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

View File

@ -1,9 +1,11 @@
import { tenancy } from "@budibase/backend-core" import { tenancy, utils } from "@budibase/backend-core"
import { UserCtx } from "@budibase/types" import { UserCtx } from "@budibase/types"
function ensureTenantAppOwnership(appIdGetter: (ctx: UserCtx) => string) { async function ensureTenantAppOwnership(ctx: UserCtx, next: any) {
return async (ctx: UserCtx, next: any) => { const appId = await utils.getAppIdFromCtx(ctx)
const appId = appIdGetter(ctx) if (!appId) {
ctx.throw(400, "appId must be provided")
}
const exportAppId = tenancy.getTenantIDFromAppID(appId) const exportAppId = tenancy.getTenantIDFromAppID(appId)
const tenantId = tenancy.getTenantId() const tenantId = tenancy.getTenantId()
if (exportAppId !== tenantId) { if (exportAppId !== tenantId) {
@ -11,6 +13,5 @@ function ensureTenantAppOwnership(appIdGetter: (ctx: UserCtx) => string) {
} }
await next() await next()
} }
}
export default ensureTenantAppOwnership export default ensureTenantAppOwnership

View File

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