diff --git a/lib/api/auth.js b/lib/api/auth.js index 3d5a1114..a62fa789 100644 --- a/lib/api/auth.js +++ b/lib/api/auth.js @@ -144,11 +144,9 @@ module.exports = (db, server, userHandler) => { user = auth.user; } catch (err) { let response = { - error: err.message + error: err.message, + code: 'AuthFailed' || err.code }; - if (err.code) { - response.code = err.code; - } if (user) { response.id = user.toString(); } diff --git a/lib/user-handler.js b/lib/user-handler.js index 77f24d7a..0784702a 100644 --- a/lib/user-handler.js +++ b/lib/user-handler.js @@ -3209,44 +3209,95 @@ class UserHandler { checkAddress(username, callback) { if (username.indexOf('@') < 0) { - // assume regular username + // not formatted as an address, assume regular username return callback(null, { unameview: tools.uview(username) }); } - // try to find existing email address - // we do not use resolveAddress because it is too lax - let address = tools.normalizeAddress(username, { removeLabel: true }); - this.users.collection('addresses').findOne( - { - addrview: tools.normalizeAddress(address, false, { removeDots: true }) - }, - { - projection: { - user: true - } - }, - (err, addressData) => { - if (err) { - err.code = 'InternalDatabaseError'; - return callback(err); - } + let address = tools.normalizeAddress(username, false, { + removeLabel: true, + removeDots: true + }); + let domain = address.substr(address.indexOf('@') + 1); + username = address.substr(0, address.indexOf('@')); - if (!addressData || !addressData.user) { - // fall back to username formatted as an address - return callback(null, { - unameview: tools.normalizeAddress(address, false, { - removeDots: true - }) + let aliasDomain; + let findAddressData = done => { + this.users.collection('addresses').findOne( + { + addrview: address + }, + { + projection: { + user: true + } + }, + (err, addressData) => { + if (err) { + err.code = 'InternalDatabaseError'; + return callback(err); + } + + if (addressData) { + return done(null, addressData); + } + + // check if the address uses an alias domain + this.users.collection('domainaliases').findOne({ alias: domain }, (err, aliasData) => { + if (err) { + return done(err); + } + + if (!aliasData) { + // not an alias domain, nothing to check for + return done(); + } + + aliasDomain = aliasData.domain; + this.users.collection('addresses').findOne( + { + addrview: username + '@' + aliasDomain + }, + { + projection: { + user: true + } + }, + (err, addressData) => { + if (err) { + err.code = 'InternalDatabaseError'; + return done(err); + } + return done(null, addressData); + } + ); }); } + ); + }; - callback(null, { - _id: addressData.user + findAddressData((err, addressData) => { + if (err) { + return callback(err); + } + + if (addressData && !addressData.user) { + // found a non-user address + return callback(null, false); + } + + if (!addressData) { + // fall back to username formatted as an address + return callback(null, { + unameview: address }); } - ); + + callback(null, { + _id: addressData.user + }); + }); } } diff --git a/package.json b/package.json index c35b6932..1bfdd7af 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "wildduck", - "version": "1.12.3", + "version": "1.12.4", "description": "IMAP/POP3 server built with Node.js and MongoDB", "main": "server.js", "scripts": { diff --git a/test/api-test.js b/test/api-test.js index 6ffc7319..3c59a02e 100644 --- a/test/api-test.js +++ b/test/api-test.js @@ -15,6 +15,23 @@ let userId = false; describe('API tests', function() { this.timeout(10000); // eslint-disable-line no-invalid-this + frisby + .create('POST domainaliases') + .post( + URL + '/domainaliases', + { + alias: 'jõgeva.öö', + domain: 'example.com' + }, + { json: true } + ) + .expectStatus(200) + .afterJSON(response => { + expect(response).to.exist; + expect(response.success).to.be.true; + }) + .toss(); + frisby .create('POST users') .post( @@ -61,6 +78,136 @@ describe('API tests', function() { }) .toss(); + frisby + .create('POST authenticate') + .post( + URL + '/authenticate', + { + username: 'testuser@example.com', + password: 'secretpass', + scope: 'master' + }, + { json: true } + ) + .expectStatus(200) + .afterJSON(response => { + expect(response).to.exist; + expect(response.success).to.be.true; + }) + .toss(); + + frisby + .create('POST authenticate - failure') + .post( + URL + '/authenticate', + { + username: 'testuser@example.com', + password: 'invalid', + scope: 'master' + }, + { json: true } + ) + .afterJSON(response => { + expect(response).to.exist; + expect(response.error).to.exist; + expect(response.success).to.not.be.true; + }) + .toss(); + + frisby + .create('POST authenticate - using aliasdomain') + .post( + URL + '/authenticate', + { + username: 'testuser@jõgeva.öö', + password: 'secretpass', + scope: 'master' + }, + { json: true } + ) + .expectStatus(200) + .afterJSON(response => { + expect(response).to.exist; + expect(response.success).to.be.true; + }) + .toss(); + + frisby + .create('POST authenticate - failure using aliasdomain') + .post( + URL + '/authenticate', + { + username: 'testuser@jõgeva.öö', + password: 'invalid', + scope: 'master' + }, + { json: true } + ) + .afterJSON(response => { + expect(response).to.exist; + expect(response.error).to.exist; + expect(response.success).to.not.be.true; + }) + .toss(); + + frisby + .create('POST users/{id}/asps - generate ASP') + .post( + URL + '/users/' + userId + '/asps', + { + description: 'test', + scopes: ['imap', 'smtp'], + generateMobileconfig: true + }, + { json: true } + ) + .afterJSON(response => { + expect(response).to.exist; + expect(response.error).to.not.exist; + expect(response.success).to.be.true; + expect(response.password).to.exist; + expect(response.mobileconfig).to.exist; + + let asp = response.password; + + frisby + .create('POST authenticate - success on correct scope') + .post( + URL + '/authenticate', + { + username: 'testuser@example.com', + password: asp, + scope: 'imap' + }, + { json: true } + ) + .expectStatus(200) + .afterJSON(response => { + expect(response).to.exist; + expect(response.success).to.be.true; + }) + .toss(); + + frisby + .create('POST authenticate - failure on incorrect scope') + .post( + URL + '/authenticate', + { + username: 'testuser@example.com', + password: asp, + scope: 'master' + }, + { json: true } + ) + .afterJSON(response => { + expect(response).to.exist; + expect(response.error).to.exist; + expect(response.success).to.not.be.true; + }) + .toss(); + }) + .toss(); + frisby .create('GET users/{id} – updated name') .get(URL + '/users/' + userId)