1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-10 13:23:34 +00:00

Hide card option from context menu when user is affected by card policy (#15272)

* Hide card option from context menu when user is affected by card policy

* Remove unused code
This commit is contained in:
Robyn MacCallum
2025-06-25 12:51:04 -04:00
committed by GitHub
parent cf6b087491
commit 4003608019
3 changed files with 50 additions and 2 deletions

View File

@@ -1,5 +1,5 @@
import { mock, MockProxy } from "jest-mock-extended"; import { mock, MockProxy } from "jest-mock-extended";
import { of } from "rxjs"; import { BehaviorSubject, of } from "rxjs";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { import {
@@ -22,6 +22,10 @@ import { UserId } from "@bitwarden/common/types/guid";
import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherType } from "@bitwarden/common/vault/enums";
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher"; import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import {
RestrictedCipherType,
RestrictedItemTypesService,
} from "@bitwarden/common/vault/services/restricted-item-types.service";
import { MainContextMenuHandler } from "./main-context-menu-handler"; import { MainContextMenuHandler } from "./main-context-menu-handler";
@@ -69,6 +73,8 @@ describe("context-menu", () => {
let logService: MockProxy<LogService>; let logService: MockProxy<LogService>;
let billingAccountProfileStateService: MockProxy<BillingAccountProfileStateService>; let billingAccountProfileStateService: MockProxy<BillingAccountProfileStateService>;
let accountService: MockProxy<AccountService>; let accountService: MockProxy<AccountService>;
let restricted$: BehaviorSubject<RestrictedCipherType[]>;
let restrictedItemTypesService: RestrictedItemTypesService;
let removeAllSpy: jest.SpyInstance<void, [callback?: () => void]>; let removeAllSpy: jest.SpyInstance<void, [callback?: () => void]>;
let createSpy: jest.SpyInstance< let createSpy: jest.SpyInstance<
@@ -85,6 +91,10 @@ describe("context-menu", () => {
logService = mock(); logService = mock();
billingAccountProfileStateService = mock(); billingAccountProfileStateService = mock();
accountService = mock(); accountService = mock();
restricted$ = new BehaviorSubject<RestrictedCipherType[]>([]);
restrictedItemTypesService = {
restricted$,
} as Partial<RestrictedItemTypesService> as RestrictedItemTypesService;
removeAllSpy = jest removeAllSpy = jest
.spyOn(chrome.contextMenus, "removeAll") .spyOn(chrome.contextMenus, "removeAll")
@@ -105,6 +115,7 @@ describe("context-menu", () => {
logService, logService,
billingAccountProfileStateService, billingAccountProfileStateService,
accountService, accountService,
restrictedItemTypesService,
); );
jest.spyOn(MainContextMenuHandler, "remove"); jest.spyOn(MainContextMenuHandler, "remove");
@@ -147,6 +158,24 @@ describe("context-menu", () => {
expect(createdMenu).toBeTruthy(); expect(createdMenu).toBeTruthy();
expect(createSpy).toHaveBeenCalledTimes(11); expect(createSpy).toHaveBeenCalledTimes(11);
}); });
it("has menu enabled and has premium, but card type is restricted", async () => {
billingAccountProfileStateService.hasPremiumFromAnySource$.mockReturnValue(of(true));
restricted$.next([{ cipherType: CipherType.Card, allowViewOrgIds: [] }]);
const createdMenu = await sut.init();
expect(createdMenu).toBeTruthy();
expect(createSpy).toHaveBeenCalledTimes(10);
});
it("has menu enabled, does not have premium, and card type is restricted", async () => {
billingAccountProfileStateService.hasPremiumFromAnySource$.mockReturnValue(of(false));
restricted$.next([{ cipherType: CipherType.Card, allowViewOrgIds: [] }]);
const createdMenu = await sut.init();
expect(createdMenu).toBeTruthy();
expect(createSpy).toHaveBeenCalledTimes(9);
});
}); });
describe("loadOptions", () => { describe("loadOptions", () => {

View File

@@ -25,8 +25,9 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { Utils } from "@bitwarden/common/platform/misc/utils"; import { Utils } from "@bitwarden/common/platform/misc/utils";
import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherType } from "@bitwarden/common/vault/enums/cipher-type";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service";
import { InitContextMenuItems } from "./abstractions/main-context-menu-handler"; import { InitContextMenuItems } from "./abstractions/main-context-menu-handler";
@@ -157,6 +158,7 @@ export class MainContextMenuHandler {
private logService: LogService, private logService: LogService,
private billingAccountProfileStateService: BillingAccountProfileStateService, private billingAccountProfileStateService: BillingAccountProfileStateService,
private accountService: AccountService, private accountService: AccountService,
private restrictedItemTypesService: RestrictedItemTypesService,
) {} ) {}
/** /**
@@ -181,6 +183,10 @@ export class MainContextMenuHandler {
this.billingAccountProfileStateService.hasPremiumFromAnySource$(account.id), this.billingAccountProfileStateService.hasPremiumFromAnySource$(account.id),
); );
const isCardRestricted = (
await firstValueFrom(this.restrictedItemTypesService.restricted$)
).some((rt) => rt.cipherType === CipherType.Card);
for (const menuItem of this.initContextMenuItems) { for (const menuItem of this.initContextMenuItems) {
const { const {
requiresPremiumAccess, requiresPremiumAccess,
@@ -192,6 +198,9 @@ export class MainContextMenuHandler {
if (requiresPremiumAccess && !hasPremium) { if (requiresPremiumAccess && !hasPremium) {
continue; continue;
} }
if (menuItem.id.startsWith(AUTOFILL_CARD_ID) && isCardRestricted) {
continue;
}
await MainContextMenuHandler.create({ ...otherOptions, contexts: ["all"] }); await MainContextMenuHandler.create({ ...otherOptions, contexts: ["all"] });
} }

View File

@@ -437,6 +437,8 @@ export default class MainBackground {
private popupViewCacheBackgroundService: PopupViewCacheBackgroundService; private popupViewCacheBackgroundService: PopupViewCacheBackgroundService;
private restrictedItemTypesService: RestrictedItemTypesService;
constructor() { constructor() {
// Services // Services
const lockedCallback = async (userId: UserId) => { const lockedCallback = async (userId: UserId) => {
@@ -1307,6 +1309,13 @@ export default class MainBackground {
this.stateProvider, this.stateProvider,
); );
this.restrictedItemTypesService = new RestrictedItemTypesService(
this.configService,
this.accountService,
this.organizationService,
this.policyService,
);
this.mainContextMenuHandler = new MainContextMenuHandler( this.mainContextMenuHandler = new MainContextMenuHandler(
this.stateService, this.stateService,
this.autofillSettingsService, this.autofillSettingsService,
@@ -1314,6 +1323,7 @@ export default class MainBackground {
this.logService, this.logService,
this.billingAccountProfileStateService, this.billingAccountProfileStateService,
this.accountService, this.accountService,
this.restrictedItemTypesService,
); );
this.cipherContextMenuHandler = new CipherContextMenuHandler( this.cipherContextMenuHandler = new CipherContextMenuHandler(