[PM-2057] update two factor email dialog (#8974)
* migrating two factor email component * two factor email component migration * two factor email component migration * two factor email component migration
This commit is contained in:
parent
419c107f87
commit
24fb3f71f1
|
@ -1,101 +1,53 @@
|
|||
<div class="modal fade" role="dialog" aria-modal="true" aria-labelledby="2faEmailTitle">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title" id="2faEmailTitle">
|
||||
{{ "twoStepLogin" | i18n }}
|
||||
<small>{{ "emailTitle" | i18n }}</small>
|
||||
</h1>
|
||||
<button
|
||||
type="button"
|
||||
class="close"
|
||||
data-dismiss="modal"
|
||||
appA11yTitle="{{ 'close' | i18n }}"
|
||||
>
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form
|
||||
#form
|
||||
(ngSubmit)="submit()"
|
||||
[appApiAction]="formPromise"
|
||||
ngNativeValidate
|
||||
*ngIf="authed"
|
||||
>
|
||||
<div class="modal-body">
|
||||
<ng-container *ngIf="enabled">
|
||||
<app-callout type="success" title="{{ 'enabled' | i18n }}" icon="bwi bwi-check-circle">
|
||||
{{ "twoStepLoginProviderEnabled" | i18n }}
|
||||
</app-callout>
|
||||
<strong>{{ "email" | i18n }}:</strong> {{ email }}
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!enabled">
|
||||
<p class="d-flex">
|
||||
<span class="mr-3">{{ "twoFactorEmailDesc" | i18n }}</span>
|
||||
<img class="float-right ml-auto mfaType1" alt="Email logo" />
|
||||
</p>
|
||||
<div class="form-group">
|
||||
<label for="email">1. {{ "twoFactorEmailEnterEmail" | i18n }}</label>
|
||||
<input
|
||||
id="email"
|
||||
type="text"
|
||||
name="Email"
|
||||
class="form-control"
|
||||
[(ngModel)]="email"
|
||||
required
|
||||
inputmode="email"
|
||||
appInputVerbatim="false"
|
||||
/>
|
||||
</div>
|
||||
<div class="mb-3 d-flex">
|
||||
<button
|
||||
#sendBtn
|
||||
type="button"
|
||||
class="btn btn-outline-primary btn-sm btn-submit align-self-start"
|
||||
(click)="sendEmail()"
|
||||
[appApiAction]="emailPromise"
|
||||
[disabled]="$any(sendBtn).loading"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-spin"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span>{{ "sendEmail" | i18n }}</span>
|
||||
</button>
|
||||
<span class="text-success ml-3" *ngIf="sentEmail">
|
||||
{{ "verificationCodeEmailSent" | i18n: sentEmail }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="token">2. {{ "twoFactorEmailEnterCode" | i18n }}</label>
|
||||
<input
|
||||
id="token"
|
||||
type="text"
|
||||
name="Token"
|
||||
class="form-control"
|
||||
[(ngModel)]="token"
|
||||
required
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary btn-submit" [disabled]="form.loading">
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-spin"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span *ngIf="!enabled">{{ "enable" | i18n }}</span>
|
||||
<span *ngIf="enabled">{{ "disable" | i18n }}</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">
|
||||
{{ "close" | i18n }}
|
||||
<form [formGroup]="formGroup" [bitSubmit]="submit" *ngIf="authed">
|
||||
<bit-dialog>
|
||||
<span bitDialogTitle>
|
||||
{{ "twoStepLogin" | i18n }}
|
||||
<span bitTypography="body1">{{ "emailTitle" | i18n }}</span>
|
||||
</span>
|
||||
<ng-container bitDialogContent>
|
||||
<ng-container *ngIf="enabled">
|
||||
<app-callout type="success" title="{{ 'enabled' | i18n }}" icon="bwi bwi-check-circle">
|
||||
{{ "twoStepLoginProviderEnabled" | i18n }}
|
||||
</app-callout>
|
||||
<strong>{{ "email" | i18n }}:</strong> {{ email }}
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!enabled">
|
||||
<p class="tw-flex">
|
||||
<span class="tw-mr-3">{{ "twoFactorEmailDesc" | i18n }}</span>
|
||||
<img class="tw-float-right tw-ml-auto mfaType1" alt="Email logo" />
|
||||
</p>
|
||||
<bit-form-field>
|
||||
<bit-label>1. {{ "twoFactorEmailEnterEmail" | i18n }}</bit-label>
|
||||
<input
|
||||
bitInput
|
||||
type="text"
|
||||
formControlName="email"
|
||||
inputmode="email"
|
||||
appInputVerbatim="false"
|
||||
/>
|
||||
</bit-form-field>
|
||||
<div class="tw-mb-3 tw-flex">
|
||||
<button bitButton type="button" buttonType="primary" [bitAction]="sendEmail">
|
||||
{{ "sendEmail" | i18n }}
|
||||
</button>
|
||||
<span class="tw-text-success tw-ml-3" *ngIf="sentEmail">
|
||||
{{ "verificationCodeEmailSent" | i18n: sentEmail }}
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<bit-form-field>
|
||||
<bit-label>2. {{ "twoFactorEmailEnterCode" | i18n }}</bit-label>
|
||||
<input bitInput type="text" formControlName="token" appInputVerbatim />
|
||||
</bit-form-field>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
<ng-container bitDialogFooter>
|
||||
<button bitButton bitFormButton type="submit" buttonType="primary">
|
||||
<span *ngIf="!enabled">{{ "enable" | i18n }}</span>
|
||||
<span *ngIf="enabled">{{ "disable" | i18n }}</span>
|
||||
</button>
|
||||
<button bitButton bitFormButton type="button" buttonType="secondary" bitDialogClose>
|
||||
{{ "close" | i18n }}
|
||||
</button>
|
||||
</ng-container>
|
||||
</bit-dialog>
|
||||
</form>
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import { Component } from "@angular/core";
|
||||
import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog";
|
||||
import { Component, EventEmitter, Inject, Output } from "@angular/core";
|
||||
import { FormBuilder, Validators } from "@angular/forms";
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
|
@ -19,18 +21,22 @@ import { TwoFactorBaseComponent } from "./two-factor-base.component";
|
|||
@Component({
|
||||
selector: "app-two-factor-email",
|
||||
templateUrl: "two-factor-email.component.html",
|
||||
outputs: ["onUpdated"],
|
||||
})
|
||||
export class TwoFactorEmailComponent extends TwoFactorBaseComponent {
|
||||
@Output() onChangeStatus: EventEmitter<boolean> = new EventEmitter();
|
||||
type = TwoFactorProviderType.Email;
|
||||
email: string;
|
||||
token: string;
|
||||
sentEmail: string;
|
||||
formPromise: Promise<TwoFactorEmailResponse>;
|
||||
emailPromise: Promise<unknown>;
|
||||
|
||||
override componentName = "app-two-factor-email";
|
||||
formGroup = this.formBuilder.group({
|
||||
token: [null],
|
||||
email: ["", [Validators.email, Validators.required]],
|
||||
});
|
||||
|
||||
constructor(
|
||||
@Inject(DIALOG_DATA) protected data: AuthResponse<TwoFactorEmailResponse>,
|
||||
apiService: ApiService,
|
||||
i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
|
@ -38,6 +44,8 @@ export class TwoFactorEmailComponent extends TwoFactorBaseComponent {
|
|||
userVerificationService: UserVerificationService,
|
||||
private accountService: AccountService,
|
||||
dialogService: DialogService,
|
||||
private formBuilder: FormBuilder,
|
||||
private dialogRef: DialogRef,
|
||||
) {
|
||||
super(
|
||||
apiService,
|
||||
|
@ -48,31 +56,49 @@ export class TwoFactorEmailComponent extends TwoFactorBaseComponent {
|
|||
dialogService,
|
||||
);
|
||||
}
|
||||
get token() {
|
||||
return this.formGroup.get("token").value;
|
||||
}
|
||||
set token(value: string) {
|
||||
this.formGroup.get("token").setValue(value);
|
||||
}
|
||||
get email() {
|
||||
return this.formGroup.get("email").value;
|
||||
}
|
||||
set email(value: string) {
|
||||
this.formGroup.get("email").setValue(value);
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
await this.auth(this.data);
|
||||
}
|
||||
|
||||
auth(authResponse: AuthResponse<TwoFactorEmailResponse>) {
|
||||
super.auth(authResponse);
|
||||
return this.processResponse(authResponse.response);
|
||||
}
|
||||
|
||||
submit() {
|
||||
submit = async () => {
|
||||
if (this.enabled) {
|
||||
return super.disable(this.formPromise);
|
||||
await this.disableEmail();
|
||||
this.onChangeStatus.emit(false);
|
||||
} else {
|
||||
return this.enable();
|
||||
await this.enable();
|
||||
this.onChangeStatus.emit(true);
|
||||
}
|
||||
};
|
||||
|
||||
private disableEmail() {
|
||||
return super.disable(this.formPromise);
|
||||
}
|
||||
|
||||
async sendEmail() {
|
||||
try {
|
||||
const request = await this.buildRequestModel(TwoFactorEmailRequest);
|
||||
request.email = this.email;
|
||||
this.emailPromise = this.apiService.postTwoFactorEmailSetup(request);
|
||||
await this.emailPromise;
|
||||
this.sentEmail = this.email;
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
}
|
||||
}
|
||||
sendEmail = async () => {
|
||||
const request = await this.buildRequestModel(TwoFactorEmailRequest);
|
||||
request.email = this.email;
|
||||
this.emailPromise = this.apiService.postTwoFactorEmailSetup(request);
|
||||
await this.emailPromise;
|
||||
this.sentEmail = this.email;
|
||||
};
|
||||
|
||||
protected async enable() {
|
||||
const request = await this.buildRequestModel(UpdateTwoFactorEmailRequest);
|
||||
|
@ -86,6 +112,10 @@ export class TwoFactorEmailComponent extends TwoFactorBaseComponent {
|
|||
});
|
||||
}
|
||||
|
||||
onClose = () => {
|
||||
this.dialogRef.close(this.enabled);
|
||||
};
|
||||
|
||||
private async processResponse(response: TwoFactorEmailResponse) {
|
||||
this.token = null;
|
||||
this.email = response.email;
|
||||
|
@ -96,4 +126,15 @@ export class TwoFactorEmailComponent extends TwoFactorBaseComponent {
|
|||
);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Strongly typed helper to open a TwoFactorEmailComponentComponent
|
||||
* @param dialogService Instance of the dialog service that will be used to open the dialog
|
||||
* @param config Configuration for the dialog
|
||||
*/
|
||||
static open(
|
||||
dialogService: DialogService,
|
||||
config: DialogConfig<AuthResponse<TwoFactorEmailResponse>>,
|
||||
) {
|
||||
return dialogService.open<boolean>(TwoFactorEmailComponent, config);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { DialogRef } from "@angular/cdk/dialog";
|
||||
import { Component, OnDestroy, OnInit, Type, ViewChild, ViewContainerRef } from "@angular/core";
|
||||
import { firstValueFrom, lastValueFrom, Observable, Subject, takeUntil } from "rxjs";
|
||||
|
||||
|
@ -178,11 +179,14 @@ export class TwoFactorSetupComponent implements OnInit, OnDestroy {
|
|||
if (!result) {
|
||||
return;
|
||||
}
|
||||
const emailComp = await this.openModal(this.emailModalRef, TwoFactorEmailComponent);
|
||||
await emailComp.auth(result);
|
||||
emailComp.onUpdated.pipe(takeUntil(this.destroy$)).subscribe((enabled: boolean) => {
|
||||
this.updateStatus(enabled, TwoFactorProviderType.Email);
|
||||
const authComp: DialogRef<boolean, any> = TwoFactorEmailComponent.open(this.dialogService, {
|
||||
data: result,
|
||||
});
|
||||
authComp.componentInstance.onChangeStatus
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe((enabled: boolean) => {
|
||||
this.updateStatus(enabled, TwoFactorProviderType.Email);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case TwoFactorProviderType.WebAuthn: {
|
||||
|
|
Loading…
Reference in New Issue