1
0
mirror of https://github.com/bitwarden/browser synced 2026-01-26 14:23:46 +00:00

Expand generic pattern for notification queue messages.

This commit is contained in:
Miles Blackwood
2026-01-23 16:47:28 -05:00
parent 3a70b94b2d
commit de5f2167f0
3 changed files with 78 additions and 63 deletions

View File

@@ -4,62 +4,62 @@ import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
import { CollectionView } from "../../content/components/common-types";
import { NotificationType, NotificationTypes } from "../../enums/notification-type.enum";
import { NotificationType } from "../../enums/notification-type.enum";
import AutofillPageDetails from "../../models/autofill-page-details";
/**
* @todo Remove Standard_ label when implemented as standard NotificationQueueMessage.
* Generic notification queue message structure.
* All notification types use this structure with type-specific data.
*/
export interface Standard_NotificationQueueMessage<T, D> {
// universal notification properties
export interface NotificationQueueMessage<T, D> {
domain: string;
tab: chrome.tabs.Tab;
launchTimestamp: number;
expires: Date;
wasVaultLocked: boolean;
type: T; // NotificationType
data: D; // notification-specific data
type: T;
data: D;
}
/**
* @todo Deprecate in favor of Standard_NotificationQueueMessage.
*/
interface NotificationQueueMessage {
type: NotificationTypes;
domain: string;
tab: chrome.tabs.Tab;
launchTimestamp: number;
expires: Date;
wasVaultLocked: boolean;
}
// Notification data type definitions
export type AddLoginNotificationData = {
username: string;
password: string;
uri: string;
};
type ChangePasswordNotificationData = {
export type ChangePasswordNotificationData = {
cipherIds: CipherView["id"][];
newPassword: string;
};
type AddChangePasswordNotificationQueueMessage = Standard_NotificationQueueMessage<
export type UnlockVaultNotificationData = never;
export type AtRiskPasswordNotificationData = {
organizationName: string;
passwordChangeUri?: string;
};
// Notification queue message types using generic pattern
export type AddLoginQueueMessage = NotificationQueueMessage<
typeof NotificationType.AddLogin,
AddLoginNotificationData
>;
export type AddChangePasswordNotificationQueueMessage = NotificationQueueMessage<
typeof NotificationType.ChangePassword,
ChangePasswordNotificationData
>;
interface AddLoginQueueMessage extends NotificationQueueMessage {
type: "add";
username: string;
password: string;
uri: string;
}
export type AddUnlockVaultQueueMessage = NotificationQueueMessage<
typeof NotificationType.UnlockVault,
UnlockVaultNotificationData
>;
interface AddUnlockVaultQueueMessage extends NotificationQueueMessage {
type: "unlock";
}
interface AtRiskPasswordQueueMessage extends NotificationQueueMessage {
type: "at-risk-password";
organizationName: string;
passwordChangeUri?: string;
}
export type AtRiskPasswordQueueMessage = NotificationQueueMessage<
typeof NotificationType.AtRiskPassword,
AtRiskPasswordNotificationData
>;
type NotificationQueueMessageItem =
| AddLoginQueueMessage
@@ -152,9 +152,6 @@ type NotificationBackgroundExtensionMessageHandlers = {
};
export {
AddChangePasswordNotificationQueueMessage,
AddLoginQueueMessage,
AddUnlockVaultQueueMessage,
NotificationQueueMessageItem,
LockedVaultPendingNotificationsData,
AdjustNotificationBarMessageData,

View File

@@ -122,9 +122,11 @@ describe("NotificationBackground", () => {
it("returns a cipher view when passed an `AddLoginQueueMessage`", () => {
const message: AddLoginQueueMessage = {
type: "add",
username: "test",
password: "password",
uri: "https://example.com",
data: {
username: "test",
password: "password",
uri: "https://example.com",
},
domain: "",
tab: createChromeTabMock(),
expires: new Date(),
@@ -136,13 +138,13 @@ describe("NotificationBackground", () => {
expect(cipherView.name).toEqual("example.com");
expect(cipherView.login).toEqual({
fido2Credentials: [],
password: message.password,
password: message.data.password,
uris: [
{
_uri: message.uri,
_uri: message.data.uri,
},
],
username: message.username,
username: message.data.username,
});
});
@@ -150,9 +152,11 @@ describe("NotificationBackground", () => {
const folderId = "folder-id";
const message: AddLoginQueueMessage = {
type: "add",
username: "test",
password: "password",
uri: "https://example.com",
data: {
username: "test",
password: "password",
uri: "https://example.com",
},
domain: "example.com",
tab: createChromeTabMock(),
expires: new Date(),
@@ -1051,8 +1055,11 @@ describe("NotificationBackground", () => {
type: NotificationType.AddLogin,
tab,
domain: "example.com",
username: "test",
password: "updated-password",
data: {
username: "test",
password: "updated-password",
uri: "https://example.com",
},
wasVaultLocked: true,
});
notificationBackground["notificationQueue"] = [queueMessage];
@@ -1066,7 +1073,7 @@ describe("NotificationBackground", () => {
expect(updatePasswordSpy).toHaveBeenCalledWith(
cipherView,
queueMessage.password,
queueMessage.data.password,
message.edit,
sender.tab,
"testId",

View File

@@ -499,12 +499,14 @@ export default class NotificationBackground {
this.removeTabFromNotificationQueue(tab);
const launchTimestamp = new Date().getTime();
const queueMessage: NotificationQueueMessageItem = {
const queueMessage: AtRiskPasswordQueueMessage = {
domain,
wasVaultLocked,
type: NotificationType.AtRiskPassword,
passwordChangeUri,
organizationName: organization.name,
data: {
passwordChangeUri,
organizationName: organization.name,
},
tab: tab,
launchTimestamp,
expires: new Date(launchTimestamp + NOTIFICATION_BAR_LIFESPAN_MS),
@@ -583,10 +585,12 @@ export default class NotificationBackground {
const launchTimestamp = new Date().getTime();
const message: AddLoginQueueMessage = {
type: NotificationType.AddLogin,
username: loginInfo.username,
password: loginInfo.password,
data: {
username: loginInfo.username,
password: loginInfo.password,
uri: loginInfo.url,
},
domain: loginDomain,
uri: loginInfo.url,
tab: tab,
launchTimestamp,
expires: new Date(launchTimestamp + NOTIFICATION_BAR_LIFESPAN_MS),
@@ -843,16 +847,23 @@ export default class NotificationBackground {
// If the vault was locked, check if a cipher needs updating instead of creating a new one
if (queueMessage.wasVaultLocked) {
const allCiphers = await this.cipherService.getAllDecryptedForUrl(
queueMessage.uri,
queueMessage.data.uri,
activeUserId,
);
const existingCipher = allCiphers.find(
(c) =>
c.login.username != null && c.login.username.toLowerCase() === queueMessage.username,
c.login.username != null &&
c.login.username.toLowerCase() === queueMessage.data.username,
);
if (existingCipher != null) {
await this.updatePassword(existingCipher, queueMessage.password, edit, tab, activeUserId);
await this.updatePassword(
existingCipher,
queueMessage.data.password,
edit,
tab,
activeUserId,
);
return;
}
}
@@ -1276,15 +1287,15 @@ export default class NotificationBackground {
folderId?: string,
): CipherView {
const uriView = new LoginUriView();
uriView.uri = message.uri;
uriView.uri = message.data.uri;
const loginView = new LoginView();
loginView.uris = [uriView];
loginView.username = message.username;
loginView.password = message.password;
loginView.username = message.data.username;
loginView.password = message.data.password;
const cipherView = new CipherView();
cipherView.name = (Utils.getHostname(message.uri) || message.domain).replace(/^www\./, "");
cipherView.name = (Utils.getHostname(message.data.uri) || message.domain).replace(/^www\./, "");
cipherView.folderId = folderId;
cipherView.type = CipherType.Login;
cipherView.login = loginView;