From a103a654f749577c89a995d8e4894ef0d933913c Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Tue, 31 Mar 2026 18:03:14 -0300 Subject: [PATCH] service: accept forwarded groups from oauth2-proxy --- pkg/service/server.go | 30 +++++++++++++++++++++++------- pkg/service/server_test.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/pkg/service/server.go b/pkg/service/server.go index 38a183e..0a731a2 100644 --- a/pkg/service/server.go +++ b/pkg/service/server.go @@ -160,22 +160,20 @@ func (a *App) withUIAuth(next http.HandlerFunc) http.HandlerFunc { } func (a *App) authorize(r *http.Request) (userContext, bool) { - user := strings.TrimSpace(r.Header.Get("X-Auth-Request-User")) - if user == "" { - user = strings.TrimSpace(r.Header.Get("X-Forwarded-User")) - } + user := firstNonEmptyHeader(r, "X-Auth-Request-User", "X-Forwarded-User", "X-Auth-Request-Email", "X-Forwarded-Email") if user == "" { return userContext{}, false } - groups := splitHeaderList(r.Header.Get("X-Auth-Request-Groups")) + groups := splitHeaderList(firstNonEmptyHeader(r, "X-Auth-Request-Groups", "X-Forwarded-Groups")) + normalizedUser := normalizeUserValue(user) for _, allowedUser := range a.settings.AllowedUsers { - if allowedUser == user { + if normalizeUserValue(allowedUser) == normalizedUser { return userContext{Name: user, Groups: groups}, true } } for _, group := range groups { for _, allowed := range a.settings.AllowedGroups { - if group == allowed { + if normalizeGroupValue(group) == normalizeGroupValue(allowed) { return userContext{Name: user, Groups: groups}, true } } @@ -183,6 +181,15 @@ func (a *App) authorize(r *http.Request) (userContext, bool) { return userContext{Name: user, Groups: groups}, false } +func firstNonEmptyHeader(r *http.Request, keys ...string) string { + for _, key := range keys { + if value := strings.TrimSpace(r.Header.Get(key)); value != "" { + return value + } + } + return "" +} + func splitHeaderList(raw string) []string { if strings.TrimSpace(raw) == "" { return nil @@ -198,6 +205,15 @@ func splitHeaderList(raw string) []string { return out } +func normalizeUserValue(raw string) string { + return strings.ToLower(strings.TrimSpace(raw)) +} + +func normalizeGroupValue(raw string) string { + value := strings.ToLower(strings.TrimSpace(raw)) + return strings.TrimPrefix(value, "/") +} + func requestValue(r *http.Request, key string) string { if err := r.ParseForm(); err == nil { if value := strings.TrimSpace(r.Form.Get(key)); value != "" { diff --git a/pkg/service/server_test.go b/pkg/service/server_test.go index dbc5a9f..8629109 100644 --- a/pkg/service/server_test.go +++ b/pkg/service/server_test.go @@ -36,6 +36,34 @@ func TestUIAuthGuardsState(t *testing.T) { } } +func TestUIAuthAcceptsForwardedSlashGroups(t *testing.T) { + app := newTestApp(t) + handler := app.Handler() + + req := httptest.NewRequest(http.MethodGet, "/api/state", nil) + req.Header.Set("X-Forwarded-User", "brad") + req.Header.Set("X-Forwarded-Groups", "/admin,/ops") + resp := httptest.NewRecorder() + handler.ServeHTTP(resp, req) + if resp.Code != http.StatusOK { + t.Fatalf("expected ok, got %d: %s", resp.Code, resp.Body.String()) + } +} + +func TestUIAuthAcceptsForwardedEmailForAllowedUser(t *testing.T) { + app := newTestApp(t) + app.settings.AllowedUsers = []string{"brad.stein@gmail.com"} + handler := app.Handler() + + req := httptest.NewRequest(http.MethodGet, "/api/state", nil) + req.Header.Set("X-Forwarded-Email", "Brad.Stein@gmail.com") + resp := httptest.NewRecorder() + handler.ServeHTTP(resp, req) + if resp.Code != http.StatusOK { + t.Fatalf("expected ok, got %d: %s", resp.Code, resp.Body.String()) + } +} + func TestInternalSnapshotAndWatch(t *testing.T) { app := newTestApp(t) handler := app.Handler()