Skip to content

Commit

Permalink
Merge e6354ad into b866ecf
Browse files Browse the repository at this point in the history
  • Loading branch information
OvieMudi committed Aug 23, 2019
2 parents b866ecf + e6354ad commit d9f2445
Show file tree
Hide file tree
Showing 13 changed files with 375 additions and 31 deletions.
62 changes: 60 additions & 2 deletions src/controllers/userController.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import debug from 'debug';
import models from '../database/models';
import helpers from '../helpers';
import services from '../services';

const log = debug('dev');

const {
authHelper, successResponse, errorResponse, responseMessage, verifyUser
} = helpers;
const { userServices: { findUser, findFollower, getAllUsers } } = services;
const {
userServices: {
findUser, findFollower, getAllUsers, addUser
}
} = services;
const { User, Follower } = models;

/**
Expand Down Expand Up @@ -80,6 +87,8 @@ const login = async (req, res) => {
const data = {
user: {
id: foundUser.id,
firstname: foundUser.firstName,
lastName: foundUser.lastName,
email: foundUser.email,
token: authHelper.generateToken({ id: foundUser.id }),
bio: foundUser.bio
Expand Down Expand Up @@ -230,6 +239,55 @@ const unfollow = async (req, res) => {
return responseMessage(res, 200, data);
};

/**
* ADMIN Get one user
* @param {Object} req - server request
* @param {Object} res - server response
* @return {Object} - custom response
*/
const getUser = async (req, res) => {
const { userId } = req.params;
const { dataValues: user } = await findUser(userId);

const response = {
message: 'successful',
data: user
};

return responseMessage(res, 200, response);
};

/**
* ADMIN create user
* @param {Object} req - server request
* @param {Object} res - server response
* @return {Object} - custom response
*/
const createUser = async (req, res) => {
try {
const foundUser = await findUser(req.body.email);
if (foundUser) {
return responseMessage(res, 409, { error: 'user with email already exists' });
}

const createdUser = await addUser(req.body);
const response = {
data: {
firstName: createdUser.firstName,
lastName: createdUser.lastName,
email: createdUser.email,
roleId: createdUser.roleId
}
};

return responseMessage(res, 201, response);
} catch (error) {
log(error.message);
return responseMessage(res, 500, { error: 'an error occurred' });
}
};


export default {
getProfile, editProfile, signUp, login, listUsers, follow, unfollow
getProfile, editProfile, signUp, login, listUsers, follow, unfollow, createUser, getUser
};
14 changes: 14 additions & 0 deletions src/database/seeders/20190724090406-user.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import bcrypt from 'bcrypt';

const superAdminRoleId = '2c4dfb3f-1798-43d4-8eb6-1c125994a263';

export const up = queryInterface => queryInterface.bulkInsert('Users', [{
id: '122a0d86-8b78-4bb8-b28f-8e5f7811c456',
firstName: 'Eden',
Expand Down Expand Up @@ -133,5 +135,17 @@ export const up = queryInterface => queryInterface.bulkInsert('Users', [{
isSubscribed: true,
createdAt: new Date(),
updatedAt: new Date()
},
{
id: 'ca849dff-da64-424a-a336-8bf1b59c5105',
firstName: 'Sheev',
lastName: 'Palpatine',
email: 'theemperor@empire.com',
password: bcrypt.hashSync('theEmperor', 5),
roleId: superAdminRoleId,
isVerified: true,
isSubscribed: false,
createdAt: new Date(),
updatedAt: new Date()
}], {});
export const down = queryInterface => queryInterface.bulkDelete('Users', null, {});
29 changes: 28 additions & 1 deletion src/helpers/authHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,31 @@ const generateToken = (payload) => {
return token;
};

export default { hashedPassword, comparePassword, generateToken };
/**
* Generate a random password
* @returns {String} randomPassword
* @returns {String} hashedRandomPassword
*/
const generateRandomPassword = () => {
const upperLetters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
const lowerLetters = upperLetters.toLowerCase();
const digits = '0123456789';
const alphanumeric = upperLetters + lowerLetters + digits;
const alphanumericArray = alphanumeric.split('');
const arrayLength = alphanumericArray.length;
let randomPassword = '';
const passwordLength = 8;
let i;

for (i = 0; i <= passwordLength; i += 1) {
const randomNumber = Math.floor(Math.random() * arrayLength);
const char = alphanumericArray[randomNumber];
randomPassword += char;
}

return { randomPassword, hashedRandomPassword: hashedPassword(randomPassword) };
};

export default {
hashedPassword, comparePassword, generateToken, generateRandomPassword
};
11 changes: 11 additions & 0 deletions src/middlewares/userValidator.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,17 @@ const userValidator = {
followUserValidator: [
isValidUUIDParam('userId'),
paramsValidatorError
],
validateCreateUser: [
isValidEmail(),
isValidName('firstName'),
isValidName('lastName'),
isValidName('roleName'),
validatorError
],
validateUUID: [
isValidUUIDParam('userId'),
validatorError
]
};

Expand Down
5 changes: 5 additions & 0 deletions src/routes/auth.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import express from 'express';
import AuthController from '../controllers/AuthController';
import userController from '../controllers/userController';
import middlewares from '../middlewares';

const auth = express.Router();

const { signUp, login } = userController;
const AUTH_URL = '/auth';

const { userValidator, verifyToken } = middlewares;
Expand All @@ -16,5 +19,7 @@ auth.post(`${AUTH_URL}/changepassword`, verifyToken, userValidator.changePasswor

// verifyUser route
auth.patch(`${AUTH_URL}/verify/:token`, verifyToken, updateStatus);
auth.post(`${AUTH_URL}/register`, userValidator.signUpValidator, signUp);
auth.post(`${AUTH_URL}/login`, userValidator.loginValidator, login);

export default auth;
23 changes: 12 additions & 11 deletions src/routes/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,34 @@ import userController from '../controllers/userController';

const {
userValidator: {
signUpValidator, loginValidator, profileValidator, editProfileValidator, followUserValidator
profileValidator,
editProfileValidator,
validateUUID,
validateCreateUser,
followUserValidator
},
verifyToken,
authorizeUser
} = middlewares;

const {
signUp, login, getProfile, editProfile, listUsers, follow, unfollow
getProfile, editProfile, createUser, listUsers, getUser, follow, unfollow
} = userController;
const user = express.Router();

const USER_URL = '/users';
const PROFILE_URL = '/profiles';

// Route to sign up a user
user.post(`${USER_URL}`, signUpValidator, signUp);

// Route to login a user
user.post(`${USER_URL}/login`, loginValidator, login);

// Route to get all users
user.get(`${USER_URL}`, verifyToken, listUsers);

// Route to get user profile by userId
user.get(`${PROFILE_URL}/:userId`, verifyToken, authorizeUser(['reader', 'author', 'admin', 'superadmin']), profileValidator, getProfile);


// Route to edit a user profile
user.patch(`${PROFILE_URL}`, verifyToken, authorizeUser(['reader', 'author', 'admin', 'superadmin']), editProfileValidator, editProfile);
user.post(`${USER_URL}`, verifyToken, authorizeUser(['superadmin']), validateCreateUser, createUser);
// Route to get all users
user.get(`${USER_URL}`, verifyToken, listUsers);
user.get(`${USER_URL}/:userId`, verifyToken, authorizeUser(['admin', 'superadmin']), validateUUID, getUser);

user.post('/profiles/:userId/follow', followUserValidator, verifyToken, follow);
user.delete('/profiles/:userId/follow', followUserValidator, verifyToken, unfollow);
Expand Down
33 changes: 32 additions & 1 deletion src/services/userService.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import models from '../database/models';
import helpers from '../helpers';

const { User, Role, Follower } = models;
const { authHelper: { generateRandomPassword } } = helpers;

/**
* @description Finds a user from the database by id or email
Expand Down Expand Up @@ -55,9 +57,38 @@ const getAllUsers = async () => {
return allUsers;
};

/**
* Retrieves all users from the database
* @param {Object} requestBody - body of request
* @returns {Object} rows - array of objects
*/
const addUser = async (requestBody) => {
const {
firstName, lastName, email, roleName
} = requestBody;

const { hashedRandomPassword } = generateRandomPassword();

const roles = await Role.findAll();
let roleId;

if (roleName) {
const role = roles.find(roleObject => roleObject.roleName === roleName);
roleId = role.id;
}

const user = {
firstName, lastName, email, password: hashedRandomPassword, roleId
};

const createdUser = await User.create(user);
return createdUser;
};

export default {
findUser,
findFollower,
getAllUsers,
updateUser
updateUser,
addUser
};
78 changes: 75 additions & 3 deletions tests/auth.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ const loggedInUser2Token = jwt.sign({ id: '8f3e7eda-090a-4c44-9ffe-58443de5e1f8'
const loggedInUser3Token = jwt.sign({ id: '8487ef08-2ac2-4387-8bd6-738b12c75dff' }, SECRET_KEY, { expiresIn: '60s' });

describe('AUTH', () => {
describe('POST /auth/signup', () => {
const signupEndpoint = `${BASE_URL}/users`;
describe('POST /auth/register', () => {
const signupEndpoint = `${BASE_URL}/auth/register`;
it('should #create a user and #generate jwt', (done) => {
chai
.request(server)
Expand Down Expand Up @@ -69,7 +69,7 @@ describe('AUTH', () => {
});

describe('POST /api/users/login', () => {
const loginsignupEndpoint = `${BASE_URL}/users/login`;
const loginsignupEndpoint = `${BASE_URL}/auth/login`;
const authErrorMessage = 'email or password is incorrect';
it('should #login a user and #generate jwt', (done) => {
chai
Expand Down Expand Up @@ -285,3 +285,75 @@ describe('AUTH', () => {
});
});
});

describe('POST /api/v1/auth/login', () => {
const loginsignupEndpoint = `${BASE_URL}/auth/login`;
const authErrorMessage = 'email or password is incorrect';
it('should #login a user and #generate jwt', (done) => {
chai
.request(server)
.post(loginsignupEndpoint)
.type('form')
.send(userMock.seededUser1)
.end((err, res) => {
const { user } = res.body;
expect(res).status(200);
expect(user).property('token');
expect(user).property('email');
expect(user).property('bio');
done(err);
});
});
it('should return authorized error on incorrect email', (done) => {
const incorrectEmail = { ...userMock.validUser };
incorrectEmail.email = 'wrong@email.com';
chai
.request(server)
.post(loginsignupEndpoint)
.type('form')
.send(incorrectEmail)
.end((err, res) => {
expect(res).status(401);
expect(res.body).property('errors').eq(authErrorMessage);
done(err);
});
});
it('should return authorized error on incorrect email', (done) => {
const incorrectPassword = { ...userMock.validUser };
incorrectPassword.password = 'WrongPassword1';
chai
.request(server)
.post(loginsignupEndpoint)
.type('form')
.send(incorrectPassword)
.end((err, res) => {
expect(res).status(401);
expect(res.body).property('errors').eq(authErrorMessage);
done(err);
});
});
it('should return error if password id not correct', (done) => {
chai
.request(server)
.post(loginsignupEndpoint)
.type('form')
.send(userMock.seededUser2)
.end((err, res) => {
expect(res).status(401);
expect(res.body).property('errors').eq('email or password is incorrect');
done(err);
});
});
it('should return error if user is not verified', (done) => {
chai
.request(server)
.post(loginsignupEndpoint)
.type('form')
.send(userMock.validUser2)
.end((err, res) => {
expect(res).status(401);
expect(res.body).property('errors').eq('please verify your email');
done(err);
});
});
});
Loading

0 comments on commit d9f2445

Please sign in to comment.