Fix cross tenant apps with session

This commit is contained in:
Rory Powell 2022-03-23 16:45:06 +00:00
parent 4ee5256ad3
commit a68abf2730
11 changed files with 725 additions and 2555 deletions

View File

@ -52,9 +52,8 @@ http {
proxy_pass http://{{ address }}:4001; proxy_pass http://{{ address }}:4001;
} }
location /app/ { location /app {
proxy_pass http://{{ address }}:4001; proxy_pass http://{{ address }}:4001;
rewrite ^/app/(.*)$ /$1 break;
} }
location /builder { location /builder {

View File

@ -62,7 +62,6 @@ http {
location /app { location /app {
proxy_pass http://$apps:4002; proxy_pass http://$apps:4002;
rewrite ^/app/(.*)$ /$1 break;
} }
location = / { location = / {

View File

@ -3,6 +3,7 @@ const {
SEPARATOR, SEPARATOR,
ViewNames, ViewNames,
generateGlobalUserID, generateGlobalUserID,
getAllApps,
} = require("./db/utils") } = require("./db/utils")
const jwt = require("jsonwebtoken") const jwt = require("jsonwebtoken")
const { options } = require("./middleware/passport/jwt") const { options } = require("./middleware/passport/jwt")
@ -20,8 +21,10 @@ const { hash } = require("./hashing")
const userCache = require("./cache/user") const userCache = require("./cache/user")
const env = require("./environment") const env = require("./environment")
const { getUserSessions, invalidateSessions } = require("./security/sessions") const { getUserSessions, invalidateSessions } = require("./security/sessions")
const tenancy = require("./tenancy")
const APP_PREFIX = DocumentTypes.APP + SEPARATOR const APP_PREFIX = DocumentTypes.APP + SEPARATOR
const PROD_APP_PREFIX = "/app/"
function confirmAppId(possibleAppId) { function confirmAppId(possibleAppId) {
return possibleAppId && possibleAppId.startsWith(APP_PREFIX) return possibleAppId && possibleAppId.startsWith(APP_PREFIX)
@ -29,16 +32,35 @@ function confirmAppId(possibleAppId) {
: undefined : undefined
} }
async function resolveAppUrl(ctx) {
const appUrl = ctx.path.split("/")[2]
let possibleAppUrl = `/${appUrl.toLowerCase()}`
let tenantId = tenancy.getTenantId()
if (!env.SELF_HOSTED && ctx.subdomains.length) {
// always use the tenant id from the url in cloud
tenantId = ctx.subdomains[0]
}
// search prod apps for a url that matches
const apps = await tenancy.doInTenant(tenantId, () =>
getAllApps({ dev: false })
)
const app = apps.filter(
a => a.url && a.url.toLowerCase() === possibleAppUrl
)[0]
return app && app.appId ? app.appId : undefined
}
/** /**
* Given a request tries to find the appId, which can be located in various places * Given a request tries to find the appId, which can be located in various places
* @param {object} ctx The main request body to look through. * @param {object} ctx The main request body to look through.
* @returns {string|undefined} If an appId was found it will be returned. * @returns {string|undefined} If an appId was found it will be returned.
*/ */
exports.getAppId = ctx => { exports.getAppIdFromCtx = async ctx => {
const options = [ctx.headers[Headers.APP_ID], ctx.params.appId] // look in headers
if (ctx.subdomains) { const options = [ctx.headers[Headers.APP_ID]]
options.push(ctx.subdomains[1])
}
let appId let appId
for (let option of options) { for (let option of options) {
appId = confirmAppId(option) appId = confirmAppId(option)
@ -47,16 +69,24 @@ exports.getAppId = ctx => {
} }
} }
// look in body if can't find it in subdomain // look in body
if (!appId && ctx.request.body && ctx.request.body.appId) { if (!appId && ctx.request.body && ctx.request.body.appId) {
appId = confirmAppId(ctx.request.body.appId) appId = confirmAppId(ctx.request.body.appId)
} }
// look in the url - dev app
let appPath = let appPath =
ctx.request.headers.referrer || ctx.request.headers.referrer ||
ctx.path.split("/").filter(subPath => subPath.startsWith(APP_PREFIX)) ctx.path.split("/").filter(subPath => subPath.startsWith(APP_PREFIX))
if (!appId && appPath.length !== 0) { if (!appId && appPath.length) {
appId = confirmAppId(appPath[0]) appId = confirmAppId(appPath[0])
} }
// look in the url - prod app
if (!appId && ctx.path.startsWith(PROD_APP_PREFIX)) {
appId = confirmAppId(await resolveAppUrl(ctx))
}
return appId return appId
} }

File diff suppressed because one or more lines are too long

View File

@ -5,7 +5,6 @@ const { resolve, join } = require("../../../utilities/centralPath")
const uuid = require("uuid") const uuid = require("uuid")
const { ObjectStoreBuckets } = require("../../../constants") const { ObjectStoreBuckets } = require("../../../constants")
const { processString } = require("@budibase/string-templates") const { processString } = require("@budibase/string-templates")
const { getAllApps } = require("@budibase/backend-core/db")
const { const {
loadHandlebarsFile, loadHandlebarsFile,
NODE_MODULES_PATH, NODE_MODULES_PATH,
@ -16,7 +15,7 @@ const { clientLibraryPath } = require("../../../utilities")
const { upload } = require("../../../utilities/fileSystem") const { upload } = require("../../../utilities/fileSystem")
const { attachmentsRelativeURL } = require("../../../utilities") const { attachmentsRelativeURL } = require("../../../utilities")
const { DocumentTypes } = require("../../../db/utils") const { DocumentTypes } = require("../../../db/utils")
const { getAppDB, updateAppId } = require("@budibase/backend-core/context") const { getAppDB, getAppId } = require("@budibase/backend-core/context")
const AWS = require("aws-sdk") const AWS = require("aws-sdk")
const AWS_REGION = env.AWS_REGION ? env.AWS_REGION : "eu-west-1" const AWS_REGION = env.AWS_REGION ? env.AWS_REGION : "eu-west-1"
@ -39,21 +38,6 @@ async function prepareUpload({ s3Key, bucket, metadata, file }) {
} }
} }
async function getAppIdFromUrl(ctx) {
// the "appId" component of the URL can be the id or the custom url
let possibleAppUrl = `/${encodeURI(ctx.params.appId).toLowerCase()}`
// search prod apps for a url that matches, exclude dev where id is always used
const apps = await getAllApps({ dev: false })
const app = apps.filter(
a => a.url && a.url.toLowerCase() === possibleAppUrl
)[0]
const appId = app && app.appId ? app.appId : ctx.params.appId
updateAppId(appId)
return appId
}
exports.serveBuilder = async function (ctx) { exports.serveBuilder = async function (ctx) {
let builderPath = resolve(TOP_LEVEL_PATH, "builder") let builderPath = resolve(TOP_LEVEL_PATH, "builder")
await send(ctx, ctx.file, { root: builderPath }) await send(ctx, ctx.file, { root: builderPath })
@ -81,10 +65,10 @@ exports.uploadFile = async function (ctx) {
} }
exports.serveApp = async function (ctx) { exports.serveApp = async function (ctx) {
let appId = await getAppIdFromUrl(ctx)
const App = require("./templates/BudibaseApp.svelte").default const App = require("./templates/BudibaseApp.svelte").default
const db = getAppDB({ skip_setup: true }) const db = getAppDB({ skip_setup: true })
const appInfo = await db.get(DocumentTypes.APP_METADATA) const appInfo = await db.get(DocumentTypes.APP_METADATA)
let appId = getAppId()
const { head, html, css } = App.render({ const { head, html, css } = App.render({
title: appInfo.name, title: appInfo.name,

View File

@ -44,8 +44,7 @@ router
authorized(PermissionTypes.TABLE, PermissionLevels.WRITE), authorized(PermissionTypes.TABLE, PermissionLevels.WRITE),
controller.uploadFile controller.uploadFile
) )
// TODO: this likely needs to be secured in some way .get("/app/:appUrl/:path*", controller.serveApp)
.get("/:appId/:path*", controller.serveApp)
.post( .post(
"/api/attachments/:datasourceId/url", "/api/attachments/:datasourceId/url",
authorized(PermissionTypes.TABLE, PermissionLevels.READ), authorized(PermissionTypes.TABLE, PermissionLevels.READ),

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
const { const {
getAppId, getAppIdFromCtx,
setCookie, setCookie,
getCookie, getCookie,
clearCookie, clearCookie,
@ -17,7 +17,7 @@ const { doInAppContext } = require("@budibase/backend-core/context")
module.exports = async (ctx, next) => { module.exports = async (ctx, next) => {
// try to get the appID from the request // try to get the appID from the request
let requestAppId = getAppId(ctx) let requestAppId = await getAppIdFromCtx(ctx)
// get app cookie if it exists // get app cookie if it exists
let appCookie = null let appCookie = null
try { try {

View File

@ -1,9 +1,9 @@
const { Headers } = require("@budibase/backend-core/constants") const { Headers } = require("@budibase/backend-core/constants")
const { getAppId } = require("@budibase/backend-core/utils") const { getAppIdFromCtx } = require("@budibase/backend-core/utils")
module.exports = function ({ requiresAppId } = {}) { module.exports = function ({ requiresAppId } = {}) {
return async (ctx, next) => { return async (ctx, next) => {
const appId = getAppId(ctx) const appId = await getAppIdFromCtx(ctx)
if (requiresAppId && !appId) { if (requiresAppId && !appId) {
ctx.throw( ctx.throw(
400, 400,

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff