[Security solution] Risk score platinum CTA (#140863)

This commit is contained in:
Steph Milovic 2022-09-19 15:51:49 -05:00 committed by GitHub
parent 51699fa21a
commit e7b4063057
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 158 additions and 49 deletions

View file

@ -54,6 +54,16 @@ Object {
"name": "Cloud Posture",
"onClick": [Function],
},
Object {
"data-href": "securitySolutionUI/entity-analytics",
"data-test-subj": "navigation-entity-analytics",
"disabled": false,
"href": "securitySolutionUI/entity-analytics",
"id": "entity-analytics",
"isSelected": false,
"name": "Entity Analytics",
"onClick": [Function],
},
],
"name": "Dashboards",
},

View file

@ -92,17 +92,6 @@ describe('useSecuritySolutionNavigation', () => {
expect(result.current).toMatchSnapshot();
});
// TODO: Steph/users remove when no longer experimental
it('should include users when feature flag is on', async () => {
(useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(true);
const { result } = renderHook<{}, KibanaPageTemplateProps['solutionNav']>(
() => useSecuritySolutionNavigation(),
{ wrapper: TestProviders }
);
expect(result?.current?.items?.[4].items?.[2].id).toEqual(SecurityPageName.users);
});
// TODO: [kubernetes] remove when no longer experimental
it('should include kubernetes when feature flag is on', async () => {
(useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(true);
@ -110,7 +99,7 @@ describe('useSecuritySolutionNavigation', () => {
() => useSecuritySolutionNavigation(),
{ wrapper: TestProviders }
);
expect(result?.current?.items?.[1].items?.[3].id).toEqual(SecurityPageName.kubernetes);
expect(result?.current?.items?.[1].items?.[4].id).toEqual(SecurityPageName.kubernetes);
});
it('should omit host isolation exceptions if hook reports false', () => {

View file

@ -21,9 +21,6 @@ import { SecurityPageName } from '../../../../../common/constants';
import { useCanSeeHostIsolationExceptionsMenu } from '../../../../management/pages/host_isolation_exceptions/view/hooks';
import { useIsExperimentalFeatureEnabled } from '../../../hooks/use_experimental_features';
import { useGlobalQueryString } from '../../../utils/global_query_string';
import { useMlCapabilities } from '../../ml/hooks/use_ml_capabilities';
import { hasMlUserPermissions } from '../../../../../common/machine_learning/has_ml_user_permissions';
import { hasMlLicense } from '../../../../../common/machine_learning/has_ml_license';
export const usePrimaryNavigationItems = ({
navTabs,
@ -75,8 +72,6 @@ function usePrimaryNavigationItemsToDisplay(navTabs: Record<string, NavTab>) {
const hasCasesReadPermissions = useGetUserCasesPermissions().read;
const canSeeHostIsolationExceptions = useCanSeeHostIsolationExceptionsMenu();
const isPolicyListEnabled = useIsExperimentalFeatureEnabled('policyListEnabled');
const mlCapabilities = useMlCapabilities();
const hasMlPermissions = hasMlLicense(mlCapabilities) && hasMlUserPermissions(mlCapabilities);
const uiCapabilities = useKibana().services.application.capabilities;
return useMemo(
@ -94,10 +89,10 @@ function usePrimaryNavigationItemsToDisplay(navTabs: Record<string, NavTab>) {
navTabs[SecurityPageName.overview],
navTabs[SecurityPageName.detectionAndResponse],
navTabs[SecurityPageName.cloudSecurityPostureDashboard],
navTabs[SecurityPageName.entityAnalytics],
...(navTabs[SecurityPageName.kubernetes] != null
? [navTabs[SecurityPageName.kubernetes]]
: []),
...(hasMlPermissions ? [navTabs[SecurityPageName.entityAnalytics]] : []),
],
},
{
@ -162,7 +157,6 @@ function usePrimaryNavigationItemsToDisplay(navTabs: Record<string, NavTab>) {
hasCasesReadPermissions,
canSeeHostIsolationExceptions,
isPolicyListEnabled,
hasMlPermissions,
]
);
}

View file

@ -0,0 +1,81 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React, { memo, useCallback } from 'react';
import {
EuiCard,
EuiIcon,
EuiFlexGroup,
EuiFlexItem,
EuiText,
EuiButton,
EuiTextColor,
} from '@elastic/eui';
import styled from 'styled-components';
import { useNavigation } from '../../lib/kibana';
import * as i18n from './translations';
const PaywallDiv = styled.div`
max-width: 85%;
margin: 0 auto;
.euiCard__betaBadgeWrapper {
.euiCard__betaBadge {
width: auto;
}
}
.platinumCardDescription {
padding: 0 15%;
}
`;
export const Paywall = memo(({ featureDescription }: { featureDescription?: string }) => {
const { getAppUrl, navigateTo } = useNavigation();
const subscriptionUrl = getAppUrl({
appId: 'management',
path: 'stack/license_management',
});
const goToSubscription = useCallback(() => {
navigateTo({ url: subscriptionUrl });
}, [navigateTo, subscriptionUrl]);
return (
<PaywallDiv>
<EuiCard
data-test-subj="platinumCard"
betaBadgeProps={{ label: i18n.PLATINUM }}
icon={<EuiIcon size="xl" type="lock" />}
display="subdued"
title={
<h3>
<strong>{i18n.UPGRADE_CTA}</strong>
</h3>
}
description={false}
>
<EuiFlexGroup className="platinumCardDescription" direction="column" gutterSize="none">
<EuiText>
<EuiFlexItem>
<p>
<EuiTextColor color="subdued">
{i18n.UPGRADE_MESSAGE(featureDescription)}
</EuiTextColor>
</p>
</EuiFlexItem>
<EuiFlexItem>
<div>
<EuiButton onClick={goToSubscription} fill>
{i18n.UPGRADE_BUTTON}
</EuiButton>
</div>
</EuiFlexItem>
</EuiText>
</EuiFlexGroup>
</EuiCard>
</PaywallDiv>
);
});
Paywall.displayName = 'Paywall';

View file

@ -0,0 +1,27 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { i18n } from '@kbn/i18n';
export const PLATINUM = i18n.translate('xpack.securitySolution.paywall.platinum', {
defaultMessage: 'Platinum',
});
export const UPGRADE_CTA = i18n.translate('xpack.securitySolution.paywall.upgradeButton', {
defaultMessage: 'Available from Platinum',
});
export const UPGRADE_MESSAGE = (description?: string) =>
i18n.translate('xpack.securitySolution.paywall.upgradeMessage', {
values: { description: description ? description : 'this feature' },
defaultMessage:
'To turn use {description}, you must upgrade your license to Platinum, start a free 30-days trial, or spin up a cloud deployment on AWS, GCP, or Azure.',
});
export const UPGRADE_BUTTON = i18n.translate('xpack.securitySolution.paywall.upgradeCta', {
defaultMessage: 'Upgrade to Platinum',
});

View file

@ -83,7 +83,6 @@ export const entityAnalyticsLinks: LinkItem = {
}),
path: ENTITY_ANALYTICS_PATH,
capabilities: [`${SERVER_APP_ID}.show`],
licenseType: 'platinum',
isBeta: true,
betaOptions: {
text: i18n.translate('xpack.securitySolution.appLinks.technicalPreview', {

View file

@ -7,6 +7,9 @@
import React from 'react';
import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui';
import { ENTITY_ANALYTICS } from '../../app/translations';
import { Paywall } from '../../common/components/paywall';
import { useMlCapabilities } from '../../common/components/ml/hooks/use_ml_capabilities';
import { SpyRoute } from '../../common/utils/route/spy_routes';
import { SecurityPageName } from '../../app/types';
import { useSourcererDataView } from '../../common/containers/sourcerer';
@ -25,13 +28,14 @@ import { InputsModelId } from '../../common/store/inputs/constants';
const EntityAnalyticsComponent = () => {
const { indicesExist, loading: isSourcererLoading, indexPattern } = useSourcererDataView();
const isPlatinumOrTrialLicense = useMlCapabilities().isPlatinumOrTrialLicense;
return (
<>
{indicesExist ? (
<>
<SecuritySolutionPageWrapper data-test-subj="entityAnalyticsPage">
<HeaderPage
title={i18n.ENTITY_ANALYTICS_TITLE}
title={ENTITY_ANALYTICS}
badgeOptions={{
text: i18n.TECHNICAL_PREVIEW,
color: 'white',
@ -39,34 +43,39 @@ const EntityAnalyticsComponent = () => {
beta: true,
}}
>
<SiemSearchBar
id={InputsModelId.global}
indexPattern={indexPattern}
hideFilterBar
hideQueryInput
/>
{isPlatinumOrTrialLicense && (
<SiemSearchBar
id={InputsModelId.global}
indexPattern={indexPattern}
hideFilterBar
hideQueryInput
/>
)}
</HeaderPage>
{isPlatinumOrTrialLicense ? (
isSourcererLoading ? (
<EuiLoadingSpinner size="l" data-test-subj="entityAnalyticsLoader" />
) : (
<EuiFlexGroup direction="column" data-test-subj="entityAnalyticsSections">
<EuiFlexItem>
<EntityAnalyticsHeader />
</EuiFlexItem>
{isSourcererLoading ? (
<EuiLoadingSpinner size="l" data-test-subj="entityAnalyticsLoader" />
<EuiFlexItem>
<EntityAnalyticsHostRiskScores />
</EuiFlexItem>
<EuiFlexItem>
<EntityAnalyticsUserRiskScores />
</EuiFlexItem>
<EuiFlexItem>
<EntityAnalyticsAnomalies />
</EuiFlexItem>
</EuiFlexGroup>
)
) : (
<EuiFlexGroup direction="column" data-test-subj="entityAnalyticsSections">
<EuiFlexItem>
<EntityAnalyticsHeader />
</EuiFlexItem>
<EuiFlexItem>
<EntityAnalyticsHostRiskScores />
</EuiFlexItem>
<EuiFlexItem>
<EntityAnalyticsUserRiskScores />
</EuiFlexItem>
<EuiFlexItem>
<EntityAnalyticsAnomalies />
</EuiFlexItem>
</EuiFlexGroup>
<Paywall featureDescription={i18n.ENTITY_ANALYTICS_LICENSE_DESC} />
)}
</SecuritySolutionPageWrapper>
</>

View file

@ -50,10 +50,10 @@ export const DETECTION_RESPONSE_TITLE = i18n.translate(
}
);
export const ENTITY_ANALYTICS_TITLE = i18n.translate(
'xpack.securitySolution.entityAnalytics.pageTitle',
export const ENTITY_ANALYTICS_LICENSE_DESC = i18n.translate(
'xpack.securitySolution.entityAnalytics.pageDesc',
{
defaultMessage: 'Entity Analytics',
defaultMessage: 'Entity Analytics features',
}
);