mirror of
https://github.com/bitwarden/browser.git
synced 2025-02-25 02:51:59 +01:00
[PM-11882] Handled identity item and unsupported items during ProtonPass import. (#10967)
This commit is contained in:
parent
2d7fb035d4
commit
0f3d8a6f89
libs/importer
spec
src/importers/protonpass
@ -85,7 +85,7 @@ describe("Protonpass Json Importer", () => {
|
||||
// "My Secure Note" is assigned to folder "Personal"
|
||||
expect(result.folderRelationships[1]).toEqual([1, 0]);
|
||||
// "Other vault login" is assigned to folder "Test"
|
||||
expect(result.folderRelationships[3]).toEqual([3, 1]);
|
||||
expect(result.folderRelationships[4]).toEqual([4, 1]);
|
||||
});
|
||||
|
||||
it("should create collections if part of an organization", async () => {
|
||||
@ -102,7 +102,7 @@ describe("Protonpass Json Importer", () => {
|
||||
// "My Secure Note" is assigned to folder "Personal"
|
||||
expect(result.collectionRelationships[1]).toEqual([1, 0]);
|
||||
// "Other vault login" is assigned to folder "Test"
|
||||
expect(result.collectionRelationships[3]).toEqual([3, 1]);
|
||||
expect(result.collectionRelationships[4]).toEqual([4, 1]);
|
||||
});
|
||||
|
||||
it("should not add deleted items", async () => {
|
||||
@ -114,7 +114,7 @@ describe("Protonpass Json Importer", () => {
|
||||
expect(cipher.name).not.toBe("My Deleted Note");
|
||||
}
|
||||
|
||||
expect(ciphers.length).toBe(4);
|
||||
expect(ciphers.length).toBe(5);
|
||||
});
|
||||
|
||||
it("should set favorites", async () => {
|
||||
@ -126,4 +126,97 @@ describe("Protonpass Json Importer", () => {
|
||||
expect(ciphers[1].favorite).toBe(false);
|
||||
expect(ciphers[2].favorite).toBe(true);
|
||||
});
|
||||
|
||||
it("should skip unsupported items", async () => {
|
||||
const testDataJson = JSON.stringify(testData);
|
||||
const result = await importer.parse(testDataJson);
|
||||
expect(result != null).toBe(true);
|
||||
|
||||
const ciphers = result.ciphers;
|
||||
expect(ciphers.length).toBe(5);
|
||||
expect(ciphers[4].type).toEqual(CipherType.Login);
|
||||
});
|
||||
|
||||
it("should parse identity data", async () => {
|
||||
const testDataJson = JSON.stringify(testData);
|
||||
const result = await importer.parse(testDataJson);
|
||||
expect(result != null).toBe(true);
|
||||
|
||||
result.ciphers.shift();
|
||||
result.ciphers.shift();
|
||||
result.ciphers.shift();
|
||||
|
||||
const cipher = result.ciphers.shift();
|
||||
expect(cipher.type).toEqual(CipherType.Identity);
|
||||
expect(cipher.identity.firstName).toBe("Test");
|
||||
expect(cipher.identity.middleName).toBe("1");
|
||||
expect(cipher.identity.lastName).toBe("1");
|
||||
expect(cipher.identity.email).toBe("test@gmail.com");
|
||||
expect(cipher.identity.phone).toBe("7507951789");
|
||||
expect(cipher.identity.company).toBe("Bitwarden");
|
||||
expect(cipher.identity.ssn).toBe("98378264782");
|
||||
expect(cipher.identity.passportNumber).toBe("7173716378612");
|
||||
expect(cipher.identity.licenseNumber).toBe("21234");
|
||||
expect(cipher.identity.address1).toBe("Bitwarden");
|
||||
expect(cipher.identity.address2).toBe("23 Street");
|
||||
expect(cipher.identity.address3).toBe("12th Foor Test County");
|
||||
expect(cipher.identity.city).toBe("New York");
|
||||
expect(cipher.identity.state).toBe("Test");
|
||||
expect(cipher.identity.postalCode).toBe("4038456");
|
||||
expect(cipher.identity.country).toBe("US");
|
||||
|
||||
expect(cipher.fields.length).toEqual(13);
|
||||
|
||||
expect(cipher.fields.at(0).name).toEqual("gender");
|
||||
expect(cipher.fields.at(0).value).toEqual("Male");
|
||||
expect(cipher.fields.at(0).type).toEqual(FieldType.Text);
|
||||
|
||||
expect(cipher.fields.at(1).name).toEqual("TestPersonal");
|
||||
expect(cipher.fields.at(1).value).toEqual("Personal");
|
||||
expect(cipher.fields.at(1).type).toEqual(FieldType.Text);
|
||||
|
||||
expect(cipher.fields.at(2).name).toEqual("TestAddress");
|
||||
expect(cipher.fields.at(2).value).toEqual("Address");
|
||||
expect(cipher.fields.at(2).type).toEqual(FieldType.Text);
|
||||
|
||||
expect(cipher.fields.at(3).name).toEqual("xHandle");
|
||||
expect(cipher.fields.at(3).value).toEqual("@twiter");
|
||||
expect(cipher.fields.at(3).type).toEqual(FieldType.Text);
|
||||
|
||||
expect(cipher.fields.at(4).name).toEqual("secondPhoneNumber");
|
||||
expect(cipher.fields.at(4).value).toEqual("243538978");
|
||||
expect(cipher.fields.at(4).type).toEqual(FieldType.Text);
|
||||
|
||||
expect(cipher.fields.at(5).name).toEqual("instagram");
|
||||
expect(cipher.fields.at(5).value).toEqual("@insta");
|
||||
expect(cipher.fields.at(5).type).toEqual(FieldType.Text);
|
||||
|
||||
expect(cipher.fields.at(6).name).toEqual("TestContact");
|
||||
expect(cipher.fields.at(6).value).toEqual("Contact");
|
||||
expect(cipher.fields.at(6).type).toEqual(FieldType.Hidden);
|
||||
|
||||
expect(cipher.fields.at(7).name).toEqual("jobTitle");
|
||||
expect(cipher.fields.at(7).value).toEqual("Engineer");
|
||||
expect(cipher.fields.at(7).type).toEqual(FieldType.Text);
|
||||
|
||||
expect(cipher.fields.at(8).name).toEqual("workPhoneNumber");
|
||||
expect(cipher.fields.at(8).value).toEqual("78236476238746");
|
||||
expect(cipher.fields.at(8).type).toEqual(FieldType.Text);
|
||||
|
||||
expect(cipher.fields.at(9).name).toEqual("TestWork");
|
||||
expect(cipher.fields.at(9).value).toEqual("Work");
|
||||
expect(cipher.fields.at(9).type).toEqual(FieldType.Hidden);
|
||||
|
||||
expect(cipher.fields.at(10).name).toEqual("TestSection");
|
||||
expect(cipher.fields.at(10).value).toEqual("Section");
|
||||
expect(cipher.fields.at(10).type).toEqual(FieldType.Text);
|
||||
|
||||
expect(cipher.fields.at(11).name).toEqual("TestSectionHidden");
|
||||
expect(cipher.fields.at(11).value).toEqual("SectionHidden");
|
||||
expect(cipher.fields.at(11).type).toEqual(FieldType.Hidden);
|
||||
|
||||
expect(cipher.fields.at(12).name).toEqual("TestExtra");
|
||||
expect(cipher.fields.at(12).value).toEqual("Extra");
|
||||
expect(cipher.fields.at(12).type).toEqual(FieldType.Text);
|
||||
});
|
||||
});
|
||||
|
@ -138,6 +138,144 @@ export const testData: ProtonPassJsonFile = {
|
||||
modifyTime: 1689182908,
|
||||
pinned: false,
|
||||
},
|
||||
{
|
||||
itemId:
|
||||
"gliCOyyJOsoBf5QIijvCF4QsPij3q_MR4nCXZ2sXm7YCJCfHjrRD_p2XG9vLsaytErsQvMhcLISVS7q8-7SCkg==",
|
||||
shareId:
|
||||
"TpawpLbs1nuUlQUCtgKZgb3zgAvbrGrOaqOylKqVe_RLROEyUvMq8_ZEuGw73PGRUSr89iNtQ2NosuggP54nwA==",
|
||||
data: {
|
||||
metadata: {
|
||||
name: "Identity",
|
||||
note: "",
|
||||
itemUuid: "c2e52768",
|
||||
},
|
||||
extraFields: [
|
||||
{
|
||||
fieldName: "TestExtra",
|
||||
type: "text",
|
||||
data: {
|
||||
content: "Extra",
|
||||
},
|
||||
},
|
||||
],
|
||||
type: "identity",
|
||||
content: {
|
||||
fullName: "Test 1",
|
||||
email: "test@gmail.com",
|
||||
phoneNumber: "7507951789",
|
||||
firstName: "Test",
|
||||
middleName: "1",
|
||||
lastName: "Test",
|
||||
birthdate: "",
|
||||
gender: "Male",
|
||||
extraPersonalDetails: [
|
||||
{
|
||||
fieldName: "TestPersonal",
|
||||
type: "text",
|
||||
data: {
|
||||
content: "Personal",
|
||||
},
|
||||
},
|
||||
],
|
||||
organization: "Bitwarden",
|
||||
streetAddress: "23 Street",
|
||||
zipOrPostalCode: "4038456",
|
||||
city: "New York",
|
||||
stateOrProvince: "Test",
|
||||
countryOrRegion: "US",
|
||||
floor: "12th Foor",
|
||||
county: "Test County",
|
||||
extraAddressDetails: [
|
||||
{
|
||||
fieldName: "TestAddress",
|
||||
type: "text",
|
||||
data: {
|
||||
content: "Address",
|
||||
},
|
||||
},
|
||||
],
|
||||
socialSecurityNumber: "98378264782",
|
||||
passportNumber: "7173716378612",
|
||||
licenseNumber: "21234",
|
||||
website: "",
|
||||
xHandle: "@twiter",
|
||||
secondPhoneNumber: "243538978",
|
||||
linkedin: "",
|
||||
reddit: "",
|
||||
facebook: "",
|
||||
yahoo: "",
|
||||
instagram: "@insta",
|
||||
extraContactDetails: [
|
||||
{
|
||||
fieldName: "TestContact",
|
||||
type: "hidden",
|
||||
data: {
|
||||
content: "Contact",
|
||||
},
|
||||
},
|
||||
],
|
||||
company: "Bitwarden",
|
||||
jobTitle: "Engineer",
|
||||
personalWebsite: "",
|
||||
workPhoneNumber: "78236476238746",
|
||||
workEmail: "",
|
||||
extraWorkDetails: [
|
||||
{
|
||||
fieldName: "TestWork",
|
||||
type: "hidden",
|
||||
data: {
|
||||
content: "Work",
|
||||
},
|
||||
},
|
||||
],
|
||||
extraSections: [
|
||||
{
|
||||
sectionName: "TestSection",
|
||||
sectionFields: [
|
||||
{
|
||||
fieldName: "TestSection",
|
||||
type: "text",
|
||||
data: {
|
||||
content: "Section",
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: "TestSectionHidden",
|
||||
type: "hidden",
|
||||
data: {
|
||||
content: "SectionHidden",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
state: 1,
|
||||
aliasEmail: null,
|
||||
contentFormatVersion: 6,
|
||||
createTime: 1725707298,
|
||||
modifyTime: 1725707298,
|
||||
pinned: false,
|
||||
},
|
||||
{
|
||||
itemId:
|
||||
"WTKLZtKfHIC3Gv7gRXUANifNjj0gN3P_52I4MznAzig9GSb_OgJ0qcZ8taOZyfsFTLOWBslXwI-HSMWXVmnKzQ==",
|
||||
shareId:
|
||||
"TpawpLbs1nuUlQUCtgKZgb3zgAvbrGrOaqOylKqVe_RLROEyUvMq8_ZEuGw73PGRUSr89iNtQ2NosuggP54nwA==",
|
||||
data: {
|
||||
metadata: { name: "Alias", note: "", itemUuid: "576f14fa" },
|
||||
extraFields: [],
|
||||
type: "alias",
|
||||
content: {},
|
||||
},
|
||||
state: 1,
|
||||
aliasEmail: "alias.removing005@passinbox.com",
|
||||
contentFormatVersion: 6,
|
||||
createTime: 1725708208,
|
||||
modifyTime: 1725708208,
|
||||
pinned: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
REDACTED_VAULT_ID_B: {
|
||||
|
@ -0,0 +1,66 @@
|
||||
import { processNames } from "./protonpass-import-utils";
|
||||
|
||||
describe("processNames", () => {
|
||||
it("should use only fullName to map names if it contains at least three words, ignoring individual name fields", () => {
|
||||
const result = processNames("Alice Beth Carter", "Kevin", "", "");
|
||||
expect(result).toEqual({
|
||||
mappedFirstName: "Alice",
|
||||
mappedMiddleName: "Beth",
|
||||
mappedLastName: "Carter",
|
||||
});
|
||||
});
|
||||
|
||||
it("should map extra words to the middle name if fullName contains more than three words", () => {
|
||||
const result = processNames("Alice Beth Middle Carter", "", "", "");
|
||||
expect(result).toEqual({
|
||||
mappedFirstName: "Alice",
|
||||
mappedMiddleName: "Beth Middle",
|
||||
mappedLastName: "Carter",
|
||||
});
|
||||
});
|
||||
|
||||
it("should map names correctly even if fullName has words separated by more than one space", () => {
|
||||
const result = processNames("Alice Carter", "", "", "");
|
||||
expect(result).toEqual({
|
||||
mappedFirstName: "Alice",
|
||||
mappedMiddleName: "",
|
||||
mappedLastName: "Carter",
|
||||
});
|
||||
});
|
||||
|
||||
it("should handle a single name in fullName and use middleName and lastName to populate rest of names", () => {
|
||||
const result = processNames("Alice", "", "Beth", "Carter");
|
||||
expect(result).toEqual({
|
||||
mappedFirstName: "Alice",
|
||||
mappedMiddleName: "Beth",
|
||||
mappedLastName: "Carter",
|
||||
});
|
||||
});
|
||||
|
||||
it("should correctly map fullName when it only contains two words", () => {
|
||||
const result = processNames("Alice Carter", "", "", "");
|
||||
expect(result).toEqual({
|
||||
mappedFirstName: "Alice",
|
||||
mappedMiddleName: "",
|
||||
mappedLastName: "Carter",
|
||||
});
|
||||
});
|
||||
|
||||
it("should map middle name from middleName if fullName only contains two words", () => {
|
||||
const result = processNames("Alice Carter", "", "Beth", "");
|
||||
expect(result).toEqual({
|
||||
mappedFirstName: "Alice",
|
||||
mappedMiddleName: "Beth",
|
||||
mappedLastName: "Carter",
|
||||
});
|
||||
});
|
||||
|
||||
it("should fall back to firstName, middleName, and lastName if fullName is empty", () => {
|
||||
const result = processNames("", "Alice", "Beth", "Carter");
|
||||
expect(result).toEqual({
|
||||
mappedFirstName: "Alice",
|
||||
mappedMiddleName: "Beth",
|
||||
mappedLastName: "Carter",
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,21 @@
|
||||
export function processNames(
|
||||
fullname: string | null,
|
||||
firstname: string | null,
|
||||
middlename: string | null,
|
||||
lastname: string | null,
|
||||
) {
|
||||
let mappedFirstName = firstname;
|
||||
let mappedMiddleName = middlename;
|
||||
let mappedLastName = lastname;
|
||||
|
||||
if (fullname) {
|
||||
const parts = fullname.trim().split(/\s+/);
|
||||
|
||||
// Assign parts to first, middle, and last name based on the number of parts
|
||||
mappedFirstName = parts[0] || firstname;
|
||||
mappedLastName = parts.length > 1 ? parts[parts.length - 1] : lastname;
|
||||
mappedMiddleName = parts.length > 2 ? parts.slice(1, -1).join(" ") : middlename;
|
||||
}
|
||||
|
||||
return { mappedFirstName, mappedMiddleName, mappedLastName };
|
||||
}
|
@ -1,24 +1,110 @@
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { FieldType, SecureNoteType, CipherType } from "@bitwarden/common/vault/enums";
|
||||
import { CardView } from "@bitwarden/common/vault/models/view/card.view";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
import { IdentityView } from "@bitwarden/common/vault/models/view/identity.view";
|
||||
import { SecureNoteView } from "@bitwarden/common/vault/models/view/secure-note.view";
|
||||
|
||||
import { ImportResult } from "../../models/import-result";
|
||||
import { BaseImporter } from "../base-importer";
|
||||
import { Importer } from "../importer";
|
||||
|
||||
import { processNames } from "./protonpass-import-utils";
|
||||
import {
|
||||
ProtonPassCreditCardItemContent,
|
||||
ProtonPassIdentityItemContent,
|
||||
ProtonPassIdentityItemExtraSection,
|
||||
ProtonPassItemExtraField,
|
||||
ProtonPassItemState,
|
||||
ProtonPassJsonFile,
|
||||
ProtonPassLoginItemContent,
|
||||
} from "./types/protonpass-json-type";
|
||||
|
||||
export class ProtonPassJsonImporter extends BaseImporter implements Importer {
|
||||
private mappedIdentityItemKeys = [
|
||||
"fullName",
|
||||
"firstName",
|
||||
"middleName",
|
||||
"lastName",
|
||||
"email",
|
||||
"phoneNumber",
|
||||
"company",
|
||||
"socialSecurityNumber",
|
||||
"passportNumber",
|
||||
"licenseNumber",
|
||||
"organization",
|
||||
"streetAddress",
|
||||
"floor",
|
||||
"county",
|
||||
"city",
|
||||
"stateOrProvince",
|
||||
"zipOrPostalCode",
|
||||
"countryOrRegion",
|
||||
];
|
||||
|
||||
private identityItemExtraFieldsKeys = [
|
||||
"extraPersonalDetails",
|
||||
"extraAddressDetails",
|
||||
"extraContactDetails",
|
||||
"extraWorkDetails",
|
||||
"extraSections",
|
||||
];
|
||||
|
||||
constructor(private i18nService: I18nService) {
|
||||
super();
|
||||
}
|
||||
|
||||
private processIdentityItemUnmappedAndExtraFields(
|
||||
cipher: CipherView,
|
||||
identityItem: ProtonPassIdentityItemContent,
|
||||
) {
|
||||
Object.keys(identityItem).forEach((key) => {
|
||||
if (
|
||||
!this.mappedIdentityItemKeys.includes(key) &&
|
||||
!this.identityItemExtraFieldsKeys.includes(key)
|
||||
) {
|
||||
this.processKvp(
|
||||
cipher,
|
||||
key,
|
||||
identityItem[key as keyof ProtonPassIdentityItemContent] as string,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.identityItemExtraFieldsKeys.includes(key)) {
|
||||
if (key !== "extraSections") {
|
||||
const extraFields = identityItem[
|
||||
key as keyof ProtonPassIdentityItemContent
|
||||
] as ProtonPassItemExtraField[];
|
||||
|
||||
extraFields?.forEach((extraField) => {
|
||||
this.processKvp(
|
||||
cipher,
|
||||
extraField.fieldName,
|
||||
extraField.data.content,
|
||||
extraField.type === "hidden" ? FieldType.Hidden : FieldType.Text,
|
||||
);
|
||||
});
|
||||
} else {
|
||||
const extraSections = identityItem[
|
||||
key as keyof ProtonPassIdentityItemContent
|
||||
] as ProtonPassIdentityItemExtraSection[];
|
||||
|
||||
extraSections?.forEach((extraSection) => {
|
||||
extraSection.sectionFields?.forEach((extraField) => {
|
||||
this.processKvp(
|
||||
cipher,
|
||||
extraField.fieldName,
|
||||
extraField.data.content,
|
||||
extraField.type === "hidden" ? FieldType.Hidden : FieldType.Text,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
parse(data: string): Promise<ImportResult> {
|
||||
const result = new ImportResult();
|
||||
const results: ProtonPassJsonFile = JSON.parse(data);
|
||||
@ -38,7 +124,6 @@ export class ProtonPassJsonImporter extends BaseImporter implements Importer {
|
||||
if (item.state == ProtonPassItemState.TRASHED) {
|
||||
continue;
|
||||
}
|
||||
this.processFolder(result, vault.name);
|
||||
|
||||
const cipher = this.initLoginCipher();
|
||||
cipher.name = this.getValueOrDefault(item.data.metadata.name, "--");
|
||||
@ -96,8 +181,55 @@ export class ProtonPassJsonImporter extends BaseImporter implements Importer {
|
||||
|
||||
break;
|
||||
}
|
||||
case "identity": {
|
||||
const identityContent = item.data.content as ProtonPassIdentityItemContent;
|
||||
cipher.type = CipherType.Identity;
|
||||
cipher.identity = new IdentityView();
|
||||
|
||||
const { mappedFirstName, mappedMiddleName, mappedLastName } = processNames(
|
||||
this.getValueOrDefault(identityContent.fullName),
|
||||
this.getValueOrDefault(identityContent.firstName),
|
||||
this.getValueOrDefault(identityContent.middleName),
|
||||
this.getValueOrDefault(identityContent.lastName),
|
||||
);
|
||||
cipher.identity.firstName = mappedFirstName;
|
||||
cipher.identity.middleName = mappedMiddleName;
|
||||
cipher.identity.lastName = mappedLastName;
|
||||
|
||||
cipher.identity.email = this.getValueOrDefault(identityContent.email);
|
||||
cipher.identity.phone = this.getValueOrDefault(identityContent.phoneNumber);
|
||||
cipher.identity.company = this.getValueOrDefault(identityContent.company);
|
||||
cipher.identity.ssn = this.getValueOrDefault(identityContent.socialSecurityNumber);
|
||||
cipher.identity.passportNumber = this.getValueOrDefault(identityContent.passportNumber);
|
||||
cipher.identity.licenseNumber = this.getValueOrDefault(identityContent.licenseNumber);
|
||||
|
||||
const address3 =
|
||||
`${identityContent.floor ?? ""} ${identityContent.county ?? ""}`.trim();
|
||||
cipher.identity.address1 = this.getValueOrDefault(identityContent.organization);
|
||||
cipher.identity.address2 = this.getValueOrDefault(identityContent.streetAddress);
|
||||
cipher.identity.address3 = this.getValueOrDefault(address3);
|
||||
|
||||
cipher.identity.city = this.getValueOrDefault(identityContent.city);
|
||||
cipher.identity.state = this.getValueOrDefault(identityContent.stateOrProvince);
|
||||
cipher.identity.postalCode = this.getValueOrDefault(identityContent.zipOrPostalCode);
|
||||
cipher.identity.country = this.getValueOrDefault(identityContent.countryOrRegion);
|
||||
this.processIdentityItemUnmappedAndExtraFields(cipher, identityContent);
|
||||
|
||||
for (const extraField of item.data.extraFields) {
|
||||
this.processKvp(
|
||||
cipher,
|
||||
extraField.fieldName,
|
||||
extraField.data.content,
|
||||
extraField.type === "hidden" ? FieldType.Hidden : FieldType.Text,
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
this.processFolder(result, vault.name);
|
||||
this.cleanupCipher(cipher);
|
||||
result.ciphers.push(cipher);
|
||||
}
|
||||
|
@ -36,8 +36,11 @@ export type ProtonPassItemData = {
|
||||
metadata: ProtonPassItemMetadata;
|
||||
extraFields: ProtonPassItemExtraField[];
|
||||
platformSpecific?: any;
|
||||
type: "login" | "alias" | "creditCard" | "note";
|
||||
content: ProtonPassLoginItemContent | ProtonPassCreditCardItemContent;
|
||||
type: "login" | "alias" | "creditCard" | "note" | "identity";
|
||||
content:
|
||||
| ProtonPassLoginItemContent
|
||||
| ProtonPassCreditCardItemContent
|
||||
| ProtonPassIdentityItemContent;
|
||||
};
|
||||
|
||||
export type ProtonPassItemMetadata = {
|
||||
@ -74,3 +77,48 @@ export type ProtonPassCreditCardItemContent = {
|
||||
expirationDate?: string;
|
||||
pin?: string;
|
||||
};
|
||||
|
||||
export type ProtonPassIdentityItemExtraSection = {
|
||||
sectionName?: string;
|
||||
sectionFields?: ProtonPassItemExtraField[];
|
||||
};
|
||||
|
||||
export type ProtonPassIdentityItemContent = {
|
||||
fullName?: string;
|
||||
email?: string;
|
||||
phoneNumber?: string;
|
||||
firstName?: string;
|
||||
middleName?: string;
|
||||
lastName?: string;
|
||||
birthdate?: string;
|
||||
gender?: string;
|
||||
extraPersonalDetails?: ProtonPassItemExtraField[];
|
||||
organization?: string;
|
||||
streetAddress?: string;
|
||||
zipOrPostalCode?: string;
|
||||
city?: string;
|
||||
stateOrProvince?: string;
|
||||
countryOrRegion?: string;
|
||||
floor?: string;
|
||||
county?: string;
|
||||
extraAddressDetails?: ProtonPassItemExtraField[];
|
||||
socialSecurityNumber?: string;
|
||||
passportNumber?: string;
|
||||
licenseNumber?: string;
|
||||
website?: string;
|
||||
xHandle?: string;
|
||||
secondPhoneNumber?: string;
|
||||
linkedin?: string;
|
||||
reddit?: string;
|
||||
facebook?: string;
|
||||
yahoo?: string;
|
||||
instagram?: string;
|
||||
extraContactDetails?: ProtonPassItemExtraField[];
|
||||
company?: string;
|
||||
jobTitle?: string;
|
||||
personalWebsite?: string;
|
||||
workPhoneNumber?: string;
|
||||
workEmail?: string;
|
||||
extraWorkDetails?: ProtonPassItemExtraField[];
|
||||
extraSections?: ProtonPassIdentityItemExtraSection[];
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user