@flusys/nestjs-iam 7.1.0 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. package/config/index.d.ts +3 -3
  2. package/config/message-keys.d.ts +2 -0
  3. package/controllers/action.controller.d.ts +4 -4
  4. package/controllers/company-action-permission.controller.d.ts +2 -2
  5. package/controllers/index.d.ts +6 -6
  6. package/controllers/my-permission.controller.d.ts +3 -3
  7. package/controllers/role-permission.controller.d.ts +5 -4
  8. package/controllers/role.controller.d.ts +2 -2
  9. package/controllers/user-action-permission.controller.d.ts +5 -4
  10. package/docs/iam-swagger.config.d.ts +2 -2
  11. package/docs/index.d.ts +1 -1
  12. package/dtos/action.dto.d.ts +2 -2
  13. package/dtos/index.d.ts +3 -3
  14. package/dtos/permission.dto.d.ts +16 -0
  15. package/entities/action.entity.d.ts +2 -2
  16. package/entities/index.d.ts +11 -11
  17. package/entities/permission-with-company.entity.d.ts +1 -1
  18. package/entities/role-with-company.entity.d.ts +1 -1
  19. package/entities/role.entity.d.ts +1 -1
  20. package/entities/user-iam-permission.entity.d.ts +1 -1
  21. package/enums/index.d.ts +4 -4
  22. package/fesm/284.js +1316 -0
  23. package/fesm/867.js +3801 -0
  24. package/fesm/chunks/364.js +86 -0
  25. package/fesm/config/index.js +135 -3
  26. package/fesm/controllers/index.js +18 -7
  27. package/fesm/docs/index.js +240 -1
  28. package/fesm/dtos/index.js +1168 -3
  29. package/fesm/entities/index.js +670 -31
  30. package/fesm/enums/index.js +97 -4
  31. package/fesm/helpers/index.js +125 -2
  32. package/fesm/index.js +818 -11
  33. package/fesm/interfaces/index.js +17 -3
  34. package/fesm/modules/index.js +259 -1
  35. package/fesm/runtime.js +134 -0
  36. package/fesm/services/index.js +43 -6
  37. package/fesm/types/index.js +15 -1
  38. package/helpers/company-access.helper.d.ts +2 -2
  39. package/helpers/index.d.ts +2 -2
  40. package/helpers/permission-mode.helper.d.ts +1 -1
  41. package/index.d.ts +11 -11
  42. package/interfaces/action.interface.d.ts +2 -2
  43. package/interfaces/iam-module-options.interface.d.ts +3 -3
  44. package/interfaces/index.d.ts +3 -3
  45. package/modules/iam.module.d.ts +2 -2
  46. package/modules/index.d.ts +1 -1
  47. package/package.json +30 -37
  48. package/services/action.service.d.ts +9 -9
  49. package/services/iam-config.service.d.ts +4 -4
  50. package/services/iam-datasource.service.d.ts +3 -3
  51. package/services/index.d.ts +6 -6
  52. package/services/permission-cache.service.d.ts +5 -5
  53. package/services/permission.service.d.ts +6 -4
  54. package/services/role.service.d.ts +8 -8
  55. package/types/index.d.ts +1 -1
  56. package/cjs/config/event-actions.js +0 -36
  57. package/cjs/config/iam.constants.js +0 -11
  58. package/cjs/config/index.js +0 -20
  59. package/cjs/config/message-keys.js +0 -71
  60. package/cjs/controllers/action.controller.js +0 -174
  61. package/cjs/controllers/action.controller.spec.js +0 -79
  62. package/cjs/controllers/company-action-permission.controller.js +0 -119
  63. package/cjs/controllers/company-action-permission.controller.spec.js +0 -65
  64. package/cjs/controllers/index.js +0 -23
  65. package/cjs/controllers/my-permission.controller.js +0 -147
  66. package/cjs/controllers/my-permission.controller.spec.js +0 -128
  67. package/cjs/controllers/role-permission.controller.js +0 -191
  68. package/cjs/controllers/role-permission.controller.spec.js +0 -140
  69. package/cjs/controllers/role.controller.js +0 -102
  70. package/cjs/controllers/role.controller.spec.js +0 -86
  71. package/cjs/controllers/user-action-permission.controller.js +0 -129
  72. package/cjs/controllers/user-action-permission.controller.spec.js +0 -68
  73. package/cjs/docs/iam-swagger.config.js +0 -209
  74. package/cjs/docs/iam-swagger.config.spec.js +0 -99
  75. package/cjs/docs/index.js +0 -18
  76. package/cjs/dtos/action.dto.js +0 -282
  77. package/cjs/dtos/index.js +0 -21
  78. package/cjs/dtos/permission.dto.js +0 -637
  79. package/cjs/dtos/role.dto.js +0 -155
  80. package/cjs/entities/action.entity.js +0 -138
  81. package/cjs/entities/index.js +0 -80
  82. package/cjs/entities/index.spec.js +0 -56
  83. package/cjs/entities/permission-base.entity.js +0 -107
  84. package/cjs/entities/permission-with-company.entity.js +0 -100
  85. package/cjs/entities/role-base.entity.js +0 -80
  86. package/cjs/entities/role-with-company.entity.js +0 -55
  87. package/cjs/entities/role.entity.js +0 -25
  88. package/cjs/entities/user-iam-permission.entity.js +0 -43
  89. package/cjs/enums/action-type.enum.js +0 -22
  90. package/cjs/enums/iam-entity-type.enum.js +0 -17
  91. package/cjs/enums/iam-permission-type.enum.js +0 -17
  92. package/cjs/enums/index.js +0 -21
  93. package/cjs/enums/permission-type.enum.js +0 -16
  94. package/cjs/helpers/company-access.helper.js +0 -23
  95. package/cjs/helpers/company-access.helper.spec.js +0 -68
  96. package/cjs/helpers/index.js +0 -19
  97. package/cjs/helpers/permission-mode.helper.js +0 -49
  98. package/cjs/helpers/permission-mode.helper.spec.js +0 -54
  99. package/cjs/index.js +0 -28
  100. package/cjs/interfaces/action.interface.js +0 -4
  101. package/cjs/interfaces/iam-module-options.interface.js +0 -4
  102. package/cjs/interfaces/index.js +0 -20
  103. package/cjs/interfaces/role.interface.js +0 -7
  104. package/cjs/modules/iam.module.js +0 -186
  105. package/cjs/modules/iam.module.spec.js +0 -282
  106. package/cjs/modules/index.js +0 -18
  107. package/cjs/services/action.service.js +0 -337
  108. package/cjs/services/action.service.spec.js +0 -351
  109. package/cjs/services/iam-config.service.js +0 -87
  110. package/cjs/services/iam-config.service.spec.js +0 -110
  111. package/cjs/services/iam-datasource.service.js +0 -129
  112. package/cjs/services/iam-datasource.service.spec.js +0 -110
  113. package/cjs/services/index.js +0 -23
  114. package/cjs/services/permission-cache.service.js +0 -544
  115. package/cjs/services/permission-cache.service.spec.js +0 -718
  116. package/cjs/services/permission.service.js +0 -914
  117. package/cjs/services/permission.service.spec.js +0 -918
  118. package/cjs/services/role.service.js +0 -224
  119. package/cjs/services/role.service.spec.js +0 -347
  120. package/cjs/types/index.js +0 -18
  121. package/cjs/types/logic-node.type.js +0 -30
  122. package/fesm/config/event-actions.js +0 -15
  123. package/fesm/config/iam.constants.js +0 -1
  124. package/fesm/config/message-keys.js +0 -35
  125. package/fesm/controllers/action.controller.js +0 -164
  126. package/fesm/controllers/action.controller.spec.js +0 -75
  127. package/fesm/controllers/company-action-permission.controller.js +0 -109
  128. package/fesm/controllers/company-action-permission.controller.spec.js +0 -61
  129. package/fesm/controllers/my-permission.controller.js +0 -137
  130. package/fesm/controllers/my-permission.controller.spec.js +0 -124
  131. package/fesm/controllers/role-permission.controller.js +0 -181
  132. package/fesm/controllers/role-permission.controller.spec.js +0 -136
  133. package/fesm/controllers/role.controller.js +0 -92
  134. package/fesm/controllers/role.controller.spec.js +0 -82
  135. package/fesm/controllers/user-action-permission.controller.js +0 -119
  136. package/fesm/controllers/user-action-permission.controller.spec.js +0 -64
  137. package/fesm/docs/iam-swagger.config.js +0 -199
  138. package/fesm/docs/iam-swagger.config.spec.js +0 -95
  139. package/fesm/dtos/action.dto.js +0 -252
  140. package/fesm/dtos/permission.dto.js +0 -571
  141. package/fesm/dtos/role.dto.js +0 -134
  142. package/fesm/entities/action.entity.js +0 -128
  143. package/fesm/entities/index.spec.js +0 -52
  144. package/fesm/entities/permission-base.entity.js +0 -100
  145. package/fesm/entities/permission-with-company.entity.js +0 -90
  146. package/fesm/entities/role-base.entity.js +0 -70
  147. package/fesm/entities/role-with-company.entity.js +0 -45
  148. package/fesm/entities/role.entity.js +0 -15
  149. package/fesm/entities/user-iam-permission.entity.js +0 -33
  150. package/fesm/enums/action-type.enum.js +0 -12
  151. package/fesm/enums/iam-entity-type.enum.js +0 -7
  152. package/fesm/enums/iam-permission-type.enum.js +0 -7
  153. package/fesm/enums/permission-type.enum.js +0 -6
  154. package/fesm/helpers/company-access.helper.js +0 -18
  155. package/fesm/helpers/company-access.helper.spec.js +0 -64
  156. package/fesm/helpers/permission-mode.helper.js +0 -49
  157. package/fesm/helpers/permission-mode.helper.spec.js +0 -50
  158. package/fesm/interfaces/action.interface.js +0 -3
  159. package/fesm/interfaces/iam-module-options.interface.js +0 -3
  160. package/fesm/interfaces/role.interface.js +0 -4
  161. package/fesm/modules/iam.module.js +0 -176
  162. package/fesm/modules/iam.module.spec.js +0 -278
  163. package/fesm/services/action.service.js +0 -327
  164. package/fesm/services/action.service.spec.js +0 -347
  165. package/fesm/services/iam-config.service.js +0 -77
  166. package/fesm/services/iam-config.service.spec.js +0 -106
  167. package/fesm/services/iam-datasource.service.js +0 -78
  168. package/fesm/services/iam-datasource.service.spec.js +0 -106
  169. package/fesm/services/permission-cache.service.js +0 -527
  170. package/fesm/services/permission-cache.service.spec.js +0 -714
  171. package/fesm/services/permission.service.js +0 -904
  172. package/fesm/services/permission.service.spec.js +0 -914
  173. package/fesm/services/role.service.js +0 -214
  174. package/fesm/services/role.service.spec.js +0 -343
  175. package/fesm/types/logic-node.type.js +0 -27
@@ -1,904 +0,0 @@
1
- function _define_property(obj, key, value) {
2
- if (key in obj) {
3
- Object.defineProperty(obj, key, {
4
- value: value,
5
- enumerable: true,
6
- configurable: true,
7
- writable: true
8
- });
9
- } else {
10
- obj[key] = value;
11
- }
12
- return obj;
13
- }
14
- function _ts_decorate(decorators, target, key, desc) {
15
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
16
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
17
- else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
18
- return c > 3 && r && Object.defineProperty(target, key, r), r;
19
- }
20
- function _ts_metadata(k, v) {
21
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
22
- }
23
- function _ts_param(paramIndex, decorator) {
24
- return function(target, key) {
25
- decorator(target, key, paramIndex);
26
- };
27
- }
28
- import { LogAction } from '@flusys/nestjs-shared';
29
- import { publishDomainEvent } from '@flusys/nestjs-shared/classes';
30
- import { BadRequestException, ConflictException, Inject, Injectable, Scope } from '@nestjs/common';
31
- import { In, IsNull } from 'typeorm';
32
- import { IAM_EVENT_ACTIONS, IAM_EVENT_ENTITIES, IAM_EVENT_MODULE, IAM_MODE_MESSAGES, PERMISSION_OPERATION_MESSAGES } from '../config';
33
- import { AssignCompanyActionsDto, AssignRoleActionsDto, AssignUserActionsDto, AssignUserRolesDto, PermissionAction } from '../dtos/permission.dto';
34
- import { Action } from '../entities/action.entity';
35
- import { UserIamPermissionWithCompany } from '../entities/permission-with-company.entity';
36
- import { RoleWithCompany } from '../entities/role-with-company.entity';
37
- import { Role } from '../entities/role.entity';
38
- import { UserIamPermission } from '../entities/user-iam-permission.entity';
39
- import { IamEntityType } from '../enums/iam-entity-type.enum';
40
- import { IamPermissionType } from '../enums/iam-permission-type.enum';
41
- import { IAMConfigService } from './iam-config.service';
42
- import { IAMDataSourceService } from './iam-datasource.service';
43
- import { PermissionCacheService } from './permission-cache.service';
44
- export class PermissionService {
45
- // Repository Getters
46
- async getPermissionRepository() {
47
- const enableCompanyFeature = this.iamConfigService.isCompanyFeatureEnabled();
48
- const entity = enableCompanyFeature ? UserIamPermissionWithCompany : UserIamPermission;
49
- return this.dataSourceProvider.getRepository(entity);
50
- }
51
- async getActionRepository() {
52
- return this.dataSourceProvider.getRepository(Action);
53
- }
54
- async getRoleRepository() {
55
- const enableCompanyFeature = this.iamConfigService.isCompanyFeatureEnabled();
56
- const entity = enableCompanyFeature ? RoleWithCompany : Role;
57
- return this.dataSourceProvider.getRepository(entity);
58
- }
59
- async getActionsMap(actionIds) {
60
- const actionRepo = await this.getActionRepository();
61
- const actions = await actionRepo.find({
62
- where: {
63
- id: In(actionIds)
64
- }
65
- });
66
- return new Map(actions.map((a)=>[
67
- a.id,
68
- a
69
- ]));
70
- }
71
- // User-Action Permissions
72
- async assignUserActions(dto) {
73
- if (!this.iamConfigService.isDirectPermissionEnabled()) {
74
- throw new BadRequestException({
75
- message: 'Direct permission assignment not available in RBAC-only mode. Use role-based permissions instead.',
76
- messageKey: IAM_MODE_MESSAGES.DIRECT_MODE_UNAVAILABLE
77
- });
78
- }
79
- const permissionRepo = await this.getPermissionRepository();
80
- const enableCompanyFeature = this.iamConfigService.isCompanyFeatureEnabled();
81
- const branchId = dto.branchId ?? null;
82
- const companyId = dto.companyId ?? null;
83
- const { toAdd: itemsToAdd, toRemove: itemsToRemove } = this.splitItemsByAction(dto.items);
84
- let added = 0;
85
- let removed = 0;
86
- if (itemsToAdd.length > 0) {
87
- const actionIdsToAdd = itemsToAdd.map((item)=>item.id);
88
- const whereFind = {
89
- permissionType: IamPermissionType.USER_ACTION,
90
- sourceType: IamEntityType.USER,
91
- sourceId: dto.userId,
92
- targetType: IamEntityType.ACTION,
93
- targetId: In(actionIdsToAdd)
94
- };
95
- if (enableCompanyFeature) {
96
- if (companyId) whereFind.companyId = companyId;
97
- if (branchId) whereFind.branchId = branchId;
98
- }
99
- const existingPermissions = await permissionRepo.find({
100
- where: whereFind,
101
- select: [
102
- 'targetId'
103
- ]
104
- });
105
- const existingActionIds = new Set(existingPermissions.map((p)=>p.targetId));
106
- const newPermissions = itemsToAdd.filter((item)=>!existingActionIds.has(item.id)).map((item)=>({
107
- permissionType: IamPermissionType.USER_ACTION,
108
- sourceType: IamEntityType.USER,
109
- sourceId: dto.userId,
110
- targetType: IamEntityType.ACTION,
111
- targetId: item.id,
112
- companyId: enableCompanyFeature ? companyId : null,
113
- branchId: enableCompanyFeature ? branchId : null
114
- }));
115
- if (newPermissions.length > 0) {
116
- try {
117
- await permissionRepo.save(newPermissions);
118
- added = newPermissions.length;
119
- } catch (error) {
120
- if (error?.code === 'ER_DUP_ENTRY' || error?.message?.includes('Duplicate entry')) {
121
- throw new ConflictException({
122
- message: 'Some permissions already exist for this user. Please refresh and try again.',
123
- messageKey: PERMISSION_OPERATION_MESSAGES.ALREADY_EXISTS
124
- });
125
- }
126
- throw error;
127
- }
128
- }
129
- }
130
- if (itemsToRemove.length > 0) {
131
- const actionIdsToRemove = itemsToRemove.map((item)=>item.id);
132
- const whereDelete = {
133
- permissionType: IamPermissionType.USER_ACTION,
134
- sourceType: IamEntityType.USER,
135
- sourceId: dto.userId,
136
- targetType: IamEntityType.ACTION,
137
- targetId: In(actionIdsToRemove)
138
- };
139
- if (enableCompanyFeature) {
140
- if (companyId) whereDelete.companyId = companyId;
141
- if (branchId) whereDelete.branchId = branchId;
142
- }
143
- const result = await permissionRepo.delete(whereDelete);
144
- removed = result.affected || 0;
145
- }
146
- await this.permissionCacheService.invalidateUser(dto.userId, enableCompanyFeature ? companyId : null, enableCompanyFeature ? [
147
- branchId
148
- ] : []);
149
- const result = this.buildOperationResult(dto.items.length, added, removed);
150
- await this.publishAssignmentEvent(IAM_EVENT_ENTITIES.USER_ACTION, dto.userId, result);
151
- return result;
152
- }
153
- async getUserActions(userId, companyId, branchId) {
154
- const permissionRepo = await this.getPermissionRepository();
155
- const enableCompanyFeature = this.iamConfigService.isCompanyFeatureEnabled();
156
- const where = {
157
- permissionType: IamPermissionType.USER_ACTION,
158
- sourceType: IamEntityType.USER,
159
- sourceId: userId
160
- };
161
- if (enableCompanyFeature) {
162
- if (companyId) where.companyId = companyId;
163
- if (branchId) where.branchId = branchId;
164
- }
165
- const permissions = await permissionRepo.find({
166
- where
167
- });
168
- if (permissions.length === 0) {
169
- return [];
170
- }
171
- const actionIds = permissions.map((p)=>p.targetId);
172
- const actionMap = await this.getActionsMap(actionIds);
173
- return permissions.filter((p)=>actionMap.has(p.targetId)).map((p)=>{
174
- const action = actionMap.get(p.targetId);
175
- return {
176
- id: p.id,
177
- userId: p.sourceId,
178
- actionId: action.id,
179
- actionCode: action.code ?? '',
180
- actionName: action.name,
181
- companyId: ('companyId' in p ? p.companyId : null) ?? null,
182
- branchId: ('branchId' in p ? p.branchId : null) ?? null,
183
- createdAt: p.createdAt
184
- };
185
- });
186
- }
187
- // Role-Action Permissions
188
- async assignRoleActions(dto) {
189
- if (!this.iamConfigService.isRbacEnabled()) {
190
- throw new BadRequestException({
191
- message: 'Role-based permission assignment not available in DIRECT-only mode. Use direct user permissions instead.',
192
- messageKey: IAM_MODE_MESSAGES.RBAC_MODE_UNAVAILABLE
193
- });
194
- }
195
- const permissionRepo = await this.getPermissionRepository();
196
- const enableCompanyFeature = this.iamConfigService.isCompanyFeatureEnabled();
197
- let roleCompanyId = null;
198
- if (enableCompanyFeature) {
199
- const roleRepo = await this.getRoleRepository();
200
- const role = await roleRepo.findOne({
201
- where: {
202
- id: dto.roleId
203
- },
204
- select: [
205
- 'id',
206
- 'companyId'
207
- ]
208
- });
209
- roleCompanyId = role?.companyId ?? null;
210
- }
211
- const { toAdd: itemsToAdd, toRemove: itemsToRemove } = this.splitItemsByAction(dto.items);
212
- let added = 0;
213
- let removed = 0;
214
- if (itemsToAdd.length > 0) {
215
- const actionIdsToAdd = itemsToAdd.map((item)=>item.id);
216
- const existingPermissions = await permissionRepo.find({
217
- where: {
218
- permissionType: IamPermissionType.ROLE_ACTION,
219
- sourceType: IamEntityType.ROLE,
220
- sourceId: dto.roleId,
221
- targetType: IamEntityType.ACTION,
222
- targetId: In(actionIdsToAdd)
223
- },
224
- select: [
225
- 'targetId'
226
- ]
227
- });
228
- const existingActionIds = new Set(existingPermissions.map((p)=>p.targetId));
229
- const newPermissions = itemsToAdd.filter((item)=>!existingActionIds.has(item.id)).map((item)=>({
230
- permissionType: IamPermissionType.ROLE_ACTION,
231
- sourceType: IamEntityType.ROLE,
232
- sourceId: dto.roleId,
233
- targetType: IamEntityType.ACTION,
234
- targetId: item.id,
235
- companyId: enableCompanyFeature ? roleCompanyId : null,
236
- branchId: null
237
- }));
238
- if (newPermissions.length > 0) {
239
- try {
240
- await permissionRepo.save(newPermissions);
241
- added = newPermissions.length;
242
- } catch (error) {
243
- if (error?.code === 'ER_DUP_ENTRY' || error?.message?.includes('Duplicate entry')) {
244
- throw new ConflictException({
245
- message: 'Some role-action permissions already exist. Please refresh and try again.',
246
- messageKey: PERMISSION_OPERATION_MESSAGES.ALREADY_EXISTS
247
- });
248
- }
249
- throw error;
250
- }
251
- }
252
- }
253
- if (itemsToRemove.length > 0) {
254
- const actionIdsToRemove = itemsToRemove.map((item)=>item.id);
255
- const result = await permissionRepo.delete({
256
- permissionType: IamPermissionType.ROLE_ACTION,
257
- sourceType: IamEntityType.ROLE,
258
- sourceId: dto.roleId,
259
- targetType: IamEntityType.ACTION,
260
- targetId: In(actionIdsToRemove)
261
- });
262
- removed = result.affected || 0;
263
- }
264
- await this.permissionCacheService.invalidateRoleMembersCache(dto.roleId);
265
- const result = this.buildOperationResult(dto.items.length, added, removed);
266
- await this.publishAssignmentEvent(IAM_EVENT_ENTITIES.ROLE_ACTION, dto.roleId, result);
267
- return result;
268
- }
269
- async getRoleActions(roleId) {
270
- const permissionRepo = await this.getPermissionRepository();
271
- const permissions = await permissionRepo.find({
272
- where: {
273
- permissionType: IamPermissionType.ROLE_ACTION,
274
- sourceType: IamEntityType.ROLE,
275
- sourceId: roleId
276
- }
277
- });
278
- if (permissions.length === 0) {
279
- return [];
280
- }
281
- const actionIds = permissions.map((p)=>p.targetId);
282
- const actionMap = await this.getActionsMap(actionIds);
283
- return permissions.filter((p)=>actionMap.has(p.targetId)).map((p)=>{
284
- const action = actionMap.get(p.targetId);
285
- return {
286
- id: p.id,
287
- roleId: p.sourceId,
288
- actionId: action.id,
289
- actionCode: action.code ?? '',
290
- actionName: action.name,
291
- createdAt: p.createdAt
292
- };
293
- });
294
- }
295
- // Company-Action Permissions (Whitelist)
296
- /** Assign or remove actions to/from a company (whitelist) */ async assignCompanyActions(dto) {
297
- const permissionRepo = await this.getPermissionRepository();
298
- const dataSource = await this.dataSourceProvider.getDataSource();
299
- const enableCompanyFeature = this.iamConfigService.isCompanyFeatureEnabled();
300
- const { toAdd: itemsToAdd, toRemove: itemsToRemove } = this.splitItemsByAction(dto.items);
301
- let added = 0;
302
- let removed = 0;
303
- await dataSource.transaction(async (manager)=>{
304
- const transactionalPermissionRepo = manager.getRepository(permissionRepo.target);
305
- if (itemsToAdd.length > 0) {
306
- const actionIdsToAdd = itemsToAdd.map((item)=>item.id);
307
- const existingPermissions = await transactionalPermissionRepo.find({
308
- where: {
309
- permissionType: IamPermissionType.COMPANY_ACTION,
310
- sourceType: IamEntityType.COMPANY,
311
- sourceId: dto.companyId,
312
- targetType: IamEntityType.ACTION,
313
- targetId: In(actionIdsToAdd)
314
- },
315
- select: [
316
- 'targetId'
317
- ]
318
- });
319
- const existingActionIds = new Set(existingPermissions.map((p)=>p.targetId));
320
- const newActionIds = actionIdsToAdd.filter((id)=>!existingActionIds.has(id));
321
- if (newActionIds.length > 0) {
322
- const newPermissions = newActionIds.map((actionId)=>transactionalPermissionRepo.create({
323
- permissionType: IamPermissionType.COMPANY_ACTION,
324
- sourceType: IamEntityType.COMPANY,
325
- sourceId: dto.companyId,
326
- targetType: IamEntityType.ACTION,
327
- targetId: actionId
328
- }));
329
- await transactionalPermissionRepo.save(newPermissions);
330
- added = newPermissions.length;
331
- }
332
- }
333
- if (itemsToRemove.length > 0) {
334
- const actionIdsToRemove = itemsToRemove.map((item)=>item.id);
335
- const companyResult = await transactionalPermissionRepo.delete({
336
- permissionType: IamPermissionType.COMPANY_ACTION,
337
- sourceType: IamEntityType.COMPANY,
338
- sourceId: dto.companyId,
339
- targetType: IamEntityType.ACTION,
340
- targetId: In(actionIdsToRemove)
341
- });
342
- removed = companyResult.affected || 0;
343
- // Cascade: an action no longer on the company whitelist must not remain
344
- // reachable via role grants or direct user grants scoped to this company.
345
- const roleEntity = enableCompanyFeature ? RoleWithCompany : Role;
346
- const roleRepo = manager.getRepository(roleEntity);
347
- const companyRoles = await roleRepo.find({
348
- where: {
349
- companyId: dto.companyId,
350
- deletedAt: IsNull()
351
- },
352
- select: [
353
- 'id'
354
- ]
355
- });
356
- if (companyRoles.length > 0) {
357
- const roleIds = companyRoles.map((role)=>role.id);
358
- await transactionalPermissionRepo.delete({
359
- permissionType: IamPermissionType.ROLE_ACTION,
360
- sourceType: IamEntityType.ROLE,
361
- sourceId: In(roleIds),
362
- targetType: IamEntityType.ACTION,
363
- targetId: In(actionIdsToRemove)
364
- });
365
- }
366
- if (enableCompanyFeature) {
367
- await transactionalPermissionRepo.delete({
368
- permissionType: IamPermissionType.USER_ACTION,
369
- companyId: dto.companyId,
370
- targetType: IamEntityType.ACTION,
371
- targetId: In(actionIdsToRemove)
372
- });
373
- }
374
- }
375
- });
376
- await this.permissionCacheService.invalidateCompanyMembersCache(dto.companyId);
377
- const result = this.buildOperationResult(dto.items.length, added, removed);
378
- await this.publishAssignmentEvent(IAM_EVENT_ENTITIES.COMPANY_ACTION, dto.companyId, result);
379
- return result;
380
- }
381
- /** Get all actions assigned to a company (whitelist) */ async getCompanyActions(companyId, isOnlyId = false) {
382
- const permissionRepo = await this.getPermissionRepository();
383
- const permissions = await permissionRepo.find({
384
- where: {
385
- permissionType: IamPermissionType.COMPANY_ACTION,
386
- sourceType: IamEntityType.COMPANY,
387
- sourceId: companyId
388
- }
389
- });
390
- if (permissions.length === 0) {
391
- return [];
392
- }
393
- const actionIds = permissions.map((p)=>p.targetId);
394
- if (isOnlyId) return actionIds;
395
- const actionMap = await this.getActionsMap(actionIds);
396
- return permissions.filter((p)=>actionMap.has(p.targetId)).map((p)=>{
397
- const action = actionMap.get(p.targetId);
398
- return {
399
- id: p.id,
400
- companyId: companyId,
401
- actionId: action.id,
402
- actionCode: action.code ?? '',
403
- actionName: action.name,
404
- createdAt: p.createdAt
405
- };
406
- });
407
- }
408
- // User-Role Permissions
409
- /** Assign user to roles (branch-scoped when company feature is enabled) */ async assignUserRoles(dto) {
410
- if (!this.iamConfigService.isRbacEnabled()) {
411
- throw new BadRequestException({
412
- message: 'Role assignment not available in DIRECT-only mode. Use direct user permissions instead.',
413
- messageKey: IAM_MODE_MESSAGES.ROLE_ASSIGNMENT_UNAVAILABLE
414
- });
415
- }
416
- const permissionRepo = await this.getPermissionRepository();
417
- const enableCompanyFeature = this.iamConfigService.isCompanyFeatureEnabled();
418
- const branchId = dto.branchId ?? null;
419
- const companyId = dto.companyId ?? null;
420
- const { toAdd: itemsToAdd, toRemove: itemsToRemove } = this.splitItemsByAction(dto.items);
421
- let added = 0;
422
- let removed = 0;
423
- if (itemsToAdd.length > 0) {
424
- const roleIdsToAdd = itemsToAdd.map((item)=>item.id);
425
- const whereFind = {
426
- permissionType: IamPermissionType.USER_ROLE,
427
- sourceType: IamEntityType.USER,
428
- sourceId: dto.userId,
429
- targetType: IamEntityType.ROLE,
430
- targetId: In(roleIdsToAdd)
431
- };
432
- if (enableCompanyFeature) {
433
- if (companyId) whereFind.companyId = companyId;
434
- if (branchId) whereFind.branchId = branchId;
435
- }
436
- const existingPermissions = await permissionRepo.find({
437
- where: whereFind,
438
- select: [
439
- 'targetId'
440
- ]
441
- });
442
- const existingRoleIds = new Set(existingPermissions.map((p)=>p.targetId));
443
- const newPermissions = itemsToAdd.filter((item)=>!existingRoleIds.has(item.id)).map((item)=>({
444
- permissionType: IamPermissionType.USER_ROLE,
445
- sourceType: IamEntityType.USER,
446
- sourceId: dto.userId,
447
- targetType: IamEntityType.ROLE,
448
- targetId: item.id,
449
- companyId: enableCompanyFeature ? companyId : null,
450
- branchId: enableCompanyFeature ? branchId : null
451
- }));
452
- if (newPermissions.length > 0) {
453
- try {
454
- await permissionRepo.save(newPermissions);
455
- added = newPermissions.length;
456
- } catch (error) {
457
- if (error?.code === 'ER_DUP_ENTRY' || error?.message?.includes('Duplicate entry')) {
458
- throw new ConflictException({
459
- message: 'Some user-role permissions already exist. Please refresh and try again.',
460
- messageKey: PERMISSION_OPERATION_MESSAGES.ALREADY_EXISTS
461
- });
462
- }
463
- throw error;
464
- }
465
- }
466
- }
467
- if (itemsToRemove.length > 0) {
468
- const roleIdsToRemove = itemsToRemove.map((item)=>item.id);
469
- const whereDelete = {
470
- permissionType: IamPermissionType.USER_ROLE,
471
- sourceType: IamEntityType.USER,
472
- sourceId: dto.userId,
473
- targetType: IamEntityType.ROLE,
474
- targetId: In(roleIdsToRemove)
475
- };
476
- if (enableCompanyFeature) {
477
- if (companyId) whereDelete.companyId = companyId;
478
- if (branchId) whereDelete.branchId = branchId;
479
- }
480
- const result = await permissionRepo.delete(whereDelete);
481
- removed = result.affected || 0;
482
- }
483
- await this.permissionCacheService.invalidateUser(dto.userId, enableCompanyFeature ? companyId : null, [
484
- branchId
485
- ]);
486
- const result = this.buildOperationResult(dto.items.length, added, removed);
487
- await this.publishAssignmentEvent(IAM_EVENT_ENTITIES.USER_ROLE, dto.userId, result);
488
- return result;
489
- }
490
- /** Get user's roles (branch-scoped, filtered by companyId and branchId if provided) */ async getUserRoles(userId, branchId, companyId) {
491
- const permissionRepo = await this.getPermissionRepository();
492
- const roleRepo = await this.getRoleRepository();
493
- const enableCompanyFeature = this.iamConfigService.isCompanyFeatureEnabled();
494
- const where = {
495
- permissionType: IamPermissionType.USER_ROLE,
496
- sourceType: IamEntityType.USER,
497
- sourceId: userId
498
- };
499
- if (enableCompanyFeature) {
500
- if (companyId) where.companyId = companyId;
501
- if (branchId) where.branchId = branchId;
502
- }
503
- const permissions = await permissionRepo.find({
504
- where
505
- });
506
- if (permissions.length === 0) {
507
- return [];
508
- }
509
- const roleIds = permissions.map((p)=>p.targetId);
510
- const roleWhere = {
511
- id: In(roleIds)
512
- };
513
- const roles = await roleRepo.find({
514
- where: roleWhere
515
- });
516
- const roleMap = new Map(roles.map((r)=>[
517
- r.id,
518
- r
519
- ]));
520
- return permissions.filter((p)=>roleMap.has(p.targetId)).map((p)=>{
521
- const role = roleMap.get(p.targetId);
522
- const permissionEntity = p;
523
- return {
524
- id: p.id,
525
- userId: p.sourceId,
526
- roleId: role.id,
527
- roleName: role.name,
528
- branchId: enableCompanyFeature ? permissionEntity.branchId ?? null : null,
529
- createdAt: p.createdAt
530
- };
531
- });
532
- }
533
- // Effective (Merged) Permissions — self-scoped, company-wide + branch-specific
534
- /** Get user's effective roles: company-wide (branchId null) merged with branch-specific */ async getUserEffectiveRoles(userId, companyId, branchId) {
535
- const permissionRepo = await this.getPermissionRepository();
536
- const roleRepo = await this.getRoleRepository();
537
- const enableCompanyFeature = this.iamConfigService.isCompanyFeatureEnabled();
538
- const baseWhere = {
539
- permissionType: IamPermissionType.USER_ROLE,
540
- sourceType: IamEntityType.USER,
541
- sourceId: userId
542
- };
543
- if (enableCompanyFeature && companyId) baseWhere.companyId = companyId;
544
- const permissions = enableCompanyFeature && branchId ? await permissionRepo.find({
545
- where: [
546
- {
547
- ...baseWhere,
548
- branchId: IsNull()
549
- },
550
- {
551
- ...baseWhere,
552
- branchId
553
- }
554
- ]
555
- }) : await permissionRepo.find({
556
- where: baseWhere
557
- });
558
- const validPermissions = permissions.filter((p)=>p.isValid());
559
- if (validPermissions.length === 0) {
560
- return [];
561
- }
562
- const roleIds = [
563
- ...new Set(validPermissions.map((p)=>p.targetId))
564
- ];
565
- const roles = await roleRepo.find({
566
- where: {
567
- id: In(roleIds)
568
- }
569
- });
570
- const roleMap = new Map(roles.map((r)=>[
571
- r.id,
572
- r
573
- ]));
574
- return validPermissions.filter((p)=>roleMap.has(p.targetId)).map((p)=>{
575
- const role = roleMap.get(p.targetId);
576
- return {
577
- id: p.id,
578
- userId: p.sourceId,
579
- roleId: role.id,
580
- roleName: role.name,
581
- branchId: enableCompanyFeature ? p.branchId ?? null : null,
582
- createdAt: p.createdAt
583
- };
584
- });
585
- }
586
- /** Get user's effective actions: company-wide (branchId null) merged with branch-specific */ async getUserEffectiveActions(userId, companyId, branchId) {
587
- const permissionRepo = await this.getPermissionRepository();
588
- const enableCompanyFeature = this.iamConfigService.isCompanyFeatureEnabled();
589
- const baseWhere = {
590
- permissionType: IamPermissionType.USER_ACTION,
591
- sourceType: IamEntityType.USER,
592
- sourceId: userId
593
- };
594
- if (enableCompanyFeature && companyId) baseWhere.companyId = companyId;
595
- const permissions = enableCompanyFeature && branchId ? await permissionRepo.find({
596
- where: [
597
- {
598
- ...baseWhere,
599
- branchId: IsNull()
600
- },
601
- {
602
- ...baseWhere,
603
- branchId
604
- }
605
- ]
606
- }) : await permissionRepo.find({
607
- where: baseWhere
608
- });
609
- const validPermissions = permissions.filter((p)=>p.isValid());
610
- if (validPermissions.length === 0) {
611
- return [];
612
- }
613
- const actionIds = [
614
- ...new Set(validPermissions.map((p)=>p.targetId))
615
- ];
616
- const actionMap = await this.getActionsMap(actionIds);
617
- return validPermissions.filter((p)=>actionMap.has(p.targetId)).map((p)=>{
618
- const action = actionMap.get(p.targetId);
619
- return {
620
- id: p.id,
621
- userId: p.sourceId,
622
- actionId: action.id,
623
- actionCode: action.code ?? '',
624
- actionName: action.name,
625
- companyId: ('companyId' in p ? p.companyId : null) ?? null,
626
- branchId: ('branchId' in p ? p.branchId : null) ?? null,
627
- createdAt: p.createdAt
628
- };
629
- });
630
- }
631
- // My Permissions
632
- /** Get user's effective permissions (cache-first approach) */ async getMyPermissions(userId, branchId, companyId, parentCodes) {
633
- return this.permissionCacheService.getMyPermissionsResponse(userId, branchId, companyId, parentCodes);
634
- }
635
- // Helper Methods
636
- /** Split permission items into add and remove arrays */ splitItemsByAction(items) {
637
- return {
638
- toAdd: items.filter((item)=>item.action === PermissionAction.ADD),
639
- toRemove: items.filter((item)=>item.action === PermissionAction.REMOVE)
640
- };
641
- }
642
- async publishAssignmentEvent(entity, targetId, result) {
643
- await publishDomainEvent({
644
- module: IAM_EVENT_MODULE,
645
- entity,
646
- action: IAM_EVENT_ACTIONS.PERMISSIONS_ASSIGNED,
647
- ids: [
648
- targetId
649
- ],
650
- metadata: {
651
- added: result.added,
652
- removed: result.removed,
653
- total: result.total
654
- }
655
- });
656
- }
657
- /** Build standard operation result DTO */ buildOperationResult(_totalItems, added, removed) {
658
- return {
659
- added,
660
- removed,
661
- total: _totalItems
662
- };
663
- }
664
- async revokeCompanyPermissions(companyId) {
665
- if (!this.iamConfigService.isCompanyFeatureEnabled()) {
666
- return;
667
- }
668
- const permissionRepo = await this.getPermissionRepository();
669
- const roleRepo = await this.getRoleRepository();
670
- const dataSource = await this.dataSourceProvider.getDataSource();
671
- const companyRoles = await roleRepo.find({
672
- where: {
673
- companyId,
674
- deletedAt: IsNull()
675
- },
676
- select: [
677
- 'id'
678
- ]
679
- });
680
- const roleIds = companyRoles.map((role)=>role.id);
681
- const affectedUserRows = await permissionRepo.find({
682
- where: {
683
- permissionType: In([
684
- IamPermissionType.USER_ROLE,
685
- IamPermissionType.USER_ACTION
686
- ]),
687
- sourceType: IamEntityType.USER,
688
- companyId
689
- },
690
- select: [
691
- 'sourceId'
692
- ]
693
- });
694
- const affectedUserIds = [
695
- ...new Set(affectedUserRows.map((row)=>row.sourceId))
696
- ];
697
- await dataSource.transaction(async (manager)=>{
698
- const txRepo = manager.getRepository(permissionRepo.target);
699
- await txRepo.delete({
700
- permissionType: IamPermissionType.COMPANY_ACTION,
701
- sourceType: IamEntityType.COMPANY,
702
- sourceId: companyId
703
- });
704
- if (roleIds.length > 0) {
705
- await txRepo.delete({
706
- permissionType: IamPermissionType.ROLE_ACTION,
707
- sourceType: IamEntityType.ROLE,
708
- sourceId: In(roleIds)
709
- });
710
- }
711
- await txRepo.delete({
712
- permissionType: In([
713
- IamPermissionType.USER_ROLE,
714
- IamPermissionType.USER_ACTION
715
- ]),
716
- companyId
717
- });
718
- if (roleIds.length > 0) {
719
- const txRoleRepo = manager.getRepository(roleRepo.target);
720
- await txRoleRepo.delete(roleIds);
721
- }
722
- });
723
- if (affectedUserIds.length > 0) {
724
- await this.permissionCacheService.invalidateUsers(affectedUserIds, companyId);
725
- }
726
- }
727
- async revokeBranchPermissions(branchId, companyId) {
728
- if (!this.iamConfigService.isCompanyFeatureEnabled()) {
729
- return;
730
- }
731
- const permissionRepo = await this.getPermissionRepository();
732
- const where = {
733
- permissionType: In([
734
- IamPermissionType.USER_ROLE,
735
- IamPermissionType.USER_ACTION
736
- ]),
737
- companyId,
738
- branchId
739
- };
740
- const affectedUserRows = await permissionRepo.find({
741
- where: {
742
- ...where,
743
- sourceType: IamEntityType.USER
744
- },
745
- select: [
746
- 'sourceId'
747
- ]
748
- });
749
- const affectedUserIds = [
750
- ...new Set(affectedUserRows.map((row)=>row.sourceId))
751
- ];
752
- await permissionRepo.delete(where);
753
- if (affectedUserIds.length > 0) {
754
- await this.permissionCacheService.invalidateUsers(affectedUserIds, companyId, [
755
- branchId
756
- ]);
757
- }
758
- }
759
- async revokeUserCompanyAccess(userId, companyId) {
760
- const permissionRepo = await this.getPermissionRepository();
761
- await permissionRepo.delete({
762
- permissionType: In([
763
- IamPermissionType.USER_ROLE,
764
- IamPermissionType.USER_ACTION
765
- ]),
766
- sourceType: IamEntityType.USER,
767
- sourceId: userId,
768
- companyId
769
- });
770
- await this.permissionCacheService.invalidateUser(userId, companyId);
771
- }
772
- async revokeUserBranchAccess(userId, branchId, companyId) {
773
- const permissionRepo = await this.getPermissionRepository();
774
- const where = {
775
- permissionType: In([
776
- IamPermissionType.USER_ROLE,
777
- IamPermissionType.USER_ACTION
778
- ]),
779
- sourceType: IamEntityType.USER,
780
- sourceId: userId,
781
- branchId,
782
- companyId
783
- };
784
- await permissionRepo.delete(where);
785
- await this.permissionCacheService.invalidateUser(userId, companyId, [
786
- branchId
787
- ]);
788
- }
789
- // NOTE: @Inject() required for bundled code - type metadata may be lost during esbuild
790
- constructor(permissionCacheService, iamConfigService, dataSourceProvider){
791
- _define_property(this, "permissionCacheService", void 0);
792
- _define_property(this, "iamConfigService", void 0);
793
- _define_property(this, "dataSourceProvider", void 0);
794
- this.permissionCacheService = permissionCacheService;
795
- this.iamConfigService = iamConfigService;
796
- this.dataSourceProvider = dataSourceProvider;
797
- }
798
- }
799
- _ts_decorate([
800
- LogAction({
801
- action: 'iam.assignUserActions',
802
- module: 'iam'
803
- }),
804
- _ts_metadata("design:type", Function),
805
- _ts_metadata("design:paramtypes", [
806
- typeof AssignUserActionsDto === "undefined" ? Object : AssignUserActionsDto
807
- ]),
808
- _ts_metadata("design:returntype", Promise)
809
- ], PermissionService.prototype, "assignUserActions", null);
810
- _ts_decorate([
811
- LogAction({
812
- action: 'iam.assignRoleActions',
813
- module: 'iam'
814
- }),
815
- _ts_metadata("design:type", Function),
816
- _ts_metadata("design:paramtypes", [
817
- typeof AssignRoleActionsDto === "undefined" ? Object : AssignRoleActionsDto
818
- ]),
819
- _ts_metadata("design:returntype", Promise)
820
- ], PermissionService.prototype, "assignRoleActions", null);
821
- _ts_decorate([
822
- LogAction({
823
- action: 'iam.assignCompanyActions',
824
- module: 'iam'
825
- }),
826
- _ts_metadata("design:type", Function),
827
- _ts_metadata("design:paramtypes", [
828
- typeof AssignCompanyActionsDto === "undefined" ? Object : AssignCompanyActionsDto
829
- ]),
830
- _ts_metadata("design:returntype", Promise)
831
- ], PermissionService.prototype, "assignCompanyActions", null);
832
- _ts_decorate([
833
- LogAction({
834
- action: 'iam.assignUserRoles',
835
- module: 'iam'
836
- }),
837
- _ts_metadata("design:type", Function),
838
- _ts_metadata("design:paramtypes", [
839
- typeof AssignUserRolesDto === "undefined" ? Object : AssignUserRolesDto
840
- ]),
841
- _ts_metadata("design:returntype", Promise)
842
- ], PermissionService.prototype, "assignUserRoles", null);
843
- _ts_decorate([
844
- LogAction({
845
- action: 'iam.revokeCompanyPermissions',
846
- module: 'iam'
847
- }),
848
- _ts_metadata("design:type", Function),
849
- _ts_metadata("design:paramtypes", [
850
- String
851
- ]),
852
- _ts_metadata("design:returntype", Promise)
853
- ], PermissionService.prototype, "revokeCompanyPermissions", null);
854
- _ts_decorate([
855
- LogAction({
856
- action: 'iam.revokeBranchPermissions',
857
- module: 'iam'
858
- }),
859
- _ts_metadata("design:type", Function),
860
- _ts_metadata("design:paramtypes", [
861
- String,
862
- String
863
- ]),
864
- _ts_metadata("design:returntype", Promise)
865
- ], PermissionService.prototype, "revokeBranchPermissions", null);
866
- _ts_decorate([
867
- LogAction({
868
- action: 'iam.revokeUserCompanyAccess',
869
- module: 'iam'
870
- }),
871
- _ts_metadata("design:type", Function),
872
- _ts_metadata("design:paramtypes", [
873
- String,
874
- String
875
- ]),
876
- _ts_metadata("design:returntype", Promise)
877
- ], PermissionService.prototype, "revokeUserCompanyAccess", null);
878
- _ts_decorate([
879
- LogAction({
880
- action: 'iam.revokeUserBranchAccess',
881
- module: 'iam'
882
- }),
883
- _ts_metadata("design:type", Function),
884
- _ts_metadata("design:paramtypes", [
885
- String,
886
- String,
887
- String
888
- ]),
889
- _ts_metadata("design:returntype", Promise)
890
- ], PermissionService.prototype, "revokeUserBranchAccess", null);
891
- PermissionService = _ts_decorate([
892
- Injectable({
893
- scope: Scope.REQUEST
894
- }),
895
- _ts_param(0, Inject(PermissionCacheService)),
896
- _ts_param(1, Inject(IAMConfigService)),
897
- _ts_param(2, Inject(IAMDataSourceService)),
898
- _ts_metadata("design:type", Function),
899
- _ts_metadata("design:paramtypes", [
900
- typeof PermissionCacheService === "undefined" ? Object : PermissionCacheService,
901
- typeof IAMConfigService === "undefined" ? Object : IAMConfigService,
902
- typeof IAMDataSourceService === "undefined" ? Object : IAMDataSourceService
903
- ])
904
- ], PermissionService);