#14 PRs 2 & 3: relocate user_property_permissions + user_group_permissions
Lifts both per-property and per-property-set RBAC tables from fewo-webapp into oatpp-authkit. Combined into one commit because they share a DbClient and the cross-table effective-permission resolver — the resolver itself stays in fewo since it joins property_set_members (a fewo-side concept). New files (all in oatpp-authkit): - dto/UserPermissionDto.hpp — UserPropertyPermissionDto + UserGroupPermissionDto, both registered as temporal. EffectivePermissionDto stays in fewo (it's the result shape of fewo's property_set_members JOIN). - db/UserPermissionDb.hpp — DbClient with CRUD for both tables. Each table also has a *Schema struct exposing kSchema for SchemaBuilder composition. Natural-key UNIQUE indexes carried explicitly: ux_..._user_property_until, ux_..._user_set_until. - repo/ConcreteUserPermissionRepository.hpp — two concrete repos + makeUserPropertyPermissionRepository / makeUserGroupPermissionRepository factories that wrap each in TemporalRepository. - test/test_user_permission_schema.cpp — verifies both schemas compose with TemporalRepository to produce the expected 5 DDL statements each (entity table + 3 schema indexes + 1 temporal composite index). 12 of 12 tests pass. Bumped 0.10.0 → 0.11.0. Per-row natural-key UNIQUE prevents duplicate live grants for the same (user_id, property_id) or (user_id, set_id) pair while still allowing historical rows for the same key (their valid_until differs). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
3ccc25f231
commit
0bb8bef634
6 changed files with 478 additions and 1 deletions
|
|
@ -1,5 +1,5 @@
|
||||||
cmake_minimum_required(VERSION 3.14)
|
cmake_minimum_required(VERSION 3.14)
|
||||||
project(oatpp-authkit VERSION 0.10.0 LANGUAGES CXX)
|
project(oatpp-authkit VERSION 0.11.0 LANGUAGES CXX)
|
||||||
|
|
||||||
# Header-only interface library — no compilation, just an include path and
|
# Header-only interface library — no compilation, just an include path and
|
||||||
# a CMake config package so consumers do:
|
# a CMake config package so consumers do:
|
||||||
|
|
|
||||||
178
include/oatpp-authkit/db/UserPermissionDb.hpp
Normal file
178
include/oatpp-authkit/db/UserPermissionDb.hpp
Normal file
|
|
@ -0,0 +1,178 @@
|
||||||
|
#ifndef OATPP_AUTHKIT_DB_USER_PERMISSION_DB_HPP
|
||||||
|
#define OATPP_AUTHKIT_DB_USER_PERMISSION_DB_HPP
|
||||||
|
|
||||||
|
// DbClient + declarative schema for user_property_permissions and
|
||||||
|
// user_group_permissions (authkit#14 PRs 2 & 3).
|
||||||
|
//
|
||||||
|
// Cross-table effective-permission queries that join consumer-side
|
||||||
|
// tables (e.g. fewo's property_set_members) stay in the consumer — only
|
||||||
|
// the standalone DbClient queries that operate on these two tables move
|
||||||
|
// here.
|
||||||
|
|
||||||
|
#include "oatpp-authkit/dto/UserPermissionDto.hpp"
|
||||||
|
#include "oatpp-authkit/repo/SchemaContract.hpp"
|
||||||
|
|
||||||
|
#include "oatpp-sqlite/orm.hpp"
|
||||||
|
|
||||||
|
#include OATPP_CODEGEN_BEGIN(DbClient)
|
||||||
|
|
||||||
|
namespace oatpp_authkit::db {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief DbClient for user_property_permissions and user_group_permissions.
|
||||||
|
*/
|
||||||
|
class UserPermissionDb : public oatpp::orm::DbClient {
|
||||||
|
public:
|
||||||
|
UserPermissionDb(const std::shared_ptr<oatpp::orm::Executor>& executor)
|
||||||
|
: oatpp::orm::DbClient(executor) {}
|
||||||
|
|
||||||
|
// ---- user_property_permissions ----
|
||||||
|
|
||||||
|
QUERY(getAllPropertyPermissions,
|
||||||
|
"SELECT * FROM user_property_permissions "
|
||||||
|
"WHERE valid_from <= datetime('now') AND valid_until > datetime('now');")
|
||||||
|
|
||||||
|
QUERY(getAllPropertyPermissionsRaw,
|
||||||
|
"SELECT * FROM user_property_permissions;")
|
||||||
|
|
||||||
|
QUERY(getPropertyPermissionsForUser,
|
||||||
|
"SELECT * FROM user_property_permissions "
|
||||||
|
"WHERE user_id = :userId "
|
||||||
|
" AND valid_from <= datetime('now') AND valid_until > datetime('now');",
|
||||||
|
PARAM(oatpp::String, userId))
|
||||||
|
|
||||||
|
QUERY(getPropertyPermissionByEntityId,
|
||||||
|
"SELECT * FROM user_property_permissions "
|
||||||
|
"WHERE entity_id = :entityId "
|
||||||
|
" AND valid_from <= datetime('now') AND valid_until > datetime('now');",
|
||||||
|
PARAM(oatpp::String, entityId))
|
||||||
|
|
||||||
|
QUERY(upsertPropertyPermissionById,
|
||||||
|
"INSERT INTO user_property_permissions "
|
||||||
|
" (id, entity_id, user_id, property_id, permission, valid_from, valid_until) "
|
||||||
|
"VALUES "
|
||||||
|
" (:p.id, :p.entityId, :p.userId, :p.propertyId, :p.permission, "
|
||||||
|
" :p.validFrom, :p.validUntil) "
|
||||||
|
"ON CONFLICT(id) DO UPDATE SET "
|
||||||
|
" entity_id = excluded.entity_id, "
|
||||||
|
" user_id = excluded.user_id, "
|
||||||
|
" property_id = excluded.property_id, "
|
||||||
|
" permission = excluded.permission, "
|
||||||
|
" valid_from = excluded.valid_from, "
|
||||||
|
" valid_until = excluded.valid_until;",
|
||||||
|
PARAM(oatpp::Object<dto::UserPropertyPermissionDto>, p))
|
||||||
|
|
||||||
|
QUERY(softDeletePropertyPermission,
|
||||||
|
"UPDATE user_property_permissions SET valid_until = datetime('now') "
|
||||||
|
"WHERE entity_id = :entityId AND valid_until > datetime('now');",
|
||||||
|
PARAM(oatpp::String, entityId))
|
||||||
|
|
||||||
|
// ---- user_group_permissions ----
|
||||||
|
|
||||||
|
QUERY(getAllGroupPermissions,
|
||||||
|
"SELECT * FROM user_group_permissions "
|
||||||
|
"WHERE valid_from <= datetime('now') AND valid_until > datetime('now');")
|
||||||
|
|
||||||
|
QUERY(getAllGroupPermissionsRaw,
|
||||||
|
"SELECT * FROM user_group_permissions;")
|
||||||
|
|
||||||
|
QUERY(getGroupPermissionsForUser,
|
||||||
|
"SELECT * FROM user_group_permissions "
|
||||||
|
"WHERE user_id = :userId "
|
||||||
|
" AND valid_from <= datetime('now') AND valid_until > datetime('now');",
|
||||||
|
PARAM(oatpp::String, userId))
|
||||||
|
|
||||||
|
QUERY(getGroupPermissionByEntityId,
|
||||||
|
"SELECT * FROM user_group_permissions "
|
||||||
|
"WHERE entity_id = :entityId "
|
||||||
|
" AND valid_from <= datetime('now') AND valid_until > datetime('now');",
|
||||||
|
PARAM(oatpp::String, entityId))
|
||||||
|
|
||||||
|
QUERY(upsertGroupPermissionById,
|
||||||
|
"INSERT INTO user_group_permissions "
|
||||||
|
" (id, entity_id, user_id, set_id, permission, valid_from, valid_until) "
|
||||||
|
"VALUES "
|
||||||
|
" (:p.id, :p.entityId, :p.userId, :p.setId, :p.permission, "
|
||||||
|
" :p.validFrom, :p.validUntil) "
|
||||||
|
"ON CONFLICT(id) DO UPDATE SET "
|
||||||
|
" entity_id = excluded.entity_id, "
|
||||||
|
" user_id = excluded.user_id, "
|
||||||
|
" set_id = excluded.set_id, "
|
||||||
|
" permission = excluded.permission, "
|
||||||
|
" valid_from = excluded.valid_from, "
|
||||||
|
" valid_until = excluded.valid_until;",
|
||||||
|
PARAM(oatpp::Object<dto::UserGroupPermissionDto>, p))
|
||||||
|
|
||||||
|
QUERY(softDeleteGroupPermission,
|
||||||
|
"UPDATE user_group_permissions SET valid_until = datetime('now') "
|
||||||
|
"WHERE entity_id = :entityId AND valid_until > datetime('now');",
|
||||||
|
PARAM(oatpp::String, entityId))
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Declarative schema for `user_property_permissions`.
|
||||||
|
*
|
||||||
|
* Composes with `TemporalRepository<UserPropertyPermissionDto>` to produce
|
||||||
|
* the full table including the temporal `valid_until` + composite UNIQUE
|
||||||
|
* index. The natural-key UNIQUE `(user_id, property_id, valid_until)` is
|
||||||
|
* carried as an explicit index here so duplicate live grants for the
|
||||||
|
* same (user, property) pair are prevented at the DB level.
|
||||||
|
*/
|
||||||
|
struct UserPropertyPermissionSchema {
|
||||||
|
inline static constexpr repo::ColumnSpec kColumns[] = {
|
||||||
|
{"id", "TEXT PRIMARY KEY"},
|
||||||
|
{"entity_id", "TEXT NOT NULL"},
|
||||||
|
{"user_id", "TEXT NOT NULL"},
|
||||||
|
{"property_id", "TEXT NOT NULL"},
|
||||||
|
{"permission", "TEXT NOT NULL DEFAULT 'readonly'"},
|
||||||
|
// valid_from / valid_until come from TemporalRepository.
|
||||||
|
};
|
||||||
|
inline static constexpr repo::IndexSpec kIndexes[] = {
|
||||||
|
{"ix_{table}_entity_id", false, "(entity_id)"},
|
||||||
|
{"ix_{table}_user_id", false, "(user_id)"},
|
||||||
|
{"ux_{table}_user_property_until", true,
|
||||||
|
"(user_id, property_id, valid_until)"},
|
||||||
|
};
|
||||||
|
|
||||||
|
inline static constexpr repo::DecoratorSchema kSchema = {
|
||||||
|
"UserPropertyPermissionSchema",
|
||||||
|
kColumns, sizeof(kColumns)/sizeof(kColumns[0]),
|
||||||
|
kIndexes, sizeof(kIndexes)/sizeof(kIndexes[0]),
|
||||||
|
nullptr, 0,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Declarative schema for `user_group_permissions`.
|
||||||
|
*
|
||||||
|
* Mirrors `UserPropertyPermissionSchema` with `set_id` instead of
|
||||||
|
* `property_id`. The natural-key UNIQUE prevents duplicate live grants
|
||||||
|
* for the same (user, set) pair.
|
||||||
|
*/
|
||||||
|
struct UserGroupPermissionSchema {
|
||||||
|
inline static constexpr repo::ColumnSpec kColumns[] = {
|
||||||
|
{"id", "TEXT PRIMARY KEY"},
|
||||||
|
{"entity_id", "TEXT NOT NULL"},
|
||||||
|
{"user_id", "TEXT NOT NULL"},
|
||||||
|
{"set_id", "TEXT NOT NULL"},
|
||||||
|
{"permission", "TEXT NOT NULL DEFAULT 'readonly'"},
|
||||||
|
};
|
||||||
|
inline static constexpr repo::IndexSpec kIndexes[] = {
|
||||||
|
{"ix_{table}_entity_id", false, "(entity_id)"},
|
||||||
|
{"ix_{table}_user_id", false, "(user_id)"},
|
||||||
|
{"ux_{table}_user_set_until", true, "(user_id, set_id, valid_until)"},
|
||||||
|
};
|
||||||
|
|
||||||
|
inline static constexpr repo::DecoratorSchema kSchema = {
|
||||||
|
"UserGroupPermissionSchema",
|
||||||
|
kColumns, sizeof(kColumns)/sizeof(kColumns[0]),
|
||||||
|
kIndexes, sizeof(kIndexes)/sizeof(kIndexes[0]),
|
||||||
|
nullptr, 0,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace oatpp_authkit::db
|
||||||
|
|
||||||
|
#include OATPP_CODEGEN_END(DbClient)
|
||||||
|
|
||||||
|
#endif
|
||||||
71
include/oatpp-authkit/dto/UserPermissionDto.hpp
Normal file
71
include/oatpp-authkit/dto/UserPermissionDto.hpp
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
#ifndef OATPP_AUTHKIT_DTO_USER_PERMISSION_DTO_HPP
|
||||||
|
#define OATPP_AUTHKIT_DTO_USER_PERMISSION_DTO_HPP
|
||||||
|
|
||||||
|
// User property + group permission DTOs (authkit#14 PRs 2 & 3).
|
||||||
|
// Lifted from fewo-webapp's `src/dto/UserPropertyPermissionDto.hpp`.
|
||||||
|
//
|
||||||
|
// Per-property and per-property-set RBAC primitives. The effective-
|
||||||
|
// permission resolver lives in the consumer (fewo-webapp) because it
|
||||||
|
// joins `property_set_members`, which is a consumer-side concept; the
|
||||||
|
// raw tables move here so any oatpp-authkit consumer can reuse them
|
||||||
|
// without copying schema.
|
||||||
|
|
||||||
|
#include "oatpp/core/macro/codegen.hpp"
|
||||||
|
#include "oatpp/core/Types.hpp"
|
||||||
|
|
||||||
|
#include OATPP_CODEGEN_BEGIN(DTO)
|
||||||
|
|
||||||
|
namespace oatpp_authkit::dto {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Per-property access grant.
|
||||||
|
*
|
||||||
|
* Maps a user to a property with one of `'readonly'` / `'editor'`. Live
|
||||||
|
* rows are temporal — soft-delete sets `valid_until` to `now()`.
|
||||||
|
*/
|
||||||
|
class UserPropertyPermissionDto : public oatpp::DTO {
|
||||||
|
DTO_INIT(UserPropertyPermissionDto, DTO)
|
||||||
|
|
||||||
|
DTO_FIELD(String, id);
|
||||||
|
DTO_FIELD(String, entityId, "entity_id");
|
||||||
|
DTO_FIELD(String, userId, "user_id");
|
||||||
|
DTO_FIELD(String, propertyId, "property_id");
|
||||||
|
DTO_FIELD(String, permission);
|
||||||
|
DTO_FIELD(String, validFrom, "valid_from");
|
||||||
|
DTO_FIELD(String, validUntil, "valid_until");
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Group-level access grant: user → property_set.
|
||||||
|
*
|
||||||
|
* `set_id` references a consumer-defined property-set table. The
|
||||||
|
* effective-permission resolver in the consumer expands group grants to
|
||||||
|
* member properties via its own join.
|
||||||
|
*/
|
||||||
|
class UserGroupPermissionDto : public oatpp::DTO {
|
||||||
|
DTO_INIT(UserGroupPermissionDto, DTO)
|
||||||
|
|
||||||
|
DTO_FIELD(String, id);
|
||||||
|
DTO_FIELD(String, entityId, "entity_id");
|
||||||
|
DTO_FIELD(String, userId, "user_id");
|
||||||
|
DTO_FIELD(String, setId, "set_id");
|
||||||
|
DTO_FIELD(String, permission);
|
||||||
|
DTO_FIELD(String, validFrom, "valid_from");
|
||||||
|
DTO_FIELD(String, validUntil, "valid_until");
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace oatpp_authkit::dto
|
||||||
|
|
||||||
|
#include OATPP_CODEGEN_END(DTO)
|
||||||
|
|
||||||
|
#include "oatpp-authkit/repo/TemporalFieldTraits.hpp"
|
||||||
|
|
||||||
|
OATPP_AUTHKIT_REGISTER_TEMPORAL(
|
||||||
|
oatpp_authkit::dto::UserPropertyPermissionDto,
|
||||||
|
id, entityId, validFrom, validUntil)
|
||||||
|
|
||||||
|
OATPP_AUTHKIT_REGISTER_TEMPORAL(
|
||||||
|
oatpp_authkit::dto::UserGroupPermissionDto,
|
||||||
|
id, entityId, validFrom, validUntil)
|
||||||
|
|
||||||
|
#endif
|
||||||
141
include/oatpp-authkit/repo/ConcreteUserPermissionRepository.hpp
Normal file
141
include/oatpp-authkit/repo/ConcreteUserPermissionRepository.hpp
Normal file
|
|
@ -0,0 +1,141 @@
|
||||||
|
#ifndef OATPP_AUTHKIT_REPO_CONCRETE_USER_PERMISSION_REPOSITORY_HPP
|
||||||
|
#define OATPP_AUTHKIT_REPO_CONCRETE_USER_PERMISSION_REPOSITORY_HPP
|
||||||
|
|
||||||
|
// Concrete inner adapters of Repository<UserPropertyPermissionDto> and
|
||||||
|
// Repository<UserGroupPermissionDto> (authkit#14 PRs 2 & 3). Stack each
|
||||||
|
// under TemporalRepository for versioning + soft-delete via valid_until.
|
||||||
|
|
||||||
|
#include "oatpp-authkit/db/UserPermissionDb.hpp"
|
||||||
|
#include "oatpp-authkit/dto/UserPermissionDto.hpp"
|
||||||
|
#include "oatpp-authkit/repo/Repository.hpp"
|
||||||
|
#include "oatpp-authkit/repo/SchemaContract.hpp"
|
||||||
|
#include "oatpp-authkit/repo/TemporalRepository.hpp"
|
||||||
|
|
||||||
|
#include "oatpp/core/Types.hpp"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace oatpp_authkit::repo {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Inner adapter for `Repository<UserPropertyPermissionDto>`,
|
||||||
|
* delegating to `db::UserPermissionDb`.
|
||||||
|
*
|
||||||
|
* Schema lives in `db::UserPropertyPermissionSchema` — this repo
|
||||||
|
* contributes nothing to the schema, only adapts queries.
|
||||||
|
*/
|
||||||
|
class ConcreteUserPropertyPermissionRepository
|
||||||
|
: public Repository<dto::UserPropertyPermissionDto>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
inline static constexpr DecoratorSchema kSchema = {
|
||||||
|
"ConcreteUserPropertyPermissionRepository",
|
||||||
|
nullptr, 0, nullptr, 0, nullptr, 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit ConcreteUserPropertyPermissionRepository(
|
||||||
|
std::shared_ptr<db::UserPermissionDb> updb)
|
||||||
|
: m_db(std::move(updb)) {}
|
||||||
|
|
||||||
|
oatpp::Object<dto::UserPropertyPermissionDto>
|
||||||
|
findByEntityId(const oatpp::String& entityId) override
|
||||||
|
{
|
||||||
|
auto res = m_db->getPropertyPermissionByEntityId(entityId);
|
||||||
|
if (!res || !res->isSuccess()) return nullptr;
|
||||||
|
auto rows = res->template fetch<
|
||||||
|
oatpp::Vector<oatpp::Object<dto::UserPropertyPermissionDto>>>();
|
||||||
|
if (!rows || rows->empty()) return nullptr;
|
||||||
|
return (*rows)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
oatpp::Vector<oatpp::Object<dto::UserPropertyPermissionDto>> list() override {
|
||||||
|
auto res = m_db->getAllPropertyPermissionsRaw();
|
||||||
|
auto out = oatpp::Vector<oatpp::Object<dto::UserPropertyPermissionDto>>::createShared();
|
||||||
|
if (!res || !res->isSuccess()) return out;
|
||||||
|
auto fetched = res->template fetch<
|
||||||
|
oatpp::Vector<oatpp::Object<dto::UserPropertyPermissionDto>>>();
|
||||||
|
if (!fetched) return out;
|
||||||
|
for (auto& row : *fetched) if (row) out->push_back(row);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void save(const oatpp::Object<dto::UserPropertyPermissionDto>& d) override {
|
||||||
|
m_db->upsertPropertyPermissionById(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
void softDelete(const oatpp::String& entityId) override {
|
||||||
|
m_db->softDeletePropertyPermission(entityId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<db::UserPermissionDb> m_db;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Inner adapter for `Repository<UserGroupPermissionDto>`,
|
||||||
|
* delegating to `db::UserPermissionDb`.
|
||||||
|
*/
|
||||||
|
class ConcreteUserGroupPermissionRepository
|
||||||
|
: public Repository<dto::UserGroupPermissionDto>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
inline static constexpr DecoratorSchema kSchema = {
|
||||||
|
"ConcreteUserGroupPermissionRepository",
|
||||||
|
nullptr, 0, nullptr, 0, nullptr, 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit ConcreteUserGroupPermissionRepository(
|
||||||
|
std::shared_ptr<db::UserPermissionDb> updb)
|
||||||
|
: m_db(std::move(updb)) {}
|
||||||
|
|
||||||
|
oatpp::Object<dto::UserGroupPermissionDto>
|
||||||
|
findByEntityId(const oatpp::String& entityId) override
|
||||||
|
{
|
||||||
|
auto res = m_db->getGroupPermissionByEntityId(entityId);
|
||||||
|
if (!res || !res->isSuccess()) return nullptr;
|
||||||
|
auto rows = res->template fetch<
|
||||||
|
oatpp::Vector<oatpp::Object<dto::UserGroupPermissionDto>>>();
|
||||||
|
if (!rows || rows->empty()) return nullptr;
|
||||||
|
return (*rows)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
oatpp::Vector<oatpp::Object<dto::UserGroupPermissionDto>> list() override {
|
||||||
|
auto res = m_db->getAllGroupPermissionsRaw();
|
||||||
|
auto out = oatpp::Vector<oatpp::Object<dto::UserGroupPermissionDto>>::createShared();
|
||||||
|
if (!res || !res->isSuccess()) return out;
|
||||||
|
auto fetched = res->template fetch<
|
||||||
|
oatpp::Vector<oatpp::Object<dto::UserGroupPermissionDto>>>();
|
||||||
|
if (!fetched) return out;
|
||||||
|
for (auto& row : *fetched) if (row) out->push_back(row);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void save(const oatpp::Object<dto::UserGroupPermissionDto>& d) override {
|
||||||
|
m_db->upsertGroupPermissionById(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
void softDelete(const oatpp::String& entityId) override {
|
||||||
|
m_db->softDeleteGroupPermission(entityId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<db::UserPermissionDb> m_db;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline std::shared_ptr<Repository<dto::UserPropertyPermissionDto>>
|
||||||
|
makeUserPropertyPermissionRepository(std::shared_ptr<db::UserPermissionDb> updb)
|
||||||
|
{
|
||||||
|
auto concrete = std::make_shared<ConcreteUserPropertyPermissionRepository>(std::move(updb));
|
||||||
|
return std::make_shared<TemporalRepository<dto::UserPropertyPermissionDto>>(concrete);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::shared_ptr<Repository<dto::UserGroupPermissionDto>>
|
||||||
|
makeUserGroupPermissionRepository(std::shared_ptr<db::UserPermissionDb> updb)
|
||||||
|
{
|
||||||
|
auto concrete = std::make_shared<ConcreteUserGroupPermissionRepository>(std::move(updb));
|
||||||
|
return std::make_shared<TemporalRepository<dto::UserGroupPermissionDto>>(concrete);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace oatpp_authkit::repo
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -57,4 +57,9 @@ if(oatpp-sqlite_FOUND AND Threads_FOUND)
|
||||||
target_link_libraries(test_role_template_schema
|
target_link_libraries(test_role_template_schema
|
||||||
PRIVATE oatpp::authkit oatpp::oatpp oatpp::oatpp-sqlite Threads::Threads)
|
PRIVATE oatpp::authkit oatpp::oatpp oatpp::oatpp-sqlite Threads::Threads)
|
||||||
add_test(NAME role_template_schema COMMAND test_role_template_schema)
|
add_test(NAME role_template_schema COMMAND test_role_template_schema)
|
||||||
|
|
||||||
|
add_executable(test_user_permission_schema test_user_permission_schema.cpp)
|
||||||
|
target_link_libraries(test_user_permission_schema
|
||||||
|
PRIVATE oatpp::authkit oatpp::oatpp oatpp::oatpp-sqlite Threads::Threads)
|
||||||
|
add_test(NAME user_permission_schema COMMAND test_user_permission_schema)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
||||||
82
test/test_user_permission_schema.cpp
Normal file
82
test/test_user_permission_schema.cpp
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
// Tests for authkit#14 PRs 2 & 3 — user_property_permissions and
|
||||||
|
// user_group_permissions schemas compose correctly with TemporalRepository.
|
||||||
|
|
||||||
|
#include "oatpp-authkit/db/UserPermissionDb.hpp"
|
||||||
|
#include "oatpp-authkit/dto/UserPermissionDto.hpp"
|
||||||
|
#include "oatpp-authkit/repo/ConcreteUserPermissionRepository.hpp"
|
||||||
|
#include "oatpp-authkit/repo/SchemaContract.hpp"
|
||||||
|
#include "oatpp-authkit/repo/TemporalRepository.hpp"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#define REQUIRE(cond) do { \
|
||||||
|
if (!(cond)) { std::fprintf(stderr, "REQUIRE failed: %s @ %s:%d\n", \
|
||||||
|
#cond, __FILE__, __LINE__); std::abort(); } } while (0)
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
bool contains(const std::string& haystack, const std::string& needle) {
|
||||||
|
return haystack.find(needle) != std::string::npos;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_user_property_permissions_create() {
|
||||||
|
using namespace oatpp_authkit::repo;
|
||||||
|
using namespace oatpp_authkit::db;
|
||||||
|
using namespace oatpp_authkit::dto;
|
||||||
|
|
||||||
|
std::vector<std::string> sqls;
|
||||||
|
SqlExec exec = [&](const std::string& sql) { sqls.push_back(sql); };
|
||||||
|
|
||||||
|
SchemaBuilder<
|
||||||
|
UserPropertyPermissionSchema,
|
||||||
|
TemporalRepository<UserPropertyPermissionDto>>::create(
|
||||||
|
"user_property_permissions", exec);
|
||||||
|
|
||||||
|
// 1 entity table + 3 schema-side indexes + 1 temporal index = 5
|
||||||
|
REQUIRE(sqls.size() == 5);
|
||||||
|
|
||||||
|
REQUIRE(contains(sqls[0], "CREATE TABLE IF NOT EXISTS user_property_permissions"));
|
||||||
|
REQUIRE(contains(sqls[0], "user_id TEXT NOT NULL"));
|
||||||
|
REQUIRE(contains(sqls[0], "property_id TEXT NOT NULL"));
|
||||||
|
REQUIRE(contains(sqls[0], "permission TEXT NOT NULL DEFAULT 'readonly'"));
|
||||||
|
REQUIRE(contains(sqls[0], "valid_until TEXT NOT NULL DEFAULT '9999-12-31T23:59:59Z'"));
|
||||||
|
|
||||||
|
// Indexes: 3 from UserPropertyPermissionSchema in order, then 1 from TemporalRepository.
|
||||||
|
REQUIRE(contains(sqls[1], "ix_user_property_permissions_entity_id"));
|
||||||
|
REQUIRE(contains(sqls[2], "ix_user_property_permissions_user_id"));
|
||||||
|
REQUIRE(contains(sqls[3], "ux_user_property_permissions_user_property_until"));
|
||||||
|
REQUIRE(contains(sqls[3], "(user_id, property_id, valid_until)"));
|
||||||
|
REQUIRE(contains(sqls[4], "ux_user_property_permissions_entity_valid_until"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_user_group_permissions_create() {
|
||||||
|
using namespace oatpp_authkit::repo;
|
||||||
|
using namespace oatpp_authkit::db;
|
||||||
|
using namespace oatpp_authkit::dto;
|
||||||
|
|
||||||
|
std::vector<std::string> sqls;
|
||||||
|
SqlExec exec = [&](const std::string& sql) { sqls.push_back(sql); };
|
||||||
|
|
||||||
|
SchemaBuilder<
|
||||||
|
UserGroupPermissionSchema,
|
||||||
|
TemporalRepository<UserGroupPermissionDto>>::create(
|
||||||
|
"user_group_permissions", exec);
|
||||||
|
|
||||||
|
REQUIRE(sqls.size() == 5);
|
||||||
|
REQUIRE(contains(sqls[0], "CREATE TABLE IF NOT EXISTS user_group_permissions"));
|
||||||
|
REQUIRE(contains(sqls[0], "set_id TEXT NOT NULL"));
|
||||||
|
REQUIRE(contains(sqls[3], "ux_user_group_permissions_user_set_until"));
|
||||||
|
REQUIRE(contains(sqls[3], "(user_id, set_id, valid_until)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
test_user_property_permissions_create();
|
||||||
|
test_user_group_permissions_create();
|
||||||
|
std::printf("test_user_permission_schema: OK\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue