first commit
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled

This commit is contained in:
2026-03-17 16:22:57 +03:30
commit 3d5eaf9445
15349 changed files with 2847338 additions and 0 deletions
@@ -0,0 +1,394 @@
import type { INodeProperties } from 'n8n-workflow';
export const certificateOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
noDataExpression: true,
type: 'options',
displayOptions: {
show: {
resource: ['certificate'],
},
},
options: [
{
name: 'Delete',
value: 'delete',
description: 'Delete a certificate',
action: 'Delete a certificate',
},
{
name: 'Download',
value: 'download',
description: 'Download a certificate',
action: 'Download a certificate',
},
{
name: 'Get',
value: 'get',
description: 'Retrieve a certificate',
action: 'Get a certificate',
},
{
name: 'Get Many',
value: 'getMany',
description: 'Retrieve many certificates',
action: 'Get many certificates',
},
{
name: 'Renew',
value: 'renew',
description: 'Renew a certificate',
action: 'Renew a certificate',
},
],
default: 'delete',
},
];
export const certificateFields: INodeProperties[] = [
/* -------------------------------------------------------------------------- */
/* certificate:download */
/* -------------------------------------------------------------------------- */
{
displayName: 'Certificate ID',
name: 'certificateId',
type: 'string',
required: true,
displayOptions: {
show: {
operation: ['download'],
resource: ['certificate'],
},
},
default: '',
},
{
displayName: 'Download Item',
name: 'downloadItem',
type: 'options',
options: [
{
name: 'Certificate',
value: 'certificate',
},
{
name: 'Keystore',
value: 'keystore',
},
],
displayOptions: {
show: {
operation: ['download'],
resource: ['certificate'],
},
},
default: 'certificate',
},
{
displayName: 'Keystore Type',
name: 'keystoreType',
type: 'options',
options: [
{
name: 'JKS',
value: 'JKS',
},
{
name: 'PKCS12',
value: 'PKCS12',
},
{
name: 'PEM',
value: 'PEM',
},
],
default: 'PEM',
displayOptions: {
show: {
operation: ['download'],
resource: ['certificate'],
downloadItem: ['keystore'],
},
},
},
{
displayName: 'Certificate Label',
name: 'certificateLabel',
type: 'string',
required: true,
displayOptions: {
show: {
operation: ['download'],
resource: ['certificate'],
downloadItem: ['keystore'],
},
},
default: '',
},
{
displayName: 'Private Key Passphrase',
name: 'privateKeyPassphrase',
type: 'string',
required: true,
displayOptions: {
show: {
operation: ['download'],
resource: ['certificate'],
downloadItem: ['keystore'],
},
},
default: '',
},
{
displayName: 'Keystore Passphrase',
name: 'keystorePassphrase',
type: 'string',
required: true,
displayOptions: {
show: {
operation: ['download'],
resource: ['certificate'],
downloadItem: ['keystore'],
keystoreType: ['JKS'],
},
},
default: '',
},
{
displayName: 'Input Data Field Name',
name: 'binaryProperty',
type: 'string',
default: 'data',
displayOptions: {
show: {
operation: ['download'],
resource: ['certificate'],
},
},
required: true,
description: 'The name of the input field containing the binary file data to be uploaded',
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
operation: ['download'],
resource: ['certificate'],
},
},
options: [
{
displayName: 'Chain Order',
name: 'chainOrder',
type: 'options',
options: [
{
name: 'EE_FIRST',
value: 'EE_FIRST',
description: 'Download the certificate with the end-entity portion of the chain first',
},
{
name: 'EE_ONLY',
value: 'EE_ONLY',
description: 'Download only the end-entity certificate',
},
{
name: 'ROOT_FIRST',
value: 'ROOT_FIRST',
description: 'Download the certificate with root portion of the chain first',
},
],
default: 'ROOT_FIRST',
},
{
displayName: 'Format',
name: 'format',
type: 'options',
options: [
{
name: 'PEM',
value: 'PEM',
},
{
name: 'DER',
value: 'DER',
},
],
default: 'PEM',
},
],
},
/* -------------------------------------------------------------------------- */
/* certificate:get */
/* -------------------------------------------------------------------------- */
{
displayName: 'Certificate ID',
name: 'certificateId',
type: 'string',
required: true,
displayOptions: {
show: {
operation: ['get', 'delete'],
resource: ['certificate'],
},
},
default: '',
},
/* -------------------------------------------------------------------------- */
/* certificate:getMany */
/* -------------------------------------------------------------------------- */
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
operation: ['getMany'],
resource: ['certificate'],
},
},
default: false,
description: 'Whether to return all results or only up to a given limit',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
operation: ['getMany'],
resource: ['certificate'],
returnAll: [false],
},
},
typeOptions: {
minValue: 1,
maxValue: 500,
},
default: 50,
description: 'Max number of results to return',
},
{
displayName: 'Filters',
name: 'filters',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
operation: ['getMany'],
resource: ['certificate'],
},
},
options: [
{
displayName: 'Subject',
name: 'subject',
type: 'string',
default: '',
},
],
},
/* -------------------------------------------------------------------------- */
/* certificate:renew */
/* -------------------------------------------------------------------------- */
{
displayName: 'Application Name or ID',
name: 'applicationId',
type: 'options',
description:
'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
typeOptions: {
loadOptionsMethod: 'getApplications',
},
displayOptions: {
show: {
operation: ['renew'],
resource: ['certificate'],
},
},
default: '',
},
{
displayName: 'Existing Certificate ID',
name: 'existingCertificateId',
type: 'string',
displayOptions: {
show: {
operation: ['renew'],
resource: ['certificate'],
},
},
default: '',
},
{
displayName: 'Certificate Issuing Template Name or ID',
name: 'certificateIssuingTemplateId',
type: 'options',
description:
'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
typeOptions: {
loadOptionsMethod: 'getCertificateIssuingTemplates',
},
displayOptions: {
show: {
operation: ['renew'],
resource: ['certificate'],
},
},
default: '',
},
{
displayName: 'Certificate Signing Request',
name: 'certificateSigningRequest',
type: 'string',
displayOptions: {
show: {
operation: ['renew'],
resource: ['certificate'],
},
},
default: '',
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
operation: ['renew'],
resource: ['certificate'],
},
},
options: [
{
displayName: 'Validity Period',
name: 'validityPeriod',
type: 'options',
options: [
{
name: '1 Year',
value: 'P1Y',
},
{
name: '10 Days',
value: 'P10D',
},
{
name: '12 Hours',
value: 'PT12H',
},
],
default: 'P1Y',
},
],
},
];
@@ -0,0 +1,41 @@
export interface ICertficateRequest {
isVaaSGenerated?: boolean;
csrAttributes?: ICsrAttributes;
applicationServerTypeId?: string;
certificateSigningRequest?: string;
applicationId?: string;
certificateIssuingTemplateId?: string;
certficateOwnerUserId?: string;
validityPeriod?: string;
}
export interface ICsrAttributes {
commonName?: string;
organization?: string;
organizationalUnits?: string[];
locality?: string;
state?: string;
country?: string;
keyTypeParameters?: IKeyTypeParameters;
subjectAlternativeNamesByType?: ISubjectAltNamesByType;
}
export interface IKeyTypeParameters {
keyType?: string;
keyCurve?: string;
keyLength?: number;
}
export interface ISubjectAltNamesByType {
dnsNames?: string[];
rfc822Names?: string[];
ipAddresses?: string[];
uniformResourceIdentifiers?: string[];
}
export interface ICertficateKeystoreRequest {
exportFormat?: string;
encryptedPrivateKeyPassphrase?: string;
encryptedKeystorePassphrase?: string;
certificateLabel?: string;
}
@@ -0,0 +1,359 @@
import type { INodeProperties } from 'n8n-workflow';
export const certificateRequestOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
noDataExpression: true,
type: 'options',
displayOptions: {
show: {
resource: ['certificateRequest'],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create a new certificate request',
action: 'Create a certificate request',
},
{
name: 'Get',
value: 'get',
description: 'Retrieve a certificate request',
action: 'Get a certificate request',
},
{
name: 'Get Many',
value: 'getMany',
description: 'Retrieve many certificate requests',
action: 'Get many certificate requests',
},
],
default: 'create',
},
];
export const certificateRequestFields: INodeProperties[] = [
/* -------------------------------------------------------------------------- */
/* certificateRequest:create */
/* -------------------------------------------------------------------------- */
{
displayName: 'Application Name or ID',
name: 'applicationId',
type: 'options',
description:
'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
typeOptions: {
loadOptionsMethod: 'getApplications',
},
displayOptions: {
show: {
operation: ['create'],
resource: ['certificateRequest'],
},
},
default: '',
},
{
displayName: 'Certificate Issuing Template Name or ID',
name: 'certificateIssuingTemplateId',
type: 'options',
description:
'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
typeOptions: {
loadOptionsMethod: 'getCertificateIssuingTemplates',
loadOptionsDependsOn: ['applicationId'],
},
displayOptions: {
show: {
operation: ['create'],
resource: ['certificateRequest'],
},
},
default: '',
},
{
displayName: 'Generate CSR',
name: 'generateCsr',
type: 'boolean',
displayOptions: {
show: {
operation: ['create'],
resource: ['certificateRequest'],
},
},
default: false,
},
{
displayName: 'Common Name',
name: 'commonName',
required: true,
displayOptions: {
show: {
operation: ['create'],
resource: ['certificateRequest'],
generateCsr: [true],
},
},
type: 'string',
default: 'n8n.io',
description: 'The Common Name field for the certificate Subject (CN)',
},
// Optional...
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
operation: ['create'],
resource: ['certificateRequest'],
generateCsr: [true],
},
},
options: [
{
displayName: 'Key Type',
name: 'keyType',
type: 'options',
options: [
{
name: 'EC',
value: 'EC',
description: 'Elliptic Curve (EC)',
},
{
name: 'RSA',
value: 'RSA',
description: 'Rivest, Shamir, Adleman key (RSA)',
},
],
default: 'RSA',
description: 'The encryption algorithm for the public key',
},
{
displayName: 'Key Curve',
name: 'keyCurve',
type: 'options',
options: [
{
name: 'ED25519',
value: 'ED25519',
description: 'Use Edwards-curve Digital Signature Algorithm (EdDSA)',
},
{
name: 'P256',
value: 'P256',
description: 'Use Elliptic Prime Curve 256 bit encryption',
},
{
name: 'P384',
value: 'P384',
description: 'Use Elliptic Prime Curve 384 bit encryption',
},
{
name: 'P521',
value: 'P521',
description: 'Use Elliptic Prime Curve 521 bit encryption',
},
{
name: 'UNKNOWN',
value: 'UNKNOWN',
},
],
default: 'ED25519',
},
{
displayName: 'Key Length',
name: 'keyLength',
type: 'number',
default: 2048,
description: 'The number of bits to allow for key generation',
},
{
displayName: '(O) Organization',
name: 'organization',
type: 'string',
default: '',
description: 'The name of a company or organization',
},
{
displayName: '(OU) Organizational Unit(s)',
name: 'organizationalUnits',
type: 'string',
typeOptions: {
multipleValues: true,
},
default: '',
description: 'The name of a department or section',
},
{
displayName: '(L) City/Locality',
name: 'locality',
type: 'string',
default: '',
description: 'The name of a city or town',
},
{
displayName: '(ST) State',
name: 'state',
type: 'string',
default: '',
description: 'The name of a state or province',
},
{
displayName: '(C) Country',
name: 'country',
type: 'string',
default: '',
description: 'A 2 letter country code',
},
{
displayName: 'Subject Alt Names',
name: 'SubjectAltNamesUi',
placeholder: 'Add Subject',
type: 'fixedCollection',
default: {},
typeOptions: {
multipleValues: true,
},
options: [
{
name: 'SubjectAltNamesValues',
displayName: 'Subject Alt Name',
values: [
{
displayName: 'Typename',
name: 'Typename',
type: 'options',
options: [
{
name: 'DNS',
value: 'dnsNames',
},
/*{
name: 'IP Address',
value: 'ipAddresses',
},
{
name: 'RFC822 Names',
value: 'rfc822Names',
},
{
name: 'URI',
value: 'uniformResourceIdentifiers',
},*/
],
description: 'What type of SAN is being used',
default: 'dnsNames',
},
{
displayName: 'Name',
name: 'name',
type: 'string',
default: 'community.n8n.io',
description:
'The SAN friendly name that corresponds to the Type or TypeName parameter. For example, if a TypeName is IPAddress, the Name value is a valid IP address.',
},
],
},
],
},
],
},
// End CSR Builder
{
displayName: 'Certificate Signing Request',
name: 'certificateSigningRequest',
type: 'string',
displayOptions: {
show: {
operation: ['create'],
resource: ['certificateRequest'],
generateCsr: [false],
},
},
default: '',
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
operation: ['create'],
resource: ['certificateRequest'],
},
},
options: [
{
displayName: 'Validity Period',
name: 'validityPeriod',
type: 'string',
placeholder: 'P1Y',
default: 'P1Y',
description:
'Specify how long the issued certificate should be valid for. Use ISO8601 format.',
hint: 'e.g. 1 year -> P1Y',
},
],
},
/* -------------------------------------------------------------------------- */
/* certificateRequest:get */
/* -------------------------------------------------------------------------- */
{
displayName: 'Certificate Request ID',
name: 'certificateRequestId',
type: 'string',
required: true,
displayOptions: {
show: {
operation: ['get'],
resource: ['certificateRequest'],
},
},
default: '',
},
/* -------------------------------------------------------------------------- */
/* certificateRequest:getMany */
/* -------------------------------------------------------------------------- */
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
operation: ['getMany'],
resource: ['certificateRequest'],
},
},
default: false,
description: 'Whether to return all results or only up to a given limit',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
operation: ['getMany'],
resource: ['certificateRequest'],
returnAll: [false],
},
},
typeOptions: {
minValue: 1,
maxValue: 500,
},
default: 50,
description: 'Max number of results to return',
},
];
@@ -0,0 +1,143 @@
import * as nacl_factory from 'js-nacl';
import get from 'lodash/get';
import type {
IExecuteFunctions,
ILoadOptionsFunctions,
IDataObject,
IHookFunctions,
JsonObject,
IHttpRequestMethods,
IRequestOptions,
} from 'n8n-workflow';
import { NodeApiError } from 'n8n-workflow';
export async function venafiApiRequest(
this: IExecuteFunctions | ILoadOptionsFunctions | IHookFunctions,
method: IHttpRequestMethods,
resource: string,
body = {},
qs: IDataObject = {},
option: IDataObject = {},
): Promise<any> {
const operation = this.getNodeParameter('operation', 0);
const credentials = await this.getCredentials('venafiTlsProtectCloudApi');
const region = credentials.region ?? 'cloud';
const options: IRequestOptions = {
headers: {
Accept: 'application/json',
'content-type': 'application/json',
},
method,
body,
qs,
uri: `https://api.venafi.${region}${resource}`,
json: true,
};
if (Object.keys(option).length) {
Object.assign(options, option);
}
// For cert download we don't need any headers
// If we remove for everything the key fetch fails
if (operation === 'download') {
// We need content-type for keystore
if (!resource.endsWith('keystore')) {
delete options.headers!.Accept;
delete options.headers!['content-type'];
}
}
try {
if (Object.keys(body).length === 0) {
delete options.body;
}
return await this.helpers.requestWithAuthentication.call(
this,
'venafiTlsProtectCloudApi',
options,
);
} catch (error) {
throw new NodeApiError(this.getNode(), error as JsonObject);
}
}
export async function venafiApiRequestAllItems(
this: IExecuteFunctions | ILoadOptionsFunctions,
propertyName: string,
method: IHttpRequestMethods,
endpoint: string,
body: IDataObject = {},
query: IDataObject = {},
) {
const returnData: IDataObject[] = [];
let responseData;
do {
responseData = await venafiApiRequest.call(this, method, endpoint, body, query);
endpoint = get(responseData, '_links[0].Next');
returnData.push.apply(returnData, responseData[propertyName] as IDataObject[]);
} while (responseData._links?.[0].Next);
return returnData;
}
export async function encryptPassphrase(
this: IExecuteFunctions | ILoadOptionsFunctions,
certificateId: string,
passphrase: string,
storePassphrase: string,
) {
let dekHash = '';
const dekResponse = await venafiApiRequest.call(
this,
'GET',
`/outagedetection/v1/certificates/${certificateId}`,
);
if (dekResponse.dekHash) {
dekHash = dekResponse.dekHash;
}
let pubKey = '';
const pubKeyResponse = await venafiApiRequest.call(
this,
'GET',
`/v1/edgeencryptionkeys/${dekHash}`,
);
if (pubKeyResponse.key) {
pubKey = pubKeyResponse.key;
}
let encryptedKeyPass = '';
let encryptedKeyStorePass = '';
const promise = async () => {
return await new Promise((resolve, reject) => {
nacl_factory.instantiate((nacl: any) => {
try {
const passphraseUTF8 = nacl.encode_utf8(passphrase) as string;
const keyPassBuffer = nacl.crypto_box_seal(passphraseUTF8, Buffer.from(pubKey, 'base64'));
encryptedKeyPass = Buffer.from(keyPassBuffer as Buffer).toString('base64');
const storePassphraseUTF8 = nacl.encode_utf8(storePassphrase) as string;
const keyStorePassBuffer = nacl.crypto_box_seal(
storePassphraseUTF8,
Buffer.from(pubKey, 'base64'),
);
encryptedKeyStorePass = Buffer.from(keyStorePassBuffer as Buffer).toString('base64');
return resolve([encryptedKeyPass, encryptedKeyStorePass]);
} catch (error) {
return reject(error);
}
});
});
};
return await promise();
}
@@ -0,0 +1,18 @@
{
"node": "n8n-nodes-base.venafiTlsProtectCloud",
"nodeVersion": "1.0",
"codexVersion": "1.0",
"categories": ["Development"],
"resources": {
"credentialDocumentation": [
{
"url": "https://docs.n8n.io/integrations/builtin/credentials/venafitlsprotectcloud/"
}
],
"primaryDocumentation": [
{
"url": "https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.venafitlsprotectcloud/"
}
]
}
}
@@ -0,0 +1,469 @@
import {
type IExecuteFunctions,
type IDataObject,
type ILoadOptionsFunctions,
type INodeExecutionData,
type INodePropertyOptions,
type INodeType,
type INodeTypeDescription,
NodeConnectionTypes,
} from 'n8n-workflow';
import { certificateFields, certificateOperations } from './CertificateDescription';
import type {
ICertficateKeystoreRequest,
ICertficateRequest,
ICsrAttributes,
IKeyTypeParameters,
ISubjectAltNamesByType,
} from './CertificateInterface';
import {
certificateRequestFields,
certificateRequestOperations,
} from './CertificateRequestDescription';
import { encryptPassphrase, venafiApiRequest, venafiApiRequestAllItems } from './GenericFunctions';
export class VenafiTlsProtectCloud implements INodeType {
description: INodeTypeDescription = {
displayName: 'Venafi TLS Protect Cloud',
name: 'venafiTlsProtectCloud',
icon: 'file:../venafi.svg',
group: ['input'],
version: 1,
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
description: 'Consume Venafi TLS Protect Cloud API',
defaults: {
name: 'Venafi TLS Protect Cloud',
},
usableAsTool: true,
inputs: [NodeConnectionTypes.Main],
outputs: [NodeConnectionTypes.Main],
credentials: [
{
name: 'venafiTlsProtectCloudApi',
required: true,
},
],
properties: [
{
displayName: 'Resource',
name: 'resource',
noDataExpression: true,
type: 'options',
options: [
{
name: 'Certificate',
value: 'certificate',
},
{
name: 'Certificate Request',
value: 'certificateRequest',
},
],
default: 'certificateRequest',
},
...certificateOperations,
...certificateFields,
...certificateRequestOperations,
...certificateRequestFields,
],
};
methods = {
loadOptions: {
async getApplications(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
const returnData: INodePropertyOptions[] = [];
const { applications } = await venafiApiRequest.call(
this,
'GET',
'/outagedetection/v1/applications',
);
for (const application of applications) {
returnData.push({
name: application.name,
value: application.id,
});
}
return returnData;
},
async getCertificateIssuingTemplates(
this: ILoadOptionsFunctions,
): Promise<INodePropertyOptions[]> {
const returnData: INodePropertyOptions[] = [];
const currentApplication: string = this.getCurrentNodeParameter('applicationId') as string;
const { certificateIssuingTemplateAliasIdMap } = (await venafiApiRequest.call(
this,
'GET',
`/outagedetection/v1/applications/${currentApplication}`,
)) as { certificateIssuingTemplateAliasIdMap: { [key: string]: string } };
for (const [templateName, templateId] of Object.entries(
certificateIssuingTemplateAliasIdMap,
)) {
returnData.push({
name: templateName,
value: templateId,
});
}
return returnData;
},
},
};
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
const items = this.getInputData();
const returnData: IDataObject[] = [];
const length = items.length;
const qs: IDataObject = {};
let responseData;
const resource = this.getNodeParameter('resource', 0);
const operation = this.getNodeParameter('operation', 0);
for (let i = 0; i < length; i++) {
try {
if (resource === 'certificateRequest') {
//https://api.venafi.cloud/webjars/swagger-ui/index.html?configUrl=/v3/api-docs/swagger-config&urls.primaryName=outagedetection-service#//v1/certificaterequests_create
if (operation === 'create') {
const applicationId = this.getNodeParameter('applicationId', i) as string;
const certificateIssuingTemplateId = this.getNodeParameter(
'certificateIssuingTemplateId',
i,
) as string;
const options = this.getNodeParameter('options', i);
const generateCsr = this.getNodeParameter('generateCsr', i) as boolean;
const body: ICertficateRequest = {
applicationId,
certificateIssuingTemplateId,
};
if (generateCsr) {
const commonName = this.getNodeParameter('commonName', i) as string;
const additionalFields = this.getNodeParameter('additionalFields', i);
const keyTypeDetails: IKeyTypeParameters = {};
const csrAttributes: ICsrAttributes = {};
const subjectAltNamesByType: ISubjectAltNamesByType = {};
body.isVaaSGenerated = true;
csrAttributes.commonName = commonName;
// Csr Generation
if (additionalFields.organization) {
csrAttributes.organization = additionalFields.organization as string;
}
if (additionalFields.organizationalUnits) {
csrAttributes.organizationalUnits =
additionalFields.organizationalUnits as string[];
}
if (additionalFields.locality) {
csrAttributes.locality = additionalFields.locality as string;
}
if (additionalFields.state) {
csrAttributes.state = additionalFields.state as string;
}
if (additionalFields.country) {
csrAttributes.country = additionalFields.country as string;
}
body.csrAttributes = csrAttributes;
// Key type
if (additionalFields.keyType) {
keyTypeDetails.keyType = additionalFields.keyType as string;
}
if (additionalFields.keyCurve) {
keyTypeDetails.keyCurve = additionalFields.keyCurve as string;
}
if (additionalFields.keyLength) {
keyTypeDetails.keyLength = additionalFields.keyLength as number;
}
if (Object.keys(keyTypeDetails).length !== 0) {
body.csrAttributes.keyTypeParameters = keyTypeDetails;
}
// SAN
if (additionalFields.SubjectAltNamesUi) {
for (const key of (additionalFields.SubjectAltNamesUi as IDataObject)
.SubjectAltNamesValues as IDataObject[]) {
if (key.Typename === 'dnsNames') {
subjectAltNamesByType.dnsNames
? subjectAltNamesByType.dnsNames.push(key.name as string)
: (subjectAltNamesByType.dnsNames = [key.name as string]);
}
/*if (key.Typename === 'ipAddresses') {
subjectAltNamesByType.ipAddresses ? subjectAltNamesByType.ipAddresses.push(key.name as string) : subjectAltNamesByType.ipAddresses = [key.name as string];
}
if (key.Typename === 'rfc822Names') {
subjectAltNamesByType.rfc822Names ? subjectAltNamesByType.rfc822Names.push(key.name as string) : subjectAltNamesByType.rfc822Names = [key.name as string];
}
if (key.Typename === 'uniformResourceIdentifiers') {
subjectAltNamesByType.uniformResourceIdentifiers ? subjectAltNamesByType.uniformResourceIdentifiers.push(key.name as string) : subjectAltNamesByType.uniformResourceIdentifiers = [key.name as string];
}*/
}
}
if (Object.keys(subjectAltNamesByType).length !== 0) {
body.csrAttributes.subjectAlternativeNamesByType = subjectAltNamesByType;
}
} else {
const certificateSigningRequest = this.getNodeParameter(
'certificateSigningRequest',
i,
) as string;
body.isVaaSGenerated = false;
body.certificateSigningRequest = certificateSigningRequest;
}
Object.assign(body, options);
responseData = await venafiApiRequest.call(
this,
'POST',
'/outagedetection/v1/certificaterequests',
body,
qs,
);
responseData = responseData.certificateRequests;
}
//https://api.venafi.cloud/webjars/swagger-ui/index.html?configUrl=/v3/api-docs/swagger-config&urls.primaryName=outagedetection-service#//v1/certificaterequests_getById
if (operation === 'get') {
const certificateId = this.getNodeParameter('certificateRequestId', i) as string;
responseData = await venafiApiRequest.call(
this,
'GET',
`/outagedetection/v1/certificaterequests/${certificateId}`,
{},
qs,
);
}
//https://api.venafi.cloud/webjars/swagger-ui/index.html?configUrl=/v3/api-docs/swagger-config&urls.primaryName=outagedetection-service#//v1/certificaterequests_getAll
if (operation === 'getMany') {
const returnAll = this.getNodeParameter('returnAll', i);
if (returnAll) {
responseData = await venafiApiRequestAllItems.call(
this,
'certificateRequests',
'GET',
'/outagedetection/v1/certificaterequests',
{},
qs,
);
} else {
const limit = this.getNodeParameter('limit', i);
responseData = await venafiApiRequest.call(
this,
'GET',
'/outagedetection/v1/certificaterequests',
{},
qs,
);
responseData = responseData.certificateRequests.splice(0, limit);
}
}
}
if (resource === 'certificate') {
//https://api.venafi.cloud/webjars/swagger-ui/index.html?configUrl=%2Fv3%2Fapi-docs%2Fswagger-config&urls.primaryName=outagedetection-service#/%2Fv1/certificateretirement_deleteCertificates
if (operation === 'delete') {
const certificateId = this.getNodeParameter('certificateId', i) as string;
responseData = await venafiApiRequest.call(
this,
'POST',
'/outagedetection/v1/certificates/deletion',
{ certificateIds: [certificateId] },
);
responseData = responseData.certificates;
}
//https://api.venafi.cloud/webjars/swagger-ui/index.html?configUrl=%2Fv3%2Fapi-docs%2Fswagger-config&urls.primaryName=outagedetection-service#/
if (operation === 'download') {
const certificateId = this.getNodeParameter('certificateId', i) as string;
const binaryProperty = this.getNodeParameter('binaryProperty', i);
const downloadItem = this.getNodeParameter('downloadItem', i) as string;
const options = this.getNodeParameter('options', i);
// Cert Download
if (downloadItem === 'certificate') {
Object.assign(qs, options);
responseData = await venafiApiRequest.call(
this,
'GET',
`/outagedetection/v1/certificates/${certificateId}/contents`,
{},
qs,
{ encoding: null, json: false, resolveWithFullResponse: true, cert: true },
);
} else {
const exportFormat = this.getNodeParameter('keystoreType', i) as string;
const body: ICertficateKeystoreRequest = {
exportFormat,
};
const privateKeyPassphrase = this.getNodeParameter(
'privateKeyPassphrase',
i,
) as string;
const certificateLabel = this.getNodeParameter('certificateLabel', i) as string;
body.certificateLabel = certificateLabel;
let keystorePassphrase = '';
if (exportFormat === 'JKS') {
keystorePassphrase = this.getNodeParameter('keystorePassphrase', i) as string;
}
const encryptedValues = (await encryptPassphrase.call(
this,
certificateId,
privateKeyPassphrase,
keystorePassphrase,
)) as string;
body.encryptedPrivateKeyPassphrase = encryptedValues[0];
if (exportFormat === 'JKS') {
body.encryptedKeystorePassphrase = encryptedValues[1];
}
responseData = await venafiApiRequest.call(
this,
'POST',
`/outagedetection/v1/certificates/${certificateId}/keystore`,
body,
{},
{ encoding: null, json: false, resolveWithFullResponse: true },
);
}
const contentDisposition: string = responseData.headers['content-disposition'];
const fileNameRegex = /(?<=filename=").*\b/;
const match = fileNameRegex.exec(contentDisposition);
let fileName = '';
if (match !== null) {
fileName = match[0];
}
const binaryData = await this.helpers.prepareBinaryData(
Buffer.from(responseData.body as Buffer),
fileName,
);
responseData = {
json: {},
binary: {
[binaryProperty]: binaryData,
},
};
}
//https://api.venafi.cloud/webjars/swagger-ui/index.html?configUrl=%2Fv3%2Fapi-docs%2Fswagger-config&urls.primaryName=outagedetection-service#/%2Fv1/certificates_getById
if (operation === 'get') {
const certificateId = this.getNodeParameter('certificateId', i) as string;
responseData = await venafiApiRequest.call(
this,
'GET',
`/outagedetection/v1/certificates/${certificateId}`,
{},
qs,
);
}
//https://api.venafi.cloud/webjars/swagger-ui/index.html?configUrl=%2Fv3%2Fapi-docs%2Fswagger-config&urls.primaryName=outagedetection-service#/%2Fv1/certificates_getAllAsCsv
if (operation === 'getMany') {
const returnAll = this.getNodeParameter('returnAll', i);
const filters = this.getNodeParameter('filters', i);
Object.assign(qs, filters);
if (returnAll) {
responseData = await venafiApiRequestAllItems.call(
this,
'certificates',
'GET',
'/outagedetection/v1/certificates',
{},
qs,
);
} else {
qs.limit = this.getNodeParameter('limit', i);
responseData = await venafiApiRequest.call(
this,
'GET',
'/outagedetection/v1/certificates',
{},
qs,
);
responseData = responseData.certificates;
}
}
//https://docs.venafi.cloud/api/t-cloud-api-renew-cert/
if (operation === 'renew') {
const applicationId = this.getNodeParameter('applicationId', i) as string;
const certificateIssuingTemplateId = this.getNodeParameter(
'certificateIssuingTemplateId',
i,
) as string;
const certificateSigningRequest = this.getNodeParameter(
'certificateSigningRequest',
i,
) as string;
const existingCertificateId = this.getNodeParameter(
'existingCertificateId',
i,
) as string;
const options = this.getNodeParameter('options', i);
const body: IDataObject = {
certificateSigningRequest,
certificateIssuingTemplateId,
applicationId,
existingCertificateId,
};
Object.assign(body, options);
responseData = await venafiApiRequest.call(
this,
'POST',
'/outagedetection/v1/certificaterequests',
body,
qs,
);
responseData = responseData.certificateRequests;
}
}
returnData.push(
...this.helpers.constructExecutionMetaData(
this.helpers.returnJsonArray(responseData as IDataObject[]),
{
itemData: { item: i },
},
),
);
} catch (error) {
if (this.continueOnFail()) {
returnData.push({ json: { error: error.message } });
continue;
}
throw error;
}
}
return [returnData as INodeExecutionData[]];
}
}
@@ -0,0 +1,18 @@
{
"node": "n8n-nodes-base.venafiTlsProtectCloudTrigger",
"nodeVersion": "1.0",
"codexVersion": "1.0",
"categories": ["Development"],
"resources": {
"credentialDocumentation": [
{
"url": "https://docs.n8n.io/integrations/builtin/credentials/venafitlsprotectcloud/"
}
],
"primaryDocumentation": [
{
"url": "https://docs.n8n.io/integrations/builtin/trigger-nodes/n8n-nodes-base.venafitlsprotectcloudtrigger/"
}
]
}
}
@@ -0,0 +1,195 @@
import {
NodeConnectionTypes,
type IHookFunctions,
type ILoadOptionsFunctions,
type INodePropertyOptions,
type INodeType,
type INodeTypeDescription,
type IWebhookFunctions,
type IWebhookResponseData,
} from 'n8n-workflow';
import { venafiApiRequest } from './GenericFunctions';
export class VenafiTlsProtectCloudTrigger implements INodeType {
description: INodeTypeDescription = {
displayName: 'Venafi TLS Protect Cloud Trigger',
name: 'venafiTlsProtectCloudTrigger',
icon: 'file:../venafi.svg',
group: ['trigger'],
version: 1,
description: 'Starts the workflow when Venafi events occur',
defaults: {
name: 'Venafi TLS Protect Cloud Trigger',
},
credentials: [
{
name: 'venafiTlsProtectCloudApi',
required: true,
},
],
webhooks: [
{
name: 'default',
httpMethod: 'POST',
responseMode: 'onReceived',
path: 'webhook',
},
],
inputs: [],
outputs: [NodeConnectionTypes.Main],
properties: [
{
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-wrong-for-dynamic-options
displayName: 'Resource',
name: 'resource',
type: 'options',
noDataExpression: true,
typeOptions: {
loadOptionsMethod: 'getActivityTypes',
},
required: true,
default: [],
description:
'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>. Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-wrong-for-dynamic-multi-options
displayName: 'Trigger On',
name: 'triggerOn',
type: 'multiOptions',
typeOptions: {
loadOptionsMethod: 'getActivitySubTypes',
loadOptionsDependsOn: ['resource'],
},
required: true,
default: [],
description:
'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>. Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>. Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
],
};
methods = {
loadOptions: {
async getActivityTypes(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
const activitytypes = await venafiApiRequest.call(this, 'GET', '/v1/activitytypes');
return activitytypes.map(
({ key, readableName }: { key: string; readableName: string }) => ({
name: readableName,
value: key,
}),
);
},
async getActivitySubTypes(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
const resource = this.getCurrentNodeParameter('resource') as string;
const activitytypes = await venafiApiRequest.call(this, 'GET', '/v1/activitytypes');
const activity = activitytypes.find(({ key }: { key: string }) => key === resource) as {
values: [{ key: string; readableName: string }];
};
const subActivities = activity.values.map(({ key, readableName }) => ({
name: readableName,
value: key,
}));
subActivities.unshift({ name: '[All]', value: '*' });
return subActivities;
},
},
};
webhookMethods = {
default: {
async checkExists(this: IHookFunctions): Promise<boolean> {
const webhookUrl = this.getNodeWebhookUrl('default');
const { connectors } = await venafiApiRequest.call(this, 'GET', '/v1/connectors');
for (const connector of connectors) {
const {
id,
status,
properties: {
target: {
connection: { url },
},
},
} = connector;
if (url === webhookUrl && status === 'Active') {
await venafiApiRequest.call(this, 'DELETE', `/v1/connectors/${id}`);
return false;
}
}
return false;
},
async create(this: IHookFunctions): Promise<boolean> {
const webhookUrl = this.getNodeWebhookUrl('default');
const resource = this.getNodeParameter('resource') as string;
const body = {
name: `n8n-webhook (${webhookUrl})`,
properties: {
connectorKind: 'WEBHOOK',
target: {
type: 'generic',
connection: {
url: webhookUrl,
},
},
filter: {
activityTypes: [resource],
},
},
};
const responseData = await venafiApiRequest.call(this, 'POST', '/v1/connectors', body);
if (responseData.id === undefined) {
// Required data is missing so was not successful
return false;
}
const webhookData = this.getWorkflowStaticData('node');
webhookData.webhookId = responseData.id as string;
return true;
},
async delete(this: IHookFunctions): Promise<boolean> {
const webhookData = this.getWorkflowStaticData('node');
if (webhookData.webhookId !== undefined) {
try {
await venafiApiRequest.call(this, 'DELETE', `/v1/connectors/${webhookData.webhookId}`);
} catch (error) {
return false;
}
// Remove from the static workflow data so that it is clear
// that no webhooks are registered anymore
delete webhookData.webhookId;
}
return true;
},
},
};
async webhook(this: IWebhookFunctions): Promise<IWebhookResponseData> {
const { events } = this.getBodyData() as { events: [{ message: string; eventName: string }] };
const triggerOn = this.getNodeParameter('triggerOn') as string;
if (Array.isArray(events) && events[0]?.message?.includes('TESTING CONNECTION...')) {
// Is a create webhook confirmation request
const res = this.getResponseObject();
res.status(200).end();
return {
noWebhookResponse: true,
};
}
if (!triggerOn.includes('*') && !triggerOn.includes(events[0]?.eventName)) return {};
return {
workflowData: [this.helpers.returnJsonArray(events)],
};
}
}