From 02ff875e93982a8352cb5971c4af9d544aabfb98 Mon Sep 17 00:00:00 2001 From: Ryland Herrick Date: Thu, 19 Mar 2020 15:25:36 -0500 Subject: [PATCH 1/7] Gate ML Rules behind a license check If they don't have a Platinum or Trial license, then we disable the ML Card and provide them a link to the subscriptions marketing page. --- .../components/select_rule_type/index.tsx | 45 ++++++++++++++++--- .../select_rule_type/translations.ts | 7 --- .../components/step_define_rule/index.tsx | 12 ++++- 3 files changed, 50 insertions(+), 14 deletions(-) diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/select_rule_type/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/select_rule_type/index.tsx index b3b35699914f6..d57993462c0ee 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/select_rule_type/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/select_rule_type/index.tsx @@ -5,18 +5,54 @@ */ import React, { useCallback } from 'react'; -import { EuiCard, EuiFlexGrid, EuiFlexItem, EuiIcon, EuiFormRow } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { + EuiCard, + EuiFlexGrid, + EuiFlexItem, + EuiFormRow, + EuiIcon, + EuiLink, + EuiText, +} from '@elastic/eui'; import { FieldHook } from '../../../../../shared_imports'; import { RuleType } from '../../../../../containers/detection_engine/rules/types'; import * as i18n from './translations'; import { isMlRule } from '../../helpers'; +const MlCardDescription = ({ hasValidLicense = false }: { hasValidLicense: boolean }) => ( + + {hasValidLicense ? ( + i18n.ML_TYPE_DESCRIPTION + ) : ( + + + + ), + }} + /> + )} + +); + interface SelectRuleTypeProps { field: FieldHook; + hasValidLicense: boolean; } -export const SelectRuleType: React.FC = ({ field }) => { +export const SelectRuleType: React.FC = ({ + field, + hasValidLicense = false, +}) => { const ruleType = field.value as RuleType; const setType = useCallback( (type: RuleType) => { @@ -26,7 +62,6 @@ export const SelectRuleType: React.FC = ({ field }) => { ); const setMl = useCallback(() => setType('machine_learning'), [setType]); const setQuery = useCallback(() => setType('query'), [setType]); - const license = true; // TODO return ( @@ -45,10 +80,10 @@ export const SelectRuleType: React.FC = ({ field }) => { } icon={} selectable={{ + isDisabled: !hasValidLicense, onClick: setMl, isSelected: isMlRule(ruleType), }} diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/select_rule_type/translations.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/select_rule_type/translations.ts index 32b860e8f703e..4dc0a89af4a49 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/select_rule_type/translations.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/select_rule_type/translations.ts @@ -33,10 +33,3 @@ export const ML_TYPE_DESCRIPTION = i18n.translate( defaultMessage: 'Select ML job to detect anomalous activity.', } ); - -export const ML_TYPE_DISABLED_DESCRIPTION = i18n.translate( - 'xpack.siem.detectionEngine.createRule.stepDefineRule.ruleTypeField.mlTypeDisabledDescription', - { - defaultMessage: 'Access to ML requires a Platinum subscription.', - } -); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/index.tsx index 6b1a9a828d950..741b5ee299e22 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/index.tsx @@ -13,13 +13,14 @@ import { EuiButton, } from '@elastic/eui'; import { isEmpty } from 'lodash/fp'; -import React, { FC, memo, useCallback, useState, useEffect } from 'react'; +import React, { FC, memo, useCallback, useState, useEffect, useContext } from 'react'; import styled from 'styled-components'; import deepEqual from 'fast-deep-equal'; import { IIndexPattern } from '../../../../../../../../../../src/plugins/data/public'; import { useFetchIndexPatterns } from '../../../../../containers/detection_engine/rules'; import { DEFAULT_INDEX_KEY } from '../../../../../../common/constants'; +import { MlCapabilitiesContext } from '../../../../../components/ml/permissions/ml_capabilities_provider'; import { useUiSetting$ } from '../../../../../lib/kibana'; import { setFieldValue, isMlRule } from '../../helpers'; import * as RuleI18n from '../../translations'; @@ -103,6 +104,7 @@ const StepDefineRuleComponent: FC = ({ setForm, setStepData, }) => { + const mlCapabilities = useContext(MlCapabilitiesContext); const [openTimelineSearch, setOpenTimelineSearch] = useState(false); const [localUseIndicesConfig, setLocalUseIndicesConfig] = useState(false); const [localIsMlRule, setIsMlRule] = useState(false); @@ -178,7 +180,13 @@ const StepDefineRuleComponent: FC = ({ <>
- + <> Date: Thu, 19 Mar 2020 15:40:18 -0500 Subject: [PATCH 2/7] Add aria-describedby for new ML input fields --- .../anomaly_threshold_slider/index.tsx | 8 ++++++-- .../rules/components/ml_job_select/index.tsx | 11 +++++++++-- .../rules/components/select_rule_type/index.tsx | 4 +++- .../rules/components/step_define_rule/index.tsx | 17 +++++++++++++++-- 4 files changed, 33 insertions(+), 7 deletions(-) diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/anomaly_threshold_slider/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/anomaly_threshold_slider/index.tsx index 18970ff935b8d..c35286c2ff0b8 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/anomaly_threshold_slider/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/anomaly_threshold_slider/index.tsx @@ -10,12 +10,16 @@ import { EuiFlexGrid, EuiFlexItem, EuiRange, EuiFormRow } from '@elastic/eui'; import { FieldHook } from '../../../../../shared_imports'; interface AnomalyThresholdSliderProps { + describedByIds: string[]; field: FieldHook; } type Event = React.ChangeEvent; type EventArg = Event | React.MouseEvent; -export const AnomalyThresholdSlider: React.FC = ({ field }) => { +export const AnomalyThresholdSlider: React.FC = ({ + describedByIds = [], + field, +}) => { const threshold = field.value as number; const onThresholdChange = useCallback( (event: EventArg) => { @@ -26,7 +30,7 @@ export const AnomalyThresholdSlider: React.FC = ({ ); return ( - + = ({ field }) => { +export const MlJobSelect: React.FC = ({ describedByIds = [], field }) => { const jobId = field.value as string; const { isInvalid, errorMessage } = getFieldValidityAndErrorMessage(field); const [isLoading, siemJobs] = useSiemJobs(false); @@ -41,7 +42,13 @@ export const MlJobSelect: React.FC = ({ field }) => { })); return ( - + = ({ + describedByIds = [], field, hasValidLicense = false, }) => { @@ -64,7 +66,7 @@ export const SelectRuleType: React.FC = ({ const setQuery = useCallback(() => setType('query'), [setType]); return ( - + = ({ path="ruleType" component={SelectRuleType} componentProps={{ + describedByIds: ['detectionEngineStepDefineRuleType'], hasValidLicense: mlCapabilities.isPlatinumOrTrialLicense, }} /> @@ -236,8 +237,20 @@ const StepDefineRuleComponent: FC = ({ <> - - + + From 42eaba7bbfa8157150fe7cccac6e2013c44500c7 Mon Sep 17 00:00:00 2001 From: Ryland Herrick Date: Thu, 19 Mar 2020 15:53:25 -0500 Subject: [PATCH 3/7] Add data-test-subj to new ML input fields --- .../rules/components/anomaly_threshold_slider/index.tsx | 7 ++++++- .../rules/components/ml_job_select/index.tsx | 1 + .../rules/components/select_rule_type/index.tsx | 7 ++++++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/anomaly_threshold_slider/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/anomaly_threshold_slider/index.tsx index c35286c2ff0b8..1e18023e0c326 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/anomaly_threshold_slider/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/anomaly_threshold_slider/index.tsx @@ -30,7 +30,12 @@ export const AnomalyThresholdSlider: React.FC = ({ ); return ( - + = ({ describedByIds = [], f label={field.label} isInvalid={isInvalid} error={errorMessage} + data-test-subj="mlJobSelect" describedByIds={describedByIds} > diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/select_rule_type/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/select_rule_type/index.tsx index 821a08d4f2ff8..244382f5a8244 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/select_rule_type/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/select_rule_type/index.tsx @@ -66,7 +66,12 @@ export const SelectRuleType: React.FC = ({ const setQuery = useCallback(() => setType('query'), [setType]); return ( - + Date: Thu, 19 Mar 2020 16:04:10 -0500 Subject: [PATCH 4/7] Remove unused prop This is already passed as isLoading --- .../detection_engine/rules/components/step_define_rule/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/index.tsx index 1ed2c4f473370..6fa71a3bcd328 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/index.tsx @@ -223,7 +223,6 @@ const StepDefineRuleComponent: FC = ({ component={QueryBarDefineRule} componentProps={{ browserFields, - loading: indexPatternLoadingQueryBar, idAria: 'detectionEngineStepDefineRuleQueryBar', indexPattern: indexPatternQueryBar, isDisabled: isLoading, From 4621085e7770294436a8282c40c7d47456ef1d9e Mon Sep 17 00:00:00 2001 From: Ryland Herrick Date: Thu, 19 Mar 2020 16:18:45 -0500 Subject: [PATCH 5/7] Fix capitalization on translation id --- .../rules/components/select_rule_type/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/select_rule_type/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/select_rule_type/index.tsx index 244382f5a8244..fb0141fb215ce 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/select_rule_type/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/select_rule_type/index.tsx @@ -33,7 +33,7 @@ const MlCardDescription = ({ hasValidLicense = false }: { hasValidLicense: boole subscriptionsLink: ( From fac289c6f4d5c9c0fdc812d5b42416a4dd58abe0 Mon Sep 17 00:00:00 2001 From: Ryland Herrick Date: Fri, 20 Mar 2020 21:36:32 -0500 Subject: [PATCH 6/7] Declare defaulted props as optional --- .../rules/components/select_rule_type/index.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/select_rule_type/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/select_rule_type/index.tsx index e0597b11604ed..0b140eefd8894 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/select_rule_type/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/select_rule_type/index.tsx @@ -21,7 +21,7 @@ import { RuleType } from '../../../../../containers/detection_engine/rules/types import * as i18n from './translations'; import { isMlRule } from '../../helpers'; -const MlCardDescription = ({ hasValidLicense = false }: { hasValidLicense: boolean }) => ( +const MlCardDescription = ({ hasValidLicense = false }: { hasValidLicense?: boolean }) => ( {hasValidLicense ? ( i18n.ML_TYPE_DESCRIPTION @@ -45,10 +45,10 @@ const MlCardDescription = ({ hasValidLicense = false }: { hasValidLicense: boole ); interface SelectRuleTypeProps { - describedByIds: string[]; + describedByIds?: string[]; field: FieldHook; - hasValidLicense: boolean; - isReadOnly: boolean; + hasValidLicense?: boolean; + isReadOnly?: boolean; } export const SelectRuleType: React.FC = ({ From 1b6b4604354773031bad4a82ca58d0468769d620 Mon Sep 17 00:00:00 2001 From: Ryland Herrick Date: Fri, 20 Mar 2020 21:48:16 -0500 Subject: [PATCH 7/7] Gray out entire ML card when ML Rules are disabled If we're editing an existing rule, or if the user has an insufficient license, we disable both the card and its selectability. This is more visually striking, and a more obvious CTA. --- .../rules/components/select_rule_type/index.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/select_rule_type/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/select_rule_type/index.tsx index 0b140eefd8894..219b3d6dc4d58 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/select_rule_type/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/select_rule_type/index.tsx @@ -66,6 +66,7 @@ export const SelectRuleType: React.FC = ({ ); const setMl = useCallback(() => setType('machine_learning'), [setType]); const setQuery = useCallback(() => setType('query'), [setType]); + const mlCardDisabled = isReadOnly || !hasValidLicense; return ( = ({ title={i18n.ML_TYPE_TITLE} description={} icon={} + isDisabled={mlCardDisabled} selectable={{ - isDisabled: isReadOnly || !hasValidLicense, + isDisabled: mlCardDisabled, onClick: setMl, isSelected: isMlRule(ruleType), }}