fix duo subscriptions and org vs individual duo setup (#9859)
This commit is contained in:
parent
ed9d82ef1e
commit
a5e7fde413
|
@ -1,7 +1,8 @@
|
||||||
|
import { DialogRef } from "@angular/cdk/dialog";
|
||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
import { ActivatedRoute } from "@angular/router";
|
import { ActivatedRoute } from "@angular/router";
|
||||||
import { concatMap, takeUntil, map, lastValueFrom } from "rxjs";
|
import { concatMap, takeUntil, map, lastValueFrom } from "rxjs";
|
||||||
import { tap } from "rxjs/operators";
|
import { first, tap } from "rxjs/operators";
|
||||||
|
|
||||||
import { ModalService } from "@bitwarden/angular/services/modal.service";
|
import { ModalService } from "@bitwarden/angular/services/modal.service";
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
|
@ -64,6 +65,9 @@ export class TwoFactorSetupComponent extends BaseTwoFactorSetupComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
async manage(type: TwoFactorProviderType) {
|
async manage(type: TwoFactorProviderType) {
|
||||||
|
// clear any existing subscriptions before creating a new one
|
||||||
|
this.twoFactorSetupSubscription?.unsubscribe();
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case TwoFactorProviderType.OrganizationDuo: {
|
case TwoFactorProviderType.OrganizationDuo: {
|
||||||
const twoFactorVerifyDialogRef = TwoFactorVerifyComponent.open(this.dialogService, {
|
const twoFactorVerifyDialogRef = TwoFactorVerifyComponent.open(this.dialogService, {
|
||||||
|
@ -75,9 +79,18 @@ export class TwoFactorSetupComponent extends BaseTwoFactorSetupComponent {
|
||||||
if (!result) {
|
if (!result) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const duoComp = TwoFactorDuoComponent.open(this.dialogService, { data: result });
|
const duoComp: DialogRef<boolean, any> = TwoFactorDuoComponent.open(this.dialogService, {
|
||||||
const enabled: boolean = await lastValueFrom(duoComp.closed);
|
data: {
|
||||||
this.updateStatus(enabled, TwoFactorProviderType.Duo);
|
authResponse: result,
|
||||||
|
organizationId: this.organizationId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this.twoFactorSetupSubscription = duoComp.componentInstance.onChangeStatus
|
||||||
|
.pipe(first(), takeUntil(this.destroy$))
|
||||||
|
.subscribe((enabled: boolean) => {
|
||||||
|
duoComp.close();
|
||||||
|
this.updateStatus(enabled, TwoFactorProviderType.OrganizationDuo);
|
||||||
|
});
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ export class TwoFactorDuoComponent extends TwoFactorBaseComponent {
|
||||||
override componentName = "app-two-factor-duo";
|
override componentName = "app-two-factor-duo";
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DIALOG_DATA) protected data: AuthResponse<TwoFactorDuoResponse>,
|
@Inject(DIALOG_DATA) protected data: TwoFactorDuoComponentConfig,
|
||||||
apiService: ApiService,
|
apiService: ApiService,
|
||||||
i18nService: I18nService,
|
i18nService: I18nService,
|
||||||
platformUtilsService: PlatformUtilsService,
|
platformUtilsService: PlatformUtilsService,
|
||||||
|
@ -71,8 +71,17 @@ export class TwoFactorDuoComponent extends TwoFactorBaseComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
super.auth(this.data);
|
if (!this.data?.authResponse) {
|
||||||
this.processResponse(this.data.response);
|
throw Error("TwoFactorDuoComponent requires a TwoFactorDuoResponse to initialize");
|
||||||
|
}
|
||||||
|
|
||||||
|
super.auth(this.data.authResponse);
|
||||||
|
this.processResponse(this.data.authResponse.response);
|
||||||
|
|
||||||
|
if (this.data.organizationId) {
|
||||||
|
this.type = TwoFactorProviderType.OrganizationDuo;
|
||||||
|
this.organizationId = this.data.organizationId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
submit = async () => {
|
submit = async () => {
|
||||||
|
@ -124,8 +133,13 @@ export class TwoFactorDuoComponent extends TwoFactorBaseComponent {
|
||||||
*/
|
*/
|
||||||
static open = (
|
static open = (
|
||||||
dialogService: DialogService,
|
dialogService: DialogService,
|
||||||
config: DialogConfig<AuthResponse<TwoFactorDuoResponse>>,
|
config: DialogConfig<TwoFactorDuoComponentConfig>,
|
||||||
) => {
|
) => {
|
||||||
return dialogService.open<boolean>(TwoFactorDuoComponent, config);
|
return dialogService.open<boolean>(TwoFactorDuoComponent, config);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TwoFactorDuoComponentConfig = {
|
||||||
|
authResponse: AuthResponse<TwoFactorDuoResponse>;
|
||||||
|
organizationId?: string;
|
||||||
|
};
|
||||||
|
|
|
@ -1,6 +1,14 @@
|
||||||
import { DialogRef } from "@angular/cdk/dialog";
|
import { DialogRef } from "@angular/cdk/dialog";
|
||||||
import { Component, OnDestroy, OnInit, Type, ViewChild, ViewContainerRef } from "@angular/core";
|
import { Component, OnDestroy, OnInit, Type, ViewChild, ViewContainerRef } from "@angular/core";
|
||||||
import { firstValueFrom, lastValueFrom, Observable, Subject, takeUntil } from "rxjs";
|
import {
|
||||||
|
first,
|
||||||
|
firstValueFrom,
|
||||||
|
lastValueFrom,
|
||||||
|
Observable,
|
||||||
|
Subject,
|
||||||
|
Subscription,
|
||||||
|
takeUntil,
|
||||||
|
} from "rxjs";
|
||||||
|
|
||||||
import { ModalRef } from "@bitwarden/angular/components/modal/modal.ref";
|
import { ModalRef } from "@bitwarden/angular/components/modal/modal.ref";
|
||||||
import { ModalService } from "@bitwarden/angular/services/modal.service";
|
import { ModalService } from "@bitwarden/angular/services/modal.service";
|
||||||
|
@ -36,9 +44,6 @@ import { TwoFactorYubiKeyComponent } from "./two-factor-yubikey.component";
|
||||||
export class TwoFactorSetupComponent implements OnInit, OnDestroy {
|
export class TwoFactorSetupComponent implements OnInit, OnDestroy {
|
||||||
@ViewChild("yubikeyTemplate", { read: ViewContainerRef, static: true })
|
@ViewChild("yubikeyTemplate", { read: ViewContainerRef, static: true })
|
||||||
yubikeyModalRef: ViewContainerRef;
|
yubikeyModalRef: ViewContainerRef;
|
||||||
@ViewChild("duoTemplate", { read: ViewContainerRef, static: true }) duoModalRef: ViewContainerRef;
|
|
||||||
@ViewChild("emailTemplate", { read: ViewContainerRef, static: true })
|
|
||||||
emailModalRef: ViewContainerRef;
|
|
||||||
|
|
||||||
organizationId: string;
|
organizationId: string;
|
||||||
organization: Organization;
|
organization: Organization;
|
||||||
|
@ -53,6 +58,7 @@ export class TwoFactorSetupComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
protected destroy$ = new Subject<void>();
|
protected destroy$ = new Subject<void>();
|
||||||
private twoFactorAuthPolicyAppliesToActiveUser: boolean;
|
private twoFactorAuthPolicyAppliesToActiveUser: boolean;
|
||||||
|
protected twoFactorSetupSubscription: Subscription;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected dialogService: DialogService,
|
protected dialogService: DialogService,
|
||||||
|
@ -126,6 +132,9 @@ export class TwoFactorSetupComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
async manage(type: TwoFactorProviderType) {
|
async manage(type: TwoFactorProviderType) {
|
||||||
|
// clear any existing subscriptions before creating a new one
|
||||||
|
this.twoFactorSetupSubscription?.unsubscribe();
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case TwoFactorProviderType.Authenticator: {
|
case TwoFactorProviderType.Authenticator: {
|
||||||
const result: AuthResponse<TwoFactorAuthenticatorResponse> =
|
const result: AuthResponse<TwoFactorAuthenticatorResponse> =
|
||||||
|
@ -137,7 +146,10 @@ export class TwoFactorSetupComponent implements OnInit, OnDestroy {
|
||||||
this.dialogService,
|
this.dialogService,
|
||||||
{ data: result },
|
{ data: result },
|
||||||
);
|
);
|
||||||
authComp.componentInstance.onChangeStatus.subscribe((enabled: boolean) => {
|
this.twoFactorSetupSubscription = authComp.componentInstance.onChangeStatus
|
||||||
|
.pipe(first(), takeUntil(this.destroy$))
|
||||||
|
.subscribe((enabled: boolean) => {
|
||||||
|
authComp.close();
|
||||||
this.updateStatus(enabled, TwoFactorProviderType.Authenticator);
|
this.updateStatus(enabled, TwoFactorProviderType.Authenticator);
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
@ -150,7 +162,9 @@ export class TwoFactorSetupComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
const yubiComp = await this.openModal(this.yubikeyModalRef, TwoFactorYubiKeyComponent);
|
const yubiComp = await this.openModal(this.yubikeyModalRef, TwoFactorYubiKeyComponent);
|
||||||
yubiComp.auth(result);
|
yubiComp.auth(result);
|
||||||
yubiComp.onUpdated.pipe(takeUntil(this.destroy$)).subscribe((enabled: boolean) => {
|
this.twoFactorSetupSubscription = yubiComp.onUpdated
|
||||||
|
.pipe(first(), takeUntil(this.destroy$))
|
||||||
|
.subscribe((enabled: boolean) => {
|
||||||
this.updateStatus(enabled, TwoFactorProviderType.Yubikey);
|
this.updateStatus(enabled, TwoFactorProviderType.Yubikey);
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
@ -162,9 +176,14 @@ export class TwoFactorSetupComponent implements OnInit, OnDestroy {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const duoComp: DialogRef<boolean, any> = TwoFactorDuoComponent.open(this.dialogService, {
|
const duoComp: DialogRef<boolean, any> = TwoFactorDuoComponent.open(this.dialogService, {
|
||||||
data: result,
|
data: {
|
||||||
|
authResponse: result,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
duoComp.componentInstance.onChangeStatus.subscribe((enabled: boolean) => {
|
this.twoFactorSetupSubscription = duoComp.componentInstance.onChangeStatus
|
||||||
|
.pipe(first(), takeUntil(this.destroy$))
|
||||||
|
.subscribe((enabled: boolean) => {
|
||||||
|
duoComp.close();
|
||||||
this.updateStatus(enabled, TwoFactorProviderType.Duo);
|
this.updateStatus(enabled, TwoFactorProviderType.Duo);
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
@ -175,12 +194,16 @@ export class TwoFactorSetupComponent implements OnInit, OnDestroy {
|
||||||
if (!result) {
|
if (!result) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const authComp: DialogRef<boolean, any> = TwoFactorEmailComponent.open(this.dialogService, {
|
const emailComp: DialogRef<boolean, any> = TwoFactorEmailComponent.open(
|
||||||
|
this.dialogService,
|
||||||
|
{
|
||||||
data: result,
|
data: result,
|
||||||
});
|
},
|
||||||
authComp.componentInstance.onChangeStatus
|
);
|
||||||
.pipe(takeUntil(this.destroy$))
|
this.twoFactorSetupSubscription = emailComp.componentInstance.onChangeStatus
|
||||||
|
.pipe(first(), takeUntil(this.destroy$))
|
||||||
.subscribe((enabled: boolean) => {
|
.subscribe((enabled: boolean) => {
|
||||||
|
emailComp.close();
|
||||||
this.updateStatus(enabled, TwoFactorProviderType.Email);
|
this.updateStatus(enabled, TwoFactorProviderType.Email);
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
@ -195,7 +218,10 @@ export class TwoFactorSetupComponent implements OnInit, OnDestroy {
|
||||||
this.dialogService,
|
this.dialogService,
|
||||||
{ data: result },
|
{ data: result },
|
||||||
);
|
);
|
||||||
webAuthnComp.componentInstance.onChangeStatus.subscribe((enabled: boolean) => {
|
this.twoFactorSetupSubscription = webAuthnComp.componentInstance.onChangeStatus
|
||||||
|
.pipe(first(), takeUntil(this.destroy$))
|
||||||
|
.subscribe((enabled: boolean) => {
|
||||||
|
webAuthnComp.close();
|
||||||
this.updateStatus(enabled, TwoFactorProviderType.WebAuthn);
|
this.updateStatus(enabled, TwoFactorProviderType.WebAuthn);
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Reference in New Issue