diff --git a/x-pack/plugins/cases/common/constants/index.ts b/x-pack/plugins/cases/common/constants/index.ts index ac689ad5b29d4..01c8543493f7a 100644 --- a/x-pack/plugins/cases/common/constants/index.ts +++ b/x-pack/plugins/cases/common/constants/index.ts @@ -158,6 +158,7 @@ export const READ_CASES_CAPABILITY = 'read_cases' as const; export const UPDATE_CASES_CAPABILITY = 'update_cases' as const; export const DELETE_CASES_CAPABILITY = 'delete_cases' as const; export const PUSH_CASES_CAPABILITY = 'push_cases' as const; +export const CASES_CONNECTORS_CAPABILITY = 'cases_connectors' as const; /** * Cases API Tags @@ -173,6 +174,11 @@ export const SUGGEST_USER_PROFILES_API_TAG = 'casesSuggestUserProfiles'; */ export const BULK_GET_USER_PROFILES_API_TAG = 'bulkGetUserProfiles'; +/** + * This tag is registered for the connectors (configure) get API + */ +export const GET_CONNECTORS_CONFIGURE_API_TAG = 'casesGetConnectorsConfigure'; + /** * User profiles */ diff --git a/x-pack/plugins/cases/common/ui/types.ts b/x-pack/plugins/cases/common/ui/types.ts index d8790a82e5ded..ebcaceaceff4f 100644 --- a/x-pack/plugins/cases/common/ui/types.ts +++ b/x-pack/plugins/cases/common/ui/types.ts @@ -12,7 +12,8 @@ import type { READ_CASES_CAPABILITY, UPDATE_CASES_CAPABILITY, } from '..'; -import type { PUSH_CASES_CAPABILITY } from '../constants'; +import type { CaseMetricsFeature, CasesMetricsResponse, SingleCaseMetricsResponse } from '../api'; +import type { CASES_CONNECTORS_CAPABILITY, PUSH_CASES_CAPABILITY } from '../constants'; import type { SnakeToCamelCase } from '../types'; import type { CaseSeverity, @@ -285,6 +286,7 @@ export interface CasesPermissions { update: boolean; delete: boolean; push: boolean; + connectors: boolean; } export interface CasesCapabilities { @@ -293,4 +295,5 @@ export interface CasesCapabilities { [UPDATE_CASES_CAPABILITY]: boolean; [DELETE_CASES_CAPABILITY]: boolean; [PUSH_CASES_CAPABILITY]: boolean; + [CASES_CONNECTORS_CAPABILITY]: boolean; } diff --git a/x-pack/plugins/cases/common/utils/__snapshots__/api_tags.test.ts.snap b/x-pack/plugins/cases/common/utils/__snapshots__/api_tags.test.ts.snap index ea1ef29e71c59..9cca596cc84d8 100644 --- a/x-pack/plugins/cases/common/utils/__snapshots__/api_tags.test.ts.snap +++ b/x-pack/plugins/cases/common/utils/__snapshots__/api_tags.test.ts.snap @@ -5,6 +5,7 @@ Object { "all": Array [ "casesSuggestUserProfiles", "bulkGetUserProfiles", + "casesGetConnectorsConfigure", "casesFilesCasesCreate", "casesFilesCasesRead", ], @@ -14,6 +15,7 @@ Object { "read": Array [ "casesSuggestUserProfiles", "bulkGetUserProfiles", + "casesGetConnectorsConfigure", "casesFilesCasesRead", ], } @@ -24,6 +26,7 @@ Object { "all": Array [ "casesSuggestUserProfiles", "bulkGetUserProfiles", + "casesGetConnectorsConfigure", "observabilityFilesCasesCreate", "observabilityFilesCasesRead", ], @@ -33,6 +36,7 @@ Object { "read": Array [ "casesSuggestUserProfiles", "bulkGetUserProfiles", + "casesGetConnectorsConfigure", "observabilityFilesCasesRead", ], } @@ -43,6 +47,7 @@ Object { "all": Array [ "casesSuggestUserProfiles", "bulkGetUserProfiles", + "casesGetConnectorsConfigure", "securitySolutionFilesCasesCreate", "securitySolutionFilesCasesRead", ], @@ -52,6 +57,7 @@ Object { "read": Array [ "casesSuggestUserProfiles", "bulkGetUserProfiles", + "casesGetConnectorsConfigure", "securitySolutionFilesCasesRead", ], } diff --git a/x-pack/plugins/cases/common/utils/api_tags.ts b/x-pack/plugins/cases/common/utils/api_tags.ts index d9e3ad25a04c0..2568c0e79b9a0 100644 --- a/x-pack/plugins/cases/common/utils/api_tags.ts +++ b/x-pack/plugins/cases/common/utils/api_tags.ts @@ -5,7 +5,11 @@ * 2.0. */ -import { BULK_GET_USER_PROFILES_API_TAG, SUGGEST_USER_PROFILES_API_TAG } from '../constants'; +import { + BULK_GET_USER_PROFILES_API_TAG, + GET_CONNECTORS_CONFIGURE_API_TAG, + SUGGEST_USER_PROFILES_API_TAG, +} from '../constants'; import { HttpApiTagOperation } from '../constants/types'; import type { Owner } from '../constants/types'; import { constructFilesHttpOperationTag } from '../files'; @@ -16,8 +20,19 @@ export const getApiTags = (owner: Owner) => { const read = constructFilesHttpOperationTag(owner, HttpApiTagOperation.Read); return { - all: [SUGGEST_USER_PROFILES_API_TAG, BULK_GET_USER_PROFILES_API_TAG, create, read] as const, - read: [SUGGEST_USER_PROFILES_API_TAG, BULK_GET_USER_PROFILES_API_TAG, read] as const, + all: [ + SUGGEST_USER_PROFILES_API_TAG, + BULK_GET_USER_PROFILES_API_TAG, + GET_CONNECTORS_CONFIGURE_API_TAG, + create, + read, + ] as const, + read: [ + SUGGEST_USER_PROFILES_API_TAG, + BULK_GET_USER_PROFILES_API_TAG, + GET_CONNECTORS_CONFIGURE_API_TAG, + read, + ] as const, delete: [deleteTag] as const, }; }; diff --git a/x-pack/plugins/cases/common/utils/capabilities.ts b/x-pack/plugins/cases/common/utils/capabilities.ts index a508d11201966..e9c05eda47171 100644 --- a/x-pack/plugins/cases/common/utils/capabilities.ts +++ b/x-pack/plugins/cases/common/utils/capabilities.ts @@ -6,6 +6,7 @@ */ import { + CASES_CONNECTORS_CAPABILITY, CREATE_CASES_CAPABILITY, DELETE_CASES_CAPABILITY, PUSH_CASES_CAPABILITY, @@ -23,7 +24,8 @@ export const createUICapabilities = () => ({ READ_CASES_CAPABILITY, UPDATE_CASES_CAPABILITY, PUSH_CASES_CAPABILITY, + CASES_CONNECTORS_CAPABILITY, ] as const, - read: [READ_CASES_CAPABILITY] as const, + read: [READ_CASES_CAPABILITY, CASES_CONNECTORS_CAPABILITY] as const, delete: [DELETE_CASES_CAPABILITY] as const, }); diff --git a/x-pack/plugins/cases/public/client/helpers/can_use_cases.test.ts b/x-pack/plugins/cases/public/client/helpers/can_use_cases.test.ts index 9898f9c374c78..5b82919523f36 100644 --- a/x-pack/plugins/cases/public/client/helpers/can_use_cases.test.ts +++ b/x-pack/plugins/cases/public/client/helpers/can_use_cases.test.ts @@ -11,8 +11,8 @@ import { allCasesPermissions, noCasesCapabilities, noCasesPermissions, - readCasesCapabilities, readCasesPermissions, + readCasesCapabilities, writeCasesCapabilities, writeCasesPermissions, } from '../../common/mock'; @@ -77,6 +77,12 @@ const hasSecurityWriteAndObservabilityRead: CasesCapabilities = { generalCases: noCasesCapabilities(), }; +const hasSecurityConnectors: CasesCapabilities = { + securitySolutionCases: readCasesCapabilities(), + observabilityCases: noCasesCapabilities(), + generalCases: noCasesCapabilities(), +}; + describe('canUseCases', () => { it.each([hasAll, hasSecurity, hasObservability, hasSecurityWriteAndObservabilityRead])( 'returns true for all permissions, if a user has access to both on any solution', @@ -109,4 +115,12 @@ describe('canUseCases', () => { expect(permissions).toStrictEqual(noCasesPermissions()); } ); + + it.each([hasSecurityConnectors])( + 'returns true for only connectors, if a user has access to only connectors on any solution', + (capability) => { + const permissions = canUseCases(capability)(); + expect(permissions).toStrictEqual(readCasesPermissions()); + } + ); }); diff --git a/x-pack/plugins/cases/public/client/helpers/can_use_cases.ts b/x-pack/plugins/cases/public/client/helpers/can_use_cases.ts index 34af1c3865da5..1cc22c0799702 100644 --- a/x-pack/plugins/cases/public/client/helpers/can_use_cases.ts +++ b/x-pack/plugins/cases/public/client/helpers/can_use_cases.ts @@ -40,8 +40,10 @@ export const canUseCases = acc.update = acc.update || userCapabilitiesForOwner.update; acc.delete = acc.delete || userCapabilitiesForOwner.delete; acc.push = acc.push || userCapabilitiesForOwner.push; - const allFromAcc = acc.create && acc.read && acc.update && acc.delete && acc.push; + const allFromAcc = + acc.create && acc.read && acc.update && acc.delete && acc.push && acc.connectors; acc.all = acc.all || userCapabilitiesForOwner.all || allFromAcc; + acc.connectors = acc.connectors || userCapabilitiesForOwner.connectors; return acc; }, @@ -52,6 +54,7 @@ export const canUseCases = update: false, delete: false, push: false, + connectors: false, } ); diff --git a/x-pack/plugins/cases/public/client/helpers/capabilities.test.ts b/x-pack/plugins/cases/public/client/helpers/capabilities.test.ts index 58d6d61e80324..a3f741f373032 100644 --- a/x-pack/plugins/cases/public/client/helpers/capabilities.test.ts +++ b/x-pack/plugins/cases/public/client/helpers/capabilities.test.ts @@ -12,6 +12,7 @@ describe('getUICapabilities', () => { expect(getUICapabilities(undefined)).toMatchInlineSnapshot(` Object { "all": false, + "connectors": false, "create": false, "delete": false, "push": false, @@ -25,6 +26,7 @@ describe('getUICapabilities', () => { expect(getUICapabilities()).toMatchInlineSnapshot(` Object { "all": false, + "connectors": false, "create": false, "delete": false, "push": false, @@ -38,6 +40,7 @@ describe('getUICapabilities', () => { expect(getUICapabilities({ create_cases: true })).toMatchInlineSnapshot(` Object { "all": false, + "connectors": false, "create": true, "delete": false, "push": false, @@ -55,10 +58,12 @@ describe('getUICapabilities', () => { update_cases: false, delete_cases: false, push_cases: false, + cases_connectors: false, }) ).toMatchInlineSnapshot(` Object { "all": false, + "connectors": false, "create": false, "delete": false, "push": false, @@ -72,6 +77,7 @@ describe('getUICapabilities', () => { expect(getUICapabilities({})).toMatchInlineSnapshot(` Object { "all": false, + "connectors": false, "create": false, "delete": false, "push": false, @@ -89,10 +95,35 @@ describe('getUICapabilities', () => { update_cases: true, delete_cases: true, push_cases: true, + cases_connectors: true, }) ).toMatchInlineSnapshot(` Object { "all": false, + "connectors": true, + "create": false, + "delete": true, + "push": true, + "read": true, + "update": true, + } + `); + }); + + it('returns false for the all field when cases_connectors is false', () => { + expect( + getUICapabilities({ + create_cases: false, + read_cases: true, + update_cases: true, + delete_cases: true, + push_cases: true, + cases_connectors: false, + }) + ).toMatchInlineSnapshot(` + Object { + "all": false, + "connectors": false, "create": false, "delete": true, "push": true, diff --git a/x-pack/plugins/cases/public/client/helpers/capabilities.ts b/x-pack/plugins/cases/public/client/helpers/capabilities.ts index f09ac84448952..278512fef623c 100644 --- a/x-pack/plugins/cases/public/client/helpers/capabilities.ts +++ b/x-pack/plugins/cases/public/client/helpers/capabilities.ts @@ -7,6 +7,7 @@ import type { CasesPermissions } from '../../../common'; import { + CASES_CONNECTORS_CAPABILITY, CREATE_CASES_CAPABILITY, DELETE_CASES_CAPABILITY, PUSH_CASES_CAPABILITY, @@ -22,7 +23,8 @@ export const getUICapabilities = ( const update = !!featureCapabilities?.[UPDATE_CASES_CAPABILITY]; const deletePriv = !!featureCapabilities?.[DELETE_CASES_CAPABILITY]; const push = !!featureCapabilities?.[PUSH_CASES_CAPABILITY]; - const all = create && read && update && deletePriv && push; + const connectors = !!featureCapabilities?.[CASES_CONNECTORS_CAPABILITY]; + const all = create && read && update && deletePriv && push && connectors; return { all, @@ -31,5 +33,6 @@ export const getUICapabilities = ( update, delete: deletePriv, push, + connectors, }; }; diff --git a/x-pack/plugins/cases/public/common/lib/kibana/hooks.ts b/x-pack/plugins/cases/public/common/lib/kibana/hooks.ts index 812840b1553e3..c540824b1ebb5 100644 --- a/x-pack/plugins/cases/public/common/lib/kibana/hooks.ts +++ b/x-pack/plugins/cases/public/common/lib/kibana/hooks.ts @@ -193,6 +193,7 @@ export const useApplicationCapabilities = (): UseApplicationCapabilities => { update: permissions.update, delete: permissions.delete, push: permissions.push, + connectors: permissions.connectors, }, visualize: { crud: !!capabilities.visualize?.save, read: !!capabilities.visualize?.show }, dashboard: { @@ -213,6 +214,7 @@ export const useApplicationCapabilities = (): UseApplicationCapabilities => { permissions.update, permissions.delete, permissions.push, + permissions.connectors, ] ); }; diff --git a/x-pack/plugins/cases/public/common/lib/kibana/kibana_react.mock.tsx b/x-pack/plugins/cases/public/common/lib/kibana/kibana_react.mock.tsx index 819c7099b5cb8..31ea452874c28 100644 --- a/x-pack/plugins/cases/public/common/lib/kibana/kibana_react.mock.tsx +++ b/x-pack/plugins/cases/public/common/lib/kibana/kibana_react.mock.tsx @@ -74,6 +74,7 @@ export const createStartServicesMock = ({ license }: StartServiceArgs = {}): Sta update_cases: true, delete_cases: true, push_cases: true, + cases_connectors: true, }, visualize: { save: true, show: true }, dashboard: { show: true, createNew: true }, diff --git a/x-pack/plugins/cases/public/common/mock/permissions.ts b/x-pack/plugins/cases/public/common/mock/permissions.ts index 01d1dc64952ed..4d68e9d36c776 100644 --- a/x-pack/plugins/cases/public/common/mock/permissions.ts +++ b/x-pack/plugins/cases/public/common/mock/permissions.ts @@ -9,9 +9,23 @@ import type { CasesCapabilities, CasesPermissions } from '../../containers/types export const allCasesPermissions = () => buildCasesPermissions(); export const noCasesPermissions = () => - buildCasesPermissions({ read: false, create: false, update: false, delete: false, push: false }); + buildCasesPermissions({ + read: false, + create: false, + update: false, + delete: false, + push: false, + connectors: false, + }); export const readCasesPermissions = () => - buildCasesPermissions({ read: true, create: false, update: false, delete: false, push: false }); + buildCasesPermissions({ + read: true, + create: false, + update: false, + delete: false, + push: false, + connectors: true, + }); export const noCreateCasesPermissions = () => buildCasesPermissions({ create: false }); export const noUpdateCasesPermissions = () => buildCasesPermissions({ update: false }); export const noPushCasesPermissions = () => buildCasesPermissions({ push: false }); @@ -19,6 +33,7 @@ export const noDeleteCasesPermissions = () => buildCasesPermissions({ delete: fa export const writeCasesPermissions = () => buildCasesPermissions({ read: false }); export const onlyDeleteCasesPermission = () => buildCasesPermissions({ read: false, create: false, update: false, delete: true, push: false }); +export const noConnectorsCasePermission = () => buildCasesPermissions({ connectors: false }); export const buildCasesPermissions = (overrides: Partial> = {}) => { const create = overrides.create ?? true; @@ -26,6 +41,7 @@ export const buildCasesPermissions = (overrides: Partial update_cases: false, delete_cases: false, push_cases: false, + cases_connectors: false, }); export const readCasesCapabilities = () => buildCasesCapabilities({ @@ -67,5 +85,6 @@ export const buildCasesCapabilities = (overrides?: Partial) = update_cases: overrides?.update_cases ?? true, delete_cases: overrides?.delete_cases ?? true, push_cases: overrides?.push_cases ?? true, + cases_connectors: overrides?.cases_connectors ?? true, }; }; diff --git a/x-pack/plugins/cases/public/components/configure_cases/connectors.test.tsx b/x-pack/plugins/cases/public/components/configure_cases/connectors.test.tsx index c77340b4f37ac..9db8d82c6b315 100644 --- a/x-pack/plugins/cases/public/components/configure_cases/connectors.test.tsx +++ b/x-pack/plugins/cases/public/components/configure_cases/connectors.test.tsx @@ -12,8 +12,12 @@ import { render, screen } from '@testing-library/react'; import type { Props } from './connectors'; import { Connectors } from './connectors'; -import type { AppMockRenderer } from '../../common/mock'; -import { createAppMockRenderer, TestProviders } from '../../common/mock'; +import { + type AppMockRenderer, + noConnectorsCasePermission, + createAppMockRenderer, + TestProviders, +} from '../../common/mock'; import { ConnectorsDropdown } from './connectors_dropdown'; import { connectors, actionTypes } from './__mock__'; import { ConnectorTypes } from '../../../common/types/domain'; @@ -161,4 +165,14 @@ describe('Connectors', () => { ).toBeInTheDocument(); expect(result.queryByTestId('case-connectors-dropdown')).toBe(null); }); + + it('shows the actions permission message if the user does not have access to case connector', async () => { + appMockRender = createAppMockRenderer({ permissions: noConnectorsCasePermission() }); + + const result = appMockRender.render(); + expect( + result.getByTestId('configure-case-connector-permissions-error-msg') + ).toBeInTheDocument(); + expect(result.queryByTestId('case-connectors-dropdown')).toBe(null); + }); }); diff --git a/x-pack/plugins/cases/public/components/configure_cases/connectors.tsx b/x-pack/plugins/cases/public/components/configure_cases/connectors.tsx index 0b51323f3ffd8..87e3100f087a7 100644 --- a/x-pack/plugins/cases/public/components/configure_cases/connectors.tsx +++ b/x-pack/plugins/cases/public/components/configure_cases/connectors.tsx @@ -27,6 +27,7 @@ import { ConnectorTypes } from '../../../common/types/domain'; import { DeprecatedCallout } from '../connectors/deprecated_callout'; import { isDeprecatedConnector } from '../utils'; import { useApplicationCapabilities } from '../../common/lib/kibana'; +import { useCasesContext } from '../cases_context/use_cases_context'; const EuiFormRowExtended = styled(EuiFormRow)` .euiFormRow__labelWrapper { @@ -63,6 +64,8 @@ const ConnectorsComponent: React.FC = ({ () => connectors.find((c) => c.id === selectedConnector.id), [connectors, selectedConnector.id] ); + const { permissions } = useCasesContext(); + const canUseConnectors = permissions.connectors && actions.read; const connectorsName = connector?.name ?? 'none'; @@ -105,7 +108,7 @@ const ConnectorsComponent: React.FC = ({ > - {actions.read ? ( + {canUseConnectors ? ( { expect(result.getByTestId('create-case-connector-permissions-error-msg')).toBeInTheDocument(); expect(result.queryByTestId('caseConnectors')).toBe(null); }); + + it('shows the actions permission message if the user does not have access to case connector', async () => { + appMockRender = createAppMockRenderer({ permissions: noConnectorsCasePermission() }); + + const result = appMockRender.render( + + + + ); + expect(result.getByTestId('create-case-connector-permissions-error-msg')).toBeInTheDocument(); + expect(result.queryByTestId('caseConnectors')).toBe(null); + }); }); diff --git a/x-pack/plugins/cases/public/components/create/connector.tsx b/x-pack/plugins/cases/public/components/create/connector.tsx index 58bf659b68cf5..7422e671fa4bb 100644 --- a/x-pack/plugins/cases/public/components/create/connector.tsx +++ b/x-pack/plugins/cases/public/components/create/connector.tsx @@ -18,6 +18,7 @@ import { useCaseConfigure } from '../../containers/configure/use_configure'; import { getConnectorById, getConnectorsFormValidators } from '../utils'; import { useApplicationCapabilities } from '../../common/lib/kibana'; import * as i18n from '../../common/translations'; +import { useCasesContext } from '../cases_context/use_cases_context'; interface Props { connectors: ActionConnector[]; @@ -30,6 +31,8 @@ const ConnectorComponent: React.FC = ({ connectors, isLoading, isLoadingC const connector = getConnectorById(connectorId, connectors) ?? null; const { connector: configurationConnector } = useCaseConfigure(); const { actions } = useApplicationCapabilities(); + const { permissions } = useCasesContext(); + const hasReadPermissions = permissions.connectors && actions.read; const defaultConnectorId = useMemo(() => { return connectors.some((c) => c.id === configurationConnector.id) @@ -42,7 +45,7 @@ const ConnectorComponent: React.FC = ({ connectors, isLoading, isLoadingC connectors, }); - if (!actions.read) { + if (!hasReadPermissions) { return ( {i18n.READ_ACTIONS_PERMISSIONS_ERROR_MSG} diff --git a/x-pack/plugins/cases/public/components/edit_connector/index.test.tsx b/x-pack/plugins/cases/public/components/edit_connector/index.test.tsx index 21ff3af65a336..b68641526c46a 100644 --- a/x-pack/plugins/cases/public/components/edit_connector/index.test.tsx +++ b/x-pack/plugins/cases/public/components/edit_connector/index.test.tsx @@ -11,12 +11,14 @@ import userEvent from '@testing-library/user-event'; import type { EditConnectorProps } from '.'; import { EditConnector } from '.'; -import type { AppMockRenderer } from '../../common/mock'; + import { + type AppMockRenderer, createAppMockRenderer, readCasesPermissions, noPushCasesPermissions, TestProviders, + noConnectorsCasePermission, } from '../../common/mock'; import { basicCase, connectorsMock } from '../../containers/mock'; import { getCaseConnectorsMockResponse } from '../../common/mock/connectors'; @@ -274,6 +276,17 @@ describe('EditConnector ', () => { }); }); + it('does not show the callout if the user does not have access to cases connectors', async () => { + const props = { ...defaultProps, connectors: [] }; + appMockRender = createAppMockRenderer({ permissions: noConnectorsCasePermission() }); + + const result = appMockRender.render(); + await waitFor(() => { + expect(result.getByTestId('edit-connector-permissions-error-msg')).toBeInTheDocument(); + expect(result.queryByTestId('push-callouts')).toBe(null); + }); + }); + it('does not show the connectors previewer if the user does not have read access to actions', async () => { const props = { ...defaultProps, connectors: [] }; appMockRender.coreStart.application.capabilities = { @@ -285,6 +298,14 @@ describe('EditConnector ', () => { expect(result.queryByTestId('connector-fields-preview')).not.toBeInTheDocument(); }); + it('does not show the connectors previewer if the user does not have access to cases connectors', async () => { + const props = { ...defaultProps, connectors: [] }; + appMockRender = createAppMockRenderer({ permissions: noConnectorsCasePermission() }); + + const result = appMockRender.render(); + expect(result.queryByTestId('connector-fields-preview')).not.toBeInTheDocument(); + }); + it('does not show the connectors form if the user does not have read access to actions', async () => { const props = { ...defaultProps, connectors: [] }; appMockRender.coreStart.application.capabilities = { @@ -296,6 +317,14 @@ describe('EditConnector ', () => { expect(result.queryByTestId('edit-connector-fields-form-flex-item')).not.toBeInTheDocument(); }); + it('does not show the connectors form if the user does not have access to cases connectors', async () => { + const props = { ...defaultProps, connectors: [] }; + appMockRender = createAppMockRenderer({ permissions: noConnectorsCasePermission() }); + + const result = appMockRender.render(); + expect(result.queryByTestId('edit-connector-fields-form-flex-item')).not.toBeInTheDocument(); + }); + it('does not show the push button if the user does not have read access to actions', async () => { appMockRender.coreStart.application.capabilities = { ...appMockRender.coreStart.application.capabilities, @@ -317,6 +346,15 @@ describe('EditConnector ', () => { }); }); + it('does not show the push button if the user does not have access to cases actions', async () => { + appMockRender = createAppMockRenderer({ permissions: noConnectorsCasePermission() }); + + const result = appMockRender.render(); + await waitFor(() => { + expect(result.queryByTestId('push-to-external-service')).toBe(null); + }); + }); + it('does not show the edit connectors pencil if the user does not have read access to actions', async () => { const props = { ...defaultProps, connectors: [] }; appMockRender.coreStart.application.capabilities = { @@ -332,6 +370,20 @@ describe('EditConnector ', () => { }); }); + it('does not show the edit connectors pencil if the user does not have access to case connectors', async () => { + const props = { ...defaultProps, connectors: [] }; + appMockRender = createAppMockRenderer({ + permissions: noConnectorsCasePermission(), + }); + + appMockRender.render(); + + await waitFor(() => { + expect(screen.getByTestId('connector-edit-header')).toBeInTheDocument(); + expect(screen.queryByTestId('connector-edit-button')).not.toBeInTheDocument(); + }); + }); + it('does not show the edit connectors pencil if the user does not have push permissions', async () => { const props = { ...defaultProps, connectors: [] }; appMockRender = createAppMockRenderer({ permissions: noPushCasesPermissions() }); diff --git a/x-pack/plugins/cases/public/components/edit_connector/index.tsx b/x-pack/plugins/cases/public/components/edit_connector/index.tsx index ee819af75b400..27d5e75b550fb 100644 --- a/x-pack/plugins/cases/public/components/edit_connector/index.tsx +++ b/x-pack/plugins/cases/public/components/edit_connector/index.tsx @@ -21,6 +21,7 @@ import { PushButton } from './push_button'; import { PushCallouts } from './push_callouts'; import { ConnectorsForm } from './connectors_form'; import { ConnectorFieldsPreviewForm } from '../connectors/fields_preview_form'; +import { useCasesContext } from '../cases_context/use_cases_context'; export interface EditConnectorProps { caseData: CaseUI; @@ -45,7 +46,8 @@ export const EditConnector = React.memo( const [isEdit, setIsEdit] = useState(false); const { actions } = useApplicationCapabilities(); - const hasActionsReadPermissions = actions.read; + const { permissions } = useCasesContext(); + const canUseConnectors = permissions.connectors && actions.read; const onEditClick = useCallback(() => setIsEdit(true), []); const onCancelConnector = useCallback(() => setIsEdit(false), []); @@ -102,7 +104,7 @@ export const EditConnector = React.memo(

{i18n.CONNECTORS}

- {!isLoading && !isEdit && hasPushPermissions && hasActionsReadPermissions ? ( + {!isLoading && !isEdit && hasPushPermissions && canUseConnectors ? ( - {!isLoading && !isEdit && hasErrorMessages && hasActionsReadPermissions && ( + {!isLoading && !isEdit && hasErrorMessages && canUseConnectors && ( )} - {!hasActionsReadPermissions && ( + {!canUseConnectors && ( {i18n.READ_ACTIONS_PERMISSIONS_ERROR_MSG} )} - {hasActionsReadPermissions && !isEdit && ( + {canUseConnectors && !isEdit && ( )} - {hasActionsReadPermissions && isEdit && ( + {canUseConnectors && isEdit && ( )} - {!hasErrorMessages && - !isLoading && - !isEdit && - hasPushPermissions && - hasActionsReadPermissions && ( - - - 0 || !needsToBePushed || !hasPushPermissions} - connectorName={connectorWithName.name} - /> - - - )} + {!hasErrorMessages && !isLoading && !isEdit && hasPushPermissions && canUseConnectors && ( + + + 0 || !needsToBePushed || !hasPushPermissions} + connectorName={connectorWithName.name} + /> + + + )}
diff --git a/x-pack/plugins/cases/public/containers/configure/use_get_supported_action_connectors.tsx b/x-pack/plugins/cases/public/containers/configure/use_get_supported_action_connectors.tsx index 0bff8bce61c3e..e98d63debce4b 100644 --- a/x-pack/plugins/cases/public/containers/configure/use_get_supported_action_connectors.tsx +++ b/x-pack/plugins/cases/public/containers/configure/use_get_supported_action_connectors.tsx @@ -11,14 +11,17 @@ import { useApplicationCapabilities, useToasts } from '../../common/lib/kibana'; import * as i18n from './translations'; import { casesQueriesKeys } from '../constants'; import type { ServerError } from '../../types'; +import { useCasesContext } from '../../components/cases_context/use_cases_context'; export function useGetSupportedActionConnectors() { const toasts = useToasts(); const { actions } = useApplicationCapabilities(); + const { permissions } = useCasesContext(); + return useQuery( casesQueriesKeys.connectorsList(), async ({ signal }) => { - if (!actions.read) { + if (!actions.read || !permissions.connectors) { return []; } return getSupportedActionConnectors({ signal }); diff --git a/x-pack/plugins/cases/public/containers/configure/use_get_supported_action_connectors.tsx.test.tsx b/x-pack/plugins/cases/public/containers/configure/use_get_supported_action_connectors.tsx.test.tsx index 36cbd9417e375..c6a05daff7f4e 100644 --- a/x-pack/plugins/cases/public/containers/configure/use_get_supported_action_connectors.tsx.test.tsx +++ b/x-pack/plugins/cases/public/containers/configure/use_get_supported_action_connectors.tsx.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { renderHook } from '@testing-library/react-hooks'; import * as api from './api'; -import { TestProviders } from '../../common/mock'; +import { noConnectorsCasePermission, TestProviders } from '../../common/mock'; import { useApplicationCapabilities, useToasts } from '../../common/lib/kibana'; import { useGetSupportedActionConnectors } from './use_get_supported_action_connectors'; @@ -65,4 +65,20 @@ describe('useConnectors', () => { expect(spyOnFetchConnectors).not.toHaveBeenCalled(); expect(result.current.data).toEqual([]); }); + + it('does not fetch connectors when the user does not has access to connectors', async () => { + const spyOnFetchConnectors = jest.spyOn(api, 'getSupportedActionConnectors'); + useApplicationCapabilitiesMock().actions = { crud: true, read: true }; + + const { result, waitForNextUpdate } = renderHook(() => useGetSupportedActionConnectors(), { + wrapper: ({ children }) => ( + {children} + ), + }); + + await waitForNextUpdate(); + + expect(spyOnFetchConnectors).not.toHaveBeenCalled(); + expect(result.current.data).toEqual([]); + }); }); diff --git a/x-pack/plugins/cases/server/authorization/__snapshots__/audit_logger.test.ts.snap b/x-pack/plugins/cases/server/authorization/__snapshots__/audit_logger.test.ts.snap index 18eef843e88c2..ebb9501ff8960 100644 --- a/x-pack/plugins/cases/server/authorization/__snapshots__/audit_logger.test.ts.snap +++ b/x-pack/plugins/cases/server/authorization/__snapshots__/audit_logger.test.ts.snap @@ -2458,7 +2458,7 @@ Object { "type": "cases", }, }, - "message": "Failed attempt to update cases [id=1] as owner \\"awesome\\"", + "message": "Failed attempt to push cases [id=1] as owner \\"awesome\\"", } `; @@ -2478,7 +2478,7 @@ Object { "change", ], }, - "message": "Failed attempt to update a case as any owners", + "message": "Failed attempt to push a case as any owners", } `; @@ -2500,7 +2500,7 @@ Object { "type": "cases", }, }, - "message": "User is updating cases [id=5] as owner \\"super\\"", + "message": "User is pushing cases [id=5] as owner \\"super\\"", } `; @@ -2516,7 +2516,7 @@ Object { "change", ], }, - "message": "User is updating a case as any owners", + "message": "User is pushing a case as any owners", } `; diff --git a/x-pack/plugins/cases/server/authorization/index.ts b/x-pack/plugins/cases/server/authorization/index.ts index 4bd881364cfc2..12653aa6079e6 100644 --- a/x-pack/plugins/cases/server/authorization/index.ts +++ b/x-pack/plugins/cases/server/authorization/index.ts @@ -39,6 +39,12 @@ const updateVerbs: Verbs = { past: 'updated', }; +const pushVerbs: Verbs = { + present: 'push', + progressive: 'pushing', + past: 'pushed', +}; + const deleteVerbs: Verbs = { present: 'delete', progressive: 'deleting', @@ -164,7 +170,7 @@ const CaseOperations = { ecsType: EVENT_TYPES.change, name: WriteOperations.PushCase as const, action: 'case_push', - verbs: updateVerbs, + verbs: pushVerbs, docType: 'case', savedObjectType: CASE_SAVED_OBJECT, }, diff --git a/x-pack/plugins/cases/server/routes/api/configure/get_connectors.ts b/x-pack/plugins/cases/server/routes/api/configure/get_connectors.ts index 4c28b896bd855..5929e39a2dd32 100644 --- a/x-pack/plugins/cases/server/routes/api/configure/get_connectors.ts +++ b/x-pack/plugins/cases/server/routes/api/configure/get_connectors.ts @@ -15,6 +15,9 @@ import { createCasesRoute } from '../create_cases_route'; export const getConnectorsRoute = createCasesRoute({ method: 'get', path: `${CASE_CONFIGURE_CONNECTORS_URL}/_find`, + routerOptions: { + tags: ['access:casesGetConnectorsConfigure'], + }, handler: async ({ context, response }) => { try { const caseContext = await context.cases; diff --git a/x-pack/plugins/cases/server/routes/api/types.ts b/x-pack/plugins/cases/server/routes/api/types.ts index 3dafad71b3cd8..a24e170ccaa16 100644 --- a/x-pack/plugins/cases/server/routes/api/types.ts +++ b/x-pack/plugins/cases/server/routes/api/types.ts @@ -40,7 +40,7 @@ interface CaseRouteHandlerArguments { kibanaVersion: PluginInitializerContext['env']['packageInfo']['version']; } -type CaseRouteTags = 'access:casesSuggestUserProfiles'; +type CaseRouteTags = 'access:casesSuggestUserProfiles' | 'access:casesGetConnectorsConfigure'; export interface CaseRoute

{ method: 'get' | 'post' | 'put' | 'delete' | 'patch'; diff --git a/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/header/add_to_case_action.test.tsx b/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/header/add_to_case_action.test.tsx index 71f7cfec8e5d2..a4d8a88507e82 100644 --- a/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/header/add_to_case_action.test.tsx +++ b/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/header/add_to_case_action.test.tsx @@ -110,6 +110,7 @@ describe('AddToCaseAction', function () { update: false, delete: false, push: false, + connectors: false, }, }) ); diff --git a/x-pack/plugins/observability/public/hooks/use_get_user_cases_permissions.tsx b/x-pack/plugins/observability/public/hooks/use_get_user_cases_permissions.tsx index d2c47da425a01..ea80fc8f8cc1c 100644 --- a/x-pack/plugins/observability/public/hooks/use_get_user_cases_permissions.tsx +++ b/x-pack/plugins/observability/public/hooks/use_get_user_cases_permissions.tsx @@ -18,6 +18,7 @@ export function useGetUserCasesPermissions() { update: false, delete: false, push: false, + connectors: false, }); const uiCapabilities = useKibana().services.application.capabilities; @@ -33,6 +34,7 @@ export function useGetUserCasesPermissions() { update: casesCapabilities.update, delete: casesCapabilities.delete, push: casesCapabilities.push, + connectors: casesCapabilities.connectors, }); }, [ casesCapabilities.all, @@ -41,6 +43,7 @@ export function useGetUserCasesPermissions() { casesCapabilities.update, casesCapabilities.delete, casesCapabilities.push, + casesCapabilities.connectors, ]); return casesPermissions; diff --git a/x-pack/plugins/observability/public/pages/cases/components/cases.stories.tsx b/x-pack/plugins/observability/public/pages/cases/components/cases.stories.tsx index ab490670f63ee..d0fc1d01734f2 100644 --- a/x-pack/plugins/observability/public/pages/cases/components/cases.stories.tsx +++ b/x-pack/plugins/observability/public/pages/cases/components/cases.stories.tsx @@ -19,7 +19,15 @@ export default { const Template: ComponentStory = (props: CasesProps) => ; const defaultProps: CasesProps = { - permissions: { read: true, all: true, create: true, delete: true, push: true, update: true }, + permissions: { + read: true, + all: true, + create: true, + delete: true, + push: true, + update: true, + connectors: true, + }, }; export const CasesPageWithAllPermissions = Template.bind({}); @@ -34,5 +42,6 @@ CasesPageWithNoPermissions.args = { delete: false, push: false, update: false, + connectors: false, }, }; diff --git a/x-pack/plugins/observability_shared/public/hooks/use_get_user_cases_permissions.tsx b/x-pack/plugins/observability_shared/public/hooks/use_get_user_cases_permissions.tsx index 4151655c5a2bd..21c6a08815b76 100644 --- a/x-pack/plugins/observability_shared/public/hooks/use_get_user_cases_permissions.tsx +++ b/x-pack/plugins/observability_shared/public/hooks/use_get_user_cases_permissions.tsx @@ -19,6 +19,7 @@ export function useGetUserCasesPermissions() { update: false, delete: false, push: false, + connectors: false, }); const uiCapabilities = useKibana().services.application!.capabilities; @@ -35,6 +36,7 @@ export function useGetUserCasesPermissions() { update: casesCapabilities.update, delete: casesCapabilities.delete, push: casesCapabilities.push, + connectors: casesCapabilities.connectors, }); }, [ casesCapabilities.all, @@ -43,6 +45,7 @@ export function useGetUserCasesPermissions() { casesCapabilities.update, casesCapabilities.delete, casesCapabilities.push, + casesCapabilities.connectors, ]); return casesPermissions; diff --git a/x-pack/plugins/observability_shared/public/utils/cases_permissions.ts b/x-pack/plugins/observability_shared/public/utils/cases_permissions.ts index 2b3ff9cfbaf54..a0b6a8aed95b0 100644 --- a/x-pack/plugins/observability_shared/public/utils/cases_permissions.ts +++ b/x-pack/plugins/observability_shared/public/utils/cases_permissions.ts @@ -12,4 +12,5 @@ export const noCasesPermissions = () => ({ update: false, delete: false, push: false, + connectors: false, }); diff --git a/x-pack/plugins/security_solution/public/cases_test_utils.ts b/x-pack/plugins/security_solution/public/cases_test_utils.ts index 8dd64424e41e5..d177934cb02ee 100644 --- a/x-pack/plugins/security_solution/public/cases_test_utils.ts +++ b/x-pack/plugins/security_solution/public/cases_test_utils.ts @@ -11,6 +11,7 @@ export const noCasesCapabilities = () => ({ update_cases: false, delete_cases: false, push_cases: false, + cases_connector: false, }); export const readCasesCapabilities = () => ({ @@ -19,6 +20,7 @@ export const readCasesCapabilities = () => ({ update_cases: false, delete_cases: false, push_cases: false, + cases_connector: true, }); export const allCasesCapabilities = () => ({ @@ -27,6 +29,7 @@ export const allCasesCapabilities = () => ({ update_cases: true, delete_cases: true, push_cases: true, + cases_connector: true, }); export const noCasesPermissions = () => ({ @@ -36,6 +39,7 @@ export const noCasesPermissions = () => ({ update: false, delete: false, push: false, + connectors: false, }); export const readCasesPermissions = () => ({ @@ -45,6 +49,7 @@ export const readCasesPermissions = () => ({ update: false, delete: false, push: false, + connectors: true, }); export const writeCasesPermissions = () => ({ @@ -54,6 +59,7 @@ export const writeCasesPermissions = () => ({ update: true, delete: true, push: true, + connectors: true, }); export const allCasesPermissions = () => ({ @@ -63,4 +69,5 @@ export const allCasesPermissions = () => ({ update: true, delete: true, push: true, + connectors: true, }); diff --git a/x-pack/plugins/security_solution/public/common/lib/kibana/hooks.ts b/x-pack/plugins/security_solution/public/common/lib/kibana/hooks.ts index 1713f35a9a2d2..043d1a0ab36f0 100644 --- a/x-pack/plugins/security_solution/public/common/lib/kibana/hooks.ts +++ b/x-pack/plugins/security_solution/public/common/lib/kibana/hooks.ts @@ -161,6 +161,7 @@ export const useGetUserCasesPermissions = () => { update: false, delete: false, push: false, + connectors: false, }); const uiCapabilities = useKibana().services.application.capabilities; const casesCapabilities = useKibana().services.cases.helpers.getUICapabilities( @@ -175,6 +176,7 @@ export const useGetUserCasesPermissions = () => { update: casesCapabilities.update, delete: casesCapabilities.delete, push: casesCapabilities.push, + connectors: casesCapabilities.connectors, }); }, [ casesCapabilities.all, @@ -183,6 +185,7 @@ export const useGetUserCasesPermissions = () => { casesCapabilities.update, casesCapabilities.delete, casesCapabilities.push, + casesCapabilities.connectors, ]); return casesPermissions; diff --git a/x-pack/plugins/security_solution/server/lib/app_features/security_cases_kibana_features.ts b/x-pack/plugins/security_solution/server/lib/app_features/security_cases_kibana_features.ts index 5384e68c5945f..a2bf02c59b306 100644 --- a/x-pack/plugins/security_solution/server/lib/app_features/security_cases_kibana_features.ts +++ b/x-pack/plugins/security_solution/server/lib/app_features/security_cases_kibana_features.ts @@ -13,6 +13,10 @@ import { createUICapabilities as createCasesUICapabilities, getApiTags as getCasesApiTags, } from '@kbn/cases-plugin/common'; +import { + CASES_CONNECTORS_CAPABILITY, + GET_CONNECTORS_CONFIGURE_API_TAG, +} from '@kbn/cases-plugin/common/constants'; import type { AppFeaturesCasesConfig, BaseKibanaFeatureConfig } from './types'; import { APP_ID, CASES_FEATURE_ID } from '../../../common/constants'; import { CasesSubFeatureId } from './security_cases_kibana_sub_features'; @@ -21,48 +25,66 @@ import { AppFeatureCasesKey } from '../../../common/types/app_features'; const casesCapabilities = createCasesUICapabilities(); const casesApiTags = getCasesApiTags(APP_ID); -export const getCasesBaseKibanaFeature = (): BaseKibanaFeatureConfig => ({ - id: CASES_FEATURE_ID, - name: i18n.translate('xpack.securitySolution.featureRegistry.linkSecuritySolutionCaseTitle', { - defaultMessage: 'Cases', - }), - order: 1100, - category: DEFAULT_APP_CATEGORIES.security, - app: [CASES_FEATURE_ID, 'kibana'], - catalogue: [APP_ID], - cases: [APP_ID], - privileges: { - all: { - api: casesApiTags.all, - app: [CASES_FEATURE_ID, 'kibana'], - catalogue: [APP_ID], - cases: { - create: [APP_ID], - read: [APP_ID], - update: [APP_ID], - push: [APP_ID], - }, - savedObject: { - all: [...filesSavedObjectTypes], - read: [...filesSavedObjectTypes], - }, - ui: casesCapabilities.all, - }, - read: { - api: casesApiTags.read, - app: [CASES_FEATURE_ID, 'kibana'], - catalogue: [APP_ID], - cases: { - read: [APP_ID], +export const getCasesBaseKibanaFeature = (): BaseKibanaFeatureConfig => { + // On SecuritySolution essentials cases does not have the connector feature + const casesAllUICapabilities = casesCapabilities.all.filter( + (capability) => capability !== CASES_CONNECTORS_CAPABILITY + ); + + const casesReadUICapabilities = casesCapabilities.read.filter( + (capability) => capability !== CASES_CONNECTORS_CAPABILITY + ); + + const casesAllAPICapabilities = casesApiTags.all.filter( + (capability) => capability !== GET_CONNECTORS_CONFIGURE_API_TAG + ); + + const casesReadAPICapabilities = casesApiTags.read.filter( + (capability) => capability !== GET_CONNECTORS_CONFIGURE_API_TAG + ); + + return { + id: CASES_FEATURE_ID, + name: i18n.translate('xpack.securitySolution.featureRegistry.linkSecuritySolutionCaseTitle', { + defaultMessage: 'Cases', + }), + order: 1100, + category: DEFAULT_APP_CATEGORIES.security, + app: [CASES_FEATURE_ID, 'kibana'], + catalogue: [APP_ID], + cases: [APP_ID], + privileges: { + all: { + api: casesAllAPICapabilities, + app: [CASES_FEATURE_ID, 'kibana'], + catalogue: [APP_ID], + cases: { + create: [APP_ID], + read: [APP_ID], + update: [APP_ID], + }, + savedObject: { + all: [...filesSavedObjectTypes], + read: [...filesSavedObjectTypes], + }, + ui: casesAllUICapabilities, }, - savedObject: { - all: [], - read: [...filesSavedObjectTypes], + read: { + api: casesReadAPICapabilities, + app: [CASES_FEATURE_ID, 'kibana'], + catalogue: [APP_ID], + cases: { + read: [APP_ID], + }, + savedObject: { + all: [], + read: [...filesSavedObjectTypes], + }, + ui: casesReadUICapabilities, }, - ui: casesCapabilities.read, }, - }, -}); + }; +}; export const getCasesBaseKibanaSubFeatureIds = (): CasesSubFeatureId[] => [ CasesSubFeatureId.deleteCases, @@ -79,6 +101,18 @@ export const getCasesBaseKibanaSubFeatureIds = (): CasesSubFeatureId[] => [ */ export const getCasesAppFeaturesConfig = (): AppFeaturesCasesConfig => ({ [AppFeatureCasesKey.casesConnectors]: { - // TODO: Add cases connector configuration privileges + privileges: { + all: { + api: [GET_CONNECTORS_CONFIGURE_API_TAG], // Add cases connector get connectors API privileges + ui: [CASES_CONNECTORS_CAPABILITY], // Add cases connector UI privileges + cases: { + push: [APP_ID], // Add cases connector push privileges + }, + }, + read: { + api: [GET_CONNECTORS_CONFIGURE_API_TAG], // Add cases connector get connectors API privileges + ui: [CASES_CONNECTORS_CAPABILITY], // Add cases connector UI privileges + }, + }, }, }); diff --git a/x-pack/test/cases_api_integration/common/lib/authentication/roles.ts b/x-pack/test/cases_api_integration/common/lib/authentication/roles.ts index 49ce36a740ac8..85c33bc9517f3 100644 --- a/x-pack/test/cases_api_integration/common/lib/authentication/roles.ts +++ b/x-pack/test/cases_api_integration/common/lib/authentication/roles.ts @@ -44,6 +44,30 @@ export const noCasesPrivilegesSpace1: Role = { }, }; +export const noCasesConnectors: Role = { + name: 'no_cases_connectors', + privileges: { + elasticsearch: { + indices: [ + { + names: ['*'], + privileges: ['all'], + }, + ], + }, + kibana: [ + { + feature: { + testNoCasesConnectorFixture: ['all'], + actions: ['all'], + actionsSimulators: ['all'], + }, + spaces: ['*'], + }, + ], + }, +}; + export const globalRead: Role = { name: 'global_read', privileges: { @@ -353,6 +377,7 @@ export const securitySolutionOnlyAllSpacesRole: Role = { export const roles = [ noKibanaPrivileges, noCasesPrivilegesSpace1, + noCasesConnectors, globalRead, securitySolutionOnlyAll, securitySolutionOnlyRead, diff --git a/x-pack/test/cases_api_integration/common/lib/authentication/users.ts b/x-pack/test/cases_api_integration/common/lib/authentication/users.ts index 8a3d5ddb8d30b..a4b7828d74b9e 100644 --- a/x-pack/test/cases_api_integration/common/lib/authentication/users.ts +++ b/x-pack/test/cases_api_integration/common/lib/authentication/users.ts @@ -21,6 +21,7 @@ import { securitySolutionOnlyReadAlerts, securitySolutionOnlyReadNoIndexAlerts, securitySolutionOnlyReadDelete, + noCasesConnectors as noCasesConnectorRole, } from './roles'; import { User } from './types'; @@ -126,6 +127,12 @@ export const noCasesPrivilegesSpace1: User = { roles: [noCasesPrivilegesSpace1Role.name], }; +export const noCasesConnectors: User = { + username: 'no_cases_connectors', + password: 'no_cases_connectors', + roles: [noCasesConnectorRole.name], +}; + /** * These users will have access to all spaces. */ @@ -154,4 +161,5 @@ export const users = [ noKibanaPrivileges, noCasesPrivilegesSpace1, testDisabled, + noCasesConnectors, ]; diff --git a/x-pack/test/cases_api_integration/common/plugins/cases/server/plugin.ts b/x-pack/test/cases_api_integration/common/plugins/cases/server/plugin.ts index 90ef74625119e..b2ac0602774d5 100644 --- a/x-pack/test/cases_api_integration/common/plugins/cases/server/plugin.ts +++ b/x-pack/test/cases_api_integration/common/plugins/cases/server/plugin.ts @@ -40,6 +40,45 @@ export class FixturePlugin implements Plugin { expect(theCase.status).to.eql('open'); }); + + it('should return 403 when the user does not have access to push', async () => { + const { postedCase } = await createCaseWithConnector({ + supertest, + serviceNowSimulatorURL, + actionsRemover, + configureReq: { owner: 'testNoCasesConnectorFixture' }, + createCaseReq: { ...getPostCaseRequest(), owner: 'testNoCasesConnectorFixture' }, + }); + + await pushCase({ + supertest: supertestWithoutAuth, + caseId: postedCase.id, + connectorId: postedCase.connector.id, + expectedHttpCode: 403, + auth: { user: noCasesConnectors, space: null }, + }); + }); }); }); }); diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/configure/get_connectors.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/configure/get_connectors.ts index e75fed4f399b7..d124047831e28 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/configure/get_connectors.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/configure/get_connectors.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; import { ObjectRemover as ActionsRemover } from '../../../../../alerting_api_integration/common/lib'; import { @@ -20,10 +20,12 @@ import { getCaseConnectors, getCasesWebhookConnector, } from '../../../../common/lib/api'; +import { noCasesConnectors } from '../../../../common/lib/authentication/users'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext): void => { const supertest = getService('supertest'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); const actionsRemover = new ActionsRemover(supertest); describe('get_connectors', () => { @@ -184,5 +186,13 @@ export default ({ getService }: FtrProviderContext): void => { }, ]); }); + + it('should return 403 when the user does not have access to the case connectors', async () => { + await getCaseConnectors({ + supertest: supertestWithoutAuth, + auth: { user: noCasesConnectors, space: null }, + expectedHttpCode: 403, + }); + }); }); }; diff --git a/x-pack/test/functional_with_es_ssl/plugins/cases/public/application.tsx b/x-pack/test/functional_with_es_ssl/plugins/cases/public/application.tsx index ad28e229e23a6..afc7860303db5 100644 --- a/x-pack/test/functional_with_es_ssl/plugins/cases/public/application.tsx +++ b/x-pack/test/functional_with_es_ssl/plugins/cases/public/application.tsx @@ -42,6 +42,7 @@ const permissions = { update: true, delete: true, push: true, + connectors: true, }; const attachments = [{ type: AttachmentType.user as const, comment: 'test' }];