package middleware import ( "net/http" "strings" "github.com/gin-gonic/gin" "github.com/yourusername/victorialogs-manager/internal/models" "github.com/yourusername/victorialogs-manager/internal/utils" ) // AuthMiddleware creates a middleware for JWT authentication func AuthMiddleware(jwtManager *utils.JWTManager) gin.HandlerFunc { return func(c *gin.Context) { // Get Authorization header authHeader := c.GetHeader("Authorization") if authHeader == "" { c.JSON(http.StatusUnauthorized, gin.H{ "error": "unauthorized", "message": "missing authorization header", }) c.Abort() return } // Extract token from header token, err := utils.ExtractTokenFromHeader(authHeader) if err != nil { c.JSON(http.StatusUnauthorized, gin.H{ "error": "unauthorized", "message": "invalid authorization header format", }) c.Abort() return } // Validate token claims, err := jwtManager.ValidateToken(token) if err != nil { c.JSON(http.StatusUnauthorized, gin.H{ "error": "unauthorized", "message": "invalid or expired token", }) c.Abort() return } // Set user information in context c.Set("user_id", claims.UserID) c.Set("username", claims.Username) c.Set("role", claims.Role) c.Set("permissions", claims.Permissions) c.Next() } } // RequirePermission creates a middleware that checks for a specific permission func RequirePermission(permission models.Permission) gin.HandlerFunc { return func(c *gin.Context) { // Get permissions from context permissionsInterface, exists := c.Get("permissions") if !exists { c.JSON(http.StatusUnauthorized, gin.H{ "error": "unauthorized", "message": "user not authenticated", }) c.Abort() return } permissions, ok := permissionsInterface.([]models.Permission) if !ok { c.JSON(http.StatusInternalServerError, gin.H{ "error": "internal error", "message": "failed to parse permissions", }) c.Abort() return } // Check if user has the required permission hasPermission := false for _, p := range permissions { if p == permission { hasPermission = true break } } if !hasPermission { c.JSON(http.StatusForbidden, gin.H{ "error": "forbidden", "message": "insufficient permissions", }) c.Abort() return } c.Next() } } // RequireRole creates a middleware that checks for specific roles func RequireRole(allowedRoles ...models.Role) gin.HandlerFunc { return func(c *gin.Context) { // Get role from context roleInterface, exists := c.Get("role") if !exists { c.JSON(http.StatusUnauthorized, gin.H{ "error": "unauthorized", "message": "user not authenticated", }) c.Abort() return } userRole, ok := roleInterface.(models.Role) if !ok { c.JSON(http.StatusInternalServerError, gin.H{ "error": "internal error", "message": "failed to parse role", }) c.Abort() return } // Check if user has one of the allowed roles hasRole := false for _, role := range allowedRoles { if userRole == role { hasRole = true break } } if !hasRole { c.JSON(http.StatusForbidden, gin.H{ "error": "forbidden", "message": "insufficient role", }) c.Abort() return } c.Next() } } // RequireAdmin creates a middleware that requires admin role func RequireAdmin() gin.HandlerFunc { return RequireRole(models.RoleAdmin) } // OptionalAuth creates a middleware for optional authentication // If token is present and valid, it sets user context // If token is missing or invalid, it continues without setting context func OptionalAuth(jwtManager *utils.JWTManager) gin.HandlerFunc { return func(c *gin.Context) { authHeader := c.GetHeader("Authorization") if authHeader == "" { c.Next() return } token, err := utils.ExtractTokenFromHeader(authHeader) if err != nil { c.Next() return } claims, err := jwtManager.ValidateToken(token) if err != nil { c.Next() return } // Set user information in context c.Set("user_id", claims.UserID) c.Set("username", claims.Username) c.Set("role", claims.Role) c.Set("permissions", claims.Permissions) c.Next() } } // CORSMiddleware handles CORS headers func CORSMiddleware() gin.HandlerFunc { return func(c *gin.Context) { origin := c.GetHeader("Origin") // Allow specific origins or all origins (for development) allowedOrigins := []string{ "http://localhost:3000", "http://localhost:5173", // Vite dev server } allowed := false for _, o := range allowedOrigins { if o == origin || origin == "" { allowed = true break } } if allowed || strings.HasPrefix(origin, "http://localhost") { c.Writer.Header().Set("Access-Control-Allow-Origin", origin) c.Writer.Header().Set("Access-Control-Allow-Credentials", "true") c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With") c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, DELETE, PATCH") } if c.Request.Method == "OPTIONS" { c.AbortWithStatus(http.StatusNoContent) return } c.Next() } } // RequestLogger logs HTTP requests func RequestLogger() gin.HandlerFunc { return func(c *gin.Context) { // Log request details method := c.Request.Method path := c.Request.URL.Path // Get user ID if available userID := "anonymous" if uid, exists := c.Get("user_id"); exists { userID = uid.(string) } c.Next() // Log after request is processed status := c.Writer.Status() // You can enhance this with a proper logger (logrus, zap, etc.) if status >= 400 { println("[ERROR]", method, path, status, "user:", userID) } else { println("[INFO]", method, path, status, "user:", userID) } } }