diff --git a/packages/auth/src/db/utils.js b/packages/auth/src/db/utils.js index 57772a8281..602520ef1a 100644 --- a/packages/auth/src/db/utils.js +++ b/packages/auth/src/db/utils.js @@ -26,19 +26,24 @@ exports.StaticDatabases = { }, } +const PRE_APP = "app" +const PRE_DEV = "dev" + const DocumentTypes = { USER: "us", WORKSPACE: "workspace", CONFIG: "config", TEMPLATE: "template", - APP: "app", - APP_DEV: "app_dev", - APP_METADATA: "app_metadata", + APP: PRE_APP, + DEV: PRE_DEV, + APP_DEV: `${PRE_APP}${SEPARATOR}${PRE_DEV}`, + APP_METADATA: `${PRE_APP}${SEPARATOR}metadata`, ROLE: "role", } exports.DocumentTypes = DocumentTypes exports.APP_PREFIX = DocumentTypes.APP + SEPARATOR +exports.APP_DEV = exports.APP_DEV_PREFIX = DocumentTypes.APP_DEV + SEPARATOR exports.SEPARATOR = SEPARATOR @@ -85,6 +90,9 @@ exports.getGlobalDB = tenantId => { * Given a koa context this tries to extra what tenant is being accessed. */ exports.getTenantIdFromCtx = ctx => { + if (!ctx) { + return null + } const user = ctx.user || {} const params = ctx.request.params || {} const query = ctx.request.query || {} @@ -208,9 +216,18 @@ exports.getAllApps = async ({ tenantId, dev, all } = {}) => { } const CouchDB = getCouch() let allDbs = await CouchDB.allDbs() - const appDbNames = allDbs.filter(dbName => - dbName.startsWith(exports.APP_PREFIX) - ) + const appDbNames = allDbs.filter(dbName => { + const split = dbName.split(SEPARATOR) + // it is an app, check the tenantId + if (split[0] === DocumentTypes.APP) { + const noTenantId = split.length === 2 || split[1] === DocumentTypes.DEV + // tenantId is always right before the UUID + const possibleTenantId = split[split.length - 2] + return (tenantId === DEFAULT_TENANT_ID && noTenantId) || + (possibleTenantId === tenantId) + } + return false + }) const appPromises = appDbNames.map(db => // skip setup otherwise databases could be re-created new CouchDB(db, { skip_setup: true }).get(DocumentTypes.APP_METADATA) @@ -222,10 +239,6 @@ exports.getAllApps = async ({ tenantId, dev, all } = {}) => { const apps = response .filter(result => result.status === "fulfilled") .map(({ value }) => value) - .filter(app => { - const appTenant = !app.tenantId ? DEFAULT_TENANT_ID : app.tenantId - return tenantId === appTenant - }) if (!all) { return apps.filter(app => { if (dev) { diff --git a/packages/server/scripts/dev/manage.js b/packages/server/scripts/dev/manage.js index ffd8c6b9e3..96c2674317 100644 --- a/packages/server/scripts/dev/manage.js +++ b/packages/server/scripts/dev/manage.js @@ -47,6 +47,7 @@ async function init() { COUCH_DB_PASSWORD: "budibase", COUCH_DB_USER: "budibase", SELF_HOSTED: 1, + MULTI_TENANCY: 0, } let envFile = "" Object.keys(envFileJson).forEach(key => { diff --git a/packages/server/src/api/controllers/application.js b/packages/server/src/api/controllers/application.js index e91ac08d65..4e13851cfe 100644 --- a/packages/server/src/api/controllers/application.js +++ b/packages/server/src/api/controllers/application.js @@ -92,8 +92,8 @@ async function getAppUrlIfNotInUse(ctx) { return url } -async function createInstance(template) { - const baseAppId = generateAppID() +async function createInstance(tenantId, template) { + const baseAppId = generateAppID(env.MULTI_TENANCY ? tenantId : null) const appId = generateDevAppID(baseAppId) const db = new CouchDB(appId) @@ -198,7 +198,7 @@ exports.create = async function (ctx) { if (ctx.request.files && ctx.request.files.templateFile) { instanceConfig.file = ctx.request.files.templateFile } - const instance = await createInstance(instanceConfig) + const instance = await createInstance(tenantId, instanceConfig) const appId = instance._id const url = await getAppUrlIfNotInUse(ctx) diff --git a/packages/server/src/db/utils.js b/packages/server/src/db/utils.js index eb8c32bb5d..92734c5e7b 100644 --- a/packages/server/src/db/utils.js +++ b/packages/server/src/db/utils.js @@ -225,8 +225,12 @@ exports.getLinkParams = (otherProps = {}) => { * Generates a new app ID. * @returns {string} The new app ID which the app doc can be stored under. */ -exports.generateAppID = () => { - return `${DocumentTypes.APP}${SEPARATOR}${newid()}` +exports.generateAppID = (tenantId = null) => { + let id = `${DocumentTypes.APP}${SEPARATOR}` + if (tenantId) { + id += `${tenantId}${SEPARATOR}` + } + return `${id}${newid()}` } /** @@ -235,8 +239,8 @@ exports.generateAppID = () => { */ exports.generateDevAppID = appId => { const prefix = `${DocumentTypes.APP}${SEPARATOR}` - const uuid = appId.split(prefix)[1] - return `${DocumentTypes.APP_DEV}${SEPARATOR}${uuid}` + const rest = appId.split(prefix)[1] + return `${DocumentTypes.APP_DEV}${SEPARATOR}${rest}` } /** diff --git a/packages/server/src/environment.js b/packages/server/src/environment.js index 52c680f65a..9f69664ffb 100644 --- a/packages/server/src/environment.js +++ b/packages/server/src/environment.js @@ -35,6 +35,7 @@ module.exports = { REDIS_URL: process.env.REDIS_URL, REDIS_PASSWORD: process.env.REDIS_PASSWORD, INTERNAL_API_KEY: process.env.INTERNAL_API_KEY, + MULTI_TENANCY: process.env.MULTI_TENANCY, // environment NODE_ENV: process.env.NODE_ENV, JEST_WORKER_ID: process.env.JEST_WORKER_ID, diff --git a/packages/worker/scripts/dev/manage.js b/packages/worker/scripts/dev/manage.js index a40391b396..dbc2f424d4 100644 --- a/packages/worker/scripts/dev/manage.js +++ b/packages/worker/scripts/dev/manage.js @@ -16,7 +16,7 @@ async function init() { REDIS_PASSWORD: "budibase", MINIO_URL: "http://localhost:10000/", COUCH_DB_URL: "http://budibase:budibase@localhost:10000/db/", - MULTI_TENANCY: false, + MULTI_TENANCY: 0, } let envFile = "" Object.keys(envFileJson).forEach(key => { diff --git a/packages/worker/src/api/controllers/global/users.js b/packages/worker/src/api/controllers/global/users.js index 9ec2922543..cb0be9e2ad 100644 --- a/packages/worker/src/api/controllers/global/users.js +++ b/packages/worker/src/api/controllers/global/users.js @@ -211,7 +211,7 @@ exports.destroy = async ctx => { exports.removeAppRole = async ctx => { const { appId } = ctx.params const db = getGlobalDBFromCtx(ctx) - const users = await allUsers() + const users = await allUsers(ctx) const bulk = [] const cacheInvalidations = [] for (let user of users) {