Przeglądaj źródła

Separate middleware from verifyBody logic into bodyVerifier

production
Jack Foltz 5 lat temu
rodzic
commit
827072212d
Podpisane przez: foltik <jack@foltz.io> ID klucza GPG: 303F88F996E95541
5 zmienionych plików z 2438 dodań i 122 usunięć
  1. +3
    -3
      app/routes/api/auth.js
  2. +9
    -8
      app/routes/api/invites.js
  3. +14
    -10
      app/util/verifyBody.js
  4. +2409
    -100
      package-lock.json
  5. +3
    -1
      package.json

+ 3
- 3
app/routes/api/auth.js Wyświetl plik

@@ -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)


+ 9
- 8
app/routes/api/invites.js Wyświetl plik

@@ -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)


+ 14
- 10
app/util/verifyBody.js Wyświetl plik

@@ -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;

+ 2409
- 100
package-lock.json
Plik diff jest za duży
Wyświetl plik


+ 3
- 1
package.json Wyświetl plik

@@ -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",


Ładowanie…
Anuluj
Zapisz