@caronte-sdk/node 0.2.0 → 0.2.1
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 +34 -34
- package/dist/adapters/apollo.cjs +9 -9
- package/dist/adapters/apollo.cjs.map +1 -1
- package/dist/adapters/apollo.d.cts +10 -10
- package/dist/adapters/apollo.d.ts +10 -10
- package/dist/adapters/apollo.js +8 -8
- package/dist/adapters/apollo.js.map +1 -1
- package/dist/adapters/express.cjs +5 -5
- package/dist/adapters/express.cjs.map +1 -1
- package/dist/adapters/express.d.cts +6 -6
- package/dist/adapters/express.d.ts +6 -6
- package/dist/adapters/express.js +5 -5
- package/dist/adapters/express.js.map +1 -1
- package/dist/adapters/fastify.cjs +9 -9
- package/dist/adapters/fastify.cjs.map +1 -1
- package/dist/adapters/fastify.d.cts +7 -7
- package/dist/adapters/fastify.d.ts +7 -7
- package/dist/adapters/fastify.js +9 -9
- package/dist/adapters/fastify.js.map +1 -1
- package/dist/adapters/nest/index.cjs +38 -38
- package/dist/adapters/nest/index.cjs.map +1 -1
- package/dist/adapters/nest/index.d.cts +15 -15
- package/dist/adapters/nest/index.d.ts +15 -15
- package/dist/adapters/nest/index.js +24 -24
- package/dist/adapters/nest/index.js.map +1 -1
- package/dist/chunk-VEDVQOBD.js +21 -0
- package/dist/chunk-VEDVQOBD.js.map +1 -0
- package/dist/{chunk-G73YTEJQ.js → chunk-XMA2NAR7.js} +15 -15
- package/dist/chunk-XMA2NAR7.js.map +1 -0
- package/dist/{client-Cgm1csuM.d.cts → client-TcOSwgrn.d.cts} +5 -5
- package/dist/{client-Cgm1csuM.d.ts → client-TcOSwgrn.d.ts} +5 -5
- package/dist/index.cjs +24 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -9
- package/dist/index.d.ts +9 -9
- package/dist/index.js +2 -2
- package/package.json +2 -2
- package/dist/chunk-AKUM2UEO.js +0 -21
- package/dist/chunk-AKUM2UEO.js.map +0 -1
- package/dist/chunk-G73YTEJQ.js.map +0 -1
|
@@ -25,30 +25,30 @@ function registerOperation(op) {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
// src/adapters/fastify.ts
|
|
28
|
-
var
|
|
29
|
-
fastify.decorateRequest("
|
|
28
|
+
var carontePlugin = async (fastify, { client }) => {
|
|
29
|
+
fastify.decorateRequest("caronteUser", void 0);
|
|
30
30
|
fastify.addHook("onRequest", async (request) => {
|
|
31
31
|
const auth = request.headers.authorization ?? "";
|
|
32
32
|
const [scheme, token] = auth.split(" ");
|
|
33
33
|
if (scheme?.toLowerCase() === "bearer" && token) {
|
|
34
34
|
try {
|
|
35
|
-
request.
|
|
35
|
+
request.caronteUser = await client.validateToken(token);
|
|
36
36
|
} catch {
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
});
|
|
40
40
|
fastify.decorate(
|
|
41
|
-
"
|
|
41
|
+
"caronteGuard",
|
|
42
42
|
(id, level, method) => {
|
|
43
43
|
const resolvedMethod = method ?? detectMethod(id);
|
|
44
44
|
registerOperation({ identifier: id, method: resolvedMethod, level });
|
|
45
45
|
return async (request, reply) => {
|
|
46
46
|
if (level === "public") return;
|
|
47
|
-
if (!request.
|
|
47
|
+
if (!request.caronteUser) {
|
|
48
48
|
await reply.status(401).send({ error: "unauthorized", detail: "Bearer token required." });
|
|
49
49
|
return;
|
|
50
50
|
}
|
|
51
|
-
const allowed = client.checkPermission(request.
|
|
51
|
+
const allowed = client.checkPermission(request.caronteUser, id, resolvedMethod);
|
|
52
52
|
if (!allowed) {
|
|
53
53
|
await reply.status(403).send({ error: "forbidden", detail: "Insufficient permissions." });
|
|
54
54
|
}
|
|
@@ -56,11 +56,11 @@ var argosPlugin = async (fastify, { client }) => {
|
|
|
56
56
|
}
|
|
57
57
|
);
|
|
58
58
|
};
|
|
59
|
-
var
|
|
60
|
-
name: "
|
|
59
|
+
var caronteFastifyPlugin = fp__default.default(carontePlugin, {
|
|
60
|
+
name: "caronte",
|
|
61
61
|
fastify: ">=4"
|
|
62
62
|
});
|
|
63
63
|
|
|
64
|
-
exports.
|
|
64
|
+
exports.caronteFastifyPlugin = caronteFastifyPlugin;
|
|
65
65
|
//# sourceMappingURL=fastify.cjs.map
|
|
66
66
|
//# sourceMappingURL=fastify.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/registry.ts","../../src/adapters/fastify.ts"],"names":["fp"],"mappings":";;;;;;;;;;;AAEA,IAAM,YAAmC,EAAC;AAKnC,SAAS,aAAa,EAAA,EAAoB;AAC/C,EAAA,MAAM,IAAA,GAAA,CAAQ,GAAG,KAAA,CAAM,GAAG,EAAE,GAAA,EAAI,IAAK,IAAI,WAAA,EAAY;AACrD,EAAA,IAAI,wBAAA,CAAyB,IAAA,CAAK,IAAI,CAAA,EAAS,OAAO,MAAA;AACtD,EAAA,IAAI,0BAAA,CAA2B,IAAA,CAAK,IAAI,CAAA,EAAQ,OAAO,QAAA;AACvD,EAAA,IAAI,iCAAA,CAAkC,IAAA,CAAK,IAAI,CAAA,EAAG,OAAO,QAAA;AACzD,EAAA,OAAO,OAAA;AACT;AAGO,SAAS,kBAAkB,EAAA,EAA+B;AAC/D,EAAA,MAAM,SAAS,SAAA,CAAU,IAAA;AAAA,IACvB,OAAK,CAAA,CAAE,UAAA,KAAe,GAAG,UAAA,IAAc,CAAA,CAAE,WAAW,EAAA,CAAG;AAAA,GACzD;AACA,EAAA,IAAI,CAAC,MAAA,EAAQ,SAAA,CAAU,IAAA,CAAK,EAAE,CAAA;AAChC;;;ACLA,IAAM,
|
|
1
|
+
{"version":3,"sources":["../../src/registry.ts","../../src/adapters/fastify.ts"],"names":["fp"],"mappings":";;;;;;;;;;;AAEA,IAAM,YAAmC,EAAC;AAKnC,SAAS,aAAa,EAAA,EAAoB;AAC/C,EAAA,MAAM,IAAA,GAAA,CAAQ,GAAG,KAAA,CAAM,GAAG,EAAE,GAAA,EAAI,IAAK,IAAI,WAAA,EAAY;AACrD,EAAA,IAAI,wBAAA,CAAyB,IAAA,CAAK,IAAI,CAAA,EAAS,OAAO,MAAA;AACtD,EAAA,IAAI,0BAAA,CAA2B,IAAA,CAAK,IAAI,CAAA,EAAQ,OAAO,QAAA;AACvD,EAAA,IAAI,iCAAA,CAAkC,IAAA,CAAK,IAAI,CAAA,EAAG,OAAO,QAAA;AACzD,EAAA,OAAO,OAAA;AACT;AAGO,SAAS,kBAAkB,EAAA,EAA+B;AAC/D,EAAA,MAAM,SAAS,SAAA,CAAU,IAAA;AAAA,IACvB,OAAK,CAAA,CAAE,UAAA,KAAe,GAAG,UAAA,IAAc,CAAA,CAAE,WAAW,EAAA,CAAG;AAAA,GACzD;AACA,EAAA,IAAI,CAAC,MAAA,EAAQ,SAAA,CAAU,IAAA,CAAK,EAAE,CAAA;AAChC;;;ACLA,IAAM,aAAA,GAA0D,OAC9D,OAAA,EACA,EAAE,QAAO,KACN;AAEH,EAAA,OAAA,CAAQ,eAAA,CAAyC,eAAe,MAAS,CAAA;AAGzE,EAAA,OAAA,CAAQ,OAAA,CAAQ,WAAA,EAAa,OAAO,OAAA,KAA4B;AAC9D,IAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,OAAA,CAAQ,aAAA,IAAiB,EAAA;AAC9C,IAAA,MAAM,CAAC,MAAA,EAAQ,KAAK,CAAA,GAAI,IAAA,CAAK,MAAM,GAAG,CAAA;AACtC,IAAA,IAAI,MAAA,EAAQ,WAAA,EAAY,KAAM,QAAA,IAAY,KAAA,EAAO;AAC/C,MAAA,IAAI;AACF,QAAA,OAAA,CAAQ,WAAA,GAAc,MAAM,MAAA,CAAO,aAAA,CAAc,KAAK,CAAA;AAAA,MACxD,CAAA,CAAA,MAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF,CAAC,CAAA;AAGD,EAAA,OAAA,CAAQ,QAAA;AAAA,IACN,cAAA;AAAA,IACA,CAAC,EAAA,EAAY,KAAA,EAAe,MAAA,KAAoB;AAC9C,MAAA,MAAM,cAAA,GAAiB,MAAA,IAAU,YAAA,CAAa,EAAE,CAAA;AAChD,MAAA,iBAAA,CAAkB,EAAE,UAAA,EAAY,EAAA,EAAI,MAAA,EAAQ,cAAA,EAAgB,OAAO,CAAA;AAEnE,MAAA,OAAO,OAAO,SAAyB,KAAA,KAAuC;AAC5E,QAAA,IAAI,UAAU,QAAA,EAAU;AAExB,QAAA,IAAI,CAAC,QAAQ,WAAA,EAAa;AACxB,UAAA,MAAM,KAAA,CAAM,MAAA,CAAO,GAAG,CAAA,CAAE,IAAA,CAAK,EAAE,KAAA,EAAO,cAAA,EAAgB,MAAA,EAAQ,wBAAA,EAA0B,CAAA;AACxF,UAAA;AAAA,QACF;AAEA,QAAA,MAAM,UAAU,MAAA,CAAO,eAAA,CAAgB,OAAA,CAAQ,WAAA,EAAa,IAAI,cAAc,CAAA;AAC9E,QAAA,IAAI,CAAC,OAAA,EAAS;AACZ,UAAA,MAAM,KAAA,CAAM,MAAA,CAAO,GAAG,CAAA,CAAE,IAAA,CAAK,EAAE,KAAA,EAAO,WAAA,EAAa,MAAA,EAAQ,2BAAA,EAA6B,CAAA;AAAA,QAC1F;AAAA,MACF,CAAA;AAAA,IACF;AAAA,GACF;AACF,CAAA;AAEO,IAAM,oBAAA,GAAuBA,oBAAG,aAAA,EAAe;AAAA,EACpD,IAAA,EAAM,SAAA;AAAA,EACN,OAAA,EAAS;AACX,CAAC","file":"fastify.cjs","sourcesContent":["import type { OperationDescriptor } from './models.js';\n\nconst _registry: OperationDescriptor[] = [];\n\n/** Infer operation method from the last segment of the operation id.\n * e.g. \"tasks:list\" → \"read\", \"tasks:create\" → \"write\", \"tasks:delete\" → \"delete\"\n */\nexport function detectMethod(id: string): string {\n const last = (id.split(':').pop() ?? '').toLowerCase();\n if (/^(get|list|fetch|read)/.test(last)) return 'read';\n if (/^(delete|remove|destroy)/.test(last)) return 'delete';\n if (/(stream|subscribe|watch|listen)/.test(last)) return 'stream';\n return 'write';\n}\n\n/** Register an operation in the global registry (used by startup to sync). */\nexport function registerOperation(op: OperationDescriptor): void {\n const exists = _registry.some(\n o => o.identifier === op.identifier && o.method === op.method,\n );\n if (!exists) _registry.push(op);\n}\n\nexport function getRegistry(): OperationDescriptor[] {\n return [..._registry];\n}\n\nexport function clearRegistry(): void {\n _registry.length = 0;\n}","import type { FastifyInstance, FastifyPluginAsync, FastifyReply, FastifyRequest } from 'fastify';\nimport fp from 'fastify-plugin';\nimport { CaronteClient } from '../client.js';\nimport type { TokenClaims } from '../models.js';\nimport { detectMethod, registerOperation } from '../registry.js';\n\ndeclare module 'fastify' {\n interface FastifyRequest {\n caronteUser?: TokenClaims;\n }\n}\n\nexport interface CarontePluginOptions {\n client: CaronteClient;\n}\n\nconst carontePlugin: FastifyPluginAsync<CarontePluginOptions> = async (\n fastify: FastifyInstance,\n { client }: CarontePluginOptions,\n) => {\n // Decorate request with caronteUser\n fastify.decorateRequest<TokenClaims | undefined>('caronteUser', undefined);\n\n // Global hook — validates Bearer token on every request\n fastify.addHook('onRequest', async (request: FastifyRequest) => {\n const auth = request.headers.authorization ?? '';\n const [scheme, token] = auth.split(' ');\n if (scheme?.toLowerCase() === 'bearer' && token) {\n try {\n request.caronteUser = await client.validateToken(token);\n } catch {\n // invalid token — caronteUser stays null; guard handles the 401\n }\n }\n });\n\n // Decorate fastify with a guard factory\n fastify.decorate(\n 'caronteGuard',\n (id: string, level: string, method?: string) => {\n const resolvedMethod = method ?? detectMethod(id);\n registerOperation({ identifier: id, method: resolvedMethod, level });\n\n return async (request: FastifyRequest, reply: FastifyReply): Promise<void> => {\n if (level === 'public') return;\n\n if (!request.caronteUser) {\n await reply.status(401).send({ error: 'unauthorized', detail: 'Bearer token required.' });\n return;\n }\n\n const allowed = client.checkPermission(request.caronteUser, id, resolvedMethod);\n if (!allowed) {\n await reply.status(403).send({ error: 'forbidden', detail: 'Insufficient permissions.' });\n }\n };\n },\n );\n};\n\nexport const caronteFastifyPlugin = fp(carontePlugin, {\n name: 'caronte',\n fastify: '>=4',\n});\n\ndeclare module 'fastify' {\n interface FastifyInstance {\n caronteGuard: (\n id: string,\n level: string,\n method?: string,\n ) => (req: FastifyRequest, reply: FastifyReply) => Promise<void>;\n }\n}"]}
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { FastifyReply, FastifyPluginAsync } from 'fastify';
|
|
2
|
-
import { T as TokenClaims,
|
|
2
|
+
import { T as TokenClaims, C as CaronteClient } from '../client-TcOSwgrn.cjs';
|
|
3
3
|
|
|
4
|
-
interface
|
|
5
|
-
client:
|
|
4
|
+
interface CarontePluginOptions {
|
|
5
|
+
client: CaronteClient;
|
|
6
6
|
}
|
|
7
|
-
declare const
|
|
7
|
+
declare const caronteFastifyPlugin: FastifyPluginAsync<CarontePluginOptions>;
|
|
8
8
|
declare module 'fastify' {
|
|
9
9
|
interface FastifyRequest {
|
|
10
|
-
|
|
10
|
+
caronteUser?: TokenClaims;
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
declare module 'fastify' {
|
|
14
14
|
interface FastifyInstance {
|
|
15
|
-
|
|
15
|
+
caronteGuard: (id: string, level: string, method?: string) => (req: FastifyRequest, reply: FastifyReply) => Promise<void>;
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
export { type
|
|
19
|
+
export { type CarontePluginOptions, caronteFastifyPlugin };
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { FastifyReply, FastifyPluginAsync } from 'fastify';
|
|
2
|
-
import { T as TokenClaims,
|
|
2
|
+
import { T as TokenClaims, C as CaronteClient } from '../client-TcOSwgrn.js';
|
|
3
3
|
|
|
4
|
-
interface
|
|
5
|
-
client:
|
|
4
|
+
interface CarontePluginOptions {
|
|
5
|
+
client: CaronteClient;
|
|
6
6
|
}
|
|
7
|
-
declare const
|
|
7
|
+
declare const caronteFastifyPlugin: FastifyPluginAsync<CarontePluginOptions>;
|
|
8
8
|
declare module 'fastify' {
|
|
9
9
|
interface FastifyRequest {
|
|
10
|
-
|
|
10
|
+
caronteUser?: TokenClaims;
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
declare module 'fastify' {
|
|
14
14
|
interface FastifyInstance {
|
|
15
|
-
|
|
15
|
+
caronteGuard: (id: string, level: string, method?: string) => (req: FastifyRequest, reply: FastifyReply) => Promise<void>;
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
export { type
|
|
19
|
+
export { type CarontePluginOptions, caronteFastifyPlugin };
|
package/dist/adapters/fastify.js
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
import { detectMethod, registerOperation } from '../chunk-HV6X5RJY.js';
|
|
2
2
|
import fp from 'fastify-plugin';
|
|
3
3
|
|
|
4
|
-
var
|
|
5
|
-
fastify.decorateRequest("
|
|
4
|
+
var carontePlugin = async (fastify, { client }) => {
|
|
5
|
+
fastify.decorateRequest("caronteUser", void 0);
|
|
6
6
|
fastify.addHook("onRequest", async (request) => {
|
|
7
7
|
const auth = request.headers.authorization ?? "";
|
|
8
8
|
const [scheme, token] = auth.split(" ");
|
|
9
9
|
if (scheme?.toLowerCase() === "bearer" && token) {
|
|
10
10
|
try {
|
|
11
|
-
request.
|
|
11
|
+
request.caronteUser = await client.validateToken(token);
|
|
12
12
|
} catch {
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
15
|
});
|
|
16
16
|
fastify.decorate(
|
|
17
|
-
"
|
|
17
|
+
"caronteGuard",
|
|
18
18
|
(id, level, method) => {
|
|
19
19
|
const resolvedMethod = method ?? detectMethod(id);
|
|
20
20
|
registerOperation({ identifier: id, method: resolvedMethod, level });
|
|
21
21
|
return async (request, reply) => {
|
|
22
22
|
if (level === "public") return;
|
|
23
|
-
if (!request.
|
|
23
|
+
if (!request.caronteUser) {
|
|
24
24
|
await reply.status(401).send({ error: "unauthorized", detail: "Bearer token required." });
|
|
25
25
|
return;
|
|
26
26
|
}
|
|
27
|
-
const allowed = client.checkPermission(request.
|
|
27
|
+
const allowed = client.checkPermission(request.caronteUser, id, resolvedMethod);
|
|
28
28
|
if (!allowed) {
|
|
29
29
|
await reply.status(403).send({ error: "forbidden", detail: "Insufficient permissions." });
|
|
30
30
|
}
|
|
@@ -32,11 +32,11 @@ var argosPlugin = async (fastify, { client }) => {
|
|
|
32
32
|
}
|
|
33
33
|
);
|
|
34
34
|
};
|
|
35
|
-
var
|
|
36
|
-
name: "
|
|
35
|
+
var caronteFastifyPlugin = fp(carontePlugin, {
|
|
36
|
+
name: "caronte",
|
|
37
37
|
fastify: ">=4"
|
|
38
38
|
});
|
|
39
39
|
|
|
40
|
-
export {
|
|
40
|
+
export { caronteFastifyPlugin };
|
|
41
41
|
//# sourceMappingURL=fastify.js.map
|
|
42
42
|
//# sourceMappingURL=fastify.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/adapters/fastify.ts"],"names":[],"mappings":";;;AAgBA,IAAM,
|
|
1
|
+
{"version":3,"sources":["../../src/adapters/fastify.ts"],"names":[],"mappings":";;;AAgBA,IAAM,aAAA,GAA0D,OAC9D,OAAA,EACA,EAAE,QAAO,KACN;AAEH,EAAA,OAAA,CAAQ,eAAA,CAAyC,eAAe,MAAS,CAAA;AAGzE,EAAA,OAAA,CAAQ,OAAA,CAAQ,WAAA,EAAa,OAAO,OAAA,KAA4B;AAC9D,IAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,OAAA,CAAQ,aAAA,IAAiB,EAAA;AAC9C,IAAA,MAAM,CAAC,MAAA,EAAQ,KAAK,CAAA,GAAI,IAAA,CAAK,MAAM,GAAG,CAAA;AACtC,IAAA,IAAI,MAAA,EAAQ,WAAA,EAAY,KAAM,QAAA,IAAY,KAAA,EAAO;AAC/C,MAAA,IAAI;AACF,QAAA,OAAA,CAAQ,WAAA,GAAc,MAAM,MAAA,CAAO,aAAA,CAAc,KAAK,CAAA;AAAA,MACxD,CAAA,CAAA,MAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF,CAAC,CAAA;AAGD,EAAA,OAAA,CAAQ,QAAA;AAAA,IACN,cAAA;AAAA,IACA,CAAC,EAAA,EAAY,KAAA,EAAe,MAAA,KAAoB;AAC9C,MAAA,MAAM,cAAA,GAAiB,MAAA,IAAU,YAAA,CAAa,EAAE,CAAA;AAChD,MAAA,iBAAA,CAAkB,EAAE,UAAA,EAAY,EAAA,EAAI,MAAA,EAAQ,cAAA,EAAgB,OAAO,CAAA;AAEnE,MAAA,OAAO,OAAO,SAAyB,KAAA,KAAuC;AAC5E,QAAA,IAAI,UAAU,QAAA,EAAU;AAExB,QAAA,IAAI,CAAC,QAAQ,WAAA,EAAa;AACxB,UAAA,MAAM,KAAA,CAAM,MAAA,CAAO,GAAG,CAAA,CAAE,IAAA,CAAK,EAAE,KAAA,EAAO,cAAA,EAAgB,MAAA,EAAQ,wBAAA,EAA0B,CAAA;AACxF,UAAA;AAAA,QACF;AAEA,QAAA,MAAM,UAAU,MAAA,CAAO,eAAA,CAAgB,OAAA,CAAQ,WAAA,EAAa,IAAI,cAAc,CAAA;AAC9E,QAAA,IAAI,CAAC,OAAA,EAAS;AACZ,UAAA,MAAM,KAAA,CAAM,MAAA,CAAO,GAAG,CAAA,CAAE,IAAA,CAAK,EAAE,KAAA,EAAO,WAAA,EAAa,MAAA,EAAQ,2BAAA,EAA6B,CAAA;AAAA,QAC1F;AAAA,MACF,CAAA;AAAA,IACF;AAAA,GACF;AACF,CAAA;AAEO,IAAM,oBAAA,GAAuB,GAAG,aAAA,EAAe;AAAA,EACpD,IAAA,EAAM,SAAA;AAAA,EACN,OAAA,EAAS;AACX,CAAC","file":"fastify.js","sourcesContent":["import type { FastifyInstance, FastifyPluginAsync, FastifyReply, FastifyRequest } from 'fastify';\nimport fp from 'fastify-plugin';\nimport { CaronteClient } from '../client.js';\nimport type { TokenClaims } from '../models.js';\nimport { detectMethod, registerOperation } from '../registry.js';\n\ndeclare module 'fastify' {\n interface FastifyRequest {\n caronteUser?: TokenClaims;\n }\n}\n\nexport interface CarontePluginOptions {\n client: CaronteClient;\n}\n\nconst carontePlugin: FastifyPluginAsync<CarontePluginOptions> = async (\n fastify: FastifyInstance,\n { client }: CarontePluginOptions,\n) => {\n // Decorate request with caronteUser\n fastify.decorateRequest<TokenClaims | undefined>('caronteUser', undefined);\n\n // Global hook — validates Bearer token on every request\n fastify.addHook('onRequest', async (request: FastifyRequest) => {\n const auth = request.headers.authorization ?? '';\n const [scheme, token] = auth.split(' ');\n if (scheme?.toLowerCase() === 'bearer' && token) {\n try {\n request.caronteUser = await client.validateToken(token);\n } catch {\n // invalid token — caronteUser stays null; guard handles the 401\n }\n }\n });\n\n // Decorate fastify with a guard factory\n fastify.decorate(\n 'caronteGuard',\n (id: string, level: string, method?: string) => {\n const resolvedMethod = method ?? detectMethod(id);\n registerOperation({ identifier: id, method: resolvedMethod, level });\n\n return async (request: FastifyRequest, reply: FastifyReply): Promise<void> => {\n if (level === 'public') return;\n\n if (!request.caronteUser) {\n await reply.status(401).send({ error: 'unauthorized', detail: 'Bearer token required.' });\n return;\n }\n\n const allowed = client.checkPermission(request.caronteUser, id, resolvedMethod);\n if (!allowed) {\n await reply.status(403).send({ error: 'forbidden', detail: 'Insufficient permissions.' });\n }\n };\n },\n );\n};\n\nexport const caronteFastifyPlugin = fp(carontePlugin, {\n name: 'caronte',\n fastify: '>=4',\n});\n\ndeclare module 'fastify' {\n interface FastifyInstance {\n caronteGuard: (\n id: string,\n level: string,\n method?: string,\n ) => (req: FastifyRequest, reply: FastifyReply) => Promise<void>;\n }\n}"]}
|
|
@@ -34,34 +34,34 @@ function getRegistry() {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
// src/adapters/nest/decorator.ts
|
|
37
|
-
var OPERATION_KEY = "
|
|
37
|
+
var OPERATION_KEY = "caronte:operation";
|
|
38
38
|
function Operation(id, level, method) {
|
|
39
39
|
const resolvedMethod = method ?? detectMethod(id);
|
|
40
40
|
registerOperation({ identifier: id, method: resolvedMethod, level });
|
|
41
41
|
return common.SetMetadata(OPERATION_KEY, { id, level, method: resolvedMethod });
|
|
42
42
|
}
|
|
43
|
-
var
|
|
44
|
-
(_data, ctx) => ctx.switchToHttp().getRequest().
|
|
43
|
+
var CaronteUser = common.createParamDecorator(
|
|
44
|
+
(_data, ctx) => ctx.switchToHttp().getRequest().caronteUser
|
|
45
45
|
);
|
|
46
46
|
|
|
47
47
|
// src/exceptions.ts
|
|
48
|
-
var
|
|
48
|
+
var CaronteError = class extends Error {
|
|
49
49
|
constructor(message) {
|
|
50
50
|
super(message);
|
|
51
51
|
this.name = this.constructor.name;
|
|
52
52
|
}
|
|
53
53
|
};
|
|
54
|
-
var
|
|
54
|
+
var CaronteAuthError = class extends CaronteError {
|
|
55
55
|
};
|
|
56
|
-
var
|
|
56
|
+
var CaronteTokenError = class extends CaronteError {
|
|
57
57
|
};
|
|
58
|
-
var
|
|
58
|
+
var CaronteSyncError = class extends CaronteError {
|
|
59
59
|
};
|
|
60
|
-
var
|
|
60
|
+
var CaronteConfigError = class extends CaronteError {
|
|
61
61
|
};
|
|
62
62
|
|
|
63
63
|
// src/client.ts
|
|
64
|
-
var
|
|
64
|
+
var CaronteClient = class _CaronteClient {
|
|
65
65
|
base;
|
|
66
66
|
realmId;
|
|
67
67
|
appId;
|
|
@@ -84,7 +84,7 @@ var ArgosClient = class _ArgosClient {
|
|
|
84
84
|
}
|
|
85
85
|
async startup() {
|
|
86
86
|
if (this.registeredOperations.length === 0) {
|
|
87
|
-
throw new
|
|
87
|
+
throw new CaronteConfigError(
|
|
88
88
|
"No operations registered. Call registerOperation() before startup()."
|
|
89
89
|
);
|
|
90
90
|
}
|
|
@@ -100,12 +100,12 @@ var ArgosClient = class _ArgosClient {
|
|
|
100
100
|
body: JSON.stringify({ app_id: this.appId, secret: this.secret })
|
|
101
101
|
});
|
|
102
102
|
if (!res.ok) {
|
|
103
|
-
throw new
|
|
103
|
+
throw new CaronteAuthError(
|
|
104
104
|
`Authentication failed: HTTP ${res.status} \u2014 ${await res.text()}`
|
|
105
105
|
);
|
|
106
106
|
}
|
|
107
107
|
const data = await res.json();
|
|
108
|
-
if (!data.access_token) throw new
|
|
108
|
+
if (!data.access_token) throw new CaronteAuthError("Authorizer returned no access_token.");
|
|
109
109
|
this.appToken = data.access_token;
|
|
110
110
|
}
|
|
111
111
|
initJwks() {
|
|
@@ -131,7 +131,7 @@ var ArgosClient = class _ArgosClient {
|
|
|
131
131
|
})
|
|
132
132
|
});
|
|
133
133
|
if (!res.ok) {
|
|
134
|
-
throw new
|
|
134
|
+
throw new CaronteSyncError(`Sync failed: HTTP ${res.status} \u2014 ${await res.text()}`);
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
137
|
async fetchOperations() {
|
|
@@ -140,7 +140,7 @@ var ArgosClient = class _ArgosClient {
|
|
|
140
140
|
headers: { Authorization: `Bearer ${this.appToken}` }
|
|
141
141
|
});
|
|
142
142
|
if (!res.ok) {
|
|
143
|
-
throw new
|
|
143
|
+
throw new CaronteError(
|
|
144
144
|
`Fetch operations failed: HTTP ${res.status} \u2014 ${await res.text()}`
|
|
145
145
|
);
|
|
146
146
|
}
|
|
@@ -160,7 +160,7 @@ var ArgosClient = class _ArgosClient {
|
|
|
160
160
|
const p = payload;
|
|
161
161
|
const tokenRealm = p["realm_id"] ?? "";
|
|
162
162
|
if (tokenRealm.toLowerCase() !== this.realmId.toLowerCase()) {
|
|
163
|
-
throw new
|
|
163
|
+
throw new CaronteTokenError(
|
|
164
164
|
`Token realm '${tokenRealm}' does not match this app's realm '${this.realmId}'.`
|
|
165
165
|
);
|
|
166
166
|
}
|
|
@@ -172,8 +172,8 @@ var ArgosClient = class _ArgosClient {
|
|
|
172
172
|
raw: p
|
|
173
173
|
};
|
|
174
174
|
} catch (err) {
|
|
175
|
-
if (err instanceof
|
|
176
|
-
throw new
|
|
175
|
+
if (err instanceof CaronteTokenError) throw err;
|
|
176
|
+
throw new CaronteTokenError(`Invalid token: ${err.message}`);
|
|
177
177
|
}
|
|
178
178
|
}
|
|
179
179
|
checkPermission(claims, identifier, method) {
|
|
@@ -188,25 +188,25 @@ var ArgosClient = class _ArgosClient {
|
|
|
188
188
|
}
|
|
189
189
|
/** Load operations from the global registry and bootstrap the client. */
|
|
190
190
|
static async create(options) {
|
|
191
|
-
const client = new
|
|
191
|
+
const client = new _CaronteClient(options);
|
|
192
192
|
for (const op of getRegistry()) client.registerOperation(op);
|
|
193
193
|
await client.startup();
|
|
194
194
|
return client;
|
|
195
195
|
}
|
|
196
196
|
ensureAppToken() {
|
|
197
197
|
if (!this.appToken) {
|
|
198
|
-
throw new
|
|
198
|
+
throw new CaronteAuthError("No app token. Call authenticate() or startup() first.");
|
|
199
199
|
}
|
|
200
200
|
}
|
|
201
201
|
};
|
|
202
202
|
|
|
203
203
|
// src/adapters/nest/guard.ts
|
|
204
|
-
exports.
|
|
204
|
+
exports.CaronteGuard = class CaronteGuard {
|
|
205
205
|
// @Inject() explícito -- não depender de emitDecoratorMetadata (design:paramtypes)
|
|
206
206
|
// pra resolver os parâmetros: o build desta lib usa tsup/esbuild, que não emite
|
|
207
207
|
// essa metadata (limitação conhecida do esbuild), então a injeção implícita por
|
|
208
208
|
// tipo do Nest falha silenciosamente (reflector fica undefined em runtime,
|
|
209
|
-
// TypeError em qualquer rota guardada por
|
|
209
|
+
// TypeError em qualquer rota guardada por CaronteGuard).
|
|
210
210
|
constructor(client, reflector) {
|
|
211
211
|
this.client = client;
|
|
212
212
|
this.reflector = reflector;
|
|
@@ -233,30 +233,30 @@ exports.ArgosGuard = class ArgosGuard {
|
|
|
233
233
|
} catch {
|
|
234
234
|
throw new common.UnauthorizedException("Invalid token.");
|
|
235
235
|
}
|
|
236
|
-
request.
|
|
236
|
+
request.caronteUser = claims;
|
|
237
237
|
const allowed = this.client.checkPermission(claims, id, method);
|
|
238
238
|
if (!allowed) throw new common.ForbiddenException("Insufficient permissions.");
|
|
239
239
|
return true;
|
|
240
240
|
}
|
|
241
241
|
};
|
|
242
|
-
exports.
|
|
242
|
+
exports.CaronteGuard = __decorateClass([
|
|
243
243
|
common.Injectable(),
|
|
244
|
-
__decorateParam(0, common.Inject(
|
|
244
|
+
__decorateParam(0, common.Inject(CaronteClient)),
|
|
245
245
|
__decorateParam(1, common.Inject(core.Reflector))
|
|
246
|
-
], exports.
|
|
247
|
-
exports.
|
|
246
|
+
], exports.CaronteGuard);
|
|
247
|
+
exports.CaronteModule = class CaronteModule {
|
|
248
248
|
/**
|
|
249
|
-
* Register the
|
|
249
|
+
* Register the caronte module globally.
|
|
250
250
|
*
|
|
251
|
-
* Creates the `
|
|
252
|
-
* and provides `
|
|
251
|
+
* Creates the `CaronteClient`, bootstraps it (authenticate + sync + fetch),
|
|
252
|
+
* and provides `CaronteGuard` for injection.
|
|
253
253
|
*
|
|
254
254
|
* @example
|
|
255
255
|
* ```ts
|
|
256
256
|
* // app.module.ts
|
|
257
257
|
* @Module({
|
|
258
258
|
* imports: [
|
|
259
|
-
*
|
|
259
|
+
* CaronteModule.forRootAsync({
|
|
260
260
|
* authorizerUrl: process.env.AUTHORIZER_URL!,
|
|
261
261
|
* realmId: process.env.REALM_ID!,
|
|
262
262
|
* appId: process.env.APP_ID!,
|
|
@@ -269,30 +269,30 @@ exports.ArgosModule = class ArgosModule {
|
|
|
269
269
|
*/
|
|
270
270
|
static forRootAsync(options) {
|
|
271
271
|
return {
|
|
272
|
-
module: exports.
|
|
272
|
+
module: exports.CaronteModule,
|
|
273
273
|
global: true,
|
|
274
274
|
providers: [
|
|
275
275
|
core.Reflector,
|
|
276
276
|
{
|
|
277
|
-
provide:
|
|
277
|
+
provide: CaronteClient,
|
|
278
278
|
useFactory: async () => {
|
|
279
|
-
const client = new
|
|
279
|
+
const client = new CaronteClient(options);
|
|
280
280
|
for (const op of getRegistry()) client.registerOperation(op);
|
|
281
281
|
await client.startup();
|
|
282
282
|
return client;
|
|
283
283
|
}
|
|
284
284
|
},
|
|
285
|
-
exports.
|
|
285
|
+
exports.CaronteGuard
|
|
286
286
|
],
|
|
287
|
-
exports: [
|
|
287
|
+
exports: [CaronteClient, exports.CaronteGuard]
|
|
288
288
|
};
|
|
289
289
|
}
|
|
290
290
|
};
|
|
291
|
-
exports.
|
|
291
|
+
exports.CaronteModule = __decorateClass([
|
|
292
292
|
common.Module({})
|
|
293
|
-
], exports.
|
|
293
|
+
], exports.CaronteModule);
|
|
294
294
|
|
|
295
|
-
exports.
|
|
295
|
+
exports.CaronteUser = CaronteUser;
|
|
296
296
|
exports.OPERATION_KEY = OPERATION_KEY;
|
|
297
297
|
exports.Operation = Operation;
|
|
298
298
|
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/registry.ts","../../../src/adapters/nest/decorator.ts","../../../src/exceptions.ts","../../../src/client.ts","../../../src/adapters/nest/guard.ts","../../../src/adapters/nest/module.ts"],"names":["SetMetadata","createParamDecorator","createRemoteJWKSet","jwtVerify","ArgosGuard","UnauthorizedException","ForbiddenException","Injectable","Reflector","ArgosModule","Module"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { OperationDescriptor } from './models.js';\n\nconst _registry: OperationDescriptor[] = [];\n\n/** Infer operation method from the last segment of the operation id.\n * e.g. \"tasks:list\" → \"read\", \"tasks:create\" → \"write\", \"tasks:delete\" → \"delete\"\n */\nexport function detectMethod(id: string): string {\n const last = (id.split(':').pop() ?? '').toLowerCase();\n if (/^(get|list|fetch|read)/.test(last)) return 'read';\n if (/^(delete|remove|destroy)/.test(last)) return 'delete';\n if (/(stream|subscribe|watch|listen)/.test(last)) return 'stream';\n return 'write';\n}\n\n/** Register an operation in the global registry (used by startup to sync). */\nexport function registerOperation(op: OperationDescriptor): void {\n const exists = _registry.some(\n o => o.identifier === op.identifier && o.method === op.method,\n );\n if (!exists) _registry.push(op);\n}\n\nexport function getRegistry(): OperationDescriptor[] {\n return [..._registry];\n}\n\nexport function clearRegistry(): void {\n _registry.length = 0;\n}","import { createParamDecorator, ExecutionContext, SetMetadata } from '@nestjs/common';\nimport type { TokenClaims } from '../../models.js';\nimport { detectMethod, registerOperation } from '../../registry.js';\n\nexport const OPERATION_KEY = 'argos:operation';\n\nexport interface OperationMeta {\n id: string;\n level: string;\n method: string;\n}\n\n/**\n * Marks a controller method as a argos operation.\n *\n * @example\n * ```ts\n * @Get()\n * @Operation('tasks:list', 'private')\n * listTasks(@ArgosUser() user: TokenClaims) { ... }\n * ```\n */\nexport function Operation(id: string, level: string, method?: string): MethodDecorator {\n const resolvedMethod = method ?? detectMethod(id);\n registerOperation({ identifier: id, method: resolvedMethod, level });\n return SetMetadata<string, OperationMeta>(OPERATION_KEY, { id, level, method: resolvedMethod });\n}\n\n/** Injects the authenticated user's TokenClaims into a parameter. */\nexport const ArgosUser = createParamDecorator(\n (_data: unknown, ctx: ExecutionContext): TokenClaims | undefined =>\n ctx.switchToHttp().getRequest().argosUser,\n);","export class ArgosError extends Error {\n constructor(message: string) {\n super(message);\n this.name = this.constructor.name;\n }\n}\n\nexport class ArgosAuthError extends ArgosError {}\nexport class ArgosTokenError extends ArgosError {}\nexport class ArgosForbiddenError extends ArgosError {}\nexport class ArgosSyncError extends ArgosError {}\nexport class ArgosConfigError extends ArgosError {}","import { createRemoteJWKSet, jwtVerify, type JWTPayload } from 'jose';\nimport {\n ArgosAuthError, ArgosConfigError, ArgosError,\n ArgosSyncError, ArgosTokenError,\n} from './exceptions.js';\nimport type { OperationDescriptor, OperationWithGroups, TokenClaims } from './models.js';\nimport { getRegistry } from './registry.js';\n\nexport interface ArgosClientOptions {\n authorizerUrl: string;\n realmId: string;\n appId: string;\n secret: string;\n}\n\nexport class ArgosClient {\n private readonly base: string;\n private readonly realmId: string;\n private readonly appId: string;\n private readonly secret: string;\n\n private appToken: string | null = null;\n private jwks: ReturnType<typeof createRemoteJWKSet> | null = null;\n private operations: OperationWithGroups[] = [];\n private registeredOperations: OperationDescriptor[] = [];\n\n constructor(options: ArgosClientOptions) {\n this.base = options.authorizerUrl.replace(/\\/$/, '');\n this.realmId = options.realmId;\n this.appId = options.appId;\n this.secret = options.secret;\n }\n\n private get realmBase(): string {\n return `${this.base}/realms/${this.realmId}`;\n }\n\n registerOperation(op: OperationDescriptor): void {\n this.registeredOperations.push(op);\n }\n\n async startup(): Promise<void> {\n if (this.registeredOperations.length === 0) {\n throw new ArgosConfigError(\n 'No operations registered. Call registerOperation() before startup().',\n );\n }\n await this.authenticate();\n this.initJwks();\n await this.syncOperations();\n await this.fetchOperations();\n }\n\n async authenticate(): Promise<void> {\n const res = await fetch(`${this.realmBase}/apps/token`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ app_id: this.appId, secret: this.secret }),\n });\n if (!res.ok) {\n throw new ArgosAuthError(\n `Authentication failed: HTTP ${res.status} — ${await res.text()}`,\n );\n }\n const data = await res.json() as { access_token?: string };\n if (!data.access_token) throw new ArgosAuthError('Authorizer returned no access_token.');\n this.appToken = data.access_token;\n }\n\n private initJwks(): void {\n this.jwks = createRemoteJWKSet(\n new URL(`${this.realmBase}/protocol/openid-connect/certs`),\n );\n }\n\n async syncOperations(): Promise<void> {\n this.ensureAppToken();\n const res = await fetch(`${this.realmBase}/apps/operations/sync`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.appToken}`,\n },\n body: JSON.stringify({\n operations: this.registeredOperations.map(op => ({\n identifier: op.identifier,\n method: op.method,\n level: op.level,\n description: op.description ?? null,\n })),\n }),\n });\n if (!res.ok) {\n throw new ArgosSyncError(`Sync failed: HTTP ${res.status} — ${await res.text()}`);\n }\n }\n\n async fetchOperations(): Promise<void> {\n this.ensureAppToken();\n const res = await fetch(`${this.realmBase}/apps/operations`, {\n headers: { Authorization: `Bearer ${this.appToken}` },\n });\n if (!res.ok) {\n throw new ArgosError(\n `Fetch operations failed: HTTP ${res.status} — ${await res.text()}`,\n );\n }\n const data = await res.json() as { operations: any[] };\n this.operations = data.operations.map(op => ({\n operationId: op.operation_id,\n identifier: op.identifier,\n method: op.method,\n level: op.level,\n allowedGroups: op.allowed_groups ?? [],\n }));\n }\n\n async validateToken(token: string): Promise<TokenClaims> {\n if (!this.jwks) this.initJwks();\n try {\n const { payload } = await jwtVerify(token, this.jwks!);\n const p = payload as JWTPayload & Record<string, unknown>;\n const tokenRealm = (p['realm_id'] as string) ?? '';\n\n // As chaves JWKS já são buscadas em /realms/{this.realmId}/... (ver\n // realmBase), então um token de outro realm normalmente já falharia a\n // verificação de assinatura acima. Esta checagem é defesa em\n // profundidade explícita — não depende desse efeito colateral, e falha\n // com uma mensagem clara em vez de erro de assinatura.\n if (tokenRealm.toLowerCase() !== this.realmId.toLowerCase()) {\n throw new ArgosTokenError(\n `Token realm '${tokenRealm}' does not match this app's realm '${this.realmId}'.`,\n );\n }\n\n return {\n sub: payload.sub ?? '',\n realmId: tokenRealm,\n groups: (p['groups'] as string[]) ?? [],\n exp: payload.exp ?? 0,\n raw: p,\n };\n } catch (err) {\n if (err instanceof ArgosTokenError) throw err;\n throw new ArgosTokenError(`Invalid token: ${(err as Error).message}`);\n }\n }\n\n checkPermission(claims: TokenClaims, identifier: string, method: string): boolean {\n const op = this.operations.find(\n o => o.identifier === identifier && o.method === method,\n );\n if (!op) return false;\n if (op.level === 'public') return true;\n if (op.level === 'private') return claims.groups.length > 0;\n if (op.level === 'protected') return claims.groups.some(g => op.allowedGroups.includes(g));\n return false;\n }\n\n /** Load operations from the global registry and bootstrap the client. */\n static async create(options: ArgosClientOptions): Promise<ArgosClient> {\n const client = new ArgosClient(options);\n for (const op of getRegistry()) client.registerOperation(op);\n await client.startup();\n return client;\n }\n\n private ensureAppToken(): void {\n if (!this.appToken) {\n throw new ArgosAuthError('No app token. Call authenticate() or startup() first.');\n }\n }\n}","import {\n CanActivate, ExecutionContext, ForbiddenException,\n Inject, Injectable, UnauthorizedException,\n} from '@nestjs/common';\nimport { Reflector } from '@nestjs/core';\nimport { ArgosClient } from '../../client.js';\nimport type { TokenClaims } from '../../models.js';\nimport { OPERATION_KEY, type OperationMeta } from './decorator.js';\n\n@Injectable()\nexport class ArgosGuard implements CanActivate {\n // @Inject() explícito -- não depender de emitDecoratorMetadata (design:paramtypes)\n // pra resolver os parâmetros: o build desta lib usa tsup/esbuild, que não emite\n // essa metadata (limitação conhecida do esbuild), então a injeção implícita por\n // tipo do Nest falha silenciosamente (reflector fica undefined em runtime,\n // TypeError em qualquer rota guardada por ArgosGuard).\n constructor(\n @Inject(ArgosClient) private readonly client: ArgosClient,\n @Inject(Reflector) private readonly reflector: Reflector,\n ) {}\n\n async canActivate(ctx: ExecutionContext): Promise<boolean> {\n const meta = this.reflector.get<OperationMeta | undefined>(\n OPERATION_KEY,\n ctx.getHandler(),\n );\n\n if (!meta) return true; // no @Operation — pass through\n\n const { id, level, method } = meta;\n const request = ctx.switchToHttp().getRequest<{ headers: Record<string, string>; argosUser?: TokenClaims }>();\n\n if (level === 'public') return true;\n\n const auth = request.headers['authorization'] ?? '';\n const [scheme, token] = auth.split(' ');\n\n if (scheme?.toLowerCase() !== 'bearer' || !token) {\n throw new UnauthorizedException('Bearer token required.');\n }\n\n let claims: TokenClaims;\n try {\n claims = await this.client.validateToken(token);\n } catch {\n // Token malformado/expirado/inválido -- 401, igual aos adapters express/\n // fastify (que engolem o erro de validateToken pelo mesmo motivo), não\n // deixar virar 500 por exceção não tratada.\n throw new UnauthorizedException('Invalid token.');\n }\n request.argosUser = claims;\n\n const allowed = this.client.checkPermission(claims, id, method);\n if (!allowed) throw new ForbiddenException('Insufficient permissions.');\n\n return true;\n }\n}","import { DynamicModule, Module } from '@nestjs/common';\nimport { Reflector } from '@nestjs/core';\nimport { ArgosClient, type ArgosClientOptions } from '../../client.js';\nimport { getRegistry } from '../../registry.js';\nimport { ArgosGuard } from './guard.js';\n\nexport interface ArgosModuleOptions extends ArgosClientOptions {}\n\n@Module({})\nexport class ArgosModule {\n /**\n * Register the argos module globally.\n *\n * Creates the `ArgosClient`, bootstraps it (authenticate + sync + fetch),\n * and provides `ArgosGuard` for injection.\n *\n * @example\n * ```ts\n * // app.module.ts\n * @Module({\n * imports: [\n * ArgosModule.forRootAsync({\n * authorizerUrl: process.env.AUTHORIZER_URL!,\n * realmId: process.env.REALM_ID!,\n * appId: process.env.APP_ID!,\n * secret: process.env.APP_SECRET!,\n * }),\n * ],\n * })\n * export class AppModule {}\n * ```\n */\n static forRootAsync(options: ArgosModuleOptions): DynamicModule {\n return {\n module: ArgosModule,\n global: true,\n providers: [\n Reflector,\n {\n provide: ArgosClient,\n useFactory: async () => {\n const client = new ArgosClient(options);\n for (const op of getRegistry()) client.registerOperation(op);\n await client.startup();\n return client;\n },\n },\n ArgosGuard,\n ],\n exports: [ArgosClient, ArgosGuard],\n };\n }\n}"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/registry.ts","../../../src/adapters/nest/decorator.ts","../../../src/exceptions.ts","../../../src/client.ts","../../../src/adapters/nest/guard.ts","../../../src/adapters/nest/module.ts"],"names":["SetMetadata","createParamDecorator","createRemoteJWKSet","jwtVerify","CaronteGuard","UnauthorizedException","ForbiddenException","Injectable","Reflector","CaronteModule","Module"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { OperationDescriptor } from './models.js';\n\nconst _registry: OperationDescriptor[] = [];\n\n/** Infer operation method from the last segment of the operation id.\n * e.g. \"tasks:list\" → \"read\", \"tasks:create\" → \"write\", \"tasks:delete\" → \"delete\"\n */\nexport function detectMethod(id: string): string {\n const last = (id.split(':').pop() ?? '').toLowerCase();\n if (/^(get|list|fetch|read)/.test(last)) return 'read';\n if (/^(delete|remove|destroy)/.test(last)) return 'delete';\n if (/(stream|subscribe|watch|listen)/.test(last)) return 'stream';\n return 'write';\n}\n\n/** Register an operation in the global registry (used by startup to sync). */\nexport function registerOperation(op: OperationDescriptor): void {\n const exists = _registry.some(\n o => o.identifier === op.identifier && o.method === op.method,\n );\n if (!exists) _registry.push(op);\n}\n\nexport function getRegistry(): OperationDescriptor[] {\n return [..._registry];\n}\n\nexport function clearRegistry(): void {\n _registry.length = 0;\n}","import { createParamDecorator, ExecutionContext, SetMetadata } from '@nestjs/common';\nimport type { TokenClaims } from '../../models.js';\nimport { detectMethod, registerOperation } from '../../registry.js';\n\nexport const OPERATION_KEY = 'caronte:operation';\n\nexport interface OperationMeta {\n id: string;\n level: string;\n method: string;\n}\n\n/**\n * Marks a controller method as a caronte operation.\n *\n * @example\n * ```ts\n * @Get()\n * @Operation('tasks:list', 'private')\n * listTasks(@CaronteUser() user: TokenClaims) { ... }\n * ```\n */\nexport function Operation(id: string, level: string, method?: string): MethodDecorator {\n const resolvedMethod = method ?? detectMethod(id);\n registerOperation({ identifier: id, method: resolvedMethod, level });\n return SetMetadata<string, OperationMeta>(OPERATION_KEY, { id, level, method: resolvedMethod });\n}\n\n/** Injects the authenticated user's TokenClaims into a parameter. */\nexport const CaronteUser = createParamDecorator(\n (_data: unknown, ctx: ExecutionContext): TokenClaims | undefined =>\n ctx.switchToHttp().getRequest().caronteUser,\n);","export class CaronteError extends Error {\n constructor(message: string) {\n super(message);\n this.name = this.constructor.name;\n }\n}\n\nexport class CaronteAuthError extends CaronteError {}\nexport class CaronteTokenError extends CaronteError {}\nexport class CaronteForbiddenError extends CaronteError {}\nexport class CaronteSyncError extends CaronteError {}\nexport class CaronteConfigError extends CaronteError {}","import { createRemoteJWKSet, jwtVerify, type JWTPayload } from 'jose';\nimport {\n CaronteAuthError, CaronteConfigError, CaronteError,\n CaronteSyncError, CaronteTokenError,\n} from './exceptions.js';\nimport type { OperationDescriptor, OperationWithGroups, TokenClaims } from './models.js';\nimport { getRegistry } from './registry.js';\n\nexport interface CaronteClientOptions {\n authorizerUrl: string;\n realmId: string;\n appId: string;\n secret: string;\n}\n\nexport class CaronteClient {\n private readonly base: string;\n private readonly realmId: string;\n private readonly appId: string;\n private readonly secret: string;\n\n private appToken: string | null = null;\n private jwks: ReturnType<typeof createRemoteJWKSet> | null = null;\n private operations: OperationWithGroups[] = [];\n private registeredOperations: OperationDescriptor[] = [];\n\n constructor(options: CaronteClientOptions) {\n this.base = options.authorizerUrl.replace(/\\/$/, '');\n this.realmId = options.realmId;\n this.appId = options.appId;\n this.secret = options.secret;\n }\n\n private get realmBase(): string {\n return `${this.base}/realms/${this.realmId}`;\n }\n\n registerOperation(op: OperationDescriptor): void {\n this.registeredOperations.push(op);\n }\n\n async startup(): Promise<void> {\n if (this.registeredOperations.length === 0) {\n throw new CaronteConfigError(\n 'No operations registered. Call registerOperation() before startup().',\n );\n }\n await this.authenticate();\n this.initJwks();\n await this.syncOperations();\n await this.fetchOperations();\n }\n\n async authenticate(): Promise<void> {\n const res = await fetch(`${this.realmBase}/apps/token`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ app_id: this.appId, secret: this.secret }),\n });\n if (!res.ok) {\n throw new CaronteAuthError(\n `Authentication failed: HTTP ${res.status} — ${await res.text()}`,\n );\n }\n const data = await res.json() as { access_token?: string };\n if (!data.access_token) throw new CaronteAuthError('Authorizer returned no access_token.');\n this.appToken = data.access_token;\n }\n\n private initJwks(): void {\n this.jwks = createRemoteJWKSet(\n new URL(`${this.realmBase}/protocol/openid-connect/certs`),\n );\n }\n\n async syncOperations(): Promise<void> {\n this.ensureAppToken();\n const res = await fetch(`${this.realmBase}/apps/operations/sync`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.appToken}`,\n },\n body: JSON.stringify({\n operations: this.registeredOperations.map(op => ({\n identifier: op.identifier,\n method: op.method,\n level: op.level,\n description: op.description ?? null,\n })),\n }),\n });\n if (!res.ok) {\n throw new CaronteSyncError(`Sync failed: HTTP ${res.status} — ${await res.text()}`);\n }\n }\n\n async fetchOperations(): Promise<void> {\n this.ensureAppToken();\n const res = await fetch(`${this.realmBase}/apps/operations`, {\n headers: { Authorization: `Bearer ${this.appToken}` },\n });\n if (!res.ok) {\n throw new CaronteError(\n `Fetch operations failed: HTTP ${res.status} — ${await res.text()}`,\n );\n }\n const data = await res.json() as { operations: any[] };\n this.operations = data.operations.map(op => ({\n operationId: op.operation_id,\n identifier: op.identifier,\n method: op.method,\n level: op.level,\n allowedGroups: op.allowed_groups ?? [],\n }));\n }\n\n async validateToken(token: string): Promise<TokenClaims> {\n if (!this.jwks) this.initJwks();\n try {\n const { payload } = await jwtVerify(token, this.jwks!);\n const p = payload as JWTPayload & Record<string, unknown>;\n const tokenRealm = (p['realm_id'] as string) ?? '';\n\n // As chaves JWKS já são buscadas em /realms/{this.realmId}/... (ver\n // realmBase), então um token de outro realm normalmente já falharia a\n // verificação de assinatura acima. Esta checagem é defesa em\n // profundidade explícita — não depende desse efeito colateral, e falha\n // com uma mensagem clara em vez de erro de assinatura.\n if (tokenRealm.toLowerCase() !== this.realmId.toLowerCase()) {\n throw new CaronteTokenError(\n `Token realm '${tokenRealm}' does not match this app's realm '${this.realmId}'.`,\n );\n }\n\n return {\n sub: payload.sub ?? '',\n realmId: tokenRealm,\n groups: (p['groups'] as string[]) ?? [],\n exp: payload.exp ?? 0,\n raw: p,\n };\n } catch (err) {\n if (err instanceof CaronteTokenError) throw err;\n throw new CaronteTokenError(`Invalid token: ${(err as Error).message}`);\n }\n }\n\n checkPermission(claims: TokenClaims, identifier: string, method: string): boolean {\n const op = this.operations.find(\n o => o.identifier === identifier && o.method === method,\n );\n if (!op) return false;\n if (op.level === 'public') return true;\n if (op.level === 'private') return claims.groups.length > 0;\n if (op.level === 'protected') return claims.groups.some(g => op.allowedGroups.includes(g));\n return false;\n }\n\n /** Load operations from the global registry and bootstrap the client. */\n static async create(options: CaronteClientOptions): Promise<CaronteClient> {\n const client = new CaronteClient(options);\n for (const op of getRegistry()) client.registerOperation(op);\n await client.startup();\n return client;\n }\n\n private ensureAppToken(): void {\n if (!this.appToken) {\n throw new CaronteAuthError('No app token. Call authenticate() or startup() first.');\n }\n }\n}","import {\n CanActivate, ExecutionContext, ForbiddenException,\n Inject, Injectable, UnauthorizedException,\n} from '@nestjs/common';\nimport { Reflector } from '@nestjs/core';\nimport { CaronteClient } from '../../client.js';\nimport type { TokenClaims } from '../../models.js';\nimport { OPERATION_KEY, type OperationMeta } from './decorator.js';\n\n@Injectable()\nexport class CaronteGuard implements CanActivate {\n // @Inject() explícito -- não depender de emitDecoratorMetadata (design:paramtypes)\n // pra resolver os parâmetros: o build desta lib usa tsup/esbuild, que não emite\n // essa metadata (limitação conhecida do esbuild), então a injeção implícita por\n // tipo do Nest falha silenciosamente (reflector fica undefined em runtime,\n // TypeError em qualquer rota guardada por CaronteGuard).\n constructor(\n @Inject(CaronteClient) private readonly client: CaronteClient,\n @Inject(Reflector) private readonly reflector: Reflector,\n ) {}\n\n async canActivate(ctx: ExecutionContext): Promise<boolean> {\n const meta = this.reflector.get<OperationMeta | undefined>(\n OPERATION_KEY,\n ctx.getHandler(),\n );\n\n if (!meta) return true; // no @Operation — pass through\n\n const { id, level, method } = meta;\n const request = ctx.switchToHttp().getRequest<{ headers: Record<string, string>; caronteUser?: TokenClaims }>();\n\n if (level === 'public') return true;\n\n const auth = request.headers['authorization'] ?? '';\n const [scheme, token] = auth.split(' ');\n\n if (scheme?.toLowerCase() !== 'bearer' || !token) {\n throw new UnauthorizedException('Bearer token required.');\n }\n\n let claims: TokenClaims;\n try {\n claims = await this.client.validateToken(token);\n } catch {\n // Token malformado/expirado/inválido -- 401, igual aos adapters express/\n // fastify (que engolem o erro de validateToken pelo mesmo motivo), não\n // deixar virar 500 por exceção não tratada.\n throw new UnauthorizedException('Invalid token.');\n }\n request.caronteUser = claims;\n\n const allowed = this.client.checkPermission(claims, id, method);\n if (!allowed) throw new ForbiddenException('Insufficient permissions.');\n\n return true;\n }\n}","import { DynamicModule, Module } from '@nestjs/common';\nimport { Reflector } from '@nestjs/core';\nimport { CaronteClient, type CaronteClientOptions } from '../../client.js';\nimport { getRegistry } from '../../registry.js';\nimport { CaronteGuard } from './guard.js';\n\nexport interface CaronteModuleOptions extends CaronteClientOptions {}\n\n@Module({})\nexport class CaronteModule {\n /**\n * Register the caronte module globally.\n *\n * Creates the `CaronteClient`, bootstraps it (authenticate + sync + fetch),\n * and provides `CaronteGuard` for injection.\n *\n * @example\n * ```ts\n * // app.module.ts\n * @Module({\n * imports: [\n * CaronteModule.forRootAsync({\n * authorizerUrl: process.env.AUTHORIZER_URL!,\n * realmId: process.env.REALM_ID!,\n * appId: process.env.APP_ID!,\n * secret: process.env.APP_SECRET!,\n * }),\n * ],\n * })\n * export class AppModule {}\n * ```\n */\n static forRootAsync(options: CaronteModuleOptions): DynamicModule {\n return {\n module: CaronteModule,\n global: true,\n providers: [\n Reflector,\n {\n provide: CaronteClient,\n useFactory: async () => {\n const client = new CaronteClient(options);\n for (const op of getRegistry()) client.registerOperation(op);\n await client.startup();\n return client;\n },\n },\n CaronteGuard,\n ],\n exports: [CaronteClient, CaronteGuard],\n };\n }\n}"]}
|
|
@@ -1,49 +1,49 @@
|
|
|
1
1
|
import { CanActivate, ExecutionContext, DynamicModule } from '@nestjs/common';
|
|
2
2
|
import { Reflector } from '@nestjs/core';
|
|
3
|
-
import {
|
|
3
|
+
import { C as CaronteClient, a as CaronteClientOptions } from '../../client-TcOSwgrn.cjs';
|
|
4
4
|
|
|
5
|
-
declare const OPERATION_KEY = "
|
|
5
|
+
declare const OPERATION_KEY = "caronte:operation";
|
|
6
6
|
interface OperationMeta {
|
|
7
7
|
id: string;
|
|
8
8
|
level: string;
|
|
9
9
|
method: string;
|
|
10
10
|
}
|
|
11
11
|
/**
|
|
12
|
-
* Marks a controller method as a
|
|
12
|
+
* Marks a controller method as a caronte operation.
|
|
13
13
|
*
|
|
14
14
|
* @example
|
|
15
15
|
* ```ts
|
|
16
16
|
* @Get()
|
|
17
17
|
* @Operation('tasks:list', 'private')
|
|
18
|
-
* listTasks(@
|
|
18
|
+
* listTasks(@CaronteUser() user: TokenClaims) { ... }
|
|
19
19
|
* ```
|
|
20
20
|
*/
|
|
21
21
|
declare function Operation(id: string, level: string, method?: string): MethodDecorator;
|
|
22
22
|
/** Injects the authenticated user's TokenClaims into a parameter. */
|
|
23
|
-
declare const
|
|
23
|
+
declare const CaronteUser: (...dataOrPipes: unknown[]) => ParameterDecorator;
|
|
24
24
|
|
|
25
|
-
declare class
|
|
25
|
+
declare class CaronteGuard implements CanActivate {
|
|
26
26
|
private readonly client;
|
|
27
27
|
private readonly reflector;
|
|
28
|
-
constructor(client:
|
|
28
|
+
constructor(client: CaronteClient, reflector: Reflector);
|
|
29
29
|
canActivate(ctx: ExecutionContext): Promise<boolean>;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
interface
|
|
32
|
+
interface CaronteModuleOptions extends CaronteClientOptions {
|
|
33
33
|
}
|
|
34
|
-
declare class
|
|
34
|
+
declare class CaronteModule {
|
|
35
35
|
/**
|
|
36
|
-
* Register the
|
|
36
|
+
* Register the caronte module globally.
|
|
37
37
|
*
|
|
38
|
-
* Creates the `
|
|
39
|
-
* and provides `
|
|
38
|
+
* Creates the `CaronteClient`, bootstraps it (authenticate + sync + fetch),
|
|
39
|
+
* and provides `CaronteGuard` for injection.
|
|
40
40
|
*
|
|
41
41
|
* @example
|
|
42
42
|
* ```ts
|
|
43
43
|
* // app.module.ts
|
|
44
44
|
* @Module({
|
|
45
45
|
* imports: [
|
|
46
|
-
*
|
|
46
|
+
* CaronteModule.forRootAsync({
|
|
47
47
|
* authorizerUrl: process.env.AUTHORIZER_URL!,
|
|
48
48
|
* realmId: process.env.REALM_ID!,
|
|
49
49
|
* appId: process.env.APP_ID!,
|
|
@@ -54,7 +54,7 @@ declare class ArgosModule {
|
|
|
54
54
|
* export class AppModule {}
|
|
55
55
|
* ```
|
|
56
56
|
*/
|
|
57
|
-
static forRootAsync(options:
|
|
57
|
+
static forRootAsync(options: CaronteModuleOptions): DynamicModule;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
export {
|
|
60
|
+
export { CaronteGuard, CaronteModule, type CaronteModuleOptions, CaronteUser, OPERATION_KEY, Operation, type OperationMeta };
|