[SIEMDPOINT][WIP] Add Management section and move Policy related views (#67417)
* Add Management top-level nav tab item * Move of Policy related views to `management` * Enhance PageView component to support sub-tabs
This commit is contained in:
parent
0712741bb3
commit
ae724f1035
67 changed files with 815 additions and 363 deletions
|
@ -14,6 +14,7 @@ import {
|
|||
} from '../../common/components/link_to';
|
||||
import * as i18n from './translations';
|
||||
import { SiemPageName, SiemNavTab } from '../types';
|
||||
import { getManagementUrl } from '../../management';
|
||||
|
||||
export const navTabs: SiemNavTab = {
|
||||
[SiemPageName.overview]: {
|
||||
|
@ -58,4 +59,11 @@ export const navTabs: SiemNavTab = {
|
|||
disabled: false,
|
||||
urlKey: 'case',
|
||||
},
|
||||
[SiemPageName.management]: {
|
||||
id: SiemPageName.management,
|
||||
name: i18n.MANAGEMENT,
|
||||
href: getManagementUrl({ name: 'default' }),
|
||||
disabled: false,
|
||||
urlKey: SiemPageName.management,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -29,3 +29,7 @@ export const TIMELINES = i18n.translate('xpack.siem.navigation.timelines', {
|
|||
export const CASE = i18n.translate('xpack.siem.navigation.case', {
|
||||
defaultMessage: 'Cases',
|
||||
});
|
||||
|
||||
export const MANAGEMENT = i18n.translate('xpack.siem.navigation.management', {
|
||||
defaultMessage: 'Management',
|
||||
});
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
|
||||
import { Reducer, AnyAction, Middleware, Dispatch } from 'redux';
|
||||
|
||||
import { NavTab } from '../common/components/navigation/types';
|
||||
import { HostsState } from '../hosts/store';
|
||||
import { NetworkState } from '../network/store';
|
||||
|
@ -15,7 +14,7 @@ import { Immutable } from '../../common/endpoint/types';
|
|||
import { AlertListState } from '../../common/endpoint_alerts/types';
|
||||
import { AppAction } from '../common/store/actions';
|
||||
import { HostState } from '../endpoint_hosts/types';
|
||||
import { PolicyDetailsState, PolicyListState } from '../endpoint_policy/types';
|
||||
import { ManagementState } from '../management/store/types';
|
||||
|
||||
export enum SiemPageName {
|
||||
overview = 'overview',
|
||||
|
@ -24,6 +23,7 @@ export enum SiemPageName {
|
|||
detections = 'detections',
|
||||
timelines = 'timelines',
|
||||
case = 'case',
|
||||
management = 'management',
|
||||
}
|
||||
|
||||
export type SiemNavTabKey =
|
||||
|
@ -32,14 +32,15 @@ export type SiemNavTabKey =
|
|||
| SiemPageName.network
|
||||
| SiemPageName.detections
|
||||
| SiemPageName.timelines
|
||||
| SiemPageName.case;
|
||||
| SiemPageName.case
|
||||
| SiemPageName.management;
|
||||
|
||||
export type SiemNavTab = Record<SiemNavTabKey, NavTab>;
|
||||
|
||||
export interface SecuritySubPluginStore<K extends SecuritySubPluginKeyStore, T> {
|
||||
initialState: Record<K, T>;
|
||||
reducer: Record<K, Reducer<T, AnyAction>>;
|
||||
middleware?: Middleware<{}, State, Dispatch<AppAction | Immutable<AppAction>>>;
|
||||
middleware?: Array<Middleware<{}, State, Dispatch<AppAction | Immutable<AppAction>>>>;
|
||||
}
|
||||
|
||||
export interface SecuritySubPlugin {
|
||||
|
@ -52,8 +53,7 @@ type SecuritySubPluginKeyStore =
|
|||
| 'timeline'
|
||||
| 'hostList'
|
||||
| 'alertList'
|
||||
| 'policyDetails'
|
||||
| 'policyList';
|
||||
| 'management';
|
||||
export interface SecuritySubPluginWithStore<K extends SecuritySubPluginKeyStore, T>
|
||||
extends SecuritySubPlugin {
|
||||
store: SecuritySubPluginStore<K, T>;
|
||||
|
@ -67,8 +67,7 @@ export interface SecuritySubPlugins extends SecuritySubPlugin {
|
|||
timeline: TimelineState;
|
||||
alertList: Immutable<AlertListState>;
|
||||
hostList: Immutable<HostState>;
|
||||
policyDetails: Immutable<PolicyDetailsState>;
|
||||
policyList: Immutable<PolicyListState>;
|
||||
management: ManagementState;
|
||||
};
|
||||
reducer: {
|
||||
hosts: Reducer<HostsState, AnyAction>;
|
||||
|
@ -76,8 +75,7 @@ export interface SecuritySubPlugins extends SecuritySubPlugin {
|
|||
timeline: Reducer<TimelineState, AnyAction>;
|
||||
alertList: ImmutableReducer<AlertListState, AppAction>;
|
||||
hostList: ImmutableReducer<HostState, AppAction>;
|
||||
policyDetails: ImmutableReducer<PolicyDetailsState, AppAction>;
|
||||
policyList: ImmutableReducer<PolicyListState, AppAction>;
|
||||
management: ImmutableReducer<ManagementState, AppAction>;
|
||||
};
|
||||
middlewares: Array<Middleware<{}, State, Dispatch<AppAction | Immutable<AppAction>>>>;
|
||||
};
|
||||
|
|
|
@ -21,6 +21,10 @@ exports[`PageView component should display body header custom element 1`] = `
|
|||
background: none;
|
||||
}
|
||||
|
||||
.c0 .endpoint-navTabs {
|
||||
margin-left: 24px;
|
||||
}
|
||||
|
||||
<PageView
|
||||
bodyHeader={
|
||||
<p>
|
||||
|
@ -112,6 +116,10 @@ exports[`PageView component should display body header wrapped in EuiTitle 1`] =
|
|||
background: none;
|
||||
}
|
||||
|
||||
.c0 .endpoint-navTabs {
|
||||
margin-left: 24px;
|
||||
}
|
||||
|
||||
<PageView
|
||||
bodyHeader="body header"
|
||||
viewType="list"
|
||||
|
@ -206,6 +214,10 @@ exports[`PageView component should display header left and right 1`] = `
|
|||
background: none;
|
||||
}
|
||||
|
||||
.c0 .endpoint-navTabs {
|
||||
margin-left: 24px;
|
||||
}
|
||||
|
||||
<PageView
|
||||
headerLeft="page title"
|
||||
headerRight="right side actions"
|
||||
|
@ -315,6 +327,10 @@ exports[`PageView component should display only body if not header props used 1`
|
|||
background: none;
|
||||
}
|
||||
|
||||
.c0 .endpoint-navTabs {
|
||||
margin-left: 24px;
|
||||
}
|
||||
|
||||
<PageView
|
||||
viewType="list"
|
||||
>
|
||||
|
@ -383,6 +399,10 @@ exports[`PageView component should display only header left 1`] = `
|
|||
background: none;
|
||||
}
|
||||
|
||||
.c0 .endpoint-navTabs {
|
||||
margin-left: 24px;
|
||||
}
|
||||
|
||||
<PageView
|
||||
headerLeft="page title"
|
||||
viewType="list"
|
||||
|
@ -481,6 +501,10 @@ exports[`PageView component should display only header right but include an empt
|
|||
background: none;
|
||||
}
|
||||
|
||||
.c0 .endpoint-navTabs {
|
||||
margin-left: 24px;
|
||||
}
|
||||
|
||||
<PageView
|
||||
headerRight="right side actions"
|
||||
viewType="list"
|
||||
|
@ -576,6 +600,10 @@ exports[`PageView component should pass through EuiPage props 1`] = `
|
|||
background: none;
|
||||
}
|
||||
|
||||
.c0 .endpoint-navTabs {
|
||||
margin-left: 24px;
|
||||
}
|
||||
|
||||
<PageView
|
||||
aria-label="test-aria-label-here"
|
||||
className="test-class-name-here"
|
||||
|
@ -661,6 +689,10 @@ exports[`PageView component should use custom element for header left and not wr
|
|||
background: none;
|
||||
}
|
||||
|
||||
.c0 .endpoint-navTabs {
|
||||
margin-left: 24px;
|
||||
}
|
||||
|
||||
<PageView
|
||||
headerLeft={
|
||||
<p>
|
||||
|
|
|
@ -14,10 +14,13 @@ import {
|
|||
EuiPageHeader,
|
||||
EuiPageHeaderSection,
|
||||
EuiPageProps,
|
||||
EuiTab,
|
||||
EuiTabs,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import React, { memo, ReactNode } from 'react';
|
||||
import React, { memo, MouseEventHandler, ReactNode, useMemo } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { EuiTabProps } from '@elastic/eui/src/components/tabs/tab';
|
||||
|
||||
const StyledEuiPage = styled(EuiPage)`
|
||||
&.endpoint--isListView {
|
||||
|
@ -39,6 +42,9 @@ const StyledEuiPage = styled(EuiPage)`
|
|||
background: none;
|
||||
}
|
||||
}
|
||||
.endpoint-navTabs {
|
||||
margin-left: ${(props) => props.theme.eui.euiSizeL};
|
||||
}
|
||||
`;
|
||||
|
||||
const isStringOrNumber = /(string|number)/;
|
||||
|
@ -74,69 +80,94 @@ export const PageViewBodyHeaderTitle = memo<{ children: ReactNode }>(
|
|||
);
|
||||
PageViewBodyHeaderTitle.displayName = 'PageViewBodyHeaderTitle';
|
||||
|
||||
export type PageViewProps = EuiPageProps & {
|
||||
/**
|
||||
* The type of view
|
||||
*/
|
||||
viewType: 'list' | 'details';
|
||||
/**
|
||||
* content to be placed on the left side of the header. If a `string` is used, then it will
|
||||
* be wrapped with `<EuiTitle><h1></h1></EuiTitle>`, else it will just be used as is.
|
||||
*/
|
||||
headerLeft?: ReactNode;
|
||||
/** Content for the right side of the header */
|
||||
headerRight?: ReactNode;
|
||||
/**
|
||||
* body (sub-)header section. If a `string` is used, then it will be wrapped with
|
||||
* `<EuiTitle><h2></h2></EuiTitle>`
|
||||
*/
|
||||
bodyHeader?: ReactNode;
|
||||
/**
|
||||
* The list of tab navigation items
|
||||
*/
|
||||
tabs?: Array<
|
||||
EuiTabProps & {
|
||||
name: ReactNode;
|
||||
id: string;
|
||||
href?: string;
|
||||
onClick?: MouseEventHandler<HTMLAnchorElement | HTMLButtonElement>;
|
||||
}
|
||||
>;
|
||||
children?: ReactNode;
|
||||
};
|
||||
|
||||
/**
|
||||
* Page View layout for use in Endpoint
|
||||
*/
|
||||
export const PageView = memo<
|
||||
EuiPageProps & {
|
||||
/**
|
||||
* The type of view
|
||||
*/
|
||||
viewType: 'list' | 'details';
|
||||
/**
|
||||
* content to be placed on the left side of the header. If a `string` is used, then it will
|
||||
* be wrapped with `<EuiTitle><h1></h1></EuiTitle>`, else it will just be used as is.
|
||||
*/
|
||||
headerLeft?: ReactNode;
|
||||
/** Content for the right side of the header */
|
||||
headerRight?: ReactNode;
|
||||
/**
|
||||
* body (sub-)header section. If a `string` is used, then it will be wrapped with
|
||||
* `<EuiTitle><h2></h2></EuiTitle>`
|
||||
*/
|
||||
bodyHeader?: ReactNode;
|
||||
children?: ReactNode;
|
||||
}
|
||||
>(({ viewType, children, headerLeft, headerRight, bodyHeader, ...otherProps }) => {
|
||||
return (
|
||||
<StyledEuiPage
|
||||
className={(viewType === 'list' && 'endpoint--isListView') || 'endpoint--isDetailsView'}
|
||||
{...otherProps}
|
||||
>
|
||||
<EuiPageBody>
|
||||
{(headerLeft || headerRight) && (
|
||||
<EuiPageHeader className="endpoint-header">
|
||||
<EuiPageHeaderSection data-test-subj="pageViewHeaderLeft">
|
||||
{isStringOrNumber.test(typeof headerLeft) ? (
|
||||
<PageViewHeaderTitle>{headerLeft}</PageViewHeaderTitle>
|
||||
) : (
|
||||
headerLeft
|
||||
)}
|
||||
</EuiPageHeaderSection>
|
||||
{headerRight && (
|
||||
<EuiPageHeaderSection data-test-subj="pageViewHeaderRight">
|
||||
{headerRight}
|
||||
</EuiPageHeaderSection>
|
||||
)}
|
||||
</EuiPageHeader>
|
||||
)}
|
||||
<EuiPageContent className="endpoint-page-content">
|
||||
{bodyHeader && (
|
||||
<EuiPageContentHeader>
|
||||
<EuiPageContentHeaderSection data-test-subj="pageViewBodyTitleArea">
|
||||
{isStringOrNumber.test(typeof bodyHeader) ? (
|
||||
<PageViewBodyHeaderTitle>{bodyHeader}</PageViewBodyHeaderTitle>
|
||||
export const PageView = memo<PageViewProps>(
|
||||
({ viewType, children, headerLeft, headerRight, bodyHeader, tabs, ...otherProps }) => {
|
||||
const tabComponents = useMemo(() => {
|
||||
if (!tabs) {
|
||||
return [];
|
||||
}
|
||||
return tabs.map(({ name, id, ...otherEuiTabProps }) => (
|
||||
<EuiTab {...otherEuiTabProps} key={id}>
|
||||
{name}
|
||||
</EuiTab>
|
||||
));
|
||||
}, [tabs]);
|
||||
|
||||
return (
|
||||
<StyledEuiPage
|
||||
className={(viewType === 'list' && 'endpoint--isListView') || 'endpoint--isDetailsView'}
|
||||
{...otherProps}
|
||||
>
|
||||
<EuiPageBody>
|
||||
{(headerLeft || headerRight) && (
|
||||
<EuiPageHeader className="endpoint-header">
|
||||
<EuiPageHeaderSection data-test-subj="pageViewHeaderLeft">
|
||||
{isStringOrNumber.test(typeof headerLeft) ? (
|
||||
<PageViewHeaderTitle>{headerLeft}</PageViewHeaderTitle>
|
||||
) : (
|
||||
bodyHeader
|
||||
headerLeft
|
||||
)}
|
||||
</EuiPageContentHeaderSection>
|
||||
</EuiPageContentHeader>
|
||||
</EuiPageHeaderSection>
|
||||
{headerRight && (
|
||||
<EuiPageHeaderSection data-test-subj="pageViewHeaderRight">
|
||||
{headerRight}
|
||||
</EuiPageHeaderSection>
|
||||
)}
|
||||
</EuiPageHeader>
|
||||
)}
|
||||
<EuiPageContentBody data-test-subj="pageViewBodyContent">{children}</EuiPageContentBody>
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
</StyledEuiPage>
|
||||
);
|
||||
});
|
||||
{tabs && <EuiTabs className="endpoint-navTabs">{tabComponents}</EuiTabs>}
|
||||
<EuiPageContent className="endpoint-page-content">
|
||||
{bodyHeader && (
|
||||
<EuiPageContentHeader>
|
||||
<EuiPageContentHeaderSection data-test-subj="pageViewBodyTitleArea">
|
||||
{isStringOrNumber.test(typeof bodyHeader) ? (
|
||||
<PageViewBodyHeaderTitle>{bodyHeader}</PageViewBodyHeaderTitle>
|
||||
) : (
|
||||
bodyHeader
|
||||
)}
|
||||
</EuiPageContentHeaderSection>
|
||||
</EuiPageContentHeader>
|
||||
)}
|
||||
<EuiPageContentBody data-test-subj="pageViewBodyContent">{children}</EuiPageContentBody>
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
</StyledEuiPage>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
PageView.displayName = 'PageView';
|
||||
|
|
|
@ -18,6 +18,7 @@ jest.mock('react-router-dom', () => ({
|
|||
state: '',
|
||||
}),
|
||||
withRouter: () => jest.fn(),
|
||||
generatePath: jest.fn(),
|
||||
}));
|
||||
|
||||
// Test will fail because we will to need to mock some core services to make the test work
|
||||
|
|
|
@ -27,6 +27,7 @@ import {
|
|||
} from './redirect_to_case';
|
||||
import { DetectionEngineTab } from '../../../alerts/pages/detection_engine/types';
|
||||
import { TimelineType } from '../../../../common/types/timeline';
|
||||
import { RedirectToManagementPage } from './redirect_to_management';
|
||||
|
||||
interface LinkToPageProps {
|
||||
match: RouteMatch<{}>;
|
||||
|
@ -120,6 +121,10 @@ export const LinkToPage = React.memo<LinkToPageProps>(({ match }) => (
|
|||
component={RedirectToTimelinesPage}
|
||||
path={`${match.url}/:pageName(${SiemPageName.timelines})/:tabName(${TimelineType.default}|${TimelineType.template})`}
|
||||
/>
|
||||
<Route
|
||||
component={RedirectToManagementPage}
|
||||
path={`${match.url}/:pageName(${SiemPageName.management})`}
|
||||
/>
|
||||
<Redirect to="/" />
|
||||
</Switch>
|
||||
));
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { memo } from 'react';
|
||||
import { RedirectWrapper } from './redirect_wrapper';
|
||||
import { SiemPageName } from '../../../app/types';
|
||||
|
||||
export const RedirectToManagementPage = memo(() => {
|
||||
return <RedirectWrapper to={`/${SiemPageName.management}`} />;
|
||||
});
|
||||
|
||||
RedirectToManagementPage.displayName = 'RedirectToManagementPage';
|
|
@ -72,6 +72,13 @@ describe('SIEM Navigation', () => {
|
|||
name: 'Cases',
|
||||
urlKey: 'case',
|
||||
},
|
||||
management: {
|
||||
disabled: false,
|
||||
href: '#/management',
|
||||
id: 'management',
|
||||
name: 'Management',
|
||||
urlKey: 'management',
|
||||
},
|
||||
detections: {
|
||||
disabled: false,
|
||||
href: '#/link-to/detections',
|
||||
|
@ -111,6 +118,7 @@ describe('SIEM Navigation', () => {
|
|||
pageName: 'hosts',
|
||||
pathName: '/hosts',
|
||||
search: '',
|
||||
state: undefined,
|
||||
tabName: 'authentications',
|
||||
query: { query: '', language: 'kuery' },
|
||||
filters: [],
|
||||
|
@ -179,6 +187,13 @@ describe('SIEM Navigation', () => {
|
|||
name: 'Hosts',
|
||||
urlKey: 'host',
|
||||
},
|
||||
management: {
|
||||
disabled: false,
|
||||
href: '#/management',
|
||||
id: 'management',
|
||||
name: 'Management',
|
||||
urlKey: 'management',
|
||||
},
|
||||
network: {
|
||||
disabled: false,
|
||||
href: '#/link-to/network',
|
||||
|
|
|
@ -12,6 +12,7 @@ export enum CONSTANTS {
|
|||
filters = 'filters',
|
||||
hostsDetails = 'hosts.details',
|
||||
hostsPage = 'hosts.page',
|
||||
management = 'management',
|
||||
networkDetails = 'network.details',
|
||||
networkPage = 'network.page',
|
||||
overviewPage = 'overview.page',
|
||||
|
@ -22,4 +23,11 @@ export enum CONSTANTS {
|
|||
unknown = 'unknown',
|
||||
}
|
||||
|
||||
export type UrlStateType = 'case' | 'detections' | 'host' | 'network' | 'overview' | 'timeline';
|
||||
export type UrlStateType =
|
||||
| 'case'
|
||||
| 'detections'
|
||||
| 'host'
|
||||
| 'network'
|
||||
| 'overview'
|
||||
| 'timeline'
|
||||
| 'management';
|
||||
|
|
|
@ -8,10 +8,10 @@ import ApolloClient from 'apollo-client';
|
|||
import * as H from 'history';
|
||||
import { ActionCreator } from 'typescript-fsa';
|
||||
import {
|
||||
IIndexPattern,
|
||||
Query,
|
||||
Filter,
|
||||
FilterManager,
|
||||
IIndexPattern,
|
||||
Query,
|
||||
SavedQueryService,
|
||||
} from 'src/plugins/data/public';
|
||||
|
||||
|
@ -46,6 +46,7 @@ export const URL_STATE_KEYS: Record<UrlStateType, KeyUrlState[]> = {
|
|||
CONSTANTS.timerange,
|
||||
CONSTANTS.timeline,
|
||||
],
|
||||
management: [],
|
||||
network: [
|
||||
CONSTANTS.appQuery,
|
||||
CONSTANTS.filters,
|
||||
|
|
|
@ -15,11 +15,10 @@ import { depsStartMock } from './dependencies_start_mock';
|
|||
import { MiddlewareActionSpyHelper, createSpyMiddleware } from '../../store/test_utils';
|
||||
import { apolloClientObservable } from '../test_providers';
|
||||
import { createStore, State, substateMiddlewareFactory } from '../../store';
|
||||
import { hostMiddlewareFactory } from '../../../endpoint_hosts/store/middleware';
|
||||
import { policyListMiddlewareFactory } from '../../../endpoint_policy/store/policy_list/middleware';
|
||||
import { policyDetailsMiddlewareFactory } from '../../../endpoint_policy/store/policy_details/middleware';
|
||||
import { hostMiddlewareFactory } from '../../../endpoint_hosts/store';
|
||||
import { alertMiddlewareFactory } from '../../../endpoint_alerts/store/middleware';
|
||||
import { AppRootProvider } from './app_root_provider';
|
||||
import { managementMiddlewareFactory } from '../../../management/store';
|
||||
import { SUB_PLUGINS_REDUCER, mockGlobalState } from '..';
|
||||
|
||||
type UiRender = (ui: React.ReactElement, options?: RenderOptions) => RenderResult;
|
||||
|
@ -63,18 +62,11 @@ export const createAppRootMockRenderer = (): AppContextTestRender => {
|
|||
(globalState) => globalState.hostList,
|
||||
hostMiddlewareFactory(coreStart, depsStart)
|
||||
),
|
||||
substateMiddlewareFactory(
|
||||
(globalState) => globalState.policyList,
|
||||
policyListMiddlewareFactory(coreStart, depsStart)
|
||||
),
|
||||
substateMiddlewareFactory(
|
||||
(globalState) => globalState.policyDetails,
|
||||
policyDetailsMiddlewareFactory(coreStart, depsStart)
|
||||
),
|
||||
substateMiddlewareFactory(
|
||||
(globalState) => globalState.alertList,
|
||||
alertMiddlewareFactory(coreStart, depsStart)
|
||||
),
|
||||
...managementMiddlewareFactory(coreStart, depsStart),
|
||||
middlewareSpy.actionSpyMiddleware,
|
||||
]);
|
||||
|
||||
|
|
|
@ -25,15 +25,13 @@ import {
|
|||
} from '../../../common/constants';
|
||||
import { networkModel } from '../../network/store';
|
||||
import { TimelineType, TimelineStatus } from '../../../common/types/timeline';
|
||||
import { initialPolicyListState } from '../../endpoint_policy/store/policy_list/reducer';
|
||||
import { initialAlertListState } from '../../endpoint_alerts/store/reducer';
|
||||
import { initialPolicyDetailsState } from '../../endpoint_policy/store/policy_details/reducer';
|
||||
import { initialHostListState } from '../../endpoint_hosts/store/reducer';
|
||||
import { getManagementInitialState } from '../../management/store';
|
||||
|
||||
const policyList = initialPolicyListState();
|
||||
const alertList = initialAlertListState();
|
||||
const policyDetails = initialPolicyDetailsState();
|
||||
const hostList = initialHostListState();
|
||||
const management = getManagementInitialState();
|
||||
|
||||
export const mockGlobalState: State = {
|
||||
app: {
|
||||
|
@ -237,6 +235,5 @@ export const mockGlobalState: State = {
|
|||
},
|
||||
alertList,
|
||||
hostList,
|
||||
policyList,
|
||||
policyDetails,
|
||||
management,
|
||||
};
|
||||
|
|
|
@ -9,8 +9,7 @@ import { networkReducer } from '../../network/store';
|
|||
import { timelineReducer } from '../../timelines/store/timeline/reducer';
|
||||
import { hostListReducer } from '../../endpoint_hosts/store';
|
||||
import { alertListReducer } from '../../endpoint_alerts/store';
|
||||
import { policyListReducer } from '../../endpoint_policy/store/policy_list';
|
||||
import { policyDetailsReducer } from '../../endpoint_policy/store/policy_details';
|
||||
import { managementReducer } from '../../management/store';
|
||||
|
||||
interface Global extends NodeJS.Global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
@ -25,6 +24,5 @@ export const SUB_PLUGINS_REDUCER = {
|
|||
timeline: timelineReducer,
|
||||
hostList: hostListReducer,
|
||||
alertList: alertListReducer,
|
||||
policyList: policyListReducer,
|
||||
policyDetails: policyDetailsReducer,
|
||||
management: managementReducer,
|
||||
};
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
|
||||
import { HostAction } from '../../endpoint_hosts/store/action';
|
||||
import { AlertAction } from '../../endpoint_alerts/store/action';
|
||||
import { PolicyListAction } from '../../endpoint_policy/store/policy_list';
|
||||
import { PolicyDetailsAction } from '../../endpoint_policy/store/policy_details';
|
||||
import { PolicyListAction } from '../../management/pages/policy/store/policy_list';
|
||||
import { PolicyDetailsAction } from '../../management/pages/policy/store/policy_details';
|
||||
|
||||
export { appActions } from './app';
|
||||
export { dragAndDropActions } from './drag_and_drop';
|
||||
|
|
|
@ -18,14 +18,8 @@ import {
|
|||
EndpointAlertsPluginReducer,
|
||||
} from '../../endpoint_alerts/store';
|
||||
import { EndpointHostsPluginState, EndpointHostsPluginReducer } from '../../endpoint_hosts/store';
|
||||
import {
|
||||
EndpointPolicyDetailsStatePluginState,
|
||||
EndpointPolicyDetailsStatePluginReducer,
|
||||
} from '../../endpoint_policy/store/policy_details';
|
||||
import {
|
||||
EndpointPolicyListStatePluginState,
|
||||
EndpointPolicyListStatePluginReducer,
|
||||
} from '../../endpoint_policy/store/policy_list';
|
||||
|
||||
import { ManagementPluginReducer, ManagementPluginState } from '../../management/store/types';
|
||||
|
||||
export interface State
|
||||
extends HostsPluginState,
|
||||
|
@ -33,8 +27,7 @@ export interface State
|
|||
TimelinePluginState,
|
||||
EndpointAlertsPluginState,
|
||||
EndpointHostsPluginState,
|
||||
EndpointPolicyDetailsStatePluginState,
|
||||
EndpointPolicyListStatePluginState {
|
||||
ManagementPluginState {
|
||||
app: AppState;
|
||||
dragAndDrop: DragAndDropState;
|
||||
inputs: InputsState;
|
||||
|
@ -51,15 +44,14 @@ type SubPluginsInitState = HostsPluginState &
|
|||
TimelinePluginState &
|
||||
EndpointAlertsPluginState &
|
||||
EndpointHostsPluginState &
|
||||
EndpointPolicyDetailsStatePluginState &
|
||||
EndpointPolicyListStatePluginState;
|
||||
ManagementPluginState;
|
||||
|
||||
export type SubPluginsInitReducer = HostsPluginReducer &
|
||||
NetworkPluginReducer &
|
||||
TimelinePluginReducer &
|
||||
EndpointAlertsPluginReducer &
|
||||
EndpointHostsPluginReducer &
|
||||
EndpointPolicyDetailsStatePluginReducer &
|
||||
EndpointPolicyListStatePluginReducer;
|
||||
ManagementPluginReducer;
|
||||
|
||||
export const createInitialState = (pluginsInitState: SubPluginsInitState): State => ({
|
||||
...initialState,
|
||||
|
|
|
@ -61,6 +61,17 @@ export type ImmutableMiddlewareFactory<S = State> = (
|
|||
depsStart: Pick<StartPlugins, 'data' | 'ingestManager'>
|
||||
) => ImmutableMiddleware<S, AppAction>;
|
||||
|
||||
/**
|
||||
* Takes application-standard middleware dependencies
|
||||
* and returns an array of redux middleware.
|
||||
* Middleware will be of the `ImmutableMiddleware` variety. Not able to directly
|
||||
* change actions or state.
|
||||
*/
|
||||
export type ImmutableMultipleMiddlewareFactory<S = State> = (
|
||||
coreStart: CoreStart,
|
||||
depsStart: Pick<StartPlugins, 'data' | 'ingestManager'>
|
||||
) => Array<ImmutableMiddleware<S, AppAction>>;
|
||||
|
||||
/**
|
||||
* Simple type for a redux selector.
|
||||
*/
|
||||
|
|
|
@ -22,10 +22,12 @@ export class EndpointAlerts {
|
|||
plugins: StartPlugins
|
||||
): SecuritySubPluginWithStore<'alertList', Immutable<AlertListState>> {
|
||||
const { data, ingestManager } = plugins;
|
||||
const middleware = substateMiddlewareFactory(
|
||||
(globalState) => globalState.alertList,
|
||||
alertMiddlewareFactory(core, { data, ingestManager })
|
||||
);
|
||||
const middleware = [
|
||||
substateMiddlewareFactory(
|
||||
(globalState) => globalState.alertList,
|
||||
alertMiddlewareFactory(core, { data, ingestManager })
|
||||
),
|
||||
];
|
||||
|
||||
return {
|
||||
routes: getEndpointAlertsRoutes(),
|
||||
|
|
|
@ -22,10 +22,12 @@ export class EndpointHosts {
|
|||
plugins: StartPlugins
|
||||
): SecuritySubPluginWithStore<'hostList', Immutable<HostState>> {
|
||||
const { data, ingestManager } = plugins;
|
||||
const middleware = substateMiddlewareFactory(
|
||||
(globalState) => globalState.hostList,
|
||||
hostMiddlewareFactory(core, { data, ingestManager })
|
||||
);
|
||||
const middleware = [
|
||||
substateMiddlewareFactory(
|
||||
(globalState) => globalState.hostList,
|
||||
hostMiddlewareFactory(core, { data, ingestManager })
|
||||
),
|
||||
];
|
||||
return {
|
||||
routes: getEndpointHostsRoutes(),
|
||||
store: {
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { SecuritySubPluginWithStore } from '../app/types';
|
||||
import { getPolicyDetailsRoutes } from './routes';
|
||||
import { PolicyDetailsState } from './types';
|
||||
import { Immutable } from '../../common/endpoint/types';
|
||||
import { initialPolicyDetailsState, policyDetailsReducer } from './store/policy_details/reducer';
|
||||
import { policyDetailsMiddlewareFactory } from './store/policy_details/middleware';
|
||||
import { CoreStart } from '../../../../../src/core/public';
|
||||
import { StartPlugins } from '../types';
|
||||
import { substateMiddlewareFactory } from '../common/store';
|
||||
|
||||
export class EndpointPolicyDetails {
|
||||
public setup() {}
|
||||
|
||||
public start(
|
||||
core: CoreStart,
|
||||
plugins: StartPlugins
|
||||
): SecuritySubPluginWithStore<'policyDetails', Immutable<PolicyDetailsState>> {
|
||||
const { data, ingestManager } = plugins;
|
||||
const middleware = substateMiddlewareFactory(
|
||||
(globalState) => globalState.policyDetails,
|
||||
policyDetailsMiddlewareFactory(core, { data, ingestManager })
|
||||
);
|
||||
|
||||
return {
|
||||
routes: getPolicyDetailsRoutes(),
|
||||
store: {
|
||||
initialState: {
|
||||
policyDetails: initialPolicyDetailsState(),
|
||||
},
|
||||
reducer: { policyDetails: policyDetailsReducer },
|
||||
middleware,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { SecuritySubPluginWithStore } from '../app/types';
|
||||
import { getPolicyListRoutes } from './routes';
|
||||
import { PolicyListState } from './types';
|
||||
import { Immutable } from '../../common/endpoint/types';
|
||||
import { initialPolicyListState, policyListReducer } from './store/policy_list/reducer';
|
||||
import { policyListMiddlewareFactory } from './store/policy_list/middleware';
|
||||
import { CoreStart } from '../../../../../src/core/public';
|
||||
import { StartPlugins } from '../types';
|
||||
import { substateMiddlewareFactory } from '../common/store';
|
||||
|
||||
export class EndpointPolicyList {
|
||||
public setup() {}
|
||||
|
||||
public start(
|
||||
core: CoreStart,
|
||||
plugins: StartPlugins
|
||||
): SecuritySubPluginWithStore<'policyList', Immutable<PolicyListState>> {
|
||||
const { data, ingestManager } = plugins;
|
||||
const middleware = substateMiddlewareFactory(
|
||||
(globalState) => globalState.policyList,
|
||||
policyListMiddlewareFactory(core, { data, ingestManager })
|
||||
);
|
||||
|
||||
return {
|
||||
routes: getPolicyListRoutes(),
|
||||
store: {
|
||||
initialState: {
|
||||
policyList: initialPolicyListState(),
|
||||
},
|
||||
reducer: { policyList: policyListReducer },
|
||||
middleware,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Route } from 'react-router-dom';
|
||||
|
||||
import { PolicyList, PolicyDetails } from './view';
|
||||
|
||||
export const getPolicyListRoutes = () => [
|
||||
<Route path="/:pageName(policy)" exact component={PolicyList} />,
|
||||
];
|
||||
|
||||
export const getPolicyDetailsRoutes = () => [
|
||||
<Route path="/:pageName(policy)/:id" exact component={PolicyDetails} />,
|
||||
];
|
|
@ -1,19 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { useSelector } from 'react-redux';
|
||||
import { PolicyListState, PolicyDetailsState } from '../types';
|
||||
import { State } from '../../common/store';
|
||||
|
||||
export function usePolicyListSelector<TSelected>(selector: (state: PolicyListState) => TSelected) {
|
||||
return useSelector((state: State) => selector(state.policyList as PolicyListState));
|
||||
}
|
||||
|
||||
export function usePolicyDetailsSelector<TSelected>(
|
||||
selector: (state: PolicyDetailsState) => TSelected
|
||||
) {
|
||||
return useSelector((state: State) => selector(state.policyDetails as PolicyDetailsState));
|
||||
}
|
21
x-pack/plugins/siem/public/management/common/constants.ts
Normal file
21
x-pack/plugins/siem/public/management/common/constants.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import { SiemPageName } from '../../app/types';
|
||||
import { ManagementStoreGlobalNamespace, ManagementSubTab } from '../types';
|
||||
|
||||
// --[ ROUTING ]---------------------------------------------------------------------------
|
||||
export const MANAGEMENT_ROUTING_ROOT_PATH = `/:pageName(${SiemPageName.management})`;
|
||||
export const MANAGEMENT_ROUTING_ENDPOINTS_PATH = `${MANAGEMENT_ROUTING_ROOT_PATH}/:tabName(${ManagementSubTab.endpoints})`;
|
||||
export const MANAGEMENT_ROUTING_POLICIES_PATH = `${MANAGEMENT_ROUTING_ROOT_PATH}/:tabName(${ManagementSubTab.policies})`;
|
||||
export const MANAGEMENT_ROUTING_POLICY_DETAILS_PATH = `${MANAGEMENT_ROUTING_ROOT_PATH}/:tabName(${ManagementSubTab.policies})/:policyId`;
|
||||
|
||||
// --[ STORE ]---------------------------------------------------------------------------
|
||||
/** The SIEM global store namespace where the management state will be mounted */
|
||||
export const MANAGEMENT_STORE_GLOBAL_NAMESPACE: ManagementStoreGlobalNamespace = 'management';
|
||||
/** Namespace within the Management state where policy list state is maintained */
|
||||
export const MANAGEMENT_STORE_POLICY_LIST_NAMESPACE = 'policyList';
|
||||
/** Namespace within the Management state where policy details state is maintained */
|
||||
export const MANAGEMENT_STORE_POLICY_DETAILS_NAMESPACE = 'policyDetails';
|
70
x-pack/plugins/siem/public/management/common/routing.ts
Normal file
70
x-pack/plugins/siem/public/management/common/routing.ts
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { generatePath } from 'react-router-dom';
|
||||
import {
|
||||
MANAGEMENT_ROUTING_ENDPOINTS_PATH,
|
||||
MANAGEMENT_ROUTING_POLICIES_PATH,
|
||||
MANAGEMENT_ROUTING_POLICY_DETAILS_PATH,
|
||||
MANAGEMENT_ROUTING_ROOT_PATH,
|
||||
} from './constants';
|
||||
import { ManagementSubTab } from '../types';
|
||||
import { SiemPageName } from '../../app/types';
|
||||
|
||||
export type GetManagementUrlProps = {
|
||||
/**
|
||||
* Exclude the URL prefix (everything to the left of where the router was mounted.
|
||||
* This may be needed when interacting with react-router (ex. to do `history.push()` or
|
||||
* validations against matched path)
|
||||
*/
|
||||
excludePrefix?: boolean;
|
||||
} & (
|
||||
| { name: 'default' }
|
||||
| { name: 'endpointList' }
|
||||
| { name: 'policyList' }
|
||||
| { name: 'policyDetails'; policyId: string }
|
||||
);
|
||||
|
||||
// Prefix is (almost) everything to the left of where the Router was mounted. In SIEM, since
|
||||
// we're using Hash router, thats the `#`.
|
||||
const URL_PREFIX = '#';
|
||||
|
||||
/**
|
||||
* Returns a URL string for a given Management page view
|
||||
* @param props
|
||||
*/
|
||||
export const getManagementUrl = (props: GetManagementUrlProps): string => {
|
||||
let url = props.excludePrefix ? '' : URL_PREFIX;
|
||||
|
||||
switch (props.name) {
|
||||
case 'default':
|
||||
url += generatePath(MANAGEMENT_ROUTING_ROOT_PATH, {
|
||||
pageName: SiemPageName.management,
|
||||
});
|
||||
break;
|
||||
case 'endpointList':
|
||||
url += generatePath(MANAGEMENT_ROUTING_ENDPOINTS_PATH, {
|
||||
pageName: SiemPageName.management,
|
||||
tabName: ManagementSubTab.endpoints,
|
||||
});
|
||||
break;
|
||||
case 'policyList':
|
||||
url += generatePath(MANAGEMENT_ROUTING_POLICIES_PATH, {
|
||||
pageName: SiemPageName.management,
|
||||
tabName: ManagementSubTab.policies,
|
||||
});
|
||||
break;
|
||||
case 'policyDetails':
|
||||
url += generatePath(MANAGEMENT_ROUTING_POLICY_DETAILS_PATH, {
|
||||
pageName: SiemPageName.management,
|
||||
tabName: ManagementSubTab.policies,
|
||||
policyId: props.policyId,
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
return url;
|
||||
};
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { memo, useMemo } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { PageView, PageViewProps } from '../../common/components/endpoint/page_view';
|
||||
import { ManagementSubTab } from '../types';
|
||||
import { getManagementUrl } from '..';
|
||||
|
||||
export const ManagementPageView = memo<Omit<PageViewProps, 'tabs'>>((options) => {
|
||||
const { tabName } = useParams<{ tabName: ManagementSubTab }>();
|
||||
const tabs = useMemo((): PageViewProps['tabs'] => {
|
||||
return [
|
||||
{
|
||||
name: i18n.translate('xpack.siem.managementTabs.endpoints', {
|
||||
defaultMessage: 'Endpoints',
|
||||
}),
|
||||
id: ManagementSubTab.endpoints,
|
||||
isSelected: tabName === ManagementSubTab.endpoints,
|
||||
href: getManagementUrl({ name: 'endpointList' }),
|
||||
},
|
||||
{
|
||||
name: i18n.translate('xpack.siem.managementTabs.policies', { defaultMessage: 'Policies' }),
|
||||
id: ManagementSubTab.policies,
|
||||
isSelected: tabName === ManagementSubTab.policies,
|
||||
href: getManagementUrl({ name: 'policyList' }),
|
||||
},
|
||||
];
|
||||
}, [tabName]);
|
||||
return <PageView {...options} tabs={tabs} />;
|
||||
});
|
||||
|
||||
ManagementPageView.displayName = 'ManagementPageView';
|
39
x-pack/plugins/siem/public/management/index.ts
Normal file
39
x-pack/plugins/siem/public/management/index.ts
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { CoreStart } from 'kibana/public';
|
||||
import { managementReducer, getManagementInitialState, managementMiddlewareFactory } from './store';
|
||||
import { getManagementRoutes } from './routes';
|
||||
import { StartPlugins } from '../types';
|
||||
import { MANAGEMENT_STORE_GLOBAL_NAMESPACE } from './common/constants';
|
||||
import { SecuritySubPluginWithStore } from '../app/types';
|
||||
import { Immutable } from '../../common/endpoint/types';
|
||||
import { ManagementStoreGlobalNamespace } from './types';
|
||||
import { ManagementState } from './store/types';
|
||||
|
||||
export { getManagementUrl } from './common/routing';
|
||||
|
||||
export class Management {
|
||||
public setup() {}
|
||||
|
||||
public start(
|
||||
core: CoreStart,
|
||||
plugins: StartPlugins
|
||||
): SecuritySubPluginWithStore<ManagementStoreGlobalNamespace, Immutable<ManagementState>> {
|
||||
return {
|
||||
routes: getManagementRoutes(),
|
||||
store: {
|
||||
initialState: {
|
||||
[MANAGEMENT_STORE_GLOBAL_NAMESPACE]: getManagementInitialState(),
|
||||
},
|
||||
reducer: {
|
||||
[MANAGEMENT_STORE_GLOBAL_NAMESPACE]: managementReducer,
|
||||
},
|
||||
middleware: managementMiddlewareFactory(core, plugins),
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
43
x-pack/plugins/siem/public/management/pages/index.tsx
Normal file
43
x-pack/plugins/siem/public/management/pages/index.tsx
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { memo } from 'react';
|
||||
import { Redirect, Route, Switch } from 'react-router-dom';
|
||||
import { SpyRoute } from '../../common/utils/route/spy_routes';
|
||||
import { PolicyContainer } from './policy';
|
||||
import {
|
||||
MANAGEMENT_ROUTING_ENDPOINTS_PATH,
|
||||
MANAGEMENT_ROUTING_POLICIES_PATH,
|
||||
MANAGEMENT_ROUTING_ROOT_PATH,
|
||||
} from '../common/constants';
|
||||
import { ManagementPageView } from '../components/management_page_view';
|
||||
import { NotFoundPage } from '../../app/404';
|
||||
|
||||
const TmpEndpoints = () => {
|
||||
return (
|
||||
<ManagementPageView viewType="list" headerLeft="Test">
|
||||
<h1>{'Endpoints will go here'}</h1>
|
||||
<SpyRoute />
|
||||
</ManagementPageView>
|
||||
);
|
||||
};
|
||||
|
||||
export const ManagementContainer = memo(() => {
|
||||
return (
|
||||
<Switch>
|
||||
<Route path={MANAGEMENT_ROUTING_ENDPOINTS_PATH} exact component={TmpEndpoints} />
|
||||
<Route path={MANAGEMENT_ROUTING_POLICIES_PATH} component={PolicyContainer} />
|
||||
<Route
|
||||
path={MANAGEMENT_ROUTING_ROOT_PATH}
|
||||
exact
|
||||
render={() => <Redirect to="/management/endpoints" />}
|
||||
/>
|
||||
<Route path="*" component={NotFoundPage} />
|
||||
</Switch>
|
||||
);
|
||||
});
|
||||
|
||||
ManagementContainer.displayName = 'ManagementContainer';
|
26
x-pack/plugins/siem/public/management/pages/policy/index.tsx
Normal file
26
x-pack/plugins/siem/public/management/pages/policy/index.tsx
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { memo } from 'react';
|
||||
import { Route, Switch } from 'react-router-dom';
|
||||
import { PolicyDetails, PolicyList } from './view';
|
||||
import {
|
||||
MANAGEMENT_ROUTING_POLICIES_PATH,
|
||||
MANAGEMENT_ROUTING_POLICY_DETAILS_PATH,
|
||||
} from '../../common/constants';
|
||||
import { NotFoundPage } from '../../../app/404';
|
||||
|
||||
export const PolicyContainer = memo(() => {
|
||||
return (
|
||||
<Switch>
|
||||
<Route path={MANAGEMENT_ROUTING_POLICIES_PATH} exact component={PolicyList} />
|
||||
<Route path={MANAGEMENT_ROUTING_POLICY_DETAILS_PATH} exact component={PolicyDetails} />
|
||||
<Route path="*" component={NotFoundPage} />
|
||||
</Switch>
|
||||
);
|
||||
});
|
||||
|
||||
PolicyContainer.displayName = 'PolicyContainer';
|
|
@ -4,7 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { UIPolicyConfig } from '../../../common/endpoint/types';
|
||||
import { UIPolicyConfig } from '../../../../../common/endpoint/types';
|
||||
|
||||
/**
|
||||
* A typed Object.entries() function where the keys and values are typed based on the given object
|
|
@ -4,9 +4,9 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { GetAgentStatusResponse } from '../../../../../ingest_manager/common/types/rest_spec';
|
||||
import { PolicyData, UIPolicyConfig } from '../../../../common/endpoint/types';
|
||||
import { ServerApiError } from '../../../common/types';
|
||||
import { GetAgentStatusResponse } from '../../../../../../../ingest_manager/common/types/rest_spec';
|
||||
import { PolicyData, UIPolicyConfig } from '../../../../../../common/endpoint/types';
|
||||
import { ServerApiError } from '../../../../../common/types';
|
||||
import { PolicyDetailsState } from '../../types';
|
||||
|
||||
interface ServerReturnedPolicyDetailsData {
|
|
@ -9,7 +9,7 @@ import { createStore, Dispatch, Store } from 'redux';
|
|||
import { policyDetailsReducer, PolicyDetailsAction } from './index';
|
||||
import { policyConfig } from './selectors';
|
||||
import { clone } from '../../models/policy_details_config';
|
||||
import { factory as policyConfigFactory } from '../../../../common/endpoint/models/policy_config';
|
||||
import { factory as policyConfigFactory } from '../../../../../../common/endpoint/models/policy_config';
|
||||
|
||||
describe('policy details: ', () => {
|
||||
let store: Store<PolicyDetailsState>;
|
|
@ -5,9 +5,9 @@
|
|||
*/
|
||||
|
||||
import { PolicyDetailsState } from '../../types';
|
||||
import { ImmutableReducer } from '../../../common/store';
|
||||
import { AppAction } from '../../../common/store/actions';
|
||||
import { Immutable } from '../../../../common/endpoint/types';
|
||||
import { ImmutableReducer } from '../../../../../common/store';
|
||||
import { AppAction } from '../../../../../common/store/actions';
|
||||
import { Immutable } from '../../../../../../common/endpoint/types';
|
||||
|
||||
export { policyDetailsMiddlewareFactory } from './middleware';
|
||||
export { PolicyDetailsAction } from './action';
|
|
@ -16,9 +16,9 @@ import {
|
|||
sendGetFleetAgentStatusForConfig,
|
||||
sendPutDatasource,
|
||||
} from '../policy_list/services/ingest';
|
||||
import { NewPolicyData, PolicyData, Immutable } from '../../../../common/endpoint/types';
|
||||
import { factory as policyConfigFactory } from '../../../../common/endpoint/models/policy_config';
|
||||
import { ImmutableMiddlewareFactory } from '../../../common/store';
|
||||
import { NewPolicyData, PolicyData, Immutable } from '../../../../../../common/endpoint/types';
|
||||
import { factory as policyConfigFactory } from '../../../../../../common/endpoint/models/policy_config';
|
||||
import { ImmutableMiddlewareFactory } from '../../../../../common/store';
|
||||
|
||||
export const policyDetailsMiddlewareFactory: ImmutableMiddlewareFactory<Immutable<
|
||||
PolicyDetailsState
|
|
@ -5,9 +5,9 @@
|
|||
*/
|
||||
import { fullPolicy, isOnPolicyDetailsPage } from './selectors';
|
||||
import { PolicyDetailsState } from '../../types';
|
||||
import { Immutable, PolicyConfig, UIPolicyConfig } from '../../../../common/endpoint/types';
|
||||
import { ImmutableReducer } from '../../../common/store';
|
||||
import { AppAction } from '../../../common/store/actions';
|
||||
import { Immutable, PolicyConfig, UIPolicyConfig } from '../../../../../../common/endpoint/types';
|
||||
import { ImmutableReducer } from '../../../../../common/store';
|
||||
import { AppAction } from '../../../../../common/store/actions';
|
||||
|
||||
export const initialPolicyDetailsState = (): PolicyDetailsState => {
|
||||
return {
|
|
@ -5,14 +5,17 @@
|
|||
*/
|
||||
|
||||
import { createSelector } from 'reselect';
|
||||
import { matchPath } from 'react-router-dom';
|
||||
import { PolicyDetailsState } from '../../types';
|
||||
import {
|
||||
Immutable,
|
||||
NewPolicyData,
|
||||
PolicyConfig,
|
||||
UIPolicyConfig,
|
||||
} from '../../../../common/endpoint/types';
|
||||
import { factory as policyConfigFactory } from '../../../../common/endpoint/models/policy_config';
|
||||
} from '../../../../../../common/endpoint/types';
|
||||
import { factory as policyConfigFactory } from '../../../../../../common/endpoint/models/policy_config';
|
||||
import { MANAGEMENT_ROUTING_POLICY_DETAILS_PATH } from '../../../../common/constants';
|
||||
import { ManagementRoutePolicyDetailsParams } from '../../../../types';
|
||||
|
||||
/** Returns the policy details */
|
||||
export const policyDetails = (state: Immutable<PolicyDetailsState>) => state.policyItem;
|
||||
|
@ -31,22 +34,24 @@ export const policyDetailsForUpdate: (
|
|||
|
||||
/** Returns a boolean of whether the user is on the policy details page or not */
|
||||
export const isOnPolicyDetailsPage = (state: Immutable<PolicyDetailsState>) => {
|
||||
if (state.location) {
|
||||
const pathnameParts = state.location.pathname.split('/');
|
||||
return pathnameParts[1] === 'policy' && pathnameParts[2];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
matchPath(state.location?.pathname ?? '', {
|
||||
path: MANAGEMENT_ROUTING_POLICY_DETAILS_PATH,
|
||||
exact: true,
|
||||
}) !== null
|
||||
);
|
||||
};
|
||||
|
||||
/** Returns the policyId from the url */
|
||||
export const policyIdFromParams: (state: Immutable<PolicyDetailsState>) => string = createSelector(
|
||||
(state) => state.location,
|
||||
(location: PolicyDetailsState['location']) => {
|
||||
if (location) {
|
||||
return location.pathname.split('/')[2];
|
||||
}
|
||||
return '';
|
||||
return (
|
||||
matchPath<ManagementRoutePolicyDetailsParams>(location?.pathname ?? '', {
|
||||
path: MANAGEMENT_ROUTING_POLICY_DETAILS_PATH,
|
||||
exact: true,
|
||||
})?.params?.policyId ?? ''
|
||||
);
|
||||
}
|
||||
);
|
||||
|
|
@ -4,8 +4,8 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { PolicyData } from '../../../../common/endpoint/types';
|
||||
import { ServerApiError } from '../../../common/types';
|
||||
import { PolicyData } from '../../../../../../common/endpoint/types';
|
||||
import { ServerApiError } from '../../../../../common/types';
|
||||
|
||||
interface ServerReturnedPolicyListData {
|
||||
type: 'serverReturnedPolicyListData';
|
|
@ -7,19 +7,24 @@
|
|||
import { PolicyListState } from '../../types';
|
||||
import { Store, applyMiddleware, createStore } from 'redux';
|
||||
|
||||
import { coreMock } from '../../../../../../../src/core/public/mocks';
|
||||
import { DATASOURCE_SAVED_OBJECT_TYPE } from '../../../../../ingest_manager/common';
|
||||
import { coreMock } from '../../../../../../../../../src/core/public/mocks';
|
||||
import { DATASOURCE_SAVED_OBJECT_TYPE } from '../../../../../../../ingest_manager/common';
|
||||
|
||||
import { policyListReducer, initialPolicyListState } from './reducer';
|
||||
import { policyListMiddlewareFactory } from './middleware';
|
||||
|
||||
import { isOnPolicyListPage, selectIsLoading, urlSearchParams } from './selectors';
|
||||
import { DepsStartMock, depsStartMock } from '../../../common/mock/endpoint';
|
||||
import { DepsStartMock, depsStartMock } from '../../../../../common/mock/endpoint';
|
||||
import { setPolicyListApiMockImplementation } from './test_mock_utils';
|
||||
import { INGEST_API_DATASOURCES } from './services/ingest';
|
||||
import { createSpyMiddleware, MiddlewareActionSpyHelper } from '../../../common/store/test_utils';
|
||||
import {
|
||||
createSpyMiddleware,
|
||||
MiddlewareActionSpyHelper,
|
||||
} from '../../../../../common/store/test_utils';
|
||||
import { getManagementUrl } from '../../../../common/routing';
|
||||
|
||||
describe('policy list store concerns', () => {
|
||||
const policyListPathUrl = getManagementUrl({ name: 'policyList', excludePrefix: true });
|
||||
let fakeCoreStart: ReturnType<typeof coreMock.createStart>;
|
||||
let depsStart: DepsStartMock;
|
||||
let store: Store;
|
||||
|
@ -57,7 +62,7 @@ describe('policy list store concerns', () => {
|
|||
store.dispatch({
|
||||
type: 'userChangedUrl',
|
||||
payload: {
|
||||
pathname: '/policy',
|
||||
pathname: policyListPathUrl,
|
||||
search: '',
|
||||
hash: '',
|
||||
},
|
||||
|
@ -70,7 +75,7 @@ describe('policy list store concerns', () => {
|
|||
store.dispatch({
|
||||
type: 'userChangedUrl',
|
||||
payload: {
|
||||
pathname: '/policy',
|
||||
pathname: policyListPathUrl,
|
||||
search: '',
|
||||
hash: '',
|
||||
},
|
||||
|
@ -84,7 +89,7 @@ describe('policy list store concerns', () => {
|
|||
store.dispatch({
|
||||
type: 'userChangedUrl',
|
||||
payload: {
|
||||
pathname: '/policy',
|
||||
pathname: policyListPathUrl,
|
||||
search: '',
|
||||
hash: '',
|
||||
},
|
||||
|
@ -112,7 +117,7 @@ describe('policy list store concerns', () => {
|
|||
store.dispatch({
|
||||
type: 'userChangedUrl',
|
||||
payload: {
|
||||
pathname: '/policy',
|
||||
pathname: policyListPathUrl,
|
||||
search: '',
|
||||
hash: '',
|
||||
},
|
||||
|
@ -132,7 +137,7 @@ describe('policy list store concerns', () => {
|
|||
store.dispatch({
|
||||
type: 'userChangedUrl',
|
||||
payload: {
|
||||
pathname: '/policy',
|
||||
pathname: policyListPathUrl,
|
||||
search: searchParams,
|
||||
hash: '',
|
||||
},
|
|
@ -5,9 +5,9 @@
|
|||
*/
|
||||
|
||||
import { PolicyListState } from '../../types';
|
||||
import { ImmutableReducer } from '../../../common/store';
|
||||
import { AppAction } from '../../../common/store/actions';
|
||||
import { Immutable } from '../../../../common/endpoint/types';
|
||||
import { ImmutableReducer } from '../../../../../common/store';
|
||||
import { AppAction } from '../../../../../common/store/actions';
|
||||
import { Immutable } from '../../../../../../common/endpoint/types';
|
||||
export { policyListReducer } from './reducer';
|
||||
export { PolicyListAction } from './action';
|
||||
export { policyListMiddlewareFactory } from './middleware';
|
|
@ -7,8 +7,8 @@
|
|||
import { GetPolicyListResponse, PolicyListState } from '../../types';
|
||||
import { sendGetEndpointSpecificDatasources } from './services/ingest';
|
||||
import { isOnPolicyListPage, urlSearchParams } from './selectors';
|
||||
import { ImmutableMiddlewareFactory } from '../../../common/store';
|
||||
import { Immutable } from '../../../../common/endpoint/types';
|
||||
import { ImmutableMiddlewareFactory } from '../../../../../common/store';
|
||||
import { Immutable } from '../../../../../../common/endpoint/types';
|
||||
|
||||
export const policyListMiddlewareFactory: ImmutableMiddlewareFactory<Immutable<PolicyListState>> = (
|
||||
coreStart
|
|
@ -6,9 +6,9 @@
|
|||
|
||||
import { PolicyListState } from '../../types';
|
||||
import { isOnPolicyListPage } from './selectors';
|
||||
import { ImmutableReducer } from '../../../common/store';
|
||||
import { AppAction } from '../../../common/store/actions';
|
||||
import { Immutable } from '../../../../common/endpoint/types';
|
||||
import { ImmutableReducer } from '../../../../../common/store';
|
||||
import { AppAction } from '../../../../../common/store/actions';
|
||||
import { Immutable } from '../../../../../../common/endpoint/types';
|
||||
|
||||
export const initialPolicyListState = (): PolicyListState => {
|
||||
return {
|
|
@ -6,8 +6,10 @@
|
|||
|
||||
import { createSelector } from 'reselect';
|
||||
import { parse } from 'query-string';
|
||||
import { matchPath } from 'react-router-dom';
|
||||
import { PolicyListState, PolicyListUrlSearchParams } from '../../types';
|
||||
import { Immutable } from '../../../../common/endpoint/types';
|
||||
import { Immutable } from '../../../../../../common/endpoint/types';
|
||||
import { MANAGEMENT_ROUTING_POLICIES_PATH } from '../../../../common/constants';
|
||||
|
||||
const PAGE_SIZES = Object.freeze([10, 20, 50]);
|
||||
|
||||
|
@ -24,7 +26,12 @@ export const selectIsLoading = (state: Immutable<PolicyListState>) => state.isLo
|
|||
export const selectApiError = (state: Immutable<PolicyListState>) => state.apiError;
|
||||
|
||||
export const isOnPolicyListPage = (state: Immutable<PolicyListState>) => {
|
||||
return state.location?.pathname === '/policy';
|
||||
return (
|
||||
matchPath(state.location?.pathname ?? '', {
|
||||
path: MANAGEMENT_ROUTING_POLICIES_PATH,
|
||||
exact: true,
|
||||
}) !== null
|
||||
);
|
||||
};
|
||||
|
||||
const routeLocation = (state: Immutable<PolicyListState>) => state.location;
|
|
@ -5,8 +5,8 @@
|
|||
*/
|
||||
|
||||
import { sendGetDatasource, sendGetEndpointSpecificDatasources } from './ingest';
|
||||
import { httpServiceMock } from '../../../../../../../../src/core/public/mocks';
|
||||
import { DATASOURCE_SAVED_OBJECT_TYPE } from '../../../../../../ingest_manager/common';
|
||||
import { httpServiceMock } from '../../../../../../../../../../src/core/public/mocks';
|
||||
import { DATASOURCE_SAVED_OBJECT_TYPE } from '../../../../../../../../ingest_manager/common';
|
||||
|
||||
describe('ingest service', () => {
|
||||
let http: ReturnType<typeof httpServiceMock.createStartContract>;
|
|
@ -9,9 +9,9 @@ import {
|
|||
GetDatasourcesRequest,
|
||||
GetAgentStatusResponse,
|
||||
DATASOURCE_SAVED_OBJECT_TYPE,
|
||||
} from '../../../../../../ingest_manager/common';
|
||||
} from '../../../../../../../../ingest_manager/common';
|
||||
import { GetPolicyListResponse, GetPolicyResponse, UpdatePolicyResponse } from '../../../types';
|
||||
import { NewPolicyData } from '../../../../../common/endpoint/types';
|
||||
import { NewPolicyData } from '../../../../../../../common/endpoint/types';
|
||||
|
||||
const INGEST_API_ROOT = `/api/ingest_manager`;
|
||||
export const INGEST_API_DATASOURCES = `${INGEST_API_ROOT}/datasources`;
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import { HttpStart } from 'kibana/public';
|
||||
import { INGEST_API_DATASOURCES } from './services/ingest';
|
||||
import { EndpointDocGenerator } from '../../../../common/endpoint/generate_data';
|
||||
import { EndpointDocGenerator } from '../../../../../../common/endpoint/generate_data';
|
||||
import { GetPolicyListResponse } from '../../types';
|
||||
|
||||
const generator = new EndpointDocGenerator('policy-list');
|
|
@ -10,14 +10,14 @@ import {
|
|||
MalwareFields,
|
||||
UIPolicyConfig,
|
||||
AppLocation,
|
||||
} from '../../common/endpoint/types';
|
||||
import { ServerApiError } from '../common/types';
|
||||
} from '../../../../common/endpoint/types';
|
||||
import { ServerApiError } from '../../../common/types';
|
||||
import {
|
||||
GetAgentStatusResponse,
|
||||
GetDatasourcesResponse,
|
||||
GetOneDatasourceResponse,
|
||||
UpdateDatasourceResponse,
|
||||
} from '../../../ingest_manager/common';
|
||||
} from '../../../../../ingest_manager/common';
|
||||
|
||||
/**
|
||||
* Policy list store state
|
|
@ -8,12 +8,20 @@ import React from 'react';
|
|||
import { mount } from 'enzyme';
|
||||
|
||||
import { PolicyDetails } from './policy_details';
|
||||
import { EndpointDocGenerator } from '../../../common/endpoint/generate_data';
|
||||
import { createAppRootMockRenderer } from '../../common/mock/endpoint';
|
||||
import { EndpointDocGenerator } from '../../../../../common/endpoint/generate_data';
|
||||
import { createAppRootMockRenderer } from '../../../../common/mock/endpoint';
|
||||
import { getManagementUrl } from '../../../common/routing';
|
||||
|
||||
describe('Policy Details', () => {
|
||||
type FindReactWrapperResponse = ReturnType<ReturnType<typeof render>['find']>;
|
||||
|
||||
const policyDetailsPathUrl = getManagementUrl({
|
||||
name: 'policyDetails',
|
||||
policyId: '1',
|
||||
excludePrefix: true,
|
||||
});
|
||||
const policyListPathUrl = getManagementUrl({ name: 'policyList', excludePrefix: true });
|
||||
const policyListPathUrlWithPrefix = getManagementUrl({ name: 'policyList' });
|
||||
const sleep = (ms = 100) => new Promise((wakeup) => setTimeout(wakeup, ms));
|
||||
const generator = new EndpointDocGenerator();
|
||||
const { history, AppWrapper, coreStart } = createAppRootMockRenderer();
|
||||
|
@ -33,7 +41,7 @@ describe('Policy Details', () => {
|
|||
describe('when displayed with invalid id', () => {
|
||||
beforeEach(() => {
|
||||
http.get.mockReturnValue(Promise.reject(new Error('policy not found')));
|
||||
history.push('/policy/1');
|
||||
history.push(policyDetailsPathUrl);
|
||||
policyView = render(<PolicyDetails />);
|
||||
});
|
||||
|
||||
|
@ -77,7 +85,7 @@ describe('Policy Details', () => {
|
|||
|
||||
return Promise.reject(new Error('unknown API call!'));
|
||||
});
|
||||
history.push('/policy/1');
|
||||
history.push(policyDetailsPathUrl);
|
||||
policyView = render(<PolicyDetails />);
|
||||
});
|
||||
|
||||
|
@ -89,7 +97,7 @@ describe('Policy Details', () => {
|
|||
|
||||
const backToListButton = pageHeaderLeft.find('EuiButtonEmpty');
|
||||
expect(backToListButton.prop('iconType')).toBe('arrowLeft');
|
||||
expect(backToListButton.prop('href')).toBe('/mock/app/endpoint/policy');
|
||||
expect(backToListButton.prop('href')).toBe(policyListPathUrlWithPrefix);
|
||||
expect(backToListButton.text()).toBe('Back to policy list');
|
||||
|
||||
const pageTitle = pageHeaderLeft.find('[data-test-subj="pageViewHeaderLeftTitle"]');
|
||||
|
@ -101,9 +109,9 @@ describe('Policy Details', () => {
|
|||
const backToListButton = policyView.find(
|
||||
'EuiPageHeaderSection[data-test-subj="pageViewHeaderLeft"] EuiButtonEmpty'
|
||||
);
|
||||
expect(history.location.pathname).toEqual('/policy/1');
|
||||
expect(history.location.pathname).toEqual(policyDetailsPathUrl);
|
||||
backToListButton.simulate('click', { button: 0 });
|
||||
expect(history.location.pathname).toEqual('/policy');
|
||||
expect(history.location.pathname).toEqual(policyListPathUrl);
|
||||
});
|
||||
it('should display agent stats', async () => {
|
||||
await asyncActions;
|
||||
|
@ -130,9 +138,9 @@ describe('Policy Details', () => {
|
|||
const cancelbutton = policyView.find(
|
||||
'EuiButtonEmpty[data-test-subj="policyDetailsCancelButton"]'
|
||||
);
|
||||
expect(history.location.pathname).toEqual('/policy/1');
|
||||
expect(history.location.pathname).toEqual(policyDetailsPathUrl);
|
||||
cancelbutton.simulate('click', { button: 0 });
|
||||
expect(history.location.pathname).toEqual('/policy');
|
||||
expect(history.location.pathname).toEqual(policyListPathUrl);
|
||||
});
|
||||
it('should display save button', async () => {
|
||||
await asyncActions;
|
|
@ -28,18 +28,21 @@ import {
|
|||
isLoading,
|
||||
apiError,
|
||||
} from '../store/policy_details/selectors';
|
||||
import { useKibana } from '../../../../../../src/plugins/kibana_react/public';
|
||||
import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public';
|
||||
import { AgentsSummary } from './agents_summary';
|
||||
import { VerticalDivider } from './vertical_divider';
|
||||
import { WindowsEvents, MacEvents, LinuxEvents } from './policy_forms/events';
|
||||
import { MalwareProtections } from './policy_forms/protections/malware';
|
||||
import { AppAction } from '../../common/store/actions';
|
||||
import { useNavigateByRouterEventHandler } from '../../common/hooks/endpoint/use_navigate_by_router_event_handler';
|
||||
import { PageView, PageViewHeaderTitle } from '../../common/components/endpoint/page_view';
|
||||
import { AppAction } from '../../../../common/store/actions';
|
||||
import { useNavigateByRouterEventHandler } from '../../../../common/hooks/endpoint/use_navigate_by_router_event_handler';
|
||||
import { PageViewHeaderTitle } from '../../../../common/components/endpoint/page_view';
|
||||
import { ManagementPageView } from '../../../components/management_page_view';
|
||||
import { SpyRoute } from '../../../../common/utils/route/spy_routes';
|
||||
import { getManagementUrl } from '../../../common/routing';
|
||||
|
||||
export const PolicyDetails = React.memo(() => {
|
||||
const dispatch = useDispatch<(action: AppAction) => void>();
|
||||
const { notifications, services } = useKibana();
|
||||
const { notifications } = useKibana();
|
||||
|
||||
// Store values
|
||||
const policyItem = usePolicyDetailsSelector(policyDetails);
|
||||
|
@ -81,7 +84,9 @@ export const PolicyDetails = React.memo(() => {
|
|||
}
|
||||
}, [notifications.toasts, policyName, policyUpdateStatus]);
|
||||
|
||||
const handleBackToListOnClick = useNavigateByRouterEventHandler('/policy');
|
||||
const handleBackToListOnClick = useNavigateByRouterEventHandler(
|
||||
getManagementUrl({ name: 'policyList', excludePrefix: true })
|
||||
);
|
||||
|
||||
const handleSaveOnClick = useCallback(() => {
|
||||
setShowConfirm(true);
|
||||
|
@ -103,7 +108,7 @@ export const PolicyDetails = React.memo(() => {
|
|||
// Else, if we have an error, then show error on the page.
|
||||
if (!policyItem) {
|
||||
return (
|
||||
<PageView viewType="details">
|
||||
<ManagementPageView viewType="details">
|
||||
{isPolicyLoading ? (
|
||||
<EuiLoadingSpinner size="xl" />
|
||||
) : policyApiError ? (
|
||||
|
@ -111,7 +116,8 @@ export const PolicyDetails = React.memo(() => {
|
|||
{policyApiError?.message}
|
||||
</EuiCallOut>
|
||||
) : null}
|
||||
</PageView>
|
||||
<SpyRoute />
|
||||
</ManagementPageView>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -122,7 +128,7 @@ export const PolicyDetails = React.memo(() => {
|
|||
iconType="arrowLeft"
|
||||
contentProps={{ style: { paddingLeft: '0' } }}
|
||||
onClick={handleBackToListOnClick}
|
||||
href={`${services.http.basePath.get()}/app/endpoint/policy`}
|
||||
href={getManagementUrl({ name: 'policyList' })}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.siem.endpoint.policy.details.backToListTitle"
|
||||
|
@ -180,7 +186,7 @@ export const PolicyDetails = React.memo(() => {
|
|||
onConfirm={handleSaveConfirmation}
|
||||
/>
|
||||
)}
|
||||
<PageView
|
||||
<ManagementPageView
|
||||
viewType="details"
|
||||
data-test-subj="policyDetailsPage"
|
||||
headerLeft={headerLeftContent}
|
||||
|
@ -211,7 +217,8 @@ export const PolicyDetails = React.memo(() => {
|
|||
<MacEvents />
|
||||
<EuiSpacer size="l" />
|
||||
<LinuxEvents />
|
||||
</PageView>
|
||||
</ManagementPageView>
|
||||
<SpyRoute />
|
||||
</>
|
||||
);
|
||||
});
|
|
@ -11,7 +11,7 @@ import { useDispatch } from 'react-redux';
|
|||
import { usePolicyDetailsSelector } from '../../policy_hooks';
|
||||
import { policyConfig } from '../../../store/policy_details/selectors';
|
||||
import { PolicyDetailsAction } from '../../../store/policy_details';
|
||||
import { UIPolicyConfig } from '../../../../../common/endpoint/types';
|
||||
import { UIPolicyConfig } from '../../../../../../../common/endpoint/types';
|
||||
|
||||
export const EventsCheckbox = React.memo(function ({
|
||||
name,
|
|
@ -14,7 +14,7 @@ import { usePolicyDetailsSelector } from '../../policy_hooks';
|
|||
import { selectedLinuxEvents, totalLinuxEvents } from '../../../store/policy_details/selectors';
|
||||
import { ConfigForm } from '../config_form';
|
||||
import { getIn, setIn } from '../../../models/policy_details_config';
|
||||
import { UIPolicyConfig } from '../../../../../common/endpoint/types';
|
||||
import { UIPolicyConfig } from '../../../../../../../common/endpoint/types';
|
||||
|
||||
export const LinuxEvents = React.memo(() => {
|
||||
const selected = usePolicyDetailsSelector(selectedLinuxEvents);
|
|
@ -14,7 +14,7 @@ import { usePolicyDetailsSelector } from '../../policy_hooks';
|
|||
import { selectedMacEvents, totalMacEvents } from '../../../store/policy_details/selectors';
|
||||
import { ConfigForm } from '../config_form';
|
||||
import { getIn, setIn } from '../../../models/policy_details_config';
|
||||
import { UIPolicyConfig } from '../../../../../common/endpoint/types';
|
||||
import { UIPolicyConfig } from '../../../../../../../common/endpoint/types';
|
||||
|
||||
export const MacEvents = React.memo(() => {
|
||||
const selected = usePolicyDetailsSelector(selectedMacEvents);
|
|
@ -14,7 +14,7 @@ import { usePolicyDetailsSelector } from '../../policy_hooks';
|
|||
import { selectedWindowsEvents, totalWindowsEvents } from '../../../store/policy_details/selectors';
|
||||
import { ConfigForm } from '../config_form';
|
||||
import { setIn, getIn } from '../../../models/policy_details_config';
|
||||
import { UIPolicyConfig, Immutable } from '../../../../../common/endpoint/types';
|
||||
import { UIPolicyConfig, Immutable } from '../../../../../../../common/endpoint/types';
|
||||
|
||||
export const WindowsEvents = React.memo(() => {
|
||||
const selected = usePolicyDetailsSelector(selectedWindowsEvents);
|
|
@ -11,7 +11,7 @@ import { EuiRadio, EuiSwitch, EuiTitle, EuiSpacer, htmlIdGenerator } from '@elas
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
import { Immutable, ProtectionModes } from '../../../../../common/endpoint/types';
|
||||
import { Immutable, ProtectionModes } from '../../../../../../../common/endpoint/types';
|
||||
import { OS, MalwareProtectionOSes } from '../../../types';
|
||||
import { ConfigForm } from '../config_form';
|
||||
import { policyConfig } from '../../../store/policy_details/selectors';
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { useSelector } from 'react-redux';
|
||||
import { PolicyListState, PolicyDetailsState } from '../types';
|
||||
import { State } from '../../../../common/store';
|
||||
import {
|
||||
MANAGEMENT_STORE_GLOBAL_NAMESPACE,
|
||||
MANAGEMENT_STORE_POLICY_DETAILS_NAMESPACE,
|
||||
MANAGEMENT_STORE_POLICY_LIST_NAMESPACE,
|
||||
} from '../../../common/constants';
|
||||
|
||||
/**
|
||||
* Narrows global state down to the PolicyListState before calling the provided Policy List Selector
|
||||
* @param selector
|
||||
*/
|
||||
export function usePolicyListSelector<TSelected>(selector: (state: PolicyListState) => TSelected) {
|
||||
return useSelector((state: State) => {
|
||||
return selector(
|
||||
state[MANAGEMENT_STORE_GLOBAL_NAMESPACE][
|
||||
MANAGEMENT_STORE_POLICY_LIST_NAMESPACE
|
||||
] as PolicyListState
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Narrows global state down to the PolicyDetailsState before calling the provided Policy Details Selector
|
||||
* @param selector
|
||||
*/
|
||||
export function usePolicyDetailsSelector<TSelected>(
|
||||
selector: (state: PolicyDetailsState) => TSelected
|
||||
) {
|
||||
return useSelector((state: State) =>
|
||||
selector(
|
||||
state[MANAGEMENT_STORE_GLOBAL_NAMESPACE][
|
||||
MANAGEMENT_STORE_POLICY_DETAILS_NAMESPACE
|
||||
] as PolicyDetailsState
|
||||
)
|
||||
);
|
||||
}
|
|
@ -20,11 +20,13 @@ import {
|
|||
} from '../store/policy_list/selectors';
|
||||
import { usePolicyListSelector } from './policy_hooks';
|
||||
import { PolicyListAction } from '../store/policy_list';
|
||||
import { useKibana } from '../../../../../../src/plugins/kibana_react/public';
|
||||
import { Immutable, PolicyData } from '../../../common/endpoint/types';
|
||||
import { useNavigateByRouterEventHandler } from '../../common/hooks/endpoint/use_navigate_by_router_event_handler';
|
||||
import { PageView } from '../../common/components/endpoint/page_view';
|
||||
import { LinkToApp } from '../../common/components/endpoint/link_to_app';
|
||||
import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public';
|
||||
import { Immutable, PolicyData } from '../../../../../common/endpoint/types';
|
||||
import { useNavigateByRouterEventHandler } from '../../../../common/hooks/endpoint/use_navigate_by_router_event_handler';
|
||||
import { LinkToApp } from '../../../../common/components/endpoint/link_to_app';
|
||||
import { ManagementPageView } from '../../../components/management_page_view';
|
||||
import { SpyRoute } from '../../../../common/utils/route/spy_routes';
|
||||
import { getManagementUrl } from '../../../common/routing';
|
||||
|
||||
interface TableChangeCallbackArguments {
|
||||
page: { index: number; size: number };
|
||||
|
@ -93,14 +95,13 @@ export const PolicyList = React.memo(() => {
|
|||
}),
|
||||
// eslint-disable-next-line react/display-name
|
||||
render: (value: string, item: Immutable<PolicyData>) => {
|
||||
const routeUri = `/policy/${item.id}`;
|
||||
return (
|
||||
<PolicyLink
|
||||
name={value}
|
||||
route={routeUri}
|
||||
href={services.application.getUrlForApp('endpoint') + routeUri}
|
||||
/>
|
||||
);
|
||||
const routePath = getManagementUrl({
|
||||
name: 'policyDetails',
|
||||
policyId: item.id,
|
||||
excludePrefix: true,
|
||||
});
|
||||
const routeUrl = getManagementUrl({ name: 'policyDetails', policyId: item.id });
|
||||
return <PolicyLink name={value} route={routePath} href={routeUrl} />;
|
||||
},
|
||||
truncateText: true,
|
||||
},
|
||||
|
@ -150,7 +151,7 @@ export const PolicyList = React.memo(() => {
|
|||
);
|
||||
|
||||
return (
|
||||
<PageView
|
||||
<ManagementPageView
|
||||
viewType="list"
|
||||
data-test-subj="policyListPage"
|
||||
headerLeft={i18n.translate('xpack.siem.endpoint.policyList.viewTitle', {
|
||||
|
@ -174,7 +175,8 @@ export const PolicyList = React.memo(() => {
|
|||
onChange={handleTableChange}
|
||||
data-test-subj="policyTable"
|
||||
/>
|
||||
</PageView>
|
||||
<SpyRoute />
|
||||
</ManagementPageView>
|
||||
);
|
||||
});
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import styled from 'styled-components';
|
||||
import { EuiTheme } from '../../../../../legacy/common/eui_styled_components';
|
||||
import { EuiTheme } from '../../../../../../../legacy/common/eui_styled_components';
|
||||
|
||||
type SpacingOptions = keyof EuiTheme['eui']['spacerSizes'];
|
||||
|
18
x-pack/plugins/siem/public/management/routes.tsx
Normal file
18
x-pack/plugins/siem/public/management/routes.tsx
Normal file
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Route } from 'react-router-dom';
|
||||
import { ManagementContainer } from './pages';
|
||||
import { MANAGEMENT_ROUTING_ROOT_PATH } from './common/constants';
|
||||
|
||||
/**
|
||||
* Returns the React Router Routes for the management area
|
||||
*/
|
||||
export const getManagementRoutes = () => [
|
||||
// Mounts the Management interface on `/management`
|
||||
<Route path={MANAGEMENT_ROUTING_ROOT_PATH} component={ManagementContainer} />,
|
||||
];
|
8
x-pack/plugins/siem/public/management/store/index.ts
Normal file
8
x-pack/plugins/siem/public/management/store/index.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export { managementReducer, getManagementInitialState } from './reducer';
|
||||
export { managementMiddlewareFactory } from './middleware';
|
33
x-pack/plugins/siem/public/management/store/middleware.ts
Normal file
33
x-pack/plugins/siem/public/management/store/middleware.ts
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { ImmutableMultipleMiddlewareFactory, substateMiddlewareFactory } from '../../common/store';
|
||||
import { policyListMiddlewareFactory } from '../pages/policy/store/policy_list';
|
||||
import { policyDetailsMiddlewareFactory } from '../pages/policy/store/policy_details';
|
||||
import {
|
||||
MANAGEMENT_STORE_GLOBAL_NAMESPACE,
|
||||
MANAGEMENT_STORE_POLICY_DETAILS_NAMESPACE,
|
||||
MANAGEMENT_STORE_POLICY_LIST_NAMESPACE,
|
||||
} from '../common/constants';
|
||||
|
||||
// @ts-ignore
|
||||
export const managementMiddlewareFactory: ImmutableMultipleMiddlewareFactory = (
|
||||
coreStart,
|
||||
depsStart
|
||||
) => {
|
||||
return [
|
||||
substateMiddlewareFactory(
|
||||
(globalState) =>
|
||||
globalState[MANAGEMENT_STORE_GLOBAL_NAMESPACE][MANAGEMENT_STORE_POLICY_LIST_NAMESPACE],
|
||||
policyListMiddlewareFactory(coreStart, depsStart)
|
||||
),
|
||||
substateMiddlewareFactory(
|
||||
(globalState) =>
|
||||
globalState[MANAGEMENT_STORE_GLOBAL_NAMESPACE][MANAGEMENT_STORE_POLICY_DETAILS_NAMESPACE],
|
||||
policyDetailsMiddlewareFactory(coreStart, depsStart)
|
||||
),
|
||||
];
|
||||
};
|
45
x-pack/plugins/siem/public/management/store/reducer.ts
Normal file
45
x-pack/plugins/siem/public/management/store/reducer.ts
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { combineReducers as reduxCombineReducers } from 'redux';
|
||||
import {
|
||||
initialPolicyDetailsState,
|
||||
policyDetailsReducer,
|
||||
} from '../pages/policy/store/policy_details/reducer';
|
||||
import {
|
||||
initialPolicyListState,
|
||||
policyListReducer,
|
||||
} from '../pages/policy/store/policy_list/reducer';
|
||||
import {
|
||||
MANAGEMENT_STORE_POLICY_DETAILS_NAMESPACE,
|
||||
MANAGEMENT_STORE_POLICY_LIST_NAMESPACE,
|
||||
} from '../common/constants';
|
||||
import { ImmutableCombineReducers } from '../../common/store';
|
||||
import { AppAction } from '../../common/store/actions';
|
||||
import { ManagementState } from './types';
|
||||
|
||||
// Change the type of `combinerReducers` locally
|
||||
const combineReducers: ImmutableCombineReducers = reduxCombineReducers;
|
||||
|
||||
/**
|
||||
* Returns the initial state of the store for the SIEM Management section
|
||||
*/
|
||||
export const getManagementInitialState = (): ManagementState => {
|
||||
return {
|
||||
[MANAGEMENT_STORE_POLICY_LIST_NAMESPACE]: initialPolicyListState(),
|
||||
[MANAGEMENT_STORE_POLICY_DETAILS_NAMESPACE]: initialPolicyDetailsState(),
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Redux store reducer for the SIEM Management section
|
||||
*/
|
||||
export const managementReducer = combineReducers<ManagementState, AppAction>({
|
||||
// @ts-ignore
|
||||
[MANAGEMENT_STORE_POLICY_LIST_NAMESPACE]: policyListReducer,
|
||||
// @ts-ignore
|
||||
[MANAGEMENT_STORE_POLICY_DETAILS_NAMESPACE]: policyDetailsReducer,
|
||||
});
|
26
x-pack/plugins/siem/public/management/store/types.ts
Normal file
26
x-pack/plugins/siem/public/management/store/types.ts
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { Immutable } from '../../../common/endpoint/types';
|
||||
import { PolicyDetailsState, PolicyListState } from '../pages/policy/types';
|
||||
import { ImmutableReducer } from '../../common/store';
|
||||
import { AppAction } from '../../common/store/actions';
|
||||
|
||||
/**
|
||||
* Redux store state for the Management section
|
||||
*/
|
||||
export interface ManagementState {
|
||||
policyDetails: Immutable<PolicyDetailsState>;
|
||||
policyList: Immutable<PolicyListState>;
|
||||
}
|
||||
|
||||
export interface ManagementPluginState {
|
||||
management: ManagementState;
|
||||
}
|
||||
|
||||
export interface ManagementPluginReducer {
|
||||
management: ImmutableReducer<ManagementState, AppAction>;
|
||||
}
|
36
x-pack/plugins/siem/public/management/types.ts
Normal file
36
x-pack/plugins/siem/public/management/types.ts
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { SiemPageName } from '../app/types';
|
||||
|
||||
/**
|
||||
* The type for the management store global namespace. Used mostly internally to reference
|
||||
* the type while defining more complex interfaces/types
|
||||
*/
|
||||
export type ManagementStoreGlobalNamespace = 'management';
|
||||
|
||||
/**
|
||||
* The management list of sub-tabs. Changes to these will impact the Router routes.
|
||||
*/
|
||||
export enum ManagementSubTab {
|
||||
endpoints = 'endpoints',
|
||||
policies = 'policy',
|
||||
}
|
||||
|
||||
/**
|
||||
* The URL route params for the Management Policy List section
|
||||
*/
|
||||
export interface ManagementRoutePolicyListParams {
|
||||
pageName: SiemPageName.management;
|
||||
tabName: ManagementSubTab.policies;
|
||||
}
|
||||
|
||||
/**
|
||||
* The URL route params for the Management Policy Details section
|
||||
*/
|
||||
export interface ManagementRoutePolicyDetailsParams extends ManagementRoutePolicyListParams {
|
||||
policyId: string;
|
||||
}
|
|
@ -64,13 +64,8 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
|
|||
const overviewSubPlugin = new (await import('./overview')).Overview();
|
||||
const timelinesSubPlugin = new (await import('./timelines')).Timelines();
|
||||
const endpointAlertsSubPlugin = new (await import('./endpoint_alerts')).EndpointAlerts();
|
||||
const endpoitHostsSubPlugin = new (await import('./endpoint_hosts')).EndpointHosts();
|
||||
const endpointPolicyListSubPlugin = new (
|
||||
await import('./endpoint_policy/list')
|
||||
).EndpointPolicyList();
|
||||
const endpointPolicyDetailsSubPlugin = new (
|
||||
await import('./endpoint_policy/details')
|
||||
).EndpointPolicyDetails();
|
||||
const endpointHostsSubPlugin = new (await import('./endpoint_hosts')).EndpointHosts();
|
||||
const managementSubPlugin = new (await import('./management')).Management();
|
||||
|
||||
const alertsStart = alertsSubPlugin.start();
|
||||
const casesStart = casesSubPlugin.start();
|
||||
|
@ -79,12 +74,8 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
|
|||
const overviewStart = overviewSubPlugin.start();
|
||||
const timelinesStart = timelinesSubPlugin.start();
|
||||
const endpointAlertsStart = endpointAlertsSubPlugin.start(coreStart, startPlugins);
|
||||
const endpointHostsStart = endpoitHostsSubPlugin.start(coreStart, startPlugins);
|
||||
const endpointPolicyListStart = endpointPolicyListSubPlugin.start(coreStart, startPlugins);
|
||||
const endpointPolicyDetailsStart = endpointPolicyDetailsSubPlugin.start(
|
||||
coreStart,
|
||||
startPlugins
|
||||
);
|
||||
const endpointHostsStart = endpointHostsSubPlugin.start(coreStart, startPlugins);
|
||||
const managementSubPluginStart = managementSubPlugin.start(coreStart, startPlugins);
|
||||
|
||||
return renderApp(services, params, {
|
||||
routes: [
|
||||
|
@ -96,8 +87,7 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
|
|||
...timelinesStart.routes,
|
||||
...endpointAlertsStart.routes,
|
||||
...endpointHostsStart.routes,
|
||||
...endpointPolicyListStart.routes,
|
||||
...endpointPolicyDetailsStart.routes,
|
||||
...managementSubPluginStart.routes,
|
||||
],
|
||||
store: {
|
||||
initialState: {
|
||||
|
@ -106,8 +96,7 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
|
|||
...timelinesStart.store.initialState,
|
||||
...endpointAlertsStart.store.initialState,
|
||||
...endpointHostsStart.store.initialState,
|
||||
...endpointPolicyListStart.store.initialState,
|
||||
...endpointPolicyDetailsStart.store.initialState,
|
||||
...managementSubPluginStart.store.initialState,
|
||||
},
|
||||
reducer: {
|
||||
...hostsStart.store.reducer,
|
||||
|
@ -115,22 +104,12 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
|
|||
...timelinesStart.store.reducer,
|
||||
...endpointAlertsStart.store.reducer,
|
||||
...endpointHostsStart.store.reducer,
|
||||
...endpointPolicyListStart.store.reducer,
|
||||
...endpointPolicyDetailsStart.store.reducer,
|
||||
...managementSubPluginStart.store.reducer,
|
||||
},
|
||||
middlewares: [
|
||||
...(endpointAlertsStart.store.middleware != null
|
||||
? [endpointAlertsStart.store.middleware]
|
||||
: []),
|
||||
...(endpointHostsStart.store.middleware != null
|
||||
? [endpointHostsStart.store.middleware]
|
||||
: []),
|
||||
...(endpointPolicyListStart.store.middleware != null
|
||||
? [endpointPolicyListStart.store.middleware]
|
||||
: []),
|
||||
...(endpointPolicyDetailsStart.store.middleware != null
|
||||
? [endpointPolicyDetailsStart.store.middleware]
|
||||
: []),
|
||||
...(endpointAlertsStart.store.middleware ?? []),
|
||||
...(endpointHostsStart.store.middleware ?? []),
|
||||
...(managementSubPluginStart.store.middleware ?? []),
|
||||
],
|
||||
},
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue