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:
@@ -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();
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user