From bef4fde453fbabb7abc41a34f4255a0801013cc9 Mon Sep 17 00:00:00 2001 From: Tomas Van Morlegan Date: Wed, 14 Apr 2021 20:53:21 -0300 Subject: [PATCH 1/6] fix field name of reset password token, add verifier and set status --- api/controllers/user.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/api/controllers/user.js b/api/controllers/user.js index b8647b62..b6b22ba1 100644 --- a/api/controllers/user.js +++ b/api/controllers/user.js @@ -419,7 +419,7 @@ async function forgotPassword(req, res) { const response = { success: 1, userid: user.id, - url: token, + //url: token, message: 'Success! Check your mail to reset your password.' }; return res.status(200).json(response); @@ -436,7 +436,6 @@ async function forgotPassword(req, res) { } async function storePassword(req, res) { const { userid, password, token } = req.body; - try { const resetPassword = await ResetPassword.findOne({ userId: userid, @@ -448,10 +447,16 @@ async function storePassword(req, res) { error: err.message }); } - // the token and the hashed token in the db are verified befor updating the password - bcrypt.compare(token, resetPassword.token, function(errBcrypt, resBcrypt) { - let expireTime = moment.utc(resetPassword.expire); + // the token and the hashed token in the db are verified before updating the password + bcrypt.compare(token, resetPassword.resetPasswordToken, function(errBcrypt, resBcrypt) { + let expireTime = moment.utc(resetPassword.expire); // expireTime and currentTime is never used let currentTime = new Date(); + if(!resBcrypt){ + return res.status(500).json({ + message: 'Error resetting user password.', + error: 'invalid Token' + }); + } //hashing the password to store in the db node.js bcrypt.genSalt(8, function(err, salt) { bcrypt.hash(password, salt, async function(err, hash) { @@ -464,8 +469,8 @@ async function storePassword(req, res) { message: 'No user found with that ID.' }); } - ResetPassword.findOneAndUpdate( - { id: resetPassword.id }, + ResetPassword.findByIdAndUpdate( + resetPassword._id, { status: true }, function(err) { if (err) { From c83815f46d5515d8b0711aa18ce1c256a1cfc520 Mon Sep 17 00:00:00 2001 From: Tomas Van Morlegan Date: Fri, 16 Apr 2021 11:09:26 -0300 Subject: [PATCH 2/6] delete Private info on res of /forgot and run prettier --- api/controllers/user.js | 260 +++++++++++++++++++++------------------- 1 file changed, 135 insertions(+), 125 deletions(-) diff --git a/api/controllers/user.js b/api/controllers/user.js index b6b22ba1..1e072a15 100644 --- a/api/controllers/user.js +++ b/api/controllers/user.js @@ -24,12 +24,12 @@ module.exports = { facebookLogin: facebookLogin, googleLogin: googleLogin, forgotPassword: forgotPassword, - storePassword: storePassword + storePassword: storePassword, }; const USER_MODEL_ID_TYPE = { facebook: 'facebook.id', - google: 'google.id' + google: 'google.id', }; async function getSettings(user) { @@ -45,49 +45,54 @@ async function getSettings(user) { function createUser(req, res) { const user = new User(req.body); - nev.createTempUser(user, function(err, existingPersistentUser, newTempUser) { + nev.createTempUser(user, function (err, existingPersistentUser, newTempUser) { if (err) { return res.status(404).json({ - message: err + message: err, }); } // user already exists in persistent collection if (existingPersistentUser) { return res.status(409).json({ message: - 'You have already signed up and confirmed your account. Did you forget your password?' + 'You have already signed up and confirmed your account. Did you forget your password?', }); } // new user created if (newTempUser) { const URL = newTempUser[nev.options.URLFieldName]; - + let domain = req.headers.origin; //if origin is private insert default hostname - if (!domain){ - domain = 'https://app.cboard.io' + if (!domain) { + domain = 'https://app.cboard.io'; } - nev.sendVerificationEmail(newTempUser.email, domain, URL, function(err, info) { - if (err) { - return res.status(500).json({ - message: 'ERROR: sending verification email FAILED ' + info - }); + nev.sendVerificationEmail( + newTempUser.email, + domain, + URL, + function (err, info) { + if (err) { + return res.status(500).json({ + message: 'ERROR: sending verification email FAILED ' + info, + }); + } } - }); + ); return res.status(200).json({ success: 1, url: URL, message: - 'An email has been sent to you. Please check it to verify your account.' + 'An email has been sent to you. Please check it to verify your account.', }); // user already exists in temporary collection! } else { return res.status(409).json({ message: - 'You have already signed up. Please check your email to verify your account.' + 'You have already signed up. Please check your email to verify your account.', }); } }); @@ -109,7 +114,7 @@ async function passportLogin(type, accessToken, refreshToken, profile, done) { const { _id: userId, email } = user; const tokenString = auth.issueToken({ id: userId, - email + email, }); const settings = await getSettings(user); @@ -117,7 +122,7 @@ async function passportLogin(type, accessToken, refreshToken, profile, done) { const response = { ...user.toJSON(), settings, - authToken: tokenString + authToken: tokenString, }; done(null, response); @@ -139,16 +144,16 @@ async function createOrUpdateUser(accessToken, profile, type = 'facebook') { const fnMap = { facebook: { create: 'createUserFromFacebook', - update: 'updateUserFromFacebook' + update: 'updateUserFromFacebook', }, google: { create: 'createUserFromGoogle', - update: 'updateUserFromGoogle' - } + update: 'updateUserFromGoogle', + }, }; const mergedProfile = { ...profile, accessToken }; - const emails = profile.emails.map(email => email.value); + const emails = profile.emails.map((email) => email.value); const existingUser = await User.findOne({ email: { $in: emails } }).exec(); const userModelFn = existingUser ? fnMap[type].update : fnMap[type].create; @@ -159,24 +164,26 @@ async function createOrUpdateUser(accessToken, profile, type = 'facebook') { function activateUser(req, res) { const url = req.swagger.params.url.value; - nev.confirmTempUser(url, function(err, user) { + nev.confirmTempUser(url, function (err, user) { if (user) { - nev.sendConfirmationEmail(user.email, function(err, info) { + nev.sendConfirmationEmail(user.email, function (err, info) { if (err) { return res.status(404).json({ - message: 'ERROR: sending confirmation email FAILED ' + info + message: 'ERROR: sending confirmation email FAILED ' + info, }); } return res.status(200).json({ success: 1, userid: user._id, - message: 'CONFIRMED!' + message: 'CONFIRMED!', }); }); } else { return res.status(404).json({ - message: 'ERROR: confirming your temporary user FAILED, please try to login again', - error: 'ERROR: confirming your temporary user FAILED, please try to login again' + message: + 'ERROR: confirming your temporary user FAILED, please try to login again', + error: + 'ERROR: confirming your temporary user FAILED, please try to login again', }); } }); @@ -192,7 +199,7 @@ async function listUser(req, res) { User, { query, - populate: ['communicators', 'boards'] + populate: ['communicators', 'boards'], }, req.query ); @@ -202,10 +209,10 @@ async function listUser(req, res) { function removeUser(req, res) { const id = req.swagger.params.id.value; - User.findByIdAndRemove(id, function(err, users) { + User.findByIdAndRemove(id, function (err, users) { if (err) { return res.status(404).json({ - message: 'User not found. User Id: ' + id + message: 'User not found. User Id: ' + id, }); } return res.status(200).json(users); @@ -223,54 +230,49 @@ async function getUser(req, res) { if (!user) { return res.status(404).json({ - message: `User does not exist. User Id: ${id}` + message: `User does not exist. User Id: ${id}`, }); } const settings = await getSettings(user); const response = { ...user.toJSON(), - settings + settings, }; return res.status(200).json(response); } catch (err) { return res.status(500).json({ message: 'Error getting user.', - error: err.message + error: err.message, }); } } -const UPDATEABLE_FIELDS = [ - 'email', - 'name', - 'birthdate', - 'locale', -] +const UPDATEABLE_FIELDS = ['email', 'name', 'birthdate', 'locale']; function updateUser(req, res) { const id = req.swagger.params.id.value; if (!req.user.isAdmin && req.auth.id !== id) { return res.status(403).json({ - message: 'Only admins can update another user.' - }) + message: 'Only admins can update another user.', + }); } User.findById(id) .populate('communicators') .populate('boards') - .exec(function(err, user) { + .exec(function (err, user) { if (err) { return res.status(500).json({ message: 'Error updating user. ', - error: err.message + error: err.message, }); } if (!user) { return res.status(404).json({ - message: 'Unable to find user. User Id: ' + id + message: 'Unable to find user. User Id: ' + id, }); } for (let key in req.body) { @@ -278,16 +280,16 @@ function updateUser(req, res) { user[key] = req.body[key]; } } - user.save(function(err, user) { + user.save(function (err, user) { if (err) { return res.status(500).json({ message: 'Error saving user. ', - error: err.message + error: err.message, }); } if (!user) { return res.status(404).json({ - message: 'Unable to find user. User id: ' + id + message: 'Unable to find user. User id: ' + id, }); } }); @@ -301,7 +303,7 @@ function loginUser(req, res) { User.authenticate(email, password, async (error, user) => { if (error || !user) { return res.status(401).json({ - message: 'Wrong email or password.' + message: 'Wrong email or password.', }); } else { const userId = user._id; @@ -309,7 +311,7 @@ function loginUser(req, res) { const tokenString = auth.issueToken({ email, - id: userId + id: userId, }); const settings = await getSettings(user); @@ -318,7 +320,7 @@ function loginUser(req, res) { ...user.toJSON(), settings, birthdate: moment(user.birthdate).format('YYYY-MM-DD'), - authToken: tokenString + authToken: tokenString, }; return res.status(200).json(response); @@ -329,18 +331,18 @@ function loginUser(req, res) { function logoutUser(req, res) { if (req.session) { // delete session object - req.session.destroy(err => { + req.session.destroy((err) => { if (err) { return res.status(500).json({ message: 'Error removing session .', - error: err.message + error: err.message, }); } }); } return res.status(200).json({ - message: 'User successfully logout' + message: 'User successfully logout', }); } @@ -363,20 +365,20 @@ async function forgotPassword(req, res) { const user = await User.findOne({ email: { $in: email } }).exec(); if (!user) { return res.status(404).json({ - message: 'No user found with that email address. Check your input.' + message: 'No user found with that email address. Check your input.', }); } const resetPassword = await ResetPassword.findOne({ userId: user.id, - status: false + status: false, }).exec(); if (resetPassword) { //remove entry if exist - await ResetPassword.deleteOne({ _id: resetPassword.id }, function(err) { + await ResetPassword.deleteOne({ _id: resetPassword.id }, function (err) { if (err) { return res.status(500).json({ message: 'ERROR: delete reset password FAILED ', - error: err.message + error: err.message, }); } }).exec(); @@ -384,19 +386,19 @@ async function forgotPassword(req, res) { //creating the token to be sent to the forgot password form token = crypto.randomBytes(32).toString('hex'); //hashing the password to store in the db node.js - bcrypt.genSalt(8, function(err, salt) { - bcrypt.hash(token, salt, function(err, hash) { + bcrypt.genSalt(8, function (err, salt) { + bcrypt.hash(token, salt, function (err, hash) { const item = new ResetPassword({ userId: user.id, resetPasswordToken: hash, resetPasswordExpires: moment.utc().add(86400, 'seconds'), - status: false + status: false, }); - item.save(function(err, rstPassword) { + item.save(function (err, rstPassword) { if (err) { return res.status(500).json({ message: 'ERROR: create reset password FAILED ', - error: err.message + error: err.message, }); } }); @@ -405,32 +407,36 @@ async function forgotPassword(req, res) { let domain = req.headers.origin; //if origin is private insert default hostname - if (!domain){ - domain = 'https://app.cboard.io' + if (!domain) { + domain = 'https://app.cboard.io'; } - nev.sendResetPasswordEmail(user.email, domain, user.id, token, function(err) { - if (err) { - return res.status(500).json({ - message: 'ERROR: sending reset your password email FAILED ', - error: err.message - }); - } else { - const response = { - success: 1, - userid: user.id, - //url: token, - message: 'Success! Check your mail to reset your password.' - }; - return res.status(200).json(response); + nev.sendResetPasswordEmail( + user.email, + domain, + user.id, + token, + function (err) { + if (err) { + return res.status(500).json({ + message: 'ERROR: sending reset your password email FAILED ', + error: err.message, + }); + } else { + const response = { + success: 1, + message: 'Success! Check your mail to reset your password.', + }; + return res.status(200).json(response); + } } - }); + ); }); }); } catch (err) { return res.status(500).json({ message: 'Error resetting user password.', - error: err.message + error: err.message, }); } } @@ -439,62 +445,66 @@ async function storePassword(req, res) { try { const resetPassword = await ResetPassword.findOne({ userId: userid, - status: false + status: false, }).exec(); if (!resetPassword) { return res.status(500).json({ message: 'Expired time to reset password! ', - error: err.message + error: err.message, }); } // the token and the hashed token in the db are verified before updating the password - bcrypt.compare(token, resetPassword.resetPasswordToken, function(errBcrypt, resBcrypt) { - let expireTime = moment.utc(resetPassword.expire); // expireTime and currentTime is never used - let currentTime = new Date(); - if(!resBcrypt){ - return res.status(500).json({ - message: 'Error resetting user password.', - error: 'invalid Token' - }); - } - //hashing the password to store in the db node.js - bcrypt.genSalt(8, function(err, salt) { - bcrypt.hash(password, salt, async function(err, hash) { - const user = await User.findOneAndUpdate( - { _id: userid }, - { password: hash } - ); - if (!user) { - return res.status(404).json({ - message: 'No user found with that ID.' - }); - } - ResetPassword.findByIdAndUpdate( - resetPassword._id, - { status: true }, - function(err) { - if (err) { - return res.status(500).json({ - message: 'ERROR: reset your password email FAILED ', - error: err.message - }); - } else { - const response = { - success: 1, - url: token, - message: 'Success! We have reset your password.' - }; - return res.status(200).json(response); - } + bcrypt.compare( + token, + resetPassword.resetPasswordToken, + function (errBcrypt, resBcrypt) { + let expireTime = moment.utc(resetPassword.expire); // expireTime and currentTime is never used + let currentTime = new Date(); + if (!resBcrypt) { + return res.status(500).json({ + message: 'Error resetting user password.', + error: 'invalid Token', + }); + } + //hashing the password to store in the db node.js + bcrypt.genSalt(8, function (err, salt) { + bcrypt.hash(password, salt, async function (err, hash) { + const user = await User.findOneAndUpdate( + { _id: userid }, + { password: hash } + ); + if (!user) { + return res.status(404).json({ + message: 'No user found with that ID.', + }); } - ); + ResetPassword.findByIdAndUpdate( + resetPassword._id, + { status: true }, + function (err) { + if (err) { + return res.status(500).json({ + message: 'ERROR: reset your password email FAILED ', + error: err.message, + }); + } else { + const response = { + success: 1, + url: token, + message: 'Success! We have reset your password.', + }; + return res.status(200).json(response); + } + } + ); + }); }); - }); - }); + } + ); } catch (err) { return res.status(500).json({ message: 'Error resetting user password.', - error: err.message + error: err.message, }); } } From e71200c186d5ef6c07868c179e9f92b8834e9618 Mon Sep 17 00:00:00 2001 From: Tomas Van Morlegan Date: Fri, 16 Apr 2021 12:20:40 -0300 Subject: [PATCH 3/6] add expire time verification --- api/controllers/user.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/api/controllers/user.js b/api/controllers/user.js index 1e072a15..018c69f9 100644 --- a/api/controllers/user.js +++ b/api/controllers/user.js @@ -447,10 +447,11 @@ async function storePassword(req, res) { userId: userid, status: false, }).exec(); - if (!resetPassword) { + const expireTime = moment.utc(resetPassword.resetPasswordExpires).isBefore(moment()); + if (expireTime) { return res.status(500).json({ message: 'Expired time to reset password! ', - error: err.message, + error: 'Expired time to reset password! ', }); } // the token and the hashed token in the db are verified before updating the password @@ -458,8 +459,6 @@ async function storePassword(req, res) { token, resetPassword.resetPasswordToken, function (errBcrypt, resBcrypt) { - let expireTime = moment.utc(resetPassword.expire); // expireTime and currentTime is never used - let currentTime = new Date(); if (!resBcrypt) { return res.status(500).json({ message: 'Error resetting user password.', From 303212e0dd73dc9ef9c28c110d9f32a046536c07 Mon Sep 17 00:00:00 2001 From: Tomas Van Morlegan Date: Fri, 29 Oct 2021 22:40:26 -0300 Subject: [PATCH 4/6] allow users reset password more than once --- api/controllers/user.js | 238 ++++++++++++++++++++-------------------- 1 file changed, 116 insertions(+), 122 deletions(-) diff --git a/api/controllers/user.js b/api/controllers/user.js index 32671367..6b7dfa48 100644 --- a/api/controllers/user.js +++ b/api/controllers/user.js @@ -24,12 +24,12 @@ module.exports = { facebookLogin: facebookLogin, googleLogin: googleLogin, forgotPassword: forgotPassword, - storePassword: storePassword, + storePassword: storePassword }; const USER_MODEL_ID_TYPE = { facebook: 'facebook.id', - google: 'google.id', + google: 'google.id' }; async function getSettings(user) { @@ -45,17 +45,17 @@ async function getSettings(user) { function createUser(req, res) { const user = new User(req.body); - nev.createTempUser(user, function (err, existingPersistentUser, newTempUser) { + nev.createTempUser(user, function(err, existingPersistentUser, newTempUser) { if (err) { return res.status(404).json({ - message: err, + message: err }); } // user already exists in persistent collection if (existingPersistentUser) { return res.status(409).json({ message: - 'You have already signed up and confirmed your account. Did you forget your password?', + 'You have already signed up and confirmed your account. Did you forget your password?' }); } // new user created @@ -68,31 +68,29 @@ function createUser(req, res) { domain = 'https://app.cboard.io'; } - nev.sendVerificationEmail( - newTempUser.email, - domain, - URL, - function (err, info) { - if (err) { - return res.status(500).json({ - message: 'ERROR: sending verification email FAILED ' + info, - }); - } + nev.sendVerificationEmail(newTempUser.email, domain, URL, function( + err, + info + ) { + if (err) { + return res.status(500).json({ + message: 'ERROR: sending verification email FAILED ' + info + }); } - ); + }); return res.status(200).json({ success: 1, url: URL, message: - 'An email has been sent to you. Please check it to verify your account.', + 'An email has been sent to you. Please check it to verify your account.' }); // user already exists in temporary collection! } else { return res.status(409).json({ message: - 'You have already signed up. Please check your email to verify your account.', + 'You have already signed up. Please check your email to verify your account.' }); } }); @@ -114,7 +112,7 @@ async function passportLogin(type, accessToken, refreshToken, profile, done) { const { _id: userId, email } = user; const tokenString = auth.issueToken({ id: userId, - email, + email }); const settings = await getSettings(user); @@ -122,7 +120,7 @@ async function passportLogin(type, accessToken, refreshToken, profile, done) { const response = { ...user.toJSON(), settings, - authToken: tokenString, + authToken: tokenString }; done(null, response); @@ -144,16 +142,16 @@ async function createOrUpdateUser(accessToken, profile, type = 'facebook') { const fnMap = { facebook: { create: 'createUserFromFacebook', - update: 'updateUserFromFacebook', + update: 'updateUserFromFacebook' }, google: { create: 'createUserFromGoogle', - update: 'updateUserFromGoogle', - }, + update: 'updateUserFromGoogle' + } }; const mergedProfile = { ...profile, accessToken }; - const emails = profile.emails.map((email) => email.value); + const emails = profile.emails.map(email => email.value); const existingUser = await User.findOne({ email: { $in: emails } }).exec(); const userModelFn = existingUser ? fnMap[type].update : fnMap[type].create; @@ -164,18 +162,18 @@ async function createOrUpdateUser(accessToken, profile, type = 'facebook') { function activateUser(req, res) { const url = req.swagger.params.url.value; - nev.confirmTempUser(url, function (err, user) { + nev.confirmTempUser(url, function(err, user) { if (user) { - nev.sendConfirmationEmail(user.email, function (err, info) { + nev.sendConfirmationEmail(user.email, function(err, info) { if (err) { return res.status(404).json({ - message: 'ERROR: sending confirmation email FAILED ' + info, + message: 'ERROR: sending confirmation email FAILED ' + info }); } return res.status(200).json({ success: 1, userid: user._id, - message: 'CONFIRMED!', + message: 'CONFIRMED!' }); }); } else { @@ -183,7 +181,7 @@ function activateUser(req, res) { message: 'ERROR: confirming your temporary user FAILED, please try to login again', error: - 'ERROR: confirming your temporary user FAILED, please try to login again', + 'ERROR: confirming your temporary user FAILED, please try to login again' }); } }); @@ -199,7 +197,7 @@ async function listUser(req, res) { User, { query, - populate: ['communicators', 'boards'], + populate: ['communicators', 'boards'] }, req.query ); @@ -209,10 +207,10 @@ async function listUser(req, res) { function removeUser(req, res) { const id = req.swagger.params.id.value; - User.findByIdAndRemove(id, function (err, users) { + User.findByIdAndRemove(id, function(err, users) { if (err) { return res.status(404).json({ - message: 'User not found. User Id: ' + id, + message: 'User not found. User Id: ' + id }); } return res.status(200).json(users); @@ -230,21 +228,21 @@ async function getUser(req, res) { if (!user) { return res.status(404).json({ - message: `User does not exist. User Id: ${id}`, + message: `User does not exist. User Id: ${id}` }); } const settings = await getSettings(user); const response = { ...user.toJSON(), - settings, + settings }; return res.status(200).json(response); } catch (err) { return res.status(500).json({ message: 'Error getting user.', - error: err.message, + error: err.message }); } } @@ -257,22 +255,22 @@ function updateUser(req, res) { if (!req.user.isAdmin && req.auth.id !== id) { return res.status(403).json({ message: 'You are not authorized to update this user.' - }) + }); } User.findById(id) .populate('communicators') .populate('boards') - .exec(function (err, user) { + .exec(function(err, user) { if (err) { return res.status(500).json({ message: 'Error updating user. ', - error: err.message, + error: err.message }); } if (!user) { return res.status(404).json({ - message: 'Unable to find user. User Id: ' + id, + message: 'Unable to find user. User Id: ' + id }); } for (let key in req.body) { @@ -280,16 +278,16 @@ function updateUser(req, res) { user[key] = req.body[key]; } } - user.save(function (err, user) { + user.save(function(err, user) { if (err) { return res.status(500).json({ message: 'Error saving user. ', - error: err.message, + error: err.message }); } if (!user) { return res.status(404).json({ - message: 'Unable to find user. User id: ' + id, + message: 'Unable to find user. User id: ' + id }); } }); @@ -303,7 +301,7 @@ function loginUser(req, res) { User.authenticate(email, password, async (error, user) => { if (error || !user) { return res.status(401).json({ - message: 'Wrong email or password.', + message: 'Wrong email or password.' }); } else { const userId = user._id; @@ -311,7 +309,7 @@ function loginUser(req, res) { const tokenString = auth.issueToken({ email, - id: userId, + id: userId }); const settings = await getSettings(user); @@ -320,7 +318,7 @@ function loginUser(req, res) { ...user.toJSON(), settings, birthdate: moment(user.birthdate).format('YYYY-MM-DD'), - authToken: tokenString, + authToken: tokenString }; return res.status(200).json(response); @@ -331,18 +329,18 @@ function loginUser(req, res) { function logoutUser(req, res) { if (req.session) { // delete session object - req.session.destroy((err) => { + req.session.destroy(err => { if (err) { return res.status(500).json({ message: 'Error removing session .', - error: err.message, + error: err.message }); } }); } return res.status(200).json({ - message: 'User successfully logout', + message: 'User successfully logout' }); } @@ -365,20 +363,20 @@ async function forgotPassword(req, res) { const user = await User.findOne({ email: { $in: email } }).exec(); if (!user) { return res.status(404).json({ - message: 'No user found with that email address. Check your input.', + message: 'No user found with that email address. Check your input.' }); } const resetPassword = await ResetPassword.findOne({ userId: user.id, - status: false, + status: false }).exec(); if (resetPassword) { //remove entry if exist - await ResetPassword.deleteOne({ _id: resetPassword.id }, function (err) { + await ResetPassword.deleteOne({ _id: resetPassword.id }, function(err) { if (err) { return res.status(500).json({ message: 'ERROR: delete reset password FAILED ', - error: err.message, + error: err.message }); } }).exec(); @@ -386,19 +384,19 @@ async function forgotPassword(req, res) { //creating the token to be sent to the forgot password form token = crypto.randomBytes(32).toString('hex'); //hashing the password to store in the db node.js - bcrypt.genSalt(8, function (err, salt) { - bcrypt.hash(token, salt, function (err, hash) { + bcrypt.genSalt(8, function(err, salt) { + bcrypt.hash(token, salt, function(err, hash) { const item = new ResetPassword({ userId: user.id, resetPasswordToken: hash, resetPasswordExpires: moment.utc().add(86400, 'seconds'), - status: false, + status: false }); - item.save(function (err, rstPassword) { + item.save(function(err, rstPassword) { if (err) { return res.status(500).json({ message: 'ERROR: create reset password FAILED ', - error: err.message, + error: err.message }); } }); @@ -411,32 +409,28 @@ async function forgotPassword(req, res) { domain = 'https://app.cboard.io'; } - nev.sendResetPasswordEmail( - user.email, - domain, - user.id, - token, - function (err) { - if (err) { - return res.status(500).json({ - message: 'ERROR: sending reset your password email FAILED ', - error: err.message, - }); - } else { - const response = { - success: 1, - message: 'Success! Check your mail to reset your password.', - }; - return res.status(200).json(response); - } + nev.sendResetPasswordEmail(user.email, domain, user.id, token, function( + err + ) { + if (err) { + return res.status(500).json({ + message: 'ERROR: sending reset your password email FAILED ', + error: err.message + }); + } else { + const response = { + success: 1, + message: 'Success! Check your mail to reset your password.' + }; + return res.status(200).json(response); } - ); + }); }); }); } catch (err) { return res.status(500).json({ message: 'Error resetting user password.', - error: err.message, + error: err.message }); } } @@ -445,65 +439,65 @@ async function storePassword(req, res) { try { const resetPassword = await ResetPassword.findOne({ userId: userid, - status: false, + status: false }).exec(); - const expireTime = moment.utc(resetPassword.resetPasswordExpires).isBefore(moment()); + const expireTime = moment + .utc(resetPassword.resetPasswordExpires) + .isBefore(moment()); if (expireTime) { return res.status(500).json({ message: 'Expired time to reset password! ', - error: 'Expired time to reset password! ', + error: 'Expired time to reset password! ' }); } // the token and the hashed token in the db are verified before updating the password - bcrypt.compare( - token, - resetPassword.resetPasswordToken, - function (errBcrypt, resBcrypt) { - if (!resBcrypt) { - return res.status(500).json({ - message: 'Error resetting user password.', - error: 'invalid Token', - }); - } - //hashing the password to store in the db node.js - bcrypt.genSalt(8, function (err, salt) { - bcrypt.hash(password, salt, async function (err, hash) { - const user = await User.findOneAndUpdate( - { _id: userid }, - { password: hash } - ); - if (!user) { - return res.status(404).json({ - message: 'No user found with that ID.', + bcrypt.compare(token, resetPassword.resetPasswordToken, function( + errBcrypt, + resBcrypt + ) { + if (!resBcrypt) { + return res.status(500).json({ + message: 'Error resetting user password.', + error: 'invalid Token' + }); + } + //hashing the password to store in the db node.js + bcrypt.genSalt(8, function(err, salt) { + bcrypt.hash(password, salt, async function(err, hash) { + const user = await User.findOneAndUpdate( + { _id: userid }, + { password: hash } + ); + if (!user) { + return res.status(404).json({ + message: 'No user found with that ID.' + }); + } + ResetPassword.findByIdAndDelete(resetPassword._id, function( + err, + docs + ) { + if (err) { + return res.status(500).json({ + message: 'ERROR: reset your password email FAILED ', + error: err.message }); + } else { + const response = { + success: 1, + url: token, + message: 'Success! We have reset your password.' + }; + return res.status(200).json(response); } - ResetPassword.findByIdAndUpdate( - resetPassword._id, - { status: true }, - function (err) { - if (err) { - return res.status(500).json({ - message: 'ERROR: reset your password email FAILED ', - error: err.message, - }); - } else { - const response = { - success: 1, - url: token, - message: 'Success! We have reset your password.', - }; - return res.status(200).json(response); - } - } - ); }); }); - } - ); + }); + }); } catch (err) { return res.status(500).json({ message: 'Error resetting user password.', - error: err.message, + error: err.message }); } } From fa93b3ebd18f147c89b549d255b270a2c9cd9b6d Mon Sep 17 00:00:00 2001 From: Tomas Van Morlegan Date: Fri, 29 Oct 2021 22:55:14 -0300 Subject: [PATCH 5/6] fix test --- test/controllers/user.js | 107 ++++++++++++++++++++------------------- 1 file changed, 54 insertions(+), 53 deletions(-) diff --git a/test/controllers/user.js b/test/controllers/user.js index 088619cc..d1fff749 100644 --- a/test/controllers/user.js +++ b/test/controllers/user.js @@ -11,36 +11,39 @@ const helper = require('../helper'); const User = require('../../api/models/User'); //Parent block -describe('User API calls', function () { +describe('User API calls', function() { let server; - before(async function () { + before(async function() { helper.prepareNodemailerMock(); //enable mockery and replace nodemailer with nodemailerMock server = require('../../app'); //register mocks before require the original dependency }); - after(async function () { + after(async function() { helper.prepareNodemailerMock(true); //disable mockery await helper.deleteMochaUsers(); await User.deleteMany({ name: 'testAlice' }); }); - describe('POST /user create User', function () { + describe('POST /user create User', function() { let url; - it('it should to create a new temporary user', async function () { + it('it should to create a new temporary user', async function() { const data = { ...helper.userData, - email: helper.generateEmail(), + email: helper.generateEmail() }; - const res = await request(server).post('/user').send(data).expect(200); + const res = await request(server) + .post('/user') + .send(data) + .expect(200); const URLLenght = 16; url = res.body.url; url.should.be.a('string').with.lengthOf(URLLenght); //nev.options.URLLenght }); - it('it should to activate user', async function () { + it('it should to activate user', async function() { const res = await request(server) .post(`/user/activate/${url}`) .expect('Content-Type', /json/) @@ -52,16 +55,16 @@ describe('User API calls', function () { }); }); - describe('POST /user/login', function () { - it('it should NOT Returns a valid token for a wrong email or password', async function () { + describe('POST /user/login', function() { + it('it should NOT Returns a valid token for a wrong email or password', async function() { await helper.prepareUser(server, { role: 'user', - email: helper.generateEmail(), + email: helper.generateEmail() }); const wrongUserData = { ...helper.userData, - password: 'wrongPassword', + password: 'wrongPassword' }; const res = await request(server) @@ -75,15 +78,15 @@ describe('User API calls', function () { res.body.message.should.be.string; }); - it('it should Returns a valid token for a user', async function () { + it('it should Returns a valid token for a user', async function() { const userEmail = helper.generateEmail(); await helper.prepareUser(server, { role: 'user', - email: userEmail, + email: userEmail }); const userData = { ...helper.userData, - email: userEmail, + email: userEmail }; const res = await request(server) @@ -98,8 +101,8 @@ describe('User API calls', function () { }); }); - describe('GET /user', function () { - it('it should NOT Get the full users list without Bearer Token', async function () { + describe('GET /user', function() { + it('it should NOT Get the full users list without Bearer Token', async function() { await request(server) .get('/user') .set('Accept', 'application/json') @@ -107,10 +110,10 @@ describe('User API calls', function () { .expect(403); }); - it('it should Get the full users list', async function () { + it('it should Get the full users list', async function() { const user = await helper.prepareUser(server, { role: 'user', - email: helper.generateEmail(), + email: helper.generateEmail() }); const res = await request(server) .get('/user') @@ -123,11 +126,11 @@ describe('User API calls', function () { }); }); - describe('GET /user/:userId', function () { - it('it should Get a specific user', async function () { + describe('GET /user/:userId', function() { + it('it should Get a specific user', async function() { const user = await helper.prepareUser(server, { role: 'user', - email: helper.generateEmail(), + email: helper.generateEmail() }); const res = await request(server) @@ -142,16 +145,16 @@ describe('User API calls', function () { }); }); - describe('PUT /user/:userId', function () { - it('only allows an admin user to update another user', async function () { + describe('PUT /user/:userId', function() { + it('only allows an admin user to update another user', async function() { const admin = await helper.prepareUser(server, { role: 'admin', - email: helper.generateEmail(), + email: helper.generateEmail() }); const user = await helper.prepareUser(server, { role: 'user', - email: helper.generateEmail(), + email: helper.generateEmail() }); // Try to update another user as a regular user. @@ -160,7 +163,7 @@ describe('User API calls', function () { .put(`/user/${admin.userId}`) .set('Authorization', `Bearer ${user.token}`) .expect({ - message: 'You are not authorized to update this user.', + message: 'You are not authorized to update this user.' }) .expect(403); @@ -172,10 +175,10 @@ describe('User API calls', function () { .expect(200); }); - it('only allows updating a subset of fields', async function () { + it('only allows updating a subset of fields', async function() { const user = await helper.prepareUser(server, { role: 'user', - email: helper.generateEmail(), + email: helper.generateEmail() }); const update = { @@ -187,7 +190,7 @@ describe('User API calls', function () { // Not updateable. role: 'foobar', - password: uuid.v4(), + password: uuid.v4() }; const res = await request(server) @@ -206,11 +209,11 @@ describe('User API calls', function () { }); }); - describe('POST /user/logout', function () { - it('it should Destroys user session and authentication token', async function () { + describe('POST /user/logout', function() { + it('it should Destroys user session and authentication token', async function() { const user = await helper.prepareUser(server, { role: 'user', - email: helper.generateEmail(), + email: helper.generateEmail() }); await request(server) @@ -223,8 +226,8 @@ describe('User API calls', function () { }); }); - describe('POST /user/forgot', function () { - it('it should NOT create a Clear token to restore password for a wrong email', async function () { + describe('POST /user/forgot', function() { + it('it should NOT create a Clear token to restore password for a wrong email', async function() { let wrongUserEmail = 'wrong_email@wrong.com'; const res = await request(server) .post('/user/forgot') @@ -235,11 +238,11 @@ describe('User API calls', function () { res.body.message.should.be.a('string'); }); - it('it should create a Clear token to restore password', async function () { + it('it should create a Clear token to restore password', async function() { const userEmail = helper.generateEmail(); await helper.prepareUser(server, { role: 'user', - email: userEmail, + email: userEmail }); const res = await request(server) .post('/user/forgot') @@ -249,18 +252,16 @@ describe('User API calls', function () { .expect(200); const userAndUrl = res.body; - userAndUrl.should.be - .a('object') - .with.all.keys('success', 'userid', 'url', 'message'); + userAndUrl.should.be.a('object').with.all.keys('success', 'message'); }); }); - describe.skip('POST /user/store-password', function () { - it('it should NOT allows to store a new password posting /user/forgot and sending data without a verification url.', async function () { + describe.skip('POST /user/store-password', function() { + it('it should NOT allows to store a new password posting /user/forgot and sending data without a verification url.', async function() { const userEmail = helper.generateEmail(); const user = await helper.prepareUser(server, { role: 'user', - email: userEmail, + email: userEmail }); await request(server) .post('/user/logout') @@ -280,7 +281,7 @@ describe('User API calls', function () { const userStorePassword = { userid: userid, password: 'newPassword', - token: '', + token: '' }; await request(server) @@ -291,10 +292,10 @@ describe('User API calls', function () { .expect(500); }); - it('it should NOT allows to store a new password without a verification url.', async function () { + it('it should NOT allows to store a new password without a verification url.', async function() { const user = await helper.prepareUser(server, { role: 'user', - email: helper.generateEmail(), + email: helper.generateEmail() }); const userStorePassword = { ...user, password: 'newPassword' }; @@ -308,11 +309,11 @@ describe('User API calls', function () { res.body.message.should.be.a('string'); }); - it('it should allows to store a new password using a verification url.', async function () { + it('it should allows to store a new password using a verification url.', async function() { const userEmail = helper.generateEmail(); await helper.prepareUser(server, { role: 'user', - email: userEmail, + email: userEmail }); const getVerificationUrl = await request(server) .post('/user/forgot') @@ -326,7 +327,7 @@ describe('User API calls', function () { const userStorePassword = { userid: userid, password: 'newPassword', - token: verificationUrl, + token: verificationUrl }; const storePasswordRes = await request(server) @@ -344,15 +345,15 @@ describe('User API calls', function () { }); }); - describe('DELETE /user/:userid', function () { - it('it should delete a user', async function () { + describe('DELETE /user/:userid', function() { + it('it should delete a user', async function() { const admin = await helper.prepareUser(server, { role: 'admin', - email: helper.generateEmail(), + email: helper.generateEmail() }); const user = await helper.prepareUser(server, { role: 'user', - email: helper.generateEmail(), + email: helper.generateEmail() }); expect(await User.exists({ _id: user.userId })).to.equal(true); From 4064bd2730b80fc0b3bd8db2dda844933a055d6c Mon Sep 17 00:00:00 2001 From: Tomas Van Morlegan Date: Fri, 29 Oct 2021 23:17:51 -0300 Subject: [PATCH 6/6] losted requested changes --- api/controllers/user.js | 96 ++++++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 45 deletions(-) diff --git a/api/controllers/user.js b/api/controllers/user.js index 6b7dfa48..26fd421f 100644 --- a/api/controllers/user.js +++ b/api/controllers/user.js @@ -441,59 +441,65 @@ async function storePassword(req, res) { userId: userid, status: false }).exec(); - const expireTime = moment - .utc(resetPassword.resetPasswordExpires) - .isBefore(moment()); - if (expireTime) { - return res.status(500).json({ - message: 'Expired time to reset password! ', - error: 'Expired time to reset password! ' - }); - } - // the token and the hashed token in the db are verified before updating the password - bcrypt.compare(token, resetPassword.resetPasswordToken, function( - errBcrypt, - resBcrypt - ) { - if (!resBcrypt) { + if (resetPassword) { + const expireTime = moment + .utc(resetPassword.resetPasswordExpires) + .isBefore(moment()); + if (expireTime) { return res.status(500).json({ - message: 'Error resetting user password.', - error: 'invalid Token' + message: 'Expired time to reset password! ', + error: 'Expired time to reset password! ' }); } - //hashing the password to store in the db node.js - bcrypt.genSalt(8, function(err, salt) { - bcrypt.hash(password, salt, async function(err, hash) { - const user = await User.findOneAndUpdate( - { _id: userid }, - { password: hash } - ); - if (!user) { - return res.status(404).json({ - message: 'No user found with that ID.' - }); - } - ResetPassword.findByIdAndDelete(resetPassword._id, function( - err, - docs - ) { - if (err) { - return res.status(500).json({ - message: 'ERROR: reset your password email FAILED ', - error: err.message + // the token and the hashed token in the db are verified before updating the password + bcrypt.compare(token, resetPassword.resetPasswordToken, function( + errBcrypt, + resBcrypt + ) { + if (!resBcrypt) { + return res.status(500).json({ + message: 'Error resetting user password.', + error: 'invalid Token' + }); + } + //hashing the password to store in the db node.js + bcrypt.genSalt(8, function(err, salt) { + bcrypt.hash(password, salt, async function(err, hash) { + const user = await User.findOneAndUpdate( + { _id: userid }, + { password: hash } + ); + if (!user) { + return res.status(404).json({ + message: 'No user found with that ID.' }); - } else { - const response = { - success: 1, - url: token, - message: 'Success! We have reset your password.' - }; - return res.status(200).json(response); } + ResetPassword.findByIdAndDelete(resetPassword._id, function( + err, + docs + ) { + if (err) { + return res.status(500).json({ + message: 'ERROR: reset your password email FAILED ', + error: err.message + }); + } else { + const response = { + success: 1, + url: token, + message: 'Success! We have reset your password.' + }; + return res.status(200).json(response); + } + }); }); }); }); - }); + return; + } + throw new Error( + 'we did not detect that the user wants to restore his password' + ); } catch (err) { return res.status(500).json({ message: 'Error resetting user password.',