mirror of
https://github.com/bitwarden/jslib
synced 2026-01-19 08:53:15 +00:00
* Add password protected export * Run prettier * Test password protected export service * Create type for known import type strings * Test import service changes * Test bitwarden password importer * Run prettier * Remove unnecessary class properties * Run prettier * Tslint fixes * Add KdfType to password protected export * Linter fixes * run prettier
447 lines
18 KiB
TypeScript
447 lines
18 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.service";
|
|
import { I18nService } from "../abstractions/i18n.service";
|
|
import {
|
|
ImportOption,
|
|
ImportService as ImportServiceAbstraction,
|
|
} from "../abstractions/import.service";
|
|
import { PlatformUtilsService } from "../abstractions/platformUtils.service";
|
|
|
|
import { ImportResult } from "../models/domain/importResult";
|
|
|
|
import { CipherType } from "../enums/cipherType";
|
|
|
|
import { Utils } from "../misc/utils";
|
|
|
|
import { CipherRequest } from "../models/request/cipherRequest";
|
|
import { CollectionRequest } from "../models/request/collectionRequest";
|
|
import { FolderRequest } from "../models/request/folderRequest";
|
|
import { ImportCiphersRequest } from "../models/request/importCiphersRequest";
|
|
import { ImportOrganizationCiphersRequest } from "../models/request/importOrganizationCiphersRequest";
|
|
import { KvpRequest } from "../models/request/kvpRequest";
|
|
|
|
import { ErrorResponse } from "../models/response/errorResponse";
|
|
import { CipherView } from "../models/view/cipherView";
|
|
|
|
import { AscendoCsvImporter } from "../importers/ascendoCsvImporter";
|
|
import { AvastCsvImporter } from "../importers/avastCsvImporter";
|
|
import { AvastJsonImporter } from "../importers/avastJsonImporter";
|
|
import { AviraCsvImporter } from "../importers/aviraCsvImporter";
|
|
import { BitwardenCsvImporter } from "../importers/bitwardenCsvImporter";
|
|
import { BitwardenJsonImporter } from "../importers/bitwardenJsonImporter";
|
|
import { BitwardenPasswordProtectedImporter } from "../importers/bitwardenPasswordProtectedImporter";
|
|
import { BlackBerryCsvImporter } from "../importers/blackBerryCsvImporter";
|
|
import { BlurCsvImporter } from "../importers/blurCsvImporter";
|
|
import { ButtercupCsvImporter } from "../importers/buttercupCsvImporter";
|
|
import { ChromeCsvImporter } from "../importers/chromeCsvImporter";
|
|
import { ClipperzHtmlImporter } from "../importers/clipperzHtmlImporter";
|
|
import { CodebookCsvImporter } from "../importers/codebookCsvImporter";
|
|
import { DashlaneJsonImporter } from "../importers/dashlaneJsonImporter";
|
|
import { EncryptrCsvImporter } from "../importers/encryptrCsvImporter";
|
|
import { EnpassCsvImporter } from "../importers/enpassCsvImporter";
|
|
import { EnpassJsonImporter } from "../importers/enpassJsonImporter";
|
|
import { FirefoxCsvImporter } from "../importers/firefoxCsvImporter";
|
|
import { FSecureFskImporter } from "../importers/fsecureFskImporter";
|
|
import { GnomeJsonImporter } from "../importers/gnomeJsonImporter";
|
|
import { Importer } from "../importers/importer";
|
|
import { KasperskyTxtImporter } from "../importers/kasperskyTxtImporter";
|
|
import { KeePass2XmlImporter } from "../importers/keepass2XmlImporter";
|
|
import { KeePassXCsvImporter } from "../importers/keepassxCsvImporter";
|
|
import { KeeperCsvImporter } from "../importers/keeperImporters/keeperCsvImporter";
|
|
import { KeeperJsonImporter } from "../importers/keeperImporters/keeperJsonImporter";
|
|
import { LastPassCsvImporter } from "../importers/lastpassCsvImporter";
|
|
import { LogMeOnceCsvImporter } from "../importers/logMeOnceCsvImporter";
|
|
import { MeldiumCsvImporter } from "../importers/meldiumCsvImporter";
|
|
import { MSecureCsvImporter } from "../importers/msecureCsvImporter";
|
|
import { MykiCsvImporter } from "../importers/mykiCsvImporter";
|
|
import { NordPassCsvImporter } from "../importers/nordpassCsvImporter";
|
|
import { OnePassword1PifImporter } from "../importers/onepasswordImporters/onepassword1PifImporter";
|
|
import { OnePasswordMacCsvImporter } from "../importers/onepasswordImporters/onepasswordMacCsvImporter";
|
|
import { OnePasswordWinCsvImporter } from "../importers/onepasswordImporters/onepasswordWinCsvImporter";
|
|
import { PadlockCsvImporter } from "../importers/padlockCsvImporter";
|
|
import { PassKeepCsvImporter } from "../importers/passkeepCsvImporter";
|
|
import { PassmanJsonImporter } from "../importers/passmanJsonImporter";
|
|
import { PasspackCsvImporter } from "../importers/passpackCsvImporter";
|
|
import { PasswordAgentCsvImporter } from "../importers/passwordAgentCsvImporter";
|
|
import { PasswordBossJsonImporter } from "../importers/passwordBossJsonImporter";
|
|
import { PasswordDragonXmlImporter } from "../importers/passwordDragonXmlImporter";
|
|
import { PasswordSafeXmlImporter } from "../importers/passwordSafeXmlImporter";
|
|
import { PasswordWalletTxtImporter } from "../importers/passwordWalletTxtImporter";
|
|
import { RememBearCsvImporter } from "../importers/rememBearCsvImporter";
|
|
import { RoboFormCsvImporter } from "../importers/roboformCsvImporter";
|
|
import { SafariCsvImporter } from "../importers/safariCsvImporter";
|
|
import { SafeInCloudXmlImporter } from "../importers/safeInCloudXmlImporter";
|
|
import { SaferPassCsvImporter } from "../importers/saferpassCsvImport";
|
|
import { SecureSafeCsvImporter } from "../importers/secureSafeCsvImporter";
|
|
import { SplashIdCsvImporter } from "../importers/splashIdCsvImporter";
|
|
import { StickyPasswordXmlImporter } from "../importers/stickyPasswordXmlImporter";
|
|
import { TrueKeyCsvImporter } from "../importers/truekeyCsvImporter";
|
|
import { UpmCsvImporter } from "../importers/upmCsvImporter";
|
|
import { YotiCsvImporter } from "../importers/yotiCsvImporter";
|
|
import { ZohoVaultCsvImporter } from "../importers/zohoVaultCsvImporter";
|
|
|
|
const featuredImportOptions = [
|
|
{ id: "bitwardenjson", name: "Bitwarden (json)" },
|
|
{ id: "bitwardencsv", name: "Bitwarden (csv)" },
|
|
{ id: "chromecsv", name: "Chrome (csv)" },
|
|
{ id: "dashlanejson", name: "Dashlane (json)" },
|
|
{ id: "firefoxcsv", name: "Firefox (csv)" },
|
|
{ id: "keepass2xml", name: "KeePass 2 (xml)" },
|
|
{ id: "lastpasscsv", name: "LastPass (csv)" },
|
|
{ id: "safaricsv", name: "Safari and macOS (csv)" },
|
|
{ id: "1password1pif", name: "1Password (1pif)" },
|
|
] as const;
|
|
|
|
const regularImportOptions = [
|
|
{ id: "keepassxcsv", name: "KeePassX (csv)" },
|
|
{ id: "1passwordwincsv", name: "1Password 6 and 7 Windows (csv)" },
|
|
{ id: "1passwordmaccsv", name: "1Password 6 and 7 Mac (csv)" },
|
|
{ id: "roboformcsv", name: "RoboForm (csv)" },
|
|
{ id: "keepercsv", name: "Keeper (csv)" },
|
|
// Temporarily remove this option for the Feb release
|
|
// { id: "keeperjson", name: "Keeper (json)" },
|
|
{ id: "enpasscsv", name: "Enpass (csv)" },
|
|
{ id: "enpassjson", name: "Enpass (json)" },
|
|
{ id: "safeincloudxml", name: "SafeInCloud (xml)" },
|
|
{ id: "pwsafexml", name: "Password Safe (xml)" },
|
|
{ id: "stickypasswordxml", name: "Sticky Password (xml)" },
|
|
{ id: "msecurecsv", name: "mSecure (csv)" },
|
|
{ id: "truekeycsv", name: "True Key (csv)" },
|
|
{ id: "passwordbossjson", name: "Password Boss (json)" },
|
|
{ id: "zohovaultcsv", name: "Zoho Vault (csv)" },
|
|
{ id: "splashidcsv", name: "SplashID (csv)" },
|
|
{ id: "passworddragonxml", name: "Password Dragon (xml)" },
|
|
{ id: "padlockcsv", name: "Padlock (csv)" },
|
|
{ id: "passboltcsv", name: "Passbolt (csv)" },
|
|
{ id: "clipperzhtml", name: "Clipperz (html)" },
|
|
{ id: "aviracsv", name: "Avira (csv)" },
|
|
{ id: "saferpasscsv", name: "SaferPass (csv)" },
|
|
{ id: "upmcsv", name: "Universal Password Manager (csv)" },
|
|
{ id: "ascendocsv", name: "Ascendo DataVault (csv)" },
|
|
{ id: "meldiumcsv", name: "Meldium (csv)" },
|
|
{ id: "passkeepcsv", name: "PassKeep (csv)" },
|
|
{ id: "operacsv", name: "Opera (csv)" },
|
|
{ id: "vivaldicsv", name: "Vivaldi (csv)" },
|
|
{ id: "gnomejson", name: "GNOME Passwords and Keys/Seahorse (json)" },
|
|
{ id: "blurcsv", name: "Blur (csv)" },
|
|
{ id: "passwordagentcsv", name: "Password Agent (csv)" },
|
|
{ id: "passpackcsv", name: "Passpack (csv)" },
|
|
{ id: "passmanjson", name: "Passman (json)" },
|
|
{ id: "avastcsv", name: "Avast Passwords (csv)" },
|
|
{ id: "avastjson", name: "Avast Passwords (json)" },
|
|
{ id: "fsecurefsk", name: "F-Secure KEY (fsk)" },
|
|
{ id: "kasperskytxt", name: "Kaspersky Password Manager (txt)" },
|
|
{ id: "remembearcsv", name: "RememBear (csv)" },
|
|
{ id: "passwordwallettxt", name: "PasswordWallet (txt)" },
|
|
{ id: "mykicsv", name: "Myki (csv)" },
|
|
{ id: "securesafecsv", name: "SecureSafe (csv)" },
|
|
{ id: "logmeoncecsv", name: "LogMeOnce (csv)" },
|
|
{ id: "blackberrycsv", name: "BlackBerry Password Keeper (csv)" },
|
|
{ id: "buttercupcsv", name: "Buttercup (csv)" },
|
|
{ id: "codebookcsv", name: "Codebook (csv)" },
|
|
{ id: "encryptrcsv", name: "Encryptr (csv)" },
|
|
{ id: "yoticsv", name: "Yoti (csv)" },
|
|
{ id: "nordpasscsv", name: "Nordpass (csv)" },
|
|
] as const;
|
|
|
|
export type ImportType =
|
|
| typeof featuredImportOptions[number]["id"]
|
|
| typeof regularImportOptions[number]["id"]
|
|
| "bitwardenpasswordprotected";
|
|
|
|
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 platformUtilsService: PlatformUtilsService,
|
|
private cryptoService: CryptoService
|
|
) {}
|
|
|
|
getImportOptions(): ImportOption[] {
|
|
return this.featuredImportOptions.concat(this.regularImportOptions);
|
|
}
|
|
|
|
async import(
|
|
importer: Importer,
|
|
fileContents: string,
|
|
organizationId: string = null
|
|
): Promise<Error> {
|
|
const importResult = await importer.parse(fileContents);
|
|
if (importResult.success) {
|
|
if (importResult.folders.length === 0 && importResult.ciphers.length === 0) {
|
|
return new Error(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 Error(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 Error(importResult.errorMessage);
|
|
} else {
|
|
return new Error(this.i18nService.t("importFormatError"));
|
|
}
|
|
}
|
|
}
|
|
|
|
getImporter(
|
|
format: ImportType,
|
|
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, 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,
|
|
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 "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 "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();
|
|
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): Error {
|
|
if (errorResponse.validationErrors == null) {
|
|
return new Error(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 Error(errorMessage);
|
|
}
|
|
}
|