@flusys/nestjs-iam 6.1.0 → 6.1.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.
Files changed (39) hide show
  1. package/cjs/controllers/company-action-permission.controller.js +1 -1
  2. package/cjs/controllers/company-action-permission.controller.spec.js +1 -1
  3. package/cjs/controllers/user-action-permission.controller.js +1 -1
  4. package/cjs/controllers/user-action-permission.controller.spec.js +1 -1
  5. package/cjs/dtos/permission.dto.js +7 -0
  6. package/cjs/entities/permission-base.entity.js +1 -10
  7. package/cjs/entities/permission-with-company.entity.js +0 -3
  8. package/cjs/entities/user-iam-permission.entity.js +0 -3
  9. package/cjs/modules/iam.module.js +2 -2
  10. package/cjs/services/action.service.js +4 -4
  11. package/cjs/services/action.service.spec.js +11 -11
  12. package/cjs/services/permission-cache.service.js +363 -52
  13. package/cjs/services/permission-cache.service.spec.js +462 -15
  14. package/cjs/services/permission.service.js +92 -396
  15. package/cjs/services/permission.service.spec.js +35 -310
  16. package/cjs/services/role.service.js +1 -1
  17. package/cjs/services/role.service.spec.js +4 -7
  18. package/dtos/permission.dto.d.ts +1 -0
  19. package/entities/permission-base.entity.d.ts +0 -1
  20. package/fesm/controllers/company-action-permission.controller.js +1 -1
  21. package/fesm/controllers/company-action-permission.controller.spec.js +1 -1
  22. package/fesm/controllers/user-action-permission.controller.js +1 -1
  23. package/fesm/controllers/user-action-permission.controller.spec.js +1 -1
  24. package/fesm/dtos/permission.dto.js +7 -0
  25. package/fesm/entities/permission-base.entity.js +1 -10
  26. package/fesm/entities/permission-with-company.entity.js +0 -3
  27. package/fesm/entities/user-iam-permission.entity.js +0 -3
  28. package/fesm/modules/iam.module.js +4 -4
  29. package/fesm/services/action.service.js +4 -4
  30. package/fesm/services/action.service.spec.js +11 -11
  31. package/fesm/services/permission-cache.service.js +365 -51
  32. package/fesm/services/permission-cache.service.spec.js +462 -15
  33. package/fesm/services/permission.service.js +92 -396
  34. package/fesm/services/permission.service.spec.js +35 -310
  35. package/fesm/services/role.service.js +1 -1
  36. package/fesm/services/role.service.spec.js +4 -7
  37. package/package.json +3 -3
  38. package/services/permission-cache.service.d.ts +20 -3
  39. 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.branchId, dto.companyId);
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', 'branch-1', 'company-1');
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, "userId", void 0), _define_property(this, "validFrom", void 0), _define_property(this, "validUntil", void 0), _define_property(this, "reason", void 0);
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',
@@ -87,9 +87,6 @@ UserIamPermissionWithCompany = _ts_decorate([
87
87
  (0, _typeorm.Index)([
88
88
  'permissionType'
89
89
  ]),
90
- (0, _typeorm.Index)([
91
- 'userId'
92
- ]),
93
90
  (0, _typeorm.Index)([
94
91
  'companyId'
95
92
  ]),
@@ -39,8 +39,5 @@ UserIamPermission = _ts_decorate([
39
39
  ]),
40
40
  (0, _typeorm.Index)([
41
41
  'permissionType'
42
- ]),
43
- (0, _typeorm.Index)([
44
- 'userId'
45
42
  ])
46
43
  ], UserIamPermission);
@@ -69,8 +69,8 @@ let IAMModule = class IAMModule {
69
69
  provide: _nestjsshared.PERMISSION_GUARD_CONFIG,
70
70
  useValue: {
71
71
  enableCompanyFeature,
72
- userPermissionKeyFormat: `${_permissioncacheservice.MY_PERMISSIONS_CACHE_PREFIX}:user:{userId}`,
73
- companyPermissionKeyFormat: `${_permissioncacheservice.MY_PERMISSIONS_CACHE_PREFIX}:company:{companyId}:branch:{branchId}:user:{userId}`
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.userId).filter((id)=>!!id));
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)=>row.userId && affectedUserIds.add(row.userId));
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.permissionService.invalidateCompanyMembersCache(companyId))
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.getCompanyActionIds(targetCompanyId);
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
- getCompanyActionIds: jest.fn(),
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.getCompanyActionIds).not.toHaveBeenCalled();
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.getCompanyActionIds.mockResolvedValue([
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.getCompanyActionIds).toHaveBeenCalledWith('company-uuid-1');
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.getCompanyActionIds.mockResolvedValue([]);
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
- userId: 'user-1'
254
+ sourceId: 'user-1'
255
255
  }
256
256
  ]) // direct user-action rows
257
257
  .mockResolvedValueOnce([
258
258
  {
259
- userId: 'user-2'
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(mockPermissionService.invalidateCompanyMembersCache).not.toHaveBeenCalled();
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(mockPermissionService.invalidateCompanyMembersCache).toHaveBeenCalledWith('company-1');
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)();