[Security Solution][Endpoint][Admin] Refactor policy details protections (#94970)

This commit is contained in:
Candace Park 2021-03-23 15:23:58 -04:00 committed by GitHub
parent 55e513364a
commit 3998a83871
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 488 additions and 632 deletions

View file

@ -120,6 +120,12 @@ export type RansomwareProtectionOSes = KeysByValueCriteria<
{ ransomware: ProtectionFields }
>;
export type PolicyProtection =
| keyof Pick<UIPolicyConfig['windows'], 'malware' | 'ransomware'>
| keyof Pick<UIPolicyConfig['mac'], 'malware'>;
export type MacPolicyProtection = keyof Pick<UIPolicyConfig['mac'], 'malware'>;
export interface GetPolicyListResponse extends GetPackagePoliciesResponse {
items: PolicyData[];
}

View file

@ -0,0 +1,91 @@
/*
* 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, { useCallback, useMemo } from 'react';
import { useDispatch } from 'react-redux';
import { cloneDeep } from 'lodash';
import { htmlIdGenerator, EuiRadio } from '@elastic/eui';
import {
ImmutableArray,
ProtectionModes,
UIPolicyConfig,
} from '../../../../../../../common/endpoint/types';
import { MacPolicyProtection, PolicyProtection } from '../../../types';
import { usePolicyDetailsSelector } from '../../policy_hooks';
import { policyConfig } from '../../../store/policy_details/selectors';
import { AppAction } from '../../../../../../common/store/actions';
import { useLicense } from '../../../../../../common/hooks/use_license';
export const ProtectionRadio = React.memo(
({
protection,
protectionMode,
osList,
label,
}: {
protection: PolicyProtection;
protectionMode: ProtectionModes;
osList: ImmutableArray<Partial<keyof UIPolicyConfig>>;
label: string;
}) => {
const policyDetailsConfig = usePolicyDetailsSelector(policyConfig);
const dispatch = useDispatch<(action: AppAction) => void>();
const radioButtonId = useMemo(() => htmlIdGenerator()(), []);
const selected = policyDetailsConfig && policyDetailsConfig.windows[protection].mode;
const isPlatinumPlus = useLicense().isPlatinumPlus();
const handleRadioChange = useCallback(() => {
if (policyDetailsConfig) {
const newPayload = cloneDeep(policyDetailsConfig);
for (const os of osList) {
if (os === 'windows') {
newPayload[os][protection].mode = protectionMode;
} else if (os === 'mac') {
newPayload[os][protection as MacPolicyProtection].mode = protectionMode;
}
if (isPlatinumPlus) {
if (os === 'windows') {
if (protectionMode === ProtectionModes.prevent) {
newPayload[os].popup[protection].enabled = true;
} else {
newPayload[os].popup[protection].enabled = false;
}
} else if (os === 'mac') {
if (protectionMode === ProtectionModes.prevent) {
newPayload[os].popup[protection as MacPolicyProtection].enabled = true;
} else {
newPayload[os].popup[protection as MacPolicyProtection].enabled = false;
}
}
}
}
dispatch({
type: 'userChangedPolicyConfig',
payload: { policyConfig: newPayload },
});
}
}, [dispatch, protectionMode, policyDetailsConfig, isPlatinumPlus, osList, protection]);
/**
* Passing an arbitrary id because EuiRadio
* requires an id if label is passed
*/
return (
<EuiRadio
className="policyDetailsProtectionRadio"
label={label}
id={radioButtonId}
checked={selected === protectionMode}
onChange={handleRadioChange}
disabled={selected === ProtectionModes.off}
/>
);
}
);
ProtectionRadio.displayName = 'ProtectionRadio';

View file

@ -0,0 +1,100 @@
/*
* 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, { useCallback } from 'react';
import { useDispatch } from 'react-redux';
import { i18n } from '@kbn/i18n';
import { EuiSwitch } from '@elastic/eui';
import { cloneDeep } from 'lodash';
import { useLicense } from '../../../../../../common/hooks/use_license';
import { policyConfig } from '../../../store/policy_details/selectors';
import { usePolicyDetailsSelector } from '../../policy_hooks';
import { AppAction } from '../../../../../../common/store/actions';
import {
ImmutableArray,
ProtectionModes,
UIPolicyConfig,
} from '../../../../../../../common/endpoint/types';
import { PolicyProtection, MacPolicyProtection } from '../../../types';
export const ProtectionSwitch = React.memo(
({
protection,
osList,
}: {
protection: PolicyProtection;
osList: ImmutableArray<Partial<keyof UIPolicyConfig>>;
}) => {
const policyDetailsConfig = usePolicyDetailsSelector(policyConfig);
const isPlatinumPlus = useLicense().isPlatinumPlus();
const dispatch = useDispatch<(action: AppAction) => void>();
const selected = policyDetailsConfig && policyDetailsConfig.windows[protection].mode;
const handleSwitchChange = useCallback(
(event) => {
if (policyDetailsConfig) {
const newPayload = cloneDeep(policyDetailsConfig);
if (event.target.checked === false) {
for (const os of osList) {
if (os === 'windows') {
newPayload[os][protection].mode = ProtectionModes.off;
} else if (os === 'mac') {
newPayload[os][protection as MacPolicyProtection].mode = ProtectionModes.off;
}
if (isPlatinumPlus) {
if (os === 'windows') {
newPayload[os].popup[protection].enabled = event.target.checked;
} else if (os === 'mac') {
newPayload[os].popup[protection as MacPolicyProtection].enabled =
event.target.checked;
}
}
}
} else {
for (const os of osList) {
if (os === 'windows') {
newPayload[os][protection].mode = ProtectionModes.prevent;
} else if (os === 'mac') {
newPayload[os][protection as MacPolicyProtection].mode = ProtectionModes.prevent;
}
if (isPlatinumPlus) {
if (os === 'windows') {
newPayload[os].popup[protection].enabled = event.target.checked;
} else if (os === 'mac') {
newPayload[os].popup[protection as MacPolicyProtection].enabled =
event.target.checked;
}
}
}
}
dispatch({
type: 'userChangedPolicyConfig',
payload: { policyConfig: newPayload },
});
}
},
[dispatch, policyDetailsConfig, isPlatinumPlus, protection, osList]
);
return (
<EuiSwitch
label={i18n.translate('xpack.securitySolution.endpoint.policy.details.protectionsEnabled', {
defaultMessage:
'{protectionName} protections {mode, select, true {enabled} false {disabled}}',
values: {
protectionName: protection.charAt(0).toUpperCase() + protection.substring(1),
mode: selected !== ProtectionModes.off,
},
})}
checked={selected !== ProtectionModes.off}
onChange={handleSwitchChange}
/>
);
}
);
ProtectionSwitch.displayName = 'ProtectionSwitch';

View file

@ -0,0 +1,96 @@
/*
* 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, { useMemo } from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import styled from 'styled-components';
import { EuiSpacer, EuiFlexItem, EuiFlexGroup } from '@elastic/eui';
import {
Immutable,
ImmutableArray,
ProtectionModes,
UIPolicyConfig,
} from '../../../../../../../common/endpoint/types';
import { PolicyProtection } from '../../../types';
import { ConfigFormHeading } from '../../components/config_form';
import { ProtectionRadio } from './protection_radio';
export const RadioFlexGroup = styled(EuiFlexGroup)`
.no-right-margin-radio {
margin-right: 0;
}
.no-horizontal-margin-radio {
margin: ${(props) => props.theme.eui.ruleMargins.marginSmall} 0;
}
`;
export const RadioButtons = React.memo(
({
protection,
osList,
}: {
protection: PolicyProtection;
osList: ImmutableArray<Partial<keyof UIPolicyConfig>>;
}) => {
const radios: Immutable<
Array<{
id: ProtectionModes;
label: string;
}>
> = useMemo(() => {
return [
{
id: ProtectionModes.detect,
label: i18n.translate('xpack.securitySolution.endpoint.policy.details.detect', {
defaultMessage: 'Detect',
}),
},
{
id: ProtectionModes.prevent,
label: i18n.translate('xpack.securitySolution.endpoint.policy.details.prevent', {
defaultMessage: 'Prevent',
}),
},
];
}, []);
return (
<>
<ConfigFormHeading>
<FormattedMessage
id="xpack.securitySolution.endpoint.policyDetailsConfig.protectionLevel"
defaultMessage="Protection Level"
/>
</ConfigFormHeading>
<EuiSpacer size="xs" />
<RadioFlexGroup>
<EuiFlexItem className="no-right-margin-radio" grow={2}>
<ProtectionRadio
protection={protection}
protectionMode={radios[0].id}
osList={osList}
key={{ protection } + radios[0].id}
label={radios[0].label}
/>
</EuiFlexItem>
<EuiFlexItem className="no-horizontal-margin-radio" grow={5}>
<ProtectionRadio
protection={protection}
protectionMode={radios[1].id}
osList={osList}
key={{ protection } + radios[1].id}
label={radios[1].label}
/>
</EuiFlexItem>
</RadioFlexGroup>
</>
);
}
);
RadioButtons.displayName = 'RadioButtons';

View file

@ -8,7 +8,7 @@
import React from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiText } from '@elastic/eui';
import { popupVersionsMap } from './popup_options_to_versions';
import { popupVersionsMap } from '../protections/popup_options_to_versions';
export const SupportedVersionNotice = ({ optionName }: { optionName: string }) => {
const version = popupVersionsMap.get(optionName);

View file

@ -0,0 +1,170 @@
/*
* 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, { useCallback } from 'react';
import { useDispatch } from 'react-redux';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { cloneDeep } from 'lodash';
import {
EuiSpacer,
EuiFlexItem,
EuiFlexGroup,
EuiCheckbox,
EuiIconTip,
EuiText,
EuiTextArea,
} from '@elastic/eui';
import {
ImmutableArray,
ProtectionModes,
UIPolicyConfig,
} from '../../../../../../../common/endpoint/types';
import { PolicyProtection, MacPolicyProtection } from '../../../types';
import { ConfigFormHeading } from '../../components/config_form';
import { usePolicyDetailsSelector } from '../../policy_hooks';
import { policyConfig } from '../../../store/policy_details/selectors';
import { AppAction } from '../../../../../../common/store/actions';
import { SupportedVersionNotice } from './supported_version';
export const UserNotification = React.memo(
({
protection,
osList,
}: {
protection: PolicyProtection;
osList: ImmutableArray<Partial<keyof UIPolicyConfig>>;
}) => {
const policyDetailsConfig = usePolicyDetailsSelector(policyConfig);
const dispatch = useDispatch<(action: AppAction) => void>();
const selected = policyDetailsConfig && policyDetailsConfig.windows[protection].mode;
const userNotificationSelected =
policyDetailsConfig && policyDetailsConfig.windows.popup[protection].enabled;
const userNotificationMessage =
policyDetailsConfig && policyDetailsConfig.windows.popup[protection].message;
const handleUserNotificationCheckbox = useCallback(
(event) => {
if (policyDetailsConfig) {
const newPayload = cloneDeep(policyDetailsConfig);
for (const os of osList) {
if (os === 'windows') {
newPayload[os].popup[protection].enabled = event.target.checked;
} else if (os === 'mac') {
newPayload[os].popup[protection as MacPolicyProtection].enabled =
event.target.checked;
}
}
dispatch({
type: 'userChangedPolicyConfig',
payload: { policyConfig: newPayload },
});
}
},
[policyDetailsConfig, dispatch, protection, osList]
);
const handleCustomUserNotification = useCallback(
(event) => {
if (policyDetailsConfig) {
const newPayload = cloneDeep(policyDetailsConfig);
for (const os of osList) {
if (os === 'windows') {
newPayload[os].popup[protection].message = event.target.value;
} else if (os === 'mac') {
newPayload[os].popup[protection as MacPolicyProtection].message = event.target.value;
}
}
dispatch({
type: 'userChangedPolicyConfig',
payload: { policyConfig: newPayload },
});
}
},
[policyDetailsConfig, dispatch, protection, osList]
);
return (
<>
<EuiSpacer size="m" />
<ConfigFormHeading>
<FormattedMessage
id="xpack.securitySolution.endpoint.policyDetailsConfig.userNotification"
defaultMessage="User Notification"
/>
</ConfigFormHeading>
<SupportedVersionNotice optionName={protection} />
<EuiSpacer size="s" />
<EuiCheckbox
data-test-subj={`${protection}UserNotificationCheckbox`}
id={`${protection}UserNotificationCheckbox}`}
onChange={handleUserNotificationCheckbox}
checked={userNotificationSelected}
disabled={selected === ProtectionModes.off}
label={i18n.translate('xpack.securitySolution.endpoint.policyDetail.notifyUser', {
defaultMessage: 'Notify User',
})}
/>
{userNotificationSelected && (
<>
<EuiSpacer size="s" />
<EuiFlexGroup gutterSize="s">
<EuiFlexItem grow={false}>
<EuiText size="xs">
<h4>
<FormattedMessage
id="xpack.securitySolution.endpoint.policyDetailsConfig.customizeUserNotification"
defaultMessage="Customize notification message"
/>
</h4>
</EuiText>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiIconTip
position="right"
data-test-subj={`${protection}Tooltip`}
content={
<>
<FormattedMessage
id="xpack.securitySolution.endpoint.policyDetailsConfig.notifyUserTooltip.a"
defaultMessage="Selecting the user notification option will display a notification to the host user when { protectionName } is prevented or detected."
values={{
protectionName: protection,
}}
/>
<EuiSpacer size="m" />
<FormattedMessage
id="xpack.securitySolution.endpoint.policyDetailsConfig.notifyUserTooltip.b"
defaultMessage="
The user notification can be customized in the text box below. Bracketed tags can be used to dynamically populate the applicable action (such as prevented or detected) and the filename."
/>
</>
}
/>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="xs" />
<EuiTextArea
placeholder={i18n.translate(
'xpack.securitySolution.endpoint.policyDetails.userNotification.placeholder',
{
defaultMessage: 'Input your custom notification message',
}
)}
value={userNotificationMessage}
onChange={handleCustomUserNotification}
fullWidth={true}
data-test-subj={`${protection}UserNotificationCustomMessage`}
/>
</>
)}
</>
);
}
);
UserNotification.displayName = 'UserNotification';

View file

@ -5,333 +5,29 @@
* 2.0.
*/
import React, { useCallback, useMemo } from 'react';
import { useDispatch } from 'react-redux';
import React from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import {
EuiCallOut,
EuiCheckbox,
EuiRadio,
EuiSpacer,
EuiSwitch,
EuiText,
EuiTextArea,
htmlIdGenerator,
EuiFlexGroup,
EuiFlexItem,
EuiIconTip,
} from '@elastic/eui';
import { cloneDeep } from 'lodash';
import styled from 'styled-components';
import { EuiCallOut, EuiSpacer } from '@elastic/eui';
import { APP_ID } from '../../../../../../../common/constants';
import { SecurityPageName } from '../../../../../../app/types';
import {
Immutable,
OperatingSystem,
ProtectionModes,
} from '../../../../../../../common/endpoint/types';
import { Immutable, OperatingSystem } from '../../../../../../../common/endpoint/types';
import { MalwareProtectionOSes, OS } from '../../../types';
import { ConfigForm, ConfigFormHeading } from '../../components/config_form';
import { policyConfig } from '../../../store/policy_details/selectors';
import { usePolicyDetailsSelector } from '../../policy_hooks';
import { ConfigForm } from '../../components/config_form';
import { LinkToApp } from '../../../../../../common/components/endpoint/link_to_app';
import { useLicense } from '../../../../../../common/hooks/use_license';
import { AppAction } from '../../../../../../common/store/actions';
import { SupportedVersionNotice } from './supported_version';
export const RadioFlexGroup = styled(EuiFlexGroup)`
.no-right-margin-radio {
margin-right: 0;
}
.no-horizontal-margin-radio {
margin: ${(props) => props.theme.eui.ruleMargins.marginSmall} 0;
}
`;
const OSes: Immutable<MalwareProtectionOSes[]> = [OS.windows, OS.mac];
const protection = 'malware';
const ProtectionRadio = React.memo(
({ protectionMode, label }: { protectionMode: ProtectionModes; label: string }) => {
const policyDetailsConfig = usePolicyDetailsSelector(policyConfig);
const dispatch = useDispatch<(action: AppAction) => void>();
const radioButtonId = useMemo(() => htmlIdGenerator()(), []);
// currently just taking windows.malware, but both windows.malware and mac.malware should be the same value
const selected = policyDetailsConfig && policyDetailsConfig.windows.malware.mode;
const isPlatinumPlus = useLicense().isPlatinumPlus();
const handleRadioChange = useCallback(() => {
if (policyDetailsConfig) {
const newPayload = cloneDeep(policyDetailsConfig);
for (const os of OSes) {
newPayload[os][protection].mode = protectionMode;
if (isPlatinumPlus) {
if (protectionMode === ProtectionModes.prevent) {
newPayload[os].popup[protection].enabled = true;
} else {
newPayload[os].popup[protection].enabled = false;
}
}
}
dispatch({
type: 'userChangedPolicyConfig',
payload: { policyConfig: newPayload },
});
}
}, [dispatch, protectionMode, policyDetailsConfig, isPlatinumPlus]);
/**
* Passing an arbitrary id because EuiRadio
* requires an id if label is passed
*/
return (
<EuiRadio
className="policyDetailsProtectionRadio"
label={label}
id={radioButtonId}
checked={selected === protectionMode}
onChange={handleRadioChange}
disabled={selected === ProtectionModes.off}
/>
);
}
);
ProtectionRadio.displayName = 'ProtectionRadio';
import { RadioButtons } from '../components/radio_buttons';
import { UserNotification } from '../components/user_notification';
import { ProtectionSwitch } from '../components/protection_switch';
/** The Malware Protections form for policy details
* which will configure for all relevant OSes.
*/
export const MalwareProtections = React.memo(() => {
const policyDetailsConfig = usePolicyDetailsSelector(policyConfig);
const dispatch = useDispatch<(action: AppAction) => void>();
// currently just taking windows.malware, but both windows.malware and mac.malware should be the same value
const selected = policyDetailsConfig && policyDetailsConfig.windows.malware.mode;
const userNotificationSelected =
policyDetailsConfig && policyDetailsConfig.windows.popup.malware.enabled;
const userNotificationMessage =
policyDetailsConfig && policyDetailsConfig.windows.popup.malware.message;
const OSes: Immutable<MalwareProtectionOSes[]> = [OS.windows, OS.mac];
const protection = 'malware';
const isPlatinumPlus = useLicense().isPlatinumPlus();
const radios: Immutable<
Array<{
id: ProtectionModes;
label: string;
protection: 'malware';
}>
> = useMemo(() => {
return [
{
id: ProtectionModes.detect,
label: i18n.translate('xpack.securitySolution.endpoint.policy.details.detect', {
defaultMessage: 'Detect',
}),
protection: 'malware',
},
{
id: ProtectionModes.prevent,
label: i18n.translate('xpack.securitySolution.endpoint.policy.details.prevent', {
defaultMessage: 'Prevent',
}),
protection: 'malware',
},
];
}, []);
const handleSwitchChange = useCallback(
(event) => {
if (policyDetailsConfig) {
const newPayload = cloneDeep(policyDetailsConfig);
if (event.target.checked === false) {
for (const os of OSes) {
newPayload[os][protection].mode = ProtectionModes.off;
if (isPlatinumPlus) {
newPayload[os].popup[protection].enabled = event.target.checked;
}
}
} else {
for (const os of OSes) {
newPayload[os][protection].mode = ProtectionModes.prevent;
if (isPlatinumPlus) {
newPayload[os].popup[protection].enabled = event.target.checked;
}
}
}
dispatch({
type: 'userChangedPolicyConfig',
payload: { policyConfig: newPayload },
});
}
},
[dispatch, policyDetailsConfig, isPlatinumPlus]
);
const handleUserNotificationCheckbox = useCallback(
(event) => {
if (policyDetailsConfig) {
const newPayload = cloneDeep(policyDetailsConfig);
for (const os of OSes) {
newPayload[os].popup[protection].enabled = event.target.checked;
}
dispatch({
type: 'userChangedPolicyConfig',
payload: { policyConfig: newPayload },
});
}
},
[policyDetailsConfig, dispatch]
);
const handleCustomUserNotification = useCallback(
(event) => {
if (policyDetailsConfig) {
const newPayload = cloneDeep(policyDetailsConfig);
for (const os of OSes) {
newPayload[os].popup[protection].message = event.target.value;
}
dispatch({
type: 'userChangedPolicyConfig',
payload: { policyConfig: newPayload },
});
}
},
[policyDetailsConfig, dispatch]
);
const radioButtons = useMemo(() => {
return (
<>
<ConfigFormHeading>
<FormattedMessage
id="xpack.securitySolution.endpoint.policyDetailsConfig.protectionLevel"
defaultMessage="Protection Level"
/>
</ConfigFormHeading>
<EuiSpacer size="xs" />
<RadioFlexGroup>
<EuiFlexItem className="no-right-margin-radio" grow={2}>
<ProtectionRadio
protectionMode={radios[0].id}
key={radios[0].protection + radios[0].id}
label={radios[0].label}
/>
</EuiFlexItem>
<EuiFlexItem className="no-horizontal-margin-radio" grow={5}>
<ProtectionRadio
protectionMode={radios[1].id}
key={radios[1].protection + radios[1].id}
label={radios[1].label}
/>
</EuiFlexItem>
</RadioFlexGroup>
{isPlatinumPlus && (
<>
<EuiSpacer size="m" />
<ConfigFormHeading>
<FormattedMessage
id="xpack.securitySolution.endpoint.policyDetailsConfig.userNotification"
defaultMessage="User Notification"
/>
</ConfigFormHeading>
<SupportedVersionNotice optionName="malware" />
<EuiSpacer size="s" />
<EuiCheckbox
data-test-subj="malwareUserNotificationCheckbox"
id="xpack.securitySolution.endpoint.policyDetail.malware.userNotification"
onChange={handleUserNotificationCheckbox}
checked={userNotificationSelected}
disabled={selected === ProtectionModes.off}
label={i18n.translate(
'xpack.securitySolution.endpoint.policyDetail.malware.notifyUser',
{
defaultMessage: 'Notify User',
}
)}
/>
</>
)}
{isPlatinumPlus && userNotificationSelected && (
<>
<EuiSpacer size="s" />
<EuiFlexGroup gutterSize="s">
<EuiFlexItem grow={false}>
<EuiText size="xs">
<h4>
<FormattedMessage
id="xpack.securitySolution.endpoint.policyDetailsConfig.customizeUserNotification"
defaultMessage="Customize notification message"
/>
</h4>
</EuiText>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiIconTip
position="right"
data-test-subj="malwareTooltip"
content={
<>
<FormattedMessage
id="xpack.securitySolution.endpoint.policyDetailsConfig.malware.notifyUserTooltip.a"
defaultMessage="Selecting the user notification option will display a notification to the host user when malware is prevented or detected."
/>
<EuiSpacer size="m" />
<FormattedMessage
id="xpack.securitySolution.endpoint.policyDetailsConfig.malware.notifyUserTooltip.b"
defaultMessage="
The user notification can be customized in the text box below. Bracketed tags can be used to dynamically populate the applicable action (such as prevented or detected) and the filename."
/>
</>
}
/>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="xs" />
<EuiTextArea
placeholder={i18n.translate(
'xpack.securitySolution.endpoint.policyDetails.malware.userNotification.placeholder',
{
defaultMessage: 'Input your custom notification message',
}
)}
value={userNotificationMessage}
onChange={handleCustomUserNotification}
fullWidth={true}
data-test-subj="malwareUserNotificationCustomMessage"
/>
</>
)}
</>
);
}, [
radios,
selected,
isPlatinumPlus,
handleUserNotificationCheckbox,
userNotificationSelected,
userNotificationMessage,
handleCustomUserNotification,
]);
const protectionSwitch = useMemo(() => {
return (
<EuiSwitch
label={i18n.translate(
'xpack.securitySolution.endpoint.policy.details.malwareProtectionsEnabled',
{
defaultMessage: 'Malware protections {mode, select, true {enabled} false {disabled}}',
values: {
mode: selected !== ProtectionModes.off,
},
}
)}
checked={selected !== ProtectionModes.off}
onChange={handleSwitchChange}
/>
);
}, [handleSwitchChange, selected]);
return (
<ConfigForm
type={i18n.translate('xpack.securitySolution.endpoint.policy.details.malware', {
@ -339,9 +35,10 @@ export const MalwareProtections = React.memo(() => {
})}
supportedOss={[OperatingSystem.WINDOWS, OperatingSystem.MAC]}
dataTestSubj="malwareProtectionsForm"
rightCorner={protectionSwitch}
rightCorner={<ProtectionSwitch protection={protection} osList={OSes} />}
>
{radioButtons}
<RadioButtons protection={protection} osList={OSes} />
{isPlatinumPlus && <UserNotification protection={protection} osList={OSes} />}
<EuiSpacer size="m" />
<EuiCallOut iconType="iInCircle">
<FormattedMessage

View file

@ -5,311 +5,26 @@
* 2.0.
*/
import React, { useCallback, useMemo } from 'react';
import { useDispatch } from 'react-redux';
import React from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import {
EuiCallOut,
EuiCheckbox,
EuiRadio,
EuiSpacer,
EuiSwitch,
EuiText,
EuiTextArea,
htmlIdGenerator,
EuiFlexGroup,
EuiFlexItem,
EuiIconTip,
EuiCheckboxProps,
EuiRadioProps,
EuiSwitchProps,
} from '@elastic/eui';
import { cloneDeep } from 'lodash';
import { EuiCallOut, EuiSpacer } from '@elastic/eui';
import { APP_ID } from '../../../../../../../common/constants';
import { SecurityPageName } from '../../../../../../app/types';
import {
Immutable,
OperatingSystem,
ProtectionModes,
} from '../../../../../../../common/endpoint/types';
import { Immutable, OperatingSystem } from '../../../../../../../common/endpoint/types';
import { RansomwareProtectionOSes, OS } from '../../../types';
import { ConfigForm, ConfigFormHeading } from '../../components/config_form';
import { policyConfig } from '../../../store/policy_details/selectors';
import { usePolicyDetailsSelector } from '../../policy_hooks';
import { ConfigForm } from '../../components/config_form';
import { LinkToApp } from '../../../../../../common/components/endpoint/link_to_app';
import { AppAction } from '../../../../../../common/store/actions';
import { SupportedVersionNotice } from './supported_version';
import { RadioFlexGroup } from './malware';
const OSes: Immutable<RansomwareProtectionOSes[]> = [OS.windows];
const protection = 'ransomware';
const ProtectionRadio = React.memo(
({ protectionMode, label }: { protectionMode: ProtectionModes; label: string }) => {
const policyDetailsConfig = usePolicyDetailsSelector(policyConfig);
const dispatch = useDispatch<(action: AppAction) => void>();
const radioButtonId = useMemo(() => htmlIdGenerator()(), []);
const selected = policyDetailsConfig && policyDetailsConfig.windows.ransomware.mode;
const handleRadioChange: EuiRadioProps['onChange'] = useCallback(() => {
if (policyDetailsConfig) {
const newPayload = cloneDeep(policyDetailsConfig);
for (const os of OSes) {
newPayload[os][protection].mode = protectionMode;
if (protectionMode === ProtectionModes.prevent) {
newPayload[os].popup[protection].enabled = true;
} else {
newPayload[os].popup[protection].enabled = false;
}
}
dispatch({
type: 'userChangedPolicyConfig',
payload: { policyConfig: newPayload },
});
}
}, [dispatch, protectionMode, policyDetailsConfig]);
/**
* Passing an arbitrary id because EuiRadio
* requires an id if label is passed
*/
return (
<EuiRadio
className="policyDetailsProtectionRadio"
label={label}
id={radioButtonId}
checked={selected === protectionMode}
onChange={handleRadioChange}
disabled={selected === ProtectionModes.off}
/>
);
}
);
ProtectionRadio.displayName = 'ProtectionRadio';
import { RadioButtons } from '../components/radio_buttons';
import { UserNotification } from '../components/user_notification';
import { ProtectionSwitch } from '../components/protection_switch';
/** The Ransomware Protections form for policy details
* which will configure for all relevant OSes.
*/
export const Ransomware = React.memo(() => {
const policyDetailsConfig = usePolicyDetailsSelector(policyConfig);
const dispatch = useDispatch<(action: AppAction) => void>();
const selected = policyDetailsConfig && policyDetailsConfig.windows.ransomware.mode;
const userNotificationSelected =
policyDetailsConfig && policyDetailsConfig.windows.popup.ransomware.enabled;
const userNotificationMessage =
policyDetailsConfig && policyDetailsConfig.windows.popup.ransomware.message;
const radios: Immutable<
Array<{
id: ProtectionModes;
label: string;
protection: 'ransomware';
}>
> = useMemo(() => {
return [
{
id: ProtectionModes.detect,
label: i18n.translate('xpack.securitySolution.endpoint.policy.details.detect', {
defaultMessage: 'Detect',
}),
protection: 'ransomware',
},
{
id: ProtectionModes.prevent,
label: i18n.translate('xpack.securitySolution.endpoint.policy.details.prevent', {
defaultMessage: 'Prevent',
}),
protection: 'ransomware',
},
];
}, []);
const handleSwitchChange: EuiSwitchProps['onChange'] = useCallback(
(event) => {
if (policyDetailsConfig) {
const newPayload = cloneDeep(policyDetailsConfig);
if (event.target.checked === false) {
for (const os of OSes) {
newPayload[os][protection].mode = ProtectionModes.off;
newPayload[os].popup[protection].enabled = event.target.checked;
}
} else {
for (const os of OSes) {
newPayload[os][protection].mode = ProtectionModes.prevent;
newPayload[os].popup[protection].enabled = event.target.checked;
}
}
dispatch({
type: 'userChangedPolicyConfig',
payload: { policyConfig: newPayload },
});
}
},
[dispatch, policyDetailsConfig]
);
const handleUserNotificationCheckbox: EuiCheckboxProps['onChange'] = useCallback(
(event) => {
if (policyDetailsConfig) {
const newPayload = cloneDeep(policyDetailsConfig);
for (const os of OSes) {
newPayload[os].popup[protection].enabled = event.target.checked;
}
dispatch({
type: 'userChangedPolicyConfig',
payload: { policyConfig: newPayload },
});
}
},
[policyDetailsConfig, dispatch]
);
const handleCustomUserNotification = useCallback(
(event) => {
if (policyDetailsConfig) {
const newPayload = cloneDeep(policyDetailsConfig);
for (const os of OSes) {
newPayload[os].popup[protection].message = event.target.value;
}
dispatch({
type: 'userChangedPolicyConfig',
payload: { policyConfig: newPayload },
});
}
},
[policyDetailsConfig, dispatch]
);
const radioButtons = useMemo(() => {
return (
<>
<ConfigFormHeading>
<FormattedMessage
id="xpack.securitySolution.endpoint.policyDetailsConfig.protectionLevel"
defaultMessage="Protection Level"
/>
</ConfigFormHeading>
<EuiSpacer size="xs" />
<RadioFlexGroup>
<EuiFlexItem className="no-right-margin-radio" grow={2}>
<ProtectionRadio
protectionMode={radios[0].id}
key={radios[0].protection + radios[0].id}
label={radios[0].label}
/>
</EuiFlexItem>
<EuiFlexItem className="no-horizontal-margin-radio" grow={5}>
<ProtectionRadio
protectionMode={radios[1].id}
key={radios[1].protection + radios[1].id}
label={radios[1].label}
/>
</EuiFlexItem>
</RadioFlexGroup>
<EuiSpacer size="m" />
<ConfigFormHeading>
<FormattedMessage
id="xpack.securitySolution.endpoint.policyDetailsConfig.userNotification"
defaultMessage="User Notification"
/>
</ConfigFormHeading>
<SupportedVersionNotice optionName="ransomware" />
<EuiSpacer size="s" />
<EuiCheckbox
data-test-subj="ransomwareUserNotificationCheckbox"
id="xpack.securitySolution.endpoint.policyDetail.ransomware.userNotification"
onChange={handleUserNotificationCheckbox}
checked={userNotificationSelected}
disabled={selected === ProtectionModes.off}
label={i18n.translate(
'xpack.securitySolution.endpoint.policyDetail.ransomware.notifyUser',
{
defaultMessage: 'Notify User',
}
)}
/>
{userNotificationSelected && (
<>
<EuiSpacer size="s" />
<EuiFlexGroup gutterSize="s">
<EuiFlexItem grow={false}>
<EuiText size="xs">
<h4>
<FormattedMessage
id="xpack.securitySolution.endpoint.policyDetailsConfig.customizeUserNotification"
defaultMessage="Customize notification message"
/>
</h4>
</EuiText>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiIconTip
position="right"
data-test-subj="ransomwareTooltip"
content={
<>
<FormattedMessage
id="xpack.securitySolution.endpoint.policyDetailsConfig.ransomware.notifyUserTooltip.a"
defaultMessage="Selecting the user notification option will display a notification to the host user when ransomware is prevented or detected."
/>
<EuiSpacer size="m" />
<FormattedMessage
id="xpack.securitySolution.endpoint.policyDetailsConfig.ransomware.notifyUserTooltip.b"
defaultMessage="
The user notification can be customized in the text box below. Bracketed tags can be used to dynamically populate the applicable action (such as prevented or detected) and the filename."
/>
</>
}
/>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="xs" />
<EuiTextArea
placeholder={i18n.translate(
'xpack.securitySolution.endpoint.policyDetails.ransomware.userNotification.placeholder',
{
defaultMessage: 'Input your custom notification message',
}
)}
value={userNotificationMessage}
onChange={handleCustomUserNotification}
fullWidth={true}
data-test-subj="ransomwareUserNotificationCustomMessage"
/>
</>
)}
</>
);
}, [
radios,
selected,
handleUserNotificationCheckbox,
userNotificationSelected,
userNotificationMessage,
handleCustomUserNotification,
]);
const protectionSwitch = useMemo(() => {
return (
<EuiSwitch
label={i18n.translate(
'xpack.securitySolution.endpoint.policy.details.ransomwareProtectionsEnabled',
{
defaultMessage:
'Ransomware protections {mode, select, true {enabled} false {disabled}}',
values: {
mode: selected !== ProtectionModes.off,
},
}
)}
checked={selected !== ProtectionModes.off}
onChange={handleSwitchChange}
/>
);
}, [handleSwitchChange, selected]);
const OSes: Immutable<RansomwareProtectionOSes[]> = [OS.windows];
const protection = 'ransomware';
return (
<ConfigForm
@ -318,9 +33,10 @@ export const Ransomware = React.memo(() => {
})}
supportedOss={[OperatingSystem.WINDOWS]}
dataTestSubj="ransomwareProtectionsForm"
rightCorner={protectionSwitch}
rightCorner={<ProtectionSwitch protection={protection} osList={OSes} />}
>
{radioButtons}
<RadioButtons protection={protection} osList={OSes} />
<UserNotification protection={protection} osList={OSes} />
<EuiSpacer size="m" />
<EuiCallOut iconType="iInCircle">
<FormattedMessage

View file

@ -19609,12 +19609,10 @@
"xpack.securitySolution.endpoint.policy.details.eventCollectionsEnabled": "{selected} / {total} 件のイベント収集が有効です",
"xpack.securitySolution.endpoint.policy.details.lockedCard": "ランサムウェア保護をオンにするには、ライセンスをプラチナに更新するか、30日間の無料トライアルを開始するか、AWS、GCP、またはAzureで{cloudDeploymentLink}にサインアップしてください。",
"xpack.securitySolution.endpoint.policy.details.malware": "マルウェア",
"xpack.securitySolution.endpoint.policy.details.malwareProtectionsEnabled": "マルウェア保護{mode, select, true {有効} false {無効}}",
"xpack.securitySolution.endpoint.policy.details.platinum": "プラチナ",
"xpack.securitySolution.endpoint.policy.details.prevent": "防御",
"xpack.securitySolution.endpoint.policy.details.protections": "保護",
"xpack.securitySolution.endpoint.policy.details.ransomware": "ランサムウェア",
"xpack.securitySolution.endpoint.policy.details.ransomwareProtectionsEnabled": "ランサムウェア保護{mode, select, true {有効} false {無効}}",
"xpack.securitySolution.endpoint.policy.details.save": "保存",
"xpack.securitySolution.endpoint.policy.details.settings": "設定",
"xpack.securitySolution.endpoint.policy.details.updateConfirm.cancelButtonTitle": "キャンセル",
@ -19626,15 +19624,11 @@
"xpack.securitySolution.endpoint.policy.details.updateSuccessMessage": "統合{name}が更新されました。",
"xpack.securitySolution.endpoint.policy.details.updateSuccessTitle": "成功!",
"xpack.securitySolution.endpoint.policy.details.upgradeToPlatinum": "Elastic Platinum へのアップグレード",
"xpack.securitySolution.endpoint.policyDetail.malware.notifyUser": "ユーザーに通知",
"xpack.securitySolution.endpoint.policyDetail.ransomware.notifyUser": "ユーザーに通知",
"xpack.securitySolution.endpoint.policyDetailOS": "オペレーティングシステム",
"xpack.securitySolution.endpoint.policyDetails.agentsSummary.errorTitle": "エラー",
"xpack.securitySolution.endpoint.policyDetails.agentsSummary.offlineTitle": "オフライン",
"xpack.securitySolution.endpoint.policyDetails.agentsSummary.onlineTitle": "オンライン",
"xpack.securitySolution.endpoint.policyDetails.agentsSummary.totalTitle": "エンドポイント",
"xpack.securitySolution.endpoint.policyDetails.malware.userNotification.placeholder": "カスタム通知メッセージを入力",
"xpack.securitySolution.endpoint.policyDetails.ransomware.userNotification.placeholder": "カスタム通知メッセージを入力",
"xpack.securitySolution.endpoint.policyDetails.supportedVersion": "エージェントバージョン {version}",
"xpack.securitySolution.endpoint.policyDetailsConfig.customizeUserNotification": "通知メッセージをカスタマイズ",
"xpack.securitySolution.endpoint.policyDetailsConfig.eventingEvents": "イベント",
@ -19644,11 +19638,7 @@
"xpack.securitySolution.endpoint.policyDetailsConfig.mac.events.file": "ファイル",
"xpack.securitySolution.endpoint.policyDetailsConfig.mac.events.network": "ネットワーク",
"xpack.securitySolution.endpoint.policyDetailsConfig.mac.events.process": "プロセス",
"xpack.securitySolution.endpoint.policyDetailsConfig.malware.notifyUserTooltip.a": "ユーザー通知オプションを選択すると、マルウェアが防御または検出されたときに、ホストユーザーに通知を表示します。",
"xpack.securitySolution.endpoint.policyDetailsConfig.malware.notifyUserTooltip.b": " ユーザー通知は、以下のテキストボックスでカスタマイズできます。括弧内のタグを使用すると、該当するアクション (防御または検出など) とファイル名を動的に入力できます。",
"xpack.securitySolution.endpoint.policyDetailsConfig.protectionLevel": "保護レベル",
"xpack.securitySolution.endpoint.policyDetailsConfig.ransomware.notifyUserTooltip.a": "ユーザー通知オプションを選択すると、ランサムウェアが防御または検出されたときに、ホストユーザーに通知を表示します。",
"xpack.securitySolution.endpoint.policyDetailsConfig.ransomware.notifyUserTooltip.b": " ユーザー通知は、以下のテキストボックスでカスタマイズできます。括弧内のタグを使用すると、該当するアクション (防御または検出など) とファイル名を動的に入力できます。",
"xpack.securitySolution.endpoint.policyDetailsConfig.userNotification": "ユーザー通知",
"xpack.securitySolution.endpoint.policyDetailsConfig.windows.events.dllDriverLoad": "DLL とドライバーの読み込み",
"xpack.securitySolution.endpoint.policyDetailsConfig.windows.events.dns": "DNS",

View file

@ -19900,12 +19900,10 @@
"xpack.securitySolution.endpoint.policy.details.eventCollectionsEnabled": "{selected} / {total} 个事件收集已启用",
"xpack.securitySolution.endpoint.policy.details.lockedCard": "要打开勒索软件防护,必须将您的许可证升级到白金级、开始 30 天免费试用或在 AWS、GCP 或 Azure 中实施{cloudDeploymentLink}。",
"xpack.securitySolution.endpoint.policy.details.malware": "恶意软件",
"xpack.securitySolution.endpoint.policy.details.malwareProtectionsEnabled": "恶意软件防护{mode, select, true {已启用} false {已禁用}}",
"xpack.securitySolution.endpoint.policy.details.platinum": "白金级",
"xpack.securitySolution.endpoint.policy.details.prevent": "防御",
"xpack.securitySolution.endpoint.policy.details.protections": "防护",
"xpack.securitySolution.endpoint.policy.details.ransomware": "勒索软件",
"xpack.securitySolution.endpoint.policy.details.ransomwareProtectionsEnabled": "勒索软件防护{mode, select, true {已启用} false {已禁用}}",
"xpack.securitySolution.endpoint.policy.details.save": "保存",
"xpack.securitySolution.endpoint.policy.details.settings": "设置",
"xpack.securitySolution.endpoint.policy.details.updateConfirm.cancelButtonTitle": "取消",
@ -19918,15 +19916,11 @@
"xpack.securitySolution.endpoint.policy.details.updateSuccessMessage": "集成 {name} 已更新。",
"xpack.securitySolution.endpoint.policy.details.updateSuccessTitle": "成功!",
"xpack.securitySolution.endpoint.policy.details.upgradeToPlatinum": "升级到 Elastic 白金级",
"xpack.securitySolution.endpoint.policyDetail.malware.notifyUser": "通知用户",
"xpack.securitySolution.endpoint.policyDetail.ransomware.notifyUser": "通知用户",
"xpack.securitySolution.endpoint.policyDetailOS": "操作系统",
"xpack.securitySolution.endpoint.policyDetails.agentsSummary.errorTitle": "错误",
"xpack.securitySolution.endpoint.policyDetails.agentsSummary.offlineTitle": "脱机",
"xpack.securitySolution.endpoint.policyDetails.agentsSummary.onlineTitle": "联机",
"xpack.securitySolution.endpoint.policyDetails.agentsSummary.totalTitle": "终端",
"xpack.securitySolution.endpoint.policyDetails.malware.userNotification.placeholder": "输入您的定制通知消息",
"xpack.securitySolution.endpoint.policyDetails.ransomware.userNotification.placeholder": "输入您的定制通知消息",
"xpack.securitySolution.endpoint.policyDetails.supportedVersion": "代理版本 {version}",
"xpack.securitySolution.endpoint.policyDetailsConfig.customizeUserNotification": "定制通知消息",
"xpack.securitySolution.endpoint.policyDetailsConfig.eventingEvents": "事件",
@ -19936,11 +19930,7 @@
"xpack.securitySolution.endpoint.policyDetailsConfig.mac.events.file": "文件",
"xpack.securitySolution.endpoint.policyDetailsConfig.mac.events.network": "网络",
"xpack.securitySolution.endpoint.policyDetailsConfig.mac.events.process": "进程",
"xpack.securitySolution.endpoint.policyDetailsConfig.malware.notifyUserTooltip.a": "选择用户通知选项后,在阻止或检测到恶意软件时将向主机用户显示通知。",
"xpack.securitySolution.endpoint.policyDetailsConfig.malware.notifyUserTooltip.b": " 可在下方文本框中定制用户通知。括号中的标签可用于动态填充适用操作 (如已阻止或已检测) 和文件名。",
"xpack.securitySolution.endpoint.policyDetailsConfig.protectionLevel": "防护级别",
"xpack.securitySolution.endpoint.policyDetailsConfig.ransomware.notifyUserTooltip.a": "选择用户通知选项后,在阻止或检测到勒索软件时将向主机用户显示通知。",
"xpack.securitySolution.endpoint.policyDetailsConfig.ransomware.notifyUserTooltip.b": " 可在下方文本框中定制用户通知。括号中的标签可用于动态填充适用操作 (如已阻止或已检测) 和文件名。",
"xpack.securitySolution.endpoint.policyDetailsConfig.userNotification": "用户通知",
"xpack.securitySolution.endpoint.policyDetailsConfig.windows.events.dllDriverLoad": "DLL 和驱动程序加载",
"xpack.securitySolution.endpoint.policyDetailsConfig.windows.events.dns": "DNS",