1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-06 00:13:28 +00:00

[PM-14965] Fix password reprompt for context menu actions (#12213)

* [PM-14965] Add return value for copy-cipher-field.service

* [PM-14965] Cleanup loadAction handling in updated browser view item page

* [PM-14965] Fix unit tests

* [PM-14965] Clear copy mock
This commit is contained in:
Shane Melton
2024-12-03 09:37:44 -08:00
committed by GitHub
parent 9188a31b4a
commit 60e52dd2f2
4 changed files with 185 additions and 23 deletions

View File

@@ -58,18 +58,21 @@ describe("CopyCipherFieldService", () => {
it("should return early when valueToCopy is null", async () => {
valueToCopy = null;
await service.copy(valueToCopy, actionType, cipher, skipReprompt);
const result = await service.copy(valueToCopy, actionType, cipher, skipReprompt);
expect(result).toBeFalsy();
expect(platformUtilsService.copyToClipboard).not.toHaveBeenCalled();
});
it("should copy value to clipboard", async () => {
await service.copy(valueToCopy, actionType, cipher, skipReprompt);
const result = await service.copy(valueToCopy, actionType, cipher, skipReprompt);
expect(result).toBeTruthy();
expect(platformUtilsService.copyToClipboard).toHaveBeenCalledWith(valueToCopy);
});
it("should show a success toast on copy", async () => {
i18nService.t.mockReturnValueOnce("Username").mockReturnValueOnce("Username copied");
await service.copy(valueToCopy, actionType, cipher, skipReprompt);
const result = await service.copy(valueToCopy, actionType, cipher, skipReprompt);
expect(result).toBeTruthy();
expect(toastService.showToast).toHaveBeenCalledWith({
variant: "success",
message: "Username copied",
@@ -87,26 +90,30 @@ describe("CopyCipherFieldService", () => {
it("should show password prompt when actionType requires it", async () => {
passwordRepromptService.showPasswordPrompt.mockResolvedValue(true);
await service.copy(valueToCopy, actionType, cipher, skipReprompt);
const result = await service.copy(valueToCopy, actionType, cipher, skipReprompt);
expect(result).toBeTruthy();
expect(passwordRepromptService.showPasswordPrompt).toHaveBeenCalled();
});
it("should skip password prompt when cipher.reprompt is 'None'", async () => {
cipher.reprompt = CipherRepromptType.None;
await service.copy(valueToCopy, actionType, cipher, skipReprompt);
const result = await service.copy(valueToCopy, actionType, cipher, skipReprompt);
expect(result).toBeTruthy();
expect(passwordRepromptService.showPasswordPrompt).not.toHaveBeenCalled();
expect(platformUtilsService.copyToClipboard).toHaveBeenCalled();
});
it("should skip password prompt when skipReprompt is true", async () => {
skipReprompt = true;
await service.copy(valueToCopy, actionType, cipher, skipReprompt);
const result = await service.copy(valueToCopy, actionType, cipher, skipReprompt);
expect(result).toBeTruthy();
expect(passwordRepromptService.showPasswordPrompt).not.toHaveBeenCalled();
});
it("should return early when password prompt is not confirmed", async () => {
passwordRepromptService.showPasswordPrompt.mockResolvedValue(false);
await service.copy(valueToCopy, actionType, cipher, skipReprompt);
const result = await service.copy(valueToCopy, actionType, cipher, skipReprompt);
expect(result).toBeFalsy();
expect(platformUtilsService.copyToClipboard).not.toHaveBeenCalled();
});
});
@@ -123,7 +130,8 @@ describe("CopyCipherFieldService", () => {
it("should get TOTP code when allowed from premium", async () => {
billingAccountProfileStateService.hasPremiumFromAnySource$ = of(true);
totpService.getCode.mockResolvedValue("123456");
await service.copy(valueToCopy, actionType, cipher, skipReprompt);
const result = await service.copy(valueToCopy, actionType, cipher, skipReprompt);
expect(result).toBeTruthy();
expect(totpService.getCode).toHaveBeenCalledWith(valueToCopy);
expect(platformUtilsService.copyToClipboard).toHaveBeenCalledWith("123456");
});
@@ -131,21 +139,24 @@ describe("CopyCipherFieldService", () => {
it("should get TOTP code when allowed from organization", async () => {
cipher.organizationUseTotp = true;
totpService.getCode.mockResolvedValue("123456");
await service.copy(valueToCopy, actionType, cipher, skipReprompt);
const result = await service.copy(valueToCopy, actionType, cipher, skipReprompt);
expect(result).toBeTruthy();
expect(totpService.getCode).toHaveBeenCalledWith(valueToCopy);
expect(platformUtilsService.copyToClipboard).toHaveBeenCalledWith("123456");
});
it("should return early when the user is not allowed to use TOTP", async () => {
billingAccountProfileStateService.hasPremiumFromAnySource$ = of(false);
await service.copy(valueToCopy, actionType, cipher, skipReprompt);
const result = await service.copy(valueToCopy, actionType, cipher, skipReprompt);
expect(result).toBeFalsy();
expect(totpService.getCode).not.toHaveBeenCalled();
expect(platformUtilsService.copyToClipboard).not.toHaveBeenCalled();
});
it("should return early when TOTP is not set", async () => {
cipher.login.totp = null;
await service.copy(valueToCopy, actionType, cipher, skipReprompt);
const result = await service.copy(valueToCopy, actionType, cipher, skipReprompt);
expect(result).toBeFalsy();
expect(totpService.getCode).not.toHaveBeenCalled();
expect(platformUtilsService.copyToClipboard).not.toHaveBeenCalled();
});

View File

@@ -95,13 +95,15 @@ export class CopyCipherFieldService {
* @param actionType The type of field being copied.
* @param cipher The cipher containing the field to copy.
* @param skipReprompt Whether to skip password re-prompting.
*
* @returns Whether the field was copied successfully.
*/
async copy(
valueToCopy: string,
actionType: CopyAction,
cipher: CipherView,
skipReprompt: boolean = false,
) {
): Promise<boolean> {
const action = CopyActions[actionType];
if (
!skipReprompt &&
@@ -109,16 +111,16 @@ export class CopyCipherFieldService {
action.protected &&
!(await this.passwordRepromptService.showPasswordPrompt())
) {
return;
return false;
}
if (valueToCopy == null) {
return;
return false;
}
if (actionType === "totp") {
if (!(await this.totpAllowed(cipher))) {
return;
return false;
}
valueToCopy = await this.totpService.getCode(valueToCopy);
}
@@ -138,6 +140,8 @@ export class CopyCipherFieldService {
cipher.organizationId,
);
}
return true;
}
/**