Replaces the shared GITEA_MCP_DEFAULT_TOKEN for all callers. When a request's bearer validates directly against Gitea's own /api/v1/user, that token is used for every upstream call this request makes instead of the service PAT, and the caller identity comes from Gitea's own login rather than the proxy header. Any other bearer (static token, JWT, none) falls through unchanged to the existing chassis auth. Prep: Authentik now SSOs into Gitea (infra a32801c), so each real user can mint their own PAT from their own linked account. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
83 lines
2.5 KiB
Go
83 lines
2.5 KiB
Go
package auth_test
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"git.d-ma.be/mathias/gitea-mcp/internal/auth"
|
|
"git.d-ma.be/mathias/gitea-mcp/internal/gitea"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
type fakeValidator struct {
|
|
login string
|
|
ok bool
|
|
calls int
|
|
}
|
|
|
|
func (f *fakeValidator) ValidateToken(_ context.Context, _ string) (string, bool) {
|
|
f.calls++
|
|
return f.login, f.ok
|
|
}
|
|
|
|
func TestPassthroughMiddleware_ValidPATGoesStraightToOnValid(t *testing.T) {
|
|
validator := &fakeValidator{login: "alice", ok: true}
|
|
var gotCaller string
|
|
var gotToken string
|
|
var gotOK bool
|
|
onValid := http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) {
|
|
gotCaller = auth.Caller(r.Context())
|
|
gotToken, gotOK = gitea.TokenFromContext(r.Context())
|
|
})
|
|
fallback := http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {
|
|
t.Fatal("fallback should not be called for a valid PAT")
|
|
})
|
|
|
|
h := auth.PassthroughMiddleware(validator, onValid, fallback)
|
|
req := httptest.NewRequest(http.MethodPost, "/mcp", nil)
|
|
req.Header.Set("Authorization", "Bearer alices-pat")
|
|
h.ServeHTTP(httptest.NewRecorder(), req)
|
|
|
|
assert.Equal(t, "alice", gotCaller)
|
|
assert.True(t, gotOK)
|
|
assert.Equal(t, "alices-pat", gotToken)
|
|
}
|
|
|
|
func TestPassthroughMiddleware_InvalidTokenFallsThrough(t *testing.T) {
|
|
validator := &fakeValidator{ok: false}
|
|
fallbackCalled := false
|
|
onValid := http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {
|
|
t.Fatal("onValid should not be called for an invalid token")
|
|
})
|
|
fallback := http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {
|
|
fallbackCalled = true
|
|
})
|
|
|
|
h := auth.PassthroughMiddleware(validator, onValid, fallback)
|
|
req := httptest.NewRequest(http.MethodPost, "/mcp", nil)
|
|
req.Header.Set("Authorization", "Bearer not-a-gitea-pat")
|
|
h.ServeHTTP(httptest.NewRecorder(), req)
|
|
|
|
assert.True(t, fallbackCalled)
|
|
}
|
|
|
|
func TestPassthroughMiddleware_NoBearerFallsThroughWithoutCallingValidator(t *testing.T) {
|
|
validator := &fakeValidator{ok: true, login: "alice"}
|
|
fallbackCalled := false
|
|
onValid := http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {
|
|
t.Fatal("onValid should not be called with no Authorization header")
|
|
})
|
|
fallback := http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {
|
|
fallbackCalled = true
|
|
})
|
|
|
|
h := auth.PassthroughMiddleware(validator, onValid, fallback)
|
|
req := httptest.NewRequest(http.MethodPost, "/mcp", nil)
|
|
h.ServeHTTP(httptest.NewRecorder(), req)
|
|
|
|
assert.True(t, fallbackCalled)
|
|
assert.Equal(t, 0, validator.calls, "validator should not be invoked when there's no bearer to check")
|
|
}
|