mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[Security Solution][Lists] - Hide exception list delete icon if Kibana read only (#126710)
Addresses bug #126313 Even if user is given index privileges to lists, UI should follow Kibana privileges. Checks if user is a read only Kibana user and hides the delete icon from exception list view if true.
This commit is contained in:
parent
f8586a87d0
commit
7af9c37016
4 changed files with 67 additions and 15 deletions
|
@ -5,6 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { ROLES } from '../../../common/test';
|
||||
import { getExceptionList, expectedExportedExceptionList } from '../../objects/exception';
|
||||
import { getNewRule } from '../../objects/rule';
|
||||
|
||||
|
@ -25,6 +26,7 @@ import {
|
|||
clearSearchSelection,
|
||||
} from '../../tasks/exceptions_table';
|
||||
import {
|
||||
EXCEPTIONS_TABLE_DELETE_BTN,
|
||||
EXCEPTIONS_TABLE_LIST_NAME,
|
||||
EXCEPTIONS_TABLE_SHOWING_LISTS,
|
||||
} from '../../screens/exceptions';
|
||||
|
@ -168,3 +170,22 @@ describe('Exceptions Table', () => {
|
|||
cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '1');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Exceptions Table - read only', () => {
|
||||
before(() => {
|
||||
// First we login as a privileged user to create exception list
|
||||
cleanKibana();
|
||||
loginAndWaitForPageWithoutDateRange(EXCEPTIONS_URL, ROLES.platform_engineer);
|
||||
createExceptionList(getExceptionList(), getExceptionList().list_id);
|
||||
|
||||
// Then we login as read-only user to test.
|
||||
loginAndWaitForPageWithoutDateRange(EXCEPTIONS_URL, ROLES.reader);
|
||||
waitForExceptionsTableToBeLoaded();
|
||||
|
||||
cy.get(EXCEPTIONS_TABLE_SHOWING_LISTS).should('have.text', `Showing 1 list`);
|
||||
});
|
||||
|
||||
it('Delete icon is not shown', () => {
|
||||
cy.get(EXCEPTIONS_TABLE_DELETE_BTN).should('not.exist');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -27,7 +27,8 @@ export const getAllExceptionListsColumns = (
|
|||
onExport: (arg: { id: string; listId: string; namespaceType: NamespaceType }) => () => void,
|
||||
onDelete: (arg: { id: string; listId: string; namespaceType: NamespaceType }) => () => void,
|
||||
formatUrl: FormatUrl,
|
||||
navigateToUrl: (url: string) => Promise<void>
|
||||
navigateToUrl: (url: string) => Promise<void>,
|
||||
isKibanaReadOnly: boolean
|
||||
): AllExceptionListsColumns[] => [
|
||||
{
|
||||
align: 'left',
|
||||
|
@ -155,7 +156,7 @@ export const getAllExceptionListsColumns = (
|
|||
},
|
||||
{
|
||||
render: ({ id, list_id: listId, namespace_type: namespaceType }: ExceptionListInfo) => {
|
||||
return listId === 'endpoint_list' ? (
|
||||
return listId === 'endpoint_list' || isKibanaReadOnly ? (
|
||||
<></>
|
||||
) : (
|
||||
<EuiButtonIcon
|
||||
|
|
|
@ -11,12 +11,14 @@ import { mount } from 'enzyme';
|
|||
import { TestProviders } from '../../../../../../common/mock';
|
||||
import { mockHistory } from '../../../../../../common/utils/route/index.test';
|
||||
import { getExceptionListSchemaMock } from '../../../../../../../../lists/common/schemas/response/exception_list_schema.mock';
|
||||
import { useUserData } from '../../../../../components/user_info';
|
||||
|
||||
import { ExceptionListsTable } from './exceptions_table';
|
||||
import { useApi, useExceptionLists } from '@kbn/securitysolution-list-hooks';
|
||||
import { useAllExceptionLists } from './use_all_exception_lists';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
jest.mock('../../../../../components/user_info');
|
||||
jest.mock('../../../../../../common/lib/kibana');
|
||||
jest.mock('./use_all_exception_lists');
|
||||
jest.mock('@kbn/securitysolution-list-hooks');
|
||||
|
@ -41,15 +43,6 @@ jest.mock('../../../../../containers/detection_engine/lists/use_lists_config', (
|
|||
useListsConfig: jest.fn().mockReturnValue({ loading: false }),
|
||||
}));
|
||||
|
||||
jest.mock('../../../../../components/user_info', () => ({
|
||||
useUserData: jest.fn().mockReturnValue([
|
||||
{
|
||||
loading: false,
|
||||
canUserCRUD: false,
|
||||
},
|
||||
]),
|
||||
}));
|
||||
|
||||
describe('ExceptionListsTable', () => {
|
||||
const exceptionList1 = getExceptionListSchemaMock();
|
||||
const exceptionList2 = { ...getExceptionListSchemaMock(), list_id: 'not_endpoint_list', id: '2' };
|
||||
|
@ -86,9 +79,17 @@ describe('ExceptionListsTable', () => {
|
|||
endpoint_list: exceptionList1,
|
||||
},
|
||||
]);
|
||||
|
||||
(useUserData as jest.Mock).mockReturnValue([
|
||||
{
|
||||
loading: false,
|
||||
canUserCRUD: false,
|
||||
canUserREAD: false,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('does not render delete option disabled if list is "endpoint_list"', async () => {
|
||||
it('does not render delete option if list is "endpoint_list"', async () => {
|
||||
const wrapper = mount(
|
||||
<TestProviders>
|
||||
<ExceptionListsTable />
|
||||
|
@ -106,4 +107,25 @@ describe('ExceptionListsTable', () => {
|
|||
wrapper.find('[data-test-subj="exceptionsTableDeleteButton"] button').at(0).prop('disabled')
|
||||
).toBeFalsy();
|
||||
});
|
||||
|
||||
it('does not render delete option if user is read only', async () => {
|
||||
(useUserData as jest.Mock).mockReturnValue([
|
||||
{
|
||||
loading: false,
|
||||
canUserCRUD: false,
|
||||
canUserREAD: true,
|
||||
},
|
||||
]);
|
||||
|
||||
const wrapper = mount(
|
||||
<TestProviders>
|
||||
<ExceptionListsTable />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(wrapper.find('[data-test-subj="exceptionsTableListId"]').at(1).text()).toEqual(
|
||||
'not_endpoint_list'
|
||||
);
|
||||
expect(wrapper.find('[data-test-subj="exceptionsTableDeleteButton"] button')).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -60,7 +60,7 @@ const exceptionReferenceModalInitialState: ReferenceModalState = {
|
|||
|
||||
export const ExceptionListsTable = React.memo(() => {
|
||||
const { formatUrl } = useFormatUrl(SecurityPageName.rules);
|
||||
const [{ loading: userInfoLoading, canUserCRUD }] = useUserData();
|
||||
const [{ loading: userInfoLoading, canUserCRUD, canUserREAD }] = useUserData();
|
||||
const hasPermissions = userHasPermissions(canUserCRUD);
|
||||
|
||||
const { loading: listsConfigLoading } = useListsConfig();
|
||||
|
@ -193,8 +193,16 @@ export const ExceptionListsTable = React.memo(() => {
|
|||
);
|
||||
|
||||
const exceptionsColumns = useMemo((): AllExceptionListsColumns[] => {
|
||||
return getAllExceptionListsColumns(handleExport, handleDelete, formatUrl, navigateToUrl);
|
||||
}, [handleExport, handleDelete, formatUrl, navigateToUrl]);
|
||||
// Defaulting to true to default to the lower privilege first
|
||||
const isKibanaReadOnly = (canUserREAD && !canUserCRUD) ?? true;
|
||||
return getAllExceptionListsColumns(
|
||||
handleExport,
|
||||
handleDelete,
|
||||
formatUrl,
|
||||
navigateToUrl,
|
||||
isKibanaReadOnly
|
||||
);
|
||||
}, [handleExport, handleDelete, formatUrl, navigateToUrl, canUserREAD, canUserCRUD]);
|
||||
|
||||
const handleRefresh = useCallback((): void => {
|
||||
if (refreshExceptions != null) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue