1
0
mirror of https://github.com/bitwarden/browser synced 2026-01-21 11:53:34 +00:00

[SM-1570] Adding DisableSMAdsForUsers - Front end changes to disable SM ads for users (#17000)

* Front end changes to disable SM ads for users

* fixing failing tests

* Update libs/common/src/admin-console/models/response/organization.response.ts

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>

* fixing merge conflicts

* claude suggestion

* adding feature flag for disable sm ads on clients

* fixing tests

---------

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
This commit is contained in:
cd-bitwarden
2026-01-06 23:02:41 -05:00
committed by GitHub
parent 07e9d56b63
commit 2a0a89a77e
8 changed files with 35 additions and 3 deletions

View File

@@ -54,6 +54,7 @@ describe("ProductSwitcherService", () => {
platformUtilsService = mock<PlatformUtilsService>();
billingAccountProfileStateService = mock<BillingAccountProfileStateService>();
configService = mock<ConfigService>();
configService.getFeatureFlag$.mockReturnValue(of(false));
router.url = "/";
router.events = of({});

View File

@@ -19,7 +19,11 @@ import {
} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service";
import { PolicyType, ProviderType } from "@bitwarden/common/admin-console/enums";
import {
OrganizationUserType,
PolicyType,
ProviderType,
} from "@bitwarden/common/admin-console/enums";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { Provider } from "@bitwarden/common/admin-console/models/domain/provider";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
@@ -155,14 +159,16 @@ export class ProductSwitcherService {
this.userHasSingleOrgPolicy$,
this.route.paramMap,
this.triggerProductUpdate$,
this.configService.getFeatureFlag$(FeatureFlag.SM1719_RemoveSecretsManagerAds),
]).pipe(
map(
([orgs, providers, userHasSingleOrgPolicy, paramMap]: [
([orgs, providers, userHasSingleOrgPolicy, paramMap, , removeSecretsManagerAdsFlag]: [
Organization[],
Provider[],
boolean,
ParamMap,
void,
boolean,
]) => {
// Sort orgs by name to match the order within the sidebar
orgs.sort((a, b) => a.name.localeCompare(b.name));
@@ -208,6 +214,15 @@ export class ProductSwitcherService {
external: false,
};
// Check if SM ads should be disabled for any organization
// SM ads are only disabled if the feature flag is enabled AND
// the user is a regular User (not Admin or Owner) in an organization that has useDisableSMAdsForUsers enabled
const shouldDisableSMAds =
removeSecretsManagerAdsFlag &&
orgs.some(
(org) => org.useDisableSMAdsForUsers === true && org.type === OrganizationUserType.User,
);
const products = {
pm: {
name: "Password Manager",
@@ -267,7 +282,8 @@ export class ProductSwitcherService {
if (smOrg) {
bento.push(products.sm);
} else {
} else if (!shouldDisableSMAds) {
// Only show SM in "other" section if ads are not disabled
other.push(products.sm);
}

View File

@@ -64,6 +64,7 @@ describe("ORGANIZATIONS state", () => {
isAdminInitiated: false,
ssoEnabled: false,
ssoMemberDecryptionType: undefined,
useDisableSMAdsForUsers: false,
usePhishingBlocker: false,
},
};

View File

@@ -64,6 +64,7 @@ export class OrganizationData {
userIsManagedByOrganization: boolean;
useAccessIntelligence: boolean;
useAdminSponsoredFamilies: boolean;
useDisableSMAdsForUsers: boolean;
isAdminInitiated: boolean;
ssoEnabled: boolean;
ssoMemberDecryptionType?: MemberDecryptionType;
@@ -133,6 +134,7 @@ export class OrganizationData {
this.userIsManagedByOrganization = response.userIsManagedByOrganization;
this.useAccessIntelligence = response.useAccessIntelligence;
this.useAdminSponsoredFamilies = response.useAdminSponsoredFamilies;
this.useDisableSMAdsForUsers = response.useDisableSMAdsForUsers ?? false;
this.isAdminInitiated = response.isAdminInitiated;
this.ssoEnabled = response.ssoEnabled;
this.ssoMemberDecryptionType = response.ssoMemberDecryptionType;

View File

@@ -95,6 +95,7 @@ export class Organization {
userIsManagedByOrganization: boolean;
useAccessIntelligence: boolean;
useAdminSponsoredFamilies: boolean;
useDisableSMAdsForUsers: boolean;
isAdminInitiated: boolean;
ssoEnabled: boolean;
ssoMemberDecryptionType?: MemberDecryptionType;
@@ -160,6 +161,7 @@ export class Organization {
this.userIsManagedByOrganization = obj.userIsManagedByOrganization;
this.useAccessIntelligence = obj.useAccessIntelligence;
this.useAdminSponsoredFamilies = obj.useAdminSponsoredFamilies;
this.useDisableSMAdsForUsers = obj.useDisableSMAdsForUsers ?? false;
this.isAdminInitiated = obj.isAdminInitiated;
this.ssoEnabled = obj.ssoEnabled;
this.ssoMemberDecryptionType = obj.ssoMemberDecryptionType;

View File

@@ -38,6 +38,7 @@ export class OrganizationResponse extends BaseResponse {
limitCollectionDeletion: boolean;
limitItemDeletion: boolean;
allowAdminAccessToAllCollectionItems: boolean;
useDisableSMAdsForUsers: boolean;
useAccessIntelligence: boolean;
usePhishingBlocker: boolean;
@@ -81,6 +82,7 @@ export class OrganizationResponse extends BaseResponse {
this.allowAdminAccessToAllCollectionItems = this.getResponseProperty(
"AllowAdminAccessToAllCollectionItems",
);
this.useDisableSMAdsForUsers = this.getResponseProperty("UseDisableSMAdsForUsers") ?? false;
// Map from backend API property (UseRiskInsights) to domain model property (useAccessIntelligence)
this.useAccessIntelligence = this.getResponseProperty("UseRiskInsights");
this.usePhishingBlocker = this.getResponseProperty("UsePhishingBlocker") ?? false;

View File

@@ -59,6 +59,7 @@ export class ProfileOrganizationResponse extends BaseResponse {
userIsManagedByOrganization: boolean;
useAccessIntelligence: boolean;
useAdminSponsoredFamilies: boolean;
useDisableSMAdsForUsers: boolean;
isAdminInitiated: boolean;
ssoEnabled: boolean;
ssoMemberDecryptionType?: MemberDecryptionType;
@@ -133,6 +134,7 @@ export class ProfileOrganizationResponse extends BaseResponse {
// Map from backend API property (UseRiskInsights) to domain model property (useAccessIntelligence)
this.useAccessIntelligence = this.getResponseProperty("UseRiskInsights");
this.useAdminSponsoredFamilies = this.getResponseProperty("UseAdminSponsoredFamilies");
this.useDisableSMAdsForUsers = this.getResponseProperty("UseDisableSMAdsForUsers") ?? false;
this.isAdminInitiated = this.getResponseProperty("IsAdminInitiated");
this.ssoEnabled = this.getResponseProperty("SsoEnabled") ?? false;
this.ssoMemberDecryptionType = this.getResponseProperty("SsoMemberDecryptionType");

View File

@@ -79,6 +79,9 @@ export enum FeatureFlag {
/* UIF */
RouterFocusManagement = "router-focus-management",
/* Secrets Manager */
SM1719_RemoveSecretsManagerAds = "sm-1719-remove-secrets-manager-ads",
}
export type AllowedFeatureFlagTypes = boolean | number | string;
@@ -164,6 +167,9 @@ export const DefaultFeatureFlagValue = {
/* UIF */
[FeatureFlag.RouterFocusManagement]: FALSE,
/* Secrets Manager */
[FeatureFlag.SM1719_RemoveSecretsManagerAds]: FALSE,
} satisfies Record<FeatureFlag, AllowedFeatureFlagTypes>;
export type DefaultFeatureFlagValueType = typeof DefaultFeatureFlagValue;