diff --git a/connect/internal/bearerauth/bearerauth.go b/connect/internal/bearerauth/bearerauth.go index 9e2d1cae..bea3e87b 100644 --- a/connect/internal/bearerauth/bearerauth.go +++ b/connect/internal/bearerauth/bearerauth.go @@ -1,7 +1,7 @@ // Package bearerauth setzt die Identitaet der Connect-APIs aus einem // verifizierten Core-Modul-Token. Die Handler lesen weiter X-User-Id und // X-Tenant-Slug; die Middleware ueberschreibt beide aus den Claims, sodass ein -// Client sie nicht frei setzen kann. +// Client sie nicht frei setzen kann. X-User-Role kommt nur aus dem Token. package bearerauth import ( @@ -16,6 +16,7 @@ import ( const ( headerUser = "X-User-Id" headerTenant = "X-Tenant-Slug" + headerRole = "X-User-Role" ) // Middleware verlangt fuer Anfragen mit Authorization: Bearer ein gueltiges @@ -23,6 +24,7 @@ const ( // allowHeaderAuth ist gesetzt (Uebergang, bis alle Clients Bearer senden). func Middleware(v *moduleauth.Verifier, allowHeaderAuth bool, next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + r.Header.Del(headerRole) token, ok := bearer(r) if !ok { if !allowHeaderAuth { @@ -47,6 +49,9 @@ func Middleware(v *moduleauth.Verifier, allowHeaderAuth bool, next http.Handler) } r.Header.Set(headerUser, claims.Subject) r.Header.Set(headerTenant, claims.Tenant) + if claims.Role != "" { + r.Header.Set(headerRole, claims.Role) + } next.ServeHTTP(w, r) }) } diff --git a/connect/internal/bearerauth/bearerauth_test.go b/connect/internal/bearerauth/bearerauth_test.go index 1df4724d..685f2fbe 100644 --- a/connect/internal/bearerauth/bearerauth_test.go +++ b/connect/internal/bearerauth/bearerauth_test.go @@ -10,7 +10,7 @@ import ( func echo() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - _, _ = w.Write([]byte(r.Header.Get("X-User-Id") + "|" + r.Header.Get("X-Tenant-Slug"))) + _, _ = w.Write([]byte(r.Header.Get("X-User-Id") + "|" + r.Header.Get("X-Tenant-Slug") + "|" + r.Header.Get("X-User-Role"))) }) } @@ -31,7 +31,7 @@ func TestBearerOverridesClientSuppliedHeaders(t *testing.T) { "Authorization": "Bearer " + iss.Token("u1", "acme", "user", "", ""), "X-User-Id": "fremd", "X-Tenant-Slug": "fremd", }) - if rec.Code != 200 || rec.Body.String() != "u1|acme" { + if rec.Code != 200 || rec.Body.String() != "u1|acme|user" { t.Fatalf("code=%d body=%q", rec.Code, rec.Body.String()) } } @@ -51,10 +51,25 @@ func TestInvalidBearerIs401(t *testing.T) { func TestWithoutBearerHeadersStrippedUnlessAllowed(t *testing.T) { v, _ := moduleauth.NewTestVerifierAndIssuer() hdr := map[string]string{"X-User-Id": "u9", "X-Tenant-Slug": "t9"} - if rec := do(Middleware(v, false, echo()), hdr); rec.Body.String() != "|" { + if rec := do(Middleware(v, false, echo()), hdr); rec.Body.String() != "||" { t.Fatalf("Header muessen entfernt werden, bekam %q", rec.Body.String()) } - if rec := do(Middleware(v, true, echo()), hdr); rec.Body.String() != "u9|t9" { + if rec := do(Middleware(v, true, echo()), hdr); rec.Body.String() != "u9|t9|" { t.Fatalf("Uebergangsmodus muss Header durchlassen, bekam %q", rec.Body.String()) } } + +func TestRoleOnlyFromToken(t *testing.T) { + v, iss := moduleauth.NewTestVerifierAndIssuer() + h := Middleware(v, true, echo()) + rec := do(h, map[string]string{ + "Authorization": "Bearer " + iss.Token("u1", "acme", "tenant_admin", "", ""), + "X-User-Role": "tenant_admin_fremd", + }) + if rec.Body.String() != "u1|acme|tenant_admin" { + t.Fatalf("Rolle muss aus dem Token kommen, bekam %q", rec.Body.String()) + } + if rec := do(h, map[string]string{"X-User-Role": "tenant_admin"}); rec.Body.String() != "||" { + t.Fatalf("Rolle ohne Token muss entfernt werden, bekam %q", rec.Body.String()) + } +}