1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-14 07:13:32 +00:00
Files
browser/libs/common/src/services/import.service.ts
Daniel James Smith 8c8d4b3e3e 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
2022-12-19 21:47:45 +01:00

384 lines
16 KiB
TypeScript

import { ApiService } from "../abstractions/api.service";
import { CipherService } from "../abstractions/cipher.service";
import { CollectionService } from "../abstractions/collection.service";
import { CryptoService } from "../abstractions/crypto.service";
import { FolderService } from "../abstractions/folder/folder.service.abstraction";
import { I18nService } from "../abstractions/i18n.service";
import { ImportService as ImportServiceAbstraction } from "../abstractions/import.service";
import { CipherType } from "../enums/cipherType";
import {
featuredImportOptions,
ImportOption,
ImportType,
regularImportOptions,
} from "../enums/importOptions";
import { AscendoCsvImporter } from "../importers/ascendo-csv-importer";
import { AvastCsvImporter } from "../importers/avast-csv-importer";
import { AvastJsonImporter } from "../importers/avast-json-importer";
import { AviraCsvImporter } from "../importers/avira-csv-importer";
import { BitwardenCsvImporter } from "../importers/bitwarden-csv-importer";
import { BitwardenJsonImporter } from "../importers/bitwarden-json-importer";
import { BitwardenPasswordProtectedImporter } from "../importers/bitwarden-password-protected-importer";
import { BlackBerryCsvImporter } from "../importers/blackberry-csv-importer";
import { BlurCsvImporter } from "../importers/blur-csv-importer";
import { ButtercupCsvImporter } from "../importers/buttercup-csv-importer";
import { ChromeCsvImporter } from "../importers/chrome-csv-importer";
import { ClipperzHtmlImporter } from "../importers/clipperz-html-importer";
import { CodebookCsvImporter } from "../importers/codebook-csv-importer";
import { DashlaneCsvImporter } from "../importers/dashlane/dashlane-csv-importer";
import { DashlaneJsonImporter } from "../importers/dashlane/dashlane-json-importer";
import { EncryptrCsvImporter } from "../importers/encryptr-csv-importer";
import { EnpassCsvImporter } from "../importers/enpass-csv-importer";
import { EnpassJsonImporter } from "../importers/enpass-json-importer";
import { FirefoxCsvImporter } from "../importers/firefox-csv-importer";
import { FSecureFskImporter } from "../importers/fsecure-fsk-importer";
import { GnomeJsonImporter } from "../importers/gnome-json-importer";
import { ImportError } from "../importers/import-error";
import { Importer } from "../importers/importer";
import { KasperskyTxtImporter } from "../importers/kaspersky-txt-importer";
import { KeePass2XmlImporter } from "../importers/keepass2-xml-importer";
import { KeePassXCsvImporter } from "../importers/keepassx-csv-importer";
import { KeeperCsvImporter } from "../importers/keeper/keeper-csv-importer";
import { LastPassCsvImporter } from "../importers/lastpass-csv-importer";
import { LogMeOnceCsvImporter } from "../importers/logmeonce-csv-importer";
import { MeldiumCsvImporter } from "../importers/meldium-csv-importer";
import { MSecureCsvImporter } from "../importers/msecure-csv-importer";
import { MykiCsvImporter } from "../importers/myki-csv-importer";
import { NordPassCsvImporter } from "../importers/nordpass-csv-importer";
import { OnePassword1PifImporter } from "../importers/onepassword/onepassword-1pif-importer";
import { OnePassword1PuxImporter } from "../importers/onepassword/onepassword-1pux-importer";
import { OnePasswordMacCsvImporter } from "../importers/onepassword/onepassword-mac-csv-importer";
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";
import { PasswordBossJsonImporter } from "../importers/passwordboss-json-importer";
import { PasswordDragonXmlImporter } from "../importers/passworddragon-xml-importer";
import { PasswordSafeXmlImporter } from "../importers/passwordsafe-xml-importer";
import { PasswordWalletTxtImporter } from "../importers/passwordwallet-txt-importer";
import { RememBearCsvImporter } from "../importers/remembear-csv-importer";
import { RoboFormCsvImporter } from "../importers/roboform-csv-importer";
import { SafariCsvImporter } from "../importers/safari-csv-importer";
import { SafeInCloudXmlImporter } from "../importers/safeincloud-xml-importer";
import { SaferPassCsvImporter } from "../importers/saferpass-csv-importer";
import { SecureSafeCsvImporter } from "../importers/securesafe-csv-importer";
import { SplashIdCsvImporter } from "../importers/splashid-csv-importer";
import { StickyPasswordXmlImporter } from "../importers/stickypassword-xml-importer";
import { TrueKeyCsvImporter } from "../importers/truekey-csv-importer";
import { UpmCsvImporter } from "../importers/upm-csv-importer";
import { YotiCsvImporter } from "../importers/yoti-csv-importer";
import { ZohoVaultCsvImporter } from "../importers/zohovault-csv-importer";
import { Utils } from "../misc/utils";
import { ImportResult } from "../models/domain/import-result";
import { CipherRequest } from "../models/request/cipher.request";
import { CollectionRequest } from "../models/request/collection.request";
import { FolderRequest } from "../models/request/folder.request";
import { ImportCiphersRequest } from "../models/request/import-ciphers.request";
import { ImportOrganizationCiphersRequest } from "../models/request/import-organization-ciphers.request";
import { KvpRequest } from "../models/request/kvp.request";
import { ErrorResponse } from "../models/response/error.response";
import { CipherView } from "../models/view/cipher.view";
export class ImportService implements ImportServiceAbstraction {
featuredImportOptions = featuredImportOptions as readonly ImportOption[];
regularImportOptions = regularImportOptions as readonly ImportOption[];
constructor(
private cipherService: CipherService,
private folderService: FolderService,
private apiService: ApiService,
private i18nService: I18nService,
private collectionService: CollectionService,
private cryptoService: CryptoService
) {}
getImportOptions(): ImportOption[] {
return this.featuredImportOptions.concat(this.regularImportOptions);
}
async import(
importer: Importer,
fileContents: string,
organizationId: string = null
): Promise<ImportError> {
const importResult = await importer.parse(fileContents);
if (importResult.success) {
if (importResult.folders.length === 0 && importResult.ciphers.length === 0) {
return new ImportError(this.i18nService.t("importNothingError"));
} else if (importResult.ciphers.length > 0) {
const halfway = Math.floor(importResult.ciphers.length / 2);
const last = importResult.ciphers.length - 1;
if (
this.badData(importResult.ciphers[0]) &&
this.badData(importResult.ciphers[halfway]) &&
this.badData(importResult.ciphers[last])
) {
return new ImportError(this.i18nService.t("importFormatError"));
}
}
try {
await this.postImport(importResult, organizationId);
} catch (error) {
const errorResponse = new ErrorResponse(error, 400);
return this.handleServerError(errorResponse, importResult);
}
return null;
} else {
if (!Utils.isNullOrWhitespace(importResult.errorMessage)) {
return new ImportError(importResult.errorMessage, importResult.missingPassword);
} else {
return new ImportError(
this.i18nService.t("importFormatError"),
importResult.missingPassword
);
}
}
}
getImporter(
format: ImportType | "bitwardenpasswordprotected",
organizationId: string = null,
password: string = null
): Importer {
const importer = this.getImporterInstance(format, password);
if (importer == null) {
return null;
}
importer.organizationId = organizationId;
return importer;
}
private getImporterInstance(format: ImportType | "bitwardenpasswordprotected", password: string) {
if (format == null) {
return null;
}
switch (format) {
case "bitwardencsv":
return new BitwardenCsvImporter();
case "bitwardenjson":
return new BitwardenJsonImporter(this.cryptoService, this.i18nService);
case "bitwardenpasswordprotected":
return new BitwardenPasswordProtectedImporter(
this.cryptoService,
this.i18nService,
password
);
case "lastpasscsv":
case "passboltcsv":
return new LastPassCsvImporter();
case "keepassxcsv":
return new KeePassXCsvImporter();
case "aviracsv":
return new AviraCsvImporter();
case "blurcsv":
return new BlurCsvImporter();
case "safeincloudxml":
return new SafeInCloudXmlImporter();
case "padlockcsv":
return new PadlockCsvImporter();
case "keepass2xml":
return new KeePass2XmlImporter();
case "chromecsv":
case "operacsv":
case "vivaldicsv":
return new ChromeCsvImporter();
case "firefoxcsv":
return new FirefoxCsvImporter();
case "upmcsv":
return new UpmCsvImporter();
case "saferpasscsv":
return new SaferPassCsvImporter();
case "safaricsv":
return new SafariCsvImporter();
case "meldiumcsv":
return new MeldiumCsvImporter();
case "1password1pif":
return new OnePassword1PifImporter();
case "1password1pux":
return new OnePassword1PuxImporter();
case "1passwordwincsv":
return new OnePasswordWinCsvImporter();
case "1passwordmaccsv":
return new OnePasswordMacCsvImporter();
case "keepercsv":
return new KeeperCsvImporter();
// case "keeperjson":
// return new KeeperJsonImporter();
case "passworddragonxml":
return new PasswordDragonXmlImporter();
case "enpasscsv":
return new EnpassCsvImporter();
case "enpassjson":
return new EnpassJsonImporter();
case "pwsafexml":
return new PasswordSafeXmlImporter();
case "dashlanecsv":
return new DashlaneCsvImporter();
case "dashlanejson":
return new DashlaneJsonImporter();
case "msecurecsv":
return new MSecureCsvImporter();
case "stickypasswordxml":
return new StickyPasswordXmlImporter();
case "truekeycsv":
return new TrueKeyCsvImporter();
case "clipperzhtml":
return new ClipperzHtmlImporter();
case "roboformcsv":
return new RoboFormCsvImporter();
case "ascendocsv":
return new AscendoCsvImporter();
case "passwordbossjson":
return new PasswordBossJsonImporter();
case "zohovaultcsv":
return new ZohoVaultCsvImporter();
case "splashidcsv":
return new SplashIdCsvImporter();
case "passkeepcsv":
return new PassKeepCsvImporter();
case "gnomejson":
return new GnomeJsonImporter();
case "passwordagentcsv":
return new PasswordAgentCsvImporter();
case "passpackcsv":
return new PasspackCsvImporter();
case "passmanjson":
return new PassmanJsonImporter();
case "avastcsv":
return new AvastCsvImporter();
case "avastjson":
return new AvastJsonImporter();
case "fsecurefsk":
return new FSecureFskImporter();
case "kasperskytxt":
return new KasperskyTxtImporter();
case "remembearcsv":
return new RememBearCsvImporter();
case "passwordwallettxt":
return new PasswordWalletTxtImporter();
case "mykicsv":
return new MykiCsvImporter();
case "securesafecsv":
return new SecureSafeCsvImporter();
case "logmeoncecsv":
return new LogMeOnceCsvImporter();
case "blackberrycsv":
return new BlackBerryCsvImporter();
case "buttercupcsv":
return new ButtercupCsvImporter();
case "codebookcsv":
return new CodebookCsvImporter();
case "encryptrcsv":
return new EncryptrCsvImporter();
case "yoticsv":
return new YotiCsvImporter();
case "nordpasscsv":
return new NordPassCsvImporter();
case "passkyjson":
return new PasskyJsonImporter();
default:
return null;
}
}
private async postImport(importResult: ImportResult, organizationId: string = null) {
if (organizationId == null) {
const request = new ImportCiphersRequest();
for (let i = 0; i < importResult.ciphers.length; i++) {
const c = await this.cipherService.encrypt(importResult.ciphers[i]);
request.ciphers.push(new CipherRequest(c));
}
if (importResult.folders != null) {
for (let i = 0; i < importResult.folders.length; i++) {
const f = await this.folderService.encrypt(importResult.folders[i]);
request.folders.push(new FolderRequest(f));
}
}
if (importResult.folderRelationships != null) {
importResult.folderRelationships.forEach((r) =>
request.folderRelationships.push(new KvpRequest(r[0], r[1]))
);
}
return await this.apiService.postImportCiphers(request);
} else {
const request = new ImportOrganizationCiphersRequest();
for (let i = 0; i < importResult.ciphers.length; i++) {
importResult.ciphers[i].organizationId = organizationId;
const c = await this.cipherService.encrypt(importResult.ciphers[i]);
request.ciphers.push(new CipherRequest(c));
}
if (importResult.collections != null) {
for (let i = 0; i < importResult.collections.length; i++) {
importResult.collections[i].organizationId = organizationId;
const c = await this.collectionService.encrypt(importResult.collections[i]);
request.collections.push(new CollectionRequest(c));
}
}
if (importResult.collectionRelationships != null) {
importResult.collectionRelationships.forEach((r) =>
request.collectionRelationships.push(new KvpRequest(r[0], r[1]))
);
}
return await this.apiService.postImportOrganizationCiphers(organizationId, request);
}
}
private badData(c: CipherView) {
return (
(c.name == null || c.name === "--") &&
c.type === CipherType.Login &&
c.login != null &&
Utils.isNullOrWhitespace(c.login.password)
);
}
private handleServerError(errorResponse: ErrorResponse, importResult: ImportResult): ImportError {
if (errorResponse.validationErrors == null) {
return new ImportError(errorResponse.message);
}
let errorMessage = "";
Object.entries(errorResponse.validationErrors).forEach(([key, value], index) => {
let item;
let itemType;
const i = Number(key.match(/[0-9]+/)[0]);
switch (key.match(/^\w+/)[0]) {
case "Ciphers":
item = importResult.ciphers[i];
itemType = CipherType[item.type];
break;
case "Folders":
item = importResult.folders[i];
itemType = "Folder";
break;
case "Collections":
item = importResult.collections[i];
itemType = "Collection";
break;
default:
return;
}
if (index > 0) {
errorMessage += "\n\n";
}
if (itemType !== "Folder" && itemType !== "Collection") {
errorMessage += "[" + (i + 1) + "] ";
}
errorMessage += "[" + itemType + '] "' + item.name + '": ' + value;
});
return new ImportError(errorMessage);
}
}