@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.
- package/CHANGELOG.md +115 -0
- package/README.md +279 -0
- package/dist/authz.guard.d.ts +48 -0
- package/dist/authz.guard.d.ts.map +1 -0
- package/dist/authz.guard.js +144 -0
- package/dist/authz.guard.js.map +1 -0
- package/dist/can-endpoint.controller.d.ts +0 -19
- package/dist/can-endpoint.controller.d.ts.map +1 -1
- package/dist/can-endpoint.controller.js +18 -9
- package/dist/can-endpoint.controller.js.map +1 -1
- package/dist/decision-provider.d.ts +25 -16
- package/dist/decision-provider.d.ts.map +1 -1
- package/dist/decision-provider.js +11 -1
- package/dist/decision-provider.js.map +1 -1
- package/dist/decisions.d.ts +24 -0
- package/dist/decisions.d.ts.map +1 -0
- package/dist/decisions.js +87 -0
- package/dist/decisions.js.map +1 -0
- package/dist/decorator/roles.decorator.d.ts +6 -12
- package/dist/decorator/roles.decorator.d.ts.map +1 -1
- package/dist/decorator/roles.decorator.js +13 -15
- package/dist/decorator/roles.decorator.js.map +1 -1
- package/dist/diagnostics.d.ts +15 -5
- package/dist/diagnostics.d.ts.map +1 -1
- package/dist/diagnostics.js +3 -1
- package/dist/diagnostics.js.map +1 -1
- package/dist/errors/exceptions.d.ts +0 -8
- package/dist/errors/exceptions.d.ts.map +1 -1
- package/dist/errors/exceptions.js +0 -14
- package/dist/errors/exceptions.js.map +1 -1
- package/dist/gate.d.ts +83 -209
- package/dist/gate.d.ts.map +1 -1
- package/dist/gate.js +209 -577
- package/dist/gate.js.map +1 -1
- package/dist/helpers.d.ts +46 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/helpers.js +89 -0
- package/dist/helpers.js.map +1 -0
- package/dist/index.d.ts +28 -21
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +30 -13
- package/dist/index.js.map +1 -1
- package/dist/links.d.ts +39 -0
- package/dist/links.d.ts.map +1 -0
- package/dist/links.js +57 -0
- package/dist/links.js.map +1 -0
- package/dist/module.d.ts +23 -22
- package/dist/module.d.ts.map +1 -1
- package/dist/module.js +129 -158
- package/dist/module.js.map +1 -1
- package/dist/official.d.ts +39 -0
- package/dist/official.d.ts.map +1 -0
- package/dist/official.js +44 -0
- package/dist/official.js.map +1 -0
- package/dist/permission-cache.d.ts +18 -9
- package/dist/permission-cache.d.ts.map +1 -1
- package/dist/permission-cache.js +87 -24
- package/dist/permission-cache.js.map +1 -1
- package/dist/permission-matcher.d.ts +13 -0
- package/dist/permission-matcher.d.ts.map +1 -1
- package/dist/permission-matcher.js +38 -2
- package/dist/permission-matcher.js.map +1 -1
- package/dist/permission-provider.d.ts +11 -0
- package/dist/permission-provider.d.ts.map +1 -1
- package/dist/policies/authz-policy.d.ts +42 -0
- package/dist/policies/authz-policy.d.ts.map +1 -0
- package/dist/policies/authz-policy.js +80 -0
- package/dist/policies/authz-policy.js.map +1 -0
- package/dist/policies/gate.policy.d.ts +18 -0
- package/dist/policies/gate.policy.d.ts.map +1 -0
- package/dist/policies/gate.policy.js +37 -0
- package/dist/policies/gate.policy.js.map +1 -0
- package/dist/policies/index.d.ts +5 -0
- package/dist/policies/index.d.ts.map +1 -0
- package/dist/policies/index.js +5 -0
- package/dist/policies/index.js.map +1 -0
- package/dist/policies/permission.policy.d.ts +27 -0
- package/dist/policies/permission.policy.d.ts.map +1 -0
- package/dist/policies/permission.policy.js +61 -0
- package/dist/policies/permission.policy.js.map +1 -0
- package/dist/policies/role.policy.d.ts +27 -0
- package/dist/policies/role.policy.d.ts.map +1 -0
- package/dist/policies/role.policy.js +51 -0
- package/dist/policies/role.policy.js.map +1 -0
- package/dist/policy-index.d.ts +51 -0
- package/dist/policy-index.d.ts.map +1 -0
- package/dist/policy-index.js +135 -0
- package/dist/policy-index.js.map +1 -0
- package/dist/rbac.d.ts +92 -0
- package/dist/rbac.d.ts.map +1 -0
- package/dist/rbac.js +293 -0
- package/dist/rbac.js.map +1 -0
- package/dist/resource-resolver.d.ts +15 -17
- package/dist/resource-resolver.d.ts.map +1 -1
- package/dist/resource-resolver.js +8 -8
- package/dist/resource-resolver.js.map +1 -1
- package/dist/runtime.d.ts +31 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +40 -0
- package/dist/runtime.js.map +1 -0
- package/dist/tokens.d.ts +4 -8
- package/dist/tokens.d.ts.map +1 -1
- package/dist/tokens.js +4 -8
- package/dist/tokens.js.map +1 -1
- package/dist/types.d.ts +52 -169
- package/dist/types.d.ts.map +1 -1
- package/package.json +19 -10
- package/skills/authz-enforcement/SKILL.md +67 -131
- package/skills/authz-policies/SKILL.md +43 -117
- package/skills/authz-query-scopes/SKILL.md +16 -9
- package/skills/authz-rbac-seams/SKILL.md +39 -12
- package/skills/authz-setup/SKILL.md +45 -106
- package/dist/decorator/can.decorator.d.ts +0 -29
- package/dist/decorator/can.decorator.d.ts.map +0 -1
- package/dist/decorator/can.decorator.js +0 -23
- package/dist/decorator/can.decorator.js.map +0 -1
- package/dist/decorator/policy.decorator.d.ts +0 -18
- package/dist/decorator/policy.decorator.d.ts.map +0 -1
- package/dist/decorator/policy.decorator.js +0 -28
- package/dist/decorator/policy.decorator.js.map +0 -1
- package/dist/guard/can.guard.d.ts +0 -31
- package/dist/guard/can.guard.d.ts.map +0 -1
- package/dist/guard/can.guard.js +0 -92
- package/dist/guard/can.guard.js.map +0 -1
- package/dist/guard/roles.guard.d.ts +0 -21
- package/dist/guard/roles.guard.d.ts.map +0 -1
- package/dist/guard/roles.guard.js +0 -50
- package/dist/guard/roles.guard.js.map +0 -1
- package/dist/policy-registry.d.ts +0 -46
- package/dist/policy-registry.d.ts.map +0 -1
- package/dist/policy-registry.js +0 -101
- package/dist/policy-registry.js.map +0 -1
package/dist/permission-cache.js
CHANGED
|
@@ -1,22 +1,84 @@
|
|
|
1
|
-
import {
|
|
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)`
|
|
4
|
-
*
|
|
5
|
-
* `allowsMany` batch, or — when wired to nestjs-context —
|
|
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).
|
|
9
|
-
*
|
|
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
|
-
*
|
|
13
|
-
*
|
|
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
|
-
//
|
|
18
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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;
|
|
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,
|
|
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
|
-
|
|
25
|
-
|
|
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;
|
|
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
|
|
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"}
|