@flusys/nestjs-shared 9.1.0-rc.1 → 9.1.1

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/fesm/470.js CHANGED
@@ -2224,6 +2224,8 @@ OptionalJwtGuard = optional_jwt_auth_guard_ts_decorate([
2224
2224
  var permission_exception = __webpack_require__(374);
2225
2225
  // EXTERNAL MODULE: ./projects/nestjs-shared/src/modules/shared-permission-cache/shared-permission-cache.service.ts
2226
2226
  var shared_permission_cache_service = __webpack_require__(657);
2227
+ // EXTERNAL MODULE: ./projects/nestjs-shared/src/utils/permission-match.util.ts
2228
+ var permission_match_util = __webpack_require__(788);
2227
2229
  ;// CONCATENATED MODULE: ./projects/nestjs-shared/src/guards/permission.guard.ts
2228
2230
  function permission_guard_ts_decorate(decorators, target, key, desc) {
2229
2231
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -2253,6 +2255,7 @@ function permission_guard_ts_param(paramIndex, decorator) {
2253
2255
 
2254
2256
 
2255
2257
 
2258
+
2256
2259
  class PermissionGuard {
2257
2260
  reflector;
2258
2261
  permissionCacheService;
@@ -2280,8 +2283,7 @@ class PermissionGuard {
2280
2283
  if (!this.permissionCacheService?.isCacheConfigured()) {
2281
2284
  throw new permission_exception/* .PermissionSystemUnavailableException */.GS();
2282
2285
  }
2283
- const permissionService = this.permissionCacheService;
2284
- const userPermissions = await permissionService.getPermissions({
2286
+ const userPermissions = await this.permissionCacheService.getPermissions({
2285
2287
  userId: user.id,
2286
2288
  companyId: user.companyId,
2287
2289
  branchId: user.branchId
@@ -2291,7 +2293,7 @@ class PermissionGuard {
2291
2293
  }
2292
2294
  const logicNode = this.normalizeToLogicNode(permissionConfig);
2293
2295
  if (!logicNode) return true;
2294
- const result = this.evaluateLogicNode(logicNode, userPermissions, permissionService);
2296
+ const result = (0,permission_match_util/* .evaluatePermissionLogic */.Ng)(logicNode, userPermissions);
2295
2297
  if (!result.passed) {
2296
2298
  throw new permission_exception/* .InsufficientPermissionsException */.z8(result.missingPermissions, result.operator);
2297
2299
  }
@@ -2341,43 +2343,6 @@ class PermissionGuard {
2341
2343
  // ILogicNode
2342
2344
  return config;
2343
2345
  }
2344
- evaluateLogicNode(node, userPermissions, permissionService) {
2345
- if (node.type === 'action') {
2346
- const passed = permissionService.matchPermission(userPermissions, node.actionId);
2347
- return {
2348
- passed,
2349
- missingPermissions: passed ? [] : [
2350
- node.actionId
2351
- ],
2352
- operator: 'AND'
2353
- };
2354
- }
2355
- // Group node
2356
- const { operator, children } = node;
2357
- // SECURITY: Fail-closed - deny access when no children configured
2358
- if (!children || children.length === 0) {
2359
- return {
2360
- passed: false,
2361
- missingPermissions: [],
2362
- operator
2363
- };
2364
- }
2365
- const results = [];
2366
- const missingPermissions = [];
2367
- for (const child of children){
2368
- const childResult = this.evaluateLogicNode(child, userPermissions, permissionService);
2369
- results.push(childResult.passed);
2370
- if (!childResult.passed) {
2371
- missingPermissions.push(...childResult.missingPermissions);
2372
- }
2373
- }
2374
- const passed = operator === 'OR' ? results.some((r)=>r) : results.every((r)=>r);
2375
- return {
2376
- passed,
2377
- missingPermissions: passed ? [] : missingPermissions,
2378
- operator
2379
- };
2380
- }
2381
2346
  }
2382
2347
  PermissionGuard = permission_guard_ts_decorate([
2383
2348
  (0,common_.Injectable)(),
package/fesm/681.js CHANGED
@@ -611,11 +611,21 @@ class SharedPermissionCacheService {
611
611
  requiredPermission
612
612
  ], 'AND');
613
613
  }
614
+ /** Throws 403 (naming what is missing) unless `user` passes the AND / OR tree; fails closed (500) when no cache is wired up. */ async assertPermissionLogic(user, logic) {
615
+ if (!this.isCacheConfigured()) throw new _exceptions_permission_exception_js__rspack_import_3/* .PermissionSystemUnavailableException */.GS();
616
+ const granted = user?.id ? await this.getPermissions({
617
+ userId: user.id,
618
+ companyId: user.companyId,
619
+ branchId: user.branchId
620
+ }) : [];
621
+ const result = (0,_utils_permission_match_util_js__rspack_import_5/* .evaluatePermissionLogic */.Ng)(logic, granted);
622
+ if (!result.passed) throw new _exceptions_permission_exception_js__rspack_import_3/* .InsufficientPermissionsException */.z8(result.missingPermissions, result.operator);
623
+ }
614
624
  /** Whether a cache instance is wired up; false means permission checks can't be served at all */ isCacheConfigured() {
615
625
  return !!this.cache;
616
626
  }
617
627
  /** Match an already-fetched permission list against a required permission, honoring `*` and `prefix.*` wildcards */ matchPermission(userPermissions, requiredPermission) {
618
- return (0,_utils_permission_match_util_js__rspack_import_5/* .matchesPermission */.x)(userPermissions, requiredPermission);
628
+ return (0,_utils_permission_match_util_js__rspack_import_5/* .matchesPermission */.xY)(userPermissions, requiredPermission);
619
629
  }
620
630
  }
621
631
  SharedPermissionCacheService = _ts_decorate([
@@ -843,6 +853,8 @@ __webpack_require__.d(__webpack_exports__, {
843
853
 
844
854
  },
845
855
  788(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
856
+ /* import */ var _exceptions_permission_exception_js__rspack_import_0 = __webpack_require__(374);
857
+
846
858
  /** Whether `granted` covers `required`, honoring `*` (everything) and `prefix.*` (everything under prefix) wildcards */ function matchesPermission(granted, required) {
847
859
  if (granted.includes(required)) return true;
848
860
  for (const permission of granted){
@@ -851,9 +863,73 @@ __webpack_require__.d(__webpack_exports__, {
851
863
  }
852
864
  return false;
853
865
  }
866
+ /** Evaluates an AND / OR permission tree against `granted`; a group without children fails closed. */ function evaluatePermissionLogic(node, granted) {
867
+ if (node.type === 'action') {
868
+ const passed = matchesPermission(granted, node.actionId);
869
+ return {
870
+ passed,
871
+ missingPermissions: passed ? [] : [
872
+ node.actionId
873
+ ],
874
+ operator: 'AND'
875
+ };
876
+ }
877
+ const { operator, children } = node;
878
+ if (!children?.length) return {
879
+ passed: false,
880
+ missingPermissions: [],
881
+ operator
882
+ };
883
+ const results = children.map((child)=>evaluatePermissionLogic(child, granted));
884
+ const passed = operator === 'OR' ? results.some((r)=>r.passed) : results.every((r)=>r.passed);
885
+ return {
886
+ passed,
887
+ missingPermissions: passed ? [] : results.flatMap((r)=>r.missingPermissions),
888
+ operator
889
+ };
890
+ }
891
+ /** Every permission code a tree names, each once, in order. */ function permissionLogicCodes(node) {
892
+ if (node.type === 'action') return node.actionId ? [
893
+ node.actionId
894
+ ] : [];
895
+ return [
896
+ ...new Set((node.children ?? []).flatMap(permissionLogicCodes))
897
+ ];
898
+ }
899
+ /** Groups nested in a permission rule, the outer one included. */ const MAX_PERMISSION_LOGIC_DEPTH = 5;
900
+ /** Permission codes one rule may name. */ const MAX_PERMISSION_LOGIC_CODES = 50;
901
+ const MAX_PERMISSION_CODE_LENGTH = 255;
902
+ /** What is wrong with a stored permission rule: `invalid` (its shape), `empty` (it names no permission), `too_many` codes, or null. */ function permissionLogicProblem(value) {
903
+ let codes = 0;
904
+ const walk = (node, depth)=>{
905
+ if (!node || typeof node !== 'object' || Array.isArray(node)) return false;
906
+ const n = node;
907
+ if (n['type'] === 'action') {
908
+ codes++;
909
+ return typeof n['actionId'] === 'string' && n['actionId'].trim() !== '' && n['actionId'].length <= MAX_PERMISSION_CODE_LENGTH;
910
+ }
911
+ if (n['type'] !== 'group' || n['operator'] !== 'AND' && n['operator'] !== 'OR' || !Array.isArray(n['children'])) return false;
912
+ if (depth >= MAX_PERMISSION_LOGIC_DEPTH || !n['children'].length) return false;
913
+ return n['children'].every((child)=>walk(child, depth + 1));
914
+ };
915
+ if (!value || typeof value !== 'object') return 'invalid';
916
+ const node = value;
917
+ if (node['type'] === 'group' && (node['operator'] === 'AND' || node['operator'] === 'OR') && Array.isArray(node['children']) && !node['children'].length) return 'empty';
918
+ if (!walk(value, 0)) return 'invalid';
919
+ return codes > MAX_PERMISSION_LOGIC_CODES ? 'too_many' : null;
920
+ }
921
+ /** A user's codes in `scope`, from the resolver or else the cache; with neither, fails closed with PermissionSystemUnavailableException. */ async function loadPermissionCodes(scope, sources) {
922
+ if (sources.resolver) return sources.resolver.getPermissionCodes(scope);
923
+ if (sources.cache?.isCacheConfigured()) return sources.cache.getPermissions(scope);
924
+ throw new _exceptions_permission_exception_js__rspack_import_0/* .PermissionSystemUnavailableException */.GS();
925
+ }
854
926
 
855
927
  __webpack_require__.d(__webpack_exports__, {
856
- x: () => (matchesPermission)
928
+ CQ: () => (loadPermissionCodes),
929
+ HU: () => (permissionLogicProblem),
930
+ MO: () => (permissionLogicCodes),
931
+ Ng: () => (evaluatePermissionLogic),
932
+ xY: () => (matchesPermission)
857
933
  });
858
934
 
859
935
 
package/fesm/948.js CHANGED
@@ -522,7 +522,6 @@ const FLOW_DEFINITION_PERMISSIONS = {
522
522
  UPDATE: 'entity_builder.flow_definition.update',
523
523
  DELETE: 'entity_builder.flow_definition.delete',
524
524
  TEST: 'entity_builder.flow_definition.test',
525
- /** Needed to save a flow that runs as the system (bypassing per-entity permissions). */ SYSTEM: 'entity_builder.flow_definition.system',
526
525
  /** Needed to save a flow containing a code node (sandboxed JavaScript). */ CODE: 'entity_builder.flow_definition.code'
527
526
  };
528
527
  const ENTITY_BUILDER_DYNAMIC_PREFIX = 'entity_builder.entity';
@@ -273,7 +273,6 @@ const FLOW_DEFINITION_PERMISSIONS = {
273
273
  UPDATE: 'entity_builder.flow_definition.update',
274
274
  DELETE: 'entity_builder.flow_definition.delete',
275
275
  TEST: 'entity_builder.flow_definition.test',
276
- /** Needed to save a flow that runs as the system (bypassing per-entity permissions). */ SYSTEM: 'entity_builder.flow_definition.system',
277
276
  /** Needed to save a flow containing a code node (sandboxed JavaScript). */ CODE: 'entity_builder.flow_definition.code'
278
277
  };
279
278
  const ENTITY_BUILDER_DYNAMIC_PREFIX = 'entity_builder.entity';
@@ -287,6 +287,8 @@ OptionalJwtGuard = optional_jwt_auth_guard_ts_decorate([
287
287
  var permission_exception = __webpack_require__(374);
288
288
  // EXTERNAL MODULE: ./projects/nestjs-shared/src/modules/shared-permission-cache/shared-permission-cache.service.ts
289
289
  var shared_permission_cache_service = __webpack_require__(657);
290
+ // EXTERNAL MODULE: ./projects/nestjs-shared/src/utils/permission-match.util.ts
291
+ var permission_match_util = __webpack_require__(788);
290
292
  ;// CONCATENATED MODULE: ./projects/nestjs-shared/src/guards/permission.guard.ts
291
293
  function permission_guard_ts_decorate(decorators, target, key, desc) {
292
294
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -316,6 +318,7 @@ function permission_guard_ts_param(paramIndex, decorator) {
316
318
 
317
319
 
318
320
 
321
+
319
322
  class PermissionGuard {
320
323
  reflector;
321
324
  permissionCacheService;
@@ -343,8 +346,7 @@ class PermissionGuard {
343
346
  if (!this.permissionCacheService?.isCacheConfigured()) {
344
347
  throw new permission_exception/* .PermissionSystemUnavailableException */.GS();
345
348
  }
346
- const permissionService = this.permissionCacheService;
347
- const userPermissions = await permissionService.getPermissions({
349
+ const userPermissions = await this.permissionCacheService.getPermissions({
348
350
  userId: user.id,
349
351
  companyId: user.companyId,
350
352
  branchId: user.branchId
@@ -354,7 +356,7 @@ class PermissionGuard {
354
356
  }
355
357
  const logicNode = this.normalizeToLogicNode(permissionConfig);
356
358
  if (!logicNode) return true;
357
- const result = this.evaluateLogicNode(logicNode, userPermissions, permissionService);
359
+ const result = (0,permission_match_util/* .evaluatePermissionLogic */.Ng)(logicNode, userPermissions);
358
360
  if (!result.passed) {
359
361
  throw new permission_exception/* .InsufficientPermissionsException */.z8(result.missingPermissions, result.operator);
360
362
  }
@@ -404,43 +406,6 @@ class PermissionGuard {
404
406
  // ILogicNode
405
407
  return config;
406
408
  }
407
- evaluateLogicNode(node, userPermissions, permissionService) {
408
- if (node.type === 'action') {
409
- const passed = permissionService.matchPermission(userPermissions, node.actionId);
410
- return {
411
- passed,
412
- missingPermissions: passed ? [] : [
413
- node.actionId
414
- ],
415
- operator: 'AND'
416
- };
417
- }
418
- // Group node
419
- const { operator, children } = node;
420
- // SECURITY: Fail-closed - deny access when no children configured
421
- if (!children || children.length === 0) {
422
- return {
423
- passed: false,
424
- missingPermissions: [],
425
- operator
426
- };
427
- }
428
- const results = [];
429
- const missingPermissions = [];
430
- for (const child of children){
431
- const childResult = this.evaluateLogicNode(child, userPermissions, permissionService);
432
- results.push(childResult.passed);
433
- if (!childResult.passed) {
434
- missingPermissions.push(...childResult.missingPermissions);
435
- }
436
- }
437
- const passed = operator === 'OR' ? results.some((r)=>r) : results.every((r)=>r);
438
- return {
439
- passed,
440
- missingPermissions: passed ? [] : missingPermissions,
441
- operator
442
- };
443
- }
444
409
  }
445
410
  PermissionGuard = permission_guard_ts_decorate([
446
411
  (0,common_.Injectable)(),
@@ -541,11 +506,21 @@ class SharedPermissionCacheService {
541
506
  requiredPermission
542
507
  ], 'AND');
543
508
  }
509
+ /** Throws 403 (naming what is missing) unless `user` passes the AND / OR tree; fails closed (500) when no cache is wired up. */ async assertPermissionLogic(user, logic) {
510
+ if (!this.isCacheConfigured()) throw new _exceptions_permission_exception_js__rspack_import_3/* .PermissionSystemUnavailableException */.GS();
511
+ const granted = user?.id ? await this.getPermissions({
512
+ userId: user.id,
513
+ companyId: user.companyId,
514
+ branchId: user.branchId
515
+ }) : [];
516
+ const result = (0,_utils_permission_match_util_js__rspack_import_5/* .evaluatePermissionLogic */.Ng)(logic, granted);
517
+ if (!result.passed) throw new _exceptions_permission_exception_js__rspack_import_3/* .InsufficientPermissionsException */.z8(result.missingPermissions, result.operator);
518
+ }
544
519
  /** Whether a cache instance is wired up; false means permission checks can't be served at all */ isCacheConfigured() {
545
520
  return !!this.cache;
546
521
  }
547
522
  /** Match an already-fetched permission list against a required permission, honoring `*` and `prefix.*` wildcards */ matchPermission(userPermissions, requiredPermission) {
548
- return (0,_utils_permission_match_util_js__rspack_import_5/* .matchesPermission */.x)(userPermissions, requiredPermission);
523
+ return (0,_utils_permission_match_util_js__rspack_import_5/* .matchesPermission */.xY)(userPermissions, requiredPermission);
549
524
  }
550
525
  }
551
526
  SharedPermissionCacheService = _ts_decorate([
@@ -590,6 +565,8 @@ __webpack_require__.d(__webpack_exports__, {
590
565
 
591
566
  },
592
567
  788(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
568
+ /* import */ var _exceptions_permission_exception_js__rspack_import_0 = __webpack_require__(374);
569
+
593
570
  /** Whether `granted` covers `required`, honoring `*` (everything) and `prefix.*` (everything under prefix) wildcards */ function matchesPermission(granted, required) {
594
571
  if (granted.includes(required)) return true;
595
572
  for (const permission of granted){
@@ -598,9 +575,73 @@ __webpack_require__.d(__webpack_exports__, {
598
575
  }
599
576
  return false;
600
577
  }
578
+ /** Evaluates an AND / OR permission tree against `granted`; a group without children fails closed. */ function evaluatePermissionLogic(node, granted) {
579
+ if (node.type === 'action') {
580
+ const passed = matchesPermission(granted, node.actionId);
581
+ return {
582
+ passed,
583
+ missingPermissions: passed ? [] : [
584
+ node.actionId
585
+ ],
586
+ operator: 'AND'
587
+ };
588
+ }
589
+ const { operator, children } = node;
590
+ if (!children?.length) return {
591
+ passed: false,
592
+ missingPermissions: [],
593
+ operator
594
+ };
595
+ const results = children.map((child)=>evaluatePermissionLogic(child, granted));
596
+ const passed = operator === 'OR' ? results.some((r)=>r.passed) : results.every((r)=>r.passed);
597
+ return {
598
+ passed,
599
+ missingPermissions: passed ? [] : results.flatMap((r)=>r.missingPermissions),
600
+ operator
601
+ };
602
+ }
603
+ /** Every permission code a tree names, each once, in order. */ function permissionLogicCodes(node) {
604
+ if (node.type === 'action') return node.actionId ? [
605
+ node.actionId
606
+ ] : [];
607
+ return [
608
+ ...new Set((node.children ?? []).flatMap(permissionLogicCodes))
609
+ ];
610
+ }
611
+ /** Groups nested in a permission rule, the outer one included. */ const MAX_PERMISSION_LOGIC_DEPTH = 5;
612
+ /** Permission codes one rule may name. */ const MAX_PERMISSION_LOGIC_CODES = 50;
613
+ const MAX_PERMISSION_CODE_LENGTH = 255;
614
+ /** What is wrong with a stored permission rule: `invalid` (its shape), `empty` (it names no permission), `too_many` codes, or null. */ function permissionLogicProblem(value) {
615
+ let codes = 0;
616
+ const walk = (node, depth)=>{
617
+ if (!node || typeof node !== 'object' || Array.isArray(node)) return false;
618
+ const n = node;
619
+ if (n['type'] === 'action') {
620
+ codes++;
621
+ return typeof n['actionId'] === 'string' && n['actionId'].trim() !== '' && n['actionId'].length <= MAX_PERMISSION_CODE_LENGTH;
622
+ }
623
+ if (n['type'] !== 'group' || n['operator'] !== 'AND' && n['operator'] !== 'OR' || !Array.isArray(n['children'])) return false;
624
+ if (depth >= MAX_PERMISSION_LOGIC_DEPTH || !n['children'].length) return false;
625
+ return n['children'].every((child)=>walk(child, depth + 1));
626
+ };
627
+ if (!value || typeof value !== 'object') return 'invalid';
628
+ const node = value;
629
+ if (node['type'] === 'group' && (node['operator'] === 'AND' || node['operator'] === 'OR') && Array.isArray(node['children']) && !node['children'].length) return 'empty';
630
+ if (!walk(value, 0)) return 'invalid';
631
+ return codes > MAX_PERMISSION_LOGIC_CODES ? 'too_many' : null;
632
+ }
633
+ /** A user's codes in `scope`, from the resolver or else the cache; with neither, fails closed with PermissionSystemUnavailableException. */ async function loadPermissionCodes(scope, sources) {
634
+ if (sources.resolver) return sources.resolver.getPermissionCodes(scope);
635
+ if (sources.cache?.isCacheConfigured()) return sources.cache.getPermissions(scope);
636
+ throw new _exceptions_permission_exception_js__rspack_import_0/* .PermissionSystemUnavailableException */.GS();
637
+ }
601
638
 
602
639
  __webpack_require__.d(__webpack_exports__, {
603
- x: () => (matchesPermission)
640
+ CQ: () => (loadPermissionCodes),
641
+ HU: () => (permissionLogicProblem),
642
+ MO: () => (permissionLogicCodes),
643
+ Ng: () => (evaluatePermissionLogic),
644
+ xY: () => (matchesPermission)
604
645
  });
605
646
 
606
647