Skip to content
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

feat(isIBAN): add white and blacklist options to the isIBAN validator #2235

Merged
merged 4 commits into from
Aug 3, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ Validator | Description
**isHexadecimal(str)** | check if the string is a hexadecimal number.
**isHexColor(str)** | check if the string is a hexadecimal color.
**isHSL(str)** | check if the string is an HSL (hue, saturation, lightness, optional alpha) color based on [CSS Colors Level 4 specification][CSS Colors Level 4 Specification].<br/><br/>Comma-separated format supported. Space-separated format supported with the exception of a few edge cases (ex: `hsl(200grad+.1%62%/1)`).
**isIBAN(str)** | check if the string is an IBAN (International Bank Account Number).
**isIBAN(str)** | check if the string is an IBAN (International Bank Account Number).<br/><br/>Also allows you to define a whitelist, when you only want to recieve IBAN codes from certain countries or a blacklist, removing some of them from the current list.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you elaborate on how you can define this? Like we explain the options object in other validators as well. Also with the list of supported country codes

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@WikiRik I have updated and put which attributes it will receive and also the values that can be used in each of them.

**isIdentityCard(str [, locale])** | check if the string is a valid identity card code.<br/><br/>`locale` is one of `['LK', 'PL', 'ES', 'FI', 'IN', 'IT', 'IR', 'MZ', 'NO', 'TH', 'zh-TW', 'he-IL', 'ar-LY', 'ar-TN', 'zh-CN', 'zh-HK']` OR `'any'`. If 'any' is used, function will check if any of the locales match.<br/><br/>Defaults to 'any'.
**isIMEI(str [, options]))** | check if the string is a valid [IMEI number][IMEI]. IMEI should be of format `###############` or `##-######-######-#`.<br/><br/>`options` is an object which can contain the keys `allow_hyphens`. Defaults to first format. If `allow_hyphens` is set to true, the validator will validate the second format.
**isIn(str, values)** | check if the string is in an array of allowed values.
Expand Down
50 changes: 46 additions & 4 deletions src/lib/isIBAN.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,25 @@ const ibanRegexThroughCountryCode = {
XK: /^(XK[0-9]{2})\d{16}$/,
};

/**
* Check if the country codes passed are valid using the
* ibanRegexThroughCountryCode as a reference
*
* @param {array} countryCodeArray
* @return {boolean}
*/

function hasOnlyValidCountryCodes(countryCodeArray) {
const countryCodeArrayFilteredWithObjectIbanCode = countryCodeArray
.filter(countryCode => !(countryCode in ibanRegexThroughCountryCode));

if (countryCodeArrayFilteredWithObjectIbanCode.length > 0) {
return false;
}

return true;
}

/**
* Check whether string has correct universal IBAN format
* The IBAN consists of up to 34 alphanumeric characters, as follows:
Expand All @@ -95,14 +114,37 @@ const ibanRegexThroughCountryCode = {
* NOTE: Permitted IBAN characters are: digits [0-9] and the 26 latin alphabetic [A-Z]
*
* @param {string} str - string under validation
* @param {object} options - object to pass the countries to be either whitelisted or blacklisted
* @return {boolean}
*/
function hasValidIbanFormat(str) {
function hasValidIbanFormat(str, options) {
// Strip white spaces and hyphens
const strippedStr = str.replace(/[\s\-]+/gi, '').toUpperCase();
const isoCountryCode = strippedStr.slice(0, 2).toUpperCase();

return (isoCountryCode in ibanRegexThroughCountryCode) &&
const isoCountryCodeInIbanRegexCodeObject = isoCountryCode in ibanRegexThroughCountryCode;

if (options.whitelist) {
if (!hasOnlyValidCountryCodes(options.whitelist)) {
throw new Error('One of the codes passed is invalid');
}

const isoCountryCodeInWhiteList = options.whitelist.includes(isoCountryCode);

if (!isoCountryCodeInWhiteList) {
throw new Error('IBAN code does not belong to one of the countries listed on whitelist!');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: the pattern so far has not been to throw errors, just true/false. I'm not sure how to go about this one...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess we can continue with the true/false approach and let the user decide how to treat this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}
}

if (options.blacklist) {
const isoCountryCodeInBlackList = options.blacklist.includes(isoCountryCode);

if (isoCountryCodeInBlackList) {
throw new Error('IBAN code belongs to one of the countries listed on blacklist!');
}
}

return (isoCountryCodeInIbanRegexCodeObject) &&
ibanRegexThroughCountryCode[isoCountryCode].test(strippedStr);
}

Expand Down Expand Up @@ -130,10 +172,10 @@ function hasValidIbanChecksum(str) {
return remainder === 1;
}

export default function isIBAN(str) {
export default function isIBAN(str, options = {}) {
assertString(str);

return hasValidIbanFormat(str) && hasValidIbanChecksum(str);
return hasValidIbanFormat(str, options) && hasValidIbanChecksum(str);
}

export const locales = Object.keys(ibanRegexThroughCountryCode);
79 changes: 79 additions & 0 deletions test/validators.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5250,6 +5250,85 @@ describe('Validators', () => {
'FR763000600001123456!!🤨7890189@',
],
});
test({
validator: 'isIBAN',
args: [{ whitelist: ['DK', 'GB'] }],
valid: [
'DK5000400440116243',
'GB29NWBK60161331926819',
],
error: [
'BE71 0961 2345 6769',
'FR76 3000 6000 0112 3456 7890 189',
'DE91 1000 0000 0123 4567 89',
'GR96 0810 0010 0000 0123 4567 890',
'RO09 BCYP 0000 0012 3456 7890',
'SA44 2000 0001 2345 6789 1234',
'ES79 2100 0813 6101 2345 6789',
'XX22YYY1234567890123',
'FR14 2004 1010 0505 0001 3',
'FR7630006000011234567890189@',
'FR7630006000011234567890189😅',
'FR763000600001123456!!🤨7890189@',
],
});
test({
validator: 'isIBAN',
args: [{ whitelist: ['XX', 'AA'] }],
error: [
'DK5000400440116243',
'GB29NWBK60161331926819',
'BE71 0961 2345 6769',
'FR76 3000 6000 0112 3456 7890 189',
'DE91 1000 0000 0123 4567 89',
'GR96 0810 0010 0000 0123 4567 890',
'RO09 BCYP 0000 0012 3456 7890',
'SA44 2000 0001 2345 6789 1234',
'ES79 2100 0813 6101 2345 6789',
'XX22YYY1234567890123',
'FR14 2004 1010 0505 0001 3',
'FR7630006000011234567890189@',
'FR7630006000011234567890189😅',
'FR763000600001123456!!🤨7890189@',
],
});
test({
validator: 'isIBAN',
args: [{ blacklist: ['IT'] }],
valid: [
'SC52BAHL01031234567890123456USD',
'LC14BOSL123456789012345678901234',
'MT31MALT01100000000000000000123',
'SV43ACAT00000000000000123123',
'EG800002000156789012345180002',
'BE71 0961 2345 6769',
'FR76 3000 6000 0112 3456 7890 189',
'DE91 1000 0000 0123 4567 89',
'GR96 0810 0010 0000 0123 4567 890',
'RO09 BCYP 0000 0012 3456 7890',
'SA44 2000 0001 2345 6789 1234',
'ES79 2100 0813 6101 2345 6789',
'CH56 0483 5012 3456 7800 9',
'GB98 MIDL 0700 9312 3456 78',
'IL170108000000012612345',
'JO71CBJO0000000000001234567890',
'TR320010009999901234567890',
'BR1500000000000010932840814P2',
'LB92000700000000123123456123',
'IR200170000000339545727003',
'MZ97123412341234123412341',
],
error: [
'IT60X0542811101000000123456',
],
invalid: [
'XX22YYY1234567890123',
'FR14 2004 1010 0505 0001 3',
'FR7630006000011234567890189@',
'FR7630006000011234567890189😅',
'FR763000600001123456!!🤨7890189@',
],
});
});

it('should validate BIC codes', () => {
Expand Down