mirror of
https://github.com/bitwarden/browser
synced 2026-02-11 14:04:03 +00:00
Ensure translations and action button are loading.
This commit is contained in:
@@ -2493,6 +2493,10 @@
|
||||
"change": {
|
||||
"message": "Change"
|
||||
},
|
||||
"changePassword": {
|
||||
"message": "Change password",
|
||||
"description": "Change password button for browser at risk notification on login."
|
||||
},
|
||||
"changeButtonTitle": {
|
||||
"message": "Change password - $ITEMNAME$",
|
||||
"placeholders": {
|
||||
@@ -2502,6 +2506,9 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"atRiskPassword": {
|
||||
"message": "At-risk password"
|
||||
},
|
||||
"atRiskPasswords": {
|
||||
"message": "At-risk passwords"
|
||||
},
|
||||
@@ -2536,6 +2543,26 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"atRiskChangePrompt": {
|
||||
"message": "Your password for this site is at-risk. $ORGANIZATION$ has requested that you change it.",
|
||||
"placeholders": {
|
||||
"organization": {
|
||||
"content": "$1",
|
||||
"example": "Acme Corp"
|
||||
}
|
||||
},
|
||||
"description": "Notification body when a login triggers an at-risk password change request and the change password domain is known."
|
||||
},
|
||||
"atRiskNavigatePrompt": {
|
||||
"message": "$ORGANIZATION$ wants you to change this password because it is at-risk. Navigate to your account settings to change the password.",
|
||||
"placeholders": {
|
||||
"organization": {
|
||||
"content": "$1",
|
||||
"example": "Acme Corp"
|
||||
}
|
||||
},
|
||||
"description": "Notification body when a login triggers an at-risk password change request and no change password domain is provided."
|
||||
},
|
||||
"reviewAndChangeAtRiskPassword": {
|
||||
"message": "Review and change one at-risk password"
|
||||
},
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||
import { NeverDomains } from "@bitwarden/common/models/domain/domain-service";
|
||||
import { ServerConfig } from "@bitwarden/common/platform/abstractions/config/server-config";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
@@ -38,8 +37,8 @@ interface AddUnlockVaultQueueMessage extends NotificationQueueMessage {
|
||||
|
||||
interface AtRiskPasswordQueueMessage extends NotificationQueueMessage {
|
||||
type: "at-risk-password";
|
||||
organization: Organization;
|
||||
cipher: CipherView;
|
||||
organizationName: string;
|
||||
passwordChangeUri?: string;
|
||||
}
|
||||
|
||||
type NotificationQueueMessageItem =
|
||||
|
||||
@@ -343,12 +343,17 @@ export default class NotificationBackground {
|
||||
tab: chrome.tabs.Tab,
|
||||
notificationQueueMessage: NotificationQueueMessageItem,
|
||||
) {
|
||||
const notificationType = notificationQueueMessage.type;
|
||||
const {
|
||||
type: notificationType,
|
||||
wasVaultLocked: isVaultLocked,
|
||||
launchTimestamp,
|
||||
...params
|
||||
} = notificationQueueMessage;
|
||||
|
||||
const typeData: NotificationTypeData = {
|
||||
isVaultLocked: notificationQueueMessage.wasVaultLocked,
|
||||
isVaultLocked,
|
||||
theme: await firstValueFrom(this.themeStateService.selectedTheme$),
|
||||
launchTimestamp: notificationQueueMessage.launchTimestamp,
|
||||
launchTimestamp,
|
||||
};
|
||||
|
||||
switch (notificationType) {
|
||||
@@ -360,6 +365,7 @@ export default class NotificationBackground {
|
||||
await BrowserApi.tabSendMessageData(tab, "openNotificationBar", {
|
||||
type: notificationType,
|
||||
typeData,
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -387,7 +393,7 @@ export default class NotificationBackground {
|
||||
message: NotificationBackgroundExtensionMessage,
|
||||
sender: chrome.runtime.MessageSender,
|
||||
) {
|
||||
const { activeUserId, cipher, securityTask, uri } = message.data;
|
||||
const { activeUserId, securityTask, uri } = message.data;
|
||||
|
||||
const domain = Utils.getDomain(uri);
|
||||
const addLoginIsEnabled = await this.getEnableAddedLoginPrompt();
|
||||
@@ -405,9 +411,9 @@ export default class NotificationBackground {
|
||||
domain,
|
||||
wasVaultLocked,
|
||||
type: NotificationQueueMessageType.AtRiskPassword,
|
||||
organization: organization,
|
||||
passwordChangeUri: domain,
|
||||
organizationName: organization.name,
|
||||
tab: sender.tab,
|
||||
cipher,
|
||||
launchTimestamp,
|
||||
expires: new Date(launchTimestamp + NOTIFICATION_BAR_LIFESPAN_MS),
|
||||
};
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { mock, MockProxy } from "jest-mock-extended";
|
||||
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { CLEAR_NOTIFICATION_LOGIN_DATA_DURATION } from "@bitwarden/common/autofill/constants";
|
||||
import { ServerConfig } from "@bitwarden/common/platform/abstractions/config/server-config";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { EnvironmentServerConfigData } from "@bitwarden/common/platform/models/data/server-config.data";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { TaskService } from "@bitwarden/common/vault/tasks";
|
||||
|
||||
import { BrowserApi } from "../../platform/browser/browser-api";
|
||||
import AutofillField from "../models/autofill-field";
|
||||
@@ -24,6 +27,9 @@ import { OverlayNotificationsBackground } from "./overlay-notifications.backgrou
|
||||
describe("OverlayNotificationsBackground", () => {
|
||||
let logService: MockProxy<LogService>;
|
||||
let notificationBackground: NotificationBackground;
|
||||
let taskService: TaskService;
|
||||
let accountService: AccountService;
|
||||
let cipherService: CipherService;
|
||||
let getEnableChangedPasswordPromptSpy: jest.SpyInstance;
|
||||
let getEnableAddedLoginPromptSpy: jest.SpyInstance;
|
||||
let overlayNotificationsBackground: OverlayNotificationsBackground;
|
||||
@@ -32,6 +38,9 @@ describe("OverlayNotificationsBackground", () => {
|
||||
jest.useFakeTimers();
|
||||
logService = mock<LogService>();
|
||||
notificationBackground = mock<NotificationBackground>();
|
||||
taskService = mock<TaskService>();
|
||||
accountService = mock<AccountService>();
|
||||
cipherService = mock<CipherService>();
|
||||
getEnableChangedPasswordPromptSpy = jest
|
||||
.spyOn(notificationBackground, "getEnableChangedPasswordPrompt")
|
||||
.mockResolvedValue(true);
|
||||
@@ -41,6 +50,9 @@ describe("OverlayNotificationsBackground", () => {
|
||||
overlayNotificationsBackground = new OverlayNotificationsBackground(
|
||||
logService,
|
||||
notificationBackground,
|
||||
taskService,
|
||||
accountService,
|
||||
cipherService,
|
||||
);
|
||||
await overlayNotificationsBackground.init();
|
||||
});
|
||||
|
||||
@@ -4,6 +4,7 @@ import { html } from "lit";
|
||||
import { Theme, ThemeTypes } from "@bitwarden/common/platform/enums";
|
||||
|
||||
import {
|
||||
NotificationMessageParams,
|
||||
NotificationType,
|
||||
NotificationTypes,
|
||||
} from "../../../notification/abstractions/notification-bar";
|
||||
@@ -20,29 +21,31 @@ const { css } = createEmotion({
|
||||
|
||||
export function NotificationBody({
|
||||
ciphers = [],
|
||||
passwordChangeUri,
|
||||
i18n,
|
||||
notificationType,
|
||||
theme = ThemeTypes.Light,
|
||||
handleEditOrUpdateAction,
|
||||
params = {},
|
||||
}: {
|
||||
ciphers?: NotificationCipherData[];
|
||||
passwordChangeUri: string;
|
||||
customClasses?: string[];
|
||||
i18n: { [key: string]: string };
|
||||
notificationType?: NotificationType;
|
||||
theme: Theme;
|
||||
handleEditOrUpdateAction: (e: Event) => void;
|
||||
params?: NotificationMessageParams;
|
||||
}) {
|
||||
// @TODO get client vendor from context
|
||||
const isSafari = false;
|
||||
const { passwordChangeUri, organizationName } = params;
|
||||
|
||||
switch (notificationType) {
|
||||
case NotificationTypes.AtRiskPassword:
|
||||
return html`
|
||||
<div class=${notificationBodyStyles({ isSafari, theme })}>
|
||||
${passwordChangeUri ? i18n.atRiskChangePrompt : i18n.atRiskNavigatePrompt}
|
||||
${passwordChangeUri && i18n.changePassword}
|
||||
${passwordChangeUri
|
||||
? chrome.i18n.getMessage("atRiskChangePrompt", organizationName)
|
||||
: chrome.i18n.getMessage("atRiskNavigatePrompt", organizationName)}
|
||||
</div>
|
||||
`;
|
||||
default:
|
||||
|
||||
@@ -134,6 +134,8 @@ function getHeaderMessage(
|
||||
return i18n.loginSaveSuccess;
|
||||
case NotificationTypes.Change:
|
||||
return i18n.loginUpdateSuccess;
|
||||
case NotificationTypes.AtRiskPassword:
|
||||
return i18n.changePassword;
|
||||
case NotificationTypes.Unlock:
|
||||
return "";
|
||||
default:
|
||||
|
||||
@@ -31,6 +31,7 @@ export type NotificationContainerProps = NotificationBarIframeInitData & {
|
||||
organizations?: OrgView[];
|
||||
personalVaultIsAllowed?: boolean;
|
||||
type: NotificationType; // @TODO typing override for generic `NotificationBarIframeInitData.type`
|
||||
params: object;
|
||||
};
|
||||
|
||||
export function NotificationContainer({
|
||||
@@ -45,6 +46,7 @@ export function NotificationContainer({
|
||||
personalVaultIsAllowed = true,
|
||||
theme = ThemeTypes.Light,
|
||||
type,
|
||||
params,
|
||||
}: NotificationContainerProps) {
|
||||
const headerMessage = getHeaderMessage(i18n, type);
|
||||
const showBody = true;
|
||||
@@ -64,6 +66,7 @@ export function NotificationContainer({
|
||||
notificationType: type,
|
||||
theme,
|
||||
i18n,
|
||||
params,
|
||||
})
|
||||
: null}
|
||||
${NotificationFooter({
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
NotificationType,
|
||||
NotificationTypes,
|
||||
} from "../../../notification/abstractions/notification-bar";
|
||||
import { ActionButton } from "../buttons/action-button";
|
||||
import { OrgView, FolderView, CollectionView } from "../common-types";
|
||||
import { spacing, themes } from "../constants/styles";
|
||||
|
||||
@@ -36,6 +37,16 @@ export function NotificationFooter({
|
||||
const isChangeNotification = notificationType === NotificationTypes.Change;
|
||||
const primaryButtonText = i18n.saveAction;
|
||||
|
||||
if (notificationType === NotificationTypes.AtRiskPassword) {
|
||||
return html`<div class=${notificationFooterStyles({ theme })}>
|
||||
${ActionButton({
|
||||
handleClick: () => {},
|
||||
buttonText: i18n.changePassword,
|
||||
theme,
|
||||
})}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
return html`
|
||||
<div class=${notificationFooterStyles({ theme })}>
|
||||
${!isChangeNotification
|
||||
|
||||
@@ -33,6 +33,7 @@ type NotificationBarIframeInitData = {
|
||||
theme?: Theme;
|
||||
type?: NotificationType; // @TODO use `NotificationType`
|
||||
passwordChangeUri?: string;
|
||||
params?: NotificationMessageParams;
|
||||
};
|
||||
|
||||
type NotificationBarWindowMessage = {
|
||||
@@ -52,7 +53,15 @@ type NotificationBarWindowMessageHandlers = {
|
||||
saveCipherAttemptCompleted: ({ message }: { message: NotificationBarWindowMessage }) => void;
|
||||
};
|
||||
|
||||
type NotificationMessageParamsAtRiskPasswordType = {
|
||||
passwordChangeUri?: string;
|
||||
organizationName: string;
|
||||
};
|
||||
|
||||
type NotificationMessageParams = NotificationMessageParamsAtRiskPasswordType | any;
|
||||
|
||||
export {
|
||||
NotificationMessageParams,
|
||||
NotificationTaskInfo,
|
||||
NotificationTypes,
|
||||
NotificationType,
|
||||
|
||||
@@ -56,8 +56,6 @@ function getI18n() {
|
||||
return {
|
||||
appName: chrome.i18n.getMessage("appName"),
|
||||
atRiskPassword: chrome.i18n.getMessage("atRiskPassword"),
|
||||
atRiskChangePrompt: chrome.i18n.getMessage("atRiskChangePrompt"),
|
||||
atRiskNavigatePrompt: chrome.i18n.getMessage("atRiskNavigatePrompt"),
|
||||
changePassword: chrome.i18n.getMessage("changePassword"),
|
||||
close: chrome.i18n.getMessage("close"),
|
||||
collection: chrome.i18n.getMessage("collection"),
|
||||
@@ -185,6 +183,7 @@ async function initNotificationBar(message: NotificationBarWindowMessage) {
|
||||
handleSaveAction,
|
||||
handleEditOrUpdateAction,
|
||||
i18n,
|
||||
params: initData.params,
|
||||
}),
|
||||
document.body,
|
||||
);
|
||||
|
||||
@@ -16,6 +16,7 @@ export type NotificationsExtensionMessage = {
|
||||
height?: number;
|
||||
error?: string;
|
||||
fadeOutNotification?: boolean;
|
||||
params: object;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
// @ts-strict-ignore
|
||||
import { EVENTS } from "@bitwarden/common/autofill/constants";
|
||||
|
||||
import { NotificationBarIframeInitData } from "../../../notification/abstractions/notification-bar";
|
||||
import {
|
||||
NotificationBarIframeInitData,
|
||||
NotificationType,
|
||||
} from "../../../notification/abstractions/notification-bar";
|
||||
import { sendExtensionMessage, setElementStyles } from "../../../utils";
|
||||
import {
|
||||
NotificationsExtensionMessage,
|
||||
@@ -78,17 +81,19 @@ export class OverlayNotificationsContentService
|
||||
return;
|
||||
}
|
||||
|
||||
const { type, typeData } = message.data;
|
||||
const { type, typeData, params } = message.data;
|
||||
|
||||
if (this.currentNotificationBarType && type !== this.currentNotificationBarType) {
|
||||
this.closeNotificationBar();
|
||||
}
|
||||
const initData = {
|
||||
type,
|
||||
type: type as NotificationType,
|
||||
isVaultLocked: typeData.isVaultLocked,
|
||||
theme: typeData.theme,
|
||||
removeIndividualVault: typeData.removeIndividualVault,
|
||||
importType: typeData.importType,
|
||||
launchTimestamp: typeData.launchTimestamp,
|
||||
params,
|
||||
};
|
||||
|
||||
if (globalThis.document.readyState === "loading") {
|
||||
|
||||
Reference in New Issue
Block a user