127 lines
4.6 KiB
Go
127 lines
4.6 KiB
Go
|
|
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)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
var errSentinel = http.ErrAbortHandler
|