feat(Venafi TLS Protect Cloud): add Venafi TLS Protect Cloud (#4253)

*  Venafi TTL Protect Cloud

*  Improvements

*  Add authenticate generic type

*  Add paired items

*  Add codex

*  Update package.json
This commit is contained in:
Ricardo Espinoza 2022-10-07 09:48:45 -04:00 committed by GitHub
parent 7abc7e6408
commit d36e920997
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 1646 additions and 0 deletions

26
package-lock.json generated
View file

@ -8498,6 +8498,11 @@
"integrity": "sha512-uaht4XcYSq5ZrPriQW8C+g5DhptewRd1E84ph7L167sCyzLObz+U3JTpmYq/CNkvjNsz2mtyQoHPNEYQYTzWmg==", "integrity": "sha512-uaht4XcYSq5ZrPriQW8C+g5DhptewRd1E84ph7L167sCyzLObz+U3JTpmYq/CNkvjNsz2mtyQoHPNEYQYTzWmg==",
"dev": true "dev": true
}, },
"node_modules/@types/js-nacl": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@types/js-nacl/-/js-nacl-1.3.0.tgz",
"integrity": "sha512-juUvxo444ZfwDSwWyhssMxSN+snqTdiUoOVXZF+/ffVrGHq3rAf1fmczWn3z9TCEAuRbaTmgAcYlZ9MutyyOkQ=="
},
"node_modules/@types/json-diff": { "node_modules/@types/json-diff": {
"version": "0.5.2", "version": "0.5.2",
"resolved": "https://registry.npmjs.org/@types/json-diff/-/json-diff-0.5.2.tgz", "resolved": "https://registry.npmjs.org/@types/json-diff/-/json-diff-0.5.2.tgz",
@ -46250,12 +46255,25 @@
"@types/vorpal": "^1.11.0" "@types/vorpal": "^1.11.0"
} }
}, },
"packages/node-dev/node_modules/typescript": {
"version": "4.6.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz",
"integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=4.2.0"
}
},
"packages/nodes-base": { "packages/nodes-base": {
"name": "n8n-nodes-base", "name": "n8n-nodes-base",
"version": "0.194.0", "version": "0.194.0",
"license": "SEE LICENSE IN LICENSE.md", "license": "SEE LICENSE IN LICENSE.md",
"dependencies": { "dependencies": {
"@kafkajs/confluent-schema-registry": "1.0.6", "@kafkajs/confluent-schema-registry": "1.0.6",
"@types/js-nacl": "^1.3.0",
"amqplib": "^0.8.0", "amqplib": "^0.8.0",
"aws4": "^1.8.0", "aws4": "^1.8.0",
"basic-auth": "^2.0.1", "basic-auth": "^2.0.1",
@ -46275,6 +46293,7 @@
"imap-simple": "^4.3.0", "imap-simple": "^4.3.0",
"isbot": "^3.3.4", "isbot": "^3.3.4",
"iso-639-1": "^2.1.3", "iso-639-1": "^2.1.3",
"js-nacl": "^1.4.0",
"jsonwebtoken": "^8.5.1", "jsonwebtoken": "^8.5.1",
"kafkajs": "^1.14.0", "kafkajs": "^1.14.0",
"lodash.get": "^4.4.2", "lodash.get": "^4.4.2",
@ -54844,6 +54863,11 @@
"integrity": "sha512-uaht4XcYSq5ZrPriQW8C+g5DhptewRd1E84ph7L167sCyzLObz+U3JTpmYq/CNkvjNsz2mtyQoHPNEYQYTzWmg==", "integrity": "sha512-uaht4XcYSq5ZrPriQW8C+g5DhptewRd1E84ph7L167sCyzLObz+U3JTpmYq/CNkvjNsz2mtyQoHPNEYQYTzWmg==",
"dev": true "dev": true
}, },
"@types/js-nacl": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@types/js-nacl/-/js-nacl-1.3.0.tgz",
"integrity": "sha512-juUvxo444ZfwDSwWyhssMxSN+snqTdiUoOVXZF+/ffVrGHq3rAf1fmczWn3z9TCEAuRbaTmgAcYlZ9MutyyOkQ=="
},
"@types/json-diff": { "@types/json-diff": {
"version": "0.5.2", "version": "0.5.2",
"resolved": "https://registry.npmjs.org/@types/json-diff/-/json-diff-0.5.2.tgz", "resolved": "https://registry.npmjs.org/@types/json-diff/-/json-diff-0.5.2.tgz",
@ -74246,6 +74270,7 @@
"@types/gm": "^1.18.2", "@types/gm": "^1.18.2",
"@types/imap-simple": "^4.2.0", "@types/imap-simple": "^4.2.0",
"@types/jest": "^27.4.0", "@types/jest": "^27.4.0",
"@types/js-nacl": "^1.3.0",
"@types/jsonwebtoken": "^8.5.2", "@types/jsonwebtoken": "^8.5.2",
"@types/lodash.set": "^4.3.6", "@types/lodash.set": "^4.3.6",
"@types/lossless-json": "^1.0.0", "@types/lossless-json": "^1.0.0",
@ -74285,6 +74310,7 @@
"isbot": "^3.3.4", "isbot": "^3.3.4",
"iso-639-1": "^2.1.3", "iso-639-1": "^2.1.3",
"jest": "^27.4.7", "jest": "^27.4.7",
"js-nacl": "^1.4.0",
"jsonwebtoken": "^8.5.1", "jsonwebtoken": "^8.5.1",
"kafkajs": "^1.14.0", "kafkajs": "^1.14.0",
"lodash.get": "^4.4.2", "lodash.get": "^4.4.2",

View file

@ -0,0 +1,35 @@
import {
IAuthenticateGeneric,
ICredentialDataDecryptedObject,
ICredentialTestRequest,
ICredentialType,
IHttpRequestOptions,
NodePropertyTypes,
} from 'n8n-workflow';
export class VenafiTlsProtectCloudApi implements ICredentialType {
name = 'venafiTlsProtectCloudApi';
displayName = 'Venafi TLS Protect Cloud';
properties = [
{
displayName: 'API Key',
name: 'apiKey',
type: 'string' as NodePropertyTypes,
default: '',
},
];
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
headers: {
'tppl-api-key': '={{$credentials.apiKey}}',
},
},
};
test: ICredentialTestRequest = {
request: {
baseURL: 'https://api.venafi.cloud',
url: '/v1/preferences',
},
};
}

View file

@ -0,0 +1,398 @@
import { 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-examples/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-examples/expressions/">expression</a>',
typeOptions: {
loadOptionsMethod: 'getCertificateIssuingTemplates',
},
displayOptions: {
show: {
operation: ['renew'],
resource: ['certificate'],
},
},
default: '',
},
{
displayName: 'Certificate Signing Request',
name: 'certificateSigningRequest',
type: 'string',
typeOptions: {
alwaysOpenEditWindow: true,
},
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',
},
],
},
];

View file

@ -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;
}

View file

@ -0,0 +1,388 @@
import { 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-examples/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-examples/expressions/">expression</a>',
typeOptions: {
loadOptionsMethod: 'getCertificateIssuingTemplates',
},
displayOptions: {
show: {
operation: ['create'],
resource: ['certificateRequest'],
},
},
default: '',
},
{
displayName: 'Generate CSR',
name: 'generateCsr',
type: 'boolean',
displayOptions: {
show: {
operation: ['create'],
resource: ['certificateRequest'],
},
},
default: false,
},
{
displayName: 'Application Server Type Name or ID',
name: 'applicationServerTypeId',
type: 'options',
description:
'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code-examples/expressions/">expression</a>',
typeOptions: {
loadOptionsMethod: 'getApplicationServerTypes',
},
displayOptions: {
show: {
operation: ['create'],
resource: ['certificateRequest'],
generateCsr: [true],
},
},
default: '',
},
{
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: 'Country',
name: 'country',
type: 'string',
default: '',
description: 'A 2 letter country code',
},
{
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: '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: 'Locality',
name: 'locality',
type: 'string',
default: '',
description: 'The name of a city or town',
},
{
displayName: 'Organization',
name: 'organization',
type: 'string',
default: '',
description: 'The name of a company or organization',
},
{
displayName: 'Organizational Units',
name: 'organizationalUnits',
type: 'string',
typeOptions: {
multipleValues: true,
},
default: '',
description: 'The name of a department or section',
},
{
displayName: 'State',
name: 'state',
type: 'string',
default: '',
description: 'The name of a state or province',
},
{
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',
typeOptions: {
alwaysOpenEditWindow: true,
},
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: 'options',
options: [
{
name: '1 Year',
value: 'P1Y',
},
{
name: '10 Days',
value: 'P10D',
},
{
name: '12 Hours',
value: 'PT12H',
},
],
default: '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',
},
];

View file

@ -0,0 +1,146 @@
import { OptionsWithUri } from 'request';
import {
IExecuteFunctions,
IExecuteSingleFunctions,
ILoadOptionsFunctions,
IPollFunctions,
} from 'n8n-core';
import { IDataObject, JsonObject, NodeApiError } from 'n8n-workflow';
import { get } from 'lodash';
import * as nacl_factory from 'js-nacl';
export async function venafiApiRequest(
this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IPollFunctions,
method: string,
resource: string,
body = {},
qs: IDataObject = {},
uri?: string,
option: IDataObject = {},
// tslint:disable-next-line:no-any
): Promise<any> {
const operation = this.getNodeParameter('operation', 0) as string;
const options: OptionsWithUri = {
headers: {
Accept: 'application/json',
'content-type': 'application/json',
},
method,
body,
qs,
uri: `https://api.venafi.cloud${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: string,
endpoint: string,
// tslint:disable-next-line:no-any
body: any = {},
query: IDataObject = {},
// tslint:disable-next-line:no-any
): 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]);
} while (responseData._links && 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 = () => {
return new Promise((resolve, reject) => {
// tslint:disable-next-line:no-any
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).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).toString('base64');
return resolve([encryptedKeyPass, encryptedKeyStorePass]);
} catch (error) {
return reject(error);
}
});
});
};
return await promise();
}

View file

@ -0,0 +1,18 @@
{
"node": "n8n-nodes-base.venafiTlsProtectCloud",
"nodeVersion": "1.0",
"codexVersion": "1.0",
"categories": ["Development"],
"resources": {
"credentialDocumentation": [
{
"url": "https://docs.n8n.io/credentials/venafiTlsProtectCloud"
}
],
"primaryDocumentation": [
{
"url": "https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.venafiTlsProtectCloud/"
}
]
}
}

View file

@ -0,0 +1,488 @@
import { IExecuteFunctions } from 'n8n-core';
import {
IDataObject,
ILoadOptionsFunctions,
INodeExecutionData,
INodePropertyOptions,
INodeType,
INodeTypeDescription,
} from 'n8n-workflow';
import { encryptPassphrase, venafiApiRequest, venafiApiRequestAllItems } from './GenericFunctions';
import { certificateFields, certificateOperations } from './CertificateDescription';
import {
certificateRequestFields,
certificateRequestOperations,
} from './CertificateRequestDescription';
import {
ICertficateKeystoreRequest,
ICertficateRequest,
ICsrAttributes,
IKeyTypeParameters,
ISubjectAltNamesByType,
} from './CertificateInterface';
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',
color: '#000000',
},
inputs: ['main'],
outputs: ['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 getApplicationServerTypes(
this: ILoadOptionsFunctions,
): Promise<INodePropertyOptions[]> {
const returnData: INodePropertyOptions[] = [];
const { applicationServerTypes } = await venafiApiRequest.call(
this,
'GET',
'/outagedetection/v1/applicationservertypes',
);
for (const applicationServerType of applicationServerTypes) {
returnData.push({
name: applicationServerType.platformName,
value: applicationServerType.id,
});
}
return returnData;
},
async getCertificateIssuingTemplates(
this: ILoadOptionsFunctions,
): Promise<INodePropertyOptions[]> {
const returnData: INodePropertyOptions[] = [];
const { certificateIssuingTemplates } = await venafiApiRequest.call(
this,
'GET',
'/v1/certificateissuingtemplates',
);
for (const issueTemplate of certificateIssuingTemplates) {
returnData.push({
name: issueTemplate.name,
value: issueTemplate.id,
});
}
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) as string;
const operation = this.getNodeParameter('operation', 0) as string;
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) as IDataObject;
const generateCsr = this.getNodeParameter('generateCsr', i) as boolean;
const body: ICertficateRequest = {
applicationId,
certificateIssuingTemplateId,
};
if (generateCsr) {
const applicationServerTypeId = this.getNodeParameter(
'applicationServerTypeId',
i,
) as string;
const commonName = this.getNodeParameter('commonName', i) as string;
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
const keyTypeDetails: IKeyTypeParameters = {};
const csrAttributes: ICsrAttributes = {};
const subjectAltNamesByType: ISubjectAltNamesByType = {};
body.isVaaSGenerated = true;
body.applicationServerTypeId = applicationServerTypeId as string;
csrAttributes.commonName = commonName as string;
// 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) as boolean;
if (returnAll) {
responseData = await venafiApiRequestAllItems.call(
this,
'certificateRequests',
'GET',
`/outagedetection/v1/certificaterequests`,
{},
qs,
);
} else {
const limit = this.getNodeParameter('limit', i) as number;
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) as string;
const downloadItem = this.getNodeParameter('downloadItem', i) as string;
const options = this.getNodeParameter('options', i) as IDataObject;
// Cert Download
if (downloadItem === 'certificate') {
Object.assign(qs, options);
responseData = await venafiApiRequest.call(
this,
'GET',
`/outagedetection/v1/certificates/${certificateId}/contents`,
{},
qs,
undefined,
{ 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,
{},
undefined,
{ encoding: null, json: false, resolveWithFullResponse: true },
);
}
const contentDisposition = 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),
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) as boolean;
const filters = this.getNodeParameter('filters', i) as IDataObject;
Object.assign(qs, filters);
if (returnAll) {
responseData = await venafiApiRequestAllItems.call(
this,
'certificates',
'GET',
`/outagedetection/v1/certificates`,
{},
qs,
);
} else {
qs.limit = this.getNodeParameter('limit', i) as number;
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) as IDataObject;
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), {
itemData: { item: i },
}),
);
} catch (error) {
if (this.continueOnFail()) {
returnData.push({ json: { error: error.message } });
continue;
}
throw error;
}
}
return [returnData as INodeExecutionData[]];
}
}

View file

@ -0,0 +1,101 @@
import { IPollFunctions } from 'n8n-core';
import { INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
import moment from 'moment';
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,
subtitle: '={{$parameter["triggerOn"]}}',
description: 'Starts the workflow when Venafi events occure',
defaults: {
name: 'Venafi TLS Protect Cloud',
color: '#000000',
},
credentials: [
{
name: 'venafiTlsProtectCloudApi',
required: true,
},
],
polling: true,
inputs: [],
outputs: ['main'],
properties: [
{
displayName: 'Trigger On',
name: 'trigger On',
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 event = this.getNodeParameter('event') as string;
const now = moment().format();
const startDate = webhookData.lastTimeChecked || now;
const endDate = now;
const { certificates: certificates } = await venafiApiRequest.call(
this,
'POST',
`/outagedetection/v1/certificatesearch`,
{
expression: {
operands: [
{
operator: 'AND',
operands: [
{
field: 'validityEnd',
operator: 'LTE',
values: [endDate],
},
{
field: 'validityEnd',
operator: 'GTE',
values: [startDate],
},
],
},
],
},
ordering: {
orders: [
{
field: 'certificatInstanceModificationDate',
direction: 'DESC',
},
],
},
},
{},
);
webhookData.lastTimeChecked = endDate;
if (Array.isArray(certificates) && certificates.length) {
return [this.helpers.returnJsonArray(certificates)];
}
return null;
}
}

View file

@ -0,0 +1 @@
<svg id="SvgjsSvg1001" width="288" height="288" xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs"><defs id="SvgjsDefs1002"></defs><g id="SvgjsG1008"><svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="animicon" viewBox="0 0 58 49" width="288" height="288"><path d="M15.3387 9.94015C18.0582 17.5866 22.0735 24.6859 27.2014 30.9138L28.9394 32.9761L30.6976 30.9138C35.8337 24.6846 39.8621 17.5863 42.6007 9.94015H36.6996C34.7605 14.8817 32.1701 19.5303 29 23.7575C25.8286 19.5348 23.2444 14.885 21.3206 9.94015H15.3387Z" fill="#ff6333" class="color000 svgShape"></path><path d="M39.6704 0.0618034C39.5289 0.412393 38.1951 4.43394 37.4878 6.06315H48.8251C44.8538 17.1118 39.0274 27.3702 31.607 36.3788L29.0808 39.4311L26.5547 36.3788C19.1282 27.3515 13.3016 17.0717 9.33659 6.00122H20.1282C19.4209 4.37201 18.1073 0.35059 17.9456 0H0C5.83647 16.2283 14.3983 31.295 25.3017 44.5249L29 49L32.7185 44.5249C43.6117 31.2913 52.1663 16.2252 58 0L39.6704 0.0618034Z" fill="#ff6333" class="color000 svgShape"></path></svg></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -317,6 +317,7 @@
"dist/credentials/UrlScanIoApi.credentials.js", "dist/credentials/UrlScanIoApi.credentials.js",
"dist/credentials/VeroApi.credentials.js", "dist/credentials/VeroApi.credentials.js",
"dist/credentials/VonageApi.credentials.js", "dist/credentials/VonageApi.credentials.js",
"dist/credentials/VenafiTlsProtectCloudApi.credentials.js",
"dist/credentials/VenafiTlsProtectDatacenterApi.credentials.js", "dist/credentials/VenafiTlsProtectDatacenterApi.credentials.js",
"dist/credentials/WebflowApi.credentials.js", "dist/credentials/WebflowApi.credentials.js",
"dist/credentials/WebflowOAuth2Api.credentials.js", "dist/credentials/WebflowOAuth2Api.credentials.js",
@ -686,6 +687,7 @@
"dist/nodes/UptimeRobot/UptimeRobot.node.js", "dist/nodes/UptimeRobot/UptimeRobot.node.js",
"dist/nodes/UrlScanIo/UrlScanIo.node.js", "dist/nodes/UrlScanIo/UrlScanIo.node.js",
"dist/nodes/Vero/Vero.node.js", "dist/nodes/Vero/Vero.node.js",
"dist/nodes/Venafi/ProtectCloud/VenafiTlsProtectCloud.node.js",
"dist/nodes/Venafi/Datacenter/VenafiTlsProtectDatacenter.node.js", "dist/nodes/Venafi/Datacenter/VenafiTlsProtectDatacenter.node.js",
"dist/nodes/Vonage/Vonage.node.js", "dist/nodes/Vonage/Vonage.node.js",
"dist/nodes/Wait/Wait.node.js", "dist/nodes/Wait/Wait.node.js",
@ -754,6 +756,7 @@
}, },
"dependencies": { "dependencies": {
"@kafkajs/confluent-schema-registry": "1.0.6", "@kafkajs/confluent-schema-registry": "1.0.6",
"@types/js-nacl": "^1.3.0",
"amqplib": "^0.8.0", "amqplib": "^0.8.0",
"aws4": "^1.8.0", "aws4": "^1.8.0",
"basic-auth": "^2.0.1", "basic-auth": "^2.0.1",
@ -773,6 +776,7 @@
"imap-simple": "^4.3.0", "imap-simple": "^4.3.0",
"isbot": "^3.3.4", "isbot": "^3.3.4",
"iso-639-1": "^2.1.3", "iso-639-1": "^2.1.3",
"js-nacl": "^1.4.0",
"jsonwebtoken": "^8.5.1", "jsonwebtoken": "^8.5.1",
"kafkajs": "^1.14.0", "kafkajs": "^1.14.0",
"lodash.get": "^4.4.2", "lodash.get": "^4.4.2",