diff --git a/dashboard/src/js/client.js b/dashboard/src/js/client.js index a8aea2173..bec71cf83 100644 --- a/dashboard/src/js/client.js +++ b/dashboard/src/js/client.js @@ -1165,7 +1165,7 @@ angular.module('Application').service('Client', ['$http', '$interval', '$timeout }; Client.prototype.setDynamicDnsConfig = function (enabled, callback) { - post('/api/v1/settings/dynamic_dns', { enabled: enabled }, null, function (error, data, status) { + post('/api/v1/network/dynamic_dns', { enabled: enabled }, null, function (error, data, status) { if (error) return callback(error); if (status !== 200) return callback(new ClientError(status, data)); callback(null); @@ -1173,7 +1173,7 @@ angular.module('Application').service('Client', ['$http', '$interval', '$timeout }; Client.prototype.getDynamicDnsConfig = function (callback) { - get('/api/v1/settings/dynamic_dns', null, function (error, data, status) { + get('/api/v1/network/dynamic_dns', null, function (error, data, status) { if (error) return callback(error); if (status !== 200) return callback(new ClientError(status, data)); diff --git a/src/cron.js b/src/cron.js index 88d77715c..d741394c2 100644 --- a/src/cron.js +++ b/src/cron.js @@ -12,6 +12,7 @@ exports = module.exports = { stopJobs, handleSettingsChanged, + dynamicDnsChanged, DEFAULT_AUTOUPDATE_PATTERN, }; @@ -28,6 +29,7 @@ const appHealthMonitor = require('./apphealthmonitor.js'), dyndns = require('./dyndns.js'), eventlog = require('./eventlog.js'), janitor = require('./janitor.js'), + network = require('./network.js'), paths = require('./paths.js'), safe = require('safetydance'), scheduler = require('./scheduler.js'), @@ -163,7 +165,7 @@ async function startJobs() { const tz = allSettings[settings.TIME_ZONE_KEY]; backupPolicyChanged(allSettings[settings.BACKUP_POLICY_KEY], tz); autoupdatePatternChanged(allSettings[settings.AUTOUPDATE_PATTERN_KEY], tz); - dynamicDnsChanged(allSettings[settings.DYNAMIC_DNS_KEY]); + dynamicDnsChanged(await network.getDynamicDns()); } // eslint-disable-next-line no-unused-vars @@ -175,7 +177,6 @@ async function handleSettingsChanged(key, value) { case settings.TIME_ZONE_KEY: case settings.BACKUP_CONFIG_KEY: case settings.AUTOUPDATE_PATTERN_KEY: - case settings.DYNAMIC_DNS_KEY: debug('handleSettingsChanged: recreating all jobs'); await stopJobs(); await startJobs(); diff --git a/src/network.js b/src/network.js index c7794933d..a20033478 100644 --- a/src/network.js +++ b/src/network.js @@ -3,10 +3,14 @@ exports = module.exports = { getBlocklist, setBlocklist, + + getDynamicDns, + setDynamicDns, }; const assert = require('assert'), BoxError = require('./boxerror.js'), + cron = require('./cron.js'), ipaddr = require('ipaddr.js'), path = require('path'), paths = require('./paths.js'), @@ -53,3 +57,15 @@ async function setBlocklist(blocklist, auditSource) { const [error] = await safe(shell.promises.sudo('setBlocklist', [ SET_BLOCKLIST_CMD ], {})); if (error) throw new BoxError(BoxError.IPTABLES_ERROR, `Error setting blocklist: ${error.message}`); } + +async function getDynamicDns() { + const enabled = await settings.get(settings.DYNAMIC_DNS_KEY); + return enabled ? !!enabled : false; // db holds string values only +} + +async function setDynamicDns(enabled) { + assert.strictEqual(typeof enabled, 'boolean'); + + await settings.set(settings.DYNAMIC_DNS_KEY, enabled ? 'enabled' : ''); // db holds string values only + cron.dynamicDnsChanged(enabled); +} diff --git a/src/routes/network.js b/src/routes/network.js index fe460a44b..02695e09e 100644 --- a/src/routes/network.js +++ b/src/routes/network.js @@ -5,7 +5,10 @@ exports = module.exports = { setBlocklist, getTrustedIps, - setTrustedIps + setTrustedIps, + + getDynamicDns, + setDynamicDns }; const assert = require('assert'), @@ -54,3 +57,21 @@ async function setTrustedIps(req, res, next) { next(new HttpSuccess(200, {})); } + +async function getDynamicDns(req, res, next) { + const [error, enabled] = await safe(network.getDynamicDns()); + if (error) return next(BoxError.toHttpError(error)); + + next(new HttpSuccess(200, { enabled })); +} + +async function setDynamicDns(req, res, next) { + assert.strictEqual(typeof req.body, 'object'); + + if (typeof req.body.enabled !== 'boolean') return next(new HttpError(400, 'enabled boolean is required')); + + const [error] = await safe(network.setDynamicDns(req.body.enabled)); + if (error) return next(BoxError.toHttpError(error)); + + next(new HttpSuccess(200, {})); +} diff --git a/src/routes/settings.js b/src/routes/settings.js index f294c64f3..d357d3f91 100644 --- a/src/routes/settings.js +++ b/src/routes/settings.js @@ -155,24 +155,6 @@ async function setDirectoryServerConfig(req, res, next) { next(new HttpSuccess(200, {})); } -async function getDynamicDnsConfig(req, res, next) { - const [error, enabled] = await safe(settings.getDynamicDnsConfig()); - if (error) return next(BoxError.toHttpError(error)); - - next(new HttpSuccess(200, { enabled })); -} - -async function setDynamicDnsConfig(req, res, next) { - assert.strictEqual(typeof req.body, 'object'); - - if (typeof req.body.enabled !== 'boolean') return next(new HttpError(400, 'enabled boolean is required')); - - const [error] = await safe(settings.setDynamicDnsConfig(req.body.enabled)); - if (error) return next(BoxError.toHttpError(error)); - - next(new HttpSuccess(200, {})); -} - async function getBackupPolicy(req, res, next) { const [error, policy] = await safe(settings.getBackupPolicy()); if (error) return next(BoxError.toHttpError(error)); @@ -294,7 +276,6 @@ function get(req, res, next) { switch (req.params.setting) { case settings.BACKUP_POLICY_KEY: return getBackupPolicy(req, res, next); - case settings.DYNAMIC_DNS_KEY: return getDynamicDnsConfig(req, res, next); case settings.IPV6_CONFIG_KEY: return getIPv6Config(req, res, next); case settings.BACKUP_CONFIG_KEY: return getBackupConfig(req, res, next); case settings.EXTERNAL_LDAP_KEY: return getExternalLdapConfig(req, res, next); @@ -317,7 +298,6 @@ function set(req, res, next) { switch (req.params.setting) { case settings.BACKUP_POLICY_KEY: return setBackupPolicy(req, res, next); - case settings.DYNAMIC_DNS_KEY: return setDynamicDnsConfig(req, res, next); case settings.IPV6_CONFIG_KEY: return setIPv6Config(req, res, next); case settings.EXTERNAL_LDAP_KEY: return setExternalLdapConfig(req, res, next); case settings.DIRECTORY_SERVER_KEY: return setDirectoryServerConfig(req, res, next); diff --git a/src/routes/test/network-test.js b/src/routes/test/network-test.js new file mode 100644 index 000000000..81e9325ef --- /dev/null +++ b/src/routes/test/network-test.js @@ -0,0 +1,52 @@ +'use strict'; + +/* global it:false */ +/* global describe:false */ +/* global before:false */ +/* global after:false */ + +const common = require('./common.js'), + expect = require('expect.js'), + superagent = require('superagent'); + +describe('Network API', function () { + const { setup, cleanup, serverUrl, owner } = common; + + before(setup); + after(cleanup); + + describe('dynamic dns', function () { + it('get default succeeds', async function () { + const response = await superagent.get(`${serverUrl}/api/v1/network/dynamic_dns`) + .query({ access_token: owner.token }); + + expect(response.statusCode).to.equal(200); + expect(response.body.enabled).to.be(false); + }); + + it('cannot set without enabled', async function () { + const response = await superagent.post(`${serverUrl}/api/v1/network/dynamic_dns`) + .query({ access_token: owner.token }) + .ok(() => true); + + expect(response.statusCode).to.equal(400); + }); + + it('cannot set', async function () { + const response = await superagent.post(`${serverUrl}/api/v1/network/dynamic_dns`) + .query({ access_token: owner.token }) + .send({ enabled: true }) + .ok(() => true); + + expect(response.statusCode).to.equal(200); + }); + + it('get succeeds', async function () { + const response = await superagent.get(`${serverUrl}/api/v1/network/dynamic_dns`) + .query({ access_token: owner.token }); + + expect(response.statusCode).to.equal(200); + expect(response.body.enabled).to.be(true); + }); + }); +}); diff --git a/src/server.js b/src/server.js index ae43661e3..0091d355d 100644 --- a/src/server.js +++ b/src/server.js @@ -290,6 +290,8 @@ async function initializeExpressSync() { router.post('/api/v1/network/blocklist', json, token, authorizeOwner, routes.network.setBlocklist); router.get ('/api/v1/network/trusted_ips', token, authorizeOwner, routes.network.getTrustedIps); router.post('/api/v1/network/trusted_ips', json, token, authorizeOwner, routes.network.setTrustedIps); + router.get ('/api/v1/network/dynamic_dns', token, authorizeOwner, routes.network.getDynamicDns); + router.post('/api/v1/network/dynamic_dns', json, token, authorizeOwner, routes.network.setDynamicDns); // settings routes (these are for the settings tab - avatar & name have public routes for normal users. see above) router.get ('/api/v1/settings/:setting', token, authorizeAdmin, routes.settings.get); diff --git a/src/settings.js b/src/settings.js index 92bdefb8e..a66012a62 100644 --- a/src/settings.js +++ b/src/settings.js @@ -7,9 +7,6 @@ exports = module.exports = { getTimeZone, setTimeZone, - getDynamicDnsConfig, - setDynamicDnsConfig, - getIPv6Config, setIPv6Config, @@ -148,7 +145,6 @@ const gDefaults = (function () { result[exports.AUTOUPDATE_PATTERN_KEY] = cron.DEFAULT_AUTOUPDATE_PATTERN; result[exports.TIME_ZONE_KEY] = 'UTC'; result[exports.CLOUDRON_COOKIE_SECRET_KEY] = ''; - result[exports.DYNAMIC_DNS_KEY] = false; result[exports.IPV6_CONFIG_KEY] = { provider: 'noop' }; @@ -276,19 +272,6 @@ async function getTimeZone() { return tz; } -async function getDynamicDnsConfig() { - const enabled = await get(exports.DYNAMIC_DNS_KEY); - if (enabled === null) return gDefaults[exports.DYNAMIC_DNS_KEY]; - return !!enabled; // db holds string values only -} - -async function setDynamicDnsConfig(enabled) { - assert.strictEqual(typeof enabled, 'boolean'); - - await set(exports.DYNAMIC_DNS_KEY, enabled ? 'enabled' : ''); // db holds string values only - notifyChange(exports.DYNAMIC_DNS_KEY, enabled); -} - async function getIPv6Config() { const value = await get(exports.IPV6_CONFIG_KEY); if (value === null) return gDefaults[exports.IPV6_CONFIG_KEY]; @@ -547,7 +530,6 @@ async function list() { settings.forEach(function (setting) { result[setting.name] = setting.value; }); // convert booleans - result[exports.DYNAMIC_DNS_KEY] = !!result[exports.DYNAMIC_DNS_KEY]; result[exports.DEMO_KEY] = !!result[exports.DEMO_KEY]; // convert JSON objects diff --git a/src/test/network-test.js b/src/test/network-test.js index cdf4a027e..d94eeb3f5 100644 --- a/src/test/network-test.js +++ b/src/test/network-test.js @@ -109,4 +109,18 @@ describe('Network', function () { expect(result).to.equal('2001:db8:1234::'); }); }); + + describe('Dynamic DNS', function () { + it('can get default dyndns', async function () { + expect(await network.getDynamicDns()).to.be(false); + }); + + it('can set dyndns', async function () { + await network.setDynamicDns(true); + }); + + it('can get dyndns', async function () { + expect(await network.getDynamicDns()).to.be(true); + }); + }); });