Skip to content

Commit

Permalink
add alert on detections
Browse files Browse the repository at this point in the history
  • Loading branch information
XavierM committed Jan 16, 2020
1 parent 41316ba commit 7660edb
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 54 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { RedirectToHostsPage, RedirectToHostDetailsPage } from './redirect_to_ho
import { RedirectToNetworkPage } from './redirect_to_network';
import { RedirectToOverviewPage } from './redirect_to_overview';
import { RedirectToTimelinesPage } from './redirect_to_timelines';
import { DetectionEngineTab } from '../../pages/detection_engine/detection_engine';

interface LinkToPageProps {
match: RouteMatch<{}>;
Expand Down Expand Up @@ -60,7 +61,7 @@ export const LinkToPage = React.memo<LinkToPageProps>(({ match }) => (
<Route
component={RedirectToDetectionEnginePage}
exact
path={`${match.url}/:pageName(${SiemPageName.detectionEngine})`}
path={`${match.url}/:pageName(${SiemPageName.detectionEngine})/:tabName(${DetectionEngineTab.alert}|${DetectionEngineTab.signal})`}
strict
/>
<Route
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,28 @@
import React from 'react';
import { RouteComponentProps } from 'react-router-dom';

import { DetectionEngineTab } from '../../pages/detection_engine/detection_engine';
import { RedirectWrapper } from './redirect_wrapper';

export type DetectionEngineComponentProps = RouteComponentProps<{
tabName: DetectionEngineTab;
search: string;
}>;

export const DETECTION_ENGINE_PAGE_NAME = 'detection-engine';

export const RedirectToDetectionEnginePage = ({
match: {
params: { tabName },
},
location: { search },
}: DetectionEngineComponentProps) => (
<RedirectWrapper to={`/${DETECTION_ENGINE_PAGE_NAME}${search}`} />
);
}: DetectionEngineComponentProps) => {
const defaultSelectedTab = DetectionEngineTab.signal;
const selectedTab = tabName ? tabName : defaultSelectedTab;
const to = `/${DETECTION_ENGINE_PAGE_NAME}/${selectedTab}${search}`;

return <RedirectWrapper to={to} />;
};

export const RedirectToRulesPage = ({ location: { search } }: DetectionEngineComponentProps) => {
return <RedirectWrapper to={`/${DETECTION_ENGINE_PAGE_NAME}/rules${search}`} />;
Expand All @@ -28,7 +37,7 @@ export const RedirectToRulesPage = ({ location: { search } }: DetectionEngineCom
export const RedirectToCreateRulePage = ({
location: { search },
}: DetectionEngineComponentProps) => {
return <RedirectWrapper to={`/${DETECTION_ENGINE_PAGE_NAME}/rules/create-rule${search}`} />;
return <RedirectWrapper to={`/${DETECTION_ENGINE_PAGE_NAME}/rules/create${search}`} />;
};

export const RedirectToRuleDetailsPage = ({
Expand All @@ -44,6 +53,8 @@ export const RedirectToEditRulePage = ({ location: { search } }: DetectionEngine
};

export const getDetectionEngineUrl = () => `#/link-to/${DETECTION_ENGINE_PAGE_NAME}`;
export const getDetectionEngineAlertUrl = () =>
`#/link-to/${DETECTION_ENGINE_PAGE_NAME}/${DetectionEngineTab.alert}`;
export const getRulesUrl = () => `#/link-to/${DETECTION_ENGINE_PAGE_NAME}/rules`;
export const getCreateRuleUrl = () => `#/link-to/${DETECTION_ENGINE_PAGE_NAME}/rules/create-rule`;
export const getRuleDetailsUrl = () => `#/link-to/${DETECTION_ENGINE_PAGE_NAME}/rules/rule-details`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,30 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { EuiButton, EuiSpacer } from '@elastic/eui';
import React, { useCallback } from 'react';
import { EuiButton, EuiSpacer, EuiTab, EuiTabs } from '@elastic/eui';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useParams } from 'react-router-dom';
import { StickyContainer } from 'react-sticky';

import { connect } from 'react-redux';
import { ActionCreator } from 'typescript-fsa';

import { Query } from '../../../../../../../src/plugins/data/common/query';
import { esFilters } from '../../../../../../../src/plugins/data/common/es_query';

import { GlobalTime } from '../../containers/global_time';
import { indicesExistOrDataTemporarilyUnavailable, WithSource } from '../../containers/source';
import { AlertsTable } from '../../components/alerts_viewer/alerts_table';
import { FiltersGlobal } from '../../components/filters_global';
import { HeaderPage } from '../../components/header_page';
import { SiemSearchBar } from '../../components/search_bar';
import { WrapperPage } from '../../components/wrapper_page';
import { GlobalTime } from '../../containers/global_time';
import { indicesExistOrDataTemporarilyUnavailable, WithSource } from '../../containers/source';
import { SpyRoute } from '../../utils/route/spy_routes';

import { Query } from '../../../../../../../src/plugins/data/common/query';
import { esFilters } from '../../../../../../../src/plugins/data/common/es_query';
import { State } from '../../store';
import { inputsSelectors } from '../../store/inputs';
import { setAbsoluteRangeDatePicker as dispatchSetAbsoluteRangeDatePicker } from '../../store/inputs/actions';
import { SpyRoute } from '../../utils/route/spy_routes';
import { InputsModelId } from '../../store/inputs/constants';
import { InputsRange } from '../../store/inputs/model';
import { AlertsByCategory } from '../overview/alerts_by_category';
import { useSignalInfo } from './components/signals_info';
import { SignalsTable } from './components/signals';
import { NoWriteSignalsCallOut } from './components/no_write_signals_callout';
Expand All @@ -35,6 +38,12 @@ import { DetectionEngineEmptyPage } from './detection_engine_empty_page';
import { DetectionEngineNoIndex } from './detection_engine_no_signal_index';
import { DetectionEngineUserUnauthenticated } from './detection_engine_user_unauthenticated';
import * as i18n from './translations';
import { DETECTION_ENGINE_PAGE_NAME } from '../../components/link_to/redirect_to_detection_engine';

export enum DetectionEngineTab {
signal = 'signal',
alert = 'alert',
}

interface ReduxProps {
filters: esFilters.Filter[];
Expand All @@ -51,8 +60,23 @@ export interface DispatchProps {

type DetectionEngineComponentProps = ReduxProps & DispatchProps;

const detectionsTabs = [
{
id: DetectionEngineTab.signal,
name: i18n.SIGNAL,
disabled: false,
},
{
id: DetectionEngineTab.alert,
name: i18n.ALERT,
disabled: false,
},
];

const DetectionEngineComponent = React.memo<DetectionEngineComponentProps>(
({ filters, query, setAbsoluteRangeDatePicker }) => {
const { tabName = DetectionEngineTab.signal } = useParams();
const [selectedTab, setSelectedTab] = useState(tabName);
const {
loading,
isSignalIndexExists,
Expand Down Expand Up @@ -87,6 +111,31 @@ const DetectionEngineComponent = React.memo<DetectionEngineComponentProps>(
</WrapperPage>
);
}

useEffect(() => {
if (selectedTab !== tabName) {
setSelectedTab(tabName);
}
}, [selectedTab, setSelectedTab, tabName]);

const tabs = useMemo(
() => (
<EuiTabs>
{detectionsTabs.map(tab => (
<EuiTab
isSelected={tab.id === selectedTab}
disabled={tab.disabled}
key={tab.name}
href={`#/${DETECTION_ENGINE_PAGE_NAME}/${tab.id}`}
>
{tab.name}
</EuiTab>
))}
</EuiTabs>
),
[detectionsTabs, selectedTab]
);

return (
<>
{hasIndexWrite != null && !hasIndexWrite && <NoWriteSignalsCallOut />}
Expand Down Expand Up @@ -117,26 +166,49 @@ const DetectionEngineComponent = React.memo<DetectionEngineComponentProps>(
</HeaderPage>

<GlobalTime>
{({ to, from }) => (
{({ to, from, deleteQuery, setQuery }) => (
<>
<SignalsHistogramPanel
filters={filters}
from={from}
loadingInitial={loading}
query={query}
stackByOptions={signalsHistogramOptions}
to={to}
updateDateRange={updateDateRangeCallback}
/>
{tabs}
<EuiSpacer />
<SignalsTable
loading={loading}
hasIndexWrite={hasIndexWrite ?? false}
canUserCRUD={canUserCRUD ?? false}
from={from}
signalsIndex={signalIndexName ?? ''}
to={to}
/>
{selectedTab === DetectionEngineTab.signal && (
<>
<SignalsHistogramPanel
filters={filters}
from={from}
loadingInitial={loading}
query={query}
stackByOptions={signalsHistogramOptions}
to={to}
updateDateRange={updateDateRangeCallback}
/>
<EuiSpacer size="l" />
<SignalsTable
loading={loading}
hasIndexWrite={hasIndexWrite ?? false}
canUserCRUD={canUserCRUD ?? false}
from={from}
signalsIndex={signalIndexName ?? ''}
to={to}
/>
</>
)}
{selectedTab === DetectionEngineTab.alert && (
<>
<AlertsByCategory
deleteQuery={deleteQuery}
filters={filters}
from={from}
hideHeaderChildren={true}
indexPattern={indexPattern}
query={query}
setAbsoluteRangeDatePicker={setAbsoluteRangeDatePicker!}
setQuery={setQuery}
to={to}
/>
<EuiSpacer size="l" />
<AlertsTable endDate={to} startDate={from} />
</>
)}
</>
)}
</GlobalTime>
Expand Down
15 changes: 11 additions & 4 deletions x-pack/legacy/plugins/siem/public/pages/detection_engine/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import React from 'react';
import { Redirect, Route, Switch, RouteComponentProps } from 'react-router-dom';

import { CreateRuleComponent } from './rules/create';
import { DetectionEngine } from './detection_engine';
import { DetectionEngine, DetectionEngineTab } from './detection_engine';
import { EditRuleComponent } from './rules/edit';
import { RuleDetails } from './rules/details';
import { RulesComponent } from './rules';
Expand All @@ -21,7 +21,11 @@ type Props = Partial<RouteComponentProps<{}>> & { url: string };
export const DetectionEngineContainer = React.memo<Props>(() => (
<ManageUserInfo>
<Switch>
<Route exact path={detectionEnginePath} strict>
<Route
exact
path={`${detectionEnginePath}/:tabName(${DetectionEngineTab.signal}|${DetectionEngineTab.alert})`}
strict
>
<DetectionEngine />
</Route>
<Route exact path={`${detectionEnginePath}/rules`}>
Expand All @@ -30,7 +34,7 @@ export const DetectionEngineContainer = React.memo<Props>(() => (
<Route exact path={`${detectionEnginePath}/rules/create`}>
<CreateRuleComponent />
</Route>
<Route exact path={`${detectionEnginePath}/rules/id/:ruleId`}>
<Route exact path={`${detectionEnginePath}/rules/id/:ruleId/`}>
<RuleDetails />
</Route>
<Route exact path={`${detectionEnginePath}/rules/id/:ruleId/edit`}>
Expand All @@ -39,7 +43,10 @@ export const DetectionEngineContainer = React.memo<Props>(() => (
<Route
path="/detection-engine/"
render={({ location: { search = '' } }) => (
<Redirect from="/detection-engine/" to={`/detection-engine${search}`} />
<Redirect
from="/detection-engine/"
to={`/detection-engine/${DetectionEngineTab.signal}${search}`}
/>
)}
/>
</Switch>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
EuiSpacer,
EuiHealth,
EuiTab,
EuiTabs,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import React, { memo, useCallback, useMemo, useState } from 'react';
Expand Down Expand Up @@ -187,17 +188,20 @@ const RuleDetailsComponent = memo<RuleDetailsComponentProps>(
: 'subdued';

const tabs = useMemo(
() =>
ruleDetailTabs.map(tab => (
<EuiTab
onClick={() => setRuleDetailTab(tab.id)}
isSelected={tab.id === ruleDetailTab}
disabled={tab.disabled}
key={tab.name}
>
{tab.name}
</EuiTab>
)),
() => (
<EuiTabs>
{ruleDetailTabs.map(tab => (
<EuiTab
onClick={() => setRuleDetailTab(tab.id)}
isSelected={tab.id === ruleDetailTab}
disabled={tab.disabled}
key={tab.name}
>
{tab.name}
</EuiTab>
))}
</EuiTabs>
),
[ruleDetailTabs, ruleDetailTab, setRuleDetailTab]
);
const ruleError = useMemo(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,12 @@ export const SIGNAL = i18n.translate('xpack.siem.detectionEngine.signalTitle', {
defaultMessage: 'Signals',
});

export const ALERT = i18n.translate('xpack.siem.detectionEngine.signalTitle', {
defaultMessage: 'Third-party alerts',
});

export const BUTTON_MANAGE_RULES = i18n.translate('xpack.siem.detectionEngine.buttonManageRules', {
defaultMessage: 'Manage rules',
defaultMessage: 'Manage signal detection rules',
});

export const PANEL_SUBTITLE_SHOWING = i18n.translate(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
UNIT,
} from '../../../components/alerts_viewer/translations';
import { alertsStackByOptions } from '../../../components/alerts_viewer';
import { getTabsOnHostsUrl } from '../../../components/link_to/redirect_to_hosts';
import { getDetectionEngineAlertUrl } from '../../../components/link_to/redirect_to_detection_engine';
import { MatrixHistogramContainer } from '../../../containers/matrix_histogram';
import { MatrixHistogramGqlQuery } from '../../../containers/matrix_histogram/index.gql_query';
import { MatrixHistogramOption } from '../../../components/matrix_histogram/types';
Expand All @@ -25,7 +25,7 @@ import { convertToBuildEsQuery } from '../../../lib/keury';
import { SetAbsoluteRangeDatePicker } from '../../network/types';
import { esQuery } from '../../../../../../../../src/plugins/data/public';
import { inputsModel } from '../../../store';
import { HostsTableType, HostsType } from '../../../store/hosts/model';
import { HostsType } from '../../../store/hosts/model';
import { DEFAULT_NUMBER_FORMAT } from '../../../../common/constants';

import * as i18n from '../translations';
Expand All @@ -39,6 +39,7 @@ interface Props {
deleteQuery?: ({ id }: { id: string }) => void;
filters?: esFilters.Filter[];
from: number;
hideHeaderChildren?: boolean;
indexPattern: IIndexPattern;
query?: Query;
setAbsoluteRangeDatePicker: SetAbsoluteRangeDatePicker;
Expand All @@ -60,6 +61,7 @@ export const AlertsByCategory = React.memo<Props>(
deleteQuery,
filters = NO_FILTERS,
from,
hideHeaderChildren = false,
indexPattern,
query = DEFAULT_QUERY,
setAbsoluteRangeDatePicker,
Expand All @@ -76,9 +78,7 @@ export const AlertsByCategory = React.memo<Props>(
);
const alertsCountViewAlertsButton = useMemo(
() => (
<ViewAlertsButton href={getTabsOnHostsUrl(HostsTableType.alerts)}>
{i18n.VIEW_ALERTS}
</ViewAlertsButton>
<ViewAlertsButton href={getDetectionEngineAlertUrl()}>{i18n.VIEW_ALERTS}</ViewAlertsButton>
),
[]
);
Expand Down Expand Up @@ -106,7 +106,7 @@ export const AlertsByCategory = React.memo<Props>(
queries: [query],
filters,
})}
headerChildren={alertsCountViewAlertsButton}
headerChildren={hideHeaderChildren ? null : alertsCountViewAlertsButton}
id={ID}
isAlertsHistogram={true}
legendPosition={'right'}
Expand Down

0 comments on commit 7660edb

Please sign in to comment.