package server import ( "context" "net/http" "net/http/httptest" "testing" "scm.bstein.dev/bstein/soteria/internal/config" ) func TestAuthorizeCoversAuthModesAndGroupChecks(t *testing.T) { t.Run("auth disabled", func(t *testing.T) { srv := &Server{cfg: &config.Config{AuthRequired: false}} req := httptest.NewRequest(http.MethodGet, "/v1/inventory", nil) identity, status, err := srv.authorize(req) if err != nil || status != http.StatusOK || identity.Authenticated { t.Fatalf("expected auth-disabled request to pass anonymously, got identity=%#v status=%d err=%v", identity, status, err) } }) t.Run("bearer token", func(t *testing.T) { srv := &Server{cfg: &config.Config{ AuthRequired: true, AuthBearerTokens: []string{"atlas-secret"}, }} req := httptest.NewRequest(http.MethodGet, "/v1/inventory", nil) req.Header.Set("Authorization", "Bearer atlas-secret") identity, status, err := srv.authorize(req) if err != nil || status != http.StatusOK || identity.User != "service-token" { t.Fatalf("expected bearer token auth, got identity=%#v status=%d err=%v", identity, status, err) } }) t.Run("forwarded headers without group restriction", func(t *testing.T) { srv := &Server{cfg: &config.Config{AuthRequired: true}} req := httptest.NewRequest(http.MethodGet, "/v1/inventory", nil) req.Header.Set("X-Forwarded-User", "brad") req.Header.Set("X-Forwarded-Email", "brad@bstein.dev") req.Header.Set("X-Forwarded-Groups", "/ops;/dev") identity, status, err := srv.authorize(req) if err != nil || status != http.StatusOK || identity.User != "brad" || len(identity.Groups) != 2 { t.Fatalf("expected forwarded header auth, got identity=%#v status=%d err=%v", identity, status, err) } }) t.Run("missing identity", func(t *testing.T) { srv := &Server{cfg: &config.Config{AuthRequired: true}} req := httptest.NewRequest(http.MethodGet, "/v1/inventory", nil) _, status, err := srv.authorize(req) if err == nil || status != http.StatusUnauthorized { t.Fatalf("expected unauthorized missing identity, got status=%d err=%v", status, err) } }) t.Run("group allowed", func(t *testing.T) { srv := &Server{cfg: &config.Config{ AuthRequired: true, AllowedGroups: []string{"admin", "ops"}, }} req := httptest.NewRequest(http.MethodGet, "/v1/inventory", nil) req.Header.Set("X-Auth-Request-User", "brad") req.Header.Set("X-Auth-Request-Groups", "/dev,/ops") identity, status, err := srv.authorize(req) if err != nil || status != http.StatusOK || identity.User != "brad" { t.Fatalf("expected allowed group auth, got identity=%#v status=%d err=%v", identity, status, err) } }) t.Run("group forbidden", func(t *testing.T) { srv := &Server{cfg: &config.Config{ AuthRequired: true, AllowedGroups: []string{"admin"}, }} req := httptest.NewRequest(http.MethodGet, "/v1/inventory", nil) req.Header.Set("X-Forwarded-User", "brad") req.Header.Set("X-Forwarded-Groups", "/dev") _, status, err := srv.authorize(req) if err == nil || status != http.StatusForbidden { t.Fatalf("expected forbidden group failure, got status=%d err=%v", status, err) } }) } func TestRequesterAndAuthzHelpers(t *testing.T) { ctx := context.WithValue(context.Background(), authContextKey, authIdentity{ Authenticated: true, User: "brad", Email: "brad@bstein.dev", }) if got := currentRequester(ctx); got != "brad" { t.Fatalf("expected requester user brad, got %q", got) } ctx = context.WithValue(context.Background(), authContextKey, authIdentity{ Authenticated: true, Email: "brad@bstein.dev", }) if got := currentRequester(ctx); got != "brad@bstein.dev" { t.Fatalf("expected requester email, got %q", got) } ctx = context.WithValue(context.Background(), authContextKey, authIdentity{Authenticated: true}) if got := currentRequester(ctx); got != "authenticated" { t.Fatalf("expected generic authenticated requester, got %q", got) } if got := currentRequester(context.Background()); got != "anonymous" { t.Fatalf("expected anonymous requester, got %q", got) } if got := authzReason(http.StatusUnauthorized, errSentinel); got != "unauthenticated" { t.Fatalf("expected unauthenticated reason, got %q", got) } if got := authzReason(http.StatusForbidden, errSentinel); got != "forbidden_group" { t.Fatalf("expected forbidden reason, got %q", got) } if got := authzReason(http.StatusBadGateway, errSentinel); got != "error" { t.Fatalf("expected generic error reason, got %q", got) } if got := authzReason(http.StatusOK, nil); got != "unknown" { t.Fatalf("expected unknown nil-error reason, got %q", got) } } func TestAuthHeaderAndGroupHelpers(t *testing.T) { req := httptest.NewRequest(http.MethodGet, "/v1/inventory", nil) req.Header.Set("X-Secondary", "second") req.Header.Set("X-Primary", " first ") if got := firstHeader(req, "X-Primary", "X-Secondary"); got != "first" { t.Fatalf("expected first populated header, got %q", got) } if got := splitGroups(" /ops, dev ; qa "); len(got) != 3 { t.Fatalf("expected split groups, got %#v", got) } if got := normalizeGroups([]string{" /ops ", "", "dev", " /qa"}); len(got) != 3 || got[0] != "ops" || got[2] != "qa" { t.Fatalf("expected normalized groups, got %#v", got) } } var errSentinel = http.ErrAbortHandler