1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-13 14:53:33 +00:00

[SM-89] Updates to encrypted export (#2963)

* Rough draft of Export/Import changes w/ password encryption

* fix for encrypted export changes

* Create launch.json

* Updates to export logic modal user secret prompt

* Updates to error handling

* renaming the component for checking the user secret to a name that is more clear about what it accomplishes

* Fixing lint errors

* Adding a comment

* Suggested changes from CR

* Suggested changes from CR

* Making suggested changes

* removing unnecessary properties

* changes suggested

* Fix

* Updating error messages

* Removing unecessary launch.json file commit

* running lint, removing commented code

* removing launch.json

* Updates to remove the userVerificationPromptService

* updates

* Removing unused import, running npm prettier/lint

* Changes to use Form Fields

* Updates

* updates requested by Matt

* Update apps/web/src/app/tools/import-export/export.component.ts

Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>

* Suggested Changes from PR

* Fix after merge from Master

* changes to styling

* Removing unused code and cleanup

* Update libs/angular/src/components/user-verification-prompt.component.ts

Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>

* Update apps/web/src/locales/en/messages.json

Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>

* Changes suggested by Thomas R

* Merging master into branch

* Revert "Merging master into branch"

This reverts commit eb2cdffe49.

* Requested changes and improvements

* merging master into feature branch

* Revert "merging master into feature branch"

This reverts commit e287715251.

* Suggested Changes

* changes

* requested changes

* Requested changes

* removing comments, fixing code

* reducing copied code

* fixing bug

* fixing bug

* changes

* WIP

* Thomas's requested changes

* adding back missing spaces

* change needed after the merge from master into feature branch

* prettier + lint

* Updating the EncryptedExportType Import

* Fixing build errors

Co-authored-by: Thomas Rittson <eliykat@users.noreply.github.com>

* Move FilePasswordPrompt to ImportExportModule

Also remove base class
Also remove duplicate service providers

* Run prettier

* Suggested Changes from Thomas

* only require filePassword and confirmFilePassword if it's type is FileEncrypted

* Update to only enable the field when submitting a file password encrypted file

* Requested changes, moving logic to web

* undoing change to bit button

* Refactor to process file-encrypted imports in main import.component

*  Refactor confirm file password check

* Remove UserVerificationPromptService

* Address CodeScene feedback

* Updates to disable the required file password field when needed

* Subscribe to reactive form changes to adjust validators

* style changes requested by suhkleen

* Delete duplicate classes

Co-authored-by: CarleyDiaz-Bitwarden <103955722+CarleyDiaz-Bitwarden@users.noreply.github.com>
Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>
Co-authored-by: Thomas Rittson <trittson@bitwarden.com>
Co-authored-by: Thomas Rittson <eliykat@users.noreply.github.com>
This commit is contained in:
cd-bitwarden
2022-08-29 10:11:44 -04:00
committed by GitHub
parent 231e1bf666
commit a108476c3c
19 changed files with 607 additions and 43 deletions

View File

@@ -1,5 +1,6 @@
import { Directive, EventEmitter, OnInit, Output } from "@angular/core";
import { UntypedFormBuilder } from "@angular/forms";
import { Directive, EventEmitter, OnDestroy, OnInit, Output } from "@angular/core";
import { UntypedFormBuilder, Validators } from "@angular/forms";
import { merge, takeUntil, Subject, startWith } from "rxjs";
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
import { EventService } from "@bitwarden/common/abstractions/event.service";
@@ -10,19 +11,25 @@ import { LogService } from "@bitwarden/common/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
import { PolicyService } from "@bitwarden/common/abstractions/policy/policy.service.abstraction";
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
import { EncryptedExportType } from "@bitwarden/common/enums/encryptedExportType";
import { EventType } from "@bitwarden/common/enums/eventType";
import { PolicyType } from "@bitwarden/common/enums/policyType";
@Directive()
export class ExportComponent implements OnInit {
export class ExportComponent implements OnInit, OnDestroy {
@Output() onSaved = new EventEmitter();
formPromise: Promise<string>;
disabledByPolicy = false;
showFilePassword: boolean;
showConfirmFilePassword: boolean;
exportForm = this.formBuilder.group({
format: ["json"],
secret: [""],
filePassword: ["", Validators.required],
confirmFilePassword: ["", Validators.required],
fileEncryptionType: [EncryptedExportType.AccountEncrypted],
});
formatOptions = [
@@ -31,6 +38,8 @@ export class ExportComponent implements OnInit {
{ name: ".json (Encrypted)", value: "encrypted_json" },
];
private destroy$ = new Subject<void>();
constructor(
protected cryptoService: CryptoService,
protected i18nService: I18nService,
@@ -47,6 +56,18 @@ export class ExportComponent implements OnInit {
async ngOnInit() {
await this.checkExportDisabled();
merge(
this.exportForm.get("format").valueChanges,
this.exportForm.get("fileEncryptionType").valueChanges
)
.pipe(takeUntil(this.destroy$))
.pipe(startWith(0))
.subscribe(() => this.adjustValidators());
}
ngOnDestroy(): void {
this.destroy$.next();
}
async checkExportDisabled() {
@@ -62,6 +83,20 @@ export class ExportComponent implements OnInit {
return this.format === "encrypted_json";
}
protected async doExport() {
try {
this.formPromise = this.getExportData();
const data = await this.formPromise;
this.downloadFile(data);
this.saved();
await this.collectEvent();
this.exportForm.get("secret").setValue("");
this.exportForm.clearValidators();
} catch (e) {
this.logService.error(e);
}
}
async submit() {
if (this.disabledByPolicy) {
this.platformUtilsService.showToast(
@@ -76,25 +111,15 @@ export class ExportComponent implements OnInit {
if (!acceptedWarning) {
return;
}
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 {
this.formPromise = this.getExportData();
const data = await this.formPromise;
this.downloadFile(data);
this.saved();
await this.collectEvent();
this.exportForm.get("secret").setValue("");
} catch (e) {
this.logService.error(e);
}
this.doExport();
}
async warningDialog() {
@@ -126,7 +151,14 @@ export class ExportComponent implements OnInit {
}
protected getExportData() {
return this.exportService.getExport(this.format);
if (
this.format === "encrypted_json" &&
this.fileEncryptionType === EncryptedExportType.FileEncrypted
) {
return this.exportService.getPasswordProtectedExport(this.filePassword);
} else {
return this.exportService.getExport(this.format, null);
}
}
protected getFileName(prefix?: string) {
@@ -150,6 +182,41 @@ export class ExportComponent implements OnInit {
return this.exportForm.get("format").value;
}
get filePassword() {
return this.exportForm.get("filePassword").value;
}
get confirmFilePassword() {
return this.exportForm.get("confirmFilePassword").value;
}
get fileEncryptionType() {
return this.exportForm.get("fileEncryptionType").value;
}
toggleFilePassword() {
this.showFilePassword = !this.showFilePassword;
document.getElementById("filePassword").focus();
}
toggleConfirmFilePassword() {
this.showConfirmFilePassword = !this.showConfirmFilePassword;
document.getElementById("confirmFilePassword").focus();
}
adjustValidators() {
this.exportForm.get("confirmFilePassword").reset();
this.exportForm.get("filePassword").reset();
if (this.encryptedFormat && this.fileEncryptionType == EncryptedExportType.FileEncrypted) {
this.exportForm.controls.filePassword.enable();
this.exportForm.controls.confirmFilePassword.enable();
} else {
this.exportForm.controls.filePassword.disable();
this.exportForm.controls.confirmFilePassword.disable();
}
}
private downloadFile(csv: string): void {
const fileName = this.getFileName();
this.fileDownloadService.download({

View File

@@ -0,0 +1,46 @@
import { Directive } from "@angular/core";
import { FormBuilder, FormControl } from "@angular/forms";
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
import { ModalConfig } from "../services/modal.service";
import { ModalRef } from "./modal/modal.ref";
/**
* Used to verify the user's identity (using their master password or email-based OTP for Key Connector users). You can customize all of the text in the modal.
*/
@Directive()
export class UserVerificationPromptComponent {
confirmDescription = this.config.data.confirmDescription;
confirmButtonText = this.config.data.confirmButtonText;
modalTitle = this.config.data.modalTitle;
secret = new FormControl();
constructor(
private modalRef: ModalRef,
protected config: ModalConfig,
protected userVerificationService: UserVerificationService,
private formBuilder: FormBuilder,
private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService
) {}
async submit() {
try {
//Incorrect secret will throw an invalid password error.
await this.userVerificationService.verifyUser(this.secret.value);
} catch (e) {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("error"),
this.i18nService.t("invalidMasterPassword")
);
return;
}
this.modalRef.close(true);
}
}