@fiado/api-invoker 4.39.0 → 4.40.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.
@@ -48,8 +48,19 @@ let ApiInvokerPermissionResolver = class ApiInvokerPermissionResolver {
48
48
  // en el adapter, enforceRbac mapea solo PermissionDeniedError a 403; este error se re-lanza -> 5xx.
49
49
  // Es fail-closed (no concede), NO un 403, y es deliberado: un fallo de red NO debe verse como
50
50
  // "usuario sin permisos". NUNCA devolver { assignments: [] } en un catch.
51
- const resp = await this._api.resolvePermissions(input);
52
- const resolved = resp.body;
51
+ //
52
+ // SHAPE (TD-AI-003): @fiado/http-client (.post) devuelve `response.data` = el BODY HTTP crudo del
53
+ // ApiResponse de rbac, o sea `{ code, data, msg }` — NO un ApiGatewayResponse `{ statusCode, body }`
54
+ // (el tipo de retorno del publisher es histórico y no refleja el runtime). El ResolvedPermissions
55
+ // viene en `.data`. Leer `.body` daba undefined → crash en enforceRbac. Ver docs/TECH_DEBT.md.
56
+ const resp = (await this._api.resolvePermissions(input));
57
+ const resolved = resp?.data;
58
+ // Fail-closed (DEC-RBAC-071): si rbac no devolvió un payload válido (403 de PRIVATE/VPC, ruta
59
+ // inexistente, shape inesperado), NO devolver undefined (rompería enforceRbac con TypeError) →
60
+ // propagar para que el adapter deny. tenantId siempre es string en un ResolvedPermissions ('' = deny).
61
+ if (!resolved || typeof resolved.tenantId !== "string") {
62
+ throw new Error("ApiInvokerPermissionResolver: respuesta de rbac sin ResolvedPermissions válido (resolvePermissions)");
63
+ }
53
64
  if (cacheable)
54
65
  this._cache.set(key, resolved);
55
66
  return resolved;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fiado/api-invoker",
3
- "version": "4.39.0",
3
+ "version": "4.40.0",
4
4
  "description": "Sirve como un puente entre diferentes funciones lambda, facilitando la comunicación entre ellas a través de invocaciones http",
5
5
  "type": "module",
6
6
  "main": "bin/index.js",
@@ -40,8 +40,19 @@ export class ApiInvokerPermissionResolver {
40
40
  // en el adapter, enforceRbac mapea solo PermissionDeniedError a 403; este error se re-lanza -> 5xx.
41
41
  // Es fail-closed (no concede), NO un 403, y es deliberado: un fallo de red NO debe verse como
42
42
  // "usuario sin permisos". NUNCA devolver { assignments: [] } en un catch.
43
- const resp = await this._api.resolvePermissions(input);
44
- const resolved = resp.body;
43
+ //
44
+ // SHAPE (TD-AI-003): @fiado/http-client (.post) devuelve `response.data` = el BODY HTTP crudo del
45
+ // ApiResponse de rbac, o sea `{ code, data, msg }` — NO un ApiGatewayResponse `{ statusCode, body }`
46
+ // (el tipo de retorno del publisher es histórico y no refleja el runtime). El ResolvedPermissions
47
+ // viene en `.data`. Leer `.body` daba undefined → crash en enforceRbac. Ver docs/TECH_DEBT.md.
48
+ const resp = (await this._api.resolvePermissions(input)) as unknown as { data?: ResolvedPermissions };
49
+ const resolved = resp?.data;
50
+ // Fail-closed (DEC-RBAC-071): si rbac no devolvió un payload válido (403 de PRIVATE/VPC, ruta
51
+ // inexistente, shape inesperado), NO devolver undefined (rompería enforceRbac con TypeError) →
52
+ // propagar para que el adapter deny. tenantId siempre es string en un ResolvedPermissions ('' = deny).
53
+ if (!resolved || typeof resolved.tenantId !== "string") {
54
+ throw new Error("ApiInvokerPermissionResolver: respuesta de rbac sin ResolvedPermissions válido (resolvePermissions)");
55
+ }
45
56
  if (cacheable) this._cache.set(key, resolved);
46
57
  return resolved;
47
58
  }