mirror of
https://github.com/bitwarden/browser
synced 2025-12-11 22:03:36 +00:00
[PM-20637] Trigger password reprompt when updating a reprompt cipher via notification (#14575)
* reprompt user on cipher update when enabled Co-authored-by: Daniel Riera <driera@livefront.com> * update tests * add test --------- Co-authored-by: Daniel Riera <driera@livefront.com>
This commit is contained in:
@@ -17,6 +17,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag
|
|||||||
import { SelfHostedEnvironment } from "@bitwarden/common/platform/services/default-environment.service";
|
import { SelfHostedEnvironment } from "@bitwarden/common/platform/services/default-environment.service";
|
||||||
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
|
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
|
||||||
import { UserId } from "@bitwarden/common/types/guid";
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
|
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
|
||||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||||
import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
|
import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
|
||||||
import { CipherService } from "@bitwarden/common/vault/services/cipher.service";
|
import { CipherService } from "@bitwarden/common/vault/services/cipher.service";
|
||||||
@@ -828,6 +829,7 @@ describe("NotificationBackground", () => {
|
|||||||
id: "testId",
|
id: "testId",
|
||||||
name: "testItemName",
|
name: "testItemName",
|
||||||
login: { username: "testUser" },
|
login: { username: "testUser" },
|
||||||
|
reprompt: CipherRepromptType.None,
|
||||||
});
|
});
|
||||||
getDecryptedCipherByIdSpy.mockResolvedValueOnce(cipherView);
|
getDecryptedCipherByIdSpy.mockResolvedValueOnce(cipherView);
|
||||||
|
|
||||||
@@ -842,6 +844,7 @@ describe("NotificationBackground", () => {
|
|||||||
message.edit,
|
message.edit,
|
||||||
sender.tab,
|
sender.tab,
|
||||||
"testId",
|
"testId",
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
expect(updateWithServerSpy).toHaveBeenCalled();
|
expect(updateWithServerSpy).toHaveBeenCalled();
|
||||||
expect(tabSendMessageDataSpy).toHaveBeenCalledWith(
|
expect(tabSendMessageDataSpy).toHaveBeenCalledWith(
|
||||||
@@ -855,6 +858,55 @@ describe("NotificationBackground", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("prompts the user for master password entry if the notification message type is for ChangePassword and the cipher reprompt is enabled", async () => {
|
||||||
|
const tab = createChromeTabMock({ id: 1, url: "https://example.com" });
|
||||||
|
const sender = mock<chrome.runtime.MessageSender>({ tab });
|
||||||
|
const message: NotificationBackgroundExtensionMessage = {
|
||||||
|
command: "bgSaveCipher",
|
||||||
|
edit: false,
|
||||||
|
folder: "folder-id",
|
||||||
|
};
|
||||||
|
const queueMessage = mock<AddChangePasswordQueueMessage>({
|
||||||
|
type: NotificationQueueMessageType.ChangePassword,
|
||||||
|
tab,
|
||||||
|
domain: "example.com",
|
||||||
|
newPassword: "newPassword",
|
||||||
|
});
|
||||||
|
notificationBackground["notificationQueue"] = [queueMessage];
|
||||||
|
const cipherView = mock<CipherView>({
|
||||||
|
id: "testId",
|
||||||
|
name: "testItemName",
|
||||||
|
login: { username: "testUser" },
|
||||||
|
reprompt: CipherRepromptType.Password,
|
||||||
|
});
|
||||||
|
getDecryptedCipherByIdSpy.mockResolvedValueOnce(cipherView);
|
||||||
|
|
||||||
|
sendMockExtensionMessage(message, sender);
|
||||||
|
await flushPromises();
|
||||||
|
|
||||||
|
expect(editItemSpy).not.toHaveBeenCalled();
|
||||||
|
expect(autofillService.isPasswordRepromptRequired).toHaveBeenCalled();
|
||||||
|
expect(createWithServerSpy).not.toHaveBeenCalled();
|
||||||
|
expect(updatePasswordSpy).toHaveBeenCalledWith(
|
||||||
|
cipherView,
|
||||||
|
queueMessage.newPassword,
|
||||||
|
message.edit,
|
||||||
|
sender.tab,
|
||||||
|
"testId",
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
expect(updateWithServerSpy).not.toHaveBeenCalled();
|
||||||
|
expect(tabSendMessageDataSpy).not.toHaveBeenCalledWith(
|
||||||
|
sender.tab,
|
||||||
|
"saveCipherAttemptCompleted",
|
||||||
|
{
|
||||||
|
itemName: "testItemName",
|
||||||
|
cipherId: cipherView.id,
|
||||||
|
task: undefined,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it("completes password update notification with a security task notice if any are present for the cipher, and dismisses tasks for the updated cipher", async () => {
|
it("completes password update notification with a security task notice if any are present for the cipher, and dismisses tasks for the updated cipher", async () => {
|
||||||
const mockCipherId = "testId";
|
const mockCipherId = "testId";
|
||||||
const mockOrgId = "testOrgId";
|
const mockOrgId = "testOrgId";
|
||||||
@@ -905,6 +957,7 @@ describe("NotificationBackground", () => {
|
|||||||
id: mockCipherId,
|
id: mockCipherId,
|
||||||
organizationId: mockOrgId,
|
organizationId: mockOrgId,
|
||||||
name: "Test Item",
|
name: "Test Item",
|
||||||
|
reprompt: CipherRepromptType.None,
|
||||||
});
|
});
|
||||||
getDecryptedCipherByIdSpy.mockResolvedValueOnce(cipherView);
|
getDecryptedCipherByIdSpy.mockResolvedValueOnce(cipherView);
|
||||||
|
|
||||||
@@ -919,6 +972,7 @@ describe("NotificationBackground", () => {
|
|||||||
message.edit,
|
message.edit,
|
||||||
sender.tab,
|
sender.tab,
|
||||||
mockCipherId,
|
mockCipherId,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
expect(updateWithServerSpy).toHaveBeenCalled();
|
expect(updateWithServerSpy).toHaveBeenCalled();
|
||||||
expect(tabSendMessageDataSpy).toHaveBeenCalledWith(
|
expect(tabSendMessageDataSpy).toHaveBeenCalledWith(
|
||||||
@@ -1000,6 +1054,7 @@ describe("NotificationBackground", () => {
|
|||||||
message.edit,
|
message.edit,
|
||||||
sender.tab,
|
sender.tab,
|
||||||
"testId",
|
"testId",
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
expect(editItemSpy).toHaveBeenCalled();
|
expect(editItemSpy).toHaveBeenCalled();
|
||||||
expect(updateWithServerSpy).not.toHaveBeenCalled();
|
expect(updateWithServerSpy).not.toHaveBeenCalled();
|
||||||
@@ -1170,7 +1225,7 @@ describe("NotificationBackground", () => {
|
|||||||
newPassword: "newPassword",
|
newPassword: "newPassword",
|
||||||
});
|
});
|
||||||
notificationBackground["notificationQueue"] = [queueMessage];
|
notificationBackground["notificationQueue"] = [queueMessage];
|
||||||
const cipherView = mock<CipherView>();
|
const cipherView = mock<CipherView>({ reprompt: CipherRepromptType.None });
|
||||||
getDecryptedCipherByIdSpy.mockResolvedValueOnce(cipherView);
|
getDecryptedCipherByIdSpy.mockResolvedValueOnce(cipherView);
|
||||||
const errorMessage = "fetch error";
|
const errorMessage = "fetch error";
|
||||||
updateWithServerSpy.mockImplementation(() => {
|
updateWithServerSpy.mockImplementation(() => {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
ExtensionCommand,
|
ExtensionCommand,
|
||||||
ExtensionCommandType,
|
ExtensionCommandType,
|
||||||
NOTIFICATION_BAR_LIFESPAN_MS,
|
NOTIFICATION_BAR_LIFESPAN_MS,
|
||||||
|
UPDATE_PASSWORD,
|
||||||
} from "@bitwarden/common/autofill/constants";
|
} from "@bitwarden/common/autofill/constants";
|
||||||
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
|
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
|
||||||
import { UserNotificationSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/user-notification-settings.service";
|
import { UserNotificationSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/user-notification-settings.service";
|
||||||
@@ -104,6 +105,8 @@ export default class NotificationBackground {
|
|||||||
this.removeTabFromNotificationQueue(sender.tab),
|
this.removeTabFromNotificationQueue(sender.tab),
|
||||||
bgReopenUnlockPopout: ({ sender }) => this.openUnlockPopout(sender.tab),
|
bgReopenUnlockPopout: ({ sender }) => this.openUnlockPopout(sender.tab),
|
||||||
bgSaveCipher: ({ message, sender }) => this.handleSaveCipherMessage(message, sender),
|
bgSaveCipher: ({ message, sender }) => this.handleSaveCipherMessage(message, sender),
|
||||||
|
bgHandleReprompt: ({ message, sender }: any) =>
|
||||||
|
this.handleCipherUpdateRepromptResponse(message),
|
||||||
bgUnlockPopoutOpened: ({ message, sender }) => this.unlockVault(message, sender.tab),
|
bgUnlockPopoutOpened: ({ message, sender }) => this.unlockVault(message, sender.tab),
|
||||||
checkNotificationQueue: ({ sender }) => this.checkNotificationQueue(sender.tab),
|
checkNotificationQueue: ({ sender }) => this.checkNotificationQueue(sender.tab),
|
||||||
collectPageDetailsResponse: ({ message }) =>
|
collectPageDetailsResponse: ({ message }) =>
|
||||||
@@ -631,6 +634,17 @@ export default class NotificationBackground {
|
|||||||
await this.saveOrUpdateCredentials(sender.tab, message.edit, message.folder);
|
await this.saveOrUpdateCredentials(sender.tab, message.edit, message.folder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async handleCipherUpdateRepromptResponse(message: NotificationBackgroundExtensionMessage) {
|
||||||
|
if (message.success) {
|
||||||
|
await this.saveOrUpdateCredentials(message.tab, false, undefined, true);
|
||||||
|
} else {
|
||||||
|
await BrowserApi.tabSendMessageData(message.tab, "saveCipherAttemptCompleted", {
|
||||||
|
error: "Password reprompt failed",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves or updates credentials based on the message within the
|
* Saves or updates credentials based on the message within the
|
||||||
* notification queue that is associated with the specified tab.
|
* notification queue that is associated with the specified tab.
|
||||||
@@ -639,7 +653,12 @@ export default class NotificationBackground {
|
|||||||
* @param edit - Identifies if the credentials should be edited or simply added
|
* @param edit - Identifies if the credentials should be edited or simply added
|
||||||
* @param folderId - The folder to add the cipher to
|
* @param folderId - The folder to add the cipher to
|
||||||
*/
|
*/
|
||||||
private async saveOrUpdateCredentials(tab: chrome.tabs.Tab, edit: boolean, folderId?: string) {
|
private async saveOrUpdateCredentials(
|
||||||
|
tab: chrome.tabs.Tab,
|
||||||
|
edit: boolean,
|
||||||
|
folderId?: string,
|
||||||
|
skipReprompt: boolean = false,
|
||||||
|
) {
|
||||||
for (let i = this.notificationQueue.length - 1; i >= 0; i--) {
|
for (let i = this.notificationQueue.length - 1; i >= 0; i--) {
|
||||||
const queueMessage = this.notificationQueue[i];
|
const queueMessage = this.notificationQueue[i];
|
||||||
if (
|
if (
|
||||||
@@ -654,18 +673,26 @@ export default class NotificationBackground {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.notificationQueue.splice(i, 1);
|
|
||||||
|
|
||||||
const activeUserId = await firstValueFrom(
|
const activeUserId = await firstValueFrom(
|
||||||
this.accountService.activeAccount$.pipe(getOptionalUserId),
|
this.accountService.activeAccount$.pipe(getOptionalUserId),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (queueMessage.type === NotificationQueueMessageType.ChangePassword) {
|
if (queueMessage.type === NotificationQueueMessageType.ChangePassword) {
|
||||||
const cipherView = await this.getDecryptedCipherById(queueMessage.cipherId, activeUserId);
|
const cipherView = await this.getDecryptedCipherById(queueMessage.cipherId, activeUserId);
|
||||||
await this.updatePassword(cipherView, queueMessage.newPassword, edit, tab, activeUserId);
|
|
||||||
|
await this.updatePassword(
|
||||||
|
cipherView,
|
||||||
|
queueMessage.newPassword,
|
||||||
|
edit,
|
||||||
|
tab,
|
||||||
|
activeUserId,
|
||||||
|
skipReprompt,
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.notificationQueue.splice(i, 1);
|
||||||
|
|
||||||
// If the vault was locked, check if a cipher needs updating instead of creating a new one
|
// If the vault was locked, check if a cipher needs updating instead of creating a new one
|
||||||
if (queueMessage.wasVaultLocked) {
|
if (queueMessage.wasVaultLocked) {
|
||||||
const allCiphers = await this.cipherService.getAllDecryptedForUrl(
|
const allCiphers = await this.cipherService.getAllDecryptedForUrl(
|
||||||
@@ -725,6 +752,7 @@ export default class NotificationBackground {
|
|||||||
edit: boolean,
|
edit: boolean,
|
||||||
tab: chrome.tabs.Tab,
|
tab: chrome.tabs.Tab,
|
||||||
userId: UserId,
|
userId: UserId,
|
||||||
|
skipReprompt: boolean = false,
|
||||||
) {
|
) {
|
||||||
cipherView.login.password = newPassword;
|
cipherView.login.password = newPassword;
|
||||||
|
|
||||||
@@ -758,6 +786,12 @@ export default class NotificationBackground {
|
|||||||
}
|
}
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
|
if (cipherView.reprompt && !skipReprompt) {
|
||||||
|
await this.autofillService.isPasswordRepromptRequired(cipherView, tab, UPDATE_PASSWORD);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await this.cipherService.updateWithServer(cipher);
|
await this.cipherService.updateWithServer(cipher);
|
||||||
|
|
||||||
await BrowserApi.tabSendMessageData(tab, "saveCipherAttemptCompleted", {
|
await BrowserApi.tabSendMessageData(tab, "saveCipherAttemptCompleted", {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export type NotificationsExtensionMessage = {
|
|||||||
typeData?: NotificationTypeData;
|
typeData?: NotificationTypeData;
|
||||||
height?: number;
|
height?: number;
|
||||||
error?: string;
|
error?: string;
|
||||||
|
closedByUser?: boolean;
|
||||||
fadeOutNotification?: boolean;
|
fadeOutNotification?: boolean;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -106,13 +106,15 @@ export class OverlayNotificationsContentService
|
|||||||
* @param message - The message containing the data for closing the notification bar.
|
* @param message - The message containing the data for closing the notification bar.
|
||||||
*/
|
*/
|
||||||
private handleCloseNotificationBarMessage(message: NotificationsExtensionMessage) {
|
private handleCloseNotificationBarMessage(message: NotificationsExtensionMessage) {
|
||||||
|
const closedByUser =
|
||||||
|
typeof message.data?.closedByUser === "boolean" ? message.data.closedByUser : true;
|
||||||
if (message.data?.fadeOutNotification) {
|
if (message.data?.fadeOutNotification) {
|
||||||
setElementStyles(this.notificationBarIframeElement, { opacity: "0" }, true);
|
setElementStyles(this.notificationBarIframeElement, { opacity: "0" }, true);
|
||||||
globalThis.setTimeout(() => this.closeNotificationBar(true), 150);
|
globalThis.setTimeout(() => this.closeNotificationBar(closedByUser), 150);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.closeNotificationBar(true);
|
this.closeNotificationBar(closedByUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -87,5 +87,9 @@ export abstract class AutofillService {
|
|||||||
cipherType?: CipherType,
|
cipherType?: CipherType,
|
||||||
) => Promise<string | null>;
|
) => Promise<string | null>;
|
||||||
setAutoFillOnPageLoadOrgPolicy: () => Promise<void>;
|
setAutoFillOnPageLoadOrgPolicy: () => Promise<void>;
|
||||||
isPasswordRepromptRequired: (cipher: CipherView, tab: chrome.tabs.Tab) => Promise<boolean>;
|
isPasswordRepromptRequired: (
|
||||||
|
cipher: CipherView,
|
||||||
|
tab: chrome.tabs.Tab,
|
||||||
|
action?: string,
|
||||||
|
) => Promise<boolean>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -593,15 +593,20 @@ export default class AutofillService implements AutofillServiceInterface {
|
|||||||
*
|
*
|
||||||
* @param cipher - The cipher to autofill
|
* @param cipher - The cipher to autofill
|
||||||
* @param tab - The tab to autofill
|
* @param tab - The tab to autofill
|
||||||
|
* @param action - override for default action once reprompt is completed successfully
|
||||||
*/
|
*/
|
||||||
async isPasswordRepromptRequired(cipher: CipherView, tab: chrome.tabs.Tab): Promise<boolean> {
|
async isPasswordRepromptRequired(
|
||||||
|
cipher: CipherView,
|
||||||
|
tab: chrome.tabs.Tab,
|
||||||
|
action?: string,
|
||||||
|
): Promise<boolean> {
|
||||||
const userHasMasterPasswordAndKeyHash =
|
const userHasMasterPasswordAndKeyHash =
|
||||||
await this.userVerificationService.hasMasterPasswordAndMasterKeyHash();
|
await this.userVerificationService.hasMasterPasswordAndMasterKeyHash();
|
||||||
if (cipher.reprompt === CipherRepromptType.Password && userHasMasterPasswordAndKeyHash) {
|
if (cipher.reprompt === CipherRepromptType.Password && userHasMasterPasswordAndKeyHash) {
|
||||||
if (!this.isDebouncingPasswordRepromptPopout()) {
|
if (!this.isDebouncingPasswordRepromptPopout()) {
|
||||||
await this.openVaultItemPasswordRepromptPopout(tab, {
|
await this.openVaultItemPasswordRepromptPopout(tab, {
|
||||||
cipherId: cipher.id,
|
cipherId: cipher.id,
|
||||||
action: "autofill",
|
action: action ?? "autofill",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import {
|
|||||||
COPY_USERNAME_ID,
|
COPY_USERNAME_ID,
|
||||||
COPY_VERIFICATION_CODE_ID,
|
COPY_VERIFICATION_CODE_ID,
|
||||||
SHOW_AUTOFILL_BUTTON,
|
SHOW_AUTOFILL_BUTTON,
|
||||||
|
UPDATE_PASSWORD,
|
||||||
} from "@bitwarden/common/autofill/constants";
|
} from "@bitwarden/common/autofill/constants";
|
||||||
import { EventType } from "@bitwarden/common/enums";
|
import { EventType } from "@bitwarden/common/enums";
|
||||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
@@ -49,6 +50,7 @@ import {
|
|||||||
PasswordRepromptService,
|
PasswordRepromptService,
|
||||||
} from "@bitwarden/vault";
|
} from "@bitwarden/vault";
|
||||||
|
|
||||||
|
import { sendExtensionMessage } from "../../../../../autofill/utils/index";
|
||||||
import { BrowserApi } from "../../../../../platform/browser/browser-api";
|
import { BrowserApi } from "../../../../../platform/browser/browser-api";
|
||||||
import BrowserPopupUtils from "../../../../../platform/popup/browser-popup-utils";
|
import BrowserPopupUtils from "../../../../../platform/popup/browser-popup-utils";
|
||||||
import { PopOutComponent } from "../../../../../platform/popup/components/pop-out.component";
|
import { PopOutComponent } from "../../../../../platform/popup/components/pop-out.component";
|
||||||
@@ -72,7 +74,8 @@ type LoadAction =
|
|||||||
| typeof SHOW_AUTOFILL_BUTTON
|
| typeof SHOW_AUTOFILL_BUTTON
|
||||||
| typeof COPY_USERNAME_ID
|
| typeof COPY_USERNAME_ID
|
||||||
| typeof COPY_PASSWORD_ID
|
| typeof COPY_PASSWORD_ID
|
||||||
| typeof COPY_VERIFICATION_CODE_ID;
|
| typeof COPY_VERIFICATION_CODE_ID
|
||||||
|
| typeof UPDATE_PASSWORD;
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-view-v2",
|
selector: "app-view-v2",
|
||||||
@@ -294,7 +297,7 @@ export class ViewV2Component {
|
|||||||
// Both vaultPopupAutofillService and copyCipherFieldService will perform password re-prompting internally.
|
// Both vaultPopupAutofillService and copyCipherFieldService will perform password re-prompting internally.
|
||||||
|
|
||||||
switch (loadAction) {
|
switch (loadAction) {
|
||||||
case "show-autofill-button":
|
case SHOW_AUTOFILL_BUTTON:
|
||||||
// This action simply shows the cipher view, no need to do anything.
|
// This action simply shows the cipher view, no need to do anything.
|
||||||
if (
|
if (
|
||||||
this.cipher.reprompt !== CipherRepromptType.None &&
|
this.cipher.reprompt !== CipherRepromptType.None &&
|
||||||
@@ -303,30 +306,42 @@ export class ViewV2Component {
|
|||||||
await closeViewVaultItemPopout(`${VaultPopoutType.viewVaultItem}_${this.cipher.id}`);
|
await closeViewVaultItemPopout(`${VaultPopoutType.viewVaultItem}_${this.cipher.id}`);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case "autofill":
|
case AUTOFILL_ID:
|
||||||
actionSuccess = await this.vaultPopupAutofillService.doAutofill(this.cipher, false);
|
actionSuccess = await this.vaultPopupAutofillService.doAutofill(this.cipher, false);
|
||||||
break;
|
break;
|
||||||
case "copy-username":
|
case COPY_USERNAME_ID:
|
||||||
actionSuccess = await this.copyCipherFieldService.copy(
|
actionSuccess = await this.copyCipherFieldService.copy(
|
||||||
this.cipher.login.username,
|
this.cipher.login.username,
|
||||||
"username",
|
"username",
|
||||||
this.cipher,
|
this.cipher,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case "copy-password":
|
case COPY_PASSWORD_ID:
|
||||||
actionSuccess = await this.copyCipherFieldService.copy(
|
actionSuccess = await this.copyCipherFieldService.copy(
|
||||||
this.cipher.login.password,
|
this.cipher.login.password,
|
||||||
"password",
|
"password",
|
||||||
this.cipher,
|
this.cipher,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case "copy-totp":
|
case COPY_VERIFICATION_CODE_ID:
|
||||||
actionSuccess = await this.copyCipherFieldService.copy(
|
actionSuccess = await this.copyCipherFieldService.copy(
|
||||||
this.cipher.login.totp,
|
this.cipher.login.totp,
|
||||||
"totp",
|
"totp",
|
||||||
this.cipher,
|
this.cipher,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
case UPDATE_PASSWORD: {
|
||||||
|
const repromptSuccess = await this.passwordRepromptService.showPasswordPrompt();
|
||||||
|
|
||||||
|
await sendExtensionMessage("bgHandleReprompt", {
|
||||||
|
tab: await chrome.tabs.get(senderTabId),
|
||||||
|
success: repromptSuccess,
|
||||||
|
});
|
||||||
|
|
||||||
|
await closeViewVaultItemPopout(`${VaultPopoutType.viewVaultItem}_${this.cipher.id}`);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BrowserPopupUtils.inPopout(window)) {
|
if (BrowserPopupUtils.inPopout(window)) {
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export const ClearClipboardDelay = {
|
|||||||
FiveMinutes: 300,
|
FiveMinutes: 300,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
/* Context Menu item Ids */
|
/* Ids for context menu items and messaging events */
|
||||||
export const AUTOFILL_CARD_ID = "autofill-card";
|
export const AUTOFILL_CARD_ID = "autofill-card";
|
||||||
export const AUTOFILL_ID = "autofill";
|
export const AUTOFILL_ID = "autofill";
|
||||||
export const SHOW_AUTOFILL_BUTTON = "show-autofill-button";
|
export const SHOW_AUTOFILL_BUTTON = "show-autofill-button";
|
||||||
@@ -54,6 +54,7 @@ export const GENERATE_PASSWORD_ID = "generate-password";
|
|||||||
export const NOOP_COMMAND_SUFFIX = "noop";
|
export const NOOP_COMMAND_SUFFIX = "noop";
|
||||||
export const ROOT_ID = "root";
|
export const ROOT_ID = "root";
|
||||||
export const SEPARATOR_ID = "separator";
|
export const SEPARATOR_ID = "separator";
|
||||||
|
export const UPDATE_PASSWORD = "update-password";
|
||||||
|
|
||||||
export const NOTIFICATION_BAR_LIFESPAN_MS = 150000; // 150 seconds
|
export const NOTIFICATION_BAR_LIFESPAN_MS = 150000; // 150 seconds
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user