From e1b8690e5a0a77342dfd0c6565047c24f358b9c2 Mon Sep 17 00:00:00 2001 From: Pablo Neves Machado Date: Thu, 17 Aug 2023 14:33:05 +0200 Subject: [PATCH 1/5] Add RBAC check for new 'Create Rule' button on Rule Group page --- .../public/rules/landing.tsx | 37 +++++++++++++------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/security_solution/public/rules/landing.tsx b/x-pack/plugins/security_solution/public/rules/landing.tsx index aaf28d48d0bd7..61d5b625abeca 100644 --- a/x-pack/plugins/security_solution/public/rules/landing.tsx +++ b/x-pack/plugins/security_solution/public/rules/landing.tsx @@ -18,6 +18,10 @@ import { SecuritySolutionLinkButton } from '../common/components/links'; import { useRootNavLink } from '../common/links/nav_links'; import { useGlobalQueryString } from '../common/utils/global_query_string'; import { trackLandingLinkClick } from '../common/lib/telemetry/trackers'; +import { useReadonlyHeader } from '../use_readonly_header'; +import { useUserData } from '../detections/components/user_info'; +import { READ_ONLY_BADGE_TOOLTIP } from './translations'; +import { hasUserCRUDPermission } from '../common/utils/privileges'; const RULES_PAGE_TITLE = i18n.translate('xpack.securitySolution.rules.landing.pageTitle', { defaultMessage: 'Rules', @@ -27,18 +31,27 @@ const CREATE_RULE_BUTTON = i18n.translate('xpack.securitySolution.rules.landing. defaultMessage: 'Create rule', }); -const RulesLandingHeader: React.FC = () => ( - - - - </EuiFlexItem> - <EuiFlexItem grow={false}> - <SecuritySolutionLinkButton deepLinkId={SecurityPageName.rulesCreate} iconType="plusInCircle"> - {CREATE_RULE_BUTTON} - </SecuritySolutionLinkButton> - </EuiFlexItem> - </EuiFlexGroup> -); +const RulesLandingHeader: React.FC = () => { + const [{ canUserCRUD }] = useUserData(); + useReadonlyHeader(READ_ONLY_BADGE_TOOLTIP); + + return ( + <EuiFlexGroup gutterSize="none" direction="row"> + <EuiFlexItem> + <Title title={RULES_PAGE_TITLE} /> + </EuiFlexItem> + <EuiFlexItem grow={false}> + <SecuritySolutionLinkButton + deepLinkId={SecurityPageName.rulesCreate} + iconType="plusInCircle" + isDisabled={!hasUserCRUDPermission(canUserCRUD)} + > + {CREATE_RULE_BUTTON} + </SecuritySolutionLinkButton> + </EuiFlexItem> + </EuiFlexGroup> + ); +}; export const RulesLandingPage = () => { const { links = [], categories = [] } = useRootNavLink(SecurityPageName.rulesLanding) ?? {}; From 98bef6f4890131c7b79ed527e35511ee4a56ebec Mon Sep 17 00:00:00 2001 From: Pablo Neves Machado <pablo.nevesmachado@elastic.co> Date: Thu, 17 Aug 2023 16:43:33 +0200 Subject: [PATCH 2/5] Add unit test --- .../public/rules/jest.config.js | 17 ++++++++++ .../public/rules/landing.test.tsx | 31 +++++++++++++++++++ .../public/rules/landing.tsx | 5 +-- 3 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/rules/jest.config.js create mode 100644 x-pack/plugins/security_solution/public/rules/landing.test.tsx diff --git a/x-pack/plugins/security_solution/public/rules/jest.config.js b/x-pack/plugins/security_solution/public/rules/jest.config.js new file mode 100644 index 0000000000000..b976316c48555 --- /dev/null +++ b/x-pack/plugins/security_solution/public/rules/jest.config.js @@ -0,0 +1,17 @@ +/* + * 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. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../../..', + roots: ['<rootDir>/x-pack/plugins/security_solution/public/rules'], + coverageDirectory: + '<rootDir>/target/kibana-coverage/jest/x-pack/plugins/security_solution/public/rules', + coverageReporters: ['text', 'html'], + collectCoverageFrom: ['<rootDir>/x-pack/plugins/security_solution/public/rules/**/*.{ts,tsx}'], + moduleNameMapper: require('../../server/__mocks__/module_name_map'), +}; diff --git a/x-pack/plugins/security_solution/public/rules/landing.test.tsx b/x-pack/plugins/security_solution/public/rules/landing.test.tsx new file mode 100644 index 0000000000000..37100e82ac9bb --- /dev/null +++ b/x-pack/plugins/security_solution/public/rules/landing.test.tsx @@ -0,0 +1,31 @@ +/* + * 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 { render } from '@testing-library/react'; +import React from 'react'; +import { TestProviders } from '../common/mock'; +import { RulesLandingHeader } from './landing'; + +const mockUseUserData = jest.fn().mockReturnValue([{ canUserCRUD: true }]); + +jest.mock('../detections/components/user_info', () => ({ + useUserData: () => mockUseUserData(), +})); + +describe('Rules landing page header', () => { + it('renders', () => { + const { queryByTestId } = render(<RulesLandingHeader />, { wrapper: TestProviders }); + expect(queryByTestId(`ruleLandingHeader`)).toBeInTheDocument(); + }); + + it('should disable "create rule" when user has no CRUD access', () => { + mockUseUserData.mockReturnValue([{ canUserCRUD: false }]); + + const { queryByTestId } = render(<RulesLandingHeader />, { wrapper: TestProviders }); + + expect(queryByTestId(`createRuleBtn`)).toHaveAttribute('disabled'); + }); +}); diff --git a/x-pack/plugins/security_solution/public/rules/landing.tsx b/x-pack/plugins/security_solution/public/rules/landing.tsx index 61d5b625abeca..09d19e7c31f28 100644 --- a/x-pack/plugins/security_solution/public/rules/landing.tsx +++ b/x-pack/plugins/security_solution/public/rules/landing.tsx @@ -31,12 +31,12 @@ const CREATE_RULE_BUTTON = i18n.translate('xpack.securitySolution.rules.landing. defaultMessage: 'Create rule', }); -const RulesLandingHeader: React.FC = () => { +export const RulesLandingHeader: React.FC = () => { const [{ canUserCRUD }] = useUserData(); useReadonlyHeader(READ_ONLY_BADGE_TOOLTIP); return ( - <EuiFlexGroup gutterSize="none" direction="row"> + <EuiFlexGroup gutterSize="none" direction="row" data-test-subj="ruleLandingHeader"> <EuiFlexItem> <Title title={RULES_PAGE_TITLE} /> </EuiFlexItem> @@ -45,6 +45,7 @@ const RulesLandingHeader: React.FC = () => { deepLinkId={SecurityPageName.rulesCreate} iconType="plusInCircle" isDisabled={!hasUserCRUDPermission(canUserCRUD)} + data-test-subj="createRuleBtn" > {CREATE_RULE_BUTTON} </SecuritySolutionLinkButton> From 010477e79b07c87c3f41a5bf7ed7fd7274f3082e Mon Sep 17 00:00:00 2001 From: Pablo Neves Machado <pablo.nevesmachado@elastic.co> Date: Mon, 21 Aug 2023 09:36:32 +0200 Subject: [PATCH 3/5] Revert "Add unit test" This reverts commit 98bef6f4890131c7b79ed527e35511ee4a56ebec. --- .../public/rules/jest.config.js | 17 ---------- .../public/rules/landing.test.tsx | 31 ------------------- .../public/rules/landing.tsx | 5 ++- 3 files changed, 2 insertions(+), 51 deletions(-) delete mode 100644 x-pack/plugins/security_solution/public/rules/jest.config.js delete mode 100644 x-pack/plugins/security_solution/public/rules/landing.test.tsx diff --git a/x-pack/plugins/security_solution/public/rules/jest.config.js b/x-pack/plugins/security_solution/public/rules/jest.config.js deleted file mode 100644 index b976316c48555..0000000000000 --- a/x-pack/plugins/security_solution/public/rules/jest.config.js +++ /dev/null @@ -1,17 +0,0 @@ -/* - * 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. - */ - -module.exports = { - preset: '@kbn/test', - rootDir: '../../../../..', - roots: ['<rootDir>/x-pack/plugins/security_solution/public/rules'], - coverageDirectory: - '<rootDir>/target/kibana-coverage/jest/x-pack/plugins/security_solution/public/rules', - coverageReporters: ['text', 'html'], - collectCoverageFrom: ['<rootDir>/x-pack/plugins/security_solution/public/rules/**/*.{ts,tsx}'], - moduleNameMapper: require('../../server/__mocks__/module_name_map'), -}; diff --git a/x-pack/plugins/security_solution/public/rules/landing.test.tsx b/x-pack/plugins/security_solution/public/rules/landing.test.tsx deleted file mode 100644 index 37100e82ac9bb..0000000000000 --- a/x-pack/plugins/security_solution/public/rules/landing.test.tsx +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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 { render } from '@testing-library/react'; -import React from 'react'; -import { TestProviders } from '../common/mock'; -import { RulesLandingHeader } from './landing'; - -const mockUseUserData = jest.fn().mockReturnValue([{ canUserCRUD: true }]); - -jest.mock('../detections/components/user_info', () => ({ - useUserData: () => mockUseUserData(), -})); - -describe('Rules landing page header', () => { - it('renders', () => { - const { queryByTestId } = render(<RulesLandingHeader />, { wrapper: TestProviders }); - expect(queryByTestId(`ruleLandingHeader`)).toBeInTheDocument(); - }); - - it('should disable "create rule" when user has no CRUD access', () => { - mockUseUserData.mockReturnValue([{ canUserCRUD: false }]); - - const { queryByTestId } = render(<RulesLandingHeader />, { wrapper: TestProviders }); - - expect(queryByTestId(`createRuleBtn`)).toHaveAttribute('disabled'); - }); -}); diff --git a/x-pack/plugins/security_solution/public/rules/landing.tsx b/x-pack/plugins/security_solution/public/rules/landing.tsx index 09d19e7c31f28..61d5b625abeca 100644 --- a/x-pack/plugins/security_solution/public/rules/landing.tsx +++ b/x-pack/plugins/security_solution/public/rules/landing.tsx @@ -31,12 +31,12 @@ const CREATE_RULE_BUTTON = i18n.translate('xpack.securitySolution.rules.landing. defaultMessage: 'Create rule', }); -export const RulesLandingHeader: React.FC = () => { +const RulesLandingHeader: React.FC = () => { const [{ canUserCRUD }] = useUserData(); useReadonlyHeader(READ_ONLY_BADGE_TOOLTIP); return ( - <EuiFlexGroup gutterSize="none" direction="row" data-test-subj="ruleLandingHeader"> + <EuiFlexGroup gutterSize="none" direction="row"> <EuiFlexItem> <Title title={RULES_PAGE_TITLE} /> </EuiFlexItem> @@ -45,7 +45,6 @@ export const RulesLandingHeader: React.FC = () => { deepLinkId={SecurityPageName.rulesCreate} iconType="plusInCircle" isDisabled={!hasUserCRUDPermission(canUserCRUD)} - data-test-subj="createRuleBtn" > {CREATE_RULE_BUTTON} </SecuritySolutionLinkButton> From 3a8384b7bfcac6fee4a6ee46c3c216a7f6143a3b Mon Sep 17 00:00:00 2001 From: Pablo Neves Machado <pablo.nevesmachado@elastic.co> Date: Mon, 21 Aug 2023 09:36:55 +0200 Subject: [PATCH 4/5] Revert "Add RBAC check for new 'Create Rule' button on Rule Group page" This reverts commit e1b8690e5a0a77342dfd0c6565047c24f358b9c2. --- .../public/rules/landing.tsx | 37 ++++++------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/x-pack/plugins/security_solution/public/rules/landing.tsx b/x-pack/plugins/security_solution/public/rules/landing.tsx index 61d5b625abeca..aaf28d48d0bd7 100644 --- a/x-pack/plugins/security_solution/public/rules/landing.tsx +++ b/x-pack/plugins/security_solution/public/rules/landing.tsx @@ -18,10 +18,6 @@ import { SecuritySolutionLinkButton } from '../common/components/links'; import { useRootNavLink } from '../common/links/nav_links'; import { useGlobalQueryString } from '../common/utils/global_query_string'; import { trackLandingLinkClick } from '../common/lib/telemetry/trackers'; -import { useReadonlyHeader } from '../use_readonly_header'; -import { useUserData } from '../detections/components/user_info'; -import { READ_ONLY_BADGE_TOOLTIP } from './translations'; -import { hasUserCRUDPermission } from '../common/utils/privileges'; const RULES_PAGE_TITLE = i18n.translate('xpack.securitySolution.rules.landing.pageTitle', { defaultMessage: 'Rules', @@ -31,27 +27,18 @@ const CREATE_RULE_BUTTON = i18n.translate('xpack.securitySolution.rules.landing. defaultMessage: 'Create rule', }); -const RulesLandingHeader: React.FC = () => { - const [{ canUserCRUD }] = useUserData(); - useReadonlyHeader(READ_ONLY_BADGE_TOOLTIP); - - return ( - <EuiFlexGroup gutterSize="none" direction="row"> - <EuiFlexItem> - <Title title={RULES_PAGE_TITLE} /> - </EuiFlexItem> - <EuiFlexItem grow={false}> - <SecuritySolutionLinkButton - deepLinkId={SecurityPageName.rulesCreate} - iconType="plusInCircle" - isDisabled={!hasUserCRUDPermission(canUserCRUD)} - > - {CREATE_RULE_BUTTON} - </SecuritySolutionLinkButton> - </EuiFlexItem> - </EuiFlexGroup> - ); -}; +const RulesLandingHeader: React.FC = () => ( + <EuiFlexGroup gutterSize="none" direction="row"> + <EuiFlexItem> + <Title title={RULES_PAGE_TITLE} /> + </EuiFlexItem> + <EuiFlexItem grow={false}> + <SecuritySolutionLinkButton deepLinkId={SecurityPageName.rulesCreate} iconType="plusInCircle"> + {CREATE_RULE_BUTTON} + </SecuritySolutionLinkButton> + </EuiFlexItem> + </EuiFlexGroup> +); export const RulesLandingPage = () => { const { links = [], categories = [] } = useRootNavLink(SecurityPageName.rulesLanding) ?? {}; From 475330f9fac4047b47c5b5a623221a6a191698d3 Mon Sep 17 00:00:00 2001 From: Pablo Neves Machado <pablo.nevesmachado@elastic.co> Date: Mon, 21 Aug 2023 09:49:15 +0200 Subject: [PATCH 5/5] =?UTF-8?q?Remove=20button=20=C2=AF\=5F(=E3=83=84)=5F/?= =?UTF-8?q?=C2=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../public/rules/landing.tsx | 22 ++----------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/x-pack/plugins/security_solution/public/rules/landing.tsx b/x-pack/plugins/security_solution/public/rules/landing.tsx index aaf28d48d0bd7..e934c917a9d30 100644 --- a/x-pack/plugins/security_solution/public/rules/landing.tsx +++ b/x-pack/plugins/security_solution/public/rules/landing.tsx @@ -7,14 +7,13 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { TrackApplicationView } from '@kbn/usage-collection-plugin/public'; -import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; +import { EuiSpacer } from '@elastic/eui'; import { LandingLinksIconsCategories } from '@kbn/security-solution-navigation/landing_links'; import { SecurityPageName } from '../../common'; import { PluginTemplateWrapper } from '../common/components/plugin_template_wrapper'; import { SecuritySolutionPageWrapper } from '../common/components/page_wrapper'; import { SpyRoute } from '../common/utils/route/spy_routes'; import { Title } from '../common/components/header_page/title'; -import { SecuritySolutionLinkButton } from '../common/components/links'; import { useRootNavLink } from '../common/links/nav_links'; import { useGlobalQueryString } from '../common/utils/global_query_string'; import { trackLandingLinkClick } from '../common/lib/telemetry/trackers'; @@ -23,23 +22,6 @@ const RULES_PAGE_TITLE = i18n.translate('xpack.securitySolution.rules.landing.pa defaultMessage: 'Rules', }); -const CREATE_RULE_BUTTON = i18n.translate('xpack.securitySolution.rules.landing.createRule', { - defaultMessage: 'Create rule', -}); - -const RulesLandingHeader: React.FC = () => ( - <EuiFlexGroup gutterSize="none" direction="row"> - <EuiFlexItem> - <Title title={RULES_PAGE_TITLE} /> - </EuiFlexItem> - <EuiFlexItem grow={false}> - <SecuritySolutionLinkButton deepLinkId={SecurityPageName.rulesCreate} iconType="plusInCircle"> - {CREATE_RULE_BUTTON} - </SecuritySolutionLinkButton> - </EuiFlexItem> - </EuiFlexGroup> -); - export const RulesLandingPage = () => { const { links = [], categories = [] } = useRootNavLink(SecurityPageName.rulesLanding) ?? {}; const urlState = useGlobalQueryString(); @@ -48,7 +30,7 @@ export const RulesLandingPage = () => { <PluginTemplateWrapper> <TrackApplicationView viewId={SecurityPageName.rulesLanding}> <SecuritySolutionPageWrapper> - <RulesLandingHeader /> + <Title title={RULES_PAGE_TITLE} /> <EuiSpacer size="xl" /> <LandingLinksIconsCategories links={links}