Skip to content

Commit

Permalink
[Cloud Security]Rules Flyout update (elastic#175650)
Browse files Browse the repository at this point in the history
## Summary

This PR is to address the Update on Rules Flyout.
Users are now able to enable or disable benchmark rule via the switch or
Options on Take Action button on Rules Flyout

This PR also includes fix for broken Rule Number order on Rules Table
<img width="1625" alt="Screenshot 2024-01-25 at 2 58 16 PM"
src="https://github.com/elastic/kibana/assets/8703149/e4d5749f-2d34-42b3-b99a-373c651c61cb">
  • Loading branch information
animehart authored Jan 29, 2024
1 parent bdb9eab commit f2eaebc
Show file tree
Hide file tree
Showing 10 changed files with 334 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { useFetchDetectionRulesAlertsStatus } from '../common/api/use_fetch_dete
import { useFetchDetectionRulesByTags } from '../common/api/use_fetch_detection_rules_by_tags';
import { RuleResponse } from '../common/types';
import { useKibana } from '../common/hooks/use_kibana';
import { showSuccessToast } from './take_action';
import { showCreateDetectionRuleSuccessToast } from './take_action';
import { DETECTION_ENGINE_ALERTS_KEY, DETECTION_ENGINE_RULES_KEY } from '../common/constants';

const RULES_PAGE_PATH = '/rules/management';
Expand Down Expand Up @@ -61,7 +61,7 @@ export const DetectionRuleCounter = ({ tags, createRuleFn }: DetectionRuleCounte
setIsCreateRuleLoading(true);
const ruleResponse = await createRuleFn(http);
setIsCreateRuleLoading(false);
showSuccessToast(notifications, http, ruleResponse);
showCreateDetectionRuleSuccessToast(notifications, http, ruleResponse);
// Triggering a refetch of rules and alerts to update the UI
queryClient.invalidateQueries([DETECTION_ENGINE_RULES_KEY]);
queryClient.invalidateQueries([DETECTION_ENGINE_ALERTS_KEY]);
Expand Down
222 changes: 194 additions & 28 deletions x-pack/plugins/cloud_security_posture/public/components/take_action.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
import { toMountPoint } from '@kbn/kibana-react-plugin/public';
import type { HttpSetup, NotificationsStart } from '@kbn/core/public';
import { FormattedMessage } from '@kbn/i18n-react';
import { useQueryClient } from '@tanstack/react-query';
import { QueryClient, useQueryClient } from '@tanstack/react-query';
import type { RuleResponse } from '../common/types';
import { CREATE_RULE_ACTION_SUBJ, TAKE_ACTION_SUBJ } from './test_subjects';
import { useKibana } from '../common/hooks/use_kibana';
Expand All @@ -28,10 +28,12 @@ import { DETECTION_ENGINE_ALERTS_KEY, DETECTION_ENGINE_RULES_KEY } from '../comm
const RULE_PAGE_PATH = '/app/security/rules/id/';

interface TakeActionProps {
createRuleFn: (http: HttpSetup) => Promise<RuleResponse>;
createRuleFn?: (http: HttpSetup) => Promise<RuleResponse>;
enableBenchmarkRuleFn?: () => Promise<void>;
disableBenchmarkRuleFn?: () => Promise<void>;
}

export const showSuccessToast = (
export const showCreateDetectionRuleSuccessToast = (
notifications: NotificationsStart,
http: HttpSetup,
ruleResponse: RuleResponse
Expand Down Expand Up @@ -76,11 +78,64 @@ export const showSuccessToast = (
});
};

export const showChangeBenchmarkRuleStatesSuccessToast = (
notifications: NotificationsStart,
isBenchmarkRuleMuted: boolean
) => {
return notifications.toasts.addSuccess({
toastLifeTimeMs: 10000,
color: 'success',
iconType: '',
'data-test-subj': 'csp:toast-success-rule-state-change',
text: toMountPoint(
<div>
<EuiText size="m">
{isBenchmarkRuleMuted ? (
<>
<EuiText size="m">
<strong data-test-subj="csp:toast-success-enable-rule-title">
<FormattedMessage
id="xpack.csp.flyout.ruleEnabledToastTitle"
defaultMessage="Rule Enabled"
/>
</strong>
</EuiText>
<FormattedMessage
id="xpack.csp.flyout.ruleEnabledToast"
defaultMessage="Successfully enabled rule"
/>
</>
) : (
<>
<EuiText size="m">
<strong data-test-subj="csp:toast-success-disable-rule-title">
<FormattedMessage
id="xpack.csp.flyout.ruleDisabledToastTitle"
defaultMessage="Rule Disabled"
/>
</strong>
</EuiText>
<FormattedMessage
id="xpack.csp.flyout.ruleDisabledToast"
defaultMessage="Successfully disabled rule"
/>
</>
)}
</EuiText>
</div>
),
});
};

/*
* This component is used to create a detection rule from Flyout.
* It accepts a createRuleFn parameter which is used to create a rule in a generic way.
*/
export const TakeAction = ({ createRuleFn }: TakeActionProps) => {
export const TakeAction = ({
createRuleFn,
enableBenchmarkRuleFn,
disableBenchmarkRuleFn,
}: TakeActionProps) => {
const queryClient = useQueryClient();
const [isPopoverOpen, setPopoverOpen] = useState(false);
const [isLoading, setIsLoading] = useState(false);
Expand All @@ -105,6 +160,44 @@ export const TakeAction = ({ createRuleFn }: TakeActionProps) => {
<FormattedMessage id="xpack.csp.flyout.takeActionButton" defaultMessage="Take action" />
</EuiButton>
);
const actionsItems = [];

if (createRuleFn)
actionsItems.push(
<CreateDetectionRule
key="createRule"
createRuleFn={createRuleFn}
setIsLoading={setIsLoading}
closePopover={closePopover}
notifications={notifications}
http={http}
queryClient={queryClient}
/>
);
if (enableBenchmarkRuleFn)
actionsItems.push(
<EnableBenchmarkRule
key="enableBenchmarkRule"
enableBenchmarkRuleFn={enableBenchmarkRuleFn}
setIsLoading={setIsLoading}
closePopover={closePopover}
notifications={notifications}
http={http}
queryClient={queryClient}
/>
);
if (disableBenchmarkRuleFn)
actionsItems.push(
<DisableBenchmarkRule
key="disableBenchmarkRule"
disableBenchmarkRuleFn={disableBenchmarkRuleFn}
setIsLoading={setIsLoading}
closePopover={closePopover}
notifications={notifications}
http={http}
queryClient={queryClient}
/>
);

return (
<EuiPopover
Expand All @@ -116,30 +209,103 @@ export const TakeAction = ({ createRuleFn }: TakeActionProps) => {
anchorPosition="downLeft"
data-test-subj={TAKE_ACTION_SUBJ}
>
<EuiContextMenuPanel
size="s"
items={[
<EuiContextMenuItem
key="createRule"
onClick={async () => {
closePopover();
setIsLoading(true);
const ruleResponse = await createRuleFn(http);
setIsLoading(false);
showSuccessToast(notifications, http, ruleResponse);
// Triggering a refetch of rules and alerts to update the UI
queryClient.invalidateQueries([DETECTION_ENGINE_RULES_KEY]);
queryClient.invalidateQueries([DETECTION_ENGINE_ALERTS_KEY]);
}}
data-test-subj={CREATE_RULE_ACTION_SUBJ}
>
<FormattedMessage
defaultMessage="Create a detection rule"
id="xpack.csp.createDetectionRuleButton"
/>
</EuiContextMenuItem>,
]}
/>
<EuiContextMenuPanel size="s" items={actionsItems} />
</EuiPopover>
);
};

const CreateDetectionRule = ({
createRuleFn,
setIsLoading,
closePopover,
notifications,
http,
queryClient,
}: {
createRuleFn: (http: HttpSetup) => Promise<RuleResponse>;
setIsLoading: (isLoading: boolean) => void;
closePopover: () => void;
notifications: NotificationsStart;
http: HttpSetup;
queryClient: QueryClient;
}) => {
return (
<EuiContextMenuItem
key="createRule"
onClick={async () => {
closePopover();
setIsLoading(true);
const ruleResponse = await createRuleFn(http);
setIsLoading(false);
showCreateDetectionRuleSuccessToast(notifications, http, ruleResponse);
// Triggering a refetch of rules and alerts to update the UI
queryClient.invalidateQueries([DETECTION_ENGINE_RULES_KEY]);
queryClient.invalidateQueries([DETECTION_ENGINE_ALERTS_KEY]);
}}
data-test-subj={CREATE_RULE_ACTION_SUBJ}
>
<FormattedMessage
defaultMessage="Create a detection rule"
id="xpack.csp.createDetectionRuleButton"
/>
</EuiContextMenuItem>
);
};

const EnableBenchmarkRule = ({
enableBenchmarkRuleFn,
setIsLoading,
closePopover,
notifications,
}: {
enableBenchmarkRuleFn: () => Promise<void>;
setIsLoading: (isLoading: boolean) => void;
closePopover: () => void;
notifications: NotificationsStart;
http: HttpSetup;
queryClient: QueryClient;
}) => {
return (
<EuiContextMenuItem
key="enableBenchmarkRule"
onClick={async () => {
closePopover();
setIsLoading(true);
await enableBenchmarkRuleFn();
setIsLoading(false);
}}
data-test-subj={'enable-benchmark-rule-take-action-button'}
>
<FormattedMessage defaultMessage="Enable Rule" id="xpack.csp.enableBenchmarkRuleButton" />
</EuiContextMenuItem>
);
};

const DisableBenchmarkRule = ({
disableBenchmarkRuleFn,
setIsLoading,
closePopover,
notifications,
}: {
disableBenchmarkRuleFn: () => Promise<void>;
setIsLoading: (isLoading: boolean) => void;
closePopover: () => void;
notifications: NotificationsStart;
http: HttpSetup;
queryClient: QueryClient;
}) => {
return (
<EuiContextMenuItem
key="disableBenchmarkRule"
onClick={async () => {
closePopover();
setIsLoading(true);
await disableBenchmarkRuleFn();
setIsLoading(false);
}}
data-test-subj={'disable-benchmark-rule-take-action-button'}
>
<FormattedMessage defaultMessage="Disable Rule" id="xpack.csp.disableBenchmarkRuleButton" />
</EuiContextMenuItem>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ const params = {
ruleNumber: undefined,
search: '',
section: undefined,
sortField: 'metadata.benchmark.rule_number',
sortOrder: 'asc',
},
benchmarkId: 'cis_k8s',
benchmarkVersion: '1.0.1',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ export const RulesContainer = () => {
search: '',
page: 0,
perPage: pageSize || 10,
sortField: 'metadata.benchmark.rule_number',
sortOrder: 'asc',
});

const { data, status, error } = useFindCspBenchmarkRule(
Expand All @@ -86,6 +88,8 @@ export const RulesContainer = () => {
search: rulesQuery.search,
page: 1,
perPage: MAX_ITEMS_PER_PAGE,
sortField: 'metadata.benchmark.rule_number',
sortOrder: 'asc',
},
params.benchmarkId,
params.benchmarkVersion
Expand All @@ -96,6 +100,8 @@ export const RulesContainer = () => {
{
page: 1,
perPage: MAX_ITEMS_PER_PAGE,
sortField: 'metadata.benchmark.rule_number',
sortOrder: 'asc',
},
params.benchmarkId,
params.benchmarkVersion
Expand Down
Loading

0 comments on commit f2eaebc

Please sign in to comment.