// Package retentionapi implementiert RET-06-API: die Backend-Seite der // Aufbewahrungsfristen-Konfigurationsoberfläche (CRUD auf // Aufbewahrungsklassen + Vorschauliste ablaufender Objekte). Das // Next.js-Frontend selbst ist NICHT Teil dieses Tickets (Board- // Entscheidung: Backend-API zuerst, Frontend als eigenes Folgeticket). package retentionapi import ( "errors" "net/http" "gitea.perlbach24.de/scripte/nexarch/archive/internal/rbacclient" ) // requestedRoleHeader trägt die vom Aufrufer beanspruchte Rolle. Die // eigentliche Autorisierungsentscheidung trifft NICHT dieser Header, // sondern Core RBAC-06 (siehe RequireRBAC) — der Header ist nur die // Behauptung, RBAC-06 prüft sie gegen die echten Policy-Rules. const requestedRoleHeader = "X-User-Role" // RequireRBAC ist RET-08: die Ablösung des früheren, header-basierten // Provisoriums (siehe archive/docs/RET-06-API-PRUEFPROTOKOLL.md) durch // einen echten Aufruf von Core RBAC-06 (POST /authorize). Fail-closed: // jeder Fehler beim Aufruf von RBAC-06 (Transport, Timeout, Ablehnung) // führt zur Verweigerung, niemals zu stillschweigendem Zugriff. func RequireRBAC(client *rbacclient.Client, permission string, next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { role := r.Header.Get(requestedRoleHeader) if role == "" { http.Error(w, "zugriff verweigert: header "+requestedRoleHeader+" erforderlich", http.StatusForbidden) return } allowed, err := client.Authorize(r.Context(), role, permission) if err != nil { if errors.Is(err, rbacclient.ErrDenied) { http.Error(w, "zugriff verweigert: rolle "+role+" hat kein recht "+permission, http.StatusForbidden) return } // RBAC-06 nicht erreichbar oder fehlerhafte Antwort: // fail-closed, kein Fallback auf offenen Zugriff. http.Error(w, "zugriff verweigert: autorisierungsdienst nicht erreichbar", http.StatusForbidden) return } if !allowed { http.Error(w, "zugriff verweigert: rolle "+role+" hat kein recht "+permission, http.StatusForbidden) return } next(w, r) } }