asp handling update

This commit is contained in:
Andris Reinman 2018-10-11 09:44:21 +03:00
parent dde325037a
commit f6797583fe
2 changed files with 444 additions and 405 deletions

View file

@ -19,6 +19,12 @@
"delete:any": ["*"]
},
"asps": {
"create:any": ["*"],
"read:any": ["*"],
"delete:any": ["*"]
},
"messages": {
"create:any": ["*"],
"read:any": ["*"],
@ -58,6 +64,12 @@
"update:own": ["*", "!audit"]
},
"asps": {
"create:own": ["*"],
"read:own": ["*"],
"delete:own": ["*"]
},
"messages": {
"create:own": ["*"],
"read:own": ["*"],

View file

@ -7,8 +7,15 @@ const mobileconfig = require('mobileconfig');
const uuid = require('uuid');
const consts = require('../consts');
const certs = require('../certs').get('api.mobileconfig');
const tools = require('../tools');
const roles = require('../roles');
const util = require('util');
module.exports = (db, server, userHandler) => {
const generateASP = util.promisify(userHandler.generateASP.bind(userHandler));
const deleteASP = util.promisify(userHandler.deleteASP.bind(userHandler));
const mobileconfigGetSignedConfig = util.promisify(mobileconfig.getSignedConfig.bind(mobileconfig));
/**
* @api {get} /users/:user/asps List Application Passwords
* @apiName GetASPs
@ -63,7 +70,9 @@ module.exports = (db, server, userHandler) => {
* "error": "Database error"
* }
*/
server.get('/users/:user/asps', (req, res, next) => {
server.get(
'/users/:user/asps',
tools.asyncifyJson(async (req, res, next) => {
res.charSet('utf-8');
const schema = Joi.object().keys({
@ -92,9 +101,19 @@ module.exports = (db, server, userHandler) => {
return next();
}
// permissions check
if (req.user && req.user === result.value.user) {
req.validate(roles.can(req.role).readOwn('asps'));
} else {
req.validate(roles.can(req.role).readAny('asps'));
}
let user = new ObjectID(result.value.user);
db.users.collection('users').findOne(
let userData;
try {
userData = await db.users.collection('users').findOne(
{
_id: user
},
@ -102,15 +121,16 @@ module.exports = (db, server, userHandler) => {
projection: {
address: true
}
},
(err, userData) => {
if (err) {
}
);
} catch (err) {
res.json({
error: 'MongoDB Error: ' + err.message,
code: 'InternalDatabaseError'
});
return next();
}
if (!userData) {
res.json({
error: 'This user does not exist',
@ -119,14 +139,16 @@ module.exports = (db, server, userHandler) => {
return next();
}
db.users
let asps;
try {
asps = await db.users
.collection('asps')
.find({
user
})
.sort({ _id: 1 })
.toArray((err, asps) => {
if (err) {
.toArray();
} catch (err) {
res.json({
error: 'MongoDB Error: ' + err.message,
code: 'InternalDatabaseError'
@ -154,10 +176,8 @@ module.exports = (db, server, userHandler) => {
});
return next();
});
}
})
);
});
/**
* @api {get} /users/:user/asps/:asp Request ASP information
@ -209,7 +229,9 @@ module.exports = (db, server, userHandler) => {
* "error": "Database error"
* }
*/
server.get('/users/:user/asps/:asp', (req, res, next) => {
server.get(
'/users/:user/asps/:asp',
tools.asyncifyJson(async (req, res, next) => {
res.charSet('utf-8');
const schema = Joi.object().keys({
@ -243,16 +265,24 @@ module.exports = (db, server, userHandler) => {
return next();
}
// permissions check
if (req.user && req.user === result.value.user) {
req.validate(roles.can(req.role).readOwn('asps'));
} else {
req.validate(roles.can(req.role).readAny('asps'));
}
let user = new ObjectID(result.value.user);
let asp = new ObjectID(result.value.asp);
db.users.collection('asps').findOne(
{
let aspData;
try {
aspData = await db.users.collection('asps').findOne({
_id: asp,
user
},
(err, asp) => {
if (err) {
});
} catch (err) {
res.json({
error: 'MongoDB Error: ' + err.message,
code: 'InternalDatabaseError'
@ -260,7 +290,7 @@ module.exports = (db, server, userHandler) => {
return next();
}
if (!asp) {
if (!aspData) {
res.json({
error: 'Invalid or unknown ASP key',
code: 'AspNotFound'
@ -270,20 +300,19 @@ module.exports = (db, server, userHandler) => {
res.json({
success: true,
id: asp._id,
description: asp.description,
scopes: asp.scopes.includes('*') ? [...consts.SCOPES] : asp.scopes,
id: aspData._id,
description: aspData.description,
scopes: aspData.scopes.includes('*') ? [...consts.SCOPES] : aspData.scopes,
lastUse: {
time: asp.used || false,
event: asp.authEvent || false
time: aspData.used || false,
event: aspData.authEvent || false
},
created: asp.created
created: aspData.created
});
return next();
}
})
);
});
/**
* @api {post} /users/:user/asps Create new Application Password
@ -299,6 +328,7 @@ module.exports = (db, server, userHandler) => {
* @apiParam {String} description Description
* @apiParam {String[]} scopes List of scopes this Password applies to. Special scope "*" indicates that this password can be used for any scope except "master"
* @apiParam {Boolean} [generateMobileconfig] If true then result contains a mobileconfig formatted file with account config
* @apiParam {String} [address] E-mail address to be used as the account address in mobileconfig file. Must be one of the listed identity addresses of the user. Defaults to the main address of the user
* @apiParam {String} [sess] Session identifier for the logs
* @apiParam {String} [ip] IP address for the logs
*
@ -333,7 +363,9 @@ module.exports = (db, server, userHandler) => {
* "error": "Database error"
* }
*/
server.post('/users/:user/asps', (req, res, next) => {
server.post(
'/users/:user/asps',
tools.asyncifyJson(async (req, res, next) => {
res.charSet('utf-8');
const schema = Joi.object().keys({
@ -353,6 +385,9 @@ module.exports = (db, server, userHandler) => {
.required()
)
.unique(),
address: Joi.string()
.empty('')
.email(),
generateMobileconfig: Joi.boolean()
.truthy(['Y', 'true', 'yes', 'on', '1', 1])
.falsy(['N', 'false', 'no', 'off', '0', 0, ''])
@ -384,6 +419,13 @@ module.exports = (db, server, userHandler) => {
return next();
}
// permissions check
if (req.user && req.user === result.value.user) {
req.validate(roles.can(req.role).createOwn('asps'));
} else {
req.validate(roles.can(req.role).createAny('asps'));
}
let user = new ObjectID(result.value.user);
let generateMobileconfig = result.value.generateMobileconfig;
let scopes = result.value.scopes || ['*'];
@ -401,7 +443,9 @@ module.exports = (db, server, userHandler) => {
return next();
}
db.users.collection('users').findOne(
let userData;
try {
userData = await db.users.collection('users').findOne(
{
_id: user
},
@ -411,15 +455,16 @@ module.exports = (db, server, userHandler) => {
name: true,
address: true
}
},
(err, userData) => {
if (err) {
}
);
} catch (err) {
res.json({
error: 'MongoDB Error: ' + err.message,
code: 'InternalDatabaseError'
});
return next();
}
if (!userData) {
res.json({
error: 'This user does not exist',
@ -428,19 +473,52 @@ module.exports = (db, server, userHandler) => {
return next();
}
userHandler.generateASP(user, result.value, (err, result) => {
if (err) {
let accountType;
let accountHost;
let accountPort;
let accountSecure;
let accountAddress;
let accountName;
if (result.value.address) {
let addressData;
try {
addressData = await db.users.collection('addresses').findOne({
addrview: tools.normalizeAddress(result.value.address, false, {
removeLabel: true,
removeDots: true
})
});
} catch (err) {
res.json({
error: err.message
error: 'MongoDB Error: ' + err.message,
code: 'InternalDatabaseError'
});
return next();
}
if (!addressData || !addressData.user.equals(userData._id)) {
res.json({
error: 'Invalid or unknown address',
code: 'AddressNotFound'
});
return next();
}
accountName = addressData.name || userData.name || '';
accountAddress = addressData.address;
} else {
accountName = userData.name || '';
accountAddress = userData.address;
}
let asp = await generateASP(user, result.value);
if (!generateMobileconfig) {
res.json({
success: true,
id: result.id,
password: result.password
id: asp.id,
password: asp.password
});
return next();
}
@ -449,15 +527,11 @@ module.exports = (db, server, userHandler) => {
Object.keys(config.api.mobileconfig || {}).forEach(key => {
profileOpts[key] = (config.api.mobileconfig[key] || '')
.toString()
.replace(/\{email\}/g, userData.address)
.replace(/\{email\}/g, accountAddress)
.replace(/\{name\}/g, accountName)
.trim();
});
let accountType;
let accountHost;
let accountPort;
let accountSecure;
if (scopes.includes('*') || scopes.includes('imap')) {
// prefer IMAP
accountType = 'EmailTypeIMAP';
@ -471,7 +545,7 @@ module.exports = (db, server, userHandler) => {
accountSecure = !!config.pop3.setup.secure;
}
mobileconfig.getSignedConfig(
let profile = await mobileconfigGetSignedConfig(
{
PayloadType: 'Configuration',
PayloadVersion: 1,
@ -492,21 +566,21 @@ module.exports = (db, server, userHandler) => {
PayloadOrganization: profileOpts.organization || 'WildDuck Mail Server',
EmailAccountDescription: profileOpts.accountDescription,
EmailAccountName: userData.name || '',
EmailAccountName: accountName,
EmailAccountType: accountType,
EmailAddress: userData.address,
EmailAddress: accountAddress,
IncomingMailServerAuthentication: 'EmailAuthPassword',
IncomingMailServerHostName: accountHost,
IncomingMailServerPortNumber: accountPort,
IncomingMailServerUseSSL: accountSecure,
IncomingMailServerUsername: userData.address,
IncomingPassword: result.password,
IncomingMailServerUsername: accountAddress,
IncomingPassword: asp.password,
OutgoingPasswordSameAsIncomingPassword: true,
OutgoingMailServerAuthentication: 'EmailAuthPassword',
OutgoingMailServerHostName: config.smtp.setup.hostname,
OutgoingMailServerPortNumber: config.smtp.setup.port || config.smtp.port,
OutgoingMailServerUseSSL: 'secure' in config.smtp.setup ? !!config.smtp.setup.secure : config.smtp.secure,
OutgoingMailServerUsername: userData.address,
OutgoingMailServerUsername: accountAddress,
PreventMove: false,
PreventAppSheet: false,
SMIMEEnabled: false,
@ -514,72 +588,21 @@ module.exports = (db, server, userHandler) => {
}
]
},
certs,
(err, data) => {
if (err) {
res.json({
error: err.message
});
return next();
}
certs
);
res.json({
success: true,
id: result.id,
password: result.password,
mobileconfig: data.toString('base64')
id: asp.id,
name: accountName,
address: accountAddress,
password: asp.password,
mobileconfig: profile.toString('base64')
});
return next();
}
})
);
/*
let options = {
displayName: description || profileOpts.displayName,
displayDescription: profileOpts.displayDescription,
accountDescription: profileOpts.accountDescription,
emailAddress: userData.address,
emailAccountName: userData.name,
identifier: profileOpts.identifier + '.' + userData.username,
imap: {
hostname: config.imap.setup.hostname,
port: config.imap.setup.port || config.imap.port,
secure: config.imap.setup.secure,
username: userData.username,
password: result.password
},
smtp: {
hostname: config.smtp.setup.hostname,
port: config.smtp.setup.port || config.smtp.port,
secure: true, //config.setup.smtp.secure,
username: userData.username,
password: false // use the same password as for IMAP
},
keys: certs
};
mobileconfig.getSignedEmailConfig(options, (err, data) => {
if (err) {
res.json({
error: err.message
});
return next();
}
res.json({
success: true,
id: result.id,
password: result.password,
mobileconfig: data.toString('base64')
});
return next();
});
*/
});
}
);
});
/**
* @api {delete} /users/:user/asps/:asp Delete an Application Password
* @apiName DeleteASP
@ -612,7 +635,9 @@ module.exports = (db, server, userHandler) => {
* "error": "Database error"
* }
*/
server.del('/users/:user/asps/:asp', (req, res, next) => {
server.del(
'/users/:user/asps/:asp',
tools.asyncifyJson(async (req, res, next) => {
res.charSet('utf-8');
const schema = Joi.object().keys({
@ -646,20 +671,22 @@ module.exports = (db, server, userHandler) => {
return next();
}
// permissions check
if (req.user && req.user === result.value.user) {
req.validate(roles.can(req.role).deleteOwn('asps'));
} else {
req.validate(roles.can(req.role).deleteAny('asps'));
}
let user = new ObjectID(result.value.user);
let asp = new ObjectID(result.value.asp);
userHandler.deleteASP(user, asp, result.value, err => {
if (err) {
res.json({
error: err.message
});
return next();
}
await deleteASP(user, asp, result.value);
res.json({
success: true
});
return next();
});
});
})
);
};