@drax/identity-back 3.13.0 → 3.15.0

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 (150) hide show
  1. package/dist/config/PasswordPolicyConfig.js +14 -0
  2. package/dist/controllers/UserApiKeyController.js +8 -119
  3. package/dist/controllers/UserController.js +10 -0
  4. package/dist/factory/PasswordPolicyResolverFactory.js +9 -0
  5. package/dist/factory/PasswordPolicyServiceFactory.js +27 -0
  6. package/dist/factory/UserPasswordHistoryServiceFactory.js +25 -0
  7. package/dist/factory/UserServiceFactory.js +3 -1
  8. package/dist/graphql/resolvers/user-api-key.resolvers.js +2 -3
  9. package/dist/index.js +20 -8
  10. package/dist/interfaces/IUserPasswordHistory.js +1 -0
  11. package/dist/interfaces/IUserPasswordHistoryRepository.js +1 -0
  12. package/dist/middleware/apiKeyMiddleware.js +1 -1
  13. package/dist/middleware/rbacMiddleware.js +1 -1
  14. package/dist/models/UserPasswordHistoryModel.js +30 -0
  15. package/dist/permissions/UserApiKeyPermissions.js +1 -2
  16. package/dist/policies/defaultPasswordPolicy.js +12 -0
  17. package/dist/repository/mongo/UserPasswordHistoryMongoRepository.js +20 -0
  18. package/dist/repository/sqlite/UserPasswordHistorySqliteRepository.js +38 -0
  19. package/dist/resolver/PasswordPolicyResolver.js +27 -0
  20. package/dist/routes/UserRoutes.js +10 -0
  21. package/dist/schemas/PasswordPolicySchema.js +18 -0
  22. package/dist/schemas/RegisterSchema.js +1 -3
  23. package/dist/schemas/UserSchema.js +1 -3
  24. package/dist/security/constants/defaultPasswordPolicy.js +12 -0
  25. package/dist/security/interfaces/IPasswordPolicy.js +1 -0
  26. package/dist/security/interfaces/IPasswordPolicyProjectContext.js +1 -0
  27. package/dist/security/schemas/PasswordPolicySchema.js +18 -0
  28. package/dist/security/services/PasswordPolicyResolver.js +21 -0
  29. package/dist/security/services/PasswordPolicyService.js +147 -0
  30. package/dist/security/utils/PasswordPolicySchemaFactory.js +36 -0
  31. package/dist/security/utils/getPasswordEnvPolicy.js +19 -0
  32. package/dist/services/PasswordPolicyService.js +147 -0
  33. package/dist/services/UserPasswordHistoryService.js +18 -0
  34. package/dist/services/UserService.js +34 -9
  35. package/dist/setup/LoadIdentityConfigFromEnv.js +10 -0
  36. package/dist/setup/SetProjectPasswordPolicy.js +7 -0
  37. package/dist/utils/PasswordPolicySchemaFactory.js +36 -0
  38. package/dist/utils/getPasswordEnvPolicy.js +19 -0
  39. package/docs/password-policy.md +33 -0
  40. package/package.json +6 -6
  41. package/src/config/PasswordPolicyConfig.ts +14 -0
  42. package/src/controllers/UserApiKeyController.ts +15 -129
  43. package/src/controllers/UserController.ts +10 -1
  44. package/src/factory/PasswordPolicyResolverFactory.ts +14 -0
  45. package/src/factory/PasswordPolicyServiceFactory.ts +38 -0
  46. package/src/factory/UserPasswordHistoryServiceFactory.ts +31 -0
  47. package/src/factory/UserServiceFactory.ts +7 -1
  48. package/src/graphql/resolvers/user-api-key.resolvers.ts +2 -3
  49. package/src/index.ts +28 -3
  50. package/src/interfaces/IUserPasswordHistory.ts +21 -0
  51. package/src/interfaces/IUserPasswordHistoryRepository.ts +8 -0
  52. package/src/middleware/apiKeyMiddleware.ts +1 -1
  53. package/src/middleware/rbacMiddleware.ts +1 -1
  54. package/src/models/UserPasswordHistoryModel.ts +42 -0
  55. package/src/permissions/UserApiKeyPermissions.ts +1 -2
  56. package/src/policies/defaultPasswordPolicy.ts +17 -0
  57. package/src/repository/mongo/UserPasswordHistoryMongoRepository.ts +25 -0
  58. package/src/repository/sqlite/UserPasswordHistorySqliteRepository.ts +47 -0
  59. package/src/resolver/PasswordPolicyResolver.ts +33 -0
  60. package/src/routes/UserRoutes.ts +11 -0
  61. package/src/schemas/PasswordPolicySchema.ts +29 -0
  62. package/src/schemas/RegisterSchema.ts +1 -3
  63. package/src/schemas/UserSchema.ts +1 -3
  64. package/src/services/PasswordPolicyService.ts +184 -0
  65. package/src/services/UserPasswordHistoryService.ts +23 -0
  66. package/src/services/UserService.ts +38 -9
  67. package/src/setup/LoadIdentityConfigFromEnv.ts +11 -0
  68. package/src/setup/SetProjectPasswordPolicy.ts +12 -0
  69. package/src/utils/PasswordPolicySchemaFactory.ts +47 -0
  70. package/src/utils/getPasswordEnvPolicy.ts +25 -0
  71. package/test/data-obj/users/root-mongo-user.ts +1 -1
  72. package/test/data-obj/users/root-sqlite-user.ts +1 -1
  73. package/test/endpoints/data/users-data.ts +3 -3
  74. package/test/endpoints/password-policy-route.test.ts +33 -0
  75. package/test/endpoints/user-route.test.ts +17 -4
  76. package/test/security/password-policy-resolver.test.ts +55 -0
  77. package/test/security/password-policy-schema-factory.test.ts +40 -0
  78. package/test/services/user-service.test.ts +218 -31
  79. package/test/setup/TestSetup.ts +22 -4
  80. package/test/setup/data/basic-user.ts +1 -1
  81. package/test/setup/data/root-user.ts +1 -1
  82. package/tsconfig.tsbuildinfo +1 -1
  83. package/types/config/PasswordPolicyConfig.d.ts +14 -0
  84. package/types/config/PasswordPolicyConfig.d.ts.map +1 -0
  85. package/types/controllers/UserApiKeyController.d.ts +10 -6
  86. package/types/controllers/UserApiKeyController.d.ts.map +1 -1
  87. package/types/controllers/UserController.d.ts +1 -0
  88. package/types/controllers/UserController.d.ts.map +1 -1
  89. package/types/factory/PasswordPolicyResolverFactory.d.ts +4 -0
  90. package/types/factory/PasswordPolicyResolverFactory.d.ts.map +1 -0
  91. package/types/factory/PasswordPolicyServiceFactory.d.ts +4 -0
  92. package/types/factory/PasswordPolicyServiceFactory.d.ts.map +1 -0
  93. package/types/factory/UserPasswordHistoryServiceFactory.d.ts +4 -0
  94. package/types/factory/UserPasswordHistoryServiceFactory.d.ts.map +1 -0
  95. package/types/factory/UserServiceFactory.d.ts.map +1 -1
  96. package/types/graphql/resolvers/user-api-key.resolvers.d.ts.map +1 -1
  97. package/types/index.d.ts +15 -2
  98. package/types/index.d.ts.map +1 -1
  99. package/types/interfaces/IUserPasswordHistory.d.ts +17 -0
  100. package/types/interfaces/IUserPasswordHistory.d.ts.map +1 -0
  101. package/types/interfaces/IUserPasswordHistoryRepository.d.ts +7 -0
  102. package/types/interfaces/IUserPasswordHistoryRepository.d.ts.map +1 -0
  103. package/types/models/UserPasswordHistoryModel.d.ts +15 -0
  104. package/types/models/UserPasswordHistoryModel.d.ts.map +1 -0
  105. package/types/permissions/UserApiKeyPermissions.d.ts +1 -2
  106. package/types/permissions/UserApiKeyPermissions.d.ts.map +1 -1
  107. package/types/permissions/index.d.ts +0 -2
  108. package/types/permissions/index.d.ts.map +1 -1
  109. package/types/policies/defaultPasswordPolicy.d.ts +4 -0
  110. package/types/policies/defaultPasswordPolicy.d.ts.map +1 -0
  111. package/types/repository/mongo/UserPasswordHistoryMongoRepository.d.ts +10 -0
  112. package/types/repository/mongo/UserPasswordHistoryMongoRepository.d.ts.map +1 -0
  113. package/types/repository/sqlite/UserPasswordHistorySqliteRepository.d.ts +25 -0
  114. package/types/repository/sqlite/UserPasswordHistorySqliteRepository.d.ts.map +1 -0
  115. package/types/resolver/PasswordPolicyResolver.d.ts +10 -0
  116. package/types/resolver/PasswordPolicyResolver.d.ts.map +1 -0
  117. package/types/routes/UserRoutes.d.ts.map +1 -1
  118. package/types/schemas/PasswordPolicySchema.d.ts +25 -0
  119. package/types/schemas/PasswordPolicySchema.d.ts.map +1 -0
  120. package/types/schemas/RegisterSchema.d.ts.map +1 -1
  121. package/types/schemas/UserSchema.d.ts.map +1 -1
  122. package/types/security/constants/defaultPasswordPolicy.d.ts +4 -0
  123. package/types/security/constants/defaultPasswordPolicy.d.ts.map +1 -0
  124. package/types/security/interfaces/IPasswordPolicy.d.ts +13 -0
  125. package/types/security/interfaces/IPasswordPolicy.d.ts.map +1 -0
  126. package/types/security/interfaces/IPasswordPolicyProjectContext.d.ts +6 -0
  127. package/types/security/interfaces/IPasswordPolicyProjectContext.d.ts.map +1 -0
  128. package/types/security/schemas/PasswordPolicySchema.d.ts +25 -0
  129. package/types/security/schemas/PasswordPolicySchema.d.ts.map +1 -0
  130. package/types/security/services/PasswordPolicyResolver.d.ts +9 -0
  131. package/types/security/services/PasswordPolicyResolver.d.ts.map +1 -0
  132. package/types/security/services/PasswordPolicyService.d.ts +35 -0
  133. package/types/security/services/PasswordPolicyService.d.ts.map +1 -0
  134. package/types/security/utils/PasswordPolicySchemaFactory.d.ts +9 -0
  135. package/types/security/utils/PasswordPolicySchemaFactory.d.ts.map +1 -0
  136. package/types/security/utils/getPasswordEnvPolicy.d.ts +5 -0
  137. package/types/security/utils/getPasswordEnvPolicy.d.ts.map +1 -0
  138. package/types/services/PasswordPolicyService.d.ts +34 -0
  139. package/types/services/PasswordPolicyService.d.ts.map +1 -0
  140. package/types/services/UserPasswordHistoryService.d.ts +10 -0
  141. package/types/services/UserPasswordHistoryService.d.ts.map +1 -0
  142. package/types/services/UserService.d.ts +5 -1
  143. package/types/services/UserService.d.ts.map +1 -1
  144. package/types/setup/LoadIdentityConfigFromEnv.d.ts.map +1 -1
  145. package/types/setup/SetProjectPasswordPolicy.d.ts +5 -0
  146. package/types/setup/SetProjectPasswordPolicy.d.ts.map +1 -0
  147. package/types/utils/PasswordPolicySchemaFactory.d.ts +9 -0
  148. package/types/utils/PasswordPolicySchemaFactory.d.ts.map +1 -0
  149. package/types/utils/getPasswordEnvPolicy.d.ts +5 -0
  150. package/types/utils/getPasswordEnvPolicy.d.ts.map +1 -0
@@ -0,0 +1,14 @@
1
+ enum PasswordPolicyConfig {
2
+ MinLength = "PASSWORD_POLICY_MIN_LENGTH",
3
+ MaxLength = "PASSWORD_POLICY_MAX_LENGTH",
4
+ RequireUppercase = "PASSWORD_POLICY_REQUIRE_UPPERCASE",
5
+ RequireLowercase = "PASSWORD_POLICY_REQUIRE_LOWERCASE",
6
+ RequireNumber = "PASSWORD_POLICY_REQUIRE_NUMBER",
7
+ RequireSpecialChar = "PASSWORD_POLICY_REQUIRE_SPECIAL_CHAR",
8
+ DisallowSpaces = "PASSWORD_POLICY_DISALLOW_SPACES",
9
+ PreventReuse = "PASSWORD_POLICY_PREVENT_REUSE",
10
+ ExpirationDays = "PASSWORD_POLICY_EXPIRATION_DAYS",
11
+ }
12
+
13
+ export default PasswordPolicyConfig;
14
+ export {PasswordPolicyConfig};
@@ -1,149 +1,35 @@
1
1
  import type {IUserApiKey, IUserApiKeyBase} from "@drax/identity-share";
2
- import {AbstractFastifyController} from "@drax/crud-back";
3
- import {ValidationError, UnauthorizedError} from "@drax/common-back";
2
+ import {AbstractFastifyController, CustomRequest} from "@drax/crud-back";
4
3
 
5
4
  import UserApiKeyServiceFactory from "../factory/UserApiKeyServiceFactory.js";
6
5
  import UserApiKeyService from "../services/UserApiKeyService.js";
7
6
  import UserApiKeyPermissions from "../permissions/UserApiKeyPermissions.js";
8
7
 
9
- class UserApiKeyController extends AbstractFastifyController<IUserApiKey, IUserApiKeyBase, IUserApiKeyBase> {
8
+ type UserApiKeyPayload = IUserApiKeyBase & {
9
+ user?: string
10
+ }
11
+
12
+ class UserApiKeyController extends AbstractFastifyController<IUserApiKey, UserApiKeyPayload, UserApiKeyPayload> {
10
13
 
11
14
  protected service: UserApiKeyService
12
15
 
16
+ protected userField: string = 'user'
17
+ protected userFilter: boolean = true
18
+ protected userSetter: boolean = true
19
+ protected userAssert: boolean = true
20
+
13
21
  constructor() {
14
22
  super(UserApiKeyServiceFactory(), UserApiKeyPermissions)
15
23
  }
16
24
 
17
-
18
- async paginate(request, reply) {
19
- try {
20
- request.rbac.assertAuthenticated()
21
-
22
- request.rbac.assertOrPermissions([
23
- UserApiKeyPermissions.View,
24
- UserApiKeyPermissions.ViewMy
25
- ])
26
-
27
- const filters = []
28
-
29
- if(!request.rbac.hasPermission(UserApiKeyPermissions.View)){
30
- filters.push({field: "user", operator: "eq", value: request.rbac.userId})
31
- }
32
-
33
- const page = request.query.page
34
- const limit = request.query.limit
35
- const orderBy = request.query.orderBy
36
- const order = request.query.order
37
- const search = request.query.search
38
- const userApiKeyService = UserApiKeyServiceFactory()
39
-
40
-
41
- let paginateResult = await userApiKeyService.paginate({page, limit, orderBy, order, search, filters})
42
- return paginateResult
43
- } catch (e) {
44
- console.log("/api/user-api-keys",e)
45
- if (e instanceof ValidationError) {
46
- reply.statusCode = e.statusCode
47
- reply.send({error: e.message, inputErrors: e.errors})
48
- } else if (e instanceof UnauthorizedError) {
49
- reply.statusCode = e.statusCode
50
- reply.send({error: e.message})
51
- } else {
52
- reply.statusCode = 500
53
- reply.send({error: 'error.server'})
54
- }
55
- }
56
-
25
+ async preCreate(request: CustomRequest, payload: UserApiKeyPayload): Promise<UserApiKeyPayload> {
26
+ request.rbac.assertAuthenticated()
27
+ payload.createdBy = request.rbac.userId
28
+ return payload
57
29
  }
58
-
59
- async create(request, reply) {
60
- try {
61
- request.rbac.assertOrPermissions([UserApiKeyPermissions.Create, UserApiKeyPermissions.CreateMy])
62
- const payload = request.body
63
-
64
- if(!request.rbac.hasPermission(UserApiKeyPermissions.Create) || !payload.user){
65
- payload.user = request.rbac.userId
66
- }
67
-
68
- payload.createdBy = request.rbac.userId
69
-
70
- const userApiKeyService = UserApiKeyServiceFactory()
71
-
72
- let userApiKey = await userApiKeyService.create(payload)
73
- return userApiKey
74
- } catch (e) {
75
- if (e instanceof ValidationError) {
76
- reply.statusCode = e.statusCode
77
- reply.send({error: e.message, inputErrors: e.errors})
78
- } else if (e instanceof UnauthorizedError) {
79
- reply.statusCode = e.statusCode
80
- reply.send({error: e.message})
81
- } else {
82
- reply.statusCode = 500
83
- reply.send({error: 'error.server'})
84
- }
85
- }
86
-
87
- }
88
-
89
-
90
- async update(request, reply) {
91
- try {
92
- request.rbac.assertPermission(UserApiKeyPermissions.Update)
93
- const id = request.params.id
94
- const payload = request.body
95
- const userApiKeyService = UserApiKeyServiceFactory()
96
- let userApiKey = await userApiKeyService.update(id, payload)
97
- return userApiKey
98
- } catch (e) {
99
- if (e instanceof ValidationError) {
100
- reply.statusCode = e.statusCode
101
- reply.send({error: e.message, inputErrors: e.errors})
102
- }
103
- if (e instanceof UnauthorizedError) {
104
- reply.statusCode = e.statusCode
105
- reply.send({error: e.message})
106
- } else if (e instanceof UnauthorizedError) {
107
- reply.statusCode = e.statusCode
108
- reply.send({error: e.message})
109
- } else {
110
- reply.statusCode = 500
111
- reply.send({error: 'error.server'})
112
- }
113
- }
114
-
115
- }
116
-
117
- async delete(request, reply) : Promise<void> {
118
- try {
119
- request.rbac.assertPermission(UserApiKeyPermissions.Delete)
120
- const id = request.params.id
121
- const userApiKeyService = UserApiKeyServiceFactory()
122
- let r = await userApiKeyService.delete(id)
123
- if(r){
124
- reply.send({message: 'Deleted successfully'})
125
- }else{
126
- reply.statusCode(400).send({message: 'Not deleted'})
127
- }
128
- } catch (e) {
129
- if (e instanceof ValidationError) {
130
- reply.statusCode = e.statusCode
131
- reply.send({error: e.message, inputErrors: e.errors})
132
- } else if (e instanceof UnauthorizedError) {
133
- reply.statusCode = e.statusCode
134
- reply.send({error: e.message})
135
- } else {
136
- reply.statusCode = 500
137
- reply.send({error: 'error.server'})
138
- }
139
- }
140
-
141
- }
142
-
143
30
  }
144
31
 
145
32
  export default UserApiKeyController;
146
33
  export {
147
34
  UserApiKeyController
148
35
  }
149
-
@@ -20,6 +20,7 @@ import UserEmailService from "../services/UserEmailService.js";
20
20
  import {IDraxCrudEvent, IDraxFieldFilter} from "@drax/crud-share";
21
21
  import TenantServiceFactory from "../factory/TenantServiceFactory.js";
22
22
  import {CustomRequest} from "@drax/crud-back/dist";
23
+ import PasswordPolicyServiceFactory from "../factory/PasswordPolicyServiceFactory.js";
23
24
 
24
25
  const BASE_FILE_DIR = DraxConfig.getOrLoad(CommonConfig.FileDir) || 'files';
25
26
  const AVATAR_DIR = DraxConfig.getOrLoad(IdentityConfig.AvatarDir) || 'avatar';
@@ -91,6 +92,15 @@ class UserController extends AbstractFastifyController<IUser, IUserCreate, IUser
91
92
  }
92
93
  }
93
94
 
95
+ async passwordPolicy(request, reply) {
96
+ try {
97
+ const passwordPolicyService = PasswordPolicyServiceFactory()
98
+ return await passwordPolicyService.getFinalPolicy()
99
+ } catch (e) {
100
+ this.handleError(e, reply)
101
+ }
102
+ }
103
+
94
104
  async me(request, reply) {
95
105
  try {
96
106
  if (request.authUser) {
@@ -504,4 +514,3 @@ export default UserController;
504
514
  export {
505
515
  UserController
506
516
  }
507
-
@@ -0,0 +1,14 @@
1
+ import PasswordPolicyResolver from "../resolver/PasswordPolicyResolver.js";
2
+
3
+ let passwordPolicyResolver: PasswordPolicyResolver
4
+
5
+ const PasswordPolicyResolverFactory = (): PasswordPolicyResolver => {
6
+ if (!passwordPolicyResolver) {
7
+
8
+ passwordPolicyResolver = new PasswordPolicyResolver()
9
+ }
10
+
11
+ return passwordPolicyResolver
12
+ }
13
+
14
+ export default PasswordPolicyResolverFactory
@@ -0,0 +1,38 @@
1
+ import PasswordPolicyService from "../services/PasswordPolicyService.js";
2
+ import PasswordPolicyResolverFactory from "./PasswordPolicyResolverFactory.js";
3
+ import UserPasswordHistoryServiceFactory from "./UserPasswordHistoryServiceFactory.js";
4
+ import UserMongoRepository from "../repository/mongo/UserMongoRepository.js";
5
+ import UserSqliteRepository from "../repository/sqlite/UserSqliteRepository.js";
6
+ import {COMMON, CommonConfig, DraxConfig} from "@drax/common-back";
7
+ import type {IUserRepository} from "../interfaces/IUserRepository.js";
8
+
9
+ let passwordPolicyService: PasswordPolicyService
10
+
11
+ const PasswordPolicyServiceFactory = (verbose: boolean = false): PasswordPolicyService => {
12
+ if (!passwordPolicyService) {
13
+ let userRepository: IUserRepository
14
+
15
+ switch (DraxConfig.getOrLoad(CommonConfig.DbEngine)) {
16
+ case COMMON.DB_ENGINES.MONGODB:
17
+ userRepository = new UserMongoRepository()
18
+ break;
19
+ case COMMON.DB_ENGINES.SQLITE:
20
+ userRepository = new UserSqliteRepository(DraxConfig.getOrLoad(CommonConfig.SqliteDbFile), verbose)
21
+ userRepository.build()
22
+ break;
23
+ default:
24
+ throw new Error("DraxConfig.DB_ENGINE must be one of " + Object.values(COMMON.DB_ENGINES).join(", "));
25
+ }
26
+
27
+ const passwordPolicyResolver = PasswordPolicyResolverFactory()
28
+ passwordPolicyService = new PasswordPolicyService(
29
+ passwordPolicyResolver,
30
+ userRepository,
31
+ UserPasswordHistoryServiceFactory(verbose)
32
+ )
33
+ }
34
+
35
+ return passwordPolicyService
36
+ }
37
+
38
+ export default PasswordPolicyServiceFactory
@@ -0,0 +1,31 @@
1
+ import {COMMON, CommonConfig, DraxConfig} from "@drax/common-back";
2
+ import type {IUserPasswordHistoryRepository} from "../interfaces/IUserPasswordHistoryRepository.js";
3
+ import UserPasswordHistoryMongoRepository from "../repository/mongo/UserPasswordHistoryMongoRepository.js";
4
+ import UserPasswordHistorySqliteRepository from "../repository/sqlite/UserPasswordHistorySqliteRepository.js";
5
+ import UserPasswordHistoryService from "../services/UserPasswordHistoryService.js";
6
+
7
+ let userPasswordHistoryService: UserPasswordHistoryService
8
+
9
+ const UserPasswordHistoryServiceFactory = (verbose: boolean = false): UserPasswordHistoryService => {
10
+ if (!userPasswordHistoryService) {
11
+ let repository: IUserPasswordHistoryRepository
12
+ switch (DraxConfig.getOrLoad(CommonConfig.DbEngine)) {
13
+ case COMMON.DB_ENGINES.MONGODB:
14
+ repository = new UserPasswordHistoryMongoRepository()
15
+ break;
16
+ case COMMON.DB_ENGINES.SQLITE:
17
+ const sqliteRepository = new UserPasswordHistorySqliteRepository(DraxConfig.getOrLoad(CommonConfig.SqliteDbFile), verbose)
18
+ sqliteRepository.build()
19
+ repository = sqliteRepository
20
+ break;
21
+ default:
22
+ throw new Error("DraxConfig.DB_ENGINE must be one of " + Object.values(COMMON.DB_ENGINES).join(", "));
23
+ }
24
+
25
+ userPasswordHistoryService = new UserPasswordHistoryService(repository)
26
+ }
27
+
28
+ return userPasswordHistoryService
29
+ }
30
+
31
+ export default UserPasswordHistoryServiceFactory
@@ -3,6 +3,8 @@ import UserService from "../services/UserService.js";
3
3
  import UserSqliteRepository from "../repository/sqlite/UserSqliteRepository.js";
4
4
  import {IUserRepository} from "../interfaces/IUserRepository";
5
5
  import {COMMON, CommonConfig, DraxConfig} from "@drax/common-back";
6
+ import PasswordPolicyServiceFactory from "./PasswordPolicyServiceFactory.js";
7
+ import UserPasswordHistoryServiceFactory from "./UserPasswordHistoryServiceFactory.js";
6
8
 
7
9
  let userService: UserService
8
10
 
@@ -22,7 +24,11 @@ const UserServiceFactory = (verbose:boolean = false) : UserService => {
22
24
  throw new Error("DraxConfig.DB_ENGINE must be one of " + Object.values(COMMON.DB_ENGINES).join(", "));
23
25
  }
24
26
 
25
- userService = new UserService(userRepository)
27
+ userService = new UserService(
28
+ userRepository,
29
+ PasswordPolicyServiceFactory(verbose),
30
+ UserPasswordHistoryServiceFactory(verbose)
31
+ )
26
32
  }
27
33
 
28
34
  return userService
@@ -14,15 +14,14 @@ export default {
14
14
 
15
15
 
16
16
  rbac.assertOrPermissions([
17
- UserApiKeyPermissions.View,
18
- UserApiKeyPermissions.ViewMy
17
+ UserApiKeyPermissions.View
19
18
  ])
20
19
 
21
20
  if(!Array.isArray(options.filters)){
22
21
  options.filters = []
23
22
  }
24
23
 
25
- if(!rbac.hasPermission(UserApiKeyPermissions.View)){
24
+ if(!rbac.hasPermission(UserApiKeyPermissions.ViewAll)){
26
25
  options.filters.push({field: "user", operator: "eq", value: rbac.userId})
27
26
  }
28
27
 
package/src/index.ts CHANGED
@@ -5,6 +5,8 @@ import TenantServiceFactory from "./factory/TenantServiceFactory.js";
5
5
  import UserApiKeyServiceFactory from "./factory/UserApiKeyServiceFactory.js";
6
6
  import UserLoginFailServiceFactory from "./factory/UserLoginFailServiceFactory.js";
7
7
  import UserSessionServiceFactory from "./factory/UserSessionServiceFactory.js";
8
+ import PasswordPolicyServiceFactory from "./factory/PasswordPolicyServiceFactory.js";
9
+ import UserPasswordHistoryServiceFactory from "./factory/UserPasswordHistoryServiceFactory.js";
8
10
 
9
11
  import RoleService from "./services/RoleService.js";
10
12
  import UserService from "./services/UserService.js";
@@ -13,6 +15,9 @@ import PermissionService from "./services/PermissionService.js";
13
15
  import UserApiKeyService from "./services/UserApiKeyService.js";
14
16
  import UserSessionService from "./services/UserSessionService.js";
15
17
  import UserLoginFailService from "./services/UserLoginFailService.js";
18
+ import UserPasswordHistoryService from "./services/UserPasswordHistoryService.js";
19
+ import PasswordPolicyService from "./services/PasswordPolicyService.js";
20
+ import PasswordPolicyResolver from "./resolver/PasswordPolicyResolver.js";
16
21
 
17
22
  import Rbac from "./rbac/Rbac.js";
18
23
 
@@ -29,6 +34,7 @@ import {rbacMiddleware} from "./middleware/rbacMiddleware.js";
29
34
  import {apiKeyMiddleware} from "./middleware/apiKeyMiddleware.js";
30
35
 
31
36
  import IdentityConfig from "./config/IdentityConfig.js";
37
+ import PasswordPolicyConfig from "./config/PasswordPolicyConfig.js";
32
38
  import BadCredentialsError from "./errors/BadCredentialsError.js";
33
39
 
34
40
  import CreateUserIfNotExist from "./setup/CreateUserIfNotExist.js";
@@ -37,6 +43,7 @@ import CreateOrUpdateRole from "./setup/CreateOrUpdateRole.js";
37
43
  import LoadPermissions from "./setup/LoadPermissions.js";
38
44
  import LoadIdentityConfigFromEnv from "./setup/LoadIdentityConfigFromEnv.js";
39
45
  import RecoveryUserPassword from "./setup/RecoveryUserPassword.js";
46
+ import SetProjectPasswordPolicy from "./setup/SetProjectPasswordPolicy.js";
40
47
 
41
48
  import type {IRoleRepository} from "./interfaces/IRoleRepository";
42
49
  import type {ITenantRepository} from "./interfaces/ITenantRepository";
@@ -44,6 +51,7 @@ import type {IUserRepository} from "./interfaces/IUserRepository";
44
51
  import type {IUserApiKeyRepository} from "./interfaces/IUserApiKeyRepository";
45
52
  import type {IUserLoginFailRepository} from "./interfaces/IUserLoginFailRepository";
46
53
  import type {IUserSessionRepository} from "./interfaces/IUserSessionRepository";
54
+ import type {IUserPasswordHistoryRepository} from "./interfaces/IUserPasswordHistoryRepository";
47
55
 
48
56
  import {RoleModel, RoleMongoSchema} from "./models/RoleModel.js";
49
57
  import {TenantModel, TenantMongoSchema} from "./models/TenantModel.js";
@@ -51,6 +59,7 @@ import {UserModel, UserMongoSchema} from "./models/UserModel.js";
51
59
  import {UserApiKeyModel, UserApiKeyMongoSchema} from "./models/UserApiKeyModel.js";
52
60
  import {UserSessionModel,UserSessionMongoSchema} from "./models/UserSessionModel.js";
53
61
  import {UserLoginFailModel,UserLoginFailMongoSchema} from "./models/UserLoginFailModel.js";
62
+ import {UserPasswordHistoryModel, UserPasswordHistoryMongoSchema} from "./models/UserPasswordHistoryModel.js";
54
63
 
55
64
 
56
65
  import RoleMongoRepository from "./repository/mongo/RoleMongoRepository.js";
@@ -59,6 +68,7 @@ import UserMongoRepository from "./repository/mongo/UserMongoRepository.js";
59
68
  import UserApiKeyMongoRepository from "./repository/mongo/UserApiKeyMongoRepository.js";
60
69
  import UserSessionMongoRepository from "./repository/mongo/UserSessionMongoRepository.js";
61
70
  import UserLoginFailMongoRepository from "./repository/mongo/UserLoginFailMongoRepository.js";
71
+ import UserPasswordHistoryMongoRepository from "./repository/mongo/UserPasswordHistoryMongoRepository.js";
62
72
 
63
73
  import RoleSqliteRepository from "./repository/sqlite/RoleSqliteRepository.js";
64
74
  import TenantSqliteRepository from "./repository/sqlite/TenantSqliteRepository.js";
@@ -66,6 +76,7 @@ import UserSqliteRepository from "./repository/sqlite/UserSqliteRepository.js";
66
76
  import UserApiKeySqliteRepository from "./repository/sqlite/UserApiKeySqliteRepository.js";
67
77
  import UserLoginFailSqliteRepository from "./repository/sqlite/UserLoginFailSqliteRepository.js";
68
78
  import UserSessionSqliteRepository from "./repository/sqlite/UserSessionSqliteRepository.js";
79
+ import UserPasswordHistorySqliteRepository from "./repository/sqlite/UserPasswordHistorySqliteRepository.js";
69
80
 
70
81
 
71
82
  import {RolePermissions} from "./permissions/RolePermissions.js";
@@ -81,6 +92,8 @@ import {RoleSchema, RoleBaseSchema} from "./schemas/RoleSchema.js";
81
92
  import {UserApiKeySchema, UserApiKeyBaseSchema} from "./schemas/UserApiKeySchema.js";
82
93
  import {UserLoginFailBaseSchema, UserLoginFailSchema} from "./schemas/UserLoginFailSchema.js";
83
94
  import {UserSessionBaseSchema, UserSessionSchema} from "./schemas/UserSessionSchema.js";
95
+ import {defaultPasswordPolicy} from "./policies/defaultPasswordPolicy.js";
96
+ import {PasswordPolicySchema} from "./schemas/PasswordPolicySchema.js";
84
97
 
85
98
 
86
99
  const graphqlMergeResult = await GraphqlMerge()
@@ -95,6 +108,7 @@ export type {
95
108
  IUserApiKeyRepository,
96
109
  IUserLoginFailRepository,
97
110
  IUserSessionRepository,
111
+ IUserPasswordHistoryRepository,
98
112
  }
99
113
 
100
114
  export {
@@ -118,6 +132,9 @@ export {
118
132
  UserApiKeyService,
119
133
  UserSessionService,
120
134
  UserLoginFailService,
135
+ UserPasswordHistoryService,
136
+ PasswordPolicyService,
137
+ PasswordPolicyResolver,
121
138
  PermissionService,
122
139
  Rbac,
123
140
 
@@ -128,6 +145,8 @@ export {
128
145
  UserApiKeyServiceFactory,
129
146
  UserSessionServiceFactory,
130
147
  UserLoginFailServiceFactory,
148
+ UserPasswordHistoryServiceFactory,
149
+ PasswordPolicyServiceFactory,
131
150
 
132
151
  //GQL
133
152
  identityTypeDefs,
@@ -163,6 +182,7 @@ export {
163
182
  UserApiKeyMongoRepository,
164
183
  UserSessionMongoRepository,
165
184
  UserLoginFailMongoRepository,
185
+ UserPasswordHistoryMongoRepository,
166
186
 
167
187
  //Mongo Models
168
188
  RoleModel,
@@ -171,6 +191,7 @@ export {
171
191
  UserApiKeyModel,
172
192
  UserSessionModel,
173
193
  UserLoginFailModel,
194
+ UserPasswordHistoryModel,
174
195
 
175
196
  RoleMongoSchema,
176
197
  TenantMongoSchema,
@@ -178,6 +199,7 @@ export {
178
199
  UserApiKeyMongoSchema,
179
200
  UserSessionMongoSchema,
180
201
  UserLoginFailMongoSchema,
202
+ UserPasswordHistoryMongoSchema,
181
203
 
182
204
  //Sqlite Repositories
183
205
  RoleSqliteRepository,
@@ -186,12 +208,16 @@ export {
186
208
  UserApiKeySqliteRepository,
187
209
  UserLoginFailSqliteRepository,
188
210
  UserSessionSqliteRepository,
211
+ UserPasswordHistorySqliteRepository,
189
212
 
190
213
  //Config
191
214
  IdentityConfig,
215
+ PasswordPolicyConfig,
192
216
 
193
217
  //Errors
194
218
  BadCredentialsError,
219
+ defaultPasswordPolicy,
220
+ PasswordPolicySchema,
195
221
 
196
222
  //Setup
197
223
  LoadIdentityConfigFromEnv,
@@ -199,7 +225,6 @@ export {
199
225
  CreateOrUpdateRole,
200
226
  CreateUserIfNotExist,
201
227
  CreateTenantIfNotExist,
202
- RecoveryUserPassword
228
+ RecoveryUserPassword,
229
+ SetProjectPasswordPolicy
203
230
  }
204
-
205
-
@@ -0,0 +1,21 @@
1
+ interface IUserPasswordHistory {
2
+ _id?: string
3
+ id?: string
4
+ user: string
5
+ passwordHash: string
6
+ createdAt?: string | Date
7
+ updatedAt?: string | Date
8
+ }
9
+
10
+ interface IUserPasswordHistoryCreate {
11
+ _id?: string
12
+ id?: string
13
+ user: string
14
+ passwordHash: string
15
+ createdAt?: string | Date
16
+ }
17
+
18
+ export type {
19
+ IUserPasswordHistory,
20
+ IUserPasswordHistoryCreate
21
+ }
@@ -0,0 +1,8 @@
1
+ import type {IUserPasswordHistory, IUserPasswordHistoryCreate} from "./IUserPasswordHistory.js";
2
+
3
+ interface IUserPasswordHistoryRepository {
4
+ create(data: IUserPasswordHistoryCreate): Promise<IUserPasswordHistory>
5
+ findLatestByUserId(userId: string, limit: number): Promise<IUserPasswordHistory[]>
6
+ }
7
+
8
+ export type {IUserPasswordHistoryRepository}
@@ -6,7 +6,7 @@ import IdentityConfig from "../config/IdentityConfig.js";
6
6
  const verifyIp = DraxConfig.getOrLoad(IdentityConfig.VerifyIP, 'boolean', true);
7
7
  const cacheTTL = DraxConfig.getOrLoad(IdentityConfig.ApiKeyCacheTTL, 'number',10000)
8
8
 
9
- const draxCache = new DraxCache<IUserApiKey>(cacheTTL);
9
+ const draxCache = new DraxCache<IUserApiKey>({ ttl: cacheTTL, namespace: 'identity:api-key' });
10
10
 
11
11
 
12
12
  async function userApiKeyLoader(k): Promise<IUserApiKey | null> {
@@ -5,7 +5,7 @@ import Rbac from "../rbac/Rbac.js";
5
5
  import IdentityConfig from "../config/IdentityConfig.js";
6
6
 
7
7
  const cacheTTL = DraxConfig.getOrLoad(IdentityConfig.RbacCacheTTL, 'number',10000) ;
8
- const draxCache = new DraxCache<IRole>(cacheTTL);
8
+ const draxCache = new DraxCache<IRole>({ ttl: cacheTTL, namespace: 'identity:role' });
9
9
 
10
10
 
11
11
  async function roleLoader(k):Promise<IRole | null> {
@@ -0,0 +1,42 @@
1
+ import {mongoose} from "@drax/common-back";
2
+ import {PaginateModel} from "mongoose";
3
+ import uniqueValidator from "mongoose-unique-validator";
4
+ import mongoosePaginate from "mongoose-paginate-v2";
5
+ import type {IUserPasswordHistory} from "../interfaces/IUserPasswordHistory.js";
6
+
7
+ const UserPasswordHistoryMongoSchema = new mongoose.Schema<IUserPasswordHistory>({
8
+ user: {type: String, required: true, index: true},
9
+ passwordHash: {type: String, required: true, index: false}
10
+ }, {timestamps: true});
11
+
12
+ UserPasswordHistoryMongoSchema.plugin(uniqueValidator, {message: "validation.unique"});
13
+ UserPasswordHistoryMongoSchema.plugin(mongoosePaginate);
14
+
15
+ UserPasswordHistoryMongoSchema.virtual("id").get(function () {
16
+ return this._id.toString();
17
+ });
18
+
19
+ UserPasswordHistoryMongoSchema.set("toJSON", {getters: true, virtuals: true});
20
+ UserPasswordHistoryMongoSchema.set("toObject", {getters: true, virtuals: true});
21
+
22
+ const MODEL_NAME = "UserPasswordHistory";
23
+ const COLLECTION_NAME = "user_password_history";
24
+
25
+ let UserPasswordHistoryModel;
26
+
27
+ try {
28
+ UserPasswordHistoryModel = mongoose.model<IUserPasswordHistory, PaginateModel<IUserPasswordHistory>>(MODEL_NAME, UserPasswordHistoryMongoSchema, COLLECTION_NAME);
29
+ } catch (e) {
30
+ if (e.name === "OverwriteModelError") {
31
+ UserPasswordHistoryModel = mongoose.model<IUserPasswordHistory, PaginateModel<IUserPasswordHistory>>(MODEL_NAME);
32
+ } else {
33
+ throw e;
34
+ }
35
+ }
36
+
37
+ export {
38
+ UserPasswordHistoryMongoSchema,
39
+ UserPasswordHistoryModel
40
+ }
41
+
42
+ export default UserPasswordHistoryModel
@@ -1,10 +1,9 @@
1
1
  enum UserApiKeyPermissions {
2
2
  Create = "userApiKey:create",
3
- CreateMy = "userApiKey:createMy",
4
3
  Update = "userApiKey:update",
5
4
  Delete = "userApiKey:delete",
6
5
  View = "userApiKey:view",
7
- ViewMy = "userApiKey:viewMy",
6
+ ViewAll = "userApiKey:viewAll",
8
7
  Manage = "userApiKey:manage",
9
8
 
10
9
  }
@@ -0,0 +1,17 @@
1
+ import type {IPasswordPolicy} from "@drax/identity-share/dist";
2
+
3
+ const defaultPasswordPolicy: IPasswordPolicy = {
4
+ minLength: 8,
5
+ maxLength: 64,
6
+ requireUppercase: true,
7
+ requireLowercase: true,
8
+ requireNumber: true,
9
+ requireSpecialChar: false,
10
+ disallowSpaces: true,
11
+ preventReuse: 3,
12
+ expirationDays: null
13
+ }
14
+
15
+ export {
16
+ defaultPasswordPolicy
17
+ }
@@ -0,0 +1,25 @@
1
+ import {AbstractMongoRepository} from "@drax/crud-back";
2
+ import type {IUserPasswordHistoryRepository} from "../../interfaces/IUserPasswordHistoryRepository.js";
3
+ import type {IUserPasswordHistory, IUserPasswordHistoryCreate} from "../../interfaces/IUserPasswordHistory.js";
4
+ import {UserPasswordHistoryModel} from "../../models/UserPasswordHistoryModel.js";
5
+
6
+ class UserPasswordHistoryMongoRepository extends AbstractMongoRepository<IUserPasswordHistory, IUserPasswordHistoryCreate, IUserPasswordHistoryCreate> implements IUserPasswordHistoryRepository {
7
+ constructor() {
8
+ super();
9
+ this._model = UserPasswordHistoryModel;
10
+ this._searchFields = ["user"];
11
+ this._populateFields = ["user"];
12
+ }
13
+
14
+ async findLatestByUserId(userId: string, limit: number): Promise<IUserPasswordHistory[]> {
15
+ return UserPasswordHistoryModel
16
+ .find({user: userId})
17
+ .sort({createdAt: -1})
18
+ .limit(limit)
19
+ .lean(true)
20
+ .exec() as Promise<IUserPasswordHistory[]>
21
+ }
22
+ }
23
+
24
+ export default UserPasswordHistoryMongoRepository
25
+ export {UserPasswordHistoryMongoRepository}