1
0
mirror of https://github.com/Foltik/Shimapan synced 2025-01-05 15:58:03 -05:00

Separate middleware from verifyBody logic into bodyVerifier

This commit is contained in:
Jack Foltz 2018-08-01 17:11:08 -04:00
parent 67909552dc
commit 827072212d
Signed by: foltik
GPG Key ID: 303F88F996E95541
5 changed files with 2338 additions and 22 deletions

View File

@ -11,7 +11,7 @@ const passport = require('passport');
const canonicalizeRequest = require('../../util/canonicalize').canonicalizeRequest;
const requireAuth = require('../../util/auth').requireAuth;
const wrap = require('../../util/wrap.js');
const verifyBody = require('../../util/verifyBody');
const bodyVerifier = require('../../util/verifyBody').bodyVerifier;
// Wraps passport.authenticate to return a promise
const authenticate = (req, res, next) => {
@ -68,7 +68,7 @@ const registerProps = [
{name: 'password', type: 'string'},
{name: 'invite', type: 'string'}];
router.post('/register',
verifyBody(registerProps), canonicalizeRequest,
bodyVerifier(registerProps), canonicalizeRequest,
validateInvite, validateUsername,
wrap(async (req, res, next) => {
// Update the database
@ -89,7 +89,7 @@ const loginProps = [
{name: 'username', type: 'string', optional: true},
{name: 'displayname', type: 'string', optional: true},
{name: 'password', type: 'string'}];
router.post('/login', verifyBody(loginProps), canonicalizeRequest, wrap(async (req, res, next) => {
router.post('/login', bodyVerifier(loginProps), canonicalizeRequest, wrap(async (req, res, next) => {
// Authenticate
const user = await authenticate(req, res, next);
if (!user)

View File

@ -9,13 +9,12 @@ const User = require(ModelPath + 'User.js');
const wrap = require('../../util/wrap.js');
const requireAuth = require('../../util/auth').requireAuth;
const verifyScope = require('../../util/verifyScope');
const verifyBody = require('../../util/verifyBody');
const bodyVerifier = require('../../util/verifyBody').bodyVerifier;
const createParams = [{name: 'scope', instance: Array}];
router.post('/create', requireAuth('invite.create'), verifyBody(createParams), wrap(async (req, res, next) => {
router.post('/create', requireAuth('invite.create'), bodyVerifier(createParams), wrap(async (req, res, next) => {
const scope = req.body.scope;
const hasPermission = scope.every(scope => verifyScope(req.scope, scope));
if (!hasPermission)
if (!scope.every(scope => verifyScope(req.scope, scope)))
return res.status(403).json({message: 'Requested scope exceeds own scope.'});
const invite = {
@ -38,7 +37,7 @@ router.post('/create', requireAuth('invite.create'), verifyBody(createParams), w
}));
const deleteParams = [{name: 'code', type: 'string'}];
router.post('/delete', requireAuth('invite.delete'), verifyBody(deleteParams), wrap(async (req, res, next) => {
router.post('/delete', requireAuth('invite.delete'), bodyVerifier(deleteParams), wrap(async (req, res, next) => {
let query = {code: req.body.code};
// Users need a permission to delete invites other than their own
@ -48,7 +47,7 @@ router.post('/delete', requireAuth('invite.delete'), verifyBody(deleteParams), w
// Find the invite
const invite = await Invite.findOne(query).catch(next);
if (!invite)
return res.status(404).json({message: 'Invite not found.'});
return res.status(422).json({message: 'Invite not found.'});
// Users need a permission to delete invites that have been used
if (!verifyScope(req.scope, 'invite.delete.used') && invite.used != null && invite.recipient != null)
@ -58,13 +57,15 @@ router.post('/delete', requireAuth('invite.delete'), verifyBody(deleteParams), w
res.status(200).json({message: 'Invite deleted.'});
}));
const getParams = [{name: 'code', type: 'string', optional: true}];
router.get('/get', requireAuth('invite.get'), verifyBody(getParams), wrap(async (req, res, next) => {
const getParams = [{name: 'code', type: 'string', optional: true}, {name: 'issuer', type: 'string', optional: true}];
router.get('/get', requireAuth('invite.get'), bodyVerifier(getParams), wrap(async (req, res, next) => {
let query = {};
// Users need a permission to list invites other than their own
if (!verifyScope(req.scope, 'invite.get.others'))
query.issuer = req.username;
else if (req.body.issuer)
query.issuer = req.body.issuer;
// Narrow down the query by code if specified
if (req.body.code)

View File

@ -1,7 +1,7 @@
// Verifies a single property is well formed
const verifyProp = (req, expected) => new Promise((resolve, reject) => {
const prop = req.body[expected.name];
const sanitizer = require('sanitizer');
// Verifies a single property is well formed
const verifyProp = (prop, expected) => new Promise((resolve, reject) => {
if (!expected.optional && !prop)
return reject({code: 400, message: expected.name + ' not specified.'});
@ -12,13 +12,13 @@ const verifyProp = (req, expected) => new Promise((resolve, reject) => {
return reject({code: 400, message: expected.name + ' malformed.'});
if (prop && expected.maxLength && prop.length > expected.maxLength)
return reject({code: 422, message: expected.name + ' too long.'});
return reject({code: 400, message: expected.name + ' too long.'});
if (prop && expected.sanitize && req.sanitize(prop) !== prop)
return reject({code: 422, message: expected.name + ' contains invalid characters.'});
if (prop && expected.sanitize && sanitizer.sanitize(prop) !== prop)
return reject({code: 400, message: expected.name + ' contains invalid characters.'});
if (prop && expected.restrict && prop.replace(expected.restrict, '') !== prop)
return reject({code: 422, message: expected.name + ' contains invalid characters.'});
return reject({code: 400, message: expected.name + ' contains invalid characters.'});
resolve();
});
@ -26,11 +26,15 @@ const verifyProp = (req, expected) => new Promise((resolve, reject) => {
// Verifies the entire request body is well formed
// expectedProps follows the format:
// [{name: 'myList', instance: 'Array'}, {name: 'myVar', type: 'string', optional: true}, etc.]
const verifyBody = expectedProps =>
const verifyBody = (body, expectedProps) =>
Promise.all(expectedProps.map(expected => verifyProp(body[expected.name], expected)));
const bodyVerifier = expectedProps =>
(req, res, next) => {
Promise.all(expectedProps.map(expected => verifyProp(req, expected)))
verifyBody(req.body, expectedProps)
.then(() => next())
.catch(err => res.status(err.code).json({message: err.message}));
};
module.exports = verifyBody;
exports.verifyBody = verifyBody;
exports.bodyVerifier = bodyVerifier;

2309
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -23,6 +23,7 @@
"passport": "^0.4.0",
"passport-local": "^1.0.0",
"passport-local-mongoose": "^5.0.1",
"sanitizer": "^0.1.3",
"type-is": "^1.6.16",
"vinyl-source-stream": "^2.0.0"
},
@ -42,7 +43,8 @@
"minimatch": "^3.0.4",
"mocha": "^5.2.0",
"nodemon": "^1.18.3",
"npx": "^10.2.0"
"npx": "^10.2.0",
"nyc": "^12.0.2"
},
"author": "Jack Foltz",
"license": "LICENSE",