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>
82 lines
3.1 KiB
C++
82 lines
3.1 KiB
C++
// 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;
|
|
}
|