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}) } }