Skip to content

Commit

Permalink
Merge pull request #14 from ditup/json-schema
Browse files Browse the repository at this point in the history
Refactoring validation to json-schema
  • Loading branch information
mrkvon authored Aug 20, 2017
2 parents c103261 + 11a1cd3 commit 1eb8831
Show file tree
Hide file tree
Showing 38 changed files with 945 additions and 1,264 deletions.
12 changes: 3 additions & 9 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,13 @@
const express = require('express'),
bodyParser = require('body-parser'),
passport = require('passport'),
helmet = require('helmet'),
expressValidator = require('express-validator');
helmet = require('helmet');

// load internal dependencies
const models = require('./models'),
config = require('./config'),
authenticate = require('./controllers/authenticate'),
deserialize = require('./controllers/deserialize'),
customValidators = require('./controllers/validators/custom');
deserialize = require('./controllers/deserialize');


// configure the database for all the models
Expand Down Expand Up @@ -58,10 +56,6 @@ app.use(deserialize);
app.use(passport.initialize());
app.use(authenticate);

app.use(expressValidator({
customValidators: customValidators
}));

// we set Content-Type header of all requests to JSON API
app.use(function (req, res, next) {
res.contentType('application/vnd.api+json');
Expand Down Expand Up @@ -122,4 +116,4 @@ app.use(function(err, req, res, next) { // eslint-disable-line no-unused-vars
});


module.exports = app;
module.exports = app;
3 changes: 2 additions & 1 deletion controllers/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ const path = require('path'),
exports.postMessages = async function (req, res, next) {
try {
// message body and receiver should be provided
const { body, to: { username: to } } = req.body;
const { body: rawBody, to: { username: to } } = req.body;
const body = rawBody.trim();

// message sender is the authorized user
const from = req.auth.username;
Expand Down
6 changes: 3 additions & 3 deletions controllers/tags.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,11 @@ exports.relatedToMyTags = async function (req, res, next) {
*/
exports.relatedToTags = async function (req, res, next) {

const tagsArray = req.query.filter.relatedToTags.split(',');
const tags = req.query.filter.relatedToTags;

try{
try {
// get tags from database
const foundTags = await models.tag.findTagsRelatedToTags(tagsArray);
const foundTags = await models.tag.findTagsRelatedToTags(tags);

// define the parameters for self link
foundTags.urlParam = encodeURIComponent('filter[relatedToTags]');
Expand Down
17 changes: 1 addition & 16 deletions controllers/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ exports.gotoGetNewUsersWithMyTags = function (req, res, next) {

exports.gotoGetUsersWithMyTags = function (req, res, next) {
// TODO DZIK req.query.filter.byMyTags returns string not bool (how was it checked before?)
if (_.has(req, 'query.filter.byMyTags') && req.query.filter.byMyTags === 'true') {
if (_.has(req, 'query.filter.byMyTags')) {
return next();
}
return next('route');
Expand Down Expand Up @@ -316,24 +316,9 @@ exports.getUser = async function (req, res, next) {
// edit a user with PATCH request
// presumption: data in body should already be valid and user should be logged in as herself
exports.patchUser = async function (req, res, next) {
// check that user id in body equals username from url
if (req.body.id !== req.params.username) {
const e = new Error('username in url parameter and in body don\'t match');
e.status = 400;
return next(e);
}

// the list of allowed profile fields (subset of these needs to be provided)
const profileFields = ['givenName', 'familyName', 'description'];

// check that only profile fields are present in the request body
const unwantedParams = _.difference(Object.keys(req.body), _.union(profileFields, ['id', 'location']));
if (unwantedParams.length > 0) { // if any unwanted fields are present, error.
const e = new Error('The request body contains unexpected attributes');
e.status = 400;
return next(e);
}

// pick only the profile fields from the body of the request
const profile = _.pick(req.body, profileFields);

Expand Down
180 changes: 7 additions & 173 deletions controllers/validators/account.js
Original file line number Diff line number Diff line change
@@ -1,175 +1,9 @@
const rules = require('./rules');
const _ = require('lodash');
const validate = require('./validate-by-schema');

exports.resetPassword = function (req, res, next) {

// check that only expected attributes are present in request body
const expectedAttrs = ['id'];
const actualAttrs = Object.keys(req.body);

const unexpectedAttrs = _.difference(actualAttrs, expectedAttrs);

if (unexpectedAttrs.length > 0) {
return res.status(400).end();
}


// check that id is a valid username or email
const { username: usernameRules, email: emailRules } = rules.user;

req.checkBody({ id: usernameRules });
req.checkBody({ id: emailRules });

const errors = req.validationErrors();

// if id is not valid username nor email, the errors length is 2 or more. Otherwise 1
if (errors.length >= 2) {

const errorOutput = { errors: [] };
for(const e of errors) {
errorOutput.errors.push({ meta: e });
}
return res.status(400).json(errorOutput);
}

return next();
};

exports.updateResetPassword = function (req, res, next) {

const { username, code, password } = rules.user;
const pickedRules = {
id: username,
code,
password
};

req.checkBody(pickedRules);
const errors = req.validationErrors();

// if id is not valid username nor email, the errors length is 2 or more. Otherwise 1
if (errors) {

const errorOutput = { errors: [] };
for(const e of errors) {
errorOutput.errors.push({ meta: e });
}
return res.status(400).json(errorOutput);
}

return next();
};

exports.updateUnverifiedEmail = function (req, res, next) {

const expectedAttrs = ['id', 'email', 'password'];
const actualAttrs = Object.keys(req.body);

// only the right attributes
const unexpectedAttrs = _.difference(actualAttrs, expectedAttrs);
const missingAttrs = _.difference(expectedAttrs, actualAttrs);

if (unexpectedAttrs.length > 0) {
return res.status(400).end();
}

if (missingAttrs.length > 0) {
return res.status(400).json({
errors: [{ meta: 'missing password attribute' }]
});
}

// mismatch body.id & auth.username
if (req.auth.username !== req.body.id) {
return res.status(403).json({
errors: [{ meta: `not enough rights to update user ${req.body.id}` }]
});
}



const pickedRules = _.pick(rules.user, ['email', 'password']);

req.checkBody(pickedRules);
const errors = req.validationErrors();

if (errors) {
const errorOutput = { errors: [] };

for(const e of errors) {
errorOutput.errors.push({ meta: e });
}
return res.status(400).json(errorOutput);
}

return next();
};

exports.verifyEmail = function (req, res, next) {

let errors = [];

const { username, code } = rules.user;

// check that the username is valid
req.body.username = req.body.id;
req.checkBody({ username });
delete req.body.username;

// check that the code is valid
req.body.code = req.body.emailVerificationCode;
req.checkBody({ code });
delete req.body.code;

errors = errors.concat(req.validationErrors() || []);

if (errors.length === 0) {
return next();
}

return next(errors);
};

exports.changePassword = function (req, res, next) {

let errors = [];

// username in url should match username in body should match logged user
if (req.body.id !== req.auth.username) {
errors.push({
param: 'parameters',
msg: 'document id doesn\'t match logged user'
});
}

// only expected fields should be present
const passwordFields = ['id', 'password', 'oldPassword'];
const requestBodyFields = Object.keys(req.body);

const unexpectedFields = _.difference(requestBodyFields, passwordFields);

if (unexpectedFields.length > 0) {
errors.push({
param: 'attributes',
msg: 'unexpected body attributes',
value: unexpectedFields
});
}

// both passwords should be valid
const passwordRules = rules.user.password;

req.checkBody({
password: passwordRules,
oldPassword: passwordRules
});

errors = errors.concat(req.validationErrors() || []);

if (errors.length === 0) {
return next();
}

return next(errors);
};
const changePassword = validate('changePassword', [['auth.username', 'body.id']]);
const resetPassword = validate('resetPassword');
const updateResetPassword = validate('updateResetPassword');
const updateUnverifiedEmail = validate('updateUnverifiedEmail', [['auth.username', 'body.id']]);
const verifyEmail = validate('verifyEmail');

module.exports = { resetPassword, updateResetPassword, updateUnverifiedEmail, verifyEmail, changePassword };
Loading

0 comments on commit 1eb8831

Please sign in to comment.