@flusys/ng-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.
Files changed (15) hide show
  1. package/README.md +301 -458
  2. package/fesm2022/{flusys-ng-iam-action-form-page.component-BQx9yset.mjs → flusys-ng-iam-action-form-page.component-DJbMu2aV.mjs} +2 -2
  3. package/fesm2022/{flusys-ng-iam-action-form-page.component-BQx9yset.mjs.map → flusys-ng-iam-action-form-page.component-DJbMu2aV.mjs.map} +1 -1
  4. package/fesm2022/{flusys-ng-iam-action-list-page.component-BrpZujxk.mjs → flusys-ng-iam-action-list-page.component-DYnYC7Nn.mjs} +2 -2
  5. package/fesm2022/{flusys-ng-iam-action-list-page.component-BrpZujxk.mjs.map → flusys-ng-iam-action-list-page.component-DYnYC7Nn.mjs.map} +1 -1
  6. package/fesm2022/{flusys-ng-iam-flusys-ng-iam-Co4ot9My.mjs → flusys-ng-iam-flusys-ng-iam-CnVOy_4s.mjs} +10 -10
  7. package/fesm2022/{flusys-ng-iam-flusys-ng-iam-Co4ot9My.mjs.map → flusys-ng-iam-flusys-ng-iam-CnVOy_4s.mjs.map} +1 -1
  8. package/fesm2022/{flusys-ng-iam-permission-page.component-Dpk90y72.mjs → flusys-ng-iam-permission-page.component-_cLVilgz.mjs} +2 -2
  9. package/fesm2022/{flusys-ng-iam-permission-page.component-Dpk90y72.mjs.map → flusys-ng-iam-permission-page.component-_cLVilgz.mjs.map} +1 -1
  10. package/fesm2022/{flusys-ng-iam-role-form-page.component-CVfRQpoa.mjs → flusys-ng-iam-role-form-page.component-BuZmko2b.mjs} +2 -2
  11. package/fesm2022/{flusys-ng-iam-role-form-page.component-CVfRQpoa.mjs.map → flusys-ng-iam-role-form-page.component-BuZmko2b.mjs.map} +1 -1
  12. package/fesm2022/{flusys-ng-iam-role-list-page.component-BHB8X5r7.mjs → flusys-ng-iam-role-list-page.component-C7GVYYIn.mjs} +2 -2
  13. package/fesm2022/{flusys-ng-iam-role-list-page.component-BHB8X5r7.mjs.map → flusys-ng-iam-role-list-page.component-C7GVYYIn.mjs.map} +1 -1
  14. package/fesm2022/flusys-ng-iam.mjs +1 -1
  15. package/package.json +4 -4
package/README.md CHANGED
@@ -1,608 +1,451 @@
1
- # @flusys/ng-iam Package Guide
1
+ # @flusys/ng-iam
2
2
 
3
- ## Overview
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**.
3
+ > Identity and Access Management UI library for the FLUSYS Angular platform — role management, permission assignment, permission evaluation, and logic builder.
8
4
 
9
- | Item | Value |
10
- |------|-------|
11
- | Package | `@flusys/ng-iam` |
12
- | Version | 4.0.1 |
13
- | Dependencies | ng-core, ng-shared, ng-layout (IMenuItem only) |
14
- | CSS Framework | Tailwind CSS (not PrimeFlex) |
15
- | Build | `npm run build:ng-iam` |
5
+ [![npm version](https://img.shields.io/npm/v/@flusys/ng-iam.svg)](https://www.npmjs.com/package/@flusys/ng-iam)
6
+ [![Angular](https://img.shields.io/badge/Angular-21-red.svg)](https://angular.io)
7
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue.svg)](https://www.typescriptlang.org)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
16
9
 
17
10
  ---
18
11
 
19
- ## Architecture
20
-
21
- ```
22
- ng-shared (defines interfaces + tokens)
23
-
24
- Your Auth System (implements interfaces with adapters)
25
-
26
- ng-iam (consumes interfaces via DI)
27
- ```
28
-
29
- ng-iam resolves all auth-related data at runtime via DI tokens defined in ng-shared.
12
+ ## Table of Contents
13
+
14
+ - [Overview](#overview)
15
+ - [Features](#features)
16
+ - [Compatibility](#compatibility)
17
+ - [Installation](#installation)
18
+ - [Quick Start](#quick-start)
19
+ - [Permission Modes](#permission-modes)
20
+ - [RBAC Mode](#rbac-mode)
21
+ - [DIRECT Mode](#direct-mode)
22
+ - [FULL Mode](#full-mode)
23
+ - [Module Registration](#module-registration)
24
+ - [Permission State Service](#permission-state-service)
25
+ - [IAM API Services](#iam-api-services)
26
+ - [Components](#components)
27
+ - [PermissionPageComponent](#permissionpagecomponent)
28
+ - [LogicBuilderComponent](#logicbuildercomponent)
29
+ - [Integration](#integration)
30
+ - [API Endpoints](#api-endpoints)
31
+ - [Troubleshooting](#troubleshooting)
32
+ - [License](#license)
30
33
 
31
34
  ---
32
35
 
33
- ## Provider Interfaces
36
+ ## Overview
34
37
 
35
- | Token | Source | Purpose | Required? |
36
- |-------|--------|---------|-----------|
37
- | `USER_PROVIDER` | ng-shared | User list for role/action assignment | Yes |
38
- | `LAYOUT_AUTH_STATE` | ng-layout | Current company/branch context | Yes (if company enabled) |
39
- | `COMPANY_API_PROVIDER` | ng-shared | Company list for whitelisting | Optional |
40
- | `USER_PERMISSION_PROVIDER` | ng-shared | User branch permissions | Optional |
38
+ `@flusys/ng-iam` provides the complete Identity and Access Management UI for FLUSYS applications. It supports three independent permission modes (RBAC, DIRECT, FULL) and integrates with the backend `nestjs-iam` module.
41
39
 
42
- ```typescript
43
- // Provider usage
44
- const companyId = inject(LAYOUT_AUTH_STATE).currentCompanyInfo()?.id;
45
- const response = await inject(USER_PERMISSION_PROVIDER).getUserBranchPermissions(userId).toPromise();
46
- ```
40
+ The package uses the Provider Interface Pattern — it never imports from `ng-auth` directly. Instead it injects `USER_PROVIDER` and `COMPANY_PROVIDER` from `ng-shared` to get current user and company context.
47
41
 
48
42
  ---
49
43
 
50
- ## Integration Options
51
-
52
- ### With FLUSYS ng-auth (Recommended)
53
-
54
- ```typescript
55
- import { provideAuthProviders } from '@flusys/ng-auth';
56
- export const appConfig: ApplicationConfig = {
57
- providers: [provideAuthProviders()], // Registers all adapters
58
- };
59
- ```
60
-
61
- ### With Custom Auth System
62
-
63
- ```typescript
64
- providers: [
65
- { provide: USER_PROVIDER, useClass: YourUserProvider },
66
- { provide: LAYOUT_AUTH_STATE, useClass: YourCompanyContextProvider },
67
- { provide: COMPANY_API_PROVIDER, useClass: YourCompanyApiProvider }, // Optional
68
- { provide: USER_PERMISSION_PROVIDER, useClass: YourUserPermissionProvider }, // Optional
69
- ]
70
- ```
44
+ ## Features
71
45
 
72
- | Feature | Providers Needed |
73
- |---------|-----------------|
74
- | Basic role assignments | USER_PROVIDER only |
75
- | Company-scoped permissions | + LAYOUT_AUTH_STATE, COMPANY_API_PROVIDER |
76
- | Branch-scoped permissions | + LAYOUT_AUTH_STATE, USER_PERMISSION_PROVIDER |
46
+ - Three permission modes: RBAC, DIRECT, FULL
47
+ - ✅ `PermissionStateService` — signal-based permission state with 1-hour cache
48
+ - Role management UI (create, assign users, set permissions)
49
+ - Direct permission assignment UI (assign permissions directly to users)
50
+ - `LogicBuilderComponent` visual AND/OR/nested logic builder for complex permissions
51
+ - ✅ Permission cache auto-invalidation on role/permission change
52
+ - ✅ `provideIamProviders()` — registers permission evaluator with `ng-shared`
53
+ - ✅ Lazy-loaded IAM pages
77
54
 
78
55
  ---
79
56
 
80
- ## Permission Modes
81
-
82
- Configured via `APP_CONFIG.iam.permissionMode`:
57
+ ## Compatibility
83
58
 
84
- | Mode | Structure | Tabs Available | Use Case |
85
- |------|-----------|---------------|----------|
86
- | **DIRECT** | User -> Actions | User-Actions | Small apps |
87
- | **RBAC** | User -> Role -> Actions | Role-Actions, User-Roles | Standard apps |
88
- | **FULL** | Both combined | All tabs | Maximum flexibility |
89
-
90
- When company feature is enabled, a **Company-Actions** tab is added for whitelisting.
59
+ | Package | Version |
60
+ |---------|---------|
61
+ | Angular | 21+ |
62
+ | @flusys/ng-core | 4.x |
63
+ | @flusys/ng-shared | 4.x |
91
64
 
92
65
  ---
93
66
 
94
- ## Routes
95
-
96
- **Export:** `IAM_ROUTES` from `@flusys/ng-iam`
67
+ ## Installation
97
68
 
69
+ ```bash
70
+ npm install @flusys/ng-iam @flusys/ng-core @flusys/ng-shared
98
71
  ```
99
- /iam → IamContainerComponent (tabbed navigation)
100
- /actions → ActionListPageComponent (hierarchical tree)
101
- /actions/new → ActionFormPageComponent (create)
102
- /actions/:id → ActionFormPageComponent (edit)
103
- /roles → RoleListPageComponent (paginated table)
104
- /roles/new → RoleFormPageComponent (create)
105
- /roles/:id → RoleFormPageComponent (edit)
106
- /permissions → PermissionPageComponent (tabbed assignment UI)
107
- ```
108
-
109
- | Route | Guard | Permission |
110
- |-------|-------|------------|
111
- | `/actions` | permissionGuard | `ACTION_PERMISSIONS.READ` |
112
- | `/roles` | permissionGuard | `ROLE_PERMISSIONS.READ` |
113
- | `/permissions` | anyPermissionGuard | Multiple READ permissions |
114
72
 
115
73
  ---
116
74
 
117
- ## Services
118
-
119
- ### PermissionStateService
120
-
121
- **File:** `services/permission-state.service.ts` | Provided at root level
122
-
123
- Manages current user's permissions with signal-based state.
75
+ ## Quick Start
124
76
 
125
- | API | Type | Description |
126
- |-----|------|-------------|
127
- | `permissions` | `Signal<IMyPermissionsResponseDto \| null>` | Current user permissions (readonly) |
128
- | `isLoading` | `Signal<boolean>` | Loading state (readonly) |
129
- | `loadPermissions(dto?)` | `Observable<void>` | Load from `/iam/permissions/my-permissions` |
130
- | `isLoaded()` | `boolean` | Check if permissions loaded |
131
-
132
- Permission checking delegated to `PermissionValidatorService` from ng-shared.
77
+ ### 1. Enable IAM in Config
133
78
 
134
79
  ```typescript
135
- await firstValueFrom(permissionState.loadPermissions()); // Load in guard
136
- validator.hasAction('user.create'); // Check permission
137
- validator.hasAllActions(['user.create', 'user.update']); // Check multiple
80
+ // environments/environment.ts
81
+ export const environment = {
82
+ permissionMode: 'FULL', // 'RBAC' | 'DIRECT' | 'FULL'
83
+ services: {
84
+ auth: { enabled: true },
85
+ iam: { enabled: true },
86
+ },
87
+ };
138
88
  ```
139
89
 
140
- ---
141
-
142
- ### ActionApiService
143
-
144
- **File:** `services/action-api.service.ts` | Extends `ApiResourceService<IUpdateActionDto, IAction>`
145
-
146
- **Resource:** `iam/actions`
147
-
148
- | Method | Endpoint | Description |
149
- |--------|----------|-------------|
150
- | `getTree(search?, isActive?, withDeleted?)` | `POST /iam/actions/tree` | Hierarchical action tree |
151
- | `getActionsForPermission()` | `POST /iam/actions/tree-for-permission` | Actions filtered by company whitelist |
90
+ ### 2. Register IAM Providers
152
91
 
153
- Inherits standard CRUD: `getAll`, `getById`, `insert`, `update`, `delete`.
154
-
155
- ---
92
+ ```typescript
93
+ // app.config.ts
94
+ import { provideIamProviders } from '@flusys/ng-iam';
156
95
 
157
- ### RoleApiService
96
+ export const appConfig: ApplicationConfig = {
97
+ providers: [
98
+ ...provideIamProviders(),
99
+ // Provides: PERMISSION_VALIDATOR (used by HasPermissionDirective, PermissionGuard)
100
+ ],
101
+ };
102
+ ```
158
103
 
159
- **File:** `services/role-api.service.ts` | Extends `ApiResourceService<IUpdateRoleDto, IRole>`
104
+ ### 3. Add IAM Routes
160
105
 
161
- **Resource:** `iam/roles` | Only available in RBAC/FULL mode.
106
+ ```typescript
107
+ // app.routes.ts
108
+ import { IAM_ROUTES } from '@flusys/ng-iam';
109
+
110
+ export const routes: Routes = [
111
+ {
112
+ path: 'iam',
113
+ loadChildren: () => IAM_ROUTES,
114
+ },
115
+ ];
116
+ ```
162
117
 
163
118
  ---
164
119
 
165
- ### PermissionApiService
120
+ ## Permission Modes
166
121
 
167
- **File:** `services/permission-api.service.ts` | Extends `BaseApiService`
122
+ ### RBAC Mode
168
123
 
169
- All endpoints use POST (POST-only RPC convention).
124
+ Role-Based Access Control. Users are assigned roles; roles hold permissions.
170
125
 
171
- | Method | Endpoint | Description |
172
- |--------|----------|-------------|
173
- | `assignUserActions(data)` | `POST /iam/permissions/user-actions/assign` | Assign/remove actions for user |
174
- | `getUserActions(userId, query?)` | `POST /iam/permissions/get-user-actions` | Get user's direct actions |
175
- | `assignUserRoles(data)` | `POST /iam/permissions/user-roles/assign` | Assign/remove roles for user |
176
- | `getUserRoles(userId, query?)` | `POST /iam/permissions/get-user-roles` | Get user's roles |
177
- | `assignRoleActions(data)` | `POST /iam/permissions/role-actions/assign` | Assign/remove actions for role |
178
- | `getRoleActions(roleId, query?)` | `POST /iam/permissions/get-role-actions` | Get role's actions |
179
- | `assignCompanyActions(data)` | `POST /iam/permissions/company-actions/assign` | Assign/remove company whitelist |
180
- | `getCompanyActions(companyId)` | `POST /iam/permissions/get-company-actions` | Get company's whitelisted actions |
126
+ ```
127
+ User ──assigned──► Role ──has──► Permissions
128
+ ```
181
129
 
182
- ---
130
+ **Active controllers:**
131
+ - Role management (create roles, assign permissions to roles)
132
+ - Role assignment (assign roles to users)
183
133
 
184
- ### ActionPermissionLogicService
134
+ **Config:**
135
+ ```typescript
136
+ { permissionMode: 'RBAC' }
137
+ ```
185
138
 
186
- **File:** `services/action-permission-logic.service.ts`
139
+ ### DIRECT Mode
187
140
 
188
- Manages smart prerequisite validation using AND/OR logic trees.
141
+ Permissions assigned directly to users. No roles.
189
142
 
190
- | Method | Description |
191
- |--------|-------------|
192
- | `handleCheck(action, selection, allActions, onUpdate, onCancel)` | Handle selection with prerequisite validation |
193
- | `handleUncheck(action, selection, allActions, onUpdate)` | Handle deselection with dependency detection |
194
- | `hasUnmetPrerequisites(action, selection, allActions)` | Check if action has unmet prerequisites |
195
- | `showValidationErrorDialog(invalid, selection, allActions, onUpdate)` | Pre-save validation with auto-fix |
196
- | `getPrerequisiteTooltip(action, selection, allActions)` | Get tooltip text with prerequisite info |
143
+ ```
144
+ User ──directly assigned──► Permissions
145
+ ```
197
146
 
198
- Features: Recursive deep-scan, smart AND/OR optimization, alternative suggestions, pre-save validation.
147
+ **Active controllers:**
148
+ - User permission assignment
199
149
 
200
- ---
150
+ **Config:**
151
+ ```typescript
152
+ { permissionMode: 'DIRECT' }
153
+ ```
201
154
 
202
- ## Components
155
+ ### FULL Mode
203
156
 
204
- ### LogicBuilderComponent
157
+ Both RBAC and DIRECT combined. User gets union of role permissions + direct permissions.
205
158
 
206
- **Selector:** `lib-logic-builder` | Visual AND/OR permission logic tree editor.
159
+ ```
160
+ User ──assigned──► Role ──has──► Permissions
161
+ └──directly──────────────► Permissions
162
+ ```
207
163
 
208
- | Input/Output | Type | Description |
209
- |--------------|------|-------------|
210
- | `logic` (input) | `ILogicNode \| null` | Current permission logic tree |
211
- | `actions` (input) | `Array<{id, name}>` | Available actions for dropdowns |
212
- | `logicChange` (output) | `ILogicNode \| null` | Emitted when logic changes |
164
+ **Config:**
165
+ ```typescript
166
+ { permissionMode: 'FULL' }
167
+ ```
213
168
 
214
169
  ---
215
170
 
216
- ### Selector Components (Common Pattern)
217
-
218
- All selector components share a common pattern with these signals:
171
+ ## Module Registration
219
172
 
220
- | Signal | Description |
221
- |--------|-------------|
222
- | `selectedXxxId` | Selected entity ID (user/role/company) |
223
- | `treeNodes` / `items` | Available items (TreeNode[] or flat array) |
224
- | `flatActions` | Flattened action list (for action selectors) |
225
- | `selectedIds` | Currently selected IDs (Set) |
226
- | `originalSelectedIds` | Original state for change tracking |
227
- | `isLoading` / `isSaving` | Loading/saving states |
228
- | `pendingAdd` / `pendingRemove` | Computed: items to add/remove |
229
- | `hasChanges` | Computed: whether there are pending changes |
173
+ ### `provideIamProviders()`
230
174
 
231
- Common features: AbortController for request cancellation, responsive tables with `overflow-x-auto`, change tracking, select/deselect all.
232
-
233
- ---
234
-
235
- ### UserActionSelectorComponent
175
+ ```typescript
176
+ import { provideIamProviders } from '@flusys/ng-iam';
236
177
 
237
- **Selector:** `flusys-user-action-selector` | Assigns actions directly to users (DIRECT/FULL mode).
178
+ // app.config.ts
179
+ providers: [
180
+ ...provideIamProviders(),
181
+ ]
182
+ ```
238
183
 
239
- - User dropdown (`lib-user-select` from ng-shared)
240
- - Branch selector (conditional, from USER_PERMISSION_PROVIDER)
241
- - Hierarchical TreeTable with checkboxes
242
- - Prerequisite validation with tooltips
184
+ Registers:
185
+ - `PERMISSION_VALIDATOR` implementation of `IPermissionValidator` used by `HasPermissionDirective` and `PermissionGuard` from `ng-shared`
243
186
 
244
187
  ---
245
188
 
246
- ### UserRoleSelectorComponent
189
+ ## Permission State Service
247
190
 
248
- **Selector:** `flusys-user-role-selector` | Assigns roles to users (RBAC/FULL mode).
191
+ `PermissionStateService` is the signal-based store for the current user's loaded permissions. It caches permissions for **1 hour** and auto-invalidates when roles or direct permissions change.
249
192
 
250
- - User dropdown with branch selector
251
- - DataTable with pagination
252
- - No prerequisite validation (roles are simple assignments)
253
-
254
- ---
255
-
256
- ### RoleActionSelectorComponent
193
+ ```typescript
194
+ import { PermissionStateService } from '@flusys/ng-iam';
195
+
196
+ @Component({ ... })
197
+ export class MyComponent {
198
+ private permState = inject(PermissionStateService);
199
+
200
+ // Signals
201
+ permissions = this.permState.permissions; // Signal<string[]>
202
+ isLoaded = this.permState.isLoaded; // Signal<boolean>
203
+ isLoading = this.permState.isLoading; // Signal<boolean>
204
+
205
+ // Check permission reactively
206
+ canEdit = computed(() =>
207
+ this.permState.hasPermission('product:update')
208
+ );
209
+
210
+ canDelete = computed(() =>
211
+ this.permState.hasAnyPermission(['product:delete', 'admin:manage'])
212
+ );
213
+
214
+ // Load permissions (called by appInitGuard after login)
215
+ loadPermissions(): void {
216
+ this.permState.load();
217
+ }
218
+
219
+ // Invalidate cache (call after role/permission changes)
220
+ invalidate(): void {
221
+ this.permState.invalidate();
222
+ }
223
+ }
224
+ ```
257
225
 
258
- **Selector:** `flusys-role-action-selector` | Assigns actions to roles (RBAC/FULL mode).
226
+ **PermissionStateService API:**
259
227
 
260
- - Role dropdown with filter
261
- - Hierarchical TreeTable with checkboxes
262
- - Prerequisite validation with auto-fix dialog
228
+ | Member | Type | Description |
229
+ |--------|------|-------------|
230
+ | `permissions` | `Signal<string[]>` | Current user's permission list |
231
+ | `isLoaded` | `Signal<boolean>` | True when permissions are loaded |
232
+ | `isLoading` | `Signal<boolean>` | True when loading is in progress |
233
+ | `hasPermission(key)` | `boolean` | Single permission check |
234
+ | `hasAllPermissions(keys[])` | `boolean` | AND logic — all must match |
235
+ | `hasAnyPermission(keys[])` | `boolean` | OR logic — at least one must match |
236
+ | `load()` | `Observable<void>` | Load permissions from backend |
237
+ | `invalidate()` | `void` | Clear cache and force reload |
263
238
 
264
- ---
239
+ ### Permission Key Format
265
240
 
266
- ### CompanyActionSelectorComponent
241
+ ```
242
+ resource:action
243
+ ```
267
244
 
268
- **Selector:** `flusys-company-action-selector` | Manages company action whitelisting.
245
+ Examples:
246
+ ```
247
+ product:read
248
+ product:create
249
+ product:update
250
+ product:delete
251
+ admin:manage
252
+ user:read
253
+ report:export
254
+ ```
269
255
 
270
- - Company dropdown (from COMPANY_API_PROVIDER)
271
- - Full action tree (not filtered)
272
- - Backend prerequisite error handling with auto-fix
256
+ Wildcard support:
257
+ ```
258
+ * — all permissions
259
+ product:* — all product actions
260
+ ```
273
261
 
274
262
  ---
275
263
 
276
- ## Page Components
264
+ ## IAM API Services
277
265
 
278
- Page components are **internal** (not exported). Accessed via `IAM_ROUTES`.
279
-
280
- | Component | Selector | Features |
281
- |-----------|----------|----------|
282
- | `IamContainerComponent` | `lib-iam-container` | Tabbed navigation, responsive tabs |
283
- | `ActionListPageComponent` | `lib-action-list-page` | TreeTable, CRUD, read-only indicators |
284
- | `ActionFormPageComponent` | `lib-action-form-page` | Signal forms, LogicBuilder integration |
285
- | `RoleListPageComponent` | `lib-role-list-page` | Paginated table (10/25/50), CRUD |
286
- | `RoleFormPageComponent` | `lib-role-form-page` | Signal forms, company auto-assignment |
287
- | `PermissionPageComponent` | `lib-permission-page` | Dynamic tabs based on permission mode |
288
-
289
- ---
290
-
291
- ## Interfaces
266
+ ### ActionApiService
292
267
 
293
- ### Core Types
268
+ Manages available permission actions (resource:action pairs):
294
269
 
295
270
  ```typescript
296
- enum ActionType { BACKEND = 'backend', FRONTEND = 'frontend', BOTH = 'both' }
297
- type PermissionAction = 'add' | 'remove';
298
- type PermissionMode = 'rbac' | 'direct' | 'full';
299
- ```
271
+ import { ActionApiService } from '@flusys/ng-iam';
300
272
 
301
- ### IAction (extends IBaseEntity)
273
+ @Injectable({ ... })
274
+ export class MyService {
275
+ private actionApi = inject(ActionApiService);
302
276
 
303
- ```typescript
304
- interface IAction extends IBaseEntity {
305
- readOnly: boolean;
306
- name: string;
307
- description: string | null;
308
- code: string | null;
309
- actionType: ActionType;
310
- permissionLogic: ILogicNode | null; // AND/OR prerequisite rules
311
- parentId: string | null;
312
- serial: number | null;
313
- isActive: boolean;
314
- metadata: Record<string, unknown> | null;
277
+ getActions(): Observable<IListResponse<IAction>> {
278
+ return this.actionApi.getAll({ pageSize: 100 });
279
+ }
315
280
  }
316
- interface IActionTreeDto extends IAction { children: IActionTreeDto[]; }
317
281
  ```
318
282
 
319
- ### IRole (extends IBaseEntity)
283
+ ### RoleApiService
284
+
285
+ Manages roles and their permission assignments:
320
286
 
321
287
  ```typescript
322
- interface IRole extends IBaseEntity {
323
- readOnly: boolean;
324
- name: string;
325
- description: string | null;
326
- companyId: string | null;
327
- isActive: boolean;
328
- serial: number | null;
329
- metadata: Record<string, unknown> | null;
330
- }
331
- ```
288
+ import { RoleApiService } from '@flusys/ng-iam';
332
289
 
333
- ### Assignment DTOs
290
+ @Injectable({ ... })
291
+ export class MyService {
292
+ private roleApi = inject(RoleApiService);
334
293
 
335
- ```typescript
336
- interface IPermissionItemDto { id: string; action: PermissionAction; }
294
+ createRole(name: string, permissions: string[]): Observable<ISingleResponse<IRole>> {
295
+ return this.roleApi.insert({ name, permissions });
296
+ }
337
297
 
338
- interface IAssignUserActionsDto {
339
- userId: string; companyId?: string; branchId?: string; items: IPermissionItemDto[];
340
- }
341
- interface IAssignRoleActionsDto { roleId: string; items: IPermissionItemDto[]; }
342
- interface IAssignCompanyActionsDto { companyId: string; items: IPermissionItemDto[]; }
343
- interface IAssignUserRolesDto {
344
- userId: string; companyId?: string; branchId?: string; items: IPermissionItemDto[];
298
+ assignRoleToUser(userId: string, roleId: string): Observable<IMessageResponse> {
299
+ return this.roleApi.assignToUser({ userId, roleId });
300
+ }
345
301
  }
346
302
  ```
347
303
 
348
- ### Response DTOs
304
+ ### UserPermissionApiService
349
305
 
350
- All response DTOs include: `id`, entity reference IDs, `actionCode`/`roleCode`, `actionName`/`roleName`, `createdAt`.
306
+ Manages direct permission assignments to users:
351
307
 
352
308
  ```typescript
353
- interface IMyPermissionsResponseDto {
354
- frontendActions: Array<{ id: string; code: string; name: string; description: string }>;
355
- cachedEndpoints: number; // Endpoint count for backend PermissionGuard
356
- }
357
-
358
- interface IPermissionOperationResultDto {
359
- success: boolean; added: number; removed: number; message: string;
360
- prerequisiteErrors?: IPrerequisiteValidationError[];
361
- }
362
- ```
309
+ import { UserPermissionApiService } from '@flusys/ng-iam';
363
310
 
364
- ### Simple Models
311
+ @Injectable({ ... })
312
+ export class MyService {
313
+ private userPermApi = inject(UserPermissionApiService);
365
314
 
366
- ```typescript
367
- interface IUser { id: string; name: string; email: string; }
368
- interface IBranch { id: string; name: string; companyId: string; }
369
- interface ICompany { id: string; name: string; slug?: string; }
315
+ assignPermissions(userId: string, permissions: string[]): Observable<IMessageResponse> {
316
+ return this.userPermApi.assign({ userId, permissions });
317
+ }
318
+ }
370
319
  ```
371
320
 
372
321
  ---
373
322
 
374
- ## Utils & Constants
375
-
376
- Utils are **internal** (not exported from public API).
377
-
378
- ### permission-logic.util.ts
379
-
380
- | Function | Description |
381
- |----------|-------------|
382
- | `validateActionPrerequisites(action, selectedIds, allActions)` | Validate prerequisites respecting AND/OR |
383
- | `extractRequiredActionIds(logic, allActions)` | Extract all action IDs from logic tree |
323
+ ## Components
384
324
 
385
- ### tree-utils.ts
325
+ ### PermissionPageComponent
386
326
 
387
- | Function | Description |
388
- |----------|-------------|
389
- | `flattenTree<T>(tree)` | Flatten hierarchical tree into flat array |
390
- | `buildTreeFromFlat<T>(flatList)` | Build tree from flat list using `parentId` |
391
- | `convertActionToTreeNode(actions)` | Convert to PrimeNG `TreeNode<IAction>[]` |
327
+ Full-page IAM management UI. Renders different panels based on `permissionMode`:
392
328
 
393
- ### Constants
329
+ | Mode | Panels Shown |
330
+ |------|-------------|
331
+ | `RBAC` | Roles list, Role editor, User-role assignment |
332
+ | `DIRECT` | User permission editor |
333
+ | `FULL` | All RBAC panels + User permission editor |
394
334
 
395
335
  ```typescript
396
- export const MAX_DROPDOWN_ITEMS = 100; // Max items for dropdown lists
336
+ // In routes:
337
+ {
338
+ path: 'iam',
339
+ loadComponent: () =>
340
+ import('@flusys/ng-iam').then(m => m.PermissionPageComponent),
341
+ }
397
342
  ```
398
343
 
399
- ---
400
-
401
- ## Responsive Table Patterns
344
+ ### LogicBuilderComponent
402
345
 
403
- All IAM tables use consistent Tailwind CSS patterns:
346
+ Visual drag-and-drop AND/OR nested logic builder for complex permission rules.
404
347
 
405
348
  ```html
406
- <!-- Container: horizontal scroll on mobile, full-width on desktop -->
407
- <div class="overflow-x-auto -mx-4 sm:mx-0">
408
- <p-treeTable [tableStyle]="{ 'min-width': '50rem' }">...</p-treeTable>
409
- </div>
410
-
411
- <!-- Responsive columns -->
412
- <th>Name</th> <!-- Always visible -->
413
- <th class="hidden sm:table-cell">Code</th> <!-- 640px+ -->
414
- <th class="hidden md:table-cell">Status</th> <!-- 768px+ -->
415
- <th class="hidden lg:table-cell">Description</th> <!-- 1024px+ -->
349
+ <flusys-logic-builder
350
+ [availableActions]="actions"
351
+ [(logicTree)]="permissionLogic"
352
+ (logicChange)="onLogicChange($event)"
353
+ />
416
354
  ```
417
355
 
418
- | Breakpoint | Class | Visibility |
419
- |------------|-------|------------|
420
- | Always | (none) | Always visible |
421
- | sm | `hidden sm:table-cell` | 640px+ |
422
- | md | `hidden md:table-cell` | 768px+ |
423
- | lg | `hidden lg:table-cell` | 1024px+ |
356
+ **Supports:**
357
+ - AND groups (all conditions must match)
358
+ - OR groups (any condition must match)
359
+ - Nesting (AND inside OR, etc.)
360
+ - Individual permission conditions
361
+ - Visual drag-to-reorder
424
362
 
425
363
  ---
426
364
 
427
- ## Angular 21 Signal Forms Pattern
428
-
429
- ```typescript
430
- import { form, required } from '@angular/forms';
431
-
432
- readonly formModel = {
433
- name: '',
434
- description: '',
435
- code: '',
436
- actionType: ActionType.FRONTEND,
437
- parentId: null as string | null,
438
- serial: null as number | null,
439
- isActive: true,
440
- permissionLogic: null as ILogicNode | null,
441
- };
442
-
443
- readonly actionForm = form(this.formModel, (f) => {
444
- required(f.name, { message: 'Name is required' });
445
- });
365
+ ## Integration
446
366
 
447
- readonly canSubmit = computed(() => this.actionForm.valid() && !this.isSaving());
448
- ```
449
-
450
- ---
367
+ ### With HasPermissionDirective (from ng-shared)
451
368
 
452
- ## Backend Integration
369
+ Once `provideIamProviders()` is registered, `HasPermissionDirective` works automatically:
453
370
 
454
- ### Required Endpoints
371
+ ```html
372
+ <button *hasPermission="'product:delete'">Delete</button>
455
373
 
374
+ <div *hasPermission="['admin:manage', 'user:read']">
375
+ Admin Section
376
+ </div>
456
377
  ```
457
- # Action CRUD
458
- POST /iam/actions/tree
459
- POST /iam/actions/tree-for-permission
460
- POST /iam/actions/get-all
461
- POST /iam/actions/get/:id
462
- POST /iam/actions/insert
463
- POST /iam/actions/update
464
- POST /iam/actions/delete
465
-
466
- # Role CRUD
467
- POST /iam/roles/get-all
468
- POST /iam/roles/get/:id
469
- POST /iam/roles/insert
470
- POST /iam/roles/update
471
- POST /iam/roles/delete
472
-
473
- # Permission Assignments
474
- POST /iam/permissions/my-permissions
475
- POST /iam/permissions/user-actions/assign
476
- POST /iam/permissions/get-user-actions
477
- POST /iam/permissions/user-roles/assign
478
- POST /iam/permissions/get-user-roles
479
- POST /iam/permissions/role-actions/assign
480
- POST /iam/permissions/get-role-actions
481
- POST /iam/permissions/company-actions/assign
482
- POST /iam/permissions/get-company-actions
483
- ```
484
-
485
- ---
486
378
 
487
- ## Best Practices
488
-
489
- ### Permission Loading
379
+ ### With PermissionGuard (from ng-shared)
490
380
 
491
381
  ```typescript
492
- // In guard - block navigation until ready
493
- if (!permissionState.isLoaded()) {
494
- await firstValueFrom(permissionState.loadPermissions());
382
+ {
383
+ path: 'admin',
384
+ canActivate: [PermissionGuard],
385
+ data: { permission: 'admin:manage' },
386
+ loadComponent: () => import('./admin.component'),
495
387
  }
496
388
  ```
497
389
 
498
- - Cache in memory (PermissionStateService handles this)
499
- - Clear on logout
500
- - Never store in localStorage (security risk)
501
- - Never load in component `ngOnInit` (race conditions)
502
-
503
- ### Permission Checks
504
-
505
- ```typescript
506
- validator.hasAction('user.create'); // Single
507
- validator.hasAllActions(['user.create', 'user.update']); // ALL required
508
- validator.hasAnyAction(['user.view', 'user.list']); // ANY required
509
- ```
510
-
511
- Cache check results in computed signals, not in templates.
390
+ ### With appInitGuard (from ng-auth)
512
391
 
513
- ### Menu Filtering
392
+ `appInitGuard` automatically loads permissions after session restoration when `iam.enabled: true`:
514
393
 
515
394
  ```typescript
516
- this.layoutService.setMenu(menuItems);
517
- this.layoutService.setPermissionChecker((code) => validator.hasAction(code));
518
- ```
519
-
520
- ### Action Naming
521
-
522
- Format: `module.operation` or `module.entity.operation`
523
-
524
- ```typescript
525
- // Good: 'user.create', 'report.export', 'admin.system.config'
526
- // Bad: 'create' (generic), 'userCreate' (not dot notation)
527
- ```
528
-
529
- ### LogicNode Pattern
530
-
531
- ```typescript
532
- // User needs 'user.create' AND ('user.update' OR 'user.delete')
533
- const logic: ILogicNode = {
534
- type: 'AND',
535
- children: [
536
- { type: 'ACTION', value: 'user.create' },
537
- { type: 'OR', children: [
538
- { type: 'ACTION', value: 'user.update' },
539
- { type: 'ACTION', value: 'user.delete' },
540
- ]},
541
- ],
542
- };
395
+ // The guard calls PermissionStateService.load() automatically
396
+ // No manual setup needed
543
397
  ```
544
398
 
545
399
  ---
546
400
 
547
- ## Configuration
401
+ ## API Endpoints
548
402
 
549
- ```typescript
550
- // Frontend: environment.ts
551
- services: { iam: { baseUrl: 'http://localhost:2002/iam', enabled: true } }
552
- ```
403
+ | Method | Endpoint | Description |
404
+ |--------|----------|-------------|
405
+ | POST | `/iam/action/get-all` | List all permission actions |
406
+ | POST | `/iam/action/insert` | Create action |
407
+ | POST | `/iam/action/update` | Update action |
408
+ | POST | `/iam/action/delete` | Delete action |
409
+ | POST | `/iam/role/get-all` | List all roles |
410
+ | POST | `/iam/role/get/:id` | Get role by ID |
411
+ | POST | `/iam/role/insert` | Create role |
412
+ | POST | `/iam/role/update` | Update role |
413
+ | POST | `/iam/role/delete` | Delete role |
414
+ | POST | `/iam/role/assign-user` | Assign role to user |
415
+ | POST | `/iam/role/unassign-user` | Remove role from user |
416
+ | POST | `/iam/user-permission/get/:userId` | Get user's direct permissions |
417
+ | POST | `/iam/user-permission/assign` | Assign direct permissions to user |
418
+ | POST | `/iam/permission/get-my-permissions` | Get current user's effective permissions |
553
419
 
554
- ```bash
555
- # Backend: .env
556
- IAM_PERMISSION_MODE=FULL # DIRECT, RBAC, or FULL
557
- ```
420
+ ---
558
421
 
559
- | Scenario | services.iam.enabled | Result |
560
- |----------|---------------------|--------|
561
- | Full IAM | `true` | Permission-based menu |
562
- | IAM Disabled | `false` | Static menu (all items visible) |
563
- | Service Down | `true` (API fails) | Graceful fallback to static menu |
422
+ ## Troubleshooting
564
423
 
565
- ---
424
+ **`HasPermissionDirective` always hides content**
566
425
 
567
- ## Provider Functions
426
+ Permissions aren't loaded. Check that:
427
+ 1. `provideIamProviders()` is in `app.config.ts`
428
+ 2. `services.iam.enabled: true` in environment
429
+ 3. `appInitGuard` runs and calls `PermissionStateService.load()`
568
430
 
569
- ### provideIamProviders()
431
+ **Permissions don't refresh after role change**
570
432
 
571
- Registers adapters for cross-package communication (e.g., ng-auth profile page).
433
+ Call `PermissionStateService.invalidate()` after modifying roles/permissions:
572
434
 
573
435
  ```typescript
574
- import { provideIamProviders } from '@flusys/ng-iam';
575
- providers: [...provideIamProviders()]
436
+ this.permState.invalidate(); // Clears 1-hour cache, forces reload
576
437
  ```
577
438
 
578
- Registers `PROFILE_PERMISSION_PROVIDER` with `ProfilePermissionProviderAdapter`.
439
+ **`FULL` mode shows RBAC UI but not direct permissions UI**
579
440
 
580
- ---
441
+ Verify `permissionMode: 'FULL'` in your environment and that `PermissionPageComponent` reads the mode from `APP_CONFIG`.
581
442
 
582
- ## Common Issues
443
+ **Permission check for wildcards (`product:*`) not working**
583
444
 
584
- | Problem | Solution |
585
- |---------|----------|
586
- | `permissionState.isLoaded()` returns false | Check `services.iam.enabled: true`, verify `/iam/permissions/my-permissions` works |
587
- | Empty menu after login | Verify user has frontend actions, check `requiredAction` matches action codes |
588
- | Menu items not filtered | Call `layoutService.setPermissionChecker()` after loading permissions |
589
- | 401 on IAM endpoints | Ensure auth interceptor adds `Authorization` header |
590
- | Provider not configured error | Add required providers to `app.config.ts` |
591
- | Empty dropdowns | Ensure adapters return `IListResponse<T>` format |
592
- | Table not scrollable on mobile | Add `overflow-x-auto -mx-4 sm:mx-0` wrapper |
593
- | Columns overflow on mobile | Add `hidden sm:table-cell` to non-essential columns |
445
+ Ensure the backend sends flat expanded permissions, not wildcards. The client evaluates exact string matches plus wildcard expansion.
594
446
 
595
447
  ---
596
448
 
597
- ## See Also
598
-
599
- - [CORE-GUIDE.md](./CORE-GUIDE.md) - Configuration system (APP_CONFIG)
600
- - [AUTH-GUIDE.md](./AUTH-GUIDE.md) - Authentication and provider adapters
601
- - [SHARED-GUIDE.md](./SHARED-GUIDE.md) - Provider interface definitions
602
- - [LAYOUT-GUIDE.md](./LAYOUT-GUIDE.md) - Menu system and filtering
603
-
604
- ---
449
+ ## License
605
450
 
606
- **Last Updated:** 2026-02-25
607
- **Version:** 3.0.1
608
- **Angular Version:** 21
451
+ MIT © FLUSYS