From 88a729913cacdf91acdda3d22a8c5b763c5c2841 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 3 Nov 2021 14:08:47 +0000 Subject: [PATCH] Fixing an issue with webhooks, couldn't use them in development (like getting schema) and making sure trigger will always use production app #3143. --- .../server/src/api/controllers/webhook.js | 49 ++++++++++++------- packages/server/src/automations/utils.js | 7 ++- packages/server/src/middleware/authorized.js | 7 +-- packages/server/src/middleware/currentapp.js | 2 + packages/server/src/middleware/utils.js | 7 +++ 5 files changed, 47 insertions(+), 25 deletions(-) create mode 100644 packages/server/src/middleware/utils.js diff --git a/packages/server/src/api/controllers/webhook.js b/packages/server/src/api/controllers/webhook.js index c810f85004..15ee748d8d 100644 --- a/packages/server/src/api/controllers/webhook.js +++ b/packages/server/src/api/controllers/webhook.js @@ -3,6 +3,7 @@ const { generateWebhookID, getWebhookParams } = require("../../db/utils") const toJsonSchema = require("to-json-schema") const validate = require("jsonschema").validate const triggers = require("../../automations/triggers") +const { getDeployedAppID } = require("@budibase/auth/db") const AUTOMATION_DESCRIPTION = "Generated from Webhook Schema" @@ -76,24 +77,34 @@ exports.buildSchema = async ctx => { } exports.trigger = async ctx => { - const db = new CouchDB(ctx.params.instance) - const webhook = await db.get(ctx.params.id) - // validate against the schema - if (webhook.bodySchema) { - validate(ctx.request.body, webhook.bodySchema) - } - const target = await db.get(webhook.action.target) - if (webhook.action.type === exports.WebhookType.AUTOMATION) { - // trigger with both the pure request and then expand it - // incase the user has produced a schema to bind to - await triggers.externalTrigger(target, { - body: ctx.request.body, - ...ctx.request.body, - appId: ctx.params.instance, - }) - } - ctx.status = 200 - ctx.body = { - message: "Webhook trigger fired successfully", + const prodAppId = getDeployedAppID(ctx.params.instance) + try { + const db = new CouchDB(prodAppId) + const webhook = await db.get(ctx.params.id) + // validate against the schema + if (webhook.bodySchema) { + validate(ctx.request.body, webhook.bodySchema) + } + const target = await db.get(webhook.action.target) + if (webhook.action.type === exports.WebhookType.AUTOMATION) { + // trigger with both the pure request and then expand it + // incase the user has produced a schema to bind to + await triggers.externalTrigger(target, { + body: ctx.request.body, + ...ctx.request.body, + appId: prodAppId, + }) + } + ctx.status = 200 + ctx.body = { + message: "Webhook trigger fired successfully", + } + } catch (err) { + if (err.status === 404) { + ctx.status = 200 + ctx.body = { + message: "Application not deployed yet.", + } + } } } diff --git a/packages/server/src/automations/utils.js b/packages/server/src/automations/utils.js index 4bef91a153..f2d1bf5699 100644 --- a/packages/server/src/automations/utils.js +++ b/packages/server/src/automations/utils.js @@ -6,6 +6,7 @@ const { queue } = require("./bullboard") const newid = require("../db/newid") const { updateEntityMetadata } = require("../utilities") const { MetadataTypes } = require("../constants") +const { getDeployedAppID } = require("@budibase/auth/db") const WH_STEP_ID = definitions.WEBHOOK.stepId const CRON_STEP_ID = definitions.CRON.stepId @@ -150,9 +151,13 @@ exports.checkForWebhooks = async ({ appId, oldAuto, newAuto }) => { await webhooks.save(ctx) const id = ctx.body.webhook._id newTrigger.webhookId = id + // the app ID has to be development for this endpoint + // it can only be used when building the app + // but the trigger endpoint will always be used in production + const prodAppId = getDeployedAppID(appId) newTrigger.inputs = { schemaUrl: `api/webhooks/schema/${appId}/${id}`, - triggerUrl: `api/webhooks/trigger/${appId}/${id}`, + triggerUrl: `api/webhooks/trigger/${prodAppId}/${id}`, } } return newAuto diff --git a/packages/server/src/middleware/authorized.js b/packages/server/src/middleware/authorized.js index bd064f7e66..d91311e165 100644 --- a/packages/server/src/middleware/authorized.js +++ b/packages/server/src/middleware/authorized.js @@ -5,20 +5,17 @@ const { doesHaveBasePermission, } = require("@budibase/auth/permissions") const builderMiddleware = require("./builder") +const { isWebhookEndpoint } = require("./utils") function hasResource(ctx) { return ctx.resourceId != null } -const WEBHOOK_ENDPOINTS = new RegExp( - ["webhooks/trigger", "webhooks/schema"].join("|") -) - module.exports = (permType, permLevel = null) => async (ctx, next) => { // webhooks don't need authentication, each webhook unique - if (WEBHOOK_ENDPOINTS.test(ctx.request.url)) { + if (isWebhookEndpoint(ctx)) { return next() } diff --git a/packages/server/src/middleware/currentapp.js b/packages/server/src/middleware/currentapp.js index 01b9dcc248..5682a860b9 100644 --- a/packages/server/src/middleware/currentapp.js +++ b/packages/server/src/middleware/currentapp.js @@ -9,6 +9,7 @@ const { isUserInAppTenant } = require("@budibase/auth/tenancy") const { getCachedSelf } = require("../utilities/global") const CouchDB = require("../db") const env = require("../environment") +const { isWebhookEndpoint } = require("./utils") module.exports = async (ctx, next) => { // try to get the appID from the request @@ -38,6 +39,7 @@ module.exports = async (ctx, next) => { // deny access to application preview if ( isDevAppID(requestAppId) && + !isWebhookEndpoint(ctx) && (!ctx.user || !ctx.user.builder || !ctx.user.builder.global) ) { clearCookie(ctx, Cookies.CurrentApp) diff --git a/packages/server/src/middleware/utils.js b/packages/server/src/middleware/utils.js new file mode 100644 index 0000000000..b1eea8cd66 --- /dev/null +++ b/packages/server/src/middleware/utils.js @@ -0,0 +1,7 @@ +const WEBHOOK_ENDPOINTS = new RegExp( + ["webhooks/trigger", "webhooks/schema"].join("|") +) + +exports.isWebhookEndpoint = ctx => { + return WEBHOOK_ENDPOINTS.test(ctx.request.url) +}