From 7a26f3769d54a14a202d8f36964844df9d7fb401 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 8 Oct 2021 18:21:40 +0100 Subject: [PATCH] Fixing some issues with cloud export/import, removing the ability to export and import your users as this was dangerous and didn't really work with passwords/SSO. --- .../admin/_components/ImportAppsModal.svelte | 5 ++- .../src/pages/builder/admin/index.svelte | 10 ++++- packages/builder/src/stores/portal/admin.js | 13 ++++++ .../server/src/api/controllers/application.js | 1 + packages/server/src/api/controllers/cloud.js | 41 ++++++++++++++----- packages/server/src/api/routes/cloud.js | 1 + .../worker/src/middleware/cloudRestricted.js | 2 +- 7 files changed, 58 insertions(+), 15 deletions(-) diff --git a/packages/builder/src/pages/builder/admin/_components/ImportAppsModal.svelte b/packages/builder/src/pages/builder/admin/_components/ImportAppsModal.svelte index 633147e910..de29e11301 100644 --- a/packages/builder/src/pages/builder/admin/_components/ImportAppsModal.svelte +++ b/packages/builder/src/pages/builder/admin/_components/ImportAppsModal.svelte @@ -1,6 +1,7 @@ @@ -73,7 +81,7 @@ > Change organisation - {:else if !cloud} + {:else if !cloud && !imported} { diff --git a/packages/builder/src/stores/portal/admin.js b/packages/builder/src/stores/portal/admin.js index ebe8294060..b9ed0f04b8 100644 --- a/packages/builder/src/stores/portal/admin.js +++ b/packages/builder/src/stores/portal/admin.js @@ -9,6 +9,7 @@ export function createAdminStore() { cloud: false, disableAccountPortal: false, accountPortalUrl: "", + importComplete: false, onboardingProgress: 0, checklist: { apps: { checked: false }, @@ -45,6 +46,17 @@ export function createAdminStore() { } } + async function checkImportComplete() { + const response = await api.get(`/api/cloud/import/complete`) + if (response.status === 200) { + const json = await response.json() + admin.update(store => { + store.importComplete = json ? json.imported : false + return store + }) + } + } + async function getEnvironment() { let multiTenancyEnabled = false let cloud = false @@ -79,6 +91,7 @@ export function createAdminStore() { return { subscribe: admin.subscribe, init, + checkImportComplete, unload, } } diff --git a/packages/server/src/api/controllers/application.js b/packages/server/src/api/controllers/application.js index 6608ba0cac..e3aac8bd63 100644 --- a/packages/server/src/api/controllers/application.js +++ b/packages/server/src/api/controllers/application.js @@ -86,6 +86,7 @@ async function getAppUrlIfNotInUse(ctx) { if ( url && deployedApps[url] != null && + ctx.params != null && deployedApps[url].appId !== ctx.params.appId ) { ctx.throw(400, "App name/URL is already in use.") diff --git a/packages/server/src/api/controllers/cloud.js b/packages/server/src/api/controllers/cloud.js index aac79bb9dd..a0057d68e4 100644 --- a/packages/server/src/api/controllers/cloud.js +++ b/packages/server/src/api/controllers/cloud.js @@ -28,15 +28,18 @@ exports.exportApps = async ctx => { ctx.throw(400, "Exporting only allowed in multi-tenant cloud environments.") } const apps = await getAllApps(CouchDB, { all: true }) - const globalDBString = await exportDB(getGlobalDBName()) + const globalDBString = await exportDB(getGlobalDBName(), { + filter: doc => !doc._id.startsWith(DocumentTypes.USER), + }) let allDBs = { global: globalDBString, } for (let app of apps) { + const appId = app.appId || app._id // only export the dev apps as they will be the latest, the user can republish the apps // in their self hosted environment - if (isDevAppID(app._id)) { - allDBs[app.name] = await exportDB(app._id) + if (isDevAppID(appId)) { + allDBs[app.name] = await exportDB(appId) } } const filename = `cloud-export-${new Date().getTime()}.txt` @@ -53,16 +56,26 @@ async function getAllDocType(db, docType) { return response.rows.map(row => row.doc) } +async function hasBeenImported() { + if (!env.SELF_HOSTED || env.MULTI_TENANCY) { + return true + } + const apps = await getAllApps(CouchDB, { all: true }) + return apps.length !== 0 +} + +exports.hasBeenImported = async ctx => { + ctx.body = { + imported: await hasBeenImported(), + } +} + exports.importApps = async ctx => { if (!env.SELF_HOSTED || env.MULTI_TENANCY) { ctx.throw(400, "Importing only allowed in self hosted environments.") } - const apps = await getAllApps(CouchDB, { all: true }) - if ( - apps.length !== 0 || - !ctx.request.files || - !ctx.request.files.importFile - ) { + const beenImported = await hasBeenImported() + if (beenImported || !ctx.request.files || !ctx.request.files.importFile) { ctx.throw( 400, "Import file is required and environment must be fresh to import apps." @@ -80,11 +93,17 @@ exports.importApps = async ctx => { for (let [appName, appImport] of Object.entries(dbs)) { await createApp(appName, appImport) } - // once apps are created clean up the global db + + // if there are any users make sure to remove them let users = await getAllDocType(globalDb, DocumentTypes.USER) + let userDeletionPromises = [] for (let user of users) { - delete user.tenantId + userDeletionPromises.push(globalDb.remove(user._id, user._rev)) } + if (userDeletionPromises.length > 0) { + await Promise.all(userDeletionPromises) + } + await globalDb.bulkDocs(users) ctx.body = { message: "Apps successfully imported.", diff --git a/packages/server/src/api/routes/cloud.js b/packages/server/src/api/routes/cloud.js index 214473f43f..02b501c399 100644 --- a/packages/server/src/api/routes/cloud.js +++ b/packages/server/src/api/routes/cloud.js @@ -9,5 +9,6 @@ router .get("/api/cloud/export", authorized(BUILDER), controller.exportApps) // has to be public, only run if apps don't exist .post("/api/cloud/import", controller.importApps) + .get("/api/cloud/import/complete", controller.hasBeenImported) module.exports = router diff --git a/packages/worker/src/middleware/cloudRestricted.js b/packages/worker/src/middleware/cloudRestricted.js index 10cdeaebd4..b29093f77e 100644 --- a/packages/worker/src/middleware/cloudRestricted.js +++ b/packages/worker/src/middleware/cloudRestricted.js @@ -6,7 +6,7 @@ const { Headers } = require("@budibase/auth").constants * Ensure that the correct API key has been supplied. */ module.exports = async (ctx, next) => { - if (!env.SELF_HOSTED) { + if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) { const apiKey = ctx.request.headers[Headers.API_KEY] if (apiKey !== env.INTERNAL_API_KEY) { ctx.throw(403, "Unauthorized")