diff --git a/src/ldap.js b/src/ldap.js index 1a1313af9..d4656a2ae 100644 --- a/src/ldap.js +++ b/src/ldap.js @@ -258,14 +258,11 @@ function mailboxSearch(req, res, next) { if (!req.dn.rdns[0].attrs.cn) return next(new ldap.NoSuchObjectError(req.dn.toString())); - var name = req.dn.rdns[0].attrs.cn.value.toLowerCase(); - // allow login via email - var parts = name.split('@'); - if (parts[1] === config.fqdn()) { - name = parts[0]; - } + var email = req.dn.rdns[0].attrs.cn.value.toLowerCase(); + var parts = email.split('@'); + if (parts.length !== 2) return next(new ldap.NoSuchObjectError(req.dn.toString())); - mailboxdb.getMailbox(name, config.fqdn(), function (error, mailbox) { + mailboxdb.getMailbox(parts[0], parts[1], function (error, mailbox) { if (error && error.reason === DatabaseError.NOT_FOUND) return next(new ldap.NoSuchObjectError(req.dn.toString())); if (error) return next(new ldap.OperationsError(error.toString())); @@ -276,7 +273,7 @@ function mailboxSearch(req, res, next) { objectcategory: 'mailbox', cn: mailbox.name, uid: mailbox.name, - mail: mailbox.name + '@' + config.fqdn(), + mail: mailbox.name + '@' + mailbox.domain, ownerType: mailbox.ownerType } }; @@ -298,7 +295,11 @@ function mailAliasSearch(req, res, next) { if (!req.dn.rdns[0].attrs.cn) return next(new ldap.NoSuchObjectError(req.dn.toString())); - mailboxdb.getAlias(req.dn.rdns[0].attrs.cn.value.toLowerCase(), config.fqdn(), function (error, alias) { + var email = req.dn.rdns[0].attrs.cn.value.toLowerCase(); + var parts = email.split('@'); + if (parts.length !== 2) return next(new ldap.NoSuchObjectError(req.dn.toString())); + + mailboxdb.getAlias(parts[0], parts[1], function (error, alias) { if (error && error.reason === DatabaseError.NOT_FOUND) return next(new ldap.NoSuchObjectError(req.dn.toString())); if (error) return next(new ldap.OperationsError(error.toString())); @@ -331,7 +332,11 @@ function mailingListSearch(req, res, next) { if (!req.dn.rdns[0].attrs.cn) return next(new ldap.NoSuchObjectError(req.dn.toString())); - mailboxdb.getGroup(req.dn.rdns[0].attrs.cn.value.toLowerCase(), config.fqdn(), function (error, group) { + var email = req.dn.rdns[0].attrs.cn.value.toLowerCase(); + var parts = email.split('@'); + if (parts.length !== 2) return next(new ldap.NoSuchObjectError(req.dn.toString())); + + mailboxdb.getGroup(parts[0], parts[1], function (error, group) { if (error && error.reason === DatabaseError.NOT_FOUND) return next(new ldap.NoSuchObjectError(req.dn.toString())); if (error) return next(new ldap.OperationsError(error.toString())); @@ -342,7 +347,7 @@ function mailingListSearch(req, res, next) { objectclass: ['mailGroup'], objectcategory: 'mailGroup', cn: group.name, - mail: group.name + '@' + config.fqdn(), + mail: group.name + '@' + group.domain, mgrpRFC822MailMember: group.members } }; @@ -411,15 +416,11 @@ function authorizeUserForApp(req, res, next) { function authenticateMailbox(req, res, next) { if (!req.dn.rdns[0].attrs.cn) return next(new ldap.NoSuchObjectError(req.dn.toString())); - var name = req.dn.rdns[0].attrs.cn.value.toLowerCase(); + var email = req.dn.rdns[0].attrs.cn.value.toLowerCase(); + var parts = email.split('@'); + if (parts.length !== 2) return next(new ldap.NoSuchObjectError(req.dn.toString())); - // allow login via email - var parts = name.split('@'); - if (parts[1] === config.fqdn()) { - name = parts[0]; - } - - mailboxdb.getMailbox(name, config.fqdn(), function (error, mailbox) { + mailboxdb.getMailbox(parts[0], parts[1], function (error, mailbox) { if (error && error.reason === DatabaseError.NOT_FOUND) return next(new ldap.NoSuchObjectError(req.dn.toString())); if (error) return next(new ldap.OperationsError(error.message)); @@ -438,9 +439,12 @@ function authenticateMailbox(req, res, next) { return res.end(); }); } else if (mailbox.ownerType === mailboxdb.TYPE_USER) { - authenticateUser(req, res, function (error) { - if (error) return next(error); - eventlog.add(eventlog.ACTION_USER_LOGIN, { authType: 'ldap', mailboxId: name }, { userId: req.user.username }); + user.verifyWithEmail(email, req.credentials || '', function (error, user) { + if (error && error.reason === UserError.NOT_FOUND) return next(new ldap.NoSuchObjectError(req.dn.toString())); + if (error && error.reason === UserError.WRONG_PASSWORD) return next(new ldap.InvalidCredentialsError(req.dn.toString())); + if (error) return next(new ldap.OperationsError(error.message)); + + eventlog.add(eventlog.ACTION_USER_LOGIN, { authType: 'ldap', mailboxId: email }, { userId: user.username }); res.end(); }); } else { diff --git a/src/test/ldap-test.js b/src/test/ldap-test.js index 60dde96b0..e5e36b879 100644 --- a/src/test/ldap-test.js +++ b/src/test/ldap-test.js @@ -729,8 +729,8 @@ describe('Ldap', function () { } describe('search mailbox', function () { - it('get specific mailbox', function (done) { - ldapSearch('cn=' + USER_0.username + ',ou=mailboxes,dc=cloudron', 'objectclass=mailbox', function (error, entries) { + it('get specific mailbox by email', function (done) { + ldapSearch('cn=' + USER_0.username + '@example.com,ou=mailboxes,dc=cloudron', 'objectclass=mailbox', function (error, entries) { if (error) return done(error); expect(entries.length).to.equal(1); expect(entries[0].cn).to.equal(USER_0.username.toLowerCase()); @@ -738,24 +738,22 @@ describe('Ldap', function () { }); }); - it('get specific mailbox by email', function (done) { - ldapSearch('cn=' + USER_0.username + '@' + config.fqdn() + ',ou=mailboxes,dc=cloudron', 'objectclass=mailbox', function (error, entries) { - if (error) return done(error); - expect(entries.length).to.equal(1); - expect(entries[0].cn).to.equal(USER_0.username.toLowerCase()); + it('cannot get mailbox with just name', function (done) { + ldapSearch('cn=' + USER_0.username + ',ou=mailboxes,dc=cloudron', 'objectclass=mailbox', function (error, entries) { + expect(error).to.be.a(ldap.NoSuchObjectError); done(); }); }); it('cannot get alias as a mailbox', function (done) { - ldapSearch('cn=' + USER_0_ALIAS + ',ou=mailboxes,dc=cloudron', 'objectclass=mailbox', function (error) { + ldapSearch('cn=' + USER_0_ALIAS + '@example.com,ou=mailboxes,dc=cloudron', 'objectclass=mailbox', function (error) { expect(error).to.be.a(ldap.NoSuchObjectError); done(); }); }); it('non-existent mailbox', function (done) { - ldapSearch('cn=random,ou=mailboxes,dc=cloudron', 'objectclass=mailbox', function (error) { + ldapSearch('cn=random@example.com,ou=mailboxes,dc=cloudron', 'objectclass=mailbox', function (error) { expect(error).to.be.a(ldap.NoSuchObjectError); done(); }); @@ -764,7 +762,7 @@ describe('Ldap', function () { describe('search aliases', function () { it('get specific alias', function (done) { - ldapSearch('cn=' + USER_0_ALIAS + ',ou=mailaliases,dc=cloudron', 'objectclass=nismailalias', function (error, entries) { + ldapSearch('cn=' + USER_0_ALIAS + '@example.com,ou=mailaliases,dc=cloudron', 'objectclass=nismailalias', function (error, entries) { if (error) return done(error); expect(entries.length).to.equal(1); expect(entries[0].cn).to.equal('asterix'); @@ -774,14 +772,14 @@ describe('Ldap', function () { }); it('cannot get mailbox as alias', function (done) { - ldapSearch('cn=' + USER_0.username + ',ou=mailaliases,dc=cloudron', 'objectclass=nismailalias', function (error) { + ldapSearch('cn=' + USER_0.username + '@example.com,ou=mailaliases,dc=cloudron', 'objectclass=nismailalias', function (error) { expect(error).to.be.a(ldap.NoSuchObjectError); done(); }); }); it('non-existent alias', function (done) { - ldapSearch('cn=random,ou=mailaliases,dc=cloudron', 'objectclass=mailbox', function (error) { + ldapSearch('cn=random@example.com,ou=mailaliases,dc=cloudron', 'objectclass=mailbox', function (error) { expect(error).to.be.a(ldap.NoSuchObjectError); done(); }); @@ -790,7 +788,7 @@ describe('Ldap', function () { describe('search groups', function () { it('get specific alias', function (done) { - ldapSearch('cn=' + USER_0_ALIAS + ',ou=mailaliases,dc=cloudron', 'objectclass=nismailalias', function (error, entries) { + ldapSearch('cn=' + USER_0_ALIAS + '@example.com,ou=mailaliases,dc=cloudron', 'objectclass=nismailalias', function (error, entries) { if (error) return done(error); expect(entries.length).to.equal(1); expect(entries[0].cn).to.equal('asterix'); @@ -800,7 +798,7 @@ describe('Ldap', function () { }); it('non-existent alias', function (done) { - ldapSearch('cn=random,ou=mailaliases,dc=cloudron', 'objectclass=mailbox', function (error) { + ldapSearch('cn=random@example.com,ou=mailaliases,dc=cloudron', 'objectclass=mailbox', function (error) { expect(error).to.be.a(ldap.NoSuchObjectError); done(); }); @@ -809,7 +807,7 @@ describe('Ldap', function () { describe('search mailing list', function () { it('get specific list', function (done) { - ldapSearch('cn=developers,ou=mailinglists,dc=cloudron', 'objectclass=mailGroup', function (error, entries) { + ldapSearch('cn=developers@example.com,ou=mailinglists,dc=cloudron', 'objectclass=mailGroup', function (error, entries) { if (error) return done(error); expect(entries.length).to.equal(1); expect(entries[0].cn).to.equal('developers'); @@ -819,7 +817,7 @@ describe('Ldap', function () { }); it('non-existent list', function (done) { - ldapSearch('cn=random,ou=mailinglists,dc=cloudron', 'objectclass=mailGroup', function (error) { + ldapSearch('cn=random@example.com,ou=mailinglists,dc=cloudron', 'objectclass=mailGroup', function (error) { expect(error).to.be.a(ldap.NoSuchObjectError); done(); }); @@ -827,32 +825,32 @@ describe('Ldap', function () { }); describe('user sendmail bind', function () { - it('does not allow with invalid password', function (done) { + it('email disabled - cannot find domain email', function (done) { var client = ldap.createClient({ url: 'ldap://127.0.0.1:' + config.get('ldapPort') }); - client.bind('cn=' + USER_0.username + ',ou=sendmail,dc=cloudron', USER_0.password + 'nope', function (error) { - expect(error).to.be.a(ldap.InvalidCredentialsError); + client.bind('cn=' + USER_0.username + '@example.com,ou=sendmail,dc=cloudron', USER_0.password + 'nope', function (error) { + expect(error).to.be.a(ldap.NoSuchObjectError); client.unbind(done); }); }); - it('allows with valid password', function (done) { + it('email disabled - cannot find reset email', function (done) { var client = ldap.createClient({ url: 'ldap://127.0.0.1:' + config.get('ldapPort') }); - client.bind('cn=' + USER_0.username + ',ou=sendmail,dc=cloudron', USER_0.password, function (error) { - client.unbind(); - done(error); + client.bind('cn=' + USER_0.email + ',ou=sendmail,dc=cloudron', USER_0.password + 'nope', function (error) { + expect(error).to.be.a(ldap.NoSuchObjectError); + client.unbind(done); }); }); - it('allows with valid email', function (done) { + it('email enabled - allows with valid email', function (done) { // user settingsdb instead of settings, to not trigger further events settingsdb.set(settings.MAIL_CONFIG_KEY, JSON.stringify({ enabled: true }), function (error) { expect(error).not.to.be.ok(); var client = ldap.createClient({ url: 'ldap://127.0.0.1:' + config.get('ldapPort') }); - client.bind('cn=' + USER_0.username + '@' + config.fqdn() + ',ou=sendmail,dc=cloudron', USER_0.password, function (error) { + client.bind('cn=' + USER_0.username + '@example.com,ou=sendmail,dc=cloudron', USER_0.password, function (error) { expect(error).not.to.be.ok(); client.unbind(); @@ -861,13 +859,35 @@ describe('Ldap', function () { }); }); }); + + it('email enabled - does not allow with invalid password', function (done) { + // user settingsdb instead of settings, to not trigger further events + settingsdb.set(settings.MAIL_CONFIG_KEY, JSON.stringify({ enabled: true }), function (error) { + expect(error).not.to.be.ok(); + + var client = ldap.createClient({ url: 'ldap://127.0.0.1:' + config.get('ldapPort') }); + + client.bind('cn=' + USER_0.username + '@example.com,ou=sendmail,dc=cloudron', USER_0.password + 'nope', function (error) { + expect(error).to.be.a(ldap.InvalidCredentialsError); + + client.unbind(); + + settingsdb.set(settings.MAIL_CONFIG_KEY, JSON.stringify({ enabled: false }), done); + }); + }); + }); }); describe('app sendmail bind', function () { + // these tests should work even when email is disabled + before(function (done) { + settingsdb.set(settings.MAIL_CONFIG_KEY, JSON.stringify({ enabled: false }), done); + }); + it('does not allow with invalid app', function (done) { var client = ldap.createClient({ url: 'ldap://127.0.0.1:' + config.get('ldapPort') }); - client.bind('cn=hacker.app,ou=sendmail,dc=cloudron', 'nope', function (error) { + client.bind('cn=hacker.app@example.com,ou=sendmail,dc=cloudron', 'nope', function (error) { expect(error).to.be.a(ldap.NoSuchObjectError); client.unbind(done); }); @@ -876,7 +896,7 @@ describe('Ldap', function () { it('does not allow with invalid password', function (done) { var client = ldap.createClient({ url: 'ldap://127.0.0.1:' + config.get('ldapPort') }); - client.bind('cn=' + APP_0.location + '.app,ou=sendmail,dc=cloudron', 'nope', function (error) { + client.bind('cn=' + APP_0.location + '.app@example.com,ou=sendmail,dc=cloudron', 'nope', function (error) { expect(error).to.be.a(ldap.InvalidCredentialsError); client.unbind(done); }); @@ -885,7 +905,7 @@ describe('Ldap', function () { it('allows with valid password', function (done) { var client = ldap.createClient({ url: 'ldap://127.0.0.1:' + config.get('ldapPort') }); - client.bind('cn=' + APP_0.location + '.app,ou=sendmail,dc=cloudron', 'sendmailpassword', function (error) { + client.bind('cn=' + APP_0.location + '.app@example.com,ou=sendmail,dc=cloudron', 'sendmailpassword', function (error) { client.unbind(); done(error); }); @@ -893,33 +913,32 @@ describe('Ldap', function () { }); describe('user recvmail bind', function () { - it('does not allow with invalid password', function (done) { + it('email disabled - cannot find domain email', function (done) { var client = ldap.createClient({ url: 'ldap://127.0.0.1:' + config.get('ldapPort') }); - client.bind('cn=' + USER_0.username + ',ou=recvmail,dc=cloudron', USER_0.password + 'nope', function (error) { - expect(error).to.be.a(ldap.InvalidCredentialsError); + client.bind('cn=' + USER_0.username + '@example.com,ou=recvmail,dc=cloudron', USER_0.password + 'nope', function (error) { + expect(error).to.be.a(ldap.NoSuchObjectError); client.unbind(done); }); }); - it('allows with valid password', function (done) { + it('email disabled - cannot find reset email', function (done) { var client = ldap.createClient({ url: 'ldap://127.0.0.1:' + config.get('ldapPort') }); - client.bind('cn=' + USER_0.username + ',ou=recvmail,dc=cloudron', USER_0.password, function (error) { - client.unbind(); - - done(error); + client.bind('cn=' + USER_0.email + ',ou=recvmail,dc=cloudron', USER_0.password + 'nope', function (error) { + expect(error).to.be.a(ldap.NoSuchObjectError); + client.unbind(done); }); }); - it('allows with valid email', function (done) { + it('email enabled - allows with valid email', function (done) { // user settingsdb instead of settings, to not trigger further events settingsdb.set(settings.MAIL_CONFIG_KEY, JSON.stringify({ enabled: true }), function (error) { expect(error).not.to.be.ok(); var client = ldap.createClient({ url: 'ldap://127.0.0.1:' + config.get('ldapPort') }); - client.bind('cn=' + USER_0.username + '@' + config.fqdn() + ',ou=recvmail,dc=cloudron', USER_0.password, function (error) { + client.bind('cn=' + USER_0.username + '@example.com,ou=recvmail,dc=cloudron', USER_0.password, function (error) { expect(error).not.to.be.ok(); client.unbind(); @@ -928,13 +947,30 @@ describe('Ldap', function () { }); }); }); + + it('email enabled - does not allow with invalid password', function (done) { + // user settingsdb instead of settings, to not trigger further events + settingsdb.set(settings.MAIL_CONFIG_KEY, JSON.stringify({ enabled: true }), function (error) { + expect(error).not.to.be.ok(); + + var client = ldap.createClient({ url: 'ldap://127.0.0.1:' + config.get('ldapPort') }); + + client.bind('cn=' + USER_0.username + '@example.com,ou=recvmail,dc=cloudron', USER_0.password + 'nope', function (error) { + expect(error).to.be.a(ldap.InvalidCredentialsError); + + client.unbind(); + + settingsdb.set(settings.MAIL_CONFIG_KEY, JSON.stringify({ enabled: false }), done); + }); + }); + }); }); describe('app recvmail bind', function () { it('does not allow with invalid app', function (done) { var client = ldap.createClient({ url: 'ldap://127.0.0.1:' + config.get('ldapPort') }); - client.bind('cn=hacker.app,ou=recvmail,dc=cloudron', 'nope', function (error) { + client.bind('cn=hacker.app@example.com,ou=recvmail,dc=cloudron', 'nope', function (error) { expect(error).to.be.a(ldap.NoSuchObjectError); client.unbind(done); }); @@ -943,7 +979,7 @@ describe('Ldap', function () { it('does not allow with invalid password', function (done) { var client = ldap.createClient({ url: 'ldap://127.0.0.1:' + config.get('ldapPort') }); - client.bind('cn=' + APP_0.location + '.app,ou=recvmail,dc=cloudron', 'nope', function (error) { + client.bind('cn=' + APP_0.location + '.app@example.com,ou=recvmail,dc=cloudron', 'nope', function (error) { expect(error).to.be.a(ldap.InvalidCredentialsError); client.unbind(done); }); @@ -952,7 +988,7 @@ describe('Ldap', function () { it('allows with valid password', function (done) { var client = ldap.createClient({ url: 'ldap://127.0.0.1:' + config.get('ldapPort') }); - client.bind('cn=' + APP_0.location + '.app,ou=recvmail,dc=cloudron', 'recvmailpassword', function (error) { + client.bind('cn=' + APP_0.location + '.app@example.com,ou=recvmail,dc=cloudron', 'recvmailpassword', function (error) { client.unbind(); done(error);