package middleware import ( "context" "net/http" "strings" "github.com/ocdp/cluster-service/internal/domain/entity" "github.com/ocdp/cluster-service/internal/domain/repository" "github.com/ocdp/cluster-service/internal/domain/service" ) // Context keys type contextKey string const ( ContextKeyUserID contextKey = "user_id" ContextKeyUsername contextKey = "username" ContextKeyUserRole contextKey = "user_role" ContextKeyWorkspaceID contextKey = "workspace_id" ) // UserClaims 用户声明(从 JWT 解析) type UserClaims struct { UserID string Username string Role entity.UserRole WorkspaceID string } // WorkspaceMiddleware 工作空间中间件 // 从 JWT 获取用户角色和 workspace_id,进行权限检查 func WorkspaceMiddleware(userRepo repository.UserRepository) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // 从 Header 获取 Token authHeader := r.Header.Get("Authorization") if authHeader == "" { http.Error(w, "Missing authorization header", http.StatusUnauthorized) return } // 解析 Bearer Token parts := strings.SplitN(authHeader, " ", 2) if len(parts) != 2 || parts[0] != "Bearer" { http.Error(w, "Invalid authorization header", http.StatusUnauthorized) return } token := parts[1] _ = token // 这里需要从 AuthService 获取验证方法 // 简化处理:假设 token 包含 user_id 和 username // 实际实现需要调用 JWT 验证服务 // 从数据库获取用户信息 // 注意:这里需要通过 token 解析出 userID // 实际实现应该在 AuthService 中完成 _ = userRepo next.ServeHTTP(w, r) }) } } // RequireWorkspace 强制要求 workspace 上下文 // 用于非 Admin 用户的资源操作 func RequireWorkspace(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { workspaceID := r.Header.Get("X-Workspace-ID") userRole := r.Header.Get("X-User-Role") // Admin 可以没有 workspace if userRole == string(entity.RoleAdmin) { next.ServeHTTP(w, r) return } // 普通用户必须有 workspace if workspaceID == "" { http.Error(w, "Workspace context required", http.StatusForbidden) return } // 将 workspace_id 放入 context ctx := context.WithValue(r.Context(), ContextKeyWorkspaceID, workspaceID) next.ServeHTTP(w, r.WithContext(ctx)) }) } // RequireAdmin 要求 Admin 角色 func RequireAdmin(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { userRole := r.Header.Get("X-User-Role") if userRole != string(entity.RoleAdmin) { http.Error(w, "Admin access required", http.StatusForbidden) return } next.ServeHTTP(w, r) }) } // GetUserClaims 从 Context 获取用户声明 func GetUserClaims(ctx context.Context) *UserClaims { userID, _ := ctx.Value(ContextKeyUserID).(string) username, _ := ctx.Value(ContextKeyUsername).(string) roleStr, _ := ctx.Value(ContextKeyUserRole).(string) workspaceID, _ := ctx.Value(ContextKeyWorkspaceID).(string) return &UserClaims{ UserID: userID, Username: username, Role: entity.UserRole(roleStr), WorkspaceID: workspaceID, } } // GetWorkspaceID 从 Context 获取 workspace ID func GetWorkspaceID(ctx context.Context) string { workspaceID, _ := ctx.Value(ContextKeyWorkspaceID).(string) return workspaceID } // GetUserID 从 Context 获取用户 ID func GetUserID(ctx context.Context) string { userID, _ := ctx.Value(ContextKeyUserID).(string) return userID } // GetUserRole 从 Context 获取用户角色 func GetUserRole(ctx context.Context) entity.UserRole { roleStr, _ := ctx.Value(ContextKeyUserRole).(string) return entity.UserRole(roleStr) } // FilterByWorkspace 根据用户角色过滤资源 // Admin: 返回所有资源(workspaceID 忽略) // User: 仅返回属于自己 workspace 的资源 func FilterByWorkspace(workspaceID, userRole string) (filterWorkspaceID string, isAdmin bool) { if userRole == string(entity.RoleAdmin) { return "", true } return workspaceID, false } // AuthorizationService 授权服务 type AuthorizationService struct { userRepo repository.UserRepository } // NewAuthorizationService 创建授权服务 func NewAuthorizationService(userRepo repository.UserRepository) *AuthorizationService { return &AuthorizationService{ userRepo: userRepo, } } // CheckResourceAccess 检查用户是否有权访问指定资源 func (s *AuthorizationService) CheckResourceAccess(ctx context.Context, userID, resourceWorkspaceID string) error { user, err := s.userRepo.GetByID(ctx, userID) if err != nil { return err } // Admin 可以访问所有资源 if user.Role == entity.RoleAdmin { return nil } // 普通用户只能访问自己 workspace 的资源 if user.WorkspaceID != resourceWorkspaceID { return entity.ErrPermissionDenied } return nil } // CanAccessWorkspace 检查用户是否可以访问指定 workspace func (s *AuthorizationService) CanAccessWorkspace(ctx context.Context, userID, targetWorkspaceID string) error { user, err := s.userRepo.GetByID(ctx, userID) if err != nil { return err } // Admin 可以访问所有 workspace if user.Role == entity.RoleAdmin { return nil } // 普通用户只能访问自己的 workspace if user.WorkspaceID != targetWorkspaceID { return entity.ErrPermissionDenied } return nil } // GetAccessibleWorkspaces 获取用户可访问的 workspace 列表 func (s *AuthorizationService) GetAccessibleWorkspaces(ctx context.Context, userID string) ([]string, error) { user, err := s.userRepo.GetByID(ctx, userID) if err != nil { return nil, err } // Admin 可以访问所有 workspace if user.Role == entity.RoleAdmin { return nil, nil // nil 表示所有 } // 普通用户只能访问自己的 workspace if user.WorkspaceID != "" { return []string{user.WorkspaceID}, nil } return []string{}, nil } // RequireRole 要求特定角色 func RequireRole(roles ...entity.UserRole) func(http.Handler) http.Handler { roleSet := make(map[entity.UserRole]bool) for _, r := range roles { roleSet[r] = true } return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { userRole := r.Header.Get("X-User-Role") if !roleSet[entity.UserRole(userRole)] { http.Error(w, "Insufficient permissions", http.StatusForbidden) return } next.ServeHTTP(w, r) }) } } // WithUserClaims 将用户声明注入到 Context func WithUserClaims(claims *UserClaims) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() ctx = context.WithValue(ctx, ContextKeyUserID, claims.UserID) ctx = context.WithValue(ctx, ContextKeyUsername, claims.Username) ctx = context.WithValue(ctx, ContextKeyUserRole, string(claims.Role)) if claims.WorkspaceID != "" { ctx = context.WithValue(ctx, ContextKeyWorkspaceID, claims.WorkspaceID) } next.ServeHTTP(w, r.WithContext(ctx)) }) } } // LoginRequired 要求登录 func LoginRequired(authService *service.AuthService) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { authHeader := r.Header.Get("Authorization") if authHeader == "" { http.Error(w, "Authorization required", http.StatusUnauthorized) return } parts := strings.SplitN(authHeader, " ", 2) if len(parts) != 2 || parts[0] != "Bearer" { http.Error(w, "Invalid authorization header", http.StatusUnauthorized) return } token := parts[1] userID, _, err := authService.VerifyAccessToken(r.Context(), token) if err != nil { http.Error(w, "Invalid token", http.StatusUnauthorized) return } ctx := context.WithValue(r.Context(), ContextKeyUserID, userID) next.ServeHTTP(w, r.WithContext(ctx)) }) } }