@flusys/ng-iam 1.1.1-beta → 1.2.0-rc.1
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 +44 -1027
- package/fesm2022/flusys-ng-iam-action-form-page.component-Vc5Toouu.mjs +502 -0
- package/fesm2022/flusys-ng-iam-action-form-page.component-Vc5Toouu.mjs.map +1 -0
- package/fesm2022/flusys-ng-iam-action-list-page.component-UGKclpFx.mjs +348 -0
- package/fesm2022/flusys-ng-iam-action-list-page.component-UGKclpFx.mjs.map +1 -0
- package/fesm2022/flusys-ng-iam-flusys-ng-iam-CrycDULz.mjs +4561 -0
- package/fesm2022/flusys-ng-iam-flusys-ng-iam-CrycDULz.mjs.map +1 -0
- package/fesm2022/flusys-ng-iam-iam-container.component-D0m8u6nu.mjs +105 -0
- package/fesm2022/flusys-ng-iam-iam-container.component-D0m8u6nu.mjs.map +1 -0
- package/fesm2022/flusys-ng-iam-permission-page.component-x88X4AgJ.mjs +143 -0
- package/fesm2022/flusys-ng-iam-permission-page.component-x88X4AgJ.mjs.map +1 -0
- package/fesm2022/flusys-ng-iam-role-form-page.component-InsxdriD.mjs +335 -0
- package/fesm2022/flusys-ng-iam-role-form-page.component-InsxdriD.mjs.map +1 -0
- package/fesm2022/flusys-ng-iam-role-list-page.component-8NGXCeSZ.mjs +389 -0
- package/fesm2022/flusys-ng-iam-role-list-page.component-8NGXCeSZ.mjs.map +1 -0
- package/fesm2022/flusys-ng-iam.mjs +1 -1
- package/package.json +11 -10
- package/types/flusys-ng-iam.d.ts +107 -686
- package/fesm2022/flusys-ng-iam-action-form-page.component-C1j10Qhw.mjs +0 -467
- package/fesm2022/flusys-ng-iam-action-form-page.component-C1j10Qhw.mjs.map +0 -1
- package/fesm2022/flusys-ng-iam-action-list-page.component-BCzSardO.mjs +0 -281
- package/fesm2022/flusys-ng-iam-action-list-page.component-BCzSardO.mjs.map +0 -1
- package/fesm2022/flusys-ng-iam-flusys-ng-iam-DISrddPh.mjs +0 -4940
- package/fesm2022/flusys-ng-iam-flusys-ng-iam-DISrddPh.mjs.map +0 -1
- package/fesm2022/flusys-ng-iam-iam-container.component-BkhqmzLi.mjs +0 -97
- package/fesm2022/flusys-ng-iam-iam-container.component-BkhqmzLi.mjs.map +0 -1
- package/fesm2022/flusys-ng-iam-permission-page.component-BSQFPt_N.mjs +0 -143
- package/fesm2022/flusys-ng-iam-permission-page.component-BSQFPt_N.mjs.map +0 -1
- package/fesm2022/flusys-ng-iam-role-form-page.component-Cqziu_BM.mjs +0 -314
- package/fesm2022/flusys-ng-iam-role-form-page.component-Cqziu_BM.mjs.map +0 -1
- package/fesm2022/flusys-ng-iam-role-list-page.component-BObCxHiB.mjs +0 -266
- package/fesm2022/flusys-ng-iam-role-list-page.component-BObCxHiB.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -1,1078 +1,95 @@
|
|
|
1
|
-
# @flusys/ng-iam
|
|
1
|
+
# @flusys/ng-iam
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Identity and Access Management UI for FLUSYS Angular apps — role management, permission assignment, and reactive permission checking via signals.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
**Key Principle:** ng-iam is **100% independent** from ng-auth through the **Provider Interface Pattern**. No compile-time or runtime dependencies on ng-auth.
|
|
8
|
-
|
|
9
|
-
## Package Information
|
|
10
|
-
|
|
11
|
-
| Item | Value |
|
|
12
|
-
|------|-------|
|
|
13
|
-
| Package | `@flusys/ng-iam` |
|
|
14
|
-
| Version | 1.0.0 |
|
|
15
|
-
| Dependencies | ng-core, ng-shared, ng-layout (IMenuItem only) |
|
|
16
|
-
| NO dependency on | ng-auth (fully independent via Provider Pattern) |
|
|
17
|
-
| Build | `npm run build:ng-iam` |
|
|
18
|
-
| Entry point | `public-api.ts` |
|
|
19
|
-
|
|
20
|
-
---
|
|
21
|
-
|
|
22
|
-
## Table of Contents
|
|
23
|
-
|
|
24
|
-
1. [Architecture](#architecture)
|
|
25
|
-
2. [Provider Interfaces](#provider-interfaces)
|
|
26
|
-
3. [Integration Options](#integration-options)
|
|
27
|
-
4. [Permission Modes](#permission-modes)
|
|
28
|
-
5. [Routes](#routes)
|
|
29
|
-
6. [Services](#services)
|
|
30
|
-
7. [Components](#components)
|
|
31
|
-
8. [Page Components](#page-components)
|
|
32
|
-
9. [Interfaces](#interfaces)
|
|
33
|
-
10. [Utils & Constants](#utils--constants)
|
|
34
|
-
11. [Backend Integration](#backend-integration)
|
|
35
|
-
12. [Best Practices](#best-practices)
|
|
36
|
-
13. [Common Issues](#common-issues)
|
|
5
|
+
[](https://www.npmjs.com/package/@flusys/ng-iam)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
37
7
|
|
|
38
8
|
---
|
|
39
9
|
|
|
40
|
-
##
|
|
41
|
-
|
|
42
|
-
```
|
|
43
|
-
ng-shared (defines interfaces + tokens)
|
|
44
|
-
|
|
|
45
|
-
Your Auth System (implements interfaces with adapters)
|
|
46
|
-
|
|
|
47
|
-
ng-iam (consumes interfaces via DI)
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
ng-iam resolves all auth-related data at runtime via DI tokens defined in ng-shared. If a required token is not provided, Angular throws a clear error at injection time.
|
|
51
|
-
|
|
52
|
-
---
|
|
10
|
+
## Installation
|
|
53
11
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
ng-iam requires these provider interfaces:
|
|
57
|
-
|
|
58
|
-
| Token | Source | Purpose | Required? |
|
|
59
|
-
|-------|--------|---------|-----------|
|
|
60
|
-
| `USER_PROVIDER` | `@flusys/ng-shared` | User list for role/action assignment | Yes |
|
|
61
|
-
| `LAYOUT_AUTH_STATE` | `@flusys/ng-layout` | Current company/branch context | Yes (if company feature enabled) |
|
|
62
|
-
| `COMPANY_API_PROVIDER` | `@flusys/ng-shared` | Company list for whitelisting | Optional (for CompanyActionSelector) |
|
|
63
|
-
| `USER_PERMISSION_PROVIDER` | `@flusys/ng-shared` | User branch permissions | Optional (for branch-scoped permissions) |
|
|
64
|
-
|
|
65
|
-
### How Components Use Providers
|
|
66
|
-
|
|
67
|
-
```typescript
|
|
68
|
-
// UserActionSelectorComponent / UserRoleSelectorComponent
|
|
69
|
-
private readonly userProvider = inject(USER_PROVIDER);
|
|
70
|
-
private readonly companyContext = inject(LAYOUT_AUTH_STATE);
|
|
71
|
-
|
|
72
|
-
async loadUsers() {
|
|
73
|
-
const response = await firstValueFrom(
|
|
74
|
-
this.userProvider.getUsers({
|
|
75
|
-
page: 0,
|
|
76
|
-
pageSize: MAX_DROPDOWN_ITEMS,
|
|
77
|
-
companyId: this.companyContext.currentCompanyInfo()?.id || undefined,
|
|
78
|
-
})
|
|
79
|
-
);
|
|
80
|
-
this.users.set(response?.data ?? []);
|
|
81
|
-
}
|
|
12
|
+
```bash
|
|
13
|
+
npm install @flusys/ng-iam @flusys/ng-core @flusys/ng-shared
|
|
82
14
|
```
|
|
83
15
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
## Integration Options
|
|
87
|
-
|
|
88
|
-
### Option 1: With FLUSYS ng-auth (Recommended)
|
|
89
|
-
|
|
90
|
-
ng-auth provides pre-built adapters for all required interfaces.
|
|
16
|
+
## 1. Register Providers
|
|
91
17
|
|
|
92
18
|
```typescript
|
|
93
19
|
// app.config.ts
|
|
94
|
-
import {
|
|
20
|
+
import { provideIamProviders } from '@flusys/ng-iam';
|
|
95
21
|
|
|
96
22
|
export const appConfig: ApplicationConfig = {
|
|
97
23
|
providers: [
|
|
98
|
-
|
|
24
|
+
...provideIamProviders(), // registers PROFILE_PERMISSION_PROVIDER for ng-shared's permission directives/guards
|
|
99
25
|
],
|
|
100
26
|
};
|
|
101
27
|
```
|
|
102
28
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
| Adapter | Interface | Path |
|
|
106
|
-
|---------|-----------|------|
|
|
107
|
-
| `AuthUserProviderAdapter` | `IUserProvider` | `projects/ng-auth/adapters/` |
|
|
108
|
-
| `AuthLayoutStateAdapter` | `ILayoutAuthState` | `projects/ng-auth/adapters/` |
|
|
109
|
-
| `AuthCompanyApiProviderAdapter` | `ICompanyApiProvider` | `projects/ng-auth/adapters/` |
|
|
110
|
-
| `AuthUserPermissionProviderAdapter` | `IUserPermissionProvider` | `projects/ng-auth/adapters/` |
|
|
111
|
-
|
|
112
|
-
### Option 2: With Custom Auth System
|
|
113
|
-
|
|
114
|
-
Implement the required interfaces with your own auth system. No ng-auth dependency needed.
|
|
115
|
-
|
|
116
|
-
```typescript
|
|
117
|
-
// Minimum configuration (basic IAM)
|
|
118
|
-
providers: [
|
|
119
|
-
{ provide: USER_PROVIDER, useClass: YourUserProvider },
|
|
120
|
-
{ provide: LAYOUT_AUTH_STATE, useClass: YourCompanyContextProvider },
|
|
121
|
-
]
|
|
122
|
-
|
|
123
|
-
// Full configuration (company + branch permissions)
|
|
124
|
-
providers: [
|
|
125
|
-
{ provide: USER_PROVIDER, useClass: YourUserProvider },
|
|
126
|
-
{ provide: LAYOUT_AUTH_STATE, useClass: YourCompanyContextProvider },
|
|
127
|
-
{ provide: COMPANY_API_PROVIDER, useClass: YourCompanyApiProvider },
|
|
128
|
-
{ provide: USER_PERMISSION_PROVIDER, useClass: YourUserPermissionProvider },
|
|
129
|
-
]
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
**Feature matrix:**
|
|
133
|
-
|
|
134
|
-
| Feature | Providers Needed |
|
|
135
|
-
|---------|-----------------|
|
|
136
|
-
| Basic role assignments | USER_PROVIDER only |
|
|
137
|
-
| Company-scoped permissions | + LAYOUT_AUTH_STATE, COMPANY_API_PROVIDER |
|
|
138
|
-
| Branch-scoped permissions | + LAYOUT_AUTH_STATE, USER_PERMISSION_PROVIDER |
|
|
139
|
-
| Direct user permissions | + USER_PERMISSION_PROVIDER |
|
|
140
|
-
|
|
141
|
-
All adapters must return `IListResponse<T>` or `ISingleResponse<T>` format from `@flusys/ng-shared`.
|
|
142
|
-
|
|
143
|
-
---
|
|
144
|
-
|
|
145
|
-
## Permission Modes
|
|
146
|
-
|
|
147
|
-
FLUSYS IAM supports three permission modes configured via `APP_CONFIG.iam.permissionMode`:
|
|
148
|
-
|
|
149
|
-
| Mode | Structure | Tabs Available | Use Case |
|
|
150
|
-
|------|-----------|---------------|----------|
|
|
151
|
-
| **DIRECT** | User -> Actions | User-Actions | Small apps, per-user permissions |
|
|
152
|
-
| **RBAC** | User -> Role -> Actions | Role-Actions, User-Roles | Standard apps, role templates |
|
|
153
|
-
| **FULL** | Both combined | Role-Actions, User-Roles, User-Actions | Maximum flexibility |
|
|
154
|
-
|
|
155
|
-
When company feature is enabled, a **Company-Actions** tab is added for whitelisting actions per company.
|
|
156
|
-
|
|
157
|
-
---
|
|
158
|
-
|
|
159
|
-
## Routes
|
|
160
|
-
|
|
161
|
-
**Export:** `IAM_ROUTES` from `@flusys/ng-iam`
|
|
162
|
-
|
|
163
|
-
```
|
|
164
|
-
/iam (IamContainerComponent - tabbed navigation)
|
|
165
|
-
/actions (ActionListPageComponent - hierarchical tree)
|
|
166
|
-
/actions/new (ActionFormPageComponent - create)
|
|
167
|
-
/actions/:id (ActionFormPageComponent - edit)
|
|
168
|
-
/roles (RoleListPageComponent - paginated table)
|
|
169
|
-
/roles/new (RoleFormPageComponent - create)
|
|
170
|
-
/roles/:id (RoleFormPageComponent - edit)
|
|
171
|
-
/permissions (PermissionPageComponent - tabbed assignment UI)
|
|
172
|
-
(default redirect -> /actions)
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
**Usage in app routes:**
|
|
29
|
+
## 2. Add IAM Routes
|
|
176
30
|
|
|
177
31
|
```typescript
|
|
32
|
+
// app.routes.ts
|
|
178
33
|
import { IAM_ROUTES } from '@flusys/ng-iam';
|
|
179
34
|
|
|
180
35
|
export const routes: Routes = [
|
|
181
|
-
{
|
|
36
|
+
{
|
|
37
|
+
path: 'iam',
|
|
38
|
+
loadChildren: () => IAM_ROUTES,
|
|
39
|
+
},
|
|
182
40
|
];
|
|
183
41
|
```
|
|
184
42
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
## Services
|
|
188
|
-
|
|
189
|
-
### PermissionStateService
|
|
190
|
-
|
|
191
|
-
Manages current user's permissions with signal-based state. Provided at root level.
|
|
192
|
-
|
|
193
|
-
**Dependencies:** `MyPermissionsApiService`, `PermissionValidatorService` (from ng-shared)
|
|
194
|
-
|
|
195
|
-
**Signals:**
|
|
196
|
-
|
|
197
|
-
| Signal | Type | Description |
|
|
198
|
-
|--------|------|-------------|
|
|
199
|
-
| `permissions` | `Signal<IMyPermissionsResponseDto \| null>` | Current user permissions (readonly) |
|
|
200
|
-
| `isLoading` | `Signal<boolean>` | Loading state (readonly) |
|
|
201
|
-
|
|
202
|
-
**Methods:**
|
|
203
|
-
|
|
204
|
-
| Method | Returns | Description |
|
|
205
|
-
|--------|---------|-------------|
|
|
206
|
-
| `loadPermissions(dto?)` | `Observable<void>` | Load permissions from `/iam/permissions/my-permissions` |
|
|
207
|
-
| `isLoaded()` | `boolean` | Check if permissions have been loaded |
|
|
208
|
-
|
|
209
|
-
Permission checking is delegated to `PermissionValidatorService` from ng-shared (hasAction, hasAllActions, hasAnyAction, getActionCodes).
|
|
210
|
-
|
|
211
|
-
```typescript
|
|
212
|
-
const permissionState = inject(PermissionStateService);
|
|
213
|
-
|
|
214
|
-
// Load permissions (typically in guard)
|
|
215
|
-
await firstValueFrom(permissionState.loadPermissions());
|
|
216
|
-
|
|
217
|
-
// Check permissions (via PermissionValidatorService)
|
|
218
|
-
const validator = inject(PermissionValidatorService);
|
|
219
|
-
validator.hasAction('user.create');
|
|
220
|
-
validator.hasAllActions(['user.create', 'user.update']);
|
|
221
|
-
validator.hasAnyAction(['user.view', 'user.list']);
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
---
|
|
225
|
-
|
|
226
|
-
### ActionApiService
|
|
227
|
-
|
|
228
|
-
Extends `ApiResourceService<IUpdateActionDto, IAction>` for action CRUD.
|
|
229
|
-
|
|
230
|
-
**Resource:** `iam/actions`
|
|
231
|
-
|
|
232
|
-
**Additional Methods:**
|
|
233
|
-
|
|
234
|
-
| Method | Endpoint | Description |
|
|
235
|
-
|--------|----------|-------------|
|
|
236
|
-
| `getTree(search?, isActive?, withDeleted?)` | `POST /iam/actions/tree` | Get hierarchical action tree |
|
|
237
|
-
| `getActionsForPermission()` | `POST /iam/actions/tree-for-permission` | Get actions filtered by company whitelist |
|
|
238
|
-
|
|
239
|
-
Inherits standard CRUD from `ApiResourceService`: `getAll`, `getById`, `insert`, `update`, `delete`.
|
|
240
|
-
|
|
241
|
-
---
|
|
242
|
-
|
|
243
|
-
### RoleApiService
|
|
244
|
-
|
|
245
|
-
Extends `ApiResourceService<IUpdateRoleDto, IRole>` for role CRUD.
|
|
246
|
-
|
|
247
|
-
**Resource:** `iam/roles`
|
|
248
|
-
|
|
249
|
-
Inherits standard CRUD from `ApiResourceService`. Only available in RBAC/FULL mode.
|
|
250
|
-
|
|
251
|
-
---
|
|
252
|
-
|
|
253
|
-
### PermissionApiService
|
|
254
|
-
|
|
255
|
-
Extends `BaseApiService` for all permission assignment operations.
|
|
256
|
-
|
|
257
|
-
**Base:** `super('iam')` - all paths resolved via `this.getUrl(...)` relative to `/iam/`
|
|
258
|
-
|
|
259
|
-
**User -> Action (Direct Permissions):**
|
|
260
|
-
|
|
261
|
-
| Method | Endpoint | Description |
|
|
262
|
-
|--------|----------|-------------|
|
|
263
|
-
| `assignUserActions(data)` | `POST /iam/permissions/user-actions/assign` | Assign/remove actions for user |
|
|
264
|
-
| `getUserActions(userId, query?)` | `GET /iam/permissions/user-actions/{userId}` | Get user's direct actions |
|
|
265
|
-
|
|
266
|
-
**User -> Role:**
|
|
267
|
-
|
|
268
|
-
| Method | Endpoint | Description |
|
|
269
|
-
|--------|----------|-------------|
|
|
270
|
-
| `assignUserRoles(data)` | `POST /iam/permissions/user-roles/assign` | Assign/remove roles for user |
|
|
271
|
-
| `getUserRoles(userId, query?)` | `GET /iam/permissions/user-roles/{userId}` | Get user's roles |
|
|
272
|
-
|
|
273
|
-
**Role -> Action:**
|
|
274
|
-
|
|
275
|
-
| Method | Endpoint | Description |
|
|
276
|
-
|--------|----------|-------------|
|
|
277
|
-
| `assignRoleActions(data)` | `POST /iam/permissions/role-actions/assign` | Assign/remove actions for role |
|
|
278
|
-
| `getRoleActions(roleId, query?)` | `GET /iam/permissions/role-actions/{roleId}` | Get role's actions |
|
|
279
|
-
|
|
280
|
-
**Company -> Action (Whitelisting):**
|
|
281
|
-
|
|
282
|
-
| Method | Endpoint | Description |
|
|
283
|
-
|--------|----------|-------------|
|
|
284
|
-
| `assignCompanyActions(data)` | `POST /iam/permissions/company-actions/assign` | Assign/remove company whitelist |
|
|
285
|
-
| `getCompanyActions(companyId)` | `GET /iam/permissions/company-actions/{companyId}` | Get company's whitelisted actions |
|
|
286
|
-
|
|
287
|
-
> **Note:** Read operations use GET (exception to the POST-only RPC convention). Write operations use POST.
|
|
288
|
-
|
|
289
|
-
---
|
|
290
|
-
|
|
291
|
-
### MyPermissionsApiService
|
|
292
|
-
|
|
293
|
-
Fetches current user's permissions.
|
|
294
|
-
|
|
295
|
-
**Method:**
|
|
296
|
-
|
|
297
|
-
| Method | Endpoint | Description |
|
|
298
|
-
|--------|----------|-------------|
|
|
299
|
-
| `getMyPermissions(dto?)` | `POST /iam/permissions/my-permissions` | Get current user's frontend actions and cached endpoint count |
|
|
300
|
-
|
|
301
|
-
---
|
|
302
|
-
|
|
303
|
-
### ActionPermissionLogicService
|
|
304
|
-
|
|
305
|
-
Manages smart prerequisite validation using AND/OR logic trees. Uses PrimeNG `ConfirmationService` and `MessageService` for dialogs.
|
|
306
|
-
|
|
307
|
-
**Core Methods:**
|
|
308
|
-
|
|
309
|
-
| Method | Description |
|
|
310
|
-
|--------|-------------|
|
|
311
|
-
| `handleCheck(action, selection, allActions, onUpdate, onCancel)` | Handle action selection with recursive prerequisite validation |
|
|
312
|
-
| `handleUncheck(action, selection, allActions, onUpdate)` | Handle deselection with dependency detection |
|
|
313
|
-
| `hasUnmetPrerequisites(action, selection, allActions)` | Check if action has unmet prerequisites |
|
|
314
|
-
| `getActionsWithUnmetPrerequisites(selection, allActions)` | Get all selected actions with unmet prerequisites |
|
|
315
|
-
| `showValidationErrorDialog(invalid, selection, allActions, onUpdate)` | Pre-save validation error dialog with auto-fix |
|
|
316
|
-
| `getPrerequisiteTooltip(action, selection, allActions)` | Get tooltip text with prerequisite info |
|
|
317
|
-
|
|
318
|
-
**Features:**
|
|
319
|
-
- Recursive deep-scan for cascading prerequisites
|
|
320
|
-
- Smart AND/OR optimization (selects minimum required actions)
|
|
321
|
-
- Visual formatting with status indicators
|
|
322
|
-
- Alternative suggestions for OR logic branches
|
|
323
|
-
- Pre-save validation with auto-fix capability
|
|
324
|
-
|
|
325
|
-
---
|
|
326
|
-
|
|
327
|
-
## Components
|
|
328
|
-
|
|
329
|
-
### LogicBuilderComponent
|
|
330
|
-
|
|
331
|
-
Visual AND/OR permission logic tree editor.
|
|
332
|
-
|
|
333
|
-
| Property | Value |
|
|
334
|
-
|----------|-------|
|
|
335
|
-
| Selector | `lib-logic-builder` |
|
|
336
|
-
| Standalone | Yes |
|
|
337
|
-
| Change Detection | OnPush |
|
|
338
|
-
|
|
339
|
-
**Inputs:**
|
|
340
|
-
|
|
341
|
-
| Input | Type | Description |
|
|
342
|
-
|-------|------|-------------|
|
|
343
|
-
| `logic` | `ILogicNode \| null` | Current permission logic tree |
|
|
344
|
-
| `actions` | `Array<{id, name}>` | Available actions for dropdowns |
|
|
345
|
-
|
|
346
|
-
**Outputs:**
|
|
347
|
-
|
|
348
|
-
| Output | Type | Description |
|
|
349
|
-
|--------|------|-------------|
|
|
350
|
-
| `logicChange` | `ILogicNode \| null` | Emitted when logic changes |
|
|
351
|
-
|
|
352
|
-
**Features:** Recursive tree builder, AND/OR operator toggling, action selector dropdowns, node add/remove.
|
|
353
|
-
|
|
354
|
-
---
|
|
355
|
-
|
|
356
|
-
### UserActionSelectorComponent
|
|
357
|
-
|
|
358
|
-
Assigns actions directly to users (DIRECT/FULL mode).
|
|
359
|
-
|
|
360
|
-
| Property | Value |
|
|
361
|
-
|----------|-------|
|
|
362
|
-
| Selector | `flusys-user-action-selector` |
|
|
363
|
-
| Standalone | Yes |
|
|
364
|
-
| Change Detection | OnPush |
|
|
365
|
-
|
|
366
|
-
**Dependencies:** `APP_CONFIG`, `LAYOUT_AUTH_STATE`, `USER_PROVIDER`, `USER_PERMISSION_PROVIDER`, `ActionApiService`, `ActionPermissionLogicService`, `PermissionApiService`, `MessageService`
|
|
367
|
-
|
|
368
|
-
**Features:**
|
|
369
|
-
- User dropdown (loaded from USER_PROVIDER)
|
|
370
|
-
- Branch selector (conditional, loaded from USER_PERMISSION_PROVIDER)
|
|
371
|
-
- Hierarchical TreeTable with checkboxes
|
|
372
|
-
- Action type tags (backend/frontend/both)
|
|
373
|
-
- Prerequisite validation with tooltips
|
|
374
|
-
- Change tracking with pending add/remove summary
|
|
375
|
-
- Select/deselect all
|
|
376
|
-
- Pre-save validation
|
|
377
|
-
|
|
378
|
-
---
|
|
379
|
-
|
|
380
|
-
### UserRoleSelectorComponent
|
|
381
|
-
|
|
382
|
-
Assigns roles to users (RBAC/FULL mode).
|
|
383
|
-
|
|
384
|
-
| Property | Value |
|
|
385
|
-
|----------|-------|
|
|
386
|
-
| Selector | `flusys-user-role-selector` |
|
|
387
|
-
| Standalone | Yes |
|
|
388
|
-
| Change Detection | OnPush |
|
|
389
|
-
|
|
390
|
-
**Dependencies:** `APP_CONFIG`, `LAYOUT_AUTH_STATE`, `USER_PROVIDER`, `USER_PERMISSION_PROVIDER`, `RoleApiService`, `PermissionApiService`, `MessageService`
|
|
391
|
-
|
|
392
|
-
**Features:**
|
|
393
|
-
- User dropdown with branch selector
|
|
394
|
-
- DataTable with pagination
|
|
395
|
-
- Change tracking with pending add/remove summary
|
|
396
|
-
- Select/deselect all
|
|
397
|
-
- No prerequisite validation (roles are simple assignments)
|
|
398
|
-
|
|
399
|
-
---
|
|
400
|
-
|
|
401
|
-
### RoleActionSelectorComponent
|
|
402
|
-
|
|
403
|
-
Assigns actions to roles (RBAC/FULL mode).
|
|
404
|
-
|
|
405
|
-
| Property | Value |
|
|
406
|
-
|----------|-------|
|
|
407
|
-
| Selector | `flusys-role-action-selector` |
|
|
408
|
-
| Standalone | Yes |
|
|
409
|
-
| Change Detection | OnPush |
|
|
410
|
-
|
|
411
|
-
**Dependencies:** `RoleApiService`, `ActionApiService`, `PermissionApiService`, `MessageService`, `ActionPermissionLogicService`
|
|
412
|
-
|
|
413
|
-
**Features:**
|
|
414
|
-
- Role dropdown
|
|
415
|
-
- Hierarchical TreeTable with checkboxes
|
|
416
|
-
- Prerequisite validation with smart dependency handling
|
|
417
|
-
- Request cancellation with AbortController
|
|
418
|
-
- Pre-save validation with auto-fix dialog
|
|
419
|
-
- Change tracking
|
|
420
|
-
|
|
421
|
-
---
|
|
422
|
-
|
|
423
|
-
### CompanyActionSelectorComponent
|
|
424
|
-
|
|
425
|
-
Manages company action whitelisting (when company feature enabled).
|
|
426
|
-
|
|
427
|
-
| Property | Value |
|
|
428
|
-
|----------|-------|
|
|
429
|
-
| Selector | `flusys-company-action-selector` |
|
|
430
|
-
| Standalone | Yes |
|
|
431
|
-
| Change Detection | OnPush |
|
|
432
|
-
|
|
433
|
-
**Dependencies:** `COMPANY_API_PROVIDER`, `ActionApiService`, `PermissionApiService`, `MessageService`, `ConfirmationService`, `ActionPermissionLogicService`
|
|
434
|
-
|
|
435
|
-
**Features:**
|
|
436
|
-
- Company dropdown (loaded from COMPANY_API_PROVIDER)
|
|
437
|
-
- Full action tree (not filtered by company whitelist)
|
|
438
|
-
- Prerequisite validation
|
|
439
|
-
- Backend prerequisite error handling with auto-fix
|
|
440
|
-
- Request cancellation with AbortController
|
|
441
|
-
|
|
442
|
-
---
|
|
443
|
-
|
|
444
|
-
## Page Components
|
|
445
|
-
|
|
446
|
-
> Page components are **not exported** from the public API. They are internal and accessed via `IAM_ROUTES` (lazy-loaded by the router).
|
|
447
|
-
|
|
448
|
-
### IamContainerComponent
|
|
449
|
-
|
|
450
|
-
Tabbed navigation container for IAM pages.
|
|
451
|
-
|
|
452
|
-
| Property | Value |
|
|
453
|
-
|----------|-------|
|
|
454
|
-
| Selector | `lib-iam-container` |
|
|
455
|
-
| Tabs | Actions (always), Roles (RBAC/FULL), Permissions (always) |
|
|
456
|
-
|
|
457
|
-
Uses `RouterLink` and `RouterLinkActive` for navigation. Tab visibility based on permission mode.
|
|
458
|
-
|
|
459
|
-
---
|
|
460
|
-
|
|
461
|
-
### ActionListPageComponent
|
|
462
|
-
|
|
463
|
-
Hierarchical action list with CRUD operations.
|
|
464
|
-
|
|
465
|
-
| Property | Value |
|
|
466
|
-
|----------|-------|
|
|
467
|
-
| Selector | `lib-action-list-page` |
|
|
468
|
-
| Features | TreeTable display, create/edit/delete, read-only indicators, company context awareness |
|
|
469
|
-
|
|
470
|
-
Reloads actions when company context changes via effect.
|
|
471
|
-
|
|
472
|
-
---
|
|
473
|
-
|
|
474
|
-
### ActionFormPageComponent
|
|
475
|
-
|
|
476
|
-
Action create/edit form with LogicBuilder integration.
|
|
477
|
-
|
|
478
|
-
| Property | Value |
|
|
479
|
-
|----------|-------|
|
|
480
|
-
| Selector | `lib-action-form-page` |
|
|
481
|
-
| Features | Create/edit mode, parent action selector, ActionType enum, permissionLogic editor, display order, active flag |
|
|
482
|
-
|
|
483
|
-
**Form fields:** name (required), description, code, actionType, permissionLogic, parentId, serial, isActive, metadata.
|
|
484
|
-
|
|
485
|
-
---
|
|
486
|
-
|
|
487
|
-
### RoleListPageComponent
|
|
488
|
-
|
|
489
|
-
Paginated role list with CRUD operations.
|
|
490
|
-
|
|
491
|
-
| Property | Value |
|
|
492
|
-
|----------|-------|
|
|
493
|
-
| Selector | `lib-role-list-page` |
|
|
494
|
-
| Features | Paginated table (10/25/50), create/edit/delete, company context filtering, read-only indicators |
|
|
495
|
-
|
|
496
|
-
---
|
|
497
|
-
|
|
498
|
-
### RoleFormPageComponent
|
|
499
|
-
|
|
500
|
-
Role create/edit form.
|
|
501
|
-
|
|
502
|
-
| Property | Value |
|
|
503
|
-
|----------|-------|
|
|
504
|
-
| Selector | `lib-role-form-page` |
|
|
505
|
-
| Features | Create/edit mode, company auto-assignment on create, active flag, display order |
|
|
506
|
-
|
|
507
|
-
**Form fields:** name (required), description, serial, isActive. Company assigned automatically from context.
|
|
508
|
-
|
|
509
|
-
---
|
|
510
|
-
|
|
511
|
-
### PermissionPageComponent
|
|
512
|
-
|
|
513
|
-
Main permission management interface with tabbed layout.
|
|
514
|
-
|
|
515
|
-
| Property | Value |
|
|
516
|
-
|----------|-------|
|
|
517
|
-
| Selector | `lib-permission-page` |
|
|
518
|
-
| Features | Dynamic tabs based on permission mode and company feature |
|
|
519
|
-
|
|
520
|
-
**Tabs:**
|
|
521
|
-
- Role-Actions (RBAC/FULL) - `RoleActionSelectorComponent`
|
|
522
|
-
- User-Roles (RBAC/FULL) - `UserRoleSelectorComponent`
|
|
523
|
-
- User-Actions (DIRECT/FULL) - `UserActionSelectorComponent`
|
|
524
|
-
- Company-Actions (if company feature enabled) - `CompanyActionSelectorComponent`
|
|
525
|
-
|
|
526
|
-
---
|
|
527
|
-
|
|
528
|
-
## Interfaces
|
|
529
|
-
|
|
530
|
-
### ActionType (Enum)
|
|
531
|
-
|
|
532
|
-
```typescript
|
|
533
|
-
enum ActionType {
|
|
534
|
-
BACKEND = 'backend', // API endpoint permissions (cached by PermissionGuard)
|
|
535
|
-
FRONTEND = 'frontend', // UI feature permissions (returned in my-permissions)
|
|
536
|
-
BOTH = 'both', // Both backend and frontend
|
|
537
|
-
}
|
|
538
|
-
```
|
|
539
|
-
|
|
540
|
-
### IAction (extends IBaseEntity)
|
|
541
|
-
|
|
542
|
-
```typescript
|
|
543
|
-
interface IAction extends IBaseEntity {
|
|
544
|
-
readOnly: boolean;
|
|
545
|
-
name: string;
|
|
546
|
-
description: string | null;
|
|
547
|
-
code: string | null;
|
|
548
|
-
actionType: ActionType;
|
|
549
|
-
permissionLogic: ILogicNode | null; // AND/OR prerequisite rules
|
|
550
|
-
parentId: string | null; // For hierarchy
|
|
551
|
-
serial: number | null; // Display order
|
|
552
|
-
isActive: boolean;
|
|
553
|
-
metadata: Record<string, any> | null;
|
|
554
|
-
}
|
|
555
|
-
```
|
|
556
|
-
|
|
557
|
-
### IActionTreeDto (extends IAction)
|
|
43
|
+
All routes are protected by `permissionGuard` using constants from `ng-shared`.
|
|
558
44
|
|
|
559
|
-
|
|
560
|
-
interface IActionTreeDto extends IAction {
|
|
561
|
-
children: IActionTreeDto[];
|
|
562
|
-
}
|
|
563
|
-
```
|
|
45
|
+
## 3. Load Permissions
|
|
564
46
|
|
|
565
|
-
|
|
47
|
+
Call `PermissionStateService.loadPermissions()` after login (e.g., in `appInitGuard` or after session restore). It fetches the current user's effective permissions from `POST /iam/permissions/my-permissions` and populates `PermissionValidatorService` from `ng-shared`.
|
|
566
48
|
|
|
567
49
|
```typescript
|
|
568
|
-
|
|
569
|
-
name: string;
|
|
570
|
-
description?: string;
|
|
571
|
-
code?: string;
|
|
572
|
-
actionType?: ActionType;
|
|
573
|
-
permissionLogic?: ILogicNode;
|
|
574
|
-
parentId?: string;
|
|
575
|
-
serial?: number;
|
|
576
|
-
isActive?: boolean;
|
|
577
|
-
metadata?: Record<string, any>;
|
|
578
|
-
}
|
|
50
|
+
import { PermissionStateService } from '@flusys/ng-iam';
|
|
579
51
|
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
```
|
|
584
|
-
|
|
585
|
-
### IRole (extends IBaseEntity)
|
|
52
|
+
@Component({ ... })
|
|
53
|
+
export class AppShellComponent {
|
|
54
|
+
private readonly permState = inject(PermissionStateService);
|
|
586
55
|
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
readOnly: boolean;
|
|
590
|
-
name: string;
|
|
591
|
-
description: string | null;
|
|
592
|
-
companyId: string | null;
|
|
593
|
-
isActive: boolean;
|
|
594
|
-
serial: number | null;
|
|
595
|
-
metadata: Record<string, any> | null;
|
|
596
|
-
}
|
|
597
|
-
```
|
|
598
|
-
|
|
599
|
-
### ICreateRoleDto / IUpdateRoleDto / IRoleQueryDto
|
|
600
|
-
|
|
601
|
-
```typescript
|
|
602
|
-
interface ICreateRoleDto {
|
|
603
|
-
name: string;
|
|
604
|
-
description?: string;
|
|
605
|
-
companyId?: string;
|
|
606
|
-
isActive?: boolean;
|
|
607
|
-
serial?: number;
|
|
608
|
-
metadata?: Record<string, any>;
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
interface IUpdateRoleDto extends Partial<ICreateRoleDto> {
|
|
612
|
-
id: string;
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
interface IRoleQueryDto {
|
|
616
|
-
companyId?: string;
|
|
617
|
-
isActive?: boolean;
|
|
618
|
-
}
|
|
619
|
-
```
|
|
620
|
-
|
|
621
|
-
### Permission Interfaces
|
|
622
|
-
|
|
623
|
-
```typescript
|
|
624
|
-
type PermissionAction = 'add' | 'remove';
|
|
625
|
-
type PermissionMode = 'rbac' | 'direct' | 'full';
|
|
56
|
+
readonly permissions = this.permState.permissions; // Signal<IMyPermissionsResponseDto | null>
|
|
57
|
+
readonly isLoading = this.permState.isLoading; // Signal<boolean>
|
|
626
58
|
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
action: PermissionAction;
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
// Assignment DTOs
|
|
633
|
-
interface IAssignUserActionsDto {
|
|
634
|
-
userId: string;
|
|
635
|
-
companyId?: string;
|
|
636
|
-
branchId?: string; // null = company-wide
|
|
637
|
-
items: IPermissionItemDto[];
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
interface IAssignCompanyActionsDto {
|
|
641
|
-
companyId: string;
|
|
642
|
-
items: IPermissionItemDto[];
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
interface IAssignRoleActionsDto {
|
|
646
|
-
roleId: string;
|
|
647
|
-
items: IPermissionItemDto[];
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
interface IAssignUserRolesDto {
|
|
651
|
-
userId: string;
|
|
652
|
-
companyId?: string;
|
|
653
|
-
branchId?: string;
|
|
654
|
-
items: IPermissionItemDto[];
|
|
655
|
-
}
|
|
656
|
-
```
|
|
657
|
-
|
|
658
|
-
### Response DTOs
|
|
659
|
-
|
|
660
|
-
```typescript
|
|
661
|
-
interface IUserActionResponseDto {
|
|
662
|
-
id: string;
|
|
663
|
-
userId: string;
|
|
664
|
-
actionId: string;
|
|
665
|
-
actionCode: string;
|
|
666
|
-
actionName: string;
|
|
667
|
-
branchId: string | null;
|
|
668
|
-
createdAt: Date;
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
interface IRoleActionResponseDto {
|
|
672
|
-
id: string;
|
|
673
|
-
roleId: string;
|
|
674
|
-
actionId: string;
|
|
675
|
-
actionCode: string;
|
|
676
|
-
actionName: string;
|
|
677
|
-
createdAt: Date;
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
interface ICompanyActionResponseDto {
|
|
681
|
-
id: string;
|
|
682
|
-
companyId: string;
|
|
683
|
-
actionId: string;
|
|
684
|
-
actionCode: string;
|
|
685
|
-
actionName: string;
|
|
686
|
-
createdAt: Date;
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
interface IUserRoleResponseDto {
|
|
690
|
-
id: string;
|
|
691
|
-
userId: string;
|
|
692
|
-
roleId: string;
|
|
693
|
-
roleCode: string;
|
|
694
|
-
roleName: string;
|
|
695
|
-
branchId: string | null; // Always null - roles are NOT branch-scoped
|
|
696
|
-
createdAt: Date;
|
|
697
|
-
}
|
|
698
|
-
```
|
|
699
|
-
|
|
700
|
-
### My Permissions Response
|
|
701
|
-
|
|
702
|
-
```typescript
|
|
703
|
-
interface IMyPermissionsResponseDto {
|
|
704
|
-
frontendActions: Array<{ id: string; code: string; name: string; description: string }>;
|
|
705
|
-
cachedEndpoints: number; // Endpoint count for backend PermissionGuard
|
|
706
|
-
}
|
|
707
|
-
```
|
|
708
|
-
|
|
709
|
-
### Menu & Logic Interfaces
|
|
710
|
-
|
|
711
|
-
```typescript
|
|
712
|
-
interface IMenuAction {
|
|
713
|
-
id: string;
|
|
714
|
-
code: string;
|
|
715
|
-
name: string;
|
|
716
|
-
route: string | null;
|
|
717
|
-
icon: string | null;
|
|
718
|
-
iconType: number | null;
|
|
719
|
-
serial: number | null;
|
|
720
|
-
parentId: string | null;
|
|
721
|
-
children?: IMenuAction[];
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
// ILogicNode is imported from @flusys/ng-shared
|
|
725
|
-
```
|
|
726
|
-
|
|
727
|
-
### Prerequisite Interfaces
|
|
728
|
-
|
|
729
|
-
```typescript
|
|
730
|
-
interface IPrerequisiteActionDto {
|
|
731
|
-
actionId: string;
|
|
732
|
-
actionCode: string;
|
|
733
|
-
actionName: string;
|
|
734
|
-
}
|
|
735
|
-
|
|
736
|
-
interface IPrerequisiteValidationError {
|
|
737
|
-
actionId: string;
|
|
738
|
-
actionCode: string;
|
|
739
|
-
actionName: string;
|
|
740
|
-
requiredActions: IPrerequisiteActionDto[];
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
interface IPermissionOperationResultDto {
|
|
744
|
-
success: boolean;
|
|
745
|
-
added: number;
|
|
746
|
-
removed: number;
|
|
747
|
-
message: string;
|
|
748
|
-
prerequisiteErrors?: IPrerequisiteValidationError[];
|
|
749
|
-
}
|
|
750
|
-
```
|
|
751
|
-
|
|
752
|
-
### Simple Models
|
|
753
|
-
|
|
754
|
-
```typescript
|
|
755
|
-
interface IUser {
|
|
756
|
-
id: string;
|
|
757
|
-
name: string;
|
|
758
|
-
email: string;
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
interface IBranch {
|
|
762
|
-
id: string;
|
|
763
|
-
name: string;
|
|
764
|
-
companyId: string;
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
interface ICompany {
|
|
768
|
-
id: string;
|
|
769
|
-
name: string;
|
|
770
|
-
slug?: string;
|
|
771
|
-
}
|
|
772
|
-
```
|
|
773
|
-
|
|
774
|
-
### Query DTOs
|
|
775
|
-
|
|
776
|
-
```typescript
|
|
777
|
-
interface IGetUserActionsDto {
|
|
778
|
-
branchId?: string;
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
interface IGetRoleActionsDto {}
|
|
782
|
-
|
|
783
|
-
interface IGetUserRolesDto {
|
|
784
|
-
branchId?: string; // Ignored by backend
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
interface IGetMyPermissionsDto {
|
|
788
|
-
parentCodes?: string[];
|
|
789
|
-
}
|
|
790
|
-
```
|
|
791
|
-
|
|
792
|
-
---
|
|
793
|
-
|
|
794
|
-
## Utils & Constants
|
|
795
|
-
|
|
796
|
-
> Utils are **internal** (not exported from the public API). They are used by components and services within the package.
|
|
797
|
-
|
|
798
|
-
### permission-logic.util.ts
|
|
799
|
-
|
|
800
|
-
| Function | Description |
|
|
801
|
-
|----------|-------------|
|
|
802
|
-
| `validateActionPrerequisites(action, selectedIds, allActions)` | Validate prerequisites respecting AND/OR. Returns `{valid, missingActions}` |
|
|
803
|
-
| `extractRequiredActionIds(logic, allActions)` | Extract all action IDs from a logic tree |
|
|
804
|
-
| `describePrerequisites(logic, allActions)` | Human-readable prerequisite description |
|
|
805
|
-
|
|
806
|
-
### tree-utils.ts
|
|
807
|
-
|
|
808
|
-
| Function | Description |
|
|
809
|
-
|----------|-------------|
|
|
810
|
-
| `flattenTree<T>(tree)` | Flatten hierarchical tree into flat array |
|
|
811
|
-
| `buildItemMap<T>(items)` | Build ID-to-item `Map` for O(1) lookup |
|
|
812
|
-
| `buildTreeFromFlat<T>(flatList)` | Build tree from flat list using `parentId` |
|
|
813
|
-
| `convertActionToTreeNode(actions)` | Convert `IActionTreeDto[]` to PrimeNG `TreeNode<IAction>[]` |
|
|
814
|
-
|
|
815
|
-
### Constants
|
|
816
|
-
|
|
817
|
-
```typescript
|
|
818
|
-
export const MAX_DROPDOWN_ITEMS = 100; // Max items for dropdown lists (users, companies, roles, branches)
|
|
819
|
-
```
|
|
820
|
-
|
|
821
|
-
Exported directly as `MAX_DROPDOWN_ITEMS` from the package.
|
|
822
|
-
|
|
823
|
-
---
|
|
824
|
-
|
|
825
|
-
## Backend Integration
|
|
826
|
-
|
|
827
|
-
### Required Endpoints
|
|
828
|
-
|
|
829
|
-
**Action CRUD:**
|
|
830
|
-
|
|
831
|
-
```
|
|
832
|
-
POST /iam/actions/tree # Get hierarchical action tree
|
|
833
|
-
POST /iam/actions/tree-for-permission # Get actions filtered by company whitelist
|
|
834
|
-
POST /iam/actions/get-all # List all actions (flat)
|
|
835
|
-
POST /iam/actions/get/:id # Get action by ID
|
|
836
|
-
POST /iam/actions/insert # Create action
|
|
837
|
-
POST /iam/actions/update # Update action
|
|
838
|
-
POST /iam/actions/delete # Delete action
|
|
839
|
-
```
|
|
840
|
-
|
|
841
|
-
**Role CRUD:**
|
|
842
|
-
|
|
843
|
-
```
|
|
844
|
-
POST /iam/roles/get-all # List roles (with pagination + company filter)
|
|
845
|
-
POST /iam/roles/get/:id # Get role by ID
|
|
846
|
-
POST /iam/roles/insert # Create role
|
|
847
|
-
POST /iam/roles/update # Update role
|
|
848
|
-
POST /iam/roles/delete # Delete role
|
|
849
|
-
```
|
|
850
|
-
|
|
851
|
-
**Permission Assignments:**
|
|
852
|
-
|
|
853
|
-
```
|
|
854
|
-
POST /iam/permissions/my-permissions # Current user's permissions
|
|
855
|
-
POST /iam/permissions/user-actions/assign # Assign/remove user actions
|
|
856
|
-
GET /iam/permissions/user-actions/:id # Get user's direct actions
|
|
857
|
-
POST /iam/permissions/user-roles/assign # Assign/remove user roles
|
|
858
|
-
GET /iam/permissions/user-roles/:id # Get user's roles
|
|
859
|
-
POST /iam/permissions/role-actions/assign # Assign/remove role actions
|
|
860
|
-
GET /iam/permissions/role-actions/:id # Get role's actions
|
|
861
|
-
POST /iam/permissions/company-actions/assign # Assign/remove company whitelist
|
|
862
|
-
GET /iam/permissions/company-actions/:id # Get company's whitelisted actions
|
|
863
|
-
```
|
|
864
|
-
|
|
865
|
-
### My Permissions Response Format
|
|
866
|
-
|
|
867
|
-
```json
|
|
868
|
-
{
|
|
869
|
-
"success": true,
|
|
870
|
-
"data": {
|
|
871
|
-
"frontendActions": [
|
|
872
|
-
{
|
|
873
|
-
"id": "uuid",
|
|
874
|
-
"code": "user.create",
|
|
875
|
-
"name": "Create User",
|
|
876
|
-
"description": "Allows creating new users"
|
|
877
|
-
}
|
|
878
|
-
],
|
|
879
|
-
"cachedEndpoints": 42
|
|
59
|
+
loadOnInit() {
|
|
60
|
+
this.permState.loadPermissions().subscribe();
|
|
880
61
|
}
|
|
881
|
-
}
|
|
882
|
-
```
|
|
883
|
-
|
|
884
|
-
---
|
|
885
62
|
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
### Permission Loading
|
|
889
|
-
|
|
890
|
-
Load permissions in a guard to block navigation until ready:
|
|
891
|
-
|
|
892
|
-
```typescript
|
|
893
|
-
// appInitGuard
|
|
894
|
-
if (!permissionState.isLoaded()) {
|
|
895
|
-
try {
|
|
896
|
-
await firstValueFrom(permissionState.loadPermissions());
|
|
897
|
-
} catch (error) {
|
|
898
|
-
console.error('Failed to load permissions:', error);
|
|
899
|
-
// Continue with graceful degradation - don't block the app
|
|
63
|
+
isLoaded() {
|
|
64
|
+
return this.permState.isLoaded(); // true when permissions are set in PermissionValidatorService
|
|
900
65
|
}
|
|
901
66
|
}
|
|
902
|
-
return true;
|
|
903
|
-
```
|
|
904
|
-
|
|
905
|
-
- Cache permissions in memory (PermissionStateService handles this)
|
|
906
|
-
- Clear permissions on logout
|
|
907
|
-
- Never store permissions in localStorage (security risk)
|
|
908
|
-
- Never load permissions in component `ngOnInit` (causes race conditions)
|
|
909
|
-
|
|
910
|
-
### Permission Checks
|
|
911
|
-
|
|
912
|
-
```typescript
|
|
913
|
-
// Single action
|
|
914
|
-
validator.hasAction('user.create');
|
|
915
|
-
|
|
916
|
-
// ALL required
|
|
917
|
-
validator.hasAllActions(['user.create', 'user.update']);
|
|
918
|
-
|
|
919
|
-
// ANY required
|
|
920
|
-
validator.hasAnyAction(['user.view', 'user.list']);
|
|
921
|
-
```
|
|
922
|
-
|
|
923
|
-
Cache check results in computed signals rather than calling in templates or tight loops.
|
|
924
|
-
|
|
925
|
-
### Menu Filtering
|
|
926
|
-
|
|
927
|
-
```typescript
|
|
928
|
-
// Set menu with requiredAction or logicNode
|
|
929
|
-
this.layoutService.setMenu(menuItems);
|
|
930
|
-
|
|
931
|
-
// Set permission checker (layout handles filtering automatically)
|
|
932
|
-
this.layoutService.setPermissionChecker((actionCode) =>
|
|
933
|
-
validator.hasAction(actionCode)
|
|
934
|
-
);
|
|
935
|
-
```
|
|
936
|
-
|
|
937
|
-
Do not filter menus manually - let layout handle it.
|
|
938
|
-
|
|
939
|
-
### Action Naming
|
|
940
|
-
|
|
941
|
-
Format: `module.operation` or `module.entity.operation`
|
|
942
|
-
|
|
943
|
-
```typescript
|
|
944
|
-
// Good
|
|
945
|
-
'user.create'
|
|
946
|
-
'user.update'
|
|
947
|
-
'report.export'
|
|
948
|
-
'admin.system.config'
|
|
949
|
-
|
|
950
|
-
// Bad
|
|
951
|
-
'create' // Too generic
|
|
952
|
-
'userCreate' // Not dot notation
|
|
953
|
-
'users' // Entity name, not action
|
|
954
|
-
```
|
|
955
|
-
|
|
956
|
-
### LogicNode Pattern
|
|
957
|
-
|
|
958
|
-
Complex permission rules using AND/OR operators:
|
|
959
|
-
|
|
960
|
-
```typescript
|
|
961
|
-
// User needs 'user.create' AND ('user.update' OR 'user.delete')
|
|
962
|
-
const logic: ILogicNode = {
|
|
963
|
-
type: 'AND',
|
|
964
|
-
children: [
|
|
965
|
-
{ type: 'ACTION', value: 'user.create' },
|
|
966
|
-
{
|
|
967
|
-
type: 'OR',
|
|
968
|
-
children: [
|
|
969
|
-
{ type: 'ACTION', value: 'user.update' },
|
|
970
|
-
{ type: 'ACTION', value: 'user.delete' },
|
|
971
|
-
],
|
|
972
|
-
},
|
|
973
|
-
],
|
|
974
|
-
};
|
|
975
|
-
```
|
|
976
|
-
|
|
977
|
-
---
|
|
978
|
-
|
|
979
|
-
## Common Issues
|
|
980
|
-
|
|
981
|
-
| Problem | Cause | Solution |
|
|
982
|
-
|---------|-------|----------|
|
|
983
|
-
| `permissionState.isLoaded()` returns false | IAM not enabled or endpoint failing | Check `services.iam.enabled: true` in environment, verify `POST /iam/permissions/my-permissions` works |
|
|
984
|
-
| Empty menu after login | Permissions not loaded or no matching actions | Check `permissionState.isLoaded()`, verify user has frontend actions, check `requiredAction` matches action codes |
|
|
985
|
-
| Menu items not filtered | Permission checker not set | Call `layoutService.setPermissionChecker()` after loading permissions |
|
|
986
|
-
| 401 on IAM endpoints with external auth | Auth token not forwarded | Ensure your auth interceptor adds `Authorization` header to IAM requests |
|
|
987
|
-
| Provider not configured error | Missing DI registration | Add required providers to `app.config.ts` (USER_PROVIDER, LAYOUT_AUTH_STATE) |
|
|
988
|
-
| Empty user/company dropdowns | API response format mismatch | Ensure adapters return `IListResponse<T>` format with `data` array |
|
|
989
|
-
|
|
990
|
-
---
|
|
991
|
-
|
|
992
|
-
## Configuration
|
|
993
|
-
|
|
994
|
-
### Frontend Environment
|
|
995
|
-
|
|
996
|
-
```typescript
|
|
997
|
-
// environment.ts
|
|
998
|
-
services: {
|
|
999
|
-
iam: { baseUrl: 'http://localhost:2002/iam', enabled: true },
|
|
1000
|
-
}
|
|
1001
|
-
```
|
|
1002
|
-
|
|
1003
|
-
### Backend Environment
|
|
1004
|
-
|
|
1005
|
-
```bash
|
|
1006
|
-
# .env
|
|
1007
|
-
IAM_PERMISSION_MODE=FULL # DIRECT, RBAC, or FULL
|
|
1008
|
-
```
|
|
1009
|
-
|
|
1010
|
-
| Scenario | services.iam.enabled | Result |
|
|
1011
|
-
|----------|---------------------|--------|
|
|
1012
|
-
| Full IAM | `true` | Permission-based menu |
|
|
1013
|
-
| IAM Disabled | `false` | Static menu (all items visible) |
|
|
1014
|
-
| Service Down | `true` (API fails) | Graceful fallback to static menu |
|
|
1015
|
-
|
|
1016
|
-
---
|
|
1017
|
-
|
|
1018
|
-
## Provider Functions
|
|
1019
|
-
|
|
1020
|
-
### provideIamProviders()
|
|
1021
|
-
|
|
1022
|
-
Registers provider adapters that enable other packages (ng-auth) to access IAM functionality without direct dependencies.
|
|
1023
|
-
|
|
1024
|
-
```typescript
|
|
1025
|
-
import { provideIamProviders } from '@flusys/ng-iam';
|
|
1026
|
-
|
|
1027
|
-
// app.config.ts
|
|
1028
|
-
export const appConfig: ApplicationConfig = {
|
|
1029
|
-
providers: [
|
|
1030
|
-
...provideIamProviders(), // Register IAM providers
|
|
1031
|
-
],
|
|
1032
|
-
};
|
|
1033
67
|
```
|
|
1034
68
|
|
|
1035
|
-
|
|
69
|
+
## 4. Check Permissions in Templates
|
|
1036
70
|
|
|
1037
|
-
|
|
1038
|
-
|-------|---------|---------|
|
|
1039
|
-
| `PROFILE_PERMISSION_PROVIDER` | `ProfilePermissionProviderAdapter` | User roles/actions for profile page |
|
|
71
|
+
Once `provideIamProviders()` is registered and `loadPermissions()` has been called, permission directives from `ng-shared` work automatically:
|
|
1040
72
|
|
|
1041
|
-
|
|
73
|
+
```html
|
|
74
|
+
<!-- show element if user has the action code -->
|
|
75
|
+
<button *hasPermission="'product.update'">Edit</button>
|
|
1042
76
|
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
```typescript
|
|
1046
|
-
interface IProfilePermissionProvider {
|
|
1047
|
-
getUserRoles(userId: string, branchId?: string): Observable<ISingleResponse<IProfileRoleInfo[]>>;
|
|
1048
|
-
getUserActions(userId: string, branchId?: string): Observable<ISingleResponse<IProfileActionInfo[]>>;
|
|
1049
|
-
}
|
|
77
|
+
<!-- show element if user has any of these action codes -->
|
|
78
|
+
<div *hasPermission="['admin.manage', 'product.delete']">Admin area</div>
|
|
1050
79
|
```
|
|
1051
80
|
|
|
1052
|
-
|
|
81
|
+
Use `permissionGuard` from `ng-shared` to protect routes:
|
|
1053
82
|
|
|
1054
83
|
```typescript
|
|
1055
|
-
|
|
1056
|
-
private readonly permissionProvider = inject(PROFILE_PERMISSION_PROVIDER, { optional: true });
|
|
84
|
+
import { permissionGuard } from '@flusys/ng-shared';
|
|
1057
85
|
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
});
|
|
86
|
+
{
|
|
87
|
+
path: 'admin',
|
|
88
|
+
canActivate: [permissionGuard('admin.manage')],
|
|
89
|
+
loadComponent: () => import('./admin.component'),
|
|
1063
90
|
}
|
|
1064
91
|
```
|
|
1065
92
|
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
## See Also
|
|
1069
|
-
|
|
1070
|
-
- **[CORE-GUIDE.md](./CORE-GUIDE.md)** - Configuration system (APP_CONFIG)
|
|
1071
|
-
- **[AUTH-GUIDE.md](./AUTH-GUIDE.md)** - Authentication and provider adapters
|
|
1072
|
-
- **[SHARED-GUIDE.md](./SHARED-GUIDE.md)** - Provider interface definitions
|
|
1073
|
-
- **[LAYOUT-GUIDE.md](./LAYOUT-GUIDE.md)** - Menu system and filtering
|
|
1074
|
-
|
|
1075
|
-
---
|
|
93
|
+
## License
|
|
1076
94
|
|
|
1077
|
-
|
|
1078
|
-
**Angular Version:** 21
|
|
95
|
+
MIT © FLUSYS
|