relay: remove usage of secret placeholder

This commit is contained in:
Girish Ramakrishnan
2025-10-08 20:01:18 +02:00
parent 9226baa63c
commit 2ad8ed5550
2 changed files with 132 additions and 123 deletions

View File

@@ -1,65 +1,5 @@
'use strict';
exports = module.exports = {
getStatus,
checkConfiguration,
listDomains,
getDomain,
clearDomains,
removePrivateFields,
setDnsRecords,
upsertDnsRecords,
validateName,
validateDisplayName,
setMailFromValidation,
setCatchAllAddress,
setMailRelay,
setMailEnabled,
setBanner,
sendTestMail,
getMailboxCount,
listMailboxes,
listAllMailboxes,
getMailbox,
addMailbox,
updateMailbox,
delMailbox,
getAlias,
getAliases,
setAliases,
searchAlias,
getListCount,
getLists,
getList,
addList,
updateList,
delList,
resolveList,
checkStatus,
OWNERTYPE_USER: 'user',
OWNERTYPE_GROUP: 'group',
OWNERTYPE_APP: 'app',
TYPE_MAILBOX: 'mailbox',
TYPE_LIST: 'list',
TYPE_ALIAS: 'alias',
_delByDomain: delByDomain,
_updateDomain: updateDomain
};
const assert = require('node:assert'),
BoxError = require('./boxerror.js'),
constants = require('./constants.js'),
@@ -171,6 +111,44 @@ function validateDisplayName(name) {
return null;
}
async function getDomain(domain) {
assert.strictEqual(typeof domain, 'string');
const result = await database.query(`SELECT ${MAILDB_FIELDS} FROM mail WHERE domain = ?`, [ domain ]);
if (result.length === 0) return null;
return postProcessDomain(result[0]);
}
async function updateDomain(domain, data) {
assert.strictEqual(typeof domain, 'string');
assert.strictEqual(typeof data, 'object');
const args = [ ];
const fields = [ ];
for (const k in data) {
if (k === 'catchAll' || k === 'banner') {
fields.push(`${k}Json = ?`);
args.push(JSON.stringify(data[k]));
} else if (k === 'relay') {
fields.push('relayJson = ?');
args.push(JSON.stringify(data[k]));
} else {
fields.push(k + ' = ?');
args.push(data[k]);
}
}
args.push(domain);
const result = await database.query('UPDATE mail SET ' + fields.join(', ') + ' WHERE domain=?', args);
if (result.affectedRows !== 1) throw new BoxError(BoxError.NOT_FOUND, 'Mail domain not found');
}
async function listDomains() {
const results = await database.query(`SELECT ${MAILDB_FIELDS} FROM mail ORDER BY domain`);
results.forEach(function (result) { postProcessDomain(result); });
return results;
}
async function checkOutboundPort25() {
return await new Promise((resolve) => {
const client = new net.Socket();
@@ -227,6 +205,15 @@ async function checkSmtpRelay(relay) {
return result;
}
function txtToDict(txt) {
const dict = {};
txt.split(';').forEach(function(v) {
const p = v.trim().split('=');
dict[p[0]]=p[1];
});
return dict;
}
async function checkDkim(mailDomain) {
assert.strictEqual(typeof mailDomain, 'object');
@@ -337,15 +324,6 @@ async function checkMx(mailDomain, mailFqdn) {
return result;
}
function txtToDict(txt) {
const dict = {};
txt.split(';').forEach(function(v) {
const p = v.trim().split('=');
dict[p[0]]=p[1];
});
return dict;
}
async function checkDmarc(mailDomain) {
assert.strictEqual(typeof mailDomain, 'object');
@@ -621,44 +599,6 @@ async function checkConfiguration() {
return { status: markdownMessage === '', message: markdownMessage };
}
async function getDomain(domain) {
assert.strictEqual(typeof domain, 'string');
const result = await database.query(`SELECT ${MAILDB_FIELDS} FROM mail WHERE domain = ?`, [ domain ]);
if (result.length === 0) return null;
return postProcessDomain(result[0]);
}
async function updateDomain(domain, data) {
assert.strictEqual(typeof domain, 'string');
assert.strictEqual(typeof data, 'object');
const args = [ ];
const fields = [ ];
for (const k in data) {
if (k === 'catchAll' || k === 'banner') {
fields.push(`${k}Json = ?`);
args.push(JSON.stringify(data[k]));
} else if (k === 'relay') {
fields.push('relayJson = ?');
args.push(JSON.stringify(data[k]));
} else {
fields.push(k + ' = ?');
args.push(data[k]);
}
}
args.push(domain);
const result = await database.query('UPDATE mail SET ' + fields.join(', ') + ' WHERE domain=?', args);
if (result.affectedRows !== 1) throw new BoxError(BoxError.NOT_FOUND, 'Mail domain not found');
}
async function listDomains() {
const results = await database.query(`SELECT ${MAILDB_FIELDS} FROM mail ORDER BY domain`);
results.forEach(function (result) { postProcessDomain(result); });
return results;
}
// https://agari.zendesk.com/hc/en-us/articles/202952749-How-long-can-my-SPF-record-be-
async function txtRecordsWithSpf(domain, mailFqdn) {
assert.strictEqual(typeof domain, 'string');
@@ -738,12 +678,11 @@ async function clearDomains() {
await database.query('DELETE FROM mail', []);
}
// remove all fields that should never be sent out via REST API
function removePrivateFields(domain) {
const result = _.pick(domain, ['domain', 'enabled', 'mailFromValidation', 'catchAll', 'relay', 'banner']);
if ('password' in result.relay) {
if ('username' in result.relay && result.relay.username === result.relay.password) result.relay.username = constants.SECRET_PLACEHOLDER;
result.relay.password = constants.SECRET_PLACEHOLDER;
if ('username' in result.relay && result.relay.username === result.relay.password) delete result.relay.username;
delete result.relay.password;
}
return result;
}
@@ -788,8 +727,10 @@ async function setMailRelay(domain, relay, options) {
if (!domain) throw new BoxError(BoxError.NOT_FOUND, 'Mail domain not found');
// inject current username/password
if (relay.username === constants.SECRET_PLACEHOLDER) relay.username = result.relay.username;
if (relay.password === constants.SECRET_PLACEHOLDER) relay.password = result.relay.password;
if ('password' in result.relay) {
if (!Object.hasOwn(relay, 'username')) relay.username = result.relay.username;
if (!Object.hasOwn(relay, 'password')) relay.password = result.relay.password;
}
if (!options.skipVerify) {
const result = await checkSmtpRelay(relay);
@@ -1235,3 +1176,63 @@ async function checkStatus() {
await notifications.pin(notifications.TYPE_MAIL_STATUS, 'Email is not configured properly', result.message, {});
}
}
exports = module.exports = {
getStatus,
checkConfiguration,
listDomains,
getDomain,
clearDomains,
removePrivateFields,
setDnsRecords,
upsertDnsRecords,
validateName,
validateDisplayName,
setMailFromValidation,
setCatchAllAddress,
setMailRelay,
setMailEnabled,
setBanner,
sendTestMail,
getMailboxCount,
listMailboxes,
listAllMailboxes,
getMailbox,
addMailbox,
updateMailbox,
delMailbox,
getAlias,
getAliases,
setAliases,
searchAlias,
getListCount,
getLists,
getList,
addList,
updateList,
delList,
resolveList,
checkStatus,
OWNERTYPE_USER: 'user',
OWNERTYPE_GROUP: 'group',
OWNERTYPE_APP: 'app',
TYPE_MAILBOX: 'mailbox',
TYPE_LIST: 'list',
TYPE_ALIAS: 'alias',
_delByDomain: delByDomain,
_updateDomain: updateDomain
};