Skip to content

Commit

Permalink
Merge branch 'master' of github.com:elastic/kibana into feat/siem-dec…
Browse files Browse the repository at this point in the history
…tion-engine-design-review-1

# Conflicts:
#	x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/columns.tsx
#	x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/index.tsx
  • Loading branch information
patrykkopycinski committed Jan 11, 2020
2 parents 7353aaa + b057f18 commit b51bd4a
Show file tree
Hide file tree
Showing 34 changed files with 939 additions and 325 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import * as i18n from './translations';

const InspectContainer = styled.div<{ showInspect: boolean }>`
.euiButtonIcon {
${props => (props.showInspect ? 'opacity: 1;' : 'opacity: 0')}
${props => (props.showInspect ? 'opacity: 1;' : 'opacity: 0;')}
transition: opacity ${props => getOr(250, 'theme.eui.euiAnimSpeedNormal', props)} ease;
}
`;
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -149,20 +149,23 @@ const SearchTimelineSuperSelectComponent: React.FC<SearchTimelineSuperSelectProp
);
}, []);

const handleTimelineChange = useCallback(options => {
const selectedTimeline = options.filter(
(option: { checked: string }) => option.checked === 'on'
);
if (selectedTimeline != null && selectedTimeline.length > 0 && onTimelineChange != null) {
onTimelineChange(
isEmpty(selectedTimeline[0].title)
? i18nTimeline.UNTITLED_TIMELINE
: selectedTimeline[0].title,
selectedTimeline[0].id
const handleTimelineChange = useCallback(
options => {
const selectedTimeline = options.filter(
(option: { checked: string }) => option.checked === 'on'
);
}
setIsPopoverOpen(false);
}, []);
if (selectedTimeline != null && selectedTimeline.length > 0) {
onTimelineChange(
isEmpty(selectedTimeline[0].title)
? i18nTimeline.UNTITLED_TIMELINE
: selectedTimeline[0].title,
selectedTimeline[0].id === '-1' ? null : selectedTimeline[0].id
);
}
setIsPopoverOpen(false);
},
[onTimelineChange]
);

const handleOnScroll = useCallback(
(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,13 @@ import {
NewRule,
Rule,
FetchRuleProps,
BasicFetchProps,
} from './types';
import { throwIfNotOk } from '../../../hooks/api/api';
import { DETECTION_ENGINE_RULES_URL } from '../../../../common/constants';
import {
DETECTION_ENGINE_RULES_URL,
DETECTION_ENGINE_PREPACKAGED_URL,
} from '../../../../common/constants';

/**
* Add provided Rule
Expand Down Expand Up @@ -199,3 +203,22 @@ export const duplicateRules = async ({ rules }: DuplicateRulesProps): Promise<Ru
responses.map<Promise<Rule>>(response => response.json())
);
};

/**
* Create Prepackaged Rules
*
* @param signal AbortSignal for cancelling request
*/
export const createPrepackagedRules = async ({ signal }: BasicFetchProps): Promise<boolean> => {
const response = await fetch(`${chrome.getBasePath()}${DETECTION_ENGINE_PREPACKAGED_URL}`, {
method: 'PUT',
credentials: 'same-origin',
headers: {
'content-type': 'application/json',
'kbn-xsrf': 'true',
},
signal,
});
await throwIfNotOk(response);
return true;
};
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,7 @@ export interface DeleteRulesProps {
export interface DuplicateRulesProps {
rules: Rules;
}

export interface BasicFetchProps {
signal: AbortSignal;
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,24 @@ export const SIGNAL_FETCH_FAILURE = i18n.translate(
defaultMessage: 'Failed to query signals',
}
);

export const PRIVILEGE_FETCH_FAILURE = i18n.translate(
'xpack.siem.containers.detectionEngine.signals.errorFetchingSignalsDescription',
{
defaultMessage: 'Failed to query signals',
}
);

export const SIGNAL_GET_NAME_FAILURE = i18n.translate(
'xpack.siem.containers.detectionEngine.signals.errorGetSignalDescription',
{
defaultMessage: 'Failed to get signal index name',
}
);

export const SIGNAL_POST_FAILURE = i18n.translate(
'xpack.siem.containers.detectionEngine.signals.errorPostSignalDescription',
{
defaultMessage: 'Failed to create signal index',
}
);
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,29 @@

import { useEffect, useState } from 'react';

import { errorToToaster } from '../../../components/ml/api/error_to_toaster';
import { useStateToaster } from '../../../components/toasters';
import { getUserPrivilege } from './api';
import * as i18n from './translations';

type Return = [boolean, boolean | null, boolean | null];

interface Return {
loading: boolean;
isAuthenticated: boolean | null;
hasIndexManage: boolean | null;
hasManageApiKey: boolean | null;
hasIndexWrite: boolean | null;
}
/**
* Hook to get user privilege from
*
*/
export const usePrivilegeUser = (): Return => {
const [loading, setLoading] = useState(true);
const [isAuthenticated, setAuthenticated] = useState<boolean | null>(null);
const [hasWrite, setHasWrite] = useState<boolean | null>(null);
const [hasIndexManage, setHasIndexManage] = useState<boolean | null>(null);
const [hasIndexWrite, setHasIndexWrite] = useState<boolean | null>(null);
const [hasManageApiKey, setHasManageApiKey] = useState<boolean | null>(null);
const [, dispatchToaster] = useStateToaster();

useEffect(() => {
let isSubscribed = true;
Expand All @@ -34,13 +45,21 @@ export const usePrivilegeUser = (): Return => {
setAuthenticated(privilege.isAuthenticated);
if (privilege.index != null && Object.keys(privilege.index).length > 0) {
const indexName = Object.keys(privilege.index)[0];
setHasWrite(privilege.index[indexName].create_index);
setHasIndexManage(privilege.index[indexName].manage);
setHasIndexWrite(privilege.index[indexName].write);
setHasManageApiKey(
privilege.cluster.manage_security ||
privilege.cluster.manage_api_key ||
privilege.cluster.manage_own_api_key
);
}
}
} catch (error) {
if (isSubscribed) {
setAuthenticated(false);
setHasWrite(false);
setHasIndexManage(false);
setHasIndexWrite(false);
errorToToaster({ title: i18n.PRIVILEGE_FETCH_FAILURE, error, dispatchToaster });
}
}
if (isSubscribed) {
Expand All @@ -55,5 +74,5 @@ export const usePrivilegeUser = (): Return => {
};
}, []);

return [loading, isAuthenticated, hasWrite];
return { loading, isAuthenticated, hasIndexManage, hasManageApiKey, hasIndexWrite };
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import { useEffect, useState, useRef } from 'react';

import { errorToToaster } from '../../../components/ml/api/error_to_toaster';
import { useStateToaster } from '../../../components/toasters';
import { createPrepackagedRules } from '../rules';
import { createSignalIndex, getSignalIndex } from './api';
import * as i18n from './translations';
import { PostSignalError } from './types';
import { PostSignalError, SignalIndexError } from './types';

type Func = () => void;

Expand Down Expand Up @@ -40,11 +41,15 @@ export const useSignalIndex = (): Return => {
if (isSubscribed && signal != null) {
setSignalIndexName(signal.name);
setSignalIndexExists(true);
createPrepackagedRules({ signal: abortCtrl.signal });
}
} catch (error) {
if (isSubscribed) {
setSignalIndexName(null);
setSignalIndexExists(false);
if (error instanceof SignalIndexError && error.statusCode !== 404) {
errorToToaster({ title: i18n.SIGNAL_GET_NAME_FAILURE, error, dispatchToaster });
}
}
}
if (isSubscribed) {
Expand All @@ -69,7 +74,7 @@ export const useSignalIndex = (): Return => {
} else {
setSignalIndexName(null);
setSignalIndexExists(false);
errorToToaster({ title: i18n.SIGNAL_FETCH_FAILURE, error, dispatchToaster });
errorToToaster({ title: i18n.SIGNAL_POST_FAILURE, error, dispatchToaster });
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { EuiCallOut, EuiButton } from '@elastic/eui';
import React, { memo, useCallback, useState } from 'react';

import * as i18n from './translations';

const NoWriteSignalsCallOutComponent = () => {
const [showCallOut, setShowCallOut] = useState(true);
const handleCallOut = useCallback(() => setShowCallOut(false), [setShowCallOut]);

return showCallOut ? (
<EuiCallOut title={i18n.NO_WRITE_SIGNALS_CALLOUT_TITLE} color="warning" iconType="alert">
<p>{i18n.NO_WRITE_SIGNALS_CALLOUT_MSG}</p>
<EuiButton color="warning" onClick={handleCallOut}>
{i18n.DISMISS_CALLOUT}
</EuiButton>
</EuiCallOut>
) : null;
};

export const NoWriteSignalsCallOut = memo(NoWriteSignalsCallOutComponent);
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { i18n } from '@kbn/i18n';

export const NO_WRITE_SIGNALS_CALLOUT_TITLE = i18n.translate(
'xpack.siem.detectionEngine.noWriteSignalsCallOutTitle',
{
defaultMessage: 'Signals index permissions required',
}
);

export const NO_WRITE_SIGNALS_CALLOUT_MSG = i18n.translate(
'xpack.siem.detectionEngine.noWriteSignalsCallOutMsg',
{
defaultMessage:
'You are currently missing the required permissions to update signals. Please contact your administrator for further assistance.',
}
);

export const DISMISS_CALLOUT = i18n.translate(
'xpack.siem.detectionEngine.dismissNoWriteSignalButton',
{
defaultMessage: 'Dismiss',
}
);
Loading

0 comments on commit b51bd4a

Please sign in to comment.