From bd770c90edd0c8b0fda93e7fa0ea13fbe326f611 Mon Sep 17 00:00:00 2001 From: Daniel James Smith Date: Thu, 27 Jan 2022 22:22:51 +0100 Subject: [PATCH] [Account Switching] Base changes for account switching (#2250) * Pull in jslib * Create new state models * Create browser specific stateService * Remove registration deprecated services, register stateService * Replace usage of deprecated services (user, constants) * Add missing properties to BrowserGroupingsComponentState * Remove StorageService from initFactory * Clear the correct state * Add null check when restoring send-grouping state * add remember email * Initialize stateservice in services.module * Fix 'lock now' not working * Comment to remove setting defaults on install * Pull jslib * Remove setting defaults on install * Bump jslib * Pass the current userId to services when logging out * Bump jslib * Override vaultTimeout default on account addition * Pull latest jslib * Retrieve vaultTimeout from stateService * Record activity per Account * Add userId to logout and add fallback if not present * Register AccountFactory * Pass userId in messages * Base changes for account switching di fixes (#2280) * [bug] Null checks on Account init * [bug] Use same stateService instance for all operations We override the stateService in browser, but currently don't pull the background service into popup and allow jslib to create its own instance of the base StateService for jslib services. This causes a split in in memory state between the three isntances that results in many errors, namely locking not working. * [chore] Update jslib * Pull in jslib * Pull in jslib * Pull in latest jslib to multiple stateservice inits * Check vault states before executing processReload * Adjust iterator * Update native messaging to include the userId (#2290) * Re-Add UserVerificationService * Fix email not being remembered by base component * Improve readability of reloadProcess * Removed unneeded null check * Fix constructor dependency (stateService) * Added missing await * Simplify dependency registration * Fixed typos * Reverted back to simple loop * Use vaultTimeoutService to retrieve Timeout Co-authored-by: Addison Beck Co-authored-by: Oscar Hinton --- jslib | 2 +- src/background/idle.background.ts | 11 +- src/background/main.background.ts | 275 ++++++++++-------- src/background/nativeMessaging.background.ts | 117 +++++--- src/background/notification.background.ts | 19 +- src/background/runtime.background.ts | 26 +- src/models/account.ts | 32 ++ src/models/browserComponentState.ts | 4 + src/models/browserGroupingsComponentState.ts | 17 ++ src/models/browserSendComponentState.ts | 8 + src/popup/accounts/home.component.ts | 10 +- src/popup/accounts/lock.component.ts | 11 +- src/popup/accounts/login.component.ts | 5 +- src/popup/accounts/set-password.component.ts | 8 +- src/popup/accounts/sso.component.ts | 3 - src/popup/accounts/two-factor.component.ts | 3 - .../update-temp-password.component.ts | 6 +- src/popup/app.component.ts | 59 ++-- .../components/action-buttons.component.ts | 6 +- .../generator/password-generator.component.ts | 2 +- src/popup/send/send-add-edit.component.ts | 9 +- src/popup/send/send-groupings.component.ts | 36 +-- src/popup/send/send-type.component.ts | 19 +- src/popup/services/services.module.ts | 58 ++-- .../settings/excluded-domains.component.ts | 10 +- src/popup/settings/options.component.ts | 103 ++----- src/popup/settings/premium.component.ts | 10 +- src/popup/settings/settings.component.ts | 32 +- src/popup/vault/add-edit.component.ts | 20 +- src/popup/vault/attachments.component.ts | 8 +- src/popup/vault/ciphers.component.ts | 17 +- src/popup/vault/current-tab.component.ts | 14 +- src/popup/vault/groupings.component.ts | 73 +++-- src/popup/vault/share.component.ts | 11 +- src/popup/vault/view.component.ts | 8 +- .../safari/SafariWebExtensionHandler.swift | 8 +- src/services/abstractions/state.service.ts | 33 +++ src/services/autofill.service.ts | 7 +- src/services/browserCrypto.service.ts | 2 +- src/services/browserPlatformUtils.service.ts | 7 +- src/services/state.service.ts | 81 ++++++ 41 files changed, 663 insertions(+), 527 deletions(-) create mode 100644 src/models/account.ts create mode 100644 src/models/browserComponentState.ts create mode 100644 src/models/browserGroupingsComponentState.ts create mode 100644 src/models/browserSendComponentState.ts create mode 100644 src/services/abstractions/state.service.ts create mode 100644 src/services/state.service.ts diff --git a/jslib b/jslib index 8fc3cf50d2..4722a287ec 160000 --- a/jslib +++ b/jslib @@ -1 +1 @@ -Subproject commit 8fc3cf50d2967212ffbbf0d57cac71d0774aa2a8 +Subproject commit 4722a287ec7212b8e67d02ec7ea1a9be0990ee6e diff --git a/src/background/idle.background.ts b/src/background/idle.background.ts index 9c59b8801a..fb51968ad4 100644 --- a/src/background/idle.background.ts +++ b/src/background/idle.background.ts @@ -1,8 +1,7 @@ import { NotificationsService } from "jslib-common/abstractions/notifications.service"; -import { StorageService } from "jslib-common/abstractions/storage.service"; import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service"; -import { ConstantsService } from "jslib-common/services/constants.service"; +import { StateService } from "../services/abstractions/state.service"; const IdleInterval = 60 * 5; // 5 minutes @@ -13,7 +12,7 @@ export default class IdleBackground { constructor( private vaultTimeoutService: VaultTimeoutService, - private storageService: StorageService, + private stateService: StateService, private notificationsService: NotificationsService ) { this.idle = chrome.idle || (browser != null ? browser.idle : null); @@ -42,12 +41,10 @@ export default class IdleBackground { this.idle.onStateChanged.addListener(async (newState: string) => { if (newState === "locked") { // If the screen is locked or the screensaver activates - const timeout = await this.storageService.get(ConstantsService.vaultTimeoutKey); + const timeout = await this.stateService.getVaultTimeout(); if (timeout === -2) { // On System Lock vault timeout option - const action = await this.storageService.get( - ConstantsService.vaultTimeoutActionKey - ); + const action = await this.stateService.getVaultTimeoutAction(); if (action === "logOut") { await this.vaultTimeoutService.logOut(); } else { diff --git a/src/background/main.background.ts b/src/background/main.background.ts index 5867d7285c..ebeb89d11d 100644 --- a/src/background/main.background.ts +++ b/src/background/main.background.ts @@ -1,6 +1,7 @@ import { CipherRepromptType } from "jslib-common/enums/cipherRepromptType"; import { CipherType } from "jslib-common/enums/cipherType"; +import { AccountFactory } from "jslib-common/models/domain/account"; import { CipherView } from "jslib-common/models/view/cipherView"; import { ApiService } from "jslib-common/services/api.service"; @@ -10,7 +11,6 @@ import { AuthService } from "jslib-common/services/auth.service"; import { CipherService } from "jslib-common/services/cipher.service"; import { CollectionService } from "jslib-common/services/collection.service"; import { ConsoleLogService } from "jslib-common/services/consoleLog.service"; -import { ConstantsService } from "jslib-common/services/constants.service"; import { ContainerService } from "jslib-common/services/container.service"; import { EnvironmentService } from "jslib-common/services/environment.service"; import { EventService } from "jslib-common/services/event.service"; @@ -19,17 +19,18 @@ import { FileUploadService } from "jslib-common/services/fileUpload.service"; import { FolderService } from "jslib-common/services/folder.service"; import { KeyConnectorService } from "jslib-common/services/keyConnector.service"; import { NotificationsService } from "jslib-common/services/notifications.service"; +import { OrganizationService } from "jslib-common/services/organization.service"; import { PasswordGenerationService } from "jslib-common/services/passwordGeneration.service"; import { PolicyService } from "jslib-common/services/policy.service"; +import { ProviderService } from "jslib-common/services/provider.service"; import { SearchService } from "jslib-common/services/search.service"; import { SendService } from "jslib-common/services/send.service"; import { SettingsService } from "jslib-common/services/settings.service"; -import { StateService } from "jslib-common/services/state.service"; +import { StateMigrationService } from "jslib-common/services/stateMigration.service"; import { SyncService } from "jslib-common/services/sync.service"; import { SystemService } from "jslib-common/services/system.service"; import { TokenService } from "jslib-common/services/token.service"; import { TotpService } from "jslib-common/services/totp.service"; -import { UserService } from "jslib-common/services/user.service"; import { UserVerificationService } from "jslib-common/services/userVerification.service"; import { WebCryptoFunctionService } from "jslib-common/services/webCryptoFunction.service"; @@ -51,19 +52,19 @@ import { KeyConnectorService as KeyConnectorServiceAbstraction } from "jslib-com import { LogService as LogServiceAbstraction } from "jslib-common/abstractions/log.service"; import { MessagingService as MessagingServiceAbstraction } from "jslib-common/abstractions/messaging.service"; import { NotificationsService as NotificationsServiceAbstraction } from "jslib-common/abstractions/notifications.service"; +import { OrganizationService as OrganizationServiceAbstraction } from "jslib-common/abstractions/organization.service"; import { PasswordGenerationService as PasswordGenerationServiceAbstraction } from "jslib-common/abstractions/passwordGeneration.service"; import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "jslib-common/abstractions/platformUtils.service"; import { PolicyService as PolicyServiceAbstraction } from "jslib-common/abstractions/policy.service"; +import { ProviderService as ProviderServiceAbstraction } from "jslib-common/abstractions/provider.service"; import { SearchService as SearchServiceAbstraction } from "jslib-common/abstractions/search.service"; import { SendService as SendServiceAbstraction } from "jslib-common/abstractions/send.service"; import { SettingsService as SettingsServiceAbstraction } from "jslib-common/abstractions/settings.service"; -import { StateService as StateServiceAbstraction } from "jslib-common/abstractions/state.service"; import { StorageService as StorageServiceAbstraction } from "jslib-common/abstractions/storage.service"; import { SyncService as SyncServiceAbstraction } from "jslib-common/abstractions/sync.service"; import { SystemService as SystemServiceAbstraction } from "jslib-common/abstractions/system.service"; import { TokenService as TokenServiceAbstraction } from "jslib-common/abstractions/token.service"; import { TotpService as TotpServiceAbstraction } from "jslib-common/abstractions/totp.service"; -import { UserService as UserServiceAbstraction } from "jslib-common/abstractions/user.service"; import { UserVerificationService as UserVerificationServiceAbstraction } from "jslib-common/abstractions/userVerification.service"; import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from "jslib-common/abstractions/vaultTimeout.service"; @@ -82,6 +83,8 @@ import TabsBackground from "./tabs.background"; import WebRequestBackground from "./webRequest.background"; import WindowsBackground from "./windows.background"; +import { StateService as StateServiceAbstraction } from "../services/abstractions/state.service"; + import { PopupUtilsService } from "../popup/services/popup-utils.service"; import AutofillService from "../services/autofill.service"; import { BrowserCryptoService } from "../services/browserCrypto.service"; @@ -89,15 +92,17 @@ import BrowserMessagingService from "../services/browserMessaging.service"; import BrowserPlatformUtilsService from "../services/browserPlatformUtils.service"; import BrowserStorageService from "../services/browserStorage.service"; import I18nService from "../services/i18n.service"; +import { StateService } from "../services/state.service"; import VaultTimeoutService from "../services/vaultTimeout.service"; +import { Account } from "../models/account"; + export default class MainBackground { messagingService: MessagingServiceAbstraction; storageService: StorageServiceAbstraction; secureStorageService: StorageServiceAbstraction; i18nService: I18nServiceAbstraction; platformUtilsService: PlatformUtilsServiceAbstraction; - constantsService: ConstantsService; logService: LogServiceAbstraction; cryptoService: CryptoServiceAbstraction; cryptoFunctionService: CryptoFunctionServiceAbstraction; @@ -105,7 +110,6 @@ export default class MainBackground { appIdService: AppIdServiceAbstraction; apiService: ApiServiceAbstraction; environmentService: EnvironmentServiceAbstraction; - userService: UserServiceAbstraction; settingsService: SettingsServiceAbstraction; cipherService: CipherServiceAbstraction; folderService: FolderServiceAbstraction; @@ -122,12 +126,15 @@ export default class MainBackground { searchService: SearchServiceAbstraction; notificationsService: NotificationsServiceAbstraction; stateService: StateServiceAbstraction; + stateMigrationService: StateMigrationService; systemService: SystemServiceAbstraction; eventService: EventServiceAbstraction; policyService: PolicyServiceAbstraction; popupUtilsService: PopupUtilsService; sendService: SendServiceAbstraction; fileUploadService: FileUploadServiceAbstraction; + organizationService: OrganizationServiceAbstraction; + providerService: ProviderServiceAbstraction; keyConnectorService: KeyConnectorServiceAbstraction; userVerificationService: UserVerificationServiceAbstraction; @@ -155,9 +162,22 @@ export default class MainBackground { // Services this.messagingService = new BrowserMessagingService(); this.storageService = new BrowserStorageService(); + this.secureStorageService = new BrowserStorageService(); + this.logService = new ConsoleLogService(false); + this.stateMigrationService = new StateMigrationService( + this.storageService, + this.secureStorageService + ); + this.stateService = new StateService( + this.storageService, + this.secureStorageService, + this.logService, + this.stateMigrationService, + new AccountFactory(Account) + ); this.platformUtilsService = new BrowserPlatformUtilsService( this.messagingService, - this.storageService, + this.stateService, (clipboardValue, clearMs) => { if (this.systemService != null) { this.systemService.clearClipboard(clipboardValue, clearMs); @@ -177,137 +197,139 @@ export default class MainBackground { } } ); - this.secureStorageService = new BrowserStorageService(); this.i18nService = new I18nService(BrowserApi.getUILanguage(window)); this.cryptoFunctionService = new WebCryptoFunctionService(window, this.platformUtilsService); - this.logService = new ConsoleLogService(false); this.cryptoService = new BrowserCryptoService( - this.storageService, - this.secureStorageService, this.cryptoFunctionService, this.platformUtilsService, - this.logService + this.logService, + this.stateService ); - this.tokenService = new TokenService(this.storageService); + this.tokenService = new TokenService(this.stateService); this.appIdService = new AppIdService(this.storageService); - this.environmentService = new EnvironmentService(this.storageService); + this.environmentService = new EnvironmentService(this.stateService); this.apiService = new ApiService( this.tokenService, this.platformUtilsService, this.environmentService, (expired: boolean) => this.logout(expired) ); - this.userService = new UserService(this.tokenService, this.storageService); - this.settingsService = new SettingsService(this.userService, this.storageService); + this.settingsService = new SettingsService(this.stateService); this.fileUploadService = new FileUploadService(this.logService, this.apiService); this.cipherService = new CipherService( this.cryptoService, - this.userService, this.settingsService, this.apiService, this.fileUploadService, - this.storageService, this.i18nService, () => this.searchService, - this.logService + this.logService, + this.stateService ); this.folderService = new FolderService( this.cryptoService, - this.userService, this.apiService, - this.storageService, this.i18nService, - this.cipherService + this.cipherService, + this.stateService ); this.collectionService = new CollectionService( this.cryptoService, - this.userService, - this.storageService, - this.i18nService + this.i18nService, + this.stateService ); this.searchService = new SearchService(this.cipherService, this.logService, this.i18nService); this.sendService = new SendService( this.cryptoService, - this.userService, this.apiService, this.fileUploadService, - this.storageService, this.i18nService, - this.cryptoFunctionService + this.cryptoFunctionService, + this.stateService + ); + this.organizationService = new OrganizationService(this.stateService); + this.policyService = new PolicyService( + this.stateService, + this.organizationService, + this.apiService ); - this.stateService = new StateService(); - this.policyService = new PolicyService(this.userService, this.storageService, this.apiService); this.keyConnectorService = new KeyConnectorService( - this.storageService, - this.userService, + this.stateService, this.cryptoService, this.apiService, this.tokenService, - this.logService + this.logService, + this.organizationService ); - this.vaultTimeoutService = new VaultTimeoutService( - this.cipherService, - this.folderService, - this.collectionService, - this.cryptoService, - this.platformUtilsService, - this.storageService, - this.messagingService, - this.searchService, - this.userService, - this.tokenService, - this.policyService, - this.keyConnectorService, - async () => { + + const vaultTimeoutServiceCallbacks = { + locked: async () => { if (this.notificationsService != null) { this.notificationsService.updateConnection(false); } await this.setIcon(); await this.refreshBadgeAndMenu(true); if (this.systemService != null) { - this.systemService.startProcessReload(); await this.systemService.clearPendingClipboard(); + await this.reloadProcess(); } }, - async () => await this.logout(false) + logout: async () => await this.logout(false), + }; + + this.vaultTimeoutService = new VaultTimeoutService( + this.cipherService, + this.folderService, + this.collectionService, + this.cryptoService, + this.platformUtilsService, + this.messagingService, + this.searchService, + this.tokenService, + this.policyService, + this.keyConnectorService, + this.stateService, + vaultTimeoutServiceCallbacks.locked, + vaultTimeoutServiceCallbacks.logout ); + this.providerService = new ProviderService(this.stateService); this.syncService = new SyncService( - this.userService, this.apiService, this.settingsService, this.folderService, this.cipherService, this.cryptoService, this.collectionService, - this.storageService, this.messagingService, this.policyService, this.sendService, this.logService, - this.tokenService, this.keyConnectorService, + this.stateService, + this.organizationService, + this.providerService, async (expired: boolean) => await this.logout(expired) ); this.eventService = new EventService( - this.storageService, this.apiService, - this.userService, this.cipherService, - this.logService + this.stateService, + this.logService, + this.organizationService ); this.passwordGenerationService = new PasswordGenerationService( this.cryptoService, - this.storageService, - this.policyService + this.policyService, + this.stateService ); this.totpService = new TotpService( - this.storageService, this.cryptoFunctionService, - this.logService + this.logService, + this.stateService ); this.autofillService = new AutofillService( this.cipherService, - this.userService, + this.stateService, this.totpService, this.eventService, this.logService @@ -321,36 +343,39 @@ export default class MainBackground { this.cryptoService ); this.notificationsService = new NotificationsService( - this.userService, this.syncService, this.appIdService, this.apiService, this.vaultTimeoutService, this.environmentService, () => this.logout(true), - this.logService + this.logService, + this.stateService ); this.popupUtilsService = new PopupUtilsService(this.platformUtilsService); - this.systemService = new SystemService( - this.storageService, - this.vaultTimeoutService, - this.messagingService, - this.platformUtilsService, - () => { - const forceWindowReload = - this.platformUtilsService.isSafari() || - this.platformUtilsService.isFirefox() || - this.platformUtilsService.isOpera(); - BrowserApi.reloadExtension(forceWindowReload ? window : null); - return Promise.resolve(); - } - ); + this.userVerificationService = new UserVerificationService( this.cryptoService, this.i18nService, this.apiService ); + const systemUtilsServiceReloadCallback = () => { + const forceWindowReload = + this.platformUtilsService.isSafari() || + this.platformUtilsService.isFirefox() || + this.platformUtilsService.isOpera(); + BrowserApi.reloadExtension(forceWindowReload ? window : null); + return Promise.resolve(); + }; + + this.systemService = new SystemService( + this.messagingService, + this.platformUtilsService, + systemUtilsServiceReloadCallback, + this.stateService + ); + // Other fields this.isSafari = this.platformUtilsService.isSafari(); this.sidebarAction = this.isSafari @@ -364,25 +389,24 @@ export default class MainBackground { this, this.autofillService, this.platformUtilsService as BrowserPlatformUtilsService, - this.storageService, this.i18nService, this.notificationsService, this.systemService, this.environmentService, this.messagingService, + this.stateService, this.logService ); this.nativeMessagingBackground = new NativeMessagingBackground( - this.storageService, this.cryptoService, this.cryptoFunctionService, - this.vaultTimeoutService, this.runtimeBackground, this.i18nService, - this.userService, this.messagingService, this.appIdService, - this.platformUtilsService + this.platformUtilsService, + this.stateService, + this.logService ); this.commandsBackground = new CommandsBackground( this, @@ -394,11 +418,10 @@ export default class MainBackground { this, this.autofillService, this.cipherService, - this.storageService, this.vaultTimeoutService, this.policyService, this.folderService, - this.userService + this.stateService ); this.tabsBackground = new TabsBackground(this, this.notificationBackground); @@ -413,7 +436,7 @@ export default class MainBackground { ); this.idleBackground = new IdleBackground( this.vaultTimeoutService, - this.storageService, + this.stateService, this.notificationsService ); this.webRequestBackground = new WebRequestBackground( @@ -424,32 +447,35 @@ export default class MainBackground { this.windowsBackground = new WindowsBackground(this); const that = this; + const backgroundMessagingService = new (class extends MessagingServiceAbstraction { + // AuthService should send the messages to the background not popup. + send = (subscriber: string, arg: any = {}) => { + const message = Object.assign({}, { command: subscriber }, arg); + that.runtimeBackground.processMessage(message, that, null); + }; + })(); this.authService = new AuthService( this.cryptoService, this.apiService, - this.userService, this.tokenService, this.appIdService, this.i18nService, this.platformUtilsService, - new (class extends MessagingServiceAbstraction { - // AuthService should send the messages to the background not popup. - send = (subscriber: string, arg: any = {}) => { - const message = Object.assign({}, { command: subscriber }, arg); - that.runtimeBackground.processMessage(message, that, null); - }; - })(), + backgroundMessagingService, this.vaultTimeoutService, this.logService, this.cryptoFunctionService, + this.keyConnectorService, this.environmentService, - this.keyConnectorService + this.stateService ); } async bootstrap() { this.containerService.attachToWindow(window); + await this.stateService.init(); + (this.authService as AuthService).init(); await (this.vaultTimeoutService as VaultTimeoutService).init(true); await (this.i18nService as I18nService).init(); @@ -480,7 +506,7 @@ export default class MainBackground { return; } - const isAuthenticated = await this.userService.isAuthenticated(); + const isAuthenticated = await this.stateService.getIsAuthenticated(); const locked = await this.vaultTimeoutService.isLocked(); let suffix = ""; @@ -499,9 +525,7 @@ export default class MainBackground { return; } - const menuDisabled = await this.storageService.get( - ConstantsService.disableContextMenuItemKey - ); + const menuDisabled = await this.stateService.getDisableContextMenuItem(); if (!menuDisabled) { await this.buildContextMenu(); } else { @@ -520,35 +544,39 @@ export default class MainBackground { } } - async logout(expired: boolean) { - await this.eventService.uploadEvents(); - const userId = await this.userService.getUserId(); + async logout(expired: boolean, userId?: string) { + if (!userId) { + userId = await this.stateService.getUserId(); + } + + await this.eventService.uploadEvents(userId); await Promise.all([ - this.eventService.clearEvents(), - this.syncService.setLastSync(new Date(0)), - this.tokenService.clearToken(), - this.cryptoService.clearKeys(), - this.userService.clear(), + this.eventService.clearEvents(userId), + this.syncService.setLastSync(new Date(0), userId), + this.tokenService.clearToken(userId), + this.cryptoService.clearKeys(userId), this.settingsService.clear(userId), this.cipherService.clear(userId), this.folderService.clear(userId), this.collectionService.clear(userId), this.policyService.clear(userId), - this.passwordGenerationService.clear(), - this.vaultTimeoutService.clear(), + this.passwordGenerationService.clear(userId), + this.vaultTimeoutService.clear(userId), this.keyConnectorService.clear(), ]); - this.searchService.clearIndex(); - this.messagingService.send("doneLoggingOut", { expired: expired }); + if (userId == null || userId === (await this.stateService.getUserId())) { + this.searchService.clearIndex(); + this.messagingService.send("doneLoggingOut", { expired: expired, userId: userId }); + } await this.setIcon(); await this.refreshBadgeAndMenu(); await this.reseedStorage(); this.notificationsService.updateConnection(false); - this.systemService.startProcessReload(); await this.systemService.clearPendingClipboard(); + await this.reloadProcess(); } async collectPageDetailsForContentScript(tab: any, sender: string, frameId: number = null) { @@ -591,9 +619,7 @@ export default class MainBackground { return; } - const currentVaultTimeout = await this.storageService.get( - ConstantsService.vaultTimeoutKey - ); + const currentVaultTimeout = await this.stateService.getVaultTimeout(); if (currentVaultTimeout == null) { return; } @@ -658,7 +684,7 @@ export default class MainBackground { title: this.i18nService.t("copyPassword"), }); - if (await this.userService.canAccessPremium()) { + if (await this.stateService.getCanAccessPremium()) { await this.contextMenusCreate({ type: "normal", id: "copy-totp", @@ -718,9 +744,7 @@ export default class MainBackground { }); } - const disableBadgeCounter = await this.storageService.get( - ConstantsService.disableBadgeCounterKey - ); + const disableBadgeCounter = await this.stateService.getDisableBadgeCounter(); let theText = ""; if (!disableBadgeCounter) { @@ -749,7 +773,7 @@ export default class MainBackground { private async loadMenuAndUpdateBadgeForNoAccessState(contextMenuEnabled: boolean) { if (contextMenuEnabled) { - const authed = await this.userService.isAuthenticated(); + const authed = await this.stateService.getIsAuthenticated(); await this.loadNoLoginsContextMenuOptions( this.i18nService.t(authed ? "unlockVaultMenu" : "loginToVaultMenu") ); @@ -830,7 +854,7 @@ export default class MainBackground { }); } - const canAccessPremium = await this.userService.canAccessPremium(); + const canAccessPremium = await this.stateService.getCanAccessPremium(); if (canAccessPremium && (cipher == null || (cipher.login.totp && cipher.login.totp !== ""))) { await this.contextMenusCreate({ type: "normal", @@ -957,4 +981,15 @@ export default class MainBackground { }); } } + + private async reloadProcess(): Promise { + const accounts = Object.keys(this.stateService.accounts.getValue()); + for (const userId of accounts) { + if (!(await this.vaultTimeoutService.isLocked(userId))) { + return; + } + } + + await this.systemService.startProcessReload(); + } } diff --git a/src/background/nativeMessaging.background.ts b/src/background/nativeMessaging.background.ts index 0b29f1b960..f231c15209 100644 --- a/src/background/nativeMessaging.background.ts +++ b/src/background/nativeMessaging.background.ts @@ -2,14 +2,14 @@ import { AppIdService } from "jslib-common/abstractions/appId.service"; import { CryptoService } from "jslib-common/abstractions/crypto.service"; import { CryptoFunctionService } from "jslib-common/abstractions/cryptoFunction.service"; import { I18nService } from "jslib-common/abstractions/i18n.service"; +import { LogService } from "jslib-common/abstractions/log.service"; import { MessagingService } from "jslib-common/abstractions/messaging.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; -import { StorageService } from "jslib-common/abstractions/storage.service"; -import { UserService } from "jslib-common/abstractions/user.service"; -import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service"; -import { ConstantsService } from "jslib-common/services/constants.service"; +import { StateService } from "jslib-common/abstractions/state.service"; import { Utils } from "jslib-common/misc/utils"; + +import { EncString } from "jslib-common/models/domain/encString"; import { SymmetricCryptoKey } from "jslib-common/models/domain/symmetricCryptoKey"; import { BrowserApi } from "../browser/browserApi"; @@ -18,6 +18,40 @@ import RuntimeBackground from "./runtime.background"; const MessageValidTimeout = 10 * 1000; const EncryptionAlgorithm = "sha1"; +type Message = { + command: string; + + // Filled in by this service + userId?: string; + timestamp?: number; + + // Used for sharing secret + publicKey?: string; +}; + +type OuterMessage = { + message: Message | EncString; + appId: string; +}; + +type ReceiveMessage = { + timestamp: number; + command: string; + response?: any; + + // Unlock key + keyB64?: string; +}; + +type ReceiveMessageOuter = { + command: string; + appId: string; + + // Should only have one of these. + message?: EncString; + sharedSecret?: string; +}; + export class NativeMessagingBackground { private connected = false; private connecting: boolean; @@ -32,18 +66,17 @@ export class NativeMessagingBackground { private validatingFingerprint: boolean; constructor( - private storageService: StorageService, private cryptoService: CryptoService, private cryptoFunctionService: CryptoFunctionService, - private vaultTimeoutService: VaultTimeoutService, private runtimeBackground: RuntimeBackground, private i18nService: I18nService, - private userService: UserService, private messagingService: MessagingService, private appIdService: AppIdService, - private platformUtilsService: PlatformUtilsService + private platformUtilsService: PlatformUtilsService, + private stateService: StateService, + private logService: LogService ) { - this.storageService.save(ConstantsService.biometricFingerprintValidated, false); + this.stateService.setBiometricFingerprintValidated(false); if (chrome?.permissions?.onAdded) { // Reload extension to activate nativeMessaging @@ -55,7 +88,7 @@ export class NativeMessagingBackground { async connect() { this.appId = await this.appIdService.getAppId(); - this.storageService.save(ConstantsService.biometricFingerprintValidated, false); + this.stateService.setBiometricFingerprintValidated(false); return new Promise((resolve, reject) => { this.port = BrowserApi.connectNative("com.8bit.bitwarden"); @@ -74,7 +107,7 @@ export class NativeMessagingBackground { connectedCallback(); } - this.port.onMessage.addListener(async (message: any) => { + this.port.onMessage.addListener(async (message: ReceiveMessageOuter) => { switch (message.command) { case "connected": connectedCallback(); @@ -107,7 +140,7 @@ export class NativeMessagingBackground { if (this.validatingFingerprint) { this.validatingFingerprint = false; - this.storageService.save(ConstantsService.biometricFingerprintValidated, true); + this.stateService.setBiometricFingerprintValidated(true); } this.sharedSecret = new SymmetricCryptoKey(decrypted); this.secureSetupResolve(); @@ -181,25 +214,26 @@ export class NativeMessagingBackground { }); } - async send(message: any) { + async send(message: Message) { if (!this.connected) { await this.connect(); } + message.userId = await this.stateService.getUserId(); + message.timestamp = Date.now(); + if (this.platformUtilsService.isSafari()) { - this.postMessage(message); + this.postMessage(message as any); } else { this.postMessage({ appId: this.appId, message: await this.encryptMessage(message) }); } } - async encryptMessage(message: any) { + async encryptMessage(message: Message) { if (this.sharedSecret == null) { await this.secureCommunication(); } - message.timestamp = Date.now(); - return await this.cryptoService.encrypt(JSON.stringify(message), this.sharedSecret); } @@ -209,13 +243,12 @@ export class NativeMessagingBackground { }); } - private postMessage(message: any) { + private postMessage(message: OuterMessage) { // Wrap in try-catch to when the port disconnected without triggering `onDisconnect`. try { this.port.postMessage(message); } catch (e) { - // tslint:disable-next-line - console.error("NativeMessaging port disconnected, disconnecting."); + this.logService.error("NativeMessaging port disconnected, disconnecting."); this.sharedSecret = null; this.privateKey = null; @@ -230,21 +263,22 @@ export class NativeMessagingBackground { } } - private async onMessage(rawMessage: any) { - let message = rawMessage; + private async onMessage(rawMessage: ReceiveMessage | EncString) { + let message = rawMessage as ReceiveMessage; if (!this.platformUtilsService.isSafari()) { - message = JSON.parse(await this.cryptoService.decryptToUtf8(rawMessage, this.sharedSecret)); + message = JSON.parse( + await this.cryptoService.decryptToUtf8(rawMessage as EncString, this.sharedSecret) + ); } if (Math.abs(message.timestamp - Date.now()) > MessageValidTimeout) { - // tslint:disable-next-line - console.error("NativeMessage is to old, ignoring."); + this.logService.error("NativeMessage is to old, ignoring."); return; } switch (message.command) { case "biometricUnlock": - await this.storageService.remove(ConstantsService.biometricAwaitingAcceptance); + await this.stateService.setBiometricAwaitingAcceptance(null); if (message.response === "not enabled") { this.messagingService.send("showDialog", { @@ -264,16 +298,16 @@ export class NativeMessagingBackground { break; } - const enabled = await this.storageService.get(ConstantsService.biometricUnlockKey); + const enabled = await this.stateService.getBiometricUnlock(); if (enabled === null || enabled === false) { if (message.response === "unlocked") { - await this.storageService.save(ConstantsService.biometricUnlockKey, true); + await this.stateService.setBiometricUnlock(true); } break; } - // Ignore unlock if already unlockeded - if (!this.vaultTimeoutService.biometricLocked) { + // Ignore unlock if already unlocked + if (!(await this.stateService.getBiometricLocked())) { break; } @@ -284,24 +318,25 @@ export class NativeMessagingBackground { // Verify key is correct by attempting to decrypt a secret try { - await this.cryptoService.getFingerprint(await this.userService.getUserId()); + await this.cryptoService.getFingerprint(await this.stateService.getUserId()); } catch (e) { - // tslint:disable-next-line - console.error("Unable to verify key:", e); + this.logService.error("Unable to verify key: " + e); await this.cryptoService.clearKey(); this.showWrongUserDialog(); - message = false; - break; + // Exit early + if (this.resolver) { + this.resolver(message); + } + return; } - this.vaultTimeoutService.biometricLocked = false; + await this.stateService.setBiometricLocked(false); this.runtimeBackground.processMessage({ command: "unlocked" }, null, null); } break; default: - // tslint:disable-next-line - console.error("NativeMessage, got unknown command: ", message.command); + this.logService.error("NativeMessage, got unknown command: " + message.command); } if (this.resolver) { @@ -317,13 +352,13 @@ export class NativeMessagingBackground { this.sendUnencrypted({ command: "setupEncryption", publicKey: Utils.fromBufferToB64(publicKey), - userId: await this.userService.getUserId(), + userId: await this.stateService.getUserId(), }); return new Promise((resolve, reject) => (this.secureSetupResolve = resolve)); } - private async sendUnencrypted(message: any) { + private async sendUnencrypted(message: Message) { if (!this.connected) { await this.connect(); } @@ -335,7 +370,7 @@ export class NativeMessagingBackground { private async showFingerprintDialog() { const fingerprint = ( - await this.cryptoService.getFingerprint(await this.userService.getUserId(), this.publicKey) + await this.cryptoService.getFingerprint(await this.stateService.getUserId(), this.publicKey) ).join(" "); this.messagingService.send("showDialog", { diff --git a/src/background/notification.background.ts b/src/background/notification.background.ts index 1896e3589e..0b03c02c25 100644 --- a/src/background/notification.background.ts +++ b/src/background/notification.background.ts @@ -7,12 +7,8 @@ import { LoginView } from "jslib-common/models/view/loginView"; import { CipherService } from "jslib-common/abstractions/cipher.service"; import { FolderService } from "jslib-common/abstractions/folder.service"; import { PolicyService } from "jslib-common/abstractions/policy.service"; -import { StorageService } from "jslib-common/abstractions/storage.service"; -import { UserService } from "jslib-common/abstractions/user.service"; import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service"; -import { ConstantsService } from "jslib-common/services/constants.service"; - import { AutofillService } from "../services/abstractions/autofill.service"; import { BrowserApi } from "../browser/browserApi"; @@ -23,6 +19,7 @@ import { Utils } from "jslib-common/misc/utils"; import { PolicyType } from "jslib-common/enums/policyType"; +import { StateService } from "../services/abstractions/state.service"; import AddChangePasswordQueueMessage from "./models/addChangePasswordQueueMessage"; import AddLoginQueueMessage from "./models/addLoginQueueMessage"; import AddLoginRuntimeMessage from "./models/addLoginRuntimeMessage"; @@ -37,11 +34,10 @@ export default class NotificationBackground { private main: MainBackground, private autofillService: AutofillService, private cipherService: CipherService, - private storageService: StorageService, private vaultTimeoutService: VaultTimeoutService, private policyService: PolicyService, private folderService: FolderService, - private userService: UserService + private stateService: StateService ) {} async init() { @@ -198,7 +194,7 @@ export default class NotificationBackground { } private async addLogin(loginInfo: AddLoginRuntimeMessage, tab: chrome.tabs.Tab) { - if (!(await this.userService.isAuthenticated())) { + if (!(await this.stateService.getIsAuthenticated())) { return; } @@ -212,9 +208,7 @@ export default class NotificationBackground { normalizedUsername = normalizedUsername.toLowerCase(); } - const disabledAddLogin = await this.storageService.get( - ConstantsService.disableAddLoginNotificationKey - ); + const disabledAddLogin = await this.stateService.getDisableAddLoginNotification(); if (await this.vaultTimeoutService.isLocked()) { if (disabledAddLogin) { return; @@ -246,9 +240,8 @@ export default class NotificationBackground { usernameMatches.length === 1 && usernameMatches[0].login.password !== loginInfo.password ) { - const disabledChangePassword = await this.storageService.get( - ConstantsService.disableChangedPasswordNotificationKey - ); + const disabledChangePassword = + await this.stateService.getDisableChangedPasswordNotification(); if (disabledChangePassword) { return; } diff --git a/src/background/runtime.background.ts b/src/background/runtime.background.ts index 9c999c00b3..5d1a31e142 100644 --- a/src/background/runtime.background.ts +++ b/src/background/runtime.background.ts @@ -3,9 +3,8 @@ import { I18nService } from "jslib-common/abstractions/i18n.service"; import { LogService } from "jslib-common/abstractions/log.service"; import { MessagingService } from "jslib-common/abstractions/messaging.service"; import { NotificationsService } from "jslib-common/abstractions/notifications.service"; -import { StorageService } from "jslib-common/abstractions/storage.service"; +import { StateService } from "jslib-common/abstractions/state.service"; import { SystemService } from "jslib-common/abstractions/system.service"; -import { ConstantsService } from "jslib-common/services/constants.service"; import { AutofillService } from "../services/abstractions/autofill.service"; import BrowserPlatformUtilsService from "../services/browserPlatformUtils.service"; @@ -27,12 +26,12 @@ export default class RuntimeBackground { private main: MainBackground, private autofillService: AutofillService, private platformUtilsService: BrowserPlatformUtilsService, - private storageService: StorageService, private i18nService: I18nService, private notificationsService: NotificationsService, private systemService: SystemService, private environmentService: EnvironmentService, private messagingService: MessagingService, + private stateService: StateService, private logService: LogService ) { // onInstalled listener must be wired up before anything else, so we do it in the ctor @@ -87,7 +86,7 @@ export default class RuntimeBackground { this.lockedVaultPendingNotifications.push(msg.data); break; case "logout": - await this.main.logout(msg.expired); + await this.main.logout(msg.expired, msg.userId); break; case "syncCompleted": if (msg.successfully) { @@ -220,29 +219,10 @@ export default class RuntimeBackground { if (this.onInstalledReason != null) { if (this.onInstalledReason === "install") { BrowserApi.createNewTab("https://bitwarden.com/browser-start/"); - await this.setDefaultSettings(); } this.onInstalledReason = null; } }, 100); } - - private async setDefaultSettings() { - // Default timeout option to "on restart". - const currentVaultTimeout = await this.storageService.get( - ConstantsService.vaultTimeoutKey - ); - if (currentVaultTimeout == null) { - await this.storageService.save(ConstantsService.vaultTimeoutKey, -1); - } - - // Default action to "lock". - const currentVaultTimeoutAction = await this.storageService.get( - ConstantsService.vaultTimeoutActionKey - ); - if (currentVaultTimeoutAction == null) { - await this.storageService.save(ConstantsService.vaultTimeoutActionKey, "lock"); - } - } } diff --git a/src/models/account.ts b/src/models/account.ts new file mode 100644 index 0000000000..3ff0797cc8 --- /dev/null +++ b/src/models/account.ts @@ -0,0 +1,32 @@ +import { + Account as BaseAccount, + AccountSettings as BaseAccountSettings, +} from "jslib-common/models/domain/account"; + +import { BrowserComponentState } from "./browserComponentState"; +import { BrowserGroupingsComponentState } from "./browserGroupingsComponentState"; +import { BrowserSendComponentState } from "./browserSendComponentState"; + +export class AccountSettings extends BaseAccountSettings { + vaultTimeout: number = -1; // On Restart +} + +export class Account extends BaseAccount { + settings?: AccountSettings = new AccountSettings(); + groupings?: BrowserGroupingsComponentState; + send?: BrowserSendComponentState; + ciphers?: BrowserComponentState; + sendType?: BrowserComponentState; + + constructor(init: Partial) { + super(init); + Object.assign(this.settings, { + ...new AccountSettings(), + ...this.settings, + }); + this.groupings = init?.groupings ?? new BrowserGroupingsComponentState(); + this.send = init?.send ?? new BrowserSendComponentState(); + this.ciphers = init?.ciphers ?? new BrowserComponentState(); + this.sendType = init?.sendType ?? new BrowserComponentState(); + } +} diff --git a/src/models/browserComponentState.ts b/src/models/browserComponentState.ts new file mode 100644 index 0000000000..d968726c41 --- /dev/null +++ b/src/models/browserComponentState.ts @@ -0,0 +1,4 @@ +export class BrowserComponentState { + scrollY: number; + searchText: string; +} diff --git a/src/models/browserGroupingsComponentState.ts b/src/models/browserGroupingsComponentState.ts new file mode 100644 index 0000000000..06ef4c8025 --- /dev/null +++ b/src/models/browserGroupingsComponentState.ts @@ -0,0 +1,17 @@ +import { CipherType } from "jslib-common/enums/cipherType"; +import { CipherView } from "jslib-common/models/view/cipherView"; +import { CollectionView } from "jslib-common/models/view/collectionView"; +import { FolderView } from "jslib-common/models/view/folderView"; +import { BrowserComponentState } from "./browserComponentState"; + +export class BrowserGroupingsComponentState extends BrowserComponentState { + favoriteCiphers: CipherView[]; + noFolderCiphers: CipherView[]; + ciphers: CipherView[]; + collectionCounts: Map; + folderCounts: Map; + typeCounts: Map; + folders: FolderView[]; + collections: CollectionView[]; + deletedCount: number; +} diff --git a/src/models/browserSendComponentState.ts b/src/models/browserSendComponentState.ts new file mode 100644 index 0000000000..5c2fb54a17 --- /dev/null +++ b/src/models/browserSendComponentState.ts @@ -0,0 +1,8 @@ +import { SendType } from "jslib-common/enums/sendType"; +import { SendView } from "jslib-common/models/view/sendView"; +import { BrowserComponentState } from "./browserComponentState"; + +export class BrowserSendComponentState extends BrowserComponentState { + sends: SendView[]; + typeCounts: Map; +} diff --git a/src/popup/accounts/home.component.ts b/src/popup/accounts/home.component.ts index 60c3b4c670..426722f942 100644 --- a/src/popup/accounts/home.component.ts +++ b/src/popup/accounts/home.component.ts @@ -1,12 +1,10 @@ import { Component } from "@angular/core"; -import { ConstantsService } from "jslib-common/services/constants.service"; - import { CryptoFunctionService } from "jslib-common/abstractions/cryptoFunction.service"; import { EnvironmentService } from "jslib-common/abstractions/environment.service"; import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; -import { StorageService } from "jslib-common/abstractions/storage.service"; +import { StateService } from "jslib-common/abstractions/state.service"; import { Utils } from "jslib-common/misc/utils"; @@ -18,7 +16,7 @@ export class HomeComponent { constructor( protected platformUtilsService: PlatformUtilsService, private passwordGenerationService: PasswordGenerationService, - private storageService: StorageService, + private stateService: StateService, private cryptoFunctionService: CryptoFunctionService, private environmentService: EnvironmentService ) {} @@ -41,8 +39,8 @@ export class HomeComponent { const codeVerifierHash = await this.cryptoFunctionService.hash(codeVerifier, "sha256"); const codeChallenge = Utils.fromBufferToUrlB64(codeVerifierHash); - await this.storageService.save(ConstantsService.ssoCodeVerifierKey, codeVerifier); - await this.storageService.save(ConstantsService.ssoStateKey, state); + await this.stateService.setSsoCodeVerifier(codeVerifier); + await this.stateService.setSsoState(state); let url = this.environmentService.getWebVaultUrl(); if (url == null) { diff --git a/src/popup/accounts/lock.component.ts b/src/popup/accounts/lock.component.ts index 84c8b9fa68..626f175b96 100644 --- a/src/popup/accounts/lock.component.ts +++ b/src/popup/accounts/lock.component.ts @@ -2,8 +2,6 @@ import { Component, NgZone } from "@angular/core"; import { Router } from "@angular/router"; import Swal from "sweetalert2"; -import { ConstantsService } from "jslib-common/services/constants.service"; - import { ApiService } from "jslib-common/abstractions/api.service"; import { CryptoService } from "jslib-common/abstractions/crypto.service"; import { EnvironmentService } from "jslib-common/abstractions/environment.service"; @@ -13,8 +11,6 @@ import { LogService } from "jslib-common/abstractions/log.service"; import { MessagingService } from "jslib-common/abstractions/messaging.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; import { StateService } from "jslib-common/abstractions/state.service"; -import { StorageService } from "jslib-common/abstractions/storage.service"; -import { UserService } from "jslib-common/abstractions/user.service"; import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service"; import { LockComponent as BaseLockComponent } from "jslib-angular/components/lock.component"; @@ -31,9 +27,7 @@ export class LockComponent extends BaseLockComponent { i18nService: I18nService, platformUtilsService: PlatformUtilsService, messagingService: MessagingService, - userService: UserService, cryptoService: CryptoService, - storageService: StorageService, vaultTimeoutService: VaultTimeoutService, environmentService: EnvironmentService, stateService: StateService, @@ -47,9 +41,7 @@ export class LockComponent extends BaseLockComponent { i18nService, platformUtilsService, messagingService, - userService, cryptoService, - storageService, vaultTimeoutService, environmentService, stateService, @@ -65,8 +57,7 @@ export class LockComponent extends BaseLockComponent { async ngOnInit() { await super.ngOnInit(); const disableAutoBiometricsPrompt = - (await this.storageService.get(ConstantsService.disableAutoBiometricsPromptKey)) ?? - true; + (await this.stateService.getDisableAutoBiometricsPrompt()) ?? true; window.setTimeout(async () => { document.getElementById(this.pinLock ? "pin" : "masterPassword").focus(); diff --git a/src/popup/accounts/login.component.ts b/src/popup/accounts/login.component.ts index 74983ea410..83c6bc4b6e 100644 --- a/src/popup/accounts/login.component.ts +++ b/src/popup/accounts/login.component.ts @@ -9,7 +9,6 @@ import { LogService } from "jslib-common/abstractions/log.service"; import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; import { StateService } from "jslib-common/abstractions/state.service"; -import { StorageService } from "jslib-common/abstractions/storage.service"; import { SyncService } from "jslib-common/abstractions/sync.service"; import { LoginComponent as BaseLoginComponent } from "jslib-angular/components/login.component"; @@ -19,6 +18,8 @@ import { LoginComponent as BaseLoginComponent } from "jslib-angular/components/l templateUrl: "login.component.html", }) export class LoginComponent extends BaseLoginComponent { + protected alwaysRememberEmail: boolean = true; + constructor( authService: AuthService, router: Router, @@ -28,7 +29,6 @@ export class LoginComponent extends BaseLoginComponent { protected environmentService: EnvironmentService, protected passwordGenerationService: PasswordGenerationService, protected cryptoFunctionService: CryptoFunctionService, - storageService: StorageService, syncService: SyncService, logService: LogService, ngZone: NgZone @@ -42,7 +42,6 @@ export class LoginComponent extends BaseLoginComponent { environmentService, passwordGenerationService, cryptoFunctionService, - storageService, logService, ngZone ); diff --git a/src/popup/accounts/set-password.component.ts b/src/popup/accounts/set-password.component.ts index adac7cfae9..6c42f956ec 100644 --- a/src/popup/accounts/set-password.component.ts +++ b/src/popup/accounts/set-password.component.ts @@ -9,8 +9,8 @@ import { MessagingService } from "jslib-common/abstractions/messaging.service"; import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; import { PolicyService } from "jslib-common/abstractions/policy.service"; +import { StateService } from "jslib-common/abstractions/state.service"; import { SyncService } from "jslib-common/abstractions/sync.service"; -import { UserService } from "jslib-common/abstractions/user.service"; import { SetPasswordComponent as BaseSetPasswordComponent } from "jslib-angular/components/set-password.component"; @@ -24,7 +24,7 @@ export class SetPasswordComponent extends BaseSetPasswordComponent { i18nService: I18nService, cryptoService: CryptoService, messagingService: MessagingService, - userService: UserService, + stateService: StateService, passwordGenerationService: PasswordGenerationService, platformUtilsService: PlatformUtilsService, policyService: PolicyService, @@ -36,14 +36,14 @@ export class SetPasswordComponent extends BaseSetPasswordComponent { i18nService, cryptoService, messagingService, - userService, passwordGenerationService, platformUtilsService, policyService, router, apiService, syncService, - route + route, + stateService ); } diff --git a/src/popup/accounts/sso.component.ts b/src/popup/accounts/sso.component.ts index 241f45f4d4..0c51f55302 100644 --- a/src/popup/accounts/sso.component.ts +++ b/src/popup/accounts/sso.component.ts @@ -11,7 +11,6 @@ import { LogService } from "jslib-common/abstractions/log.service"; import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; import { StateService } from "jslib-common/abstractions/state.service"; -import { StorageService } from "jslib-common/abstractions/storage.service"; import { SyncService } from "jslib-common/abstractions/sync.service"; import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service"; @@ -28,7 +27,6 @@ export class SsoComponent extends BaseSsoComponent { router: Router, i18nService: I18nService, route: ActivatedRoute, - storageService: StorageService, stateService: StateService, platformUtilsService: PlatformUtilsService, apiService: ApiService, @@ -44,7 +42,6 @@ export class SsoComponent extends BaseSsoComponent { router, i18nService, route, - storageService, stateService, platformUtilsService, apiService, diff --git a/src/popup/accounts/two-factor.component.ts b/src/popup/accounts/two-factor.component.ts index 231fcba701..92b0f1d993 100644 --- a/src/popup/accounts/two-factor.component.ts +++ b/src/popup/accounts/two-factor.component.ts @@ -13,7 +13,6 @@ import { LogService } from "jslib-common/abstractions/log.service"; import { MessagingService } from "jslib-common/abstractions/messaging.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; import { StateService } from "jslib-common/abstractions/state.service"; -import { StorageService } from "jslib-common/abstractions/storage.service"; import { SyncService } from "jslib-common/abstractions/sync.service"; import { TwoFactorComponent as BaseTwoFactorComponent } from "jslib-angular/components/two-factor.component"; @@ -42,7 +41,6 @@ export class TwoFactorComponent extends BaseTwoFactorComponent { private broadcasterService: BroadcasterService, private popupUtilsService: PopupUtilsService, stateService: StateService, - storageService: StorageService, route: ActivatedRoute, private messagingService: MessagingService, logService: LogService @@ -56,7 +54,6 @@ export class TwoFactorComponent extends BaseTwoFactorComponent { window, environmentService, stateService, - storageService, route, logService ); diff --git a/src/popup/accounts/update-temp-password.component.ts b/src/popup/accounts/update-temp-password.component.ts index 5496c6940e..4e150009c4 100644 --- a/src/popup/accounts/update-temp-password.component.ts +++ b/src/popup/accounts/update-temp-password.component.ts @@ -8,8 +8,8 @@ import { MessagingService } from "jslib-common/abstractions/messaging.service"; import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; import { PolicyService } from "jslib-common/abstractions/policy.service"; +import { StateService } from "jslib-common/abstractions/state.service"; import { SyncService } from "jslib-common/abstractions/sync.service"; -import { UserService } from "jslib-common/abstractions/user.service"; import { UpdateTempPasswordComponent as BaseUpdateTempPasswordComponent } from "jslib-angular/components/update-temp-password.component"; @@ -60,7 +60,7 @@ export class UpdateTempPasswordComponent extends BaseUpdateTempPasswordComponent passwordGenerationService: PasswordGenerationService, policyService: PolicyService, cryptoService: CryptoService, - userService: UserService, + stateService: StateService, messagingService: MessagingService, apiService: ApiService, syncService: SyncService, @@ -72,9 +72,9 @@ export class UpdateTempPasswordComponent extends BaseUpdateTempPasswordComponent passwordGenerationService, policyService, cryptoService, - userService, messagingService, apiService, + stateService, syncService, logService ); diff --git a/src/popup/app.component.ts b/src/popup/app.component.ts index 635b88504a..e020e0a337 100644 --- a/src/popup/app.component.ts +++ b/src/popup/app.component.ts @@ -11,10 +11,8 @@ import { I18nService } from "jslib-common/abstractions/i18n.service"; import { KeyConnectorService } from "jslib-common/abstractions/keyConnector.service"; import { MessagingService } from "jslib-common/abstractions/messaging.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; -import { StateService } from "jslib-common/abstractions/state.service"; -import { StorageService } from "jslib-common/abstractions/storage.service"; -import { ConstantsService } from "jslib-common/services/constants.service"; +import { StateService } from "../services/abstractions/state.service"; import { routerTransition } from "./app-routing.animations"; @@ -31,7 +29,6 @@ export class AppComponent implements OnInit { constructor( private toastrService: ToastrService, - private storageService: StorageService, private broadcasterService: BroadcasterService, private authService: AuthService, private i18nService: I18nService, @@ -49,14 +46,19 @@ export class AppComponent implements OnInit { if (BrowserApi.getBackgroundPage() == null) { return; } + let activeUserId: string = null; + this.stateService.activeAccount.subscribe((userId) => { + activeUserId = userId; + }); this.ngZone.runOutsideAngular(() => { - window.onmousemove = () => this.recordActivity(); - window.onmousedown = () => this.recordActivity(); - window.ontouchstart = () => this.recordActivity(); - window.onclick = () => this.recordActivity(); - window.onscroll = () => this.recordActivity(); - window.onkeypress = () => this.recordActivity(); + if (activeUserId != null) { + window.onmousedown = () => this.recordActivity(activeUserId); + window.ontouchstart = () => this.recordActivity(activeUserId); + window.onclick = () => this.recordActivity(activeUserId); + window.onscroll = () => this.recordActivity(activeUserId); + window.onkeypress = () => this.recordActivity(activeUserId); + } }); (window as any).bitwardenPopupMainMessageListener = async ( @@ -66,7 +68,7 @@ export class AppComponent implements OnInit { ) => { if (msg.command === "doneLoggingOut") { this.ngZone.run(async () => { - this.authService.logOut(() => { + this.authService.logOut(async () => { if (msg.expired) { this.showToast({ type: "warning", @@ -74,8 +76,12 @@ export class AppComponent implements OnInit { text: this.i18nService.t("loginExpired"), }); } - this.router.navigate(["home"]); - this.stateService.purge(); + + await this.stateService.clean({ userId: msg.userId }); + + if (this.stateService.activeAccount.getValue() == null) { + this.router.navigate(["home"]); + } }); this.changeDetectorRef.detectChanges(); }); @@ -84,10 +90,11 @@ export class AppComponent implements OnInit { this.router.navigate(["home"]); }); } else if (msg.command === "locked") { - this.stateService.purge(); - this.ngZone.run(() => { - this.router.navigate(["lock"]); - }); + if (msg.userId == null || msg.userId === (await this.stateService.getUserId())) { + this.ngZone.run(() => { + this.router.navigate(["lock"]); + }); + } } else if (msg.command === "showDialog") { await this.showDialog(msg); } else if (msg.command === "showToast") { @@ -120,7 +127,7 @@ export class AppComponent implements OnInit { BrowserApi.messageListener("app.component", (window as any).bitwardenPopupMainMessageListener); - this.router.events.subscribe((event) => { + this.router.events.subscribe(async (event) => { if (event instanceof NavigationEnd) { const url = event.urlAfterRedirects || event.url || ""; if ( @@ -128,15 +135,13 @@ export class AppComponent implements OnInit { (window as any).previousPopupUrl != null && (window as any).previousPopupUrl.startsWith("/tabs/") ) { - this.stateService.remove("GroupingsComponent"); - this.stateService.remove("GroupingsComponentScope"); - this.stateService.remove("CiphersComponent"); - this.stateService.remove("SendGroupingsComponent"); - this.stateService.remove("SendGroupingsComponentScope"); - this.stateService.remove("SendTypeComponent"); + await this.stateService.setBrowserGroupingComponentState(null); + await this.stateService.setBrowserCipherComponentState(null); + await this.stateService.setBrowserSendComponentState(null); + await this.stateService.setBrowserSendTypeComponentState(null); } if (url.startsWith("/tabs/")) { - this.stateService.remove("addEditCipherInfo"); + await this.stateService.setAddEditCipherInfo(null); } (window as any).previousPopupUrl = url; @@ -167,14 +172,14 @@ export class AppComponent implements OnInit { } } - private async recordActivity() { + private async recordActivity(userId: string) { const now = new Date().getTime(); if (this.lastActivity != null && now - this.lastActivity < 250) { return; } this.lastActivity = now; - this.storageService.save(ConstantsService.lastActiveKey, now); + this.stateService.setLastActive(now, { userId: userId }); } private showToast(msg: any) { diff --git a/src/popup/components/action-buttons.component.ts b/src/popup/components/action-buttons.component.ts index 1f073d1079..3dce9d6cea 100644 --- a/src/popup/components/action-buttons.component.ts +++ b/src/popup/components/action-buttons.component.ts @@ -10,8 +10,8 @@ import { EventService } from "jslib-common/abstractions/event.service"; import { I18nService } from "jslib-common/abstractions/i18n.service"; import { PasswordRepromptService } from "jslib-common/abstractions/passwordReprompt.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; +import { StateService } from "jslib-common/abstractions/state.service"; import { TotpService } from "jslib-common/abstractions/totp.service"; -import { UserService } from "jslib-common/abstractions/user.service"; @Component({ selector: "app-action-buttons", @@ -31,12 +31,12 @@ export class ActionButtonsComponent { private platformUtilsService: PlatformUtilsService, private eventService: EventService, private totpService: TotpService, - private userService: UserService, + private stateService: StateService, private passwordRepromptService: PasswordRepromptService ) {} async ngOnInit() { - this.userHasPremiumAccess = await this.userService.canAccessPremium(); + this.userHasPremiumAccess = await this.stateService.getCanAccessPremium(); } launchCipher() { diff --git a/src/popup/generator/password-generator.component.ts b/src/popup/generator/password-generator.component.ts index 266e7aadc4..5a0505a4b7 100644 --- a/src/popup/generator/password-generator.component.ts +++ b/src/popup/generator/password-generator.component.ts @@ -29,7 +29,7 @@ export class PasswordGeneratorComponent extends BasePasswordGeneratorComponent { async ngOnInit() { await super.ngOnInit(); - const addEditCipherInfo = await this.stateService.get("addEditCipherInfo"); + const addEditCipherInfo = await this.stateService.getAddEditCipherInfo(); if (addEditCipherInfo != null) { this.cipherState = addEditCipherInfo.cipher; } diff --git a/src/popup/send/send-add-edit.component.ts b/src/popup/send/send-add-edit.component.ts index fcfa434c29..f42e2a70db 100644 --- a/src/popup/send/send-add-edit.component.ts +++ b/src/popup/send/send-add-edit.component.ts @@ -13,7 +13,8 @@ import { MessagingService } from "jslib-common/abstractions/messaging.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; import { PolicyService } from "jslib-common/abstractions/policy.service"; import { SendService } from "jslib-common/abstractions/send.service"; -import { UserService } from "jslib-common/abstractions/user.service"; + +import { StateService } from "../../services/abstractions/state.service"; import { PopupUtilsService } from "../services/popup-utils.service"; @@ -36,7 +37,7 @@ export class SendAddEditComponent extends BaseAddEditComponent { constructor( i18nService: I18nService, platformUtilsService: PlatformUtilsService, - userService: UserService, + stateService: StateService, messagingService: MessagingService, policyService: PolicyService, environmentService: EnvironmentService, @@ -54,10 +55,10 @@ export class SendAddEditComponent extends BaseAddEditComponent { environmentService, datePipe, sendService, - userService, messagingService, policyService, - logService + logService, + stateService ); } diff --git a/src/popup/send/send-groupings.component.ts b/src/popup/send/send-groupings.component.ts index 323ff2ed90..cac5eb7830 100644 --- a/src/popup/send/send-groupings.component.ts +++ b/src/popup/send/send-groupings.component.ts @@ -14,16 +14,16 @@ import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.se import { PolicyService } from "jslib-common/abstractions/policy.service"; import { SearchService } from "jslib-common/abstractions/search.service"; import { SendService } from "jslib-common/abstractions/send.service"; -import { StateService } from "jslib-common/abstractions/state.service"; import { SyncService } from "jslib-common/abstractions/sync.service"; -import { UserService } from "jslib-common/abstractions/user.service"; +import { StateService } from "../../services/abstractions/state.service"; import { PopupUtilsService } from "../services/popup-utils.service"; import { SendType } from "jslib-common/enums/sendType"; +import { BrowserSendComponentState } from "src/models/browserSendComponentState"; + const ComponentId = "SendComponent"; -const ScopeStateId = ComponentId + "Scope"; @Component({ selector: "app-send-groupings", @@ -35,8 +35,7 @@ export class SendGroupingsComponent extends BaseSendComponent { // Send Type Calculations typeCounts = new Map(); // State Handling - state: any; - scopeState: any; + state: BrowserSendComponentState; private loadedTimeout: number; constructor( @@ -46,7 +45,6 @@ export class SendGroupingsComponent extends BaseSendComponent { environmentService: EnvironmentService, ngZone: NgZone, policyService: PolicyService, - userService: UserService, searchService: SearchService, private popupUtils: PopupUtilsService, private stateService: StateService, @@ -64,7 +62,6 @@ export class SendGroupingsComponent extends BaseSendComponent { ngZone, searchService, policyService, - userService, logService ); super.onSuccessfulLoad = async () => { @@ -79,13 +76,12 @@ export class SendGroupingsComponent extends BaseSendComponent { this.popupUtils.inSidebar(window) && this.platformUtilsService.isFirefox() ); // Clear state of Send Type Component - this.stateService.remove("SendTypeComponent"); + await this.stateService.setBrowserSendTypeComponentState(null); // Let super class finish await super.ngOnInit(); // Handle State Restore if necessary const restoredScopeState = await this.restoreState(); - this.state = (await this.stateService.get(ComponentId)) || {}; - if (this.state.searchText != null) { + if (this.state?.searchText != null) { this.searchText = this.state.searchText; } @@ -100,7 +96,7 @@ export class SendGroupingsComponent extends BaseSendComponent { } if (!this.syncService.syncInProgress || restoredScopeState) { - window.setTimeout(() => this.popupUtils.setContentScrollY(window, this.state.scrollY), 0); + window.setTimeout(() => this.popupUtils.setContentScrollY(window, this.state?.scrollY), 0); } // Load all sends if sync completed in background @@ -177,27 +173,23 @@ export class SendGroupingsComponent extends BaseSendComponent { this.state = { scrollY: this.popupUtils.getContentScrollY(window), searchText: this.searchText, - }; - await this.stateService.save(ComponentId, this.state); - - this.scopeState = { sends: this.sends, typeCounts: this.typeCounts, }; - await this.stateService.save(ScopeStateId, this.scopeState); + await this.stateService.setBrowserSendComponentState(this.state); } private async restoreState(): Promise { - this.scopeState = await this.stateService.get(ScopeStateId); - if (this.scopeState == null) { + this.state = await this.stateService.getBrowserSendComponentState(); + if (this.state == null) { return false; } - if (this.scopeState.sends != null) { - this.sends = this.scopeState.sends; + if (this.state.sends != null) { + this.sends = this.state.sends; } - if (this.scopeState.typeCounts != null) { - this.typeCounts = this.scopeState.typeCounts; + if (this.state.typeCounts != null) { + this.typeCounts = this.state.typeCounts; } return true; diff --git a/src/popup/send/send-type.component.ts b/src/popup/send/send-type.component.ts index bd9fd4e14c..81e9ce36fa 100644 --- a/src/popup/send/send-type.component.ts +++ b/src/popup/send/send-type.component.ts @@ -18,13 +18,14 @@ import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.se import { PolicyService } from "jslib-common/abstractions/policy.service"; import { SearchService } from "jslib-common/abstractions/search.service"; import { SendService } from "jslib-common/abstractions/send.service"; -import { StateService } from "jslib-common/abstractions/state.service"; -import { UserService } from "jslib-common/abstractions/user.service"; +import { StateService } from "../../services/abstractions/state.service"; import { PopupUtilsService } from "../services/popup-utils.service"; import { SendType } from "jslib-common/enums/sendType"; +import { BrowserComponentState } from "../../models/browserComponentState"; + const ComponentId = "SendTypeComponent"; @Component({ @@ -34,7 +35,7 @@ const ComponentId = "SendTypeComponent"; export class SendTypeComponent extends BaseSendComponent { groupingTitle: string; // State Handling - state: any; + state: BrowserComponentState; private refreshTimeout: number; private applySavedState = true; @@ -45,7 +46,6 @@ export class SendTypeComponent extends BaseSendComponent { environmentService: EnvironmentService, ngZone: NgZone, policyService: PolicyService, - userService: UserService, searchService: SearchService, private popupUtils: PopupUtilsService, private stateService: StateService, @@ -64,7 +64,6 @@ export class SendTypeComponent extends BaseSendComponent { ngZone, searchService, policyService, - userService, logService ); super.onSuccessfulLoad = async () => { @@ -80,8 +79,8 @@ export class SendTypeComponent extends BaseSendComponent { await super.ngOnInit(); this.route.queryParams.pipe(first()).subscribe(async (params) => { if (this.applySavedState) { - this.state = (await this.stateService.get(ComponentId)) || {}; - if (this.state.searchText != null) { + this.state = await this.stateService.getBrowserSendTypeComponentState(); + if (this.state?.searchText != null) { this.searchText = this.state.searchText; } } @@ -103,9 +102,9 @@ export class SendTypeComponent extends BaseSendComponent { // Restore state and remove reference if (this.applySavedState && this.state != null) { - window.setTimeout(() => this.popupUtils.setContentScrollY(window, this.state.scrollY), 0); + window.setTimeout(() => this.popupUtils.setContentScrollY(window, this.state?.scrollY), 0); } - this.stateService.remove(ComponentId); + this.stateService.setBrowserSendTypeComponentState(null); }); // Refresh Send list if sync completed in background @@ -167,6 +166,6 @@ export class SendTypeComponent extends BaseSendComponent { scrollY: this.popupUtils.getContentScrollY(window), searchText: this.searchText, }; - await this.stateService.save(ComponentId, this.state); + await this.stateService.setBrowserSendTypeComponentState(this.state); } } diff --git a/src/popup/services/services.module.ts b/src/popup/services/services.module.ts index d3a710cda4..1ace27669a 100644 --- a/src/popup/services/services.module.ts +++ b/src/popup/services/services.module.ts @@ -30,19 +30,20 @@ import { KeyConnectorService } from "jslib-common/abstractions/keyConnector.serv import { LogService as LogServiceAbstraction } from "jslib-common/abstractions/log.service"; import { MessagingService } from "jslib-common/abstractions/messaging.service"; import { NotificationsService } from "jslib-common/abstractions/notifications.service"; +import { OrganizationService } from "jslib-common/abstractions/organization.service"; import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service"; import { PasswordRepromptService as PasswordRepromptServiceAbstraction } from "jslib-common/abstractions/passwordReprompt.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; import { PolicyService } from "jslib-common/abstractions/policy.service"; +import { ProviderService } from "jslib-common/abstractions/provider.service"; import { SearchService as SearchServiceAbstraction } from "jslib-common/abstractions/search.service"; import { SendService } from "jslib-common/abstractions/send.service"; import { SettingsService } from "jslib-common/abstractions/settings.service"; -import { StateService as StateServiceAbstraction } from "jslib-common/abstractions/state.service"; -import { StorageService } from "jslib-common/abstractions/storage.service"; +import { StateService as BaseStateServiceAbstraction } from "jslib-common/abstractions/state.service"; +import { StorageService as StorageServiceAbstraction } from "jslib-common/abstractions/storage.service"; import { SyncService } from "jslib-common/abstractions/sync.service"; import { TokenService } from "jslib-common/abstractions/token.service"; import { TotpService } from "jslib-common/abstractions/totp.service"; -import { UserService } from "jslib-common/abstractions/user.service"; import { UserVerificationService } from "jslib-common/abstractions/userVerification.service"; import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service"; @@ -51,15 +52,15 @@ import BrowserMessagingService from "../../services/browserMessaging.service"; import { AuthService } from "jslib-common/services/auth.service"; import { ConsoleLogService } from "jslib-common/services/consoleLog.service"; -import { ConstantsService } from "jslib-common/services/constants.service"; import { SearchService } from "jslib-common/services/search.service"; -import { StateService } from "jslib-common/services/state.service"; import { PopupSearchService } from "./popup-search.service"; import { PopupUtilsService } from "./popup-utils.service"; import { ThemeType } from "jslib-common/enums/themeType"; +import { StateService as StateServiceAbstraction } from "../../services/abstractions/state.service"; + function getBgService(service: string) { return (): T => { const page = BrowserApi.getBackgroundPage(); @@ -72,12 +73,13 @@ const isPrivateMode = BrowserApi.getBackgroundPage() == null; export function initFactory( platformUtilsService: PlatformUtilsService, i18nService: I18nService, - storageService: StorageService, popupUtilsService: PopupUtilsService, stateService: StateServiceAbstraction, logService: LogServiceAbstraction ): Function { return async () => { + await stateService.init(); + if (!popupUtilsService.inPopup(window)) { window.document.body.classList.add("body-full"); } else if (window.screen.availHeight < 600) { @@ -87,21 +89,11 @@ export function initFactory( } if (!isPrivateMode) { - await stateService.save( - ConstantsService.disableFaviconKey, - await storageService.get(ConstantsService.disableFaviconKey) - ); - - await stateService.save( - ConstantsService.disableBadgeCounterKey, - await storageService.get(ConstantsService.disableBadgeCounterKey) - ); - const htmlEl = window.document.documentElement; const theme = await platformUtilsService.getEffectiveTheme(); htmlEl.classList.add("theme_" + theme); platformUtilsService.onDefaultSystemThemeChange(async (sysTheme) => { - const bwTheme = await storageService.get(ConstantsService.themeKey); + const bwTheme = await stateService.getTheme(); if (bwTheme == null || bwTheme === ThemeType.System) { htmlEl.classList.remove("theme_" + ThemeType.Light, "theme_" + ThemeType.Dark); htmlEl.classList.add("theme_" + sysTheme); @@ -143,7 +135,6 @@ export function initFactory( deps: [ PlatformUtilsService, I18nService, - StorageService, PopupUtilsService, StateServiceAbstraction, LogServiceAbstraction, @@ -161,7 +152,6 @@ export function initFactory( useFactory: getBgService("authService"), deps: [], }, - { provide: StateServiceAbstraction, useClass: StateService }, { provide: SearchServiceAbstraction, useFactory: ( @@ -226,15 +216,14 @@ export function initFactory( }, { provide: ApiService, useFactory: getBgService("apiService"), deps: [] }, { provide: SyncService, useFactory: getBgService("syncService"), deps: [] }, - { provide: UserService, useFactory: getBgService("userService"), deps: [] }, { provide: SettingsService, useFactory: getBgService("settingsService"), deps: [], }, { - provide: StorageService, - useFactory: getBgService("storageService"), + provide: StorageServiceAbstraction, + useFactory: getBgService("storageService"), deps: [], }, { provide: AppIdService, useFactory: getBgService("appIdService"), deps: [] }, @@ -271,6 +260,31 @@ export function initFactory( deps: [], }, { provide: PasswordRepromptServiceAbstraction, useClass: PasswordRepromptService }, + { + provide: OrganizationService, + useFactory: getBgService("organizationService"), + deps: [], + }, + { + provide: ProviderService, + useFactory: getBgService("providerService"), + deps: [], + }, + { + provide: "SECURE_STORAGE", + useFactory: getBgService("secureStorageService"), + deps: [], + }, + { + provide: StateServiceAbstraction, + useFactory: getBgService("stateService"), + deps: [], + }, + { + provide: BaseStateServiceAbstraction, + useExisting: StateServiceAbstraction, + deps: [], + }, ], }) export class ServicesModule {} diff --git a/src/popup/settings/excluded-domains.component.ts b/src/popup/settings/excluded-domains.component.ts index 6f856842a9..fecc788d73 100644 --- a/src/popup/settings/excluded-domains.component.ts +++ b/src/popup/settings/excluded-domains.component.ts @@ -2,12 +2,10 @@ import { Component, NgZone, OnDestroy, OnInit } from "@angular/core"; import { Router } from "@angular/router"; -import { ConstantsService } from "jslib-common/services/constants.service"; - import { BroadcasterService } from "jslib-common/abstractions/broadcaster.service"; import { I18nService } from "jslib-common/abstractions/i18n.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; -import { StorageService } from "jslib-common/abstractions/storage.service"; +import { StateService } from "jslib-common/abstractions/state.service"; import { BrowserApi } from "../../browser/browserApi"; @@ -30,7 +28,7 @@ export class ExcludedDomainsComponent implements OnInit, OnDestroy { loadCurrentUrisTimeout: number; constructor( - private storageService: StorageService, + private stateService: StateService, private i18nService: I18nService, private router: Router, private broadcasterService: BroadcasterService, @@ -39,7 +37,7 @@ export class ExcludedDomainsComponent implements OnInit, OnDestroy { ) {} async ngOnInit() { - const savedDomains = await this.storageService.get(ConstantsService.neverDomainsKey); + const savedDomains = await this.stateService.getNeverDomains(); if (savedDomains) { for (const uri of Object.keys(savedDomains)) { this.excludedDomains.push({ uri: uri, showCurrentUris: false }); @@ -96,7 +94,7 @@ export class ExcludedDomainsComponent implements OnInit, OnDestroy { savedDomains[validDomain] = null; } } - await this.storageService.save(ConstantsService.neverDomainsKey, savedDomains); + await this.stateService.setNeverDomains(savedDomains); this.router.navigate(["/tabs/settings"]); } diff --git a/src/popup/settings/options.component.ts b/src/popup/settings/options.component.ts index 3a6e3924f5..ca7757e3e9 100644 --- a/src/popup/settings/options.component.ts +++ b/src/popup/settings/options.component.ts @@ -6,11 +6,8 @@ import { UriMatchType } from "jslib-common/enums/uriMatchType"; import { I18nService } from "jslib-common/abstractions/i18n.service"; import { MessagingService } from "jslib-common/abstractions/messaging.service"; import { StateService } from "jslib-common/abstractions/state.service"; -import { StorageService } from "jslib-common/abstractions/storage.service"; import { TotpService } from "jslib-common/abstractions/totp.service"; -import { ConstantsService } from "jslib-common/services/constants.service"; - @Component({ selector: "app-options", templateUrl: "options.component.html", @@ -40,7 +37,6 @@ export class OptionsComponent implements OnInit { constructor( private messagingService: MessagingService, - private storageService: StorageService, private stateService: StateService, private totpService: TotpService, i18nService: I18nService @@ -76,136 +72,89 @@ export class OptionsComponent implements OnInit { } async ngOnInit() { - this.enableAutoFillOnPageLoad = await this.storageService.get( - ConstantsService.enableAutoFillOnPageLoadKey - ); + this.enableAutoFillOnPageLoad = await this.stateService.getEnableAutoFillOnPageLoad(); this.autoFillOnPageLoadDefault = - (await this.storageService.get(ConstantsService.autoFillOnPageLoadDefaultKey)) ?? - true; + (await this.stateService.getAutoFillOnPageLoadDefault()) ?? true; - this.disableAddLoginNotification = await this.storageService.get( - ConstantsService.disableAddLoginNotificationKey - ); + this.disableAddLoginNotification = await this.stateService.getDisableAddLoginNotification(); - this.disableChangedPasswordNotification = await this.storageService.get( - ConstantsService.disableChangedPasswordNotificationKey - ); + this.disableChangedPasswordNotification = + await this.stateService.getDisableChangedPasswordNotification(); - this.disableContextMenuItem = await this.storageService.get( - ConstantsService.disableContextMenuItemKey - ); + this.disableContextMenuItem = await this.stateService.getDisableContextMenuItem(); - this.dontShowCards = await this.storageService.get( - ConstantsService.dontShowCardsCurrentTab - ); - this.dontShowIdentities = await this.storageService.get( - ConstantsService.dontShowIdentitiesCurrentTab - ); + this.dontShowCards = await this.stateService.getDontShowCardsCurrentTab(); + this.dontShowIdentities = await this.stateService.getDontShowIdentitiesCurrentTab(); this.disableAutoTotpCopy = !(await this.totpService.isAutoCopyEnabled()); - this.disableFavicon = await this.storageService.get( - ConstantsService.disableFaviconKey - ); + this.disableFavicon = await this.stateService.getDisableFavicon(); - this.disableBadgeCounter = await this.storageService.get( - ConstantsService.disableBadgeCounterKey - ); + this.disableBadgeCounter = await this.stateService.getDisableBadgeCounter(); - this.theme = await this.storageService.get(ConstantsService.themeKey); + this.theme = await this.stateService.getTheme(); - const defaultUriMatch = await this.storageService.get( - ConstantsService.defaultUriMatch - ); + const defaultUriMatch = await this.stateService.getDefaultUriMatch(); this.defaultUriMatch = defaultUriMatch == null ? UriMatchType.Domain : defaultUriMatch; - this.clearClipboard = await this.storageService.get(ConstantsService.clearClipboardKey); + this.clearClipboard = await this.stateService.getClearClipboard(); } async updateAddLoginNotification() { - await this.storageService.save( - ConstantsService.disableAddLoginNotificationKey, - this.disableAddLoginNotification - ); + await this.stateService.setDisableAddLoginNotification(this.disableAddLoginNotification); } async updateChangedPasswordNotification() { - await this.storageService.save( - ConstantsService.disableChangedPasswordNotificationKey, + await this.stateService.setDisableChangedPasswordNotification( this.disableChangedPasswordNotification ); } async updateDisableContextMenuItem() { - await this.storageService.save( - ConstantsService.disableContextMenuItemKey, - this.disableContextMenuItem - ); + await this.stateService.setDisableContextMenuItem(this.disableContextMenuItem); this.messagingService.send("bgUpdateContextMenu"); } async updateAutoTotpCopy() { - await this.storageService.save( - ConstantsService.disableAutoTotpCopyKey, - this.disableAutoTotpCopy - ); + await this.stateService.setDisableAutoTotpCopy(this.disableAutoTotpCopy); } async updateAutoFillOnPageLoad() { - await this.storageService.save( - ConstantsService.enableAutoFillOnPageLoadKey, - this.enableAutoFillOnPageLoad - ); + await this.stateService.setEnableAutoFillOnPageLoad(this.enableAutoFillOnPageLoad); } async updateAutoFillOnPageLoadDefault() { - await this.storageService.save( - ConstantsService.autoFillOnPageLoadDefaultKey, - this.autoFillOnPageLoadDefault - ); + await this.stateService.setAutoFillOnPageLoadDefault(this.autoFillOnPageLoadDefault); } async updateDisableFavicon() { - await this.storageService.save(ConstantsService.disableFaviconKey, this.disableFavicon); - await this.stateService.save(ConstantsService.disableFaviconKey, this.disableFavicon); + await this.stateService.setDisableFavicon(this.disableFavicon); } async updateDisableBadgeCounter() { - await this.storageService.save( - ConstantsService.disableBadgeCounterKey, - this.disableBadgeCounter - ); - await this.stateService.save(ConstantsService.disableBadgeCounterKey, this.disableBadgeCounter); + await this.stateService.setDisableBadgeCounter(this.disableBadgeCounter); this.messagingService.send("bgUpdateContextMenu"); } async updateShowCards() { - await this.storageService.save(ConstantsService.dontShowCardsCurrentTab, this.dontShowCards); - await this.stateService.save(ConstantsService.dontShowCardsCurrentTab, this.dontShowCards); + await this.stateService.setDontShowCardsCurrentTab(this.dontShowCards); } async updateShowIdentities() { - await this.storageService.save( - ConstantsService.dontShowIdentitiesCurrentTab, - this.dontShowIdentities - ); - await this.stateService.save( - ConstantsService.dontShowIdentitiesCurrentTab, - this.dontShowIdentities - ); + await this.stateService.setDontShowIdentitiesCurrentTab(this.dontShowIdentities); } async saveTheme() { - await this.storageService.save(ConstantsService.themeKey, this.theme); + await this.stateService.setTheme(this.theme); window.setTimeout(() => window.location.reload(), 200); } async saveDefaultUriMatch() { - await this.storageService.save(ConstantsService.defaultUriMatch, this.defaultUriMatch); + await this.stateService.setDefaultUriMatch(this.defaultUriMatch); } async saveClearClipboard() { - await this.storageService.save(ConstantsService.clearClipboardKey, this.clearClipboard); + await this.stateService.setClearClipboard(this.clearClipboard); } } diff --git a/src/popup/settings/premium.component.ts b/src/popup/settings/premium.component.ts index db032c959c..190c28ba8e 100644 --- a/src/popup/settings/premium.component.ts +++ b/src/popup/settings/premium.component.ts @@ -5,7 +5,7 @@ import { ApiService } from "jslib-common/abstractions/api.service"; import { I18nService } from "jslib-common/abstractions/i18n.service"; import { LogService } from "jslib-common/abstractions/log.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; -import { UserService } from "jslib-common/abstractions/user.service"; +import { StateService } from "jslib-common/abstractions/state.service"; import { PremiumComponent as BasePremiumComponent } from "jslib-angular/components/premium.component"; @@ -20,11 +20,11 @@ export class PremiumComponent extends BasePremiumComponent { i18nService: I18nService, platformUtilsService: PlatformUtilsService, apiService: ApiService, - userService: UserService, - private currencyPipe: CurrencyPipe, - logService: LogService + stateService: StateService, + logService: LogService, + private currencyPipe: CurrencyPipe ) { - super(i18nService, platformUtilsService, apiService, userService, logService); + super(i18nService, platformUtilsService, apiService, logService, stateService); // Support old price string. Can be removed in future once all translations are properly updated. const thePrice = this.currencyPipe.transform(this.price, "$"); diff --git a/src/popup/settings/settings.component.ts b/src/popup/settings/settings.component.ts index 6a6108db09..c3b6fdeacb 100644 --- a/src/popup/settings/settings.component.ts +++ b/src/popup/settings/settings.component.ts @@ -7,16 +7,13 @@ import { BrowserApi } from "../../browser/browserApi"; import { DeviceType } from "jslib-common/enums/deviceType"; -import { ConstantsService } from "jslib-common/services/constants.service"; - import { CryptoService } from "jslib-common/abstractions/crypto.service"; import { EnvironmentService } from "jslib-common/abstractions/environment.service"; import { I18nService } from "jslib-common/abstractions/i18n.service"; import { KeyConnectorService } from "jslib-common/abstractions/keyConnector.service"; import { MessagingService } from "jslib-common/abstractions/messaging.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; -import { StorageService } from "jslib-common/abstractions/storage.service"; -import { UserService } from "jslib-common/abstractions/user.service"; +import { StateService } from "jslib-common/abstractions/state.service"; import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service"; import { PopupUtilsService } from "../services/popup-utils.service"; @@ -61,12 +58,11 @@ export class SettingsComponent implements OnInit { private platformUtilsService: PlatformUtilsService, private i18nService: I18nService, private vaultTimeoutService: VaultTimeoutService, - private storageService: StorageService, public messagingService: MessagingService, private router: Router, private environmentService: EnvironmentService, private cryptoService: CryptoService, - private userService: UserService, + private stateService: StateService, private popupUtilsService: PopupUtilsService, private modalService: ModalService, private keyConnectorService: KeyConnectorService @@ -112,7 +108,7 @@ export class SettingsComponent implements OnInit { this.saveVaultTimeout(value); }); - const action = await this.storageService.get(ConstantsService.vaultTimeoutActionKey); + const action = await this.stateService.getVaultTimeoutAction(); this.vaultTimeoutAction = action == null ? "lock" : action; const pinSet = await this.vaultTimeoutService.isPinLockSet(); @@ -121,8 +117,7 @@ export class SettingsComponent implements OnInit { this.supportsBiometric = await this.platformUtilsService.supportsBiometric(); this.biometric = await this.vaultTimeoutService.isBiometricLockSet(); this.disableAutoBiometricsPrompt = - (await this.storageService.get(ConstantsService.disableAutoBiometricsPromptKey)) ?? - true; + (await this.stateService.getDisableAutoBiometricsPrompt()) ?? true; this.showChangeMasterPass = !(await this.keyConnectorService.getUsesKeyConnector()); } @@ -250,14 +245,14 @@ export class SettingsComponent implements OnInit { allowOutsideClick: false, }); - await this.storageService.save(ConstantsService.biometricAwaitingAcceptance, true); + await this.stateService.setBiometricAwaitingAcceptance(true); await this.cryptoService.toggleKey(); await Promise.race([ - submitted.then((result) => { + submitted.then(async (result) => { if (result.dismiss === Swal.DismissReason.cancel) { this.biometric = false; - this.storageService.remove(ConstantsService.biometricAwaitingAcceptance); + await this.stateService.setBiometricAwaitingAcceptance(null); } }), this.platformUtilsService @@ -280,16 +275,13 @@ export class SettingsComponent implements OnInit { }), ]); } else { - await this.storageService.remove(ConstantsService.biometricUnlockKey); - this.vaultTimeoutService.biometricLocked = false; + await this.stateService.setBiometricUnlock(null); + await this.stateService.setBiometricLocked(false); } } async updateAutoBiometricsPrompt() { - await this.storageService.save( - ConstantsService.disableAutoBiometricsPromptKey, - this.disableAutoBiometricsPrompt - ); + await this.stateService.setDisableAutoBiometricsPrompt(this.disableAutoBiometricsPrompt); } async lock() { @@ -385,7 +377,9 @@ export class SettingsComponent implements OnInit { } async fingerprint() { - const fingerprint = await this.cryptoService.getFingerprint(await this.userService.getUserId()); + const fingerprint = await this.cryptoService.getFingerprint( + await this.stateService.getUserId() + ); const p = document.createElement("p"); p.innerText = this.i18nService.t("yourAccountsFingerprint") + ":"; const p2 = document.createElement("p"); diff --git a/src/popup/vault/add-edit.component.ts b/src/popup/vault/add-edit.component.ts index 841ba1dfcd..277fa9eb52 100644 --- a/src/popup/vault/add-edit.component.ts +++ b/src/popup/vault/add-edit.component.ts @@ -14,14 +14,11 @@ import { FolderService } from "jslib-common/abstractions/folder.service"; import { I18nService } from "jslib-common/abstractions/i18n.service"; import { LogService } from "jslib-common/abstractions/log.service"; import { MessagingService } from "jslib-common/abstractions/messaging.service"; +import { OrganizationService } from "jslib-common/abstractions/organization.service"; import { PasswordRepromptService } from "jslib-common/abstractions/passwordReprompt.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; import { PolicyService } from "jslib-common/abstractions/policy.service"; import { StateService } from "jslib-common/abstractions/state.service"; -import { StorageService } from "jslib-common/abstractions/storage.service"; -import { UserService } from "jslib-common/abstractions/user.service"; - -import { ConstantsService } from "jslib-common/services/constants.service"; import { PopupUtilsService } from "../services/popup-utils.service"; @@ -48,7 +45,6 @@ export class AddEditComponent extends BaseAddEditComponent { platformUtilsService: PlatformUtilsService, auditService: AuditService, stateService: StateService, - userService: UserService, collectionService: CollectionService, messagingService: MessagingService, private route: ActivatedRoute, @@ -57,9 +53,9 @@ export class AddEditComponent extends BaseAddEditComponent { eventService: EventService, policyService: PolicyService, private popupUtilsService: PopupUtilsService, - private storageService: StorageService, - logService: LogService, - passwordRepromptService: PasswordRepromptService + organizationService: OrganizationService, + passwordRepromptService: PasswordRepromptService, + logService: LogService ) { super( cipherService, @@ -68,13 +64,13 @@ export class AddEditComponent extends BaseAddEditComponent { platformUtilsService, auditService, stateService, - userService, collectionService, messagingService, eventService, policyService, + logService, passwordRepromptService, - logService + organizationService ); } @@ -149,7 +145,7 @@ export class AddEditComponent extends BaseAddEditComponent { await super.load(); this.showAutoFillOnPageLoadOptions = this.cipher.type === CipherType.Login && - (await this.storageService.get(ConstantsService.enableAutoFillOnPageLoadKey)); + (await this.stateService.getEnableAutoFillOnPageLoad()); } async submit(): Promise { @@ -194,7 +190,7 @@ export class AddEditComponent extends BaseAddEditComponent { async generatePassword(): Promise { const confirmed = await super.generatePassword(); if (confirmed) { - this.stateService.save("addEditCipherInfo", { + this.stateService.setAddEditCipherInfo({ cipher: this.cipher, collectionIds: this.collections == null diff --git a/src/popup/vault/attachments.component.ts b/src/popup/vault/attachments.component.ts index 5bd11dda7a..3ab26d497a 100644 --- a/src/popup/vault/attachments.component.ts +++ b/src/popup/vault/attachments.component.ts @@ -10,7 +10,7 @@ import { CryptoService } from "jslib-common/abstractions/crypto.service"; import { I18nService } from "jslib-common/abstractions/i18n.service"; import { LogService } from "jslib-common/abstractions/log.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; -import { UserService } from "jslib-common/abstractions/user.service"; +import { StateService } from "jslib-common/abstractions/state.service"; import { AttachmentsComponent as BaseAttachmentsComponent } from "jslib-angular/components/attachments.component"; @@ -25,22 +25,22 @@ export class AttachmentsComponent extends BaseAttachmentsComponent { cipherService: CipherService, i18nService: I18nService, cryptoService: CryptoService, - userService: UserService, platformUtilsService: PlatformUtilsService, apiService: ApiService, private location: Location, private route: ActivatedRoute, + stateService: StateService, logService: LogService ) { super( cipherService, i18nService, cryptoService, - userService, platformUtilsService, apiService, window, - logService + logService, + stateService ); } diff --git a/src/popup/vault/ciphers.component.ts b/src/popup/vault/ciphers.component.ts index 93e6ca38b3..4ca4d5bf3d 100644 --- a/src/popup/vault/ciphers.component.ts +++ b/src/popup/vault/ciphers.component.ts @@ -13,18 +13,19 @@ import { FolderService } from "jslib-common/abstractions/folder.service"; import { I18nService } from "jslib-common/abstractions/i18n.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; import { SearchService } from "jslib-common/abstractions/search.service"; -import { StateService } from "jslib-common/abstractions/state.service"; import { CipherType } from "jslib-common/enums/cipherType"; +import { TreeNode } from "jslib-common/models/domain/treeNode"; + import { CipherView } from "jslib-common/models/view/cipherView"; import { CollectionView } from "jslib-common/models/view/collectionView"; import { FolderView } from "jslib-common/models/view/folderView"; -import { TreeNode } from "jslib-common/models/domain/treeNode"; - import { CiphersComponent as BaseCiphersComponent } from "jslib-angular/components/ciphers.component"; +import { BrowserComponentState } from "src/models/browserComponentState"; +import { StateService } from "../../services/abstractions/state.service"; import { PopupUtilsService } from "../services/popup-utils.service"; const ComponentId = "CiphersComponent"; @@ -35,7 +36,7 @@ const ComponentId = "CiphersComponent"; }) export class CiphersComponent extends BaseCiphersComponent implements OnInit, OnDestroy { groupingTitle: string; - state: any; + state: BrowserComponentState; folderId: string = null; collectionId: string = null; type: CipherType = null; @@ -74,8 +75,8 @@ export class CiphersComponent extends BaseCiphersComponent implements OnInit, On this.searchTypeSearch = !this.platformUtilsService.isSafari(); this.route.queryParams.pipe(first()).subscribe(async (params) => { if (this.applySavedState) { - this.state = (await this.stateService.get(ComponentId)) || {}; - if (this.state.searchText) { + this.state = await this.stateService.getBrowserCipherComponentState(); + if (this.state?.searchText) { this.searchText = this.state.searchText; } } @@ -146,7 +147,7 @@ export class CiphersComponent extends BaseCiphersComponent implements OnInit, On 0 ); } - this.stateService.remove(ComponentId); + await this.stateService.setBrowserCipherComponentState(null); }); this.broadcasterService.subscribe(ComponentId, (message: any) => { @@ -241,6 +242,6 @@ export class CiphersComponent extends BaseCiphersComponent implements OnInit, On scrollY: this.popupUtils.getContentScrollY(window, this.scrollingContainer), searchText: this.searchText, }; - await this.stateService.save(ComponentId, this.state); + await this.stateService.setBrowserCipherComponentState(this.state); } } diff --git a/src/popup/vault/current-tab.component.ts b/src/popup/vault/current-tab.component.ts index afe8e6c0ee..3b7d2e55c4 100644 --- a/src/popup/vault/current-tab.component.ts +++ b/src/popup/vault/current-tab.component.ts @@ -14,11 +14,9 @@ import { I18nService } from "jslib-common/abstractions/i18n.service"; import { PasswordRepromptService } from "jslib-common/abstractions/passwordReprompt.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; import { SearchService } from "jslib-common/abstractions/search.service"; -import { StorageService } from "jslib-common/abstractions/storage.service"; +import { StateService } from "jslib-common/abstractions/state.service"; import { SyncService } from "jslib-common/abstractions/sync.service"; -import { ConstantsService } from "jslib-common/services/constants.service"; - import { AutofillService } from "../../services/abstractions/autofill.service"; import { PopupUtilsService } from "../services/popup-utils.service"; @@ -60,7 +58,7 @@ export class CurrentTabComponent implements OnInit, OnDestroy { private changeDetectorRef: ChangeDetectorRef, private syncService: SyncService, private searchService: SearchService, - private storageService: StorageService, + private stateService: StateService, private passwordRepromptService: PasswordRepromptService ) {} @@ -209,12 +207,8 @@ export class CurrentTabComponent implements OnInit, OnDestroy { }); const otherTypes: CipherType[] = []; - const dontShowCards = await this.storageService.get( - ConstantsService.dontShowCardsCurrentTab - ); - const dontShowIdentities = await this.storageService.get( - ConstantsService.dontShowIdentitiesCurrentTab - ); + const dontShowCards = await this.stateService.getDontShowCardsCurrentTab(); + const dontShowIdentities = await this.stateService.getDontShowIdentitiesCurrentTab(); if (!dontShowCards) { otherTypes.push(CipherType.Card); } diff --git a/src/popup/vault/groupings.component.ts b/src/popup/vault/groupings.component.ts index 584537b25d..e94842de33 100644 --- a/src/popup/vault/groupings.component.ts +++ b/src/popup/vault/groupings.component.ts @@ -18,17 +18,17 @@ import { CollectionService } from "jslib-common/abstractions/collection.service" import { FolderService } from "jslib-common/abstractions/folder.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; import { SearchService } from "jslib-common/abstractions/search.service"; -import { StateService } from "jslib-common/abstractions/state.service"; -import { StorageService } from "jslib-common/abstractions/storage.service"; import { SyncService } from "jslib-common/abstractions/sync.service"; -import { UserService } from "jslib-common/abstractions/user.service"; import { GroupingsComponent as BaseGroupingsComponent } from "jslib-angular/components/groupings.component"; +import { StateService } from "../../services/abstractions/state.service"; + import { PopupUtilsService } from "../services/popup-utils.service"; +import { BrowserGroupingsComponentState } from "src/models/browserGroupingsComponentState"; + const ComponentId = "GroupingsComponent"; -const ScopeStateId = ComponentId + "Scope"; @Component({ selector: "app-vault-groupings", @@ -53,8 +53,7 @@ export class GroupingsComponent extends BaseGroupingsComponent implements OnInit collectionCounts = new Map(); typeCounts = new Map(); searchText: string; - state: any; - scopeState: any; + state: BrowserGroupingsComponentState; showLeftHeader = true; searchPending = false; searchTypeSearch = false; @@ -72,22 +71,20 @@ export class GroupingsComponent extends BaseGroupingsComponent implements OnInit constructor( collectionService: CollectionService, folderService: FolderService, - storageService: StorageService, - userService: UserService, private cipherService: CipherService, private router: Router, private ngZone: NgZone, private broadcasterService: BroadcasterService, private changeDetectorRef: ChangeDetectorRef, private route: ActivatedRoute, - private stateService: StateService, private popupUtils: PopupUtilsService, private syncService: SyncService, private platformUtilsService: PlatformUtilsService, private searchService: SearchService, - private location: Location + private location: Location, + private browserStateService: StateService ) { - super(collectionService, folderService, storageService, userService); + super(collectionService, folderService, browserStateService); this.noFolderListSize = 100; } @@ -96,7 +93,7 @@ export class GroupingsComponent extends BaseGroupingsComponent implements OnInit this.showLeftHeader = !( this.popupUtils.inSidebar(window) && this.platformUtilsService.isFirefox() ); - this.stateService.remove("CiphersComponent"); + await this.browserStateService.setBrowserCipherComponentState(null); this.broadcasterService.subscribe(ComponentId, (message: any) => { this.ngZone.run(async () => { @@ -116,8 +113,8 @@ export class GroupingsComponent extends BaseGroupingsComponent implements OnInit const restoredScopeState = await this.restoreState(); this.route.queryParams.pipe(first()).subscribe(async (params) => { - this.state = (await this.stateService.get(ComponentId)) || {}; - if (this.state.searchText) { + this.state = await this.browserStateService.getBrowserGroupingComponentState(); + if (this.state?.searchText) { this.searchText = this.state.searchText; } else if (params.searchText) { this.searchText = params.searchText; @@ -135,7 +132,7 @@ export class GroupingsComponent extends BaseGroupingsComponent implements OnInit } if (!this.syncService.syncInProgress || restoredScopeState) { - window.setTimeout(() => this.popupUtils.setContentScrollY(window, this.state.scrollY), 0); + window.setTimeout(() => this.popupUtils.setContentScrollY(window, this.state?.scrollY), 0); } }); } @@ -320,10 +317,6 @@ export class GroupingsComponent extends BaseGroupingsComponent implements OnInit this.state = { scrollY: this.popupUtils.getContentScrollY(window), searchText: this.searchText, - }; - await this.stateService.save(ComponentId, this.state); - - this.scopeState = { favoriteCiphers: this.favoriteCiphers, noFolderCiphers: this.noFolderCiphers, ciphers: this.ciphers, @@ -334,41 +327,41 @@ export class GroupingsComponent extends BaseGroupingsComponent implements OnInit collections: this.collections, deletedCount: this.deletedCount, }; - await this.stateService.save(ScopeStateId, this.scopeState); + await this.browserStateService.setBrowserGroupingComponentState(this.state); } private async restoreState(): Promise { - this.scopeState = await this.stateService.get(ScopeStateId); - if (this.scopeState == null) { + this.state = await this.browserStateService.getBrowserGroupingComponentState(); + if (this.state == null) { return false; } - if (this.scopeState.favoriteCiphers != null) { - this.favoriteCiphers = this.scopeState.favoriteCiphers; + if (this.state.favoriteCiphers != null) { + this.favoriteCiphers = this.state.favoriteCiphers; } - if (this.scopeState.noFolderCiphers != null) { - this.noFolderCiphers = this.scopeState.noFolderCiphers; + if (this.state.noFolderCiphers != null) { + this.noFolderCiphers = this.state.noFolderCiphers; } - if (this.scopeState.ciphers != null) { - this.ciphers = this.scopeState.ciphers; + if (this.state.ciphers != null) { + this.ciphers = this.state.ciphers; } - if (this.scopeState.collectionCounts != null) { - this.collectionCounts = this.scopeState.collectionCounts; + if (this.state.collectionCounts != null) { + this.collectionCounts = this.state.collectionCounts; } - if (this.scopeState.folderCounts != null) { - this.folderCounts = this.scopeState.folderCounts; + if (this.state.folderCounts != null) { + this.folderCounts = this.state.folderCounts; } - if (this.scopeState.typeCounts != null) { - this.typeCounts = this.scopeState.typeCounts; + if (this.state.typeCounts != null) { + this.typeCounts = this.state.typeCounts; } - if (this.scopeState.folders != null) { - this.folders = this.scopeState.folders; + if (this.state.folders != null) { + this.folders = this.state.folders; } - if (this.scopeState.collections != null) { - this.collections = this.scopeState.collections; + if (this.state.collections != null) { + this.collections = this.state.collections; } - if (this.scopeState.deletedCiphers != null) { - this.deletedCount = this.scopeState.deletedCount; + if (this.state.deletedCount != null) { + this.deletedCount = this.state.deletedCount; } return true; diff --git a/src/popup/vault/share.component.ts b/src/popup/vault/share.component.ts index 29b30ea4b9..3264a7e6a4 100644 --- a/src/popup/vault/share.component.ts +++ b/src/popup/vault/share.component.ts @@ -1,4 +1,3 @@ -import { Location } from "@angular/common"; import { Component } from "@angular/core"; import { ActivatedRoute, Router } from "@angular/router"; @@ -8,8 +7,8 @@ import { CipherService } from "jslib-common/abstractions/cipher.service"; import { CollectionService } from "jslib-common/abstractions/collection.service"; import { I18nService } from "jslib-common/abstractions/i18n.service"; import { LogService } from "jslib-common/abstractions/log.service"; +import { OrganizationService } from "jslib-common/abstractions/organization.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; -import { UserService } from "jslib-common/abstractions/user.service"; import { ShareComponent as BaseShareComponent } from "jslib-angular/components/share.component"; @@ -22,19 +21,19 @@ export class ShareComponent extends BaseShareComponent { collectionService: CollectionService, platformUtilsService: PlatformUtilsService, i18nService: I18nService, - userService: UserService, + logService: LogService, cipherService: CipherService, private route: ActivatedRoute, private router: Router, - logService: LogService + organizationService: OrganizationService ) { super( collectionService, platformUtilsService, i18nService, - userService, cipherService, - logService + logService, + organizationService ); } diff --git a/src/popup/vault/view.component.ts b/src/popup/vault/view.component.ts index f00b715e6c..74bc840269 100644 --- a/src/popup/vault/view.component.ts +++ b/src/popup/vault/view.component.ts @@ -15,9 +15,9 @@ import { LogService } from "jslib-common/abstractions/log.service"; import { MessagingService } from "jslib-common/abstractions/messaging.service"; import { PasswordRepromptService } from "jslib-common/abstractions/passwordReprompt.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; +import { StateService } from "jslib-common/abstractions/state.service"; import { TokenService } from "jslib-common/abstractions/token.service"; import { TotpService } from "jslib-common/abstractions/totp.service"; -import { UserService } from "jslib-common/abstractions/user.service"; import { Cipher } from "jslib-common/models/domain/cipher"; import { LoginUriView } from "jslib-common/models/view/loginUriView"; @@ -57,7 +57,7 @@ export class ViewComponent extends BaseViewComponent { broadcasterService: BroadcasterService, ngZone: NgZone, changeDetectorRef: ChangeDetectorRef, - userService: UserService, + stateService: StateService, eventService: EventService, private autofillService: AutofillService, private messagingService: MessagingService, @@ -78,11 +78,11 @@ export class ViewComponent extends BaseViewComponent { broadcasterService, ngZone, changeDetectorRef, - userService, eventService, apiService, passwordRepromptService, - logService + logService, + stateService ); } diff --git a/src/safari/safari/SafariWebExtensionHandler.swift b/src/safari/safari/SafariWebExtensionHandler.swift index 153cb85dfe..5a12da3a02 100644 --- a/src/safari/safari/SafariWebExtensionHandler.swift +++ b/src/safari/safari/SafariWebExtensionHandler.swift @@ -115,13 +115,17 @@ class SafariWebExtensionHandler: NSObject, NSExtensionRequestHandling { } laContext.evaluateAccessControl(accessControl, operation: .useKeySign, localizedReason: "Bitwarden Safari Extension") { (success, error) in if success { - let passwordName = "key" + guard let userId = message?["userId"] as? String else { + return + } + let passwordName = userId + "_masterkey_biometric" var passwordLength: UInt32 = 0 var passwordPtr: UnsafeMutableRawPointer? = nil var status = SecKeychainFindGenericPassword(nil, UInt32(ServiceNameBiometric.utf8.count), ServiceNameBiometric, UInt32(passwordName.utf8.count), passwordName, &passwordLength, &passwordPtr, nil) if status != errSecSuccess { - status = SecKeychainFindGenericPassword(nil, UInt32(ServiceName.utf8.count), ServiceName, UInt32(passwordName.utf8.count), passwordName, &passwordLength, &passwordPtr, nil) + let fallbackName = "key" + status = SecKeychainFindGenericPassword(nil, UInt32(ServiceNameBiometric.utf8.count), ServiceNameBiometric, UInt32(fallbackName.utf8.count), fallbackName, &passwordLength, &passwordPtr, nil) } if status == errSecSuccess { diff --git a/src/services/abstractions/state.service.ts b/src/services/abstractions/state.service.ts new file mode 100644 index 0000000000..1cf50c9048 --- /dev/null +++ b/src/services/abstractions/state.service.ts @@ -0,0 +1,33 @@ +import { StateService as BaseStateServiceAbstraction } from "jslib-common/abstractions/state.service"; + +import { StorageOptions } from "jslib-common/models/domain/storageOptions"; + +import { Account } from "src/models/account"; +import { BrowserComponentState } from "src/models/browserComponentState"; +import { BrowserGroupingsComponentState } from "src/models/browserGroupingsComponentState"; +import { BrowserSendComponentState } from "src/models/browserSendComponentState"; + +export abstract class StateService extends BaseStateServiceAbstraction { + getBrowserGroupingComponentState: ( + options?: StorageOptions + ) => Promise; + setBrowserGroupingComponentState: ( + value: BrowserGroupingsComponentState, + options?: StorageOptions + ) => Promise; + getBrowserCipherComponentState: (options?: StorageOptions) => Promise; + setBrowserCipherComponentState: ( + value: BrowserComponentState, + options?: StorageOptions + ) => Promise; + getBrowserSendComponentState: (options?: StorageOptions) => Promise; + setBrowserSendComponentState: ( + value: BrowserSendComponentState, + options?: StorageOptions + ) => Promise; + getBrowserSendTypeComponentState: (options?: StorageOptions) => Promise; + setBrowserSendTypeComponentState: ( + value: BrowserComponentState, + options?: StorageOptions + ) => Promise; +} diff --git a/src/services/autofill.service.ts b/src/services/autofill.service.ts index 5f88f7f63f..d3a048e47f 100644 --- a/src/services/autofill.service.ts +++ b/src/services/autofill.service.ts @@ -2,7 +2,6 @@ import { CipherService } from "jslib-common/abstractions/cipher.service"; import { EventService } from "jslib-common/abstractions/event.service"; import { LogService } from "jslib-common/abstractions/log.service"; import { TotpService } from "jslib-common/abstractions/totp.service"; -import { UserService } from "jslib-common/abstractions/user.service"; import { AutofillService as AutofillServiceInterface } from "./abstractions/autofill.service"; @@ -18,6 +17,8 @@ import AutofillField from "../models/autofillField"; import AutofillPageDetails from "../models/autofillPageDetails"; import AutofillScript from "../models/autofillScript"; +import { StateService } from "../services/abstractions/state.service"; + import { BrowserApi } from "../browser/browserApi"; import { @@ -29,7 +30,7 @@ import { export default class AutofillService implements AutofillServiceInterface { constructor( private cipherService: CipherService, - private userService: UserService, + private stateService: StateService, private totpService: TotpService, private eventService: EventService, private logService: LogService @@ -74,7 +75,7 @@ export default class AutofillService implements AutofillServiceInterface { throw new Error("Nothing to auto-fill."); } - const canAccessPremium = await this.userService.canAccessPremium(); + const canAccessPremium = await this.stateService.getCanAccessPremium(); let didAutofill = false; options.pageDetails.forEach((pd: any) => { // make sure we're still on correct tab diff --git a/src/services/browserCrypto.service.ts b/src/services/browserCrypto.service.ts index 22fe131c0b..0eb0f94b17 100644 --- a/src/services/browserCrypto.service.ts +++ b/src/services/browserCrypto.service.ts @@ -1,4 +1,4 @@ -import { KeySuffixOptions } from "jslib-common/abstractions/storage.service"; +import { KeySuffixOptions } from "jslib-common/enums/keySuffixOptions"; import { CryptoService } from "jslib-common/services/crypto.service"; export class BrowserCryptoService extends CryptoService { diff --git a/src/services/browserPlatformUtils.service.ts b/src/services/browserPlatformUtils.service.ts index 31e8d32180..878c1e2624 100644 --- a/src/services/browserPlatformUtils.service.ts +++ b/src/services/browserPlatformUtils.service.ts @@ -6,9 +6,8 @@ import { ThemeType } from "jslib-common/enums/themeType"; import { MessagingService } from "jslib-common/abstractions/messaging.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; -import { StorageService } from "jslib-common/abstractions/storage.service"; -import { ConstantsService } from "jslib-common/services/constants.service"; +import { StateService } from "../services/abstractions/state.service"; const DialogPromiseExpiration = 600000; // 10 minutes @@ -25,7 +24,7 @@ export default class BrowserPlatformUtilsService implements PlatformUtilsService constructor( private messagingService: MessagingService, - private storageService: StorageService, + private stateService: StateService, private clipboardWriteCallback: (clipboardValue: string, clearMs: number) => void, private biometricCallback: () => Promise ) {} @@ -367,7 +366,7 @@ export default class BrowserPlatformUtilsService implements PlatformUtilsService } async getEffectiveTheme() { - const theme = await this.storageService.get(ConstantsService.themeKey); + const theme = (await this.stateService.getTheme()) as ThemeType; if (theme == null || theme === ThemeType.System) { return this.getDefaultSystemTheme(); } else { diff --git a/src/services/state.service.ts b/src/services/state.service.ts new file mode 100644 index 0000000000..bb7556a796 --- /dev/null +++ b/src/services/state.service.ts @@ -0,0 +1,81 @@ +import { StorageOptions } from "jslib-common/models/domain/storageOptions"; +import { StateService as BaseStateService } from "jslib-common/services/state.service"; + +import { Account } from "../models/account"; +import { BrowserComponentState } from "../models/browserComponentState"; +import { BrowserGroupingsComponentState } from "../models/browserGroupingsComponentState"; +import { BrowserSendComponentState } from "../models/browserSendComponentState"; +import { StateService as StateServiceAbstraction } from "./abstractions/state.service"; + +export class StateService extends BaseStateService implements StateServiceAbstraction { + async addAccount(account: Account) { + // Apply browser overrides to default account values + account = new Account(account); + await super.addAccount(account); + } + + async getBrowserGroupingComponentState( + options?: StorageOptions + ): Promise { + return (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.groupings; + } + + async setBrowserGroupingComponentState( + value: BrowserGroupingsComponentState, + options?: StorageOptions + ): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.groupings = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getBrowserCipherComponentState(options?: StorageOptions): Promise { + return (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.ciphers; + } + + async setBrowserCipherComponentState( + value: BrowserComponentState, + options?: StorageOptions + ): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.ciphers = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getBrowserSendComponentState(options?: StorageOptions): Promise { + return (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.send; + } + + async setBrowserSendComponentState( + value: BrowserSendComponentState, + options?: StorageOptions + ): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.send = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + async getBrowserSendTypeComponentState(options?: StorageOptions): Promise { + return (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.sendType; + } + + async setBrowserSendTypeComponentState( + value: BrowserComponentState, + options?: StorageOptions + ): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.sendType = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } +}