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)); + } +}