added option for auditing

This commit is contained in:
Andris Reinman 2018-09-18 10:20:06 +03:00
parent 7e88acca28
commit 208ebb90e5
6 changed files with 158 additions and 100 deletions

View file

@ -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": ["*"]
}
}
}

View file

@ -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);
}
/**

View file

@ -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) {

View file

@ -87,7 +87,8 @@ class FilterHandler {
encryptMessages: true,
encryptForwarded: true,
pubKey: true,
spamLevel: true
spamLevel: true,
audit: true
};
if (collection === 'users') {

View file

@ -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);

View file

@ -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",