Schliesst die in QA-05 gefundene Luecke: der zentrale Audit-Log (AUD-01/02)
existierte und war getestet, wurde aber von keinem Produktions-Handler
befuellt. Additive WithAudit(...)-Methode je Store (Konvention aus
lockout.Store.WithPolicy uebernommen, audit==nil bleibt gueltig, kein
Verhaltensbruch fuer bestehende Aufrufer):
- internal/policy.Store.Grant/Revoke -> policy.grant/policy.revoke
- internal/tenant.Registry (Suspend/Reactivate/ScheduleDeletion/
CancelDeletion via transition) -> tenant.transition
- internal/lockout.Store.RecordFailure/Unlock -> auth.login_failed/
auth.account_locked/auth.account_unlocked
- internal/kek.Store.RotateTenantKEK/RotateMasterKey -> kek.tenant_rotated/
kek.master_rotated
Neues Testpaket internal/audit/wiring_test.go: fuer jeden der vier Bereiche
eine reale Aktion ausgefuehrt und per direkter audit_events-Abfrage
nachgewiesen (derselbe Nachweisstil wie der QA-05-Stichprobenabgleich, der
die Luecke fand). Alle bestehenden Tests der vier Pakete bleiben gruen.
51/51 Pakete gruen auf 192.168.1.131.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HhgFcLS8tYMhDJpP74C6AQ
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>