mirror of
https://github.com/bitwarden/browser
synced 2025-12-21 02:33:46 +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:
@@ -1,14 +1,31 @@
|
||||
import { concatMap, distinctUntilChanged, Observable, ReplaySubject } from "rxjs";
|
||||
import {
|
||||
concatMap,
|
||||
distinctUntilChanged,
|
||||
firstValueFrom,
|
||||
map,
|
||||
Observable,
|
||||
ReplaySubject,
|
||||
} from "rxjs";
|
||||
|
||||
import { AccountService } from "../../auth/abstractions/account.service";
|
||||
import { EnvironmentUrls } from "../../auth/models/domain/environment-urls";
|
||||
import { UserId } from "../../types/guid";
|
||||
import {
|
||||
EnvironmentService as EnvironmentServiceAbstraction,
|
||||
Region,
|
||||
RegionDomain,
|
||||
Urls,
|
||||
} from "../abstractions/environment.service";
|
||||
import { StateService } from "../abstractions/state.service";
|
||||
import { Utils } from "../misc/utils";
|
||||
import { ENVIRONMENT_DISK, GlobalState, KeyDefinition, StateProvider } from "../state";
|
||||
|
||||
const REGION_KEY = new KeyDefinition<Region>(ENVIRONMENT_DISK, "region", {
|
||||
deserializer: (s) => s,
|
||||
});
|
||||
|
||||
const URLS_KEY = new KeyDefinition<EnvironmentUrls>(ENVIRONMENT_DISK, "urls", {
|
||||
deserializer: EnvironmentUrls.fromJSON,
|
||||
});
|
||||
|
||||
export class EnvironmentService implements EnvironmentServiceAbstraction {
|
||||
private readonly urlsSubject = new ReplaySubject<void>(1);
|
||||
@@ -27,6 +44,11 @@ export class EnvironmentService implements EnvironmentServiceAbstraction {
|
||||
private scimUrl: string = null;
|
||||
private cloudWebVaultUrl: string;
|
||||
|
||||
private regionGlobalState: GlobalState<Region | null>;
|
||||
private urlsGlobalState: GlobalState<EnvironmentUrls | null>;
|
||||
|
||||
private activeAccountId$: Observable<UserId | null>;
|
||||
|
||||
readonly usUrls: Urls = {
|
||||
base: null,
|
||||
api: "https://api.bitwarden.com",
|
||||
@@ -49,8 +71,15 @@ export class EnvironmentService implements EnvironmentServiceAbstraction {
|
||||
scim: "https://scim.bitwarden.eu",
|
||||
};
|
||||
|
||||
constructor(private stateService: StateService) {
|
||||
this.stateService.activeAccount$
|
||||
constructor(
|
||||
private stateProvider: StateProvider,
|
||||
private accountService: AccountService,
|
||||
) {
|
||||
// We intentionally don't want the helper on account service, we want the null back if there is no active user
|
||||
this.activeAccountId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id));
|
||||
|
||||
// TODO: Get rid of early subscription during EnvironmentService refactor
|
||||
this.activeAccountId$
|
||||
.pipe(
|
||||
// Use == here to not trigger on undefined -> null transition
|
||||
distinctUntilChanged((oldUserId: string, newUserId: string) => oldUserId == newUserId),
|
||||
@@ -62,6 +91,9 @@ export class EnvironmentService implements EnvironmentServiceAbstraction {
|
||||
}),
|
||||
)
|
||||
.subscribe();
|
||||
|
||||
this.regionGlobalState = this.stateProvider.getGlobal(REGION_KEY);
|
||||
this.urlsGlobalState = this.stateProvider.getGlobal(URLS_KEY);
|
||||
}
|
||||
|
||||
hasBaseUrl() {
|
||||
@@ -180,8 +212,10 @@ export class EnvironmentService implements EnvironmentServiceAbstraction {
|
||||
}
|
||||
|
||||
async setUrlsFromStorage(): Promise<void> {
|
||||
const region = await this.stateService.getRegion();
|
||||
const savedUrls = await this.stateService.getEnvironmentUrls();
|
||||
const activeUserId = await firstValueFrom(this.activeAccountId$);
|
||||
|
||||
const region = await this.getRegion(activeUserId);
|
||||
const savedUrls = await this.getEnvironmentUrls(activeUserId);
|
||||
const envUrls = new EnvironmentUrls();
|
||||
|
||||
// In release `2023.5.0`, we set the `base` property of the environment URLs to the US web vault URL when a user clicked the "US" region.
|
||||
@@ -231,7 +265,8 @@ export class EnvironmentService implements EnvironmentServiceAbstraction {
|
||||
// scimUrl cannot be cleared
|
||||
urls.scim = this.formatUrl(urls.scim) ?? this.scimUrl;
|
||||
|
||||
await this.stateService.setEnvironmentUrls({
|
||||
// Don't save scim url
|
||||
await this.urlsGlobalState.update(() => ({
|
||||
base: urls.base,
|
||||
api: urls.api,
|
||||
identity: urls.identity,
|
||||
@@ -240,8 +275,7 @@ export class EnvironmentService implements EnvironmentServiceAbstraction {
|
||||
notifications: urls.notifications,
|
||||
events: urls.events,
|
||||
keyConnector: urls.keyConnector,
|
||||
// scimUrl is not saved to storage
|
||||
});
|
||||
}));
|
||||
|
||||
this.baseUrl = urls.base;
|
||||
this.webVaultUrl = urls.webVault;
|
||||
@@ -287,8 +321,8 @@ export class EnvironmentService implements EnvironmentServiceAbstraction {
|
||||
);
|
||||
}
|
||||
|
||||
async getHost(userId?: string) {
|
||||
const region = await this.getRegion(userId ? userId : null);
|
||||
async getHost(userId?: UserId) {
|
||||
const region = await this.getRegion(userId);
|
||||
|
||||
switch (region) {
|
||||
case Region.US:
|
||||
@@ -297,21 +331,30 @@ export class EnvironmentService implements EnvironmentServiceAbstraction {
|
||||
return RegionDomain.EU;
|
||||
default: {
|
||||
// Environment is self-hosted
|
||||
const envUrls = await this.stateService.getEnvironmentUrls(
|
||||
userId ? { userId: userId } : null,
|
||||
);
|
||||
const envUrls = await this.getEnvironmentUrls(userId);
|
||||
return Utils.getHost(envUrls.webVault || envUrls.base);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async getRegion(userId?: string) {
|
||||
return this.stateService.getRegion(userId ? { userId: userId } : null);
|
||||
private async getRegion(userId: UserId | null) {
|
||||
// Previous rules dictated that we only get from user scoped state if there is an active user.
|
||||
const activeUserId = await firstValueFrom(this.activeAccountId$);
|
||||
return activeUserId == null
|
||||
? await firstValueFrom(this.regionGlobalState.state$)
|
||||
: await firstValueFrom(this.stateProvider.getUser(userId ?? activeUserId, REGION_KEY).state$);
|
||||
}
|
||||
|
||||
private async getEnvironmentUrls(userId: UserId | null) {
|
||||
return userId == null
|
||||
? (await firstValueFrom(this.urlsGlobalState.state$)) ?? new EnvironmentUrls()
|
||||
: (await firstValueFrom(this.stateProvider.getUser(userId, URLS_KEY).state$)) ??
|
||||
new EnvironmentUrls();
|
||||
}
|
||||
|
||||
async setRegion(region: Region) {
|
||||
this.selectedRegion = region;
|
||||
await this.stateService.setRegion(region);
|
||||
await this.regionGlobalState.update(() => region);
|
||||
|
||||
if (region === Region.SelfHosted) {
|
||||
// If user saves a self-hosted region with empty fields, default to US
|
||||
@@ -320,7 +363,7 @@ export class EnvironmentService implements EnvironmentServiceAbstraction {
|
||||
}
|
||||
} else {
|
||||
// If we are setting the region to EU or US, clear the self-hosted URLs
|
||||
await this.stateService.setEnvironmentUrls(new EnvironmentUrls());
|
||||
await this.urlsGlobalState.update(() => new EnvironmentUrls());
|
||||
if (region === Region.EU) {
|
||||
this.setUrlsInternal(this.euUrls);
|
||||
} else if (region === Region.US) {
|
||||
@@ -329,6 +372,13 @@ export class EnvironmentService implements EnvironmentServiceAbstraction {
|
||||
}
|
||||
}
|
||||
|
||||
async seedUserEnvironment(userId: UserId) {
|
||||
const globalRegion = await firstValueFrom(this.regionGlobalState.state$);
|
||||
const globalUrls = await firstValueFrom(this.urlsGlobalState.state$);
|
||||
await this.stateProvider.getUser(userId, REGION_KEY).update(() => globalRegion);
|
||||
await this.stateProvider.getUser(userId, URLS_KEY).update(() => globalUrls);
|
||||
}
|
||||
|
||||
private setUrlsInternal(urls: Urls) {
|
||||
this.baseUrl = this.formatUrl(urls.base);
|
||||
this.webVaultUrl = this.formatUrl(urls.webVault);
|
||||
|
||||
Reference in New Issue
Block a user