From a074c06864cb9b78c31b93f05c3345c780cc0809 Mon Sep 17 00:00:00 2001 From: Pablo Machado Date: Wed, 26 Jul 2023 13:12:09 +0200 Subject: [PATCH] [Security Solutions] Add PLI authorisation for Advanced Insights (Entity Risk) (#161190) ## Summary Add PLI authorization checks for Entity Analytics features. *This PR only restricts access to the features* but doesn't implement PLG/Upselling. It will be added later when we have defined the UX for it. The `advancedInsights` PLI was already configured, so I only had to add extra checks to make sure users can't see the Risk score on other components. Updated components: * "All hosts" table on the Hosts page * "All users" table on the Users page * Host overview on the Host details page and Host details flyout * User overview on the User details page and User details flyout * Alerts flyout * Remove sample Upselling components config ### Not included * Upselling/PLG * I left empty tabs/pages where the Upselling component will be added ### How to test it? #### ESS * Run ESS with a basic license * Run ESS with a platinum #### Serverless * Run Serverless with security essentials (serverless.security.yml) ``` xpack.serverless.security.productTypes: [ { product_line: 'security', product_tier: 'essentials' } ] ``` * Run Serverless with security complete (kibana/config/serverless.security.yml) ``` xpack.serverless.security.productTypes: [ { product_line: 'security', product_tier: 'complete' }, ] ``` https://github.com/elastic/kibana/assets/1490444/1ab84134-bee1-497c-9b41-a9ec398bd921 ### Checklist Delete any items that are not applicable to this PR. - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../cti_details/threat_summary_view.test.tsx | 4 ++ .../cti_details/threat_summary_view.tsx | 41 ++++++++++++------- .../event_details/event_details.tsx | 6 +-- .../event_details/use_risk_score_data.test.ts | 4 +- .../event_details/use_risk_score_data.ts | 10 +++-- .../common/lib/kibana/kibana_react.mock.ts | 11 +++++ .../summary/host_panel/host_panel.test.tsx | 6 +-- .../tabs/summary/host_panel/index.tsx | 4 +- .../tabs/summary/user_panel/index.tsx | 4 +- .../summary/user_panel/user_panel.test.tsx | 6 +-- .../risk_details_tab_body/index.tsx | 21 +++++----- .../containers/risk_score/all/index.test.tsx | 8 ++-- .../containers/risk_score/all/index.tsx | 28 +++---------- .../risk_score/feature_status/index.test.ts | 26 +++++++++++- .../risk_score/feature_status/index.ts | 32 ++++++++------- .../components/hosts_table/index.test.tsx | 35 +++++++++++++++- .../hosts/components/hosts_table/index.tsx | 11 ++++- .../navigation/host_risk_score_tab_body.tsx | 7 ++-- .../navigation/user_risk_score_tab_body.tsx | 8 ++-- .../left/components/host_details.test.tsx | 34 ++++++++++++--- .../flyout/left/components/host_details.tsx | 8 +++- .../left/components/user_details.test.tsx | 6 ++- .../flyout/left/components/user_details.tsx | 9 +++- .../components/host_entity_overview.test.tsx | 8 ++-- .../right/components/host_entity_overview.tsx | 4 +- .../components/user_entity_overview.test.tsx | 8 ++-- .../right/components/user_entity_overview.tsx | 4 +- .../security_solution/public/helper_hooks.tsx | 10 +++++ .../risk_score/index.test.tsx | 4 +- .../entity_analytics/risk_score/index.tsx | 4 +- .../components/host_overview/index.test.tsx | 4 +- .../components/host_overview/index.tsx | 4 +- .../components/user_overview/index.test.tsx | 4 +- .../components/user_overview/index.tsx | 4 +- .../public/overview/links.ts | 2 +- .../new_user_detail/__mocks__/index.ts | 2 +- .../new_user_detail/risk_score_field.test.tsx | 2 +- .../new_user_detail/risk_score_field.tsx | 4 +- .../public/upselling/register_upsellings.tsx | 31 +++++++------- 39 files changed, 273 insertions(+), 155 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/threat_summary_view.test.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/threat_summary_view.test.tsx index 3e1ddd6b01202..10d50cce74c9f 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/threat_summary_view.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/threat_summary_view.test.tsx @@ -24,6 +24,10 @@ jest.mock('../../../lib/kibana', () => ({ }), })); +jest.mock('../../../../helper_hooks', () => ({ + useHasSecurityCapability: () => true, +})); + jest.mock('../table/field_name_cell'); const RISK_SCORE_DATA_ROWS = 2; diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/threat_summary_view.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/threat_summary_view.tsx index 87d6d317e440c..da6cf3a04436d 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/threat_summary_view.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/threat_summary_view.tsx @@ -30,6 +30,7 @@ import { RiskSummary } from './risk_summary'; import { EnrichmentSummary } from './enrichment_summary'; import type { HostRisk, UserRisk } from '../../../../explore/containers/risk_score'; import { RiskScoreEntity } from '../../../../../common/search_strategy'; +import { useHasSecurityCapability } from '../../../../helper_hooks'; const UppercaseEuiTitle = styled(EuiTitle)` text-transform: uppercase; @@ -151,6 +152,12 @@ const ThreatSummaryViewComponent: React.FC<{ (eventDetail) => eventDetail?.field === 'user.risk.calculated_level' )?.values?.[0] as RiskSeverity | undefined; + const hasEntityAnalyticsCapability = useHasSecurityCapability('entity-analytics'); + + if (!hasEntityAnalyticsCapability && enrichments.length === 0) { + return null; + } + return ( <> @@ -161,21 +168,25 @@ const ThreatSummaryViewComponent: React.FC<{ - - - - - - - + {hasEntityAnalyticsCapability && ( + <> + + + + + + + + + )} = ({ const enrichmentCount = allEnrichments.length; - const { hostRisk, userRisk, isLicenseValid } = useRiskScoreData(data); + const { hostRisk, userRisk, isAuthorized } = useRiskScoreData(data); const renderer = useMemo( () => @@ -212,9 +212,9 @@ const EventDetailsComponent: React.FC = ({ const showThreatSummary = useMemo(() => { const hasEnrichments = enrichmentCount > 0; - const hasRiskInfoWithLicense = isLicenseValid && (hostRisk || userRisk); + const hasRiskInfoWithLicense = isAuthorized && (hostRisk || userRisk); return hasEnrichments || hasRiskInfoWithLicense; - }, [enrichmentCount, hostRisk, isLicenseValid, userRisk]); + }, [enrichmentCount, hostRisk, isAuthorized, userRisk]); const endpointResponseActionsEnabled = useIsExperimentalFeatureEnabled( 'endpointResponseActionsEnabled' ); diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/use_risk_score_data.test.ts b/x-pack/plugins/security_solution/public/common/components/event_details/use_risk_score_data.test.ts index c135da358ebda..656c13b22d856 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/use_risk_score_data.test.ts +++ b/x-pack/plugins/security_solution/public/common/components/event_details/use_risk_score_data.test.ts @@ -20,7 +20,7 @@ const defaultResult = { data: [], inspect: {}, isInspected: false, - isLicenseValid: true, + isAuthorized: true, isModuleEnabled: true, refetch: () => {}, totalCount: 0, @@ -55,7 +55,7 @@ describe('useRiskScoreData', () => { expect(result.current).toEqual({ hostRisk: defaultRisk, userRisk: defaultRisk, - isLicenseValid: true, + isAuthorized: true, }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/use_risk_score_data.ts b/x-pack/plugins/security_solution/public/common/components/event_details/use_risk_score_data.ts index aee2ee025072f..8ac02b1ce47e4 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/use_risk_score_data.ts +++ b/x-pack/plugins/security_solution/public/common/components/event_details/use_risk_score_data.ts @@ -31,7 +31,7 @@ export const useRiskScoreData = (data: TimelineEventsDetailsItem[]) => { const { data: hostRiskData, loading: hostRiskLoading, - isLicenseValid: isHostLicenseValid, + isAuthorized: isHostRiskScoreAuthorized, isModuleEnabled: isHostRiskModuleEnabled, } = useRiskScore({ filterQuery: hostNameFilterQuery, @@ -57,7 +57,7 @@ export const useRiskScoreData = (data: TimelineEventsDetailsItem[]) => { const { data: userRiskData, loading: userRiskLoading, - isLicenseValid: isUserLicenseValid, + isAuthorized: isUserRiskScoreAuthorized, isModuleEnabled: isUserRiskModuleEnabled, } = useRiskScore({ filterQuery: userNameFilterQuery, @@ -75,5 +75,9 @@ export const useRiskScoreData = (data: TimelineEventsDetailsItem[]) => { [userRiskLoading, isUserRiskModuleEnabled, userRiskData] ); - return { userRisk, hostRisk, isLicenseValid: isHostLicenseValid && isUserLicenseValid }; + return { + userRisk, + hostRisk, + isAuthorized: isHostRiskScoreAuthorized && isUserRiskScoreAuthorized, + }; }; diff --git a/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts b/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts index db06300121c24..ee06c92f5f76c 100644 --- a/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts +++ b/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts @@ -34,6 +34,7 @@ import { DEFAULT_RULES_TABLE_REFRESH_SETTING, DEFAULT_RULE_REFRESH_INTERVAL_ON, DEFAULT_RULE_REFRESH_INTERVAL_VALUE, + SERVER_APP_ID, } from '../../../../common/constants'; import type { StartServices } from '../../../types'; import { createSecuritySolutionStorageMock } from '../../mock/mock_local_storage'; @@ -178,6 +179,16 @@ export const createStartServicesMock = ( })), }, }, + application: { + ...core.application, + capabilities: { + ...core.application.capabilities, + [SERVER_APP_ID]: { + crud: true, + read: true, + }, + }, + }, security, storage, fleet, diff --git a/x-pack/plugins/security_solution/public/detections/pages/alert_details/tabs/summary/host_panel/host_panel.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/alert_details/tabs/summary/host_panel/host_panel.test.tsx index 210c700c2eb37..304e03a27de9f 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/alert_details/tabs/summary/host_panel/host_panel.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/alert_details/tabs/summary/host_panel/host_panel.test.tsx @@ -60,7 +60,7 @@ describe('AlertDetailsPage - SummaryTab - HostPanel', () => { inspect: null, refetch: () => {}, isModuleEnabled: true, - isLicenseValid: true, + isAuthorized: true, loading: false, }; const HostPanelWithDefaultProps = (propOverrides: Partial) => ( @@ -106,7 +106,7 @@ describe('AlertDetailsPage - SummaryTab - HostPanel', () => { it('should not show risk if the license is not valid', () => { mockUseRiskScore.mockReturnValue({ ...defaultRiskReturnValues, - isLicenseValid: false, + isAuthorized: false, data: null, }); const { queryByTestId } = render(); @@ -119,7 +119,7 @@ describe('AlertDetailsPage - SummaryTab - HostPanel', () => { mockUseRiskScore.mockReturnValue({ ...defaultRiskReturnValues, - isLicenseValid: true, + isAuthorized: true, data: [ { host: { diff --git a/x-pack/plugins/security_solution/public/detections/pages/alert_details/tabs/summary/host_panel/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/alert_details/tabs/summary/host_panel/index.tsx index c6b0f2160095b..9c4fdf7fab522 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/alert_details/tabs/summary/host_panel/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/alert_details/tabs/summary/host_panel/index.tsx @@ -88,7 +88,7 @@ export const HostPanel = React.memo( ); }, [browserFields, data, id]); - const { data: hostRisk, isLicenseValid: isRiskLicenseValid } = useRiskScore({ + const { data: hostRisk, isAuthorized: isRiskScoreAuthorized } = useRiskScore({ riskEntity: RiskScoreEntity.host, skip: hostName == null, }); @@ -149,7 +149,7 @@ export const HostPanel = React.memo( )} - {isRiskLicenseValid && ( + {isRiskScoreAuthorized && ( <> {hostRiskScore && ( diff --git a/x-pack/plugins/security_solution/public/detections/pages/alert_details/tabs/summary/user_panel/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/alert_details/tabs/summary/user_panel/index.tsx index 044427d46212b..3d931a952ed8e 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/alert_details/tabs/summary/user_panel/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/alert_details/tabs/summary/user_panel/index.tsx @@ -62,7 +62,7 @@ export const UserPanel = React.memo( ({ data, selectedPatterns, openUserDetailsPanel }: UserPanelProps) => { const userName = useMemo(() => getTimelineEventData('user.name', data), [data]); - const { data: userRisk, isLicenseValid: isRiskLicenseValid } = useRiskScore({ + const { data: userRisk, isAuthorized: isRiskScoreAuthorized } = useRiskScore({ riskEntity: RiskScoreEntity.user, skip: userName == null, }); @@ -114,7 +114,7 @@ export const UserPanel = React.memo( - {isRiskLicenseValid && ( + {isRiskScoreAuthorized && ( <> {userRiskScore && ( diff --git a/x-pack/plugins/security_solution/public/detections/pages/alert_details/tabs/summary/user_panel/user_panel.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/alert_details/tabs/summary/user_panel/user_panel.test.tsx index 58e5fa65a9885..a2d5978d05e96 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/alert_details/tabs/summary/user_panel/user_panel.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/alert_details/tabs/summary/user_panel/user_panel.test.tsx @@ -27,7 +27,7 @@ describe('AlertDetailsPage - SummaryTab - UserPanel', () => { inspect: null, refetch: () => {}, isModuleEnabled: true, - isLicenseValid: true, + isAuthorized: true, loading: false, }; const UserPanelWithDefaultProps = (propOverrides: Partial) => ( @@ -64,7 +64,7 @@ describe('AlertDetailsPage - SummaryTab - UserPanel', () => { it('should not show risk if the license is not valid', () => { mockUseRiskScore.mockReturnValue({ ...defaultRiskReturnValues, - isLicenseValid: false, + isAuthorized: false, data: null, }); const { queryByTestId } = render(); @@ -77,7 +77,7 @@ describe('AlertDetailsPage - SummaryTab - UserPanel', () => { mockUseRiskScore.mockReturnValue({ ...defaultRiskReturnValues, - isLicenseValid: true, + isAuthorized: true, data: [ { user: { diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_details_tab_body/index.tsx b/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_details_tab_body/index.tsx index 87f56d7cc1ff9..185096a7dde97 100644 --- a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_details_tab_body/index.tsx +++ b/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_details_tab_body/index.tsx @@ -32,7 +32,6 @@ import type { UsersComponentsQueryProps } from '../../../users/pages/navigation/ import type { HostsComponentsQueryProps } from '../../../hosts/pages/navigation/types'; import { useDashboardHref } from '../../../../common/hooks/use_dashboard_href'; import { RiskScoresNoDataDetected } from '../risk_score_onboarding/risk_score_no_data_detected'; -import { useUpsellingComponent } from '../../../../common/hooks/use_upselling'; const StyledEuiFlexGroup = styled(EuiFlexGroup)` margin-top: ${({ theme }) => theme.eui.euiSizeL}; @@ -49,7 +48,6 @@ const RiskDetailsTabBodyComponent: React.FC< riskEntity: RiskScoreEntity; } > = ({ entityName, startDate, endDate, setQuery, deleteQuery, riskEntity }) => { - const RiskScoreUpsell = useUpsellingComponent('entity_analytics_panel'); const queryId = useMemo( () => riskEntity === RiskScoreEntity.host @@ -84,13 +82,14 @@ const RiskDetailsTabBodyComponent: React.FC< [entityName, riskEntity] ); - const { data, loading, refetch, inspect, isDeprecated, isModuleEnabled } = useRiskScore({ - filterQuery, - onlyLatest: false, - riskEntity, - skip: !overTimeToggleStatus && !contributorsToggleStatus, - timerange, - }); + const { data, loading, refetch, inspect, isDeprecated, isModuleEnabled, isAuthorized } = + useRiskScore({ + filterQuery, + onlyLatest: false, + riskEntity, + skip: !overTimeToggleStatus && !contributorsToggleStatus, + timerange, + }); const rules = useMemo(() => { const lastRiskItem = data && data.length > 0 ? data[data.length - 1] : null; @@ -130,8 +129,8 @@ const RiskDetailsTabBodyComponent: React.FC< isDeprecated: isDeprecated && !loading, }; - if (RiskScoreUpsell) { - return ; + if (!isAuthorized) { + return <>{'TODO: Add RiskScore Upsell'}; } if (status.isDisabled || status.isDeprecated) { diff --git a/x-pack/plugins/security_solution/public/explore/containers/risk_score/all/index.test.tsx b/x-pack/plugins/security_solution/public/explore/containers/risk_score/all/index.test.tsx index 6ad707c2ddf7d..dfbba68e25c97 100644 --- a/x-pack/plugins/security_solution/public/explore/containers/risk_score/all/index.test.tsx +++ b/x-pack/plugins/security_solution/public/explore/containers/risk_score/all/index.test.tsx @@ -34,7 +34,7 @@ let appToastsMock: jest.Mocked>; const defaultFeatureStatus = { isLoading: false, isDeprecated: false, - isLicenseValid: true, + isAuthorized: true, isEnabled: true, refetch: () => {}, }; @@ -42,7 +42,7 @@ const defaultRisk = { data: undefined, inspect: {}, isInspected: false, - isLicenseValid: true, + isAuthorized: true, isModuleEnabled: true, isDeprecated: false, totalCount: 0, @@ -72,7 +72,7 @@ describe.each([RiskScoreEntity.host, RiskScoreEntity.user])( test('does not search if license is not valid', () => { mockUseRiskScoreFeatureStatus.mockReturnValue({ ...defaultFeatureStatus, - isLicenseValid: false, + isAuthorized: false, }); const { result } = renderHook(() => useRiskScore({ riskEntity }), { wrapper: TestProviders, @@ -81,7 +81,7 @@ describe.each([RiskScoreEntity.host, RiskScoreEntity.user])( expect(result.current).toEqual({ loading: false, ...defaultRisk, - isLicenseValid: false, + isAuthorized: false, refetch: result.current.refetch, }); }); diff --git a/x-pack/plugins/security_solution/public/explore/containers/risk_score/all/index.tsx b/x-pack/plugins/security_solution/public/explore/containers/risk_score/all/index.tsx index 063997512e063..f9b2f86a4a51a 100644 --- a/x-pack/plugins/security_solution/public/explore/containers/risk_score/all/index.tsx +++ b/x-pack/plugins/security_solution/public/explore/containers/risk_score/all/index.tsx @@ -40,7 +40,7 @@ export interface RiskScoreState { beforeEach(() => { jest.clearAllMocks(); mockUseMlCapabilities.mockReturnValue({ isPlatinumOrTrialLicense: true }); mockUseFetch.mockReturnValue(defaultFetch); + mockUseHasSecurityCapability.mockReturnValue(true); }); const defaultFetch = { @@ -36,7 +40,7 @@ describe(`risk score feature status`, () => { const defaultResult = { error: undefined, isDeprecated: true, - isLicenseValid: true, + isAuthorized: true, isEnabled: true, isLoading: true, }; @@ -52,7 +56,25 @@ describe(`risk score feature status`, () => { expect(mockFetch).not.toHaveBeenCalled(); expect(result.current).toEqual({ ...defaultResult, - isLicenseValid: false, + isAuthorized: false, + isDeprecated: false, + isEnabled: false, + refetch: result.current.refetch, + }); + }); + + test("does not search if the user doesn't has entity analytics capability", () => { + mockUseHasSecurityCapability.mockReturnValue(false); + const { result } = renderHook( + () => useRiskScoreFeatureStatus(RiskScoreEntity.host, 'the_right_one'), + { + wrapper: TestProviders, + } + ); + expect(mockFetch).not.toHaveBeenCalled(); + expect(result.current).toEqual({ + ...defaultResult, + isAuthorized: false, isDeprecated: false, isEnabled: false, refetch: result.current.refetch, diff --git a/x-pack/plugins/security_solution/public/explore/containers/risk_score/feature_status/index.ts b/x-pack/plugins/security_solution/public/explore/containers/risk_score/feature_status/index.ts index 20bfe8b7646d6..97994f58295c1 100644 --- a/x-pack/plugins/security_solution/public/explore/containers/risk_score/feature_status/index.ts +++ b/x-pack/plugins/security_solution/public/explore/containers/risk_score/feature_status/index.ts @@ -10,15 +10,16 @@ import { useMlCapabilities } from '../../../../common/components/ml/hooks/use_ml import { REQUEST_NAMES, useFetch } from '../../../../common/hooks/use_fetch'; import type { RiskScoreEntity } from '../../../../../common/search_strategy'; import { getRiskScoreIndexStatus } from './api'; +import { useHasSecurityCapability } from '../../../../helper_hooks'; interface RiskScoresFeatureStatus { error: unknown; // Is transform index an old version? isDeprecated: boolean; - // does the transform index exist? + // Does the transform index exist? isEnabled: boolean; - // is the user's license platinum? - isLicenseValid: boolean; + // Does the user has the authorization for the risk score feature? + isAuthorized: boolean; isLoading: boolean; refetch: (indexName: string) => void; } @@ -28,6 +29,8 @@ export const useRiskScoreFeatureStatus = ( defaultIndex?: string ): RiskScoresFeatureStatus => { const { isPlatinumOrTrialLicense, capabilitiesFetched } = useMlCapabilities(); + const hasEntityAnalyticsCapability = useHasSecurityCapability('entity-analytics'); + const isAuthorized = isPlatinumOrTrialLicense && hasEntityAnalyticsCapability; const { fetch, data, isLoading, error } = useFetch( REQUEST_NAMES.GET_RISK_SCORE_DEPRECATED, @@ -35,35 +38,36 @@ export const useRiskScoreFeatureStatus = ( ); const response = useMemo( - // if license is enabled, let isDeprecated = true so the actual + // if authorized is true, let isDeprecated = true so the actual // risk score fetch is not called until this check is complete - () => - data ? data : { isDeprecated: isPlatinumOrTrialLicense, isEnabled: isPlatinumOrTrialLicense }, - // isPlatinumOrTrialLicense is initial state, not update requirement + () => (data ? data : { isDeprecated: isAuthorized, isEnabled: isAuthorized }), + // isAuthorized is initial state, not update requirement // eslint-disable-next-line react-hooks/exhaustive-deps [data] ); const searchIndexStatus = useCallback( (indexName: string) => { - fetch({ - query: { indexName, entity: riskEntity }, - }); + if (isAuthorized) { + fetch({ + query: { indexName, entity: riskEntity }, + }); + } }, - [riskEntity, fetch] + [isAuthorized, fetch, riskEntity] ); useEffect(() => { - if (isPlatinumOrTrialLicense && defaultIndex != null) { + if (defaultIndex != null) { searchIndexStatus(defaultIndex); } - }, [isPlatinumOrTrialLicense, defaultIndex, searchIndexStatus]); + }, [defaultIndex, searchIndexStatus]); return { error, isLoading: isLoading || !capabilitiesFetched || defaultIndex == null, refetch: searchIndexStatus, - isLicenseValid: isPlatinumOrTrialLicense, + isAuthorized, ...response, }; }; diff --git a/x-pack/plugins/security_solution/public/explore/hosts/components/hosts_table/index.test.tsx b/x-pack/plugins/security_solution/public/explore/hosts/components/hosts_table/index.test.tsx index 2c3772b02c8e8..9bca791409a48 100644 --- a/x-pack/plugins/security_solution/public/explore/hosts/components/hosts_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/explore/hosts/components/hosts_table/index.test.tsx @@ -50,6 +50,11 @@ jest.mock('../../../../common/components/ml/hooks/use_ml_capabilities', () => ({ useMlCapabilities: () => mockUseMlCapabilities(), })); +const mockUseHasSecurityCapability = jest.fn().mockReturnValue(false); +jest.mock('../../../../helper_hooks', () => ({ + useHasSecurityCapability: () => mockUseHasSecurityCapability(), +})); + describe('Hosts Table', () => { const loadPage = jest.fn(); const state: State = mockGlobalState; @@ -84,8 +89,9 @@ describe('Hosts Table', () => { expect(wrapper.find('HostsTable')).toMatchSnapshot(); }); - test('it renders "Host Risk classfication" column when "isPlatinumOrTrialLicense" is truthy', () => { + test('it renders "Host Risk classification" column when "isPlatinumOrTrialLicense" is truthy and user has risk-entity capability', () => { mockUseMlCapabilities.mockReturnValue({ isPlatinumOrTrialLicense: true }); + mockUseHasSecurityCapability.mockReturnValue(true); const { queryByTestId } = render( @@ -107,8 +113,33 @@ describe('Hosts Table', () => { expect(queryByTestId('tableHeaderCell_node.risk_4')).toBeInTheDocument(); }); - test("it doesn't renders 'Host Risk classfication' column when 'isPlatinumOrTrialLicense' is falsy", () => { + test("it doesn't renders 'Host Risk classification' column when 'isPlatinumOrTrialLicense' is falsy", () => { mockUseMlCapabilities.mockReturnValue({ isPlatinumOrTrialLicense: false }); + mockUseHasSecurityCapability.mockReturnValue(true); + + const { queryByTestId } = render( + + + + ); + + expect(queryByTestId('tableHeaderCell_node.riskScore_4')).not.toBeInTheDocument(); + }); + + test("it doesn't renders 'Host Risk classification' column when user doesn't has entity-analytics capabilities", () => { + mockUseMlCapabilities.mockReturnValue({ isPlatinumOrTrialLicense: true }); + mockUseHasSecurityCapability.mockReturnValue(false); const { queryByTestId } = render( diff --git a/x-pack/plugins/security_solution/public/explore/hosts/components/hosts_table/index.tsx b/x-pack/plugins/security_solution/public/explore/hosts/components/hosts_table/index.tsx index 3d629213d1b66..185dfc687fb4a 100644 --- a/x-pack/plugins/security_solution/public/explore/hosts/components/hosts_table/index.tsx +++ b/x-pack/plugins/security_solution/public/explore/hosts/components/hosts_table/index.tsx @@ -31,6 +31,7 @@ import { SecurityPageName } from '../../../../../common/constants'; import { HostsTableType } from '../../store/model'; import { useNavigateTo } from '../../../../common/lib/kibana/hooks'; import { useMlCapabilities } from '../../../../common/components/ml/hooks/use_ml_capabilities'; +import { useHasSecurityCapability } from '../../../../helper_hooks'; const tableType = hostsModel.HostsTableType.hosts; @@ -132,6 +133,8 @@ const HostsTableComponent: React.FC = ({ }, [direction, sortField, type, dispatch] ); + + const hasEntityAnalyticsCapability = useHasSecurityCapability('entity-analytics'); const isPlatinumOrTrialLicense = useMlCapabilities().isPlatinumOrTrialLicense; const dispatchSeverityUpdate = useCallback( @@ -151,8 +154,12 @@ const HostsTableComponent: React.FC = ({ ); const hostsColumns = useMemo( - () => getHostsColumns(isPlatinumOrTrialLicense, dispatchSeverityUpdate), - [dispatchSeverityUpdate, isPlatinumOrTrialLicense] + () => + getHostsColumns( + isPlatinumOrTrialLicense && hasEntityAnalyticsCapability, + dispatchSeverityUpdate + ), + [dispatchSeverityUpdate, isPlatinumOrTrialLicense, hasEntityAnalyticsCapability] ); const sorting = useMemo(() => getSorting(sortField, direction), [sortField, direction]); diff --git a/x-pack/plugins/security_solution/public/explore/hosts/pages/navigation/host_risk_score_tab_body.tsx b/x-pack/plugins/security_solution/public/explore/hosts/pages/navigation/host_risk_score_tab_body.tsx index 333e6061c6d4e..889c42406e54b 100644 --- a/x-pack/plugins/security_solution/public/explore/hosts/pages/navigation/host_risk_score_tab_body.tsx +++ b/x-pack/plugins/security_solution/public/explore/hosts/pages/navigation/host_risk_score_tab_body.tsx @@ -22,7 +22,6 @@ import { import { useQueryToggle } from '../../../../common/containers/query_toggle'; import { EMPTY_SEVERITY_COUNT, RiskScoreEntity } from '../../../../../common/search_strategy'; import { RiskScoresNoDataDetected } from '../../../components/risk_score/risk_score_onboarding/risk_score_no_data_detected'; -import { useUpsellingComponent } from '../../../../common/hooks/use_upselling'; const HostRiskScoreTableManage = manageQuery(HostRiskScoreTable); @@ -35,7 +34,6 @@ export const HostRiskScoreQueryTabBody = ({ startDate: from, type, }: HostsComponentsQueryProps) => { - const RiskScoreUpsell = useUpsellingComponent('entity_analytics_panel'); const getHostRiskScoreSelector = useMemo(() => hostsSelectors.hostRiskScoreSelector(), []); const { activePage, limit, sort } = useDeepEqualSelector((state: State) => getHostRiskScoreSelector(state, hostsModel.HostsType.page) @@ -71,6 +69,7 @@ export const HostRiskScoreQueryTabBody = ({ isModuleEnabled, loading, refetch, + isAuthorized, totalCount, } = useRiskScore({ filterQuery, @@ -92,8 +91,8 @@ export const HostRiskScoreQueryTabBody = ({ isDeprecated: isDeprecated && !loading, }; - if (RiskScoreUpsell) { - return ; + if (!isAuthorized) { + return <>{'TODO: Add RiskScore Upsell'}; } if (status.isDisabled || status.isDeprecated) { diff --git a/x-pack/plugins/security_solution/public/explore/users/pages/navigation/user_risk_score_tab_body.tsx b/x-pack/plugins/security_solution/public/explore/users/pages/navigation/user_risk_score_tab_body.tsx index 8c59e4f13c55f..ed204dd0811fb 100644 --- a/x-pack/plugins/security_solution/public/explore/users/pages/navigation/user_risk_score_tab_body.tsx +++ b/x-pack/plugins/security_solution/public/explore/users/pages/navigation/user_risk_score_tab_body.tsx @@ -24,7 +24,6 @@ import { import { useQueryToggle } from '../../../../common/containers/query_toggle'; import { EMPTY_SEVERITY_COUNT, RiskScoreEntity } from '../../../../../common/search_strategy'; import { RiskScoresNoDataDetected } from '../../../components/risk_score/risk_score_onboarding/risk_score_no_data_detected'; -import { useUpsellingComponent } from '../../../../common/hooks/use_upselling'; const UserRiskScoreTableManage = manageQuery(UserRiskScoreTable); @@ -37,8 +36,6 @@ export const UserRiskScoreQueryTabBody = ({ startDate: from, type, }: UsersComponentsQueryProps) => { - const RiskScoreUpsell = useUpsellingComponent('entity_analytics_panel'); - const getUserRiskScoreSelector = useMemo(() => usersSelectors.userRiskScoreSelector(), []); const { activePage, limit, sort } = useDeepEqualSelector((state: State) => getUserRiskScoreSelector(state) @@ -75,6 +72,7 @@ export const UserRiskScoreQueryTabBody = ({ loading, refetch, totalCount, + isAuthorized, } = useRiskScore({ filterQuery, pagination, @@ -95,8 +93,8 @@ export const UserRiskScoreQueryTabBody = ({ isDeprecated: isDeprecated && !loading, }; - if (RiskScoreUpsell) { - return ; + if (!isAuthorized) { + return <>{'TODO: Add RiskScore Upsell'}; } if (status.isDisabled || status.isDeprecated) { diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/host_details.test.tsx b/x-pack/plugins/security_solution/public/flyout/left/components/host_details.test.tsx index f15c081c70d27..d3281b2169877 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/components/host_details.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/left/components/host_details.test.tsx @@ -55,6 +55,11 @@ jest.mock('uuid', () => ({ jest.mock('../../../common/components/ml/hooks/use_ml_capabilities'); const mockUseMlUserPermissions = useMlCapabilities as jest.Mock; +const mockUseHasSecurityCapability = jest.fn().mockReturnValue(false); +jest.mock('../../../helper_hooks', () => ({ + useHasSecurityCapability: () => mockUseHasSecurityCapability(), +})); + jest.mock('../../../common/containers/sourcerer', () => ({ useSourcererDataView: jest.fn().mockReturnValue({ selectedPatterns: ['index'] }), })); @@ -105,7 +110,7 @@ const mockRiskScoreResponse = { }, }, ], - isLicenseValid: true, + isAuthorized: true, }; const mockRelatedUsersResponse = { @@ -150,11 +155,13 @@ describe('', () => { expect(getByTestId(HOST_DETAILS_INFO_TEST_ID)).toBeInTheDocument(); }); - it('should render host risk score when license is valid', () => { + it('should render host risk score when authorized', () => { mockUseMlUserPermissions.mockReturnValue({ isPlatinumOrTrialLicense: true, capabilities: {}, }); + mockUseRiskScore.mockReturnValue({ data: [], isAuthorized: true }); + const { getByText } = render( @@ -163,8 +170,8 @@ describe('', () => { expect(getByText('Host risk score')).toBeInTheDocument(); }); - it('should not render host risk score when license is not valid', () => { - mockUseRiskScore.mockReturnValue({ data: [], isLicenseValid: false }); + it('should not render host risk score when unauthorized', () => { + mockUseRiskScore.mockReturnValue({ data: [], isAuthorized: false }); const { queryByText } = render( @@ -190,11 +197,13 @@ describe('', () => { expect(getByTestId(HOST_DETAILS_RELATED_USERS_TABLE_TEST_ID)).toBeInTheDocument(); }); - it('should render user risk score column when license is valid', () => { + it('should render user risk score column when license and capabilities are valid', () => { mockUseMlUserPermissions.mockReturnValue({ isPlatinumOrTrialLicense: true, capabilities: {}, }); + mockUseHasSecurityCapability.mockReturnValue(true); + const { queryAllByRole } = render( @@ -206,6 +215,21 @@ describe('', () => { expect(queryAllByRole('row')[1].textContent).toContain('Low'); }); + it('should not render host risk score column when user has no entity-risk capability', () => { + mockUseMlUserPermissions.mockReturnValue({ + isPlatinumOrTrialLicense: true, + capabilities: {}, + }); + mockUseHasSecurityCapability.mockReturnValue(false); + + const { queryAllByRole } = render( + + + + ); + expect(queryAllByRole('columnheader').length).toBe(2); + }); + it('should not render host risk score column when license is not valid', () => { const { queryAllByRole } = render( diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/host_details.tsx b/x-pack/plugins/security_solution/public/flyout/left/components/host_details.tsx index c1a399af45aea..269aff686cf61 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/components/host_details.tsx +++ b/x-pack/plugins/security_solution/public/flyout/left/components/host_details.tsx @@ -50,6 +50,7 @@ import { HOST_DETAILS_TEST_ID, HOST_DETAILS_RELATED_USERS_TABLE_TEST_ID } from ' import { ENTITY_RISK_CLASSIFICATION } from '../../../explore/components/risk_score/translations'; import { USER_RISK_TOOLTIP } from '../../../explore/users/components/all_users/translations'; import * as i18n from './translations'; +import { useHasSecurityCapability } from '../../../helper_hooks'; const HOST_DETAILS_ID = 'entities-hosts-details'; const RELATED_USERS_ID = 'entities-hosts-related-users'; @@ -77,7 +78,10 @@ export const HostDetails: React.FC = ({ hostName, timestamp }) // create a unique, but stable (across re-renders) query id const hostDetailsQueryId = useMemo(() => `${HOST_DETAILS_ID}-${uuid()}`, []); const relatedUsersQueryId = useMemo(() => `${RELATED_USERS_ID}-${uuid()}`, []); + const hasEntityAnalyticsCapability = useHasSecurityCapability('entity-analytics'); const isPlatinumOrTrialLicense = useMlCapabilities().isPlatinumOrTrialLicense; + const isEntityAnalyticsAuthorized = isPlatinumOrTrialLicense && hasEntityAnalyticsCapability; + const narrowDateRange = useCallback( (score, interval) => { const fromTo = scoreIntervalToDateTime(score, interval); @@ -151,7 +155,7 @@ export const HostDetails: React.FC = ({ hostName, timestamp }) ); }, }, - ...(isPlatinumOrTrialLicense + ...(isEntityAnalyticsAuthorized ? [ { field: 'risk', @@ -176,7 +180,7 @@ export const HostDetails: React.FC = ({ hostName, timestamp }) ] : []), ], - [isPlatinumOrTrialLicense] + [isEntityAnalyticsAuthorized] ); const relatedUsersCount = useMemo( diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/user_details.test.tsx b/x-pack/plugins/security_solution/public/flyout/left/components/user_details.test.tsx index e5bc253829373..6f449e2279195 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/components/user_details.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/left/components/user_details.test.tsx @@ -71,6 +71,8 @@ jest.mock('../../../common/components/ml/anomaly/anomaly_table_provider', () => }) => children({ anomaliesData: mockAnomalies, isLoadingAnomaliesData: false, jobNameById: {} }), })); +jest.mock('../../../helper_hooks', () => ({ useHasSecurityCapability: () => true })); + jest.mock('../../../explore/users/containers/users/observed_details'); const mockUseObservedUserDetails = useObservedUserDetails as jest.Mock; @@ -105,7 +107,7 @@ const mockRiskScoreResponse = { }, }, ], - isLicenseValid: true, + isAuthorized: true, }; const mockRelatedHostsResponse = { @@ -165,7 +167,7 @@ describe('', () => { }); it('should not render user risk score when license is not valid', () => { - mockUseRiskScore.mockReturnValue({ data: [], isLicenseValid: false }); + mockUseRiskScore.mockReturnValue({ data: [], isAuthorized: false }); const { queryByText } = render( diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/user_details.tsx b/x-pack/plugins/security_solution/public/flyout/left/components/user_details.tsx index cee8189859154..bc0066f2488fd 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/components/user_details.tsx +++ b/x-pack/plugins/security_solution/public/flyout/left/components/user_details.tsx @@ -50,6 +50,7 @@ import { USER_DETAILS_RELATED_HOSTS_TABLE_TEST_ID, USER_DETAILS_TEST_ID } from ' import { ENTITY_RISK_CLASSIFICATION } from '../../../explore/components/risk_score/translations'; import { HOST_RISK_TOOLTIP } from '../../../explore/hosts/components/hosts_table/translations'; import * as i18n from './translations'; +import { useHasSecurityCapability } from '../../../helper_hooks'; const USER_DETAILS_ID = 'entities-users-details'; const RELATED_HOSTS_ID = 'entities-users-related-hosts'; @@ -77,7 +78,11 @@ export const UserDetails: React.FC = ({ userName, timestamp }) // create a unique, but stable (across re-renders) query id const userDetailsQueryId = useMemo(() => `${USER_DETAILS_ID}-${uuid()}`, []); const relatedHostsQueryId = useMemo(() => `${RELATED_HOSTS_ID}-${uuid()}`, []); + + const hasEntityAnalyticsCapability = useHasSecurityCapability('entity-analytics'); const isPlatinumOrTrialLicense = useMlCapabilities().isPlatinumOrTrialLicense; + const isEntityAnalyticsAuthorized = isPlatinumOrTrialLicense && hasEntityAnalyticsCapability; + const narrowDateRange = useCallback( (score, interval) => { const fromTo = scoreIntervalToDateTime(score, interval); @@ -151,7 +156,7 @@ export const UserDetails: React.FC = ({ userName, timestamp }) ); }, }, - ...(isPlatinumOrTrialLicense + ...(isEntityAnalyticsAuthorized ? [ { field: 'risk', @@ -176,7 +181,7 @@ export const UserDetails: React.FC = ({ userName, timestamp }) ] : []), ], - [isPlatinumOrTrialLicense] + [isEntityAnalyticsAuthorized] ); const relatedHostsCount = useMemo( diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/host_entity_overview.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/host_entity_overview.test.tsx index b3a6a1043c030..4515e011d3790 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/host_entity_overview.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/host_entity_overview.test.tsx @@ -48,7 +48,7 @@ describe('', () => { describe('license is valid', () => { it('should render ip addresses and host risk classification', () => { mockUseHostDetails.mockReturnValue([false, { hostDetails: hostData }]); - mockUseRiskScore.mockReturnValue({ data: riskLevel, isLicenseValid: true }); + mockUseRiskScore.mockReturnValue({ data: riskLevel, isAuthorized: true }); const { getByTestId } = render( @@ -63,7 +63,7 @@ describe('', () => { it('should render correctly if returned data is null', () => { mockUseHostDetails.mockReturnValue([false, { hostDetails: null }]); - mockUseRiskScore.mockReturnValue({ data: null, isLicenseValid: true }); + mockUseRiskScore.mockReturnValue({ data: null, isAuthorized: true }); const { getByTestId } = render( @@ -79,7 +79,7 @@ describe('', () => { describe('license is not valid', () => { it('should render ip but not host risk classification', () => { mockUseHostDetails.mockReturnValue([false, { hostDetails: hostData }]); - mockUseRiskScore.mockReturnValue({ data: riskLevel, isLicenseValid: false }); + mockUseRiskScore.mockReturnValue({ data: riskLevel, isAuthorized: false }); const { getByTestId, queryByTestId } = render( @@ -92,7 +92,7 @@ describe('', () => { it('should render correctly if returned data is null', () => { mockUseHostDetails.mockReturnValue([false, { hostDetails: null }]); - mockUseRiskScore.mockReturnValue({ data: null, isLicenseValid: false }); + mockUseRiskScore.mockReturnValue({ data: null, isAuthorized: false }); const { getByTestId, queryByTestId } = render( diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/host_entity_overview.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/host_entity_overview.tsx index c7b3484f19086..14a72804ead1f 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/host_entity_overview.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/host_entity_overview.tsx @@ -66,7 +66,7 @@ export const HostEntityOverview: React.FC = ({ hostName [hostName] ); - const { data: hostRisk, isLicenseValid } = useRiskScore({ + const { data: hostRisk, isAuthorized } = useRiskScore({ filterQuery, riskEntity: RiskScoreEntity.host, skip: hostName == null, @@ -138,7 +138,7 @@ export const HostEntityOverview: React.FC = ({ hostName /> - {isLicenseValid && ( + {isAuthorized && ( ', () => { describe('license is valid', () => { it('should render ip addresses and user risk classification', () => { mockUseUserDetails.mockReturnValue([false, { userDetails: userData }]); - mockUseRiskScore.mockReturnValue({ data: riskLevel, isLicenseValid: true }); + mockUseRiskScore.mockReturnValue({ data: riskLevel, isAuthorized: true }); const { getByTestId } = render( @@ -64,7 +64,7 @@ describe('', () => { it('should render correctly if returned data is null', () => { mockUseUserDetails.mockReturnValue([false, { userDetails: null }]); - mockUseRiskScore.mockReturnValue({ data: null, isLicenseValid: true }); + mockUseRiskScore.mockReturnValue({ data: null, isAuthorized: true }); const { getByTestId } = render( @@ -80,7 +80,7 @@ describe('', () => { describe('license is not valid', () => { it('should render ip but not user risk classification', () => { mockUseUserDetails.mockReturnValue([false, { userDetails: userData }]); - mockUseRiskScore.mockReturnValue({ data: riskLevel, isLicenseValid: false }); + mockUseRiskScore.mockReturnValue({ data: riskLevel, isAuthorized: false }); const { getByTestId, queryByTestId } = render( @@ -93,7 +93,7 @@ describe('', () => { it('should render correctly if returned data is null', () => { mockUseUserDetails.mockReturnValue([false, { userDetails: null }]); - mockUseRiskScore.mockReturnValue({ data: null, isLicenseValid: false }); + mockUseRiskScore.mockReturnValue({ data: null, isAuthorized: false }); const { getByTestId, queryByTestId } = render( diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/user_entity_overview.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/user_entity_overview.tsx index af1a33580b475..da821902ff190 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/user_entity_overview.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/user_entity_overview.tsx @@ -73,7 +73,7 @@ export const UserEntityOverview: React.FC = ({ userName startDate: from, }); - const { data: userRisk, isLicenseValid } = useRiskScore({ + const { data: userRisk, isAuthorized } = useRiskScore({ filterQuery, riskEntity: RiskScoreEntity.user, timerange, @@ -138,7 +138,7 @@ export const UserEntityOverview: React.FC = ({ userName /> - {isLicenseValid && ( + {isAuthorized && ( void, () => void] => { const [isOpen, setIsOpen] = useState(false); @@ -19,3 +20,12 @@ export const useOnOpenCloseHandler = (): [boolean, () => void, () => void] => { }, []); return [isOpen, handleOnOpen, handleOnClose]; }; + +/** + * + * @param capability Main Security feature capability name. + */ +export const useHasSecurityCapability = (capability: string): boolean => { + const { capabilities } = useKibana().services.application; + return !!capabilities.siem[capability]; +}; diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/index.test.tsx index 7869a234ccd50..8af9349d9bcea 100644 --- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/index.test.tsx @@ -53,7 +53,7 @@ const defaultProps = { inspect: null, refetch: () => {}, isModuleEnabled: true, - isLicenseValid: true, + isAuthorized: true, loading: false, }; const mockUseRiskScore = useRiskScore as jest.Mock; @@ -156,7 +156,7 @@ describe.each([RiskScoreEntity.host, RiskScoreEntity.user])( { '@timestamp': '1234567899', [riskEntity]: { - name: 'testUsermame', + name: 'testUsername', risk: { rule_risks: [], calculated_level: RiskSeverity.high, diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/index.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/index.tsx index 4393a2ae506eb..332f5535a1304 100644 --- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/index.tsx @@ -111,7 +111,7 @@ const EntityAnalyticsRiskScoresComponent = ({ riskEntity }: { riskEntity: RiskSc inspect, refetch, isDeprecated, - isLicenseValid, + isAuthorized, isModuleEnabled, } = useRiskScore({ filterQuery, @@ -140,7 +140,7 @@ const EntityAnalyticsRiskScoresComponent = ({ riskEntity }: { riskEntity: RiskSc const refreshPage = useRefetchQueries(); - if (!isLicenseValid) { + if (!isAuthorized) { return null; } diff --git a/x-pack/plugins/security_solution/public/overview/components/host_overview/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/host_overview/index.test.tsx index 4f7c2882c022e..edbb6d03bf9a9 100644 --- a/x-pack/plugins/security_solution/public/overview/components/host_overview/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/host_overview/index.test.tsx @@ -21,7 +21,7 @@ const defaultProps = { inspect: null, refetch: () => {}, isModuleEnabled: true, - isLicenseValid: true, + isAuthorized: true, loading: true, }; @@ -88,7 +88,7 @@ describe('Host Summary Component', () => { data: [ { host: { - name: 'testHostmame', + name: 'testHostname', risk: { rule_risks: [], calculated_score_norm: riskScore, diff --git a/x-pack/plugins/security_solution/public/overview/components/host_overview/index.tsx b/x-pack/plugins/security_solution/public/overview/components/host_overview/index.tsx index 8b0e973f3c274..5f040fe274bb8 100644 --- a/x-pack/plugins/security_solution/public/overview/components/host_overview/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/host_overview/index.tsx @@ -98,7 +98,7 @@ export const HostOverview = React.memo( }), [from, to] ); - const { data: hostRisk, isLicenseValid } = useRiskScore({ + const { data: hostRisk, isAuthorized } = useRiskScore({ filterQuery, riskEntity: RiskScoreEntity.host, skip: hostName == null, @@ -297,7 +297,7 @@ export const HostOverview = React.memo( )} - {isLicenseValid && ( + {isAuthorized && ( {}, isModuleEnabled: true, - isLicenseValid: true, + isAuthorized: true, loading: false, }; @@ -101,7 +101,7 @@ describe('User Summary Component', () => { data: [ { user: { - name: 'testUsermame', + name: 'testUsername', risk: { rule_risks: [], calculated_level: risk, diff --git a/x-pack/plugins/security_solution/public/overview/components/user_overview/index.tsx b/x-pack/plugins/security_solution/public/overview/components/user_overview/index.tsx index 8a17d4dee8241..636551522be42 100644 --- a/x-pack/plugins/security_solution/public/overview/components/user_overview/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/user_overview/index.tsx @@ -98,7 +98,7 @@ export const UserOverview = React.memo( [from, to] ); - const { data: userRisk, isLicenseValid } = useRiskScore({ + const { data: userRisk, isAuthorized } = useRiskScore({ filterQuery, skip: userName == null, timerange, @@ -291,7 +291,7 @@ export const UserOverview = React.memo( )} - {isLicenseValid && ( + {isAuthorized && ( {}, totalCount: 0, isModuleEnabled: true, - isLicenseValid: true, + isAuthorized: true, isDeprecated: false, loading: false, }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/risk_score_field.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/risk_score_field.test.tsx index 9e759fd246178..48d927c97030c 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/risk_score_field.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/risk_score_field.test.tsx @@ -27,7 +27,7 @@ describe('RiskScoreField', () => { it('does not render content when the license is invalid', () => { const { queryByTestId } = render( - + ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/risk_score_field.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/risk_score_field.tsx index 9dce74009a0fa..d9a52b2c4c23b 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/risk_score_field.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/risk_score_field.tsx @@ -25,10 +25,10 @@ export const RiskScoreField = ({ }) => { const { euiTheme } = useEuiTheme(); const { fontSize: xsFontSize } = useEuiFontSize('xs'); - const { data: userRisk, isLicenseValid: isRiskLicenseValid } = riskScoreState; + const { data: userRisk, isAuthorized: isRiskScoreAuthorized } = riskScoreState; const userRiskData = userRisk && userRisk.length > 0 ? userRisk[0] : undefined; - if (!isRiskLicenseValid) { + if (!isRiskScoreAuthorized) { return null; } diff --git a/x-pack/plugins/security_solution_serverless/public/upselling/register_upsellings.tsx b/x-pack/plugins/security_solution_serverless/public/upselling/register_upsellings.tsx index a2ff9066f644c..7f5551ed8a1c3 100644 --- a/x-pack/plugins/security_solution_serverless/public/upselling/register_upsellings.tsx +++ b/x-pack/plugins/security_solution_serverless/public/upselling/register_upsellings.tsx @@ -4,9 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { lazy } from 'react'; -import { SecurityPageName } from '@kbn/security-solution-navigation'; -import { AppFeatureKey } from '@kbn/security-solution-plugin/common'; +import type { SecurityPageName, AppFeatureKey } from '@kbn/security-solution-plugin/common'; import type { UpsellingService, PageUpsellings, @@ -16,9 +14,6 @@ import type { import type { SecurityProductTypes } from '../../common/config'; import { getProductAppFeatures } from '../../common/pli/pli_features'; -const GenericUpsellingPageLazy = lazy(() => import('./pages/generic_upselling_page')); -const GenericUpsellingSectionLazy = lazy(() => import('./pages/generic_upselling_section')); - interface UpsellingsConfig { pli: AppFeatureKey; component: React.ComponentType; @@ -59,18 +54,22 @@ export const registerUpsellings = ( // Upsellings for entire pages, linked to a SecurityPageName export const upsellingPages: UpsellingPages = [ - { - pageName: SecurityPageName.entityAnalytics, - pli: AppFeatureKey.advancedInsights, - component: () => , - }, + // Sample code for registering a Upselling page + // Make sure the component is lazy loaded `const GenericUpsellingPageLazy = lazy(() => import('./pages/generic_upselling_page'));` + // { + // pageName: SecurityPageName.entityAnalytics, + // pli: AppFeatureKey.advancedInsights, + // component: () => , + // }, ]; // Upsellings for sections, linked by arbitrary ids export const upsellingSections: UpsellingSections = [ - { - id: 'entity_analytics_panel', - pli: AppFeatureKey.advancedInsights, - component: () => , - }, + // Sample code for registering a Upselling section + // Make sure the component is lazy loaded `const GenericUpsellingSectionLazy = lazy(() => import('./pages/generic_upselling_section'));` + // { + // id: 'entity_analytics_panel', + // pli: AppFeatureKey.advancedInsights, + // component: () => , + // }, ];