diff --git a/src/importers/baseImporter.ts b/src/importers/baseImporter.ts index 620627facb..d995d71221 100644 --- a/src/importers/baseImporter.ts +++ b/src/importers/baseImporter.ts @@ -1,5 +1,8 @@ import * as papa from 'papaparse'; +import { ImportResult } from '../models/domain/importResult'; + +import { CollectionView } from '../models/view/collectionView'; import { LoginUriView } from '../models/view/loginUriView'; import { Utils } from '../misc/utils'; @@ -194,4 +197,15 @@ export abstract class BaseImporter { return null; } + + protected moveFoldersToCollections(result: ImportResult) { + result.folderRelationships.forEach((value, key) => result.collectionRelationships.set(key, value)); + result.collections = result.folders.map((f) => { + const collection = new CollectionView(); + collection.name = f.name; + return collection; + }); + result.folderRelationships = new Map(); + result.folders = []; + } } diff --git a/src/importers/bitwardenCsvImporter.ts b/src/importers/bitwardenCsvImporter.ts index 4643e17021..acfdc3d1ed 100644 --- a/src/importers/bitwardenCsvImporter.ts +++ b/src/importers/bitwardenCsvImporter.ts @@ -4,6 +4,7 @@ import { Importer } from './importer'; import { ImportResult } from '../models/domain/importResult'; import { CipherView } from '../models/view/cipherView'; +import { CollectionView } from '../models/view/collectionView'; import { FieldView } from '../models/view/fieldView'; import { FolderView } from '../models/view/folderView'; import { LoginView } from '../models/view/loginView'; @@ -14,7 +15,7 @@ import { FieldType } from '../enums/fieldType'; import { SecureNoteType } from '../enums/secureNoteType'; export class BitwardenCsvImporter extends BaseImporter implements Importer { - parse(data: string): ImportResult { + parse(data: string, organization = false): ImportResult { const result = new ImportResult(); const results = this.parseCsv(data, true); if (results == null) { @@ -23,24 +24,57 @@ export class BitwardenCsvImporter extends BaseImporter implements Importer { } results.forEach((value) => { - let folderIndex = result.folders.length; - const cipherIndex = result.ciphers.length; - const hasFolder = !this.isNullOrWhitespace(value.folder); - let addFolder = hasFolder; + if (organization && !this.isNullOrWhitespace(value.collections)) { + const collections = (value.collections as string).split(','); + collections.forEach((col) => { + let addCollection = true; + let collectionIndex = result.collections.length; - if (hasFolder) { - for (let i = 0; i < result.folders.length; i++) { - if (result.folders[i].name === value.folder) { - addFolder = false; - folderIndex = i; - break; + for (let i = 0; i < result.collections.length; i++) { + if (result.collections[i].name === col) { + addCollection = false; + collectionIndex = i; + break; + } } + + if (addCollection) { + const collection = new CollectionView(); + collection.name = col; + result.collections.push(collection); + } + + result.collectionRelationships.set(result.ciphers.length, collectionIndex); + }); + } else if (!organization) { + let folderIndex = result.folders.length; + const hasFolder = !organization && !this.isNullOrWhitespace(value.folder); + let addFolder = hasFolder; + + if (hasFolder) { + for (let i = 0; i < result.folders.length; i++) { + if (result.folders[i].name === value.folder) { + addFolder = false; + folderIndex = i; + break; + } + } + } + + if (addFolder) { + const f = new FolderView(); + f.name = value.folder; + result.folders.push(f); + } + + if (hasFolder) { + result.folderRelationships.set(result.ciphers.length, folderIndex); } } const cipher = new CipherView(); + cipher.favorite = !organization && this.getValueOrDefault(value.favorite, '0') !== '0' ? true : false; cipher.type = CipherType.Login; - cipher.favorite = this.getValueOrDefault(value.favorite, '0') !== '0' ? true : false; cipher.notes = this.getValueOrDefault(value.notes); cipher.name = this.getValueOrDefault(value.name, '--'); @@ -93,15 +127,6 @@ export class BitwardenCsvImporter extends BaseImporter implements Importer { } result.ciphers.push(cipher); - - if (addFolder) { - const f = new FolderView(); - f.name = value.folder; - result.folders.push(f); - } - if (hasFolder) { - result.folderRelationships.set(cipherIndex, folderIndex); - } }); result.success = true; diff --git a/src/importers/importer.ts b/src/importers/importer.ts index e6df305dde..78f838882f 100644 --- a/src/importers/importer.ts +++ b/src/importers/importer.ts @@ -1,5 +1,5 @@ import { ImportResult } from '../models/domain/importResult'; export interface Importer { - parse(data: string): ImportResult; + parse(data: string, organization?: boolean): ImportResult; } diff --git a/src/importers/keepassxCsvImporter.ts b/src/importers/keepassxCsvImporter.ts index c8ab05ec9f..610351fd09 100644 --- a/src/importers/keepassxCsvImporter.ts +++ b/src/importers/keepassxCsvImporter.ts @@ -19,12 +19,15 @@ export class KeePassXCsvImporter extends BaseImporter implements Importer { } results.forEach((value) => { + if (this.isNullOrWhitespace(value.Title)) { + return; + } + value.Group = !this.isNullOrWhitespace(value.Group) && value.Group.startsWith('Root/') ? value.Group.replace('Root/', '') : value.Group; const groupName = !this.isNullOrWhitespace(value.Group) ? value.Group.split('/').join(' > ') : null; let folderIndex = result.folders.length; - const cipherIndex = result.ciphers.length; const hasFolder = groupName != null; let addFolder = hasFolder; @@ -38,6 +41,15 @@ export class KeePassXCsvImporter extends BaseImporter implements Importer { } } + if (addFolder) { + const f = new FolderView(); + f.name = groupName; + result.folders.push(f); + } + if (hasFolder) { + result.folderRelationships.set(result.ciphers.length, folderIndex); + } + const cipher = new CipherView(); cipher.type = CipherType.Login; cipher.favorite = false; @@ -47,19 +59,7 @@ export class KeePassXCsvImporter extends BaseImporter implements Importer { cipher.login.username = this.getValueOrDefault(value.Username); cipher.login.password = this.getValueOrDefault(value.Password); cipher.login.uris = this.makeUriArray(value.URL); - - if (!this.isNullOrWhitespace(value.Title)) { - result.ciphers.push(cipher); - } - - if (addFolder) { - const f = new FolderView(); - f.name = groupName; - result.folders.push(f); - } - if (hasFolder) { - result.folderRelationships.set(cipherIndex, folderIndex); - } + result.ciphers.push(cipher); }); result.success = true; diff --git a/src/importers/lastpassCsvImporter.ts b/src/importers/lastpassCsvImporter.ts index 1177686e5f..0eba428ebc 100644 --- a/src/importers/lastpassCsvImporter.ts +++ b/src/importers/lastpassCsvImporter.ts @@ -14,7 +14,7 @@ import { CipherType } from '../enums/cipherType'; import { SecureNoteType } from '../enums/secureNoteType'; export class LastPassCsvImporter extends BaseImporter implements Importer { - parse(data: string): ImportResult { + parse(data: string, organization = false): ImportResult { const result = new ImportResult(); const results = this.parseCsv(data, true); if (results == null) { @@ -23,8 +23,8 @@ export class LastPassCsvImporter extends BaseImporter implements Importer { } results.forEach((value, index) => { - let folderIndex = result.folders.length; const cipherIndex = result.ciphers.length; + let folderIndex = result.folders.length; const hasFolder = this.getValueOrDefault(value.grouping, '(none)') !== '(none)'; let addFolder = hasFolder; @@ -38,7 +38,7 @@ export class LastPassCsvImporter extends BaseImporter implements Importer { } } - const cipher = this.buildBaseCipher(value); + const cipher = this.buildBaseCipher(value, organization); if (cipher.name === '--' && results.length > 2 && index >= (results.length - 2)) { // LastPass file traditionally has two empty lines at the end return; @@ -60,7 +60,7 @@ export class LastPassCsvImporter extends BaseImporter implements Importer { cipher.notes = this.getValueOrDefault(value.notes); if (!this.isNullOrWhitespace(value.ccnum)) { // there is a card on this identity too - const cardCipher = this.buildBaseCipher(value); + const cardCipher = this.buildBaseCipher(value, organization); cardCipher.identity = null; cardCipher.type = CipherType.Card; cardCipher.card = this.parseCard(value); @@ -80,11 +80,15 @@ export class LastPassCsvImporter extends BaseImporter implements Importer { } }); + if (organization) { + this.moveFoldersToCollections(result); + } + result.success = true; return result; } - private buildBaseCipher(value: any) { + private buildBaseCipher(value: any, organization: boolean) { const cipher = new CipherView(); if (value.hasOwnProperty('profilename') && value.hasOwnProperty('profilelanguage')) { // form fill @@ -100,7 +104,7 @@ export class LastPassCsvImporter extends BaseImporter implements Importer { } } else { // site or secure note - cipher.favorite = this.getValueOrDefault(value.fav, '0') === '1'; // TODO: if org, always false + cipher.favorite = !organization && this.getValueOrDefault(value.fav, '0') === '1'; cipher.name = this.getValueOrDefault(value.name, '--'); cipher.type = value.url === 'http://sn' ? CipherType.SecureNote : CipherType.Login; }