diff --git a/packages/nodes-base/nodes/Aws/CertificateManager/AwsCertificateManager.node.json b/packages/nodes-base/nodes/Aws/CertificateManager/AwsCertificateManager.node.json new file mode 100644 index 0000000000..f1bc023d8e --- /dev/null +++ b/packages/nodes-base/nodes/Aws/CertificateManager/AwsCertificateManager.node.json @@ -0,0 +1,29 @@ +{ + "node": "n8n-nodes-base.awsCertificateManager", + "nodeVersion": "1.0", + "codexVersion": "1.0", + "categories": ["Development"], + "resources": { + "credentialDocumentation": [ + { + "url": "https://docs.n8n.io/credentials/aws" + } + ], + "primaryDocumentation": [ + { + "url": "https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.awsCertificateManager/" + } + ], + "generic": [ + { + "label": "Why business process automation with n8n can change your daily life", + "icon": "🧬", + "url": "https://n8n.io/blog/why-business-process-automation-with-n8n-can-change-your-daily-life/" + }, + { + "label": "7 no-code workflow automations for Amazon Web Services", + "url": "https://n8n.io/blog/aws-workflow-automation/" + } + ] + } +} diff --git a/packages/nodes-base/nodes/Aws/CertificateManager/AwsCertificateManager.node.ts b/packages/nodes-base/nodes/Aws/CertificateManager/AwsCertificateManager.node.ts new file mode 100644 index 0000000000..3039268da3 --- /dev/null +++ b/packages/nodes-base/nodes/Aws/CertificateManager/AwsCertificateManager.node.ts @@ -0,0 +1,231 @@ +import { IExecuteFunctions } from 'n8n-core'; + +import { IDataObject, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow'; + +import { certificateFields, certificateOperations } from './CertificateDescription'; + +import { awsApiRequestAllItems, awsApiRequestREST } from './GenericFunctions'; + +export class AwsCertificateManager implements INodeType { + description: INodeTypeDescription = { + displayName: 'AWS Certificate Manager', + name: 'awsCertificateManager', + icon: 'file:acm.svg', + group: ['output'], + version: 1, + subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', + description: 'Sends data to AWS Certificate Manager', + defaults: { + name: 'AWS Certificate Manager', + color: '#7d9a4b', + }, + inputs: ['main'], + outputs: ['main'], + credentials: [ + { + name: 'aws', + required: true, + }, + ], + properties: [ + { + displayName: 'Resource', + name: 'resource', + type: 'options', + noDataExpression: true, + options: [ + { + name: 'Certificate', + value: 'certificate', + }, + ], + default: 'certificate', + }, + // Certificate + ...certificateOperations, + ...certificateFields, + ], + }; + + async execute(this: IExecuteFunctions): Promise { + const items = this.getInputData(); + const returnData: IDataObject[] = []; + 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 < items.length; i++) { + try { + if (resource === 'certificate') { + //https://docs.aws.amazon.com/acm/latest/APIReference/API_DeleteCertificate.html + if (operation === 'delete') { + const certificateArn = this.getNodeParameter('certificateArn', i) as string; + + const body: IDataObject = { + CertificateArn: certificateArn, + }; + + responseData = await awsApiRequestREST.call( + this, + `acm`, + 'POST', + '', + JSON.stringify(body), + qs, + { + 'X-Amz-Target': 'CertificateManager.DeleteCertificate', + 'Content-Type': 'application/x-amz-json-1.1', + }, + ); + + responseData = { success: true }; + } + + //https://docs.aws.amazon.com/acm/latest/APIReference/API_GetCertificate.html + if (operation === 'get') { + const certificateArn = this.getNodeParameter('certificateArn', i) as string; + + const body: IDataObject = { + CertificateArn: certificateArn, + }; + + responseData = await awsApiRequestREST.call( + this, + `acm`, + 'POST', + '', + JSON.stringify(body), + qs, + { + 'X-Amz-Target': 'CertificateManager.GetCertificate', + 'Content-Type': 'application/x-amz-json-1.1', + }, + ); + } + + //https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjectsV2.html + if (operation === 'getMany') { + const returnAll = this.getNodeParameter('returnAll', 0) as boolean; + const options = this.getNodeParameter('options', i) as IDataObject; + + const body: { Includes: IDataObject; CertificateStatuses: string[]; MaxItems: number } = { + CertificateStatuses: [], + Includes: {}, + MaxItems: 0, + }; + + if (options.certificateStatuses) { + body.CertificateStatuses = options.certificateStatuses as string[]; + } + + if (options.certificateStatuses) { + body.Includes['extendedKeyUsage'] = options.extendedKeyUsage as string[]; + } + + if (options.keyTypes) { + body.Includes['keyTypes'] = options.keyTypes as string[]; + } + + if (options.keyUsage) { + body.Includes['keyUsage'] = options.keyUsage as string[]; + } + + if (returnAll) { + responseData = await awsApiRequestAllItems.call( + this, + 'CertificateSummaryList', + 'acm', + 'POST', + '', + '{}', + qs, + { + 'X-Amz-Target': 'CertificateManager.ListCertificates', + 'Content-Type': 'application/x-amz-json-1.1', + }, + ); + } else { + body.MaxItems = this.getNodeParameter('limit', 0) as number; + responseData = await awsApiRequestREST.call( + this, + `acm`, + 'POST', + '', + JSON.stringify(body), + qs, + { + 'X-Amz-Target': 'CertificateManager.ListCertificates', + 'Content-Type': 'application/x-amz-json-1.1', + }, + ); + responseData = responseData.CertificateSummaryList; + } + } + + //https://docs.aws.amazon.com/acm/latest/APIReference/API_DescribeCertificate.html + if (operation === 'getMetadata') { + const certificateArn = this.getNodeParameter('certificateArn', i) as string; + + const body: IDataObject = { + CertificateArn: certificateArn, + }; + + responseData = await awsApiRequestREST.call( + this, + `acm`, + 'POST', + '', + JSON.stringify(body), + qs, + { + 'X-Amz-Target': 'CertificateManager.DescribeCertificate', + 'Content-Type': 'application/x-amz-json-1.1', + }, + ); + + responseData = responseData.Certificate; + } + + //https://docs.aws.amazon.com/acm/latest/APIReference/API_RenewCertificate.html + if (operation === 'renew') { + const certificateArn = this.getNodeParameter('certificateArn', i) as string; + + const body: IDataObject = { + CertificateArn: certificateArn, + }; + + responseData = await awsApiRequestREST.call( + this, + `acm`, + 'POST', + '', + JSON.stringify(body), + qs, + { + 'X-Amz-Target': 'CertificateManager.RenewCertificate', + 'Content-Type': 'application/x-amz-json-1.1', + }, + ); + + responseData = { success: true }; + } + + const executionData = this.helpers.constructExecutionMetaData( + this.helpers.returnJsonArray(responseData), + { itemData: { item: i } }, + ); + + returnData.push(...executionData); + } + } catch (error) { + if (this.continueOnFail()) { + returnData.push({ json: { error: error.message } }); + continue; + } + throw error; + } + } + + return [returnData as INodeExecutionData[]]; + } +} diff --git a/packages/nodes-base/nodes/Aws/CertificateManager/CertificateDescription.ts b/packages/nodes-base/nodes/Aws/CertificateManager/CertificateDescription.ts new file mode 100644 index 0000000000..7b3a8b0097 --- /dev/null +++ b/packages/nodes-base/nodes/Aws/CertificateManager/CertificateDescription.ts @@ -0,0 +1,327 @@ +import { INodeProperties } from 'n8n-workflow'; + +export const certificateOperations: INodeProperties[] = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + noDataExpression: true, + displayOptions: { + show: { + resource: ['certificate'], + }, + }, + options: [ + { + name: 'Delete', + value: 'delete', + description: 'Delete a certificate', + action: 'Delete a certificate', + }, + { + name: 'Get', + value: 'get', + description: 'Get a certificate', + action: 'Get a certificate', + }, + { + name: 'Get Many', + value: 'getMany', + description: 'Get many certificates', + action: 'Get many certificates', + }, + { + name: 'Get Metadata', + value: 'getMetadata', + description: 'Get certificate metadata', + action: 'Get certificate metadata', + }, + { + name: 'Renew', + value: 'renew', + description: 'Renew a certificate', + action: 'Renew a certificate', + }, + ], + default: 'renew', + }, +]; + +export const certificateFields: INodeProperties[] = [ + /* -------------------------------------------------------------------------- */ + /* certificate:renew */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Certificate ARN', + name: 'certificateArn', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: ['certificate'], + operation: ['renew', 'get', 'delete', 'getMetadata'], + }, + }, + description: + 'String that contains the ARN of the ACM certificate to be renewed. This must be of the form: arn:aws:acm:region:123456789012:certificate/12345678-1234-1234-1234-123456789012.', + }, + /* -------------------------------------------------------------------------- */ + /* certificate:delete */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Bucket Name', + name: 'bucketName', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: ['certificate'], + operation: ['delete'], + }, + }, + }, + { + displayName: 'Certificate Key', + name: 'certificateKey', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: ['certificate'], + operation: ['delete'], + }, + }, + }, + /* -------------------------------------------------------------------------- */ + /* 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 Field', + default: {}, + displayOptions: { + show: { + resource: ['certificate'], + operation: ['getMany'], + }, + }, + options: [ + { + displayName: 'Certificate Statuses', + name: 'certificateStatuses', + type: 'multiOptions', + options: [ + { + name: 'Expired', + value: 'EXPIRED', + }, + { + name: 'Failed', + value: 'FAILED', + }, + { + name: 'Inactive', + value: 'INACTIVE', + }, + { + name: 'Issued', + value: 'ISSUED', + }, + { + name: 'Pending Validation', + value: 'PENDING_VALIDATION', + }, + { + name: 'Revoked', + value: 'REVOKED', + }, + { + name: 'Validation Timed Out', + value: 'VALIDATION_TIMED_OUT', + }, + ], + default: [], + description: 'Filter the certificate list by status value', + }, + { + displayName: 'Extended Key Usage', + name: 'extendedKeyUsage', + type: 'multiOptions', + options: [ + { + name: 'Any', + value: 'ANY', + }, + { + name: 'Code Signing', + value: 'CODE_SIGNING', + }, + { + name: 'Custom', + value: 'CUSTOM', + }, + { + name: 'Email Protection', + value: 'EMAIL_PROTECTION', + }, + { + name: 'IPSEC End System', + value: 'IPSEC_END_SYSTEM', + }, + { + name: 'IPSEC Tunnel', + value: 'IPSEC_TUNNEL', + }, + { + name: 'IPSEC User', + value: 'IPSEC_USER', + }, + { + name: 'None', + value: 'NONE', + }, + { + name: 'OCSP Signing', + value: 'OCSP_SIGNING', + }, + { + name: 'Time Stamping', + value: 'TIME_STAMPING', + }, + { + name: 'TLS Web Client Authentication', + value: 'TLS_WEB_CLIENT_AUTHENTICATION', + }, + { + name: 'TLS Web Server Authentication', + value: 'TLS_WEB_SERVER_AUTHENTICATION', + }, + ], + default: [], + description: 'Specify one or more ExtendedKeyUsage extension values', + }, + { + displayName: 'Key Types', + name: 'keyTypes', + type: 'multiOptions', + options: [ + { + name: 'EC Prime256v1', + value: 'EC_prime256v1', + }, + { + name: 'EC Secp384r1', + value: 'EC_secp384r1', + }, + { + name: 'EC Secp521r1', + value: 'EC_secp521r1', + }, + { + name: 'RSA 1024', + value: 'RSA_1024', + }, + { + name: 'RSA 2048', + value: 'RSA_2048', + }, + { + name: 'RSA 4096', + value: 'RSA_4096', + }, + ], + default: ['RSA_2048'], + description: 'Specify one or more algorithms that can be used to generate key pairs', + }, + { + displayName: 'Key Usage', + name: 'keyUsage', + type: 'multiOptions', + options: [ + { + name: 'Any', + value: 'ANY', + }, + { + name: 'Certificate Signing', + value: 'CERTIFICATE_SIGNING', + }, + { + name: 'CRL Signing', + value: 'CRL_SIGNING', + }, + { + name: 'Custom', + value: 'CUSTOM', + }, + { + name: 'Data Encipherment', + value: 'DATA_ENCIPHERMENT', + }, + { + name: 'Decipher Only', + value: 'DECIPHER_ONLY', + }, + { + name: 'Digital Signature', + value: 'DIGITAL_SIGNATURE', + }, + { + name: 'Encipher Only', + value: 'ENCIPHER_ONLY', + }, + { + name: 'Key Agreement', + value: 'KEY_AGREEMENT', + }, + { + name: 'Key Encipherment', + value: 'KEY_ENCIPHERMENT', + }, + { + name: 'Non Repudiation', + value: 'NON_REPUDIATION', + }, + ], + default: [], + description: 'Specify one or more KeyUsage extension values', + }, + ], + }, +]; diff --git a/packages/nodes-base/nodes/Aws/CertificateManager/GenericFunctions.ts b/packages/nodes-base/nodes/Aws/CertificateManager/GenericFunctions.ts new file mode 100644 index 0000000000..e9beb709ef --- /dev/null +++ b/packages/nodes-base/nodes/Aws/CertificateManager/GenericFunctions.ts @@ -0,0 +1,96 @@ +import { get } from 'lodash'; + +import { + IExecuteFunctions, + IHookFunctions, + ILoadOptionsFunctions, + IWebhookFunctions, +} from 'n8n-core'; + +import { IDataObject, IHttpRequestOptions, NodeApiError } from 'n8n-workflow'; + +export async function awsApiRequest( + this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions | IWebhookFunctions, + service: string, + method: string, + path: string, + body?: string | Buffer, + query: IDataObject = {}, + headers?: object, + // tslint:disable-next-line:no-any + ): Promise { + const credentials = await this.getCredentials('aws'); + + const requestOptions = { + qs: { + service, + path, + ...query, + }, + headers, + method, + url: '', + body, + region: credentials?.region as string, + } as IHttpRequestOptions; + + try { + return await this.helpers.requestWithAuthentication.call(this, 'aws', requestOptions); + } catch (error) { + throw new NodeApiError(this.getNode(), error); + } +} + +export async function awsApiRequestREST( + this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions, + service: string, + method: string, + path: string, + body?: string, + query: IDataObject = {}, + headers?: object, + // tslint:disable-next-line:no-any + ): Promise { + const response = await awsApiRequest.call(this, service, method, path, body, query, headers); + try { + return JSON.parse(response); + } catch (e) { + return response; + } +} + +export async function awsApiRequestAllItems( + this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions, + propertyName: string, + service: string, + method: string, + path: string, + body?: string, + query: IDataObject = {}, + headers: IDataObject = {}, + // tslint:disable-next-line:no-any +): Promise { + + const returnData: IDataObject[] = []; + + let responseData; + + do { + responseData = await awsApiRequestREST.call( + this, + service, + method, + path, + body, + query, + headers, + ); + if (responseData.NextToken) { + const data = JSON.parse(body as string); + data['NextToken'] = responseData.NextToken; + } + returnData.push.apply(returnData, get(responseData, propertyName)); + } while (responseData.NextToken !== undefined); + + return returnData; +} diff --git a/packages/nodes-base/nodes/Aws/CertificateManager/acm.svg b/packages/nodes-base/nodes/Aws/CertificateManager/acm.svg new file mode 100644 index 0000000000..22d4e0e7cb --- /dev/null +++ b/packages/nodes-base/nodes/Aws/CertificateManager/acm.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index f2db52bec0..1f643f929e 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -360,6 +360,7 @@ "dist/nodes/Aws/AwsLambda.node.js", "dist/nodes/Aws/AwsSns.node.js", "dist/nodes/Aws/AwsSnsTrigger.node.js", + "dist/nodes/Aws/CertificateManager/AwsCertificateManager.node.js", "dist/nodes/Aws/Comprehend/AwsComprehend.node.js", "dist/nodes/Aws/DynamoDB/AwsDynamoDB.node.js", "dist/nodes/Aws/ELB/AwsElb.node.js",