@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.
Files changed (56) hide show
  1. package/__tests__/unit/components/group-reporting-user/group-reporting-user.spec.ts +12 -8
  2. package/__tests__/unit/components/login-user/login.spec.ts +310 -72
  3. package/__tests__/unit/components/login-user/password-expiry.spec.ts +87 -0
  4. package/dist/__tests__/unit/components/group-reporting-user/group-reporting-user.spec.js +6 -2
  5. package/dist/__tests__/unit/components/group-reporting-user/group-reporting-user.spec.js.map +1 -1
  6. package/dist/__tests__/unit/components/login-user/login.spec.js +227 -69
  7. package/dist/__tests__/unit/components/login-user/login.spec.js.map +1 -1
  8. package/dist/__tests__/unit/components/login-user/password-expiry.spec.d.ts +1 -0
  9. package/dist/__tests__/unit/components/login-user/password-expiry.spec.js +86 -0
  10. package/dist/__tests__/unit/components/login-user/password-expiry.spec.js.map +1 -0
  11. package/dist/src/components/login-user/interfaces/user-info.interface.d.ts +1 -0
  12. package/dist/src/components/login-user/login-user.d.ts +1 -0
  13. package/dist/src/components/login-user/login-user.js +85 -14
  14. package/dist/src/components/login-user/login-user.js.map +1 -1
  15. package/dist/src/components/login-user/user.d.ts +1 -0
  16. package/dist/src/components/login-user/user.js +7 -1
  17. package/dist/src/components/login-user/user.js.map +1 -1
  18. package/dist/src/components/system/system.d.ts +1 -0
  19. package/dist/src/components/system/system.js +1 -0
  20. package/dist/src/components/system/system.js.map +1 -1
  21. package/dist/src/components/system-privilege/system-privilege.d.ts +1 -0
  22. package/dist/src/components/system-privilege/system-privilege.js +2 -0
  23. package/dist/src/components/system-privilege/system-privilege.js.map +1 -1
  24. package/dist/src/index.d.ts +1 -0
  25. package/dist/src/index.js +1 -0
  26. package/dist/src/index.js.map +1 -1
  27. package/dist/src/interfaces/system-privilege.interface.d.ts +1 -0
  28. package/dist/src/interfaces/system.interface.d.ts +1 -0
  29. package/dist/src/models/group.entity.d.ts +1 -0
  30. package/dist/src/models/group.entity.js +8 -1
  31. package/dist/src/models/group.entity.js.map +1 -1
  32. package/dist/src/models/system-privilege.entity.d.ts +1 -0
  33. package/dist/src/models/system-privilege.entity.js +7 -0
  34. package/dist/src/models/system-privilege.entity.js.map +1 -1
  35. package/dist/src/models/system.entity.d.ts +1 -0
  36. package/dist/src/models/system.entity.js +7 -0
  37. package/dist/src/models/system.entity.js.map +1 -1
  38. package/dist/src/models/user.entity.d.ts +1 -0
  39. package/dist/src/models/user.entity.js +7 -0
  40. package/dist/src/models/user.entity.js.map +1 -1
  41. package/dist/tsconfig.tsbuildinfo +1 -1
  42. package/package.json +2 -1
  43. package/src/components/login-user/interfaces/user-info.interface.ts +1 -0
  44. package/src/components/login-user/login-user.ts +125 -17
  45. package/src/components/login-user/user.ts +14 -2
  46. package/src/components/system/system.ts +2 -0
  47. package/src/components/system-privilege/system-privilege.ts +3 -0
  48. package/src/index.ts +1 -0
  49. package/src/interfaces/system-privilege.interface.ts +1 -0
  50. package/src/interfaces/system.interface.ts +1 -0
  51. package/src/models/group.entity.ts +7 -1
  52. package/src/models/system-privilege.entity.ts +6 -0
  53. package/src/models/system.entity.ts +6 -0
  54. package/src/models/user.entity.ts +6 -0
  55. package/coverage/cobertura-coverage.xml +0 -6837
  56. package/coverage/test-report.xml +0 -161
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tomei/sso",
3
- "version": "0.66.1-test.2",
3
+ "version": "0.66.1-test.4",
4
4
  "description": "Tomei SSO Package",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -65,6 +65,7 @@
65
65
  "@tomei/general": "^0.21.10",
66
66
  "@tomei/mailer": "^0.6.2",
67
67
  "argon2": "^0.44.0",
68
+ "axios": "^1.13.2",
68
69
  "redis": "^5.8.2",
69
70
  "reflect-metadata": "^0.2.2",
70
71
  "sequelize": "^6.37.7",
@@ -9,6 +9,7 @@ export interface IUserInfo {
9
9
  ContactNo: string;
10
10
  UserName: string;
11
11
  UserId?: number;
12
+ V1Id?: number;
12
13
  Password: string;
13
14
  staffs?: any;
14
15
  }
@@ -12,13 +12,27 @@ import { createHash, randomBytes, randomUUID } from 'crypto';
12
12
  import { UserGroupRepository } from '../user-group/user-group.repository';
13
13
  import GroupSystemAccessModel from '../../models/group-system-access.entity';
14
14
  import SystemModel from '../../models/system.entity';
15
- import { ApplicationConfig } from '@tomei/config';
15
+ import { ApplicationConfig, ComponentConfig } from '@tomei/config';
16
+ import axios from 'axios';
16
17
 
17
18
  export class LoginUser extends User implements ILoginUser {
18
19
  session = {
19
20
  Id: null,
20
21
  };
21
22
 
23
+ private static isLoginApiEnabled(): boolean {
24
+ try {
25
+ const useLoginApiYN =
26
+ ComponentConfig.getComponentConfigValue(
27
+ '@tomei/sso',
28
+ 'useLoginApiYN',
29
+ ) || 'Y';
30
+ return String(useLoginApiYN).toUpperCase() !== 'N';
31
+ } catch (error) {
32
+ return true;
33
+ }
34
+ }
35
+
22
36
  static async init(
23
37
  sessionService: ISessionService,
24
38
  userId?: number,
@@ -26,20 +40,43 @@ export class LoginUser extends User implements ILoginUser {
26
40
  ): Promise<LoginUser> {
27
41
  User._RedisService = await RedisService.init();
28
42
  if (userId) {
29
- if (dbTransaction) {
30
- User._Repository = new UserRepository();
31
- }
32
- const user = await User._Repository.findOne({
33
- where: {
34
- UserId: userId,
35
- },
36
- include: [
43
+ let user: any;
44
+
45
+ if (LoginUser.isLoginApiEnabled()) {
46
+ const loginApiEndpoint = ComponentConfig.getComponentConfigValue(
47
+ '@tomei/sso',
48
+ 'loginApiEndpoint',
49
+ );
50
+
51
+ let payload = { UserId: userId };
52
+
53
+ const { data } = await axios.post(
54
+ `${loginApiEndpoint.baseUrl}${loginApiEndpoint.getProfile}`,
55
+ payload,
37
56
  {
38
- model: Staff,
57
+ headers: {
58
+ 'Content-Type': 'application/json',
59
+ 'x-api-key': loginApiEndpoint.apiKey,
60
+ },
39
61
  },
40
- ],
41
- transaction: dbTransaction,
42
- });
62
+ );
63
+ user = data.loginUser;
64
+ } else {
65
+ if (dbTransaction) {
66
+ User._Repository = new UserRepository();
67
+ }
68
+ user = await User._Repository.findOne({
69
+ where: {
70
+ UserId: userId,
71
+ },
72
+ include: [
73
+ {
74
+ model: Staff,
75
+ },
76
+ ],
77
+ transaction: dbTransaction,
78
+ });
79
+ }
43
80
 
44
81
  if (!user) {
45
82
  throw new Error('Invalid credentials.');
@@ -99,6 +136,30 @@ export class LoginUser extends User implements ILoginUser {
99
136
  throw new Error('Session name is not set');
100
137
  }
101
138
 
139
+ if (LoginUser.isLoginApiEnabled()) {
140
+ const loginApiEndpoint = ComponentConfig.getComponentConfigValue(
141
+ '@tomei/sso',
142
+ 'loginApiEndpoint',
143
+ );
144
+ let payload = {
145
+ SystemCode: systemCode,
146
+ SessionName: sessionName,
147
+ PrivilegeName: privilegeName,
148
+ UserId: this.ObjectId,
149
+ };
150
+ const { data } = await axios.post(
151
+ `${loginApiEndpoint.baseUrl}${loginApiEndpoint.checkPrivileges}`,
152
+ payload,
153
+ {
154
+ headers: {
155
+ 'Content-Type': 'application/json',
156
+ 'x-api-key': loginApiEndpoint.apiKey,
157
+ },
158
+ },
159
+ );
160
+ return data.hasPrivilege;
161
+ }
162
+
102
163
  const userSession = await this._SessionService.retrieveUserSession(
103
164
  this.ObjectId,
104
165
  sessionName,
@@ -113,8 +174,7 @@ export class LoginUser extends User implements ILoginUser {
113
174
  }
114
175
 
115
176
  const privileges = systemLogin.privileges;
116
- const hasPrivilege = privileges.includes(privilegeName);
117
- return hasPrivilege;
177
+ return privileges.includes(privilegeName);
118
178
  } catch (error) {
119
179
  throw error;
120
180
  }
@@ -131,6 +191,31 @@ export class LoginUser extends User implements ILoginUser {
131
191
  if (!sessionName) {
132
192
  throw new Error('Session name is not set');
133
193
  }
194
+
195
+ if (LoginUser.isLoginApiEnabled()) {
196
+ const loginApiEndpoint = ComponentConfig.getComponentConfigValue(
197
+ '@tomei/sso',
198
+ 'loginApiEndpoint',
199
+ );
200
+ let payload = {
201
+ SystemCode: systemCode,
202
+ SessionName: sessionName,
203
+ SessionId: sessionId,
204
+ UserId: userId,
205
+ };
206
+ const { data } = await axios.post(
207
+ `${loginApiEndpoint.baseUrl}${loginApiEndpoint.checkSession}`,
208
+ payload,
209
+ {
210
+ headers: {
211
+ 'Content-Type': 'application/json',
212
+ 'x-api-key': loginApiEndpoint.apiKey,
213
+ },
214
+ },
215
+ );
216
+ return data.systemLogin;
217
+ }
218
+
134
219
  const userSession = await this._SessionService.retrieveUserSession(
135
220
  userId,
136
221
  sessionName,
@@ -328,6 +413,31 @@ export class LoginUser extends User implements ILoginUser {
328
413
  throw new Error('Session name is not set in the configuration');
329
414
  }
330
415
 
416
+ if (LoginUser.isLoginApiEnabled()) {
417
+ const loginApiEndpoint = ComponentConfig.getComponentConfigValue(
418
+ '@tomei/sso',
419
+ 'loginApiEndpoint',
420
+ );
421
+ let payload = {
422
+ SystemCode: systemCode,
423
+ SessionName: sessionName,
424
+ SessionId: sessionId,
425
+ UserId: this.ObjectId,
426
+ };
427
+
428
+ await axios.post(
429
+ `${loginApiEndpoint.baseUrl}${loginApiEndpoint.setSession}`,
430
+ payload,
431
+ {
432
+ headers: {
433
+ 'Content-Type': 'application/json',
434
+ 'x-api-key': loginApiEndpoint.apiKey,
435
+ },
436
+ },
437
+ );
438
+ return;
439
+ }
440
+
331
441
  const userSession = await this._SessionService.retrieveUserSession(
332
442
  this.ObjectId,
333
443
  sessionName,
@@ -344,7 +454,6 @@ export class LoginUser extends User implements ILoginUser {
344
454
  system.code === systemCode ? systemLogin : system,
345
455
  );
346
456
  } else {
347
- // if not, add new system login into the userSession
348
457
  const newLogin = {
349
458
  id: systemCode,
350
459
  code: systemCode,
@@ -353,7 +462,6 @@ export class LoginUser extends User implements ILoginUser {
353
462
  };
354
463
  userSession.systemLogins.push(newLogin);
355
464
  }
356
- // then update userSession inside the redis storage with 1 day duration of time-to-live
357
465
  this._SessionService.setUserSession(
358
466
  this.ObjectId,
359
467
  userSession,
@@ -40,6 +40,7 @@ import { UserPasswordHistory } from '../user-password-history/user-password-hist
40
40
 
41
41
  export class User extends UserBase {
42
42
  ObjectId: string;
43
+ V1Id: number;
43
44
  Email: string;
44
45
  private _UserName: string;
45
46
  private _Password: string;
@@ -291,6 +292,7 @@ export class User extends UserBase {
291
292
  // set all the class properties
292
293
  if (userInfo) {
293
294
  this.UserId = userInfo.UserId;
295
+ this.V1Id = userInfo.V1Id;
294
296
  this.UserName = userInfo.UserName;
295
297
  this.FullName = userInfo.FullName;
296
298
  this.IDNo = userInfo.IDNo;
@@ -3071,6 +3073,11 @@ export class User extends UserBase {
3071
3073
  return { expired: false, expiringSoon: false, expiresInDays: null };
3072
3074
  }
3073
3075
 
3076
+ // Accounts authenticated without a local password cannot rotate one here.
3077
+ if (!this.Password) {
3078
+ return { expired: false, expiringSoon: false, expiresInDays: null };
3079
+ }
3080
+
3074
3081
  // Forces first-login change for temp-password/admin-created accounts (AC#4), regardless of password age.
3075
3082
  if (this.NeedToChangePasswordYN === YN.Yes) {
3076
3083
  return { expired: true, expiringSoon: false, expiresInDays: null };
@@ -3092,14 +3099,19 @@ export class User extends UserBase {
3092
3099
  }
3093
3100
 
3094
3101
  const createdAt = latestHistory.CreatedAt;
3102
+ const expiryMonth = createdAt.getUTCMonth() + expiryMonths;
3103
+ const lastDayOfExpiryMonth = new Date(
3104
+ Date.UTC(createdAt.getUTCFullYear(), expiryMonth + 1, 0),
3105
+ ).getUTCDate();
3095
3106
  const expiryDate = new Date(
3096
3107
  Date.UTC(
3097
3108
  createdAt.getUTCFullYear(),
3098
- createdAt.getUTCMonth() + expiryMonths,
3099
- createdAt.getUTCDate(),
3109
+ expiryMonth,
3110
+ Math.min(createdAt.getUTCDate(), lastDayOfExpiryMonth),
3100
3111
  createdAt.getUTCHours(),
3101
3112
  createdAt.getUTCMinutes(),
3102
3113
  createdAt.getUTCSeconds(),
3114
+ createdAt.getUTCMilliseconds(),
3103
3115
  ),
3104
3116
  );
3105
3117
 
@@ -10,6 +10,7 @@ import { v4 as uuidv4 } from 'uuid';
10
10
 
11
11
  export class System extends ObjectBase {
12
12
  ObjectId: string;
13
+ V1Id: number;
13
14
  ObjectName: string;
14
15
  TableName = 'sso_System';
15
16
  ObjectType = 'System';
@@ -49,6 +50,7 @@ export class System extends ObjectBase {
49
50
  private constructor(systemAttr?: ISystemAttr) {
50
51
  super();
51
52
  if (systemAttr) {
53
+ this.V1Id = systemAttr.V1Id;
52
54
  this.SystemCode = systemAttr.SystemCode;
53
55
  this.Name = systemAttr.Name;
54
56
  this.Description = systemAttr?.Description;
@@ -13,6 +13,7 @@ export class SystemPrivilege extends ObjectBase {
13
13
  ObjectType = 'SystemPrivilege';
14
14
  TableName = 'sso_SystemPrivilege';
15
15
  ObjectId: string;
16
+ V1Id: string;
16
17
  PrivilegeCode: string;
17
18
  SystemCode: string;
18
19
  ObjectName: string;
@@ -53,6 +54,7 @@ export class SystemPrivilege extends ObjectBase {
53
54
  private constructor(systemPrivilegeAttr?: ISystemPrivilegeAttr) {
54
55
  super();
55
56
  if (systemPrivilegeAttr) {
57
+ this.V1Id = systemPrivilegeAttr.V1Id;
56
58
  this.SystemPrivilegeId = systemPrivilegeAttr.SystemPrivilegeId;
57
59
  this.PrivilegeCode = systemPrivilegeAttr.PrivilegeCode;
58
60
  this.SystemCode = systemPrivilegeAttr.SystemCode;
@@ -66,6 +68,7 @@ export class SystemPrivilege extends ObjectBase {
66
68
  }
67
69
 
68
70
  setAttributes(systemPrivilegeAttr: ISystemPrivilegeAttr) {
71
+ this.V1Id = systemPrivilegeAttr.V1Id;
69
72
  this.PrivilegeCode = systemPrivilegeAttr.PrivilegeCode;
70
73
  this.SystemPrivilegeId = systemPrivilegeAttr.SystemPrivilegeId;
71
74
  this.SystemCode = systemPrivilegeAttr.SystemCode;
package/src/index.ts CHANGED
@@ -6,3 +6,4 @@ export * from './session';
6
6
  export * from './enum';
7
7
  export * as ssoDb from './database';
8
8
  export * from './types';
9
+ export * from './models/user.entity';
@@ -1,4 +1,5 @@
1
1
  export interface ISystemPrivilegeAttr {
2
+ V1Id?: string;
2
3
  SystemPrivilegeId: string;
3
4
  PrivilegeCode: string;
4
5
  SystemCode: string;
@@ -1,4 +1,5 @@
1
1
  export interface ISystemAttr {
2
+ V1Id?: number;
2
3
  SystemCode: string;
3
4
  Name: string;
4
5
  Description: string;
@@ -24,6 +24,12 @@ import GroupReportingUserModel from './group-reporting-user.entity';
24
24
  updatedAt: 'UpdatedAt',
25
25
  })
26
26
  export default class GroupModel extends Model {
27
+ @Column({
28
+ allowNull: true,
29
+ type: DataType.INTEGER,
30
+ })
31
+ V1Id: number;
32
+
27
33
  @Column({
28
34
  primaryKey: true,
29
35
  allowNull: false,
@@ -74,7 +80,7 @@ export default class GroupModel extends Model {
74
80
 
75
81
  @Column({
76
82
  allowNull: false,
77
- type: DataType.STRING(10),
83
+ type: DataType.STRING(100),
78
84
  })
79
85
  Path: string;
80
86
 
@@ -76,6 +76,12 @@ export default class SystemPrivilegeModel extends Model {
76
76
  @UpdatedAt
77
77
  UpdatedAt: Date;
78
78
 
79
+ @Column({
80
+ allowNull: true,
81
+ type: DataType.STRING(30),
82
+ })
83
+ V1Id: string;
84
+
79
85
  @BelongsTo(() => System)
80
86
  System: System;
81
87
 
@@ -96,6 +96,12 @@ export default class SystemModel extends Model {
96
96
  @UpdatedAt
97
97
  UpdatedAt: Date;
98
98
 
99
+ @Column({
100
+ allowNull: true,
101
+ type: DataType.INTEGER,
102
+ })
103
+ V1Id: number;
104
+
99
105
  @BelongsTo(() => User, 'CreatedById')
100
106
  CreatedByUser: User;
101
107
 
@@ -171,6 +171,12 @@ export default class User extends Model {
171
171
  @UpdatedAt
172
172
  UpdatedAt: Date;
173
173
 
174
+ @Column({
175
+ allowNull: true,
176
+ type: DataType.INTEGER,
177
+ })
178
+ V1Id: number;
179
+
174
180
  @HasOne(() => Staff, 'UserId')
175
181
  Staff: Staff;
176
182