diff --git a/apps/browser/src/autofill/background/notification.background.spec.ts b/apps/browser/src/autofill/background/notification.background.spec.ts index e02e3d8d951..d474e303336 100644 --- a/apps/browser/src/autofill/background/notification.background.spec.ts +++ b/apps/browser/src/autofill/background/notification.background.spec.ts @@ -1,6 +1,7 @@ import { mock, MockProxy } from "jest-mock-extended"; import { BehaviorSubject, firstValueFrom } from "rxjs"; +import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/services/policy/policy.service"; import { AccountInfo, AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; @@ -59,6 +60,7 @@ describe("NotificationBackground", () => { const themeStateService = mock(); const configService = mock(); const accountService = mock(); + const organizationService = mock(); const activeAccountSubject = new BehaviorSubject<{ id: UserId } & AccountInfo>({ id: "testId" as UserId, @@ -73,18 +75,19 @@ describe("NotificationBackground", () => { authService.activeAccountStatus$ = activeAccountStatusMock$; accountService.activeAccount$ = activeAccountSubject; notificationBackground = new NotificationBackground( + accountService, + authService, autofillService, cipherService, - authService, - policyService, - folderService, - userNotificationSettingsService, + configService, domainSettingsService, environmentService, + folderService, logService, + organizationService, + policyService, themeStateService, - configService, - accountService, + userNotificationSettingsService, ); }); diff --git a/apps/browser/src/autofill/background/notification.background.ts b/apps/browser/src/autofill/background/notification.background.ts index 11037e7e261..50e0ee0aa75 100644 --- a/apps/browser/src/autofill/background/notification.background.ts +++ b/apps/browser/src/autofill/background/notification.background.ts @@ -2,6 +2,7 @@ // @ts-strict-ignore import { firstValueFrom } from "rxjs"; +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 { PolicyType } from "@bitwarden/common/admin-console/enums"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; @@ -63,46 +64,48 @@ export default class NotificationBackground { ExtensionCommand.AutofillIdentity, ]); private readonly extensionMessageHandlers: NotificationBackgroundExtensionMessageHandlers = { - unlockCompleted: ({ message, sender }) => this.handleUnlockCompleted(message, sender), - bgGetFolderData: () => this.getFolderData(), - bgCloseNotificationBar: ({ message, sender }) => - this.handleCloseNotificationBarMessage(message, sender), + bgAddLogin: ({ message, sender }) => this.addLogin(message, sender), bgAdjustNotificationBar: ({ message, sender }) => this.handleAdjustNotificationBarMessage(message, sender), - bgAddLogin: ({ message, sender }) => this.addLogin(message, sender), bgChangedPassword: ({ message, sender }) => this.changedPassword(message, sender), - bgRemoveTabFromNotificationQueue: ({ sender }) => - this.removeTabFromNotificationQueue(sender.tab), - bgSaveCipher: ({ message, sender }) => this.handleSaveCipherMessage(message, sender), - bgNeverSave: ({ sender }) => this.saveNever(sender.tab), - collectPageDetailsResponse: ({ message }) => - this.handleCollectPageDetailsResponseMessage(message), - bgUnlockPopoutOpened: ({ message, sender }) => this.unlockVault(message, sender.tab), - checkNotificationQueue: ({ sender }) => this.checkNotificationQueue(sender.tab), - bgReopenUnlockPopout: ({ sender }) => this.openUnlockPopout(sender.tab), + bgCloseNotificationBar: ({ message, sender }) => + this.handleCloseNotificationBarMessage(message, sender), + bgGetActiveUserServerConfig: () => this.getActiveUserServerConfig(), + bgGetDecryptedCiphers: () => this.getNotificationCipherData(), bgGetEnableChangedPasswordPrompt: () => this.getEnableChangedPasswordPrompt(), bgGetEnableAddedLoginPrompt: () => this.getEnableAddedLoginPrompt(), bgGetExcludedDomains: () => this.getExcludedDomains(), - bgGetActiveUserServerConfig: () => this.getActiveUserServerConfig(), + bgGetFolderData: () => this.getFolderData(), + bgGetOrgData: () => this.getOrgData(), + bgNeverSave: ({ sender }) => this.saveNever(sender.tab), + bgOpenVault: ({ message, sender }) => this.openVault(message, sender.tab), + bgRemoveTabFromNotificationQueue: ({ sender }) => + this.removeTabFromNotificationQueue(sender.tab), + bgReopenUnlockPopout: ({ sender }) => this.openUnlockPopout(sender.tab), + bgSaveCipher: ({ message, sender }) => this.handleSaveCipherMessage(message, sender), + bgUnlockPopoutOpened: ({ message, sender }) => this.unlockVault(message, sender.tab), + checkNotificationQueue: ({ sender }) => this.checkNotificationQueue(sender.tab), + collectPageDetailsResponse: ({ message }) => + this.handleCollectPageDetailsResponseMessage(message), getWebVaultUrlForNotification: () => this.getWebVaultUrl(), notificationRefreshFlagValue: () => this.getNotificationFlag(), - bgGetDecryptedCiphers: () => this.getNotificationCipherData(), - bgOpenVault: ({ message, sender }) => this.openVault(message, sender.tab), + unlockCompleted: ({ message, sender }) => this.handleUnlockCompleted(message, sender), }; constructor( + private accountService: AccountService, + private authService: AuthService, private autofillService: AutofillService, private cipherService: CipherService, - private authService: AuthService, - private policyService: PolicyService, - private folderService: FolderService, - private userNotificationSettingsService: UserNotificationSettingsServiceAbstraction, + private configService: ConfigService, private domainSettingsService: DomainSettingsService, private environmentService: EnvironmentService, + private folderService: FolderService, private logService: LogService, + private organizationService: OrganizationService, + private policyService: PolicyService, private themeStateService: ThemeStateService, - private configService: ConfigService, - private accountService: AccountService, + private userNotificationSettingsService: UserNotificationSettingsServiceAbstraction, ) {} init() { @@ -744,6 +747,26 @@ export default class NotificationBackground { ); } + /** + * Returns the first value found from the organization service organizations$ observable. + */ + private async getOrgData() { + const activeUserId = await firstValueFrom( + this.accountService.activeAccount$.pipe(getOptionalUserId), + ); + const organizations = await firstValueFrom( + this.organizationService.organizations$(activeUserId), + ); + return organizations.map((org) => { + const { id, name, productTierType } = org; + return { + id, + name, + productTierType, + }; + }); + } + /** * Handles the unlockCompleted extension message. Will close the notification bar * after an attempted autofill action, and retry the autofill action if the message diff --git a/apps/browser/src/autofill/content/components/notification/body.ts b/apps/browser/src/autofill/content/components/notification/body.ts index 2433381dfba..66b580bde43 100644 --- a/apps/browser/src/autofill/content/components/notification/body.ts +++ b/apps/browser/src/autofill/content/components/notification/body.ts @@ -16,12 +16,12 @@ const { css } = createEmotion({ }); export function NotificationBody({ - ciphers, + ciphers = [], notificationType, theme = ThemeTypes.Light, handleEditOrUpdateAction, }: { - ciphers: NotificationCipherData[]; + ciphers?: NotificationCipherData[]; customClasses?: string[]; notificationType?: NotificationType; theme: Theme; diff --git a/apps/browser/src/autofill/content/components/notification/container.ts b/apps/browser/src/autofill/content/components/notification/container.ts index f98ef795749..8d80dc9fb50 100644 --- a/apps/browser/src/autofill/content/components/notification/container.ts +++ b/apps/browser/src/autofill/content/components/notification/container.ts @@ -9,6 +9,7 @@ import { NotificationType, } from "../../../notification/abstractions/notification-bar"; import { NotificationCipherData } from "../cipher/types"; +import { FolderView, OrgView } from "../common-types"; import { themes, spacing } from "../constants/styles"; import { NotificationBody, componentClassPrefix as notificationBodyClassPrefix } from "./body"; @@ -20,20 +21,24 @@ import { export function NotificationContainer({ handleCloseNotification, + handleEditOrUpdateAction, + handleSaveAction, + ciphers, + folders, i18n, + organizations, theme = ThemeTypes.Light, type, - ciphers, - handleSaveAction, - handleEditOrUpdateAction, }: NotificationBarIframeInitData & { handleCloseNotification: (e: Event) => void; handleSaveAction: (e: Event) => void; handleEditOrUpdateAction: (e: Event) => void; } & { + ciphers?: NotificationCipherData[]; + folders?: FolderView[]; i18n: { [key: string]: string }; + organizations?: OrgView[]; type: NotificationType; // @TODO typing override for generic `NotificationBarIframeInitData.type` - ciphers: NotificationCipherData[]; }) { const headerMessage = getHeaderMessage(i18n, type); const showBody = true; @@ -42,8 +47,8 @@ export function NotificationContainer({
${NotificationHeader({ handleCloseNotification, - standalone: showBody, message: headerMessage, + standalone: showBody, theme, })} ${showBody @@ -56,9 +61,11 @@ export function NotificationContainer({ : null} ${NotificationFooter({ handleSaveAction, - theme, - notificationType: type, + folders, i18n, + notificationType: type, + organizations, + theme, })}
`; diff --git a/apps/browser/src/autofill/notification/abstractions/notification-bar.ts b/apps/browser/src/autofill/notification/abstractions/notification-bar.ts index cb14a86dffa..6e7427e3a38 100644 --- a/apps/browser/src/autofill/notification/abstractions/notification-bar.ts +++ b/apps/browser/src/autofill/notification/abstractions/notification-bar.ts @@ -1,5 +1,8 @@ import { Theme } from "@bitwarden/common/platform/enums"; +import { NotificationCipherData } from "../../../autofill/content/components/cipher/types"; +import { FolderView, OrgView } from "../../../autofill/content/components/common-types"; + const NotificationTypes = { Add: "add", Change: "change", @@ -9,21 +12,24 @@ const NotificationTypes = { type NotificationType = (typeof NotificationTypes)[keyof typeof NotificationTypes]; type NotificationBarIframeInitData = { - type?: string; // @TODO use `NotificationType` - isVaultLocked?: boolean; - theme?: Theme; - removeIndividualVault?: boolean; - importType?: string; applyRedesign?: boolean; + ciphers?: NotificationCipherData[]; + folders?: FolderView[]; + importType?: string; + isVaultLocked?: boolean; launchTimestamp?: number; + organizations?: OrgView[]; + removeIndividualVault?: boolean; + theme?: Theme; + type?: string; // @TODO use `NotificationType` }; type NotificationBarWindowMessage = { + cipherId?: string; command: string; error?: string; initData?: NotificationBarIframeInitData; username?: string; - cipherId?: string; }; type NotificationBarWindowMessageHandlers = { diff --git a/apps/browser/src/autofill/notification/bar.ts b/apps/browser/src/autofill/notification/bar.ts index 617b1e58c14..d17c008372d 100644 --- a/apps/browser/src/autofill/notification/bar.ts +++ b/apps/browser/src/autofill/notification/bar.ts @@ -5,6 +5,8 @@ import { ConsoleLogService } from "@bitwarden/common/platform/services/console-l import type { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; import { AdjustNotificationBarMessageData } from "../background/abstractions/notification.background"; +import { NotificationCipherData } from "../content/components/cipher/types"; +import { OrgView } from "../content/components/common-types"; import { NotificationConfirmationContainer } from "../content/components/notification/confirmation-container"; import { NotificationContainer } from "../content/components/notification/container"; import { buildSvgDomElement } from "../utils"; @@ -115,7 +117,7 @@ function setElementText(template: HTMLTemplateElement, elementId: string, text: } } -function initNotificationBar(message: NotificationBarWindowMessage) { +async function initNotificationBar(message: NotificationBarWindowMessage) { const { initData } = message; if (!initData) { return; @@ -131,7 +133,23 @@ function initNotificationBar(message: NotificationBarWindowMessage) { // Current implementations utilize a require for scss files which creates the need to remove the node. document.head.querySelectorAll('link[rel="stylesheet"]').forEach((node) => node.remove()); - sendPlatformMessage({ command: "bgGetDecryptedCiphers" }, (cipherData) => { + await Promise.all([ + new Promise((resolve) => + sendPlatformMessage({ command: "bgGetOrgData" }, resolve), + ), + new Promise((resolve) => + sendPlatformMessage({ command: "bgGetFolderData" }, resolve), + ), + new Promise((resolve) => + sendPlatformMessage({ command: "bgGetDecryptedCiphers" }, resolve), + ), + ]).then(([organizations, folders, ciphers]) => { + notificationBarIframeInitData = { + ...notificationBarIframeInitData, + folders, + ciphers, + organizations, + }; // @TODO use context to avoid prop drilling return render( NotificationContainer({ @@ -142,7 +160,6 @@ function initNotificationBar(message: NotificationBarWindowMessage) { handleSaveAction, handleEditOrUpdateAction, i18n, - ciphers: cipherData, }), document.body, ); diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index f8f86e6a277..74fa6acdf79 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -1173,18 +1173,19 @@ export default class MainBackground { () => this.generatePasswordToClipboard(), ); this.notificationBackground = new NotificationBackground( + this.accountService, + this.authService, this.autofillService, this.cipherService, - this.authService, - this.policyService, - this.folderService, - this.userNotificationSettingsService, + this.configService, this.domainSettingsService, this.environmentService, + this.folderService, this.logService, + this.organizationService, + this.policyService, this.themeStateService, - this.configService, - this.accountService, + this.userNotificationSettingsService, ); this.overlayNotificationsBackground = new OverlayNotificationsBackground(