mirror of
https://github.com/mattermost/mattermost.git
synced 2026-05-28 04:35:04 -04:00
Add system console settings for mobile security (#30456)
* Add config settings for additional security features on mobile * Add system console settings for mobile security * Update svg and link * Fix strings * Add test for the discovery feature * Fix tests * Add permission migrations * Add relevant e2e tests * Fix key alignment * fix tests * Fix lint * Mock new migration * Fix playwright prettier * Add new section to delegated permissions * Update snapshots * Fix flakyness in playwright test --------- Co-authored-by: Elias Nahum <nahumhbl@gmail.com> Co-authored-by: Mattermost Build <build@mattermost.com>
This commit is contained in:
parent
8ce78c302d
commit
7999239ccf
29 changed files with 2990 additions and 5 deletions
File diff suppressed because one or more lines are too long
|
|
@ -36,6 +36,8 @@ import {SystemUsersColumnToggleMenu} from './system_console/sections/system_user
|
|||
import ChannelsPostEdit from '@e2e-support/ui/components/channels/post_edit';
|
||||
import DeletePostConfirmationDialog from '@e2e-support/ui/components/channels/delete_post_confirmation_dialog';
|
||||
import RestorePostConfirmationDialog from '@e2e-support/ui/components/channels/restore_post_confirmation_dialog';
|
||||
import MobileSecurity from './system_console/sections/system_users/mobile_security';
|
||||
import FeatureDiscovery from './system_console/sections/system_users/feature_discovery';
|
||||
|
||||
const components = {
|
||||
GlobalHeader,
|
||||
|
|
@ -71,6 +73,8 @@ const components = {
|
|||
UserProfilePopover,
|
||||
DeletePostConfirmationDialog,
|
||||
RestorePostConfirmationDialog,
|
||||
MobileSecurity,
|
||||
FeatureDiscovery,
|
||||
};
|
||||
|
||||
export {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {expect, Locator} from '@playwright/test';
|
||||
|
||||
/**
|
||||
* System Console -> Feature Discovery
|
||||
*/
|
||||
export default class FeatureDiscovery {
|
||||
readonly container: Locator;
|
||||
|
||||
constructor(container: Locator) {
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
async toBeVisible() {
|
||||
await expect(this.container).toBeVisible();
|
||||
}
|
||||
|
||||
async toHaveTitle(title: string) {
|
||||
await expect(this.container.getByTestId('featureDiscovery_title')).toHaveText(title);
|
||||
}
|
||||
}
|
||||
|
||||
export {FeatureDiscovery};
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {expect, Locator} from '@playwright/test';
|
||||
|
||||
/**
|
||||
* System Console -> Environment -> Mobile Security
|
||||
*/
|
||||
export default class MobileSecurity {
|
||||
readonly container: Locator;
|
||||
|
||||
readonly enableBiometricAuthenticationToggleTrue: Locator;
|
||||
readonly enableBiometricAuthenticationToggleFalse: Locator;
|
||||
readonly preventScreenCaptureToggleTrue: Locator;
|
||||
readonly preventScreenCaptureToggleFalse: Locator;
|
||||
readonly jailbreakProtectionToggleTrue: Locator;
|
||||
readonly jailbreakProtectionToggleFalse: Locator;
|
||||
|
||||
readonly saveButton: Locator;
|
||||
|
||||
constructor(container: Locator) {
|
||||
this.container = container;
|
||||
|
||||
this.enableBiometricAuthenticationToggleTrue = this.container.getByTestId(
|
||||
'NativeAppSettings.MobileEnableBiometricstrue',
|
||||
);
|
||||
this.enableBiometricAuthenticationToggleFalse = this.container.getByTestId(
|
||||
'NativeAppSettings.MobileEnableBiometricsfalse',
|
||||
);
|
||||
|
||||
this.preventScreenCaptureToggleTrue = this.container.getByTestId(
|
||||
'NativeAppSettings.MobilePreventScreenCapturetrue',
|
||||
);
|
||||
this.preventScreenCaptureToggleFalse = this.container.getByTestId(
|
||||
'NativeAppSettings.MobilePreventScreenCapturefalse',
|
||||
);
|
||||
|
||||
this.jailbreakProtectionToggleTrue = this.container.getByTestId(
|
||||
'NativeAppSettings.MobileJailbreakProtectiontrue',
|
||||
);
|
||||
this.jailbreakProtectionToggleFalse = this.container.getByTestId(
|
||||
'NativeAppSettings.MobileJailbreakProtectionfalse',
|
||||
);
|
||||
|
||||
this.saveButton = this.container.getByRole('button', {name: 'Save'});
|
||||
}
|
||||
|
||||
async toBeVisible() {
|
||||
await expect(this.container).toBeVisible();
|
||||
}
|
||||
|
||||
async clickEnableBiometricAuthenticationToggleTrue() {
|
||||
await this.enableBiometricAuthenticationToggleTrue.click();
|
||||
}
|
||||
|
||||
async clickEnableBiometricAuthenticationToggleFalse() {
|
||||
await this.enableBiometricAuthenticationToggleFalse.click();
|
||||
}
|
||||
|
||||
async clickPreventScreenCaptureToggleTrue() {
|
||||
await this.preventScreenCaptureToggleTrue.click();
|
||||
}
|
||||
|
||||
async clickPreventScreenCaptureToggleFalse() {
|
||||
await this.preventScreenCaptureToggleFalse.click();
|
||||
}
|
||||
|
||||
async clickJailbreakProtectionToggleTrue() {
|
||||
await this.jailbreakProtectionToggleTrue.click();
|
||||
}
|
||||
|
||||
async clickJailbreakProtectionToggleFalse() {
|
||||
await this.jailbreakProtectionToggleFalse.click();
|
||||
}
|
||||
|
||||
async clickSaveButton() {
|
||||
await this.saveButton.click();
|
||||
}
|
||||
}
|
||||
|
||||
export {MobileSecurity};
|
||||
|
|
@ -21,6 +21,10 @@ class SystemConsolePage {
|
|||
readonly systemUsersColumnToggleMenu;
|
||||
readonly systemUsersActionMenus;
|
||||
|
||||
readonly mobileSecurity;
|
||||
|
||||
readonly featureDiscovery;
|
||||
|
||||
// modal
|
||||
readonly confirmModal;
|
||||
readonly exportModal;
|
||||
|
|
@ -35,6 +39,10 @@ class SystemConsolePage {
|
|||
|
||||
// Sections and sub-sections
|
||||
this.systemUsers = new components.SystemUsers(page.getByTestId('systemUsersSection'));
|
||||
this.mobileSecurity = new components.MobileSecurity(
|
||||
page.getByTestId('sysconsole_section_MobileSecuritySettings'),
|
||||
);
|
||||
this.featureDiscovery = new components.FeatureDiscovery(page.getByTestId('featureDiscovery'));
|
||||
|
||||
// Menus & Popovers
|
||||
this.systemUsersFilterPopover = new components.SystemUsersFilterPopover(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,126 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {waitUntil} from '@e2e-support/test_action';
|
||||
import {expect, test} from '@e2e-support/test_fixture';
|
||||
|
||||
test('should be able to enable mobile security settings when licensed', async ({pw}) => {
|
||||
const {adminUser, adminClient} = await pw.initSetup();
|
||||
|
||||
const license = await adminClient.getClientLicenseOld();
|
||||
|
||||
test.skip(license.SkuShortName !== 'enterprise', 'Skipping test - server has no enterprise license');
|
||||
|
||||
if (!adminUser) {
|
||||
throw new Error('Failed to create admin user');
|
||||
}
|
||||
|
||||
// # Log in as admin
|
||||
const {systemConsolePage} = await pw.testBrowser.login(adminUser);
|
||||
|
||||
// # Visit system console
|
||||
await systemConsolePage.goto();
|
||||
await systemConsolePage.toBeVisible();
|
||||
|
||||
// # Go to Mobile Security section
|
||||
await systemConsolePage.sidebar.goToItem('Mobile Security');
|
||||
await systemConsolePage.mobileSecurity.toBeVisible();
|
||||
|
||||
// # Enable Biometric Authentication
|
||||
await systemConsolePage.mobileSecurity.clickEnableBiometricAuthenticationToggleTrue();
|
||||
|
||||
// * Verify only Biometric Authentication is enabled
|
||||
expect(await systemConsolePage.mobileSecurity.enableBiometricAuthenticationToggleTrue.isChecked()).toBe(true);
|
||||
expect(await systemConsolePage.mobileSecurity.preventScreenCaptureToggleTrue.isChecked()).toBe(false);
|
||||
expect(await systemConsolePage.mobileSecurity.jailbreakProtectionToggleTrue.isChecked()).toBe(false);
|
||||
|
||||
// # Save settings
|
||||
await systemConsolePage.mobileSecurity.clickSaveButton();
|
||||
// # Wait until the save button has settled
|
||||
await waitUntil(async () => (await systemConsolePage.mobileSecurity.saveButton.textContent()) === 'Save');
|
||||
|
||||
// # Go to any other section and come back to Mobile Security
|
||||
await systemConsolePage.sidebar.goToItem('Users');
|
||||
await systemConsolePage.systemUsers.toBeVisible();
|
||||
|
||||
await systemConsolePage.sidebar.goToItem('Mobile Security');
|
||||
|
||||
// * Verify Biometric Authentication is still enabled
|
||||
expect(await systemConsolePage.mobileSecurity.enableBiometricAuthenticationToggleTrue.isChecked()).toBe(true);
|
||||
expect(await systemConsolePage.mobileSecurity.preventScreenCaptureToggleTrue.isChecked()).toBe(false);
|
||||
expect(await systemConsolePage.mobileSecurity.jailbreakProtectionToggleTrue.isChecked()).toBe(false);
|
||||
|
||||
// # Enable Prevent Screen Capture
|
||||
await systemConsolePage.mobileSecurity.clickPreventScreenCaptureToggleTrue();
|
||||
|
||||
// * Verify only Biometric Authentication and Prevent Screen Capture are enabled
|
||||
expect(await systemConsolePage.mobileSecurity.enableBiometricAuthenticationToggleTrue.isChecked()).toBe(true);
|
||||
expect(await systemConsolePage.mobileSecurity.preventScreenCaptureToggleTrue.isChecked()).toBe(true);
|
||||
expect(await systemConsolePage.mobileSecurity.jailbreakProtectionToggleTrue.isChecked()).toBe(false);
|
||||
|
||||
// # Save settings
|
||||
await systemConsolePage.mobileSecurity.clickSaveButton();
|
||||
// # Wait until the save button has settled
|
||||
await waitUntil(async () => (await systemConsolePage.mobileSecurity.saveButton.textContent()) === 'Save');
|
||||
|
||||
// # Go to any other section and come back to Mobile Security
|
||||
await systemConsolePage.sidebar.goToItem('Users');
|
||||
await systemConsolePage.systemUsers.toBeVisible();
|
||||
|
||||
await systemConsolePage.sidebar.goToItem('Mobile Security');
|
||||
|
||||
// * Verify Biometric Authentication and Prevent Screen Capture are still enabled
|
||||
expect(await systemConsolePage.mobileSecurity.enableBiometricAuthenticationToggleTrue.isChecked()).toBe(true);
|
||||
expect(await systemConsolePage.mobileSecurity.preventScreenCaptureToggleTrue.isChecked()).toBe(true);
|
||||
expect(await systemConsolePage.mobileSecurity.jailbreakProtectionToggleTrue.isChecked()).toBe(false);
|
||||
|
||||
// # Enable Jailbreak Protection
|
||||
await systemConsolePage.mobileSecurity.clickJailbreakProtectionToggleTrue();
|
||||
|
||||
// * Verify all toggles are enabled
|
||||
expect(await systemConsolePage.mobileSecurity.enableBiometricAuthenticationToggleTrue.isChecked()).toBe(true);
|
||||
expect(await systemConsolePage.mobileSecurity.preventScreenCaptureToggleTrue.isChecked()).toBe(true);
|
||||
expect(await systemConsolePage.mobileSecurity.jailbreakProtectionToggleTrue.isChecked()).toBe(true);
|
||||
|
||||
// # Save settings
|
||||
await systemConsolePage.mobileSecurity.clickSaveButton();
|
||||
// # Wait until the save button has settled
|
||||
await waitUntil(async () => (await systemConsolePage.mobileSecurity.saveButton.textContent()) === 'Save');
|
||||
|
||||
// # Go to any other section and come back to Mobile Security
|
||||
await systemConsolePage.sidebar.goToItem('Users');
|
||||
await systemConsolePage.systemUsers.toBeVisible();
|
||||
|
||||
await systemConsolePage.sidebar.goToItem('Mobile Security');
|
||||
|
||||
// * Verify all toggles are still enabled
|
||||
expect(await systemConsolePage.mobileSecurity.enableBiometricAuthenticationToggleTrue.isChecked()).toBe(true);
|
||||
expect(await systemConsolePage.mobileSecurity.preventScreenCaptureToggleTrue.isChecked()).toBe(true);
|
||||
expect(await systemConsolePage.mobileSecurity.jailbreakProtectionToggleTrue.isChecked()).toBe(true);
|
||||
});
|
||||
|
||||
test('should show mobile security upsell when not licensed', async ({pw}) => {
|
||||
const {adminUser, adminClient} = await pw.initSetup();
|
||||
|
||||
const license = await adminClient.getClientLicenseOld();
|
||||
|
||||
test.skip(license.SkuShortName === 'enterprise', 'Skipping test - server has enterprise license');
|
||||
|
||||
if (!adminUser) {
|
||||
throw new Error('Failed to create admin user');
|
||||
}
|
||||
|
||||
// # Log in as admin
|
||||
const {systemConsolePage} = await pw.testBrowser.login(adminUser);
|
||||
|
||||
// # Visit system console
|
||||
await systemConsolePage.goto();
|
||||
await systemConsolePage.toBeVisible();
|
||||
|
||||
// # Go to Mobile Security section
|
||||
await systemConsolePage.sidebar.goToItem('Mobile Security');
|
||||
await systemConsolePage.featureDiscovery.toBeVisible();
|
||||
|
||||
// * Verify title is correct
|
||||
await systemConsolePage.featureDiscovery.toHaveTitle('Enhance mobile app security with Mattermost Enterprise');
|
||||
});
|
||||
|
|
@ -1154,6 +1154,25 @@ func (a *App) removeGetAnalyticsPermissionMigration() (permissionsMap, error) {
|
|||
return transformations, nil
|
||||
}
|
||||
|
||||
func (a *App) addSysConsoleMobileSecurityPermission() (permissionsMap, error) {
|
||||
transformations := []permissionTransformation{}
|
||||
|
||||
transformations = append(transformations, permissionTransformation{
|
||||
On: isExactRole(model.SystemAdminRoleId),
|
||||
Add: []string{model.PermissionSysconsoleWriteEnvironmentMobileSecurity.Id},
|
||||
})
|
||||
|
||||
transformations = append(transformations, permissionTransformation{
|
||||
On: permissionOr(
|
||||
isExactRole(model.SystemAdminRoleId),
|
||||
isExactRole(model.SystemReadOnlyAdminRoleId),
|
||||
),
|
||||
Add: []string{model.PermissionSysconsoleReadEnvironmentMobileSecurity.Id},
|
||||
})
|
||||
|
||||
return transformations, nil
|
||||
}
|
||||
|
||||
// Only sysadmins, team admins, and users with channels and groups managements have access to "convert channel to public"
|
||||
func (a *App) getRestrictAcessToChannelConversionToPublic() (permissionsMap, error) {
|
||||
return []permissionTransformation{
|
||||
|
|
@ -1223,6 +1242,7 @@ func (s *Server) doPermissionsMigrations() error {
|
|||
{Key: model.RestrictAccessToChannelConversionToPublic, Migration: a.getRestrictAcessToChannelConversionToPublic},
|
||||
{Key: model.MigrationKeyFixReadAuditsPermission, Migration: a.getFixReadAuditsPermissionMigration},
|
||||
{Key: model.MigrationRemoveGetAnalyticsPermission, Migration: a.removeGetAnalyticsPermissionMigration},
|
||||
{Key: model.MigrationAddSysconsoleMobileSecurityPermission, Migration: a.addSysConsoleMobileSecurityPermission},
|
||||
}
|
||||
|
||||
roles, err := s.Store().Role().GetAll()
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@ func GetMockStoreForSetupFunctions() *mocks.Store {
|
|||
systemStore.On("GetByName", "CustomGroupAdminRoleCreationMigrationComplete").Return(&model.System{Name: model.MigrationKeyAddPlayboosksManageRolesPermissions, Value: "true"}, nil)
|
||||
systemStore.On("GetByName", "products_boards").Return(&model.System{Name: "products_boards", Value: "true"}, nil)
|
||||
systemStore.On("GetByName", "elasticsearch_fix_channel_index_migration").Return(&model.System{Name: "elasticsearch_fix_channel_index_migration", Value: "true"}, nil)
|
||||
systemStore.On("GetByName", model.MigrationAddSysconsoleMobileSecurityPermission).Return(&model.System{Name: model.MigrationAddSysconsoleMobileSecurityPermission, Value: "true"}, nil)
|
||||
systemStore.On("InsertIfExists", mock.AnythingOfType("*model.System")).Return(&model.System{}, nil).Once()
|
||||
systemStore.On("Save", mock.AnythingOfType("*model.System")).Return(nil)
|
||||
|
||||
|
|
|
|||
|
|
@ -53,4 +53,5 @@ const (
|
|||
RestrictAccessToChannelConversionToPublic = "restrict_access_to_channel_conversion_to_public_permissions"
|
||||
MigrationKeyFixReadAuditsPermission = "fix_read_audits_permission"
|
||||
MigrationRemoveGetAnalyticsPermission = "remove_get_analytics_permission"
|
||||
MigrationAddSysconsoleMobileSecurityPermission = "add_sysconsole_mobile_security_permission"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -252,6 +252,9 @@ var PermissionSysconsoleWriteEnvironmentPerformanceMonitoring *Permission
|
|||
var PermissionSysconsoleReadEnvironmentDeveloper *Permission
|
||||
var PermissionSysconsoleWriteEnvironmentDeveloper *Permission
|
||||
|
||||
var PermissionSysconsoleReadEnvironmentMobileSecurity *Permission
|
||||
var PermissionSysconsoleWriteEnvironmentMobileSecurity *Permission
|
||||
|
||||
var PermissionSysconsoleReadSite *Permission
|
||||
var PermissionSysconsoleWriteSite *Permission
|
||||
|
||||
|
|
@ -1622,6 +1625,18 @@ func initializePermissions() {
|
|||
"",
|
||||
PermissionScopeSystem,
|
||||
}
|
||||
PermissionSysconsoleReadEnvironmentMobileSecurity = &Permission{
|
||||
"sysconsole_read_environment_mobile_security",
|
||||
"",
|
||||
"",
|
||||
PermissionScopeSystem,
|
||||
}
|
||||
PermissionSysconsoleWriteEnvironmentMobileSecurity = &Permission{
|
||||
"sysconsole_write_environment_mobile_security",
|
||||
"",
|
||||
"",
|
||||
PermissionScopeSystem,
|
||||
}
|
||||
// DEPRECATED
|
||||
PermissionSysconsoleReadSite = &Permission{
|
||||
"sysconsole_read_site",
|
||||
|
|
@ -2262,6 +2277,7 @@ func initializePermissions() {
|
|||
PermissionSysconsoleReadEnvironmentSessionLengths,
|
||||
PermissionSysconsoleReadEnvironmentPerformanceMonitoring,
|
||||
PermissionSysconsoleReadEnvironmentDeveloper,
|
||||
PermissionSysconsoleReadEnvironmentMobileSecurity,
|
||||
PermissionSysconsoleReadSiteCustomization,
|
||||
PermissionSysconsoleReadSiteLocalization,
|
||||
PermissionSysconsoleReadSiteUsersAndTeams,
|
||||
|
|
@ -2321,6 +2337,7 @@ func initializePermissions() {
|
|||
PermissionSysconsoleWriteEnvironmentSessionLengths,
|
||||
PermissionSysconsoleWriteEnvironmentPerformanceMonitoring,
|
||||
PermissionSysconsoleWriteEnvironmentDeveloper,
|
||||
PermissionSysconsoleWriteEnvironmentMobileSecurity,
|
||||
PermissionSysconsoleWriteSiteCustomization,
|
||||
PermissionSysconsoleWriteSiteLocalization,
|
||||
PermissionSysconsoleWriteSiteUsersAndTeams,
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ exports[`components/admin_console/SchemaAdminSettings should match snapshot with
|
|||
exports[`components/admin_console/SchemaAdminSettings should match snapshot with settings and plugin 1`] = `
|
||||
<div
|
||||
className="wrapper--fixed "
|
||||
data-testid="sysconsole_section_Config"
|
||||
>
|
||||
<AdminHeader>
|
||||
config
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ import {
|
|||
GuestAccessFeatureDiscovery,
|
||||
SystemRolesFeatureDiscovery,
|
||||
GroupsFeatureDiscovery,
|
||||
MobileSecurityFeatureDiscovery,
|
||||
} from './feature_discovery/features';
|
||||
import FeatureFlags, {messages as featureFlagsMessages} from './feature_flags';
|
||||
import GroupDetails from './group_settings/group_details';
|
||||
|
|
@ -2039,6 +2040,61 @@ const AdminDefinition: AdminDefinitionType = {
|
|||
],
|
||||
},
|
||||
},
|
||||
mobile_security: {
|
||||
url: 'environment/mobile_security',
|
||||
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)),
|
||||
),
|
||||
schema: {
|
||||
id: 'MobileSecuritySettings',
|
||||
name: defineMessage({id: 'admin.mobileSecurity.title', defaultMessage: 'Mobile Security'}),
|
||||
settings: [
|
||||
{
|
||||
type: 'bool',
|
||||
key: 'NativeAppSettings.MobileEnableBiometrics',
|
||||
label: defineMessage({id: 'admin.mobileSecurity.biometricsTitle', defaultMessage: 'Enable Biometric Authentication:'}),
|
||||
help_text: defineMessage({id: 'admin.mobileSecurity.biometricsDescription', defaultMessage: 'Enforces biometric authentication (with PIN/passcode fallback) before accessing the app. Users will be prompted based on session activity and server switching rules.'}),
|
||||
},
|
||||
{
|
||||
type: 'bool',
|
||||
key: 'NativeAppSettings.MobilePreventScreenCapture',
|
||||
label: defineMessage({id: 'admin.mobileSecurity.screenCaptureTitle', defaultMessage: 'Prevent Screen Capture:'}),
|
||||
help_text: defineMessage({id: 'admin.mobileSecurity.screenCaptureDescription', defaultMessage: 'Blocks screenshots and screen recordings when using the mobile app. Screenshots will appear blank, and screen recordings will blur (iOS) or show a black screen (Android). Also applies when switching apps.'}),
|
||||
},
|
||||
{
|
||||
type: 'bool',
|
||||
key: 'NativeAppSettings.MobileJailbreakProtection',
|
||||
label: defineMessage({id: 'admin.mobileSecurity.jailbreakTitle', defaultMessage: 'Enable Jailbreak/Root Protection:'}),
|
||||
help_text: defineMessage({id: 'admin.mobileSecurity.jailbreakDescription', defaultMessage: 'Prevents access to the app on devices detected as jailbroken or rooted. If a device fails the security check, users will be denied access or prompted to switch to a compliant server.'}),
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
mobile_security_feature_discovery: {
|
||||
url: 'environment/mobile_security_feature_discovery',
|
||||
isDiscovery: true,
|
||||
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.not(it.enterpriseReady),
|
||||
),
|
||||
schema: {
|
||||
id: 'MobileSecurityFeatureDiscoverySettings',
|
||||
name: defineMessage({id: 'admin.mobileSecurity.title', defaultMessage: 'Mobile Security'}),
|
||||
settings: [
|
||||
{
|
||||
type: 'custom',
|
||||
component: MobileSecurityFeatureDiscovery,
|
||||
key: 'MobileSecurityFeatureDiscovery',
|
||||
isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ABOUT.EDITION_AND_LICENSE)),
|
||||
},
|
||||
],
|
||||
},
|
||||
restrictedIndicator: getRestrictedIndicator(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
site: {
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -354,6 +354,134 @@ describe('components/AdminSidebar', () => {
|
|||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot with license with enterprise SKU', () => {
|
||||
const props: Props = {
|
||||
license: {
|
||||
IsLicensed: 'true',
|
||||
SkuShortName: 'enterprise',
|
||||
},
|
||||
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,
|
||||
},
|
||||
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();
|
||||
});
|
||||
|
||||
test('should match snapshot with license with professional SKU', () => {
|
||||
const props: Props = {
|
||||
license: {
|
||||
IsLicensed: 'true',
|
||||
SkuShortName: 'professional',
|
||||
},
|
||||
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,
|
||||
},
|
||||
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: {},
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
exports[`components/admin_console/CustomPluginSettings should match snapshot with no settings and plugin 1`] = `
|
||||
<div
|
||||
className="wrapper--fixed "
|
||||
data-testid="sysconsole_section_testplugin"
|
||||
>
|
||||
<AdminHeader>
|
||||
testplugin
|
||||
|
|
@ -203,6 +204,7 @@ exports[`components/admin_console/CustomPluginSettings should match snapshot wit
|
|||
exports[`components/admin_console/CustomPluginSettings should match snapshot with settings and no plugin 1`] = `
|
||||
<div
|
||||
className="wrapper--fixed "
|
||||
data-testid="sysconsole_section_testplugin"
|
||||
>
|
||||
<AdminHeader>
|
||||
testplugin
|
||||
|
|
@ -253,6 +255,7 @@ exports[`components/admin_console/CustomPluginSettings should match snapshot wit
|
|||
exports[`components/admin_console/CustomPluginSettings should match snapshot with settings and plugin 1`] = `
|
||||
<div
|
||||
className="wrapper--fixed "
|
||||
data-testid="sysconsole_section_testplugin"
|
||||
>
|
||||
<AdminHeader>
|
||||
testplugin
|
||||
|
|
|
|||
|
|
@ -294,7 +294,10 @@ export default class FeatureDiscovery extends React.PureComponent<Props, State>
|
|||
}
|
||||
|
||||
return (
|
||||
<div className='FeatureDiscovery'>
|
||||
<div
|
||||
className='FeatureDiscovery'
|
||||
data-testid='featureDiscovery'
|
||||
>
|
||||
<div className='FeatureDiscovery_copyWrapper'>
|
||||
<div
|
||||
className='FeatureDiscovery_title'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,360 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
|
||||
type SvgProps = {
|
||||
width?: number;
|
||||
height?: number;
|
||||
}
|
||||
|
||||
const MobileSecuritySVG = (props: SvgProps) => (
|
||||
<svg
|
||||
width={props.width ? props.width.toString() : '208'}
|
||||
height={props.height ? props.height.toString() : '120'}
|
||||
viewBox='0 0 208 120'
|
||||
fill='none'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
>
|
||||
<rect
|
||||
x='10.7451'
|
||||
y='22'
|
||||
width='181'
|
||||
height='78'
|
||||
rx='5.625'
|
||||
fill='#1C58D9'
|
||||
fillOpacity='0.12'
|
||||
/>
|
||||
<path
|
||||
d='M12 28L17.5 33.5V72.5H182.5V101.5'
|
||||
stroke='#3F4350'
|
||||
strokeOpacity='0.24'
|
||||
strokeLinecap='round'
|
||||
/>
|
||||
<circle
|
||||
cx='2.5'
|
||||
cy='2.5'
|
||||
r='2.5'
|
||||
transform='matrix(1 0 0 -1 180 105)'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.48'
|
||||
/>
|
||||
<circle
|
||||
cx='2.5'
|
||||
cy='2.5'
|
||||
r='2.5'
|
||||
transform='matrix(1 0 0 -1 8 36)'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.48'
|
||||
/>
|
||||
<ellipse
|
||||
cx='102.5'
|
||||
cy='60.5'
|
||||
rx='59.5'
|
||||
ry='59.5'
|
||||
fill='#1C58D9'
|
||||
fillOpacity='0.04'
|
||||
/>
|
||||
<path
|
||||
d='M50 41L50 49.7097L148.46 49.7097L148.46 59L163 59'
|
||||
stroke='#3F4350'
|
||||
strokeOpacity='0.24'
|
||||
strokeLinecap='round'
|
||||
/>
|
||||
<path
|
||||
d='M136 100L136 85L121 85'
|
||||
stroke='#3F4350'
|
||||
strokeOpacity='0.24'
|
||||
strokeLinecap='round'
|
||||
/>
|
||||
<ellipse
|
||||
cx='1.5'
|
||||
cy='1.5'
|
||||
rx='1.5'
|
||||
ry='1.5'
|
||||
transform='matrix(1 8.74228e-08 8.74228e-08 -1 48.5 42)'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.48'
|
||||
/>
|
||||
<ellipse
|
||||
cx='136'
|
||||
cy='101.5'
|
||||
rx='1.5'
|
||||
ry='1.5'
|
||||
transform='rotate(180 136 101.5)'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.48'
|
||||
/>
|
||||
<ellipse
|
||||
cx='1.5'
|
||||
cy='1.5'
|
||||
rx='1.5'
|
||||
ry='1.5'
|
||||
transform='matrix(1 8.74228e-08 8.74228e-08 -1 162 70)'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.48'
|
||||
/>
|
||||
<path
|
||||
d='M102.238 107.436L102.482 107.572L102.725 107.437L107.738 104.652L107.738 104.652C134.208 89.9349 140.141 62.6051 142.498 32.2629L142.533 31.8248L142.102 31.7348L141.977 31.7086L141.927 31.698H141.875C129.258 31.698 116.365 27.1582 102.822 14.6329L102.444 14.2833L102.105 14.6715C96.4271 21.1871 91.4818 25.1561 85.8022 27.6605C80.114 30.1687 73.6481 31.2268 64.8879 31.85L64.8664 31.8515L64.8451 31.8549L62.9217 32.1596L62.4635 32.2323L62.5017 32.6946C64.9873 62.7953 70.8178 89.8754 97.0623 104.543L102.238 107.436Z'
|
||||
fill='white'
|
||||
stroke='#3F4350'
|
||||
/>
|
||||
<path
|
||||
d='M70.2469 81.6538C73.544 87.9479 77.7115 93.688 83.0142 98.7308M84.5619 100.154C85.6612 101.131 86.8066 102.08 88 103'
|
||||
stroke='#3F4350'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
/>
|
||||
<path
|
||||
d='M102.485 99L98.0951 96.6096C76.045 84.6062 71.1078 62.4456 69 37.5833L70.6312 37.3316C85.5165 36.3001 92.8039 33.8204 102.485 23C114.04 33.4089 125.074 37.207 135.894 37.207L136 37.2287C134.001 62.2886 128.977 84.6549 106.736 96.6989L102.485 99Z'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.12'
|
||||
/>
|
||||
<path
|
||||
d='M77.5632 51.6124H121.686'
|
||||
stroke='#3F4350'
|
||||
strokeOpacity='0.12'
|
||||
strokeLinecap='round'
|
||||
/>
|
||||
<path
|
||||
d='M84.1345 57.2451H128.257'
|
||||
stroke='#3F4350'
|
||||
strokeOpacity='0.12'
|
||||
strokeLinecap='round'
|
||||
/>
|
||||
<path
|
||||
d='M79.4407 63.8165H123.563'
|
||||
stroke='#3F4350'
|
||||
strokeOpacity='0.12'
|
||||
strokeLinecap='round'
|
||||
/>
|
||||
<path
|
||||
d='M87.8896 75.0818H108.543'
|
||||
stroke='#3F4350'
|
||||
strokeOpacity='0.12'
|
||||
strokeLinecap='round'
|
||||
/>
|
||||
<path
|
||||
d='M73.8076 46.1042H87.8893'
|
||||
stroke='#3F4350'
|
||||
strokeOpacity='0.12'
|
||||
strokeLinecap='round'
|
||||
/>
|
||||
<path
|
||||
d='M90.7056 46.1042H111.359'
|
||||
stroke='#3F4350'
|
||||
strokeOpacity='0.12'
|
||||
strokeLinecap='round'
|
||||
/>
|
||||
<path
|
||||
d='M114.175 46.1042H132.012'
|
||||
stroke='#3F4350'
|
||||
strokeOpacity='0.12'
|
||||
strokeLinecap='round'
|
||||
/>
|
||||
<path
|
||||
d='M101.033 80.7149H118.869'
|
||||
stroke='#3F4350'
|
||||
strokeOpacity='0.12'
|
||||
strokeLinecap='round'
|
||||
/>
|
||||
<path
|
||||
d='M87.8899 80.7149H98.2164'
|
||||
stroke='#3F4350'
|
||||
strokeOpacity='0.12'
|
||||
strokeLinecap='round'
|
||||
/>
|
||||
<path
|
||||
d='M81.3184 69.1778H101.971'
|
||||
stroke='#3F4350'
|
||||
strokeOpacity='0.12'
|
||||
strokeLinecap='round'
|
||||
/>
|
||||
<path
|
||||
d='M104.788 69.1777H122.624'
|
||||
stroke='#3F4350'
|
||||
strokeOpacity='0.12'
|
||||
strokeLinecap='round'
|
||||
/>
|
||||
<path
|
||||
d='M77.563 40.8164H101.032'
|
||||
stroke='#3F4350'
|
||||
strokeOpacity='0.12'
|
||||
strokeLinecap='round'
|
||||
/>
|
||||
<path
|
||||
d='M104.788 40.8164H128.257'
|
||||
stroke='#3F4350'
|
||||
strokeOpacity='0.12'
|
||||
strokeLinecap='round'
|
||||
/>
|
||||
<path
|
||||
fillRule='evenodd'
|
||||
clipRule='evenodd'
|
||||
d='M92.0013 57.3883H113.118C114.095 57.3883 114.885 58.1742 114.885 59.1484V66.9186C114.885 73.5528 111.909 78.9321 105.259 78.9321H99.8606C93.2109 78.9321 90.2349 73.5528 90.2349 66.9186V59.1484C90.2349 58.1775 91.0273 57.3883 92.0013 57.3883ZM102.172 61.9248H101.235V64.127V68.0017L102.478 69.2449L101.235 70.4881V72.1297H104.636V65.3642L103.686 64.4136L104.636 63.4629V61.9248H102.172Z'
|
||||
fill='#1C58D9'
|
||||
/>
|
||||
<path
|
||||
fillRule='evenodd'
|
||||
clipRule='evenodd'
|
||||
d='M108.374 54.1074V57.4273C108.374 57.4476 108.375 57.4675 108.376 57.4871H111.35C111.352 57.4675 111.352 57.4476 111.352 57.4273V54.1074C111.352 49.1351 108.096 45.0882 103.308 45.0882H101.28C96.4909 45.0882 93.2349 49.1351 93.2349 54.1074V57.4273C93.2349 57.4476 93.2356 57.4675 93.2371 57.4871H96.205C96.2066 57.4675 96.2073 57.4476 96.2073 57.4273V54.1074C96.2073 50.7242 98.3244 48.1476 100.835 48.1476H103.746C106.424 48.1476 108.374 50.7242 108.374 54.1074Z'
|
||||
fill='#1C58D9'
|
||||
/>
|
||||
<path
|
||||
d='M109 16C109 16 118.172 25.6296 137 29'
|
||||
stroke='#3F4350'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
/>
|
||||
<rect
|
||||
x='1.5'
|
||||
y='20.5'
|
||||
width='43'
|
||||
height='79'
|
||||
rx='7.5'
|
||||
fill='white'
|
||||
stroke='#3F4350'
|
||||
strokeWidth='3'
|
||||
/>
|
||||
<path
|
||||
d='M3 27.5C3 24.4624 5.46243 22 8.5 22H37.5C40.5376 22 43 24.4624 43 27.5V29H3V27.5Z'
|
||||
fill='#1C58D9'
|
||||
fillOpacity='0.16'
|
||||
/>
|
||||
<circle
|
||||
cx='12.831'
|
||||
cy='64.831'
|
||||
r='5.83099'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.32'
|
||||
/>
|
||||
<path
|
||||
d='M9.69995 64.7411L12.0947 67.1786L16.2 63'
|
||||
stroke='white'
|
||||
strokeWidth='0.5525'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
/>
|
||||
<path
|
||||
d='M22.5488 63H32.915'
|
||||
stroke='#3F4350'
|
||||
strokeOpacity='0.48'
|
||||
strokeLinecap='round'
|
||||
/>
|
||||
<path
|
||||
d='M8 74H34'
|
||||
stroke='#3F4350'
|
||||
strokeOpacity='0.48'
|
||||
strokeLinecap='round'
|
||||
/>
|
||||
<path
|
||||
d='M8 82H28'
|
||||
stroke='#3F4350'
|
||||
strokeOpacity='0.48'
|
||||
strokeLinecap='round'
|
||||
/>
|
||||
<path
|
||||
d='M22.5488 67H38.746'
|
||||
stroke='#3F4350'
|
||||
strokeOpacity='0.48'
|
||||
strokeLinecap='round'
|
||||
/>
|
||||
<path
|
||||
d='M8 78H24'
|
||||
stroke='#3F4350'
|
||||
strokeOpacity='0.48'
|
||||
strokeLinecap='round'
|
||||
/>
|
||||
<path
|
||||
d='M27 78L39 78'
|
||||
stroke='#3F4350'
|
||||
strokeOpacity='0.48'
|
||||
strokeLinecap='round'
|
||||
/>
|
||||
<rect
|
||||
x='15'
|
||||
y='25'
|
||||
width='16'
|
||||
height='1'
|
||||
rx='0.5'
|
||||
fill='#3F4350'
|
||||
/>
|
||||
<rect
|
||||
opacity='0.16'
|
||||
x='157'
|
||||
y='23'
|
||||
width='49'
|
||||
height='64'
|
||||
rx='4'
|
||||
fill='#090A0B'
|
||||
/>
|
||||
<rect
|
||||
x='159.5'
|
||||
y='19.5'
|
||||
width='48'
|
||||
height='65'
|
||||
rx='3.5'
|
||||
fill='white'
|
||||
stroke='#3F4350'
|
||||
/>
|
||||
<path
|
||||
d='M185 38H201'
|
||||
stroke='#3F4350'
|
||||
strokeOpacity='0.56'
|
||||
strokeLinecap='round'
|
||||
/>
|
||||
<path
|
||||
d='M184 65H200'
|
||||
stroke='#3F4350'
|
||||
strokeOpacity='0.56'
|
||||
strokeLinecap='round'
|
||||
/>
|
||||
<path
|
||||
d='M184 71H200'
|
||||
stroke='#3F4350'
|
||||
strokeOpacity='0.56'
|
||||
strokeLinecap='round'
|
||||
/>
|
||||
<path
|
||||
d='M165 47H202'
|
||||
stroke='#3F4350'
|
||||
strokeOpacity='0.56'
|
||||
strokeLinecap='round'
|
||||
/>
|
||||
<path
|
||||
d='M165 53H202'
|
||||
stroke='#3F4350'
|
||||
strokeOpacity='0.56'
|
||||
strokeLinecap='round'
|
||||
/>
|
||||
<path
|
||||
d='M185 32H194.635'
|
||||
stroke='#3F4350'
|
||||
strokeOpacity='0.56'
|
||||
strokeLinecap='round'
|
||||
/>
|
||||
<path
|
||||
d='M196.932 32H199.946'
|
||||
stroke='#3F4350'
|
||||
strokeOpacity='0.56'
|
||||
strokeLinecap='round'
|
||||
/>
|
||||
<path
|
||||
fillRule='evenodd'
|
||||
clipRule='evenodd'
|
||||
d='M171.467 26.918H173.457L173.709 28.5555C174.318 28.6939 174.888 28.9343 175.401 29.2577L176.74 28.2756L178.147 29.6827L177.153 31.0376C177.462 31.545 177.691 32.1069 177.822 32.7055L179.505 32.9645V34.9544L177.778 35.2201C177.634 35.7869 177.401 36.3184 177.094 36.7986L178.147 38.2337L176.74 39.6407L175.271 38.5634C174.801 38.8416 174.286 39.0514 173.74 39.179L173.46 41H171.47L171.188 39.1638C170.651 39.0316 170.145 38.8198 169.684 38.5418L168.186 39.6406L166.779 38.2335L167.867 36.7505C167.577 36.2856 167.355 35.774 167.215 35.2294L165.422 34.9535V32.9636L167.172 32.6944C167.299 32.1186 167.516 31.577 167.809 31.0854L166.78 29.6826L168.187 28.2755L169.556 29.2795C170.058 28.9575 170.616 28.7157 171.213 28.5718L171.467 26.918ZM175.147 33.8704C175.147 35.335 173.96 36.5222 172.495 36.5222C171.031 36.5222 169.844 35.335 169.844 33.8704C169.844 32.4059 171.031 31.2187 172.495 31.2187C173.96 31.2187 175.147 32.4059 175.147 33.8704Z'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.56'
|
||||
/>
|
||||
<path
|
||||
opacity='0.7'
|
||||
fillRule='evenodd'
|
||||
clipRule='evenodd'
|
||||
d='M172.405 61.9492H174.038L174.244 63.2907C174.745 63.4044 175.213 63.602 175.635 63.8679L176.73 63.0643L177.885 64.2188L177.071 65.3279C177.325 65.744 177.513 66.2048 177.619 66.6957L179 66.9081V68.5407L177.584 68.7585C177.465 69.2245 177.274 69.6613 177.022 70.0558L177.885 71.2329L176.73 72.3874L175.525 71.5032C175.14 71.7307 174.719 71.9023 174.272 72.0069L174.042 73.5039H172.409L172.177 71.9948C171.737 71.8867 171.324 71.7135 170.946 71.4861L169.715 72.3888L168.561 71.2344L169.453 70.0171C169.215 69.6355 169.033 69.2155 168.918 68.7683L167.445 68.5418V66.9092L168.881 66.6882C168.985 66.2163 169.163 65.7723 169.403 65.3692L168.56 64.2202L169.715 63.0657L170.835 63.887C171.247 63.6218 171.706 63.4226 172.197 63.3043L172.405 61.9492ZM175.426 67.6523C175.426 68.8539 174.452 69.828 173.25 69.828C172.048 69.828 171.074 68.8539 171.074 67.6523C171.074 66.4507 172.048 65.4766 173.25 65.4766C174.452 65.4766 175.426 66.4507 175.426 67.6523Z'
|
||||
fill='#3F4350'
|
||||
fillOpacity='0.56'
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default MobileSecuritySVG;
|
||||
|
|
@ -8,6 +8,7 @@ import DataRetentionFeatureDiscovery from './data_retention';
|
|||
import GroupsFeatureDiscovery from './groups';
|
||||
import GuestAccessFeatureDiscovery from './guest_access';
|
||||
import LDAPFeatureDiscovery from './ldap';
|
||||
import MobileSecurityFeatureDiscovery from './mobile_security';
|
||||
import OpenIDFeatureDiscovery from './openid';
|
||||
import OpenIDCustomFeatureDiscovery from './openid_custom';
|
||||
import SAMLFeatureDiscovery from './saml';
|
||||
|
|
@ -25,4 +26,5 @@ export {
|
|||
GuestAccessFeatureDiscovery,
|
||||
SystemRolesFeatureDiscovery,
|
||||
GroupsFeatureDiscovery,
|
||||
MobileSecurityFeatureDiscovery,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import {renderWithContext, screen} from 'tests/react_testing_utils';
|
||||
import {LicenseSkus} from 'utils/constants';
|
||||
|
||||
import MobileSecurityFeatureDiscovery from './mobile_security';
|
||||
|
||||
// Mock the FeatureDiscovery component
|
||||
jest.mock('../index', () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn((props) => {
|
||||
// Render a simple div with key props for testing
|
||||
return (
|
||||
<div data-testid='feature-discovery'>
|
||||
<div data-testid='feature-name'>{props.featureName}</div>
|
||||
<div data-testid='minimum-sku'>{props.minimumSKURequiredForFeature}</div>
|
||||
<div data-testid='title'>{props.title.defaultMessage}</div>
|
||||
<div data-testid='copy'>{props.copy.defaultMessage}</div>
|
||||
<div data-testid='learn-more-url'>{props.learnMoreURL}</div>
|
||||
<div data-testid='feature-image'>{props.featureDiscoveryImage}</div>
|
||||
</div>
|
||||
);
|
||||
}),
|
||||
}));
|
||||
|
||||
// Mock the SVG component
|
||||
jest.mock('./images/mobile_security_svg', () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn((props) => (
|
||||
<div data-testid='mobile-security-svg'>
|
||||
<div data-testid='mobile-security-svg-width'>{props.width}</div>
|
||||
<div data-testid='mobile-security-svg-height'>{props.height}</div>
|
||||
</div>
|
||||
)),
|
||||
}));
|
||||
|
||||
describe('components/admin_console/feature_discovery/features/MobileSecurityFeatureDiscovery', () => {
|
||||
it('renders correctly with expected props', () => {
|
||||
renderWithContext(<MobileSecurityFeatureDiscovery/>);
|
||||
|
||||
// Verify feature name
|
||||
expect(screen.getByTestId('feature-name')).toHaveTextContent('mobile_security');
|
||||
|
||||
// Verify minimum SKU
|
||||
expect(screen.getByTestId('minimum-sku')).toHaveTextContent(LicenseSkus.Enterprise);
|
||||
|
||||
// Verify title
|
||||
expect(screen.getByTestId('title')).toHaveTextContent('Enhance mobile app security with Mattermost Enterprise');
|
||||
|
||||
// Verify copy text
|
||||
expect(screen.getByTestId('copy')).toHaveTextContent(
|
||||
'Enable advanced security features like biometric authentication, screen capture prevention, and jailbreak/root detection for your mobile users.',
|
||||
);
|
||||
|
||||
// Verify learn more URL
|
||||
expect(screen.getByTestId('learn-more-url')).toHaveTextContent(
|
||||
'https://docs.mattermost.com/configure/environment-configuration-settings.html#mobile-security',
|
||||
);
|
||||
|
||||
// Verify SVG is rendered
|
||||
expect(screen.getByTestId('mobile-security-svg')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('mobile-security-svg-width')).toHaveTextContent('294');
|
||||
expect(screen.getByTestId('mobile-security-svg-height')).toHaveTextContent('170');
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {defineMessage} from 'react-intl';
|
||||
|
||||
import {LicenseSkus} from 'utils/constants';
|
||||
|
||||
import MobileSecuritySVG from './images/mobile_security_svg';
|
||||
|
||||
import FeatureDiscovery from '../index';
|
||||
|
||||
const MobileSecurityFeatureDiscovery = () => {
|
||||
return (
|
||||
<FeatureDiscovery
|
||||
featureName='mobile_security'
|
||||
minimumSKURequiredForFeature={LicenseSkus.Enterprise}
|
||||
title={defineMessage({
|
||||
id: 'admin.mobile_security_feature_discovery.title',
|
||||
defaultMessage: 'Enhance mobile app security with Mattermost Enterprise',
|
||||
})}
|
||||
copy={defineMessage({
|
||||
id: 'admin.mobile_security_feature_discovery.copy',
|
||||
defaultMessage: 'Enable advanced security features like biometric authentication, screen capture prevention, and jailbreak/root detection for your mobile users.',
|
||||
})}
|
||||
learnMoreURL='https://docs.mattermost.com/configure/environment-configuration-settings.html#mobile-security'
|
||||
featureDiscoveryImage={
|
||||
<MobileSecuritySVG
|
||||
width={294}
|
||||
height={170}
|
||||
/>}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default MobileSecurityFeatureDiscovery;
|
||||
|
|
@ -1328,7 +1328,10 @@ export class SchemaAdminSettings extends React.PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className={'wrapper--fixed ' + this.state.customComponentWrapperClass}>
|
||||
<div
|
||||
className={'wrapper--fixed ' + this.state.customComponentWrapperClass}
|
||||
data-testid={`sysconsole_section_${this.props.schema.id}`}
|
||||
>
|
||||
{this.renderTitle()}
|
||||
<div className='admin-console__wrapper'>
|
||||
<div className='admin-console__content'>
|
||||
|
|
|
|||
|
|
@ -172,6 +172,9 @@ exports[`admin_console/system_role_permissions should match snapshot 1`] = `
|
|||
Object {
|
||||
"name": "environment_developer",
|
||||
},
|
||||
Object {
|
||||
"name": "environment_mobile_security",
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -200,6 +200,12 @@ export const sectionStrings: Record<string, Record<string, MessageDescriptor>> =
|
|||
defaultMessage: 'Developer',
|
||||
},
|
||||
}),
|
||||
environment_mobile_security: defineMessages({
|
||||
name: {
|
||||
id: 'admin.permissions.sysconsole_section_environment_mobile_security.name',
|
||||
defaultMessage: 'Mobile Security',
|
||||
},
|
||||
}),
|
||||
site: defineMessages({
|
||||
name: {
|
||||
id: 'admin.permissions.sysconsole_section_site.name',
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@ const sectionsList: SystemSection[] = [
|
|||
{name: 'environment_session_lengths'},
|
||||
{name: 'environment_performance_monitoring'},
|
||||
{name: 'environment_developer'},
|
||||
{name: 'environment_mobile_security'},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ exports[`components/global/product_switcher_menu should match snapshot with id 1
|
|||
"sysconsole_read_environment_session_lengths",
|
||||
"sysconsole_read_environment_performance_monitoring",
|
||||
"sysconsole_read_environment_developer",
|
||||
"sysconsole_read_environment_mobile_security",
|
||||
"sysconsole_read_authentication_signup",
|
||||
"sysconsole_read_authentication_email",
|
||||
"sysconsole_read_authentication_password",
|
||||
|
|
@ -234,6 +235,7 @@ exports[`components/global/product_switcher_menu should match snapshot with most
|
|||
"sysconsole_read_environment_session_lengths",
|
||||
"sysconsole_read_environment_performance_monitoring",
|
||||
"sysconsole_read_environment_developer",
|
||||
"sysconsole_read_environment_mobile_security",
|
||||
"sysconsole_read_authentication_signup",
|
||||
"sysconsole_read_authentication_email",
|
||||
"sysconsole_read_authentication_password",
|
||||
|
|
@ -548,6 +550,7 @@ exports[`components/global/product_switcher_menu should show integrations should
|
|||
"sysconsole_read_environment_session_lengths",
|
||||
"sysconsole_read_environment_performance_monitoring",
|
||||
"sysconsole_read_environment_developer",
|
||||
"sysconsole_read_environment_mobile_security",
|
||||
"sysconsole_read_authentication_signup",
|
||||
"sysconsole_read_authentication_email",
|
||||
"sysconsole_read_authentication_password",
|
||||
|
|
|
|||
|
|
@ -1531,6 +1531,15 @@
|
|||
"admin.metrics.listenAddressEx": "E.g.: \":8067\"",
|
||||
"admin.metrics.listenAddressTitle": "Listen Address:",
|
||||
"admin.mfa.bannerDesc": "<link>Multi-factor authentication</link> is available for accounts with AD/LDAP or email login. If other login methods are used, MFA should be configured with the authentication provider.",
|
||||
"admin.mobile_security_feature_discovery.copy": "Enable advanced security features like biometric authentication, screen capture prevention, and jailbreak/root detection for your mobile users.",
|
||||
"admin.mobile_security_feature_discovery.title": "Enhance mobile app security with Mattermost Enterprise",
|
||||
"admin.mobileSecurity.biometricsDescription": "Enforces biometric authentication (with PIN/passcode fallback) before accessing the app. Users will be prompted based on session activity and server switching rules.",
|
||||
"admin.mobileSecurity.biometricsTitle": "Enable Biometric Authentication:",
|
||||
"admin.mobileSecurity.jailbreakDescription": "Prevents access to the app on devices detected as jailbroken or rooted. If a device fails the security check, users will be denied access or prompted to switch to a compliant server.",
|
||||
"admin.mobileSecurity.jailbreakTitle": "Enable Jailbreak/Root Protection:",
|
||||
"admin.mobileSecurity.screenCaptureDescription": "Blocks screenshots and screen recordings when using the mobile app. Screenshots will appear blank, and screen recordings will blur (iOS) or show a black screen (Android). Also applies when switching apps.",
|
||||
"admin.mobileSecurity.screenCaptureTitle": "Prevent Screen Capture:",
|
||||
"admin.mobileSecurity.title": "Mobile Security",
|
||||
"admin.nav.administratorsGuide": "Administrator's Guide",
|
||||
"admin.nav.commercialSupport": "Commercial Support",
|
||||
"admin.nav.menuAriaLabel": "Admin Console Menu",
|
||||
|
|
@ -1846,6 +1855,7 @@
|
|||
"admin.permissions.sysconsole_section_environment_high_availability.name": "High Availability",
|
||||
"admin.permissions.sysconsole_section_environment_image_proxy.name": "Image Proxy",
|
||||
"admin.permissions.sysconsole_section_environment_logging.name": "Logging",
|
||||
"admin.permissions.sysconsole_section_environment_mobile_security.name": "Mobile Security",
|
||||
"admin.permissions.sysconsole_section_environment_performance_monitoring.name": "Performance Monitoring",
|
||||
"admin.permissions.sysconsole_section_environment_push_notification_server.name": "Push Notification Server",
|
||||
"admin.permissions.sysconsole_section_environment_rate_limiting.name": "Rate Limiting",
|
||||
|
|
@ -2471,6 +2481,7 @@
|
|||
"admin.sidebar.logs": "Server Logs",
|
||||
"admin.sidebar.metrics": "Performance Monitoring",
|
||||
"admin.sidebar.mfa": "MFA",
|
||||
"admin.sidebar.mobileSecurity": "Mobile Security",
|
||||
"admin.sidebar.move_thread": "Move thread (Beta)",
|
||||
"admin.sidebar.notices": "Notices",
|
||||
"admin.sidebar.notifications": "Notifications",
|
||||
|
|
|
|||
|
|
@ -181,6 +181,8 @@ const values = {
|
|||
SYSCONSOLE_WRITE_ENVIRONMENT_PERFORMANCE_MONITORING: 'sysconsole_write_environment_performance_monitoring',
|
||||
SYSCONSOLE_READ_ENVIRONMENT_DEVELOPER: 'sysconsole_read_environment_developer',
|
||||
SYSCONSOLE_WRITE_ENVIRONMENT_DEVELOPER: 'sysconsole_write_environment_developer',
|
||||
SYSCONSOLE_READ_ENVIRONMENT_MOBILE_SECURITY: 'sysconsole_read_environment_mobile_security',
|
||||
SYSCONSOLE_WRITE_ENVIRONMENT_MOBILE_SECURITY: 'sysconsole_write_environment_mobile_security',
|
||||
SYSCONSOLE_READ_AUTHENTICATION_SIGNUP: 'sysconsole_read_authentication_signup',
|
||||
SYSCONSOLE_WRITE_AUTHENTICATION_SIGNUP: 'sysconsole_write_authentication_signup',
|
||||
SYSCONSOLE_READ_AUTHENTICATION_EMAIL: 'sysconsole_read_authentication_email',
|
||||
|
|
@ -306,6 +308,7 @@ values.SYSCONSOLE_READ_PERMISSIONS = [
|
|||
values.SYSCONSOLE_READ_ENVIRONMENT_SESSION_LENGTHS,
|
||||
values.SYSCONSOLE_READ_ENVIRONMENT_PERFORMANCE_MONITORING,
|
||||
values.SYSCONSOLE_READ_ENVIRONMENT_DEVELOPER,
|
||||
values.SYSCONSOLE_READ_ENVIRONMENT_MOBILE_SECURITY,
|
||||
values.SYSCONSOLE_READ_AUTHENTICATION_SIGNUP,
|
||||
values.SYSCONSOLE_READ_AUTHENTICATION_EMAIL,
|
||||
values.SYSCONSOLE_READ_AUTHENTICATION_PASSWORD,
|
||||
|
|
@ -363,6 +366,7 @@ values.SYSCONSOLE_WRITE_PERMISSIONS = [
|
|||
values.SYSCONSOLE_WRITE_ENVIRONMENT_SESSION_LENGTHS,
|
||||
values.SYSCONSOLE_WRITE_ENVIRONMENT_PERFORMANCE_MONITORING,
|
||||
values.SYSCONSOLE_WRITE_ENVIRONMENT_DEVELOPER,
|
||||
values.SYSCONSOLE_WRITE_ENVIRONMENT_MOBILE_SECURITY,
|
||||
values.SYSCONSOLE_WRITE_AUTHENTICATION_SIGNUP,
|
||||
values.SYSCONSOLE_WRITE_AUTHENTICATION_EMAIL,
|
||||
values.SYSCONSOLE_WRITE_AUTHENTICATION_PASSWORD,
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ export const RESOURCE_KEYS = {
|
|||
SESSION_LENGTHS: 'environment.session_lengths',
|
||||
PERFORMANCE_MONITORING: 'environment.performance_monitoring',
|
||||
DEVELOPER: 'environment.developer',
|
||||
MOBILE_SECURITY: 'environment.mobile_security',
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -116,6 +117,7 @@ export const ResourceToSysConsolePermissionsTable: Record<string, string[]> = {
|
|||
[RESOURCE_KEYS.ENVIRONMENT.SESSION_LENGTHS]: [Permissions.SYSCONSOLE_READ_ENVIRONMENT_SESSION_LENGTHS, Permissions.SYSCONSOLE_WRITE_ENVIRONMENT_SESSION_LENGTHS],
|
||||
[RESOURCE_KEYS.ENVIRONMENT.PERFORMANCE_MONITORING]: [Permissions.SYSCONSOLE_READ_ENVIRONMENT_PERFORMANCE_MONITORING, Permissions.SYSCONSOLE_WRITE_ENVIRONMENT_PERFORMANCE_MONITORING],
|
||||
[RESOURCE_KEYS.ENVIRONMENT.DEVELOPER]: [Permissions.SYSCONSOLE_READ_ENVIRONMENT_DEVELOPER, Permissions.SYSCONSOLE_WRITE_ENVIRONMENT_DEVELOPER],
|
||||
[RESOURCE_KEYS.ENVIRONMENT.MOBILE_SECURITY]: [Permissions.SYSCONSOLE_READ_ENVIRONMENT_MOBILE_SECURITY, Permissions.SYSCONSOLE_WRITE_ENVIRONMENT_MOBILE_SECURITY],
|
||||
[RESOURCE_KEYS.AUTHENTICATION.SIGNUP]: [Permissions.SYSCONSOLE_READ_AUTHENTICATION_SIGNUP, Permissions.SYSCONSOLE_WRITE_AUTHENTICATION_SIGNUP],
|
||||
[RESOURCE_KEYS.AUTHENTICATION.EMAIL]: [Permissions.SYSCONSOLE_READ_AUTHENTICATION_EMAIL, Permissions.SYSCONSOLE_WRITE_AUTHENTICATION_EMAIL],
|
||||
[RESOURCE_KEYS.AUTHENTICATION.PASSWORD]: [Permissions.SYSCONSOLE_READ_AUTHENTICATION_PASSWORD, Permissions.SYSCONSOLE_WRITE_AUTHENTICATION_PASSWORD],
|
||||
|
|
|
|||
|
|
@ -784,6 +784,7 @@
|
|||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
|
||||
i {
|
||||
|
|
|
|||
Loading…
Reference in a new issue