From d10c14791d8905251e28fb47ae3986c36193e1f6 Mon Sep 17 00:00:00 2001 From: Thomas Rittson <31796059+eliykat@users.noreply.github.com> Date: Thu, 28 Mar 2024 08:44:08 +1000 Subject: [PATCH] [AC-2329] [BEEEP] Use safeProvider in desktop services module (#8457) --- .../src/app/services/services.module.ts | 328 ++++++++++-------- .../native-message-handler.service.ts | 2 +- .../src/platform/utils/safe-provider.ts | 15 + 3 files changed, 198 insertions(+), 147 deletions(-) diff --git a/apps/desktop/src/app/services/services.module.ts b/apps/desktop/src/app/services/services.module.ts index 495d6abcf1..1d75ff4ca9 100644 --- a/apps/desktop/src/app/services/services.module.ts +++ b/apps/desktop/src/app/services/services.module.ts @@ -1,8 +1,8 @@ -import { APP_INITIALIZER, InjectionToken, NgModule } from "@angular/core"; +import { APP_INITIALIZER, NgModule } from "@angular/core"; +import { SafeProvider, safeProvider } from "@bitwarden/angular/platform/utils/safe-provider"; import { SECURE_STORAGE, - STATE_FACTORY, STATE_SERVICE_USE_CACHE, LOCALES_DIRECTORY, SYSTEM_LANGUAGE, @@ -12,6 +12,8 @@ import { WINDOW, SUPPORTS_SECURE_STORAGE, SYSTEM_THEME_OBSERVABLE, + SafeInjectionToken, + STATE_FACTORY, } from "@bitwarden/angular/services/injection-tokens"; import { JslibServicesModule } from "@bitwarden/angular/services/jslib-services.module"; import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service"; @@ -77,153 +79,187 @@ import { DesktopFileDownloadService } from "./desktop-file-download.service"; import { InitService } from "./init.service"; import { RendererCryptoFunctionService } from "./renderer-crypto-function.service"; -const RELOAD_CALLBACK = new InjectionToken<() => any>("RELOAD_CALLBACK"); +const RELOAD_CALLBACK = new SafeInjectionToken<() => any>("RELOAD_CALLBACK"); + +// Desktop has its own Account definition which must be used in its StateService +const DESKTOP_STATE_FACTORY = new SafeInjectionToken>( + "DESKTOP_STATE_FACTORY", +); + +/** + * Provider definitions used in the ngModule. + * Add your provider definition here using the safeProvider function as a wrapper. This will give you type safety. + * If you need help please ask for it, do NOT change the type of this array. + */ +const safeProviders: SafeProvider[] = [ + safeProvider(InitService), + safeProvider(NativeMessagingService), + safeProvider(SearchBarService), + safeProvider(LoginGuard), + safeProvider(DialogService), + safeProvider({ + provide: APP_INITIALIZER as SafeInjectionToken<() => void>, + useFactory: (initService: InitService) => initService.init(), + deps: [InitService], + multi: true, + }), + safeProvider({ + provide: DESKTOP_STATE_FACTORY, + useValue: new StateFactory(GlobalState, Account), + }), + safeProvider({ + provide: STATE_FACTORY, + useValue: null, + }), + safeProvider({ + provide: RELOAD_CALLBACK, + useValue: null, + }), + safeProvider({ + provide: LogServiceAbstraction, + useClass: ElectronLogRendererService, + deps: [], + }), + safeProvider({ + provide: PlatformUtilsServiceAbstraction, + useClass: ElectronPlatformUtilsService, + deps: [I18nServiceAbstraction, MessagingServiceAbstraction], + }), + safeProvider({ + // We manually override the value of SUPPORTS_SECURE_STORAGE here to avoid + // the TokenService having to inject the PlatformUtilsService which introduces a + // circular dependency on Desktop only. + provide: SUPPORTS_SECURE_STORAGE, + useValue: ELECTRON_SUPPORTS_SECURE_STORAGE, + }), + safeProvider({ + provide: I18nServiceAbstraction, + useClass: I18nRendererService, + deps: [SYSTEM_LANGUAGE, LOCALES_DIRECTORY, GlobalStateProvider], + }), + safeProvider({ + provide: MessagingServiceAbstraction, + useClass: ElectronRendererMessagingService, + deps: [BroadcasterServiceAbstraction], + }), + safeProvider({ + provide: AbstractStorageService, + useClass: ElectronRendererStorageService, + deps: [], + }), + safeProvider({ + provide: SECURE_STORAGE, + useClass: ElectronRendererSecureStorageService, + deps: [], + }), + safeProvider({ provide: MEMORY_STORAGE, useClass: MemoryStorageService, deps: [] }), + safeProvider({ + provide: OBSERVABLE_MEMORY_STORAGE, + useClass: MemoryStorageServiceForStateProviders, + deps: [], + }), + safeProvider({ provide: OBSERVABLE_DISK_STORAGE, useExisting: AbstractStorageService }), + safeProvider({ + provide: SystemServiceAbstraction, + useClass: SystemService, + deps: [ + MessagingServiceAbstraction, + PlatformUtilsServiceAbstraction, + RELOAD_CALLBACK, + StateServiceAbstraction, + AutofillSettingsServiceAbstraction, + VaultTimeoutSettingsService, + BiometricStateService, + ], + }), + safeProvider({ + provide: StateServiceAbstraction, + useClass: ElectronStateService, + deps: [ + AbstractStorageService, + SECURE_STORAGE, + MEMORY_STORAGE, + LogService, + DESKTOP_STATE_FACTORY, + AccountServiceAbstraction, + EnvironmentService, + TokenService, + MigrationRunner, + STATE_SERVICE_USE_CACHE, + ], + }), + safeProvider({ + provide: FileDownloadService, + useClass: DesktopFileDownloadService, + deps: [], + }), + safeProvider({ + provide: SYSTEM_THEME_OBSERVABLE, + useFactory: () => fromIpcSystemTheme(), + deps: [], + }), + safeProvider({ + provide: EncryptedMessageHandlerService, + deps: [ + StateServiceAbstraction, + AuthServiceAbstraction, + CipherServiceAbstraction, + PolicyServiceAbstraction, + MessagingServiceAbstraction, + PasswordGenerationServiceAbstraction, + ], + }), + safeProvider({ + provide: NativeMessageHandlerService, + deps: [ + StateServiceAbstraction, + CryptoServiceAbstraction, + CryptoFunctionServiceAbstraction, + MessagingServiceAbstraction, + EncryptedMessageHandlerService, + DialogService, + DesktopAutofillSettingsService, + ], + }), + safeProvider({ + provide: LoginServiceAbstraction, + useClass: LoginService, + deps: [StateServiceAbstraction], + }), + safeProvider({ + provide: CryptoFunctionServiceAbstraction, + useClass: RendererCryptoFunctionService, + deps: [WINDOW], + }), + safeProvider({ + provide: CryptoServiceAbstraction, + useClass: ElectronCryptoService, + deps: [ + KeyGenerationServiceAbstraction, + CryptoFunctionServiceAbstraction, + EncryptService, + PlatformUtilsServiceAbstraction, + LogService, + StateServiceAbstraction, + AccountServiceAbstraction, + StateProvider, + BiometricStateService, + ], + }), + safeProvider({ + provide: DesktopSettingsService, + deps: [StateProvider], + }), + safeProvider({ + provide: DesktopAutofillSettingsService, + deps: [StateProvider], + }), +]; @NgModule({ imports: [JslibServicesModule], declarations: [], - providers: [ - InitService, - NativeMessagingService, - SearchBarService, - LoginGuard, - DialogService, - { - provide: APP_INITIALIZER, - useFactory: (initService: InitService) => initService.init(), - deps: [InitService], - multi: true, - }, - { - provide: STATE_FACTORY, - useValue: new StateFactory(GlobalState, Account), - }, - { - provide: RELOAD_CALLBACK, - useValue: null, - }, - { provide: LogServiceAbstraction, useClass: ElectronLogRendererService, deps: [] }, - { - provide: PlatformUtilsServiceAbstraction, - useClass: ElectronPlatformUtilsService, - deps: [I18nServiceAbstraction, MessagingServiceAbstraction], - }, - { - // We manually override the value of SUPPORTS_SECURE_STORAGE here to avoid - // the TokenService having to inject the PlatformUtilsService which introduces a - // circular dependency on Desktop only. - provide: SUPPORTS_SECURE_STORAGE, - useValue: ELECTRON_SUPPORTS_SECURE_STORAGE, - }, - { - provide: I18nServiceAbstraction, - useClass: I18nRendererService, - deps: [SYSTEM_LANGUAGE, LOCALES_DIRECTORY, GlobalStateProvider], - }, - { - provide: MessagingServiceAbstraction, - useClass: ElectronRendererMessagingService, - deps: [BroadcasterServiceAbstraction], - }, - { provide: AbstractStorageService, useClass: ElectronRendererStorageService }, - { provide: SECURE_STORAGE, useClass: ElectronRendererSecureStorageService }, - { provide: MEMORY_STORAGE, useClass: MemoryStorageService }, - { provide: OBSERVABLE_MEMORY_STORAGE, useClass: MemoryStorageServiceForStateProviders }, - { provide: OBSERVABLE_DISK_STORAGE, useExisting: AbstractStorageService }, - { - provide: SystemServiceAbstraction, - useClass: SystemService, - deps: [ - MessagingServiceAbstraction, - PlatformUtilsServiceAbstraction, - RELOAD_CALLBACK, - StateServiceAbstraction, - AutofillSettingsServiceAbstraction, - VaultTimeoutSettingsService, - BiometricStateService, - ], - }, - { - provide: StateServiceAbstraction, - useClass: ElectronStateService, - deps: [ - AbstractStorageService, - SECURE_STORAGE, - MEMORY_STORAGE, - LogService, - STATE_FACTORY, - AccountServiceAbstraction, - EnvironmentService, - TokenService, - MigrationRunner, - STATE_SERVICE_USE_CACHE, - ], - }, - { - provide: FileDownloadService, - useClass: DesktopFileDownloadService, - }, - { - provide: SYSTEM_THEME_OBSERVABLE, - useFactory: () => fromIpcSystemTheme(), - }, - { - provide: EncryptedMessageHandlerService, - deps: [ - StateServiceAbstraction, - AuthServiceAbstraction, - CipherServiceAbstraction, - PolicyServiceAbstraction, - MessagingServiceAbstraction, - PasswordGenerationServiceAbstraction, - ], - }, - { - provide: NativeMessageHandlerService, - deps: [ - StateServiceAbstraction, - CryptoServiceAbstraction, - CryptoFunctionServiceAbstraction, - MessagingServiceAbstraction, - EncryptedMessageHandlerService, - DialogService, - ], - }, - { - provide: LoginServiceAbstraction, - useClass: LoginService, - deps: [StateServiceAbstraction], - }, - { - provide: CryptoFunctionServiceAbstraction, - useClass: RendererCryptoFunctionService, - deps: [WINDOW], - }, - { - provide: CryptoServiceAbstraction, - useClass: ElectronCryptoService, - deps: [ - KeyGenerationServiceAbstraction, - CryptoFunctionServiceAbstraction, - EncryptService, - PlatformUtilsServiceAbstraction, - LogService, - StateServiceAbstraction, - AccountServiceAbstraction, - StateProvider, - BiometricStateService, - ], - }, - { - provide: DesktopSettingsService, - useClass: DesktopSettingsService, - deps: [StateProvider], - }, - { - provide: DesktopAutofillSettingsService, - useClass: DesktopAutofillSettingsService, - deps: [StateProvider], - }, - ], + // Do not register your dependency here! Add it to the typesafeProviders array using the helper function + providers: safeProviders, }) export class ServicesModule {} diff --git a/apps/desktop/src/services/native-message-handler.service.ts b/apps/desktop/src/services/native-message-handler.service.ts index 785b65195a..ebe1ee6248 100644 --- a/apps/desktop/src/services/native-message-handler.service.ts +++ b/apps/desktop/src/services/native-message-handler.service.ts @@ -5,10 +5,10 @@ import { NativeMessagingVersion } from "@bitwarden/common/enums"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; +import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { EncryptedString, EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; -import { StateService } from "@bitwarden/common/platform/services/state.service"; import { DialogService } from "@bitwarden/components"; import { VerifyNativeMessagingDialogComponent } from "../app/components/verify-native-messaging-dialog.component"; diff --git a/libs/angular/src/platform/utils/safe-provider.ts b/libs/angular/src/platform/utils/safe-provider.ts index 4ab1d2ae2a..7c19a280d6 100644 --- a/libs/angular/src/platform/utils/safe-provider.ts +++ b/libs/angular/src/platform/utils/safe-provider.ts @@ -74,6 +74,17 @@ type SafeExistingProvider< useExisting: I; }; +/** + * Represents a dependency where there is no abstract token, the token is the implementation + */ +type SafeConcreteProvider< + I extends Constructor, + D extends MapParametersToDeps>, +> = { + provide: I; + deps: D; +}; + /** * A factory function that creates a provider for the ngModule providers array. * This guarantees type safety for your provider definition. It does nothing at runtime. @@ -97,11 +108,15 @@ export const safeProvider = < IExisting extends | Constructor> | AbstractConstructor>, + // types for no token + IConcrete extends Constructor, + DConcrete extends MapParametersToDeps>, >( provider: | SafeClassProvider | SafeValueProvider | SafeFactoryProvider | SafeExistingProvider + | SafeConcreteProvider | Constructor, ): SafeProvider => provider as SafeProvider;