diff --git a/packages/auth/deprovision.js b/packages/auth/deprovision.js new file mode 100644 index 0000000000..b4b8dc6110 --- /dev/null +++ b/packages/auth/deprovision.js @@ -0,0 +1 @@ +module.exports = require("./src/tenancy/deprovision") diff --git a/packages/auth/src/tenancy/deprovision.js b/packages/auth/src/tenancy/deprovision.js new file mode 100644 index 0000000000..b8e5bc82cf --- /dev/null +++ b/packages/auth/src/tenancy/deprovision.js @@ -0,0 +1,81 @@ +const { getGlobalUserParams, getAllApps } = require("../db/utils") +const { getDB, getCouch } = require("../db") +const { getGlobalDB } = require("./tenancy") +const { StaticDatabases } = require("../db/constants") + +const TENANT_DOC = StaticDatabases.PLATFORM_INFO.docs.tenants +const PLATFORM_INFO_DB = StaticDatabases.PLATFORM_INFO.name + +const removeTenantFromInfoDB = async tenantId => { + try { + const infoDb = getDB(PLATFORM_INFO_DB) + let tenants = await infoDb.get(TENANT_DOC) + tenants.tenantIds = tenants.tenantIds.filter(id => id !== tenantId) + + await infoDb.put(tenants) + } catch (err) { + console.error(`Error removing tenant ${tenantId} from info db`, err) + throw err + } +} + +const removeUsersFromInfoDB = async tenantId => { + try { + const globalDb = getGlobalDB(tenantId) + const infoDb = getDB(PLATFORM_INFO_DB) + const allUsers = await globalDb.allDocs( + getGlobalUserParams(null, { + include_docs: true, + }) + ) + const allEmails = allUsers.rows.map(row => row.doc.email) + // get the id docs + let keys = allUsers.rows.map(row => row.id) + // and the email docs + keys = keys.concat(allEmails) + // retrieve the docs and delete them + const userDocs = await infoDb.allDocs({ + keys, + include_docs: true, + }) + const toDelete = userDocs.rows.map(row => { + return { + ...row.doc, + _deleted: true, + } + }) + await infoDb.bulkDocs(toDelete) + } catch (err) { + console.error(`Error removing tenant ${tenantId} users from info db`, err) + throw err + } +} + +const removeGlobalDB = async tenantId => { + try { + const globalDb = getGlobalDB(tenantId) + await globalDb.destroy() + } catch (err) { + console.error(`Error removing tenant ${tenantId} users from info db`, err) + throw err + } +} + +const removeTenantApps = async tenantId => { + try { + const apps = await getAllApps(getCouch(), { all: true }) + const destroyPromises = apps.map(app => getDB(app.appId).destroy()) + await Promise.allSettled(destroyPromises) + } catch (err) { + console.error(`Error removing tenant ${tenantId} apps`, err) + throw err + } +} + +// can't live in tenancy package due to circular dependency on db/utils +exports.deleteTenant = async tenantId => { + await removeTenantFromInfoDB(tenantId) + await removeUsersFromInfoDB(tenantId) + await removeGlobalDB(tenantId) + await removeTenantApps(tenantId) +} diff --git a/packages/auth/src/tenancy/tenancy.js b/packages/auth/src/tenancy/tenancy.js index 6010274442..ebd573496c 100644 --- a/packages/auth/src/tenancy/tenancy.js +++ b/packages/auth/src/tenancy/tenancy.js @@ -73,92 +73,6 @@ exports.tryAddTenant = async (tenantId, userId, email) => { await Promise.all(promises) } -const DocumentTypes = { - USER: "us", -} -const UNICODE_MAX = "\ufff0" - -/** - * Gets parameters for retrieving users. - * Duplicate of "../db/utils" due to circular dependency - */ -const getGlobalUserParams = (globalId, otherProps = {}) => { - if (!globalId) { - globalId = "" - } - return { - ...otherProps, - startkey: `${DocumentTypes.USER}${SEPARATOR}${globalId}`, - endkey: `${DocumentTypes.USER}${SEPARATOR}${globalId}${UNICODE_MAX}`, - } -} - -const removeTenantFromInfoDB = async tenantId => { - try { - const infoDb = getDB(PLATFORM_INFO_DB) - let tenants = await infoDb.get(TENANT_DOC) - tenants.tenantIds = tenants.tenantIds.filter(id => id !== tenantId) - - await infoDb.put(tenants) - } catch (err) { - console.error(`Error removing tenant ${tenantId} from info db`, err) - throw err - } -} - -const removeUsersFromInfoDB = async tenantId => { - try { - const globalDb = exports.getGlobalDB(tenantId) - const infoDb = getDB(PLATFORM_INFO_DB) - const allUsers = await globalDb.allDocs( - getGlobalUserParams(null, { - include_docs: true, - }) - ) - const allEmails = allUsers.rows.map(row => row.doc.email) - // get the id docs - let keys = allUsers.rows.map(row => row.id) - // and the email docs - keys = keys.concat(allEmails) - // retrieve the docs and delete them - const userDocs = await infoDb.allDocs({ - keys, - include_docs: true, - }) - const toDelete = userDocs.rows.map(row => { - return { - ...row.doc, - _deleted: true, - } - }) - await infoDb.bulkDocs(toDelete) - } catch (err) { - console.error(`Error removing tenant ${tenantId} users from info db`, err) - throw err - } -} - -const removeGlobalDB = async tenantId => { - try { - const globalDb = exports.getGlobalDB(tenantId) - await globalDb.destroy() - } catch (err) { - console.error(`Error removing tenant ${tenantId} users from info db`, err) - throw err - } -} - -const removeTenantApps = async () => { - // TODO -} - -exports.deleteTenant = async tenantId => { - await removeTenantFromInfoDB(tenantId) - await removeUsersFromInfoDB(tenantId) - await removeGlobalDB(tenantId) - await removeTenantApps(tenantId) -} - exports.getGlobalDB = (tenantId = null) => { // tenant ID can be set externally, for example user API where // new tenants are being created, this may be the case diff --git a/packages/worker/src/api/controllers/system/tenants.js b/packages/worker/src/api/controllers/system/tenants.js index 22a6967619..a96c5e5f9f 100644 --- a/packages/worker/src/api/controllers/system/tenants.js +++ b/packages/worker/src/api/controllers/system/tenants.js @@ -1,6 +1,7 @@ const CouchDB = require("../../../db") const { StaticDatabases } = require("@budibase/auth/db") -const { deleteTenant, getTenantId } = require("@budibase/auth/tenancy") +const { getTenantId } = require("@budibase/auth/tenancy") +const { deleteTenant } = require("@budibase/auth/deprovision") exports.exists = async ctx => { const tenantId = ctx.request.params