From 9e93e35aa794aece5cf2f322caeb16a10813149e Mon Sep 17 00:00:00 2001
From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Date: Thu, 5 Dec 2024 05:01:58 +1100
Subject: [PATCH] [8.x] [ResponseOps][Cases] Fix edit cases settings privilege
(#202053) (#202971)
# Backport
This will backport the following commits from `main` to `8.x`:
- [[ResponseOps][Cases] Fix edit cases settings privilege
(#202053)](https://github.com/elastic/kibana/pull/202053)
### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)
Co-authored-by: Janki Salvi <117571355+js-jankisalvi@users.noreply.github.com>
---
.../cases/common/utils/capabilities.test.tsx | 1 -
.../cases/common/utils/capabilities.ts | 1 -
.../components/configure_cases/index.test.tsx | 6 +-
.../components/configure_cases/index.tsx | 24 +++----
.../public/components/custom_fields/index.tsx | 8 +--
.../public/components/templates/index.tsx | 51 +++++++--------
.../apps/cases/common/roles.ts | 26 +++++++-
.../apps/cases/common/users.ts | 28 +++++---
.../apps/cases/group1/index.ts | 2 +-
.../group1/{deletion.ts => sub_privileges.ts} | 65 ++++++++++++++++++-
10 files changed, 146 insertions(+), 66 deletions(-)
rename x-pack/test/functional_with_es_ssl/apps/cases/group1/{deletion.ts => sub_privileges.ts} (69%)
diff --git a/x-pack/plugins/cases/common/utils/capabilities.test.tsx b/x-pack/plugins/cases/common/utils/capabilities.test.tsx
index 11f74af8e02d8..6194cfd9aef02 100644
--- a/x-pack/plugins/cases/common/utils/capabilities.test.tsx
+++ b/x-pack/plugins/cases/common/utils/capabilities.test.tsx
@@ -17,7 +17,6 @@ describe('createUICapabilities', () => {
"update_cases",
"push_cases",
"cases_connectors",
- "cases_settings",
],
"createComment": Array [
"create_comment",
diff --git a/x-pack/plugins/cases/common/utils/capabilities.ts b/x-pack/plugins/cases/common/utils/capabilities.ts
index 6897dc6bae774..79f67b7b5445e 100644
--- a/x-pack/plugins/cases/common/utils/capabilities.ts
+++ b/x-pack/plugins/cases/common/utils/capabilities.ts
@@ -36,7 +36,6 @@ export const createUICapabilities = (): CasesUiCapabilities => ({
UPDATE_CASES_CAPABILITY,
PUSH_CASES_CAPABILITY,
CASES_CONNECTORS_CAPABILITY,
- CASES_SETTINGS_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/components/configure_cases/index.test.tsx b/x-pack/plugins/cases/public/components/configure_cases/index.test.tsx
index 7a29c959d2525..c309509d563a3 100644
--- a/x-pack/plugins/cases/public/components/configure_cases/index.test.tsx
+++ b/x-pack/plugins/cases/public/components/configure_cases/index.test.tsx
@@ -12,7 +12,7 @@ import { waitFor, screen, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { ConfigureCases } from '.';
-import { noUpdateCasesPermissions, TestProviders, createAppMockRenderer } from '../../common/mock';
+import { noCasesSettingsPermission, TestProviders, createAppMockRenderer } from '../../common/mock';
import { customFieldsConfigurationMock, templatesConfigurationMock } from '../../containers/mock';
import type { AppMockRenderer } from '../../common/mock';
import { Connectors } from './connectors';
@@ -200,10 +200,10 @@ describe('ConfigureCases', () => {
expect(wrapper.find('[data-test-subj="edit-connector-flyout"]').exists()).toBe(false);
});
- test('it disables correctly when the user cannot update', () => {
+ test('it disables correctly when the user does not have settings privilege', () => {
const newWrapper = mount(, {
wrappingComponent: TestProviders as ComponentType>,
- wrappingComponentProps: { permissions: noUpdateCasesPermissions() },
+ wrappingComponentProps: { permissions: noCasesSettingsPermission() },
});
expect(newWrapper.find('button[data-test-subj="dropdown-connectors"]').prop('disabled')).toBe(
diff --git a/x-pack/plugins/cases/public/components/configure_cases/index.tsx b/x-pack/plugins/cases/public/components/configure_cases/index.tsx
index 641482ceca4fe..071a4c5cfac4e 100644
--- a/x-pack/plugins/cases/public/components/configure_cases/index.tsx
+++ b/x-pack/plugins/cases/public/components/configure_cases/index.tsx
@@ -480,12 +480,7 @@ export const ConfigureCases: React.FC = React.memo(() => {
flyOutVisibility?.type === 'customField' && flyOutVisibility?.visible ? (
isLoading={loadingCaseConfigure || isPersistingConfiguration}
- disabled={
- !permissions.create ||
- !permissions.update ||
- loadingCaseConfigure ||
- isPersistingConfiguration
- }
+ disabled={!permissions.settings || loadingCaseConfigure || isPersistingConfiguration}
onCloseFlyout={onCloseCustomFieldFlyout}
onSaveField={onCustomFieldSave}
renderHeader={() => (
@@ -502,12 +497,7 @@ export const ConfigureCases: React.FC = React.memo(() => {
flyOutVisibility?.type === 'template' && flyOutVisibility?.visible ? (
isLoading={loadingCaseConfigure || isPersistingConfiguration}
- disabled={
- !permissions.create ||
- !permissions.update ||
- loadingCaseConfigure ||
- isPersistingConfiguration
- }
+ disabled={!permissions.settings || loadingCaseConfigure || isPersistingConfiguration}
onCloseFlyout={onCloseTemplateFlyout}
onSaveField={onTemplateSave}
renderHeader={() => (
@@ -565,7 +555,9 @@ export const ConfigureCases: React.FC = React.memo(() => {
@@ -574,13 +566,15 @@ export const ConfigureCases: React.FC = React.memo(() => {
diff --git a/x-pack/plugins/cases/public/components/custom_fields/index.tsx b/x-pack/plugins/cases/public/components/custom_fields/index.tsx
index d749a7aba9bea..f93c72eb6a18e 100644
--- a/x-pack/plugins/cases/public/components/custom_fields/index.tsx
+++ b/x-pack/plugins/cases/public/components/custom_fields/index.tsx
@@ -17,7 +17,6 @@ import {
} from '@elastic/eui';
import * as i18n from './translations';
-import { useCasesContext } from '../cases_context/use_cases_context';
import type { CustomFieldsConfiguration } from '../../../common/types/domain';
import { MAX_CUSTOM_FIELDS_PER_CASE } from '../../../common/constants';
import { CustomFieldsList } from './custom_fields_list';
@@ -38,8 +37,6 @@ const CustomFieldsComponent: React.FC = ({
handleEditCustomField,
customFields,
}) => {
- const { permissions } = useCasesContext();
- const canAddCustomFields = permissions.create && permissions.update;
const [error, setError] = useState(false);
const onAddCustomField = useCallback(() => {
@@ -64,7 +61,7 @@ const CustomFieldsComponent: React.FC = ({
setError(false);
}
- return canAddCustomFields ? (
+ return (
{i18n.TITLE}}
@@ -113,10 +110,11 @@ const CustomFieldsComponent: React.FC = ({
)}
+
- ) : null;
+ );
};
CustomFieldsComponent.displayName = 'CustomFields';
diff --git a/x-pack/plugins/cases/public/components/templates/index.tsx b/x-pack/plugins/cases/public/components/templates/index.tsx
index 479101d2889ad..6c15f1e9ef464 100644
--- a/x-pack/plugins/cases/public/components/templates/index.tsx
+++ b/x-pack/plugins/cases/public/components/templates/index.tsx
@@ -17,7 +17,6 @@ import {
} from '@elastic/eui';
import { MAX_TEMPLATES_LENGTH } from '../../../common/constants';
import type { CasesConfigurationUITemplate } from '../../../common/ui';
-import { useCasesContext } from '../cases_context/use_cases_context';
import { ExperimentalBadge } from '../experimental_badge/experimental_badge';
import * as i18n from './translations';
import { TemplatesList } from './templates_list';
@@ -39,8 +38,6 @@ const TemplatesComponent: React.FC = ({
onEditTemplate,
onDeleteTemplate,
}) => {
- const { permissions } = useCasesContext();
- const canAddTemplates = permissions.create && permissions.update;
const [error, setError] = useState(false);
const handleAddTemplate = useCallback(() => {
@@ -103,31 +100,29 @@ const TemplatesComponent: React.FC = ({
) : null}
- {canAddTemplates ? (
-
-
- {templates.length < MAX_TEMPLATES_LENGTH ? (
-
- {i18n.ADD_TEMPLATE}
-
- ) : (
-
-
- {i18n.MAX_TEMPLATE_LIMIT(MAX_TEMPLATES_LENGTH)}
-
-
- )}
-
-
-
- ) : null}
+
+
+ {templates.length < MAX_TEMPLATES_LENGTH ? (
+
+ {i18n.ADD_TEMPLATE}
+
+ ) : (
+
+
+ {i18n.MAX_TEMPLATE_LIMIT(MAX_TEMPLATES_LENGTH)}
+
+
+ )}
+
+
+
);
diff --git a/x-pack/test/functional_with_es_ssl/apps/cases/common/roles.ts b/x-pack/test/functional_with_es_ssl/apps/cases/common/roles.ts
index 0e8cb455ad299..f10fc0394569f 100644
--- a/x-pack/test/functional_with_es_ssl/apps/cases/common/roles.ts
+++ b/x-pack/test/functional_with_es_ssl/apps/cases/common/roles.ts
@@ -59,6 +59,30 @@ export const casesNoDelete: Role = {
},
};
+export const casesReadAndEditSettings: Role = {
+ name: 'cases_read_and_edit_settings',
+ privileges: {
+ elasticsearch: {
+ indices: [
+ {
+ names: ['*'],
+ privileges: ['all'],
+ },
+ ],
+ },
+ kibana: [
+ {
+ feature: {
+ generalCasesV2: ['minimal_read', 'cases_settings'],
+ actions: ['all'],
+ actionsSimulators: ['all'],
+ },
+ spaces: ['*'],
+ },
+ ],
+ },
+};
+
export const casesAll: Role = {
name: 'cases_all_role',
privileges: {
@@ -83,4 +107,4 @@ export const casesAll: Role = {
},
};
-export const roles = [casesReadDelete, casesNoDelete, casesAll];
+export const roles = [casesReadDelete, casesNoDelete, casesAll, casesReadAndEditSettings];
diff --git a/x-pack/test/functional_with_es_ssl/apps/cases/common/users.ts b/x-pack/test/functional_with_es_ssl/apps/cases/common/users.ts
index 8d213e5b78075..8964f414662fc 100644
--- a/x-pack/test/functional_with_es_ssl/apps/cases/common/users.ts
+++ b/x-pack/test/functional_with_es_ssl/apps/cases/common/users.ts
@@ -6,7 +6,7 @@
*/
import { User } from '../../../../cases_api_integration/common/lib/authentication/types';
-import { casesAll, casesNoDelete, casesReadDelete } from './roles';
+import { casesAll, casesNoDelete, casesReadDelete, casesReadAndEditSettings } from './roles';
/**
* Users for Cases in the Stack
@@ -18,12 +18,6 @@ export const casesReadDeleteUser: User = {
roles: [casesReadDelete.name],
};
-export const casesNoDeleteUser: User = {
- username: 'cases_no_delete_user',
- password: 'password',
- roles: [casesNoDelete.name],
-};
-
export const casesAllUser: User = {
username: 'cases_all_user',
password: 'password',
@@ -36,4 +30,22 @@ export const casesAllUser2: User = {
roles: [casesAll.name],
};
-export const users = [casesReadDeleteUser, casesNoDeleteUser, casesAllUser, casesAllUser2];
+export const casesReadAndEditSettingsUser: User = {
+ username: 'cases_read_and_edit_settings_user',
+ password: 'password',
+ roles: [casesReadAndEditSettings.name],
+};
+
+export const casesNoDeleteUser: User = {
+ username: 'cases_no_delete_user',
+ password: 'password',
+ roles: [casesNoDelete.name],
+};
+
+export const users = [
+ casesReadDeleteUser,
+ casesNoDeleteUser,
+ casesAllUser,
+ casesAllUser2,
+ casesReadAndEditSettingsUser,
+];
diff --git a/x-pack/test/functional_with_es_ssl/apps/cases/group1/index.ts b/x-pack/test/functional_with_es_ssl/apps/cases/group1/index.ts
index 330bd820aea85..c7a1405c562b4 100644
--- a/x-pack/test/functional_with_es_ssl/apps/cases/group1/index.ts
+++ b/x-pack/test/functional_with_es_ssl/apps/cases/group1/index.ts
@@ -11,6 +11,6 @@ export default ({ loadTestFile }: FtrProviderContext) => {
describe('Cases - group 1', function () {
loadTestFile(require.resolve('./create_case_form'));
loadTestFile(require.resolve('./view_case'));
- loadTestFile(require.resolve('./deletion'));
+ loadTestFile(require.resolve('./sub_privileges'));
});
};
diff --git a/x-pack/test/functional_with_es_ssl/apps/cases/group1/deletion.ts b/x-pack/test/functional_with_es_ssl/apps/cases/group1/sub_privileges.ts
similarity index 69%
rename from x-pack/test/functional_with_es_ssl/apps/cases/group1/deletion.ts
rename to x-pack/test/functional_with_es_ssl/apps/cases/group1/sub_privileges.ts
index f23d0d01867cf..aecdb1623ff3d 100644
--- a/x-pack/test/functional_with_es_ssl/apps/cases/group1/deletion.ts
+++ b/x-pack/test/functional_with_es_ssl/apps/cases/group1/sub_privileges.ts
@@ -5,8 +5,16 @@
* 2.0.
*/
+import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
-import { users, roles, casesReadDeleteUser, casesAllUser, casesNoDeleteUser } from '../common';
+import {
+ users,
+ roles,
+ casesReadDeleteUser,
+ casesAllUser,
+ casesNoDeleteUser,
+ casesReadAndEditSettingsUser,
+} from '../common';
import {
createUsersAndRoles,
deleteUsersAndRoles,
@@ -16,8 +24,9 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
const PageObjects = getPageObjects(['security', 'header']);
const testSubjects = getService('testSubjects');
const cases = getService('cases');
+ const toasts = getService('toasts');
- describe('cases deletion sub privilege', () => {
+ describe('cases sub privilege', () => {
before(async () => {
await createUsersAndRoles(getService, users, roles);
await PageObjects.security.forceLogout();
@@ -29,7 +38,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
await PageObjects.security.forceLogout();
});
- describe('create two cases', () => {
+ describe('cases_delete', () => {
beforeEach(async () => {
await cases.api.createNthRandomCases(2);
});
@@ -119,6 +128,56 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
});
}
});
+
+ describe('cases_settings', () => {
+ afterEach(async () => {
+ await cases.api.deleteAllCases();
+ });
+
+ for (const user of [casesReadAndEditSettingsUser, casesAllUser]) {
+ describe(`logging in with user ${user.username}`, () => {
+ before(async () => {
+ await PageObjects.security.login(user.username, user.password);
+ });
+
+ after(async () => {
+ await PageObjects.security.forceLogout();
+ });
+
+ it(`User ${user.username} can navigate to settings`, async () => {
+ await cases.navigation.navigateToConfigurationPage();
+ });
+
+ it(`User ${user.username} can update settings`, async () => {
+ await cases.common.selectRadioGroupValue(
+ 'closure-options-radio-group',
+ 'close-by-pushing'
+ );
+ const toast = await toasts.getElementByIndex(1);
+ expect(await toast.getVisibleText()).to.be('Settings successfully updated');
+ await toasts.dismissAll();
+ });
+ });
+ }
+
+ // below users do not have access to settings
+ for (const user of [casesNoDeleteUser, casesReadDeleteUser]) {
+ describe(`cannot access settings page with user ${user.username}`, () => {
+ before(async () => {
+ await PageObjects.security.login(user.username, user.password);
+ });
+
+ after(async () => {
+ await PageObjects.security.forceLogout();
+ });
+
+ it(`User ${user.username} cannot navigate to settings`, async () => {
+ await cases.navigation.navigateToApp();
+ await testSubjects.missingOrFail('configure-case-button');
+ });
+ });
+ }
+ });
});
};