oatpp-authkit/include/oatpp-authkit/repo/ConcreteUserPermissionRepository.hpp
Uwe Schuster 0bb8bef634 #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>
2026-05-06 12:39:52 +02:00

141 lines
5 KiB
C++

#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