diff --git a/apps/desktop/src/app/services/services.module.ts b/apps/desktop/src/app/services/services.module.ts index ea16f36402c..9f2bb1acc90 100644 --- a/apps/desktop/src/app/services/services.module.ts +++ b/apps/desktop/src/app/services/services.module.ts @@ -468,6 +468,7 @@ const safeProviders: SafeProvider[] = [ GlobalStateProvider, PlatformUtilsServiceAbstraction, BillingAccountProfileStateService, + DesktopAutotypeDefaultSettingPolicy, ], }), safeProvider({ diff --git a/apps/desktop/src/autofill/services/desktop-autotype-policy.service.ts b/apps/desktop/src/autofill/services/desktop-autotype-policy.service.ts index 76ffc090600..887a30ef6f6 100644 --- a/apps/desktop/src/autofill/services/desktop-autotype-policy.service.ts +++ b/apps/desktop/src/autofill/services/desktop-autotype-policy.service.ts @@ -21,7 +21,7 @@ export class DesktopAutotypeDefaultSettingPolicy { ) {} /** - * Emits the autotype policy enabled status (true | false | null) when account is unlocked and WindowsDesktopAutotype is enabled. + * Emits the autotype policy enabled status when account is unlocked and WindowsDesktopAutotype is enabled. * - true: autotype policy exists and is enabled * - null: no autotype policy exists for the user's organization */ diff --git a/apps/desktop/src/autofill/services/desktop-autotype.service.ts b/apps/desktop/src/autofill/services/desktop-autotype.service.ts index 60e87aa2aa5..b156ffd3597 100644 --- a/apps/desktop/src/autofill/services/desktop-autotype.service.ts +++ b/apps/desktop/src/autofill/services/desktop-autotype.service.ts @@ -17,7 +17,9 @@ import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.servi import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { UserId } from "@bitwarden/user-core"; -export const AUTOTYPE_ENABLED = new KeyDefinition( +import { DesktopAutotypeDefaultSettingPolicy } from "./desktop-autotype-policy.service"; + +export const AUTOTYPE_ENABLED = new KeyDefinition( AUTOTYPE_SETTINGS_DISK, "autotypeEnabled", { deserializer: (b) => b }, @@ -37,6 +39,7 @@ export class DesktopAutotypeService { private globalStateProvider: GlobalStateProvider, private platformUtilsService: PlatformUtilsService, private billingAccountProfileStateService: BillingAccountProfileStateService, + private desktopAutotypePolicy: DesktopAutotypeDefaultSettingPolicy, ) { ipc.autofill.listenAutotypeRequest(async (windowTitle, callback) => { const possibleCiphers = await this.matchCiphersToWindowTitle(windowTitle); @@ -50,9 +53,32 @@ export class DesktopAutotypeService { } async init() { - this.autotypeEnabledUserSetting$ = this.autotypeEnabledState.state$; - + // Currently Autotype is only supported for Windows if (this.platformUtilsService.getDevice() === DeviceType.WindowsDesktop) { + // If `autotypeDefaultPolicy` is `true` for a user's organization, and the + // user has never changed their local autotype setting (`autotypeEnabledState`), + // we set their local setting to `true` (once the local user setting is changed + // by this policy or the user themselves, the default policy should + // never change the user setting again). + combineLatest([ + this.autotypeEnabledState.state$, + this.desktopAutotypePolicy.autotypeDefaultSetting$, + ]) + .pipe( + map(async ([autotypeEnabledState, autotypeDefaultPolicy]) => { + if (autotypeDefaultPolicy === true && autotypeEnabledState === null) { + await this.setAutotypeEnabledState(true); + } + }), + ) + .subscribe(); + + // autotypeEnabledUserSetting$ publicly represents the value the + // user has set for autotyeEnabled in their local settings. + this.autotypeEnabledUserSetting$ = this.autotypeEnabledState.state$; + + // resolvedAutotypeEnabled$ represents the final determination if the Autotype + // feature should be on or off. this.resolvedAutotypeEnabled$ = combineLatest([ this.autotypeEnabledState.state$, this.configService.getFeatureFlag$(FeatureFlag.WindowsDesktopAutotype), @@ -76,6 +102,8 @@ export class DesktopAutotypeService { ), ); + // When the resolvedAutotypeEnabled$ value changes, this might require + // hotkey registration / deregistration in the main process. this.resolvedAutotypeEnabled$.subscribe((enabled) => { ipc.autofill.configureAutotype(enabled); });