@drax/identity-back 3.14.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/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/index.js +20 -8
- package/dist/interfaces/IUserPasswordHistory.js +1 -0
- package/dist/interfaces/IUserPasswordHistoryRepository.js +1 -0
- package/dist/models/UserPasswordHistoryModel.js +30 -0
- 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 +4 -4
- package/src/config/PasswordPolicyConfig.ts +14 -0
- 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/index.ts +28 -3
- package/src/interfaces/IUserPasswordHistory.ts +21 -0
- package/src/interfaces/IUserPasswordHistoryRepository.ts +8 -0
- package/src/models/UserPasswordHistoryModel.ts +42 -0
- 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/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/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/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
|
@@ -1,38 +1,173 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {beforeEach, describe, expect, it} from "vitest"
|
|
2
2
|
import UserService from "../../src/services/UserService";
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import {IRole} from "../../../identity-share/src/interfaces/IRole";
|
|
6
|
-
import UserMongoRepository from "../../src/repository/mongo/UserMongoRepository";
|
|
7
|
-
import {IUserRepository} from "../../src/interfaces/IUserRepository";
|
|
3
|
+
import type {IUserRepository} from "../../src/interfaces/IUserRepository";
|
|
4
|
+
import type {IUser, IUserCreate, IUserUpdate} from "@drax/identity-share";
|
|
8
5
|
import {ValidationError} from "@drax/common-back";
|
|
6
|
+
import PasswordPolicyResolver from "../../src/resolver/PasswordPolicyResolver";
|
|
7
|
+
import PasswordPolicyService from "../../src/services/PasswordPolicyService";
|
|
8
|
+
import type {IUserPasswordHistory} from "../../src/interfaces/IUserPasswordHistory";
|
|
9
|
+
import type {IUserPasswordHistoryRepository} from "../../src/interfaces/IUserPasswordHistoryRepository";
|
|
10
|
+
import UserPasswordHistoryService from "../../src/services/UserPasswordHistoryService";
|
|
11
|
+
|
|
12
|
+
class InMemoryUserRepository implements IUserRepository {
|
|
13
|
+
private items = new Map<string, IUser>()
|
|
14
|
+
|
|
15
|
+
async create(data: IUserCreate): Promise<IUser> {
|
|
16
|
+
const user: IUser = {...data, _id: data._id || "user-1", role: data.role as any, tenant: data.tenant as any} as IUser
|
|
17
|
+
this.items.set(user._id.toString(), user)
|
|
18
|
+
return {...user}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async update(id: string, data: IUserUpdate): Promise<IUser> {
|
|
22
|
+
const current = this.items.get(id)
|
|
23
|
+
const updated = {...current, ...data, _id: id} as IUser
|
|
24
|
+
this.items.set(id, updated)
|
|
25
|
+
return {...updated}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async updatePartial(id: string, data: Partial<IUserUpdate & IUser>): Promise<IUser> {
|
|
29
|
+
const current = this.items.get(id)
|
|
30
|
+
const updated = {...current, ...data, _id: id} as IUser
|
|
31
|
+
this.items.set(id, updated)
|
|
32
|
+
return {...updated}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async delete(id: string): Promise<boolean> {
|
|
36
|
+
return this.items.delete(id)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async findById(id: string): Promise<IUser | null> {
|
|
40
|
+
const user = this.items.get(id)
|
|
41
|
+
if (!user) {
|
|
42
|
+
return null
|
|
43
|
+
}
|
|
44
|
+
const safeUser = {...user}
|
|
45
|
+
delete safeUser.password
|
|
46
|
+
return safeUser
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async findByIdWithPassword(id: string): Promise<IUser | null> {
|
|
50
|
+
const user = this.items.get(id)
|
|
51
|
+
return user ? {...user} : null
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async findByUsername(username: string): Promise<IUser | null> {
|
|
55
|
+
const user = [...this.items.values()].find((item) => item.username === username)
|
|
56
|
+
if (!user) {
|
|
57
|
+
return null
|
|
58
|
+
}
|
|
59
|
+
const safeUser = {...user}
|
|
60
|
+
delete safeUser.password
|
|
61
|
+
return safeUser
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async findByUsernameWithPassword(username: string): Promise<IUser | null> {
|
|
65
|
+
const user = [...this.items.values()].find((item) => item.username === username)
|
|
66
|
+
return user ? {...user} : null
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async findByEmail(email: string): Promise<IUser | null> {
|
|
70
|
+
const user = [...this.items.values()].find((item) => item.email === email)
|
|
71
|
+
return user ? {...user} : null
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async changePassword(id: string, password: string): Promise<Boolean> {
|
|
75
|
+
const current = this.items.get(id)
|
|
76
|
+
this.items.set(id, {...current, password} as IUser)
|
|
77
|
+
return true
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async changeAvatar(id: string, avatarUrl: string): Promise<Boolean> {
|
|
81
|
+
const current = this.items.get(id)
|
|
82
|
+
this.items.set(id, {...current, avatar: avatarUrl} as IUser)
|
|
83
|
+
return true
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async findByEmailCode(code: string): Promise<IUser | null> {
|
|
87
|
+
const user = [...this.items.values()].find((item) => item.emailCode === code)
|
|
88
|
+
return user ? {...user} : null
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async findByPhoneCode(code: string): Promise<IUser | null> {
|
|
92
|
+
const user = [...this.items.values()].find((item) => item.phoneCode === code)
|
|
93
|
+
return user ? {...user} : null
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async findByRecoveryCode(code: string): Promise<IUser | null> {
|
|
97
|
+
const user = [...this.items.values()].find((item) => item.recoveryCode === code)
|
|
98
|
+
return user ? {...user} : null
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async paginate(): Promise<any> {
|
|
102
|
+
return {items: [...this.items.values()], page: 1, limit: 10, total: this.items.size}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async search(): Promise<IUser[]> {
|
|
106
|
+
return [...this.items.values()]
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async groupBy(): Promise<any[]> {
|
|
110
|
+
return []
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async export(): Promise<IUser[]> {
|
|
114
|
+
return [...this.items.values()]
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async count(): Promise<number> {
|
|
118
|
+
return this.items.size
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
build(): void {
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
class InMemoryUserPasswordHistoryRepository implements IUserPasswordHistoryRepository {
|
|
126
|
+
private items: IUserPasswordHistory[] = []
|
|
127
|
+
|
|
128
|
+
async create(data: IUserPasswordHistory): Promise<IUserPasswordHistory> {
|
|
129
|
+
const created = {...data, _id: `${this.items.length + 1}`, createdAt: new Date()}
|
|
130
|
+
this.items.unshift(created)
|
|
131
|
+
return created
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async findLatestByUserId(userId: string, limit: number): Promise<IUserPasswordHistory[]> {
|
|
135
|
+
return this.items.filter((item) => item.user === userId).slice(0, limit)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
9
138
|
|
|
10
139
|
describe("UserServiceTest", function () {
|
|
11
|
-
let userRepository: IUserRepository
|
|
12
|
-
let userService
|
|
13
|
-
let
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
userAdminData = (await import("../data-obj/users/root-mongo-user")).default
|
|
21
|
-
return
|
|
22
|
-
})
|
|
140
|
+
let userRepository: IUserRepository
|
|
141
|
+
let userService: UserService
|
|
142
|
+
let userAdminData: IUserCreate
|
|
143
|
+
|
|
144
|
+
beforeEach(async () => {
|
|
145
|
+
userRepository = new InMemoryUserRepository()
|
|
146
|
+
const userPasswordHistoryService = new UserPasswordHistoryService(new InMemoryUserPasswordHistoryRepository())
|
|
147
|
+
const passwordPolicyService = new PasswordPolicyService(new PasswordPolicyResolver(), userRepository, userPasswordHistoryService)
|
|
148
|
+
userService = new UserService(userRepository, passwordPolicyService, userPasswordHistoryService)
|
|
23
149
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
150
|
+
userAdminData = {
|
|
151
|
+
_id: "user-1",
|
|
152
|
+
active: true,
|
|
153
|
+
groups: [],
|
|
154
|
+
username: "root",
|
|
155
|
+
email: "root@example.com",
|
|
156
|
+
password: "Root1234",
|
|
157
|
+
name: "root",
|
|
158
|
+
phone: "123456789",
|
|
159
|
+
role: "role-1",
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
await userService.create({...userAdminData})
|
|
27
163
|
})
|
|
28
164
|
|
|
29
165
|
it("should create user", async function () {
|
|
30
|
-
const userData = {...userAdminData}
|
|
166
|
+
const userData = {...userAdminData, _id: "user-2", username: "admin2", email: "admin2@example.com"}
|
|
31
167
|
let userCreated = await userService.create(userData)
|
|
32
|
-
expect(userCreated.username).toBe(
|
|
168
|
+
expect(userCreated.username).toBe(userData.username)
|
|
33
169
|
})
|
|
34
170
|
|
|
35
|
-
|
|
36
171
|
it("should find one user", async function () {
|
|
37
172
|
let user = await userService.findById(userAdminData._id)
|
|
38
173
|
expect(user.username).toBe(userAdminData.username)
|
|
@@ -42,31 +177,83 @@ describe("UserServiceTest", function () {
|
|
|
42
177
|
const userData = {...userAdminData}
|
|
43
178
|
const newName = "AdminUpdated"
|
|
44
179
|
userData.name = newName
|
|
45
|
-
let userUpdated = await userService.update(userAdminData._id, userData)
|
|
180
|
+
let userUpdated = await userService.update(userAdminData._id, userData as any)
|
|
46
181
|
expect(userUpdated.name).toBe(newName)
|
|
47
182
|
})
|
|
48
183
|
|
|
49
184
|
it("should fail create user with short password", async function () {
|
|
50
|
-
let userData = {...userAdminData, password: "123"}
|
|
185
|
+
let userData = {...userAdminData, _id: "user-3", username: "shortpass", email: "shortpass@example.com", password: "123"}
|
|
186
|
+
|
|
187
|
+
await expect(async () => {
|
|
188
|
+
await userService.create(userData)
|
|
189
|
+
}).rejects.toSatisfy((err) => {
|
|
190
|
+
expect(err).toBeInstanceOf(ValidationError)
|
|
191
|
+
expect(err.errors[0].field).toBe('password')
|
|
192
|
+
expect(err.errors[0].reason).toBe('validation.password.minLength')
|
|
193
|
+
return true;
|
|
194
|
+
});
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
it("create rejects password without uppercase according to policy", async function () {
|
|
198
|
+
let userData = {...userAdminData, _id: "user-4", username: "no-uppercase", email: "nouppercase@example.com", password: "lowercase1"}
|
|
51
199
|
|
|
52
200
|
await expect(async () => {
|
|
53
201
|
await userService.create(userData)
|
|
54
202
|
}).rejects.toSatisfy((err) => {
|
|
55
203
|
expect(err).toBeInstanceOf(ValidationError)
|
|
56
204
|
expect(err.errors[0].field).toBe('password')
|
|
57
|
-
expect(err.errors[0].reason).toBe('validation.password.
|
|
205
|
+
expect(err.errors[0].reason).toBe('validation.password.requireUppercase')
|
|
58
206
|
return true;
|
|
59
207
|
});
|
|
60
208
|
})
|
|
61
209
|
|
|
210
|
+
it("changeUserPassword rejects password that does not meet policy", async function () {
|
|
211
|
+
const userId = userAdminData._id
|
|
212
|
+
await expect(async () => {
|
|
213
|
+
await userService.changeUserPassword(userId, "lowercase1")
|
|
214
|
+
}).rejects.toSatisfy((err) => {
|
|
215
|
+
expect(err).toBeInstanceOf(ValidationError)
|
|
216
|
+
expect(err.errors[0].field).toBe('newPassword')
|
|
217
|
+
expect(err.errors[0].reason).toBe('validation.password.requireUppercase')
|
|
218
|
+
return true;
|
|
219
|
+
});
|
|
220
|
+
})
|
|
62
221
|
|
|
63
|
-
it("
|
|
222
|
+
it("changeOwnPassword rejects password that does not meet policy", async function () {
|
|
64
223
|
const userId = userAdminData._id
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
224
|
+
await expect(async () => {
|
|
225
|
+
await userService.changeOwnPassword(userId, "Root1234", "short1A")
|
|
226
|
+
}).rejects.toSatisfy((err) => {
|
|
227
|
+
expect(err).toBeInstanceOf(ValidationError)
|
|
228
|
+
expect(err.errors[0].field).toBe('newPassword')
|
|
229
|
+
expect(err.errors[0].reason).toBe('validation.password.minLength')
|
|
230
|
+
return true;
|
|
231
|
+
});
|
|
68
232
|
})
|
|
69
233
|
|
|
234
|
+
it("changeUserPasswordByCode rejects password that does not meet policy", async function () {
|
|
235
|
+
const code = await userService.recoveryCode(userAdminData.email)
|
|
236
|
+
await expect(async () => {
|
|
237
|
+
await userService.changeUserPasswordByCode(code, "lowercase1")
|
|
238
|
+
}).rejects.toSatisfy((err) => {
|
|
239
|
+
expect(err).toBeInstanceOf(ValidationError)
|
|
240
|
+
expect(err.errors[0].field).toBe('newPassword')
|
|
241
|
+
expect(err.errors[0].reason).toBe('validation.password.requireUppercase')
|
|
242
|
+
return true;
|
|
243
|
+
});
|
|
244
|
+
})
|
|
70
245
|
|
|
246
|
+
it("preventReuse rejects a recently used password", async function () {
|
|
247
|
+
const userId = userAdminData._id
|
|
248
|
+
await userService.changeUserPassword(userId, "SecondPass1")
|
|
71
249
|
|
|
250
|
+
await expect(async () => {
|
|
251
|
+
await userService.changeUserPassword(userId, "Root1234")
|
|
252
|
+
}).rejects.toSatisfy((err) => {
|
|
253
|
+
expect(err).toBeInstanceOf(ValidationError)
|
|
254
|
+
expect(err.errors[0].field).toBe('newPassword')
|
|
255
|
+
expect(err.errors[0].reason).toBe('validation.password.preventReuse')
|
|
256
|
+
return true;
|
|
257
|
+
});
|
|
258
|
+
})
|
|
72
259
|
})
|
package/test/setup/TestSetup.ts
CHANGED
|
@@ -20,6 +20,7 @@ import basicUserData from "./data/basic-user";
|
|
|
20
20
|
|
|
21
21
|
import {IUser, IRole} from "@drax/identity-share";
|
|
22
22
|
import MongoInMemory from "./MongoInMemory";
|
|
23
|
+
import {randomUUID} from "crypto";
|
|
23
24
|
|
|
24
25
|
class TestSetup {
|
|
25
26
|
|
|
@@ -29,8 +30,12 @@ class TestSetup {
|
|
|
29
30
|
private _basicUser: IUser;
|
|
30
31
|
private _adminRole: IRole;
|
|
31
32
|
private _restrictedRole: IRole;
|
|
33
|
+
private readonly dbEngine: "mongo" | "sqlite";
|
|
34
|
+
private readonly sqliteFile: string;
|
|
32
35
|
|
|
33
|
-
constructor() {
|
|
36
|
+
constructor(dbEngine: "mongo" | "sqlite" = "mongo", sqliteFile?: string) {
|
|
37
|
+
this.dbEngine = dbEngine
|
|
38
|
+
this.sqliteFile = sqliteFile || `/tmp/drax-identity-back-${randomUUID()}.sqlite`
|
|
34
39
|
}
|
|
35
40
|
|
|
36
41
|
async setup() {
|
|
@@ -45,8 +50,11 @@ class TestSetup {
|
|
|
45
50
|
|
|
46
51
|
setupEnvironmentVariables() {
|
|
47
52
|
// Define environment variables
|
|
48
|
-
process.env.DRAX_DB_ENGINE =
|
|
53
|
+
process.env.DRAX_DB_ENGINE = this.dbEngine;
|
|
49
54
|
process.env.DRAX_JWT_SECRET = "xxx";
|
|
55
|
+
if (this.dbEngine === "sqlite") {
|
|
56
|
+
process.env.DRAX_SQLITE_FILE = this.sqliteFile;
|
|
57
|
+
}
|
|
50
58
|
}
|
|
51
59
|
|
|
52
60
|
setupConfig() {
|
|
@@ -80,6 +88,9 @@ class TestSetup {
|
|
|
80
88
|
}
|
|
81
89
|
|
|
82
90
|
async setupMongoInMemoryAndConnect() {
|
|
91
|
+
if (this.dbEngine !== "mongo") {
|
|
92
|
+
return
|
|
93
|
+
}
|
|
83
94
|
this._mongoInMemory = new MongoInMemory();
|
|
84
95
|
await this._mongoInMemory.connect();
|
|
85
96
|
}
|
|
@@ -95,11 +106,18 @@ class TestSetup {
|
|
|
95
106
|
}
|
|
96
107
|
|
|
97
108
|
async dropData() {
|
|
98
|
-
|
|
109
|
+
if (this._mongoInMemory) {
|
|
110
|
+
await this._mongoInMemory.dropData()
|
|
111
|
+
}
|
|
99
112
|
}
|
|
100
113
|
|
|
101
114
|
async dropAndClose() {
|
|
102
|
-
|
|
115
|
+
if (this._fastifyInstance) {
|
|
116
|
+
await this._fastifyInstance.close()
|
|
117
|
+
}
|
|
118
|
+
if (this._mongoInMemory) {
|
|
119
|
+
await this._mongoInMemory.dropAndClose()
|
|
120
|
+
}
|
|
103
121
|
}
|
|
104
122
|
|
|
105
123
|
async login(username: string, password: string): Promise<{accessToken: string}> {
|