RET-08: ret-06-api auf rbac-06 migrieren
- 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
This commit is contained in:
@@ -6,43 +6,45 @@
|
||||
package retentionapi
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"gitea.perlbach24.de/scripte/nexarch/archive/internal/rbacclient"
|
||||
)
|
||||
|
||||
// requiredRoleHeader ist der Header-Name des PROVISORISCHEN Rollen-
|
||||
// Checks (siehe RequireRole-Dokumentation).
|
||||
const requiredRoleHeader = "X-Admin-Roles"
|
||||
// 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"
|
||||
|
||||
// RequireRole ist ein PROVISORISCHER Rollen-Check, KEIN RBAC-02-Aufruf.
|
||||
//
|
||||
// Core RBAC-02 (internal/policy, Enforcer.Authorize) ist reiner
|
||||
// Go-Code innerhalb des Core-Moduls, hat keine HTTP-Schnittstelle, über
|
||||
// die Archive (physisch getrenntes Go-Modul) es aufrufen könnte —
|
||||
// derselbe "gefunden, aber nicht modulübergreifend verdrahtet"-Befund
|
||||
// wie bei Core FDN-03/FDN-09. Bis ein Core-seitiger HTTP-Wrapper um
|
||||
// RBAC-02 existiert (eigenes, künftiges Core-Ticket, z. B. RBAC-06),
|
||||
// prüft dieser Middleware NUR einen einfachen, selbst gesetzten Header
|
||||
// (`X-Admin-Roles`, kommagetrennt) auf das Vorhandensein der
|
||||
// geforderten Rolle — KEINE echte Autorisierung gegen Core, leicht zu
|
||||
// fälschen von jedem, der den Header selbst setzen kann. Muss ersetzt
|
||||
// werden, sobald der Core-HTTP-Wrapper existiert.
|
||||
func RequireRole(requiredRole string, next http.HandlerFunc) http.HandlerFunc {
|
||||
// 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) {
|
||||
roles := r.Header.Get(requiredRoleHeader)
|
||||
if !containsRole(roles, requiredRole) {
|
||||
http.Error(w, "zugriff verweigert: rolle "+requiredRole+" erforderlich (provisorischer check, siehe RequireRole)", http.StatusForbidden)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
func containsRole(commaSeparated, role string) bool {
|
||||
for _, r := range strings.Split(commaSeparated, ",") {
|
||||
if strings.TrimSpace(r) == role {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user