1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-20 02:03:39 +00:00

[PM-24677] Slim StateService down so it can be moved to state lib (#16021)

* Slim StateService down so it can be moved to state lib

* Fix accidental import changes

* Add `switchAccount` assertion

* Needs to use mock
This commit is contained in:
Justin Baur
2025-08-18 12:37:25 -04:00
committed by GitHub
parent ea305a0f71
commit 939fd402c3
49 changed files with 286 additions and 1274 deletions

View File

@@ -2,6 +2,7 @@ import { mock, MockProxy } from "jest-mock-extended";
import { BehaviorSubject, of } from "rxjs";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
import {
AUTOFILL_CARD_ID,
AUTOFILL_ID,
@@ -17,7 +18,6 @@ import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/s
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { UserId } from "@bitwarden/common/types/guid";
import { CipherType } from "@bitwarden/common/vault/enums";
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
@@ -67,7 +67,7 @@ const createCipher = (data?: {
};
describe("context-menu", () => {
let stateService: MockProxy<StateService>;
let tokenService: MockProxy<TokenService>;
let autofillSettingsService: MockProxy<AutofillSettingsServiceAbstraction>;
let i18nService: MockProxy<I18nService>;
let logService: MockProxy<LogService>;
@@ -85,7 +85,7 @@ describe("context-menu", () => {
let sut: MainContextMenuHandler;
beforeEach(() => {
stateService = mock();
tokenService = mock();
autofillSettingsService = mock();
i18nService = mock();
logService = mock();
@@ -109,7 +109,7 @@ describe("context-menu", () => {
i18nService.t.mockImplementation((key) => key);
sut = new MainContextMenuHandler(
stateService,
tokenService,
autofillSettingsService,
i18nService,
logService,
@@ -276,7 +276,7 @@ describe("context-menu", () => {
it("removes menu items that require code injection", async () => {
billingAccountProfileStateService.hasPremiumFromAnySource$.mockReturnValue(of(true));
autofillSettingsService.enableContextMenu$ = of(true);
stateService.getIsAuthenticated.mockResolvedValue(true);
tokenService.hasAccessToken$.mockReturnValue(of(true));
const optionId = "1";
await sut.loadOptions("TEST_TITLE", optionId, createCipher());
@@ -317,7 +317,7 @@ describe("context-menu", () => {
});
it("Loads context menu items that ask the user to unlock their vault if they are authed", async () => {
stateService.getIsAuthenticated.mockResolvedValue(true);
tokenService.hasAccessToken$.mockReturnValue(of(true));
await sut.noAccess();
@@ -325,7 +325,7 @@ describe("context-menu", () => {
});
it("Loads context menu items that ask the user to login to their vault if they are not authed", async () => {
stateService.getIsAuthenticated.mockResolvedValue(false);
tokenService.hasAccessToken$.mockReturnValue(of(false));
await sut.noAccess();

View File

@@ -1,8 +1,9 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { firstValueFrom } from "rxjs";
import { firstValueFrom, map } from "rxjs";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
import {
AUTOFILL_CARD_ID,
AUTOFILL_ID,
@@ -23,7 +24,6 @@ import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/s
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { CipherType } from "@bitwarden/common/vault/enums/cipher-type";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
@@ -152,7 +152,7 @@ export class MainContextMenuHandler {
];
constructor(
private stateService: StateService,
private tokenService: TokenService,
private autofillSettingsService: AutofillSettingsServiceAbstraction,
private i18nService: I18nService,
private logService: LogService,
@@ -343,7 +343,11 @@ export class MainContextMenuHandler {
async noAccess() {
if (await this.init()) {
const authed = await this.stateService.getIsAuthenticated();
const userId = await firstValueFrom(
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
);
const authed =
userId != null && (await firstValueFrom(this.tokenService.hasAccessToken$(userId)));
this.loadOptions(
this.i18nService.t(authed ? "unlockVaultMenu" : "loginToVaultMenu"),
NOOP_COMMAND_SUFFIX,

View File

@@ -111,14 +111,11 @@ import {
ObservableStorageService,
} from "@bitwarden/common/platform/abstractions/storage.service";
import { SystemService as SystemServiceAbstraction } from "@bitwarden/common/platform/abstractions/system.service";
import { StateFactory } from "@bitwarden/common/platform/factories/state-factory";
import { IpcService } from "@bitwarden/common/platform/ipc";
import { Message, MessageListener, MessageSender } from "@bitwarden/common/platform/messaging";
// eslint-disable-next-line no-restricted-imports -- Used for dependency creation
import { SubjectMessageSender } from "@bitwarden/common/platform/messaging/internal";
import { Lazy } from "@bitwarden/common/platform/misc/lazy";
import { Account } from "@bitwarden/common/platform/models/domain/account";
import { GlobalState } from "@bitwarden/common/platform/models/domain/global-state";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { NotificationsService } from "@bitwarden/common/platform/notifications";
// eslint-disable-next-line no-restricted-imports -- Needed for service creation
@@ -143,11 +140,11 @@ import { MigrationRunner } from "@bitwarden/common/platform/services/migration-r
import { DefaultSdkClientFactory } from "@bitwarden/common/platform/services/sdk/default-sdk-client-factory";
import { DefaultSdkService } from "@bitwarden/common/platform/services/sdk/default-sdk.service";
import { NoopSdkClientFactory } from "@bitwarden/common/platform/services/sdk/noop-sdk-client-factory";
import { StateService } from "@bitwarden/common/platform/services/state.service";
import { SystemService } from "@bitwarden/common/platform/services/system.service";
import { UserAutoUnlockKeyService } from "@bitwarden/common/platform/services/user-auto-unlock-key.service";
import {
ActiveUserStateProvider,
DefaultStateService,
DerivedStateProvider,
GlobalStateProvider,
SingleUserStateProvider,
@@ -387,6 +384,7 @@ export default class MainBackground {
activeUserStateProvider: ActiveUserStateProvider;
derivedStateProvider: DerivedStateProvider;
stateProvider: StateProvider;
migrationRunner: MigrationRunner;
taskSchedulerService: BrowserTaskSchedulerService;
fido2Background: Fido2BackgroundAbstraction;
individualVaultExportService: IndividualVaultExportServiceAbstraction;
@@ -592,8 +590,9 @@ export default class MainBackground {
this.globalStateProvider,
this.singleUserStateProvider,
);
const activeUserAccessor = new DefaultActiveUserAccessor(this.accountService);
this.activeUserStateProvider = new DefaultActiveUserStateProvider(
new DefaultActiveUserAccessor(this.accountService),
activeUserAccessor,
this.singleUserStateProvider,
);
this.derivedStateProvider = new InlineDerivedStateProvider();
@@ -639,23 +638,17 @@ export default class MainBackground {
this.taskSchedulerService,
);
const migrationRunner = new MigrationRunner(
this.migrationRunner = new MigrationRunner(
this.storageService,
this.logService,
new MigrationBuilderService(),
ClientType.Browser,
);
this.stateService = new StateService(
this.stateService = new DefaultStateService(
this.storageService,
this.secureStorageService,
this.memoryStorageService,
this.logService,
new StateFactory(GlobalState, Account),
this.accountService,
this.environmentService,
this.tokenService,
migrationRunner,
activeUserAccessor,
);
this.masterPasswordService = new MasterPasswordService(
@@ -887,7 +880,6 @@ export default class MainBackground {
this.apiService,
this.i18nService,
this.searchService,
this.stateService,
this.autofillSettingsService,
this.encryptService,
this.cipherFileUploadService,
@@ -946,6 +938,7 @@ export default class MainBackground {
this.messagingService,
this.searchService,
this.stateService,
this.tokenService,
this.authService,
this.vaultTimeoutSettingsService,
this.stateEventRunnerService,
@@ -989,7 +982,6 @@ export default class MainBackground {
this.sendService,
this.logService,
this.keyConnectorService,
this.stateService,
this.providerService,
this.folderApiService,
this.organizationService,
@@ -1320,7 +1312,7 @@ export default class MainBackground {
);
this.mainContextMenuHandler = new MainContextMenuHandler(
this.stateService,
this.tokenService,
this.autofillSettingsService,
this.i18nService,
this.logService,
@@ -1387,7 +1379,7 @@ export default class MainBackground {
await this.sdkLoadService.loadAndInit();
// Only the "true" background should run migrations
await this.stateService.init({ runMigrations: true });
await this.migrationRunner.run();
// This is here instead of in in the InitService b/c we don't plan for
// side effects to run in the Browser InitService.
@@ -1607,6 +1599,7 @@ export default class MainBackground {
const needStorageReseed = await this.needsStorageReseed(userBeingLoggedOut);
await this.stateService.clean({ userId: userBeingLoggedOut });
await this.tokenService.clearAccessToken(userBeingLoggedOut);
await this.accountService.clean(userBeingLoggedOut);
await this.stateEventRunnerService.handleEvent("logout", userBeingLoggedOut);

View File

@@ -4,8 +4,8 @@ import { Subject } from "rxjs";
import { CollectionService } from "@bitwarden/admin-console/common";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { MessageListener, MessageSender } from "@bitwarden/common/platform/messaging";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { SyncOptions } from "@bitwarden/common/platform/sync/sync.service";
@@ -22,7 +22,7 @@ import { FullSyncFinishedMessage } from "./sync-service.listener";
describe("ForegroundSyncService", () => {
const userId = Utils.newGuid() as UserId;
const stateService = mock<StateService>();
const tokenService = mock<TokenService>();
const folderService = mock<InternalFolderService>();
const folderApiService = mock<FolderApiServiceAbstraction>();
const messageSender = mock<MessageSender>();
@@ -38,7 +38,7 @@ describe("ForegroundSyncService", () => {
const stateProvider = new FakeStateProvider(accountService);
const sut = new ForegroundSyncService(
stateService,
tokenService,
folderService,
folderApiService,
messageSender,

View File

@@ -4,8 +4,8 @@ import { CollectionService } from "@bitwarden/admin-console/common";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import {
CommandDefinition,
MessageListener,
@@ -31,7 +31,7 @@ export const DO_FULL_SYNC = new CommandDefinition<FullSyncMessage>("doFullSync")
export class ForegroundSyncService extends CoreSyncService {
constructor(
stateService: StateService,
tokenService: TokenService,
folderService: InternalFolderService,
folderApiService: FolderApiServiceAbstraction,
messageSender: MessageSender,
@@ -47,7 +47,7 @@ export class ForegroundSyncService extends CoreSyncService {
stateProvider: StateProvider,
) {
super(
stateService,
tokenService,
folderService,
folderApiService,
messageSender,

View File

@@ -28,13 +28,13 @@ import { DocumentLangSetter } from "@bitwarden/angular/platform/i18n";
import { LogoutReason, UserDecryptionOptionsServiceAbstraction } from "@bitwarden/auth/common";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { AnimationControlService } from "@bitwarden/common/platform/abstractions/animation-control.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { SdkService } from "@bitwarden/common/platform/abstractions/sdk/sdk.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { MessageListener } from "@bitwarden/common/platform/messaging";
import { UserId } from "@bitwarden/common/types/guid";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
@@ -102,7 +102,7 @@ export class AppComponent implements OnInit, OnDestroy {
private authService: AuthService,
private i18nService: I18nService,
private router: Router,
private stateService: StateService,
private readonly tokenService: TokenService,
private vaultBrowserStateService: VaultBrowserStateService,
private cipherService: CipherService,
private changeDetectorRef: ChangeDetectorRef,
@@ -321,7 +321,7 @@ export class AppComponent implements OnInit, OnDestroy {
}
private async clearComponentStates() {
if (!(await this.stateService.getIsAuthenticated())) {
if (!(await firstValueFrom(this.tokenService.hasAccessToken$(this.activeUserId)))) {
return;
}

View File

@@ -8,6 +8,7 @@ import { LogService as LogServiceAbstraction } from "@bitwarden/common/platform/
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner";
import { BrowserApi } from "../../platform/browser/browser-api";
import BrowserPopupUtils from "../../platform/browser/browser-popup-utils";
@@ -27,13 +28,14 @@ export class InitService {
private themingService: AbstractThemingService,
private sdkLoadService: SdkLoadService,
private viewCacheService: PopupViewCacheService,
private readonly migrationRunner: MigrationRunner,
@Inject(DOCUMENT) private document: Document,
) {}
init() {
return async () => {
await this.sdkLoadService.loadAndInit();
await this.stateService.init({ runMigrations: false }); // Browser background is responsible for migrations
await this.migrationRunner.waitForCompletion(); // Browser background is responsible for migrations
await this.i18nService.init();
this.twoFactorService.init();
await this.viewCacheService.init();

View File

@@ -47,6 +47,7 @@ import {
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { MasterPasswordApiService } from "@bitwarden/common/auth/abstractions/master-password-api.service.abstraction";
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import {
AutofillSettingsService,
@@ -333,7 +334,7 @@ const safeProviders: SafeProvider[] = [
provide: SyncService,
useClass: ForegroundSyncService,
deps: [
StateService,
TokenService,
InternalFolderService,
FolderApiServiceAbstraction,
MessageSender,