diff --git a/packages/server/src/middleware/currentapp.ts b/packages/server/src/middleware/currentapp.ts index 984dd8e5e9..e13982a624 100644 --- a/packages/server/src/middleware/currentapp.ts +++ b/packages/server/src/middleware/currentapp.ts @@ -12,103 +12,112 @@ import { getCachedSelf } from "../utilities/global" import env from "../environment" import { isWebhookEndpoint } from "./utils" import { UserCtx, ContextUser } from "@budibase/types" +import tracer from "dd-trace" export default async (ctx: UserCtx, next: any) => { - // try to get the appID from the request - let requestAppId = await utils.getAppIdFromCtx(ctx) - if (!requestAppId) { - return next() - } - - // deny access to application preview - if (!env.isTest()) { - if ( - isDevAppID(requestAppId) && - !isWebhookEndpoint(ctx) && - !users.isBuilder(ctx.user, requestAppId) - ) { - return ctx.redirect("/") + return tracer.trace("currentapp middleware", {}, async span => { + // try to get the appID from the request + let requestAppId = await utils.getAppIdFromCtx(ctx) + if (!requestAppId) { + return next() } - } - let appId: string | undefined, - roleId = roles.BUILTIN_ROLE_IDS.PUBLIC - if (!ctx.user?._id) { - // not logged in, try to set a cookie for public apps - appId = requestAppId - } else if (requestAppId != null) { - // Different App ID means cookie needs reset, or if the same public user has logged in - const globalUser = await getCachedSelf(ctx, requestAppId) - appId = requestAppId - // retrieving global user gets the right role - roleId = globalUser.roleId || roleId + if (requestAppId) { + span?.addTags({ app_id: requestAppId }) + } - // Allow builders to specify their role via a header - const isBuilder = users.isBuilder(globalUser, appId) - const isDevApp = appId && isDevAppID(appId) - const roleHeader = - ctx.request && - (ctx.request.headers[constants.Header.PREVIEW_ROLE] as string) - if (isBuilder && isDevApp && roleHeader) { - // Ensure the role is valid by ensuring a definition exists - try { - if (roleHeader) { - await roles.getRole(roleHeader) - roleId = roleHeader + // deny access to application preview + if (!env.isTest()) { + if ( + isDevAppID(requestAppId) && + !isWebhookEndpoint(ctx) && + !users.isBuilder(ctx.user, requestAppId) + ) { + return ctx.redirect("/") + } + } - // Delete admin and builder flags so that the specified role is honoured - ctx.user = users.removePortalUserPermissions(ctx.user) as ContextUser + let appId: string | undefined, + roleId = roles.BUILTIN_ROLE_IDS.PUBLIC + if (!ctx.user?._id) { + // not logged in, try to set a cookie for public apps + appId = requestAppId + } else if (requestAppId != null) { + // Different App ID means cookie needs reset, or if the same public user has logged in + const globalUser = await getCachedSelf(ctx, requestAppId) + appId = requestAppId + // retrieving global user gets the right role + roleId = globalUser.roleId || roleId + + // Allow builders to specify their role via a header + const isBuilder = users.isBuilder(globalUser, appId) + const isDevApp = appId && isDevAppID(appId) + const roleHeader = + ctx.request && + (ctx.request.headers[constants.Header.PREVIEW_ROLE] as string) + if (isBuilder && isDevApp && roleHeader) { + // Ensure the role is valid by ensuring a definition exists + try { + if (roleHeader) { + await roles.getRole(roleHeader) + roleId = roleHeader + + // Delete admin and builder flags so that the specified role is honoured + ctx.user = users.removePortalUserPermissions( + ctx.user + ) as ContextUser + } + } catch (error) { + // Swallow error and do nothing } - } catch (error) { - // Swallow error and do nothing } } - } - // nothing more to do - if (!appId) { - return next() - } + // nothing more to do + if (!appId) { + return next() + } - const userId = ctx.user ? generateUserMetadataID(ctx.user._id!) : undefined + const userId = ctx.user ? generateUserMetadataID(ctx.user._id!) : undefined - // if the user is not in the right tenant then make sure to wipe their cookie - // also cleanse any information about them that has been allocated - // this avoids apps making calls to say the worker which are cross tenant, - // we simply remove the authentication - if ( - env.MULTI_TENANCY && - userId && - requestAppId && - !tenancy.isUserInAppTenant(requestAppId, ctx.user) - ) { - // clear out the user - ctx.user = users.cleanseUserObject(ctx.user) as ContextUser - ctx.isAuthenticated = false - roleId = roles.BUILTIN_ROLE_IDS.PUBLIC - // remove the cookie, so future calls are public - await auth.platformLogout({ - ctx, - userId, - }) - } - - return context.doInAppContext(appId, async () => { - ctx.appId = appId - if (roleId) { - ctx.roleId = roleId - const globalId = ctx.user ? ctx.user._id : undefined - ctx.user = { - ...ctx.user!, - // override userID with metadata one - _id: userId, + // if the user is not in the right tenant then make sure to wipe their cookie + // also cleanse any information about them that has been allocated + // this avoids apps making calls to say the worker which are cross tenant, + // we simply remove the authentication + if ( + env.MULTI_TENANCY && + userId && + requestAppId && + !tenancy.isUserInAppTenant(requestAppId, ctx.user) + ) { + // clear out the user + ctx.user = users.cleanseUserObject(ctx.user) as ContextUser + ctx.isAuthenticated = false + roleId = roles.BUILTIN_ROLE_IDS.PUBLIC + // remove the cookie, so future calls are public + await auth.platformLogout({ + ctx, userId, - globalId, - roleId, - role: await roles.getRole(roleId, { defaultPublic: true }), - } + }) } - return next() + return context.doInAppContext(appId, async () => { + ctx.appId = appId + if (roleId) { + ctx.roleId = roleId + const globalId = ctx.user ? ctx.user._id : undefined + ctx.user = { + ...ctx.user!, + // override userID with metadata one + _id: userId, + userId, + globalId, + roleId, + role: await roles.getRole(roleId, { defaultPublic: true }), + } + } + + return next() + }) }) } diff --git a/packages/server/src/utilities/rowProcessor/utils.ts b/packages/server/src/utilities/rowProcessor/utils.ts index 9eb725dd7c..d0072053b8 100644 --- a/packages/server/src/utilities/rowProcessor/utils.ts +++ b/packages/server/src/utilities/rowProcessor/utils.ts @@ -11,6 +11,7 @@ import { Row, Table, } from "@budibase/types" +import tracer from "dd-trace" interface FormulaOpts { dynamic?: boolean @@ -50,33 +51,41 @@ export function processFormulas( inputRows: T, { dynamic, contextRows }: FormulaOpts = { dynamic: true } ): T { - const rows = Array.isArray(inputRows) ? inputRows : [inputRows] - if (rows) - for (let [column, schema] of Object.entries(table.schema)) { - if (schema.type !== FieldTypes.FORMULA) { - continue - } + return tracer.trace("processFormulas", {}, span => { + span?.addTags({ table_id: table._id }) + const rows = Array.isArray(inputRows) ? inputRows : [inputRows] + if (rows) { + for (let [column, schema] of Object.entries(table.schema)) { + if (schema.type !== FieldTypes.FORMULA) { + continue + } - const isStatic = schema.formulaType === FormulaTypes.STATIC + const isStatic = schema.formulaType === FormulaTypes.STATIC - if ( - schema.formula == null || - (dynamic && isStatic) || - (!dynamic && !isStatic) - ) { - continue - } - // iterate through rows and process formula - for (let i = 0; i < rows.length; i++) { - let row = rows[i] - let context = contextRows ? contextRows[i] : row - rows[i] = { - ...row, - [column]: processStringSync(schema.formula, context), + if ( + schema.formula == null || + (dynamic && isStatic) || + (!dynamic && !isStatic) + ) { + continue + } + // iterate through rows and process formula + for (let i = 0; i < rows.length; i++) { + let row = rows[i] + let context = contextRows ? contextRows[i] : row + let formula = schema.formula + rows[i] = { + ...row, + [column]: tracer.trace("processStringSync", {}, span => { + span?.addTags({ column }) + return processStringSync(formula, context) + }), + } } } } - return Array.isArray(inputRows) ? rows : rows[0] + return Array.isArray(inputRows) ? rows : rows[0] + }) } /**