allow to set user specific IMAP max connections

This commit is contained in:
Andris Reinman 2019-02-12 13:20:32 +02:00
parent 01463cf386
commit 39bd87ebaa
3 changed files with 34 additions and 4 deletions

View file

@ -352,6 +352,7 @@ module.exports = (db, server, userHandler) => {
* @apiParam {Number} [imapMaxUpload] How many bytes can be uploaded via IMAP during 24 hour * @apiParam {Number} [imapMaxUpload] How many bytes can be uploaded via IMAP during 24 hour
* @apiParam {Number} [imapMaxDownload] How many bytes can be downloaded via IMAP during 24 hour * @apiParam {Number} [imapMaxDownload] How many bytes can be downloaded via IMAP during 24 hour
* @apiParam {Number} [pop3MaxDownload] How many bytes can be downloaded via POP3 during 24 hour * @apiParam {Number} [pop3MaxDownload] How many bytes can be downloaded via POP3 during 24 hour
* @apiParam {Number} [imapMaxConnections] How many parallel IMAP connections are alowed
* @apiParam {Number} [receivedMax] How many messages can be received from MX during 60 seconds * @apiParam {Number} [receivedMax] How many messages can be received from MX during 60 seconds
* @apiParam {Object} [mailboxes] Optional names for special mailboxes * @apiParam {Object} [mailboxes] Optional names for special mailboxes
* @apiParam {String} [mailboxes.sent="Sent Mail"] Path of Sent Mail folder * @apiParam {String} [mailboxes.sent="Sent Mail"] Path of Sent Mail folder
@ -486,6 +487,9 @@ module.exports = (db, server, userHandler) => {
pop3MaxDownload: Joi.number() pop3MaxDownload: Joi.number()
.min(0) .min(0)
.default(0), .default(0),
imapMaxConnections: Joi.number()
.min(0)
.default(0),
receivedMax: Joi.number() receivedMax: Joi.number()
.min(0) .min(0)
.default(0), .default(0),
@ -909,6 +913,8 @@ module.exports = (db, server, userHandler) => {
* @apiSuccess {Number} limits.pop3Download.allowed How many bytes per 24 hours can be downloaded via POP3. Only message contents are counted, not protocol overhead. * @apiSuccess {Number} limits.pop3Download.allowed How many bytes per 24 hours can be downloaded via POP3. Only message contents are counted, not protocol overhead.
* @apiSuccess {Number} limits.pop3Download.used How many bytes are downloaded during current 24 hour period * @apiSuccess {Number} limits.pop3Download.used How many bytes are downloaded during current 24 hour period
* @apiSuccess {Number} limits.pop3Download.ttl Time until the end of current 24 hour period * @apiSuccess {Number} limits.pop3Download.ttl Time until the end of current 24 hour period
* @apiSuccess {Number} limits.imapMaxConnections.allowed How many parallel IMAP connections are permitted
* @apiSuccess {Number} limits.imapMaxConnections.used How many parallel IMAP connections are currenlty in use
* *
* @apiSuccess {String[]} tags List of tags associated with the User * @apiSuccess {String[]} tags List of tags associated with the User
* @apiSuccess {String[]} disabledScopes Disabled scopes for this user * @apiSuccess {String[]} disabledScopes Disabled scopes for this user
@ -1062,6 +1068,8 @@ module.exports = (db, server, userHandler) => {
.get('pdw:' + userData._id.toString()) .get('pdw:' + userData._id.toString())
.ttl('pdw:' + userData._id.toString()) .ttl('pdw:' + userData._id.toString())
.hget('lim:imap', userData._id.toString())
.exec(); .exec();
} catch (err) { } catch (err) {
// ignore // ignore
@ -1089,6 +1097,8 @@ module.exports = (db, server, userHandler) => {
let pop3Download = Number(response && response[10] && response[10][1]) || 0; let pop3Download = Number(response && response[10] && response[10][1]) || 0;
let pop3DownloadTtl = Number(response && response[11] && response[11][1]) || 0; let pop3DownloadTtl = Number(response && response[11] && response[11][1]) || 0;
let imapMaxConnections = Number(response && response[12] && response[12][1]) || 0;
let keyInfo; let keyInfo;
try { try {
keyInfo = await getKeyInfo(userData.pubKey); keyInfo = await getKeyInfo(userData.pubKey);
@ -1161,6 +1171,11 @@ module.exports = (db, server, userHandler) => {
allowed: Number(userData.pop3MaxDownload) || (config.pop3.maxDownloadMB || 10000) * 1024 * 1024, allowed: Number(userData.pop3MaxDownload) || (config.pop3.maxDownloadMB || 10000) * 1024 * 1024,
used: pop3Download, used: pop3Download,
ttl: pop3DownloadTtl >= 0 ? pop3DownloadTtl : false ttl: pop3DownloadTtl >= 0 ? pop3DownloadTtl : false
},
imapMaxConnections: {
allowed: Number(userData.imapMaxConnections) || config.imap.maxConnections,
used: imapMaxConnections
} }
}, },
@ -1210,6 +1225,7 @@ module.exports = (db, server, userHandler) => {
* @apiParam {Number} [imapMaxUpload] How many bytes can be uploaded via IMAP during 24 hour * @apiParam {Number} [imapMaxUpload] How many bytes can be uploaded via IMAP during 24 hour
* @apiParam {Number} [imapMaxDownload] How many bytes can be downloaded via IMAP during 24 hour * @apiParam {Number} [imapMaxDownload] How many bytes can be downloaded via IMAP during 24 hour
* @apiParam {Number} [pop3MaxDownload] How many bytes can be downloaded via POP3 during 24 hour * @apiParam {Number} [pop3MaxDownload] How many bytes can be downloaded via POP3 during 24 hour
* @apiParam {Number} [imapMaxConnections] How many parallel IMAP connections are alowed
* @apiParam {Number} [receivedMax] How many messages can be received from MX during 60 seconds * @apiParam {Number} [receivedMax] How many messages can be received from MX during 60 seconds
* @apiParam {Boolean} [disable2fa] If true, then disables 2FA for this user * @apiParam {Boolean} [disable2fa] If true, then disables 2FA for this user
* @apiParam {String[]} disabledScopes List of scopes that are disabled for this user ("imap", "pop3", "smtp") * @apiParam {String[]} disabledScopes List of scopes that are disabled for this user ("imap", "pop3", "smtp")
@ -1319,6 +1335,8 @@ module.exports = (db, server, userHandler) => {
imapMaxUpload: Joi.number().min(0), imapMaxUpload: Joi.number().min(0),
imapMaxDownload: Joi.number().min(0), imapMaxDownload: Joi.number().min(0),
pop3MaxDownload: Joi.number().min(0), pop3MaxDownload: Joi.number().min(0),
imapMaxConnections: Joi.number().min(0),
receivedMax: Joi.number().min(0), receivedMax: Joi.number().min(0),
disable2fa: Joi.boolean() disable2fa: Joi.boolean()

View file

@ -1382,6 +1382,8 @@ class UserHandler {
imapMaxUpload: data.imapMaxUpload || 0, imapMaxUpload: data.imapMaxUpload || 0,
imapMaxDownload: data.imapMaxDownload || 0, imapMaxDownload: data.imapMaxDownload || 0,
pop3MaxDownload: data.pop3MaxDownload || 0, pop3MaxDownload: data.pop3MaxDownload || 0,
imapMaxConnections: data.imapMaxConnections || 0,
receivedMax: data.receivedMax || 0, receivedMax: data.receivedMax || 0,
targets: [].concat(data.targets || []), targets: [].concat(data.targets || []),
@ -2831,6 +2833,7 @@ class UserHandler {
['receivedMax', 'rl:rcpt'] ['receivedMax', 'rl:rcpt']
]); ]);
let flushKeys = []; let flushKeys = [];
let flushHKeys = [];
Object.keys(data).forEach(key => { Object.keys(data).forEach(key => {
if (['user', 'existingPassword', 'hashedPassword', 'allowUnsafe', 'ip', 'sess'].includes(key)) { if (['user', 'existingPassword', 'hashedPassword', 'allowUnsafe', 'ip', 'sess'].includes(key)) {
@ -2840,6 +2843,9 @@ class UserHandler {
if (resetKeys.has(key)) { if (resetKeys.has(key)) {
flushKeys.push(resetKeys.get(key) + ':' + user); flushKeys.push(resetKeys.get(key) + ':' + user);
} }
if (key === 'imapMaxConnections') {
flushHKeys.push({ key: 'lim:imap', value: user.toString() });
}
if (key === 'password') { if (key === 'password') {
if (!data[key]) { if (!data[key]) {
@ -3042,11 +3048,17 @@ class UserHandler {
} }
// check if we need to reset any ttl counters // check if we need to reset any ttl counters
if (flushKeys.length) { if (flushKeys.length || flushHKeys.length) {
let flushreq = this.redis.multi(); let flushreq = this.redis.multi();
flushKeys.forEach(key => { flushKeys.forEach(key => {
flushreq = flushreq.del(key); flushreq = flushreq.del(key);
}); });
flushHKeys.forEach(entry => {
flushreq = flushreq.hdel(entry.key, entry.value);
});
// just call the operations and hope for the best, no problems if fails // just call the operations and hope for the best, no problems if fails
flushreq.exec(() => false); flushreq.exec(() => false);
} }

View file

@ -15,7 +15,7 @@
"author": "Andris Reinman", "author": "Andris Reinman",
"license": "EUPL-1.1+", "license": "EUPL-1.1+",
"devDependencies": { "devDependencies": {
"ajv": "6.8.1", "ajv": "6.9.1",
"apidoc": "0.17.7", "apidoc": "0.17.7",
"browserbox": "0.9.1", "browserbox": "0.9.1",
"chai": "4.2.0", "chai": "4.2.0",
@ -61,7 +61,7 @@
"node-forge": "0.8.0", "node-forge": "0.8.0",
"nodemailer": "5.1.1", "nodemailer": "5.1.1",
"npmlog": "4.1.2", "npmlog": "4.1.2",
"openpgp": "4.4.6", "openpgp": "4.4.7",
"pem": "1.14.1", "pem": "1.14.1",
"pwnedpasswords": "1.0.4", "pwnedpasswords": "1.0.4",
"qrcode": "1.3.3", "qrcode": "1.3.3",
@ -74,7 +74,7 @@
"utf7": "1.0.2", "utf7": "1.0.2",
"uuid": "3.3.2", "uuid": "3.3.2",
"wild-config": "1.4.0", "wild-config": "1.4.0",
"yargs": "12.0.5" "yargs": "13.1.0"
}, },
"repository": { "repository": {
"type": "git", "type": "git",