From 6e0bd30c5b02b473aa62dd95f96d305c4dc63b38 Mon Sep 17 00:00:00 2001 From: Yara Tercero Date: Mon, 30 Aug 2021 13:36:44 -0700 Subject: [PATCH 1/4] updating tests for use_alerts_privileges --- .../alerts/use_alerts_privileges.test.tsx | 74 +++++++++++++++++-- .../alerts/use_alerts_privileges.tsx | 4 +- 2 files changed, 71 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_alerts_privileges.test.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_alerts_privileges.test.tsx index 381c47fa75673..e6ee954c426c0 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_alerts_privileges.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_alerts_privileges.test.tsx @@ -90,7 +90,7 @@ const userPrivilegesInitial: ReturnType = { kibanaSecuritySolutionsPrivileges: { crud: true, read: true }, }; -describe('usePrivilegeUser', () => { +describe('useAlertsPrivileges', () => { let appToastsMock: jest.Mocked>; beforeEach(() => { @@ -113,6 +113,8 @@ describe('usePrivilegeUser', () => { hasIndexMaintenance: null, hasIndexWrite: null, hasIndexUpdateDelete: null, + hasKibanaCRUD: false, + hasKibanaREAD: false, isAuthenticated: null, loading: false, }); @@ -137,6 +139,8 @@ describe('usePrivilegeUser', () => { hasIndexRead: false, hasIndexWrite: false, hasIndexUpdateDelete: false, + hasKibanaCRUD: false, + hasKibanaREAD: false, isAuthenticated: false, loading: false, }); @@ -162,9 +166,11 @@ describe('usePrivilegeUser', () => { hasEncryptionKey: true, hasIndexManage: false, hasIndexMaintenance: true, - hasIndexRead: false, - hasIndexWrite: false, + hasIndexRead: true, + hasIndexWrite: true, hasIndexUpdateDelete: true, + hasKibanaCRUD: true, + hasKibanaREAD: true, isAuthenticated: true, loading: false, }); @@ -187,9 +193,67 @@ describe('usePrivilegeUser', () => { hasEncryptionKey: true, hasIndexManage: true, hasIndexMaintenance: true, - hasIndexRead: false, - hasIndexWrite: false, + hasIndexRead: true, + hasIndexWrite: true, + hasIndexUpdateDelete: true, + hasKibanaCRUD: true, + hasKibanaREAD: true, + isAuthenticated: true, + loading: false, + }); + }); + }); + + test('returns "hasKibanaCRUD" as false if user does not have SIEM Kibana "all" privileges', async () => { + const userPrivileges = produce(userPrivilegesInitial, (draft) => { + draft.detectionEnginePrivileges.result = privilege; + draft.kibanaSecuritySolutionsPrivileges = { crud: false, read: true }; + }); + useUserPrivilegesMock.mockReturnValue(userPrivileges); + + await act(async () => { + const { result, waitForNextUpdate } = renderHook(() => + useAlertsPrivileges() + ); + await waitForNextUpdate(); + await waitForNextUpdate(); + expect(result.current).toEqual({ + hasEncryptionKey: true, + hasIndexManage: true, + hasIndexMaintenance: true, + hasIndexRead: true, + hasIndexWrite: true, + hasIndexUpdateDelete: true, + hasKibanaCRUD: false, + hasKibanaREAD: true, + isAuthenticated: true, + loading: false, + }); + }); + }); + + test('returns "hasKibanaREAD" as false if user does not have at least SIEM Kibana "read" privileges', async () => { + const userPrivileges = produce(userPrivilegesInitial, (draft) => { + draft.detectionEnginePrivileges.result = privilege; + draft.kibanaSecuritySolutionsPrivileges = { crud: false, read: false }; + }); + useUserPrivilegesMock.mockReturnValue(userPrivileges); + + await act(async () => { + const { result, waitForNextUpdate } = renderHook(() => + useAlertsPrivileges() + ); + await waitForNextUpdate(); + await waitForNextUpdate(); + expect(result.current).toEqual({ + hasEncryptionKey: true, + hasIndexManage: true, + hasIndexMaintenance: true, + hasIndexRead: true, + hasIndexWrite: true, hasIndexUpdateDelete: true, + hasKibanaCRUD: false, + hasKibanaREAD: false, isAuthenticated: true, loading: false, }); diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_alerts_privileges.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_alerts_privileges.tsx index b377eda49d0cd..e0d4cd75bb0a3 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_alerts_privileges.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_alerts_privileges.tsx @@ -54,8 +54,8 @@ export const useAlertsPrivileges = (): UseAlertsPrivelegesReturn => { hasIndexWrite: false, hasIndexUpdateDelete: false, hasIndexMaintenance: false, - hasKibanaCRUD, - hasKibanaREAD, + hasKibanaCRUD: false, + hasKibanaREAD: false, }); } }, [detectionEnginePrivileges.error, hasKibanaCRUD, hasKibanaREAD]); From c677b6612624335ae0765b4a1b25684971b54efa Mon Sep 17 00:00:00 2001 From: Yara Tercero Date: Mon, 30 Aug 2021 17:46:41 -0700 Subject: [PATCH 2/4] updated alerts page to not show table when no privileges and updates to tests --- .../public/doc_links/doc_links_service.ts | 2 + .../public/app/deep_links/index.test.ts | 64 ------------------- .../translations.tsx | 30 ++++----- .../use_missing_privileges.ts | 17 ++++- .../take_action_dropdown/index.test.tsx | 3 + .../components/user_info/index.test.tsx | 1 + .../detections/components/user_info/index.tsx | 21 ++++++ .../detection_engine.test.tsx | 3 +- .../detection_engine/detection_engine.tsx | 29 ++++++++- .../pages/detection_engine/translations.ts | 15 +++++ 10 files changed, 100 insertions(+), 85 deletions(-) diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts index 88075b66ad045..a1edb767856c6 100644 --- a/src/core/public/doc_links/doc_links_service.ts +++ b/src/core/public/doc_links/doc_links_service.ts @@ -204,6 +204,7 @@ export class DocLinksService { siem: { guide: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/index.html`, gettingStarted: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/index.html`, + privileges: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/sec-requirements.html`, ml: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/machine-learning.html`, ruleChangeLog: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/prebuilt-rules-changelog.html`, detectionsReq: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/detections-permissions-section.html`, @@ -568,6 +569,7 @@ export interface DocLinksStart { readonly rollupJobs: string; readonly elasticsearch: Record; readonly siem: { + readonly privileges: string; readonly guide: string; readonly gettingStarted: string; readonly ml: string; diff --git a/x-pack/plugins/security_solution/public/app/deep_links/index.test.ts b/x-pack/plugins/security_solution/public/app/deep_links/index.test.ts index 4df49b957ad9c..59af6737e495f 100644 --- a/x-pack/plugins/security_solution/public/app/deep_links/index.test.ts +++ b/x-pack/plugins/security_solution/public/app/deep_links/index.test.ts @@ -101,68 +101,4 @@ describe('public search functions', () => { }); expect(deepLinks.some((l) => l.id === SecurityPageName.ueba)).toBeTruthy(); }); - - describe('Detections Alerts deep links', () => { - it('should return alerts link for basic license with only read_alerts capabilities', () => { - const basicLicense = 'basic'; - const basicLinks = getDeepLinks(mockGlobalState.app.enableExperimental, basicLicense, ({ - siem: { read_alerts: true, crud_alerts: false }, - } as unknown) as Capabilities); - - const detectionsDeepLinks = - basicLinks.find((l) => l.id === SecurityPageName.detections)?.deepLinks ?? []; - - expect( - detectionsDeepLinks.length && - detectionsDeepLinks.some((l) => l.id === SecurityPageName.alerts) - ).toBeTruthy(); - }); - - it('should return alerts link with for basic license with crud_alerts capabilities', () => { - const basicLicense = 'basic'; - const basicLinks = getDeepLinks(mockGlobalState.app.enableExperimental, basicLicense, ({ - siem: { read_alerts: true, crud_alerts: true }, - } as unknown) as Capabilities); - - const detectionsDeepLinks = - basicLinks.find((l) => l.id === SecurityPageName.detections)?.deepLinks ?? []; - - expect( - detectionsDeepLinks.length && - detectionsDeepLinks.some((l) => l.id === SecurityPageName.alerts) - ).toBeTruthy(); - }); - - it('should NOT return alerts link for basic license with NO read_alerts capabilities', () => { - const basicLicense = 'basic'; - const basicLinks = getDeepLinks(mockGlobalState.app.enableExperimental, basicLicense, ({ - siem: { read_alerts: false, crud_alerts: false }, - } as unknown) as Capabilities); - - const detectionsDeepLinks = - basicLinks.find((l) => l.id === SecurityPageName.detections)?.deepLinks ?? []; - - expect( - detectionsDeepLinks.length && - detectionsDeepLinks.some((l) => l.id === SecurityPageName.alerts) - ).toBeFalsy(); - }); - - it('should return alerts link for basic license with undefined capabilities', () => { - const basicLicense = 'basic'; - const basicLinks = getDeepLinks( - mockGlobalState.app.enableExperimental, - basicLicense, - undefined - ); - - const detectionsDeepLinks = - basicLinks.find((l) => l.id === SecurityPageName.detections)?.deepLinks ?? []; - - expect( - detectionsDeepLinks.length && - detectionsDeepLinks.some((l) => l.id === SecurityPageName.alerts) - ).toBeTruthy(); - }); - }); }); diff --git a/x-pack/plugins/security_solution/public/detections/components/callouts/missing_privileges_callout/translations.tsx b/x-pack/plugins/security_solution/public/detections/components/callouts/missing_privileges_callout/translations.tsx index bda43c0e3d834..12011496c252b 100644 --- a/x-pack/plugins/security_solution/public/detections/components/callouts/missing_privileges_callout/translations.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/callouts/missing_privileges_callout/translations.tsx @@ -46,7 +46,7 @@ const CANNOT_EDIT_LISTS = i18n.translate( const CANNOT_EDIT_ALERTS = i18n.translate( 'xpack.securitySolution.detectionEngine.missingPrivilegesCallOut.cannotEditAlerts', { - defaultMessage: 'Without these privileges, you cannot open or close alerts.', + defaultMessage: 'Without these privileges, you cannot interact with alerts.', } ); @@ -83,20 +83,20 @@ export const missingPrivilegesCallOutBody = ({ ) : null, - // featurePrivileges: - // featurePrivileges.length > 0 ? ( - // <> - // - //
    - // {featurePrivileges.map(([feature, missingPrivileges]) => ( - //
  • {missingFeaturePrivileges(feature, missingPrivileges)}
  • - // ))} - //
- // - // ) : null, + featurePrivileges: + featurePrivileges.length > 0 ? ( + <> + +
    + {featurePrivileges.map(([feature, missingPrivileges]) => ( +
  • {missingFeaturePrivileges(feature, missingPrivileges)}
  • + ))} +
+ + ) : null, docs: (
  • diff --git a/x-pack/plugins/security_solution/public/detections/components/callouts/missing_privileges_callout/use_missing_privileges.ts b/x-pack/plugins/security_solution/public/detections/components/callouts/missing_privileges_callout/use_missing_privileges.ts index ea2b081239fda..eec9bd1f09053 100644 --- a/x-pack/plugins/security_solution/public/detections/components/callouts/missing_privileges_callout/use_missing_privileges.ts +++ b/x-pack/plugins/security_solution/public/detections/components/callouts/missing_privileges_callout/use_missing_privileges.ts @@ -40,14 +40,18 @@ export interface MissingPrivileges { } export const useMissingPrivileges = (): MissingPrivileges => { - const { listPrivileges } = useUserPrivileges(); + const { detectionEnginePrivileges, listPrivileges } = useUserPrivileges(); const [{ canUserCRUD }] = useUserData(); return useMemo(() => { const featurePrivileges: MissingFeaturePrivileges[] = []; const indexPrivileges: MissingIndexPrivileges[] = []; - if (canUserCRUD == null || listPrivileges.result == null) { + if ( + canUserCRUD == null || + listPrivileges.result == null || + detectionEnginePrivileges.result == null + ) { /** * Do not check privileges till we get all the data. That helps to reduce * subsequent layout shift while loading and skip unneeded re-renders. @@ -72,9 +76,16 @@ export const useMissingPrivileges = (): MissingPrivileges => { indexPrivileges.push(missingListsPrivileges); } + const missingDetectionPrivileges = getMissingIndexPrivileges( + detectionEnginePrivileges.result.index + ); + if (missingDetectionPrivileges) { + indexPrivileges.push(missingDetectionPrivileges); + } + return { featurePrivileges, indexPrivileges, }; - }, [canUserCRUD, listPrivileges]); + }, [canUserCRUD, listPrivileges, detectionEnginePrivileges]); }; diff --git a/x-pack/plugins/security_solution/public/detections/components/take_action_dropdown/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/take_action_dropdown/index.test.tsx index 500b042916925..bba652bcdd030 100644 --- a/x-pack/plugins/security_solution/public/detections/components/take_action_dropdown/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/take_action_dropdown/index.test.tsx @@ -24,6 +24,9 @@ jest.mock('../../../common/lib/kibana', () => ({ useKibana: jest.fn(), useGetUserCasesPermissions: jest.fn().mockReturnValue({ crud: true }), })); +jest.mock('../../containers/detection_engine/alerts/use_alerts_privileges', () => ({ + useAlertsPrivileges: jest.fn().mockReturnValue({ hasIndexWrite: true, hasKibanaCRUD: true }), +})); jest.mock('../../../cases/components/use_insert_timeline'); jest.mock('../../../common/hooks/use_experimental_features', () => ({ diff --git a/x-pack/plugins/security_solution/public/detections/components/user_info/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/user_info/index.test.tsx index 9972233dce351..7d0dc15a18cc0 100644 --- a/x-pack/plugins/security_solution/public/detections/components/user_info/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/user_info/index.test.tsx @@ -43,6 +43,7 @@ describe('useUserInfo', () => { expect(result.all).toHaveLength(1); expect(result.current).toEqual({ canUserCRUD: null, + canUserRead: null, hasEncryptionKey: null, hasIndexManage: null, hasIndexMaintenance: null, diff --git a/x-pack/plugins/security_solution/public/detections/components/user_info/index.tsx b/x-pack/plugins/security_solution/public/detections/components/user_info/index.tsx index da6df631d951e..fd2b414360cb5 100644 --- a/x-pack/plugins/security_solution/public/detections/components/user_info/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/user_info/index.tsx @@ -15,6 +15,7 @@ import { useCreateTransforms } from '../../../transforms/containers/use_create_t export interface State { canUserCRUD: boolean | null; + canUserRead: boolean | null; hasIndexManage: boolean | null; hasIndexMaintenance: boolean | null; hasIndexWrite: boolean | null; @@ -30,6 +31,7 @@ export interface State { export const initialState: State = { canUserCRUD: null, + canUserRead: null, hasIndexManage: null, hasIndexMaintenance: null, hasIndexWrite: null, @@ -81,6 +83,10 @@ export type Action = type: 'updateCanUserCRUD'; canUserCRUD: boolean | null; } + | { + type: 'updateCanUserRead'; + canUserRead: boolean | null; + } | { type: 'updateSignalIndexName'; signalIndexName: string | null; @@ -152,6 +158,12 @@ export const userInfoReducer = (state: State, action: Action): State => { canUserCRUD: action.canUserCRUD, }; } + case 'updateCanUserRead': { + return { + ...state, + canUserRead: action.canUserRead, + }; + } case 'updateSignalIndexName': { return { ...state, @@ -187,6 +199,7 @@ export const useUserInfo = (): State => { const [ { canUserCRUD, + canUserRead, hasIndexManage, hasIndexMaintenance, hasIndexWrite, @@ -223,6 +236,7 @@ export const useUserInfo = (): State => { const uiCapabilities = useKibana().services.application.capabilities; const capabilitiesCanUserCRUD: boolean = uiCapabilities.siem.crud === true; + const capabilitiesCanUserRead: boolean = uiCapabilities.siem.read === true; useEffect(() => { if (loading !== (privilegeLoading || indexNameLoading)) { @@ -299,6 +313,12 @@ export const useUserInfo = (): State => { } }, [dispatch, loading, canUserCRUD, capabilitiesCanUserCRUD]); + useEffect(() => { + if (!loading && canUserRead !== capabilitiesCanUserRead) { + dispatch({ type: 'updateCanUserRead', canUserRead: capabilitiesCanUserRead }); + } + }, [dispatch, loading, canUserRead, capabilitiesCanUserRead]); + useEffect(() => { if (!loading && signalIndexName !== apiSignalIndexName && apiSignalIndexName != null) { dispatch({ type: 'updateSignalIndexName', signalIndexName: apiSignalIndexName }); @@ -351,6 +371,7 @@ export const useUserInfo = (): State => { isAuthenticated, hasEncryptionKey, canUserCRUD, + canUserRead, hasIndexManage, hasIndexMaintenance, hasIndexWrite, diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx index a92f4d706dc7c..92f92f0793157 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx @@ -80,7 +80,7 @@ jest.mock('../../../common/lib/kibana', () => { docLinks: { links: { siem: { - gettingStarted: 'link', + privileges: 'link', }, }, }, @@ -107,6 +107,7 @@ describe('DetectionEnginePageComponent', () => { (useUserData as jest.Mock).mockReturnValue([ { hasIndexRead: true, + canUserRead: true, }, ]); (useSourcererScope as jest.Mock).mockReturnValue({ diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx index b766410a4e7e6..1165209e67d54 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx @@ -72,6 +72,7 @@ import { AlertsTableFilterGroup, FILTER_OPEN, } from '../../components/alerts_table/alerts_filter_group'; +import { EmptyPage } from '../../../common/components/empty_page'; /** * Need a 100% height here to account for the graph/analyze tool, which sets no explicit height parameters, but fills the available space. @@ -119,6 +120,8 @@ const DetectionEnginePageComponent: React.FC = ({ hasIndexWrite = false, hasIndexMaintenance = false, canUserCRUD = false, + canUserRead = false, + hasIndexRead, }, ] = useUserData(); const { @@ -132,6 +135,7 @@ const DetectionEnginePageComponent: React.FC = ({ const { application: { navigateToUrl }, timelines: timelinesUi, + docLinks, } = useKibana().services; const [filterGroup, setFilterGroup] = useState(FILTER_OPEN); @@ -248,6 +252,18 @@ const DetectionEnginePageComponent: React.FC = ({ [containerElement, onSkipFocusBeforeEventsTable, onSkipFocusAfterEventsTable] ); + const emptyPageActions = useMemo( + () => ({ + feature: { + icon: 'documents', + label: i18n.GO_TO_DOCUMENTATION, + url: `${docLinks.links.siem.privileges}`, + target: '_blank', + }, + }), + [docLinks] + ); + if (isUserAuthenticated != null && !isUserAuthenticated && !loading) { return ( @@ -274,7 +290,15 @@ const DetectionEnginePageComponent: React.FC = ({ {hasEncryptionKey != null && !hasEncryptionKey && } - {indicesExist ? ( + {indicesExist && (!hasIndexRead || !canUserRead) && ( + + )} + {indicesExist && hasIndexRead && canUserRead && ( @@ -351,7 +375,8 @@ const DetectionEnginePageComponent: React.FC = ({ /> - ) : ( + )} + {!indicesExist && ( diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/translations.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/translations.ts index 96e423aff1658..fedf119025304 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/translations.ts @@ -143,3 +143,18 @@ export const ML_RULES_UNAVAILABLE = (totalRules: number) => defaultMessage: '{totalRules} {totalRules, plural, =1 {rule requires} other {rules require}} Machine Learning to enable.', }); + +export const FEATURE_NO_PERMISSIONS_TITLE = i18n.translate( + 'xpack.securitySolution.detectionEngine.noPermissionsTitle', + { + defaultMessage: 'Privileges required', + } +); + +export const ALERTS_FEATURE_NO_PERMISSIONS_MSG = i18n.translate( + 'xpack.securitySolution.detectionEngine.noPermissionsMessage', + { + defaultMessage: + 'To view alerts, you must update privileges. For more information, contact your Kibana administrator.', + } +); From dc357856ad7acc7da1a13138b23baaebe13d344c Mon Sep 17 00:00:00 2001 From: Yara Tercero Date: Tue, 31 Aug 2021 11:30:54 -0700 Subject: [PATCH 3/4] updates tests and move to use privileges page when user tries to access alerts without proper access --- .../translations.tsx | 55 +++++++++---------- .../components/user_info/index.test.tsx | 1 - .../detections/components/user_info/index.tsx | 20 ------- .../alerts/use_alerts_privileges.test.tsx | 6 +- .../alerts/use_alerts_privileges.tsx | 4 +- .../detection_engine.test.tsx | 1 - .../detection_engine/detection_engine.tsx | 14 +++-- 7 files changed, 40 insertions(+), 61 deletions(-) diff --git a/x-pack/plugins/security_solution/public/detections/components/callouts/missing_privileges_callout/translations.tsx b/x-pack/plugins/security_solution/public/detections/components/callouts/missing_privileges_callout/translations.tsx index 12011496c252b..ddba9110770db 100644 --- a/x-pack/plugins/security_solution/public/detections/components/callouts/missing_privileges_callout/translations.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/callouts/missing_privileges_callout/translations.tsx @@ -9,10 +9,6 @@ import { EuiCode } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import React from 'react'; -import { - DetectionsRequirementsLink, - SecuritySolutionRequirementsLink, -} from '../../../../common/components/links_to_docs'; import { DEFAULT_ITEMS_INDEX, DEFAULT_LISTS_INDEX, @@ -46,7 +42,7 @@ const CANNOT_EDIT_LISTS = i18n.translate( const CANNOT_EDIT_ALERTS = i18n.translate( 'xpack.securitySolution.detectionEngine.missingPrivilegesCallOut.cannotEditAlerts', { - defaultMessage: 'Without these privileges, you cannot interact with alerts.', + defaultMessage: 'Without these privileges, you cannot view or change status of alerts.', } ); @@ -83,30 +79,31 @@ export const missingPrivilegesCallOutBody = ({
) : null, - featurePrivileges: - featurePrivileges.length > 0 ? ( - <> - -
    - {featurePrivileges.map(([feature, missingPrivileges]) => ( -
  • {missingFeaturePrivileges(feature, missingPrivileges)}
  • - ))} -
- - ) : null, - docs: ( -
    -
  • - -
  • -
  • - -
  • -
- ), + // TODO: Uncomment once RBAC for alerts is reenabled + // featurePrivileges: + // featurePrivileges.length > 0 ? ( + // <> + // + //
    + // {featurePrivileges.map(([feature, missingPrivileges]) => ( + //
  • {missingFeaturePrivileges(feature, missingPrivileges)}
  • + // ))} + //
+ // + // ) : null, + // docs: ( + //
    + //
  • + // + //
  • + //
  • + // + //
  • + //
+ // ), }} /> ); diff --git a/x-pack/plugins/security_solution/public/detections/components/user_info/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/user_info/index.test.tsx index 7d0dc15a18cc0..9972233dce351 100644 --- a/x-pack/plugins/security_solution/public/detections/components/user_info/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/user_info/index.test.tsx @@ -43,7 +43,6 @@ describe('useUserInfo', () => { expect(result.all).toHaveLength(1); expect(result.current).toEqual({ canUserCRUD: null, - canUserRead: null, hasEncryptionKey: null, hasIndexManage: null, hasIndexMaintenance: null, diff --git a/x-pack/plugins/security_solution/public/detections/components/user_info/index.tsx b/x-pack/plugins/security_solution/public/detections/components/user_info/index.tsx index fd2b414360cb5..63f69c4eaeb2a 100644 --- a/x-pack/plugins/security_solution/public/detections/components/user_info/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/user_info/index.tsx @@ -15,7 +15,6 @@ import { useCreateTransforms } from '../../../transforms/containers/use_create_t export interface State { canUserCRUD: boolean | null; - canUserRead: boolean | null; hasIndexManage: boolean | null; hasIndexMaintenance: boolean | null; hasIndexWrite: boolean | null; @@ -31,7 +30,6 @@ export interface State { export const initialState: State = { canUserCRUD: null, - canUserRead: null, hasIndexManage: null, hasIndexMaintenance: null, hasIndexWrite: null, @@ -83,10 +81,6 @@ export type Action = type: 'updateCanUserCRUD'; canUserCRUD: boolean | null; } - | { - type: 'updateCanUserRead'; - canUserRead: boolean | null; - } | { type: 'updateSignalIndexName'; signalIndexName: string | null; @@ -158,12 +152,6 @@ export const userInfoReducer = (state: State, action: Action): State => { canUserCRUD: action.canUserCRUD, }; } - case 'updateCanUserRead': { - return { - ...state, - canUserRead: action.canUserRead, - }; - } case 'updateSignalIndexName': { return { ...state, @@ -199,7 +187,6 @@ export const useUserInfo = (): State => { const [ { canUserCRUD, - canUserRead, hasIndexManage, hasIndexMaintenance, hasIndexWrite, @@ -313,12 +300,6 @@ export const useUserInfo = (): State => { } }, [dispatch, loading, canUserCRUD, capabilitiesCanUserCRUD]); - useEffect(() => { - if (!loading && canUserRead !== capabilitiesCanUserRead) { - dispatch({ type: 'updateCanUserRead', canUserRead: capabilitiesCanUserRead }); - } - }, [dispatch, loading, canUserRead, capabilitiesCanUserRead]); - useEffect(() => { if (!loading && signalIndexName !== apiSignalIndexName && apiSignalIndexName != null) { dispatch({ type: 'updateSignalIndexName', signalIndexName: apiSignalIndexName }); @@ -371,7 +352,6 @@ export const useUserInfo = (): State => { isAuthenticated, hasEncryptionKey, canUserCRUD, - canUserRead, hasIndexManage, hasIndexMaintenance, hasIndexWrite, diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_alerts_privileges.test.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_alerts_privileges.test.tsx index e6ee954c426c0..34d23ebc48523 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_alerts_privileges.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_alerts_privileges.test.tsx @@ -121,7 +121,7 @@ describe('useAlertsPrivileges', () => { }); }); - test('if there is an error when fetching user privilege, we should get back false for every properties', async () => { + test('if there is an error when fetching user privilege, we should get back false for all index related properties', async () => { const userPrivileges = produce(userPrivilegesInitial, (draft) => { draft.detectionEnginePrivileges.error = new Error('Something went wrong'); }); @@ -141,8 +141,8 @@ describe('useAlertsPrivileges', () => { hasIndexUpdateDelete: false, hasKibanaCRUD: false, hasKibanaREAD: false, - isAuthenticated: false, - loading: false, + isAuthenticated: true, + loading: true, }); }); }); diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_alerts_privileges.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_alerts_privileges.tsx index e0d4cd75bb0a3..b377eda49d0cd 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_alerts_privileges.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_alerts_privileges.tsx @@ -54,8 +54,8 @@ export const useAlertsPrivileges = (): UseAlertsPrivelegesReturn => { hasIndexWrite: false, hasIndexUpdateDelete: false, hasIndexMaintenance: false, - hasKibanaCRUD: false, - hasKibanaREAD: false, + hasKibanaCRUD, + hasKibanaREAD, }); } }, [detectionEnginePrivileges.error, hasKibanaCRUD, hasKibanaREAD]); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx index 92f92f0793157..5fe60f47f0f40 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx @@ -107,7 +107,6 @@ describe('DetectionEnginePageComponent', () => { (useUserData as jest.Mock).mockReturnValue([ { hasIndexRead: true, - canUserRead: true, }, ]); (useSourcererScope as jest.Mock).mockReturnValue({ diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx index 1165209e67d54..962ab483db6d6 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx @@ -5,6 +5,10 @@ * 2.0. */ +// No bueno, I know! Encountered when reverting RBAC work post initial BCs +// Don't want to include large amounts of refactor in this temporary workaround +// TODO: Refactor code - component can be broken apart +/* eslint-disable complexity */ import { EuiFlexGroup, EuiFlexItem, @@ -73,6 +77,7 @@ import { FILTER_OPEN, } from '../../components/alerts_table/alerts_filter_group'; import { EmptyPage } from '../../../common/components/empty_page'; +import { useAlertsPrivileges } from '../../containers/detection_engine/alerts/use_alerts_privileges'; /** * Need a 100% height here to account for the graph/analyze tool, which sets no explicit height parameters, but fills the available space. @@ -120,10 +125,10 @@ const DetectionEnginePageComponent: React.FC = ({ hasIndexWrite = false, hasIndexMaintenance = false, canUserCRUD = false, - canUserRead = false, hasIndexRead, }, ] = useUserData(); + const { hasKibanaREAD } = useAlertsPrivileges(); const { loading: listsConfigLoading, needsConfiguration: needsListsConfiguration, @@ -290,7 +295,7 @@ const DetectionEnginePageComponent: React.FC = ({ {hasEncryptionKey != null && !hasEncryptionKey && } - {indicesExist && (!hasIndexRead || !canUserRead) && ( + {indicesExist && (!hasIndexRead || !hasKibanaREAD) && ( = ({ title={i18n.FEATURE_NO_PERMISSIONS_TITLE} /> )} - {indicesExist && hasIndexRead && canUserRead && ( + {indicesExist && hasIndexRead && hasKibanaREAD ? ( @@ -375,8 +380,7 @@ const DetectionEnginePageComponent: React.FC = ({ /> - )} - {!indicesExist && ( + ) : ( From 1bb3327bf45efe4d4c1bc9727125ca993efa72a3 Mon Sep 17 00:00:00 2001 From: Yara Tercero Date: Tue, 31 Aug 2021 11:56:10 -0700 Subject: [PATCH 4/4] updating jest tests --- .../translations.tsx | 56 +++++++++++-------- .../detections/components/user_info/index.tsx | 1 - .../alerts/use_alerts_privileges.test.tsx | 8 +-- .../detection_engine.test.tsx | 5 ++ .../detection_engine/detection_engine.tsx | 5 +- 5 files changed, 43 insertions(+), 32 deletions(-) diff --git a/x-pack/plugins/security_solution/public/detections/components/callouts/missing_privileges_callout/translations.tsx b/x-pack/plugins/security_solution/public/detections/components/callouts/missing_privileges_callout/translations.tsx index ddba9110770db..0d628d89c0925 100644 --- a/x-pack/plugins/security_solution/public/detections/components/callouts/missing_privileges_callout/translations.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/callouts/missing_privileges_callout/translations.tsx @@ -17,6 +17,10 @@ import { } from '../../../../../common/constants'; import { CommaSeparatedValues } from './comma_separated_values'; import { MissingPrivileges } from './use_missing_privileges'; +import { + DetectionsRequirementsLink, + SecuritySolutionRequirementsLink, +} from '../../../../common/components/links_to_docs'; export const MISSING_PRIVILEGES_CALLOUT_TITLE = i18n.translate( 'xpack.securitySolution.detectionEngine.missingPrivilegesCallOut.messageTitle', @@ -48,7 +52,7 @@ const CANNOT_EDIT_ALERTS = i18n.translate( export const missingPrivilegesCallOutBody = ({ indexPrivileges, - featurePrivileges, + featurePrivileges = [], }: MissingPrivileges) => ( (
  • {missingIndexPrivileges(index, missingPrivileges)}
  • ))} - {featurePrivileges.map(([feature, missingPrivileges]) => ( + { + // TODO: Uncomment once RBAC for alerts is reenabled + /* {featurePrivileges.map(([feature, missingPrivileges]) => (
  • {missingFeaturePrivileges(feature, missingPrivileges)}
  • - ))} + ))} */ + } ) : null, @@ -94,16 +101,16 @@ export const missingPrivilegesCallOutBody = ({ // // // ) : null, - // docs: ( - //
      - //
    • - // - //
    • - //
    • - // - //
    • - //
    - // ), + docs: ( +
      +
    • + +
    • +
    • + +
    • +
    + ), }} /> ); @@ -152,14 +159,15 @@ const missingIndexPrivileges = (index: string, privileges: string[]) => ( /> ); -const missingFeaturePrivileges = (feature: string, privileges: string[]) => ( - , - index: {feature}, - explanation: getPrivilegesExplanation(privileges, feature), - }} - /> -); +// TODO: Uncomment once RBAC for alerts is reenabled +// const missingFeaturePrivileges = (feature: string, privileges: string[]) => ( +// , +// index: {feature}, +// explanation: getPrivilegesExplanation(privileges, feature), +// }} +// /> +// ); diff --git a/x-pack/plugins/security_solution/public/detections/components/user_info/index.tsx b/x-pack/plugins/security_solution/public/detections/components/user_info/index.tsx index 63f69c4eaeb2a..da6df631d951e 100644 --- a/x-pack/plugins/security_solution/public/detections/components/user_info/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/user_info/index.tsx @@ -223,7 +223,6 @@ export const useUserInfo = (): State => { const uiCapabilities = useKibana().services.application.capabilities; const capabilitiesCanUserCRUD: boolean = uiCapabilities.siem.crud === true; - const capabilitiesCanUserRead: boolean = uiCapabilities.siem.read === true; useEffect(() => { if (loading !== (privilegeLoading || indexNameLoading)) { diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_alerts_privileges.test.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_alerts_privileges.test.tsx index 34d23ebc48523..cbab24835c1ac 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_alerts_privileges.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_alerts_privileges.test.tsx @@ -139,10 +139,10 @@ describe('useAlertsPrivileges', () => { hasIndexRead: false, hasIndexWrite: false, hasIndexUpdateDelete: false, - hasKibanaCRUD: false, - hasKibanaREAD: false, - isAuthenticated: true, - loading: true, + hasKibanaCRUD: true, + hasKibanaREAD: true, + isAuthenticated: false, + loading: false, }); }); }); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx index 5fe60f47f0f40..837e08b7b46de 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx @@ -23,6 +23,7 @@ import { useSourcererScope } from '../../../common/containers/sourcerer'; import { createStore, State } from '../../../common/store'; import { mockHistory, Router } from '../../../common/mock/router'; import { mockTimelines } from '../../../common/mock/mock_timelines_plugin'; +import { useAlertsPrivileges } from '../../containers/detection_engine/alerts/use_alerts_privileges'; // Test will fail because we will to need to mock some core services to make the test work // For now let's forget about SiemSearchBar and QueryBar @@ -34,6 +35,7 @@ jest.mock('../../../common/components/query_bar', () => ({ })); jest.mock('../../containers/detection_engine/lists/use_lists_config'); jest.mock('../../components/user_info'); +jest.mock('../../containers/detection_engine/alerts/use_alerts_privileges'); jest.mock('../../../common/containers/sourcerer'); jest.mock('../../../common/components/link_to'); jest.mock('../../../common/containers/use_global_time', () => ({ @@ -109,6 +111,9 @@ describe('DetectionEnginePageComponent', () => { hasIndexRead: true, }, ]); + (useAlertsPrivileges as jest.Mock).mockReturnValue({ + hasKibanaREAD: true, + }); (useSourcererScope as jest.Mock).mockReturnValue({ indicesExist: true, indexPattern: {}, diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx index 962ab483db6d6..093082c288a98 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx @@ -295,15 +295,14 @@ const DetectionEnginePageComponent: React.FC = ({ {hasEncryptionKey != null && !hasEncryptionKey && } - {indicesExist && (!hasIndexRead || !hasKibanaREAD) && ( + {indicesExist && (!hasIndexRead || !hasKibanaREAD) ? ( - )} - {indicesExist && hasIndexRead && hasKibanaREAD ? ( + ) : indicesExist && hasIndexRead && hasKibanaREAD ? (