@caronte-sdk/node 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/README.md +198 -0
- package/dist/adapters/apollo.cjs +71 -0
- package/dist/adapters/apollo.cjs.map +1 -0
- package/dist/adapters/apollo.d.cts +34 -0
- package/dist/adapters/apollo.d.ts +34 -0
- package/dist/adapters/apollo.js +45 -0
- package/dist/adapters/apollo.js.map +1 -0
- package/dist/adapters/express.cjs +57 -0
- package/dist/adapters/express.cjs.map +1 -0
- package/dist/adapters/express.d.cts +19 -0
- package/dist/adapters/express.d.ts +19 -0
- package/dist/adapters/express.js +41 -0
- package/dist/adapters/express.js.map +1 -0
- package/dist/adapters/fastify.cjs +66 -0
- package/dist/adapters/fastify.cjs.map +1 -0
- package/dist/adapters/fastify.d.cts +19 -0
- package/dist/adapters/fastify.d.ts +19 -0
- package/dist/adapters/fastify.js +42 -0
- package/dist/adapters/fastify.js.map +1 -0
- package/dist/adapters/nest/index.cjs +299 -0
- package/dist/adapters/nest/index.cjs.map +1 -0
- package/dist/adapters/nest/index.d.cts +60 -0
- package/dist/adapters/nest/index.d.ts +60 -0
- package/dist/adapters/nest/index.js +109 -0
- package/dist/adapters/nest/index.js.map +1 -0
- package/dist/chunk-AKUM2UEO.js +21 -0
- package/dist/chunk-AKUM2UEO.js.map +1 -0
- package/dist/chunk-G73YTEJQ.js +146 -0
- package/dist/chunk-G73YTEJQ.js.map +1 -0
- package/dist/chunk-HV6X5RJY.js +37 -0
- package/dist/chunk-HV6X5RJY.js.map +1 -0
- package/dist/client-Cgm1csuM.d.cts +49 -0
- package/dist/client-Cgm1csuM.d.ts +49 -0
- package/dist/index.cjs +199 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +27 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/package.json +78 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/adapters/nest/decorator.ts","../../../src/adapters/nest/guard.ts","../../../src/adapters/nest/module.ts"],"names":["Reflector"],"mappings":"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","file":"index.js","sourcesContent":["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);","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}"]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// src/exceptions.ts
|
|
2
|
+
var ArgosError = class extends Error {
|
|
3
|
+
constructor(message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = this.constructor.name;
|
|
6
|
+
}
|
|
7
|
+
};
|
|
8
|
+
var ArgosAuthError = class extends ArgosError {
|
|
9
|
+
};
|
|
10
|
+
var ArgosTokenError = class extends ArgosError {
|
|
11
|
+
};
|
|
12
|
+
var ArgosForbiddenError = class extends ArgosError {
|
|
13
|
+
};
|
|
14
|
+
var ArgosSyncError = class extends ArgosError {
|
|
15
|
+
};
|
|
16
|
+
var ArgosConfigError = class extends ArgosError {
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export { ArgosAuthError, ArgosConfigError, ArgosError, ArgosForbiddenError, ArgosSyncError, ArgosTokenError };
|
|
20
|
+
//# sourceMappingURL=chunk-AKUM2UEO.js.map
|
|
21
|
+
//# sourceMappingURL=chunk-AKUM2UEO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/exceptions.ts"],"names":[],"mappings":";AAAO,IAAM,UAAA,GAAN,cAAyB,KAAA,CAAM;AAAA,EACpC,YAAY,OAAA,EAAiB;AAC3B,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,KAAK,WAAA,CAAY,IAAA;AAAA,EAC/B;AACF;AAEO,IAAM,cAAA,GAAN,cAAgC,UAAA,CAAW;AAAC;AAC5C,IAAM,eAAA,GAAN,cAAgC,UAAA,CAAW;AAAC;AAC5C,IAAM,mBAAA,GAAN,cAAkC,UAAA,CAAW;AAAC;AAC9C,IAAM,cAAA,GAAN,cAAgC,UAAA,CAAW;AAAC;AAC5C,IAAM,gBAAA,GAAN,cAAgC,UAAA,CAAW;AAAC","file":"chunk-AKUM2UEO.js","sourcesContent":["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 {}"]}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { ArgosConfigError, ArgosAuthError, ArgosSyncError, ArgosError, ArgosTokenError } from './chunk-AKUM2UEO.js';
|
|
2
|
+
import { getRegistry } from './chunk-HV6X5RJY.js';
|
|
3
|
+
import { createRemoteJWKSet, jwtVerify } from 'jose';
|
|
4
|
+
|
|
5
|
+
var ArgosClient = class _ArgosClient {
|
|
6
|
+
base;
|
|
7
|
+
realmId;
|
|
8
|
+
appId;
|
|
9
|
+
secret;
|
|
10
|
+
appToken = null;
|
|
11
|
+
jwks = null;
|
|
12
|
+
operations = [];
|
|
13
|
+
registeredOperations = [];
|
|
14
|
+
constructor(options) {
|
|
15
|
+
this.base = options.authorizerUrl.replace(/\/$/, "");
|
|
16
|
+
this.realmId = options.realmId;
|
|
17
|
+
this.appId = options.appId;
|
|
18
|
+
this.secret = options.secret;
|
|
19
|
+
}
|
|
20
|
+
get realmBase() {
|
|
21
|
+
return `${this.base}/realms/${this.realmId}`;
|
|
22
|
+
}
|
|
23
|
+
registerOperation(op) {
|
|
24
|
+
this.registeredOperations.push(op);
|
|
25
|
+
}
|
|
26
|
+
async startup() {
|
|
27
|
+
if (this.registeredOperations.length === 0) {
|
|
28
|
+
throw new ArgosConfigError(
|
|
29
|
+
"No operations registered. Call registerOperation() before startup()."
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
await this.authenticate();
|
|
33
|
+
this.initJwks();
|
|
34
|
+
await this.syncOperations();
|
|
35
|
+
await this.fetchOperations();
|
|
36
|
+
}
|
|
37
|
+
async authenticate() {
|
|
38
|
+
const res = await fetch(`${this.realmBase}/apps/token`, {
|
|
39
|
+
method: "POST",
|
|
40
|
+
headers: { "Content-Type": "application/json" },
|
|
41
|
+
body: JSON.stringify({ app_id: this.appId, secret: this.secret })
|
|
42
|
+
});
|
|
43
|
+
if (!res.ok) {
|
|
44
|
+
throw new ArgosAuthError(
|
|
45
|
+
`Authentication failed: HTTP ${res.status} \u2014 ${await res.text()}`
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
const data = await res.json();
|
|
49
|
+
if (!data.access_token) throw new ArgosAuthError("Authorizer returned no access_token.");
|
|
50
|
+
this.appToken = data.access_token;
|
|
51
|
+
}
|
|
52
|
+
initJwks() {
|
|
53
|
+
this.jwks = createRemoteJWKSet(
|
|
54
|
+
new URL(`${this.realmBase}/protocol/openid-connect/certs`)
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
async syncOperations() {
|
|
58
|
+
this.ensureAppToken();
|
|
59
|
+
const res = await fetch(`${this.realmBase}/apps/operations/sync`, {
|
|
60
|
+
method: "POST",
|
|
61
|
+
headers: {
|
|
62
|
+
"Content-Type": "application/json",
|
|
63
|
+
Authorization: `Bearer ${this.appToken}`
|
|
64
|
+
},
|
|
65
|
+
body: JSON.stringify({
|
|
66
|
+
operations: this.registeredOperations.map((op) => ({
|
|
67
|
+
identifier: op.identifier,
|
|
68
|
+
method: op.method,
|
|
69
|
+
level: op.level,
|
|
70
|
+
description: op.description ?? null
|
|
71
|
+
}))
|
|
72
|
+
})
|
|
73
|
+
});
|
|
74
|
+
if (!res.ok) {
|
|
75
|
+
throw new ArgosSyncError(`Sync failed: HTTP ${res.status} \u2014 ${await res.text()}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async fetchOperations() {
|
|
79
|
+
this.ensureAppToken();
|
|
80
|
+
const res = await fetch(`${this.realmBase}/apps/operations`, {
|
|
81
|
+
headers: { Authorization: `Bearer ${this.appToken}` }
|
|
82
|
+
});
|
|
83
|
+
if (!res.ok) {
|
|
84
|
+
throw new ArgosError(
|
|
85
|
+
`Fetch operations failed: HTTP ${res.status} \u2014 ${await res.text()}`
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
const data = await res.json();
|
|
89
|
+
this.operations = data.operations.map((op) => ({
|
|
90
|
+
operationId: op.operation_id,
|
|
91
|
+
identifier: op.identifier,
|
|
92
|
+
method: op.method,
|
|
93
|
+
level: op.level,
|
|
94
|
+
allowedGroups: op.allowed_groups ?? []
|
|
95
|
+
}));
|
|
96
|
+
}
|
|
97
|
+
async validateToken(token) {
|
|
98
|
+
if (!this.jwks) this.initJwks();
|
|
99
|
+
try {
|
|
100
|
+
const { payload } = await jwtVerify(token, this.jwks);
|
|
101
|
+
const p = payload;
|
|
102
|
+
const tokenRealm = p["realm_id"] ?? "";
|
|
103
|
+
if (tokenRealm.toLowerCase() !== this.realmId.toLowerCase()) {
|
|
104
|
+
throw new ArgosTokenError(
|
|
105
|
+
`Token realm '${tokenRealm}' does not match this app's realm '${this.realmId}'.`
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
sub: payload.sub ?? "",
|
|
110
|
+
realmId: tokenRealm,
|
|
111
|
+
groups: p["groups"] ?? [],
|
|
112
|
+
exp: payload.exp ?? 0,
|
|
113
|
+
raw: p
|
|
114
|
+
};
|
|
115
|
+
} catch (err) {
|
|
116
|
+
if (err instanceof ArgosTokenError) throw err;
|
|
117
|
+
throw new ArgosTokenError(`Invalid token: ${err.message}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
checkPermission(claims, identifier, method) {
|
|
121
|
+
const op = this.operations.find(
|
|
122
|
+
(o) => o.identifier === identifier && o.method === method
|
|
123
|
+
);
|
|
124
|
+
if (!op) return false;
|
|
125
|
+
if (op.level === "public") return true;
|
|
126
|
+
if (op.level === "private") return claims.groups.length > 0;
|
|
127
|
+
if (op.level === "protected") return claims.groups.some((g) => op.allowedGroups.includes(g));
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
/** Load operations from the global registry and bootstrap the client. */
|
|
131
|
+
static async create(options) {
|
|
132
|
+
const client = new _ArgosClient(options);
|
|
133
|
+
for (const op of getRegistry()) client.registerOperation(op);
|
|
134
|
+
await client.startup();
|
|
135
|
+
return client;
|
|
136
|
+
}
|
|
137
|
+
ensureAppToken() {
|
|
138
|
+
if (!this.appToken) {
|
|
139
|
+
throw new ArgosAuthError("No app token. Call authenticate() or startup() first.");
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
export { ArgosClient };
|
|
145
|
+
//# sourceMappingURL=chunk-G73YTEJQ.js.map
|
|
146
|
+
//# sourceMappingURL=chunk-G73YTEJQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client.ts"],"names":[],"mappings":"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","file":"chunk-G73YTEJQ.js","sourcesContent":["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}"]}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
4
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
5
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
6
|
+
if (decorator = decorators[i])
|
|
7
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
8
|
+
if (kind && result) __defProp(target, key, result);
|
|
9
|
+
return result;
|
|
10
|
+
};
|
|
11
|
+
var __decorateParam = (index, decorator) => (target, key) => decorator(target, key, index);
|
|
12
|
+
|
|
13
|
+
// src/registry.ts
|
|
14
|
+
var _registry = [];
|
|
15
|
+
function detectMethod(id) {
|
|
16
|
+
const last = (id.split(":").pop() ?? "").toLowerCase();
|
|
17
|
+
if (/^(get|list|fetch|read)/.test(last)) return "read";
|
|
18
|
+
if (/^(delete|remove|destroy)/.test(last)) return "delete";
|
|
19
|
+
if (/(stream|subscribe|watch|listen)/.test(last)) return "stream";
|
|
20
|
+
return "write";
|
|
21
|
+
}
|
|
22
|
+
function registerOperation(op) {
|
|
23
|
+
const exists = _registry.some(
|
|
24
|
+
(o) => o.identifier === op.identifier && o.method === op.method
|
|
25
|
+
);
|
|
26
|
+
if (!exists) _registry.push(op);
|
|
27
|
+
}
|
|
28
|
+
function getRegistry() {
|
|
29
|
+
return [..._registry];
|
|
30
|
+
}
|
|
31
|
+
function clearRegistry() {
|
|
32
|
+
_registry.length = 0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export { __decorateClass, __decorateParam, clearRegistry, detectMethod, getRegistry, registerOperation };
|
|
36
|
+
//# sourceMappingURL=chunk-HV6X5RJY.js.map
|
|
37
|
+
//# sourceMappingURL=chunk-HV6X5RJY.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/registry.ts"],"names":[],"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;AAEO,SAAS,WAAA,GAAqC;AACnD,EAAA,OAAO,CAAC,GAAG,SAAS,CAAA;AACtB;AAEO,SAAS,aAAA,GAAsB;AACpC,EAAA,SAAA,CAAU,MAAA,GAAS,CAAA;AACrB","file":"chunk-HV6X5RJY.js","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}"]}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
interface TokenClaims {
|
|
2
|
+
sub: string;
|
|
3
|
+
realmId: string;
|
|
4
|
+
groups: string[];
|
|
5
|
+
exp: number;
|
|
6
|
+
raw: Record<string, unknown>;
|
|
7
|
+
}
|
|
8
|
+
interface OperationDescriptor {
|
|
9
|
+
identifier: string;
|
|
10
|
+
method: string;
|
|
11
|
+
level: string;
|
|
12
|
+
description?: string | null;
|
|
13
|
+
}
|
|
14
|
+
interface OperationWithGroups extends OperationDescriptor {
|
|
15
|
+
operationId: string;
|
|
16
|
+
allowedGroups: string[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface ArgosClientOptions {
|
|
20
|
+
authorizerUrl: string;
|
|
21
|
+
realmId: string;
|
|
22
|
+
appId: string;
|
|
23
|
+
secret: string;
|
|
24
|
+
}
|
|
25
|
+
declare class ArgosClient {
|
|
26
|
+
private readonly base;
|
|
27
|
+
private readonly realmId;
|
|
28
|
+
private readonly appId;
|
|
29
|
+
private readonly secret;
|
|
30
|
+
private appToken;
|
|
31
|
+
private jwks;
|
|
32
|
+
private operations;
|
|
33
|
+
private registeredOperations;
|
|
34
|
+
constructor(options: ArgosClientOptions);
|
|
35
|
+
private get realmBase();
|
|
36
|
+
registerOperation(op: OperationDescriptor): void;
|
|
37
|
+
startup(): Promise<void>;
|
|
38
|
+
authenticate(): Promise<void>;
|
|
39
|
+
private initJwks;
|
|
40
|
+
syncOperations(): Promise<void>;
|
|
41
|
+
fetchOperations(): Promise<void>;
|
|
42
|
+
validateToken(token: string): Promise<TokenClaims>;
|
|
43
|
+
checkPermission(claims: TokenClaims, identifier: string, method: string): boolean;
|
|
44
|
+
/** Load operations from the global registry and bootstrap the client. */
|
|
45
|
+
static create(options: ArgosClientOptions): Promise<ArgosClient>;
|
|
46
|
+
private ensureAppToken;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export { ArgosClient as A, type OperationDescriptor as O, type TokenClaims as T, type ArgosClientOptions as a, type OperationWithGroups as b };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
interface TokenClaims {
|
|
2
|
+
sub: string;
|
|
3
|
+
realmId: string;
|
|
4
|
+
groups: string[];
|
|
5
|
+
exp: number;
|
|
6
|
+
raw: Record<string, unknown>;
|
|
7
|
+
}
|
|
8
|
+
interface OperationDescriptor {
|
|
9
|
+
identifier: string;
|
|
10
|
+
method: string;
|
|
11
|
+
level: string;
|
|
12
|
+
description?: string | null;
|
|
13
|
+
}
|
|
14
|
+
interface OperationWithGroups extends OperationDescriptor {
|
|
15
|
+
operationId: string;
|
|
16
|
+
allowedGroups: string[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface ArgosClientOptions {
|
|
20
|
+
authorizerUrl: string;
|
|
21
|
+
realmId: string;
|
|
22
|
+
appId: string;
|
|
23
|
+
secret: string;
|
|
24
|
+
}
|
|
25
|
+
declare class ArgosClient {
|
|
26
|
+
private readonly base;
|
|
27
|
+
private readonly realmId;
|
|
28
|
+
private readonly appId;
|
|
29
|
+
private readonly secret;
|
|
30
|
+
private appToken;
|
|
31
|
+
private jwks;
|
|
32
|
+
private operations;
|
|
33
|
+
private registeredOperations;
|
|
34
|
+
constructor(options: ArgosClientOptions);
|
|
35
|
+
private get realmBase();
|
|
36
|
+
registerOperation(op: OperationDescriptor): void;
|
|
37
|
+
startup(): Promise<void>;
|
|
38
|
+
authenticate(): Promise<void>;
|
|
39
|
+
private initJwks;
|
|
40
|
+
syncOperations(): Promise<void>;
|
|
41
|
+
fetchOperations(): Promise<void>;
|
|
42
|
+
validateToken(token: string): Promise<TokenClaims>;
|
|
43
|
+
checkPermission(claims: TokenClaims, identifier: string, method: string): boolean;
|
|
44
|
+
/** Load operations from the global registry and bootstrap the client. */
|
|
45
|
+
static create(options: ArgosClientOptions): Promise<ArgosClient>;
|
|
46
|
+
private ensureAppToken;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export { ArgosClient as A, type OperationDescriptor as O, type TokenClaims as T, type ArgosClientOptions as a, type OperationWithGroups as b };
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var jose = require('jose');
|
|
4
|
+
|
|
5
|
+
// src/client.ts
|
|
6
|
+
|
|
7
|
+
// src/exceptions.ts
|
|
8
|
+
var ArgosError = class extends Error {
|
|
9
|
+
constructor(message) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = this.constructor.name;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
var ArgosAuthError = class extends ArgosError {
|
|
15
|
+
};
|
|
16
|
+
var ArgosTokenError = class extends ArgosError {
|
|
17
|
+
};
|
|
18
|
+
var ArgosForbiddenError = class extends ArgosError {
|
|
19
|
+
};
|
|
20
|
+
var ArgosSyncError = class extends ArgosError {
|
|
21
|
+
};
|
|
22
|
+
var ArgosConfigError = class extends ArgosError {
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// src/registry.ts
|
|
26
|
+
var _registry = [];
|
|
27
|
+
function detectMethod(id) {
|
|
28
|
+
const last = (id.split(":").pop() ?? "").toLowerCase();
|
|
29
|
+
if (/^(get|list|fetch|read)/.test(last)) return "read";
|
|
30
|
+
if (/^(delete|remove|destroy)/.test(last)) return "delete";
|
|
31
|
+
if (/(stream|subscribe|watch|listen)/.test(last)) return "stream";
|
|
32
|
+
return "write";
|
|
33
|
+
}
|
|
34
|
+
function registerOperation(op) {
|
|
35
|
+
const exists = _registry.some(
|
|
36
|
+
(o) => o.identifier === op.identifier && o.method === op.method
|
|
37
|
+
);
|
|
38
|
+
if (!exists) _registry.push(op);
|
|
39
|
+
}
|
|
40
|
+
function getRegistry() {
|
|
41
|
+
return [..._registry];
|
|
42
|
+
}
|
|
43
|
+
function clearRegistry() {
|
|
44
|
+
_registry.length = 0;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// src/client.ts
|
|
48
|
+
var ArgosClient = class _ArgosClient {
|
|
49
|
+
base;
|
|
50
|
+
realmId;
|
|
51
|
+
appId;
|
|
52
|
+
secret;
|
|
53
|
+
appToken = null;
|
|
54
|
+
jwks = null;
|
|
55
|
+
operations = [];
|
|
56
|
+
registeredOperations = [];
|
|
57
|
+
constructor(options) {
|
|
58
|
+
this.base = options.authorizerUrl.replace(/\/$/, "");
|
|
59
|
+
this.realmId = options.realmId;
|
|
60
|
+
this.appId = options.appId;
|
|
61
|
+
this.secret = options.secret;
|
|
62
|
+
}
|
|
63
|
+
get realmBase() {
|
|
64
|
+
return `${this.base}/realms/${this.realmId}`;
|
|
65
|
+
}
|
|
66
|
+
registerOperation(op) {
|
|
67
|
+
this.registeredOperations.push(op);
|
|
68
|
+
}
|
|
69
|
+
async startup() {
|
|
70
|
+
if (this.registeredOperations.length === 0) {
|
|
71
|
+
throw new ArgosConfigError(
|
|
72
|
+
"No operations registered. Call registerOperation() before startup()."
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
await this.authenticate();
|
|
76
|
+
this.initJwks();
|
|
77
|
+
await this.syncOperations();
|
|
78
|
+
await this.fetchOperations();
|
|
79
|
+
}
|
|
80
|
+
async authenticate() {
|
|
81
|
+
const res = await fetch(`${this.realmBase}/apps/token`, {
|
|
82
|
+
method: "POST",
|
|
83
|
+
headers: { "Content-Type": "application/json" },
|
|
84
|
+
body: JSON.stringify({ app_id: this.appId, secret: this.secret })
|
|
85
|
+
});
|
|
86
|
+
if (!res.ok) {
|
|
87
|
+
throw new ArgosAuthError(
|
|
88
|
+
`Authentication failed: HTTP ${res.status} \u2014 ${await res.text()}`
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
const data = await res.json();
|
|
92
|
+
if (!data.access_token) throw new ArgosAuthError("Authorizer returned no access_token.");
|
|
93
|
+
this.appToken = data.access_token;
|
|
94
|
+
}
|
|
95
|
+
initJwks() {
|
|
96
|
+
this.jwks = jose.createRemoteJWKSet(
|
|
97
|
+
new URL(`${this.realmBase}/protocol/openid-connect/certs`)
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
async syncOperations() {
|
|
101
|
+
this.ensureAppToken();
|
|
102
|
+
const res = await fetch(`${this.realmBase}/apps/operations/sync`, {
|
|
103
|
+
method: "POST",
|
|
104
|
+
headers: {
|
|
105
|
+
"Content-Type": "application/json",
|
|
106
|
+
Authorization: `Bearer ${this.appToken}`
|
|
107
|
+
},
|
|
108
|
+
body: JSON.stringify({
|
|
109
|
+
operations: this.registeredOperations.map((op) => ({
|
|
110
|
+
identifier: op.identifier,
|
|
111
|
+
method: op.method,
|
|
112
|
+
level: op.level,
|
|
113
|
+
description: op.description ?? null
|
|
114
|
+
}))
|
|
115
|
+
})
|
|
116
|
+
});
|
|
117
|
+
if (!res.ok) {
|
|
118
|
+
throw new ArgosSyncError(`Sync failed: HTTP ${res.status} \u2014 ${await res.text()}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
async fetchOperations() {
|
|
122
|
+
this.ensureAppToken();
|
|
123
|
+
const res = await fetch(`${this.realmBase}/apps/operations`, {
|
|
124
|
+
headers: { Authorization: `Bearer ${this.appToken}` }
|
|
125
|
+
});
|
|
126
|
+
if (!res.ok) {
|
|
127
|
+
throw new ArgosError(
|
|
128
|
+
`Fetch operations failed: HTTP ${res.status} \u2014 ${await res.text()}`
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
const data = await res.json();
|
|
132
|
+
this.operations = data.operations.map((op) => ({
|
|
133
|
+
operationId: op.operation_id,
|
|
134
|
+
identifier: op.identifier,
|
|
135
|
+
method: op.method,
|
|
136
|
+
level: op.level,
|
|
137
|
+
allowedGroups: op.allowed_groups ?? []
|
|
138
|
+
}));
|
|
139
|
+
}
|
|
140
|
+
async validateToken(token) {
|
|
141
|
+
if (!this.jwks) this.initJwks();
|
|
142
|
+
try {
|
|
143
|
+
const { payload } = await jose.jwtVerify(token, this.jwks);
|
|
144
|
+
const p = payload;
|
|
145
|
+
const tokenRealm = p["realm_id"] ?? "";
|
|
146
|
+
if (tokenRealm.toLowerCase() !== this.realmId.toLowerCase()) {
|
|
147
|
+
throw new ArgosTokenError(
|
|
148
|
+
`Token realm '${tokenRealm}' does not match this app's realm '${this.realmId}'.`
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
sub: payload.sub ?? "",
|
|
153
|
+
realmId: tokenRealm,
|
|
154
|
+
groups: p["groups"] ?? [],
|
|
155
|
+
exp: payload.exp ?? 0,
|
|
156
|
+
raw: p
|
|
157
|
+
};
|
|
158
|
+
} catch (err) {
|
|
159
|
+
if (err instanceof ArgosTokenError) throw err;
|
|
160
|
+
throw new ArgosTokenError(`Invalid token: ${err.message}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
checkPermission(claims, identifier, method) {
|
|
164
|
+
const op = this.operations.find(
|
|
165
|
+
(o) => o.identifier === identifier && o.method === method
|
|
166
|
+
);
|
|
167
|
+
if (!op) return false;
|
|
168
|
+
if (op.level === "public") return true;
|
|
169
|
+
if (op.level === "private") return claims.groups.length > 0;
|
|
170
|
+
if (op.level === "protected") return claims.groups.some((g) => op.allowedGroups.includes(g));
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
/** Load operations from the global registry and bootstrap the client. */
|
|
174
|
+
static async create(options) {
|
|
175
|
+
const client = new _ArgosClient(options);
|
|
176
|
+
for (const op of getRegistry()) client.registerOperation(op);
|
|
177
|
+
await client.startup();
|
|
178
|
+
return client;
|
|
179
|
+
}
|
|
180
|
+
ensureAppToken() {
|
|
181
|
+
if (!this.appToken) {
|
|
182
|
+
throw new ArgosAuthError("No app token. Call authenticate() or startup() first.");
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
exports.ArgosAuthError = ArgosAuthError;
|
|
188
|
+
exports.ArgosClient = ArgosClient;
|
|
189
|
+
exports.ArgosConfigError = ArgosConfigError;
|
|
190
|
+
exports.ArgosError = ArgosError;
|
|
191
|
+
exports.ArgosForbiddenError = ArgosForbiddenError;
|
|
192
|
+
exports.ArgosSyncError = ArgosSyncError;
|
|
193
|
+
exports.ArgosTokenError = ArgosTokenError;
|
|
194
|
+
exports.clearRegistry = clearRegistry;
|
|
195
|
+
exports.detectMethod = detectMethod;
|
|
196
|
+
exports.getRegistry = getRegistry;
|
|
197
|
+
exports.registerOperation = registerOperation;
|
|
198
|
+
//# sourceMappingURL=index.cjs.map
|
|
199
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/exceptions.ts","../src/registry.ts","../src/client.ts"],"names":["createRemoteJWKSet","jwtVerify"],"mappings":"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","file":"index.cjs","sourcesContent":["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 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 { 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}"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { O as OperationDescriptor } from './client-Cgm1csuM.cjs';
|
|
2
|
+
export { A as ArgosClient, a as ArgosClientOptions, b as OperationWithGroups, T as TokenClaims } from './client-Cgm1csuM.cjs';
|
|
3
|
+
|
|
4
|
+
declare class ArgosError extends Error {
|
|
5
|
+
constructor(message: string);
|
|
6
|
+
}
|
|
7
|
+
declare class ArgosAuthError extends ArgosError {
|
|
8
|
+
}
|
|
9
|
+
declare class ArgosTokenError extends ArgosError {
|
|
10
|
+
}
|
|
11
|
+
declare class ArgosForbiddenError extends ArgosError {
|
|
12
|
+
}
|
|
13
|
+
declare class ArgosSyncError extends ArgosError {
|
|
14
|
+
}
|
|
15
|
+
declare class ArgosConfigError extends ArgosError {
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Infer operation method from the last segment of the operation id.
|
|
19
|
+
* e.g. "tasks:list" → "read", "tasks:create" → "write", "tasks:delete" → "delete"
|
|
20
|
+
*/
|
|
21
|
+
declare function detectMethod(id: string): string;
|
|
22
|
+
/** Register an operation in the global registry (used by startup to sync). */
|
|
23
|
+
declare function registerOperation(op: OperationDescriptor): void;
|
|
24
|
+
declare function getRegistry(): OperationDescriptor[];
|
|
25
|
+
declare function clearRegistry(): void;
|
|
26
|
+
|
|
27
|
+
export { ArgosAuthError, ArgosConfigError, ArgosError, ArgosForbiddenError, ArgosSyncError, ArgosTokenError, OperationDescriptor, clearRegistry, detectMethod, getRegistry, registerOperation };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { O as OperationDescriptor } from './client-Cgm1csuM.js';
|
|
2
|
+
export { A as ArgosClient, a as ArgosClientOptions, b as OperationWithGroups, T as TokenClaims } from './client-Cgm1csuM.js';
|
|
3
|
+
|
|
4
|
+
declare class ArgosError extends Error {
|
|
5
|
+
constructor(message: string);
|
|
6
|
+
}
|
|
7
|
+
declare class ArgosAuthError extends ArgosError {
|
|
8
|
+
}
|
|
9
|
+
declare class ArgosTokenError extends ArgosError {
|
|
10
|
+
}
|
|
11
|
+
declare class ArgosForbiddenError extends ArgosError {
|
|
12
|
+
}
|
|
13
|
+
declare class ArgosSyncError extends ArgosError {
|
|
14
|
+
}
|
|
15
|
+
declare class ArgosConfigError extends ArgosError {
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Infer operation method from the last segment of the operation id.
|
|
19
|
+
* e.g. "tasks:list" → "read", "tasks:create" → "write", "tasks:delete" → "delete"
|
|
20
|
+
*/
|
|
21
|
+
declare function detectMethod(id: string): string;
|
|
22
|
+
/** Register an operation in the global registry (used by startup to sync). */
|
|
23
|
+
declare function registerOperation(op: OperationDescriptor): void;
|
|
24
|
+
declare function getRegistry(): OperationDescriptor[];
|
|
25
|
+
declare function clearRegistry(): void;
|
|
26
|
+
|
|
27
|
+
export { ArgosAuthError, ArgosConfigError, ArgosError, ArgosForbiddenError, ArgosSyncError, ArgosTokenError, OperationDescriptor, clearRegistry, detectMethod, getRegistry, registerOperation };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { ArgosClient } from './chunk-G73YTEJQ.js';
|
|
2
|
+
export { ArgosAuthError, ArgosConfigError, ArgosError, ArgosForbiddenError, ArgosSyncError, ArgosTokenError } from './chunk-AKUM2UEO.js';
|
|
3
|
+
export { clearRegistry, detectMethod, getRegistry, registerOperation } from './chunk-HV6X5RJY.js';
|
|
4
|
+
//# sourceMappingURL=index.js.map
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
|