use roles for totp/u2f setup

This commit is contained in:
Andris Reinman 2019-03-20 23:39:38 +02:00
parent 5fffe6fb79
commit 5ebda12bde
2 changed files with 442 additions and 417 deletions

View file

@ -2,10 +2,19 @@
const Joi = require('joi'); const Joi = require('joi');
const ObjectID = require('mongodb').ObjectID; const ObjectID = require('mongodb').ObjectID;
const tools = require('../../tools');
const roles = require('../../roles');
const util = require('util');
module.exports = (db, server, userHandler) => { module.exports = (db, server, userHandler) => {
// Create TOTP seed and request a QR code // Create TOTP seed and request a QR code
const setupTotp = util.promisify(userHandler.setupTotp.bind(userHandler));
const enableTotp = util.promisify(userHandler.enableTotp.bind(userHandler));
const disableTotp = util.promisify(userHandler.disableTotp.bind(userHandler));
const checkTotp = util.promisify(userHandler.checkTotp.bind(userHandler));
const disable2fa = util.promisify(userHandler.disable2fa.bind(userHandler));
/** /**
* @api {post} /users/:user/2fa/totp/setup Generate TOTP seed * @api {post} /users/:user/2fa/totp/setup Generate TOTP seed
* @apiName SetupTotp2FA * @apiName SetupTotp2FA
@ -53,62 +62,63 @@ module.exports = (db, server, userHandler) => {
* "code": "UserNotFound" * "code": "UserNotFound"
* } * }
*/ */
server.post('/users/:user/2fa/totp/setup', (req, res, next) => { server.post(
res.charSet('utf-8'); '/users/:user/2fa/totp/setup',
const schema = Joi.object().keys({ tools.asyncifyJson(async (req, res, next) => {
user: Joi.string() res.charSet('utf-8');
.hex() const schema = Joi.object().keys({
.lowercase() user: Joi.string()
.length(24) .hex()
.required(), .lowercase()
label: Joi.string() .length(24)
.empty('') .required(),
.trim() label: Joi.string()
.max(255), .empty('')
issuer: Joi.string() .trim()
.trim() .max(255),
.max(255) issuer: Joi.string()
.required(), .trim()
sess: Joi.string().max(255), .max(255)
ip: Joi.string().ip({ .required(),
version: ['ipv4', 'ipv6'], sess: Joi.string().max(255),
cidr: 'forbidden' ip: Joi.string().ip({
}) version: ['ipv4', 'ipv6'],
}); cidr: 'forbidden'
})
const result = Joi.validate(req.params, schema, {
abortEarly: false,
convert: true
});
if (result.error) {
res.json({
error: result.error.message,
code: 'InputValidationError'
}); });
return next();
}
let user = new ObjectID(result.value.user); const result = Joi.validate(req.params, schema, {
abortEarly: false,
convert: true
});
userHandler.setupTotp(user, result.value, (err, result) => { if (result.error) {
if (err) {
res.json({ res.json({
error: err.message, error: result.error.message,
code: err.code code: 'InputValidationError'
}); });
return next(); return next();
} }
// permissions check
if (req.user && req.user === result.value.user) {
req.validate(roles.can(req.role).updateOwn('users'));
} else {
req.validate(roles.can(req.role).updateAny('users'));
}
let user = new ObjectID(result.value.user);
let totp = await setupTotp(user, result.value);
res.json({ res.json({
success: true, success: true,
seed: result.secret, seed: totp.secret,
qrcode: result.dataUrl qrcode: totp.dataUrl
}); });
return next(); return next();
}); })
}); );
/** /**
* @api {post} /users/:user/2fa/totp/enable Enable TOTP seed * @api {post} /users/:user/2fa/totp/enable Enable TOTP seed
@ -151,50 +161,51 @@ module.exports = (db, server, userHandler) => {
* "code": "UserNotFound" * "code": "UserNotFound"
* } * }
*/ */
server.post('/users/:user/2fa/totp/enable', (req, res, next) => { server.post(
res.charSet('utf-8'); '/users/:user/2fa/totp/enable',
tools.asyncifyJson(async (req, res, next) => {
res.charSet('utf-8');
const schema = Joi.object().keys({ const schema = Joi.object().keys({
user: Joi.string() user: Joi.string()
.hex() .hex()
.lowercase() .lowercase()
.length(24) .length(24)
.required(), .required(),
token: Joi.string() token: Joi.string()
.length(6) .length(6)
.required(), .required(),
sess: Joi.string().max(255), sess: Joi.string().max(255),
ip: Joi.string().ip({ ip: Joi.string().ip({
version: ['ipv4', 'ipv6'], version: ['ipv4', 'ipv6'],
cidr: 'forbidden' cidr: 'forbidden'
}) })
});
const result = Joi.validate(req.params, schema, {
abortEarly: false,
convert: true
});
if (result.error) {
res.json({
error: result.error.message,
code: 'InputValidationError'
}); });
return next();
}
let user = new ObjectID(result.value.user); const result = Joi.validate(req.params, schema, {
abortEarly: false,
convert: true
});
userHandler.enableTotp(user, result.value, (err, result) => { if (result.error) {
if (err) {
res.json({ res.json({
error: err.message, error: result.error.message,
code: err.code code: 'InputValidationError'
}); });
return next(); return next();
} }
if (!result) { // permissions check
if (req.user && req.user === result.value.user) {
req.validate(roles.can(req.role).updateOwn('users'));
} else {
req.validate(roles.can(req.role).updateAny('users'));
}
let user = new ObjectID(result.value.user);
let totp = await enableTotp(user, result.value);
if (!totp) {
res.json({ res.json({
error: 'Invalid authentication token', error: 'Invalid authentication token',
code: 'InvalidToken' code: 'InvalidToken'
@ -207,8 +218,8 @@ module.exports = (db, server, userHandler) => {
}); });
return next(); return next();
}); })
}); );
/** /**
* @api {delete} /users/:user/2fa/totp Disable TOTP auth * @api {delete} /users/:user/2fa/totp Disable TOTP auth
@ -245,55 +256,56 @@ module.exports = (db, server, userHandler) => {
* "code": "UserNotFound" * "code": "UserNotFound"
* } * }
*/ */
server.del('/users/:user/2fa/totp', (req, res, next) => { server.del(
res.charSet('utf-8'); '/users/:user/2fa/totp',
tools.asyncifyJson(async (req, res, next) => {
res.charSet('utf-8');
const schema = Joi.object().keys({ const schema = Joi.object().keys({
user: Joi.string() user: Joi.string()
.hex() .hex()
.lowercase() .lowercase()
.length(24) .length(24)
.required(), .required(),
sess: Joi.string().max(255), sess: Joi.string().max(255),
ip: Joi.string().ip({ ip: Joi.string().ip({
version: ['ipv4', 'ipv6'], version: ['ipv4', 'ipv6'],
cidr: 'forbidden' cidr: 'forbidden'
}) })
});
req.query.user = req.params.user;
const result = Joi.validate(req.query, schema, {
abortEarly: false,
convert: true
});
if (result.error) {
res.json({
error: result.error.message,
code: 'InputValidationError'
}); });
return next();
}
let user = new ObjectID(result.value.user); req.query.user = req.params.user;
userHandler.disableTotp(user, result.value, (err, success) => { const result = Joi.validate(req.query, schema, {
if (err) { abortEarly: false,
convert: true
});
if (result.error) {
res.json({ res.json({
error: err.message, error: result.error.message,
code: err.code code: 'InputValidationError'
}); });
return next(); return next();
} }
// permissions check
if (req.user && req.user === result.value.user) {
req.validate(roles.can(req.role).updateOwn('users'));
} else {
req.validate(roles.can(req.role).updateAny('users'));
}
let user = new ObjectID(result.value.user);
let success = await disableTotp(user, result.value);
res.json({ res.json({
success success
}); });
return next(); return next();
}); })
}); );
/** /**
* @api {post} /users/:user/2fa/totp/check Validate TOTP Token * @api {post} /users/:user/2fa/totp/check Validate TOTP Token
@ -336,50 +348,51 @@ module.exports = (db, server, userHandler) => {
* "code": "InvalidToken" * "code": "InvalidToken"
* } * }
*/ */
server.post('/users/:user/2fa/totp/check', (req, res, next) => { server.post(
res.charSet('utf-8'); '/users/:user/2fa/totp/check',
tools.asyncifyJson(async (req, res, next) => {
res.charSet('utf-8');
const schema = Joi.object().keys({ const schema = Joi.object().keys({
user: Joi.string() user: Joi.string()
.hex() .hex()
.lowercase() .lowercase()
.length(24) .length(24)
.required(), .required(),
token: Joi.string() token: Joi.string()
.length(6) .length(6)
.required(), .required(),
sess: Joi.string().max(255), sess: Joi.string().max(255),
ip: Joi.string().ip({ ip: Joi.string().ip({
version: ['ipv4', 'ipv6'], version: ['ipv4', 'ipv6'],
cidr: 'forbidden' cidr: 'forbidden'
}) })
});
const result = Joi.validate(req.params, schema, {
abortEarly: false,
convert: true
});
if (result.error) {
res.json({
error: result.error.message,
code: 'InputValidationError'
}); });
return next();
}
let user = new ObjectID(result.value.user); const result = Joi.validate(req.params, schema, {
abortEarly: false,
convert: true
});
userHandler.checkTotp(user, result.value, (err, result) => { if (result.error) {
if (err) {
res.json({ res.json({
error: err.message, error: result.error.message,
code: err.code code: 'InputValidationError'
}); });
return next(); return next();
} }
if (!result) { // permissions check
if (req.user && req.user === result.value.user) {
req.validate(roles.can(req.role).readOwn('users'));
} else {
req.validate(roles.can(req.role).readAny('users'));
}
let user = new ObjectID(result.value.user);
let totp = await checkTotp(user, result.value);
if (!totp) {
res.json({ res.json({
error: 'Failed to validate TOTP', error: 'Failed to validate TOTP',
code: 'InvalidToken' code: 'InvalidToken'
@ -392,8 +405,8 @@ module.exports = (db, server, userHandler) => {
}); });
return next(); return next();
}); })
}); );
/** /**
* @api {delete} /users/:user/2fa Disable 2FA * @api {delete} /users/:user/2fa Disable 2FA
@ -430,53 +443,54 @@ module.exports = (db, server, userHandler) => {
* "code": "UserNotFound" * "code": "UserNotFound"
* } * }
*/ */
server.del('/users/:user/2fa', (req, res, next) => { server.del(
res.charSet('utf-8'); '/users/:user/2fa',
tools.asyncifyJson(async (req, res, next) => {
res.charSet('utf-8');
const schema = Joi.object().keys({ const schema = Joi.object().keys({
user: Joi.string() user: Joi.string()
.hex() .hex()
.lowercase() .lowercase()
.length(24) .length(24)
.required(), .required(),
sess: Joi.string().max(255), sess: Joi.string().max(255),
ip: Joi.string().ip({ ip: Joi.string().ip({
version: ['ipv4', 'ipv6'], version: ['ipv4', 'ipv6'],
cidr: 'forbidden' cidr: 'forbidden'
}) })
});
req.query.user = req.params.user;
const result = Joi.validate(req.query, schema, {
abortEarly: false,
convert: true
});
if (result.error) {
res.json({
error: result.error.message,
code: 'InputValidationError'
}); });
return next();
}
let user = new ObjectID(result.value.user); req.query.user = req.params.user;
userHandler.disable2fa(user, result.value, (err, success) => { const result = Joi.validate(req.query, schema, {
if (err) { abortEarly: false,
convert: true
});
if (result.error) {
res.json({ res.json({
error: err.message, error: result.error.message,
code: err.code code: 'InputValidationError'
}); });
return next(); return next();
} }
// permissions check
if (req.user && req.user === result.value.user) {
req.validate(roles.can(req.role).updateOwn('users'));
} else {
req.validate(roles.can(req.role).updateAny('users'));
}
let user = new ObjectID(result.value.user);
let success = await disable2fa(user, result.value);
res.json({ res.json({
success success
}); });
return next(); return next();
}); })
}); );
}; };

View file

@ -2,6 +2,9 @@
const Joi = require('joi'); const Joi = require('joi');
const ObjectID = require('mongodb').ObjectID; const ObjectID = require('mongodb').ObjectID;
const tools = require('../../tools');
const roles = require('../../roles');
const util = require('util');
const U2F_ERROR_CODES = { const U2F_ERROR_CODES = {
OK: 0, OK: 0,
@ -21,129 +24,137 @@ const U2F_ERROR_MESSAGES = new Map([
]); ]);
module.exports = (db, server, userHandler) => { module.exports = (db, server, userHandler) => {
const setupU2f = util.promisify(userHandler.setupU2f.bind(userHandler));
const enableU2f = util.promisify(userHandler.enableU2f.bind(userHandler));
const disableU2f = util.promisify(userHandler.disableU2f.bind(userHandler));
const startU2f = util.promisify(userHandler.startU2f.bind(userHandler));
const checkU2f = util.promisify(userHandler.checkU2f.bind(userHandler));
// Create U2F keys // Create U2F keys
server.post('/users/:user/2fa/u2f/setup', (req, res, next) => { server.post(
res.charSet('utf-8'); '/users/:user/2fa/u2f/setup',
const schema = Joi.object().keys({ tools.asyncifyJson(async (req, res, next) => {
user: Joi.string() res.charSet('utf-8');
.hex() const schema = Joi.object().keys({
.lowercase() user: Joi.string()
.length(24) .hex()
.required(), .lowercase()
appId: Joi.string() .length(24)
.empty('') .required(),
.uri(), appId: Joi.string()
sess: Joi.string().max(255), .empty('')
ip: Joi.string().ip({ .uri(),
version: ['ipv4', 'ipv6'], sess: Joi.string().max(255),
cidr: 'forbidden' ip: Joi.string().ip({
}) version: ['ipv4', 'ipv6'],
}); cidr: 'forbidden'
})
const result = Joi.validate(req.params, schema, {
abortEarly: false,
convert: true
});
if (result.error) {
res.json({
error: result.error.message,
code: 'InputValidationError'
}); });
return next();
}
let user = new ObjectID(result.value.user); const result = Joi.validate(req.params, schema, {
abortEarly: false,
convert: true
});
userHandler.setupU2f(user, result.value, (err, u2fRegRequest) => { if (result.error) {
if (err) {
res.json({ res.json({
error: err.message, error: result.error.message,
code: err.code code: 'InputValidationError'
}); });
return next(); return next();
} }
// permissions check
if (req.user && req.user === result.value.user) {
req.validate(roles.can(req.role).updateOwn('users'));
} else {
req.validate(roles.can(req.role).updateAny('users'));
}
let user = new ObjectID(result.value.user);
let u2fRegRequest = await setupU2f(user, result.value);
res.json({ res.json({
success: true, success: true,
u2fRegRequest u2fRegRequest
}); });
return next(); return next();
}); })
}); );
// Send response from U2F key // Send response from U2F key
server.post('/users/:user/2fa/u2f/enable', (req, res, next) => { server.post(
res.charSet('utf-8'); '/users/:user/2fa/u2f/enable',
tools.asyncifyJson(async (req, res, next) => {
res.charSet('utf-8');
const schema = Joi.object().keys({ const schema = Joi.object().keys({
user: Joi.string() user: Joi.string()
.hex() .hex()
.lowercase() .lowercase()
.length(24) .length(24)
.required(), .required(),
errorCode: Joi.number().max(100), errorCode: Joi.number().max(100),
clientData: Joi.string() clientData: Joi.string()
.regex(/^[0-9a-z\-_]+$/i, 'web safe base64') .regex(/^[0-9a-z\-_]+$/i, 'web safe base64')
.max(10240), .max(10240),
registrationData: Joi.string() registrationData: Joi.string()
.regex(/^[0-9a-z\-_]+$/i, 'web safe base64') .regex(/^[0-9a-z\-_]+$/i, 'web safe base64')
.max(10240), .max(10240),
version: Joi.string().allow('U2F_V2'), version: Joi.string().allow('U2F_V2'),
challenge: Joi.string() challenge: Joi.string()
.regex(/^[0-9a-z\-_]+$/i, 'web safe base64') .regex(/^[0-9a-z\-_]+$/i, 'web safe base64')
.max(1024), .max(1024),
sess: Joi.string().max(255), sess: Joi.string().max(255),
ip: Joi.string().ip({ ip: Joi.string().ip({
version: ['ipv4', 'ipv6'], version: ['ipv4', 'ipv6'],
cidr: 'forbidden' cidr: 'forbidden'
}) })
});
const result = Joi.validate(req.params, schema, {
abortEarly: false,
convert: true
});
if (result.error) {
res.json({
error: result.error.message,
code: 'InputValidationError'
});
return next();
}
if (result.value.errorCode) {
let error;
switch (result.value.errorCode) {
case U2F_ERROR_CODES.DEVICE_INELIGIBLE:
error = 'U2F token is already registered';
break;
default:
error = U2F_ERROR_MESSAGES.get(result.value.errorCode) || 'Unknown error code' + result.value.errorCode;
}
res.json({
error
}); });
return next(); const result = Joi.validate(req.params, schema, {
} abortEarly: false,
convert: true
});
let user = new ObjectID(result.value.user); if (result.error) {
userHandler.enableU2f(user, result.value, (err, result) => {
if (err) {
res.json({ res.json({
error: err.message, error: result.error.message,
code: err.code code: 'InputValidationError'
}); });
return next(); return next();
} }
if (!result) { if (result.value.errorCode) {
let error;
switch (result.value.errorCode) {
case U2F_ERROR_CODES.DEVICE_INELIGIBLE:
error = 'U2F token is already registered';
break;
default:
error = U2F_ERROR_MESSAGES.get(result.value.errorCode) || 'Unknown error code' + result.value.errorCode;
}
res.json({
error
});
return next();
}
// permissions check
if (req.user && req.user === result.value.user) {
req.validate(roles.can(req.role).updateOwn('users'));
} else {
req.validate(roles.can(req.role).updateAny('users'));
}
let user = new ObjectID(result.value.user);
let u2f = await enableU2f(user, result.value);
if (!u2f) {
res.json({ res.json({
error: 'Failed to enable U2F', error: 'Failed to enable U2F',
code: 'U2fEnableFailed' code: 'U2fEnableFailed'
@ -156,53 +167,53 @@ module.exports = (db, server, userHandler) => {
}); });
return next(); return next();
}); })
}); );
// Disable U2F auth for an user // Disable U2F auth for an user
server.del('/users/:user/2fa/u2f', (req, res, next) => { server.del(
res.charSet('utf-8'); '/users/:user/2fa/u2f',
tools.asyncifyJson(async (req, res, next) => {
res.charSet('utf-8');
const schema = Joi.object().keys({ const schema = Joi.object().keys({
user: Joi.string() user: Joi.string()
.hex() .hex()
.lowercase() .lowercase()
.length(24) .length(24)
.required(), .required(),
sess: Joi.string().max(255), sess: Joi.string().max(255),
ip: Joi.string().ip({ ip: Joi.string().ip({
version: ['ipv4', 'ipv6'], version: ['ipv4', 'ipv6'],
cidr: 'forbidden' cidr: 'forbidden'
}) })
});
req.query.user = req.params.user;
const result = Joi.validate(req.query, schema, {
abortEarly: false,
convert: true
});
if (result.error) {
res.json({
error: result.error.message,
code: 'InputValidationError'
}); });
return next();
}
let user = new ObjectID(result.value.user); req.query.user = req.params.user;
userHandler.disableU2f(user, result.value, (err, result) => { const result = Joi.validate(req.query, schema, {
if (err) { abortEarly: false,
convert: true
});
if (result.error) {
res.json({ res.json({
error: err.message, error: result.error.message,
code: err.code code: 'InputValidationError'
}); });
return next(); return next();
} }
if (!result) { // permissions check
if (req.user && req.user === result.value.user) {
req.validate(roles.can(req.role).updateOwn('users'));
} else {
req.validate(roles.can(req.role).updateAny('users'));
}
let user = new ObjectID(result.value.user);
let u2f = await disableU2f(user, result.value);
if (!u2f) {
res.json({ res.json({
error: 'Failed to disable U2F', error: 'Failed to disable U2F',
code: 'U2fDisableFailed' code: 'U2fDisableFailed'
@ -215,53 +226,53 @@ module.exports = (db, server, userHandler) => {
}); });
return next(); return next();
}); })
}); );
// Generate U2F Authentciation Request // Generate U2F Authentciation Request
server.post('/users/:user/2fa/u2f/start', (req, res, next) => { server.post(
res.charSet('utf-8'); '/users/:user/2fa/u2f/start',
tools.asyncifyJson(async (req, res, next) => {
res.charSet('utf-8');
const schema = Joi.object().keys({ const schema = Joi.object().keys({
user: Joi.string() user: Joi.string()
.hex() .hex()
.lowercase() .lowercase()
.length(24) .length(24)
.required(), .required(),
appId: Joi.string() appId: Joi.string()
.empty('') .empty('')
.uri(), .uri(),
sess: Joi.string().max(255), sess: Joi.string().max(255),
ip: Joi.string().ip({ ip: Joi.string().ip({
version: ['ipv4', 'ipv6'], version: ['ipv4', 'ipv6'],
cidr: 'forbidden' cidr: 'forbidden'
}) })
});
const result = Joi.validate(req.params, schema, {
abortEarly: false,
convert: true
});
if (result.error) {
res.json({
error: result.error.message,
code: 'InputValidationError'
}); });
return next();
}
let user = new ObjectID(result.value.user); const result = Joi.validate(req.params, schema, {
abortEarly: false,
convert: true
});
userHandler.startU2f(user, result.value, (err, u2fAuthRequest) => { if (result.error) {
if (err) {
res.json({ res.json({
error: err.message, error: result.error.message,
code: err.code code: 'InputValidationError'
}); });
return next(); return next();
} }
// permissions check
if (req.user && req.user === result.value.user) {
req.validate(roles.can(req.role).readOwn('users'));
} else {
req.validate(roles.can(req.role).readAny('users'));
}
let user = new ObjectID(result.value.user);
let u2fAuthRequest = await startU2f(user, result.value);
if (!result) { if (!result) {
res.json({ res.json({
error: 'Failed to generate authentication request for U2F', error: 'Failed to generate authentication request for U2F',
@ -276,76 +287,76 @@ module.exports = (db, server, userHandler) => {
}); });
return next(); return next();
}); })
}); );
// Send response from U2F key // Send response from U2F key
server.post('/users/:user/2fa/u2f/check', (req, res, next) => { server.post(
res.charSet('utf-8'); '/users/:user/2fa/u2f/check',
tools.asyncifyJson(async (req, res, next) => {
res.charSet('utf-8');
const schema = Joi.object().keys({ const schema = Joi.object().keys({
user: Joi.string() user: Joi.string()
.hex() .hex()
.lowercase() .lowercase()
.length(24) .length(24)
.required(), .required(),
errorCode: Joi.number().max(100), errorCode: Joi.number().max(100),
clientData: Joi.string() clientData: Joi.string()
.regex(/^[0-9a-z\-_]+$/i, 'web safe base64') .regex(/^[0-9a-z\-_]+$/i, 'web safe base64')
.max(10240), .max(10240),
signatureData: Joi.string() signatureData: Joi.string()
.regex(/^[0-9a-z\-_]+$/i, 'web safe base64') .regex(/^[0-9a-z\-_]+$/i, 'web safe base64')
.max(10240), .max(10240),
sess: Joi.string().max(255), sess: Joi.string().max(255),
ip: Joi.string().ip({ ip: Joi.string().ip({
version: ['ipv4', 'ipv6'], version: ['ipv4', 'ipv6'],
cidr: 'forbidden' cidr: 'forbidden'
}) })
});
const result = Joi.validate(req.params, schema, {
abortEarly: false,
convert: true
});
if (result.error) {
res.json({
error: result.error.message,
code: 'InputValidationError'
});
return next();
}
if (result.value.errorCode) {
let error;
switch (result.value.errorCode) {
case U2F_ERROR_CODES.DEVICE_INELIGIBLE:
error = 'U2F token is not registered';
break;
default:
error = U2F_ERROR_MESSAGES.get(result.value.errorCode) || 'Unknown error code' + result.value.errorCode;
}
res.json({
error
}); });
return next(); const result = Joi.validate(req.params, schema, {
} abortEarly: false,
convert: true
});
let user = new ObjectID(result.value.user); if (result.error) {
userHandler.checkU2f(user, result.value, (err, result) => {
if (err) {
res.json({ res.json({
error: err.message, error: result.error.message,
code: err.code code: 'InputValidationError'
}); });
return next(); return next();
} }
if (!result) { if (result.value.errorCode) {
let error;
switch (result.value.errorCode) {
case U2F_ERROR_CODES.DEVICE_INELIGIBLE:
error = 'U2F token is not registered';
break;
default:
error = U2F_ERROR_MESSAGES.get(result.value.errorCode) || 'Unknown error code' + result.value.errorCode;
}
res.json({
error
});
return next();
}
// permissions check
if (req.user && req.user === result.value.user) {
req.validate(roles.can(req.role).updateOwn('users'));
} else {
req.validate(roles.can(req.role).updateAny('users'));
}
let user = new ObjectID(result.value.user);
let u2f = await checkU2f(user, result.value);
if (!u2f) {
res.json({ res.json({
error: 'Failed to validate U2F request', error: 'Failed to validate U2F request',
code: 'U2fFail' code: 'U2fFail'
@ -358,6 +369,6 @@ module.exports = (db, server, userHandler) => {
}); });
return next(); return next();
}); })
}); );
}; };