mirror of
https://github.com/nodemailer/wildduck.git
synced 2025-09-15 09:34:33 +08:00
some access control
This commit is contained in:
parent
beca0b1e33
commit
1ad43a267e
7 changed files with 439 additions and 206 deletions
143
api.js
143
api.js
|
@ -10,8 +10,8 @@ const MessageHandler = require('./lib/message-handler');
|
|||
const ImapNotifier = require('./lib/imap-notifier');
|
||||
const db = require('./lib/db');
|
||||
const certs = require('./lib/certs');
|
||||
const ObjectID = require('mongodb').ObjectID;
|
||||
const rootUser = new ObjectID('0'.repeat(24));
|
||||
const tools = require('./lib/tools');
|
||||
const crypto = require('crypto');
|
||||
|
||||
const usersRoutes = require('./lib/api/users');
|
||||
const addressesRoutes = require('./lib/api/addresses');
|
||||
|
@ -80,56 +80,111 @@ server.use(
|
|||
})
|
||||
);
|
||||
|
||||
server.use((req, res, next) => {
|
||||
let accessToken = req.query.accessToken || req.headers['x-access-token'] || false;
|
||||
if (req.query.accessToken) {
|
||||
delete req.query.accessToken;
|
||||
}
|
||||
server.use(
|
||||
tools.asyncifyJson(async (req, res, next) => {
|
||||
let accessToken = req.query.accessToken || req.headers['x-access-token'] || false;
|
||||
|
||||
let tokenRequired = false;
|
||||
|
||||
let fail = () => {
|
||||
res.status(403);
|
||||
res.charSet('utf-8');
|
||||
return res.json({
|
||||
error: 'Invalid accessToken value',
|
||||
code: 'InvalidToken'
|
||||
});
|
||||
};
|
||||
|
||||
req.validate = permission => {
|
||||
if (!permission.granted) {
|
||||
let err = new Error('Not enough privileges');
|
||||
err.responseCode = 403;
|
||||
err.code = 'MissingPrivileges';
|
||||
throw err;
|
||||
if (req.query.accessToken) {
|
||||
req.query.accessToken = '';
|
||||
}
|
||||
};
|
||||
|
||||
// hard coded master token
|
||||
if (config.api.accessToken) {
|
||||
tokenRequired = true;
|
||||
if (config.api.accessToken === accessToken) {
|
||||
req.role = 'root';
|
||||
req.user = rootUser;
|
||||
return next();
|
||||
if (req.headers['x-access-token']) {
|
||||
req.headers['x-access-token'] = '';
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: dynamically allocated tokens
|
||||
let tokenRequired = false;
|
||||
|
||||
if (tokenRequired) {
|
||||
// no valid token found
|
||||
return fail();
|
||||
}
|
||||
let fail = () => {
|
||||
res.status(403);
|
||||
res.charSet('utf-8');
|
||||
return res.json({
|
||||
error: 'Invalid accessToken value',
|
||||
code: 'InvalidToken'
|
||||
});
|
||||
};
|
||||
|
||||
// allow all
|
||||
req.role = 'root';
|
||||
req.user = rootUser;
|
||||
next();
|
||||
});
|
||||
req.validate = permission => {
|
||||
if (!permission.granted) {
|
||||
let err = new Error('Not enough privileges');
|
||||
err.responseCode = 403;
|
||||
err.code = 'MissingPrivileges';
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
logger.token('user', req => (req.user && req.user.toString()) || '?'.repeat(24));
|
||||
// hard coded master token
|
||||
if (config.api.accessToken) {
|
||||
tokenRequired = true;
|
||||
if (config.api.accessToken === accessToken) {
|
||||
req.role = 'root';
|
||||
req.user = 'root';
|
||||
return next();
|
||||
}
|
||||
}
|
||||
|
||||
if (config.api.accessControl.enabled) {
|
||||
tokenRequired = true;
|
||||
if (accessToken && accessToken.length === 40 && /^[a-fA-F0-9]{40}$/.test(accessToken)) {
|
||||
let tokenData;
|
||||
let tokenHash = crypto
|
||||
.createHash('sha256')
|
||||
.update(accessToken)
|
||||
.digest('hex');
|
||||
|
||||
try {
|
||||
let key = 'tn:token:' + tokenHash;
|
||||
tokenData = await db.redis.hgetall(key);
|
||||
} catch (err) {
|
||||
err.responseCode = 500;
|
||||
err.code = 'InternalDatabaseError';
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (tokenData && tokenData.user && tokenData.role && config.api.roles[tokenData.role]) {
|
||||
let signature = crypto
|
||||
.createHmac('sha256', config.api.accessControl.secret)
|
||||
.update(
|
||||
JSON.stringify({
|
||||
token: accessToken,
|
||||
user: tokenData.user,
|
||||
role: tokenData.role
|
||||
})
|
||||
)
|
||||
.digest('hex');
|
||||
|
||||
if (signature !== tokenData.s) {
|
||||
// rogue token
|
||||
try {
|
||||
await db.redis
|
||||
.multi()
|
||||
.del('tn:token:' + tokenHash)
|
||||
.srem('tn:user:' + tokenData.user, tokenHash)
|
||||
.exec();
|
||||
} catch (err) {
|
||||
// ignore
|
||||
}
|
||||
} else {
|
||||
req.role = tokenData.role;
|
||||
req.user = tokenData.user;
|
||||
}
|
||||
return next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tokenRequired) {
|
||||
// no valid token found
|
||||
return fail();
|
||||
}
|
||||
|
||||
// allow all
|
||||
req.role = 'root';
|
||||
req.user = 'root';
|
||||
next();
|
||||
})
|
||||
);
|
||||
|
||||
logger.token('user', req => (req.user && req.user.toString()) || '-');
|
||||
logger.token('url', req => {
|
||||
if (/\baccessToken=/.test(req.url)) {
|
||||
return req.url.replace(/\baccessToken=[^&]+/g, 'accessToken=' + 'x'.repeat(6));
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
/* eslint no-console: 0*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// FUTURE FEATURE
|
||||
|
@ -7,17 +9,141 @@
|
|||
|
||||
//const config = require('wild-config');
|
||||
const db = require('../lib/db');
|
||||
const errors = require('../lib/errors');
|
||||
const log = require('npmlog');
|
||||
const crypto = require('crypto');
|
||||
const config = require('wild-config');
|
||||
const yargs = require('yargs');
|
||||
const util = require('util');
|
||||
|
||||
// Initialize database connection
|
||||
db.connect(err => {
|
||||
if (err) {
|
||||
log.error('Db', 'Failed to setup database connection');
|
||||
errors.notify(err);
|
||||
return setTimeout(() => process.exit(1), 3000);
|
||||
}
|
||||
const dbconnect = util.promisify(db.connect);
|
||||
|
||||
log.info('Future feature');
|
||||
process.exit();
|
||||
});
|
||||
let argv = yargs
|
||||
.usage('Usage: $0 <command> [options]')
|
||||
.command(
|
||||
'provision <user> <role>',
|
||||
'Provision new access token',
|
||||
yargs =>
|
||||
yargs
|
||||
.option('user', {
|
||||
alias: 'u',
|
||||
describe: 'Username',
|
||||
demandOption: true
|
||||
})
|
||||
.option('role', {
|
||||
alias: 'r',
|
||||
describe: 'Choose role',
|
||||
choices: Object.keys(config.api.roles),
|
||||
demandOption: true
|
||||
}),
|
||||
async argv => {
|
||||
await dbconnect();
|
||||
|
||||
let accessToken = crypto.randomBytes(20).toString('hex');
|
||||
let tokenHash = crypto
|
||||
.createHash('sha256')
|
||||
.update(accessToken)
|
||||
.digest('hex');
|
||||
let key = 'tn:token:' + tokenHash;
|
||||
|
||||
let tokenData = {
|
||||
user: argv.user,
|
||||
role: argv.role,
|
||||
created: Date.now(),
|
||||
// signature
|
||||
s: crypto
|
||||
.createHmac('sha256', config.api.accessControl.secret)
|
||||
.update(
|
||||
JSON.stringify({
|
||||
token: accessToken,
|
||||
user: argv.user,
|
||||
role: argv.role
|
||||
})
|
||||
)
|
||||
.digest('hex')
|
||||
};
|
||||
|
||||
await db.redis
|
||||
.multi()
|
||||
.hmset(key, tokenData)
|
||||
.sadd('tn:user:' + argv.user, tokenHash)
|
||||
.exec();
|
||||
|
||||
console.error('Generated access token for %s[%s]:', tokenData.user, tokenData.role);
|
||||
|
||||
process.stdout.write(accessToken);
|
||||
|
||||
console.error('');
|
||||
|
||||
process.exit();
|
||||
}
|
||||
)
|
||||
.command(
|
||||
'delete <token>',
|
||||
'Delete existing access token',
|
||||
yargs =>
|
||||
yargs.option('token', {
|
||||
alias: 't',
|
||||
describe: 'Access token',
|
||||
demandOption: true
|
||||
}),
|
||||
async argv => {
|
||||
await dbconnect();
|
||||
|
||||
let accessToken = argv.token;
|
||||
|
||||
let tokenHash = crypto
|
||||
.createHash('sha256')
|
||||
.update(accessToken)
|
||||
.digest('hex');
|
||||
|
||||
let key = 'tn:token:' + tokenHash;
|
||||
|
||||
let tokenData = await db.redis.hgetall(key);
|
||||
if (tokenData) {
|
||||
await db.redis.del(key);
|
||||
if (tokenData.user) {
|
||||
await db.redis.srem('tn:user:' + tokenData.user, tokenHash);
|
||||
}
|
||||
console.error('Token deleted');
|
||||
} else {
|
||||
console.error('Token not found');
|
||||
}
|
||||
|
||||
process.exit();
|
||||
}
|
||||
)
|
||||
.command(
|
||||
'clear <user>',
|
||||
'Delete all tokens for an user',
|
||||
yargs =>
|
||||
yargs.option('user', {
|
||||
alias: 'u',
|
||||
describe: 'User to de-provision',
|
||||
demandOption: true
|
||||
}),
|
||||
async argv => {
|
||||
await dbconnect();
|
||||
|
||||
let user = argv.user;
|
||||
let tokens = await db.redis.smembers('tn:user:' + user);
|
||||
if (!tokens || !tokens.length) {
|
||||
console.error('No tokens found for %s', user);
|
||||
process.exit();
|
||||
}
|
||||
|
||||
let query = db.redis.multi().del('tn:user:' + user);
|
||||
|
||||
tokens.forEach(tokenHash => {
|
||||
query = query.del('tn:token:' + tokenHash);
|
||||
});
|
||||
|
||||
await query.exec();
|
||||
|
||||
console.error('Deleted %s tokens for %s', tokens.length, user);
|
||||
|
||||
process.exit();
|
||||
}
|
||||
)
|
||||
.help().argv;
|
||||
if (argv) {
|
||||
// ignore
|
||||
}
|
||||
|
|
|
@ -13,11 +13,9 @@ secure=false
|
|||
[accessControl]
|
||||
# If true then require a valid access token to perform API calls
|
||||
enabled=false
|
||||
# If enabled then encrypt access tokens with the secret password. By default the tokens
|
||||
# are not encrypted and stored as cleartext. Once set up do not change these values,
|
||||
# otherwise decrypting tokens is going to fail
|
||||
#cipher="aes192"
|
||||
#secret="a secret cat"
|
||||
# Secret for HMAC
|
||||
# Changing this value invalidates all tokens
|
||||
secret="a secret cat"
|
||||
|
||||
[roles]
|
||||
# @include "roles.json"
|
||||
|
|
|
@ -5,6 +5,17 @@
|
|||
"read:any": ["*"],
|
||||
"update:any": ["*"],
|
||||
"delete:any": ["*"]
|
||||
},
|
||||
|
||||
"authentication": {
|
||||
"create:any": ["*"],
|
||||
"read:any": ["*"]
|
||||
}
|
||||
},
|
||||
|
||||
"auth": {
|
||||
"authentication": {
|
||||
"create:any": ["*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,8 +75,6 @@ module.exports = (db, server) => {
|
|||
server.get(
|
||||
{ name: 'addresses', path: '/addresses' },
|
||||
tools.asyncifyJson(async (req, res, next) => {
|
||||
req.validate(roles.can(req.role).readAny('addresses'));
|
||||
|
||||
res.charSet('utf-8');
|
||||
|
||||
const schema = Joi.object().keys({
|
||||
|
@ -121,6 +119,9 @@ module.exports = (db, server) => {
|
|||
return next();
|
||||
}
|
||||
|
||||
// permissions check
|
||||
req.validate(roles.can(req.role).readAny('addresses'));
|
||||
|
||||
let query = result.value.query;
|
||||
let limit = result.value.limit;
|
||||
let page = result.value.page;
|
||||
|
@ -335,7 +336,7 @@ module.exports = (db, server) => {
|
|||
let user = new ObjectID(result.value.user);
|
||||
|
||||
// permissions check
|
||||
if (req.user && req.user.equals(user)) {
|
||||
if (req.user && req.user === result.value.user) {
|
||||
req.validate(roles.can(req.role).createOwn('addresses'));
|
||||
} else {
|
||||
req.validate(roles.can(req.role).createAny('addresses'));
|
||||
|
@ -575,7 +576,7 @@ module.exports = (db, server) => {
|
|||
let user = new ObjectID(result.value.user);
|
||||
|
||||
// permissions check
|
||||
if (req.user && req.user.equals(user)) {
|
||||
if (req.user && req.user === result.value.user) {
|
||||
req.validate(roles.can(req.role).readOwn('addresses'));
|
||||
} else {
|
||||
req.validate(roles.can(req.role).readAny('addresses'));
|
||||
|
@ -726,7 +727,7 @@ module.exports = (db, server) => {
|
|||
let user = new ObjectID(result.value.user);
|
||||
|
||||
// permissions check
|
||||
if (req.user && req.user.equals(user)) {
|
||||
if (req.user && req.user === result.value.user) {
|
||||
req.validate(roles.can(req.role).readOwn('addresses'));
|
||||
} else {
|
||||
req.validate(roles.can(req.role).readAny('addresses'));
|
||||
|
@ -886,7 +887,7 @@ module.exports = (db, server) => {
|
|||
let user = new ObjectID(result.value.user);
|
||||
|
||||
// permissions check
|
||||
if (req.user && req.user.equals(user)) {
|
||||
if (req.user && req.user === result.value.user) {
|
||||
req.validate(roles.can(req.role).updateOwn('addresses'));
|
||||
} else {
|
||||
req.validate(roles.can(req.role).updateAny('addresses'));
|
||||
|
@ -1140,7 +1141,7 @@ module.exports = (db, server) => {
|
|||
let user = new ObjectID(result.value.user);
|
||||
|
||||
// permissions check
|
||||
if (req.user && req.user.equals(user)) {
|
||||
if (req.user && req.user === result.value.user) {
|
||||
req.validate(roles.can(req.role).deleteOwn('addresses'));
|
||||
} else {
|
||||
req.validate(roles.can(req.role).deleteAny('addresses'));
|
||||
|
|
315
lib/api/auth.js
315
lib/api/auth.js
|
@ -4,8 +4,20 @@ const Joi = require('../joi');
|
|||
const MongoPaging = require('mongo-cursor-pagination');
|
||||
const ObjectID = require('mongodb').ObjectID;
|
||||
const tools = require('../tools');
|
||||
const util = require('util');
|
||||
const roles = require('../roles');
|
||||
|
||||
module.exports = (db, server, userHandler) => {
|
||||
const authenticate = util.promisify((...args) => {
|
||||
let callback = args.pop();
|
||||
userHandler.authenticate(...args, (err, authData, user) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
return callback({ authData, user });
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* @api {post} /authenticate Authenticate an User
|
||||
* @apiName PostAuth
|
||||
|
@ -64,63 +76,73 @@ module.exports = (db, server, userHandler) => {
|
|||
* "id": "5b22283d45e8d47572eb0381"
|
||||
* }
|
||||
*/
|
||||
server.post('/authenticate', (req, res, next) => {
|
||||
res.charSet('utf-8');
|
||||
server.post(
|
||||
'/authenticate',
|
||||
tools.asyncifyJson(async (req, res, next) => {
|
||||
res.charSet('utf-8');
|
||||
|
||||
const schema = Joi.object().keys({
|
||||
username: Joi.alternatives()
|
||||
.try(
|
||||
Joi.string()
|
||||
.lowercase()
|
||||
.regex(/^[a-z0-9][a-z0-9.]+[a-z0-9]$/, 'username')
|
||||
.min(3)
|
||||
.max(30),
|
||||
Joi.string().email()
|
||||
)
|
||||
.required(),
|
||||
password: Joi.string()
|
||||
.max(256)
|
||||
.required(),
|
||||
const schema = Joi.object().keys({
|
||||
username: Joi.alternatives()
|
||||
.try(
|
||||
Joi.string()
|
||||
.lowercase()
|
||||
.regex(/^[a-z0-9][a-z0-9.]+[a-z0-9]$/, 'username')
|
||||
.min(3)
|
||||
.max(30),
|
||||
Joi.string().email()
|
||||
)
|
||||
.required(),
|
||||
password: Joi.string()
|
||||
.max(256)
|
||||
.required(),
|
||||
|
||||
protocol: Joi.string().default('API'),
|
||||
scope: Joi.string().default('master'),
|
||||
protocol: Joi.string().default('API'),
|
||||
scope: Joi.string().default('master'),
|
||||
|
||||
appId: Joi.string()
|
||||
.empty('')
|
||||
.uri(),
|
||||
appId: Joi.string()
|
||||
.empty('')
|
||||
.uri(),
|
||||
|
||||
sess: Joi.string().max(255),
|
||||
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'
|
||||
sess: Joi.string().max(255),
|
||||
ip: Joi.string().ip({
|
||||
version: ['ipv4', 'ipv6'],
|
||||
cidr: 'forbidden'
|
||||
})
|
||||
});
|
||||
return next();
|
||||
}
|
||||
|
||||
let meta = {
|
||||
protocol: result.value.protocol,
|
||||
sess: result.value.sess,
|
||||
ip: result.value.ip
|
||||
};
|
||||
const result = Joi.validate(req.params, schema, {
|
||||
abortEarly: false,
|
||||
convert: true
|
||||
});
|
||||
|
||||
if (result.value.appId) {
|
||||
meta.appId = result.value.appId;
|
||||
}
|
||||
if (result.error) {
|
||||
res.json({
|
||||
error: result.error.message,
|
||||
code: 'InputValidationError'
|
||||
});
|
||||
return next();
|
||||
}
|
||||
|
||||
userHandler.authenticate(result.value.username, result.value.password, result.value.scope, meta, (err, authData, user) => {
|
||||
if (err) {
|
||||
// permissions check
|
||||
req.validate(roles.can(req.role).createAny('authentication'));
|
||||
|
||||
let meta = {
|
||||
protocol: result.value.protocol,
|
||||
sess: result.value.sess,
|
||||
ip: result.value.ip
|
||||
};
|
||||
|
||||
if (result.value.appId) {
|
||||
meta.appId = result.value.appId;
|
||||
}
|
||||
|
||||
let authData, user;
|
||||
|
||||
try {
|
||||
let auth = await authenticate(result.value.username, result.value.password, result.value.scope, meta);
|
||||
authData = auth.authData;
|
||||
user = auth.user;
|
||||
} catch (err) {
|
||||
let response = {
|
||||
error: err.message
|
||||
};
|
||||
|
@ -162,8 +184,8 @@ module.exports = (db, server, userHandler) => {
|
|||
res.json(authResponse);
|
||||
|
||||
return next();
|
||||
});
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
/**
|
||||
* @api {get} /users/:user/authlog List authentication Events
|
||||
|
@ -283,6 +305,13 @@ module.exports = (db, server, userHandler) => {
|
|||
return next();
|
||||
}
|
||||
|
||||
// permissions check
|
||||
if (req.user && req.user === result.value.user) {
|
||||
req.validate(roles.can(req.role).readOwn('authentication'));
|
||||
} else {
|
||||
req.validate(roles.can(req.role).readAny('authentication'));
|
||||
}
|
||||
|
||||
let user = new ObjectID(result.value.user);
|
||||
let limit = result.value.limit;
|
||||
|
||||
|
@ -424,96 +453,108 @@ module.exports = (db, server, userHandler) => {
|
|||
* "error": "Database error"
|
||||
* }
|
||||
*/
|
||||
server.get('/users/:user/authlog/:event', (req, res, next) => {
|
||||
res.charSet('utf-8');
|
||||
server.get(
|
||||
'/users/:user/authlog/:event',
|
||||
tools.asyncifyJson(async (req, res, next) => {
|
||||
res.charSet('utf-8');
|
||||
|
||||
const schema = Joi.object().keys({
|
||||
user: Joi.string()
|
||||
.hex()
|
||||
.lowercase()
|
||||
.length(24)
|
||||
.required(),
|
||||
event: Joi.string()
|
||||
.hex()
|
||||
.lowercase()
|
||||
.length(24)
|
||||
.required()
|
||||
});
|
||||
|
||||
const result = Joi.validate(req.params, schema, {
|
||||
abortEarly: false,
|
||||
convert: true,
|
||||
allowUnknown: false
|
||||
});
|
||||
|
||||
if (result.error) {
|
||||
res.json({
|
||||
error: result.error.message,
|
||||
code: 'InputValidationError'
|
||||
const schema = Joi.object().keys({
|
||||
user: Joi.string()
|
||||
.hex()
|
||||
.lowercase()
|
||||
.length(24)
|
||||
.required(),
|
||||
event: Joi.string()
|
||||
.hex()
|
||||
.lowercase()
|
||||
.length(24)
|
||||
.required()
|
||||
});
|
||||
return next();
|
||||
}
|
||||
|
||||
let user = new ObjectID(result.value.user);
|
||||
let event = new ObjectID(result.value.event);
|
||||
const result = Joi.validate(req.params, schema, {
|
||||
abortEarly: false,
|
||||
convert: true,
|
||||
allowUnknown: false
|
||||
});
|
||||
|
||||
db.users.collection('users').findOne(
|
||||
{
|
||||
_id: user
|
||||
},
|
||||
{
|
||||
projection: {
|
||||
_id: true
|
||||
}
|
||||
},
|
||||
(err, userData) => {
|
||||
if (err) {
|
||||
res.json({
|
||||
error: 'MongoDB Error: ' + err.message,
|
||||
code: 'InternalDatabaseError'
|
||||
});
|
||||
return next();
|
||||
}
|
||||
if (!userData) {
|
||||
res.json({
|
||||
error: 'This user does not exist',
|
||||
code: 'UserNotFound'
|
||||
});
|
||||
return next();
|
||||
}
|
||||
|
||||
let filter = { _id: event, user };
|
||||
|
||||
db.database.collection('authlog').findOne(filter, (err, eventData) => {
|
||||
if (err) {
|
||||
res.json({
|
||||
error: err.message
|
||||
});
|
||||
return next();
|
||||
}
|
||||
|
||||
if (!eventData) {
|
||||
res.json({
|
||||
error: 'Event was not found',
|
||||
code: 'EventNotFound'
|
||||
});
|
||||
return next();
|
||||
}
|
||||
|
||||
let response = {
|
||||
success: true,
|
||||
id: eventData._id
|
||||
};
|
||||
Object.keys(eventData).forEach(key => {
|
||||
if (!['_id', 'user'].includes(key)) {
|
||||
response[key] = eventData[key];
|
||||
}
|
||||
});
|
||||
|
||||
res.json(response);
|
||||
return next();
|
||||
if (result.error) {
|
||||
res.json({
|
||||
error: result.error.message,
|
||||
code: 'InputValidationError'
|
||||
});
|
||||
return next();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// permissions check
|
||||
if (req.user && req.user === result.value.user) {
|
||||
req.validate(roles.can(req.role).readOwn('authentication'));
|
||||
} else {
|
||||
req.validate(roles.can(req.role).readAny('authentication'));
|
||||
}
|
||||
|
||||
let user = new ObjectID(result.value.user);
|
||||
let event = new ObjectID(result.value.event);
|
||||
|
||||
let userData;
|
||||
try {
|
||||
userData = await db.users.collection('users').findOne(
|
||||
{
|
||||
_id: user
|
||||
},
|
||||
{
|
||||
projection: {
|
||||
_id: true
|
||||
}
|
||||
}
|
||||
);
|
||||
} catch (err) {
|
||||
res.json({
|
||||
error: 'MongoDB Error: ' + err.message,
|
||||
code: 'InternalDatabaseError'
|
||||
});
|
||||
return next();
|
||||
}
|
||||
|
||||
if (!userData) {
|
||||
res.json({
|
||||
error: 'This user does not exist',
|
||||
code: 'UserNotFound'
|
||||
});
|
||||
return next();
|
||||
}
|
||||
|
||||
let filter = { _id: event, user };
|
||||
let eventData;
|
||||
try {
|
||||
eventData = await db.database.collection('authlog').findOne(filter);
|
||||
} catch (err) {
|
||||
res.json({
|
||||
error: 'MongoDB Error: ' + err.message,
|
||||
code: 'InternalDatabaseError'
|
||||
});
|
||||
return next();
|
||||
}
|
||||
|
||||
if (!eventData) {
|
||||
res.json({
|
||||
error: 'Event was not found',
|
||||
code: 'EventNotFound'
|
||||
});
|
||||
return next();
|
||||
}
|
||||
|
||||
let response = {
|
||||
success: true,
|
||||
id: eventData._id
|
||||
};
|
||||
Object.keys(eventData).forEach(key => {
|
||||
if (!['_id', 'user'].includes(key)) {
|
||||
response[key] = eventData[key];
|
||||
}
|
||||
});
|
||||
|
||||
res.json(response);
|
||||
return next();
|
||||
})
|
||||
);
|
||||
};
|
||||
|
|
|
@ -75,7 +75,8 @@
|
|||
"u2f": "0.1.3",
|
||||
"utf7": "1.0.2",
|
||||
"uuid": "3.3.2",
|
||||
"wild-config": "1.3.6"
|
||||
"wild-config": "1.3.6",
|
||||
"yargs": "^12.0.1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
Loading…
Add table
Reference in a new issue