mirror of
https://github.com/bitwarden/browser
synced 2025-12-14 07:13:32 +00:00
[PS-2108] Enpass importer add support for androidurl's (#4314)
* Move and rename importers ater new naming convention Create a subfolder to hold all enpass-importers Change names to new naming convention Fix imports Remove entries from whitelist * Added types for exported enpass json file * Add unit tests to verify for current behaviour * Prefer types over enums * Replace `any` types with defined Enpass types * Add support for parsing Android urls Fixes #2831 Added test-file with several combinations Wrote test cases to verify
This commit is contained in:
committed by
GitHub
parent
3976271d61
commit
ae3edcc34d
@@ -1,11 +1,10 @@
|
||||
import { CipherType } from "../enums/cipherType";
|
||||
import { SecureNoteType } from "../enums/secureNoteType";
|
||||
import { ImportResult } from "../models/domain/import-result";
|
||||
import { CardView } from "../models/view/card.view";
|
||||
import { SecureNoteView } from "../models/view/secure-note.view";
|
||||
|
||||
import { BaseImporter } from "./base-importer";
|
||||
import { Importer } from "./importer";
|
||||
import { CipherType } from "../../enums/cipherType";
|
||||
import { SecureNoteType } from "../../enums/secureNoteType";
|
||||
import { ImportResult } from "../../models/domain/import-result";
|
||||
import { CardView } from "../../models/view/card.view";
|
||||
import { SecureNoteView } from "../../models/view/secure-note.view";
|
||||
import { BaseImporter } from "../base-importer";
|
||||
import { Importer } from "../importer";
|
||||
|
||||
export class EnpassCsvImporter extends BaseImporter implements Importer {
|
||||
parse(data: string): Promise<ImportResult> {
|
||||
@@ -1,17 +1,21 @@
|
||||
import { CipherType } from "../enums/cipherType";
|
||||
import { FieldType } from "../enums/fieldType";
|
||||
import { ImportResult } from "../models/domain/import-result";
|
||||
import { CardView } from "../models/view/card.view";
|
||||
import { CipherView } from "../models/view/cipher.view";
|
||||
import { FolderView } from "../models/view/folder.view";
|
||||
import { CipherType } from "../../enums/cipherType";
|
||||
import { FieldType } from "../../enums/fieldType";
|
||||
import { ImportResult } from "../../models/domain/import-result";
|
||||
import { CardView } from "../../models/view/card.view";
|
||||
import { CipherView } from "../../models/view/cipher.view";
|
||||
import { FolderView } from "../../models/view/folder.view";
|
||||
import { BaseImporter } from "../base-importer";
|
||||
import { Importer } from "../importer";
|
||||
|
||||
import { BaseImporter } from "./base-importer";
|
||||
import { Importer } from "./importer";
|
||||
import { EnpassJsonFile, EnpassFolder, EnpassField } from "./types/enpass-json-type";
|
||||
|
||||
type EnpassFolderTreeItem = EnpassFolder & { children: EnpassFolderTreeItem[] };
|
||||
const androidUrlRegex = new RegExp("androidapp://.*==@", "g");
|
||||
|
||||
export class EnpassJsonImporter extends BaseImporter implements Importer {
|
||||
parse(data: string): Promise<ImportResult> {
|
||||
const result = new ImportResult();
|
||||
const results = JSON.parse(data);
|
||||
const results: EnpassJsonFile = JSON.parse(data);
|
||||
if (results == null || results.items == null || results.items.length === 0) {
|
||||
result.success = false;
|
||||
return Promise.resolve(result);
|
||||
@@ -28,7 +32,7 @@ export class EnpassJsonImporter extends BaseImporter implements Importer {
|
||||
result.folders.push(f);
|
||||
});
|
||||
|
||||
results.items.forEach((item: any) => {
|
||||
results.items.forEach((item) => {
|
||||
if (item.folders != null && item.folders.length > 0 && foldersIndexMap.has(item.folders[0])) {
|
||||
result.folderRelationships.push([
|
||||
result.ciphers.length,
|
||||
@@ -50,7 +54,7 @@ export class EnpassJsonImporter extends BaseImporter implements Importer {
|
||||
this.processCard(cipher, item.fields);
|
||||
} else if (
|
||||
item.template_type.indexOf("identity.") < 0 &&
|
||||
item.fields.some((f: any) => f.type === "password" && !this.isNullOrWhitespace(f.value))
|
||||
item.fields.some((f) => f.type === "password" && !this.isNullOrWhitespace(f.value))
|
||||
) {
|
||||
this.processLogin(cipher, item.fields);
|
||||
} else {
|
||||
@@ -68,9 +72,9 @@ export class EnpassJsonImporter extends BaseImporter implements Importer {
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
private processLogin(cipher: CipherView, fields: any[]) {
|
||||
private processLogin(cipher: CipherView, fields: EnpassField[]) {
|
||||
const urls: string[] = [];
|
||||
fields.forEach((field: any) => {
|
||||
fields.forEach((field) => {
|
||||
if (this.isNullOrWhitespace(field.value) || field.type === "section") {
|
||||
return;
|
||||
}
|
||||
@@ -86,6 +90,13 @@ export class EnpassJsonImporter extends BaseImporter implements Importer {
|
||||
cipher.login.totp = field.value;
|
||||
} else if (field.type === "url") {
|
||||
urls.push(field.value);
|
||||
} else if (field.type === ".Android#") {
|
||||
let cleanedValue = field.value.startsWith("androidapp://")
|
||||
? field.value
|
||||
: "androidapp://" + field.value;
|
||||
cleanedValue = cleanedValue.replace("android://", "");
|
||||
cleanedValue = cleanedValue.replace(androidUrlRegex, "androidapp://");
|
||||
urls.push(cleanedValue);
|
||||
} else {
|
||||
this.processKvp(
|
||||
cipher,
|
||||
@@ -98,10 +109,10 @@ export class EnpassJsonImporter extends BaseImporter implements Importer {
|
||||
cipher.login.uris = this.makeUriArray(urls);
|
||||
}
|
||||
|
||||
private processCard(cipher: CipherView, fields: any[]) {
|
||||
private processCard(cipher: CipherView, fields: EnpassField[]) {
|
||||
cipher.card = new CardView();
|
||||
cipher.type = CipherType.Card;
|
||||
fields.forEach((field: any) => {
|
||||
fields.forEach((field) => {
|
||||
if (
|
||||
this.isNullOrWhitespace(field.value) ||
|
||||
field.type === "section" ||
|
||||
@@ -137,8 +148,8 @@ export class EnpassJsonImporter extends BaseImporter implements Importer {
|
||||
});
|
||||
}
|
||||
|
||||
private processNote(cipher: CipherView, fields: any[]) {
|
||||
fields.forEach((field: any) => {
|
||||
private processNote(cipher: CipherView, fields: EnpassField[]) {
|
||||
fields.forEach((field) => {
|
||||
if (this.isNullOrWhitespace(field.value) || field.type === "section") {
|
||||
return;
|
||||
}
|
||||
@@ -151,17 +162,17 @@ export class EnpassJsonImporter extends BaseImporter implements Importer {
|
||||
});
|
||||
}
|
||||
|
||||
private buildFolderTree(folders: any[]): any[] {
|
||||
private buildFolderTree(folders: EnpassFolder[]): EnpassFolderTreeItem[] {
|
||||
if (folders == null) {
|
||||
return [];
|
||||
}
|
||||
const folderTree: any[] = [];
|
||||
const map = new Map<string, any>([]);
|
||||
folders.forEach((obj: any) => {
|
||||
const folderTree: EnpassFolderTreeItem[] = [];
|
||||
const map = new Map<string, EnpassFolderTreeItem>([]);
|
||||
folders.forEach((obj: EnpassFolderTreeItem) => {
|
||||
map.set(obj.uuid, obj);
|
||||
obj.children = [];
|
||||
});
|
||||
folders.forEach((obj: any) => {
|
||||
folders.forEach((obj: EnpassFolderTreeItem) => {
|
||||
if (obj.parent_uuid != null && obj.parent_uuid !== "" && map.has(obj.parent_uuid)) {
|
||||
map.get(obj.parent_uuid).children.push(obj);
|
||||
} else {
|
||||
@@ -171,11 +182,15 @@ export class EnpassJsonImporter extends BaseImporter implements Importer {
|
||||
return folderTree;
|
||||
}
|
||||
|
||||
private flattenFolderTree(titlePrefix: string, tree: any[], map: Map<string, string>) {
|
||||
private flattenFolderTree(
|
||||
titlePrefix: string,
|
||||
tree: EnpassFolderTreeItem[],
|
||||
map: Map<string, string>
|
||||
) {
|
||||
if (tree == null) {
|
||||
return;
|
||||
}
|
||||
tree.forEach((f: any) => {
|
||||
tree.forEach((f) => {
|
||||
if (f.title != null && f.title.trim() !== "") {
|
||||
let title = f.title.trim();
|
||||
if (titlePrefix != null && titlePrefix.trim() !== "") {
|
||||
@@ -0,0 +1,79 @@
|
||||
type Login = "login.default";
|
||||
|
||||
type CreditCard = "creditcard.default";
|
||||
|
||||
type Identity = "identity.default";
|
||||
|
||||
type Note = "note.default";
|
||||
|
||||
type Password = "password.default";
|
||||
|
||||
type Finance =
|
||||
| "finance.stock"
|
||||
| "finance.bankaccount"
|
||||
| "finance.loan"
|
||||
| "finance.mutualfund"
|
||||
| "finance.insurance"
|
||||
| "finance.other";
|
||||
|
||||
type License = "license.driving" | "license.hunting" | "license.software" | "license.other";
|
||||
|
||||
type Travel =
|
||||
| "travel.passport"
|
||||
| "travel.flightdetails"
|
||||
| "travel.hotelreservation"
|
||||
| "travel.visa"
|
||||
| "travel.freqflyer"
|
||||
| "travel.other";
|
||||
|
||||
type Computer =
|
||||
| "computer.database"
|
||||
| "computer.emailaccount"
|
||||
| "computer.ftp"
|
||||
| "computer.messaging"
|
||||
| "computer.internetprovider"
|
||||
| "computer.server"
|
||||
| "computer.wifi"
|
||||
| "computer.hosting"
|
||||
| "computer.other";
|
||||
|
||||
type Misc =
|
||||
| "misc.Aadhar"
|
||||
| "misc.address"
|
||||
| "misc.library"
|
||||
| "misc.rewardprogram"
|
||||
| "misc.lens"
|
||||
| "misc.service"
|
||||
| "misc.vehicleinfo"
|
||||
| "misc.itic"
|
||||
| "misc.itz"
|
||||
| "misc.propertyinfo"
|
||||
| "misc.clothsize"
|
||||
| "misc.contact"
|
||||
| "misc.membership"
|
||||
| "misc.cellphone"
|
||||
| "misc.emergencyno"
|
||||
| "misc.pan"
|
||||
| "misc.identity"
|
||||
| "misc.regcode"
|
||||
| "misc.prescription"
|
||||
| "misc.serial"
|
||||
| "misc.socialsecurityno"
|
||||
| "misc.isic"
|
||||
| "misc.calling"
|
||||
| "misc.voicemail"
|
||||
| "misc.voter"
|
||||
| "misc.combilock"
|
||||
| "misc.other";
|
||||
|
||||
export type EnpassItemTemplate =
|
||||
| Login
|
||||
| CreditCard
|
||||
| Identity
|
||||
| Note
|
||||
| Password
|
||||
| Finance
|
||||
| License
|
||||
| Travel
|
||||
| Computer
|
||||
| Misc;
|
||||
85
libs/common/src/importers/enpass/types/enpass-json-type.ts
Normal file
85
libs/common/src/importers/enpass/types/enpass-json-type.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import { EnpassItemTemplate } from "./enpass-item-templates";
|
||||
|
||||
export type EnpassJsonFile = {
|
||||
folders: EnpassFolder[];
|
||||
items: EnpassItem[];
|
||||
};
|
||||
|
||||
export type EnpassFolder = {
|
||||
icon: string;
|
||||
parent_uuid: string;
|
||||
title: string;
|
||||
updated_at: number;
|
||||
uuid: string;
|
||||
};
|
||||
|
||||
export type EnpassItem = {
|
||||
archived: number;
|
||||
auto_submit: number;
|
||||
category: string;
|
||||
createdAt: number;
|
||||
favorite: number;
|
||||
fields?: EnpassField[];
|
||||
icon: Icon;
|
||||
note: string;
|
||||
subtitle: string;
|
||||
template_type: EnpassItemTemplate;
|
||||
title: string;
|
||||
trashed: number;
|
||||
updated_at: number;
|
||||
uuid: string;
|
||||
folders?: string[];
|
||||
};
|
||||
|
||||
export type EnpassFieldType =
|
||||
| "text"
|
||||
| "password"
|
||||
| "pin"
|
||||
| "numeric"
|
||||
| "date"
|
||||
| "email"
|
||||
| "url"
|
||||
| "phone"
|
||||
| "username"
|
||||
| "totp"
|
||||
| "multiline"
|
||||
| "ccName"
|
||||
| "ccNumber"
|
||||
| "ccCvc"
|
||||
| "ccPin"
|
||||
| "ccExpiry"
|
||||
| "ccBankname"
|
||||
| "ccTxnpassword"
|
||||
| "ccType"
|
||||
| "ccValidfrom"
|
||||
| "section"
|
||||
| ".Android#";
|
||||
|
||||
export type EnpassField = {
|
||||
deleted: number;
|
||||
history?: History[];
|
||||
label: string;
|
||||
order: number;
|
||||
sensitive: number;
|
||||
type: EnpassFieldType;
|
||||
uid: number;
|
||||
updated_at: number;
|
||||
value: string;
|
||||
value_updated_at: number;
|
||||
};
|
||||
|
||||
export type History = {
|
||||
updated_at: number;
|
||||
value: string;
|
||||
};
|
||||
|
||||
export type Icon = {
|
||||
fav: string;
|
||||
image: Image;
|
||||
type: number;
|
||||
uuid: string;
|
||||
};
|
||||
|
||||
export type Image = {
|
||||
file: string;
|
||||
};
|
||||
@@ -28,8 +28,8 @@ 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 { EnpassCsvImporter } from "../importers/enpass/enpass-csv-importer";
|
||||
import { EnpassJsonImporter } from "../importers/enpass/enpass-json-importer";
|
||||
import { FirefoxCsvImporter } from "../importers/firefox-csv-importer";
|
||||
import { FSecureFskImporter } from "../importers/fsecure/fsecure-fsk-importer";
|
||||
import { GnomeJsonImporter } from "../importers/gnome-json-importer";
|
||||
|
||||
Reference in New Issue
Block a user