@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.
- package/dist/config/PasswordPolicyConfig.js +14 -0
- package/dist/controllers/UserApiKeyController.js +8 -119
- package/dist/controllers/UserController.js +10 -0
- package/dist/factory/PasswordPolicyResolverFactory.js +9 -0
- package/dist/factory/PasswordPolicyServiceFactory.js +27 -0
- package/dist/factory/UserPasswordHistoryServiceFactory.js +25 -0
- package/dist/factory/UserServiceFactory.js +3 -1
- package/dist/graphql/resolvers/user-api-key.resolvers.js +2 -3
- package/dist/index.js +20 -8
- package/dist/interfaces/IUserPasswordHistory.js +1 -0
- package/dist/interfaces/IUserPasswordHistoryRepository.js +1 -0
- package/dist/middleware/apiKeyMiddleware.js +1 -1
- package/dist/middleware/rbacMiddleware.js +1 -1
- package/dist/models/UserPasswordHistoryModel.js +30 -0
- package/dist/permissions/UserApiKeyPermissions.js +1 -2
- package/dist/policies/defaultPasswordPolicy.js +12 -0
- package/dist/repository/mongo/UserPasswordHistoryMongoRepository.js +20 -0
- package/dist/repository/sqlite/UserPasswordHistorySqliteRepository.js +38 -0
- package/dist/resolver/PasswordPolicyResolver.js +27 -0
- package/dist/routes/UserRoutes.js +10 -0
- package/dist/schemas/PasswordPolicySchema.js +18 -0
- package/dist/schemas/RegisterSchema.js +1 -3
- package/dist/schemas/UserSchema.js +1 -3
- package/dist/security/constants/defaultPasswordPolicy.js +12 -0
- package/dist/security/interfaces/IPasswordPolicy.js +1 -0
- package/dist/security/interfaces/IPasswordPolicyProjectContext.js +1 -0
- package/dist/security/schemas/PasswordPolicySchema.js +18 -0
- package/dist/security/services/PasswordPolicyResolver.js +21 -0
- package/dist/security/services/PasswordPolicyService.js +147 -0
- package/dist/security/utils/PasswordPolicySchemaFactory.js +36 -0
- package/dist/security/utils/getPasswordEnvPolicy.js +19 -0
- package/dist/services/PasswordPolicyService.js +147 -0
- package/dist/services/UserPasswordHistoryService.js +18 -0
- package/dist/services/UserService.js +34 -9
- package/dist/setup/LoadIdentityConfigFromEnv.js +10 -0
- package/dist/setup/SetProjectPasswordPolicy.js +7 -0
- package/dist/utils/PasswordPolicySchemaFactory.js +36 -0
- package/dist/utils/getPasswordEnvPolicy.js +19 -0
- package/docs/password-policy.md +33 -0
- package/package.json +6 -6
- package/src/config/PasswordPolicyConfig.ts +14 -0
- package/src/controllers/UserApiKeyController.ts +15 -129
- package/src/controllers/UserController.ts +10 -1
- package/src/factory/PasswordPolicyResolverFactory.ts +14 -0
- package/src/factory/PasswordPolicyServiceFactory.ts +38 -0
- package/src/factory/UserPasswordHistoryServiceFactory.ts +31 -0
- package/src/factory/UserServiceFactory.ts +7 -1
- package/src/graphql/resolvers/user-api-key.resolvers.ts +2 -3
- package/src/index.ts +28 -3
- package/src/interfaces/IUserPasswordHistory.ts +21 -0
- package/src/interfaces/IUserPasswordHistoryRepository.ts +8 -0
- package/src/middleware/apiKeyMiddleware.ts +1 -1
- package/src/middleware/rbacMiddleware.ts +1 -1
- package/src/models/UserPasswordHistoryModel.ts +42 -0
- package/src/permissions/UserApiKeyPermissions.ts +1 -2
- package/src/policies/defaultPasswordPolicy.ts +17 -0
- package/src/repository/mongo/UserPasswordHistoryMongoRepository.ts +25 -0
- package/src/repository/sqlite/UserPasswordHistorySqliteRepository.ts +47 -0
- package/src/resolver/PasswordPolicyResolver.ts +33 -0
- package/src/routes/UserRoutes.ts +11 -0
- package/src/schemas/PasswordPolicySchema.ts +29 -0
- package/src/schemas/RegisterSchema.ts +1 -3
- package/src/schemas/UserSchema.ts +1 -3
- package/src/services/PasswordPolicyService.ts +184 -0
- package/src/services/UserPasswordHistoryService.ts +23 -0
- package/src/services/UserService.ts +38 -9
- package/src/setup/LoadIdentityConfigFromEnv.ts +11 -0
- package/src/setup/SetProjectPasswordPolicy.ts +12 -0
- package/src/utils/PasswordPolicySchemaFactory.ts +47 -0
- package/src/utils/getPasswordEnvPolicy.ts +25 -0
- package/test/data-obj/users/root-mongo-user.ts +1 -1
- package/test/data-obj/users/root-sqlite-user.ts +1 -1
- package/test/endpoints/data/users-data.ts +3 -3
- package/test/endpoints/password-policy-route.test.ts +33 -0
- package/test/endpoints/user-route.test.ts +17 -4
- package/test/security/password-policy-resolver.test.ts +55 -0
- package/test/security/password-policy-schema-factory.test.ts +40 -0
- package/test/services/user-service.test.ts +218 -31
- package/test/setup/TestSetup.ts +22 -4
- package/test/setup/data/basic-user.ts +1 -1
- package/test/setup/data/root-user.ts +1 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/types/config/PasswordPolicyConfig.d.ts +14 -0
- package/types/config/PasswordPolicyConfig.d.ts.map +1 -0
- package/types/controllers/UserApiKeyController.d.ts +10 -6
- package/types/controllers/UserApiKeyController.d.ts.map +1 -1
- package/types/controllers/UserController.d.ts +1 -0
- package/types/controllers/UserController.d.ts.map +1 -1
- package/types/factory/PasswordPolicyResolverFactory.d.ts +4 -0
- package/types/factory/PasswordPolicyResolverFactory.d.ts.map +1 -0
- package/types/factory/PasswordPolicyServiceFactory.d.ts +4 -0
- package/types/factory/PasswordPolicyServiceFactory.d.ts.map +1 -0
- package/types/factory/UserPasswordHistoryServiceFactory.d.ts +4 -0
- package/types/factory/UserPasswordHistoryServiceFactory.d.ts.map +1 -0
- package/types/factory/UserServiceFactory.d.ts.map +1 -1
- package/types/graphql/resolvers/user-api-key.resolvers.d.ts.map +1 -1
- package/types/index.d.ts +15 -2
- package/types/index.d.ts.map +1 -1
- package/types/interfaces/IUserPasswordHistory.d.ts +17 -0
- package/types/interfaces/IUserPasswordHistory.d.ts.map +1 -0
- package/types/interfaces/IUserPasswordHistoryRepository.d.ts +7 -0
- package/types/interfaces/IUserPasswordHistoryRepository.d.ts.map +1 -0
- package/types/models/UserPasswordHistoryModel.d.ts +15 -0
- package/types/models/UserPasswordHistoryModel.d.ts.map +1 -0
- package/types/permissions/UserApiKeyPermissions.d.ts +1 -2
- package/types/permissions/UserApiKeyPermissions.d.ts.map +1 -1
- package/types/permissions/index.d.ts +0 -2
- package/types/permissions/index.d.ts.map +1 -1
- package/types/policies/defaultPasswordPolicy.d.ts +4 -0
- package/types/policies/defaultPasswordPolicy.d.ts.map +1 -0
- package/types/repository/mongo/UserPasswordHistoryMongoRepository.d.ts +10 -0
- package/types/repository/mongo/UserPasswordHistoryMongoRepository.d.ts.map +1 -0
- package/types/repository/sqlite/UserPasswordHistorySqliteRepository.d.ts +25 -0
- package/types/repository/sqlite/UserPasswordHistorySqliteRepository.d.ts.map +1 -0
- package/types/resolver/PasswordPolicyResolver.d.ts +10 -0
- package/types/resolver/PasswordPolicyResolver.d.ts.map +1 -0
- package/types/routes/UserRoutes.d.ts.map +1 -1
- package/types/schemas/PasswordPolicySchema.d.ts +25 -0
- package/types/schemas/PasswordPolicySchema.d.ts.map +1 -0
- package/types/schemas/RegisterSchema.d.ts.map +1 -1
- package/types/schemas/UserSchema.d.ts.map +1 -1
- package/types/security/constants/defaultPasswordPolicy.d.ts +4 -0
- package/types/security/constants/defaultPasswordPolicy.d.ts.map +1 -0
- package/types/security/interfaces/IPasswordPolicy.d.ts +13 -0
- package/types/security/interfaces/IPasswordPolicy.d.ts.map +1 -0
- package/types/security/interfaces/IPasswordPolicyProjectContext.d.ts +6 -0
- package/types/security/interfaces/IPasswordPolicyProjectContext.d.ts.map +1 -0
- package/types/security/schemas/PasswordPolicySchema.d.ts +25 -0
- package/types/security/schemas/PasswordPolicySchema.d.ts.map +1 -0
- package/types/security/services/PasswordPolicyResolver.d.ts +9 -0
- package/types/security/services/PasswordPolicyResolver.d.ts.map +1 -0
- package/types/security/services/PasswordPolicyService.d.ts +35 -0
- package/types/security/services/PasswordPolicyService.d.ts.map +1 -0
- package/types/security/utils/PasswordPolicySchemaFactory.d.ts +9 -0
- package/types/security/utils/PasswordPolicySchemaFactory.d.ts.map +1 -0
- package/types/security/utils/getPasswordEnvPolicy.d.ts +5 -0
- package/types/security/utils/getPasswordEnvPolicy.d.ts.map +1 -0
- package/types/services/PasswordPolicyService.d.ts +34 -0
- package/types/services/PasswordPolicyService.d.ts.map +1 -0
- package/types/services/UserPasswordHistoryService.d.ts +10 -0
- package/types/services/UserPasswordHistoryService.d.ts.map +1 -0
- package/types/services/UserService.d.ts +5 -1
- package/types/services/UserService.d.ts.map +1 -1
- package/types/setup/LoadIdentityConfigFromEnv.d.ts.map +1 -1
- package/types/setup/SetProjectPasswordPolicy.d.ts +5 -0
- package/types/setup/SetProjectPasswordPolicy.d.ts.map +1 -0
- package/types/utils/PasswordPolicySchemaFactory.d.ts +9 -0
- package/types/utils/PasswordPolicySchemaFactory.d.ts.map +1 -0
- package/types/utils/getPasswordEnvPolicy.d.ts +5 -0
- 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
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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(
|
|
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.
|
|
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
|
-
|
|
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}
|