1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-09-30 04:28:19 +02:00

[PM-2417] Update LoginApprovalComponent on Desktop (#6751)

* [PM-2417] convert modal to dialog service

* code format

* [PM-2417] Fix title

* [PM-2417] Remove unnecessary class

* Updated to use a local reference for the dialog.

* Changes to clarify the method naming

* More cleanup with Will.

* Removed unused style

---------

Co-authored-by: Todd Martin <tmartin@bitwarden.com>
Co-authored-by: Jared Snider <116684653+JaredSnider-Bitwarden@users.noreply.github.com>
Co-authored-by: Todd Martin <106564991+trmartin4@users.noreply.github.com>
This commit is contained in:
André Bispo 2024-01-05 19:46:57 +00:00 committed by GitHub
parent 167648e213
commit 2a338319ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 89 additions and 84 deletions

View File

@ -399,7 +399,11 @@ export class AppComponent implements OnInit, OnDestroy {
break;
case "openLoginApproval":
if (message.notificationId != null) {
await this.openLoginApproval(message.notificationId);
this.dialogService.closeAll();
const dialogRef = LoginApprovalComponent.open(this.dialogService, {
notificationId: message.notificationId,
});
await firstValueFrom(dialogRef.closed);
}
break;
case "redrawMenu":
@ -473,19 +477,6 @@ export class AppComponent implements OnInit, OnDestroy {
});
}
async openLoginApproval(notificationId: string) {
this.modalService.closeAll();
this.modal = await this.modalService.open(LoginApprovalComponent, {
data: { notificationId: notificationId },
});
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
this.modal.onClosed.subscribe(() => {
this.modal = null;
});
}
private async updateAppMenu() {
let updateRequest: MenuUpdateRequest;
const stateAccounts = await firstValueFrom(this.stateService.accounts$);

View File

@ -14,7 +14,6 @@ import { DeleteAccountComponent } from "../auth/delete-account.component";
import { EnvironmentComponent } from "../auth/environment.component";
import { HintComponent } from "../auth/hint.component";
import { LockComponent } from "../auth/lock.component";
import { LoginApprovalComponent } from "../auth/login/login-approval.component";
import { LoginModule } from "../auth/login/login.module";
import { RegisterComponent } from "../auth/register.component";
import { RemovePasswordComponent } from "../auth/remove-password.component";
@ -101,7 +100,6 @@ import { SendComponent } from "./tools/send/send.component";
VaultTimeoutInputComponent,
ViewComponent,
ViewCustomFieldsComponent,
LoginApprovalComponent,
],
bootstrap: [AppComponent],
})

View File

@ -1,43 +1,42 @@
<div class="modal fade" role="dialog" aria-modal="true" aria-labelledby="loginApprovalTitle">
<div class="modal-dialog modal-md" role="document">
<div id="login-approval-page" class="modal-content">
<div class="section-title">
<p style="text-transform: uppercase">{{ "areYouTryingtoLogin" | i18n }}</p>
</div>
<div class="content">
<div class="section">
<h4>{{ "logInAttemptBy" | i18n: email }}</h4>
</div>
<div class="section">
<h4 class="label">{{ "fingerprintPhraseHeader" | i18n }}</h4>
<code>{{ fingerprintPhrase }}</code>
</div>
<div class="section">
<h4 class="label">{{ "deviceType" | i18n }}</h4>
<p>{{ authRequestResponse?.requestDeviceType }}</p>
</div>
<div class="section">
<h4 class="label">{{ "ipAddress" | i18n }}</h4>
<p>{{ authRequestResponse?.requestIpAddress }}</p>
</div>
<div class="section">
<h4 class="label">{{ "time" | i18n }}</h4>
<p>{{ requestTimeText }}</p>
</div>
</div>
<div class="modal-footer">
<button type="button" class="primary" (click)="approveLogin(true, true)">
{{ "confirmLogIn" | i18n }}
</button>
<button type="button" (click)="approveLogin(false, true)">
{{ "denyLogIn" | i18n }}
</button>
</div>
<bit-dialog>
<span bitDialogTitle>{{ "areYouTryingtoLogin" | i18n }}</span>
<ng-container bitDialogContent>
<h4>{{ "logInAttemptBy" | i18n: email }}</h4>
<div>
<b>{{ "fingerprintPhraseHeader" | i18n }}</b>
<p class="tw-text-code">{{ fingerprintPhrase }}</p>
</div>
</div>
</div>
<div>
<b>{{ "deviceType" | i18n }}</b>
<p>{{ authRequestResponse?.requestDeviceType }}</p>
</div>
<div>
<b>{{ "ipAddress" | i18n }}</b>
<p>{{ authRequestResponse?.requestIpAddress }}</p>
</div>
<div>
<b>{{ "time" | i18n }}</b>
<p>{{ requestTimeText }}</p>
</div>
</ng-container>
<ng-container bitDialogFooter>
<button
bitButton
type="button"
buttonType="primary"
[bitAction]="approveLogin"
[bitDialogClose]="true"
>
{{ "confirmLogIn" | i18n }}
</button>
<button
bitButton
type="button"
buttonType="secondary"
[bitAction]="denyLogin"
[bitDialogClose]="true"
>
{{ "denyLogIn" | i18n }}
</button>
</ng-container>
</bit-dialog>

View File

@ -1,8 +1,9 @@
import { Component, OnInit, OnDestroy } from "@angular/core";
import { Subject } from "rxjs";
import { DIALOG_DATA, DialogRef } from "@angular/cdk/dialog";
import { CommonModule } from "@angular/common";
import { Component, OnInit, OnDestroy, Inject } from "@angular/core";
import { Subject, firstValueFrom } from "rxjs";
import { ModalRef } from "@bitwarden/angular/components/modal/modal.ref";
import { ModalConfig } from "@bitwarden/angular/services/modal.service";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response";
@ -12,13 +13,25 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import {
AsyncActionsModule,
ButtonModule,
DialogModule,
DialogService,
} from "@bitwarden/components";
const RequestTimeOut = 60000 * 15; //15 Minutes
const RequestTimeUpdate = 60000 * 5; //5 Minutes
export interface LoginApprovalDialogParams {
notificationId: string;
}
@Component({
selector: "login-approval",
templateUrl: "login-approval.component.html",
standalone: true,
imports: [CommonModule, AsyncActionsModule, ButtonModule, DialogModule, JslibModule],
})
export class LoginApprovalComponent implements OnInit, OnDestroy {
notificationId: string;
@ -30,9 +43,9 @@ export class LoginApprovalComponent implements OnInit, OnDestroy {
authRequestResponse: AuthRequestResponse;
interval: NodeJS.Timeout;
requestTimeText: string;
dismissModal: boolean;
constructor(
@Inject(DIALOG_DATA) private params: LoginApprovalDialogParams,
protected stateService: StateService,
protected platformUtilsService: PlatformUtilsService,
protected i18nService: I18nService,
@ -40,23 +53,17 @@ export class LoginApprovalComponent implements OnInit, OnDestroy {
protected authService: AuthService,
protected appIdService: AppIdService,
protected cryptoService: CryptoService,
private modalRef: ModalRef,
config: ModalConfig,
private dialogRef: DialogRef,
) {
this.notificationId = config.data.notificationId;
this.dismissModal = true;
this.modalRef.onClosed
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
.subscribe(() => {
if (this.dismissModal) {
this.approveLogin(false, false);
}
});
this.notificationId = params.notificationId;
}
ngOnDestroy(): void {
async ngOnDestroy(): Promise<void> {
clearInterval(this.interval);
const closedWithButton = await firstValueFrom(this.dialogRef.closed);
if (!closedWithButton) {
this.retrieveAuthRequestAndRespond(false);
}
this.destroy$.next();
this.destroy$.complete();
}
@ -86,14 +93,24 @@ export class LoginApprovalComponent implements OnInit, OnDestroy {
}
}
async approveLogin(approveLogin: boolean, approveDenyButtonClicked: boolean) {
clearInterval(this.interval);
/**
* Strongly-typed helper to open a LoginApprovalDialog
* @param dialogService Instance of the dialog service that will be used to open the dialog
* @param data Configuration for the dialog
*/
static open(dialogService: DialogService, data: LoginApprovalDialogParams) {
return dialogService.open(LoginApprovalComponent, { data });
}
this.dismissModal = !approveDenyButtonClicked;
if (approveDenyButtonClicked) {
this.modalRef.close();
}
denyLogin = async () => {
await this.retrieveAuthRequestAndRespond(false);
};
approveLogin = async () => {
await this.retrieveAuthRequestAndRespond(true);
};
private async retrieveAuthRequestAndRespond(approve: boolean) {
this.authRequestResponse = await this.apiService.getAuthRequest(this.notificationId);
if (this.authRequestResponse.requestApproved || this.authRequestResponse.responseDate != null) {
this.platformUtilsService.showToast(
@ -105,7 +122,7 @@ export class LoginApprovalComponent implements OnInit, OnDestroy {
const loginResponse = await this.authService.passwordlessLogin(
this.authRequestResponse.id,
this.authRequestResponse.publicKey,
approveLogin,
approve,
);
this.showResultToast(loginResponse);
}
@ -165,7 +182,7 @@ export class LoginApprovalComponent implements OnInit, OnDestroy {
);
} else {
clearInterval(this.interval);
this.modalRef.close();
this.dialogRef.close();
this.platformUtilsService.showToast(
"info",
null,