@grantjs/server 1.0.0 → 1.1.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/README.md +1 -1
- package/dist/debug-DICR8lwQ.js +15 -0
- package/dist/debug-DICR8lwQ.js.map +1 -0
- package/dist/debug-DInHA4QB.cjs +20 -0
- package/dist/debug-DInHA4QB.cjs.map +1 -0
- package/dist/errors-BUeTn_3G.cjs +91 -0
- package/dist/errors-BUeTn_3G.cjs.map +1 -0
- package/dist/errors-CSC_Z_KP.js +62 -0
- package/dist/errors-CSC_Z_KP.js.map +1 -0
- package/dist/express/index.d.ts +3 -3
- package/dist/express/index.d.ts.map +1 -1
- package/dist/express/middleware.d.ts +2 -2
- package/dist/express/middleware.d.ts.map +1 -1
- package/dist/express.cjs +106 -95
- package/dist/express.cjs.map +1 -1
- package/dist/express.mjs +101 -99
- package/dist/express.mjs.map +1 -1
- package/dist/fastify/index.d.ts +4 -4
- package/dist/fastify/index.d.ts.map +1 -1
- package/dist/fastify/plugin.d.ts +2 -2
- package/dist/fastify/plugin.d.ts.map +1 -1
- package/dist/fastify.cjs +133 -97
- package/dist/fastify.cjs.map +1 -1
- package/dist/fastify.mjs +128 -101
- package/dist/fastify.mjs.map +1 -1
- package/dist/grant-client-f6IKMyk5.cjs +228 -0
- package/dist/grant-client-f6IKMyk5.cjs.map +1 -0
- package/dist/grant-client-t3KadxFo.js +205 -0
- package/dist/grant-client-t3KadxFo.js.map +1 -0
- package/dist/grant-client.d.ts +1 -1
- package/dist/grant-client.d.ts.map +1 -1
- package/dist/index.cjs +10 -49
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +3 -50
- package/dist/nest/grant.guard.d.ts +1 -1
- package/dist/nest/grant.guard.d.ts.map +1 -1
- package/dist/nest/grant.module.d.ts.map +1 -1
- package/dist/nest/index.d.ts +6 -6
- package/dist/nest/index.d.ts.map +1 -1
- package/dist/nest.cjs +163 -143
- package/dist/nest.cjs.map +1 -1
- package/dist/nest.mjs +150 -149
- package/dist/nest.mjs.map +1 -1
- package/dist/next/index.d.ts +4 -4
- package/dist/next/index.d.ts.map +1 -1
- package/dist/next/with-grant.d.ts +1 -1
- package/dist/next/with-grant.d.ts.map +1 -1
- package/dist/next.cjs +75 -58
- package/dist/next.cjs.map +1 -1
- package/dist/next.mjs +70 -60
- package/dist/next.mjs.map +1 -1
- package/package.json +17 -17
- package/dist/debug-C8ibbrE5.js +0 -9
- package/dist/debug-C8ibbrE5.js.map +0 -1
- package/dist/debug-Crb5gNDM.cjs +0 -8
- package/dist/debug-Crb5gNDM.cjs.map +0 -1
- package/dist/grant-client-D1LZI2f4.js +0 -180
- package/dist/grant-client-D1LZI2f4.js.map +0 -1
- package/dist/grant-client-DywfJN5P.cjs +0 -179
- package/dist/grant-client-DywfJN5P.cjs.map +0 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.mjs.map +0 -1
package/dist/nest.cjs
CHANGED
|
@@ -1,154 +1,174 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
4
|
-
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
5
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
2
|
+
const require_grant_client = require("./grant-client-f6IKMyk5.cjs");
|
|
3
|
+
const require_debug = require("./debug-DInHA4QB.cjs");
|
|
4
|
+
let _nestjs_common = require("@nestjs/common");
|
|
5
|
+
//#region src/nest/grant.decorator.ts
|
|
6
|
+
/**
|
|
7
|
+
* Metadata key for Grant options (resource, action) on route handlers.
|
|
8
|
+
* Used by GrantGuard when options are not passed in the constructor.
|
|
9
|
+
*/
|
|
10
|
+
var GRANT_OPTIONS_KEY = "grant:options";
|
|
10
11
|
function Grant(resourceOrOptions, action) {
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
return (0, _nestjs_common.SetMetadata)(GRANT_OPTIONS_KEY, typeof resourceOrOptions === "string" && action !== void 0 ? {
|
|
13
|
+
resource: resourceOrOptions,
|
|
14
|
+
action
|
|
15
|
+
} : resourceOrOptions);
|
|
13
16
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
var __decorateClass = (decorators, target, key, kind) => {
|
|
42
|
-
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
43
|
-
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
44
|
-
if (decorator = decorators[i])
|
|
45
|
-
result = decorator(result) || result;
|
|
46
|
-
return result;
|
|
17
|
+
//#endregion
|
|
18
|
+
//#region \0@oxc-project+runtime@0.127.0/helpers/decorate.js
|
|
19
|
+
function __decorate(decorators, target, key, desc) {
|
|
20
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
21
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
22
|
+
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;
|
|
23
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
//#region src/nest/grant.module.ts
|
|
27
|
+
var _GrantModule;
|
|
28
|
+
var GRANT_CLIENT = "GrantClient";
|
|
29
|
+
var GrantModule = _GrantModule = class GrantModule {
|
|
30
|
+
static forRoot(config) {
|
|
31
|
+
const client = new require_grant_client.GrantClient(config);
|
|
32
|
+
return {
|
|
33
|
+
module: _GrantModule,
|
|
34
|
+
providers: [{
|
|
35
|
+
provide: require_grant_client.GrantClient,
|
|
36
|
+
useValue: client
|
|
37
|
+
}, {
|
|
38
|
+
provide: GRANT_CLIENT,
|
|
39
|
+
useValue: client
|
|
40
|
+
}],
|
|
41
|
+
exports: [require_grant_client.GrantClient, GRANT_CLIENT]
|
|
42
|
+
};
|
|
43
|
+
}
|
|
47
44
|
};
|
|
48
|
-
|
|
49
|
-
|
|
45
|
+
GrantModule = _GrantModule = __decorate([(0, _nestjs_common.Global)(), (0, _nestjs_common.Module)({})], GrantModule);
|
|
46
|
+
//#endregion
|
|
47
|
+
//#region \0@oxc-project+runtime@0.127.0/helpers/decorateMetadata.js
|
|
48
|
+
function __decorateMetadata(k, v) {
|
|
49
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
50
|
+
}
|
|
51
|
+
//#endregion
|
|
52
|
+
//#region \0@oxc-project+runtime@0.127.0/helpers/decorateParam.js
|
|
53
|
+
function __decorateParam(paramIndex, decorator) {
|
|
54
|
+
return function(target, key) {
|
|
55
|
+
decorator(target, key, paramIndex);
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
//#endregion
|
|
59
|
+
//#region src/nest/grant.guard.ts
|
|
60
|
+
var _ref;
|
|
61
|
+
/** Injection token for GrantGuard. Use with @UseGuards(GRANT_GUARD) so Nest resolves the guard from the container (with GrantClient injected). */
|
|
62
|
+
var GRANT_GUARD = "GrantGuard";
|
|
63
|
+
/**
|
|
64
|
+
* NestJS guard that checks Grant authorization before allowing access to a route.
|
|
65
|
+
*
|
|
66
|
+
* Use in one of two ways:
|
|
67
|
+
*
|
|
68
|
+
* 1. With decorator (resource/action from metadata):
|
|
69
|
+
* - Import GrantModule.forRoot() in AppModule. In a feature module, add
|
|
70
|
+
* `{ provide: GRANT_GUARD, useClass: GrantGuard }` and use @Grant + @UseGuards(GRANT_GUARD).
|
|
71
|
+
* - Using the GRANT_GUARD token ensures Nest resolves the guard from the container (GrantClient injected).
|
|
72
|
+
*
|
|
73
|
+
* 2. With explicit options (supports resourceResolver):
|
|
74
|
+
* - @UseGuards(new GrantGuard(grantClient, { resource: 'Document', action: 'Update', resourceResolver: async ({ request }) => ... }))
|
|
75
|
+
*
|
|
76
|
+
* On success, the authorization result is attached to the request as request.authorization.
|
|
77
|
+
* On failure, throws UnauthorizedException (401), ForbiddenException (403), or NotFoundException (404).
|
|
78
|
+
*/
|
|
50
79
|
function isGrantGuardOptions(x) {
|
|
51
|
-
|
|
80
|
+
return typeof x === "object" && x !== null && "resource" in x && "action" in x && typeof x.resource === "string" && typeof x.action === "string";
|
|
52
81
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
options.action,
|
|
121
|
-
{
|
|
122
|
-
context: {
|
|
123
|
-
resource: resolvedResource ?? void 0
|
|
124
|
-
}
|
|
125
|
-
},
|
|
126
|
-
request
|
|
127
|
-
);
|
|
128
|
-
debug.debugGrant("Nest", {
|
|
129
|
-
resource: options.resource,
|
|
130
|
-
action: options.action,
|
|
131
|
-
authorized: result.authorized,
|
|
132
|
-
...result.authorized ? {} : { reason: result.reason }
|
|
133
|
-
});
|
|
134
|
-
if (!result.authorized) {
|
|
135
|
-
throw new common.ForbiddenException({
|
|
136
|
-
error: "Forbidden",
|
|
137
|
-
code: "FORBIDDEN",
|
|
138
|
-
reason: result.reason
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
request.authorization = result;
|
|
142
|
-
return true;
|
|
143
|
-
}
|
|
82
|
+
var GrantGuard = class GrantGuard {
|
|
83
|
+
constructor(client, reflectorOrOptions) {
|
|
84
|
+
require_grant_client._defineProperty(this, "explicitOptions", void 0);
|
|
85
|
+
require_grant_client._defineProperty(this, "reflector", void 0);
|
|
86
|
+
this.client = client;
|
|
87
|
+
if (reflectorOrOptions && isGrantGuardOptions(reflectorOrOptions)) this.explicitOptions = reflectorOrOptions;
|
|
88
|
+
else this.reflector = reflectorOrOptions;
|
|
89
|
+
}
|
|
90
|
+
getOptionsFromContext(context) {
|
|
91
|
+
const handler = context.getHandler();
|
|
92
|
+
const clazz = context.getClass();
|
|
93
|
+
if (this.reflector) {
|
|
94
|
+
const fromHandler = this.reflector.get(GRANT_OPTIONS_KEY, handler);
|
|
95
|
+
const fromClass = this.reflector.get(GRANT_OPTIONS_KEY, clazz);
|
|
96
|
+
if (fromHandler) return fromHandler;
|
|
97
|
+
if (fromClass) return fromClass;
|
|
98
|
+
}
|
|
99
|
+
if (typeof Reflect !== "undefined" && typeof Reflect.getMetadata === "function") {
|
|
100
|
+
const fromHandler = Reflect.getMetadata(GRANT_OPTIONS_KEY, handler);
|
|
101
|
+
const fromClass = Reflect.getMetadata(GRANT_OPTIONS_KEY, clazz);
|
|
102
|
+
if (fromHandler) return fromHandler;
|
|
103
|
+
if (fromClass) return fromClass;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
async canActivate(context) {
|
|
107
|
+
const request = context.switchToHttp().getRequest();
|
|
108
|
+
const options = this.explicitOptions ?? this.getOptionsFromContext(context);
|
|
109
|
+
if (options?.resource && options?.action) require_debug.debugGrant("Nest", {
|
|
110
|
+
resource: options.resource,
|
|
111
|
+
action: options.action,
|
|
112
|
+
hasReflector: !!this.reflector,
|
|
113
|
+
handler: context.getHandler()?.name,
|
|
114
|
+
controller: context.getClass()?.name
|
|
115
|
+
});
|
|
116
|
+
if (!options || !options.resource || !options.action) throw new _nestjs_common.ForbiddenException("Grant options (resource, action) required. Use @Grant() or pass options to GrantGuard.");
|
|
117
|
+
if (!this.client) throw new _nestjs_common.ForbiddenException("GrantGuard: GrantClient not injected. Import GrantModule.forRoot() in AppModule and register GrantGuard as a provider.");
|
|
118
|
+
if (!await require_grant_client.extractTokenFromRequest(request, this.client.config)) throw new _nestjs_common.UnauthorizedException({
|
|
119
|
+
error: "Unauthorized",
|
|
120
|
+
code: "UNAUTHENTICATED"
|
|
121
|
+
});
|
|
122
|
+
let resolvedResource = null;
|
|
123
|
+
const opts = options;
|
|
124
|
+
if (opts.resourceResolver) {
|
|
125
|
+
resolvedResource = await opts.resourceResolver({
|
|
126
|
+
resourceSlug: options.resource,
|
|
127
|
+
request
|
|
128
|
+
});
|
|
129
|
+
if (!resolvedResource) throw new _nestjs_common.NotFoundException({
|
|
130
|
+
error: "Resource not found",
|
|
131
|
+
code: "NOT_FOUND"
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
const result = await this.client.isAuthorized(options.resource, options.action, { context: { resource: resolvedResource ?? void 0 } }, request);
|
|
135
|
+
require_debug.debugGrant("Nest", {
|
|
136
|
+
resource: options.resource,
|
|
137
|
+
action: options.action,
|
|
138
|
+
authorized: result.authorized,
|
|
139
|
+
...result.authorized ? {} : { reason: result.reason }
|
|
140
|
+
});
|
|
141
|
+
if (!result.authorized) throw new _nestjs_common.ForbiddenException({
|
|
142
|
+
error: "Forbidden",
|
|
143
|
+
code: "FORBIDDEN",
|
|
144
|
+
reason: result.reason
|
|
145
|
+
});
|
|
146
|
+
request.authorization = result;
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
144
149
|
};
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
+
GrantGuard = __decorate([
|
|
151
|
+
(0, _nestjs_common.Injectable)(),
|
|
152
|
+
__decorateParam(0, (0, _nestjs_common.Inject)(GRANT_CLIENT)),
|
|
153
|
+
__decorateMetadata("design:paramtypes", [typeof (_ref = typeof require_grant_client.GrantClient !== "undefined" && require_grant_client.GrantClient) === "function" ? _ref : Object, Object])
|
|
154
|
+
], GrantGuard);
|
|
155
|
+
//#endregion
|
|
150
156
|
exports.GRANT_CLIENT = GRANT_CLIENT;
|
|
151
157
|
exports.GRANT_GUARD = GRANT_GUARD;
|
|
152
158
|
exports.GRANT_OPTIONS_KEY = GRANT_OPTIONS_KEY;
|
|
153
159
|
exports.Grant = Grant;
|
|
154
|
-
|
|
160
|
+
exports.GrantClient = require_grant_client.GrantClient;
|
|
161
|
+
Object.defineProperty(exports, "GrantGuard", {
|
|
162
|
+
enumerable: true,
|
|
163
|
+
get: function() {
|
|
164
|
+
return GrantGuard;
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
Object.defineProperty(exports, "GrantModule", {
|
|
168
|
+
enumerable: true,
|
|
169
|
+
get: function() {
|
|
170
|
+
return GrantModule;
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
//# sourceMappingURL=nest.cjs.map
|
package/dist/nest.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nest.cjs","sources":["../src/nest/grant.decorator.ts","../src/nest/grant.module.ts","../src/nest/grant.guard.ts"],"sourcesContent":["import { SetMetadata } from '@nestjs/common';\n\n/**\n * Metadata key for Grant options (resource, action) on route handlers.\n * Used by GrantGuard when options are not passed in the constructor.\n */\nexport const GRANT_OPTIONS_KEY = 'grant:options';\n\n/**\n * Options for the Grant guard (resource, action; resourceResolver only via guard constructor).\n */\nexport interface GrantOptions {\n /** The resource slug to check (e.g., \"Organization\", \"Project\", \"Document\") */\n resource: string;\n /** The action to check (e.g., \"Query\", \"Create\", \"Update\", \"Delete\") */\n action: string;\n}\n\n/**\n * Decorator to require a Grant permission on a route.\n * Use with GrantGuard (injected) so the guard reads resource/action from this metadata.\n *\n * @example\n * ```ts\n * @Get()\n * @Grant('Document', 'Query')\n * @UseGuards(GrantGuard)\n * list() { ... }\n *\n * @Post()\n * @Grant('Document', 'Create')\n * @UseGuards(GrantGuard)\n * create(@Body() dto: CreateDocumentDto) { ... }\n * ```\n *\n * For dynamic resource resolution (resourceResolver), use the guard with explicit options:\n * `@UseGuards(new GrantGuard(grantClient, { resource: 'Document', action: 'Update', resourceResolver: ... }))`\n */\nexport function Grant(resource: string, action: string): ReturnType<typeof SetMetadata>;\nexport function Grant(options: GrantOptions): ReturnType<typeof SetMetadata>;\nexport function Grant(\n resourceOrOptions: string | GrantOptions,\n action?: string\n): ReturnType<typeof SetMetadata> {\n const options: GrantOptions =\n typeof resourceOrOptions === 'string' && action !== undefined\n ? { resource: resourceOrOptions, action }\n : (resourceOrOptions as GrantOptions);\n return SetMetadata(GRANT_OPTIONS_KEY, options);\n}\n","import { DynamicModule, Global, Module } from '@nestjs/common';\n\nimport { GrantClient } from '../grant-client';\n\nimport type { GrantServerConfig } from '../types';\n\nexport const GRANT_CLIENT = 'GrantClient';\n\n/**\n * NestJS module that provides GrantClient for injection.\n *\n * @example\n * ```ts\n * // app.module.ts\n * import { GrantModule } from '@grantjs/server/nest';\n *\n * @Module({\n * imports: [\n * GrantModule.forRoot({\n * apiUrl: process.env.GRANT_API_URL!,\n * getToken: (req) => req.headers?.authorization?.replace?.('Bearer ', '') ?? null,\n * }),\n * ],\n * })\n * export class AppModule {}\n * ```\n *\n * Then inject GrantClient in controllers and use with GrantGuard.\n */\n@Global()\n@Module({})\nexport class GrantModule {\n static forRoot(config: GrantServerConfig): DynamicModule {\n const client = new GrantClient(config);\n return {\n module: GrantModule,\n providers: [\n { provide: GrantClient, useValue: client },\n { provide: GRANT_CLIENT, useValue: client },\n ],\n exports: [GrantClient, GRANT_CLIENT],\n };\n }\n}\n","import {\n CanActivate,\n ExecutionContext,\n ForbiddenException,\n Inject,\n Injectable,\n NotFoundException,\n UnauthorizedException,\n} from '@nestjs/common';\nimport { Reflector } from '@nestjs/core';\n\nimport { GrantClient } from '../grant-client';\nimport { debugGrant } from '../utils/debug';\nimport { extractTokenFromRequest } from '../utils/token-extractor';\n\nimport { GRANT_OPTIONS_KEY } from './grant.decorator';\nimport { GRANT_CLIENT } from './grant.module';\n\n/** Injection token for GrantGuard. Use with @UseGuards(GRANT_GUARD) so Nest resolves the guard from the container (with GrantClient injected). */\nexport const GRANT_GUARD = 'GrantGuard';\n\nimport type { ResourceResolver, AuthorizationResult } from '../types';\nimport type { GrantOptions } from './grant.decorator';\n\n/**\n * Extended request type with Grant authorization result (for use in controllers).\n */\nexport interface AuthorizedRequest {\n authorization?: AuthorizationResult;\n}\n\n/**\n * Guard options when using explicit options (includes optional resourceResolver).\n */\nexport interface GrantGuardOptions {\n resource: string;\n action: string;\n resourceResolver?: ResourceResolver;\n}\n\n/**\n * NestJS guard that checks Grant authorization before allowing access to a route.\n *\n * Use in one of two ways:\n *\n * 1. With decorator (resource/action from metadata):\n * - Import GrantModule.forRoot() in AppModule. In a feature module, add\n * `{ provide: GRANT_GUARD, useClass: GrantGuard }` and use @Grant + @UseGuards(GRANT_GUARD).\n * - Using the GRANT_GUARD token ensures Nest resolves the guard from the container (GrantClient injected).\n *\n * 2. With explicit options (supports resourceResolver):\n * - @UseGuards(new GrantGuard(grantClient, { resource: 'Document', action: 'Update', resourceResolver: async ({ request }) => ... }))\n *\n * On success, the authorization result is attached to the request as request.authorization.\n * On failure, throws UnauthorizedException (401), ForbiddenException (403), or NotFoundException (404).\n */\nfunction isGrantGuardOptions(x: unknown): x is GrantGuardOptions {\n return (\n typeof x === 'object' &&\n x !== null &&\n 'resource' in x &&\n 'action' in x &&\n typeof (x as GrantGuardOptions).resource === 'string' &&\n typeof (x as GrantGuardOptions).action === 'string'\n );\n}\n\n@Injectable()\nexport class GrantGuard implements CanActivate {\n private readonly explicitOptions?: GrantGuardOptions;\n private readonly reflector?: Reflector;\n\n constructor(\n @Inject(GRANT_CLIENT) private readonly client: GrantClient,\n reflectorOrOptions?: Reflector | GrantGuardOptions\n ) {\n if (reflectorOrOptions && isGrantGuardOptions(reflectorOrOptions)) {\n this.explicitOptions = reflectorOrOptions;\n } else {\n this.reflector = reflectorOrOptions as Reflector | undefined;\n }\n }\n\n private getOptionsFromContext(context: ExecutionContext): GrantOptions | undefined {\n const handler = context.getHandler();\n const clazz = context.getClass();\n // Prefer Reflector when available (Nest DI)\n if (this.reflector) {\n const fromHandler = this.reflector.get<GrantOptions>(GRANT_OPTIONS_KEY, handler);\n const fromClass = this.reflector.get<GrantOptions>(GRANT_OPTIONS_KEY, clazz);\n if (fromHandler) return fromHandler;\n if (fromClass) return fromClass;\n }\n // Fallback: read metadata via Reflect (works when Reflector is not injected, e.g. ESM/CJS boundary)\n if (typeof Reflect !== 'undefined' && typeof Reflect.getMetadata === 'function') {\n const fromHandler = Reflect.getMetadata(GRANT_OPTIONS_KEY, handler) as\n | GrantOptions\n | undefined;\n const fromClass = Reflect.getMetadata(GRANT_OPTIONS_KEY, clazz) as GrantOptions | undefined;\n if (fromHandler) return fromHandler;\n if (fromClass) return fromClass;\n }\n return undefined;\n }\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const request = context.switchToHttp().getRequest();\n\n const options = this.explicitOptions ?? this.getOptionsFromContext(context);\n\n if (options?.resource && options?.action) {\n debugGrant('Nest', {\n resource: options.resource,\n action: options.action,\n hasReflector: !!this.reflector,\n handler: context.getHandler()?.name,\n controller: context.getClass()?.name,\n });\n }\n\n if (!options || !options.resource || !options.action) {\n throw new ForbiddenException(\n 'Grant options (resource, action) required. Use @Grant() or pass options to GrantGuard.'\n );\n }\n\n if (!this.client) {\n throw new ForbiddenException(\n 'GrantGuard: GrantClient not injected. Import GrantModule.forRoot() in AppModule and register GrantGuard as a provider.'\n );\n }\n\n const token = await extractTokenFromRequest(request, this.client.config);\n if (!token) {\n throw new UnauthorizedException({ error: 'Unauthorized', code: 'UNAUTHENTICATED' });\n }\n\n let resolvedResource: Record<string, unknown> | null = null;\n\n const opts = options as GrantGuardOptions;\n if (opts.resourceResolver) {\n resolvedResource = await opts.resourceResolver({\n resourceSlug: options.resource,\n request,\n });\n if (!resolvedResource) {\n throw new NotFoundException({ error: 'Resource not found', code: 'NOT_FOUND' });\n }\n }\n\n const result = await this.client.isAuthorized(\n options.resource,\n options.action,\n {\n context: {\n resource: resolvedResource ?? undefined,\n },\n },\n request\n );\n\n debugGrant('Nest', {\n resource: options.resource,\n action: options.action,\n authorized: result.authorized,\n ...(result.authorized ? {} : { reason: result.reason }),\n });\n\n if (!result.authorized) {\n throw new ForbiddenException({\n error: 'Forbidden',\n code: 'FORBIDDEN',\n reason: result.reason,\n });\n }\n\n (request as AuthorizedRequest).authorization = result;\n return true;\n }\n}\n"],"names":["SetMetadata","GrantModule","GrantClient","__decorateClass","Global","Module","GrantGuard","debugGrant","ForbiddenException","extractTokenFromRequest","UnauthorizedException","NotFoundException","Injectable"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"nest.cjs","names":[],"sources":["../src/nest/grant.decorator.ts","../src/nest/grant.module.ts","../src/nest/grant.guard.ts"],"sourcesContent":["import { SetMetadata } from '@nestjs/common';\n\n/**\n * Metadata key for Grant options (resource, action) on route handlers.\n * Used by GrantGuard when options are not passed in the constructor.\n */\nexport const GRANT_OPTIONS_KEY = 'grant:options';\n\n/**\n * Options for the Grant guard (resource, action; resourceResolver only via guard constructor).\n */\nexport interface GrantOptions {\n /** The resource slug to check (e.g., \"Organization\", \"Project\", \"Document\") */\n resource: string;\n /** The action to check (e.g., \"Query\", \"Create\", \"Update\", \"Delete\") */\n action: string;\n}\n\n/**\n * Decorator to require a Grant permission on a route.\n * Use with GrantGuard (injected) so the guard reads resource/action from this metadata.\n *\n * @example\n * ```ts\n * @Get()\n * @Grant('Document', 'Query')\n * @UseGuards(GrantGuard)\n * list() { ... }\n *\n * @Post()\n * @Grant('Document', 'Create')\n * @UseGuards(GrantGuard)\n * create(@Body() dto: CreateDocumentDto) { ... }\n * ```\n *\n * For dynamic resource resolution (resourceResolver), use the guard with explicit options:\n * `@UseGuards(new GrantGuard(grantClient, { resource: 'Document', action: 'Update', resourceResolver: ... }))`\n */\nexport function Grant(resource: string, action: string): ReturnType<typeof SetMetadata>;\nexport function Grant(options: GrantOptions): ReturnType<typeof SetMetadata>;\nexport function Grant(\n resourceOrOptions: string | GrantOptions,\n action?: string\n): ReturnType<typeof SetMetadata> {\n const options: GrantOptions =\n typeof resourceOrOptions === 'string' && action !== undefined\n ? { resource: resourceOrOptions, action }\n : (resourceOrOptions as GrantOptions);\n return SetMetadata(GRANT_OPTIONS_KEY, options);\n}\n","import { DynamicModule, Global, Module } from '@nestjs/common';\n\nimport { GrantClient } from '../grant-client';\nimport type { GrantServerConfig } from '../types';\n\nexport const GRANT_CLIENT = 'GrantClient';\n\n/**\n * NestJS module that provides GrantClient for injection.\n *\n * @example\n * ```ts\n * // app.module.ts\n * import { GrantModule } from '@grantjs/server/nest';\n *\n * @Module({\n * imports: [\n * GrantModule.forRoot({\n * apiUrl: process.env.GRANT_API_URL!,\n * getToken: (req) => req.headers?.authorization?.replace?.('Bearer ', '') ?? null,\n * }),\n * ],\n * })\n * export class AppModule {}\n * ```\n *\n * Then inject GrantClient in controllers and use with GrantGuard.\n */\n@Global()\n@Module({})\nexport class GrantModule {\n static forRoot(config: GrantServerConfig): DynamicModule {\n const client = new GrantClient(config);\n return {\n module: GrantModule,\n providers: [\n { provide: GrantClient, useValue: client },\n { provide: GRANT_CLIENT, useValue: client },\n ],\n exports: [GrantClient, GRANT_CLIENT],\n };\n }\n}\n","import {\n CanActivate,\n ExecutionContext,\n ForbiddenException,\n Inject,\n Injectable,\n NotFoundException,\n UnauthorizedException,\n} from '@nestjs/common';\nimport { Reflector } from '@nestjs/core';\n\nimport { GrantClient } from '../grant-client';\nimport { debugGrant } from '../utils/debug';\nimport { extractTokenFromRequest } from '../utils/token-extractor';\nimport { GRANT_OPTIONS_KEY } from './grant.decorator';\nimport { GRANT_CLIENT } from './grant.module';\n\n/** Injection token for GrantGuard. Use with @UseGuards(GRANT_GUARD) so Nest resolves the guard from the container (with GrantClient injected). */\nexport const GRANT_GUARD = 'GrantGuard';\n\nimport type { AuthorizationResult, ResourceResolver } from '../types';\nimport type { GrantOptions } from './grant.decorator';\n\n/**\n * Extended request type with Grant authorization result (for use in controllers).\n */\nexport interface AuthorizedRequest {\n authorization?: AuthorizationResult;\n}\n\n/**\n * Guard options when using explicit options (includes optional resourceResolver).\n */\nexport interface GrantGuardOptions {\n resource: string;\n action: string;\n resourceResolver?: ResourceResolver;\n}\n\n/**\n * NestJS guard that checks Grant authorization before allowing access to a route.\n *\n * Use in one of two ways:\n *\n * 1. With decorator (resource/action from metadata):\n * - Import GrantModule.forRoot() in AppModule. In a feature module, add\n * `{ provide: GRANT_GUARD, useClass: GrantGuard }` and use @Grant + @UseGuards(GRANT_GUARD).\n * - Using the GRANT_GUARD token ensures Nest resolves the guard from the container (GrantClient injected).\n *\n * 2. With explicit options (supports resourceResolver):\n * - @UseGuards(new GrantGuard(grantClient, { resource: 'Document', action: 'Update', resourceResolver: async ({ request }) => ... }))\n *\n * On success, the authorization result is attached to the request as request.authorization.\n * On failure, throws UnauthorizedException (401), ForbiddenException (403), or NotFoundException (404).\n */\nfunction isGrantGuardOptions(x: unknown): x is GrantGuardOptions {\n return (\n typeof x === 'object' &&\n x !== null &&\n 'resource' in x &&\n 'action' in x &&\n typeof (x as GrantGuardOptions).resource === 'string' &&\n typeof (x as GrantGuardOptions).action === 'string'\n );\n}\n\n@Injectable()\nexport class GrantGuard implements CanActivate {\n private readonly explicitOptions?: GrantGuardOptions;\n private readonly reflector?: Reflector;\n\n constructor(\n @Inject(GRANT_CLIENT) private readonly client: GrantClient,\n reflectorOrOptions?: Reflector | GrantGuardOptions\n ) {\n if (reflectorOrOptions && isGrantGuardOptions(reflectorOrOptions)) {\n this.explicitOptions = reflectorOrOptions;\n } else {\n this.reflector = reflectorOrOptions as Reflector | undefined;\n }\n }\n\n private getOptionsFromContext(context: ExecutionContext): GrantOptions | undefined {\n const handler = context.getHandler();\n const clazz = context.getClass();\n // Prefer Reflector when available (Nest DI)\n if (this.reflector) {\n const fromHandler = this.reflector.get<GrantOptions>(GRANT_OPTIONS_KEY, handler);\n const fromClass = this.reflector.get<GrantOptions>(GRANT_OPTIONS_KEY, clazz);\n if (fromHandler) return fromHandler;\n if (fromClass) return fromClass;\n }\n // Fallback: read metadata via Reflect (works when Reflector is not injected, e.g. ESM/CJS boundary)\n if (typeof Reflect !== 'undefined' && typeof Reflect.getMetadata === 'function') {\n const fromHandler = Reflect.getMetadata(GRANT_OPTIONS_KEY, handler) as\n | GrantOptions\n | undefined;\n const fromClass = Reflect.getMetadata(GRANT_OPTIONS_KEY, clazz) as GrantOptions | undefined;\n if (fromHandler) return fromHandler;\n if (fromClass) return fromClass;\n }\n return undefined;\n }\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const request = context.switchToHttp().getRequest();\n\n const options = this.explicitOptions ?? this.getOptionsFromContext(context);\n\n if (options?.resource && options?.action) {\n debugGrant('Nest', {\n resource: options.resource,\n action: options.action,\n hasReflector: !!this.reflector,\n handler: context.getHandler()?.name,\n controller: context.getClass()?.name,\n });\n }\n\n if (!options || !options.resource || !options.action) {\n throw new ForbiddenException(\n 'Grant options (resource, action) required. Use @Grant() or pass options to GrantGuard.'\n );\n }\n\n if (!this.client) {\n throw new ForbiddenException(\n 'GrantGuard: GrantClient not injected. Import GrantModule.forRoot() in AppModule and register GrantGuard as a provider.'\n );\n }\n\n const token = await extractTokenFromRequest(request, this.client.config);\n if (!token) {\n throw new UnauthorizedException({ error: 'Unauthorized', code: 'UNAUTHENTICATED' });\n }\n\n let resolvedResource: Record<string, unknown> | null = null;\n\n const opts = options as GrantGuardOptions;\n if (opts.resourceResolver) {\n resolvedResource = await opts.resourceResolver({\n resourceSlug: options.resource,\n request,\n });\n if (!resolvedResource) {\n throw new NotFoundException({ error: 'Resource not found', code: 'NOT_FOUND' });\n }\n }\n\n const result = await this.client.isAuthorized(\n options.resource,\n options.action,\n {\n context: {\n resource: resolvedResource ?? undefined,\n },\n },\n request\n );\n\n debugGrant('Nest', {\n resource: options.resource,\n action: options.action,\n authorized: result.authorized,\n ...(result.authorized ? {} : { reason: result.reason }),\n });\n\n if (!result.authorized) {\n throw new ForbiddenException({\n error: 'Forbidden',\n code: 'FORBIDDEN',\n reason: result.reason,\n });\n }\n\n (request as AuthorizedRequest).authorization = result;\n return true;\n }\n}\n"],"mappings":"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"}
|