Skip to content

feat(permissions): portable scope refs for non-group dimensions (cdb_station, cdb_bereich, cdb_gruppentyp) #98

Closed

Description

@2000game

Problem

resolveScope (src/permissions/scope.ts) accepts exactly two kinds of scope entry:

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" }] }
  • campus is 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_gruppentyp need 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 — the status permission 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 scopeField and reject a mismatch at plan
    time (a {campus:…} ref on a cc_securitylevel right 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 scopeField fails 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 grants emits 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).

Metadata

Metadata

Assignees

No one assigned

    Labels

    triageUnsorted intake — decide in the weekly sweep

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions