From 1032c9ce9f140f485e0ba58714c6342685429ea3 Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Wed, 20 Nov 2024 19:03:42 -0500 Subject: [PATCH] First pass of a working implementation. Need to remove the hard coded trues. --- .../src/app/accounts/settings.component.html | 38 +++++++------------ .../src/app/accounts/settings.component.ts | 22 ++++++++++- apps/desktop/src/app/app.module.ts | 3 +- .../src/biometrics/biometric-state.service.ts | 18 +++++++++ 4 files changed, 54 insertions(+), 27 deletions(-) diff --git a/apps/desktop/src/app/accounts/settings.component.html b/apps/desktop/src/app/accounts/settings.component.html index 7336ce09dd8..fafab9632fb 100644 --- a/apps/desktop/src/app/accounts/settings.component.html +++ b/apps/desktop/src/app/accounts/settings.component.html @@ -119,49 +119,38 @@ -
+
- {{ + {{ additionalBiometricSettingsText | i18n }}
-
-
+ + +
+
-
-
-
+
@@ -170,6 +159,7 @@ "recommendedForSecurity" | i18n }}
+
diff --git a/apps/desktop/src/app/accounts/settings.component.ts b/apps/desktop/src/app/accounts/settings.component.ts index a8ce45f53c7..971b4ffd6a7 100644 --- a/apps/desktop/src/app/accounts/settings.component.ts +++ b/apps/desktop/src/app/accounts/settings.component.ts @@ -114,6 +114,7 @@ export class SettingsComponent implements OnInit, OnDestroy { enableDuckDuckGoBrowserIntegration: false, theme: [null as ThemeType | null], locale: [null as string | null], + startupBehavior: [null as "autoPromptBiometrics" | "requirePasswordOnStart" | null], }); private refreshTimeoutSettings$ = new BehaviorSubject(undefined); @@ -143,7 +144,7 @@ export class SettingsComponent implements OnInit, OnDestroy { private nativeMessagingManifestService: NativeMessagingManifestService, private configService: ConfigService, ) { - const isMac = this.platformUtilsService.getDevice() === DeviceType.MacOsDesktop; + const isMac = false; // Workaround to avoid ghosting trays https://github.com/electron/electron/issues/17622 this.requireEnableTray = this.platformUtilsService.getDevice() === DeviceType.LinuxDesktop; @@ -209,7 +210,7 @@ export class SettingsComponent implements OnInit, OnDestroy { this.showSshAgentOption = await this.configService.getFeatureFlag(FeatureFlag.SSHAgent); this.userHasMasterPassword = await this.userVerificationService.hasMasterPassword(); - this.isWindows = this.platformUtilsService.getDevice() === DeviceType.WindowsDesktop; + this.isWindows = true; this.currentUserEmail = activeAccount.email; this.currentUserId = activeAccount.id; @@ -282,6 +283,7 @@ export class SettingsComponent implements OnInit, OnDestroy { enableSshAgent: await firstValueFrom(this.desktopSettingsService.sshAgentEnabled$), theme: await firstValueFrom(this.themeStateService.selectedTheme$), locale: await firstValueFrom(this.i18nService.userSetLocale$), + startupBehavior: await firstValueFrom(this.biometricStateService.startupBehavior$), }; this.form.setValue(initialValues, { emitEvent: false }); @@ -355,6 +357,19 @@ export class SettingsComponent implements OnInit, OnDestroy { this.form.controls.enableBrowserIntegrationFingerprint.disable(); } }); + + this.form.controls.startupBehavior.valueChanges + .pipe( + concatMap(async (value) => { + if (value === "autoPromptBiometrics") { + await this.updateAutoPromptBiometrics(); + } else if (value === "requirePasswordOnStart") { + await this.updateRequirePasswordOnStart(); + } + }), + takeUntil(this.destroy$), + ) + .subscribe(); } async saveVaultTimeout(newValue: VaultTimeout) { @@ -769,6 +784,7 @@ export class SettingsComponent implements OnInit, OnDestroy { } get biometricText() { + return "unlockWithWindowsHello"; switch (this.platformUtilsService.getDevice()) { case DeviceType.MacOsDesktop: return "unlockWithTouchId"; @@ -782,6 +798,7 @@ export class SettingsComponent implements OnInit, OnDestroy { } get autoPromptBiometricsText() { + return "autoPromptWindowsHello"; switch (this.platformUtilsService.getDevice()) { case DeviceType.MacOsDesktop: return "autoPromptTouchId"; @@ -795,6 +812,7 @@ export class SettingsComponent implements OnInit, OnDestroy { } get additionalBiometricSettingsText() { + return "additionalWindowsHelloSettings"; switch (this.platformUtilsService.getDevice()) { case DeviceType.MacOsDesktop: return "additionalTouchIdSettings"; diff --git a/apps/desktop/src/app/app.module.ts b/apps/desktop/src/app/app.module.ts index d787234e8b3..bf0ce0fcbb9 100644 --- a/apps/desktop/src/app/app.module.ts +++ b/apps/desktop/src/app/app.module.ts @@ -7,7 +7,7 @@ import { NgModule } from "@angular/core"; import { ColorPasswordCountPipe } from "@bitwarden/angular/pipes/color-password-count.pipe"; import { ColorPasswordPipe } from "@bitwarden/angular/pipes/color-password.pipe"; -import { DialogModule, CalloutModule } from "@bitwarden/components"; +import { DialogModule, CalloutModule, RadioButtonModule } from "@bitwarden/components"; import { AccessibilityCookieComponent } from "../auth/accessibility-cookie.component"; import { DeleteAccountComponent } from "../auth/delete-account.component"; @@ -62,6 +62,7 @@ import { SendComponent } from "./tools/send/send.component"; CalloutModule, DeleteAccountComponent, UserVerificationComponent, + RadioButtonModule, ], declarations: [ AccessibilityCookieComponent, diff --git a/libs/key-management/src/biometrics/biometric-state.service.ts b/libs/key-management/src/biometrics/biometric-state.service.ts index e8153007390..6bc22bff5a2 100644 --- a/libs/key-management/src/biometrics/biometric-state.service.ts +++ b/libs/key-management/src/biometrics/biometric-state.service.ts @@ -55,6 +55,11 @@ export abstract class BiometricStateService { */ abstract fingerprintValidated$: Observable; + /** + * + */ + abstract startupBehavior$: Observable<"autoPromptBiometrics" | "requirePasswordOnStart">; + /** * Updates the require password on start state for the currently active user. * @@ -62,42 +67,54 @@ export abstract class BiometricStateService { * @param value whether or not a password is required on first unlock after opening the application */ abstract setRequirePasswordOnStart(value: boolean): Promise; + /** * Updates the biometric unlock enabled state for the currently active user. * @param enabled whether or not to store a biometric key to unlock the vault */ abstract setBiometricUnlockEnabled(enabled: boolean): Promise; + /** * Gets the biometric unlock enabled state for the given user. * @param userId user Id to check */ abstract getBiometricUnlockEnabled(userId: UserId): Promise; + abstract setEncryptedClientKeyHalf(encryptedKeyHalf: EncString, userId?: UserId): Promise; + abstract getEncryptedClientKeyHalf(userId: UserId): Promise; + abstract getRequirePasswordOnStart(userId: UserId): Promise; + abstract removeEncryptedClientKeyHalf(userId: UserId): Promise; + /** * Updates the active user's state to reflect that they've been warned about requiring password on start. */ abstract setDismissedRequirePasswordOnStartCallout(): Promise; + /** * Updates the active user's state to reflect that they've cancelled the biometric prompt. */ abstract setUserPromptCancelled(): Promise; + /** * Resets the given user's state to reflect that they haven't cancelled the biometric prompt. * @param userId the user to reset the prompt cancelled state for. If not provided, the currently active user will be used. */ abstract resetUserPromptCancelled(userId?: UserId): Promise; + /** * Resets all user's state to reflect that they haven't cancelled the biometric prompt. */ abstract resetAllPromptCancelled(): Promise; + /** * Updates the currently active user's setting for auto prompting for biometrics on application start and lock * @param prompt Whether or not to prompt for biometrics on application start. */ abstract setPromptAutomatically(prompt: boolean): Promise; + /** * Updates whether or not IPC has been validated by the user this session * @param validated the value to save @@ -122,6 +139,7 @@ export class DefaultBiometricStateService implements BiometricStateService { promptCancelled$: Observable; promptAutomatically$: Observable; fingerprintValidated$: Observable; + startupBehavior$: Observable<"autoPromptBiometrics" | "requirePasswordOnStart">; constructor(private stateProvider: StateProvider) { this.biometricUnlockEnabledState = this.stateProvider.getActive(BIOMETRIC_UNLOCK_ENABLED);