mail: add flag to enable/disable pop3 access per mailbox
This commit is contained in:
@@ -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);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
@@ -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
@@ -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
@@ -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 });
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|||||||
Reference in New Issue
Block a user