package auth_test import ( "bytes" "context" "log/slog" "net/http" "net/http/httptest" "testing" "git.d-ma.be/mathias/gitea-mcp/internal/auth" "github.com/stretchr/testify/assert" ) func discardLogger() *slog.Logger { return slog.New(slog.NewTextHandler(bytes.NewBuffer(nil), nil)) } // Header precedence: X-Auth-Request-User (verified OIDC identity) wins over // X-Forwarded-User, and X-Forwarded-User is only a fallback when the former is // absent. func TestCallerHeaderPrecedence(t *testing.T) { tests := []struct { name string authReq string forwarded string wantCaller string }{ {"auth-request only", "mathiasbq", "", "mathiasbq"}, {"forwarded fallback", "", "fwduser", "fwduser"}, {"both present, same", "same", "same", "same"}, {"both present, differ → auth-request wins", "authuser", "fwduser", "authuser"}, {"neither", "", "", ""}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { var got string h := auth.CallerMiddleware(discardLogger(), http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) { got = auth.Caller(r.Context()) })) req := httptest.NewRequest(http.MethodPost, "/", nil) if tc.authReq != "" { req.Header.Set("X-Auth-Request-User", tc.authReq) } if tc.forwarded != "" { req.Header.Set("X-Forwarded-User", tc.forwarded) } h.ServeHTTP(httptest.NewRecorder(), req) assert.Equal(t, tc.wantCaller, got) }) } } // When both headers are present and disagree, a warning is logged so the proxy // misconfiguration is visible rather than silent. func TestCallerConflictingHeadersLogsWarning(t *testing.T) { var buf bytes.Buffer logger := slog.New(slog.NewJSONHandler(&buf, &slog.HandlerOptions{Level: slog.LevelWarn})) h := auth.CallerMiddleware(logger, http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {})) req := httptest.NewRequest(http.MethodPost, "/", nil) req.Header.Set("X-Auth-Request-User", "authuser") req.Header.Set("X-Forwarded-User", "fwduser") h.ServeHTTP(httptest.NewRecorder(), req) logged := buf.String() assert.Contains(t, logged, "conflicting") assert.Contains(t, logged, "authuser") assert.Contains(t, logged, "fwduser") // No warning when they agree. buf.Reset() req2 := httptest.NewRequest(http.MethodPost, "/", nil) req2.Header.Set("X-Auth-Request-User", "same") req2.Header.Set("X-Forwarded-User", "same") h.ServeHTTP(httptest.NewRecorder(), req2) assert.Empty(t, buf.String(), "no warning expected when headers agree") } func TestCallerEmptyWhenHeaderMissing(t *testing.T) { assert.Equal(t, "", auth.Caller(context.Background())) }