// SPDX-License-Identifier: GPL-2.0-or-later // Copyright (C) 2026 Bryan Joshua Pedini package main import ( "context" "errors" "net/http" "strings" "sync" "testing" ) // fakeLookup is an in-memory TokenLookup type fakeLookup struct { mu sync.Mutex tokens map[string]Token err error calls []string } func (f *fakeLookup) GetToken(ctx context.Context, value string) (Token, error) { f.mu.Lock() defer f.mu.Unlock() f.calls = append(f.calls, value) if f.err != nil { return Token{}, f.err } tok, ok := f.tokens[value] if !ok { return Token{}, ErrTokenNotFound } return tok, nil } func newFakeLookup() *fakeLookup { return &fakeLookup{tokens: map[string]Token{ "good": {Token: "good", Active: true}, "Good": {Token: "Good", Active: true}, "inactive": {Token: "inactive", Active: false}, }} } func headerWith(values ...string) http.Header { h := http.Header{} for _, v := range values { h.Add("Authorization", v) } return h } func TestParseAuthorization(t *testing.T) { t.Parallel() cases := []struct { name string header http.Header token string class string }{ {"missing", http.Header{}, "", authMissingHeader}, {"other headers only", http.Header{"X-Authorization": {"Bearer good"}}, "", authMissingHeader}, {"empty", headerWith(""), "", authEmptyHeader}, {"multiple", headerWith("Bearer good", "Bearer good"), "", authMultipleHeaders}, {"multiple with empty", headerWith("", "Bearer good"), "", authMultipleHeaders}, {"basic", headerWith("Basic Z29vZDo="), "", authBadScheme}, {"token scheme", headerWith("Token good"), "", authBadScheme}, {"no space", headerWith("Bearergood"), "", authBadScheme}, {"tab separator", headerWith("Bearer\tgood"), "", authBadScheme}, {"leading space", headerWith(" Bearer good"), "", authBadScheme}, {"bare token", headerWith("good"), "", authBadScheme}, {"unicode scheme", headerWith("Beаrer good"), "", authBadScheme}, // Cyrillic a {"scheme only", headerWith("Bearer"), "", authEmptyToken}, {"scheme and space", headerWith("Bearer "), "", authEmptyToken}, {"ok", headerWith("Bearer good"), "good", ""}, {"lowercase scheme", headerWith("bearer good"), "good", ""}, {"uppercase scheme", headerWith("BEARER good"), "good", ""}, {"double space kept", headerWith("Bearer good"), " good", ""}, {"trailing kept", headerWith("Bearer good "), "good ", ""}, {"extra words kept", headerWith("Bearer good extra"), "good extra", ""}, {"comma kept", headerWith("Bearer good, Bearer other"), "good, Bearer other", ""}, } for _, c := range cases { token, class := parseAuthorization(c.header) if token != c.token || class != c.class { t.Errorf("%s: got (%q, %q), want (%q, %q)", c.name, token, class, c.token, c.class) } } } func TestAuthenticate(t *testing.T) { t.Parallel() cases := []struct { name string header http.Header class string token string }{ {"missing", http.Header{}, authMissingHeader, ""}, {"empty", headerWith(""), authEmptyHeader, ""}, {"wrong scheme", headerWith("Basic good"), authBadScheme, ""}, {"empty bearer", headerWith("Bearer "), authEmptyToken, ""}, {"malformed bearer", headerWith("Bearer good"), authUnknownToken, ""}, {"multiple", headerWith("Bearer good", "Bearer good"), authMultipleHeaders, ""}, {"unknown", headerWith("Bearer nope"), authUnknownToken, ""}, {"case changed", headerWith("Bearer GOOD"), authUnknownToken, ""}, {"near match", headerWith("Bearer goo"), authUnknownToken, ""}, {"inactive", headerWith("Bearer inactive"), authInactiveToken, ""}, {"valid", headerWith("Bearer good"), authOK, "good"}, {"valid case-distinct token", headerWith("bearer Good"), authOK, "Good"}, } lookup := newFakeLookup() for _, c := range cases { tok, class, err := authenticate(context.Background(), traceDiscard(), lookup, c.header) if err != nil || class != c.class || tok.Token != c.token { t.Errorf("%s: got (%q, %q, %v), want (%q, %q)", c.name, tok.Token, class, err, c.token, c.class) } } } func TestAuthenticateLookupError(t *testing.T) { t.Parallel() lookup := &fakeLookup{err: errors.New("database is locked")} _, class, err := authenticate(context.Background(), traceDiscard(), lookup, headerWith("Bearer good")) if err == nil || class != authLookupError { t.Fatalf("got (%q, %v)", class, err) } } func TestAuthenticateDoesNotLookUpMalformedHeaders(t *testing.T) { t.Parallel() lookup := newFakeLookup() for _, h := range []http.Header{{}, headerWith(""), headerWith("Basic x"), headerWith("Bearer "), headerWith("Bearer a", "Bearer b")} { authenticate(context.Background(), traceDiscard(), lookup, h) } if len(lookup.calls) != 0 { t.Fatalf("lookups performed: %q", lookup.calls) } } func TestAuthenticationClassesAreFixedStrings(t *testing.T) { t.Parallel() for _, c := range []string{authMissingHeader, authEmptyHeader, authMultipleHeaders, authBadScheme, authEmptyToken, authUnknownToken, authInactiveToken, authLookupError, authOK} { if c == "" || strings.ContainsAny(c, " =\"") { t.Errorf("class %q is not a plain identifier", c) } } }