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,801 @@
/* eslint-disable n8n-nodes-base/node-param-description-boolean-without-whether */
import type { INodeProperties } from 'n8n-workflow';
export const certificateOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['certificate'],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Provision a new certificate',
action: 'Create a certificate',
},
{
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: 'create',
},
];
export const certificateFields: INodeProperties[] = [
/* -------------------------------------------------------------------------- */
/* certificate:create */
/* -------------------------------------------------------------------------- */
{
displayName: 'Policy DN',
name: 'PolicyDN',
type: 'string',
displayOptions: {
show: {
operation: ['create'],
resource: ['certificate'],
},
},
default: '',
description:
'The folder DN for the new certificate. If the value is missing, the folder name is the system default. If no system default is configured',
},
{
displayName: 'Subject',
name: 'Subject',
type: 'string',
displayOptions: {
show: {
operation: ['create'],
resource: ['certificate'],
},
},
default: '',
description: 'The Common Name field for the certificate Subject (DN)',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
operation: ['create'],
resource: ['certificate'],
},
},
options: [
{
displayName: 'Approvers',
name: 'Approvers',
type: 'string',
typeOptions: {
multipleValues: true,
},
default: '',
description: 'An array of one or more identities for certificate workflow approvers',
},
{
displayName: 'CADN',
name: 'CADN',
type: 'string',
default: '',
description:
'Only required when no policy sets a CA template. The Distinguished Name (DN) of the Trust Protection Platform Certificate Authority Template object for enrolling the certificate.',
},
{
displayName: 'Certificate Type',
name: 'CertificateType',
type: 'options',
options: [
{
name: 'Code Signing',
value: 'Code Signing',
description: 'X.509 Code Signing Certificate',
},
{
name: 'Device',
value: 'Device',
description: 'X.509 Device Certificate',
},
{
name: 'Server',
value: 'Server',
description: 'X.509 Server Certificate',
},
{
name: 'User',
value: 'User',
description: 'X.509 User Certificate',
},
],
default: '',
description: 'One of the following Certificate objects. Ignores any other value.',
},
{
displayName: 'City',
name: 'City',
type: 'string',
default: '',
description:
'The City field for the certificate Subject DN. Specify a value when requesting a centrally generated CSR.',
},
{
displayName: 'Contacts',
name: 'Contacts',
type: 'string',
typeOptions: {
multipleValues: true,
},
default: [],
description:
'An array of one or more identities for users or groups who receive notifications about events pertaining to the object',
},
{
displayName: 'Country',
name: 'Country',
type: 'string',
default: '',
description:
'The Country field for the certificate Subject DN. Specify a value when requesting a centrally generated CSR.',
},
{
displayName: 'Custom Fields',
name: 'customFieldsUi',
placeholder: 'Custom Fields',
type: 'fixedCollection',
default: {},
typeOptions: {
multipleValues: false,
},
options: [
{
name: 'customFielsValues',
displayName: 'Address',
values: [
{
displayName: 'Name',
name: 'Name',
type: 'string',
default: '',
},
{
displayName: 'Values',
name: 'Values',
type: 'string',
default: '',
},
],
},
],
},
{
displayName: 'Created By',
name: 'CreatedBy',
type: 'string',
default: 'Web SDK',
description:
'The person, entity, or caller of this request. The default is Web SDK. Avoid overriding the default unless the caller is a significant enterprise application that is tightly integrated with Trust Protection Platform, such as a custom web portal. To add details, use Origin instead. If you want both attributes to have the same value, set only CreatedBy.',
},
{
displayName: 'Devices',
name: 'Devices',
type: 'collection',
placeholder: 'Add Field',
typeOptions: {
multipleValues: true,
multipleValueButtonText: 'Add Device',
},
default: {},
options: [
{
displayName: 'Applications',
name: 'applications',
type: 'string',
default: '',
description:
'An array of one or more Application objects to allow software, which runs on ObjectName, to use the same certificate',
},
{
displayName: 'Cloud Instance ID',
name: 'CloudInstanceID',
type: 'string',
default: '',
description: 'Required for Amazon EC2 provisioning. The unique cloud instance ID.',
},
{
displayName: 'Cloud Region',
name: 'CloudRegion',
type: 'string',
default: '',
description:
'Required for Amazon EC2 provisioning. The geographic location where the cloud service instance resides. An instance in AWS can only exist in a single region.',
},
{
displayName: 'Cloud Service',
name: 'CloudService',
type: 'string',
default: '',
description:
'Required for Amazon EC2 provisioning. AWS: An Amazon E2C cloud service. Requires you to install and configure the Cloud Instance Monitoring feature.',
},
{
displayName: 'Concurrent Connection Limit',
name: 'ConcurrentConnectionLimit',
type: 'number',
default: 0,
description:
'Maximum number of connections the device will accept from Trust Protection Platform',
},
{
displayName: 'Contacts',
name: 'Contacts',
type: 'string',
typeOptions: {
multipleValues: true,
},
default: [],
description:
'An array of one or more identities who receive notifications for this device',
},
{
displayName: 'Created By',
name: 'CreatedBy',
type: 'string',
default: 'Web SDK',
description:
'The person or entity that is creating the device and any associated software applications for the device. Any value is accepted. Default is Web SDK.',
},
{
displayName: 'Credential DN',
name: 'CredentialDN',
type: 'string',
default: '',
description: 'The device credential',
},
{
displayName: 'Description',
name: 'description',
type: 'string',
default: '',
description: 'The description for this device',
},
{
displayName: 'Enforce Known Host',
name: 'EnforceKnownHost',
type: 'boolean',
default: true,
description:
'For SSH keys. true: Enable known host key enforcement. false: Disable known host key enforcement.',
},
{
displayName: 'Host',
name: 'host',
type: 'string',
default: '',
description:
'The physical Fully Qualified Domain Name (FQDN) for the host or the IP address for a device',
},
{
displayName: 'Object Name',
name: 'ObjectName',
type: 'string',
default: '',
description:
'The device host name or IP address for the certificate object in Trust Protection Platform. If the value is missing, the object name is the Subject.',
},
{
displayName: 'Port',
name: 'port',
type: 'number',
default: 0,
description: 'The port number to communicate with the device',
},
{
displayName: 'Sudo Credential DN',
name: 'SudoCredentialDN',
type: 'number',
default: 0,
description:
'Use in conjunction with UseSudo. The DN that holds the password credential to be used if sudo is configured to prompt for a password when executing a command.',
},
{
displayName: 'Temp Directory',
name: 'TempDirectory',
type: 'string',
default: '',
description:
'The host directory path to hold temporary files during provisioning. For example /tmp/. The folder should have the necessary write permissions.',
},
{
displayName: 'Trusted Fingerprint',
name: 'TrustedFingerprint',
type: 'string',
default: '',
description:
'For Secure Shell (SSH) keys. The SSH server key fingerprint. If this value is set, and EnforceKnownHost is enabled, Trust Protection Platform will only successfully connect to the device if the hosts fingerprint matches this value.',
},
{
displayName: 'Use Sudo',
name: 'UseSudo',
type: 'boolean',
default: false,
description:
'Use in conjunction with SudoCredentialDN. For cases where the device credentials require sudo privilege elevation to execute commands when installing the certificate on a Unix or Linux device: true: Execute commands using sudo when provisioning. false: Execute commands directly without using sudo.',
},
],
},
{
displayName: 'Disable Automatic Renewal',
name: 'DisableAutomaticRenewal',
type: 'boolean',
default: false,
description:
'The setting to control whether manual intervention is required for certificate renewal',
},
{
displayName: 'Elliptic Curve',
name: 'EllipticCurve',
type: 'options',
options: [
{
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. (not supported by all Certificate Authorities).',
},
],
default: '',
description:
'For Elliptic Curve Cryptography (ECC), use this parameter in conjunction with KeyAlgorithm',
},
{
displayName: 'Key Algorithm',
name: 'KeyAlgorithm',
type: 'options',
options: [
{
name: 'RSA',
value: 'RSA',
description: 'Rivest, Shamir, Adleman key (RSA)',
},
{
name: 'ECC',
value: 'ECC',
description: 'Elliptic Curve Cryptography (ECC)',
},
],
default: '',
description: 'The encryption algorithm for the public ke:',
},
{
displayName: 'Key Bit Size',
name: 'KeyBitSize',
type: 'number',
default: 2048,
description:
'Use this parameter when KeyAlgorithm is RSA. The number of bits to allow for key generation.',
},
{
displayName: 'Management Type',
name: 'ManagementType',
type: 'options',
options: [
{
name: 'Enrollment',
value: 'Enrollment',
description:
'Issue a new certificate, renewcertificate, or key generation request to a CA for enrollment',
},
{
name: 'Monitoring',
value: 'Monitoring',
description:
'Allow Trust Protection Platform to monitor the certificate for expiration and renewal',
},
{
name: 'Provisioning',
value: 'Provisioning',
description:
'Issue a new certificate, renew a certificate, or send a key generation request to a CA for enrollment. Automatically install or provision the certificate.',
},
{
name: 'Unassigned',
value: 'Unassigned',
description:
'Certificates are neither enrolled or monitored by Trust Protection Platform',
},
],
default: '',
description:
'The level of management that Trust Protection Platform applies to the certificate',
},
{
displayName: 'Origin',
name: 'origin',
type: 'string',
typeOptions: {
multipleValues: true,
},
default: 'Web SDK',
description:
'Additional information, such as the name and version of the calling application, that describes the source of this enrollment, renewal, or provisioning request. The default is Web SDK.',
},
{
displayName: 'Organization',
name: 'Organization',
type: 'string',
default: '',
description:
'The Organization field for the certificate Subject DN. Specify a value when the CSR centrally generates.',
},
{
displayName: 'Organizational Unit',
name: 'OrganizationalUnit',
type: 'string',
default: '',
description:
'The department or division within the organization that is responsible for maintaining the certificate',
},
{
displayName: 'PKCS10',
name: 'PKCS10',
type: 'string',
default: '',
description:
'The PKCS#10 Certificate Signing Request (CSR). Omit escape characters such as or . If this value is provided, any Subject DN fields and the KeyBitSize in the request are ignored.',
},
{
displayName: 'Reenable',
name: 'Reenable',
type: 'boolean',
default: false,
description: 'The action to control a previously disabled certificate',
},
{
displayName: 'Set Work To Do',
name: 'SetWorkToDo',
type: 'boolean',
default: false,
description: 'The setting to control certificate processing',
},
{
displayName: 'State',
name: 'State',
type: 'string',
default: '',
description:
'The State field for the certificate Subject DN. Specify a value when requesting a centrally generated CSR.',
},
{
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: 'OtherName',
value: 0,
description: 'Specify a Uniform Resource Name (URN) or username',
},
{
name: 'Email',
value: 1,
},
{
name: 'DNS',
value: 2,
},
{
name: 'URI',
value: 6,
},
{
name: 'IP Address',
value: 7,
},
],
description: 'An integer that represents the kind of SAN',
default: '',
},
{
displayName: 'Name',
name: 'name',
type: 'string',
default: '',
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.',
},
],
},
],
},
],
},
/* -------------------------------------------------------------------------- */
/* certificate:download */
/* -------------------------------------------------------------------------- */
{
displayName: 'Certificate DN',
name: 'certificateDn',
type: 'string',
required: true,
displayOptions: {
show: {
operation: ['download'],
resource: ['certificate'],
},
},
default: '',
},
{
displayName: 'Include Private Key',
name: 'includePrivateKey',
type: 'boolean',
displayOptions: {
show: {
resource: ['certificate'],
operation: ['download'],
},
},
default: false,
},
{
displayName: 'Password',
name: 'password',
type: 'string',
typeOptions: { password: true },
required: true,
displayOptions: {
show: {
resource: ['certificate'],
operation: ['download'],
includePrivateKey: [true],
},
},
default: '',
},
{
displayName: 'Input Data Field Name',
name: 'binaryProperty',
type: 'string',
required: true,
default: 'data',
displayOptions: {
show: {
operation: ['download'],
resource: ['certificate'],
},
},
description: 'The name of the input field containing the binary file data to be uploaded',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
operation: ['download'],
resource: ['certificate'],
},
},
options: [
{
displayName: 'Include Chain',
name: 'IncludeChain',
type: 'boolean',
default: true,
},
{
displayName: 'Root First Order',
name: 'RootFirstOrder',
type: 'string',
default: '',
},
{
displayName: 'Keystore Password',
name: 'KeystorePassword',
type: 'string',
typeOptions: { password: true },
default: '',
},
],
},
/* -------------------------------------------------------------------------- */
/* certificate:get */
/* -------------------------------------------------------------------------- */
{
displayName: 'Certificate GUID',
name: 'certificateId',
type: 'string',
required: true,
displayOptions: {
show: {
operation: ['get', 'delete'],
resource: ['certificate'],
},
},
default: '',
description: 'A GUID that uniquely identifies the certificate',
},
/* -------------------------------------------------------------------------- */
/* 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: 100,
description: 'Max number of results to return',
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add option',
default: {},
displayOptions: {
show: {
operation: ['getMany'],
resource: ['certificate'],
},
},
options: [
{
displayName: 'Fields',
name: 'fields',
type: 'multiOptions',
options: [
{
name: 'Issuer',
value: 'Issuer',
},
{
name: 'KeyAlgorithm',
value: 'KeyAlgorithm',
},
{
name: 'KeySize',
value: 'KeySize',
},
{
name: 'Subject',
value: 'Subject',
},
],
default: [],
description:
'Include one or more of the following certificate attributes in the return value',
},
],
},
/* -------------------------------------------------------------------------- */
/* certificate:renew */
/* -------------------------------------------------------------------------- */
{
displayName: 'Certificate DN',
name: 'certificateDN',
type: 'string',
required: true,
displayOptions: {
show: {
operation: ['renew'],
resource: ['certificate'],
},
},
default: '',
description: 'The Distinguished Name (DN) of the certificate to renew',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
operation: ['renew'],
resource: ['certificate'],
},
},
options: [
{
displayName: 'PKCS10',
name: 'PKCS10',
type: 'string',
default: '',
description:
'The PKCS#10 Certificate Signing Request (CSR). Omit escape characters such as or . If this value is provided, any Subject DN fields and the KeyBitSize in the request are ignored.',
},
{
displayName: 'Reenable',
name: 'Reenable',
type: 'boolean',
default: false,
description: 'The action to control a previously disabled certificate',
},
],
},
];
@@ -0,0 +1,81 @@
import get from 'lodash/get';
import { ApplicationError } from '@n8n/errors';
import type {
IDataObject,
IExecuteFunctions,
IHttpRequestMethods,
ILoadOptionsFunctions,
IPollFunctions,
IRequestOptions,
} from 'n8n-workflow';
export async function venafiApiRequest(
this: IExecuteFunctions | ILoadOptionsFunctions | IPollFunctions,
method: IHttpRequestMethods,
resource: string,
body: IDataObject = {},
qs: IDataObject = {},
uri?: string,
headers: IDataObject = {},
): Promise<any> {
const credentials = await this.getCredentials('venafiTlsProtectDatacenterApi');
const options: IRequestOptions = {
headers: {
'Content-Type': 'application/json',
},
method,
body,
qs,
rejectUnauthorized: !credentials.allowUnauthorizedCerts,
uri: uri || `${credentials.domain}${resource}`,
json: true,
};
try {
if (Object.keys(headers).length !== 0) {
options.headers = Object.assign({}, options.headers, headers);
}
if (Object.keys(body).length === 0) {
delete options.body;
}
return await this.helpers.requestWithAuthentication.call(
this,
'venafiTlsProtectDatacenterApi',
options,
);
} catch (error) {
if (error.response?.body?.error) {
let errors = error.response.body.error.errors;
errors = errors.map((e: IDataObject) => e.message);
// Try to return the error prettier
throw new ApplicationError(
`Venafi error response [${error.statusCode}]: ${errors.join('|')}`,
{ level: 'warning' },
);
}
throw error;
}
}
export async function venafiApiRequestAllItems(
this: IExecuteFunctions | ILoadOptionsFunctions,
propertyName: string,
method: IHttpRequestMethods,
endpoint: string,
body: IDataObject = {},
query: IDataObject = {},
): Promise<any> {
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;
}
@@ -0,0 +1,67 @@
import type { INodeProperties } from 'n8n-workflow';
export const policyOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['policy'],
},
},
options: [
{
name: 'Get',
value: 'get',
description: 'Get a policy',
action: 'Get a policy',
},
],
default: 'get',
},
];
export const policyFields: INodeProperties[] = [
/* -------------------------------------------------------------------------- */
/* policy:get */
/* -------------------------------------------------------------------------- */
{
displayName: 'Policy DN',
name: 'policyDn',
type: 'string',
required: true,
displayOptions: {
show: {
operation: ['get'],
resource: ['policy'],
},
},
default: '',
description: 'The Distinguished Name (DN) of the policy folder',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
operation: ['get'],
resource: ['policy'],
},
},
options: [
{
displayName: 'PKCS10',
name: 'PKCS10',
type: 'string',
default: '',
description:
'The PKCS#10 policy Signing Request (CSR). Omit escape characters such as or . If this value is provided, any Subject DN fields and the KeyBitSize in the request are ignored.',
},
],
},
];
@@ -0,0 +1,18 @@
{
"node": "n8n-nodes-base.venafiTlsProtectDatacenter",
"nodeVersion": "1.0",
"codexVersion": "1.0",
"categories": ["Development"],
"resources": {
"credentialDocumentation": [
{
"url": "https://docs.n8n.io/integrations/builtin/credentials/venafitlsprotectdatacenter/"
}
],
"primaryDocumentation": [
{
"url": "https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.venafitlsprotectdatacenter/"
}
]
}
}
@@ -0,0 +1,258 @@
import type {
IExecuteFunctions,
IDataObject,
INodeExecutionData,
INodeType,
INodeTypeDescription,
} from 'n8n-workflow';
import { NodeConnectionTypes } from 'n8n-workflow';
import { certificateFields, certificateOperations } from './CertificateDescription';
import { venafiApiRequest, venafiApiRequestAllItems } from './GenericFunctions';
import { policyFields, policyOperations } from './PolicyDescription';
export class VenafiTlsProtectDatacenter implements INodeType {
description: INodeTypeDescription = {
displayName: 'Venafi TLS Protect Datacenter',
name: 'venafiTlsProtectDatacenter',
icon: 'file:../venafi.svg',
group: ['input'],
version: 1,
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
description: 'Consume Venafi TLS Protect Datacenter',
defaults: {
name: 'Venafi TLS Protect Datacenter',
},
usableAsTool: true,
inputs: [NodeConnectionTypes.Main],
outputs: [NodeConnectionTypes.Main],
credentials: [
{
name: 'venafiTlsProtectDatacenterApi',
required: true,
},
],
properties: [
{
displayName: 'Resource',
name: 'resource',
type: 'options',
noDataExpression: true,
options: [
{
name: 'Certificate',
value: 'certificate',
},
{
name: 'Policy',
value: 'policy',
},
],
default: 'certificate',
},
...certificateOperations,
...certificateFields,
...policyOperations,
...policyFields,
],
};
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 === 'certificate') {
if (operation === 'create') {
const policyDN = this.getNodeParameter('PolicyDN', i) as string;
const subject = this.getNodeParameter('Subject', i) as string;
const additionalFields = this.getNodeParameter('additionalFields', i);
const body: IDataObject = {
PolicyDN: policyDN,
Subject: subject,
};
Object.assign(body, additionalFields);
if (body.SubjectAltNamesUi) {
body.SubjectAltNames = (body.SubjectAltNamesUi as IDataObject).SubjectAltNamesValues;
delete body.SubjectAltNamesUi;
}
responseData = await venafiApiRequest.call(
this,
'POST',
'/vedsdk/Certificates/Request',
body,
qs,
);
}
if (operation === 'delete') {
const certificateId = this.getNodeParameter('certificateId', i) as string;
responseData = await venafiApiRequest.call(
this,
'DELETE',
`/vedsdk/Certificates/${certificateId}`,
{},
qs,
);
}
if (operation === 'download') {
const certificateDn = this.getNodeParameter('certificateDn', i) as string;
const includePrivateKey = this.getNodeParameter('includePrivateKey', i) as boolean;
const binaryProperty = this.getNodeParameter('binaryProperty', i);
const additionalFields = this.getNodeParameter('additionalFields', i);
const body: IDataObject = {
CertificateDN: certificateDn,
Format: 'Base64',
IncludeChain: true,
};
if (includePrivateKey) {
const password = this.getNodeParameter('password', i) as string;
body.IncludePrivateKey = true;
body.Password = password;
}
Object.assign(body, additionalFields);
responseData = await venafiApiRequest.call(
this,
'POST',
'/vedsdk/Certificates/Retrieve',
body,
);
const binaryData = await this.helpers.prepareBinaryData(
Buffer.from(responseData.CertificateData as BufferEncoding, 'base64'),
responseData.Filename as string,
);
responseData = {
json: {},
binary: {
[binaryProperty]: binaryData,
},
};
}
if (operation === 'get') {
const certificateId = this.getNodeParameter('certificateId', i) as string;
responseData = await venafiApiRequest.call(
this,
'GET',
`/vedsdk/Certificates/${certificateId}`,
{},
qs,
);
}
if (operation === 'getMany') {
const returnAll = this.getNodeParameter('returnAll', i);
const options = this.getNodeParameter('options', i);
if (options.fields) {
qs.OptionalFields = (options.fields as string[]).join(',');
}
if (returnAll) {
responseData = await venafiApiRequestAllItems.call(
this,
'Certificates',
'GET',
'/vedsdk/Certificates',
{},
qs,
);
} else {
qs.Limit = this.getNodeParameter('limit', i);
responseData = await venafiApiRequest.call(
this,
'GET',
'/vedsdk/Certificates',
{},
qs,
);
responseData = responseData.Certificates;
}
}
if (operation === 'renew') {
const certificateDN = this.getNodeParameter('certificateDN', i) as string;
const additionalFields = this.getNodeParameter('additionalFields', i);
const body: IDataObject = {
CertificateDN: certificateDN,
};
Object.assign(body, additionalFields);
responseData = await venafiApiRequest.call(
this,
'POST',
'/vedsdk/Certificates/Renew',
{},
qs,
);
}
}
if (resource === 'policy') {
if (operation === 'get') {
const policy = this.getNodeParameter('policyDn', i) as string;
const additionalFields = this.getNodeParameter('additionalFields', i);
const body: IDataObject = {
PolicyDN: policy,
};
Object.assign(body, additionalFields);
responseData = await venafiApiRequest.call(
this,
'POST',
'/vedsdk/Certificates/CheckPolicy',
body,
qs,
);
}
}
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,78 @@
import moment from 'moment-timezone';
import {
type IPollFunctions,
type IDataObject,
type INodeExecutionData,
type INodeType,
type INodeTypeDescription,
NodeConnectionTypes,
} from 'n8n-workflow';
import { venafiApiRequest } from './GenericFunctions';
export class VenafiTlsProtectDatacenterTrigger implements INodeType {
description: INodeTypeDescription = {
displayName: 'Venafi TLS Protect Datacenter Trigger',
name: 'venafiTlsProtectDatacenterTrigger',
icon: 'file:../venafi.svg',
group: ['trigger'],
version: 1,
subtitle: '={{$parameter["triggerOn"]}}',
description: 'Starts the workflow when Venafi events occur',
defaults: {
name: 'Venafi TLS Protect Datacenter',
},
credentials: [
{
name: 'venafiTlsProtectDatacenterApi',
required: true,
},
],
polling: true,
inputs: [],
outputs: [NodeConnectionTypes.Main],
properties: [
{
displayName: 'Trigger On',
name: 'triggerOn',
type: 'options',
options: [
{
name: 'Certificate Expired',
value: 'certificateExpired',
},
],
required: true,
default: 'certificateExpired',
},
],
};
async poll(this: IPollFunctions): Promise<INodeExecutionData[][] | null> {
const webhookData = this.getWorkflowStaticData('node');
const qs: IDataObject = {};
const now = moment().format();
qs.ValidToGreater = webhookData.lastTimeChecked || now;
qs.ValidToLess = now;
const { Certificates: certificates } = await venafiApiRequest.call(
this,
'GET',
'/vedsdk/certificates',
{},
qs,
);
webhookData.lastTimeChecked = qs.ValidToLess;
if (Array.isArray(certificates) && certificates.length !== 0) {
return [this.helpers.returnJsonArray(certificates)];
}
return null;
}
}
@@ -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)],
};
}
}
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="288" height="288"><svg xmlns="http://www.w3.org/2000/svg" width="288" height="288" fill="currentColor" class="animicon" viewBox="0 0 58 49"><path fill="#ff6333" d="M15.339 9.94A69.8 69.8 0 0 0 27.2 30.914l1.738 2.062 1.759-2.062A70.1 70.1 0 0 0 42.6 9.94H36.7A58.9 58.9 0 0 1 29 23.758 58 58 0 0 1 21.32 9.94z" class="color000 svgShape"/><path fill="#ff6333" d="M39.67.062c-.141.35-1.475 4.372-2.182 6.001h11.337A101.9 101.9 0 0 1 31.607 36.38l-2.526 3.052-2.526-3.052A101.9 101.9 0 0 1 9.337 6h10.791C19.421 4.372 18.108.351 17.946 0H0c5.836 16.228 14.398 31.295 25.302 44.525L29 49l3.718-4.475C43.613 31.29 52.167 16.225 58 0z" class="color000 svgShape"/></svg></svg>

After

Width:  |  Height:  |  Size: 716 B