mirror of
https://github.com/elastic/kibana.git
synced 2025-06-28 03:01:21 -04:00
[Security Solution] Top-level Cases feature under the Security (#112980)
* add the new top cases feature in security * fix api intyegration and cypress * fix api integration * fix cypress roles test * missing api integration * review Joe Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
01e6ff3b53
commit
3b958e76aa
20 changed files with 91 additions and 68 deletions
|
@ -10,6 +10,7 @@ import { ENABLE_CASE_CONNECTOR } from '../../cases/common';
|
|||
import { metadataTransformPattern } from './endpoint/constants';
|
||||
|
||||
export const APP_ID = 'securitySolution';
|
||||
export const CASES_FEATURE_ID = 'securitySolutionCases';
|
||||
export const SERVER_APP_ID = 'siem';
|
||||
export const APP_NAME = 'Security';
|
||||
export const APP_ICON = 'securityAnalyticsApp';
|
||||
|
|
|
@ -81,6 +81,7 @@ const secAll: Role = {
|
|||
{
|
||||
feature: {
|
||||
siem: ['all'],
|
||||
securitySolutionCases: ['all'],
|
||||
actions: ['all'],
|
||||
actionsSimulators: ['all'],
|
||||
},
|
||||
|
@ -110,7 +111,8 @@ const secReadCasesAll: Role = {
|
|||
kibana: [
|
||||
{
|
||||
feature: {
|
||||
siem: ['minimal_read', 'cases_all'],
|
||||
siem: ['read'],
|
||||
securitySolutionCases: ['all'],
|
||||
actions: ['all'],
|
||||
actionsSimulators: ['all'],
|
||||
},
|
||||
|
|
|
@ -8,6 +8,7 @@ import { getDeepLinks, PREMIUM_DEEP_LINK_IDS } from '.';
|
|||
import { AppDeepLink, Capabilities } from '../../../../../../src/core/public';
|
||||
import { SecurityPageName } from '../types';
|
||||
import { mockGlobalState } from '../../common/mock';
|
||||
import { CASES_FEATURE_ID } from '../../../common/constants';
|
||||
|
||||
const findDeepLink = (id: string, deepLinks: AppDeepLink[]): AppDeepLink | null =>
|
||||
deepLinks.reduce((deepLinkFound: AppDeepLink | null, deepLink) => {
|
||||
|
@ -58,7 +59,7 @@ describe('deepLinks', () => {
|
|||
it('should return case links for basic license with only read_cases capabilities', () => {
|
||||
const basicLicense = 'basic';
|
||||
const basicLinks = getDeepLinks(mockGlobalState.app.enableExperimental, basicLicense, {
|
||||
siem: { read_cases: true, crud_cases: false },
|
||||
[CASES_FEATURE_ID]: { read_cases: true, crud_cases: false },
|
||||
} as unknown as Capabilities);
|
||||
|
||||
expect(findDeepLink(SecurityPageName.case, basicLinks)).toBeTruthy();
|
||||
|
@ -67,7 +68,7 @@ describe('deepLinks', () => {
|
|||
it('should return case links with NO deepLinks for basic license with only read_cases capabilities', () => {
|
||||
const basicLicense = 'basic';
|
||||
const basicLinks = getDeepLinks(mockGlobalState.app.enableExperimental, basicLicense, {
|
||||
siem: { read_cases: true, crud_cases: false },
|
||||
[CASES_FEATURE_ID]: { read_cases: true, crud_cases: false },
|
||||
} as unknown as Capabilities);
|
||||
expect(findDeepLink(SecurityPageName.case, basicLinks)?.deepLinks?.length === 0).toBeTruthy();
|
||||
});
|
||||
|
@ -75,7 +76,7 @@ describe('deepLinks', () => {
|
|||
it('should return case links with deepLinks for basic license with crud_cases capabilities', () => {
|
||||
const basicLicense = 'basic';
|
||||
const basicLinks = getDeepLinks(mockGlobalState.app.enableExperimental, basicLicense, {
|
||||
siem: { read_cases: true, crud_cases: true },
|
||||
[CASES_FEATURE_ID]: { read_cases: true, crud_cases: true },
|
||||
} as unknown as Capabilities);
|
||||
|
||||
expect(
|
||||
|
@ -86,7 +87,7 @@ describe('deepLinks', () => {
|
|||
it('should return NO case links for basic license with NO read_cases capabilities', () => {
|
||||
const basicLicense = 'basic';
|
||||
const basicLinks = getDeepLinks(mockGlobalState.app.enableExperimental, basicLicense, {
|
||||
siem: { read_cases: false, crud_cases: false },
|
||||
[CASES_FEATURE_ID]: { read_cases: false, crud_cases: false },
|
||||
} as unknown as Capabilities);
|
||||
|
||||
expect(findDeepLink(SecurityPageName.case, basicLinks)).toBeFalsy();
|
||||
|
|
|
@ -48,6 +48,7 @@ import {
|
|||
TRUSTED_APPS_PATH,
|
||||
EVENT_FILTERS_PATH,
|
||||
UEBA_PATH,
|
||||
CASES_FEATURE_ID,
|
||||
HOST_ISOLATION_EXCEPTIONS_PATH,
|
||||
} from '../../../common/constants';
|
||||
import { ExperimentalFeatures } from '../../../common/experimental_features';
|
||||
|
@ -362,7 +363,7 @@ export function getDeepLinks(
|
|||
return false;
|
||||
}
|
||||
if (deepLink.id === SecurityPageName.case) {
|
||||
return capabilities == null || capabilities.siem.read_cases === true;
|
||||
return capabilities == null || capabilities[CASES_FEATURE_ID].read_cases === true;
|
||||
}
|
||||
if (deepLink.id === SecurityPageName.ueba) {
|
||||
return enableExperimental.uebaEnabled;
|
||||
|
@ -373,7 +374,7 @@ export function getDeepLinks(
|
|||
if (
|
||||
deepLink.id === SecurityPageName.case &&
|
||||
capabilities != null &&
|
||||
capabilities.siem.crud_cases === false
|
||||
capabilities[CASES_FEATURE_ID].crud_cases === false
|
||||
) {
|
||||
return {
|
||||
...deepLink,
|
||||
|
|
|
@ -12,7 +12,12 @@ import { i18n } from '@kbn/i18n';
|
|||
|
||||
import { camelCase, isArray, isObject } from 'lodash';
|
||||
import { set } from '@elastic/safer-lodash-set';
|
||||
import { APP_ID, DEFAULT_DATE_FORMAT, DEFAULT_DATE_FORMAT_TZ } from '../../../../common/constants';
|
||||
import {
|
||||
APP_ID,
|
||||
CASES_FEATURE_ID,
|
||||
DEFAULT_DATE_FORMAT,
|
||||
DEFAULT_DATE_FORMAT_TZ,
|
||||
} from '../../../../common/constants';
|
||||
import { errorToToaster, useStateToaster } from '../../components/toasters';
|
||||
import { AuthenticatedUser } from '../../../../../security/common/model';
|
||||
import { NavigateToAppOptions } from '../../../../../../../src/core/public';
|
||||
|
@ -151,13 +156,9 @@ export const useGetUserCasesPermissions = () => {
|
|||
const uiCapabilities = useKibana().services.application.capabilities;
|
||||
|
||||
useEffect(() => {
|
||||
const capabilitiesCanUserCRUD: boolean =
|
||||
typeof uiCapabilities.siem.crud_cases === 'boolean' ? uiCapabilities.siem.crud_cases : false;
|
||||
const capabilitiesCanUserRead: boolean =
|
||||
typeof uiCapabilities.siem.read_cases === 'boolean' ? uiCapabilities.siem.read_cases : false;
|
||||
setCasesPermissions({
|
||||
crud: capabilitiesCanUserCRUD,
|
||||
read: capabilitiesCanUserRead,
|
||||
crud: !!uiCapabilities[CASES_FEATURE_ID].crud_cases,
|
||||
read: !!uiCapabilities[CASES_FEATURE_ID].read_cases,
|
||||
});
|
||||
}, [uiCapabilities]);
|
||||
|
||||
|
|
|
@ -9,49 +9,48 @@ import { i18n } from '@kbn/i18n';
|
|||
|
||||
import { KibanaFeatureConfig, SubFeatureConfig } from '../../features/common';
|
||||
import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/server';
|
||||
import { APP_ID, SERVER_APP_ID } from '../common/constants';
|
||||
import { APP_ID, CASES_FEATURE_ID, SERVER_APP_ID } from '../common/constants';
|
||||
import { savedObjectTypes } from './saved_objects';
|
||||
|
||||
const CASES_SUB_FEATURE: SubFeatureConfig = {
|
||||
name: 'Cases',
|
||||
privilegeGroups: [
|
||||
{
|
||||
groupType: 'mutually_exclusive',
|
||||
privileges: [
|
||||
{
|
||||
id: 'cases_all',
|
||||
includeIn: 'all',
|
||||
name: 'All',
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: [],
|
||||
},
|
||||
// using variables with underscores here otherwise when we retrieve them from the kibana
|
||||
// capabilities in a hook I get type errors regarding boolean | ReadOnly<{[x: string]: boolean}>
|
||||
ui: ['crud_cases', 'read_cases'], // uiCapabilities.siem.crud_cases
|
||||
cases: {
|
||||
all: [APP_ID],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'cases_read',
|
||||
includeIn: 'read',
|
||||
name: 'Read',
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: [],
|
||||
},
|
||||
// using variables with underscores here otherwise when we retrieve them from the kibana
|
||||
// capabilities in a hook I get type errors regarding boolean | ReadOnly<{[x: string]: boolean}>
|
||||
ui: ['read_cases'], // uiCapabilities.siem.read_cases
|
||||
cases: {
|
||||
read: [APP_ID],
|
||||
},
|
||||
},
|
||||
],
|
||||
export const getCasesKibanaFeature = (): KibanaFeatureConfig => ({
|
||||
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: {
|
||||
app: [CASES_FEATURE_ID, 'kibana'],
|
||||
catalogue: [APP_ID],
|
||||
cases: {
|
||||
all: [APP_ID],
|
||||
},
|
||||
api: [],
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: [],
|
||||
},
|
||||
ui: ['crud_cases', 'read_cases'], // uiCapabilities[CASES_FEATURE_ID].crud_cases or read_cases
|
||||
},
|
||||
],
|
||||
};
|
||||
read: {
|
||||
app: [CASES_FEATURE_ID, 'kibana'],
|
||||
catalogue: [APP_ID],
|
||||
cases: {
|
||||
read: [APP_ID],
|
||||
},
|
||||
api: [],
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: [],
|
||||
},
|
||||
ui: ['read_cases'], // uiCapabilities[CASES_FEATURE_ID].read_cases
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const getAlertsSubFeature = (ruleTypes: string[]): SubFeatureConfig => ({
|
||||
name: i18n.translate('xpack.securitySolution.featureRegistry.manageAlertsName', {
|
||||
|
@ -108,18 +107,17 @@ export const getKibanaPrivilegesFeaturePrivileges = (ruleTypes: string[]): Kiban
|
|||
order: 1100,
|
||||
category: DEFAULT_APP_CATEGORIES.security,
|
||||
app: [APP_ID, 'kibana'],
|
||||
catalogue: ['securitySolution'],
|
||||
catalogue: [APP_ID],
|
||||
management: {
|
||||
insightsAndAlerting: ['triggersActions'],
|
||||
},
|
||||
alerting: ruleTypes,
|
||||
cases: [APP_ID],
|
||||
subFeatures: [{ ...CASES_SUB_FEATURE } /* , { ...getAlertsSubFeature(ruleTypes) } */],
|
||||
subFeatures: [],
|
||||
privileges: {
|
||||
all: {
|
||||
app: [APP_ID, 'kibana'],
|
||||
catalogue: ['securitySolution'],
|
||||
api: ['securitySolution', 'lists-all', 'lists-read', 'rac'],
|
||||
catalogue: [APP_ID],
|
||||
api: [APP_ID, 'lists-all', 'lists-read', 'rac'],
|
||||
savedObject: {
|
||||
all: ['alert', 'exception-list', 'exception-list-agnostic', ...savedObjectTypes],
|
||||
read: [],
|
||||
|
@ -136,8 +134,8 @@ export const getKibanaPrivilegesFeaturePrivileges = (ruleTypes: string[]): Kiban
|
|||
},
|
||||
read: {
|
||||
app: [APP_ID, 'kibana'],
|
||||
catalogue: ['securitySolution'],
|
||||
api: ['securitySolution', 'lists-read', 'rac'],
|
||||
catalogue: [APP_ID],
|
||||
api: [APP_ID, 'lists-read', 'rac'],
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: ['exception-list', 'exception-list-agnostic', ...savedObjectTypes],
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
"feature": {
|
||||
"ml": ["all"],
|
||||
"siem": ["all", "read_alerts", "crud_alerts"],
|
||||
"securitySolutionCases": ["all"],
|
||||
"actions": ["read"],
|
||||
"builtInAlerts": ["all"],
|
||||
"dev_tools": ["all"]
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
"feature": {
|
||||
"ml": ["read"],
|
||||
"siem": ["all", "read_alerts", "crud_alerts"],
|
||||
"securitySolutionCases": ["all"],
|
||||
"actions": ["read"],
|
||||
"builtInAlerts": ["all"]
|
||||
},
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
"feature": {
|
||||
"ml": ["all"],
|
||||
"siem": ["all", "read_alerts", "crud_alerts"],
|
||||
"securitySolutionCases": ["all"],
|
||||
"actions": ["all"],
|
||||
"builtInAlerts": ["all"]
|
||||
},
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
"feature": {
|
||||
"ml": ["read"],
|
||||
"siem": ["read", "read_alerts"],
|
||||
"securitySolutionCases": ["read"],
|
||||
"actions": ["read"],
|
||||
"builtInAlerts": ["read"]
|
||||
},
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
"feature": {
|
||||
"ml": ["read"],
|
||||
"siem": ["all", "read_alerts", "crud_alerts"],
|
||||
"securitySolutionCases": ["all"],
|
||||
"actions": ["read"],
|
||||
"builtInAlerts": ["all"]
|
||||
},
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
"feature": {
|
||||
"ml": ["read"],
|
||||
"siem": ["all", "read_alerts", "crud_alerts"],
|
||||
"securitySolutionCases": ["all"],
|
||||
"actions": ["all"],
|
||||
"builtInAlerts": ["all"]
|
||||
},
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
"feature": {
|
||||
"ml": ["read"],
|
||||
"siem": ["read", "read_alerts"],
|
||||
"securitySolutionCases": ["read"],
|
||||
"actions": ["read"],
|
||||
"builtInAlerts": ["read"]
|
||||
},
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
"feature": {
|
||||
"ml": ["read"],
|
||||
"siem": ["read", "read_alerts"],
|
||||
"securitySolutionCases": ["read"],
|
||||
"actions": ["read"],
|
||||
"builtInAlerts": ["read"]
|
||||
},
|
||||
|
|
|
@ -100,7 +100,7 @@ import aadFieldConversion from './lib/detection_engine/routes/index/signal_aad_m
|
|||
import { alertsFieldMap } from './lib/detection_engine/rule_types/field_maps/alerts';
|
||||
import { rulesFieldMap } from './lib/detection_engine/rule_types/field_maps/rules';
|
||||
import { RuleExecutionLogClient } from './lib/detection_engine/rule_execution_log/rule_execution_log_client';
|
||||
import { getKibanaPrivilegesFeaturePrivileges } from './features';
|
||||
import { getKibanaPrivilegesFeaturePrivileges, getCasesKibanaFeature } from './features';
|
||||
import { EndpointMetadataService } from './endpoint/services/metadata';
|
||||
import { CreateRuleOptions } from './lib/detection_engine/rule_types/types';
|
||||
import { ctiFieldMap } from './lib/detection_engine/rule_types/field_maps/cti';
|
||||
|
@ -305,6 +305,7 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
|
|||
];
|
||||
|
||||
plugins.features.registerKibanaFeature(getKibanaPrivilegesFeaturePrivileges(ruleTypes));
|
||||
plugins.features.registerKibanaFeature(getCasesKibanaFeature());
|
||||
|
||||
// Continue to register legacy rules against alerting client exposed through rule-registry
|
||||
if (this.setupPlugins.alerting != null) {
|
||||
|
|
|
@ -116,6 +116,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
'osquery',
|
||||
'uptime',
|
||||
'siem',
|
||||
'securitySolutionCases',
|
||||
'fleet',
|
||||
].sort()
|
||||
);
|
||||
|
|
|
@ -32,8 +32,9 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
actions: ['all', 'read'],
|
||||
stackAlerts: ['all', 'read'],
|
||||
ml: ['all', 'read'],
|
||||
siem: ['all', 'read', 'minimal_all', 'minimal_read', 'cases_all', 'cases_read'],
|
||||
siem: ['all', 'read'],
|
||||
uptime: ['all', 'read'],
|
||||
securitySolutionCases: ['all', 'read'],
|
||||
infrastructure: ['all', 'read'],
|
||||
logs: ['all', 'read'],
|
||||
apm: ['all', 'read', 'minimal_all', 'minimal_read', 'alerts_all', 'alerts_read'],
|
||||
|
|
|
@ -38,6 +38,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
osquery: ['all', 'read'],
|
||||
ml: ['all', 'read'],
|
||||
siem: ['all', 'read'],
|
||||
securitySolutionCases: ['all', 'read'],
|
||||
fleet: ['all', 'read'],
|
||||
stackAlerts: ['all', 'read'],
|
||||
actions: ['all', 'read'],
|
||||
|
|
|
@ -37,6 +37,7 @@ const secAll: Role = {
|
|||
{
|
||||
feature: {
|
||||
siem: ['all'],
|
||||
securitySolutionCases: ['all'],
|
||||
actions: ['all'],
|
||||
actionsSimulators: ['all'],
|
||||
},
|
||||
|
@ -66,7 +67,8 @@ const secAllCasesRead: Role = {
|
|||
kibana: [
|
||||
{
|
||||
feature: {
|
||||
siem: ['minimal_all', 'cases_read'],
|
||||
siem: ['all'],
|
||||
securitySolutionCases: ['read'],
|
||||
actions: ['all'],
|
||||
actionsSimulators: ['all'],
|
||||
},
|
||||
|
@ -96,7 +98,7 @@ const secAllCasesNone: Role = {
|
|||
kibana: [
|
||||
{
|
||||
feature: {
|
||||
siem: ['minimal_all'],
|
||||
siem: ['all'],
|
||||
actions: ['all'],
|
||||
actionsSimulators: ['all'],
|
||||
},
|
||||
|
@ -126,7 +128,8 @@ const secReadCasesAll: Role = {
|
|||
kibana: [
|
||||
{
|
||||
feature: {
|
||||
siem: ['minimal_read', 'cases_all'],
|
||||
siem: ['read'],
|
||||
securitySolutionCases: ['all'],
|
||||
actions: ['all'],
|
||||
actionsSimulators: ['all'],
|
||||
},
|
||||
|
@ -156,7 +159,8 @@ const secReadCasesRead: Role = {
|
|||
kibana: [
|
||||
{
|
||||
feature: {
|
||||
siem: ['minimal_read', 'cases_read'],
|
||||
siem: ['read'],
|
||||
securitySolutionCases: ['read'],
|
||||
actions: ['all'],
|
||||
actionsSimulators: ['all'],
|
||||
},
|
||||
|
@ -187,6 +191,7 @@ const secRead: Role = {
|
|||
{
|
||||
feature: {
|
||||
siem: ['read'],
|
||||
securitySolutionCases: ['read'],
|
||||
actions: ['all'],
|
||||
actionsSimulators: ['all'],
|
||||
},
|
||||
|
@ -216,7 +221,7 @@ const secReadCasesNone: Role = {
|
|||
kibana: [
|
||||
{
|
||||
feature: {
|
||||
siem: ['minimal_read'],
|
||||
siem: ['read'],
|
||||
actions: ['all'],
|
||||
actionsSimulators: ['all'],
|
||||
},
|
||||
|
|
|
@ -37,6 +37,7 @@ const secAll: Role = {
|
|||
{
|
||||
feature: {
|
||||
siem: ['all'],
|
||||
securitySolutionCases: ['all'],
|
||||
actions: ['all'],
|
||||
actionsSimulators: ['all'],
|
||||
},
|
||||
|
@ -67,6 +68,7 @@ const secRead: Role = {
|
|||
{
|
||||
feature: {
|
||||
siem: ['read'],
|
||||
securitySolutionCases: ['read'],
|
||||
actions: ['all'],
|
||||
actionsSimulators: ['all'],
|
||||
},
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue