From f6a7315d1333a6fdf21120b8294ce1a0265a6634 Mon Sep 17 00:00:00 2001 From: CarleyDiaz-Bitwarden <103955722+CarleyDiaz-Bitwarden@users.noreply.github.com> Date: Thu, 16 Jun 2022 16:20:10 -0400 Subject: [PATCH 01/14] Rough draft of Export/Import changes w/ password encryption --- .../file-password-prompt.component.html | 64 ++++++++ .../file-password-prompt.component.ts | 8 + .../app/modules/loose-components.module.ts | 3 + .../organizations/tools/export.component.ts | 13 +- .../organizations/tools/import.component.ts | 17 +- apps/web/src/app/services/services.module.ts | 20 +++ apps/web/src/app/tools/export.component.html | 146 ++++++++++++++++-- apps/web/src/app/tools/export.component.ts | 62 +++++++- apps/web/src/app/tools/import.component.ts | 34 +++- apps/web/src/locales/en/messages.json | 27 ++++ apps/web/src/locales/en_GB/messages.json | 27 ++++ apps/web/src/locales/en_IN/messages.json | 27 ++++ .../services/filePasswordPrompt.service.ts | 10 ++ .../src/components/export.component.ts | 107 +++++++++++-- .../file-password-prompt.component.ts | 58 +++++++ .../services/filePasswordPrompt.service.ts | 51 ++++++ .../common/src/abstractions/export.service.ts | 2 +- .../filePasswordPrompt.service.ts | 5 + libs/common/src/services/export.service.ts | 10 +- 19 files changed, 647 insertions(+), 44 deletions(-) create mode 100644 apps/web/src/app/components/file-password-prompt.component.html create mode 100644 apps/web/src/app/components/file-password-prompt.component.ts create mode 100644 apps/web/src/services/filePasswordPrompt.service.ts create mode 100644 libs/angular/src/components/file-password-prompt.component.ts create mode 100644 libs/angular/src/services/filePasswordPrompt.service.ts create mode 100644 libs/common/src/abstractions/filePasswordPrompt.service.ts diff --git a/apps/web/src/app/components/file-password-prompt.component.html b/apps/web/src/app/components/file-password-prompt.component.html new file mode 100644 index 00000000000..4c98ef66833 --- /dev/null +++ b/apps/web/src/app/components/file-password-prompt.component.html @@ -0,0 +1,64 @@ + diff --git a/apps/web/src/app/components/file-password-prompt.component.ts b/apps/web/src/app/components/file-password-prompt.component.ts new file mode 100644 index 00000000000..b055107c80c --- /dev/null +++ b/apps/web/src/app/components/file-password-prompt.component.ts @@ -0,0 +1,8 @@ +import { Component } from "@angular/core"; + +import { FilePasswordPromptComponent as BaseFilePasswordPrompt } from "@bitwarden/angular/components/file-password-prompt.component"; + +@Component({ + templateUrl: "file-password-prompt.component.html", +}) +export class FilePasswordPromptComponent extends BaseFilePasswordPrompt {} diff --git a/apps/web/src/app/modules/loose-components.module.ts b/apps/web/src/app/modules/loose-components.module.ts index 3fa418f6c5a..382c7fdd193 100644 --- a/apps/web/src/app/modules/loose-components.module.ts +++ b/apps/web/src/app/modules/loose-components.module.ts @@ -19,6 +19,7 @@ import { UpdatePasswordComponent } from "../accounts/update-password.component"; import { UpdateTempPasswordComponent } from "../accounts/update-temp-password.component"; import { VerifyEmailTokenComponent } from "../accounts/verify-email-token.component"; import { VerifyRecoverDeleteComponent } from "../accounts/verify-recover-delete.component"; +import { FilePasswordPromptComponent } from "../components/file-password-prompt.component"; import { NestedCheckboxComponent } from "../components/nested-checkbox.component"; import { OrganizationSwitcherComponent } from "../components/organization-switcher.component"; import { PasswordRepromptComponent } from "../components/password-reprompt.component"; @@ -269,6 +270,7 @@ import { OrganizationBadgeModule } from "./vault/modules/organization-badge/orga PasswordGeneratorHistoryComponent, PasswordGeneratorPolicyComponent, PasswordRepromptComponent, + FilePasswordPromptComponent, PasswordStrengthComponent, PaymentComponent, PaymentMethodComponent, @@ -427,6 +429,7 @@ import { OrganizationBadgeModule } from "./vault/modules/organization-badge/orga PasswordGeneratorHistoryComponent, PasswordGeneratorPolicyComponent, PasswordRepromptComponent, + FilePasswordPromptComponent, PasswordStrengthComponent, PaymentComponent, PaymentMethodComponent, diff --git a/apps/web/src/app/organizations/tools/export.component.ts b/apps/web/src/app/organizations/tools/export.component.ts index 8c482f58b7a..e4b89654e9f 100644 --- a/apps/web/src/app/organizations/tools/export.component.ts +++ b/apps/web/src/app/organizations/tools/export.component.ts @@ -2,6 +2,8 @@ import { Component } from "@angular/core"; import { FormBuilder } from "@angular/forms"; import { ActivatedRoute } from "@angular/router"; +import { ModalService } from "@bitwarden/angular/services/modal.service"; +import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { EventService } from "@bitwarden/common/abstractions/event.service"; import { ExportService } from "@bitwarden/common/abstractions/export.service"; @@ -9,6 +11,7 @@ import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { PolicyService } from "@bitwarden/common/abstractions/policy.service"; +import { StateService } from "@bitwarden/common/abstractions/state.service"; import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification.service"; import { ExportComponent as BaseExportComponent } from "../../tools/export.component"; @@ -28,7 +31,10 @@ export class ExportComponent extends BaseExportComponent { policyService: PolicyService, logService: LogService, userVerificationService: UserVerificationService, - formBuilder: FormBuilder + formBuilder: FormBuilder, + modalService: ModalService, + apiService: ApiService, + stateService: StateService ) { super( cryptoService, @@ -39,7 +45,10 @@ export class ExportComponent extends BaseExportComponent { policyService, logService, userVerificationService, - formBuilder + formBuilder, + modalService, + apiService, + stateService ); } diff --git a/apps/web/src/app/organizations/tools/import.component.ts b/apps/web/src/app/organizations/tools/import.component.ts index 08aea5b1aca..44032545312 100644 --- a/apps/web/src/app/organizations/tools/import.component.ts +++ b/apps/web/src/app/organizations/tools/import.component.ts @@ -1,6 +1,8 @@ import { Component } from "@angular/core"; import { ActivatedRoute, Router } from "@angular/router"; +import { ModalService } from "@bitwarden/angular/services/modal.service"; +import { FilePasswordPromptService } from "@bitwarden/common/abstractions/filePasswordPrompt.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { ImportService } from "@bitwarden/common/abstractions/import.service"; import { LogService } from "@bitwarden/common/abstractions/log.service"; @@ -25,9 +27,20 @@ export class ImportComponent extends BaseImportComponent { platformUtilsService: PlatformUtilsService, policyService: PolicyService, private organizationService: OrganizationService, - logService: LogService + logService: LogService, + modalService: ModalService, + filePasswordPromptService: FilePasswordPromptService ) { - super(i18nService, importService, router, platformUtilsService, policyService, logService); + super( + i18nService, + importService, + router, + platformUtilsService, + policyService, + logService, + modalService, + filePasswordPromptService + ); } async ngOnInit() { diff --git a/apps/web/src/app/services/services.module.ts b/apps/web/src/app/services/services.module.ts index cf4b290e62b..f616d683fdd 100644 --- a/apps/web/src/app/services/services.module.ts +++ b/apps/web/src/app/services/services.module.ts @@ -14,6 +14,9 @@ import { ApiService as ApiServiceAbstraction } from "@bitwarden/common/abstracti import { CipherService as CipherServiceAbstraction } from "@bitwarden/common/abstractions/cipher.service"; import { CollectionService as CollectionServiceAbstraction } from "@bitwarden/common/abstractions/collection.service"; import { CryptoService as CryptoServiceAbstraction } from "@bitwarden/common/abstractions/crypto.service"; +import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitwarden/common/abstractions/cryptoFunction.service"; +import { ExportService as ExportServiceAbstraction } from "@bitwarden/common/abstractions/export.service"; +import { FilePasswordPromptService as FilePasswordPromptServiceAbstraction } from "@bitwarden/common/abstractions/filePasswordPrompt.service"; import { FolderService as FolderServiceAbstraction } from "@bitwarden/common/abstractions/folder.service"; import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/abstractions/i18n.service"; import { ImportService as ImportServiceAbstraction } from "@bitwarden/common/abstractions/import.service"; @@ -25,12 +28,14 @@ import { StateService as BaseStateServiceAbstraction } from "@bitwarden/common/a import { StateMigrationService as StateMigrationServiceAbstraction } from "@bitwarden/common/abstractions/stateMigration.service"; import { StorageService as StorageServiceAbstraction } from "@bitwarden/common/abstractions/storage.service"; import { StateFactory } from "@bitwarden/common/factories/stateFactory"; +import { ExportService } from "@bitwarden/common/services/export.service"; import { ImportService } from "@bitwarden/common/services/import.service"; import { StateService as StateServiceAbstraction } from "../../abstractions/state.service"; import { Account } from "../../models/account"; import { GlobalState } from "../../models/globalState"; import { BroadcasterMessagingService } from "../../services/broadcasterMessaging.service"; +import { FilePasswordPromptService } from "../../services/filePasswordPrompt.service"; import { HtmlStorageService } from "../../services/htmlStorage.service"; import { I18nService } from "../../services/i18n.service"; import { MemoryStorageService } from "../../services/memoryStorage.service"; @@ -103,6 +108,17 @@ import { RouterService } from "./router.service"; CryptoServiceAbstraction, ], }, + { + provide: ExportServiceAbstraction, + useClass: ExportService, + deps: [ + FolderServiceAbstraction, + CipherServiceAbstraction, + ApiServiceAbstraction, + CryptoServiceAbstraction, + CryptoFunctionServiceAbstraction, + ], + }, { provide: StateMigrationServiceAbstraction, useClass: StateMigrationService, @@ -128,6 +144,10 @@ import { RouterService } from "./router.service"; provide: PasswordRepromptServiceAbstraction, useClass: PasswordRepromptService, }, + { + provide: FilePasswordPromptServiceAbstraction, + useClass: FilePasswordPromptService, + }, HomeGuard, ], }) diff --git a/apps/web/src/app/tools/export.component.html b/apps/web/src/app/tools/export.component.html index da667f3e44a..a22ec2dd797 100644 --- a/apps/web/src/app/tools/export.component.html +++ b/apps/web/src/app/tools/export.component.html @@ -1,6 +1,6 @@
-
-
- - +
+ + + +
+ + + +
+ + +
+ {{ "accountBackupOptionDescription" | i18n }} +
+ + + + + +
+ {{ "passwordProtectedOptionDescription" | i18n }} +
+
+
+
+
+ + + + +
+ +
+ +
+
+ +
+ {{ "exportPasswordDescription" | i18n }} +
+
+ +
+ + +
+
+ +
+
+
+
+
+
- diff --git a/apps/web/src/app/tools/export.component.ts b/apps/web/src/app/tools/export.component.ts index d5e6aadccb3..87ad669875b 100644 --- a/apps/web/src/app/tools/export.component.ts +++ b/apps/web/src/app/tools/export.component.ts @@ -1,7 +1,9 @@ -import { Component } from "@angular/core"; -import { FormBuilder } from "@angular/forms"; +import { Component, EventEmitter, Output, ViewChild, ViewContainerRef } from "@angular/core"; +import { FormBuilder, FormControl } from "@angular/forms"; import { ExportComponent as BaseExportComponent } from "@bitwarden/angular/components/export.component"; +import { ModalService } from "@bitwarden/angular/services/modal.service"; +import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { EventService } from "@bitwarden/common/abstractions/event.service"; import { ExportService } from "@bitwarden/common/abstractions/export.service"; @@ -9,14 +11,26 @@ import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { PolicyService } from "@bitwarden/common/abstractions/policy.service"; +import { StateService } from "@bitwarden/common/abstractions/state.service"; import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification.service"; +import { ApiKeyComponent } from "../settings/api-key.component"; + @Component({ selector: "app-export", templateUrl: "export.component.html", }) export class ExportComponent extends BaseExportComponent { organizationId: string; + formatControl: string; + encryptionType: string; + showPassword: boolean; + showConfirmPassword: boolean; + secretValue: string; + secret: FormControl; + + @ViewChild("viewUserApiKeyModalRef", { read: ViewContainerRef, static: true }) + viewUserApiKeyModalRef: ViewContainerRef; constructor( cryptoService: CryptoService, @@ -27,7 +41,10 @@ export class ExportComponent extends BaseExportComponent { policyService: PolicyService, logService: LogService, userVerificationService: UserVerificationService, - formBuilder: FormBuilder + formBuilder: FormBuilder, + modalService: ModalService, + apiService: ApiService, + stateService: StateService ) { super( cryptoService, @@ -39,10 +56,47 @@ export class ExportComponent extends BaseExportComponent { window, logService, userVerificationService, - formBuilder + formBuilder, + modalService, + apiService, + stateService ); } + async promptUserForSecret() { + const entityId = await this.stateService.getUserId(); + try { + // //TODO get help from Thomas on this/ other options I have to get a Secret : + // await this.modalService.openViewRef(ApiKeyComponent, this.viewUserApiKeyModalRef, (comp) => { + // comp.keyType = "user"; + // comp.entityId = entityId; + // comp.postKey = this.apiService.postUserApiKey.bind(this.apiService); + // comp.scope = "api"; + // comp.grantType = "client_credentials"; + // comp.apiKeyTitle = "apiKey"; + // comp.apiKeyWarning = "userApiKeyWarning"; + // comp.apiKeyDescription = "userApiKeyDesc"; + // }); + + // this.platformUtilsService.showToast("error", "This line doesn't get called because of an error ", this.i18nService.t("exportFail")); + + //If verification is successful: + this.submitWithSecretAlreadyVerified(); + } catch { + this.platformUtilsService.showToast("error", "FAIL", this.i18nService.t("exportFail")); + } + } + + togglePassword() { + this.showPassword = !this.showPassword; + document.getElementById("newPassword").focus(); + } + + toggleConfirmPassword() { + this.showConfirmPassword = !this.showConfirmPassword; + document.getElementById("newConfirmPassword").focus(); + } + protected saved() { super.saved(); this.platformUtilsService.showToast("success", null, this.i18nService.t("exportSuccess")); diff --git a/apps/web/src/app/tools/import.component.ts b/apps/web/src/app/tools/import.component.ts index 540872daf99..00977522877 100644 --- a/apps/web/src/app/tools/import.component.ts +++ b/apps/web/src/app/tools/import.component.ts @@ -3,6 +3,8 @@ import { Router } from "@angular/router"; import * as JSZip from "jszip"; import Swal, { SweetAlertIcon } from "sweetalert2"; +import { ModalService } from "@bitwarden/angular/services/modal.service"; +import { FilePasswordPromptService } from "@bitwarden/common/abstractions/filePasswordPrompt.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { ImportService } from "@bitwarden/common/abstractions/import.service"; import { LogService } from "@bitwarden/common/abstractions/log.service"; @@ -10,6 +12,7 @@ import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUti import { PolicyService } from "@bitwarden/common/abstractions/policy.service"; import { ImportOption, ImportType } from "@bitwarden/common/enums/importOptions"; import { PolicyType } from "@bitwarden/common/enums/policyType"; +import { ImportError } from "@bitwarden/common/importers/importError"; @Component({ selector: "app-import", @@ -20,7 +23,7 @@ export class ImportComponent implements OnInit { importOptions: ImportOption[]; format: ImportType = null; fileContents: string; - formPromise: Promise; + formPromise: Promise; loading = false; importBlockedByPolicy = false; @@ -33,7 +36,9 @@ export class ImportComponent implements OnInit { protected router: Router, protected platformUtilsService: PlatformUtilsService, protected policyService: PolicyService, - private logService: LogService + private logService: LogService, + private modalService: ModalService, + private filePasswordPromptService: FilePasswordPromptService ) {} async ngOnInit() { @@ -55,7 +60,6 @@ export class ImportComponent implements OnInit { } this.loading = true; - const importer = this.importService.getImporter(this.format, this.organizationId); if (importer === null) { this.platformUtilsService.showToast( @@ -108,10 +112,24 @@ export class ImportComponent implements OnInit { this.formPromise = this.importService.import(importer, fileContents, this.organizationId); const error = await this.formPromise; if (error != null) { - this.error(error); - this.loading = false; - return; + //Check if the error is that a password is required + if (error.passwordRequired) { + if (await this.promptPassword(fileContents)) { + //successful + } else { + //failed + this.error(error); //TODO different error + this.loading = false; + return; + } + } else { + this.error(error); + this.loading = false; + return; + } } + + //No errors, display success message this.platformUtilsService.showToast("success", null, this.i18nService.t("importSuccess")); this.router.navigate(this.successNavigate); } catch (e) { @@ -121,6 +139,10 @@ export class ImportComponent implements OnInit { this.loading = false; } + private async promptPassword(fcontents: string) { + return await this.filePasswordPromptService.showPasswordPrompt(fcontents, this.organizationId); + } + getFormatInstructionTitle() { if (this.format == null) { return null; diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index a930e76fbf2..60aa6e4e275 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -878,6 +878,30 @@ "fileFormat": { "message": "File Format" }, + "confirmFormat": { + "message": "Confirm Format" + }, + "filePassword": { + "message": "File Password" + }, + "confirmFilePassword": { + "message": "Confirm File Password" + }, + "passwordProtectedOptionDescription": { + "message": "Leverages your bitwarden account encryption not master password, to protect the export. This export can only be imported into the current account. Use this to create a backup that cannot be used elsewhere." + }, + "accountBackupOptionDescription": { + "message": "Create a user-generated password to protect the export. Use this to create an export that can be used in other accounts." + }, + "fileTypeHeading": { + "message": "File Type" + }, + "confirmVaultImport": { + "message": "Confirm Vault Import" + }, + "confirmVaultImportDesc": { + "message": "This file is password protected. Please enter the file password to import data." + }, "exportSuccess": { "message": "Your vault data has been exported." }, @@ -4701,6 +4725,9 @@ "confirmIdentity": { "message": "Confirm your identity to continue." }, + "exportPasswordDescription" : { + "message": "This password will be used to export and import this file" + }, "verificationCodeRequired": { "message": "Verification code is required." }, diff --git a/apps/web/src/locales/en_GB/messages.json b/apps/web/src/locales/en_GB/messages.json index 109935c44c6..6cf8a48ed04 100644 --- a/apps/web/src/locales/en_GB/messages.json +++ b/apps/web/src/locales/en_GB/messages.json @@ -875,6 +875,30 @@ "exportVault": { "message": "Export vault" }, + "confirmFormat": { + "message": "Confirm Format" + }, + "filePassword": { + "message": "File Password" + }, + "confirmFilePassword": { + "message": "Confirm File Password" + }, + "passwordProtectedOptionDescription": { + "message": "Leverages your bitwarden account encryption not master password, to protect the export. This export can only be imported into the current account. Use this to create a backup that cannot be used elsewhere." + }, + "accountBackupOptionDescription": { + "message": "Create a user-generated password to protect the export. Use this to create an export that can be used in other accounts." + }, + "fileTypeHeading": { + "message": "File Type" + }, + "confirmVaultImport": { + "message": "Confirm Vault Import" + }, + "confirmVaultImportDesc": { + "message": "This file is password protected. Please enter the file password to import data." + }, "fileFormat": { "message": "File format" }, @@ -4701,6 +4725,9 @@ "confirmIdentity": { "message": "Confirm your identity to continue." }, + "exportPasswordDescription" : { + "message": "This password will be used to export and import this file" + }, "verificationCodeRequired": { "message": "Verification code is required." }, diff --git a/apps/web/src/locales/en_IN/messages.json b/apps/web/src/locales/en_IN/messages.json index c9a76572299..555a229131c 100644 --- a/apps/web/src/locales/en_IN/messages.json +++ b/apps/web/src/locales/en_IN/messages.json @@ -878,6 +878,30 @@ "fileFormat": { "message": "File format" }, + "confirmFormat": { + "message": "Confirm Format" + }, + "filePassword": { + "message": "File Password" + }, + "confirmFilePassword": { + "message": "Confirm File Password" + }, + "passwordProtectedOptionDescription": { + "message": "Leverages your bitwarden account encryption not master password, to protect the export. This export can only be imported into the current account. Use this to create a backup that cannot be used elsewhere." + }, + "accountBackupOptionDescription": { + "message": "Create a user-generated password to protect the export. Use this to create an export that can be used in other accounts." + }, + "fileTypeHeading": { + "message": "File Type" + }, + "confirmVaultImport": { + "message": "Confirm Vault Import" + }, + "confirmVaultImportDesc": { + "message": "This file is password protected. Please enter the file password to import data." + }, "exportSuccess": { "message": "Your vault data has been exported." }, @@ -4701,6 +4725,9 @@ "confirmIdentity": { "message": "Confirm your identity to continue." }, + "exportPasswordDescription" : { + "message": "This password will be used to export and import this file" + }, "verificationCodeRequired": { "message": "Verification code is required." }, diff --git a/apps/web/src/services/filePasswordPrompt.service.ts b/apps/web/src/services/filePasswordPrompt.service.ts new file mode 100644 index 00000000000..40cd9ad6fed --- /dev/null +++ b/apps/web/src/services/filePasswordPrompt.service.ts @@ -0,0 +1,10 @@ +import { Injectable } from "@angular/core"; + +import { FilePasswordPromptService as BaseFilePasswordPromptService } from "@bitwarden/angular/services/filePasswordPrompt.service"; + +import { FilePasswordPromptComponent } from "../app/components/file-password-prompt.component"; + +@Injectable() +export class FilePasswordPromptService extends BaseFilePasswordPromptService { + component = FilePasswordPromptComponent; +} diff --git a/libs/angular/src/components/export.component.ts b/libs/angular/src/components/export.component.ts index 353a58dc726..f139aabdb1a 100644 --- a/libs/angular/src/components/export.component.ts +++ b/libs/angular/src/components/export.component.ts @@ -1,6 +1,14 @@ -import { Directive, EventEmitter, OnInit, Output } from "@angular/core"; +import { + Directive, + EventEmitter, + OnInit, + Output, + ViewChild, + ViewContainerRef, +} from "@angular/core"; import { FormBuilder } from "@angular/forms"; +import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { EventService } from "@bitwarden/common/abstractions/event.service"; import { ExportService } from "@bitwarden/common/abstractions/export.service"; @@ -8,20 +16,32 @@ import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { PolicyService } from "@bitwarden/common/abstractions/policy.service"; +import { StateService } from "@bitwarden/common/abstractions/state.service"; import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification.service"; import { EventType } from "@bitwarden/common/enums/eventType"; import { PolicyType } from "@bitwarden/common/enums/policyType"; +import { ModalService } from "../services/modal.service"; + @Directive() export class ExportComponent implements OnInit { @Output() onSaved = new EventEmitter(); formPromise: Promise; disabledByPolicy = false; + private alreadyVerified = false; + + @ViewChild("viewUserApiKeyTemplate", { read: ViewContainerRef, static: true }) + viewUserApiKeyModalRef: ViewContainerRef; + + encryptionPassword: string; exportForm = this.formBuilder.group({ format: ["json"], secret: [""], + password: [""], + confirmPassword: [""], + fileEncryptionType: [""], }); formatOptions = [ @@ -40,7 +60,10 @@ export class ExportComponent implements OnInit { protected win: Window, private logService: LogService, private userVerificationService: UserVerificationService, - private formBuilder: FormBuilder + private formBuilder: FormBuilder, + protected modalService: ModalService, + protected apiService: ApiService, + protected stateService: StateService ) {} async ngOnInit() { @@ -60,6 +83,15 @@ export class ExportComponent implements OnInit { return this.format === "encrypted_json"; } + async submitWithSecretAlreadyVerified() { + if (!this.validForm) { + return; + } + + this.alreadyVerified = true; + this.submit(); + } + async submit() { if (this.disabledByPolicy) { this.platformUtilsService.showToast( @@ -70,19 +102,24 @@ export class ExportComponent implements OnInit { return; } - const acceptedWarning = await this.warningDialog(); - if (!acceptedWarning) { - return; - } + if (!this.alreadyVerified) { + const acceptedWarning = await this.warningDialog(); + if (!acceptedWarning) { + return; + } + const secret = this.exportForm.get("secret").value; - const secret = this.exportForm.get("secret").value; - try { - await this.userVerificationService.verifyUser(secret); - } catch (e) { - this.platformUtilsService.showToast("error", this.i18nService.t("errorOccurred"), e.message); - return; + try { + await this.userVerificationService.verifyUser(secret); + } catch (e) { + this.platformUtilsService.showToast( + "error", + this.i18nService.t("errorOccurred"), + e.message + ); + return; + } } - try { this.formPromise = this.getExportData(); const data = await this.formPromise; @@ -124,7 +161,7 @@ export class ExportComponent implements OnInit { } protected getExportData() { - return this.exportService.getExport(this.format); + return this.exportService.getExport(this.format, null, this.encryptionPassword); } protected getFileName(prefix?: string) { @@ -144,10 +181,52 @@ export class ExportComponent implements OnInit { await this.eventService.collect(EventType.User_ClientExportedVault); } + get validForm() { + //TODO check if fileEncryption type is null? + if (this.fileEncryptionType == 2 && this.format == "encrypted_json") { + //password encryption type + const password = this.password; + const confirmPassword = this.confirmPassword; + + if (password.length > 0 || confirmPassword.length > 0) { + if (password != confirmPassword) { + this.platformUtilsService.showToast( + "error", + this.i18nService.t("errorOccurred"), + "File Password and Confirm File Password do not match." + ); + return false; + } + + this.encryptionPassword = password; + return true; + } + } else { + this.clearPasswordField(); + return true; + } + } + + protected clearPasswordField() { + this.encryptionPassword = ""; + } + get format() { return this.exportForm.get("format").value; } + get password() { + return this.exportForm.get("password").value; + } + + get confirmPassword() { + return this.exportForm.get("confirmPassword").value; + } + + get fileEncryptionType() { + return this.exportForm.get("fileEncryptionType").value; + } + private downloadFile(csv: string): void { const fileName = this.getFileName(); this.platformUtilsService.saveFile(this.win, csv, { type: "text/plain" }, fileName); diff --git a/libs/angular/src/components/file-password-prompt.component.ts b/libs/angular/src/components/file-password-prompt.component.ts new file mode 100644 index 00000000000..f148e0d45d9 --- /dev/null +++ b/libs/angular/src/components/file-password-prompt.component.ts @@ -0,0 +1,58 @@ +import { Directive } from "@angular/core"; + +import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; +import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; +import { ImportService } from "@bitwarden/common/abstractions/import.service"; +import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; + +import { ModalConfig } from "../services/modal.service"; + +import { ModalRef } from "./modal/modal.ref"; + +/** + * Used to verify the user's File password to import their encyrpted export file" feature only. + */ +@Directive() +export class FilePasswordPromptComponent { + showPassword = false; + filePassword = ""; + organizationId = ""; + fileContents = ""; + + constructor( + private modalRef: ModalRef, + private cryptoService: CryptoService, + private platformUtilsService: PlatformUtilsService, + private i18nService: I18nService, + private importService: ImportService, + config: ModalConfig + ) { + this.fileContents = config.data.fileContents; + this.organizationId = config.data.organizationId; + } + + togglePassword() { + this.showPassword = !this.showPassword; + } + + async submit() { + const importerPassword = this.importService.getImporter( + "bitwardenpasswordprotected", + this.organizationId, + this.filePassword + ); + + const formPromise = this.importService.import( + importerPassword, + this.fileContents, + this.organizationId + ); + const passwordError = await formPromise; + + if (passwordError != null) { + this.modalRef.close(false); + } else { + this.modalRef.close(true); + } + } +} diff --git a/libs/angular/src/services/filePasswordPrompt.service.ts b/libs/angular/src/services/filePasswordPrompt.service.ts new file mode 100644 index 00000000000..911bd5bbe4c --- /dev/null +++ b/libs/angular/src/services/filePasswordPrompt.service.ts @@ -0,0 +1,51 @@ +import { Injectable } from "@angular/core"; + +import { FilePasswordPromptService as FilePasswordPromptServiceAbstraction } from "@bitwarden/common/abstractions/filePasswordPrompt.service"; +import { KeyConnectorService } from "@bitwarden/common/abstractions/keyConnector.service"; + + +import { FilePasswordPromptComponent } from "../components/file-password-prompt.component"; + +import { ModalService } from "./modal.service"; + +/** + * Used to verify the user's File Password for the "Import passwords using File Password" feature only. + */ +@Injectable() +export class FilePasswordPromptService implements FilePasswordPromptServiceAbstraction { + protected component = FilePasswordPromptComponent; + + constructor( + private modalService: ModalService, + private keyConnectorService: KeyConnectorService + ) {} + + protectedFields() { + return ["TOTP", "Password", "H_Field", "Card Number", "Security Code"]; + } + + async showPasswordPrompt(fcontents: string, organizationId: string) { + if (!(await this.enabled())) { + return true; + } + + const ref = this.modalService.open(this.component, { + allowMultipleModals: true, + data: { + fileContents: fcontents, + organizationId: organizationId, + }, + }); + + if (ref == null) { + return false; + } + + const result = await ref.onClosedPromise(); + return result === true; + } + + async enabled() { + return !(await this.keyConnectorService.getUsesKeyConnector()); + } +} diff --git a/libs/common/src/abstractions/export.service.ts b/libs/common/src/abstractions/export.service.ts index b0266530bd6..9dc5fa8044d 100644 --- a/libs/common/src/abstractions/export.service.ts +++ b/libs/common/src/abstractions/export.service.ts @@ -3,7 +3,7 @@ import { EventView } from "../models/view/eventView"; export type ExportFormat = "csv" | "json" | "encrypted_json"; export abstract class ExportService { - getExport: (format?: ExportFormat, organizationId?: string) => Promise; + getExport: (format?: ExportFormat, organizationId?: string, password?: string) => Promise; getPasswordProtectedExport: (password: string, organizationId?: string) => Promise; getOrganizationExport: (organizationId: string, format?: ExportFormat) => Promise; getEventExport: (events: EventView[]) => Promise; diff --git a/libs/common/src/abstractions/filePasswordPrompt.service.ts b/libs/common/src/abstractions/filePasswordPrompt.service.ts new file mode 100644 index 00000000000..f7b0900b647 --- /dev/null +++ b/libs/common/src/abstractions/filePasswordPrompt.service.ts @@ -0,0 +1,5 @@ +export abstract class FilePasswordPromptService { + protectedFields: () => string[]; + showPasswordPrompt: (fcontents: string, organizationId: string) => Promise; + enabled: () => Promise; +} diff --git a/libs/common/src/services/export.service.ts b/libs/common/src/services/export.service.ts index c6f08be6d26..299d9697b6a 100644 --- a/libs/common/src/services/export.service.ts +++ b/libs/common/src/services/export.service.ts @@ -36,13 +36,19 @@ export class ExportService implements ExportServiceAbstraction { private cryptoFunctionService: CryptoFunctionService ) {} - async getExport(format: ExportFormat = "csv", organizationId?: string): Promise { + async getExport( + format: ExportFormat = "csv", + organizationId?: string, + password?: string + ): Promise { if (organizationId) { return await this.getOrganizationExport(organizationId, format); } if (format === "encrypted_json") { - return this.getEncryptedExport(); + return password == undefined || password == "" + ? this.getEncryptedExport() + : this.getPasswordProtectedExport(password); } else { return this.getDecryptedExport(format); } From ff137e0072528bf7d48f050a16ed0744f8ce92c9 Mon Sep 17 00:00:00 2001 From: CarleyDiaz-Bitwarden <103955722+CarleyDiaz-Bitwarden@users.noreply.github.com> Date: Thu, 16 Jun 2022 16:54:51 -0400 Subject: [PATCH 02/14] fix for encrypted export changes --- apps/desktop/src/app/vault/export.component.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/apps/desktop/src/app/vault/export.component.ts b/apps/desktop/src/app/vault/export.component.ts index b1e48abcddd..83bda3a7249 100644 --- a/apps/desktop/src/app/vault/export.component.ts +++ b/apps/desktop/src/app/vault/export.component.ts @@ -4,6 +4,8 @@ import { Component, OnInit } from "@angular/core"; import { FormBuilder } from "@angular/forms"; import { ExportComponent as BaseExportComponent } from "@bitwarden/angular/components/export.component"; +import { ModalService } from "@bitwarden/angular/services/modal.service"; +import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { BroadcasterService } from "@bitwarden/common/abstractions/broadcaster.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { EventService } from "@bitwarden/common/abstractions/event.service"; @@ -12,6 +14,7 @@ import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { PolicyService } from "@bitwarden/common/abstractions/policy.service"; +import { StateService } from "@bitwarden/common/abstractions/state.service"; import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification.service"; const BroadcasterSubscriptionId = "ExportComponent"; @@ -31,7 +34,10 @@ export class ExportComponent extends BaseExportComponent implements OnInit { userVerificationService: UserVerificationService, formBuilder: FormBuilder, private broadcasterService: BroadcasterService, - logService: LogService + logService: LogService, + protected modalService: ModalService, + protected apiService: ApiService, + protected stateService: StateService ) { super( cryptoService, @@ -43,7 +49,10 @@ export class ExportComponent extends BaseExportComponent implements OnInit { window, logService, userVerificationService, - formBuilder + formBuilder, + modalService, + apiService, + stateService ); } From 8f06a8cf357842f733852771401e88e7db011f40 Mon Sep 17 00:00:00 2001 From: CarleyDiaz-Bitwarden <103955722+CarleyDiaz-Bitwarden@users.noreply.github.com> Date: Thu, 16 Jun 2022 17:05:25 -0400 Subject: [PATCH 03/14] Create launch.json --- apps/.vscode/launch.json | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 apps/.vscode/launch.json diff --git a/apps/.vscode/launch.json b/apps/.vscode/launch.json new file mode 100644 index 00000000000..2be50c4fa2d --- /dev/null +++ b/apps/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "pwa-chrome", + "request": "launch", + "name": "Launch Chrome against localhost", + "url": "https://localhost:8080/#", + "webRoot": "${workspaceFolder}" + } + ] +} From f5c78590dbe63e56a739087282c2cbf0970b9146 Mon Sep 17 00:00:00 2001 From: CarleyDiaz-Bitwarden <103955722+CarleyDiaz-Bitwarden@users.noreply.github.com> Date: Mon, 20 Jun 2022 12:50:56 -0400 Subject: [PATCH 04/14] Updates to export logic modal user secret prompt --- ...export-file-password-prompt.component.html | 36 ++++++++++ .../export-file-password-prompt.component.ts | 8 +++ .../app/modules/loose-components.module.ts | 2 + .../organizations/tools/export.component.ts | 19 +++--- apps/web/src/app/services/services.module.ts | 9 ++- apps/web/src/app/tools/export.component.html | 2 +- apps/web/src/app/tools/export.component.ts | 67 +++++++++++++------ apps/web/src/locales/en/messages.json | 14 ++-- apps/web/src/locales/en_IN/messages.json | 27 -------- apps/web/src/scss/modals.scss | 4 ++ .../exportFilePasswordPrompt.service.ts | 10 +++ .../export-file-password-prompt.component.ts | 60 +++++++++++++++++ .../src/components/export.component.ts | 8 ++- .../exportFilePasswordPrompt.service.ts | 54 +++++++++++++++ .../exportFilePasswordPrompt.service.ts | 9 +++ 15 files changed, 264 insertions(+), 65 deletions(-) create mode 100644 apps/web/src/app/components/export-file-password-prompt.component.html create mode 100644 apps/web/src/app/components/export-file-password-prompt.component.ts create mode 100644 apps/web/src/services/exportFilePasswordPrompt.service.ts create mode 100644 libs/angular/src/components/export-file-password-prompt.component.ts create mode 100644 libs/angular/src/services/exportFilePasswordPrompt.service.ts create mode 100644 libs/common/src/abstractions/exportFilePasswordPrompt.service.ts diff --git a/apps/web/src/app/components/export-file-password-prompt.component.html b/apps/web/src/app/components/export-file-password-prompt.component.html new file mode 100644 index 00000000000..0f31451e01e --- /dev/null +++ b/apps/web/src/app/components/export-file-password-prompt.component.html @@ -0,0 +1,36 @@ + diff --git a/apps/web/src/app/components/export-file-password-prompt.component.ts b/apps/web/src/app/components/export-file-password-prompt.component.ts new file mode 100644 index 00000000000..504c606c4bb --- /dev/null +++ b/apps/web/src/app/components/export-file-password-prompt.component.ts @@ -0,0 +1,8 @@ +import { Component } from "@angular/core"; + +import { ExportFilePasswordPromptComponent as BaseExportFilePasswordPrompt } from "@bitwarden/angular/components/export-file-password-prompt.component"; + +@Component({ + templateUrl: "export-file-password-prompt.component.html", +}) +export class ExportFilePasswordPromptComponent extends BaseExportFilePasswordPrompt {} diff --git a/apps/web/src/app/modules/loose-components.module.ts b/apps/web/src/app/modules/loose-components.module.ts index 382c7fdd193..66de29888aa 100644 --- a/apps/web/src/app/modules/loose-components.module.ts +++ b/apps/web/src/app/modules/loose-components.module.ts @@ -19,6 +19,7 @@ import { UpdatePasswordComponent } from "../accounts/update-password.component"; import { UpdateTempPasswordComponent } from "../accounts/update-temp-password.component"; import { VerifyEmailTokenComponent } from "../accounts/verify-email-token.component"; import { VerifyRecoverDeleteComponent } from "../accounts/verify-recover-delete.component"; +import { ExportFilePasswordPromptComponent } from "../components/export-file-password-prompt.component"; import { FilePasswordPromptComponent } from "../components/file-password-prompt.component"; import { NestedCheckboxComponent } from "../components/nested-checkbox.component"; import { OrganizationSwitcherComponent } from "../components/organization-switcher.component"; @@ -271,6 +272,7 @@ import { OrganizationBadgeModule } from "./vault/modules/organization-badge/orga PasswordGeneratorPolicyComponent, PasswordRepromptComponent, FilePasswordPromptComponent, + ExportFilePasswordPromptComponent, PasswordStrengthComponent, PaymentComponent, PaymentMethodComponent, diff --git a/apps/web/src/app/organizations/tools/export.component.ts b/apps/web/src/app/organizations/tools/export.component.ts index e4b89654e9f..d56fa061119 100644 --- a/apps/web/src/app/organizations/tools/export.component.ts +++ b/apps/web/src/app/organizations/tools/export.component.ts @@ -2,7 +2,8 @@ import { Component } from "@angular/core"; import { FormBuilder } from "@angular/forms"; import { ActivatedRoute } from "@angular/router"; -import { ModalService } from "@bitwarden/angular/services/modal.service"; +import { ExportFilePasswordPromptService } from "@bitwarden/angular/services/exportFilePasswordPrompt.service"; +import { ModalConfig, ModalService } from "@bitwarden/angular/services/modal.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { EventService } from "@bitwarden/common/abstractions/event.service"; @@ -34,7 +35,9 @@ export class ExportComponent extends BaseExportComponent { formBuilder: FormBuilder, modalService: ModalService, apiService: ApiService, - stateService: StateService + stateService: StateService, + exportFilePasswordPromptService: ExportFilePasswordPromptService, + modalConfig: ModalConfig ) { super( cryptoService, @@ -48,8 +51,13 @@ export class ExportComponent extends BaseExportComponent { formBuilder, modalService, apiService, - stateService + stateService, + exportFilePasswordPromptService, + modalConfig ); + this.confirmDescription = modalConfig.data.confirmDescription; + this.confirmButtonText = modalConfig.data.confirmButtonText; + this.modalTitle = modalConfig.data.modalTitle; } async ngOnInit() { @@ -70,9 +78,4 @@ export class ExportComponent extends BaseExportComponent { getFileName() { return super.getFileName("org"); } - - async collectEvent(): Promise { - // TODO - // await this.eventService.collect(EventType.Organization_ClientExportedVault); - } } diff --git a/apps/web/src/app/services/services.module.ts b/apps/web/src/app/services/services.module.ts index f616d683fdd..f055bb66b87 100644 --- a/apps/web/src/app/services/services.module.ts +++ b/apps/web/src/app/services/services.module.ts @@ -9,13 +9,14 @@ import { LOCALES_DIRECTORY, SYSTEM_LANGUAGE, } from "@bitwarden/angular/services/jslib-services.module"; -import { ModalService as ModalServiceAbstraction } from "@bitwarden/angular/services/modal.service"; +import { ModalService as ModalServiceAbstraction , ModalConfig as ModalConfigAbstraction , ModalConfig } from "@bitwarden/angular/services/modal.service"; import { ApiService as ApiServiceAbstraction } from "@bitwarden/common/abstractions/api.service"; import { CipherService as CipherServiceAbstraction } from "@bitwarden/common/abstractions/cipher.service"; import { CollectionService as CollectionServiceAbstraction } from "@bitwarden/common/abstractions/collection.service"; import { CryptoService as CryptoServiceAbstraction } from "@bitwarden/common/abstractions/crypto.service"; import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitwarden/common/abstractions/cryptoFunction.service"; import { ExportService as ExportServiceAbstraction } from "@bitwarden/common/abstractions/export.service"; +import { ExportFilePasswordPromptService as ExportFilePasswordPromptServiceAbstraction } from "@bitwarden/common/abstractions/exportFilePasswordPrompt.service"; import { FilePasswordPromptService as FilePasswordPromptServiceAbstraction } from "@bitwarden/common/abstractions/filePasswordPrompt.service"; import { FolderService as FolderServiceAbstraction } from "@bitwarden/common/abstractions/folder.service"; import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/abstractions/i18n.service"; @@ -35,6 +36,7 @@ import { StateService as StateServiceAbstraction } from "../../abstractions/stat import { Account } from "../../models/account"; import { GlobalState } from "../../models/globalState"; import { BroadcasterMessagingService } from "../../services/broadcasterMessaging.service"; +import { ExportFilePasswordPromptService } from "../../services/exportFilePasswordPrompt.service"; import { FilePasswordPromptService } from "../../services/filePasswordPrompt.service"; import { HtmlStorageService } from "../../services/htmlStorage.service"; import { I18nService } from "../../services/i18n.service"; @@ -95,6 +97,7 @@ import { RouterService } from "./router.service"; }, { provide: MessagingServiceAbstraction, useClass: BroadcasterMessagingService }, { provide: ModalServiceAbstraction, useClass: ModalService }, + { provide: ModalConfigAbstraction, useClass: ModalConfig }, { provide: ImportServiceAbstraction, useClass: ImportService, @@ -148,6 +151,10 @@ import { RouterService } from "./router.service"; provide: FilePasswordPromptServiceAbstraction, useClass: FilePasswordPromptService, }, + { + provide: ExportFilePasswordPromptServiceAbstraction, + useClass: ExportFilePasswordPromptService, + }, HomeGuard, ], }) diff --git a/apps/web/src/app/tools/export.component.html b/apps/web/src/app/tools/export.component.html index a22ec2dd797..5c94f5dc37b 100644 --- a/apps/web/src/app/tools/export.component.html +++ b/apps/web/src/app/tools/export.component.html @@ -1,6 +1,6 @@
{ - // comp.keyType = "user"; - // comp.entityId = entityId; - // comp.postKey = this.apiService.postUserApiKey.bind(this.apiService); - // comp.scope = "api"; - // comp.grantType = "client_credentials"; - // comp.apiKeyTitle = "apiKey"; - // comp.apiKeyWarning = "userApiKeyWarning"; - // comp.apiKeyDescription = "userApiKeyDesc"; - // }); + if ( + await this.exportFilePasswordPromptService.showPasswordPrompt( + confirmDescription, + confirmButtonText, + modalTitle + ) + ) { + //successful + } else { + //failed + this.platformUtilsService.showToast( + "error", + this.i18nService.t("error"), + this.i18nService.t("invalidMasterPassword") + ); + return; + } - // this.platformUtilsService.showToast("error", "This line doesn't get called because of an error ", this.i18nService.t("exportFail")); - - //If verification is successful: this.submitWithSecretAlreadyVerified(); } catch { - this.platformUtilsService.showToast("error", "FAIL", this.i18nService.t("exportFail")); + this.platformUtilsService.showToast( + "error", + this.i18nService.t("error"), + this.i18nService.t("invalidMasterPassword") + ); } } diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 60aa6e4e275..ff3777ed6f1 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -878,6 +878,15 @@ "fileFormat": { "message": "File Format" }, + "confirmVaultExportDesc" : { + "message": "This file export will be password protected and require the file password to decrypt." + }, + "exportPasswordDescription" : { + "message": "This password will be used to export and import this file" + }, + "confirmMasterPassword" : { + "message": "Confirm Master Password" + }, "confirmFormat": { "message": "Confirm Format" }, @@ -900,7 +909,7 @@ "message": "Confirm Vault Import" }, "confirmVaultImportDesc": { - "message": "This file is password protected. Please enter the file password to import data." + "message": "This file is password-protected. Please enter the file password to import data." }, "exportSuccess": { "message": "Your vault data has been exported." @@ -4725,9 +4734,6 @@ "confirmIdentity": { "message": "Confirm your identity to continue." }, - "exportPasswordDescription" : { - "message": "This password will be used to export and import this file" - }, "verificationCodeRequired": { "message": "Verification code is required." }, diff --git a/apps/web/src/locales/en_IN/messages.json b/apps/web/src/locales/en_IN/messages.json index 555a229131c..c9a76572299 100644 --- a/apps/web/src/locales/en_IN/messages.json +++ b/apps/web/src/locales/en_IN/messages.json @@ -878,30 +878,6 @@ "fileFormat": { "message": "File format" }, - "confirmFormat": { - "message": "Confirm Format" - }, - "filePassword": { - "message": "File Password" - }, - "confirmFilePassword": { - "message": "Confirm File Password" - }, - "passwordProtectedOptionDescription": { - "message": "Leverages your bitwarden account encryption not master password, to protect the export. This export can only be imported into the current account. Use this to create a backup that cannot be used elsewhere." - }, - "accountBackupOptionDescription": { - "message": "Create a user-generated password to protect the export. Use this to create an export that can be used in other accounts." - }, - "fileTypeHeading": { - "message": "File Type" - }, - "confirmVaultImport": { - "message": "Confirm Vault Import" - }, - "confirmVaultImportDesc": { - "message": "This file is password protected. Please enter the file password to import data." - }, "exportSuccess": { "message": "Your vault data has been exported." }, @@ -4725,9 +4701,6 @@ "confirmIdentity": { "message": "Confirm your identity to continue." }, - "exportPasswordDescription" : { - "message": "This password will be used to export and import this file" - }, "verificationCodeRequired": { "message": "Verification code is required." }, diff --git a/apps/web/src/scss/modals.scss b/apps/web/src/scss/modals.scss index 337bc3169c1..6b141d8677f 100644 --- a/apps/web/src/scss/modals.scss +++ b/apps/web/src/scss/modals.scss @@ -75,6 +75,10 @@ } } +.modal-top-padding-sm { + padding-top: 1.5em; +} + .close { @include themify($themes) { color: themed("textColor"); diff --git a/apps/web/src/services/exportFilePasswordPrompt.service.ts b/apps/web/src/services/exportFilePasswordPrompt.service.ts new file mode 100644 index 00000000000..c18f20f0e9e --- /dev/null +++ b/apps/web/src/services/exportFilePasswordPrompt.service.ts @@ -0,0 +1,10 @@ +import { Injectable } from "@angular/core"; + +import { ExportFilePasswordPromptService as BaseExportFilePasswordPrompt } from "@bitwarden/angular/services/exportFilePasswordPrompt.service"; + +import { ExportFilePasswordPromptComponent } from "../app/components/export-file-password-prompt.component"; + +@Injectable() +export class ExportFilePasswordPromptService extends BaseExportFilePasswordPrompt { + component = ExportFilePasswordPromptComponent; +} diff --git a/libs/angular/src/components/export-file-password-prompt.component.ts b/libs/angular/src/components/export-file-password-prompt.component.ts new file mode 100644 index 00000000000..4f7ae35caef --- /dev/null +++ b/libs/angular/src/components/export-file-password-prompt.component.ts @@ -0,0 +1,60 @@ +import { Directive } from "@angular/core"; +import { FormBuilder, FormControl, FormGroup } from "@angular/forms"; + +import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; +import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; +import { ImportService } from "@bitwarden/common/abstractions/import.service"; +import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; +import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification.service"; + +import { ModalConfig } from "../services/modal.service"; + +import { ModalRef } from "./modal/modal.ref"; + +/** + * Used to verify the user's secret, you can customize all of the text in the modal. + */ +@Directive() +export class ExportFilePasswordPromptComponent { + showPassword = false; + organizationId = ""; + confirmDescription = ""; + confirmButtonText = ""; + modalTitle = ""; + + myGroup = this.formBuilder.group({ + secret: new FormControl(), + }); + + constructor( + private modalRef: ModalRef, + private cryptoService: CryptoService, + private platformUtilsService: PlatformUtilsService, + private i18nService: I18nService, + private importService: ImportService, + protected config: ModalConfig, + protected userVerificationService: UserVerificationService, + private formBuilder: FormBuilder + ) { + this.confirmDescription = config.data.confirmDescription; + this.confirmButtonText = config.data.confirmButtonText; + this.modalTitle = config.data.modalTitle; + } + + togglePassword() { + this.showPassword = !this.showPassword; + } + + async submit() { + const secret = this.myGroup.get("secret").value; + + try { + await this.userVerificationService.verifyUser(secret); + } catch (e) { + this.modalRef.close(false); + return; + } + + this.modalRef.close(true); + } +} diff --git a/libs/angular/src/components/export.component.ts b/libs/angular/src/components/export.component.ts index f139aabdb1a..ce5916e13cd 100644 --- a/libs/angular/src/components/export.component.ts +++ b/libs/angular/src/components/export.component.ts @@ -8,10 +8,12 @@ import { } from "@angular/core"; import { FormBuilder } from "@angular/forms"; +import { ModalConfig, ModalService } from "@bitwarden/angular/services/modal.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { EventService } from "@bitwarden/common/abstractions/event.service"; import { ExportService } from "@bitwarden/common/abstractions/export.service"; +import { ExportFilePasswordPromptService } from "@bitwarden/common/abstractions/exportFilePasswordPrompt.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; @@ -21,8 +23,6 @@ import { UserVerificationService } from "@bitwarden/common/abstractions/userVeri import { EventType } from "@bitwarden/common/enums/eventType"; import { PolicyType } from "@bitwarden/common/enums/policyType"; -import { ModalService } from "../services/modal.service"; - @Directive() export class ExportComponent implements OnInit { @Output() onSaved = new EventEmitter(); @@ -63,7 +63,9 @@ export class ExportComponent implements OnInit { private formBuilder: FormBuilder, protected modalService: ModalService, protected apiService: ApiService, - protected stateService: StateService + protected stateService: StateService, + protected exportFilePasswordPromptService: ExportFilePasswordPromptService, + protected modalConfig: ModalConfig ) {} async ngOnInit() { diff --git a/libs/angular/src/services/exportFilePasswordPrompt.service.ts b/libs/angular/src/services/exportFilePasswordPrompt.service.ts new file mode 100644 index 00000000000..6645c12b47e --- /dev/null +++ b/libs/angular/src/services/exportFilePasswordPrompt.service.ts @@ -0,0 +1,54 @@ +import { Injectable } from "@angular/core"; + +import { ExportFilePasswordPromptService as ExportFilePasswordPromptServiceAbstraction } from "@bitwarden/common/abstractions/exportFilePasswordPrompt.service"; +import { KeyConnectorService } from "@bitwarden/common/abstractions/keyConnector.service"; + +import { ExportFilePasswordPromptComponent } from "../components/export-file-password-prompt.component"; + +import { ModalService } from "./modal.service"; + +/** + * Used to verify the user's File Password for the "Import passwords using File Password" feature only. + */ +@Injectable() +export class ExportFilePasswordPromptService implements ExportFilePasswordPromptServiceAbstraction { + protected component = ExportFilePasswordPromptComponent; + + constructor( + private modalService: ModalService, + private keyConnectorService: KeyConnectorService + ) {} + + protectedFields() { + return ["TOTP", "Password", "H_Field", "Card Number", "Security Code"]; + } + + async showPasswordPrompt( + confirmDescription: string, + confirmButtonText: string, + modalTitle: string + ) { + if (!(await this.enabled())) { + return true; + } + + const ref = await this.modalService.open(this.component, { + allowMultipleModals: true, + data: { + confirmDescription: confirmDescription, + confirmButtonText: confirmButtonText, + modalTitle: modalTitle, + }, + }); + + if (ref == null) { + return false; + } + + return (await ref.onClosedPromise()) === true; + } + + async enabled() { + return !(await this.keyConnectorService.getUsesKeyConnector()); + } +} diff --git a/libs/common/src/abstractions/exportFilePasswordPrompt.service.ts b/libs/common/src/abstractions/exportFilePasswordPrompt.service.ts new file mode 100644 index 00000000000..ca17d4191e8 --- /dev/null +++ b/libs/common/src/abstractions/exportFilePasswordPrompt.service.ts @@ -0,0 +1,9 @@ +export abstract class ExportFilePasswordPromptService { + protectedFields: () => string[]; + showPasswordPrompt: ( + confirmDescription: string, + confirmButtonText: string, + modalTitle: string + ) => Promise; + enabled: () => Promise; +} From 15949ed8bc264b1bdfd5fbc4da53a323fec5baeb Mon Sep 17 00:00:00 2001 From: CarleyDiaz-Bitwarden <103955722+CarleyDiaz-Bitwarden@users.noreply.github.com> Date: Mon, 20 Jun 2022 14:56:57 -0400 Subject: [PATCH 05/14] Updates to error handling --- .../export-file-password-prompt.component.html | 2 +- .../components/file-password-prompt.component.html | 2 +- apps/web/src/app/tools/import.component.ts | 12 ++++++++---- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/apps/web/src/app/components/export-file-password-prompt.component.html b/apps/web/src/app/components/export-file-password-prompt.component.html index 0f31451e01e..c5de28156c7 100644 --- a/apps/web/src/app/components/export-file-password-prompt.component.html +++ b/apps/web/src/app/components/export-file-password-prompt.component.html @@ -5,7 +5,7 @@
-

{{ modalTitle | i18n }}

diff --git a/apps/web/src/app/components/file-password-prompt.component.html b/apps/web/src/app/components/file-password-prompt.component.html index 4c98ef66833..3a2ff6f27d3 100644 --- a/apps/web/src/app/components/file-password-prompt.component.html +++ b/apps/web/src/app/components/file-password-prompt.component.html @@ -5,7 +5,7 @@ -

{{ "confirmVaultImport" | i18n }}

diff --git a/apps/web/src/app/tools/import.component.ts b/apps/web/src/app/tools/import.component.ts index 00977522877..36917a20277 100644 --- a/apps/web/src/app/tools/import.component.ts +++ b/apps/web/src/app/tools/import.component.ts @@ -114,12 +114,16 @@ export class ImportComponent implements OnInit { if (error != null) { //Check if the error is that a password is required if (error.passwordRequired) { - if (await this.promptPassword(fileContents)) { + if (await this.promptFilePassword(fileContents)) { //successful } else { - //failed - this.error(error); //TODO different error + //failed - File Password issues this.loading = false; + this.platformUtilsService.showToast( + "error", + this.i18nService.t("error"), + this.i18nService.t("invalidMasterPassword") + ); return; } } else { @@ -139,7 +143,7 @@ export class ImportComponent implements OnInit { this.loading = false; } - private async promptPassword(fcontents: string) { + private async promptFilePassword(fcontents: string) { return await this.filePasswordPromptService.showPasswordPrompt(fcontents, this.organizationId); } From a1b1b632e08a381bd87397411c48c27f3d2a015e Mon Sep 17 00:00:00 2001 From: CarleyDiaz-Bitwarden <103955722+CarleyDiaz-Bitwarden@users.noreply.github.com> Date: Mon, 20 Jun 2022 17:24:48 -0400 Subject: [PATCH 06/14] renaming the component for checking the user secret to a name that is more clear about what it accomplishes --- .../export-file-password-prompt.component.ts | 8 -------- ...nent.html => user-secret-prompt.component.html} | 0 .../app/components/user-secret-prompt.component.ts | 8 ++++++++ .../web/src/app/modules/loose-components.module.ts | 4 ++-- .../app/organizations/tools/export.component.ts | 6 +++--- apps/web/src/app/services/services.module.ts | 14 +++++++++----- apps/web/src/app/tools/export.component.ts | 8 ++++---- .../services/exportFilePasswordPrompt.service.ts | 10 ---------- apps/web/src/services/userSecretPrompt.service.ts | 10 ++++++++++ libs/angular/src/components/export.component.ts | 4 ++-- ...omponent.ts => user-secret-prompt.component.ts} | 2 +- ...ompt.service.ts => userSecretPrompt.service.ts} | 8 ++++---- ...ompt.service.ts => userSecretPrompt.service.ts} | 2 +- 13 files changed, 44 insertions(+), 40 deletions(-) delete mode 100644 apps/web/src/app/components/export-file-password-prompt.component.ts rename apps/web/src/app/components/{export-file-password-prompt.component.html => user-secret-prompt.component.html} (100%) create mode 100644 apps/web/src/app/components/user-secret-prompt.component.ts delete mode 100644 apps/web/src/services/exportFilePasswordPrompt.service.ts create mode 100644 apps/web/src/services/userSecretPrompt.service.ts rename libs/angular/src/components/{export-file-password-prompt.component.ts => user-secret-prompt.component.ts} (97%) rename libs/angular/src/services/{exportFilePasswordPrompt.service.ts => userSecretPrompt.service.ts} (73%) rename libs/common/src/abstractions/{exportFilePasswordPrompt.service.ts => userSecretPrompt.service.ts} (78%) diff --git a/apps/web/src/app/components/export-file-password-prompt.component.ts b/apps/web/src/app/components/export-file-password-prompt.component.ts deleted file mode 100644 index 504c606c4bb..00000000000 --- a/apps/web/src/app/components/export-file-password-prompt.component.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Component } from "@angular/core"; - -import { ExportFilePasswordPromptComponent as BaseExportFilePasswordPrompt } from "@bitwarden/angular/components/export-file-password-prompt.component"; - -@Component({ - templateUrl: "export-file-password-prompt.component.html", -}) -export class ExportFilePasswordPromptComponent extends BaseExportFilePasswordPrompt {} diff --git a/apps/web/src/app/components/export-file-password-prompt.component.html b/apps/web/src/app/components/user-secret-prompt.component.html similarity index 100% rename from apps/web/src/app/components/export-file-password-prompt.component.html rename to apps/web/src/app/components/user-secret-prompt.component.html diff --git a/apps/web/src/app/components/user-secret-prompt.component.ts b/apps/web/src/app/components/user-secret-prompt.component.ts new file mode 100644 index 00000000000..2dfc34c59e5 --- /dev/null +++ b/apps/web/src/app/components/user-secret-prompt.component.ts @@ -0,0 +1,8 @@ +import { Component } from "@angular/core"; + +import { UserSecretPromptComponent as BaseUserSecretPrompt } from "@bitwarden/angular/components/user-secret-prompt.component"; + +@Component({ + templateUrl: "user-secret-prompt.component.html", +}) +export class UserSecretPromptComponent extends BaseUserSecretPrompt {} diff --git a/apps/web/src/app/modules/loose-components.module.ts b/apps/web/src/app/modules/loose-components.module.ts index 66de29888aa..8bcd7125a3c 100644 --- a/apps/web/src/app/modules/loose-components.module.ts +++ b/apps/web/src/app/modules/loose-components.module.ts @@ -19,13 +19,13 @@ import { UpdatePasswordComponent } from "../accounts/update-password.component"; import { UpdateTempPasswordComponent } from "../accounts/update-temp-password.component"; import { VerifyEmailTokenComponent } from "../accounts/verify-email-token.component"; import { VerifyRecoverDeleteComponent } from "../accounts/verify-recover-delete.component"; -import { ExportFilePasswordPromptComponent } from "../components/export-file-password-prompt.component"; import { FilePasswordPromptComponent } from "../components/file-password-prompt.component"; import { NestedCheckboxComponent } from "../components/nested-checkbox.component"; import { OrganizationSwitcherComponent } from "../components/organization-switcher.component"; import { PasswordRepromptComponent } from "../components/password-reprompt.component"; import { PasswordStrengthComponent } from "../components/password-strength.component"; import { PremiumBadgeComponent } from "../components/premium-badge.component"; +import { UserSecretPromptComponent } from "../components/user-secret-prompt.component"; import { FooterComponent } from "../layouts/footer.component"; import { FrontendLayoutComponent } from "../layouts/frontend-layout.component"; import { NavbarComponent } from "../layouts/navbar.component"; @@ -272,7 +272,7 @@ import { OrganizationBadgeModule } from "./vault/modules/organization-badge/orga PasswordGeneratorPolicyComponent, PasswordRepromptComponent, FilePasswordPromptComponent, - ExportFilePasswordPromptComponent, + UserSecretPromptComponent, PasswordStrengthComponent, PaymentComponent, PaymentMethodComponent, diff --git a/apps/web/src/app/organizations/tools/export.component.ts b/apps/web/src/app/organizations/tools/export.component.ts index d56fa061119..a666df8db14 100644 --- a/apps/web/src/app/organizations/tools/export.component.ts +++ b/apps/web/src/app/organizations/tools/export.component.ts @@ -2,8 +2,8 @@ import { Component } from "@angular/core"; import { FormBuilder } from "@angular/forms"; import { ActivatedRoute } from "@angular/router"; -import { ExportFilePasswordPromptService } from "@bitwarden/angular/services/exportFilePasswordPrompt.service"; import { ModalConfig, ModalService } from "@bitwarden/angular/services/modal.service"; +import { UserSecretPromptService } from "@bitwarden/angular/services/userSecretPrompt.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { EventService } from "@bitwarden/common/abstractions/event.service"; @@ -36,7 +36,7 @@ export class ExportComponent extends BaseExportComponent { modalService: ModalService, apiService: ApiService, stateService: StateService, - exportFilePasswordPromptService: ExportFilePasswordPromptService, + userSecretPromptService: UserSecretPromptService, modalConfig: ModalConfig ) { super( @@ -52,7 +52,7 @@ export class ExportComponent extends BaseExportComponent { modalService, apiService, stateService, - exportFilePasswordPromptService, + userSecretPromptService, modalConfig ); this.confirmDescription = modalConfig.data.confirmDescription; diff --git a/apps/web/src/app/services/services.module.ts b/apps/web/src/app/services/services.module.ts index f055bb66b87..51ec7dcae19 100644 --- a/apps/web/src/app/services/services.module.ts +++ b/apps/web/src/app/services/services.module.ts @@ -9,14 +9,17 @@ import { LOCALES_DIRECTORY, SYSTEM_LANGUAGE, } from "@bitwarden/angular/services/jslib-services.module"; -import { ModalService as ModalServiceAbstraction , ModalConfig as ModalConfigAbstraction , ModalConfig } from "@bitwarden/angular/services/modal.service"; +import { + ModalService as ModalServiceAbstraction, + ModalConfig as ModalConfigAbstraction, + ModalConfig, +} from "@bitwarden/angular/services/modal.service"; import { ApiService as ApiServiceAbstraction } from "@bitwarden/common/abstractions/api.service"; import { CipherService as CipherServiceAbstraction } from "@bitwarden/common/abstractions/cipher.service"; import { CollectionService as CollectionServiceAbstraction } from "@bitwarden/common/abstractions/collection.service"; import { CryptoService as CryptoServiceAbstraction } from "@bitwarden/common/abstractions/crypto.service"; import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitwarden/common/abstractions/cryptoFunction.service"; import { ExportService as ExportServiceAbstraction } from "@bitwarden/common/abstractions/export.service"; -import { ExportFilePasswordPromptService as ExportFilePasswordPromptServiceAbstraction } from "@bitwarden/common/abstractions/exportFilePasswordPrompt.service"; import { FilePasswordPromptService as FilePasswordPromptServiceAbstraction } from "@bitwarden/common/abstractions/filePasswordPrompt.service"; import { FolderService as FolderServiceAbstraction } from "@bitwarden/common/abstractions/folder.service"; import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/abstractions/i18n.service"; @@ -28,6 +31,7 @@ import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwar import { StateService as BaseStateServiceAbstraction } from "@bitwarden/common/abstractions/state.service"; import { StateMigrationService as StateMigrationServiceAbstraction } from "@bitwarden/common/abstractions/stateMigration.service"; import { StorageService as StorageServiceAbstraction } from "@bitwarden/common/abstractions/storage.service"; +import { UserSecretPromptService as UserSecretPromptServiceAbstraction } from "@bitwarden/common/abstractions/userSecretPrompt.service"; import { StateFactory } from "@bitwarden/common/factories/stateFactory"; import { ExportService } from "@bitwarden/common/services/export.service"; import { ImportService } from "@bitwarden/common/services/import.service"; @@ -36,7 +40,6 @@ import { StateService as StateServiceAbstraction } from "../../abstractions/stat import { Account } from "../../models/account"; import { GlobalState } from "../../models/globalState"; import { BroadcasterMessagingService } from "../../services/broadcasterMessaging.service"; -import { ExportFilePasswordPromptService } from "../../services/exportFilePasswordPrompt.service"; import { FilePasswordPromptService } from "../../services/filePasswordPrompt.service"; import { HtmlStorageService } from "../../services/htmlStorage.service"; import { I18nService } from "../../services/i18n.service"; @@ -44,6 +47,7 @@ import { MemoryStorageService } from "../../services/memoryStorage.service"; import { PasswordRepromptService } from "../../services/passwordReprompt.service"; import { StateService } from "../../services/state.service"; import { StateMigrationService } from "../../services/stateMigration.service"; +import { UserSecretPromptService } from "../../services/userSecretPrompt.service"; import { WebPlatformUtilsService } from "../../services/webPlatformUtils.service"; import { HomeGuard } from "../guards/home.guard"; import { PermissionsGuard as OrgPermissionsGuard } from "../organizations/guards/permissions.guard"; @@ -152,8 +156,8 @@ import { RouterService } from "./router.service"; useClass: FilePasswordPromptService, }, { - provide: ExportFilePasswordPromptServiceAbstraction, - useClass: ExportFilePasswordPromptService, + provide: UserSecretPromptServiceAbstraction, + useClass: UserSecretPromptService, }, HomeGuard, ], diff --git a/apps/web/src/app/tools/export.component.ts b/apps/web/src/app/tools/export.component.ts index 555b8883145..d3db6496bf1 100644 --- a/apps/web/src/app/tools/export.component.ts +++ b/apps/web/src/app/tools/export.component.ts @@ -7,12 +7,12 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { EventService } from "@bitwarden/common/abstractions/event.service"; import { ExportService } from "@bitwarden/common/abstractions/export.service"; -import { ExportFilePasswordPromptService } from "@bitwarden/common/abstractions/exportFilePasswordPrompt.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { PolicyService } from "@bitwarden/common/abstractions/policy.service"; import { StateService } from "@bitwarden/common/abstractions/state.service"; +import { UserSecretPromptService } from "@bitwarden/common/abstractions/userSecretPrompt.service"; import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification.service"; @Component({ @@ -47,7 +47,7 @@ export class ExportComponent extends BaseExportComponent { modalService: ModalService, apiService: ApiService, stateService: StateService, - exportFilePasswordPromptService: ExportFilePasswordPromptService, + userSecretPromptService: UserSecretPromptService, modalConfig: ModalConfig ) { super( @@ -64,7 +64,7 @@ export class ExportComponent extends BaseExportComponent { modalService, apiService, stateService, - exportFilePasswordPromptService, + userSecretPromptService, modalConfig ); } @@ -85,7 +85,7 @@ export class ExportComponent extends BaseExportComponent { const entityId = await this.stateService.getUserId(); try { if ( - await this.exportFilePasswordPromptService.showPasswordPrompt( + await this.userSecretPromptService.showPasswordPrompt( confirmDescription, confirmButtonText, modalTitle diff --git a/apps/web/src/services/exportFilePasswordPrompt.service.ts b/apps/web/src/services/exportFilePasswordPrompt.service.ts deleted file mode 100644 index c18f20f0e9e..00000000000 --- a/apps/web/src/services/exportFilePasswordPrompt.service.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Injectable } from "@angular/core"; - -import { ExportFilePasswordPromptService as BaseExportFilePasswordPrompt } from "@bitwarden/angular/services/exportFilePasswordPrompt.service"; - -import { ExportFilePasswordPromptComponent } from "../app/components/export-file-password-prompt.component"; - -@Injectable() -export class ExportFilePasswordPromptService extends BaseExportFilePasswordPrompt { - component = ExportFilePasswordPromptComponent; -} diff --git a/apps/web/src/services/userSecretPrompt.service.ts b/apps/web/src/services/userSecretPrompt.service.ts new file mode 100644 index 00000000000..dfbd61bbeb5 --- /dev/null +++ b/apps/web/src/services/userSecretPrompt.service.ts @@ -0,0 +1,10 @@ +import { Injectable } from "@angular/core"; + +import { UserSecretPromptService as BaseUserSecretPrompt } from "@bitwarden/angular/services/userSecretPrompt.service"; + +import { UserSecretPromptComponent } from "../app/components/user-secret-prompt.component"; + +@Injectable() +export class UserSecretPromptService extends BaseUserSecretPrompt { + component = UserSecretPromptComponent; +} diff --git a/libs/angular/src/components/export.component.ts b/libs/angular/src/components/export.component.ts index ce5916e13cd..a05e3ac3cbd 100644 --- a/libs/angular/src/components/export.component.ts +++ b/libs/angular/src/components/export.component.ts @@ -13,12 +13,12 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { EventService } from "@bitwarden/common/abstractions/event.service"; import { ExportService } from "@bitwarden/common/abstractions/export.service"; -import { ExportFilePasswordPromptService } from "@bitwarden/common/abstractions/exportFilePasswordPrompt.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { PolicyService } from "@bitwarden/common/abstractions/policy.service"; import { StateService } from "@bitwarden/common/abstractions/state.service"; +import { UserSecretPromptService } from "@bitwarden/common/abstractions/userSecretPrompt.service"; import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification.service"; import { EventType } from "@bitwarden/common/enums/eventType"; import { PolicyType } from "@bitwarden/common/enums/policyType"; @@ -64,7 +64,7 @@ export class ExportComponent implements OnInit { protected modalService: ModalService, protected apiService: ApiService, protected stateService: StateService, - protected exportFilePasswordPromptService: ExportFilePasswordPromptService, + protected userSecretPromptService: UserSecretPromptService, protected modalConfig: ModalConfig ) {} diff --git a/libs/angular/src/components/export-file-password-prompt.component.ts b/libs/angular/src/components/user-secret-prompt.component.ts similarity index 97% rename from libs/angular/src/components/export-file-password-prompt.component.ts rename to libs/angular/src/components/user-secret-prompt.component.ts index 4f7ae35caef..9e174672c58 100644 --- a/libs/angular/src/components/export-file-password-prompt.component.ts +++ b/libs/angular/src/components/user-secret-prompt.component.ts @@ -15,7 +15,7 @@ import { ModalRef } from "./modal/modal.ref"; * Used to verify the user's secret, you can customize all of the text in the modal. */ @Directive() -export class ExportFilePasswordPromptComponent { +export class UserSecretPromptComponent { showPassword = false; organizationId = ""; confirmDescription = ""; diff --git a/libs/angular/src/services/exportFilePasswordPrompt.service.ts b/libs/angular/src/services/userSecretPrompt.service.ts similarity index 73% rename from libs/angular/src/services/exportFilePasswordPrompt.service.ts rename to libs/angular/src/services/userSecretPrompt.service.ts index 6645c12b47e..af1b61ec262 100644 --- a/libs/angular/src/services/exportFilePasswordPrompt.service.ts +++ b/libs/angular/src/services/userSecretPrompt.service.ts @@ -1,9 +1,9 @@ import { Injectable } from "@angular/core"; -import { ExportFilePasswordPromptService as ExportFilePasswordPromptServiceAbstraction } from "@bitwarden/common/abstractions/exportFilePasswordPrompt.service"; import { KeyConnectorService } from "@bitwarden/common/abstractions/keyConnector.service"; +import { UserSecretPromptService as UserSecretPromptServiceAbstraction } from "@bitwarden/common/abstractions/userSecretPrompt.service"; -import { ExportFilePasswordPromptComponent } from "../components/export-file-password-prompt.component"; +import { UserSecretPromptComponent } from "../components/user-secret-prompt.component"; import { ModalService } from "./modal.service"; @@ -11,8 +11,8 @@ import { ModalService } from "./modal.service"; * Used to verify the user's File Password for the "Import passwords using File Password" feature only. */ @Injectable() -export class ExportFilePasswordPromptService implements ExportFilePasswordPromptServiceAbstraction { - protected component = ExportFilePasswordPromptComponent; +export class UserSecretPromptService implements UserSecretPromptServiceAbstraction { + protected component = UserSecretPromptComponent; constructor( private modalService: ModalService, diff --git a/libs/common/src/abstractions/exportFilePasswordPrompt.service.ts b/libs/common/src/abstractions/userSecretPrompt.service.ts similarity index 78% rename from libs/common/src/abstractions/exportFilePasswordPrompt.service.ts rename to libs/common/src/abstractions/userSecretPrompt.service.ts index ca17d4191e8..523b46a2f00 100644 --- a/libs/common/src/abstractions/exportFilePasswordPrompt.service.ts +++ b/libs/common/src/abstractions/userSecretPrompt.service.ts @@ -1,4 +1,4 @@ -export abstract class ExportFilePasswordPromptService { +export abstract class UserSecretPromptService { protectedFields: () => string[]; showPasswordPrompt: ( confirmDescription: string, From 3fcb5669855dae2ad27bfb0d3e33b49013ccd0df Mon Sep 17 00:00:00 2001 From: CarleyDiaz-Bitwarden <103955722+CarleyDiaz-Bitwarden@users.noreply.github.com> Date: Mon, 20 Jun 2022 17:32:19 -0400 Subject: [PATCH 07/14] Fixing lint errors --- apps/web/src/app/organizations/tools/export.component.ts | 5 +++++ apps/web/src/app/tools/export.component.ts | 3 +-- libs/angular/src/components/user-secret-prompt.component.ts | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/apps/web/src/app/organizations/tools/export.component.ts b/apps/web/src/app/organizations/tools/export.component.ts index a666df8db14..9589e6fdb2e 100644 --- a/apps/web/src/app/organizations/tools/export.component.ts +++ b/apps/web/src/app/organizations/tools/export.component.ts @@ -78,4 +78,9 @@ export class ExportComponent extends BaseExportComponent { getFileName() { return super.getFileName("org"); } + + async collectEvent(): Promise { + // TODO + // await this.eventService.collect(EventType.Organization_ClientExportedVault); + } } diff --git a/apps/web/src/app/tools/export.component.ts b/apps/web/src/app/tools/export.component.ts index d3db6496bf1..727eb4e0c81 100644 --- a/apps/web/src/app/tools/export.component.ts +++ b/apps/web/src/app/tools/export.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, Output, ViewChild, ViewContainerRef } from "@angular/core"; +import { Component, ViewChild, ViewContainerRef } from "@angular/core"; import { FormBuilder, FormControl } from "@angular/forms"; import { ExportComponent as BaseExportComponent } from "@bitwarden/angular/components/export.component"; @@ -82,7 +82,6 @@ export class ExportComponent extends BaseExportComponent { modalTitle = "confirmVaultExport"; } - const entityId = await this.stateService.getUserId(); try { if ( await this.userSecretPromptService.showPasswordPrompt( diff --git a/libs/angular/src/components/user-secret-prompt.component.ts b/libs/angular/src/components/user-secret-prompt.component.ts index 9e174672c58..92cb93d29a8 100644 --- a/libs/angular/src/components/user-secret-prompt.component.ts +++ b/libs/angular/src/components/user-secret-prompt.component.ts @@ -1,5 +1,5 @@ import { Directive } from "@angular/core"; -import { FormBuilder, FormControl, FormGroup } from "@angular/forms"; +import { FormBuilder, FormControl } from "@angular/forms"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; From 2a6f71bd0051ec9f8cda0ebe2080071dafa193a0 Mon Sep 17 00:00:00 2001 From: CarleyDiaz-Bitwarden <103955722+CarleyDiaz-Bitwarden@users.noreply.github.com> Date: Mon, 20 Jun 2022 17:37:19 -0400 Subject: [PATCH 08/14] Adding a comment --- libs/angular/src/components/export.component.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libs/angular/src/components/export.component.ts b/libs/angular/src/components/export.component.ts index a05e3ac3cbd..5bce50e914d 100644 --- a/libs/angular/src/components/export.component.ts +++ b/libs/angular/src/components/export.component.ts @@ -184,9 +184,8 @@ export class ExportComponent implements OnInit { } get validForm() { - //TODO check if fileEncryption type is null? + //fileEncryptionType 2 = file requires a user entered password, specific to the file if (this.fileEncryptionType == 2 && this.format == "encrypted_json") { - //password encryption type const password = this.password; const confirmPassword = this.confirmPassword; From 85e327ab7d745b5b1462ef4c808b27ad7bb0ea33 Mon Sep 17 00:00:00 2001 From: CarleyDiaz-Bitwarden <103955722+CarleyDiaz-Bitwarden@users.noreply.github.com> Date: Tue, 21 Jun 2022 16:17:41 -0400 Subject: [PATCH 09/14] Suggested changes from CR --- .../user-secret-prompt.component.html | 36 ----------- .../user-secret-prompt.component.ts | 8 --- .../src/services/userSecretPrompt.service.ts | 10 ---- .../user-secret-prompt.component.ts | 60 ------------------- .../src/services/userSecretPrompt.service.ts | 54 ----------------- .../abstractions/userSecretPrompt.service.ts | 9 --- .../userVerificationPrompt.service.ts | 9 +++ libs/common/src/enums/encryptedExportType.ts | 4 ++ 8 files changed, 13 insertions(+), 177 deletions(-) delete mode 100644 apps/web/src/app/components/user-secret-prompt.component.html delete mode 100644 apps/web/src/app/components/user-secret-prompt.component.ts delete mode 100644 apps/web/src/services/userSecretPrompt.service.ts delete mode 100644 libs/angular/src/components/user-secret-prompt.component.ts delete mode 100644 libs/angular/src/services/userSecretPrompt.service.ts delete mode 100644 libs/common/src/abstractions/userSecretPrompt.service.ts create mode 100644 libs/common/src/abstractions/userVerificationPrompt.service.ts create mode 100644 libs/common/src/enums/encryptedExportType.ts diff --git a/apps/web/src/app/components/user-secret-prompt.component.html b/apps/web/src/app/components/user-secret-prompt.component.html deleted file mode 100644 index c5de28156c7..00000000000 --- a/apps/web/src/app/components/user-secret-prompt.component.html +++ /dev/null @@ -1,36 +0,0 @@ - diff --git a/apps/web/src/app/components/user-secret-prompt.component.ts b/apps/web/src/app/components/user-secret-prompt.component.ts deleted file mode 100644 index 2dfc34c59e5..00000000000 --- a/apps/web/src/app/components/user-secret-prompt.component.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Component } from "@angular/core"; - -import { UserSecretPromptComponent as BaseUserSecretPrompt } from "@bitwarden/angular/components/user-secret-prompt.component"; - -@Component({ - templateUrl: "user-secret-prompt.component.html", -}) -export class UserSecretPromptComponent extends BaseUserSecretPrompt {} diff --git a/apps/web/src/services/userSecretPrompt.service.ts b/apps/web/src/services/userSecretPrompt.service.ts deleted file mode 100644 index dfbd61bbeb5..00000000000 --- a/apps/web/src/services/userSecretPrompt.service.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Injectable } from "@angular/core"; - -import { UserSecretPromptService as BaseUserSecretPrompt } from "@bitwarden/angular/services/userSecretPrompt.service"; - -import { UserSecretPromptComponent } from "../app/components/user-secret-prompt.component"; - -@Injectable() -export class UserSecretPromptService extends BaseUserSecretPrompt { - component = UserSecretPromptComponent; -} diff --git a/libs/angular/src/components/user-secret-prompt.component.ts b/libs/angular/src/components/user-secret-prompt.component.ts deleted file mode 100644 index 92cb93d29a8..00000000000 --- a/libs/angular/src/components/user-secret-prompt.component.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { Directive } from "@angular/core"; -import { FormBuilder, FormControl } from "@angular/forms"; - -import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; -import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; -import { ImportService } from "@bitwarden/common/abstractions/import.service"; -import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; -import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification.service"; - -import { ModalConfig } from "../services/modal.service"; - -import { ModalRef } from "./modal/modal.ref"; - -/** - * Used to verify the user's secret, you can customize all of the text in the modal. - */ -@Directive() -export class UserSecretPromptComponent { - showPassword = false; - organizationId = ""; - confirmDescription = ""; - confirmButtonText = ""; - modalTitle = ""; - - myGroup = this.formBuilder.group({ - secret: new FormControl(), - }); - - constructor( - private modalRef: ModalRef, - private cryptoService: CryptoService, - private platformUtilsService: PlatformUtilsService, - private i18nService: I18nService, - private importService: ImportService, - protected config: ModalConfig, - protected userVerificationService: UserVerificationService, - private formBuilder: FormBuilder - ) { - this.confirmDescription = config.data.confirmDescription; - this.confirmButtonText = config.data.confirmButtonText; - this.modalTitle = config.data.modalTitle; - } - - togglePassword() { - this.showPassword = !this.showPassword; - } - - async submit() { - const secret = this.myGroup.get("secret").value; - - try { - await this.userVerificationService.verifyUser(secret); - } catch (e) { - this.modalRef.close(false); - return; - } - - this.modalRef.close(true); - } -} diff --git a/libs/angular/src/services/userSecretPrompt.service.ts b/libs/angular/src/services/userSecretPrompt.service.ts deleted file mode 100644 index af1b61ec262..00000000000 --- a/libs/angular/src/services/userSecretPrompt.service.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { Injectable } from "@angular/core"; - -import { KeyConnectorService } from "@bitwarden/common/abstractions/keyConnector.service"; -import { UserSecretPromptService as UserSecretPromptServiceAbstraction } from "@bitwarden/common/abstractions/userSecretPrompt.service"; - -import { UserSecretPromptComponent } from "../components/user-secret-prompt.component"; - -import { ModalService } from "./modal.service"; - -/** - * Used to verify the user's File Password for the "Import passwords using File Password" feature only. - */ -@Injectable() -export class UserSecretPromptService implements UserSecretPromptServiceAbstraction { - protected component = UserSecretPromptComponent; - - constructor( - private modalService: ModalService, - private keyConnectorService: KeyConnectorService - ) {} - - protectedFields() { - return ["TOTP", "Password", "H_Field", "Card Number", "Security Code"]; - } - - async showPasswordPrompt( - confirmDescription: string, - confirmButtonText: string, - modalTitle: string - ) { - if (!(await this.enabled())) { - return true; - } - - const ref = await this.modalService.open(this.component, { - allowMultipleModals: true, - data: { - confirmDescription: confirmDescription, - confirmButtonText: confirmButtonText, - modalTitle: modalTitle, - }, - }); - - if (ref == null) { - return false; - } - - return (await ref.onClosedPromise()) === true; - } - - async enabled() { - return !(await this.keyConnectorService.getUsesKeyConnector()); - } -} diff --git a/libs/common/src/abstractions/userSecretPrompt.service.ts b/libs/common/src/abstractions/userSecretPrompt.service.ts deleted file mode 100644 index 523b46a2f00..00000000000 --- a/libs/common/src/abstractions/userSecretPrompt.service.ts +++ /dev/null @@ -1,9 +0,0 @@ -export abstract class UserSecretPromptService { - protectedFields: () => string[]; - showPasswordPrompt: ( - confirmDescription: string, - confirmButtonText: string, - modalTitle: string - ) => Promise; - enabled: () => Promise; -} diff --git a/libs/common/src/abstractions/userVerificationPrompt.service.ts b/libs/common/src/abstractions/userVerificationPrompt.service.ts new file mode 100644 index 00000000000..83b9f7d2eae --- /dev/null +++ b/libs/common/src/abstractions/userVerificationPrompt.service.ts @@ -0,0 +1,9 @@ +export abstract class UserVerificationPromptService { + protectedFields: () => string[]; + showUserVerificationPrompt: ( + confirmDescription?: string, + confirmButtonText?: string, + modalTitle?: string + ) => Promise; + enabled: () => Promise; +} diff --git a/libs/common/src/enums/encryptedExportType.ts b/libs/common/src/enums/encryptedExportType.ts new file mode 100644 index 00000000000..4767869f7d0 --- /dev/null +++ b/libs/common/src/enums/encryptedExportType.ts @@ -0,0 +1,4 @@ +export enum EncryptedExportType { + AccountEncrypted = 0, + FileEncrypted = 1, +} From 568537af19dcfdbee3e903b38168602f8b3582c1 Mon Sep 17 00:00:00 2001 From: CarleyDiaz-Bitwarden <103955722+CarleyDiaz-Bitwarden@users.noreply.github.com> Date: Tue, 21 Jun 2022 16:17:57 -0400 Subject: [PATCH 10/14] Suggested changes from CR --- .../file-password-prompt.component.html | 2 +- .../user-verification-prompt.component.html | 36 +++++ .../user-verification-prompt.component.ts | 8 ++ .../app/modules/loose-components.module.ts | 4 +- .../organizations/tools/export.component.ts | 6 +- apps/web/src/app/services/services.module.ts | 8 +- apps/web/src/app/tools/export.component.html | 132 ++++++++---------- apps/web/src/app/tools/export.component.ts | 44 ++---- apps/web/src/app/tools/import.component.ts | 5 - apps/web/src/locales/en/messages.json | 15 +- apps/web/src/locales/en_GB/messages.json | 27 ---- apps/web/src/scss/modals.scss | 19 ++- .../userVerificationPrompt.service.ts | 10 ++ .../src/components/export.component.ts | 76 ++++++---- .../file-password-prompt.component.ts | 8 +- .../user-verification-prompt.component.ts | 56 ++++++++ .../services/filePasswordPrompt.service.ts | 3 +- .../userVerificationPrompt.service.ts | 56 ++++++++ 18 files changed, 329 insertions(+), 186 deletions(-) create mode 100644 apps/web/src/app/components/user-verification-prompt.component.html create mode 100644 apps/web/src/app/components/user-verification-prompt.component.ts create mode 100644 apps/web/src/services/userVerificationPrompt.service.ts create mode 100644 libs/angular/src/components/user-verification-prompt.component.ts create mode 100644 libs/angular/src/services/userVerificationPrompt.service.ts diff --git a/apps/web/src/app/components/file-password-prompt.component.html b/apps/web/src/app/components/file-password-prompt.component.html index 3a2ff6f27d3..993d957af66 100644 --- a/apps/web/src/app/components/file-password-prompt.component.html +++ b/apps/web/src/app/components/file-password-prompt.component.html @@ -49,7 +49,7 @@ -
-
- - - -
+
+ +
- -
+
- +
{{ "accountBackupOptionDescription" | i18n }}
@@ -67,87 +60,84 @@ id="2" [value]="2" [(ngModel)]="encryptionType" - (change)="encryptionType = '2'" + (change)="encryptionType = 1" /> - +
{{ "passwordProtectedOptionDescription" | i18n }}

- - - + + -
- -
- -
-
- -
- {{ "exportPasswordDescription" | i18n }} -
-
- -
- - -
-
+
+ +
-
-
+ +
+ {{ "exportPasswordDescription" | i18n }} +
+
+ +
+ + +
+
+ +
+
+
+
+