@drax/identity-back 0.38.0 → 0.39.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/controllers/UserController.js +50 -7
- package/dist/graphql/resolvers/user.resolvers.js +8 -2
- package/dist/rbac/Rbac.js +3 -3
- package/dist/services/UserService.js +8 -8
- package/dist/utils/AuthUtils.js +3 -6
- package/package.json +7 -7
- package/src/controllers/UserController.ts +63 -8
- package/src/graphql/resolvers/user.resolvers.ts +9 -2
- package/src/rbac/Rbac.ts +3 -3
- package/src/services/UserService.ts +12 -12
- package/src/utils/AuthUtils.ts +5 -7
- package/tsconfig.tsbuildinfo +1 -1
- package/types/controllers/UserController.d.ts +2 -0
- package/types/controllers/UserController.d.ts.map +1 -1
- package/types/graphql/resolvers/user.resolvers.d.ts +3 -3
- package/types/graphql/resolvers/user.resolvers.d.ts.map +1 -1
- package/types/services/UserService.d.ts +5 -5
- package/types/services/UserService.d.ts.map +1 -1
- package/types/utils/AuthUtils.d.ts +1 -1
- package/types/utils/AuthUtils.d.ts.map +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AbstractFastifyController } from "@drax/crud-back";
|
|
2
2
|
import RegistrationCompleteHtml from "../html/RegistrationCompleteHtml.js";
|
|
3
|
-
import { CommonConfig, DraxConfig, StoreManager, ValidationError, UnauthorizedError, SecuritySensitiveError, } from "@drax/common-back";
|
|
3
|
+
import { CommonConfig, DraxConfig, StoreManager, ValidationError, UnauthorizedError, SecuritySensitiveError, BadRequestError, } from "@drax/common-back";
|
|
4
4
|
import UserServiceFactory from "../factory/UserServiceFactory.js";
|
|
5
5
|
import RoleServiceFactory from "../factory/RoleServiceFactory.js";
|
|
6
6
|
import UserPermissions from "../permissions/UserPermissions.js";
|
|
@@ -17,6 +17,7 @@ class UserController extends AbstractFastifyController {
|
|
|
17
17
|
super(UserServiceFactory(), UserPermissions);
|
|
18
18
|
this.tenantField = "tenant";
|
|
19
19
|
this.tenantFilter = true;
|
|
20
|
+
this.entityName = 'User';
|
|
20
21
|
}
|
|
21
22
|
async auth(request, reply) {
|
|
22
23
|
try {
|
|
@@ -62,13 +63,37 @@ class UserController extends AbstractFastifyController {
|
|
|
62
63
|
this.handleError(e, reply);
|
|
63
64
|
}
|
|
64
65
|
}
|
|
66
|
+
async onUserEvent(request, action, resourceId = null, detail = null) {
|
|
67
|
+
const requestData = this.extractRequestData(request);
|
|
68
|
+
const eventData = {
|
|
69
|
+
action: action,
|
|
70
|
+
entity: this.entityName,
|
|
71
|
+
resourceId: resourceId.toString(),
|
|
72
|
+
postItem: null,
|
|
73
|
+
preItem: null,
|
|
74
|
+
detail: detail,
|
|
75
|
+
timestamp: new Date(),
|
|
76
|
+
...requestData
|
|
77
|
+
};
|
|
78
|
+
this.eventEmitter.emitCrudEvent(eventData);
|
|
79
|
+
}
|
|
65
80
|
async switchTenant(request, reply) {
|
|
66
81
|
try {
|
|
67
82
|
request.rbac.assertPermission(UserPermissions.SwitchTenant);
|
|
68
83
|
if (request.authUser && request.token) {
|
|
69
84
|
const tenantId = request.body.tenantId;
|
|
85
|
+
if (!tenantId) {
|
|
86
|
+
throw new BadRequestError('Missing tenantId');
|
|
87
|
+
}
|
|
88
|
+
const tenant = await TenantServiceFactory().findById(tenantId);
|
|
89
|
+
if (!tenant) {
|
|
90
|
+
throw new BadRequestError('Invalid tenantId');
|
|
91
|
+
}
|
|
92
|
+
const tenantName = tenant?.name;
|
|
70
93
|
const userService = UserServiceFactory();
|
|
71
|
-
let { accessToken } = await userService.switchTenant(request.token, tenantId);
|
|
94
|
+
let { accessToken } = await userService.switchTenant(request.token, tenantId, tenantName);
|
|
95
|
+
const detail = `Switched to tenant "${tenantName}" (ID: ${tenantId})`;
|
|
96
|
+
this.onUserEvent(request, 'switchTenant', request.rbac.userId, detail);
|
|
72
97
|
return { accessToken };
|
|
73
98
|
}
|
|
74
99
|
else {
|
|
@@ -141,6 +166,8 @@ class UserController extends AbstractFastifyController {
|
|
|
141
166
|
const userService = UserServiceFactory();
|
|
142
167
|
let user = await userService.register(payload);
|
|
143
168
|
if (user) {
|
|
169
|
+
const detail = `User ${user?.username} registered successfully.`;
|
|
170
|
+
this.onUserEvent(request, 'register', user?._id, detail);
|
|
144
171
|
//SEND EMAIL FOR EMAIL VERIFICATION
|
|
145
172
|
await UserEmailService.emailVerifyCode(user.emailCode, user.email);
|
|
146
173
|
return {
|
|
@@ -187,6 +214,7 @@ class UserController extends AbstractFastifyController {
|
|
|
187
214
|
payload.origin ?? (payload.origin = 'Admin');
|
|
188
215
|
const userService = UserServiceFactory();
|
|
189
216
|
let user = await userService.create(payload);
|
|
217
|
+
this.onCreated(request, user);
|
|
190
218
|
return user;
|
|
191
219
|
}
|
|
192
220
|
catch (e) {
|
|
@@ -202,7 +230,9 @@ class UserController extends AbstractFastifyController {
|
|
|
202
230
|
payload.tenant = request.rbac.getAuthUser.tenantId;
|
|
203
231
|
}
|
|
204
232
|
const userService = UserServiceFactory();
|
|
233
|
+
let preUser = await userService.findById(id);
|
|
205
234
|
let user = await userService.update(id, payload);
|
|
235
|
+
this.onUpdated(request, preUser, user);
|
|
206
236
|
return user;
|
|
207
237
|
}
|
|
208
238
|
catch (e) {
|
|
@@ -214,8 +244,10 @@ class UserController extends AbstractFastifyController {
|
|
|
214
244
|
request.rbac.assertPermission(UserPermissions.Delete);
|
|
215
245
|
const id = request.params.id;
|
|
216
246
|
const userService = UserServiceFactory();
|
|
247
|
+
let preUser = await userService.findById(id);
|
|
217
248
|
let r = await userService.delete(id);
|
|
218
249
|
if (r) {
|
|
250
|
+
this.onDeleted(request, preUser);
|
|
219
251
|
reply.send({
|
|
220
252
|
id: id,
|
|
221
253
|
message: 'Item deleted successfully',
|
|
@@ -246,11 +278,14 @@ class UserController extends AbstractFastifyController {
|
|
|
246
278
|
throw new ValidationError([{ field: 'email', reason: 'validation.email.invalid' }]);
|
|
247
279
|
}
|
|
248
280
|
const userService = UserServiceFactory();
|
|
281
|
+
const user = await userService.findByEmail(email);
|
|
249
282
|
const code = await userService.recoveryCode(email);
|
|
250
283
|
console.log("CODE", code);
|
|
251
284
|
if (code) {
|
|
252
285
|
await UserEmailService.recoveryCode(code, email);
|
|
253
286
|
}
|
|
287
|
+
const detail = `User ${user?.username} request a password recovery .`;
|
|
288
|
+
this.onUserEvent(request, 'passwordRecoveryRequest', user?._id, detail);
|
|
254
289
|
reply.send({ message });
|
|
255
290
|
}
|
|
256
291
|
catch (e) {
|
|
@@ -274,8 +309,10 @@ class UserController extends AbstractFastifyController {
|
|
|
274
309
|
throw new ValidationError([{ field: 'newPassword', reason: 'validation.required' }]);
|
|
275
310
|
}
|
|
276
311
|
const userService = UserServiceFactory();
|
|
277
|
-
const
|
|
278
|
-
if (
|
|
312
|
+
const user = await userService.changeUserPasswordByCode(recoveryCode, newPassword);
|
|
313
|
+
if (user) {
|
|
314
|
+
const detail = `User ${user?.username} complete a password recovery .`;
|
|
315
|
+
this.onUserEvent(request, 'passwordRecoveryCompleted', user?._id, detail);
|
|
279
316
|
reply.send({ message: 'action.success' });
|
|
280
317
|
}
|
|
281
318
|
else {
|
|
@@ -296,7 +333,9 @@ class UserController extends AbstractFastifyController {
|
|
|
296
333
|
const currentPassword = request.body.currentPassword;
|
|
297
334
|
const newPassword = request.body.newPassword;
|
|
298
335
|
const userService = UserServiceFactory();
|
|
299
|
-
await userService.changeOwnPassword(userId, currentPassword, newPassword);
|
|
336
|
+
const user = await userService.changeOwnPassword(userId, currentPassword, newPassword);
|
|
337
|
+
const detail = `User ${user?.username} changed his password.`;
|
|
338
|
+
this.onUserEvent(request, 'changeMyPassword', user?._id, detail);
|
|
300
339
|
return { message: 'Password updated successfully' };
|
|
301
340
|
}
|
|
302
341
|
catch (e) {
|
|
@@ -312,7 +351,9 @@ class UserController extends AbstractFastifyController {
|
|
|
312
351
|
}
|
|
313
352
|
const newPassword = request.body.newPassword;
|
|
314
353
|
const userService = UserServiceFactory();
|
|
315
|
-
await userService.changeUserPassword(userId, newPassword);
|
|
354
|
+
const user = await userService.changeUserPassword(userId, newPassword);
|
|
355
|
+
const detail = `User ${request.rbac.username} changed password for user ${user.username}.`;
|
|
356
|
+
this.onUserEvent(request, 'changePassword', user?._id, detail);
|
|
316
357
|
return { message: 'Password updated successfully' };
|
|
317
358
|
}
|
|
318
359
|
catch (e) {
|
|
@@ -334,7 +375,9 @@ class UserController extends AbstractFastifyController {
|
|
|
334
375
|
const urlFile = BASE_URL + '/api/users/avatar/' + storedFile.filename;
|
|
335
376
|
//Save into DB
|
|
336
377
|
const userService = UserServiceFactory();
|
|
337
|
-
await userService.changeAvatar(userId, urlFile);
|
|
378
|
+
const user = await userService.changeAvatar(userId, urlFile);
|
|
379
|
+
const detail = `User ${request.rbac.username} changed avatar.`;
|
|
380
|
+
this.onUserEvent(request, 'changeAvatar', user?._id, detail);
|
|
338
381
|
return {
|
|
339
382
|
filename: storedFile.filename,
|
|
340
383
|
size: storedFile.size,
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import UserServiceFactory from "../../factory/UserServiceFactory.js";
|
|
2
2
|
import { GraphQLError } from "graphql";
|
|
3
|
-
import { ValidationErrorToGraphQLError, ValidationError, StoreManager, DraxConfig, CommonConfig } from "@drax/common-back";
|
|
3
|
+
import { ValidationErrorToGraphQLError, ValidationError, StoreManager, DraxConfig, CommonConfig, BadRequestError } from "@drax/common-back";
|
|
4
4
|
import { UnauthorizedError } from "@drax/common-back";
|
|
5
5
|
import BadCredentialsError from "../../errors/BadCredentialsError.js";
|
|
6
6
|
import { join } from "path";
|
|
7
7
|
import IdentityConfig from "../../config/IdentityConfig.js";
|
|
8
8
|
import UserPermissions from "../../permissions/UserPermissions.js";
|
|
9
|
+
import TenantServiceFactory from "../../factory/TenantServiceFactory.js";
|
|
9
10
|
export default {
|
|
10
11
|
Query: {
|
|
11
12
|
me: async (_, {}, { authUser }) => {
|
|
@@ -79,8 +80,13 @@ export default {
|
|
|
79
80
|
rbac.assertPermission(UserPermissions.SwitchTenant);
|
|
80
81
|
if (authUser && token) {
|
|
81
82
|
const tenantId = input.tenantId;
|
|
83
|
+
const tenant = await TenantServiceFactory().findById(tenantId);
|
|
84
|
+
if (!tenant) {
|
|
85
|
+
throw new BadRequestError('Invalid tenantId');
|
|
86
|
+
}
|
|
87
|
+
const tenantName = tenant?.name;
|
|
82
88
|
const userService = UserServiceFactory();
|
|
83
|
-
let { accessToken } = await userService.switchTenant(token, tenantId);
|
|
89
|
+
let { accessToken } = await userService.switchTenant(token, tenantId, tenantName);
|
|
84
90
|
return { accessToken };
|
|
85
91
|
}
|
|
86
92
|
else {
|
package/dist/rbac/Rbac.js
CHANGED
|
@@ -30,13 +30,13 @@ class Rbac {
|
|
|
30
30
|
return this.authUser?.session;
|
|
31
31
|
}
|
|
32
32
|
get apiKeyId() {
|
|
33
|
-
return this.authUser?.apiKeyId
|
|
33
|
+
return this.authUser?.apiKeyId?.toString();
|
|
34
34
|
}
|
|
35
35
|
get apiKeyName() {
|
|
36
36
|
return this.authUser?.apiKeyName;
|
|
37
37
|
}
|
|
38
38
|
get roleId() {
|
|
39
|
-
return this.authUser?.roleId
|
|
39
|
+
return this.authUser?.roleId?.toString();
|
|
40
40
|
}
|
|
41
41
|
get roleName() {
|
|
42
42
|
return this.authUser?.roleName;
|
|
@@ -45,7 +45,7 @@ class Rbac {
|
|
|
45
45
|
return this.authUser?.tenantId?.toString();
|
|
46
46
|
}
|
|
47
47
|
get tenantName() {
|
|
48
|
-
return this.authUser?.tenantName;
|
|
48
|
+
return this.authUser?.tenantName ?? undefined;
|
|
49
49
|
}
|
|
50
50
|
assertAuthenticated() {
|
|
51
51
|
if (!this.authUser) {
|
|
@@ -51,8 +51,8 @@ class UserService extends AbstractService {
|
|
|
51
51
|
});
|
|
52
52
|
return sessionUUID;
|
|
53
53
|
}
|
|
54
|
-
async switchTenant(accessToken, tenantId) {
|
|
55
|
-
const newAccessToken = AuthUtils.switchTenant(accessToken, tenantId);
|
|
54
|
+
async switchTenant(accessToken, tenantId, tenantName) {
|
|
55
|
+
const newAccessToken = AuthUtils.switchTenant(accessToken, tenantId, tenantName);
|
|
56
56
|
return { accessToken: newAccessToken };
|
|
57
57
|
}
|
|
58
58
|
async authByEmail(email, createIfNotFound = false, userData, { userAgent, ip }) {
|
|
@@ -87,7 +87,8 @@ class UserService extends AbstractService {
|
|
|
87
87
|
if (user) {
|
|
88
88
|
newPassword = AuthUtils.hashPassword(newPassword);
|
|
89
89
|
await this._repository.changePassword(userId, newPassword);
|
|
90
|
-
|
|
90
|
+
delete user.password;
|
|
91
|
+
return user;
|
|
91
92
|
}
|
|
92
93
|
else {
|
|
93
94
|
throw new ValidationError([{ field: 'userId', reason: 'validation.notFound' }]);
|
|
@@ -102,7 +103,8 @@ class UserService extends AbstractService {
|
|
|
102
103
|
if (AuthUtils.checkPassword(currentPassword, user.password)) {
|
|
103
104
|
newPassword = AuthUtils.hashPassword(newPassword);
|
|
104
105
|
await this._repository.changePassword(userId, newPassword);
|
|
105
|
-
|
|
106
|
+
delete user.password;
|
|
107
|
+
return user;
|
|
106
108
|
}
|
|
107
109
|
else {
|
|
108
110
|
throw new ValidationError([{ field: 'currentPassword', reason: 'validation.notMatch' }]);
|
|
@@ -116,7 +118,7 @@ class UserService extends AbstractService {
|
|
|
116
118
|
const user = await this.findById(userId);
|
|
117
119
|
if (user && user.active) {
|
|
118
120
|
await this._repository.changeAvatar(userId, avatar);
|
|
119
|
-
return
|
|
121
|
+
return user;
|
|
120
122
|
}
|
|
121
123
|
else {
|
|
122
124
|
throw new BadCredentialsError();
|
|
@@ -141,14 +143,12 @@ class UserService extends AbstractService {
|
|
|
141
143
|
}
|
|
142
144
|
async changeUserPasswordByCode(recoveryCode, newPassword) {
|
|
143
145
|
try {
|
|
144
|
-
console.log("changeUserPasswordByCode recoveryCode", recoveryCode);
|
|
145
146
|
const user = await this._repository.findByRecoveryCode(recoveryCode);
|
|
146
|
-
console.log("changeUserPasswordByCode user", user);
|
|
147
147
|
if (user && user.active) {
|
|
148
148
|
newPassword = AuthUtils.hashPassword(newPassword);
|
|
149
149
|
await this._repository.changePassword(user._id, newPassword);
|
|
150
150
|
await this._repository.updatePartial(user._id, { recoveryCode: null });
|
|
151
|
-
return
|
|
151
|
+
return user;
|
|
152
152
|
}
|
|
153
153
|
else {
|
|
154
154
|
throw new ValidationError([{ field: 'recoveryCode', reason: 'validation.notFound' }]);
|
package/dist/utils/AuthUtils.js
CHANGED
|
@@ -54,23 +54,20 @@ class AuthUtils {
|
|
|
54
54
|
// Generar el hash en formato hexadecimal
|
|
55
55
|
return hmac.digest('hex');
|
|
56
56
|
}
|
|
57
|
-
static switchTenant(accessToken, newTenantId) {
|
|
57
|
+
static switchTenant(accessToken, newTenantId, tenantName) {
|
|
58
58
|
// Verificar que el token actual sea válido
|
|
59
59
|
const tokenPayload = AuthUtils.verifyToken(accessToken);
|
|
60
60
|
if (!tokenPayload) {
|
|
61
61
|
throw new Error("Invalid access token");
|
|
62
62
|
}
|
|
63
63
|
tokenPayload.tenantId = newTenantId;
|
|
64
|
+
tokenPayload.tenantName = tenantName;
|
|
64
65
|
const JWT_SECRET = DraxConfig.getOrLoad(IdentityConfig.JwtSecret);
|
|
65
66
|
if (!JWT_SECRET) {
|
|
66
67
|
throw new Error("JWT_SECRET ENV must be provided");
|
|
67
68
|
}
|
|
68
|
-
const JWT_ISSUER = DraxConfig.getOrLoad(IdentityConfig.JwtIssuer) || 'DRAX';
|
|
69
69
|
const options = {
|
|
70
|
-
|
|
71
|
-
algorithm: 'HS256',
|
|
72
|
-
audience: tokenPayload.username,
|
|
73
|
-
issuer: JWT_ISSUER
|
|
70
|
+
algorithm: 'HS256'
|
|
74
71
|
};
|
|
75
72
|
return jsonwebtoken.sign(tokenPayload, JWT_SECRET, options);
|
|
76
73
|
}
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.39.0",
|
|
7
7
|
"description": "Identity module for user management, authentication and authorization.",
|
|
8
8
|
"main": "dist/index.js",
|
|
9
9
|
"types": "types/index.d.ts",
|
|
@@ -28,11 +28,11 @@
|
|
|
28
28
|
"author": "Cristian Incarnato & Drax Team",
|
|
29
29
|
"license": "ISC",
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@drax/common-back": "^0.
|
|
32
|
-
"@drax/crud-back": "^0.
|
|
33
|
-
"@drax/crud-share": "^0.
|
|
34
|
-
"@drax/email-back": "^0.
|
|
35
|
-
"@drax/identity-share": "^0.
|
|
31
|
+
"@drax/common-back": "^0.39.0",
|
|
32
|
+
"@drax/crud-back": "^0.39.0",
|
|
33
|
+
"@drax/crud-share": "^0.39.0",
|
|
34
|
+
"@drax/email-back": "^0.39.0",
|
|
35
|
+
"@drax/identity-share": "^0.39.0",
|
|
36
36
|
"bcryptjs": "^2.4.3",
|
|
37
37
|
"graphql": "^16.8.2",
|
|
38
38
|
"jsonwebtoken": "^9.0.2"
|
|
@@ -63,5 +63,5 @@
|
|
|
63
63
|
"debug": "0"
|
|
64
64
|
}
|
|
65
65
|
},
|
|
66
|
-
"gitHead": "
|
|
66
|
+
"gitHead": "b019c40f954cf60e4ff61c53e27d5bafaea6f16c"
|
|
67
67
|
}
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
DraxConfig,
|
|
7
7
|
StoreManager,
|
|
8
8
|
ValidationError,
|
|
9
|
-
UnauthorizedError, SecuritySensitiveError,
|
|
9
|
+
UnauthorizedError, SecuritySensitiveError, BadRequestError,
|
|
10
10
|
} from "@drax/common-back";
|
|
11
11
|
|
|
12
12
|
import UserServiceFactory from "../factory/UserServiceFactory.js";
|
|
@@ -17,8 +17,9 @@ import BadCredentialsError from "../errors/BadCredentialsError.js";
|
|
|
17
17
|
import {join} from "path";
|
|
18
18
|
import {IdentityConfig} from "../config/IdentityConfig.js";
|
|
19
19
|
import UserEmailService from "../services/UserEmailService.js";
|
|
20
|
-
import {IDraxFieldFilter} from "@drax/crud-share";
|
|
20
|
+
import {IDraxCrudEvent, IDraxFieldFilter} from "@drax/crud-share";
|
|
21
21
|
import TenantServiceFactory from "../factory/TenantServiceFactory.js";
|
|
22
|
+
import {CustomRequest} from "@drax/crud-back/dist";
|
|
22
23
|
|
|
23
24
|
const BASE_FILE_DIR = DraxConfig.getOrLoad(CommonConfig.FileDir) || 'files';
|
|
24
25
|
const AVATAR_DIR = DraxConfig.getOrLoad(IdentityConfig.AvatarDir) || 'avatar';
|
|
@@ -33,6 +34,7 @@ class UserController extends AbstractFastifyController<IUser, IUserCreate, IUser
|
|
|
33
34
|
super(UserServiceFactory(), UserPermissions)
|
|
34
35
|
this.tenantField = "tenant";
|
|
35
36
|
this.tenantFilter = true;
|
|
37
|
+
this.entityName = 'User'
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
async auth(request, reply) {
|
|
@@ -83,13 +85,45 @@ class UserController extends AbstractFastifyController<IUser, IUserCreate, IUser
|
|
|
83
85
|
}
|
|
84
86
|
}
|
|
85
87
|
|
|
88
|
+
async onUserEvent(request: CustomRequest, action: string, resourceId: string = null, detail: string = null) {
|
|
89
|
+
const requestData = this.extractRequestData(request)
|
|
90
|
+
const eventData : IDraxCrudEvent = {
|
|
91
|
+
action: action,
|
|
92
|
+
entity: this.entityName,
|
|
93
|
+
resourceId: resourceId.toString(),
|
|
94
|
+
postItem: null,
|
|
95
|
+
preItem: null,
|
|
96
|
+
detail: detail,
|
|
97
|
+
timestamp: new Date(),
|
|
98
|
+
...requestData
|
|
99
|
+
}
|
|
100
|
+
this.eventEmitter.emitCrudEvent(eventData)
|
|
101
|
+
}
|
|
102
|
+
|
|
86
103
|
async switchTenant(request, reply) {
|
|
87
104
|
try {
|
|
105
|
+
|
|
88
106
|
request.rbac.assertPermission(UserPermissions.SwitchTenant)
|
|
107
|
+
|
|
89
108
|
if (request.authUser && request.token) {
|
|
90
109
|
const tenantId = request.body.tenantId
|
|
110
|
+
if(!tenantId){
|
|
111
|
+
throw new BadRequestError('Missing tenantId')
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const tenant = await TenantServiceFactory().findById(tenantId);
|
|
115
|
+
|
|
116
|
+
if(!tenant){
|
|
117
|
+
throw new BadRequestError('Invalid tenantId')
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const tenantName = tenant?.name;
|
|
91
121
|
const userService = UserServiceFactory()
|
|
92
|
-
let {accessToken} = await userService.switchTenant(request.token, tenantId)
|
|
122
|
+
let {accessToken} = await userService.switchTenant(request.token, tenantId, tenantName)
|
|
123
|
+
|
|
124
|
+
const detail = `Switched to tenant "${tenantName}" (ID: ${tenantId})`;
|
|
125
|
+
this.onUserEvent(request,'switchTenant',request.rbac.userId, detail)
|
|
126
|
+
|
|
93
127
|
return {accessToken}
|
|
94
128
|
} else {
|
|
95
129
|
throw new UnauthorizedError()
|
|
@@ -169,6 +203,9 @@ class UserController extends AbstractFastifyController<IUser, IUserCreate, IUser
|
|
|
169
203
|
let user = await userService.register(payload)
|
|
170
204
|
|
|
171
205
|
if(user){
|
|
206
|
+
const detail = `User ${user?.username} registered successfully.`;
|
|
207
|
+
this.onUserEvent(request,'register',user?._id, detail)
|
|
208
|
+
|
|
172
209
|
//SEND EMAIL FOR EMAIL VERIFICATION
|
|
173
210
|
await UserEmailService.emailVerifyCode(user.emailCode, user.email)
|
|
174
211
|
|
|
@@ -221,6 +258,7 @@ class UserController extends AbstractFastifyController<IUser, IUserCreate, IUser
|
|
|
221
258
|
|
|
222
259
|
const userService = UserServiceFactory()
|
|
223
260
|
let user = await userService.create(payload)
|
|
261
|
+
this.onCreated(request, user)
|
|
224
262
|
return user
|
|
225
263
|
} catch (e) {
|
|
226
264
|
this.handleError(e,reply)
|
|
@@ -238,7 +276,9 @@ class UserController extends AbstractFastifyController<IUser, IUserCreate, IUser
|
|
|
238
276
|
}
|
|
239
277
|
|
|
240
278
|
const userService = UserServiceFactory()
|
|
279
|
+
let preUser = await userService.findById(id)
|
|
241
280
|
let user = await userService.update(id, payload)
|
|
281
|
+
this.onUpdated(request, preUser, user)
|
|
242
282
|
return user
|
|
243
283
|
} catch (e) {
|
|
244
284
|
this.handleError(e,reply)
|
|
@@ -250,8 +290,10 @@ class UserController extends AbstractFastifyController<IUser, IUserCreate, IUser
|
|
|
250
290
|
request.rbac.assertPermission(UserPermissions.Delete)
|
|
251
291
|
const id = request.params.id
|
|
252
292
|
const userService = UserServiceFactory()
|
|
293
|
+
let preUser = await userService.findById(id)
|
|
253
294
|
let r: boolean = await userService.delete(id)
|
|
254
295
|
if (r) {
|
|
296
|
+
this.onDeleted(request, preUser)
|
|
255
297
|
reply.send({
|
|
256
298
|
id: id,
|
|
257
299
|
message: 'Item deleted successfully',
|
|
@@ -284,6 +326,7 @@ class UserController extends AbstractFastifyController<IUser, IUserCreate, IUser
|
|
|
284
326
|
}
|
|
285
327
|
|
|
286
328
|
const userService = UserServiceFactory()
|
|
329
|
+
const user = await userService.findByEmail(email)
|
|
287
330
|
const code = await userService.recoveryCode(email)
|
|
288
331
|
|
|
289
332
|
console.log("CODE", code)
|
|
@@ -292,6 +335,9 @@ class UserController extends AbstractFastifyController<IUser, IUserCreate, IUser
|
|
|
292
335
|
await UserEmailService.recoveryCode(code, email)
|
|
293
336
|
}
|
|
294
337
|
|
|
338
|
+
const detail = `User ${user?.username} request a password recovery .`;
|
|
339
|
+
this.onUserEvent(request,'passwordRecoveryRequest',user?._id, detail)
|
|
340
|
+
|
|
295
341
|
reply.send({message})
|
|
296
342
|
|
|
297
343
|
} catch (e) {
|
|
@@ -319,8 +365,10 @@ class UserController extends AbstractFastifyController<IUser, IUserCreate, IUser
|
|
|
319
365
|
}
|
|
320
366
|
|
|
321
367
|
const userService = UserServiceFactory()
|
|
322
|
-
const
|
|
323
|
-
if(
|
|
368
|
+
const user: IUser = await userService.changeUserPasswordByCode(recoveryCode, newPassword)
|
|
369
|
+
if(user){
|
|
370
|
+
const detail = `User ${user?.username} complete a password recovery .`;
|
|
371
|
+
this.onUserEvent(request,'passwordRecoveryCompleted',user?._id, detail)
|
|
324
372
|
reply.send({message: 'action.success'})
|
|
325
373
|
}else{
|
|
326
374
|
reply.statusCode = 400
|
|
@@ -342,7 +390,9 @@ class UserController extends AbstractFastifyController<IUser, IUserCreate, IUser
|
|
|
342
390
|
const currentPassword = request.body.currentPassword
|
|
343
391
|
const newPassword = request.body.newPassword
|
|
344
392
|
const userService = UserServiceFactory()
|
|
345
|
-
await userService.changeOwnPassword(userId, currentPassword, newPassword)
|
|
393
|
+
const user = await userService.changeOwnPassword(userId, currentPassword, newPassword)
|
|
394
|
+
const detail = `User ${user?.username} changed his password.`;
|
|
395
|
+
this.onUserEvent(request,'changeMyPassword',user?._id, detail)
|
|
346
396
|
return {message: 'Password updated successfully'}
|
|
347
397
|
} catch (e) {
|
|
348
398
|
this.handleError(e,reply)
|
|
@@ -358,7 +408,9 @@ class UserController extends AbstractFastifyController<IUser, IUserCreate, IUser
|
|
|
358
408
|
}
|
|
359
409
|
const newPassword = request.body.newPassword
|
|
360
410
|
const userService = UserServiceFactory()
|
|
361
|
-
await userService.changeUserPassword(userId, newPassword)
|
|
411
|
+
const user = await userService.changeUserPassword(userId, newPassword)
|
|
412
|
+
const detail = `User ${request.rbac.username} changed password for user ${user.username}.`;
|
|
413
|
+
this.onUserEvent(request,'changePassword',user?._id, detail)
|
|
362
414
|
return {message: 'Password updated successfully'}
|
|
363
415
|
} catch (e) {
|
|
364
416
|
this.handleError(e,reply)
|
|
@@ -385,7 +437,10 @@ class UserController extends AbstractFastifyController<IUser, IUserCreate, IUser
|
|
|
385
437
|
|
|
386
438
|
//Save into DB
|
|
387
439
|
const userService = UserServiceFactory()
|
|
388
|
-
await userService.changeAvatar(userId, urlFile)
|
|
440
|
+
const user = await userService.changeAvatar(userId, urlFile)
|
|
441
|
+
|
|
442
|
+
const detail = `User ${request.rbac.username} changed avatar.`
|
|
443
|
+
this.onUserEvent(request,'changeAvatar',user?._id, detail)
|
|
389
444
|
|
|
390
445
|
return {
|
|
391
446
|
filename: storedFile.filename,
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
ValidationError,
|
|
6
6
|
StoreManager,
|
|
7
7
|
DraxConfig,
|
|
8
|
-
CommonConfig
|
|
8
|
+
CommonConfig, BadRequestError
|
|
9
9
|
} from "@drax/common-back";
|
|
10
10
|
import {UnauthorizedError} from "@drax/common-back";
|
|
11
11
|
import BadCredentialsError from "../../errors/BadCredentialsError.js";
|
|
@@ -13,6 +13,7 @@ import {join} from "path";
|
|
|
13
13
|
import IdentityConfig from "../../config/IdentityConfig.js";
|
|
14
14
|
import {IDraxPaginateOptions} from "@drax/crud-share";
|
|
15
15
|
import UserPermissions from "../../permissions/UserPermissions.js";
|
|
16
|
+
import TenantServiceFactory from "../../factory/TenantServiceFactory.js";
|
|
16
17
|
|
|
17
18
|
export default {
|
|
18
19
|
Query: {
|
|
@@ -88,8 +89,14 @@ export default {
|
|
|
88
89
|
rbac.assertPermission(UserPermissions.SwitchTenant)
|
|
89
90
|
if (authUser && token) {
|
|
90
91
|
const tenantId = input.tenantId
|
|
92
|
+
const tenant = await TenantServiceFactory().findById(tenantId);
|
|
93
|
+
|
|
94
|
+
if(!tenant){
|
|
95
|
+
throw new BadRequestError('Invalid tenantId')
|
|
96
|
+
}
|
|
97
|
+
const tenantName = tenant?.name;
|
|
91
98
|
const userService = UserServiceFactory()
|
|
92
|
-
let {accessToken} = await userService.switchTenant(token, tenantId)
|
|
99
|
+
let {accessToken} = await userService.switchTenant(token, tenantId, tenantName)
|
|
93
100
|
return {accessToken}
|
|
94
101
|
} else {
|
|
95
102
|
throw new UnauthorizedError()
|
package/src/rbac/Rbac.ts
CHANGED
|
@@ -41,7 +41,7 @@ class Rbac implements IRbac{
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
get apiKeyId(): string {
|
|
44
|
-
return this.authUser?.apiKeyId
|
|
44
|
+
return this.authUser?.apiKeyId?.toString()
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
get apiKeyName(): string {
|
|
@@ -49,7 +49,7 @@ class Rbac implements IRbac{
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
get roleId(): string {
|
|
52
|
-
return this.authUser?.roleId
|
|
52
|
+
return this.authUser?.roleId?.toString()
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
get roleName(): string {
|
|
@@ -61,7 +61,7 @@ class Rbac implements IRbac{
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
get tenantName(): string | undefined {
|
|
64
|
-
return this.authUser?.tenantName;
|
|
64
|
+
return this.authUser?.tenantName ?? undefined;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
assertAuthenticated() {
|
|
@@ -64,8 +64,8 @@ class UserService extends AbstractService<IUser, IUserCreate, IUserUpdate> {
|
|
|
64
64
|
return sessionUUID;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
async switchTenant(accessToken: string, tenantId: string) {
|
|
68
|
-
const newAccessToken = AuthUtils.switchTenant(accessToken, tenantId)
|
|
67
|
+
async switchTenant(accessToken: string, tenantId: string, tenantName: string) {
|
|
68
|
+
const newAccessToken = AuthUtils.switchTenant(accessToken, tenantId, tenantName)
|
|
69
69
|
return {accessToken: newAccessToken}
|
|
70
70
|
}
|
|
71
71
|
|
|
@@ -102,19 +102,20 @@ class UserService extends AbstractService<IUser, IUserCreate, IUserUpdate> {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
|
|
105
|
-
async changeUserPassword(userId: string, newPassword: string) {
|
|
105
|
+
async changeUserPassword(userId: string, newPassword: string):Promise<IUser> {
|
|
106
106
|
const user = await this._repository.findByIdWithPassword(userId)
|
|
107
107
|
if (user) {
|
|
108
108
|
newPassword = AuthUtils.hashPassword(newPassword)
|
|
109
109
|
await this._repository.changePassword(userId, newPassword)
|
|
110
|
-
|
|
110
|
+
delete user.password
|
|
111
|
+
return user
|
|
111
112
|
} else {
|
|
112
113
|
throw new ValidationError([{field: 'userId', reason: 'validation.notFound'}])
|
|
113
114
|
}
|
|
114
115
|
}
|
|
115
116
|
|
|
116
117
|
|
|
117
|
-
async changeOwnPassword(userId: string, currentPassword: string, newPassword: string) {
|
|
118
|
+
async changeOwnPassword(userId: string, currentPassword: string, newPassword: string): Promise<IUser> {
|
|
118
119
|
const user = await this._repository.findByIdWithPassword(userId)
|
|
119
120
|
if (user && user.active) {
|
|
120
121
|
|
|
@@ -125,7 +126,8 @@ class UserService extends AbstractService<IUser, IUserCreate, IUserUpdate> {
|
|
|
125
126
|
if (AuthUtils.checkPassword(currentPassword, user.password)) {
|
|
126
127
|
newPassword = AuthUtils.hashPassword(newPassword)
|
|
127
128
|
await this._repository.changePassword(userId, newPassword)
|
|
128
|
-
|
|
129
|
+
delete user.password
|
|
130
|
+
return user
|
|
129
131
|
} else {
|
|
130
132
|
throw new ValidationError([{field: 'currentPassword', reason: 'validation.notMatch'}])
|
|
131
133
|
}
|
|
@@ -135,11 +137,11 @@ class UserService extends AbstractService<IUser, IUserCreate, IUserUpdate> {
|
|
|
135
137
|
}
|
|
136
138
|
}
|
|
137
139
|
|
|
138
|
-
async changeAvatar(userId: string, avatar: string) {
|
|
140
|
+
async changeAvatar(userId: string, avatar: string):Promise<IUser> {
|
|
139
141
|
const user = await this.findById(userId)
|
|
140
142
|
if (user && user.active) {
|
|
141
143
|
await this._repository.changeAvatar(userId, avatar)
|
|
142
|
-
return
|
|
144
|
+
return user
|
|
143
145
|
} else {
|
|
144
146
|
throw new BadCredentialsError()
|
|
145
147
|
}
|
|
@@ -161,16 +163,14 @@ class UserService extends AbstractService<IUser, IUserCreate, IUserUpdate> {
|
|
|
161
163
|
}
|
|
162
164
|
}
|
|
163
165
|
|
|
164
|
-
async changeUserPasswordByCode(recoveryCode: string, newPassword: string): Promise<
|
|
166
|
+
async changeUserPasswordByCode(recoveryCode: string, newPassword: string): Promise<IUser> {
|
|
165
167
|
try {
|
|
166
|
-
console.log("changeUserPasswordByCode recoveryCode", recoveryCode)
|
|
167
168
|
const user = await this._repository.findByRecoveryCode(recoveryCode)
|
|
168
|
-
console.log("changeUserPasswordByCode user", user)
|
|
169
169
|
if (user && user.active) {
|
|
170
170
|
newPassword = AuthUtils.hashPassword(newPassword)
|
|
171
171
|
await this._repository.changePassword(user._id, newPassword)
|
|
172
172
|
await this._repository.updatePartial(user._id, {recoveryCode: null})
|
|
173
|
-
return
|
|
173
|
+
return user
|
|
174
174
|
} else {
|
|
175
175
|
throw new ValidationError([{field:'recoveryCode', reason: 'validation.notFound'}])
|
|
176
176
|
}
|