diff --git a/.codeclimate.yml b/.codeclimate.yml index 7f32f09..28fcf90 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -5,6 +5,9 @@ engines: config: .eslintrc duplication: enabled: true + checks: + Similar code: + enabled: false config: languages: - javascript @@ -18,3 +21,5 @@ ratings: exclude_paths: - node_modules/**/* - tests/* +- server/models +- server/migrations diff --git a/server/controllers/documents.js b/server/controllers/documents.js index cfd5792..5808f64 100644 --- a/server/controllers/documents.js +++ b/server/controllers/documents.js @@ -1,5 +1,5 @@ import models from '../models'; -import { documentCreator, isAdmin, isUser } from '../helpers/helper'; +import { documentCreator, isUser } from '../helpers/helper'; import paginate from '../helpers/paginate'; const Document = models.Document; @@ -81,7 +81,7 @@ export default { update(req, res) { Document.findById(req.params.id) .then((document) => { - if (!isAdmin(req.user.id) && !isUser(document.userId, req.user.id)) { + if (!isUser(document.userId, req.user.id)) { return res.status(401).send({ message: "Unauthorised user. You don't have permission to update this document" }); @@ -121,7 +121,7 @@ export default { delete(req, res) { Document.findById(req.params.id) .then((document) => { - if (!isAdmin(req.user.id) && !isUser(document.userId, req.user.id)) { + if (!isUser(document.userId, req.user.id)) { return res.status(401).send({ message: "Unauthorised user. You don't have permission to delete this document" }); diff --git a/server/middleware/middleware.js b/server/middleware/middleware.js index 183f1c8..8a0641e 100644 --- a/server/middleware/middleware.js +++ b/server/middleware/middleware.js @@ -6,262 +6,253 @@ const Document = models.Document; const Role = models.Role; const User = models.User; -/** - * @description checks if a user is authenticated - * @function - * @param {Object} req - * @param {Object} res - * @param {callback} next - * @returns {void} - */ -export function authenticate(req, res, next) { - const token = req.headers['x-auth']; +export default { + /** + * @description checks if a user is authenticated + * @function + * @param {Object} req + * @param {Object} res + * @param {callback} next + * @returns {void} + */ + authenticate(req, res, next) { + const token = req.headers['x-auth']; - if (!token) { - return res.status(401).send({ message: 'Unauthorised user' }); - } + if (!token) { + return res.status(401).send({ message: 'Unauthorised user' }); + } - findByToken(token) - .then((user) => { - if (!user) { - return Promise.reject(); - } + findByToken(token) + .then((user) => { + if (!user) { + return Promise.reject(); + } - req.user = user; - req.token = token; - next(); - }) - .catch(() => res.status(401).send({ error: 'Unauthorised user' })); -} + req.user = user; + req.token = token; + next(); + }) + .catch(() => res.status(401).send({ error: 'Unauthorised user' })); + }, + /** + * @description checks if a document can be found by its id + * @function + * @param {Object} req + * @param {Object} res + * @param {callback} next + * @returns {void} + */ + findDocumentById(req, res, next) { + Document.findById(req.params.id) + .then((document) => { + if (!document) { + return res.status(404).send({ + message: 'Document not found' + }); + } -/** - * @description checks if a document can be found by its id - * @function - * @param {Object} req - * @param {Object} res - * @param {callback} next - * @returns {void} - */ -export function findDocumentById(req, res, next) { - Document.findById(req.params.id) - .then((document) => { - if (!document) { - return res.status(404).send({ - message: 'Document not found' - }); - } + next(); + }) + .catch(() => res.status(400).send({ + message: 'Invalid ID' + })); + }, + /** + * @description checks if a role can be found by its id + * @function + * @param {Object} req + * @param {Object} res + * @param {callback} next + * @returns {void} + */ + findRoleById(req, res, next) { + Role.findById(req.params.id) + .then((role) => { + if (!role) { + return res.status(404).send({ + message: 'Role not found' + }); + } - next(); - }) - .catch(() => res.status(400).send({ - message: 'Invalid ID' - })); -} + next(); + }) + .catch(() => res.status(400).send({ + message: 'Invalid ID' + })); + }, + /** + * @description checks if a user can be found by its id + * @function + * @param {Object} req + * @param {Object} res + * @param {callback} next + * @returns {void} + */ + findUserById(req, res, next) { + User.findById(req.params.id) + .then((user) => { + if (!user) { + return res.status(404).send({ + message: 'User not found' + }); + } -/** - * @description checks if a role can be found by its id - * @function - * @param {Object} req - * @param {Object} res - * @param {callback} next - * @returns {void} - */ -export function findRoleById(req, res, next) { - Role.findById(req.params.id) - .then((role) => { - if (!role) { - return res.status(404).send({ - message: 'Role not found' - }); - } + next(); + }) + .catch(() => res.status(400).send({ + message: 'Invalid ID' + })); + }, + /** + * @description checks if an authenticated user is an admin + * @function + * @param {Object} req + * @param {Object} res + * @param {callback} next + * @returns {Response} message + */ + isAdministrator(req, res, next) { + if (req.user.roleId !== 1) { + return res.status(403).send({ + message: 'The resource you are looking for does not exist' + }); + } + + next(); + }, + /** + * @description validates inputs for document creation + * @function + * @param {Object} req + * @param {Object} res + * @param {callback} next + * @returns {json} json response containing the errors if any + */ + validateDocument(req, res, next) { + req.checkBody('title', 'Title must be at least five characters long').isLength({ min: 5 }); + req.checkBody('content', 'Content is required').notEmpty(); + req.checkBody('access', 'Access is required').notEmpty(); + req.checkBody('access', 'Access must be a string').isAlpha(); - next(); - }) - .catch(() => res.status(400).send({ - message: 'Invalid ID' - })); -} + const errors = req.validationErrors(); -/** - * @description checks if a user can be found by its id - * @function - * @param {Object} req - * @param {Object} res - * @param {callback} next - * @returns {void} - */ -export function findUserById(req, res, next) { - User.findById(req.params.id) - .then((user) => { - if (!user) { - return res.status(404).send({ - message: 'User not found' + if (errors) { + return res.status(400).json(generateErrors(errors)); + } + next(); + }, + /** + * @description validates limit and offset if they are provided + * @function + * @param {Object} req + * @param {Object} res + * @param {callback} next + * @returns {json} json error response + */ + validateLimitAndOffset(req, res, next) { + if (req.query.limit || req.query.offset) { + if (!Number.isInteger(Number(req.query.limit)) + || !Number.isInteger(Number(req.query.offset))) { + return res.status(400).send({ + message: 'Limit and Offset params must be numbers' }); } + } - next(); - }) - .catch(() => res.status(400).send({ - message: 'Invalid ID' - })); -} - -/** - * @description checks if an authenticated user is an admin - * @function - * @param {Object} req - * @param {Object} res - * @param {callback} next - * @returns {Response} message - */ -export function isAdministrator(req, res, next) { - if (req.user.roleId !== 1) { - return res.status(403).send({ - message: 'The resource you are looking for does not exist' - }); - } - - next(); -} - -/** - * @description validates inputs for document creation - * @function - * @param {Object} req - * @param {Object} res - * @param {callback} next - * @returns {json} json response containing the errors if any - */ -export function validateDocument(req, res, next) { - req.checkBody('title', 'Title must be at least five characters long').isLength({ min: 5 }); - req.checkBody('content', 'Content is required').notEmpty(); - req.checkBody('access', 'Access is required').notEmpty(); - req.checkBody('access', 'Access must be a string').isAlpha(); - - const errors = req.validationErrors(); - - if (errors) { - return res.status(400).json(generateErrors(errors)); - } - next(); -} + next(); + }, + /** + * @description validates login fields + * @function + * @param {Object} req + * @param {Object} res + * @param {callback} next + * @returns {json} json response containing the errors if any + */ + validateLogin(req, res, next) { + req.checkBody('email', 'Email must be valid').isEmail(); + req.checkBody('email', 'Email is required').notEmpty(); + req.checkBody('password', 'Password is required').notEmpty(); -/** - * @description validates limit and offset if they are provided - * @function - * @param {Object} req - * @param {Object} res - * @param {callback} next - * @returns {json} json error response - */ -export function validateLimitAndOffset(req, res, next) { - if (req.query.limit || req.query.offset) { - if (!Number.isInteger(Number(req.query.limit)) - || !Number.isInteger(Number(req.query.offset))) { + const errors = req.validationErrors(); + if (errors) { + return res.status(400).json(generateErrors(errors)); + } + next(); + }, + /** + * @description validates input param in the url + * @function + * @param {Object} req + * @param {Object} res + * @param {callback} next + * @returns {json} json response containing the errors if any + */ + validateParam(req, res, next) { + if (isNaN(req.params.id)) { return res.status(400).send({ - message: 'Limit and Offset params must be numbers' + message: 'Param must be a number' }); } - } - - next(); -} - -/** - * @description validates login fields - * @function - * @param {Object} req - * @param {Object} res - * @param {callback} next - * @returns {json} json response containing the errors if any - */ -export function validateLogin(req, res, next) { - req.checkBody('email', 'Email must be valid').isEmail(); - req.checkBody('email', 'Email is required').notEmpty(); - req.checkBody('password', 'Password is required').notEmpty(); - - const errors = req.validationErrors(); - if (errors) { - return res.status(400).json(generateErrors(errors)); - } - next(); -} - -/** - * @description validates input param in the url - * @function - * @param {Object} req - * @param {Object} res - * @param {callback} next - * @returns {json} json response containing the errors if any - */ -export function validateParam(req, res, next) { - if (isNaN(req.params.id)) { - return res.status(400).send({ - message: 'Param must be a number' - }); - } - next(); -} - -/** - * @description validates search query param in the url - * @function - * @param {Object} req - * @param {Object} res - * @param {callback} next - * @returns {json} json response containing the errors if any - */ -export function validateQuery(req, res, next) { - if (req.query.q === undefined || req.query.q === '') { - return res.status(400).send({ - message: 'Query param is required' - }); - } - next(); -} - -/** - * @description validates input for role creation - * @function - * @param {Object} req - * @param {Object} res - * @param {callback} next - * @returns {json} json response containing the errors if any - */ -export function validateRole(req, res, next) { - req.checkBody('name', 'Name can only contain letters').isAlpha(); - req.checkBody('name', 'Name cannot be empty').notEmpty(); - - const errors = req.validationErrors(); - if (errors) { - return res.status(400).json(generateErrors(errors)); - } - next(); -} + next(); + }, + /** + * @description validates search query param in the url + * @function + * @param {Object} req + * @param {Object} res + * @param {callback} next + * @returns {json} json response containing the errors if any + */ + validateQuery(req, res, next) { + if (req.query.q === undefined || req.query.q === '') { + return res.status(400).send({ + message: 'Query param is required' + }); + } + next(); + }, + /** + * @description validates input for role creation + * @function + * @param {Object} req + * @param {Object} res + * @param {callback} next + * @returns {json} json response containing the errors if any + */ + validateRole(req, res, next) { + req.checkBody('name', 'Name can only contain letters').isAlpha(); + req.checkBody('name', 'Name cannot be empty').notEmpty(); -/** - * @description validates inputs for user creation - * @function - * @param {Object} req - * @param {Object} res - * @param {callback} next - * @returns {json} json response containing the errors if any - */ -export function validateUser(req, res, next) { - req.checkBody('email', 'Invalid email').isEmail(); - req.checkBody('email', 'Email is required').notEmpty(); - req.checkBody('username', 'Username cannot contain number').isAlpha(); - req.checkBody('username', 'Username is required').notEmpty(); - req.checkBody('firstname', 'Firstname cannot contain number').isAlpha(); - req.checkBody('firstname', 'Firstname is required').notEmpty(); - req.checkBody('lastname', 'Lastname cannot contain number').isAlpha(); - req.checkBody('lastname', 'Lastname is required').notEmpty(); - req.checkBody('password', 'Password is required').notEmpty(); + const errors = req.validationErrors(); + if (errors) { + return res.status(400).json(generateErrors(errors)); + } + next(); + }, + /** + * @description validates inputs for user creation + * @function + * @param {Object} req + * @param {Object} res + * @param {callback} next + * @returns {json} json response containing the errors if any + */ + validateUser(req, res, next) { + req.checkBody('email', 'Invalid email').isEmail(); + req.checkBody('email', 'Email is required').notEmpty(); + req.checkBody('username', 'Username cannot contain number').isAlpha(); + req.checkBody('username', 'Username is required').notEmpty(); + req.checkBody('firstname', 'Firstname cannot contain number').isAlpha(); + req.checkBody('firstname', 'Firstname is required').notEmpty(); + req.checkBody('lastname', 'Lastname cannot contain number').isAlpha(); + req.checkBody('lastname', 'Lastname is required').notEmpty(); + req.checkBody('password', 'Password is required').notEmpty(); - const errors = req.validationErrors(); - if (errors) { - return res.status(400).json(generateErrors(errors)); + const errors = req.validationErrors(); + if (errors) { + return res.status(400).json(generateErrors(errors)); + } + next(); } - next(); -} +}; diff --git a/server/routes/routes.js b/server/routes/routes.js index b83016b..a06c4f1 100644 --- a/server/routes/routes.js +++ b/server/routes/routes.js @@ -1,68 +1,58 @@ import documentController from '../controllers/documents'; -import rolesController from '../controllers/roles'; +import roleController from '../controllers/roles'; import searchController from '../controllers/search'; -import usersController from '../controllers/users'; -import { - authenticate, - findDocumentById, - findRoleById, - findUserById, - isAdministrator, - validateDocument, - validateLimitAndOffset, - validateLogin, - validateParam, - validateQuery, - validateRole, - validateUser -} from '../middleware/middleware'; +import userController from '../controllers/users'; +import m from '../middleware/middleware'; const routes = (router) => { // Document routes router.route('/documents') - .get(authenticate, validateLimitAndOffset, documentController.getAll) - .post(authenticate, validateDocument, documentController.create); + .get(m.authenticate, m.validateLimitAndOffset, documentController.getAll) + .post(m.authenticate, m.validateDocument, documentController.create); router.route('/documents/:id') - .get(authenticate, validateParam, findDocumentById, documentController.getOne) - .put(authenticate, validateParam, findDocumentById, documentController.update) - .delete(authenticate, validateParam, findDocumentById, documentController.delete); + .get(m.authenticate, m.validateParam, m.findDocumentById, documentController.getOne) + .put(m.authenticate, m.validateParam, m.findDocumentById, documentController.update) + .delete(m.authenticate, m.validateParam, m.findDocumentById, documentController.delete); // Role routes router.route('/roles') - .get(authenticate, isAdministrator, rolesController.getAll) - .post(authenticate, isAdministrator, validateRole, rolesController.create); + .get(m.authenticate, m.isAdministrator, roleController.getAll) + .post(m.authenticate, m.isAdministrator, m.validateRole, roleController.create); router.route('/roles/:id') - .get(authenticate, isAdministrator, validateParam, findRoleById, rolesController.getOne) - .put(authenticate, isAdministrator, validateParam, findRoleById, rolesController.update) - .delete(authenticate, isAdministrator, validateParam, findRoleById, rolesController.delete); + .get(m.authenticate, m.isAdministrator, + m.validateParam, m.findRoleById, roleController.getOne) + .put(m.authenticate, m.isAdministrator, + m.validateParam, m.findRoleById, roleController.update) + .delete(m.authenticate, m.isAdministrator, + m.validateParam, m.findRoleById, roleController.delete); // Search route router.route('/search/users') - .get(validateQuery, searchController.searchUser); + .get(m.validateQuery, searchController.searchUser); router.route('/search/documents') - .get(validateQuery, searchController.searchDocument); + .get(m.validateQuery, searchController.searchDocument); // User routes router.route('/users/login') - .post(validateLogin, usersController.login); + .post(m.validateLogin, userController.login); router.route('/users/logout') - .post(usersController.logout); + .post(userController.logout); router.route('/users') - .get(authenticate, isAdministrator, validateLimitAndOffset, usersController.getAll) - .post(validateUser, usersController.signup); + .get(m.authenticate, m.isAdministrator, m.validateLimitAndOffset, userController.getAll) + .post(m.validateUser, userController.signup); router.route('/users/:id') - .get(authenticate, validateParam, findUserById, usersController.getOne) - .put(authenticate, validateParam, findUserById, usersController.update) - .delete(authenticate, validateParam, findUserById, usersController.delete); + .get(m.authenticate, m.validateParam, m.findUserById, userController.getOne) + .put(m.authenticate, m.validateParam, m.findUserById, userController.update) + .delete(m.authenticate, m.validateParam, m.findUserById, userController.delete); router.route('/users/:id/documents') - .get(authenticate, validateParam, findUserById, usersController.getUserDocuments); + .get(m.authenticate, m.validateParam, m.findUserById, userController.getUserDocuments); }; export default routes;