diff --git a/apps/web/src/app/tools/import-export/import.component.html b/apps/web/src/app/tools/import-export/import.component.html index e3baef5da49..8911a63306f 100644 --- a/apps/web/src/app/tools/import-export/import.component.html +++ b/apps/web/src/app/tools/import-export/import.component.html @@ -288,6 +288,10 @@ From the Yoti browser extension, click on "Settings", then "Export Saved Logins" and save the CSV file. + + Log in to the Psono web vault, click on the "Signed in as"-dropdown, select "Others". Go to + the "Export"-tab and select the json type export and then click on Export. + Log in to "https://vault.passky.org" → "Import & Export" → "Export" in the Passky section. ("Backup" is unsupported as it is encrypted). diff --git a/libs/common/spec/importers/psono-json-importer.spec.ts b/libs/common/spec/importers/psono-json-importer.spec.ts new file mode 100644 index 00000000000..f9fc5f8c7fe --- /dev/null +++ b/libs/common/spec/importers/psono-json-importer.spec.ts @@ -0,0 +1,228 @@ +import { CipherType } from "@bitwarden/common/enums/cipherType"; +import { FieldType } from "@bitwarden/common/enums/fieldType"; +import { PsonoJsonImporter as Importer } from "@bitwarden/common/importers/psono/psono-json-importer"; +import { FieldView } from "@bitwarden/common/models/view/field.view"; + +import { ApplicationPasswordsData } from "./test-data/psono-json/application-passwords"; +import { BookmarkData } from "./test-data/psono-json/bookmark.json"; +import { EmptyTestFolderData } from "./test-data/psono-json/empty-folders"; +import { EnvVariablesData } from "./test-data/psono-json/environment-variables"; +import { FoldersTestData } from "./test-data/psono-json/folders"; +import { GPGData } from "./test-data/psono-json/gpg"; +import { NotesData } from "./test-data/psono-json/notes"; +import { TOTPData } from "./test-data/psono-json/totp"; +import { WebsiteLoginsData } from "./test-data/psono-json/website-logins"; + +function validateCustomField( + fields: FieldView[], + fieldName: string, + expectedValue: any, + fieldType = FieldType.Text +) { + expect(fields).not.toBeUndefined(); + const customField = fields.find((f) => f.name === fieldName); + expect(customField).not.toBeNull(); + + expect(customField.value).toEqual(expectedValue); + expect(customField.type).toEqual(fieldType); +} + +describe("PSONO JSON Importer", () => { + const WebsiteLoginsDataJson = JSON.stringify(WebsiteLoginsData); + const ApplicationPasswordsDataJson = JSON.stringify(ApplicationPasswordsData); + const BookmarkDataJson = JSON.stringify(BookmarkData); + const NotesDataJson = JSON.stringify(NotesData); + const TOTPDataJson = JSON.stringify(TOTPData); + const EmptyTestFolderDataJson = JSON.stringify(EmptyTestFolderData); + const FoldersTestDataJson = JSON.stringify(FoldersTestData); + const GPGDataJson = JSON.stringify(GPGData); + const EnvVariablesDataJson = JSON.stringify(EnvVariablesData); + + it("should parse Website/Password data", async () => { + const importer = new Importer(); + const result = await importer.parse(WebsiteLoginsDataJson); + expect(result != null).toBe(true); + + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.Login); + expect(cipher.name).toEqual("TestEntry"); + expect(cipher.notes).toEqual("some notes"); + + expect(cipher.login.username).toEqual("testUser"); + expect(cipher.login.password).toEqual("testPassword"); + expect(cipher.login.uris.length).toEqual(1); + expect(cipher.login.uri).toEqual("http://bitwarden.com"); + + expect(cipher.fields.length).toBe(7); + validateCustomField(cipher.fields, "website_password_auto_submit", "true", FieldType.Boolean); + validateCustomField(cipher.fields, "website_password_url_filter", "filter"); + validateCustomField(cipher.fields, "create_date", "2022-12-13T19:24:09.810266Z"); + validateCustomField(cipher.fields, "write_date", "2022-12-13T19:24:09.810292Z"); + validateCustomField(cipher.fields, "callback_url", "callback"); + validateCustomField(cipher.fields, "callback_user", "callbackUser"); + validateCustomField(cipher.fields, "callback_pass", "callbackPassword"); + }); + + it("should parse Application Password data", async () => { + const importer = new Importer(); + const result = await importer.parse(ApplicationPasswordsDataJson); + expect(result != null).toBe(true); + + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.Login); + expect(cipher.name).toEqual("My App Password"); + expect(cipher.notes).toEqual("some notes for the APP"); + + expect(cipher.login.username).toEqual("someUser"); + expect(cipher.login.password).toEqual("somePassword"); + + expect(cipher.fields.length).toBe(2); + validateCustomField(cipher.fields, "create_date", "2022-12-13T19:42:05.784077Z"); + validateCustomField(cipher.fields, "write_date", "2022-12-13T19:42:05.784103Z"); + }); + + it("should parse bookmark data", async () => { + const importer = new Importer(); + const result = await importer.parse(BookmarkDataJson); + expect(result != null).toBe(true); + + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.Login); + expect(cipher.name).toEqual("MyBookmark"); + expect(cipher.notes).toEqual("my notes for bitwarden.com"); + + expect(cipher.login.uris.length).toEqual(1); + expect(cipher.login.uri).toEqual("https://bitwarden.com"); + + expect(cipher.fields.length).toBe(4); + validateCustomField(cipher.fields, "create_date", "2022-12-13T19:39:26.631530Z"); + validateCustomField(cipher.fields, "write_date", "2022-12-13T19:39:26.631553Z"); + }); + + it("should parse notes", async () => { + const importer = new Importer(); + const result = await importer.parse(NotesDataJson); + expect(result != null).toBe(true); + + const cipher = result.ciphers.shift(); + expect(cipher.type).toEqual(CipherType.SecureNote); + expect(cipher.name).toEqual("My Note"); + expect(cipher.notes).toEqual("Notes for my Note"); + + expect(cipher.fields.length).toBe(2); + validateCustomField(cipher.fields, "create_date", "2022-12-13T19:41:18.770714Z"); + validateCustomField(cipher.fields, "write_date", "2022-12-13T19:41:18.770738Z"); + }); + + it("should parse TOTP", async () => { + const importer = new Importer(); + const result = await importer.parse(TOTPDataJson); + expect(result != null).toBe(true); + + const cipher = result.ciphers.shift(); + expect(cipher.type).toEqual(CipherType.Login); + expect(cipher.name).toEqual("My TOTP"); + expect(cipher.notes).toEqual("Notes for TOTP"); + + expect(cipher.login.totp).toEqual("someSecretOfMine"); + + expect(cipher.fields.length).toBe(5); + validateCustomField(cipher.fields, "totp_period", "30", FieldType.Text); + validateCustomField(cipher.fields, "totp_algorithm", "SHA1", FieldType.Text); + validateCustomField(cipher.fields, "totp_digits", "6", FieldType.Text); + validateCustomField(cipher.fields, "create_date", "2022-12-13T19:41:42.972586Z"); + validateCustomField(cipher.fields, "write_date", "2022-12-13T19:41:42.972609Z"); + }); + + // Skipping this test until we can save GPG into notes/custom fields + it.skip("should parse GPG data", async () => { + const importer = new Importer(); + const result = await importer.parse(GPGDataJson); + expect(result != null).toBe(true); + + const cipher = result.ciphers.shift(); + expect(cipher.type).toEqual(CipherType.SecureNote); + expect(cipher.name).toEqual("my test gpg key"); + //Public Key + expect(cipher.notes).toEqual( + "-----BEGIN PGP PUBLIC KEY BLOCK-----\n\n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\nAZTY\n=kfi8\n-----END PGP PUBLIC KEY BLOCK-----\n" + ); + + expect(cipher.fields.length).toBe(5); + //Keyname + validateCustomField(cipher.fields, "mail_gpg_own_key_name", "My key name"); + //Email + validateCustomField(cipher.fields, "mail_gpg_own_key_email", "some@email.com"); + // Private Key + validateCustomField( + cipher.fields, + "mail_gpg_own_key_private", + "-----BEGIN PGP PRIVATE KEY BLOCK-----\n\n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\nEiIRI+xLdLnqAbrpmRsb9kIBlNg=\n=+NhB\n-----END PGP PRIVATE KEY BLOCK-----\n", + FieldType.Hidden + ); + validateCustomField(cipher.fields, "create_date", "2022-12-13T19:40:16.070379Z"); + validateCustomField(cipher.fields, "write_date", "2022-12-13T19:40:16.070404Z"); + }); + + it("should parse Environment variables data", async () => { + const importer = new Importer(); + const result = await importer.parse(EnvVariablesDataJson); + expect(result != null).toBe(true); + + const cipher = result.ciphers.shift(); + expect(cipher.type).toEqual(CipherType.SecureNote); + expect(cipher.name).toEqual("My Environment Variables"); + expect(cipher.notes).toBe("Notes for environment variables"); + + expect(cipher.fields.length).toBe(4); + validateCustomField(cipher.fields, "Key1", "Value1"); + validateCustomField(cipher.fields, "Key2", "Value2"); + validateCustomField(cipher.fields, "create_date", "2022-12-13T19:41:02.028884Z"); + validateCustomField(cipher.fields, "write_date", "2022-12-13T19:41:02.028909Z"); + }); + + it("should not create empty folders", async () => { + const importer = new Importer(); + const result = await importer.parse(EmptyTestFolderDataJson); + expect(result != null).toBe(true); + + expect(result.folders.length).toBe(0); + }); + + it("should create folders", async () => { + const importer = new Importer(); + const result = await importer.parse(FoldersTestDataJson); + expect(result != null).toBe(true); + + const folders = result.folders; + expect(folders.length).toBe(2); + expect(folders[0].name).toBe("TestFolder"); + expect(folders[1].name).toBe("TestFolder2"); + }); + + it("should assign entries to folders", async () => { + const importer = new Importer(); + const result = await importer.parse(FoldersTestDataJson); + expect(result != null).toBe(true); + + const folders = result.folders; + // // Check that ciphers have a folder assigned to them + expect(result.ciphers.filter((c) => c.folderId === folders[0].id).length).toBeGreaterThan(0); + expect(result.ciphers.filter((c) => c.folderId === folders[1].id).length).toBeGreaterThan(0); + }); + + it("should create collections if part of an organization", async () => { + const importer = new Importer(); + importer.organizationId = "someOrg"; + const result = await importer.parse(FoldersTestDataJson); + expect(result != null).toBe(true); + + const collections = result.collections; + expect(collections.length).toBe(2); + expect(collections[0].name).toBe("TestFolder"); + expect(collections[1].name).toBe("TestFolder2"); + }); +}); diff --git a/libs/common/spec/importers/test-data/psono-json/application-passwords.ts b/libs/common/spec/importers/test-data/psono-json/application-passwords.ts new file mode 100644 index 00000000000..df9416dd52d --- /dev/null +++ b/libs/common/spec/importers/test-data/psono-json/application-passwords.ts @@ -0,0 +1,20 @@ +import { PsonoJsonExport } from "@bitwarden/common/importers/psono/psono-json-types"; + +export const ApplicationPasswordsData: PsonoJsonExport = { + folders: [], + items: [ + { + type: "application_password", + name: "My App Password", + application_password_title: "My App Password", + application_password_username: "someUser", + application_password_password: "somePassword", + application_password_notes: "some notes for the APP", + create_date: "2022-12-13T19:42:05.784077Z", + write_date: "2022-12-13T19:42:05.784103Z", + callback_url: "", + callback_user: "", + callback_pass: "", + }, + ], +}; diff --git a/libs/common/spec/importers/test-data/psono-json/bookmark.json.ts b/libs/common/spec/importers/test-data/psono-json/bookmark.json.ts new file mode 100644 index 00000000000..a4870b8d0ab --- /dev/null +++ b/libs/common/spec/importers/test-data/psono-json/bookmark.json.ts @@ -0,0 +1,21 @@ +import { PsonoJsonExport } from "@bitwarden/common/importers/psono/psono-json-types"; + +export const BookmarkData: PsonoJsonExport = { + folders: [], + items: [ + { + type: "bookmark", + name: "MyBookmark", + urlfilter: "bitwarden.com", + bookmark_title: "MyBookmark", + bookmark_url: "https://bitwarden.com", + bookmark_notes: "my notes for bitwarden.com", + bookmark_url_filter: "bitwarden.com", + create_date: "2022-12-13T19:39:26.631530Z", + write_date: "2022-12-13T19:39:26.631553Z", + callback_url: "", + callback_user: "", + callback_pass: "", + }, + ], +}; diff --git a/libs/common/spec/importers/test-data/psono-json/empty-folders.ts b/libs/common/spec/importers/test-data/psono-json/empty-folders.ts new file mode 100644 index 00000000000..e775113be20 --- /dev/null +++ b/libs/common/spec/importers/test-data/psono-json/empty-folders.ts @@ -0,0 +1,10 @@ +import { PsonoJsonExport } from "@bitwarden/common/importers/psono/psono-json-types"; + +export const EmptyTestFolderData: PsonoJsonExport = { + folders: [ + { + name: "EmptyFolder", + items: [], + }, + ], +}; diff --git a/libs/common/spec/importers/test-data/psono-json/environment-variables.ts b/libs/common/spec/importers/test-data/psono-json/environment-variables.ts new file mode 100644 index 00000000000..3bc6de30bde --- /dev/null +++ b/libs/common/spec/importers/test-data/psono-json/environment-variables.ts @@ -0,0 +1,22 @@ +import { PsonoJsonExport } from "@bitwarden/common/importers/psono/psono-json-types"; + +export const EnvVariablesData: PsonoJsonExport = { + folders: [], + items: [ + { + type: "environment_variables", + name: "My Environment Variables", + environment_variables_title: "My Environment Variables", + environment_variables_variables: [ + { key: "Key1", value: "Value1" }, + { key: "Key2", value: "Value2" }, + ], + environment_variables_notes: "Notes for environment variables", + create_date: "2022-12-13T19:41:02.028884Z", + write_date: "2022-12-13T19:41:02.028909Z", + callback_url: "", + callback_user: "", + callback_pass: "", + }, + ], +}; diff --git a/libs/common/spec/importers/test-data/psono-json/folders.ts b/libs/common/spec/importers/test-data/psono-json/folders.ts new file mode 100644 index 00000000000..3968f714d86 --- /dev/null +++ b/libs/common/spec/importers/test-data/psono-json/folders.ts @@ -0,0 +1,53 @@ +import { PsonoJsonExport } from "@bitwarden/common/importers/psono/psono-json-types"; + +export const FoldersTestData: PsonoJsonExport = { + folders: [ + { + name: "TestFolder", + items: [ + { + type: "website_password", + name: "TestEntry", + autosubmit: true, + urlfilter: "filter", + website_password_title: "TestEntry", + website_password_url: "bitwarden.com", + website_password_username: "testUser", + website_password_password: "testPassword", + website_password_notes: "some notes", + website_password_auto_submit: true, + website_password_url_filter: "filter", + create_date: "2022-12-13T19:24:09.810266Z", + write_date: "2022-12-13T19:24:09.810292Z", + callback_url: "callback", + callback_user: "callbackUser", + callback_pass: "callbackPassword", + }, + ], + }, + { + name: "TestFolder2", + items: [ + { + type: "website_password", + name: "TestEntry2", + autosubmit: true, + urlfilter: "filter", + website_password_title: "TestEntry2", + website_password_url: "bitwarden.com", + website_password_username: "testUser", + website_password_password: "testPassword", + website_password_notes: "some notes", + website_password_auto_submit: true, + website_password_url_filter: "filter", + create_date: "2022-12-13T19:24:09.810266Z", + write_date: "2022-12-13T19:24:09.810292Z", + callback_url: "callback", + callback_user: "callbackUser", + callback_pass: "callbackPassword", + }, + ], + }, + ], + items: [], +}; diff --git a/libs/common/spec/importers/test-data/psono-json/gpg.ts b/libs/common/spec/importers/test-data/psono-json/gpg.ts new file mode 100644 index 00000000000..177f4ef762d --- /dev/null +++ b/libs/common/spec/importers/test-data/psono-json/gpg.ts @@ -0,0 +1,23 @@ +import { PsonoJsonExport } from "@bitwarden/common/importers/psono/psono-json-types"; + +export const GPGData: PsonoJsonExport = { + folders: [], + items: [ + { + type: "mail_gpg_own_key", + name: "my test gpg key", + mail_gpg_own_key_title: "my test gpg key", + mail_gpg_own_key_email: "some@email.com", + mail_gpg_own_key_name: "My key name", + mail_gpg_own_key_public: + "-----BEGIN PGP PUBLIC KEY BLOCK-----\n\n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\nAZTY\n=kfi8\n-----END PGP PUBLIC KEY BLOCK-----\n", + mail_gpg_own_key_private: + "-----BEGIN PGP PRIVATE KEY BLOCK-----\n\n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\nEiIRI+xLdLnqAbrpmRsb9kIBlNg=\n=+NhB\n-----END PGP PRIVATE KEY BLOCK-----\n", + create_date: "2022-12-13T19:40:16.070379Z", + write_date: "2022-12-13T19:40:16.070404Z", + callback_url: "", + callback_user: "", + callback_pass: "", + }, + ], +}; diff --git a/libs/common/spec/importers/test-data/psono-json/notes.ts b/libs/common/spec/importers/test-data/psono-json/notes.ts new file mode 100644 index 00000000000..c5290afdad4 --- /dev/null +++ b/libs/common/spec/importers/test-data/psono-json/notes.ts @@ -0,0 +1,18 @@ +import { PsonoJsonExport } from "@bitwarden/common/importers/psono/psono-json-types"; + +export const NotesData: PsonoJsonExport = { + folders: [], + items: [ + { + type: "note", + name: "My Note", + note_title: "My Note", + note_notes: "Notes for my Note", + create_date: "2022-12-13T19:41:18.770714Z", + write_date: "2022-12-13T19:41:18.770738Z", + callback_url: "", + callback_user: "", + callback_pass: "", + }, + ], +}; diff --git a/libs/common/spec/importers/test-data/psono-json/totp.ts b/libs/common/spec/importers/test-data/psono-json/totp.ts new file mode 100644 index 00000000000..8c903437996 --- /dev/null +++ b/libs/common/spec/importers/test-data/psono-json/totp.ts @@ -0,0 +1,22 @@ +import { PsonoJsonExport } from "@bitwarden/common/importers/psono/psono-json-types"; + +export const TOTPData: PsonoJsonExport = { + folders: [], + items: [ + { + type: "totp", + name: "My TOTP", + totp_title: "My TOTP", + totp_period: 30, + totp_algorithm: "SHA1", + totp_digits: 6, + totp_code: "someSecretOfMine", + totp_notes: "Notes for TOTP", + create_date: "2022-12-13T19:41:42.972586Z", + write_date: "2022-12-13T19:41:42.972609Z", + callback_url: "", + callback_user: "", + callback_pass: "", + }, + ], +}; diff --git a/libs/common/spec/importers/test-data/psono-json/website-logins.ts b/libs/common/spec/importers/test-data/psono-json/website-logins.ts new file mode 100644 index 00000000000..3dd2320b23f --- /dev/null +++ b/libs/common/spec/importers/test-data/psono-json/website-logins.ts @@ -0,0 +1,25 @@ +import { PsonoJsonExport } from "@bitwarden/common/importers/psono/psono-json-types"; + +export const WebsiteLoginsData: PsonoJsonExport = { + folders: [], + items: [ + { + type: "website_password", + name: "TestEntry", + autosubmit: true, + urlfilter: "filter", + website_password_title: "TestEntry", + website_password_url: "bitwarden.com", + website_password_username: "testUser", + website_password_password: "testPassword", + website_password_notes: "some notes", + website_password_auto_submit: true, + website_password_url_filter: "filter", + create_date: "2022-12-13T19:24:09.810266Z", + write_date: "2022-12-13T19:24:09.810292Z", + callback_url: "callback", + callback_user: "callbackUser", + callback_pass: "callbackPassword", + }, + ], +}; diff --git a/libs/common/src/enums/importOptions.ts b/libs/common/src/enums/importOptions.ts index e6657a92d05..cd9a5bc01c7 100644 --- a/libs/common/src/enums/importOptions.ts +++ b/libs/common/src/enums/importOptions.ts @@ -67,6 +67,7 @@ export const regularImportOptions = [ { id: "encryptrcsv", name: "Encryptr (csv)" }, { id: "yoticsv", name: "Yoti (csv)" }, { id: "nordpasscsv", name: "Nordpass (csv)" }, + { id: "psonojson", name: "Psono (json)" }, { id: "passkyjson", name: "Passky (json)" }, ] as const; diff --git a/libs/common/src/importers/psono/psono-json-importer.ts b/libs/common/src/importers/psono/psono-json-importer.ts new file mode 100644 index 00000000000..0e173b131d2 --- /dev/null +++ b/libs/common/src/importers/psono/psono-json-importer.ts @@ -0,0 +1,281 @@ +import { CipherType } from "../../enums/cipherType"; +import { FieldType } from "../../enums/fieldType"; +import { SecureNoteType } from "../../enums/secureNoteType"; +import { ImportResult } from "../../models/domain/import-result"; +import { CipherView } from "../../models/view/cipher.view"; +import { SecureNoteView } from "../../models/view/secure-note.view"; +import { BaseImporter } from "../base-importer"; +import { Importer } from "../importer"; + +import { + AppPasswordEntry, + BookmarkEntry, + EnvironmentVariablesEntry, + FoldersEntity, + GPGEntry, + NotesEntry, + PsonoItemTypes, + PsonoJsonExport, + TOTPEntry, + WebsitePasswordEntry, +} from "./psono-json-types"; + +export class PsonoJsonImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const psonoExport: PsonoJsonExport = JSON.parse(data); + if (psonoExport == null) { + result.success = false; + return Promise.resolve(result); + } + + this.parseFolders(result, psonoExport.folders); + this.handleItemParsing(result, psonoExport.items); + + if (this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } + + private parseFolders(result: ImportResult, folders: FoldersEntity[]) { + if (folders == null || folders.length === 0) { + return; + } + + folders.forEach((folder) => { + if (folder.items == null || folder.items.length == 0) { + return; + } + + this.processFolder(result, folder.name); + + this.handleItemParsing(result, folder.items); + }); + } + + private handleItemParsing(result: ImportResult, items?: PsonoItemTypes[]) { + if (items == null || items.length === 0) { + return; + } + + items.forEach((record) => { + const cipher = this.parsePsonoItem(record); + + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + } + + private parsePsonoItem(item: PsonoItemTypes): CipherView { + const cipher = this.initLoginCipher(); + + switch (item.type) { + case "website_password": + this.parseWebsiteLogins(item, cipher); + break; + case "application_password": + this.parseApplicationPasswords(item, cipher); + break; + case "environment_variables": + this.parseEnvironmentVariables(item, cipher); + break; + case "totp": + this.parseTOTP(item, cipher); + break; + case "bookmark": + this.parseBookmarks(item, cipher); + break; + // Skipping this until we can save GPG into notes/custom fields + // case "mail_gpg_own_key": + // this.parseGPG(item, cipher); + // break; + case "note": + this.parseNotes(item, cipher); + break; + default: + break; + } + + return cipher; + } + + readonly WEBSITE_mappedValues = new Set([ + "type", + "name", + "website_password_title", + "website_password_notes", + "website_password_username", + "website_password_password", + "website_password_url", + "autosubmit", + "website_password_auto_submit", + "urlfilter", + "website_password_url_filter", + ]); + private parseWebsiteLogins(entry: WebsitePasswordEntry, cipher: CipherView) { + if (entry == null || entry.type != "website_password") { + return; + } + + cipher.name = entry.website_password_title; + cipher.notes = entry.website_password_notes; + + cipher.login.username = entry.website_password_username; + cipher.login.password = entry.website_password_password; + + cipher.login.uris = this.makeUriArray(entry.website_password_url); + + this.processKvp( + cipher, + "website_password_auto_submit", + entry.website_password_auto_submit.toString(), + FieldType.Boolean + ); + + this.processKvp(cipher, "website_password_url_filter", entry.website_password_url_filter); + + this.importUnmappedFields(cipher, entry, this.WEBSITE_mappedValues); + } + + readonly APP_PWD_mappedValues = new Set([ + "type", + "name", + "application_password_title", + "application_password_notes", + "application_password_username", + "application_password_password", + ]); + private parseApplicationPasswords(entry: AppPasswordEntry, cipher: CipherView) { + if (entry == null || entry.type != "application_password") { + return; + } + + cipher.name = entry.application_password_title; + cipher.notes = entry.application_password_notes; + + cipher.login.username = entry.application_password_username; + cipher.login.password = entry.application_password_password; + + this.importUnmappedFields(cipher, entry, this.APP_PWD_mappedValues); + } + + readonly BOOKMARK_mappedValues = new Set([ + "type", + "name", + "bookmark_title", + "bookmark_notes", + "bookmark_url", + ]); + private parseBookmarks(entry: BookmarkEntry, cipher: CipherView) { + if (entry == null || entry.type != "bookmark") { + return; + } + + cipher.name = entry.bookmark_title; + cipher.notes = entry.bookmark_notes; + + cipher.login.uris = this.makeUriArray(entry.bookmark_url); + + this.importUnmappedFields(cipher, entry, this.BOOKMARK_mappedValues); + } + + readonly NOTES_mappedValues = new Set(["type", "name", "note_title", "note_notes"]); + private parseNotes(entry: NotesEntry, cipher: CipherView) { + if (entry == null || entry.type != "note") { + return; + } + cipher.type = CipherType.SecureNote; + cipher.secureNote = new SecureNoteView(); + cipher.secureNote.type = SecureNoteType.Generic; + cipher.name = entry.note_title; + cipher.notes = entry.note_notes; + + this.importUnmappedFields(cipher, entry, this.NOTES_mappedValues); + } + + readonly TOTP_mappedValues = new Set(["type", "name", "totp_title", "totp_notes", "totp_code"]); + private parseTOTP(entry: TOTPEntry, cipher: CipherView) { + if (entry == null || entry.type != "totp") { + return; + } + + cipher.name = entry.totp_title; + cipher.notes = entry.totp_notes; + + cipher.login.totp = entry.totp_code; + + this.importUnmappedFields(cipher, entry, this.TOTP_mappedValues); + } + + readonly ENV_VARIABLES_mappedValues = new Set([ + "type", + "name", + "environment_variables_title", + "environment_variables_notes", + "environment_variables_variables", + ]); + private parseEnvironmentVariables(entry: EnvironmentVariablesEntry, cipher: CipherView) { + if (entry == null || entry.type != "environment_variables") { + return; + } + + cipher.type = CipherType.SecureNote; + cipher.secureNote = new SecureNoteView(); + cipher.secureNote.type = SecureNoteType.Generic; + cipher.name = entry.environment_variables_title; + cipher.notes = entry.environment_variables_notes; + + entry.environment_variables_variables.forEach((KvPair) => { + this.processKvp(cipher, KvPair.key, KvPair.value); + }); + + this.importUnmappedFields(cipher, entry, this.ENV_VARIABLES_mappedValues); + } + + readonly GPG_mappedValues = new Set([ + "type", + "name", + "mail_gpg_own_key_title", + "mail_gpg_own_key_public", + "mail_gpg_own_key_name", + "mail_gpg_own_key_email", + "mail_gpg_own_key_private", + ]); + private parseGPG(entry: GPGEntry, cipher: CipherView) { + if (entry == null || entry.type != "mail_gpg_own_key") { + return; + } + + cipher.type = CipherType.SecureNote; + cipher.secureNote = new SecureNoteView(); + cipher.secureNote.type = SecureNoteType.Generic; + cipher.name = entry.mail_gpg_own_key_title; + cipher.notes = entry.mail_gpg_own_key_public; + + this.processKvp(cipher, "mail_gpg_own_key_name", entry.mail_gpg_own_key_name); + this.processKvp(cipher, "mail_gpg_own_key_email", entry.mail_gpg_own_key_email); + this.processKvp( + cipher, + "mail_gpg_own_key_private", + entry.mail_gpg_own_key_private, + FieldType.Hidden + ); + + this.importUnmappedFields(cipher, entry, this.GPG_mappedValues); + } + + private importUnmappedFields( + cipher: CipherView, + entry: PsonoItemTypes, + mappedValues: Set + ) { + const unmappedFields = Object.keys(entry).filter((x) => !mappedValues.has(x)); + unmappedFields.forEach((key) => { + const item = entry as any; + this.processKvp(cipher, key, item[key].toString()); + }); + } +} diff --git a/libs/common/src/importers/psono/psono-json-types.ts b/libs/common/src/importers/psono/psono-json-types.ts new file mode 100644 index 00000000000..bc15f4f3ec0 --- /dev/null +++ b/libs/common/src/importers/psono/psono-json-types.ts @@ -0,0 +1,109 @@ +export type PsonoItemTypes = + | WebsitePasswordEntry + | AppPasswordEntry + | TOTPEntry + | NotesEntry + | EnvironmentVariablesEntry + | GPGEntry + | BookmarkEntry; + +export interface PsonoJsonExport { + folders?: FoldersEntity[]; + items?: PsonoItemTypes[]; +} + +export interface FoldersEntity { + name: string; + items: PsonoItemTypes[] | null; +} + +export interface RecordBase { + type: PsonoEntryTypes; + name: string; + create_date: string; + write_date: string; + callback_url: string; + callback_user: string; + callback_pass: string; +} + +export type PsonoEntryTypes = + | "website_password" + | "bookmark" + | "mail_gpg_own_key" + | "environment_variables" + | "note" + | "application_password" + | "totp"; + +export interface WebsitePasswordEntry extends RecordBase { + type: "website_password"; + autosubmit: boolean; + urlfilter: string; + website_password_title: string; + website_password_url: string; + website_password_username: string; + website_password_password: string; + website_password_notes: string; + website_password_auto_submit: boolean; + website_password_url_filter: string; +} + +export interface PsonoEntry { + type: string; + name: string; +} + +export interface BookmarkEntry extends RecordBase { + type: "bookmark"; + urlfilter: string; + bookmark_title: string; + bookmark_url: string; + bookmark_notes: string; + bookmark_url_filter: string; +} + +export interface GPGEntry extends RecordBase { + type: "mail_gpg_own_key"; + mail_gpg_own_key_title: string; + mail_gpg_own_key_email: string; + mail_gpg_own_key_name: string; + mail_gpg_own_key_public: string; + mail_gpg_own_key_private: string; +} + +export interface EnvironmentVariablesEntry extends RecordBase { + type: "environment_variables"; + environment_variables_title: string; + environment_variables_variables: EnvironmentVariables_KVPair[]; + environment_variables_notes: string; +} + +export interface EnvironmentVariables_KVPair { + key: string; + value: string; +} + +export interface AppPasswordEntry extends RecordBase { + type: "application_password"; + application_password_title: string; + application_password_username: string; + application_password_password: string; + application_password_notes: string; +} + +export interface TOTPEntry extends RecordBase { + type: "totp"; + totp_title: string; + totp_period: number; + totp_algorithm: "SHA1"; + totp_digits: number; + totp_code: string; + totp_notes: string; +} + +export interface NotesEntry extends RecordBase { + type: "note"; + note_title: string; + note_notes: string; +} diff --git a/libs/common/src/services/import.service.ts b/libs/common/src/services/import.service.ts index eec8edf6c56..d7c8cd12375 100644 --- a/libs/common/src/services/import.service.ts +++ b/libs/common/src/services/import.service.ts @@ -59,6 +59,7 @@ import { PasswordBossJsonImporter } from "../importers/passwordboss-json-importe import { PasswordDragonXmlImporter } from "../importers/passworddragon-xml-importer"; import { PasswordSafeXmlImporter } from "../importers/passwordsafe-xml-importer"; import { PasswordWalletTxtImporter } from "../importers/passwordwallet-txt-importer"; +import { PsonoJsonImporter } from "../importers/psono/psono-json-importer"; import { RememBearCsvImporter } from "../importers/remembear-csv-importer"; import { RoboFormCsvImporter } from "../importers/roboform-csv-importer"; import { SafariCsvImporter } from "../importers/safari-csv-importer"; @@ -280,6 +281,8 @@ export class ImportService implements ImportServiceAbstraction { return new YotiCsvImporter(); case "nordpasscsv": return new NordPassCsvImporter(); + case "psonojson": + return new PsonoJsonImporter(); case "passkyjson": return new PasskyJsonImporter(); default: