permissions-contractx 1.0.2 → 1.1.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 (87) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +53 -1346
  3. package/dist/constants/contractx-permissions.constants.d.ts +84 -92
  4. package/dist/constants/contractx-permissions.constants.d.ts.map +1 -1
  5. package/dist/constants/contractx-permissions.constants.js +2 -2
  6. package/dist/constants/contractx-roles.constants.d.ts +150 -254
  7. package/dist/constants/contractx-roles.constants.d.ts.map +1 -1
  8. package/dist/constants/contractx-roles.constants.js +2 -2
  9. package/dist/constants/index.d.ts +1 -0
  10. package/dist/constants/index.d.ts.map +1 -1
  11. package/dist/constants/index.js +1 -0
  12. package/dist/constants/permission-names.constants.d.ts +310 -0
  13. package/dist/constants/permission-names.constants.d.ts.map +1 -0
  14. package/dist/constants/permission-names.constants.js +209 -0
  15. package/dist/constants/security.constants.d.ts +49 -49
  16. package/dist/constants/security.constants.d.ts.map +1 -1
  17. package/dist/constants/security.constants.js +2 -2
  18. package/dist/decorators/current-user.decorator.d.ts +5 -53
  19. package/dist/decorators/current-user.decorator.d.ts.map +1 -1
  20. package/dist/decorators/current-user.decorator.js +4 -51
  21. package/dist/decorators/index.d.ts +1 -0
  22. package/dist/decorators/index.d.ts.map +1 -1
  23. package/dist/decorators/index.js +1 -0
  24. package/dist/decorators/permission-writes.decorator.d.ts +14 -0
  25. package/dist/decorators/permission-writes.decorator.d.ts.map +1 -0
  26. package/dist/decorators/permission-writes.decorator.js +18 -0
  27. package/dist/decorators/permissions.decorator.d.ts +0 -58
  28. package/dist/decorators/permissions.decorator.d.ts.map +1 -1
  29. package/dist/decorators/permissions.decorator.js +0 -58
  30. package/dist/decorators/public.decorator.d.ts +0 -0
  31. package/dist/decorators/public.decorator.d.ts.map +0 -0
  32. package/dist/decorators/public.decorator.js +0 -0
  33. package/dist/decorators/roles.decorator.d.ts +4 -57
  34. package/dist/decorators/roles.decorator.d.ts.map +1 -1
  35. package/dist/decorators/roles.decorator.js +6 -57
  36. package/dist/guards/authorization.guard.d.ts +37 -0
  37. package/dist/guards/authorization.guard.d.ts.map +1 -0
  38. package/dist/guards/authorization.guard.js +150 -0
  39. package/dist/guards/index.d.ts +1 -0
  40. package/dist/guards/index.d.ts.map +1 -1
  41. package/dist/guards/index.js +1 -0
  42. package/dist/guards/jwt-auth.guard.d.ts +0 -0
  43. package/dist/guards/jwt-auth.guard.d.ts.map +1 -1
  44. package/dist/guards/jwt-auth.guard.js +0 -0
  45. package/dist/guards/permissions.guard.d.ts +0 -0
  46. package/dist/guards/permissions.guard.d.ts.map +1 -1
  47. package/dist/guards/permissions.guard.js +8 -2
  48. package/dist/guards/roles.guard.d.ts +0 -0
  49. package/dist/guards/roles.guard.d.ts.map +1 -1
  50. package/dist/guards/roles.guard.js +1 -1
  51. package/dist/index.d.ts +0 -0
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +0 -6
  54. package/dist/interfaces/index.d.ts +1 -0
  55. package/dist/interfaces/index.d.ts.map +1 -1
  56. package/dist/interfaces/index.js +1 -0
  57. package/dist/interfaces/jwt-payload.interface.d.ts +46 -9
  58. package/dist/interfaces/jwt-payload.interface.d.ts.map +1 -1
  59. package/dist/interfaces/jwt-payload.interface.js +19 -0
  60. package/dist/interfaces/permission-mode.enum.d.ts +22 -0
  61. package/dist/interfaces/permission-mode.enum.d.ts.map +1 -0
  62. package/dist/interfaces/permission-mode.enum.js +25 -0
  63. package/dist/modules/index.d.ts +0 -0
  64. package/dist/modules/index.d.ts.map +0 -0
  65. package/dist/modules/index.js +0 -0
  66. package/dist/modules/permissions-contractx.module.d.ts +0 -0
  67. package/dist/modules/permissions-contractx.module.d.ts.map +1 -1
  68. package/dist/modules/permissions-contractx.module.js +4 -2
  69. package/dist/services/contractx-authorization.service.d.ts +198 -27
  70. package/dist/services/contractx-authorization.service.d.ts.map +1 -1
  71. package/dist/services/contractx-authorization.service.js +2 -0
  72. package/dist/services/contractx-validation.service.d.ts +93 -12
  73. package/dist/services/contractx-validation.service.d.ts.map +1 -1
  74. package/dist/services/contractx-validation.service.js +1 -0
  75. package/dist/services/index.d.ts +0 -2
  76. package/dist/services/index.d.ts.map +1 -1
  77. package/dist/services/index.js +2 -0
  78. package/dist/services/user-context.service.d.ts +29 -34
  79. package/dist/services/user-context.service.d.ts.map +1 -1
  80. package/dist/services/user-context.service.js +65 -44
  81. package/package.json +5 -24
  82. package/dist/services/contractx-document-compliance.service.d.ts +0 -85
  83. package/dist/services/contractx-document-compliance.service.d.ts.map +0 -1
  84. package/dist/services/contractx-document-compliance.service.js +0 -536
  85. package/dist/test-document-compliance.d.ts +0 -7
  86. package/dist/test-document-compliance.d.ts.map +0 -1
  87. package/dist/test-document-compliance.js +0 -118
package/README.md CHANGED
@@ -1,1397 +1,104 @@
1
- # 🔐 Permissions ContractX
1
+ # permissions-contractx
2
2
 
3
- A comprehensive **NestJS authentication and authorization package** designed for microservices in the ContractX ecosystem. This package provides JWT-based authentication, role-based access control (RBAC), and granular permission management.
3
+ Librería compartida de autorización para la plataforma **ContractX**. Provee los guards, decoradores, interfaces y constantes que los microservicios usan para verificar los JWT que emite el servicio de Auth y aplicar el modelo de permisos de dominio (ADR-004).
4
4
 
5
- [![TypeScript](https://img.shields.io/badge/TypeScript-007ACC?style=flat&logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
6
- [![NestJS](https://img.shields.io/badge/NestJS-E0234E?style=flat&logo=nestjs&logoColor=white)](https://nestjs.com/)
7
- [![JWT](https://img.shields.io/badge/JWT-000000?style=flat&logo=JSON%20web%20tokens)](https://jwt.io/)
8
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ Auth **emite y firma** los tokens; cada microservicio **verifica** los suyos usando este paquete. Ningún servicio mantiene su propio catálogo de permisos: todos referencian el vocabulario central definido en Auth.
9
6
 
10
- ## ✨ Features
11
-
12
- - 🔑 **JWT Authentication** - Secure token-based authentication
13
- - 👥 **Role-Based Access Control** - Hierarchical role system
14
- - 🔐 **Permission-Based Authorization** - Granular permission control
15
- - 🏢 **Multi-Tenant Support** - Client/Provider organizational structure
16
- - 🚀 **Easy Integration** - Simple decorator-based API
17
- - 🛡️ **Security First** - Built-in security best practices
18
- - 📊 **Request Context** - User context service for easy access
19
- - 🔧 **TypeScript** - Full TypeScript support with strict typing
20
- - 📖 **Comprehensive Documentation** - Detailed examples and guides
21
-
22
- ## 📦 Installation
7
+ ## Instalación
23
8
 
24
9
  ```bash
25
10
  npm install permissions-contractx
26
11
  ```
27
12
 
28
- ### Peer Dependencies
13
+ ### Peer dependencies
29
14
 
30
- Make sure you have the required peer dependencies installed:
15
+ El paquete espera que el servicio consumidor ya tenga instaladas:
31
16
 
32
- ```bash
33
- npm install @nestjs/common @nestjs/core @nestjs/jwt @nestjs/config jsonwebtoken rxjs reflect-metadata
34
- ```
17
+ - `@nestjs/common`, `@nestjs/core`
18
+ - `reflect-metadata`, `rxjs`
19
+ - `jsonwebtoken`
20
+ - `express`
35
21
 
36
- ## 🚀 Quick Start
22
+ ## Qué incluye
37
23
 
38
- ### 1. Basic Setup
24
+ ### Guards
39
25
 
40
- ```typescript
41
- // app.module.ts
42
- import { Module } from '@nestjs/common';
43
- import { PermissionsContractXModule } from 'permissions-contractx';
26
+ - **`AuthorizationGuard`** — guard de autorización con cuatro estados de migración por endpoint (legacy / observación / estricto / solo-permisos), eje lectura/escritura derivado del método HTTP, y soporte de modo-ver (un permiso de solo lectura bloquea escrituras). Resuelve conflictos con la regla *deny-overrides* (deny siempre gana).
27
+ - `JwtAuthGuard` — verificación y decodificación del JWT.
44
28
 
45
- @Module({
46
- imports: [
47
- PermissionsContractXModule.register({
48
- jwt: {
49
- secret: 'your-secret-key',
50
- issuer: 'your-api',
51
- audience: 'your-users',
52
- expiresIn: '15m',
53
- },
54
- guards: {
55
- enableGlobalAuth: true, // Apply authentication globally
56
- },
57
- }),
58
- ],
59
- })
60
- export class AppModule {}
61
- ```
29
+ ### Decoradores
62
30
 
63
- ### 2. Using Decorators in Controllers
31
+ - `@RequirePermissions(...)` declara los permisos que un endpoint exige.
32
+ - `@PermissionWrites(...)` — override explícito del eje lectura/escritura (por ejemplo, un `GET` con efectos o un `POST` de solo lectura).
33
+ - `@CurrentUser()` — inyecta el usuario resuelto del token.
34
+ - `@Public()` — marca un endpoint como abierto.
64
35
 
65
- ```typescript
66
- import { Controller, Get, Post, UseGuards } from '@nestjs/common';
67
- import {
68
- CurrentUser,
69
- Roles,
70
- RequirePermissions,
71
- Public,
72
- JwtAuthGuard,
73
- RolesGuard,
74
- PermissionsGuard,
75
- JwtPayload,
76
- } from 'permissions-contractx';
36
+ ### Interfaces
77
37
 
78
- @Controller('users')
79
- @UseGuards(JwtAuthGuard, RolesGuard, PermissionsGuard)
80
- export class UsersController {
81
-
82
- // Public endpoint (no authentication)
83
- @Public()
84
- @Get('public')
85
- getPublicInfo() {
86
- return { message: 'This is public' };
87
- }
38
+ - **`JwtPayload`** — contrato del token que emite Auth: `sub`, `role[]`, `permissions[]`, `permissionsView[]`, `clientId[]`, `providerId`, `tenantContext` (`'client' | 'provider' | 'system'`), `key_client`, entre otros.
39
+ - `PermissionMode` — enum `WRITE` / `READ`, espejo del modo del par rol↔permiso en Auth.
88
40
 
89
- // Authenticated endpoint
90
- @Get('profile')
91
- getProfile(@CurrentUser() user: JwtPayload) {
92
- return {
93
- id: user.sub,
94
- name: user.fullName,
95
- roles: user.role,
96
- permissions: user.permissions,
97
- };
98
- }
41
+ ### Constantes
99
42
 
100
- // Role-based access
101
- @Roles('superadmin', 'client_contract_admin')
102
- @Get('admin-data')
103
- getAdminData() {
104
- return { message: 'Admin only data' };
105
- }
43
+ - **`permission-names.constants`** — los 130 códigos de permiso de dominio, agrupados por microservicio, más `PERMISSIONS_BY_DOMAIN`, `ALL_PERMISSION_CODES` y el tipo `PermissionCode`.
44
+ - `security.constants` — constantes de roles del sistema.
106
45
 
107
- // Permission-based access
108
- @RequirePermissions('users.create')
109
- @Post()
110
- createUser(@CurrentUser() user: JwtPayload) {
111
- return { message: 'User created', createdBy: user.fullName };
112
- }
113
-
114
- // Combined role and permission
115
- @Roles('client_contract_admin')
116
- @RequirePermissions('users.delete')
117
- @Delete(':id')
118
- deleteUser(@Param('id') id: string) {
119
- return { message: `User ${id} deleted` };
120
- }
121
- }
122
- ```
123
-
124
- ### 3. Environment Variables Setup
125
-
126
- Create a `.env` file:
127
-
128
- ```env
129
- JWT_SECRET=your-super-secret-jwt-key-change-in-production
130
- JWT_ISSUER=contractx-api
131
- JWT_AUDIENCE=contractx-users
132
- JWT_EXPIRES_IN=15m
133
-
134
- # Optional: Enable global guards
135
- ENABLE_GLOBAL_AUTH=true
136
- ENABLE_AUTH_LOGGING=true
137
-
138
- # Development only
139
- DISABLE_AUTH=false # Set to true to disable auth in development
140
- ```
46
+ ## Uso básico
141
47
 
142
- ### 4. Auto-Configuration with Environment Variables
48
+ Registrar el guard de forma global en un microservicio (NestJS):
143
49
 
144
50
  ```typescript
145
- // app.module.ts - Easiest setup
146
- @Module({
147
- imports: [
148
- PermissionsContractXModule.forRoot(), // Auto-configures from environment variables
149
- ],
150
- })
151
- export class AppModule {}
152
- ```
153
-
154
- ## 🏗️ Advanced Configuration
155
-
156
- ### Async Configuration
157
-
158
- ```typescript
159
- import { ConfigModule, ConfigService } from '@nestjs/config';
51
+ import { AuthorizationGuard } from 'permissions-contractx';
52
+ import { APP_GUARD } from '@nestjs/core';
160
53
 
161
54
  @Module({
162
- imports: [
163
- PermissionsContractXModule.registerAsync({
164
- imports: [ConfigModule],
165
- useFactory: async (configService: ConfigService) => ({
166
- jwt: {
167
- secret: configService.get('JWT_SECRET'),
168
- issuer: configService.get('JWT_ISSUER'),
169
- audience: configService.get('JWT_AUDIENCE'),
170
- expiresIn: configService.get('JWT_EXPIRES_IN', '15m'),
171
- clockTolerance: 30, // seconds
172
- },
173
- guards: {
174
- enableGlobalAuth: true,
175
- enableGlobalRoles: false,
176
- enableGlobalPermissions: false,
177
- },
178
- security: {
179
- enableLogging: process.env.NODE_ENV === 'production',
180
- },
181
- development: {
182
- disableAuth: process.env.NODE_ENV === 'development',
183
- mockUser: {
184
- sub: 'dev-user-123',
185
- role: ['superadmin'],
186
- permissions: ['*'],
187
- fullName: 'Development User',
188
- email: 'dev@example.com',
189
- },
190
- },
191
- }),
192
- inject: [ConfigService],
193
- }),
55
+ providers: [
56
+ { provide: APP_GUARD, useClass: AuthorizationGuard },
194
57
  ],
195
58
  })
196
59
  export class AppModule {}
197
60
  ```
198
61
 
199
- ## 📋 JWT Token Structure
200
-
201
- The JWT token must include the following payload structure:
202
-
203
- ```json
204
- {
205
- "sub": "user_123456789", // User ID (required)
206
- "fullName": "John Doe", // User's full name (required)
207
- "email": "john@example.com", // User's email
208
- "clientId": "client_001", // Organization/Client ID
209
- "sessionId": "session_abc", // Session tracking
210
-
211
- "role": [ // User roles array (required)
212
- "admin",
213
- "manager",
214
- "user"
215
- ],
216
-
217
- "permissions": [ // User permissions array (required)
218
- "users:read",
219
- "users:create",
220
- "users:update",
221
- "users:delete",
222
- "settings:manage"
223
- ],
224
-
225
- // Standard JWT fields
226
- "iat": 1609459200, // Issued at
227
- "exp": 1609545600, // Expires at
228
- "iss": "contractx-api", // Issuer
229
- "aud": "contractx-users" // Audience
230
- }
231
- ```
232
-
233
- ## 🛡️ Guards
234
-
235
- ### 1. JwtAuthGuard
236
-
237
- Validates JWT tokens and extracts user information.
238
-
239
- ```typescript
240
- import { Controller, UseGuards } from '@nestjs/common';
241
- import { JwtAuthGuard } from 'permissions-contractx';
242
-
243
- @Controller('protected')
244
- @UseGuards(JwtAuthGuard)
245
- export class ProtectedController {
246
- // All routes require valid JWT token
247
- }
248
- ```
249
-
250
- ### 2. RolesGuard
251
-
252
- Checks if user has required roles.
62
+ Proteger un endpoint exigiendo un permiso:
253
63
 
254
64
  ```typescript
255
- import { Controller, UseGuards } from '@nestjs/common';
256
- import { JwtAuthGuard, RolesGuard, RequireRoles } from 'permissions-contractx';
257
-
258
- @Controller('admin')
259
- @UseGuards(JwtAuthGuard, RolesGuard)
260
- export class AdminController {
261
-
262
- @Get('dashboard')
263
- @RequireRoles('admin', 'manager') // User must have admin OR manager role
264
- getDashboard() {
265
- return { message: 'Admin dashboard' };
266
- }
267
- }
268
- ```
269
-
270
- ### 3. PermissionsGuard
271
-
272
- Checks if user has required permissions.
273
-
274
- ```typescript
275
- import { Controller, UseGuards } from '@nestjs/common';
276
- import { JwtAuthGuard, PermissionsGuard, RequirePermissions } from 'permissions-contractx';
277
-
278
- @Controller('users')
279
- @UseGuards(JwtAuthGuard, PermissionsGuard)
280
- export class UsersController {
281
-
282
- @Post()
283
- @RequirePermissions('users:create') // User must have this exact permission
284
- createUser() {
285
- return { message: 'User created' };
286
- }
287
-
288
- @Delete(':id')
289
- @RequirePermissions('users:delete', 'admin:all') // User must have one of these
290
- deleteUser() {
291
- return { message: 'User deleted' };
292
- }
293
- }
294
- ```
295
-
296
- ## 🎯 Decorators
297
-
298
- The permissions-contractx package provides a comprehensive set of decorators for authentication and authorization in your NestJS applications.
299
-
300
- ### 🔐 Authentication Decorators
301
-
302
- #### `@Public()`
303
- Marks routes as public (no authentication required). Essential for login endpoints, health checks, and public APIs.
304
-
305
- ```typescript
306
- @Controller('auth')
307
- export class AuthController {
308
- @Public()
309
- @Post('login')
310
- login(@Body() credentials: LoginDto) {
311
- return this.authService.login(credentials);
312
- }
313
-
314
- @Public()
315
- @Get('health')
316
- getHealth() {
317
- return { status: 'OK', timestamp: new Date() };
318
- }
319
- }
320
- ```
321
-
322
- #### `@CurrentUser(property?)`
323
- Injects the current authenticated user or specific user properties into route handlers.
324
-
325
- ```typescript
326
- @Controller('users')
327
- export class UsersController {
328
- // Inject full user object
329
- @Get('profile')
330
- getProfile(@CurrentUser() user: JwtPayload) {
331
- return {
332
- id: user.sub,
333
- name: user.fullName,
334
- email: user.email,
335
- roles: user.role,
336
- permissions: user.permissions
337
- };
338
- }
339
-
340
- // Inject specific user property
341
- @Post('actions')
342
- performAction(
343
- @CurrentUser('sub') userId: string,
344
- @CurrentUser('fullName') userName: string,
345
- @Body() actionData: any
346
- ) {
347
- return {
348
- action: 'completed',
349
- performedBy: userName,
350
- userId: userId,
351
- data: actionData
352
- };
353
- }
354
- }
355
- ```
356
-
357
- #### `@UserId()`
358
- Convenience decorator to get the current user's ID directly.
359
-
360
- ```typescript
361
- @Controller('resources')
362
- export class ResourcesController {
363
- @Post()
364
- createResource(
365
- @UserId() userId: string,
366
- @Body() resourceData: CreateResourceDto
367
- ) {
368
- return this.resourcesService.create({
369
- ...resourceData,
370
- createdBy: userId,
371
- createdAt: new Date()
372
- });
373
- }
374
-
375
- @Get('my-resources')
376
- getMyResources(@UserId() userId: string) {
377
- return this.resourcesService.findByUser(userId);
378
- }
379
- }
380
- ```
381
-
382
- #### `@UserRoles()`
383
- Injects the current user's roles array into the route handler.
384
-
385
- ```typescript
386
- @Controller('dashboard')
387
- export class DashboardController {
388
- @Get('capabilities')
389
- getUserCapabilities(@UserRoles() roles: string[]) {
390
- const capabilities = {
391
- canManageUsers: roles.includes('superadmin') || roles.includes('client_contract_admin'),
392
- canViewReports: roles.some(role => role.includes('reports')),
393
- canManageContracts: roles.some(role => role.includes('contract_admin')),
394
- isClientSide: roles.some(role => role.startsWith('client_')),
395
- isProviderSide: roles.some(role => role.startsWith('provider_'))
396
- };
397
-
398
- return { roles, capabilities };
399
- }
400
- }
401
- ```
65
+ import { RequirePermissions } from 'permissions-contractx';
402
66
 
403
- #### `@UserPermissions()`
404
- Injects the current user's permissions array into the route handler.
405
-
406
- ```typescript
407
- @Controller('admin')
408
- export class AdminController {
409
- @Get('available-actions')
410
- getAvailableActions(@UserPermissions() permissions: string[]) {
411
- const modulePermissions = permissions.reduce((acc, permission) => {
412
- const [module, action] = permission.split(':');
413
- if (!acc[module]) acc[module] = [];
414
- acc[module].push(action);
415
- return acc;
416
- }, {});
417
-
418
- return {
419
- totalPermissions: permissions.length,
420
- moduleBreakdown: modulePermissions,
421
- rawPermissions: permissions
422
- };
423
- }
424
- }
425
- ```
426
-
427
- #### `@UserClientId()`
428
- Injects the current user's client ID for multi-tenant applications.
429
-
430
- ```typescript
431
- @Controller('tenant')
432
- export class TenantController {
433
- @Get('data')
434
- getTenantData(@UserClientId() clientId: string) {
435
- return this.dataService.findByClient(clientId);
436
- }
437
-
438
- @Post('settings')
439
- updateTenantSettings(
440
- @UserClientId() clientId: string,
441
- @Body() settings: TenantSettingsDto
442
- ) {
443
- return this.settingsService.update(clientId, settings);
444
- }
445
- }
446
- ```
447
-
448
- ### 👥 Role-Based Authorization Decorators
449
-
450
- #### `@Roles(...roles)`
451
- Requires user to have **at least one** of the specified roles (OR logic).
452
-
453
- ```typescript
454
- @Controller('admin')
455
- export class AdminController {
456
- // User needs superadmin OR any contract admin role
457
- @Roles('superadmin', 'client_contract_admin', 'provider_contract_admin')
458
- @Get('dashboard')
459
- getAdminDashboard() {
460
- return { message: 'Admin dashboard data' };
461
- }
462
-
463
- // Multiple specific roles
464
- @Roles('client_finance_manager', 'provider_finance_manager')
465
- @Get('financial-reports')
466
- getFinancialReports() {
467
- return this.reportsService.getFinancialData();
468
- }
469
- }
470
- ```
471
-
472
- #### `@AdminOnly()`
473
- Shortcut for admin-level access (superadmin, client_contract_admin, provider_contract_admin).
474
-
475
- ```typescript
476
- @Controller('system')
477
- export class SystemController {
478
- @AdminOnly()
479
- @Delete('cache')
480
- clearCache() {
481
- return this.cacheService.clear();
482
- }
483
-
484
- @AdminOnly()
485
- @Post('maintenance-mode')
486
- enableMaintenanceMode(@Body() config: MaintenanceConfigDto) {
487
- return this.systemService.enableMaintenance(config);
488
- }
489
- }
490
- ```
491
-
492
- #### `@ClientOnly()`
493
- Restricts access to client-side roles only.
494
-
495
- ```typescript
496
- @Controller('client')
497
- export class ClientController {
498
- @ClientOnly()
499
- @Get('performance-metrics')
500
- getClientPerformanceMetrics(@UserClientId() clientId: string) {
501
- return this.metricsService.getClientMetrics(clientId);
502
- }
503
-
504
- @ClientOnly()
505
- @Post('escalation')
506
- createEscalation(@Body() escalationData: CreateEscalationDto) {
507
- return this.escalationService.create(escalationData);
508
- }
509
- }
510
- ```
511
-
512
- #### `@ProviderOnly()`
513
- Restricts access to provider-side roles only.
514
-
515
- ```typescript
516
- @Controller('provider')
517
- export class ProviderController {
518
- @ProviderOnly()
519
- @Get('delivery-schedule')
520
- getDeliverySchedule() {
521
- return this.deliveryService.getSchedule();
522
- }
523
-
524
- @ProviderOnly()
525
- @Put('deliverable/:id/status')
526
- updateDeliverableStatus(
527
- @Param('id') id: string,
528
- @Body() statusUpdate: DeliverableStatusDto
529
- ) {
530
- return this.deliverableService.updateStatus(id, statusUpdate);
531
- }
532
- }
533
- ```
534
-
535
- #### `@SuperAdminOnly()`
536
- Restricts access to superadmin role only.
537
-
538
- ```typescript
539
- @Controller('system-admin')
540
- export class SystemAdminController {
541
- @SuperAdminOnly()
542
- @Post('create-tenant')
543
- createTenant(@Body() tenantData: CreateTenantDto) {
544
- return this.tenantService.create(tenantData);
545
- }
546
-
547
- @SuperAdminOnly()
548
- @Delete('user/:id')
549
- deleteUser(@Param('id') userId: string) {
550
- return this.userService.delete(userId);
551
- }
552
- }
553
- ```
554
-
555
- ### 🔐 Permission-Based Authorization Decorators
556
-
557
- #### `@RequirePermissions(...permissions)`
558
- Requires user to have **all** specified permissions (AND logic).
559
-
560
- ```typescript
561
67
  @Controller('contracts')
562
68
  export class ContractsController {
563
- // User must have ALL specified permissions
564
- @RequirePermissions('contracts.create', 'contracts.validate')
565
69
  @Post()
566
- createContract(@Body() contractData: CreateContractDto) {
567
- return this.contractService.create(contractData);
568
- }
569
-
570
- // Single permission requirement
571
- @RequirePermissions('contracts.delete')
572
- @Delete(':id')
573
- deleteContract(@Param('id') id: string) {
574
- return this.contractService.delete(id);
575
- }
576
- }
577
- ```
578
-
579
- #### `@RequireAnyPermission(...permissions)`
580
- Requires user to have **at least one** of the specified permissions (OR logic).
581
-
582
- ```typescript
583
- @Controller('documents')
584
- export class DocumentsController {
585
- // User needs ANY of these permissions
586
- @RequireAnyPermission('documents.read', 'documents.show', 'documents.filter')
587
- @Get()
588
- getDocuments(@Query() filters: DocumentFiltersDto) {
589
- return this.documentService.findAll(filters);
590
- }
591
-
592
- // Administrative permissions
593
- @RequireAnyPermission('documents.admin', 'superadmin.all')
594
- @Post('batch-process')
595
- batchProcessDocuments(@Body() batchData: BatchProcessDto) {
596
- return this.documentService.batchProcess(batchData);
597
- }
598
- }
599
- ```
600
-
601
- ### 🎯 Module-Level Permission Decorators
602
-
603
- These decorators provide convenient access patterns based on common CRUD operations:
604
-
605
- #### `@ReadAccess(module)`
606
- Grants read access to a module (read, show, or filter permissions).
607
-
608
- ```typescript
609
- @Controller('reports')
610
- export class ReportsController {
611
- @ReadAccess('reports')
612
- @Get('financial')
613
- getFinancialReports() {
614
- // User needs reports.read, reports.show, OR reports.filter
615
- return this.reportsService.getFinancialReports();
616
- }
617
-
618
- @ReadAccess('contracts')
619
- @Get('contract-summary')
620
- getContractSummary() {
621
- // User needs contracts.read, contracts.show, OR contracts.filter
622
- return this.contractService.getSummary();
623
- }
624
- }
625
- ```
626
-
627
- #### `@WriteAccess(module)`
628
- Grants write access to a module (create or update permissions).
629
-
630
- ```typescript
631
- @Controller('users')
632
- export class UsersController {
633
- @WriteAccess('users')
634
- @Post()
635
- createUser(@Body() userData: CreateUserDto) {
636
- // User needs users.create OR users.update
637
- return this.userService.create(userData);
638
- }
639
-
640
- @WriteAccess('users')
641
- @Put(':id')
642
- updateUser(
643
- @Param('id') id: string,
644
- @Body() userData: UpdateUserDto
645
- ) {
646
- // User needs users.create OR users.update
647
- return this.userService.update(id, userData);
648
- }
649
- }
650
- ```
651
-
652
- #### `@DeleteAccess(module)`
653
- Grants delete access to a module.
654
-
655
- ```typescript
656
- @Controller('deliverables')
657
- export class DeliverablesController {
658
- @DeleteAccess('deliverables')
659
- @Delete(':id')
660
- deleteDeliverable(@Param('id') id: string) {
661
- // User needs deliverables.delete
662
- return this.deliverableService.delete(id);
663
- }
664
- }
665
- ```
666
-
667
- #### `@FullAccess(module)`
668
- Grants full CRUD access to a module (any module permission).
669
-
670
- ```typescript
671
- @Controller('admin/modules')
672
- export class ModuleAdminController {
673
- @FullAccess('users')
674
- @Post('users/bulk-operation')
675
- bulkUserOperation(@Body() operation: BulkOperationDto) {
676
- // User needs ANY users module permission
677
- return this.userService.bulkOperation(operation);
678
- }
679
- }
680
- ```
681
-
682
- ### 🔄 Combining Decorators
683
-
684
- Decorators can be combined for complex authorization logic:
685
-
686
- ```typescript
687
- @Controller('advanced')
688
- export class AdvancedController {
689
- // Combine role and permission requirements
690
- @Roles('client_contract_admin', 'provider_contract_admin')
691
- @RequirePermissions('contracts.approve')
692
- @Put('contracts/:id/approve')
693
- approveContract(
694
- @Param('id') contractId: string,
695
- @CurrentUser() user: JwtPayload
696
- ) {
697
- return this.contractService.approve(contractId, user.sub);
698
- }
699
-
700
- // Complex permission logic
701
- @ClientOnly()
702
- @RequireAnyPermission('reports.generate', 'reports.admin')
703
- @Post('custom-report')
704
- generateCustomReport(
705
- @Body() reportSpec: CustomReportDto,
706
- @UserClientId() clientId: string
707
- ) {
708
- return this.reportsService.generateCustom(reportSpec, clientId);
709
- }
710
-
711
- // Multiple permission sets
712
- @RequirePermissions('documents.create')
713
- @RequireAnyPermission('contracts.update', 'deliverables.update')
714
- @Post('document-with-linking')
715
- createLinkedDocument(
716
- @Body() documentData: LinkedDocumentDto,
717
- @UserId() userId: string
718
- ) {
719
- // User must have documents.create AND (contracts.update OR deliverables.update)
720
- return this.documentService.createWithLinking(documentData, userId);
721
- }
722
- }
723
- ```
724
-
725
- ### 🏷️ Method vs Class Level Decorators
726
-
727
- Decorators can be applied at both class and method levels:
728
-
729
- ```typescript
730
- // Class-level decorators apply to all methods
731
- @Controller('secure')
732
- @UseGuards(JwtAuthGuard, RolesGuard, PermissionsGuard)
733
- @ClientOnly() // All methods require client roles
734
- export class SecureController {
735
- @Get('data')
736
- getData() {
737
- // Inherits @ClientOnly from class
738
- return this.dataService.getClientData();
739
- }
740
-
741
- @RequirePermissions('admin.override')
742
- @Get('admin-data')
743
- getAdminData() {
744
- // Combines @ClientOnly + @RequirePermissions
745
- return this.dataService.getAdminData();
746
- }
747
-
748
- @Public() // Overrides class-level @ClientOnly
749
- @Get('public-info')
750
- getPublicInfo() {
751
- return { message: 'Public information' };
752
- }
753
- }
754
- ```
755
-
756
- ## 🛠️ Services
757
-
758
- ### UserContextService
759
-
760
- Access user information and check permissions programmatically:
761
-
762
- ```typescript
763
- import { Injectable } from '@nestjs/common';
764
- import { UserContextService } from 'permissions-contractx';
765
-
766
- @Injectable()
767
- export class MyService {
768
- constructor(private userContext: UserContextService) {}
769
-
770
- async doSomething() {
771
- // Get user information
772
- const user = this.userContext.getUser();
773
- const userId = this.userContext.getUserId();
774
- const roles = this.userContext.getUserRoles();
775
-
776
- // Check permissions
777
- if (this.userContext.hasRole('admin')) {
778
- // Do admin stuff
779
- }
780
-
781
- if (this.userContext.hasPermission('users.create')) {
782
- // Can create users
783
- }
784
-
785
- if (this.userContext.isClientUser()) {
786
- // Client-side user logic
787
- }
788
-
789
- // Get user summary for logging
790
- const summary = this.userContext.getUserSummary();
791
- console.log('User accessing service:', summary);
792
- }
793
- }
794
- ```
795
-
796
- ## 🚫 Error Handling
797
-
798
- The package provides specific error messages for different authentication and authorization failures:
799
-
800
- ### Common Error Responses
801
-
802
- ```json
803
- // 401 - Missing token
804
- {
805
- "statusCode": 401,
806
- "message": "Access token is required",
807
- "error": "Unauthorized"
808
- }
809
-
810
- // 401 - Invalid token
811
- {
812
- "statusCode": 401,
813
- "message": "Invalid access token",
814
- "error": "Unauthorized"
815
- }
816
-
817
- // 401 - Expired token
818
- {
819
- "statusCode": 401,
820
- "message": "Access token has expired",
821
- "error": "Unauthorized"
822
- }
823
-
824
- // 403 - Insufficient permissions
825
- {
826
- "statusCode": 403,
827
- "message": "Insufficient permissions. Required: users:create",
828
- "error": "Forbidden"
829
- }
830
-
831
- // 403 - Missing roles
832
- {
833
- "statusCode": 403,
834
- "message": "Insufficient roles. Required: admin",
835
- "error": "Forbidden"
836
- }
837
- ```
838
-
839
- ### Custom Error Handling
840
-
841
- ```typescript
842
- import { ExceptionFilter, Catch, ArgumentsHost } from '@nestjs/common';
843
- import { UnauthorizedException, ForbiddenException } from '@nestjs/common';
844
-
845
- @Catch(UnauthorizedException, ForbiddenException)
846
- export class AuthExceptionFilter implements ExceptionFilter {
847
- catch(exception: UnauthorizedException | ForbiddenException, host: ArgumentsHost) {
848
- const ctx = host.switchToHttp();
849
- const response = ctx.getResponse();
850
-
851
- // Custom error handling logic
852
- response.status(exception.getStatus()).json({
853
- statusCode: exception.getStatus(),
854
- message: exception.message,
855
- timestamp: new Date().toISOString(),
856
- });
857
- }
858
- }
859
- ```
860
-
861
- ## 💾 Database Integration & ODS Seeder
862
-
863
- ### ODS Roles and Permissions Seeder
864
-
865
- The package includes a complete seeder for the ODS (Operational Data Store) roles and permissions matrix with **16 predefined roles** and **23 modules**:
866
-
867
- ```typescript
868
- import { ContractXRolePermissionSeeder } from '@your-org/permissions-contractx';
869
-
870
- @Injectable()
871
- export class DatabaseSeederService {
872
- constructor(
873
- private readonly seeder: ContractXRolePermissionSeeder,
874
- // Your repository dependencies
875
- @InjectRepository(Role) private roleRepo: Repository<Role>,
876
- @InjectRepository(Permission) private permissionRepo: Repository<Permission>,
877
- @InjectRepository(RolePermission) private rolePermissionRepo: Repository<RolePermission>
878
- ) {}
879
-
880
- async seedRolesAndPermissions() {
881
- const { roles, permissions, rolePermissions } = await this.seeder.seed();
882
-
883
- // Save to database
884
- const savedRoles = await this.roleRepo.save(roles);
885
- const savedPermissions = await this.permissionRepo.save(permissions);
886
-
887
- // Update IDs for role-permission mappings
888
- const mappingsWithIds = rolePermissions.map(rp => ({
889
- ...rp,
890
- roleId: savedRoles.find(r => r.name === roles.find(role => role.id === rp.roleId)?.name)?.id,
891
- permissionId: savedPermissions.find(p => p.name === permissions.find(perm => perm.id === rp.permissionId)?.name)?.id
892
- }));
893
-
894
- await this.rolePermissionRepo.save(mappingsWithIds);
895
-
896
- // Validate seeded data
897
- const isValid = await this.seeder.validateSeed(savedRoles, savedPermissions, mappingsWithIds);
898
- if (!isValid) {
899
- throw new Error('Seeder validation failed');
900
- }
901
-
902
- // Get statistics
903
- const stats = this.seeder.getSeederStats();
904
- console.log('Seeding completed:', stats);
905
- /*
906
- Output:
907
- {
908
- totalRoles: 16,
909
- totalPermissions: 161, // 23 modules × 7 actions each
910
- totalModules: 23,
911
- rolesByType: {
912
- system: 4,
913
- client: 6,
914
- provider: 6
915
- },
916
- permissionsByCategory: {
917
- contract_management: 98,
918
- financial: 21,
919
- system_admin: 21,
920
- user_management: 7,
921
- client_management: 7,
922
- provider_management: 7
923
- }
924
- }
925
- */
926
- }
927
- }
928
- ```
929
-
930
- ### ODS Roles Matrix (16 Roles)
931
-
932
- The seeder creates the following roles based on the complete ODS specification:
933
-
934
- #### 🔧 System Roles
935
- - **superadmin**: Full system access to all modules
936
- - **support**: Broad access excluding client management
937
- - **auditor**: Read-only audit and compliance access
938
- - **guest**: Limited read-only access
939
-
940
- #### 👥 Client-Side Roles
941
- - **client_contract_admin**: Full contract management for client
942
- - **client_performance_manager**: SLA and performance management
943
- - **client_finance_manager**: Financial operations and invoicing
944
- - **client_reports_manager**: Reporting and analytics
945
- - **client_relationship_manager**: Relationship and meeting management
946
- - **client_risk_manager**: Risk and security management
947
-
948
- #### 🏢 Provider-Side Roles
949
- - **provider_contract_admin**: Provider contract administration
950
- - **provider_performance_manager**: Provider performance management
951
- - **provider_finance_manager**: Provider financial operations
952
- - **provider_relationship_manager**: Provider relationship management
953
- - **provider_risk_manager**: Provider risk management
954
- - **provider_operator**: Operational support and deliverable management
955
-
956
- ### 📊 ODS Modules (23 Modules)
957
-
958
- The seeder covers all ContractX modules with comprehensive CRUD permissions:
959
-
960
- - **Core Management**: clients, contracts, users, providers, documents, clauses
961
- - **Deliverables**: deliverables, subdeliverables, deliverable_history
962
- - **SLA Management**: sla_services, measurement_windows, credit_service_levels
963
- - **Collaboration**: meetings, meeting_participants, action_items
964
- - **Notifications**: notification_escalations
965
- - **Financial**: invoice_services, invoice_lines
966
- - **System**: security_control, configuration, workflows
967
-
968
- ### Database Entity Examples
969
-
970
- For database integration with TypeORM, implement the seeder interfaces:
971
-
972
- ```typescript
973
- // Role Entity
974
- @Entity('roles')
975
- export class Role implements RoleEntity {
976
- @PrimaryGeneratedColumn('uuid')
977
- id: string;
978
-
979
- @Column({ unique: true })
980
- name: string;
981
-
982
- @Column()
983
- displayName: string;
984
-
985
- @Column()
986
- description: string;
987
-
988
- @Column()
989
- type: string;
990
-
991
- @Column()
992
- scope: string;
993
-
994
- @Column()
995
- level: number;
996
-
997
- @Column({ default: true })
998
- isActive: boolean;
999
-
1000
- @Column()
1001
- tenantAssociation: string;
1002
-
1003
- @Column('jsonb')
1004
- metadata: Record<string, any>;
1005
-
1006
- @CreateDateColumn()
1007
- createdAt: Date;
1008
-
1009
- @UpdateDateColumn()
1010
- updatedAt: Date;
1011
- }
1012
-
1013
- // Permission Entity
1014
- @Entity('permissions')
1015
- export class Permission implements PermissionEntity {
1016
- @PrimaryGeneratedColumn('uuid')
1017
- id: string;
1018
-
1019
- @Column({ unique: true })
1020
- name: string;
1021
-
1022
- @Column()
1023
- displayName: string;
1024
-
1025
- @Column()
1026
- description: string;
1027
-
1028
- @Column()
1029
- module: string;
1030
-
1031
- @Column()
1032
- action: string;
1033
-
1034
- @Column()
1035
- category: string;
1036
-
1037
- @Column()
1038
- scope: string;
1039
-
1040
- @Column({ default: true })
1041
- isActive: boolean;
1042
-
1043
- @Column('jsonb')
1044
- metadata: Record<string, any>;
1045
-
1046
- @CreateDateColumn()
1047
- createdAt: Date;
1048
-
1049
- @UpdateDateColumn()
1050
- updatedAt: Date;
1051
- }
1052
-
1053
- // Role-Permission Junction
1054
- @Entity('role_permissions')
1055
- export class RolePermission implements RolePermissionEntity {
1056
- @PrimaryGeneratedColumn('uuid')
1057
- id: string;
1058
-
1059
- @Column()
1060
- roleId: string;
1061
-
1062
- @Column()
1063
- permissionId: string;
1064
-
1065
- @Column({ default: true })
1066
- isActive: boolean;
1067
-
1068
- @Column('jsonb', { nullable: true })
1069
- metadata: Record<string, any>;
1070
-
1071
- @CreateDateColumn()
1072
- createdAt: Date;
1073
-
1074
- @UpdateDateColumn()
1075
- updatedAt: Date;
1076
- }
1077
-
1078
- // User entity example with JWT payload
1079
- @Entity('users')
1080
- export class User implements JwtPayload {
1081
- @PrimaryGeneratedColumn('uuid')
1082
- id: string;
1083
-
1084
- @Column()
1085
- email: string;
1086
-
1087
- @Column('simple-array')
1088
- roles: string[];
1089
-
1090
- @Column('simple-array')
1091
- permissions: string[];
1092
-
1093
- @Column({ nullable: true })
1094
- clientId?: string;
1095
-
1096
- @Column({ nullable: true })
1097
- providerId?: string;
1098
-
1099
- @Column()
1100
- firstName: string;
1101
-
1102
- @Column()
1103
- lastName: string;
1104
-
1105
- @Column({ default: true })
1106
- isActive: boolean;
1107
-
1108
- @CreateDateColumn()
1109
- createdAt: Date;
1110
-
1111
- @UpdateDateColumn()
1112
- updatedAt: Date;
1113
- }
1114
- ```
1115
-
1116
- ### Integration with Auth Service
1117
-
1118
- ```typescript
1119
- // In your auth-service-contract
1120
- @Injectable()
1121
- export class AuthService {
1122
- constructor(
1123
- private readonly seeder: ContractXRolePermissionSeeder,
1124
- private readonly authorizationService: ContractXAuthorizationService,
1125
- private readonly validationService: ContractXValidationService,
1126
- ) {}
1127
-
1128
- async initializeSystem() {
1129
- // Seed ODS roles and permissions
1130
- await this.seeder.seed();
1131
- }
1132
-
1133
- async validateUserAccess(user: JwtPayload, requiredPermission: string) {
1134
- return this.authorizationService.checkPermission(user, requiredPermission);
1135
- }
1136
-
1137
- async getUserAccessMatrix(user: JwtPayload) {
1138
- return this.authorizationService.generateAccessMatrix(user);
1139
- }
70
+ @RequirePermissions('contracts.create')
71
+ create() { /* ... */ }
1140
72
  }
1141
73
  ```
1142
74
 
1143
- ## 🌍 Environment Variables
1144
-
1145
- | Variable | Description | Default | Required |
1146
- |----------|-------------|---------|----------|
1147
- | `JWT_SECRET` | JWT signing secret | - | ✅ Yes |
1148
- | `JWT_ISSUER` | Token issuer | `contractx-api` | No |
1149
- | `JWT_AUDIENCE` | Token audience | `contractx-users` | No |
1150
- | `JWT_EXPIRES_IN` | Token expiration | `15m` | No |
1151
- | `JWT_CLOCK_TOLERANCE` | Clock tolerance (seconds) | `0` | No |
1152
- | `ENABLE_GLOBAL_AUTH` | Apply auth guard globally | `false` | No |
1153
- | `ENABLE_GLOBAL_ROLES` | Apply roles guard globally | `false` | No |
1154
- | `ENABLE_GLOBAL_PERMISSIONS` | Apply permissions guard globally | `false` | No |
1155
- | `ENABLE_AUTH_LOGGING` | Enable security logging | `false` | No |
1156
- | `DISABLE_AUTH` | Disable authentication | `false` | No |
1157
-
1158
- ## 🔒 Security Best Practices
1159
-
1160
- ### 1. JWT Token Structure
1161
-
1162
- The package expects JWT tokens with this payload structure:
75
+ Override del eje lectura/escritura cuando la convención HTTP no aplica:
1163
76
 
1164
77
  ```typescript
1165
- interface JwtPayload {
1166
- sub: string; // User ID
1167
- role: string[]; // User roles
1168
- permissions: string[]; // User permissions
1169
- fullName: string; // User's full name
1170
- email?: string; // User's email
1171
- clientId?: string; // Organization/client ID
1172
- sessionId?: string; // Session tracking
1173
- iat?: number; // Issued at
1174
- exp?: number; // Expires at
1175
- }
1176
- ```
1177
-
1178
- ### 2. Environment Variables
1179
-
1180
- ```env
1181
- # Required
1182
- JWT_SECRET=your-256-bit-secret-key-here
1183
- JWT_ISSUER=your-api-name
1184
- JWT_AUDIENCE=your-api-users
1185
-
1186
- # Optional but recommended
1187
- JWT_EXPIRES_IN=15m
1188
- JWT_CLOCK_TOLERANCE=30
1189
-
1190
- # Security
1191
- ENABLE_AUTH_LOGGING=true
1192
- ```
1193
-
1194
- ### 3. Global Guards
78
+ import { PermissionWrites } from 'permissions-contractx';
1195
79
 
1196
- ```typescript
1197
- // For maximum security, enable global authentication
1198
- PermissionsContractXModule.register({
1199
- guards: {
1200
- enableGlobalAuth: true, // All routes require auth by default
1201
- enableGlobalRoles: false, // Only specific routes check roles
1202
- enableGlobalPermissions: false, // Only specific routes check permissions
1203
- },
1204
- })
80
+ // Un POST que en realidad solo consulta:
81
+ @Post('search')
82
+ @RequirePermissions('contracts.view_list')
83
+ @PermissionWrites(false)
84
+ search() { /* ... */ }
1205
85
  ```
1206
86
 
1207
- ## 🧪 Testing
1208
-
1209
- ### Unit Testing
1210
-
1211
- ```typescript
1212
- import { Test } from '@nestjs/testing';
1213
- import { JwtService } from '@nestjs/jwt';
1214
- import { JwtAuthGuard } from 'permissions-contractx';
1215
-
1216
- describe('Authentication', () => {
1217
- let guard: JwtAuthGuard;
1218
-
1219
- beforeEach(async () => {
1220
- const module = await Test.createTestingModule({
1221
- providers: [
1222
- JwtAuthGuard,
1223
- {
1224
- provide: JwtService,
1225
- useValue: { verifyAsync: jest.fn() },
1226
- },
1227
- // ... other providers
1228
- ],
1229
- }).compile();
1230
-
1231
- guard = module.get(JwtAuthGuard);
1232
- });
1233
-
1234
- it('should authenticate valid token', async () => {
1235
- // Test implementation
1236
- });
1237
- });
1238
- ```
1239
-
1240
- ### Integration Testing
1241
-
1242
- ```typescript
1243
- import * as request from 'supertest';
1244
-
1245
- describe('Protected Routes', () => {
1246
- it('should reject unauthenticated requests', () => {
1247
- return request(app.getHttpServer())
1248
- .get('/protected-route')
1249
- .expect(401);
1250
- });
1251
-
1252
- it('should allow authenticated requests', () => {
1253
- return request(app.getHttpServer())
1254
- .get('/protected-route')
1255
- .set('Authorization', `Bearer ${validToken}`)
1256
- .expect(200);
1257
- });
1258
- });
1259
- ```
1260
-
1261
- ## 🚀 Production Deployment
1262
-
1263
- ### Docker Support
1264
-
1265
- ```dockerfile
1266
- FROM node:18-alpine
1267
- WORKDIR /app
1268
- COPY package*.json ./
1269
- RUN npm ci --only=production
1270
- COPY . .
1271
- RUN npm run build
1272
-
1273
- ENV NODE_ENV=production
1274
- ENV JWT_SECRET=${JWT_SECRET}
1275
- ENV JWT_ISSUER=contractx-production
1276
-
1277
- EXPOSE 3000
1278
- CMD ["npm", "run", "start:prod"]
1279
- ```
1280
-
1281
- ### Environment Configuration
1282
-
1283
- ```yaml
1284
- # docker-compose.yml
1285
- version: '3.8'
1286
- services:
1287
- app:
1288
- build: .
1289
- environment:
1290
- - NODE_ENV=production
1291
- - JWT_SECRET=${JWT_SECRET}
1292
- - JWT_ISSUER=contractx-api
1293
- - JWT_AUDIENCE=contractx-users
1294
- - ENABLE_AUTH_LOGGING=true
1295
- ```
1296
-
1297
- ## 📖 Examples
1298
-
1299
- ### Complete Controller Example
1300
-
1301
- ```typescript
1302
- import {
1303
- Controller,
1304
- Get,
1305
- Post,
1306
- Put,
1307
- Delete,
1308
- Body,
1309
- Param,
1310
- UseGuards,
1311
- } from '@nestjs/common';
1312
- import {
1313
- JwtAuthGuard,
1314
- RolesGuard,
1315
- PermissionsGuard,
1316
- CurrentUser,
1317
- Roles,
1318
- RequirePermissions,
1319
- Public,
1320
- ClientOnly,
1321
- AdminOnly,
1322
- JwtPayload,
1323
- } from 'permissions-contractx';
1324
-
1325
- @Controller('contracts')
1326
- @UseGuards(JwtAuthGuard, RolesGuard, PermissionsGuard)
1327
- export class ContractsController {
1328
-
1329
- @Public()
1330
- @Get('health')
1331
- getHealth() {
1332
- return { status: 'OK' };
1333
- }
1334
-
1335
- @Get()
1336
- @RequirePermissions('contracts.read', 'contracts.filter')
1337
- async getContracts(@CurrentUser() user: JwtPayload) {
1338
- // User must have both read AND filter permissions
1339
- return { contracts: [], requestedBy: user.fullName };
1340
- }
1341
-
1342
- @Post()
1343
- @ClientOnly()
1344
- @RequirePermissions('contracts.create')
1345
- async createContract(
1346
- @Body() contractData: any,
1347
- @CurrentUser() user: JwtPayload,
1348
- ) {
1349
- // Only client-side users with create permission
1350
- return { id: '123', createdBy: user.sub };
1351
- }
1352
-
1353
- @Put(':id/approve')
1354
- @AdminOnly()
1355
- @RequirePermissions('contracts.approve')
1356
- async approveContract(
1357
- @Param('id') id: string,
1358
- @CurrentUser() user: JwtPayload,
1359
- ) {
1360
- // Only admins with approval permission
1361
- return { id, approvedBy: user.fullName, approvedAt: new Date() };
1362
- }
1363
-
1364
- @Delete(':id')
1365
- @Roles('superadmin')
1366
- async deleteContract(@Param('id') id: string) {
1367
- // Only superadmin can delete
1368
- return { message: `Contract ${id} deleted` };
1369
- }
1370
- }
1371
- ```
1372
-
1373
- ## 🤝 Contributing
1374
-
1375
- We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
1376
-
1377
- ## 📄 License
87
+ ## Modelo de cuatro estados (migración)
1378
88
 
1379
- This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
89
+ El `AuthorizationGuard` permite migrar cada endpoint de forma gradual mediante la variable de entorno de enforcement:
1380
90
 
1381
- ## 🆘 Support
91
+ 1. **legacy / solo-roles** — sin gateo por permisos (estado de partida).
92
+ 2. **observación** — el gateo por permisos evalúa y registra discrepancias, pero solo el rol-gate bloquea.
93
+ 3. **estricto** — ambos gates bloquean.
94
+ 4. **solo-permisos** — endpoint graduado, gateado únicamente por permisos.
1382
95
 
1383
- - 📧 Email: support@contractx.dev
1384
- - 🐛 Issues: [GitHub Issues](https://github.com/your-org/permissions-contractx/issues)
1385
- - 📚 Documentation: [Full Documentation](https://permissions-contractx.dev)
96
+ Esto permite introducir el enforcement servicio por servicio sin romper el acceso existente. El principio rector es *fallar-seguro*: ante cualquier conflicto, gana lo más restrictivo.
1386
97
 
1387
- ## 📊 Package Stats
98
+ ## Versionado
1388
99
 
1389
- - **Zero Dependencies** (only peer dependencies)
1390
- - 🚀 **TypeScript First** - Built with TypeScript for TypeScript
1391
- - 📦 **Tree Shakeable** - Only import what you need
1392
- - 🔒 **Security Focused** - Built with security best practices
1393
- - 🧪 **Well Tested** - Comprehensive test coverage
100
+ Sigue [SemVer](https://semver.org/). La serie `1.x` es aditiva sobre el modelo ADR-004; los cambios que retiren la API antigua de autorización se reservan para una mayor (`2.0.0`).
1394
101
 
1395
- ---
102
+ ## Licencia
1396
103
 
1397
- Made with ❤️ by the ContractX Team
104
+ MIT