mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
# Backport This will backport the following commits from `main` to `8.6`: - [[Security Solution][Bug] Add privilege check in open timeline (#147964)](https://github.com/elastic/kibana/pull/147964) <!--- Backport version: 8.9.4 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) <!--BACKPORT [{"author":{"name":"christineweng","email":"18648970+christineweng@users.noreply.github.com"},"sourceCommit":{"committedDate":"2023-01-03T19:21:48Z","message":"[Security Solution][Bug] Add privilege check in open timeline (#147964)\n\n## Summary\r\n\r\nThis PR contains fixe for\r\nhttps://github.com/elastic/kibana/issues/147544. On Timelines page, a\r\nKibana read-only user was able to see and click on options to create and\r\nduplicate timelines. This PR fixes this bug by checking user privilege\r\n(have crud access) before showing timeline actions.\r\n\r\n## After: \r\nUser with read only access to kibana security solutions can: \r\n1) select timelines\r\n2) export timelines\r\n3) export timelines in bulk\r\n\r\nUser with crud access to kibana security solutions can: \r\n1) select timelines\r\n2) have the options to modify timelines as before\r\n3) bulk actions include delete timelines and export timelines\r\n4) see and click on 'import', ' Create new timeline', 'Create new\r\ntimeline template' buttons\r\n\r\n### User with read access but not crud access\r\n- Have access to export ('Export selected'), cannot see 'Create new\r\ntimeline' buttons\r\n\r\n\r\n\r\n- 'Export selected' in bulk actions\r\n\r\n\r\n\r\n### User with full access\r\n\r\n\r\n\r\n- 'Export selected' and 'Delete selected' available in bulk actions\r\ndropdown\r\n\r\n\r\n\r\n### Checklist\r\n\r\n- [x] [Unit or functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere updated or added to match the most common scenarios\r\n\r\nCo-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>","sha":"3abf705b10926d3c6221504dd5575b97d15c9a31","branchLabelMapping":{"^v8.7.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","backport missing","Team:Threat Hunting","Team: SecuritySolution","Feature:Timeline","Team:Threat Hunting:Investigations","v8.6.0","v8.7.0"],"number":147964,"url":"https://github.com/elastic/kibana/pull/147964","mergeCommit":{"message":"[Security Solution][Bug] Add privilege check in open timeline (#147964)\n\n## Summary\r\n\r\nThis PR contains fixe for\r\nhttps://github.com/elastic/kibana/issues/147544. On Timelines page, a\r\nKibana read-only user was able to see and click on options to create and\r\nduplicate timelines. This PR fixes this bug by checking user privilege\r\n(have crud access) before showing timeline actions.\r\n\r\n## After: \r\nUser with read only access to kibana security solutions can: \r\n1) select timelines\r\n2) export timelines\r\n3) export timelines in bulk\r\n\r\nUser with crud access to kibana security solutions can: \r\n1) select timelines\r\n2) have the options to modify timelines as before\r\n3) bulk actions include delete timelines and export timelines\r\n4) see and click on 'import', ' Create new timeline', 'Create new\r\ntimeline template' buttons\r\n\r\n### User with read access but not crud access\r\n- Have access to export ('Export selected'), cannot see 'Create new\r\ntimeline' buttons\r\n\r\n\r\n\r\n- 'Export selected' in bulk actions\r\n\r\n\r\n\r\n### User with full access\r\n\r\n\r\n\r\n- 'Export selected' and 'Delete selected' available in bulk actions\r\ndropdown\r\n\r\n\r\n\r\n### Checklist\r\n\r\n- [x] [Unit or functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere updated or added to match the most common scenarios\r\n\r\nCo-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>","sha":"3abf705b10926d3c6221504dd5575b97d15c9a31"}},"sourceBranch":"main","suggestedTargetBranches":["8.6"],"targetPullRequestStates":[{"branch":"8.6","label":"v8.6.0","labelRegex":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v8.7.0","labelRegex":"^v8.7.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/147964","number":147964,"mergeCommit":{"message":"[Security Solution][Bug] Add privilege check in open timeline (#147964)\n\n## Summary\r\n\r\nThis PR contains fixe for\r\nhttps://github.com/elastic/kibana/issues/147544. On Timelines page, a\r\nKibana read-only user was able to see and click on options to create and\r\nduplicate timelines. This PR fixes this bug by checking user privilege\r\n(have crud access) before showing timeline actions.\r\n\r\n## After: \r\nUser with read only access to kibana security solutions can: \r\n1) select timelines\r\n2) export timelines\r\n3) export timelines in bulk\r\n\r\nUser with crud access to kibana security solutions can: \r\n1) select timelines\r\n2) have the options to modify timelines as before\r\n3) bulk actions include delete timelines and export timelines\r\n4) see and click on 'import', ' Create new timeline', 'Create new\r\ntimeline template' buttons\r\n\r\n### User with read access but not crud access\r\n- Have access to export ('Export selected'), cannot see 'Create new\r\ntimeline' buttons\r\n\r\n\r\n\r\n- 'Export selected' in bulk actions\r\n\r\n\r\n\r\n### User with full access\r\n\r\n\r\n\r\n- 'Export selected' and 'Delete selected' available in bulk actions\r\ndropdown\r\n\r\n\r\n\r\n### Checklist\r\n\r\n- [x] [Unit or functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere updated or added to match the most common scenarios\r\n\r\nCo-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>","sha":"3abf705b10926d3c6221504dd5575b97d15c9a31"}}]}] BACKPORT--> Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
8debf8647b
commit
d6a411167a
7 changed files with 174 additions and 64 deletions
|
@ -71,6 +71,33 @@ export const useEditTimelineBatchActions = ({
|
|||
const getBatchItemsPopoverContent = useCallback(
|
||||
(closePopover: () => void) => {
|
||||
const disabled = selectedItems == null || selectedItems.length === 0;
|
||||
const items = [];
|
||||
if (selectedItems) {
|
||||
items.push(
|
||||
<EuiContextMenuItem
|
||||
data-test-subj="export-timeline-action"
|
||||
disabled={disabled}
|
||||
icon="exportAction"
|
||||
key="ExportItemKey"
|
||||
onClick={handleEnableExportTimelineDownloader}
|
||||
>
|
||||
{i18n.EXPORT_SELECTED}
|
||||
</EuiContextMenuItem>
|
||||
);
|
||||
}
|
||||
if (deleteTimelines) {
|
||||
items.push(
|
||||
<EuiContextMenuItem
|
||||
data-test-subj="delete-timeline-action"
|
||||
disabled={disabled}
|
||||
icon="trash"
|
||||
key="DeleteItemKey"
|
||||
onClick={handleOnOpenDeleteTimelineModal}
|
||||
>
|
||||
{i18n.DELETE_SELECTED}
|
||||
</EuiContextMenuItem>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<EditTimelineActions
|
||||
|
@ -87,29 +114,7 @@ export const useEditTimelineBatchActions = ({
|
|||
: selectedItems[0]?.title ?? ''
|
||||
}
|
||||
/>
|
||||
|
||||
<EuiContextMenuPanel
|
||||
items={[
|
||||
<EuiContextMenuItem
|
||||
data-test-subj="export-timeline-action"
|
||||
disabled={disabled}
|
||||
icon="exportAction"
|
||||
key="ExportItemKey"
|
||||
onClick={handleEnableExportTimelineDownloader}
|
||||
>
|
||||
{i18n.EXPORT_SELECTED}
|
||||
</EuiContextMenuItem>,
|
||||
<EuiContextMenuItem
|
||||
data-test-subj="delete-timeline-action"
|
||||
disabled={disabled}
|
||||
icon="trash"
|
||||
key="DeleteItemKey"
|
||||
onClick={handleOnOpenDeleteTimelineModal}
|
||||
>
|
||||
{i18n.DELETE_SELECTED}
|
||||
</EuiContextMenuItem>,
|
||||
]}
|
||||
/>
|
||||
<EuiContextMenuPanel items={items} />
|
||||
</>
|
||||
);
|
||||
},
|
||||
|
|
|
@ -29,6 +29,7 @@ import { TimelineTabsStyle } from './types';
|
|||
import type { UseTimelineTypesArgs, UseTimelineTypesResult } from './use_timeline_types';
|
||||
import { useTimelineTypes } from './use_timeline_types';
|
||||
import { deleteTimelinesByIds } from '../../containers/api';
|
||||
import { useUserPrivileges } from '../../../common/components/user_privileges';
|
||||
|
||||
jest.mock('react-router-dom', () => {
|
||||
const originalModule = jest.requireActual('react-router-dom');
|
||||
|
@ -78,6 +79,9 @@ jest.mock('../../containers/api', () => ({
|
|||
deleteTimelinesByIds: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('../../../common/components/user_privileges');
|
||||
const useUserPrivilegesMock = useUserPrivileges as jest.Mock;
|
||||
|
||||
describe('StatefulOpenTimeline', () => {
|
||||
const title = 'All Timelines / Open Timelines';
|
||||
let mockHistory: History[];
|
||||
|
@ -88,6 +92,9 @@ describe('StatefulOpenTimeline', () => {
|
|||
tabName: TimelineType.default,
|
||||
pageName: SecurityPageName.timelines,
|
||||
});
|
||||
useUserPrivilegesMock.mockReturnValue({
|
||||
kibanaSecuritySolutionsPrivileges: { crud: true, read: true },
|
||||
});
|
||||
mockHistory = [];
|
||||
(useHistory as jest.Mock).mockReturnValue(mockHistory);
|
||||
(useGetAllTimeline as unknown as jest.Mock).mockReturnValue({
|
||||
|
|
|
@ -20,6 +20,7 @@ import { OpenTimeline } from './open_timeline';
|
|||
import { DEFAULT_SORT_DIRECTION, DEFAULT_SORT_FIELD } from './constants';
|
||||
import { TimelineType, TimelineStatus } from '../../../../common/types/timeline';
|
||||
import { getMockTheme } from '../../../common/lib/kibana/kibana_react.mock';
|
||||
import { useUserPrivileges } from '../../../common/components/user_privileges';
|
||||
|
||||
jest.mock('../../../common/lib/kibana');
|
||||
|
||||
|
@ -42,6 +43,9 @@ const mockTheme = getMockTheme({
|
|||
},
|
||||
});
|
||||
|
||||
jest.mock('../../../common/components/user_privileges');
|
||||
const useUserPrivilegesMock = useUserPrivileges as jest.Mock;
|
||||
|
||||
describe('OpenTimeline', () => {
|
||||
const title = 'All Timelines / Open Timelines';
|
||||
|
||||
|
@ -102,6 +106,9 @@ describe('OpenTimeline', () => {
|
|||
});
|
||||
|
||||
test('it shows the delete action columns when onDeleteSelected and deleteTimelines are specified', () => {
|
||||
useUserPrivilegesMock.mockReturnValue({
|
||||
kibanaSecuritySolutionsPrivileges: { crud: true, read: true },
|
||||
});
|
||||
const defaultProps = getDefaultTestProps(mockResults);
|
||||
const wrapper = mountWithIntl(
|
||||
<ThemeProvider theme={mockTheme}>
|
||||
|
@ -177,6 +184,29 @@ describe('OpenTimeline', () => {
|
|||
expect(props.actionTimelineToShow).not.toContain('delete');
|
||||
});
|
||||
|
||||
test('it does NOT show the delete action when user has read only access', () => {
|
||||
const defaultProps = {
|
||||
...getDefaultTestProps(mockResults),
|
||||
onDeleteSelected: undefined,
|
||||
deleteTimelines: undefined,
|
||||
};
|
||||
useUserPrivilegesMock.mockReturnValue({
|
||||
kibanaSecuritySolutionsPrivileges: { crud: false, read: true },
|
||||
});
|
||||
const wrapper = mountWithIntl(
|
||||
<ThemeProvider theme={mockTheme}>
|
||||
<OpenTimeline {...defaultProps} />
|
||||
</ThemeProvider>
|
||||
);
|
||||
|
||||
const props = wrapper
|
||||
.find('[data-test-subj="timelines-table"]')
|
||||
.first()
|
||||
.props() as TimelinesTableProps;
|
||||
|
||||
expect(props.actionTimelineToShow).not.toContain('delete');
|
||||
});
|
||||
|
||||
test('it renders an empty string when the query is an empty string', () => {
|
||||
const defaultProps = {
|
||||
...getDefaultTestProps(mockResults),
|
||||
|
@ -324,6 +354,9 @@ describe('OpenTimeline', () => {
|
|||
});
|
||||
|
||||
test('it should disable delete timeline if no timeline is selected', async () => {
|
||||
useUserPrivilegesMock.mockReturnValue({
|
||||
kibanaSecuritySolutionsPrivileges: { crud: true, read: true },
|
||||
});
|
||||
const defaultProps = {
|
||||
...getDefaultTestProps(mockResults),
|
||||
timelineStatus: null,
|
||||
|
@ -372,6 +405,9 @@ describe('OpenTimeline', () => {
|
|||
});
|
||||
|
||||
test('it should enable delete timeline if a timeline is selected', async () => {
|
||||
useUserPrivilegesMock.mockReturnValue({
|
||||
kibanaSecuritySolutionsPrivileges: { crud: true, read: true },
|
||||
});
|
||||
const defaultProps = {
|
||||
...getDefaultTestProps(mockResults),
|
||||
timelineStatus: null,
|
||||
|
@ -396,6 +432,9 @@ describe('OpenTimeline', () => {
|
|||
});
|
||||
|
||||
test("it should render a selectable timeline table if timelineStatus is active (selecting custom templates' tab)", () => {
|
||||
useUserPrivilegesMock.mockReturnValue({
|
||||
kibanaSecuritySolutionsPrivileges: { crud: true, read: true },
|
||||
});
|
||||
const defaultProps = {
|
||||
...getDefaultTestProps(mockResults),
|
||||
timelineStatus: TimelineStatus.active,
|
||||
|
@ -411,6 +450,25 @@ describe('OpenTimeline', () => {
|
|||
).toEqual(['createFrom', 'duplicate', 'export', 'selectable', 'delete']);
|
||||
});
|
||||
|
||||
test('it should NOT include createFrom, duplicate, delete in timeline actions when user has read only access', () => {
|
||||
const defaultProps = {
|
||||
...getDefaultTestProps(mockResults),
|
||||
timelineStatus: TimelineStatus.active,
|
||||
};
|
||||
useUserPrivilegesMock.mockReturnValue({
|
||||
kibanaSecuritySolutionsPrivileges: { crud: false, read: true },
|
||||
});
|
||||
const wrapper = mountWithIntl(
|
||||
<ThemeProvider theme={mockTheme}>
|
||||
<OpenTimeline {...defaultProps} />
|
||||
</ThemeProvider>
|
||||
);
|
||||
|
||||
expect(
|
||||
wrapper.find('[data-test-subj="timelines-table"]').first().prop('actionTimelineToShow')
|
||||
).toEqual(['export', 'selectable']);
|
||||
});
|
||||
|
||||
test("it should render selected count if timelineStatus is active (selecting custom templates' tab)", () => {
|
||||
const defaultProps = {
|
||||
...getDefaultTestProps(mockResults),
|
||||
|
@ -440,6 +498,9 @@ describe('OpenTimeline', () => {
|
|||
});
|
||||
|
||||
test("it should not render a selectable timeline table if timelineStatus is immutable (selecting Elastic templates' tab)", () => {
|
||||
useUserPrivilegesMock.mockReturnValue({
|
||||
kibanaSecuritySolutionsPrivileges: { crud: true, read: true },
|
||||
});
|
||||
const defaultProps = {
|
||||
...getDefaultTestProps(mockResults),
|
||||
timelineStatus: TimelineStatus.immutable,
|
||||
|
@ -484,6 +545,9 @@ describe('OpenTimeline', () => {
|
|||
});
|
||||
|
||||
test("it should render a selectable timeline table if timelineStatus is null (no template timelines' tab selected)", () => {
|
||||
useUserPrivilegesMock.mockReturnValue({
|
||||
kibanaSecuritySolutionsPrivileges: { crud: true, read: true },
|
||||
});
|
||||
const defaultProps = {
|
||||
...getDefaultTestProps(mockResults),
|
||||
timelineStatus: null,
|
||||
|
@ -499,6 +563,25 @@ describe('OpenTimeline', () => {
|
|||
).toEqual(['createFrom', 'duplicate', 'export', 'selectable', 'delete']);
|
||||
});
|
||||
|
||||
test("it should render a selectable timeline table if timelineStatus is null (no template timelines' tab selected) and user has read only access", () => {
|
||||
const defaultProps = {
|
||||
...getDefaultTestProps(mockResults),
|
||||
timelineStatus: null,
|
||||
};
|
||||
useUserPrivilegesMock.mockReturnValue({
|
||||
kibanaSecuritySolutionsPrivileges: { crud: false, read: true },
|
||||
});
|
||||
const wrapper = mountWithIntl(
|
||||
<ThemeProvider theme={mockTheme}>
|
||||
<OpenTimeline {...defaultProps} />
|
||||
</ThemeProvider>
|
||||
);
|
||||
|
||||
expect(
|
||||
wrapper.find('[data-test-subj="timelines-table"]').first().prop('actionTimelineToShow')
|
||||
).toEqual(['export', 'selectable']);
|
||||
});
|
||||
|
||||
test("it should render selected count if timelineStatus is null (no template timelines' tab selected)", () => {
|
||||
const defaultProps = {
|
||||
...getDefaultTestProps(mockResults),
|
||||
|
|
|
@ -21,7 +21,7 @@ import {
|
|||
} from '../../../common/components/utility_bar';
|
||||
|
||||
import { importTimelines } from '../../containers/api';
|
||||
|
||||
import { useUserPrivileges } from '../../../common/components/user_privileges';
|
||||
import { useEditTimelineBatchActions } from './edit_timeline_batch_actions';
|
||||
import { useEditTimelineActions } from './edit_timeline_actions';
|
||||
import { EditTimelineActions } from './export_timeline';
|
||||
|
@ -77,8 +77,9 @@ export const OpenTimeline = React.memo<OpenTimelineProps>(
|
|||
onCompleteEditTimelineAction,
|
||||
} = useEditTimelineActions();
|
||||
|
||||
const { kibanaSecuritySolutionsPrivileges } = useUserPrivileges();
|
||||
const { getBatchItemsPopoverContent } = useEditTimelineBatchActions({
|
||||
deleteTimelines,
|
||||
deleteTimelines: kibanaSecuritySolutionsPrivileges.crud ? deleteTimelines : undefined,
|
||||
selectedItems,
|
||||
tableRef,
|
||||
timelineType,
|
||||
|
@ -148,23 +149,30 @@ export const OpenTimeline = React.memo<OpenTimelineProps>(
|
|||
}, [setImportDataModalToggle, refetch]);
|
||||
|
||||
const actionTimelineToShow = useMemo<ActionTimelineToShow[]>(() => {
|
||||
const timelineActions: ActionTimelineToShow[] = ['createFrom', 'duplicate'];
|
||||
if (kibanaSecuritySolutionsPrivileges.crud) {
|
||||
const timelineActions: ActionTimelineToShow[] = ['createFrom', 'duplicate'];
|
||||
|
||||
if (timelineStatus !== TimelineStatus.immutable) {
|
||||
timelineActions.push('export');
|
||||
timelineActions.push('selectable');
|
||||
}
|
||||
|
||||
if (
|
||||
onDeleteSelected != null &&
|
||||
deleteTimelines != null &&
|
||||
timelineStatus !== TimelineStatus.immutable
|
||||
) {
|
||||
timelineActions.push('delete');
|
||||
}
|
||||
|
||||
return timelineActions;
|
||||
}
|
||||
// user with read access should only see export
|
||||
if (timelineStatus !== TimelineStatus.immutable) {
|
||||
timelineActions.push('export');
|
||||
timelineActions.push('selectable');
|
||||
return ['export', 'selectable'];
|
||||
}
|
||||
|
||||
if (
|
||||
onDeleteSelected != null &&
|
||||
deleteTimelines != null &&
|
||||
timelineStatus !== TimelineStatus.immutable
|
||||
) {
|
||||
timelineActions.push('delete');
|
||||
}
|
||||
|
||||
return timelineActions;
|
||||
}, [onDeleteSelected, deleteTimelines, timelineStatus]);
|
||||
return [];
|
||||
}, [timelineStatus, onDeleteSelected, deleteTimelines, kibanaSecuritySolutionsPrivileges]);
|
||||
|
||||
const SearchRowContent = useMemo(() => <>{templateTimelineFilter}</>, [templateTimelineFilter]);
|
||||
|
||||
|
|
|
@ -26,12 +26,14 @@ export const getActionsColumns = ({
|
|||
enableExportTimelineDownloader,
|
||||
onOpenDeleteTimelineModal,
|
||||
onOpenTimeline,
|
||||
hasCrudAccess,
|
||||
}: {
|
||||
actionTimelineToShow: ActionTimelineToShow[];
|
||||
deleteTimelines?: DeleteTimelines;
|
||||
enableExportTimelineDownloader?: EnableExportTimelineDownloader;
|
||||
onOpenDeleteTimelineModal?: OnOpenDeleteTimelineModal;
|
||||
onOpenTimeline: OnOpenTimeline;
|
||||
hasCrudAccess: boolean;
|
||||
}): [TimelineActionsOverflowColumns] => {
|
||||
const createTimelineFromTemplate = {
|
||||
name: i18n.CREATE_TIMELINE_FROM_TEMPLATE,
|
||||
|
@ -134,7 +136,7 @@ export const getActionsColumns = ({
|
|||
|
||||
return [
|
||||
{
|
||||
width: '80px',
|
||||
width: hasCrudAccess ? '80px' : '150px',
|
||||
actions: [
|
||||
createTimelineFromTemplate,
|
||||
createTemplateFromTimeline,
|
||||
|
|
|
@ -27,7 +27,7 @@ import { getExtendedColumns } from './extended_columns';
|
|||
import { getIconHeaderColumns } from './icon_header_columns';
|
||||
import type { TimelineTypeLiteralWithNull } from '../../../../../common/types/timeline';
|
||||
import { TimelineStatus, TimelineType } from '../../../../../common/types/timeline';
|
||||
|
||||
import { useUserPrivileges } from '../../../../common/components/user_privileges';
|
||||
// there are a number of type mismatches across this file
|
||||
const EuiBasicTable: any = _EuiBasicTable; // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
|
||||
|
@ -59,6 +59,7 @@ export const getTimelinesTableColumns = ({
|
|||
onToggleShowNotes,
|
||||
showExtendedColumns,
|
||||
timelineType,
|
||||
hasCrudAccess,
|
||||
}: {
|
||||
actionTimelineToShow: ActionTimelineToShow[];
|
||||
deleteTimelines?: DeleteTimelines;
|
||||
|
@ -70,6 +71,7 @@ export const getTimelinesTableColumns = ({
|
|||
onToggleShowNotes: OnToggleShowNotes;
|
||||
showExtendedColumns: boolean;
|
||||
timelineType: TimelineTypeLiteralWithNull;
|
||||
hasCrudAccess: boolean;
|
||||
}) => {
|
||||
return [
|
||||
...getCommonColumns({
|
||||
|
@ -87,6 +89,7 @@ export const getTimelinesTableColumns = ({
|
|||
enableExportTimelineDownloader,
|
||||
onOpenDeleteTimelineModal,
|
||||
onOpenTimeline,
|
||||
hasCrudAccess,
|
||||
})
|
||||
: []),
|
||||
];
|
||||
|
@ -170,7 +173,7 @@ export const TimelinesTable = React.memo<TimelinesTableProps>(
|
|||
onSelectionChange,
|
||||
};
|
||||
const basicTableProps = tableRef != null ? { ref: tableRef } : {};
|
||||
|
||||
const { kibanaSecuritySolutionsPrivileges } = useUserPrivileges();
|
||||
const columns = useMemo(
|
||||
() =>
|
||||
getTimelinesTableColumns({
|
||||
|
@ -184,6 +187,7 @@ export const TimelinesTable = React.memo<TimelinesTableProps>(
|
|||
onToggleShowNotes,
|
||||
showExtendedColumns,
|
||||
timelineType,
|
||||
hasCrudAccess: kibanaSecuritySolutionsPrivileges.crud,
|
||||
}),
|
||||
[
|
||||
actionTimelineToShow,
|
||||
|
@ -196,6 +200,7 @@ export const TimelinesTable = React.memo<TimelinesTableProps>(
|
|||
onToggleShowNotes,
|
||||
showExtendedColumns,
|
||||
timelineType,
|
||||
kibanaSecuritySolutionsPrivileges,
|
||||
]
|
||||
);
|
||||
|
||||
|
|
|
@ -47,9 +47,9 @@ export const TimelinesPageComponent: React.FC = () => {
|
|||
<>
|
||||
<SecuritySolutionPageWrapper>
|
||||
<HeaderPage title={i18n.PAGE_TITLE}>
|
||||
<EuiFlexGroup gutterSize="s" alignItems="center">
|
||||
<EuiFlexItem>
|
||||
{capabilitiesCanUserCRUD && (
|
||||
{capabilitiesCanUserCRUD && (
|
||||
<EuiFlexGroup gutterSize="s" alignItems="center">
|
||||
<EuiFlexItem>
|
||||
<EuiButton
|
||||
iconType="indexOpen"
|
||||
onClick={onImportTimelineBtnClick}
|
||||
|
@ -57,26 +57,26 @@ export const TimelinesPageComponent: React.FC = () => {
|
|||
>
|
||||
{i18n.ALL_TIMELINES_IMPORT_TIMELINE_TITLE}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
{tabName === TimelineType.default ? (
|
||||
<EuiFlexItem>
|
||||
<NewTimeline
|
||||
timelineId={TimelineId.active}
|
||||
outline={true}
|
||||
data-test-subj="create-default-btn"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
) : (
|
||||
<EuiFlexItem>
|
||||
<NewTemplateTimeline
|
||||
outline={true}
|
||||
title={NEW_TEMPLATE_TIMELINE}
|
||||
data-test-subj="create-template-btn"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
{tabName === TimelineType.default ? (
|
||||
<EuiFlexItem>
|
||||
<NewTimeline
|
||||
timelineId={TimelineId.active}
|
||||
outline={true}
|
||||
data-test-subj="create-default-btn"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
) : (
|
||||
<EuiFlexItem>
|
||||
<NewTemplateTimeline
|
||||
outline={true}
|
||||
title={NEW_TEMPLATE_TIMELINE}
|
||||
data-test-subj="create-template-btn"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexGroup>
|
||||
)}
|
||||
</HeaderPage>
|
||||
|
||||
<TimelinesContainer data-test-subj="timelines-container">
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue