@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
|
@@ -10,16 +10,18 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
10
10
|
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
11
11
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
12
12
|
};
|
|
13
|
-
import {
|
|
13
|
+
import { Can } from '@nestjs/authorization';
|
|
14
|
+
import { Body, Controller, Inject, Optional, createParamDecorator, } from '@nestjs/common';
|
|
14
15
|
import { Post as HttpPost } from '@nestjs/common';
|
|
15
16
|
import { Gate } from './gate.js';
|
|
17
|
+
import { currentUserOf } from './runtime.js';
|
|
16
18
|
import { RESOURCE_HYDRATOR } from './tokens.js';
|
|
17
19
|
/**
|
|
18
20
|
* Build the opt-in fallback controller class, mounted at `path`. Kept as a
|
|
19
21
|
* factory so the route path is configurable via `AuthzModule.forRoot({ canEndpoint })`.
|
|
20
22
|
*
|
|
21
|
-
* The endpoint runs `gate.allows(ability, resource?)` for the
|
|
22
|
-
*
|
|
23
|
+
* The endpoint runs `gate.allows(ability, resource?)` for the caller, read the way
|
|
24
|
+
* the official guard reads it.
|
|
23
25
|
*
|
|
24
26
|
* RESOURCE REHYDRATION — how per-instance decisions resolve:
|
|
25
27
|
* the client sends a `{ type, id }` shim whose constructor is `Object`, which never
|
|
@@ -33,7 +35,11 @@ import { RESOURCE_HYDRATOR } from './tokens.js';
|
|
|
33
35
|
* (a deliberate, documented fall-through, not a silent dead-end). An unresolved ability
|
|
34
36
|
* is likewise treated as a deny (never a 500).
|
|
35
37
|
*/
|
|
38
|
+
/** The caller, read the way the official guard reads it (`getUser`, else `defaultGetUser`). */
|
|
39
|
+
const Caller = createParamDecorator((_data, ctx) => currentUserOf(ctx));
|
|
36
40
|
export function createCanController(path) {
|
|
41
|
+
// The endpoint answers for any caller (guests get `false`s); the official guard denies
|
|
42
|
+
// undeclared handlers, so it is opened explicitly.
|
|
37
43
|
let AuthzCanController = class AuthzCanController {
|
|
38
44
|
gate;
|
|
39
45
|
loaders;
|
|
@@ -41,10 +47,11 @@ export function createCanController(path) {
|
|
|
41
47
|
this.gate = gate;
|
|
42
48
|
this.loaders = loaders;
|
|
43
49
|
}
|
|
44
|
-
async can(body) {
|
|
50
|
+
async can(body, caller) {
|
|
51
|
+
const gate = this.gate.forUser(await caller);
|
|
45
52
|
// Array body → batch path (one user/permission resolution for the whole list).
|
|
46
53
|
if (Array.isArray(body)) {
|
|
47
|
-
return this.canBatch(body);
|
|
54
|
+
return this.canBatch(gate, body);
|
|
48
55
|
}
|
|
49
56
|
const ability = body?.ability;
|
|
50
57
|
if (typeof ability !== 'string' || ability.length === 0) {
|
|
@@ -56,7 +63,7 @@ export function createCanController(path) {
|
|
|
56
63
|
// A registered loader that produced nothing → not found → deny.
|
|
57
64
|
if (target === null)
|
|
58
65
|
return { allowed: false };
|
|
59
|
-
const allowed = await
|
|
66
|
+
const allowed = await gate.allows(ability, target);
|
|
60
67
|
return { allowed };
|
|
61
68
|
}
|
|
62
69
|
catch {
|
|
@@ -73,7 +80,7 @@ export function createCanController(path) {
|
|
|
73
80
|
* not-found loader, an empty ability, or an unresolved ability denies that item
|
|
74
81
|
* (never fails the whole batch).
|
|
75
82
|
*/
|
|
76
|
-
async canBatch(items) {
|
|
83
|
+
async canBatch(gate, items) {
|
|
77
84
|
// Validate + rehydrate each item up front. `verdicts[i] === false` already
|
|
78
85
|
// pins the outright-denials (bad ability / not-found resource); `undefined`
|
|
79
86
|
// means "send to the batch". `batchInput` carries the surviving items plus
|
|
@@ -99,7 +106,7 @@ export function createCanController(path) {
|
|
|
99
106
|
}
|
|
100
107
|
// ONE pass over the surviving items: user + permission set resolved once.
|
|
101
108
|
// `allowsMany` is fault-isolating, so an unresolved ability denies just its item.
|
|
102
|
-
const results = await
|
|
109
|
+
const results = await gate.allowsMany(batchInput.map((b) => b.resource === undefined
|
|
103
110
|
? { ability: b.ability }
|
|
104
111
|
: { ability: b.ability, resource: b.resource }));
|
|
105
112
|
results.forEach((r, k) => {
|
|
@@ -134,11 +141,13 @@ export function createCanController(path) {
|
|
|
134
141
|
__decorate([
|
|
135
142
|
HttpPost(),
|
|
136
143
|
__param(0, Body()),
|
|
144
|
+
__param(1, Caller()),
|
|
137
145
|
__metadata("design:type", Function),
|
|
138
|
-
__metadata("design:paramtypes", [Object]),
|
|
146
|
+
__metadata("design:paramtypes", [Object, Promise]),
|
|
139
147
|
__metadata("design:returntype", Promise)
|
|
140
148
|
], AuthzCanController.prototype, "can", null);
|
|
141
149
|
AuthzCanController = __decorate([
|
|
150
|
+
Can.Anyone(),
|
|
142
151
|
Controller(path),
|
|
143
152
|
__param(1, Optional()),
|
|
144
153
|
__param(1, Inject(RESOURCE_HYDRATOR)),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"can-endpoint.controller.js","sourceRoot":"","sources":["../src/can-endpoint.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"can-endpoint.controller.js","sourceRoot":"","sources":["../src/can-endpoint.controller.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,12 +4,11 @@ import type { PolicyResult, Resource, User } from './types.js';
|
|
|
4
4
|
/**
|
|
5
5
|
* What a {@link DecisionProvider} answers for one `(user, ability, resource)`:
|
|
6
6
|
*
|
|
7
|
-
* - `true` / `{ allowed: true }` → **allow** (short-circuits
|
|
8
|
-
* - `false` / `{ allowed: false, message? }` → **deny** (short-circuits
|
|
7
|
+
* - `true` / `{ allowed: true }` → **allow** (short-circuits);
|
|
8
|
+
* - `false` / `{ allowed: false, message? }` → **deny** (short-circuits — unlike the
|
|
9
9
|
* grant-only {@link PermissionProvider}, a decision provider CAN deny);
|
|
10
10
|
* - `undefined` / `null` → **abstain**: the provider has no opinion (e.g. the resource kind or
|
|
11
|
-
* the tenant isn't governed by the external engine) and
|
|
12
|
-
* resolution (permission provider → policy → ad-hoc gate).
|
|
11
|
+
* the tenant isn't governed by the external engine) and resolution continues as usual.
|
|
13
12
|
*/
|
|
14
13
|
export type DecisionVerdict = PolicyResult | undefined;
|
|
15
14
|
/** One `(ability, resource)` pair in a {@link DecisionProvider.decideMany} batch. */
|
|
@@ -22,28 +21,33 @@ export interface DecisionRequest {
|
|
|
22
21
|
* authz service, …) — the authoritative, resource-aware counterpart of the grant-only
|
|
23
22
|
* {@link PermissionProvider}. Register it under the {@link DECISION_PROVIDER} token.
|
|
24
23
|
*
|
|
25
|
-
* ##
|
|
24
|
+
* ## Where it is asked
|
|
26
25
|
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* 3. permission provider grant (RBAC, grant-only);
|
|
30
|
-
* 4. policy `before` → policy method, else ad-hoc gate;
|
|
31
|
-
* 5. global `after` hook (only when the path above had no opinion).
|
|
26
|
+
* Right after the global `superAdmin` hook (which receives the resource too), everywhere a
|
|
27
|
+
* decision is made:
|
|
32
28
|
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
29
|
+
* - **routes**: the official guard runs each policy's `before()`. Policies extending
|
|
30
|
+
* `AuthzPolicy` ask `superAdmin`, then `decide(user, ability, firstArg)`. `RolePolicy` and
|
|
31
|
+
* `PermissionPolicy` do not ask it: their abilities (`has`, `hasAll`) name no resource.
|
|
32
|
+
* - **`gate.allows(ability, resource)`**: before the linked policy, the persisted
|
|
33
|
+
* permissions and the ad-hoc gate.
|
|
34
|
+
* - **`gate.allows(PostPolicy, 'update', post)`**: through the policy's `before()`, as above.
|
|
35
|
+
*
|
|
36
|
+
* The provider is also consulted for guests (`user` is `null`) so an engine can allow public
|
|
37
|
+
* access; abstain to keep the default guest-deny.
|
|
35
38
|
*
|
|
36
39
|
* ## Batching
|
|
37
40
|
*
|
|
38
|
-
* `gate.allowsMany(...)` calls {@link decideMany} ONCE for the
|
|
39
|
-
* list page with N cards costs one engine round-trip instead of N);
|
|
41
|
+
* `gate.allowsMany(...)` calls {@link decideMany} ONCE for the string abilities of the batch
|
|
42
|
+
* when implemented (a list page with N cards costs one engine round-trip instead of N);
|
|
43
|
+
* otherwise `decide` per item.
|
|
40
44
|
*
|
|
41
45
|
* ## Query scopes
|
|
42
46
|
*
|
|
43
47
|
* `gate.scope(Entity, ability)` calls {@link planScope} right after the `superAdmin` hook. A
|
|
44
48
|
* returned {@link ScopeConstraint} is used as-is (e.g. an engine's query plan mapped onto the scope
|
|
45
|
-
* AST — see `@dudousxd/nestjs-authz/cerbos`); `undefined` abstains and the
|
|
46
|
-
*
|
|
49
|
+
* AST — see `@dudousxd/nestjs-authz/cerbos`); `undefined` abstains and the policy's `scope`
|
|
50
|
+
* method decides.
|
|
47
51
|
*
|
|
48
52
|
* Errors thrown by the provider propagate (the check fails loudly). Providers that talk to a
|
|
49
53
|
* remote engine should decide their own failure mode — the Cerbos adapter fails CLOSED (deny).
|
|
@@ -55,4 +59,9 @@ export interface DecisionProvider {
|
|
|
55
59
|
/** Optional query-plan → scope mapping. `undefined` = abstain. */
|
|
56
60
|
planScope?(user: User, entity: Type<unknown>, ability: string): ScopeConstraint | undefined | Promise<ScopeConstraint | undefined>;
|
|
57
61
|
}
|
|
62
|
+
/** A verdict as `{ allowed, message }`; `allowed` is `undefined` when the provider abstains. */
|
|
63
|
+
export declare function normalizeVerdict(verdict: DecisionVerdict | null): {
|
|
64
|
+
allowed: boolean | undefined;
|
|
65
|
+
message?: string;
|
|
66
|
+
};
|
|
58
67
|
//# sourceMappingURL=decision-provider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decision-provider.d.ts","sourceRoot":"","sources":["../src/decision-provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAE/D
|
|
1
|
+
{"version":3,"file":"decision-provider.d.ts","sourceRoot":"","sources":["../src/decision-provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAE/D;;;;;;;;GAQG;AACH,MAAM,MAAM,eAAe,GAAG,YAAY,GAAG,SAAS,CAAC;AAEvD,qFAAqF;AACrF,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CACJ,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,QAAQ,GAClB,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IAC9C,0EAA0E;IAC1E,UAAU,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,eAAe,EAAE,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;IACjF,kEAAkE;IAClE,SAAS,CAAC,CACR,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,EACrB,OAAO,EAAE,MAAM,GACd,eAAe,GAAG,SAAS,GAAG,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC,CAAC;CACvE;AAED,gGAAgG;AAChG,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI,GAAG;IACjE,OAAO,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAQA"}
|
|
@@ -1,2 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
/** A verdict as `{ allowed, message }`; `allowed` is `undefined` when the provider abstains. */
|
|
2
|
+
export function normalizeVerdict(verdict) {
|
|
3
|
+
if (verdict === true || verdict === false)
|
|
4
|
+
return { allowed: verdict };
|
|
5
|
+
if (verdict && typeof verdict === 'object' && typeof verdict.allowed === 'boolean') {
|
|
6
|
+
return verdict.message === undefined
|
|
7
|
+
? { allowed: verdict.allowed }
|
|
8
|
+
: { allowed: verdict.allowed, message: verdict.message };
|
|
9
|
+
}
|
|
10
|
+
return { allowed: undefined };
|
|
11
|
+
}
|
|
2
12
|
//# sourceMappingURL=decision-provider.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decision-provider.js","sourceRoot":"","sources":["../src/decision-provider.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"decision-provider.js","sourceRoot":"","sources":["../src/decision-provider.ts"],"names":[],"mappings":"AAyEA,gGAAgG;AAChG,MAAM,UAAU,gBAAgB,CAAC,OAA+B;IAI9D,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK;QAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;IACvE,IAAI,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,OAAO,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QACnF,OAAO,OAAO,CAAC,OAAO,KAAK,SAAS;YAClC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE;YAC9B,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC;IAC7D,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;AAChC,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { AuthorizationEvents } from '@nestjs/authorization';
|
|
2
|
+
import { type CallHandler, type ExecutionContext, type NestInterceptor, type OnModuleDestroy, type OnModuleInit } from '@nestjs/common';
|
|
3
|
+
import type { Observable } from 'rxjs';
|
|
4
|
+
/**
|
|
5
|
+
* Publishes the official guard's decisions on the `authz:decision` channel, allowed and
|
|
6
|
+
* denied, for Telescope-style watchers (the official package publishes denials only).
|
|
7
|
+
*
|
|
8
|
+
* - Denials: the official `AuthorizationEvents` stream, for events the guard raised
|
|
9
|
+
* (those with a `handler`). `authorize()` denials come from the `Gate` itself.
|
|
10
|
+
* - Allows: an interceptor, which runs only once every guard let the call through,
|
|
11
|
+
* publishes one decision per official `@Can()` requirement of the handler.
|
|
12
|
+
*
|
|
13
|
+
* Nothing is computed while the channel has no subscriber.
|
|
14
|
+
*/
|
|
15
|
+
export declare class AuthzDecisionPublisher implements NestInterceptor, OnModuleInit, OnModuleDestroy {
|
|
16
|
+
private readonly events?;
|
|
17
|
+
private subscription;
|
|
18
|
+
constructor(events?: AuthorizationEvents | undefined);
|
|
19
|
+
onModuleInit(): void;
|
|
20
|
+
onModuleDestroy(): void;
|
|
21
|
+
intercept(context: ExecutionContext, next: CallHandler): Observable<unknown>;
|
|
22
|
+
private publishAllows;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=decisions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decisions.d.ts","sourceRoot":"","sources":["../src/decisions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,gBAAgB,EAErB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,YAAY,EAGlB,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,UAAU,EAAgB,MAAM,MAAM,CAAC;AAOrD;;;;;;;;;;GAUG;AACH,qBACa,sBAAuB,YAAW,eAAe,EAAE,YAAY,EAAE,eAAe;IAGnE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IAFhD,OAAO,CAAC,YAAY,CAA2B;gBAEN,MAAM,CAAC,EAAE,mBAAmB,YAAA;IAErE,YAAY,IAAI,IAAI;IAUpB,eAAe,IAAI,IAAI;IAIvB,SAAS,CAAC,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,WAAW,GAAG,UAAU,CAAC,OAAO,CAAC;YAK9D,aAAa;CAmB5B"}
|
|
@@ -0,0 +1,87 @@
|
|
|
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
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
11
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
12
|
+
};
|
|
13
|
+
import { AuthorizationEvents } from '@nestjs/authorization';
|
|
14
|
+
import { Injectable, Optional, } from '@nestjs/common';
|
|
15
|
+
import { authzDecisionChannel, publishAuthzDecision } from './diagnostics.js';
|
|
16
|
+
import { authorizedResource } from './helpers.js';
|
|
17
|
+
import { getPolicyResource } from './links.js';
|
|
18
|
+
import { routeRequirements } from './official.js';
|
|
19
|
+
import { currentUserOf } from './runtime.js';
|
|
20
|
+
/**
|
|
21
|
+
* Publishes the official guard's decisions on the `authz:decision` channel, allowed and
|
|
22
|
+
* denied, for Telescope-style watchers (the official package publishes denials only).
|
|
23
|
+
*
|
|
24
|
+
* - Denials: the official `AuthorizationEvents` stream, for events the guard raised
|
|
25
|
+
* (those with a `handler`). `authorize()` denials come from the `Gate` itself.
|
|
26
|
+
* - Allows: an interceptor, which runs only once every guard let the call through,
|
|
27
|
+
* publishes one decision per official `@Can()` requirement of the handler.
|
|
28
|
+
*
|
|
29
|
+
* Nothing is computed while the channel has no subscriber.
|
|
30
|
+
*/
|
|
31
|
+
let AuthzDecisionPublisher = class AuthzDecisionPublisher {
|
|
32
|
+
events;
|
|
33
|
+
subscription;
|
|
34
|
+
constructor(events) {
|
|
35
|
+
this.events = events;
|
|
36
|
+
}
|
|
37
|
+
onModuleInit() {
|
|
38
|
+
this.subscription = this.events?.events$.subscribe((event) => {
|
|
39
|
+
if (event.handler === undefined || event.ability === null)
|
|
40
|
+
return;
|
|
41
|
+
publishAuthzDecision(event.ability, false, 'route', event.user, firstObject(event.args), {
|
|
42
|
+
policy: event.policy,
|
|
43
|
+
handler: event.handler,
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
onModuleDestroy() {
|
|
48
|
+
this.subscription?.unsubscribe();
|
|
49
|
+
}
|
|
50
|
+
intercept(context, next) {
|
|
51
|
+
if (authzDecisionChannel.hasSubscribers)
|
|
52
|
+
void this.publishAllows(context);
|
|
53
|
+
return next.handle();
|
|
54
|
+
}
|
|
55
|
+
async publishAllows(context) {
|
|
56
|
+
try {
|
|
57
|
+
const type = context.getClass();
|
|
58
|
+
const handler = context.getHandler();
|
|
59
|
+
const requirements = routeRequirements(type, handler);
|
|
60
|
+
if (requirements.length === 0)
|
|
61
|
+
return;
|
|
62
|
+
const user = await currentUserOf(context);
|
|
63
|
+
for (const { policy, ability } of requirements) {
|
|
64
|
+
const linked = getPolicyResource(policy);
|
|
65
|
+
const resource = linked ? authorizedResource(context, linked) : undefined;
|
|
66
|
+
publishAuthzDecision(ability, true, 'route', user, resource, {
|
|
67
|
+
policy: policy.name,
|
|
68
|
+
handler: `${type.name}.${handler.name}`,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// Observability never breaks a request.
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
AuthzDecisionPublisher = __decorate([
|
|
78
|
+
Injectable(),
|
|
79
|
+
__param(0, Optional()),
|
|
80
|
+
__metadata("design:paramtypes", [AuthorizationEvents])
|
|
81
|
+
], AuthzDecisionPublisher);
|
|
82
|
+
export { AuthzDecisionPublisher };
|
|
83
|
+
function firstObject(args) {
|
|
84
|
+
const first = args[0];
|
|
85
|
+
return first !== null && typeof first === 'object' ? first : undefined;
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=decisions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decisions.js","sourceRoot":"","sources":["../src/decisions.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,15 +1,9 @@
|
|
|
1
|
-
import 'reflect-metadata';
|
|
2
1
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* Roles come from the user object (`user.roles` / `user.role`) by default, plus the
|
|
8
|
-
* optional `ROLE_PROVIDER` seam (a persisted RBAC store) when registered — see
|
|
9
|
-
* {@link RolesGuard}.
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* `@Roles('admin', 'teacher')` // allow if the user is an admin OR a teacher
|
|
2
|
+
* Sugar for the official `@Can(RolePolicy, 'has', roles(...names))`: the user holds any
|
|
3
|
+
* of the roles. Stacks with other `@Can()`s (all must pass); a class-level `@Roles`
|
|
4
|
+
* applies to every handler, together with the method's.
|
|
13
5
|
*/
|
|
14
|
-
export declare function Roles(...
|
|
6
|
+
export declare function Roles(...names: string[]): MethodDecorator & ClassDecorator;
|
|
7
|
+
/** Sugar for the official `@Can(PermissionPolicy, 'has', permission(...names))`: any of the permissions. */
|
|
8
|
+
export declare function Permission(...names: string[]): MethodDecorator & ClassDecorator;
|
|
15
9
|
//# sourceMappingURL=roles.decorator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"roles.decorator.d.ts","sourceRoot":"","sources":["../../src/decorator/roles.decorator.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"roles.decorator.d.ts","sourceRoot":"","sources":["../../src/decorator/roles.decorator.ts"],"names":[],"mappings":"AAKA;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,eAAe,GAAG,cAAc,CAE1E;AAED,4GAA4G;AAC5G,wBAAgB,UAAU,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,eAAe,GAAG,cAAc,CAE/E"}
|
|
@@ -1,19 +1,17 @@
|
|
|
1
|
-
import '
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { Can } from '@nestjs/authorization';
|
|
2
|
+
import { permission, roles } from '../helpers.js';
|
|
3
|
+
import { PermissionPolicy } from '../policies/permission.policy.js';
|
|
4
|
+
import { RolePolicy } from '../policies/role.policy.js';
|
|
4
5
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* Roles come from the user object (`user.roles` / `user.role`) by default, plus the
|
|
10
|
-
* optional `ROLE_PROVIDER` seam (a persisted RBAC store) when registered — see
|
|
11
|
-
* {@link RolesGuard}.
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* `@Roles('admin', 'teacher')` // allow if the user is an admin OR a teacher
|
|
6
|
+
* Sugar for the official `@Can(RolePolicy, 'has', roles(...names))`: the user holds any
|
|
7
|
+
* of the roles. Stacks with other `@Can()`s (all must pass); a class-level `@Roles`
|
|
8
|
+
* applies to every handler, together with the method's.
|
|
15
9
|
*/
|
|
16
|
-
export function Roles(...
|
|
17
|
-
return
|
|
10
|
+
export function Roles(...names) {
|
|
11
|
+
return Can(RolePolicy, 'has', roles(...names));
|
|
12
|
+
}
|
|
13
|
+
/** Sugar for the official `@Can(PermissionPolicy, 'has', permission(...names))`: any of the permissions. */
|
|
14
|
+
export function Permission(...names) {
|
|
15
|
+
return Can(PermissionPolicy, 'has', permission(...names));
|
|
18
16
|
}
|
|
19
17
|
//# sourceMappingURL=roles.decorator.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"roles.decorator.js","sourceRoot":"","sources":["../../src/decorator/roles.decorator.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"roles.decorator.js","sourceRoot":"","sources":["../../src/decorator/roles.decorator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,uBAAuB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAExD;;;;GAIG;AACH,MAAM,UAAU,KAAK,CAAC,GAAG,KAAe;IACtC,OAAO,GAAG,CAAC,UAAU,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC;AACjD,CAAC;AAED,4GAA4G;AAC5G,MAAM,UAAU,UAAU,CAAC,GAAG,KAAe;IAC3C,OAAO,GAAG,CAAC,gBAAgB,EAAE,KAAK,EAAE,UAAU,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC;AAC5D,CAAC"}
|
package/dist/diagnostics.d.ts
CHANGED
|
@@ -10,9 +10,10 @@ import type { Resource, User } from './types.js';
|
|
|
10
10
|
*/
|
|
11
11
|
export declare const authzDecisionChannel: import("diagnostics_channel").Channel<unknown, unknown>;
|
|
12
12
|
/**
|
|
13
|
-
* Every authorization decision
|
|
14
|
-
* `
|
|
15
|
-
*
|
|
13
|
+
* Every authorization decision, allowed or denied: each `Gate` check, and each official
|
|
14
|
+
* `@Can()` requirement the guard enforced (`reason: 'route'`). The official package
|
|
15
|
+
* publishes only denials, on `nestjs:authorization:denied`; this channel carries both,
|
|
16
|
+
* for Telescope-style watchers.
|
|
16
17
|
*/
|
|
17
18
|
export interface AuthzDecisionDiagnostic {
|
|
18
19
|
v: 1;
|
|
@@ -22,6 +23,10 @@ export interface AuthzDecisionDiagnostic {
|
|
|
22
23
|
allowed: boolean;
|
|
23
24
|
/** Which resolution path decided it (see {@link AuthzDecisionReason}). */
|
|
24
25
|
reason: AuthzDecisionReason;
|
|
26
|
+
/** The official policy class that decided, e.g. `'PostPolicy'`, or `null` when none did. */
|
|
27
|
+
policy: string | null;
|
|
28
|
+
/** The handler the official guard checked (`Class.method`), or `null` for a `Gate` call. */
|
|
29
|
+
handler: string | null;
|
|
25
30
|
/** A label for the resolved user (`<type>#<id>` / constructor name), or `null` when anonymous. */
|
|
26
31
|
userRef: string | null;
|
|
27
32
|
/** The resource class name the ability targeted, or `null` for a model-less ability. */
|
|
@@ -30,10 +35,15 @@ export interface AuthzDecisionDiagnostic {
|
|
|
30
35
|
resourceId: string | number | null;
|
|
31
36
|
}
|
|
32
37
|
/** The resolution path that produced a decision — the "why" behind a 403 (or a grant). */
|
|
33
|
-
export type AuthzDecisionReason = 'super-admin' | 'decision-provider' | 'permission-provider' | 'policy
|
|
38
|
+
export type AuthzDecisionReason = 'super-admin' | 'decision-provider' | 'permission-provider' | 'policy' | 'gate' | 'anonymous' | 'route';
|
|
39
|
+
/** Where a decision came from, beyond the verdict. */
|
|
40
|
+
export interface AuthzDecisionSource {
|
|
41
|
+
policy?: string | null;
|
|
42
|
+
handler?: string | null;
|
|
43
|
+
}
|
|
34
44
|
/**
|
|
35
45
|
* Publish a decision to the channel, but only when something is listening. Never
|
|
36
46
|
* throws — building the payload or notifying a subscriber must not break a check.
|
|
37
47
|
*/
|
|
38
|
-
export declare function publishAuthzDecision(ability: string, allowed: boolean, reason: AuthzDecisionReason, user: User, resource: Resource | undefined): void;
|
|
48
|
+
export declare function publishAuthzDecision(ability: string, allowed: boolean, reason: AuthzDecisionReason, user: User, resource: Resource | undefined, source?: AuthzDecisionSource): void;
|
|
39
49
|
//# sourceMappingURL=diagnostics.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../src/diagnostics.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEjD;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,yDAAkC,CAAC;AAEpE
|
|
1
|
+
{"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../src/diagnostics.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEjD;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,yDAAkC,CAAC;AAEpE;;;;;GAKG;AACH,MAAM,WAAW,uBAAuB;IACtC,CAAC,EAAE,CAAC,CAAC;IACL,gEAAgE;IAChE,OAAO,EAAE,MAAM,CAAC;IAChB,gDAAgD;IAChD,OAAO,EAAE,OAAO,CAAC;IACjB,0EAA0E;IAC1E,MAAM,EAAE,mBAAmB,CAAC;IAC5B,4FAA4F;IAC5F,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,4FAA4F;IAC5F,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,kGAAkG;IAClG,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,wFAAwF;IACxF,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,0EAA0E;IAC1E,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;CACpC;AAED,0FAA0F;AAC1F,MAAM,MAAM,mBAAmB,GAC3B,aAAa,GACb,mBAAmB,GACnB,qBAAqB,GACrB,QAAQ,GACR,MAAM,GACN,WAAW,GACX,OAAO,CAAC;AAEZ,sDAAsD;AACtD,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,mBAAmB,EAC3B,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,QAAQ,GAAG,SAAS,EAC9B,MAAM,GAAE,mBAAwB,GAC/B,IAAI,CAgBN"}
|
package/dist/diagnostics.js
CHANGED
|
@@ -13,7 +13,7 @@ export const authzDecisionChannel = getChannel('authz', 'decision');
|
|
|
13
13
|
* Publish a decision to the channel, but only when something is listening. Never
|
|
14
14
|
* throws — building the payload or notifying a subscriber must not break a check.
|
|
15
15
|
*/
|
|
16
|
-
export function publishAuthzDecision(ability, allowed, reason, user, resource) {
|
|
16
|
+
export function publishAuthzDecision(ability, allowed, reason, user, resource, source = {}) {
|
|
17
17
|
if (!authzDecisionChannel.hasSubscribers)
|
|
18
18
|
return;
|
|
19
19
|
const payload = {
|
|
@@ -21,6 +21,8 @@ export function publishAuthzDecision(ability, allowed, reason, user, resource) {
|
|
|
21
21
|
ability,
|
|
22
22
|
allowed,
|
|
23
23
|
reason,
|
|
24
|
+
policy: source.policy ?? null,
|
|
25
|
+
handler: source.handler ?? null,
|
|
24
26
|
userRef: labelUser(user),
|
|
25
27
|
resourceType: resourceType(resource),
|
|
26
28
|
resourceId: resourceId(resource),
|
package/dist/diagnostics.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diagnostics.js","sourceRoot":"","sources":["../src/diagnostics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAGhE;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,UAAU,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"diagnostics.js","sourceRoot":"","sources":["../src/diagnostics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAGhE;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,UAAU,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AA4CpE;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAClC,OAAe,EACf,OAAgB,EAChB,MAA2B,EAC3B,IAAU,EACV,QAA8B,EAC9B,SAA8B,EAAE;IAEhC,IAAI,CAAC,oBAAoB,CAAC,cAAc;QAAE,OAAO;IACjD,MAAM,OAAO,GAA4B;QACvC,CAAC,EAAE,CAAC;QACJ,OAAO;QACP,OAAO;QACP,MAAM;QACN,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,IAAI;QAC7B,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,IAAI;QAC/B,OAAO,EAAE,SAAS,CAAC,IAAI,CAAC;QACxB,YAAY,EAAE,YAAY,CAAC,QAAQ,CAAC;QACpC,UAAU,EAAE,UAAU,CAAC,QAAQ,CAAC;KACjC,CAAC;IACF,gFAAgF;IAChF,+EAA+E;IAC/E,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;AACrC,CAAC;AAED,4FAA4F;AAC5F,SAAS,SAAS,CAAC,IAAU;IAC3B,IAAI,IAAI,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IAC9B,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC7B,MAAM,GAAG,GAAG,IAA+B,CAAC;QAC5C,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,WAAW,EAAE,IAAI,CAAuB,CAAC;QACvE,MAAM,EAAE,GAAG,GAAG,CAAC,EAAE,CAAC;QAClB,IAAI,IAAI,IAAI,CAAC,OAAO,EAAE,KAAK,QAAQ,IAAI,OAAO,EAAE,KAAK,QAAQ,CAAC;YAAE,OAAO,GAAG,IAAI,IAAI,EAAE,EAAE,CAAC;QACvF,IAAI,OAAO,GAAG,CAAC,EAAE,KAAK,QAAQ,IAAI,OAAO,GAAG,CAAC,EAAE,KAAK,QAAQ;YAAE,OAAO,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACpF,OAAO,GAAG,CAAC,WAAW,EAAE,IAAI,IAAI,IAAI,CAAC;IACvC,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC;AACtB,CAAC;AAED,sGAAsG;AACtG,SAAS,YAAY,CAAC,QAA8B;IAClD,IAAI,QAAQ,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IAClC,IAAI,OAAO,QAAQ,KAAK,UAAU;QAAE,OAAQ,QAA8B,CAAC,IAAI,IAAI,IAAI,CAAC;IACxF,OAAQ,QAAgD,CAAC,WAAW,EAAE,IAAI,IAAI,IAAI,CAAC;AACrF,CAAC;AAED,kFAAkF;AAClF,SAAS,UAAU,CAAC,QAA8B;IAChD,IAAI,QAAQ,IAAI,IAAI,IAAI,OAAO,QAAQ,KAAK,UAAU;QAAE,OAAO,IAAI,CAAC;IACpE,MAAM,EAAE,GAAI,QAAoC,CAAC,EAAE,CAAC;IACpD,OAAO,OAAO,EAAE,KAAK,QAAQ,IAAI,OAAO,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AACtE,CAAC"}
|
|
@@ -2,10 +2,6 @@ import { BadRequestException } from '@nestjs/common';
|
|
|
2
2
|
export declare abstract class AuthzException extends Error {
|
|
3
3
|
constructor(message: string);
|
|
4
4
|
}
|
|
5
|
-
export declare class PolicyNotDecoratedException extends AuthzException {
|
|
6
|
-
readonly policyName: string;
|
|
7
|
-
constructor(policyName: string);
|
|
8
|
-
}
|
|
9
5
|
/**
|
|
10
6
|
* Thrown when an ability cannot be matched to any policy method or ad-hoc gate.
|
|
11
7
|
*
|
|
@@ -30,8 +26,4 @@ export declare class AmbiguousAbilityException extends BadRequestException {
|
|
|
30
26
|
readonly name = "AmbiguousAbilityException";
|
|
31
27
|
constructor(ability: string, policyNames: string[]);
|
|
32
28
|
}
|
|
33
|
-
export declare class ResourceResolverMissingException extends AuthzException {
|
|
34
|
-
readonly ability: string;
|
|
35
|
-
constructor(ability: string);
|
|
36
|
-
}
|
|
37
29
|
//# sourceMappingURL=exceptions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"exceptions.d.ts","sourceRoot":"","sources":["../../src/errors/exceptions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAErD,8BAAsB,cAAe,SAAQ,KAAK;gBACpC,OAAO,EAAE,MAAM;CAI5B;AAED
|
|
1
|
+
{"version":3,"file":"exceptions.d.ts","sourceRoot":"","sources":["../../src/errors/exceptions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAErD,8BAAsB,cAAe,SAAQ,KAAK;gBACpC,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;GAMG;AACH,qBAAa,2BAA4B,SAAQ,mBAAmB;aAEtC,OAAO,EAAE,MAAM;IAD3C,SAAkB,IAAI,iCAAiC;gBAC3B,OAAO,EAAE,MAAM;CAK5C;AAED;;;;;GAKG;AACH,qBAAa,yBAA0B,SAAQ,mBAAmB;aAG9C,OAAO,EAAE,MAAM;aACf,WAAW,EAAE,MAAM,EAAE;IAHvC,SAAkB,IAAI,+BAA+B;gBAEnC,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EAAE;CAQxC"}
|
|
@@ -5,13 +5,6 @@ export class AuthzException extends Error {
|
|
|
5
5
|
this.name = new.target.name;
|
|
6
6
|
}
|
|
7
7
|
}
|
|
8
|
-
export class PolicyNotDecoratedException extends AuthzException {
|
|
9
|
-
policyName;
|
|
10
|
-
constructor(policyName) {
|
|
11
|
-
super(`${policyName} is missing @Policy(Resource). Decorate it with the resource class.`);
|
|
12
|
-
this.policyName = policyName;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
8
|
/**
|
|
16
9
|
* Thrown when an ability cannot be matched to any policy method or ad-hoc gate.
|
|
17
10
|
*
|
|
@@ -43,11 +36,4 @@ export class AmbiguousAbilityException extends BadRequestException {
|
|
|
43
36
|
this.policyNames = policyNames;
|
|
44
37
|
}
|
|
45
38
|
}
|
|
46
|
-
export class ResourceResolverMissingException extends AuthzException {
|
|
47
|
-
ability;
|
|
48
|
-
constructor(ability) {
|
|
49
|
-
super(`@Can('${ability}', Resource) needs an instance but no ResourceResolver is registered. Register one via AuthzModule.forRoot or pass the resource programmatically.`);
|
|
50
|
-
this.ability = ability;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
39
|
//# sourceMappingURL=exceptions.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"exceptions.js","sourceRoot":"","sources":["../../src/errors/exceptions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAErD,MAAM,OAAgB,cAAe,SAAQ,KAAK;IAChD,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC;IAC9B,CAAC;CACF;AAED
|
|
1
|
+
{"version":3,"file":"exceptions.js","sourceRoot":"","sources":["../../src/errors/exceptions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAErD,MAAM,OAAgB,cAAe,SAAQ,KAAK;IAChD,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC;IAC9B,CAAC;CACF;AAED;;;;;;GAMG;AACH,MAAM,OAAO,2BAA4B,SAAQ,mBAAmB;IAEtC;IADV,IAAI,GAAG,6BAA6B,CAAC;IACvD,YAA4B,OAAe;QACzC,KAAK,CACH,YAAY,OAAO,sEAAsE,OAAO,UAAU,CAC3G,CAAC;QAHwB,YAAO,GAAP,OAAO,CAAQ;IAI3C,CAAC;CACF;AAED;;;;;GAKG;AACH,MAAM,OAAO,yBAA0B,SAAQ,mBAAmB;IAG9C;IACA;IAHA,IAAI,GAAG,2BAA2B,CAAC;IACrD,YACkB,OAAe,EACf,WAAqB;QAErC,KAAK,CACH,YAAY,OAAO,uDAAuD,WAAW,CAAC,IAAI,CACxF,IAAI,CACL,4DAA4D,OAAO,eAAe,CACpF,CAAC;QAPc,YAAO,GAAP,OAAO,CAAQ;QACf,gBAAW,GAAX,WAAW,CAAU;IAOvC,CAAC;CACF"}
|