@lark-apaas/nestjs-authzpaas 0.1.7 → 0.1.8
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 +601 -456
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +213 -183
- package/dist/index.d.ts +213 -183
- package/dist/index.js +597 -452
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -41,11 +41,11 @@ __export(index_exports, {
|
|
|
41
41
|
module.exports = __toCommonJS(index_exports);
|
|
42
42
|
|
|
43
43
|
// src/authzpaas.module.ts
|
|
44
|
-
var
|
|
44
|
+
var import_common7 = require("@nestjs/common");
|
|
45
45
|
var import_core2 = require("@nestjs/core");
|
|
46
46
|
|
|
47
47
|
// src/services/permission.service.ts
|
|
48
|
-
var
|
|
48
|
+
var import_common3 = require("@nestjs/common");
|
|
49
49
|
var import_nestjs_authnpaas = require("@lark-apaas/nestjs-authnpaas");
|
|
50
50
|
|
|
51
51
|
// src/const.ts
|
|
@@ -79,6 +79,11 @@ var AbilityFactory = class {
|
|
|
79
79
|
for (const role of permissionData.roles) {
|
|
80
80
|
can(role, ROLE_SUBJECT);
|
|
81
81
|
}
|
|
82
|
+
if (permissionData.permissions) {
|
|
83
|
+
for (const { action, subject } of permissionData.permissions) {
|
|
84
|
+
can(action, subject);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
82
87
|
return build();
|
|
83
88
|
}
|
|
84
89
|
};
|
|
@@ -157,41 +162,471 @@ var PermissionDeniedException = class _PermissionDeniedException extends import_
|
|
|
157
162
|
} else {
|
|
158
163
|
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(", ")}`;
|
|
159
164
|
}
|
|
160
|
-
return new _PermissionDeniedException({
|
|
161
|
-
type: "PERMISSION_REQUIRED",
|
|
162
|
-
message,
|
|
163
|
-
requiredPermissions,
|
|
164
|
-
metadata: {
|
|
165
|
-
or
|
|
166
|
-
}
|
|
167
|
-
});
|
|
165
|
+
return new _PermissionDeniedException({
|
|
166
|
+
type: "PERMISSION_REQUIRED",
|
|
167
|
+
message,
|
|
168
|
+
requiredPermissions,
|
|
169
|
+
metadata: {
|
|
170
|
+
or
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
// src/services/permission.service.ts
|
|
177
|
+
var import_nestjs_common = require("@lark-apaas/nestjs-common");
|
|
178
|
+
function _ts_decorate2(decorators, target, key, desc) {
|
|
179
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
180
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
181
|
+
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;
|
|
182
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
183
|
+
}
|
|
184
|
+
__name(_ts_decorate2, "_ts_decorate");
|
|
185
|
+
function _ts_metadata(k, v) {
|
|
186
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
187
|
+
}
|
|
188
|
+
__name(_ts_metadata, "_ts_metadata");
|
|
189
|
+
function _ts_param(paramIndex, decorator) {
|
|
190
|
+
return function(target, key) {
|
|
191
|
+
decorator(target, key, paramIndex);
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
__name(_ts_param, "_ts_param");
|
|
195
|
+
var PermissionService = class {
|
|
196
|
+
static {
|
|
197
|
+
__name(this, "PermissionService");
|
|
198
|
+
}
|
|
199
|
+
apiConfig;
|
|
200
|
+
abilityFactory;
|
|
201
|
+
client;
|
|
202
|
+
requestContextService;
|
|
203
|
+
permissionResolver;
|
|
204
|
+
constructor(apiConfig, abilityFactory, client, requestContextService, permissionResolver = null) {
|
|
205
|
+
this.apiConfig = apiConfig;
|
|
206
|
+
this.abilityFactory = abilityFactory;
|
|
207
|
+
this.client = client;
|
|
208
|
+
this.requestContextService = requestContextService;
|
|
209
|
+
this.permissionResolver = permissionResolver;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* 获取用户权限数据
|
|
213
|
+
*/
|
|
214
|
+
async getUserPermissions({ laneId }) {
|
|
215
|
+
const permissionData = await this.fetchFromApi({
|
|
216
|
+
laneId
|
|
217
|
+
});
|
|
218
|
+
const dataWithTimestamp = {
|
|
219
|
+
...permissionData,
|
|
220
|
+
fetchedAt: /* @__PURE__ */ new Date()
|
|
221
|
+
};
|
|
222
|
+
return dataWithTimestamp;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* 从内部 API 获取当前用户的权限数据
|
|
226
|
+
* 通过平台内部接口 /app/{appId}/inner/api/v1/permissions/roles 获取
|
|
227
|
+
*/
|
|
228
|
+
async fetchFromApi(options) {
|
|
229
|
+
const { timeout = 5e3 } = this.apiConfig || {};
|
|
230
|
+
const { appId = "", userId = "" } = this.requestContextService.getContext() || {};
|
|
231
|
+
if (!appId) {
|
|
232
|
+
throw new PermissionDeniedException({
|
|
233
|
+
type: PermissionDeniedType.PERMISSION_CONFIG_QUERY_FAILED,
|
|
234
|
+
message: "appId is empty"
|
|
235
|
+
}, import_common3.HttpStatus.BAD_REQUEST);
|
|
236
|
+
}
|
|
237
|
+
const url = `/app/${appId}/inner/api/v1/permissions/roles`;
|
|
238
|
+
const requestHeaders = {
|
|
239
|
+
"Content-Type": "application/json",
|
|
240
|
+
...options.laneId ? {
|
|
241
|
+
"x-tt-env": options.laneId
|
|
242
|
+
} : {}
|
|
243
|
+
};
|
|
244
|
+
const controller = new AbortController();
|
|
245
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
246
|
+
try {
|
|
247
|
+
const response = await this.client.post(url, {
|
|
248
|
+
userID: userId || ""
|
|
249
|
+
}, {
|
|
250
|
+
credentials: "include",
|
|
251
|
+
headers: requestHeaders,
|
|
252
|
+
signal: controller.signal
|
|
253
|
+
});
|
|
254
|
+
clearTimeout(timeoutId);
|
|
255
|
+
if (!response.ok) {
|
|
256
|
+
throw new Error(`Permission API returned ${response.status}: ${response.statusText}`);
|
|
257
|
+
}
|
|
258
|
+
let data;
|
|
259
|
+
try {
|
|
260
|
+
const responseText = await response.text();
|
|
261
|
+
data = JSON.parse(responseText);
|
|
262
|
+
} catch {
|
|
263
|
+
throw new Error("Permission API returned invalid JSON");
|
|
264
|
+
}
|
|
265
|
+
return {
|
|
266
|
+
userId,
|
|
267
|
+
roles: data.data?.roleList || [],
|
|
268
|
+
fetchedAt: /* @__PURE__ */ new Date()
|
|
269
|
+
};
|
|
270
|
+
} catch (error) {
|
|
271
|
+
clearTimeout(timeoutId);
|
|
272
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
273
|
+
if (err.name === "AbortError") {
|
|
274
|
+
throw new PermissionDeniedException({
|
|
275
|
+
cause: err,
|
|
276
|
+
type: PermissionDeniedType.PERMISSION_CONFIG_QUERY_FAILED,
|
|
277
|
+
message: `Permission API request timeout after ${timeout}ms`
|
|
278
|
+
}, import_common3.HttpStatus.INTERNAL_SERVER_ERROR);
|
|
279
|
+
}
|
|
280
|
+
throw new PermissionDeniedException({
|
|
281
|
+
cause: err,
|
|
282
|
+
type: PermissionDeniedType.PERMISSION_CONFIG_QUERY_FAILED,
|
|
283
|
+
message: err.message
|
|
284
|
+
}, import_common3.HttpStatus.INTERNAL_SERVER_ERROR);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
// /**
|
|
288
|
+
// * 获取用户的 Ability 实例(带缓存)
|
|
289
|
+
// * @param userId 用户ID
|
|
290
|
+
// * @returns CASL Ability 实例
|
|
291
|
+
// */
|
|
292
|
+
// private async getUserAbility(
|
|
293
|
+
// userId?: string,
|
|
294
|
+
// mockRoles?: string[]
|
|
295
|
+
// ): Promise<AppAbility> {
|
|
296
|
+
// // 计算缓存 key
|
|
297
|
+
// const key = this.buildCacheKey(userId, mockRoles);
|
|
298
|
+
// // 尝试从缓存获取
|
|
299
|
+
// const cached = this.cache.get(key);
|
|
300
|
+
// if (cached) {
|
|
301
|
+
// return cached.ability;
|
|
302
|
+
// }
|
|
303
|
+
// // 缓存未命中,调用 getUserPermissions 会创建并缓存
|
|
304
|
+
// await this.getUserPermissions(userId, mockRoles);
|
|
305
|
+
// // 再次从缓存获取(此时一定存在)
|
|
306
|
+
// const newCached = this.cache.get(key);
|
|
307
|
+
// return newCached!.ability;
|
|
308
|
+
// }
|
|
309
|
+
/**
|
|
310
|
+
* 检查角色要求
|
|
311
|
+
* 使用 CASL Ability 统一鉴权方式
|
|
312
|
+
* @param requirement 角色要求
|
|
313
|
+
* @param laneId 环境ID
|
|
314
|
+
* @returns 用户权限检查结果,包含结果和详细信息
|
|
315
|
+
* @throws PermissionDeniedException 当权限数据获取失败时
|
|
316
|
+
*/
|
|
317
|
+
async checkRoles(requirement, laneId, userContext) {
|
|
318
|
+
const { userId = "" } = this.requestContextService.getContext() || {};
|
|
319
|
+
let permissionData = null;
|
|
320
|
+
if (!userContext || !userContext.roles) {
|
|
321
|
+
permissionData = await this.getUserPermissions({
|
|
322
|
+
laneId
|
|
323
|
+
});
|
|
324
|
+
} else {
|
|
325
|
+
permissionData = {
|
|
326
|
+
userId,
|
|
327
|
+
roles: userContext.roles || [],
|
|
328
|
+
fetchedAt: /* @__PURE__ */ new Date()
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
if (!permissionData) {
|
|
332
|
+
throw new PermissionDeniedException({
|
|
333
|
+
cause: new Error("Permission data fetch api is not configured"),
|
|
334
|
+
type: PermissionDeniedType.PERMISSION_CONFIG_QUERY_FAILED,
|
|
335
|
+
message: "Permission data fetch api is not configured"
|
|
336
|
+
}, import_common3.HttpStatus.BAD_REQUEST);
|
|
337
|
+
}
|
|
338
|
+
const ability = this.abilityFactory.createForUser(permissionData);
|
|
339
|
+
const { roles } = requirement;
|
|
340
|
+
if (!roles || roles.length === 0) {
|
|
341
|
+
return {
|
|
342
|
+
result: true
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
const hasRole = roles.some((role) => ability.can(role, ROLE_SUBJECT));
|
|
346
|
+
if (!hasRole) {
|
|
347
|
+
const userRoles = permissionData.roles;
|
|
348
|
+
return {
|
|
349
|
+
result: false,
|
|
350
|
+
details: `\u7528\u6237 ${userId}, \u7528\u6237\u89D2\u8272 [${userRoles.join(", ")}], \u9700\u8981 [${roles.join(", ")}]`
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
return {
|
|
354
|
+
result: true
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* 检查权限点位要求
|
|
359
|
+
* 通过 IPermissionResolver 解析用户角色对应的权限点位,然后检查是否满足要求
|
|
360
|
+
* @param requirement 权限点位要求
|
|
361
|
+
* @param laneId 环境ID
|
|
362
|
+
* @param userContext 用户上下文
|
|
363
|
+
* @returns 用户权限检查结果,包含结果和详细信息
|
|
364
|
+
* @throws PermissionDeniedException 当权限解析器未注入或权限数据获取失败时
|
|
365
|
+
*/
|
|
366
|
+
async checkPermissions(requirement, laneId, userContext) {
|
|
367
|
+
if (!this.permissionResolver) {
|
|
368
|
+
throw new PermissionDeniedException({
|
|
369
|
+
cause: new Error("PermissionResolver is not registered. Provide permissionResolver in AuthZPaasModule options."),
|
|
370
|
+
type: PermissionDeniedType.PERMISSION_CONFIG_QUERY_FAILED,
|
|
371
|
+
message: "PermissionResolver is not registered. Provide permissionResolver in AuthZPaasModule options."
|
|
372
|
+
}, import_common3.HttpStatus.BAD_REQUEST);
|
|
373
|
+
}
|
|
374
|
+
const { userId = "" } = this.requestContextService.getContext() || {};
|
|
375
|
+
let permissionData = null;
|
|
376
|
+
if (!userContext || !userContext.roles) {
|
|
377
|
+
permissionData = await this.getUserPermissions({
|
|
378
|
+
laneId
|
|
379
|
+
});
|
|
380
|
+
} else {
|
|
381
|
+
permissionData = {
|
|
382
|
+
userId,
|
|
383
|
+
roles: userContext.roles || [],
|
|
384
|
+
fetchedAt: /* @__PURE__ */ new Date()
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
if (!permissionData) {
|
|
388
|
+
throw new PermissionDeniedException({
|
|
389
|
+
cause: new Error("Permission data fetch api is not configured"),
|
|
390
|
+
type: PermissionDeniedType.PERMISSION_CONFIG_QUERY_FAILED,
|
|
391
|
+
message: "Permission data fetch api is not configured"
|
|
392
|
+
}, import_common3.HttpStatus.BAD_REQUEST);
|
|
393
|
+
}
|
|
394
|
+
const { action, subject } = requirement;
|
|
395
|
+
if (!action || !subject) {
|
|
396
|
+
return {
|
|
397
|
+
result: true
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
const userPermissions = await this.permissionResolver.resolvePermissions(permissionData.roles);
|
|
401
|
+
permissionData.permissions = userPermissions;
|
|
402
|
+
const ability = this.abilityFactory.createForUser(permissionData);
|
|
403
|
+
const hasPermission = ability.can(action, subject);
|
|
404
|
+
if (!hasPermission) {
|
|
405
|
+
return {
|
|
406
|
+
result: false,
|
|
407
|
+
details: `\u7528\u6237 ${userId}, \u9700\u8981 can('${action}', '${subject}'), \u7528\u6237\u6743\u9650\u70B9\u4F4D [${userPermissions.map((p) => `${p.action}:${p.subject}`).join(", ")}]`
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
return {
|
|
411
|
+
result: true
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
PermissionService = _ts_decorate2([
|
|
416
|
+
(0, import_common3.Injectable)(),
|
|
417
|
+
(0, import_nestjs_authnpaas.Public)(),
|
|
418
|
+
_ts_param(0, (0, import_common3.Inject)(PERMISSION_API_CONFIG_TOKEN)),
|
|
419
|
+
_ts_param(2, (0, import_common3.Inject)(import_nestjs_common.PLATFORM_HTTP_CLIENT)),
|
|
420
|
+
_ts_param(4, (0, import_common3.Optional)()),
|
|
421
|
+
_ts_param(4, (0, import_common3.Inject)(PERMISSION_RESOLVER_TOKEN)),
|
|
422
|
+
_ts_metadata("design:type", Function),
|
|
423
|
+
_ts_metadata("design:paramtypes", [
|
|
424
|
+
typeof PermissionApiConfig === "undefined" ? Object : PermissionApiConfig,
|
|
425
|
+
typeof AbilityFactory === "undefined" ? Object : AbilityFactory,
|
|
426
|
+
typeof import_nestjs_common.PlatformHttpClient === "undefined" ? Object : import_nestjs_common.PlatformHttpClient,
|
|
427
|
+
typeof import_nestjs_common.RequestContextService === "undefined" ? Object : import_nestjs_common.RequestContextService,
|
|
428
|
+
Object
|
|
429
|
+
])
|
|
430
|
+
], PermissionService);
|
|
431
|
+
|
|
432
|
+
// src/guards/authzpaas.guard.ts
|
|
433
|
+
var import_common4 = require("@nestjs/common");
|
|
434
|
+
var import_core = require("@nestjs/core");
|
|
435
|
+
var import_nestjs_common2 = require("@lark-apaas/nestjs-common");
|
|
436
|
+
function _ts_decorate3(decorators, target, key, desc) {
|
|
437
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
438
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
439
|
+
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;
|
|
440
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
441
|
+
}
|
|
442
|
+
__name(_ts_decorate3, "_ts_decorate");
|
|
443
|
+
function _ts_metadata2(k, v) {
|
|
444
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
445
|
+
}
|
|
446
|
+
__name(_ts_metadata2, "_ts_metadata");
|
|
447
|
+
function _ts_param2(paramIndex, decorator) {
|
|
448
|
+
return function(target, key) {
|
|
449
|
+
decorator(target, key, paramIndex);
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
__name(_ts_param2, "_ts_param");
|
|
453
|
+
var AuthZPaasGuard = class _AuthZPaasGuard {
|
|
454
|
+
static {
|
|
455
|
+
__name(this, "AuthZPaasGuard");
|
|
456
|
+
}
|
|
457
|
+
reflector;
|
|
458
|
+
permissionService;
|
|
459
|
+
obs;
|
|
460
|
+
constructor(reflector, permissionService, obs) {
|
|
461
|
+
this.reflector = reflector;
|
|
462
|
+
this.permissionService = permissionService;
|
|
463
|
+
this.obs = obs;
|
|
464
|
+
}
|
|
465
|
+
logger = new import_common4.Logger(_AuthZPaasGuard.name);
|
|
466
|
+
/**
|
|
467
|
+
* 验证权限点位要求是否有效
|
|
468
|
+
* @param requirements 权限点位要求
|
|
469
|
+
* @returns 是否有效
|
|
470
|
+
*/
|
|
471
|
+
isValidPermissionRequirement(requirements) {
|
|
472
|
+
return Boolean(requirements && requirements.action && requirements.subject);
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* 验证角色要求是否有效
|
|
476
|
+
* @param requirements 角色要求
|
|
477
|
+
* @returns 是否有效
|
|
478
|
+
*/
|
|
479
|
+
isValidRoleRequirement(requirements) {
|
|
480
|
+
return Boolean(requirements && requirements.roles && requirements.roles.length > 0);
|
|
481
|
+
}
|
|
482
|
+
async canActivate(context) {
|
|
483
|
+
const http = context.switchToHttp();
|
|
484
|
+
const request = http.getRequest();
|
|
485
|
+
const laneId = request.headers["x-tt-env"];
|
|
486
|
+
const baseUrl = `${request.protocol}://${request.get("host")}`;
|
|
487
|
+
const userContext = request.userContext;
|
|
488
|
+
userContext.baseUrl = baseUrl;
|
|
489
|
+
const checkRoleRequirement = this.reflector.getAllAndOverride(ROLES_KEY, [
|
|
490
|
+
context.getHandler(),
|
|
491
|
+
context.getClass()
|
|
492
|
+
]);
|
|
493
|
+
if (this.isValidRoleRequirement(checkRoleRequirement)) {
|
|
494
|
+
const spanName = checkRoleRequirement.roles.join(" or ");
|
|
495
|
+
const logAttrs = {
|
|
496
|
+
source_type: "platform",
|
|
497
|
+
paas_attributes_module: "permission"
|
|
498
|
+
};
|
|
499
|
+
return this.obs.trace(spanName, async (span) => {
|
|
500
|
+
span.setAttributes({
|
|
501
|
+
module: "permission"
|
|
502
|
+
});
|
|
503
|
+
const elapsed = (0, import_nestjs_common2.createTimer)();
|
|
504
|
+
try {
|
|
505
|
+
const checkResult = await this.permissionService.checkRoles(checkRoleRequirement, laneId, userContext);
|
|
506
|
+
const durationMs = elapsed();
|
|
507
|
+
span.setAttribute("duration_ms", durationMs);
|
|
508
|
+
if (!checkResult.result) {
|
|
509
|
+
this.logger.warn(JSON.stringify({
|
|
510
|
+
role: spanName,
|
|
511
|
+
duration_ms: durationMs,
|
|
512
|
+
result: "no_auth",
|
|
513
|
+
result_detail: checkResult.details
|
|
514
|
+
}), {
|
|
515
|
+
...logAttrs,
|
|
516
|
+
duration_ms: durationMs
|
|
517
|
+
});
|
|
518
|
+
} else {
|
|
519
|
+
this.logger.log(JSON.stringify({
|
|
520
|
+
role: spanName,
|
|
521
|
+
duration_ms: durationMs,
|
|
522
|
+
result: "has_auth"
|
|
523
|
+
}), {
|
|
524
|
+
...logAttrs,
|
|
525
|
+
duration_ms: durationMs
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
return checkResult.result;
|
|
529
|
+
} catch (error) {
|
|
530
|
+
const durationMs = elapsed();
|
|
531
|
+
span.setAttribute("duration_ms", durationMs);
|
|
532
|
+
this.logger.error(JSON.stringify({
|
|
533
|
+
role: spanName,
|
|
534
|
+
duration_ms: durationMs,
|
|
535
|
+
result: "",
|
|
536
|
+
error_message: error.message
|
|
537
|
+
}), {
|
|
538
|
+
...logAttrs,
|
|
539
|
+
duration_ms: durationMs
|
|
540
|
+
});
|
|
541
|
+
throw error;
|
|
542
|
+
}
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
const permissionRequirement = this.reflector.getAllAndOverride(PERMISSIONS_KEY, [
|
|
546
|
+
context.getHandler(),
|
|
547
|
+
context.getClass()
|
|
548
|
+
]);
|
|
549
|
+
if (this.isValidPermissionRequirement(permissionRequirement)) {
|
|
550
|
+
const spanName = `${permissionRequirement.action}:${permissionRequirement.subject}`;
|
|
551
|
+
return this.obs.trace(spanName, async (span) => {
|
|
552
|
+
span.setAttributes({
|
|
553
|
+
module: "permission"
|
|
554
|
+
});
|
|
555
|
+
const startTime = Date.now();
|
|
556
|
+
try {
|
|
557
|
+
const checkResult = await this.permissionService.checkPermissions(permissionRequirement, laneId, userContext);
|
|
558
|
+
const endTime = Date.now();
|
|
559
|
+
if (!checkResult.result) {
|
|
560
|
+
this.logger.warn(JSON.stringify({
|
|
561
|
+
permission: spanName,
|
|
562
|
+
duration_ms: endTime - startTime,
|
|
563
|
+
result: "no_auth",
|
|
564
|
+
result_detail: checkResult.details
|
|
565
|
+
}), {
|
|
566
|
+
source_type: "platform",
|
|
567
|
+
paas_attributes_module: "permission"
|
|
568
|
+
});
|
|
569
|
+
} else {
|
|
570
|
+
this.logger.log(JSON.stringify({
|
|
571
|
+
permission: spanName,
|
|
572
|
+
duration_ms: endTime - startTime,
|
|
573
|
+
result: "has_auth"
|
|
574
|
+
}), {
|
|
575
|
+
source_type: "platform",
|
|
576
|
+
paas_attributes_module: "permission"
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
return checkResult.result;
|
|
580
|
+
} catch (error) {
|
|
581
|
+
const endTime = Date.now();
|
|
582
|
+
this.logger.error(JSON.stringify({
|
|
583
|
+
permission: spanName,
|
|
584
|
+
duration_ms: endTime - startTime,
|
|
585
|
+
result: "",
|
|
586
|
+
error_message: error.message
|
|
587
|
+
}), {
|
|
588
|
+
source_type: "platform",
|
|
589
|
+
paas_attributes_module: "permission"
|
|
590
|
+
});
|
|
591
|
+
throw error;
|
|
592
|
+
}
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
return true;
|
|
168
596
|
}
|
|
169
597
|
};
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
598
|
+
AuthZPaasGuard = _ts_decorate3([
|
|
599
|
+
(0, import_common4.Injectable)(),
|
|
600
|
+
_ts_param2(2, (0, import_common4.Inject)(import_nestjs_common2.OBSERVABLE_SERVICE)),
|
|
601
|
+
_ts_metadata2("design:type", Function),
|
|
602
|
+
_ts_metadata2("design:paramtypes", [
|
|
603
|
+
typeof import_core.Reflector === "undefined" ? Object : import_core.Reflector,
|
|
604
|
+
typeof PermissionService === "undefined" ? Object : PermissionService,
|
|
605
|
+
typeof import_nestjs_common2.ObservableService === "undefined" ? Object : import_nestjs_common2.ObservableService
|
|
606
|
+
])
|
|
607
|
+
], AuthZPaasGuard);
|
|
173
608
|
|
|
174
609
|
// src/sdk/authorization.sdk.ts
|
|
175
|
-
var
|
|
176
|
-
var
|
|
177
|
-
var
|
|
178
|
-
function
|
|
610
|
+
var import_common5 = require("@nestjs/common");
|
|
611
|
+
var import_nestjs_common3 = require("@lark-apaas/nestjs-common");
|
|
612
|
+
var import_nestjs_common4 = require("@lark-apaas/nestjs-common");
|
|
613
|
+
function _ts_decorate4(decorators, target, key, desc) {
|
|
179
614
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
180
615
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
181
616
|
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;
|
|
182
617
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
183
618
|
}
|
|
184
|
-
__name(
|
|
185
|
-
function
|
|
619
|
+
__name(_ts_decorate4, "_ts_decorate");
|
|
620
|
+
function _ts_metadata3(k, v) {
|
|
186
621
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
187
622
|
}
|
|
188
|
-
__name(
|
|
189
|
-
function
|
|
623
|
+
__name(_ts_metadata3, "_ts_metadata");
|
|
624
|
+
function _ts_param3(paramIndex, decorator) {
|
|
190
625
|
return function(target, key) {
|
|
191
626
|
decorator(target, key, paramIndex);
|
|
192
627
|
};
|
|
193
628
|
}
|
|
194
|
-
__name(
|
|
629
|
+
__name(_ts_param3, "_ts_param");
|
|
195
630
|
var SDK_MODULE = "permission";
|
|
196
631
|
var LANG_CODE_MAP = {
|
|
197
632
|
2052: "zh_cn",
|
|
@@ -218,9 +653,25 @@ function normalizeDeptList(list) {
|
|
|
218
653
|
}
|
|
219
654
|
__name(normalizeDeptList, "normalizeDeptList");
|
|
220
655
|
function normalizeRoleMembers(members) {
|
|
221
|
-
if (!members) return
|
|
656
|
+
if (!members) return {
|
|
657
|
+
userList: [],
|
|
658
|
+
departmentList: [],
|
|
659
|
+
groupChatList: [],
|
|
660
|
+
allEmployees: false,
|
|
661
|
+
public: false,
|
|
662
|
+
presetGroup: {
|
|
663
|
+
isContainsAdmin: false
|
|
664
|
+
}
|
|
665
|
+
};
|
|
222
666
|
return {
|
|
223
667
|
...members,
|
|
668
|
+
userList: members.userList || [],
|
|
669
|
+
groupChatList: members.groupChatList || [],
|
|
670
|
+
allEmployees: members.allEmployees ?? false,
|
|
671
|
+
public: members.public ?? false,
|
|
672
|
+
presetGroup: members.presetGroup ?? {
|
|
673
|
+
isContainsAdmin: false
|
|
674
|
+
},
|
|
224
675
|
departmentList: normalizeDeptList(members.departmentList)
|
|
225
676
|
};
|
|
226
677
|
}
|
|
@@ -263,7 +714,7 @@ function normalizeSearchResponse(res) {
|
|
|
263
714
|
}
|
|
264
715
|
__name(normalizeSearchResponse, "normalizeSearchResponse");
|
|
265
716
|
function throwPlatformError(message, httpStatus, platformCode) {
|
|
266
|
-
throw new
|
|
717
|
+
throw new import_common5.HttpException({
|
|
267
718
|
statusCode: httpStatus,
|
|
268
719
|
code: "PLATFORM_API_ERROR",
|
|
269
720
|
message,
|
|
@@ -280,7 +731,7 @@ async function parseResponseWithRaw(response) {
|
|
|
280
731
|
} catch {
|
|
281
732
|
}
|
|
282
733
|
const message = err?.error_msg || err?.msg || `\u5E73\u53F0 API \u8BF7\u6C42\u5931\u8D25 (${response.status})`;
|
|
283
|
-
const httpStatus = response.status >= 500 ?
|
|
734
|
+
const httpStatus = response.status >= 500 ? import_common5.HttpStatus.BAD_GATEWAY : response.status;
|
|
284
735
|
throwPlatformError(message, httpStatus, err?.status_code ?? err?.code);
|
|
285
736
|
}
|
|
286
737
|
if (!text) return {
|
|
@@ -290,7 +741,7 @@ async function parseResponseWithRaw(response) {
|
|
|
290
741
|
const json = JSON.parse(text);
|
|
291
742
|
if (json.status_code && json.status_code !== "0") {
|
|
292
743
|
const message = json.error_msg || `\u5E73\u53F0\u4E1A\u52A1\u9519\u8BEF (${json.status_code})`;
|
|
293
|
-
throwPlatformError(message,
|
|
744
|
+
throwPlatformError(message, import_common5.HttpStatus.BAD_GATEWAY, json.status_code);
|
|
294
745
|
}
|
|
295
746
|
return {
|
|
296
747
|
data: json.data ?? json,
|
|
@@ -314,7 +765,7 @@ function traced(obs, logger, method, ctx, fn) {
|
|
|
314
765
|
};
|
|
315
766
|
return obs.trace(spanName, async (span) => {
|
|
316
767
|
span.setAttributes(spanAttrs);
|
|
317
|
-
const elapsed = (0,
|
|
768
|
+
const elapsed = (0, import_nestjs_common3.createTimer)();
|
|
318
769
|
try {
|
|
319
770
|
const { data, raw, logId } = await fn();
|
|
320
771
|
const durationMs = elapsed();
|
|
@@ -341,7 +792,7 @@ function traced(obs, logger, method, ctx, fn) {
|
|
|
341
792
|
} catch (error) {
|
|
342
793
|
const durationMs = elapsed();
|
|
343
794
|
const errMsg = error instanceof Error ? error.message : String(error);
|
|
344
|
-
const errorLogId = error instanceof
|
|
795
|
+
const errorLogId = error instanceof import_common5.HttpException ? error.getResponse()?.["x-tt-logid"] ?? "" : "";
|
|
345
796
|
span.setAttributes({
|
|
346
797
|
duration_ms: durationMs,
|
|
347
798
|
success: false,
|
|
@@ -377,7 +828,7 @@ async function requestWithTrace(response) {
|
|
|
377
828
|
logId
|
|
378
829
|
};
|
|
379
830
|
} catch (error) {
|
|
380
|
-
if (error instanceof
|
|
831
|
+
if (error instanceof import_common5.HttpException && logId) {
|
|
381
832
|
const res = error.getResponse();
|
|
382
833
|
res["x-tt-logid"] = logId;
|
|
383
834
|
}
|
|
@@ -421,7 +872,7 @@ var RolesAPI = class RolesAPI2 {
|
|
|
421
872
|
const { data, raw, logId } = await requestWithTrace(response);
|
|
422
873
|
const roles = (data.roles || []).map((r) => ({
|
|
423
874
|
...r,
|
|
424
|
-
roleMembers: normalizeRoleMembers(r.roleMembers)
|
|
875
|
+
roleMembers: needMember ? normalizeRoleMembers(r.roleMembers) : void 0
|
|
425
876
|
}));
|
|
426
877
|
return {
|
|
427
878
|
data: roles,
|
|
@@ -568,6 +1019,21 @@ var MembersAPI = class MembersAPI2 {
|
|
|
568
1019
|
const result = await requestWithTrace(response);
|
|
569
1020
|
if (result.data?.members) {
|
|
570
1021
|
result.data.members = normalizeRoleMembers(result.data.members);
|
|
1022
|
+
} else {
|
|
1023
|
+
result.data = {
|
|
1024
|
+
members: {
|
|
1025
|
+
userList: [],
|
|
1026
|
+
departmentList: [],
|
|
1027
|
+
groupChatList: [],
|
|
1028
|
+
allEmployees: false,
|
|
1029
|
+
public: false,
|
|
1030
|
+
presetGroup: {
|
|
1031
|
+
isContainsAdmin: false
|
|
1032
|
+
}
|
|
1033
|
+
},
|
|
1034
|
+
total: 0,
|
|
1035
|
+
hasMore: false
|
|
1036
|
+
};
|
|
571
1037
|
}
|
|
572
1038
|
return result;
|
|
573
1039
|
});
|
|
@@ -648,454 +1114,131 @@ var SearchAPI = class SearchAPI2 {
|
|
|
648
1114
|
enableChatSearch: true,
|
|
649
1115
|
include_external_user: params.includeExternalUser ?? true,
|
|
650
1116
|
include_external_group: params.includeExternalGroup ?? true,
|
|
651
|
-
pageSize: params.pageSize ?? 20,
|
|
652
|
-
page: params.page ?? 1
|
|
653
|
-
},
|
|
654
|
-
userID: params.userID ?? (this.getUserId() || void 0)
|
|
655
|
-
};
|
|
656
|
-
return traced(this.obs, this.logger, "search.search", {
|
|
657
|
-
httpMethod: "POST",
|
|
658
|
-
path: this.basePath,
|
|
659
|
-
params: body
|
|
660
|
-
}, async () => {
|
|
661
|
-
const response = await this.client.post(this.basePath, body);
|
|
662
|
-
const result = await requestWithTrace(response);
|
|
663
|
-
result.data = normalizeSearchResponse(result?.data);
|
|
664
|
-
return result;
|
|
665
|
-
});
|
|
666
|
-
}
|
|
667
|
-
};
|
|
668
|
-
var AuthorizationSDK = class _AuthorizationSDK {
|
|
669
|
-
static {
|
|
670
|
-
__name(this, "AuthorizationSDK");
|
|
671
|
-
}
|
|
672
|
-
requestContextService;
|
|
673
|
-
roles;
|
|
674
|
-
members;
|
|
675
|
-
search;
|
|
676
|
-
logger = new import_common3.Logger(_AuthorizationSDK.name);
|
|
677
|
-
constructor(client, obs, requestContextService) {
|
|
678
|
-
this.requestContextService = requestContextService;
|
|
679
|
-
const getAppId = /* @__PURE__ */ __name(() => {
|
|
680
|
-
const { appId = "" } = this.requestContextService.getContext() || {};
|
|
681
|
-
return appId;
|
|
682
|
-
}, "getAppId");
|
|
683
|
-
const getUserId = /* @__PURE__ */ __name(() => {
|
|
684
|
-
const { userId = "" } = this.requestContextService.getContext() || {};
|
|
685
|
-
return userId;
|
|
686
|
-
}, "getUserId");
|
|
687
|
-
this.roles = new RolesAPI(client, getAppId, getUserId, obs, this.logger);
|
|
688
|
-
this.members = new MembersAPI(client, getAppId, getUserId, obs, this.logger);
|
|
689
|
-
this.search = new SearchAPI(client, getAppId, getUserId, obs, this.logger);
|
|
690
|
-
}
|
|
691
|
-
};
|
|
692
|
-
AuthorizationSDK = _ts_decorate2([
|
|
693
|
-
(0, import_common3.Injectable)(),
|
|
694
|
-
_ts_param(0, (0, import_common3.Inject)(import_nestjs_common.PLATFORM_HTTP_CLIENT)),
|
|
695
|
-
_ts_param(1, (0, import_common3.Inject)(import_nestjs_common.OBSERVABLE_SERVICE)),
|
|
696
|
-
_ts_metadata("design:type", Function),
|
|
697
|
-
_ts_metadata("design:paramtypes", [
|
|
698
|
-
typeof import_nestjs_common.PlatformHttpClient === "undefined" ? Object : import_nestjs_common.PlatformHttpClient,
|
|
699
|
-
typeof ObservableService === "undefined" ? Object : ObservableService,
|
|
700
|
-
typeof import_nestjs_common2.RequestContextService === "undefined" ? Object : import_nestjs_common2.RequestContextService
|
|
701
|
-
])
|
|
702
|
-
], AuthorizationSDK);
|
|
703
|
-
|
|
704
|
-
// src/services/permission.service.ts
|
|
705
|
-
function _ts_decorate3(decorators, target, key, desc) {
|
|
706
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
707
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
708
|
-
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;
|
|
709
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
710
|
-
}
|
|
711
|
-
__name(_ts_decorate3, "_ts_decorate");
|
|
712
|
-
function _ts_metadata2(k, v) {
|
|
713
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
714
|
-
}
|
|
715
|
-
__name(_ts_metadata2, "_ts_metadata");
|
|
716
|
-
function _ts_param2(paramIndex, decorator) {
|
|
717
|
-
return function(target, key) {
|
|
718
|
-
decorator(target, key, paramIndex);
|
|
719
|
-
};
|
|
720
|
-
}
|
|
721
|
-
__name(_ts_param2, "_ts_param");
|
|
722
|
-
var PermissionService = class {
|
|
723
|
-
static {
|
|
724
|
-
__name(this, "PermissionService");
|
|
725
|
-
}
|
|
726
|
-
abilityFactory;
|
|
727
|
-
authorizationSDK;
|
|
728
|
-
requestContextService;
|
|
729
|
-
permissionResolver;
|
|
730
|
-
constructor(abilityFactory, authorizationSDK, requestContextService, permissionResolver = null) {
|
|
731
|
-
this.abilityFactory = abilityFactory;
|
|
732
|
-
this.authorizationSDK = authorizationSDK;
|
|
733
|
-
this.requestContextService = requestContextService;
|
|
734
|
-
this.permissionResolver = permissionResolver;
|
|
735
|
-
}
|
|
736
|
-
/**
|
|
737
|
-
* 获取用户权限数据
|
|
738
|
-
*/
|
|
739
|
-
async getUserPermissions({ laneId }) {
|
|
740
|
-
const permissionData = await this.fetchFromApi({
|
|
741
|
-
laneId
|
|
742
|
-
});
|
|
743
|
-
const dataWithTimestamp = {
|
|
744
|
-
...permissionData,
|
|
745
|
-
fetchedAt: /* @__PURE__ */ new Date()
|
|
746
|
-
};
|
|
747
|
-
return dataWithTimestamp;
|
|
748
|
-
}
|
|
749
|
-
/**
|
|
750
|
-
* 从 AuthorizationSDK 获取权限数据
|
|
751
|
-
* 通过 roles.list() 获取用户关联的角色列表
|
|
752
|
-
*/
|
|
753
|
-
async fetchFromApi(_options) {
|
|
754
|
-
const { userId = "" } = this.requestContextService.getContext() || {};
|
|
755
|
-
try {
|
|
756
|
-
const roles = await this.authorizationSDK.roles.list({
|
|
757
|
-
userID: userId || void 0
|
|
758
|
-
});
|
|
759
|
-
return {
|
|
760
|
-
userId,
|
|
761
|
-
roles: roles.map((role) => role.bizID).filter((id) => !!id),
|
|
762
|
-
fetchedAt: /* @__PURE__ */ new Date()
|
|
763
|
-
};
|
|
764
|
-
} catch (error) {
|
|
765
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
766
|
-
throw new PermissionDeniedException({
|
|
767
|
-
cause: err,
|
|
768
|
-
type: PermissionDeniedType.PERMISSION_CONFIG_QUERY_FAILED,
|
|
769
|
-
message: err.message
|
|
770
|
-
}, import_common4.HttpStatus.INTERNAL_SERVER_ERROR);
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
|
-
// /**
|
|
774
|
-
// * 获取用户的 Ability 实例(带缓存)
|
|
775
|
-
// * @param userId 用户ID
|
|
776
|
-
// * @returns CASL Ability 实例
|
|
777
|
-
// */
|
|
778
|
-
// private async getUserAbility(
|
|
779
|
-
// userId?: string,
|
|
780
|
-
// mockRoles?: string[]
|
|
781
|
-
// ): Promise<AppAbility> {
|
|
782
|
-
// // 计算缓存 key
|
|
783
|
-
// const key = this.buildCacheKey(userId, mockRoles);
|
|
784
|
-
// // 尝试从缓存获取
|
|
785
|
-
// const cached = this.cache.get(key);
|
|
786
|
-
// if (cached) {
|
|
787
|
-
// return cached.ability;
|
|
788
|
-
// }
|
|
789
|
-
// // 缓存未命中,调用 getUserPermissions 会创建并缓存
|
|
790
|
-
// await this.getUserPermissions(userId, mockRoles);
|
|
791
|
-
// // 再次从缓存获取(此时一定存在)
|
|
792
|
-
// const newCached = this.cache.get(key);
|
|
793
|
-
// return newCached!.ability;
|
|
794
|
-
// }
|
|
795
|
-
/**
|
|
796
|
-
* 检查角色要求
|
|
797
|
-
* 使用 CASL Ability 统一鉴权方式
|
|
798
|
-
* @param requirement 角色要求
|
|
799
|
-
* @param laneId 环境ID
|
|
800
|
-
* @returns 用户权限检查结果,包含结果和详细信息
|
|
801
|
-
* @throws PermissionDeniedException 当权限数据获取失败时
|
|
802
|
-
*/
|
|
803
|
-
async checkRoles(requirement, laneId, userContext) {
|
|
804
|
-
const { userId = "" } = this.requestContextService.getContext() || {};
|
|
805
|
-
let permissionData = null;
|
|
806
|
-
if (!userContext || !userContext.roles) {
|
|
807
|
-
permissionData = await this.getUserPermissions({
|
|
808
|
-
laneId
|
|
809
|
-
});
|
|
810
|
-
} else {
|
|
811
|
-
permissionData = {
|
|
812
|
-
userId,
|
|
813
|
-
roles: userContext.roles || [],
|
|
814
|
-
fetchedAt: /* @__PURE__ */ new Date()
|
|
815
|
-
};
|
|
816
|
-
}
|
|
817
|
-
if (!permissionData) {
|
|
818
|
-
throw new PermissionDeniedException({
|
|
819
|
-
cause: new Error("Permission data fetch api is not configured"),
|
|
820
|
-
type: PermissionDeniedType.PERMISSION_CONFIG_QUERY_FAILED,
|
|
821
|
-
message: "Permission data fetch api is not configured"
|
|
822
|
-
}, import_common4.HttpStatus.BAD_REQUEST);
|
|
823
|
-
}
|
|
824
|
-
const ability = this.abilityFactory.createForUser(permissionData);
|
|
825
|
-
const { roles } = requirement;
|
|
826
|
-
if (!roles || roles.length === 0) {
|
|
827
|
-
return {
|
|
828
|
-
result: true
|
|
829
|
-
};
|
|
830
|
-
}
|
|
831
|
-
const hasRole = roles.some((role) => ability.can(role, ROLE_SUBJECT));
|
|
832
|
-
if (!hasRole) {
|
|
833
|
-
const userRoles = permissionData.roles;
|
|
834
|
-
return {
|
|
835
|
-
result: false,
|
|
836
|
-
details: `\u7528\u6237 ${userId}, \u7528\u6237\u89D2\u8272 [${userRoles.join(", ")}], \u9700\u8981 [${roles.join(", ")}]`
|
|
837
|
-
};
|
|
838
|
-
}
|
|
839
|
-
return {
|
|
840
|
-
result: true
|
|
1117
|
+
pageSize: params.pageSize ?? 20,
|
|
1118
|
+
page: params.page ?? 1
|
|
1119
|
+
},
|
|
1120
|
+
userID: params.userID ?? (this.getUserId() || void 0)
|
|
841
1121
|
};
|
|
1122
|
+
return traced(this.obs, this.logger, "search.search", {
|
|
1123
|
+
httpMethod: "POST",
|
|
1124
|
+
path: this.basePath,
|
|
1125
|
+
params: body
|
|
1126
|
+
}, async () => {
|
|
1127
|
+
const response = await this.client.post(this.basePath, body);
|
|
1128
|
+
const result = await requestWithTrace(response);
|
|
1129
|
+
result.data = normalizeSearchResponse(result?.data);
|
|
1130
|
+
return result;
|
|
1131
|
+
});
|
|
842
1132
|
}
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
} else {
|
|
867
|
-
permissionData = {
|
|
868
|
-
userId,
|
|
869
|
-
roles: userContext.roles || [],
|
|
870
|
-
fetchedAt: /* @__PURE__ */ new Date()
|
|
871
|
-
};
|
|
872
|
-
}
|
|
873
|
-
if (!permissionData) {
|
|
874
|
-
throw new PermissionDeniedException({
|
|
875
|
-
cause: new Error("Permission data fetch api is not configured"),
|
|
876
|
-
type: PermissionDeniedType.PERMISSION_CONFIG_QUERY_FAILED,
|
|
877
|
-
message: "Permission data fetch api is not configured"
|
|
878
|
-
}, import_common4.HttpStatus.BAD_REQUEST);
|
|
879
|
-
}
|
|
880
|
-
const { permissions } = requirement;
|
|
881
|
-
if (!permissions || permissions.length === 0) {
|
|
882
|
-
return {
|
|
883
|
-
result: true
|
|
884
|
-
};
|
|
885
|
-
}
|
|
886
|
-
const userPermissions = await this.permissionResolver.resolvePermissions(permissionData.roles);
|
|
887
|
-
const hasAllPermissions = permissions.every((p) => userPermissions.includes(p));
|
|
888
|
-
if (!hasAllPermissions) {
|
|
889
|
-
const missingPermissions = permissions.filter((p) => !userPermissions.includes(p));
|
|
890
|
-
return {
|
|
891
|
-
result: false,
|
|
892
|
-
details: `\u7528\u6237 ${userId}, \u7528\u6237\u6743\u9650 [${userPermissions.join(", ")}], \u7F3A\u5C11 [${missingPermissions.join(", ")}]`
|
|
893
|
-
};
|
|
894
|
-
}
|
|
895
|
-
return {
|
|
896
|
-
result: true
|
|
897
|
-
};
|
|
1133
|
+
};
|
|
1134
|
+
var AuthorizationSDK = class _AuthorizationSDK {
|
|
1135
|
+
static {
|
|
1136
|
+
__name(this, "AuthorizationSDK");
|
|
1137
|
+
}
|
|
1138
|
+
requestContextService;
|
|
1139
|
+
roles;
|
|
1140
|
+
members;
|
|
1141
|
+
search;
|
|
1142
|
+
logger = new import_common5.Logger(_AuthorizationSDK.name);
|
|
1143
|
+
constructor(client, obs, requestContextService) {
|
|
1144
|
+
this.requestContextService = requestContextService;
|
|
1145
|
+
const getAppId = /* @__PURE__ */ __name(() => {
|
|
1146
|
+
const { appId = "" } = this.requestContextService.getContext() || {};
|
|
1147
|
+
return appId;
|
|
1148
|
+
}, "getAppId");
|
|
1149
|
+
const getUserId = /* @__PURE__ */ __name(() => {
|
|
1150
|
+
const { userId = "" } = this.requestContextService.getContext() || {};
|
|
1151
|
+
return userId;
|
|
1152
|
+
}, "getUserId");
|
|
1153
|
+
this.roles = new RolesAPI(client, getAppId, getUserId, obs, this.logger);
|
|
1154
|
+
this.members = new MembersAPI(client, getAppId, getUserId, obs, this.logger);
|
|
1155
|
+
this.search = new SearchAPI(client, getAppId, getUserId, obs, this.logger);
|
|
898
1156
|
}
|
|
899
1157
|
};
|
|
900
|
-
|
|
901
|
-
(0,
|
|
902
|
-
(0,
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
typeof
|
|
908
|
-
typeof
|
|
909
|
-
typeof import_nestjs_common3.RequestContextService === "undefined" ? Object : import_nestjs_common3.RequestContextService,
|
|
910
|
-
Object
|
|
1158
|
+
AuthorizationSDK = _ts_decorate4([
|
|
1159
|
+
(0, import_common5.Injectable)(),
|
|
1160
|
+
_ts_param3(0, (0, import_common5.Inject)(import_nestjs_common3.PLATFORM_HTTP_CLIENT)),
|
|
1161
|
+
_ts_param3(1, (0, import_common5.Inject)(import_nestjs_common3.OBSERVABLE_SERVICE)),
|
|
1162
|
+
_ts_metadata3("design:type", Function),
|
|
1163
|
+
_ts_metadata3("design:paramtypes", [
|
|
1164
|
+
typeof import_nestjs_common3.PlatformHttpClient === "undefined" ? Object : import_nestjs_common3.PlatformHttpClient,
|
|
1165
|
+
typeof ObservableService === "undefined" ? Object : ObservableService,
|
|
1166
|
+
typeof import_nestjs_common4.RequestContextService === "undefined" ? Object : import_nestjs_common4.RequestContextService
|
|
911
1167
|
])
|
|
912
|
-
],
|
|
1168
|
+
], AuthorizationSDK);
|
|
913
1169
|
|
|
914
|
-
// src/
|
|
915
|
-
var
|
|
916
|
-
|
|
917
|
-
var import_nestjs_common4 = require("@lark-apaas/nestjs-common");
|
|
918
|
-
function _ts_decorate4(decorators, target, key, desc) {
|
|
1170
|
+
// src/controllers/platform-permission.controller.ts
|
|
1171
|
+
var import_common6 = require("@nestjs/common");
|
|
1172
|
+
function _ts_decorate5(decorators, target, key, desc) {
|
|
919
1173
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
920
1174
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
921
1175
|
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;
|
|
922
1176
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
923
1177
|
}
|
|
924
|
-
__name(
|
|
925
|
-
function
|
|
1178
|
+
__name(_ts_decorate5, "_ts_decorate");
|
|
1179
|
+
function _ts_metadata4(k, v) {
|
|
926
1180
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
927
1181
|
}
|
|
928
|
-
__name(
|
|
929
|
-
function
|
|
1182
|
+
__name(_ts_metadata4, "_ts_metadata");
|
|
1183
|
+
function _ts_param4(paramIndex, decorator) {
|
|
930
1184
|
return function(target, key) {
|
|
931
1185
|
decorator(target, key, paramIndex);
|
|
932
1186
|
};
|
|
933
1187
|
}
|
|
934
|
-
__name(
|
|
935
|
-
var
|
|
1188
|
+
__name(_ts_param4, "_ts_param");
|
|
1189
|
+
var PlatformPermissionController = class {
|
|
936
1190
|
static {
|
|
937
|
-
__name(this, "
|
|
1191
|
+
__name(this, "PlatformPermissionController");
|
|
938
1192
|
}
|
|
939
|
-
reflector;
|
|
940
1193
|
permissionService;
|
|
941
|
-
|
|
942
|
-
constructor(
|
|
943
|
-
this.reflector = reflector;
|
|
1194
|
+
permissionResolver;
|
|
1195
|
+
constructor(permissionService, permissionResolver = null) {
|
|
944
1196
|
this.permissionService = permissionService;
|
|
945
|
-
this.
|
|
946
|
-
}
|
|
947
|
-
logger = new import_common5.Logger(_AuthZPaasGuard.name);
|
|
948
|
-
/**
|
|
949
|
-
* 验证权限点位要求是否有效
|
|
950
|
-
* @param requirements 权限点位要求
|
|
951
|
-
* @returns 是否有效
|
|
952
|
-
*/
|
|
953
|
-
isValidPermissionRequirement(requirements) {
|
|
954
|
-
return Boolean(requirements && requirements.permissions && requirements.permissions.length > 0);
|
|
955
|
-
}
|
|
956
|
-
/**
|
|
957
|
-
* 验证角色要求是否有效
|
|
958
|
-
* @param requirements 角色要求
|
|
959
|
-
* @returns 是否有效
|
|
960
|
-
*/
|
|
961
|
-
isValidRoleRequirement(requirements) {
|
|
962
|
-
return Boolean(requirements && requirements.roles && requirements.roles.length > 0);
|
|
1197
|
+
this.permissionResolver = permissionResolver;
|
|
963
1198
|
}
|
|
964
|
-
async
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
const laneId = request.headers["x-tt-env"];
|
|
968
|
-
const baseUrl = `${request.protocol}://${request.get("host")}`;
|
|
969
|
-
const userContext = request.userContext;
|
|
970
|
-
userContext.baseUrl = baseUrl;
|
|
971
|
-
const checkRoleRequirement = this.reflector.getAllAndOverride(ROLES_KEY, [
|
|
972
|
-
context.getHandler(),
|
|
973
|
-
context.getClass()
|
|
974
|
-
]);
|
|
975
|
-
if (this.isValidRoleRequirement(checkRoleRequirement)) {
|
|
976
|
-
const spanName = checkRoleRequirement.roles.join(" or ");
|
|
977
|
-
const logAttrs = {
|
|
978
|
-
source_type: "platform",
|
|
979
|
-
paas_attributes_module: "permission"
|
|
980
|
-
};
|
|
981
|
-
return this.obs.trace(spanName, async (span) => {
|
|
982
|
-
span.setAttributes({
|
|
983
|
-
module: "permission"
|
|
984
|
-
});
|
|
985
|
-
const elapsed = (0, import_nestjs_common4.createTimer)();
|
|
986
|
-
try {
|
|
987
|
-
const checkResult = await this.permissionService.checkRoles(checkRoleRequirement, laneId, userContext);
|
|
988
|
-
const durationMs = elapsed();
|
|
989
|
-
span.setAttribute("duration_ms", durationMs);
|
|
990
|
-
if (!checkResult.result) {
|
|
991
|
-
this.logger.warn(JSON.stringify({
|
|
992
|
-
role: spanName,
|
|
993
|
-
duration_ms: durationMs,
|
|
994
|
-
result: "no_auth",
|
|
995
|
-
result_detail: checkResult.details
|
|
996
|
-
}), {
|
|
997
|
-
...logAttrs,
|
|
998
|
-
duration_ms: durationMs
|
|
999
|
-
});
|
|
1000
|
-
} else {
|
|
1001
|
-
this.logger.log(JSON.stringify({
|
|
1002
|
-
role: spanName,
|
|
1003
|
-
duration_ms: durationMs,
|
|
1004
|
-
result: "has_auth"
|
|
1005
|
-
}), {
|
|
1006
|
-
...logAttrs,
|
|
1007
|
-
duration_ms: durationMs
|
|
1008
|
-
});
|
|
1009
|
-
}
|
|
1010
|
-
return checkResult.result;
|
|
1011
|
-
} catch (error) {
|
|
1012
|
-
const durationMs = elapsed();
|
|
1013
|
-
span.setAttribute("duration_ms", durationMs);
|
|
1014
|
-
this.logger.error(JSON.stringify({
|
|
1015
|
-
role: spanName,
|
|
1016
|
-
duration_ms: durationMs,
|
|
1017
|
-
result: "",
|
|
1018
|
-
error_message: error.message
|
|
1019
|
-
}), {
|
|
1020
|
-
...logAttrs,
|
|
1021
|
-
duration_ms: durationMs
|
|
1022
|
-
});
|
|
1023
|
-
throw error;
|
|
1024
|
-
}
|
|
1025
|
-
});
|
|
1199
|
+
async getUserPoints(req) {
|
|
1200
|
+
if (!this.permissionResolver) {
|
|
1201
|
+
return [];
|
|
1026
1202
|
}
|
|
1027
|
-
const
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
const
|
|
1033
|
-
|
|
1034
|
-
span.setAttributes({
|
|
1035
|
-
module: "permission"
|
|
1036
|
-
});
|
|
1037
|
-
const startTime = Date.now();
|
|
1038
|
-
try {
|
|
1039
|
-
const checkResult = await this.permissionService.checkPermissions(permissionRequirement, laneId, userContext);
|
|
1040
|
-
const endTime = Date.now();
|
|
1041
|
-
if (!checkResult.result) {
|
|
1042
|
-
this.logger.warn(JSON.stringify({
|
|
1043
|
-
permission: spanName,
|
|
1044
|
-
duration_ms: endTime - startTime,
|
|
1045
|
-
result: checkResult.result ? "has_auth" : "no_auth",
|
|
1046
|
-
result_detail: checkResult.details
|
|
1047
|
-
}), {
|
|
1048
|
-
source_type: "platform",
|
|
1049
|
-
paas_attributes_module: "permission"
|
|
1050
|
-
});
|
|
1051
|
-
} else {
|
|
1052
|
-
this.logger.log(JSON.stringify({
|
|
1053
|
-
permission: spanName,
|
|
1054
|
-
duration_ms: endTime - startTime,
|
|
1055
|
-
result: checkResult.result ? "has_auth" : "no_auth"
|
|
1056
|
-
}), {
|
|
1057
|
-
source_type: "platform",
|
|
1058
|
-
paas_attributes_module: "permission"
|
|
1059
|
-
});
|
|
1060
|
-
}
|
|
1061
|
-
return checkResult.result;
|
|
1062
|
-
} catch (error) {
|
|
1063
|
-
const endTime = Date.now();
|
|
1064
|
-
this.logger.error(JSON.stringify({
|
|
1065
|
-
permission: spanName,
|
|
1066
|
-
duration_ms: endTime - startTime,
|
|
1067
|
-
result: "",
|
|
1068
|
-
error_message: error.message
|
|
1069
|
-
}), {
|
|
1070
|
-
source_type: "platform",
|
|
1071
|
-
paas_attributes_module: "permission"
|
|
1072
|
-
});
|
|
1073
|
-
throw error;
|
|
1074
|
-
}
|
|
1075
|
-
});
|
|
1203
|
+
const userContext = req.userContext;
|
|
1204
|
+
let roleKeys;
|
|
1205
|
+
if (userContext?.roles) {
|
|
1206
|
+
roleKeys = userContext.roles;
|
|
1207
|
+
} else {
|
|
1208
|
+
const permissionData = await this.permissionService.getUserPermissions({});
|
|
1209
|
+
roleKeys = permissionData?.roles || [];
|
|
1076
1210
|
}
|
|
1077
|
-
return
|
|
1211
|
+
return this.permissionResolver.resolvePermissions(roleKeys);
|
|
1078
1212
|
}
|
|
1079
1213
|
};
|
|
1080
|
-
|
|
1081
|
-
(0,
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
typeof
|
|
1214
|
+
_ts_decorate5([
|
|
1215
|
+
(0, import_common6.Get)("user-points"),
|
|
1216
|
+
_ts_param4(0, (0, import_common6.Req)()),
|
|
1217
|
+
_ts_metadata4("design:type", Function),
|
|
1218
|
+
_ts_metadata4("design:paramtypes", [
|
|
1219
|
+
typeof Request === "undefined" ? Object : Request
|
|
1220
|
+
]),
|
|
1221
|
+
_ts_metadata4("design:returntype", Promise)
|
|
1222
|
+
], PlatformPermissionController.prototype, "getUserPoints", null);
|
|
1223
|
+
PlatformPermissionController = _ts_decorate5([
|
|
1224
|
+
(0, import_common6.Controller)("api/sdk_innerapi/permission"),
|
|
1225
|
+
_ts_param4(1, (0, import_common6.Optional)()),
|
|
1226
|
+
_ts_param4(1, (0, import_common6.Inject)(PERMISSION_RESOLVER_TOKEN)),
|
|
1227
|
+
_ts_metadata4("design:type", Function),
|
|
1228
|
+
_ts_metadata4("design:paramtypes", [
|
|
1086
1229
|
typeof PermissionService === "undefined" ? Object : PermissionService,
|
|
1087
|
-
|
|
1230
|
+
Object
|
|
1088
1231
|
])
|
|
1089
|
-
],
|
|
1232
|
+
], PlatformPermissionController);
|
|
1090
1233
|
|
|
1091
1234
|
// src/authzpaas.module.ts
|
|
1092
|
-
function
|
|
1235
|
+
function _ts_decorate6(decorators, target, key, desc) {
|
|
1093
1236
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1094
1237
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1095
1238
|
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;
|
|
1096
1239
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1097
1240
|
}
|
|
1098
|
-
__name(
|
|
1241
|
+
__name(_ts_decorate6, "_ts_decorate");
|
|
1099
1242
|
var AuthZPaasModule = class _AuthZPaasModule {
|
|
1100
1243
|
static {
|
|
1101
1244
|
__name(this, "AuthZPaasModule");
|
|
@@ -1105,7 +1248,9 @@ var AuthZPaasModule = class _AuthZPaasModule {
|
|
|
1105
1248
|
return {
|
|
1106
1249
|
module: _AuthZPaasModule,
|
|
1107
1250
|
global: true,
|
|
1108
|
-
controllers: [
|
|
1251
|
+
controllers: [
|
|
1252
|
+
PlatformPermissionController
|
|
1253
|
+
],
|
|
1109
1254
|
providers: [
|
|
1110
1255
|
// 配置提供者
|
|
1111
1256
|
{
|
|
@@ -1186,7 +1331,9 @@ var AuthZPaasModule = class _AuthZPaasModule {
|
|
|
1186
1331
|
module: _AuthZPaasModule,
|
|
1187
1332
|
global: true,
|
|
1188
1333
|
imports,
|
|
1189
|
-
controllers: [
|
|
1334
|
+
controllers: [
|
|
1335
|
+
PlatformPermissionController
|
|
1336
|
+
],
|
|
1190
1337
|
providers: [
|
|
1191
1338
|
// 异步配置提供者
|
|
1192
1339
|
{
|
|
@@ -1237,12 +1384,12 @@ var AuthZPaasModule = class _AuthZPaasModule {
|
|
|
1237
1384
|
};
|
|
1238
1385
|
}
|
|
1239
1386
|
};
|
|
1240
|
-
AuthZPaasModule =
|
|
1241
|
-
(0,
|
|
1387
|
+
AuthZPaasModule = _ts_decorate6([
|
|
1388
|
+
(0, import_common7.Module)({})
|
|
1242
1389
|
], AuthZPaasModule);
|
|
1243
1390
|
|
|
1244
1391
|
// src/decorators/can-role.decorator.ts
|
|
1245
|
-
var
|
|
1392
|
+
var import_common8 = require("@nestjs/common");
|
|
1246
1393
|
var CanRole = /* @__PURE__ */ __name((role) => {
|
|
1247
1394
|
let requirement;
|
|
1248
1395
|
if (!Array.isArray(role) && typeof role === "string") {
|
|
@@ -1258,19 +1405,17 @@ var CanRole = /* @__PURE__ */ __name((role) => {
|
|
|
1258
1405
|
} else {
|
|
1259
1406
|
throw new Error("Invalid CanRole parameter: " + JSON.stringify(role));
|
|
1260
1407
|
}
|
|
1261
|
-
return (0,
|
|
1408
|
+
return (0, import_common8.SetMetadata)(ROLES_KEY, requirement);
|
|
1262
1409
|
}, "CanRole");
|
|
1263
1410
|
|
|
1264
1411
|
// src/decorators/can.decorator.ts
|
|
1265
|
-
var
|
|
1266
|
-
var Can = /* @__PURE__ */ __name((
|
|
1267
|
-
if (!Array.isArray(permissions) || !permissions.every((p) => typeof p === "string")) {
|
|
1268
|
-
throw new Error("Invalid Can parameter: " + JSON.stringify(permissions));
|
|
1269
|
-
}
|
|
1412
|
+
var import_common9 = require("@nestjs/common");
|
|
1413
|
+
var Can = /* @__PURE__ */ __name((action, subject) => {
|
|
1270
1414
|
const requirement = {
|
|
1271
|
-
|
|
1415
|
+
action,
|
|
1416
|
+
subject
|
|
1272
1417
|
};
|
|
1273
|
-
return (0,
|
|
1418
|
+
return (0, import_common9.SetMetadata)(PERMISSIONS_KEY, requirement);
|
|
1274
1419
|
}, "Can");
|
|
1275
1420
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1276
1421
|
0 && (module.exports = {
|