@ -9,6 +9,7 @@ import {
lastValueFrom ,
Observable ,
Subject ,
zip ,
} from "rxjs" ;
import {
concatMap ,
@ -25,6 +26,7 @@ import {
} from "rxjs/operators" ;
import {
AutomaticUserConfirmationService ,
CollectionData ,
CollectionDetailsResponse ,
CollectionService ,
@ -54,7 +56,9 @@ import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service" ;
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billing-api.service.abstraction" ;
import { EventType } from "@bitwarden/common/enums" ;
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum" ;
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service" ;
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service" ;
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service" ;
import { LogService } from "@bitwarden/common/platform/abstractions/log.service" ;
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service" ;
@ -102,6 +106,11 @@ import {
getNestedCollectionTree ,
getFlatCollectionTree ,
} from "../../admin-console/organizations/collections" ;
import {
AutoConfirmPolicy ,
AutoConfirmPolicyDialogComponent ,
PolicyEditDialogResult ,
} from "../../admin-console/organizations/policies" ;
import {
CollectionDialogAction ,
CollectionDialogTabType ,
@ -213,6 +222,8 @@ export class VaultComponent<C extends CipherViewLike> implements OnInit, OnDestr
private destroy $ = new Subject < void > ( ) ;
private vaultItemDialogRef? : DialogRef < VaultItemDialogResult > | undefined ;
private autoConfirmDialogRef? : DialogRef < PolicyEditDialogResult > | undefined ;
protected showAddCipherBtn : boolean = false ;
organizations $ = this . accountService . activeAccount $
@ -328,6 +339,8 @@ export class VaultComponent<C extends CipherViewLike> implements OnInit, OnDestr
private policyService : PolicyService ,
private unifiedUpgradePromptService : UnifiedUpgradePromptService ,
private premiumUpgradePromptService : PremiumUpgradePromptService ,
private autoConfirmService : AutomaticUserConfirmationService ,
private configService : ConfigService ,
) { }
async ngOnInit() {
@ -629,6 +642,8 @@ export class VaultComponent<C extends CipherViewLike> implements OnInit, OnDestr
} ,
) ;
void this . unifiedUpgradePromptService . displayUpgradePromptConditionally ( ) ;
this . setupAutoConfirm ( ) ;
}
ngOnDestroy() {
@ -1547,6 +1562,72 @@ export class VaultComponent<C extends CipherViewLike> implements OnInit, OnDestr
const cipherView = await this . cipherService . decrypt ( _cipher , activeUserId ) ;
return cipherView . login ? . password ;
}
private async openAutoConfirmFeatureDialog ( organization : Organization ) {
if ( this . autoConfirmDialogRef ) {
return ;
}
this . autoConfirmDialogRef = AutoConfirmPolicyDialogComponent . open ( this . dialogService , {
data : {
policy : new AutoConfirmPolicy ( ) ,
organizationId : organization.id ,
firstTimeDialog : true ,
} ,
} ) ;
await lastValueFrom ( this . autoConfirmDialogRef . closed ) ;
this . autoConfirmDialogRef = undefined ;
}
private setupAutoConfirm() {
// if the policy is enabled, then the user may only belong to one organization at most.
const organization $ = this . organizations $ . pipe ( map ( ( organizations ) = > organizations [ 0 ] ) ) ;
const featureFlag $ = this . configService . getFeatureFlag $ ( FeatureFlag . AutoConfirm ) ;
const autoConfirmState $ = this . userId $ . pipe (
switchMap ( ( userId ) = > this . autoConfirmService . configuration $ ( userId ) ) ,
) ;
const policyEnabled $ = combineLatest ( [
this . userId $ . pipe (
switchMap ( ( userId ) = > this . policyService . policies $ ( userId ) ) ,
map ( ( policies ) = > policies . find ( ( p ) = > p . type === PolicyType . AutoConfirm && p . enabled ) ) ,
) ,
organization $ ,
] ) . pipe (
map (
( [ policy , organization ] ) = > ( policy && policy . organizationId === organization ? . id ) ? ? false ,
) ,
) ;
zip ( [ organization $ , featureFlag $ , autoConfirmState $ , policyEnabled $ , this . userId $ ] )
. pipe (
first ( ) ,
switchMap ( async ( [ organization , flagEnabled , autoConfirmState , policyEnabled , userId ] ) = > {
const showDialog =
flagEnabled &&
! policyEnabled &&
autoConfirmState . showSetupDialog &&
! ! organization &&
( organization . canManageUsers || organization . canManagePolicies ) ;
if ( showDialog ) {
await this . openAutoConfirmFeatureDialog ( organization ) ;
await this . autoConfirmService . upsert ( userId , {
. . . autoConfirmState ,
showSetupDialog : false ,
} ) ;
}
} ) ,
takeUntil ( this . destroy $ ) ,
)
. subscribe ( {
error : ( err : unknown ) = > this . logService . error ( "Failed to update auto-confirm state" , err ) ,
} ) ;
}
}
/ * *