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) }