@flusys/nestjs-iam 4.1.1 → 5.0.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 (45) hide show
  1. package/README.md +87 -369
  2. package/cjs/config/message-keys.js +7 -49
  3. package/cjs/controllers/company-action-permission.controller.js +19 -18
  4. package/cjs/controllers/my-permission.controller.js +1 -4
  5. package/cjs/controllers/role-permission.controller.js +30 -20
  6. package/cjs/controllers/user-action-permission.controller.js +16 -11
  7. package/cjs/docs/iam-swagger.config.js +3 -2
  8. package/cjs/dtos/action.dto.js +0 -16
  9. package/cjs/dtos/permission.dto.js +4 -19
  10. package/cjs/dtos/role.dto.js +0 -16
  11. package/cjs/entities/action-base.entity.js +3 -8
  12. package/cjs/entities/permission-base.entity.js +1 -7
  13. package/cjs/entities/role-base.entity.js +1 -7
  14. package/cjs/services/action.service.js +1 -2
  15. package/cjs/services/permission.service.js +7 -14
  16. package/cjs/services/role.service.js +0 -1
  17. package/config/message-keys.d.ts +4 -84
  18. package/controllers/company-action-permission.controller.d.ts +3 -3
  19. package/controllers/role-permission.controller.d.ts +4 -4
  20. package/controllers/user-action-permission.controller.d.ts +3 -3
  21. package/docs/iam-swagger.config.d.ts +1 -1
  22. package/dtos/action.dto.d.ts +0 -2
  23. package/dtos/permission.dto.d.ts +1 -3
  24. package/dtos/role.dto.d.ts +0 -2
  25. package/entities/action-base.entity.d.ts +0 -1
  26. package/entities/permission-base.entity.d.ts +0 -1
  27. package/entities/role-base.entity.d.ts +0 -1
  28. package/fesm/config/message-keys.js +7 -44
  29. package/fesm/controllers/company-action-permission.controller.js +22 -21
  30. package/fesm/controllers/my-permission.controller.js +2 -5
  31. package/fesm/controllers/role-permission.controller.js +33 -23
  32. package/fesm/controllers/user-action-permission.controller.js +19 -14
  33. package/fesm/docs/iam-swagger.config.js +3 -2
  34. package/fesm/dtos/action.dto.js +0 -16
  35. package/fesm/dtos/permission.dto.js +4 -19
  36. package/fesm/dtos/role.dto.js +0 -16
  37. package/fesm/entities/action-base.entity.js +3 -8
  38. package/fesm/entities/permission-base.entity.js +1 -7
  39. package/fesm/entities/role-base.entity.js +1 -7
  40. package/fesm/services/action.service.js +1 -2
  41. package/fesm/services/permission.service.js +7 -14
  42. package/fesm/services/role.service.js +0 -1
  43. package/interfaces/action.interface.d.ts +0 -1
  44. package/interfaces/role.interface.d.ts +0 -1
  45. package/package.json +3 -3
package/README.md CHANGED
@@ -1,84 +1,9 @@
1
1
  # @flusys/nestjs-iam
2
2
 
3
- > Identity and Access Management for NestJS — RBAC, DIRECT, and FULL permission modes with hierarchical actions, company scoping, intelligent 1-hour caching, and multi-tenant support.
3
+ Identity and Access Management for NestJS — RBAC, DIRECT, and FULL permission modes with caching and multi-tenant support.
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/@flusys/nestjs-iam.svg)](https://www.npmjs.com/package/@flusys/nestjs-iam)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
- [![NestJS](https://img.shields.io/badge/NestJS-11.x-red.svg)](https://nestjs.com/)
8
- [![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue.svg)](https://www.typescriptlang.org/)
9
- [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D18.x-green.svg)](https://nodejs.org/)
10
-
11
- ---
12
-
13
- ## Table of Contents
14
-
15
- - [Overview](#overview)
16
- - [Features](#features)
17
- - [Permission Modes](#permission-modes)
18
- - [Compatibility](#compatibility)
19
- - [Installation](#installation)
20
- - [Quick Start](#quick-start)
21
- - [Module Registration](#module-registration)
22
- - [forRoot (Sync)](#forroot-sync)
23
- - [forRootAsync (Factory)](#forrootasync-factory)
24
- - [Configuration Reference](#configuration-reference)
25
- - [Feature Toggles](#feature-toggles)
26
- - [API Endpoints](#api-endpoints)
27
- - [Entities](#entities)
28
- - [Permission Cache](#permission-cache)
29
- - [Exported Services](#exported-services)
30
- - [Using Permissions in Controllers](#using-permissions-in-controllers)
31
- - [Action Types](#action-types)
32
- - [Hierarchical Actions](#hierarchical-actions)
33
- - [Troubleshooting](#troubleshooting)
34
- - [License](#license)
35
-
36
- ---
37
-
38
- ## Overview
39
-
40
- `@flusys/nestjs-iam` is a complete Identity and Access Management system. It supports three permission modes — RBAC (role-based), DIRECT (user-to-action), and FULL (both simultaneously). Permissions are cached per user+company+branch for 1 hour and automatically invalidated on changes.
41
-
42
- The module is fully independent from `nestjs-auth` — it only depends on `nestjs-core` and `nestjs-shared`. Integration with auth is done via the `USER_ENRICHER` provider interface in `nestjs-auth`.
43
-
44
- ---
45
-
46
- ## Features
47
-
48
- - **RBAC Mode** — Users inherit permissions through Roles → Actions assignments
49
- - **DIRECT Mode** — Users are assigned directly to Actions without roles
50
- - **FULL Mode** — Both RBAC and DIRECT active simultaneously (union of permissions)
51
- - **Hierarchical Actions** — Actions form parent-child trees with `parentId`; tree queries supported
52
- - **Action Types** — `BACKEND` (API guard only), `FRONTEND` (returned to client), `BOTH`
53
- - **Company Whitelist** — `COMPANY_ACTION` records control which actions are available per company
54
- - **Permission Logic** — Complex AND/OR nested logic on `@RequirePermission()` decorator
55
- - **1-Hour Cache** — Permissions cached per `userId + companyId + branchId`, auto-invalidated
56
- - **Menu Management** — Navigation menu items with per-item action requirements
57
- - **Multi-tenant** — Isolated DataSource Provider per request
58
-
59
- ---
60
-
61
- ## Permission Modes
62
-
63
- | Mode | How it works | Controllers loaded |
64
- |------|--------------|--------------------|
65
- | `RBAC` | User → Role → Action | ActionController, RoleController, RolePermissionController, MyPermissionController |
66
- | `DIRECT` | User → Action (direct) | ActionController, UserActionPermissionController, MyPermissionController |
67
- | `FULL` | RBAC + DIRECT (union) | All above controllers |
68
-
69
- `FULL` is the default when `permissionMode` is not specified.
70
-
71
- ---
72
-
73
- ## Compatibility
74
-
75
- | Package | Version |
76
- |---------|---------|
77
- | `@flusys/nestjs-core` | `^4.0.0` |
78
- | `@flusys/nestjs-shared` | `^4.0.0` |
79
- | `@nestjs/core` | `^11.0.0` |
80
- | `typeorm` | `^0.3.0` |
81
- | Node.js | `>= 18.x` |
82
7
 
83
8
  ---
84
9
 
@@ -88,14 +13,13 @@ The module is fully independent from `nestjs-auth` — it only depends on `nestj
88
13
  npm install @flusys/nestjs-iam @flusys/nestjs-shared @flusys/nestjs-core
89
14
  ```
90
15
 
91
- ---
16
+ ## 1. Register the Module
92
17
 
93
- ## Quick Start
18
+ ### Synchronous
94
19
 
95
- ### RBAC Mode
20
+ #### Mode 1: Single Database
96
21
 
97
22
  ```typescript
98
- import { Module } from '@nestjs/common';
99
23
  import { IAMModule } from '@flusys/nestjs-iam';
100
24
 
101
25
  @Module({
@@ -106,13 +30,13 @@ import { IAMModule } from '@flusys/nestjs-iam';
106
30
  bootstrapAppConfig: {
107
31
  databaseMode: 'single',
108
32
  enableCompanyFeature: false,
109
- permissionMode: 'RBAC',
33
+ permissionMode: 'RBAC', // 'RBAC' | 'DIRECT' | 'FULL' (default: 'FULL')
110
34
  },
111
35
  config: {
112
36
  defaultDatabaseConfig: {
113
- type: 'postgres',
37
+ type: 'mysql',
114
38
  host: process.env.DB_HOST,
115
- port: Number(process.env.DB_PORT ?? 5432),
39
+ port: Number(process.env.DB_PORT ?? 3306),
116
40
  username: process.env.DB_USER,
117
41
  password: process.env.DB_PASSWORD,
118
42
  database: process.env.DB_NAME,
@@ -124,45 +48,41 @@ import { IAMModule } from '@flusys/nestjs-iam';
124
48
  export class AppModule {}
125
49
  ```
126
50
 
127
- ### FULL Mode with Company Feature
51
+ #### Mode 2: Multi-Tenant
128
52
 
129
53
  ```typescript
130
54
  IAMModule.forRoot({
131
55
  global: true,
132
56
  includeController: true,
133
57
  bootstrapAppConfig: {
134
- databaseMode: 'single',
135
- enableCompanyFeature: true, // Company/branch scoping
136
- permissionMode: 'FULL', // RBAC + DIRECT
58
+ databaseMode: 'multi-tenant',
59
+ enableCompanyFeature: true,
60
+ permissionMode: 'FULL',
137
61
  },
138
- config: { defaultDatabaseConfig: { /* ... */ } },
139
- })
140
- ```
141
-
142
- ---
143
-
144
- ## Module Registration
145
-
146
- ### forRoot (Sync)
147
-
148
- ```typescript
149
- IAMModule.forRoot({
150
- global?: boolean; // Default: false
151
- includeController?: boolean; // Default: false
152
- bootstrapAppConfig?: {
153
- databaseMode: 'single' | 'multi-tenant';
154
- enableCompanyFeature: boolean;
155
- permissionMode?: 'FULL' | 'RBAC' | 'DIRECT'; // Default: 'FULL'
156
- };
157
- config?: IIAMModuleConfig;
158
- })
62
+ config: {
63
+ tenantDefaultDatabaseConfig: {
64
+ type: 'mysql',
65
+ host: process.env.TENANT_DB_HOST,
66
+ port: Number(process.env.TENANT_DB_PORT ?? 3306),
67
+ username: process.env.TENANT_DB_USER,
68
+ password: process.env.TENANT_DB_PASSWORD,
69
+ database: process.env.TENANT_DB_NAME,
70
+ },
71
+ tenants: [
72
+ { id: 'tenant-a', database: 'tenant_a_db', permissionMode: 'FULL' },
73
+ { id: 'tenant-b', database: 'tenant_b_db', permissionMode: 'RBAC' },
74
+ ],
75
+ },
76
+ });
159
77
  ```
160
78
 
161
- ### forRootAsync (Factory)
79
+ ### Asynchronous (with ConfigService)
162
80
 
163
81
  ```typescript
164
- import { ConfigService } from '@nestjs/config';
82
+ import { ConfigModule, ConfigService } from '@nestjs/config';
83
+ import { IAMModule, ITenantDatabaseConfig } from '@flusys/nestjs-iam';
165
84
 
85
+ // Single database
166
86
  IAMModule.forRootAsync({
167
87
  global: true,
168
88
  includeController: true,
@@ -172,9 +92,9 @@ IAMModule.forRootAsync({
172
92
  permissionMode: 'FULL',
173
93
  },
174
94
  imports: [ConfigModule],
175
- useFactory: async (configService: ConfigService) => ({
95
+ useFactory: (configService: ConfigService) => ({
176
96
  defaultDatabaseConfig: {
177
- type: 'postgres',
97
+ type: 'mysql',
178
98
  host: configService.get('DB_HOST'),
179
99
  port: configService.get<number>('DB_PORT'),
180
100
  username: configService.get('DB_USER'),
@@ -183,308 +103,106 @@ IAMModule.forRootAsync({
183
103
  },
184
104
  }),
185
105
  inject: [ConfigService],
186
- })
187
- ```
188
-
189
- ---
190
-
191
- ## Configuration Reference
192
-
193
- ```typescript
194
- interface IIAMModuleConfig extends IDataSourceServiceOptions {
195
- // All IAM behaviour is controlled via bootstrapAppConfig.
196
- // IDataSourceServiceOptions provides:
197
- // defaultDatabaseConfig?: IDatabaseConfig
198
- // tenantDefaultDatabaseConfig?: IDatabaseConfig
199
- // tenants?: ITenantDatabaseConfig[]
200
- }
201
- ```
106
+ });
202
107
 
203
- Bootstrap configuration is fixed at startup and cannot change at runtime:
204
-
205
- ```typescript
206
- interface IBootstrapAppConfig {
207
- databaseMode: 'single' | 'multi-tenant';
208
- enableCompanyFeature: boolean;
209
- permissionMode?: 'FULL' | 'RBAC' | 'DIRECT'; // Default: 'FULL'
210
- }
108
+ // Multi-tenant
109
+ IAMModule.forRootAsync({
110
+ global: true,
111
+ includeController: true,
112
+ bootstrapAppConfig: {
113
+ databaseMode: 'multi-tenant',
114
+ enableCompanyFeature: true,
115
+ permissionMode: 'FULL',
116
+ },
117
+ imports: [ConfigModule],
118
+ useFactory: (configService: ConfigService) => ({
119
+ tenantDefaultDatabaseConfig: {
120
+ type: 'mysql',
121
+ host: configService.get('TENANT_DB_HOST'),
122
+ port: configService.get<number>('TENANT_DB_PORT'),
123
+ username: configService.get('TENANT_DB_USER'),
124
+ password: configService.get('TENANT_DB_PASSWORD'),
125
+ database: configService.get('TENANT_DB_NAME'),
126
+ },
127
+ tenants: configService.get<ITenantDatabaseConfig[]>('TENANTS'),
128
+ }),
129
+ inject: [ConfigService],
130
+ });
211
131
  ```
212
132
 
213
- ---
214
-
215
- ## Feature Toggles
216
-
217
- | Feature | Config | Default | Effect |
218
- |---------|--------|---------|--------|
219
- | Company scoping | `enableCompanyFeature: true` | `false` | Uses `*WithCompany` entity variants; adds `companyId`/`branchId` to permission assignments; registers `CompanyActionPermissionController` |
220
- | RBAC | `permissionMode: 'RBAC'` | — | Loads Role, RoleAction, UserRole entities; registers Role controllers |
221
- | DIRECT | `permissionMode: 'DIRECT'` | — | Loads UserAction entities; registers UserActionPermissionController |
222
- | FULL | `permissionMode: 'FULL'` | default | All entities and controllers |
223
-
224
- **Controller loading by mode:**
225
-
226
- | Mode | + `enableCompanyFeature` | Additional controller |
227
- |------|--------------------------|-----------------------|
228
- | `RBAC` | true | `CompanyActionPermissionController` |
229
- | `DIRECT` | true | `CompanyActionPermissionController` |
230
- | `FULL` | true | `CompanyActionPermissionController` |
231
-
232
- ---
233
-
234
- ## API Endpoints
235
-
236
- All endpoints use **POST** and require JWT authentication.
237
-
238
- ### Actions — `POST /iam/action/*`
239
-
240
- | Endpoint | Permission | Description |
241
- |----------|-----------|-------------|
242
- | `POST /iam/action/insert` | `action.create` | Create an action |
243
- | `POST /iam/action/get-all` | `action.read` | List all actions |
244
- | `POST /iam/action/get/:id` | `action.read` | Get action by ID |
245
- | `POST /iam/action/get-tree` | `action.read` | Get hierarchical action tree |
246
- | `POST /iam/action/update` | `action.update` | Update action |
247
- | `POST /iam/action/delete` | `action.delete` | Delete action |
248
-
249
- ### Roles — `POST /iam/role/*` *(RBAC and FULL modes)*
250
-
251
- | Endpoint | Permission | Description |
252
- |----------|-----------|-------------|
253
- | `POST /iam/role/insert` | `role.create` | Create a role |
254
- | `POST /iam/role/get-all` | `role.read` | List all roles |
255
- | `POST /iam/role/get/:id` | `role.read` | Get role by ID |
256
- | `POST /iam/role/update` | `role.update` | Update role |
257
- | `POST /iam/role/delete` | `role.delete` | Delete role |
258
-
259
- ### Role Permissions — `POST /iam/role-permission/*` *(RBAC and FULL modes)*
260
-
261
- | Endpoint | Permission | Description |
262
- |----------|-----------|-------------|
263
- | `POST /iam/role-permission/assign` | `role.update` | Assign action to role |
264
- | `POST /iam/role-permission/remove` | `role.update` | Remove action from role |
265
- | `POST /iam/role-permission/get-by-role` | `role.read` | Get all actions for a role |
266
- | `POST /iam/role-permission/assign-user-role` | `role.update` | Assign role to user |
267
- | `POST /iam/role-permission/remove-user-role` | `role.update` | Remove role from user |
268
-
269
- ### User Action Permissions — `POST /iam/user-action-permission/*` *(DIRECT and FULL modes)*
133
+ ## 2. Register Entities in TypeORM
270
134
 
271
- | Endpoint | Permission | Description |
272
- |----------|-----------|-------------|
273
- | `POST /iam/user-action-permission/assign` | `action.update` | Directly assign action to user |
274
- | `POST /iam/user-action-permission/remove` | `action.update` | Remove direct action from user |
275
- | `POST /iam/user-action-permission/get-by-user` | `action.read` | Get all direct permissions for user |
276
-
277
- ### Company Action Whitelist — `POST /iam/company-action/*` *(`enableCompanyFeature: true`)*
278
-
279
- | Endpoint | Permission | Description |
280
- |----------|-----------|-------------|
281
- | `POST /iam/company-action/assign` | `action.update` | Add action to company whitelist |
282
- | `POST /iam/company-action/remove` | `action.update` | Remove action from whitelist |
283
- | `POST /iam/company-action/get-by-company` | `action.read` | Get company's available actions |
284
-
285
- ### My Permissions — `POST /iam/my-permission/*`
286
-
287
- | Endpoint | Auth | Description |
288
- |----------|------|-------------|
289
- | `POST /iam/my-permission/get-all` | JWT | Get all permissions for current user |
290
-
291
- ---
292
-
293
- ## Entities
294
-
295
- ### Core Entities (always registered)
296
-
297
- | Entity | Table | Description |
298
- |--------|-------|-------------|
299
- | `Action` | `iam_action` | Permission actions (e.g., `product.create`) with type and hierarchy |
300
- | `Menu` | `iam_menu` | Navigation menu items with optional action requirement |
301
-
302
- ### RBAC Entities (`permissionMode: 'RBAC'` or `'FULL'`)
303
-
304
- | Entity | Table | Description |
305
- |--------|-------|-------------|
306
- | `Role` | `iam_role` | Role definitions |
307
- | `RoleAction` | `iam_role_action` | Role → Action assignments |
308
- | `UserRole` | `iam_user_role` | User → Role assignments |
309
-
310
- ### DIRECT Entities (`permissionMode: 'DIRECT'` or `'FULL'`)
311
-
312
- | Entity | Table | Description |
313
- |--------|-------|-------------|
314
- | `UserAction` | `iam_user_action` | Direct User → Action assignments |
315
-
316
- ### Company Feature Entities (`enableCompanyFeature: true`)
317
-
318
- All above entities get `WithCompany` variants with `companyId` and `branchId` columns, plus:
319
-
320
- | Entity | Table | Description |
321
- |--------|-------|-------------|
322
- | `CompanyAction` | `iam_company_action` | Actions whitelisted per company |
323
-
324
- #### Register Entities in TypeORM
135
+ Use `getIAMEntitiesByConfig()` arguments must match `bootstrapAppConfig`:
325
136
 
326
137
  ```typescript
327
- import { IAMModule } from '@flusys/nestjs-iam';
138
+ import { getIAMEntitiesByConfig } from '@flusys/nestjs-iam/entities';
328
139
 
329
140
  TypeOrmModule.forRoot({
330
141
  entities: [
331
- ...IAMModule.getEntities({
332
- enableCompanyFeature: true,
333
- permissionMode: 'FULL',
334
- }),
142
+ ...getIAMEntitiesByConfig(
143
+ true, // enableCompanyFeature
144
+ 'FULL', // permissionMode: 'FULL' | 'RBAC' | 'DIRECT'
145
+ ),
335
146
  ],
336
- })
147
+ });
337
148
  ```
338
149
 
339
- ---
340
-
341
- ## Permission Cache
342
-
343
- Permissions are cached with the key `iam:permissions:{userId}:{companyId}:{branchId}` for **1 hour**. The cache is automatically invalidated when:
344
-
345
- - A role is assigned to or removed from a user
346
- - An action is directly assigned to or removed from a user
347
- - A role's action set is modified
348
- - The company action whitelist changes
349
-
350
- Cache uses `HybridCache` (in-memory + Redis). If Redis is not configured, in-memory cache is used.
351
-
352
- ---
353
-
354
- ## Exported Services
150
+ ## 3. Protect Endpoints
355
151
 
356
- | Service | Description |
357
- |---------|-------------|
358
- | `PermissionService` | Resolve and check permissions for a user |
359
- | `ActionService` | Action CRUD and tree queries |
360
- | `RoleService` | Role CRUD *(RBAC/FULL modes)* |
361
- | `UserActionPermissionService` | Direct user-action assignment *(DIRECT/FULL modes)* |
362
- | `IAMConfigService` | Exposes runtime config and feature flags |
363
- | `IAMDataSourceProvider` | Dynamic DataSource resolution per request |
364
-
365
- ---
366
-
367
- ## Using Permissions in Controllers
368
-
369
- ### Using the Decorator
152
+ All FLUSYS endpoints use POST. Apply `JwtAuthGuard` and `@RequirePermission` from `nestjs-shared`:
370
153
 
371
154
  ```typescript
372
- import { RequirePermission } from '@flusys/nestjs-shared/decorators';
373
155
  import { JwtAuthGuard } from '@flusys/nestjs-shared/guards';
156
+ import { RequirePermission, CurrentUser } from '@flusys/nestjs-shared/decorators';
157
+ import { ILoggedUserInfo } from '@flusys/nestjs-shared/interfaces';
374
158
 
375
159
  @UseGuards(JwtAuthGuard)
376
160
  @Controller('products')
377
161
  export class ProductController {
378
162
  @Post('insert')
379
163
  @RequirePermission('product.create')
380
- async create() { }
164
+ async create(@CurrentUser() user: ILoggedUserInfo) {
165
+ /* ... */
166
+ }
381
167
 
382
168
  @Post('get-all')
383
169
  @RequirePermission('product.read')
384
- async getAll() { }
170
+ async getAll(@CurrentUser() user: ILoggedUserInfo) {
171
+ /* ... */
172
+ }
385
173
  }
386
174
  ```
387
175
 
388
- ### Checking Permissions Programmatically
176
+ Wildcard matching is supported: `@RequirePermission('product.*')` matches any action whose code starts with `product.`.
177
+
178
+ ## 5. Programmatic Permission Check
179
+
180
+ Inject `PermissionService` (use `@Inject()` — required for bundled code):
389
181
 
390
182
  ```typescript
391
183
  import { PermissionService } from '@flusys/nestjs-iam';
392
184
 
393
185
  @Injectable()
394
186
  export class ProductService {
395
- constructor(
396
- @Inject(PermissionService) private readonly permissionService: PermissionService,
397
- ) {}
187
+ constructor(@Inject(PermissionService) private readonly permissionService: PermissionService) {}
398
188
 
399
- async canUserCreate(userId: string, companyId?: string): Promise<boolean> {
189
+ async canCreate(userId: string, companyId?: string): Promise<boolean> {
400
190
  return this.permissionService.hasPermission(userId, 'product.create', companyId);
401
191
  }
402
-
403
- async getUserPermissions(userId: string, companyId?: string): Promise<string[]> {
404
- return this.permissionService.getPermissions(userId, companyId);
405
- }
406
- }
407
- ```
408
-
409
- ---
410
-
411
- ## Action Types
412
-
413
- | Type | Description |
414
- |------|-------------|
415
- | `BACKEND` | Enforced by `PermissionGuard` on API requests only. Not returned to the frontend. |
416
- | `FRONTEND` | Returned to the frontend in permission lists. Not enforced server-side. |
417
- | `BOTH` | Enforced server-side AND returned to the frontend. |
418
-
419
- ```json
420
- POST /iam/action/insert
421
- {
422
- "name": "Create Product",
423
- "code": "product.create",
424
- "type": "BOTH",
425
- "parentId": null,
426
- "description": "Allows creating new products"
427
192
  }
428
193
  ```
429
194
 
430
- ---
431
-
432
- ## Hierarchical Actions
433
-
434
- Actions support parent-child relationships. Use `parentId` to create a tree:
435
-
436
- ```
437
- product (BOTH - parent)
438
- ├── product.read (BOTH - child)
439
- ├── product.create (BOTH - child)
440
- ├── product.update (BOTH - child)
441
- └── product.delete (BOTH - child)
442
- ```
443
-
444
- Get the full tree:
445
- ```json
446
- POST /iam/action/get-tree
447
- {}
448
- ```
449
-
450
- The wildcard `product.*` in `@RequirePermission('product.*')` matches any action whose code starts with `product.`.
451
-
452
- ---
453
-
454
- ## Troubleshooting
455
-
456
- **`PermissionGuard` always denies — user has correct role**
457
-
458
- Check that the IAM entities are included in your `TypeOrmModule` registration. Missing entities cause the permission query to return empty results.
459
-
460
- ---
461
-
462
- **Permission cache not invalidating after role change**
463
-
464
- Check that `REDIS_URL` is configured correctly. If multiple service instances share the same Redis, cache invalidation propagates automatically. In single-instance in-memory mode, invalidation only affects the current process.
465
-
466
- ---
467
-
468
- **`No metadata for entity` on startup**
469
-
470
- Use `IAMModule.getEntities()` with both `enableCompanyFeature` and `permissionMode` matching your `bootstrapAppConfig`:
471
-
472
- ```typescript
473
- ...IAMModule.getEntities({ enableCompanyFeature: true, permissionMode: 'FULL' })
474
- ```
475
-
476
- ---
195
+ ## Exported Services
477
196
 
478
- **Company action whitelist blocks all permissions**
197
+ | Service | Scope | Description |
198
+ | ------------------- | ------- | -------------------------------------------------------------- |
199
+ | `PermissionService` | REQUEST | `hasPermission()`, `getUserPermissions()`, `getRolesForUser()` |
200
+ | |
479
201
 
480
- When `enableCompanyFeature: true`, a user's permissions are intersected with the company's action whitelist. If the whitelist is empty, the user has no permissions. Seed the whitelist using `POST /iam/company-action/assign`.
202
+ All constructor injections need explicit `@Inject()` TypeScript metadata is lost during esbuild bundling.
481
203
 
482
204
  ---
483
205
 
484
206
  ## License
485
207
 
486
208
  MIT © FLUSYS
487
-
488
- ---
489
-
490
- > Part of the **FLUSYS** framework — a full-stack monorepo powering Angular 21 + NestJS 11 applications.
@@ -19,18 +19,12 @@ _export(exports, {
19
19
  get IAM_MODE_MESSAGES () {
20
20
  return IAM_MODE_MESSAGES;
21
21
  },
22
- get IAM_MODULE_MESSAGES () {
23
- return IAM_MODULE_MESSAGES;
24
- },
25
22
  get MY_PERMISSION_MESSAGES () {
26
23
  return MY_PERMISSION_MESSAGES;
27
24
  },
28
25
  get PERMISSION_OPERATION_MESSAGES () {
29
26
  return PERMISSION_OPERATION_MESSAGES;
30
27
  },
31
- get ROLE_MESSAGES () {
32
- return ROLE_MESSAGES;
33
- },
34
28
  get ROLE_PERMISSION_MESSAGES () {
35
29
  return ROLE_PERMISSION_MESSAGES;
36
30
  },
@@ -39,43 +33,22 @@ _export(exports, {
39
33
  }
40
34
  });
41
35
  const ACTION_MESSAGES = {
42
- CREATE_SUCCESS: 'action.create.success',
43
- CREATE_MANY_SUCCESS: 'action.create.many.success',
44
- GET_SUCCESS: 'action.get.success',
45
- GET_ALL_SUCCESS: 'action.get.all.success',
46
- UPDATE_SUCCESS: 'action.update.success',
47
- UPDATE_MANY_SUCCESS: 'action.update.many.success',
48
- DELETE_SUCCESS: 'action.delete.success',
49
- RESTORE_SUCCESS: 'action.restore.success',
50
- NOT_FOUND: 'action.not.found'
36
+ GET_ALL_SUCCESS: 'action.get.all.success'
51
37
  };
52
- const ROLE_MESSAGES = {
53
- CREATE_SUCCESS: 'role.create.success',
54
- CREATE_MANY_SUCCESS: 'role.create.many.success',
55
- GET_SUCCESS: 'role.get.success',
56
- GET_ALL_SUCCESS: 'role.get.all.success',
57
- UPDATE_SUCCESS: 'role.update.success',
58
- UPDATE_MANY_SUCCESS: 'role.update.many.success',
59
- DELETE_SUCCESS: 'role.delete.success',
60
- RESTORE_SUCCESS: 'role.restore.success',
61
- NOT_FOUND: 'role.not.found'
38
+ const PERMISSION_OPERATION_MESSAGES = {
39
+ PROCESS_SUCCESS: 'permission.process.success',
40
+ ALREADY_EXISTS: 'permission.already.exists',
41
+ USER_REQUIRED: 'permission.user.required'
62
42
  };
63
43
  const ROLE_PERMISSION_MESSAGES = {
64
- GET_SUCCESS: 'role.permission.get.success',
65
- ASSIGN_SUCCESS: 'role.permission.assign.success',
66
44
  ACTIONS_SUCCESS: 'role.permission.actions.success',
67
- USERS_SUCCESS: 'role.permission.users.success',
68
45
  USER_ROLES_SUCCESS: 'role.permission.user.roles.success'
69
46
  };
70
47
  const USER_ACTION_PERMISSION_MESSAGES = {
71
- GET_SUCCESS: 'user.action.permission.get.success',
72
- ASSIGN_SUCCESS: 'user.action.permission.assign.success',
73
- REVOKE_SUCCESS: 'user.action.permission.revoke.success'
48
+ GET_SUCCESS: 'user.action.permission.get.success'
74
49
  };
75
50
  const COMPANY_ACTION_PERMISSION_MESSAGES = {
76
- GET_SUCCESS: 'company.action.permission.get.success',
77
- ASSIGN_SUCCESS: 'company.action.permission.assign.success',
78
- REVOKE_SUCCESS: 'company.action.permission.revoke.success'
51
+ GET_SUCCESS: 'company.action.permission.get.success'
79
52
  };
80
53
  const MY_PERMISSION_MESSAGES = {
81
54
  GET_SUCCESS: 'my.permission.get.success'
@@ -85,18 +58,3 @@ const IAM_MODE_MESSAGES = {
85
58
  RBAC_MODE_UNAVAILABLE: 'iam.rbac.mode.unavailable',
86
59
  ROLE_ASSIGNMENT_UNAVAILABLE: 'iam.role.assignment.unavailable'
87
60
  };
88
- const PERMISSION_OPERATION_MESSAGES = {
89
- PROCESS_SUCCESS: 'permission.process.success',
90
- ALREADY_EXISTS: 'permission.already.exists',
91
- USER_REQUIRED: 'permission.user.required'
92
- };
93
- const IAM_MODULE_MESSAGES = {
94
- ACTION: ACTION_MESSAGES,
95
- ROLE: ROLE_MESSAGES,
96
- ROLE_PERMISSION: ROLE_PERMISSION_MESSAGES,
97
- USER_ACTION_PERMISSION: USER_ACTION_PERMISSION_MESSAGES,
98
- COMPANY_ACTION_PERMISSION: COMPANY_ACTION_PERMISSION_MESSAGES,
99
- MY_PERMISSION: MY_PERMISSION_MESSAGES,
100
- IAM_MODE: IAM_MODE_MESSAGES,
101
- PERMISSION_OPERATION: PERMISSION_OPERATION_MESSAGES
102
- };