Skip to content

Commit

Permalink
Merge branch 'develop' into ft-tag-articles-164198500
Browse files Browse the repository at this point in the history
  • Loading branch information
Dsalz committed Mar 4, 2019
2 parents 6241c87 + 436b7bb commit 91abb22
Show file tree
Hide file tree
Showing 4 changed files with 271 additions and 8 deletions.
106 changes: 100 additions & 6 deletions server/controllers/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import db from '../database/models';
import { HelperUtils } from '../utils';
import response from '../utils/response';
import verifyEmailMarkup from '../utils/markups/emailVerificationMarkup';
import passwordResetMarkup from '../utils/markups/passwordResetMarkup';
import '@babel/polyfill';

const { User } = db;
Expand Down Expand Up @@ -93,12 +94,105 @@ export default class Users {
}

/**
* @description This controller method completes the social sign in process
*
* @param {object} req - Express request object
* @param {object} res - Express response object
* @return {undefined}
*/
* @description This controller method sends password reset link e-mail
*
* @param {object} req - Express request object
* @param {object} res - Express response object
* @returns {object} Json response
*/
static async resetPasswordEmail(req, res) {
const { email } = req.body;

const hashedEmail = HelperUtils.hashPassword(email);

try {
const user = await User.findOne({
where: { email }
});

if (user === null) {
response(res).notFound({
message: 'user not found in our records'
});
} else {
HelperUtils.sendMail(
email,
'Authors Haven <no-reply@authorshaven.com>',
'Password Reset',
'Reset Password',
passwordResetMarkup(user.firstname, email, hashedEmail)
);
response(res).success({
message: 'Please, verify password reset link in your email box'
});
}
} catch (err) {
response(res).sendData(400, {
message: err
});
}
}

/**
* @description This controller method resets user password
*
* @param {object} req - Express request object
* @param {object} res - Express response object
* @return {object} Json response
*/
static async resetPassword(req, res) {
const { newPassword, confirmPassword } = req.body;
const isPassword = newPassword === confirmPassword;

if (!isPassword) {
response(res).sendData(400, {
message: 'The supplied passwords do not match'
});
}

try {
const hashPassword = HelperUtils.hashPassword(newPassword);

const { email, hash } = req.query;
const isEmail = await HelperUtils.comparePasswordOrEmail(email, hash);

if (isEmail) {
const user = await User.findOne({
where: { email }
});

if (!user) {
response(res).notFound({
message: 'User not found'
})
} else {
await user.update({
password: hashPassword
});
response(res).success({
message: 'Password reset successful. Please, login using your new password.'
});
}
} else {
response(res).sendData(400, {
message: 'Invalid password reset link'
})
}
} catch (err) {
response(res).sendData(400, {
message: err
});
}
}


/**
* @description This controller method completes the social sign in process
*
* @param {object} req - Express request object
* @param {object} res - Express response object
* @return {undefined}
*/
static async socialLogin(req, res) {
const { data } = req.user;

Expand Down
2 changes: 2 additions & 0 deletions server/routes/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ const authRoute = express.Router();

authRoute.post('/users', Users.signupUser);
authRoute.get('/users/verifyemail', Users.verifyUserEmail);
authRoute.post('/users/reset-password', Users.resetPasswordEmail);
authRoute.patch('/users/reset-password', Users.resetPassword);
authRoute.get('/users/auth/google', passport.authenticate('google', {
scope: [
'https://www.googleapis.com/auth/userinfo.profile',
Expand Down
82 changes: 80 additions & 2 deletions server/test/user.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,15 @@ import app from '../app';

chai.use(chaiHttp);

const QueryURL = '?email=nwabuzor.obiora@gmail.com&hash=$2a$08$vu6Gwj1EgU7/6IJv6juphuraxOv6tOHaeNOvWmsjh0oYHOLRO8/9q';
const invalidQueryURL = '?email=invalid.obiora@gmail.com&hash=$2a$08$vu6Gwj1EgU7/6IJv6juphuraxOv6tOHaeNOvWmsjh0oYHOLRO8/9q';
const signupURL = '/api/users';
const verifyURL = '/api/users/verifyemail?email=nwabuzor.obiora@gmail.com&hash=$2a$08$vu6Gwj1EgU7/6IJv6juphuraxOv6tOHaeNOvWmsjh0oYHOLRO8/9q';
const invalidVerifyURL = '/api/users/verifyemail?email=invalid.obiora@gmail.com&hash=$2a$08$vu6Gwj1EgU7/6IJv6juphuraxOv6tOHaeNOvWmsjh0oYHOLRO8/9q';
const resetPassword = '/api/users/reset-password';
const resetPasswordURL = `/api/users/reset-password${QueryURL}`;
const invalidResetPasswordURL = `/api/users/reset-password${invalidQueryURL}`;
const verifyURL = `/api/users/verifyemail${QueryURL}`;
const invalidVerifyURL = `/api/users/verifyemail${invalidQueryURL}`;


describe('Test signup endpoint and email verification endpoint', () => {
it('It should return a 404 if user don\'t exist during email verification', (done) => {
Expand Down Expand Up @@ -66,6 +72,78 @@ describe('Test signup endpoint and email verification endpoint', () => {
});
});

describe('Test reset password mail endpoint and password link endpoint', () => {
it('It should return a 404 if user records not found', (done) => {
const data = { email: 'ayo-oluwa.adebayo@andela.com' };
chai
.request(app)
.post(resetPassword)
.send(data)
.end((err, res) => {
expect(res.status).to.equal(404);
expect(res.body.message).to.be.a('string');
expect(res.body.message).to.equal('user not found in our records');
done();
});
});

it('It should return a 200 if user email is found in the database', (done) => {
const data = { email: 'nwabuzor.obiora@gmail.com' };
chai
.request(app)
.post(resetPassword)
.send(data)
.end((err, res) => {
expect(res.status).to.equal(200);
expect(res.body.message).to.be.a('string');
expect(res.body.message).to.equal('Please, verify password reset link in your email box');
done();
});
});

it('It should return a 400 if user passwords do not match', (done) => {
const data = { newPassword: 'hello', confirmPassword: 'hell' };
chai
.request(app)
.patch(resetPasswordURL)
.send(data)
.end((err, res) => {
expect(res.status).to.equal(400);
expect(res.body.message).to.be.a('string');
expect(res.body.message).to.equal('The supplied passwords do not match');
done();
});
});

it('It should return a 200 if user passwords match', (done) => {
const data = { newPassword: 'hello', confirmPassword: 'hello' };
chai
.request(app)
.patch(resetPasswordURL)
.send(data)
.end((err, res) => {
expect(res.status).to.equal(200);
expect(res.body.message).to.be.a('string');
expect(res.body.message).to.equal('Password reset successful. Please, login using your new password.');
done();
});
});

it('It should return a 400 if reset link is invalid', (done) => {
const data = { newPassword: 'hello', confirmPassword: 'hello' };
chai
.request(app)
.patch(invalidResetPasswordURL)
.send(data)
.end((err, res) => {
expect(res.status).to.equal(400);
expect(res.body.message).to.be.a('string');
expect(res.body.message).to.equal('Invalid password reset link');
done();
});
});
});

describe('Social Login with Google', () => {
it('should return the google authentication webpage', (done) => {
chai
Expand Down
89 changes: 89 additions & 0 deletions server/utils/markups/passwordResetMarkup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
const passwordResetMarkup = (username, email, hash) => (
`<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Authors Haven</title>
<link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet">
<style>
* {
font-family: 'Open Sans', sans-serif;
}
.logoWrapper {
margin: 0 auto;
background: #5863f8;
padding-top: 20px;
text-align: center;
}
.logoWrapper img {
width: 70px;
}
.username {
font-size: 1em;
color: white;
}
.message{
font-size: 1em;
color: white;
}
.verifyLink {
display: inline-block;
background: #5863f8;
padding: 10px;
color: rgb(255, 255, 255);
text-decoration: none;
font-size: 1em;
border-radius: 5px;
}
.verifyLink:hover{
background: #7c85fa;
cursor: pointer;
}
body {
text-align: center;
padding: 100px;
}
.borderWrapper {
border: #5863f8 1px solid;
padding-bottom: 30px;
background: #363636;
border-radius: 0 0 20px 20px;
text-align: center;
}
.title{
color:white;
font-size: 1em;
padding-bottom: 10px;
}
.content{
padding: 50px 0;
text-align: center;
}
</style>
</head>
<body>
<div class="borderWrapper">
<div class="logoWrapper">
<img src="https://res.cloudinary.com/shaolinmkz/image/upload/v1551370652/authors-haven/AH_logo.gif"
alt="AH_logo" />
<p class="title">Authors Haven</p>
</div>
<div class="content">
<h3 class="username">Hello ${username},</h3>
<p class="message">
You recently requested to reset your password for your Authors Haven account.
</p><br>
<p class="message">Please, click the button below to proceed.</p>
</div>
<a class="verifyLink" href="http://localhost:3000/api/users/reset-password?email=${email}&hash=${hash}" target="_blank">
Reset Password
</a>
</div>
</body>
</html>
`
);

export default passwordResetMarkup;

0 comments on commit 91abb22

Please sign in to comment.