Add passky importer (#4253)
Create types for passky export format Add test files Write tests for passky-json-importer Write importer for passky export Register 'passkyjson' with `importOptions` Import/register passky-json-importer with import.service Add instructions on how to export from Passky
This commit is contained in:
parent
f67fffcc08
commit
8c8d4b3e3e
|
@ -288,6 +288,10 @@
|
|||
From the Yoti browser extension, click on "Settings", then "Export Saved Logins" and save the
|
||||
CSV file.
|
||||
</ng-container>
|
||||
<ng-container *ngIf="format === 'passkyjson'">
|
||||
Log in to "https://vault.passky.org" → "Import & Export" → "Export" in the Passky
|
||||
section. ("Backup" is unsupported as it is encrypted).
|
||||
</ng-container>
|
||||
</app-callout>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
import { PasskyJsonImporter as Importer } from "@bitwarden/common/importers/passky/passky-json-importer";
|
||||
|
||||
import { testData as EncryptedData } from "./test-data/passky-json/passky-encrypted.json";
|
||||
import { testData as UnencryptedData } from "./test-data/passky-json/passky-unencrypted.json";
|
||||
|
||||
describe("Passky Json Importer", () => {
|
||||
let importer: Importer;
|
||||
beforeEach(() => {
|
||||
importer = new Importer();
|
||||
});
|
||||
|
||||
it("should not import encrypted backups", async () => {
|
||||
const testDataJson = JSON.stringify(EncryptedData);
|
||||
const result = await importer.parse(testDataJson);
|
||||
expect(result != null).toBe(true);
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.errorMessage).toBe("Unable to import an encrypted passky backup.");
|
||||
});
|
||||
|
||||
it("should parse login data", async () => {
|
||||
const testDataJson = JSON.stringify(UnencryptedData);
|
||||
const result = await importer.parse(testDataJson);
|
||||
expect(result != null).toBe(true);
|
||||
|
||||
const cipher = result.ciphers.shift();
|
||||
expect(cipher.name).toEqual("https://bitwarden.com/");
|
||||
expect(cipher.login.username).toEqual("testUser");
|
||||
expect(cipher.login.password).toEqual("testPassword");
|
||||
expect(cipher.login.uris.length).toEqual(1);
|
||||
const uriView = cipher.login.uris.shift();
|
||||
expect(uriView.uri).toEqual("https://bitwarden.com/");
|
||||
expect(cipher.notes).toEqual("my notes");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
import { PasskyJsonExport } from "@bitwarden/common/importers/passky/passky-json-types";
|
||||
|
||||
export const testData: PasskyJsonExport = {
|
||||
encrypted: true,
|
||||
passwords: [
|
||||
{
|
||||
website:
|
||||
"w68uw6nCjUI3w7MNYsK7w6xqwqHDlXLCpsOEw4/Dq8KbIMK3w6fCvQJFFcOECsOlwprCqUAawqnDvsKbwrLCsCXCtcOlw4dp",
|
||||
username: "bMKyUC0VPTx5woHCr8K9wpvDgGrClFAKw6VfJTgob8KVwqNoN8KIEA==",
|
||||
password: "XcKxO2FjwqIJPkoHwqrDvcKtXcORw6TDlMOlw7TDvMORfmlNdMKOwq7DocO+",
|
||||
message:
|
||||
"w5jCrWTCgAV1RcO+DsOzw5zCvD5CwqLCtcKtw6sPwpbCmcOxwrfDlcOQw4h1wqomEhNtUkRgwrzCkxrClFBSHsO5wrfCrg==",
|
||||
},
|
||||
],
|
||||
};
|
|
@ -0,0 +1,13 @@
|
|||
import { PasskyJsonExport } from "@bitwarden/common/importers/passky/passky-json-types";
|
||||
|
||||
export const testData: PasskyJsonExport = {
|
||||
encrypted: false,
|
||||
passwords: [
|
||||
{
|
||||
website: "https://bitwarden.com/",
|
||||
username: "testUser",
|
||||
password: "testPassword",
|
||||
message: "my notes",
|
||||
},
|
||||
],
|
||||
};
|
|
@ -67,6 +67,7 @@ export const regularImportOptions = [
|
|||
{ id: "encryptrcsv", name: "Encryptr (csv)" },
|
||||
{ id: "yoticsv", name: "Yoti (csv)" },
|
||||
{ id: "nordpasscsv", name: "Nordpass (csv)" },
|
||||
{ id: "passkyjson", name: "Passky (json)" },
|
||||
] as const;
|
||||
|
||||
export type ImportType =
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
import { ImportResult } from "../../models/domain/import-result";
|
||||
import { BaseImporter } from "../base-importer";
|
||||
import { Importer } from "../importer";
|
||||
|
||||
import { PasskyJsonExport } from "./passky-json-types";
|
||||
|
||||
export class PasskyJsonImporter extends BaseImporter implements Importer {
|
||||
parse(data: string): Promise<ImportResult> {
|
||||
const result = new ImportResult();
|
||||
const passkyExport: PasskyJsonExport = JSON.parse(data);
|
||||
if (
|
||||
passkyExport == null ||
|
||||
passkyExport.passwords == null ||
|
||||
passkyExport.passwords.length === 0
|
||||
) {
|
||||
result.success = false;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
if (passkyExport.encrypted == true) {
|
||||
result.success = false;
|
||||
result.errorMessage = "Unable to import an encrypted passky backup.";
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
passkyExport.passwords.forEach((record) => {
|
||||
const cipher = this.initLoginCipher();
|
||||
cipher.name = record.website;
|
||||
cipher.login.username = record.username;
|
||||
cipher.login.password = record.password;
|
||||
|
||||
cipher.login.uris = this.makeUriArray(record.website);
|
||||
cipher.notes = record.message;
|
||||
|
||||
this.convertToNoteIfNeeded(cipher);
|
||||
this.cleanupCipher(cipher);
|
||||
result.ciphers.push(cipher);
|
||||
});
|
||||
|
||||
result.success = true;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
export interface PasskyJsonExport {
|
||||
encrypted: boolean;
|
||||
passwords: LoginEntry[];
|
||||
}
|
||||
|
||||
export interface LoginEntry {
|
||||
website: string;
|
||||
username: string;
|
||||
password: string;
|
||||
message: string;
|
||||
}
|
|
@ -51,6 +51,7 @@ import { OnePasswordMacCsvImporter } from "../importers/onepassword/onepassword-
|
|||
import { OnePasswordWinCsvImporter } from "../importers/onepassword/onepassword-win-csv-importer";
|
||||
import { PadlockCsvImporter } from "../importers/padlock-csv-importer";
|
||||
import { PassKeepCsvImporter } from "../importers/passkeep-csv-importer";
|
||||
import { PasskyJsonImporter } from "../importers/passky/passky-json-importer";
|
||||
import { PassmanJsonImporter } from "../importers/passman-json-importer";
|
||||
import { PasspackCsvImporter } from "../importers/passpack-csv-importer";
|
||||
import { PasswordAgentCsvImporter } from "../importers/passwordagent-csv-importer";
|
||||
|
@ -279,6 +280,8 @@ export class ImportService implements ImportServiceAbstraction {
|
|||
return new YotiCsvImporter();
|
||||
case "nordpasscsv":
|
||||
return new NordPassCsvImporter();
|
||||
case "passkyjson":
|
||||
return new PasskyJsonImporter();
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue