internal/policyapi: POST /authorize wrapt policy.Enforcer.Authorize (RBAC-02) fuer physisch getrennte Module (DMS, Mail, Archive) - reiner Wrapper, keine zweite Autorisierungslogik, 100 Stichproben beweisen Uebereinstimmung mit dem direkten Enforcer-Aufruf. Service-Auth ueber schlanken, timing-safe verglichenen Token statt moduleregistrys schwererem Credential-System (unnoetige internal/flag-Abhaengigkeit, nie mit internal/policy gemergt). Ersetzt spaeter das dokumentierte RET-06-API-Provisorium (Archive, eigenes Folgeticket). Reale Rechtevergabe-Luecke auf policy_rules gefunden und behoben (Tabelle von frueherem Testlauf unter anderem Owner). Real auf 131 deployed, beide Pfade (Allow/Deny) per curl end-to-end verifiziert.
57 lines
1.9 KiB
Go
57 lines
1.9 KiB
Go
package policyapi
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"net/http"
|
|
|
|
"gitea.perlbach24.de/scripte/nexarch/internal/policy"
|
|
"gitea.perlbach24.de/scripte/nexarch/internal/rbac"
|
|
)
|
|
|
|
// Mount registriert den Autorisierungs-Endpunkt auf mux, hinter dem
|
|
// Service-Token-Check (Akzeptanzkriterium 2).
|
|
func Mount(mux *http.ServeMux, enforcer *policy.Enforcer, serviceToken string) {
|
|
mux.HandleFunc("POST /authorize", RequireServiceToken(serviceToken, authorizeHandler(enforcer)))
|
|
}
|
|
|
|
type authorizeRequest struct {
|
|
Role string `json:"role"`
|
|
Permission string `json:"permission"`
|
|
}
|
|
|
|
type authorizeResponse struct {
|
|
Allowed bool `json:"allowed"`
|
|
}
|
|
|
|
// authorizeHandler ruft AUSSCHLIESSLICH enforcer.Authorize auf — keine
|
|
// eigene, zweite Autorisierungsentscheidung (Akzeptanzkriterium 1: die
|
|
// HTTP-Antwort MUSS mit dem direkten Enforcer-Ergebnis übereinstimmen,
|
|
// das ist strukturell garantiert, weil es derselbe Aufruf ist, nicht
|
|
// nur getestet).
|
|
func authorizeHandler(enforcer *policy.Enforcer) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
var req authorizeRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
http.Error(w, "ungültiger request-body: "+err.Error(), http.StatusBadRequest)
|
|
return
|
|
}
|
|
if req.Role == "" || req.Permission == "" {
|
|
http.Error(w, "role und permission sind pflichtfelder", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
err := enforcer.Authorize(r.Context(), rbac.Role(req.Role), rbac.Permission(req.Permission))
|
|
allowed := err == nil
|
|
if err != nil && !errors.Is(err, policy.ErrDenied) {
|
|
// Ein anderer Fehler als "verboten" (z.B. DB-Fehler) ist kein
|
|
// gueltiges Allow/Deny-Ergebnis, sondern ein echter Fehlerfall.
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_ = json.NewEncoder(w).Encode(authorizeResponse{Allowed: allowed})
|
|
}
|
|
}
|