test: Add RequireAuth tests for API package

Add comprehensive tests for the RequireAuth middleware covering:
- No auth configured (allows access by design)
- API-only mode (rejects requests without token)
- API-only mode (accepts valid X-API-Token)
- Basic auth with invalid credentials
- Basic auth JSON vs plain text error responses
- Valid basic auth (allowed)
- Proxy auth (allowed)
- Proxy auth with invalid secret (rejected)
- Bearer token with basic auth configured (allowed)
- Invalid Bearer token (rejected)

Coverage: RequireAuth 7.1% → 78.6%
Coverage: CheckAuth 66.9% → 69.1%
Coverage: API package 31.9% → 32.1%
This commit is contained in:
rcourtman 2025-12-02 13:09:48 +00:00
parent 1073c511b3
commit d970055532

View file

@ -1793,3 +1793,312 @@ func TestRequireAdmin_NoProxyAuthAuthenticatedAllowed(t *testing.T) {
t.Errorf("RequireAdmin returned status %d, want %d", w.Code, http.StatusOK)
}
}
// RequireAuth tests
func TestRequireAuth_NoAuthConfiguredAllowsAccess(t *testing.T) {
// When no auth is configured at all, CheckAuth returns true (allows access)
cfg := &config.Config{}
handlerCalled := false
handler := RequireAuth(cfg, func(w http.ResponseWriter, r *http.Request) {
handlerCalled = true
w.WriteHeader(http.StatusOK)
})
w := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/api/test", nil)
handler(w, req)
if !handlerCalled {
t.Error("RequireAuth should call handler when no auth is configured")
}
if w.Code != http.StatusOK {
t.Errorf("RequireAuth returned status %d, want %d", w.Code, http.StatusOK)
}
}
func TestRequireAuth_APIOnlyModeRejectsNoToken(t *testing.T) {
// When only API tokens are configured, requests without token should be rejected
rawToken := "test-api-token-12345"
record, _ := config.NewAPITokenRecord(rawToken, "test-token", []string{"read"})
cfg := &config.Config{
APITokens: []config.APITokenRecord{*record},
}
handlerCalled := false
handler := RequireAuth(cfg, func(w http.ResponseWriter, r *http.Request) {
handlerCalled = true
})
w := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/api/test", nil)
// No token provided
handler(w, req)
if handlerCalled {
t.Error("RequireAuth should not call handler without API token in API-only mode")
}
if w.Code != http.StatusUnauthorized {
t.Errorf("RequireAuth returned status %d, want %d", w.Code, http.StatusUnauthorized)
}
}
func TestRequireAuth_APIOnlyModeAcceptsValidToken(t *testing.T) {
rawToken := "test-api-token-12345"
record, _ := config.NewAPITokenRecord(rawToken, "test-token", []string{"read"})
cfg := &config.Config{
APITokens: []config.APITokenRecord{*record},
}
handlerCalled := false
handler := RequireAuth(cfg, func(w http.ResponseWriter, r *http.Request) {
handlerCalled = true
w.WriteHeader(http.StatusOK)
})
w := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/api/test", nil)
req.Header.Set("X-API-Token", rawToken)
handler(w, req)
if !handlerCalled {
t.Error("RequireAuth should call handler with valid API token")
}
if w.Code != http.StatusOK {
t.Errorf("RequireAuth returned status %d, want %d", w.Code, http.StatusOK)
}
}
func TestRequireAuth_InvalidBasicAuthRejectsRequest(t *testing.T) {
hashedPass, _ := auth.HashPassword("password123")
cfg := &config.Config{
AuthUser: "testuser",
AuthPass: hashedPass,
}
handlerCalled := false
handler := RequireAuth(cfg, func(w http.ResponseWriter, r *http.Request) {
handlerCalled = true
})
w := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/api/test", nil)
req.SetBasicAuth("testuser", "wrongpassword")
handler(w, req)
if handlerCalled {
t.Error("RequireAuth should not call handler with invalid credentials")
}
if w.Code != http.StatusUnauthorized {
t.Errorf("RequireAuth returned status %d, want %d", w.Code, http.StatusUnauthorized)
}
}
func TestRequireAuth_InvalidBasicAuthAPIPathReturnsJSON(t *testing.T) {
hashedPass, _ := auth.HashPassword("password123")
cfg := &config.Config{
AuthUser: "testuser",
AuthPass: hashedPass,
}
handler := RequireAuth(cfg, func(w http.ResponseWriter, r *http.Request) {})
w := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/api/test", nil)
req.SetBasicAuth("testuser", "wrongpassword")
handler(w, req)
if w.Code != http.StatusUnauthorized {
t.Errorf("RequireAuth returned status %d, want %d", w.Code, http.StatusUnauthorized)
}
if ct := w.Header().Get("Content-Type"); ct != "application/json" {
t.Errorf("RequireAuth Content-Type = %q, want application/json", ct)
}
if body := w.Body.String(); !strings.Contains(body, "Authentication required") {
t.Errorf("RequireAuth body = %q, want to contain 'Authentication required'", body)
}
}
func TestRequireAuth_InvalidBasicAuthAcceptJSONReturnsJSON(t *testing.T) {
hashedPass, _ := auth.HashPassword("password123")
cfg := &config.Config{
AuthUser: "testuser",
AuthPass: hashedPass,
}
handler := RequireAuth(cfg, func(w http.ResponseWriter, r *http.Request) {})
w := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/test", nil)
req.Header.Set("Accept", "application/json")
req.SetBasicAuth("testuser", "wrongpassword")
handler(w, req)
if w.Code != http.StatusUnauthorized {
t.Errorf("RequireAuth returned status %d, want %d", w.Code, http.StatusUnauthorized)
}
if ct := w.Header().Get("Content-Type"); ct != "application/json" {
t.Errorf("RequireAuth Content-Type = %q, want application/json", ct)
}
}
func TestRequireAuth_InvalidBasicAuthNonAPIReturnsPlainText(t *testing.T) {
hashedPass, _ := auth.HashPassword("password123")
cfg := &config.Config{
AuthUser: "testuser",
AuthPass: hashedPass,
}
handler := RequireAuth(cfg, func(w http.ResponseWriter, r *http.Request) {})
w := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/test", nil)
req.SetBasicAuth("testuser", "wrongpassword")
handler(w, req)
if w.Code != http.StatusUnauthorized {
t.Errorf("RequireAuth returned status %d, want %d", w.Code, http.StatusUnauthorized)
}
// Should be plain text error
body := w.Body.String()
if !strings.Contains(body, "Unauthorized") {
t.Errorf("RequireAuth body = %q, want to contain 'Unauthorized'", body)
}
}
func TestRequireAuth_ValidBasicAuthAllowsAccess(t *testing.T) {
hashedPass, _ := auth.HashPassword("password123")
cfg := &config.Config{
AuthUser: "testuser",
AuthPass: hashedPass,
}
handlerCalled := false
handler := RequireAuth(cfg, func(w http.ResponseWriter, r *http.Request) {
handlerCalled = true
w.WriteHeader(http.StatusOK)
})
w := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/api/test", nil)
req.SetBasicAuth("testuser", "password123")
handler(w, req)
if !handlerCalled {
t.Error("RequireAuth should call handler for basic auth authenticated user")
}
if w.Code != http.StatusOK {
t.Errorf("RequireAuth returned status %d, want %d", w.Code, http.StatusOK)
}
}
func TestRequireAuth_ProxyAuthAllowsAccess(t *testing.T) {
cfg := &config.Config{
ProxyAuthSecret: "secret123",
ProxyAuthUserHeader: "X-Remote-User",
}
handlerCalled := false
handler := RequireAuth(cfg, func(w http.ResponseWriter, r *http.Request) {
handlerCalled = true
w.WriteHeader(http.StatusOK)
})
w := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/api/test", nil)
req.Header.Set("X-Proxy-Secret", "secret123")
req.Header.Set("X-Remote-User", "proxyuser")
handler(w, req)
if !handlerCalled {
t.Error("RequireAuth should call handler for proxy authenticated user")
}
if w.Code != http.StatusOK {
t.Errorf("RequireAuth returned status %d, want %d", w.Code, http.StatusOK)
}
}
func TestRequireAuth_ProxyAuthInvalidSecretRejects(t *testing.T) {
cfg := &config.Config{
ProxyAuthSecret: "secret123",
ProxyAuthUserHeader: "X-Remote-User",
}
handlerCalled := false
handler := RequireAuth(cfg, func(w http.ResponseWriter, r *http.Request) {
handlerCalled = true
})
w := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/api/test", nil)
req.Header.Set("X-Proxy-Secret", "wrong-secret")
req.Header.Set("X-Remote-User", "proxyuser")
handler(w, req)
if handlerCalled {
t.Error("RequireAuth should not call handler with invalid proxy secret")
}
if w.Code != http.StatusUnauthorized {
t.Errorf("RequireAuth returned status %d, want %d", w.Code, http.StatusUnauthorized)
}
}
func TestRequireAuth_BearerTokenAllowsAccess(t *testing.T) {
// Bearer tokens are only checked when basic auth is also configured
// (not in API-only mode)
rawToken := "test-bearer-token-12345"
record, _ := config.NewAPITokenRecord(rawToken, "bearer-token", []string{"read"})
hashedPass, _ := auth.HashPassword("password123")
cfg := &config.Config{
AuthUser: "testuser",
AuthPass: hashedPass,
APITokens: []config.APITokenRecord{*record},
}
handlerCalled := false
handler := RequireAuth(cfg, func(w http.ResponseWriter, r *http.Request) {
handlerCalled = true
w.WriteHeader(http.StatusOK)
})
w := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/api/test", nil)
req.Header.Set("Authorization", "Bearer "+rawToken)
handler(w, req)
if !handlerCalled {
t.Error("RequireAuth should call handler with valid Bearer token")
}
if w.Code != http.StatusOK {
t.Errorf("RequireAuth returned status %d, want %d", w.Code, http.StatusOK)
}
}
func TestRequireAuth_InvalidBearerTokenRejects(t *testing.T) {
// Bearer tokens are only checked when basic auth is also configured
rawToken := "test-bearer-token-12345"
record, _ := config.NewAPITokenRecord(rawToken, "bearer-token", []string{"read"})
hashedPass, _ := auth.HashPassword("password123")
cfg := &config.Config{
AuthUser: "testuser",
AuthPass: hashedPass,
APITokens: []config.APITokenRecord{*record},
}
handlerCalled := false
handler := RequireAuth(cfg, func(w http.ResponseWriter, r *http.Request) {
handlerCalled = true
})
w := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/api/test", nil)
req.Header.Set("Authorization", "Bearer invalid-token")
handler(w, req)
if handlerCalled {
t.Error("RequireAuth should not call handler with invalid Bearer token")
}
if w.Code != http.StatusUnauthorized {
t.Errorf("RequireAuth returned status %d, want %d", w.Code, http.StatusUnauthorized)
}
}