permissions-contractx 2.2.0 → 2.4.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.
Files changed (32) hide show
  1. package/README.md +93 -0
  2. package/dist/constants/permission-names.constants.d.ts +40 -4
  3. package/dist/constants/permission-names.constants.d.ts.map +1 -1
  4. package/dist/constants/permission-names.constants.js +53 -6
  5. package/dist/index.d.ts +1 -0
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +1 -0
  8. package/dist/inter-service-auth/index.d.ts +8 -0
  9. package/dist/inter-service-auth/index.d.ts.map +1 -0
  10. package/dist/inter-service-auth/index.js +31 -0
  11. package/dist/inter-service-auth/inter-service-auth.config.interface.d.ts +10 -0
  12. package/dist/inter-service-auth/inter-service-auth.config.interface.d.ts.map +1 -0
  13. package/dist/inter-service-auth/inter-service-auth.config.interface.js +2 -0
  14. package/dist/inter-service-auth/inter-service-auth.middleware.d.ts +11 -0
  15. package/dist/inter-service-auth/inter-service-auth.middleware.d.ts.map +1 -0
  16. package/dist/inter-service-auth/inter-service-auth.middleware.js +75 -0
  17. package/dist/inter-service-auth/inter-service-auth.module.d.ts +11 -0
  18. package/dist/inter-service-auth/inter-service-auth.module.d.ts.map +1 -0
  19. package/dist/inter-service-auth/inter-service-auth.module.js +44 -0
  20. package/dist/inter-service-auth/inter-service-auth.service.d.ts +23 -0
  21. package/dist/inter-service-auth/inter-service-auth.service.d.ts.map +1 -0
  22. package/dist/inter-service-auth/inter-service-auth.service.js +202 -0
  23. package/dist/inter-service-auth/inter-service-auth.types.d.ts +57 -0
  24. package/dist/inter-service-auth/inter-service-auth.types.d.ts.map +1 -0
  25. package/dist/inter-service-auth/inter-service-auth.types.js +15 -0
  26. package/dist/inter-service-auth/service-auth.decorators.d.ts +12 -0
  27. package/dist/inter-service-auth/service-auth.decorators.d.ts.map +1 -0
  28. package/dist/inter-service-auth/service-auth.decorators.js +22 -0
  29. package/dist/inter-service-auth/service-auth.guard.d.ts +17 -0
  30. package/dist/inter-service-auth/service-auth.guard.d.ts.map +1 -0
  31. package/dist/inter-service-auth/service-auth.guard.js +121 -0
  32. package/package.json +1 -1
package/README.md CHANGED
@@ -84,6 +84,99 @@ import { PermissionWrites } from 'permissions-contractx';
84
84
  search() { /* ... */ }
85
85
  ```
86
86
 
87
+ ## Inter-Service Authentication (HMAC)
88
+
89
+ Además del modelo de autorización basado en JWT de usuario, el paquete incluye el sistema de autenticación **machine-to-machine (M2M)** que usan los microservicios de ContractX para llamarse entre sí de forma segura.
90
+
91
+ Cada llamada saliente lleva headers `X-Service-*` con una firma HMAC-SHA256 del par `apiKey + secretKey` del servicio emisor. El receptor verifica la firma, el timestamp (ventana de expiración + drift de reloj) y el nonce (anti-replay). Ningún JWT de usuario interviene en estas llamadas.
92
+
93
+ Para detalles de implementación, arquitectura y deuda conocida, ver [`src/inter-service-auth/README.md`](./src/inter-service-auth/README.md).
94
+
95
+ ### Configurar el módulo
96
+
97
+ ```typescript
98
+ import { InterServiceAuthModule, SERVICE_NAMES } from 'permissions-contractx';
99
+ import { ConfigModule, ConfigService } from '@nestjs/config';
100
+
101
+ @Module({
102
+ imports: [
103
+ InterServiceAuthModule.forRootAsync({
104
+ imports: [ConfigModule],
105
+ useFactory: (cfg: ConfigService) => ({
106
+ services: {
107
+ [SERVICE_NAMES.AUTH]: {
108
+ serviceId: SERVICE_NAMES.AUTH,
109
+ apiKey: cfg.get('AUTH_API_KEY'),
110
+ secretKey: cfg.get('AUTH_SECRET_KEY'),
111
+ },
112
+ [SERVICE_NAMES.CONTRACTS]: {
113
+ serviceId: SERVICE_NAMES.CONTRACTS,
114
+ apiKey: cfg.get('CONTRACTS_API_KEY'),
115
+ secretKey: cfg.get('CONTRACTS_SECRET_KEY'),
116
+ },
117
+ },
118
+ signatureExpireMinutes: 5,
119
+ allowedTimeDriftSeconds: 30,
120
+ enableLogging: cfg.get('SERVICE_AUTH_LOGGING') === 'true',
121
+ }),
122
+ inject: [ConfigService],
123
+ }),
124
+ ],
125
+ })
126
+ export class AppModule {}
127
+ ```
128
+
129
+ El middleware (`InterServiceAuthMiddleware`) debe registrarse en el módulo consumidor:
130
+
131
+ ```typescript
132
+ import { InterServiceAuthMiddleware } from 'permissions-contractx';
133
+
134
+ export class AppModule implements NestModule {
135
+ configure(consumer: MiddlewareConsumer) {
136
+ consumer.apply(InterServiceAuthMiddleware).forRoutes('*');
137
+ }
138
+ }
139
+ ```
140
+
141
+ ### Proteger un endpoint (solo M2M)
142
+
143
+ ```typescript
144
+ import {
145
+ ServiceAuthGuard,
146
+ RequireServiceAuth,
147
+ RequireServicePermissions,
148
+ } from 'permissions-contractx';
149
+
150
+ @Controller('internal')
151
+ @RequireServiceAuth() // rechaza cualquier request no-M2M
152
+ @UseGuards(ServiceAuthGuard)
153
+ export class InternalController {
154
+ @Get('contracts')
155
+ @RequireServicePermissions('contracts.read')
156
+ listContracts() { /* ... */ }
157
+ }
158
+ ```
159
+
160
+ ### Emitir una llamada M2M saliente
161
+
162
+ ```typescript
163
+ constructor(private readonly authSvc: InterServiceAuthService) {}
164
+
165
+ async fetchFromContracts(schema: string) {
166
+ const headers = this.authSvc.generateAuthHeaders(SERVICE_NAMES.CONTRACTS, schema);
167
+ return this.httpService.get('/internal/contracts', { headers }).toPromise();
168
+ }
169
+ ```
170
+
171
+ ### Decoradores disponibles
172
+
173
+ | Decorador | Propósito |
174
+ |---|---|
175
+ | `@RequireServiceAuth()` | Endpoint exclusivo M2M; rechaza JWT de usuario |
176
+ | `@AllowServiceAuth(false)` | Bloquea M2M en un endpoint específico (gana sobre clase) |
177
+ | `@RequireServicePermissions(...p)` | El servicio llamante debe tener todos los permisos listados |
178
+ | `@RequireServiceRoles(...r)` | El servicio llamante debe tener al menos uno de los roles listados |
179
+
87
180
  ## Modelo de cuatro estados (migración)
88
181
 
89
182
  El `AuthorizationGuard` permite migrar cada endpoint de forma gradual mediante la variable de entorno de enforcement:
@@ -1,17 +1,21 @@
1
1
  /**
2
2
  * permission-names.constants.ts
3
3
  * ------------------------------------------------------------------------
4
- * Constantes de NOMBRES (codes) de los 237 permisos de dominio de ContractX.
4
+ * Constantes de NOMBRES (codes) de los 264 permisos de dominio de ContractX.
5
5
  * Derivadas del catálogo de Auth (domain-permissions.catalog.ts, ADR-004 Fase 1/2),
6
6
  * tomando SOLO el campo `code` — SIN la metadata de seeding (rolesGranted,
7
7
  * rolesReadOnly, approvalCategory, side, requiresHuman), que vive solo en Auth.
8
+ * Los comentarios `// rolesGranted: ...` sobre cada constante nueva desde
9
+ * v2.4.0 son referencia informal para la siembra en Auth, no metadata real
10
+ * de este paquete.
8
11
  *
9
12
  * Fuente única de NOMBRES: este archivo (en el paquete permissions-contractx).
10
13
  * La metadata se cuelga encima, en Auth. Un test en Auth valida que todo `code`
11
14
  * del catálogo existe como constante aquí (sincronización — Fase 6/8 del porte).
12
15
  *
13
- * Agrupación por microservicio: Auth(13) Contratos(60) Entregables(48)
14
- * Facturación(27) SLAs(45) Proveedores(46) = 239.
16
+ * Agrupación por microservicio: Auth(13) Contratos(73) Entregables(52)
17
+ * Facturación(29) SLAs(45) Reuniones(2) UserContractRole(3) Proveedores(46)
18
+ * Workflows(1) = 264.
15
19
  *
16
20
  * Convención de claves: reflejan el recurso completo del code, en
17
21
  * SCREAMING_SNAKE_CASE (recurso + acción). Esto evita colisiones entre dominios
@@ -98,6 +102,17 @@ export declare const CONTRATOS_PERMISSIONS: {
98
102
  readonly WORK_ORDERS_DELETE: "work_orders.delete";
99
103
  readonly RAID_DECISIONS_DELETE: "raid_decisions.delete";
100
104
  readonly RAID_ISSUES_DELETE: "raid_issues.delete";
105
+ readonly CONTRACT_TYPES_VIEW: "contract_types.view";
106
+ readonly CONTRACT_TYPES_VIEW_DETAIL: "contract_types.view_detail";
107
+ readonly CONTRACT_CLAUSES_VIEW_WALK_AWAY_NOTES: "contract_clauses.view_walk_away_notes";
108
+ readonly CONTRACT_CLAUSES_VIEW_NEGOTIATION_NOTES: "contract_clauses.view_negotiation_notes";
109
+ readonly CONTRACT_CLAUSE_DEVIATIONS_VIEW: "contract_clause_deviations.view";
110
+ readonly CONTRACT_CLAUSE_DEVIATIONS_CREATE: "contract_clause_deviations.create";
111
+ readonly CONTRACT_CLAUSE_DEVIATIONS_APPROVE_WALK_AWAY_BREACH: "contract_clause_deviations.approve_walk_away_breach";
112
+ readonly CONTRACT_OBLIGATIONS_VIEW_LIST: "contract_obligations.view_list";
113
+ readonly CONTRACT_OBLIGATIONS_VIEW_DETAIL: "contract_obligations.view_detail";
114
+ readonly CONTRACT_OBLIGATIONS_WAIVE: "contract_obligations.waive";
115
+ readonly CONTRACTS_VIEW_RISK_DETAILS: "contracts.view_risk_details";
101
116
  };
102
117
  export declare const ENTREGABLES_PERMISSIONS: {
103
118
  readonly DELIVERABLES_CREATE: "deliverables.create";
@@ -150,6 +165,8 @@ export declare const ENTREGABLES_PERMISSIONS: {
150
165
  readonly REJECTION_REASONS_DELETE: "rejection_reasons.delete";
151
166
  readonly DELIVERABLES_PLANNING_PLAN: "deliverables.planning.plan";
152
167
  readonly DELIVERABLES_PLANNING_REPLAN: "deliverables.planning.replan";
168
+ readonly DELIVERABLE_REJECTION_CATALOG_MANAGE: "deliverable_rejection_catalog.manage";
169
+ readonly DELIVERABLES_RESOLVE_DISPUTE: "deliverables.resolve_dispute";
153
170
  };
154
171
  export declare const FACTURACION_PERMISSIONS: {
155
172
  readonly INVOICES_REGISTER: "invoices.register";
@@ -337,6 +354,9 @@ export declare const PROVEEDORES_PERMISSIONS: {
337
354
  readonly PROVIDERS_RISKS_DELETE: "providers.risks.delete";
338
355
  readonly PROVIDERS_SURVEYS_DELETE: "providers.surveys.delete";
339
356
  };
357
+ export declare const WORKFLOWS_PERMISSIONS: {
358
+ readonly WORKFLOWS_ADMIN: "workflows.admin";
359
+ };
340
360
  /** Todas las constantes agrupadas por dominio. */
341
361
  export declare const PERMISSIONS_BY_DOMAIN: {
342
362
  readonly Auth: {
@@ -417,6 +437,17 @@ export declare const PERMISSIONS_BY_DOMAIN: {
417
437
  readonly WORK_ORDERS_DELETE: "work_orders.delete";
418
438
  readonly RAID_DECISIONS_DELETE: "raid_decisions.delete";
419
439
  readonly RAID_ISSUES_DELETE: "raid_issues.delete";
440
+ readonly CONTRACT_TYPES_VIEW: "contract_types.view";
441
+ readonly CONTRACT_TYPES_VIEW_DETAIL: "contract_types.view_detail";
442
+ readonly CONTRACT_CLAUSES_VIEW_WALK_AWAY_NOTES: "contract_clauses.view_walk_away_notes";
443
+ readonly CONTRACT_CLAUSES_VIEW_NEGOTIATION_NOTES: "contract_clauses.view_negotiation_notes";
444
+ readonly CONTRACT_CLAUSE_DEVIATIONS_VIEW: "contract_clause_deviations.view";
445
+ readonly CONTRACT_CLAUSE_DEVIATIONS_CREATE: "contract_clause_deviations.create";
446
+ readonly CONTRACT_CLAUSE_DEVIATIONS_APPROVE_WALK_AWAY_BREACH: "contract_clause_deviations.approve_walk_away_breach";
447
+ readonly CONTRACT_OBLIGATIONS_VIEW_LIST: "contract_obligations.view_list";
448
+ readonly CONTRACT_OBLIGATIONS_VIEW_DETAIL: "contract_obligations.view_detail";
449
+ readonly CONTRACT_OBLIGATIONS_WAIVE: "contract_obligations.waive";
450
+ readonly CONTRACTS_VIEW_RISK_DETAILS: "contracts.view_risk_details";
420
451
  };
421
452
  readonly Entregables: {
422
453
  readonly DELIVERABLES_CREATE: "deliverables.create";
@@ -469,6 +500,8 @@ export declare const PERMISSIONS_BY_DOMAIN: {
469
500
  readonly REJECTION_REASONS_DELETE: "rejection_reasons.delete";
470
501
  readonly DELIVERABLES_PLANNING_PLAN: "deliverables.planning.plan";
471
502
  readonly DELIVERABLES_PLANNING_REPLAN: "deliverables.planning.replan";
503
+ readonly DELIVERABLE_REJECTION_CATALOG_MANAGE: "deliverable_rejection_catalog.manage";
504
+ readonly DELIVERABLES_RESOLVE_DISPUTE: "deliverables.resolve_dispute";
472
505
  };
473
506
  readonly Facturacion: {
474
507
  readonly INVOICES_REGISTER: "invoices.register";
@@ -609,9 +642,12 @@ export declare const PERMISSIONS_BY_DOMAIN: {
609
642
  readonly PROVIDERS_RISKS_DELETE: "providers.risks.delete";
610
643
  readonly PROVIDERS_SURVEYS_DELETE: "providers.surveys.delete";
611
644
  };
645
+ readonly Workflows: {
646
+ readonly WORKFLOWS_ADMIN: "workflows.admin";
647
+ };
612
648
  };
613
649
  /** Lista plana de los permission codes (para validación de sincronización con Auth). */
614
650
  export declare const ALL_PERMISSION_CODES: readonly string[];
615
651
  /** Tipo unión de todos los codes de permiso válidos. */
616
- export type PermissionCode = (typeof AUTH_PERMISSIONS)[keyof typeof AUTH_PERMISSIONS] | (typeof CONTRATOS_PERMISSIONS)[keyof typeof CONTRATOS_PERMISSIONS] | (typeof ENTREGABLES_PERMISSIONS)[keyof typeof ENTREGABLES_PERMISSIONS] | (typeof FACTURACION_PERMISSIONS)[keyof typeof FACTURACION_PERMISSIONS] | (typeof SLAS_PERMISSIONS)[keyof typeof SLAS_PERMISSIONS] | (typeof REUNIONES_PERMISSIONS)[keyof typeof REUNIONES_PERMISSIONS] | (typeof USER_CONTRACT_ROLE_PERMISSIONS)[keyof typeof USER_CONTRACT_ROLE_PERMISSIONS] | (typeof PROVEEDORES_PERMISSIONS)[keyof typeof PROVEEDORES_PERMISSIONS];
652
+ export type PermissionCode = (typeof AUTH_PERMISSIONS)[keyof typeof AUTH_PERMISSIONS] | (typeof CONTRATOS_PERMISSIONS)[keyof typeof CONTRATOS_PERMISSIONS] | (typeof ENTREGABLES_PERMISSIONS)[keyof typeof ENTREGABLES_PERMISSIONS] | (typeof FACTURACION_PERMISSIONS)[keyof typeof FACTURACION_PERMISSIONS] | (typeof SLAS_PERMISSIONS)[keyof typeof SLAS_PERMISSIONS] | (typeof REUNIONES_PERMISSIONS)[keyof typeof REUNIONES_PERMISSIONS] | (typeof USER_CONTRACT_ROLE_PERMISSIONS)[keyof typeof USER_CONTRACT_ROLE_PERMISSIONS] | (typeof PROVEEDORES_PERMISSIONS)[keyof typeof PROVEEDORES_PERMISSIONS] | (typeof WORKFLOWS_PERMISSIONS)[keyof typeof WORKFLOWS_PERMISSIONS];
617
653
  //# sourceMappingURL=permission-names.constants.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"permission-names.constants.d.ts","sourceRoot":"","sources":["../../src/constants/permission-names.constants.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"permission-names.constants.d.ts","sourceRoot":"","sources":["../../src/constants/permission-names.constants.ts"],"names":[],"mappings":"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"}
@@ -2,17 +2,21 @@
2
2
  /**
3
3
  * permission-names.constants.ts
4
4
  * ------------------------------------------------------------------------
5
- * Constantes de NOMBRES (codes) de los 237 permisos de dominio de ContractX.
5
+ * Constantes de NOMBRES (codes) de los 264 permisos de dominio de ContractX.
6
6
  * Derivadas del catálogo de Auth (domain-permissions.catalog.ts, ADR-004 Fase 1/2),
7
7
  * tomando SOLO el campo `code` — SIN la metadata de seeding (rolesGranted,
8
8
  * rolesReadOnly, approvalCategory, side, requiresHuman), que vive solo en Auth.
9
+ * Los comentarios `// rolesGranted: ...` sobre cada constante nueva desde
10
+ * v2.4.0 son referencia informal para la siembra en Auth, no metadata real
11
+ * de este paquete.
9
12
  *
10
13
  * Fuente única de NOMBRES: este archivo (en el paquete permissions-contractx).
11
14
  * La metadata se cuelga encima, en Auth. Un test en Auth valida que todo `code`
12
15
  * del catálogo existe como constante aquí (sincronización — Fase 6/8 del porte).
13
16
  *
14
- * Agrupación por microservicio: Auth(13) Contratos(60) Entregables(48)
15
- * Facturación(27) SLAs(45) Proveedores(46) = 239.
17
+ * Agrupación por microservicio: Auth(13) Contratos(73) Entregables(52)
18
+ * Facturación(29) SLAs(45) Reuniones(2) UserContractRole(3) Proveedores(46)
19
+ * Workflows(1) = 264.
16
20
  *
17
21
  * Convención de claves: reflejan el recurso completo del code, en
18
22
  * SCREAMING_SNAKE_CASE (recurso + acción). Esto evita colisiones entre dominios
@@ -23,7 +27,7 @@
23
27
  */
24
28
  Object.defineProperty(exports, "__esModule", { value: true });
25
29
  exports.PROVIDERS_EVALS_DELETE = exports.PROVIDERS_DEPS_DELETE = exports.PROVIDERS_CONTACTS_DELETE = exports.PROVIDERS_PROFILE_DELETE = exports.PROVIDERS_REPORTS_VIEW = exports.PROVIDERS_REPORTS_INCIDENTS = exports.PROVIDERS_REPORTS_SURVEYS = exports.PROVIDERS_REPORTS_RISKS = exports.PROVIDERS_REPORTS_EVALUATIONS = exports.PROVIDERS_REPORTS_GENERATE = exports.PROVIDERS_INCIDENTS_VIEW = exports.PROVIDERS_INCIDENTS_REOPEN = exports.PROVIDERS_INCIDENTS_CLOSE = exports.PROVIDERS_INCIDENTS_MANAGE = exports.PROVIDERS_INCIDENTS_ASSIGN = exports.PROVIDERS_INCIDENTS_REGISTER = exports.PROVIDERS_SURVEYS_VIEW = exports.PROVIDERS_SURVEYS_RESPOND = exports.PROVIDERS_SURVEYS_MANAGE_LIFECYCLE = exports.PROVIDERS_SURVEYS_EDIT_QUESTIONS = exports.PROVIDERS_SURVEYS_CREATE = exports.PROVIDERS_RISKS_VIEW_PLANS = exports.PROVIDERS_RISKS_EXECUTE_ACTION = exports.PROVIDERS_RISKS_CREATE_PLAN = exports.PROVIDERS_RISKS_VIEW_MATRIX = exports.PROVIDERS_RISKS_EDIT = exports.PROVIDERS_RISKS_REGISTER = exports.PROVIDERS_EVALS_VIEW = exports.PROVIDERS_EVALS_CANCEL = exports.PROVIDERS_EVALS_EXECUTE = exports.PROVIDERS_EVALS_SCHEDULE = exports.PROVIDERS_DEPS_VIEW = exports.PROVIDERS_DEPS_MANAGE = exports.PROVIDERS_CONTACTS_SET_PRIMARY = exports.PROVIDERS_CONTACTS_VIEW = exports.PROVIDERS_CONTACTS_MANAGE = exports.PROVIDERS_PROFILE_CHANGE_RISK = exports.PROVIDERS_PROFILE_VIEW_BANK_INFO = exports.PROVIDERS_PROFILE_VIEW = exports.PROVIDERS_PROFILE_BLACKLIST = exports.PROVIDERS_PROFILE_CHANGE_STATUS = exports.PROVIDERS_PROFILE_EDIT = exports.PROVIDERS_PROFILE_CREATE = exports.USER_CONTRACT_ROLE_PERMISSIONS = exports.REUNIONES_PERMISSIONS = exports.SLAS_PERMISSIONS = exports.FACTURACION_PERMISSIONS = exports.ENTREGABLES_PERMISSIONS = exports.CONTRATOS_PERMISSIONS = exports.AUTH_PERMISSIONS = void 0;
26
- exports.ALL_PERMISSION_CODES = exports.PERMISSIONS_BY_DOMAIN = exports.PROVEEDORES_PERMISSIONS = exports.PROVIDERS_SURVEYS_DELETE = exports.PROVIDERS_RISKS_DELETE = exports.PROVIDERS_INCIDENTS_DELETE = void 0;
30
+ exports.ALL_PERMISSION_CODES = exports.PERMISSIONS_BY_DOMAIN = exports.WORKFLOWS_PERMISSIONS = exports.PROVEEDORES_PERMISSIONS = exports.PROVIDERS_SURVEYS_DELETE = exports.PROVIDERS_RISKS_DELETE = exports.PROVIDERS_INCIDENTS_DELETE = void 0;
27
31
  // ======================= AUTH (13) =======================
28
32
  exports.AUTH_PERMISSIONS = {
29
33
  ADMIN_USERS_CREATE_ANY_TENANT: 'admin.users.create_any_tenant',
@@ -40,7 +44,7 @@ exports.AUTH_PERMISSIONS = {
40
44
  ADMIN_SESSIONS_VIEW_ME: 'admin.sessions.view_me',
41
45
  ADMIN_DELEGATION_MANAGE_DELEGATE: 'admin.delegation.manage_delegate',
42
46
  };
43
- // ======================= CONTRATOS (48) =======================
47
+ // ======================= CONTRATOS (73) =======================
44
48
  exports.CONTRATOS_PERMISSIONS = {
45
49
  // contracts.*
46
50
  CONTRACTS_CREATE: 'contracts.create',
@@ -112,8 +116,35 @@ exports.CONTRATOS_PERMISSIONS = {
112
116
  WORK_ORDERS_DELETE: 'work_orders.delete',
113
117
  RAID_DECISIONS_DELETE: 'raid_decisions.delete',
114
118
  RAID_ISSUES_DELETE: 'raid_issues.delete',
119
+ // contract_types.* (PR 0.b MVPV0 — scaffold contract_type + playbook, v2.4.0)
120
+ // rolesGranted: super_admin, client_superadmin, client_contract_admin, client_finance_resp
121
+ CONTRACT_TYPES_VIEW: 'contract_types.view',
122
+ // rolesGranted: super_admin, client_superadmin, client_contract_admin, client_finance_resp
123
+ CONTRACT_TYPES_VIEW_DETAIL: 'contract_types.view_detail',
124
+ // contract_clauses.* — notas internas (PR 0.c MVPV0, v2.4.0)
125
+ // rolesGranted: super_admin, client_superadmin
126
+ CONTRACT_CLAUSES_VIEW_WALK_AWAY_NOTES: 'contract_clauses.view_walk_away_notes',
127
+ // rolesGranted: super_admin, client_superadmin, client_contract_admin
128
+ CONTRACT_CLAUSES_VIEW_NEGOTIATION_NOTES: 'contract_clauses.view_negotiation_notes',
129
+ // contract_clause_deviations.* (PR 0.c MVPV0, v2.4.0)
130
+ // rolesGranted: super_admin, client_superadmin, client_contract_admin, client_finance_resp
131
+ CONTRACT_CLAUSE_DEVIATIONS_VIEW: 'contract_clause_deviations.view',
132
+ // rolesGranted: super_admin, client_superadmin, client_contract_admin
133
+ CONTRACT_CLAUSE_DEVIATIONS_CREATE: 'contract_clause_deviations.create',
134
+ // rolesGranted: super_admin, client_superadmin
135
+ CONTRACT_CLAUSE_DEVIATIONS_APPROVE_WALK_AWAY_BREACH: 'contract_clause_deviations.approve_walk_away_breach',
136
+ // contract_obligations.* (PR 0.a MVPV0, v2.4.0)
137
+ // rolesGranted: super_admin, client_superadmin, client_contract_admin, client_finance_resp, client_performance_resp
138
+ CONTRACT_OBLIGATIONS_VIEW_LIST: 'contract_obligations.view_list',
139
+ // rolesGranted: super_admin, client_superadmin, client_contract_admin, client_finance_resp, client_performance_resp
140
+ CONTRACT_OBLIGATIONS_VIEW_DETAIL: 'contract_obligations.view_detail',
141
+ // rolesGranted: super_admin, client_superadmin, client_contract_admin
142
+ CONTRACT_OBLIGATIONS_WAIVE: 'contract_obligations.waive',
143
+ // contracts.view_risk_details (PR 0.d MVPV0, v2.4.0)
144
+ // rolesGranted: super_admin, client_superadmin, client_contract_admin, client_finance_resp
145
+ CONTRACTS_VIEW_RISK_DETAILS: 'contracts.view_risk_details',
115
146
  };
116
- // ======================= ENTREGABLES (47) =======================
147
+ // ======================= ENTREGABLES (52) =======================
117
148
  exports.ENTREGABLES_PERMISSIONS = {
118
149
  // deliverables.*
119
150
  DELIVERABLES_CREATE: 'deliverables.create',
@@ -178,6 +209,11 @@ exports.ENTREGABLES_PERMISSIONS = {
178
209
  // deliverables.planning.*
179
210
  DELIVERABLES_PLANNING_PLAN: 'deliverables.planning.plan',
180
211
  DELIVERABLES_PLANNING_REPLAN: 'deliverables.planning.replan',
212
+ // Sesión Entregables 2026-07-01, Matriz V7.3 publicada 2026-07-02 (v2.4.0)
213
+ // rolesGranted: super_admin, client_superadmin, client_contract_admin, client_performance_resp
214
+ DELIVERABLE_REJECTION_CATALOG_MANAGE: 'deliverable_rejection_catalog.manage',
215
+ // rolesGranted: super_admin, client_superadmin, client_contract_admin (RR-C excluido por conflicto de interés)
216
+ DELIVERABLES_RESOLVE_DISPUTE: 'deliverables.resolve_dispute',
181
217
  };
182
218
  // ======================= FACTURACIÓN (29) =======================
183
219
  exports.FACTURACION_PERMISSIONS = {
@@ -399,6 +435,15 @@ exports.PROVEEDORES_PERMISSIONS = {
399
435
  PROVIDERS_RISKS_DELETE: exports.PROVIDERS_RISKS_DELETE,
400
436
  PROVIDERS_SURVEYS_DELETE: exports.PROVIDERS_SURVEYS_DELETE,
401
437
  };
438
+ // ======================= WORKFLOWS (1) =======================
439
+ // Módulo: Flujos (contractx-workflows, puerto 3005).
440
+ // Fase 1: workflows.admin (semilla del dominio, v2.4.0).
441
+ // Permisos adicionales llegarán en v2.5.0+ tras cerrar la Fase 10 de la
442
+ // auditoría cruzada de Notificaciones y Permisos (ver BITACORA).
443
+ exports.WORKFLOWS_PERMISSIONS = {
444
+ // rolesGranted: super_admin (exclusivo, por diseño)
445
+ WORKFLOWS_ADMIN: 'workflows.admin',
446
+ };
402
447
  // ======================= AGREGADOS Y TIPOS =======================
403
448
  /** Todas las constantes agrupadas por dominio. */
404
449
  exports.PERMISSIONS_BY_DOMAIN = {
@@ -410,6 +455,7 @@ exports.PERMISSIONS_BY_DOMAIN = {
410
455
  Reuniones: exports.REUNIONES_PERMISSIONS,
411
456
  UserContractRole: exports.USER_CONTRACT_ROLE_PERMISSIONS,
412
457
  Proveedores: exports.PROVEEDORES_PERMISSIONS,
458
+ Workflows: exports.WORKFLOWS_PERMISSIONS,
413
459
  };
414
460
  /** Lista plana de los permission codes (para validación de sincronización con Auth). */
415
461
  exports.ALL_PERMISSION_CODES = [
@@ -421,4 +467,5 @@ exports.ALL_PERMISSION_CODES = [
421
467
  ...Object.values(exports.REUNIONES_PERMISSIONS),
422
468
  ...Object.values(exports.USER_CONTRACT_ROLE_PERMISSIONS),
423
469
  ...Object.values(exports.PROVEEDORES_PERMISSIONS),
470
+ ...Object.values(exports.WORKFLOWS_PERMISSIONS),
424
471
  ];
package/dist/index.d.ts CHANGED
@@ -4,5 +4,6 @@ export * from './decorators';
4
4
  export * from './services';
5
5
  export * from './interfaces';
6
6
  export * from './constants';
7
+ export * from './inter-service-auth';
7
8
  export type { JwtPayload, AuthenticatedRequest, PermissionsModuleOptions, JwtAuthConfig } from './interfaces';
8
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,YAAY,EAAE,UAAU,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,sBAAsB,CAAC;AACrC,YAAY,EAAE,UAAU,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC"}
package/dist/index.js CHANGED
@@ -20,3 +20,4 @@ __exportStar(require("./decorators"), exports);
20
20
  __exportStar(require("./services"), exports);
21
21
  __exportStar(require("./interfaces"), exports);
22
22
  __exportStar(require("./constants"), exports);
23
+ __exportStar(require("./inter-service-auth"), exports);
@@ -0,0 +1,8 @@
1
+ export * from './inter-service-auth.types';
2
+ export * from './inter-service-auth.config.interface';
3
+ export { InterServiceAuthService, INTER_SERVICE_AUTH_CONFIG } from './inter-service-auth.service';
4
+ export * from './inter-service-auth.module';
5
+ export * from './inter-service-auth.middleware';
6
+ export { ServiceAuthGuard, SERVICE_PERMISSIONS_KEY, SERVICE_ROLES_KEY, ALLOW_SERVICE_AUTH_KEY, REQUIRE_SERVICE_AUTH_KEY, } from './service-auth.guard';
7
+ export * from './service-auth.decorators';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/inter-service-auth/index.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAC;AAC3C,cAAc,uCAAuC,CAAC;AACtD,OAAO,EAAE,uBAAuB,EAAE,yBAAyB,EAAE,MAAM,8BAA8B,CAAC;AAClG,cAAc,6BAA6B,CAAC;AAC5C,cAAc,iCAAiC,CAAC;AAChD,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,iBAAiB,EACjB,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,sBAAsB,CAAC;AAC9B,cAAc,2BAA2B,CAAC"}
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.REQUIRE_SERVICE_AUTH_KEY = exports.ALLOW_SERVICE_AUTH_KEY = exports.SERVICE_ROLES_KEY = exports.SERVICE_PERMISSIONS_KEY = exports.ServiceAuthGuard = exports.INTER_SERVICE_AUTH_CONFIG = exports.InterServiceAuthService = void 0;
18
+ __exportStar(require("./inter-service-auth.types"), exports);
19
+ __exportStar(require("./inter-service-auth.config.interface"), exports);
20
+ var inter_service_auth_service_1 = require("./inter-service-auth.service");
21
+ Object.defineProperty(exports, "InterServiceAuthService", { enumerable: true, get: function () { return inter_service_auth_service_1.InterServiceAuthService; } });
22
+ Object.defineProperty(exports, "INTER_SERVICE_AUTH_CONFIG", { enumerable: true, get: function () { return inter_service_auth_service_1.INTER_SERVICE_AUTH_CONFIG; } });
23
+ __exportStar(require("./inter-service-auth.module"), exports);
24
+ __exportStar(require("./inter-service-auth.middleware"), exports);
25
+ var service_auth_guard_1 = require("./service-auth.guard");
26
+ Object.defineProperty(exports, "ServiceAuthGuard", { enumerable: true, get: function () { return service_auth_guard_1.ServiceAuthGuard; } });
27
+ Object.defineProperty(exports, "SERVICE_PERMISSIONS_KEY", { enumerable: true, get: function () { return service_auth_guard_1.SERVICE_PERMISSIONS_KEY; } });
28
+ Object.defineProperty(exports, "SERVICE_ROLES_KEY", { enumerable: true, get: function () { return service_auth_guard_1.SERVICE_ROLES_KEY; } });
29
+ Object.defineProperty(exports, "ALLOW_SERVICE_AUTH_KEY", { enumerable: true, get: function () { return service_auth_guard_1.ALLOW_SERVICE_AUTH_KEY; } });
30
+ Object.defineProperty(exports, "REQUIRE_SERVICE_AUTH_KEY", { enumerable: true, get: function () { return service_auth_guard_1.REQUIRE_SERVICE_AUTH_KEY; } });
31
+ __exportStar(require("./service-auth.decorators"), exports);
@@ -0,0 +1,10 @@
1
+ import { IServiceCredentials } from './inter-service-auth.types';
2
+ export interface IServiceAuthConfig {
3
+ services: Record<string, IServiceCredentials>;
4
+ /** Minutes before a signed request expires (looking backward). Default: 5 */
5
+ signatureExpireMinutes?: number;
6
+ /** Seconds of future clock skew tolerated (looking forward). Default: 30 */
7
+ allowedTimeDriftSeconds?: number;
8
+ enableLogging?: boolean;
9
+ }
10
+ //# sourceMappingURL=inter-service-auth.config.interface.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inter-service-auth.config.interface.d.ts","sourceRoot":"","sources":["../../src/inter-service-auth/inter-service-auth.config.interface.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAEjE,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;IAC9C,6EAA6E;IAC7E,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,4EAA4E;IAC5E,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB"}
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,11 @@
1
+ import { NestMiddleware } from '@nestjs/common';
2
+ import { Request, Response, NextFunction } from 'express';
3
+ import { InterServiceAuthService } from './inter-service-auth.service';
4
+ export declare class InterServiceAuthMiddleware implements NestMiddleware {
5
+ private readonly interServiceAuthService;
6
+ private readonly logger;
7
+ constructor(interServiceAuthService: InterServiceAuthService);
8
+ use(req: Request, res: Response, next: NextFunction): void | Response<any, Record<string, any>>;
9
+ private normalizeHeaders;
10
+ }
11
+ //# sourceMappingURL=inter-service-auth.middleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inter-service-auth.middleware.d.ts","sourceRoot":"","sources":["../../src/inter-service-auth/inter-service-auth.middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,cAAc,EAAiC,MAAM,gBAAgB,CAAC;AAC3F,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE1D,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAEvE,qBACa,0BAA2B,YAAW,cAAc;IAGnD,OAAO,CAAC,QAAQ,CAAC,uBAAuB;IAFpD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA4C;gBAEtC,uBAAuB,EAAE,uBAAuB;IAE7E,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY;IA4CnD,OAAO,CAAC,gBAAgB;CAczB"}
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ 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;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.InterServiceAuthMiddleware = void 0;
13
+ const common_1 = require("@nestjs/common");
14
+ const inter_service_auth_service_1 = require("./inter-service-auth.service");
15
+ let InterServiceAuthMiddleware = class InterServiceAuthMiddleware {
16
+ constructor(interServiceAuthService) {
17
+ this.interServiceAuthService = interServiceAuthService;
18
+ this.logger = new common_1.Logger('InterServiceAuthMiddleware');
19
+ }
20
+ use(req, res, next) {
21
+ try {
22
+ const isServiceAuth = req.headers['x-service-auth'] === 'true';
23
+ if (!isServiceAuth) {
24
+ // Not a service-to-service request — pass through for normal auth guards
25
+ req.isServiceRequest = false;
26
+ return next();
27
+ }
28
+ const headers = this.normalizeHeaders(req.headers);
29
+ const servicePayload = this.interServiceAuthService.validateServiceAuth(headers);
30
+ req.serviceAuth = servicePayload;
31
+ req.isServiceRequest = true;
32
+ this.logger.debug(`Service-to-service call authenticated: ${servicePayload.serviceId} -> ${req.path}`);
33
+ next();
34
+ }
35
+ catch (error) {
36
+ const message = error instanceof Error ? error.message : String(error);
37
+ const stack = error instanceof Error ? error.stack : undefined;
38
+ this.logger.error(`Service authentication failed: ${message}`, stack);
39
+ if (error instanceof common_1.UnauthorizedException) {
40
+ return res.status(401).json({
41
+ statusCode: 401,
42
+ message: 'Service authentication failed',
43
+ error: 'Unauthorized',
44
+ timestamp: new Date().toISOString(),
45
+ path: req.url,
46
+ });
47
+ }
48
+ return res.status(500).json({
49
+ statusCode: 500,
50
+ message: 'Internal server error during service authentication',
51
+ error: 'Internal Server Error',
52
+ timestamp: new Date().toISOString(),
53
+ path: req.url,
54
+ });
55
+ }
56
+ }
57
+ normalizeHeaders(headers) {
58
+ const normalized = {};
59
+ Object.keys(headers).forEach((key) => {
60
+ const value = headers[key];
61
+ if (typeof value === 'string') {
62
+ normalized[key.toLowerCase()] = value;
63
+ }
64
+ else if (Array.isArray(value) && value.length > 0) {
65
+ normalized[key.toLowerCase()] = value[0];
66
+ }
67
+ });
68
+ return normalized;
69
+ }
70
+ };
71
+ exports.InterServiceAuthMiddleware = InterServiceAuthMiddleware;
72
+ exports.InterServiceAuthMiddleware = InterServiceAuthMiddleware = __decorate([
73
+ (0, common_1.Injectable)(),
74
+ __metadata("design:paramtypes", [inter_service_auth_service_1.InterServiceAuthService])
75
+ ], InterServiceAuthMiddleware);
@@ -0,0 +1,11 @@
1
+ import { DynamicModule } from '@nestjs/common';
2
+ import { IServiceAuthConfig } from './inter-service-auth.config.interface';
3
+ export declare class InterServiceAuthModule {
4
+ static forRoot(config: IServiceAuthConfig): DynamicModule;
5
+ static forRootAsync(options: {
6
+ imports?: any[];
7
+ useFactory: (...args: any[]) => IServiceAuthConfig | Promise<IServiceAuthConfig>;
8
+ inject?: any[];
9
+ }): DynamicModule;
10
+ }
11
+ //# sourceMappingURL=inter-service-auth.module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inter-service-auth.module.d.ts","sourceRoot":"","sources":["../../src/inter-service-auth/inter-service-auth.module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAkB,MAAM,gBAAgB,CAAC;AAE/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAE3E,qBAEa,sBAAsB;IACjC,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,kBAAkB,GAAG,aAAa;IAWzD,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE;QAC3B,OAAO,CAAC,EAAE,GAAG,EAAE,CAAC;QAChB,UAAU,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;QACjF,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC;KAChB,GAAG,aAAa;CAelB"}
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ 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;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var InterServiceAuthModule_1;
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.InterServiceAuthModule = void 0;
11
+ const common_1 = require("@nestjs/common");
12
+ const inter_service_auth_service_1 = require("./inter-service-auth.service");
13
+ let InterServiceAuthModule = InterServiceAuthModule_1 = class InterServiceAuthModule {
14
+ static forRoot(config) {
15
+ return {
16
+ module: InterServiceAuthModule_1,
17
+ providers: [
18
+ { provide: inter_service_auth_service_1.INTER_SERVICE_AUTH_CONFIG, useValue: config },
19
+ inter_service_auth_service_1.InterServiceAuthService,
20
+ ],
21
+ exports: [inter_service_auth_service_1.InterServiceAuthService],
22
+ };
23
+ }
24
+ static forRootAsync(options) {
25
+ return {
26
+ module: InterServiceAuthModule_1,
27
+ imports: options.imports || [],
28
+ providers: [
29
+ {
30
+ provide: inter_service_auth_service_1.INTER_SERVICE_AUTH_CONFIG,
31
+ useFactory: options.useFactory,
32
+ inject: options.inject || [],
33
+ },
34
+ inter_service_auth_service_1.InterServiceAuthService,
35
+ ],
36
+ exports: [inter_service_auth_service_1.InterServiceAuthService],
37
+ };
38
+ }
39
+ };
40
+ exports.InterServiceAuthModule = InterServiceAuthModule;
41
+ exports.InterServiceAuthModule = InterServiceAuthModule = InterServiceAuthModule_1 = __decorate([
42
+ (0, common_1.Global)(),
43
+ (0, common_1.Module)({})
44
+ ], InterServiceAuthModule);
@@ -0,0 +1,23 @@
1
+ import { OnModuleDestroy } from '@nestjs/common';
2
+ import { IServiceAuthConfig } from './inter-service-auth.config.interface';
3
+ import { IServiceAuthPayload, IServiceCredentials } from './inter-service-auth.types';
4
+ export declare const INTER_SERVICE_AUTH_CONFIG = "INTER_SERVICE_AUTH_CONFIG";
5
+ export declare class InterServiceAuthService implements OnModuleDestroy {
6
+ private readonly config;
7
+ private readonly logger;
8
+ private readonly seenNonces;
9
+ private readonly intervalId;
10
+ constructor(config: IServiceAuthConfig);
11
+ onModuleDestroy(): void;
12
+ generateAuthHeaders(serviceId: string, targetSchema?: string): Record<string, string>;
13
+ validateServiceAuth(headers: Record<string, string>): IServiceAuthPayload;
14
+ getServiceConfig(serviceId: string): IServiceCredentials | undefined;
15
+ getConfiguredServices(): string[];
16
+ hasPermission(serviceId: string, permission: string): boolean;
17
+ hasRole(serviceId: string, role: string): boolean;
18
+ private purgeExpiredNonces;
19
+ private generateSignature;
20
+ private verifySignature;
21
+ private generateNonce;
22
+ }
23
+ //# sourceMappingURL=inter-service-auth.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inter-service-auth.service.d.ts","sourceRoot":"","sources":["../../src/inter-service-auth/inter-service-auth.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqD,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEpG,OAAO,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAC3E,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAEtF,eAAO,MAAM,yBAAyB,8BAA8B,CAAC;AAErE,qBACa,uBAAwB,YAAW,eAAe;IASd,OAAO,CAAC,QAAQ,CAAC,MAAM;IARtE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA4C;IAKnE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAkC;IAC7D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAiC;gBAEI,MAAM,EAAE,kBAAkB;IAK1F,eAAe,IAAI,IAAI;IAIvB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,GAAE,MAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IA6BzF,mBAAmB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,mBAAmB;IA2EzE,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS;IAIpE,qBAAqB,IAAI,MAAM,EAAE;IAIjC,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO;IAU7D,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO;IAUjD,OAAO,CAAC,kBAAkB;IAS1B,OAAO,CAAC,iBAAiB;IAKzB,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,aAAa;CAGtB"}
@@ -0,0 +1,202 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
19
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
20
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
21
+ 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;
22
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
23
+ };
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ var __metadata = (this && this.__metadata) || function (k, v) {
42
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
43
+ };
44
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
45
+ return function (target, key) { decorator(target, key, paramIndex); }
46
+ };
47
+ var InterServiceAuthService_1;
48
+ Object.defineProperty(exports, "__esModule", { value: true });
49
+ exports.InterServiceAuthService = exports.INTER_SERVICE_AUTH_CONFIG = void 0;
50
+ const common_1 = require("@nestjs/common");
51
+ const crypto = __importStar(require("crypto"));
52
+ exports.INTER_SERVICE_AUTH_CONFIG = 'INTER_SERVICE_AUTH_CONFIG';
53
+ let InterServiceAuthService = InterServiceAuthService_1 = class InterServiceAuthService {
54
+ constructor(config) {
55
+ this.config = config;
56
+ this.logger = new common_1.Logger(InterServiceAuthService_1.name);
57
+ // Replay protection: tracks nonces seen within their validity window.
58
+ // Key: `${serviceId}:${nonce}`, value: expiresAt timestamp (ms).
59
+ // DEBT: in-memory only — assumes single-node deployment. For horizontal scaling, migrate to Redis.
60
+ this.seenNonces = new Map();
61
+ // Purge expired nonces every 60 s to prevent unbounded memory growth
62
+ this.intervalId = setInterval(() => this.purgeExpiredNonces(), 60_000);
63
+ }
64
+ onModuleDestroy() {
65
+ clearInterval(this.intervalId);
66
+ }
67
+ generateAuthHeaders(serviceId, targetSchema = '') {
68
+ const service = this.config.services[serviceId];
69
+ if (!service) {
70
+ throw new common_1.UnauthorizedException(`Service ${serviceId} not configured`);
71
+ }
72
+ const timestamp = Date.now();
73
+ const nonce = this.generateNonce();
74
+ const payload = {
75
+ serviceId: service.serviceId,
76
+ permissions: service.permissions ?? ['inter-service.communicate'],
77
+ roles: service.roles ?? ['service'],
78
+ timestamp,
79
+ nonce,
80
+ };
81
+ const signature = this.generateSignature(payload, service.secretKey, targetSchema);
82
+ return {
83
+ 'X-Service-ID': service.serviceId,
84
+ 'X-Service-API-Key': service.apiKey,
85
+ 'X-Service-Timestamp': timestamp.toString(),
86
+ 'X-Service-Nonce': nonce,
87
+ 'X-Service-Signature': signature,
88
+ 'X-Service-Auth': 'true',
89
+ };
90
+ }
91
+ validateServiceAuth(headers) {
92
+ const serviceId = headers['x-service-id'];
93
+ const apiKey = headers['x-service-api-key'];
94
+ const timestamp = parseInt(headers['x-service-timestamp'] || '0');
95
+ const nonce = headers['x-service-nonce'];
96
+ const signature = headers['x-service-signature'];
97
+ const isServiceAuth = headers['x-service-auth'] === 'true';
98
+ if (!isServiceAuth) {
99
+ throw new common_1.UnauthorizedException('Not a service-to-service request');
100
+ }
101
+ if (!serviceId || !apiKey || !timestamp || !nonce || !signature) {
102
+ throw new common_1.UnauthorizedException('Missing service authentication headers');
103
+ }
104
+ const service = Object.values(this.config.services).find((s) => s.serviceId === serviceId && s.apiKey === apiKey);
105
+ if (!service) {
106
+ this.logger.warn(`Unknown service attempted authentication: ${serviceId}`);
107
+ throw new common_1.UnauthorizedException('Invalid service credentials');
108
+ }
109
+ // signatureExpireMinutes: window looking backward — how long a signature remains valid.
110
+ // allowedTimeDriftSeconds: window looking forward — tolerance for sender clock skew.
111
+ // These are distinct concepts. Using Math.abs() with OR collapses both into min(expiry, drift),
112
+ // making the effective window only 30 s in both directions instead of 5 min back / 30 s forward.
113
+ const now = Date.now();
114
+ const maxAge = (this.config.signatureExpireMinutes ?? 5) * 60 * 1000;
115
+ const timeDrift = (this.config.allowedTimeDriftSeconds ?? 30) * 1000;
116
+ const ageMs = now - timestamp; // positive = timestamp is in the past; negative = timestamp is in the future
117
+ if (ageMs > maxAge) {
118
+ this.logger.warn(`Service auth signature expired: ${serviceId}`);
119
+ throw new common_1.UnauthorizedException('Service authentication signature expired');
120
+ }
121
+ if (ageMs < -timeDrift) {
122
+ this.logger.warn(`Service auth timestamp too far in the future: ${serviceId}`);
123
+ throw new common_1.UnauthorizedException('Service authentication timestamp invalid');
124
+ }
125
+ const payload = {
126
+ serviceId,
127
+ permissions: service.permissions ?? ['inter-service.communicate'],
128
+ roles: service.roles ?? ['service'],
129
+ timestamp,
130
+ nonce,
131
+ };
132
+ const targetSchema = headers['x-target-schema'] || '';
133
+ const expectedSignature = this.generateSignature(payload, service.secretKey, targetSchema);
134
+ if (!this.verifySignature(signature, expectedSignature)) {
135
+ this.logger.warn(`Invalid signature for service: ${serviceId}`);
136
+ throw new common_1.UnauthorizedException('Invalid service signature');
137
+ }
138
+ // Replay protection: reject any nonce already seen within its validity window.
139
+ // DEBT: in-memory only — assumes single-node deployment. For horizontal scaling, migrate to Redis.
140
+ const nonceKey = `${serviceId}:${nonce}`;
141
+ if (this.seenNonces.has(nonceKey)) {
142
+ this.logger.warn(`Replayed nonce detected for service: ${serviceId}`);
143
+ throw new common_1.UnauthorizedException('Nonce already used');
144
+ }
145
+ this.seenNonces.set(nonceKey, timestamp + maxAge);
146
+ if (this.config.enableLogging) {
147
+ this.logger.log(`Service authenticated successfully: ${serviceId}`);
148
+ }
149
+ return payload;
150
+ }
151
+ getServiceConfig(serviceId) {
152
+ return this.config.services[serviceId];
153
+ }
154
+ getConfiguredServices() {
155
+ return Object.keys(this.config.services);
156
+ }
157
+ hasPermission(serviceId, permission) {
158
+ const service = this.config.services[serviceId];
159
+ if (!service)
160
+ return false;
161
+ return ((service.permissions ?? []).includes(permission) ||
162
+ (service.permissions ?? []).includes('*') ||
163
+ (service.permissions ?? []).includes('inter-service.*'));
164
+ }
165
+ hasRole(serviceId, role) {
166
+ const service = this.config.services[serviceId];
167
+ if (!service)
168
+ return false;
169
+ const roles = service.roles ?? ['service'];
170
+ // Every configured service implicitly carries the base 'service' role,
171
+ // regardless of explicit configuration. This enables coarse-grained checks
172
+ // like @RequireServiceRoles('service') to pass for any valid credential.
173
+ return roles.includes(role) || role === 'service';
174
+ }
175
+ purgeExpiredNonces() {
176
+ const now = Date.now();
177
+ for (const [key, expiresAt] of this.seenNonces) {
178
+ if (expiresAt < now) {
179
+ this.seenNonces.delete(key);
180
+ }
181
+ }
182
+ }
183
+ generateSignature(payload, secretKey, targetSchema = '') {
184
+ const data = `${payload.serviceId}:${payload.timestamp}:${payload.nonce}:${targetSchema}`;
185
+ return crypto.createHmac('sha256', secretKey).update(data).digest('hex');
186
+ }
187
+ verifySignature(provided, expected) {
188
+ if (provided.length !== expected.length) {
189
+ return false;
190
+ }
191
+ return crypto.timingSafeEqual(Buffer.from(provided, 'hex'), Buffer.from(expected, 'hex'));
192
+ }
193
+ generateNonce() {
194
+ return crypto.randomBytes(16).toString('hex');
195
+ }
196
+ };
197
+ exports.InterServiceAuthService = InterServiceAuthService;
198
+ exports.InterServiceAuthService = InterServiceAuthService = InterServiceAuthService_1 = __decorate([
199
+ (0, common_1.Injectable)(),
200
+ __param(0, (0, common_1.Inject)(exports.INTER_SERVICE_AUTH_CONFIG)),
201
+ __metadata("design:paramtypes", [Object])
202
+ ], InterServiceAuthService);
@@ -0,0 +1,57 @@
1
+ import { Request } from 'express';
2
+ export interface IServiceCredentials {
3
+ serviceId: string;
4
+ apiKey: string;
5
+ secretKey: string;
6
+ /** Default: ['inter-service.communicate'] */
7
+ permissions?: string[];
8
+ /** Default: ['service'] */
9
+ roles?: string[];
10
+ }
11
+ export interface IServiceAuthPayload {
12
+ serviceId: string;
13
+ permissions: string[];
14
+ roles: string[];
15
+ timestamp: number;
16
+ nonce: string;
17
+ }
18
+ export interface IServiceAuthRequest {
19
+ serviceId: string;
20
+ timestamp: number;
21
+ nonce: string;
22
+ signature: string;
23
+ }
24
+ export declare const SERVICE_NAMES: {
25
+ readonly AUTH: "contractx-authorizations";
26
+ readonly CONTRACTS: "contractx-contracts";
27
+ readonly PROVIDERS: "contractx-providers";
28
+ readonly DELIVERIES: "contractx-deliveries";
29
+ readonly INVOICES: "contractx-invoices";
30
+ readonly WORKFLOWS: "contractx-workflows";
31
+ readonly SLAS: "contractx-SLAs";
32
+ readonly MEETINGS: "contractx-meetings";
33
+ readonly NOTIFICATIONS: "contractx-notifications";
34
+ readonly AUDIT: "contractx-audit";
35
+ };
36
+ export type ServiceName = (typeof SERVICE_NAMES)[keyof typeof SERVICE_NAMES];
37
+ declare module 'express-serve-static-core' {
38
+ interface Request {
39
+ serviceAuth?: IServiceAuthPayload;
40
+ isServiceRequest?: boolean;
41
+ }
42
+ }
43
+ /**
44
+ * Narrowed Request type for handlers that sit behind ServiceAuthGuard.
45
+ * Cast to this type when you need non-optional access to serviceAuth:
46
+ *
47
+ * @Get()
48
+ * @UseGuards(ServiceAuthGuard)
49
+ * myEndpoint(@Req() req: AuthenticatedServiceRequest) {
50
+ * const { serviceId } = req.serviceAuth; // guaranteed non-null
51
+ * }
52
+ */
53
+ export type AuthenticatedServiceRequest = Request & {
54
+ serviceAuth: IServiceAuthPayload;
55
+ isServiceRequest: true;
56
+ };
57
+ //# sourceMappingURL=inter-service-auth.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inter-service-auth.types.d.ts","sourceRoot":"","sources":["../../src/inter-service-auth/inter-service-auth.types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,6CAA6C;IAC7C,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,2BAA2B;IAC3B,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,eAAO,MAAM,aAAa;;;;;;;;;;;CAWhB,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,OAAO,aAAa,CAAC,CAAC;AAI7E,OAAO,QAAQ,2BAA2B,CAAC;IACzC,UAAU,OAAO;QACf,WAAW,CAAC,EAAE,mBAAmB,CAAC;QAClC,gBAAgB,CAAC,EAAE,OAAO,CAAC;KAC5B;CACF;AAED;;;;;;;;;GASG;AACH,MAAM,MAAM,2BAA2B,GAAG,OAAO,GAAG;IAClD,WAAW,EAAE,mBAAmB,CAAC;IACjC,gBAAgB,EAAE,IAAI,CAAC;CACxB,CAAC"}
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SERVICE_NAMES = void 0;
4
+ exports.SERVICE_NAMES = {
5
+ AUTH: 'contractx-authorizations',
6
+ CONTRACTS: 'contractx-contracts',
7
+ PROVIDERS: 'contractx-providers',
8
+ DELIVERIES: 'contractx-deliveries',
9
+ INVOICES: 'contractx-invoices',
10
+ WORKFLOWS: 'contractx-workflows',
11
+ SLAS: 'contractx-SLAs',
12
+ MEETINGS: 'contractx-meetings',
13
+ NOTIFICATIONS: 'contractx-notifications',
14
+ AUDIT: 'contractx-audit',
15
+ };
@@ -0,0 +1,12 @@
1
+ /** Requires the caller to have all listed permissions in their service credentials. */
2
+ export declare const RequireServicePermissions: (...permissions: string[]) => import("@nestjs/common").CustomDecorator<string>;
3
+ /** Requires the caller to have at least one of the listed roles. */
4
+ export declare const RequireServiceRoles: (...roles: string[]) => import("@nestjs/common").CustomDecorator<string>;
5
+ /**
6
+ * Controls whether M2M requests are accepted on this endpoint.
7
+ * @AllowServiceAuth(false) blocks M2M even when @RequireServiceAuth() is set at class level.
8
+ */
9
+ export declare const AllowServiceAuth: (allow?: boolean) => import("@nestjs/common").CustomDecorator<string>;
10
+ /** Marks this endpoint as M2M-only. Rejects any request without valid service credentials. */
11
+ export declare const RequireServiceAuth: () => import("@nestjs/common").CustomDecorator<string>;
12
+ //# sourceMappingURL=service-auth.decorators.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-auth.decorators.d.ts","sourceRoot":"","sources":["../../src/inter-service-auth/service-auth.decorators.ts"],"names":[],"mappings":"AAQA,uFAAuF;AACvF,eAAO,MAAM,yBAAyB,GAAI,GAAG,aAAa,MAAM,EAAE,qDACf,CAAC;AAEpD,oEAAoE;AACpE,eAAO,MAAM,mBAAmB,GAAI,GAAG,OAAO,MAAM,EAAE,qDAA0C,CAAC;AAEjG;;;GAGG;AACH,eAAO,MAAM,gBAAgB,GAAI,QAAO,OAAc,qDAA+C,CAAC;AAEtG,8FAA8F;AAC9F,eAAO,MAAM,kBAAkB,wDAAoD,CAAC"}
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RequireServiceAuth = exports.AllowServiceAuth = exports.RequireServiceRoles = exports.RequireServicePermissions = void 0;
4
+ const common_1 = require("@nestjs/common");
5
+ const service_auth_guard_1 = require("./service-auth.guard");
6
+ /** Requires the caller to have all listed permissions in their service credentials. */
7
+ const RequireServicePermissions = (...permissions) => (0, common_1.SetMetadata)(service_auth_guard_1.SERVICE_PERMISSIONS_KEY, permissions);
8
+ exports.RequireServicePermissions = RequireServicePermissions;
9
+ /** Requires the caller to have at least one of the listed roles. */
10
+ const RequireServiceRoles = (...roles) => (0, common_1.SetMetadata)(service_auth_guard_1.SERVICE_ROLES_KEY, roles);
11
+ exports.RequireServiceRoles = RequireServiceRoles;
12
+ /**
13
+ * Controls whether M2M requests are accepted on this endpoint.
14
+ * @AllowServiceAuth(false) blocks M2M even when @RequireServiceAuth() is set at class level.
15
+ */
16
+ const AllowServiceAuth = (allow = true) => (0, common_1.SetMetadata)(service_auth_guard_1.ALLOW_SERVICE_AUTH_KEY, allow);
17
+ exports.AllowServiceAuth = AllowServiceAuth;
18
+ /** Marks this endpoint as M2M-only. Rejects any request without valid service credentials. */
19
+ const RequireServiceAuth = () => (0, common_1.SetMetadata)(service_auth_guard_1.REQUIRE_SERVICE_AUTH_KEY, true);
20
+ exports.RequireServiceAuth = RequireServiceAuth;
21
+ // TODO: @CurrentService() param decorator postponed — implement cleanly with
22
+ // createParamDecorator if a handler needs the calling serviceId.
@@ -0,0 +1,17 @@
1
+ import { CanActivate, ExecutionContext } from '@nestjs/common';
2
+ import { Reflector } from '@nestjs/core';
3
+ import { InterServiceAuthService } from './inter-service-auth.service';
4
+ export declare const SERVICE_PERMISSIONS_KEY = "service_permissions";
5
+ export declare const SERVICE_ROLES_KEY = "service_roles";
6
+ export declare const ALLOW_SERVICE_AUTH_KEY = "allow_service_auth";
7
+ export declare const REQUIRE_SERVICE_AUTH_KEY = "require_service_auth";
8
+ export declare class ServiceAuthGuard implements CanActivate {
9
+ private readonly reflector;
10
+ private readonly interServiceAuthService;
11
+ private readonly logger;
12
+ constructor(reflector: Reflector, interServiceAuthService: InterServiceAuthService);
13
+ canActivate(context: ExecutionContext): boolean;
14
+ private checkPermission;
15
+ private checkRole;
16
+ }
17
+ //# sourceMappingURL=service-auth.guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-auth.guard.d.ts","sourceRoot":"","sources":["../../src/inter-service-auth/service-auth.guard.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,WAAW,EACX,gBAAgB,EAIjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAGvE,eAAO,MAAM,uBAAuB,wBAAwB,CAAC;AAC7D,eAAO,MAAM,iBAAiB,kBAAkB,CAAC;AACjD,eAAO,MAAM,sBAAsB,uBAAuB,CAAC;AAC3D,eAAO,MAAM,wBAAwB,yBAAyB,CAAC;AAK/D,qBACa,gBAAiB,YAAW,WAAW;IAIhD,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,uBAAuB;IAJ1C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkC;gBAGtC,SAAS,EAAE,SAAS,EACpB,uBAAuB,EAAE,uBAAuB;IAGnE,WAAW,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO;IA6E/C,OAAO,CAAC,eAAe;IAyBvB,OAAO,CAAC,SAAS;CAYlB"}
@@ -0,0 +1,121 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ 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;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.ServiceAuthGuard = exports.REQUIRE_SERVICE_AUTH_KEY = exports.ALLOW_SERVICE_AUTH_KEY = exports.SERVICE_ROLES_KEY = exports.SERVICE_PERMISSIONS_KEY = void 0;
13
+ const common_1 = require("@nestjs/common");
14
+ const core_1 = require("@nestjs/core");
15
+ const inter_service_auth_service_1 = require("./inter-service-auth.service");
16
+ // Exported so decorators and consumers can reference keys without magic strings
17
+ exports.SERVICE_PERMISSIONS_KEY = 'service_permissions';
18
+ exports.SERVICE_ROLES_KEY = 'service_roles';
19
+ exports.ALLOW_SERVICE_AUTH_KEY = 'allow_service_auth';
20
+ exports.REQUIRE_SERVICE_AUTH_KEY = 'require_service_auth';
21
+ // TODO: @CurrentService() param decorator postponed — implement cleanly with
22
+ // createParamDecorator if a handler needs the calling serviceId.
23
+ let ServiceAuthGuard = class ServiceAuthGuard {
24
+ constructor(reflector, interServiceAuthService) {
25
+ this.reflector = reflector;
26
+ this.interServiceAuthService = interServiceAuthService;
27
+ this.logger = new common_1.Logger('ServiceAuthGuard');
28
+ }
29
+ canActivate(context) {
30
+ const request = context.switchToHttp().getRequest();
31
+ // Step 1: if @RequireServiceAuth() is set, reject non-M2M requests immediately
32
+ const requireServiceAuth = this.reflector.getAllAndOverride(exports.REQUIRE_SERVICE_AUTH_KEY, [
33
+ context.getHandler(),
34
+ context.getClass(),
35
+ ]);
36
+ if (requireServiceAuth === true) {
37
+ if (!request.isServiceRequest || !request.serviceAuth) {
38
+ this.logger.warn(`Service authentication required but not provided for ${request.method} ${request.path}`);
39
+ throw new common_1.UnauthorizedException('Service-to-service authentication required');
40
+ }
41
+ }
42
+ else {
43
+ // No service auth requirement — let other guards handle non-M2M requests
44
+ if (!request.isServiceRequest || !request.serviceAuth) {
45
+ return true;
46
+ }
47
+ }
48
+ // Step 2: @AllowServiceAuth(false) explicitly blocks M2M on this endpoint.
49
+ // This wins even when @RequireServiceAuth() is set at class level.
50
+ const allowServiceAuth = this.reflector.getAllAndOverride(exports.ALLOW_SERVICE_AUTH_KEY, [
51
+ context.getHandler(),
52
+ context.getClass(),
53
+ ]);
54
+ if (allowServiceAuth === false) {
55
+ throw new common_1.ForbiddenException('Service authentication not allowed for this endpoint');
56
+ }
57
+ const serviceAuth = request.serviceAuth;
58
+ // Step 3: permission check (handler metadata takes precedence over class via getAllAndOverride)
59
+ const requiredPermissions = this.reflector.getAllAndOverride(exports.SERVICE_PERMISSIONS_KEY, [
60
+ context.getHandler(),
61
+ context.getClass(),
62
+ ]) || [];
63
+ if (requiredPermissions.length > 0) {
64
+ const hasPermission = requiredPermissions.some((permission) => this.checkPermission(serviceAuth.serviceId, serviceAuth.permissions, permission));
65
+ if (!hasPermission) {
66
+ this.logger.warn(`Service ${serviceAuth.serviceId} lacks required permissions: ${requiredPermissions.join(', ')}`);
67
+ throw new common_1.ForbiddenException('Insufficient permissions for service operation');
68
+ }
69
+ }
70
+ // Step 4: role check
71
+ const requiredRoles = this.reflector.getAllAndOverride(exports.SERVICE_ROLES_KEY, [
72
+ context.getHandler(),
73
+ context.getClass(),
74
+ ]) || [];
75
+ if (requiredRoles.length > 0) {
76
+ const hasRole = requiredRoles.some((role) => this.checkRole(serviceAuth.serviceId, serviceAuth.roles, role));
77
+ if (!hasRole) {
78
+ this.logger.warn(`Service ${serviceAuth.serviceId} lacks required roles: ${requiredRoles.join(', ')}`);
79
+ throw new common_1.ForbiddenException('Insufficient role for service operation');
80
+ }
81
+ }
82
+ this.logger.debug(`Service ${serviceAuth.serviceId} authorized for ${request.method} ${request.path}`);
83
+ return true;
84
+ }
85
+ checkPermission(serviceId, servicePermissions, requiredPermission) {
86
+ if (servicePermissions.includes('*') || servicePermissions.includes('inter-service.*')) {
87
+ return true;
88
+ }
89
+ if (servicePermissions.includes(requiredPermission)) {
90
+ return true;
91
+ }
92
+ // Prefix wildcard: 'contracts.*' covers 'contracts.read', 'contracts.write', etc.
93
+ const hasWildcardPermission = servicePermissions.some((permission) => {
94
+ if (permission.endsWith('.*')) {
95
+ const basePermission = permission.slice(0, -2);
96
+ return requiredPermission.startsWith(basePermission);
97
+ }
98
+ return false;
99
+ });
100
+ if (hasWildcardPermission) {
101
+ return true;
102
+ }
103
+ return this.interServiceAuthService.hasPermission(serviceId, requiredPermission);
104
+ }
105
+ checkRole(serviceId, serviceRoles, requiredRole) {
106
+ if (serviceRoles.includes(requiredRole)) {
107
+ return true;
108
+ }
109
+ // Any service with the base 'service' role passes coarse-grained role checks
110
+ if (serviceRoles.includes('service')) {
111
+ return true;
112
+ }
113
+ return this.interServiceAuthService.hasRole(serviceId, requiredRole);
114
+ }
115
+ };
116
+ exports.ServiceAuthGuard = ServiceAuthGuard;
117
+ exports.ServiceAuthGuard = ServiceAuthGuard = __decorate([
118
+ (0, common_1.Injectable)(),
119
+ __metadata("design:paramtypes", [core_1.Reflector,
120
+ inter_service_auth_service_1.InterServiceAuthService])
121
+ ], ServiceAuthGuard);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "permissions-contractx",
3
- "version": "2.2.0",
3
+ "version": "2.4.0",
4
4
  "description": "Enterprise-grade authentication and authorization package for NestJS microservices with role-based and permission-based access control",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",