@dudousxd/nestjs-authz 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/CHANGELOG.md +115 -0
  2. package/README.md +279 -0
  3. package/dist/authz.guard.d.ts +48 -0
  4. package/dist/authz.guard.d.ts.map +1 -0
  5. package/dist/authz.guard.js +144 -0
  6. package/dist/authz.guard.js.map +1 -0
  7. package/dist/can-endpoint.controller.d.ts +0 -19
  8. package/dist/can-endpoint.controller.d.ts.map +1 -1
  9. package/dist/can-endpoint.controller.js +18 -9
  10. package/dist/can-endpoint.controller.js.map +1 -1
  11. package/dist/decision-provider.d.ts +25 -16
  12. package/dist/decision-provider.d.ts.map +1 -1
  13. package/dist/decision-provider.js +11 -1
  14. package/dist/decision-provider.js.map +1 -1
  15. package/dist/decisions.d.ts +24 -0
  16. package/dist/decisions.d.ts.map +1 -0
  17. package/dist/decisions.js +87 -0
  18. package/dist/decisions.js.map +1 -0
  19. package/dist/decorator/roles.decorator.d.ts +6 -12
  20. package/dist/decorator/roles.decorator.d.ts.map +1 -1
  21. package/dist/decorator/roles.decorator.js +13 -15
  22. package/dist/decorator/roles.decorator.js.map +1 -1
  23. package/dist/diagnostics.d.ts +15 -5
  24. package/dist/diagnostics.d.ts.map +1 -1
  25. package/dist/diagnostics.js +3 -1
  26. package/dist/diagnostics.js.map +1 -1
  27. package/dist/errors/exceptions.d.ts +0 -8
  28. package/dist/errors/exceptions.d.ts.map +1 -1
  29. package/dist/errors/exceptions.js +0 -14
  30. package/dist/errors/exceptions.js.map +1 -1
  31. package/dist/gate.d.ts +83 -209
  32. package/dist/gate.d.ts.map +1 -1
  33. package/dist/gate.js +209 -577
  34. package/dist/gate.js.map +1 -1
  35. package/dist/helpers.d.ts +46 -0
  36. package/dist/helpers.d.ts.map +1 -0
  37. package/dist/helpers.js +89 -0
  38. package/dist/helpers.js.map +1 -0
  39. package/dist/index.d.ts +28 -21
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +30 -13
  42. package/dist/index.js.map +1 -1
  43. package/dist/links.d.ts +39 -0
  44. package/dist/links.d.ts.map +1 -0
  45. package/dist/links.js +57 -0
  46. package/dist/links.js.map +1 -0
  47. package/dist/module.d.ts +23 -22
  48. package/dist/module.d.ts.map +1 -1
  49. package/dist/module.js +129 -158
  50. package/dist/module.js.map +1 -1
  51. package/dist/official.d.ts +39 -0
  52. package/dist/official.d.ts.map +1 -0
  53. package/dist/official.js +44 -0
  54. package/dist/official.js.map +1 -0
  55. package/dist/permission-cache.d.ts +18 -9
  56. package/dist/permission-cache.d.ts.map +1 -1
  57. package/dist/permission-cache.js +87 -24
  58. package/dist/permission-cache.js.map +1 -1
  59. package/dist/permission-matcher.d.ts +13 -0
  60. package/dist/permission-matcher.d.ts.map +1 -1
  61. package/dist/permission-matcher.js +38 -2
  62. package/dist/permission-matcher.js.map +1 -1
  63. package/dist/permission-provider.d.ts +11 -0
  64. package/dist/permission-provider.d.ts.map +1 -1
  65. package/dist/policies/authz-policy.d.ts +42 -0
  66. package/dist/policies/authz-policy.d.ts.map +1 -0
  67. package/dist/policies/authz-policy.js +80 -0
  68. package/dist/policies/authz-policy.js.map +1 -0
  69. package/dist/policies/gate.policy.d.ts +18 -0
  70. package/dist/policies/gate.policy.d.ts.map +1 -0
  71. package/dist/policies/gate.policy.js +37 -0
  72. package/dist/policies/gate.policy.js.map +1 -0
  73. package/dist/policies/index.d.ts +5 -0
  74. package/dist/policies/index.d.ts.map +1 -0
  75. package/dist/policies/index.js +5 -0
  76. package/dist/policies/index.js.map +1 -0
  77. package/dist/policies/permission.policy.d.ts +27 -0
  78. package/dist/policies/permission.policy.d.ts.map +1 -0
  79. package/dist/policies/permission.policy.js +61 -0
  80. package/dist/policies/permission.policy.js.map +1 -0
  81. package/dist/policies/role.policy.d.ts +27 -0
  82. package/dist/policies/role.policy.d.ts.map +1 -0
  83. package/dist/policies/role.policy.js +51 -0
  84. package/dist/policies/role.policy.js.map +1 -0
  85. package/dist/policy-index.d.ts +51 -0
  86. package/dist/policy-index.d.ts.map +1 -0
  87. package/dist/policy-index.js +135 -0
  88. package/dist/policy-index.js.map +1 -0
  89. package/dist/rbac.d.ts +92 -0
  90. package/dist/rbac.d.ts.map +1 -0
  91. package/dist/rbac.js +293 -0
  92. package/dist/rbac.js.map +1 -0
  93. package/dist/resource-resolver.d.ts +15 -17
  94. package/dist/resource-resolver.d.ts.map +1 -1
  95. package/dist/resource-resolver.js +8 -8
  96. package/dist/resource-resolver.js.map +1 -1
  97. package/dist/runtime.d.ts +31 -0
  98. package/dist/runtime.d.ts.map +1 -0
  99. package/dist/runtime.js +40 -0
  100. package/dist/runtime.js.map +1 -0
  101. package/dist/tokens.d.ts +4 -8
  102. package/dist/tokens.d.ts.map +1 -1
  103. package/dist/tokens.js +4 -8
  104. package/dist/tokens.js.map +1 -1
  105. package/dist/types.d.ts +52 -169
  106. package/dist/types.d.ts.map +1 -1
  107. package/package.json +19 -10
  108. package/skills/authz-enforcement/SKILL.md +67 -131
  109. package/skills/authz-policies/SKILL.md +43 -117
  110. package/skills/authz-query-scopes/SKILL.md +16 -9
  111. package/skills/authz-rbac-seams/SKILL.md +39 -12
  112. package/skills/authz-setup/SKILL.md +45 -106
  113. package/dist/decorator/can.decorator.d.ts +0 -29
  114. package/dist/decorator/can.decorator.d.ts.map +0 -1
  115. package/dist/decorator/can.decorator.js +0 -23
  116. package/dist/decorator/can.decorator.js.map +0 -1
  117. package/dist/decorator/policy.decorator.d.ts +0 -18
  118. package/dist/decorator/policy.decorator.d.ts.map +0 -1
  119. package/dist/decorator/policy.decorator.js +0 -28
  120. package/dist/decorator/policy.decorator.js.map +0 -1
  121. package/dist/guard/can.guard.d.ts +0 -31
  122. package/dist/guard/can.guard.d.ts.map +0 -1
  123. package/dist/guard/can.guard.js +0 -92
  124. package/dist/guard/can.guard.js.map +0 -1
  125. package/dist/guard/roles.guard.d.ts +0 -21
  126. package/dist/guard/roles.guard.d.ts.map +0 -1
  127. package/dist/guard/roles.guard.js +0 -50
  128. package/dist/guard/roles.guard.js.map +0 -1
  129. package/dist/policy-registry.d.ts +0 -46
  130. package/dist/policy-registry.d.ts.map +0 -1
  131. package/dist/policy-registry.js +0 -101
  132. package/dist/policy-registry.js.map +0 -1
@@ -1,22 +1,84 @@
1
- import { permissionSatisfied } from './permission-matcher.js';
1
+ import { CompiledPermissions } from './permission-matcher.js';
2
+ /** A distinct number per user class, so two classes that share a name never share a key. */
3
+ const classIds = new WeakMap();
4
+ let nextClassId = 0;
5
+ function classIdOf(ctor) {
6
+ let id = classIds.get(ctor);
7
+ if (id === undefined) {
8
+ id = nextClassId++;
9
+ classIds.set(ctor, id);
10
+ }
11
+ return id;
12
+ }
13
+ /**
14
+ * The string identity of a user object that names its row, or `undefined` when it
15
+ * doesn't. A string/number `id` names a row when the object also says what kind of
16
+ * row it is: a string `type`, or a class of its own (an ORM entity). Two
17
+ * separately-loaded copies of that row (an ORM without an identity map, or a
18
+ * re-hydrated user) then share one entry. A plain object without `type` names
19
+ * nothing beyond itself (a human user and a service account may both be `{ id: 1 }`),
20
+ * so it keys by identity.
21
+ */
22
+ function rowKey(user) {
23
+ const { type, id } = user;
24
+ if (typeof id !== 'string' && typeof id !== 'number')
25
+ return undefined;
26
+ const proto = Object.getPrototypeOf(user);
27
+ const plain = proto === null || proto === Object.prototype;
28
+ if (plain && typeof type !== 'string')
29
+ return undefined;
30
+ const ctor = proto?.constructor;
31
+ const kind = typeof ctor === 'function' ? classIdOf(ctor) : -1;
32
+ return JSON.stringify([kind, typeof type === 'string' ? type : null, typeof id, id]);
33
+ }
2
34
  /**
3
- * A memo over a {@link PermissionProvider}'s `getPermissions(user)` so a user's
4
- * granted-permission set is fetched AT MOST ONCE per logical scope (a single
5
- * `allowsMany` batch, or — when wired to nestjs-context — a single HTTP request).
35
+ * A memo over a {@link PermissionProvider}'s `getPermissions(user)` and a
36
+ * {@link RoleProvider}'s `getRoles(user)` so each is fetched AT MOST ONCE per
37
+ * logical scope (a single `allowsMany` batch, or — when wired to nestjs-context —
38
+ * a single HTTP request).
6
39
  *
7
40
  * Without it, every `gate.allows(...)` re-fetches the set (an N+1 within a list
8
- * page, since each card runs its own check). The cache keys by the resolved
9
- * `user` identity so two checks for the SAME user share one fetch while a
10
- * different user still triggers its own.
41
+ * page, since each card runs its own check). Entries are keyed by the row a user
42
+ * names (see {@link rowKey}), else by identity, so checks for the SAME user share
43
+ * one fetch while a different user still triggers its own. The granted set is
44
+ * compiled once per entry, so each check is a hash lookup plus a scan of the
45
+ * wildcard grants only.
11
46
  *
12
- * Only the `getPermissions` (full-set, wildcard-matched) path is memoized — the
13
- * exact-match `hasPermission` fast path is left to the provider, which is free to
14
- * memoize internally. A provider WITHOUT `getPermissions` makes this a no-op.
47
+ * A grant or revocation made while the scope is alive is seen by the next scope
48
+ * (the next request), not by later checks in the same one.
15
49
  */
16
50
  export class PermissionCache {
17
- // Keyed by the user object identity. `Map` (not WeakMap) because a batch/request
18
- // cache is short-lived and we also accept primitive ids/refs as keys.
51
+ // `Map` (not WeakMap) because a batch/request cache is short-lived and keys are
52
+ // strings or primitive ids as often as objects.
19
53
  byUser = new Map();
54
+ rolesByUser = new Map();
55
+ // One token object per row key, so a string row key can never equal a bare-id user.
56
+ rowTokens = new Map();
57
+ userCacheKey(user) {
58
+ if (user === null || typeof user !== 'object')
59
+ return user;
60
+ const row = rowKey(user);
61
+ if (row === undefined)
62
+ return user;
63
+ let token = this.rowTokens.get(row);
64
+ if (!token) {
65
+ token = {};
66
+ this.rowTokens.set(row, token);
67
+ }
68
+ return token;
69
+ }
70
+ compiled(provider, user) {
71
+ if (typeof provider.getPermissions !== 'function')
72
+ return undefined;
73
+ const key = this.userCacheKey(user);
74
+ const cached = this.byUser.get(key);
75
+ if (cached)
76
+ return cached;
77
+ const pending = Promise.resolve(provider.getPermissions(user)).then((granted) => granted == null ? undefined : new CompiledPermissions(granted));
78
+ this.byUser.set(key, pending);
79
+ pending.catch(() => this.byUser.delete(key));
80
+ return pending;
81
+ }
20
82
  /**
21
83
  * Resolve the user's granted-permission set via the provider, memoized for the
22
84
  * cache's lifetime. Returns `undefined` when the provider can't list grants
@@ -24,14 +86,7 @@ export class PermissionCache {
24
86
  * exact-match `hasPermission` path.
25
87
  */
26
88
  async getPermissions(provider, user) {
27
- if (typeof provider.getPermissions !== 'function')
28
- return undefined;
29
- const cached = this.byUser.get(user);
30
- if (cached)
31
- return cached;
32
- const pending = Promise.resolve(provider.getPermissions(user)).then((granted) => granted == null ? undefined : new Set(granted));
33
- this.byUser.set(user, pending);
34
- return pending;
89
+ return (await this.compiled(provider, user))?.granted;
35
90
  }
36
91
  /**
37
92
  * True when the user's (memoized) granted set satisfies `ability` under the
@@ -39,10 +94,18 @@ export class PermissionCache {
39
94
  * defer to `hasPermission`).
40
95
  */
41
96
  async satisfies(provider, user, ability) {
42
- const set = await this.getPermissions(provider, user);
43
- if (set === undefined)
44
- return undefined;
45
- return permissionSatisfied(set, ability);
97
+ return (await this.compiled(provider, user))?.satisfies(ability);
98
+ }
99
+ /** The user's roles from `provider`, memoized for the cache's lifetime. */
100
+ getRoles(provider, user) {
101
+ const key = this.userCacheKey(user);
102
+ const cached = this.rolesByUser.get(key);
103
+ if (cached)
104
+ return cached;
105
+ const pending = Promise.resolve(provider.getRoles(user));
106
+ this.rolesByUser.set(key, pending);
107
+ pending.catch(() => this.rolesByUser.delete(key));
108
+ return pending;
46
109
  }
47
110
  }
48
111
  //# sourceMappingURL=permission-cache.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"permission-cache.js","sourceRoot":"","sources":["../src/permission-cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAI9D;;;;;;;;;;;;;GAaG;AACH,MAAM,OAAO,eAAe;IAC1B,iFAAiF;IACjF,sEAAsE;IACrD,MAAM,GAAG,IAAI,GAAG,EAAqD,CAAC;IAEvF;;;;;OAKG;IACH,KAAK,CAAC,cAAc,CAClB,QAA4B,EAC5B,IAAU;QAEV,IAAI,OAAO,QAAQ,CAAC,cAAc,KAAK,UAAU;YAAE,OAAO,SAAS,CAAC;QACpE,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACrC,IAAI,MAAM;YAAE,OAAO,MAAM,CAAC;QAC1B,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAC9E,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,CAC/C,CAAC;QACF,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC/B,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,SAAS,CACb,QAA4B,EAC5B,IAAU,EACV,OAAe;QAEf,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QACtD,IAAI,GAAG,KAAK,SAAS;YAAE,OAAO,SAAS,CAAC;QACxC,OAAO,mBAAmB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAC3C,CAAC;CACF"}
1
+ {"version":3,"file":"permission-cache.js","sourceRoot":"","sources":["../src/permission-cache.ts"],"names":[],"mappings":"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"}
@@ -19,4 +19,17 @@
19
19
  export declare function permissionMatches(granted: string, required: string): boolean;
20
20
  /** True when ANY permission in the granted set satisfies the required ability. */
21
21
  export declare function permissionSatisfied(granted: Iterable<string>, required: string): boolean;
22
+ /**
23
+ * A granted set prepared once for repeated checks with the same semantics as
24
+ * {@link permissionSatisfied}: grants without a `*` segment can only match by
25
+ * equality, so they live in a `Set`; only the wildcard grants are pre-split and
26
+ * scanned. A check costs one hash lookup plus one pass over the wildcard grants.
27
+ */
28
+ export declare class CompiledPermissions {
29
+ readonly granted: ReadonlySet<string>;
30
+ private readonly grantsAll;
31
+ private readonly patterns;
32
+ constructor(granted: Iterable<string>);
33
+ satisfies(required: string): boolean;
34
+ }
22
35
  //# sourceMappingURL=permission-matcher.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"permission-matcher.d.ts","sourceRoot":"","sources":["../src/permission-matcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CA0B5E;AAED,kFAAkF;AAClF,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAKxF"}
1
+ {"version":3,"file":"permission-matcher.d.ts","sourceRoot":"","sources":["../src/permission-matcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAI5E;AAwBD,kFAAkF;AAClF,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAKxF;AAED;;;;;GAKG;AACH,qBAAa,mBAAmB;IAC9B,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACtC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAU;IACpC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkB;gBAE/B,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC;IAWrC,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;CASrC"}
@@ -21,8 +21,9 @@ export function permissionMatches(granted, required) {
21
21
  return true;
22
22
  if (granted === '*')
23
23
  return true;
24
- const grantedParts = granted.split('.');
25
- const requiredParts = required.split('.');
24
+ return segmentsMatch(granted.split('.'), required.split('.'));
25
+ }
26
+ function segmentsMatch(grantedParts, requiredParts) {
26
27
  for (let i = 0; i < grantedParts.length; i++) {
27
28
  const g = grantedParts[i];
28
29
  if (g === '*') {
@@ -52,4 +53,39 @@ export function permissionSatisfied(granted, required) {
52
53
  }
53
54
  return false;
54
55
  }
56
+ /**
57
+ * A granted set prepared once for repeated checks with the same semantics as
58
+ * {@link permissionSatisfied}: grants without a `*` segment can only match by
59
+ * equality, so they live in a `Set`; only the wildcard grants are pre-split and
60
+ * scanned. A check costs one hash lookup plus one pass over the wildcard grants.
61
+ */
62
+ export class CompiledPermissions {
63
+ granted;
64
+ grantsAll;
65
+ patterns = [];
66
+ constructor(granted) {
67
+ const set = new Set(granted);
68
+ this.granted = set;
69
+ this.grantsAll = set.has('*');
70
+ for (const g of set) {
71
+ if (!g.includes('*'))
72
+ continue;
73
+ const parts = g.split('.');
74
+ if (parts.includes('*'))
75
+ this.patterns.push(parts);
76
+ }
77
+ }
78
+ satisfies(required) {
79
+ if (this.grantsAll || this.granted.has(required))
80
+ return true;
81
+ if (this.patterns.length === 0)
82
+ return false;
83
+ const requiredParts = required.split('.');
84
+ for (const parts of this.patterns) {
85
+ if (segmentsMatch(parts, requiredParts))
86
+ return true;
87
+ }
88
+ return false;
89
+ }
90
+ }
55
91
  //# sourceMappingURL=permission-matcher.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"permission-matcher.js","sourceRoot":"","sources":["../src/permission-matcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAe,EAAE,QAAgB;IACjE,IAAI,OAAO,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACtC,IAAI,OAAO,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IAEjC,MAAM,YAAY,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACxC,MAAM,aAAa,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAE1C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,YAAY,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7C,MAAM,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;QAC1B,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;YACd,kEAAkE;YAClE,yEAAyE;YACzE,0DAA0D;YAC1D,IAAI,CAAC,KAAK,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAClC,OAAO,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC;YAClC,CAAC;YACD,yEAAyE;YACzE,IAAI,aAAa,CAAC,CAAC,CAAC,KAAK,SAAS;gBAAE,OAAO,KAAK,CAAC;YACjD,SAAS;QACX,CAAC;QACD,IAAI,CAAC,KAAK,aAAa,CAAC,CAAC,CAAC;YAAE,OAAO,KAAK,CAAC;IAC3C,CAAC;IAED,6EAA6E;IAC7E,uDAAuD;IACvD,OAAO,YAAY,CAAC,MAAM,KAAK,aAAa,CAAC,MAAM,CAAC;AACtD,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,mBAAmB,CAAC,OAAyB,EAAE,QAAgB;IAC7E,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,IAAI,iBAAiB,CAAC,CAAC,EAAE,QAAQ,CAAC;YAAE,OAAO,IAAI,CAAC;IAClD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
1
+ {"version":3,"file":"permission-matcher.js","sourceRoot":"","sources":["../src/permission-matcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAe,EAAE,QAAgB;IACjE,IAAI,OAAO,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACtC,IAAI,OAAO,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IACjC,OAAO,aAAa,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;AAChE,CAAC;AAED,SAAS,aAAa,CAAC,YAA+B,EAAE,aAAgC;IACtF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,YAAY,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7C,MAAM,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;QAC1B,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;YACd,kEAAkE;YAClE,yEAAyE;YACzE,0DAA0D;YAC1D,IAAI,CAAC,KAAK,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAClC,OAAO,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC;YAClC,CAAC;YACD,yEAAyE;YACzE,IAAI,aAAa,CAAC,CAAC,CAAC,KAAK,SAAS;gBAAE,OAAO,KAAK,CAAC;YACjD,SAAS;QACX,CAAC;QACD,IAAI,CAAC,KAAK,aAAa,CAAC,CAAC,CAAC;YAAE,OAAO,KAAK,CAAC;IAC3C,CAAC;IAED,6EAA6E;IAC7E,uDAAuD;IACvD,OAAO,YAAY,CAAC,MAAM,KAAK,aAAa,CAAC,MAAM,CAAC;AACtD,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,mBAAmB,CAAC,OAAyB,EAAE,QAAgB;IAC7E,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,IAAI,iBAAiB,CAAC,CAAC,EAAE,QAAQ,CAAC;YAAE,OAAO,IAAI,CAAC;IAClD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;GAKG;AACH,MAAM,OAAO,mBAAmB;IACrB,OAAO,CAAsB;IACrB,SAAS,CAAU;IACnB,QAAQ,GAAe,EAAE,CAAC;IAE3C,YAAY,OAAyB;QACnC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;QAC7B,IAAI,CAAC,OAAO,GAAG,GAAG,CAAC;QACnB,IAAI,CAAC,SAAS,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC9B,KAAK,MAAM,CAAC,IAAI,GAAG,EAAE,CAAC;YACpB,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC;gBAAE,SAAS;YAC/B,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC3B,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC;gBAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrD,CAAC;IACH,CAAC;IAED,SAAS,CAAC,QAAgB;QACxB,IAAI,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;YAAE,OAAO,IAAI,CAAC;QAC9D,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QAC7C,MAAM,aAAa,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC1C,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClC,IAAI,aAAa,CAAC,KAAK,EAAE,aAAa,CAAC;gBAAE,OAAO,IAAI,CAAC;QACvD,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;CACF"}
@@ -28,6 +28,8 @@ import type { Resource, User } from './types.js';
28
28
  * (see `permission-matcher.ts`), so wildcard semantics work regardless of adapter
29
29
  * and without each adapter re-implementing the matching. `hasPermission` remains the
30
30
  * exact-match fast path (and the sole contract for providers that don't list grants).
31
+ * Within one request (nestjs-context) or one `allowsMany` batch, the set is fetched once
32
+ * per user.
31
33
  */
32
34
  export interface PermissionProvider {
33
35
  hasPermission(user: User, permission: string, resource?: Resource): boolean | undefined | Promise<boolean | undefined>;
@@ -39,5 +41,14 @@ export interface PermissionProvider {
39
41
  * `hasPermission` (e.g. for an anonymous/unmappable user).
40
42
  */
41
43
  getPermissions?(user: User): Iterable<string> | undefined | Promise<Iterable<string> | undefined>;
44
+ /**
45
+ * Optional: `true` when the set returned by {@link getPermissions} is everything
46
+ * {@link hasPermission} would ever grant, for any resource. The Gate then answers a
47
+ * check the set doesn't satisfy as "no grant" without calling `hasPermission`, so a
48
+ * request costs one `getPermissions` fetch no matter how many checks it runs. Leave it
49
+ * unset when `hasPermission` can grant beyond the listed set (e.g. it inspects the
50
+ * resource); the Gate then still calls `hasPermission` for those checks.
51
+ */
52
+ readonly exhaustivePermissions?: boolean;
42
53
  }
43
54
  //# sourceMappingURL=permission-provider.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"permission-provider.d.ts","sourceRoot":"","sources":["../src/permission-provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,WAAW,kBAAkB;IACjC,aAAa,CACX,IAAI,EAAE,IAAI,EACV,UAAU,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,QAAQ,GAClB,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC;IACtD;;;;;;OAMG;IACH,cAAc,CAAC,CAAC,IAAI,EAAE,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,SAAS,GAAG,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC;CACnG"}
1
+ {"version":3,"file":"permission-provider.d.ts","sourceRoot":"","sources":["../src/permission-provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,WAAW,kBAAkB;IACjC,aAAa,CACX,IAAI,EAAE,IAAI,EACV,UAAU,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,QAAQ,GAClB,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC;IACtD;;;;;;OAMG;IACH,cAAc,CAAC,CAAC,IAAI,EAAE,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,SAAS,GAAG,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC;IAClG;;;;;;;OAOG;IACH,QAAQ,CAAC,qBAAqB,CAAC,EAAE,OAAO,CAAC;CAC1C"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * @internal Runs `fn` (the `Gate` handing a string check to the official service) knowing
3
+ * `superAdmin` did not grant `ability` and the decision point abstained, so the policy's
4
+ * {@link superAdminBefore} / {@link decisionProviderBefore} skip asking them again.
5
+ */
6
+ export declare function withAskedCheck<T>(user: unknown, ability: string, args: readonly unknown[], fn: () => Promise<T>): Promise<T>;
7
+ /**
8
+ * Asks the `superAdmin` hook given to `AuthzModule.forRoot()`. `true` grants; anything
9
+ * else falls through. Call it from your own `before()` when a policy cannot extend
10
+ * {@link AuthzPolicy}.
11
+ */
12
+ export declare function superAdminBefore(user: unknown, ability: string, resource?: object): Promise<true | undefined>;
13
+ /**
14
+ * Asks the external decision point (`DECISION_PROVIDER`, e.g. the Cerbos adapter), when
15
+ * one is registered: `true` allows, `false` denies, `undefined` abstains (no provider, or
16
+ * it has no opinion). Call it from your own `before()` when a policy cannot extend
17
+ * {@link AuthzPolicy}.
18
+ */
19
+ export declare function decisionProviderBefore(user: unknown, ability: string, resource?: object): Promise<boolean | undefined>;
20
+ /**
21
+ * Base class for official policies that honor the global `superAdmin` hook and the
22
+ * optional external decision point.
23
+ *
24
+ * The official guard runs only each policy's own `before()`, so a global "super-admins
25
+ * may do anything" needs every policy to ask. Extend this class and they do:
26
+ *
27
+ * ```ts
28
+ * @Policy()
29
+ * export class PostPolicy extends AuthzPolicy {
30
+ * update(user: User | null, post: Post) { return post.authorId === user?.id; }
31
+ * }
32
+ * ```
33
+ *
34
+ * `before()` asks `superAdmin(user, ability, resource)` (a `true` allows), then the
35
+ * `DECISION_PROVIDER` when one is registered (its allow or deny is final, `undefined`
36
+ * abstains), then lets the ability decide. The resource is the check's first argument.
37
+ * To add your own `before()`, override it and call `super.before(...)` first.
38
+ */
39
+ export declare abstract class AuthzPolicy {
40
+ before(user: unknown, ability: string, ...args: unknown[]): boolean | undefined | Promise<boolean | undefined>;
41
+ }
42
+ //# sourceMappingURL=authz-policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authz-policy.d.ts","sourceRoot":"","sources":["../../src/policies/authz-policy.ts"],"names":[],"mappings":"AAqBA;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAC9B,IAAI,EAAE,OAAO,EACb,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,SAAS,OAAO,EAAE,EACxB,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,CAAC,CAAC,CAEZ;AAaD;;;;GAIG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,OAAO,EACb,OAAO,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,GAAG,SAAS,CAAC,CAI3B;AAED;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,OAAO,EACb,OAAO,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAI9B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,8BAAsB,WAAW;IAC/B,MAAM,CACJ,IAAI,EAAE,OAAO,EACb,OAAO,EAAE,MAAM,EACf,GAAG,IAAI,EAAE,OAAO,EAAE,GACjB,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC;CAOtD"}
@@ -0,0 +1,80 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks';
2
+ import { normalizeVerdict } from '../decision-provider.js';
3
+ import { authzRuntime } from '../runtime.js';
4
+ /** The resource of a policy check: its first argument, when that is an object or a class. */
5
+ function resourceOf(args) {
6
+ const first = args[0];
7
+ return (typeof first === 'object' && first !== null) || typeof first === 'function'
8
+ ? first
9
+ : undefined;
10
+ }
11
+ const asked = new AsyncLocalStorage();
12
+ /**
13
+ * @internal Runs `fn` (the `Gate` handing a string check to the official service) knowing
14
+ * `superAdmin` did not grant `ability` and the decision point abstained, so the policy's
15
+ * {@link superAdminBefore} / {@link decisionProviderBefore} skip asking them again.
16
+ */
17
+ export function withAskedCheck(user, ability, args, fn) {
18
+ return asked.run({ user: user ?? null, ability, resource: resourceOf(args) }, fn);
19
+ }
20
+ /** True when the `Gate` already asked about exactly this check (see {@link withAskedCheck}). */
21
+ function alreadyAsked(user, ability, resource) {
22
+ const check = asked.getStore();
23
+ return (check !== undefined &&
24
+ check.user === (user ?? null) &&
25
+ check.ability === ability &&
26
+ check.resource === resource);
27
+ }
28
+ /**
29
+ * Asks the `superAdmin` hook given to `AuthzModule.forRoot()`. `true` grants; anything
30
+ * else falls through. Call it from your own `before()` when a policy cannot extend
31
+ * {@link AuthzPolicy}.
32
+ */
33
+ export async function superAdminBefore(user, ability, resource) {
34
+ const hook = authzRuntime().superAdmin;
35
+ if (!hook || alreadyAsked(user, ability, resource))
36
+ return undefined;
37
+ return (await hook(user ?? null, ability, resource)) === true ? true : undefined;
38
+ }
39
+ /**
40
+ * Asks the external decision point (`DECISION_PROVIDER`, e.g. the Cerbos adapter), when
41
+ * one is registered: `true` allows, `false` denies, `undefined` abstains (no provider, or
42
+ * it has no opinion). Call it from your own `before()` when a policy cannot extend
43
+ * {@link AuthzPolicy}.
44
+ */
45
+ export async function decisionProviderBefore(user, ability, resource) {
46
+ const provider = authzRuntime().decisionProvider();
47
+ if (!provider || alreadyAsked(user, ability, resource))
48
+ return undefined;
49
+ return normalizeVerdict(await provider.decide(user ?? null, ability, resource)).allowed;
50
+ }
51
+ /**
52
+ * Base class for official policies that honor the global `superAdmin` hook and the
53
+ * optional external decision point.
54
+ *
55
+ * The official guard runs only each policy's own `before()`, so a global "super-admins
56
+ * may do anything" needs every policy to ask. Extend this class and they do:
57
+ *
58
+ * ```ts
59
+ * @Policy()
60
+ * export class PostPolicy extends AuthzPolicy {
61
+ * update(user: User | null, post: Post) { return post.authorId === user?.id; }
62
+ * }
63
+ * ```
64
+ *
65
+ * `before()` asks `superAdmin(user, ability, resource)` (a `true` allows), then the
66
+ * `DECISION_PROVIDER` when one is registered (its allow or deny is final, `undefined`
67
+ * abstains), then lets the ability decide. The resource is the check's first argument.
68
+ * To add your own `before()`, override it and call `super.before(...)` first.
69
+ */
70
+ export class AuthzPolicy {
71
+ before(user, ability, ...args) {
72
+ const resource = resourceOf(args);
73
+ return (async () => {
74
+ if (await superAdminBefore(user, ability, resource))
75
+ return true;
76
+ return decisionProviderBefore(user, ability, resource);
77
+ })();
78
+ }
79
+ }
80
+ //# sourceMappingURL=authz-policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authz-policy.js","sourceRoot":"","sources":["../../src/policies/authz-policy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,6FAA6F;AAC7F,SAAS,UAAU,CAAC,IAAwB;IAC1C,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACtB,OAAO,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC,IAAI,OAAO,KAAK,KAAK,UAAU;QACjF,CAAC,CAAE,KAAgB;QACnB,CAAC,CAAC,SAAS,CAAC;AAChB,CAAC;AASD,MAAM,KAAK,GAAG,IAAI,iBAAiB,EAAc,CAAC;AAElD;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAC5B,IAAa,EACb,OAAe,EACf,IAAwB,EACxB,EAAoB;IAEpB,OAAO,KAAK,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,IAAI,IAAI,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;AACpF,CAAC;AAED,gGAAgG;AAChG,SAAS,YAAY,CAAC,IAAa,EAAE,OAAe,EAAE,QAA4B;IAChF,MAAM,KAAK,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC;IAC/B,OAAO,CACL,KAAK,KAAK,SAAS;QACnB,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,IAAI,IAAI,CAAC;QAC7B,KAAK,CAAC,OAAO,KAAK,OAAO;QACzB,KAAK,CAAC,QAAQ,KAAK,QAAQ,CAC5B,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,IAAa,EACb,OAAe,EACf,QAAiB;IAEjB,MAAM,IAAI,GAAG,YAAY,EAAE,CAAC,UAAU,CAAC;IACvC,IAAI,CAAC,IAAI,IAAI,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC;QAAE,OAAO,SAAS,CAAC;IACrE,OAAO,CAAC,MAAM,IAAI,CAAC,IAAI,IAAI,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;AACnF,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,IAAa,EACb,OAAe,EACf,QAAiB;IAEjB,MAAM,QAAQ,GAAG,YAAY,EAAE,CAAC,gBAAgB,EAAE,CAAC;IACnD,IAAI,CAAC,QAAQ,IAAI,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC;QAAE,OAAO,SAAS,CAAC;IACzE,OAAO,gBAAgB,CAAC,MAAM,QAAQ,CAAC,MAAM,CAAC,IAAI,IAAI,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC;AAC1F,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,OAAgB,WAAW;IAC/B,MAAM,CACJ,IAAa,EACb,OAAe,EACf,GAAG,IAAe;QAElB,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;QAClC,OAAO,CAAC,KAAK,IAAI,EAAE;YACjB,IAAI,MAAM,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC;gBAAE,OAAO,IAAI,CAAC;YACjE,OAAO,sBAAsB,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;QACzD,CAAC,CAAC,EAAE,CAAC;IACP,CAAC;CACF"}
@@ -0,0 +1,18 @@
1
+ import { Gate } from '../gate.js';
2
+ /**
3
+ * The `Gate`'s string abilities as an official policy: ad-hoc gates from
4
+ * `gate.define()`, named permissions, and `ability` + resource checks through the
5
+ * resource's linked policy.
6
+ *
7
+ * ```ts
8
+ * gate.define('access-admin', (user) => user.isStaff);
9
+ *
10
+ * @Can(GatePolicy, 'allows', ability('access-admin'))
11
+ * ```
12
+ */
13
+ export declare class GatePolicy {
14
+ private readonly gate;
15
+ constructor(gate: Gate);
16
+ allows(user: unknown, ability: string, resource?: object): Promise<boolean>;
17
+ }
18
+ //# sourceMappingURL=gate.policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gate.policy.d.ts","sourceRoot":"","sources":["../../src/policies/gate.policy.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAElC;;;;;;;;;;GAUG;AACH,qBACa,UAAU;IACT,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,IAAI;IAEvC,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAG5E"}
@@ -0,0 +1,37 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ 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;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { Policy } from '@nestjs/authorization';
11
+ import { Gate } from '../gate.js';
12
+ /**
13
+ * The `Gate`'s string abilities as an official policy: ad-hoc gates from
14
+ * `gate.define()`, named permissions, and `ability` + resource checks through the
15
+ * resource's linked policy.
16
+ *
17
+ * ```ts
18
+ * gate.define('access-admin', (user) => user.isStaff);
19
+ *
20
+ * @Can(GatePolicy, 'allows', ability('access-admin'))
21
+ * ```
22
+ */
23
+ let GatePolicy = class GatePolicy {
24
+ gate;
25
+ constructor(gate) {
26
+ this.gate = gate;
27
+ }
28
+ allows(user, ability, resource) {
29
+ return this.gate.forUser(user).allows(ability, resource);
30
+ }
31
+ };
32
+ GatePolicy = __decorate([
33
+ Policy(),
34
+ __metadata("design:paramtypes", [Gate])
35
+ ], GatePolicy);
36
+ export { GatePolicy };
37
+ //# sourceMappingURL=gate.policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gate.policy.js","sourceRoot":"","sources":["../../src/policies/gate.policy.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAElC;;;;;;;;;;GAUG;AAEI,IAAM,UAAU,GAAhB,MAAM,UAAU;IACQ;IAA7B,YAA6B,IAAU;QAAV,SAAI,GAAJ,IAAI,CAAM;IAAG,CAAC;IAE3C,MAAM,CAAC,IAAa,EAAE,OAAe,EAAE,QAAiB;QACtD,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC3D,CAAC;CACF,CAAA;AANY,UAAU;IADtB,MAAM,EAAE;qCAE4B,IAAI;GAD5B,UAAU,CAMtB"}
@@ -0,0 +1,5 @@
1
+ export { AuthzPolicy, decisionProviderBefore, superAdminBefore } from './authz-policy.js';
2
+ export { RolePolicy } from './role.policy.js';
3
+ export { PermissionPolicy } from './permission.policy.js';
4
+ export { GatePolicy } from './gate.policy.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/policies/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1F,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { AuthzPolicy, decisionProviderBefore, superAdminBefore } from './authz-policy.js';
2
+ export { RolePolicy } from './role.policy.js';
3
+ export { PermissionPolicy } from './permission.policy.js';
4
+ export { GatePolicy } from './gate.policy.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/policies/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1F,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,27 @@
1
+ import { AuthzRbac } from '../rbac.js';
2
+ import { AuthzPolicy } from './authz-policy.js';
3
+ /**
4
+ * Persisted permissions (through roles, tenant-scoped roles, or granted directly) as an
5
+ * official policy. Wildcard grants apply: `posts.*` satisfies `posts.update`.
6
+ *
7
+ * ```ts
8
+ * @Can(PermissionPolicy, 'has', permission('posts.update'))
9
+ * @Can(PermissionPolicy, 'hasAll', permission('posts.update', 'posts.publish'))
10
+ * ```
11
+ *
12
+ * Honors `superAdmin`.
13
+ */
14
+ export declare class PermissionPolicy extends AuthzPolicy {
15
+ private readonly rbac;
16
+ constructor(rbac: AuthzRbac);
17
+ /**
18
+ * Asks only `superAdmin`: the abilities here name permissions, not a resource, so the
19
+ * external decision point is not consulted.
20
+ */
21
+ before(user: unknown, ability: string): Promise<true | undefined>;
22
+ /** The user holds any of `permissions`. */
23
+ has(user: unknown, ...permissions: string[]): Promise<boolean>;
24
+ /** The user holds every one of `permissions`. */
25
+ hasAll(user: unknown, ...permissions: string[]): Promise<boolean>;
26
+ }
27
+ //# sourceMappingURL=permission.policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permission.policy.d.ts","sourceRoot":"","sources":["../../src/policies/permission.policy.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,WAAW,EAAoB,MAAM,mBAAmB,CAAC;AAElE;;;;;;;;;;GAUG;AACH,qBACa,gBAAiB,SAAQ,WAAW;IACnC,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,SAAS;IAI5C;;;OAGG;IACM,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,SAAS,CAAC;IAI1E,2CAA2C;IACrC,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAOpE,iDAAiD;IAC3C,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;CAOxE"}
@@ -0,0 +1,61 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ 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;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { Policy } from '@nestjs/authorization';
11
+ import { AuthzRbac } from '../rbac.js';
12
+ import { AuthzPolicy, superAdminBefore } from './authz-policy.js';
13
+ /**
14
+ * Persisted permissions (through roles, tenant-scoped roles, or granted directly) as an
15
+ * official policy. Wildcard grants apply: `posts.*` satisfies `posts.update`.
16
+ *
17
+ * ```ts
18
+ * @Can(PermissionPolicy, 'has', permission('posts.update'))
19
+ * @Can(PermissionPolicy, 'hasAll', permission('posts.update', 'posts.publish'))
20
+ * ```
21
+ *
22
+ * Honors `superAdmin`.
23
+ */
24
+ let PermissionPolicy = class PermissionPolicy extends AuthzPolicy {
25
+ rbac;
26
+ constructor(rbac) {
27
+ super();
28
+ this.rbac = rbac;
29
+ }
30
+ /**
31
+ * Asks only `superAdmin`: the abilities here name permissions, not a resource, so the
32
+ * external decision point is not consulted.
33
+ */
34
+ before(user, ability) {
35
+ return superAdminBefore(user, ability);
36
+ }
37
+ /** The user holds any of `permissions`. */
38
+ async has(user, ...permissions) {
39
+ for (const permission of permissions) {
40
+ if (await this.rbac.grants(user, permission))
41
+ return true;
42
+ }
43
+ return false;
44
+ }
45
+ /** The user holds every one of `permissions`. */
46
+ async hasAll(user, ...permissions) {
47
+ if (permissions.length === 0)
48
+ return false;
49
+ for (const permission of permissions) {
50
+ if (!(await this.rbac.grants(user, permission)))
51
+ return false;
52
+ }
53
+ return true;
54
+ }
55
+ };
56
+ PermissionPolicy = __decorate([
57
+ Policy(),
58
+ __metadata("design:paramtypes", [AuthzRbac])
59
+ ], PermissionPolicy);
60
+ export { PermissionPolicy };
61
+ //# sourceMappingURL=permission.policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permission.policy.js","sourceRoot":"","sources":["../../src/policies/permission.policy.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAElE;;;;;;;;;;GAUG;AAEI,IAAM,gBAAgB,GAAtB,MAAM,gBAAiB,SAAQ,WAAW;IAClB;IAA7B,YAA6B,IAAe;QAC1C,KAAK,EAAE,CAAC;QADmB,SAAI,GAAJ,IAAI,CAAW;IAE5C,CAAC;IAED;;;OAGG;IACM,MAAM,CAAC,IAAa,EAAE,OAAe;QAC5C,OAAO,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACzC,CAAC;IAED,2CAA2C;IAC3C,KAAK,CAAC,GAAG,CAAC,IAAa,EAAE,GAAG,WAAqB;QAC/C,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;YACrC,IAAI,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,UAAU,CAAC;gBAAE,OAAO,IAAI,CAAC;QAC5D,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,iDAAiD;IACjD,KAAK,CAAC,MAAM,CAAC,IAAa,EAAE,GAAG,WAAqB;QAClD,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QAC3C,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;YACrC,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;gBAAE,OAAO,KAAK,CAAC;QAChE,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAA;AA7BY,gBAAgB;IAD5B,MAAM,EAAE;qCAE4B,SAAS;GADjC,gBAAgB,CA6B5B"}
@@ -0,0 +1,27 @@
1
+ import { AuthzRbac } from '../rbac.js';
2
+ import { AuthzPolicy } from './authz-policy.js';
3
+ /**
4
+ * Persisted (and user-object) roles as an official policy.
5
+ *
6
+ * ```ts
7
+ * @Can(RolePolicy, 'has', roles('admin', 'editor')) // any of
8
+ * @Can(RolePolicy, 'hasAll', roles('admin', 'editor')) // all of
9
+ * ```
10
+ *
11
+ * Roles come from `user.roles` / `user.role` (or `resolveRoles`) and from the RBAC
12
+ * adapter's role store, scoped to the current tenant. Honors `superAdmin`.
13
+ */
14
+ export declare class RolePolicy extends AuthzPolicy {
15
+ private readonly rbac;
16
+ constructor(rbac: AuthzRbac);
17
+ /**
18
+ * Asks only `superAdmin`: the abilities here name roles, not a resource, so the
19
+ * external decision point is not consulted.
20
+ */
21
+ before(user: unknown, ability: string): Promise<true | undefined>;
22
+ /** The user holds any of `roles`. */
23
+ has(user: unknown, ...roles: string[]): Promise<boolean>;
24
+ /** The user holds every one of `roles`. */
25
+ hasAll(user: unknown, ...roles: string[]): Promise<boolean>;
26
+ }
27
+ //# sourceMappingURL=role.policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"role.policy.d.ts","sourceRoot":"","sources":["../../src/policies/role.policy.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,WAAW,EAAoB,MAAM,mBAAmB,CAAC;AAElE;;;;;;;;;;GAUG;AACH,qBACa,UAAW,SAAQ,WAAW;IAC7B,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,SAAS;IAI5C;;;OAGG;IACM,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,SAAS,CAAC;IAI1E,qCAAqC;IACrC,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAIxD,2CAA2C;IAC3C,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;CAG5D"}