System console tiered license check (#30916)

* Make the Mobile Security settings available on Enterprise Advance

* rename to minLicenseTier

* used license tier checks in system console

* lint fix

* renamed license in test

* Made license name display in single line

---------

Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
This commit is contained in:
Harshil Sharma 2025-05-05 14:18:48 +05:30 committed by GitHub
parent ea4ab9aa90
commit b7ff54acee
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 1243 additions and 48 deletions

View file

@ -232,7 +232,7 @@ func GenerateClientConfig(c *model.Config, telemetryID string, license *model.Li
props["ScheduledPosts"] = strconv.FormatBool(*c.ServiceSettings.ScheduledPosts)
}
if license.SkuShortName == model.LicenseShortSkuEnterprise {
if model.MinimumEnterpriseLicense(license) {
props["MobileEnableBiometrics"] = strconv.FormatBool(*c.NativeAppSettings.MobileEnableBiometrics)
props["MobilePreventScreenCapture"] = strconv.FormatBool(*c.NativeAppSettings.MobilePreventScreenCapture)
props["MobileJailbreakProtection"] = strconv.FormatBool(*c.NativeAppSettings.MobileJailbreakProtection)
@ -402,7 +402,7 @@ func GenerateLimitedClientConfig(c *model.Config, telemetryID string, license *m
props["OpenIdButtonText"] = *c.OpenIdSettings.ButtonText
}
if license.SkuShortName == model.LicenseShortSkuEnterprise {
if model.MinimumEnterpriseLicense(license) {
props["MobileEnableBiometrics"] = strconv.FormatBool(*c.NativeAppSettings.MobileEnableBiometrics)
props["MobilePreventScreenCapture"] = strconv.FormatBool(*c.NativeAppSettings.MobilePreventScreenCapture)
props["MobileJailbreakProtection"] = strconv.FormatBool(*c.NativeAppSettings.MobileJailbreakProtection)

View file

@ -36,7 +36,7 @@ import {searchableStrings as teamAnalyticsSearchableStrings} from 'components/an
import ExternalLink from 'components/external_link';
import RestrictedIndicator from 'components/widgets/menu/menu_items/restricted_indicator';
import {Constants, CloudProducts, LicenseSkus, AboutLinks, DocLinks, DeveloperLinks, CacheTypes} from 'utils/constants';
import {Constants, CloudProducts, LicenseSkus, AboutLinks, DocLinks, DeveloperLinks, CacheTypes, getLicenseTier} from 'utils/constants';
import {isCloudLicense} from 'utils/license_utils';
import {ID_PATH_PATTERN} from 'utils/path';
import {getSiteURL} from 'utils/url';
@ -228,6 +228,7 @@ export const it = {
cloudLicensed: (config: Partial<AdminConfig>, state: any, license?: ClientLicense) => Boolean(license?.IsLicensed && isCloudLicense(license)),
licensedForFeature: (feature: string) => (config: Partial<AdminConfig>, state: any, license?: ClientLicense) => Boolean(license?.IsLicensed && license[feature] === 'true'),
licensedForSku: (skuName: string) => (config: Partial<AdminConfig>, state: any, license?: ClientLicense) => Boolean(license?.IsLicensed && license.SkuShortName === skuName),
minLicenseTier: (skuName: string) => (config: Partial<AdminConfig>, state: any, license?: ClientLicense) => Boolean(license?.IsLicensed && getLicenseTier(license.SkuShortName) >= getLicenseTier(skuName)),
licensedForCloudStarter: (config: Partial<AdminConfig>, state: any, license?: ClientLicense) => Boolean(license?.IsLicensed && isCloudLicense(license) && license.SkuShortName === LicenseSkus.Starter),
hidePaymentInfo: (config: Partial<AdminConfig>, state: any, license?: ClientLicense, enterpriseReady?: boolean, consoleAccess?: ConsoleAccess, cloud?: CloudState) => {
if (!cloud) {
@ -1156,7 +1157,7 @@ const AdminDefinition: AdminDefinitionType = {
title: defineMessage({id: 'admin.sidebar.exportStorage', defaultMessage: 'Export Storage'}),
isHidden: it.any(
it.not(it.licensedForFeature('Cloud')),
it.not(it.licensedForSku(LicenseSkus.Enterprise)),
it.not(it.minLicenseTier(LicenseSkus.Enterprise)),
it.configIsFalse('FeatureFlags', 'CloudDedicatedExportUI'),
),
schema: {
@ -2047,7 +2048,7 @@ const AdminDefinition: AdminDefinitionType = {
title: defineMessage({id: 'admin.sidebar.mobileSecurity', defaultMessage: 'Mobile Security'}),
isHidden: it.any(
it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.MOBILE_SECURITY)),
it.not(it.licensedForSku(LicenseSkus.Enterprise)),
it.not(it.minLicenseTier(LicenseSkus.Enterprise)),
),
schema: {
id: 'MobileSecuritySettings',
@ -2080,7 +2081,7 @@ const AdminDefinition: AdminDefinitionType = {
title: defineMessage({id: 'admin.sidebar.mobileSecurity', defaultMessage: 'Mobile Security'}),
isHidden: it.any(
it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.MOBILE_SECURITY)),
it.licensedForSku(LicenseSkus.Enterprise),
it.minLicenseTier(LicenseSkus.Enterprise),
it.not(it.enterpriseReady),
),
schema: {
@ -2337,7 +2338,7 @@ const AdminDefinition: AdminDefinitionType = {
title: defineMessage({id: 'admin.sidebar.system_properties', defaultMessage: 'System Properties'}),
searchableStrings: systemPropertiesSearchableStrings,
isHidden: it.not(it.all(
it.licensedForSku(LicenseSkus.Enterprise),
it.minLicenseTier(LicenseSkus.Enterprise),
it.configIsTrue('FeatureFlags', 'CustomProfileAttributes'),
)),
schema: {
@ -2521,8 +2522,7 @@ const AdminDefinition: AdminDefinitionType = {
help_text: defineMessage({id: 'admin.team.customUserGroupsDescription', defaultMessage: 'When true, users with appropriate permissions can create custom user groups and enables at-mentions for those groups.'}),
isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.USERS_AND_TEAMS)),
isHidden: it.not(it.any(
it.licensedForSku(LicenseSkus.Enterprise),
it.licensedForSku(LicenseSkus.Professional),
it.minLicenseTier(LicenseSkus.Professional),
)),
},
{
@ -3348,7 +3348,7 @@ const AdminDefinition: AdminDefinitionType = {
ip_filtering: {
url: 'site_config/ip_filtering',
title: adminDefinitionMessages.ip_filtering_title,
isHidden: it.not(it.all(it.configIsTrue('FeatureFlags', 'CloudIPFiltering'), it.licensedForSku('enterprise'))),
isHidden: it.not(it.all(it.configIsTrue('FeatureFlags', 'CloudIPFiltering'), it.minLicenseTier(LicenseSkus.Enterprise))),
isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.IP_FILTERING)),
searchableStrings: [adminDefinitionMessages.ip_filtering_title],
schema: {
@ -4008,7 +4008,7 @@ const AdminDefinition: AdminDefinitionType = {
key: 'LdapSettings.CustomProfileAttributes',
component: CustomProfileAttributes,
isHidden: it.not(it.all(
it.licensedForSku(LicenseSkus.Enterprise),
it.minLicenseTier(LicenseSkus.Enterprise),
it.configIsTrue('FeatureFlags', 'CustomProfileAttributes'),
)),
},
@ -4720,7 +4720,7 @@ const AdminDefinition: AdminDefinitionType = {
key: 'SamlSettings.CustomProfileAttributes',
component: CustomProfileAttributes,
isHidden: it.not(it.all(
it.licensedForSku(LicenseSkus.Enterprise),
it.minLicenseTier(LicenseSkus.Enterprise),
it.configIsTrue('FeatureFlags', 'CustomProfileAttributes'),
)),
},
@ -6413,9 +6413,7 @@ const AdminDefinition: AdminDefinitionType = {
),
},
help_text_markdown: false,
isHidden: it.not(it.any(
it.licensedForSku(LicenseSkus.Enterprise),
it.licensedForSku(LicenseSkus.E20))),
isHidden: it.not(it.minLicenseTier(LicenseSkus.Enterprise)),
isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)),
},
{
@ -6434,9 +6432,7 @@ const AdminDefinition: AdminDefinitionType = {
display_name: defineMessage({id: 'admin.experimental.clientSideCertCheck.options.secondary', defaultMessage: 'secondary'}),
},
],
isHidden: it.not(it.any(
it.licensedForSku(LicenseSkus.Enterprise),
it.licensedForSku(LicenseSkus.E20))),
isHidden: it.not(it.minLicenseTier(LicenseSkus.Enterprise)),
isDisabled: it.any(
it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)),
it.stateIsFalse('ExperimentalSettings.ClientSideCertEnable'),
@ -6699,7 +6695,11 @@ const AdminDefinition: AdminDefinitionType = {
audit_logging: {
url: 'experimental/audit_logging',
title: defineMessage({id: 'admin.sidebar.audit_logging_experimental', defaultMessage: 'Audit Logging'}),
isHidden: it.any(it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), it.configIsFalse('FeatureFlags', 'ExperimentalAuditSettingsSystemConsoleUI'), it.not(it.licensedForSku('enterprise'))),
isHidden: it.any(
it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)),
it.configIsFalse('FeatureFlags', 'ExperimentalAuditSettingsSystemConsoleUI'),
it.not(it.minLicenseTier(LicenseSkus.Enterprise)),
),
schema: {
id: 'ExperimentalAuditSettings',
name: 'Audit Log Settings (Experimental)',

View file

@ -359,6 +359,7 @@ describe('components/AdminSidebar', () => {
license: {
IsLicensed: 'true',
SkuShortName: 'enterprise',
Cloud: 'true',
},
config: {
...defaultProps.config,
@ -384,6 +385,12 @@ describe('components/AdminSidebar', () => {
Secret: 'office365Secret',
Scope: 'scope',
} as Office365Settings,
FeatureFlags: {
CustomProfileAttributes: true,
CloudDedicatedExportUI: true,
CloudIPFiltering: true,
ExperimentalAuditSettingsSystemConsoleUI: true,
},
},
adminDefinition: AdminDefinition,
buildEnterpriseReady: true,
@ -482,6 +489,77 @@ describe('components/AdminSidebar', () => {
expect(wrapper).toMatchSnapshot();
});
test('should match snapshot with license with enterprise advanced SKU', () => {
const props: Props = {
license: {
IsLicensed: 'true',
SkuShortName: 'advanced',
Cloud: 'true',
},
config: {
...defaultProps.config,
ExperimentalSettings: {
RestrictSystemAdmin: false,
} as ExperimentalSettings,
PluginSettings: {
Enable: true,
EnableUploads: true,
} as PluginSettings,
GoogleSettings: {
Id: 'googleID',
Secret: 'googleSecret',
Scope: 'scope',
} as SSOSettings,
GitLabSettings: {
Id: 'gitlabID',
Secret: 'gitlabSecret',
Scope: 'scope',
} as SSOSettings,
Office365Settings: {
Id: 'office365ID',
Secret: 'office365Secret',
Scope: 'scope',
} as Office365Settings,
FeatureFlags: {
CustomProfileAttributes: true,
CloudDedicatedExportUI: true,
CloudIPFiltering: true,
ExperimentalAuditSettingsSystemConsoleUI: true,
},
},
adminDefinition: AdminDefinition,
buildEnterpriseReady: true,
navigationBlocked: false,
siteName: 'test snap',
subscriptionProduct: undefined,
plugins: {
plugin_0: {
active: false,
description: 'The plugin 0.',
id: 'plugin_0',
name: 'Plugin 0',
version: '0.1.0',
settings_schema: {
footer: '',
header: '',
settings: [],
},
webapp: {bundle_path: 'webapp/dist/main.js'},
},
},
onSearchChange: jest.fn(),
actions: {
getPlugins: jest.fn(),
},
consoleAccess: {...defaultProps.consoleAccess},
cloud: {...defaultProps.cloud},
showTaskList: false,
};
const wrapper = shallowWithIntl(<AdminSidebar {...props}/>);
expect(wrapper).toMatchSnapshot();
});
describe('generateIndex', () => {
const props: Props = {
license: {},

View file

@ -60,9 +60,7 @@
font-family: Metropolis;
&__Grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-template-rows: 1fr;
display: flex;
}
.PlanDetails__viewPlansButton {