@flusys/nestjs-iam 6.1.0 → 6.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/controllers/company-action-permission.controller.js +1 -1
- package/cjs/controllers/company-action-permission.controller.spec.js +1 -1
- package/cjs/controllers/user-action-permission.controller.js +1 -1
- package/cjs/controllers/user-action-permission.controller.spec.js +1 -1
- package/cjs/dtos/permission.dto.js +7 -0
- package/cjs/entities/permission-base.entity.js +1 -10
- package/cjs/entities/permission-with-company.entity.js +0 -3
- package/cjs/entities/user-iam-permission.entity.js +0 -3
- package/cjs/modules/iam.module.js +2 -2
- package/cjs/services/action.service.js +4 -4
- package/cjs/services/action.service.spec.js +11 -11
- package/cjs/services/permission-cache.service.js +363 -52
- package/cjs/services/permission-cache.service.spec.js +462 -15
- package/cjs/services/permission.service.js +92 -396
- package/cjs/services/permission.service.spec.js +35 -310
- package/cjs/services/role.service.js +1 -1
- package/cjs/services/role.service.spec.js +4 -7
- package/dtos/permission.dto.d.ts +1 -0
- package/entities/permission-base.entity.d.ts +0 -1
- package/fesm/controllers/company-action-permission.controller.js +1 -1
- package/fesm/controllers/company-action-permission.controller.spec.js +1 -1
- package/fesm/controllers/user-action-permission.controller.js +1 -1
- package/fesm/controllers/user-action-permission.controller.spec.js +1 -1
- package/fesm/dtos/permission.dto.js +7 -0
- package/fesm/entities/permission-base.entity.js +1 -10
- package/fesm/entities/permission-with-company.entity.js +0 -3
- package/fesm/entities/user-iam-permission.entity.js +0 -3
- package/fesm/modules/iam.module.js +4 -4
- package/fesm/services/action.service.js +4 -4
- package/fesm/services/action.service.spec.js +11 -11
- package/fesm/services/permission-cache.service.js +365 -51
- package/fesm/services/permission-cache.service.spec.js +462 -15
- package/fesm/services/permission.service.js +92 -396
- package/fesm/services/permission.service.spec.js +35 -310
- package/fesm/services/role.service.js +1 -1
- package/fesm/services/role.service.spec.js +4 -7
- package/package.json +3 -3
- package/services/permission-cache.service.d.ts +20 -3
- package/services/permission.service.d.ts +2 -16
|
@@ -57,7 +57,7 @@ let CompanyActionPermissionController = class CompanyActionPermissionController
|
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
59
|
async getCompanyActions(dto) {
|
|
60
|
-
const actions = await this.permissionService.getCompanyActions(dto.companyId);
|
|
60
|
+
const actions = await this.permissionService.getCompanyActions(dto.companyId, false);
|
|
61
61
|
return {
|
|
62
62
|
success: true,
|
|
63
63
|
message: 'Company actions retrieved successfully',
|
|
@@ -54,7 +54,7 @@ describe('CompanyActionPermissionController', ()=>{
|
|
|
54
54
|
const result = await controller.getCompanyActions({
|
|
55
55
|
companyId: 'company-1'
|
|
56
56
|
});
|
|
57
|
-
expect(mockPermissionService.getCompanyActions).toHaveBeenCalledWith('company-1');
|
|
57
|
+
expect(mockPermissionService.getCompanyActions).toHaveBeenCalledWith('company-1', false);
|
|
58
58
|
expect(result.data).toEqual([
|
|
59
59
|
{
|
|
60
60
|
actionId: 'action-1'
|
|
@@ -58,7 +58,7 @@ let UserActionPermissionController = class UserActionPermissionController {
|
|
|
58
58
|
};
|
|
59
59
|
}
|
|
60
60
|
async getUserActions(dto, user) {
|
|
61
|
-
const actions = await this.permissionService.getUserActions(dto.userId, dto.
|
|
61
|
+
const actions = await this.permissionService.getUserActions(dto.userId, dto.companyId, dto.branchId);
|
|
62
62
|
return {
|
|
63
63
|
success: true,
|
|
64
64
|
message: 'User actions retrieved successfully',
|
|
@@ -57,7 +57,7 @@ describe('UserActionPermissionController', ()=>{
|
|
|
57
57
|
branchId: 'branch-1',
|
|
58
58
|
companyId: 'company-1'
|
|
59
59
|
}, user);
|
|
60
|
-
expect(mockPermissionService.getUserActions).toHaveBeenCalledWith('user-2', '
|
|
60
|
+
expect(mockPermissionService.getUserActions).toHaveBeenCalledWith('user-2', 'company-1', 'branch-1');
|
|
61
61
|
expect(result.data).toEqual([
|
|
62
62
|
{
|
|
63
63
|
actionId: 'action-1'
|
|
@@ -351,6 +351,7 @@ let UserActionResponseDto = class UserActionResponseDto {
|
|
|
351
351
|
_define_property(this, "actionId", void 0);
|
|
352
352
|
_define_property(this, "actionCode", void 0);
|
|
353
353
|
_define_property(this, "actionName", void 0);
|
|
354
|
+
_define_property(this, "companyId", void 0);
|
|
354
355
|
_define_property(this, "branchId", void 0);
|
|
355
356
|
_define_property(this, "createdAt", void 0);
|
|
356
357
|
}
|
|
@@ -385,6 +386,12 @@ _ts_decorate([
|
|
|
385
386
|
}),
|
|
386
387
|
_ts_metadata("design:type", String)
|
|
387
388
|
], UserActionResponseDto.prototype, "actionName", void 0);
|
|
389
|
+
_ts_decorate([
|
|
390
|
+
(0, _swagger.ApiPropertyOptional)({
|
|
391
|
+
description: 'Company ID (null = global)'
|
|
392
|
+
}),
|
|
393
|
+
_ts_metadata("design:type", Object)
|
|
394
|
+
], UserActionResponseDto.prototype, "companyId", void 0);
|
|
388
395
|
_ts_decorate([
|
|
389
396
|
(0, _swagger.ApiPropertyOptional)({
|
|
390
397
|
description: 'Branch ID (null = company-wide)'
|
|
@@ -39,7 +39,7 @@ let PermissionBase = class PermissionBase extends _nestjsshared.Identity {
|
|
|
39
39
|
return true;
|
|
40
40
|
}
|
|
41
41
|
constructor(...args){
|
|
42
|
-
super(...args), _define_property(this, "permissionType", void 0), _define_property(this, "sourceType", void 0), _define_property(this, "sourceId", void 0), _define_property(this, "targetType", void 0), _define_property(this, "targetId", void 0), _define_property(this, "
|
|
42
|
+
super(...args), _define_property(this, "permissionType", void 0), _define_property(this, "sourceType", void 0), _define_property(this, "sourceId", void 0), _define_property(this, "targetType", void 0), _define_property(this, "targetId", void 0), _define_property(this, "validFrom", void 0), _define_property(this, "validUntil", void 0), _define_property(this, "reason", void 0);
|
|
43
43
|
}
|
|
44
44
|
};
|
|
45
45
|
_ts_decorate([
|
|
@@ -82,15 +82,6 @@ _ts_decorate([
|
|
|
82
82
|
}),
|
|
83
83
|
_ts_metadata("design:type", String)
|
|
84
84
|
], PermissionBase.prototype, "targetId", void 0);
|
|
85
|
-
_ts_decorate([
|
|
86
|
-
(0, _typeorm.Column)({
|
|
87
|
-
type: 'varchar',
|
|
88
|
-
length: 150,
|
|
89
|
-
nullable: true,
|
|
90
|
-
name: 'user_id'
|
|
91
|
-
}),
|
|
92
|
-
_ts_metadata("design:type", Object)
|
|
93
|
-
], PermissionBase.prototype, "userId", void 0);
|
|
94
85
|
_ts_decorate([
|
|
95
86
|
(0, _typeorm.Column)({
|
|
96
87
|
type: 'timestamp',
|
|
@@ -69,8 +69,8 @@ let IAMModule = class IAMModule {
|
|
|
69
69
|
provide: _nestjsshared.PERMISSION_GUARD_CONFIG,
|
|
70
70
|
useValue: {
|
|
71
71
|
enableCompanyFeature,
|
|
72
|
-
userPermissionKeyFormat: `${
|
|
73
|
-
companyPermissionKeyFormat: `${
|
|
72
|
+
userPermissionKeyFormat: `${_nestjsshared.PERMISSIONS_CACHE_PREFIX}:user:{userId}`,
|
|
73
|
+
companyPermissionKeyFormat: `${_nestjsshared.PERMISSIONS_CACHE_PREFIX}:company:{companyId}:branch:{branchId}:user:{userId}`
|
|
74
74
|
}
|
|
75
75
|
};
|
|
76
76
|
}
|
|
@@ -85,7 +85,7 @@ let ActionService = class ActionService extends _classes.ApiService {
|
|
|
85
85
|
}
|
|
86
86
|
}) : Promise.resolve([])
|
|
87
87
|
]);
|
|
88
|
-
const affectedUserIds = new Set(userActionRows.map((row)=>row.
|
|
88
|
+
const affectedUserIds = new Set(userActionRows.map((row)=>row.sourceId));
|
|
89
89
|
const affectedCompanyIds = [
|
|
90
90
|
...new Set(companyActionRows.map((row)=>row.sourceId).filter((id)=>!!id))
|
|
91
91
|
];
|
|
@@ -100,7 +100,7 @@ let ActionService = class ActionService extends _classes.ApiService {
|
|
|
100
100
|
targetId: (0, _typeorm.In)(affectedRoleIds)
|
|
101
101
|
}
|
|
102
102
|
});
|
|
103
|
-
userRoleRows.forEach((row)=>
|
|
103
|
+
userRoleRows.forEach((row)=>affectedUserIds.add(row.sourceId));
|
|
104
104
|
}
|
|
105
105
|
await permissionRepo.delete({
|
|
106
106
|
permissionType: _iampermissiontypeenum.IamPermissionType.ROLE_ACTION,
|
|
@@ -122,7 +122,7 @@ let ActionService = class ActionService extends _classes.ApiService {
|
|
|
122
122
|
affectedUserIds.size > 0 ? this.permissionCacheService.invalidateUsers([
|
|
123
123
|
...affectedUserIds
|
|
124
124
|
]) : Promise.resolve(),
|
|
125
|
-
...affectedCompanyIds.map((companyId)=>this.
|
|
125
|
+
...affectedCompanyIds.map((companyId)=>this.permissionCacheService.invalidateCompanyMembersCache(companyId))
|
|
126
126
|
]);
|
|
127
127
|
}
|
|
128
128
|
// Query Customization
|
|
@@ -194,7 +194,7 @@ let ActionService = class ActionService extends _classes.ApiService {
|
|
|
194
194
|
let whereClause = {};
|
|
195
195
|
const targetCompanyId = companyId || user.companyId;
|
|
196
196
|
if (this.iamConfigService.isCompanyFeatureEnabled() && targetCompanyId) {
|
|
197
|
-
const companyActionIds = await this.permissionService.
|
|
197
|
+
const companyActionIds = await this.permissionService.getCompanyActions(targetCompanyId, true);
|
|
198
198
|
if (companyActionIds.length === 0) {
|
|
199
199
|
return [];
|
|
200
200
|
}
|
|
@@ -46,11 +46,11 @@ describe('ActionService', ()=>{
|
|
|
46
46
|
isCompanyFeatureEnabled: jest.fn().mockReturnValue(false)
|
|
47
47
|
};
|
|
48
48
|
mockPermissionService = {
|
|
49
|
-
|
|
50
|
-
invalidateCompanyMembersCache: jest.fn()
|
|
49
|
+
getCompanyActions: jest.fn()
|
|
51
50
|
};
|
|
52
51
|
mockPermissionCacheService = {
|
|
53
|
-
invalidateUsers: jest.fn()
|
|
52
|
+
invalidateUsers: jest.fn(),
|
|
53
|
+
invalidateCompanyMembersCache: jest.fn()
|
|
54
54
|
};
|
|
55
55
|
const module = await _testing.Test.createTestingModule({
|
|
56
56
|
providers: [
|
|
@@ -89,20 +89,20 @@ describe('ActionService', ()=>{
|
|
|
89
89
|
buildAction()
|
|
90
90
|
]);
|
|
91
91
|
const result = await service.getActionsForPermission((0, _loggedusermock.buildMockUser)());
|
|
92
|
-
expect(mockPermissionService.
|
|
92
|
+
expect(mockPermissionService.getCompanyActions).not.toHaveBeenCalled();
|
|
93
93
|
expect(result).toHaveLength(1);
|
|
94
94
|
expect(result[0].code).toBe('products.read');
|
|
95
95
|
});
|
|
96
96
|
it('scopes actions to the company whitelist when the company feature is enabled', async ()=>{
|
|
97
97
|
mockIamConfigService.isCompanyFeatureEnabled.mockReturnValue(true);
|
|
98
|
-
mockPermissionService.
|
|
98
|
+
mockPermissionService.getCompanyActions.mockResolvedValue([
|
|
99
99
|
'action-uuid-1'
|
|
100
100
|
]);
|
|
101
101
|
mockRepo.find.mockResolvedValue([
|
|
102
102
|
buildAction()
|
|
103
103
|
]);
|
|
104
104
|
await service.getActionsForPermission((0, _loggedusermock.buildMockUser)(), 'company-uuid-1');
|
|
105
|
-
expect(mockPermissionService.
|
|
105
|
+
expect(mockPermissionService.getCompanyActions).toHaveBeenCalledWith('company-uuid-1', true);
|
|
106
106
|
expect(mockRepo.find).toHaveBeenCalledWith(expect.objectContaining({
|
|
107
107
|
where: {
|
|
108
108
|
id: expect.anything()
|
|
@@ -111,7 +111,7 @@ describe('ActionService', ()=>{
|
|
|
111
111
|
});
|
|
112
112
|
it('short-circuits without querying when the company has no whitelisted actions', async ()=>{
|
|
113
113
|
mockIamConfigService.isCompanyFeatureEnabled.mockReturnValue(true);
|
|
114
|
-
mockPermissionService.
|
|
114
|
+
mockPermissionService.getCompanyActions.mockResolvedValue([]);
|
|
115
115
|
const result = await service.getActionsForPermission((0, _loggedusermock.buildMockUser)(), 'company-uuid-1');
|
|
116
116
|
expect(result).toEqual([]);
|
|
117
117
|
expect(mockRepo.find).not.toHaveBeenCalled();
|
|
@@ -251,12 +251,12 @@ describe('ActionService', ()=>{
|
|
|
251
251
|
]) // role-action rows referencing the deleted action
|
|
252
252
|
.mockResolvedValueOnce([
|
|
253
253
|
{
|
|
254
|
-
|
|
254
|
+
sourceId: 'user-1'
|
|
255
255
|
}
|
|
256
256
|
]) // direct user-action rows
|
|
257
257
|
.mockResolvedValueOnce([
|
|
258
258
|
{
|
|
259
|
-
|
|
259
|
+
sourceId: 'user-2'
|
|
260
260
|
}
|
|
261
261
|
]); // users holding role-1 (cascaded lookup)
|
|
262
262
|
const queryRunner = buildQueryRunner(permissionRepo);
|
|
@@ -277,7 +277,7 @@ describe('ActionService', ()=>{
|
|
|
277
277
|
'user-1',
|
|
278
278
|
'user-2'
|
|
279
279
|
]));
|
|
280
|
-
expect(
|
|
280
|
+
expect(mockPermissionCacheService.invalidateCompanyMembersCache).not.toHaveBeenCalled();
|
|
281
281
|
});
|
|
282
282
|
it('also queries and invalidates company whitelist entries when the company feature is enabled', async ()=>{
|
|
283
283
|
mockIamConfigService.isCompanyFeatureEnabled.mockReturnValue(true);
|
|
@@ -296,7 +296,7 @@ describe('ActionService', ()=>{
|
|
|
296
296
|
],
|
|
297
297
|
type: 'delete'
|
|
298
298
|
}, (0, _loggedusermock.buildMockUser)(), queryRunner);
|
|
299
|
-
expect(
|
|
299
|
+
expect(mockPermissionCacheService.invalidateCompanyMembersCache).toHaveBeenCalledWith('company-1');
|
|
300
300
|
});
|
|
301
301
|
it('does not invalidate any cache when nothing is affected', async ()=>{
|
|
302
302
|
const permissionRepo = (0, _repositorymock.createMockRepository)();
|