Skip to content

Commit

Permalink
[PM-11882] Handled identity item and unsupported items during ProtonP…
Browse files Browse the repository at this point in the history
…ass import. (#10967)
  • Loading branch information
aliaftab612 authored Sep 18, 2024
1 parent 2d7fb03 commit 0f3d8a6
Show file tree
Hide file tree
Showing 6 changed files with 504 additions and 6 deletions.
99 changes: 96 additions & 3 deletions libs/importer/spec/protonpass-json-importer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand All @@ -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 () => {
Expand All @@ -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 () => {
Expand All @@ -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 changes: 138 additions & 0 deletions libs/importer/spec/test-data/protonpass-json/protonpass.json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down
Original file line number Diff line number Diff line change
@@ -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",
});
});
});
21 changes: 21 additions & 0 deletions libs/importer/src/importers/protonpass/protonpass-import-utils.ts
Original file line number Diff line number Diff line change
@@ -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 };
}
Loading

0 comments on commit 0f3d8a6

Please sign in to comment.