diff --git a/apps/web/src/app/layouts/product-switcher/shared/product-switcher.service.spec.ts b/apps/web/src/app/layouts/product-switcher/shared/product-switcher.service.spec.ts index f7f319f2fab..b46629bc6a8 100644 --- a/apps/web/src/app/layouts/product-switcher/shared/product-switcher.service.spec.ts +++ b/apps/web/src/app/layouts/product-switcher/shared/product-switcher.service.spec.ts @@ -54,6 +54,7 @@ describe("ProductSwitcherService", () => { platformUtilsService = mock(); billingAccountProfileStateService = mock(); configService = mock(); + configService.getFeatureFlag$.mockReturnValue(of(false)); router.url = "/"; router.events = of({}); diff --git a/apps/web/src/app/layouts/product-switcher/shared/product-switcher.service.ts b/apps/web/src/app/layouts/product-switcher/shared/product-switcher.service.ts index 6cfecd59403..31fcf9ffe6d 100644 --- a/apps/web/src/app/layouts/product-switcher/shared/product-switcher.service.ts +++ b/apps/web/src/app/layouts/product-switcher/shared/product-switcher.service.ts @@ -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); } diff --git a/libs/common/src/admin-console/models/data/organization.data.spec.ts b/libs/common/src/admin-console/models/data/organization.data.spec.ts index 4b74e03db8d..ef8db3b01be 100644 --- a/libs/common/src/admin-console/models/data/organization.data.spec.ts +++ b/libs/common/src/admin-console/models/data/organization.data.spec.ts @@ -64,6 +64,7 @@ describe("ORGANIZATIONS state", () => { isAdminInitiated: false, ssoEnabled: false, ssoMemberDecryptionType: undefined, + useDisableSMAdsForUsers: false, usePhishingBlocker: false, }, }; diff --git a/libs/common/src/admin-console/models/data/organization.data.ts b/libs/common/src/admin-console/models/data/organization.data.ts index de0d21fbf17..de3f6af4448 100644 --- a/libs/common/src/admin-console/models/data/organization.data.ts +++ b/libs/common/src/admin-console/models/data/organization.data.ts @@ -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; diff --git a/libs/common/src/admin-console/models/domain/organization.ts b/libs/common/src/admin-console/models/domain/organization.ts index dba4a1fedb3..13c7a48e6c4 100644 --- a/libs/common/src/admin-console/models/domain/organization.ts +++ b/libs/common/src/admin-console/models/domain/organization.ts @@ -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; diff --git a/libs/common/src/admin-console/models/response/organization.response.ts b/libs/common/src/admin-console/models/response/organization.response.ts index cf3ae6a90f7..c2f7dd6b6a8 100644 --- a/libs/common/src/admin-console/models/response/organization.response.ts +++ b/libs/common/src/admin-console/models/response/organization.response.ts @@ -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; diff --git a/libs/common/src/admin-console/models/response/profile-organization.response.ts b/libs/common/src/admin-console/models/response/profile-organization.response.ts index 263e8e7d6b9..44ba7555a7e 100644 --- a/libs/common/src/admin-console/models/response/profile-organization.response.ts +++ b/libs/common/src/admin-console/models/response/profile-organization.response.ts @@ -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"); diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index 850c3be4d5e..080b52de05d 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -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; export type DefaultFeatureFlagValueType = typeof DefaultFeatureFlagValue;