diff --git a/apps/browser/src/autofill/background/abstractions/overlay.background.ts b/apps/browser/src/autofill/background/abstractions/overlay.background.ts index 5e2b755ad4..75f2659c9d 100644 --- a/apps/browser/src/autofill/background/abstractions/overlay.background.ts +++ b/apps/browser/src/autofill/background/abstractions/overlay.background.ts @@ -245,6 +245,7 @@ export type OverlayBackgroundExtensionMessageHandlers = { editedCipher: () => void; deletedCipher: () => void; bgSaveCipher: () => void; + updateOverlayCiphers: () => void; fido2AbortRequest: ({ message, sender }: BackgroundOnMessageHandlerParams) => void; }; diff --git a/apps/browser/src/autofill/background/overlay.background.ts b/apps/browser/src/autofill/background/overlay.background.ts index 1f24945439..f55b5c8cc3 100644 --- a/apps/browser/src/autofill/background/overlay.background.ts +++ b/apps/browser/src/autofill/background/overlay.background.ts @@ -191,6 +191,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { editedCipher: () => this.updateOverlayCiphers(), deletedCipher: () => this.updateOverlayCiphers(), bgSaveCipher: () => this.updateOverlayCiphers(), + updateOverlayCiphers: () => this.updateOverlayCiphers(), fido2AbortRequest: ({ sender }) => this.abortFido2ActiveRequest(sender.tab.id), }; private readonly inlineMenuButtonPortMessageHandlers: InlineMenuButtonPortMessageHandlers = { diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index bb3f64115c..da5f0fbfc6 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -898,6 +898,7 @@ export default class MainBackground { this.accountService, this.logService, this.cipherEncryptionService, + this.messagingService, ); this.folderService = new FolderService( this.keyService, diff --git a/apps/cli/src/service-container/service-container.ts b/apps/cli/src/service-container/service-container.ts index d4dfada05b..0ec24768b7 100644 --- a/apps/cli/src/service-container/service-container.ts +++ b/apps/cli/src/service-container/service-container.ts @@ -723,6 +723,7 @@ export class ServiceContainer { this.accountService, this.logService, this.cipherEncryptionService, + this.messagingService, ); this.folderService = new FolderService( diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index c671635bf7..6a145fb321 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -537,6 +537,7 @@ const safeProviders: SafeProvider[] = [ accountService: AccountServiceAbstraction, logService: LogService, cipherEncryptionService: CipherEncryptionService, + messagingService: MessagingServiceAbstraction, ) => new CipherService( keyService, @@ -553,6 +554,7 @@ const safeProviders: SafeProvider[] = [ accountService, logService, cipherEncryptionService, + messagingService, ), deps: [ KeyService, @@ -569,6 +571,7 @@ const safeProviders: SafeProvider[] = [ AccountServiceAbstraction, LogService, CipherEncryptionService, + MessagingServiceAbstraction, ], }), safeProvider({ diff --git a/libs/common/src/vault/services/cipher.service.spec.ts b/libs/common/src/vault/services/cipher.service.spec.ts index 996de182f6..be72d618de 100644 --- a/libs/common/src/vault/services/cipher.service.spec.ts +++ b/libs/common/src/vault/services/cipher.service.spec.ts @@ -1,5 +1,5 @@ import { mock } from "jest-mock-extended"; -import { BehaviorSubject, map, of } from "rxjs"; +import { BehaviorSubject, filter, firstValueFrom, map, of } from "rxjs"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; @@ -7,6 +7,7 @@ import { CipherResponse } from "@bitwarden/common/vault/models/response/cipher.r // This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. // eslint-disable-next-line no-restricted-imports import { CipherDecryptionKeys, KeyService } from "@bitwarden/key-management"; +import { MessageSender } from "@bitwarden/messaging"; import { FakeAccountService, mockAccountServiceWith } from "../../../spec/fake-account-service"; import { FakeStateProvider } from "../../../spec/fake-state-provider"; @@ -106,6 +107,7 @@ describe("Cipher Service", () => { const logService = mock(); const stateProvider = new FakeStateProvider(accountService); const cipherEncryptionService = mock(); + const messageSender = mock(); const userId = "TestUserId" as UserId; const orgId = "4ff8c0b2-1d3e-4f8c-9b2d-1d3e4f8c0b2" as OrganizationId; @@ -134,6 +136,7 @@ describe("Cipher Service", () => { accountService, logService, cipherEncryptionService, + messageSender, ); encryptionContext = { cipher: new Cipher(cipherData), encryptedFor: userId }; @@ -551,6 +554,23 @@ describe("Cipher Service", () => { newUserKey, ); }); + + it("sends overlay update when cipherViews$ emits", async () => { + (cipherService.cipherViews$ as jest.Mock)?.mockRestore(); + + const decryptedView = new CipherView(encryptionContext.cipher); + jest.spyOn(cipherService, "getAllDecrypted").mockResolvedValue([decryptedView]); + + const sendSpy = jest.spyOn(messageSender, "send"); + + await firstValueFrom( + cipherService + .cipherViews$(mockUserId) + .pipe(filter((cipherViews): cipherViews is CipherView[] => cipherViews != null)), + ); + expect(sendSpy).toHaveBeenCalledWith("updateOverlayCiphers"); + expect(sendSpy).toHaveBeenCalledTimes(1); + }); }); describe("decrypt", () => { diff --git a/libs/common/src/vault/services/cipher.service.ts b/libs/common/src/vault/services/cipher.service.ts index 596483c19e..ba986dcc76 100644 --- a/libs/common/src/vault/services/cipher.service.ts +++ b/libs/common/src/vault/services/cipher.service.ts @@ -1,9 +1,19 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { combineLatest, filter, firstValueFrom, map, Observable, Subject, switchMap } from "rxjs"; +import { + combineLatest, + filter, + firstValueFrom, + map, + Observable, + Subject, + switchMap, + tap, +} from "rxjs"; import { SemVer } from "semver"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { MessageSender } from "@bitwarden/common/platform/messaging"; // This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. // eslint-disable-next-line no-restricted-imports import { KeyService } from "@bitwarden/key-management"; @@ -109,6 +119,7 @@ export class CipherService implements CipherServiceAbstraction { private accountService: AccountService, private logService: LogService, private cipherEncryptionService: CipherEncryptionService, + private messageSender: MessageSender, ) {} localData$(userId: UserId): Observable> { @@ -174,6 +185,10 @@ export class CipherService implements CipherServiceAbstraction { ]).pipe( filter(([ciphers, _, keys]) => ciphers != null && keys != null), // Skip if ciphers haven't been loaded yor synced yet switchMap(() => this.getAllDecrypted(userId)), + tap(async (decrypted) => { + await this.searchService.indexCiphers(userId, decrypted); + this.messageSender.send("updateOverlayCiphers"); + }), ); }, this.clearCipherViewsForUser$);