Commit Graph
6 Commits
Author SHA1 Message Date
sysopsandClaude Sonnet 5 3d5f53f103 RBAC-04: modul-scoped-berechtigungen
internal/policy/module_scope.go: policy_module_scopes verknuepft optional
eine (role, permission)-Regel mit einem LIC-02-Feature-Flag. Enforcer.
AuthorizeForTenant ist DIESELBE zentrale Entscheidungsfunktion wie Authorize
(Akzeptanzkriterium 3, kein zweiter Enforcement-Mechanismus) — prueft
zusaetzlich zur Grundregel, ob das verknuepfte Modul fuer den Tenant aktiv
ist. Existiert kein ModuleScope-Eintrag, bleibt eine Regel wie bisher ohne
Lizenzbindung gueltig (Kombinationsfall). GuardModuleScoped erweitert
policy.Guard um dieselbe Pruefung.

internal/flag (LIC-02) wurde 1:1 aus dem lic-02-Branch uebernommen (git show
aus derselben Repo-Historie, keine Aenderung) — RBAC-04 haengt sowohl an
RBAC-01/02 als auch an LIC-02, aber diese leben auf getrennten, noch nicht
gemergten Feature-Branches ohne gemeinsame Historie.

Fail-Safe-Verhalten aus LIC-02 greift automatisch: ein nicht konfiguriertes
oder nicht erreichbares Feature-Flag gilt als deaktiviert, nie als aktiviert
(sicherer Default fuer Modul-Aktivierungspruefungen).

Pruefungen (ausgefuehrt auf root@192.168.1.131, go build/vet/test PASS):
1. Zugriff auf deaktiviertes Modul trotz passender Rolle abgewiesen —
   TestAuthorizeForTenant_DeniesWhenModuleNotActivated: GuardModuleScoped
   ruft die Query-Funktion nachweislich nicht auf. PASS.
2. Reaktivierung macht Berechtigung im laufenden Betrieb wirksam, kein
   Neustart — TestAuthorizeForTenant_BecomesActiveWithoutRestart: derselbe
   Enforcer/Service-Prozess, Flag per Store.Set aktiviert, TTL abgewartet,
   danach erlaubt. PASS.
3. Zusammenspiel Modul-Scope + Rollenscope in Kombinationsfaellen —
   TestAuthorizeForTenant_CombinationsOfRoleAndModuleScope: keine Regel ->
   verboten; Regel ohne Modul-Scope -> immer erlaubt; Regel mit Modul-Scope
   und Flag aus -> verboten; Flag an -> erlaubt. PASS.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-27 22:04:58 +02:00
sysopsandClaude Sonnet 5 d63fcbb49e RBAC-02: policy-enforcement-schicht-zentral
internal/policy: deklarativer, DB-gehaltener Regelsatz (policy_rules) statt
hartcodierter Go-Entscheidungslogik — Store.IsAllowed schaut ausschliesslich
in die Datenbank, kein Go-Fallback. Ein Regelwechsel (Grant/Revoke) wirkt
sich sofort aus, ohne Codeaenderung/Deploy (Akzeptanzkriterium 3). Jede
Aenderung wird atomar mit einem versionierten Historieneintrag in
policy_rule_changes festgehalten (grant/revoke, Akteur, Version).

Enforcer.Authorize ist Default-Deny: existiert keine Regel fuer role+
permission, ist der Zugriff verboten (Akzeptanzkriterium 2), fuer sich
genommen ohne Anwendungslogik testbar.

Guard/GuardTenantScoped sind die zentrale Enforcement-Funktion
(Akzeptanzkriterium 1): die uebergebene Query-Funktion wird NUR bei
erfolgreicher Autorisierung aufgerufen — es gibt keinen Weg, Daten ohne
vorherige Authorize-Entscheidung zu erhalten. GuardTenantScoped erzwingt
zusaetzlich per Funktionssignatur, dass tenantSlug TEIL der Query-Funktion
ist (Akzeptanzkriterium 3) — ein nachgelagerter Post-Filter (der
archivmail-Fehler aus "Bekannte Fehler vermeiden": Tenant-Filter nach statt
in der Query) ist mit dieser Signatur strukturell nicht moeglich, da die
Repository-Implementierung tenantSlug selbst fuer ihre eigene WHERE-Klausel
entgegennimmt.

Pruefungen (ausgefuehrt auf root@192.168.1.131, go build/vet/test PASS):
1. Kein Datenzugriffs-Pfad umgeht die zentrale Enforcement-Schicht —
   TestGuard_NeverCallsQueryWithoutAuthorization: query-Funktion wird
   nachweislich NICHT aufgerufen ohne vorherige Regel, erst nach Grant. PASS.
2. Anfrage ohne passende Policy wird zuverlaessig abgewiesen (Default-Deny) —
   TestAuthorize_DefaultDeny: keine Regel konfiguriert -> ErrDenied, nicht
   automatisch erlaubt. PASS.
3. Policy-Regelsatz versioniert, Regelwechsel ohne Codeaenderung
   nachvollziehbar — TestGrantRevoke_ChangesBehaviorWithoutCodeChange:
   Verhalten aendert sich durch reinen Datenbank-Grant/Revoke, Historie
   zeigt beide Versionen korrekt. PASS.

Zusaetzlich: TestGuardTenantScoped_IsolatesDataBetweenTenants belegt das
Tenant-Scoping-Muster aus Akzeptanzkriterium 3 konkret anhand zweier
Tenants. PASS.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-27 21:43:26 +02:00
sysopsandClaude Sonnet 5 ed67887385 RBAC-01: rollenmodell-grundrechte
internal/rbac: drei Grundrollen (superadmin, tenant_admin, user) mit
Hierarchie ueber eine einfache Eltern-Map (tenant_admin erbt von user,
superadmin erbt von tenant_admin) — EffectivePermissions loest die volle
vererbte Rechtemenge auf (Akzeptanzkriterium 2). Policy-Modell bewusst als
reine Go-Datenstruktur getrennt von der Durchsetzung (RBAC-02), nach
Casbin-Prinzip.

Store verwaltet Rollenzuweisungen innerhalb EINER Tenant-Datenbank (Modell C,
analog internal/user.TenantUserStore) — nur 'user' und 'tenant_admin' sind
hier zuweisbar (assignableRoles-Matrix). Ein Zuweisungsversuch fuer
'superadmin' wird abgewiesen, da diese Rolle mandantenuebergreifend ist und
bereits durch die Existenz eines Kontos in IAM-01s SuperadminStore
repraesentiert wird — keine doppelte Modellierung. Jede Zuweisung schreibt
zusaetzlich einen Historieneintrag (role_assignment_history) mit
grantedBy/grantedAt, atomar in derselben Transaktion (Akzeptanzkriterium 3).

Pruefungen (ausgefuehrt auf root@192.168.1.131, go build/vet/test PASS):
1. Zuweisung ausserhalb der erlaubten Matrix abgewiesen —
   TestStore_RejectsSuperadminOutsideAllowedMatrix und
   TestStore_RejectsUnknownRole: beide ErrRoleNotAssignableInTenantScope,
   kein Datensatz hinterlassen. PASS.
2. Rollenhierarchie liefert erwartete effektive Rechtemenge —
   TestEffectivePermissions_Inheritance: tenant_admin hat geerbte
   user-Rechte + eigene, aber nicht platform.manage_tenants; superadmin hat
   die volle Kette. PASS.
3. Datenmodell von zweiter Person gegen Dokumentation geprueft — NICHT
   durchgefuehrt (keine zweite Person in dieser Session verfuegbar). Offen.

Zusaetzlich automatisiert getestet (Akzeptanzkriterium 3):
TestStore_HistoryTracksWhoAndWhen — zwei aufeinanderfolgende Zuweisungen,
Historie liefert beide mit korrektem grantedBy in chronologischer
Reihenfolge. PASS.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-27 19:45:16 +02:00
sysopsandClaude Sonnet 5 e4793303fc IAM-01: benutzer-datenmodell-crud
Benutzer-Datenmodell + CRUD fuer Tenant-User (tenant-scoped, keine
tenant_id-Spalte noetig, Tenant ergibt sich aus der DB-Verbindung, Modell C)
und getrennt dafuer SuperadminStore fuer mandantenuebergreifende Konten in
der Registry-DB — First-Class-Typ statt tenant_id-NULL-Sonderfall im
Tenant-User-Code (bekannter archivdms-Fehler vermieden).

E-Mail-Eindeutigkeit: tenant-scoped fuer normale Benutzer (UNIQUE-Constraint
gilt nur innerhalb der jeweiligen Tenant-DB), global fuer Superadmins
(eine Registry-DB, ein UNIQUE-Constraint).

Pruefungen (ausgefuehrt auf root@192.168.1.131, go build/vet/test PASS):
1. CRUD automatisiert getestet inkl. Negativfaellen — TestTenantUserStore_CRUD
   deckt doppelte E-Mail (ErrEmailTaken) und unbekannte ID ab. PASS.
2. Superadmin-Anlage ohne Tenant-Kontext — TestSuperadminStore_CreateWithoutTenantContext:
   SuperadminStore.Create hat syntaktisch keinen Tenant-Parameter, kein
   if-Zweig fuer "kein Tenant" im Code. PASS.
3. Datenmodell von zweiter Person gegen Dokumentation geprueft — NICHT
   durchgefuehrt (keine zweite Person in dieser Session verfuegbar). Offen.

Tenant-User-Handler ist im Code vorhanden, aber in cmd/core/main.go noch
nicht geroutet — braucht Connection-Routing pro Mandant (TEN-06), das nicht
Teil dieser Kachel ist. Nur der Superadmin-Endpunkt ist verdrahtet.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-27 17:57:49 +02:00
sysopsandClaude Sonnet 5 8da9c67d08 TEN-01: tenant-registry-datenbank-provisioning
Registry-DB (nur Tenant-Metadaten), Provisioning-Routine legt pro Mandant
eine physisch isolierte Postgres-DB an und registriert sie transaktional
(Rollback der DB bei fehlgeschlagener Registrierung). Schlanker HTTP-Handler
als Schnittstellen-Vorbereitung fuer API-01/TEN-02, kein eigenes REST-Grundgerüst.

Pruefungen:
1. Migration up/down geschrieben (0001_tenant_registry.{up,down}.sql) — nicht
   gegen echte DB ausgefuehrt, da auf dieser Maschine kein Go/Postgres-Test-
   Setup verfuegbar ist. Offen zur Ausfuehrung.
2. Integrationstest TestProvision_CreatesIsolatedDatabases geschrieben (zwei
   Mandanten, prueft unterschiedliche db_name und current_database()) —
   ebenfalls nicht ausgefuehrt, guarded per TEST_ADMIN_DSN env var. Offen.
3. Slug-Validierung (unit test TestValidateSlug) deckt SQL-Injection-Versuch
   im Datenbanknamen ab — ebenfalls nicht lokal ausgefuehrt, da kein Go
   Compiler auf dieser Maschine vorhanden ist. Offen.

Alle drei Pruefungen sind vorbereitet, aber NICHT durchgefuehrt worden —
zaehlen laut Vorgabe als offen bis auf einer Maschine mit Go+Postgres verifiziert.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-27 17:40:35 +02:00
sysops c895a67c4b core: initial Go module skeleton (config, db pool, tenant registry migration) 2026-08-27 17:27:59 +02:00