@tomei/sso 0.66.0 → 0.66.1-test.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.
Files changed (84) hide show
  1. package/.commitlintrc.json +22 -22
  2. package/.gitlab-ci.yml +237 -16
  3. package/.husky/commit-msg +15 -15
  4. package/.husky/pre-commit +7 -7
  5. package/.prettierrc +4 -4
  6. package/Jenkinsfile +57 -57
  7. package/README.md +23 -23
  8. package/__tests__/unit/components/api-key/api-key.spec.ts +201 -0
  9. package/__tests__/unit/components/group/group.spec.ts +85 -79
  10. package/__tests__/unit/components/group-object-privilege/group-object-privilege.spec.ts +88 -88
  11. package/__tests__/unit/components/group-privilege/group-privilege.spec.ts +68 -68
  12. package/__tests__/unit/components/group-reporting-user/group-reporting-user.spec.ts +79 -67
  13. package/__tests__/unit/components/group-system-access/group-system-access.spec.ts +83 -83
  14. package/__tests__/unit/components/login-user/l.spec.ts +746 -746
  15. package/__tests__/unit/components/login-user/login.spec.ts +2327 -1164
  16. package/__tests__/unit/components/password-hash/password-hash.service.spec.ts +31 -31
  17. package/__tests__/unit/components/system/system.spec.ts +255 -254
  18. package/__tests__/unit/components/system-privilege/system-privilege.spec.ts +83 -83
  19. package/__tests__/unit/components/user-group/user-group.spec.ts +86 -86
  20. package/__tests__/unit/components/user-object-privilege/user-object-privilege.spec.ts +78 -78
  21. package/__tests__/unit/components/user-password-history/user-password-history.spec.ts +165 -0
  22. package/__tests__/unit/components/user-privilege/user-privilege.spec.ts +72 -72
  23. package/__tests__/unit/components/user-reporting-hierarchy/user-reporting-hierarchy.spec.ts +233 -0
  24. package/__tests__/unit/components/user-system-access/user-system-access.spec.ts +89 -89
  25. package/__tests__/unit/redis-client/redis.service.spec.ts +23 -23
  26. package/__tests__/unit/session/session.service.spec.ts +47 -47
  27. package/__tests__/unit/system-privilege/system-privilage.spec.ts +91 -91
  28. package/coverage/cobertura-coverage.xml +237 -0
  29. package/coverage/test-report.xml +33 -0
  30. package/create-sso-user.sql +39 -39
  31. package/dist/__tests__/unit/components/{group-privilege/group-privilege.test.d.ts → api-key/api-key.spec.d.ts} +1 -1
  32. package/dist/__tests__/unit/components/api-key/api-key.spec.js +158 -0
  33. package/dist/__tests__/unit/components/api-key/api-key.spec.js.map +1 -0
  34. package/dist/__tests__/unit/components/group/group.spec.js +4 -0
  35. package/dist/__tests__/unit/components/group/group.spec.js.map +1 -1
  36. package/dist/__tests__/unit/components/group-reporting-user/group-reporting-user.spec.js +15 -3
  37. package/dist/__tests__/unit/components/group-reporting-user/group-reporting-user.spec.js.map +1 -1
  38. package/dist/__tests__/unit/components/login-user/login.spec.js +664 -0
  39. package/dist/__tests__/unit/components/login-user/login.spec.js.map +1 -1
  40. package/dist/__tests__/unit/components/system/system.spec.js +1 -0
  41. package/dist/__tests__/unit/components/system/system.spec.js.map +1 -1
  42. package/dist/__tests__/unit/components/user-password-history/user-password-history.spec.d.ts +1 -0
  43. package/dist/__tests__/unit/components/user-password-history/user-password-history.spec.js +138 -0
  44. package/dist/__tests__/unit/components/user-password-history/user-password-history.spec.js.map +1 -0
  45. package/dist/__tests__/unit/components/user-reporting-hierarchy/user-reporting-hierarchy.spec.d.ts +1 -0
  46. package/dist/__tests__/unit/components/user-reporting-hierarchy/user-reporting-hierarchy.spec.js +182 -0
  47. package/dist/__tests__/unit/components/user-reporting-hierarchy/user-reporting-hierarchy.spec.js.map +1 -0
  48. package/dist/src/components/login-user/login-user.js +2 -2
  49. package/dist/src/components/login-user/login-user.js.map +1 -1
  50. package/dist/tsconfig.tsbuildinfo +1 -1
  51. package/eslint.config.mjs +58 -58
  52. package/jest.config.js +16 -14
  53. package/migrations/20240314080602-create-user-table.js +124 -124
  54. package/migrations/20240314080603-create-user-group-table.js +85 -85
  55. package/migrations/20240314080604-create-user-user-group-table.js +55 -55
  56. package/migrations/20240314080605-create-login-history-table.js +53 -53
  57. package/migrations/20240527064925-create-system-table.js +78 -78
  58. package/migrations/20240527064926-create-system-privilege-table.js +71 -71
  59. package/migrations/20240527065342-create-group-table.js +93 -93
  60. package/migrations/20240527065633-create-group-reporting-user-table.js +76 -76
  61. package/migrations/20240528011551-create-group-system-access-table.js +72 -72
  62. package/migrations/20240528023018-user-system-access-table.js +75 -75
  63. package/migrations/20240528032229-user-privilege-table.js +76 -76
  64. package/migrations/20240528063003-create-group-privilege-table.js +76 -76
  65. package/migrations/20240528063051-create-group-object-privilege-table.js +84 -84
  66. package/migrations/20240528063107-create-user-object-privilege-table.js +84 -84
  67. package/migrations/20240528063108-create-api-key-table.js +85 -85
  68. package/migrations/20241104104802-create-building-table.js +95 -95
  69. package/migrations/20250108091132-add-area-manager-user-id-to-building-table.js +14 -14
  70. package/migrations/20250108091133-add-passcode-to-user-table.js +36 -36
  71. package/migrations/20250210115636-create-user-reporting-hierarchy.js +76 -76
  72. package/migrations/20250326043818-crate-user-password-history.js +42 -42
  73. package/migrations/20250610070720-added-MFBypassYN-to-sso-user.js +30 -30
  74. package/migrations/20250805085707-add-bulk-approval-code-to-sso-user.js +29 -0
  75. package/package.json +88 -90
  76. package/sampledotenv +7 -7
  77. package/src/components/login-user/login-user.ts +2 -2
  78. package/tsconfig.build.json +5 -5
  79. package/tsconfig.json +23 -23
  80. package/dist/__tests__/unit/components/group-privilege/group-privilege.test.js +0 -71
  81. package/dist/__tests__/unit/components/group-privilege/group-privilege.test.js.map +0 -1
  82. package/dist/__tests__/unit/components/login-user/login-user.spec.d.ts +0 -0
  83. package/dist/__tests__/unit/components/login-user/login-user.spec.js +0 -6
  84. package/dist/__tests__/unit/components/login-user/login-user.spec.js.map +0 -1
@@ -1,3 +1,667 @@
1
1
  "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
2
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
+ const session_service_1 = require("../../../../src/session/session.service");
13
+ const login_user_1 = require("../../../../src/components/login-user/login-user");
14
+ const config_1 = require("@tomei/config");
15
+ const general_1 = require("@tomei/general");
16
+ const user_status_enum_1 = require("../../../../src/enum/user-status.enum");
17
+ const system_privilege_entity_1 = require("../../../../src/models/system-privilege.entity");
18
+ const group_system_access_repository_1 = require("../../../../src/components/group-system-access/group-system-access.repository");
19
+ const group_repository_1 = require("../../../../src/components/group/group.repository");
20
+ const axios_1 = require("axios");
21
+ const mockUserData = {
22
+ UserId: 1,
23
+ FullName: 'John Doe',
24
+ Email: 'john.doe@example.com',
25
+ Password: 'password',
26
+ Status: 'Active',
27
+ DefaultPasswordChangedYN: 'N',
28
+ FirstLoginAt: new Date(),
29
+ LastLoginAt: new Date(),
30
+ MFAEnabled: 1,
31
+ MFAConfig: 'config',
32
+ RecoveryEmail: 'john.doe@example.com',
33
+ FailedLoginAttemptCount: 0,
34
+ LastFailedLoginAt: null,
35
+ LastPasswordChangedAt: new Date(),
36
+ NeedToChangePasswordYN: 'N',
37
+ CreatedById: 1,
38
+ CreatedAt: new Date(),
39
+ UpdatedById: 1,
40
+ UpdatedAt: new Date(),
41
+ Staff: {
42
+ FullName: 'John Doe',
43
+ IdNo: '1234567890',
44
+ Mobile: '1234567890',
45
+ },
46
+ };
47
+ const mockSessionService = {
48
+ setUserSession: jest.fn(),
49
+ retrieveUserSession: jest.fn(),
50
+ refreshDuration: jest.fn(),
51
+ };
52
+ describe('LoginUser', () => {
53
+ beforeAll(() => {
54
+ process.env.REDIS_HOST = 'localhost';
55
+ process.env.REDIS_PORT = '6379';
56
+ });
57
+ afterAll(() => {
58
+ jest.restoreAllMocks();
59
+ });
60
+ describe('init', () => {
61
+ let sessionService;
62
+ let userId;
63
+ let dbTransaction;
64
+ const mockLoginApiEndpoint = {
65
+ baseUrl: 'http://sso-api',
66
+ getProfile: '/get-profile',
67
+ apiKey: 'test-api-key',
68
+ };
69
+ beforeEach(() => {
70
+ sessionService = {};
71
+ userId = 1;
72
+ dbTransaction = null;
73
+ jest
74
+ .spyOn(config_1.ComponentConfig, 'getComponentConfigValue')
75
+ .mockReturnValue(mockLoginApiEndpoint);
76
+ });
77
+ it('should initialize LoginUser with valid userId', () => __awaiter(void 0, void 0, void 0, function* () {
78
+ const user = {
79
+ UserId: 1,
80
+ FullName: 'John Doe',
81
+ Email: 'john.doe@example.com',
82
+ Password: 'password',
83
+ Status: 'active',
84
+ DefaultPasswordChangedYN: 'yes',
85
+ FirstLoginAt: new Date(),
86
+ LastLoginAt: new Date(),
87
+ MFAEnabled: 1,
88
+ MFAConfig: 'config',
89
+ RecoveryEmail: 'john.doe@example.com',
90
+ FailedLoginAttemptCount: 0,
91
+ LastFailedLoginAt: null,
92
+ LastPasswordChangedAt: new Date(),
93
+ NeedToChangePasswordYN: 'no',
94
+ CreatedById: 1,
95
+ CreatedAt: new Date(),
96
+ UpdatedById: 1,
97
+ UpdatedAt: new Date(),
98
+ Staff: {
99
+ FullName: 'John Doe',
100
+ IdNo: '1234567890',
101
+ Mobile: '1234567890',
102
+ },
103
+ };
104
+ const axiosPostMock = jest
105
+ .spyOn(axios_1.default, 'post')
106
+ .mockResolvedValueOnce({ data: { loginUser: user } });
107
+ const result = yield login_user_1.LoginUser.init(sessionService, userId, dbTransaction);
108
+ expect(axiosPostMock).toHaveBeenCalledTimes(1);
109
+ expect(axiosPostMock).toHaveBeenCalledWith(`${mockLoginApiEndpoint.baseUrl}${mockLoginApiEndpoint.getProfile}`, { UserId: userId }, { headers: { 'Content-Type': 'application/json', 'x-api-key': mockLoginApiEndpoint.apiKey } });
110
+ expect(result).toBeInstanceOf(login_user_1.LoginUser);
111
+ expect(result.UserId).toBe(user.UserId);
112
+ expect(result.FullName).toBe(user.FullName);
113
+ expect(result.Email).toBe(user.Email);
114
+ expect(result.Status).toBe(user.Status);
115
+ }));
116
+ it('should throw an error when user is not found', () => __awaiter(void 0, void 0, void 0, function* () {
117
+ const axiosPostMock = jest
118
+ .spyOn(axios_1.default, 'post')
119
+ .mockResolvedValueOnce({ data: { loginUser: null } });
120
+ yield expect(login_user_1.LoginUser.init(sessionService, userId, dbTransaction)).rejects.toThrow(Error);
121
+ expect(axiosPostMock).toHaveBeenCalledTimes(1);
122
+ }));
123
+ });
124
+ describe('shouldReleaseLock', () => {
125
+ const minuteToAutoRelease = 5;
126
+ const autoReleaseYN = 'Y';
127
+ beforeEach(() => {
128
+ jest
129
+ .spyOn(config_1.ComponentConfig, 'getComponentConfigValue')
130
+ .mockImplementation((componentName, configKey) => {
131
+ if (configKey === 'minuteToAutoRelease') {
132
+ return minuteToAutoRelease;
133
+ }
134
+ if (configKey === 'autoReleaseYN') {
135
+ return autoReleaseYN;
136
+ }
137
+ });
138
+ });
139
+ it('should return true if autoReleaseYN is "Y" and time difference is greater than minuteToAutoRelease', () => __awaiter(void 0, void 0, void 0, function* () {
140
+ const lastFailedLoginAt = new Date();
141
+ lastFailedLoginAt.setMinutes(lastFailedLoginAt.getMinutes() - 10);
142
+ const result = login_user_1.LoginUser.shouldReleaseLock(lastFailedLoginAt);
143
+ expect(result).toBe(true);
144
+ }));
145
+ it('should return false if autoReleaseYN is "Y" and time difference is less than or equal to minuteToAutoRelease', () => __awaiter(void 0, void 0, void 0, function* () {
146
+ const lastFailedLoginAt = new Date();
147
+ lastFailedLoginAt.setMinutes(lastFailedLoginAt.getMinutes() - 3);
148
+ const result = login_user_1.LoginUser.shouldReleaseLock(lastFailedLoginAt);
149
+ expect(result).toBe(false);
150
+ }));
151
+ it('should return false if autoReleaseYN is "N"', () => __awaiter(void 0, void 0, void 0, function* () {
152
+ const lastFailedLoginAt = new Date();
153
+ const result = login_user_1.LoginUser.shouldReleaseLock(lastFailedLoginAt);
154
+ expect(result).toBe(false);
155
+ }));
156
+ });
157
+ describe('releaseLock', () => {
158
+ it('should release the lock for a user', () => __awaiter(void 0, void 0, void 0, function* () {
159
+ const UserId = 1;
160
+ const dbTransaction = null;
161
+ const updateMock = jest
162
+ .spyOn(login_user_1.LoginUser['_Repository'], 'update')
163
+ .mockImplementationOnce(() => Promise.resolve({}));
164
+ login_user_1.LoginUser['releaseLock'](UserId, dbTransaction);
165
+ expect(updateMock).toHaveBeenCalledTimes(1);
166
+ expect(updateMock).toHaveBeenCalledWith({
167
+ FailedLoginAttemptCount: 0,
168
+ Status: 'Active',
169
+ }, {
170
+ where: {
171
+ UserId,
172
+ },
173
+ transaction: dbTransaction,
174
+ });
175
+ }));
176
+ });
177
+ describe('checkUserInfoDuplicated', () => {
178
+ it('should throw an error if duplicate user info is found', () => __awaiter(void 0, void 0, void 0, function* () {
179
+ const dbTransaction = null;
180
+ const query = {
181
+ Email: 'test@example.com',
182
+ IdType: 'passport',
183
+ IdNo: '123456789',
184
+ ContactNo: '1234567890',
185
+ };
186
+ jest
187
+ .spyOn(login_user_1.LoginUser['_Repository'], 'findAll')
188
+ .mockResolvedValueOnce([{ id: 1 }]);
189
+ yield expect(login_user_1.LoginUser['checkUserInfoDuplicated'](dbTransaction, query)).rejects.toThrow();
190
+ }));
191
+ it('should not throw an error if duplicate user info is not found', () => __awaiter(void 0, void 0, void 0, function* () {
192
+ const dbTransaction = null;
193
+ const query = {
194
+ Email: 'test@example.com',
195
+ IdType: 'passport',
196
+ IdNo: '123456789',
197
+ ContactNo: '1234567890',
198
+ };
199
+ jest.spyOn(login_user_1.LoginUser['_Repository'], 'findAll').mockResolvedValueOnce([]);
200
+ yield expect(login_user_1.LoginUser['checkUserInfoDuplicated'](dbTransaction, query)).resolves.not.toThrow();
201
+ }));
202
+ });
203
+ describe('generateDefaultPassword', () => {
204
+ const passwordPolicy = {
205
+ minLen: 6,
206
+ maxLen: 10,
207
+ nonAcceptableChar: 'i,l,o',
208
+ numOfCapitalLetters: 1,
209
+ numOfNumbers: 1,
210
+ numOfSpecialChars: 1,
211
+ };
212
+ beforeEach(() => {
213
+ jest
214
+ .spyOn(config_1.ComponentConfig, 'getComponentConfigValue')
215
+ .mockImplementation((componentName, configKey) => {
216
+ if (configKey === 'passwordPolicy') {
217
+ return passwordPolicy;
218
+ }
219
+ });
220
+ });
221
+ it('should generate a default password with the specified length', () => {
222
+ const password = login_user_1.LoginUser['generateDefaultPassword']();
223
+ expect(password.length).toBeGreaterThanOrEqual(6);
224
+ expect(password.length).toBeLessThanOrEqual(10);
225
+ });
226
+ it('should generate a default password with at least one capital letter', () => {
227
+ const password = login_user_1.LoginUser['generateDefaultPassword']();
228
+ expect(/[A-Z]/.test(password)).toBe(true);
229
+ });
230
+ it('should generate a default password with at least one number', () => {
231
+ const password = login_user_1.LoginUser['generateDefaultPassword']();
232
+ expect(/[0-9]/.test(password)).toBe(true);
233
+ });
234
+ it('should generate a default password with at least one special character', () => {
235
+ const password = login_user_1.LoginUser['generateDefaultPassword']();
236
+ expect(/[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?~`]/.test(password)).toBe(true);
237
+ });
238
+ it('should generate a default password without any non-acceptable characters', () => {
239
+ const password = login_user_1.LoginUser['generateDefaultPassword']();
240
+ const nonAcceptableChars = ['i', 'l', 'o'];
241
+ expect(nonAcceptableChars.some((char) => password.includes(char))).toBe(false);
242
+ });
243
+ });
244
+ describe('setPassword', () => {
245
+ const passwordPolicy = {
246
+ minLen: 6,
247
+ maxLen: 10,
248
+ nonAcceptableChar: 'i,l,o',
249
+ numOfCapitalLetters: 1,
250
+ numOfNumbers: 1,
251
+ numOfSpecialChars: 1,
252
+ };
253
+ beforeEach(() => {
254
+ jest
255
+ .spyOn(config_1.ComponentConfig, 'getComponentConfigValue')
256
+ .mockImplementation((componentName, configKey) => {
257
+ if (configKey === 'passwordPolicy') {
258
+ return passwordPolicy;
259
+ }
260
+ });
261
+ });
262
+ it('should set the password for the user', () => __awaiter(void 0, void 0, void 0, function* () {
263
+ const dbTransaction = null;
264
+ const sessionService = yield session_service_1.SessionService.init();
265
+ const user = yield login_user_1.LoginUser.init(sessionService);
266
+ const password = 'N3wP@ssw0';
267
+ const result = yield login_user_1.LoginUser['setPassword'](dbTransaction, user, password);
268
+ expect(result).toBeInstanceOf(login_user_1.LoginUser);
269
+ yield expect(login_user_1.LoginUser['setPassword'](dbTransaction, user, password)).resolves.not.toThrow();
270
+ expect(result.Password).toBeDefined();
271
+ }));
272
+ it('should throw an error if the password does not meet the security requirements', () => __awaiter(void 0, void 0, void 0, function* () {
273
+ const dbTransaction = null;
274
+ const sessionService = yield session_service_1.SessionService.init();
275
+ const user = yield login_user_1.LoginUser.init(sessionService);
276
+ const password = 'weakpassword';
277
+ yield expect(login_user_1.LoginUser['setPassword'](dbTransaction, user, password)).rejects.toThrow();
278
+ }));
279
+ });
280
+ describe('incrementFailedLoginAttemptCount', () => {
281
+ afterAll(() => {
282
+ jest.restoreAllMocks();
283
+ });
284
+ it('should increment FailedLoginAttemptCount and update user status', () => __awaiter(void 0, void 0, void 0, function* () {
285
+ const sessionService = yield session_service_1.SessionService.init();
286
+ const loginUser = yield login_user_1.LoginUser.init(sessionService);
287
+ loginUser['FailedLoginAttemptCount'] = 2;
288
+ loginUser['LastFailedLoginAt'] = new Date();
289
+ loginUser['Status'] = user_status_enum_1.UserStatus.ACTIVE;
290
+ const dbTransaction = null;
291
+ jest
292
+ .spyOn(login_user_1.LoginUser['_Repository'], 'update')
293
+ .mockReturnValueOnce(null);
294
+ jest
295
+ .spyOn(config_1.ComponentConfig, 'getComponentConfigValue')
296
+ .mockImplementation((componentName, configKey) => {
297
+ if (configKey === 'maxFailedLoginAttempts') {
298
+ return 3;
299
+ }
300
+ if (configKey === 'autoReleaseYN') {
301
+ return 'Y';
302
+ }
303
+ });
304
+ yield loginUser['incrementFailedLoginAttemptCount'](dbTransaction);
305
+ expect(login_user_1.LoginUser['_Repository'].update).toHaveBeenCalledWith({
306
+ FailedLoginAttemptCount: 3,
307
+ LastFailedLoginAt: expect.any(Date),
308
+ Status: user_status_enum_1.UserStatus.ACTIVE,
309
+ }, {
310
+ where: {
311
+ UserId: loginUser.UserId,
312
+ },
313
+ transaction: dbTransaction,
314
+ });
315
+ }));
316
+ it('should throw an error if maxFailedLoginAttempts or autoReleaseYN is missing', () => __awaiter(void 0, void 0, void 0, function* () {
317
+ const sessionService = yield session_service_1.SessionService.init();
318
+ const loginUser = yield login_user_1.LoginUser.init(sessionService);
319
+ loginUser['FailedLoginAttemptCount'] = 2;
320
+ loginUser['LastFailedLoginAt'] = new Date();
321
+ loginUser['Status'] = user_status_enum_1.UserStatus.ACTIVE;
322
+ const dbTransaction = null;
323
+ jest
324
+ .spyOn(login_user_1.LoginUser['_Repository'], 'update')
325
+ .mockReturnValueOnce(null);
326
+ jest
327
+ .spyOn(config_1.ComponentConfig, 'getComponentConfigValue')
328
+ .mockImplementationOnce((componentName, configKey) => {
329
+ if (configKey === 'maxFailedLoginAttempts') {
330
+ return undefined;
331
+ }
332
+ if (configKey === 'autoReleaseYN') {
333
+ return undefined;
334
+ }
335
+ });
336
+ yield expect(loginUser['incrementFailedLoginAttemptCount'](dbTransaction)).rejects.toThrow(new general_1.ClassError('LoginUser', 'LoginUserErrMsg0X', 'Missing maxFailedLoginAttempts and or autoReleaseYN. Please set in config file.'));
337
+ }));
338
+ it('should lock the user account if the failed login attempts exceed the maximum allowed', () => __awaiter(void 0, void 0, void 0, function* () {
339
+ const sessionService = yield session_service_1.SessionService.init();
340
+ const loginUser = yield login_user_1.LoginUser.init(sessionService);
341
+ loginUser['FailedLoginAttemptCount'] = 3;
342
+ loginUser['LastFailedLoginAt'] = new Date();
343
+ loginUser['Status'] = user_status_enum_1.UserStatus.ACTIVE;
344
+ const dbTransaction = null;
345
+ jest
346
+ .spyOn(login_user_1.LoginUser['_Repository'], 'update')
347
+ .mockReturnValueOnce(null);
348
+ jest
349
+ .spyOn(config_1.ComponentConfig, 'getComponentConfigValue')
350
+ .mockImplementation((componentName, configKey) => {
351
+ if (configKey === 'maxFailedLoginAttempts') {
352
+ return 3;
353
+ }
354
+ if (configKey === 'autoReleaseYN') {
355
+ return 'Y';
356
+ }
357
+ });
358
+ try {
359
+ yield loginUser['incrementFailedLoginAttemptCount'](dbTransaction);
360
+ expect(false).toBe(true);
361
+ }
362
+ catch (error) {
363
+ expect(login_user_1.LoginUser['_Repository'].update).toHaveBeenCalledWith({
364
+ FailedLoginAttemptCount: 4,
365
+ LastFailedLoginAt: expect.any(Date),
366
+ Status: user_status_enum_1.UserStatus.LOCKED,
367
+ }, {
368
+ where: {
369
+ UserId: loginUser.UserId,
370
+ },
371
+ transaction: dbTransaction,
372
+ });
373
+ expect(error).toBeInstanceOf(general_1.ClassError);
374
+ expect(error.message).toBe('Your account has been temporarily locked due to too many failed login attempts, please try again later.');
375
+ }
376
+ }));
377
+ it('should permanently lock the user account if the failed login attempts exceed the maximum allowed and autoReleaseYN is N', () => __awaiter(void 0, void 0, void 0, function* () {
378
+ const sessionService = yield session_service_1.SessionService.init();
379
+ const loginUser = yield login_user_1.LoginUser.init(sessionService);
380
+ loginUser['FailedLoginAttemptCount'] = 3;
381
+ loginUser['LastFailedLoginAt'] = new Date();
382
+ loginUser['Status'] = user_status_enum_1.UserStatus.ACTIVE;
383
+ const dbTransaction = null;
384
+ jest
385
+ .spyOn(login_user_1.LoginUser['_Repository'], 'update')
386
+ .mockReturnValueOnce(null);
387
+ jest
388
+ .spyOn(config_1.ComponentConfig, 'getComponentConfigValue')
389
+ .mockImplementation((componentName, configKey) => {
390
+ if (configKey === 'maxFailedLoginAttempts') {
391
+ return 3;
392
+ }
393
+ if (configKey === 'autoReleaseYN') {
394
+ return 'N';
395
+ }
396
+ });
397
+ try {
398
+ yield loginUser['incrementFailedLoginAttemptCount'](dbTransaction);
399
+ expect(false).toBe(true);
400
+ }
401
+ catch (error) {
402
+ expect(login_user_1.LoginUser['_Repository'].update).toHaveBeenCalledWith({
403
+ FailedLoginAttemptCount: 4,
404
+ LastFailedLoginAt: expect.any(Date),
405
+ Status: user_status_enum_1.UserStatus.LOCKED,
406
+ }, {
407
+ where: {
408
+ UserId: loginUser.UserId,
409
+ },
410
+ transaction: dbTransaction,
411
+ });
412
+ expect(error).toBeInstanceOf(general_1.ClassError);
413
+ expect(error.message).toBe('Your account has been locked due to too many failed login attempts, please contact IT Support for instructions on how to unlock your account');
414
+ }
415
+ }));
416
+ });
417
+ describe('combineSystemAccess', () => {
418
+ it('should combine user and group system access and remove duplicates', () => __awaiter(void 0, void 0, void 0, function* () {
419
+ const sessionService = yield session_service_1.SessionService.init();
420
+ const loginUser = yield login_user_1.LoginUser.init(sessionService);
421
+ const dbTransaction = null;
422
+ const groups = [
423
+ { InheritParentSystemAccessYN: true },
424
+ { InheritParentSystemAccessYN: false },
425
+ ];
426
+ jest
427
+ .spyOn(login_user_1.LoginUser, 'getInheritedSystemAccess')
428
+ .mockResolvedValueOnce([
429
+ { SystemCode: 'system1' },
430
+ { SystemCode: 'system2' },
431
+ ]);
432
+ jest
433
+ .spyOn(login_user_1.LoginUser['_UserSystemAccessRepo'], 'findAll')
434
+ .mockResolvedValueOnce([{ SystemCode: 'system3' }]);
435
+ const result = yield login_user_1.LoginUser['combineSystemAccess'](loginUser, dbTransaction, groups);
436
+ expect(result).toEqual([
437
+ { SystemCode: 'system3' },
438
+ { SystemCode: 'system1' },
439
+ { SystemCode: 'system2' },
440
+ ]);
441
+ }));
442
+ });
443
+ describe('checkPrivileges', () => {
444
+ const mockLoginApiEndpoint = {
445
+ baseUrl: 'http://sso-api',
446
+ checkPrivileges: '/check-privileges',
447
+ apiKey: 'test-api-key',
448
+ };
449
+ beforeEach(() => {
450
+ jest
451
+ .spyOn(config_1.ApplicationConfig, 'getComponentConfigValue')
452
+ .mockReturnValue('test-session');
453
+ jest
454
+ .spyOn(config_1.ComponentConfig, 'getComponentConfigValue')
455
+ .mockReturnValue(mockLoginApiEndpoint);
456
+ });
457
+ it('should return true if user has the specified privilege', () => __awaiter(void 0, void 0, void 0, function* () {
458
+ const systemCode = 'SS';
459
+ const privilegeName = 'Privilege 1';
460
+ const sessionService = yield session_service_1.SessionService.init();
461
+ const loginUser = yield login_user_1.LoginUser.init(sessionService);
462
+ loginUser.ObjectId = '1';
463
+ const axiosPostMock = jest
464
+ .spyOn(axios_1.default, 'post')
465
+ .mockResolvedValueOnce({ data: { hasPrivilege: true } });
466
+ const hasPrivilege = yield loginUser.checkPrivileges(systemCode, privilegeName);
467
+ expect(hasPrivilege).toBe(true);
468
+ expect(axiosPostMock).toHaveBeenCalledWith(`${mockLoginApiEndpoint.baseUrl}${mockLoginApiEndpoint.checkPrivileges}`, { SystemCode: systemCode, SessionName: 'test-session', PrivilegeName: privilegeName, UserId: '1' }, { headers: { 'Content-Type': 'application/json', 'x-api-key': mockLoginApiEndpoint.apiKey } });
469
+ }));
470
+ it('should return false if user does not have the specified privilege', () => __awaiter(void 0, void 0, void 0, function* () {
471
+ const systemCode = 'SS';
472
+ const privilegeName = 'Privilege 1';
473
+ const sessionService = yield session_service_1.SessionService.init();
474
+ const loginUser = yield login_user_1.LoginUser.init(sessionService);
475
+ loginUser.ObjectId = '1';
476
+ const axiosPostMock = jest
477
+ .spyOn(axios_1.default, 'post')
478
+ .mockResolvedValueOnce({ data: { hasPrivilege: false } });
479
+ const hasPrivilege = yield loginUser.checkPrivileges(systemCode, privilegeName);
480
+ expect(hasPrivilege).toBe(false);
481
+ }));
482
+ it('should throw an error if ObjectId is not set', () => __awaiter(void 0, void 0, void 0, function* () {
483
+ const systemCode = 'SS';
484
+ const privilegeName = 'Privilege 1';
485
+ const sessionService = yield session_service_1.SessionService.init();
486
+ const loginUser = yield login_user_1.LoginUser.init(sessionService);
487
+ loginUser.ObjectId = null;
488
+ yield expect(loginUser.checkPrivileges(systemCode, privilegeName)).rejects.toThrow();
489
+ }));
490
+ });
491
+ describe('getObjectPrivileges', () => {
492
+ it('should return an array of privileges', () => __awaiter(void 0, void 0, void 0, function* () {
493
+ const systemCode = 'system1';
494
+ const dbTransaction = null;
495
+ const sessionService = yield session_service_1.SessionService.init();
496
+ const loginUser = yield login_user_1.LoginUser.init(sessionService);
497
+ const findAllMock = jest
498
+ .spyOn(login_user_1.LoginUser['_UserObjectPrivilegeRepo'], 'findAll')
499
+ .mockResolvedValue([
500
+ {
501
+ PrivilegeCode: 'privilege1',
502
+ Privilege: {
503
+ PrivilegeCode: 'privilege1',
504
+ SystemCode: systemCode,
505
+ Name: 'privilege1',
506
+ },
507
+ },
508
+ {
509
+ PrivilegeCode: 'privilege2',
510
+ Privilege: {
511
+ PrivilegeCode: 'privilege2',
512
+ Name: 'privilege2',
513
+ },
514
+ },
515
+ ]);
516
+ const result = yield loginUser['getObjectPrivileges'](systemCode, dbTransaction);
517
+ expect(findAllMock).toHaveBeenCalledTimes(1);
518
+ expect(findAllMock).toHaveBeenCalledWith({
519
+ where: {
520
+ UserId: loginUser.UserId,
521
+ },
522
+ include: {
523
+ model: system_privilege_entity_1.default,
524
+ where: {
525
+ SystemCode: systemCode,
526
+ Status: 'Active',
527
+ },
528
+ },
529
+ transaction: dbTransaction,
530
+ });
531
+ expect(result).toEqual(['privilege1', 'privilege2']);
532
+ }));
533
+ it('should throw an error if an exception occurs', () => __awaiter(void 0, void 0, void 0, function* () {
534
+ const systemCode = 'system1';
535
+ const dbTransaction = null;
536
+ const sessionService = yield session_service_1.SessionService.init();
537
+ const loginUser = yield login_user_1.LoginUser.init(sessionService);
538
+ jest
539
+ .spyOn(login_user_1.LoginUser['_UserObjectPrivilegeRepo'], 'findAll')
540
+ .mockRejectedValue(new Error('Database error'));
541
+ yield expect(loginUser['getObjectPrivileges'](systemCode, dbTransaction)).rejects.toThrow(Error);
542
+ }));
543
+ });
544
+ describe('getUserPersonalPrivileges', () => {
545
+ it('should return an array of privileges', () => __awaiter(void 0, void 0, void 0, function* () {
546
+ const sessionService = yield session_service_1.SessionService.init();
547
+ const loginUser = yield login_user_1.LoginUser.init(sessionService);
548
+ const systemCode = 'system1';
549
+ const dbTransaction = null;
550
+ const findAllMock = jest.spyOn(login_user_1.LoginUser['_UserPrivilegeRepo'], 'findAll');
551
+ findAllMock.mockResolvedValue([
552
+ { Privilege: { PrivilegeCode: 'privilege1' } },
553
+ { Privilege: { PrivilegeCode: 'privilege2' } },
554
+ ]);
555
+ const privileges = yield loginUser['getUserPersonalPrivileges'](systemCode, dbTransaction);
556
+ expect(privileges).toEqual(['privilege1', 'privilege2']);
557
+ expect(findAllMock).toHaveBeenCalledTimes(1);
558
+ expect(findAllMock).toHaveBeenCalledWith({
559
+ where: {
560
+ UserId: loginUser.UserId,
561
+ Status: 'Active',
562
+ },
563
+ include: {
564
+ model: system_privilege_entity_1.default,
565
+ where: {
566
+ SystemCode: systemCode,
567
+ Status: 'Active',
568
+ },
569
+ },
570
+ transaction: dbTransaction,
571
+ });
572
+ }));
573
+ it('should throw an error if an error occurs', () => __awaiter(void 0, void 0, void 0, function* () {
574
+ const sessionService = yield session_service_1.SessionService.init();
575
+ const loginUser = yield login_user_1.LoginUser.init(sessionService);
576
+ const systemCode = 'system1';
577
+ const dbTransaction = null;
578
+ const findAllMock = jest.spyOn(login_user_1.LoginUser['_UserPrivilegeRepo'], 'findAll');
579
+ findAllMock.mockRejectedValue(new Error('Database error'));
580
+ yield expect(loginUser['getUserPersonalPrivileges'](systemCode, dbTransaction)).rejects.toThrow(Error);
581
+ expect(findAllMock).toHaveBeenCalledTimes(1);
582
+ expect(findAllMock).toHaveBeenCalledWith({
583
+ where: {
584
+ UserId: loginUser.UserId,
585
+ Status: 'Active',
586
+ },
587
+ include: {
588
+ model: system_privilege_entity_1.default,
589
+ where: {
590
+ SystemCode: systemCode,
591
+ Status: 'Active',
592
+ },
593
+ },
594
+ transaction: dbTransaction,
595
+ });
596
+ }));
597
+ });
598
+ describe('getInheritedSystemAccess', () => {
599
+ it('should return group system access with its parent group system access if applicable', () => __awaiter(void 0, void 0, void 0, function* () {
600
+ const dbTransaction = null;
601
+ const group = {
602
+ GroupCode: 'group1',
603
+ InheritParentSystemAccessYN: 'Y',
604
+ ParentGroupCode: 'parentGroup',
605
+ };
606
+ const parentGroup = {
607
+ GroupCode: 'parentGroup',
608
+ InheritParentSystemAccessYN: 'N',
609
+ ParentGroupCode: null,
610
+ };
611
+ const systemAccess = [
612
+ {
613
+ SystemCode: 'system1',
614
+ GroupCode: 'group1',
615
+ System: { SystemCode: 'system1' },
616
+ },
617
+ {
618
+ SystemCode: 'system2',
619
+ GroupCode: 'group1',
620
+ System: { SystemCode: 'system1' },
621
+ },
622
+ ];
623
+ const parentSystemAccess = [
624
+ {
625
+ SystemCode: 'system3',
626
+ GroupCode: 'parentGroup',
627
+ System: { SystemCode: 'system3' },
628
+ },
629
+ ];
630
+ const groupFindByPkMock = jest
631
+ .spyOn(group_repository_1.GroupRepository.prototype, 'findByPk')
632
+ .mockResolvedValueOnce(parentGroup);
633
+ const systemAccessFindAllMock = jest
634
+ .spyOn(group_system_access_repository_1.GroupSystemAccessRepository.prototype, 'findAll')
635
+ .mockImplementation((options) => {
636
+ if (options.where.GroupCode === group.GroupCode) {
637
+ return Promise.resolve(systemAccess);
638
+ }
639
+ else if (options.where.GroupCode === parentGroup.GroupCode) {
640
+ return Promise.resolve(parentSystemAccess);
641
+ }
642
+ });
643
+ const result = yield login_user_1.LoginUser['getInheritedSystemAccess'](dbTransaction, group);
644
+ console.log(result);
645
+ expect(result).toEqual([
646
+ {
647
+ SystemCode: 'system1',
648
+ GroupCode: 'group1',
649
+ System: { SystemCode: 'system1' },
650
+ },
651
+ {
652
+ SystemCode: 'system2',
653
+ GroupCode: 'group1',
654
+ System: { SystemCode: 'system1' },
655
+ },
656
+ {
657
+ SystemCode: 'system3',
658
+ GroupCode: 'parentGroup',
659
+ System: { SystemCode: 'system3' },
660
+ },
661
+ ]);
662
+ expect(groupFindByPkMock).toHaveBeenCalledWith(group.ParentGroupCode, dbTransaction);
663
+ expect(systemAccessFindAllMock).toHaveBeenCalledTimes(2);
664
+ }));
665
+ });
666
+ });
3
667
  //# sourceMappingURL=login.spec.js.map