diff --git a/lib/api/addresses.js b/lib/api/addresses.js index 93c331de..a239faab 100644 --- a/lib/api/addresses.js +++ b/lib/api/addresses.js @@ -485,7 +485,7 @@ module.exports = (db, server) => { if (!userData.address || main) { // register this address as the default address for that user try { - await db.users.collection('users').findOneAndUpdate( + await db.users.collection('users').updateOne( { _id: user }, diff --git a/lib/user-handler.js b/lib/user-handler.js index 6166e38d..16656341 100644 --- a/lib/user-handler.js +++ b/lib/user-handler.js @@ -258,7 +258,7 @@ class UserHandler { * @param {Function} callback */ rateLimitIP(meta, count, callback) { - if (!meta.ip) { + if (!meta || !meta.ip) { return callback(null, { success: true }); } let wlKey = 'rl-wl'; @@ -286,8 +286,30 @@ class UserHandler { * @param {Integer} count * @param {Function} callback */ - rateLimitUser(tokenID, count, callback) { - this.counters.ttlcounter('auth_user:' + tokenID, count, consts.USER_AUTH_FAILURES, consts.USER_AUTH_WINDOW, callback); + rateLimitUser(tokenID, meta, count, callback) { + let checkUserRateLimit = () => this.counters.ttlcounter('auth_user:' + tokenID, count, consts.USER_AUTH_FAILURES, consts.USER_AUTH_WINDOW, callback); + + if (!meta || !meta.ip) { + // not IP address to check for + return checkUserRateLimit(); + } + + let wlKey = 'rl-wl'; + // $ redis-cli + // > SADD "rl-wl" "1.2.3.4" + this.redis.sismember(wlKey, meta.ip, (err, isMember) => { + if (err) { + log.error('Redis', 'SMFAIL key=%s value=%s error=%s', wlKey, meta.ip, err.message); + // ignore errors + } + + if (isMember) { + // whitelisted IP, allow authentication attempt without rate limits + return callback(null, { success: true }); + } + + return checkUserRateLimit(); + }); } /** @@ -314,7 +336,7 @@ class UserHandler { return callback(err); } - this.rateLimitUser(tokenID, count, (err, userRes) => { + this.rateLimitUser(tokenID, meta, count, (err, userRes) => { if (err) { return callback(err); } @@ -402,8 +424,8 @@ class UserHandler { }); } - // check if there are not too many auth attemtp for that user - this.rateLimitUser(userData._id, 0, (err, res) => { + // check if there are not too many auth attempts for that user + this.rateLimitUser(userData._id, meta, 0, (err, res) => { if (err) { err.code = 'InternalDatabaseError'; return callback(err, false, userData._id); @@ -657,7 +679,6 @@ class UserHandler { // don't really care } let authEvent = r && r.insertedId; - this.rateLimitReleaseUser(userData._id, () => false); this.users.collection('asps').findOneAndUpdate( { _id: asp._id diff --git a/package.json b/package.json index a9c4f0a4..62baf7c3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "wildduck", - "version": "1.4.19", + "version": "1.4.20", "description": "IMAP/POP3 server built with Node.js and MongoDB", "main": "server.js", "scripts": { @@ -63,7 +63,7 @@ "node-forge": "0.7.6", "nodemailer": "4.6.8", "npmlog": "4.1.2", - "openpgp": "4.0.2", + "openpgp": "4.1.0", "pem": "1.13.1", "pwnedpasswords": "1.0.4", "qrcode": "1.2.2",