useAppIdFromCtx helper

This commit is contained in:
Martin McKeaveney 2025-02-15 19:06:21 +00:00
parent c26357c347
commit b85f198fc2
3 changed files with 33 additions and 16 deletions

View File

@ -2,7 +2,6 @@ import Router from "@koa/router"
import * as controller from "../controllers/backup"
import authorized from "../../middleware/authorized"
import { permissions } from "@budibase/backend-core"
import { UserCtx } from "@budibase/types"
import ensureTenantAppOwnership from "../../middleware/ensureTenantAppOwnership"
const router: Router = new Router()
@ -10,7 +9,7 @@ const router: Router = new Router()
router.post(
"/api/backups/export",
authorized(permissions.BUILDER),
ensureTenantAppOwnership((ctx: UserCtx) => ctx.query.appId as string),
ensureTenantAppOwnership,
controller.exportAppDump
)

View File

@ -1,16 +1,17 @@
import { tenancy } from "@budibase/backend-core"
import { tenancy, utils } from "@budibase/backend-core"
import { UserCtx } from "@budibase/types"
function ensureTenantAppOwnership(appIdGetter: (ctx: UserCtx) => string) {
return async (ctx: UserCtx, next: any) => {
const appId = appIdGetter(ctx)
const exportAppId = tenancy.getTenantIDFromAppID(appId)
const tenantId = tenancy.getTenantId()
if (exportAppId !== tenantId) {
ctx.throw(403, `Cannot export app from another tenant`)
}
await next()
async function ensureTenantAppOwnership(ctx: UserCtx, next: any) {
const appId = await utils.getAppIdFromCtx(ctx)
if (!appId) {
ctx.throw(400, "appId must be provided")
}
const exportAppId = tenancy.getTenantIDFromAppID(appId)
const tenantId = tenancy.getTenantId()
if (exportAppId !== tenantId) {
ctx.throw(403, `Cannot export app from another tenant`)
}
await next()
}
export default ensureTenantAppOwnership

View File

@ -1,26 +1,31 @@
import ensureTenantAppOwnership from "../ensureTenantAppOwnership"
import { tenancy } from "@budibase/backend-core"
import { tenancy, utils } from "@budibase/backend-core"
jest.mock("@budibase/backend-core", () => ({
tenancy: {
getTenantId: jest.fn(),
getTenantIDFromAppID: jest.fn(),
},
utils: {
getAppIdFromCtx: jest.fn(),
},
}))
class TestConfiguration {
constructor() {
constructor(appId = "app_123") {
this.next = jest.fn()
this.throw = jest.fn()
this.middleware = ensureTenantAppOwnership(() => "app_123")
this.middleware = ensureTenantAppOwnership
this.ctx = {
next: this.next,
throw: this.throw,
}
utils.getAppIdFromCtx.mockResolvedValue(appId)
}
executeMiddleware() {
async executeMiddleware() {
return this.middleware(this.ctx, this.next)
}
@ -45,6 +50,8 @@ describe("Ensure Tenant Ownership Middleware", () => {
tenancy.getTenantId.mockReturnValue("tenant_1")
await config.executeMiddleware()
expect(utils.getAppIdFromCtx).toHaveBeenCalledWith(config.ctx)
expect(config.next).toHaveBeenCalled()
})
@ -53,9 +60,19 @@ describe("Ensure Tenant Ownership Middleware", () => {
tenancy.getTenantId.mockReturnValue("tenant_1")
await config.executeMiddleware()
expect(utils.getAppIdFromCtx).toHaveBeenCalledWith(config.ctx)
expect(config.throw).toHaveBeenCalledWith(
403,
"Cannot export app from another tenant"
)
})
it("throws 400 when appId is missing", async () => {
utils.getAppIdFromCtx.mockResolvedValue(null)
await config.executeMiddleware()
expect(config.throw).toHaveBeenCalledWith(400, "appId must be provided")
})
})