This commit is contained in:
Andris Reinman 2021-09-05 14:11:24 +03:00
parent a65d708f12
commit 64c47ec587
11 changed files with 185 additions and 23 deletions

1
api.js
View file

@ -490,7 +490,6 @@ module.exports = done => {
users: db.users,
redis: db.redis,
messageHandler,
authlogExpireDays: config.log.authlogExpireDays,
loggelf: message => loggelf(message)
});

View file

@ -51,7 +51,8 @@ processes=1
# changing this value only affects new entries
# set to false to not log authentication events
# set to 0 to keep the logs infinitely
authlogExpireDays=30
# NB! Removed. Use const:authlog:time setting instead
#authlogExpireDays=30
[log.gelf]
enabled=false

View file

@ -227,7 +227,6 @@ module.exports = done => {
database: db.database,
users: db.users,
redis: db.redis,
authlogExpireDays: config.log.authlogExpireDays,
loggelf: message => loggelf(message)
});

View file

@ -75,6 +75,7 @@ module.exports = {
// merge similar authlog events into 6 hour buckets instead of storing each separately
// this is mostly needed for IMAP clients that make crazy amout of connections and thus logins
AUTHLOG_BUCKET: 6 * 3600 * 1000,
AUTHLOG_TIME: 30 * 24 * 3600 * 1000,
// start processing tasks 5 minues after startup
TASK_STARTUP_INTERVAL: 1 * 60 * 1000,

View file

@ -39,6 +39,15 @@ const SETTING_KEYS = [
type: 'number',
constKey: 'MAX_FORWARDS',
schema: Joi.number()
},
{
key: 'const:authlog:time',
name: 'Auth log time',
description: 'Time in ms after authentication log entries will be purged',
type: 'duration',
constKey: 'AUTHLOG_TIME',
schema: Joi.number()
}
];
@ -189,6 +198,7 @@ class SettingsHandler {
name: keyInfo.name,
description: keyInfo.description,
default: keyInfo.constKey ? consts[keyInfo.constKey] : undefined,
type: keyInfo.type,
custom: true
});
} catch (err) {
@ -207,6 +217,7 @@ class SettingsHandler {
name: row.name,
description: row.description,
default: row.constKey ? consts[row.constKey] : undefined,
type: row.type,
custom: false
});
}

View file

@ -20,6 +20,7 @@ const UserCache = require('./user-cache');
const isemail = require('isemail');
const util = require('util');
const TaskHandler = require('./task-handler');
const { SettingsHandler } = require('./settings-handler');
const { encrypt, decrypt } = require('./encrypt');
const {
@ -60,12 +61,7 @@ class UserHandler {
this.flushUserCache = util.promisify(this.userCache.flush.bind(this.userCache));
this.taskHandler = new TaskHandler({ database: this.database });
if (!('authlogExpireDays' in options)) {
this.authlogExpireDays = 30;
} else {
this.authlogExpireDays = options.authlogExpireDays;
}
this.settingsHandler = new SettingsHandler({ db: this.database });
}
resolveAddress(address, options, callback) {
@ -3343,8 +3339,10 @@ class UserHandler {
}
async logAuthEvent(user, entry) {
let authlogTime = await this.settingsHandler.get('const:authlog:time', {});
// only log auth events if we have a valid user id and logging is not disabled
if (!user || !tools.isId(user) || this.authlogExpireDays === false) {
if (!user || !tools.isId(user) || !authlogTime) {
return false;
}
@ -3354,10 +3352,8 @@ class UserHandler {
entry.action = entry.action || 'authentication';
entry.created = now;
if (typeof this.authlogExpireDays === 'number' && this.authlogExpireDays !== 0) {
// this entry expires in set days
entry.expires = new Date(Date.now() + Math.abs(this.authlogExpireDays) * 24 * 3600 * 1000);
}
// this entry expires in set days
entry.expires = new Date(Date.now() + authlogTime);
// key is for merging similar events
entry.key = crypto

View file

@ -247,7 +247,6 @@ module.exports = done => {
database: db.database,
users: db.users,
redis: db.redis,
authlogExpireDays: config.log.authlogExpireDays,
loggelf: message => loggelf(message)
});

View file

@ -1,6 +1,6 @@
{
"name": "wildduck",
"version": "1.35.0",
"version": "1.35.1",
"description": "IMAP/POP3 server built with Node.js and MongoDB",
"main": "server.js",
"scripts": {

View file

@ -464,7 +464,6 @@ module.exports = done => {
database: db.database,
users: db.users,
redis: db.redis,
authlogExpireDays: config.log.authlogExpireDays,
loggelf: message => loggelf(message)
});

View file

@ -19,6 +19,159 @@
return document.getElementById('accessToken').value.trim();
}
function formatDuration(value) {
value = Number(value);
let parts = [];
let days = Math.floor(value / (24 * 3600 * 1000));
value = value - days * 24 * 3600 * 1000;
if (days) {
parts.push(`${days}d`);
}
let hours = Math.floor(value / (3600 * 1000));
value = value - hours * 3600 * 1000;
if (hours) {
parts.push(`${hours}h`);
}
let minutes = Math.floor(value / (60 * 1000));
value = value - minutes * 60 * 1000;
if (minutes) {
parts.push(`${minutes}m`);
}
let seconds = Math.floor(value / 1000);
value = value - seconds * 1000;
if (seconds) {
parts.push(`${seconds}s`);
}
let ms = value;
if (ms || !parts.length) {
parts.push(`${ms}ms`);
}
return parts.join(' ');
}
function formatSize(value) {
value = Number(value);
let parts = [];
let gb = Math.floor(value / (1024 * 1024 * 1024));
value = value - gb * 1024 * 1024 * 1024;
if (gb) {
parts.push(`${gb}GB`);
}
let mb = Math.floor(value / (1024 * 1024));
value = value - mb * 1024 * 1024;
if (mb) {
parts.push(`${mb}MB`);
}
let kb = Math.floor(value / 1024);
value = value - kb * 1024;
if (kb) {
parts.push(`${kb}kB`);
}
let b = value;
if (b || !parts.length) {
parts.push(`${b}B`);
}
return parts.join(' ');
}
function formatNumber(value, type) {
switch (type) {
case 'duration':
return formatDuration(value);
case 'size':
return formatSize(value);
default:
return value;
}
}
function parseSize(value) {
// remove unneeded spaces
value = value.toString().replace(/(\d)[\s\.]+(?=\d|g|m|k|b)/gi, '$1');
let result = 0;
let parts = value.split(/\s+/);
for (let part of parts) {
part.replace(/(\d+)(g|m|k|b)?/i, (o, num, unit) => {
switch ((unit || '').toString().toLowerCase()) {
case 'g':
result += Number(num) * 1024 * 1024 * 1024;
break;
case 'm':
result += Number(num) * 1024 * 1024;
break;
case 'k':
result += Number(num) * 1024;
break;
case 'b':
default:
result += Number(num);
break;
}
});
}
return result;
}
function parseDuration(value) {
// remove unneeded spaces
value = value.toString().replace(/(\d)[\s\.]+(?=\d|d|h|ms|m|s)/gi, '$1');
let result = 0;
let parts = value.split(/\s+/);
for (let part of parts) {
part.replace(/(\d+)(d|h|ms|m|s)?/i, (o, num, unit) => {
switch ((unit || '').toString().toLowerCase()) {
case 'd':
result += Number(num) * 24 * 3600 * 1000;
break;
case 'h':
result += Number(num) * 3600 * 1000;
break;
case 'm':
result += Number(num) * 60 * 1000;
break;
case 's':
result += Number(num) * 1000;
break;
case 'ms':
default:
result += Number(num);
break;
}
});
}
return result;
}
function parseNumber(value, type) {
switch (type) {
case 'duration':
return parseDuration(value);
case 'size':
return parseSize(value);
default:
return value;
}
}
function addSettingsRow(tableElm, setting) {
let trElm = document.createElement('tr');
@ -38,7 +191,7 @@
if (setting.default) {
let defaultTextElm = document.createElement('code');
defaultTextElm.textContent = setting.default;
defaultTextElm.textContent = formatNumber(setting.default, setting.type);
defaultTdElm.title = setting.description;
defaultTdElm.appendChild(defaultTextElm);
} else {
@ -54,7 +207,7 @@
valueInputElm.setAttribute('placeholder', 'Value');
valueInputElm.setAttribute('autocomplete', 'off');
valueInputElm.setAttribute('data-lpignore', 'true');
valueInputElm.value = setting.value || '';
valueInputElm.value = formatNumber(setting.value || '', setting.type);
valueTdElm.appendChild(valueInputElm);
let btnInputElm = document.createElement('button');
@ -73,7 +226,7 @@
'Content-Type': 'application/json'
},
body: JSON.stringify({
value: valueInputElm.value
value: parseNumber(valueInputElm.value, setting.type)
})
})
.then(res => {
@ -175,6 +328,13 @@
<tbody id="settings-table"></tbody>
</table>
</div>
<div>
<ul>
<li>Duration: <code>XXd XXh XXm XXs XXms</code></li>
<li>Size: <code>XXGB XXMB XXkB XXB</code></li>
</ul>
</div>
</div>
</body>
</html>

View file

@ -70,9 +70,6 @@ interfaces=[\"feeder\"]
# defaults to os.hostname()
hostname=\"$HOSTNAME\"
# How long to keep auth records in log
authlogExpireDays=30
# SRS settings for forwarded emails
[wildduck.srs]