@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.
Files changed (32) hide show
  1. package/README.md +44 -1027
  2. package/fesm2022/flusys-ng-iam-action-form-page.component-Vc5Toouu.mjs +502 -0
  3. package/fesm2022/flusys-ng-iam-action-form-page.component-Vc5Toouu.mjs.map +1 -0
  4. package/fesm2022/flusys-ng-iam-action-list-page.component-UGKclpFx.mjs +348 -0
  5. package/fesm2022/flusys-ng-iam-action-list-page.component-UGKclpFx.mjs.map +1 -0
  6. package/fesm2022/flusys-ng-iam-flusys-ng-iam-CrycDULz.mjs +4561 -0
  7. package/fesm2022/flusys-ng-iam-flusys-ng-iam-CrycDULz.mjs.map +1 -0
  8. package/fesm2022/flusys-ng-iam-iam-container.component-D0m8u6nu.mjs +105 -0
  9. package/fesm2022/flusys-ng-iam-iam-container.component-D0m8u6nu.mjs.map +1 -0
  10. package/fesm2022/flusys-ng-iam-permission-page.component-x88X4AgJ.mjs +143 -0
  11. package/fesm2022/flusys-ng-iam-permission-page.component-x88X4AgJ.mjs.map +1 -0
  12. package/fesm2022/flusys-ng-iam-role-form-page.component-InsxdriD.mjs +335 -0
  13. package/fesm2022/flusys-ng-iam-role-form-page.component-InsxdriD.mjs.map +1 -0
  14. package/fesm2022/flusys-ng-iam-role-list-page.component-8NGXCeSZ.mjs +389 -0
  15. package/fesm2022/flusys-ng-iam-role-list-page.component-8NGXCeSZ.mjs.map +1 -0
  16. package/fesm2022/flusys-ng-iam.mjs +1 -1
  17. package/package.json +11 -10
  18. package/types/flusys-ng-iam.d.ts +107 -686
  19. package/fesm2022/flusys-ng-iam-action-form-page.component-C1j10Qhw.mjs +0 -467
  20. package/fesm2022/flusys-ng-iam-action-form-page.component-C1j10Qhw.mjs.map +0 -1
  21. package/fesm2022/flusys-ng-iam-action-list-page.component-BCzSardO.mjs +0 -281
  22. package/fesm2022/flusys-ng-iam-action-list-page.component-BCzSardO.mjs.map +0 -1
  23. package/fesm2022/flusys-ng-iam-flusys-ng-iam-DISrddPh.mjs +0 -4940
  24. package/fesm2022/flusys-ng-iam-flusys-ng-iam-DISrddPh.mjs.map +0 -1
  25. package/fesm2022/flusys-ng-iam-iam-container.component-BkhqmzLi.mjs +0 -97
  26. package/fesm2022/flusys-ng-iam-iam-container.component-BkhqmzLi.mjs.map +0 -1
  27. package/fesm2022/flusys-ng-iam-permission-page.component-BSQFPt_N.mjs +0 -143
  28. package/fesm2022/flusys-ng-iam-permission-page.component-BSQFPt_N.mjs.map +0 -1
  29. package/fesm2022/flusys-ng-iam-role-form-page.component-Cqziu_BM.mjs +0 -314
  30. package/fesm2022/flusys-ng-iam-role-form-page.component-Cqziu_BM.mjs.map +0 -1
  31. package/fesm2022/flusys-ng-iam-role-list-page.component-BObCxHiB.mjs +0 -266
  32. 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 Package Guide
1
+ # @flusys/ng-iam
2
2
 
3
- ## Overview
3
+ Identity and Access Management UI for FLUSYS Angular apps — role management, permission assignment, and reactive permission checking via signals.
4
4
 
5
- `@flusys/ng-iam` provides Identity and Access Management (IAM) for FLUSYS applications. It includes permission state management, role-based access control (RBAC), action management with AND/OR prerequisite logic, and dynamic menu filtering based on user permissions.
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
+ [![npm version](https://img.shields.io/npm/v/@flusys/ng-iam.svg)](https://www.npmjs.com/package/@flusys/ng-iam)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
37
7
 
38
8
  ---
39
9
 
40
- ## Architecture
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
- ## Provider Interfaces
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 { provideAuthProviders } from '@flusys/ng-auth';
20
+ import { provideIamProviders } from '@flusys/ng-iam';
95
21
 
96
22
  export const appConfig: ApplicationConfig = {
97
23
  providers: [
98
- provideAuthProviders(), // Registers all adapters
24
+ ...provideIamProviders(), // registers PROFILE_PERMISSION_PROVIDER for ng-shared's permission directives/guards
99
25
  ],
100
26
  };
101
27
  ```
102
28
 
103
- **Reference adapters in ng-auth:**
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
- { path: 'iam', children: IAM_ROUTES },
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
- ```typescript
560
- interface IActionTreeDto extends IAction {
561
- children: IActionTreeDto[];
562
- }
563
- ```
45
+ ## 3. Load Permissions
564
46
 
565
- ### ICreateActionDto / IUpdateActionDto
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
- interface ICreateActionDto {
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
- interface IUpdateActionDto extends Partial<ICreateActionDto> {
581
- id: string;
582
- }
583
- ```
584
-
585
- ### IRole (extends IBaseEntity)
52
+ @Component({ ... })
53
+ export class AppShellComponent {
54
+ private readonly permState = inject(PermissionStateService);
586
55
 
587
- ```typescript
588
- interface IRole extends IBaseEntity {
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
- interface IPermissionItemDto {
628
- id: string;
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
- ## Best Practices
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
- **Registers:**
69
+ ## 4. Check Permissions in Templates
1036
70
 
1037
- | Token | Adapter | Purpose |
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
- ### ProfilePermissionProviderAdapter
73
+ ```html
74
+ <!-- show element if user has the action code -->
75
+ <button *hasPermission="'product.update'">Edit</button>
1042
76
 
1043
- Implements `IProfilePermissionProvider` from ng-shared. Enables ng-auth's profile page to display user permissions.
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
- **Usage in ng-auth profile page:**
81
+ Use `permissionGuard` from `ng-shared` to protect routes:
1053
82
 
1054
83
  ```typescript
1055
- // Optional injection - graceful degradation when IAM disabled
1056
- private readonly permissionProvider = inject(PROFILE_PERMISSION_PROVIDER, { optional: true });
84
+ import { permissionGuard } from '@flusys/ng-shared';
1057
85
 
1058
- // Load user permissions
1059
- if (this.permissionProvider) {
1060
- this.permissionProvider.getUserRoles(userId, branchId).subscribe(response => {
1061
- this.roles.set(response.data ?? []);
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
- **Last Updated:** 2026-02-16
1078
- **Angular Version:** 21
95
+ MIT © FLUSYS