diff --git a/src/apps.js b/src/apps.js index 7f4cd3256..5301f2863 100644 --- a/src/apps.js +++ b/src/apps.js @@ -160,6 +160,7 @@ const appstore = require('./appstore.js'), BoxError = require('./boxerror.js'), constants = require('./constants.js'), CronJob = require('cron').CronJob, + dashboard = require('./dashboard.js'), database = require('./database.js'), debug = require('debug')('box:apps'), dns = require('./dns.js'), @@ -181,7 +182,6 @@ const appstore = require('./appstore.js'), safe = require('safetydance'), semver = require('semver'), services = require('./services.js'), - settings = require('./settings.js'), shell = require('./shell.js'), storage = require('./storage.js'), system = require('./system.js'), @@ -1270,6 +1270,7 @@ async function validateLocations(locations) { constants.IMAP_SUBDOMAIN ]; + const { fqdn:dashboardFqdn } = await dashboard.getLocation(); for (const location of locations) { if (!(location.domain in domainObjectMap)) return new BoxError(BoxError.BAD_FIELD, `No such domain in ${location.type} location`); @@ -1281,7 +1282,7 @@ async function validateLocations(locations) { if (RESERVED_SUBDOMAINS.indexOf(subdomain) !== -1) return new BoxError(BoxError.BAD_FIELD, `subdomain '${subdomain}' is reserved`); - if (dns.fqdn(subdomain, location.domain) === settings.dashboardFqdn()) return new BoxError(BoxError.BAD_FIELD, `subdomain '${subdomain}' is reserved`); + if (dns.fqdn(subdomain, location.domain) === dashboardFqdn) return new BoxError(BoxError.BAD_FIELD, `subdomain '${subdomain}' is reserved for dashboard`); const error = dns.validateHostname(subdomain, location.domain); if (error) return new BoxError(BoxError.BAD_FIELD, `Bad ${location.type} location: ${error.message}`); diff --git a/src/appstore.js b/src/appstore.js index a8cba2075..c0ad2c78d 100644 --- a/src/appstore.js +++ b/src/appstore.js @@ -37,6 +37,7 @@ const apps = require('./apps.js'), assert = require('assert'), BoxError = require('./boxerror.js'), constants = require('./constants.js'), + dashboard = require('./dashboard.js'), debug = require('debug')('box:appstore'), eventlog = require('./eventlog.js'), network = require('./network.js'), @@ -346,7 +347,8 @@ async function registerWithLoginCredentials(options) { if (options.signup) await registerUser(options.email, options.password); const result = await login(options.email, options.password, options.totpToken || ''); - await registerCloudron({ domain: settings.dashboardDomain(), accessToken: result.accessToken, version: constants.VERSION }); + const { domain } = await dashboard.getLocation(); + await registerCloudron({ domain, accessToken: result.accessToken, version: constants.VERSION }); for (const app of await apps.list()) { await purchaseApp({ appId: app.id, appstoreId: app.appStoreId, manifestId: app.manifest.id || 'customapp' }); diff --git a/src/backuptask.js b/src/backuptask.js index 536083480..74b146e85 100644 --- a/src/backuptask.js +++ b/src/backuptask.js @@ -27,7 +27,6 @@ const apps = require('./apps.js'), paths = require('./paths.js'), safe = require('safetydance'), services = require('./services.js'), - settings = require('./settings.js'), shell = require('./shell.js'), storage = require('./storage.js'); @@ -117,8 +116,6 @@ async function restore(backupConfig, remotePath, progressCallback) { await database.importFromFile(`${dataLayout.localRoot()}/box.mysqldump`); debug('restore: database imported'); - - await settings.initCache(); } async function downloadApp(app, restoreConfig, progressCallback) { diff --git a/src/cloudron.js b/src/cloudron.js index f55d57c92..ee558f784 100644 --- a/src/cloudron.js +++ b/src/cloudron.js @@ -31,6 +31,7 @@ const apps = require('./apps.js'), constants = require('./constants.js'), cron = require('./cron.js'), debug = require('debug')('box:cloudron'), + dashboard = require('./dashboard.js'), dns = require('./dns.js'), dockerProxy = require('./dockerproxy.js'), eventlog = require('./eventlog.js'), @@ -104,9 +105,9 @@ async function runStartupTasks() { // always generate webadmin config since we have no versioning mechanism for the ejs tasks.push(async function () { - if (!settings.dashboardDomain()) return; + if (!await dashboard.getDomain()) return; - await reverseProxy.writeDashboardConfig(settings.dashboardDomain()); + await reverseProxy.writeDashboardConfig(await dashboard.getDomain()); }); tasks.push(async function () { @@ -142,14 +143,15 @@ async function getConfig() { if (release === null) throw new BoxError(BoxError.FS_ERROR, safe.error.message); const ubuntuVersion = release.match(/DISTRIB_DESCRIPTION="(.*)"/)[1]; const profileConfig = await users.getProfileConfig(); + const { fqdn:adminFqdn, domain:adminDomain } = await dashboard.getLocation(); // be picky about what we send out here since this is sent for 'normal' users as well return { apiServerOrigin: await appstore.getApiServerOrigin(), webServerOrigin: await appstore.getWebServerOrigin(), consoleServerOrigin: await appstore.getConsoleServerOrigin(), - adminDomain: settings.dashboardDomain(), - adminFqdn: settings.dashboardFqdn(), + adminDomain, + adminFqdn, mailFqdn: await mailServer.getLocation().fqdn, version: constants.VERSION, ubuntuVersion, @@ -192,7 +194,7 @@ async function setDashboardDomain(domain, auditSource) { await reverseProxy.writeDashboardConfig(domain); const fqdn = dns.fqdn(constants.DASHBOARD_SUBDOMAIN, domain); - await settings.setDashboardLocation(domain, fqdn); + await dashboard.setLocation(domain, fqdn); await safe(appstore.updateCloudron({ domain }), { debug }); diff --git a/src/dashboard.js b/src/dashboard.js new file mode 100644 index 000000000..dd0912201 --- /dev/null +++ b/src/dashboard.js @@ -0,0 +1,26 @@ +'use strict'; + +exports = module.exports = { + getLocation, + setLocation +}; + +const assert = require('assert'), + constants = require('./constants.js'), + settings = require('./settings.js'); + +async function setLocation(domain, fqdn) { + assert.strictEqual(typeof domain, 'string'); + assert.strictEqual(typeof fqdn, 'string'); + + // should probably be just one key. or make this a transaction + await settings.set(settings.DASHBOARD_DOMAIN_KEY, domain); + await settings.set(settings.DASHBOARD_FQDN_KEY, fqdn); +} + +async function getLocation() { + const domain = await settings.get(settings.DASHBOARD_DOMAIN_KEY) || ''; + const fqdn = await settings.get(settings.DASHBOARD_FQDN_KEY) || ''; + + return { domain, fqdn, subdomain: constants.DASHBOARD_SUBDOMAIN }; +} diff --git a/src/dns.js b/src/dns.js index c9d3423f0..612321a5c 100644 --- a/src/dns.js +++ b/src/dns.js @@ -26,6 +26,7 @@ const apps = require('./apps.js'), assert = require('assert'), BoxError = require('./boxerror.js'), constants = require('./constants.js'), + dashboard = require('./dashboard.js'), debug = require('debug')('box:dns'), domains = require('./domains.js'), ipaddr = require('ipaddr.js'), @@ -34,7 +35,6 @@ const apps = require('./apps.js'), network = require('./network.js'), promiseRetry = require('./promise-retry.js'), safe = require('safetydance'), - settings = require('./settings.js'), tasks = require('./tasks.js'), tld = require('tldjs'); @@ -281,6 +281,7 @@ async function syncDnsRecords(options, progressCallback) { if (options.domain) allDomains = allDomains.filter(d => d.domain === options.domain); const { domain:mailDomain, fqdn:mailFqdn, subdomain:mailSubdomain } = await mailServer.getLocation(); + const { domain:dashboardDomain, fqdn:dashboardFqdn } = await dashboard.getLocation(); const allApps = await apps.list(); @@ -292,8 +293,8 @@ async function syncDnsRecords(options, progressCallback) { progress += Math.round(100/(1+allDomains.length)); let locations = []; - if (domain.domain === settings.dashboardDomain()) locations.push({ subdomain: constants.DASHBOARD_SUBDOMAIN, domain: settings.dashboardDomain() }); - if (domain.domain === mailDomain && mailFqdn !== settings.dashboardFqdn()) locations.push({ subdomain: mailSubdomain, domain: mailDomain }); + if (domain.domain === dashboardDomain) locations.push({ subdomain: constants.DASHBOARD_SUBDOMAIN, domain: dashboardDomain }); + if (domain.domain === mailDomain && mailFqdn !== dashboardFqdn) locations.push({ subdomain: mailSubdomain, domain: mailDomain }); for (const app of allApps) { const appLocations = [{ subdomain: app.subdomain, domain: app.domain }] diff --git a/src/docker.js b/src/docker.js index 6b9ebd47c..4fb11fcd0 100644 --- a/src/docker.js +++ b/src/docker.js @@ -38,6 +38,7 @@ const apps = require('./apps.js'), assert = require('assert'), BoxError = require('./boxerror.js'), constants = require('./constants.js'), + dashboard = require('./dashboard.js'), debug = require('debug')('box:docker'), Docker = require('dockerode'), paths = require('./paths.js'), @@ -269,13 +270,14 @@ async function createSubcontainer(app, name, cmd, options) { const manifest = app.manifest; const exposedPorts = {}, dockerPortBindings = { }; const domain = app.fqdn; + const { fqdn:dashboardFqdn } = await dashboard.getLocation(); const stdEnv = [ 'CLOUDRON=1', 'CLOUDRON_PROXY_IP=172.18.0.1', `CLOUDRON_APP_HOSTNAME=${app.id}`, - `CLOUDRON_WEBADMIN_ORIGIN=https://${settings.dashboardFqdn()}`, - `CLOUDRON_API_ORIGIN=https://${settings.dashboardFqdn()}`, + `CLOUDRON_WEBADMIN_ORIGIN=https://${dashboardFqdn}`, + `CLOUDRON_API_ORIGIN=https://${dashboardFqdn}`, `CLOUDRON_APP_ORIGIN=https://${domain}`, `CLOUDRON_APP_DOMAIN=${domain}` ]; diff --git a/src/domains.js b/src/domains.js index 9ba42551b..ca6c8ee7f 100644 --- a/src/domains.js +++ b/src/domains.js @@ -19,13 +19,13 @@ const assert = require('assert'), BoxError = require('./boxerror.js'), constants = require('./constants.js'), crypto = require('crypto'), + dashboard = require('./dashboard.js'), database = require('./database.js'), debug = require('debug')('box:domains'), eventlog = require('./eventlog.js'), mailServer = require('./mailserver.js'), reverseProxy = require('./reverseproxy.js'), safe = require('safetydance'), - settings = require('./settings.js'), tld = require('tldjs'), _ = require('underscore'); @@ -152,8 +152,9 @@ async function add(domain, data, auditSource) { const dkimKey = await mailServer.generateDkimKey(); if (!dkimSelector) { + const { domain:dashboardDomain } = await dashboard.getLocation(); // create a unique suffix. this lets one add this domain can be added in another cloudron instance and not have their dkim selector conflict - const suffix = crypto.createHash('sha256').update(settings.dashboardDomain()).digest('hex').substr(0, 6); + const suffix = crypto.createHash('sha256').update(dashboardDomain).digest('hex').substr(0, 6); dkimSelector = `cloudron-${suffix}`; } @@ -201,7 +202,8 @@ async function setConfig(domain, data, auditSource) { let { zoneName, provider, config, fallbackCertificate, tlsConfig } = data; - if (constants.DEMO && (domain === settings.dashboardDomain())) throw new BoxError(BoxError.CONFLICT, 'Not allowed in demo mode'); + const { domain:dashboardDomain } = await dashboard.getLocation(); + if (constants.DEMO && (domain === dashboardDomain)) throw new BoxError(BoxError.CONFLICT, 'Not allowed in demo mode'); const domainObject = await get(domain); if (zoneName) { @@ -270,7 +272,8 @@ async function del(domain, auditSource) { assert.strictEqual(typeof domain, 'string'); assert.strictEqual(typeof auditSource, 'object'); - if (domain === settings.dashboardDomain()) throw new BoxError(BoxError.CONFLICT, 'Cannot remove admin domain'); + const { domain:dashboardDomain } = await dashboard.getLocation(); + if (domain === dashboardDomain) throw new BoxError(BoxError.CONFLICT, 'Cannot remove admin domain'); const { domain:mailDomain } = await mailServer.getLocation(); if (domain === mailDomain) throw new BoxError(BoxError.CONFLICT, 'Cannot remove mail domain. Change the mail server location first'); diff --git a/src/dyndns.js b/src/dyndns.js index 11b66113e..c738ff79e 100644 --- a/src/dyndns.js +++ b/src/dyndns.js @@ -7,14 +7,13 @@ exports = module.exports = { const apps = require('./apps.js'), assert = require('assert'), - constants = require('./constants.js'), + dashboard = require('./dashboard.js'), debug = require('debug')('box:dyndns'), dns = require('./dns.js'), eventlog = require('./eventlog.js'), network = require('./network.js'), paths = require('./paths.js'), safe = require('safetydance'), - settings = require('./settings.js'), tasks = require('./tasks.js'); // FIXME: this races with apptask. can result in a conflict if apptask is doing some dns operation and this code changes entries @@ -59,9 +58,10 @@ async function sync(ipv4, ipv6, auditSource, progressCallback) { assert.strictEqual(typeof progressCallback, 'function'); let percent = 10; - progressCallback({ percent, message: `Updating dashboard domain ${settings.dashboardFqdn()}`}); - if (ipv4) await safe(dns.upsertDnsRecords(constants.DASHBOARD_SUBDOMAIN, settings.dashboardDomain(), 'A', [ ipv4 ]), { debug }); - if (ipv6) await safe(dns.upsertDnsRecords(constants.DASHBOARD_SUBDOMAIN, settings.dashboardDomain(), 'AAAA', [ ipv6 ]), { debug }); + const { domain:dashboardDomain, fqdn:dashboardFqdn, subdomain:dashboardSubdomain } = await dashboard.getLocation(); + progressCallback({ percent, message: `Updating dashboard domain ${dashboardFqdn}`}); + if (ipv4) await safe(dns.upsertDnsRecords(dashboardSubdomain, dashboardDomain, 'A', [ ipv4 ]), { debug }); + if (ipv6) await safe(dns.upsertDnsRecords(dashboardSubdomain, dashboardDomain, 'AAAA', [ ipv6 ]), { debug }); const result = await apps.list(); for (const app of result) { diff --git a/src/mailer.js b/src/mailer.js index 6658dba9c..0b7595499 100644 --- a/src/mailer.js +++ b/src/mailer.js @@ -18,13 +18,13 @@ exports = module.exports = { const assert = require('assert'), BoxError = require('./boxerror.js'), branding = require('./branding.js'), + dashboard = require('./dashboard.js'), debug = require('debug')('box:mailer'), ejs = require('ejs'), mailServer = require('./mailserver.js'), nodemailer = require('nodemailer'), path = require('path'), safe = require('safetydance'), - settings = require('./settings.js'), support = require('./support.js'), translation = require('./translation.js'), util = require('util'); @@ -35,10 +35,11 @@ const MAIL_TEMPLATES_DIR = path.join(__dirname, 'mail_templates'); async function getMailConfig() { const cloudronName = await branding.getCloudronName(); const supportConfig = await support.getConfig(); + const { domain:dashboardDomain } = await dashboard.getLocation(); return { cloudronName, - notificationFrom: `"${cloudronName}" `, + notificationFrom: `"${cloudronName}" `, supportEmail: supportConfig.email }; } @@ -51,13 +52,14 @@ async function sendMail(mailOptions) { return; } + const { domain:dashboardDomain } = await dashboard.getLocation(); const data = await mailServer.getMailAuth(); const transport = nodemailer.createTransport({ host: data.ip, port: data.port, auth: { - user: mailOptions.authUser || `no-reply@${settings.dashboardDomain()}`, + user: mailOptions.authUser || `no-reply@${dashboardDomain}`, pass: data.relayToken } }); @@ -98,14 +100,15 @@ async function sendInvite(user, invitor, email, inviteLink) { const mailConfig = await getMailConfig(); const translationAssets = await translation.getTranslations(); + const { fqdn:dashboardFqdn } = await dashboard.getLocation(); const templateData = { user: user.displayName || user.username || user.email, - webadminUrl: `https://${settings.dashboardFqdn()}`, + webadminUrl: `https://${dashboardFqdn}`, inviteLink: inviteLink, invitor: invitor ? invitor.email : null, cloudronName: mailConfig.cloudronName, - cloudronAvatarUrl: `https://${settings.dashboardFqdn()}/api/v1/cloudron/avatar` + cloudronAvatarUrl: `https://${dashboardFqdn}/api/v1/cloudron/avatar` }; const mailOptions = { @@ -132,6 +135,7 @@ async function sendNewLoginLocation(user, loginLocation) { const mailConfig = await getMailConfig(); const translationAssets = await translation.getTranslations(); + const { fqdn:dashboardFqdn } = await dashboard.getLocation(); const templateData = { user: user.displayName || user.username || user.email, @@ -140,7 +144,7 @@ async function sendNewLoginLocation(user, loginLocation) { country, city, cloudronName: mailConfig.cloudronName, - cloudronAvatarUrl: `https://${settings.dashboardFqdn()}/api/v1/cloudron/avatar` + cloudronAvatarUrl: `https://${dashboardFqdn}/api/v1/cloudron/avatar` }; const mailOptions = { @@ -161,12 +165,13 @@ async function passwordReset(user, email, resetLink) { const mailConfig = await getMailConfig(); const translationAssets = await translation.getTranslations(); + const { fqdn:dashboardFqdn } = await dashboard.getLocation(); const templateData = { user: user.displayName || user.username || user.email, resetLink: resetLink, cloudronName: mailConfig.cloudronName, - cloudronAvatarUrl: `https://${settings.dashboardFqdn()}/api/v1/cloudron/avatar` + cloudronAvatarUrl: `https://${dashboardFqdn}/api/v1/cloudron/avatar` }; const mailOptions = { diff --git a/src/notifications.js b/src/notifications.js index 3f770c24b..169789a8f 100644 --- a/src/notifications.js +++ b/src/notifications.js @@ -28,10 +28,10 @@ const assert = require('assert'), BoxError = require('./boxerror.js'), changelog = require('./changelog.js'), constants = require('./constants.js'), + dashboard = require('./dashboard.js'), database = require('./database.js'), eventlog = require('./eventlog.js'), mailer = require('./mailer.js'), - settings = require('./settings.js'), users = require('./users.js'); const NOTIFICATION_FIELDS = [ 'id', 'eventId', 'title', 'message', 'creationTime', 'acknowledged' ]; @@ -129,6 +129,7 @@ async function oomEvent(eventId, containerId, app, addonName /*, event*/) { assert(app || addonName); + const { fqdn:dashboardFqdn } = await dashboard.getLocation(); let title, message; if (addonName) { if (app) { @@ -136,10 +137,10 @@ async function oomEvent(eventId, containerId, app, addonName /*, event*/) { } else { title = `The ${addonName} service ran out of memory`; } - message = `The service has been restarted automatically. If you see this notification often, consider increasing the [memory limit](https://${settings.dashboardFqdn()}/#/services)`; + message = `The service has been restarted automatically. If you see this notification often, consider increasing the [memory limit](https://${dashboardFqdn}/#/services)`; } else if (app) { title = `The app at ${app.fqdn} ran out of memory.`; - message = `The app has been restarted automatically. If you see this notification often, consider increasing the [memory limit](https://${settings.dashboardFqdn()}/#/app/${app.id}/resources)`; + message = `The app has been restarted automatically. If you see this notification often, consider increasing the [memory limit](https://${dashboardFqdn}/#/app/${app.id}/resources)`; } await add(title, message, { eventId }); @@ -218,9 +219,10 @@ async function backupFailed(eventId, taskId, errorMessage) { } if (count !== 3) return; // less than 3 failures + const { fqdn:dashboardFqdn } = await dashboard.getLocation(); const superadmins = await users.getSuperadmins(); for (const superadmin of superadmins) { - await mailer.backupFailed(superadmin.email, errorMessage, `https://${settings.dashboardFqdn()}/logs.html?taskId=${taskId}`); + await mailer.backupFailed(superadmin.email, errorMessage, `https://${dashboardFqdn}/logs.html?taskId=${taskId}`); } } diff --git a/src/oidc.js b/src/oidc.js index fafcaa14e..1f9a37928 100644 --- a/src/oidc.js +++ b/src/oidc.js @@ -19,6 +19,7 @@ const assert = require('assert'), blobs = require('./blobs.js'), branding = require('./branding.js'), constants = require('./constants.js'), + dashboard = require('./dashboard.js'), database = require('./database.js'), debug = require('debug')('box:oidc'), ejs = require('ejs'), @@ -79,13 +80,14 @@ async function clientsGet(id) { assert.strictEqual(typeof id, 'string'); if (id === tokens.ID_WEBADMIN) { + const { fqdn:dashboardFqdn } = await dashboard.getLocation(); return { id: tokens.ID_WEBADMIN, secret: 'notused', application_type: 'web', response_types: ['code', 'code token'], grant_types: ['authorization_code', 'implicit'], - loginRedirectUri: `https://${settings.dashboardFqdn()}/authcallback.html` + loginRedirectUri: `https://${dashboardFqdn}/authcallback.html` }; } else if (id === tokens.ID_DEVELOPMENT) { return { @@ -789,8 +791,9 @@ async function start() { } }; - debug(`start: create provider for ${settings.dashboardFqdn()} at ${ROUTE_PREFIX}`); - const provider = new Provider(`https://${settings.dashboardFqdn()}${ROUTE_PREFIX}`, configuration); + const { fqdn:dashboardFqdn } = await dashboard.getLocation(); + debug(`start: create provider for ${dashboardFqdn} at ${ROUTE_PREFIX}`); + const provider = new Provider(`https://${dashboardFqdn}${ROUTE_PREFIX}`, configuration); app.enable('trust proxy'); provider.proxy = true; diff --git a/src/provision.js b/src/provision.js index 57fd2d854..3fa68f94d 100644 --- a/src/provision.js +++ b/src/provision.js @@ -11,6 +11,7 @@ const assert = require('assert'), backups = require('./backups.js'), backuptask = require('./backuptask.js'), BoxError = require('./boxerror.js'), + dashboard = require('./dashboard.js'), constants = require('./constants.js'), cloudron = require('./cloudron.js'), debug = require('debug')('box:provision'), @@ -24,7 +25,6 @@ const assert = require('assert'), reverseProxy = require('./reverseproxy.js'), safe = require('safetydance'), semver = require('semver'), - settings = require('./settings.js'), paths = require('./paths.js'), system = require('./system.js'), users = require('./users.js'), @@ -64,7 +64,7 @@ async function ensureDhparams() { async function unprovision() { // TODO: also cancel any existing configureWebadmin task - await settings.setDashboardLocation('', ''); + await dashboard.setLocation('', ''); await mail.clearDomains(); await domains.clear(); } @@ -177,8 +177,8 @@ async function restoreTask(backupConfig, remotePath, ipv4Config, options, auditS await network.setIPv4Config(ipv4Config); await reverseProxy.restoreFallbackCertificates(); - const dashboardDomain = settings.dashboardDomain(); // load this fresh from after the backup.restore - if (!options.skipDnsSetup) await cloudron.setupDnsAndCert(constants.DASHBOARD_SUBDOMAIN, dashboardDomain, auditSource, (progress) => setProgress('restore', progress.message)); + const { subdomain:dashboardSubdomain, domain:dashboardDomain } = await dashboard.getLocation(); // load this fresh from after the backup.restore + if (!options.skipDnsSetup) await cloudron.setupDnsAndCert(dashboardSubdomain, dashboardDomain, auditSource, (progress) => setProgress('restore', progress.message)); await cloudron.setDashboardDomain(dashboardDomain, auditSource); await backups.setConfig(backupConfig); @@ -246,7 +246,8 @@ async function restore(backupConfig, remotePath, version, ipv4Config, options, a } async function getStatus() { - gStatus.adminFqdn = settings.dashboardDomain() ? settings.dashboardFqdn() : null; // indicator for main.js if dnssetup was already done but not activated + const { fqdn:dashboardFqdn } = await dashboard.getLocation(); + gStatus.adminFqdn = dashboardFqdn; // indicator for main.js if dnssetup was already done but not activated gStatus.activated = await users.isActivated(); // indicator for admin setup gStatus.provider = await system.getProvider(); // preselect dns provider and ami token return gStatus; diff --git a/src/proxyauth.js b/src/proxyauth.js index 9b12c4b10..2fba94f51 100644 --- a/src/proxyauth.js +++ b/src/proxyauth.js @@ -12,6 +12,7 @@ const apps = require('./apps.js'), basicAuth = require('basic-auth'), blobs = require('./blobs.js'), constants = require('./constants.js'), + dashboard = require('./dashboard.js'), debug = require('debug')('box:proxyAuth'), ejs = require('ejs'), express = require('express'), @@ -24,7 +25,6 @@ const apps = require('./apps.js'), path = require('path'), paths = require('./paths.js'), safe = require('safetydance'), - settings = require('./settings.js'), translation = require('./translation.js'), users = require('./users.js'), util = require('util'); @@ -101,7 +101,8 @@ async function loginPage(req, res, next) { } const icon = 'data:image/png;base64,' + iconBuffer.toString('base64'); - const dashboardOrigin = `https://${settings.dashboardFqdn()}`; + const { fqdn:dashboardFqdn } = await dashboard.getLocation(); + const dashboardOrigin = `https://${dashboardFqdn}`; try { finalContent = ejs.render(translatedContent, { title, icon, dashboardOrigin }); diff --git a/src/reverseproxy.js b/src/reverseproxy.js index b80ee9ef5..0cf56cfc9 100644 --- a/src/reverseproxy.js +++ b/src/reverseproxy.js @@ -41,6 +41,7 @@ const acme2 = require('./acme2.js'), BoxError = require('./boxerror.js'), constants = require('./constants.js'), crypto = require('crypto'), + dashboard = require('./dashboard.js'), debug = require('debug')('box:reverseproxy'), dns = require('./dns.js'), docker = require('./docker.js'), @@ -306,7 +307,8 @@ async function getMailCertificate() { } async function getDirectoryServerCertificate() { - return await getCertificate({ domain: settings.dashboardDomain(), fqdn: settings.dashboardFqdn(), certificate: null, type: apps.LOCATION_TYPE_DIRECTORY_SERVER }); + const { domain, fqdn } = await dashboard.getLocation(); + return await getCertificate({ domain, fqdn, certificate: null, type: apps.LOCATION_TYPE_DIRECTORY_SERVER }); } // write if contents mismatch (thus preserving mtime) @@ -643,9 +645,11 @@ async function checkCerts(options, auditSource, progressCallback) { assert.strictEqual(typeof progressCallback, 'function'); let locations = []; + const { domain:dashboardDomain, fqdn:dashboardFqdn } = await dashboard.getLocation(); + locations.push({ domain: dashboardDomain, fqdn: dashboardFqdn, certificate: null, type: apps.LOCATION_TYPE_DASHBOARD }); + const { domain:mailDomain, fqdn:mailFqdn } = await mailServer.getLocation(); - if (settings.dashboardFqdn() !== mailFqdn) locations.push({ domain: mailDomain, fqdn: mailFqdn, certificate: null, type: apps.LOCATION_TYPE_MAIL }); - locations.push({ domain: settings.dashboardDomain(), fqdn: settings.dashboardFqdn(), certificate: null, type: apps.LOCATION_TYPE_DASHBOARD }); + if (dashboardFqdn !== mailFqdn) locations.push({ domain: mailDomain, fqdn: mailFqdn, certificate: null, type: apps.LOCATION_TYPE_MAIL }); const allApps = (await apps.list()).filter(app => app.runState !== apps.RSTATE_STOPPED); for (const app of allApps) { @@ -664,7 +668,8 @@ async function checkCerts(options, auditSource, progressCallback) { for (const app of allApps) { await writeAppConfigs(app); } - await writeDashboardConfig(settings.dashboardDomain()); + const { domain:dashboardDomain } = await dashboard.getLocation(); + await writeDashboardConfig(dashboardDomain); await notifyCertChange(); // this allows user to "rebuild" using UI just in case we crashed and went out of sync safe.fs.unlinkSync(paths.REVERSE_PROXY_REBUILD_FILE); } else { diff --git a/src/routes/test/cloudron-test.js b/src/routes/test/cloudron-test.js index 01dca1410..49632e51e 100644 --- a/src/routes/test/cloudron-test.js +++ b/src/routes/test/cloudron-test.js @@ -8,11 +8,10 @@ const constants = require('../../constants.js'), common = require('./common.js'), expect = require('expect.js'), - superagent = require('superagent'), - settings = require('../../settings.js'); + superagent = require('superagent'); describe('Cloudron API', function () { - const { setup, cleanup, serverUrl, owner, user } = common; + const { setup, cleanup, serverUrl, owner, user, dashboardFqdn } = common; before(setup); after(cleanup); @@ -32,7 +31,7 @@ describe('Cloudron API', function () { expect(response.statusCode).to.equal(200); expect(response.body.apiServerOrigin).to.eql('http://localhost:6060'); expect(response.body.webServerOrigin).to.eql('https://cloudron.io'); - expect(response.body.adminFqdn).to.eql(settings.dashboardFqdn()); + expect(response.body.adminFqdn).to.eql(dashboardFqdn); expect(response.body.version).to.eql(constants.VERSION); expect(response.body.cloudronName).to.be.a('string'); }); @@ -44,7 +43,7 @@ describe('Cloudron API', function () { expect(response.statusCode).to.equal(200); expect(response.body.apiServerOrigin).to.eql('http://localhost:6060'); expect(response.body.webServerOrigin).to.eql('https://cloudron.io'); - expect(response.body.adminFqdn).to.eql(settings.dashboardFqdn()); + expect(response.body.adminFqdn).to.eql(dashboardFqdn); expect(response.body.version).to.eql(constants.VERSION); expect(response.body.cloudronName).to.be.a('string'); }); diff --git a/src/routes/test/mail-test.js b/src/routes/test/mail-test.js index 774c7f344..cb3cbbf8e 100644 --- a/src/routes/test/mail-test.js +++ b/src/routes/test/mail-test.js @@ -8,12 +8,11 @@ const common = require('./common.js'), expect = require('expect.js'), mail = require('../../mail.js'), - settings = require('../../settings.js'), superagent = require('superagent'), _ = require('underscore'); describe('Mail API', function () { - const { setup, cleanup, serverUrl, owner, dashboardDomain, mailFqdn } = common; + const { setup, cleanup, serverUrl, owner, dashboardDomain, dashboardFqdn, mailFqdn } = common; let publicKey; before(async () => { @@ -117,14 +116,14 @@ describe('Mail API', function () { expect(response.body.dns.dkim.domain).to.eql(dkimDomain); expect(response.body.dns.dkim.type).to.eql('TXT'); expect(response.body.dns.dkim.value).to.eql(null); - expect(response.body.dns.dkim.expected).to.eql('v=DKIM1; t=s; p=' + publicKey); + expect(response.body.dns.dkim.expected).to.eql(`v=DKIM1; t=s; p=${publicKey}`); expect(response.body.dns.dkim.status).to.eql(false); expect(response.body.dns.spf).to.be.an('object'); expect(response.body.dns.spf.domain).to.eql(spfDomain); expect(response.body.dns.spf.type).to.eql('TXT'); expect(response.body.dns.spf.value).to.eql(null); - expect(response.body.dns.spf.expected).to.eql('v=spf1 a:' + settings.dashboardFqdn() + ' ~all'); + expect(response.body.dns.spf.expected).to.eql(`v=spf1 a:${dashboardFqdn} ~all`); expect(response.body.dns.spf.status).to.eql(false); expect(response.body.dns.dmarc).to.be.an('object'); @@ -136,7 +135,7 @@ describe('Mail API', function () { expect(response.body.dns.mx).to.be.an('object'); expect(response.body.dns.mx.type).to.eql('MX'); expect(response.body.dns.mx.value).to.eql(null); - expect(response.body.dns.mx.expected).to.eql('10 ' + mailFqdn + '.'); + expect(response.body.dns.mx.expected).to.eql(`10 ${mailFqdn}.`); expect(response.body.dns.mx.status).to.eql(false); expect(response.body.dns.ptr).to.be.an('object'); @@ -160,12 +159,12 @@ describe('Mail API', function () { expect(response.statusCode).to.equal(200); expect(response.body.dns.spf).to.be.an('object'); - expect(response.body.dns.spf.expected).to.eql('v=spf1 a:' + settings.dashboardFqdn() + ' ~all'); + expect(response.body.dns.spf.expected).to.eql(`v=spf1 a:${dashboardFqdn} ~all`); expect(response.body.dns.spf.status).to.eql(false); expect(response.body.dns.spf.value).to.eql(null); expect(response.body.dns.dkim).to.be.an('object'); - expect(response.body.dns.dkim.expected).to.eql('v=DKIM1; t=s; p=' + publicKey); + expect(response.body.dns.dkim.expected).to.eql(`v=DKIM1; t=s; p=${publicKey}`); expect(response.body.dns.dkim.status).to.eql(false); expect(response.body.dns.dkim.value).to.eql(null); @@ -176,7 +175,7 @@ describe('Mail API', function () { expect(response.body.dns.mx).to.be.an('object'); expect(response.body.dns.mx.status).to.eql(false); - expect(response.body.dns.mx.expected).to.eql('10 ' + mailFqdn + '.'); + expect(response.body.dns.mx.expected).to.eql(`10 ${mailFqdn}.`); expect(response.body.dns.mx.value).to.eql(null); expect(response.body.dns.ptr).to.be.an('object'); @@ -199,7 +198,7 @@ describe('Mail API', function () { expect(response.statusCode).to.equal(200); expect(response.body.dns.spf).to.be.an('object'); - expect(response.body.dns.spf.expected).to.eql('v=spf1 a:' + settings.dashboardFqdn() + ' a:random.com ~all'); + expect(response.body.dns.spf.expected).to.eql(`v=spf1 a:${dashboardFqdn} a:random.com ~all`); expect(response.body.dns.spf.status).to.eql(false); expect(response.body.dns.spf.value).to.eql('v=spf1 a:random.com ~all'); @@ -215,8 +214,8 @@ describe('Mail API', function () { expect(response.body.dns.mx).to.be.an('object'); expect(response.body.dns.mx.status).to.eql(true); - expect(response.body.dns.mx.expected).to.eql('10 ' + mailFqdn + '.'); - expect(response.body.dns.mx.value).to.eql('20 ' + mailFqdn + '. 10 some.other.server.'); + expect(response.body.dns.mx.expected).to.eql(`10 ${mailFqdn}.`); + expect(response.body.dns.mx.value).to.eql(`20 ${mailFqdn}. 10 some.other.server.`); expect(response.body.dns.ptr).to.be.an('object'); expect(response.body.dns.ptr.expected).to.eql(mailFqdn); @@ -239,8 +238,8 @@ describe('Mail API', function () { expect(response.body.dns.spf).to.be.an('object'); expect(response.body.dns.spf.domain).to.eql(spfDomain); expect(response.body.dns.spf.type).to.eql('TXT'); - expect(response.body.dns.spf.value).to.eql('v=spf1 a:example.com a:' + mailFqdn + ' ~all'); - expect(response.body.dns.spf.expected).to.eql('v=spf1 a:example.com a:' + mailFqdn + ' ~all'); + expect(response.body.dns.spf.value).to.eql(`v=spf1 a:example.com a:${mailFqdn} ~all`); + expect(response.body.dns.spf.expected).to.eql(`v=spf1 a:example.com a:${mailFqdn} ~all`); expect(response.body.dns.spf.status).to.eql(true); }); @@ -266,7 +265,7 @@ describe('Mail API', function () { dnsAnswerQueue[mxDomain].MX = [ { priority: '10', exchange: mailFqdn } ]; dnsAnswerQueue[dmarcDomain].TXT = [['v=DMARC1; p=reject; pct=100']]; dnsAnswerQueue[dkimDomain].TXT = [['v=DKIM1; t=s; p=', publicKey ]]; - dnsAnswerQueue[spfDomain].TXT = [['v=spf1 a:' + settings.dashboardFqdn() + ' ~all']]; + dnsAnswerQueue[spfDomain].TXT = [[`v=spf1 a:${dashboardFqdn} ~all`]]; const response = await superagent.get(`${serverUrl}/api/v1/mail/${dashboardDomain}` + '/status') .query({ access_token: owner.token }); @@ -276,15 +275,15 @@ describe('Mail API', function () { expect(response.body.dns.dkim).to.be.an('object'); expect(response.body.dns.dkim.domain).to.eql(dkimDomain); expect(response.body.dns.dkim.type).to.eql('TXT'); - expect(response.body.dns.dkim.value).to.eql('v=DKIM1; t=s; p=' + publicKey); - expect(response.body.dns.dkim.expected).to.eql('v=DKIM1; t=s; p=' + publicKey); + expect(response.body.dns.dkim.value).to.eql(`v=DKIM1; t=s; p=${publicKey}`); + expect(response.body.dns.dkim.expected).to.eql(`v=DKIM1; t=s; p=${publicKey}`); expect(response.body.dns.dkim.status).to.eql(true); expect(response.body.dns.spf).to.be.an('object'); expect(response.body.dns.spf.domain).to.eql(spfDomain); expect(response.body.dns.spf.type).to.eql('TXT'); - expect(response.body.dns.spf.value).to.eql('v=spf1 a:' + settings.dashboardFqdn() + ' ~all'); - expect(response.body.dns.spf.expected).to.eql('v=spf1 a:' + settings.dashboardFqdn() + ' ~all'); + expect(response.body.dns.spf.value).to.eql(`v=spf1 a:${dashboardFqdn} ~all`); + expect(response.body.dns.spf.expected).to.eql(`v=spf1 a:${dashboardFqdn} ~all`); expect(response.body.dns.spf.status).to.eql(true); expect(response.body.dns.dmarc).to.be.an('object'); @@ -294,8 +293,8 @@ describe('Mail API', function () { expect(response.body.dns.mx).to.be.an('object'); expect(response.body.dns.mx.status).to.eql(true); - expect(response.body.dns.mx.expected).to.eql('10 ' + mailFqdn + '.'); - expect(response.body.dns.mx.value).to.eql('10 ' + mailFqdn + '.'); + expect(response.body.dns.mx.expected).to.eql(`10 ${mailFqdn}.`); + expect(response.body.dns.mx.value).to.eql(`10 ${mailFqdn}.`); }); }); diff --git a/src/scripts/backupupload.js b/src/scripts/backupupload.js index 816d400af..cbdf59c7c 100755 --- a/src/scripts/backupupload.js +++ b/src/scripts/backupupload.js @@ -10,8 +10,7 @@ if (process.argv[2] === '--check') { const backuptask = require('../backuptask.js'), database = require('../database.js'), debug = require('debug')('box:backupupload'), - safe = require('safetydance'), - settings = require('../settings.js'); + safe = require('safetydance'); // Main process starts here const remotePath = process.argv[2]; @@ -44,7 +43,6 @@ function throttledProgressCallback(msecs) { (async function main() { await database.initialize(); - await settings.initCache(); const [uploadError] = await safe(backuptask.upload(remotePath, format, dataLayoutString, throttledProgressCallback(5000))); debug('upload completed. error: %o', uploadError); diff --git a/src/server.js b/src/server.js index 1e338f6df..c6aa4ae3d 100644 --- a/src/server.js +++ b/src/server.js @@ -462,7 +462,6 @@ async function start() { debug('=========================================='); await database.initialize(); - await settings.initCache(); // pre-load very often used settings await cloudron.initialize(); gHttpServer = await initializeExpressSync(); diff --git a/src/services.js b/src/services.js index c82d274c4..1e97e8410 100644 --- a/src/services.js +++ b/src/services.js @@ -39,6 +39,7 @@ const addonConfigs = require('./addonconfigs.js'), BoxError = require('./boxerror.js'), constants = require('./constants.js'), crypto = require('crypto'), + dashboard = require('./dashboard.js'), debug = require('debug')('box:services'), dig = require('./dig.js'), docker = require('./docker.js'), @@ -893,11 +894,13 @@ async function setupTurn(app, options) { const turnSecret = await blobs.getString(blobs.ADDON_TURN_SECRET); if (!turnSecret) throw new BoxError(BoxError.ADDONS_ERROR, 'Turn secret is missing'); + const { fqdn:dashboardFqdn } = await dashboard.getLocation(); + const env = [ - { name: 'CLOUDRON_STUN_SERVER', value: settings.dashboardFqdn() }, + { name: 'CLOUDRON_STUN_SERVER', value: dashboardFqdn }, { name: 'CLOUDRON_STUN_PORT', value: '3478' }, { name: 'CLOUDRON_STUN_TLS_PORT', value: '5349' }, - { name: 'CLOUDRON_TURN_SERVER', value: settings.dashboardFqdn() }, + { name: 'CLOUDRON_TURN_SERVER', value: dashboardFqdn }, { name: 'CLOUDRON_TURN_PORT', value: '3478' }, { name: 'CLOUDRON_TURN_TLS_PORT', value: '5349' }, { name: 'CLOUDRON_TURN_SECRET', value: turnSecret } @@ -915,7 +918,7 @@ async function startTurn(existingInfra) { const image = infra.images.turn; const memoryLimit = serviceConfig.memoryLimit || SERVICES['turn'].defaultMemoryLimit; const memory = await system.getMemoryAllocation(memoryLimit); - const realm = settings.dashboardFqdn(); + const { fqdn:realm } = await dashboard.getLocation(); let turnSecret = await blobs.getString(blobs.ADDON_TURN_SECRET); if (!turnSecret) { @@ -1995,14 +1998,16 @@ async function getDynamicEnvironmentOidc(app, options) { const tmp = {}; + const { fqdn:dashboardFqdn } = await dashboard.getLocation(); + if (app.sso && app.manifest.addons['oidc']) { - tmp['CLOUDRON_OIDC_DISCOVERY_URL'] = `https://${settings.dashboardFqdn()}/openid/.well-known/openid-configuration`; - tmp['CLOUDRON_OIDC_ISSUER'] = `https://${settings.dashboardFqdn()}/openid`; - tmp['CLOUDRON_OIDC_AUTH_ENDPOINT'] = `https://${settings.dashboardFqdn()}/openid/auth`; - tmp['CLOUDRON_OIDC_TOKEN_ENDPOINT'] = `https://${settings.dashboardFqdn()}/openid/token`; - tmp['CLOUDRON_OIDC_KEYS_ENDPOINT'] = `https://${settings.dashboardFqdn()}/openid/jwks`; - tmp['CLOUDRON_OIDC_PROFILE_ENDPOINT'] = `https://${settings.dashboardFqdn()}/openid/me`; - tmp['CLOUDRON_OIDC_LOGOUT_URL'] = `https://${settings.dashboardFqdn()}/openid/session/env`; + tmp['CLOUDRON_OIDC_DISCOVERY_URL'] = `https://${dashboardFqdn}/openid/.well-known/openid-configuration`; + tmp['CLOUDRON_OIDC_ISSUER'] = `https://${dashboardFqdn}/openid`; + tmp['CLOUDRON_OIDC_AUTH_ENDPOINT'] = `https://${dashboardFqdn}/openid/auth`; + tmp['CLOUDRON_OIDC_TOKEN_ENDPOINT'] = `https://${dashboardFqdn}/openid/token`; + tmp['CLOUDRON_OIDC_KEYS_ENDPOINT'] = `https://${dashboardFqdn}/openid/jwks`; + tmp['CLOUDRON_OIDC_PROFILE_ENDPOINT'] = `https://${dashboardFqdn}/openid/me`; + tmp['CLOUDRON_OIDC_LOGOUT_URL'] = `https://${dashboardFqdn}/openid/session/env`; const client = await oidc.clients.get(app.id); tmp['CLOUDRON_OIDC_CLIENT_ID'] = client.id; diff --git a/src/settings.js b/src/settings.js index 245df4624..b95cdf38b 100644 --- a/src/settings.js +++ b/src/settings.js @@ -1,13 +1,6 @@ 'use strict'; exports = module.exports = { - initCache, - - // these values come from the cache - dashboardDomain, - setDashboardLocation, - dashboardFqdn, - get, set, @@ -57,22 +50,11 @@ exports = module.exports = { const assert = require('assert'), database = require('./database.js'), - debug = require('debug')('box:settings'), safe = require('safetydance'); const SETTINGS_FIELDS = [ 'name', 'value' ].join(','); const SETTINGS_BLOB_FIELDS = [ 'name', 'valueBlob' ].join(','); -const gDefaults = (function () { - const result = { }; - result[exports.DASHBOARD_DOMAIN_KEY] = ''; - result[exports.DASHBOARD_FQDN_KEY] = ''; - - return result; -})(); - -let gCache = {}; - async function get(key) { assert.strictEqual(typeof key, 'string'); @@ -121,41 +103,3 @@ async function setBlob(key, value) { async function clear() { await database.query('DELETE FROM settings'); } - -async function list() { - const settings = await database.query(`SELECT ${SETTINGS_FIELDS} FROM settings WHERE value IS NOT NULL ORDER BY name`); - - const result = Object.assign({}, gDefaults); - settings.forEach(function (setting) { result[setting.name] = setting.value; }); - - // convert booleans - result[exports.DEMO_KEY] = !!result[exports.DEMO_KEY]; - - return result; -} - -async function initCache() { - debug('initCache: pre-load settings'); - - const allSettings = await list(); - - gCache = { - dashboardDomain: allSettings[exports.DASHBOARD_DOMAIN_KEY], - dashboardFqdn: allSettings[exports.DASHBOARD_FQDN_KEY], - }; -} - -// this is together so we can do this in a transaction later -async function setDashboardLocation(dashboardDomain, dashboardFqdn) { - assert.strictEqual(typeof dashboardDomain, 'string'); - assert.strictEqual(typeof dashboardFqdn, 'string'); - - await set(exports.DASHBOARD_DOMAIN_KEY, dashboardDomain); - await set(exports.DASHBOARD_FQDN_KEY, dashboardFqdn); - - gCache.dashboardDomain = dashboardDomain; - gCache.dashboardFqdn = dashboardFqdn; -} - -function dashboardDomain() { return gCache.dashboardDomain; } -function dashboardFqdn() { return gCache.dashboardFqdn; } diff --git a/src/taskworker.js b/src/taskworker.js index 9351c76f9..27e1cc720 100755 --- a/src/taskworker.js +++ b/src/taskworker.js @@ -72,7 +72,6 @@ const startTime = new Date(); async.series([ setupLogging, database.initialize, - settings.initCache ], async function (initError) { if (initError) { console.error(initError); diff --git a/src/test/common.js b/src/test/common.js index d3ffe4e09..016f57c86 100644 --- a/src/test/common.js +++ b/src/test/common.js @@ -3,6 +3,7 @@ const apps = require('../apps.js'), appstore = require('../appstore.js'), constants = require('../constants.js'), + dashboard = require('../dashboard.js'), database = require('../database.js'), domains = require('../domains.js'), expect = require('expect.js'), @@ -214,8 +215,7 @@ async function databaseSetup() { await database.initialize(); await database._clear(); await appstore._setApiServerOrigin(exports.mockApiServerOrigin); - await settings.setDashboardLocation(exports.dashboardDomain, exports.dashboardFqdn); - await settings.initCache(); + await dashboard.setLocation(exports.dashboardDomain, exports.dashboardFqdn); } async function domainSetup() { diff --git a/src/users.js b/src/users.js index bcef0f019..36daf14e2 100644 --- a/src/users.js +++ b/src/users.js @@ -78,6 +78,7 @@ const appPasswords = require('./apppasswords.js'), BoxError = require('./boxerror.js'), crypto = require('crypto'), constants = require('./constants.js'), + dashboard = require('./dashboard.js'), database = require('./database.js'), debug = require('debug')('box:user'), eventlog = require('./eventlog.js'), @@ -667,7 +668,8 @@ async function getPasswordResetLink(user, auditSource) { await update(user, { resetToken, resetTokenCreationTime }, auditSource); } - const resetLink = `https://${settings.dashboardFqdn()}/passwordreset.html?resetToken=${resetToken}`; + const { fqdn:dashboardFqdn } = await dashboard.getLocation(); + const resetLink = `https://${dashboardFqdn}/passwordreset.html?resetToken=${resetToken}`; return resetLink; } @@ -796,7 +798,8 @@ async function getInviteLink(user, auditSource) { if (!user.inviteToken) throw new BoxError(BoxError.BAD_STATE, 'User already used invite link'); const directoryConfig = await getProfileConfig(); - let inviteLink = `https://${settings.dashboardFqdn()}/setupaccount.html?inviteToken=${user.inviteToken}&email=${encodeURIComponent(user.email)}`; + const { fqdn:dashboardFqdn } = await dashboard.getLocation(); + let inviteLink = `https://${dashboardFqdn}/setupaccount.html?inviteToken=${user.inviteToken}&email=${encodeURIComponent(user.email)}`; if (user.username) inviteLink += `&username=${encodeURIComponent(user.username)}`; if (user.displayName) inviteLink += `&displayName=${encodeURIComponent(user.displayName)}`; @@ -853,7 +856,8 @@ async function setTwoFactorAuthenticationSecret(userId, auditSource) { if (user.twoFactorAuthenticationEnabled) throw new BoxError(BoxError.ALREADY_EXISTS); - const secret = speakeasy.generateSecret({ name: `Cloudron ${settings.dashboardFqdn()} (${user.username})` }); + const { fqdn:dashboardFqdn } = await dashboard.getLocation(); + const secret = speakeasy.generateSecret({ name: `Cloudron ${dashboardFqdn} (${user.username})` }); await update(user, { twoFactorAuthenticationSecret: secret.base32, twoFactorAuthenticationEnabled: false }, auditSource); @@ -909,13 +913,14 @@ function compareRoles(role1, role2) { async function getAvatarUrl(user) { assert.strictEqual(typeof user, 'object'); - const fallbackUrl = `https://${settings.dashboardFqdn()}/img/avatar-default-symbolic.svg`; + const { fqdn:dashboardFqdn } = await dashboard.getLocation(); + const fallbackUrl = `https://${dashboardFqdn}/img/avatar-default-symbolic.svg`; const result = await getAvatar(user.id); if (result.equals(constants.AVATAR_NONE)) return fallbackUrl; else if (result.equals(constants.AVATAR_GRAVATAR)) return `https://www.gravatar.com/avatar/${require('crypto').createHash('md5').update(user.email).digest('hex')}.jpg`; - else if (result) return `https://${settings.dashboardFqdn()}/api/v1/profile/avatar/${user.id}`; + else if (result) return `https://${dashboardFqdn}/api/v1/profile/avatar/${user.id}`; else return fallbackUrl; } diff --git a/src/wellknown.js b/src/wellknown.js index a865bb8f1..78fd4e9db 100644 --- a/src/wellknown.js +++ b/src/wellknown.js @@ -6,13 +6,13 @@ exports = module.exports = { const assert = require('assert'), BoxError = require('./boxerror.js'), + dashboard = require('./dashboard.js'), domains = require('./domains.js'), ejs = require('ejs'), fs = require('fs'), mail = require('./mail.js'), mailServer = require('./mailserver.js'), safe = require('safetydance'), - settings = require('./settings.js'), superagent = require('superagent'); const MAIL_AUTOCONFIG_EJS = fs.readFileSync(__dirname + '/autoconfig.xml.ejs', { encoding: 'utf8' }); @@ -38,10 +38,12 @@ async function get(domain, location) { if (!domainObject.wellKnown || !(location in domainObject.wellKnown)) throw new BoxError(BoxError.NOT_FOUND, 'No custom well-known config'); return { type, body: domainObject.wellKnown[location] }; - } else if (location === 'openid-configuration' && domain === settings.dashboardFqdn()) { + } else if (location === 'openid-configuration') { + const { fqdn:dashboardFqdn } = await dashboard.getLocation(); + if (domain !== dashboardFqdn) throw new BoxError(BoxError.NOT_FOUND, 'No custom well-known config'); // the oidc-provider module does not expose this in javascript but only via a route handler // we have to use the external route even - const [error, result] = await safe(superagent.get(`https://${settings.dashboardFqdn()}/openid/.well-known/openid-configuration`)); + const [error, result] = await safe(superagent.get(`https://${dashboardFqdn}/openid/.well-known/openid-configuration`)); if (error) return new BoxError(BoxError.INTERNAL_ERROR, 'unable to connect to internal OpenID routes'); return { type: 'application/json', body: result.body };