@dudousxd/nestjs-authz 0.2.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 +7 -0
- package/LICENSE +21 -0
- package/dist/context-accessor.d.ts +31 -0
- package/dist/context-accessor.d.ts.map +1 -0
- package/dist/context-accessor.js +2 -0
- package/dist/context-accessor.js.map +1 -0
- package/dist/decorator/can.decorator.d.ts +29 -0
- package/dist/decorator/can.decorator.d.ts.map +1 -0
- package/dist/decorator/can.decorator.js +23 -0
- package/dist/decorator/can.decorator.js.map +1 -0
- package/dist/decorator/policy.decorator.d.ts +18 -0
- package/dist/decorator/policy.decorator.d.ts.map +1 -0
- package/dist/decorator/policy.decorator.js +28 -0
- package/dist/decorator/policy.decorator.js.map +1 -0
- package/dist/errors/exceptions.d.ts +37 -0
- package/dist/errors/exceptions.d.ts.map +1 -0
- package/dist/errors/exceptions.js +53 -0
- package/dist/errors/exceptions.js.map +1 -0
- package/dist/gate.d.ts +81 -0
- package/dist/gate.d.ts.map +1 -0
- package/dist/gate.js +247 -0
- package/dist/gate.js.map +1 -0
- package/dist/guard/can.guard.d.ts +31 -0
- package/dist/guard/can.guard.d.ts.map +1 -0
- package/dist/guard/can.guard.js +92 -0
- package/dist/guard/can.guard.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/module.d.ts +20 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +194 -0
- package/dist/module.js.map +1 -0
- package/dist/permission-provider.d.ts +23 -0
- package/dist/permission-provider.d.ts.map +1 -0
- package/dist/permission-provider.js +2 -0
- package/dist/permission-provider.js.map +1 -0
- package/dist/policy-registry.d.ts +23 -0
- package/dist/policy-registry.d.ts.map +1 -0
- package/dist/policy-registry.js +49 -0
- package/dist/policy-registry.js.map +1 -0
- package/dist/resource-resolver.d.ts +29 -0
- package/dist/resource-resolver.d.ts.map +1 -0
- package/dist/resource-resolver.js +24 -0
- package/dist/resource-resolver.js.map +1 -0
- package/dist/tokens.d.ts +31 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +31 -0
- package/dist/tokens.js.map +1 -0
- package/dist/types.d.ts +97 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +71 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type CanActivate, type ExecutionContext } from '@nestjs/common';
|
|
2
|
+
import { ModuleRef, Reflector } from '@nestjs/core';
|
|
3
|
+
import { Gate } from '../gate.js';
|
|
4
|
+
import type { ResourceResolver } from '../resource-resolver.js';
|
|
5
|
+
/**
|
|
6
|
+
* Enforces `@Can(ability, Resource?)` on routes.
|
|
7
|
+
*
|
|
8
|
+
* - No `@Can` metadata → allow (the guard is inert on un-annotated routes).
|
|
9
|
+
* - Resource class + not class-level → load instance via {@link ResourceResolver}
|
|
10
|
+
* (a missing/undefined instance is a denial).
|
|
11
|
+
* - Class-level or no resource → dispatch against the class / ad-hoc gate.
|
|
12
|
+
*
|
|
13
|
+
* The decision is delegated to {@link Gate.authorize}, which throws
|
|
14
|
+
* `ForbiddenException` on denial.
|
|
15
|
+
*/
|
|
16
|
+
export declare class CanGuard implements CanActivate {
|
|
17
|
+
private readonly reflector;
|
|
18
|
+
private readonly gate;
|
|
19
|
+
private readonly moduleRef;
|
|
20
|
+
private readonly resolver?;
|
|
21
|
+
constructor(reflector: Reflector, gate: Gate, moduleRef: ModuleRef, resolver?: ResourceResolver | undefined);
|
|
22
|
+
/**
|
|
23
|
+
* Locate the resource resolver. Prefers the value injected into this module;
|
|
24
|
+
* falls back to a non-strict {@link ModuleRef} lookup so a resolver provided by
|
|
25
|
+
* ANY module (e.g. the app root) is found even though the guard lives in the
|
|
26
|
+
* global AuthzModule.
|
|
27
|
+
*/
|
|
28
|
+
private resolveResolver;
|
|
29
|
+
canActivate(ctx: ExecutionContext): Promise<boolean>;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=can.guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"can.guard.d.ts","sourceRoot":"","sources":["../../src/guard/can.guard.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,gBAAgB,EAKtB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGpD,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAClC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAIhE;;;;;;;;;;GAUG;AACH,qBACa,QAAS,YAAW,WAAW;IAExC,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAG1B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;gBALT,SAAS,EAAE,SAAS,EACpB,IAAI,EAAE,IAAI,EACV,SAAS,EAAE,SAAS,EAGpB,QAAQ,CAAC,EAAE,gBAAgB,YAAA;IAG9C;;;;;OAKG;IACH,OAAO,CAAC,eAAe;IASjB,WAAW,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;CA2B3D"}
|
|
@@ -0,0 +1,92 @@
|
|
|
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 { ForbiddenException, Inject, Injectable, Optional, } from '@nestjs/common';
|
|
14
|
+
import { ModuleRef, Reflector } from '@nestjs/core';
|
|
15
|
+
import { ResourceResolverMissingException } from '../errors/exceptions.js';
|
|
16
|
+
import { Gate } from '../gate.js';
|
|
17
|
+
import { CAN_METADATA, RESOURCE_RESOLVER } from '../tokens.js';
|
|
18
|
+
/**
|
|
19
|
+
* Enforces `@Can(ability, Resource?)` on routes.
|
|
20
|
+
*
|
|
21
|
+
* - No `@Can` metadata → allow (the guard is inert on un-annotated routes).
|
|
22
|
+
* - Resource class + not class-level → load instance via {@link ResourceResolver}
|
|
23
|
+
* (a missing/undefined instance is a denial).
|
|
24
|
+
* - Class-level or no resource → dispatch against the class / ad-hoc gate.
|
|
25
|
+
*
|
|
26
|
+
* The decision is delegated to {@link Gate.authorize}, which throws
|
|
27
|
+
* `ForbiddenException` on denial.
|
|
28
|
+
*/
|
|
29
|
+
let CanGuard = class CanGuard {
|
|
30
|
+
reflector;
|
|
31
|
+
gate;
|
|
32
|
+
moduleRef;
|
|
33
|
+
resolver;
|
|
34
|
+
constructor(reflector, gate, moduleRef, resolver) {
|
|
35
|
+
this.reflector = reflector;
|
|
36
|
+
this.gate = gate;
|
|
37
|
+
this.moduleRef = moduleRef;
|
|
38
|
+
this.resolver = resolver;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Locate the resource resolver. Prefers the value injected into this module;
|
|
42
|
+
* falls back to a non-strict {@link ModuleRef} lookup so a resolver provided by
|
|
43
|
+
* ANY module (e.g. the app root) is found even though the guard lives in the
|
|
44
|
+
* global AuthzModule.
|
|
45
|
+
*/
|
|
46
|
+
resolveResolver() {
|
|
47
|
+
if (this.resolver)
|
|
48
|
+
return this.resolver;
|
|
49
|
+
try {
|
|
50
|
+
return this.moduleRef.get(RESOURCE_RESOLVER, { strict: false });
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
async canActivate(ctx) {
|
|
57
|
+
const meta = this.reflector.getAllAndOverride(CAN_METADATA, [
|
|
58
|
+
ctx.getHandler(),
|
|
59
|
+
ctx.getClass(),
|
|
60
|
+
]);
|
|
61
|
+
if (!meta)
|
|
62
|
+
return true;
|
|
63
|
+
// Ad-hoc gate or class-level → no instance to load.
|
|
64
|
+
if (!meta.resource || meta.classLevel) {
|
|
65
|
+
const resource = meta.classLevel ? meta.resource : undefined;
|
|
66
|
+
await this.gate.authorize(meta.ability, resource);
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
// Instance ability → resolve the resource by route param / app resolver.
|
|
70
|
+
const resolver = this.resolveResolver();
|
|
71
|
+
if (!resolver) {
|
|
72
|
+
throw new ResourceResolverMissingException(meta.ability);
|
|
73
|
+
}
|
|
74
|
+
const instance = await resolver.resolve(meta.resource, ctx);
|
|
75
|
+
if (instance === undefined) {
|
|
76
|
+
// Not found → deny rather than dispatch a policy with a bogus resource.
|
|
77
|
+
throw new ForbiddenException(`Unauthorized: ${meta.ability}`);
|
|
78
|
+
}
|
|
79
|
+
await this.gate.authorize(meta.ability, instance);
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
CanGuard = __decorate([
|
|
84
|
+
Injectable(),
|
|
85
|
+
__param(3, Optional()),
|
|
86
|
+
__param(3, Inject(RESOURCE_RESOLVER)),
|
|
87
|
+
__metadata("design:paramtypes", [Reflector,
|
|
88
|
+
Gate,
|
|
89
|
+
ModuleRef, Object])
|
|
90
|
+
], CanGuard);
|
|
91
|
+
export { CanGuard };
|
|
92
|
+
//# sourceMappingURL=can.guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"can.guard.js","sourceRoot":"","sources":["../../src/guard/can.guard.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,OAAO,EAGL,kBAAkB,EAClB,MAAM,EACN,UAAU,EACV,QAAQ,GACT,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEpD,OAAO,EAAE,gCAAgC,EAAE,MAAM,yBAAyB,CAAC;AAC3E,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAElC,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAG/D;;;;;;;;;;GAUG;AAEI,IAAM,QAAQ,GAAd,MAAM,QAAQ;IAEA;IACA;IACA;IAGA;IANnB,YACmB,SAAoB,EACpB,IAAU,EACV,SAAoB,EAGpB,QAA2B;QAL3B,cAAS,GAAT,SAAS,CAAW;QACpB,SAAI,GAAJ,IAAI,CAAM;QACV,cAAS,GAAT,SAAS,CAAW;QAGpB,aAAQ,GAAR,QAAQ,CAAmB;IAC3C,CAAC;IAEJ;;;;;OAKG;IACK,eAAe;QACrB,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAC,QAAQ,CAAC;QACxC,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAmB,iBAAiB,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;QACpF,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,GAAqB;QACrC,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,iBAAiB,CAA0B,YAAY,EAAE;YACnF,GAAG,CAAC,UAAU,EAAE;YAChB,GAAG,CAAC,QAAQ,EAAE;SACf,CAAC,CAAC;QACH,IAAI,CAAC,IAAI;YAAE,OAAO,IAAI,CAAC;QAEvB,oDAAoD;QACpD,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACtC,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAE,IAAI,CAAC,QAAgC,CAAC,CAAC,CAAC,SAAS,CAAC;YACtF,MAAM,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;YAClD,OAAO,IAAI,CAAC;QACd,CAAC;QAED,yEAAyE;QACzE,MAAM,QAAQ,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;QACxC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,gCAAgC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC3D,CAAC;QACD,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QAC5D,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,wEAAwE;YACxE,MAAM,IAAI,kBAAkB,CAAC,iBAAiB,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;QAChE,CAAC;QACD,MAAM,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAClD,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAA;AApDY,QAAQ;IADpB,UAAU,EAAE;IAMR,WAAA,QAAQ,EAAE,CAAA;IACV,WAAA,MAAM,CAAC,iBAAiB,CAAC,CAAA;qCAJE,SAAS;QACd,IAAI;QACC,SAAS;GAJ5B,QAAQ,CAoDpB"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare const VERSION = "0.0.0";
|
|
2
|
+
export { Gate, BoundGate } from './gate.js';
|
|
3
|
+
export { PolicyRegistry } from './policy-registry.js';
|
|
4
|
+
export { Policy, getPolicyResource } from './decorator/policy.decorator.js';
|
|
5
|
+
export { Can } from './decorator/can.decorator.js';
|
|
6
|
+
export type { CanMetadata, CanOptions } from './decorator/can.decorator.js';
|
|
7
|
+
export { CanGuard } from './guard/can.guard.js';
|
|
8
|
+
export { AuthzModule } from './module.js';
|
|
9
|
+
export { IdParamResourceResolver } from './resource-resolver.js';
|
|
10
|
+
export type { ResourceResolver } from './resource-resolver.js';
|
|
11
|
+
export type { ContextAccessor, ContextStore, UserRef } from './context-accessor.js';
|
|
12
|
+
export type { PermissionProvider } from './permission-provider.js';
|
|
13
|
+
export { AUTHZ_MODULE_OPTIONS, RESOURCE_RESOLVER, CONTEXT_ACCESSOR, PERMISSION_PROVIDER, POLICY_RESOURCE_METADATA, CAN_METADATA, } from './tokens.js';
|
|
14
|
+
export { AuthzException, PolicyNotDecoratedException, AbilityNotResolvedException, AmbiguousAbilityException, ResourceResolverMissingException, } from './errors/exceptions.js';
|
|
15
|
+
export type { AuthzModuleOptions, AuthzModuleAsyncOptions, AuthzModuleOptionsFactory, GateFn, PolicyBeforeHook, PolicyInstance, PolicyMethod, Resource, SuperAdminHook, User, } from './types.js';
|
|
16
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,OAAO,UAAU,CAAC;AAE/B,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAC5E,OAAO,EAAE,GAAG,EAAE,MAAM,8BAA8B,CAAC;AACnD,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAC5E,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,YAAY,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACpF,YAAY,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,gBAAgB,EAChB,mBAAmB,EACnB,wBAAwB,EACxB,YAAY,GACb,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,cAAc,EACd,2BAA2B,EAC3B,2BAA2B,EAC3B,yBAAyB,EACzB,gCAAgC,GACjC,MAAM,wBAAwB,CAAC;AAChC,YAAY,EACV,kBAAkB,EAClB,uBAAuB,EACvB,yBAAyB,EACzB,MAAM,EACN,gBAAgB,EAChB,cAAc,EACd,YAAY,EACZ,QAAQ,EACR,cAAc,EACd,IAAI,GACL,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const VERSION = '0.0.0';
|
|
2
|
+
export { Gate, BoundGate } from './gate.js';
|
|
3
|
+
export { PolicyRegistry } from './policy-registry.js';
|
|
4
|
+
export { Policy, getPolicyResource } from './decorator/policy.decorator.js';
|
|
5
|
+
export { Can } from './decorator/can.decorator.js';
|
|
6
|
+
export { CanGuard } from './guard/can.guard.js';
|
|
7
|
+
export { AuthzModule } from './module.js';
|
|
8
|
+
export { IdParamResourceResolver } from './resource-resolver.js';
|
|
9
|
+
export { AUTHZ_MODULE_OPTIONS, RESOURCE_RESOLVER, CONTEXT_ACCESSOR, PERMISSION_PROVIDER, POLICY_RESOURCE_METADATA, CAN_METADATA, } from './tokens.js';
|
|
10
|
+
export { AuthzException, PolicyNotDecoratedException, AbilityNotResolvedException, AmbiguousAbilityException, ResourceResolverMissingException, } from './errors/exceptions.js';
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC;AAE/B,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAC5E,OAAO,EAAE,GAAG,EAAE,MAAM,8BAA8B,CAAC;AAEnD,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAIjE,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,gBAAgB,EAChB,mBAAmB,EACnB,wBAAwB,EACxB,YAAY,GACb,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,cAAc,EACd,2BAA2B,EAC3B,2BAA2B,EAC3B,yBAAyB,EACzB,gCAAgC,GACjC,MAAM,wBAAwB,CAAC"}
|
package/dist/module.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type DynamicModule } from '@nestjs/common';
|
|
2
|
+
import type { AuthzModuleAsyncOptions, AuthzModuleOptions } from './types.js';
|
|
3
|
+
export declare class AuthzModule {
|
|
4
|
+
static forRoot(options?: AuthzModuleOptions): DynamicModule;
|
|
5
|
+
static forRootAsync(options: AuthzModuleAsyncOptions): DynamicModule;
|
|
6
|
+
/**
|
|
7
|
+
* Register a default {@link ResourceResolver} bound to {@link RESOURCE_RESOLVER}
|
|
8
|
+
* so `@Can('update', Post)` works out-of-the-box on a clean install (no manual
|
|
9
|
+
* resolver wiring needed). Reads the resolved options so a `resourceResolver`
|
|
10
|
+
* override / `idParam` knob from forRoot OR forRootAsync is honored.
|
|
11
|
+
*/
|
|
12
|
+
private static resourceResolverProviders;
|
|
13
|
+
/**
|
|
14
|
+
* Provider that populates the {@link PolicyRegistry} on init (explicit policies
|
|
15
|
+
* + auto-discovered `@Policy` providers). Registered for both forRoot/forRootAsync.
|
|
16
|
+
*/
|
|
17
|
+
private static bootstrapProviders;
|
|
18
|
+
private static buildAsyncOptionsProvider;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=module.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,aAAa,EAQnB,MAAM,gBAAgB,CAAC;AAQxB,OAAO,KAAK,EACV,uBAAuB,EACvB,kBAAkB,EAGnB,MAAM,YAAY,CAAC;AAmEpB,qBACa,WAAW;IACtB,MAAM,CAAC,OAAO,CAAC,OAAO,GAAE,kBAAuB,GAAG,aAAa;IA2B/D,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,uBAAuB,GAAG,aAAa;IAoBpE;;;;;OAKG;IACH,OAAO,CAAC,MAAM,CAAC,yBAAyB;IAWxC;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,kBAAkB;IAIjC,OAAO,CAAC,MAAM,CAAC,yBAAyB;CA2BzC"}
|
package/dist/module.js
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
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
|
+
var AuthzModule_1;
|
|
14
|
+
import { Inject, Injectable, Module, Optional, } from '@nestjs/common';
|
|
15
|
+
import { APP_GUARD, DiscoveryModule, DiscoveryService, ModuleRef } from '@nestjs/core';
|
|
16
|
+
import { getPolicyResource } from './decorator/policy.decorator.js';
|
|
17
|
+
import { Gate } from './gate.js';
|
|
18
|
+
import { CanGuard } from './guard/can.guard.js';
|
|
19
|
+
import { PolicyRegistry } from './policy-registry.js';
|
|
20
|
+
import { IdParamResourceResolver } from './resource-resolver.js';
|
|
21
|
+
import { AUTHZ_MODULE_OPTIONS, RESOURCE_RESOLVER } from './tokens.js';
|
|
22
|
+
/**
|
|
23
|
+
* Populates the {@link PolicyRegistry} at boot from explicit `policies: []` and
|
|
24
|
+
* auto-discovered `@Policy`-decorated providers.
|
|
25
|
+
*
|
|
26
|
+
* Explicit policies are read from the RESOLVED {@link AUTHZ_MODULE_OPTIONS} (so a
|
|
27
|
+
* `forRootAsync` `useFactory`/`useClass` that returns `policies: [...]` registers
|
|
28
|
+
* them too) and resolved as provider instances via {@link ModuleRef}.
|
|
29
|
+
*/
|
|
30
|
+
let AuthzPolicyBootstrap = class AuthzPolicyBootstrap {
|
|
31
|
+
registry;
|
|
32
|
+
discovery;
|
|
33
|
+
moduleRef;
|
|
34
|
+
options;
|
|
35
|
+
constructor(registry, discovery, moduleRef, options) {
|
|
36
|
+
this.registry = registry;
|
|
37
|
+
this.discovery = discovery;
|
|
38
|
+
this.moduleRef = moduleRef;
|
|
39
|
+
this.options = options;
|
|
40
|
+
}
|
|
41
|
+
async onModuleInit() {
|
|
42
|
+
const seen = new Set();
|
|
43
|
+
// 1. Explicit `policies: []` from resolved options (forRoot AND forRootAsync).
|
|
44
|
+
for (const PolicyClass of this.options?.policies ?? []) {
|
|
45
|
+
if (seen.has(PolicyClass))
|
|
46
|
+
continue;
|
|
47
|
+
const instance = await this.resolvePolicyInstance(PolicyClass);
|
|
48
|
+
if (instance) {
|
|
49
|
+
seen.add(PolicyClass);
|
|
50
|
+
this.registry.register(instance);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// 2. Auto-discovered @Policy-decorated providers.
|
|
54
|
+
for (const wrapper of this.discovery.getProviders()) {
|
|
55
|
+
const instance = wrapper.instance;
|
|
56
|
+
if (!instance || typeof instance !== 'object')
|
|
57
|
+
continue;
|
|
58
|
+
const ctor = instance.constructor;
|
|
59
|
+
if (!ctor || seen.has(ctor))
|
|
60
|
+
continue;
|
|
61
|
+
if (getPolicyResource(instance)) {
|
|
62
|
+
seen.add(ctor);
|
|
63
|
+
this.registry.register(instance);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Resolve a policy class to an instance. Prefers an existing provider (so DI
|
|
69
|
+
* deps are honored); falls back to instantiating it through the container when
|
|
70
|
+
* the class was passed via `forRootAsync` options but not registered as a
|
|
71
|
+
* provider anywhere.
|
|
72
|
+
*/
|
|
73
|
+
async resolvePolicyInstance(PolicyClass) {
|
|
74
|
+
try {
|
|
75
|
+
return this.moduleRef.get(PolicyClass, { strict: false });
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// not a registered provider — instantiate via the container.
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
return await this.moduleRef.create(PolicyClass);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
AuthzPolicyBootstrap = __decorate([
|
|
89
|
+
Injectable(),
|
|
90
|
+
__param(3, Optional()),
|
|
91
|
+
__param(3, Inject(AUTHZ_MODULE_OPTIONS)),
|
|
92
|
+
__metadata("design:paramtypes", [PolicyRegistry,
|
|
93
|
+
DiscoveryService,
|
|
94
|
+
ModuleRef, Object])
|
|
95
|
+
], AuthzPolicyBootstrap);
|
|
96
|
+
let AuthzModule = AuthzModule_1 = class AuthzModule {
|
|
97
|
+
static forRoot(options = {}) {
|
|
98
|
+
const policyProviders = (options.policies ?? []).map((P) => P);
|
|
99
|
+
return {
|
|
100
|
+
module: AuthzModule_1,
|
|
101
|
+
global: true,
|
|
102
|
+
imports: [DiscoveryModule],
|
|
103
|
+
providers: [
|
|
104
|
+
{ provide: AUTHZ_MODULE_OPTIONS, useValue: options },
|
|
105
|
+
...policyProviders,
|
|
106
|
+
PolicyRegistry,
|
|
107
|
+
Gate,
|
|
108
|
+
CanGuard,
|
|
109
|
+
{ provide: APP_GUARD, useExisting: CanGuard },
|
|
110
|
+
...AuthzModule_1.resourceResolverProviders(),
|
|
111
|
+
...AuthzModule_1.bootstrapProviders(),
|
|
112
|
+
],
|
|
113
|
+
exports: [
|
|
114
|
+
Gate,
|
|
115
|
+
PolicyRegistry,
|
|
116
|
+
CanGuard,
|
|
117
|
+
AUTHZ_MODULE_OPTIONS,
|
|
118
|
+
RESOURCE_RESOLVER,
|
|
119
|
+
...(options.policies ?? []),
|
|
120
|
+
],
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
static forRootAsync(options) {
|
|
124
|
+
const asyncProvider = AuthzModule_1.buildAsyncOptionsProvider(options);
|
|
125
|
+
const asyncProviders = Array.isArray(asyncProvider) ? asyncProvider : [asyncProvider];
|
|
126
|
+
return {
|
|
127
|
+
module: AuthzModule_1,
|
|
128
|
+
global: true,
|
|
129
|
+
imports: [DiscoveryModule, ...(options.imports ?? [])],
|
|
130
|
+
providers: [
|
|
131
|
+
...asyncProviders,
|
|
132
|
+
PolicyRegistry,
|
|
133
|
+
Gate,
|
|
134
|
+
CanGuard,
|
|
135
|
+
{ provide: APP_GUARD, useExisting: CanGuard },
|
|
136
|
+
...AuthzModule_1.resourceResolverProviders(),
|
|
137
|
+
...AuthzModule_1.bootstrapProviders(),
|
|
138
|
+
],
|
|
139
|
+
exports: [Gate, PolicyRegistry, CanGuard, AUTHZ_MODULE_OPTIONS, RESOURCE_RESOLVER],
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Register a default {@link ResourceResolver} bound to {@link RESOURCE_RESOLVER}
|
|
144
|
+
* so `@Can('update', Post)` works out-of-the-box on a clean install (no manual
|
|
145
|
+
* resolver wiring needed). Reads the resolved options so a `resourceResolver`
|
|
146
|
+
* override / `idParam` knob from forRoot OR forRootAsync is honored.
|
|
147
|
+
*/
|
|
148
|
+
static resourceResolverProviders() {
|
|
149
|
+
return [
|
|
150
|
+
{
|
|
151
|
+
provide: RESOURCE_RESOLVER,
|
|
152
|
+
useFactory: (options) => options?.resourceResolver ?? new IdParamResourceResolver(options?.idParam),
|
|
153
|
+
inject: [{ token: AUTHZ_MODULE_OPTIONS, optional: true }],
|
|
154
|
+
},
|
|
155
|
+
];
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Provider that populates the {@link PolicyRegistry} on init (explicit policies
|
|
159
|
+
* + auto-discovered `@Policy` providers). Registered for both forRoot/forRootAsync.
|
|
160
|
+
*/
|
|
161
|
+
static bootstrapProviders() {
|
|
162
|
+
return [AuthzPolicyBootstrap];
|
|
163
|
+
}
|
|
164
|
+
static buildAsyncOptionsProvider(options) {
|
|
165
|
+
if (options.useFactory) {
|
|
166
|
+
return {
|
|
167
|
+
provide: AUTHZ_MODULE_OPTIONS,
|
|
168
|
+
useFactory: options.useFactory,
|
|
169
|
+
inject: (options.inject ?? []),
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
if (options.useClass) {
|
|
173
|
+
return [
|
|
174
|
+
{ provide: options.useClass, useClass: options.useClass },
|
|
175
|
+
{
|
|
176
|
+
provide: AUTHZ_MODULE_OPTIONS,
|
|
177
|
+
useFactory: async (factory) => factory.createAuthzOptions(),
|
|
178
|
+
inject: [options.useClass],
|
|
179
|
+
},
|
|
180
|
+
];
|
|
181
|
+
}
|
|
182
|
+
const factoryClass = options.useExisting;
|
|
183
|
+
return {
|
|
184
|
+
provide: AUTHZ_MODULE_OPTIONS,
|
|
185
|
+
useFactory: async (factory) => factory.createAuthzOptions(),
|
|
186
|
+
inject: [factoryClass],
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
AuthzModule = AuthzModule_1 = __decorate([
|
|
191
|
+
Module({})
|
|
192
|
+
], AuthzModule);
|
|
193
|
+
export { AuthzModule };
|
|
194
|
+
//# sourceMappingURL=module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module.js","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Resource, User } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Optional seam that lets a persisted RBAC layer answer named, model-less abilities.
|
|
4
|
+
*
|
|
5
|
+
* This is the Laravel/spatie `Gate::before` grant: when the current user holds the
|
|
6
|
+
* named permission, the ability is allowed regardless of policies/gates. The core
|
|
7
|
+
* does NOT implement this — `@dudousxd/nestjs-authz-typeorm` (and the other RBAC
|
|
8
|
+
* adapters) provide an implementation and register it under the shared
|
|
9
|
+
* {@link PERMISSION_PROVIDER} token. When no provider is registered the Gate is
|
|
10
|
+
* unaffected.
|
|
11
|
+
*
|
|
12
|
+
* Semantics (grant-only, mirroring `Gate::before`):
|
|
13
|
+
* - `true` → the user holds the permission → allow (short-circuit).
|
|
14
|
+
* - `false`/`undefined` → the provider does not grant it → fall through to the
|
|
15
|
+
* normal policy/gate resolution.
|
|
16
|
+
*
|
|
17
|
+
* `userRef` is the current user (whatever the app's auth layer produced; `undefined`
|
|
18
|
+
* when anonymous). `permission` is the ability name passed to `gate.allows(...)`.
|
|
19
|
+
*/
|
|
20
|
+
export interface PermissionProvider {
|
|
21
|
+
hasPermission(user: User, permission: string, resource?: Resource): boolean | undefined | Promise<boolean | undefined>;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=permission-provider.d.ts.map
|
|
@@ -0,0 +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;;;;;;;;;;;;;;;;;GAiBG;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;CACvD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permission-provider.js","sourceRoot":"","sources":["../src/permission-provider.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type Type } from '@nestjs/common';
|
|
2
|
+
import type { PolicyInstance } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Maps a resource class → its policy instance. Populated at module init from
|
|
5
|
+
* explicit `policies: []` and/or auto-discovered `@Policy` providers.
|
|
6
|
+
*/
|
|
7
|
+
export declare class PolicyRegistry {
|
|
8
|
+
private readonly byResource;
|
|
9
|
+
/**
|
|
10
|
+
* Register a policy instance. Throws if the instance's class was not decorated
|
|
11
|
+
* with `@Policy(Resource)`.
|
|
12
|
+
*/
|
|
13
|
+
register(policy: PolicyInstance): void;
|
|
14
|
+
/** Resolve the policy instance for a resource class, or `undefined`. */
|
|
15
|
+
forResource(resource: Type<unknown>): PolicyInstance | undefined;
|
|
16
|
+
/** Resolve the policy for the class of a resource instance, or `undefined`. */
|
|
17
|
+
forInstance(instance: object): PolicyInstance | undefined;
|
|
18
|
+
/** True when a policy is registered for the resource class. */
|
|
19
|
+
has(resource: Type<unknown>): boolean;
|
|
20
|
+
/** All registered policies (for introspection/testing). */
|
|
21
|
+
all(): PolicyInstance[];
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=policy-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy-registry.d.ts","sourceRoot":"","sources":["../src/policy-registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAGvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD;;;GAGG;AACH,qBACa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA4C;IAEvE;;;OAGG;IACH,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI;IAStC,wEAAwE;IACxE,WAAW,CAAC,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,cAAc,GAAG,SAAS;IAIhE,+EAA+E;IAC/E,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAIzD,+DAA+D;IAC/D,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,OAAO;IAIrC,2DAA2D;IAC3D,GAAG,IAAI,cAAc,EAAE;CAGxB"}
|
|
@@ -0,0 +1,49 @@
|
|
|
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
|
+
import { Injectable } from '@nestjs/common';
|
|
8
|
+
import { getPolicyResource } from './decorator/policy.decorator.js';
|
|
9
|
+
import { PolicyNotDecoratedException } from './errors/exceptions.js';
|
|
10
|
+
/**
|
|
11
|
+
* Maps a resource class → its policy instance. Populated at module init from
|
|
12
|
+
* explicit `policies: []` and/or auto-discovered `@Policy` providers.
|
|
13
|
+
*/
|
|
14
|
+
let PolicyRegistry = class PolicyRegistry {
|
|
15
|
+
byResource = new Map();
|
|
16
|
+
/**
|
|
17
|
+
* Register a policy instance. Throws if the instance's class was not decorated
|
|
18
|
+
* with `@Policy(Resource)`.
|
|
19
|
+
*/
|
|
20
|
+
register(policy) {
|
|
21
|
+
const resource = getPolicyResource(policy);
|
|
22
|
+
if (!resource) {
|
|
23
|
+
const name = policy.constructor?.name ?? 'Policy';
|
|
24
|
+
throw new PolicyNotDecoratedException(name);
|
|
25
|
+
}
|
|
26
|
+
this.byResource.set(resource, policy);
|
|
27
|
+
}
|
|
28
|
+
/** Resolve the policy instance for a resource class, or `undefined`. */
|
|
29
|
+
forResource(resource) {
|
|
30
|
+
return this.byResource.get(resource);
|
|
31
|
+
}
|
|
32
|
+
/** Resolve the policy for the class of a resource instance, or `undefined`. */
|
|
33
|
+
forInstance(instance) {
|
|
34
|
+
return this.byResource.get(instance.constructor);
|
|
35
|
+
}
|
|
36
|
+
/** True when a policy is registered for the resource class. */
|
|
37
|
+
has(resource) {
|
|
38
|
+
return this.byResource.has(resource);
|
|
39
|
+
}
|
|
40
|
+
/** All registered policies (for introspection/testing). */
|
|
41
|
+
all() {
|
|
42
|
+
return [...this.byResource.values()];
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
PolicyRegistry = __decorate([
|
|
46
|
+
Injectable()
|
|
47
|
+
], PolicyRegistry);
|
|
48
|
+
export { PolicyRegistry };
|
|
49
|
+
//# sourceMappingURL=policy-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy-registry.js","sourceRoot":"","sources":["../src/policy-registry.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,UAAU,EAAa,MAAM,gBAAgB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,2BAA2B,EAAE,MAAM,wBAAwB,CAAC;AAGrE;;;GAGG;AAEI,IAAM,cAAc,GAApB,MAAM,cAAc;IACR,UAAU,GAAG,IAAI,GAAG,EAAiC,CAAC;IAEvE;;;OAGG;IACH,QAAQ,CAAC,MAAsB;QAC7B,MAAM,QAAQ,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;QAC3C,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,GAAG,MAAM,CAAC,WAAW,EAAE,IAAI,IAAI,QAAQ,CAAC;YAClD,MAAM,IAAI,2BAA2B,CAAC,IAAI,CAAC,CAAC;QAC9C,CAAC;QACD,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACxC,CAAC;IAED,wEAAwE;IACxE,WAAW,CAAC,QAAuB;QACjC,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACvC,CAAC;IAED,+EAA+E;IAC/E,WAAW,CAAC,QAAgB;QAC1B,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,WAA4B,CAAC,CAAC;IACpE,CAAC;IAED,+DAA+D;IAC/D,GAAG,CAAC,QAAuB;QACzB,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACvC,CAAC;IAED,2DAA2D;IAC3D,GAAG;QACD,OAAO,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC;IACvC,CAAC;CACF,CAAA;AAnCY,cAAc;IAD1B,UAAU,EAAE;GACA,cAAc,CAmC1B"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ExecutionContext, Type } from '@nestjs/common';
|
|
2
|
+
/**
|
|
3
|
+
* Resolves a resource instance for a `@Can(ability, Resource)` route so the guard
|
|
4
|
+
* can pass it to the policy method. Apps register an implementation (typically
|
|
5
|
+
* ORM-backed) via `AuthzModule.forRoot({ ... })` or as a provider bound to the
|
|
6
|
+
* {@link RESOURCE_RESOLVER} token.
|
|
7
|
+
*
|
|
8
|
+
* Class-level abilities (no resource class on `@Can`) never call the resolver.
|
|
9
|
+
*/
|
|
10
|
+
export interface ResourceResolver {
|
|
11
|
+
/**
|
|
12
|
+
* Load the resource instance for `resource` in the given request context.
|
|
13
|
+
* Return `undefined` to signal "not found" — the guard treats a missing
|
|
14
|
+
* resource as a denial.
|
|
15
|
+
*/
|
|
16
|
+
resolve(resource: Type<unknown>, ctx: ExecutionContext): Promise<object | undefined> | object | undefined;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Default resolver: reads the route `:id` param and produces a lightweight
|
|
20
|
+
* `{ id }` shim of the requested resource class. This keeps the guard usable
|
|
21
|
+
* out-of-the-box (e.g. ownership checks on `resource.id`) without an ORM, while
|
|
22
|
+
* apps that need real entities register their own {@link ResourceResolver}.
|
|
23
|
+
*/
|
|
24
|
+
export declare class IdParamResourceResolver implements ResourceResolver {
|
|
25
|
+
private readonly param;
|
|
26
|
+
constructor(param?: string);
|
|
27
|
+
resolve(resource: Type<unknown>, ctx: ExecutionContext): object | undefined;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=resource-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource-resolver.d.ts","sourceRoot":"","sources":["../src/resource-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAE7D;;;;;;;GAOG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;OAIG;IACH,OAAO,CACL,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,EACvB,GAAG,EAAE,gBAAgB,GACpB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,GAAG,MAAM,GAAG,SAAS,CAAC;CACrD;AAED;;;;;GAKG;AACH,qBAAa,uBAAwB,YAAW,gBAAgB;IAClD,OAAO,CAAC,QAAQ,CAAC,KAAK;gBAAL,KAAK,GAAE,MAAa;IAEjD,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,gBAAgB,GAAG,MAAM,GAAG,SAAS;CAU5E"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default resolver: reads the route `:id` param and produces a lightweight
|
|
3
|
+
* `{ id }` shim of the requested resource class. This keeps the guard usable
|
|
4
|
+
* out-of-the-box (e.g. ownership checks on `resource.id`) without an ORM, while
|
|
5
|
+
* apps that need real entities register their own {@link ResourceResolver}.
|
|
6
|
+
*/
|
|
7
|
+
export class IdParamResourceResolver {
|
|
8
|
+
param;
|
|
9
|
+
constructor(param = 'id') {
|
|
10
|
+
this.param = param;
|
|
11
|
+
}
|
|
12
|
+
resolve(resource, ctx) {
|
|
13
|
+
const req = ctx.switchToHttp().getRequest();
|
|
14
|
+
const id = req?.params?.[this.param];
|
|
15
|
+
if (id === undefined)
|
|
16
|
+
return undefined;
|
|
17
|
+
// Construct a shaped instance so `instance.constructor === resource`, which
|
|
18
|
+
// is how PolicyRegistry maps an instance back to its policy.
|
|
19
|
+
const instance = Object.create(resource.prototype);
|
|
20
|
+
instance.id = id;
|
|
21
|
+
return instance;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=resource-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource-resolver.js","sourceRoot":"","sources":["../src/resource-resolver.ts"],"names":[],"mappings":"AAsBA;;;;;GAKG;AACH,MAAM,OAAO,uBAAuB;IACL;IAA7B,YAA6B,QAAgB,IAAI;QAApB,UAAK,GAAL,KAAK,CAAe;IAAG,CAAC;IAErD,OAAO,CAAC,QAAuB,EAAE,GAAqB;QACpD,MAAM,GAAG,GAAG,GAAG,CAAC,YAAY,EAAE,CAAC,UAAU,EAAwC,CAAC;QAClF,MAAM,EAAE,GAAG,GAAG,EAAE,MAAM,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrC,IAAI,EAAE,KAAK,SAAS;YAAE,OAAO,SAAS,CAAC;QACvC,4EAA4E;QAC5E,6DAA6D;QAC7D,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAA4B,CAAC;QAC9E,QAAQ,CAAC,EAAE,GAAG,EAAE,CAAC;QACjB,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF"}
|
package/dist/tokens.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** Injection token holding the resolved {@link AuthzModuleOptions}. */
|
|
2
|
+
export declare const AUTHZ_MODULE_OPTIONS: unique symbol;
|
|
3
|
+
/** Injection token for the optional {@link ResourceResolver} registered by the app. */
|
|
4
|
+
export declare const RESOURCE_RESOLVER: unique symbol;
|
|
5
|
+
/** Metadata key for `@Policy(Resource)` — stores the resource class on the policy. */
|
|
6
|
+
export declare const POLICY_RESOURCE_METADATA = "nestjs-authz:policy-resource";
|
|
7
|
+
/** Metadata key for `@Can(ability, Resource?)` — stores the ability descriptor on a route. */
|
|
8
|
+
export declare const CAN_METADATA = "nestjs-authz:can";
|
|
9
|
+
/**
|
|
10
|
+
* Cross-lib injection token for an optional {@link PermissionProvider} — the seam the
|
|
11
|
+
* RBAC adapter (`@dudousxd/nestjs-authz-typeorm`) registers so that a model-less,
|
|
12
|
+
* named ability (`gate.allows('posts.publish')`) is granted when the current user
|
|
13
|
+
* holds that persisted permission. This is the Laravel/spatie `Gate::before` grant.
|
|
14
|
+
*
|
|
15
|
+
* `Symbol.for(key)` uses the global symbol registry so an RBAC package registering
|
|
16
|
+
* this same key resolves to the SAME symbol instance without importing core internals.
|
|
17
|
+
* The token is consulted with `@Optional()` — when absent, the Gate behaves exactly
|
|
18
|
+
* as before (backward-compatible: unknown abilities still throw).
|
|
19
|
+
*/
|
|
20
|
+
export declare const PERMISSION_PROVIDER: unique symbol;
|
|
21
|
+
/**
|
|
22
|
+
* Cross-lib injection token for the current-request context accessor, owned by
|
|
23
|
+
* `@dudousxd/nestjs-context`. We do NOT import nestjs-context — instead we share
|
|
24
|
+
* its well-known token by value so DI resolves the same provider when present.
|
|
25
|
+
*
|
|
26
|
+
* `Symbol.for(key)` uses the global symbol registry, so this resolves to the
|
|
27
|
+
* SAME symbol instance as nestjs-context's `tokens.ts` without any import.
|
|
28
|
+
* The key MUST stay byte-identical with nestjs-context's export.
|
|
29
|
+
*/
|
|
30
|
+
export declare const CONTEXT_ACCESSOR: unique symbol;
|
|
31
|
+
//# sourceMappingURL=tokens.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,eAAO,MAAM,oBAAoB,eAA+C,CAAC;AAEjF,uFAAuF;AACvF,eAAO,MAAM,iBAAiB,eAAyD,CAAC;AAExF,sFAAsF;AACtF,eAAO,MAAM,wBAAwB,iCAAiC,CAAC;AAEvE,8FAA8F;AAC9F,eAAO,MAAM,YAAY,qBAAqB,CAAC;AAE/C;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB,eAA2D,CAAC;AAE5F;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,eAAkD,CAAC"}
|