mirror of
https://github.com/nodemailer/wildduck.git
synced 2025-10-09 21:37:13 +08:00
asp handling update
This commit is contained in:
parent
dde325037a
commit
f6797583fe
2 changed files with 444 additions and 405 deletions
|
@ -19,6 +19,12 @@
|
||||||
"delete:any": ["*"]
|
"delete:any": ["*"]
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"asps": {
|
||||||
|
"create:any": ["*"],
|
||||||
|
"read:any": ["*"],
|
||||||
|
"delete:any": ["*"]
|
||||||
|
},
|
||||||
|
|
||||||
"messages": {
|
"messages": {
|
||||||
"create:any": ["*"],
|
"create:any": ["*"],
|
||||||
"read:any": ["*"],
|
"read:any": ["*"],
|
||||||
|
@ -58,6 +64,12 @@
|
||||||
"update:own": ["*", "!audit"]
|
"update:own": ["*", "!audit"]
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"asps": {
|
||||||
|
"create:own": ["*"],
|
||||||
|
"read:own": ["*"],
|
||||||
|
"delete:own": ["*"]
|
||||||
|
},
|
||||||
|
|
||||||
"messages": {
|
"messages": {
|
||||||
"create:own": ["*"],
|
"create:own": ["*"],
|
||||||
"read:own": ["*"],
|
"read:own": ["*"],
|
||||||
|
|
837
lib/api/asps.js
837
lib/api/asps.js
|
@ -7,8 +7,15 @@ const mobileconfig = require('mobileconfig');
|
||||||
const uuid = require('uuid');
|
const uuid = require('uuid');
|
||||||
const consts = require('../consts');
|
const consts = require('../consts');
|
||||||
const certs = require('../certs').get('api.mobileconfig');
|
const certs = require('../certs').get('api.mobileconfig');
|
||||||
|
const tools = require('../tools');
|
||||||
|
const roles = require('../roles');
|
||||||
|
const util = require('util');
|
||||||
|
|
||||||
module.exports = (db, server, userHandler) => {
|
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
|
* @api {get} /users/:user/asps List Application Passwords
|
||||||
* @apiName GetASPs
|
* @apiName GetASPs
|
||||||
|
@ -63,101 +70,114 @@ module.exports = (db, server, userHandler) => {
|
||||||
* "error": "Database error"
|
* "error": "Database error"
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
server.get('/users/:user/asps', (req, res, next) => {
|
server.get(
|
||||||
res.charSet('utf-8');
|
'/users/:user/asps',
|
||||||
|
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'
|
||||||
})
|
})
|
||||||
});
|
|
||||||
|
|
||||||
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
|
||||||
|
});
|
||||||
|
|
||||||
db.users.collection('users').findOne(
|
if (result.error) {
|
||||||
{
|
res.json({
|
||||||
_id: user
|
error: result.error.message,
|
||||||
},
|
code: 'InputValidationError'
|
||||||
{
|
});
|
||||||
projection: {
|
return next();
|
||||||
address: 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
db.users
|
// 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 userData;
|
||||||
|
|
||||||
|
try {
|
||||||
|
userData = await db.users.collection('users').findOne(
|
||||||
|
{
|
||||||
|
_id: user
|
||||||
|
},
|
||||||
|
{
|
||||||
|
projection: {
|
||||||
|
address: 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 asps;
|
||||||
|
try {
|
||||||
|
asps = await db.users
|
||||||
.collection('asps')
|
.collection('asps')
|
||||||
.find({
|
.find({
|
||||||
user
|
user
|
||||||
})
|
})
|
||||||
.sort({ _id: 1 })
|
.sort({ _id: 1 })
|
||||||
.toArray((err, asps) => {
|
.toArray();
|
||||||
if (err) {
|
} catch (err) {
|
||||||
res.json({
|
res.json({
|
||||||
error: 'MongoDB Error: ' + err.message,
|
error: 'MongoDB Error: ' + err.message,
|
||||||
code: 'InternalDatabaseError'
|
code: 'InternalDatabaseError'
|
||||||
});
|
});
|
||||||
return next();
|
return next();
|
||||||
}
|
|
||||||
|
|
||||||
if (!asps) {
|
|
||||||
asps = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
success: true,
|
|
||||||
|
|
||||||
results: asps.map(asp => ({
|
|
||||||
id: asp._id,
|
|
||||||
description: asp.description,
|
|
||||||
scopes: asp.scopes.includes('*') ? [...consts.SCOPES] : asp.scopes,
|
|
||||||
lastUse: {
|
|
||||||
time: asp.used || false,
|
|
||||||
event: asp.authEvent || false
|
|
||||||
},
|
|
||||||
created: asp.created
|
|
||||||
}))
|
|
||||||
});
|
|
||||||
|
|
||||||
return next();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
);
|
|
||||||
});
|
if (!asps) {
|
||||||
|
asps = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
|
||||||
|
results: asps.map(asp => ({
|
||||||
|
id: asp._id,
|
||||||
|
description: asp.description,
|
||||||
|
scopes: asp.scopes.includes('*') ? [...consts.SCOPES] : asp.scopes,
|
||||||
|
lastUse: {
|
||||||
|
time: asp.used || false,
|
||||||
|
event: asp.authEvent || false
|
||||||
|
},
|
||||||
|
created: asp.created
|
||||||
|
}))
|
||||||
|
});
|
||||||
|
|
||||||
|
return next();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api {get} /users/:user/asps/:asp Request ASP information
|
* @api {get} /users/:user/asps/:asp Request ASP information
|
||||||
|
@ -209,81 +229,90 @@ module.exports = (db, server, userHandler) => {
|
||||||
* "error": "Database error"
|
* "error": "Database error"
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
server.get('/users/:user/asps/:asp', (req, res, next) => {
|
server.get(
|
||||||
res.charSet('utf-8');
|
'/users/:user/asps/:asp',
|
||||||
|
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(),
|
||||||
asp: Joi.string()
|
asp: 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'
|
||||||
})
|
})
|
||||||
});
|
|
||||||
|
|
||||||
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, {
|
||||||
let asp = new ObjectID(result.value.asp);
|
abortEarly: false,
|
||||||
|
convert: true
|
||||||
db.users.collection('asps').findOne(
|
});
|
||||||
{
|
|
||||||
_id: asp,
|
|
||||||
user
|
|
||||||
},
|
|
||||||
(err, asp) => {
|
|
||||||
if (err) {
|
|
||||||
res.json({
|
|
||||||
error: 'MongoDB Error: ' + err.message,
|
|
||||||
code: 'InternalDatabaseError'
|
|
||||||
});
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!asp) {
|
|
||||||
res.json({
|
|
||||||
error: 'Invalid or unknown ASP key',
|
|
||||||
code: 'AspNotFound'
|
|
||||||
});
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (result.error) {
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
error: result.error.message,
|
||||||
id: asp._id,
|
code: 'InputValidationError'
|
||||||
description: asp.description,
|
|
||||||
scopes: asp.scopes.includes('*') ? [...consts.SCOPES] : asp.scopes,
|
|
||||||
lastUse: {
|
|
||||||
time: asp.used || false,
|
|
||||||
event: asp.authEvent || false
|
|
||||||
},
|
|
||||||
created: asp.created
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return next();
|
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);
|
||||||
|
|
||||||
|
let aspData;
|
||||||
|
|
||||||
|
try {
|
||||||
|
aspData = await db.users.collection('asps').findOne({
|
||||||
|
_id: asp,
|
||||||
|
user
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
res.json({
|
||||||
|
error: 'MongoDB Error: ' + err.message,
|
||||||
|
code: 'InternalDatabaseError'
|
||||||
|
});
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!aspData) {
|
||||||
|
res.json({
|
||||||
|
error: 'Invalid or unknown ASP key',
|
||||||
|
code: 'AspNotFound'
|
||||||
|
});
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
id: aspData._id,
|
||||||
|
description: aspData.description,
|
||||||
|
scopes: aspData.scopes.includes('*') ? [...consts.SCOPES] : aspData.scopes,
|
||||||
|
lastUse: {
|
||||||
|
time: aspData.used || false,
|
||||||
|
event: aspData.authEvent || false
|
||||||
|
},
|
||||||
|
created: aspData.created
|
||||||
|
});
|
||||||
|
|
||||||
|
return next();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api {post} /users/:user/asps Create new Application Password
|
* @api {post} /users/:user/asps Create new Application Password
|
||||||
|
@ -299,6 +328,7 @@ module.exports = (db, server, userHandler) => {
|
||||||
* @apiParam {String} description Description
|
* @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 {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 {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} [sess] Session identifier for the logs
|
||||||
* @apiParam {String} [ip] IP address for the logs
|
* @apiParam {String} [ip] IP address for the logs
|
||||||
*
|
*
|
||||||
|
@ -333,252 +363,245 @@ module.exports = (db, server, userHandler) => {
|
||||||
* "error": "Database error"
|
* "error": "Database error"
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
server.post('/users/:user/asps', (req, res, next) => {
|
server.post(
|
||||||
res.charSet('utf-8');
|
'/users/:user/asps',
|
||||||
|
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(),
|
||||||
description: Joi.string()
|
description: Joi.string()
|
||||||
.trim()
|
.trim()
|
||||||
.max(255)
|
.max(255)
|
||||||
.required(),
|
.required(),
|
||||||
scopes: Joi.array()
|
scopes: Joi.array()
|
||||||
.items(
|
.items(
|
||||||
Joi.string()
|
Joi.string()
|
||||||
.valid(...consts.SCOPES, '*')
|
.valid(...consts.SCOPES, '*')
|
||||||
.required()
|
.required()
|
||||||
)
|
)
|
||||||
.unique(),
|
.unique(),
|
||||||
generateMobileconfig: Joi.boolean()
|
address: Joi.string()
|
||||||
.truthy(['Y', 'true', 'yes', 'on', '1', 1])
|
.empty('')
|
||||||
.falsy(['N', 'false', 'no', 'off', '0', 0, ''])
|
.email(),
|
||||||
.default(false),
|
generateMobileconfig: Joi.boolean()
|
||||||
sess: Joi.string().max(255),
|
.truthy(['Y', 'true', 'yes', 'on', '1', 1])
|
||||||
ip: Joi.string().ip({
|
.falsy(['N', 'false', 'no', 'off', '0', 0, ''])
|
||||||
version: ['ipv4', 'ipv6'],
|
.default(false),
|
||||||
cidr: 'forbidden'
|
sess: Joi.string().max(255),
|
||||||
})
|
ip: Joi.string().ip({
|
||||||
});
|
version: ['ipv4', 'ipv6'],
|
||||||
|
cidr: 'forbidden'
|
||||||
if (typeof req.params.scopes === 'string') {
|
})
|
||||||
req.params.scopes = req.params.scopes
|
|
||||||
.split(',')
|
|
||||||
.map(scope => scope.trim())
|
|
||||||
.filter(scope => scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
if (typeof req.params.scopes === 'string') {
|
||||||
let generateMobileconfig = result.value.generateMobileconfig;
|
req.params.scopes = req.params.scopes
|
||||||
let scopes = result.value.scopes || ['*'];
|
.split(',')
|
||||||
let description = result.value.description;
|
.map(scope => scope.trim())
|
||||||
|
.filter(scope => scope);
|
||||||
|
}
|
||||||
|
|
||||||
if (scopes.includes('*')) {
|
const result = Joi.validate(req.params, schema, {
|
||||||
scopes = ['*'];
|
abortEarly: false,
|
||||||
}
|
convert: true
|
||||||
|
|
||||||
if (generateMobileconfig && !scopes.includes('*') && ((!scopes.includes('imap') && !scopes.includes('pop3')) || !scopes.includes('smtp'))) {
|
|
||||||
res.json({
|
|
||||||
error: 'Profile file requires either imap or pop3 and smtp scopes',
|
|
||||||
code: 'InvalidAuthScope'
|
|
||||||
});
|
});
|
||||||
return next();
|
|
||||||
}
|
|
||||||
|
|
||||||
db.users.collection('users').findOne(
|
if (result.error) {
|
||||||
{
|
res.json({
|
||||||
_id: user
|
error: result.error.message,
|
||||||
},
|
code: 'InputValidationError'
|
||||||
{
|
});
|
||||||
projection: {
|
return next();
|
||||||
username: true,
|
}
|
||||||
name: true,
|
|
||||||
address: true
|
// permissions check
|
||||||
}
|
if (req.user && req.user === result.value.user) {
|
||||||
},
|
req.validate(roles.can(req.role).createOwn('asps'));
|
||||||
(err, userData) => {
|
} else {
|
||||||
if (err) {
|
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 || ['*'];
|
||||||
|
let description = result.value.description;
|
||||||
|
|
||||||
|
if (scopes.includes('*')) {
|
||||||
|
scopes = ['*'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (generateMobileconfig && !scopes.includes('*') && ((!scopes.includes('imap') && !scopes.includes('pop3')) || !scopes.includes('smtp'))) {
|
||||||
|
res.json({
|
||||||
|
error: 'Profile file requires either imap or pop3 and smtp scopes',
|
||||||
|
code: 'InvalidAuthScope'
|
||||||
|
});
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
let userData;
|
||||||
|
try {
|
||||||
|
userData = await db.users.collection('users').findOne(
|
||||||
|
{
|
||||||
|
_id: user
|
||||||
|
},
|
||||||
|
{
|
||||||
|
projection: {
|
||||||
|
username: true,
|
||||||
|
name: true,
|
||||||
|
address: 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 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({
|
res.json({
|
||||||
error: 'MongoDB Error: ' + err.message,
|
error: 'MongoDB Error: ' + err.message,
|
||||||
code: 'InternalDatabaseError'
|
code: 'InternalDatabaseError'
|
||||||
});
|
});
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
if (!userData) {
|
|
||||||
|
if (!addressData || !addressData.user.equals(userData._id)) {
|
||||||
res.json({
|
res.json({
|
||||||
error: 'This user does not exist',
|
error: 'Invalid or unknown address',
|
||||||
code: 'UserNotFound'
|
code: 'AddressNotFound'
|
||||||
});
|
});
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
|
|
||||||
userHandler.generateASP(user, result.value, (err, result) => {
|
accountName = addressData.name || userData.name || '';
|
||||||
if (err) {
|
accountAddress = addressData.address;
|
||||||
res.json({
|
} else {
|
||||||
error: err.message
|
accountName = userData.name || '';
|
||||||
});
|
accountAddress = userData.address;
|
||||||
return next();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!generateMobileconfig) {
|
let asp = await generateASP(user, result.value);
|
||||||
res.json({
|
|
||||||
success: true,
|
|
||||||
id: result.id,
|
|
||||||
password: result.password
|
|
||||||
});
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
|
|
||||||
let profileOpts = {};
|
if (!generateMobileconfig) {
|
||||||
Object.keys(config.api.mobileconfig || {}).forEach(key => {
|
res.json({
|
||||||
profileOpts[key] = (config.api.mobileconfig[key] || '')
|
success: true,
|
||||||
.toString()
|
id: asp.id,
|
||||||
.replace(/\{email\}/g, userData.address)
|
password: asp.password
|
||||||
.trim();
|
});
|
||||||
});
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
let accountType;
|
let profileOpts = {};
|
||||||
let accountHost;
|
Object.keys(config.api.mobileconfig || {}).forEach(key => {
|
||||||
let accountPort;
|
profileOpts[key] = (config.api.mobileconfig[key] || '')
|
||||||
let accountSecure;
|
.toString()
|
||||||
|
.replace(/\{email\}/g, accountAddress)
|
||||||
|
.replace(/\{name\}/g, accountName)
|
||||||
|
.trim();
|
||||||
|
});
|
||||||
|
|
||||||
if (scopes.includes('*') || scopes.includes('imap')) {
|
if (scopes.includes('*') || scopes.includes('imap')) {
|
||||||
// prefer IMAP
|
// prefer IMAP
|
||||||
accountType = 'EmailTypeIMAP';
|
accountType = 'EmailTypeIMAP';
|
||||||
accountHost = config.imap.setup.hostname;
|
accountHost = config.imap.setup.hostname;
|
||||||
accountPort = config.imap.setup.port || config.imap.port;
|
accountPort = config.imap.setup.port || config.imap.port;
|
||||||
accountSecure = !!config.imap.setup.secure;
|
accountSecure = !!config.imap.setup.secure;
|
||||||
} else {
|
} else {
|
||||||
accountType = 'EmailTypePOP';
|
accountType = 'EmailTypePOP';
|
||||||
accountHost = config.pop3.setup.hostname;
|
accountHost = config.pop3.setup.hostname;
|
||||||
accountPort = config.pop3.setup.port || config.pop3.port;
|
accountPort = config.pop3.setup.port || config.pop3.port;
|
||||||
accountSecure = !!config.pop3.setup.secure;
|
accountSecure = !!config.pop3.setup.secure;
|
||||||
}
|
}
|
||||||
|
|
||||||
mobileconfig.getSignedConfig(
|
let profile = await mobileconfigGetSignedConfig(
|
||||||
|
{
|
||||||
|
PayloadType: 'Configuration',
|
||||||
|
PayloadVersion: 1,
|
||||||
|
PayloadIdentifier: profileOpts.identifier + '.' + userData._id,
|
||||||
|
PayloadUUID: uuid.v4(),
|
||||||
|
PayloadDisplayName: description || profileOpts.displayName,
|
||||||
|
PayloadDescription: profileOpts.displayDescription,
|
||||||
|
PayloadOrganization: profileOpts.organization || 'WildDuck Mail Server',
|
||||||
|
|
||||||
|
PayloadContent: [
|
||||||
{
|
{
|
||||||
PayloadType: 'Configuration',
|
PayloadType: 'com.apple.mail.managed',
|
||||||
PayloadVersion: 1,
|
PayloadVersion: 1,
|
||||||
PayloadIdentifier: profileOpts.identifier + '.' + userData._id,
|
PayloadIdentifier: profileOpts.identifier + '.' + userData._id,
|
||||||
PayloadUUID: uuid.v4(),
|
PayloadUUID: uuid.v4(),
|
||||||
PayloadDisplayName: description || profileOpts.displayName,
|
PayloadDisplayName: 'Email Account',
|
||||||
PayloadDescription: profileOpts.displayDescription,
|
PayloadDescription: 'Configures email account',
|
||||||
PayloadOrganization: profileOpts.organization || 'WildDuck Mail Server',
|
PayloadOrganization: profileOpts.organization || 'WildDuck Mail Server',
|
||||||
|
|
||||||
PayloadContent: [
|
EmailAccountDescription: profileOpts.accountDescription,
|
||||||
{
|
EmailAccountName: accountName,
|
||||||
PayloadType: 'com.apple.mail.managed',
|
EmailAccountType: accountType,
|
||||||
PayloadVersion: 1,
|
EmailAddress: accountAddress,
|
||||||
PayloadIdentifier: profileOpts.identifier + '.' + userData._id,
|
IncomingMailServerAuthentication: 'EmailAuthPassword',
|
||||||
PayloadUUID: uuid.v4(),
|
IncomingMailServerHostName: accountHost,
|
||||||
PayloadDisplayName: 'Email Account',
|
IncomingMailServerPortNumber: accountPort,
|
||||||
PayloadDescription: 'Configures email account',
|
IncomingMailServerUseSSL: accountSecure,
|
||||||
PayloadOrganization: profileOpts.organization || 'WildDuck Mail Server',
|
IncomingMailServerUsername: accountAddress,
|
||||||
|
IncomingPassword: asp.password,
|
||||||
EmailAccountDescription: profileOpts.accountDescription,
|
OutgoingPasswordSameAsIncomingPassword: true,
|
||||||
EmailAccountName: userData.name || '',
|
OutgoingMailServerAuthentication: 'EmailAuthPassword',
|
||||||
EmailAccountType: accountType,
|
OutgoingMailServerHostName: config.smtp.setup.hostname,
|
||||||
EmailAddress: userData.address,
|
OutgoingMailServerPortNumber: config.smtp.setup.port || config.smtp.port,
|
||||||
IncomingMailServerAuthentication: 'EmailAuthPassword',
|
OutgoingMailServerUseSSL: 'secure' in config.smtp.setup ? !!config.smtp.setup.secure : config.smtp.secure,
|
||||||
IncomingMailServerHostName: accountHost,
|
OutgoingMailServerUsername: accountAddress,
|
||||||
IncomingMailServerPortNumber: accountPort,
|
PreventMove: false,
|
||||||
IncomingMailServerUseSSL: accountSecure,
|
PreventAppSheet: false,
|
||||||
IncomingMailServerUsername: userData.address,
|
SMIMEEnabled: false,
|
||||||
IncomingPassword: result.password,
|
allowMailDrop: true
|
||||||
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,
|
|
||||||
PreventMove: false,
|
|
||||||
PreventAppSheet: false,
|
|
||||||
SMIMEEnabled: false,
|
|
||||||
allowMailDrop: true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
certs,
|
|
||||||
(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();
|
|
||||||
}
|
}
|
||||||
);
|
]
|
||||||
|
},
|
||||||
|
certs
|
||||||
|
);
|
||||||
|
|
||||||
/*
|
res.json({
|
||||||
let options = {
|
success: true,
|
||||||
displayName: description || profileOpts.displayName,
|
id: asp.id,
|
||||||
displayDescription: profileOpts.displayDescription,
|
name: accountName,
|
||||||
accountDescription: profileOpts.accountDescription,
|
address: accountAddress,
|
||||||
emailAddress: userData.address,
|
password: asp.password,
|
||||||
emailAccountName: userData.name,
|
mobileconfig: profile.toString('base64')
|
||||||
identifier: profileOpts.identifier + '.' + userData.username,
|
});
|
||||||
imap: {
|
return next();
|
||||||
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
|
* @api {delete} /users/:user/asps/:asp Delete an Application Password
|
||||||
|
@ -612,54 +635,58 @@ module.exports = (db, server, userHandler) => {
|
||||||
* "error": "Database error"
|
* "error": "Database error"
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
server.del('/users/:user/asps/:asp', (req, res, next) => {
|
server.del(
|
||||||
res.charSet('utf-8');
|
'/users/:user/asps/:asp',
|
||||||
|
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(),
|
||||||
asp: Joi.string()
|
asp: 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'
|
||||||
})
|
})
|
||||||
});
|
|
||||||
|
|
||||||
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, {
|
||||||
let asp = new ObjectID(result.value.asp);
|
abortEarly: false,
|
||||||
|
convert: true
|
||||||
|
});
|
||||||
|
|
||||||
userHandler.deleteASP(user, asp, result.value, err => {
|
if (result.error) {
|
||||||
if (err) {
|
|
||||||
res.json({
|
res.json({
|
||||||
error: err.message
|
error: result.error.message,
|
||||||
|
code: 'InputValidationError'
|
||||||
});
|
});
|
||||||
return next();
|
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);
|
||||||
|
|
||||||
|
await deleteASP(user, asp, result.value);
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
success: true
|
success: true
|
||||||
});
|
});
|
||||||
return next();
|
return next();
|
||||||
});
|
})
|
||||||
});
|
);
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Reference in a new issue