diff --git a/src/apps.js b/src/apps.js index 6b1e6bbef..7aff191bc 100644 --- a/src/apps.js +++ b/src/apps.js @@ -800,6 +800,7 @@ async function add(id, appStoreId, manifest, subdomain, domain, portBindings, da reverseProxyConfigJson = data.reverseProxyConfig ? JSON.stringify(data.reverseProxyConfig) : null, servicesConfigJson = data.servicesConfig ? JSON.stringify(data.servicesConfig) : null, enableMailbox = data.enableMailbox || false, + upstreamUri = data.upstreamUri || '', icon = data.icon || null; const queries = []; @@ -807,11 +808,11 @@ async function add(id, appStoreId, manifest, subdomain, domain, portBindings, da queries.push({ query: 'INSERT INTO apps (id, appStoreId, manifestJson, installationState, runState, accessRestrictionJson, memoryLimit, cpuShares, ' + 'sso, debugModeJson, mailboxName, mailboxDomain, label, tagsJson, reverseProxyConfigJson, servicesConfigJson, icon, ' - + 'enableMailbox, mailboxDisplayName) ' - + ' VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', + + 'enableMailbox, mailboxDisplayName, upstreamUri) ' + + ' VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', args: [ id, appStoreId, manifestJson, installationState, runState, accessRestrictionJson, memoryLimit, cpuShares, sso, debugModeJson, mailboxName, mailboxDomain, label, tagsJson, reverseProxyConfigJson, servicesConfigJson, icon, - enableMailbox, mailboxDisplayName ] + enableMailbox, mailboxDisplayName, upstreamUri ] }); queries.push({ @@ -1292,6 +1293,7 @@ async function install(data, auditSource) { overwriteDns = 'overwriteDns' in data ? data.overwriteDns : false, skipDnsSetup = 'skipDnsSetup' in data ? data.skipDnsSetup : false, appStoreId = data.appStoreId, + upstreamUri = data.upstreamUri, manifest = data.manifest; let error = manifestFormat.parse(manifest); @@ -1315,6 +1317,9 @@ async function install(data, auditSource) { error = validateLabel(label); if (error) throw error; + error = validateUpstreamUri(upstreamUri); + if (error) throw error; + error = validateTags(tags); if (error) throw error; @@ -1372,6 +1377,7 @@ async function install(data, auditSource) { tags, icon, enableMailbox, + upstreamUri, runState: exports.RSTATE_RUNNING, installationState: exports.ISTATE_PENDING_INSTALL }; diff --git a/src/reverseproxy.js b/src/reverseproxy.js index 4ecebbc9e..9e68bf939 100644 --- a/src/reverseproxy.js +++ b/src/reverseproxy.js @@ -510,6 +510,10 @@ async function writeAppNginxConfig(app, fqdn, type, bundle) { if (app.manifest.id === constants.PROXY_APP_APPSTORE_ID) { data.endpoint = 'external'; + + // prevent generating invalid nginx configs + if (!app.upstreamUri) throw new BoxError(BoxError.BAD_FIELD, 'upstreamUri cannot be empty'); + data.upstreamUri = app.upstreamUri; } diff --git a/src/routes/apps.js b/src/routes/apps.js index 7887b486c..30d2d7762 100644 --- a/src/routes/apps.js +++ b/src/routes/apps.js @@ -66,6 +66,7 @@ const apps = require('../apps.js'), assert = require('assert'), AuditSource = require('../auditsource.js'), BoxError = require('../boxerror.js'), + constants = require('../constants.js'), debug = require('debug')('box:routes/apps'), HttpError = require('connect-lastmile').HttpError, HttpSuccess = require('connect-lastmile').HttpSuccess, @@ -172,9 +173,13 @@ async function install(req, res, next) { if ('skipDnsSetup' in data && typeof data.skipDnsSetup !== 'boolean') return next(new HttpError(400, 'skipDnsSetup must be boolean')); if ('enableMailbox' in data && typeof data.enableMailbox !== 'boolean') return next(new HttpError(400, 'enableMailbox must be boolean')); + if ('upstreamUri' in data && (typeof data.upstreamUri !== 'string' || !data.upstreamUri)) return next(new HttpError(400, 'upstreamUri must be a non emptry string')); + let [error, result] = await safe(apps.downloadManifest(data.appStoreId, data.manifest)); if (error) return next(BoxError.toHttpError(error)); + if (result.manifest.appStoreId === constants.PROXY_APP_APPSTORE_ID && (typeof data.upstreamUri !== 'string' || !data.upstreamUri)) return next(new HttpError(400, 'upstreamUri must be a non empty string')); + if (safe.query(result.manifest, 'addons.docker') && req.user.role !== users.ROLE_OWNER) return next(new HttpError(403, '"owner" role is required to install app with docker addon')); data.appStoreId = result.appStoreId;