From 6f75e0bba06af34574d84f73a14992a350b3afed Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Thu, 5 Jul 2018 23:38:36 -0400 Subject: [PATCH] org imports --- jslib | 2 +- src/app/app-routing.module.ts | 3 +- src/app/app.module.ts | 2 + .../organizations/tools/import.component.ts | 86 +++++++++ src/app/tools/import.component.html | 6 +- src/app/tools/import.component.ts | 173 ++++++++++-------- 6 files changed, 190 insertions(+), 82 deletions(-) create mode 100644 src/app/organizations/tools/import.component.ts diff --git a/jslib b/jslib index ed93fa9ea3..a600c4a539 160000 --- a/jslib +++ b/jslib @@ -1 +1 @@ -Subproject commit ed93fa9ea3c486d78ba2591b3057245ead147a15 +Subproject commit a600c4a5398f8893d4cfea298fcfb66f90c3d975 diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index d3a9d77ced..c2b67cbb5d 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -15,6 +15,7 @@ import { RegisterComponent } from './accounts/register.component'; import { TwoFactorComponent } from './accounts/two-factor.component'; import { ExportComponent as OrgExportComponent } from './organizations/tools/export.component'; +import { ImportComponent as OrgImportComponent } from './organizations/tools/import.component'; import { ToolsComponent as OrgToolsComponent } from './organizations/tools/tools.component'; import { VaultComponent as OrgVaultComponent } from './organizations/vault/vault.component'; @@ -122,7 +123,7 @@ const routes: Routes = [ data: { allowedTypes: [OrganizationUserType.Owner, OrganizationUserType.Admin] }, children: [ { path: '', pathMatch: 'full', redirectTo: 'export' }, - // { path: 'import', component: ImportComponent, data: { titleId: 'importData' } }, + { path: 'import', component: OrgImportComponent, data: { titleId: 'importData' } }, { path: 'export', component: OrgExportComponent, data: { titleId: 'exportVault' } }, ], }, diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 51c28cb4bc..7676a10a0b 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -33,6 +33,7 @@ import { TwoFactorOptionsComponent } from './accounts/two-factor-options.compone import { TwoFactorComponent } from './accounts/two-factor.component'; import { ExportComponent as OrgExportComponent } from './organizations/tools/export.component'; +import { ImportComponent as OrgImportComponent } from './organizations/tools/import.component'; import { ToolsComponent as OrgToolsComponent } from './organizations/tools/tools.component'; import { AddEditComponent as OrgAddEditComponent } from './organizations/vault/add-edit.component'; @@ -163,6 +164,7 @@ import { SearchCiphersPipe } from 'jslib/angular/pipes/search-ciphers.pipe'; OrgCiphersComponent, OrgCollectionsComponent, OrgExportComponent, + OrgImportComponent, OrgGroupingsComponent, OrgToolsComponent, OrganizationsComponent, diff --git a/src/app/organizations/tools/import.component.ts b/src/app/organizations/tools/import.component.ts new file mode 100644 index 0000000000..1b8e800c63 --- /dev/null +++ b/src/app/organizations/tools/import.component.ts @@ -0,0 +1,86 @@ +import { Component } from '@angular/core'; +import { + ActivatedRoute, + Router, +} from '@angular/router'; + +import { ToasterService } from 'angular2-toaster'; +import { Angulartics2 } from 'angulartics2'; + +import { ApiService } from 'jslib/abstractions/api.service'; +import { CipherService } from 'jslib/abstractions/cipher.service'; +import { CollectionService } from 'jslib/abstractions/collection.service'; +import { FolderService } from 'jslib/abstractions/folder.service'; +import { I18nService } from 'jslib/abstractions/i18n.service'; + +import { Importer } from 'jslib/importers/importer'; + +import { CipherRequest } from 'jslib/models/request/cipherRequest'; +import { CollectionRequest } from 'jslib/models/request/collectionRequest'; +import { ImportOrganizationCiphersRequest } from 'jslib/models/request/importOrganizationCiphersRequest'; +import { KvpRequest } from 'jslib/models/request/kvpRequest'; + +import { ImportResult } from 'jslib/models/domain/importResult'; + +import { ImportComponent as BaseImportComponent } from '../../tools/import.component'; + +@Component({ + selector: 'app-org-import', + templateUrl: '../../tools/import.component.html', +}) +export class ImportComponent extends BaseImportComponent { + organizationId: string; + + constructor(i18nService: I18nService, analytics: Angulartics2, + toasterService: ToasterService, cipherService: CipherService, + folderService: FolderService, apiService: ApiService, + router: Router, private collectionService: CollectionService, + private route: ActivatedRoute) { + super(i18nService, analytics, toasterService, cipherService, folderService, apiService, router); + } + + ngOnInit() { + this.route.parent.parent.params.subscribe(async (params) => { + this.organizationId = params.organizationId; + super.ngOnInit(); + }); + } + + protected async postImport(importResult: ImportResult) { + const request = new ImportOrganizationCiphersRequest(); + for (let i = 0; i < importResult.ciphers.length; i++) { + importResult.ciphers[i].organizationId = this.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 = this.organizationId; + const c = await this.collectionService.encrypt(importResult.collections[i]); + request.collections.push(new CollectionRequest(c)); + } + } + if (importResult.collectionRelationships != null) { + importResult.collectionRelationships.forEach((v: number, k: number) => + request.collectionRelationships.push(new KvpRequest(k, v))); + } + return await this.apiService.postImportOrganizationCiphers(this.organizationId, request); + } + + protected setImportOptions() { + this.featuredImportOptions = [ + { id: null, name: '-- ' + this.i18nService.t('select') + ' --' }, + { id: 'bitwardencsv', name: 'Bitwarden (csv)' }, + { id: 'lastpasscsv', name: 'LastPass (csv)' }, + ]; + this.importOptions = []; + } + + protected getImporter(): Importer { + const importer = super.getImporter(); + if (importer != null) { + importer.organization = true; + } + return importer; + } +} diff --git a/src/app/tools/import.component.html b/src/app/tools/import.component.html index 7f30505912..b50fc50cd2 100644 --- a/src/app/tools/import.component.html +++ b/src/app/tools/import.component.html @@ -8,8 +8,10 @@ diff --git a/src/app/tools/import.component.ts b/src/app/tools/import.component.ts index def085f1d9..d011998ead 100644 --- a/src/app/tools/import.component.ts +++ b/src/app/tools/import.component.ts @@ -1,5 +1,6 @@ import { Component, + OnInit, } from '@angular/core'; import { Router } from '@angular/router'; @@ -11,6 +12,8 @@ import { CipherService } from 'jslib/abstractions/cipher.service'; import { FolderService } from 'jslib/abstractions/folder.service'; import { I18nService } from 'jslib/abstractions/i18n.service'; +import { ImportResult } from 'jslib/models/domain/importResult'; + import { CipherRequest } from 'jslib/models/request/cipherRequest'; import { FolderRequest } from 'jslib/models/request/folderRequest'; import { ImportCiphersRequest } from 'jslib/models/request/importCiphersRequest'; @@ -28,57 +31,22 @@ import { CipherView } from 'jslib/models/view'; selector: 'app-import', templateUrl: 'import.component.html', }) -export class ImportComponent { +export class ImportComponent implements OnInit { featuredImportOptions: any[]; importOptions: any[]; format: string = null; fileContents: string; - formPromise: Promise; - constructor(private i18nService: I18nService, private analytics: Angulartics2, - private toasterService: ToasterService, private cipherService: CipherService, - private folderService: FolderService, private apiService: ApiService, - private router: Router) { - this.featuredImportOptions = [ - { id: null, name: '-- ' + i18nService.t('select') + ' --' }, - { id: 'bitwardencsv', name: 'Bitwarden (csv)' }, - { id: 'lastpasscsv', name: 'LastPass (csv)' }, - { id: 'chromecsv', name: 'Chrome (csv)' }, - { id: 'firefoxcsv', name: 'Firefox (csv)' }, - { id: 'keepass2xml', name: 'KeePass 2 (xml)' }, - { id: '1password1pif', name: '1Password (1pif)' }, - { id: 'dashlanecsv', name: 'Dashlane (csv)' }, - ]; + constructor(protected i18nService: I18nService, protected analytics: Angulartics2, + protected toasterService: ToasterService, protected cipherService: CipherService, + protected folderService: FolderService, protected apiService: ApiService, + protected router: Router) { + } - this.importOptions = [ - { id: 'keepassxcsv', name: 'KeePassX (csv)' }, - { id: '1password6wincsv', name: '1Password 6 Windows (csv)' }, - { id: 'roboformcsv', name: 'RoboForm (csv)' }, - { id: 'keepercsv', name: 'Keeper (csv)' }, - { id: 'enpasscsv', name: 'Enpass (csv)' }, - { 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: '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)' }, - ].sort((a, b) => { + ngOnInit() { + this.setImportOptions(); + this.importOptions.sort((a, b) => { if (a.name == null && b.name != null) { return -1; } @@ -141,20 +109,8 @@ export class ImportComponent { } } - 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)); - } - for (let i = 0; i < importResult.folders.length; i++) { - const f = await this.folderService.encrypt(importResult.folders[i]); - request.folders.push(new FolderRequest(f)); - } - importResult.folderRelationships.forEach((v: number, k: number) => - request.folderRelationships.push(new KvpRequest(k, v))); - try { - this.formPromise = this.apiService.postImportCiphers(request); + this.formPromise = this.postImport(importResult); await this.formPromise; this.analytics.eventTrack.next({ action: 'Imported Data', @@ -180,6 +136,88 @@ export class ImportComponent { return null; } + protected async postImport(importResult: ImportResult) { + 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((v: number, k: number) => + request.folderRelationships.push(new KvpRequest(k, v))); + } + return await this.apiService.postImportCiphers(request); + } + + protected setImportOptions() { + this.featuredImportOptions = [ + { id: null, name: '-- ' + this.i18nService.t('select') + ' --' }, + { id: 'bitwardencsv', name: 'Bitwarden (csv)' }, + { id: 'lastpasscsv', name: 'LastPass (csv)' }, + { id: 'chromecsv', name: 'Chrome (csv)' }, + { id: 'firefoxcsv', name: 'Firefox (csv)' }, + { id: 'keepass2xml', name: 'KeePass 2 (xml)' }, + { id: '1password1pif', name: '1Password (1pif)' }, + { id: 'dashlanecsv', name: 'Dashlane (csv)' }, + ]; + + this.importOptions = [ + { id: 'keepassxcsv', name: 'KeePassX (csv)' }, + { id: '1password6wincsv', name: '1Password 6 Windows (csv)' }, + { id: 'roboformcsv', name: 'RoboForm (csv)' }, + { id: 'keepercsv', name: 'Keeper (csv)' }, + { id: 'enpasscsv', name: 'Enpass (csv)' }, + { 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: '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)' }, + ]; + } + + protected getImporter(): Importer { + if (this.format == null || this.format === '') { + return null; + } + + switch (this.format) { + case 'bitwardencsv': + return new BitwardenCsvImporter(); + case 'lastpasscsv': + return new LastPassCsvImporter(); + case 'keepassxcsv': + return new KeePassXCsvImporter(); + case 'aviracsv': + return new AviraCsvImporter(); + case 'blurcsv': + return new BlurCsvImporter(); + default: + return null; + } + } + private error(errorMessage: string) { this.analytics.eventTrack.next({ action: 'Import Data Failed', @@ -213,27 +251,6 @@ export class ImportComponent { }); } - private getImporter(): Importer { - if (this.format == null || this.format === '') { - return null; - } - - switch (this.format) { - case 'bitwardencsv': - return new BitwardenCsvImporter(); - case 'lastpasscsv': - return new LastPassCsvImporter(); - case 'keepassxcsv': - return new KeePassXCsvImporter(); - case 'aviracsv': - return new AviraCsvImporter(); - case 'blurcsv': - return new BlurCsvImporter(); - default: - return null; - } - } - private badData(c: CipherView) { return (c.name == null || c.name === '--') && (c.login != null && (c.login.password == null || c.login.password === ''));