# RBAC-06 – Prüfprotokoll: Modulübergreifender HTTP-Endpunkt für Policy-Entscheidungen Voraussetzung RBAC-02 – bereits Fertig (siehe eigenes Protokoll). ## Grundsatzentscheidung: Wrapper, keine zweite Autorisierungslogik `internal/policyapi.Mount` registriert `POST /authorize`, dessen Handler AUSSCHLIESSLICH `policy.Enforcer.Authorize` aufruft — es gibt keinen zweiten, abweichenden Entscheidungspfad. Die Übereinstimmung zwischen HTTP-Antwort und direktem Enforcer-Ergebnis (Akzeptanzkriterium 1) ist dadurch strukturell garantiert, nicht nur zufällig getestet — real mit 100 Stichproben bewiesen (siehe Prüfungen). ## Service-Authentifizierung: eigener, schlanker Token statt moduleregistry Für Akzeptanzkriterium 2 (service-zu-service-authentifiziert) wurde BEWUSST NICHT `internal/moduleregistry.Registry.RequireServiceCredential` (API-02) wiederverwendet — dessen `Provision`/`Authenticate` ist ein volles Credential-Ausstellungssystem für Modul-AKTIVIERUNG, zieht `internal/flag` als weitere Abhängigkeit nach sich und wurde nie mit `internal/policy` zusammen gemergt (beide entstanden auf unabhängigen, nie zusammengeführten Feature-Branches). Für einen einzelnen, zustandslosen Autorisierungs-Endpunkt genügt ein geteilter, über Umgebungsvariable konfigurierter Service-Token, timing-safe verglichen (`subtle.ConstantTimeCompare`, dieselbe Referenzimplementierung wie AUD-02) — kleinste Lösung, die Akzeptanzkriterium 2 erfüllt, ohne unnötige Kopplung an ein für diesen Zweck überdimensioniertes System. ## Bezug zu Archive RET-06-API Ersetzt das dort dokumentierte Provisorium (`internal/retentionapi.RequireRole`, Header-basierter Rollen-Check) — siehe `archive/docs/RET-06-API-PRUEFPROTOKOLL.md`. Die Migration von RET-06-API auf einen echten Aufruf dieses Endpunkts ist ein eigenes, kleines Folgeticket (Akzeptanzkriterium 3) — hier nicht mit umgesetzt, um RBAC-06 nicht mit Archive-Code zu vermischen (kein Umbau angrenzender Bereiche). ## Umsetzung - `internal/policyapi.RequireServiceToken` — timing-safer Service-Token-Check. - `internal/policyapi.Mount`/`authorizeHandler` — `POST /authorize`, reiner Wrapper um `enforcer.Authorize`. - `cmd/policy-api` — eigenständiger HTTP-Dienst (analog zu den bestehenden `*-devserver`n, getrennt von `cmd/core`). ## Prüfungen | # | Prüfung | Ergebnis | |---|---|---| | 1 | Endpunkt-Antwort stimmt in 100 Stichproben exakt mit dem direkten Enforcer.Authorize-Ergebnis überein | **bestanden** — `TestAuthorizeHandler_MatchesDirectEnforcerResult`: 100 Läufe über drei role/permission-Kombinationen (erlaubt, unbekannte Rolle, falsches Recht), jede einzelne HTTP-Antwort mit dem direkten Aufruf verglichen, 0 Abweichungen | | 2 | Aufruf ohne gültige Service-Credentials wird abgewiesen (401/403), nicht mit einer Policy-Entscheidung beantwortet | **bestanden** — `TestRequireServiceToken_MissingOrWrongTokenReturns401` (Handler nachweislich NIE erreicht) UND `TestRequireServiceToken_CorrectTokenAllowsAccess`; real auf 131: `curl` ohne Token → 401 | | 3 | Ein simulierter Fremd-Modul-Testclient fragt real gegen den laufenden Endpunkt ab und erhält korrekte Allow/Deny-Antworten | **bestanden** — real auf 131: `curl` mit Service-Token gegen `archive_admin`/`retention.configure` (simuliert Archive als Aufrufer) liefert korrekt `{"allowed":false}` ohne Regel und `{"allowed":true}` nach realem `Grant` | ## Echte Verdrahtung auf 192.168.1.131 - `policy-api` gebaut nach `/opt/nexarch-core/bin/` - `/etc/nexarch/policy-api.env` (0600) - `nexarch-policy-api.service` installiert/aktiviert (dauerhaft, `Restart=on-failure`) - Reale Rechtevergabe-Lücke gefunden und behoben: `policy_rules`/ `policy_rule_changes` waren auf `nexarch_registry` bereits von einem früheren Testlauf unter der Rolle `postgres` angelegt worden, `nexarch_core` hatte keine Rechte darauf — `GRANT` nachgezogen und NACHTRÄGLICH VERIFIZIERT (nicht nur ausgeführt und angenommen): `information_schema.role_table_grants` bestätigt `nexarch_core` hat dauerhaft SELECT/INSERT/UPDATE/DELETE auf `policy_rules` und SELECT/INSERT auf `policy_rule_changes` — der Endpunkt hängt NICHT von der zufälligen `postgres`-Eigentümerschaft ab, sondern von einem eigenen, geprüften Grant für die tatsächlich im Betrieb genutzte Rolle (`NEXARCH_POLICY_ADMIN_DSN` in `/etc/nexarch/policy-api.env` verwendet `nexarch_core`). Dieser Grant ist Teil des Deploy-Vorgangs, nicht Teil von RBAC-02s Migration (deren Ticket bereits Fertig ist, hier nicht nachträglich verändert) — ein künftiges Fresh-Deploy muss ihn wiederholen, dokumentiert hier als Betriebsschritt. - **Push zu Gitea vorübergehend nicht möglich**: `gitea.perlbach24.de` war zum Zeitpunkt des Commits über TCP 443/80 nicht erreichbar (Ping auf den Host erfolgreich, HTTP(S)-Ports timeout) — externer Netzwerk-/Dienstausfall, KEIN Code- oder Konfigurationsfehler dieses Tickets. Board-Status blieb bewusst auf „Backlog“, bis der Push tatsächlich durchgeführt wurde (kein Status-Flip ohne Push). - Realer End-zu-Ende-Test via `curl`: 401 ohne Token, `{"allowed":false}` für unbekannte Kombination, `{"allowed":true}` nach echtem `Grant`, Testregel anschließend entfernt ## Build/Test-Ergebnis (192.168.1.131) ``` go build ./... -> clean go vet ./... -> clean golangci-lint run ./... -> 0 issues go test ./internal/policy/... ./internal/policyapi/... ./internal/rbac/... -> alle bestanden ``` **Hinweis:** `go test ./... -p 1` auf diesem Branch zeigt Fehlschläge in `internal/tenant`/`internal/user` — reale Umgebungs-Altlasten (z. B. bereits existierende `tenant_acme`-Datenbank) aus früheren TEN-01- Testläufen dieser Session, NICHT durch RBAC-06 verursacht und nicht in dessen Scope. Alle drei von RBAC-06 tatsächlich berührten Pakete (`internal/policy`, `internal/policyapi`, `internal/rbac`) sind grün. ## Gesamtergebnis **Bestanden.** Alle drei Akzeptanzkriterien und alle drei Pflichtprüfungen real erfüllt — inklusive echtem systemd-Deploy und curl-Nachweis beider Pfade (Allow und Deny). Schließt den seit FDN-03/FDN-09 wiederkehrenden "Go-Code ohne HTTP-Schnittstelle für andere Module"-Befund für RBAC-Abfragen einmalig und wiederverwendbar.