- internal/rbacclient: HTTP-Client fuer Core RBAC-06 (POST /authorize) - retentionapi.RequireRole (Header-Provisorium) ersetzt durch RequireRBAC, echter Aufruf gegen RBAC-06, fail-closed bei Fehlern - Mount nimmt jetzt rbacclient.Client entgegen - alle bestehenden RET-06-API-Tests weiterhin gruen - neue Tests: verweigerte Rolle (403 gegen echte RBAC-06-Antwort), erlaubte Rolle (200), RBAC-06 nicht erreichbar -> fail-closed (403) - real deployed auf 131, end-zu-ende per curl nachgewiesen (403/403/200/403) Pruefungen siehe archive/docs/RET-08-PRUEFPROTOKOLL.md
51 lines
2.1 KiB
Go
51 lines
2.1 KiB
Go
// 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)
|
|
}
|
|
}
|