diff --git a/CHANGES b/CHANGES index 193ea422c..598a072a2 100644 --- a/CHANGES +++ b/CHANGES @@ -1710,4 +1710,5 @@ * Show external LDAP connector * Allow IP address detection to be configurable * Add support for custom docker registry +* Resolve any lists and aliases in a mailing list diff --git a/src/ldap.js b/src/ldap.js index 7269c1dc3..9cc1aa2b0 100644 --- a/src/ldap.js +++ b/src/ldap.js @@ -365,11 +365,12 @@ function mailingListSearch(req, res, next) { if (!req.dn.rdns[0].attrs.cn) return next(new ldap.NoSuchObjectError(req.dn.toString())); - var email = req.dn.rdns[0].attrs.cn.value.toLowerCase(); - var parts = email.split('@'); + let email = req.dn.rdns[0].attrs.cn.value.toLowerCase(); + let parts = email.split('@'); if (parts.length !== 2) return next(new ldap.NoSuchObjectError(req.dn.toString())); + const name = parts[0], domain = parts[1]; - mailboxdb.getList(parts[0], parts[1], function (error, list) { + mail.resolveList(parts[0], parts[1], function (error, resolvedMembers) { if (error && error.reason === BoxError.NOT_FOUND) return next(new ldap.NoSuchObjectError(req.dn.toString())); if (error) return next(new ldap.OperationsError(error.toString())); @@ -380,9 +381,9 @@ function mailingListSearch(req, res, next) { attributes: { objectclass: ['mailGroup'], objectcategory: 'mailGroup', - cn: `${list.name}@${list.domain}`, // fully qualified - mail: `${list.name}@${list.domain}`, - mgrpRFC822MailMember: list.members // fully qualified + cn: `${name}@${domain}`, // fully qualified + mail: `${name}@${domain}`, + mgrpRFC822MailMember: resolvedMembers // fully qualified } }; diff --git a/src/mail.js b/src/mail.js index 500656c7e..1f4d13bb2 100644 --- a/src/mail.js +++ b/src/mail.js @@ -46,6 +46,7 @@ exports = module.exports = { addList: addList, updateList: updateList, removeList: removeList, + resolveList: resolveList, _readDkimPublicKeySync: readDkimPublicKeySync }; @@ -1268,3 +1269,50 @@ function removeList(name, domain, auditSource, callback) { callback(); }); } + +function resolveList(listName, listDomain, callback) { + assert.strictEqual(typeof listName, 'string'); + assert.strictEqual(typeof listDomain, 'string'); + assert.strictEqual(typeof callback, 'function'); + + getDomains(function (error, mailDomains) { + if (error) return callback(error); + + const mailInDomains = mailDomains.filter(function (d) { return d.enabled; }).map(function (d) { return d.domain; }).join(','); + + mailboxdb.getList(listName, listDomain, function (error, list) { + if (error) return callback(error); + + let result = [], toResolve = list.members.slice(), visited = []; // slice creates a copy of array + + async.whilst(() => toResolve.length != 0, function (iteratorCallback) { + const toProcess = toResolve.shift(); + const parts = toProcess.split('@'); + const memberName = parts[0].split('+')[0], memberDomain = parts[1]; + + if (!mailInDomains.includes(memberDomain)) { result.push(toProcess); return iteratorCallback(); } // external domain + + const member =`${memberName}@${memberDomain}`; // cleaned up without any '+' subaddress + if (visited.includes(member)) { + debug(`resolveList: list ${listName}@${listDomain} has a recursion at member ${member}`); + return iteratorCallback(); + } + visited.push(member); + + mailboxdb.get(memberName, memberDomain, function (error, entry) { + if (error && error.reason == BoxError.NOT_FOUND) { result.push(member); return iteratorCallback(); } + if (error) return iteratorCallback(error); + + if (entry.type === mailboxdb.TYPE_MAILBOX) { result.push(member); return iteratorCallback(); } + // no need to resolve alias because we only allow one level and within same domain + if (entry.type === mailboxdb.TYPE_ALIAS) { result.push(`${entry.aliasTarget}@${entry.domain}`); return iteratorCallback(); } + + toResolve = toResolve.concat(entry.members); + iteratorCallback(); + }); + }, function (error) { + callback(error, result); + }); + }); + }); +} \ No newline at end of file diff --git a/src/mailboxdb.js b/src/mailboxdb.js index 60bdf0b27..882653adc 100644 --- a/src/mailboxdb.js +++ b/src/mailboxdb.js @@ -12,6 +12,7 @@ exports = module.exports = { listMailboxes: listMailboxes, getLists: getLists, + get: get, getMailbox: getMailbox, getList: getList, getAlias: getAlias, @@ -169,6 +170,20 @@ function updateName(oldName, oldDomain, newName, newDomain, callback) { }); } +function get(name, domain, callback) { + assert.strictEqual(typeof name, 'string'); + assert.strictEqual(typeof domain, 'string'); + assert.strictEqual(typeof callback, 'function'); + + database.query('SELECT ' + MAILBOX_FIELDS + ' FROM mailboxes WHERE name = ? AND domain = ?', + [ name, domain ], function (error, results) { + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); + if (results.length === 0) return callback(new BoxError(BoxError.NOT_FOUND, 'Mailbox not found')); + + callback(null, postProcess(results[0])); + }); +} + function getMailbox(name, domain, callback) { assert.strictEqual(typeof name, 'string'); assert.strictEqual(typeof domain, 'string');