@lark-apaas/nestjs-authzpaas 0.1.0-alpha.0 → 0.1.0-alpha.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/authzpaas.module.ts","../src/const.ts","../src/services/permission.service.ts","../src/utils/memory-cache.ts","../src/casl/ability.factory.ts","../src/exceptions/permission-denied.exception.ts","../src/guards/authzpaas.guard.ts","../src/utils/index.ts","../src/middlewares/roles.middleware.ts","../src/filters/authzpaas-exception.filter.ts","../src/decorators/can-role.decorator.ts","../src/decorators/can-permission.decorator.ts","../src/decorators/can-env.decorator.ts","../src/decorators/user-id.decorator.ts","../src/decorators/mock-roles.decorator.ts","../src/controllers/permission.controller.ts"],"sourcesContent":["import { DynamicModule, Module, Type } from '@nestjs/common';\nimport { APP_FILTER, APP_GUARD, Reflector } from '@nestjs/core';\nimport { AuthZPaasModuleOptions } from './types';\nimport {\n PERMISSION_API_CONFIG_TOKEN,\n CACHE_CONFIG_TOKEN,\n AUTHZPAAS_MODULE_OPTIONS,\n} from './const';\nimport { PermissionService } from './services/permission.service';\nimport { AbilityFactory } from './casl/ability.factory';\nimport { AuthZPaasGuard } from './guards/authzpaas.guard';\nimport { RolesMiddleware } from './middlewares/roles.middleware';\nimport { AuthZPaasExceptionFilter } from './filters/authzpaas-exception.filter';\nimport { DEFAULT_LOGIN_PATH } from './const';\n\nexport interface AuthZPaasModuleAsyncOptions {\n imports?: Type<unknown>[];\n inject?: (string | symbol | Type<unknown>)[];\n useFactory: (\n ...args: unknown[]\n ) => Promise<AuthZPaasModuleOptions> | AuthZPaasModuleOptions;\n isGlobal?: boolean;\n}\n\n@Module({})\nexport class AuthZPaasModule {\n static forRoot(options: AuthZPaasModuleOptions): DynamicModule {\n const {\n permissionApi,\n cache = { ttl: 300, max: 1000, enabled: true },\n isGlobal = true,\n } = options;\n\n return {\n module: AuthZPaasModule,\n global: isGlobal,\n controllers: [],\n providers: [\n // 配置提供者\n {\n provide: AUTHZPAAS_MODULE_OPTIONS,\n useValue: {\n ...options,\n loginPath: options.loginPath || DEFAULT_LOGIN_PATH,\n },\n },\n {\n provide: PERMISSION_API_CONFIG_TOKEN,\n useValue: permissionApi,\n },\n {\n provide: CACHE_CONFIG_TOKEN,\n useValue: cache,\n },\n // 核心服务\n Reflector,\n // 服务提供者\n PermissionService,\n AbilityFactory,\n AuthZPaasGuard,\n RolesMiddleware,\n // 守卫提供者\n {\n provide: APP_GUARD,\n useClass: AuthZPaasGuard,\n },\n {\n provide: APP_FILTER,\n useClass: AuthZPaasExceptionFilter,\n },\n ],\n exports: [PermissionService, AbilityFactory, RolesMiddleware],\n };\n }\n\n /**\n * 异步注册 AuthZPaas 模块(根模块)\n * 用于需要从配置服务获取设置的场景\n *\n * @param options 异步配置选项\n * @returns 动态模块\n *\n * @example\n * ```typescript\n * @Module({\n * imports: [\n * AuthZPaasModule.forRootAsync({\n * imports: [ConfigModule],\n * inject: [ConfigService],\n * useFactory: async (configService: ConfigService) => ({\n * permissionApi: {\n * baseUrl: configService.get('PERMISSION_API_URL'),\n * apiToken: configService.get('PERMISSION_API_TOKEN'),\n * },\n * cache: {\n * ttl: configService.get('CACHE_TTL', 300),\n * max: configService.get('CACHE_MAX', 1000),\n * },\n * }),\n * }),\n * ],\n * })\n * export class AppModule {}\n * ```\n */\n static forRootAsync(options: AuthZPaasModuleAsyncOptions): DynamicModule {\n const { imports = [], inject = [], useFactory, isGlobal = true } = options;\n\n return {\n module: AuthZPaasModule,\n global: isGlobal,\n imports,\n controllers: [],\n providers: [\n // 异步配置提供者\n {\n provide: AUTHZPAAS_MODULE_OPTIONS,\n useFactory,\n inject,\n },\n // 权限 API 配置提供者\n {\n provide: PERMISSION_API_CONFIG_TOKEN,\n useFactory: (moduleOptions: AuthZPaasModuleOptions) => {\n return moduleOptions.permissionApi;\n },\n inject: [AUTHZPAAS_MODULE_OPTIONS],\n },\n // 缓存配置提供者\n {\n provide: CACHE_CONFIG_TOKEN,\n useFactory: (moduleOptions: AuthZPaasModuleOptions) => {\n return (\n moduleOptions.cache || { ttl: 300, max: 1000, enabled: true }\n );\n },\n inject: [AUTHZPAAS_MODULE_OPTIONS],\n },\n // 核心服务\n Reflector,\n // 服务提供者\n PermissionService,\n AbilityFactory,\n AuthZPaasGuard,\n RolesMiddleware,\n // 守卫提供者\n {\n provide: APP_GUARD,\n useClass: AuthZPaasGuard,\n },\n {\n provide: APP_FILTER,\n useClass: AuthZPaasExceptionFilter,\n },\n ],\n exports: [PermissionService, AbilityFactory, RolesMiddleware],\n };\n }\n}\n","/**\n * 常量\n */\n\n/** 匿名用户 ID */\nexport const ANONYMOUS_USER_ID = 'anonymous_user_id';\n\n/**\n * 依赖注入 Token\n */\n\n/** 权限 API 配置 Token */\nexport const PERMISSION_API_CONFIG_TOKEN = Symbol('PERMISSION_API_CONFIG');\n\n/** 缓存配置 Token */\nexport const CACHE_CONFIG_TOKEN = Symbol('CACHE_CONFIG');\n\n/** AuthZPaas 模块选项 Token */\nexport const AUTHZPAAS_MODULE_OPTIONS = Symbol('AUTHZPAAS_MODULE_OPTIONS');\n\n/**\n * 元数据键\n */\n\n/** 需要的角色元数据键 */\nexport const ROLES_KEY = 'authzpaas:roles';\n\n/** 需要的权限元数据键 */\nexport const PERMISSIONS_KEY = 'authzpaas:permissions';\n\n/** 需要的环境元数据键 */\nexport const ENVIRONMENT_KEY = 'authzpaas:environment';\n\n/** 需要登录元数据键 */\nexport const NEED_LOGIN_KEY = 'authzpaas:needLogin';\n\n/** 模块选项:登录页路径默认值 */\nexport const DEFAULT_LOGIN_PATH = '/login';\n\n/** 角色模拟的 Cookie 键名 */\nexport const MOCK_ROLES_COOKIE_KEY = 'mockRoles';\n\nexport const ENABLE_MOCK_ROLE_KEY = '__authzpaas_enableMockRole';\n","import { Injectable, Inject, Logger, HttpStatus } from '@nestjs/common';\nimport type { UserPermissionData, Permission, CacheConfig, PermissionApiConfig } from '../types';\nimport { PERMISSION_API_CONFIG_TOKEN, CACHE_CONFIG_TOKEN, ANONYMOUS_USER_ID } from '../const';\nimport { MemoryCache } from '../utils/memory-cache';\nimport { AbilityFactory, AppAbility, ROLE_SUBJECT } from '../casl/ability.factory';\nimport { PermissionDeniedException, PermissionDeniedType } from '../exceptions/permission-denied.exception';\nimport type { RoleRequirement, PermissionRequirement } from '../decorators';\n\n/**\n * 用户权限缓存数据结构\n */\ninterface UserPermissionCache {\n permissionData: UserPermissionData;\n ability: AppAbility;\n}\n\nexport interface CheckPermissionsParams {\n requirements: PermissionRequirement[];\n or?: boolean;\n}\n\n/**\n * 权限服务\n * 内置权限获取和缓存逻辑,以及权限检查逻辑\n */\n@Injectable()\nexport class PermissionService {\n private readonly logger = new Logger(PermissionService.name);\n // 统一的缓存,同时缓存权限数据和 Ability 实例\n private readonly cache: MemoryCache<UserPermissionCache>;\n // 缓存正在进行中的 Promise,避免重复请求\n private readonly pendingRequests = new Map<string, Promise<UserPermissionData>>();\n\n constructor(\n @Inject(PERMISSION_API_CONFIG_TOKEN)\n private readonly apiConfig: PermissionApiConfig,\n @Inject(CACHE_CONFIG_TOKEN)\n cacheConfig: CacheConfig,\n private readonly abilityFactory: AbilityFactory,\n ) {\n // 统一的缓存实例,同时缓存权限数据和 Ability 实例\n // 缓存 key 为 userId,缓存值包含 permissionData 和 ability\n // 缓存时间以 permissionData 的更新时间为准\n this.cache = new MemoryCache<UserPermissionCache>({\n ttl: cacheConfig.ttl || 300,\n max: cacheConfig.max || 1000,\n enabled: cacheConfig.enabled !== false,\n });\n\n this.logger.log(\n `PermissionService initialized with API: ${apiConfig?.baseUrl}`,\n );\n }\n\n /**\n * 构建权限/Ability 缓存 key\n * - 若存在模拟角色:按角色集合排序拼接 + 用户维度\n * - 否则按 userId/匿名用户\n */\n private buildCacheKey(userId?: string, mockRoles?: string[]): string {\n if (mockRoles && mockRoles.length > 0) {\n const sortedRoles = [...mockRoles].sort();\n return `@roles:${sortedRoles.join('|')}#u:${userId || ANONYMOUS_USER_ID}`;\n }\n return userId || ANONYMOUS_USER_ID;\n }\n\n /**\n * 获取用户权限数据(带缓存)\n */\n async getUserPermissions(userId?: string, mockRoles?: string[]): Promise<UserPermissionData | null> {\n if (!this.apiConfig?.endpoint) {\n this.logger.warn('Permission API endpoint is not configured, returning null');\n return null;\n }\n // 计算缓存 key:支持基于角色的缓存\n const key = this.buildCacheKey(userId, mockRoles);\n\n // 尝试从缓存获取\n const cached = this.cache.get(key);\n if (cached) {\n this.logger.debug(`Cache hit for user ${key}`);\n return cached.permissionData;\n }\n\n // 检查是否有正在进行中的请求\n const pendingRequest = this.pendingRequests.get(key);\n if (pendingRequest) {\n this.logger.debug(`Reusing pending request for user ${key}`);\n return pendingRequest;\n }\n\n // 缓存未命中,从 API 获取\n this.logger.debug(`Cache miss for key ${key}, fetching from API`);\n \n // 创建新的请求 Promise\n const requestPromise = (async () => {\n try {\n const permissionData = (mockRoles && mockRoles.length > 0)\n ? await this.getPermissionsByMockRoles(userId, mockRoles)\n : await this.fetchFromApi(userId);\n // 添加获取时间\n const dataWithTimestamp: UserPermissionData = {\n ...permissionData,\n fetchedAt: new Date(),\n };\n\n // 创建 Ability 实例\n const ability = this.abilityFactory.createForUser(dataWithTimestamp);\n\n // 存入缓存(同时缓存权限数据和 Ability 实例)\n this.cache.set(key, {\n permissionData: dataWithTimestamp,\n ability,\n });\n\n return dataWithTimestamp;\n } catch (error) {\n this.logger.error(`Failed to fetch permissions for key ${key}:`, error);\n throw error;\n } finally {\n // 无论成功还是失败,都要清除 pending 状态\n this.pendingRequests.delete(key);\n }\n })();\n\n // 缓存这个 Promise\n this.pendingRequests.set(key, requestPromise);\n\n return requestPromise;\n }\n\n /**\n * 从 API 获取权限数据\n * 内置实现,用户无需配置\n */\n private async fetchFromApi(userId?: string): Promise<UserPermissionData> {\n const {\n baseUrl,\n apiToken,\n endpoint,\n headers = {},\n timeout = 5000,\n } = this.apiConfig || {};\n // 构建完整 URL\n const url = `${baseUrl}${endpoint}${userId ? `?userId=${userId}` : ''}`;\n\n // 构建请求头\n const requestHeaders: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...headers,\n };\n\n if (apiToken) {\n requestHeaders['Authorization'] = `Bearer ${apiToken}`;\n }\n\n // 发起请求(带超时控制)\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const response = await fetch(url, {\n method: 'GET',\n headers: requestHeaders,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const error = new Error(\n `Permission API returned ${response.status}: ${response.statusText}`,\n );\n throw new PermissionDeniedException({\n cause: error,\n type: PermissionDeniedType.PERMISSION_CONFIG_QUERY_FAILED,\n message: error.message,\n }, HttpStatus.INTERNAL_SERVER_ERROR);\n }\n\n const data = await response.json() as {\n roles?: string[] | { name: string }[];\n permissions?: Permission[];\n };\n\n // 标准化返回数据\n // 如果 roles 是对象数组,提取 name 字段;如果是字符串数组,直接使用\n const roles = (data.roles || []).map(role => \n typeof role === 'string' ? role : role.name,\n );\n\n return {\n userId,\n roles,\n permissions: data.permissions || [],\n fetchedAt: new Date(),\n };\n } catch (error: unknown) {\n clearTimeout(timeoutId);\n let err = error as Error;\n\n if ((error as { name?: string }).name === 'AbortError') {\n err = new Error(`Permission API request timeout after ${timeout}ms`);\n }\n\n throw new PermissionDeniedException({\n cause: err,\n type: PermissionDeniedType.PERMISSION_CONFIG_QUERY_FAILED,\n message: err.message,\n }, HttpStatus.INTERNAL_SERVER_ERROR);\n }\n }\n\n /**\n * 基于模拟角色获取权限数据(不使用缓存)\n * 该方法用于前端/守卫在检测到 mockRoles 时直接按角色获取权限\n */\n async getPermissionsByMockRoles(userId: string | undefined, mockRoles: string[]): Promise<UserPermissionData> {\n const { baseUrl, timeout = 5000 } = this.apiConfig || {};\n const rolesParam = encodeURIComponent(mockRoles.join(','));\n const userParam = userId ? `&userId=${encodeURIComponent(userId)}` : '';\n const url = `${baseUrl}/mock-api/permissions-by-roles?roles=${rolesParam}${userParam}`;\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const response = await fetch(url, { method: 'GET', signal: controller.signal });\n clearTimeout(timeoutId);\n if (!response.ok) {\n throw new Error(`Permission API (mock by roles) returned ${response.status}: ${response.statusText}`);\n }\n const data = await response.json() as { userId?: string; roles: string[]; permissions: Permission[] };\n return {\n userId: userId,\n roles: data.roles || [],\n permissions: data.permissions || [],\n fetchedAt: new Date(),\n };\n } catch (error: unknown) {\n clearTimeout(timeoutId);\n throw new PermissionDeniedException({\n cause: error as Error,\n type: PermissionDeniedType.PERMISSION_CONFIG_QUERY_FAILED,\n message: (error as Error).message,\n }, HttpStatus.INTERNAL_SERVER_ERROR);\n }\n }\n\n /**\n * 获取用户的 Ability 实例(带缓存)\n * @param userId 用户ID\n * @returns CASL Ability 实例\n */\n private async getUserAbility(userId?: string, mockRoles?: string[]): Promise<AppAbility> {\n // 计算缓存 key\n const key = this.buildCacheKey(userId, mockRoles);\n\n // 尝试从缓存获取\n const cached = this.cache.get(key);\n if (cached) {\n return cached.ability;\n }\n\n // 缓存未命中,调用 getUserPermissions 会创建并缓存\n await this.getUserPermissions(userId, mockRoles);\n \n // 再次从缓存获取(此时一定存在)\n const newCached = this.cache.get(key);\n return newCached!.ability;\n }\n\n /**\n * 清除用户权限缓存\n */\n clearUserCache(userId: string): void {\n const key = userId || ANONYMOUS_USER_ID;\n this.cache.delete(key);\n this.pendingRequests.delete(key);\n this.logger.debug(`Cache cleared for user ${key}`);\n }\n\n /**\n * 清除所有缓存\n */\n clearAllCache(): void {\n this.cache.clear();\n this.pendingRequests.clear();\n this.logger.log('All permission cache cleared');\n }\n\n /**\n * 获取缓存统计信息\n */\n getCacheStats() {\n return this.cache.getStats();\n }\n\n /**\n * 检查角色要求\n * 使用 CASL Ability 统一鉴权方式\n * @param requirement 角色要求\n * @param userId 用户ID,匿名用户时为空\n * @returns 用户权限数据\n * @throws PermissionDeniedException 当角色不满足时\n */\n async checkRoles(\n requirement: RoleRequirement,\n userId?: string,\n mockRoles?: string[],\n ): Promise<UserPermissionData> {\n const permissionData = await this.getUserPermissions(userId, mockRoles);\n if (!permissionData) {\n throw new PermissionDeniedException({\n cause: new Error('Permission data fetch api is not configured'),\n type: PermissionDeniedType.PERMISSION_CONFIG_QUERY_FAILED,\n message: 'Permission data fetch api is not configured',\n }, HttpStatus.BAD_REQUEST);\n }\n const ability = await this.getUserAbility(userId, mockRoles);\n const { roles, and } = requirement;\n\n // 使用 CASL 统一检查角色权限\n // 角色作为 action,'@role' 作为 subject\n const checkResults = roles.map((role) =>\n ability.can(role, ROLE_SUBJECT),\n );\n\n const hasRole = and\n ? checkResults.every((result) => result)\n : checkResults.some((result) => result);\n\n if (!hasRole) {\n const userRoles = permissionData.roles;\n this.logger.warn(\n `角色检查失败: 用户 ${userId}, 用户角色 [${userRoles.join(', ')}], 需要 [${roles.join(', ')}]`,\n );\n throw PermissionDeniedException.roleRequired(roles, and);\n }\n\n return permissionData;\n }\n\n /**\n * 检查权限要求\n * @param requirements 权限要求列表\n * @param userId 用户ID\n * @returns 用户权限数据\n * @throws PermissionDeniedException 当权限不满足时\n */\n async checkPermissions(\n params: CheckPermissionsParams,\n userId?: string,\n mockRoles?: string[],\n ): Promise<UserPermissionData> {\n // 获取权限数据(用于返回)\n const permissionData = await this.getUserPermissions(userId, mockRoles);\n if (!permissionData) {\n throw new PermissionDeniedException({\n cause: new Error('Permission data fetch api is not configured'),\n type: PermissionDeniedType.PERMISSION_CONFIG_QUERY_FAILED,\n message: 'Permission data fetch api is not configured',\n }, HttpStatus.BAD_REQUEST);\n }\n const { requirements, or } = params;\n if (!requirements || requirements.length === 0) {\n return permissionData;\n }\n\n // 获取缓存的 Ability 实例\n const ability = await this.getUserAbility(userId, mockRoles);\n\n // 收集所有失败的权限要求\n const failedRequirements: Array<{\n actions: string[];\n subject: string;\n or: boolean;\n }> = [];\n\n // 检查每个权限要求\n for (const requirement of requirements) {\n const { actions, subject, or = false } = requirement;\n\n // 使用缓存的 Ability 实例检查权限\n const checkResults = actions.map((action) =>\n ability.can(action, subject),\n );\n\n // 根据 or 决定是 AND 还是 OR 逻辑\n const hasPermission = or\n ? checkResults.some((result) => result)\n : checkResults.every((result) => result)\n\n if (!hasPermission) {\n failedRequirements.push({\n actions,\n subject: String(subject),\n or,\n });\n }\n }\n\n // 如果有失败的权限要求,抛出异常\n if (failedRequirements.length > 0) {\n if(or && failedRequirements.length === requirements.length) {\n throw PermissionDeniedException.permissionRequired(failedRequirements.map(({ actions, subject }) => ({\n actions,\n subject: String(subject),\n })), true);\n } else if(!or) {\n throw PermissionDeniedException.permissionRequired(failedRequirements.map(({ actions, subject }) => ({\n actions,\n subject: String(subject),\n })), false);\n }\n }\n return permissionData;\n }\n\n async getAbility(userId: string): Promise<AppAbility> {\n return this.getUserAbility(userId);\n }\n}\n","/**\n * 缓存项\n */\ninterface CacheItem<T> {\n value: T;\n expireAt: number;\n}\n\n/**\n * 缓存配置\n */\ninterface MemoryCacheOptions {\n /** 过期时间(秒) */\n ttl: number;\n /** 最大缓存数量 */\n max: number;\n /** 是否启用 */\n enabled: boolean;\n}\n\n/**\n * 内存缓存实现\n * 使用 LRU(Least Recently Used)策略\n */\nexport class MemoryCache<T> {\n private cache: Map<string, CacheItem<T>>;\n private accessOrder: string[];\n private options: MemoryCacheOptions;\n private hits = 0;\n private misses = 0;\n\n constructor(options: MemoryCacheOptions) {\n this.cache = new Map();\n this.accessOrder = [];\n this.options = options;\n }\n\n /**\n * 获取缓存值\n */\n get(key: string): T | undefined {\n if (!this.options.enabled) {\n return undefined;\n }\n\n const item = this.cache.get(key);\n if (!item) {\n this.misses++;\n return undefined;\n }\n\n // 检查是否过期\n if (Date.now() > item.expireAt) {\n this.cache.delete(key);\n this.removeFromAccessOrder(key);\n this.misses++;\n return undefined;\n }\n\n // 更新访问顺序(LRU)\n this.updateAccessOrder(key);\n this.hits++;\n return item.value;\n }\n\n /**\n * 设置缓存值\n */\n set(key: string, value: T): void {\n if (!this.options.enabled) {\n return;\n }\n\n // 检查是否需要淘汰旧数据\n if (this.cache.size >= this.options.max && !this.cache.has(key)) {\n this.evictLRU();\n }\n\n const expireAt = Date.now() + this.options.ttl * 1000;\n this.cache.set(key, { value, expireAt });\n this.updateAccessOrder(key);\n }\n\n /**\n * 删除缓存\n */\n delete(key: string): void {\n this.cache.delete(key);\n this.removeFromAccessOrder(key);\n }\n\n /**\n * 清空所有缓存\n */\n clear(): void {\n this.cache.clear();\n this.accessOrder = [];\n this.hits = 0;\n this.misses = 0;\n }\n\n /**\n * 获取缓存统计信息\n */\n getStats() {\n return {\n size: this.cache.size,\n hits: this.hits,\n misses: this.misses,\n hitRate: this.hits / (this.hits + this.misses) || 0,\n enabled: this.options.enabled,\n };\n }\n\n /**\n * 更新访问顺序\n */\n private updateAccessOrder(key: string): void {\n this.removeFromAccessOrder(key);\n this.accessOrder.push(key);\n }\n\n /**\n * 从访问顺序中移除\n */\n private removeFromAccessOrder(key: string): void {\n const index = this.accessOrder.indexOf(key);\n if (index > -1) {\n this.accessOrder.splice(index, 1);\n }\n }\n\n /**\n * 淘汰最少使用的项\n */\n private evictLRU(): void {\n if (this.accessOrder.length > 0) {\n const oldestKey = this.accessOrder[0];\n this.delete(oldestKey);\n }\n }\n}\n","import { Injectable } from '@nestjs/common';\nimport { AbilityBuilder, PureAbility, AbilityClass } from '@casl/ability';\nimport { UserPermissionData, Action, Subject } from '../types';\n\n/**\n * CASL Ability 类型\n */\nexport type AppAbility = PureAbility<[Action, Subject]>;\n\n/**\n * 角色检查的特殊 Subject\n * 用于统一角色鉴权和权限点位鉴权\n * \n * 使用方式:\n * - 权限点位鉴权:ability.can('read', 'Todo')\n * - 角色鉴权:ability.can('admin', ROLE_SUBJECT) 或 ability.can('admin', '@role')\n */\nexport const ROLE_SUBJECT = '@role';\n\n/**\n * Ability 工厂\n * 负责根据用户权限数据创建 CASL Ability 实例\n * \n * 统一了两种鉴权方式:\n * 1. 基于角色的鉴权 - 角色名作为 action,'@role' 作为 subject\n * 2. 基于权限点位的鉴权 - 标准的 action + subject 模式\n */\n@Injectable()\nexport class AbilityFactory {\n /**\n * 为用户创建 Ability\n */\n createForUser(permissionData: UserPermissionData): AppAbility {\n const { can, build } = new AbilityBuilder<AppAbility>(\n PureAbility as AbilityClass<AppAbility>,\n );\n\n // 基于权限点位设置能力\n for (const permission of permissionData.permissions) {\n const { sub, actions } = permission;\n \n // 为每个 action 添加权限\n for (const action of actions) {\n can(action as Action, sub);\n }\n }\n\n for (const role of permissionData.roles) {\n can(role as Action, ROLE_SUBJECT);\n }\n\n return build();\n }\n}\n","import { HttpException } from '@nestjs/common';\n\n/**\n * 权限拒绝异常类型\n */\nexport enum PermissionDeniedType {\n /** 用户未认证 */\n UNAUTHENTICATED = 'UNAUTHENTICATED',\n /** 缺少角色 */\n ROLE_REQUIRED = 'ROLE_REQUIRED',\n /** 缺少权限 */\n PERMISSION_REQUIRED = 'PERMISSION_REQUIRED',\n /** 环境不满足 */\n ENVIRONMENT_REQUIRED = 'ENVIRONMENT_REQUIRED',\n /** 权限配置查询失败 */\n PERMISSION_CONFIG_QUERY_FAILED = 'PERMISSION_CONFIG_QUERY_FAILED',\n}\n\n/**\n * 权限拒绝异常详情\n */\nexport interface PermissionDeniedDetails {\n /** 错误堆栈 */\n cause?: Error;\n /** 异常类型 */\n type: PermissionDeniedType;\n /** 错误消息 */\n message: string;\n /** 需要的角色(如果适用) */\n requiredRoles?: string[];\n /** 需要的权限(如果适用) */\n requiredPermissions?: Array<{\n actions: string[];\n subject: string;\n }>;\n /** 环境要求(如果适用) */\n environmentRequirement?: string;\n /** 额外信息 */\n metadata?: Record<string, any>;\n}\n\n/**\n * 权限拒绝异常\n * 专门用于 AuthZPaas 模块的权限检查失败场景\n */\nexport class PermissionDeniedException extends HttpException {\n public readonly type: PermissionDeniedType;\n public readonly details: PermissionDeniedDetails;\n\n constructor(details: PermissionDeniedDetails, httpStatusCode: number = 403) {\n super({\n statusCode: httpStatusCode,\n cause: details.cause,\n type: details.type,\n message: details.message,\n ...(details.requiredRoles && { requiredRoles: details.requiredRoles }),\n ...(details.requiredPermissions && { requiredPermissions: details.requiredPermissions }),\n ...(details.environmentRequirement && { environmentRequirement: details.environmentRequirement }),\n ...(details.metadata && { metadata: details.metadata }),\n }, httpStatusCode);\n\n this.type = details.type;\n this.details = details;\n this.name = 'PermissionDeniedException';\n }\n\n /**\n * 创建用户未认证异常\n */\n static unauthenticated(message: string = '用户未认证'): PermissionDeniedException {\n return new PermissionDeniedException({\n type: PermissionDeniedType.UNAUTHENTICATED,\n message,\n });\n }\n\n /**\n * 创建角色不足异常\n */\n static roleRequired(\n requiredRoles: string[],\n and: boolean = false,\n ): PermissionDeniedException {\n const message = and\n ? `需要所有角色: ${requiredRoles.join(', ')}`\n : `需要以下任一角色: ${requiredRoles.join(', ')}`;\n\n return new PermissionDeniedException({\n type: PermissionDeniedType.ROLE_REQUIRED,\n message,\n requiredRoles,\n metadata: { and },\n });\n }\n\n /**\n * 创建权限不足异常\n */\n static permissionRequired(\n requiredPermissions: Array<{ actions: string[]; subject: string }>,\n or: boolean = false,\n customMessage?: string,\n ): PermissionDeniedException {\n let message: string;\n\n if (customMessage) {\n message = customMessage;\n } else if (requiredPermissions.length === 1) {\n const perm = requiredPermissions[0];\n message = or\n ? `缺少权限: 需要对 ${perm.subject} 执行以下任一操作 [${perm.actions.join(', ')}]`\n : `缺少权限: 需要对 ${perm.subject} 执行所有操作 [${perm.actions.join(', ')}]`\n } else {\n message = or\n ? `缺少权限: 需要满足以下任一权限要求: ${requiredPermissions.map(({ actions, subject }) => `对 ${subject} 执行以下任一操作 [${actions.join(', ')}]`).join(', ')}`\n : `缺少权限: 需要满足以下所有权限要求: ${requiredPermissions.map(({ actions, subject }) => `对 ${subject} 执行所有操作 [${actions.join(', ')}]`).join(', ')}`;\n }\n\n return new PermissionDeniedException({\n type: PermissionDeniedType.PERMISSION_REQUIRED,\n message,\n requiredPermissions,\n metadata: { or },\n });\n }\n\n /**\n * 创建环境不满足异常\n */\n static environmentRequired(\n requirement: string,\n message?: string,\n ): PermissionDeniedException {\n return new PermissionDeniedException({\n type: PermissionDeniedType.ENVIRONMENT_REQUIRED,\n message: message || '不满足环境要求',\n environmentRequirement: requirement,\n });\n }\n}\n\n","import {\n Injectable,\n CanActivate,\n ExecutionContext,\n Inject,\n} from '@nestjs/common';\nimport { Reflector } from '@nestjs/core';\nimport {\n CheckPermissionsParams,\n PermissionService,\n} from '../services/permission.service';\nimport {\n ROLES_KEY,\n PERMISSIONS_KEY,\n ENVIRONMENT_KEY,\n AUTHZPAAS_MODULE_OPTIONS,\n ENABLE_MOCK_ROLE_KEY,\n} from '../const';\nimport {\n RoleRequirement,\n EnvironmentRequirement,\n CheckRoleRequirement,\n} from '../decorators';\nimport { EnvironmentContext, type AuthZPaasModuleOptions } from '../types';\nimport { PermissionDeniedException } from '../exceptions/permission-denied.exception';\nimport { getMockRolesFromCookie } from '../utils';\n\n/**\n * AuthZPaas 守卫\n * 负责协调所有鉴权检查,具体检查逻辑委托给 PermissionService\n */\n@Injectable()\nexport class AuthZPaasGuard implements CanActivate {\n constructor(\n private reflector: Reflector,\n private permissionService: PermissionService,\n @Inject(AUTHZPAAS_MODULE_OPTIONS)\n private moduleOptions: AuthZPaasModuleOptions\n ) {}\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const http = context.switchToHttp();\n const request = http.getRequest();\n request[ENABLE_MOCK_ROLE_KEY] = this.moduleOptions?.enableMockRole;\n\n const userId = this.extractUserId(request);\n\n const mockRoles = this.moduleOptions?.enableMockRole\n ? getMockRolesFromCookie(request)\n : undefined;\n\n // 检查角色要求\n const checkRoleRequirement =\n this.reflector.getAllAndOverride<CheckRoleRequirement>(ROLES_KEY, [\n context.getHandler(),\n context.getClass(),\n ]);\n\n if (checkRoleRequirement) {\n await this.checkRoleRequirement(checkRoleRequirement, userId, mockRoles);\n }\n\n // 检查权限要求\n const checkPermissionParams =\n this.reflector.getAllAndOverride<CheckPermissionsParams>(\n PERMISSIONS_KEY,\n [context.getHandler(), context.getClass()]\n );\n\n if (checkPermissionParams) {\n await this.checkPermissionRequirement(\n checkPermissionParams,\n userId,\n mockRoles\n );\n }\n\n // 检查环境要求\n const envRequirement =\n this.reflector.getAllAndOverride<EnvironmentRequirement>(\n ENVIRONMENT_KEY,\n [context.getHandler(), context.getClass()]\n );\n\n if (envRequirement) {\n const envContext = this.extractEnvironmentContext(request);\n await this.checkEnvironmentRequirement(\n envRequirement,\n envContext,\n request\n );\n }\n\n return true;\n }\n\n /**\n * 从请求中提取用户ID\n * 子类可以重写此方法以适应不同的认证策略\n */\n protected extractUserId(request: {\n userContext?: { userId?: string };\n cookies?: Record<string, string | undefined>;\n }): string | undefined {\n // 1) 优先使用 cookie 中的 mockUserId 实现角色模拟\n const mockUserId = request.cookies?.mockUserId;\n if (mockUserId) return mockUserId;\n // 2) 退回使用真实用户 ID\n return request.userContext?.userId;\n }\n\n /**\n * 从请求中提取环境上下文\n */\n protected extractEnvironmentContext(request: {\n ip?: string;\n connection?: { remoteAddress?: string };\n headers: Record<string, string | string[] | undefined>;\n query?: Record<string, unknown>;\n }): EnvironmentContext {\n const regionHeader = request.headers['x-region'];\n const osHeader = request.headers['x-os'];\n const uaHeader = request.headers['user-agent'];\n const region = Array.isArray(regionHeader) ? regionHeader[0] : regionHeader;\n const os = Array.isArray(osHeader) ? osHeader[0] : osHeader;\n const userAgent = Array.isArray(uaHeader) ? uaHeader[0] : uaHeader;\n return {\n network: {\n ip: request.ip || request.connection?.remoteAddress,\n region,\n },\n device: {\n type: this.detectDeviceType(userAgent),\n os,\n browser: userAgent,\n },\n custom: {\n headers: request.headers,\n query: request.query,\n },\n };\n }\n\n /**\n * 检测设备类型\n */\n private detectDeviceType(\n userAgent?: string\n ): 'mobile' | 'desktop' | 'tablet' {\n if (!userAgent) return 'desktop';\n\n const ua = userAgent.toLowerCase();\n if (/(tablet|ipad|playbook|silk)|(android(?!.*mobile))/i.test(ua)) {\n return 'tablet';\n }\n if (/mobile|iphone|ipod|android|blackberry|opera mini|iemobile/i.test(ua)) {\n return 'mobile';\n }\n return 'desktop';\n }\n\n /**\n * 检查角色要求\n */\n private async checkRoleRequirement(\n requirement: RoleRequirement,\n userId?: string,\n mockRoles?: string[]\n ) {\n return this.permissionService.checkRoles(requirement, userId, mockRoles);\n }\n\n /**\n * 检查权限要求\n */\n private async checkPermissionRequirement(\n params: CheckPermissionsParams,\n userId?: string,\n mockRoles?: string[]\n ) {\n return this.permissionService.checkPermissions(params, userId, mockRoles);\n }\n\n /**\n * 检查环境要求\n */\n private async checkEnvironmentRequirement(\n requirement: EnvironmentRequirement,\n envContext: EnvironmentContext,\n request: unknown\n ): Promise<void> {\n // 检查网络要求\n if (requirement.network) {\n await this.checkNetworkRequirement(requirement.network, envContext);\n }\n\n // 检查设备要求\n if (requirement.device) {\n await this.checkDeviceRequirement(requirement.device, envContext);\n }\n\n // 执行自定义检查\n if (requirement.custom) {\n const result = await requirement.custom(request);\n if (!result) {\n throw PermissionDeniedException.environmentRequired(\n 'custom',\n '不满足自定义环境要求'\n );\n }\n }\n }\n\n /**\n * 检查网络要求\n */\n private async checkNetworkRequirement(\n requirement: {\n allowedIPs?: string[];\n blockedIPs?: string[];\n allowedRegions?: string[];\n },\n envContext: EnvironmentContext\n ): Promise<void> {\n const ip = envContext.network?.ip;\n\n if (requirement.allowedIPs && ip) {\n const isAllowed = this.checkIPMatch(ip, requirement.allowedIPs);\n if (!isAllowed) {\n throw PermissionDeniedException.environmentRequired(\n 'network.allowedIPs',\n `IP 地址不在允许列表中: ${ip}`\n );\n }\n }\n\n if (requirement.blockedIPs && ip) {\n const isBlocked = this.checkIPMatch(ip, requirement.blockedIPs);\n if (isBlocked) {\n throw PermissionDeniedException.environmentRequired(\n 'network.blockedIPs',\n `IP 地址被禁止访问: ${ip}`\n );\n }\n }\n\n if (requirement.allowedRegions && envContext.network?.region) {\n const isAllowed = requirement.allowedRegions.includes(\n envContext.network.region\n );\n if (!isAllowed) {\n throw PermissionDeniedException.environmentRequired(\n 'network.allowedRegions',\n `地区不在允许列表中: ${envContext.network.region}`\n );\n }\n }\n }\n\n /**\n * 检查设备要求\n */\n private async checkDeviceRequirement(\n requirement: { types?: Array<'mobile' | 'desktop' | 'tablet'> },\n envContext: EnvironmentContext\n ): Promise<void> {\n if (requirement.types && envContext.device?.type) {\n const isAllowed = requirement.types.includes(envContext.device.type);\n if (!isAllowed) {\n throw PermissionDeniedException.environmentRequired(\n 'device.types',\n `不允许的设备类型: ${envContext.device.type}`\n );\n }\n }\n }\n\n /**\n * 检查 IP 是否匹配\n * 简化版本,仅支持精确匹配\n * 生产环境建议使用 ipaddr.js 等库\n */\n private checkIPMatch(ip: string, patterns: string[]): boolean {\n // 简单实现:仅支持精确匹配和基本的 CIDR(未来可扩展)\n return patterns.some(pattern => {\n if (pattern === ip) return true;\n // TODO: 支持 CIDR 匹配\n return false;\n });\n }\n}\n","export * from './memory-cache';\nimport { MOCK_ROLES_COOKIE_KEY } from '../const';\nimport { Request } from 'express';\n\nexport function getMockRolesFromCookie(request: Request): string[] | undefined {\n const mockRoles = request.cookies?.[MOCK_ROLES_COOKIE_KEY];\n return mockRoles ? mockRoles.split(',').map((s: string) => s.trim()).filter(Boolean) : undefined;\n}","import { Injectable, NestMiddleware, Logger } from '@nestjs/common';\nimport { Request, Response, NextFunction } from 'express';\nimport { PermissionService } from '../services/permission.service';\nimport { getMockRolesFromCookie } from '../utils';\nimport { ENABLE_MOCK_ROLE_KEY } from '../const';\n\nexport interface UserContext {\n userId?: string;\n tenantId?: number;\n appId?: string;\n // 用户角色\n userRoles?: string[];\n}\n\n/**\n * 扩展 Express Request 类型,添加用户权限相关字段\n */\n// eslint-disable-next-line @typescript-eslint/no-namespace\ndeclare global {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n namespace Express {\n interface Request {\n userContext: UserContext;\n [ENABLE_MOCK_ROLE_KEY]?: boolean;\n }\n }\n}\n\n@Injectable()\nexport class RolesMiddleware implements NestMiddleware {\n private readonly logger = new Logger(RolesMiddleware.name);\n constructor(private readonly permissionService: PermissionService) {}\n\n async use(req: Request, _res: Response, next: NextFunction) {\n try {\n // 1. 从请求中提取 userId\n const userId = req.userContext?.userId;\n const mockRoles = req[ENABLE_MOCK_ROLE_KEY]\n ? getMockRolesFromCookie(req)\n : undefined;\n\n // 2. 通过 PermissionService 获取用户权限数据\n this.logger.debug(`Fetching roles for user: ${userId}`);\n const permissionData = await this.permissionService.getUserPermissions(\n userId,\n mockRoles\n );\n let roles;\n if (permissionData) {\n roles = permissionData.roles;\n }\n // 3. 将 roles 附加到 userContext\n req.userContext.userRoles = roles;\n req.userContext.userId = userId;\n\n // 4. 记录日志\n this.logger.debug(`User ${userId} roles loaded: [${roles?.join(', ')}]`);\n\n next();\n } catch (error) {\n // 获取权限失败时,记录错误但不阻塞请求\n // 让后续的 Guard 来处理权限验证\n this.logger.warn(\n `Failed to fetch roles for request: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n next();\n }\n }\n}\n","import {\n ExceptionFilter,\n Catch,\n ArgumentsHost,\n} from '@nestjs/common';\nimport { Response } from 'express';\nimport { PermissionDeniedException } from '../exceptions/permission-denied.exception';\n\n/**\n * AuthZPaas 异常过滤器\n * 确保权限错误信息能够正确返回给客户端\n */\n@Catch(PermissionDeniedException)\nexport class AuthZPaasExceptionFilter implements ExceptionFilter {\n catch(exception: PermissionDeniedException, host: ArgumentsHost) {\n const ctx = host.switchToHttp();\n const response = ctx.getResponse<Response>();\n const status = exception.getStatus();\n const exceptionResponse = exception.getResponse();\n\n // 格式化错误响应\n const errorResponse: any = {\n statusCode: status,\n timestamp: new Date().toISOString(),\n cause: exception.cause,\n type: exception.type,\n message:\n typeof exceptionResponse === 'string'\n ? exceptionResponse\n : (exceptionResponse as any).message || '权限不足',\n };\n\n // 添加详细信息\n if (exception.details.requiredRoles) {\n errorResponse.requiredRoles = exception.details.requiredRoles;\n }\n\n if (exception.details.requiredPermissions) {\n errorResponse.requiredPermissions = exception.details.requiredPermissions;\n }\n\n if (exception.details.environmentRequirement) {\n errorResponse.environmentRequirement = exception.details.environmentRequirement;\n }\n\n if (exception.details.metadata) {\n errorResponse.metadata = exception.details.metadata;\n }\n\n response.status(status).json(errorResponse);\n }\n}\n","import { SetMetadata } from '@nestjs/common';\nimport { ROLES_KEY } from '../const';\n\n/**\n * 角色要求配置\n */\nexport interface RoleRequirement {\n /** 需要的角色列表 */\n roles: string[];\n /** 是否需要所有角色(AND),默认 false(OR) */\n and?: boolean;\n}\n\nexport type CheckRoleRequirement = RoleRequirement;\n\n/**\n * 要求用户拥有指定角色\n * \n * @example\n * ```typescript\n * // 需要任一角色\n * @CanRole(['admin', 'moderator'])\n * async deleteUser() {}\n * \n * // 需要所有角色\n * @CanRole(['admin', 'superuser'], true)\n * async criticalOperation() {}\n * ```\n */\nexport const CanRole = (\n role: string[] | string,\n and: boolean = false,\n): MethodDecorator => {\n // 解析参数\n let requirement: RoleRequirement;\n\n if (!Array.isArray(role) && typeof role === 'string') {\n // 对象形式\n requirement = {\n roles: [role],\n and: and,\n };\n } else if (Array.isArray(role) && role.some((role) => typeof role === 'string')) {\n // 字符串列表形式\n requirement = {\n roles: role as string[],\n and: and,\n };\n } else {\n throw new Error('Invalid CanRole parameter: ' + JSON.stringify(role));\n }\n\n return SetMetadata(ROLES_KEY, requirement);\n};\n","import { SetMetadata } from '@nestjs/common';\nimport { PERMISSIONS_KEY } from '../const';\nimport { Action, Subject } from '../types';\n\n/**\n * 权限要求配置\n */\nexport interface PermissionRequirement {\n /** 操作类型 */\n actions: Action[];\n /** 资源类型 */\n subject: Subject;\n /** 是否需要所有操作(AND),默认 true(AND) */\n or?: boolean;\n}\n\n/**\n * 要求用户拥有指定权限\n * \n * @example\n * ```typescript\n * // 通过 CASL 动作和资源检查(推荐使用)\n * @CanPermission({ actions: ['create'], subject: 'User' })\n * async createUser() {}\n * \n * @CanPermission({ actions: ['update'], subject: 'Article' })\n * async updateArticle() {}\n * \n * @CanPermission({ actions: ['delete'], subject: 'Comment' })\n * async deleteComment() {}\n * \n * // 注意:基于权限名称列表的检查方式暂不支持\n * // permissions 参数暂时保留用于未来扩展\n * ```\n */\nexport const CanPermission = (\n permission: PermissionRequirement[] | PermissionRequirement,\n or?: boolean,\n): MethodDecorator => {\n let requirements: PermissionRequirement[];\n if (Array.isArray(permission)) {\n requirements = permission as unknown as PermissionRequirement[];\n } else if ( typeof permission === 'object' && 'subject' in permission) {\n requirements =[permission as unknown as PermissionRequirement];\n } else {\n throw new Error('Invalid CanPermission parameter: ' + JSON.stringify(permission));\n }\n return SetMetadata(PERMISSIONS_KEY, {\n requirements,\n or: or ?? false,\n });\n};\n","import { SetMetadata } from '@nestjs/common';\nimport { ENVIRONMENT_KEY } from '../const';\n\n/**\n * 网络要求\n */\nexport interface NetworkRequirement {\n /** 允许的 IP 地址列表(支持 CIDR) */\n allowedIPs?: string[];\n /** 禁止的 IP 地址列表 */\n blockedIPs?: string[];\n /** 允许的地区列表 */\n allowedRegions?: string[];\n}\n\n/**\n * 设备要求\n */\nexport interface DeviceRequirement {\n /** 允许的设备类型 */\n types?: Array<'mobile' | 'desktop' | 'tablet'>;\n /** 允许的操作系统 */\n os?: string[];\n /** 允许的浏览器 */\n browsers?: string[];\n}\n\n/**\n * 环境要求配置\n */\nexport interface EnvironmentRequirement {\n /** 网络要求 */\n network?: NetworkRequirement;\n /** 设备要求 */\n device?: DeviceRequirement;\n /** 自定义环境验证函数 */\n custom?: (context: any) => boolean | Promise<boolean>;\n}\n\n/**\n * 要求特定的环境条件\n * \n * @example\n * ```typescript\n * // 仅允许桌面端访问\n * @CanEnv({ device: { types: ['desktop'] } })\n * async adminPanel() {}\n * \n * // 限制 IP 访问\n * @CanEnv({ \n * network: { \n * allowedIPs: ['192.168.1.0/24', '10.0.0.1'] \n * } \n * })\n * async internalAPI() {}\n * \n * // 自定义验证\n * @CanEnv({ \n * custom: (ctx) => ctx.headers['x-api-key'] === 'secret' \n * })\n * async secretEndpoint() {}\n * ```\n */\nexport const CanEnv = (\n requirement: EnvironmentRequirement,\n): MethodDecorator => {\n return SetMetadata(ENVIRONMENT_KEY, requirement);\n};\n","import { createParamDecorator, ExecutionContext } from '@nestjs/common';\n\n/**\n * 获取当前用户 ID\n * \n * @example\n * ```typescript\n * @Get('profile')\n * getProfile(@UserId() userId: string) {\n * return { userId };\n * }\n * ```\n */\nexport const UserId = createParamDecorator(\n (_data: unknown, ctx: ExecutionContext): string | undefined => {\n const request = ctx.switchToHttp().getRequest();\n return request.userContext?.userId;\n },\n);\n\n\n","import { createParamDecorator, ExecutionContext } from '@nestjs/common';\nimport { getMockRolesFromCookie } from '../utils';\nimport { ENABLE_MOCK_ROLE_KEY } from '../const';\n\nexport const MockRoles = createParamDecorator(\n (_data: unknown, ctx: ExecutionContext): string[] | undefined => {\n const request = ctx.switchToHttp().getRequest();\n const mockRoles = getMockRolesFromCookie(request);\n return request[ENABLE_MOCK_ROLE_KEY] ? mockRoles : undefined;\n },\n);\n","import { Controller, Get, Post, Res, Body } from '@nestjs/common';\nimport { PermissionService } from '../services/permission.service';\nimport { UserId } from '../decorators/user-id.decorator';\nimport { MockRoles } from '../decorators/mock-roles.decorator';\nimport type { Permission } from '../types';\nimport { ApiOperation, ApiResponse, ApiTags, ApiProperty } from '@nestjs/swagger';\nimport { MOCK_ROLES_COOKIE_KEY } from '../const';\nimport type { Response } from 'express';\n\n/**\n * 权限数据 DTO\n */\nexport class PermissionDto {\n @ApiProperty({ description: '资源类型', example: 'task' })\n sub!: string;\n\n @ApiProperty({ description: '操作类型列表', example: ['create', 'read', 'update', 'delete'], type: [String] })\n actions!: string[];\n\n @ApiProperty({ description: '权限ID', required: false })\n id?: string;\n\n @ApiProperty({ description: '权限名称', required: false })\n name?: string;\n\n @ApiProperty({ description: '权限条件', required: false, type: 'object' })\n conditions?: Record<string, unknown>;\n}\n\n/**\n * 权限查询响应\n */\nexport class PermissionResponse {\n @ApiProperty({ description: '用户ID', example: 'user123', required: false })\n userId?: string;\n\n @ApiProperty({ description: '用户角色列表', example: ['admin', 'user'], type: [String] })\n roles!: string[];\n\n @ApiProperty({ description: '用户权限点位列表', type: [PermissionDto] })\n permissions!: Permission[];\n\n @ApiProperty({ description: '数据获取时间', example: '2025-10-14T00:00:00.000Z' })\n fetchedAt!: Date;\n}\n\n/**\n * 权限控制器\n * 提供权限查询接口,供前端客户端使用\n */\n@ApiTags('权限管理')\n@Controller('api/permissions')\nexport class PermissionController {\n constructor(private readonly permissionService: PermissionService) {}\n\n /**\n * 获取当前用户的权限数据\n * \n * @param userId 当前用户ID(从请求上下文中获取)\n * @returns 用户权限数据\n * \n * @example\n * GET /api/permissions\n * \n * Response:\n * {\n * \"userId\": \"user123\",\n * \"roles\": [\"admin\", \"user\"],\n * \"permissions\": [\n * { \"sub\": \"task\", \"actions\": [\"create\", \"read\", \"update\", \"delete\"] }\n * ],\n * \"fetchedAt\": \"2025-10-14T00:00:00.000Z\"\n * }\n */\n @Get()\n @ApiOperation({ summary: '获取当前用户的权限数据' })\n @ApiResponse({ status: 200, description: '成功返回用户权限数据', type: PermissionResponse })\n @ApiResponse({ status: 400, description: '用户未认证' })\n async getUserPermissions(@UserId() userId: string,\n @MockRoles() mockRoles: string[],\n ): Promise<PermissionResponse> {\n const permissionData = await this.permissionService.getUserPermissions(userId, mockRoles);\n \n return {\n userId: permissionData?.userId,\n roles: permissionData?.roles || [],\n permissions: permissionData?.permissions || [],\n fetchedAt: permissionData?.fetchedAt || new Date(),\n };\n }\n\n /**\n * 开启角色模拟:将传入的 userId 写入 cookie,服务端优先使用该值请求权限\n */\n @Post('mock/enable')\n @ApiOperation({ summary: '开启角色模拟(写入 cookie: mockRoles)' })\n @ApiResponse({ status: 200, description: '模拟已开启' })\n async enableMock(@Res({ passthrough: true }) res: Response, @Body('roles') roles: string[]) {\n if (!Array.isArray(roles) || roles.length === 0) {\n return { success: false, message: 'roles 不能为空' };\n }\n const val = roles.join(',');\n res.cookie(MOCK_ROLES_COOKIE_KEY, val, {\n httpOnly: false,\n sameSite: 'lax',\n maxAge: 24 * 60 * 60 * 1000,\n path: '/',\n });\n return { success: true, roles };\n }\n\n /**\n * 关闭角色模拟:清除 cookie\n */\n @Post('mock/disable')\n @ApiOperation({ summary: '关闭角色模拟(清除 cookie: mockRoles)' })\n @ApiResponse({ status: 200, description: '模拟已关闭' })\n async disableMock(@Res({ passthrough: true }) res: Response) {\n res.clearCookie(MOCK_ROLES_COOKIE_KEY, { path: '/' });\n return { success: true };\n }\n @Get('mock/status')\n @ApiOperation({ summary: '获取当前用户的角色模拟状态' })\n @ApiResponse({ status: 200, description: '成功返回角色模拟数据' })\n async getMockRoles(@MockRoles() mockRoles: string[] | undefined, @UserId() userId: string): Promise<{ mocking: boolean, roles: string[], permissions: Permission[], fetchedAt: Date, userId: string }> {\n const permissionData = await this.permissionService.getUserPermissions(userId, mockRoles);\n return { mocking: !!mockRoles, roles: permissionData?.roles || [], permissions: permissionData?.permissions || [], fetchedAt: permissionData?.fetchedAt || new Date(), userId: userId };\n }\n}\n"],"mappings":"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","names":["Module","APP_FILTER","APP_GUARD","Reflector","ANONYMOUS_USER_ID","PERMISSION_API_CONFIG_TOKEN","Symbol","CACHE_CONFIG_TOKEN","AUTHZPAAS_MODULE_OPTIONS","ROLES_KEY","PERMISSIONS_KEY","ENVIRONMENT_KEY","NEED_LOGIN_KEY","DEFAULT_LOGIN_PATH","MOCK_ROLES_COOKIE_KEY","ENABLE_MOCK_ROLE_KEY","Injectable","Inject","Logger","HttpStatus","MemoryCache","cache","accessOrder","options","hits","misses","Map","get","key","enabled","undefined","item","Date","now","expireAt","delete","removeFromAccessOrder","updateAccessOrder","value","set","size","max","has","evictLRU","ttl","clear","getStats","hitRate","push","index","indexOf","splice","length","oldestKey","Injectable","AbilityBuilder","PureAbility","ROLE_SUBJECT","AbilityFactory","createForUser","permissionData","can","build","AbilityBuilder","PureAbility","permission","permissions","sub","actions","action","role","roles","HttpException","PermissionDeniedType","PermissionDeniedException","HttpException","type","details","httpStatusCode","statusCode","cause","message","requiredRoles","requiredPermissions","environmentRequirement","metadata","name","unauthenticated","roleRequired","and","join","permissionRequired","or","customMessage","length","perm","subject","actions","map","environmentRequired","requirement","PermissionService","logger","Logger","name","cache","pendingRequests","Map","apiConfig","cacheConfig","abilityFactory","MemoryCache","ttl","max","enabled","log","baseUrl","buildCacheKey","userId","mockRoles","length","sortedRoles","sort","join","ANONYMOUS_USER_ID","getUserPermissions","endpoint","warn","key","cached","get","debug","permissionData","pendingRequest","requestPromise","getPermissionsByMockRoles","fetchFromApi","dataWithTimestamp","fetchedAt","Date","ability","createForUser","set","error","delete","apiToken","headers","timeout","url","requestHeaders","controller","AbortController","timeoutId","setTimeout","abort","response","fetch","method","signal","clearTimeout","ok","Error","status","statusText","PermissionDeniedException","cause","type","PermissionDeniedType","PERMISSION_CONFIG_QUERY_FAILED","message","HttpStatus","INTERNAL_SERVER_ERROR","data","json","roles","map","role","permissions","err","rolesParam","encodeURIComponent","userParam","getUserAbility","newCached","clearUserCache","clearAllCache","clear","getCacheStats","getStats","checkRoles","requirement","BAD_REQUEST","and","checkResults","can","ROLE_SUBJECT","hasRole","every","result","some","userRoles","roleRequired","checkPermissions","params","requirements","or","failedRequirements","actions","subject","action","hasPermission","push","String","permissionRequired","getAbility","Injectable","Inject","Reflector","getMockRolesFromCookie","request","mockRoles","cookies","MOCK_ROLES_COOKIE_KEY","split","map","s","trim","filter","Boolean","undefined","AuthZPaasGuard","reflector","permissionService","moduleOptions","canActivate","context","http","switchToHttp","request","getRequest","ENABLE_MOCK_ROLE_KEY","enableMockRole","userId","extractUserId","mockRoles","getMockRolesFromCookie","undefined","checkRoleRequirement","getAllAndOverride","ROLES_KEY","getHandler","getClass","checkPermissionParams","PERMISSIONS_KEY","checkPermissionRequirement","envRequirement","ENVIRONMENT_KEY","envContext","extractEnvironmentContext","checkEnvironmentRequirement","mockUserId","cookies","userContext","regionHeader","headers","osHeader","uaHeader","region","Array","isArray","os","userAgent","network","ip","connection","remoteAddress","device","type","detectDeviceType","browser","custom","query","ua","toLowerCase","test","requirement","checkRoles","params","checkPermissions","checkNetworkRequirement","checkDeviceRequirement","result","PermissionDeniedException","environmentRequired","allowedIPs","isAllowed","checkIPMatch","blockedIPs","isBlocked","allowedRegions","includes","types","patterns","some","pattern","Injectable","Logger","RolesMiddleware","logger","Logger","name","permissionService","use","req","_res","next","userId","userContext","mockRoles","ENABLE_MOCK_ROLE_KEY","getMockRolesFromCookie","undefined","debug","permissionData","getUserPermissions","roles","userRoles","join","error","warn","Error","message","Catch","AuthZPaasExceptionFilter","catch","exception","host","ctx","switchToHttp","response","getResponse","status","getStatus","exceptionResponse","errorResponse","statusCode","timestamp","Date","toISOString","cause","type","message","details","requiredRoles","requiredPermissions","environmentRequirement","metadata","json","AuthZPaasModule","forRoot","options","permissionApi","cache","ttl","max","enabled","isGlobal","module","global","controllers","providers","provide","AUTHZPAAS_MODULE_OPTIONS","useValue","loginPath","DEFAULT_LOGIN_PATH","PERMISSION_API_CONFIG_TOKEN","CACHE_CONFIG_TOKEN","Reflector","PermissionService","AbilityFactory","AuthZPaasGuard","RolesMiddleware","APP_GUARD","useClass","APP_FILTER","AuthZPaasExceptionFilter","exports","forRootAsync","imports","inject","useFactory","moduleOptions","SetMetadata","CanRole","role","and","requirement","Array","isArray","roles","some","Error","JSON","stringify","SetMetadata","ROLES_KEY","SetMetadata","CanPermission","permission","or","requirements","Array","isArray","Error","JSON","stringify","SetMetadata","PERMISSIONS_KEY","SetMetadata","CanEnv","requirement","SetMetadata","ENVIRONMENT_KEY","createParamDecorator","UserId","_data","ctx","request","switchToHttp","getRequest","userContext","userId","createParamDecorator","MockRoles","createParamDecorator","_data","ctx","request","switchToHttp","getRequest","mockRoles","getMockRolesFromCookie","ENABLE_MOCK_ROLE_KEY","undefined","Controller","Get","Post","Res","Body","ApiOperation","ApiResponse","ApiTags","ApiProperty","PermissionDto","sub","actions","id","name","conditions","description","example","type","String","required","PermissionResponse","userId","roles","permissions","fetchedAt","PermissionController","permissionService","getUserPermissions","mockRoles","permissionData","Date","enableMock","res","Array","isArray","length","success","message","val","join","cookie","MOCK_ROLES_COOKIE_KEY","httpOnly","sameSite","maxAge","path","disableMock","clearCookie","getMockRoles","mocking","summary","status","passthrough"]}
1
+ {"version":3,"sources":["../src/authzpaas.module.ts","../src/services/permission.service.ts","../../nestjs-authnpaas/src/authnpaas.module.ts","../../nestjs-authnpaas/src/const.ts","../../nestjs-authnpaas/src/guards/authnpaas.guard.ts","../../nestjs-authnpaas/src/decorators/public.decorator.ts","../../nestjs-authnpaas/src/decorators/need-login.decorator.ts","../src/const.ts","../src/casl/ability.factory.ts","../src/exceptions/permission-denied.exception.ts","../src/guards/authzpaas.guard.ts","../src/decorators/can-role.decorator.ts"],"sourcesContent":["import { DynamicModule, Module, Type } from '@nestjs/common';\nimport { APP_GUARD, Reflector } from '@nestjs/core';\n\nimport { PermissionService } from './services/permission.service';\nimport { AbilityFactory } from './casl/ability.factory';\nimport { AuthZPaasGuard } from './guards/authzpaas.guard';\nimport { AuthZPaasModuleOptions } from './types';\nimport { AUTHZPAAS_MODULE_OPTIONS, PERMISSION_API_CONFIG_TOKEN } from './const';\n\nexport interface AuthZPaasModuleAsyncOptions {\n imports?: Type<unknown>[];\n inject?: (string | symbol | Type<unknown>)[];\n useFactory: (\n ...args: unknown[]\n ) => Promise<AuthZPaasModuleOptions> | AuthZPaasModuleOptions;\n}\n\n@Module({})\nexport class AuthZPaasModule {\n static forRoot(options?: AuthZPaasModuleOptions): DynamicModule {\n const { permissionApi = {} } = options || {};\n return {\n module: AuthZPaasModule,\n global: true,\n controllers: [],\n providers: [\n // 配置提供者\n {\n provide: AUTHZPAAS_MODULE_OPTIONS,\n useValue: { ...options },\n },\n {\n provide: PERMISSION_API_CONFIG_TOKEN,\n useValue: permissionApi,\n },\n // 核心服务\n Reflector,\n // 服务提供者\n PermissionService,\n AbilityFactory,\n AuthZPaasGuard,\n // 守卫提供者\n {\n provide: APP_GUARD,\n useClass: AuthZPaasGuard,\n },\n ],\n exports: [PermissionService, AbilityFactory],\n };\n }\n\n /**\n * 异步注册 AuthZPaas 模块(根模块)\n * 用于需要从配置服务获取设置的场景\n *\n * @param options 异步配置选项\n * @returns 动态模块\n *\n * @example\n * ```typescript\n * @Module({\n * imports: [\n * AuthZPaasModule.forRootAsync({\n * imports: [ConfigModule],\n * inject: [ConfigService],\n * useFactory: async (configService: ConfigService) => ({\n * permissionApi: {\n * baseUrl: configService.get('PERMISSION_API_URL'),\n * apiToken: configService.get('PERMISSION_API_TOKEN'),\n * },\n * cache: {\n * ttl: configService.get('CACHE_TTL', 300),\n * max: configService.get('CACHE_MAX', 1000),\n * },\n * }),\n * }),\n * ],\n * })\n * export class AppModule {}\n * ```\n */\n static forRootAsync(options: AuthZPaasModuleAsyncOptions): DynamicModule {\n const { imports = [], inject = [], useFactory } = options;\n\n return {\n module: AuthZPaasModule,\n global: true,\n imports,\n controllers: [],\n providers: [\n // 异步配置提供者\n {\n provide: AUTHZPAAS_MODULE_OPTIONS,\n useFactory,\n inject,\n },\n // 权限 API 配置提供者\n {\n provide: PERMISSION_API_CONFIG_TOKEN,\n useFactory: (moduleOptions: AuthZPaasModuleOptions) => {\n return moduleOptions.permissionApi;\n },\n inject: [AUTHZPAAS_MODULE_OPTIONS],\n },\n // 核心服务\n Reflector,\n // 服务提供者\n PermissionService,\n AbilityFactory,\n AuthZPaasGuard,\n // 守卫提供者\n {\n provide: APP_GUARD,\n useClass: AuthZPaasGuard,\n },\n ],\n exports: [PermissionService, AbilityFactory],\n };\n }\n}\n","import { Injectable, Logger, HttpStatus, Inject } from '@nestjs/common';\nimport { Public } from '@lark-apaas/nestjs-authnpaas';\n\nimport type {\n UserPermissionData,\n UserRolesDTO,\n UserContext,\n PermissionApiConfig,\n} from '../types';\nimport { ANONYMOUS_USER_ID, PERMISSION_API_CONFIG_TOKEN } from '../const';\nimport { AbilityFactory, ROLE_SUBJECT } from '../casl/ability.factory';\nimport {\n PermissionDeniedException,\n PermissionDeniedType,\n} from '../exceptions/permission-denied.exception';\nimport type { RoleRequirement } from '../decorators';\n\n/**\n * 权限服务\n * 内置权限获取和缓存逻辑,以及权限检查逻辑\n */\n@Injectable()\n@Public() // 跳过 Guard 的鉴权检查,避免循环调用\nexport class PermissionService {\n private readonly logger = new Logger(PermissionService.name);\n\n constructor(\n @Inject(PERMISSION_API_CONFIG_TOKEN)\n private readonly apiConfig: PermissionApiConfig,\n private readonly abilityFactory: AbilityFactory\n ) {}\n\n /**\n * 获取用户权限数据\n */\n async getUserPermissions(\n requestDto: UserRolesDTO\n ): Promise<UserPermissionData | null> {\n // 创建新的请求 Promise\n const requestPromise = (async () => {\n const userId = requestDto.userId || ANONYMOUS_USER_ID;\n try {\n const permissionData = await this.fetchFromApi(requestDto);\n // 添加获取时间\n const dataWithTimestamp: UserPermissionData = {\n ...permissionData,\n fetchedAt: new Date(),\n };\n\n return dataWithTimestamp;\n } catch (error) {\n this.logger.error(\n `Failed to fetch permissions for user ${userId}:`,\n error\n );\n throw error;\n }\n })();\n\n return requestPromise;\n }\n\n /**\n * 从 API 获取权限数据\n * 内置实现,用户无需配置\n */\n private async fetchFromApi(\n requestDto: UserRolesDTO\n ): Promise<UserPermissionData> {\n const { timeout = 5000 } = this.apiConfig || {};\n const { baseUrl, userId, appId, cookie, csrfToken } = requestDto;\n\n // 构建完整获取用户权限 URL\n const url = `${baseUrl}/spark/app/${appId}/runtime/api/v1/permissions/roles`;\n\n // 构建请求头\n const requestHeaders: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n if (cookie) {\n requestHeaders.Cookie = cookie;\n }\n\n if (csrfToken) {\n requestHeaders['X-Suda-Csrf-Token'] = csrfToken;\n }\n\n // 发起请求(带超时控制)\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n credentials: 'include',\n headers: requestHeaders,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const error = new Error(\n `Permission API returned ${response.status}: ${response.statusText}`\n );\n throw new PermissionDeniedException(\n {\n cause: error,\n type: PermissionDeniedType.PERMISSION_CONFIG_QUERY_FAILED,\n message: error.message,\n },\n HttpStatus.INTERNAL_SERVER_ERROR\n );\n }\n\n const data = (await response.json()) as {\n data?: { roleList?: string[] };\n };\n\n return {\n userId,\n roles: data.data?.roleList || [],\n // TODO: 基于权限点位设置能力\n // permissions: data.permissions || [],\n fetchedAt: new Date(),\n };\n } catch (error: unknown) {\n console.log('error', error);\n clearTimeout(timeoutId);\n let err = error as Error;\n\n if ((error as { name?: string }).name === 'AbortError') {\n err = new Error(`Permission API request timeout after ${timeout}ms`);\n }\n\n throw new PermissionDeniedException(\n {\n cause: err,\n type: PermissionDeniedType.PERMISSION_CONFIG_QUERY_FAILED,\n message: err.message,\n },\n HttpStatus.INTERNAL_SERVER_ERROR\n );\n }\n }\n\n // /**\n // * 获取用户的 Ability 实例(带缓存)\n // * @param userId 用户ID\n // * @returns CASL Ability 实例\n // */\n // private async getUserAbility(\n // userId?: string,\n // mockRoles?: string[]\n // ): Promise<AppAbility> {\n // // 计算缓存 key\n // const key = this.buildCacheKey(userId, mockRoles);\n\n // // 尝试从缓存获取\n // const cached = this.cache.get(key);\n // if (cached) {\n // return cached.ability;\n // }\n\n // // 缓存未命中,调用 getUserPermissions 会创建并缓存\n // await this.getUserPermissions(userId, mockRoles);\n\n // // 再次从缓存获取(此时一定存在)\n // const newCached = this.cache.get(key);\n // return newCached!.ability;\n // }\n\n /**\n * 检查角色要求\n * 使用 CASL Ability 统一鉴权方式\n * @param requirement 角色要求\n * @param userId 用户ID,匿名用户时为空\n * @returns 用户权限数据\n * @throws PermissionDeniedException 当角色不满足时\n */\n async checkRoles(\n requirement: RoleRequirement,\n userContext?: UserContext,\n cookie?: string,\n csrfToken?: string\n ): Promise<UserPermissionData> {\n const userId = userContext?.userId || ANONYMOUS_USER_ID;\n if (!csrfToken) {\n throw new PermissionDeniedException(\n {\n cause: new Error('CSRF token is required'),\n type: PermissionDeniedType.PERMISSION_CONFIG_QUERY_FAILED,\n message: 'CSRF token is required',\n },\n HttpStatus.BAD_REQUEST\n );\n }\n\n const permissionData = await this.getUserPermissions({\n // FIXME: 使用 request 的 base url\n baseUrl: userContext?.baseUrl || '',\n userId,\n appId: userContext?.appId || '',\n csrfToken,\n cookie,\n });\n\n if (!permissionData) {\n throw new PermissionDeniedException(\n {\n cause: new Error('Permission data fetch api is not configured'),\n type: PermissionDeniedType.PERMISSION_CONFIG_QUERY_FAILED,\n message: 'Permission data fetch api is not configured',\n },\n HttpStatus.BAD_REQUEST\n );\n }\n\n // 创建 Ability 实例\n const ability = this.abilityFactory.createForUser(permissionData);\n\n const { roles, and } = requirement;\n\n // 使用 CASL 统一检查角色权限\n // 角色作为 action,'@role' 作为 subject\n const checkResults = roles.map(role => ability.can(role, ROLE_SUBJECT));\n\n const hasRole = and\n ? checkResults.every(result => result)\n : checkResults.some(result => result);\n\n if (!hasRole) {\n const userRoles = permissionData.roles;\n this.logger.warn(\n `角色检查失败: 用户 ${userId}, 用户角色 [${userRoles.join(', ')}], 需要 [${roles.join(', ')}]`\n );\n throw PermissionDeniedException.roleRequired(roles, and);\n }\n\n return permissionData;\n }\n\n // /**\n // * 检查权限要求\n // * @param requirements 权限要求列表\n // * @param userId 用户ID\n // * @returns 用户权限数据\n // * @throws PermissionDeniedException 当权限不满足时\n // */\n // async checkPermissions(\n // params: CheckPermissionsParams,\n // userId?: string,\n // mockRoles?: string[]\n // ): Promise<UserPermissionData> {\n // // 获取权限数据(用于返回)\n // const permissionData = await this.getUserPermissions(userId, mockRoles);\n // if (!permissionData) {\n // throw new PermissionDeniedException(\n // {\n // cause: new Error('Permission data fetch api is not configured'),\n // type: PermissionDeniedType.PERMISSION_CONFIG_QUERY_FAILED,\n // message: 'Permission data fetch api is not configured',\n // },\n // HttpStatus.BAD_REQUEST\n // );\n // }\n // const { requirements, or } = params;\n // if (!requirements || requirements.length === 0) {\n // return permissionData;\n // }\n\n // // 获取缓存的 Ability 实例\n // const ability = await this.getUserAbility(userId, mockRoles);\n\n // // 收集所有失败的权限要求\n // const failedRequirements: Array<{\n // actions: string[];\n // subject: string;\n // or: boolean;\n // }> = [];\n\n // // 检查每个权限要求\n // for (const requirement of requirements) {\n // const { actions, subject, or = false } = requirement;\n\n // // 使用缓存的 Ability 实例检查权限\n // const checkResults = actions.map(action => ability.can(action, subject));\n\n // // 根据 or 决定是 AND 还是 OR 逻辑\n // const hasPermission = or\n // ? checkResults.some(result => result)\n // : checkResults.every(result => result);\n\n // if (!hasPermission) {\n // failedRequirements.push({\n // actions,\n // subject: String(subject),\n // or,\n // });\n // }\n // }\n\n // // 如果有失败的权限要求,抛出异常\n // if (failedRequirements.length > 0) {\n // if (or && failedRequirements.length === requirements.length) {\n // throw PermissionDeniedException.permissionRequired(\n // failedRequirements.map(({ actions, subject }) => ({\n // actions,\n // subject: String(subject),\n // })),\n // true\n // );\n // } else if (!or) {\n // throw PermissionDeniedException.permissionRequired(\n // failedRequirements.map(({ actions, subject }) => ({\n // actions,\n // subject: String(subject),\n // })),\n // false\n // );\n // }\n // }\n // return permissionData;\n // }\n}\n","import { DynamicModule, Module } from '@nestjs/common';\nimport { APP_GUARD, Reflector } from '@nestjs/core';\nimport { AuthNPaasModuleOptions } from './types';\nimport { AUTHNPAAS_MODULE_OPTIONS } from './const';\nimport { AuthNPaasGuard } from './guards/authnpaas.guard';\n\n@Module({})\nexport class AuthNPaasModule {\n static forRoot(options?: AuthNPaasModuleOptions): DynamicModule {\n return {\n module: AuthNPaasModule,\n global: true,\n controllers: [],\n providers: [\n // 配置提供者\n {\n provide: AUTHNPAAS_MODULE_OPTIONS,\n useValue: {\n ...(options || {}),\n },\n },\n // 核心服务\n Reflector,\n // 服务提供者\n AuthNPaasGuard,\n // 守卫提供者\n {\n provide: APP_GUARD,\n useClass: AuthNPaasGuard,\n },\n ],\n exports: [],\n };\n }\n}\n","/**\n * 常量\n */\n\n/** AuthNPaas 模块选项 Token */\nexport const AUTHNPAAS_MODULE_OPTIONS = Symbol('AUTHNPAAS_MODULE_OPTIONS');\n\n/**\n * 元数据键\n */\n\n/** 需要登录元数据键 */\nexport const NEED_LOGIN_KEY = 'authnpaas:needLogin';\n","import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';\nimport { Response } from 'express';\nimport { Reflector } from '@nestjs/core';\nimport { NEED_LOGIN_KEY } from '../const';\n\n/**\n * AuthNPaas 守卫\n * 负责协调所有鉴权检查,具体检查逻辑委托给 PermissionService\n */\n@Injectable()\nexport class AuthNPaasGuard implements CanActivate {\n constructor(private reflector: Reflector) {}\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const http = context.switchToHttp();\n const request = http.getRequest();\n const response = http.getResponse<Response>();\n\n const { userId, loginUrl } = request.userContext || {};\n\n // 读取 NeedLogin 元数据并标记到请求对象,供异常过滤器使用\n const needLoginMeta = this.reflector.getAllAndOverride<{\n loginPath?: string;\n }>(NEED_LOGIN_KEY, [context.getHandler(), context.getClass()]);\n\n // NeedLogin 且无 userId -> 在守卫内直接重定向并拦截\n if (needLoginMeta && !userId && loginUrl) {\n response.redirect(302, loginUrl);\n return false;\n }\n\n return true;\n }\n}\n","import { SetMetadata } from '@nestjs/common';\n\n/**\n * Public 装饰器的元数据键\n */\nexport const IS_PUBLIC_KEY = 'isPublic';\n\n/**\n * 标记接口为公开接口,跳过 AuthNPaasGuard 的鉴权检查\n * \n * @example\n * ```typescript\n * @Controller('mock-api/users')\n * @Public() // 控制器级别\n * export class MockPermissionController {\n * @Get(':userId/permissions')\n * getUserPermissions() {}\n * }\n * \n * // 或者在方法级别\n * @Controller('api')\n * export class ApiController {\n * @Get('public-info')\n * @Public() // 方法级别\n * getPublicInfo() {}\n * }\n * ```\n */\nexport const Public = () => SetMetadata(IS_PUBLIC_KEY, true);\n\n","import { SetMetadata } from '@nestjs/common';\nimport { NEED_LOGIN_KEY } from '../const';\n\n/**\n * NeedLogin 装饰器\n * 标记接口需要登录。如果鉴权失败(如 401/403),异常过滤器会将请求重定向到登录页。\n * 可选地传入登录页路径,默认 '/login'。\n */\nexport const NeedLogin = (loginPath?: string): MethodDecorator & ClassDecorator => {\n return SetMetadata(NEED_LOGIN_KEY, { loginPath });\n};\n\n\n","/**\n * 常量\n */\n\n/** 匿名用户 ID */\nexport const ANONYMOUS_USER_ID = 'anonymous_user_id';\n\n/**\n * 依赖注入 Token\n */\n\n/** 权限 API 配置 Token */\nexport const PERMISSION_API_CONFIG_TOKEN = Symbol('PERMISSION_API_CONFIG');\n\n/** 缓存配置 Token */\nexport const CACHE_CONFIG_TOKEN = Symbol('CACHE_CONFIG');\n\n/** AuthZPaas 模块选项 Token */\nexport const AUTHZPAAS_MODULE_OPTIONS = Symbol('AUTHZPAAS_MODULE_OPTIONS');\n\n/**\n * 元数据键\n */\n\n/** 需要的角色元数据键 */\nexport const ROLES_KEY = 'authzpaas:roles';\n\n/** 需要的权限元数据键 */\nexport const PERMISSIONS_KEY = 'authzpaas:permissions';\n\n/** 需要的环境元数据键 */\nexport const ENVIRONMENT_KEY = 'authzpaas:environment';\n\n/** 需要登录元数据键 */\nexport const NEED_LOGIN_KEY = 'authzpaas:needLogin';\n\n/** 模块选项:登录页路径默认值 */\nexport const DEFAULT_LOGIN_PATH = '/login';\n\n/** 角色模拟的 Cookie 键名 */\nexport const MOCK_ROLES_COOKIE_KEY = 'mockRoles';\n\nexport const ENABLE_MOCK_ROLE_KEY = '__authzpaas_enableMockRole';\n","import { Injectable } from '@nestjs/common';\nimport { AbilityBuilder, PureAbility, AbilityClass } from '@casl/ability';\nimport { UserPermissionData, Action, Subject } from '../types';\n\n/**\n * CASL Ability 类型\n */\nexport type AppAbility = PureAbility<[Action, Subject]>;\n\n/**\n * 角色检查的特殊 Subject\n * 用于统一角色鉴权和权限点位鉴权\n * \n * 使用方式:\n * - 权限点位鉴权:ability.can('read', 'Todo')\n * - 角色鉴权:ability.can('admin', ROLE_SUBJECT) 或 ability.can('admin', '@role')\n */\nexport const ROLE_SUBJECT = '@role';\n\n/**\n * Ability 工厂\n * 负责根据用户权限数据创建 CASL Ability 实例\n * \n * 统一了两种鉴权方式:\n * 1. 基于角色的鉴权 - 角色名作为 action,'@role' 作为 subject\n * 2. 基于权限点位的鉴权 - 标准的 action + subject 模式\n */\n@Injectable()\nexport class AbilityFactory {\n /**\n * 为用户创建 Ability\n */\n createForUser(permissionData: UserPermissionData): AppAbility {\n const { can, build } = new AbilityBuilder<AppAbility>(\n PureAbility as AbilityClass<AppAbility>,\n );\n\n // TODO: 基于权限点位设置能力\n // for (const permission of permissionData.permissions) {\n // const { sub, actions } = permission;\n \n // // 为每个 action 添加权限\n // for (const action of actions) {\n // can(action as Action, sub);\n // }\n // }\n\n // 基于角色设置能力\n for (const role of permissionData.roles) {\n can(role as Action, ROLE_SUBJECT);\n }\n\n return build();\n }\n}\n","import { HttpException } from '@nestjs/common';\n\n/**\n * 权限拒绝异常类型\n */\nexport enum PermissionDeniedType {\n /** 用户未认证 */\n UNAUTHENTICATED = 'UNAUTHENTICATED',\n /** 缺少角色 */\n ROLE_REQUIRED = 'ROLE_REQUIRED',\n /** 缺少权限 */\n PERMISSION_REQUIRED = 'PERMISSION_REQUIRED',\n /** 权限配置查询失败 */\n PERMISSION_CONFIG_QUERY_FAILED = 'PERMISSION_CONFIG_QUERY_FAILED',\n}\n\n/**\n * 权限拒绝异常详情\n */\nexport interface PermissionDeniedDetails {\n /** 错误堆栈 */\n cause?: Error;\n /** 异常类型 */\n type: PermissionDeniedType;\n /** 错误消息 */\n message: string;\n /** 需要的角色(如果适用) */\n requiredRoles?: string[];\n /** 需要的权限(如果适用) */\n requiredPermissions?: Array<{\n actions: string[];\n subject: string;\n }>;\n /** 环境要求(如果适用) */\n environmentRequirement?: string;\n /** 额外信息 */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * 权限拒绝异常\n * 专门用于 AuthZPaas 模块的权限检查失败场景\n */\nexport class PermissionDeniedException extends HttpException {\n public readonly type: PermissionDeniedType;\n public readonly details: PermissionDeniedDetails;\n\n constructor(details: PermissionDeniedDetails, httpStatusCode: number = 403) {\n super(\n {\n statusCode: httpStatusCode,\n cause: details.cause,\n type: details.type,\n message: details.message,\n ...(details.requiredRoles && { requiredRoles: details.requiredRoles }),\n ...(details.requiredPermissions && {\n requiredPermissions: details.requiredPermissions,\n }),\n ...(details.environmentRequirement && {\n environmentRequirement: details.environmentRequirement,\n }),\n ...(details.metadata && { metadata: details.metadata }),\n },\n httpStatusCode\n );\n\n this.type = details.type;\n this.details = details;\n this.name = 'PermissionDeniedException';\n }\n\n /**\n * 创建用户未认证异常\n */\n static unauthenticated(\n message: string = '用户未认证'\n ): PermissionDeniedException {\n return new PermissionDeniedException({\n type: PermissionDeniedType.UNAUTHENTICATED,\n message,\n });\n }\n\n /**\n * 创建角色不足异常\n */\n static roleRequired(\n requiredRoles: string[],\n and: boolean = false\n ): PermissionDeniedException {\n const message = and\n ? `需要所有角色: ${requiredRoles.join(', ')}`\n : `需要以下任一角色: ${requiredRoles.join(', ')}`;\n\n return new PermissionDeniedException({\n type: PermissionDeniedType.ROLE_REQUIRED,\n message,\n requiredRoles,\n metadata: { and },\n });\n }\n\n /**\n * 创建权限不足异常\n */\n static permissionRequired(\n requiredPermissions: Array<{ actions: string[]; subject: string }>,\n or: boolean = false,\n customMessage?: string\n ): PermissionDeniedException {\n let message: string;\n\n if (customMessage) {\n message = customMessage;\n } else if (requiredPermissions.length === 1) {\n const perm = requiredPermissions[0];\n message = or\n ? `缺少权限: 需要对 ${perm.subject} 执行以下任一操作 [${perm.actions.join(', ')}]`\n : `缺少权限: 需要对 ${perm.subject} 执行所有操作 [${perm.actions.join(', ')}]`;\n } else {\n message = or\n ? `缺少权限: 需要满足以下任一权限要求: ${requiredPermissions.map(({ actions, subject }) => `对 ${subject} 执行以下任一操作 [${actions.join(', ')}]`).join(', ')}`\n : `缺少权限: 需要满足以下所有权限要求: ${requiredPermissions.map(({ actions, subject }) => `对 ${subject} 执行所有操作 [${actions.join(', ')}]`).join(', ')}`;\n }\n\n return new PermissionDeniedException({\n type: PermissionDeniedType.PERMISSION_REQUIRED,\n message,\n requiredPermissions,\n metadata: { or },\n });\n }\n}\n","import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';\nimport { Reflector } from '@nestjs/core';\nimport { PermissionService } from '../services/permission.service';\nimport { ROLES_KEY } from '../const';\nimport { CheckRoleRequirement } from '../decorators';\nimport { UserContext } from '../types';\n\n/**\n * AuthZPaas 守卫\n * 负责协调所有鉴权检查,具体检查逻辑委托给 PermissionService\n */\n@Injectable()\nexport class AuthZPaasGuard implements CanActivate {\n constructor(\n private reflector: Reflector,\n private permissionService: PermissionService\n ) {}\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const http = context.switchToHttp();\n const request = http.getRequest();\n const cookie = request.headers.cookie as string;\n const csrfToken = request.csrfToken as string;\n\n // FIXME: use new api to get base url\n const baseUrl = `${request.protocol}://${request.get('host')}`;\n\n const userContext = request.userContext as UserContext;\n userContext.baseUrl = baseUrl;\n\n // 检查角色要求\n const checkRoleRequirement =\n this.reflector.getAllAndOverride<CheckRoleRequirement>(ROLES_KEY, [\n context.getHandler(),\n context.getClass(),\n ]);\n\n if (checkRoleRequirement) {\n // 检查角色要求\n await this.permissionService.checkRoles(\n checkRoleRequirement,\n userContext,\n cookie,\n csrfToken\n );\n }\n\n // // 检查权限要求\n // const checkPermissionParams =\n // this.reflector.getAllAndOverride<CheckPermissionsParams>(\n // PERMISSIONS_KEY,\n // [context.getHandler(), context.getClass()]\n // );\n\n // if (checkPermissionParams) {\n // await this.checkPermissionRequirement(\n // checkPermissionParams,\n // userId,\n // mockRoles\n // );\n // }\n\n return true;\n }\n\n // /**\n // * 检查权限要求\n // */\n // private async checkPermissionRequirement(\n // params: CheckPermissionsParams,\n // userId?: string,\n // mockRoles?: string[]\n // ) {\n // return this.permissionService.checkPermissions(params, userId, mockRoles);\n // }\n}\n","import { SetMetadata } from '@nestjs/common';\nimport { ROLES_KEY } from '../const';\n\n/**\n * 角色要求配置\n */\nexport interface RoleRequirement {\n /** 需要的角色列表 */\n roles: string[];\n /** 是否需要所有角色(AND),默认 false(OR) */\n and?: boolean;\n}\n\nexport type CheckRoleRequirement = RoleRequirement;\n\n/**\n * 要求用户拥有指定角色\n * \n * @example\n * ```typescript\n * // 需要任一角色\n * @CanRole(['admin', 'moderator'])\n * async deleteUser() {}\n * \n * // 需要所有角色\n * @CanRole(['admin', 'superuser'], true)\n * async criticalOperation() {}\n * ```\n */\nexport const CanRole = (\n role: string[] | string,\n and: boolean = false,\n): MethodDecorator => {\n // 解析参数\n let requirement: RoleRequirement;\n\n if (!Array.isArray(role) && typeof role === 'string') {\n // 对象形式\n requirement = {\n roles: [role],\n and: and,\n };\n } else if (Array.isArray(role) && role.some((role) => typeof role === 'string')) {\n // 字符串列表形式\n requirement = {\n roles: role as string[],\n and: and,\n };\n } else {\n throw new Error('Invalid CanRole parameter: ' + JSON.stringify(role));\n }\n\n return SetMetadata(ROLES_KEY, requirement);\n};\n"],"mappings":"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","names":["Module","APP_GUARD","Reflector","Injectable","Logger","HttpStatus","Inject","Module","APP_GUARD","Reflector","Injectable","SetMetadata","AUTHNPAAS_MODULE_OPTIONS","Symbol","NEED_LOGIN_KEY","AuthNPaasGuard","reflector","canActivate","context","http","switchToHttp","request","getRequest","response","getResponse","userId","loginUrl","userContext","needLoginMeta","getAllAndOverride","getHandler","getClass","redirect","AuthNPaasModule","forRoot","options","module","global","controllers","providers","provide","useValue","Reflector","APP_GUARD","useClass","exports","IS_PUBLIC_KEY","Public","__name","SetMetadata","ANONYMOUS_USER_ID","PERMISSION_API_CONFIG_TOKEN","Symbol","CACHE_CONFIG_TOKEN","AUTHZPAAS_MODULE_OPTIONS","ROLES_KEY","PERMISSIONS_KEY","ENVIRONMENT_KEY","NEED_LOGIN_KEY","DEFAULT_LOGIN_PATH","MOCK_ROLES_COOKIE_KEY","ENABLE_MOCK_ROLE_KEY","Injectable","AbilityBuilder","PureAbility","ROLE_SUBJECT","AbilityFactory","createForUser","permissionData","can","build","AbilityBuilder","PureAbility","role","roles","HttpException","PermissionDeniedType","PermissionDeniedException","HttpException","type","details","httpStatusCode","statusCode","cause","message","requiredRoles","requiredPermissions","environmentRequirement","metadata","name","unauthenticated","roleRequired","and","join","permissionRequired","or","customMessage","length","perm","subject","actions","map","PermissionService","logger","Logger","name","apiConfig","abilityFactory","getUserPermissions","requestDto","requestPromise","userId","ANONYMOUS_USER_ID","permissionData","fetchFromApi","dataWithTimestamp","fetchedAt","Date","error","timeout","baseUrl","appId","cookie","csrfToken","url","requestHeaders","Cookie","controller","AbortController","timeoutId","setTimeout","abort","response","fetch","method","credentials","headers","signal","clearTimeout","ok","Error","status","statusText","PermissionDeniedException","cause","type","PermissionDeniedType","PERMISSION_CONFIG_QUERY_FAILED","message","HttpStatus","INTERNAL_SERVER_ERROR","data","json","roles","roleList","console","log","err","checkRoles","requirement","userContext","BAD_REQUEST","ability","createForUser","and","checkResults","map","role","can","ROLE_SUBJECT","hasRole","every","result","some","userRoles","warn","join","roleRequired","Injectable","Reflector","AuthZPaasGuard","reflector","permissionService","canActivate","context","http","switchToHttp","request","getRequest","cookie","headers","csrfToken","baseUrl","protocol","get","userContext","checkRoleRequirement","getAllAndOverride","ROLES_KEY","getHandler","getClass","checkRoles","AuthZPaasModule","forRoot","options","permissionApi","module","global","controllers","providers","provide","AUTHZPAAS_MODULE_OPTIONS","useValue","PERMISSION_API_CONFIG_TOKEN","Reflector","PermissionService","AbilityFactory","AuthZPaasGuard","APP_GUARD","useClass","exports","forRootAsync","imports","inject","useFactory","moduleOptions","SetMetadata","CanRole","role","and","requirement","Array","isArray","roles","some","Error","JSON","stringify","SetMetadata","ROLES_KEY"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/nestjs-authzpaas",
3
- "version": "0.1.0-alpha.0",
3
+ "version": "0.1.0-alpha.10",
4
4
  "description": "FullStack Nestjs authzpaas",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",