mail: add flag to enable/disable pop3 access per mailbox

This commit is contained in:
Girish Ramakrishnan
2021-10-08 10:15:48 -07:00
parent 9414041ba8
commit 000db4e33d
6 changed files with 30 additions and 6 deletions
+1
View File
@@ -2355,4 +2355,5 @@
* postgresql: fix restore issue with long table names * postgresql: fix restore issue with long table names
* recvmail: make the addon work again * recvmail: make the addon work again
* mail: update solr to 8.10.0 * mail: update solr to 8.10.0
* mail: POP3 support
@@ -0,0 +1,16 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE mailboxes ADD COLUMN enablePop3 BOOLEAN DEFAULT 0', function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE mailboxes DROP COLUMN enablePop3', function (error) {
if (error) console.error(error);
callback(error);
});
};
+1
View File
@@ -206,6 +206,7 @@ CREATE TABLE IF NOT EXISTS mailboxes(
creationTime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, creationTime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
domain VARCHAR(128), domain VARCHAR(128),
active BOOLEAN DEFAULT 1, active BOOLEAN DEFAULT 1,
enablePop3 BOOLEAN DEFAULT 0,
FOREIGN KEY(domain) REFERENCES mail(domain), FOREIGN KEY(domain) REFERENCES mail(domain),
FOREIGN KEY(aliasDomain) REFERENCES mail(domain), FOREIGN KEY(aliasDomain) REFERENCES mail(domain),
+6 -3
View File
@@ -616,18 +616,20 @@ async function authenticateMail(req, res, next) {
const parts = email.split('@'); 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(req.dn.toString()));
const serviceId = req.dn.rdns[1].attrs.ou.value.toLowerCase(); // msa, imap, sieve const knownServices = [ 'msa', 'imap', 'pop3', 'sieve' ];
if (serviceId !== 'msa' && serviceId !== 'imap' && serviceId !== 'sieve') return next(new ldap.OperationsError('Invalid DN')); const serviceId = req.dn.rdns[1].attrs.ou.value.toLowerCase();
if (!knownServices.includes(serviceId)) return next(new ldap.OperationsError('Invalid DN. Unknown service'));
const [error, domain] = await safe(mail.getDomain(parts[1])); const [error, domain] = await safe(mail.getDomain(parts[1]));
if (error) return next(new ldap.OperationsError(error.message)); 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(req.dn.toString()));
const serviceNeedsMailbox = serviceId === 'imap' || serviceId === 'sieve'; const serviceNeedsMailbox = serviceId === 'imap' || serviceId === 'sieve' || serviceId === 'pop3';
if (serviceNeedsMailbox && !domain.enabled) return next(new ldap.NoSuchObjectError(req.dn.toString())); if (serviceNeedsMailbox && !domain.enabled) return next(new ldap.NoSuchObjectError(req.dn.toString()));
const [getMailboxError, mailbox] = await safe(mail.getMailbox(parts[0], parts[1])); const [getMailboxError, mailbox] = await safe(mail.getMailbox(parts[0], parts[1]));
if (getMailboxError) return next(new ldap.OperationsError(getMailboxError.message)); if (getMailboxError) return next(new ldap.OperationsError(getMailboxError.message));
if (serviceId === 'pop3' && !mailbox.enablePop3) return next(new ldap.OperationsError('POP3 is not enabled'));
const [appPasswordError] = await safe(verifyAppMailboxPassword(serviceId, email, req.credentials || '')); const [appPasswordError] = await safe(verifyAppMailboxPassword(serviceId, email, req.credentials || ''));
if (!appPasswordError) { // validated as app if (!appPasswordError) { // validated as app
@@ -681,6 +683,7 @@ async function start() {
gServer.bind('ou=imap,dc=cloudron', authenticateMail); // dovecot (IMAP auth) gServer.bind('ou=imap,dc=cloudron', authenticateMail); // dovecot (IMAP auth)
gServer.bind('ou=msa,dc=cloudron', authenticateMail); // haraka (MSA auth) gServer.bind('ou=msa,dc=cloudron', authenticateMail); // haraka (MSA auth)
gServer.bind('ou=sieve,dc=cloudron', authenticateMail); // dovecot (sieve auth) gServer.bind('ou=sieve,dc=cloudron', authenticateMail); // dovecot (sieve auth)
gServer.bind('ou=pop3,dc=cloudron', authenticateMail); // dovecot (pop3 auth)
gServer.bind('ou=sftp,dc=cloudron', authenticateSftp); // sftp gServer.bind('ou=sftp,dc=cloudron', authenticateSftp); // sftp
gServer.search('ou=sftp,dc=cloudron', userSearchSftp); gServer.search('ou=sftp,dc=cloudron', userSearchSftp);
+5 -3
View File
@@ -106,7 +106,7 @@ const assert = require('assert'),
const DNS_OPTIONS = { timeout: 5000 }; const DNS_OPTIONS = { timeout: 5000 };
const REMOVE_MAILBOX_CMD = path.join(__dirname, 'scripts/rmmailbox.sh'); const REMOVE_MAILBOX_CMD = path.join(__dirname, 'scripts/rmmailbox.sh');
const MAILBOX_FIELDS = [ 'name', 'type', 'ownerId', 'ownerType', 'aliasName', 'aliasDomain', 'creationTime', 'membersJson', 'membersOnly', 'domain', 'active' ].join(','); const MAILBOX_FIELDS = [ 'name', 'type', 'ownerId', 'ownerType', 'aliasName', 'aliasDomain', 'creationTime', 'membersJson', 'membersOnly', 'domain', 'active', 'enablePop3' ].join(',');
const MAILDB_FIELDS = [ 'domain', 'enabled', 'mailFromValidation', 'catchAllJson', 'relayJson', 'dkimSelector', 'bannerJson' ].join(','); const MAILDB_FIELDS = [ 'domain', 'enabled', 'mailFromValidation', 'catchAllJson', 'relayJson', 'dkimSelector', 'bannerJson' ].join(',');
function postProcessMailbox(data) { function postProcessMailbox(data) {
@@ -115,6 +115,7 @@ function postProcessMailbox(data) {
data.membersOnly = !!data.membersOnly; data.membersOnly = !!data.membersOnly;
data.active = !!data.active; data.active = !!data.active;
data.enablePop3 = !!data.enablePop3;
return data; return data;
} }
@@ -1216,10 +1217,11 @@ async function updateMailbox(name, domain, data, auditSource) {
assert.strictEqual(typeof data, 'object'); assert.strictEqual(typeof data, 'object');
assert.strictEqual(typeof auditSource, 'object'); assert.strictEqual(typeof auditSource, 'object');
const { ownerId, ownerType, active } = data; const { ownerId, ownerType, active, enablePop3 } = data;
assert.strictEqual(typeof ownerId, 'string'); assert.strictEqual(typeof ownerId, 'string');
assert.strictEqual(typeof ownerType, 'string'); assert.strictEqual(typeof ownerType, 'string');
assert.strictEqual(typeof active, 'boolean'); assert.strictEqual(typeof active, 'boolean');
assert.strictEqual(typeof enablePop3, 'boolean');
name = name.toLowerCase(); name = name.toLowerCase();
@@ -1228,7 +1230,7 @@ async function updateMailbox(name, domain, data, auditSource) {
const mailbox = await getMailbox(name, domain); const mailbox = await getMailbox(name, domain);
if (!mailbox) throw new BoxError(BoxError.NOT_FOUND, 'No such mailbox'); if (!mailbox) throw new BoxError(BoxError.NOT_FOUND, 'No such mailbox');
const result = await database.query('UPDATE mailboxes SET ownerId = ?, ownerType = ?, active = ? WHERE name = ? AND domain = ?', [ ownerId, ownerType, active, name, domain ]); const result = await database.query('UPDATE mailboxes SET ownerId = ?, ownerType = ?, active = ?, enablePop3 = ? WHERE name = ? AND domain = ?', [ ownerId, ownerType, active, enablePop3, name, domain ]);
if (result.affectedRows === 0) throw new BoxError(BoxError.NOT_FOUND, 'Mailbox not found'); if (result.affectedRows === 0) throw new BoxError(BoxError.NOT_FOUND, 'Mailbox not found');
eventlog.add(eventlog.ACTION_MAIL_MAILBOX_UPDATE, auditSource, { name, domain, oldUserId: mailbox.userId, ownerId, ownerType, active }); eventlog.add(eventlog.ACTION_MAIL_MAILBOX_UPDATE, auditSource, { name, domain, oldUserId: mailbox.userId, ownerId, ownerType, active });
+1
View File
@@ -189,6 +189,7 @@ async function updateMailbox(req, res, next) {
if (typeof req.body.ownerId !== 'string') return next(new HttpError(400, 'ownerId must be a string')); if (typeof req.body.ownerId !== 'string') return next(new HttpError(400, 'ownerId must be a string'));
if (typeof req.body.ownerType !== 'string') return next(new HttpError(400, 'ownerType must be a string')); if (typeof req.body.ownerType !== 'string') return next(new HttpError(400, 'ownerType must be a string'));
if (typeof req.body.active !== 'boolean') return next(new HttpError(400, 'active must be a boolean')); if (typeof req.body.active !== 'boolean') return next(new HttpError(400, 'active must be a boolean'));
if (typeof req.body.enablePop3 !== 'boolean') return next(new HttpError(400, 'enablePop3 must be a boolean'));
const [error] = await safe(mail.updateMailbox(req.params.name, req.params.domain, req.body, AuditSource.fromRequest(req))); const [error] = await safe(mail.updateMailbox(req.params.name, req.params.domain, req.body, AuditSource.fromRequest(req)));
if (error) return next(BoxError.toHttpError(error)); if (error) return next(BoxError.toHttpError(error));