mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-22 11:45:59 +01:00
[PM-1823] Defining the card brand according to its number (#5204)
* Defining the card brand according to its number * Moving cardBrandByPatterns function to Card View * Getting Card brand via cardBrandByPatterns function * Changing cardBrandByPatterns method to static. See: The reason being that someone wanting to use this outside of the onCardNumberChange would need to know to set the cc-number on the view-model before calling cardBrandByPatterns * Defining the card brand according to its number on Desktop * Defining the card brand according to its number on Web
This commit is contained in:
parent
ab260a3653
commit
c70d67bad3
@ -163,6 +163,7 @@
|
||||
class="monospaced"
|
||||
type="{{ showCardNumber ? 'text' : 'password' }}"
|
||||
name="Card.Number"
|
||||
(input)="onCardNumberChange()"
|
||||
[(ngModel)]="cipher.card.number"
|
||||
appInputVerbatim
|
||||
[readonly]="!cipher.edit && editMode"
|
||||
|
@ -148,6 +148,7 @@
|
||||
class="monospaced"
|
||||
type="{{ showCardNumber ? 'text' : 'password' }}"
|
||||
name="Card.Number"
|
||||
(input)="onCardNumberChange()"
|
||||
[(ngModel)]="cipher.card.number"
|
||||
appInputVerbatim
|
||||
[readonly]="!cipher.edit && editMode"
|
||||
|
@ -440,6 +440,7 @@
|
||||
class="form-control text-monospace"
|
||||
type="{{ showCardNumber ? 'text' : 'password' }}"
|
||||
name="Card.Number"
|
||||
(input)="onCardNumberChange()"
|
||||
[(ngModel)]="cipher.card.number"
|
||||
appInputVerbatim
|
||||
autocomplete="new-password"
|
||||
|
@ -368,6 +368,10 @@ export class AddEditComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
onCardNumberChange(): void {
|
||||
this.cipher.card.brand = CardView.getCardBrandByPatterns(this.cipher.card.number);
|
||||
}
|
||||
|
||||
getCardExpMonthDisplay() {
|
||||
return this.cardExpMonthOptions.find((x) => x.value == this.cipher.card.expMonth)?.name;
|
||||
}
|
||||
|
@ -81,4 +81,67 @@ export class CardView extends ItemView {
|
||||
static fromJSON(obj: Partial<Jsonify<CardView>>): CardView {
|
||||
return Object.assign(new CardView(), obj);
|
||||
}
|
||||
|
||||
// ref https://stackoverflow.com/a/5911300
|
||||
static getCardBrandByPatterns(cardNum: string): string {
|
||||
if (cardNum == null || typeof cardNum !== "string" || cardNum.trim() === "") {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Visa
|
||||
let re = new RegExp("^4");
|
||||
if (cardNum.match(re) != null) {
|
||||
return "Visa";
|
||||
}
|
||||
|
||||
// Mastercard
|
||||
// Updated for Mastercard 2017 BINs expansion
|
||||
if (
|
||||
/^(5[1-5][0-9]{14}|2(22[1-9][0-9]{12}|2[3-9][0-9]{13}|[3-6][0-9]{14}|7[0-1][0-9]{13}|720[0-9]{12}))$/.test(
|
||||
cardNum
|
||||
)
|
||||
) {
|
||||
return "Mastercard";
|
||||
}
|
||||
|
||||
// AMEX
|
||||
re = new RegExp("^3[47]");
|
||||
if (cardNum.match(re) != null) {
|
||||
return "Amex";
|
||||
}
|
||||
|
||||
// Discover
|
||||
re = new RegExp(
|
||||
"^(6011|622(12[6-9]|1[3-9][0-9]|[2-8][0-9]{2}|9[0-1][0-9]|92[0-5]|64[4-9])|65)"
|
||||
);
|
||||
if (cardNum.match(re) != null) {
|
||||
return "Discover";
|
||||
}
|
||||
|
||||
// Diners
|
||||
re = new RegExp("^36");
|
||||
if (cardNum.match(re) != null) {
|
||||
return "Diners Club";
|
||||
}
|
||||
|
||||
// Diners - Carte Blanche
|
||||
re = new RegExp("^30[0-5]");
|
||||
if (cardNum.match(re) != null) {
|
||||
return "Diners Club";
|
||||
}
|
||||
|
||||
// JCB
|
||||
re = new RegExp("^35(2[89]|[3-8][0-9])");
|
||||
if (cardNum.match(re) != null) {
|
||||
return "JCB";
|
||||
}
|
||||
|
||||
// Visa Electron
|
||||
re = new RegExp("^(4026|417500|4508|4844|491(3|7))");
|
||||
if (cardNum.match(re) != null) {
|
||||
return "Visa";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { SecureNoteType } from "@bitwarden/common/enums";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums/cipher-type";
|
||||
import { CardView } from "@bitwarden/common/vault/models/view/card.view";
|
||||
|
||||
import { ImportResult } from "../../models/import-result";
|
||||
import { BaseImporter } from "../base-importer";
|
||||
@ -48,7 +49,7 @@ export class AvastJsonImporter extends BaseImporter implements Importer {
|
||||
cipher.card.cardholderName = this.getValueOrDefault(value.holderName);
|
||||
cipher.card.number = this.getValueOrDefault(value.cardNumber);
|
||||
cipher.card.code = this.getValueOrDefault(value.cvv);
|
||||
cipher.card.brand = this.getCardBrand(cipher.card.number);
|
||||
cipher.card.brand = CardView.getCardBrandByPatterns(cipher.card.number);
|
||||
if (value.expirationDate != null) {
|
||||
if (value.expirationDate.month != null) {
|
||||
cipher.card.expMonth = value.expirationDate.month + "";
|
||||
|
@ -241,69 +241,6 @@ export abstract class BaseImporter {
|
||||
return str.split(this.newLineRegex);
|
||||
}
|
||||
|
||||
// ref https://stackoverflow.com/a/5911300
|
||||
protected getCardBrand(cardNum: string) {
|
||||
if (this.isNullOrWhitespace(cardNum)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Visa
|
||||
let re = new RegExp("^4");
|
||||
if (cardNum.match(re) != null) {
|
||||
return "Visa";
|
||||
}
|
||||
|
||||
// Mastercard
|
||||
// Updated for Mastercard 2017 BINs expansion
|
||||
if (
|
||||
/^(5[1-5][0-9]{14}|2(22[1-9][0-9]{12}|2[3-9][0-9]{13}|[3-6][0-9]{14}|7[0-1][0-9]{13}|720[0-9]{12}))$/.test(
|
||||
cardNum
|
||||
)
|
||||
) {
|
||||
return "Mastercard";
|
||||
}
|
||||
|
||||
// AMEX
|
||||
re = new RegExp("^3[47]");
|
||||
if (cardNum.match(re) != null) {
|
||||
return "Amex";
|
||||
}
|
||||
|
||||
// Discover
|
||||
re = new RegExp(
|
||||
"^(6011|622(12[6-9]|1[3-9][0-9]|[2-8][0-9]{2}|9[0-1][0-9]|92[0-5]|64[4-9])|65)"
|
||||
);
|
||||
if (cardNum.match(re) != null) {
|
||||
return "Discover";
|
||||
}
|
||||
|
||||
// Diners
|
||||
re = new RegExp("^36");
|
||||
if (cardNum.match(re) != null) {
|
||||
return "Diners Club";
|
||||
}
|
||||
|
||||
// Diners - Carte Blanche
|
||||
re = new RegExp("^30[0-5]");
|
||||
if (cardNum.match(re) != null) {
|
||||
return "Diners Club";
|
||||
}
|
||||
|
||||
// JCB
|
||||
re = new RegExp("^35(2[89]|[3-8][0-9])");
|
||||
if (cardNum.match(re) != null) {
|
||||
return "JCB";
|
||||
}
|
||||
|
||||
// Visa Electron
|
||||
re = new RegExp("^(4026|417500|4508|4844|491(3|7))");
|
||||
if (cardNum.match(re) != null) {
|
||||
return "Visa";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected setCardExpiration(cipher: CipherView, expiration: string): boolean {
|
||||
if (this.isNullOrWhitespace(expiration)) {
|
||||
return false;
|
||||
|
@ -136,7 +136,7 @@ export class DashlaneCsvImporter extends BaseImporter implements Importer {
|
||||
case "credit_card":
|
||||
cipher.card.cardholderName = row.account_name;
|
||||
cipher.card.number = row.cc_number;
|
||||
cipher.card.brand = this.getCardBrand(cipher.card.number);
|
||||
cipher.card.brand = CardView.getCardBrandByPatterns(cipher.card.number);
|
||||
cipher.card.code = row.code;
|
||||
this.setCardExpiration(cipher, `${row.expiration_month}/${row.expiration_year}`);
|
||||
|
||||
|
@ -134,7 +134,7 @@ export class DashlaneJsonImporter extends BaseImporter implements Importer {
|
||||
cipher.type = CipherType.Card;
|
||||
cipher.name = this.getValueOrDefault(obj.bank);
|
||||
cipher.card.number = this.getValueOrDefault(obj.cardNumber);
|
||||
cipher.card.brand = this.getCardBrand(cipher.card.number);
|
||||
cipher.card.brand = CardView.getCardBrandByPatterns(cipher.card.number);
|
||||
cipher.card.cardholderName = this.getValueOrDefault(obj.owner);
|
||||
if (!this.isNullOrWhitespace(cipher.card.brand)) {
|
||||
if (this.isNullOrWhitespace(cipher.name)) {
|
||||
|
@ -38,7 +38,7 @@ export class EncryptrCsvImporter extends BaseImporter implements Importer {
|
||||
cipher.card = new CardView();
|
||||
cipher.card.cardholderName = this.getValueOrDefault(value["Name on card"]);
|
||||
cipher.card.number = this.getValueOrDefault(value["Card Number"]);
|
||||
cipher.card.brand = this.getCardBrand(cipher.card.number);
|
||||
cipher.card.brand = CardView.getCardBrandByPatterns(cipher.card.number);
|
||||
cipher.card.code = this.getValueOrDefault(value.CVV);
|
||||
const expiry = this.getValueOrDefault(value.Expiry);
|
||||
if (!this.isNullOrWhitespace(expiry)) {
|
||||
|
@ -90,7 +90,7 @@ export class EnpassCsvImporter extends BaseImporter implements Importer {
|
||||
continue;
|
||||
} else if (fieldNameLower === "number" && this.isNullOrWhitespace(cipher.card.number)) {
|
||||
cipher.card.number = fieldValue;
|
||||
cipher.card.brand = this.getCardBrand(fieldValue);
|
||||
cipher.card.brand = CardView.getCardBrandByPatterns(cipher.card.number);
|
||||
continue;
|
||||
} else if (fieldNameLower === "cvc" && this.isNullOrWhitespace(cipher.card.code)) {
|
||||
cipher.card.code = fieldValue;
|
||||
|
@ -126,7 +126,7 @@ export class EnpassJsonImporter extends BaseImporter implements Importer {
|
||||
cipher.card.cardholderName = field.value;
|
||||
} else if (field.type === "ccNumber" && this.isNullOrWhitespace(cipher.card.number)) {
|
||||
cipher.card.number = field.value;
|
||||
cipher.card.brand = this.getCardBrand(cipher.card.number);
|
||||
cipher.card.brand = CardView.getCardBrandByPatterns(cipher.card.number);
|
||||
} else if (field.type === "ccCvc" && this.isNullOrWhitespace(cipher.card.code)) {
|
||||
cipher.card.code = field.value;
|
||||
} else if (field.type === "ccExpiry" && this.isNullOrWhitespace(cipher.card.expYear)) {
|
||||
|
@ -66,7 +66,7 @@ export class FSecureFskImporter extends BaseImporter implements Importer {
|
||||
cipher.card = new CardView();
|
||||
cipher.card.cardholderName = this.getValueOrDefault(entry.username);
|
||||
cipher.card.number = this.getValueOrDefault(entry.creditNumber);
|
||||
cipher.card.brand = this.getCardBrand(cipher.card.number);
|
||||
cipher.card.brand = CardView.getCardBrandByPatterns(cipher.card.number);
|
||||
cipher.card.code = this.getValueOrDefault(entry.creditCvv);
|
||||
if (!this.isNullOrWhitespace(entry.creditExpiry)) {
|
||||
if (!this.setCardExpiration(cipher, entry.creditExpiry)) {
|
||||
|
@ -122,7 +122,7 @@ export class LastPassCsvImporter extends BaseImporter implements Importer {
|
||||
card.cardholderName = this.getValueOrDefault(value.ccname);
|
||||
card.number = this.getValueOrDefault(value.ccnum);
|
||||
card.code = this.getValueOrDefault(value.cccsc);
|
||||
card.brand = this.getCardBrand(value.ccnum);
|
||||
card.brand = CardView.getCardBrandByPatterns(card.number);
|
||||
|
||||
if (!this.isNullOrWhitespace(value.ccexp) && value.ccexp.indexOf("-") > -1) {
|
||||
const ccexpParts = (value.ccexp as string).split("-");
|
||||
|
@ -72,7 +72,7 @@ export class MykiCsvImporter extends BaseImporter implements Importer {
|
||||
cipher.type = CipherType.Card;
|
||||
cipher.card.cardholderName = this.getValueOrDefault(value.cardName);
|
||||
cipher.card.number = this.getValueOrDefault(value.cardNumber);
|
||||
cipher.card.brand = this.getCardBrand(cipher.card.number);
|
||||
cipher.card.brand = CardView.getCardBrandByPatterns(cipher.card.number);
|
||||
cipher.card.expMonth = this.getValueOrDefault(value.exp_month);
|
||||
cipher.card.expYear = this.getValueOrDefault(value.exp_year);
|
||||
cipher.card.code = this.getValueOrDefault(value.cvv);
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { SecureNoteType } from "@bitwarden/common/enums";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums/cipher-type";
|
||||
import { CardView } from "@bitwarden/common/vault/models/view/card.view";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
import { LoginView } from "@bitwarden/common/vault/models/view/login.view";
|
||||
|
||||
@ -66,7 +67,7 @@ export class NordPassCsvImporter extends BaseImporter implements Importer {
|
||||
cipher.card.cardholderName = this.getValueOrDefault(record.cardholdername);
|
||||
cipher.card.number = this.getValueOrDefault(record.cardnumber);
|
||||
cipher.card.code = this.getValueOrDefault(record.cvc);
|
||||
cipher.card.brand = this.getCardBrand(cipher.card.number);
|
||||
cipher.card.brand = CardView.getCardBrandByPatterns(cipher.card.number);
|
||||
this.setCardExpiration(cipher, record.expirydate);
|
||||
break;
|
||||
|
||||
|
@ -198,7 +198,7 @@ export class OnePassword1PifImporter extends BaseImporter implements Importer {
|
||||
} else if (cipher.type === CipherType.Card) {
|
||||
if (this.isNullOrWhitespace(cipher.card.number) && fieldDesignation === "ccnum") {
|
||||
cipher.card.number = fieldValue;
|
||||
cipher.card.brand = this.getCardBrand(fieldValue);
|
||||
cipher.card.brand = CardView.getCardBrandByPatterns(cipher.card.number);
|
||||
return;
|
||||
} else if (this.isNullOrWhitespace(cipher.card.code) && fieldDesignation === "cvv") {
|
||||
cipher.card.code = fieldValue;
|
||||
|
@ -407,7 +407,7 @@ export class OnePassword1PuxImporter extends BaseImporter implements Importer {
|
||||
private fillCreditCard(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean {
|
||||
if (this.isNullOrWhitespace(cipher.card.number) && field.id === "ccnum") {
|
||||
cipher.card.number = fieldValue;
|
||||
cipher.card.brand = this.getCardBrand(fieldValue);
|
||||
cipher.card.brand = CardView.getCardBrandByPatterns(cipher.card.number);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { FieldType } from "@bitwarden/common/enums";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums/cipher-type";
|
||||
import { CardView } from "@bitwarden/common/vault/models/view/card.view";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
|
||||
import { ImportResult } from "../../models/import-result";
|
||||
@ -295,7 +296,7 @@ export abstract class OnePasswordCsvImporter extends BaseImporter implements Imp
|
||||
context.lowerProperty.includes("number")
|
||||
) {
|
||||
context.cipher.card.number = context.importRecord[context.property];
|
||||
context.cipher.card.brand = this.getCardBrand(context.cipher.card.number);
|
||||
context.cipher.card.brand = CardView.getCardBrandByPatterns(context.cipher.card.number);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -75,7 +75,7 @@ export class PasswordBossJsonImporter extends BaseImporter implements Importer {
|
||||
if (cipher.type === CipherType.Card) {
|
||||
if (property === "cardNumber") {
|
||||
cipher.card.number = val;
|
||||
cipher.card.brand = this.getCardBrand(val);
|
||||
cipher.card.brand = CardView.getCardBrandByPatterns(cipher.card.number);
|
||||
continue;
|
||||
} else if (property === "nameOnCard") {
|
||||
cipher.card.cardholderName = val;
|
||||
|
@ -31,7 +31,7 @@ export class RememBearCsvImporter extends BaseImporter implements Importer {
|
||||
cipher.card = new CardView();
|
||||
cipher.card.cardholderName = this.getValueOrDefault(value.cardholder);
|
||||
cipher.card.number = this.getValueOrDefault(value.number);
|
||||
cipher.card.brand = this.getCardBrand(cipher.card.number);
|
||||
cipher.card.brand = CardView.getCardBrandByPatterns(cipher.card.number);
|
||||
cipher.card.code = this.getValueOrDefault(value.verification);
|
||||
|
||||
try {
|
||||
|
@ -50,7 +50,7 @@ export class TrueKeyCsvImporter extends BaseImporter implements Importer {
|
||||
cipher.card = new CardView();
|
||||
cipher.card.cardholderName = this.getValueOrDefault(value.cardholder);
|
||||
cipher.card.number = this.getValueOrDefault(value.number);
|
||||
cipher.card.brand = this.getCardBrand(cipher.card.number);
|
||||
cipher.card.brand = CardView.getCardBrandByPatterns(cipher.card.number);
|
||||
if (!this.isNullOrWhitespace(value.expiryDate)) {
|
||||
try {
|
||||
const expDate = new Date(value.expiryDate);
|
||||
|
Loading…
Reference in New Issue
Block a user