Link app context with tenancy, remove app tenancy middleware

This commit is contained in:
Rory Powell 2022-03-24 13:04:49 +00:00
parent 5851525c88
commit 819f9b75de
13 changed files with 59 additions and 2766 deletions

View File

@ -31,9 +31,12 @@
"nuke:docker": "lerna run --parallel dev:stack:nuke", "nuke:docker": "lerna run --parallel dev:stack:nuke",
"clean": "lerna clean", "clean": "lerna clean",
"kill-port": "kill-port 4001", "kill-port": "kill-port 4001",
"dev": "yarn run kill-port && lerna link && lerna run --parallel dev:builder --concurrency 1", "kill-builder": "kill-port 3000",
"dev:noserver": "lerna link && lerna run dev:stack:up && lerna run --parallel dev:builder --concurrency 1 --ignore @budibase/server --ignore @budibase/worker", "kill-server": "kill-port 4001 4002",
"dev:server": "lerna run --parallel dev:builder --concurrency 1 --scope @budibase/worker --scope @budibase/server", "kill-all": "yarn run kill-builder && yarn run kill-server",
"dev": "yarn run kill-all && lerna link && lerna run --parallel dev:builder --concurrency 1",
"dev:noserver": "yarn run kill-builder && lerna link && lerna run dev:stack:up && lerna run --parallel dev:builder --concurrency 1 --ignore @budibase/server --ignore @budibase/worker",
"dev:server": "yarn run kill-server && lerna run --parallel dev:builder --concurrency 1 --scope @budibase/worker --scope @budibase/server",
"test": "lerna run test", "test": "lerna run test",
"lint:eslint": "eslint packages", "lint:eslint": "eslint packages",
"lint:prettier": "prettier --check \"packages/**/*.{js,ts,svelte}\"", "lint:prettier": "prettier --check \"packages/**/*.{js,ts,svelte}\"",

View File

@ -1,5 +1,6 @@
const env = require("../environment") const env = require("../environment")
const { Headers } = require("../../constants") const { Headers } = require("../../constants")
const { SEPARATOR, DocumentTypes } = require("../db/constants")
const cls = require("./FunctionContext") const cls = require("./FunctionContext")
const { getCouch } = require("../db") const { getCouch } = require("../db")
const { getProdAppID, getDevelopmentAppID } = require("../db/conversions") const { getProdAppID, getDevelopmentAppID } = require("../db/conversions")
@ -42,8 +43,39 @@ exports.doInTenant = (tenantId, task) => {
}) })
} }
/**
* Given an app ID this will attempt to retrieve the tenant ID from it.
* @return {null|string} The tenant ID found within the app ID.
*/
exports.getTenantIDFromAppID = appId => {
if (!appId) {
return null
}
const split = appId.split(SEPARATOR)
const hasDev = split[1] === DocumentTypes.DEV
if ((hasDev && split.length === 3) || (!hasDev && split.length === 2)) {
return null
}
if (hasDev) {
return split[2]
} else {
return split[1]
}
}
const setAppTenantId = appId => {
const appTenantId = this.getTenantIDFromAppID(appId) || this.DEFAULT_TENANT_ID
this.updateTenantId(appTenantId)
}
exports.doInAppContext = (appId, task) => { exports.doInAppContext = (appId, task) => {
if (!appId) {
throw new Error("appId is required")
}
return cls.run(() => { return cls.run(() => {
// set the app tenant id
setAppTenantId(appId)
// set the app ID // set the app ID
cls.setOnContext(ContextKeys.APP_ID, appId) cls.setOnContext(ContextKeys.APP_ID, appId)

View File

@ -9,11 +9,7 @@ const {
APP_PREFIX, APP_PREFIX,
APP_DEV, APP_DEV,
} = require("./constants") } = require("./constants")
const { const { getTenantId, getGlobalDBName } = require("../tenancy")
getTenantId,
getTenantIDFromAppID,
getGlobalDBName,
} = require("../tenancy")
const fetch = require("node-fetch") const fetch = require("node-fetch")
const { getCouch } = require("./index") const { getCouch } = require("./index")
const { getAppMetadata } = require("../cache/appMetadata") const { getAppMetadata } = require("../cache/appMetadata")
@ -39,7 +35,6 @@ exports.DocumentTypes = DocumentTypes
exports.APP_PREFIX = APP_PREFIX exports.APP_PREFIX = APP_PREFIX
exports.APP_DEV = exports.APP_DEV_PREFIX = APP_DEV exports.APP_DEV = exports.APP_DEV_PREFIX = APP_DEV
exports.SEPARATOR = SEPARATOR exports.SEPARATOR = SEPARATOR
exports.getTenantIDFromAppID = getTenantIDFromAppID
exports.isDevApp = isDevApp exports.isDevApp = isDevApp
exports.isProdAppID = isProdAppID exports.isProdAppID = isProdAppID
exports.isDevAppID = isDevAppID exports.isDevAppID = isDevAppID

View File

@ -1,27 +0,0 @@
const {
isMultiTenant,
updateTenantId,
isTenantIdSet,
DEFAULT_TENANT_ID,
updateAppId,
} = require("../tenancy")
const ContextFactory = require("../context/FunctionContext")
const { getTenantIDFromAppID } = require("../db/utils")
module.exports = () => {
return ContextFactory.getMiddleware(ctx => {
// if not in multi-tenancy mode make sure its default and exit
if (!isMultiTenant()) {
updateTenantId(DEFAULT_TENANT_ID)
return
}
// if tenant ID already set no need to continue
if (isTenantIdSet()) {
return
}
const appId = ctx.appId ? ctx.appId : ctx.user ? ctx.user.appId : null
const tenantId = getTenantIDFromAppID(appId) || DEFAULT_TENANT_ID
updateTenantId(tenantId)
updateAppId(appId)
})
}

View File

@ -6,7 +6,6 @@ const { authError } = require("./passport/utils")
const authenticated = require("./authenticated") const authenticated = require("./authenticated")
const auditLog = require("./auditLog") const auditLog = require("./auditLog")
const tenancy = require("./tenancy") const tenancy = require("./tenancy")
const appTenancy = require("./appTenancy")
const internalApi = require("./internalApi") const internalApi = require("./internalApi")
const datasourceGoogle = require("./passport/datasource/google") const datasourceGoogle = require("./passport/datasource/google")
const csrf = require("./csrf") const csrf = require("./csrf")
@ -19,7 +18,6 @@ module.exports = {
authenticated, authenticated,
auditLog, auditLog,
tenancy, tenancy,
appTenancy,
authError, authError,
internalApi, internalApi,
datasource: { datasource: {

View File

@ -1,6 +1,11 @@
const { getDB } = require("../db") const { getDB } = require("../db")
const { SEPARATOR, StaticDatabases, DocumentTypes } = require("../db/constants") const { SEPARATOR, StaticDatabases } = require("../db/constants")
const { getTenantId, DEFAULT_TENANT_ID, isMultiTenant } = require("../context") const {
getTenantId,
DEFAULT_TENANT_ID,
isMultiTenant,
getTenantIDFromAppID,
} = require("../context")
const env = require("../environment") const env = require("../environment")
const TENANT_DOC = StaticDatabases.PLATFORM_INFO.docs.tenants const TENANT_DOC = StaticDatabases.PLATFORM_INFO.docs.tenants
@ -118,26 +123,6 @@ exports.getTenantUser = async identifier => {
} }
} }
/**
* Given an app ID this will attempt to retrieve the tenant ID from it.
* @return {null|string} The tenant ID found within the app ID.
*/
exports.getTenantIDFromAppID = appId => {
if (!appId) {
return null
}
const split = appId.split(SEPARATOR)
const hasDev = split[1] === DocumentTypes.DEV
if ((hasDev && split.length === 3) || (!hasDev && split.length === 2)) {
return null
}
if (hasDev) {
return split[2]
} else {
return split[1]
}
}
exports.isUserInAppTenant = (appId, user = null) => { exports.isUserInAppTenant = (appId, user = null) => {
let userTenantId let userTenantId
if (user) { if (user) {
@ -145,7 +130,7 @@ exports.isUserInAppTenant = (appId, user = null) => {
} else { } else {
userTenantId = getTenantId() userTenantId = getTenantId()
} }
const tenantId = exports.getTenantIDFromAppID(appId) || DEFAULT_TENANT_ID const tenantId = getTenantIDFromAppID(appId) || DEFAULT_TENANT_ID
return tenantId === userTenantId return tenantId === userTenantId
} }

File diff suppressed because one or more lines are too long

View File

@ -7,7 +7,7 @@ const { getAppDB, getAppId } = require("@budibase/backend-core/context")
exports.fetchSelf = async ctx => { exports.fetchSelf = async ctx => {
let userId = ctx.user.userId || ctx.user._id let userId = ctx.user.userId || ctx.user._id
/* istanbul ignore next */ /* istanbul ignore next */
if (!userId) { if (!userId || !ctx.isAuthenticated) {
ctx.body = {} ctx.body = {}
return return
} }

View File

@ -3,7 +3,6 @@ const {
buildAuthMiddleware, buildAuthMiddleware,
auditLog, auditLog,
buildTenancyMiddleware, buildTenancyMiddleware,
buildAppTenancyMiddleware,
} = require("@budibase/backend-core/auth") } = require("@budibase/backend-core/auth")
const currentApp = require("../middleware/currentapp") const currentApp = require("../middleware/currentapp")
const compress = require("koa-compress") const compress = require("koa-compress")
@ -52,8 +51,6 @@ router
}) })
) )
.use(currentApp) .use(currentApp)
// this middleware will try to use the app ID to determine the tenancy
.use(buildAppTenancyMiddleware())
.use(auditLog) .use(auditLog)
// error handling middleware // error handling middleware

View File

@ -71,21 +71,22 @@ module.exports = async (ctx, next) => {
} }
return doInAppContext(appId, async () => { return doInAppContext(appId, async () => {
let noCookieSet = false let skipCookie = false
// if the user not in the right tenant then make sure they have no permissions // if the user not in the right tenant then make sure they have no permissions
// need to judge this only based on the request app ID, // need to judge this only based on the request app ID,
if ( if (
env.MULTI_TENANCY && env.MULTI_TENANCY &&
ctx.user && ctx.user &&
requestAppId && requestAppId &&
!isUserInAppTenant(requestAppId) !isUserInAppTenant(requestAppId, ctx.user)
) { ) {
// don't error, simply remove the users rights (they are a public user) // don't error, simply remove the users rights (they are a public user)
delete ctx.user.builder delete ctx.user.builder
delete ctx.user.admin delete ctx.user.admin
delete ctx.user.roles delete ctx.user.roles
ctx.isAuthenticated = false
roleId = BUILTIN_ROLE_IDS.PUBLIC roleId = BUILTIN_ROLE_IDS.PUBLIC
noCookieSet = true skipCookie = true
} }
ctx.appId = appId ctx.appId = appId
@ -105,7 +106,7 @@ module.exports = async (ctx, next) => {
(requestAppId !== appId || (requestAppId !== appId ||
appCookie == null || appCookie == null ||
appCookie.appId !== requestAppId) && appCookie.appId !== requestAppId) &&
!noCookieSet !skipCookie
) { ) {
setCookie(ctx, { appId }, Cookies.CurrentApp) setCookie(ctx, { appId }, Cookies.CurrentApp)
} }

View File

@ -35,9 +35,7 @@ class TestConfiguration {
} }
executeMiddleware() { executeMiddleware() {
return doInAppContext(APP_ID, () => { return this.middleware(this.ctx, this.next)
return this.middleware(this.ctx, this.next)
})
} }
setUser(user) { setUser(user) {

View File

@ -38,7 +38,7 @@ function mockAuthWithNoCookie() {
}, },
})) }))
jest.mock("@budibase/backend-core/utils", () => ({ jest.mock("@budibase/backend-core/utils", () => ({
getAppId: jest.fn(), getAppIdFromCtx: jest.fn(),
setCookie: jest.fn(), setCookie: jest.fn(),
getCookie: jest.fn(), getCookie: jest.fn(),
})) }))
@ -51,7 +51,7 @@ function mockAuthWithCookie() {
jest.resetModules() jest.resetModules()
mockWorker() mockWorker()
jest.mock("@budibase/backend-core/utils", () => ({ jest.mock("@budibase/backend-core/utils", () => ({
getAppId: () => { getAppIdFromCtx: () => {
return "app_test" return "app_test"
}, },
setCookie: jest.fn(), setCookie: jest.fn(),
@ -143,7 +143,7 @@ describe("Current app middleware", () => {
it("should perform correct when no cookie exists", async () => { it("should perform correct when no cookie exists", async () => {
mockReset() mockReset()
jest.mock("@budibase/backend-core/utils", () => ({ jest.mock("@budibase/backend-core/utils", () => ({
getAppId: () => { getAppIdFromCtx: () => {
return "app_test" return "app_test"
}, },
setCookie: jest.fn(), setCookie: jest.fn(),
@ -158,7 +158,7 @@ describe("Current app middleware", () => {
it("lastly check what occurs when cookie doesn't need updated", async () => { it("lastly check what occurs when cookie doesn't need updated", async () => {
mockReset() mockReset()
jest.mock("@budibase/backend-core/utils", () => ({ jest.mock("@budibase/backend-core/utils", () => ({
getAppId: () => { getAppIdFromCtx: () => {
return "app_test" return "app_test"
}, },
setCookie: jest.fn(), setCookie: jest.fn(),

View File

@ -80,7 +80,7 @@ class TestConfiguration {
return request.body return request.body
} }
// check if already in a context // check if already in a context
if (context.getAppId() == null) { if (context.getAppId() == null && this.appId !== null) {
return context.doInAppContext(this.appId, async () => { return context.doInAppContext(this.appId, async () => {
return run() return run()
}) })