mirror of
https://github.com/nodemailer/wildduck.git
synced 2024-09-20 23:36:15 +08:00
v1.4.2
This commit is contained in:
parent
225aa9ec8b
commit
ac205c2622
|
@ -31,6 +31,8 @@ module.exports = (db, server, userHandler) => {
|
|||
* @apiSuccess {Boolean} requirePasswordChange Indicates if account hassword has been reset and should be replaced
|
||||
*
|
||||
* @apiError error Description of the error
|
||||
* @apiError [code] Error code
|
||||
* @apiError [id] User ID if the user exists
|
||||
*
|
||||
* @apiExample {curl} Example usage:
|
||||
* curl -i -XPOST http://localhost:8080/authenticate \
|
||||
|
@ -57,7 +59,9 @@ module.exports = (db, server, userHandler) => {
|
|||
* @apiErrorExample {json} Error-Response:
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "error": "Authentication failed. Invalid scope"
|
||||
* "error": "Authentication failed. Invalid scope",
|
||||
* "code": "InvalidAuthScope",
|
||||
* "id": "5b22283d45e8d47572eb0381"
|
||||
* }
|
||||
*/
|
||||
server.post('/authenticate', (req, res, next) => {
|
||||
|
@ -115,18 +119,30 @@ module.exports = (db, server, userHandler) => {
|
|||
meta.appId = result.value.appId;
|
||||
}
|
||||
|
||||
userHandler.authenticate(result.value.username, result.value.password, result.value.scope, meta, (err, authData) => {
|
||||
userHandler.authenticate(result.value.username, result.value.password, result.value.scope, meta, (err, authData, user) => {
|
||||
if (err) {
|
||||
res.json({
|
||||
let response = {
|
||||
error: err.message
|
||||
});
|
||||
};
|
||||
if (err.code) {
|
||||
response.code = err.code;
|
||||
}
|
||||
if (user) {
|
||||
response.id = user.toString();
|
||||
}
|
||||
res.json(response);
|
||||
return next();
|
||||
}
|
||||
|
||||
if (!authData) {
|
||||
res.json({
|
||||
error: 'Authentication failed'
|
||||
});
|
||||
let response = {
|
||||
error: 'Authentication failed',
|
||||
code: 'AuthFailed'
|
||||
};
|
||||
if (user) {
|
||||
response.id = user.toString();
|
||||
}
|
||||
res.json(response);
|
||||
return next();
|
||||
}
|
||||
|
||||
|
|
|
@ -320,29 +320,29 @@ class UserHandler {
|
|||
|
||||
if (!password) {
|
||||
// do not allow signing in without a password
|
||||
return callback(null, false);
|
||||
return callback(null, false, false);
|
||||
}
|
||||
|
||||
// first check if client IP is not used too much
|
||||
this.rateLimitIP(meta, 0, (err, res) => {
|
||||
if (err) {
|
||||
err.code = 'InternalDatabaseError';
|
||||
return callback(err);
|
||||
return callback(err, false, false);
|
||||
}
|
||||
|
||||
if (!res.success) {
|
||||
// too many failed attempts from this IP
|
||||
return rateLimitResponse(res, callback);
|
||||
return rateLimitResponse(res, err => callback(err, false, false));
|
||||
}
|
||||
|
||||
this.checkAddress(username, (err, query) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
return callback(err, false, false);
|
||||
}
|
||||
|
||||
if (!query) {
|
||||
// nothing to do here
|
||||
return callback(null, false);
|
||||
return callback(null, false, false);
|
||||
}
|
||||
|
||||
this.users.collection('users').findOne(
|
||||
|
@ -361,7 +361,7 @@ class UserHandler {
|
|||
(err, userData) => {
|
||||
if (err) {
|
||||
err.code = 'InternalDatabaseError';
|
||||
return callback(err);
|
||||
return callback(err, false, false);
|
||||
}
|
||||
|
||||
if (!userData) {
|
||||
|
@ -370,13 +370,13 @@ class UserHandler {
|
|||
return this.rateLimit(ustring, meta, 1, (err, res) => {
|
||||
if (err) {
|
||||
err.code = 'InternalDatabaseError';
|
||||
return callback(err);
|
||||
return callback(err, false, false);
|
||||
}
|
||||
if (!res.success) {
|
||||
// does not really matter but respond with a rate limit error, not auth fail error
|
||||
return rateLimitResponse(res, callback);
|
||||
return rateLimitResponse(res, err => callback(err, false, false));
|
||||
}
|
||||
callback(null, false);
|
||||
callback(null, false, false);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -384,18 +384,18 @@ class UserHandler {
|
|||
this.rateLimitUser(userData._id, 0, (err, res) => {
|
||||
if (err) {
|
||||
err.code = 'InternalDatabaseError';
|
||||
return callback(err);
|
||||
return callback(err, false, userData._id);
|
||||
}
|
||||
if (!res.success) {
|
||||
// too many failed attempts for this user
|
||||
return rateLimitResponse(res, callback);
|
||||
return rateLimitResponse(res, err => callback(err, false, userData._id));
|
||||
}
|
||||
|
||||
if (userData.disabled) {
|
||||
// disabled users can not log in
|
||||
meta.result = 'disabled';
|
||||
// TODO: should we send some specific error message?
|
||||
return this.logAuthEvent(userData._id, meta, () => callback(null, false));
|
||||
return this.logAuthEvent(userData._id, meta, () => callback(null, false, userData._id));
|
||||
}
|
||||
|
||||
let enabled2fa = Array.isArray(userData.enabled2fa) ? userData.enabled2fa : [].concat(userData.enabled2fa ? 'totp' : []);
|
||||
|
@ -432,6 +432,7 @@ class UserHandler {
|
|||
// try temporary password
|
||||
return bcrypt.compare(password, userData.tempPassword.password || '', (err, success) => {
|
||||
if (err) {
|
||||
err.code = 'BcryptError';
|
||||
return next(err);
|
||||
}
|
||||
if (success) {
|
||||
|
@ -455,8 +456,7 @@ class UserHandler {
|
|||
// try master password
|
||||
checkMasterPassword((err, success) => {
|
||||
if (err) {
|
||||
err.code = err.code || 'BcryptError';
|
||||
return callback(err);
|
||||
return callback(err, false, userData._id);
|
||||
}
|
||||
|
||||
if (success) {
|
||||
|
@ -472,8 +472,9 @@ class UserHandler {
|
|||
// temporary password is only valid for master
|
||||
meta.result = 'fail';
|
||||
let err = new Error('Authentication failed. Invalid scope');
|
||||
err.code = 'InvalidAuthScope';
|
||||
err.response = 'NO'; // imap response code
|
||||
return this.logAuthEvent(userData._id, meta, () => authFail(err));
|
||||
return this.logAuthEvent(userData._id, meta, () => authFail(err, false, userData._id));
|
||||
}
|
||||
|
||||
return this.logAuthEvent(userData._id, meta, () => {
|
||||
|
@ -496,11 +497,11 @@ class UserHandler {
|
|||
if (u2fAuthRequest) {
|
||||
authResponse.u2fAuthRequest = u2fAuthRequest;
|
||||
}
|
||||
authSuccess(null, authResponse);
|
||||
authSuccess(null, authResponse, userData._id);
|
||||
});
|
||||
}
|
||||
|
||||
authSuccess(null, authResponse);
|
||||
authSuccess(null, authResponse, userData._id);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -508,7 +509,7 @@ class UserHandler {
|
|||
// only master password can be used for management tasks
|
||||
meta.result = 'fail';
|
||||
meta.source = 'master';
|
||||
return this.logAuthEvent(userData._id, meta, () => authFail(null, false));
|
||||
return this.logAuthEvent(userData._id, meta, () => authFail(null, false, userData._id));
|
||||
}
|
||||
|
||||
// try application specific passwords
|
||||
|
@ -518,7 +519,7 @@ class UserHandler {
|
|||
// does not look like an application specific password
|
||||
meta.result = 'fail';
|
||||
meta.source = 'master';
|
||||
return this.logAuthEvent(userData._id, meta, () => authFail(null, false));
|
||||
return this.logAuthEvent(userData._id, meta, () => authFail(null, false, userData._id));
|
||||
}
|
||||
|
||||
let selector = getStringSelector(password);
|
||||
|
@ -531,14 +532,14 @@ class UserHandler {
|
|||
.toArray((err, asps) => {
|
||||
if (err) {
|
||||
err.code = 'InternalDatabaseError';
|
||||
return callback(err);
|
||||
return callback(err, false, userData._id);
|
||||
}
|
||||
|
||||
if (!asps || !asps.length) {
|
||||
// user does not have app specific passwords set
|
||||
meta.result = 'fail';
|
||||
meta.source = 'master';
|
||||
return this.logAuthEvent(userData._id, meta, () => authFail(null, false));
|
||||
return this.logAuthEvent(userData._id, meta, () => authFail(null, false, userData._id));
|
||||
}
|
||||
|
||||
let pos = 0;
|
||||
|
@ -546,7 +547,7 @@ class UserHandler {
|
|||
if (pos >= asps.length) {
|
||||
meta.result = 'fail';
|
||||
meta.source = 'master';
|
||||
return this.logAuthEvent(userData._id, meta, () => authFail(null, false));
|
||||
return this.logAuthEvent(userData._id, meta, () => authFail(null, false, userData._id));
|
||||
}
|
||||
|
||||
let asp = asps[pos++];
|
||||
|
@ -558,7 +559,7 @@ class UserHandler {
|
|||
bcrypt.compare(password, asp.password || '', (err, success) => {
|
||||
if (err) {
|
||||
err.code = 'BcryptError';
|
||||
return callback(err);
|
||||
return callback(err, false, userData._id);
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
|
@ -572,9 +573,12 @@ class UserHandler {
|
|||
|
||||
if (!asp.scopes.includes('*') && !asp.scopes.includes(requiredScope)) {
|
||||
meta.result = 'fail';
|
||||
return this.logAuthEvent(userData._id, meta, () =>
|
||||
authFail(new Error('Authentication failed. Invalid scope'))
|
||||
);
|
||||
return this.logAuthEvent(userData._id, meta, () => {
|
||||
let err = new Error('Authentication failed. Invalid scope');
|
||||
err.code = 'InvalidAuthScope';
|
||||
err.response = 'NO'; // imap response code
|
||||
authFail(err, false, userData._id);
|
||||
});
|
||||
}
|
||||
|
||||
meta.result = 'success';
|
||||
|
@ -596,13 +600,17 @@ class UserHandler {
|
|||
}
|
||||
},
|
||||
() => {
|
||||
authSuccess(null, {
|
||||
user: userData._id,
|
||||
username: userData.username,
|
||||
scope: requiredScope,
|
||||
asp: asp._id.toString(),
|
||||
require2fa: false // application scope never requires 2FA
|
||||
});
|
||||
authSuccess(
|
||||
null,
|
||||
{
|
||||
user: userData._id,
|
||||
username: userData.username,
|
||||
scope: requiredScope,
|
||||
asp: asp._id.toString(),
|
||||
require2fa: false // application scope never requires 2FA
|
||||
},
|
||||
userData._id
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "wildduck",
|
||||
"version": "1.4.1",
|
||||
"version": "1.4.2",
|
||||
"description": "IMAP/POP3 server built with Node.js and MongoDB",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
|
@ -27,7 +27,7 @@
|
|||
"grunt-shell-spawn": "0.3.10",
|
||||
"grunt-wait": "0.1.0",
|
||||
"icedfrisby": "1.5.0",
|
||||
"mailparser": "2.3.2",
|
||||
"mailparser": "2.3.3",
|
||||
"markdown-toc": "1.2.0",
|
||||
"mocha": "5.2.0",
|
||||
"request": "2.88.0"
|
||||
|
@ -40,7 +40,7 @@
|
|||
"he": "1.1.1",
|
||||
"html-to-text": "4.0.0",
|
||||
"humanname": "0.2.2",
|
||||
"iconv-lite": "0.4.23",
|
||||
"iconv-lite": "0.4.24",
|
||||
"ioredfour": "1.0.2-ioredis-02",
|
||||
"ioredis": "4.0.0",
|
||||
"isemail": "3.1.3",
|
||||
|
|
Loading…
Reference in a new issue