@flusys/nestjs-iam 6.0.0 → 6.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/controllers/action.controller.js +17 -9
- package/cjs/controllers/user-action-permission.controller.js +0 -3
- package/cjs/docs/iam-swagger.config.js +6 -0
- package/cjs/dtos/action.dto.js +17 -0
- package/cjs/modules/iam.module.js +3 -1
- package/cjs/services/action.service.js +90 -7
- package/cjs/services/permission-cache.service.js +113 -110
- package/cjs/services/permission.service.js +213 -92
- package/cjs/services/role.service.js +47 -3
- package/controllers/action.controller.d.ts +5 -3
- package/dtos/action.dto.d.ts +3 -0
- package/entities/index.d.ts +2 -2
- package/fesm/controllers/action.controller.js +18 -10
- package/fesm/controllers/user-action-permission.controller.js +0 -3
- package/fesm/docs/iam-swagger.config.js +6 -0
- package/fesm/dtos/action.dto.js +14 -0
- package/fesm/modules/iam.module.js +4 -2
- package/fesm/services/action.service.js +90 -7
- package/fesm/services/permission-cache.service.js +101 -108
- package/fesm/services/permission.service.js +213 -92
- package/fesm/services/role.service.js +48 -4
- package/package.json +3 -3
- package/services/action.service.d.ts +7 -3
- package/services/permission-cache.service.d.ts +13 -19
- package/services/permission.service.d.ts +6 -3
- package/services/role.service.d.ts +7 -3
|
@@ -35,9 +35,9 @@ import { UserIamPermissionWithCompany } from '../entities/permission-with-compan
|
|
|
35
35
|
import { RoleWithCompany } from '../entities/role-with-company.entity';
|
|
36
36
|
import { Role } from '../entities/role.entity';
|
|
37
37
|
import { UserIamPermission } from '../entities/user-iam-permission.entity';
|
|
38
|
+
import { ActionType } from '../enums/action-type.enum';
|
|
38
39
|
import { IamEntityType } from '../enums/iam-entity-type.enum';
|
|
39
40
|
import { IamPermissionType } from '../enums/iam-permission-type.enum';
|
|
40
|
-
import { ActionType } from '../enums/action-type.enum';
|
|
41
41
|
import { IAMPermissionMode } from '../enums/permission-type.enum';
|
|
42
42
|
import { IAMConfigService } from './iam-config.service';
|
|
43
43
|
import { IAMDataSourceService } from './iam-datasource.service';
|
|
@@ -57,6 +57,18 @@ export class PermissionService {
|
|
|
57
57
|
const entity = enableCompanyFeature ? RoleWithCompany : Role;
|
|
58
58
|
return this.dataSourceProvider.getRepository(entity);
|
|
59
59
|
}
|
|
60
|
+
async getActionsMap(actionIds) {
|
|
61
|
+
const actionRepo = await this.getActionRepository();
|
|
62
|
+
const actions = await actionRepo.find({
|
|
63
|
+
where: {
|
|
64
|
+
id: In(actionIds)
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
return new Map(actions.map((a)=>[
|
|
68
|
+
a.id,
|
|
69
|
+
a
|
|
70
|
+
]));
|
|
71
|
+
}
|
|
60
72
|
// User-Action Permissions
|
|
61
73
|
async assignUserActions(dto) {
|
|
62
74
|
if (!this.iamConfigService.isDirectPermissionEnabled()) {
|
|
@@ -133,12 +145,13 @@ export class PermissionService {
|
|
|
133
145
|
const result = await permissionRepo.delete(whereDelete);
|
|
134
146
|
removed = result.affected || 0;
|
|
135
147
|
}
|
|
136
|
-
await this.
|
|
148
|
+
await this.permissionCacheService.invalidateUser(dto.userId, enableCompanyFeature ? companyId : null, [
|
|
149
|
+
branchId
|
|
150
|
+
]);
|
|
137
151
|
return this.buildOperationResult(dto.items.length, added, removed);
|
|
138
152
|
}
|
|
139
153
|
async getUserActions(userId, branchId, companyId) {
|
|
140
154
|
const permissionRepo = await this.getPermissionRepository();
|
|
141
|
-
const actionRepo = await this.getActionRepository();
|
|
142
155
|
const enableCompanyFeature = this.iamConfigService.isCompanyFeatureEnabled();
|
|
143
156
|
const where = {
|
|
144
157
|
permissionType: IamPermissionType.USER_ACTION,
|
|
@@ -156,16 +169,7 @@ export class PermissionService {
|
|
|
156
169
|
return [];
|
|
157
170
|
}
|
|
158
171
|
const actionIds = permissions.map((p)=>p.targetId);
|
|
159
|
-
const
|
|
160
|
-
id: In(actionIds)
|
|
161
|
-
};
|
|
162
|
-
const actions = await actionRepo.find({
|
|
163
|
-
where: actionWhere
|
|
164
|
-
});
|
|
165
|
-
const actionMap = new Map(actions.map((a)=>[
|
|
166
|
-
a.id,
|
|
167
|
-
a
|
|
168
|
-
]));
|
|
172
|
+
const actionMap = await this.getActionsMap(actionIds);
|
|
169
173
|
return permissions.filter((p)=>actionMap.has(p.targetId)).map((p)=>{
|
|
170
174
|
const action = actionMap.get(p.targetId);
|
|
171
175
|
return {
|
|
@@ -262,7 +266,6 @@ export class PermissionService {
|
|
|
262
266
|
}
|
|
263
267
|
async getRoleActions(roleId) {
|
|
264
268
|
const permissionRepo = await this.getPermissionRepository();
|
|
265
|
-
const actionRepo = await this.getActionRepository();
|
|
266
269
|
const permissions = await permissionRepo.find({
|
|
267
270
|
where: {
|
|
268
271
|
permissionType: IamPermissionType.ROLE_ACTION,
|
|
@@ -274,16 +277,7 @@ export class PermissionService {
|
|
|
274
277
|
return [];
|
|
275
278
|
}
|
|
276
279
|
const actionIds = permissions.map((p)=>p.targetId);
|
|
277
|
-
const
|
|
278
|
-
id: In(actionIds)
|
|
279
|
-
};
|
|
280
|
-
const actions = await actionRepo.find({
|
|
281
|
-
where: actionWhere
|
|
282
|
-
});
|
|
283
|
-
const actionMap = new Map(actions.map((a)=>[
|
|
284
|
-
a.id,
|
|
285
|
-
a
|
|
286
|
-
]));
|
|
280
|
+
const actionMap = await this.getActionsMap(actionIds);
|
|
287
281
|
return permissions.filter((p)=>actionMap.has(p.targetId)).map((p)=>{
|
|
288
282
|
const action = actionMap.get(p.targetId);
|
|
289
283
|
return {
|
|
@@ -310,8 +304,7 @@ export class PermissionService {
|
|
|
310
304
|
}
|
|
311
305
|
if (itemsToRemove.length > 0) {
|
|
312
306
|
const actionIdsToRemove = itemsToRemove.map((item)=>item.id);
|
|
313
|
-
|
|
314
|
-
removed = cascadeResult.removedCompanyActions;
|
|
307
|
+
removed = await this.removeCompanyActionsWithCascade(manager, dto.companyId, actionIdsToRemove);
|
|
315
308
|
}
|
|
316
309
|
});
|
|
317
310
|
await this.invalidateCompanyMembersCache(dto.companyId);
|
|
@@ -367,37 +360,28 @@ export class PermissionService {
|
|
|
367
360
|
'id'
|
|
368
361
|
]
|
|
369
362
|
});
|
|
370
|
-
let removedRoleActions = 0;
|
|
371
|
-
let removedUserActions = 0;
|
|
372
363
|
if (companyRoles.length > 0) {
|
|
373
364
|
const roleIds = companyRoles.map((role)=>role.id);
|
|
374
|
-
|
|
365
|
+
await permissionRepo.delete({
|
|
375
366
|
permissionType: IamPermissionType.ROLE_ACTION,
|
|
376
367
|
sourceType: IamEntityType.ROLE,
|
|
377
368
|
sourceId: In(roleIds),
|
|
378
369
|
targetType: IamEntityType.ACTION,
|
|
379
370
|
targetId: In(actionIds)
|
|
380
371
|
});
|
|
381
|
-
removedRoleActions = roleResult.affected || 0;
|
|
382
372
|
}
|
|
383
373
|
if (this.iamConfigService.isCompanyFeatureEnabled()) {
|
|
384
|
-
|
|
374
|
+
await permissionRepo.delete({
|
|
385
375
|
permissionType: IamPermissionType.USER_ACTION,
|
|
386
376
|
companyId,
|
|
387
377
|
targetType: IamEntityType.ACTION,
|
|
388
378
|
targetId: In(actionIds)
|
|
389
379
|
});
|
|
390
|
-
removedUserActions = userResult.affected || 0;
|
|
391
380
|
}
|
|
392
|
-
return
|
|
393
|
-
removedCompanyActions: companyResult.affected || 0,
|
|
394
|
-
removedRoleActions,
|
|
395
|
-
removedUserActions
|
|
396
|
-
};
|
|
381
|
+
return companyResult.affected || 0;
|
|
397
382
|
}
|
|
398
383
|
/** Get all actions assigned to a company (whitelist) */ async getCompanyActions(companyId) {
|
|
399
384
|
const permissionRepo = await this.getPermissionRepository();
|
|
400
|
-
const actionRepo = await this.getActionRepository();
|
|
401
385
|
const permissions = await permissionRepo.find({
|
|
402
386
|
where: {
|
|
403
387
|
permissionType: IamPermissionType.COMPANY_ACTION,
|
|
@@ -409,15 +393,7 @@ export class PermissionService {
|
|
|
409
393
|
return [];
|
|
410
394
|
}
|
|
411
395
|
const actionIds = permissions.map((p)=>p.targetId);
|
|
412
|
-
const
|
|
413
|
-
where: {
|
|
414
|
-
id: In(actionIds)
|
|
415
|
-
}
|
|
416
|
-
});
|
|
417
|
-
const actionMap = new Map(actions.map((a)=>[
|
|
418
|
-
a.id,
|
|
419
|
-
a
|
|
420
|
-
]));
|
|
396
|
+
const actionMap = await this.getActionsMap(actionIds);
|
|
421
397
|
return permissions.filter((p)=>actionMap.has(p.targetId)).map((p)=>{
|
|
422
398
|
const action = actionMap.get(p.targetId);
|
|
423
399
|
return {
|
|
@@ -520,7 +496,9 @@ export class PermissionService {
|
|
|
520
496
|
const result = await permissionRepo.delete(whereDelete);
|
|
521
497
|
removed = result.affected || 0;
|
|
522
498
|
}
|
|
523
|
-
await this.
|
|
499
|
+
await this.permissionCacheService.invalidateUser(dto.userId, enableCompanyFeature ? companyId : null, [
|
|
500
|
+
branchId
|
|
501
|
+
]);
|
|
524
502
|
return this.buildOperationResult(dto.items.length, added, removed);
|
|
525
503
|
}
|
|
526
504
|
/** Get user's roles (branch-scoped, filtered by companyId and branchId if provided) */ async getUserRoles(userId, branchId, companyId) {
|
|
@@ -571,9 +549,8 @@ export class PermissionService {
|
|
|
571
549
|
const enableCompanyFeature = this.iamConfigService.isCompanyFeatureEnabled();
|
|
572
550
|
const cacheOptions = {
|
|
573
551
|
userId,
|
|
574
|
-
companyId,
|
|
575
|
-
branchId
|
|
576
|
-
enableCompanyFeature
|
|
552
|
+
companyId: enableCompanyFeature ? companyId : null,
|
|
553
|
+
branchId
|
|
577
554
|
};
|
|
578
555
|
const cachedData = await this.permissionCacheService.getMyPermissions(cacheOptions);
|
|
579
556
|
if (cachedData) {
|
|
@@ -602,42 +579,25 @@ export class PermissionService {
|
|
|
602
579
|
cachedEndpoints: cachedData.backendCodes.length
|
|
603
580
|
};
|
|
604
581
|
}
|
|
605
|
-
/**
|
|
606
|
-
if (!this.iamConfigService.isMultiTenant()) {
|
|
607
|
-
return undefined;
|
|
608
|
-
}
|
|
609
|
-
const tenant = this.dataSourceProvider.getCurrentTenant();
|
|
610
|
-
return tenant?.id;
|
|
611
|
-
}
|
|
612
|
-
/** Get parent IDs by codes, using cache first (tenant-aware) */ async getParentIdsByCodesWithCache(codes) {
|
|
613
|
-
const tenantId = this.getCurrentTenantId();
|
|
614
|
-
const cachedMap = await this.permissionCacheService.getActionIdsByCodes(codes, tenantId);
|
|
615
|
-
if (cachedMap) {
|
|
616
|
-
return new Set(Object.values(cachedMap));
|
|
617
|
-
}
|
|
582
|
+
/** Resolve action IDs for the given codes */ async getParentIdsByCodesWithCache(codes) {
|
|
618
583
|
const actionRepo = await this.getActionRepository();
|
|
619
|
-
const
|
|
584
|
+
const actions = await actionRepo.find({
|
|
585
|
+
where: {
|
|
586
|
+
code: In(codes)
|
|
587
|
+
},
|
|
620
588
|
select: [
|
|
621
589
|
'id',
|
|
622
590
|
'code'
|
|
623
591
|
]
|
|
624
592
|
});
|
|
625
|
-
|
|
626
|
-
for (const action of allActions){
|
|
627
|
-
if (action.code) {
|
|
628
|
-
fullMap[action.code] = action.id;
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
await this.permissionCacheService.setActionCodeMap(fullMap, tenantId);
|
|
632
|
-
return new Set(codes.map((code)=>fullMap[code]).filter(Boolean));
|
|
593
|
+
return new Set(actions.map((action)=>action.id));
|
|
633
594
|
}
|
|
634
595
|
/** Fetch permissions from DB and cache them (empty permissions are also cached) */ async fetchAndCachePermissions(userId, branchId, companyId) {
|
|
635
596
|
const enableCompanyFeature = this.iamConfigService.isCompanyFeatureEnabled();
|
|
636
597
|
const cacheOptions = {
|
|
637
598
|
userId,
|
|
638
|
-
companyId,
|
|
639
|
-
branchId
|
|
640
|
-
enableCompanyFeature
|
|
599
|
+
companyId: enableCompanyFeature ? companyId : null,
|
|
600
|
+
branchId
|
|
641
601
|
};
|
|
642
602
|
const emptyData = {
|
|
643
603
|
frontendActions: [],
|
|
@@ -690,7 +650,8 @@ export class PermissionService {
|
|
|
690
650
|
const actionRepo = await this.getActionRepository();
|
|
691
651
|
const actions = await actionRepo.find({
|
|
692
652
|
where: {
|
|
693
|
-
id: In(Array.from(actionIds))
|
|
653
|
+
id: In(Array.from(actionIds)),
|
|
654
|
+
isActive: true
|
|
694
655
|
}
|
|
695
656
|
});
|
|
696
657
|
const backendActions = actions.filter((a)=>a.actionType === ActionType.BACKEND || a.actionType === ActionType.BOTH);
|
|
@@ -706,10 +667,7 @@ export class PermissionService {
|
|
|
706
667
|
})),
|
|
707
668
|
backendCodes
|
|
708
669
|
};
|
|
709
|
-
await
|
|
710
|
-
this.permissionCacheService.setMyPermissions(cacheOptions, cacheData),
|
|
711
|
-
this.permissionCacheService.setPermissions(cacheOptions, backendCodes)
|
|
712
|
-
]);
|
|
670
|
+
await this.permissionCacheService.setMyPermissions(cacheOptions, cacheData);
|
|
713
671
|
return cacheData;
|
|
714
672
|
}
|
|
715
673
|
// Helper Methods
|
|
@@ -743,9 +701,11 @@ export class PermissionService {
|
|
|
743
701
|
const baseWhere = {
|
|
744
702
|
permissionType: IamPermissionType.USER_ROLE,
|
|
745
703
|
sourceType: IamEntityType.USER,
|
|
746
|
-
sourceId: userId
|
|
747
|
-
companyId: companyId
|
|
704
|
+
sourceId: userId
|
|
748
705
|
};
|
|
706
|
+
if (companyId) {
|
|
707
|
+
baseWhere.companyId = companyId;
|
|
708
|
+
}
|
|
749
709
|
if (branchId) {
|
|
750
710
|
// Get company-wide + branch-specific roles
|
|
751
711
|
const companyWidePermissions = await permissionRepo.find({
|
|
@@ -829,14 +789,6 @@ export class PermissionService {
|
|
|
829
789
|
}
|
|
830
790
|
return Array.from(actionIds);
|
|
831
791
|
}
|
|
832
|
-
/** Invalidate permission cache for a user */ async invalidateUserPermissionCache(userId, branchId, companyId) {
|
|
833
|
-
const branchIds = branchId !== undefined ? [
|
|
834
|
-
branchId
|
|
835
|
-
] : [
|
|
836
|
-
null
|
|
837
|
-
];
|
|
838
|
-
await this.permissionCacheService.invalidateUser(userId, companyId, branchIds);
|
|
839
|
-
}
|
|
840
792
|
async invalidateRoleMembersCache(roleId) {
|
|
841
793
|
const permissionRepo = await this.getPermissionRepository();
|
|
842
794
|
const roleRepo = await this.getRoleRepository();
|
|
@@ -874,7 +826,7 @@ export class PermissionService {
|
|
|
874
826
|
...new Set(userBranches.map((p)=>p.branchId))
|
|
875
827
|
];
|
|
876
828
|
}
|
|
877
|
-
return this.permissionCacheService.
|
|
829
|
+
return this.permissionCacheService.invalidateUsers(userIds, companyId, branchIds);
|
|
878
830
|
}
|
|
879
831
|
/** Invalidate permission cache for all users in a company */ async invalidateCompanyMembersCache(companyId) {
|
|
880
832
|
if (!this.iamConfigService.isCompanyFeatureEnabled()) {
|
|
@@ -895,6 +847,127 @@ export class PermissionService {
|
|
|
895
847
|
}
|
|
896
848
|
return await this.permissionCacheService.invalidateUsers(userIds, companyId, branchIds);
|
|
897
849
|
}
|
|
850
|
+
async revokeCompanyPermissions(companyId) {
|
|
851
|
+
if (!this.iamConfigService.isCompanyFeatureEnabled()) {
|
|
852
|
+
return;
|
|
853
|
+
}
|
|
854
|
+
const permissionRepo = await this.getPermissionRepository();
|
|
855
|
+
const roleRepo = await this.getRoleRepository();
|
|
856
|
+
const dataSource = permissionRepo.manager.connection;
|
|
857
|
+
const companyRoles = await roleRepo.find({
|
|
858
|
+
where: {
|
|
859
|
+
companyId,
|
|
860
|
+
deletedAt: IsNull()
|
|
861
|
+
},
|
|
862
|
+
select: [
|
|
863
|
+
'id'
|
|
864
|
+
]
|
|
865
|
+
});
|
|
866
|
+
const roleIds = companyRoles.map((role)=>role.id);
|
|
867
|
+
const affectedUserRows = await permissionRepo.find({
|
|
868
|
+
where: {
|
|
869
|
+
permissionType: In([
|
|
870
|
+
IamPermissionType.USER_ROLE,
|
|
871
|
+
IamPermissionType.USER_ACTION
|
|
872
|
+
]),
|
|
873
|
+
companyId
|
|
874
|
+
},
|
|
875
|
+
select: [
|
|
876
|
+
'userId'
|
|
877
|
+
]
|
|
878
|
+
});
|
|
879
|
+
const affectedUserIds = [
|
|
880
|
+
...new Set(affectedUserRows.map((row)=>row.userId).filter((id)=>!!id))
|
|
881
|
+
];
|
|
882
|
+
await dataSource.transaction(async (manager)=>{
|
|
883
|
+
const txRepo = manager.getRepository(permissionRepo.target);
|
|
884
|
+
await txRepo.delete({
|
|
885
|
+
permissionType: IamPermissionType.COMPANY_ACTION,
|
|
886
|
+
sourceType: IamEntityType.COMPANY,
|
|
887
|
+
sourceId: companyId
|
|
888
|
+
});
|
|
889
|
+
if (roleIds.length > 0) {
|
|
890
|
+
await txRepo.delete({
|
|
891
|
+
permissionType: IamPermissionType.ROLE_ACTION,
|
|
892
|
+
sourceType: IamEntityType.ROLE,
|
|
893
|
+
sourceId: In(roleIds)
|
|
894
|
+
});
|
|
895
|
+
}
|
|
896
|
+
await txRepo.delete({
|
|
897
|
+
permissionType: In([
|
|
898
|
+
IamPermissionType.USER_ROLE,
|
|
899
|
+
IamPermissionType.USER_ACTION
|
|
900
|
+
]),
|
|
901
|
+
companyId
|
|
902
|
+
});
|
|
903
|
+
if (roleIds.length > 0) {
|
|
904
|
+
const txRoleRepo = manager.getRepository(roleRepo.target);
|
|
905
|
+
await txRoleRepo.delete(roleIds);
|
|
906
|
+
}
|
|
907
|
+
});
|
|
908
|
+
if (affectedUserIds.length > 0) {
|
|
909
|
+
await this.permissionCacheService.invalidateUsers(affectedUserIds, companyId);
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
async revokeBranchPermissions(branchId, companyId) {
|
|
913
|
+
if (!this.iamConfigService.isCompanyFeatureEnabled()) {
|
|
914
|
+
return;
|
|
915
|
+
}
|
|
916
|
+
const permissionRepo = await this.getPermissionRepository();
|
|
917
|
+
const where = {
|
|
918
|
+
permissionType: In([
|
|
919
|
+
IamPermissionType.USER_ROLE,
|
|
920
|
+
IamPermissionType.USER_ACTION
|
|
921
|
+
]),
|
|
922
|
+
branchId,
|
|
923
|
+
companyId
|
|
924
|
+
};
|
|
925
|
+
const affectedUserRows = await permissionRepo.find({
|
|
926
|
+
where,
|
|
927
|
+
select: [
|
|
928
|
+
'userId'
|
|
929
|
+
]
|
|
930
|
+
});
|
|
931
|
+
const affectedUserIds = [
|
|
932
|
+
...new Set(affectedUserRows.map((row)=>row.userId).filter((id)=>!!id))
|
|
933
|
+
];
|
|
934
|
+
await permissionRepo.delete(where);
|
|
935
|
+
if (affectedUserIds.length > 0) {
|
|
936
|
+
await this.permissionCacheService.invalidateUsers(affectedUserIds, companyId, [
|
|
937
|
+
branchId
|
|
938
|
+
]);
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
async revokeUserCompanyAccess(userId, companyId) {
|
|
942
|
+
const permissionRepo = await this.getPermissionRepository();
|
|
943
|
+
await permissionRepo.delete({
|
|
944
|
+
permissionType: In([
|
|
945
|
+
IamPermissionType.USER_ROLE,
|
|
946
|
+
IamPermissionType.USER_ACTION
|
|
947
|
+
]),
|
|
948
|
+
sourceType: IamEntityType.USER,
|
|
949
|
+
sourceId: userId,
|
|
950
|
+
companyId
|
|
951
|
+
});
|
|
952
|
+
await this.permissionCacheService.invalidateUser(userId, companyId);
|
|
953
|
+
}
|
|
954
|
+
async revokeUserBranchAccess(userId, branchId, companyId) {
|
|
955
|
+
const permissionRepo = await this.getPermissionRepository();
|
|
956
|
+
const where = {
|
|
957
|
+
permissionType: In([
|
|
958
|
+
IamPermissionType.USER_ROLE,
|
|
959
|
+
IamPermissionType.USER_ACTION
|
|
960
|
+
]),
|
|
961
|
+
sourceType: IamEntityType.USER,
|
|
962
|
+
sourceId: userId,
|
|
963
|
+
branchId,
|
|
964
|
+
companyId
|
|
965
|
+
};
|
|
966
|
+
await permissionRepo.delete(where);
|
|
967
|
+
await this.permissionCacheService.invalidateUser(userId, companyId, [
|
|
968
|
+
branchId
|
|
969
|
+
]);
|
|
970
|
+
}
|
|
898
971
|
// NOTE: @Inject() required for bundled code - type metadata may be lost during esbuild
|
|
899
972
|
constructor(permissionCacheService, iamConfigService, dataSourceProvider){
|
|
900
973
|
_define_property(this, "permissionCacheService", void 0);
|
|
@@ -949,6 +1022,54 @@ _ts_decorate([
|
|
|
949
1022
|
]),
|
|
950
1023
|
_ts_metadata("design:returntype", Promise)
|
|
951
1024
|
], PermissionService.prototype, "assignUserRoles", null);
|
|
1025
|
+
_ts_decorate([
|
|
1026
|
+
LogAction({
|
|
1027
|
+
action: 'iam.revokeCompanyPermissions',
|
|
1028
|
+
module: 'iam'
|
|
1029
|
+
}),
|
|
1030
|
+
_ts_metadata("design:type", Function),
|
|
1031
|
+
_ts_metadata("design:paramtypes", [
|
|
1032
|
+
String
|
|
1033
|
+
]),
|
|
1034
|
+
_ts_metadata("design:returntype", Promise)
|
|
1035
|
+
], PermissionService.prototype, "revokeCompanyPermissions", null);
|
|
1036
|
+
_ts_decorate([
|
|
1037
|
+
LogAction({
|
|
1038
|
+
action: 'iam.revokeBranchPermissions',
|
|
1039
|
+
module: 'iam'
|
|
1040
|
+
}),
|
|
1041
|
+
_ts_metadata("design:type", Function),
|
|
1042
|
+
_ts_metadata("design:paramtypes", [
|
|
1043
|
+
String,
|
|
1044
|
+
String
|
|
1045
|
+
]),
|
|
1046
|
+
_ts_metadata("design:returntype", Promise)
|
|
1047
|
+
], PermissionService.prototype, "revokeBranchPermissions", null);
|
|
1048
|
+
_ts_decorate([
|
|
1049
|
+
LogAction({
|
|
1050
|
+
action: 'iam.revokeUserCompanyAccess',
|
|
1051
|
+
module: 'iam'
|
|
1052
|
+
}),
|
|
1053
|
+
_ts_metadata("design:type", Function),
|
|
1054
|
+
_ts_metadata("design:paramtypes", [
|
|
1055
|
+
String,
|
|
1056
|
+
String
|
|
1057
|
+
]),
|
|
1058
|
+
_ts_metadata("design:returntype", Promise)
|
|
1059
|
+
], PermissionService.prototype, "revokeUserCompanyAccess", null);
|
|
1060
|
+
_ts_decorate([
|
|
1061
|
+
LogAction({
|
|
1062
|
+
action: 'iam.revokeUserBranchAccess',
|
|
1063
|
+
module: 'iam'
|
|
1064
|
+
}),
|
|
1065
|
+
_ts_metadata("design:type", Function),
|
|
1066
|
+
_ts_metadata("design:paramtypes", [
|
|
1067
|
+
String,
|
|
1068
|
+
String,
|
|
1069
|
+
String
|
|
1070
|
+
]),
|
|
1071
|
+
_ts_metadata("design:returntype", Promise)
|
|
1072
|
+
], PermissionService.prototype, "revokeUserBranchAccess", null);
|
|
952
1073
|
PermissionService = _ts_decorate([
|
|
953
1074
|
Injectable({
|
|
954
1075
|
scope: Scope.REQUEST
|
|
@@ -25,15 +25,57 @@ function _ts_param(paramIndex, decorator) {
|
|
|
25
25
|
decorator(target, key, paramIndex);
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
|
-
import {
|
|
28
|
+
import { ApiService, HybridCache } from '@flusys/nestjs-shared/classes';
|
|
29
29
|
import { UtilsService } from '@flusys/nestjs-shared/modules';
|
|
30
30
|
import { applyCompanyFilter } from '@flusys/nestjs-shared/utils';
|
|
31
31
|
import { Inject, Injectable, Scope } from '@nestjs/common';
|
|
32
|
+
import { In } from 'typeorm';
|
|
33
|
+
import { UserIamPermissionWithCompany } from '../entities/permission-with-company.entity';
|
|
32
34
|
import { RoleWithCompany } from '../entities/role-with-company.entity';
|
|
33
35
|
import { Role } from '../entities/role.entity';
|
|
36
|
+
import { UserIamPermission } from '../entities/user-iam-permission.entity';
|
|
37
|
+
import { IamEntityType } from '../enums/iam-entity-type.enum';
|
|
38
|
+
import { IamPermissionType } from '../enums/iam-permission-type.enum';
|
|
34
39
|
import { IAMConfigService } from './iam-config.service';
|
|
35
40
|
import { IAMDataSourceService } from './iam-datasource.service';
|
|
41
|
+
import { PermissionCacheService } from './permission-cache.service';
|
|
36
42
|
export class RoleService extends ApiService {
|
|
43
|
+
async beforeDeleteOperation(dto, user, queryRunner) {
|
|
44
|
+
await super.beforeDeleteOperation(dto, user, queryRunner);
|
|
45
|
+
const roleIds = Array.isArray(dto.id) ? dto.id : [
|
|
46
|
+
dto.id
|
|
47
|
+
];
|
|
48
|
+
if (roleIds.length === 0) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const enableCompanyFeature = this.iamConfigService.isCompanyFeatureEnabled();
|
|
52
|
+
const permissionEntity = enableCompanyFeature ? UserIamPermissionWithCompany : UserIamPermission;
|
|
53
|
+
const permissionRepo = queryRunner.manager.getRepository(permissionEntity);
|
|
54
|
+
const userRoleRows = await permissionRepo.find({
|
|
55
|
+
where: {
|
|
56
|
+
permissionType: IamPermissionType.USER_ROLE,
|
|
57
|
+
sourceType: IamEntityType.USER,
|
|
58
|
+
targetType: IamEntityType.ROLE,
|
|
59
|
+
targetId: In(roleIds)
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
const affectedUserIds = [
|
|
63
|
+
...new Set(userRoleRows.map((row)=>row.userId).filter((id)=>!!id))
|
|
64
|
+
];
|
|
65
|
+
await permissionRepo.delete({
|
|
66
|
+
permissionType: IamPermissionType.ROLE_ACTION,
|
|
67
|
+
sourceType: IamEntityType.ROLE,
|
|
68
|
+
sourceId: In(roleIds)
|
|
69
|
+
});
|
|
70
|
+
await permissionRepo.delete({
|
|
71
|
+
permissionType: IamPermissionType.USER_ROLE,
|
|
72
|
+
targetType: IamEntityType.ROLE,
|
|
73
|
+
targetId: In(roleIds)
|
|
74
|
+
});
|
|
75
|
+
if (affectedUserIds.length > 0) {
|
|
76
|
+
await this.permissionCacheService.invalidateUsers(affectedUserIds);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
37
79
|
resolveEntity() {
|
|
38
80
|
return this.iamConfigService.isCompanyFeatureEnabled() ? RoleWithCompany : Role;
|
|
39
81
|
}
|
|
@@ -107,8 +149,8 @@ export class RoleService extends ApiService {
|
|
|
107
149
|
deletedById: entity.deletedById
|
|
108
150
|
};
|
|
109
151
|
}
|
|
110
|
-
constructor(cacheManager, utilsService, iamConfigService, dataSourceProvider){
|
|
111
|
-
super('role', cacheManager, utilsService, RoleService.name, true, 'iam', undefined, dataSourceProvider), _define_property(this, "cacheManager", void 0), _define_property(this, "utilsService", void 0), _define_property(this, "iamConfigService", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.iamConfigService = iamConfigService;
|
|
152
|
+
constructor(cacheManager, utilsService, iamConfigService, dataSourceProvider, permissionCacheService){
|
|
153
|
+
super('role', cacheManager, utilsService, RoleService.name, true, 'iam', undefined, dataSourceProvider), _define_property(this, "cacheManager", void 0), _define_property(this, "utilsService", void 0), _define_property(this, "iamConfigService", void 0), _define_property(this, "permissionCacheService", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.iamConfigService = iamConfigService, this.permissionCacheService = permissionCacheService;
|
|
112
154
|
}
|
|
113
155
|
}
|
|
114
156
|
RoleService = _ts_decorate([
|
|
@@ -119,11 +161,13 @@ RoleService = _ts_decorate([
|
|
|
119
161
|
_ts_param(1, Inject(UtilsService)),
|
|
120
162
|
_ts_param(2, Inject(IAMConfigService)),
|
|
121
163
|
_ts_param(3, Inject(IAMDataSourceService)),
|
|
164
|
+
_ts_param(4, Inject(PermissionCacheService)),
|
|
122
165
|
_ts_metadata("design:type", Function),
|
|
123
166
|
_ts_metadata("design:paramtypes", [
|
|
124
167
|
typeof HybridCache === "undefined" ? Object : HybridCache,
|
|
125
168
|
typeof UtilsService === "undefined" ? Object : UtilsService,
|
|
126
169
|
typeof IAMConfigService === "undefined" ? Object : IAMConfigService,
|
|
127
|
-
typeof IAMDataSourceService === "undefined" ? Object : IAMDataSourceService
|
|
170
|
+
typeof IAMDataSourceService === "undefined" ? Object : IAMDataSourceService,
|
|
171
|
+
typeof PermissionCacheService === "undefined" ? Object : PermissionCacheService
|
|
128
172
|
])
|
|
129
173
|
], RoleService);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flusys/nestjs-iam",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.2",
|
|
4
4
|
"description": "Identity and Access Management (IAM) module for NestJS applications",
|
|
5
5
|
"main": "cjs/index.js",
|
|
6
6
|
"module": "fesm/index.js",
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
"express": "^5.0.0"
|
|
91
91
|
},
|
|
92
92
|
"dependencies": {
|
|
93
|
-
"@flusys/nestjs-core": "6.0.
|
|
94
|
-
"@flusys/nestjs-shared": "6.0.
|
|
93
|
+
"@flusys/nestjs-core": "6.0.2",
|
|
94
|
+
"@flusys/nestjs-shared": "6.0.2"
|
|
95
95
|
}
|
|
96
96
|
}
|
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
import { ApiService, HybridCache } from '@flusys/nestjs-shared/classes';
|
|
2
|
+
import { DeleteDto } from '@flusys/nestjs-shared/dtos';
|
|
2
3
|
import { ILoggedUserInfo } from '@flusys/nestjs-shared/interfaces';
|
|
3
4
|
import { UtilsService } from '@flusys/nestjs-shared/modules';
|
|
4
|
-
import { SelectQueryBuilder } from 'typeorm';
|
|
5
|
+
import { QueryRunner, SelectQueryBuilder } from 'typeorm';
|
|
5
6
|
import { CreateActionDto, UpdateActionDto } from '../dtos/action.dto';
|
|
6
7
|
import { Action } from '../entities/action.entity';
|
|
7
8
|
import { IAction, IActionTree } from '../interfaces/action.interface';
|
|
8
9
|
import { IAMConfigService } from './iam-config.service';
|
|
9
10
|
import { IAMDataSourceService } from './iam-datasource.service';
|
|
11
|
+
import { PermissionCacheService } from './permission-cache.service';
|
|
10
12
|
import { PermissionService } from './permission.service';
|
|
11
13
|
export declare class ActionService extends ApiService<CreateActionDto, UpdateActionDto, IAction, Action> {
|
|
12
14
|
protected cacheManager: HybridCache;
|
|
13
15
|
protected utilsService: UtilsService;
|
|
14
16
|
private readonly iamConfigService;
|
|
15
17
|
private readonly permissionService;
|
|
16
|
-
|
|
18
|
+
private readonly permissionCacheService;
|
|
19
|
+
constructor(cacheManager: HybridCache, utilsService: UtilsService, iamConfigService: IAMConfigService, dataSourceProvider: IAMDataSourceService, permissionService: PermissionService, permissionCacheService: PermissionCacheService);
|
|
20
|
+
protected beforeDeleteOperation(dto: DeleteDto, user: ILoggedUserInfo | null, queryRunner: QueryRunner): Promise<void>;
|
|
17
21
|
getSelectQuery(query: SelectQueryBuilder<Action>, _user: ILoggedUserInfo | null, select?: string[]): Promise<{
|
|
18
22
|
query: SelectQueryBuilder<Action>;
|
|
19
23
|
isRaw: boolean;
|
|
@@ -25,7 +29,7 @@ export declare class ActionService extends ApiService<CreateActionDto, UpdateAct
|
|
|
25
29
|
protected convertEntityToResponseDto(entity: Action, _isRaw: boolean): IAction;
|
|
26
30
|
private readonly actionSelectFields;
|
|
27
31
|
private requireUser;
|
|
28
|
-
getActionsForPermission(user: ILoggedUserInfo): Promise<IAction[]>;
|
|
32
|
+
getActionsForPermission(user: ILoggedUserInfo, companyId?: string): Promise<IAction[]>;
|
|
29
33
|
getActionTree(user: ILoggedUserInfo, search?: string, isActive?: boolean, withDeleted?: boolean): Promise<IActionTree[]>;
|
|
30
34
|
private buildActionTree;
|
|
31
35
|
}
|
|
@@ -1,10 +1,5 @@
|
|
|
1
|
-
import { HybridCache } from '@flusys/nestjs-shared';
|
|
2
|
-
export
|
|
3
|
-
userId: string;
|
|
4
|
-
companyId?: string | null;
|
|
5
|
-
branchId?: string | null;
|
|
6
|
-
enableCompanyFeature: boolean;
|
|
7
|
-
}
|
|
1
|
+
import { HybridCache, PermissionCacheKeyOptions, PermissionGuardConfig } from '@flusys/nestjs-shared';
|
|
2
|
+
export { PermissionCacheKeyOptions } from '@flusys/nestjs-shared';
|
|
8
3
|
export interface CachedMyPermissions {
|
|
9
4
|
frontendActions: Array<{
|
|
10
5
|
id: string;
|
|
@@ -15,24 +10,23 @@ export interface CachedMyPermissions {
|
|
|
15
10
|
}>;
|
|
16
11
|
backendCodes: string[];
|
|
17
12
|
}
|
|
13
|
+
export declare const MY_PERMISSIONS_CACHE_PREFIX = "my-permissions";
|
|
18
14
|
export declare class PermissionCacheService {
|
|
19
15
|
private readonly cacheManager;
|
|
16
|
+
private readonly guardConfig?;
|
|
17
|
+
private readonly TRACKED_KEYS_PREFIX;
|
|
18
|
+
private readonly TRACKED_SCOPES_PREFIX;
|
|
20
19
|
private readonly TTL;
|
|
21
|
-
|
|
22
|
-
private
|
|
23
|
-
private readonly MY_PERMISSIONS_PREFIX;
|
|
24
|
-
private readonly ACTION_CODE_PREFIX;
|
|
25
|
-
constructor(cacheManager: HybridCache);
|
|
26
|
-
generateCacheKey(options: PermissionCacheKeyOptions): string;
|
|
27
|
-
generateMyPermissionsCacheKey(options: PermissionCacheKeyOptions): string;
|
|
20
|
+
constructor(cacheManager: HybridCache, guardConfig?: PermissionGuardConfig | undefined);
|
|
21
|
+
private parseDurationMs;
|
|
28
22
|
private buildCacheKey;
|
|
29
|
-
|
|
23
|
+
private scopeToken;
|
|
24
|
+
private buildTrackedKeysKey;
|
|
25
|
+
private buildTrackedScopesKey;
|
|
30
26
|
setMyPermissions(options: PermissionCacheKeyOptions, data: CachedMyPermissions): Promise<void>;
|
|
31
27
|
getMyPermissions(options: PermissionCacheKeyOptions): Promise<CachedMyPermissions | null>;
|
|
32
|
-
private
|
|
33
|
-
setActionCodeMap(codeToIdMap: Record<string, string>, tenantId?: string): Promise<void>;
|
|
34
|
-
getActionIdsByCodes(codes: string[], tenantId?: string): Promise<Record<string, string> | null>;
|
|
28
|
+
private trackKey;
|
|
35
29
|
invalidateUser(userId: string, companyId?: string | null, branchIds?: (string | null)[]): Promise<void>;
|
|
30
|
+
private invalidateScope;
|
|
36
31
|
invalidateUsers(userIds: string[], companyId?: string | null, branchIds?: (string | null)[]): Promise<number>;
|
|
37
|
-
invalidateRole(_roleId: string, userIds: string[], companyId?: string | null, branchIds?: (string | null)[]): Promise<number>;
|
|
38
32
|
}
|