diff --git a/apps/web/src/app/auth/settings/emergency-access/takeover/emergency-access-takeover-dialog.component.html b/apps/web/src/app/auth/settings/emergency-access/takeover/emergency-access-takeover-dialog.component.html
index cd8cbf85b7b..42bacf28d8d 100644
--- a/apps/web/src/app/auth/settings/emergency-access/takeover/emergency-access-takeover-dialog.component.html
+++ b/apps/web/src/app/auth/settings/emergency-access/takeover/emergency-access-takeover-dialog.component.html
@@ -9,6 +9,13 @@
"emergencyAccessLoggedOutWarning" | i18n: dialogData.grantorName
}}
+
+
diff --git a/apps/web/src/app/auth/settings/emergency-access/takeover/emergency-access-takeover-dialog.component.ts b/apps/web/src/app/auth/settings/emergency-access/takeover/emergency-access-takeover-dialog.component.ts
index 25dedde4a24..08b38b1b783 100644
--- a/apps/web/src/app/auth/settings/emergency-access/takeover/emergency-access-takeover-dialog.component.ts
+++ b/apps/web/src/app/auth/settings/emergency-access/takeover/emergency-access-takeover-dialog.component.ts
@@ -1,18 +1,33 @@
import { CommonModule } from "@angular/common";
-import { Component, Inject } from "@angular/core";
+import { Component, Inject, OnInit } from "@angular/core";
+import { firstValueFrom } from "rxjs";
-import { ChangePasswordComponent, InputPasswordFlow } from "@bitwarden/auth/angular";
+import {
+ ChangePasswordComponent,
+ InputPasswordComponent,
+ InputPasswordFlow,
+ PasswordInputResult,
+} from "@bitwarden/auth/angular";
+import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
+import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options";
+import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
+import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
+import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import {
ButtonModule,
CalloutModule,
DIALOG_DATA,
DialogConfig,
DialogModule,
+ DialogRef,
DialogService,
FormFieldModule,
+ ToastService,
} from "@bitwarden/components";
import { I18nPipe } from "@bitwarden/ui-common";
+import { EmergencyAccessService } from "../../../emergency-access";
+
type EmergencyAccessTakeoverDialogData = {
grantorName: string;
grantorEmail: string;
@@ -43,12 +58,58 @@ export enum EmergencyAccessTakeoverDialogResultType {
DialogModule,
FormFieldModule,
I18nPipe,
+ InputPasswordComponent,
],
})
-export class EmergencyAccessTakeoverDialogComponent {
+export class EmergencyAccessTakeoverDialogComponent implements OnInit {
inputPasswordFlow = InputPasswordFlow.ChangePasswordDelegation;
- constructor(@Inject(DIALOG_DATA) protected dialogData: EmergencyAccessTakeoverDialogData) {}
+ initializing = true;
+ masterPasswordPolicyOptions?: MasterPasswordPolicyOptions;
+
+ constructor(
+ @Inject(DIALOG_DATA) protected dialogData: EmergencyAccessTakeoverDialogData,
+ private accountService: AccountService,
+ private dialogRef: DialogRef,
+ private emergencyAccessService: EmergencyAccessService,
+ private i18nService: I18nService,
+ private logService: LogService,
+ private policyService: PolicyService,
+ private toastService: ToastService,
+ ) {}
+
+ async ngOnInit() {
+ const activeAccount = await firstValueFrom(this.accountService.activeAccount$);
+ const userId = activeAccount?.id;
+
+ const grantorPolicies = await this.emergencyAccessService.getGrantorPolicies(
+ this.dialogData.emergencyAccessId,
+ );
+
+ this.masterPasswordPolicyOptions = await firstValueFrom(
+ this.policyService.masterPasswordPolicyOptions$(userId, grantorPolicies),
+ );
+ }
+
+ async handlePasswordFormSubmit(passwordInputResult: PasswordInputResult) {
+ try {
+ await this.emergencyAccessService.takeover(
+ this.dialogData.emergencyAccessId,
+ passwordInputResult.newPassword,
+ this.dialogData.grantorEmail,
+ );
+ } catch (e) {
+ this.logService.error(e);
+
+ this.toastService.showToast({
+ variant: "error",
+ title: this.i18nService.t("errorOccurred"),
+ message: this.i18nService.t("unexpectedError"),
+ });
+ }
+
+ this.dialogRef.close(EmergencyAccessTakeoverDialogResultType.Done);
+ }
/**
* Strongly typed helper to open a EmergencyAccessTakeoverDialogComponent
diff --git a/libs/auth/src/angular/input-password/input-password.component.ts b/libs/auth/src/angular/input-password/input-password.component.ts
index a23a4c67199..e73cbf677f0 100644
--- a/libs/auth/src/angular/input-password/input-password.component.ts
+++ b/libs/auth/src/angular/input-password/input-password.component.ts
@@ -108,8 +108,8 @@ export class InputPasswordComponent implements OnInit {
@Output() onSecondaryButtonClick = new EventEmitter();
@Input({ required: true }) flow!: InputPasswordFlow;
- @Input({ required: true, transform: (val: string) => val.trim().toLowerCase() }) email!: string;
+ @Input({ transform: (val: string) => val?.trim().toLowerCase() }) email?: string;
@Input() userId?: UserId;
@Input() loading = false;
@Input() masterPasswordPolicyOptions: MasterPasswordPolicyOptions | null = null;
@@ -247,7 +247,7 @@ export class InputPasswordComponent implements OnInit {
}
protected submit = async () => {
- this.verifyFlowAndUserId();
+ this.verifyFlow();
this.formGroup.markAllAsTouched();
@@ -375,13 +375,16 @@ export class InputPasswordComponent implements OnInit {
* We cannot mark the `userId` `@Input` as required because in an account registration
* flow we will not have an active account `userId` to pass down.
*/
- private verifyFlowAndUserId() {
+ private verifyFlow() {
/**
* There can be no active account (and thus no userId) in an account registration
* flow. If there is a userId, it means the dev passed down the wrong InputPasswordFlow
* from the parent component.
*/
- if (this.flow === InputPasswordFlow.AccountRegistration) {
+ if (
+ this.flow === InputPasswordFlow.AccountRegistration ||
+ this.flow === InputPasswordFlow.ChangePasswordDelegation
+ ) {
if (this.userId) {
throw new Error(
"There can be no userId in an account registration flow. Please pass down the appropriate InputPasswordFlow from the parent component.",
@@ -395,11 +398,30 @@ export class InputPasswordComponent implements OnInit {
* (a) passed down the wrong InputPasswordFlow, or
* (b) passed down the correct InputPasswordFlow but failed to pass down a userId
*/
- if (this.flow !== InputPasswordFlow.AccountRegistration) {
+ if (
+ this.flow !== InputPasswordFlow.AccountRegistration &&
+ this.flow !== InputPasswordFlow.ChangePasswordDelegation
+ ) {
if (!this.userId) {
throw new Error("The selected InputPasswordFlow requires that a userId be passed down");
}
}
+
+ if (this.flow === InputPasswordFlow.ChangePasswordDelegation) {
+ if (this.email) {
+ throw new Error(
+ "There should be no email in a ChangePasswordDelegation flow. Please pass down the appropriate InputPasswordFlow from the parent component.",
+ );
+ }
+ }
+
+ if (this.flow !== InputPasswordFlow.ChangePasswordDelegation) {
+ if (!this.email) {
+ throw new Error(
+ "The selected InputPasswordFlow requires that an email be passed down to create a master key.",
+ );
+ }
+ }
}
/**