hako/internal/server/middleware/auth_test.go
2026-01-12 19:35:18 +01:00

359 lines
12 KiB
Go

package middleware
import (
"context"
"net/http"
"net/http/httptest"
"testing"
archivalDomain "git.nakama.town/fmartingr/hako/internal/archival/domain"
authDomain "git.nakama.town/fmartingr/hako/internal/auth/domain"
"git.nakama.town/fmartingr/hako/internal/server/webcontext"
"git.nakama.town/fmartingr/hako/internal/testutil"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
)
func TestAuthMiddleware_ValidToken(t *testing.T) {
ctx := context.Background()
deps := testutil.GetTestConfigurationAndDependencies(t, ctx)
// Initialize domains
deps.Dependencies.Domains().SetLinks(archivalDomain.NewLinkDomain(deps.Dependencies))
deps.Dependencies.Domains().SetArchives(archivalDomain.NewArchiveDomain(deps.Dependencies))
deps.Dependencies.Domains().SetCategories(archivalDomain.NewCategoryDomain(deps.Dependencies))
deps.Dependencies.Domains().SetAuth(authDomain.NewAuthDomain(deps.Dependencies))
// Create a test user first
user, _, err := testutil.NewTestUser(t, deps.Dependencies, "test@example.com", "testpassword123")
require.NoError(t, err)
// Generate a valid token for the user
tokenString, _, err := deps.Dependencies.GetJWTService().GenerateToken(user.ID, user.Email)
require.NoError(t, err)
// Create middleware with user store
middleware := AuthMiddleware(deps.Dependencies.GetJWTService(), deps.Dependencies.UserStore)
// Create a test handler that checks context
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
c := webcontext.NewWebContext(w, r)
ctxUserID := c.GetUserID()
userEmail := c.GetUserEmail()
userRole := c.GetUserRole()
require.Equal(t, user.ID.String(), ctxUserID)
require.Equal(t, user.Email, userEmail)
// Role should be "user" by default
require.Equal(t, "user", userRole)
w.WriteHeader(http.StatusOK)
})
// Wrap handler with middleware
wrappedHandler := middleware(handler)
// Create request with token
req := httptest.NewRequest(http.MethodGet, "/test", nil)
req.Header.Set("Authorization", "Bearer "+tokenString)
w := httptest.NewRecorder()
wrappedHandler.ServeHTTP(w, req)
require.Equal(t, http.StatusOK, w.Code)
}
func TestAuthMiddleware_NoToken(t *testing.T) {
ctx := context.Background()
deps := testutil.GetTestConfigurationAndDependencies(t, ctx)
middleware := AuthMiddleware(deps.Dependencies.GetJWTService(), deps.Dependencies.UserStore)
// Create handler
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
c := webcontext.NewWebContext(w, r)
userID := c.GetUserID()
// Should be empty without token
require.Empty(t, userID)
w.WriteHeader(http.StatusOK)
})
wrappedHandler := middleware(handler)
// Request without token
req := httptest.NewRequest(http.MethodGet, "/test", nil)
w := httptest.NewRecorder()
wrappedHandler.ServeHTTP(w, req)
// Should still succeed (middleware doesn't block)
require.Equal(t, http.StatusOK, w.Code)
}
func TestAuthMiddleware_InvalidToken(t *testing.T) {
ctx := context.Background()
deps := testutil.GetTestConfigurationAndDependencies(t, ctx)
middleware := AuthMiddleware(deps.Dependencies.GetJWTService(), deps.Dependencies.UserStore)
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
c := webcontext.NewWebContext(w, r)
userID := c.GetUserID()
// Should be empty with invalid token
require.Empty(t, userID)
w.WriteHeader(http.StatusOK)
})
wrappedHandler := middleware(handler)
// Request with invalid token
req := httptest.NewRequest(http.MethodGet, "/test", nil)
req.Header.Set("Authorization", "Bearer invalid-token")
w := httptest.NewRecorder()
wrappedHandler.ServeHTTP(w, req)
// Should still succeed (global middleware doesn't block)
require.Equal(t, http.StatusOK, w.Code)
}
func TestAuthMiddleware_UserDoesNotExist(t *testing.T) {
ctx := context.Background()
deps := testutil.GetTestConfigurationAndDependencies(t, ctx)
// Generate a token for a user that doesn't exist in the database
nonExistentUserID := uuid.New()
tokenString, _, err := deps.Dependencies.GetJWTService().GenerateToken(nonExistentUserID, "nonexistent@example.com")
require.NoError(t, err)
// Create middleware
middleware := AuthMiddleware(deps.Dependencies.GetJWTService(), deps.Dependencies.UserStore)
// Create a test handler that checks context
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
c := webcontext.NewWebContext(w, r)
ctxUserID := c.GetUserID()
userEmail := c.GetUserEmail()
// Should be empty because user doesn't exist
require.Empty(t, ctxUserID)
require.Empty(t, userEmail)
w.WriteHeader(http.StatusOK)
})
// Wrap handler with middleware
wrappedHandler := middleware(handler)
// Create request with valid token but non-existent user
req := httptest.NewRequest(http.MethodGet, "/test", nil)
req.Header.Set("Authorization", "Bearer "+tokenString)
w := httptest.NewRecorder()
wrappedHandler.ServeHTTP(w, req)
// Should still succeed (middleware doesn't block), but context should be empty
require.Equal(t, http.StatusOK, w.Code)
}
func TestRequireAuthMiddleware_Authenticated(t *testing.T) {
ctx := context.Background()
deps := testutil.GetTestConfigurationAndDependencies(t, ctx)
// Initialize domains
deps.Dependencies.Domains().SetLinks(archivalDomain.NewLinkDomain(deps.Dependencies))
deps.Dependencies.Domains().SetArchives(archivalDomain.NewArchiveDomain(deps.Dependencies))
deps.Dependencies.Domains().SetCategories(archivalDomain.NewCategoryDomain(deps.Dependencies))
deps.Dependencies.Domains().SetAuth(authDomain.NewAuthDomain(deps.Dependencies))
// Create a test user first
user, _, err := testutil.NewTestUser(t, deps.Dependencies, "test@example.com", "testpassword123")
require.NoError(t, err)
// Generate token
tokenString, _, err := deps.Dependencies.GetJWTService().GenerateToken(user.ID, user.Email)
require.NoError(t, err)
// Create both middlewares
globalAuth := AuthMiddleware(deps.Dependencies.GetJWTService(), deps.Dependencies.UserStore)
requireAuth := RequireAuthMiddleware()
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
// Apply both middlewares
wrappedHandler := globalAuth(requireAuth(handler))
req := httptest.NewRequest(http.MethodGet, "/test", nil)
req.Header.Set("Authorization", "Bearer "+tokenString)
w := httptest.NewRecorder()
wrappedHandler.ServeHTTP(w, req)
require.Equal(t, http.StatusOK, w.Code)
}
func TestRequireAuthMiddleware_NotAuthenticated(t *testing.T) {
ctx := context.Background()
deps := testutil.GetTestConfigurationAndDependencies(t, ctx)
globalAuth := AuthMiddleware(deps.Dependencies.GetJWTService(), deps.Dependencies.UserStore)
requireAuth := RequireAuthMiddleware()
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
wrappedHandler := globalAuth(requireAuth(handler))
// Request without token
req := httptest.NewRequest(http.MethodGet, "/test", nil)
w := httptest.NewRecorder()
wrappedHandler.ServeHTTP(w, req)
// Should be unauthorized
require.Equal(t, http.StatusUnauthorized, w.Code)
}
func TestRequireAuthMiddleware_UserDoesNotExist(t *testing.T) {
ctx := context.Background()
deps := testutil.GetTestConfigurationAndDependencies(t, ctx)
// Generate a token for a user that doesn't exist in the database
nonExistentUserID := uuid.New()
tokenString, _, err := deps.Dependencies.GetJWTService().GenerateToken(nonExistentUserID, "nonexistent@example.com")
require.NoError(t, err)
// Create both middlewares
globalAuth := AuthMiddleware(deps.Dependencies.GetJWTService(), deps.Dependencies.UserStore)
requireAuth := RequireAuthMiddleware()
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
// Apply both middlewares
wrappedHandler := globalAuth(requireAuth(handler))
// Request with valid token but non-existent user
req := httptest.NewRequest(http.MethodGet, "/test", nil)
req.Header.Set("Authorization", "Bearer "+tokenString)
w := httptest.NewRecorder()
wrappedHandler.ServeHTTP(w, req)
// Should be unauthorized because user doesn't exist
require.Equal(t, http.StatusUnauthorized, w.Code)
}
func TestRequireAdminMiddleware_AdminUser(t *testing.T) {
ctx := context.Background()
deps := testutil.GetTestConfigurationAndDependencies(t, ctx)
// Initialize domains
deps.Dependencies.Domains().SetLinks(archivalDomain.NewLinkDomain(deps.Dependencies))
deps.Dependencies.Domains().SetArchives(archivalDomain.NewArchiveDomain(deps.Dependencies))
deps.Dependencies.Domains().SetCategories(archivalDomain.NewCategoryDomain(deps.Dependencies))
deps.Dependencies.Domains().SetAuth(authDomain.NewAuthDomain(deps.Dependencies))
// Create a test user with admin role
user, _, err := testutil.NewTestUser(t, deps.Dependencies, "admin@example.com", "testpassword123")
require.NoError(t, err)
// Update user to admin role directly in database
// Get the write DB connection from dependencies
writeDB := deps.Dependencies.Database().WriterDB()
_, err = writeDB.Exec(
"UPDATE users SET role = 'admin' WHERE id = ?",
user.ID.String(),
)
require.NoError(t, err)
// Generate token with admin role
tokenString, _, err := deps.Dependencies.GetJWTService().GenerateTokenWithRole(user.ID, user.Email, "admin")
require.NoError(t, err)
// Create both middlewares
globalAuth := AuthMiddleware(deps.Dependencies.GetJWTService(), deps.Dependencies.UserStore)
requireAdmin := RequireAdminMiddleware()
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
// Apply both middlewares
wrappedHandler := globalAuth(requireAdmin(handler))
req := httptest.NewRequest(http.MethodGet, "/test", nil)
req.Header.Set("Authorization", "Bearer "+tokenString)
w := httptest.NewRecorder()
wrappedHandler.ServeHTTP(w, req)
// Should succeed for admin user
require.Equal(t, http.StatusOK, w.Code)
}
func TestRequireAdminMiddleware_RegularUser(t *testing.T) {
ctx := context.Background()
deps := testutil.GetTestConfigurationAndDependencies(t, ctx)
// Initialize domains
deps.Dependencies.Domains().SetLinks(archivalDomain.NewLinkDomain(deps.Dependencies))
deps.Dependencies.Domains().SetArchives(archivalDomain.NewArchiveDomain(deps.Dependencies))
deps.Dependencies.Domains().SetCategories(archivalDomain.NewCategoryDomain(deps.Dependencies))
deps.Dependencies.Domains().SetAuth(authDomain.NewAuthDomain(deps.Dependencies))
// Create a test user (defaults to "user" role)
user, _, err := testutil.NewTestUser(t, deps.Dependencies, "user@example.com", "testpassword123")
require.NoError(t, err)
// Generate token (defaults to "user" role)
tokenString, _, err := deps.Dependencies.GetJWTService().GenerateToken(user.ID, user.Email)
require.NoError(t, err)
// Create both middlewares
globalAuth := AuthMiddleware(deps.Dependencies.GetJWTService(), deps.Dependencies.UserStore)
requireAdmin := RequireAdminMiddleware()
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
// Apply both middlewares
wrappedHandler := globalAuth(requireAdmin(handler))
req := httptest.NewRequest(http.MethodGet, "/test", nil)
req.Header.Set("Authorization", "Bearer "+tokenString)
w := httptest.NewRecorder()
wrappedHandler.ServeHTTP(w, req)
// Should be forbidden for regular user
require.Equal(t, http.StatusForbidden, w.Code)
}
func TestRequireAdminMiddleware_NotAuthenticated(t *testing.T) {
ctx := context.Background()
deps := testutil.GetTestConfigurationAndDependencies(t, ctx)
globalAuth := AuthMiddleware(deps.Dependencies.GetJWTService(), deps.Dependencies.UserStore)
requireAdmin := RequireAdminMiddleware()
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
wrappedHandler := globalAuth(requireAdmin(handler))
// Request without token
req := httptest.NewRequest(http.MethodGet, "/test", nil)
w := httptest.NewRecorder()
wrappedHandler.ServeHTTP(w, req)
// Should be unauthorized
require.Equal(t, http.StatusUnauthorized, w.Code)
}