@lark-apaas/nestjs-authzpaas 0.1.0-alpha.4 → 0.1.0-alpha.40
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/README.md +8 -529
- package/dist/index.cjs +160 -102
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +92 -115
- package/dist/index.d.ts +92 -115
- package/dist/index.js +161 -98
- package/dist/index.js.map +1 -1
- package/package.json +8 -3
package/dist/index.js
CHANGED
|
@@ -6,12 +6,12 @@ import { Module as Module2 } from "@nestjs/common";
|
|
|
6
6
|
import { APP_GUARD as APP_GUARD2, Reflector as Reflector4 } from "@nestjs/core";
|
|
7
7
|
|
|
8
8
|
// src/services/permission.service.ts
|
|
9
|
-
import { Injectable as Injectable3,
|
|
9
|
+
import { Injectable as Injectable3, HttpStatus, Inject } from "@nestjs/common";
|
|
10
10
|
|
|
11
11
|
// ../nestjs-authnpaas/dist/index.js
|
|
12
12
|
import { Module } from "@nestjs/common";
|
|
13
13
|
import { APP_GUARD, Reflector as Reflector2 } from "@nestjs/core";
|
|
14
|
-
import { Injectable } from "@nestjs/common";
|
|
14
|
+
import { Injectable, UnauthorizedException } from "@nestjs/common";
|
|
15
15
|
import { Reflector } from "@nestjs/core";
|
|
16
16
|
import { SetMetadata } from "@nestjs/common";
|
|
17
17
|
import { SetMetadata as SetMetadata2 } from "@nestjs/common";
|
|
@@ -20,7 +20,7 @@ var __name2 = /* @__PURE__ */ __name((target, value) => __defProp2(target, "name
|
|
|
20
20
|
value,
|
|
21
21
|
configurable: true
|
|
22
22
|
}), "__name");
|
|
23
|
-
var AUTHNPAAS_MODULE_OPTIONS = Symbol("AUTHNPAAS_MODULE_OPTIONS");
|
|
23
|
+
var AUTHNPAAS_MODULE_OPTIONS = /* @__PURE__ */ Symbol("AUTHNPAAS_MODULE_OPTIONS");
|
|
24
24
|
var NEED_LOGIN_KEY = "authnpaas:needLogin";
|
|
25
25
|
function _ts_decorate(decorators, target, key, desc) {
|
|
26
26
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
@@ -56,8 +56,8 @@ var AuthNPaasGuard = class {
|
|
|
56
56
|
context.getClass()
|
|
57
57
|
]);
|
|
58
58
|
if (needLoginMeta && !userId && loginUrl) {
|
|
59
|
-
response.
|
|
60
|
-
|
|
59
|
+
response.setHeader("x-login-url", loginUrl);
|
|
60
|
+
throw new UnauthorizedException("\u672A\u767B\u5F55");
|
|
61
61
|
}
|
|
62
62
|
return true;
|
|
63
63
|
}
|
|
@@ -119,16 +119,9 @@ var Public = /* @__PURE__ */ __name2(() => SetMetadata(IS_PUBLIC_KEY, true), "Pu
|
|
|
119
119
|
|
|
120
120
|
// src/const.ts
|
|
121
121
|
var ANONYMOUS_USER_ID = "anonymous_user_id";
|
|
122
|
-
var PERMISSION_API_CONFIG_TOKEN = Symbol("PERMISSION_API_CONFIG");
|
|
123
|
-
var
|
|
124
|
-
var AUTHZPAAS_MODULE_OPTIONS = Symbol("AUTHZPAAS_MODULE_OPTIONS");
|
|
122
|
+
var PERMISSION_API_CONFIG_TOKEN = /* @__PURE__ */ Symbol("PERMISSION_API_CONFIG");
|
|
123
|
+
var AUTHZPAAS_MODULE_OPTIONS = /* @__PURE__ */ Symbol("AUTHZPAAS_MODULE_OPTIONS");
|
|
125
124
|
var ROLES_KEY = "authzpaas:roles";
|
|
126
|
-
var PERMISSIONS_KEY = "authzpaas:permissions";
|
|
127
|
-
var ENVIRONMENT_KEY = "authzpaas:environment";
|
|
128
|
-
var NEED_LOGIN_KEY2 = "authzpaas:needLogin";
|
|
129
|
-
var DEFAULT_LOGIN_PATH = "/login";
|
|
130
|
-
var MOCK_ROLES_COOKIE_KEY = "mockRoles";
|
|
131
|
-
var ENABLE_MOCK_ROLE_KEY = "__authzpaas_enableMockRole";
|
|
132
125
|
|
|
133
126
|
// src/casl/ability.factory.ts
|
|
134
127
|
import { Injectable as Injectable2 } from "@nestjs/common";
|
|
@@ -210,15 +203,12 @@ var PermissionDeniedException = class _PermissionDeniedException extends HttpExc
|
|
|
210
203
|
/**
|
|
211
204
|
* 创建角色不足异常
|
|
212
205
|
*/
|
|
213
|
-
static roleRequired(requiredRoles
|
|
214
|
-
const message =
|
|
206
|
+
static roleRequired(requiredRoles) {
|
|
207
|
+
const message = `\u9700\u8981\u4EE5\u4E0B\u4EFB\u4E00\u89D2\u8272: ${requiredRoles.join(", ")}`;
|
|
215
208
|
return new _PermissionDeniedException({
|
|
216
209
|
type: "ROLE_REQUIRED",
|
|
217
210
|
message,
|
|
218
|
-
requiredRoles
|
|
219
|
-
metadata: {
|
|
220
|
-
and
|
|
221
|
-
}
|
|
211
|
+
requiredRoles
|
|
222
212
|
});
|
|
223
213
|
}
|
|
224
214
|
/**
|
|
@@ -246,6 +236,8 @@ var PermissionDeniedException = class _PermissionDeniedException extends HttpExc
|
|
|
246
236
|
};
|
|
247
237
|
|
|
248
238
|
// src/services/permission.service.ts
|
|
239
|
+
import { PLATFORM_HTTP_CLIENT, PlatformHttpClient } from "@lark-apaas/nestjs-common";
|
|
240
|
+
import { RequestContextService } from "@lark-apaas/nestjs-common";
|
|
249
241
|
function _ts_decorate4(decorators, target, key, desc) {
|
|
250
242
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
251
243
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -263,62 +255,61 @@ function _ts_param(paramIndex, decorator) {
|
|
|
263
255
|
};
|
|
264
256
|
}
|
|
265
257
|
__name(_ts_param, "_ts_param");
|
|
266
|
-
var PermissionService = class
|
|
258
|
+
var PermissionService = class {
|
|
267
259
|
static {
|
|
268
260
|
__name(this, "PermissionService");
|
|
269
261
|
}
|
|
270
262
|
apiConfig;
|
|
271
263
|
abilityFactory;
|
|
272
|
-
|
|
273
|
-
|
|
264
|
+
client;
|
|
265
|
+
requestContextService;
|
|
266
|
+
constructor(apiConfig, abilityFactory, client, requestContextService) {
|
|
274
267
|
this.apiConfig = apiConfig;
|
|
275
268
|
this.abilityFactory = abilityFactory;
|
|
269
|
+
this.client = client;
|
|
270
|
+
this.requestContextService = requestContextService;
|
|
276
271
|
}
|
|
277
272
|
/**
|
|
278
273
|
* 获取用户权限数据
|
|
279
274
|
*/
|
|
280
|
-
async getUserPermissions(
|
|
281
|
-
const
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
return dataWithTimestamp;
|
|
290
|
-
} catch (error) {
|
|
291
|
-
this.logger.error(`Failed to fetch permissions for user ${userId}:`, error);
|
|
292
|
-
throw error;
|
|
293
|
-
}
|
|
294
|
-
})();
|
|
295
|
-
return requestPromise;
|
|
275
|
+
async getUserPermissions({ laneId }) {
|
|
276
|
+
const permissionData = await this.fetchFromApi({
|
|
277
|
+
laneId
|
|
278
|
+
});
|
|
279
|
+
const dataWithTimestamp = {
|
|
280
|
+
...permissionData,
|
|
281
|
+
fetchedAt: /* @__PURE__ */ new Date()
|
|
282
|
+
};
|
|
283
|
+
return dataWithTimestamp;
|
|
296
284
|
}
|
|
297
285
|
/**
|
|
298
286
|
* 从 API 获取权限数据
|
|
299
287
|
* 内置实现,用户无需配置
|
|
300
288
|
*/
|
|
301
|
-
async fetchFromApi(
|
|
289
|
+
async fetchFromApi({ laneId }) {
|
|
302
290
|
const { timeout = 5e3 } = this.apiConfig || {};
|
|
303
|
-
const {
|
|
304
|
-
|
|
291
|
+
const { appId = "", userId = "" } = this.requestContextService.getContext() || {};
|
|
292
|
+
if (!appId) {
|
|
293
|
+
throw new PermissionDeniedException({
|
|
294
|
+
type: PermissionDeniedType.PERMISSION_CONFIG_QUERY_FAILED,
|
|
295
|
+
message: "appId is empty"
|
|
296
|
+
}, HttpStatus.BAD_REQUEST);
|
|
297
|
+
}
|
|
298
|
+
const url = `/app/${appId}/inner/api/v1/permissions/roles`;
|
|
305
299
|
const requestHeaders = {
|
|
306
|
-
"Content-Type": "application/json"
|
|
300
|
+
"Content-Type": "application/json",
|
|
301
|
+
// 透传 laneId 到权限服务
|
|
302
|
+
...laneId ? {
|
|
303
|
+
"x-tt-env": laneId || ""
|
|
304
|
+
} : {}
|
|
307
305
|
};
|
|
308
|
-
if (cookies) {
|
|
309
|
-
const cookieString = Object.entries(cookies).map(([key, value]) => `${key}=${value}`).join("; ");
|
|
310
|
-
if (cookieString) {
|
|
311
|
-
requestHeaders.Cookie = cookieString;
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
if (csrfToken) {
|
|
315
|
-
requestHeaders["X-Suda-Csrf-Token"] = csrfToken;
|
|
316
|
-
}
|
|
317
306
|
const controller = new AbortController();
|
|
318
307
|
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
319
308
|
try {
|
|
320
|
-
const response = await
|
|
321
|
-
|
|
309
|
+
const response = await this.client.post(url, {
|
|
310
|
+
userID: userId || ""
|
|
311
|
+
}, {
|
|
312
|
+
credentials: "include",
|
|
322
313
|
headers: requestHeaders,
|
|
323
314
|
signal: controller.signal
|
|
324
315
|
});
|
|
@@ -331,10 +322,22 @@ var PermissionService = class _PermissionService {
|
|
|
331
322
|
message: error.message
|
|
332
323
|
}, HttpStatus.INTERNAL_SERVER_ERROR);
|
|
333
324
|
}
|
|
334
|
-
|
|
325
|
+
let data;
|
|
326
|
+
let responseText = "";
|
|
327
|
+
try {
|
|
328
|
+
responseText = await response.text();
|
|
329
|
+
data = JSON.parse(responseText);
|
|
330
|
+
} catch (jsonError) {
|
|
331
|
+
const error = new Error(`Permission API returned invalid JSON: ${responseText}`);
|
|
332
|
+
throw new PermissionDeniedException({
|
|
333
|
+
cause: error,
|
|
334
|
+
type: PermissionDeniedType.PERMISSION_CONFIG_QUERY_FAILED,
|
|
335
|
+
message: error.message
|
|
336
|
+
}, HttpStatus.INTERNAL_SERVER_ERROR);
|
|
337
|
+
}
|
|
335
338
|
return {
|
|
336
339
|
userId,
|
|
337
|
-
roles: data.
|
|
340
|
+
roles: data.data?.roleList || [],
|
|
338
341
|
// TODO: 基于权限点位设置能力
|
|
339
342
|
// permissions: data.permissions || [],
|
|
340
343
|
fetchedAt: /* @__PURE__ */ new Date()
|
|
@@ -378,18 +381,14 @@ var PermissionService = class _PermissionService {
|
|
|
378
381
|
* 检查角色要求
|
|
379
382
|
* 使用 CASL Ability 统一鉴权方式
|
|
380
383
|
* @param requirement 角色要求
|
|
381
|
-
* @param
|
|
382
|
-
* @returns
|
|
383
|
-
* @throws PermissionDeniedException
|
|
384
|
+
* @param laneId 环境ID
|
|
385
|
+
* @returns 用户权限检查结果,包含结果和详细信息
|
|
386
|
+
* @throws PermissionDeniedException 当权限数据获取失败时
|
|
384
387
|
*/
|
|
385
|
-
async checkRoles(requirement,
|
|
386
|
-
const userId =
|
|
388
|
+
async checkRoles(requirement, laneId) {
|
|
389
|
+
const { userId = "" } = this.requestContextService.getContext() || {};
|
|
387
390
|
const permissionData = await this.getUserPermissions({
|
|
388
|
-
|
|
389
|
-
userId,
|
|
390
|
-
appId: userContext?.appId || "",
|
|
391
|
-
cookies: userContext?.cookies || {},
|
|
392
|
-
csrfToken: userContext?.csrfToken || ""
|
|
391
|
+
laneId
|
|
393
392
|
});
|
|
394
393
|
if (!permissionData) {
|
|
395
394
|
throw new PermissionDeniedException({
|
|
@@ -399,31 +398,43 @@ var PermissionService = class _PermissionService {
|
|
|
399
398
|
}, HttpStatus.BAD_REQUEST);
|
|
400
399
|
}
|
|
401
400
|
const ability = this.abilityFactory.createForUser(permissionData);
|
|
402
|
-
const { roles
|
|
403
|
-
|
|
404
|
-
|
|
401
|
+
const { roles } = requirement;
|
|
402
|
+
if (!roles || roles.length === 0) {
|
|
403
|
+
return {
|
|
404
|
+
result: true
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
const hasRole = roles.some((role) => ability.can(role, ROLE_SUBJECT));
|
|
405
408
|
if (!hasRole) {
|
|
406
409
|
const userRoles = permissionData.roles;
|
|
407
|
-
|
|
408
|
-
|
|
410
|
+
return {
|
|
411
|
+
result: false,
|
|
412
|
+
details: `\u7528\u6237 ${userId}, \u7528\u6237\u89D2\u8272 [${userRoles.join(", ")}], \u9700\u8981 [${roles.join(", ")}]`
|
|
413
|
+
};
|
|
409
414
|
}
|
|
410
|
-
return
|
|
415
|
+
return {
|
|
416
|
+
result: true
|
|
417
|
+
};
|
|
411
418
|
}
|
|
412
419
|
};
|
|
413
420
|
PermissionService = _ts_decorate4([
|
|
414
421
|
Injectable3(),
|
|
415
422
|
Public(),
|
|
416
423
|
_ts_param(0, Inject(PERMISSION_API_CONFIG_TOKEN)),
|
|
424
|
+
_ts_param(2, Inject(PLATFORM_HTTP_CLIENT)),
|
|
417
425
|
_ts_metadata2("design:type", Function),
|
|
418
426
|
_ts_metadata2("design:paramtypes", [
|
|
419
427
|
typeof PermissionApiConfig === "undefined" ? Object : PermissionApiConfig,
|
|
420
|
-
typeof AbilityFactory === "undefined" ? Object : AbilityFactory
|
|
428
|
+
typeof AbilityFactory === "undefined" ? Object : AbilityFactory,
|
|
429
|
+
typeof PlatformHttpClient === "undefined" ? Object : PlatformHttpClient,
|
|
430
|
+
typeof RequestContextService === "undefined" ? Object : RequestContextService
|
|
421
431
|
])
|
|
422
432
|
], PermissionService);
|
|
423
433
|
|
|
424
434
|
// src/guards/authzpaas.guard.ts
|
|
425
|
-
import { Injectable as Injectable4 } from "@nestjs/common";
|
|
435
|
+
import { Injectable as Injectable4, Logger, Inject as Inject2 } from "@nestjs/common";
|
|
426
436
|
import { Reflector as Reflector3 } from "@nestjs/core";
|
|
437
|
+
import { OBSERVABLE_SERVICE, ObservableService } from "@lark-apaas/nestjs-common";
|
|
427
438
|
function _ts_decorate5(decorators, target, key, desc) {
|
|
428
439
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
429
440
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -435,42 +446,101 @@ function _ts_metadata3(k, v) {
|
|
|
435
446
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
436
447
|
}
|
|
437
448
|
__name(_ts_metadata3, "_ts_metadata");
|
|
438
|
-
|
|
449
|
+
function _ts_param2(paramIndex, decorator) {
|
|
450
|
+
return function(target, key) {
|
|
451
|
+
decorator(target, key, paramIndex);
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
__name(_ts_param2, "_ts_param");
|
|
455
|
+
var AuthZPaasGuard = class _AuthZPaasGuard {
|
|
439
456
|
static {
|
|
440
457
|
__name(this, "AuthZPaasGuard");
|
|
441
458
|
}
|
|
442
459
|
reflector;
|
|
443
460
|
permissionService;
|
|
444
|
-
|
|
461
|
+
obs;
|
|
462
|
+
constructor(reflector, permissionService, obs) {
|
|
445
463
|
this.reflector = reflector;
|
|
446
464
|
this.permissionService = permissionService;
|
|
465
|
+
this.obs = obs;
|
|
466
|
+
}
|
|
467
|
+
logger = new Logger(_AuthZPaasGuard.name);
|
|
468
|
+
/**
|
|
469
|
+
* 验证角色要求是否有效
|
|
470
|
+
* @param requirements 角色要求
|
|
471
|
+
* @returns 是否有效
|
|
472
|
+
*/
|
|
473
|
+
isValidRoleRequirement(requirements) {
|
|
474
|
+
return Boolean(requirements && requirements.roles && requirements.roles.length > 0);
|
|
447
475
|
}
|
|
448
476
|
async canActivate(context) {
|
|
449
477
|
const http = context.switchToHttp();
|
|
450
478
|
const request = http.getRequest();
|
|
479
|
+
const laneId = request.headers["x-tt-env"];
|
|
480
|
+
const baseUrl = `${request.protocol}://${request.get("host")}`;
|
|
451
481
|
const userContext = request.userContext;
|
|
482
|
+
userContext.baseUrl = baseUrl;
|
|
452
483
|
const checkRoleRequirement = this.reflector.getAllAndOverride(ROLES_KEY, [
|
|
453
484
|
context.getHandler(),
|
|
454
485
|
context.getClass()
|
|
455
486
|
]);
|
|
456
|
-
if (checkRoleRequirement) {
|
|
457
|
-
|
|
487
|
+
if (this.isValidRoleRequirement(checkRoleRequirement)) {
|
|
488
|
+
const spanName = checkRoleRequirement.roles.join(" or ");
|
|
489
|
+
return this.obs.trace(spanName, async (span) => {
|
|
490
|
+
span.setAttributes({
|
|
491
|
+
module: "permission"
|
|
492
|
+
});
|
|
493
|
+
const startTime = Date.now();
|
|
494
|
+
try {
|
|
495
|
+
const checkResult = await this.permissionService.checkRoles(checkRoleRequirement, laneId);
|
|
496
|
+
const endTime = Date.now();
|
|
497
|
+
if (!checkResult.result) {
|
|
498
|
+
this.logger.warn(JSON.stringify({
|
|
499
|
+
role: spanName,
|
|
500
|
+
duration_ms: endTime - startTime,
|
|
501
|
+
result: checkResult.result ? "has_auth" : "no_auth",
|
|
502
|
+
result_detail: checkResult.details
|
|
503
|
+
}), {
|
|
504
|
+
source_type: "platform",
|
|
505
|
+
paas_attributes_module: "permission"
|
|
506
|
+
});
|
|
507
|
+
} else {
|
|
508
|
+
this.logger.log(JSON.stringify({
|
|
509
|
+
role: spanName,
|
|
510
|
+
duration_ms: endTime - startTime,
|
|
511
|
+
result: checkResult.result ? "has_auth" : "no_auth"
|
|
512
|
+
}), {
|
|
513
|
+
source_type: "platform",
|
|
514
|
+
paas_attributes_module: "permission"
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
return checkResult.result;
|
|
518
|
+
} catch (error) {
|
|
519
|
+
const endTime = Date.now();
|
|
520
|
+
this.logger.error(JSON.stringify({
|
|
521
|
+
role: spanName,
|
|
522
|
+
duration_ms: endTime - startTime,
|
|
523
|
+
result: "",
|
|
524
|
+
error_message: error.message
|
|
525
|
+
}), {
|
|
526
|
+
source_type: "platform",
|
|
527
|
+
paas_attributes_module: "permission"
|
|
528
|
+
});
|
|
529
|
+
throw error;
|
|
530
|
+
}
|
|
531
|
+
});
|
|
458
532
|
}
|
|
459
533
|
return true;
|
|
460
534
|
}
|
|
461
|
-
/**
|
|
462
|
-
* 检查角色要求
|
|
463
|
-
*/
|
|
464
|
-
async checkRoleRequirement(requirement, userContext) {
|
|
465
|
-
return this.permissionService.checkRoles(requirement, userContext);
|
|
466
|
-
}
|
|
467
535
|
};
|
|
468
536
|
AuthZPaasGuard = _ts_decorate5([
|
|
469
537
|
Injectable4(),
|
|
538
|
+
_ts_param2(2, Inject2(OBSERVABLE_SERVICE)),
|
|
470
539
|
_ts_metadata3("design:type", Function),
|
|
471
540
|
_ts_metadata3("design:paramtypes", [
|
|
472
541
|
typeof Reflector3 === "undefined" ? Object : Reflector3,
|
|
473
|
-
typeof PermissionService === "undefined" ? Object : PermissionService
|
|
542
|
+
typeof PermissionService === "undefined" ? Object : PermissionService,
|
|
543
|
+
typeof ObservableService === "undefined" ? Object : ObservableService
|
|
474
544
|
])
|
|
475
545
|
], AuthZPaasGuard);
|
|
476
546
|
|
|
@@ -601,19 +671,17 @@ AuthZPaasModule = _ts_decorate6([
|
|
|
601
671
|
|
|
602
672
|
// src/decorators/can-role.decorator.ts
|
|
603
673
|
import { SetMetadata as SetMetadata3 } from "@nestjs/common";
|
|
604
|
-
var CanRole = /* @__PURE__ */ __name((role
|
|
674
|
+
var CanRole = /* @__PURE__ */ __name((role) => {
|
|
605
675
|
let requirement;
|
|
606
676
|
if (!Array.isArray(role) && typeof role === "string") {
|
|
607
677
|
requirement = {
|
|
608
678
|
roles: [
|
|
609
679
|
role
|
|
610
|
-
]
|
|
611
|
-
and
|
|
680
|
+
]
|
|
612
681
|
};
|
|
613
|
-
} else if (Array.isArray(role) && role.
|
|
682
|
+
} else if (Array.isArray(role) && role.every((role2) => typeof role2 === "string")) {
|
|
614
683
|
requirement = {
|
|
615
|
-
roles: role
|
|
616
|
-
and
|
|
684
|
+
roles: role
|
|
617
685
|
};
|
|
618
686
|
} else {
|
|
619
687
|
throw new Error("Invalid CanRole parameter: " + JSON.stringify(role));
|
|
@@ -624,18 +692,13 @@ export {
|
|
|
624
692
|
ANONYMOUS_USER_ID,
|
|
625
693
|
AUTHZPAAS_MODULE_OPTIONS,
|
|
626
694
|
AbilityFactory,
|
|
695
|
+
AuthZPaasGuard,
|
|
627
696
|
AuthZPaasModule,
|
|
628
|
-
CACHE_CONFIG_TOKEN,
|
|
629
697
|
CanRole,
|
|
630
|
-
DEFAULT_LOGIN_PATH,
|
|
631
|
-
ENABLE_MOCK_ROLE_KEY,
|
|
632
|
-
ENVIRONMENT_KEY,
|
|
633
|
-
MOCK_ROLES_COOKIE_KEY,
|
|
634
|
-
NEED_LOGIN_KEY2 as NEED_LOGIN_KEY,
|
|
635
|
-
PERMISSIONS_KEY,
|
|
636
698
|
PERMISSION_API_CONFIG_TOKEN,
|
|
637
699
|
PermissionDeniedException,
|
|
638
700
|
PermissionDeniedType,
|
|
701
|
+
PermissionService,
|
|
639
702
|
ROLES_KEY,
|
|
640
703
|
ROLE_SUBJECT
|
|
641
704
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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, cookies, csrfToken } = requestDto;\n\n // 构建完整获取用户权限 URL\n const url = `${baseUrl}/api/v1/permission/apps/${appId}/roles`;\n\n // 构建请求头\n const requestHeaders: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n if (cookies) {\n const cookieString = Object.entries(cookies)\n .map(([key, value]) => `${key}=${value}`)\n .join('; ');\n if (cookieString) {\n requestHeaders.Cookie = cookieString;\n }\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: '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 {\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 role_list?: string[];\n };\n\n return {\n userId,\n roles: data.role_list || [],\n // TODO: 基于权限点位设置能力\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 {\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 ): Promise<UserPermissionData> {\n const userId = userContext?.userId || ANONYMOUS_USER_ID;\n const permissionData = await this.getUserPermissions({\n baseUrl: userContext?.baseUrl || '',\n userId,\n appId: userContext?.appId || '',\n cookies: userContext?.cookies || {},\n csrfToken: userContext?.csrfToken || '',\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 { RoleRequirement, 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\n const userContext = request.userContext as UserContext;\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, userContext);\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 checkRoleRequirement(\n requirement: RoleRequirement,\n userContext?: UserContext\n ) {\n return this.permissionService.checkRoles(requirement, userContext);\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":";;;;AAAA,SAAwBA,UAAAA,eAAoB;AAC5C,SAASC,aAAAA,YAAWC,aAAAA,kBAAiB;;;ACDrC,SAASC,cAAAA,aAAYC,QAAQC,YAAYC,cAAc;;;ACAvD,SAAwBC,cAAc;AACtC,SAASC,WAAWC,aAAAA,kBAAiB;AEDrC,SAASC,kBAAiD;AAE1D,SAASD,iBAAiB;ACF1B,SAASE,mBAAmB;ACA5B,SAASA,eAAAA,oBAAmB;;;;;;AHKrB,IAAMC,2BAA2BC,OAAO,0BAAA;AAOxC,IAAMC,iBAAiB;;;;;;;;;;;;;;ACFvB,IAAMC,iBAAN,MAAMA;SAAAA;;;SAAAA;;;;EACX,YAAoBC,WAAsB;SAAtBA,YAAAA;EAAuB;EAE3C,MAAMC,YAAYC,SAA6C;AAC7D,UAAMC,OAAOD,QAAQE,aAAY;AACjC,UAAMC,UAAUF,KAAKG,WAAU;AAC/B,UAAMC,WAAWJ,KAAKK,YAAW;AAEjC,UAAM,EAAEC,QAAQC,SAAQ,IAAKL,QAAQM,eAAe,CAAC;AAGrD,UAAMC,gBAAgB,KAAKZ,UAAUa,kBAElCf,gBAAgB;MAACI,QAAQY,WAAU;MAAIZ,QAAQa,SAAQ;KAAG;AAG7D,QAAIH,iBAAiB,CAACH,UAAUC,UAAU;AACxCH,eAASS,SAAS,KAAKN,QAAAA;AACvB,aAAO;IACT;AAEA,WAAO;EACT;AACF;;;;;;;;;;;;;;;;AF1BO,IAAMO,kBAAN,MAAMA,iBAAAA;SAAAA;;;SAAAA;;;EACX,OAAOC,QAAQC,SAAiD;AAC9D,WAAO;MACLC,QAAQH;MACRI,QAAQ;MACRC,aAAa,CAAA;MACbC,WAAW;;QAET;UACEC,SAAS5B;UACT6B,UAAU;YACR,GAAIN,WAAW,CAAC;UAClB;QACF;;QAEAO;;QAEA3B;;QAEA;UACEyB,SAASG;UACTC,UAAU7B;QACZ;;MAEF8B,SAAS,CAAA;IACX;EACF;AACF;;;;AG7BO,IAAMC,gBAAgB;AAuBtB,IAAMC,SAAS,gBAAAC,QAAA,MAAMC,YAAYH,eAAe,IAAA,GAAjC,QAAA;;;AEvBf,IAAMI,oBAAoB;AAO1B,IAAMC,8BAA8BC,OAAO,uBAAA;AAG3C,IAAMC,qBAAqBD,OAAO,cAAA;AAGlC,IAAME,2BAA2BF,OAAO,0BAAA;AAOxC,IAAMG,YAAY;AAGlB,IAAMC,kBAAkB;AAGxB,IAAMC,kBAAkB;AAGxB,IAAMC,kBAAiB;AAGvB,IAAMC,qBAAqB;AAG3B,IAAMC,wBAAwB;AAE9B,IAAMC,uBAAuB;;;AC1CpC,SAASC,cAAAA,mBAAkB;AAC3B,SAASC,gBAAgBC,mBAAiC;;;;;;;;AAgBnD,IAAMC,eAAe;AAWrB,IAAMC,iBAAN,MAAMA;SAAAA;;;;;;EAIXC,cAAcC,gBAAgD;AAC5D,UAAM,EAAEC,KAAKC,MAAK,IAAK,IAAIC,eACzBC,WAAAA;AAcF,eAAWC,QAAQL,eAAeM,OAAO;AACvCL,UAAII,MAAgBR,YAAAA;IACtB;AAEA,WAAOK,MAAAA;EACT;AACF;;;;;;ACtDA,SAASK,qBAAqB;AAKvB,IAAKC,uBAAAA,0BAAAA,uBAAAA;AACA,EAAAA,sBAAA,iBAAA,IAAA;AAED,EAAAA,sBAAA,eAAA,IAAA;AAEA,EAAAA,sBAAA,qBAAA,IAAA;AAEI,EAAAA,sBAAA,gCAAA,IAAA;SAPHA;;AAsCL,IAAMC,4BAAN,MAAMA,mCAAkCC,cAAAA;EA3C/C,OA2C+CA;;;EAC7BC;EACAC;EAEhB,YAAYA,SAAkCC,iBAAyB,KAAK;AAC1E,UACE;MACEC,YAAYD;MACZE,OAAOH,QAAQG;MACfJ,MAAMC,QAAQD;MACdK,SAASJ,QAAQI;MACjB,GAAIJ,QAAQK,iBAAiB;QAAEA,eAAeL,QAAQK;MAAc;MACpE,GAAIL,QAAQM,uBAAuB;QACjCA,qBAAqBN,QAAQM;MAC/B;MACA,GAAIN,QAAQO,0BAA0B;QACpCA,wBAAwBP,QAAQO;MAClC;MACA,GAAIP,QAAQQ,YAAY;QAAEA,UAAUR,QAAQQ;MAAS;IACvD,GACAP,cAAAA;AAGF,SAAKF,OAAOC,QAAQD;AACpB,SAAKC,UAAUA;AACf,SAAKS,OAAO;EACd;;;;EAKA,OAAOC,gBACLN,UAAkB,kCACS;AAC3B,WAAO,IAAIP,2BAA0B;MACnCE,MAAI;MACJK;IACF,CAAA;EACF;;;;EAKA,OAAOO,aACLN,eACAO,MAAe,OACY;AAC3B,UAAMR,UAAUQ,MACZ,yCAAWP,cAAcQ,KAAK,IAAA,CAAA,KAC9B,qDAAaR,cAAcQ,KAAK,IAAA,CAAA;AAEpC,WAAO,IAAIhB,2BAA0B;MACnCE,MAAI;MACJK;MACAC;MACAG,UAAU;QAAEI;MAAI;IAClB,CAAA;EACF;;;;EAKA,OAAOE,mBACLR,qBACAS,KAAc,OACdC,eAC2B;AAC3B,QAAIZ;AAEJ,QAAIY,eAAe;AACjBZ,gBAAUY;IACZ,WAAWV,oBAAoBW,WAAW,GAAG;AAC3C,YAAMC,OAAOZ,oBAAoB,CAAA;AACjCF,gBAAUW,KACN,gDAAaG,KAAKC,OAAO,sDAAcD,KAAKE,QAAQP,KAAK,IAAA,CAAA,MACzD,gDAAaK,KAAKC,OAAO,0CAAYD,KAAKE,QAAQP,KAAK,IAAA,CAAA;IAC7D,OAAO;AACLT,gBAAUW,KACN,uGAAuBT,oBAAoBe,IAAI,CAAC,EAAED,SAASD,QAAO,MAAO,UAAKA,OAAAA,sDAAqBC,QAAQP,KAAK,IAAA,CAAA,GAAQ,EAAEA,KAAK,IAAA,CAAA,KAC/H,uGAAuBP,oBAAoBe,IAAI,CAAC,EAAED,SAASD,QAAO,MAAO,UAAKA,OAAAA,0CAAmBC,QAAQP,KAAK,IAAA,CAAA,GAAQ,EAAEA,KAAK,IAAA,CAAA;IACnI;AAEA,WAAO,IAAIhB,2BAA0B;MACnCE,MAAI;MACJK;MACAE;MACAE,UAAU;QAAEO;MAAG;IACjB,CAAA;EACF;AACF;;;;;;;;;;;;;;;;;;;;AR7GO,IAAMO,oBAAN,MAAMA,mBAAAA;SAAAA;;;;;EACMC,SAAS,IAAIC,OAAOF,mBAAkBG,IAAI;EAE3D,YAEmBC,WACAC,gBACjB;SAFiBD,YAAAA;SACAC,iBAAAA;EAChB;;;;EAKH,MAAMC,mBACJC,YACoC;AAEpC,UAAMC,kBAAkB,YAAA;AACtB,YAAMC,SAASF,WAAWE,UAAUC;AACpC,UAAI;AACF,cAAMC,iBAAiB,MAAM,KAAKC,aAAaL,UAAAA;AAE/C,cAAMM,oBAAwC;UAC5C,GAAGF;UACHG,WAAW,oBAAIC,KAAAA;QACjB;AAEA,eAAOF;MACT,SAASG,OAAO;AACd,aAAKf,OAAOe,MACV,wCAAwCP,MAAAA,KACxCO,KAAAA;AAEF,cAAMA;MACR;IACF,GAAA;AAEA,WAAOR;EACT;;;;;EAMA,MAAcI,aACZL,YAC6B;AAC7B,UAAM,EAAEU,UAAU,IAAI,IAAK,KAAKb,aAAa,CAAC;AAC9C,UAAM,EAAEc,SAAST,QAAQU,OAAOC,SAASC,UAAS,IAAKd;AAGvD,UAAMe,MAAM,GAAGJ,OAAAA,2BAAkCC,KAAAA;AAGjD,UAAMI,iBAAyC;MAC7C,gBAAgB;IAClB;AAEA,QAAIH,SAAS;AACX,YAAMI,eAAeC,OAAOC,QAAQN,OAAAA,EACjCO,IAAI,CAAC,CAACC,KAAKC,KAAAA,MAAW,GAAGD,GAAAA,IAAOC,KAAAA,EAAO,EACvCC,KAAK,IAAA;AACR,UAAIN,cAAc;AAChBD,uBAAeQ,SAASP;MAC1B;IACF;AAEA,QAAIH,WAAW;AACbE,qBAAe,mBAAA,IAAuBF;IACxC;AAGA,UAAMW,aAAa,IAAIC,gBAAAA;AACvB,UAAMC,YAAYC,WAAW,MAAMH,WAAWI,MAAK,GAAInB,OAAAA;AAEvD,QAAI;AACF,YAAMoB,WAAW,MAAMC,MAAMhB,KAAK;QAChCiB,QAAQ;QACRC,SAASjB;QACTkB,QAAQT,WAAWS;MACrB,CAAA;AAEAC,mBAAaR,SAAAA;AAEb,UAAI,CAACG,SAASM,IAAI;AAChB,cAAM3B,QAAQ,IAAI4B,MAChB,2BAA2BP,SAASQ,MAAM,KAAKR,SAASS,UAAU,EAAE;AAEtE,cAAM,IAAIC,0BACR;UACEC,OAAOhC;UACPiC,MAAMC,qBAAqBC;UAC3BC,SAASpC,MAAMoC;QACjB,GACAC,WAAWC,qBAAqB;MAEpC;AAEA,YAAMC,OAAQ,MAAMlB,SAASmB,KAAI;AAIjC,aAAO;QACL/C;QACAgD,OAAOF,KAAKG,aAAa,CAAA;;;QAGzB5C,WAAW,oBAAIC,KAAAA;MACjB;IACF,SAASC,OAAgB;AACvB0B,mBAAaR,SAAAA;AACb,UAAIyB,MAAM3C;AAEV,UAAKA,MAA4Bb,SAAS,cAAc;AACtDwD,cAAM,IAAIf,MAAM,wCAAwC3B,OAAAA,IAAW;MACrE;AAEA,YAAM,IAAI8B,0BACR;QACEC,OAAOW;QACPV,MAAMC,qBAAqBC;QAC3BC,SAASO,IAAIP;MACf,GACAC,WAAWC,qBAAqB;IAEpC;EACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoCA,MAAMM,WACJC,aACAC,aAC6B;AAC7B,UAAMrD,SAASqD,aAAarD,UAAUC;AACtC,UAAMC,iBAAiB,MAAM,KAAKL,mBAAmB;MACnDY,SAAS4C,aAAa5C,WAAW;MACjCT;MACAU,OAAO2C,aAAa3C,SAAS;MAC7BC,SAAS0C,aAAa1C,WAAW,CAAC;MAClCC,WAAWyC,aAAazC,aAAa;IACvC,CAAA;AACA,QAAI,CAACV,gBAAgB;AACnB,YAAM,IAAIoC,0BACR;QACEC,OAAO,IAAIJ,MAAM,6CAAA;QACjBK,MAAMC,qBAAqBC;QAC3BC,SAAS;MACX,GACAC,WAAWU,WAAW;IAE1B;AAGA,UAAMC,UAAU,KAAK3D,eAAe4D,cAActD,cAAAA;AAElD,UAAM,EAAE8C,OAAOS,IAAG,IAAKL;AAIvB,UAAMM,eAAeV,MAAM9B,IAAIyC,CAAAA,SAAQJ,QAAQK,IAAID,MAAME,YAAAA,CAAAA;AAEzD,UAAMC,UAAUL,MACZC,aAAaK,MAAMC,CAAAA,WAAUA,MAAAA,IAC7BN,aAAaO,KAAKD,CAAAA,WAAUA,MAAAA;AAEhC,QAAI,CAACF,SAAS;AACZ,YAAMI,YAAYhE,eAAe8C;AACjC,WAAKxD,OAAO2E,KACV,sDAAcnE,MAAAA,+BAAiBkE,UAAU7C,KAAK,IAAA,CAAA,oBAAe2B,MAAM3B,KAAK,IAAA,CAAA,GAAQ;AAElF,YAAMiB,0BAA0B8B,aAAapB,OAAOS,GAAAA;IACtD;AAEA,WAAOvD;EACT;AAoFF;;;;;;;;;;;;;ASzTA,SAASmE,cAAAA,mBAAiD;AAC1D,SAASC,aAAAA,kBAAiB;;;;;;;;;;;;AAWnB,IAAMC,iBAAN,MAAMA;SAAAA;;;;;EACX,YACUC,WACAC,mBACR;SAFQD,YAAAA;SACAC,oBAAAA;EACP;EAEH,MAAMC,YAAYC,SAA6C;AAC7D,UAAMC,OAAOD,QAAQE,aAAY;AACjC,UAAMC,UAAUF,KAAKG,WAAU;AAE/B,UAAMC,cAAcF,QAAQE;AAG5B,UAAMC,uBACJ,KAAKT,UAAUU,kBAAwCC,WAAW;MAChER,QAAQS,WAAU;MAClBT,QAAQU,SAAQ;KACjB;AAEH,QAAIJ,sBAAsB;AACxB,YAAM,KAAKA,qBAAqBA,sBAAsBD,WAAAA;IACxD;AAiBA,WAAO;EACT;;;;EAKA,MAAcC,qBACZK,aACAN,aACA;AACA,WAAO,KAAKP,kBAAkBc,WAAWD,aAAaN,WAAAA;EACxD;AAYF;;;;;;;;;;;;;;;;;;AVvDO,IAAMQ,kBAAN,MAAMA,iBAAAA;SAAAA;;;EACX,OAAOC,QAAQC,SAAiD;AAC9D,UAAM,EAAEC,gBAAgB,CAAC,EAAC,IAAKD,WAAW,CAAC;AAC3C,WAAO;MACLE,QAAQJ;MACRK,QAAQ;MACRC,aAAa,CAAA;MACbC,WAAW;;QAET;UACEC,SAASC;UACTC,UAAU;YAAE,GAAGR;UAAQ;QACzB;QACA;UACEM,SAASG;UACTD,UAAUP;QACZ;;QAEAS;;QAEAC;QACAC;QACAC;;QAEA;UACEP,SAASQ;UACTC,UAAUF;QACZ;;MAEFG,SAAS;QAACL;QAAmBC;;IAC/B;EACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgCA,OAAOK,aAAajB,SAAqD;AACvE,UAAM,EAAEkB,UAAU,CAAA,GAAIC,SAAS,CAAA,GAAIC,WAAU,IAAKpB;AAElD,WAAO;MACLE,QAAQJ;MACRK,QAAQ;MACRe;MACAd,aAAa,CAAA;MACbC,WAAW;;QAET;UACEC,SAASC;UACTa;UACAD;QACF;;QAEA;UACEb,SAASG;UACTW,YAAY,wBAACC,kBAAAA;AACX,mBAAOA,cAAcpB;UACvB,GAFY;UAGZkB,QAAQ;YAACZ;;QACX;;QAEAG;;QAEAC;QACAC;QACAC;;QAEA;UACEP,SAASQ;UACTC,UAAUF;QACZ;;MAEFG,SAAS;QAACL;QAAmBC;;IAC/B;EACF;AACF;;;;;;AWvHA,SAASU,eAAAA,oBAAmB;AA6BrB,IAAMC,UAAU,wBACrBC,MACAC,MAAe,UAAK;AAGpB,MAAIC;AAEJ,MAAI,CAACC,MAAMC,QAAQJ,IAAAA,KAAS,OAAOA,SAAS,UAAU;AAEpDE,kBAAc;MACZG,OAAO;QAACL;;MACRC;IACF;EACF,WAAWE,MAAMC,QAAQJ,IAAAA,KAASA,KAAKM,KAAK,CAACN,UAAS,OAAOA,UAAS,QAAA,GAAW;AAE/EE,kBAAc;MACZG,OAAOL;MACPC;IACF;EACF,OAAO;AACL,UAAM,IAAIM,MAAM,gCAAgCC,KAAKC,UAAUT,IAAAA,CAAAA;EACjE;AAEA,SAAOU,aAAYC,WAAWT,WAAAA;AAChC,GAxBuB;","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","cookies","csrfToken","url","requestHeaders","cookieString","Object","entries","map","key","value","join","Cookie","controller","AbortController","timeoutId","setTimeout","abort","response","fetch","method","headers","signal","clearTimeout","ok","Error","status","statusText","PermissionDeniedException","cause","type","PermissionDeniedType","PERMISSION_CONFIG_QUERY_FAILED","message","HttpStatus","INTERNAL_SERVER_ERROR","data","json","roles","role_list","err","checkRoles","requirement","userContext","BAD_REQUEST","ability","createForUser","and","checkResults","role","can","ROLE_SUBJECT","hasRole","every","result","some","userRoles","warn","roleRequired","Injectable","Reflector","AuthZPaasGuard","reflector","permissionService","canActivate","context","http","switchToHttp","request","getRequest","userContext","checkRoleRequirement","getAllAndOverride","ROLES_KEY","getHandler","getClass","requirement","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"]}
|
|
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, HttpStatus, Inject } from '@nestjs/common';\nimport { Public } from '@lark-apaas/nestjs-authnpaas';\n\nimport type { UserPermissionData, PermissionApiConfig } from '../types';\nimport { 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';\nimport {\n PLATFORM_HTTP_CLIENT,\n PlatformHttpClient,\n} from '@lark-apaas/nestjs-common';\nimport { RequestContextService } from '@lark-apaas/nestjs-common';\n\n/**\n * 权限服务\n * 内置权限获取和缓存逻辑,以及权限检查逻辑\n */\n@Injectable()\n@Public() // 跳过 Guard 的鉴权检查,避免循环调用\nexport class PermissionService {\n\n constructor(\n @Inject(PERMISSION_API_CONFIG_TOKEN)\n private readonly apiConfig: PermissionApiConfig,\n private readonly abilityFactory: AbilityFactory,\n @Inject(PLATFORM_HTTP_CLIENT) private readonly client: PlatformHttpClient,\n private readonly requestContextService: RequestContextService\n ) {}\n\n /**\n * 获取用户权限数据\n */\n async getUserPermissions({\n laneId,\n }: {\n laneId?: string;\n }): Promise<UserPermissionData | null> {\n const permissionData = await this.fetchFromApi({ laneId });\n // 添加获取时间\n const dataWithTimestamp: UserPermissionData = {\n ...permissionData,\n fetchedAt: new Date(),\n };\n\n return dataWithTimestamp;\n }\n\n /**\n * 从 API 获取权限数据\n * 内置实现,用户无需配置\n */\n private async fetchFromApi({\n laneId,\n }: {\n laneId?: string;\n }): Promise<UserPermissionData> {\n const { timeout = 5000 } = this.apiConfig || {};\n // 从请求上下文获取 appId 和 userId,默认空字符串\n const { appId = '', userId = '' } =\n this.requestContextService.getContext() || {};\n\n if (!appId) {\n throw new PermissionDeniedException(\n {\n type: PermissionDeniedType.PERMISSION_CONFIG_QUERY_FAILED,\n message: 'appId is empty',\n },\n HttpStatus.BAD_REQUEST\n );\n }\n\n // 构建完整获取用户权限 URL\n const url = `/app/${appId}/inner/api/v1/permissions/roles`;\n\n // 构建请求头\n const requestHeaders: Record<string, string> = {\n 'Content-Type': 'application/json',\n // 透传 laneId 到权限服务\n ...(laneId ? { 'x-tt-env': laneId || '' } : {}),\n };\n\n // 发起请求(带超时控制)\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const response = await this.client.post(\n url,\n {\n userID: userId || '',\n },\n {\n credentials: 'include',\n headers: requestHeaders,\n signal: controller.signal,\n }\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 // 解析 JSON 响应体\n let data: { data?: { roleList?: string[] } };\n let responseText: string = '';\n\n try {\n responseText = await response.text();\n data = JSON.parse(responseText);\n } catch (jsonError) {\n // Handle JSON parse error\n const error = new Error(\n `Permission API returned invalid JSON: ${responseText}`\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 return {\n userId,\n roles: data.data?.roleList || [],\n // TODO: 基于权限点位设置能力\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 {\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 laneId 环境ID\n * @returns 用户权限检查结果,包含结果和详细信息\n * @throws PermissionDeniedException 当权限数据获取失败时\n */\n async checkRoles(\n requirement: RoleRequirement,\n laneId?: string\n ): Promise<{ result: boolean; details?: string }> {\n // 从请求上下文获取 userId,默认空字符串\n const { userId = '' } = this.requestContextService.getContext() || {};\n const permissionData = await this.getUserPermissions({ laneId });\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 } = requirement;\n if (!roles || roles.length === 0) {\n return { result: true };\n }\n\n // 使用 CASL 统一检查角色权限\n // 角色作为 action,'@role' 作为 subject\n const hasRole = roles.some(role => ability.can(role, ROLE_SUBJECT));\n if (!hasRole) {\n const userRoles = permissionData.roles;\n return {\n result: false,\n details: `用户 ${userId}, 用户角色 [${userRoles.join(', ')}], 需要 [${roles.join(', ')}]`\n };\n }\n return { result: true };\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, UnauthorizedException } 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 -> 在守卫内透出跳转 url\n if (needLoginMeta && !userId && loginUrl) {\n response.setHeader('x-login-url', loginUrl);\n throw new UnauthorizedException('未登录');\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/** 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/** 需要的权限元数据键 */\n// export const PERMISSIONS_KEY = 'authzpaas:permissions';\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 ): PermissionDeniedException {\n const message = `需要以下任一角色: ${requiredRoles.join(', ')}`;\n\n return new PermissionDeniedException({\n type: PermissionDeniedType.ROLE_REQUIRED,\n message,\n requiredRoles,\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 {\n Injectable,\n CanActivate,\n ExecutionContext,\n Logger,\n Inject,\n} 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';\nimport {\n OBSERVABLE_SERVICE,\n ObservableService,\n} from '@lark-apaas/nestjs-common';\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(OBSERVABLE_SERVICE) private obs: ObservableService\n ) {}\n private logger = new Logger(AuthZPaasGuard.name);\n\n /**\n * 验证角色要求是否有效\n * @param requirements 角色要求\n * @returns 是否有效\n */\n private isValidRoleRequirement(requirements: CheckRoleRequirement): boolean {\n return Boolean(\n requirements && requirements.roles && requirements.roles.length > 0\n );\n }\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const http = context.switchToHttp();\n const request = http.getRequest();\n // Extract laneId from request headers\n const laneId = request.headers['x-tt-env'];\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 (this.isValidRoleRequirement(checkRoleRequirement)) {\n const spanName = checkRoleRequirement.roles.join(' or ');\n return this.obs.trace(spanName, async span => {\n span.setAttributes({\n module: 'permission',\n });\n const startTime = Date.now();\n // 检查角色要求\n try {\n const checkResult = await this.permissionService.checkRoles(\n checkRoleRequirement,\n laneId\n );\n\n const endTime = Date.now();\n // 角色检查失败, 记录警告日志\n if (!checkResult.result) {\n this.logger.warn(\n JSON.stringify({\n role: spanName,\n duration_ms: endTime - startTime,\n result: checkResult.result ? 'has_auth' : 'no_auth',\n result_detail: checkResult.details,\n }),\n {\n source_type: 'platform',\n paas_attributes_module: 'permission',\n }\n );\n } else {\n // 角色检查成功记录成功日志\n this.logger.log(\n JSON.stringify({\n role: spanName,\n duration_ms: endTime - startTime,\n result: checkResult.result ? 'has_auth' : 'no_auth',\n }),\n {\n source_type: 'platform',\n paas_attributes_module: 'permission',\n }\n );\n }\n return checkResult.result;\n } catch (error: unknown) {\n const endTime = Date.now();\n // 记录错误日志\n this.logger.error(\n JSON.stringify({\n role: spanName,\n duration_ms: endTime - startTime,\n result: '',\n error_message: (error as Error).message,\n }),\n {\n source_type: 'platform',\n paas_attributes_module: 'permission',\n }\n );\n throw error;\n }\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}\n\nexport type CheckRoleRequirement = RoleRequirement;\n\n/**\n * 要求用户拥有指定角色\n *\n * @example\n * ```typescript\n * 单一角色\n * @CanRole(['admin'])\n * async deleteUser() {}\n *\n * 任一角色\n * @CanRole(['admin', 'editor'])\n * async criticalOperation() {}\n * ```\n */\nexport const CanRole = (role: string[] | string): MethodDecorator => {\n // 解析参数\n let requirement: RoleRequirement;\n\n if (!Array.isArray(role) && typeof role === 'string') {\n // 字符串形式\n requirement = {\n roles: [role],\n };\n } else if (\n Array.isArray(role) &&\n role.every(role => typeof role === 'string')\n ) {\n // 字符串列表形式\n requirement = {\n roles: role as string[],\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","HttpStatus","Inject","Module","APP_GUARD","Reflector","Injectable","UnauthorizedException","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","setHeader","UnauthorizedException","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","AUTHZPAAS_MODULE_OPTIONS","ROLES_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","join","permissionRequired","or","customMessage","length","perm","subject","actions","map","PLATFORM_HTTP_CLIENT","PlatformHttpClient","RequestContextService","PermissionService","apiConfig","abilityFactory","client","requestContextService","getUserPermissions","laneId","permissionData","fetchFromApi","dataWithTimestamp","fetchedAt","Date","timeout","appId","userId","getContext","PermissionDeniedException","type","PermissionDeniedType","PERMISSION_CONFIG_QUERY_FAILED","message","HttpStatus","BAD_REQUEST","url","requestHeaders","controller","AbortController","timeoutId","setTimeout","abort","response","post","userID","credentials","headers","signal","clearTimeout","ok","error","Error","status","statusText","cause","INTERNAL_SERVER_ERROR","data","responseText","text","JSON","parse","jsonError","roles","roleList","err","name","checkRoles","requirement","ability","createForUser","length","result","hasRole","some","role","can","ROLE_SUBJECT","userRoles","details","join","Injectable","Logger","Inject","Reflector","OBSERVABLE_SERVICE","ObservableService","AuthZPaasGuard","reflector","permissionService","obs","logger","Logger","name","isValidRoleRequirement","requirements","Boolean","roles","length","canActivate","context","http","switchToHttp","request","getRequest","laneId","headers","baseUrl","protocol","get","userContext","checkRoleRequirement","getAllAndOverride","ROLES_KEY","getHandler","getClass","spanName","join","trace","span","setAttributes","module","startTime","Date","now","checkResult","checkRoles","endTime","result","warn","JSON","stringify","role","duration_ms","result_detail","details","source_type","paas_attributes_module","log","error","error_message","message","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","requirement","Array","isArray","roles","every","Error","JSON","stringify","SetMetadata","ROLES_KEY"]}
|