1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-19 09:43:23 +00:00

[Account Switching] Base changes for account switching (#2250)

* Pull in jslib

* Create new state models

* Create browser specific stateService

* Remove registration deprecated services, register stateService

* Replace usage of deprecated services (user, constants)

* Add missing properties to BrowserGroupingsComponentState

* Remove StorageService from initFactory

* Clear the correct state

* Add null check when restoring send-grouping state

* add remember email

* Initialize stateservice in services.module

* Fix 'lock now' not working

* Comment to remove setting defaults on install

* Pull jslib

* Remove setting defaults on install

* Bump jslib

* Pass the current userId to services when logging out

* Bump jslib

* Override vaultTimeout default on account addition

* Pull latest jslib

* Retrieve vaultTimeout from stateService

* Record activity per Account

* Add userId to logout and add fallback if not present

* Register AccountFactory

* Pass userId in messages

* Base changes for account switching di fixes (#2280)

* [bug] Null checks on Account init

* [bug] Use same stateService instance for all operations

We override the stateService in browser, but currently don't pull the background service into popup and allow jslib to create its own instance of the base StateService for jslib services.
This causes a split in in memory state between the three isntances that results in many errors, namely locking not working.

* [chore] Update jslib

* Pull in jslib

* Pull in jslib

* Pull in latest jslib to multiple stateservice inits

* Check vault states before executing processReload

* Adjust iterator

* Update native messaging to include the userId (#2290)

* Re-Add UserVerificationService

* Fix email not being remembered by base component

* Improve readability of reloadProcess

* Removed unneeded null check

* Fix constructor dependency (stateService)

* Added missing await

* Simplify dependency registration

* Fixed typos

* Reverted back to simple loop

* Use vaultTimeoutService to retrieve Timeout

Co-authored-by: Addison Beck <abeck@bitwarden.com>
Co-authored-by: Oscar Hinton <oscar@oscarhinton.com>
This commit is contained in:
Daniel James Smith
2022-01-27 22:22:51 +01:00
committed by GitHub
parent ade2a96239
commit bd770c90ed
41 changed files with 663 additions and 527 deletions

View File

@@ -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<number>(ConstantsService.vaultTimeoutKey);
const timeout = await this.stateService.getVaultTimeout();
if (timeout === -2) {
// On System Lock vault timeout option
const action = await this.storageService.get<string>(
ConstantsService.vaultTimeoutActionKey
);
const action = await this.stateService.getVaultTimeoutAction();
if (action === "logOut") {
await this.vaultTimeoutService.logOut();
} else {

View File

@@ -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<boolean>(
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<number>(
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<boolean>(
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<void> {
const accounts = Object.keys(this.stateService.accounts.getValue());
for (const userId of accounts) {
if (!(await this.vaultTimeoutService.isLocked(userId))) {
return;
}
}
await this.systemService.startProcessReload();
}
}

View File

@@ -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<void>((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", {

View File

@@ -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<boolean>(
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<boolean>(
ConstantsService.disableChangedPasswordNotificationKey
);
const disabledChangePassword =
await this.stateService.getDisableChangedPasswordNotification();
if (disabledChangePassword) {
return;
}

View File

@@ -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<number>(
ConstantsService.vaultTimeoutKey
);
if (currentVaultTimeout == null) {
await this.storageService.save(ConstantsService.vaultTimeoutKey, -1);
}
// Default action to "lock".
const currentVaultTimeoutAction = await this.storageService.get<string>(
ConstantsService.vaultTimeoutActionKey
);
if (currentVaultTimeoutAction == null) {
await this.storageService.save(ConstantsService.vaultTimeoutActionKey, "lock");
}
}
}