ensure app ownership middleware
This commit is contained in:
parent
511d3200ee
commit
c26357c347
|
@ -2,12 +2,15 @@ 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"
|
||||||
|
|
||||||
const router: Router = new Router()
|
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),
|
||||||
controller.exportAppDump
|
controller.exportAppDump
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { tenancy } 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ensureTenantAppOwnership
|
|
@ -0,0 +1,61 @@
|
||||||
|
import ensureTenantAppOwnership from "../ensureTenantAppOwnership"
|
||||||
|
import { tenancy } from "@budibase/backend-core"
|
||||||
|
|
||||||
|
jest.mock("@budibase/backend-core", () => ({
|
||||||
|
tenancy: {
|
||||||
|
getTenantId: jest.fn(),
|
||||||
|
getTenantIDFromAppID: jest.fn(),
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
class TestConfiguration {
|
||||||
|
constructor() {
|
||||||
|
this.next = jest.fn()
|
||||||
|
this.throw = jest.fn()
|
||||||
|
this.middleware = ensureTenantAppOwnership(() => "app_123")
|
||||||
|
|
||||||
|
this.ctx = {
|
||||||
|
next: this.next,
|
||||||
|
throw: this.throw,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
executeMiddleware() {
|
||||||
|
return this.middleware(this.ctx, this.next)
|
||||||
|
}
|
||||||
|
|
||||||
|
afterEach() {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("Ensure Tenant Ownership Middleware", () => {
|
||||||
|
let config
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
config = new TestConfiguration()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
config.afterEach()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("calls next() when tenant IDs match", async () => {
|
||||||
|
tenancy.getTenantIDFromAppID.mockReturnValue("tenant_1")
|
||||||
|
tenancy.getTenantId.mockReturnValue("tenant_1")
|
||||||
|
|
||||||
|
await config.executeMiddleware()
|
||||||
|
expect(config.next).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("throws 403 when tenant IDs do not match", async () => {
|
||||||
|
tenancy.getTenantIDFromAppID.mockReturnValue("tenant_2")
|
||||||
|
tenancy.getTenantId.mockReturnValue("tenant_1")
|
||||||
|
|
||||||
|
await config.executeMiddleware()
|
||||||
|
expect(config.throw).toHaveBeenCalledWith(
|
||||||
|
403,
|
||||||
|
"Cannot export app from another tenant"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
Loading…
Reference in New Issue