Repository navigation
feat(permissions): portable scope refs for non-group dimensions (cdb_station, cdb_bereich, cdb_gruppentyp) #98
Description
Problem
resolveScope (src/permissions/scope.ts) accepts exactly two kinds of scope entry:
- a logical group key — resolved against managed groups, and
- a raw numeric dataId — the bug(permissions): ct adopt grants assumes every scoped dataId is a group #49 escape hatch, passed straight through.
That covers cdb_gruppe (logical) and universal dimensions like cc_securitylevel (numeric,
ids 1/2/3 mean the same everywhere). It does not cover scoped rights whose scopeField
names a managed non-group resource — most importantly cdb_station (campuses, which this
scaffold DOES manage as first-class resources with logical keys) and cdb_bereich /
cdb_gruppentyp.
The result: a grant scoped by campus can only be written as a numeric literal, and campus ids
are host-specific (dev Mainz = 6, prod Mainz = 0). Declaring one is therefore a
cross-environment misgrant waiting to happen — and because declaring a group_role domain makes
ct own it, the wrong-scope grant also revokes whatever is really there on the other host.
Concrete case that motivated this
eqrm/ct-structure declares sLL-Koblenz (group + auto-membership ruleset) as code, but the two
grants that give the group its entire purpose cannot be declared:
| Right | authId | scopeField | dataId on prod |
|---|---|---|---|
churchdb:view alldata ("Personen eines Bereiches sehen") |
102 | cdb_bereich |
7 (Equippers Koblenz) |
churchdb:view station ("Standort sehen") |
124 | cdb_station |
23 (Koblenz) |
So the repo ships a group whose permissions must be clicked in by hand after every fresh apply —
the structure is reproducible, its point is not. Same story for sLL-EQRM (Bereich 1 + six
campuses), sIntegrationmeeting, sFlow*Editor and sFlowmodulBetatester; see
docs/permissions-status.md
"Per-group grants: NOT adopted (accepted limitation)".
Note this is not blocked on #96 — that one is person-status (ct.status) and orthogonal.
The two get conflated because commit 9a72ab3 in ct-structure said so in its subject line.
Proposed direction
Let a scope entry be a typed logical ref whose dimension matches the right's scopeField,
resolved through the same resolver that already handles campus:-style refs elsewhere (#20):
{ right: "churchdb:view station", scope: [{ campus: "koblenz" }] }
{ right: "churchdb:view alldata", scope: [{ department: "equippers_koblenz" }] }
{ right: "churchgroup:edit group memberships of grouptype", scope: [{ groupType: "struktur" }] }campusis the easy win: campuses are already managed resources with state-backed ids, so this
is resolver plumbing, not a new resource type.cdb_bereich/cdb_gruppentypneed a decision on whether departments become adoptable or stay
a read-only ref catalog resolved by name — the same fork feat(resources): person statuses are not declarable — thestatuspermission domain is only half-portable #96 hit with/statuses. A name-resolved
ref that hard-errors when the name is absent is still strictly better than a silent numeric
misgrant, but it needs the failure mode chosen deliberately.- Validate the ref's dimension against the catalog's
scopeFieldand reject a mismatch at plan
time (a{campus:…}ref on acc_securitylevelright is a config bug, not a runtime surprise).
Acceptance
- A campus-scoped grant can be declared with a logical key and plans clean on both dev and prod
from one config. - A scope ref whose dimension does not match the right's
scopeFieldfails at plan time with a
message naming both. - A scope ref that cannot resolve is a hard error, never a silently dropped or numeric-guessed dataId.
-
ct adopt grantsemits logical refs for these dimensions instead of numeric literals (extends bug(permissions): ct adopt grants assumes every scoped dataId is a group #49). - ct-structure can declare
sLL-Koblenz's two grants and drop them from the manual surface.
Related: #20 (portable configs / shared resolver), #49 (adopt assumes every scoped dataId is a
group), #96 (person-status — same "half-portable domain" shape, different domain).
Activity
fix(permissions): paginate ref catalogs, guard person-status destroy,…
feat(permissions): portable scope refs + declarable person statuses (#99