mirror of
https://github.com/bitwarden/browser
synced 2025-12-11 05:43:41 +00:00
[PM-13365] - don't display totp capture when in popout (#12645)
* don't display totp capture when in popout * add canCaptureTotp method * dry up logic * add unit tests * fix failing tests * add missing mock to cipher-form story
This commit is contained in:
@@ -2,6 +2,7 @@ import { TestBed } from "@angular/core/testing";
|
|||||||
import qrcodeParser from "qrcode-parser";
|
import qrcodeParser from "qrcode-parser";
|
||||||
|
|
||||||
import { BrowserApi } from "../../../platform/browser/browser-api";
|
import { BrowserApi } from "../../../platform/browser/browser-api";
|
||||||
|
import BrowserPopupUtils from "../../../platform/popup/browser-popup-utils";
|
||||||
|
|
||||||
import { BrowserTotpCaptureService } from "./browser-totp-capture.service";
|
import { BrowserTotpCaptureService } from "./browser-totp-capture.service";
|
||||||
|
|
||||||
@@ -13,12 +14,14 @@ describe("BrowserTotpCaptureService", () => {
|
|||||||
let testBed: TestBed;
|
let testBed: TestBed;
|
||||||
let service: BrowserTotpCaptureService;
|
let service: BrowserTotpCaptureService;
|
||||||
let mockCaptureVisibleTab: jest.SpyInstance;
|
let mockCaptureVisibleTab: jest.SpyInstance;
|
||||||
|
let mockBrowserPopupUtilsInPopout: jest.SpyInstance;
|
||||||
|
|
||||||
const validTotpUrl = "otpauth://totp/label?secret=123";
|
const validTotpUrl = "otpauth://totp/label?secret=123";
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mockCaptureVisibleTab = jest.spyOn(BrowserApi, "captureVisibleTab");
|
mockCaptureVisibleTab = jest.spyOn(BrowserApi, "captureVisibleTab");
|
||||||
mockCaptureVisibleTab.mockResolvedValue("screenshot");
|
mockCaptureVisibleTab.mockResolvedValue("screenshot");
|
||||||
|
mockBrowserPopupUtilsInPopout = jest.spyOn(BrowserPopupUtils, "inPopout");
|
||||||
|
|
||||||
testBed = TestBed.configureTestingModule({
|
testBed = TestBed.configureTestingModule({
|
||||||
providers: [BrowserTotpCaptureService],
|
providers: [BrowserTotpCaptureService],
|
||||||
@@ -66,4 +69,16 @@ describe("BrowserTotpCaptureService", () => {
|
|||||||
|
|
||||||
expect(result).toBeNull();
|
expect(result).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("canCaptureTotp", () => {
|
||||||
|
it("should return true when not in a popout window", () => {
|
||||||
|
mockBrowserPopupUtilsInPopout.mockReturnValue(false);
|
||||||
|
expect(service.canCaptureTotp({} as Window)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false when in a popout window", () => {
|
||||||
|
mockBrowserPopupUtilsInPopout.mockReturnValue(true);
|
||||||
|
expect(service.canCaptureTotp({} as Window)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import qrcodeParser from "qrcode-parser";
|
|||||||
import { TotpCaptureService } from "@bitwarden/vault";
|
import { TotpCaptureService } from "@bitwarden/vault";
|
||||||
|
|
||||||
import { BrowserApi } from "../../../platform/browser/browser-api";
|
import { BrowserApi } from "../../../platform/browser/browser-api";
|
||||||
|
import BrowserPopupUtils from "../../../platform/popup/browser-popup-utils";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of TotpCaptureService for the browser which captures the
|
* Implementation of TotpCaptureService for the browser which captures the
|
||||||
@@ -20,4 +21,8 @@ export class BrowserTotpCaptureService implements TotpCaptureService {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
canCaptureTotp(window: Window) {
|
||||||
|
return !BrowserPopupUtils.inPopout(window);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,4 +6,9 @@ export abstract class TotpCaptureService {
|
|||||||
* Captures a TOTP secret and returns it as a string. Returns null if no TOTP secret was found.
|
* Captures a TOTP secret and returns it as a string. Returns null if no TOTP secret was found.
|
||||||
*/
|
*/
|
||||||
abstract captureTotpSecret(): Promise<string | null>;
|
abstract captureTotpSecret(): Promise<string | null>;
|
||||||
|
/**
|
||||||
|
* Returns whether the TOTP secret can be captured from the current tab.
|
||||||
|
* Only available in the browser extension and when not in a popout window.
|
||||||
|
*/
|
||||||
|
abstract canCaptureTotp(window: Window): boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -152,6 +152,7 @@ export default {
|
|||||||
provide: TotpCaptureService,
|
provide: TotpCaptureService,
|
||||||
useValue: {
|
useValue: {
|
||||||
captureTotpSecret: () => Promise.resolve("some-value"),
|
captureTotpSecret: () => Promise.resolve("some-value"),
|
||||||
|
canCaptureTotp: () => true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -434,6 +434,7 @@ describe("LoginDetailsSectionComponent", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should call captureTotp when the capture totp button is clicked", fakeAsync(() => {
|
it("should call captureTotp when the capture totp button is clicked", fakeAsync(() => {
|
||||||
|
jest.spyOn(component, "canCaptureTotp", "get").mockReturnValue(true);
|
||||||
component.captureTotp = jest.fn();
|
component.captureTotp = jest.fn();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
@@ -445,7 +446,8 @@ describe("LoginDetailsSectionComponent", () => {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
describe("canCaptureTotp", () => {
|
describe("canCaptureTotp", () => {
|
||||||
it("should return true when totpCaptureService is present and totp is editable", () => {
|
it("should return true when totpCaptureService is present and totpCaptureService.canCaptureTotp is true and totp is editable", () => {
|
||||||
|
jest.spyOn(component, "canCaptureTotp", "get").mockReturnValue(true);
|
||||||
component.loginDetailsForm.controls.totp.enable();
|
component.loginDetailsForm.controls.totp.enable();
|
||||||
expect(component.canCaptureTotp).toBe(true);
|
expect(component.canCaptureTotp).toBe(true);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -65,10 +65,14 @@ export class LoginDetailsSectionComponent implements OnInit {
|
|||||||
newPasswordGenerated: boolean;
|
newPasswordGenerated: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the TOTP field can be captured from the current tab. Only available in the browser extension.
|
* Whether the TOTP field can be captured from the current tab. Only available in the browser extension and
|
||||||
|
* when not in a popout window.
|
||||||
*/
|
*/
|
||||||
get canCaptureTotp() {
|
get canCaptureTotp() {
|
||||||
return this.totpCaptureService != null && this.loginDetailsForm.controls.totp.enabled;
|
return (
|
||||||
|
!!this.totpCaptureService?.canCaptureTotp(window) &&
|
||||||
|
this.loginDetailsForm.controls.totp.enabled
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private datePipe = inject(DatePipe);
|
private datePipe = inject(DatePipe);
|
||||||
|
|||||||
Reference in New Issue
Block a user