@fiado/api-invoker 4.37.1 → 4.39.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/bin/platformRbac/api/PlatformRbacBusinessApi.d.ts +2 -2
- package/bin/platformRbac/api/PlatformRbacBusinessApi.js +7 -1
- package/bin/platformRbac/api/interfaces/IPlatformRbacBusinessApi.d.ts +3 -1
- package/bin/platformRbac/index.d.ts +2 -0
- package/bin/platformRbac/index.js +2 -0
- package/bin/platformRbac/resolver/ApiInvokerPermissionResolver.d.ts +25 -0
- package/bin/platformRbac/resolver/ApiInvokerPermissionResolver.js +63 -0
- package/bin/platformRbac/resolver/apiInvokerResolverBindings.d.ts +4 -0
- package/bin/platformRbac/resolver/apiInvokerResolverBindings.js +13 -0
- package/bin/transactionProcessor/api/TransactionProcessorApi.d.ts +0 -2
- package/bin/transactionProcessor/api/TransactionProcessorApi.js +0 -4
- package/bin/transactionProcessor/api/interfaces/ITransactionProcessorApi.d.ts +0 -18
- package/jest.config.cjs +12 -0
- package/package.json +2 -2
- package/src/platformRbac/api/PlatformRbacBusinessApi.ts +11 -2
- package/src/platformRbac/api/interfaces/IPlatformRbacBusinessApi.ts +4 -1
- package/src/platformRbac/index.ts +2 -0
- package/src/platformRbac/resolver/ApiInvokerPermissionResolver.ts +48 -0
- package/src/platformRbac/resolver/apiInvokerResolverBindings.ts +23 -0
- package/src/transactionProcessor/api/TransactionProcessorApi.ts +0 -8
- package/src/transactionProcessor/api/interfaces/ITransactionProcessorApi.ts +0 -21
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { IHttpRequest } from "@fiado/http-client";
|
|
2
|
-
import { ApiGatewayResponse } from "@fiado/gateway-adapter";
|
|
2
|
+
import { ApiGatewayResponse, ResolvedPermissions, ResolveInput } from "@fiado/gateway-adapter";
|
|
3
3
|
import { DefineNextChallengeRequest, DefineNextChallengeResponse, PrepareChallengeRequest, PrepareChallengeResponse, VerifyChallengeRequest, VerifyChallengeResponse, AuthorizeRequest, AuthorizeResponse, EffectivePermissionsResponse, InjectableUserAttributesResponse } from "@fiado/type-kit/bin/platformRbac/index.js";
|
|
4
4
|
import { IPlatformRbacBusinessApi } from "./interfaces/IPlatformRbacBusinessApi.js";
|
|
5
5
|
/**
|
|
@@ -17,7 +17,6 @@ import { IPlatformRbacBusinessApi } from "./interfaces/IPlatformRbacBusinessApi.
|
|
|
17
17
|
*/
|
|
18
18
|
export default class PlatformRbacBusinessApi implements IPlatformRbacBusinessApi {
|
|
19
19
|
private httpRequest;
|
|
20
|
-
/** URL base del lambda. Leída de env var en cold start del consumer. */
|
|
21
20
|
private readonly baseUrl;
|
|
22
21
|
constructor(httpRequest: IHttpRequest);
|
|
23
22
|
defineNextChallenge(input: DefineNextChallengeRequest): Promise<ApiGatewayResponse<DefineNextChallengeResponse>>;
|
|
@@ -26,4 +25,5 @@ export default class PlatformRbacBusinessApi implements IPlatformRbacBusinessApi
|
|
|
26
25
|
authorize(input: AuthorizeRequest): Promise<ApiGatewayResponse<AuthorizeResponse>>;
|
|
27
26
|
effectivePermissions(cognitoSub: string, tenantId?: string): Promise<ApiGatewayResponse<EffectivePermissionsResponse>>;
|
|
28
27
|
getInjectableUserAttributes(cognitoSub: string, tenantId: string): Promise<ApiGatewayResponse<InjectableUserAttributesResponse>>;
|
|
28
|
+
resolvePermissions(input: ResolveInput): Promise<ApiGatewayResponse<ResolvedPermissions>>;
|
|
29
29
|
}
|
|
@@ -26,7 +26,9 @@ import { inject, injectable } from "inversify";
|
|
|
26
26
|
*/
|
|
27
27
|
let PlatformRbacBusinessApi = class PlatformRbacBusinessApi {
|
|
28
28
|
httpRequest;
|
|
29
|
-
|
|
29
|
+
// DEC-RBAC-069 (unificado a env var): toda la URL de rbac — incluido el endpoint del resolver — sale
|
|
30
|
+
// del env var PLATFORM_RBAC_BUSINESS_URL ('{{resolve:ssm:platform-rbac-business}}'), una por ambiente.
|
|
31
|
+
// Es server-to-server (VpcEndpoint), sin relación con el dominio de cookies del navegador.
|
|
30
32
|
baseUrl = process.env.PLATFORM_RBAC_BUSINESS_URL || "";
|
|
31
33
|
constructor(httpRequest) {
|
|
32
34
|
this.httpRequest = httpRequest;
|
|
@@ -62,6 +64,10 @@ let PlatformRbacBusinessApi = class PlatformRbacBusinessApi {
|
|
|
62
64
|
const url = `${this.baseUrl}/internal/users/${encodeURIComponent(cognitoSub)}/injectable-attributes?tenantId=${encodeURIComponent(tenantId)}`;
|
|
63
65
|
return await this.httpRequest.get(url);
|
|
64
66
|
}
|
|
67
|
+
async resolvePermissions(input) {
|
|
68
|
+
const url = `${this.baseUrl}/internal/permissions/resolve`;
|
|
69
|
+
return await this.httpRequest.post(url, input);
|
|
70
|
+
}
|
|
65
71
|
};
|
|
66
72
|
PlatformRbacBusinessApi = __decorate([
|
|
67
73
|
injectable(),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ApiGatewayResponse } from "@fiado/gateway-adapter";
|
|
1
|
+
import { ApiGatewayResponse, ResolvedPermissions, ResolveInput } from "@fiado/gateway-adapter";
|
|
2
2
|
import { DefineNextChallengeRequest, DefineNextChallengeResponse, PrepareChallengeRequest, PrepareChallengeResponse, VerifyChallengeRequest, VerifyChallengeResponse, AuthorizeRequest, AuthorizeResponse, EffectivePermissionsResponse, InjectableUserAttributesResponse } from "@fiado/type-kit/bin/platformRbac/index.js";
|
|
3
3
|
/**
|
|
4
4
|
* Contrato del publisher HTTP del lambda `platform-rbac-business` (componente 06 SureKeep Fase 0)
|
|
@@ -61,4 +61,6 @@ export interface IPlatformRbacBusinessApi {
|
|
|
61
61
|
* jwt-inyector-trigger para estampar el claim `customAttributes`. DEC-RBAC-022.
|
|
62
62
|
*/
|
|
63
63
|
getInjectableUserAttributes(cognitoSub: string, tenantId: string): Promise<ApiGatewayResponse<InjectableUserAttributesResponse>>;
|
|
64
|
+
/** POST /internal/permissions/resolve — resuelve permisos scope-bound (set completo, paginación interna) para el RBAC enforcement del gateway-adapter. DEC-RBAC-064. */
|
|
65
|
+
resolvePermissions(input: ResolveInput): Promise<ApiGatewayResponse<ResolvedPermissions>>;
|
|
64
66
|
}
|
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
export * from "./api/interfaces/IPlatformRbacBusinessApi.js";
|
|
2
2
|
export { default as PlatformRbacBusinessApi } from "./api/PlatformRbacBusinessApi.js";
|
|
3
|
+
export { ApiInvokerPermissionResolver } from "./resolver/ApiInvokerPermissionResolver.js";
|
|
4
|
+
export { apiInvokerResolverBindings } from "./resolver/apiInvokerResolverBindings.js";
|
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
export * from "./api/interfaces/IPlatformRbacBusinessApi.js";
|
|
2
2
|
export { default as PlatformRbacBusinessApi } from "./api/PlatformRbacBusinessApi.js";
|
|
3
|
+
export { ApiInvokerPermissionResolver } from "./resolver/ApiInvokerPermissionResolver.js";
|
|
4
|
+
export { apiInvokerResolverBindings } from "./resolver/apiInvokerResolverBindings.js";
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ResolveInput, ResolvedPermissions } from "@fiado/gateway-adapter";
|
|
2
|
+
import type { IPlatformRbacBusinessApi } from "../api/interfaces/IPlatformRbacBusinessApi.js";
|
|
3
|
+
/**
|
|
4
|
+
* DEC-RBAC-064/066: resuelve permisos invocando el endpoint privado de rbac-business vía el publisher
|
|
5
|
+
* tipado (HTTP sobre VpcEndpoint), reemplazando la lectura directa de DDB. Cachea la rama fallback;
|
|
6
|
+
* la rama token-trust NO se cachea (paridad con DdbPermissionResolver, evita poisoning).
|
|
7
|
+
* Cache key = `${issuer}#${cognitoSub}` (decisión consciente: el cliente no conoce el tenantId antes
|
|
8
|
+
* de invocar; issuer<->tenantId mapean 1:1 vía el pool).
|
|
9
|
+
*
|
|
10
|
+
* NOTA (TD-AI-002): `@fiado/gateway-adapter` NO re-exporta el tipo `IPermissionResolver` desde su
|
|
11
|
+
* barrel (`bin/index.d.ts` solo expone ResolveInput/ResolvedPermissions/ScopedAssignment/PERMISSION_RESOLVER)
|
|
12
|
+
* y su `exports` map bloquea el deep-import del sub-path bajo nodenext. Por eso esta clase implementa
|
|
13
|
+
* el contrato ESTRUCTURALMENTE (`resolve(input): Promise<ResolvedPermissions>`) sin la cláusula
|
|
14
|
+
* `implements IPermissionResolver`. TypeScript es structural-typed: la instancia es asignable al
|
|
15
|
+
* símbolo PERMISSION_RESOLVER en el binding. Ver docs/TECH_DEBT.md.
|
|
16
|
+
*/
|
|
17
|
+
export declare class ApiInvokerPermissionResolver {
|
|
18
|
+
private readonly _api;
|
|
19
|
+
private readonly _cache;
|
|
20
|
+
constructor(_api: IPlatformRbacBusinessApi, opts?: {
|
|
21
|
+
ttlMs: number;
|
|
22
|
+
maxEntries: number;
|
|
23
|
+
});
|
|
24
|
+
resolve(input: ResolveInput): Promise<ResolvedPermissions>;
|
|
25
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
11
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
12
|
+
};
|
|
13
|
+
import { injectable, inject } from "inversify";
|
|
14
|
+
import { TtlCache } from "@fiado/gateway-adapter";
|
|
15
|
+
/**
|
|
16
|
+
* DEC-RBAC-064/066: resuelve permisos invocando el endpoint privado de rbac-business vía el publisher
|
|
17
|
+
* tipado (HTTP sobre VpcEndpoint), reemplazando la lectura directa de DDB. Cachea la rama fallback;
|
|
18
|
+
* la rama token-trust NO se cachea (paridad con DdbPermissionResolver, evita poisoning).
|
|
19
|
+
* Cache key = `${issuer}#${cognitoSub}` (decisión consciente: el cliente no conoce el tenantId antes
|
|
20
|
+
* de invocar; issuer<->tenantId mapean 1:1 vía el pool).
|
|
21
|
+
*
|
|
22
|
+
* NOTA (TD-AI-002): `@fiado/gateway-adapter` NO re-exporta el tipo `IPermissionResolver` desde su
|
|
23
|
+
* barrel (`bin/index.d.ts` solo expone ResolveInput/ResolvedPermissions/ScopedAssignment/PERMISSION_RESOLVER)
|
|
24
|
+
* y su `exports` map bloquea el deep-import del sub-path bajo nodenext. Por eso esta clase implementa
|
|
25
|
+
* el contrato ESTRUCTURALMENTE (`resolve(input): Promise<ResolvedPermissions>`) sin la cláusula
|
|
26
|
+
* `implements IPermissionResolver`. TypeScript es structural-typed: la instancia es asignable al
|
|
27
|
+
* símbolo PERMISSION_RESOLVER en el binding. Ver docs/TECH_DEBT.md.
|
|
28
|
+
*/
|
|
29
|
+
let ApiInvokerPermissionResolver = class ApiInvokerPermissionResolver {
|
|
30
|
+
_api;
|
|
31
|
+
_cache;
|
|
32
|
+
constructor(_api, opts = {
|
|
33
|
+
ttlMs: Number(process.env.RBAC_AUTHZ_CACHE_TTL_MS) || 60000,
|
|
34
|
+
maxEntries: 500,
|
|
35
|
+
}) {
|
|
36
|
+
this._api = _api;
|
|
37
|
+
this._cache = new TtlCache(opts);
|
|
38
|
+
}
|
|
39
|
+
async resolve(input) {
|
|
40
|
+
const cacheable = input.roleAssignmentsFromToken === undefined;
|
|
41
|
+
const key = `${input.issuer ?? ""}#${input.cognitoSub}`;
|
|
42
|
+
if (cacheable) {
|
|
43
|
+
const hit = this._cache.get(key);
|
|
44
|
+
if (hit)
|
|
45
|
+
return hit;
|
|
46
|
+
}
|
|
47
|
+
// DEC-RBAC-071: fail-closed por propagación. Si el invoke (HTTP/VpcEndpoint) falla, se propaga:
|
|
48
|
+
// en el adapter, enforceRbac mapea solo PermissionDeniedError a 403; este error se re-lanza -> 5xx.
|
|
49
|
+
// Es fail-closed (no concede), NO un 403, y es deliberado: un fallo de red NO debe verse como
|
|
50
|
+
// "usuario sin permisos". NUNCA devolver { assignments: [] } en un catch.
|
|
51
|
+
const resp = await this._api.resolvePermissions(input);
|
|
52
|
+
const resolved = resp.body;
|
|
53
|
+
if (cacheable)
|
|
54
|
+
this._cache.set(key, resolved);
|
|
55
|
+
return resolved;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
ApiInvokerPermissionResolver = __decorate([
|
|
59
|
+
injectable(),
|
|
60
|
+
__param(0, inject("IPlatformRbacBusinessApi")),
|
|
61
|
+
__metadata("design:paramtypes", [Object, Object])
|
|
62
|
+
], ApiInvokerPermissionResolver);
|
|
63
|
+
export { ApiInvokerPermissionResolver };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { ContainerModule } from "inversify";
|
|
2
|
+
/** DEC-RBAC-066: bindea ApiInvokerPermissionResolver al símbolo PERMISSION_RESOLVER de gateway-adapter.
|
|
3
|
+
* El lambda consumidor carga este módulo DESPUÉS de gatewayAdapterBindings y de bindear "IPlatformRbacBusinessApi". */
|
|
4
|
+
export declare const apiInvokerResolverBindings: ContainerModule;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ContainerModule } from "inversify";
|
|
2
|
+
import { PERMISSION_RESOLVER } from "@fiado/gateway-adapter";
|
|
3
|
+
import { ApiInvokerPermissionResolver } from "./ApiInvokerPermissionResolver.js";
|
|
4
|
+
/** DEC-RBAC-066: bindea ApiInvokerPermissionResolver al símbolo PERMISSION_RESOLVER de gateway-adapter.
|
|
5
|
+
* El lambda consumidor carga este módulo DESPUÉS de gatewayAdapterBindings y de bindear "IPlatformRbacBusinessApi". */
|
|
6
|
+
export const apiInvokerResolverBindings = new ContainerModule(({ bind }) => {
|
|
7
|
+
bind(PERMISSION_RESOLVER)
|
|
8
|
+
.toDynamicValue((ctx) => {
|
|
9
|
+
const api = ctx.get("IPlatformRbacBusinessApi");
|
|
10
|
+
return new ApiInvokerPermissionResolver(api);
|
|
11
|
+
})
|
|
12
|
+
.inSingletonScope();
|
|
13
|
+
});
|
|
@@ -4,7 +4,6 @@ import { AuthorizeCollectorOrderRequest, AuthorizeCollectorOrderResponse, Author
|
|
|
4
4
|
import { ApiGatewayResponse } from "@fiado/gateway-adapter";
|
|
5
5
|
import { ValidateWalletFundingRequest, ValidateWalletFundingResponse, CreditWalletFundingRequest, CreditWalletFundingResponse, ReverseWalletFundingRequest, ReverseWalletFundingResponse, GetWalletFundingLimitRequest, GetWalletFundingLimitResponse } from "@fiado/type-kit/bin/walletFunding/index.js";
|
|
6
6
|
import { RemittanceCreditBackRequest, RemittanceCreditBackResponse } from "@fiado/type-kit/bin/remittance/index.js";
|
|
7
|
-
import { PublishFiadoTxRequest, PublishFiadoTxResponse } from "@fiado/type-kit/bin/transactionProcessor/index.js";
|
|
8
7
|
type ProcessUpdateLevelRequest = import("@fiado/type-kit/bin/transactionProcessor/dtos/ProcessUpdateLevelRequest.js").default;
|
|
9
8
|
type FiadoApiResponse<T> = import("@fiado/type-kit/bin/apiResponse/dtos/FiadoApiResponse.js").default<T>;
|
|
10
9
|
export default class TransactionProcessorApi implements ITransactionProcessorApi {
|
|
@@ -23,6 +22,5 @@ export default class TransactionProcessorApi implements ITransactionProcessorApi
|
|
|
23
22
|
walletFundingReverse(request: ReverseWalletFundingRequest): Promise<ApiGatewayResponse<ReverseWalletFundingResponse>>;
|
|
24
23
|
walletFundingGetLimit(request: GetWalletFundingLimitRequest): Promise<ApiGatewayResponse<GetWalletFundingLimitResponse>>;
|
|
25
24
|
creditBack(request: RemittanceCreditBackRequest): Promise<FiadoApiResponse<RemittanceCreditBackResponse>>;
|
|
26
|
-
publishFiadoTx(request: PublishFiadoTxRequest): Promise<ApiGatewayResponse<PublishFiadoTxResponse>>;
|
|
27
25
|
}
|
|
28
26
|
export {};
|
|
@@ -65,10 +65,6 @@ let TransactionProcessorApi = class TransactionProcessorApi {
|
|
|
65
65
|
const url = `${this.baseUrl}remittance/credit-back`;
|
|
66
66
|
return await this.httpRequest.post(url, request);
|
|
67
67
|
}
|
|
68
|
-
async publishFiadoTx(request) {
|
|
69
|
-
const url = `${this.baseUrl}wallet-funding/publish`;
|
|
70
|
-
return await this.httpRequest.post(url, request);
|
|
71
|
-
}
|
|
72
68
|
};
|
|
73
69
|
TransactionProcessorApi = __decorate([
|
|
74
70
|
injectable(),
|
|
@@ -3,7 +3,6 @@ import { AuthorizeCollectorOrderRequest, AuthorizeCollectorOrderResponse, Author
|
|
|
3
3
|
import { ApiGatewayResponse } from "@fiado/gateway-adapter";
|
|
4
4
|
import { ValidateWalletFundingRequest, ValidateWalletFundingResponse, CreditWalletFundingRequest, CreditWalletFundingResponse, ReverseWalletFundingRequest, ReverseWalletFundingResponse, GetWalletFundingLimitRequest, GetWalletFundingLimitResponse } from "@fiado/type-kit/bin/walletFunding/index.js";
|
|
5
5
|
import { RemittanceCreditBackRequest, RemittanceCreditBackResponse } from "@fiado/type-kit/bin/remittance/index.js";
|
|
6
|
-
import { PublishFiadoTxRequest, PublishFiadoTxResponse } from "@fiado/type-kit/bin/transactionProcessor/index.js";
|
|
7
6
|
type ProcessUpdateLevelRequest = import("@fiado/type-kit/bin/transactionProcessor/dtos/ProcessUpdateLevelRequest.js").default;
|
|
8
7
|
export interface ITransactionProcessorApi {
|
|
9
8
|
authorizeTransaction(request: AuthorizeCreditCardPurchaseRequest): Promise<any>;
|
|
@@ -44,22 +43,5 @@ export interface ITransactionProcessorApi {
|
|
|
44
43
|
* Backend: POST /transaction-processor/remittance/credit-back (privado VPC).
|
|
45
44
|
*/
|
|
46
45
|
creditBack(request: RemittanceCreditBackRequest): Promise<FiadoApiResponse<RemittanceCreditBackResponse>>;
|
|
47
|
-
/**
|
|
48
|
-
* Publica el evento FiadoTx del wallet-funding cash-in al SQS
|
|
49
|
-
* TRANSACTION_QUEUE para que transaction-business persista la
|
|
50
|
-
* tupla EQ_CASH_SA en FiadoTx_GT.
|
|
51
|
-
*
|
|
52
|
-
* Ownership del SQS sigue en el processor (el settlement NO publica,
|
|
53
|
-
* solo ejecuta el TransactWriteItems atómico). Best-effort: status
|
|
54
|
-
* FAILED indica que sqs.sendMessage falló — el caller debe loggear
|
|
55
|
-
* y continuar; el reconciler T+0 del connector recupera gaps.
|
|
56
|
-
*
|
|
57
|
-
* Backend: POST /transaction-processor/wallet-funding/publish (privado VPC).
|
|
58
|
-
* Consumido por equality-connector (post-settlement OK) y por su
|
|
59
|
-
* FiadoTxGapReconcilerCron.
|
|
60
|
-
*
|
|
61
|
-
* Ver spec 15 §6.2.
|
|
62
|
-
*/
|
|
63
|
-
publishFiadoTx(request: PublishFiadoTxRequest): Promise<ApiGatewayResponse<PublishFiadoTxResponse>>;
|
|
64
46
|
}
|
|
65
47
|
export {};
|
package/jest.config.cjs
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** @type {import('ts-jest').JestConfigWithTsJest} */
|
|
2
|
+
module.exports = {
|
|
3
|
+
testEnvironment: 'node',
|
|
4
|
+
testMatch: ['**/test/**/*.test.ts'],
|
|
5
|
+
setupFiles: ['reflect-metadata'],
|
|
6
|
+
extensionsToTreatAsEsm: ['.ts'],
|
|
7
|
+
moduleNameMapper: { '^(\\.{1,2}/.*)\\.js$': '$1' },
|
|
8
|
+
transform: {
|
|
9
|
+
'^.+\\.[tj]sx?$': ['ts-jest', { useESM: true, isolatedModules: true, tsconfig: 'tsconfig.test.json' }],
|
|
10
|
+
},
|
|
11
|
+
transformIgnorePatterns: ['/node_modules/(?!(inversify|@inversifyjs)/)'],
|
|
12
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fiado/api-invoker",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.39.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",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"license": "ISC",
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@aws-sdk/client-sqs": "^3.919.0",
|
|
34
|
-
"@fiado/gateway-adapter": "^
|
|
34
|
+
"@fiado/gateway-adapter": "^3.9.0",
|
|
35
35
|
"@fiado/http-client": "^2.0.1",
|
|
36
36
|
"@fiado/logger": "^1.1.3",
|
|
37
37
|
"@fiado/type-kit": "^3.123.0",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { inject, injectable } from "inversify";
|
|
2
2
|
import type { IHttpRequest } from "@fiado/http-client";
|
|
3
|
-
import { ApiGatewayResponse } from "@fiado/gateway-adapter";
|
|
3
|
+
import { ApiGatewayResponse, ResolvedPermissions, ResolveInput } from "@fiado/gateway-adapter";
|
|
4
4
|
// TD-AI-001: import de @fiado/type-kit/bin/platformRbac requiere npm link a type-kit 3.43.1 hasta publish final. Ver docs/TECH_DEBT.md.
|
|
5
5
|
import {
|
|
6
6
|
DefineNextChallengeRequest,
|
|
@@ -31,7 +31,9 @@ import { IPlatformRbacBusinessApi } from "./interfaces/IPlatformRbacBusinessApi.
|
|
|
31
31
|
*/
|
|
32
32
|
@injectable()
|
|
33
33
|
export default class PlatformRbacBusinessApi implements IPlatformRbacBusinessApi {
|
|
34
|
-
|
|
34
|
+
// DEC-RBAC-069 (unificado a env var): toda la URL de rbac — incluido el endpoint del resolver — sale
|
|
35
|
+
// del env var PLATFORM_RBAC_BUSINESS_URL ('{{resolve:ssm:platform-rbac-business}}'), una por ambiente.
|
|
36
|
+
// Es server-to-server (VpcEndpoint), sin relación con el dominio de cookies del navegador.
|
|
35
37
|
private readonly baseUrl = process.env.PLATFORM_RBAC_BUSINESS_URL || "";
|
|
36
38
|
|
|
37
39
|
constructor(@inject("IHttpRequest") private httpRequest: IHttpRequest) {}
|
|
@@ -86,4 +88,11 @@ export default class PlatformRbacBusinessApi implements IPlatformRbacBusinessApi
|
|
|
86
88
|
const url = `${this.baseUrl}/internal/users/${encodeURIComponent(cognitoSub)}/injectable-attributes?tenantId=${encodeURIComponent(tenantId)}`;
|
|
87
89
|
return await this.httpRequest.get(url);
|
|
88
90
|
}
|
|
91
|
+
|
|
92
|
+
async resolvePermissions(
|
|
93
|
+
input: ResolveInput,
|
|
94
|
+
): Promise<ApiGatewayResponse<ResolvedPermissions>> {
|
|
95
|
+
const url = `${this.baseUrl}/internal/permissions/resolve`;
|
|
96
|
+
return await this.httpRequest.post(url, input);
|
|
97
|
+
}
|
|
89
98
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ApiGatewayResponse } from "@fiado/gateway-adapter";
|
|
1
|
+
import { ApiGatewayResponse, ResolvedPermissions, ResolveInput } from "@fiado/gateway-adapter";
|
|
2
2
|
import {
|
|
3
3
|
DefineNextChallengeRequest,
|
|
4
4
|
DefineNextChallengeResponse,
|
|
@@ -92,4 +92,7 @@ export interface IPlatformRbacBusinessApi {
|
|
|
92
92
|
cognitoSub: string,
|
|
93
93
|
tenantId: string,
|
|
94
94
|
): Promise<ApiGatewayResponse<InjectableUserAttributesResponse>>;
|
|
95
|
+
|
|
96
|
+
/** POST /internal/permissions/resolve — resuelve permisos scope-bound (set completo, paginación interna) para el RBAC enforcement del gateway-adapter. DEC-RBAC-064. */
|
|
97
|
+
resolvePermissions(input: ResolveInput): Promise<ApiGatewayResponse<ResolvedPermissions>>;
|
|
95
98
|
}
|
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
export * from "./api/interfaces/IPlatformRbacBusinessApi.js";
|
|
2
2
|
export { default as PlatformRbacBusinessApi } from "./api/PlatformRbacBusinessApi.js";
|
|
3
|
+
export { ApiInvokerPermissionResolver } from "./resolver/ApiInvokerPermissionResolver.js";
|
|
4
|
+
export { apiInvokerResolverBindings } from "./resolver/apiInvokerResolverBindings.js";
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { injectable, inject } from "inversify";
|
|
2
|
+
import type { ResolveInput, ResolvedPermissions } from "@fiado/gateway-adapter";
|
|
3
|
+
import { TtlCache } from "@fiado/gateway-adapter";
|
|
4
|
+
import type { IPlatformRbacBusinessApi } from "../api/interfaces/IPlatformRbacBusinessApi.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* DEC-RBAC-064/066: resuelve permisos invocando el endpoint privado de rbac-business vía el publisher
|
|
8
|
+
* tipado (HTTP sobre VpcEndpoint), reemplazando la lectura directa de DDB. Cachea la rama fallback;
|
|
9
|
+
* la rama token-trust NO se cachea (paridad con DdbPermissionResolver, evita poisoning).
|
|
10
|
+
* Cache key = `${issuer}#${cognitoSub}` (decisión consciente: el cliente no conoce el tenantId antes
|
|
11
|
+
* de invocar; issuer<->tenantId mapean 1:1 vía el pool).
|
|
12
|
+
*
|
|
13
|
+
* NOTA (TD-AI-002): `@fiado/gateway-adapter` NO re-exporta el tipo `IPermissionResolver` desde su
|
|
14
|
+
* barrel (`bin/index.d.ts` solo expone ResolveInput/ResolvedPermissions/ScopedAssignment/PERMISSION_RESOLVER)
|
|
15
|
+
* y su `exports` map bloquea el deep-import del sub-path bajo nodenext. Por eso esta clase implementa
|
|
16
|
+
* el contrato ESTRUCTURALMENTE (`resolve(input): Promise<ResolvedPermissions>`) sin la cláusula
|
|
17
|
+
* `implements IPermissionResolver`. TypeScript es structural-typed: la instancia es asignable al
|
|
18
|
+
* símbolo PERMISSION_RESOLVER en el binding. Ver docs/TECH_DEBT.md.
|
|
19
|
+
*/
|
|
20
|
+
@injectable()
|
|
21
|
+
export class ApiInvokerPermissionResolver {
|
|
22
|
+
private readonly _cache: TtlCache<ResolvedPermissions>;
|
|
23
|
+
constructor(
|
|
24
|
+
@inject("IPlatformRbacBusinessApi") private readonly _api: IPlatformRbacBusinessApi,
|
|
25
|
+
opts: { ttlMs: number; maxEntries: number } = {
|
|
26
|
+
ttlMs: Number(process.env.RBAC_AUTHZ_CACHE_TTL_MS) || 60000,
|
|
27
|
+
maxEntries: 500,
|
|
28
|
+
},
|
|
29
|
+
) {
|
|
30
|
+
this._cache = new TtlCache<ResolvedPermissions>(opts);
|
|
31
|
+
}
|
|
32
|
+
async resolve(input: ResolveInput): Promise<ResolvedPermissions> {
|
|
33
|
+
const cacheable = input.roleAssignmentsFromToken === undefined;
|
|
34
|
+
const key = `${input.issuer ?? ""}#${input.cognitoSub}`;
|
|
35
|
+
if (cacheable) {
|
|
36
|
+
const hit = this._cache.get(key);
|
|
37
|
+
if (hit) return hit;
|
|
38
|
+
}
|
|
39
|
+
// DEC-RBAC-071: fail-closed por propagación. Si el invoke (HTTP/VpcEndpoint) falla, se propaga:
|
|
40
|
+
// en el adapter, enforceRbac mapea solo PermissionDeniedError a 403; este error se re-lanza -> 5xx.
|
|
41
|
+
// Es fail-closed (no concede), NO un 403, y es deliberado: un fallo de red NO debe verse como
|
|
42
|
+
// "usuario sin permisos". NUNCA devolver { assignments: [] } en un catch.
|
|
43
|
+
const resp = await this._api.resolvePermissions(input);
|
|
44
|
+
const resolved = resp.body;
|
|
45
|
+
if (cacheable) this._cache.set(key, resolved);
|
|
46
|
+
return resolved;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ContainerModule } from "inversify";
|
|
2
|
+
import { PERMISSION_RESOLVER } from "@fiado/gateway-adapter";
|
|
3
|
+
import type { ResolvedPermissions, ResolveInput } from "@fiado/gateway-adapter";
|
|
4
|
+
import type { IPlatformRbacBusinessApi } from "../api/interfaces/IPlatformRbacBusinessApi.js";
|
|
5
|
+
import { ApiInvokerPermissionResolver } from "./ApiInvokerPermissionResolver.js";
|
|
6
|
+
|
|
7
|
+
/** Contrato estructural de `IPermissionResolver` del gateway-adapter.
|
|
8
|
+
* El barrel del adapter no re-exporta el tipo `IPermissionResolver` (ver TD-AI-002 en
|
|
9
|
+
* ApiInvokerPermissionResolver.ts), así que se replica el shape mínimo para tipar el binding. */
|
|
10
|
+
type IPermissionResolver = {
|
|
11
|
+
resolve(input: ResolveInput): Promise<ResolvedPermissions>;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/** DEC-RBAC-066: bindea ApiInvokerPermissionResolver al símbolo PERMISSION_RESOLVER de gateway-adapter.
|
|
15
|
+
* El lambda consumidor carga este módulo DESPUÉS de gatewayAdapterBindings y de bindear "IPlatformRbacBusinessApi". */
|
|
16
|
+
export const apiInvokerResolverBindings = new ContainerModule(({ bind }) => {
|
|
17
|
+
bind<IPermissionResolver>(PERMISSION_RESOLVER)
|
|
18
|
+
.toDynamicValue((ctx) => {
|
|
19
|
+
const api = ctx.get<IPlatformRbacBusinessApi>("IPlatformRbacBusinessApi");
|
|
20
|
+
return new ApiInvokerPermissionResolver(api);
|
|
21
|
+
})
|
|
22
|
+
.inSingletonScope();
|
|
23
|
+
});
|
|
@@ -22,7 +22,6 @@ import {
|
|
|
22
22
|
GetWalletFundingLimitResponse,
|
|
23
23
|
} from "@fiado/type-kit/bin/walletFunding/index.js";
|
|
24
24
|
import { RemittanceCreditBackRequest, RemittanceCreditBackResponse } from "@fiado/type-kit/bin/remittance/index.js";
|
|
25
|
-
import { PublishFiadoTxRequest, PublishFiadoTxResponse } from "@fiado/type-kit/bin/transactionProcessor/index.js";
|
|
26
25
|
type ProcessUpdateLevelRequest = import("@fiado/type-kit/bin/transactionProcessor/dtos/ProcessUpdateLevelRequest.js").default;
|
|
27
26
|
type FiadoApiResponse<T> = import("@fiado/type-kit/bin/apiResponse/dtos/FiadoApiResponse.js").default<T>;
|
|
28
27
|
|
|
@@ -103,11 +102,4 @@ export default class TransactionProcessorApi implements ITransactionProcessorApi
|
|
|
103
102
|
const url = `${this.baseUrl}remittance/credit-back`;
|
|
104
103
|
return await this.httpRequest.post(url, request);
|
|
105
104
|
}
|
|
106
|
-
|
|
107
|
-
async publishFiadoTx(
|
|
108
|
-
request: PublishFiadoTxRequest,
|
|
109
|
-
): Promise<ApiGatewayResponse<PublishFiadoTxResponse>> {
|
|
110
|
-
const url = `${this.baseUrl}wallet-funding/publish`;
|
|
111
|
-
return await this.httpRequest.post(url, request);
|
|
112
|
-
}
|
|
113
105
|
}
|
|
@@ -21,7 +21,6 @@ import {
|
|
|
21
21
|
GetWalletFundingLimitResponse,
|
|
22
22
|
} from "@fiado/type-kit/bin/walletFunding/index.js";
|
|
23
23
|
import { RemittanceCreditBackRequest, RemittanceCreditBackResponse } from "@fiado/type-kit/bin/remittance/index.js";
|
|
24
|
-
import { PublishFiadoTxRequest, PublishFiadoTxResponse } from "@fiado/type-kit/bin/transactionProcessor/index.js";
|
|
25
24
|
type ProcessUpdateLevelRequest = import("@fiado/type-kit/bin/transactionProcessor/dtos/ProcessUpdateLevelRequest.js").default;
|
|
26
25
|
|
|
27
26
|
export interface ITransactionProcessorApi {
|
|
@@ -85,24 +84,4 @@ export interface ITransactionProcessorApi {
|
|
|
85
84
|
creditBack(
|
|
86
85
|
request: RemittanceCreditBackRequest,
|
|
87
86
|
): Promise<FiadoApiResponse<RemittanceCreditBackResponse>>;
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Publica el evento FiadoTx del wallet-funding cash-in al SQS
|
|
91
|
-
* TRANSACTION_QUEUE para que transaction-business persista la
|
|
92
|
-
* tupla EQ_CASH_SA en FiadoTx_GT.
|
|
93
|
-
*
|
|
94
|
-
* Ownership del SQS sigue en el processor (el settlement NO publica,
|
|
95
|
-
* solo ejecuta el TransactWriteItems atómico). Best-effort: status
|
|
96
|
-
* FAILED indica que sqs.sendMessage falló — el caller debe loggear
|
|
97
|
-
* y continuar; el reconciler T+0 del connector recupera gaps.
|
|
98
|
-
*
|
|
99
|
-
* Backend: POST /transaction-processor/wallet-funding/publish (privado VPC).
|
|
100
|
-
* Consumido por equality-connector (post-settlement OK) y por su
|
|
101
|
-
* FiadoTxGapReconcilerCron.
|
|
102
|
-
*
|
|
103
|
-
* Ver spec 15 §6.2.
|
|
104
|
-
*/
|
|
105
|
-
publishFiadoTx(
|
|
106
|
-
request: PublishFiadoTxRequest,
|
|
107
|
-
): Promise<ApiGatewayResponse<PublishFiadoTxResponse>>;
|
|
108
87
|
}
|