package middleware import ( "database/sql" "errors" "strings" "github.com/gin-gonic/gin" "github.com/wmms/wmms/internal/service" "github.com/wmms/wmms/pkg/response" ) const ( AuthorizationHeader = "Authorization" BearerPrefix = "Bearer " UserIDKey = "user_id" UsernameKey = "username" RolesKey = "roles" ) // AuthMiddleware validates JWT tokens func AuthMiddleware() gin.HandlerFunc { authSvc := service.NewAuthService() return func(c *gin.Context) { authHeader := c.GetHeader(AuthorizationHeader) if authHeader == "" { response.Unauthorized(c, "missing authorization header") c.Abort() return } if !strings.HasPrefix(authHeader, BearerPrefix) { response.Unauthorized(c, "invalid authorization format") c.Abort() return } tokenString := strings.TrimPrefix(authHeader, BearerPrefix) token, err := authSvc.ValidateToken(tokenString) if err != nil { response.Unauthorized(c, "invalid or expired token") c.Abort() return } roles, err := service.NewOrganizationAccessService().EffectiveRoles(token.UserID) if err != nil { if errors.Is(err, sql.ErrNoRows) { response.Unauthorized(c, "用户不存在或已被删除") } else { response.InternalError(c, "用户权限加载失败") } c.Abort() return } // Set the current database principal, never the token role snapshot. c.Set(UserIDKey, token.UserID) c.Set(UsernameKey, token.Username) c.Set(RolesKey, roles) c.Next() } } // AdminOnly middleware restricts access to admin role only func AdminOnly() gin.HandlerFunc { return func(c *gin.Context) { roles, exists := c.Get(RolesKey) if !exists { response.Unauthorized(c, "unauthorized") c.Abort() return } if !hasRole(roles, "super_admin") { response.Forbidden(c, "admin access required") c.Abort() return } c.Next() } } // GetCurrentUserID returns the current user's ID from context func GetCurrentUserID(c *gin.Context) int64 { if id, exists := c.Get(UserIDKey); exists { return id.(int64) } return 0 } // GetCurrentUsername returns the current username from context func GetCurrentUsername(c *gin.Context) string { if username, exists := c.Get(UsernameKey); exists { return username.(string) } return "" } // GetCurrentRoles returns the effective role codes loaded for this request. func GetCurrentRoles(c *gin.Context) []string { if roles, exists := c.Get(RolesKey); exists { if values, ok := roles.([]string); ok { return values } } return []string{} } func IsSuperAdmin(c *gin.Context) bool { return hasRole(GetCurrentRoles(c), "super_admin") } func hasRole(value interface{}, expected string) bool { roles, ok := value.([]string) if !ok { return false } for _, role := range roles { if role == expected { return true } } return false } // OptionalAuth middleware extracts user info if token is present but doesn't require it func OptionalAuth() gin.HandlerFunc { authSvc := service.NewAuthService() return func(c *gin.Context) { authHeader := c.GetHeader(AuthorizationHeader) if authHeader == "" { c.Next() return } if !strings.HasPrefix(authHeader, BearerPrefix) { c.Next() return } tokenString := strings.TrimPrefix(authHeader, BearerPrefix) token, err := authSvc.ValidateToken(tokenString) if err != nil { c.Next() return } roles, err := service.NewOrganizationAccessService().EffectiveRoles(token.UserID) if err != nil { c.Next() return } c.Set(UserIDKey, token.UserID) c.Set(UsernameKey, token.Username) c.Set(RolesKey, roles) c.Next() } }