Skip to content

Commit

Permalink
[Security Solution][Endpoint] Add Response Actions console feature fl…
Browse files Browse the repository at this point in the history
…ag and top header menu item (#127667)

* Add experimental feature: responseActionsConsoleEnabled
* Add header button for endpoint consoles
  • Loading branch information
paul-tavares authored Mar 15, 2022
1 parent b8a03f9 commit 5078062
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ export const allowedExperimentalValues = Object.freeze({
* @see test/detection_engine_api_integration/security_and_spaces/tests/telemetry/README.md
*/
previewTelemetryUrlEnabled: false,

/**
* Enables the Endpoint response actions console in various areas of the app
*/
responseActionsConsoleEnabled: false,
});

type ExperimentalConfigKeys = Array<keyof ExperimentalFeatures>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { timelineDefaults } from '../../../timelines/store/timeline/defaults';
import { timelineSelectors } from '../../../timelines/store/timeline';
import { useShallowEqualSelector } from '../../../common/hooks/use_selector';
import { getScopeFromPath, showSourcererByPath } from '../../../common/containers/sourcerer';
import { ConsolesPopoverHeaderSectionItem } from '../../../common/components/consoles_popover_header_section_item';

const BUTTON_ADD_DATA = i18n.translate('xpack.securitySolution.globalHeader.buttonAddData', {
defaultMessage: 'Add integrations',
Expand Down Expand Up @@ -72,11 +73,15 @@ export const GlobalHeader = React.memo(
return (
<InPortal node={portalNode}>
<EuiHeaderSection side="right">
{/* The consoles Popover may or may not be shown, depending on the user's authz */}
<ConsolesPopoverHeaderSectionItem />

{isDetectionsPath(pathname) && (
<EuiHeaderSectionItem>
<MlPopover />
</EuiHeaderSectionItem>
)}

<EuiHeaderSectionItem>
<EuiHeaderLinks>
<EuiHeaderLink
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React from 'react';
import { AppContextTestRender, createAppRootMockRenderer } from '../../mock/endpoint';
import { ConsolesPopoverHeaderSectionItem } from './consoles_popover_header_section_item';
import { useUserPrivileges as _useUserPrivileges } from '../user_privileges';
import { getEndpointPrivilegesInitialStateMock } from '../user_privileges/endpoint/mocks';

jest.mock('../user_privileges');
const userUserPrivilegesMock = _useUserPrivileges as jest.Mock;

describe('When rendering the `ConsolesPopoverHeaderSectionItem`', () => {
let render: () => ReturnType<AppContextTestRender['render']>;
let renderResult: ReturnType<typeof render>;
let setExperimentalFlag: AppContextTestRender['setExperimentalFlag'];

beforeEach(() => {
const mockedContext = createAppRootMockRenderer();

setExperimentalFlag = mockedContext.setExperimentalFlag;
setExperimentalFlag({ responseActionsConsoleEnabled: true });
render = () => {
return (renderResult = mockedContext.render(<ConsolesPopoverHeaderSectionItem />));
};
});

afterEach(() => {
userUserPrivilegesMock.mockReturnValue({
...userUserPrivilegesMock(),
endpointPrivileges: getEndpointPrivilegesInitialStateMock(),
});
});

it('should show menu item if feature flag is true and user has authz to endpoint management', () => {
render();

expect(renderResult.getByTestId('endpointConsoles')).toBeTruthy();
});

it('should hide the menu item if feature flag is false', () => {
setExperimentalFlag({ responseActionsConsoleEnabled: false });
render();

expect(renderResult.queryByTestId('endpointConsoles')).toBeNull();
});

it('should hide menu item if user does not have authz to endpoint management', () => {
userUserPrivilegesMock.mockReturnValue({
...userUserPrivilegesMock(),
endpointPrivileges: getEndpointPrivilegesInitialStateMock({
canAccessEndpointManagement: false,
}),
});
render();

expect(renderResult.queryByTestId('endpointConsoles')).toBeNull();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React, { memo, useState, useCallback, useMemo } from 'react';
import { EuiHeaderSectionItem, EuiHeaderSectionItemButton, EuiPopover } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { useUserPrivileges } from '../user_privileges';
import { useIsExperimentalFeatureEnabled } from '../../hooks/use_experimental_features';

const LABELS = Object.freeze({
buttonLabel: i18n.translate('xpack.securitySolution.consolesPopoverHeaderItem.buttonLabel', {
defaultMessage: 'Endpoint consoles',
}),
});

const ConsolesPopover = memo(() => {
const [isPopoverOpen, setIsPopoverOpen] = useState<boolean>(false);

const handlePopoverToggle = useCallback(() => {
setIsPopoverOpen((prevState) => !prevState);
}, []);

const handlePopoverClose = useCallback(() => {
setIsPopoverOpen(false);
}, []);

const buttonTextProps = useMemo(() => {
return { style: { fontSize: '1rem' } };
}, []);

return (
<EuiHeaderSectionItem>
<EuiPopover
anchorPosition="downRight"
id="consoles-popover"
button={
<EuiHeaderSectionItemButton
aria-expanded={isPopoverOpen}
aria-haspopup="true"
aria-label={LABELS.buttonLabel}
color="primary"
data-test-subj="endpointConsoles"
iconType="console"
iconSide="left"
onClick={handlePopoverToggle}
textProps={buttonTextProps}
>
{LABELS.buttonLabel}
</EuiHeaderSectionItemButton>
}
isOpen={isPopoverOpen}
closePopover={handlePopoverClose}
repositionOnScroll
>
{
'TODO: Currently open consoles and the ability to start a new console will be shown here soon'
}
</EuiPopover>
</EuiHeaderSectionItem>
);
});
ConsolesPopover.displayName = 'ConsolesPopover';

export const ConsolesPopoverHeaderSectionItem = memo((props) => {
const canAccessEndpointManagement =
useUserPrivileges().endpointPrivileges.canAccessEndpointManagement;
const isExperimentalFeatureEnabled = useIsExperimentalFeatureEnabled(
'responseActionsConsoleEnabled'
);

return canAccessEndpointManagement && isExperimentalFeatureEnabled ? <ConsolesPopover /> : null;
});
ConsolesPopoverHeaderSectionItem.displayName = 'ConsolesPopoverHeaderSectionItem';
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

export { ConsolesPopoverHeaderSectionItem } from './consoles_popover_header_section_item';

0 comments on commit 5078062

Please sign in to comment.