feat(Jira Trigger Node): Add optional query auth for security (#3172)

*  Add query auth for Jira Trigger security

*  small fixes:

*  Response with 403 when invalid query authentication

* 👕 Fix linting issues

Co-authored-by: Michael Kret <michael.k@radency.com>
Co-authored-by: ricardo <ricardoespinoza105@gmail.com>
This commit is contained in:
pemontto 2022-07-10 09:41:32 +01:00 committed by GitHub
parent dbc02803db
commit 25093b64e6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -4,10 +4,12 @@ import {
} from 'n8n-core'; } from 'n8n-core';
import { import {
ICredentialDataDecryptedObject,
IDataObject, IDataObject,
INodeType, INodeType,
INodeTypeDescription, INodeTypeDescription,
IWebhookResponseData, IWebhookResponseData,
NodeOperationError,
} from 'n8n-workflow'; } from 'n8n-workflow';
import { import {
@ -55,6 +57,17 @@ export class JiraTrigger implements INodeType {
}, },
}, },
}, },
{
name: 'httpQueryAuth',
required: true,
displayOptions: {
show: {
incomingAuthentication: [
'queryAuth',
],
},
},
},
], ],
webhooks: [ webhooks: [
{ {
@ -81,6 +94,23 @@ export class JiraTrigger implements INodeType {
], ],
default: 'cloud', default: 'cloud',
}, },
{
displayName: 'Incoming Authentication',
name: 'incomingAuthentication',
type: 'options',
options: [
{
name: 'Query Auth',
value: 'queryAuth',
},
{
name: 'None',
value: 'none',
},
],
default: 'none',
description: 'If authentication should be activated for the webhook (makes it more secure)',
},
{ {
displayName: 'Events', displayName: 'Events',
name: 'events', name: 'events',
@ -379,6 +409,8 @@ export class JiraTrigger implements INodeType {
const webhookData = this.getWorkflowStaticData('node'); const webhookData = this.getWorkflowStaticData('node');
const incomingAuthentication = this.getNodeParameter('incomingAuthentication') as string;
if (events.includes('*')) { if (events.includes('*')) {
events = allEvents; events = allEvents;
} }
@ -402,12 +434,30 @@ export class JiraTrigger implements INodeType {
body.excludeBody = additionalFields.excludeBody as boolean; body.excludeBody = additionalFields.excludeBody as boolean;
} }
// tslint:disable-next-line: no-any
const parameters: any = {};
if (incomingAuthentication === 'queryAuth') {
let httpQueryAuth;
try{
httpQueryAuth = await this.getCredentials('httpQueryAuth');
} catch (e) {
throw new NodeOperationError(this.getNode(), `Could not retrieve HTTP Query Auth credentials: ${e}`);
}
if (!httpQueryAuth.name && !httpQueryAuth.value) {
throw new NodeOperationError(this.getNode(), `HTTP Query Auth credentials are empty`);
}
parameters[encodeURIComponent(httpQueryAuth.name as string)] = Buffer.from(httpQueryAuth.value as string).toString('base64');
}
if (additionalFields.includeFields) { if (additionalFields.includeFields) {
// tslint:disable-next-line: no-any
const parameters: any = {};
for (const field of additionalFields.includeFields as string[]) { for (const field of additionalFields.includeFields as string[]) {
parameters[field] = '${' + field + '}'; parameters[field] = '${' + field + '}';
} }
}
if (Object.keys(parameters).length) {
body.url = `${body.url}?${queryString.unescape(queryString.stringify(parameters))}`; body.url = `${body.url}?${queryString.unescape(queryString.stringify(parameters))}`;
} }
@ -441,9 +491,46 @@ export class JiraTrigger implements INodeType {
async webhook(this: IWebhookFunctions): Promise<IWebhookResponseData> { async webhook(this: IWebhookFunctions): Promise<IWebhookResponseData> {
const bodyData = this.getBodyData(); const bodyData = this.getBodyData();
const queryData = this.getQueryData(); const queryData = this.getQueryData() as IDataObject;
const response = this.getResponseObject();
Object.assign(bodyData, queryData); const incomingAuthentication = this.getNodeParameter('incomingAuthentication') as string;
if (incomingAuthentication === 'queryAuth') {
let httpQueryAuth: ICredentialDataDecryptedObject | undefined;
try {
httpQueryAuth = await this.getCredentials('httpQueryAuth');
} catch (error) { }
if (httpQueryAuth === undefined || !httpQueryAuth.name || !httpQueryAuth.value) {
response.status(403).json({ message: 'Auth settings are not valid, some data are missing' });
return {
noWebhookResponse: true,
};
}
const paramName = httpQueryAuth.name as string;
const paramValue = Buffer.from(httpQueryAuth.value as string).toString('base64');
if (!queryData.hasOwnProperty(paramName) || queryData[paramName] !== paramValue) {
response.status(403).json({ message: 'Provided authentication data is not valid' });
return {
noWebhookResponse: true,
};
}
delete queryData[paramName];
Object.assign(bodyData, queryData);
} else {
Object.assign(bodyData, queryData);
}
return { return {
workflowData: [ workflowData: [