diff --git a/src/mail.js b/src/mail.js index 9b0a5e563..479666350 100644 --- a/src/mail.js +++ b/src/mail.js @@ -5,6 +5,25 @@ exports = module.exports = { getStatus: getStatus, checkRblStatus: checkRblStatus, + getMailFromValidation: getMailFromValidation, + setMailFromValidation: setMailFromValidation, + + setCatchAllAddress: setCatchAllAddress, + getCatchAllAddress: getCatchAllAddress, + + getMailRelay: getMailRelay, + setMailRelay: setMailRelay, + + getMailConfig: getMailConfig, + setMailConfig: setMailConfig, + + createMailConfig: createMailConfig, + + MAIL_FROM_VALIDATION_KEY: 'mail_from_validation', + CATCH_ALL_ADDRESS_KEY: 'catch_all_address', + MAIL_RELAY_KEY: 'mail_relay', + MAIL_CONFIG_KEY: 'mail_config', + MailError: MailError }; @@ -12,18 +31,33 @@ var assert = require('assert'), async = require('async'), cloudron = require('./cloudron.js'), config = require('./config.js'), - debug = require('debug')('box:email'), + DatabaseError = require('./databaseerror.js'), + debug = require('debug')('box:mail'), dig = require('./dig.js'), net = require('net'), nodemailer = require('nodemailer'), + paths = require('./paths.js'), + platform = require('./platform.js'), safe = require('safetydance'), - settings = require('./settings.js'), + settingsdb = require('./settingsdb.js'), smtpTransport = require('nodemailer-smtp-transport'), sysinfo = require('./sysinfo.js'), + user = require('./user.js'), util = require('util'), _ = require('underscore'); const digOptions = { server: '127.0.0.1', port: 53, timeout: 5000 }; +var NOOP_CALLBACK = function (error) { if (error) debug(error); }; + +var gDefaults = (function () { + var result = { }; + result[exports.MAIL_FROM_VALIDATION_KEY] = true; + result[exports.CATCH_ALL_ADDRESS_KEY] = [ ]; + result[exports.MAIL_RELAY_KEY] = { provider: 'cloudron-smtp' }; + result[exports.MAIL_CONFIG_KEY] = { enabled: false }; + + return result; +})(); function MailError(reason, errorOrMessage) { assert.strictEqual(typeof reason, 'string'); @@ -370,7 +404,7 @@ function getStatus(callback) { }; } - settings.getMailRelay(function (error, relay) { + getMailRelay(function (error, relay) { if (error) return callback(error); var checks = [ @@ -396,3 +430,165 @@ function getStatus(callback) { }); }); } + +// FIXME: temporary function till we move to domain API +function getAll(callback) { + assert.strictEqual(typeof callback, 'function'); + + settingsdb.getAll(function (error, settings) { + if (error) return callback(new MailError(MailError.INTERNAL_ERROR, error)); + + var result = _.extend({ }, gDefaults); + settings.forEach(function (setting) { result[setting.name] = setting.value; }); + + // convert JSON objects + [ exports.CATCH_ALL_ADDRESS_KEY, exports.MAIL_RELAY_KEY, exports.MAIL_CONFIG_KEY ].forEach(function (key) { + result[key] = typeof result[key] === 'object' ? result[key] : safe.JSON.parse(result[key]); + }); + + callback(null, result); + }); +} + +function createMailConfig(callback) { + assert.strictEqual(typeof callback, 'function'); + + const fqdn = config.fqdn(); + const mailFqdn = config.mailFqdn(); + const alertsFrom = 'no-reply@' + config.fqdn(); + + debug('createMailConfig: generating mail config'); + + user.getOwner(function (error, owner) { + var alertsTo = config.provider() === 'caas' ? [ 'support@cloudron.io' ] : [ ]; + alertsTo.concat(error ? [] : owner.email).join(','); // owner may not exist yet + + getAll(function (error, result) { + if (error) return callback(error); + + var catchAll = result[exports.CATCH_ALL_ADDRESS_KEY].map(function (c) { return `${c}@${fqdn}`; }).join(','); + var mailFromValidation = result[exports.MAIL_FROM_VALIDATION_KEY]; + + if (!safe.fs.writeFileSync(paths.ADDON_CONFIG_DIR + '/mail/mail.ini', + `mail_domains=${fqdn}\nmail_default_domain=${fqdn}\nmail_server_name=${mailFqdn}\nalerts_from=${alertsFrom}\nalerts_to=${alertsTo}\ncatch_all=${catchAll}\nmail_from_validation=${mailFromValidation}\n`, 'utf8')) { + return callback(new Error('Could not create mail var file:' + safe.error.message)); + } + + var relay = result[exports.MAIL_RELAY_KEY]; + + const enabled = relay.provider !== 'cloudron-smtp' ? true : false, + host = relay.host || '', + port = relay.port || 25, + username = relay.username || '', + password = relay.password || ''; + + if (!safe.fs.writeFileSync(paths.ADDON_CONFIG_DIR + '/mail/smtp_forward.ini', + `enable_outbound=${enabled}\nhost=${host}\nport=${port}\nenable_tls=true\nauth_type=plain\nauth_user=${username}\nauth_pass=${password}`, 'utf8')) { + return callback(new Error('Could not create mail var file:' + safe.error.message)); + } + + callback(); + }); + }); +} + +function getMailFromValidation(callback) { + assert.strictEqual(typeof callback, 'function'); + + settingsdb.get(exports.MAIL_FROM_VALIDATION_KEY, function (error, enabled) { + if (error && error.reason === DatabaseError.NOT_FOUND) return callback(null, gDefaults[exports.MAIL_FROM_VALIDATION_KEY]); + if (error) return callback(new MailError(MailError.INTERNAL_ERROR, error)); + + callback(null, !!enabled); // settingsdb holds string values only + }); +} + +function setMailFromValidation(enabled, callback) { + assert.strictEqual(typeof enabled, 'boolean'); + assert.strictEqual(typeof callback, 'function'); + + settingsdb.set(exports.MAIL_FROM_VALIDATION_KEY, enabled ? 'enabled' : '', function (error) { + if (error) return callback(new MailError(MailError.INTERNAL_ERROR, error)); + + createMailConfig(NOOP_CALLBACK); + + callback(null); + }); +} + +function getCatchAllAddress(callback) { + assert.strictEqual(typeof callback, 'function'); + + settingsdb.get(exports.CATCH_ALL_ADDRESS_KEY, function (error, value) { + if (error && error.reason === DatabaseError.NOT_FOUND) return callback(null, gDefaults[exports.CATCH_ALL_ADDRESS_KEY]); + if (error) return callback(new MailError(MailError.INTERNAL_ERROR, error)); + + callback(null, JSON.parse(value)); + }); +} + +function setCatchAllAddress(address, callback) { + assert(Array.isArray(address)); + assert.strictEqual(typeof callback, 'function'); + + settingsdb.set(exports.CATCH_ALL_ADDRESS_KEY, JSON.stringify(address), function (error) { + if (error) return callback(new MailError(MailError.INTERNAL_ERROR, error)); + + createMailConfig(NOOP_CALLBACK); + + callback(null); + }); +} + +function getMailRelay(callback) { + assert.strictEqual(typeof callback, 'function'); + + settingsdb.get(exports.MAIL_RELAY_KEY, function (error, value) { + if (error && error.reason === DatabaseError.NOT_FOUND) return callback(null, gDefaults[exports.MAIL_RELAY_KEY]); + if (error) return callback(new MailError(MailError.INTERNAL_ERROR, error)); + + callback(null, JSON.parse(value)); + }); +} + +function setMailRelay(relay, callback) { + assert.strictEqual(typeof relay, 'object'); + assert.strictEqual(typeof callback, 'function'); + + verifyRelay(relay, function (error) { + if (error) return callback(error); + + settingsdb.set(exports.MAIL_RELAY_KEY, JSON.stringify(relay), function (error) { + if (error) return callback(new MailError(MailError.INTERNAL_ERROR, error)); + + platform.restartMail(NOOP_CALLBACK); + + callback(null); + }); + }); +} + + +function getMailConfig(callback) { + assert.strictEqual(typeof callback, 'function'); + + settingsdb.get(exports.MAIL_CONFIG_KEY, function (error, value) { + if (error && error.reason === DatabaseError.NOT_FOUND) return callback(null, gDefaults[exports.MAIL_CONFIG_KEY]); + if (error) return callback(new MailError(MailError.INTERNAL_ERROR, error)); + + callback(null, JSON.parse(value)); + }); +} + +function setMailConfig(mailConfig, callback) { + assert.strictEqual(typeof mailConfig, 'object'); + assert.strictEqual(typeof callback, 'function'); + + settingsdb.set(exports.MAIL_CONFIG_KEY, JSON.stringify(mailConfig), function (error) { + if (error) return callback(new MailError(MailError.INTERNAL_ERROR, error)); + + platform.restartMail(NOOP_CALLBACK); + + callback(null); + }); +} diff --git a/src/platform.js b/src/platform.js index 4046448ef..1b3e93bda 100644 --- a/src/platform.js +++ b/src/platform.js @@ -4,7 +4,7 @@ exports = module.exports = { start: start, stop: stop, - createMailConfig: createMailConfig + restartMail: startMail }; var apps = require('./apps.js'), @@ -18,15 +18,14 @@ var apps = require('./apps.js'), hat = require('hat'), infra = require('./infra_version.js'), locker = require('./locker.js'), + mail = require('./mail.js'), nginx = require('./nginx.js'), os = require('os'), paths = require('./paths.js'), safe = require('safetydance'), semver = require('semver'), - settings = require('./settings.js'), shell = require('./shell.js'), taskmanager = require('./taskmanager.js'), - user = require('./user.js'), util = require('util'), _ = require('underscore'); @@ -41,10 +40,6 @@ function start(callback) { debug('initializing addon infrastructure'); - // restart mail container if any of these keys change - settings.events.on(settings.MAIL_CONFIG_KEY, function () { startMail(NOOP_CALLBACK); }); - settings.events.on(settings.MAIL_RELAY_KEY, function () { startMail(NOOP_CALLBACK); }); - certificates.events.on(certificates.EVENT_CERT_CHANGED, function (domain) { if (domain === '*.' + config.fqdn() || domain === config.adminFqdn()) startMail(NOOP_CALLBACK); }); @@ -240,48 +235,6 @@ function startMongodb(callback) { setTimeout(callback, 5000); } -function createMailConfig(callback) { - assert.strictEqual(typeof callback, 'function'); - - const fqdn = config.fqdn(); - const mailFqdn = config.mailFqdn(); - const alertsFrom = 'no-reply@' + config.fqdn(); - - debug('createMailConfig: generating mail config'); - - user.getOwner(function (error, owner) { - var alertsTo = config.provider() === 'caas' ? [ 'support@cloudron.io' ] : [ ]; - alertsTo.concat(error ? [] : owner.email).join(','); // owner may not exist yet - - settings.getAll(function (error, result) { - if (error) return callback(error); - - var catchAll = result[settings.CATCH_ALL_ADDRESS_KEY].map(function (c) { return `${c}@${fqdn}`; }).join(','); - var mailFromValidation = result[settings.MAIL_FROM_VALIDATION_KEY]; - - if (!safe.fs.writeFileSync(paths.ADDON_CONFIG_DIR + '/mail/mail.ini', - `mail_domains=${fqdn}\nmail_default_domain=${fqdn}\nmail_server_name=${mailFqdn}\nalerts_from=${alertsFrom}\nalerts_to=${alertsTo}\ncatch_all=${catchAll}\nmail_from_validation=${mailFromValidation}\n`, 'utf8')) { - return callback(new Error('Could not create mail var file:' + safe.error.message)); - } - - var relay = result[settings.MAIL_RELAY_KEY]; - - const enabled = relay.provider !== 'cloudron-smtp' ? true : false, - host = relay.host || '', - port = relay.port || 25, - username = relay.username || '', - password = relay.password || ''; - - if (!safe.fs.writeFileSync(paths.ADDON_CONFIG_DIR + '/mail/smtp_forward.ini', - `enable_outbound=${enabled}\nhost=${host}\nport=${port}\nenable_tls=true\nauth_type=plain\nauth_user=${username}\nauth_pass=${password}`, 'utf8')) { - return callback(new Error('Could not create mail var file:' + safe.error.message)); - } - - callback(); - }); - }); -} - function startMail(callback) { // mail (note: 2525 is hardcoded in mail container and app use this port) // MAIL_SERVER_NAME is the hostname of the mailserver i.e server uses these certs @@ -299,12 +252,12 @@ function startMail(callback) { if (!safe.fs.writeFileSync(paths.ADDON_CONFIG_DIR + '/mail/tls_cert.pem', cert)) return callback(new Error('Could not create cert file:' + safe.error.message)); if (!safe.fs.writeFileSync(paths.ADDON_CONFIG_DIR + '/mail/tls_key.pem', key)) return callback(new Error('Could not create key file:' + safe.error.message)); - settings.getMailConfig(function (error, mailConfig) { + mail.getMailConfig(function (error, mailConfig) { if (error) return callback(error); shell.execSync('startMail', 'docker rm -f mail || true'); - createMailConfig(function (error) { + mail.createMailConfig(function (error) { if (error) return callback(error); var ports = mailConfig.enabled ? '-p 587:2525 -p 993:9993 -p 4190:4190 -p 25:2525' : ''; diff --git a/src/routes/mail.js b/src/routes/mail.js index d2dc6efd2..1c3199371 100644 --- a/src/routes/mail.js +++ b/src/routes/mail.js @@ -1,10 +1,24 @@ 'use strict'; exports = module.exports = { - getStatus: getStatus + getStatus: getStatus, + + getMailFromValidation: getMailFromValidation, + setMailFromValidation: setMailFromValidation, + + getCatchAllAddress: getCatchAllAddress, + setCatchAllAddress: setCatchAllAddress, + + getMailRelay: getMailRelay, + setMailRelay: setMailRelay, + + getMailConfig: getMailConfig, + setMailConfig: setMailConfig, }; -var mail = require('../mail.js'), +var assert = require('assert'), + mail = require('../mail.js'), + MailError = mail.MailError, HttpError = require('connect-lastmile').HttpError, HttpSuccess = require('connect-lastmile').HttpSuccess; @@ -15,3 +29,95 @@ function getStatus(req, res, next) { next(new HttpSuccess(200, records)); }); } + +function getMailFromValidation(req, res, next) { + mail.getMailFromValidation(function (error, enabled) { + if (error) return next(new HttpError(500, error)); + + next(new HttpSuccess(200, { enabled: enabled })); + }); +} + +function setMailFromValidation(req, res, next) { + assert.strictEqual(typeof req.body, 'object'); + + if (typeof req.body.enabled !== 'boolean') return next(new HttpError(400, 'enabled is required')); + + mail.setMailFromValidation(req.body.enabled, function (error) { + if (error && error.reason === MailError.BAD_FIELD) return next(new HttpError(400, error.message)); + if (error) return next(new HttpError(500, error)); + + next(new HttpSuccess(202)); + }); +} + +function getCatchAllAddress(req, res, next) { + mail.getCatchAllAddress(function (error, address) { + if (error) return next(new HttpError(500, error)); + + next(new HttpSuccess(200, { address: address })); + }); +} + +function setCatchAllAddress(req, res, next) { + assert.strictEqual(typeof req.body, 'object'); + + if (!req.body.address || !Array.isArray(req.body.address)) return next(new HttpError(400, 'address array is required')); + + for (var i = 0; i < req.body.address.length; i++) { + if (typeof req.body.address[i] !== 'string') return next(new HttpError(400, 'address must be an array of string')); + } + + mail.setCatchAllAddress(req.body.address, function (error) { + if (error && error.reason === MailError.BAD_FIELD) return next(new HttpError(400, error.message)); + if (error) return next(new HttpError(500, error)); + + next(new HttpSuccess(202)); + }); +} + +function getMailRelay(req, res, next) { + mail.getMailRelay(function (error, mail) { + if (error) return next(new HttpError(500, error)); + + next(new HttpSuccess(200, mail)); + }); +} + +function setMailRelay(req, res, next) { + assert.strictEqual(typeof req.body, 'object'); + + if (typeof req.body.provider !== 'string') return next(new HttpError(400, 'provider is required')); + if ('host' in req.body && typeof req.body.host !== 'string') return next(new HttpError(400, 'host must be a string')); + if ('port' in req.body && typeof req.body.port !== 'number') return next(new HttpError(400, 'port must be a string')); + if ('username' in req.body && typeof req.body.username !== 'string') return next(new HttpError(400, 'username must be a string')); + if ('password' in req.body && typeof req.body.password !== 'string') return next(new HttpError(400, 'password must be a string')); + + mail.setMailRelay(req.body, function (error) { + if (error && error.reason === MailError.BAD_FIELD) return next(new HttpError(400, error.message)); + if (error) return next(new HttpError(500, error)); + + next(new HttpSuccess(202)); + }); +} + +function getMailConfig(req, res, next) { + mail.getMailConfig(function (error, mail) { + if (error) return next(new HttpError(500, error)); + + next(new HttpSuccess(200, mail)); + }); +} + +function setMailConfig(req, res, next) { + assert.strictEqual(typeof req.body, 'object'); + + if (typeof req.body.enabled !== 'boolean') return next(new HttpError(400, 'enabled is required')); + + mail.setMailConfig({ enabled: req.body.enabled }, function (error) { + if (error && error.reason === MailError.BAD_FIELD) return next(new HttpError(400, error.message)); + if (error) return next(new HttpError(500, error)); + + next(new HttpSuccess(202)); + }); +} diff --git a/src/routes/settings.js b/src/routes/settings.js index 86de14da2..c5f483129 100644 --- a/src/routes/settings.js +++ b/src/routes/settings.js @@ -16,18 +16,6 @@ exports = module.exports = { getTimeZone: getTimeZone, setTimeZone: setTimeZone, - getMailConfig: getMailConfig, - setMailConfig: setMailConfig, - - getMailRelay: getMailRelay, - setMailRelay: setMailRelay, - - getCatchAllAddress: getCatchAllAddress, - setCatchAllAddress: setCatchAllAddress, - - getMailFromValidation: getMailFromValidation, - setMailFromValidation: setMailFromValidation, - getAppstoreConfig: getAppstoreConfig, setAppstoreConfig: setAppstoreConfig, @@ -106,98 +94,6 @@ function setTimeZone(req, res, next) { }); } -function getMailConfig(req, res, next) { - settings.getMailConfig(function (error, mail) { - if (error) return next(new HttpError(500, error)); - - next(new HttpSuccess(200, mail)); - }); -} - -function setMailConfig(req, res, next) { - assert.strictEqual(typeof req.body, 'object'); - - if (typeof req.body.enabled !== 'boolean') return next(new HttpError(400, 'enabled is required')); - - settings.setMailConfig({ enabled: req.body.enabled }, function (error) { - if (error && error.reason === SettingsError.BAD_FIELD) return next(new HttpError(400, error.message)); - if (error) return next(new HttpError(500, error)); - - next(new HttpSuccess(202)); - }); -} - -function getMailFromValidation(req, res, next) { - settings.getMailFromValidation(function (error, enabled) { - if (error) return next(new HttpError(500, error)); - - next(new HttpSuccess(200, { enabled: enabled })); - }); -} - -function setMailFromValidation(req, res, next) { - assert.strictEqual(typeof req.body, 'object'); - - if (typeof req.body.enabled !== 'boolean') return next(new HttpError(400, 'enabled is required')); - - settings.setMailFromValidation(req.body.enabled, function (error) { - if (error && error.reason === SettingsError.BAD_FIELD) return next(new HttpError(400, error.message)); - if (error) return next(new HttpError(500, error)); - - next(new HttpSuccess(202)); - }); -} - -function getMailRelay(req, res, next) { - settings.getMailRelay(function (error, mail) { - if (error) return next(new HttpError(500, error)); - - next(new HttpSuccess(200, mail)); - }); -} - -function setMailRelay(req, res, next) { - assert.strictEqual(typeof req.body, 'object'); - - if (typeof req.body.provider !== 'string') return next(new HttpError(400, 'provider is required')); - if ('host' in req.body && typeof req.body.host !== 'string') return next(new HttpError(400, 'host must be a string')); - if ('port' in req.body && typeof req.body.port !== 'number') return next(new HttpError(400, 'port must be a string')); - if ('username' in req.body && typeof req.body.username !== 'string') return next(new HttpError(400, 'username must be a string')); - if ('password' in req.body && typeof req.body.password !== 'string') return next(new HttpError(400, 'password must be a string')); - - settings.setMailRelay(req.body, function (error) { - if (error && error.reason === SettingsError.BAD_FIELD) return next(new HttpError(400, error.message)); - if (error) return next(new HttpError(500, error)); - - next(new HttpSuccess(202)); - }); -} - -function getCatchAllAddress(req, res, next) { - settings.getCatchAllAddress(function (error, address) { - if (error) return next(new HttpError(500, error)); - - next(new HttpSuccess(200, { address: address })); - }); -} - -function setCatchAllAddress(req, res, next) { - assert.strictEqual(typeof req.body, 'object'); - - if (!req.body.address || !Array.isArray(req.body.address)) return next(new HttpError(400, 'address array is required')); - - for (var i = 0; i < req.body.address.length; i++) { - if (typeof req.body.address[i] !== 'string') return next(new HttpError(400, 'address must be an array of string')); - } - - settings.setCatchAllAddress(req.body.address, function (error) { - if (error && error.reason === SettingsError.BAD_FIELD) return next(new HttpError(400, error.message)); - if (error) return next(new HttpError(500, error)); - - next(new HttpSuccess(202)); - }); -} - function setCloudronAvatar(req, res, next) { assert.strictEqual(typeof req.files, 'object'); diff --git a/src/routes/test/mail-test.js b/src/routes/test/mail-test.js index b1e0976f9..ac2361076 100644 --- a/src/routes/test/mail-test.js +++ b/src/routes/test/mail-test.js @@ -10,7 +10,9 @@ var async = require('async'), config = require('../../config.js'), database = require('../../database.js'), expect = require('expect.js'), + mail = require('../../mail.js'), server = require('../../server.js'), + settingsdb = require('../../settingsdb.js'), superagent = require('superagent'); var SERVER_URL = 'http://localhost:' + config.get('port'); @@ -302,4 +304,186 @@ describe('Mail API', function () { }); }); }); + + describe('mail from validation', function () { + it('get mail from validation succeeds', function (done) { + superagent.get(SERVER_URL + '/api/v1/mail/mail_from_validation') + .query({ access_token: token }) + .end(function (err, res) { + expect(res.statusCode).to.equal(200); + expect(res.body).to.eql({ enabled: true }); + done(); + }); + }); + + it('cannot set without enabled field', function (done) { + superagent.post(SERVER_URL + '/api/v1/mail/mail_from_validation') + .query({ access_token: token }) + .send({ }) + .end(function (err, res) { + expect(res.statusCode).to.equal(400); + done(); + }); + }); + + it('can set with enabled field', function (done) { + superagent.post(SERVER_URL + '/api/v1/mail/mail_from_validation') + .query({ access_token: token }) + .send({ enabled: false }) + .end(function (err, res) { + expect(res.statusCode).to.equal(202); + done(); + }); + }); + }); + + describe('catch_all', function () { + it('get catch_all succeeds', function (done) { + superagent.get(SERVER_URL + '/api/v1/mail/catch_all_address') + .query({ access_token: token }) + .end(function (err, res) { + expect(res.statusCode).to.equal(200); + expect(res.body).to.eql({ address: [ ] }); + done(); + }); + }); + + it('cannot set without address field', function (done) { + superagent.put(SERVER_URL + '/api/v1/mail/catch_all_address') + .query({ access_token: token }) + .end(function (err, res) { + expect(res.statusCode).to.equal(400); + done(); + }); + }); + + it('cannot set with bad address field', function (done) { + superagent.put(SERVER_URL + '/api/v1/mail/catch_all_address') + .query({ access_token: token }) + .send({ address: [ 'user1', 123 ] }) + .end(function (err, res) { + expect(res.statusCode).to.equal(400); + done(); + }); + }); + + it('set succeeds', function (done) { + superagent.put(SERVER_URL + '/api/v1/mail/catch_all_address') + .query({ access_token: token }) + .send({ address: [ 'user1' ] }) + .end(function (err, res) { + expect(res.statusCode).to.equal(202); + done(); + }); + }); + + it('get succeeds', function (done) { + superagent.get(SERVER_URL + '/api/v1/mail/catch_all_address') + .query({ access_token: token }) + .end(function (err, res) { + expect(res.statusCode).to.equal(200); + expect(res.body).to.eql({ address: [ 'user1' ] }); + done(); + }); + }); + }); + + describe('mail relay', function () { + it('get mail relay succeeds', function (done) { + superagent.get(SERVER_URL + '/api/v1/mail/mail_relay') + .query({ access_token: token }) + .end(function (err, res) { + expect(res.statusCode).to.equal(200); + expect(res.body).to.eql({ provider: 'cloudron-smtp' }); + done(); + }); + }); + + it('cannot set without provider field', function (done) { + superagent.post(SERVER_URL + '/api/v1/mail/mail_relay') + .query({ access_token: token }) + .send({ }) + .end(function (err, res) { + expect(res.statusCode).to.equal(400); + done(); + }); + }); + + it('cannot set with bad host', function (done) { + superagent.post(SERVER_URL + '/api/v1/mail/mail_relay') + .query({ access_token: token }) + .send({ provider: 'external-smtp', host: true }) + .end(function (err, res) { + expect(res.statusCode).to.equal(400); + done(); + }); + }); + + it('set fails because mail server is unreachable', function (done) { + superagent.post(SERVER_URL + '/api/v1/mail/mail_relay') + .query({ access_token: token }) + .send({ provider: 'external-smtp', host: 'host', port: 25, username: 'u', password: 'p', tls: true }) + .end(function (err, res) { + expect(res.statusCode).to.equal(400); + done(); + }); + }); + + it('get succeeds', function (done) { + var relay = { provider: 'external-smtp', host: 'host', port: 25, username: 'u', password: 'p', tls: true }; + + settingsdb.set(mail.MAIL_RELAY_KEY, JSON.stringify(relay), function (error) { // skip the mail server verify() + expect(error).to.not.be.ok(); + + superagent.get(SERVER_URL + '/api/v1/mail/mail_relay') + .query({ access_token: token }) + .end(function (err, res) { + expect(res.statusCode).to.equal(200); + expect(res.body).to.eql(relay); + done(); + }); + }); + }); + }); + + describe('mail_config', function () { + it('get mail_config succeeds', function (done) { + superagent.get(SERVER_URL + '/api/v1/mail/mail_config') + .query({ access_token: token }) + .end(function (err, res) { + expect(res.statusCode).to.equal(200); + expect(res.body).to.eql({ enabled: false }); + done(); + }); + }); + + it('cannot set without enabled field', function (done) { + superagent.post(SERVER_URL + '/api/v1/mail/mail_config') + .query({ access_token: token }) + .end(function (err, res) { + expect(res.statusCode).to.equal(400); + done(); + }); + }); + + it('set succeeds', function (done) { + superagent.post(SERVER_URL + '/api/v1/mail/mail_config') + .query({ access_token: token }) + .send({ enabled: true }) + .end(function (err, res) { + expect(res.statusCode).to.equal(202); + done(); + }); + }); + + it('get succeeds', function (done) { + superagent.get(SERVER_URL + '/api/v1/mail/mail_config') + .query({ access_token: token }) + .end(function (err, res) { + expect(res.statusCode).to.equal(200); + expect(res.body).to.eql({ enabled: true }); + done(); + }); + }); + }); }); diff --git a/src/routes/test/settings-test.js b/src/routes/test/settings-test.js index 06e887d26..fd4795c2e 100644 --- a/src/routes/test/settings-test.js +++ b/src/routes/test/settings-test.js @@ -8,7 +8,6 @@ var async = require('async'), child_process = require('child_process'), - cloudron = require('../../cloudron.js'), config = require('../../config.js'), constants = require('../../constants.js'), database = require('../../database.js'), @@ -19,7 +18,6 @@ var async = require('async'), paths = require('../../paths.js'), server = require('../../server.js'), settings = require('../../settings.js'), - settingsdb = require('../../settingsdb.js'), superagent = require('superagent'); var SERVER_URL = 'http://localhost:' + config.get('port'); @@ -222,98 +220,6 @@ describe('Settings API', function () { }); }); - describe('mail_config', function () { - it('get mail_config succeeds', function (done) { - superagent.get(SERVER_URL + '/api/v1/settings/mail_config') - .query({ access_token: token }) - .end(function (err, res) { - expect(res.statusCode).to.equal(200); - expect(res.body).to.eql({ enabled: false }); - done(); - }); - }); - - it('cannot set without enabled field', function (done) { - superagent.post(SERVER_URL + '/api/v1/settings/mail_config') - .query({ access_token: token }) - .end(function (err, res) { - expect(res.statusCode).to.equal(400); - done(); - }); - }); - - it('set succeeds', function (done) { - superagent.post(SERVER_URL + '/api/v1/settings/mail_config') - .query({ access_token: token }) - .send({ enabled: true }) - .end(function (err, res) { - expect(res.statusCode).to.equal(202); - done(); - }); - }); - - it('get succeeds', function (done) { - superagent.get(SERVER_URL + '/api/v1/settings/mail_config') - .query({ access_token: token }) - .end(function (err, res) { - expect(res.statusCode).to.equal(200); - expect(res.body).to.eql({ enabled: true }); - done(); - }); - }); - }); - - describe('catch_all', function () { - it('get catch_all succeeds', function (done) { - superagent.get(SERVER_URL + '/api/v1/settings/catch_all_address') - .query({ access_token: token }) - .end(function (err, res) { - expect(res.statusCode).to.equal(200); - expect(res.body).to.eql({ address: [ ] }); - done(); - }); - }); - - it('cannot set without address field', function (done) { - superagent.put(SERVER_URL + '/api/v1/settings/catch_all_address') - .query({ access_token: token }) - .end(function (err, res) { - expect(res.statusCode).to.equal(400); - done(); - }); - }); - - it('cannot set with bad address field', function (done) { - superagent.put(SERVER_URL + '/api/v1/settings/catch_all_address') - .query({ access_token: token }) - .send({ address: [ 'user1', 123 ] }) - .end(function (err, res) { - expect(res.statusCode).to.equal(400); - done(); - }); - }); - - it('set succeeds', function (done) { - superagent.put(SERVER_URL + '/api/v1/settings/catch_all_address') - .query({ access_token: token }) - .send({ address: [ 'user1' ] }) - .end(function (err, res) { - expect(res.statusCode).to.equal(202); - done(); - }); - }); - - it('get succeeds', function (done) { - superagent.get(SERVER_URL + '/api/v1/settings/catch_all_address') - .query({ access_token: token }) - .end(function (err, res) { - expect(res.statusCode).to.equal(200); - expect(res.body).to.eql({ address: [ 'user1' ] }); - done(); - }); - }); - }); - xdescribe('Certificates API', function () { var validCert0, validKey0, // example.com validCert1, validKey1; // *.example.com @@ -526,94 +432,4 @@ describe('Settings API', function () { }); }); }); - - describe('mail relay', function () { - it('get mail relay succeeds', function (done) { - superagent.get(SERVER_URL + '/api/v1/settings/mail_relay') - .query({ access_token: token }) - .end(function (err, res) { - expect(res.statusCode).to.equal(200); - expect(res.body).to.eql({ provider: 'cloudron-smtp' }); - done(); - }); - }); - - it('cannot set without provider field', function (done) { - superagent.post(SERVER_URL + '/api/v1/settings/mail_relay') - .query({ access_token: token }) - .send({ }) - .end(function (err, res) { - expect(res.statusCode).to.equal(400); - done(); - }); - }); - - it('cannot set with bad host', function (done) { - superagent.post(SERVER_URL + '/api/v1/settings/mail_relay') - .query({ access_token: token }) - .send({ provider: 'external-smtp', host: true }) - .end(function (err, res) { - expect(res.statusCode).to.equal(400); - done(); - }); - }); - - it('set fails because mail server is unreachable', function (done) { - superagent.post(SERVER_URL + '/api/v1/settings/mail_relay') - .query({ access_token: token }) - .send({ provider: 'external-smtp', host: 'host', port: 25, username: 'u', password: 'p', tls: true }) - .end(function (err, res) { - expect(res.statusCode).to.equal(400); - done(); - }); - }); - - it('get succeeds', function (done) { - var relay = { provider: 'external-smtp', host: 'host', port: 25, username: 'u', password: 'p', tls: true }; - - settingsdb.set(settings.MAIL_RELAY_KEY, JSON.stringify(relay), function (error) { // skip the mail server verify() - expect(error).to.not.be.ok(); - - superagent.get(SERVER_URL + '/api/v1/settings/mail_relay') - .query({ access_token: token }) - .end(function (err, res) { - expect(res.statusCode).to.equal(200); - expect(res.body).to.eql(relay); - done(); - }); - }); - }); - }); - - describe('mail from validation', function () { - it('get mail from validation succeeds', function (done) { - superagent.get(SERVER_URL + '/api/v1/settings/mail_from_validation') - .query({ access_token: token }) - .end(function (err, res) { - expect(res.statusCode).to.equal(200); - expect(res.body).to.eql({ enabled: true }); - done(); - }); - }); - - it('cannot set without enabled field', function (done) { - superagent.post(SERVER_URL + '/api/v1/settings/mail_from_validation') - .query({ access_token: token }) - .send({ }) - .end(function (err, res) { - expect(res.statusCode).to.equal(400); - done(); - }); - }); - - it('can set with enabled field', function (done) { - superagent.post(SERVER_URL + '/api/v1/settings/mail_from_validation') - .query({ access_token: token }) - .send({ enabled: false }) - .end(function (err, res) { - expect(res.statusCode).to.equal(202); - done(); - }); - }); - }); }); diff --git a/src/server.js b/src/server.js index d0e4480b4..a3adfcbbd 100644 --- a/src/server.js +++ b/src/server.js @@ -214,15 +214,14 @@ function initializeExpressSync() { // email routes router.get ('/api/v1/mail/status', settingsScope, routes.user.requireAdmin, routes.mail.getStatus); - - router.get ('/api/v1/settings/mail_config', settingsScope, routes.user.requireAdmin, routes.settings.getMailConfig); - router.post('/api/v1/settings/mail_config', settingsScope, routes.user.requireAdmin, routes.settings.setMailConfig); - router.get ('/api/v1/settings/mail_relay', settingsScope, routes.user.requireAdmin, routes.settings.getMailRelay); - router.post('/api/v1/settings/mail_relay', settingsScope, routes.user.requireAdmin, routes.settings.setMailRelay); - router.get ('/api/v1/settings/catch_all_address', settingsScope, routes.user.requireAdmin, routes.settings.getCatchAllAddress); - router.put ('/api/v1/settings/catch_all_address', settingsScope, routes.user.requireAdmin, routes.settings.setCatchAllAddress); - router.get ('/api/v1/settings/mail_from_validation', settingsScope, routes.user.requireAdmin, routes.settings.getMailFromValidation); - router.post('/api/v1/settings/mail_from_validation', settingsScope, routes.user.requireAdmin, routes.settings.setMailFromValidation); + router.get ('/api/v1/mail/mail_from_validation', settingsScope, routes.user.requireAdmin, routes.mail.getMailFromValidation); + router.post('/api/v1/mail/mail_from_validation', settingsScope, routes.user.requireAdmin, routes.mail.setMailFromValidation); + router.get ('/api/v1/mail/catch_all_address', settingsScope, routes.user.requireAdmin, routes.mail.getCatchAllAddress); + router.put ('/api/v1/mail/catch_all_address', settingsScope, routes.user.requireAdmin, routes.mail.setCatchAllAddress); + router.get ('/api/v1/mail/mail_relay', settingsScope, routes.user.requireAdmin, routes.mail.getMailRelay); + router.post('/api/v1/mail/mail_relay', settingsScope, routes.user.requireAdmin, routes.mail.setMailRelay); + router.get ('/api/v1/mail/mail_config', settingsScope, routes.user.requireAdmin, routes.mail.getMailConfig); + router.post('/api/v1/mail/mail_config', settingsScope, routes.user.requireAdmin, routes.mail.setMailConfig); // feedback router.post('/api/v1/feedback', usersScope, routes.cloudron.feedback); diff --git a/src/settings.js b/src/settings.js index 6edd572fe..300a3868b 100644 --- a/src/settings.js +++ b/src/settings.js @@ -32,18 +32,6 @@ exports = module.exports = { getAppstoreConfig: getAppstoreConfig, setAppstoreConfig: setAppstoreConfig, - getMailConfig: getMailConfig, - setMailConfig: setMailConfig, - - getMailFromValidation: getMailFromValidation, - setMailFromValidation: setMailFromValidation, - - getMailRelay: getMailRelay, - setMailRelay: setMailRelay, - - setCatchAllAddress: setCatchAllAddress, - getCatchAllAddress: getCatchAllAddress, - getEmailDigest: getEmailDigest, setEmailDigest: setEmailDigest, @@ -51,7 +39,6 @@ exports = module.exports = { // booleans. if you add an entry here, be sure to fix getAll DYNAMIC_DNS_KEY: 'dynamic_dns', - MAIL_FROM_VALIDATION_KEY: 'mail_from_validation', EMAIL_DIGEST: 'email_digest', // json. if you add an entry here, be sure to fix getAll @@ -60,9 +47,6 @@ exports = module.exports = { UPDATE_CONFIG_KEY: 'update_config', APPSTORE_CONFIG_KEY: 'appstore_config', CAAS_CONFIG_KEY: 'caas_config', - MAIL_CONFIG_KEY: 'mail_config', - MAIL_RELAY_KEY: 'mail_relay', - CATCH_ALL_ADDRESS_KEY: 'catch_all_address', // strings AUTOUPDATE_PATTERN_KEY: 'autoupdate_pattern', @@ -79,12 +63,8 @@ var assert = require('assert'), constants = require('./constants.js'), CronJob = require('cron').CronJob, DatabaseError = require('./databaseerror.js'), - debug = require('debug')('box:settings'), - mail = require('./mail.js'), - MailError = mail.MailError, moment = require('moment-timezone'), paths = require('./paths.js'), - platform = require('./platform.js'), safe = require('safetydance'), settingsdb = require('./settingsdb.js'), superagent = require('superagent'), @@ -106,17 +86,11 @@ var gDefaults = (function () { result[exports.TLS_CONFIG_KEY] = { provider: 'letsencrypt-prod' }; result[exports.UPDATE_CONFIG_KEY] = { prerelease: false }; result[exports.APPSTORE_CONFIG_KEY] = {}; - result[exports.MAIL_CONFIG_KEY] = { enabled: false }; - result[exports.MAIL_RELAY_KEY] = { provider: 'cloudron-smtp' }; - result[exports.CATCH_ALL_ADDRESS_KEY] = [ ]; - result[exports.MAIL_FROM_VALIDATION_KEY] = true; result[exports.EMAIL_DIGEST] = true; return result; })(); -var NOOP_CALLBACK = function (error) { if (error) debug(error); }; - function SettingsError(reason, errorOrMessage) { assert.strictEqual(typeof reason, 'string'); assert(errorOrMessage instanceof Error || typeof errorOrMessage === 'string' || typeof errorOrMessage === 'undefined'); @@ -349,56 +323,6 @@ function setBackupConfig(backupConfig, callback) { }); } -function getMailConfig(callback) { - assert.strictEqual(typeof callback, 'function'); - - settingsdb.get(exports.MAIL_CONFIG_KEY, function (error, value) { - if (error && error.reason === DatabaseError.NOT_FOUND) return callback(null, gDefaults[exports.MAIL_CONFIG_KEY]); - if (error) return callback(new SettingsError(SettingsError.INTERNAL_ERROR, error)); - - callback(null, JSON.parse(value)); - }); -} - -function setMailConfig(mailConfig, callback) { - assert.strictEqual(typeof mailConfig, 'object'); - assert.strictEqual(typeof callback, 'function'); - - settingsdb.set(exports.MAIL_CONFIG_KEY, JSON.stringify(mailConfig), function (error) { - if (error) return callback(new SettingsError(SettingsError.INTERNAL_ERROR, error)); - - exports.events.emit(exports.MAIL_CONFIG_KEY, mailConfig); - - callback(null); - }); -} - -function getMailFromValidation(callback) { - assert.strictEqual(typeof callback, 'function'); - - settingsdb.get(exports.MAIL_FROM_VALIDATION_KEY, function (error, enabled) { - if (error && error.reason === DatabaseError.NOT_FOUND) return callback(null, gDefaults[exports.MAIL_FROM_VALIDATION_KEY]); - if (error) return callback(new SettingsError(SettingsError.INTERNAL_ERROR, error)); - - callback(null, !!enabled); // settingsdb holds string values only - }); -} - -function setMailFromValidation(enabled, callback) { - assert.strictEqual(typeof enabled, 'boolean'); - assert.strictEqual(typeof callback, 'function'); - - settingsdb.set(exports.MAIL_FROM_VALIDATION_KEY, enabled ? 'enabled' : '', function (error) { - if (error) return callback(new SettingsError(SettingsError.INTERNAL_ERROR, error)); - - exports.events.emit(exports.MAIL_FROM_VALIDATION_KEY, enabled); - - platform.createMailConfig(NOOP_CALLBACK); - - callback(null); - }); -} - function getEmailDigest(callback) { assert.strictEqual(typeof callback, 'function'); @@ -423,61 +347,6 @@ function setEmailDigest(enabled, callback) { }); } -function getMailRelay(callback) { - assert.strictEqual(typeof callback, 'function'); - - settingsdb.get(exports.MAIL_RELAY_KEY, function (error, value) { - if (error && error.reason === DatabaseError.NOT_FOUND) return callback(null, gDefaults[exports.MAIL_RELAY_KEY]); - if (error) return callback(new SettingsError(SettingsError.INTERNAL_ERROR, error)); - - callback(null, JSON.parse(value)); - }); -} - -function setMailRelay(relay, callback) { - assert.strictEqual(typeof relay, 'object'); - assert.strictEqual(typeof callback, 'function'); - - mail.verifyRelay(relay, function (error) { - if (error && error.reason === MailError.BAD_FIELD) return callback(new SettingsError(SettingsError.BAD_FIELD, error.message)); - if (error) return callback(new SettingsError(SettingsError.INTERNAL_ERROR, error)); - - settingsdb.set(exports.MAIL_RELAY_KEY, JSON.stringify(relay), function (error) { - if (error) return callback(new SettingsError(SettingsError.INTERNAL_ERROR, error)); - - exports.events.emit(exports.MAIL_RELAY_KEY, relay); - - callback(null); - }); - }); -} - -function getCatchAllAddress(callback) { - assert.strictEqual(typeof callback, 'function'); - - settingsdb.get(exports.CATCH_ALL_ADDRESS_KEY, function (error, value) { - if (error && error.reason === DatabaseError.NOT_FOUND) return callback(null, gDefaults[exports.CATCH_ALL_ADDRESS_KEY]); - if (error) return callback(new SettingsError(SettingsError.INTERNAL_ERROR, error)); - - callback(null, JSON.parse(value)); - }); -} - -function setCatchAllAddress(address, callback) { - assert(Array.isArray(address)); - assert.strictEqual(typeof callback, 'function'); - - settingsdb.set(exports.CATCH_ALL_ADDRESS_KEY, JSON.stringify(address), function (error) { - if (error) return callback(new SettingsError(SettingsError.INTERNAL_ERROR, error)); - - exports.events.emit(exports.CATCH_ALL_ADDRESS_KEY, address); - - platform.createMailConfig(NOOP_CALLBACK); - - callback(null); - }); -} - function getCaasConfig(callback) { assert.strictEqual(typeof callback, 'function'); @@ -570,11 +439,9 @@ function getAll(callback) { // convert booleans result[exports.DYNAMIC_DNS_KEY] = !!result[exports.DYNAMIC_DNS_KEY]; - result[exports.MAIL_FROM_VALIDATION_KEY] = !!result[exports.MAIL_FROM_VALIDATION_KEY]; // convert JSON objects - [exports.TLS_CONFIG_KEY, exports.BACKUP_CONFIG_KEY, exports.MAIL_CONFIG_KEY, - exports.UPDATE_CONFIG_KEY, exports.APPSTORE_CONFIG_KEY, exports.MAIL_RELAY_KEY, exports.CATCH_ALL_ADDRESS_KEY].forEach(function (key) { + [exports.TLS_CONFIG_KEY, exports.BACKUP_CONFIG_KEY, exports.UPDATE_CONFIG_KEY, exports.APPSTORE_CONFIG_KEY ].forEach(function (key) { result[key] = typeof result[key] === 'object' ? result[key] : safe.JSON.parse(result[key]); }); diff --git a/src/test/digest-test.js b/src/test/digest-test.js index 7987b7d6a..943818f50 100644 --- a/src/test/digest-test.js +++ b/src/test/digest-test.js @@ -9,9 +9,9 @@ var async = require('async'), config = require('../config.js'), database = require('../database.js'), digest = require('../digest.js'), - domaindb = require('../domaindb.js'), eventlog = require('../eventlog.js'), expect = require('expect.js'), + mail = require('../mail.js'), mailer = require('../mailer.js'), paths = require('../paths.js'), safe = require('safetydance'), @@ -67,7 +67,7 @@ describe('digest', function () { settings.initialize, user.createOwner.bind(null, USER_0.username, USER_0.password, USER_0.email, USER_0.displayName, AUDIT_SOURCE), eventlog.add.bind(null, eventlog.ACTION_UPDATE, AUDIT_SOURCE, { boxUpdateInfo: { sourceTarballUrl: 'xx', version: '1.2.3', changelog: [ 'good stuff' ] } }), - settingsdb.set.bind(null, settings.MAIL_CONFIG_KEY, JSON.stringify({ enabled: true })), + settingsdb.set.bind(null, mail.MAIL_CONFIG_KEY, JSON.stringify({ enabled: true })), mailer._clearMailQueue ], done); }); @@ -123,7 +123,7 @@ describe('digest', function () { it('sends mail for pending update to owner account email', function (done) { updatechecker._setUpdateInfo({ box: null, apps: { 'appid': { manifest: { version: '1.2.5', changelog: 'noop\nreally' } } } }); - settingsdb.set(settings.MAIL_CONFIG_KEY, JSON.stringify({ enabled: true }), function (error) { + settingsdb.set(mail.MAIL_CONFIG_KEY, JSON.stringify({ enabled: true }), function (error) { if (error) return done(error); digest.maybeSend(function (error) { diff --git a/src/test/ldap-test.js b/src/test/ldap-test.js index 6e7c10fde..df0addccc 100644 --- a/src/test/ldap-test.js +++ b/src/test/ldap-test.js @@ -1,6 +1,6 @@ /* jslint node:true */ /* global it:false */ -/* global xdescribe:false */ +/* global describe:false */ /* global before:false */ /* global after:false */ @@ -16,8 +16,8 @@ var appdb = require('../appdb.js'), groups = require('../groups.js'), http = require('http'), ldapServer = require('../ldap.js'), + mail = require('../mail.js'), mailboxdb = require('../mailboxdb.js'), - settings = require('../settings.js'), settingsdb = require('../settingsdb.js'), ldap = require('ldapjs'), user = require('../user.js'); @@ -246,7 +246,7 @@ describe('Ldap', function () { it('succeeds without accessRestriction when email is enabled', function (done) { // user settingsdb instead of settings, to not trigger further events - settingsdb.set(settings.MAIL_CONFIG_KEY, JSON.stringify({ enabled: true }), function (error) { + settingsdb.set(mail.MAIL_CONFIG_KEY, JSON.stringify({ enabled: true }), function (error) { expect(error).not.to.be.ok(); var client = ldap.createClient({ url: 'ldap://127.0.0.1:' + config.get('ldapPort') }); @@ -256,7 +256,7 @@ describe('Ldap', function () { client.unbind(); - settingsdb.set(settings.MAIL_CONFIG_KEY, JSON.stringify({ enabled: false }), done); + settingsdb.set(mail.MAIL_CONFIG_KEY, JSON.stringify({ enabled: false }), done); }); }); }); @@ -378,7 +378,7 @@ describe('Ldap', function () { it ('succeeds with basic filter and email enabled', function (done) { // user settingsdb instead of settings, to not trigger further events - settingsdb.set(settings.MAIL_CONFIG_KEY, JSON.stringify({ enabled: true }), function (error) { + settingsdb.set(mail.MAIL_CONFIG_KEY, JSON.stringify({ enabled: true }), function (error) { expect(error).not.to.be.ok(); var client = ldap.createClient({ url: 'ldap://127.0.0.1:' + config.get('ldapPort') }); @@ -409,7 +409,7 @@ describe('Ldap', function () { client.unbind(); - settingsdb.set(settings.MAIL_CONFIG_KEY, JSON.stringify({ enabled: false }), done); + settingsdb.set(mail.MAIL_CONFIG_KEY, JSON.stringify({ enabled: false }), done); }); }); }); @@ -739,7 +739,7 @@ describe('Ldap', function () { }); it('cannot get mailbox with just name', function (done) { - ldapSearch('cn=' + USER_0.username + ',ou=mailboxes,dc=cloudron', 'objectclass=mailbox', function (error, entries) { + ldapSearch('cn=' + USER_0.username + ',ou=mailboxes,dc=cloudron', 'objectclass=mailbox', function (error) { expect(error).to.be.a(ldap.NoSuchObjectError); done(); }); @@ -845,7 +845,7 @@ describe('Ldap', function () { it('email enabled - allows with valid email', function (done) { // user settingsdb instead of settings, to not trigger further events - settingsdb.set(settings.MAIL_CONFIG_KEY, JSON.stringify({ enabled: true }), function (error) { + settingsdb.set(mail.MAIL_CONFIG_KEY, JSON.stringify({ enabled: true }), function (error) { expect(error).not.to.be.ok(); var client = ldap.createClient({ url: 'ldap://127.0.0.1:' + config.get('ldapPort') }); @@ -855,14 +855,14 @@ describe('Ldap', function () { client.unbind(); - settingsdb.set(settings.MAIL_CONFIG_KEY, JSON.stringify({ enabled: false }), done); + settingsdb.set(mail.MAIL_CONFIG_KEY, JSON.stringify({ enabled: false }), done); }); }); }); it('email enabled - does not allow with invalid password', function (done) { // user settingsdb instead of settings, to not trigger further events - settingsdb.set(settings.MAIL_CONFIG_KEY, JSON.stringify({ enabled: true }), function (error) { + settingsdb.set(mail.MAIL_CONFIG_KEY, JSON.stringify({ enabled: true }), function (error) { expect(error).not.to.be.ok(); var client = ldap.createClient({ url: 'ldap://127.0.0.1:' + config.get('ldapPort') }); @@ -872,7 +872,7 @@ describe('Ldap', function () { client.unbind(); - settingsdb.set(settings.MAIL_CONFIG_KEY, JSON.stringify({ enabled: false }), done); + settingsdb.set(mail.MAIL_CONFIG_KEY, JSON.stringify({ enabled: false }), done); }); }); }); @@ -881,7 +881,7 @@ describe('Ldap', function () { describe('app sendmail bind', function () { // these tests should work even when email is disabled before(function (done) { - settingsdb.set(settings.MAIL_CONFIG_KEY, JSON.stringify({ enabled: false }), done); + settingsdb.set(mail.MAIL_CONFIG_KEY, JSON.stringify({ enabled: false }), done); }); it('does not allow with invalid app', function (done) { @@ -933,7 +933,7 @@ describe('Ldap', function () { it('email enabled - allows with valid email', function (done) { // user settingsdb instead of settings, to not trigger further events - settingsdb.set(settings.MAIL_CONFIG_KEY, JSON.stringify({ enabled: true }), function (error) { + settingsdb.set(mail.MAIL_CONFIG_KEY, JSON.stringify({ enabled: true }), function (error) { expect(error).not.to.be.ok(); var client = ldap.createClient({ url: 'ldap://127.0.0.1:' + config.get('ldapPort') }); @@ -943,14 +943,14 @@ describe('Ldap', function () { client.unbind(); - settingsdb.set(settings.MAIL_CONFIG_KEY, JSON.stringify({ enabled: false }), done); + settingsdb.set(mail.MAIL_CONFIG_KEY, JSON.stringify({ enabled: false }), done); }); }); }); it('email enabled - does not allow with invalid password', function (done) { // user settingsdb instead of settings, to not trigger further events - settingsdb.set(settings.MAIL_CONFIG_KEY, JSON.stringify({ enabled: true }), function (error) { + settingsdb.set(mail.MAIL_CONFIG_KEY, JSON.stringify({ enabled: true }), function (error) { expect(error).not.to.be.ok(); var client = ldap.createClient({ url: 'ldap://127.0.0.1:' + config.get('ldapPort') }); @@ -960,7 +960,7 @@ describe('Ldap', function () { client.unbind(); - settingsdb.set(settings.MAIL_CONFIG_KEY, JSON.stringify({ enabled: false }), done); + settingsdb.set(mail.MAIL_CONFIG_KEY, JSON.stringify({ enabled: false }), done); }); }); }); diff --git a/src/test/mail-test.js b/src/test/mail-test.js new file mode 100644 index 000000000..7a8e523e4 --- /dev/null +++ b/src/test/mail-test.js @@ -0,0 +1,108 @@ +/* global it:false */ +/* global describe:false */ +/* global before:false */ +/* global after:false */ + +'use strict'; + +var async = require('async'), + config = require('../config.js'), + database = require('../database.js'), + expect = require('expect.js'), + mail = require('../mail.js'), + settingsdb = require('../settingsdb.js'); + +function setup(done) { + config._reset(); + config.set('fqdn', 'example.com'); + config.set('provider', 'caas'); + + async.series([ + database.initialize, + ], done); +} + +function cleanup(done) { + async.series([ + database._clear, + database.uninitialize + ], done); +} + +describe('Settings', function () { + describe('values', function () { + before(setup); + after(cleanup); + + it('can set mail from validation', function (done) { + mail.setMailFromValidation(true, function (error) { + expect(error).to.be(null); + done(); + }); + }); + + it('can get mail from validation', function (done) { + mail.getMailFromValidation(function (error, enabled) { + expect(error).to.be(null); + expect(enabled).to.be(true); + done(); + }); + }); + + it('can get catch all address', function (done) { + mail.getCatchAllAddress(function (error, address) { + expect(error).to.be(null); + expect(address).to.eql([ ]); + done(); + }); + }); + + it('can set catch all address', function (done) { + mail.setCatchAllAddress([ 'user1', 'user2' ], function (error) { + expect(error).to.be(null); + + mail.getCatchAllAddress(function (error, address) { + expect(error).to.be(null); + expect(address).to.eql([ 'user1', 'user2' ]); + done(); + }); + }); + }); + + it('can get mail relay', function (done) { + mail.getMailRelay(function (error, address) { + expect(error).to.be(null); + expect(address).to.eql({ provider: 'cloudron-smtp' }); + done(); + }); + }); + + it('can set mail relay', function (done) { + var relay = { provider: 'external-smtp', host: 'mx.foo.com', port: 25 }; + settingsdb.set(mail.MAIL_RELAY_KEY, JSON.stringify(relay), function (error) { // skip the mail server verify() + expect(error).to.be(null); + + mail.getMailRelay(function (error, address) { + expect(error).to.be(null); + expect(address).to.eql(relay); + done(); + }); + }); + }); + + it('can set mail config', function (done) { + mail.setMailConfig({ enabled: true }, function (error) { + expect(error).to.be(null); + done(); + }); + }); + + it('can get mail config', function (done) { + mail.getMailConfig(function (error, mailConfig) { + expect(error).to.be(null); + expect(mailConfig.enabled).to.be(true); + done(); + }); + }); + }); +}); diff --git a/src/test/settings-test.js b/src/test/settings-test.js index acb6467ce..bad2327f0 100644 --- a/src/test/settings-test.js +++ b/src/test/settings-test.js @@ -105,7 +105,7 @@ describe('Settings', function () { }); it('can set backup config', function (done) { - var scope2 = nock(config.apiServerOrigin()) + nock(config.apiServerOrigin()) .post('/api/v1/boxes/' + config.fqdn() + '/awscredentials?token=TOKEN') .reply(201, { credentials: { AccessKeyId: 'accessKeyId', SecretAccessKey: 'secretAccessKey', SessionToken: 'sessionToken' } }); @@ -124,36 +124,6 @@ describe('Settings', function () { }); }); - it('can set mail config', function (done) { - settings.setMailConfig({ enabled: true }, function (error) { - expect(error).to.be(null); - done(); - }); - }); - - it('can get mail config', function (done) { - settings.getMailConfig(function (error, mailConfig) { - expect(error).to.be(null); - expect(mailConfig.enabled).to.be(true); - done(); - }); - }); - - it('can set mail from validation', function (done) { - settings.setMailFromValidation(true, function (error) { - expect(error).to.be(null); - done(); - }); - }); - - it('can get mail from validation', function (done) { - settings.getMailFromValidation(function (error, enabled) { - expect(error).to.be(null); - expect(enabled).to.be(true); - done(); - }); - }); - it('can enable mail digest', function (done) { settings.setEmailDigest(true, function (error) { expect(error).to.be(null); @@ -169,47 +139,6 @@ describe('Settings', function () { }); }); - it('can get mail relay', function (done) { - settings.getMailRelay(function (error, address) { - expect(error).to.be(null); - expect(address).to.eql({ provider: 'cloudron-smtp' }); - done(); - }); - }); - - it('can set mail relay', function (done) { - var relay = { provider: 'external-smtp', host: 'mx.foo.com', port: 25 }; -+ settingsdb.set(settings.MAIL_RELAY_KEY, JSON.stringify(relay), function (error) { // skip the mail server verify() - expect(error).to.be(null); - - settings.getMailRelay(function (error, address) { - expect(error).to.be(null); - expect(address).to.eql(relay); - done(); - }); - }); - }); - - it('can get catch all address', function (done) { - settings.getCatchAllAddress(function (error, address) { - expect(error).to.be(null); - expect(address).to.eql([ ]); - done(); - }); - }); - - it('can set catch all address', function (done) { - settings.setCatchAllAddress([ "user1", "user2" ], function (error) { - expect(error).to.be(null); - - settings.getCatchAllAddress(function (error, address) { - expect(error).to.be(null); - expect(address).to.eql([ "user1", "user2" ]); - done(); - }); - }); - }); - it('can get all values', function (done) { settings.getAll(function (error, allSettings) { expect(error).to.be(null); diff --git a/src/test/user-test.js b/src/test/user-test.js index a7f7a3b2f..b5a6c97ca 100644 --- a/src/test/user-test.js +++ b/src/test/user-test.js @@ -14,11 +14,11 @@ var async = require('async'), fs = require('fs'), groupdb = require('../groupdb.js'), groups = require('../groups.js'), + mail = require('../mail.js'), mailboxdb = require('../mailboxdb.js'), mailer = require('../mailer.js'), user = require('../user.js'), userdb = require('../userdb.js'), - settings = require('../settings.js'), settingsdb = require('../settingsdb.js'), UserError = user.UserError; @@ -269,7 +269,7 @@ describe('User', function () { it('succeeds and attempts to send invite to alternateEmail', function (done) { // user settingsdb instead of settings, to not trigger further events - settingsdb.set(settings.MAIL_CONFIG_KEY, JSON.stringify({ enabled: true }), function (error) { + settingsdb.set(mail.MAIL_CONFIG_KEY, JSON.stringify({ enabled: true }), function (error) { expect(error).not.to.be.ok(); user.create(USERNAME_1, PASSWORD_1, EMAIL_1, DISPLAY_NAME_1, AUDIT_SOURCE, { sendInvite: true }, function (error, result) { @@ -283,7 +283,7 @@ describe('User', function () { checkMails(2, { sentTo: EMAIL_1.toLowerCase() }, function (error) { expect(error).not.to.be.ok(); - settingsdb.set(settings.MAIL_CONFIG_KEY, JSON.stringify({ enabled: false }), done); + settingsdb.set(mail.MAIL_CONFIG_KEY, JSON.stringify({ enabled: false }), done); }); }); }); @@ -589,28 +589,28 @@ describe('User', function () { it('succeeds with cloudron mail enabled', function (done) { // user settingsdb instead of settings, to not trigger further events - settingsdb.set(settings.MAIL_CONFIG_KEY, JSON.stringify({ enabled: true }), function (error) { + settingsdb.set(mail.MAIL_CONFIG_KEY, JSON.stringify({ enabled: true }), function (error) { expect(error).not.to.be.ok(); user.verifyWithEmail(USERNAME + '@' + config.fqdn(), PASSWORD, function (error, result) { expect(error).to.not.be.ok(); expect(result).to.be.ok(); - settingsdb.set(settings.MAIL_CONFIG_KEY, JSON.stringify({ enabled: false }), done); + settingsdb.set(mail.MAIL_CONFIG_KEY, JSON.stringify({ enabled: false }), done); }); }); }); it('fails with cloudron mail enabled and invite email', function (done) { // user settingsdb instead of settings, to not trigger further events - settingsdb.set(settings.MAIL_CONFIG_KEY, JSON.stringify({ enabled: true }), function (error) { + settingsdb.set(mail.MAIL_CONFIG_KEY, JSON.stringify({ enabled: true }), function (error) { expect(error).not.to.be.ok(); user.verifyWithEmail(EMAIL, PASSWORD, function (error) { expect(error).to.be.a(UserError); expect(error.reason).to.equal(UserError.NOT_FOUND); - settingsdb.set(settings.MAIL_CONFIG_KEY, JSON.stringify({ enabled: false }), done); + settingsdb.set(mail.MAIL_CONFIG_KEY, JSON.stringify({ enabled: false }), done); }); }); }); @@ -645,7 +645,7 @@ describe('User', function () { it('succeeds with email enabled', function (done) { // user settingsdb instead of settings, to not trigger further events - settingsdb.set(settings.MAIL_CONFIG_KEY, JSON.stringify({ enabled: true }), function (error) { + settingsdb.set(mail.MAIL_CONFIG_KEY, JSON.stringify({ enabled: true }), function (error) { expect(error).not.to.be.ok(); user.get(userObject.id, function (error, result) { @@ -657,7 +657,7 @@ describe('User', function () { expect(result.username).to.equal(USERNAME.toLowerCase()); expect(result.displayName).to.equal(DISPLAY_NAME); - settingsdb.set(settings.MAIL_CONFIG_KEY, JSON.stringify({ enabled: false }), done); + settingsdb.set(mail.MAIL_CONFIG_KEY, JSON.stringify({ enabled: false }), done); }); }); }); @@ -1026,7 +1026,7 @@ describe('User', function () { }); it('did delete mailbox and aliases', function (done) { - mailboxdb.getMailbox(userObject.username.toLowerCase(), DOMAIN_0.domain, function (error, mailbox) { + mailboxdb.getMailbox(userObject.username.toLowerCase(), DOMAIN_0.domain, function (error) { expect(error.reason).to.be(DatabaseError.NOT_FOUND); mailboxdb.getAliasesForName(USERNAME.toLowerCase(), DOMAIN_0.domain, function (error, results) { diff --git a/src/user.js b/src/user.js index 2d66f48ee..b1aec02a9 100644 --- a/src/user.js +++ b/src/user.js @@ -36,10 +36,10 @@ var assert = require('assert'), groups = require('./groups.js'), GroupError = groups.GroupError, hat = require('hat'), + mail = require('./mail.js'), mailer = require('./mailer.js'), mailboxdb = require('./mailboxdb.js'), safe = require('safetydance'), - settings = require('./settings.js'), tokendb = require('./tokendb.js'), userdb = require('./userdb.js'), util = require('util'), @@ -178,7 +178,7 @@ function createUser(username, password, email, displayName, auditSource, options if (error && error.reason === DatabaseError.ALREADY_EXISTS) return callback(new UserError(UserError.ALREADY_EXISTS, error.message)); if (error) return callback(new UserError(UserError.INTERNAL_ERROR, error)); - settings.getMailConfig(function (error, mailConfig) { + mail.getMailConfig(function (error, mailConfig) { if (error) return callback(new UserError(UserError.INTERNAL_ERROR, error)); if (mailConfig.enabled) { @@ -259,7 +259,7 @@ function verifyWithEmail(email, password, callback) { email = email.toLowerCase(); - settings.getMailConfig(function (error, mailConfig) { + mail.getMailConfig(function (error, mailConfig) { if (error) return callback(new UserError(UserError.INTERNAL_ERROR, error)); if (mailConfig.enabled) return verifyWithUsername(email.split('@')[0], password, callback); @@ -302,7 +302,7 @@ function listUsers(callback) { userdb.getAllWithGroupIds(function (error, results) { if (error) return callback(new UserError(UserError.INTERNAL_ERROR, error)); - settings.getMailConfig(function (error, mailConfig) { + mail.getMailConfig(function (error, mailConfig) { if (error) return callback(new UserError(UserError.INTERNAL_ERROR, error)); results.forEach(function (result) { @@ -345,7 +345,7 @@ function getUser(userId, callback) { result.groupIds = groupIds; result.admin = groupIds.indexOf(constants.ADMIN_GROUP_ID) !== -1; - settings.getMailConfig(function (error, mailConfig) { + mail.getMailConfig(function (error, mailConfig) { if (error) return callback(new UserError(UserError.INTERNAL_ERROR, error)); if (mailConfig.enabled) { @@ -451,7 +451,7 @@ function getAllAdmins(callback) { userdb.getAllAdmins(function (error, admins) { if (error) return callback(new UserError(UserError.INTERNAL_ERROR, error)); - settings.getMailConfig(function (error, mailConfig) { + mail.getMailConfig(function (error, mailConfig) { if (error) return callback(new UserError(UserError.INTERNAL_ERROR, error)); admins.forEach(function (admin) { @@ -575,7 +575,7 @@ function getOwner(callback) { if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new UserError(UserError.NOT_FOUND)); if (error) return callback(new UserError(UserError.INTERNAL_ERROR, error)); - settings.getMailConfig(function (error, mailConfig) { + mail.getMailConfig(function (error, mailConfig) { if (error) return callback(new UserError(UserError.INTERNAL_ERROR, error)); if (mailConfig.enabled) { diff --git a/webadmin/src/js/client.js b/webadmin/src/js/client.js index 931548800..8f990ffee 100644 --- a/webadmin/src/js/client.js +++ b/webadmin/src/js/client.js @@ -440,14 +440,14 @@ angular.module('Application').service('Client', ['$http', '$interval', 'md5', 'N }; Client.prototype.setCatchallAddresses = function (addresses, callback) { - put('/api/v1/settings/catch_all_address', { address: addresses }).success(function(data, status) { + put('/api/v1/mail/catch_all_address', { address: addresses }).success(function(data, status) { if (status !== 202) return callback(new ClientError(status, data)); callback(null); }).error(defaultErrorHandler(callback)); }; Client.prototype.getCatchallAddresses = function (callback) { - get('/api/v1/settings/catch_all_address').success(function(data, status) { + get('/api/v1/mail/catch_all_address').success(function(data, status) { if (status !== 200) return callback(new ClientError(status, data)); callback(null, data.address); }).error(defaultErrorHandler(callback)); @@ -520,28 +520,28 @@ angular.module('Application').service('Client', ['$http', '$interval', 'md5', 'N }; Client.prototype.getMailConfig = function (callback) { - get('/api/v1/settings/mail_config').success(function (data, status) { + get('/api/v1/mail/mail_config').success(function (data, status) { if (status !== 200 || typeof data !== 'object') return callback(new ClientError(status, data)); callback(null, data); }).error(defaultErrorHandler(callback)); }; Client.prototype.setMailConfig = function (config, callback) { - post('/api/v1/settings/mail_config', config).success(function (data, status) { + post('/api/v1/mail/mail_config', config).success(function (data, status) { if (status !== 202) return callback(new ClientError(status, data)); callback(null); }).error(defaultErrorHandler(callback)); }; Client.prototype.getMailRelay = function (callback) { - get('/api/v1/settings/mail_relay').success(function (data, status) { + get('/api/v1/mail/mail_relay').success(function (data, status) { if (status !== 200 || typeof data !== 'object') return callback(new ClientError(status, data)); callback(null, data); }).error(defaultErrorHandler(callback)); }; Client.prototype.setMailRelay = function (config, callback) { - post('/api/v1/settings/mail_relay', config).success(function (data, status) { + post('/api/v1/mail/mail_relay', config).success(function (data, status) { if (status !== 202) return callback(new ClientError(status, data)); callback(null); }).error(defaultErrorHandler(callback));