package router import ( "net/http" "strings" "github.com/atlasos/calypso/internal/auth" "github.com/atlasos/calypso/internal/common/database" "github.com/atlasos/calypso/internal/iam" "github.com/gin-gonic/gin" ) // authMiddleware validates JWT tokens and sets user context func authMiddleware(authHandler *auth.Handler) gin.HandlerFunc { return func(c *gin.Context) { var token string // Try to extract token from Authorization header first authHeader := c.GetHeader("Authorization") if authHeader != "" { // Parse Bearer token parts := strings.SplitN(authHeader, " ", 2) if len(parts) == 2 && parts[0] == "Bearer" { token = parts[1] } } // If no token from header, try query parameter (for WebSocket) if token == "" { token = c.Query("token") } // If still no token, return error if token == "" { c.JSON(http.StatusUnauthorized, gin.H{"error": "missing authorization token"}) c.Abort() return } // Validate token and get user user, err := authHandler.ValidateToken(token) if err != nil { c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid or expired token"}) c.Abort() return } // Load user roles and permissions from database // We need to get the DB from the auth handler's context // For now, we'll load them in the permission middleware instead // Set user in context c.Set("user", user) c.Set("user_id", user.ID) c.Set("username", user.Username) c.Next() } } // requireRole creates middleware that requires a specific role func requireRole(roleName string) gin.HandlerFunc { return func(c *gin.Context) { user, exists := c.Get("user") if !exists { c.JSON(http.StatusUnauthorized, gin.H{"error": "authentication required"}) c.Abort() return } authUser, ok := user.(*iam.User) if !ok { c.JSON(http.StatusInternalServerError, gin.H{"error": "invalid user context"}) c.Abort() return } // Load roles if not already loaded if len(authUser.Roles) == 0 { // Get DB from context (set by router) db, exists := c.Get("db") if exists { if dbConn, ok := db.(*database.DB); ok { roles, err := iam.GetUserRoles(dbConn, authUser.ID) if err == nil { authUser.Roles = roles } } } } // Check if user has the required role hasRole := false for _, role := range authUser.Roles { if role == roleName { hasRole = true break } } if !hasRole { c.JSON(http.StatusForbidden, gin.H{"error": "insufficient permissions"}) c.Abort() return } c.Next() } } // requirePermission creates middleware that requires a specific permission func requirePermission(resource, action string) gin.HandlerFunc { return func(c *gin.Context) { user, exists := c.Get("user") if !exists { c.JSON(http.StatusUnauthorized, gin.H{"error": "authentication required"}) c.Abort() return } authUser, ok := user.(*iam.User) if !ok { c.JSON(http.StatusInternalServerError, gin.H{"error": "invalid user context"}) c.Abort() return } // Load permissions if not already loaded if len(authUser.Permissions) == 0 { // Get DB from context (set by router) db, exists := c.Get("db") if exists { if dbConn, ok := db.(*database.DB); ok { permissions, err := iam.GetUserPermissions(dbConn, authUser.ID) if err == nil { authUser.Permissions = permissions } } } } // Check if user has the required permission permissionName := resource + ":" + action hasPermission := false for _, perm := range authUser.Permissions { if perm == permissionName { hasPermission = true break } } if !hasPermission { c.JSON(http.StatusForbidden, gin.H{"error": "insufficient permissions"}) c.Abort() return } c.Next() } }