@flusys/nestjs-iam 4.0.1 → 4.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.
- package/README.md +306 -992
- package/controllers/action.controller.d.ts +5 -0
- package/controllers/role.controller.d.ts +5 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,55 +1,84 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @flusys/nestjs-iam
|
|
2
2
|
|
|
3
|
-
>
|
|
4
|
-
|
|
5
|
-
|
|
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.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@flusys/nestjs-iam)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
[](https://nestjs.com/)
|
|
8
|
+
[](https://www.typescriptlang.org/)
|
|
9
|
+
[](https://nodejs.org/)
|
|
10
|
+
|
|
11
|
+
---
|
|
6
12
|
|
|
7
13
|
## Table of Contents
|
|
8
14
|
|
|
9
15
|
- [Overview](#overview)
|
|
16
|
+
- [Features](#features)
|
|
17
|
+
- [Permission Modes](#permission-modes)
|
|
18
|
+
- [Compatibility](#compatibility)
|
|
10
19
|
- [Installation](#installation)
|
|
11
|
-
- [
|
|
12
|
-
- [
|
|
13
|
-
- [
|
|
14
|
-
- [
|
|
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)
|
|
15
27
|
- [Entities](#entities)
|
|
16
|
-
- [Permission
|
|
17
|
-
- [
|
|
18
|
-
- [
|
|
19
|
-
- [
|
|
20
|
-
- [
|
|
21
|
-
- [
|
|
22
|
-
- [
|
|
23
|
-
- [Best Practices](#best-practices)
|
|
24
|
-
- [DTOs Reference](#dtos-reference)
|
|
25
|
-
- [API Reference](#api-reference)
|
|
26
|
-
- [Package Architecture](#package-architecture)
|
|
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)
|
|
27
35
|
|
|
28
36
|
---
|
|
29
37
|
|
|
30
38
|
## Overview
|
|
31
39
|
|
|
32
|
-
`@flusys/nestjs-iam`
|
|
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.
|
|
33
41
|
|
|
34
|
-
-
|
|
35
|
-
- **Action Types** - BACKEND (cached for guards), FRONTEND (returned to UI), BOTH
|
|
36
|
-
- **RBAC** - Role-Based Access Control with validation
|
|
37
|
-
- **DIRECT** - Direct user-to-action assignments with validation
|
|
38
|
-
- **Permission Mode Validation** - Throws `BadRequestException` when using wrong mode
|
|
39
|
-
- **Company/Branch Scoping** - Multi-tenant permissions with three-level granularity
|
|
40
|
-
- **Tenant-Aware Caching** - Automatic cache invalidation with multi-tenant support
|
|
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`.
|
|
41
43
|
|
|
42
|
-
|
|
44
|
+
---
|
|
43
45
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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` |
|
|
53
82
|
|
|
54
83
|
---
|
|
55
84
|
|
|
@@ -61,32 +90,33 @@ npm install @flusys/nestjs-iam @flusys/nestjs-shared @flusys/nestjs-core
|
|
|
61
90
|
|
|
62
91
|
---
|
|
63
92
|
|
|
64
|
-
##
|
|
93
|
+
## Quick Start
|
|
65
94
|
|
|
66
|
-
###
|
|
95
|
+
### RBAC Mode
|
|
67
96
|
|
|
68
97
|
```typescript
|
|
69
|
-
import {
|
|
98
|
+
import { Module } from '@nestjs/common';
|
|
70
99
|
import { IAMModule } from '@flusys/nestjs-iam';
|
|
71
100
|
|
|
72
101
|
@Module({
|
|
73
102
|
imports: [
|
|
74
|
-
AuthModule.forRoot({
|
|
75
|
-
includeIAM: true, // Required for single-tenant mode
|
|
76
|
-
bootstrapAppConfig: {
|
|
77
|
-
databaseMode: 'single',
|
|
78
|
-
enableCompanyFeature: true,
|
|
79
|
-
},
|
|
80
|
-
config: { /* auth config */ },
|
|
81
|
-
}),
|
|
82
|
-
|
|
83
103
|
IAMModule.forRoot({
|
|
84
104
|
global: true,
|
|
85
105
|
includeController: true,
|
|
86
106
|
bootstrapAppConfig: {
|
|
87
107
|
databaseMode: 'single',
|
|
88
|
-
enableCompanyFeature:
|
|
89
|
-
permissionMode: '
|
|
108
|
+
enableCompanyFeature: false,
|
|
109
|
+
permissionMode: 'RBAC',
|
|
110
|
+
},
|
|
111
|
+
config: {
|
|
112
|
+
defaultDatabaseConfig: {
|
|
113
|
+
type: 'postgres',
|
|
114
|
+
host: process.env.DB_HOST,
|
|
115
|
+
port: Number(process.env.DB_PORT ?? 5432),
|
|
116
|
+
username: process.env.DB_USER,
|
|
117
|
+
password: process.env.DB_PASSWORD,
|
|
118
|
+
database: process.env.DB_NAME,
|
|
119
|
+
},
|
|
90
120
|
},
|
|
91
121
|
}),
|
|
92
122
|
],
|
|
@@ -94,1083 +124,367 @@ import { IAMModule } from '@flusys/nestjs-iam';
|
|
|
94
124
|
export class AppModule {}
|
|
95
125
|
```
|
|
96
126
|
|
|
97
|
-
###
|
|
127
|
+
### FULL Mode with Company Feature
|
|
98
128
|
|
|
99
129
|
```typescript
|
|
100
|
-
IAMModule.
|
|
130
|
+
IAMModule.forRoot({
|
|
101
131
|
global: true,
|
|
102
132
|
includeController: true,
|
|
103
133
|
bootstrapAppConfig: {
|
|
104
134
|
databaseMode: 'single',
|
|
105
|
-
enableCompanyFeature: true,
|
|
106
|
-
permissionMode: '
|
|
135
|
+
enableCompanyFeature: true, // Company/branch scoping
|
|
136
|
+
permissionMode: 'FULL', // RBAC + DIRECT
|
|
107
137
|
},
|
|
108
|
-
|
|
109
|
-
useFactory: (config: ConfigService) => ({
|
|
110
|
-
// IAM-specific options from config
|
|
111
|
-
}),
|
|
112
|
-
inject: [ConfigService],
|
|
138
|
+
config: { defaultDatabaseConfig: { /* ... */ } },
|
|
113
139
|
})
|
|
114
140
|
```
|
|
115
141
|
|
|
116
142
|
---
|
|
117
143
|
|
|
118
|
-
##
|
|
119
|
-
|
|
120
|
-
```typescript
|
|
121
|
-
// Injection Token
|
|
122
|
-
export const IAM_MODULE_OPTIONS = 'IAM_MODULE_OPTIONS';
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
---
|
|
126
|
-
|
|
127
|
-
## Core Concepts
|
|
128
|
-
|
|
129
|
-
### Actions
|
|
130
|
-
|
|
131
|
-
Actions represent permissions in the system:
|
|
132
|
-
|
|
133
|
-
| Field | Description |
|
|
134
|
-
|-------|-------------|
|
|
135
|
-
| `name` | Human-readable name |
|
|
136
|
-
| `code` | Unique identifier (e.g., `user.create`) |
|
|
137
|
-
| `actionType` | Category: `backend`, `frontend`, or `both` |
|
|
138
|
-
| `metadata` | Additional data (icon, routerLink for frontend) |
|
|
139
|
-
| `parentId` | Parent action for hierarchy |
|
|
140
|
-
| `serial` | Display order |
|
|
144
|
+
## Module Registration
|
|
141
145
|
|
|
142
|
-
###
|
|
146
|
+
### forRoot (Sync)
|
|
143
147
|
|
|
144
148
|
```typescript
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
| Field | Description |
|
|
157
|
-
|-------|-------------|
|
|
158
|
-
| `name` | Human-readable name |
|
|
159
|
-
| `description` | Role description |
|
|
160
|
-
| `companyId` | Company scope (when company feature enabled) |
|
|
161
|
-
| `isActive` | Active status |
|
|
162
|
-
|
|
163
|
-
### Permission Types
|
|
164
|
-
|
|
165
|
-
```typescript
|
|
166
|
-
enum IamPermissionType {
|
|
167
|
-
USER_ROLE = 'user_role', // User assigned to role
|
|
168
|
-
ROLE_ACTION = 'role_action', // Role has action
|
|
169
|
-
USER_ACTION = 'user_action', // User has action directly
|
|
170
|
-
COMPANY_ACTION = 'company_action', // Company action whitelist
|
|
171
|
-
}
|
|
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
|
+
})
|
|
172
159
|
```
|
|
173
160
|
|
|
174
|
-
###
|
|
175
|
-
|
|
176
|
-
Used for source/target type fields in permissions:
|
|
161
|
+
### forRootAsync (Factory)
|
|
177
162
|
|
|
178
163
|
```typescript
|
|
179
|
-
|
|
180
|
-
USER = 'user',
|
|
181
|
-
ROLE = 'role',
|
|
182
|
-
ACTION = 'action',
|
|
183
|
-
COMPANY = 'company',
|
|
184
|
-
}
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
### Permission Resolution Order
|
|
188
|
-
|
|
189
|
-
1. **Company-Action Whitelist** - Filter by company (if enabled)
|
|
190
|
-
2. **UserAction (DENY)** - Explicit denials take precedence
|
|
191
|
-
3. **UserAction (GRANT)** - Explicit grants
|
|
192
|
-
4. **UserRole -> RoleAction** - Inherited from roles
|
|
193
|
-
5. **Action Permission Logic** - Complex AND/OR rules
|
|
194
|
-
|
|
195
|
-
### Permission Cascade Deletion
|
|
164
|
+
import { ConfigService } from '@nestjs/config';
|
|
196
165
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
166
|
+
IAMModule.forRootAsync({
|
|
167
|
+
global: true,
|
|
168
|
+
includeController: true,
|
|
169
|
+
bootstrapAppConfig: {
|
|
170
|
+
databaseMode: 'single',
|
|
171
|
+
enableCompanyFeature: true,
|
|
172
|
+
permissionMode: 'FULL',
|
|
173
|
+
},
|
|
174
|
+
imports: [ConfigModule],
|
|
175
|
+
useFactory: async (configService: ConfigService) => ({
|
|
176
|
+
defaultDatabaseConfig: {
|
|
177
|
+
type: 'postgres',
|
|
178
|
+
host: configService.get('DB_HOST'),
|
|
179
|
+
port: configService.get<number>('DB_PORT'),
|
|
180
|
+
username: configService.get('DB_USER'),
|
|
181
|
+
password: configService.get('DB_PASSWORD'),
|
|
182
|
+
database: configService.get('DB_NAME'),
|
|
183
|
+
},
|
|
184
|
+
}),
|
|
185
|
+
inject: [ConfigService],
|
|
186
|
+
})
|
|
213
187
|
```
|
|
214
188
|
|
|
215
189
|
---
|
|
216
190
|
|
|
217
|
-
##
|
|
218
|
-
|
|
219
|
-
### IAction
|
|
191
|
+
## Configuration Reference
|
|
220
192
|
|
|
221
193
|
```typescript
|
|
222
|
-
interface
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
actionType: ActionType;
|
|
229
|
-
permissionLogic: LogicNode | null;
|
|
230
|
-
serial: number | null;
|
|
231
|
-
isActive: boolean;
|
|
232
|
-
parentId: string | null;
|
|
233
|
-
metadata: Record<string, any> | null;
|
|
234
|
-
createdAt: Date;
|
|
235
|
-
updatedAt: Date;
|
|
236
|
-
deletedAt: Date | null;
|
|
237
|
-
createdById: string | null;
|
|
238
|
-
updatedById: string | null;
|
|
239
|
-
deletedById: string | null;
|
|
240
|
-
}
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
### IActionTree
|
|
244
|
-
|
|
245
|
-
```typescript
|
|
246
|
-
interface IActionTree extends IAction {
|
|
247
|
-
children?: IActionTree[];
|
|
248
|
-
}
|
|
249
|
-
```
|
|
250
|
-
|
|
251
|
-
### IRole
|
|
252
|
-
|
|
253
|
-
```typescript
|
|
254
|
-
interface IRole {
|
|
255
|
-
id: string;
|
|
256
|
-
readOnly: boolean;
|
|
257
|
-
name: string;
|
|
258
|
-
description: string | null;
|
|
259
|
-
isActive: boolean;
|
|
260
|
-
serial: number | null;
|
|
261
|
-
companyId: string | null;
|
|
262
|
-
metadata: Record<string, any> | null;
|
|
263
|
-
createdAt: Date;
|
|
264
|
-
updatedAt: Date;
|
|
265
|
-
deletedAt: Date | null;
|
|
266
|
-
createdById: string | null;
|
|
267
|
-
updatedById: string | null;
|
|
268
|
-
deletedById: string | null;
|
|
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[]
|
|
269
200
|
}
|
|
270
201
|
```
|
|
271
202
|
|
|
272
|
-
|
|
203
|
+
Bootstrap configuration is fixed at startup and cannot change at runtime:
|
|
273
204
|
|
|
274
205
|
```typescript
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
// Full module options
|
|
281
|
-
interface IAMModuleOptions extends IDynamicModuleConfig {
|
|
282
|
-
bootstrapAppConfig?: IBootstrapAppConfig;
|
|
283
|
-
config?: IIAMModuleConfig;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
// Async options
|
|
287
|
-
interface IAMModuleAsyncOptions extends Pick<ModuleMetadata, 'imports'>, IDynamicModuleConfig {
|
|
288
|
-
bootstrapAppConfig?: IBootstrapAppConfig;
|
|
289
|
-
config?: IIAMModuleConfig;
|
|
290
|
-
useFactory?: (...args: any[]) => Promise<IIAMModuleConfig> | IIAMModuleConfig;
|
|
291
|
-
inject?: any[];
|
|
292
|
-
useClass?: Type<IAMOptionsFactory>;
|
|
293
|
-
useExisting?: Type<IAMOptionsFactory>;
|
|
206
|
+
interface IBootstrapAppConfig {
|
|
207
|
+
databaseMode: 'single' | 'multi-tenant';
|
|
208
|
+
enableCompanyFeature: boolean;
|
|
209
|
+
permissionMode?: 'FULL' | 'RBAC' | 'DIRECT'; // Default: 'FULL'
|
|
294
210
|
}
|
|
295
211
|
```
|
|
296
212
|
|
|
297
213
|
---
|
|
298
214
|
|
|
299
|
-
##
|
|
215
|
+
## Feature Toggles
|
|
300
216
|
|
|
301
|
-
|
|
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 |
|
|
302
223
|
|
|
303
|
-
|
|
304
|
-
// Core entities (no company feature)
|
|
305
|
-
export const IAMCoreEntities = [Action, Role, UserIamPermission];
|
|
306
|
-
|
|
307
|
-
// Company-specific entities
|
|
308
|
-
export const IAMCompanyEntities = [RoleWithCompany, UserIamPermissionWithCompany];
|
|
309
|
-
|
|
310
|
-
// All entities
|
|
311
|
-
export const IAMAllEntities = [
|
|
312
|
-
Action,
|
|
313
|
-
Role,
|
|
314
|
-
RoleWithCompany,
|
|
315
|
-
UserIamPermission,
|
|
316
|
-
UserIamPermissionWithCompany,
|
|
317
|
-
];
|
|
318
|
-
|
|
319
|
-
// Helper function
|
|
320
|
-
export function getIAMEntitiesByConfig(
|
|
321
|
-
enableCompanyFeature: boolean,
|
|
322
|
-
permissionMode: 'FULL' | 'RBAC' | 'DIRECT' = 'FULL',
|
|
323
|
-
): any[] {
|
|
324
|
-
const entities: any[] = [Action];
|
|
325
|
-
|
|
326
|
-
// Permission entity - always included
|
|
327
|
-
if (enableCompanyFeature) {
|
|
328
|
-
entities.push(UserIamPermissionWithCompany);
|
|
329
|
-
} else {
|
|
330
|
-
entities.push(UserIamPermission);
|
|
331
|
-
}
|
|
224
|
+
**Controller loading by mode:**
|
|
332
225
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
entities.push(Role);
|
|
339
|
-
}
|
|
340
|
-
}
|
|
226
|
+
| Mode | + `enableCompanyFeature` | Additional controller |
|
|
227
|
+
|------|--------------------------|-----------------------|
|
|
228
|
+
| `RBAC` | true | `CompanyActionPermissionController` |
|
|
229
|
+
| `DIRECT` | true | `CompanyActionPermissionController` |
|
|
230
|
+
| `FULL` | true | `CompanyActionPermissionController` |
|
|
341
231
|
|
|
342
|
-
|
|
343
|
-
}
|
|
344
|
-
```
|
|
345
|
-
|
|
346
|
-
### ActionBase
|
|
347
|
-
|
|
348
|
-
Core action fields in `action-base.entity.ts`:
|
|
349
|
-
|
|
350
|
-
| Column | Type | Description |
|
|
351
|
-
|--------|------|-------------|
|
|
352
|
-
| `readOnly` | boolean | System-protected action |
|
|
353
|
-
| `name` | varchar(255) | Action name |
|
|
354
|
-
| `code` | varchar(255) | Unique code |
|
|
355
|
-
| `description` | varchar(500) | Description |
|
|
356
|
-
| `actionType` | enum | BACKEND, FRONTEND, BOTH |
|
|
357
|
-
| `permissionLogic` | json | AND/OR rules |
|
|
358
|
-
| `parentId` | uuid | Parent action ID |
|
|
359
|
-
| `serial` | int | Display order |
|
|
360
|
-
| `isActive` | boolean | Active status |
|
|
361
|
-
| `metadata` | json | Additional data |
|
|
232
|
+
---
|
|
362
233
|
|
|
363
|
-
|
|
234
|
+
## API Endpoints
|
|
364
235
|
|
|
365
|
-
|
|
236
|
+
All endpoints use **POST** and require JWT authentication.
|
|
366
237
|
|
|
367
|
-
|
|
368
|
-
|--------|------|-------------|
|
|
369
|
-
| `readOnly` | boolean | System-protected role |
|
|
370
|
-
| `name` | varchar(255) | Role name |
|
|
371
|
-
| `description` | varchar(500) | Description |
|
|
372
|
-
| `isActive` | boolean | Active status |
|
|
373
|
-
| `serial` | int | Display order |
|
|
374
|
-
| `metadata` | json | Additional data |
|
|
238
|
+
### Actions — `POST /iam/action/*`
|
|
375
239
|
|
|
376
|
-
|
|
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 |
|
|
377
248
|
|
|
378
|
-
|
|
379
|
-
- `companyId` (uuid) - Company scope
|
|
249
|
+
### Roles — `POST /iam/role/*` *(RBAC and FULL modes)*
|
|
380
250
|
|
|
381
|
-
|
|
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 |
|
|
382
258
|
|
|
383
|
-
|
|
259
|
+
### Role Permissions — `POST /iam/role-permission/*` *(RBAC and FULL modes)*
|
|
384
260
|
|
|
385
|
-
|
|
|
386
|
-
|
|
387
|
-
| `
|
|
388
|
-
| `
|
|
389
|
-
| `
|
|
390
|
-
| `
|
|
391
|
-
| `
|
|
392
|
-
| `userId` | uuid | User ID (nullable) |
|
|
393
|
-
| `validFrom` | timestamp | Permission valid from |
|
|
394
|
-
| `validUntil` | timestamp | Permission valid until |
|
|
395
|
-
| `reason` | text | Reason for permission |
|
|
396
|
-
| `metadata` | json | Additional data |
|
|
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 |
|
|
397
268
|
|
|
398
|
-
|
|
269
|
+
### User Action Permissions — `POST /iam/user-action-permission/*` *(DIRECT and FULL modes)*
|
|
399
270
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
return true;
|
|
406
|
-
}
|
|
407
|
-
```
|
|
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 |
|
|
408
276
|
|
|
409
|
-
###
|
|
277
|
+
### Company Action Whitelist — `POST /iam/company-action/*` *(`enableCompanyFeature: true`)*
|
|
410
278
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
- `
|
|
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 |
|
|
414
284
|
|
|
415
|
-
|
|
285
|
+
### My Permissions — `POST /iam/my-permission/*`
|
|
416
286
|
|
|
417
|
-
|
|
|
418
|
-
|
|
419
|
-
|
|
|
420
|
-
| Company-wide | set | null | Manager for ALL branches in company |
|
|
421
|
-
| Branch-specific | set | set | Manager for specific branch only |
|
|
287
|
+
| Endpoint | Auth | Description |
|
|
288
|
+
|----------|------|-------------|
|
|
289
|
+
| `POST /iam/my-permission/get-all` | JWT | Get all permissions for current user |
|
|
422
290
|
|
|
423
291
|
---
|
|
424
292
|
|
|
425
|
-
##
|
|
426
|
-
|
|
427
|
-
Set in `bootstrapAppConfig.permissionMode`:
|
|
428
|
-
|
|
429
|
-
### Permission Mode Enum
|
|
430
|
-
|
|
431
|
-
```typescript
|
|
432
|
-
enum IAMPermissionMode {
|
|
433
|
-
RBAC = 1, // Role-Based: Action + RoleAction + UserRole
|
|
434
|
-
DIRECT = 2, // Direct User Permissions: Action + UserAction
|
|
435
|
-
FULL = 3, // Both RBAC + Direct permissions
|
|
436
|
-
}
|
|
437
|
-
```
|
|
438
|
-
|
|
439
|
-
### RBAC Mode (Role-Based)
|
|
440
|
-
|
|
441
|
-
```
|
|
442
|
-
User -> Role -> Action
|
|
443
|
-
```
|
|
444
|
-
|
|
445
|
-
Only role-based permissions. Users get permissions through assigned roles.
|
|
446
|
-
|
|
447
|
-
**Note:** Calling `assignUserActions()` in RBAC mode throws `BadRequestException`.
|
|
293
|
+
## Entities
|
|
448
294
|
|
|
449
|
-
###
|
|
295
|
+
### Core Entities (always registered)
|
|
450
296
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
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 |
|
|
454
301
|
|
|
455
|
-
|
|
302
|
+
### RBAC Entities (`permissionMode: 'RBAC'` or `'FULL'`)
|
|
456
303
|
|
|
457
|
-
|
|
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 |
|
|
458
309
|
|
|
459
|
-
###
|
|
310
|
+
### DIRECT Entities (`permissionMode: 'DIRECT'` or `'FULL'`)
|
|
460
311
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
User
|
|
464
|
-
```
|
|
312
|
+
| Entity | Table | Description |
|
|
313
|
+
|--------|-------|-------------|
|
|
314
|
+
| `UserAction` | `iam_user_action` | Direct User → Action assignments |
|
|
465
315
|
|
|
466
|
-
|
|
316
|
+
### Company Feature Entities (`enableCompanyFeature: true`)
|
|
467
317
|
|
|
468
|
-
|
|
318
|
+
All above entities get `WithCompany` variants with `companyId` and `branchId` columns, plus:
|
|
469
319
|
|
|
470
|
-
|
|
320
|
+
| Entity | Table | Description |
|
|
321
|
+
|--------|-------|-------------|
|
|
322
|
+
| `CompanyAction` | `iam_company_action` | Actions whitelisted per company |
|
|
471
323
|
|
|
472
|
-
|
|
324
|
+
#### Register Entities in TypeORM
|
|
473
325
|
|
|
474
326
|
```typescript
|
|
475
|
-
|
|
476
|
-
id: string;
|
|
477
|
-
type: 'group' | 'action';
|
|
478
|
-
operator?: 'AND' | 'OR';
|
|
479
|
-
children?: LogicNode[];
|
|
480
|
-
actionId?: string;
|
|
481
|
-
}
|
|
482
|
-
```
|
|
483
|
-
|
|
484
|
-
**Example:** User must have "employee" AND ("department-head" OR "hr-clearance")
|
|
327
|
+
import { IAMModule } from '@flusys/nestjs-iam';
|
|
485
328
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
{ id: '1', type: 'action', actionId: 'employee-action-id' },
|
|
493
|
-
{
|
|
494
|
-
id: '2',
|
|
495
|
-
type: 'group',
|
|
496
|
-
operator: 'OR',
|
|
497
|
-
children: [
|
|
498
|
-
{ id: '2-1', type: 'action', actionId: 'department-head' },
|
|
499
|
-
{ id: '2-2', type: 'action', actionId: 'hr-clearance' },
|
|
500
|
-
],
|
|
501
|
-
},
|
|
329
|
+
TypeOrmModule.forRoot({
|
|
330
|
+
entities: [
|
|
331
|
+
...IAMModule.getEntities({
|
|
332
|
+
enableCompanyFeature: true,
|
|
333
|
+
permissionMode: 'FULL',
|
|
334
|
+
}),
|
|
502
335
|
],
|
|
503
|
-
}
|
|
336
|
+
})
|
|
504
337
|
```
|
|
505
338
|
|
|
506
339
|
---
|
|
507
340
|
|
|
508
|
-
##
|
|
509
|
-
|
|
510
|
-
| Service | Scope | Description |
|
|
511
|
-
|---------|-------|-------------|
|
|
512
|
-
| `ActionService` | REQUEST | Action CRUD, tree queries |
|
|
513
|
-
| `RoleService` | REQUEST | Role CRUD (RBAC/FULL mode only) |
|
|
514
|
-
| `PermissionService` | REQUEST | Permission assignments, my-permissions |
|
|
515
|
-
| `PermissionCacheService` | SINGLETON | Cache management |
|
|
516
|
-
| `IAMConfigService` | SINGLETON | IAM configuration access |
|
|
517
|
-
| `IAMDataSourceService` | REQUEST | DataSource provider for multi-tenant |
|
|
518
|
-
|
|
519
|
-
### IAMConfigService
|
|
341
|
+
## Permission Cache
|
|
520
342
|
|
|
521
|
-
|
|
522
|
-
import { IAMConfigService } from '@flusys/nestjs-iam';
|
|
523
|
-
|
|
524
|
-
// Database mode
|
|
525
|
-
const dbMode = config.getDatabaseMode(); // 'single' | 'multi-tenant'
|
|
526
|
-
const isMultiTenant = config.isMultiTenant();
|
|
343
|
+
Permissions are cached with the key `iam:permissions:{userId}:{companyId}:{branchId}` for **1 hour**. The cache is automatically invalidated when:
|
|
527
344
|
|
|
528
|
-
|
|
529
|
-
|
|
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
|
|
530
349
|
|
|
531
|
-
|
|
532
|
-
const mode = config.getPermissionMode(); // IAMPermissionMode enum
|
|
533
|
-
const isRbac = config.isRbacEnabled(); // true for RBAC or FULL
|
|
534
|
-
const isDirect = config.isDirectPermissionEnabled(); // true for DIRECT or FULL
|
|
535
|
-
|
|
536
|
-
// Get raw options
|
|
537
|
-
const options = config.getOptions();
|
|
538
|
-
```
|
|
350
|
+
Cache uses `HybridCache` (in-memory + Redis). If Redis is not configured, in-memory cache is used.
|
|
539
351
|
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
```typescript
|
|
543
|
-
import { IAMDataSourceService } from '@flusys/nestjs-iam';
|
|
544
|
-
|
|
545
|
-
// Get repository for entity
|
|
546
|
-
const actionRepo = await dataSourceProvider.getRepository(Action);
|
|
547
|
-
|
|
548
|
-
// Tenant-specific features
|
|
549
|
-
const enableCompany = dataSourceProvider.getEnableCompanyFeatureForCurrentTenant();
|
|
550
|
-
const entities = await dataSourceProvider.getIAMEntities();
|
|
551
|
-
```
|
|
552
|
-
|
|
553
|
-
### ActionService
|
|
554
|
-
|
|
555
|
-
```typescript
|
|
556
|
-
import { ActionService } from '@flusys/nestjs-iam';
|
|
557
|
-
|
|
558
|
-
// Create action
|
|
559
|
-
await actionService.insert({
|
|
560
|
-
name: 'View Users',
|
|
561
|
-
code: 'user.view',
|
|
562
|
-
actionType: ActionType.BACKEND,
|
|
563
|
-
parentId: parentAction.id,
|
|
564
|
-
});
|
|
352
|
+
---
|
|
565
353
|
|
|
566
|
-
|
|
567
|
-
const tree = await actionService.getActionTree(user, 'search', true, false);
|
|
354
|
+
## Exported Services
|
|
568
355
|
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
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 |
|
|
572
364
|
|
|
573
|
-
|
|
365
|
+
---
|
|
574
366
|
|
|
575
|
-
|
|
576
|
-
import { RoleService } from '@flusys/nestjs-iam';
|
|
577
|
-
|
|
578
|
-
// Create role
|
|
579
|
-
await roleService.insert({
|
|
580
|
-
name: 'Administrator',
|
|
581
|
-
description: 'Full system access',
|
|
582
|
-
companyId: 'company-id',
|
|
583
|
-
});
|
|
584
|
-
```
|
|
367
|
+
## Using Permissions in Controllers
|
|
585
368
|
|
|
586
|
-
###
|
|
369
|
+
### Using the Decorator
|
|
587
370
|
|
|
588
371
|
```typescript
|
|
589
|
-
import {
|
|
590
|
-
|
|
591
|
-
// Assign roles to user (RBAC/FULL mode only)
|
|
592
|
-
await permissionService.assignUserRoles({
|
|
593
|
-
userId: 'user-id',
|
|
594
|
-
companyId: 'company-id',
|
|
595
|
-
branchId: null, // null = company-wide
|
|
596
|
-
items: [{ id: 'role-id', action: PermissionAction.ADD }],
|
|
597
|
-
});
|
|
598
|
-
|
|
599
|
-
// Assign actions directly (DIRECT/FULL mode only)
|
|
600
|
-
await permissionService.assignUserActions({
|
|
601
|
-
userId: 'user-id',
|
|
602
|
-
companyId: 'company-id',
|
|
603
|
-
branchId: 'branch-id',
|
|
604
|
-
items: [{ id: 'action-id', action: PermissionAction.ADD }],
|
|
605
|
-
});
|
|
606
|
-
|
|
607
|
-
// Assign actions to role (RBAC/FULL mode only)
|
|
608
|
-
await permissionService.assignRoleActions({
|
|
609
|
-
roleId: 'role-id',
|
|
610
|
-
items: [{ id: 'action-id', action: PermissionAction.ADD }],
|
|
611
|
-
});
|
|
612
|
-
|
|
613
|
-
// Get user's permissions
|
|
614
|
-
const permissions = await permissionService.getMyPermissions(
|
|
615
|
-
userId, branchId, companyId, parentCodes
|
|
616
|
-
);
|
|
617
|
-
```
|
|
618
|
-
|
|
619
|
-
### PermissionCacheService
|
|
620
|
-
|
|
621
|
-
Centralized cache management for IAM permissions. Cache key format matches PermissionGuard in nestjs-shared.
|
|
372
|
+
import { RequirePermission } from '@flusys/nestjs-shared/decorators';
|
|
373
|
+
import { JwtAuthGuard } from '@flusys/nestjs-shared/guards';
|
|
622
374
|
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
```typescript
|
|
630
|
-
interface PermissionCacheKeyOptions {
|
|
631
|
-
userId: string;
|
|
632
|
-
companyId?: string | null;
|
|
633
|
-
branchId?: string | null;
|
|
634
|
-
enableCompanyFeature: boolean;
|
|
635
|
-
}
|
|
375
|
+
@UseGuards(JwtAuthGuard)
|
|
376
|
+
@Controller('products')
|
|
377
|
+
export class ProductController {
|
|
378
|
+
@Post('insert')
|
|
379
|
+
@RequirePermission('product.create')
|
|
380
|
+
async create() { }
|
|
636
381
|
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
code: string;
|
|
641
|
-
name: string;
|
|
642
|
-
description: string | null;
|
|
643
|
-
parentId: string | null;
|
|
644
|
-
}>;
|
|
645
|
-
backendCodes: string[];
|
|
382
|
+
@Post('get-all')
|
|
383
|
+
@RequirePermission('product.read')
|
|
384
|
+
async getAll() { }
|
|
646
385
|
}
|
|
647
386
|
```
|
|
648
387
|
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
```typescript
|
|
652
|
-
import { PermissionCacheService } from '@flusys/nestjs-iam';
|
|
653
|
-
|
|
654
|
-
// Set permissions (backend codes for PermissionGuard)
|
|
655
|
-
await cacheService.setPermissions(
|
|
656
|
-
{ userId, companyId, branchId, enableCompanyFeature: true },
|
|
657
|
-
['users.read', 'users.write']
|
|
658
|
-
);
|
|
659
|
-
|
|
660
|
-
// Set my-permissions (full response with frontend actions)
|
|
661
|
-
await cacheService.setMyPermissions(cacheOptions, {
|
|
662
|
-
frontendActions: [...],
|
|
663
|
-
backendCodes: ['users.read', 'users.write'],
|
|
664
|
-
});
|
|
665
|
-
|
|
666
|
-
// Get cached my-permissions
|
|
667
|
-
const cached = await cacheService.getMyPermissions(cacheOptions);
|
|
668
|
-
|
|
669
|
-
// Invalidate single user cache
|
|
670
|
-
await cacheService.invalidateUser('user-id', 'company-id', ['branch-id']);
|
|
671
|
-
|
|
672
|
-
// Invalidate multiple users cache
|
|
673
|
-
await cacheService.invalidateUsers(userIds, 'company-id', branchIds);
|
|
674
|
-
|
|
675
|
-
// Invalidate role members (all users assigned to role)
|
|
676
|
-
await cacheService.invalidateRole('role-id', userIds, 'company-id', branchIds);
|
|
677
|
-
|
|
678
|
-
// Tenant-aware action code cache
|
|
679
|
-
await cacheService.setActionCodeMap(codeToIdMap, tenantId);
|
|
680
|
-
await cacheService.getActionIdsByCodes(['user.view'], tenantId);
|
|
681
|
-
```
|
|
682
|
-
|
|
683
|
-
**Cache Key Formats:**
|
|
684
|
-
```
|
|
685
|
-
permissions:user:{userId} // Without company
|
|
686
|
-
permissions:company:{companyId}:branch:{branchId}:user:{userId} // With company
|
|
687
|
-
my-permissions:user:{userId} // My-permissions (no company)
|
|
688
|
-
my-permissions:company:{companyId}:branch:{branchId}:user:{userId} // My-permissions (with company)
|
|
689
|
-
action-codes:map // Single tenant
|
|
690
|
-
action-codes:tenant:{tenantId}:map // Multi-tenant
|
|
691
|
-
```
|
|
692
|
-
|
|
693
|
-
---
|
|
694
|
-
|
|
695
|
-
## Helpers
|
|
696
|
-
|
|
697
|
-
### PermissionModeHelper
|
|
698
|
-
|
|
699
|
-
Centralizes conversion from string to enum to prevent duplication:
|
|
388
|
+
### Checking Permissions Programmatically
|
|
700
389
|
|
|
701
390
|
```typescript
|
|
702
|
-
import {
|
|
703
|
-
|
|
704
|
-
// Convert string to enum
|
|
705
|
-
const mode = PermissionModeHelper.fromString('RBAC');
|
|
706
|
-
// Returns: IAMPermissionMode.RBAC
|
|
391
|
+
import { PermissionService } from '@flusys/nestjs-iam';
|
|
707
392
|
|
|
708
|
-
|
|
709
|
-
|
|
393
|
+
@Injectable()
|
|
394
|
+
export class ProductService {
|
|
395
|
+
constructor(
|
|
396
|
+
@Inject(PermissionService) private readonly permissionService: PermissionService,
|
|
397
|
+
) {}
|
|
710
398
|
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
```
|
|
715
|
-
|
|
716
|
-
**Used by:**
|
|
717
|
-
- `iam.module.ts` (forRoot, forRootAsync)
|
|
718
|
-
- `iam-config.service.ts` (getPermissionMode)
|
|
719
|
-
- `main.ts` (Swagger setup)
|
|
720
|
-
|
|
721
|
-
### validateCompanyAccess
|
|
722
|
-
|
|
723
|
-
Validates user access to a company for permission operations:
|
|
399
|
+
async canUserCreate(userId: string, companyId?: string): Promise<boolean> {
|
|
400
|
+
return this.permissionService.hasPermission(userId, 'product.create', companyId);
|
|
401
|
+
}
|
|
724
402
|
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
validateCompanyAccess(
|
|
730
|
-
iamConfig,
|
|
731
|
-
dto.companyId,
|
|
732
|
-
user,
|
|
733
|
-
'You do not have access to this company',
|
|
734
|
-
);
|
|
735
|
-
// Throws ForbiddenException if user.companyId !== dto.companyId
|
|
403
|
+
async getUserPermissions(userId: string, companyId?: string): Promise<string[]> {
|
|
404
|
+
return this.permissionService.getPermissions(userId, companyId);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
736
407
|
```
|
|
737
408
|
|
|
738
409
|
---
|
|
739
410
|
|
|
740
|
-
##
|
|
741
|
-
|
|
742
|
-
### Actions API
|
|
743
|
-
|
|
744
|
-
| Endpoint | Method | Permission | Description |
|
|
745
|
-
|----------|--------|------------|-------------|
|
|
746
|
-
| `/iam/actions/insert` | POST | `action.create` | Create action |
|
|
747
|
-
| `/iam/actions/insert-many` | POST | `action.create` | Create multiple actions |
|
|
748
|
-
| `/iam/actions/get-all` | POST | `action.read` | List actions (paginated) |
|
|
749
|
-
| `/iam/actions/get` | POST | `action.read` | Get action by ID |
|
|
750
|
-
| `/iam/actions/tree` | POST | JWT Auth | Get hierarchical tree (ALL actions) |
|
|
751
|
-
| `/iam/actions/tree-for-permission` | POST | JWT Auth | Get company-filtered actions |
|
|
752
|
-
| `/iam/actions/update` | POST | `action.update` | Update action |
|
|
753
|
-
| `/iam/actions/update-many` | POST | `action.update` | Update multiple actions |
|
|
754
|
-
| `/iam/actions/delete` | POST | `action.delete` | Delete action |
|
|
755
|
-
|
|
756
|
-
### Roles API (RBAC/FULL mode only)
|
|
757
|
-
|
|
758
|
-
| Endpoint | Method | Permission | Description |
|
|
759
|
-
|----------|--------|------------|-------------|
|
|
760
|
-
| `/iam/roles/insert` | POST | `role.create` | Create role |
|
|
761
|
-
| `/iam/roles/insert-many` | POST | `role.create` | Create multiple roles |
|
|
762
|
-
| `/iam/roles/get-all` | POST | `role.read` | List roles (paginated, auto-filtered by company) |
|
|
763
|
-
| `/iam/roles/get` | POST | `role.read` | Get role by ID |
|
|
764
|
-
| `/iam/roles/update` | POST | `role.update` | Update role |
|
|
765
|
-
| `/iam/roles/update-many` | POST | `role.update` | Update multiple roles |
|
|
766
|
-
| `/iam/roles/delete` | POST | `role.delete` | Delete role |
|
|
767
|
-
|
|
768
|
-
### Permissions API
|
|
769
|
-
|
|
770
|
-
| Endpoint | Method | Permission | Modes | Description |
|
|
771
|
-
|----------|--------|------------|-------|-------------|
|
|
772
|
-
| `/iam/permissions/role-actions/assign` | POST | `role-action.assign` | RBAC, FULL | Assign actions to role |
|
|
773
|
-
| `/iam/permissions/get-role-actions` | POST | `role-action.read` | RBAC, FULL | Get role actions |
|
|
774
|
-
| `/iam/permissions/user-roles/assign` | POST | `user-role.assign` | RBAC, FULL | Assign roles to user |
|
|
775
|
-
| `/iam/permissions/get-user-roles` | POST | `user-role.read` | RBAC, FULL | Get user roles |
|
|
776
|
-
| `/iam/permissions/user-actions/assign` | POST | `user-action.assign` | DIRECT, FULL | Assign actions to user |
|
|
777
|
-
| `/iam/permissions/get-user-actions` | POST | `user-action.read` | DIRECT, FULL | Get user actions |
|
|
778
|
-
| `/iam/permissions/company-actions/assign` | POST | `company-action.assign` | Company enabled | Company action whitelist |
|
|
779
|
-
| `/iam/permissions/get-company-actions` | POST | `company-action.read` | Company enabled | Get company actions |
|
|
780
|
-
| `/iam/permissions/my-permissions` | POST | JWT Auth | All | Get current user's permissions |
|
|
781
|
-
|
|
782
|
-
### Permission Constants
|
|
783
|
-
|
|
784
|
-
These permission codes are defined in `@flusys/nestjs-shared`:
|
|
411
|
+
## Action Types
|
|
785
412
|
|
|
786
|
-
|
|
787
|
-
// Actions
|
|
788
|
-
ACTION_PERMISSIONS = { CREATE: 'action.create', READ: 'action.read', UPDATE: 'action.update', DELETE: 'action.delete' }
|
|
789
|
-
|
|
790
|
-
// Roles
|
|
791
|
-
ROLE_PERMISSIONS = { CREATE: 'role.create', READ: 'role.read', UPDATE: 'role.update', DELETE: 'role.delete' }
|
|
792
|
-
|
|
793
|
-
// Role-Action assignments
|
|
794
|
-
ROLE_ACTION_PERMISSIONS = { ASSIGN: 'role-action.assign', READ: 'role-action.read' }
|
|
795
|
-
|
|
796
|
-
// User-Role assignments
|
|
797
|
-
USER_ROLE_PERMISSIONS = { ASSIGN: 'user-role.assign', READ: 'user-role.read' }
|
|
798
|
-
|
|
799
|
-
// User-Action assignments (direct)
|
|
800
|
-
USER_ACTION_PERMISSIONS = { ASSIGN: 'user-action.assign', READ: 'user-action.read' }
|
|
801
|
-
|
|
802
|
-
// Company-Action assignments
|
|
803
|
-
COMPANY_ACTION_PERMISSIONS = { ASSIGN: 'company-action.assign', READ: 'company-action.read' }
|
|
804
|
-
```
|
|
805
|
-
|
|
806
|
-
**Controller Registration by Mode:**
|
|
807
|
-
|
|
808
|
-
| Mode | Controllers |
|
|
413
|
+
| Type | Description |
|
|
809
414
|
|------|-------------|
|
|
810
|
-
|
|
|
811
|
-
|
|
|
812
|
-
|
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
// config/app.config.ts
|
|
823
|
-
export const bootstrapAppConfig: IBootstrapAppConfig = {
|
|
824
|
-
databaseMode: 'single', // or 'multi-tenant'
|
|
825
|
-
enableCompanyFeature: true,
|
|
826
|
-
permissionMode: 'FULL',
|
|
827
|
-
};
|
|
828
|
-
```
|
|
829
|
-
|
|
830
|
-
### When `enableCompanyFeature: true`
|
|
831
|
-
|
|
832
|
-
- `companyId` and `branchId` fields available in DTOs
|
|
833
|
-
- Company-Action Permissions controller registered
|
|
834
|
-
- Three-level permission granularity (Global, Company-wide, Branch-specific)
|
|
835
|
-
- Uses `UserIamPermissionWithCompany` and `RoleWithCompany` entities
|
|
836
|
-
|
|
837
|
-
### When `enableCompanyFeature: false`
|
|
838
|
-
|
|
839
|
-
- Company endpoints NOT available (404)
|
|
840
|
-
- `companyId`/`branchId` fields visible in Swagger but ignored
|
|
841
|
-
- All permissions are global
|
|
842
|
-
- Uses `UserIamPermission` and `Role` entities
|
|
843
|
-
|
|
844
|
-
### Multi-Tenant Database Mode
|
|
845
|
-
|
|
846
|
-
When `databaseMode: 'multi-tenant'`:
|
|
847
|
-
- Each tenant has isolated database/schema
|
|
848
|
-
- Action code cache is tenant-aware (separate cache per tenant)
|
|
849
|
-
- Uses `IAMDataSourceService` for tenant-specific connections
|
|
850
|
-
|
|
851
|
-
### Permission Merging
|
|
852
|
-
|
|
853
|
-
When `getMyPermissions` is called:
|
|
854
|
-
|
|
855
|
-
**With branchId specified:**
|
|
856
|
-
1. Company-wide roles (branchId=null) + Branch-specific roles for that branch
|
|
857
|
-
2. Actions from all merged roles
|
|
858
|
-
3. Company-wide user actions + Branch-specific user actions for that branch
|
|
859
|
-
|
|
860
|
-
**Without branchId (null):**
|
|
861
|
-
1. ALL roles for the user in the company (any branch)
|
|
862
|
-
2. Actions from all roles
|
|
863
|
-
3. ALL user actions for the company (any branch)
|
|
864
|
-
|
|
865
|
-
**Result:** Complete permission set without duplicates, filtered by company whitelist.
|
|
866
|
-
|
|
867
|
-
---
|
|
868
|
-
|
|
869
|
-
## Permission Guard Integration
|
|
870
|
-
|
|
871
|
-
```typescript
|
|
872
|
-
import { RequirePermission, RequireAnyPermission } from '@flusys/nestjs-shared/decorators';
|
|
873
|
-
|
|
874
|
-
@Controller('users')
|
|
875
|
-
export class UserController {
|
|
876
|
-
@RequirePermission('user.view')
|
|
877
|
-
@Post('get-all')
|
|
878
|
-
getUsers() {}
|
|
879
|
-
|
|
880
|
-
@RequireAnyPermission('user.create', 'admin')
|
|
881
|
-
@Post('insert')
|
|
882
|
-
createUser() {}
|
|
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"
|
|
883
427
|
}
|
|
884
428
|
```
|
|
885
429
|
|
|
886
430
|
---
|
|
887
431
|
|
|
888
|
-
##
|
|
432
|
+
## Hierarchical Actions
|
|
889
433
|
|
|
890
|
-
|
|
434
|
+
Actions support parent-child relationships. Use `parentId` to create a tree:
|
|
891
435
|
|
|
892
|
-
```typescript
|
|
893
|
-
// Hierarchical naming for backend
|
|
894
|
-
'user.view', 'user.create', 'user.delete'
|
|
895
|
-
|
|
896
|
-
// UPPERCASE for frontend actions
|
|
897
|
-
'MENU_USERS', 'MENU_REPORTS', 'FEATURE_EXPORT'
|
|
898
436
|
```
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
{ companyId: 'company-a', branchId: null }
|
|
905
|
-
|
|
906
|
-
// Branch-specific for location-bound staff
|
|
907
|
-
{ companyId: 'company-a', branchId: 'branch-x' }
|
|
437
|
+
product (BOTH - parent)
|
|
438
|
+
├── product.read (BOTH - child)
|
|
439
|
+
├── product.create (BOTH - child)
|
|
440
|
+
├── product.update (BOTH - child)
|
|
441
|
+
└── product.delete (BOTH - child)
|
|
908
442
|
```
|
|
909
443
|
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
const roles = [
|
|
915
|
-
{ name: 'Viewer', actions: ['*.view'] },
|
|
916
|
-
{ name: 'Editor', actions: ['*.view', '*.create', '*.update'] },
|
|
917
|
-
{ name: 'Admin', actions: ['*'] },
|
|
918
|
-
];
|
|
444
|
+
Get the full tree:
|
|
445
|
+
```json
|
|
446
|
+
POST /iam/action/get-tree
|
|
447
|
+
{}
|
|
919
448
|
```
|
|
920
449
|
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
Direct actions should be exceptions and branch-specific overrides, not the primary permission mechanism.
|
|
450
|
+
The wildcard `product.*` in `@RequirePermission('product.*')` matches any action whose code starts with `product.`.
|
|
924
451
|
|
|
925
452
|
---
|
|
926
453
|
|
|
927
|
-
##
|
|
928
|
-
|
|
929
|
-
### Permission Operation DTOs
|
|
930
|
-
|
|
931
|
-
```typescript
|
|
932
|
-
// Permission action enum for add/remove operations
|
|
933
|
-
enum PermissionAction {
|
|
934
|
-
ADD = 'add',
|
|
935
|
-
REMOVE = 'remove',
|
|
936
|
-
}
|
|
454
|
+
## Troubleshooting
|
|
937
455
|
|
|
938
|
-
|
|
939
|
-
interface PermissionItemDto {
|
|
940
|
-
id: string; // ID of target (action or role)
|
|
941
|
-
action: PermissionAction; // ADD or REMOVE
|
|
942
|
-
}
|
|
943
|
-
```
|
|
456
|
+
**`PermissionGuard` always denies — user has correct role**
|
|
944
457
|
|
|
945
|
-
|
|
458
|
+
Check that the IAM entities are included in your `TypeOrmModule` registration. Missing entities cause the permission query to return empty results.
|
|
946
459
|
|
|
947
|
-
|
|
948
|
-
// Assign actions directly to user
|
|
949
|
-
interface AssignUserActionsDto {
|
|
950
|
-
userId: string;
|
|
951
|
-
companyId?: string; // Company scope (when enabled)
|
|
952
|
-
branchId?: string; // Branch scope (null = company-wide)
|
|
953
|
-
items: PermissionItemDto[];
|
|
954
|
-
}
|
|
955
|
-
|
|
956
|
-
// Assign roles to user
|
|
957
|
-
interface AssignUserRolesDto {
|
|
958
|
-
userId: string;
|
|
959
|
-
companyId?: string;
|
|
960
|
-
branchId?: string;
|
|
961
|
-
items: PermissionItemDto[];
|
|
962
|
-
}
|
|
963
|
-
|
|
964
|
-
// Assign actions to role
|
|
965
|
-
interface AssignRoleActionsDto {
|
|
966
|
-
roleId: string;
|
|
967
|
-
items: PermissionItemDto[];
|
|
968
|
-
}
|
|
969
|
-
|
|
970
|
-
// Assign actions to company (whitelist)
|
|
971
|
-
interface AssignCompanyActionsDto {
|
|
972
|
-
companyId: string;
|
|
973
|
-
items: PermissionItemDto[];
|
|
974
|
-
}
|
|
975
|
-
```
|
|
976
|
-
|
|
977
|
-
### Query DTOs
|
|
978
|
-
|
|
979
|
-
```typescript
|
|
980
|
-
// Query user's permissions (my-permissions)
|
|
981
|
-
interface MyPermissionsQueryDto {
|
|
982
|
-
parentCodes?: string[]; // Filter by parent action codes
|
|
983
|
-
}
|
|
984
|
-
|
|
985
|
-
// Query action tree
|
|
986
|
-
interface ActionTreeQueryDto {
|
|
987
|
-
search?: string; // Filter by name or code
|
|
988
|
-
isActive?: boolean; // Filter by active status
|
|
989
|
-
withDeleted?: boolean; // Include deleted actions
|
|
990
|
-
}
|
|
991
|
-
```
|
|
992
|
-
|
|
993
|
-
### Response DTOs
|
|
994
|
-
|
|
995
|
-
```typescript
|
|
996
|
-
// My-permissions response
|
|
997
|
-
interface MyPermissionsResponseDto {
|
|
998
|
-
frontendActions: FrontendActionDto[];
|
|
999
|
-
cachedEndpoints: number; // Count of backend codes cached
|
|
1000
|
-
}
|
|
460
|
+
---
|
|
1001
461
|
|
|
1002
|
-
|
|
1003
|
-
id: string;
|
|
1004
|
-
code: string;
|
|
1005
|
-
name: string;
|
|
1006
|
-
description: string | null;
|
|
1007
|
-
}
|
|
462
|
+
**Permission cache not invalidating after role change**
|
|
1008
463
|
|
|
1009
|
-
|
|
1010
|
-
interface PermissionOperationResultDto {
|
|
1011
|
-
success: boolean;
|
|
1012
|
-
added: number;
|
|
1013
|
-
removed: number;
|
|
1014
|
-
message: string;
|
|
1015
|
-
}
|
|
1016
|
-
```
|
|
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.
|
|
1017
465
|
|
|
1018
466
|
---
|
|
1019
467
|
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
### Exports
|
|
468
|
+
**`No metadata for entity` on startup**
|
|
1023
469
|
|
|
1024
|
-
|
|
1025
|
-
// Config
|
|
1026
|
-
export { IAM_MODULE_OPTIONS } from './config';
|
|
1027
|
-
|
|
1028
|
-
// Modules
|
|
1029
|
-
export { IAMModule } from './modules';
|
|
1030
|
-
|
|
1031
|
-
// Entities
|
|
1032
|
-
export { Action } from './entities/action.entity';
|
|
1033
|
-
export { ActionBase } from './entities/action-base.entity';
|
|
1034
|
-
export { Role } from './entities/role.entity';
|
|
1035
|
-
export { RoleBase } from './entities/role-base.entity';
|
|
1036
|
-
export { RoleWithCompany } from './entities/role-with-company.entity';
|
|
1037
|
-
export { PermissionBase } from './entities/permission-base.entity';
|
|
1038
|
-
export { UserIamPermission } from './entities/user-iam-permission.entity';
|
|
1039
|
-
export { UserIamPermissionWithCompany } from './entities/permission-with-company.entity';
|
|
1040
|
-
export { IAMCoreEntities, IAMCompanyEntities, IAMAllEntities, getIAMEntitiesByConfig } from './entities';
|
|
1041
|
-
|
|
1042
|
-
// Services
|
|
1043
|
-
export { ActionService } from './services/action.service';
|
|
1044
|
-
export { RoleService } from './services/role.service';
|
|
1045
|
-
export { PermissionService } from './services/permission.service';
|
|
1046
|
-
export { PermissionCacheService } from './services/permission-cache.service';
|
|
1047
|
-
export { IAMConfigService } from './services/iam-config.service';
|
|
1048
|
-
export { IAMDataSourceService } from './services/iam-datasource.service';
|
|
1049
|
-
|
|
1050
|
-
// Helpers
|
|
1051
|
-
export { PermissionModeHelper } from './helpers/permission-mode.helper';
|
|
1052
|
-
export { validateCompanyAccess } from './helpers/company-access.helper';
|
|
1053
|
-
|
|
1054
|
-
// Enums
|
|
1055
|
-
export { ActionType } from './enums/action-type.enum';
|
|
1056
|
-
export { IAMPermissionMode } from './enums/permission-type.enum';
|
|
1057
|
-
|
|
1058
|
-
// Types
|
|
1059
|
-
export { LogicNode } from './types/logic-node.type';
|
|
1060
|
-
|
|
1061
|
-
// DTOs
|
|
1062
|
-
export * from './dtos';
|
|
1063
|
-
// Key DTOs:
|
|
1064
|
-
// - CreateActionDto, UpdateActionDto, ActionResponseDto, ActionTreeDto, ActionTreeQueryDto
|
|
1065
|
-
// - CreateRoleDto, UpdateRoleDto, RoleResponseDto
|
|
1066
|
-
// - PermissionAction (enum: ADD, REMOVE), PermissionItemDto
|
|
1067
|
-
// - AssignUserActionsDto, AssignRoleActionsDto, AssignUserRolesDto, AssignCompanyActionsDto
|
|
1068
|
-
// - GetUserActionsDto, GetRoleActionsDto, GetUserRolesDto, GetCompanyActionsDto
|
|
1069
|
-
// - UserActionResponseDto, RoleActionResponseDto, UserRoleResponseDto, CompanyActionResponseDto
|
|
1070
|
-
// - MyPermissionsQueryDto, MyPermissionsResponseDto, FrontendActionDto
|
|
1071
|
-
// - PermissionOperationResultDto
|
|
1072
|
-
|
|
1073
|
-
// Interfaces
|
|
1074
|
-
export * from './interfaces';
|
|
1075
|
-
// Key Interfaces:
|
|
1076
|
-
// - IAction, IActionTree (for action responses)
|
|
1077
|
-
// - IRole (for role responses)
|
|
1078
|
-
// - IIAMModuleConfig, IAMModuleOptions, IAMOptionsFactory, IAMModuleAsyncOptions
|
|
1079
|
-
|
|
1080
|
-
// Swagger Config
|
|
1081
|
-
export { iamSwaggerConfig } from './docs';
|
|
1082
|
-
```
|
|
1083
|
-
|
|
1084
|
-
### Swagger Configuration
|
|
470
|
+
Use `IAMModule.getEntities()` with both `enableCompanyFeature` and `permissionMode` matching your `bootstrapAppConfig`:
|
|
1085
471
|
|
|
1086
472
|
```typescript
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
// Generate swagger config based on features
|
|
1090
|
-
const swaggerOptions = iamSwaggerConfig(
|
|
1091
|
-
enableCompanyFeature, // boolean
|
|
1092
|
-
permissionMode, // IAMPermissionMode
|
|
1093
|
-
);
|
|
1094
|
-
|
|
1095
|
-
// Returns IModuleSwaggerOptions with:
|
|
1096
|
-
// - title, description, version, path, bearerAuth
|
|
1097
|
-
// - excludeSchemaProperties (hides companyId/branchId when company disabled)
|
|
1098
|
-
// - excludeTags (hides unused controllers based on mode)
|
|
1099
|
-
// - excludeQueryParameters (hides unused query params)
|
|
473
|
+
...IAMModule.getEntities({ enableCompanyFeature: true, permissionMode: 'FULL' })
|
|
1100
474
|
```
|
|
1101
475
|
|
|
1102
|
-
|
|
1103
|
-
- Excludes Auth-related tags (Authentication, Users, Companies, etc.)
|
|
1104
|
-
- Hides Company Action endpoints when company feature is disabled
|
|
1105
|
-
- Hides RBAC endpoints (Roles, User-Roles) in DIRECT mode
|
|
1106
|
-
- Hides Direct endpoints (User-Actions) in RBAC mode
|
|
1107
|
-
- Shows appropriate documentation based on current configuration
|
|
476
|
+
---
|
|
1108
477
|
|
|
1109
|
-
|
|
478
|
+
**Company action whitelist blocks all permissions**
|
|
1110
479
|
|
|
1111
|
-
|
|
1112
|
-
// Configure module
|
|
1113
|
-
IAMModule.forRoot(options?: IAMModuleOptions): DynamicModule
|
|
1114
|
-
IAMModule.forRootAsync(options: IAMModuleAsyncOptions): DynamicModule
|
|
1115
|
-
IAMModule.forFeature(options?: IAMModuleOptions): DynamicModule
|
|
1116
|
-
```
|
|
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`.
|
|
1117
481
|
|
|
1118
482
|
---
|
|
1119
483
|
|
|
1120
|
-
##
|
|
484
|
+
## License
|
|
1121
485
|
|
|
1122
|
-
|
|
1123
|
-
nestjs-iam/src/
|
|
1124
|
-
├── config/
|
|
1125
|
-
│ ├── iam.constants.ts # IAM_MODULE_OPTIONS token
|
|
1126
|
-
│ └── index.ts
|
|
1127
|
-
├── modules/
|
|
1128
|
-
│ └── iam.module.ts # Main module with dynamic config
|
|
1129
|
-
├── entities/
|
|
1130
|
-
│ ├── action-base.entity.ts # Action base fields
|
|
1131
|
-
│ ├── action.entity.ts # Action entity
|
|
1132
|
-
│ ├── role-base.entity.ts # Role base fields
|
|
1133
|
-
│ ├── role.entity.ts # Role (no company)
|
|
1134
|
-
│ ├── role-with-company.entity.ts
|
|
1135
|
-
│ ├── permission-base.entity.ts # Permission base fields
|
|
1136
|
-
│ ├── user-iam-permission.entity.ts
|
|
1137
|
-
│ ├── permission-with-company.entity.ts
|
|
1138
|
-
│ └── index.ts # Entity groups & getIAMEntitiesByConfig
|
|
1139
|
-
├── services/
|
|
1140
|
-
│ ├── action.service.ts # Action CRUD
|
|
1141
|
-
│ ├── role.service.ts # Role CRUD
|
|
1142
|
-
│ ├── permission.service.ts # Permission management
|
|
1143
|
-
│ ├── permission-cache.service.ts # Cache management
|
|
1144
|
-
│ ├── iam-config.service.ts # Configuration
|
|
1145
|
-
│ └── iam-datasource.service.ts # DataSource provider
|
|
1146
|
-
├── controllers/
|
|
1147
|
-
│ ├── action.controller.ts
|
|
1148
|
-
│ ├── role.controller.ts
|
|
1149
|
-
│ ├── my-permission.controller.ts
|
|
1150
|
-
│ ├── role-permission.controller.ts
|
|
1151
|
-
│ ├── user-action-permission.controller.ts
|
|
1152
|
-
│ └── company-action-permission.controller.ts
|
|
1153
|
-
├── helpers/
|
|
1154
|
-
│ ├── company-access.helper.ts # Company access validation
|
|
1155
|
-
│ └── permission-mode.helper.ts # String to enum conversion
|
|
1156
|
-
├── dtos/
|
|
1157
|
-
│ ├── action.dto.ts
|
|
1158
|
-
│ ├── role.dto.ts
|
|
1159
|
-
│ └── permission.dto.ts
|
|
1160
|
-
├── interfaces/
|
|
1161
|
-
│ ├── action.interface.ts
|
|
1162
|
-
│ ├── role.interface.ts
|
|
1163
|
-
│ └── iam-module-options.interface.ts
|
|
1164
|
-
├── enums/
|
|
1165
|
-
│ ├── action-type.enum.ts
|
|
1166
|
-
│ └── permission-type.enum.ts
|
|
1167
|
-
├── types/
|
|
1168
|
-
│ └── logic-node.type.ts
|
|
1169
|
-
├── docs/
|
|
1170
|
-
│ └── iam-swagger.config.ts
|
|
1171
|
-
└── index.ts
|
|
1172
|
-
```
|
|
486
|
+
MIT © FLUSYS
|
|
1173
487
|
|
|
1174
488
|
---
|
|
1175
489
|
|
|
1176
|
-
**
|
|
490
|
+
> Part of the **FLUSYS** framework — a full-stack monorepo powering Angular 21 + NestJS 11 applications.
|