@tomei/sso 0.66.1-test.2 → 0.66.1-test.4
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/__tests__/unit/components/group-reporting-user/group-reporting-user.spec.ts +12 -8
- package/__tests__/unit/components/login-user/login.spec.ts +310 -72
- package/__tests__/unit/components/login-user/password-expiry.spec.ts +87 -0
- package/dist/__tests__/unit/components/group-reporting-user/group-reporting-user.spec.js +6 -2
- package/dist/__tests__/unit/components/group-reporting-user/group-reporting-user.spec.js.map +1 -1
- package/dist/__tests__/unit/components/login-user/login.spec.js +227 -69
- package/dist/__tests__/unit/components/login-user/login.spec.js.map +1 -1
- package/dist/__tests__/unit/components/login-user/password-expiry.spec.d.ts +1 -0
- package/dist/__tests__/unit/components/login-user/password-expiry.spec.js +86 -0
- package/dist/__tests__/unit/components/login-user/password-expiry.spec.js.map +1 -0
- package/dist/src/components/login-user/interfaces/user-info.interface.d.ts +1 -0
- package/dist/src/components/login-user/login-user.d.ts +1 -0
- package/dist/src/components/login-user/login-user.js +85 -14
- package/dist/src/components/login-user/login-user.js.map +1 -1
- package/dist/src/components/login-user/user.d.ts +1 -0
- package/dist/src/components/login-user/user.js +7 -1
- package/dist/src/components/login-user/user.js.map +1 -1
- package/dist/src/components/system/system.d.ts +1 -0
- package/dist/src/components/system/system.js +1 -0
- package/dist/src/components/system/system.js.map +1 -1
- package/dist/src/components/system-privilege/system-privilege.d.ts +1 -0
- package/dist/src/components/system-privilege/system-privilege.js +2 -0
- package/dist/src/components/system-privilege/system-privilege.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/interfaces/system-privilege.interface.d.ts +1 -0
- package/dist/src/interfaces/system.interface.d.ts +1 -0
- package/dist/src/models/group.entity.d.ts +1 -0
- package/dist/src/models/group.entity.js +8 -1
- package/dist/src/models/group.entity.js.map +1 -1
- package/dist/src/models/system-privilege.entity.d.ts +1 -0
- package/dist/src/models/system-privilege.entity.js +7 -0
- package/dist/src/models/system-privilege.entity.js.map +1 -1
- package/dist/src/models/system.entity.d.ts +1 -0
- package/dist/src/models/system.entity.js +7 -0
- package/dist/src/models/system.entity.js.map +1 -1
- package/dist/src/models/user.entity.d.ts +1 -0
- package/dist/src/models/user.entity.js +7 -0
- package/dist/src/models/user.entity.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -1
- package/src/components/login-user/interfaces/user-info.interface.ts +1 -0
- package/src/components/login-user/login-user.ts +125 -17
- package/src/components/login-user/user.ts +14 -2
- package/src/components/system/system.ts +2 -0
- package/src/components/system-privilege/system-privilege.ts +3 -0
- package/src/index.ts +1 -0
- package/src/interfaces/system-privilege.interface.ts +1 -0
- package/src/interfaces/system.interface.ts +1 -0
- package/src/models/group.entity.ts +7 -1
- package/src/models/system-privilege.entity.ts +6 -0
- package/src/models/system.entity.ts +6 -0
- package/src/models/user.entity.ts +6 -0
- package/coverage/cobertura-coverage.xml +0 -6837
- package/coverage/test-report.xml +0 -161
|
@@ -27,12 +27,12 @@ describe('GroupReportingUser', () => {
|
|
|
27
27
|
jest.clearAllMocks();
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
-
|
|
31
30
|
describe('init', () => {
|
|
32
31
|
const mockDbTransaction = {};
|
|
33
32
|
|
|
34
33
|
it('should initialize GroupReportingUser without GroupReportingUserId', async () => {
|
|
35
|
-
const initGroupReportingUser =
|
|
34
|
+
const initGroupReportingUser =
|
|
35
|
+
await GroupReportingUser.init(mockDbTransaction);
|
|
36
36
|
expect(initGroupReportingUser).toBeDefined();
|
|
37
37
|
expect(initGroupReportingUser).toBeInstanceOf(GroupReportingUser);
|
|
38
38
|
});
|
|
@@ -47,7 +47,7 @@ describe('GroupReportingUser', () => {
|
|
|
47
47
|
|
|
48
48
|
const initGroupReportingUser = await GroupReportingUser.init(
|
|
49
49
|
mockDbTransaction,
|
|
50
|
-
'1'
|
|
50
|
+
'1',
|
|
51
51
|
);
|
|
52
52
|
expect(initGroupReportingUser).toBeDefined();
|
|
53
53
|
expect(initGroupReportingUser).toBeInstanceOf(GroupReportingUser);
|
|
@@ -57,19 +57,23 @@ describe('GroupReportingUser', () => {
|
|
|
57
57
|
});
|
|
58
58
|
|
|
59
59
|
it('should throw ClassError when GroupReportingUser is not found', async () => {
|
|
60
|
-
jest
|
|
60
|
+
jest
|
|
61
|
+
.spyOn(GroupReportingUserRepository.prototype, 'findByPk')
|
|
62
|
+
.mockResolvedValue(null);
|
|
61
63
|
|
|
62
64
|
await expect(
|
|
63
|
-
GroupReportingUser.init(mockDbTransaction, '1')
|
|
65
|
+
GroupReportingUser.init(mockDbTransaction, '1'),
|
|
64
66
|
).rejects.toThrow(ClassError);
|
|
65
67
|
});
|
|
66
68
|
|
|
67
69
|
it('should throw ClassError when failed to initialize GroupReportingUser', async () => {
|
|
68
|
-
jest
|
|
70
|
+
jest
|
|
71
|
+
.spyOn(GroupReportingUserRepository.prototype, 'findByPk')
|
|
72
|
+
.mockRejectedValue(new Error());
|
|
69
73
|
|
|
70
74
|
await expect(
|
|
71
|
-
GroupReportingUser.init(mockDbTransaction, '1')
|
|
75
|
+
GroupReportingUser.init(mockDbTransaction, '1'),
|
|
72
76
|
).rejects.toThrow(Error);
|
|
73
77
|
});
|
|
74
78
|
});
|
|
75
|
-
});
|
|
79
|
+
});
|
|
@@ -9,6 +9,7 @@ import { YN } from '../../../../src/enum/yn.enum';
|
|
|
9
9
|
import SystemPrivilegeModel from '../../../../src/models/system-privilege.entity';
|
|
10
10
|
import { GroupSystemAccessRepository } from '../../../../src/components/group-system-access/group-system-access.repository';
|
|
11
11
|
import { GroupRepository } from '../../../../src/components/group/group.repository';
|
|
12
|
+
import axios from 'axios';
|
|
12
13
|
|
|
13
14
|
const mockUserData = {
|
|
14
15
|
UserId: 1,
|
|
@@ -58,11 +59,23 @@ describe('LoginUser', () => {
|
|
|
58
59
|
let sessionService: any;
|
|
59
60
|
let userId: number;
|
|
60
61
|
let dbTransaction: any;
|
|
62
|
+
const mockLoginApiEndpoint = {
|
|
63
|
+
baseUrl: 'http://sso-api',
|
|
64
|
+
getProfile: '/get-profile',
|
|
65
|
+
apiKey: 'test-api-key',
|
|
66
|
+
};
|
|
61
67
|
|
|
62
68
|
beforeEach(() => {
|
|
63
69
|
sessionService = {};
|
|
64
70
|
userId = 1;
|
|
65
71
|
dbTransaction = null;
|
|
72
|
+
jest
|
|
73
|
+
.spyOn(ComponentConfig, 'getComponentConfigValue')
|
|
74
|
+
.mockImplementation((componentName: string, configKey: string) =>
|
|
75
|
+
configKey === 'useLoginApiYN'
|
|
76
|
+
? ('Y' as any)
|
|
77
|
+
: (mockLoginApiEndpoint as any),
|
|
78
|
+
);
|
|
66
79
|
});
|
|
67
80
|
|
|
68
81
|
it('should initialize LoginUser with valid userId', async () => {
|
|
@@ -93,9 +106,9 @@ describe('LoginUser', () => {
|
|
|
93
106
|
},
|
|
94
107
|
};
|
|
95
108
|
|
|
96
|
-
const
|
|
97
|
-
.spyOn(
|
|
98
|
-
.mockResolvedValueOnce(user as any);
|
|
109
|
+
const axiosPostMock = jest
|
|
110
|
+
.spyOn(axios, 'post')
|
|
111
|
+
.mockResolvedValueOnce({ data: { loginUser: user } } as any);
|
|
99
112
|
|
|
100
113
|
const result = await LoginUser.init(
|
|
101
114
|
sessionService,
|
|
@@ -103,63 +116,58 @@ describe('LoginUser', () => {
|
|
|
103
116
|
dbTransaction,
|
|
104
117
|
);
|
|
105
118
|
|
|
106
|
-
expect(
|
|
107
|
-
expect(
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
},
|
|
111
|
-
|
|
112
|
-
{
|
|
113
|
-
model: expect.anything(),
|
|
114
|
-
},
|
|
115
|
-
],
|
|
116
|
-
transaction: dbTransaction,
|
|
117
|
-
});
|
|
119
|
+
expect(axiosPostMock).toHaveBeenCalledTimes(1);
|
|
120
|
+
expect(axiosPostMock).toHaveBeenCalledWith(
|
|
121
|
+
`${mockLoginApiEndpoint.baseUrl}${mockLoginApiEndpoint.getProfile}`,
|
|
122
|
+
{ UserId: userId },
|
|
123
|
+
{ headers: { 'Content-Type': 'application/json', 'x-api-key': mockLoginApiEndpoint.apiKey } },
|
|
124
|
+
);
|
|
118
125
|
expect(result).toBeInstanceOf(LoginUser);
|
|
119
126
|
expect(result.UserId).toBe(user.UserId);
|
|
120
127
|
expect(result.FullName).toBe(user.FullName);
|
|
121
128
|
expect(result.Email).toBe(user.Email);
|
|
122
|
-
expect(result.Password).toBe(user.Password);
|
|
123
129
|
expect(result.Status).toBe(user.Status);
|
|
124
|
-
expect(result.DefaultPasswordChangedYN).toBe(
|
|
125
|
-
user.DefaultPasswordChangedYN,
|
|
126
|
-
);
|
|
127
|
-
expect(result.FirstLoginAt).toBe(user.FirstLoginAt);
|
|
128
|
-
expect(result.LastLoginAt).toBe(user.LastLoginAt);
|
|
129
|
-
expect(result.MFAEnabled).toBe(user.MFAEnabled);
|
|
130
|
-
expect(result.MFAConfig).toBe(user.MFAConfig);
|
|
131
|
-
expect(result.RecoveryEmail).toBe(user.RecoveryEmail);
|
|
132
|
-
expect(result.FailedLoginAttemptCount).toBe(user.FailedLoginAttemptCount);
|
|
133
|
-
expect(result.LastFailedLoginAt).toBe(user.LastFailedLoginAt);
|
|
134
|
-
expect(result.LastPasswordChangedAt).toBe(user.LastPasswordChangedAt);
|
|
135
|
-
expect(result.NeedToChangePasswordYN).toBe(user.NeedToChangePasswordYN);
|
|
136
|
-
expect(result.CreatedById).toBe(user.CreatedById);
|
|
137
|
-
expect(result.CreatedAt).toBe(user.CreatedAt);
|
|
138
|
-
expect(result.UpdatedById).toBe(user.UpdatedById);
|
|
139
|
-
expect(result.UpdatedAt).toBe(user.UpdatedAt);
|
|
140
130
|
});
|
|
141
131
|
|
|
142
132
|
it('should throw an error when user is not found', async () => {
|
|
143
|
-
const
|
|
144
|
-
.spyOn(
|
|
145
|
-
.mockResolvedValueOnce(null);
|
|
133
|
+
const axiosPostMock = jest
|
|
134
|
+
.spyOn(axios, 'post')
|
|
135
|
+
.mockResolvedValueOnce({ data: { loginUser: null } } as any);
|
|
146
136
|
|
|
147
137
|
await expect(
|
|
148
138
|
LoginUser.init(sessionService, userId, dbTransaction),
|
|
149
139
|
).rejects.toThrow(Error);
|
|
150
140
|
|
|
151
|
-
expect(
|
|
141
|
+
expect(axiosPostMock).toHaveBeenCalledTimes(1);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('should initialize LoginUser via direct db query when useLoginApi is false', async () => {
|
|
145
|
+
jest
|
|
146
|
+
.spyOn(ComponentConfig, 'getComponentConfigValue')
|
|
147
|
+
.mockImplementation((componentName: string, configKey: string) => {
|
|
148
|
+
if (configKey === 'useLoginApiYN') return 'N' as any;
|
|
149
|
+
return mockLoginApiEndpoint as any;
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
const findOneMock = jest
|
|
153
|
+
.spyOn(LoginUser['_Repository'], 'findOne')
|
|
154
|
+
.mockResolvedValueOnce(mockUserData as any);
|
|
155
|
+
const axiosPostMock = jest.spyOn(axios, 'post');
|
|
156
|
+
|
|
157
|
+
const result = await LoginUser.init(
|
|
158
|
+
sessionService,
|
|
159
|
+
userId,
|
|
160
|
+
dbTransaction,
|
|
161
|
+
);
|
|
162
|
+
|
|
152
163
|
expect(findOneMock).toHaveBeenCalledWith({
|
|
153
|
-
where: {
|
|
154
|
-
|
|
155
|
-
},
|
|
156
|
-
include: [
|
|
157
|
-
{
|
|
158
|
-
model: expect.anything(),
|
|
159
|
-
},
|
|
160
|
-
],
|
|
164
|
+
where: { UserId: userId },
|
|
165
|
+
include: [{ model: expect.anything() }],
|
|
161
166
|
transaction: dbTransaction,
|
|
162
167
|
});
|
|
168
|
+
expect(axiosPostMock).not.toHaveBeenCalled();
|
|
169
|
+
expect(result).toBeInstanceOf(LoginUser);
|
|
170
|
+
expect(result.Email).toBe(mockUserData.Email);
|
|
163
171
|
});
|
|
164
172
|
});
|
|
165
173
|
|
|
@@ -889,10 +897,23 @@ describe('LoginUser', () => {
|
|
|
889
897
|
});
|
|
890
898
|
|
|
891
899
|
describe('checkPrivileges', () => {
|
|
900
|
+
const mockLoginApiEndpoint = {
|
|
901
|
+
baseUrl: 'http://sso-api',
|
|
902
|
+
checkPrivileges: '/check-privileges',
|
|
903
|
+
apiKey: 'test-api-key',
|
|
904
|
+
};
|
|
905
|
+
|
|
892
906
|
beforeEach(() => {
|
|
893
907
|
jest
|
|
894
908
|
.spyOn(ApplicationConfig, 'getComponentConfigValue')
|
|
895
909
|
.mockReturnValue('test-session' as any);
|
|
910
|
+
jest
|
|
911
|
+
.spyOn(ComponentConfig, 'getComponentConfigValue')
|
|
912
|
+
.mockImplementation((componentName: string, configKey: string) =>
|
|
913
|
+
configKey === 'useLoginApiYN'
|
|
914
|
+
? ('Y' as any)
|
|
915
|
+
: (mockLoginApiEndpoint as any),
|
|
916
|
+
);
|
|
896
917
|
});
|
|
897
918
|
|
|
898
919
|
it('should return true if user has the specified privilege', async () => {
|
|
@@ -903,19 +924,10 @@ describe('LoginUser', () => {
|
|
|
903
924
|
const loginUser = await LoginUser.init(sessionService);
|
|
904
925
|
loginUser.ObjectId = '1';
|
|
905
926
|
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
.
|
|
909
|
-
|
|
910
|
-
systemLogins: [
|
|
911
|
-
{
|
|
912
|
-
id: '1',
|
|
913
|
-
sessionId: 'sessionId',
|
|
914
|
-
code: systemCode,
|
|
915
|
-
privileges: [privilegeName],
|
|
916
|
-
},
|
|
917
|
-
],
|
|
918
|
-
});
|
|
927
|
+
const axiosPostMock = jest
|
|
928
|
+
.spyOn(axios, 'post')
|
|
929
|
+
.mockResolvedValueOnce({ data: { hasPrivilege: true } } as any);
|
|
930
|
+
|
|
919
931
|
// Act
|
|
920
932
|
const hasPrivilege = await loginUser.checkPrivileges(
|
|
921
933
|
systemCode,
|
|
@@ -924,7 +936,11 @@ describe('LoginUser', () => {
|
|
|
924
936
|
|
|
925
937
|
// Assert
|
|
926
938
|
expect(hasPrivilege).toBe(true);
|
|
927
|
-
expect(
|
|
939
|
+
expect(axiosPostMock).toHaveBeenCalledWith(
|
|
940
|
+
`${mockLoginApiEndpoint.baseUrl}${mockLoginApiEndpoint.checkPrivileges}`,
|
|
941
|
+
{ SystemCode: systemCode, SessionName: 'test-session', PrivilegeName: privilegeName, UserId: '1' },
|
|
942
|
+
{ headers: { 'Content-Type': 'application/json', 'x-api-key': mockLoginApiEndpoint.apiKey } },
|
|
943
|
+
);
|
|
928
944
|
});
|
|
929
945
|
|
|
930
946
|
it('should return false if user does not have the specified privilege', async () => {
|
|
@@ -935,19 +951,10 @@ describe('LoginUser', () => {
|
|
|
935
951
|
const loginUser = await LoginUser.init(sessionService);
|
|
936
952
|
loginUser.ObjectId = '1';
|
|
937
953
|
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
.
|
|
941
|
-
|
|
942
|
-
systemLogins: [
|
|
943
|
-
{
|
|
944
|
-
id: '1',
|
|
945
|
-
sessionId: 'sessionId',
|
|
946
|
-
code: systemCode,
|
|
947
|
-
privileges: [],
|
|
948
|
-
},
|
|
949
|
-
],
|
|
950
|
-
});
|
|
954
|
+
const axiosPostMock = jest
|
|
955
|
+
.spyOn(axios, 'post')
|
|
956
|
+
.mockResolvedValueOnce({ data: { hasPrivilege: false } } as any);
|
|
957
|
+
|
|
951
958
|
// Act
|
|
952
959
|
const hasPrivilege = await loginUser.checkPrivileges(
|
|
953
960
|
systemCode,
|
|
@@ -956,7 +963,6 @@ describe('LoginUser', () => {
|
|
|
956
963
|
|
|
957
964
|
// Assert
|
|
958
965
|
expect(hasPrivilege).toBe(false);
|
|
959
|
-
expect(rus).toHaveBeenCalledWith(loginUser.ObjectId, 'test-session');
|
|
960
966
|
});
|
|
961
967
|
|
|
962
968
|
it('should throw an error if ObjectId is not set', async () => {
|
|
@@ -972,6 +978,238 @@ describe('LoginUser', () => {
|
|
|
972
978
|
loginUser.checkPrivileges(systemCode, privilegeName),
|
|
973
979
|
).rejects.toThrow();
|
|
974
980
|
});
|
|
981
|
+
|
|
982
|
+
it('should check the session privileges directly when useLoginApi is false', async () => {
|
|
983
|
+
const systemCode = 'SS';
|
|
984
|
+
const privilegeName = 'Privilege 1';
|
|
985
|
+
jest
|
|
986
|
+
.spyOn(ComponentConfig, 'getComponentConfigValue')
|
|
987
|
+
.mockImplementation((componentName: string, configKey: string) => {
|
|
988
|
+
if (configKey === 'useLoginApiYN') return 'N' as any;
|
|
989
|
+
return mockLoginApiEndpoint as any;
|
|
990
|
+
});
|
|
991
|
+
|
|
992
|
+
const sessionService = await SessionService.init();
|
|
993
|
+
const loginUser = await LoginUser.init(sessionService);
|
|
994
|
+
loginUser.ObjectId = '1';
|
|
995
|
+
loginUser['_SessionService'].retrieveUserSession = jest
|
|
996
|
+
.fn()
|
|
997
|
+
.mockResolvedValue({
|
|
998
|
+
systemLogins: [{ code: systemCode, privileges: [privilegeName] }],
|
|
999
|
+
});
|
|
1000
|
+
|
|
1001
|
+
const axiosPostMock = jest.spyOn(axios, 'post');
|
|
1002
|
+
|
|
1003
|
+
const hasPrivilege = await loginUser.checkPrivileges(
|
|
1004
|
+
systemCode,
|
|
1005
|
+
privilegeName,
|
|
1006
|
+
);
|
|
1007
|
+
|
|
1008
|
+
expect(hasPrivilege).toBe(true);
|
|
1009
|
+
expect(axiosPostMock).not.toHaveBeenCalled();
|
|
1010
|
+
});
|
|
1011
|
+
});
|
|
1012
|
+
|
|
1013
|
+
describe('checkSession', () => {
|
|
1014
|
+
const mockLoginApiEndpoint = {
|
|
1015
|
+
baseUrl: 'http://sso-api',
|
|
1016
|
+
checkSession: '/check-session',
|
|
1017
|
+
apiKey: 'test-api-key',
|
|
1018
|
+
};
|
|
1019
|
+
|
|
1020
|
+
beforeEach(() => {
|
|
1021
|
+
jest
|
|
1022
|
+
.spyOn(ApplicationConfig, 'getComponentConfigValue')
|
|
1023
|
+
.mockReturnValue('test-session' as any);
|
|
1024
|
+
});
|
|
1025
|
+
|
|
1026
|
+
it('should call the login-api endpoint when useLoginApi is true', async () => {
|
|
1027
|
+
jest
|
|
1028
|
+
.spyOn(ComponentConfig, 'getComponentConfigValue')
|
|
1029
|
+
.mockImplementation((componentName: string, configKey: string) =>
|
|
1030
|
+
configKey === 'useLoginApiYN'
|
|
1031
|
+
? ('Y' as any)
|
|
1032
|
+
: (mockLoginApiEndpoint as any),
|
|
1033
|
+
);
|
|
1034
|
+
|
|
1035
|
+
const systemCode = 'SS';
|
|
1036
|
+
const sessionId = 'session-1';
|
|
1037
|
+
const userId = '1';
|
|
1038
|
+
const sessionService = await SessionService.init();
|
|
1039
|
+
const loginUser = await LoginUser.init(sessionService);
|
|
1040
|
+
|
|
1041
|
+
const mockSystemLogin = { code: systemCode, sessionId };
|
|
1042
|
+
const axiosPostMock = jest
|
|
1043
|
+
.spyOn(axios, 'post')
|
|
1044
|
+
.mockResolvedValueOnce({
|
|
1045
|
+
data: { systemLogin: mockSystemLogin },
|
|
1046
|
+
} as any);
|
|
1047
|
+
|
|
1048
|
+
const systemLogin = await loginUser.checkSession(
|
|
1049
|
+
systemCode,
|
|
1050
|
+
sessionId,
|
|
1051
|
+
userId,
|
|
1052
|
+
);
|
|
1053
|
+
|
|
1054
|
+
expect(systemLogin).toEqual(mockSystemLogin);
|
|
1055
|
+
expect(axiosPostMock).toHaveBeenCalledWith(
|
|
1056
|
+
`${mockLoginApiEndpoint.baseUrl}${mockLoginApiEndpoint.checkSession}`,
|
|
1057
|
+
{
|
|
1058
|
+
SystemCode: systemCode,
|
|
1059
|
+
SessionName: 'test-session',
|
|
1060
|
+
SessionId: sessionId,
|
|
1061
|
+
UserId: userId,
|
|
1062
|
+
},
|
|
1063
|
+
{
|
|
1064
|
+
headers: {
|
|
1065
|
+
'Content-Type': 'application/json',
|
|
1066
|
+
'x-api-key': mockLoginApiEndpoint.apiKey,
|
|
1067
|
+
},
|
|
1068
|
+
},
|
|
1069
|
+
);
|
|
1070
|
+
});
|
|
1071
|
+
|
|
1072
|
+
it('should validate the session directly against redis when useLoginApi is false', async () => {
|
|
1073
|
+
jest
|
|
1074
|
+
.spyOn(ComponentConfig, 'getComponentConfigValue')
|
|
1075
|
+
.mockImplementation((componentName: string, configKey: string) => {
|
|
1076
|
+
if (configKey === 'useLoginApiYN') return 'N' as any;
|
|
1077
|
+
return mockLoginApiEndpoint as any;
|
|
1078
|
+
});
|
|
1079
|
+
|
|
1080
|
+
const systemCode = 'SS';
|
|
1081
|
+
const sessionId = 'session-1';
|
|
1082
|
+
const userId = '1';
|
|
1083
|
+
const sessionService = await SessionService.init();
|
|
1084
|
+
const loginUser = await LoginUser.init(sessionService);
|
|
1085
|
+
|
|
1086
|
+
const mockSystemLogin = { code: systemCode, sessionId };
|
|
1087
|
+
loginUser['_SessionService'].retrieveUserSession = jest
|
|
1088
|
+
.fn()
|
|
1089
|
+
.mockResolvedValue({ systemLogins: [mockSystemLogin] });
|
|
1090
|
+
loginUser['_SessionService'].refreshDuration = jest.fn();
|
|
1091
|
+
|
|
1092
|
+
const axiosPostMock = jest.spyOn(axios, 'post');
|
|
1093
|
+
|
|
1094
|
+
const systemLogin = await loginUser.checkSession(
|
|
1095
|
+
systemCode,
|
|
1096
|
+
sessionId,
|
|
1097
|
+
userId,
|
|
1098
|
+
);
|
|
1099
|
+
|
|
1100
|
+
expect(systemLogin).toEqual(mockSystemLogin);
|
|
1101
|
+
expect(
|
|
1102
|
+
loginUser['_SessionService'].refreshDuration,
|
|
1103
|
+
).toHaveBeenCalledWith(userId, 'test-session');
|
|
1104
|
+
expect(axiosPostMock).not.toHaveBeenCalled();
|
|
1105
|
+
});
|
|
1106
|
+
|
|
1107
|
+
it('should throw "Session expired." when useLoginApi is false and the session is stale', async () => {
|
|
1108
|
+
jest
|
|
1109
|
+
.spyOn(ComponentConfig, 'getComponentConfigValue')
|
|
1110
|
+
.mockImplementation((componentName: string, configKey: string) => {
|
|
1111
|
+
if (configKey === 'useLoginApiYN') return 'N' as any;
|
|
1112
|
+
return mockLoginApiEndpoint as any;
|
|
1113
|
+
});
|
|
1114
|
+
|
|
1115
|
+
const sessionService = await SessionService.init();
|
|
1116
|
+
const loginUser = await LoginUser.init(sessionService);
|
|
1117
|
+
loginUser['_SessionService'].retrieveUserSession = jest
|
|
1118
|
+
.fn()
|
|
1119
|
+
.mockResolvedValue({ systemLogins: [] });
|
|
1120
|
+
|
|
1121
|
+
await expect(
|
|
1122
|
+
loginUser.checkSession('SS', 'session-1', '1'),
|
|
1123
|
+
).rejects.toThrow('Session expired.');
|
|
1124
|
+
});
|
|
1125
|
+
});
|
|
1126
|
+
|
|
1127
|
+
describe('setSession', () => {
|
|
1128
|
+
const mockLoginApiEndpoint = {
|
|
1129
|
+
baseUrl: 'http://sso-api',
|
|
1130
|
+
setSession: '/set-session',
|
|
1131
|
+
apiKey: 'test-api-key',
|
|
1132
|
+
};
|
|
1133
|
+
|
|
1134
|
+
beforeEach(() => {
|
|
1135
|
+
jest
|
|
1136
|
+
.spyOn(ApplicationConfig, 'getComponentConfigValue')
|
|
1137
|
+
.mockReturnValue('test-session' as any);
|
|
1138
|
+
});
|
|
1139
|
+
|
|
1140
|
+
it('should call the login-api endpoint when useLoginApi is true', async () => {
|
|
1141
|
+
jest
|
|
1142
|
+
.spyOn(ComponentConfig, 'getComponentConfigValue')
|
|
1143
|
+
.mockImplementation((componentName: string, configKey: string) =>
|
|
1144
|
+
configKey === 'useLoginApiYN'
|
|
1145
|
+
? ('Y' as any)
|
|
1146
|
+
: (mockLoginApiEndpoint as any),
|
|
1147
|
+
);
|
|
1148
|
+
|
|
1149
|
+
const systemCode = 'SS';
|
|
1150
|
+
const sessionId = 'session-1';
|
|
1151
|
+
const sessionService = await SessionService.init();
|
|
1152
|
+
const loginUser = await LoginUser.init(sessionService);
|
|
1153
|
+
loginUser.ObjectId = '1';
|
|
1154
|
+
|
|
1155
|
+
const axiosPostMock = jest
|
|
1156
|
+
.spyOn(axios, 'post')
|
|
1157
|
+
.mockResolvedValueOnce({ data: {} } as any);
|
|
1158
|
+
|
|
1159
|
+
await loginUser.setSession(systemCode, sessionId, null);
|
|
1160
|
+
|
|
1161
|
+
expect(axiosPostMock).toHaveBeenCalledWith(
|
|
1162
|
+
`${mockLoginApiEndpoint.baseUrl}${mockLoginApiEndpoint.setSession}`,
|
|
1163
|
+
{
|
|
1164
|
+
SystemCode: systemCode,
|
|
1165
|
+
SessionName: 'test-session',
|
|
1166
|
+
SessionId: sessionId,
|
|
1167
|
+
UserId: '1',
|
|
1168
|
+
},
|
|
1169
|
+
{
|
|
1170
|
+
headers: {
|
|
1171
|
+
'Content-Type': 'application/json',
|
|
1172
|
+
'x-api-key': mockLoginApiEndpoint.apiKey,
|
|
1173
|
+
},
|
|
1174
|
+
},
|
|
1175
|
+
);
|
|
1176
|
+
});
|
|
1177
|
+
|
|
1178
|
+
it('should persist the session directly to redis when useLoginApi is false', async () => {
|
|
1179
|
+
jest
|
|
1180
|
+
.spyOn(ComponentConfig, 'getComponentConfigValue')
|
|
1181
|
+
.mockImplementation((componentName: string, configKey: string) => {
|
|
1182
|
+
if (configKey === 'useLoginApiYN') return 'N' as any;
|
|
1183
|
+
return mockLoginApiEndpoint as any;
|
|
1184
|
+
});
|
|
1185
|
+
|
|
1186
|
+
const systemCode = 'SS';
|
|
1187
|
+
const sessionId = 'session-1';
|
|
1188
|
+
const sessionService = await SessionService.init();
|
|
1189
|
+
const loginUser = await LoginUser.init(sessionService);
|
|
1190
|
+
loginUser.ObjectId = '1';
|
|
1191
|
+
|
|
1192
|
+
jest.spyOn(loginUser, 'getPrivileges').mockResolvedValue(['privilege1']);
|
|
1193
|
+
loginUser['_SessionService'].retrieveUserSession = jest
|
|
1194
|
+
.fn()
|
|
1195
|
+
.mockResolvedValue({ systemLogins: [] });
|
|
1196
|
+
loginUser['_SessionService'].setUserSession = jest.fn();
|
|
1197
|
+
|
|
1198
|
+
const axiosPostMock = jest.spyOn(axios, 'post');
|
|
1199
|
+
|
|
1200
|
+
await loginUser.setSession(systemCode, sessionId, null);
|
|
1201
|
+
|
|
1202
|
+
expect(axiosPostMock).not.toHaveBeenCalled();
|
|
1203
|
+
expect(loginUser['_SessionService'].setUserSession).toHaveBeenCalledWith(
|
|
1204
|
+
'1',
|
|
1205
|
+
{
|
|
1206
|
+
systemLogins: [
|
|
1207
|
+
{ id: systemCode, code: systemCode, sessionId, privileges: ['privilege1'] },
|
|
1208
|
+
],
|
|
1209
|
+
},
|
|
1210
|
+
'test-session',
|
|
1211
|
+
);
|
|
1212
|
+
});
|
|
975
1213
|
});
|
|
976
1214
|
|
|
977
1215
|
describe('getObjectPrivileges', () => {
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { User } from '../../../../src/components/login-user/user';
|
|
2
|
+
import { UserPasswordHistory } from '../../../../src/components/user-password-history/user-password-history';
|
|
3
|
+
import { YN } from '../../../../src/enum/yn.enum';
|
|
4
|
+
|
|
5
|
+
// User and LoginUser import each other; this test exercises only User's expiry policy.
|
|
6
|
+
jest.mock('../../../../src/components/login-user/login-user', () => ({
|
|
7
|
+
LoginUser: class {},
|
|
8
|
+
}));
|
|
9
|
+
|
|
10
|
+
describe('User.getPasswordExpiryStatus', () => {
|
|
11
|
+
const originalExpiryMonths = process.env.PASSWORD_EXPIRY_MONTHS;
|
|
12
|
+
const originalWarningDays = process.env.PASSWORD_EXPIRY_WARNING_DAYS;
|
|
13
|
+
|
|
14
|
+
const user = (password: string | null) =>
|
|
15
|
+
Object.assign(Object.create(User.prototype), {
|
|
16
|
+
ObjectId: '123',
|
|
17
|
+
_Password: password,
|
|
18
|
+
_NeedToChangePasswordYN: YN.No,
|
|
19
|
+
}) as User;
|
|
20
|
+
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
process.env.PASSWORD_EXPIRY_MONTHS = '1';
|
|
23
|
+
process.env.PASSWORD_EXPIRY_WARNING_DAYS = '14';
|
|
24
|
+
jest.useFakeTimers().setSystemTime(new Date('2026-02-20T12:00:00Z'));
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
afterEach(() => {
|
|
28
|
+
jest.restoreAllMocks();
|
|
29
|
+
jest.useRealTimers();
|
|
30
|
+
if (originalExpiryMonths === undefined)
|
|
31
|
+
delete process.env.PASSWORD_EXPIRY_MONTHS;
|
|
32
|
+
else process.env.PASSWORD_EXPIRY_MONTHS = originalExpiryMonths;
|
|
33
|
+
if (originalWarningDays === undefined)
|
|
34
|
+
delete process.env.PASSWORD_EXPIRY_WARNING_DAYS;
|
|
35
|
+
else process.env.PASSWORD_EXPIRY_WARNING_DAYS = originalWarningDays;
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('disables expiry when the period is zero', async () => {
|
|
39
|
+
process.env.PASSWORD_EXPIRY_MONTHS = '0';
|
|
40
|
+
const lookup = jest.spyOn(UserPasswordHistory, 'getLatestByUserId');
|
|
41
|
+
|
|
42
|
+
expect(await user('hash').getPasswordExpiryStatus()).toEqual({
|
|
43
|
+
expired: false,
|
|
44
|
+
expiringSoon: false,
|
|
45
|
+
expiresInDays: null,
|
|
46
|
+
});
|
|
47
|
+
expect(lookup).not.toHaveBeenCalled();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('skips an account without a local password', async () => {
|
|
51
|
+
const lookup = jest.spyOn(UserPasswordHistory, 'getLatestByUserId');
|
|
52
|
+
|
|
53
|
+
expect(await user(null).getPasswordExpiryStatus()).toEqual({
|
|
54
|
+
expired: false,
|
|
55
|
+
expiringSoon: false,
|
|
56
|
+
expiresInDays: null,
|
|
57
|
+
});
|
|
58
|
+
expect(lookup).not.toHaveBeenCalled();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('expires an account without password history', async () => {
|
|
62
|
+
jest
|
|
63
|
+
.spyOn(UserPasswordHistory, 'getLatestByUserId')
|
|
64
|
+
.mockResolvedValue(null);
|
|
65
|
+
|
|
66
|
+
expect(await user('hash').getPasswordExpiryStatus()).toEqual({
|
|
67
|
+
expired: true,
|
|
68
|
+
expiringSoon: false,
|
|
69
|
+
expiresInDays: null,
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('warns before expiry and clamps month-end dates', async () => {
|
|
74
|
+
jest.spyOn(UserPasswordHistory, 'getLatestByUserId').mockResolvedValue({
|
|
75
|
+
CreatedAt: new Date('2026-01-31T12:00:00Z'),
|
|
76
|
+
} as UserPasswordHistory);
|
|
77
|
+
|
|
78
|
+
expect(await user('hash').getPasswordExpiryStatus()).toEqual({
|
|
79
|
+
expired: false,
|
|
80
|
+
expiringSoon: true,
|
|
81
|
+
expiresInDays: 8,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
jest.setSystemTime(new Date('2026-02-28T12:00:00Z'));
|
|
85
|
+
expect((await user('hash').getPasswordExpiryStatus()).expired).toBe(true);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
@@ -54,11 +54,15 @@ describe('GroupReportingUser', () => {
|
|
|
54
54
|
});
|
|
55
55
|
}));
|
|
56
56
|
it('should throw ClassError when GroupReportingUser is not found', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
57
|
-
jest
|
|
57
|
+
jest
|
|
58
|
+
.spyOn(group_reporting_user_repository_1.GroupReportingUserRepository.prototype, 'findByPk')
|
|
59
|
+
.mockResolvedValue(null);
|
|
58
60
|
yield expect(group_reporting_user_1.GroupReportingUser.init(mockDbTransaction, '1')).rejects.toThrow(general_1.ClassError);
|
|
59
61
|
}));
|
|
60
62
|
it('should throw ClassError when failed to initialize GroupReportingUser', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
61
|
-
jest
|
|
63
|
+
jest
|
|
64
|
+
.spyOn(group_reporting_user_repository_1.GroupReportingUserRepository.prototype, 'findByPk')
|
|
65
|
+
.mockRejectedValue(new Error());
|
|
62
66
|
yield expect(group_reporting_user_1.GroupReportingUser.init(mockDbTransaction, '1')).rejects.toThrow(Error);
|
|
63
67
|
}));
|
|
64
68
|
});
|
package/dist/__tests__/unit/components/group-reporting-user/group-reporting-user.spec.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"group-reporting-user.spec.js","sourceRoot":"","sources":["../../../../../__tests__/unit/components/group-reporting-user/group-reporting-user.spec.ts"],"names":[],"mappings":";;;;;;;;;;;AACA,IAAI,CAAC,IAAI,CAAC,kDAAkD,EAAE,GAAG,EAAE,CAAC,CAAC;IACnE,SAAS,EAAE,MAAM,aAAa;KAAG;CAClC,CAAC,CAAC,CAAC;AACJ,IAAI,CAAC,IAAI,CAAC,4CAA4C,EAAE,GAAG,EAAE,CAAC,CAAC;IAC7D,IAAI,EAAE,MAAM,QAAQ;KAAG;CACxB,CAAC,CAAC,CAAC;AAEJ,+GAA0G;AAC1G,qIAA+H;AAC/H,4CAA4C;AAE5C,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;IAClC,MAAM,0BAA0B,GAAG;QACjC,oBAAoB,EAAE,CAAC;QACvB,SAAS,EAAE,MAAM;QACjB,MAAM,EAAE,CAAC;QACT,IAAI,EAAE,CAAC;QACP,MAAM,EAAE,QAAQ;QAChB,WAAW,EAAE,CAAC;QACd,SAAS,EAAE,IAAI,IAAI,EAAE;QACrB,WAAW,EAAE,CAAC;QACd,SAAS,EAAE,IAAI,IAAI,EAAE;KACtB,CAAC;IAEF,UAAU,CAAC,GAAS,EAAE;QACpB,IAAI,CAAC,aAAa,EAAE,CAAC;IACvB,CAAC,CAAA,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"group-reporting-user.spec.js","sourceRoot":"","sources":["../../../../../__tests__/unit/components/group-reporting-user/group-reporting-user.spec.ts"],"names":[],"mappings":"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"}
|