Skip to content

Commit fa61a65

Browse files
authored
[SIEM] [Detection engine] Add user permission to detection engine (#53778) (#53988)
* add logic to see if we can show signals or create signal index for user * fix unit test * fix spelling set up * Update msg from review * review II * fix type * review III * fix bug found by Garrett * fix snapshot
1 parent b4f5d89 commit fa61a65

File tree

23 files changed

+696
-113
lines changed

23 files changed

+696
-113
lines changed

x-pack/legacy/plugins/siem/public/components/empty_page/__snapshots__/index.test.tsx.snap

Lines changed: 6 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

x-pack/legacy/plugins/siem/public/components/empty_page/index.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ export const EmptyPage = React.memo<EmptyPageProps>(
4646
title={<h2>{title}</h2>}
4747
body={message && <p>{message}</p>}
4848
actions={
49-
<EuiFlexGroup>
50-
<EuiFlexItem>
49+
<EuiFlexGroup justifyContent="center">
50+
<EuiFlexItem grow={false}>
5151
<EuiButton
5252
fill
5353
href={actionPrimaryUrl}
@@ -59,7 +59,7 @@ export const EmptyPage = React.memo<EmptyPageProps>(
5959
</EuiFlexItem>
6060

6161
{actionSecondaryLabel && actionSecondaryUrl && (
62-
<EuiFlexItem>
62+
<EuiFlexItem grow={false}>
6363
<EuiButton
6464
href={actionSecondaryUrl}
6565
iconType={actionSecondaryIcon}

x-pack/legacy/plugins/siem/public/components/ml/api/throw_if_not_ok.ts

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,7 @@ import * as i18n from './translations';
1010
import { MlError } from '../types';
1111
import { SetupMlResponse } from '../../ml_popover/types';
1212

13-
export interface MessageBody {
14-
error?: string;
15-
message?: string;
16-
statusCode?: number;
17-
}
13+
export { MessageBody, parseJsonFromBody } from '../../../utils/api';
1814

1915
export interface MlStartJobError {
2016
error: MlError;
@@ -35,15 +31,6 @@ export class ToasterErrors extends Error implements ToasterErrorsType {
3531
}
3632
}
3733

38-
export const parseJsonFromBody = async (response: Response): Promise<MessageBody | null> => {
39-
try {
40-
const text = await response.text();
41-
return JSON.parse(text);
42-
} catch (error) {
43-
return null;
44-
}
45-
};
46-
4734
export const tryParseResponse = (response: string): string => {
4835
try {
4936
return JSON.stringify(JSON.parse(response), null, 2);

x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/api.ts

Lines changed: 102 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,27 @@ import { throwIfNotOk } from '../../../hooks/api/api';
1010
import {
1111
DETECTION_ENGINE_QUERY_SIGNALS_URL,
1212
DETECTION_ENGINE_SIGNALS_STATUS_URL,
13+
DETECTION_ENGINE_INDEX_URL,
14+
DETECTION_ENGINE_PRIVILEGES_URL,
1315
} from '../../../../common/constants';
14-
import { QuerySignals, SignalSearchResponse, UpdateSignalStatusProps } from './types';
16+
import {
17+
QuerySignals,
18+
SignalSearchResponse,
19+
UpdateSignalStatusProps,
20+
SignalsIndex,
21+
SignalIndexError,
22+
Privilege,
23+
PostSignalError,
24+
BasicSignals,
25+
} from './types';
26+
import { parseJsonFromBody } from '../../../utils/api';
1527

1628
/**
1729
* Fetch Signals by providing a query
1830
*
1931
* @param query String to match a dsl
2032
* @param kbnVersion current Kibana Version to use for headers
33+
* @param signal AbortSignal for cancelling request
2134
*/
2235
export const fetchQuerySignals = async <Hit, Aggregations>({
2336
query,
@@ -46,7 +59,7 @@ export const fetchQuerySignals = async <Hit, Aggregations>({
4659
* @param query of signals to update
4760
* @param status to update to('open' / 'closed')
4861
* @param kbnVersion current Kibana Version to use for headers
49-
* @param signal to cancel request
62+
* @param signal AbortSignal for cancelling request
5063
*/
5164
export const updateSignalStatus = async ({
5265
query,
@@ -69,3 +82,90 @@ export const updateSignalStatus = async ({
6982
await throwIfNotOk(response);
7083
return response.json();
7184
};
85+
86+
/**
87+
* Fetch Signal Index
88+
*
89+
* @param kbnVersion current Kibana Version to use for headers
90+
* @param signal AbortSignal for cancelling request
91+
*/
92+
export const getSignalIndex = async ({
93+
kbnVersion,
94+
signal,
95+
}: BasicSignals): Promise<SignalsIndex | null> => {
96+
const response = await fetch(`${chrome.getBasePath()}${DETECTION_ENGINE_INDEX_URL}`, {
97+
method: 'GET',
98+
credentials: 'same-origin',
99+
headers: {
100+
'content-type': 'application/json',
101+
'kbn-version': kbnVersion,
102+
'kbn-xsrf': kbnVersion,
103+
},
104+
signal,
105+
});
106+
if (response.ok) {
107+
const signalIndex = await response.json();
108+
return signalIndex;
109+
}
110+
const error = await parseJsonFromBody(response);
111+
if (error != null) {
112+
throw new SignalIndexError(error);
113+
}
114+
return null;
115+
};
116+
117+
/**
118+
* Get User Privileges
119+
*
120+
* @param kbnVersion current Kibana Version to use for headers
121+
* @param signal AbortSignal for cancelling request
122+
*/
123+
export const getUserPrivilege = async ({
124+
kbnVersion,
125+
signal,
126+
}: BasicSignals): Promise<Privilege | null> => {
127+
const response = await fetch(`${chrome.getBasePath()}${DETECTION_ENGINE_PRIVILEGES_URL}`, {
128+
method: 'GET',
129+
credentials: 'same-origin',
130+
headers: {
131+
'content-type': 'application/json',
132+
'kbn-version': kbnVersion,
133+
'kbn-xsrf': kbnVersion,
134+
},
135+
signal,
136+
});
137+
138+
await throwIfNotOk(response);
139+
return response.json();
140+
};
141+
142+
/**
143+
* Create Signal Index if needed it
144+
*
145+
* @param kbnVersion current Kibana Version to use for headers
146+
* @param signal AbortSignal for cancelling request
147+
*/
148+
export const createSignalIndex = async ({
149+
kbnVersion,
150+
signal,
151+
}: BasicSignals): Promise<SignalsIndex | null> => {
152+
const response = await fetch(`${chrome.getBasePath()}${DETECTION_ENGINE_INDEX_URL}`, {
153+
method: 'POST',
154+
credentials: 'same-origin',
155+
headers: {
156+
'content-type': 'application/json',
157+
'kbn-version': kbnVersion,
158+
'kbn-xsrf': kbnVersion,
159+
},
160+
signal,
161+
});
162+
if (response.ok) {
163+
const signalIndex = await response.json();
164+
return signalIndex;
165+
}
166+
const error = await parseJsonFromBody(response);
167+
if (error != null) {
168+
throw new PostSignalError(error);
169+
}
170+
return null;
171+
};
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import { MessageBody } from '../../../../components/ml/api/throw_if_not_ok';
8+
9+
export class SignalIndexError extends Error {
10+
message: string = '';
11+
statusCode: number = -1;
12+
error: string = '';
13+
14+
constructor(errObj: MessageBody) {
15+
super(errObj.message);
16+
this.message = errObj.message ?? '';
17+
this.statusCode = errObj.statusCode ?? -1;
18+
this.error = errObj.error ?? '';
19+
this.name = 'SignalIndexError';
20+
21+
// Set the prototype explicitly.
22+
Object.setPrototypeOf(this, SignalIndexError.prototype);
23+
}
24+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
export * from './get_index_error';
8+
export * from './post_index_error';
9+
export * from './privilege_user_error';
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import { MessageBody } from '../../../../components/ml/api/throw_if_not_ok';
8+
9+
export class PostSignalError extends Error {
10+
message: string = '';
11+
statusCode: number = -1;
12+
error: string = '';
13+
14+
constructor(errObj: MessageBody) {
15+
super(errObj.message);
16+
this.message = errObj.message ?? '';
17+
this.statusCode = errObj.statusCode ?? -1;
18+
this.error = errObj.error ?? '';
19+
this.name = 'PostSignalError';
20+
21+
// Set the prototype explicitly.
22+
Object.setPrototypeOf(this, PostSignalError.prototype);
23+
}
24+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import { MessageBody } from '../../../../components/ml/api/throw_if_not_ok';
8+
9+
export class PrivilegeUserError extends Error {
10+
message: string = '';
11+
statusCode: number = -1;
12+
error: string = '';
13+
14+
constructor(errObj: MessageBody) {
15+
super(errObj.message);
16+
this.message = errObj.message ?? '';
17+
this.statusCode = errObj.statusCode ?? -1;
18+
this.error = errObj.error ?? '';
19+
this.name = 'PrivilegeUserError';
20+
21+
// Set the prototype explicitly.
22+
Object.setPrototypeOf(this, PrivilegeUserError.prototype);
23+
}
24+
}

x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/types.ts

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,15 @@
44
* you may not use this file except in compliance with the Elastic License.
55
*/
66

7-
export interface QuerySignals {
8-
query: string;
7+
export * from './errors_types';
8+
9+
export interface BasicSignals {
910
kbnVersion: string;
1011
signal: AbortSignal;
1112
}
13+
export interface QuerySignals extends BasicSignals {
14+
query: string;
15+
}
1216

1317
export interface SignalsResponse {
1418
took: number;
@@ -38,3 +42,60 @@ export interface UpdateSignalStatusProps {
3842
kbnVersion: string;
3943
signal?: AbortSignal; // TODO: implement cancelling
4044
}
45+
46+
export interface SignalsIndex {
47+
name: string;
48+
}
49+
50+
export interface Privilege {
51+
username: string;
52+
has_all_requested: boolean;
53+
cluster: {
54+
monitor_ml: boolean;
55+
manage_ccr: boolean;
56+
manage_index_templates: boolean;
57+
monitor_watcher: boolean;
58+
monitor_transform: boolean;
59+
read_ilm: boolean;
60+
manage_api_key: boolean;
61+
manage_security: boolean;
62+
manage_own_api_key: boolean;
63+
manage_saml: boolean;
64+
all: boolean;
65+
manage_ilm: boolean;
66+
manage_ingest_pipelines: boolean;
67+
read_ccr: boolean;
68+
manage_rollup: boolean;
69+
monitor: boolean;
70+
manage_watcher: boolean;
71+
manage: boolean;
72+
manage_transform: boolean;
73+
manage_token: boolean;
74+
manage_ml: boolean;
75+
manage_pipeline: boolean;
76+
monitor_rollup: boolean;
77+
transport_client: boolean;
78+
create_snapshot: boolean;
79+
};
80+
index: {
81+
[indexName: string]: {
82+
all: boolean;
83+
manage_ilm: boolean;
84+
read: boolean;
85+
create_index: boolean;
86+
read_cross_cluster: boolean;
87+
index: boolean;
88+
monitor: boolean;
89+
delete: boolean;
90+
manage: boolean;
91+
delete_index: boolean;
92+
create_doc: boolean;
93+
view_index_metadata: boolean;
94+
create: boolean;
95+
manage_follow_index: boolean;
96+
manage_leader_index: boolean;
97+
write: boolean;
98+
};
99+
};
100+
isAuthenticated: boolean;
101+
}

0 commit comments

Comments
 (0)