Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Security Solutions] Add PLI authorisation for Advanced Insights (Entity Risk) #161190

Merged
merged 9 commits into from
Jul 26, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 (
<>
<EuiHorizontalRule />
Expand All @@ -161,21 +168,25 @@ const ThreatSummaryViewComponent: React.FC<{
<EuiSpacer size="m" />

<EuiFlexGroup direction="column" gutterSize="m" style={{ flexGrow: 0 }}>
<EuiFlexItem grow={false}>
<RiskSummary
riskEntity={RiskScoreEntity.host}
risk={hostRisk}
originalRisk={originalHostRisk}
/>
</EuiFlexItem>

<EuiFlexItem grow={false}>
<RiskSummary
riskEntity={RiskScoreEntity.user}
risk={userRisk}
originalRisk={originalUserRisk}
/>
</EuiFlexItem>
{hasEntityAnalyticsCapability && (
<>
<EuiFlexItem grow={false}>
<RiskSummary
riskEntity={RiskScoreEntity.host}
risk={hostRisk}
originalRisk={originalHostRisk}
/>
</EuiFlexItem>

<EuiFlexItem grow={false}>
<RiskSummary
riskEntity={RiskScoreEntity.user}
risk={userRisk}
originalRisk={originalUserRisk}
/>
</EuiFlexItem>
</>
)}

<EnrichmentSummary
browserFields={browserFields}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ const EventDetailsComponent: React.FC<Props> = ({

const enrichmentCount = allEnrichments.length;

const { hostRisk, userRisk, isLicenseValid } = useRiskScoreData(data);
const { hostRisk, userRisk, isAuthorized } = useRiskScoreData(data);

const renderer = useMemo(
() =>
Expand All @@ -212,9 +212,9 @@ const EventDetailsComponent: React.FC<Props> = ({

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'
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const defaultResult = {
data: [],
inspect: {},
isInspected: false,
isLicenseValid: true,
isAuthorized: true,
isModuleEnabled: true,
refetch: () => {},
totalCount: 0,
Expand Down Expand Up @@ -55,7 +55,7 @@ describe('useRiskScoreData', () => {
expect(result.current).toEqual({
hostRisk: defaultRisk,
userRisk: defaultRisk,
isLicenseValid: true,
isAuthorized: true,
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export const useRiskScoreData = (data: TimelineEventsDetailsItem[]) => {
const {
data: hostRiskData,
loading: hostRiskLoading,
isLicenseValid: isHostLicenseValid,
isAuthorized: isHostRiskScoreAuthorized,
isModuleEnabled: isHostRiskModuleEnabled,
} = useRiskScore({
filterQuery: hostNameFilterQuery,
Expand All @@ -57,7 +57,7 @@ export const useRiskScoreData = (data: TimelineEventsDetailsItem[]) => {
const {
data: userRiskData,
loading: userRiskLoading,
isLicenseValid: isUserLicenseValid,
isAuthorized: isUserRiskScoreAuthorized,
isModuleEnabled: isUserRiskModuleEnabled,
} = useRiskScore({
filterQuery: userNameFilterQuery,
Expand All @@ -75,5 +75,9 @@ export const useRiskScoreData = (data: TimelineEventsDetailsItem[]) => {
[userRiskLoading, isUserRiskModuleEnabled, userRiskData]
);

return { userRisk, hostRisk, isLicenseValid: isHostLicenseValid && isUserLicenseValid };
return {
userRisk,
hostRisk,
isAuthorized: isHostRiskScoreAuthorized && isUserRiskScoreAuthorized,
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -176,6 +177,16 @@ export const createStartServicesMock = (
})),
},
},
application: {
...core.application,
capabilities: {
...core.application.capabilities,
[SERVER_APP_ID]: {
crud: true,
read: true,
},
},
},
security,
storage,
fleet,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ describe('AlertDetailsPage - SummaryTab - HostPanel', () => {
inspect: null,
refetch: () => {},
isModuleEnabled: true,
isLicenseValid: true,
isAuthorized: true,
loading: false,
};
const HostPanelWithDefaultProps = (propOverrides: Partial<HostPanelProps>) => (
Expand Down Expand Up @@ -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(<HostPanelWithDefaultProps />);
Expand All @@ -119,7 +119,7 @@ describe('AlertDetailsPage - SummaryTab - HostPanel', () => {

mockUseRiskScore.mockReturnValue({
...defaultRiskReturnValues,
isLicenseValid: true,
isAuthorized: true,
data: [
{
host: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
});
Expand Down Expand Up @@ -149,7 +149,7 @@ export const HostPanel = React.memo(
)}
</EuiFlexGroup>
<EuiSpacer size="l" />
{isRiskLicenseValid && (
{isRiskScoreAuthorized && (
<>
<EuiFlexGroup data-test-subj="host-panel-risk">
{hostRiskScore && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
});
Expand Down Expand Up @@ -114,7 +114,7 @@ export const UserPanel = React.memo(
</UserPanelSection>
</EuiFlexGroup>
<EuiSpacer size="l" />
{isRiskLicenseValid && (
{isRiskScoreAuthorized && (
<>
<EuiFlexGroup data-test-subj="user-panel-risk">
{userRiskScore && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ describe('AlertDetailsPage - SummaryTab - UserPanel', () => {
inspect: null,
refetch: () => {},
isModuleEnabled: true,
isLicenseValid: true,
isAuthorized: true,
loading: false,
};
const UserPanelWithDefaultProps = (propOverrides: Partial<UserPanelProps>) => (
Expand Down Expand Up @@ -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(<UserPanelWithDefaultProps />);
Expand All @@ -77,7 +77,7 @@ describe('AlertDetailsPage - SummaryTab - UserPanel', () => {

mockUseRiskScore.mockReturnValue({
...defaultRiskReturnValues,
isLicenseValid: true,
isAuthorized: true,
data: [
{
user: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -130,8 +129,8 @@ const RiskDetailsTabBodyComponent: React.FC<
isDeprecated: isDeprecated && !loading,
};

if (RiskScoreUpsell) {
return <RiskScoreUpsell />;
if (!isAuthorized) {
return <>{'TODO: Add RiskScore Upsell'}</>;
}

if (status.isDisabled || status.isDeprecated) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,15 @@ let appToastsMock: jest.Mocked<ReturnType<typeof useAppToastsMock.create>>;
const defaultFeatureStatus = {
isLoading: false,
isDeprecated: false,
isLicenseValid: true,
isAuthorized: true,
isEnabled: true,
refetch: () => {},
};
const defaultRisk = {
data: undefined,
inspect: {},
isInspected: false,
isLicenseValid: true,
isAuthorized: true,
isModuleEnabled: true,
isDeprecated: false,
totalCount: 0,
Expand Down Expand Up @@ -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,
Expand All @@ -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,
});
});
Expand Down
Loading
Loading