1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-15 07:43:35 +00:00

[PM-5535] Migrate Environment Service to StateProvider (#7621)

* Migrate EnvironmentService

* Move Migration Test Helper

* Claim StateDefinition

* Add State Migration

* Update StateServices

* Update EnvironmentService Abstraction

* Update DI

* Update Browser Instantiation

* Fix BrowserEnvironmentService

* Update Desktop & CLI Instantiation

* Update Usage

* Create isStringRecord helper

* Fix Old Tests

* Use Existing AccountService

* Don't Rely on Parameter Mutation

* Fix Conflicts
This commit is contained in:
Justin Baur
2024-01-24 14:21:50 -05:00
committed by GitHub
parent 842fa5153b
commit c1d5351075
26 changed files with 648 additions and 232 deletions

View File

@@ -344,6 +344,11 @@ export default class MainBackground {
this.globalStateProvider,
this.derivedStateProvider,
);
this.environmentService = new BrowserEnvironmentService(
this.logService,
this.stateProvider,
this.accountService,
);
this.stateService = new BrowserStateService(
this.storageService,
this.secureStorageService,
@@ -351,6 +356,7 @@ export default class MainBackground {
this.logService,
new StateFactory(GlobalState, Account),
this.accountService,
this.environmentService,
);
this.platformUtilsService = new BrowserPlatformUtilsService(
this.messagingService,
@@ -386,7 +392,6 @@ export default class MainBackground {
);
this.tokenService = new TokenService(this.stateService);
this.appIdService = new AppIdService(this.storageService);
this.environmentService = new BrowserEnvironmentService(this.stateService, this.logService);
this.apiService = new ApiService(
this.tokenService,
this.platformUtilsService,

View File

@@ -1,16 +1,18 @@
import {
accountServiceFactory,
AccountServiceInitOptions,
} from "../../../auth/background/service-factories/account-service.factory";
import { BrowserEnvironmentService } from "../../services/browser-environment.service";
import { CachedServices, factory, FactoryOptions } from "./factory-options";
import { logServiceFactory, LogServiceInitOptions } from "./log-service.factory";
import {
stateServiceFactory as stateServiceFactory,
StateServiceInitOptions,
} from "./state-service.factory";
import { stateProviderFactory, StateProviderInitOptions } from "./state-provider.factory";
type EnvironmentServiceFactoryOptions = FactoryOptions;
export type EnvironmentServiceInitOptions = EnvironmentServiceFactoryOptions &
StateServiceInitOptions &
StateProviderInitOptions &
AccountServiceInitOptions &
LogServiceInitOptions;
export function environmentServiceFactory(
@@ -23,8 +25,9 @@ export function environmentServiceFactory(
opts,
async () =>
new BrowserEnvironmentService(
await stateServiceFactory(cache, opts),
await logServiceFactory(cache, opts),
await stateProviderFactory(cache, opts),
await accountServiceFactory(cache, opts),
),
);
}

View File

@@ -8,6 +8,10 @@ import {
import { Account } from "../../../models/account";
import { BrowserStateService } from "../../services/browser-state.service";
import {
environmentServiceFactory,
EnvironmentServiceInitOptions,
} from "./environment-service.factory";
import { CachedServices, factory, FactoryOptions } from "./factory-options";
import { logServiceFactory, LogServiceInitOptions } from "./log-service.factory";
import {
@@ -31,7 +35,8 @@ export type StateServiceInitOptions = StateServiceFactoryOptions &
SecureStorageServiceInitOptions &
MemoryStorageServiceInitOptions &
LogServiceInitOptions &
AccountServiceInitOptions;
AccountServiceInitOptions &
EnvironmentServiceInitOptions;
export async function stateServiceFactory(
cache: { stateService?: BrowserStateService } & CachedServices,
@@ -42,13 +47,14 @@ export async function stateServiceFactory(
"stateService",
opts,
async () =>
await new BrowserStateService(
new BrowserStateService(
await diskStorageServiceFactory(cache, opts),
await secureStorageServiceFactory(cache, opts),
await memoryStorageServiceFactory(cache, opts),
await logServiceFactory(cache, opts),
opts.stateServiceOptions.stateFactory,
await accountServiceFactory(cache, opts),
await environmentServiceFactory(cache, opts),
opts.stateServiceOptions.useAccountCache,
),
);

View File

@@ -1,16 +1,18 @@
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { EnvironmentService } from "@bitwarden/common/platform/services/environment.service";
import { StateProvider } from "@bitwarden/common/platform/state";
import { GroupPolicyEnvironment } from "../../admin-console/types/group-policy-environment";
import { devFlagEnabled, devFlagValue } from "../flags";
export class BrowserEnvironmentService extends EnvironmentService {
constructor(
stateService: StateService,
private logService: LogService,
stateProvider: StateProvider,
accountService: AccountService,
) {
super(stateService);
super(stateProvider, accountService);
}
async hasManagedEnvironment(): Promise<boolean> {

View File

@@ -1,6 +1,7 @@
import { mock, MockProxy } from "jest-mock-extended";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import {
AbstractMemoryStorageService,
@@ -29,6 +30,7 @@ describe("Browser State Service", () => {
let stateFactory: MockProxy<StateFactory<GlobalState, Account>>;
let useAccountCache: boolean;
let accountService: MockProxy<AccountService>;
let environmentService: MockProxy<EnvironmentService>;
let state: State<GlobalState, Account>;
const userId = "userId";
@@ -41,6 +43,7 @@ describe("Browser State Service", () => {
logService = mock();
stateFactory = mock();
accountService = mock();
environmentService = mock();
// turn off account cache for tests
useAccountCache = false;
@@ -66,6 +69,7 @@ describe("Browser State Service", () => {
logService,
stateFactory,
accountService,
environmentService,
useAccountCache,
);
});

View File

@@ -1,6 +1,7 @@
import { BehaviorSubject } from "rxjs";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import {
AbstractStorageService,
@@ -44,6 +45,7 @@ export class BrowserStateService
logService: LogService,
stateFactory: StateFactory<GlobalState, Account>,
accountService: AccountService,
environmentService: EnvironmentService,
useAccountCache = true,
) {
super(
@@ -53,6 +55,7 @@ export class BrowserStateService
logService,
stateFactory,
accountService,
environmentService,
useAccountCache,
);

View File

@@ -483,6 +483,7 @@ function getBgService<T>(service: keyof MainBackground) {
memoryStorageService: AbstractMemoryStorageService,
logService: LogServiceAbstraction,
accountService: AccountServiceAbstraction,
environmentService: EnvironmentService,
) => {
return new BrowserStateService(
storageService,
@@ -491,6 +492,7 @@ function getBgService<T>(service: keyof MainBackground) {
logService,
new StateFactory(GlobalState, Account),
accountService,
environmentService,
);
},
deps: [
@@ -499,6 +501,7 @@ function getBgService<T>(service: keyof MainBackground) {
MEMORY_STORAGE,
LogServiceAbstraction,
AccountServiceAbstraction,
EnvironmentService,
],
},
{

View File

@@ -255,6 +255,8 @@ export class Main {
this.derivedStateProvider,
);
this.environmentService = new EnvironmentService(this.stateProvider, this.accountService);
this.stateService = new StateService(
this.storageService,
this.secureStorageService,
@@ -262,6 +264,7 @@ export class Main {
this.logService,
new StateFactory(GlobalState, Account),
this.accountService,
this.environmentService,
);
this.cryptoService = new CryptoService(
@@ -276,7 +279,6 @@ export class Main {
this.appIdService = new AppIdService(this.storageService);
this.tokenService = new TokenService(this.stateService);
this.environmentService = new EnvironmentService(this.stateService);
const customUserAgent =
"Bitwarden_CLI/" +

View File

@@ -22,6 +22,7 @@ import { BroadcasterService as BroadcasterServiceAbstraction } from "@bitwarden/
import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitwarden/common/platform/abstractions/crypto-function.service";
import { CryptoService as CryptoServiceAbstraction } from "@bitwarden/common/platform/abstractions/crypto.service";
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service";
import {
@@ -129,6 +130,7 @@ const RELOAD_CALLBACK = new InjectionToken<() => any>("RELOAD_CALLBACK");
LogService,
STATE_FACTORY,
AccountServiceAbstraction,
EnvironmentService,
STATE_SERVICE_USE_CACHE,
],
},

View File

@@ -5,10 +5,16 @@ import { app } from "electron";
import { AccountServiceImplementation } from "@bitwarden/common/auth/services/account.service";
import { StateFactory } from "@bitwarden/common/platform/factories/state-factory";
import { GlobalState } from "@bitwarden/common/platform/models/domain/global-state";
import { EnvironmentService } from "@bitwarden/common/platform/services/environment.service";
import { MemoryStorageService } from "@bitwarden/common/platform/services/memory-storage.service";
import { NoopMessagingService } from "@bitwarden/common/platform/services/noop-messaging.service";
// eslint-disable-next-line import/no-restricted-paths -- We need the implementation to inject, but generally this should not be accessed
/* eslint-disable import/no-restricted-paths -- We need the implementation to inject, but generally this should not be accessed */
import { DefaultActiveUserStateProvider } from "@bitwarden/common/platform/state/implementations/default-active-user-state.provider";
import { DefaultDerivedStateProvider } from "@bitwarden/common/platform/state/implementations/default-derived-state.provider";
import { DefaultGlobalStateProvider } from "@bitwarden/common/platform/state/implementations/default-global-state.provider";
import { DefaultSingleUserStateProvider } from "@bitwarden/common/platform/state/implementations/default-single-user-state.provider";
import { DefaultStateProvider } from "@bitwarden/common/platform/state/implementations/default-state.provider";
/*/ eslint-enable import/no-restricted-paths */
import { MenuMain } from "./main/menu/menu.main";
import { MessagingMain } from "./main/messaging.main";
@@ -34,6 +40,7 @@ export class Main {
memoryStorageService: MemoryStorageService;
messagingService: ElectronMainMessagingService;
stateService: ElectronStateService;
environmentService: EnvironmentService;
desktopCredentialStorageListener: DesktopCredentialStorageListener;
windowMain: WindowMain;
@@ -93,6 +100,25 @@ export class Main {
this.storageService,
);
const accountService = new AccountServiceImplementation(
new NoopMessagingService(),
this.logService,
globalStateProvider,
);
const stateProvider = new DefaultStateProvider(
new DefaultActiveUserStateProvider(
accountService,
this.memoryStorageService,
this.storageService,
),
new DefaultSingleUserStateProvider(this.memoryStorageService, this.storageService),
globalStateProvider,
new DefaultDerivedStateProvider(this.memoryStorageService),
);
this.environmentService = new EnvironmentService(stateProvider, accountService);
// TODO: this state service will have access to on disk storage, but not in memory storage.
// If we could get this to work using the stateService singleton that the rest of the app uses we could save
// ourselves from some hacks, like having to manually update the app menu vs. the menu subscribing to events.
@@ -102,11 +128,8 @@ export class Main {
this.memoryStorageService,
this.logService,
new StateFactory(GlobalState, Account),
new AccountServiceImplementation(
new NoopMessagingService(),
this.logService,
globalStateProvider,
), // will not broadcast logouts. This is a hack until we can remove messaging dependency
accountService, // will not broadcast logouts. This is a hack until we can remove messaging dependency
this.environmentService,
false, // Do not use disk caching because this will get out of sync with the renderer service
);
@@ -128,7 +151,7 @@ export class Main {
this.menuMain = new MenuMain(
this.i18nService,
this.messagingService,
this.stateService,
this.environmentService,
this.windowMain,
this.updaterMain,
);

View File

@@ -1,8 +1,8 @@
import { app, Menu } from "electron";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { UpdaterMain } from "../updater.main";
import { WindowMain } from "../window.main";
@@ -16,7 +16,7 @@ export class MenuMain {
constructor(
private i18nService: I18nService,
private messagingService: MessagingService,
private stateService: StateService,
private environmentService: EnvironmentService,
private windowMain: WindowMain,
private updaterMain: UpdaterMain,
) {}
@@ -45,16 +45,7 @@ export class MenuMain {
}
private async getWebVaultUrl() {
let webVaultUrl = cloudWebVaultUrl;
const urlsObj = await this.stateService.getEnvironmentUrls();
if (urlsObj != null) {
if (urlsObj.base != null) {
webVaultUrl = urlsObj.base;
} else if (urlsObj.webVault != null) {
webVaultUrl = urlsObj.webVault;
}
}
return webVaultUrl;
return this.environmentService.getWebVaultUrl() ?? cloudWebVaultUrl;
}
private initContextMenu() {

View File

@@ -7,6 +7,7 @@ import {
STATE_SERVICE_USE_CACHE,
} from "@bitwarden/angular/services/injection-tokens";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import {
AbstractMemoryStorageService,
@@ -32,6 +33,7 @@ export class StateService extends BaseStateService<GlobalState, Account> {
logService: LogService,
@Inject(STATE_FACTORY) stateFactory: StateFactory<GlobalState, Account>,
accountService: AccountService,
environmentService: EnvironmentService,
@Inject(STATE_SERVICE_USE_CACHE) useAccountCache = true,
) {
super(
@@ -41,6 +43,7 @@ export class StateService extends BaseStateService<GlobalState, Account> {
logService,
stateFactory,
accountService,
environmentService,
useAccountCache,
);
}