From 422e96cc20400e0977a294fd318c9f87d0618f4d Mon Sep 17 00:00:00 2001 From: patrick Date: Thu, 29 Oct 2026 23:42:16 +0100 Subject: [PATCH] connect/statusapi: Rolle aus X-User-Role an die Policy Bisher fragte statusapi mit fester Rolle user. bearerauth setzt X-User-Role aus dem Token; ohne Header bleibt es bei user. Co-Authored-By: Claude Sonnet 5.5 --- connect/internal/statusapi/statusapi.go | 12 ++++++-- .../statusapi/statusapi_policy_test.go | 29 +++++++++++++++++++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/connect/internal/statusapi/statusapi.go b/connect/internal/statusapi/statusapi.go index 7348f108..55af4a39 100644 --- a/connect/internal/statusapi/statusapi.go +++ b/connect/internal/statusapi/statusapi.go @@ -22,8 +22,7 @@ type Server struct { // WithPolicy aktiviert RBAC-12: echte Rechtepruefung gegen Core (POST // /authorize), zusaetzlich zur bestehenden X-User-Id/X-Tenant-Slug-Pruefung. -// Rolle ist mangels eigenem Rollenkonzept in Connect fest "user" (Uebergang, -// wie beim restlichen Header-basierten Identitaetsmodell dieses Pakets). +// Die Rolle kommt aus X-User-Role (setzt bearerauth aus dem Token), sonst "user". func (s *Server) WithPolicy(client *policyclient.Client) *Server { s.policy = client return s @@ -40,7 +39,7 @@ func (s *Server) RegisterRoutes(mux *http.ServeMux) { func (s *Server) requirePermission(permission string, next func(w http.ResponseWriter, r *http.Request, tenantSlug string)) func(w http.ResponseWriter, r *http.Request, tenantSlug string) { return func(w http.ResponseWriter, r *http.Request, tenantSlug string) { if s.policy != nil { - allowed, err := s.policy.Allowed(r.Context(), tenantSlug, "user", permission) + allowed, err := s.policy.Allowed(r.Context(), tenantSlug, roleOf(r), permission) if err != nil { writeError(w, http.StatusServiceUnavailable, "berechtigungsdienst nicht erreichbar") return @@ -54,6 +53,13 @@ func (s *Server) requirePermission(permission string, next func(w http.ResponseW } } +func roleOf(r *http.Request) string { + if role := r.Header.Get("X-User-Role"); role != "" { + return role + } + return "user" +} + func (s *Server) requireIdentity(next func(w http.ResponseWriter, r *http.Request, tenantSlug string)) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if r.Header.Get("X-User-Id") == "" { diff --git a/connect/internal/statusapi/statusapi_policy_test.go b/connect/internal/statusapi/statusapi_policy_test.go index adad71f9..39a5fb8e 100644 --- a/connect/internal/statusapi/statusapi_policy_test.go +++ b/connect/internal/statusapi/statusapi_policy_test.go @@ -60,3 +60,32 @@ func TestHandleList_PolicyAllows_Passes(t *testing.T) { t.Fatalf("erwartet 200 mit berechtigung, bekam %d: %s", rec.Code, rec.Body.String()) } } + +func TestHandleList_RoleFromHeaderReachesPolicy(t *testing.T) { + var got []string + policyAPI := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var body struct { + Role string `json:"role"` + } + _ = json.NewDecoder(r.Body).Decode(&body) + got = append(got, body.Role) + _ = json.NewEncoder(w).Encode(map[string]bool{"allowed": true}) + })) + defer policyAPI.Close() + s := newTestServer(t).WithPolicy(policyclient.New(policyAPI.URL, "test-token")) + mux := http.NewServeMux() + s.RegisterRoutes(mux) + + for _, role := range []string{"tenant_admin", ""} { + req := httptest.NewRequest("GET", "/connectors/status", nil) + req.Header.Set("X-User-Id", "u1") + req.Header.Set("X-Tenant-Slug", "acme") + if role != "" { + req.Header.Set("X-User-Role", role) + } + mux.ServeHTTP(httptest.NewRecorder(), req) + } + if len(got) != 2 || got[0] != "tenant_admin" || got[1] != "user" { + t.Fatalf("erwartet [tenant_admin user], bekam %v", got) + } +} -- 2.47.3