package middleware import ( "eslogad-be/internal/appcontext" "net/http" "strings" "eslogad-be/internal/constants" "eslogad-be/internal/contract" "eslogad-be/internal/logger" "github.com/gin-gonic/gin" ) type AuthMiddleware struct { authService AuthValidateService } func NewAuthMiddleware(authService AuthValidateService) *AuthMiddleware { return &AuthMiddleware{ authService: authService, } } func (m *AuthMiddleware) RequireAuth() gin.HandlerFunc { return func(c *gin.Context) { token := m.extractTokenFromHeader(c) if token == "" { logger.FromContext(c.Request.Context()).Error("AuthMiddleware::RequireAuth -> Missing authorization token") m.sendErrorResponse(c, "Authorization token is required", http.StatusUnauthorized) c.Abort() return } userResponse, err := m.authService.ValidateToken(token) if err != nil { logger.FromContext(c.Request.Context()).WithError(err).Error("AuthMiddleware::RequireAuth -> Invalid token") m.sendErrorResponse(c, "Invalid or expired token", http.StatusUnauthorized) c.Abort() return } setKeyInContext(c, appcontext.UserIDKey, userResponse.ID.String()) if roles, perms, err := m.authService.ExtractAccess(token); err == nil { c.Set("user_roles", roles) c.Set("user_permissions", perms) } logger.FromContext(c.Request.Context()).Infof("AuthMiddleware::RequireAuth -> User authenticated: %s", userResponse.Email) c.Next() } } func (m *AuthMiddleware) RequireRole(allowedRoles ...string) gin.HandlerFunc { return func(c *gin.Context) { appCtx := appcontext.FromGinContext(c.Request.Context()) hasRequiredRole := false for _, role := range allowedRoles { if appCtx.UserRole == role { hasRequiredRole = true break } } if !hasRequiredRole { m.sendErrorResponse(c, "Insufficient permissions", http.StatusForbidden) c.Abort() return } c.Next() } } func (m *AuthMiddleware) RequireAdminOrManager() gin.HandlerFunc { return m.RequireRole("admin", "manager") } func (m *AuthMiddleware) RequireAdmin() gin.HandlerFunc { return m.RequireRole("admin") } func (m *AuthMiddleware) RequireSuperAdmin() gin.HandlerFunc { return m.RequireRole("superadmin") } func (m *AuthMiddleware) RequireActiveUser() gin.HandlerFunc { return func(c *gin.Context) { userResponse, exists := c.Get("user") if !exists { logger.FromContext(c.Request.Context()).Error("AuthMiddleware::RequireActiveUser -> User not authenticated") m.sendErrorResponse(c, "Authentication required", http.StatusUnauthorized) c.Abort() return } user, ok := userResponse.(*contract.UserResponse) if !ok { logger.FromContext(c.Request.Context()).Error("AuthMiddleware::RequireActiveUser -> Invalid user context") m.sendErrorResponse(c, "Invalid user context", http.StatusInternalServerError) c.Abort() return } if !user.IsActive { logger.FromContext(c.Request.Context()).Errorf("AuthMiddleware::RequireActiveUser -> User account is deactivated: %s", user.Email) m.sendErrorResponse(c, "User account is deactivated", http.StatusForbidden) c.Abort() return } logger.FromContext(c.Request.Context()).Infof("AuthMiddleware::RequireActiveUser -> Active user check passed: %s", user.Email) c.Next() } } func (m *AuthMiddleware) RequirePermissions(required ...string) gin.HandlerFunc { return func(c *gin.Context) { if _, exists := c.Get("user_permissions"); !exists { m.sendErrorResponse(c, "Authentication required", http.StatusUnauthorized) c.Abort() return } permIface, _ := c.Get("user_permissions") perms, _ := permIface.([]string) userPerms := map[string]bool{} for _, code := range perms { userPerms[code] = true } for _, need := range required { if !userPerms[need] { m.sendErrorResponse(c, "Insufficient permissions", http.StatusForbidden) c.Abort() return } } c.Next() } } func (m *AuthMiddleware) extractTokenFromHeader(c *gin.Context) string { authHeader := c.GetHeader("Authorization") if authHeader == "" { return "" } parts := strings.Split(authHeader, " ") if len(parts) != 2 || parts[0] != "Bearer" { return "" } return parts[1] } func (m *AuthMiddleware) sendErrorResponse(c *gin.Context, message string, statusCode int) { errorResponse := &contract.ErrorResponse{ Error: "auth_error", Message: message, Code: statusCode, Details: map[string]interface{}{ "entity": constants.AuthHandlerEntity, }, } c.JSON(statusCode, errorResponse) }