diff --git a/src/apps.js b/src/apps.js index d6f194632..96873866c 100644 --- a/src/apps.js +++ b/src/apps.js @@ -1418,17 +1418,6 @@ async function install(data, auditSource) { error = validateEnv(env); if (error) throw error; - let reverseProxyConfig = null; - if ('reverseProxyConfig' in data) { - reverseProxyConfig = Object.assign({ robotsTxt: null, csp: null, hstsPreload: false }, data.reverseProxyConfig); // ensure fields - - let error = validateCsp(reverseProxyConfig.csp); - if (error) throw error; - - error = validateRobotsTxt(reverseProxyConfig.robotsTxt); - if (error) throw error; - } - if (constants.DEMO && constants.DEMO_BLOCKED_APPS.includes(appStoreId)) throw new BoxError(BoxError.BAD_FIELD, 'This app is blocked in the demo'); // sendmail is enabled by default @@ -1452,18 +1441,6 @@ async function install(data, auditSource) { if (constants.DEMO && (await getCount() >= constants.DEMO_APP_LIMIT)) throw new BoxError(BoxError.BAD_STATE, 'Too many installed apps, please uninstall a few and try again'); - let restoreConfig = null; - - if ('archive' in data) { // install from archive. assume these are already validated - assert(data.archive.iconBuffer === null || Buffer.isBuffer(data.archive.iconBuffer)); - assert.strictEqual(typeof data.archive.backupId, 'string'); - - icon = data.archive.iconBuffer; // install from archive - const backup = await backups.get(data.archive.backupId); - if (!backup) throw new BoxError(BoxError.BAD_FIELD, 'Backup not found in archive'); - restoreConfig = { remotePath: backup.remotePath, backupFormat: backup.format }; - } - const appId = uuid.v4(); debug(`Installing app ${appId}`); @@ -1492,7 +1469,6 @@ async function install(data, auditSource) { enableRedis, notes, crontab, - reverseProxyConfig, runState: exports.RSTATE_RUNNING, installationState: exports.ISTATE_PENDING_INSTALL }; @@ -1504,7 +1480,7 @@ async function install(data, auditSource) { await purchaseApp({ appId, appstoreId: appStoreId, manifestId: manifest.id || 'customapp' }); const task = { - args: { restoreConfig, skipDnsSetup, overwriteDns }, + args: { restoreConfig: null, skipDnsSetup, overwriteDns }, values: { }, requiredState: app.installationState }; @@ -2513,31 +2489,75 @@ async function unarchive(archive, data, auditSource) { assert.strictEqual(typeof data, 'object'); assert(auditSource && typeof auditSource === 'object'); - const appConfig = archive.appConfig; + const backup = await backups.get(archive.backupId); + const restoreConfig = { remotePath: backup.remotePath, backupFormat: backup.format }; - const dolly = _.pick(appConfig, 'appStoreId', 'manifest', 'memoryLimit', 'cpuQuota', 'crontab', 'reverseProxyConfig', 'env', 'servicesConfig', + const subdomain = data.subdomain.toLowerCase(), + domain = data.domain.toLowerCase(), + overwriteDns = 'overwriteDns' in data ? data.overwriteDns : false; + + const appConfig = archive.appConfig; + const { appStoreId, manifest } = appConfig; + + let error = validateSecondaryDomains(data.secondaryDomains || {}, manifest); + if (error) throw error; + const secondaryDomains = translateSecondaryDomains(data.secondaryDomains || {}); + + const locations = [new Location(subdomain, domain, Location.TYPE_PRIMARY)] + .concat(secondaryDomains.map(sd => new Location(sd.subdomain, sd.domain, Location.TYPE_SECONDARY))); + + error = await validateLocations(locations); + if (error) throw error; + + // re-validate because this new box version may not accept old configs + error = await checkManifest(manifest); + if (error) throw error; + + error = validatePorts(data.ports || null, manifest); + if (error) throw error; + const portBindings = translateToPortBindings(data.ports || null, manifest); + + const appId = uuid.v4(); + + const dolly = _.pick(appConfig, 'memoryLimit', 'cpuQuota', 'crontab', 'reverseProxyConfig', 'env', 'servicesConfig', 'tags', 'label', 'enableMailbox', 'mailboxDisplayName', 'mailboxName', 'mailboxDomain', 'enableInbox', 'inboxName', 'inboxDomain', 'devices', 'enableTurn', 'enableRedis', 'mounts', 'enableBackup', 'enableAutomaticUpdate', 'accessRestriction', 'operators', 'sso', 'notes', 'checklist'); // intentionally not filled up: redirectDomain, aliasDomains, mailboxDomain - const newAppData = Object.assign(dolly, { - // from request - subdomain: data.subdomain, - domain: data.domain, - secondaryDomains: data.secondaryDomains || {}, - ports: data.ports || null, - overwriteDns: 'overwriteDns' in data ? data.overwriteDns : false, + const obj = Object.assign(dolly, { + secondaryDomains, + redirectDomains: [], + aliasDomains: [], mailboxDomain: data.domain, // archive's mailboxDomain may not exist - - // from the archive - archive: { - iconBuffer: (await archives.getIcons(archive.id))?.icon, - backupId: archive.backupId - } + runState: exports.RSTATE_RUNNING, + installationState: exports.ISTATE_PENDING_INSTALL }); + obj.icon = (await archives.getIcons(archive.id))?.icon; - return await install(newAppData, auditSource); + const [addError] = await safe(add(appId, appStoreId, manifest, subdomain, domain, portBindings, obj)); + if (addError && addError.reason === BoxError.ALREADY_EXISTS) throw getDuplicateErrorDetails(addError.message, locations, portBindings); + if (addError) throw addError; + + await purchaseApp({ appId, appstoreId: appStoreId, manifestId: manifest.id || 'customapp' }); + + const task = { + args: { restoreConfig, overwriteDns }, + values: {}, + requiredState: obj.installationState + }; + + const taskId = await addTask(appId, obj.installationState, task, auditSource); + + const newApp = Object.assign({}, _.omit(obj, 'icon'), { appStoreId, manifest, subdomain, domain, portBindings }); + newApp.fqdn = dns.fqdn(newApp.subdomain, newApp.domain); + newApp.secondaryDomains.forEach(function (ad) { ad.fqdn = dns.fqdn(ad.subdomain, ad.domain); }); + newApp.redirectDomains.forEach(function (ad) { ad.fqdn = dns.fqdn(ad.subdomain, ad.domain); }); + newApp.aliasDomains.forEach(function (ad) { ad.fqdn = dns.fqdn(ad.subdomain, ad.domain); }); + + await eventlog.add(eventlog.ACTION_APP_INSTALL, auditSource, { appId, app: newApp, taskId }); + + return { id : appId, taskId }; } async function uninstall(app, auditSource) { diff --git a/src/routes/apps.js b/src/routes/apps.js index 77c4ba2d6..a7a0f38e3 100644 --- a/src/routes/apps.js +++ b/src/routes/apps.js @@ -198,8 +198,6 @@ async function install(req, res, next) { data.appStoreId = result.appStoreId; data.manifest = result.manifest; - delete data.archive; // internally used for archive code path - [error, result] = await safe(apps.install(data, AuditSource.fromRequest(req))); if (error) return next(BoxError.toHttpError(error));