-
Notifications
You must be signed in to change notification settings - Fork 48
PGP MTLS Batch Upload #165
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
mahmishr
wants to merge
9
commits into
master
Choose a base branch
from
feature/pgp-mtls
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 4 commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
b085368
pgp mtls
mahmishr 2df31aa
Update package.json
mahmishr bb0fe83
Update cybersource_node_sdk_gen.bat
mahmishr 0d1d107
code refactored
mahmishr b2dcfd0
comments resolved
mahmishr 4cc5533
opts change
mahmishr f2ab7da
server name and log error chngs
mahmishr 870af5c
minor fix
mahmishr e16e630
Update package.json
mahmishr File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,123 @@ | ||
| const path = require('path'); | ||
| const fs = require('fs'); | ||
| const { handlePGPEncrypt } = require('../utilities/PGP/BatchUpload/PgpEncryptionUtility'); | ||
| const { | ||
| handleUploadOperationUsingP12orPfx, | ||
| handleUploadOperationUsingPrivateKeyAndCerts | ||
| } = require('../utilities/PGP/BatchUpload/MutualAuthUploadUtility'); | ||
| const BatchUploadUtility = require('../utilities/PGP/BatchUpload/BatchUploadUtility'); | ||
|
|
||
| /** | ||
| * BatchUploadWithMTLSApi | ||
| * Class for uploading batch files to CyberSource using mutual TLS authentication. | ||
| * Supports PKCS#12 client certificates, and direct private key/certificate paths. | ||
| * Handles PGP encryption of files before upload. | ||
| */ | ||
| class BatchUploadWithMTLSApi { | ||
| constructor(logger = console) { | ||
| this.logger = logger; | ||
| } | ||
|
|
||
| uploadBatchAPIWithP12(opts, callback) { | ||
gaubansa marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| try { | ||
| const { | ||
mahmishr marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| inputFilePath, | ||
| environmentHostname, | ||
| publicKeyFilePath, | ||
| clientCertP12FilePath, | ||
| clientCertP12Password, | ||
| serverTrustCertPath, | ||
| rejectUnauthorizedFlag = true | ||
mahmishr marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } = opts; | ||
| if (rejectUnauthorizedFlag === false) { | ||
| this.logger.warn('rejectUnauthorized is set to false. SSL verification is DISABLED. This setting is NOT SAFE for production and should NOT be used in production environments!'); | ||
| } | ||
| this.logger.info('Starting batch upload with p12/pfx for given file'); | ||
| const endpoint = '/pts/v1/transaction-batch-upload'; | ||
| const endpointUrl = BatchUploadUtility.getEndpointUrl(environmentHostname, endpoint); | ||
| BatchUploadUtility.validateBatchApiP12Inputs( | ||
| inputFilePath, environmentHostname, publicKeyFilePath, clientCertP12FilePath, serverTrustCertPath | ||
| ); | ||
|
|
||
| handlePGPEncrypt(inputFilePath, publicKeyFilePath) | ||
| .then(encryptedBuffer => { | ||
| const uploadFileName = path.basename(inputFilePath) + '.pgp'; | ||
| console.log('Encrypted file name:', uploadFileName); | ||
| const clientCertP12 = fs.readFileSync(clientCertP12FilePath); | ||
| const serverTrustCert = serverTrustCertPath ? fs.readFileSync(serverTrustCertPath) : undefined; | ||
| return handleUploadOperationUsingP12orPfx( | ||
| encryptedBuffer, | ||
| endpointUrl, | ||
| environmentHostname, | ||
| uploadFileName, | ||
| clientCertP12, | ||
| clientCertP12Password, | ||
| serverTrustCert, | ||
| rejectUnauthorizedFlag | ||
| ); | ||
| }) | ||
| .then(result => callback(null, result)) | ||
| .catch(error => { | ||
| this.logger.error(error); | ||
| callback(error); | ||
| }); | ||
| } catch (e) { | ||
| this.logger.error('Exception in Batch Upload API', e); | ||
| callback(e); | ||
| } | ||
| } | ||
|
|
||
| uploadBatchAPIWithKeys(opts, callback) { | ||
| try { | ||
| const { | ||
| inputFilePath, | ||
| environmentHostname, | ||
mahmishr marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| publicKeyFilePath, | ||
| clientPrivateKeyFilePath, | ||
| clientCertFilePath, | ||
| serverTrustCertPath, | ||
| clientKeyPassword, | ||
| rejectUnauthorizedFlag = true | ||
|
|
||
| } = opts; | ||
| if (rejectUnauthorizedFlag === false) { | ||
| this.logger.warn('rejectUnauthorized is set to false. SSL verification is DISABLED. This setting is NOT SAFE for production and should NOT be used in production environments!'); | ||
| } | ||
| this.logger.info('Starting batch upload with client private key and certs for given file'); | ||
| const endpoint = '/pts/v1/transaction-batch-upload'; | ||
| const endpointUrl = BatchUploadUtility.getEndpointUrl(environmentHostname, endpoint); | ||
| BatchUploadUtility.validateBatchApiKeysInputs( | ||
| inputFilePath, environmentHostname, publicKeyFilePath, clientPrivateKeyFilePath, clientCertFilePath, serverTrustCertPath | ||
| ); | ||
|
|
||
| handlePGPEncrypt(inputFilePath, publicKeyFilePath) | ||
| .then(encryptedBuffer => { | ||
| const uploadFileName = path.basename(inputFilePath) + '.pgp'; | ||
| const clientPrivateKey = fs.readFileSync(clientPrivateKeyFilePath); | ||
| const clientCert = fs.readFileSync(clientCertFilePath); | ||
| const serverTrustCert = serverTrustCertPath ? fs.readFileSync(serverTrustCertPath) : undefined; | ||
| return handleUploadOperationUsingPrivateKeyAndCerts( | ||
| encryptedBuffer, | ||
| endpointUrl, | ||
| environmentHostname, | ||
| uploadFileName, | ||
| clientPrivateKey, | ||
| clientCert, | ||
| serverTrustCert, | ||
| clientKeyPassword, | ||
| rejectUnauthorizedFlag | ||
| ); | ||
| }) | ||
| .then(result => callback(null, result)) | ||
| .catch(error => { | ||
| this.logger.error(error); | ||
| callback(error); | ||
| }); | ||
| } catch (e) { | ||
| this.logger.error('Exception in Batch Upload API', e); | ||
| callback(e); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| module.exports = BatchUploadWithMTLSApi; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,118 @@ | ||
| const fs = require('fs'); | ||
| const path = require('path'); | ||
|
|
||
| const MAX_FILE_SIZE_BYTES = 75 * 1024 * 1024; // 75MB | ||
|
|
||
| class BatchUploadUtility { | ||
| /** | ||
| * Constructs the full endpoint URL for the given environment hostname and endpoint path. | ||
| * @param {string} environmentHostname - The environment hostname (with or without protocol prefix). | ||
| * @param {string} endpoint - The endpoint path to append. | ||
| * @returns {string} The full endpoint URL. | ||
| */ | ||
| static getEndpointUrl(environmentHostname, endpoint) { | ||
| const URL_PREFIX = 'https://'; | ||
| let baseUrl; | ||
| if (environmentHostname.trim().toLowerCase().startsWith(URL_PREFIX)) { | ||
| baseUrl = environmentHostname.trim(); | ||
| } else { | ||
| baseUrl = URL_PREFIX + environmentHostname.trim(); | ||
| } | ||
| return baseUrl + endpoint; | ||
| } | ||
|
|
||
| /** | ||
| * Validates the input parameters for batch API using P12 client certificate. | ||
| * @param {string} inputFile - Path to the input CSV file for batch upload. | ||
| * @param {string} environmentHostname | ||
| * @param {string} pgpEncryptionCertPath | ||
| * @param {string} clientCertP12FilePath | ||
| * @param {string} serverTrustCertPath | ||
| */ | ||
| static validateBatchApiP12Inputs(inputFile, environmentHostname, pgpEncryptionCertPath, clientCertP12FilePath, serverTrustCertPath) { | ||
| this.validateInputFile(inputFile); | ||
| if (!environmentHostname || !environmentHostname.trim()) { | ||
| throw new Error('Environment Host Name for Batch Upload API cannot be null or empty.'); | ||
| } | ||
| this.validatePathAndFile(pgpEncryptionCertPath, 'PGP Encryption Cert Path'); | ||
| this.validatePathAndFile(clientCertP12FilePath, 'Client Cert P12 File Path'); | ||
| // serverTrustCertPath is optional, but if provided, validate | ||
| if (serverTrustCertPath && serverTrustCertPath.trim()) { | ||
| this.validatePathAndFile(serverTrustCertPath, 'Server Trust Cert Path'); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Validates the input parameters for batch API using direct key and certificate file paths. | ||
| * @param {string} inputFile - Path to the input CSV file for batch upload. | ||
| * @param {string} environmentHostname | ||
| * @param {string} pgpPublicKeyPath - Path to the PGP public key file (.asc). | ||
| * @param {string} clientPrivateKeyPath - Path to the client private key file (PEM). | ||
| * @param {string} clientCertPath - Path to the client X509 certificate file (PEM). | ||
| * @param {string} serverTrustCertPath - Path to the server trust X509 certificate file (PEM, optional). | ||
| */ | ||
| static validateBatchApiKeysInputs(inputFile, environmentHostname, pgpPublicKeyPath, clientPrivateKeyPath, clientCertPath, serverTrustCertPath) { | ||
| this.validateInputFile(inputFile); | ||
| if (!environmentHostname || !environmentHostname.trim()) { | ||
| throw new Error('Environment Host Name for Batch Upload API cannot be null or empty.'); | ||
| } | ||
| this.validatePathAndFile(pgpPublicKeyPath, 'PGP Public Key Path'); | ||
| this.validatePathAndFile(clientPrivateKeyPath, 'Client Private Key Path'); | ||
| this.validatePathAndFile(clientCertPath, 'Client Certificate Path'); | ||
| // serverTrustCertPath is optional, but if provided, validate | ||
| if (serverTrustCertPath && serverTrustCertPath.trim()) { | ||
| this.validatePathAndFile(serverTrustCertPath, 'Server Trust Certificate Path'); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Validates the input file for batch upload. | ||
| * Checks for existence, file type (CSV), and maximum file size (75MB). | ||
| * @param {string} inputFile - Path to the input file to validate. | ||
| */ | ||
| static validateInputFile(inputFile) { | ||
| if (!inputFile || !fs.existsSync(inputFile) || !fs.statSync(inputFile).isFile()) { | ||
| throw new Error(`Input file is invalid or does not exist: ${inputFile}`); | ||
| } | ||
| // Only CSV files are allowed for batch API | ||
| if (!inputFile.toLowerCase().endsWith('.csv')) { | ||
| throw new Error(`Only CSV file type is allowed: ${path.basename(inputFile)}`); | ||
| } | ||
| // Max file size allowed is 75MB | ||
| const fileSize = fs.statSync(inputFile).size; | ||
| if (fileSize > MAX_FILE_SIZE_BYTES) { | ||
| throw new Error(`Input file size exceeds the maximum allowed size of 75MB: ${fileSize}`); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Validates that the given file path exists and is not empty. | ||
| * @param {string} filePath - The file path to validate. | ||
| * @param {string} pathType - A description of the path type (e.g., "Input file"). | ||
| */ | ||
| static validatePathAndFile(filePath, pathType) { | ||
| if (!filePath || !filePath.trim()) { | ||
| throw new Error(`${pathType} path cannot be null or empty`); | ||
| } | ||
|
|
||
| // Normalize Windows-style paths that start with a slash before the drive letter | ||
| let normalizedPath = filePath; | ||
| if (path.sep === '\\' && normalizedPath.match(/^\/[A-Za-z]:.*/)) { | ||
| normalizedPath = normalizedPath.substring(1); | ||
| } | ||
|
|
||
| if (!fs.existsSync(normalizedPath)) { | ||
| throw new Error(`${pathType} does not exist: ${normalizedPath}`); | ||
| } | ||
| if (!fs.statSync(normalizedPath).isFile()) { | ||
| throw new Error(`${pathType} does not have valid file: ${normalizedPath}`); | ||
| } | ||
| try { | ||
| fs.accessSync(normalizedPath, fs.constants.R_OK); | ||
| } catch (err) { | ||
| throw new Error(`${pathType} is not readable: ${normalizedPath}`); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| module.exports = BatchUploadUtility; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.