n8n-nodes-base
Version:
Base nodes of n8n
379 lines • 15.4 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.checkResponseModeConfiguration = exports.isIpAllowed = exports.setupOutputConnection = exports.configuredOutputs = exports.getResponseData = exports.getResponseCode = void 0;
exports.validateWebhookAuthentication = validateWebhookAuthentication;
exports.handleFormData = handleFormData;
exports.generateFormPostBasicAuthToken = generateFormPostBasicAuthToken;
exports.generateBasicAuthToken = generateBasicAuthToken;
const basic_auth_1 = __importDefault(require("basic-auth"));
const promises_1 = require("fs/promises");
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
const n8n_workflow_1 = require("n8n-workflow");
const a = __importStar(require("node:assert"));
const node_crypto_1 = require("node:crypto");
const node_net_1 = require("node:net");
const error_1 = require("./error");
const utilities_1 = require("../../utils/utilities");
const getResponseCode = (parameters) => {
if (parameters.responseCode) {
return parameters.responseCode;
}
const responseCodeOptions = parameters.options;
if (responseCodeOptions?.responseCode?.values) {
const { responseCode, customCode } = responseCodeOptions.responseCode.values;
if (customCode) {
return customCode;
}
return responseCode;
}
return 200;
};
exports.getResponseCode = getResponseCode;
const getResponseData = (parameters) => {
const { responseData, responseMode, options } = parameters;
if (responseData)
return responseData;
if (responseMode === 'onReceived') {
const data = options?.responseData;
if (data)
return data;
}
if (options?.noResponseBody)
return 'noData';
return undefined;
};
exports.getResponseData = getResponseData;
const configuredOutputs = (parameters) => {
const httpMethod = parameters.httpMethod;
if (!Array.isArray(httpMethod))
return [
{
type: 'main',
displayName: httpMethod,
},
];
const outputs = httpMethod.map((method) => {
return {
type: 'main',
displayName: method,
};
});
return outputs;
};
exports.configuredOutputs = configuredOutputs;
const setupOutputConnection = (ctx, method, additionalData) => {
const httpMethod = ctx.getNodeParameter('httpMethod', []);
let webhookUrl = ctx.getNodeWebhookUrl('default');
const executionMode = ctx.getMode() === 'manual' ? 'test' : 'production';
if (executionMode === 'test') {
webhookUrl = webhookUrl.replace('/webhook/', '/webhook-test/');
}
// multi methods could be set in settings of node, so we need to check if it's an array
if (!Array.isArray(httpMethod)) {
return (outputData) => {
outputData.json.webhookUrl = webhookUrl;
outputData.json.executionMode = executionMode;
if (additionalData?.jwtPayload) {
outputData.json.jwtPayload = additionalData.jwtPayload;
}
return [[outputData]];
};
}
const outputIndex = httpMethod.indexOf(method.toUpperCase());
const outputs = httpMethod.map(() => []);
return (outputData) => {
outputData.json.webhookUrl = webhookUrl;
outputData.json.executionMode = executionMode;
if (additionalData?.jwtPayload) {
outputData.json.jwtPayload = additionalData.jwtPayload;
}
outputs[outputIndex] = [outputData];
return outputs;
};
};
exports.setupOutputConnection = setupOutputConnection;
const isIpAllowed = (allowlist, ips, ip) => {
if (allowlist === undefined || allowlist === '') {
return true;
}
if (!Array.isArray(allowlist)) {
allowlist = allowlist.split(',').map((entry) => entry.trim());
}
const allowList = getAllowList(allowlist);
// Check the primary IP address with proper family detection
if (ip) {
const ipFamily = (0, node_net_1.isIPv6)(ip) ? 'ipv6' : 'ipv4';
if (allowList.check(ip, ipFamily)) {
return true;
}
}
// Check proxy IPs with proper family detection
if (ips.some((ipEntry) => {
const ipFamily = (0, node_net_1.isIPv6)(ipEntry) ? 'ipv6' : 'ipv4';
return allowList.check(ipEntry, ipFamily);
})) {
return true;
}
return false;
};
exports.isIpAllowed = isIpAllowed;
const getAllowList = (allowlist) => {
const allowList = new node_net_1.BlockList();
for (const entry of allowlist) {
try {
// Check if entry is in CIDR notation (contains /)
if (entry.includes('/')) {
const [network, prefixStr] = entry.split('/');
const prefix = parseInt(prefixStr, 10);
// Validate prefix is a number
if (isNaN(prefix)) {
continue;
}
// Detect IP type (IPv4 vs IPv6)
const type = network.includes(':') ? 'ipv6' : 'ipv4';
// Validate prefix range
const maxPrefix = type === 'ipv4' ? 32 : 128;
if (prefix < 0 || prefix > maxPrefix) {
continue;
}
allowList.addSubnet(network, prefix, type);
}
else {
// Single IP address
const type = entry.includes(':') ? 'ipv6' : 'ipv4';
allowList.addAddress(entry, type);
}
}
catch {
// Ignore invalid entries
}
}
return allowList;
};
const checkResponseModeConfiguration = (context) => {
const responseMode = context.getNodeParameter('responseMode', 'onReceived');
const connectedNodes = context.getChildNodes(context.getNode().name);
const isRespondToWebhookConnected = connectedNodes.some((node) => node.type === 'n8n-nodes-base.respondToWebhook');
if (!isRespondToWebhookConnected && responseMode === 'responseNode') {
throw new n8n_workflow_1.WorkflowConfigurationError(context.getNode(), new Error('No Respond to Webhook node found in the workflow'), {
description: 'Insert a Respond to Webhook node to your workflow to respond to the webhook or choose another option for the “Respond” parameter',
});
}
if (isRespondToWebhookConnected && !['responseNode', 'streaming'].includes(responseMode)) {
throw new n8n_workflow_1.WorkflowConfigurationError(context.getNode(), new Error('Unused Respond to Webhook node found in the workflow'), {
description: 'Set the “Respond” parameter to “Using Respond to Webhook Node” or remove the Respond to Webhook node',
});
}
};
exports.checkResponseModeConfiguration = checkResponseModeConfiguration;
async function validateWebhookAuthentication(ctx, authPropertyName) {
const authentication = ctx.getNodeParameter(authPropertyName);
if (authentication === 'none')
return;
const req = ctx.getRequestObject();
const headers = ctx.getHeaderData();
if (authentication === 'basicAuth') {
// Basic authorization is needed to call webhook
let expectedAuth;
try {
expectedAuth = await ctx.getCredentials('httpBasicAuth');
}
catch { }
if (expectedAuth === undefined || !expectedAuth.user || !expectedAuth.password) {
// Data is not defined on node so can not authenticate
throw new error_1.WebhookAuthorizationError(500, 'No authentication data defined on node!');
}
const providedAuth = (0, basic_auth_1.default)(req);
// Authorization data is missing
if (!providedAuth) {
const authToken = headers['x-auth-token'];
if (!authToken) {
throw new error_1.WebhookAuthorizationError(401);
}
const expectedAuthToken = generateBasicAuthToken(ctx.getNode(), expectedAuth);
if (!expectedAuthToken ||
typeof authToken !== 'string' ||
expectedAuthToken.length !== authToken.length ||
!(0, node_crypto_1.timingSafeEqual)(Buffer.from(expectedAuthToken), Buffer.from(authToken))) {
throw new error_1.WebhookAuthorizationError(403);
}
}
else if (providedAuth.name !== expectedAuth.user ||
providedAuth.pass !== expectedAuth.password) {
// Provided authentication data is wrong
throw new error_1.WebhookAuthorizationError(403);
}
}
else if (authentication === 'bearerAuth') {
let expectedAuth;
try {
expectedAuth = await ctx.getCredentials('httpBearerAuth');
}
catch { }
const expectedToken = expectedAuth?.token;
if (!expectedToken) {
throw new error_1.WebhookAuthorizationError(500, 'No authentication data defined on node!');
}
if (headers.authorization !== `Bearer ${expectedToken}`) {
throw new error_1.WebhookAuthorizationError(403);
}
}
else if (authentication === 'headerAuth') {
// Special header with value is needed to call webhook
let expectedAuth;
try {
expectedAuth = await ctx.getCredentials('httpHeaderAuth');
}
catch { }
if (expectedAuth === undefined || !expectedAuth.name || !expectedAuth.value) {
// Data is not defined on node so can not authenticate
throw new error_1.WebhookAuthorizationError(500, 'No authentication data defined on node!');
}
const headerName = expectedAuth.name.toLowerCase();
const expectedValue = expectedAuth.value;
if (!headers.hasOwnProperty(headerName) ||
headers[headerName] !== expectedValue) {
// Provided authentication data is wrong
throw new error_1.WebhookAuthorizationError(403);
}
}
else if (authentication === 'jwtAuth') {
let expectedAuth;
try {
expectedAuth = await ctx.getCredentials('jwtAuth');
}
catch { }
if (expectedAuth === undefined) {
// Data is not defined on node so can not authenticate
throw new error_1.WebhookAuthorizationError(500, 'No authentication data defined on node!');
}
const authHeader = req.headers.authorization;
const token = authHeader?.split(' ')[1];
if (!token) {
throw new error_1.WebhookAuthorizationError(401, 'No token provided');
}
let secretOrPublicKey;
if (expectedAuth.keyType === 'passphrase') {
secretOrPublicKey = expectedAuth.secret;
}
else {
secretOrPublicKey = (0, utilities_1.formatPrivateKey)(expectedAuth.publicKey, true);
}
try {
return jsonwebtoken_1.default.verify(token, secretOrPublicKey, {
algorithms: [expectedAuth.algorithm],
});
}
catch (error) {
throw new error_1.WebhookAuthorizationError(403, error.message);
}
}
}
async function handleFormData(context, prepareOutput) {
const req = context.getRequestObject();
a.ok(req.contentType === 'multipart/form-data', 'Expected multipart/form-data');
const options = context.getNodeParameter('options', {});
const { data, files } = req.body;
const returnItem = {
json: {
headers: req.headers,
params: req.params,
query: req.query,
body: data,
},
};
if (files && Object.keys(files).length) {
returnItem.binary = {};
}
let count = 0;
for (const key of Object.keys(files)) {
const processFiles = [];
let multiFile = false;
if (Array.isArray(files[key])) {
processFiles.push.apply(processFiles, files[key]);
multiFile = true;
}
else {
processFiles.push(files[key]);
}
let fileCount = 0;
for (const file of processFiles) {
let binaryPropertyName = key;
if (binaryPropertyName.endsWith('[]')) {
binaryPropertyName = binaryPropertyName.slice(0, -2);
}
if (!binaryPropertyName.trim().length) {
binaryPropertyName = `data${count}`;
}
else if (multiFile) {
binaryPropertyName += fileCount++;
}
if (options.binaryPropertyName) {
binaryPropertyName = `${options.binaryPropertyName}${count}`;
}
returnItem.binary[binaryPropertyName] = await context.nodeHelpers.copyBinaryFile(file.filepath, file.originalFilename ?? file.newFilename, file.mimetype);
// Delete original file to prevent tmp directory from growing too large
await (0, promises_1.rm)(file.filepath, { force: true });
count += 1;
}
}
return { workflowData: prepareOutput(returnItem) };
}
async function generateFormPostBasicAuthToken(context, authPropertyName) {
const node = context.getNode();
const authentication = context.getNodeParameter(authPropertyName);
if (authentication === 'none')
return;
let credentials;
try {
credentials = await context.getCredentials('httpBasicAuth');
}
catch { }
return generateBasicAuthToken(node, credentials);
}
function generateBasicAuthToken(node, credentials) {
if (!credentials || !credentials.user || !credentials.password) {
return;
}
const token = (0, node_crypto_1.createHmac)('sha256', `${credentials.user}:${credentials.password}`)
.update(`${node.id}-${node.webhookId}`)
.digest('hex');
return token;
}
//# sourceMappingURL=utils.js.map