mirror of
https://github.com/bitwarden/browser
synced 2025-12-20 10:13:31 +00:00
[PM-12598] Create dedicated importer for Password-XP (csv) (#11751)
* Create dedicated password-xp csv importer * Add support for importing unmapped columns as custom fields * Add support for importing folders and assiging items to them * On import into an organization, convert folders to collections * Register importer within importService and make it selectable via the UI Add instructions on how to export from Password XP * Mark method as private * Add docs * Add comment around folder detection * Move test data into separate file --------- Co-authored-by: Daniel James Smith <djsmith85@users.noreply.github.com> Co-authored-by: Matt Bishop <mbishop@bitwarden.com>
This commit is contained in:
committed by
GitHub
parent
d4a381e1bd
commit
73632cd368
@@ -380,6 +380,11 @@
|
||||
In the ProtonPass browser extension, go to Settings > Export. Export without PGP
|
||||
encryption and save the zip file.
|
||||
</ng-container>
|
||||
<ng-container *ngIf="format === 'passwordxpcsv'">
|
||||
Select Database → Export to file... menu. → Within the export options, ensure
|
||||
Folder names and Column titles is checked. → Press Browse... button and choose the
|
||||
target file and set it's type to CSV file.
|
||||
</ng-container>
|
||||
<ng-container *ngIf="format === 'netwrixpasswordsecure'">
|
||||
Open the FullClient, go to the Main Menu and select Export. Start the export passwords
|
||||
wizard and follow the instructions to export a CSV file.
|
||||
|
||||
@@ -45,6 +45,7 @@ export { PasswordBossJsonImporter } from "./passwordboss-json-importer";
|
||||
export { PasswordDragonXmlImporter } from "./passworddragon-xml-importer";
|
||||
export { PasswordSafeXmlImporter } from "./passwordsafe-xml-importer";
|
||||
export { PasswordWalletTxtImporter } from "./passwordwallet-txt-importer";
|
||||
export { PasswordXPCsvImporter } from "./passwordxp-csv-importer";
|
||||
export { ProtonPassJsonImporter } from "./protonpass/protonpass-json-importer";
|
||||
export { PsonoJsonImporter } from "./psono/psono-json-importer";
|
||||
export { RememBearCsvImporter } from "./remembear-csv-importer";
|
||||
|
||||
78
libs/importer/src/importers/passwordxp-csv-importer.ts
Normal file
78
libs/importer/src/importers/passwordxp-csv-importer.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
|
||||
import { ImportResult } from "../models/import-result";
|
||||
|
||||
import { BaseImporter } from "./base-importer";
|
||||
import { Importer } from "./importer";
|
||||
|
||||
const _mappedColumns = new Set(["Title", "Username", "URL", "Password", "Description"]);
|
||||
|
||||
/**
|
||||
* PasswordXP CSV importer
|
||||
*/
|
||||
export class PasswordXPCsvImporter extends BaseImporter implements Importer {
|
||||
/**
|
||||
* Parses the PasswordXP CSV data.
|
||||
* @param data
|
||||
*/
|
||||
parse(data: string): Promise<ImportResult> {
|
||||
// The header column 'User name' is parsed by the parser, but cannot be used as a variable. This converts it to a valid variable name, prior to parsing.
|
||||
data = data.replace(";User name;", ";Username;");
|
||||
|
||||
const result = new ImportResult();
|
||||
const results = this.parseCsv(data, true, { skipEmptyLines: true });
|
||||
if (results == null) {
|
||||
result.success = false;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
let currentFolderName = "";
|
||||
results.forEach((row) => {
|
||||
// Skip rows starting with '>>>' as they indicate items following have no folder assigned to them
|
||||
if (row.Title == ">>>") {
|
||||
return;
|
||||
}
|
||||
|
||||
const title = row.Title;
|
||||
// If the title is in the format [title], then it is a folder name
|
||||
if (title.startsWith("[") && title.endsWith("]")) {
|
||||
currentFolderName = title.startsWith("/")
|
||||
? title.replace("/", "")
|
||||
: title.substring(1, title.length - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Utils.isNullOrWhitespace(currentFolderName)) {
|
||||
this.processFolder(result, currentFolderName);
|
||||
}
|
||||
|
||||
const cipher = this.initLoginCipher();
|
||||
cipher.name = this.getValueOrDefault(row.Title);
|
||||
cipher.login.username = this.getValueOrDefault(row.Username);
|
||||
cipher.notes = this.getValueOrDefault(row.Description);
|
||||
cipher.login.uris = this.makeUriArray(row.URL);
|
||||
cipher.login.password = this.getValueOrDefault(row.Password);
|
||||
|
||||
this.importUnmappedFields(cipher, row, _mappedColumns);
|
||||
|
||||
this.convertToNoteIfNeeded(cipher);
|
||||
this.cleanupCipher(cipher);
|
||||
result.ciphers.push(cipher);
|
||||
});
|
||||
|
||||
if (this.organization) {
|
||||
this.moveFoldersToCollections(result);
|
||||
}
|
||||
|
||||
result.success = true;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
private importUnmappedFields(cipher: CipherView, row: any, mappedValues: Set<string>) {
|
||||
const unmappedFields = Object.keys(row).filter((x) => !mappedValues.has(x));
|
||||
unmappedFields.forEach((key) => {
|
||||
const item = row as any;
|
||||
this.processKvp(cipher, key, item[key]);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -70,6 +70,7 @@ export const regularImportOptions = [
|
||||
{ id: "nordpasscsv", name: "Nordpass (csv)" },
|
||||
{ id: "psonojson", name: "Psono (json)" },
|
||||
{ id: "passkyjson", name: "Passky (json)" },
|
||||
{ id: "passwordxpcsv", name: "Password XP (csv)" },
|
||||
{ id: "netwrixpasswordsecure", name: "Netwrix Password Secure (csv)" },
|
||||
] as const;
|
||||
|
||||
|
||||
@@ -84,6 +84,7 @@ import {
|
||||
UpmCsvImporter,
|
||||
YotiCsvImporter,
|
||||
ZohoVaultCsvImporter,
|
||||
PasswordXPCsvImporter,
|
||||
} from "../importers";
|
||||
import { Importer } from "../importers/importer";
|
||||
import {
|
||||
@@ -336,6 +337,8 @@ export class ImportService implements ImportServiceAbstraction {
|
||||
return new PasskyJsonImporter();
|
||||
case "protonpass":
|
||||
return new ProtonPassJsonImporter(this.i18nService);
|
||||
case "passwordxpcsv":
|
||||
return new PasswordXPCsvImporter();
|
||||
case "netwrixpasswordsecure":
|
||||
return new NetwrixPasswordSecureCsvImporter();
|
||||
default:
|
||||
|
||||
Reference in New Issue
Block a user