From ab83e822f751ee6f78317d6905a6345f7ba996b2 Mon Sep 17 00:00:00 2001 From: vinith-kovan <156108204+vinith-kovan@users.noreply.github.com> Date: Wed, 26 Jun 2024 23:25:42 +0530 Subject: [PATCH] [PM-2056] update two factor duo dialog (#8976) * migrating two factor duo component * migrating two factor duo component * two factor duo component migration * two factor duo component migration * removed null check from two-factor-setup * cleanup duo changes * remove ikey and skey references * clean up --------- Co-authored-by: Jake Fink --- .../settings/two-factor-setup.component.ts | 21 ++- .../settings/two-factor-duo.component.html | 156 +++++++----------- .../auth/settings/two-factor-duo.component.ts | 99 ++++++++--- .../settings/two-factor-setup.component.ts | 7 +- 4 files changed, 144 insertions(+), 139 deletions(-) diff --git a/apps/web/src/app/admin-console/organizations/settings/two-factor-setup.component.ts b/apps/web/src/app/admin-console/organizations/settings/two-factor-setup.component.ts index c95ff754c45..b0dd6c63a77 100644 --- a/apps/web/src/app/admin-console/organizations/settings/two-factor-setup.component.ts +++ b/apps/web/src/app/admin-console/organizations/settings/two-factor-setup.component.ts @@ -1,6 +1,6 @@ import { Component } from "@angular/core"; import { ActivatedRoute } from "@angular/router"; -import { concatMap, takeUntil, map } from "rxjs"; +import { concatMap, takeUntil, map, lastValueFrom } from "rxjs"; import { tap } from "rxjs/operators"; import { ModalService } from "@bitwarden/angular/services/modal.service"; @@ -16,6 +16,7 @@ import { DialogService } from "@bitwarden/components"; import { TwoFactorDuoComponent } from "../../../auth/settings/two-factor-duo.component"; import { TwoFactorSetupComponent as BaseTwoFactorSetupComponent } from "../../../auth/settings/two-factor-setup.component"; +import { TwoFactorVerifyComponent } from "../../../auth/settings/two-factor-verify.component"; @Component({ selector: "app-two-factor-setup", @@ -65,21 +66,19 @@ export class TwoFactorSetupComponent extends BaseTwoFactorSetupComponent { async manage(type: TwoFactorProviderType) { switch (type) { case TwoFactorProviderType.OrganizationDuo: { - const result: AuthResponse = await this.callTwoFactorVerifyDialog( - TwoFactorProviderType.OrganizationDuo, + const twoFactorVerifyDialogRef = TwoFactorVerifyComponent.open(this.dialogService, { + data: { type: type, organizationId: this.organizationId }, + }); + const result: AuthResponse = await lastValueFrom( + twoFactorVerifyDialogRef.closed, ); - if (!result) { return; } + const duoComp = TwoFactorDuoComponent.open(this.dialogService, { data: result }); + const enabled: boolean = await lastValueFrom(duoComp.closed); + this.updateStatus(enabled, TwoFactorProviderType.Duo); - const duoComp = await this.openModal(this.duoModalRef, TwoFactorDuoComponent); - duoComp.type = TwoFactorProviderType.OrganizationDuo; - duoComp.organizationId = this.organizationId; - duoComp.auth(result); - duoComp.onUpdated.pipe(takeUntil(this.destroy$)).subscribe((enabled: boolean) => { - this.updateStatus(enabled, TwoFactorProviderType.OrganizationDuo); - }); break; } default: diff --git a/apps/web/src/app/auth/settings/two-factor-duo.component.html b/apps/web/src/app/auth/settings/two-factor-duo.component.html index 23ccaf6bde2..6c733ed798a 100644 --- a/apps/web/src/app/auth/settings/two-factor-duo.component.html +++ b/apps/web/src/app/auth/settings/two-factor-duo.component.html @@ -1,98 +1,58 @@ - +
+ + + {{ "twoStepLogin" | i18n }} + Duo + + + + + {{ "twoStepLoginProviderEnabled" | i18n }} + + Duo logo + {{ "twoFactorDuoClientId" | i18n }}: {{ clientId }} +
+ {{ "twoFactorDuoClientSecret" | i18n }}: {{ clientSecret }} +
+ {{ "twoFactorDuoApiHostname" | i18n }}: {{ host }} +
+ + Duo logo +

{{ "twoFactorDuoDesc" | i18n }}

+ + {{ "twoFactorDuoClientId" | i18n }} + + + + {{ "twoFactorDuoClientSecret" | i18n }} + + + + {{ "twoFactorDuoApiHostname" | i18n }} + + +
+
+ + + + +
+
diff --git a/apps/web/src/app/auth/settings/two-factor-duo.component.ts b/apps/web/src/app/auth/settings/two-factor-duo.component.ts index 9a1ec6c4da6..c3ed10ed87b 100644 --- a/apps/web/src/app/auth/settings/two-factor-duo.component.ts +++ b/apps/web/src/app/auth/settings/two-factor-duo.component.ts @@ -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 { ApiService } from "@bitwarden/common/abstractions/api.service"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; @@ -18,21 +20,26 @@ import { TwoFactorBaseComponent } from "./two-factor-base.component"; templateUrl: "two-factor-duo.component.html", }) export class TwoFactorDuoComponent extends TwoFactorBaseComponent { - type = TwoFactorProviderType.Duo; - clientId: string; - clientSecret: string; - host: string; - formPromise: Promise; + @Output() onChangeStatus: EventEmitter = new EventEmitter(); + type = TwoFactorProviderType.Duo; + formGroup = this.formBuilder.group({ + clientId: ["", [Validators.required]], + clientSecret: ["", [Validators.required]], + host: ["", [Validators.required]], + }); override componentName = "app-two-factor-duo"; constructor( + @Inject(DIALOG_DATA) protected data: AuthResponse, apiService: ApiService, i18nService: I18nService, platformUtilsService: PlatformUtilsService, logService: LogService, userVerificationService: UserVerificationService, dialogService: DialogService, + private formBuilder: FormBuilder, + private dialogRef: DialogRef, ) { super( apiService, @@ -44,43 +51,81 @@ export class TwoFactorDuoComponent extends TwoFactorBaseComponent { ); } - auth(authResponse: AuthResponse) { - super.auth(authResponse); - this.processResponse(authResponse.response); + get clientId() { + return this.formGroup.get("clientId").value; + } + get clientSecret() { + return this.formGroup.get("clientSecret").value; + } + get host() { + return this.formGroup.get("host").value; + } + set clientId(value: string) { + this.formGroup.get("clientId").setValue(value); + } + set clientSecret(value: string) { + this.formGroup.get("clientSecret").setValue(value); + } + set host(value: string) { + this.formGroup.get("host").setValue(value); } - submit() { - if (this.enabled) { - return super.disable(this.formPromise); - } else { - return this.enable(); - } + async ngOnInit() { + super.auth(this.data); + this.processResponse(this.data.response); } + submit = async () => { + this.formGroup.markAllAsTouched(); + if (this.formGroup.invalid) { + return; + } + if (this.enabled) { + await this.disableMethod(); + } else { + await this.enable(); + } + this.onChangeStatus.emit(this.enabled); + }; + protected async enable() { const request = await this.buildRequestModel(UpdateTwoFactorDuoRequest); request.clientId = this.clientId; request.clientSecret = this.clientSecret; request.host = this.host; - return super.enable(async () => { - if (this.organizationId != null) { - this.formPromise = this.apiService.putTwoFactorOrganizationDuo( - this.organizationId, - request, - ); - } else { - this.formPromise = this.apiService.putTwoFactorDuo(request); - } - const response = await this.formPromise; - await this.processResponse(response); - }); + let response: TwoFactorDuoResponse; + + if (this.organizationId != null) { + response = await this.apiService.putTwoFactorOrganizationDuo(this.organizationId, request); + } else { + response = await this.apiService.putTwoFactorDuo(request); + } + + this.processResponse(response); + this.onUpdated.emit(true); } + onClose = () => { + this.dialogRef.close(this.enabled); + }; + private processResponse(response: TwoFactorDuoResponse) { this.clientId = response.clientId; this.clientSecret = response.clientSecret; this.host = response.host; this.enabled = response.enabled; } + + /** + * Strongly typed helper to open a TwoFactorDuoComponentComponent + * @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>, + ) => { + return dialogService.open(TwoFactorDuoComponent, config); + }; } diff --git a/apps/web/src/app/auth/settings/two-factor-setup.component.ts b/apps/web/src/app/auth/settings/two-factor-setup.component.ts index 10f113d4963..d60153230b0 100644 --- a/apps/web/src/app/auth/settings/two-factor-setup.component.ts +++ b/apps/web/src/app/auth/settings/two-factor-setup.component.ts @@ -161,9 +161,10 @@ export class TwoFactorSetupComponent implements OnInit, OnDestroy { if (!result) { return; } - const duoComp = await this.openModal(this.duoModalRef, TwoFactorDuoComponent); - duoComp.auth(result); - duoComp.onUpdated.pipe(takeUntil(this.destroy$)).subscribe((enabled: boolean) => { + const duoComp: DialogRef = TwoFactorDuoComponent.open(this.dialogService, { + data: result, + }); + duoComp.componentInstance.onChangeStatus.subscribe((enabled: boolean) => { this.updateStatus(enabled, TwoFactorProviderType.Duo); }); break;