mirror of
https://github.com/nodemailer/wildduck.git
synced 2025-09-11 15:45:53 +08:00
added option for auditing
This commit is contained in:
parent
7e88acca28
commit
208ebb90e5
6 changed files with 158 additions and 100 deletions
|
@ -13,9 +13,9 @@
|
|||
},
|
||||
|
||||
"users": {
|
||||
"create:any": ["*"],
|
||||
"read:any": ["*"],
|
||||
"update:any": ["*"],
|
||||
"create:any": ["*", "!audit"],
|
||||
"read:any": ["*", "!audit"],
|
||||
"update:any": ["*", "!audit"],
|
||||
"delete:any": ["*"]
|
||||
},
|
||||
|
||||
|
@ -54,8 +54,8 @@
|
|||
},
|
||||
|
||||
"users": {
|
||||
"read:own": ["*"],
|
||||
"update:own": ["*"]
|
||||
"read:own": ["*", "!audit"],
|
||||
"update:own": ["*", "!audit"]
|
||||
},
|
||||
|
||||
"messages": {
|
||||
|
@ -77,5 +77,14 @@
|
|||
"authentication": {
|
||||
"create:any": ["*"]
|
||||
}
|
||||
},
|
||||
|
||||
"audit": {
|
||||
"users": {
|
||||
"create:any": ["*"],
|
||||
"read:any": ["*"],
|
||||
"update:any": ["*"],
|
||||
"delete:any": ["*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -207,9 +207,12 @@ class Indexer {
|
|||
|
||||
attachmentStream.once('end', () => finalize());
|
||||
|
||||
attachmentStream.pipe(res, {
|
||||
end: false
|
||||
});
|
||||
attachmentStream.pipe(
|
||||
res,
|
||||
{
|
||||
end: false
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -494,7 +497,7 @@ class Indexer {
|
|||
}
|
||||
|
||||
let newlines = Math.floor(b64size / 78);
|
||||
return Math.ceil((b64size - newlines * 2) / 4 * 3);
|
||||
return Math.ceil(((b64size - newlines * 2) / 4) * 3);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
209
lib/api/users.js
209
lib/api/users.js
|
@ -152,8 +152,10 @@ module.exports = (db, server, userHandler) => {
|
|||
return next();
|
||||
}
|
||||
|
||||
let permission = roles.can(req.role).readAny('users');
|
||||
|
||||
// permissions check
|
||||
req.validate(roles.can(req.role).readAny('users'));
|
||||
req.validate(permission);
|
||||
|
||||
let query = result.value.query;
|
||||
let limit = result.value.limit;
|
||||
|
@ -238,7 +240,8 @@ module.exports = (db, server, userHandler) => {
|
|||
disabled: true,
|
||||
password: true,
|
||||
encryptMessages: true,
|
||||
encryptForwarded: true
|
||||
encryptForwarded: true,
|
||||
audit: true
|
||||
}
|
||||
},
|
||||
// _id gets removed in response if not explicitly set in paginatedField
|
||||
|
@ -274,23 +277,26 @@ module.exports = (db, server, userHandler) => {
|
|||
page,
|
||||
previousCursor: listing.hasPrevious ? listing.previous : false,
|
||||
nextCursor: listing.hasNext ? listing.next : false,
|
||||
results: (listing.results || []).map(userData => ({
|
||||
id: userData._id.toString(),
|
||||
username: userData.username,
|
||||
name: userData.name,
|
||||
address: userData.address,
|
||||
tags: userData.tags || [],
|
||||
targets: userData.targets && userData.targets.map(t => t.value),
|
||||
encryptMessages: !!userData.encryptMessages,
|
||||
encryptForwarded: !!userData.encryptForwarded,
|
||||
quota: {
|
||||
allowed: Number(userData.quota) || config.maxStorage * 1024 * 1024,
|
||||
used: Math.max(Number(userData.storageUsed) || 0, 0)
|
||||
},
|
||||
hasPasswordSet: !!userData.password || !!userData.tempPassword,
|
||||
activated: userData.activated,
|
||||
disabled: userData.disabled
|
||||
}))
|
||||
results: (listing.results || []).map(userData =>
|
||||
permission.filter({
|
||||
id: userData._id.toString(),
|
||||
username: userData.username,
|
||||
name: userData.name,
|
||||
address: userData.address,
|
||||
tags: userData.tags || [],
|
||||
targets: userData.targets && userData.targets.map(t => t.value),
|
||||
encryptMessages: !!userData.encryptMessages,
|
||||
encryptForwarded: !!userData.encryptForwarded,
|
||||
quota: {
|
||||
allowed: Number(userData.quota) || config.maxStorage * 1024 * 1024,
|
||||
used: Math.max(Number(userData.storageUsed) || 0, 0)
|
||||
},
|
||||
audit: userData.audit || false,
|
||||
hasPasswordSet: !!userData.password || !!userData.tempPassword,
|
||||
activated: userData.activated,
|
||||
disabled: userData.disabled
|
||||
})
|
||||
)
|
||||
};
|
||||
|
||||
res.json(response);
|
||||
|
@ -491,6 +497,13 @@ module.exports = (db, server, userHandler) => {
|
|||
.truthy(['Y', 'true', 'yes', 'on', '1', 1])
|
||||
.falsy(['N', 'false', 'no', 'off', '0', 0, ''])
|
||||
.default(false),
|
||||
audit: Joi.string()
|
||||
.empty('')
|
||||
.hex()
|
||||
.lowercase()
|
||||
.length(24)
|
||||
.allow(false),
|
||||
|
||||
sess: Joi.string().max(255),
|
||||
ip: Joi.string().ip({
|
||||
version: ['ipv4', 'ipv6'],
|
||||
|
@ -526,8 +539,17 @@ module.exports = (db, server, userHandler) => {
|
|||
}
|
||||
}
|
||||
|
||||
let permission = roles.can(req.role).createAny('users');
|
||||
|
||||
// permissions check
|
||||
req.validate(roles.can(req.role).createAny('users'));
|
||||
req.validate(permission);
|
||||
|
||||
// filter out unallowed fields
|
||||
result.value = permission.filter(result.value);
|
||||
|
||||
if (result.value.audit) {
|
||||
result.value.audit = new ObjectID(result.value.audit);
|
||||
}
|
||||
|
||||
let targets = result.value.targets;
|
||||
|
||||
|
@ -893,11 +915,13 @@ module.exports = (db, server, userHandler) => {
|
|||
}
|
||||
|
||||
// permissions check
|
||||
let permission;
|
||||
if (req.user && req.user === result.value.user) {
|
||||
req.validate(roles.can(req.role).readOwn('users'));
|
||||
permission = roles.can(req.role).readOwn('users');
|
||||
} else {
|
||||
req.validate(roles.can(req.role).readAny('users'));
|
||||
permission = roles.can(req.role).readAny('users');
|
||||
}
|
||||
req.validate(permission);
|
||||
|
||||
let user = new ObjectID(result.value.user);
|
||||
|
||||
|
@ -987,76 +1011,80 @@ module.exports = (db, server, userHandler) => {
|
|||
errors.notify(err, { userId: user, source: 'pgp' });
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
id: user,
|
||||
res.json(
|
||||
permission.filter({
|
||||
success: true,
|
||||
id: user.toString(),
|
||||
|
||||
username: userData.username,
|
||||
name: userData.name,
|
||||
username: userData.username,
|
||||
name: userData.name,
|
||||
|
||||
address: userData.address,
|
||||
address: userData.address,
|
||||
|
||||
language: userData.language,
|
||||
retention: userData.retention || false,
|
||||
language: userData.language,
|
||||
retention: userData.retention || false,
|
||||
|
||||
enabled2fa: Array.isArray(userData.enabled2fa) ? userData.enabled2fa : [].concat(userData.enabled2fa ? 'totp' : []),
|
||||
enabled2fa: Array.isArray(userData.enabled2fa) ? userData.enabled2fa : [].concat(userData.enabled2fa ? 'totp' : []),
|
||||
|
||||
encryptMessages: userData.encryptMessages,
|
||||
encryptForwarded: userData.encryptForwarded,
|
||||
pubKey: userData.pubKey,
|
||||
spamLevel: userData.spamLevel,
|
||||
keyInfo,
|
||||
encryptMessages: userData.encryptMessages,
|
||||
encryptForwarded: userData.encryptForwarded,
|
||||
pubKey: userData.pubKey,
|
||||
spamLevel: userData.spamLevel,
|
||||
keyInfo,
|
||||
|
||||
targets: [].concat(userData.targets || []),
|
||||
targets: [].concat(userData.targets || []),
|
||||
|
||||
limits: {
|
||||
quota: {
|
||||
allowed: Number(userData.quota) || config.maxStorage * 1024 * 1024,
|
||||
used: Math.max(Number(userData.storageUsed) || 0, 0)
|
||||
limits: {
|
||||
quota: {
|
||||
allowed: Number(userData.quota) || config.maxStorage * 1024 * 1024,
|
||||
used: Math.max(Number(userData.storageUsed) || 0, 0)
|
||||
},
|
||||
|
||||
recipients: {
|
||||
allowed: recipients,
|
||||
used: recipientsSent,
|
||||
ttl: recipientsTtl >= 0 ? recipientsTtl : false
|
||||
},
|
||||
|
||||
forwards: {
|
||||
allowed: forwards,
|
||||
used: forwardsSent,
|
||||
ttl: forwardsTtl >= 0 ? forwardsTtl : false
|
||||
},
|
||||
|
||||
received: {
|
||||
allowed: Number(userData.receivedMax) || 150,
|
||||
used: received,
|
||||
ttl: receivedTtl >= 0 ? receivedTtl : false
|
||||
},
|
||||
|
||||
imapUpload: {
|
||||
allowed: Number(userData.imapMaxUpload) || (config.imap.maxUploadMB || 10) * 1024 * 1024,
|
||||
used: imapUpload,
|
||||
ttl: imapUploadTtl >= 0 ? imapUploadTtl : false
|
||||
},
|
||||
|
||||
imapDownload: {
|
||||
allowed: Number(userData.imapMaxDownload) || (config.imap.maxDownloadMB || 10) * 1024 * 1024,
|
||||
used: imapDownload,
|
||||
ttl: imapDownloadTtl >= 0 ? imapDownloadTtl : false
|
||||
},
|
||||
|
||||
pop3Download: {
|
||||
allowed: Number(userData.pop3MaxDownload) || (config.pop3.maxDownloadMB || 10) * 1024 * 1024,
|
||||
used: pop3Download,
|
||||
ttl: pop3DownloadTtl >= 0 ? pop3DownloadTtl : false
|
||||
}
|
||||
},
|
||||
|
||||
recipients: {
|
||||
allowed: recipients,
|
||||
used: recipientsSent,
|
||||
ttl: recipientsTtl >= 0 ? recipientsTtl : false
|
||||
},
|
||||
tags: userData.tags || [],
|
||||
|
||||
forwards: {
|
||||
allowed: forwards,
|
||||
used: forwardsSent,
|
||||
ttl: forwardsTtl >= 0 ? forwardsTtl : false
|
||||
},
|
||||
|
||||
received: {
|
||||
allowed: Number(userData.receivedMax) || 150,
|
||||
used: received,
|
||||
ttl: receivedTtl >= 0 ? receivedTtl : false
|
||||
},
|
||||
|
||||
imapUpload: {
|
||||
allowed: Number(userData.imapMaxUpload) || (config.imap.maxUploadMB || 10) * 1024 * 1024,
|
||||
used: imapUpload,
|
||||
ttl: imapUploadTtl >= 0 ? imapUploadTtl : false
|
||||
},
|
||||
|
||||
imapDownload: {
|
||||
allowed: Number(userData.imapMaxDownload) || (config.imap.maxDownloadMB || 10) * 1024 * 1024,
|
||||
used: imapDownload,
|
||||
ttl: imapDownloadTtl >= 0 ? imapDownloadTtl : false
|
||||
},
|
||||
|
||||
pop3Download: {
|
||||
allowed: Number(userData.pop3MaxDownload) || (config.pop3.maxDownloadMB || 10) * 1024 * 1024,
|
||||
used: pop3Download,
|
||||
ttl: pop3DownloadTtl >= 0 ? pop3DownloadTtl : false
|
||||
}
|
||||
},
|
||||
|
||||
tags: userData.tags || [],
|
||||
hasPasswordSet: !!userData.password || !!userData.tempPassword,
|
||||
activated: userData.activated,
|
||||
disabled: userData.disabled
|
||||
});
|
||||
audit: userData.audit || false,
|
||||
hasPasswordSet: !!userData.password || !!userData.tempPassword,
|
||||
activated: userData.activated,
|
||||
disabled: userData.disabled
|
||||
})
|
||||
);
|
||||
|
||||
return next();
|
||||
})
|
||||
|
@ -1210,6 +1238,13 @@ module.exports = (db, server, userHandler) => {
|
|||
.max(128)
|
||||
),
|
||||
|
||||
audit: Joi.string()
|
||||
.empty('')
|
||||
.hex()
|
||||
.lowercase()
|
||||
.length(24)
|
||||
.allow(false),
|
||||
|
||||
disabled: Joi.boolean()
|
||||
.empty('')
|
||||
.truthy(['Y', 'true', 'yes', 'on', '1', 1])
|
||||
|
@ -1235,10 +1270,16 @@ module.exports = (db, server, userHandler) => {
|
|||
}
|
||||
|
||||
// permissions check
|
||||
let permission;
|
||||
if (req.user && req.user === result.value.user) {
|
||||
req.validate(roles.can(req.role).updateOwn('users'));
|
||||
permission = roles.can(req.role).updateOwn('users');
|
||||
} else {
|
||||
req.validate(roles.can(req.role).updateAny('users'));
|
||||
permission = roles.can(req.role).updateAny('users');
|
||||
}
|
||||
req.validate(permission);
|
||||
result.value = permission.filter(result.value);
|
||||
if (result.value.audit) {
|
||||
result.value.audit = new ObjectID(result.value.audit);
|
||||
}
|
||||
|
||||
if (result.value.password && !result.value.hashedPassword && !result.value.allowUnsafe) {
|
||||
|
|
|
@ -87,7 +87,8 @@ class FilterHandler {
|
|||
encryptMessages: true,
|
||||
encryptForwarded: true,
|
||||
pubKey: true,
|
||||
spamLevel: true
|
||||
spamLevel: true,
|
||||
audit: true
|
||||
};
|
||||
|
||||
if (collection === 'users') {
|
||||
|
|
|
@ -272,7 +272,7 @@ class MessageHandler {
|
|||
.filter(html => html);
|
||||
}
|
||||
|
||||
this.users.collection('users').findOneAndUpdate(
|
||||
this.users.collection('users').updateOne(
|
||||
{
|
||||
_id: mailboxData.user
|
||||
},
|
||||
|
@ -287,7 +287,7 @@ class MessageHandler {
|
|||
}
|
||||
|
||||
let rollback = err => {
|
||||
this.users.collection('users').findOneAndUpdate(
|
||||
this.users.collection('users').updateOne(
|
||||
{
|
||||
_id: mailboxData.user
|
||||
},
|
||||
|
@ -315,6 +315,10 @@ class MessageHandler {
|
|||
modifyIndex: 1
|
||||
}
|
||||
},
|
||||
{
|
||||
// use original value to get correct UIDNext
|
||||
returnOriginal: true
|
||||
},
|
||||
(err, item) => {
|
||||
if (err) {
|
||||
return rollback(err);
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
"apidoc": "0.17.6",
|
||||
"browserbox": "0.9.1",
|
||||
"chai": "4.1.2",
|
||||
"eslint": "5.5.0",
|
||||
"eslint": "5.6.0",
|
||||
"eslint-config-nodemailer": "1.2.0",
|
||||
"eslint-config-prettier": "3.0.1",
|
||||
"grunt": "1.0.3",
|
||||
|
@ -28,7 +28,7 @@
|
|||
"grunt-eslint": "21.0.0",
|
||||
"grunt-mocha-test": "0.13.3",
|
||||
"grunt-shell-spawn": "0.3.10",
|
||||
"grunt-wait": "0.1.0",
|
||||
"grunt-wait": "0.3.0",
|
||||
"icedfrisby": "1.5.0",
|
||||
"mailparser": "2.3.4",
|
||||
"markdown-toc": "1.2.0",
|
||||
|
@ -58,13 +58,13 @@
|
|||
"mailsplit": "4.2.3",
|
||||
"mobileconfig": "2.1.0",
|
||||
"mongo-cursor-pagination": "7.1.0",
|
||||
"mongodb": "3.1.4",
|
||||
"mongodb": "3.1.6",
|
||||
"mongodb-extended-json": "1.10.0",
|
||||
"node-forge": "0.7.6",
|
||||
"nodemailer": "4.6.8",
|
||||
"npmlog": "4.1.2",
|
||||
"openpgp": "4.0.1",
|
||||
"pem": "1.13.0",
|
||||
"pem": "1.13.1",
|
||||
"pwnedpasswords": "1.0.4",
|
||||
"qrcode": "1.2.2",
|
||||
"restify": "7.2.1",
|
||||
|
|
Loading…
Add table
Reference in a new issue