ADR 022 iam groups geo hierarchie permission caching - smart-village-solutions/sva-studio GitHub Wiki
Status: Accepted Entscheidungsdatum: 2025-01-01 Entschieden durch: Core Platform Team GitHub PR: (offen)
Das SVA Studio IAM-System bestand bisher aus einem RBAC-Modell mit direkten Rollen-Zuweisungen und einfachen Delegierungen. Mit den OpenSpec-Änderungspaketen 3–5 wurden folgende Erweiterungen notwendig:
- Paket 3 – IAM Groups: Benutzergruppen mit eigenen Rollenzuweisungen als zusätzliche Quelle für effektive Berechtigungen (neben direkten Account-Rollen und Delegierungen)
-
Paket 3 – Geo-Hierarchie: Closure-Table für räumlich strukturierte Organisationsknoten (Tiefen 0–5) mit
BEFORE INSERT-Trigger - Paket 4 – Redis Permission Snapshot Cache: Verteilter L2-Cache für effektive Berechtigungen mit HMAC-SHA-256-Integritätsschutz, TTL 900 s und strukturierten Invalidierungsereignissen
- Paket 5 – Legal-Text-Enforcement: Middleware zur Pflichtakzeptanz aktiver Rechtstexte sowie DSGVO-konformes Consent-Export-API
Die SQL-Abfragen listScopedPermissionRows und listUnscopedPermissionRows verwenden einen 3-arm UNION als source-Unterabfrage:
SELECT ar.account_id, ar.role_id, ar.instance_id, NULL::uuid AS group_id, NULL::text AS group_key
FROM iam.account_roles ar
UNION
SELECT d.delegatee_account_id, d.role_id, d.instance_id, NULL::uuid, NULL::text
FROM iam.delegations d WHERE d.status = 'active' AND now() BETWEEN d.starts_at AND d.ends_at
UNION
SELECT ag.account_id, gr.role_id, ag.instance_id, ag.group_id, g.group_key
FROM iam.account_groups ag
JOIN iam.group_roles gr ...
JOIN iam.groups g ...
WHERE (ag.valid_until IS NULL OR ag.valid_until > now())Die neuen Spalten group_id und group_key werden direkt in PermissionRow und EffectivePermission weitergeleitet, sodass auf Anwendungsebene erkennbar ist, welche Berechtigung aus einer Gruppe stammt.
Statt einer rekursiven CTE-Lösung verwenden wir eine materialisierende Closure-Table (iam.geo_hierarchy) mit expliziter Tiefenspalte (depth). Ein BEFORE INSERT-Trigger füllt diese automatisch. Maximal 5 Hierarchieebenen.
Begründung:
- Lesezugriffe deutlich effizienter als rekursive CTEs bei tiefen Hierarchien
- Gut kompatibel mit PostgreSQL RLS-Policies
-
Schlüsselformat:
perm:v1:{instanceId}:{userId}:{orgCtxHash}:{geoCtxHash} - TTL: 900 Sekunden
- Shared-Read-Path: lokaler In-Memory-L1 zuerst, danach Redis als geteilter Snapshot-Store
-
Integrität: HMAC-SHA-256 auf dem JSON-Payload mit
schema_version,signed_at,permissions,versionundhmacausREDIS_SNAPSHOT_HMAC_SECRET -
Fehlerbehandlung: Bei
integrity_errorwird der Schlüssel sofort evicted und als Cache-Miss behandelt -
Fail-Closed: Bei Redis-Ausfall, Snapshot-Write-Fehler oder fehlgeschlagenem Recompute wird kein fachlicher Zugriff aus einem stale oder nur lokal vorhandenen Zustand abgeleitet; der Pfad endet mit HTTP
503
| Event-Typ | Scope der Invalidierung |
|---|---|
role_permission_changed |
Alle Snapshots der Instance |
group_membership_changed |
Snapshots des betroffenen Accounts |
group_deleted |
Snapshots aller im Event referenzierten Accounts |
delegation_changed |
Snapshots des Delegationsempfängers |
organization_membership_changed |
Snapshots des betroffenen Accounts |
account_role_assignment_changed |
Snapshots des betroffenen Accounts |
org_hierarchy_changed |
Potenziell betroffene Instanz-Snapshots im Batch |
geo_assignment_changed |
Potenziell betroffene Instanz-Snapshots im Batch |
instance_settings_changed |
Alle Snapshots der Instance |
Die Middleware withLegalTextCompliance prüft, ob der Nutzer alle aktiven Rechtstexte akzeptiert hat. Bei DB-Fehler wird fail-open verfahren (d.h., der Request wird nicht blockiert), da ein Datenbankausfall kein Grund sein soll, alle API-Aufrufe zu sperren.
| Alternative | Grund für Ablehnung |
|---|---|
| Gruppen als eigenständiger Auth-Pfad (kein UNION) | Zu viel Duplizierung in Query-Logik; keine saubere Aggregierung in EffectivePermission
|
| Adjacency-List für Geo-Hierarchie (rekursive CTEs) | Performance-Einbußen bei tiefen Hierarchien; nicht RLS-freundlich |
| Redis-Snapshot ohne HMAC | Integritätsverlust bei Speicherkorruption oder externen Zugriffen nicht erkennbar |
| Legal-Text-Enforcement als Fail-Closed (Hard-Block bei DB-Fehler) | Zu aggressiv; würde bei DB-Wartung alle Nutzer aussperren |
- Berechtigungsquellen (Rolle, Gruppe, Delegation) sind transparent in
EffectivePermission.sourceGroupIdsundgroupNamesichtbar - Redis-L2-Cache reduziert DB-Last bei häufigen
/authorize-Aufrufen erheblich - Consent-Export-API ermöglicht DSGVO-Auskunftsanfragen ohne manuelle DB-Queries
-
REDIS_SNAPSHOT_HMAC_SECRETmuss in Produktion ein starkes Secret sein (nicht der Default) - Die Geo-Hierarchie-Closure-Table muss bei Import von Organisationsstrukturen explizit befüllt werden
- Maximal 2 Import-Typen für
LegalAcceptanceActionType(accepted,revoked,prompted) – Erweiterungen erfordern ADR-Revision
- Abschnitt 6 (Laufzeitsicht): Ergänzung des Berechtigungsflusses um Gruppen-Arm und Redis-L2-Cache
- Abschnitt 8 (Querschnittliche Konzepte): Erweiterung des IAM-Kapitels um Groups, Geo-Scope, Legal Compliance
- Abschnitt 9 (Architekturentscheidungen): Dieses ADR
| Migration | Inhalt |
|---|---|
0014_iam_groups |
Tabellen iam.groups, iam.group_roles, iam.account_groups mit RLS |
0016_iam_geo_hierarchy |
Tabellen iam.geo_nodes, iam.geo_hierarchy (Closure-Table) mit Trigger |
0017_iam_legal_acceptance_audit |
ALTER TABLE iam.legal_text_acceptances: neue Spalten für Consent-Export |