Skip to content

Commit 0fa8a23

Browse files
committed
done for password updates
1 parent ca06331 commit 0fa8a23

19 files changed

Lines changed: 825 additions & 648 deletions

File tree

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,35 @@
1-
const { registerUserUseCase,
2-
deleteUserUseCase,
3-
findAllUsersUseCase,
4-
findOneUserUseCase,
5-
loginUserUseCase,
6-
logoutUseCase,
7-
refreshTokenUseCase,
8-
updateUserUseCase,
9-
blockUserUseCase,
10-
unBlockUserUseCase
11-
} = require("./user-handlers");
12-
1+
const userUseCases = require("./user-handlers");
132
const { dbUserHandler } = require("../../../interface-adapters/database-access")
14-
// const {loginUserUseCase} = require("./login-user");
3+
const { makeUser, validateId, validateUserDataUpdates } = require("../../../enterprise-business-rules/entities");
4+
const { RequiredParameterError } = require("../../../interface-adapters/validators-errors/errors");
5+
const { logEvents } = require("../../../interface-adapters/middlewares/loggers/logger");
6+
const { makeHttpError } = require("../../../interface-adapters/validators-errors/http-error");
7+
158

169
const entityModels = require("../../../enterprise-business-rules/entities");
1710

18-
const registerUserUseCaseHandler = registerUserUseCase({ dbUserHandler, entityModels });
19-
const loginUserUseCaseHandler = loginUserUseCase({ dbUserHandler });
20-
const findOneUserUseCaseHandler = findOneUserUseCase({ dbUserHandler });
21-
const findAllUsersUseCaseHandler = findAllUsersUseCase({ dbUserHandler });
22-
const logoutUseCaseHandler = logoutUseCase();
23-
const refreshTokenUseCaseHandler = refreshTokenUseCase({ dbUserHandler });
24-
const updateUserUseCaseHandler = updateUserUseCase({ dbUserHandler });
25-
const deleteUserUseCaseHandler = deleteUserUseCase({ dbUserHandler });
26-
const blockUserUseCaseHandler = blockUserUseCase({ dbUserHandler });
27-
const unBlockUserUseCaseHandler = unBlockUserUseCase({ dbUserHandler });
11+
const registerUserUseCaseHandler = userUseCases.registerUserUseCase({ dbUserHandler, entityModels, logEvents, makeHttpError });
12+
13+
const loginUserUseCaseHandler = userUseCases.loginUserUseCase({ dbUserHandler, logEvents, makeHttpError });
14+
15+
const findOneUserUseCaseHandler = userUseCases.findOneUserUseCase({ dbUserHandler, validateId, logEvents });
16+
17+
const findAllUsersUseCaseHandler = userUseCases.findAllUsersUseCase({ dbUserHandler, logEvents });
18+
const logoutUseCaseHandler = userUseCases.logoutUseCase({ RequiredParameterError, logEvents });
19+
20+
const refreshTokenUseCaseHandler = userUseCases.refreshTokenUseCase({ dbUserHandler, RequiredParameterError, logEvents });
21+
22+
const updateUserUseCaseHandler = userUseCases.updateUserUseCase({ dbUserHandler, makeUser, validateId, RequiredParameterError, logEvents, makeHttpError });
23+
24+
const deleteUserUseCaseHandler = userUseCases.deleteUserUseCase({ dbUserHandler, validateId, RequiredParameterError, logEvents });
25+
26+
const blockUserUseCaseHandler = userUseCases.blockUserUseCase({ dbUserHandler, validateId, RequiredParameterError, logEvents });
27+
28+
const unBlockUserUseCaseHandler = userUseCases.unBlockUserUseCase({ dbUserHandler, validateId, RequiredParameterError, logEvents });
29+
30+
const forgotPasswordUseCaseHandler = userUseCases.forgotPasswordUseCase({ dbUserHandler, logEvents });
2831

32+
const resetPasswordUseCaseHandler = userUseCases.resetPasswordUseCase({ dbUserHandler, logEvents, makeHttpError });
2933

3034

3135
module.exports = {
@@ -39,5 +43,7 @@ module.exports = {
3943
registerUserUseCaseHandler,
4044
loginUserUseCaseHandler,
4145
blockUserUseCaseHandler,
42-
unBlockUserUseCaseHandler
46+
unBlockUserUseCaseHandler,
47+
forgotPasswordUseCaseHandler,
48+
resetPasswordUseCaseHandler
4349
}

application-business-rules/use-cases/user/user-handlers.js

Lines changed: 101 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,3 @@
1-
const { makeUser, validateId, validateUserDataUpdates } = require("../../../enterprise-business-rules/entities");
2-
const { UniqueConstraintError, InvalidPropertyError, RequiredParameterError } = require("../../../interface-adapters/validators-errors/errors");
3-
const { makeHttpError } = require("../../../interface-adapters/validators-errors/http-error");
4-
const jwt = require("jsonwebtoken");
5-
const bcrypt = require("bcrypt");
6-
const { logEvents } = require("../../../interface-adapters/middlewares/loggers/logger");
7-
81

92
module.exports = {
103

@@ -16,7 +9,7 @@ module.exports = {
169
* @return {Promise<Object|Error>} Returns a promise that resolves to the registered user object or rejects with an error.
1710
* @throws {HttpError} Throws an HttpError if the user already exists or if there is an error during registration.
1811
*/
19-
registerUserUseCase: ({ dbUserHandler, entityModels }) => async function registerUserUseCaseHandler(userData) {
12+
registerUserUseCase: ({ dbUserHandler, entityModels, logEvents, makeHttpError }) => async function registerUserUseCaseHandler(userData) {
2013

2114
const { makeUser } = entityModels;
2215
try {
@@ -34,7 +27,7 @@ module.exports = {
3427
}
3528

3629
} catch (error) {
37-
console.log("error from register use case handler: ", err);
30+
console.log("error from register use case handler: ", error);
3831
logEvents(
3932
`${error.no}:${error.code}\t${error.syscall}\t${error.hostname}`,
4033
"userHandlerErr.log"
@@ -57,11 +50,11 @@ module.exports = {
5750
* @throws {InvalidPropertyError} If the provided password does not match the stored password.
5851
* @return {Promise<Object>} An object containing the access token and an empty refresh token.
5952
*/
60-
loginUserUseCase: ({ dbUserHandler }) => {
53+
loginUserUseCase: ({ dbUserHandler, logEvents, makeHttpError }) => {
6154

6255
return async function loginUserUseCaseHandler(userData) {
6356

64-
const { email, password } = userData;
57+
const { email, password, bcrypt, jwt } = userData;
6558

6659
//basic verification
6760
if (!email || !password) {
@@ -133,7 +126,7 @@ module.exports = {
133126
* @return {Promise<{allUsers: Array}>} A promise that resolves to an object containing all users.
134127
* @throws {new Error} If no users are found in the database.
135128
*/
136-
findAllUsersUseCase: ({ dbUserHandler }) => {
129+
findAllUsersUseCase: ({ dbUserHandler, logEvents }) => {
137130

138131
return async function findAllUsersUseCaseHandler() {
139132
try {
@@ -158,7 +151,7 @@ module.exports = {
158151
* @return {Promise<{user: Object}>} A promise that resolves to an object containing the user.
159152
* @throws {new Error} If the user is not found.
160153
*/
161-
findOneUserUseCase: ({ dbUserHandler }) => {
154+
findOneUserUseCase: ({ dbUserHandler, validateId, logEvents }) => {
162155
return async function findOneUserUseCaseHandler({ userId, email }) {
163156

164157
const newId = validateId(userId);
@@ -198,7 +191,7 @@ module.exports = {
198191
* @throws {RequiredParameterError} If the ID is not provided.
199192
* @throws {new Error} If the user is not found.
200193
*/
201-
updateUserUseCase: ({ dbUserHandler }) => async function updateUserUseCaseHandler({ userId, ...userData }) {
194+
updateUserUseCase: ({ dbUserHandler, makeUser, validateId, RequiredParameterError, logEvents, makeHttpError }) => async function updateUserUseCaseHandler({ userId, ...userData }) {
202195

203196
const newId = validateId(userId);
204197
try {
@@ -245,7 +238,7 @@ module.exports = {
245238
* @throws {RequiredParameterError} If the ID is not provided.
246239
* @throws {new Error} If the user is not found.
247240
*/
248-
deleteUserUseCase: ({ dbUserHandler }) => {
241+
deleteUserUseCase: ({ dbUserHandler, validateId, RequiredParameterError, logEvents }) => {
249242
return async function deleteUserUseCaseHandler({ userId }) {
250243

251244
const newId = validateId(userId);
@@ -286,8 +279,8 @@ module.exports = {
286279
* @throws {new Error} If the user is not found.
287280
* @throws {Error} If there is an error refreshing the token.
288281
*/
289-
refreshTokenUseCase: ({ dbUserHandler }) => {
290-
return async function refreshTokenUseCaseHandler({ refreshToken }) {
282+
refreshTokenUseCase: ({ dbUserHandler, RequiredParameterError, logEvents }) => {
283+
return async function refreshTokenUseCaseHandler({ refreshToken, jwt }) {
291284

292285
try {
293286
console.log(`refreshToken: ${refreshToken}`);
@@ -330,7 +323,7 @@ module.exports = {
330323
* @param {string} refreshToken - The refresh token to be used for logout.
331324
* @return {Object} An object containing the access token and refresh token.
332325
*/
333-
logoutUseCase: () => {
326+
logoutUseCase: ({ RequiredParameterError, logEvents }) => {
334327
return async function logoutUseCaseHandler({ refreshToken }) {
335328
try {
336329
if (!refreshToken) {
@@ -348,7 +341,7 @@ module.exports = {
348341
},
349342

350343
//block user
351-
blockUserUseCase: ({ dbUserHandler }) => {
344+
blockUserUseCase: ({ dbUserHandler, validateId, RequiredParameterError, logEvents }) => {
352345
return async function blockUserUseCaseHandler({ userId }) {
353346

354347
const newId = validateId(userId);
@@ -378,7 +371,7 @@ module.exports = {
378371
},
379372

380373
//un-block user
381-
unBlockUserUseCase: ({ dbUserHandler }) => {
374+
unBlockUserUseCase: ({ dbUserHandler, validateId, RequiredParameterError, logEvents }) => {
382375
return async function unBlockUserUseCaseHandler({ userId }) {
383376

384377
const newId = validateId(userId);
@@ -405,5 +398,93 @@ module.exports = {
405398
throw new Error("Error unblock user: " + error.stack);
406399
}
407400
}
401+
},
402+
403+
// forgot password user handler
404+
forgotPasswordUseCase: ({ dbUserHandler, logEvents }) => {
405+
return async function forgotPasswordUseCaseHandler({ email }) {
406+
407+
try {
408+
const user = await dbUserHandler.findUserByEmail({ email });
409+
if (!user) {
410+
throw new Error("user not found");
411+
}
412+
//genrate token and create an expiration link
413+
const token = require("crypto").randomBytes(64).toString("hex");
414+
const tokenExpiration = new Date(Date.now() + 10 * 60 * 1000); // 10 min from now
415+
416+
// update user in DB with the new token expiration time
417+
const updatedUser = await dbUserHandler.updateUser({ id: user.id, passwordResetToken: token, passwordResetExpires: tokenExpiration });
418+
419+
if (!updatedUser) {
420+
throw new Error("user not found");
421+
}
422+
423+
//send back usr and the email will be fired at the controller level
424+
return {
425+
...user,
426+
token,
427+
tokenExpiration
428+
}
429+
430+
} catch (error) {
431+
console.log("Error from forgot password use case handler: ", error);
432+
logEvents(
433+
`${error.no}:${error.code}\t${error.name}\t${error.message}`,
434+
"userHandlerErr.log"
435+
);
436+
throw new Error("Error forgot password: " + error.stack);
437+
}
438+
}
439+
},
440+
441+
442+
// reset password
443+
resetPasswordUseCase: ({ dbUserHandler, logEvents, makeHttpError }) => {
444+
return async function resetPasswordUseCaseHandler({ token, password }) {
445+
446+
try {
447+
const user = await dbUserHandler.findUserByToken({ token });
448+
if (!user) {
449+
return makeHttpError({
450+
statusCode: 404,
451+
errorMessage: "No user with this token"
452+
})
453+
}
454+
// check if token has expired
455+
if (new Date(user.passwordResetExpires).getTime() < Date.now()) {
456+
return makeHttpError({
457+
statusCode: 500,
458+
errorMessage: "Your password reset token has expired"
459+
})
460+
}
461+
// update user in DB with new password
462+
const updatedUser = await dbUserHandler.updateUser({
463+
id: user.id,
464+
password,
465+
passwordChangedAt: new Date().toISOString(),
466+
passwordResetToken: null,
467+
passwordResetExpires: null
468+
});
469+
470+
if (!updatedUser) {
471+
return makeHttpError({
472+
statusCode: 500,
473+
errorMessage: "Reset password failed"
474+
})
475+
}
476+
return updatedUser;
477+
} catch (error) {
478+
console.log("Error from reset password use case handler: ", error);
479+
logEvents(
480+
`${error.no}:${error.code}\t${error.name}\t${error.message}`,
481+
"userHandlerErr.log"
482+
);
483+
return makeHttpError({
484+
statusCode: 500,
485+
errorMessage: "Your password reset failed"
486+
})
487+
}
488+
}
408489
}
409490
}

enterprise-business-rules/entities/index.js

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
1-
const { makeUserModel } = require("./user-model");
1+
2+
// user and product validation
3+
const userValidationData = require("../validate-models/user-validation-functions");
24
const productValidation = require("../validate-models/product-validation-fcts")();
35

4-
const {
5-
makeProductModel,
6-
makeRatingProductModel
7-
} = require("./product-model");
6+
//log errors
7+
const { logEvents } = require("../../interface-adapters/middlewares/loggers/logger");
88

9+
// user and product models
10+
const { makeUserModel } = require("./user-model");
11+
const productModels = require("./product-model");
912

10-
const makeUser = makeUserModel({ productValidation });
11-
const makeProductModelHandler = makeProductModel({ productValidation });
12-
const makeProductRatingModelHandler = makeRatingProductModel({ productValidation });
13+
const makeUser = makeUserModel({ userValidationData, logEvents });
14+
const makeProductModelHandler = productModels.makeProductModel({ productValidation });
15+
const makeProductRatingModelHandler = productModels.makeRatingProductModel({ productValidation });
1316

1417

1518
module.exports = {

enterprise-business-rules/entities/user-model.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
1-
const { logEvents } = require("../../interface-adapters/middlewares/loggers/logger");
21

32
module.exports = {
43

5-
makeUserModel: ({ productValidation }) => {
4+
makeUserModel: ({ userValidationData, logEvents }) => {
65
return async function makeUser({ userData, update = false }) {
76

87
console.log("hit user model: ");
9-
const { normalise, validateUserData, validateUserDataUpdates } = productValidation;
8+
const {
9+
validateUserData,
10+
normalise,
11+
validateUserDataUpdates,
12+
} = userValidationData;
1013
let normalisedUserData = {}, validatedUserData = null;
1114
try {
1215
// for update user data we have to set "update = true" from the user handler

enterprise-business-rules/validate-models/user-validation-functions.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,11 @@ function validateId(id) {
142142
* - mobile: The result of validating the mobile number.
143143
* - password: The result of validating the password.
144144
*/
145-
async function validateUserData({ firstName, lastName, email, mobile, password, roles, active, address, whishlist }) {
145+
async function validateUserData(userData) {
146146
const errors = [];
147+
const { firstName, lastName, email, mobile, password, roles, active, address, whishlist, passwordChangedAt,
148+
passwordResetToken,
149+
passwordResetExpires } = userData;
147150

148151
if (!firstName && !lastName) errors.push('user must have a first name or last name.');
149152
if (!email) errors.push('user must have an email.');
@@ -167,7 +170,10 @@ async function validateUserData({ firstName, lastName, email, mobile, password,
167170
whishlist: roles === "admin" ? [] : Array.isArray(whishlist) ? whishlist : whishlist ? [whishlist] : [],
168171
cart: [],
169172
isBlocked: false,
170-
createdAt: { $currentDate: true }
173+
createdAt: new Date().toISOString(),
174+
passwordChangedAt,
175+
passwordResetToken,
176+
passwordResetExpires
171177
};
172178
}
173179

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
const nodemailer = require("nodemailer");
2+
3+
// const transporter = nodemailer.createTransport({
4+
// host: "smtp.ethereal.email",
5+
// port: 465,
6+
// secure: true, // Use `true` for port 465, `false` for all other ports
7+
// auth: {
8+
// user: process.env.MY_EMAIL,
9+
// pass: process.env.PASSWORD,
10+
// },
11+
// });
12+
13+
const transporter = nodemailer.createTransport({
14+
service: "gmail",
15+
host: "smtp.google.com",
16+
port: 465,
17+
secure: true,
18+
auth: {
19+
user: process.env.MY_EMAIL,
20+
pass: process.env.PASSWORD,
21+
},
22+
});
23+
24+
25+
// async..await is not allowed in global scope, must use a wrapper
26+
module.exports = async function sendEmail({ userEmail, resetPasswordLink }) {
27+
28+
console.log("hit the email sender")
29+
return await transporter.sendMail({
30+
from: '"maebrie-commerce" <maebrice@ethereal.email>',
31+
to: userEmail,
32+
subject: "FORGOT PASSWORD",
33+
text: `Hello! kindly click on the following email in order to reset your password ${resetPasswordLink}`, // plain text body
34+
35+
}).then((emaildata) => {
36+
console.log("Email sent: ", emaildata);
37+
}).catch((error) => {
38+
console.error(error);
39+
});
40+
}

interface-adapters/adapter/request-response-adapter.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ module.exports = (controller) => function responseAdapterHandler(req, res) {
1313
}
1414
};
1515

16-
console.log("passed the request response handler check")
17-
1816
controller(httpRequest)
1917
.then(httpResponse => {
2018
console.log("response adapter: ", httpResponse)

0 commit comments

Comments
 (0)