diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 828cca3c85..1854f1f6ff 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -3148,5 +3148,8 @@ }, "errorAssigningTargetFolder": { "message": "Error assigning target folder." + }, + "new": { + "message": "New" } } diff --git a/apps/browser/src/auth/popup/account-switching/current-account.component.ts b/apps/browser/src/auth/popup/account-switching/current-account.component.ts index 643c37b9aa..fcb772f024 100644 --- a/apps/browser/src/auth/popup/account-switching/current-account.component.ts +++ b/apps/browser/src/auth/popup/account-switching/current-account.component.ts @@ -1,13 +1,15 @@ -import { Location } from "@angular/common"; +import { CommonModule, Location } from "@angular/common"; import { Component } from "@angular/core"; import { ActivatedRoute, Router } from "@angular/router"; import { Observable, combineLatest, switchMap } from "rxjs"; +import { JslibModule } from "@bitwarden/angular/jslib.module"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { AvatarService } from "@bitwarden/common/auth/abstractions/avatar.service"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { UserId } from "@bitwarden/common/types/guid"; +import { AvatarModule } from "@bitwarden/components"; export type CurrentAccount = { id: UserId; @@ -20,6 +22,8 @@ export type CurrentAccount = { @Component({ selector: "app-current-account", templateUrl: "current-account.component.html", + standalone: true, + imports: [CommonModule, JslibModule, AvatarModule], }) export class CurrentAccountComponent { currentAccount$: Observable; diff --git a/apps/browser/src/auth/popup/login-via-auth-request.component.ts b/apps/browser/src/auth/popup/login-via-auth-request.component.ts index 158296058e..69d3204701 100644 --- a/apps/browser/src/auth/popup/login-via-auth-request.component.ts +++ b/apps/browser/src/auth/popup/login-via-auth-request.component.ts @@ -20,7 +20,6 @@ import { EnvironmentService } from "@bitwarden/common/platform/abstractions/envi import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; @@ -44,7 +43,6 @@ export class LoginViaAuthRequestComponent extends BaseLoginWithDeviceComponent { platformUtilsService: PlatformUtilsService, anonymousHubService: AnonymousHubService, validationService: ValidationService, - stateService: StateService, loginEmailService: LoginEmailServiceAbstraction, syncService: SyncService, deviceTrustService: DeviceTrustServiceAbstraction, @@ -67,12 +65,11 @@ export class LoginViaAuthRequestComponent extends BaseLoginWithDeviceComponent { platformUtilsService, anonymousHubService, validationService, - stateService, + accountService, loginEmailService, deviceTrustService, authRequestService, loginStrategyService, - accountService, ); super.onSuccessfulLogin = async () => { await syncService.fullSync(true); diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index 41f300270e..e77203f83e 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -109,6 +109,7 @@ import { DefaultConfigService } from "@bitwarden/common/platform/services/config import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service"; import { ContainerService } from "@bitwarden/common/platform/services/container.service"; import { EncryptServiceImplementation } from "@bitwarden/common/platform/services/cryptography/encrypt.service.implementation"; +import { MultithreadEncryptServiceImplementation } from "@bitwarden/common/platform/services/cryptography/multithread-encrypt.service.implementation"; import { Fido2AuthenticatorService } from "@bitwarden/common/platform/services/fido2/fido2-authenticator.service"; import { Fido2ClientService } from "@bitwarden/common/platform/services/fido2/fido2-client.service"; import { FileUploadService } from "@bitwarden/common/platform/services/file-upload/file-upload.service"; @@ -220,7 +221,6 @@ import { BrowserCryptoService } from "../platform/services/browser-crypto.servic import { BrowserEnvironmentService } from "../platform/services/browser-environment.service"; import BrowserLocalStorageService from "../platform/services/browser-local-storage.service"; import BrowserMemoryStorageService from "../platform/services/browser-memory-storage.service"; -import { BrowserMultithreadEncryptServiceImplementation } from "../platform/services/browser-multithread-encrypt.service.implementation"; import { BrowserScriptInjectorService } from "../platform/services/browser-script-injector.service"; import { DefaultBrowserStateService } from "../platform/services/default-browser-state.service"; import I18nService from "../platform/services/i18n.service"; @@ -479,14 +479,14 @@ export default class MainBackground { storageServiceProvider, ); - this.encryptService = flagEnabled("multithreadDecryption") - ? new BrowserMultithreadEncryptServiceImplementation( - this.cryptoFunctionService, - this.logService, - true, - this.offscreenDocumentService, - ) - : new EncryptServiceImplementation(this.cryptoFunctionService, this.logService, true); + this.encryptService = + flagEnabled("multithreadDecryption") && BrowserApi.isManifestVersion(2) + ? new MultithreadEncryptServiceImplementation( + this.cryptoFunctionService, + this.logService, + true, + ) + : new EncryptServiceImplementation(this.cryptoFunctionService, this.logService, true); this.singleUserStateProvider = new DefaultSingleUserStateProvider( storageServiceProvider, diff --git a/apps/browser/src/platform/offscreen-document/abstractions/offscreen-document.ts b/apps/browser/src/platform/offscreen-document/abstractions/offscreen-document.ts index 2a67d55c96..2d3c6a3e71 100644 --- a/apps/browser/src/platform/offscreen-document/abstractions/offscreen-document.ts +++ b/apps/browser/src/platform/offscreen-document/abstractions/offscreen-document.ts @@ -2,7 +2,6 @@ export type OffscreenDocumentExtensionMessage = { [key: string]: any; command: string; text?: string; - decryptRequest?: string; }; type OffscreenExtensionMessageEventParams = { @@ -14,7 +13,6 @@ export type OffscreenDocumentExtensionMessageHandlers = { [key: string]: ({ message, sender }: OffscreenExtensionMessageEventParams) => any; offscreenCopyToClipboard: ({ message }: OffscreenExtensionMessageEventParams) => any; offscreenReadFromClipboard: () => any; - offscreenDecryptItems: ({ message }: OffscreenExtensionMessageEventParams) => Promise; }; export interface OffscreenDocument { diff --git a/apps/browser/src/platform/offscreen-document/offscreen-document.spec.ts b/apps/browser/src/platform/offscreen-document/offscreen-document.spec.ts index 9d3cadbba8..933cd08c2e 100644 --- a/apps/browser/src/platform/offscreen-document/offscreen-document.spec.ts +++ b/apps/browser/src/platform/offscreen-document/offscreen-document.spec.ts @@ -1,25 +1,7 @@ -import { mock } from "jest-mock-extended"; - -import { Decryptable } from "@bitwarden/common/platform/interfaces/decryptable.interface"; -import { InitializerMetadata } from "@bitwarden/common/platform/interfaces/initializer-metadata.interface"; -import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; - import { flushPromises, sendExtensionRuntimeMessage } from "../../autofill/spec/testing-utils"; import { BrowserApi } from "../browser/browser-api"; import BrowserClipboardService from "../services/browser-clipboard.service"; -jest.mock( - "@bitwarden/common/platform/services/cryptography/multithread-encrypt.service.implementation", - () => ({ - MultithreadEncryptServiceImplementation: class MultithreadEncryptServiceImplementation { - getDecryptedItemsFromWorker = async ( - items: Decryptable[], - _key: SymmetricCryptoKey, - ): Promise => JSON.stringify(items); - }, - }), -); - describe("OffscreenDocument", () => { const browserApiMessageListenerSpy = jest.spyOn(BrowserApi, "messageListener"); const browserClipboardServiceCopySpy = jest.spyOn(BrowserClipboardService, "copy"); @@ -78,37 +60,5 @@ describe("OffscreenDocument", () => { expect(browserClipboardServiceReadSpy).toHaveBeenCalledWith(window); }); }); - - describe("handleOffscreenDecryptItems", () => { - it("returns an empty array as a string if the decrypt request is not present in the message", async () => { - let response: string | undefined; - sendExtensionRuntimeMessage( - { command: "offscreenDecryptItems" }, - mock(), - (res: string) => (response = res), - ); - await flushPromises(); - - expect(response).toBe("[]"); - }); - - it("decrypts the items and sends back the response as a string", async () => { - const items = [{ id: "test" }]; - const key = { id: "test" }; - const decryptRequest = JSON.stringify({ items, key }); - let response: string | undefined; - - sendExtensionRuntimeMessage( - { command: "offscreenDecryptItems", decryptRequest }, - mock(), - (res: string) => { - response = res; - }, - ); - await flushPromises(); - - expect(response).toBe(JSON.stringify(items)); - }); - }); }); }); diff --git a/apps/browser/src/platform/offscreen-document/offscreen-document.ts b/apps/browser/src/platform/offscreen-document/offscreen-document.ts index 509193d5ee..4994a6e9ba 100644 --- a/apps/browser/src/platform/offscreen-document/offscreen-document.ts +++ b/apps/browser/src/platform/offscreen-document/offscreen-document.ts @@ -1,35 +1,21 @@ import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service"; -import { MultithreadEncryptServiceImplementation } from "@bitwarden/common/platform/services/cryptography/multithread-encrypt.service.implementation"; -import { WebCryptoFunctionService } from "@bitwarden/common/platform/services/web-crypto-function.service"; import { BrowserApi } from "../browser/browser-api"; import BrowserClipboardService from "../services/browser-clipboard.service"; import { - OffscreenDocument as OffscreenDocumentInterface, OffscreenDocumentExtensionMessage, OffscreenDocumentExtensionMessageHandlers, + OffscreenDocument as OffscreenDocumentInterface, } from "./abstractions/offscreen-document"; class OffscreenDocument implements OffscreenDocumentInterface { - private readonly consoleLogService: ConsoleLogService; - private encryptService: MultithreadEncryptServiceImplementation; + private consoleLogService: ConsoleLogService = new ConsoleLogService(false); private readonly extensionMessageHandlers: OffscreenDocumentExtensionMessageHandlers = { offscreenCopyToClipboard: ({ message }) => this.handleOffscreenCopyToClipboard(message), offscreenReadFromClipboard: () => this.handleOffscreenReadFromClipboard(), - offscreenDecryptItems: ({ message }) => this.handleOffscreenDecryptItems(message), }; - constructor() { - const cryptoFunctionService = new WebCryptoFunctionService(self); - this.consoleLogService = new ConsoleLogService(false); - this.encryptService = new MultithreadEncryptServiceImplementation( - cryptoFunctionService, - this.consoleLogService, - true, - ); - } - /** * Initializes the offscreen document extension. */ @@ -53,23 +39,6 @@ class OffscreenDocument implements OffscreenDocumentInterface { return await BrowserClipboardService.read(self); } - /** - * Decrypts the items in the message using the encrypt service. - * - * @param message - The extension message containing the items to decrypt - */ - private async handleOffscreenDecryptItems( - message: OffscreenDocumentExtensionMessage, - ): Promise { - const { decryptRequest } = message; - if (!decryptRequest) { - return "[]"; - } - - const request = JSON.parse(decryptRequest); - return await this.encryptService.getDecryptedItemsFromWorker(request.items, request.key); - } - /** * Sets up the listener for extension messages. */ diff --git a/apps/browser/src/platform/popup/components/pop-out.component.html b/apps/browser/src/platform/popup/components/pop-out.component.html index 73bf76941d..c3f1f8ca15 100644 --- a/apps/browser/src/platform/popup/components/pop-out.component.html +++ b/apps/browser/src/platform/popup/components/pop-out.component.html @@ -1,5 +1,15 @@ - + + + + diff --git a/apps/browser/src/platform/popup/components/pop-out.component.ts b/apps/browser/src/platform/popup/components/pop-out.component.ts index 154bb55a0c..54bed7aa34 100644 --- a/apps/browser/src/platform/popup/components/pop-out.component.ts +++ b/apps/browser/src/platform/popup/components/pop-out.component.ts @@ -2,7 +2,10 @@ import { CommonModule } from "@angular/common"; import { Component, Input, OnInit } from "@angular/core"; import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { IconButtonModule } from "@bitwarden/components"; import BrowserPopupUtils from "../browser-popup-utils"; @@ -10,14 +13,20 @@ import BrowserPopupUtils from "../browser-popup-utils"; selector: "app-pop-out", templateUrl: "pop-out.component.html", standalone: true, - imports: [CommonModule, JslibModule], + imports: [CommonModule, JslibModule, IconButtonModule], }) export class PopOutComponent implements OnInit { @Input() show = true; + useRefreshVariant = false; - constructor(private platformUtilsService: PlatformUtilsService) {} + constructor( + private platformUtilsService: PlatformUtilsService, + private configService: ConfigService, + ) {} + + async ngOnInit() { + this.useRefreshVariant = await this.configService.getFeatureFlag(FeatureFlag.ExtensionRefresh); - ngOnInit() { if (this.show) { if ( (BrowserPopupUtils.inSidebar(window) && this.platformUtilsService.isFirefox()) || diff --git a/apps/browser/src/platform/services/browser-multithread-encrypt.service.implementation.spec.ts b/apps/browser/src/platform/services/browser-multithread-encrypt.service.implementation.spec.ts deleted file mode 100644 index db5b3df7a3..0000000000 --- a/apps/browser/src/platform/services/browser-multithread-encrypt.service.implementation.spec.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { mock, MockProxy } from "jest-mock-extended"; - -import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; -import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { EncryptionType } from "@bitwarden/common/platform/enums"; -import { Decryptable } from "@bitwarden/common/platform/interfaces/decryptable.interface"; -import { InitializerMetadata } from "@bitwarden/common/platform/interfaces/initializer-metadata.interface"; -import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; -import { InitializerKey } from "@bitwarden/common/platform/services/cryptography/initializer-key"; -import { makeStaticByteArray } from "@bitwarden/common/spec"; - -import { BrowserApi } from "../browser/browser-api"; -import { OffscreenDocumentService } from "../offscreen-document/abstractions/offscreen-document"; - -import { BrowserMultithreadEncryptServiceImplementation } from "./browser-multithread-encrypt.service.implementation"; - -describe("BrowserMultithreadEncryptServiceImplementation", () => { - let cryptoFunctionServiceMock: MockProxy; - let logServiceMock: MockProxy; - let offscreenDocumentServiceMock: MockProxy; - let encryptService: BrowserMultithreadEncryptServiceImplementation; - const manifestVersionSpy = jest.spyOn(BrowserApi, "manifestVersion", "get"); - const sendMessageWithResponseSpy = jest.spyOn(BrowserApi, "sendMessageWithResponse"); - const encType = EncryptionType.AesCbc256_HmacSha256_B64; - const key = new SymmetricCryptoKey(makeStaticByteArray(64, 100), encType); - const items: Decryptable[] = [ - { - decrypt: jest.fn(), - initializerKey: InitializerKey.Cipher, - }, - ]; - - beforeEach(() => { - cryptoFunctionServiceMock = mock(); - logServiceMock = mock(); - offscreenDocumentServiceMock = mock({ - withDocument: jest.fn((_, __, callback) => callback() as any), - }); - encryptService = new BrowserMultithreadEncryptServiceImplementation( - cryptoFunctionServiceMock, - logServiceMock, - false, - offscreenDocumentServiceMock, - ); - manifestVersionSpy.mockReturnValue(3); - sendMessageWithResponseSpy.mockResolvedValue(JSON.stringify([])); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - it("decrypts items using web workers if the chrome.offscreen API is not supported", async () => { - manifestVersionSpy.mockReturnValue(2); - - await encryptService.decryptItems([], key); - - expect(offscreenDocumentServiceMock.withDocument).not.toHaveBeenCalled(); - }); - - it("decrypts items using the chrome.offscreen API if it is supported", async () => { - sendMessageWithResponseSpy.mockResolvedValue(JSON.stringify(items)); - - await encryptService.decryptItems(items, key); - - expect(offscreenDocumentServiceMock.withDocument).toHaveBeenCalledWith( - [chrome.offscreen.Reason.WORKERS], - "Use web worker to decrypt items.", - expect.any(Function), - ); - expect(BrowserApi.sendMessageWithResponse).toHaveBeenCalledWith("offscreenDecryptItems", { - decryptRequest: expect.any(String), - }); - }); - - it("returns an empty array if the passed items are not defined", async () => { - const result = await encryptService.decryptItems(null, key); - - expect(result).toEqual([]); - }); - - it("returns an empty array if the offscreen document message returns an empty value", async () => { - sendMessageWithResponseSpy.mockResolvedValue(""); - - const result = await encryptService.decryptItems(items, key); - - expect(result).toEqual([]); - }); - - it("returns an empty array if the offscreen document message returns an empty array", async () => { - sendMessageWithResponseSpy.mockResolvedValue("[]"); - - const result = await encryptService.decryptItems(items, key); - - expect(result).toEqual([]); - }); -}); diff --git a/apps/browser/src/platform/services/browser-multithread-encrypt.service.implementation.ts b/apps/browser/src/platform/services/browser-multithread-encrypt.service.implementation.ts deleted file mode 100644 index ace5015c8e..0000000000 --- a/apps/browser/src/platform/services/browser-multithread-encrypt.service.implementation.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; -import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { Decryptable } from "@bitwarden/common/platform/interfaces/decryptable.interface"; -import { InitializerMetadata } from "@bitwarden/common/platform/interfaces/initializer-metadata.interface"; -import { Utils } from "@bitwarden/common/platform/misc/utils"; -import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; -import { MultithreadEncryptServiceImplementation } from "@bitwarden/common/platform/services/cryptography/multithread-encrypt.service.implementation"; - -import { BrowserApi } from "../browser/browser-api"; -import { OffscreenDocumentService } from "../offscreen-document/abstractions/offscreen-document"; - -export class BrowserMultithreadEncryptServiceImplementation extends MultithreadEncryptServiceImplementation { - constructor( - cryptoFunctionService: CryptoFunctionService, - logService: LogService, - logMacFailures: boolean, - private offscreenDocumentService: OffscreenDocumentService, - ) { - super(cryptoFunctionService, logService, logMacFailures); - } - - /** - * Handles decryption of items, will use the offscreen document if supported. - * - * @param items - The items to decrypt. - * @param key - The key to use for decryption. - */ - async decryptItems( - items: Decryptable[], - key: SymmetricCryptoKey, - ): Promise { - if (!this.isOffscreenDocumentSupported()) { - return await super.decryptItems(items, key); - } - - return await this.decryptItemsInOffscreenDocument(items, key); - } - - /** - * Decrypts items using the offscreen document api. - * - * @param items - The items to decrypt. - * @param key - The key to use for decryption. - */ - private async decryptItemsInOffscreenDocument( - items: Decryptable[], - key: SymmetricCryptoKey, - ): Promise { - if (items == null || items.length < 1) { - return []; - } - - const request = { - id: Utils.newGuid(), - items: items, - key: key, - }; - - const response = await this.offscreenDocumentService.withDocument( - [chrome.offscreen.Reason.WORKERS], - "Use web worker to decrypt items.", - async () => { - return (await BrowserApi.sendMessageWithResponse("offscreenDecryptItems", { - decryptRequest: JSON.stringify(request), - })) as string; - }, - ); - - if (!response) { - return []; - } - - const responseItems = JSON.parse(response); - if (responseItems?.length < 1) { - return []; - } - - return this.initializeItems(responseItems); - } - - /** - * Checks if the offscreen document api is supported. - */ - private isOffscreenDocumentSupported() { - return ( - BrowserApi.isManifestVersion(3) && - typeof chrome !== "undefined" && - typeof chrome.offscreen !== "undefined" - ); - } -} diff --git a/apps/browser/src/popup/app.module.ts b/apps/browser/src/popup/app.module.ts index 74e24433b2..2f69d8253f 100644 --- a/apps/browser/src/popup/app.module.ts +++ b/apps/browser/src/popup/app.module.ts @@ -126,6 +126,7 @@ import "../platform/popup/locales"; PopupHeaderComponent, UserVerificationDialogComponent, PopupSectionHeaderComponent, + CurrentAccountComponent, ], declarations: [ ActionButtonsComponent, @@ -188,7 +189,6 @@ import "../platform/popup/locales"; HelpAndFeedbackComponent, AutofillComponent, EnvironmentSelectorComponent, - CurrentAccountComponent, AccountSwitcherComponent, VaultV2Component, ], diff --git a/apps/browser/src/tools/popup/generator/generator.component.ts b/apps/browser/src/tools/popup/generator/generator.component.ts index 0c11c28f27..fbe02d34f5 100644 --- a/apps/browser/src/tools/popup/generator/generator.component.ts +++ b/apps/browser/src/tools/popup/generator/generator.component.ts @@ -4,6 +4,7 @@ import { ActivatedRoute } from "@angular/router"; import { firstValueFrom } from "rxjs"; import { GeneratorComponent as BaseGeneratorComponent } from "@bitwarden/angular/tools/generator/components/generator.component"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; @@ -32,6 +33,7 @@ export class GeneratorComponent extends BaseGeneratorComponent { cipherService: CipherService, route: ActivatedRoute, logService: LogService, + accountService: AccountService, private location: Location, ) { super( @@ -42,6 +44,7 @@ export class GeneratorComponent extends BaseGeneratorComponent { i18nService, logService, route, + accountService, window, ); this.cipherService = cipherService; diff --git a/apps/browser/src/vault/popup/components/vault/add-edit.component.ts b/apps/browser/src/vault/popup/components/vault/add-edit.component.ts index 05255a3c01..62f2737be2 100644 --- a/apps/browser/src/vault/popup/components/vault/add-edit.component.ts +++ b/apps/browser/src/vault/popup/components/vault/add-edit.component.ts @@ -10,13 +10,13 @@ import { AuditService } from "@bitwarden/common/abstractions/audit.service"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service"; @@ -53,7 +53,7 @@ export class AddEditComponent extends BaseAddEditComponent { i18nService: I18nService, platformUtilsService: PlatformUtilsService, auditService: AuditService, - stateService: StateService, + accountService: AccountService, private autofillSettingsService: AutofillSettingsServiceAbstraction, collectionService: CollectionService, messagingService: MessagingService, @@ -78,7 +78,7 @@ export class AddEditComponent extends BaseAddEditComponent { i18nService, platformUtilsService, auditService, - stateService, + accountService, collectionService, messagingService, eventCollectionService, diff --git a/apps/browser/src/vault/popup/components/vault/vault-v2.component.html b/apps/browser/src/vault/popup/components/vault/vault-v2.component.html index a653f46332..c36d2d2db9 100644 --- a/apps/browser/src/vault/popup/components/vault/vault-v2.component.html +++ b/apps/browser/src/vault/popup/components/vault/vault-v2.component.html @@ -1 +1,13 @@ -

Vault V2 Extension Refresh

+ + + + + + + {{ "new" | i18n }} + + + + + + diff --git a/apps/cli/src/auth/commands/login.command.ts b/apps/cli/src/auth/commands/login.command.ts index bd61727a6c..5e80f6faf4 100644 --- a/apps/cli/src/auth/commands/login.command.ts +++ b/apps/cli/src/auth/commands/login.command.ts @@ -3,7 +3,7 @@ import * as http from "http"; import { OptionValues } from "commander"; import * as inquirer from "inquirer"; import Separator from "inquirer/lib/objects/separator"; -import { firstValueFrom } from "rxjs"; +import { firstValueFrom, map } from "rxjs"; import { LoginStrategyServiceAbstraction, @@ -15,6 +15,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service"; @@ -31,7 +32,6 @@ import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/c import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; @@ -60,7 +60,7 @@ export class LoginCommand { protected passwordGenerationService: PasswordGenerationServiceAbstraction, protected passwordStrengthService: PasswordStrengthServiceAbstraction, protected platformUtilsService: PlatformUtilsService, - protected stateService: StateService, + protected accountService: AccountService, protected cryptoService: CryptoService, protected policyService: PolicyService, protected twoFactorService: TwoFactorService, @@ -491,7 +491,9 @@ export class LoginCommand { hint?: string; }> { if (this.email == null || this.email === "undefined") { - this.email = await this.stateService.getEmail(); + this.email = await firstValueFrom( + this.accountService.activeAccount$.pipe(map((a) => a?.email)), + ); } // Get New Master Password diff --git a/apps/cli/src/auth/commands/unlock.command.ts b/apps/cli/src/auth/commands/unlock.command.ts index ca42be5ac5..e3bb9257fa 100644 --- a/apps/cli/src/auth/commands/unlock.command.ts +++ b/apps/cli/src/auth/commands/unlock.command.ts @@ -1,4 +1,4 @@ -import { firstValueFrom } from "rxjs"; +import { firstValueFrom, map } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; @@ -49,10 +49,11 @@ export class UnlockCommand { } await this.setNewSessionKey(); - const email = await this.stateService.getEmail(); + const [userId, email] = await firstValueFrom( + this.accountService.activeAccount$.pipe(map((a) => [a?.id, a?.email])), + ); const kdfConfig = await this.kdfConfigService.getKdfConfig(); const masterKey = await this.cryptoService.makeMasterKey(password, email, kdfConfig); - const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id; const storedMasterKeyHash = await firstValueFrom( this.masterPasswordService.masterKeyHash$(userId), ); diff --git a/apps/cli/src/commands/serve.command.ts b/apps/cli/src/commands/serve.command.ts index aad205998f..2b1e21f8ab 100644 --- a/apps/cli/src/commands/serve.command.ts +++ b/apps/cli/src/commands/serve.command.ts @@ -103,7 +103,7 @@ export class ServeCommand { this.statusCommand = new StatusCommand( this.serviceContainer.environmentService, this.serviceContainer.syncService, - this.serviceContainer.stateService, + this.serviceContainer.accountService, this.serviceContainer.authService, ); this.deleteCommand = new DeleteCommand( diff --git a/apps/cli/src/commands/status.command.ts b/apps/cli/src/commands/status.command.ts index 32b93a7e40..3fe8be620c 100644 --- a/apps/cli/src/commands/status.command.ts +++ b/apps/cli/src/commands/status.command.ts @@ -1,9 +1,9 @@ -import { firstValueFrom } from "rxjs"; +import { firstValueFrom, map } from "rxjs"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; import { Response } from "../models/response"; @@ -13,7 +13,7 @@ export class StatusCommand { constructor( private envService: EnvironmentService, private syncService: SyncService, - private stateService: StateService, + private accountService: AccountService, private authService: AuthService, ) {} @@ -22,8 +22,9 @@ export class StatusCommand { const baseUrl = await this.baseUrl(); const status = await this.status(); const lastSync = await this.syncService.getLastSync(); - const userId = await this.stateService.getUserId(); - const email = await this.stateService.getEmail(); + const [userId, email] = await firstValueFrom( + this.accountService.activeAccount$.pipe(map((a) => [a?.id, a?.email])), + ); return Response.success( new TemplateResponse({ diff --git a/apps/cli/src/program.ts b/apps/cli/src/program.ts index 3a2858aa81..667e0f683f 100644 --- a/apps/cli/src/program.ts +++ b/apps/cli/src/program.ts @@ -1,6 +1,6 @@ import * as chalk from "chalk"; import { program, Command, OptionValues } from "commander"; -import { firstValueFrom } from "rxjs"; +import { firstValueFrom, map } from "rxjs"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; @@ -161,7 +161,7 @@ export class Program { this.serviceContainer.passwordGenerationService, this.serviceContainer.passwordStrengthService, this.serviceContainer.platformUtilsService, - this.serviceContainer.stateService, + this.serviceContainer.accountService, this.serviceContainer.cryptoService, this.serviceContainer.policyService, this.serviceContainer.twoFactorService, @@ -481,7 +481,7 @@ export class Program { const command = new StatusCommand( this.serviceContainer.environmentService, this.serviceContainer.syncService, - this.serviceContainer.stateService, + this.serviceContainer.accountService, this.serviceContainer.authService, ); const response = await command.run(); @@ -603,9 +603,15 @@ export class Program { } private async exitIfAuthed() { - const authed = await this.serviceContainer.stateService.getIsAuthenticated(); + const authed = await firstValueFrom( + this.serviceContainer.authService.activeAccountStatus$.pipe( + map((status) => status > AuthenticationStatus.LoggedOut), + ), + ); if (authed) { - const email = await this.serviceContainer.stateService.getEmail(); + const email = await firstValueFrom( + this.serviceContainer.accountService.activeAccount$.pipe(map((a) => a?.email)), + ); this.processResponse(Response.error("You are already logged in as " + email + "."), true); } } diff --git a/apps/desktop/src/app/accounts/settings.component.ts b/apps/desktop/src/app/accounts/settings.component.ts index ade019b9fb..cfc646337a 100644 --- a/apps/desktop/src/app/accounts/settings.component.ts +++ b/apps/desktop/src/app/accounts/settings.component.ts @@ -224,7 +224,9 @@ export class SettingsComponent implements OnInit { if ((await this.stateService.getUserId()) == null) { return; } - this.currentUserEmail = await this.stateService.getEmail(); + this.currentUserEmail = await firstValueFrom( + this.accountService.activeAccount$.pipe(map((a) => a?.email)), + ); this.currentUserId = (await this.stateService.getUserId()) as UserId; this.availableVaultTimeoutActions$ = this.refreshTimeoutSettings$.pipe( diff --git a/apps/desktop/src/app/tools/generator.component.ts b/apps/desktop/src/app/tools/generator.component.ts index a36bffa0e5..5bc5328943 100644 --- a/apps/desktop/src/app/tools/generator.component.ts +++ b/apps/desktop/src/app/tools/generator.component.ts @@ -2,6 +2,7 @@ import { Component } from "@angular/core"; import { ActivatedRoute } from "@angular/router"; import { GeneratorComponent as BaseGeneratorComponent } from "@bitwarden/angular/tools/generator/components/generator.component"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; @@ -22,6 +23,7 @@ export class GeneratorComponent extends BaseGeneratorComponent { i18nService: I18nService, route: ActivatedRoute, logService: LogService, + accountService: AccountService, ) { super( passwordGenerationService, @@ -31,6 +33,7 @@ export class GeneratorComponent extends BaseGeneratorComponent { i18nService, logService, route, + accountService, window, ); } diff --git a/apps/desktop/src/auth/login/login-approval.component.ts b/apps/desktop/src/auth/login/login-approval.component.ts index 048d294f4a..296efb50e4 100644 --- a/apps/desktop/src/auth/login/login-approval.component.ts +++ b/apps/desktop/src/auth/login/login-approval.component.ts @@ -1,17 +1,17 @@ import { DIALOG_DATA, DialogRef } from "@angular/cdk/dialog"; import { CommonModule } from "@angular/common"; import { Component, OnInit, OnDestroy, Inject } from "@angular/core"; -import { Subject, firstValueFrom } from "rxjs"; +import { Subject, firstValueFrom, map } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { AuthRequestServiceAbstraction } from "@bitwarden/auth/common"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { AsyncActionsModule, @@ -47,7 +47,7 @@ export class LoginApprovalComponent implements OnInit, OnDestroy { constructor( @Inject(DIALOG_DATA) private params: LoginApprovalDialogParams, protected authRequestService: AuthRequestServiceAbstraction, - protected stateService: StateService, + protected accountService: AccountService, protected platformUtilsService: PlatformUtilsService, protected i18nService: I18nService, protected apiService: ApiService, @@ -74,7 +74,9 @@ export class LoginApprovalComponent implements OnInit, OnDestroy { if (this.notificationId != null) { this.authRequestResponse = await this.apiService.getAuthRequest(this.notificationId); const publicKey = Utils.fromB64ToArray(this.authRequestResponse.publicKey); - this.email = await this.stateService.getEmail(); + this.email = await await firstValueFrom( + this.accountService.activeAccount$.pipe(map((a) => a?.email)), + ); this.fingerprintPhrase = ( await this.cryptoService.getFingerprint(this.email, publicKey) ).join("-"); diff --git a/apps/desktop/src/auth/login/login-via-auth-request.component.ts b/apps/desktop/src/auth/login/login-via-auth-request.component.ts index 2d0f560205..40d41f2236 100644 --- a/apps/desktop/src/auth/login/login-via-auth-request.component.ts +++ b/apps/desktop/src/auth/login/login-via-auth-request.component.ts @@ -21,7 +21,6 @@ import { EnvironmentService } from "@bitwarden/common/platform/abstractions/envi import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; @@ -53,7 +52,6 @@ export class LoginViaAuthRequestComponent extends BaseLoginWithDeviceComponent { validationService: ValidationService, private modalService: ModalService, syncService: SyncService, - stateService: StateService, loginEmailService: LoginEmailServiceAbstraction, deviceTrustService: DeviceTrustServiceAbstraction, authRequestService: AuthRequestServiceAbstraction, @@ -75,12 +73,11 @@ export class LoginViaAuthRequestComponent extends BaseLoginWithDeviceComponent { platformUtilsService, anonymousHubService, validationService, - stateService, + accountService, loginEmailService, deviceTrustService, authRequestService, loginStrategyService, - accountService, ); super.onSuccessfulLogin = () => { diff --git a/apps/desktop/src/vault/app/vault/add-edit.component.ts b/apps/desktop/src/vault/app/vault/add-edit.component.ts index 86e0b881ee..d7fd394795 100644 --- a/apps/desktop/src/vault/app/vault/add-edit.component.ts +++ b/apps/desktop/src/vault/app/vault/add-edit.component.ts @@ -7,13 +7,13 @@ import { AuditService } from "@bitwarden/common/abstractions/audit.service"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service"; @@ -36,7 +36,7 @@ export class AddEditComponent extends BaseAddEditComponent implements OnChanges, i18nService: I18nService, platformUtilsService: PlatformUtilsService, auditService: AuditService, - stateService: StateService, + accountService: AccountService, collectionService: CollectionService, messagingService: MessagingService, eventCollectionService: EventCollectionService, @@ -57,7 +57,7 @@ export class AddEditComponent extends BaseAddEditComponent implements OnChanges, i18nService, platformUtilsService, auditService, - stateService, + accountService, collectionService, messagingService, eventCollectionService, diff --git a/apps/web/src/app/admin-console/organizations/guards/is-paid-org.guard.ts b/apps/web/src/app/admin-console/organizations/guards/is-paid-org.guard.ts index f6968daca9..aaf24e4834 100644 --- a/apps/web/src/app/admin-console/organizations/guards/is-paid-org.guard.ts +++ b/apps/web/src/app/admin-console/organizations/guards/is-paid-org.guard.ts @@ -27,15 +27,26 @@ export class IsPaidOrgGuard implements CanActivate { // Users without billing permission can't access billing if (!org.canEditSubscription) { await this.dialogService.openSimpleDialog({ - title: { key: "upgradeOrganization" }, - content: { key: "notAvailableForFreeOrganization" }, + title: { key: "upgradeOrganizationCloseSecurityGaps" }, + content: { key: "upgradeOrganizationCloseSecurityGapsDesc" }, acceptButtonText: { key: "ok" }, cancelButtonText: null, type: "info", }); return false; } else { - this.messagingService.send("upgradeOrganization", { organizationId: org.id }); + const upgradeConfirmed = await this.dialogService.openSimpleDialog({ + title: { key: "upgradeOrganizationCloseSecurityGaps" }, + content: { key: "upgradeOrganizationCloseSecurityGapsDesc" }, + acceptButtonText: { key: "upgradeOrganization" }, + type: "info", + icon: "bwi-arrow-circle-up", + }); + if (upgradeConfirmed) { + await this.router.navigate(["organizations", org.id, "billing", "subscription"], { + queryParams: { upgrade: true }, + }); + } } } diff --git a/apps/web/src/app/admin-console/settings/sponsored-families.component.ts b/apps/web/src/app/admin-console/settings/sponsored-families.component.ts index 562192a262..3477b9a425 100644 --- a/apps/web/src/app/admin-console/settings/sponsored-families.component.ts +++ b/apps/web/src/app/admin-console/settings/sponsored-families.component.ts @@ -1,15 +1,15 @@ import { Component, OnDestroy, OnInit } from "@angular/core"; import { FormBuilder, FormControl, FormGroup, Validators } from "@angular/forms"; -import { map, Observable, Subject, takeUntil } from "rxjs"; +import { firstValueFrom, map, Observable, Subject, takeUntil } from "rxjs"; import { notAllowedValueAsync } from "@bitwarden/angular/admin-console/validators/not-allowed-value-async.validator"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { PlanSponsorshipType } from "@bitwarden/common/billing/enums/"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; interface RequestSponsorshipForm { @@ -43,7 +43,7 @@ export class SponsoredFamiliesComponent implements OnInit, OnDestroy { private syncService: SyncService, private organizationService: OrganizationService, private formBuilder: FormBuilder, - private stateService: StateService, + private accountService: AccountService, ) { this.sponsorshipForm = this.formBuilder.group({ selectedSponsorshipOrgId: new FormControl("", { @@ -52,7 +52,10 @@ export class SponsoredFamiliesComponent implements OnInit, OnDestroy { sponsorshipEmail: new FormControl("", { validators: [Validators.email], asyncValidators: [ - notAllowedValueAsync(async () => await this.stateService.getEmail(), true), + notAllowedValueAsync( + () => firstValueFrom(this.accountService.activeAccount$.pipe(map((a) => a?.email))), + true, + ), ], updateOn: "blur", }), diff --git a/apps/web/src/app/auth/key-rotation/user-key-rotation.service.ts b/apps/web/src/app/auth/key-rotation/user-key-rotation.service.ts index 2763de71b3..4b8d6ca139 100644 --- a/apps/web/src/app/auth/key-rotation/user-key-rotation.service.ts +++ b/apps/web/src/app/auth/key-rotation/user-key-rotation.service.ts @@ -1,5 +1,5 @@ import { Injectable } from "@angular/core"; -import { firstValueFrom } from "rxjs"; +import { firstValueFrom, map } from "rxjs"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction"; @@ -52,7 +52,7 @@ export class UserKeyRotationService { // Create master key to validate the master password const masterKey = await this.cryptoService.makeMasterKey( masterPassword, - await this.stateService.getEmail(), + await firstValueFrom(this.accountService.activeAccount$.pipe(map((a) => a?.email))), await this.kdfConfigService.getKdfConfig(), ); diff --git a/apps/web/src/app/auth/settings/change-password.component.ts b/apps/web/src/app/auth/settings/change-password.component.ts index 39d0af30ef..8c52e71bdd 100644 --- a/apps/web/src/app/auth/settings/change-password.component.ts +++ b/apps/web/src/app/auth/settings/change-password.component.ts @@ -1,10 +1,12 @@ import { Component } from "@angular/core"; import { Router } from "@angular/router"; +import { firstValueFrom, map } from "rxjs"; import { ChangePasswordComponent as BaseChangePasswordComponent } from "@bitwarden/angular/auth/components/change-password.component"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AuditService } from "@bitwarden/common/abstractions/audit.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; @@ -52,6 +54,7 @@ export class ChangePasswordComponent extends BaseChangePasswordComponent { private keyRotationService: UserKeyRotationService, kdfConfigService: KdfConfigService, masterPasswordService: InternalMasterPasswordServiceAbstraction, + accountService: AccountService, ) { super( i18nService, @@ -64,6 +67,7 @@ export class ChangePasswordComponent extends BaseChangePasswordComponent { dialogService, kdfConfigService, masterPasswordService, + accountService, ); } @@ -170,7 +174,7 @@ export class ChangePasswordComponent extends BaseChangePasswordComponent { ) { const masterKey = await this.cryptoService.makeMasterKey( this.currentMasterPassword, - await this.stateService.getEmail(), + await firstValueFrom(this.accountService.activeAccount$.pipe(map((a) => a?.email))), await this.kdfConfigService.getKdfConfig(), ); diff --git a/apps/web/src/app/auth/settings/emergency-access/takeover/emergency-access-takeover.component.ts b/apps/web/src/app/auth/settings/emergency-access/takeover/emergency-access-takeover.component.ts index 5755156bf9..6115b4bac1 100644 --- a/apps/web/src/app/auth/settings/emergency-access/takeover/emergency-access-takeover.component.ts +++ b/apps/web/src/app/auth/settings/emergency-access/takeover/emergency-access-takeover.component.ts @@ -5,6 +5,7 @@ import { takeUntil } from "rxjs"; import { ChangePasswordComponent } from "@bitwarden/angular/auth/components/change-password.component"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; @@ -62,6 +63,7 @@ export class EmergencyAccessTakeoverComponent private dialogRef: DialogRef, kdfConfigService: KdfConfigService, masterPasswordService: InternalMasterPasswordServiceAbstraction, + accountService: AccountService, ) { super( i18nService, @@ -74,6 +76,7 @@ export class EmergencyAccessTakeoverComponent dialogService, kdfConfigService, masterPasswordService, + accountService, ); } diff --git a/apps/web/src/app/auth/settings/emergency-access/view/emergency-add-edit-cipher.component.ts b/apps/web/src/app/auth/settings/emergency-access/view/emergency-add-edit-cipher.component.ts index 9312ce5fc0..1e070d42ab 100644 --- a/apps/web/src/app/auth/settings/emergency-access/view/emergency-add-edit-cipher.component.ts +++ b/apps/web/src/app/auth/settings/emergency-access/view/emergency-add-edit-cipher.component.ts @@ -5,13 +5,13 @@ import { AuditService } from "@bitwarden/common/abstractions/audit.service"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password/"; import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; @@ -39,7 +39,7 @@ export class EmergencyAddEditCipherComponent extends BaseAddEditComponent { i18nService: I18nService, platformUtilsService: PlatformUtilsService, auditService: AuditService, - stateService: StateService, + accountService: AccountService, collectionService: CollectionService, totpService: TotpService, passwordGenerationService: PasswordGenerationServiceAbstraction, @@ -61,7 +61,7 @@ export class EmergencyAddEditCipherComponent extends BaseAddEditComponent { i18nService, platformUtilsService, auditService, - stateService, + accountService, collectionService, totpService, passwordGenerationService, diff --git a/apps/web/src/app/auth/settings/security/change-kdf/change-kdf-confirmation.component.ts b/apps/web/src/app/auth/settings/security/change-kdf/change-kdf-confirmation.component.ts index 985fb3e038..1424310a33 100644 --- a/apps/web/src/app/auth/settings/security/change-kdf/change-kdf-confirmation.component.ts +++ b/apps/web/src/app/auth/settings/security/change-kdf/change-kdf-confirmation.component.ts @@ -1,8 +1,10 @@ import { DIALOG_DATA } from "@angular/cdk/dialog"; import { Component, Inject } from "@angular/core"; import { FormGroup, FormControl, Validators } from "@angular/forms"; +import { firstValueFrom, map } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config"; import { KdfRequest } from "@bitwarden/common/models/request/kdf.request"; @@ -11,7 +13,6 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { KdfType } from "@bitwarden/common/platform/enums"; @Component({ @@ -35,7 +36,7 @@ export class ChangeKdfConfirmationComponent { private platformUtilsService: PlatformUtilsService, private cryptoService: CryptoService, private messagingService: MessagingService, - private stateService: StateService, + private accountService: AccountService, private logService: LogService, private kdfConfigService: KdfConfigService, @Inject(DIALOG_DATA) params: { kdfConfig: KdfConfig }, @@ -78,7 +79,9 @@ export class ChangeKdfConfirmationComponent { } const masterKey = await this.cryptoService.getOrDeriveMasterKey(masterPassword); request.masterPasswordHash = await this.cryptoService.hashMasterKey(masterPassword, masterKey); - const email = await this.stateService.getEmail(); + const email = await firstValueFrom( + this.accountService.activeAccount$.pipe(map((a) => a?.email)), + ); const newMasterKey = await this.cryptoService.makeMasterKey( masterPassword, diff --git a/apps/web/src/app/auth/settings/two-factor-authenticator.component.ts b/apps/web/src/app/auth/settings/two-factor-authenticator.component.ts index 849e003440..88b695eb72 100644 --- a/apps/web/src/app/auth/settings/two-factor-authenticator.component.ts +++ b/apps/web/src/app/auth/settings/two-factor-authenticator.component.ts @@ -1,6 +1,8 @@ import { Component, OnDestroy, OnInit } from "@angular/core"; +import { firstValueFrom, map } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { UpdateTwoFactorAuthenticatorRequest } from "@bitwarden/common/auth/models/request/update-two-factor-authenticator.request"; @@ -9,7 +11,6 @@ import { AuthResponse } from "@bitwarden/common/auth/types/auth-response"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { DialogService } from "@bitwarden/components"; @@ -51,7 +52,7 @@ export class TwoFactorAuthenticatorComponent userVerificationService: UserVerificationService, platformUtilsService: PlatformUtilsService, logService: LogService, - private stateService: StateService, + private accountService: AccountService, dialogService: DialogService, ) { super( @@ -104,7 +105,9 @@ export class TwoFactorAuthenticatorComponent this.token = null; this.enabled = response.enabled; this.key = response.key; - const email = await this.stateService.getEmail(); + const email = await firstValueFrom( + this.accountService.activeAccount$.pipe(map((a) => a?.email)), + ); window.setTimeout(() => { new window.QRious({ element: document.getElementById("qr"), diff --git a/apps/web/src/app/auth/settings/two-factor-email.component.ts b/apps/web/src/app/auth/settings/two-factor-email.component.ts index cfbe10823c..7a2e6de580 100644 --- a/apps/web/src/app/auth/settings/two-factor-email.component.ts +++ b/apps/web/src/app/auth/settings/two-factor-email.component.ts @@ -1,6 +1,8 @@ import { Component } from "@angular/core"; +import { firstValueFrom, map } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { TwoFactorEmailRequest } from "@bitwarden/common/auth/models/request/two-factor-email.request"; @@ -10,7 +12,6 @@ import { AuthResponse } from "@bitwarden/common/auth/types/auth-response"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { DialogService } from "@bitwarden/components"; import { TwoFactorBaseComponent } from "./two-factor-base.component"; @@ -35,7 +36,7 @@ export class TwoFactorEmailComponent extends TwoFactorBaseComponent { platformUtilsService: PlatformUtilsService, logService: LogService, userVerificationService: UserVerificationService, - private stateService: StateService, + private accountService: AccountService, dialogService: DialogService, ) { super( @@ -90,7 +91,9 @@ export class TwoFactorEmailComponent extends TwoFactorBaseComponent { this.email = response.email; this.enabled = response.enabled; if (!this.enabled && (this.email == null || this.email === "")) { - this.email = await this.stateService.getEmail(); + this.email = await firstValueFrom( + this.accountService.activeAccount$.pipe(map((a) => a?.email)), + ); } } } diff --git a/apps/web/src/app/auth/update-password.component.ts b/apps/web/src/app/auth/update-password.component.ts index 403d31be43..b8cfb47db1 100644 --- a/apps/web/src/app/auth/update-password.component.ts +++ b/apps/web/src/app/auth/update-password.component.ts @@ -4,6 +4,7 @@ import { Router } from "@angular/router"; import { UpdatePasswordComponent as BaseUpdatePasswordComponent } from "@bitwarden/angular/auth/components/update-password.component"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; @@ -36,6 +37,7 @@ export class UpdatePasswordComponent extends BaseUpdatePasswordComponent { dialogService: DialogService, kdfConfigService: KdfConfigService, masterPasswordService: InternalMasterPasswordServiceAbstraction, + accountService: AccountService, ) { super( router, @@ -52,6 +54,7 @@ export class UpdatePasswordComponent extends BaseUpdatePasswordComponent { dialogService, kdfConfigService, masterPasswordService, + accountService, ); } } diff --git a/apps/web/src/app/billing/shared/add-credit.component.ts b/apps/web/src/app/billing/shared/add-credit.component.ts index 71050a9a6e..7cf9054198 100644 --- a/apps/web/src/app/billing/shared/add-credit.component.ts +++ b/apps/web/src/app/billing/shared/add-credit.component.ts @@ -7,16 +7,16 @@ import { Output, ViewChild, } from "@angular/core"; -import { firstValueFrom } from "rxjs"; +import { firstValueFrom, map } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { PaymentMethodType } from "@bitwarden/common/billing/enums"; import { BitPayInvoiceRequest } from "@bitwarden/common/billing/models/request/bit-pay-invoice.request"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; export type PayPalConfig = { businessId?: string; @@ -52,7 +52,7 @@ export class AddCreditComponent implements OnInit { private region: string; constructor( - private stateService: StateService, + private accountService: AccountService, private apiService: ApiService, private platformUtilsService: PlatformUtilsService, private organizationService: OrganizationService, @@ -79,8 +79,11 @@ export class AddCreditComponent implements OnInit { if (this.creditAmount == null) { this.creditAmount = "10.00"; } - this.userId = await this.stateService.getUserId(); - this.subject = await this.stateService.getEmail(); + const [userId, email] = await firstValueFrom( + this.accountService.activeAccount$.pipe(map((a) => [a?.id, a?.email])), + ); + this.userId = userId; + this.subject = email; this.email = this.subject; this.ppButtonCustomField = "user_id:" + this.userId; } diff --git a/apps/web/src/app/tools/generator.component.ts b/apps/web/src/app/tools/generator.component.ts index ee422ca527..0ddf3064b9 100644 --- a/apps/web/src/app/tools/generator.component.ts +++ b/apps/web/src/app/tools/generator.component.ts @@ -2,6 +2,7 @@ import { Component } from "@angular/core"; import { ActivatedRoute } from "@angular/router"; import { GeneratorComponent as BaseGeneratorComponent } from "@bitwarden/angular/tools/generator/components/generator.component"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; @@ -26,6 +27,7 @@ export class GeneratorComponent extends BaseGeneratorComponent { logService: LogService, route: ActivatedRoute, private dialogService: DialogService, + accountService: AccountService, ) { super( passwordGenerationService, @@ -35,6 +37,7 @@ export class GeneratorComponent extends BaseGeneratorComponent { i18nService, logService, route, + accountService, window, ); if (platformUtilsService.isSelfHost()) { diff --git a/apps/web/src/app/tools/reports/pages/breach-report.component.ts b/apps/web/src/app/tools/reports/pages/breach-report.component.ts index 8f371cf754..5728d36078 100644 --- a/apps/web/src/app/tools/reports/pages/breach-report.component.ts +++ b/apps/web/src/app/tools/reports/pages/breach-report.component.ts @@ -1,8 +1,9 @@ import { Component, OnInit } from "@angular/core"; +import { firstValueFrom, map } from "rxjs"; import { AuditService } from "@bitwarden/common/abstractions/audit.service"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { BreachAccountResponse } from "@bitwarden/common/models/response/breach-account.response"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; @Component({ selector: "app-breach-report", @@ -17,11 +18,13 @@ export class BreachReportComponent implements OnInit { constructor( private auditService: AuditService, - private stateService: StateService, + private accountService: AccountService, ) {} async ngOnInit() { - this.username = await this.stateService.getEmail(); + this.username = await firstValueFrom( + this.accountService.activeAccount$.pipe(map((a) => a?.email)), + ); } async submit() { diff --git a/apps/web/src/app/vault/individual-vault/add-edit.component.ts b/apps/web/src/app/vault/individual-vault/add-edit.component.ts index 56f18c4a3b..fee728ca99 100644 --- a/apps/web/src/app/vault/individual-vault/add-edit.component.ts +++ b/apps/web/src/app/vault/individual-vault/add-edit.component.ts @@ -7,6 +7,7 @@ import { AuditService } from "@bitwarden/common/abstractions/audit.service"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { EventType, ProductType } from "@bitwarden/common/enums"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; @@ -14,7 +15,6 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password"; import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; @@ -52,7 +52,7 @@ export class AddEditComponent extends BaseAddEditComponent implements OnInit, On i18nService: I18nService, platformUtilsService: PlatformUtilsService, auditService: AuditService, - stateService: StateService, + accountService: AccountService, collectionService: CollectionService, protected totpService: TotpService, protected passwordGenerationService: PasswordGenerationServiceAbstraction, @@ -74,7 +74,7 @@ export class AddEditComponent extends BaseAddEditComponent implements OnInit, On i18nService, platformUtilsService, auditService, - stateService, + accountService, collectionService, messagingService, eventCollectionService, diff --git a/apps/web/src/app/vault/org-vault/add-edit.component.ts b/apps/web/src/app/vault/org-vault/add-edit.component.ts index 82055cc916..9c7c3c30f3 100644 --- a/apps/web/src/app/vault/org-vault/add-edit.component.ts +++ b/apps/web/src/app/vault/org-vault/add-edit.component.ts @@ -6,13 +6,13 @@ import { AuditService } from "@bitwarden/common/abstractions/audit.service"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password"; import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; @@ -40,7 +40,7 @@ export class AddEditComponent extends BaseAddEditComponent { i18nService: I18nService, platformUtilsService: PlatformUtilsService, auditService: AuditService, - stateService: StateService, + accountService: AccountService, collectionService: CollectionService, totpService: TotpService, passwordGenerationService: PasswordGenerationServiceAbstraction, @@ -63,7 +63,7 @@ export class AddEditComponent extends BaseAddEditComponent { i18nService, platformUtilsService, auditService, - stateService, + accountService, collectionService, totpService, passwordGenerationService, diff --git a/apps/web/src/app/vault/org-vault/vault.component.html b/apps/web/src/app/vault/org-vault/vault.component.html index a6d1cd3074..06907b9e5d 100644 --- a/apps/web/src/app/vault/org-vault/vault.component.html +++ b/apps/web/src/app/vault/org-vault/vault.component.html @@ -12,7 +12,7 @@ >
-
+
@@ -26,7 +26,7 @@
-
+
(); private refresh$ = new BehaviorSubject(null); private destroy$ = new Subject(); diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 3afd318266..fb1c30e546 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -8222,5 +8222,11 @@ }, "protectYourFamilyOrBusiness": { "message": "Protect your family or business" + }, + "upgradeOrganizationCloseSecurityGaps": { + "message": "Close security gaps with monitoring reports" + }, + "upgradeOrganizationCloseSecurityGapsDesc": { + "message": "Stay ahead of security vulnerabilities by upgrading to a paid plan for enhanced monitoring." } } diff --git a/libs/angular/src/auth/components/base-login-decryption-options.component.ts b/libs/angular/src/auth/components/base-login-decryption-options.component.ts index b5cc50d847..0cc416a74b 100644 --- a/libs/angular/src/auth/components/base-login-decryption-options.component.ts +++ b/libs/angular/src/auth/components/base-login-decryption-options.component.ts @@ -12,6 +12,9 @@ import { takeUntil, defer, throwError, + map, + Observable, + take, } from "rxjs"; import { @@ -67,6 +70,8 @@ export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy { protected data?: Data; protected loading = true; + private email$: Observable; + activeAccountId: UserId; // Remember device means for the user to trust the device @@ -104,6 +109,14 @@ export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy { async ngOnInit() { this.loading = true; this.activeAccountId = (await firstValueFrom(this.accountService.activeAccount$))?.id; + this.email$ = this.accountService.activeAccount$.pipe( + map((a) => a?.email), + catchError((err: unknown) => { + this.validationService.showError(err); + return of(undefined); + }), + takeUntil(this.destroy$), + ); this.setupRememberDeviceValueChanges(); @@ -193,16 +206,8 @@ export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy { }), ); - const email$ = from(this.stateService.getEmail()).pipe( - catchError((err: unknown) => { - this.validationService.showError(err); - return of(undefined); - }), - takeUntil(this.destroy$), - ); - const autoEnrollStatus = await firstValueFrom(autoEnrollStatus$); - const email = await firstValueFrom(email$); + const email = await firstValueFrom(this.email$); this.data = { state: State.NewUser, organizationId: autoEnrollStatus.id, userEmail: email }; this.loading = false; @@ -211,17 +216,9 @@ export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy { loadUntrustedDeviceData(userDecryptionOptions: UserDecryptionOptions) { this.loading = true; - const email$ = from(this.stateService.getEmail()).pipe( - catchError((err: unknown) => { - this.validationService.showError(err); - return of(undefined); - }), - takeUntil(this.destroy$), - ); - - email$ + this.email$ .pipe( - takeUntil(this.destroy$), + take(1), finalize(() => { this.loading = false; }), diff --git a/libs/angular/src/auth/components/change-password.component.ts b/libs/angular/src/auth/components/change-password.component.ts index 714c5f56d9..975090b389 100644 --- a/libs/angular/src/auth/components/change-password.component.ts +++ b/libs/angular/src/auth/components/change-password.component.ts @@ -1,8 +1,9 @@ import { Directive, OnDestroy, OnInit } from "@angular/core"; -import { Subject, takeUntil } from "rxjs"; +import { Subject, firstValueFrom, map, takeUntil } from "rxjs"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction"; import { KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config"; @@ -47,10 +48,13 @@ export class ChangePasswordComponent implements OnInit, OnDestroy { protected dialogService: DialogService, protected kdfConfigService: KdfConfigService, protected masterPasswordService: InternalMasterPasswordServiceAbstraction, + protected accountService: AccountService, ) {} async ngOnInit() { - this.email = await this.stateService.getEmail(); + this.email = await firstValueFrom( + this.accountService.activeAccount$.pipe(map((a) => a?.email)), + ); this.policyService .masterPasswordPolicyOptions$() .pipe(takeUntil(this.destroy$)) @@ -74,7 +78,9 @@ export class ChangePasswordComponent implements OnInit, OnDestroy { return; } - const email = await this.stateService.getEmail(); + const email = await firstValueFrom( + this.accountService.activeAccount$.pipe(map((a) => a?.email)), + ); if (this.kdfConfig == null) { this.kdfConfig = await this.kdfConfigService.getKdfConfig(); } diff --git a/libs/angular/src/auth/components/lock.component.ts b/libs/angular/src/auth/components/lock.component.ts index fe4be9b5de..a1c9e94fb0 100644 --- a/libs/angular/src/auth/components/lock.component.ts +++ b/libs/angular/src/auth/components/lock.component.ts @@ -372,7 +372,9 @@ export class LockComponent implements OnInit, OnDestroy { (await this.vaultTimeoutSettingsService.isBiometricLockSet()) && ((await this.cryptoService.hasUserKeyStored(KeySuffixOptions.Biometric)) || !this.platformUtilsService.supportsSecureStorage()); - this.email = await this.stateService.getEmail(); + this.email = await firstValueFrom( + this.accountService.activeAccount$.pipe(map((a) => a?.email)), + ); this.webVaultHostname = (await this.environmentService.getEnvironment()).getHostname(); } diff --git a/libs/angular/src/auth/components/login-via-auth-request.component.ts b/libs/angular/src/auth/components/login-via-auth-request.component.ts index a60468e244..401abab3b1 100644 --- a/libs/angular/src/auth/components/login-via-auth-request.component.ts +++ b/libs/angular/src/auth/components/login-via-auth-request.component.ts @@ -1,6 +1,6 @@ import { Directive, OnDestroy, OnInit } from "@angular/core"; import { IsActiveMatchOptions, Router } from "@angular/router"; -import { Subject, firstValueFrom, takeUntil } from "rxjs"; +import { Subject, firstValueFrom, map, takeUntil } from "rxjs"; import { AuthRequestLoginCredentials, @@ -29,7 +29,6 @@ import { EnvironmentService } from "@bitwarden/common/platform/abstractions/envi import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password"; @@ -84,12 +83,11 @@ export class LoginViaAuthRequestComponent platformUtilsService: PlatformUtilsService, private anonymousHubService: AnonymousHubService, private validationService: ValidationService, - private stateService: StateService, + private accountService: AccountService, private loginEmailService: LoginEmailServiceAbstraction, private deviceTrustService: DeviceTrustServiceAbstraction, private authRequestService: AuthRequestServiceAbstraction, private loginStrategyService: LoginStrategyServiceAbstraction, - private accountService: AccountService, ) { super(environmentService, i18nService, platformUtilsService); @@ -131,7 +129,9 @@ export class LoginViaAuthRequestComponent // Pull email from state for admin auth reqs b/c it is available // This also prevents it from being lost on refresh as the // login service email does not persist. - this.email = await this.stateService.getEmail(); + this.email = await firstValueFrom( + this.accountService.activeAccount$.pipe(map((a) => a?.email)), + ); const userId = (await firstValueFrom(this.accountService.activeAccount$)).id; if (!this.email) { diff --git a/libs/angular/src/auth/components/remove-password.component.ts b/libs/angular/src/auth/components/remove-password.component.ts index 9a65728fa9..ddd494e31a 100644 --- a/libs/angular/src/auth/components/remove-password.component.ts +++ b/libs/angular/src/auth/components/remove-password.component.ts @@ -1,12 +1,13 @@ import { Directive, OnInit } from "@angular/core"; import { Router } from "@angular/router"; +import { firstValueFrom, map } from "rxjs"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; import { DialogService } from "@bitwarden/components"; @@ -22,7 +23,7 @@ export class RemovePasswordComponent implements OnInit { constructor( private router: Router, - private stateService: StateService, + private accountService: AccountService, private syncService: SyncService, private platformUtilsService: PlatformUtilsService, private i18nService: I18nService, @@ -33,7 +34,9 @@ export class RemovePasswordComponent implements OnInit { async ngOnInit() { this.organization = await this.keyConnectorService.getManagingOrganization(); - this.email = await this.stateService.getEmail(); + this.email = await firstValueFrom( + this.accountService.activeAccount$.pipe(map((a) => a?.email)), + ); await this.syncService.fullSync(false); this.loading = false; } diff --git a/libs/angular/src/auth/components/set-password.component.ts b/libs/angular/src/auth/components/set-password.component.ts index a009e9f480..73c38c8ddb 100644 --- a/libs/angular/src/auth/components/set-password.component.ts +++ b/libs/angular/src/auth/components/set-password.component.ts @@ -51,7 +51,7 @@ export class SetPasswordComponent extends BaseChangePasswordComponent { ForceSetPasswordReason = ForceSetPasswordReason; constructor( - private accountService: AccountService, + accountService: AccountService, masterPasswordService: InternalMasterPasswordServiceAbstraction, i18nService: I18nService, cryptoService: CryptoService, @@ -83,6 +83,7 @@ export class SetPasswordComponent extends BaseChangePasswordComponent { dialogService, kdfConfigService, masterPasswordService, + accountService, ); } diff --git a/libs/angular/src/auth/components/update-password.component.ts b/libs/angular/src/auth/components/update-password.component.ts index b2c37359f4..3b709b3e7f 100644 --- a/libs/angular/src/auth/components/update-password.component.ts +++ b/libs/angular/src/auth/components/update-password.component.ts @@ -4,6 +4,7 @@ import { Router } from "@angular/router"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; @@ -48,6 +49,7 @@ export class UpdatePasswordComponent extends BaseChangePasswordComponent { dialogService: DialogService, kdfConfigService: KdfConfigService, masterPasswordService: InternalMasterPasswordServiceAbstraction, + accountService: AccountService, ) { super( i18nService, @@ -60,6 +62,7 @@ export class UpdatePasswordComponent extends BaseChangePasswordComponent { dialogService, kdfConfigService, masterPasswordService, + accountService, ); } diff --git a/libs/angular/src/auth/components/update-temp-password.component.ts b/libs/angular/src/auth/components/update-temp-password.component.ts index d9a7549641..e797c93be1 100644 --- a/libs/angular/src/auth/components/update-temp-password.component.ts +++ b/libs/angular/src/auth/components/update-temp-password.component.ts @@ -1,6 +1,6 @@ import { Directive } from "@angular/core"; import { Router } from "@angular/router"; -import { firstValueFrom } from "rxjs"; +import { firstValueFrom, map } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; @@ -61,7 +61,7 @@ export class UpdateTempPasswordComponent extends BaseChangePasswordComponent { protected router: Router, dialogService: DialogService, kdfConfigService: KdfConfigService, - private accountService: AccountService, + accountService: AccountService, masterPasswordService: InternalMasterPasswordServiceAbstraction, ) { super( @@ -75,6 +75,7 @@ export class UpdateTempPasswordComponent extends BaseChangePasswordComponent { dialogService, kdfConfigService, masterPasswordService, + accountService, ); } @@ -107,7 +108,9 @@ export class UpdateTempPasswordComponent extends BaseChangePasswordComponent { } async setupSubmitActions(): Promise { - this.email = await this.stateService.getEmail(); + this.email = await firstValueFrom( + this.accountService.activeAccount$.pipe(map((a) => a?.email)), + ); this.kdfConfig = await this.kdfConfigService.getKdfConfig(); return true; } diff --git a/libs/angular/src/tools/generator/components/generator.component.ts b/libs/angular/src/tools/generator/components/generator.component.ts index d1857a88ad..5015fca7fc 100644 --- a/libs/angular/src/tools/generator/components/generator.component.ts +++ b/libs/angular/src/tools/generator/components/generator.component.ts @@ -1,9 +1,10 @@ import { Directive, EventEmitter, Input, OnInit, Output } from "@angular/core"; import { ActivatedRoute } from "@angular/router"; -import { BehaviorSubject } from "rxjs"; +import { BehaviorSubject, firstValueFrom } from "rxjs"; import { debounceTime, first, map } from "rxjs/operators"; import { PasswordGeneratorPolicyOptions } from "@bitwarden/common/admin-console/models/domain/password-generator-policy-options"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; @@ -60,6 +61,7 @@ export class GeneratorComponent implements OnInit { protected i18nService: I18nService, protected logService: LogService, protected route: ActivatedRoute, + protected accountService: AccountService, private win: Window, ) { this.typeOptions = [ @@ -113,7 +115,9 @@ export class GeneratorComponent implements OnInit { this.usernameOptions.subaddressEmail == null || this.usernameOptions.subaddressEmail === "" ) { - this.usernameOptions.subaddressEmail = await this.stateService.getEmail(); + this.usernameOptions.subaddressEmail = await firstValueFrom( + this.accountService.activeAccount$.pipe(map((a) => a?.email)), + ); } if (this.usernameWebsite == null) { this.usernameOptions.subaddressType = this.usernameOptions.catchallType = "random"; diff --git a/libs/angular/src/vault/components/add-edit.component.ts b/libs/angular/src/vault/components/add-edit.component.ts index 74c368d726..ea659284de 100644 --- a/libs/angular/src/vault/components/add-edit.component.ts +++ b/libs/angular/src/vault/components/add-edit.component.ts @@ -1,6 +1,6 @@ import { DatePipe } from "@angular/common"; import { Directive, EventEmitter, Input, OnDestroy, OnInit, Output } from "@angular/core"; -import { concatMap, firstValueFrom, Observable, Subject, takeUntil } from "rxjs"; +import { concatMap, firstValueFrom, map, Observable, Subject, takeUntil } from "rxjs"; import { AuditService } from "@bitwarden/common/abstractions/audit.service"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; @@ -11,6 +11,7 @@ import { import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { OrganizationUserStatusType, PolicyType } from "@bitwarden/common/admin-console/enums"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { EventType } from "@bitwarden/common/enums"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { UriMatchStrategy } from "@bitwarden/common/models/domain/domain-service"; @@ -19,7 +20,6 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; @@ -108,7 +108,7 @@ export class AddEditComponent implements OnInit, OnDestroy { protected i18nService: I18nService, protected platformUtilsService: PlatformUtilsService, protected auditService: AuditService, - protected stateService: StateService, + protected accountService: AccountService, protected collectionService: CollectionService, protected messagingService: MessagingService, protected eventCollectionService: EventCollectionService, @@ -215,7 +215,9 @@ export class AddEditComponent implements OnInit, OnDestroy { if (this.personalOwnershipPolicyAppliesToActiveUser) { this.allowPersonal = false; } else { - const myEmail = await this.stateService.getEmail(); + const myEmail = await firstValueFrom( + this.accountService.activeAccount$.pipe(map((a) => a?.email)), + ); this.ownershipOptions.push({ name: myEmail, value: null }); } diff --git a/libs/common/src/auth/services/user-verification/user-verification.service.ts b/libs/common/src/auth/services/user-verification/user-verification.service.ts index d59d675a8d..7561023a27 100644 --- a/libs/common/src/auth/services/user-verification/user-verification.service.ts +++ b/libs/common/src/auth/services/user-verification/user-verification.service.ts @@ -1,4 +1,4 @@ -import { firstValueFrom } from "rxjs"; +import { firstValueFrom, map } from "rxjs"; import { UserDecryptionOptionsServiceAbstraction } from "@bitwarden/auth/common"; @@ -115,12 +115,14 @@ export class UserVerificationService implements UserVerificationServiceAbstracti if (verification.type === VerificationType.OTP) { request.otp = verification.secret; } else { - const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id; + const [userId, email] = await firstValueFrom( + this.accountService.activeAccount$.pipe(map((a) => [a?.id, a?.email])), + ); let masterKey = await firstValueFrom(this.masterPasswordService.masterKey$(userId)); if (!masterKey && !alreadyHashed) { masterKey = await this.cryptoService.makeMasterKey( verification.secret, - await this.stateService.getEmail(), + email, await this.kdfConfigService.getKdfConfig(), ); } @@ -138,7 +140,9 @@ export class UserVerificationService implements UserVerificationServiceAbstracti * @param verification User-supplied verification data (OTP, MP, PIN, or biometrics) */ async verifyUser(verification: Verification): Promise { - const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id; + const [userId, email] = await firstValueFrom( + this.accountService.activeAccount$.pipe(map((a) => [a?.id, a?.email])), + ); if (verificationHasSecret(verification)) { this.validateSecretInput(verification); @@ -148,7 +152,7 @@ export class UserVerificationService implements UserVerificationServiceAbstracti case VerificationType.OTP: return this.verifyUserByOTP(verification); case VerificationType.MasterPassword: - return this.verifyUserByMasterPassword(verification, userId); + return this.verifyUserByMasterPassword(verification, userId, email); case VerificationType.PIN: return this.verifyUserByPIN(verification, userId); case VerificationType.Biometrics: @@ -174,6 +178,7 @@ export class UserVerificationService implements UserVerificationServiceAbstracti private async verifyUserByMasterPassword( verification: MasterPasswordVerification, userId: UserId, + email: string, ): Promise { if (!userId) { throw new Error("User ID is required. Cannot verify user by master password."); @@ -183,7 +188,7 @@ export class UserVerificationService implements UserVerificationServiceAbstracti if (!masterKey) { masterKey = await this.cryptoService.makeMasterKey( verification.secret, - await this.stateService.getEmail(), + email, await this.kdfConfigService.getKdfConfig(), ); } diff --git a/libs/common/src/platform/abstractions/state.service.ts b/libs/common/src/platform/abstractions/state.service.ts index 0f678a6bf3..b2ea27ecb0 100644 --- a/libs/common/src/platform/abstractions/state.service.ts +++ b/libs/common/src/platform/abstractions/state.service.ts @@ -82,8 +82,6 @@ export abstract class StateService { ) => Promise; getDuckDuckGoSharedKey: (options?: StorageOptions) => Promise; setDuckDuckGoSharedKey: (value: string, options?: StorageOptions) => Promise; - getEmail: (options?: StorageOptions) => Promise; - setEmail: (value: string, options?: StorageOptions) => Promise; getEnableBrowserIntegration: (options?: StorageOptions) => Promise; setEnableBrowserIntegration: (value: boolean, options?: StorageOptions) => Promise; getEnableBrowserIntegrationFingerprint: (options?: StorageOptions) => Promise; diff --git a/libs/common/src/platform/services/crypto.service.ts b/libs/common/src/platform/services/crypto.service.ts index 2813bfb960..61dc6b81ad 100644 --- a/libs/common/src/platform/services/crypto.service.ts +++ b/libs/common/src/platform/services/crypto.service.ts @@ -1,5 +1,5 @@ import * as bigInt from "big-integer"; -import { Observable, filter, firstValueFrom, map, zip } from "rxjs"; +import { Observable, combineLatest, filter, firstValueFrom, map, zip } from "rxjs"; import { PinServiceAbstraction } from "../../../../auth/src/common/abstractions"; import { EncryptedOrganizationKeyData } from "../../admin-console/models/data/encrypted-organization-key.data"; @@ -280,11 +280,18 @@ export class CryptoService implements CryptoServiceAbstraction { // TODO: Move to MasterPasswordService async getOrDeriveMasterKey(password: string, userId?: UserId) { - userId ??= await firstValueFrom(this.stateProvider.activeUserId$); - let masterKey = await firstValueFrom(this.masterPasswordService.masterKey$(userId)); + const [resolvedUserId, email] = await firstValueFrom( + combineLatest([this.accountService.activeAccount$, this.accountService.accounts$]).pipe( + map(([activeAccount, accounts]) => { + userId ??= activeAccount?.id; + return [userId, accounts[userId]?.email]; + }), + ), + ); + let masterKey = await firstValueFrom(this.masterPasswordService.masterKey$(resolvedUserId)); return (masterKey ||= await this.makeMasterKey( password, - await this.stateService.getEmail({ userId: userId }), + email, await this.kdfConfigService.getKdfConfig(), )); } diff --git a/libs/common/src/platform/services/cryptography/multithread-encrypt.service.implementation.ts b/libs/common/src/platform/services/cryptography/multithread-encrypt.service.implementation.ts index 75a571fef2..6ac343bcb6 100644 --- a/libs/common/src/platform/services/cryptography/multithread-encrypt.service.implementation.ts +++ b/libs/common/src/platform/services/cryptography/multithread-encrypt.service.implementation.ts @@ -19,36 +19,17 @@ export class MultithreadEncryptServiceImplementation extends EncryptServiceImple private clear$ = new Subject(); /** - * Decrypts items using a web worker if the environment supports it. - * Will fall back to the main thread if the window object is not available. + * Sends items to a web worker to decrypt them. + * This utilises multithreading to decrypt items faster without interrupting other operations (e.g. updating UI). */ async decryptItems( items: Decryptable[], key: SymmetricCryptoKey, ): Promise { - if (typeof window === "undefined") { - return super.decryptItems(items, key); - } - if (items == null || items.length < 1) { return []; } - const decryptedItems = await this.getDecryptedItemsFromWorker(items, key); - const parsedItems = JSON.parse(decryptedItems); - - return this.initializeItems(parsedItems); - } - - /** - * Sends items to a web worker to decrypt them. This utilizes multithreading to decrypt items - * faster without interrupting other operations (e.g. updating UI). This method returns values - * prior to deserialization to support forwarding results to another party - */ - async getDecryptedItemsFromWorker( - items: Decryptable[], - key: SymmetricCryptoKey, - ): Promise { this.logService.info("Starting decryption using multithreading"); this.worker ??= new Worker( @@ -72,20 +53,19 @@ export class MultithreadEncryptServiceImplementation extends EncryptServiceImple return await firstValueFrom( fromEvent(this.worker, "message").pipe( filter((response: MessageEvent) => response.data?.id === request.id), - map((response) => response.data.items), + map((response) => JSON.parse(response.data.items)), + map((items) => + items.map((jsonItem: Jsonify) => { + const initializer = getClassInitializer(jsonItem.initializerKey); + return initializer(jsonItem); + }), + ), takeUntil(this.clear$), - defaultIfEmpty("[]"), + defaultIfEmpty([]), ), ); } - protected initializeItems(items: Jsonify[]): T[] { - return items.map((jsonItem: Jsonify) => { - const initializer = getClassInitializer(jsonItem.initializerKey); - return initializer(jsonItem); - }); - } - private clear() { this.clear$.next(); this.worker?.terminate(); diff --git a/libs/common/src/platform/services/state.service.ts b/libs/common/src/platform/services/state.service.ts index 497e6e6703..0339baa3fa 100644 --- a/libs/common/src/platform/services/state.service.ts +++ b/libs/common/src/platform/services/state.service.ts @@ -347,23 +347,6 @@ export class StateService< : await this.secureStorageService.save(DDG_SHARED_KEY, value, options); } - async getEmail(options?: StorageOptions): Promise { - return ( - await this.getAccount(this.reconcileOptions(options, await this.defaultInMemoryOptions())) - )?.profile?.email; - } - - async setEmail(value: string, options?: StorageOptions): Promise { - const account = await this.getAccount( - this.reconcileOptions(options, await this.defaultInMemoryOptions()), - ); - account.profile.email = value; - await this.saveAccount( - account, - this.reconcileOptions(options, await this.defaultInMemoryOptions()), - ); - } - async getEnableBrowserIntegration(options?: StorageOptions): Promise { return ( (await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) diff --git a/libs/tools/export/vault-export/vault-export-ui/src/components/export-scope-callout.component.ts b/libs/tools/export/vault-export/vault-export-ui/src/components/export-scope-callout.component.ts index 0f246c3a34..2cf93f72ae 100644 --- a/libs/tools/export/vault-export/vault-export-ui/src/components/export-scope-callout.component.ts +++ b/libs/tools/export/vault-export/vault-export-ui/src/components/export-scope-callout.component.ts @@ -1,9 +1,10 @@ import { CommonModule } from "@angular/common"; import { Component, Input, OnInit } from "@angular/core"; +import { firstValueFrom, map } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { CalloutModule } from "@bitwarden/components"; @Component({ @@ -35,7 +36,7 @@ export class ExportScopeCalloutComponent implements OnInit { constructor( protected organizationService: OrganizationService, - protected stateService: StateService, + protected accountService: AccountService, ) {} async ngOnInit(): Promise { @@ -58,7 +59,9 @@ export class ExportScopeCalloutComponent implements OnInit { : { title: "exportingPersonalVaultTitle", description: "exportingIndividualVaultDescription", - scopeIdentifier: await this.stateService.getEmail(), + scopeIdentifier: await firstValueFrom( + this.accountService.activeAccount$.pipe(map((a) => a?.email)), + ), }; } }