Skip to content

Commit

Permalink
⚡ add access control options for Webhook
Browse files Browse the repository at this point in the history
  • Loading branch information
michael-radency authored and netroy committed Nov 22, 2023
1 parent 9b4856e commit c0187a9
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 0 deletions.
4 changes: 4 additions & 0 deletions packages/cli/src/Interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,10 @@ export type WaitingWebhookRequest = WebhookRequest & {

export interface IWebhookManager {
getWebhookMethods?: (path: string) => Promise<IHttpRequestMethods[]>;
getAccessControlOptions?: (
path: string,
httpMethod: IHttpRequestMethods,
) => Promise<IDataObject | null>;
executeWebhook(req: WebhookRequest, res: Response): Promise<IResponseCallbackData>;
}

Expand Down
28 changes: 28 additions & 0 deletions packages/cli/src/TestWebhooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type {
Workflow,
WorkflowActivateMode,
WorkflowExecuteMode,
IDataObject,
} from 'n8n-workflow';

import { ActiveWebhooks } from '@/ActiveWebhooks';
Expand Down Expand Up @@ -177,6 +178,33 @@ export class TestWebhooks implements IWebhookManager {
return webhookMethods;
}

/**
* Gets all request methods associated with a single test webhook
*/
async getAccessControlOptions(path: string, httpMethod: string): Promise<IDataObject | null> {
const webhookWorkflow = Object.keys(this.testWebhookData).find(
(key) => key.includes(path) && key.startsWith(httpMethod),
);

const nodes = webhookWorkflow ? this.testWebhookData[webhookWorkflow].workflow.nodes : {};

if (!Object.keys(nodes).length) {
return null;
}

const result = Object.values(nodes).find((node) => {
return (
node.type === 'n8n-nodes-base.webhook' &&
node.parameters?.path === path &&
node.parameters?.httpMethod === httpMethod
);
});

const { accessControl } = (result?.parameters?.options as IDataObject) || {};

return accessControl ? ((accessControl as IDataObject).values as IDataObject) : null;
}

/**
* Checks if it has to wait for webhook data to execute the workflow.
* If yes it waits for it and resolves with the result of the workflow if not it simply resolves with undefined
Expand Down
19 changes: 19 additions & 0 deletions packages/cli/src/WebhookHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,25 @@ export const webhookRequestHandler =
}

if (method === 'OPTIONS') {
const controlRequestMethod = req.headers['access-control-request-method'];

if (webhookManager.getAccessControlOptions && controlRequestMethod) {
try {
const accessControlOptions = await webhookManager.getAccessControlOptions(
path,
controlRequestMethod as IHttpRequestMethods,
);

if (accessControlOptions) {
const { allowMethods, allowOrigin, maxAge } = accessControlOptions;

res.header('Access-Control-Allow-Methods', (allowMethods as string) || '*');
res.header('Access-Control-Allow-Origin', (allowOrigin as string) || '*');
res.header('Access-Control-Max-Age', String((maxAge as number) * 1000) || '60000');
}
} catch (error) {}
}

return ResponseHelper.sendSuccessResponse(res, {}, true, 204);
}

Expand Down
1 change: 1 addition & 0 deletions packages/nodes-base/nodes/Webhook/Webhook.node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ export class Webhook extends Node {
ignoreBots: boolean;
rawBody: Buffer;
responseData?: string;
accessControl: IDataObject;
};
const req = context.getRequestObject();
const resp = context.getResponseObject();
Expand Down
54 changes: 54 additions & 0 deletions packages/nodes-base/nodes/Webhook/description.ts
Original file line number Diff line number Diff line change
Expand Up @@ -335,5 +335,59 @@ export const optionsProperty: INodeProperties = {
default: 'data',
description: 'Name of the property to return the data of instead of the whole JSON',
},
{
displayName: 'Access Control',
name: 'accessControl',
placeholder: 'Add Options Header',
description: 'Add headers to the preflight response',
type: 'fixedCollection',
default: {
values: {
allowOrigin: '*',
allowMethods: '*',
// allowHeaders: '',
maxAge: 60,
},
},
options: [
{
name: 'values',
displayName: 'Values',
values: [
{
displayName: 'Origin',
name: 'allowOrigin',
type: 'string',
default: '',
description: 'The origin(s) to allow requests from',
},
{
displayName: 'Methods',
name: 'allowMethods',
type: 'string',
default: '',
description: 'The methods to allow',
},
// {
// displayName: 'Headers',
// name: 'allowHeaders',
// type: 'string',
// default: '',
// description: 'The headers to allow',
// },
{
displayName: 'Max Age',
name: 'maxAge',
type: 'number',
typeOptions: {
minValue: 0,
},
default: 0,
description: 'The max age to allow',
},
],
},
],
},
],
};

0 comments on commit c0187a9

Please sign in to comment.