package middleware import ( "net/http" "strings" "fotbal-club/internal/config" "fotbal-club/internal/models" "fotbal-club/pkg/utils" "github.com/gin-gonic/gin" "gorm.io/gorm" ) // JWTAuth is a middleware that checks for a valid JWT token func JWTAuth(db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { // Admin token shortcut (DEV/TEST ONLY): allow only outside production if config.AppConfig != nil && config.AppConfig.AppEnv != "production" && config.AppConfig.AdminAccessToken != "" { header := c.GetHeader("X-Admin-Token") if header != "" && header == config.AppConfig.AdminAccessToken { c.Set("userRole", "admin") c.Set("user", &models.User{Role: "admin"}) c.Next() return } } authHeader := c.GetHeader("Authorization") var tokenString string if authHeader != "" { // Extract the token from the header (format: "Bearer ") tokenParts := strings.Split(authHeader, " ") if len(tokenParts) == 2 && tokenParts[0] == "Bearer" { tokenString = tokenParts[1] } else { // If header present but malformed, reject early c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid authorization header format"}) c.Abort() return } } else { // Fallback: try HttpOnly cookie set by server if cookie, err := c.Request.Cookie("auth_token"); err == nil { tokenString = cookie.Value } else { c.JSON(http.StatusUnauthorized, gin.H{"error": "Authorization header or auth cookie is required"}) c.Abort() return } } claims, err := utils.ParseJWT(tokenString) if err != nil { c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid or expired token"}) c.Abort() return } // Check if user exists var user models.User if err := db.First(&user, claims.UserID).Error; err != nil { c.JSON(http.StatusUnauthorized, gin.H{"error": "User not found"}) c.Abort() return } // Add user information to the context c.Set("user", &user) // Also expose parsed JWT claims for helpers/utilities c.Set("claims", claims) c.Set("userID", user.ID) c.Set("userRole", user.Role) c.Next() } } // DevBypass checks for special dev header and grants admin role when not in production func DevBypass() gin.HandlerFunc { return func(c *gin.Context) { if config.AppConfig != nil && config.AppConfig.AppEnv != "production" { if strings.ToLower(c.GetHeader("X-Dev-Admin")) == "true" { c.Set("userRole", "admin") // set a placeholder user c.Set("user", &models.User{Role: "admin"}) c.Next() return } } c.Next() } } // RoleAuth is a middleware that checks if the user has the required role // Admin always has access. Editor has access to content creation (articles, activities). func RoleAuth(requiredRole string) gin.HandlerFunc { return func(c *gin.Context) { userRole, exists := c.Get("userRole") if !exists { c.JSON(http.StatusUnauthorized, gin.H{"error": "User role not found"}) c.Abort() return } // Admin always has full access if userRole == "admin" { c.Next() return } // Check if user has the required role if userRole != requiredRole { c.JSON(http.StatusForbidden, gin.H{"error": "Insufficient permissions"}) c.Abort() return } c.Next() } }