bridge-node-server/node_server/utils/anon.js
Martin Donnelly 57bd6c8e6a init
2018-06-24 21:15:03 +01:00

273 lines
7.6 KiB
JavaScript

/**
* Support utilities for anonymising data
*
*/
'use strict';
const _ = require('lodash');
const utils = require('../ComServe/utils.js');
module.exports = {
anonymisePhoneNumber,
anonymiseAccountNumber,
anonymiseSortCode,
anonymiseWorldpayServiceKey,
anonymiseCardPAN,
anonymiseMerchantID,
anonymiseAccount,
anonymiseDevice,
anonymiseAddress,
anonymiseKYC
};
/**
* Helper function to anonymise a phone number. This converts something like
* +441506592361
* into
* +44 1*** ***361
*
* @param {string} phoneNumber - the phone number string
* @returns {string} - the anonymised number
*/
function anonymisePhoneNumber(phoneNumber) {
let tempString;
/**
* We display 8 digits, so make sure we aren't returning almost everything.
*/
if (phoneNumber.length > 10) {
tempString =
phoneNumber.substr(0, 3) +
' ' +
phoneNumber.substr(3, 1) +
'*** ***' +
phoneNumber.substr(-3);
} else {
/**
* To short to be a good number, so just return an empty string (as if there was no number)
*/
tempString = '';
}
return tempString;
}
/**
* Anonymises an account number which is passed as a string. It retains the last 3 characters
* and adds 5 stars at the beginning regardless of actual length.
* - AccountNumber 12345678 => *****678
*
* @type {Function} anonymiseAccountNumber
* @param {!string} accountNumber - Expected input is an 8 digit string.
* @returns {!string} Anonymised account number.
*/
function anonymiseAccountNumber(accountNumber) {
if (!accountNumber) {
return '';
}
return ('*****' + accountNumber.substr(-3));
}
/**
* Anonymises a sort code which is passed as a string. It retains the last 2 characters
* and adds 4 stars and dashes at the beginning regardless of actual length.
* - SortCode 12-34-56 => **-**-56
*
* @type {Function} anonymiseSortCode
* @param {!string} sortCode - Expected input is an 8 character string.
* @returns {!string} Anonymised sort code number.
*/
function anonymiseSortCode(sortCode) {
if (!sortCode) {
return '';
}
return ('**-**-' + sortCode.substr(-2));
}
/**
* Anonymises a worldpay service key which is passed as a string. It retains the first 1 and last 4 characters,
* replaces all Hex characters with stars retaining dashes.
* - serviceKey T_S_713d2a60-a20b-4047-bc3a-3e863a11e414 => T_S_********-****-****-****-********e414
* The function does not work with 8 or less characters so simply returns what it received.
*
* @type {Function} anonymiseWorldpayServiceKey
* @param {!string} serviceKey - Expected input is an 40 character string.
* @returns {!string} Anonymised card PAN.
*/
function anonymiseWorldpayServiceKey(serviceKey) {
if (!serviceKey) {
throw new Error('service key not set');
}
if ((/^(?:T_S_|T_C_|L_S_|L_C_)[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(serviceKey))) {
let anonServiceKey = serviceKey.slice(0);
anonServiceKey = anonServiceKey.substr(0, 4) + '********-****-****-****-********' + anonServiceKey.substr(anonServiceKey.length - 4);
return anonServiceKey;
} else {
throw new Error('service key not consistent with a Worldpay service key');
}
}
/**
* Anonymises a card PAN which is passed as a string. It retains the first 1 and last 3 characters,
* adds stars and spaces after every quad in the middle regardless of actual length.
* - CardPAN 0123 4567 8901 2345=> 0*** **** **** *345
* The function does not work with 4 or less characters so simply returns what it received.
*
* @type {Function} anonymiseCardPAN
* @param {!string} cardPAN - Expected input is an 8 character string.
* @returns {!string} Anonymised card PAN.
*/
function anonymiseCardPAN(cardPAN) {
if (!cardPAN) {
throw new Error('cardPAN not set');
}
const tempCardPAN = cardPAN.slice(0).replace(/ /g, '');
if (tempCardPAN.length < 5) {
return tempCardPAN;
}
/**
* CardPAN is not always 16 digits.
*/
let anonPAN = tempCardPAN.substr(0, 1);
for (let xx = 1; xx < tempCardPAN.length; xx++) {
if ((xx % 4) === 0) {
anonPAN += ' ';
}
if (xx > (tempCardPAN.length - 4)) {
anonPAN += tempCardPAN.substr(xx, 1);
} else {
anonPAN += '*';
}
}
return anonPAN;
}
/**
* Anonymises a merchant acquirer ID which is passed as a string. It retains the last 3 characters
* and adds 5 stars at the beginning regardless of actual length.
* - AcquirerMerchantID ABCDEFGH => *****FGH
*
* @type {Function} anonymiseMerchantID
* @param {!string} merchantID - Expected input is an 8 digit string.
* @returns {!string} Anonymised merchant ID.
*/
function anonymiseMerchantID(merchantID) {
if (!merchantID) {
return '';
}
return ('*****' + merchantID.substr(-3));
}
/**
* Anonymises the given account by:
* 1. Deleting any fields that are not appropriate for this type of account
* 2. Anonymising any remaining fields.
*
* @param {Object} account - the account object to anonymise
* W074 cyclomatic complexity problem ignored on line. Suspected software error.
*/
function anonymiseAccount(account) { // jshint ignore:line
if (!account) {
return;
}
const fields = ['AccountNumber', 'SortCode', 'CardPAN', 'AcquirerMerchantID'];
const keep = [];
switch (account.AccountType) {
case utils.PaymentInstrumentType.CREDIT_DEBIT_PAYMENT_CARD:
keep.push('CardPAN');
break;
case 'Bank Account':
keep.push('AccountNumber');
keep.push('SortCode');
break;
case 'Credit/Debit Receiving Account':
keep.push('AcquirerMerchantID');
break;
default:
// Not a known type, so delete everything.
break;
}
_.forEach(
fields,
(value) => {
if (keep.indexOf(value) === -1) {
// Not in the keep list, so delete
delete account[value];
}
});
/**
* Now anonymise anything that's left
*/
if (account.AccountNumber !== undefined) {
account.AccountNumber = anonymiseAccountNumber(account.AccountNumber);
}
if (account.SortCode !== undefined) {
account.SortCode = anonymiseSortCode(account.SortCode);
}
if (account.AcquirerMerchantID !== undefined) {
account.AcquirerMerchantID = anonymiseMerchantID(account.AcquirerMerchantID);
}
}
/**
* Anonymises the given device by:
* 1. Anonymising fields as follows:
* - DeviceNumber => +44 7*** ***234
*
* @param {Object} device - the device object to anonymise
*/
function anonymiseDevice(device) {
if (!device) {
return;
}
/**
* Anonymise fields
*/
if (device.DeviceNumber !== undefined) {
device.DeviceNumber = anonymisePhoneNumber(device.DeviceNumber);
}
}
/**
* Anonymises the given address by:
* 1. Anonymising fields as follows:
* - PhoneNumber => +44 1*** ***234
*
* @param {Object} address - the address object to anonymise
*/
function anonymiseAddress(address) {
if (!address) {
return;
}
/**
* Anonymise fields
*/
if (address.PhoneNumber) {
address.PhoneNumber = anonymisePhoneNumber(address.PhoneNumber);
}
}
/**
* Anonymises KYC data:
* 1. Remove the date of birth
*
* @param {Object} kyc - The object to be anonymised
*/
function anonymiseKYC(kyc) {
if (!kyc) {
return;
}
kyc.DateOfBirth = null;
}