ldap: make mailbox app passwords work with sogo
This commit is contained in:
+23
-47
@@ -466,36 +466,6 @@ async function verifyMailboxPassword(mailbox, password) {
|
||||
return verifiedUser;
|
||||
}
|
||||
|
||||
async function authenticateUserMailbox(req, res, next) {
|
||||
debug('user mailbox auth: %s (from %s)', req.dn.toString(), req.connection.ldap.id);
|
||||
|
||||
if (!req.dn.rdns[0].attrs.cn) return next(new ldap.NoSuchObjectError(req.dn.toString()));
|
||||
|
||||
const email = req.dn.rdns[0].attrs.cn.value.toLowerCase();
|
||||
const parts = email.split('@');
|
||||
if (parts.length !== 2) return next(new ldap.NoSuchObjectError(req.dn.toString()));
|
||||
|
||||
const [error, domain] = await safe(mail.getDomain(parts[1]));
|
||||
if (error) return next(new ldap.OperationsError(error.message));
|
||||
if (!domain) return next(new ldap.NoSuchObjectError(req.dn.toString()));
|
||||
|
||||
if (!domain.enabled) return next(new ldap.NoSuchObjectError(req.dn.toString()));
|
||||
|
||||
const [getMailboxError, mailbox] = await safe(mail.getMailbox(parts[0], parts[1]));
|
||||
if (getMailboxError) return next(new ldap.OperationsError(getMailboxError.message));
|
||||
if (!mailbox) return next(new ldap.NoSuchObjectError(req.dn.toString()));
|
||||
if (!mailbox.active) return next(new ldap.NoSuchObjectError(req.dn.toString()));
|
||||
|
||||
const [verifyError, result] = await safe(verifyMailboxPassword(mailbox, req.credentials || ''));
|
||||
if (verifyError && verifyError.reason === BoxError.NOT_FOUND) return next(new ldap.NoSuchObjectError(req.dn.toString()));
|
||||
if (verifyError && verifyError.reason === BoxError.INVALID_CREDENTIALS) return next(new ldap.InvalidCredentialsError(req.dn.toString()));
|
||||
if (verifyError) return next(new ldap.OperationsError(verifyError.message));
|
||||
|
||||
eventlog.upsertLoginEvent(eventlog.ACTION_USER_LOGIN, { authType: 'ldap', mailboxId: email }, { userId: result.id, user: users.removePrivateFields(result) });
|
||||
|
||||
res.end();
|
||||
}
|
||||
|
||||
async function authenticateSftp(req, res, next) {
|
||||
debug('sftp auth: %s (from %s)', req.dn.toString(), req.connection.ldap.id);
|
||||
|
||||
@@ -573,25 +543,24 @@ async function verifyAppMailboxPassword(serviceId, username, password) {
|
||||
if (!result.some(r => r.name.endsWith(`${pattern}_USERNAME`) && r.value === username)) throw new BoxError(BoxError.INVALID_CREDENTIALS);
|
||||
}
|
||||
|
||||
async function authenticateMail(req, res, next) {
|
||||
debug('authenticateMail: %s (from %s)', req.dn.toString(), req.connection.ldap.id);
|
||||
async function authenticateService(serviceId, dn, req, res, next) {
|
||||
debug(`authenticateService: ${req.dn.toString()} (from ${req.connection.ldap.id})`);
|
||||
|
||||
if (!req.dn.rdns[0].attrs.cn) return next(new ldap.NoSuchObjectError(req.dn.toString()));
|
||||
if (!dn.rdns[0].attrs.cn) return next(new ldap.NoSuchObjectError(dn.toString()));
|
||||
|
||||
const email = req.dn.rdns[0].attrs.cn.value.toLowerCase();
|
||||
const email = dn.rdns[0].attrs.cn.value.toLowerCase();
|
||||
const parts = email.split('@');
|
||||
if (parts.length !== 2) return next(new ldap.NoSuchObjectError(req.dn.toString()));
|
||||
if (parts.length !== 2) return next(new ldap.NoSuchObjectError(dn.toString()));
|
||||
|
||||
const knownServices = [ 'msa', 'imap', 'pop3', 'sieve' ];
|
||||
const serviceId = req.dn.rdns[1].attrs.ou.value.toLowerCase();
|
||||
const knownServices = [ 'msa', 'imap', 'pop3', 'sieve', 'sogo' ];
|
||||
if (!knownServices.includes(serviceId)) return next(new ldap.OperationsError('Invalid DN. Unknown service'));
|
||||
|
||||
const [error, domain] = await safe(mail.getDomain(parts[1]));
|
||||
if (error) return next(new ldap.OperationsError(error.message));
|
||||
if (!domain) return next(new ldap.NoSuchObjectError(req.dn.toString()));
|
||||
if (!domain) return next(new ldap.NoSuchObjectError(dn.toString()));
|
||||
|
||||
const serviceNeedsMailbox = serviceId === 'imap' || serviceId === 'sieve' || serviceId === 'pop3';
|
||||
if (serviceNeedsMailbox && !domain.enabled) return next(new ldap.NoSuchObjectError(req.dn.toString()));
|
||||
const serviceNeedsMailbox = serviceId === 'imap' || serviceId === 'sieve' || serviceId === 'pop3' || serviceId === 'sogo';
|
||||
if (serviceNeedsMailbox && !domain.enabled) return next(new ldap.NoSuchObjectError(dn.toString()));
|
||||
|
||||
const [getMailboxError, mailbox] = await safe(mail.getMailbox(parts[0], parts[1]));
|
||||
if (getMailboxError) return next(new ldap.OperationsError(getMailboxError.message));
|
||||
@@ -599,20 +568,20 @@ async function authenticateMail(req, res, next) {
|
||||
|
||||
const [appPasswordError] = await safe(verifyAppMailboxPassword(serviceId, email, req.credentials || ''));
|
||||
if (!appPasswordError) { // validated as app
|
||||
if (serviceNeedsMailbox && (!mailbox || !mailbox.active)) return next(new ldap.NoSuchObjectError(req.dn.toString()));
|
||||
if (serviceNeedsMailbox && (!mailbox || !mailbox.active)) return next(new ldap.NoSuchObjectError(dn.toString()));
|
||||
return res.end();
|
||||
}
|
||||
|
||||
if (appPasswordError && appPasswordError.reason === BoxError.INVALID_CREDENTIALS) return next(new ldap.InvalidCredentialsError(req.dn.toString()));
|
||||
if (appPasswordError && appPasswordError.reason === BoxError.INVALID_CREDENTIALS) return next(new ldap.InvalidCredentialsError(dn.toString()));
|
||||
if (appPasswordError && appPasswordError.reason !== BoxError.NOT_FOUND) return next(new ldap.OperationsError(appPasswordError.message));
|
||||
|
||||
// user password check requires an active mailbox
|
||||
if (!mailbox) return next(new ldap.NoSuchObjectError(req.dn.toString()));
|
||||
if (!mailbox.active) return next(new ldap.NoSuchObjectError(req.dn.toString()));
|
||||
if (!mailbox) return next(new ldap.NoSuchObjectError(dn.toString()));
|
||||
if (!mailbox.active) return next(new ldap.NoSuchObjectError(dn.toString()));
|
||||
|
||||
const [verifyError, result] = await safe(verifyMailboxPassword(mailbox, req.credentials || ''));
|
||||
if (verifyError && verifyError.reason === BoxError.NOT_FOUND) return next(new ldap.NoSuchObjectError(req.dn.toString()));
|
||||
if (verifyError && verifyError.reason === BoxError.INVALID_CREDENTIALS) return next(new ldap.InvalidCredentialsError(req.dn.toString()));
|
||||
if (verifyError && verifyError.reason === BoxError.NOT_FOUND) return next(new ldap.NoSuchObjectError(dn.toString()));
|
||||
if (verifyError && verifyError.reason === BoxError.INVALID_CREDENTIALS) return next(new ldap.InvalidCredentialsError(dn.toString()));
|
||||
if (verifyError) return next(new ldap.OperationsError(verifyError.message));
|
||||
|
||||
eventlog.upsertLoginEvent(eventlog.ACTION_USER_LOGIN, { authType: 'ldap', mailboxId: email }, { userId: result.id, user: users.removePrivateFields(result) });
|
||||
@@ -620,6 +589,11 @@ async function authenticateMail(req, res, next) {
|
||||
res.end();
|
||||
}
|
||||
|
||||
async function authenticateMail(req, res, next) {
|
||||
if (!req.dn.rdns[1].attrs.ou) return next(new ldap.NoSuchObjectError(req.dn.toString()));
|
||||
await authenticateService(req.dn.rdns[1].attrs.ou.value.toLowerCase(), req.dn, req, res, next);
|
||||
}
|
||||
|
||||
async function start() {
|
||||
const logger = {
|
||||
trace: NOOP,
|
||||
@@ -642,7 +616,9 @@ async function start() {
|
||||
|
||||
// http://www.ietf.org/proceedings/43/I-D/draft-srivastava-ldap-mail-00.txt
|
||||
gServer.search('ou=mailboxes,dc=cloudron', mailboxSearch); // haraka (address translation), dovecot (LMTP), sogo (mailbox search)
|
||||
gServer.bind('ou=mailboxes,dc=cloudron', authenticateUserMailbox); // apps like sogo can use domain=${domain} to authenticate a mailbox
|
||||
gServer.bind('ou=mailboxes,dc=cloudron', async function (req, res, next) { // used for sogo only. this route happens only at sogo login time. after that it will use imap ldap route
|
||||
await authenticateService('sogo', req.dn, req, res, next);
|
||||
});
|
||||
gServer.search('ou=mailaliases,dc=cloudron', mailAliasSearch); // haraka
|
||||
gServer.search('ou=mailinglists,dc=cloudron', mailingListSearch); // haraka
|
||||
|
||||
|
||||
Reference in New Issue
Block a user