mirror of
https://github.com/bitwarden/browser
synced 2025-12-10 21:33:27 +00:00
[PM-22419] dismiss account nudge when biometric unlock is set (#15139)
* update account-security-nudge service to look at biomentricUnlockEnabled$ observable, add success toast for biometric unlock
This commit is contained in:
@@ -5062,6 +5062,9 @@
|
|||||||
"unlockPinSet": {
|
"unlockPinSet": {
|
||||||
"message": "Unlock PIN set"
|
"message": "Unlock PIN set"
|
||||||
},
|
},
|
||||||
|
"unlockBiometricSet": {
|
||||||
|
"message": "Unlock biometrics set"
|
||||||
|
},
|
||||||
"authenticating": {
|
"authenticating": {
|
||||||
"message": "Authenticating"
|
"message": "Authenticating"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -534,6 +534,11 @@ export class AccountSecurityComponent implements OnInit, OnDestroy {
|
|||||||
if (!successful) {
|
if (!successful) {
|
||||||
await this.biometricStateService.setFingerprintValidated(false);
|
await this.biometricStateService.setFingerprintValidated(false);
|
||||||
}
|
}
|
||||||
|
this.toastService.showToast({
|
||||||
|
variant: "success",
|
||||||
|
title: null,
|
||||||
|
message: this.i18nService.t("unlockBiometricSet"),
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.form.controls.biometric.setValue(false);
|
this.form.controls.biometric.setValue(false);
|
||||||
this.validationService.showError(error);
|
this.validationService.showError(error);
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
import { Injectable, inject } from "@angular/core";
|
import { Injectable, inject } from "@angular/core";
|
||||||
import { Observable, combineLatest, from, of } from "rxjs";
|
import { Observable, combineLatest, from, of } from "rxjs";
|
||||||
import { catchError, map } from "rxjs/operators";
|
import { catchError, switchMap } from "rxjs/operators";
|
||||||
|
|
||||||
import { VaultProfileService } from "@bitwarden/angular/vault/services/vault-profile.service";
|
import { VaultProfileService } from "@bitwarden/angular/vault/services/vault-profile.service";
|
||||||
// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
|
// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
|
||||||
// eslint-disable-next-line no-restricted-imports
|
// eslint-disable-next-line no-restricted-imports
|
||||||
import { PinServiceAbstraction } from "@bitwarden/auth/common";
|
import { PinServiceAbstraction } from "@bitwarden/auth/common";
|
||||||
|
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||||
|
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||||
|
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||||
import { VaultTimeoutSettingsService } from "@bitwarden/common/key-management/vault-timeout";
|
import { VaultTimeoutSettingsService } from "@bitwarden/common/key-management/vault-timeout";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
import { UserId } from "@bitwarden/common/types/guid";
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
|
import { BiometricStateService } from "@bitwarden/key-management";
|
||||||
|
|
||||||
import { DefaultSingleNudgeService } from "../default-single-nudge.service";
|
import { DefaultSingleNudgeService } from "../default-single-nudge.service";
|
||||||
import { NudgeStatus, NudgeType } from "../nudges.service";
|
import { NudgeStatus, NudgeType } from "../nudges.service";
|
||||||
@@ -21,6 +25,9 @@ export class AccountSecurityNudgeService extends DefaultSingleNudgeService {
|
|||||||
private logService = inject(LogService);
|
private logService = inject(LogService);
|
||||||
private pinService = inject(PinServiceAbstraction);
|
private pinService = inject(PinServiceAbstraction);
|
||||||
private vaultTimeoutSettingsService = inject(VaultTimeoutSettingsService);
|
private vaultTimeoutSettingsService = inject(VaultTimeoutSettingsService);
|
||||||
|
private biometricStateService = inject(BiometricStateService);
|
||||||
|
private policyService = inject(PolicyService);
|
||||||
|
private organizationService = inject(OrganizationService);
|
||||||
|
|
||||||
nudgeStatus$(nudgeType: NudgeType, userId: UserId): Observable<NudgeStatus> {
|
nudgeStatus$(nudgeType: NudgeType, userId: UserId): Observable<NudgeStatus> {
|
||||||
const profileDate$ = from(this.vaultProfileService.getProfileCreationDate(userId)).pipe(
|
const profileDate$ = from(this.vaultProfileService.getProfileCreationDate(userId)).pipe(
|
||||||
@@ -36,16 +43,45 @@ export class AccountSecurityNudgeService extends DefaultSingleNudgeService {
|
|||||||
this.getNudgeStatus$(nudgeType, userId),
|
this.getNudgeStatus$(nudgeType, userId),
|
||||||
of(Date.now() - THIRTY_DAYS_MS),
|
of(Date.now() - THIRTY_DAYS_MS),
|
||||||
from(this.pinService.isPinSet(userId)),
|
from(this.pinService.isPinSet(userId)),
|
||||||
from(this.vaultTimeoutSettingsService.isBiometricLockSet(userId)),
|
this.biometricStateService.biometricUnlockEnabled$,
|
||||||
|
this.organizationService.organizations$(userId),
|
||||||
|
this.policyService.policiesByType$(PolicyType.RemoveUnlockWithPin, userId),
|
||||||
]).pipe(
|
]).pipe(
|
||||||
map(([profileCreationDate, status, profileCutoff, isPinSet, isBiometricLockSet]) => {
|
switchMap(
|
||||||
const profileOlderThanCutoff = profileCreationDate.getTime() < profileCutoff;
|
async ([
|
||||||
const hideNudge = profileOlderThanCutoff || isPinSet || isBiometricLockSet;
|
profileCreationDate,
|
||||||
return {
|
status,
|
||||||
hasBadgeDismissed: status.hasBadgeDismissed || hideNudge,
|
profileCutoff,
|
||||||
hasSpotlightDismissed: status.hasSpotlightDismissed || hideNudge,
|
isPinSet,
|
||||||
};
|
biometricUnlockEnabled,
|
||||||
}),
|
organizations,
|
||||||
|
policies,
|
||||||
|
]) => {
|
||||||
|
const profileOlderThanCutoff = profileCreationDate.getTime() < profileCutoff;
|
||||||
|
|
||||||
|
const hasOrgWithRemovePinPolicyOn = organizations.some((org) => {
|
||||||
|
return policies.some(
|
||||||
|
(p) => p.type === PolicyType.RemoveUnlockWithPin && p.organizationId === org.id,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const hideNudge =
|
||||||
|
profileOlderThanCutoff ||
|
||||||
|
isPinSet ||
|
||||||
|
biometricUnlockEnabled ||
|
||||||
|
hasOrgWithRemovePinPolicyOn;
|
||||||
|
|
||||||
|
const acctSecurityNudgeStatus = {
|
||||||
|
hasBadgeDismissed: status.hasBadgeDismissed || hideNudge,
|
||||||
|
hasSpotlightDismissed: status.hasSpotlightDismissed || hideNudge,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isPinSet || biometricUnlockEnabled || hasOrgWithRemovePinPolicyOn) {
|
||||||
|
await this.setNudgeStatus(nudgeType, acctSecurityNudgeStatus, userId);
|
||||||
|
}
|
||||||
|
return acctSecurityNudgeStatus;
|
||||||
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import { firstValueFrom, of } from "rxjs";
|
|||||||
// eslint-disable-next-line no-restricted-imports
|
// eslint-disable-next-line no-restricted-imports
|
||||||
import { PinServiceAbstraction } from "@bitwarden/auth/common";
|
import { PinServiceAbstraction } from "@bitwarden/auth/common";
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
|
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||||
|
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { VaultTimeoutSettingsService } from "@bitwarden/common/key-management/vault-timeout";
|
import { VaultTimeoutSettingsService } from "@bitwarden/common/key-management/vault-timeout";
|
||||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
@@ -13,6 +15,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service"
|
|||||||
import { StateProvider } from "@bitwarden/common/platform/state";
|
import { StateProvider } from "@bitwarden/common/platform/state";
|
||||||
import { UserId } from "@bitwarden/common/types/guid";
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||||
|
import { BiometricStateService } from "@bitwarden/key-management";
|
||||||
|
|
||||||
import { FakeStateProvider, mockAccountServiceWith } from "../../../../../libs/common/spec";
|
import { FakeStateProvider, mockAccountServiceWith } from "../../../../../libs/common/spec";
|
||||||
|
|
||||||
@@ -91,6 +94,18 @@ describe("Vault Nudges Service", () => {
|
|||||||
provide: VaultTimeoutSettingsService,
|
provide: VaultTimeoutSettingsService,
|
||||||
useValue: mock<VaultTimeoutSettingsService>(),
|
useValue: mock<VaultTimeoutSettingsService>(),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: BiometricStateService,
|
||||||
|
useValue: mock<BiometricStateService>(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: PolicyService,
|
||||||
|
useValue: mock<PolicyService>(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: OrganizationService,
|
||||||
|
useValue: mock<OrganizationService>(),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user