Prefer app url resolution over referer (#9218)
* Prefer app url resolution over referer * Update lockfiles
This commit is contained in:
parent
cca09eaba1
commit
e95222c0f4
|
@ -1,7 +1,8 @@
|
|||
import { structures } from "../../../tests"
|
||||
import * as utils from "../../utils"
|
||||
import * as events from "../../events"
|
||||
import { DEFAULT_TENANT_ID } from "../../constants"
|
||||
import * as db from "../../db"
|
||||
import { DEFAULT_TENANT_ID, Header } from "../../constants"
|
||||
import { doInTenant } from "../../context"
|
||||
|
||||
describe("utils", () => {
|
||||
|
@ -14,4 +15,95 @@ describe("utils", () => {
|
|||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("getAppIdFromCtx", () => {
|
||||
it("gets appId from header", async () => {
|
||||
const ctx = structures.koa.newContext()
|
||||
const expected = db.generateAppID()
|
||||
ctx.request.headers = {
|
||||
[Header.APP_ID]: expected,
|
||||
}
|
||||
|
||||
const actual = await utils.getAppIdFromCtx(ctx)
|
||||
expect(actual).toBe(expected)
|
||||
})
|
||||
|
||||
it("gets appId from body", async () => {
|
||||
const ctx = structures.koa.newContext()
|
||||
const expected = db.generateAppID()
|
||||
ctx.request.body = {
|
||||
appId: expected,
|
||||
}
|
||||
|
||||
const actual = await utils.getAppIdFromCtx(ctx)
|
||||
expect(actual).toBe(expected)
|
||||
})
|
||||
|
||||
it("gets appId from path", async () => {
|
||||
const ctx = structures.koa.newContext()
|
||||
const expected = db.generateAppID()
|
||||
ctx.path = `/apps/${expected}`
|
||||
|
||||
const actual = await utils.getAppIdFromCtx(ctx)
|
||||
expect(actual).toBe(expected)
|
||||
})
|
||||
|
||||
it("gets appId from url", async () => {
|
||||
const ctx = structures.koa.newContext()
|
||||
const expected = db.generateAppID()
|
||||
const app = structures.apps.app(expected)
|
||||
|
||||
// set custom url
|
||||
const appUrl = "custom-url"
|
||||
app.url = `/${appUrl}`
|
||||
ctx.path = `/app/${appUrl}`
|
||||
|
||||
// save the app
|
||||
const database = db.getDB(expected)
|
||||
await database.put(app)
|
||||
|
||||
const actual = await utils.getAppIdFromCtx(ctx)
|
||||
expect(actual).toBe(expected)
|
||||
})
|
||||
|
||||
it("doesn't get appId from url when previewing", async () => {
|
||||
const ctx = structures.koa.newContext()
|
||||
const appId = db.generateAppID()
|
||||
const app = structures.apps.app(appId)
|
||||
|
||||
// set custom url
|
||||
const appUrl = "preview"
|
||||
app.url = `/${appUrl}`
|
||||
ctx.path = `/app/${appUrl}`
|
||||
|
||||
// save the app
|
||||
const database = db.getDB(appId)
|
||||
await database.put(app)
|
||||
|
||||
const actual = await utils.getAppIdFromCtx(ctx)
|
||||
expect(actual).toBe(undefined)
|
||||
})
|
||||
|
||||
it("gets appId from referer", async () => {
|
||||
const ctx = structures.koa.newContext()
|
||||
const expected = db.generateAppID()
|
||||
ctx.request.headers = {
|
||||
referer: `http://test.com/builder/app/${expected}/design/screen_123/screens`,
|
||||
}
|
||||
|
||||
const actual = await utils.getAppIdFromCtx(ctx)
|
||||
expect(actual).toBe(expected)
|
||||
})
|
||||
|
||||
it("doesn't get appId from referer when not builder", async () => {
|
||||
const ctx = structures.koa.newContext()
|
||||
const appId = db.generateAppID()
|
||||
ctx.request.headers = {
|
||||
referer: `http://test.com/foo/app/${appId}/bar`,
|
||||
}
|
||||
|
||||
const actual = await utils.getAppIdFromCtx(ctx)
|
||||
expect(actual).toBe(undefined)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -25,13 +25,16 @@ const jwt = require("jsonwebtoken")
|
|||
const APP_PREFIX = DocumentType.APP + SEPARATOR
|
||||
const PROD_APP_PREFIX = "/app/"
|
||||
|
||||
const BUILDER_PREVIEW_PATH = "/app/preview"
|
||||
const BUILDER_REFERER_PREFIX = "/builder/app/"
|
||||
|
||||
function confirmAppId(possibleAppId: string | undefined) {
|
||||
return possibleAppId && possibleAppId.startsWith(APP_PREFIX)
|
||||
? possibleAppId
|
||||
: undefined
|
||||
}
|
||||
|
||||
async function resolveAppUrl(ctx: Ctx) {
|
||||
export async function resolveAppUrl(ctx: Ctx) {
|
||||
const appUrl = ctx.path.split("/")[2]
|
||||
let possibleAppUrl = `/${appUrl.toLowerCase()}`
|
||||
|
||||
|
@ -75,7 +78,7 @@ export function isServingApp(ctx: Ctx) {
|
|||
*/
|
||||
export async function getAppIdFromCtx(ctx: Ctx) {
|
||||
// look in headers
|
||||
const options = [ctx.headers[Header.APP_ID]]
|
||||
const options = [ctx.request.headers[Header.APP_ID]]
|
||||
let appId
|
||||
for (let option of options) {
|
||||
appId = confirmAppId(option as string)
|
||||
|
@ -95,15 +98,23 @@ export async function getAppIdFromCtx(ctx: Ctx) {
|
|||
appId = confirmAppId(pathId)
|
||||
}
|
||||
|
||||
// look in the referer
|
||||
const refererId = parseAppIdFromUrl(ctx.request.headers.referer)
|
||||
if (!appId && refererId) {
|
||||
appId = confirmAppId(refererId)
|
||||
// lookup using custom url - prod apps only
|
||||
// filter out the builder preview path which collides with the prod app path
|
||||
// to ensure we don't load all apps excessively
|
||||
const isBuilderPreview = ctx.path.startsWith(BUILDER_PREVIEW_PATH)
|
||||
const isViewingProdApp =
|
||||
ctx.path.startsWith(PROD_APP_PREFIX) && !isBuilderPreview
|
||||
if (!appId && isViewingProdApp) {
|
||||
appId = confirmAppId(await resolveAppUrl(ctx))
|
||||
}
|
||||
|
||||
// look in the url - prod app
|
||||
if (!appId && ctx.path.startsWith(PROD_APP_PREFIX)) {
|
||||
appId = confirmAppId(await resolveAppUrl(ctx))
|
||||
// look in the referer - builder only
|
||||
// make sure this is performed after prod app url resolution, in case the
|
||||
// referer header is present from a builder redirect
|
||||
const referer = ctx.request.headers.referer
|
||||
if (!appId && referer?.includes(BUILDER_REFERER_PREFIX)) {
|
||||
const refererId = parseAppIdFromUrl(ctx.request.headers.referer)
|
||||
appId = confirmAppId(refererId)
|
||||
}
|
||||
|
||||
return appId
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
import { generator } from "."
|
||||
import { App } from "@budibase/types"
|
||||
import { DEFAULT_TENANT_ID, DocumentType } from "../../../src/constants"
|
||||
|
||||
export function app(id: string): App {
|
||||
return {
|
||||
_id: DocumentType.APP_METADATA,
|
||||
appId: id,
|
||||
type: "",
|
||||
version: "0.0.1",
|
||||
componentLibraries: [],
|
||||
name: generator.name(),
|
||||
url: `/custom-url`,
|
||||
instance: {
|
||||
_id: id,
|
||||
},
|
||||
tenantId: DEFAULT_TENANT_ID,
|
||||
status: "",
|
||||
template: undefined,
|
||||
}
|
||||
}
|
|
@ -3,7 +3,8 @@ export * from "./common"
|
|||
import Chance from "chance"
|
||||
export const generator = new Chance()
|
||||
|
||||
export * as koa from "./koa"
|
||||
export * as accounts from "./accounts"
|
||||
export * as apps from "./apps"
|
||||
export * as koa from "./koa"
|
||||
export * as licenses from "./licenses"
|
||||
export * as plugins from "./plugins"
|
||||
|
|
|
@ -5,9 +5,11 @@ export const newContext = (): BBContext => {
|
|||
const ctx = createMockContext()
|
||||
return {
|
||||
...ctx,
|
||||
path: "/",
|
||||
cookies: createMockCookies(),
|
||||
request: {
|
||||
...ctx.request,
|
||||
headers: {},
|
||||
body: {},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -3645,9 +3645,9 @@ json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1:
|
|||
integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==
|
||||
|
||||
json5@^2.2.1:
|
||||
version "2.2.3"
|
||||
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
|
||||
integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c"
|
||||
integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==
|
||||
|
||||
jsonc-parser@^3.2.0:
|
||||
version "3.2.0"
|
||||
|
|
Loading…
Reference in New Issue