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

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.cjs ADDED
@@ -0,0 +1,1472 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/index.ts
22
+ var index_exports = {};
23
+ __export(index_exports, {
24
+ ANONYMOUS_USER_ID: () => ANONYMOUS_USER_ID,
25
+ AUTHZPAAS_MODULE_OPTIONS: () => AUTHZPAAS_MODULE_OPTIONS,
26
+ AbilityFactory: () => AbilityFactory,
27
+ AuthZPaasExceptionFilter: () => AuthZPaasExceptionFilter,
28
+ AuthZPaasGuard: () => AuthZPaasGuard,
29
+ AuthZPaasModule: () => AuthZPaasModule,
30
+ CACHE_CONFIG_TOKEN: () => CACHE_CONFIG_TOKEN,
31
+ CanEnv: () => CanEnv,
32
+ CanPermission: () => CanPermission,
33
+ CanRole: () => CanRole,
34
+ DEFAULT_LOGIN_PATH: () => DEFAULT_LOGIN_PATH,
35
+ ENABLE_MOCK_ROLE_KEY: () => ENABLE_MOCK_ROLE_KEY,
36
+ ENVIRONMENT_KEY: () => ENVIRONMENT_KEY,
37
+ MOCK_ROLES_COOKIE_KEY: () => MOCK_ROLES_COOKIE_KEY,
38
+ MockRoles: () => MockRoles,
39
+ NEED_LOGIN_KEY: () => NEED_LOGIN_KEY,
40
+ PERMISSIONS_KEY: () => PERMISSIONS_KEY,
41
+ PERMISSION_API_CONFIG_TOKEN: () => PERMISSION_API_CONFIG_TOKEN,
42
+ PermissionController: () => PermissionController,
43
+ PermissionDeniedException: () => PermissionDeniedException,
44
+ PermissionDeniedType: () => PermissionDeniedType,
45
+ PermissionDto: () => PermissionDto,
46
+ PermissionResponse: () => PermissionResponse,
47
+ PermissionService: () => PermissionService,
48
+ ROLES_KEY: () => ROLES_KEY,
49
+ ROLE_SUBJECT: () => ROLE_SUBJECT,
50
+ RolesMiddleware: () => RolesMiddleware,
51
+ UserId: () => UserId
52
+ });
53
+ module.exports = __toCommonJS(index_exports);
54
+
55
+ // src/authzpaas.module.ts
56
+ var import_common7 = require("@nestjs/common");
57
+ var import_core2 = require("@nestjs/core");
58
+
59
+ // src/const.ts
60
+ var ANONYMOUS_USER_ID = "anonymous_user_id";
61
+ var PERMISSION_API_CONFIG_TOKEN = Symbol("PERMISSION_API_CONFIG");
62
+ var CACHE_CONFIG_TOKEN = Symbol("CACHE_CONFIG");
63
+ var AUTHZPAAS_MODULE_OPTIONS = Symbol("AUTHZPAAS_MODULE_OPTIONS");
64
+ var ROLES_KEY = "authzpaas:roles";
65
+ var PERMISSIONS_KEY = "authzpaas:permissions";
66
+ var ENVIRONMENT_KEY = "authzpaas:environment";
67
+ var NEED_LOGIN_KEY = "authzpaas:needLogin";
68
+ var DEFAULT_LOGIN_PATH = "/login";
69
+ var MOCK_ROLES_COOKIE_KEY = "mockRoles";
70
+ var ENABLE_MOCK_ROLE_KEY = "__authzpaas_enableMockRole";
71
+
72
+ // src/services/permission.service.ts
73
+ var import_common3 = require("@nestjs/common");
74
+
75
+ // src/utils/memory-cache.ts
76
+ var MemoryCache = class {
77
+ static {
78
+ __name(this, "MemoryCache");
79
+ }
80
+ cache;
81
+ accessOrder;
82
+ options;
83
+ hits = 0;
84
+ misses = 0;
85
+ constructor(options) {
86
+ this.cache = /* @__PURE__ */ new Map();
87
+ this.accessOrder = [];
88
+ this.options = options;
89
+ }
90
+ /**
91
+ * 获取缓存值
92
+ */
93
+ get(key) {
94
+ if (!this.options.enabled) {
95
+ return void 0;
96
+ }
97
+ const item = this.cache.get(key);
98
+ if (!item) {
99
+ this.misses++;
100
+ return void 0;
101
+ }
102
+ if (Date.now() > item.expireAt) {
103
+ this.cache.delete(key);
104
+ this.removeFromAccessOrder(key);
105
+ this.misses++;
106
+ return void 0;
107
+ }
108
+ this.updateAccessOrder(key);
109
+ this.hits++;
110
+ return item.value;
111
+ }
112
+ /**
113
+ * 设置缓存值
114
+ */
115
+ set(key, value) {
116
+ if (!this.options.enabled) {
117
+ return;
118
+ }
119
+ if (this.cache.size >= this.options.max && !this.cache.has(key)) {
120
+ this.evictLRU();
121
+ }
122
+ const expireAt = Date.now() + this.options.ttl * 1e3;
123
+ this.cache.set(key, {
124
+ value,
125
+ expireAt
126
+ });
127
+ this.updateAccessOrder(key);
128
+ }
129
+ /**
130
+ * 删除缓存
131
+ */
132
+ delete(key) {
133
+ this.cache.delete(key);
134
+ this.removeFromAccessOrder(key);
135
+ }
136
+ /**
137
+ * 清空所有缓存
138
+ */
139
+ clear() {
140
+ this.cache.clear();
141
+ this.accessOrder = [];
142
+ this.hits = 0;
143
+ this.misses = 0;
144
+ }
145
+ /**
146
+ * 获取缓存统计信息
147
+ */
148
+ getStats() {
149
+ return {
150
+ size: this.cache.size,
151
+ hits: this.hits,
152
+ misses: this.misses,
153
+ hitRate: this.hits / (this.hits + this.misses) || 0,
154
+ enabled: this.options.enabled
155
+ };
156
+ }
157
+ /**
158
+ * 更新访问顺序
159
+ */
160
+ updateAccessOrder(key) {
161
+ this.removeFromAccessOrder(key);
162
+ this.accessOrder.push(key);
163
+ }
164
+ /**
165
+ * 从访问顺序中移除
166
+ */
167
+ removeFromAccessOrder(key) {
168
+ const index = this.accessOrder.indexOf(key);
169
+ if (index > -1) {
170
+ this.accessOrder.splice(index, 1);
171
+ }
172
+ }
173
+ /**
174
+ * 淘汰最少使用的项
175
+ */
176
+ evictLRU() {
177
+ if (this.accessOrder.length > 0) {
178
+ const oldestKey = this.accessOrder[0];
179
+ this.delete(oldestKey);
180
+ }
181
+ }
182
+ };
183
+
184
+ // src/casl/ability.factory.ts
185
+ var import_common = require("@nestjs/common");
186
+ var import_ability = require("@casl/ability");
187
+ function _ts_decorate(decorators, target, key, desc) {
188
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
189
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
190
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
191
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
192
+ }
193
+ __name(_ts_decorate, "_ts_decorate");
194
+ var ROLE_SUBJECT = "@role";
195
+ var AbilityFactory = class {
196
+ static {
197
+ __name(this, "AbilityFactory");
198
+ }
199
+ /**
200
+ * 为用户创建 Ability
201
+ */
202
+ createForUser(permissionData) {
203
+ const { can, build } = new import_ability.AbilityBuilder(import_ability.PureAbility);
204
+ for (const permission of permissionData.permissions) {
205
+ const { sub, actions } = permission;
206
+ for (const action of actions) {
207
+ can(action, sub);
208
+ }
209
+ }
210
+ for (const role of permissionData.roles) {
211
+ can(role, ROLE_SUBJECT);
212
+ }
213
+ return build();
214
+ }
215
+ };
216
+ AbilityFactory = _ts_decorate([
217
+ (0, import_common.Injectable)()
218
+ ], AbilityFactory);
219
+
220
+ // src/exceptions/permission-denied.exception.ts
221
+ var import_common2 = require("@nestjs/common");
222
+ var PermissionDeniedType = /* @__PURE__ */ (function(PermissionDeniedType2) {
223
+ PermissionDeniedType2["UNAUTHENTICATED"] = "UNAUTHENTICATED";
224
+ PermissionDeniedType2["ROLE_REQUIRED"] = "ROLE_REQUIRED";
225
+ PermissionDeniedType2["PERMISSION_REQUIRED"] = "PERMISSION_REQUIRED";
226
+ PermissionDeniedType2["ENVIRONMENT_REQUIRED"] = "ENVIRONMENT_REQUIRED";
227
+ PermissionDeniedType2["PERMISSION_CONFIG_QUERY_FAILED"] = "PERMISSION_CONFIG_QUERY_FAILED";
228
+ return PermissionDeniedType2;
229
+ })({});
230
+ var PermissionDeniedException = class _PermissionDeniedException extends import_common2.HttpException {
231
+ static {
232
+ __name(this, "PermissionDeniedException");
233
+ }
234
+ type;
235
+ details;
236
+ constructor(details, httpStatusCode = 403) {
237
+ super({
238
+ statusCode: httpStatusCode,
239
+ cause: details.cause,
240
+ type: details.type,
241
+ message: details.message,
242
+ ...details.requiredRoles && {
243
+ requiredRoles: details.requiredRoles
244
+ },
245
+ ...details.requiredPermissions && {
246
+ requiredPermissions: details.requiredPermissions
247
+ },
248
+ ...details.environmentRequirement && {
249
+ environmentRequirement: details.environmentRequirement
250
+ },
251
+ ...details.metadata && {
252
+ metadata: details.metadata
253
+ }
254
+ }, httpStatusCode);
255
+ this.type = details.type;
256
+ this.details = details;
257
+ this.name = "PermissionDeniedException";
258
+ }
259
+ /**
260
+ * 创建用户未认证异常
261
+ */
262
+ static unauthenticated(message = "\u7528\u6237\u672A\u8BA4\u8BC1") {
263
+ return new _PermissionDeniedException({
264
+ type: "UNAUTHENTICATED",
265
+ message
266
+ });
267
+ }
268
+ /**
269
+ * 创建角色不足异常
270
+ */
271
+ static roleRequired(requiredRoles, and = false) {
272
+ const message = and ? `\u9700\u8981\u6240\u6709\u89D2\u8272: ${requiredRoles.join(", ")}` : `\u9700\u8981\u4EE5\u4E0B\u4EFB\u4E00\u89D2\u8272: ${requiredRoles.join(", ")}`;
273
+ return new _PermissionDeniedException({
274
+ type: "ROLE_REQUIRED",
275
+ message,
276
+ requiredRoles,
277
+ metadata: {
278
+ and
279
+ }
280
+ });
281
+ }
282
+ /**
283
+ * 创建权限不足异常
284
+ */
285
+ static permissionRequired(requiredPermissions, or = false, customMessage) {
286
+ let message;
287
+ if (customMessage) {
288
+ message = customMessage;
289
+ } else if (requiredPermissions.length === 1) {
290
+ const perm = requiredPermissions[0];
291
+ message = or ? `\u7F3A\u5C11\u6743\u9650: \u9700\u8981\u5BF9 ${perm.subject} \u6267\u884C\u4EE5\u4E0B\u4EFB\u4E00\u64CD\u4F5C [${perm.actions.join(", ")}]` : `\u7F3A\u5C11\u6743\u9650: \u9700\u8981\u5BF9 ${perm.subject} \u6267\u884C\u6240\u6709\u64CD\u4F5C [${perm.actions.join(", ")}]`;
292
+ } else {
293
+ message = or ? `\u7F3A\u5C11\u6743\u9650: \u9700\u8981\u6EE1\u8DB3\u4EE5\u4E0B\u4EFB\u4E00\u6743\u9650\u8981\u6C42: ${requiredPermissions.map(({ actions, subject }) => `\u5BF9 ${subject} \u6267\u884C\u4EE5\u4E0B\u4EFB\u4E00\u64CD\u4F5C [${actions.join(", ")}]`).join(", ")}` : `\u7F3A\u5C11\u6743\u9650: \u9700\u8981\u6EE1\u8DB3\u4EE5\u4E0B\u6240\u6709\u6743\u9650\u8981\u6C42: ${requiredPermissions.map(({ actions, subject }) => `\u5BF9 ${subject} \u6267\u884C\u6240\u6709\u64CD\u4F5C [${actions.join(", ")}]`).join(", ")}`;
294
+ }
295
+ return new _PermissionDeniedException({
296
+ type: "PERMISSION_REQUIRED",
297
+ message,
298
+ requiredPermissions,
299
+ metadata: {
300
+ or
301
+ }
302
+ });
303
+ }
304
+ /**
305
+ * 创建环境不满足异常
306
+ */
307
+ static environmentRequired(requirement, message) {
308
+ return new _PermissionDeniedException({
309
+ type: "ENVIRONMENT_REQUIRED",
310
+ message: message || "\u4E0D\u6EE1\u8DB3\u73AF\u5883\u8981\u6C42",
311
+ environmentRequirement: requirement
312
+ });
313
+ }
314
+ };
315
+
316
+ // src/services/permission.service.ts
317
+ function _ts_decorate2(decorators, target, key, desc) {
318
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
319
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
320
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
321
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
322
+ }
323
+ __name(_ts_decorate2, "_ts_decorate");
324
+ function _ts_metadata(k, v) {
325
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
326
+ }
327
+ __name(_ts_metadata, "_ts_metadata");
328
+ function _ts_param(paramIndex, decorator) {
329
+ return function(target, key) {
330
+ decorator(target, key, paramIndex);
331
+ };
332
+ }
333
+ __name(_ts_param, "_ts_param");
334
+ var PermissionService = class _PermissionService {
335
+ static {
336
+ __name(this, "PermissionService");
337
+ }
338
+ apiConfig;
339
+ abilityFactory;
340
+ logger = new import_common3.Logger(_PermissionService.name);
341
+ // 统一的缓存,同时缓存权限数据和 Ability 实例
342
+ cache;
343
+ // 缓存正在进行中的 Promise,避免重复请求
344
+ pendingRequests = /* @__PURE__ */ new Map();
345
+ constructor(apiConfig, cacheConfig, abilityFactory) {
346
+ this.apiConfig = apiConfig;
347
+ this.abilityFactory = abilityFactory;
348
+ this.cache = new MemoryCache({
349
+ ttl: cacheConfig.ttl || 300,
350
+ max: cacheConfig.max || 1e3,
351
+ enabled: cacheConfig.enabled !== false
352
+ });
353
+ this.logger.log(`PermissionService initialized with API: ${apiConfig?.baseUrl}`);
354
+ }
355
+ /**
356
+ * 构建权限/Ability 缓存 key
357
+ * - 若存在模拟角色:按角色集合排序拼接 + 用户维度
358
+ * - 否则按 userId/匿名用户
359
+ */
360
+ buildCacheKey(userId, mockRoles) {
361
+ if (mockRoles && mockRoles.length > 0) {
362
+ const sortedRoles = [
363
+ ...mockRoles
364
+ ].sort();
365
+ return `@roles:${sortedRoles.join("|")}#u:${userId || ANONYMOUS_USER_ID}`;
366
+ }
367
+ return userId || ANONYMOUS_USER_ID;
368
+ }
369
+ /**
370
+ * 获取用户权限数据(带缓存)
371
+ */
372
+ async getUserPermissions(userId, mockRoles) {
373
+ if (!this.apiConfig?.endpoint) {
374
+ this.logger.warn("Permission API endpoint is not configured, returning null");
375
+ return null;
376
+ }
377
+ const key = this.buildCacheKey(userId, mockRoles);
378
+ const cached = this.cache.get(key);
379
+ if (cached) {
380
+ this.logger.debug(`Cache hit for user ${key}`);
381
+ return cached.permissionData;
382
+ }
383
+ const pendingRequest = this.pendingRequests.get(key);
384
+ if (pendingRequest) {
385
+ this.logger.debug(`Reusing pending request for user ${key}`);
386
+ return pendingRequest;
387
+ }
388
+ this.logger.debug(`Cache miss for key ${key}, fetching from API`);
389
+ const requestPromise = (async () => {
390
+ try {
391
+ const permissionData = mockRoles && mockRoles.length > 0 ? await this.getPermissionsByMockRoles(userId, mockRoles) : await this.fetchFromApi(userId);
392
+ const dataWithTimestamp = {
393
+ ...permissionData,
394
+ fetchedAt: /* @__PURE__ */ new Date()
395
+ };
396
+ const ability = this.abilityFactory.createForUser(dataWithTimestamp);
397
+ this.cache.set(key, {
398
+ permissionData: dataWithTimestamp,
399
+ ability
400
+ });
401
+ return dataWithTimestamp;
402
+ } catch (error) {
403
+ this.logger.error(`Failed to fetch permissions for key ${key}:`, error);
404
+ throw error;
405
+ } finally {
406
+ this.pendingRequests.delete(key);
407
+ }
408
+ })();
409
+ this.pendingRequests.set(key, requestPromise);
410
+ return requestPromise;
411
+ }
412
+ /**
413
+ * 从 API 获取权限数据
414
+ * 内置实现,用户无需配置
415
+ */
416
+ async fetchFromApi(userId) {
417
+ const { baseUrl, apiToken, endpoint, headers = {}, timeout = 5e3 } = this.apiConfig || {};
418
+ const url = `${baseUrl}${endpoint}${userId ? `?userId=${userId}` : ""}`;
419
+ const requestHeaders = {
420
+ "Content-Type": "application/json",
421
+ ...headers
422
+ };
423
+ if (apiToken) {
424
+ requestHeaders["Authorization"] = `Bearer ${apiToken}`;
425
+ }
426
+ const controller = new AbortController();
427
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
428
+ try {
429
+ const response = await fetch(url, {
430
+ method: "GET",
431
+ headers: requestHeaders,
432
+ signal: controller.signal
433
+ });
434
+ clearTimeout(timeoutId);
435
+ if (!response.ok) {
436
+ const error = new Error(`Permission API returned ${response.status}: ${response.statusText}`);
437
+ throw new PermissionDeniedException({
438
+ cause: error,
439
+ type: PermissionDeniedType.PERMISSION_CONFIG_QUERY_FAILED,
440
+ message: error.message
441
+ }, import_common3.HttpStatus.INTERNAL_SERVER_ERROR);
442
+ }
443
+ const data = await response.json();
444
+ const roles = (data.roles || []).map((role) => typeof role === "string" ? role : role.name);
445
+ return {
446
+ userId,
447
+ roles,
448
+ permissions: data.permissions || [],
449
+ fetchedAt: /* @__PURE__ */ new Date()
450
+ };
451
+ } catch (error) {
452
+ clearTimeout(timeoutId);
453
+ let err = error;
454
+ if (error.name === "AbortError") {
455
+ err = new Error(`Permission API request timeout after ${timeout}ms`);
456
+ }
457
+ throw new PermissionDeniedException({
458
+ cause: err,
459
+ type: PermissionDeniedType.PERMISSION_CONFIG_QUERY_FAILED,
460
+ message: err.message
461
+ }, import_common3.HttpStatus.INTERNAL_SERVER_ERROR);
462
+ }
463
+ }
464
+ /**
465
+ * 基于模拟角色获取权限数据(不使用缓存)
466
+ * 该方法用于前端/守卫在检测到 mockRoles 时直接按角色获取权限
467
+ */
468
+ async getPermissionsByMockRoles(userId, mockRoles) {
469
+ const { baseUrl, timeout = 5e3 } = this.apiConfig || {};
470
+ const rolesParam = encodeURIComponent(mockRoles.join(","));
471
+ const userParam = userId ? `&userId=${encodeURIComponent(userId)}` : "";
472
+ const url = `${baseUrl}/mock-api/permissions-by-roles?roles=${rolesParam}${userParam}`;
473
+ const controller = new AbortController();
474
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
475
+ try {
476
+ const response = await fetch(url, {
477
+ method: "GET",
478
+ signal: controller.signal
479
+ });
480
+ clearTimeout(timeoutId);
481
+ if (!response.ok) {
482
+ throw new Error(`Permission API (mock by roles) returned ${response.status}: ${response.statusText}`);
483
+ }
484
+ const data = await response.json();
485
+ return {
486
+ userId,
487
+ roles: data.roles || [],
488
+ permissions: data.permissions || [],
489
+ fetchedAt: /* @__PURE__ */ new Date()
490
+ };
491
+ } catch (error) {
492
+ clearTimeout(timeoutId);
493
+ throw new PermissionDeniedException({
494
+ cause: error,
495
+ type: PermissionDeniedType.PERMISSION_CONFIG_QUERY_FAILED,
496
+ message: error.message
497
+ }, import_common3.HttpStatus.INTERNAL_SERVER_ERROR);
498
+ }
499
+ }
500
+ /**
501
+ * 获取用户的 Ability 实例(带缓存)
502
+ * @param userId 用户ID
503
+ * @returns CASL Ability 实例
504
+ */
505
+ async getUserAbility(userId, mockRoles) {
506
+ const key = this.buildCacheKey(userId, mockRoles);
507
+ const cached = this.cache.get(key);
508
+ if (cached) {
509
+ return cached.ability;
510
+ }
511
+ await this.getUserPermissions(userId, mockRoles);
512
+ const newCached = this.cache.get(key);
513
+ return newCached.ability;
514
+ }
515
+ /**
516
+ * 清除用户权限缓存
517
+ */
518
+ clearUserCache(userId) {
519
+ const key = userId || ANONYMOUS_USER_ID;
520
+ this.cache.delete(key);
521
+ this.pendingRequests.delete(key);
522
+ this.logger.debug(`Cache cleared for user ${key}`);
523
+ }
524
+ /**
525
+ * 清除所有缓存
526
+ */
527
+ clearAllCache() {
528
+ this.cache.clear();
529
+ this.pendingRequests.clear();
530
+ this.logger.log("All permission cache cleared");
531
+ }
532
+ /**
533
+ * 获取缓存统计信息
534
+ */
535
+ getCacheStats() {
536
+ return this.cache.getStats();
537
+ }
538
+ /**
539
+ * 检查角色要求
540
+ * 使用 CASL Ability 统一鉴权方式
541
+ * @param requirement 角色要求
542
+ * @param userId 用户ID,匿名用户时为空
543
+ * @returns 用户权限数据
544
+ * @throws PermissionDeniedException 当角色不满足时
545
+ */
546
+ async checkRoles(requirement, userId, mockRoles) {
547
+ const permissionData = await this.getUserPermissions(userId, mockRoles);
548
+ if (!permissionData) {
549
+ throw new PermissionDeniedException({
550
+ cause: new Error("Permission data fetch api is not configured"),
551
+ type: PermissionDeniedType.PERMISSION_CONFIG_QUERY_FAILED,
552
+ message: "Permission data fetch api is not configured"
553
+ }, import_common3.HttpStatus.BAD_REQUEST);
554
+ }
555
+ const ability = await this.getUserAbility(userId, mockRoles);
556
+ const { roles, and } = requirement;
557
+ const checkResults = roles.map((role) => ability.can(role, ROLE_SUBJECT));
558
+ const hasRole = and ? checkResults.every((result) => result) : checkResults.some((result) => result);
559
+ if (!hasRole) {
560
+ const userRoles = permissionData.roles;
561
+ this.logger.warn(`\u89D2\u8272\u68C0\u67E5\u5931\u8D25: \u7528\u6237 ${userId}, \u7528\u6237\u89D2\u8272 [${userRoles.join(", ")}], \u9700\u8981 [${roles.join(", ")}]`);
562
+ throw PermissionDeniedException.roleRequired(roles, and);
563
+ }
564
+ return permissionData;
565
+ }
566
+ /**
567
+ * 检查权限要求
568
+ * @param requirements 权限要求列表
569
+ * @param userId 用户ID
570
+ * @returns 用户权限数据
571
+ * @throws PermissionDeniedException 当权限不满足时
572
+ */
573
+ async checkPermissions(params, userId, mockRoles) {
574
+ const permissionData = await this.getUserPermissions(userId, mockRoles);
575
+ if (!permissionData) {
576
+ throw new PermissionDeniedException({
577
+ cause: new Error("Permission data fetch api is not configured"),
578
+ type: PermissionDeniedType.PERMISSION_CONFIG_QUERY_FAILED,
579
+ message: "Permission data fetch api is not configured"
580
+ }, import_common3.HttpStatus.BAD_REQUEST);
581
+ }
582
+ const { requirements, or } = params;
583
+ if (!requirements || requirements.length === 0) {
584
+ return permissionData;
585
+ }
586
+ const ability = await this.getUserAbility(userId, mockRoles);
587
+ const failedRequirements = [];
588
+ for (const requirement of requirements) {
589
+ const { actions, subject, or: or2 = false } = requirement;
590
+ const checkResults = actions.map((action) => ability.can(action, subject));
591
+ const hasPermission = or2 ? checkResults.some((result) => result) : checkResults.every((result) => result);
592
+ if (!hasPermission) {
593
+ failedRequirements.push({
594
+ actions,
595
+ subject: String(subject),
596
+ or: or2
597
+ });
598
+ }
599
+ }
600
+ if (failedRequirements.length > 0) {
601
+ if (or && failedRequirements.length === requirements.length) {
602
+ throw PermissionDeniedException.permissionRequired(failedRequirements.map(({ actions, subject }) => ({
603
+ actions,
604
+ subject: String(subject)
605
+ })), true);
606
+ } else if (!or) {
607
+ throw PermissionDeniedException.permissionRequired(failedRequirements.map(({ actions, subject }) => ({
608
+ actions,
609
+ subject: String(subject)
610
+ })), false);
611
+ }
612
+ }
613
+ return permissionData;
614
+ }
615
+ async getAbility(userId) {
616
+ return this.getUserAbility(userId);
617
+ }
618
+ };
619
+ PermissionService = _ts_decorate2([
620
+ (0, import_common3.Injectable)(),
621
+ _ts_param(0, (0, import_common3.Inject)(PERMISSION_API_CONFIG_TOKEN)),
622
+ _ts_param(1, (0, import_common3.Inject)(CACHE_CONFIG_TOKEN)),
623
+ _ts_metadata("design:type", Function),
624
+ _ts_metadata("design:paramtypes", [
625
+ typeof PermissionApiConfig === "undefined" ? Object : PermissionApiConfig,
626
+ typeof CacheConfig === "undefined" ? Object : CacheConfig,
627
+ typeof AbilityFactory === "undefined" ? Object : AbilityFactory
628
+ ])
629
+ ], PermissionService);
630
+
631
+ // src/guards/authzpaas.guard.ts
632
+ var import_common4 = require("@nestjs/common");
633
+ var import_core = require("@nestjs/core");
634
+
635
+ // src/utils/index.ts
636
+ function getMockRolesFromCookie(request) {
637
+ const mockRoles = request.cookies?.[MOCK_ROLES_COOKIE_KEY];
638
+ return mockRoles ? mockRoles.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
639
+ }
640
+ __name(getMockRolesFromCookie, "getMockRolesFromCookie");
641
+
642
+ // src/guards/authzpaas.guard.ts
643
+ function _ts_decorate3(decorators, target, key, desc) {
644
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
645
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
646
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
647
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
648
+ }
649
+ __name(_ts_decorate3, "_ts_decorate");
650
+ function _ts_metadata2(k, v) {
651
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
652
+ }
653
+ __name(_ts_metadata2, "_ts_metadata");
654
+ function _ts_param2(paramIndex, decorator) {
655
+ return function(target, key) {
656
+ decorator(target, key, paramIndex);
657
+ };
658
+ }
659
+ __name(_ts_param2, "_ts_param");
660
+ var AuthZPaasGuard = class {
661
+ static {
662
+ __name(this, "AuthZPaasGuard");
663
+ }
664
+ reflector;
665
+ permissionService;
666
+ moduleOptions;
667
+ constructor(reflector, permissionService, moduleOptions) {
668
+ this.reflector = reflector;
669
+ this.permissionService = permissionService;
670
+ this.moduleOptions = moduleOptions;
671
+ }
672
+ async canActivate(context) {
673
+ const http = context.switchToHttp();
674
+ const request = http.getRequest();
675
+ request[ENABLE_MOCK_ROLE_KEY] = this.moduleOptions?.enableMockRole;
676
+ const userId = this.extractUserId(request);
677
+ const mockRoles = this.moduleOptions?.enableMockRole ? getMockRolesFromCookie(request) : void 0;
678
+ const checkRoleRequirement = this.reflector.getAllAndOverride(ROLES_KEY, [
679
+ context.getHandler(),
680
+ context.getClass()
681
+ ]);
682
+ if (checkRoleRequirement) {
683
+ await this.checkRoleRequirement(checkRoleRequirement, userId, mockRoles);
684
+ }
685
+ const checkPermissionParams = this.reflector.getAllAndOverride(PERMISSIONS_KEY, [
686
+ context.getHandler(),
687
+ context.getClass()
688
+ ]);
689
+ if (checkPermissionParams) {
690
+ await this.checkPermissionRequirement(checkPermissionParams, userId, mockRoles);
691
+ }
692
+ const envRequirement = this.reflector.getAllAndOverride(ENVIRONMENT_KEY, [
693
+ context.getHandler(),
694
+ context.getClass()
695
+ ]);
696
+ if (envRequirement) {
697
+ const envContext = this.extractEnvironmentContext(request);
698
+ await this.checkEnvironmentRequirement(envRequirement, envContext, request);
699
+ }
700
+ return true;
701
+ }
702
+ /**
703
+ * 从请求中提取用户ID
704
+ * 子类可以重写此方法以适应不同的认证策略
705
+ */
706
+ extractUserId(request) {
707
+ const mockUserId = request.cookies?.mockUserId;
708
+ if (mockUserId) return mockUserId;
709
+ return request.userContext?.userId;
710
+ }
711
+ /**
712
+ * 从请求中提取环境上下文
713
+ */
714
+ extractEnvironmentContext(request) {
715
+ const regionHeader = request.headers["x-region"];
716
+ const osHeader = request.headers["x-os"];
717
+ const uaHeader = request.headers["user-agent"];
718
+ const region = Array.isArray(regionHeader) ? regionHeader[0] : regionHeader;
719
+ const os = Array.isArray(osHeader) ? osHeader[0] : osHeader;
720
+ const userAgent = Array.isArray(uaHeader) ? uaHeader[0] : uaHeader;
721
+ return {
722
+ network: {
723
+ ip: request.ip || request.connection?.remoteAddress,
724
+ region
725
+ },
726
+ device: {
727
+ type: this.detectDeviceType(userAgent),
728
+ os,
729
+ browser: userAgent
730
+ },
731
+ custom: {
732
+ headers: request.headers,
733
+ query: request.query
734
+ }
735
+ };
736
+ }
737
+ /**
738
+ * 检测设备类型
739
+ */
740
+ detectDeviceType(userAgent) {
741
+ if (!userAgent) return "desktop";
742
+ const ua = userAgent.toLowerCase();
743
+ if (/(tablet|ipad|playbook|silk)|(android(?!.*mobile))/i.test(ua)) {
744
+ return "tablet";
745
+ }
746
+ if (/mobile|iphone|ipod|android|blackberry|opera mini|iemobile/i.test(ua)) {
747
+ return "mobile";
748
+ }
749
+ return "desktop";
750
+ }
751
+ /**
752
+ * 检查角色要求
753
+ */
754
+ async checkRoleRequirement(requirement, userId, mockRoles) {
755
+ return this.permissionService.checkRoles(requirement, userId, mockRoles);
756
+ }
757
+ /**
758
+ * 检查权限要求
759
+ */
760
+ async checkPermissionRequirement(params, userId, mockRoles) {
761
+ return this.permissionService.checkPermissions(params, userId, mockRoles);
762
+ }
763
+ /**
764
+ * 检查环境要求
765
+ */
766
+ async checkEnvironmentRequirement(requirement, envContext, request) {
767
+ if (requirement.network) {
768
+ await this.checkNetworkRequirement(requirement.network, envContext);
769
+ }
770
+ if (requirement.device) {
771
+ await this.checkDeviceRequirement(requirement.device, envContext);
772
+ }
773
+ if (requirement.custom) {
774
+ const result = await requirement.custom(request);
775
+ if (!result) {
776
+ throw PermissionDeniedException.environmentRequired("custom", "\u4E0D\u6EE1\u8DB3\u81EA\u5B9A\u4E49\u73AF\u5883\u8981\u6C42");
777
+ }
778
+ }
779
+ }
780
+ /**
781
+ * 检查网络要求
782
+ */
783
+ async checkNetworkRequirement(requirement, envContext) {
784
+ const ip = envContext.network?.ip;
785
+ if (requirement.allowedIPs && ip) {
786
+ const isAllowed = this.checkIPMatch(ip, requirement.allowedIPs);
787
+ if (!isAllowed) {
788
+ throw PermissionDeniedException.environmentRequired("network.allowedIPs", `IP \u5730\u5740\u4E0D\u5728\u5141\u8BB8\u5217\u8868\u4E2D: ${ip}`);
789
+ }
790
+ }
791
+ if (requirement.blockedIPs && ip) {
792
+ const isBlocked = this.checkIPMatch(ip, requirement.blockedIPs);
793
+ if (isBlocked) {
794
+ throw PermissionDeniedException.environmentRequired("network.blockedIPs", `IP \u5730\u5740\u88AB\u7981\u6B62\u8BBF\u95EE: ${ip}`);
795
+ }
796
+ }
797
+ if (requirement.allowedRegions && envContext.network?.region) {
798
+ const isAllowed = requirement.allowedRegions.includes(envContext.network.region);
799
+ if (!isAllowed) {
800
+ throw PermissionDeniedException.environmentRequired("network.allowedRegions", `\u5730\u533A\u4E0D\u5728\u5141\u8BB8\u5217\u8868\u4E2D: ${envContext.network.region}`);
801
+ }
802
+ }
803
+ }
804
+ /**
805
+ * 检查设备要求
806
+ */
807
+ async checkDeviceRequirement(requirement, envContext) {
808
+ if (requirement.types && envContext.device?.type) {
809
+ const isAllowed = requirement.types.includes(envContext.device.type);
810
+ if (!isAllowed) {
811
+ throw PermissionDeniedException.environmentRequired("device.types", `\u4E0D\u5141\u8BB8\u7684\u8BBE\u5907\u7C7B\u578B: ${envContext.device.type}`);
812
+ }
813
+ }
814
+ }
815
+ /**
816
+ * 检查 IP 是否匹配
817
+ * 简化版本,仅支持精确匹配
818
+ * 生产环境建议使用 ipaddr.js 等库
819
+ */
820
+ checkIPMatch(ip, patterns) {
821
+ return patterns.some((pattern) => {
822
+ if (pattern === ip) return true;
823
+ return false;
824
+ });
825
+ }
826
+ };
827
+ AuthZPaasGuard = _ts_decorate3([
828
+ (0, import_common4.Injectable)(),
829
+ _ts_param2(2, (0, import_common4.Inject)(AUTHZPAAS_MODULE_OPTIONS)),
830
+ _ts_metadata2("design:type", Function),
831
+ _ts_metadata2("design:paramtypes", [
832
+ typeof import_core.Reflector === "undefined" ? Object : import_core.Reflector,
833
+ typeof PermissionService === "undefined" ? Object : PermissionService,
834
+ typeof AuthZPaasModuleOptions === "undefined" ? Object : AuthZPaasModuleOptions
835
+ ])
836
+ ], AuthZPaasGuard);
837
+
838
+ // src/middlewares/roles.middleware.ts
839
+ var import_common5 = require("@nestjs/common");
840
+ function _ts_decorate4(decorators, target, key, desc) {
841
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
842
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
843
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
844
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
845
+ }
846
+ __name(_ts_decorate4, "_ts_decorate");
847
+ function _ts_metadata3(k, v) {
848
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
849
+ }
850
+ __name(_ts_metadata3, "_ts_metadata");
851
+ var RolesMiddleware = class _RolesMiddleware {
852
+ static {
853
+ __name(this, "RolesMiddleware");
854
+ }
855
+ permissionService;
856
+ logger = new import_common5.Logger(_RolesMiddleware.name);
857
+ constructor(permissionService) {
858
+ this.permissionService = permissionService;
859
+ }
860
+ async use(req, _res, next) {
861
+ try {
862
+ const userId = req.userContext?.userId;
863
+ const mockRoles = req[ENABLE_MOCK_ROLE_KEY] ? getMockRolesFromCookie(req) : void 0;
864
+ this.logger.debug(`Fetching roles for user: ${userId}`);
865
+ const permissionData = await this.permissionService.getUserPermissions(userId, mockRoles);
866
+ let roles;
867
+ if (permissionData) {
868
+ roles = permissionData.roles;
869
+ }
870
+ req.userContext.userRoles = roles;
871
+ req.userContext.userId = userId;
872
+ this.logger.debug(`User ${userId} roles loaded: [${roles?.join(", ")}]`);
873
+ next();
874
+ } catch (error) {
875
+ this.logger.warn(`Failed to fetch roles for request: ${error instanceof Error ? error.message : "Unknown error"}`);
876
+ next();
877
+ }
878
+ }
879
+ };
880
+ RolesMiddleware = _ts_decorate4([
881
+ (0, import_common5.Injectable)(),
882
+ _ts_metadata3("design:type", Function),
883
+ _ts_metadata3("design:paramtypes", [
884
+ typeof PermissionService === "undefined" ? Object : PermissionService
885
+ ])
886
+ ], RolesMiddleware);
887
+
888
+ // src/filters/authzpaas-exception.filter.ts
889
+ var import_common6 = require("@nestjs/common");
890
+ function _ts_decorate5(decorators, target, key, desc) {
891
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
892
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
893
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
894
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
895
+ }
896
+ __name(_ts_decorate5, "_ts_decorate");
897
+ var AuthZPaasExceptionFilter = class {
898
+ static {
899
+ __name(this, "AuthZPaasExceptionFilter");
900
+ }
901
+ catch(exception, host) {
902
+ const ctx = host.switchToHttp();
903
+ const response = ctx.getResponse();
904
+ const status = exception.getStatus();
905
+ const exceptionResponse = exception.getResponse();
906
+ const errorResponse = {
907
+ statusCode: status,
908
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
909
+ cause: exception.cause,
910
+ type: exception.type,
911
+ message: typeof exceptionResponse === "string" ? exceptionResponse : exceptionResponse.message || "\u6743\u9650\u4E0D\u8DB3"
912
+ };
913
+ if (exception.details.requiredRoles) {
914
+ errorResponse.requiredRoles = exception.details.requiredRoles;
915
+ }
916
+ if (exception.details.requiredPermissions) {
917
+ errorResponse.requiredPermissions = exception.details.requiredPermissions;
918
+ }
919
+ if (exception.details.environmentRequirement) {
920
+ errorResponse.environmentRequirement = exception.details.environmentRequirement;
921
+ }
922
+ if (exception.details.metadata) {
923
+ errorResponse.metadata = exception.details.metadata;
924
+ }
925
+ response.status(status).json(errorResponse);
926
+ }
927
+ };
928
+ AuthZPaasExceptionFilter = _ts_decorate5([
929
+ (0, import_common6.Catch)(PermissionDeniedException)
930
+ ], AuthZPaasExceptionFilter);
931
+
932
+ // src/authzpaas.module.ts
933
+ function _ts_decorate6(decorators, target, key, desc) {
934
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
935
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
936
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
937
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
938
+ }
939
+ __name(_ts_decorate6, "_ts_decorate");
940
+ var AuthZPaasModule = class _AuthZPaasModule {
941
+ static {
942
+ __name(this, "AuthZPaasModule");
943
+ }
944
+ static forRoot(options) {
945
+ const { permissionApi, cache = {
946
+ ttl: 300,
947
+ max: 1e3,
948
+ enabled: true
949
+ }, isGlobal = true } = options;
950
+ return {
951
+ module: _AuthZPaasModule,
952
+ global: isGlobal,
953
+ controllers: [],
954
+ providers: [
955
+ // 配置提供者
956
+ {
957
+ provide: AUTHZPAAS_MODULE_OPTIONS,
958
+ useValue: {
959
+ ...options,
960
+ loginPath: options.loginPath || DEFAULT_LOGIN_PATH
961
+ }
962
+ },
963
+ {
964
+ provide: PERMISSION_API_CONFIG_TOKEN,
965
+ useValue: permissionApi
966
+ },
967
+ {
968
+ provide: CACHE_CONFIG_TOKEN,
969
+ useValue: cache
970
+ },
971
+ // 核心服务
972
+ import_core2.Reflector,
973
+ // 服务提供者
974
+ PermissionService,
975
+ AbilityFactory,
976
+ AuthZPaasGuard,
977
+ RolesMiddleware,
978
+ // 守卫提供者
979
+ {
980
+ provide: import_core2.APP_GUARD,
981
+ useClass: AuthZPaasGuard
982
+ },
983
+ {
984
+ provide: import_core2.APP_FILTER,
985
+ useClass: AuthZPaasExceptionFilter
986
+ }
987
+ ],
988
+ exports: [
989
+ PermissionService,
990
+ AbilityFactory,
991
+ RolesMiddleware
992
+ ]
993
+ };
994
+ }
995
+ /**
996
+ * 异步注册 AuthZPaas 模块(根模块)
997
+ * 用于需要从配置服务获取设置的场景
998
+ *
999
+ * @param options 异步配置选项
1000
+ * @returns 动态模块
1001
+ *
1002
+ * @example
1003
+ * ```typescript
1004
+ * @Module({
1005
+ * imports: [
1006
+ * AuthZPaasModule.forRootAsync({
1007
+ * imports: [ConfigModule],
1008
+ * inject: [ConfigService],
1009
+ * useFactory: async (configService: ConfigService) => ({
1010
+ * permissionApi: {
1011
+ * baseUrl: configService.get('PERMISSION_API_URL'),
1012
+ * apiToken: configService.get('PERMISSION_API_TOKEN'),
1013
+ * },
1014
+ * cache: {
1015
+ * ttl: configService.get('CACHE_TTL', 300),
1016
+ * max: configService.get('CACHE_MAX', 1000),
1017
+ * },
1018
+ * }),
1019
+ * }),
1020
+ * ],
1021
+ * })
1022
+ * export class AppModule {}
1023
+ * ```
1024
+ */
1025
+ static forRootAsync(options) {
1026
+ const { imports = [], inject = [], useFactory, isGlobal = true } = options;
1027
+ return {
1028
+ module: _AuthZPaasModule,
1029
+ global: isGlobal,
1030
+ imports,
1031
+ controllers: [],
1032
+ providers: [
1033
+ // 异步配置提供者
1034
+ {
1035
+ provide: AUTHZPAAS_MODULE_OPTIONS,
1036
+ useFactory,
1037
+ inject
1038
+ },
1039
+ // 权限 API 配置提供者
1040
+ {
1041
+ provide: PERMISSION_API_CONFIG_TOKEN,
1042
+ useFactory: /* @__PURE__ */ __name((moduleOptions) => {
1043
+ return moduleOptions.permissionApi;
1044
+ }, "useFactory"),
1045
+ inject: [
1046
+ AUTHZPAAS_MODULE_OPTIONS
1047
+ ]
1048
+ },
1049
+ // 缓存配置提供者
1050
+ {
1051
+ provide: CACHE_CONFIG_TOKEN,
1052
+ useFactory: /* @__PURE__ */ __name((moduleOptions) => {
1053
+ return moduleOptions.cache || {
1054
+ ttl: 300,
1055
+ max: 1e3,
1056
+ enabled: true
1057
+ };
1058
+ }, "useFactory"),
1059
+ inject: [
1060
+ AUTHZPAAS_MODULE_OPTIONS
1061
+ ]
1062
+ },
1063
+ // 核心服务
1064
+ import_core2.Reflector,
1065
+ // 服务提供者
1066
+ PermissionService,
1067
+ AbilityFactory,
1068
+ AuthZPaasGuard,
1069
+ RolesMiddleware,
1070
+ // 守卫提供者
1071
+ {
1072
+ provide: import_core2.APP_GUARD,
1073
+ useClass: AuthZPaasGuard
1074
+ },
1075
+ {
1076
+ provide: import_core2.APP_FILTER,
1077
+ useClass: AuthZPaasExceptionFilter
1078
+ }
1079
+ ],
1080
+ exports: [
1081
+ PermissionService,
1082
+ AbilityFactory,
1083
+ RolesMiddleware
1084
+ ]
1085
+ };
1086
+ }
1087
+ };
1088
+ AuthZPaasModule = _ts_decorate6([
1089
+ (0, import_common7.Module)({})
1090
+ ], AuthZPaasModule);
1091
+
1092
+ // src/decorators/can-role.decorator.ts
1093
+ var import_common8 = require("@nestjs/common");
1094
+ var CanRole = /* @__PURE__ */ __name((role, and = false) => {
1095
+ let requirement;
1096
+ if (!Array.isArray(role) && typeof role === "string") {
1097
+ requirement = {
1098
+ roles: [
1099
+ role
1100
+ ],
1101
+ and
1102
+ };
1103
+ } else if (Array.isArray(role) && role.some((role2) => typeof role2 === "string")) {
1104
+ requirement = {
1105
+ roles: role,
1106
+ and
1107
+ };
1108
+ } else {
1109
+ throw new Error("Invalid CanRole parameter: " + JSON.stringify(role));
1110
+ }
1111
+ return (0, import_common8.SetMetadata)(ROLES_KEY, requirement);
1112
+ }, "CanRole");
1113
+
1114
+ // src/decorators/can-permission.decorator.ts
1115
+ var import_common9 = require("@nestjs/common");
1116
+ var CanPermission = /* @__PURE__ */ __name((permission, or) => {
1117
+ let requirements;
1118
+ if (Array.isArray(permission)) {
1119
+ requirements = permission;
1120
+ } else if (typeof permission === "object" && "subject" in permission) {
1121
+ requirements = [
1122
+ permission
1123
+ ];
1124
+ } else {
1125
+ throw new Error("Invalid CanPermission parameter: " + JSON.stringify(permission));
1126
+ }
1127
+ return (0, import_common9.SetMetadata)(PERMISSIONS_KEY, {
1128
+ requirements,
1129
+ or: or ?? false
1130
+ });
1131
+ }, "CanPermission");
1132
+
1133
+ // src/decorators/can-env.decorator.ts
1134
+ var import_common10 = require("@nestjs/common");
1135
+ var CanEnv = /* @__PURE__ */ __name((requirement) => {
1136
+ return (0, import_common10.SetMetadata)(ENVIRONMENT_KEY, requirement);
1137
+ }, "CanEnv");
1138
+
1139
+ // src/decorators/user-id.decorator.ts
1140
+ var import_common11 = require("@nestjs/common");
1141
+ var UserId = (0, import_common11.createParamDecorator)((_data, ctx) => {
1142
+ const request = ctx.switchToHttp().getRequest();
1143
+ return request.userContext?.userId;
1144
+ });
1145
+
1146
+ // src/decorators/mock-roles.decorator.ts
1147
+ var import_common12 = require("@nestjs/common");
1148
+ var MockRoles = (0, import_common12.createParamDecorator)((_data, ctx) => {
1149
+ const request = ctx.switchToHttp().getRequest();
1150
+ const mockRoles = getMockRolesFromCookie(request);
1151
+ return request[ENABLE_MOCK_ROLE_KEY] ? mockRoles : void 0;
1152
+ });
1153
+
1154
+ // src/controllers/permission.controller.ts
1155
+ var import_common13 = require("@nestjs/common");
1156
+ var import_swagger = require("@nestjs/swagger");
1157
+ function _ts_decorate7(decorators, target, key, desc) {
1158
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1159
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1160
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1161
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1162
+ }
1163
+ __name(_ts_decorate7, "_ts_decorate");
1164
+ function _ts_metadata4(k, v) {
1165
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1166
+ }
1167
+ __name(_ts_metadata4, "_ts_metadata");
1168
+ function _ts_param3(paramIndex, decorator) {
1169
+ return function(target, key) {
1170
+ decorator(target, key, paramIndex);
1171
+ };
1172
+ }
1173
+ __name(_ts_param3, "_ts_param");
1174
+ var PermissionDto = class {
1175
+ static {
1176
+ __name(this, "PermissionDto");
1177
+ }
1178
+ sub;
1179
+ actions;
1180
+ id;
1181
+ name;
1182
+ conditions;
1183
+ };
1184
+ _ts_decorate7([
1185
+ (0, import_swagger.ApiProperty)({
1186
+ description: "\u8D44\u6E90\u7C7B\u578B",
1187
+ example: "task"
1188
+ }),
1189
+ _ts_metadata4("design:type", String)
1190
+ ], PermissionDto.prototype, "sub", void 0);
1191
+ _ts_decorate7([
1192
+ (0, import_swagger.ApiProperty)({
1193
+ description: "\u64CD\u4F5C\u7C7B\u578B\u5217\u8868",
1194
+ example: [
1195
+ "create",
1196
+ "read",
1197
+ "update",
1198
+ "delete"
1199
+ ],
1200
+ type: [
1201
+ String
1202
+ ]
1203
+ }),
1204
+ _ts_metadata4("design:type", Array)
1205
+ ], PermissionDto.prototype, "actions", void 0);
1206
+ _ts_decorate7([
1207
+ (0, import_swagger.ApiProperty)({
1208
+ description: "\u6743\u9650ID",
1209
+ required: false
1210
+ }),
1211
+ _ts_metadata4("design:type", String)
1212
+ ], PermissionDto.prototype, "id", void 0);
1213
+ _ts_decorate7([
1214
+ (0, import_swagger.ApiProperty)({
1215
+ description: "\u6743\u9650\u540D\u79F0",
1216
+ required: false
1217
+ }),
1218
+ _ts_metadata4("design:type", String)
1219
+ ], PermissionDto.prototype, "name", void 0);
1220
+ _ts_decorate7([
1221
+ (0, import_swagger.ApiProperty)({
1222
+ description: "\u6743\u9650\u6761\u4EF6",
1223
+ required: false,
1224
+ type: "object"
1225
+ }),
1226
+ _ts_metadata4("design:type", typeof Record === "undefined" ? Object : Record)
1227
+ ], PermissionDto.prototype, "conditions", void 0);
1228
+ var PermissionResponse = class {
1229
+ static {
1230
+ __name(this, "PermissionResponse");
1231
+ }
1232
+ userId;
1233
+ roles;
1234
+ permissions;
1235
+ fetchedAt;
1236
+ };
1237
+ _ts_decorate7([
1238
+ (0, import_swagger.ApiProperty)({
1239
+ description: "\u7528\u6237ID",
1240
+ example: "user123",
1241
+ required: false
1242
+ }),
1243
+ _ts_metadata4("design:type", String)
1244
+ ], PermissionResponse.prototype, "userId", void 0);
1245
+ _ts_decorate7([
1246
+ (0, import_swagger.ApiProperty)({
1247
+ description: "\u7528\u6237\u89D2\u8272\u5217\u8868",
1248
+ example: [
1249
+ "admin",
1250
+ "user"
1251
+ ],
1252
+ type: [
1253
+ String
1254
+ ]
1255
+ }),
1256
+ _ts_metadata4("design:type", Array)
1257
+ ], PermissionResponse.prototype, "roles", void 0);
1258
+ _ts_decorate7([
1259
+ (0, import_swagger.ApiProperty)({
1260
+ description: "\u7528\u6237\u6743\u9650\u70B9\u4F4D\u5217\u8868",
1261
+ type: [
1262
+ PermissionDto
1263
+ ]
1264
+ }),
1265
+ _ts_metadata4("design:type", Array)
1266
+ ], PermissionResponse.prototype, "permissions", void 0);
1267
+ _ts_decorate7([
1268
+ (0, import_swagger.ApiProperty)({
1269
+ description: "\u6570\u636E\u83B7\u53D6\u65F6\u95F4",
1270
+ example: "2025-10-14T00:00:00.000Z"
1271
+ }),
1272
+ _ts_metadata4("design:type", typeof Date === "undefined" ? Object : Date)
1273
+ ], PermissionResponse.prototype, "fetchedAt", void 0);
1274
+ var PermissionController = class {
1275
+ static {
1276
+ __name(this, "PermissionController");
1277
+ }
1278
+ permissionService;
1279
+ constructor(permissionService) {
1280
+ this.permissionService = permissionService;
1281
+ }
1282
+ /**
1283
+ * 获取当前用户的权限数据
1284
+ *
1285
+ * @param userId 当前用户ID(从请求上下文中获取)
1286
+ * @returns 用户权限数据
1287
+ *
1288
+ * @example
1289
+ * GET /api/permissions
1290
+ *
1291
+ * Response:
1292
+ * {
1293
+ * "userId": "user123",
1294
+ * "roles": ["admin", "user"],
1295
+ * "permissions": [
1296
+ * { "sub": "task", "actions": ["create", "read", "update", "delete"] }
1297
+ * ],
1298
+ * "fetchedAt": "2025-10-14T00:00:00.000Z"
1299
+ * }
1300
+ */
1301
+ async getUserPermissions(userId, mockRoles) {
1302
+ const permissionData = await this.permissionService.getUserPermissions(userId, mockRoles);
1303
+ return {
1304
+ userId: permissionData?.userId,
1305
+ roles: permissionData?.roles || [],
1306
+ permissions: permissionData?.permissions || [],
1307
+ fetchedAt: permissionData?.fetchedAt || /* @__PURE__ */ new Date()
1308
+ };
1309
+ }
1310
+ /**
1311
+ * 开启角色模拟:将传入的 userId 写入 cookie,服务端优先使用该值请求权限
1312
+ */
1313
+ async enableMock(res, roles) {
1314
+ if (!Array.isArray(roles) || roles.length === 0) {
1315
+ return {
1316
+ success: false,
1317
+ message: "roles \u4E0D\u80FD\u4E3A\u7A7A"
1318
+ };
1319
+ }
1320
+ const val = roles.join(",");
1321
+ res.cookie(MOCK_ROLES_COOKIE_KEY, val, {
1322
+ httpOnly: false,
1323
+ sameSite: "lax",
1324
+ maxAge: 24 * 60 * 60 * 1e3,
1325
+ path: "/"
1326
+ });
1327
+ return {
1328
+ success: true,
1329
+ roles
1330
+ };
1331
+ }
1332
+ /**
1333
+ * 关闭角色模拟:清除 cookie
1334
+ */
1335
+ async disableMock(res) {
1336
+ res.clearCookie(MOCK_ROLES_COOKIE_KEY, {
1337
+ path: "/"
1338
+ });
1339
+ return {
1340
+ success: true
1341
+ };
1342
+ }
1343
+ async getMockRoles(mockRoles, userId) {
1344
+ const permissionData = await this.permissionService.getUserPermissions(userId, mockRoles);
1345
+ return {
1346
+ mocking: !!mockRoles,
1347
+ roles: permissionData?.roles || [],
1348
+ permissions: permissionData?.permissions || [],
1349
+ fetchedAt: permissionData?.fetchedAt || /* @__PURE__ */ new Date(),
1350
+ userId
1351
+ };
1352
+ }
1353
+ };
1354
+ _ts_decorate7([
1355
+ (0, import_common13.Get)(),
1356
+ (0, import_swagger.ApiOperation)({
1357
+ summary: "\u83B7\u53D6\u5F53\u524D\u7528\u6237\u7684\u6743\u9650\u6570\u636E"
1358
+ }),
1359
+ (0, import_swagger.ApiResponse)({
1360
+ status: 200,
1361
+ description: "\u6210\u529F\u8FD4\u56DE\u7528\u6237\u6743\u9650\u6570\u636E",
1362
+ type: PermissionResponse
1363
+ }),
1364
+ (0, import_swagger.ApiResponse)({
1365
+ status: 400,
1366
+ description: "\u7528\u6237\u672A\u8BA4\u8BC1"
1367
+ }),
1368
+ _ts_param3(0, UserId()),
1369
+ _ts_param3(1, MockRoles()),
1370
+ _ts_metadata4("design:type", Function),
1371
+ _ts_metadata4("design:paramtypes", [
1372
+ String,
1373
+ Array
1374
+ ]),
1375
+ _ts_metadata4("design:returntype", Promise)
1376
+ ], PermissionController.prototype, "getUserPermissions", null);
1377
+ _ts_decorate7([
1378
+ (0, import_common13.Post)("mock/enable"),
1379
+ (0, import_swagger.ApiOperation)({
1380
+ summary: "\u5F00\u542F\u89D2\u8272\u6A21\u62DF\uFF08\u5199\u5165 cookie: mockRoles\uFF09"
1381
+ }),
1382
+ (0, import_swagger.ApiResponse)({
1383
+ status: 200,
1384
+ description: "\u6A21\u62DF\u5DF2\u5F00\u542F"
1385
+ }),
1386
+ _ts_param3(0, (0, import_common13.Res)({
1387
+ passthrough: true
1388
+ })),
1389
+ _ts_param3(1, (0, import_common13.Body)("roles")),
1390
+ _ts_metadata4("design:type", Function),
1391
+ _ts_metadata4("design:paramtypes", [
1392
+ typeof Response === "undefined" ? Object : Response,
1393
+ Array
1394
+ ]),
1395
+ _ts_metadata4("design:returntype", Promise)
1396
+ ], PermissionController.prototype, "enableMock", null);
1397
+ _ts_decorate7([
1398
+ (0, import_common13.Post)("mock/disable"),
1399
+ (0, import_swagger.ApiOperation)({
1400
+ summary: "\u5173\u95ED\u89D2\u8272\u6A21\u62DF\uFF08\u6E05\u9664 cookie: mockRoles\uFF09"
1401
+ }),
1402
+ (0, import_swagger.ApiResponse)({
1403
+ status: 200,
1404
+ description: "\u6A21\u62DF\u5DF2\u5173\u95ED"
1405
+ }),
1406
+ _ts_param3(0, (0, import_common13.Res)({
1407
+ passthrough: true
1408
+ })),
1409
+ _ts_metadata4("design:type", Function),
1410
+ _ts_metadata4("design:paramtypes", [
1411
+ typeof Response === "undefined" ? Object : Response
1412
+ ]),
1413
+ _ts_metadata4("design:returntype", Promise)
1414
+ ], PermissionController.prototype, "disableMock", null);
1415
+ _ts_decorate7([
1416
+ (0, import_common13.Get)("mock/status"),
1417
+ (0, import_swagger.ApiOperation)({
1418
+ summary: "\u83B7\u53D6\u5F53\u524D\u7528\u6237\u7684\u89D2\u8272\u6A21\u62DF\u72B6\u6001"
1419
+ }),
1420
+ (0, import_swagger.ApiResponse)({
1421
+ status: 200,
1422
+ description: "\u6210\u529F\u8FD4\u56DE\u89D2\u8272\u6A21\u62DF\u6570\u636E"
1423
+ }),
1424
+ _ts_param3(0, MockRoles()),
1425
+ _ts_param3(1, UserId()),
1426
+ _ts_metadata4("design:type", Function),
1427
+ _ts_metadata4("design:paramtypes", [
1428
+ Object,
1429
+ String
1430
+ ]),
1431
+ _ts_metadata4("design:returntype", Promise)
1432
+ ], PermissionController.prototype, "getMockRoles", null);
1433
+ PermissionController = _ts_decorate7([
1434
+ (0, import_swagger.ApiTags)("\u6743\u9650\u7BA1\u7406"),
1435
+ (0, import_common13.Controller)("api/permissions"),
1436
+ _ts_metadata4("design:type", Function),
1437
+ _ts_metadata4("design:paramtypes", [
1438
+ typeof PermissionService === "undefined" ? Object : PermissionService
1439
+ ])
1440
+ ], PermissionController);
1441
+ // Annotate the CommonJS export names for ESM import in node:
1442
+ 0 && (module.exports = {
1443
+ ANONYMOUS_USER_ID,
1444
+ AUTHZPAAS_MODULE_OPTIONS,
1445
+ AbilityFactory,
1446
+ AuthZPaasExceptionFilter,
1447
+ AuthZPaasGuard,
1448
+ AuthZPaasModule,
1449
+ CACHE_CONFIG_TOKEN,
1450
+ CanEnv,
1451
+ CanPermission,
1452
+ CanRole,
1453
+ DEFAULT_LOGIN_PATH,
1454
+ ENABLE_MOCK_ROLE_KEY,
1455
+ ENVIRONMENT_KEY,
1456
+ MOCK_ROLES_COOKIE_KEY,
1457
+ MockRoles,
1458
+ NEED_LOGIN_KEY,
1459
+ PERMISSIONS_KEY,
1460
+ PERMISSION_API_CONFIG_TOKEN,
1461
+ PermissionController,
1462
+ PermissionDeniedException,
1463
+ PermissionDeniedType,
1464
+ PermissionDto,
1465
+ PermissionResponse,
1466
+ PermissionService,
1467
+ ROLES_KEY,
1468
+ ROLE_SUBJECT,
1469
+ RolesMiddleware,
1470
+ UserId
1471
+ });
1472
+ //# sourceMappingURL=index.cjs.map