1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-06 00:13:28 +00:00

[PM-2169] Update Password Reprompt Modal to use Component Library (#5720)

* update password reprompt to use the dialog CL

* Override showPasswordPrompt and submit method on web child classes from base classes to allow dialog work on web and modal on other clients

* Override showPasswordPrompt and submit method on web child classes from base classes to allow dialog work on web and modal on other clients

* Fixed lint issues

* Corrected comments

* Refactored passwored reprompt to use dialog service after changes to make the dialog service work on the desktop and browser

* Changed access modifier from protected to protected

* Refactored passwprd reprompt component to a stand alone component and fixed all references

* fix merge changes

* fix import aliases in password-reprompt.component.ts

* fix alias typo in browser tsconfig

* import from root vault alias

* revert tsconfig changes

* remove service abstraction and update imports

* remove component from imports

* Removed unneccesary show password toggle

* renamed selector to use vault prefix

* removed unnecessary data dismiss

* merged and fixed conflicts

* remove reintroduced file

* Added appAutoFocus to reprompt dialog

* delayed validation until submit happens

---------

Co-authored-by: William Martin <contact@willmartian.com>
This commit is contained in:
SmithThe4th
2023-10-04 22:56:27 -04:00
committed by GitHub
parent a91a39fe80
commit 922de469f4
51 changed files with 144 additions and 332 deletions

View File

@@ -136,7 +136,6 @@ import {
FolderService as FolderServiceAbstraction,
InternalFolderService,
} from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
import { PasswordRepromptService as PasswordRepromptServiceAbstraction } from "@bitwarden/common/vault/abstractions/password-reprompt.service";
import { SyncNotifierService as SyncNotifierServiceAbstraction } from "@bitwarden/common/vault/abstractions/sync/sync-notifier.service.abstraction";
import { SyncService as SyncServiceAbstraction } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { CipherService } from "@bitwarden/common/vault/services/cipher.service";
@@ -156,13 +155,13 @@ import {
ImportService,
ImportServiceAbstraction,
} from "@bitwarden/importer";
import { PasswordRepromptService } from "@bitwarden/vault";
import { AuthGuard } from "../auth/guards/auth.guard";
import { UnauthGuard } from "../auth/guards/unauth.guard";
import { FormValidationErrorsService as FormValidationErrorsServiceAbstraction } from "../platform/abstractions/form-validation-errors.service";
import { BroadcasterService } from "../platform/services/broadcaster.service";
import { FormValidationErrorsService } from "../platform/services/form-validation-errors.service";
import { PasswordRepromptService } from "../vault/services/password-reprompt.service";
import {
LOCALES_DIRECTORY,
@@ -186,6 +185,8 @@ import { AbstractThemingService } from "./theming/theming.service.abstraction";
AuthGuard,
UnauthGuard,
ModalService,
PasswordRepromptService,
{ provide: WINDOW, useValue: window },
{
provide: LOCALE_ID,
@@ -603,7 +604,6 @@ import { AbstractThemingService } from "./theming/theming.service.abstraction";
UserVerificationApiServiceAbstraction,
],
},
{ provide: PasswordRepromptServiceAbstraction, useClass: PasswordRepromptService },
{
provide: OrganizationServiceAbstraction,
useClass: OrganizationService,

View File

@@ -21,7 +21,6 @@ import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.s
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service";
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
import { PasswordRepromptService } from "@bitwarden/common/vault/abstractions/password-reprompt.service";
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
import { CipherType } from "@bitwarden/common/vault/enums/cipher-type";
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
@@ -34,6 +33,7 @@ import { LoginUriView } from "@bitwarden/common/vault/models/view/login-uri.view
import { LoginView } from "@bitwarden/common/vault/models/view/login.view";
import { SecureNoteView } from "@bitwarden/common/vault/models/view/secure-note.view";
import { DialogService } from "@bitwarden/components";
import { PasswordRepromptService } from "@bitwarden/vault";
@Directive()
export class AddEditComponent implements OnInit, OnDestroy {

View File

@@ -1,42 +0,0 @@
import { Directive } from "@angular/core";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { ModalRef } from "../../components/modal/modal.ref";
/**
* Used to verify the user's Master Password for the "Master Password Re-prompt" feature only.
* See UserVerificationComponent for any other situation where you need to verify the user's identity.
*/
@Directive()
export class PasswordRepromptComponent {
showPassword = false;
masterPassword = "";
constructor(
private modalRef: ModalRef,
private cryptoService: CryptoService,
private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService
) {}
togglePassword() {
this.showPassword = !this.showPassword;
}
async submit() {
const storedMasterKey = await this.cryptoService.getOrDeriveMasterKey(this.masterPassword);
if (!(await this.cryptoService.compareAndUpdateKeyHash(this.masterPassword, storedMasterKey))) {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
this.i18nService.t("invalidMasterPassword")
);
return;
}
this.modalRef.close(true);
}
}

View File

@@ -27,7 +27,6 @@ import { StateService } from "@bitwarden/common/platform/abstractions/state.serv
import { EncArrayBuffer } from "@bitwarden/common/platform/models/domain/enc-array-buffer";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
import { PasswordRepromptService } from "@bitwarden/common/vault/abstractions/password-reprompt.service";
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
import { CipherType } from "@bitwarden/common/vault/enums/cipher-type";
import { AttachmentView } from "@bitwarden/common/vault/models/view/attachment.view";
@@ -35,6 +34,7 @@ import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
import { LoginUriView } from "@bitwarden/common/vault/models/view/login-uri.view";
import { DialogService } from "@bitwarden/components";
import { PasswordRepromptService } from "@bitwarden/vault";
const BroadcasterSubscriptionId = "ViewComponent";

View File

@@ -1,5 +0,0 @@
export abstract class PasswordRepromptService {
protectedFields: () => string[];
showPasswordPrompt: () => Promise<boolean>;
enabled: () => Promise<boolean>;
}

View File

@@ -0,0 +1,31 @@
<form [formGroup]="formGroup" [bitSubmit]="submit">
<bit-dialog>
<span bitDialogTitle>
{{ "passwordConfirmation" | i18n }}
</span>
<ng-container bitDialogContent>
{{ "passwordConfirmationDesc" | i18n }}
<bit-form-field disableMargin class="tw-mt-6">
<bit-label>{{ "masterPass" | i18n }}</bit-label>
<input
bitInput
appAutofocus
id="masterPassword"
type="password"
formControlName="masterPassword"
/>
<button type="button" bitSuffix bitIconButton bitPasswordInputToggle></button>
</bit-form-field>
</ng-container>
<ng-container bitDialogFooter>
<button bitButton buttonType="primary" bitFormButton type="submit">
<span>{{ "ok" | i18n }}</span>
</button>
<button bitButton buttonType="secondary" bitDialogClose type="button">
{{ "cancel" | i18n }}
</button>
</ng-container>
</bit-dialog>
</form>

View File

@@ -0,0 +1,68 @@
import { DialogRef } from "@angular/cdk/dialog";
import { Component } from "@angular/core";
import { FormBuilder, ReactiveFormsModule, Validators } from "@angular/forms";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import {
AsyncActionsModule,
ButtonModule,
DialogModule,
FormFieldModule,
IconButtonModule,
} from "@bitwarden/components";
/**
* Used to verify the user's Master Password for the "Master Password Re-prompt" feature only.
* See UserVerificationComponent for any other situation where you need to verify the user's identity.
*/
@Component({
standalone: true,
selector: "vault-password-reprompt",
imports: [
JslibModule,
AsyncActionsModule,
ButtonModule,
DialogModule,
FormFieldModule,
IconButtonModule,
ReactiveFormsModule,
],
templateUrl: "password-reprompt.component.html",
})
export class PasswordRepromptComponent {
formGroup = this.formBuilder.group({
masterPassword: ["", { validators: [Validators.required], updateOn: "submit" }],
});
constructor(
protected cryptoService: CryptoService,
protected platformUtilsService: PlatformUtilsService,
protected i18nService: I18nService,
protected formBuilder: FormBuilder,
protected dialogRef: DialogRef
) {}
submit = async () => {
const storedMasterKey = await this.cryptoService.getOrDeriveMasterKey(
this.formGroup.value.masterPassword
);
if (
!(await this.cryptoService.compareAndUpdateKeyHash(
this.formGroup.value.masterPassword,
storedMasterKey
))
) {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
this.i18nService.t("invalidMasterPassword")
);
return;
}
this.dialogRef.close(true);
};
}

View File

@@ -0,0 +1 @@
export { PasswordRepromptService } from "./services/password-reprompt.service";

View File

@@ -1,8 +1,7 @@
import { MockProxy, mock } from "jest-mock-extended";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { ModalService } from "../../services/modal.service";
import { UserVerificationService } from "@bitwarden/common/src/auth/abstractions/user-verification/user-verification.service.abstraction";
import { DialogService } from "@bitwarden/components";
import { PasswordRepromptService } from "./password-reprompt.service";
@@ -10,13 +9,13 @@ describe("PasswordRepromptService", () => {
let passwordRepromptService: PasswordRepromptService;
let userVerificationService: MockProxy<UserVerificationService>;
let modalService: MockProxy<ModalService>;
let dialogService: MockProxy<DialogService>;
beforeEach(() => {
modalService = mock<ModalService>();
dialogService = mock<DialogService>();
userVerificationService = mock<UserVerificationService>();
passwordRepromptService = new PasswordRepromptService(modalService, userVerificationService);
passwordRepromptService = new PasswordRepromptService(dialogService, userVerificationService);
});
describe("enabled()", () => {

View File

@@ -1,9 +1,9 @@
import { Injectable } from "@angular/core";
import { lastValueFrom } from "rxjs";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { PasswordRepromptService as PasswordRepromptServiceAbstraction } from "@bitwarden/common/vault/abstractions/password-reprompt.service";
import { DialogService } from "@bitwarden/components";
import { ModalService } from "../../services/modal.service";
import { PasswordRepromptComponent } from "../components/password-reprompt.component";
/**
@@ -11,11 +11,9 @@ import { PasswordRepromptComponent } from "../components/password-reprompt.compo
* See UserVerificationService for any other situation where you need to verify the user's identity.
*/
@Injectable()
export class PasswordRepromptService implements PasswordRepromptServiceAbstraction {
protected component = PasswordRepromptComponent;
export class PasswordRepromptService {
constructor(
private modalService: ModalService,
private dialogService: DialogService,
private userVerificationService: UserVerificationService
) {}
@@ -28,13 +26,12 @@ export class PasswordRepromptService implements PasswordRepromptServiceAbstracti
return true;
}
const ref = this.modalService.open(this.component, { allowMultipleModals: true });
const dialog = this.dialogService.open<boolean>(PasswordRepromptComponent, {
ariaModal: true,
});
if (ref == null) {
return false;
}
const result = await lastValueFrom(dialog.closed);
const result = await ref.onClosedPromise();
return result === true;
}