@tomei/sso 0.60.0 → 0.60.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. package/.commitlintrc.json +22 -22
  2. package/.gitlab-ci.yml +16 -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/group/group.spec.ts +79 -79
  9. package/__tests__/unit/components/group-object-privilege/group-object-privilege.spec.ts +88 -88
  10. package/__tests__/unit/components/group-privilege/group-privilege.spec.ts +68 -68
  11. package/__tests__/unit/components/group-reporting-user/group-reporting-user.spec.ts +66 -66
  12. package/__tests__/unit/components/group-system-access/group-system-access.spec.ts +83 -83
  13. package/__tests__/unit/components/login-user/l.spec.ts +746 -746
  14. package/__tests__/unit/components/login-user/login.spec.ts +1164 -1164
  15. package/__tests__/unit/components/password-hash/password-hash.service.spec.ts +31 -31
  16. package/__tests__/unit/components/system/system.spec.ts +254 -254
  17. package/__tests__/unit/components/system-privilege/system-privilege.spec.ts +83 -83
  18. package/__tests__/unit/components/user-group/user-group.spec.ts +86 -86
  19. package/__tests__/unit/components/user-object-privilege/user-object-privilege.spec.ts +78 -78
  20. package/__tests__/unit/components/user-privilege/user-privilege.spec.ts +72 -72
  21. package/__tests__/unit/components/user-system-access/user-system-access.spec.ts +89 -89
  22. package/__tests__/unit/redis-client/redis.service.spec.ts +23 -23
  23. package/__tests__/unit/session/session.service.spec.ts +47 -47
  24. package/__tests__/unit/system-privilege/system-privilage.spec.ts +91 -91
  25. package/coverage/clover.xml +1452 -1452
  26. package/coverage/coverage-final.json +47 -47
  27. package/coverage/lcov-report/base.css +224 -224
  28. package/coverage/lcov-report/block-navigation.js +87 -87
  29. package/coverage/lcov-report/components/group/group.repository.ts.html +117 -117
  30. package/coverage/lcov-report/components/group/group.ts.html +327 -327
  31. package/coverage/lcov-report/components/group/index.html +130 -130
  32. package/coverage/lcov-report/components/group-object-privilege/group-object-privilege.repository.ts.html +117 -117
  33. package/coverage/lcov-report/components/group-object-privilege/group-object-privilege.ts.html +321 -321
  34. package/coverage/lcov-report/components/group-object-privilege/index.html +130 -130
  35. package/coverage/lcov-report/components/group-privilege/group-privilege.repository.ts.html +117 -117
  36. package/coverage/lcov-report/components/group-privilege/group-privilege.ts.html +303 -303
  37. package/coverage/lcov-report/components/group-privilege/index.html +130 -130
  38. package/coverage/lcov-report/components/group-reporting-user/group-reporting-user.repository.ts.html +117 -117
  39. package/coverage/lcov-report/components/group-reporting-user/group-reporting-user.ts.html +327 -327
  40. package/coverage/lcov-report/components/group-reporting-user/index.html +130 -130
  41. package/coverage/lcov-report/components/group-system-access/group-system-access.repository.ts.html +117 -117
  42. package/coverage/lcov-report/components/group-system-access/group-system-access.ts.html +309 -309
  43. package/coverage/lcov-report/components/group-system-access/index.html +130 -130
  44. package/coverage/lcov-report/components/login-history/index.html +115 -115
  45. package/coverage/lcov-report/components/login-history/login-history.repository.ts.html +117 -117
  46. package/coverage/lcov-report/components/login-user/index.html +130 -130
  47. package/coverage/lcov-report/components/login-user/login-user.ts.html +5007 -5007
  48. package/coverage/lcov-report/components/login-user/user.repository.ts.html +117 -117
  49. package/coverage/lcov-report/components/password-hash/index.html +115 -115
  50. package/coverage/lcov-report/components/password-hash/password-hash.service.ts.html +126 -126
  51. package/coverage/lcov-report/components/system/index.html +130 -130
  52. package/coverage/lcov-report/components/system/system.repository.ts.html +117 -117
  53. package/coverage/lcov-report/components/system/system.ts.html +909 -909
  54. package/coverage/lcov-report/components/system-privilege/index.html +130 -130
  55. package/coverage/lcov-report/components/system-privilege/system-privilege.repository.ts.html +120 -120
  56. package/coverage/lcov-report/components/system-privilege/system-privilege.ts.html +390 -390
  57. package/coverage/lcov-report/components/user-group/index.html +130 -130
  58. package/coverage/lcov-report/components/user-group/user-group.repository.ts.html +117 -117
  59. package/coverage/lcov-report/components/user-group/user-group.ts.html +354 -354
  60. package/coverage/lcov-report/components/user-object-privilege/index.html +130 -130
  61. package/coverage/lcov-report/components/user-object-privilege/user-object-privilege.repository.ts.html +117 -117
  62. package/coverage/lcov-report/components/user-object-privilege/user-object-privilege.ts.html +312 -312
  63. package/coverage/lcov-report/components/user-privilege/index.html +130 -130
  64. package/coverage/lcov-report/components/user-privilege/user-privilege.repository.ts.html +117 -117
  65. package/coverage/lcov-report/components/user-privilege/user-privilege.ts.html +306 -306
  66. package/coverage/lcov-report/components/user-system-access/index.html +130 -130
  67. package/coverage/lcov-report/components/user-system-access/user-system-access.repository.ts.html +117 -117
  68. package/coverage/lcov-report/components/user-system-access/user-system-access.ts.html +312 -312
  69. package/coverage/lcov-report/enum/group-type.enum.ts.html +108 -108
  70. package/coverage/lcov-report/enum/index.html +160 -160
  71. package/coverage/lcov-report/enum/index.ts.html +93 -93
  72. package/coverage/lcov-report/enum/user-status.enum.ts.html +105 -105
  73. package/coverage/lcov-report/enum/yn.enum.ts.html +96 -96
  74. package/coverage/lcov-report/index.html +370 -370
  75. package/coverage/lcov-report/models/group-object-privilege.entity.ts.html +333 -333
  76. package/coverage/lcov-report/models/group-privilege.entity.ts.html +315 -315
  77. package/coverage/lcov-report/models/group-reporting-user.entity.ts.html +339 -339
  78. package/coverage/lcov-report/models/group-system-access.entity.ts.html +324 -324
  79. package/coverage/lcov-report/models/group.entity.ts.html +435 -435
  80. package/coverage/lcov-report/models/index.html +310 -310
  81. package/coverage/lcov-report/models/login-history.entity.ts.html +252 -252
  82. package/coverage/lcov-report/models/staff.entity.ts.html +411 -411
  83. package/coverage/lcov-report/models/system-privilege.entity.ts.html +354 -354
  84. package/coverage/lcov-report/models/system.entity.ts.html +423 -423
  85. package/coverage/lcov-report/models/user-group.entity.ts.html +354 -354
  86. package/coverage/lcov-report/models/user-object-privilege.entity.ts.html +330 -330
  87. package/coverage/lcov-report/models/user-privilege.entity.ts.html +315 -315
  88. package/coverage/lcov-report/models/user-system-access.entity.ts.html +315 -315
  89. package/coverage/lcov-report/models/user.entity.ts.html +522 -522
  90. package/coverage/lcov-report/prettify.css +1 -1
  91. package/coverage/lcov-report/prettify.js +2 -2
  92. package/coverage/lcov-report/redis-client/index.html +115 -115
  93. package/coverage/lcov-report/redis-client/redis.service.ts.html +240 -240
  94. package/coverage/lcov-report/session/index.html +115 -115
  95. package/coverage/lcov-report/session/session.service.ts.html +246 -246
  96. package/coverage/lcov-report/sorter.js +196 -196
  97. package/coverage/lcov.info +2490 -2490
  98. package/coverage/test-report.xml +128 -128
  99. package/create-sso-user.sql +39 -39
  100. package/dist/src/components/login-user/user.js +8 -1
  101. package/dist/src/components/login-user/user.js.map +1 -1
  102. package/dist/src/components/user-password-history/index.d.ts +2 -0
  103. package/dist/src/components/user-password-history/index.js +19 -0
  104. package/dist/src/components/user-password-history/index.js.map +1 -0
  105. package/dist/src/components/user-password-history/user-password-history.d.ts +20 -0
  106. package/dist/src/components/user-password-history/user-password-history.js +112 -0
  107. package/dist/src/components/user-password-history/user-password-history.js.map +1 -0
  108. package/dist/src/components/user-password-history/user-password-history.repository.d.ts +8 -0
  109. package/dist/src/components/user-password-history/user-password-history.repository.js +49 -0
  110. package/dist/src/components/user-password-history/user-password-history.repository.js.map +1 -0
  111. package/dist/src/components/user-system-access/user-system-access.js +4 -0
  112. package/dist/src/components/user-system-access/user-system-access.js.map +1 -1
  113. package/dist/src/interfaces/user-password-history.interface.d.ts +6 -0
  114. package/dist/src/interfaces/user-password-history.interface.js +3 -0
  115. package/dist/src/interfaces/user-password-history.interface.js.map +1 -0
  116. package/dist/src/models/user-password-history.d.ts +10 -0
  117. package/dist/src/models/user-password-history.js +60 -0
  118. package/dist/src/models/user-password-history.js.map +1 -0
  119. package/dist/tsconfig.tsbuildinfo +1 -1
  120. package/eslint.config.mjs +58 -58
  121. package/jest.config.js +14 -14
  122. package/migrations/20240314080602-create-user-table.js +124 -124
  123. package/migrations/20240314080603-create-user-group-table.js +85 -85
  124. package/migrations/20240314080604-create-user-user-group-table.js +55 -55
  125. package/migrations/20240314080605-create-login-history-table.js +53 -53
  126. package/migrations/20240527064925-create-system-table.js +78 -78
  127. package/migrations/20240527064926-create-system-privilege-table.js +71 -71
  128. package/migrations/20240527065342-create-group-table.js +93 -93
  129. package/migrations/20240527065633-create-group-reporting-user-table.js +76 -76
  130. package/migrations/20240528011551-create-group-system-access-table.js +72 -72
  131. package/migrations/20240528023018-user-system-access-table.js +75 -75
  132. package/migrations/20240528032229-user-privilege-table.js +76 -76
  133. package/migrations/20240528063003-create-group-privilege-table.js +76 -76
  134. package/migrations/20240528063051-create-group-object-privilege-table.js +84 -84
  135. package/migrations/20240528063107-create-user-object-privilege-table.js +84 -84
  136. package/migrations/20240528063108-create-api-key-table.js +85 -85
  137. package/migrations/20241104104802-create-building-table.js +95 -95
  138. package/migrations/20250108091132-add-area-manager-user-id-to-building-table.js +14 -14
  139. package/migrations/20250108091133-add-passcode-to-user-table.js +36 -36
  140. package/migrations/20250210115636-create-user-reporting-hierarchy.js +76 -76
  141. package/migrations/20250326043818-crate-user-password-history.js +41 -0
  142. package/package.json +90 -90
  143. package/sampledotenv +7 -7
  144. package/sonar-project.properties +22 -22
  145. package/src/components/login-user/user.ts +46 -1
  146. package/src/components/user-password-history/index.ts +2 -0
  147. package/src/components/user-password-history/user-password-history.repository.ts +39 -0
  148. package/src/components/user-password-history/user-password-history.ts +185 -0
  149. package/src/components/user-system-access/user-system-access.ts +5 -0
  150. package/src/interfaces/user-password-history.interface.ts +6 -0
  151. package/src/models/user-password-history.ts +52 -0
  152. package/tsconfig.build.json +5 -5
  153. package/tsconfig.json +23 -23
  154. package/dist/__tests__/unit/components/group-privilege/group-privilege.test.d.ts +0 -1
  155. package/dist/__tests__/unit/components/group-privilege/group-privilege.test.js +0 -71
  156. package/dist/__tests__/unit/components/group-privilege/group-privilege.test.js.map +0 -1
  157. package/dist/__tests__/unit/components/login-user/login-user.spec.d.ts +0 -0
  158. package/dist/__tests__/unit/components/login-user/login-user.spec.js +0 -6
  159. package/dist/__tests__/unit/components/login-user/login-user.spec.js.map +0 -1
package/sampledotenv CHANGED
@@ -1,8 +1,8 @@
1
- DATABASE_URL=
2
- SHADOW_DATABASE_URL=
3
- REDIS_URL=
4
- REDIS_PASSWORD=
5
- SMTP_HOST=
6
- SMTP_PORT=
7
- EMAIL_SENDER=
1
+ DATABASE_URL=
2
+ SHADOW_DATABASE_URL=
3
+ REDIS_URL=
4
+ REDIS_PASSWORD=
5
+ SMTP_HOST=
6
+ SMTP_PORT=
7
+ EMAIL_SENDER=
8
8
  EMAIL_PASSWORD=
@@ -1,23 +1,23 @@
1
- sonar.projectKey=all-tomei-projects_sso
2
- sonar.organization=all-tomei-projects
3
- sonar.exclusions=**/*.js,test-data,dist,coverage, node_modules, __tests__, **/*.spec.ts, __mocks__
4
- sonar.scm.provider=git
5
-
6
- sonar.sources=src
7
- sonar.test=__tests__
8
- sonar.test.inclusions=src/**/*.spec.ts
9
-
10
- sonar.javascript.lcov.reportPaths=./coverage/lcov.info
11
- sonar.testExecutionReportPaths=coverage/test-report.xml
12
- sonar.sourceEnconding=UTF-8
13
-
14
- # This is the name and version displayed in the SonarCloud UI.
15
- #sonar.projectName=sso
16
- #sonar.projectVersion=1.0
17
-
18
-
19
- # Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows.
20
- #sonar.sources=.
21
-
22
- # Encoding of the source code. Default is default system encoding
1
+ sonar.projectKey=all-tomei-projects_sso
2
+ sonar.organization=all-tomei-projects
3
+ sonar.exclusions=**/*.js,test-data,dist,coverage, node_modules, __tests__, **/*.spec.ts, __mocks__
4
+ sonar.scm.provider=git
5
+
6
+ sonar.sources=src
7
+ sonar.test=__tests__
8
+ sonar.test.inclusions=src/**/*.spec.ts
9
+
10
+ sonar.javascript.lcov.reportPaths=./coverage/lcov.info
11
+ sonar.testExecutionReportPaths=coverage/test-report.xml
12
+ sonar.sourceEnconding=UTF-8
13
+
14
+ # This is the name and version displayed in the SonarCloud UI.
15
+ #sonar.projectName=sso
16
+ #sonar.projectVersion=1.0
17
+
18
+
19
+ # Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows.
20
+ #sonar.sources=.
21
+
22
+ # Encoding of the source code. Default is default system encoding
23
23
  #sonar.sourceEncoding=UTF-8
@@ -36,6 +36,7 @@ import { AuthContext } from 'types';
36
36
  import UserModel from '../../models/user.entity';
37
37
  import { UserReportingHierarchyRepository } from '../user-reporting-hierarchy/user-reporting-hierarchy.repository';
38
38
  import { IUserReportingHierarchyAttr } from 'interfaces/user-reporting-hierarchy.interface';
39
+ import { UserPasswordHistory } from '../user-password-history/user-password-history';
39
40
 
40
41
  export class User extends UserBase {
41
42
  ObjectId: string;
@@ -1462,6 +1463,19 @@ export class User extends UserBase {
1462
1463
  await sessionService.deleteAuthorizationCode(hashedSubmittedToken);
1463
1464
  console.log(`Token verified for user: ${userId}`);
1464
1465
 
1466
+ // Part 2: Validate Password History
1467
+ // Call by passing:
1468
+ // dbTransaction
1469
+ // UserId: loginUser.ObjectId
1470
+ // Password: password
1471
+ const passwordHashService = new PasswordHashService();
1472
+ await UserPasswordHistory.validate(
1473
+ dbTransaction,
1474
+ parseInt(userId),
1475
+ password,
1476
+ passwordHashService,
1477
+ );
1478
+
1465
1479
  //Instantiate user
1466
1480
  const user = await User.init(
1467
1481
  sessionService,
@@ -1473,7 +1487,7 @@ export class User extends UserBase {
1473
1487
  await User.setPassword(dbTransaction, user, password);
1474
1488
 
1475
1489
  //Update user record
1476
- await User._Repository.update(
1490
+ let userData = await User._Repository.update(
1477
1491
  {
1478
1492
  Password: user._Password,
1479
1493
  DefaultPasswordChangedYN: YN.Yes,
@@ -1486,6 +1500,12 @@ export class User extends UserBase {
1486
1500
  transaction: dbTransaction,
1487
1501
  },
1488
1502
  );
1503
+
1504
+ await UserPasswordHistory.create(
1505
+ dbTransaction,
1506
+ parseInt(userId),
1507
+ userData.Password,
1508
+ );
1489
1509
  } catch (error) {
1490
1510
  throw error;
1491
1511
  }
@@ -1603,6 +1623,12 @@ export class User extends UserBase {
1603
1623
  userInfo,
1604
1624
  );
1605
1625
 
1626
+ await UserPasswordHistory.create(
1627
+ dbTransaction,
1628
+ userToBeCreated.UserId,
1629
+ userToBeCreated.Password,
1630
+ );
1631
+
1606
1632
  //Part 5: Record Create User Activity
1607
1633
  const activity = new Activity();
1608
1634
  activity.ActivityId = activity.createId();
@@ -2845,6 +2871,19 @@ export class User extends UserBase {
2845
2871
  ) {
2846
2872
  try {
2847
2873
  const passwordHashService = new PasswordHashService();
2874
+
2875
+ // Part 2: Validate Password History
2876
+ // Call by passing:
2877
+ // dbTransaction
2878
+ // UserId: loginUser.ObjectId
2879
+ // Password: password
2880
+ await UserPasswordHistory.validate(
2881
+ dbTransaction,
2882
+ loginUser.UserId,
2883
+ newPassword,
2884
+ passwordHashService,
2885
+ );
2886
+
2848
2887
  const isPasswordValid = await passwordHashService.verify(
2849
2888
  oldPassword,
2850
2889
  this.Password,
@@ -2929,6 +2968,12 @@ export class User extends UserBase {
2929
2968
  },
2930
2969
  );
2931
2970
 
2971
+ await UserPasswordHistory.create(
2972
+ dbTransaction,
2973
+ this.UserId,
2974
+ this.Password,
2975
+ );
2976
+
2932
2977
  // Record update activity using Activity class create method.
2933
2978
  const activity = new Activity();
2934
2979
  activity.ActivityId = activity.createId();
@@ -0,0 +1,2 @@
1
+ export * from './user-password-history';
2
+ export * from './user-password-history.repository';
@@ -0,0 +1,39 @@
1
+ import { RepositoryBase, IRepositoryBase } from '@tomei/general';
2
+ import UserPasswordHistoryModel from '../../models/user-password-history';
3
+ import { Op } from 'sequelize';
4
+
5
+ export class UserPasswordHistoryRepository
6
+ extends RepositoryBase<UserPasswordHistoryModel>
7
+ implements IRepositoryBase<UserPasswordHistoryModel>
8
+ {
9
+ constructor() {
10
+ super(UserPasswordHistoryModel);
11
+ }
12
+
13
+ async findByPk(id: string, options?: any): Promise<UserPasswordHistoryModel> {
14
+ return await UserPasswordHistoryModel.findByPk(parseInt(id), options);
15
+ }
16
+
17
+ async destroy(HistoryId: number, dbTransaction: any): Promise<void> {
18
+ await UserPasswordHistoryModel.destroy({
19
+ where: {
20
+ HistoryId: HistoryId,
21
+ },
22
+ transaction: dbTransaction,
23
+ });
24
+ }
25
+
26
+ async destroyMultiple(
27
+ HistoryIdList: number[],
28
+ dbTransaction: any,
29
+ ): Promise<void> {
30
+ await UserPasswordHistoryModel.destroy({
31
+ where: {
32
+ HistoryId: {
33
+ [Op.in]: HistoryIdList,
34
+ },
35
+ },
36
+ transaction: dbTransaction,
37
+ });
38
+ }
39
+ }
@@ -0,0 +1,185 @@
1
+ import { ClassError, ObjectBase } from '@tomei/general';
2
+ import { ComponentConfig } from '@tomei/config';
3
+ import { IUserPasswordHistoryAttr } from '../../interfaces/user-password-history.interface';
4
+ import { UserPasswordHistoryRepository } from './user-password-history.repository';
5
+ import { PasswordHashService } from '../../components/password-hash';
6
+
7
+ export class UserPasswordHistory
8
+ extends ObjectBase
9
+ implements IUserPasswordHistoryAttr
10
+ {
11
+ ObjectId: string;
12
+ ObjectName: string;
13
+ ObjectType = 'UserPasswordHistory';
14
+ TableName = 'sso_UserPasswordHistory';
15
+ UserId: number;
16
+ PasswordHash: string;
17
+ private _CreatedAt: Date;
18
+
19
+ private static _Repo = new UserPasswordHistoryRepository();
20
+
21
+ get HistoryId(): number {
22
+ return parseInt(this.ObjectId);
23
+ }
24
+
25
+ set HistoryId(value: number) {
26
+ this.ObjectId = value.toString();
27
+ }
28
+
29
+ get CreatedAt(): Date {
30
+ return this._CreatedAt;
31
+ }
32
+
33
+ private constructor(params?: IUserPasswordHistoryAttr) {
34
+ super();
35
+ if (params) {
36
+ this.ObjectId = params.HistoryId.toString();
37
+ this.UserId = params.UserId;
38
+ this.PasswordHash = params.PasswordHash;
39
+ this._CreatedAt = params.CreatedAt;
40
+ }
41
+ }
42
+
43
+ public static async init(
44
+ historyId?: number,
45
+ dbTransaction?: any,
46
+ ): Promise<UserPasswordHistory> {
47
+ try {
48
+ if (historyId) {
49
+ const data = await UserPasswordHistory._Repo.findByPk(
50
+ historyId.toString(),
51
+ dbTransaction,
52
+ );
53
+ if (!data) {
54
+ throw new ClassError(
55
+ 'UserPasswordHistory',
56
+ 'UserPasswordHistoryErrMsg01',
57
+ 'UserPasswordHistory not found',
58
+ 'init',
59
+ 400,
60
+ );
61
+ }
62
+
63
+ return new UserPasswordHistory(data.get({ plain: true }));
64
+ }
65
+ return new UserPasswordHistory();
66
+ } catch (error) {
67
+ throw error;
68
+ }
69
+ }
70
+
71
+ public static async validate(
72
+ dbTransaction: any,
73
+ UserId: number,
74
+ Password: string,
75
+ passwordHashService: PasswordHashService,
76
+ ): Promise<void> {
77
+ // This method used to check if password entered is valid by checking previous password history
78
+ try {
79
+ // Part 1-2: Retrieve password history policy by using component config, call ComponentConfig. by passing:
80
+ // - ComponentName: "@tomei/sso"
81
+ // - ConfigKey: "passwordHistory"
82
+ // If no password history set, use default value 3
83
+
84
+ const passwordHistoryPolicy =
85
+ ComponentConfig.getComponentConfigValue(
86
+ '@tomei/sso',
87
+ 'passwordHistory',
88
+ ) || 3;
89
+
90
+ // Part 3-4: Retrieve records from the table by using class._repo findAll() by passing:
91
+ // where: { UserId: params.UserId }
92
+ // order: [['CreatedAt', 'DESC']]
93
+ // limit: passwordHistory count above.
94
+ // If no record found, return null.
95
+
96
+ let passwordHistory = await UserPasswordHistory._Repo.findAll({
97
+ where: { UserId: UserId },
98
+ order: [['CreatedAt', 'DESC']],
99
+ limit: passwordHistoryPolicy,
100
+ transaction: dbTransaction,
101
+ });
102
+
103
+ if (passwordHistory?.length < 1) {
104
+ return null;
105
+ } else {
106
+ // Part 5: If record found, map each record to compare params.Password and record.PasswordHash using the params.passwordHashService. If match, stop the mapping, and return ClassError:
107
+ // ClassName: "UserPasswordHistory"
108
+ // MethodName: "validate"
109
+ // MessageCode: "UserPasswordHistory01"
110
+ // Message: You cannot reuse your last ${passwordHistory} passwords. Please choose a new and unique password.
111
+ for (let index = 0; index < passwordHistory.length; index++) {
112
+ const isPasswordSame = await passwordHashService.verify(
113
+ Password,
114
+ passwordHistory[index].PasswordHash,
115
+ );
116
+
117
+ if (isPasswordSame) {
118
+ throw new ClassError(
119
+ 'UserPasswordHistory',
120
+ 'UserPasswordHistory01',
121
+ `You cannot reuse your last ${passwordHistoryPolicy} passwords. Please choose a new and unique password.`,
122
+ 'validate',
123
+ 403,
124
+ );
125
+ }
126
+ }
127
+ }
128
+ } catch (error) {
129
+ throw error;
130
+ }
131
+ }
132
+
133
+ public static async create(
134
+ dbTransaction: any,
135
+ UserId: number,
136
+ PasswordHash: string,
137
+ ): Promise<void> {
138
+ // This method used to check if password entered is valid by checking previous password history
139
+ try {
140
+ // Part 1-2: Retrieve password history policy by using component config, call ComponentConfig. by passing:
141
+ // - ComponentName: "@tomei/sso"
142
+ // - ConfigKey: "passwordHistory"
143
+ // If no password history set, use default value 3
144
+
145
+ const passwordHistoryPolicy =
146
+ ComponentConfig.getComponentConfigValue(
147
+ '@tomei/sso',
148
+ 'passwordHistory',
149
+ ) || 3;
150
+
151
+ // Part 3: Insert new password history by calling class _repo create() method.
152
+ let passwordHistory = await UserPasswordHistory._Repo.create(
153
+ {
154
+ UserId: UserId,
155
+ PasswordHash: PasswordHash,
156
+ },
157
+ {
158
+ transaction: dbTransaction,
159
+ },
160
+ );
161
+
162
+ // Part 3: When inserted successfully, retrieve all the password history for the user to check
163
+ // how many previous password records. If records more than the passwordHistory count from
164
+ // config. Remove the oldest record.
165
+ if (passwordHistory) {
166
+ let passwordHistoryList = await UserPasswordHistory._Repo.findAll({
167
+ where: { UserId: UserId },
168
+ order: [['CreatedAt', 'DESC']],
169
+ transaction: dbTransaction,
170
+ });
171
+
172
+ if (passwordHistoryList.length > passwordHistoryPolicy) {
173
+ let deleteList = passwordHistoryList.slice(passwordHistoryPolicy);
174
+ let historyIdList = deleteList.map((record) => record.HistoryId);
175
+ await UserPasswordHistory._Repo.destroyMultiple(
176
+ historyIdList,
177
+ dbTransaction,
178
+ );
179
+ }
180
+ }
181
+ } catch (error) {
182
+ throw error;
183
+ }
184
+ }
185
+ }
@@ -13,6 +13,8 @@ import { ActionEnum, Activity } from '@tomei/activity-history';
13
13
  import { Op } from 'sequelize';
14
14
  import { UserPrivilegeRepository } from '../user-privilege/user-privilege.repository';
15
15
  import UserGroupModel from 'models/user-group.entity';
16
+ import { UserStatus } from '../../enum';
17
+
16
18
  export class UserSystemAccess extends ObjectBase {
17
19
  ObjectType = 'UserSystemAccess';
18
20
  TableName = 'sso_UserSystemAccess';
@@ -243,6 +245,9 @@ export class UserSystemAccess extends ObjectBase {
243
245
  },
244
246
  {
245
247
  model: UserModel,
248
+ where: {
249
+ Status: UserStatus.ACTIVE,
250
+ },
246
251
  as: 'User',
247
252
  attributes: ['UserId', 'FullName'],
248
253
  },
@@ -0,0 +1,6 @@
1
+ export interface IUserPasswordHistoryAttr {
2
+ HistoryId: number;
3
+ UserId: number;
4
+ PasswordHash: string;
5
+ CreatedAt: Date;
6
+ }
@@ -0,0 +1,52 @@
1
+ import {
2
+ BelongsTo,
3
+ Column,
4
+ CreatedAt,
5
+ DataType,
6
+ ForeignKey,
7
+ Model,
8
+ Table,
9
+ } from 'sequelize-typescript';
10
+ import User from './user.entity';
11
+ import { IUserPasswordHistoryAttr } from '../interfaces/user-password-history.interface';
12
+
13
+ @Table({
14
+ tableName: 'sso_UserPasswordHistory',
15
+ timestamps: true,
16
+ createdAt: 'CreatedAt',
17
+ updatedAt: 'UpdatedAt',
18
+ })
19
+ export default class UserPasswordHistoryModel
20
+ extends Model
21
+ implements IUserPasswordHistoryAttr
22
+ {
23
+ @Column({
24
+ primaryKey: true,
25
+ allowNull: false,
26
+ type: DataType.INTEGER,
27
+ autoIncrement: true,
28
+ })
29
+ HistoryId: number;
30
+
31
+ @ForeignKey(() => User)
32
+ @Column({
33
+ allowNull: false,
34
+ type: DataType.INTEGER,
35
+ })
36
+ UserId: number;
37
+
38
+ @Column({
39
+ allowNull: false,
40
+ type: DataType.STRING(225),
41
+ })
42
+ PasswordHash: string;
43
+
44
+ @CreatedAt
45
+ CreatedAt: Date;
46
+
47
+ @BelongsTo(() => User, {
48
+ as: 'User',
49
+ foreignKey: 'UserId',
50
+ })
51
+ User: User;
52
+ }
@@ -1,6 +1,6 @@
1
- {
2
- "extends": "./tsconfig.json",
3
- "include": ["**/*.ts"],
4
- "exclude": ["node_modules", "__tests__", "dist", "**/*spec.ts"]
5
- }
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "include": ["**/*.ts"],
4
+ "exclude": ["node_modules", "__tests__", "dist", "**/*spec.ts"]
5
+ }
6
6
 
package/tsconfig.json CHANGED
@@ -1,24 +1,24 @@
1
- {
2
- "compilerOptions": {
3
- "module": "commonjs",
4
- "declaration": true,
5
- "removeComments": true,
6
- "emitDecoratorMetadata": true,
7
- "experimentalDecorators": true,
8
- "allowSyntheticDefaultImports": true,
9
- "moduleResolution": "node",
10
- "target": "es6",
11
- "sourceMap": true,
12
- "outDir": "./dist",
13
- "baseUrl": "./src",
14
- "rootDir": "./",
15
- "incremental": true,
16
- "skipLibCheck": true,
17
- "noImplicitAny": false,
18
- "strictBindCallApply": false,
19
- "forceConsistentCasingInFileNames": false,
20
- "noFallthroughCasesInSwitch": false,
21
- "strictNullChecks": false,
22
- },
23
- "exclude": ["node_modules", "dist"]
1
+ {
2
+ "compilerOptions": {
3
+ "module": "commonjs",
4
+ "declaration": true,
5
+ "removeComments": true,
6
+ "emitDecoratorMetadata": true,
7
+ "experimentalDecorators": true,
8
+ "allowSyntheticDefaultImports": true,
9
+ "moduleResolution": "node",
10
+ "target": "es6",
11
+ "sourceMap": true,
12
+ "outDir": "./dist",
13
+ "baseUrl": "./src",
14
+ "rootDir": "./",
15
+ "incremental": true,
16
+ "skipLibCheck": true,
17
+ "noImplicitAny": false,
18
+ "strictBindCallApply": false,
19
+ "forceConsistentCasingInFileNames": false,
20
+ "noFallthroughCasesInSwitch": false,
21
+ "strictNullChecks": false,
22
+ },
23
+ "exclude": ["node_modules", "dist"]
24
24
  }
@@ -1,71 +0,0 @@
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
- };
11
- Object.defineProperty(exports, "__esModule", { value: true });
12
- const group_privilege_1 = require("../../../../src/components/group-privilege/group-privilege");
13
- const group_privilege_repository_1 = require("../../../../src/components/group-privilege/group-privilege.repository");
14
- const general_1 = require("@tomei/general");
15
- describe('GroupPrivilege', () => {
16
- afterEach(() => {
17
- jest.restoreAllMocks();
18
- });
19
- it('should initialize with GroupPrivilegeAttr', () => __awaiter(void 0, void 0, void 0, function* () {
20
- const mockGroupPrivilegeAttr = {
21
- GroupPrivilegeId: 1,
22
- GroupCode: 'group1',
23
- PrivilegeCode: 'privilege1',
24
- Status: 'active',
25
- CreatedAt: new Date(),
26
- UpdatedAt: new Date(),
27
- CreatedById: 1,
28
- UpdatedById: 1,
29
- };
30
- const findOneMock = jest
31
- .spyOn(group_privilege_repository_1.GroupPrivilegeRepository.prototype, 'findOne')
32
- .mockResolvedValueOnce({ get: jest.fn().mockReturnValue(mockGroupPrivilegeAttr) });
33
- const groupPrivilege = yield group_privilege_1.GroupPrivilege.init(null, 1);
34
- expect(findOneMock).toBeCalledWith({
35
- where: { GroupPrivilegeId: 1 },
36
- transaction: null,
37
- });
38
- expect(groupPrivilege).toBeInstanceOf(group_privilege_1.GroupPrivilege);
39
- expect(groupPrivilege.GroupPrivilegeId).toEqual(mockGroupPrivilegeAttr.GroupPrivilegeId);
40
- expect(groupPrivilege.GroupCode).toEqual(mockGroupPrivilegeAttr.GroupCode);
41
- expect(groupPrivilege.PrivilegeCode).toEqual(mockGroupPrivilegeAttr.PrivilegeCode);
42
- expect(groupPrivilege.Status).toEqual(mockGroupPrivilegeAttr.Status);
43
- expect(groupPrivilege.CreatedAt).toEqual(mockGroupPrivilegeAttr.CreatedAt);
44
- expect(groupPrivilege.UpdatedAt).toEqual(mockGroupPrivilegeAttr.UpdatedAt);
45
- expect(groupPrivilege.CreatedById).toEqual(mockGroupPrivilegeAttr.CreatedById);
46
- expect(groupPrivilege.UpdatedById).toEqual(mockGroupPrivilegeAttr.UpdatedById);
47
- }));
48
- it('should throw ClassError when GroupPrivilegeAttr is not found', () => __awaiter(void 0, void 0, void 0, function* () {
49
- const findOneMock = jest
50
- .spyOn(group_privilege_repository_1.GroupPrivilegeRepository.prototype, 'findOne')
51
- .mockResolvedValueOnce(null);
52
- yield expect(group_privilege_1.GroupPrivilege.init(null, 1)).rejects.toThrow(general_1.ClassError);
53
- expect(findOneMock).toBeCalledWith({
54
- where: { GroupPrivilegeId: 1 },
55
- transaction: null,
56
- });
57
- }));
58
- it('should initialize with default values', () => __awaiter(void 0, void 0, void 0, function* () {
59
- const groupPrivilege = yield group_privilege_1.GroupPrivilege.init();
60
- expect(groupPrivilege).toBeInstanceOf(group_privilege_1.GroupPrivilege);
61
- expect(groupPrivilege.GroupPrivilegeId).toBeUndefined();
62
- expect(groupPrivilege.GroupCode).toBeUndefined();
63
- expect(groupPrivilege.PrivilegeCode).toBeUndefined();
64
- expect(groupPrivilege.Status).toBeUndefined();
65
- expect(groupPrivilege.CreatedAt).toBeUndefined();
66
- expect(groupPrivilege.UpdatedAt).toBeUndefined();
67
- expect(groupPrivilege.CreatedById).toBeUndefined();
68
- expect(groupPrivilege.UpdatedById).toBeUndefined();
69
- }));
70
- });
71
- //# sourceMappingURL=group-privilege.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"group-privilege.test.js","sourceRoot":"","sources":["../../../../../__tests__/unit/components/group-privilege/group-privilege.test.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,gGAA4F;AAC5F,sHAAiH;AACjH,4CAA4C;AAE5C,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,eAAe,EAAE,CAAC;IACzB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2CAA2C,EAAE,GAAS,EAAE;QACzD,MAAM,sBAAsB,GAAG;YAC7B,gBAAgB,EAAE,CAAC;YACnB,SAAS,EAAE,QAAQ;YACnB,aAAa,EAAE,YAAY;YAC3B,MAAM,EAAE,QAAQ;YAChB,SAAS,EAAE,IAAI,IAAI,EAAE;YACrB,SAAS,EAAE,IAAI,IAAI,EAAE;YACrB,WAAW,EAAE,CAAC;YACd,WAAW,EAAE,CAAC;SACf,CAAC;QAEF,MAAM,WAAW,GAAG,IAAI;aACrB,KAAK,CAAC,qDAAwB,CAAC,SAAS,EAAE,SAAS,CAAC;aACpD,qBAAqB,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,sBAAsB,CAAC,EAAS,CAAC,CAAC;QAE5F,MAAM,cAAc,GAAG,MAAM,gCAAc,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAE1D,MAAM,CAAC,WAAW,CAAC,CAAC,cAAc,CAAC;YACjC,KAAK,EAAE,EAAE,gBAAgB,EAAE,CAAC,EAAE;YAC9B,WAAW,EAAE,IAAI;SAClB,CAAC,CAAC;QACH,MAAM,CAAC,cAAc,CAAC,CAAC,cAAc,CAAC,gCAAc,CAAC,CAAC;QACtD,MAAM,CAAC,cAAc,CAAC,gBAAgB,CAAC,CAAC,OAAO,CAAC,sBAAsB,CAAC,gBAAgB,CAAC,CAAC;QACzF,MAAM,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC;QAC3E,MAAM,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC,OAAO,CAAC,sBAAsB,CAAC,aAAa,CAAC,CAAC;QACnF,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,sBAAsB,CAAC,MAAM,CAAC,CAAC;QACrE,MAAM,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC;QAC3E,MAAM,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC;QAC3E,MAAM,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,sBAAsB,CAAC,WAAW,CAAC,CAAC;QAC/E,MAAM,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,sBAAsB,CAAC,WAAW,CAAC,CAAC;IACjF,CAAC,CAAA,CAAC,CAAC;IAEH,EAAE,CAAC,8DAA8D,EAAE,GAAS,EAAE;QAC5E,MAAM,WAAW,GAAG,IAAI;aACrB,KAAK,CAAC,qDAAwB,CAAC,SAAS,EAAE,SAAS,CAAC;aACpD,qBAAqB,CAAC,IAAI,CAAC,CAAC;QAE/B,MAAM,MAAM,CAAC,gCAAc,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,oBAAU,CAAC,CAAC;QAEvE,MAAM,CAAC,WAAW,CAAC,CAAC,cAAc,CAAC;YACjC,KAAK,EAAE,EAAE,gBAAgB,EAAE,CAAC,EAAE;YAC9B,WAAW,EAAE,IAAI;SAClB,CAAC,CAAC;IACL,CAAC,CAAA,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAS,EAAE;QACrD,MAAM,cAAc,GAAG,MAAM,gCAAc,CAAC,IAAI,EAAE,CAAC;QAEnD,MAAM,CAAC,cAAc,CAAC,CAAC,cAAc,CAAC,gCAAc,CAAC,CAAC;QACtD,MAAM,CAAC,cAAc,CAAC,gBAAgB,CAAC,CAAC,aAAa,EAAE,CAAC;QACxD,MAAM,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC,aAAa,EAAE,CAAC;QACjD,MAAM,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC,aAAa,EAAE,CAAC;QACrD,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,aAAa,EAAE,CAAC;QAC9C,MAAM,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC,aAAa,EAAE,CAAC;QACjD,MAAM,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC,aAAa,EAAE,CAAC;QACjD,MAAM,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC,aAAa,EAAE,CAAC;QACnD,MAAM,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC,aAAa,EAAE,CAAC;IACrD,CAAC,CAAA,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -1,6 +0,0 @@
1
- describe('LoginUser', () => {
2
- it('should be true', () => {
3
- expect(true).toBe(true);
4
- });
5
- });
6
- //# sourceMappingURL=login-user.spec.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"login-user.spec.js","sourceRoot":"","sources":["../../../../../__tests__/unit/components/login-user/login-user.spec.ts"],"names":[],"mappings":"AAuuBA,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE;IACvB,EAAE,CAAC,gBAAgB,EAAE,GAAG,EAAE;QACxB,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}