Skip to content

Commit

Permalink
[Security Solution][Endpoint][Admin] Refactor policy details protecti…
Browse files Browse the repository at this point in the history
…ons (#94970) (#95234)
  • Loading branch information
parkiino authored Mar 23, 2021
1 parent 3441ee6 commit 4b78a51
Show file tree
Hide file tree
Showing 10 changed files with 488 additions and 632 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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[];
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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';
Original file line number Diff line number Diff line change
@@ -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';
Original file line number Diff line number Diff line change
@@ -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';
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading

0 comments on commit 4b78a51

Please sign in to comment.