@flusys/nestjs-iam 6.0.2 → 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/action.controller.spec.js +79 -0
- package/cjs/controllers/company-action-permission.controller.js +1 -1
- package/cjs/controllers/company-action-permission.controller.spec.js +65 -0
- package/cjs/controllers/my-permission.controller.spec.js +56 -0
- package/cjs/controllers/role-permission.controller.spec.js +140 -0
- package/cjs/controllers/role.controller.spec.js +86 -0
- package/cjs/controllers/user-action-permission.controller.js +1 -1
- package/cjs/controllers/user-action-permission.controller.spec.js +68 -0
- package/cjs/docs/iam-swagger.config.spec.js +99 -0
- package/cjs/dtos/permission.dto.js +7 -0
- package/cjs/entities/index.spec.js +56 -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/helpers/company-access.helper.spec.js +68 -0
- package/cjs/helpers/permission-mode.helper.spec.js +54 -0
- package/cjs/modules/iam.module.js +2 -2
- package/cjs/modules/iam.module.spec.js +293 -0
- package/cjs/services/action.service.js +4 -4
- package/cjs/services/action.service.spec.js +314 -0
- package/cjs/services/iam-config.service.spec.js +110 -0
- package/cjs/services/iam-datasource.service.spec.js +117 -0
- package/cjs/services/permission-cache.service.js +363 -52
- package/cjs/services/permission-cache.service.spec.js +699 -0
- package/cjs/services/permission.service.js +92 -396
- package/cjs/services/permission.service.spec.js +692 -0
- package/cjs/services/role.service.js +1 -1
- package/cjs/services/role.service.spec.js +309 -0
- package/dtos/permission.dto.d.ts +1 -0
- package/entities/permission-base.entity.d.ts +0 -1
- package/fesm/controllers/action.controller.spec.js +75 -0
- package/fesm/controllers/company-action-permission.controller.js +1 -1
- package/fesm/controllers/company-action-permission.controller.spec.js +61 -0
- package/fesm/controllers/my-permission.controller.spec.js +52 -0
- package/fesm/controllers/role-permission.controller.spec.js +136 -0
- package/fesm/controllers/role.controller.spec.js +82 -0
- package/fesm/controllers/user-action-permission.controller.js +1 -1
- package/fesm/controllers/user-action-permission.controller.spec.js +64 -0
- package/fesm/docs/iam-swagger.config.spec.js +95 -0
- package/fesm/dtos/permission.dto.js +7 -0
- package/fesm/entities/index.spec.js +52 -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/helpers/company-access.helper.spec.js +64 -0
- package/fesm/helpers/permission-mode.helper.spec.js +50 -0
- package/fesm/modules/iam.module.js +4 -4
- package/fesm/modules/iam.module.spec.js +289 -0
- package/fesm/services/action.service.js +4 -4
- package/fesm/services/action.service.spec.js +310 -0
- package/fesm/services/iam-config.service.spec.js +106 -0
- package/fesm/services/iam-datasource.service.spec.js +113 -0
- package/fesm/services/permission-cache.service.js +365 -51
- package/fesm/services/permission-cache.service.spec.js +695 -0
- package/fesm/services/permission.service.js +92 -396
- package/fesm/services/permission.service.spec.js +688 -0
- package/fesm/services/role.service.js +1 -1
- package/fesm/services/role.service.spec.js +305 -0
- package/package.json +3 -3
- package/services/permission-cache.service.d.ts +20 -3
- package/services/permission.service.d.ts +2 -16
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
const _loggedusermock = require("@test-utils/mocks/logged-user.mock");
|
|
6
|
+
const _actioncontroller = require("./action.controller");
|
|
7
|
+
describe('ActionController', ()=>{
|
|
8
|
+
let controller;
|
|
9
|
+
let mockActionService;
|
|
10
|
+
let mockIamConfigService;
|
|
11
|
+
beforeEach(()=>{
|
|
12
|
+
mockActionService = {
|
|
13
|
+
insert: jest.fn(),
|
|
14
|
+
findById: jest.fn(),
|
|
15
|
+
getAll: jest.fn(),
|
|
16
|
+
update: jest.fn(),
|
|
17
|
+
delete: jest.fn(),
|
|
18
|
+
getActionsForPermission: jest.fn(),
|
|
19
|
+
getActionTree: jest.fn()
|
|
20
|
+
};
|
|
21
|
+
mockIamConfigService = {};
|
|
22
|
+
controller = new _actioncontroller.ActionController(mockActionService, mockIamConfigService);
|
|
23
|
+
});
|
|
24
|
+
it('wires the injected service onto the generated controller base', ()=>{
|
|
25
|
+
expect(controller.actionService).toBe(mockActionService);
|
|
26
|
+
expect(controller.service).toBe(mockActionService);
|
|
27
|
+
});
|
|
28
|
+
describe('getActionsForPermission', ()=>{
|
|
29
|
+
it('returns actions scoped to the caller session company when no companyId is given', async ()=>{
|
|
30
|
+
const user = (0, _loggedusermock.buildMockUser)();
|
|
31
|
+
mockActionService.getActionsForPermission.mockResolvedValue([
|
|
32
|
+
{
|
|
33
|
+
id: 'action-1',
|
|
34
|
+
code: 'user.create'
|
|
35
|
+
}
|
|
36
|
+
]);
|
|
37
|
+
const result = await controller.getActionsForPermission({}, user);
|
|
38
|
+
expect(mockActionService.getActionsForPermission).toHaveBeenCalledWith(user, undefined);
|
|
39
|
+
expect(result.success).toBe(true);
|
|
40
|
+
expect(result.data).toEqual([
|
|
41
|
+
{
|
|
42
|
+
id: 'action-1',
|
|
43
|
+
code: 'user.create'
|
|
44
|
+
}
|
|
45
|
+
]);
|
|
46
|
+
});
|
|
47
|
+
it('forwards an explicit companyId override', async ()=>{
|
|
48
|
+
const user = (0, _loggedusermock.buildMockUser)();
|
|
49
|
+
mockActionService.getActionsForPermission.mockResolvedValue([]);
|
|
50
|
+
await controller.getActionsForPermission({
|
|
51
|
+
companyId: 'company-2'
|
|
52
|
+
}, user);
|
|
53
|
+
expect(mockActionService.getActionsForPermission).toHaveBeenCalledWith(user, 'company-2');
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
describe('getActionTree', ()=>{
|
|
57
|
+
it('returns the hierarchical action tree with query filters applied', async ()=>{
|
|
58
|
+
const user = (0, _loggedusermock.buildMockUser)();
|
|
59
|
+
mockActionService.getActionTree.mockResolvedValue([
|
|
60
|
+
{
|
|
61
|
+
id: 'action-1',
|
|
62
|
+
children: []
|
|
63
|
+
}
|
|
64
|
+
]);
|
|
65
|
+
const result = await controller.getActionTree({
|
|
66
|
+
search: 'user',
|
|
67
|
+
isActive: true,
|
|
68
|
+
withDeleted: false
|
|
69
|
+
}, user);
|
|
70
|
+
expect(mockActionService.getActionTree).toHaveBeenCalledWith(user, 'user', true, false);
|
|
71
|
+
expect(result.data).toEqual([
|
|
72
|
+
{
|
|
73
|
+
id: 'action-1',
|
|
74
|
+
children: []
|
|
75
|
+
}
|
|
76
|
+
]);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
});
|
|
@@ -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',
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
const _companyactionpermissioncontroller = require("./company-action-permission.controller");
|
|
6
|
+
describe('CompanyActionPermissionController', ()=>{
|
|
7
|
+
let controller;
|
|
8
|
+
let mockPermissionService;
|
|
9
|
+
beforeEach(()=>{
|
|
10
|
+
mockPermissionService = {
|
|
11
|
+
assignCompanyActions: jest.fn(),
|
|
12
|
+
getCompanyActions: jest.fn()
|
|
13
|
+
};
|
|
14
|
+
controller = new _companyactionpermissioncontroller.CompanyActionPermissionController(mockPermissionService);
|
|
15
|
+
});
|
|
16
|
+
describe('assignCompanyActions', ()=>{
|
|
17
|
+
it('whitelists actions for a company', async ()=>{
|
|
18
|
+
mockPermissionService.assignCompanyActions.mockResolvedValue({
|
|
19
|
+
added: 3,
|
|
20
|
+
removed: 0,
|
|
21
|
+
total: 3
|
|
22
|
+
});
|
|
23
|
+
const result = await controller.assignCompanyActions({
|
|
24
|
+
companyId: 'company-1',
|
|
25
|
+
actionIds: [
|
|
26
|
+
'action-1',
|
|
27
|
+
'action-2',
|
|
28
|
+
'action-3'
|
|
29
|
+
]
|
|
30
|
+
});
|
|
31
|
+
expect(mockPermissionService.assignCompanyActions).toHaveBeenCalledWith({
|
|
32
|
+
companyId: 'company-1',
|
|
33
|
+
actionIds: [
|
|
34
|
+
'action-1',
|
|
35
|
+
'action-2',
|
|
36
|
+
'action-3'
|
|
37
|
+
]
|
|
38
|
+
});
|
|
39
|
+
expect(result.success).toBe(true);
|
|
40
|
+
expect(result.data).toEqual({
|
|
41
|
+
added: 3,
|
|
42
|
+
removed: 0,
|
|
43
|
+
total: 3
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
describe('getCompanyActions', ()=>{
|
|
48
|
+
it('returns the whitelisted actions for a company', async ()=>{
|
|
49
|
+
mockPermissionService.getCompanyActions.mockResolvedValue([
|
|
50
|
+
{
|
|
51
|
+
actionId: 'action-1'
|
|
52
|
+
}
|
|
53
|
+
]);
|
|
54
|
+
const result = await controller.getCompanyActions({
|
|
55
|
+
companyId: 'company-1'
|
|
56
|
+
});
|
|
57
|
+
expect(mockPermissionService.getCompanyActions).toHaveBeenCalledWith('company-1', false);
|
|
58
|
+
expect(result.data).toEqual([
|
|
59
|
+
{
|
|
60
|
+
actionId: 'action-1'
|
|
61
|
+
}
|
|
62
|
+
]);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
const _loggedusermock = require("@test-utils/mocks/logged-user.mock");
|
|
6
|
+
const _mypermissioncontroller = require("./my-permission.controller");
|
|
7
|
+
describe('MyPermissionController', ()=>{
|
|
8
|
+
let controller;
|
|
9
|
+
let mockPermissionService;
|
|
10
|
+
beforeEach(()=>{
|
|
11
|
+
mockPermissionService = {
|
|
12
|
+
getMyPermissions: jest.fn()
|
|
13
|
+
};
|
|
14
|
+
controller = new _mypermissioncontroller.MyPermissionController(mockPermissionService);
|
|
15
|
+
});
|
|
16
|
+
describe('getMyPermissions', ()=>{
|
|
17
|
+
it('resolves permissions for the current user, branch, and company', async ()=>{
|
|
18
|
+
const user = (0, _loggedusermock.buildMockUser)({
|
|
19
|
+
id: 'user-1',
|
|
20
|
+
companyId: 'company-1',
|
|
21
|
+
branchId: 'branch-1'
|
|
22
|
+
});
|
|
23
|
+
const permissions = {
|
|
24
|
+
menus: [],
|
|
25
|
+
actions: [
|
|
26
|
+
'user.create'
|
|
27
|
+
]
|
|
28
|
+
};
|
|
29
|
+
mockPermissionService.getMyPermissions.mockResolvedValue(permissions);
|
|
30
|
+
const result = await controller.getMyPermissions({
|
|
31
|
+
parentCodes: [
|
|
32
|
+
'user'
|
|
33
|
+
]
|
|
34
|
+
}, user);
|
|
35
|
+
expect(mockPermissionService.getMyPermissions).toHaveBeenCalledWith('user-1', 'branch-1', 'company-1', [
|
|
36
|
+
'user'
|
|
37
|
+
]);
|
|
38
|
+
expect(result).toEqual({
|
|
39
|
+
success: true,
|
|
40
|
+
message: 'Permissions loaded successfully',
|
|
41
|
+
messageKey: expect.any(String),
|
|
42
|
+
data: permissions
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
it('passes null for missing branch and company context', async ()=>{
|
|
46
|
+
const user = (0, _loggedusermock.buildMockUser)({
|
|
47
|
+
id: 'user-1',
|
|
48
|
+
companyId: undefined,
|
|
49
|
+
branchId: undefined
|
|
50
|
+
});
|
|
51
|
+
mockPermissionService.getMyPermissions.mockResolvedValue({});
|
|
52
|
+
await controller.getMyPermissions({}, user);
|
|
53
|
+
expect(mockPermissionService.getMyPermissions).toHaveBeenCalledWith('user-1', null, null, undefined);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
});
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
const _common = require("@nestjs/common");
|
|
6
|
+
const _loggedusermock = require("@test-utils/mocks/logged-user.mock");
|
|
7
|
+
const _rolepermissioncontroller = require("./role-permission.controller");
|
|
8
|
+
describe('RolePermissionController', ()=>{
|
|
9
|
+
let controller;
|
|
10
|
+
let mockPermissionService;
|
|
11
|
+
let mockConfig;
|
|
12
|
+
beforeEach(()=>{
|
|
13
|
+
mockPermissionService = {
|
|
14
|
+
assignRoleActions: jest.fn(),
|
|
15
|
+
getRoleActions: jest.fn(),
|
|
16
|
+
assignUserRoles: jest.fn(),
|
|
17
|
+
getUserRoles: jest.fn()
|
|
18
|
+
};
|
|
19
|
+
mockConfig = {
|
|
20
|
+
isCompanyFeatureEnabled: jest.fn().mockReturnValue(false)
|
|
21
|
+
};
|
|
22
|
+
controller = new _rolepermissioncontroller.RolePermissionController(mockPermissionService, mockConfig);
|
|
23
|
+
});
|
|
24
|
+
describe('assignRoleActions', ()=>{
|
|
25
|
+
it('assigns actions to a role and returns the operation summary', async ()=>{
|
|
26
|
+
mockPermissionService.assignRoleActions.mockResolvedValue({
|
|
27
|
+
added: 2,
|
|
28
|
+
removed: 1,
|
|
29
|
+
total: 3
|
|
30
|
+
});
|
|
31
|
+
const result = await controller.assignRoleActions({
|
|
32
|
+
roleId: 'role-1',
|
|
33
|
+
actionIds: [
|
|
34
|
+
'action-1',
|
|
35
|
+
'action-2'
|
|
36
|
+
]
|
|
37
|
+
});
|
|
38
|
+
expect(mockPermissionService.assignRoleActions).toHaveBeenCalledWith({
|
|
39
|
+
roleId: 'role-1',
|
|
40
|
+
actionIds: [
|
|
41
|
+
'action-1',
|
|
42
|
+
'action-2'
|
|
43
|
+
]
|
|
44
|
+
});
|
|
45
|
+
expect(result.success).toBe(true);
|
|
46
|
+
expect(result.data).toEqual({
|
|
47
|
+
added: 2,
|
|
48
|
+
removed: 1,
|
|
49
|
+
total: 3
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
describe('getRoleActions', ()=>{
|
|
54
|
+
it('returns actions assigned to the role', async ()=>{
|
|
55
|
+
mockPermissionService.getRoleActions.mockResolvedValue([
|
|
56
|
+
{
|
|
57
|
+
actionId: 'action-1'
|
|
58
|
+
}
|
|
59
|
+
]);
|
|
60
|
+
const result = await controller.getRoleActions({
|
|
61
|
+
roleId: 'role-1'
|
|
62
|
+
});
|
|
63
|
+
expect(mockPermissionService.getRoleActions).toHaveBeenCalledWith('role-1');
|
|
64
|
+
expect(result.data).toEqual([
|
|
65
|
+
{
|
|
66
|
+
actionId: 'action-1'
|
|
67
|
+
}
|
|
68
|
+
]);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
describe('assignUserRoles', ()=>{
|
|
72
|
+
it('assigns roles to a user when the caller has access to the target company', async ()=>{
|
|
73
|
+
mockConfig.isCompanyFeatureEnabled.mockReturnValue(true);
|
|
74
|
+
const user = (0, _loggedusermock.buildMockUser)({
|
|
75
|
+
companyId: 'company-1'
|
|
76
|
+
});
|
|
77
|
+
mockPermissionService.assignUserRoles.mockResolvedValue({
|
|
78
|
+
added: 1,
|
|
79
|
+
removed: 0,
|
|
80
|
+
total: 1
|
|
81
|
+
});
|
|
82
|
+
const result = await controller.assignUserRoles({
|
|
83
|
+
userId: 'user-2',
|
|
84
|
+
roleIds: [
|
|
85
|
+
'role-1'
|
|
86
|
+
],
|
|
87
|
+
companyId: 'company-1'
|
|
88
|
+
}, user);
|
|
89
|
+
expect(mockPermissionService.assignUserRoles).toHaveBeenCalledWith({
|
|
90
|
+
userId: 'user-2',
|
|
91
|
+
roleIds: [
|
|
92
|
+
'role-1'
|
|
93
|
+
],
|
|
94
|
+
companyId: 'company-1'
|
|
95
|
+
});
|
|
96
|
+
expect(result.data).toEqual({
|
|
97
|
+
added: 1,
|
|
98
|
+
removed: 0,
|
|
99
|
+
total: 1
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
it('rejects assignment when the caller has no access to the target company', async ()=>{
|
|
103
|
+
mockConfig.isCompanyFeatureEnabled.mockReturnValue(true);
|
|
104
|
+
const user = (0, _loggedusermock.buildMockUser)({
|
|
105
|
+
companyId: 'company-1'
|
|
106
|
+
});
|
|
107
|
+
await expect(controller.assignUserRoles({
|
|
108
|
+
userId: 'user-2',
|
|
109
|
+
roleIds: [
|
|
110
|
+
'role-1'
|
|
111
|
+
],
|
|
112
|
+
companyId: 'company-2'
|
|
113
|
+
}, user)).rejects.toThrow(_common.ForbiddenException);
|
|
114
|
+
expect(mockPermissionService.assignUserRoles).not.toHaveBeenCalled();
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
describe('getUserRoles', ()=>{
|
|
118
|
+
it('returns roles assigned to the user scoped by branch and company', async ()=>{
|
|
119
|
+
const user = (0, _loggedusermock.buildMockUser)({
|
|
120
|
+
companyId: 'company-1'
|
|
121
|
+
});
|
|
122
|
+
mockPermissionService.getUserRoles.mockResolvedValue([
|
|
123
|
+
{
|
|
124
|
+
roleId: 'role-1'
|
|
125
|
+
}
|
|
126
|
+
]);
|
|
127
|
+
const result = await controller.getUserRoles({
|
|
128
|
+
userId: 'user-2',
|
|
129
|
+
branchId: 'branch-1',
|
|
130
|
+
companyId: 'company-1'
|
|
131
|
+
}, user);
|
|
132
|
+
expect(mockPermissionService.getUserRoles).toHaveBeenCalledWith('user-2', 'branch-1', 'company-1');
|
|
133
|
+
expect(result.data).toEqual([
|
|
134
|
+
{
|
|
135
|
+
roleId: 'role-1'
|
|
136
|
+
}
|
|
137
|
+
]);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
});
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
const _loggedusermock = require("@test-utils/mocks/logged-user.mock");
|
|
6
|
+
const _rolecontroller = require("./role.controller");
|
|
7
|
+
function buildRole(overrides = {}) {
|
|
8
|
+
return {
|
|
9
|
+
id: 'role-1',
|
|
10
|
+
name: 'Admin',
|
|
11
|
+
description: null,
|
|
12
|
+
isActive: true,
|
|
13
|
+
readOnly: false,
|
|
14
|
+
createdAt: new Date(),
|
|
15
|
+
updatedAt: new Date(),
|
|
16
|
+
...overrides
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
describe('RoleController', ()=>{
|
|
20
|
+
let controller;
|
|
21
|
+
let mockRoleService;
|
|
22
|
+
beforeEach(()=>{
|
|
23
|
+
mockRoleService = {
|
|
24
|
+
insert: jest.fn(),
|
|
25
|
+
findById: jest.fn(),
|
|
26
|
+
getAll: jest.fn(),
|
|
27
|
+
update: jest.fn(),
|
|
28
|
+
delete: jest.fn()
|
|
29
|
+
};
|
|
30
|
+
controller = new _rolecontroller.RoleController(mockRoleService);
|
|
31
|
+
});
|
|
32
|
+
it('wires the injected service onto the generated controller base', ()=>{
|
|
33
|
+
expect(controller.roleService).toBe(mockRoleService);
|
|
34
|
+
expect(controller.service).toBe(mockRoleService);
|
|
35
|
+
});
|
|
36
|
+
describe('insert', ()=>{
|
|
37
|
+
it('creates a role and returns the mapped response', async ()=>{
|
|
38
|
+
const user = (0, _loggedusermock.buildMockUser)();
|
|
39
|
+
mockRoleService.insert.mockResolvedValue(buildRole());
|
|
40
|
+
const result = await controller.insert({
|
|
41
|
+
name: 'Admin'
|
|
42
|
+
}, user);
|
|
43
|
+
expect(mockRoleService.insert).toHaveBeenCalledWith({
|
|
44
|
+
name: 'Admin'
|
|
45
|
+
}, user);
|
|
46
|
+
expect(result.success).toBe(true);
|
|
47
|
+
expect(result.data).toEqual(expect.objectContaining({
|
|
48
|
+
id: 'role-1',
|
|
49
|
+
name: 'Admin'
|
|
50
|
+
}));
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
describe('getAll', ()=>{
|
|
54
|
+
it('paginates roles and maps the response list', async ()=>{
|
|
55
|
+
mockRoleService.getAll.mockResolvedValue({
|
|
56
|
+
data: [
|
|
57
|
+
buildRole()
|
|
58
|
+
],
|
|
59
|
+
total: 1
|
|
60
|
+
});
|
|
61
|
+
const result = await controller.getAll({}, (0, _loggedusermock.buildMockUser)(), '');
|
|
62
|
+
expect(mockRoleService.getAll).toHaveBeenCalledWith('', {}, (0, _loggedusermock.buildMockUser)());
|
|
63
|
+
expect(result.data).toHaveLength(1);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
describe('delete', ()=>{
|
|
67
|
+
it('delegates soft-delete to the service', async ()=>{
|
|
68
|
+
mockRoleService.delete.mockResolvedValue({
|
|
69
|
+
count: 1
|
|
70
|
+
});
|
|
71
|
+
const result = await controller.delete({
|
|
72
|
+
id: [
|
|
73
|
+
'role-1'
|
|
74
|
+
],
|
|
75
|
+
type: 'delete'
|
|
76
|
+
}, (0, _loggedusermock.buildMockUser)());
|
|
77
|
+
expect(mockRoleService.delete).toHaveBeenCalledWith({
|
|
78
|
+
id: [
|
|
79
|
+
'role-1'
|
|
80
|
+
],
|
|
81
|
+
type: 'delete'
|
|
82
|
+
}, (0, _loggedusermock.buildMockUser)());
|
|
83
|
+
expect(result.success).toBe(true);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
});
|
|
@@ -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',
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
const _loggedusermock = require("@test-utils/mocks/logged-user.mock");
|
|
6
|
+
const _useractionpermissioncontroller = require("./user-action-permission.controller");
|
|
7
|
+
describe('UserActionPermissionController', ()=>{
|
|
8
|
+
let controller;
|
|
9
|
+
let mockPermissionService;
|
|
10
|
+
let mockConfig;
|
|
11
|
+
beforeEach(()=>{
|
|
12
|
+
mockPermissionService = {
|
|
13
|
+
assignUserActions: jest.fn(),
|
|
14
|
+
getUserActions: jest.fn()
|
|
15
|
+
};
|
|
16
|
+
mockConfig = {};
|
|
17
|
+
controller = new _useractionpermissioncontroller.UserActionPermissionController(mockPermissionService, mockConfig);
|
|
18
|
+
});
|
|
19
|
+
describe('assignUserActions', ()=>{
|
|
20
|
+
it('assigns direct action permissions to a user', async ()=>{
|
|
21
|
+
const user = (0, _loggedusermock.buildMockUser)();
|
|
22
|
+
mockPermissionService.assignUserActions.mockResolvedValue({
|
|
23
|
+
added: 1,
|
|
24
|
+
removed: 0,
|
|
25
|
+
total: 1
|
|
26
|
+
});
|
|
27
|
+
const result = await controller.assignUserActions({
|
|
28
|
+
userId: 'user-2',
|
|
29
|
+
actionIds: [
|
|
30
|
+
'action-1'
|
|
31
|
+
]
|
|
32
|
+
}, user);
|
|
33
|
+
expect(mockPermissionService.assignUserActions).toHaveBeenCalledWith({
|
|
34
|
+
userId: 'user-2',
|
|
35
|
+
actionIds: [
|
|
36
|
+
'action-1'
|
|
37
|
+
]
|
|
38
|
+
});
|
|
39
|
+
expect(result.success).toBe(true);
|
|
40
|
+
expect(result.data).toEqual({
|
|
41
|
+
added: 1,
|
|
42
|
+
removed: 0,
|
|
43
|
+
total: 1
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
describe('getUserActions', ()=>{
|
|
48
|
+
it('returns direct action permissions for a user scoped by branch/company', async ()=>{
|
|
49
|
+
const user = (0, _loggedusermock.buildMockUser)();
|
|
50
|
+
mockPermissionService.getUserActions.mockResolvedValue([
|
|
51
|
+
{
|
|
52
|
+
actionId: 'action-1'
|
|
53
|
+
}
|
|
54
|
+
]);
|
|
55
|
+
const result = await controller.getUserActions({
|
|
56
|
+
userId: 'user-2',
|
|
57
|
+
branchId: 'branch-1',
|
|
58
|
+
companyId: 'company-1'
|
|
59
|
+
}, user);
|
|
60
|
+
expect(mockPermissionService.getUserActions).toHaveBeenCalledWith('user-2', 'company-1', 'branch-1');
|
|
61
|
+
expect(result.data).toEqual([
|
|
62
|
+
{
|
|
63
|
+
actionId: 'action-1'
|
|
64
|
+
}
|
|
65
|
+
]);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
});
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
const _iamswaggerconfig = require("./iam-swagger.config");
|
|
6
|
+
const _permissiontypeenum = require("../enums/permission-type.enum");
|
|
7
|
+
describe('iamSwaggerConfig', ()=>{
|
|
8
|
+
it('should default to company disabled, FULL mode, single database', ()=>{
|
|
9
|
+
const config = (0, _iamswaggerconfig.iamSwaggerConfig)();
|
|
10
|
+
expect(config.title).toBe('IAM API');
|
|
11
|
+
expect(config.path).toBe('api/docs/iam');
|
|
12
|
+
expect(config.bearerAuth).toBe(true);
|
|
13
|
+
// enableCompanyFeature defaults to false, so company-scoped exclusions apply
|
|
14
|
+
expect(config.excludeSchemaProperties?.length).toBeGreaterThan(0);
|
|
15
|
+
expect(config.excludeQueryParameters?.length).toBeGreaterThan(0);
|
|
16
|
+
expect(config.description).not.toContain('Multi-Tenant Mode');
|
|
17
|
+
});
|
|
18
|
+
it('should always exclude auth-related tags regardless of configuration', ()=>{
|
|
19
|
+
const config = (0, _iamswaggerconfig.iamSwaggerConfig)(true, _permissiontypeenum.IAMPermissionMode.FULL);
|
|
20
|
+
expect(config.excludeTags).toEqual(expect.arrayContaining([
|
|
21
|
+
'Authentication',
|
|
22
|
+
'Users',
|
|
23
|
+
'Companies',
|
|
24
|
+
'Branches'
|
|
25
|
+
]));
|
|
26
|
+
});
|
|
27
|
+
it('should exclude company-scoped schema properties and query params when company feature is off', ()=>{
|
|
28
|
+
const config = (0, _iamswaggerconfig.iamSwaggerConfig)(false);
|
|
29
|
+
expect(config.excludeSchemaProperties).toEqual(expect.arrayContaining([
|
|
30
|
+
expect.objectContaining({
|
|
31
|
+
schemaName: 'AssignUserActionsDto',
|
|
32
|
+
properties: [
|
|
33
|
+
'companyId',
|
|
34
|
+
'branchId'
|
|
35
|
+
]
|
|
36
|
+
})
|
|
37
|
+
]));
|
|
38
|
+
expect(config.excludeQueryParameters).toEqual(expect.arrayContaining([
|
|
39
|
+
expect.objectContaining({
|
|
40
|
+
pathPattern: '/iam/permissions/user-actions/*',
|
|
41
|
+
method: 'get'
|
|
42
|
+
})
|
|
43
|
+
]));
|
|
44
|
+
expect(config.excludeTags).toContain('IAM - Company Action Permissions');
|
|
45
|
+
});
|
|
46
|
+
it('should not exclude company-scoped schema properties, query params, or tags when company feature is on', ()=>{
|
|
47
|
+
const config = (0, _iamswaggerconfig.iamSwaggerConfig)(true);
|
|
48
|
+
expect(config.excludeSchemaProperties).toEqual([]);
|
|
49
|
+
expect(config.excludeQueryParameters).toEqual([]);
|
|
50
|
+
expect(config.excludeTags).not.toContain('IAM - Company Action Permissions');
|
|
51
|
+
});
|
|
52
|
+
it('should hide DIRECT-mode tags in RBAC mode', ()=>{
|
|
53
|
+
const config = (0, _iamswaggerconfig.iamSwaggerConfig)(false, _permissiontypeenum.IAMPermissionMode.RBAC);
|
|
54
|
+
expect(config.excludeTags).toContain('IAM - Permissions (Direct)');
|
|
55
|
+
expect(config.excludeTags).not.toContain('IAM - Permissions (RBAC)');
|
|
56
|
+
expect(config.excludeTags).not.toContain('IAM - Roles');
|
|
57
|
+
});
|
|
58
|
+
it('should hide RBAC-mode tags (including Roles) in DIRECT mode', ()=>{
|
|
59
|
+
const config = (0, _iamswaggerconfig.iamSwaggerConfig)(false, _permissiontypeenum.IAMPermissionMode.DIRECT);
|
|
60
|
+
expect(config.excludeTags).toContain('IAM - Permissions (RBAC)');
|
|
61
|
+
expect(config.excludeTags).toContain('IAM - Roles');
|
|
62
|
+
expect(config.excludeTags).not.toContain('IAM - Permissions (Direct)');
|
|
63
|
+
});
|
|
64
|
+
it('should hide neither RBAC nor DIRECT tags in FULL mode', ()=>{
|
|
65
|
+
const config = (0, _iamswaggerconfig.iamSwaggerConfig)(false, _permissiontypeenum.IAMPermissionMode.FULL);
|
|
66
|
+
expect(config.excludeTags).not.toContain('IAM - Permissions (RBAC)');
|
|
67
|
+
expect(config.excludeTags).not.toContain('IAM - Permissions (Direct)');
|
|
68
|
+
expect(config.excludeTags).not.toContain('IAM - Roles');
|
|
69
|
+
});
|
|
70
|
+
it('should mention multi-tenant mode in the description only for multi-tenant database mode', ()=>{
|
|
71
|
+
const multiTenant = (0, _iamswaggerconfig.iamSwaggerConfig)(false, _permissiontypeenum.IAMPermissionMode.FULL, 'multi-tenant');
|
|
72
|
+
const single = (0, _iamswaggerconfig.iamSwaggerConfig)(false, _permissiontypeenum.IAMPermissionMode.FULL, 'single');
|
|
73
|
+
expect(multiTenant.description).toContain('Multi-Tenant Mode');
|
|
74
|
+
expect(single.description).not.toContain('Multi-Tenant Mode');
|
|
75
|
+
});
|
|
76
|
+
it('should describe RBAC as active and DIRECT as disabled in the endpoints summary for RBAC mode', ()=>{
|
|
77
|
+
const config = (0, _iamswaggerconfig.iamSwaggerConfig)(false, _permissiontypeenum.IAMPermissionMode.RBAC);
|
|
78
|
+
expect(config.description).toContain('**Roles**: CRUD operations');
|
|
79
|
+
expect(config.description).toContain('❌ **User-Actions**: Disabled (DIRECT mode not active)');
|
|
80
|
+
});
|
|
81
|
+
it('should describe DIRECT as active and Roles as disabled in the endpoints summary for DIRECT mode', ()=>{
|
|
82
|
+
const config = (0, _iamswaggerconfig.iamSwaggerConfig)(false, _permissiontypeenum.IAMPermissionMode.DIRECT);
|
|
83
|
+
expect(config.description).toContain('✅ **User-Actions**: Direct action assignment to users');
|
|
84
|
+
expect(config.description).toContain('❌ **Roles**: Disabled (RBAC mode not active)');
|
|
85
|
+
});
|
|
86
|
+
it('should describe company features as active only when company feature is enabled', ()=>{
|
|
87
|
+
const withCompany = (0, _iamswaggerconfig.iamSwaggerConfig)(true, _permissiontypeenum.IAMPermissionMode.FULL);
|
|
88
|
+
const withoutCompany = (0, _iamswaggerconfig.iamSwaggerConfig)(false, _permissiontypeenum.IAMPermissionMode.FULL);
|
|
89
|
+
expect(withCompany.description).toContain('Company Features (Active)');
|
|
90
|
+
expect(withCompany.description).toContain('✅ **Company-Actions**: Whitelist actions for companies');
|
|
91
|
+
expect(withoutCompany.description).not.toContain('Company Features (Active)');
|
|
92
|
+
expect(withoutCompany.description).toContain('❌ **Company-Actions**: Disabled (company feature not enabled)');
|
|
93
|
+
});
|
|
94
|
+
it('should describe the current permission mode label correctly for each mode', ()=>{
|
|
95
|
+
expect((0, _iamswaggerconfig.iamSwaggerConfig)(false, _permissiontypeenum.IAMPermissionMode.RBAC).description).toContain('**RBAC** (Role-Based Access Control)');
|
|
96
|
+
expect((0, _iamswaggerconfig.iamSwaggerConfig)(false, _permissiontypeenum.IAMPermissionMode.DIRECT).description).toContain('**DIRECT** (Direct User Permissions)');
|
|
97
|
+
expect((0, _iamswaggerconfig.iamSwaggerConfig)(false, _permissiontypeenum.IAMPermissionMode.FULL).description).toContain('**FULL** (RBAC + Direct)');
|
|
98
|
+
});
|
|
99
|
+
});
|
|
@@ -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)'
|