"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.EndpointList = void 0; var _react = _interopRequireWildcard(require("react")); var _styledComponents = _interopRequireDefault(require("styled-components")); var _eui = require("@elastic/eui"); var _reactRouterDom = require("react-router-dom"); var _i18n = require("@kbn/i18n"); var _i18nReact = require("@kbn/i18n-react"); var _reselect = require("reselect"); var _reactRedux = require("react-redux"); var _transform_failed_callout = require("./components/transform_failed_callout"); var _endpoint_list_nav_link = require("./components/endpoint_list_nav_link"); var _endpoint_agent_status = require("../../../../common/components/endpoint/endpoint_agent_status"); var _details = require("./details"); var selectors = _interopRequireWildcard(require("../store/selectors")); var _hooks = require("./hooks"); var _utils = require("../utils"); var _host_constants = require("./host_constants"); var _types = require("../../../../../common/endpoint/types"); var _constants = require("../../../common/constants"); var _management_empty_state = require("../../../components/management_empty_state"); var _formatted_date = require("../../../../common/components/formatted_date"); var _use_navigate_to_app_event_handler = require("../../../../common/hooks/endpoint/use_navigate_to_app_event_handler"); var _endpoint_policy_link = require("../../../components/endpoint_policy_link"); var _types2 = require("../../../../app/types"); var _routing = require("../../../common/routing"); var _link_to = require("../../../../common/components/link_to"); var _hooks2 = require("../../../../common/lib/kibana/hooks"); var _out_of_date = require("./components/out_of_date"); var _search_bar = require("./components/search_bar"); var _administration_list_page = require("../../../components/administration_list_page"); var _table_row_actions = require("./components/table_row_actions"); var _constants2 = require("../../../../../common/constants"); var _management_empty_state_wrapper = require("../../../components/management_empty_state_wrapper"); var _user_privileges = require("../../../../common/components/user_privileges"); var _back_to_policy_list_button = require("./components/back_to_policy_list_button"); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } /* * 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. */ const MAX_PAGINATED_ITEM = 9999; const StyledDatePicker = _styledComponents.default.div` .euiFormControlLayout--group { background-color: rgba(0, 119, 204, 0.2); } `; const columnWidths = { [_types.EndpointSortableField.HOSTNAME]: '18%', [_types.EndpointSortableField.HOST_STATUS]: '15%', [_types.EndpointSortableField.POLICY_NAME]: '20%', [_types.EndpointSortableField.POLICY_STATUS]: '150px', [_types.EndpointSortableField.HOST_OS_NAME]: '90px', [_types.EndpointSortableField.HOST_IP]: '22%', [_types.EndpointSortableField.AGENT_VERSION]: '10%', [_types.EndpointSortableField.LAST_SEEN]: '15%', actions: '65px' }; const getEndpointListColumns = ({ canReadPolicyManagement, backToEndpointList, getHostPendingActions, queryParams, search, getAppUrl }) => { const lastActiveColumnName = _i18n.i18n.translate('xpack.securitySolution.endpoint.list.lastActive', { defaultMessage: 'Last active' }); const padLeft = { paddingLeft: '6px' }; return [{ field: _types.EndpointSortableField.HOSTNAME, width: columnWidths[_types.EndpointSortableField.HOSTNAME], name: _i18n.i18n.translate('xpack.securitySolution.endpoint.list.hostname', { defaultMessage: 'Endpoint' }), sortable: true, render: (hostname, item) => { const toRoutePath = (0, _routing.getEndpointDetailsPath)({ ...queryParams, name: 'endpointDetails', selected_endpoint: item.metadata.agent.id }, search); const toRouteUrl = getAppUrl({ path: toRoutePath }); return /*#__PURE__*/_react.default.createElement(_eui.EuiToolTip, { content: hostname, anchorClassName: "eui-textTruncate" }, /*#__PURE__*/_react.default.createElement(_endpoint_list_nav_link.EndpointListNavLink, { name: hostname, href: toRouteUrl, route: toRoutePath, dataTestSubj: "hostnameCellLink" })); } }, { field: _types.EndpointSortableField.HOST_STATUS, width: columnWidths[_types.EndpointSortableField.HOST_STATUS], name: _i18n.i18n.translate('xpack.securitySolution.endpoint.list.hostStatus', { defaultMessage: 'Agent status' }), sortable: true, render: (hostStatus, endpointInfo) => { return /*#__PURE__*/_react.default.createElement(_endpoint_agent_status.EndpointAgentStatus, { endpointHostInfo: endpointInfo, pendingActions: getHostPendingActions(endpointInfo.metadata.agent.id), "data-test-subj": "rowHostStatus" }); } }, { field: _types.EndpointSortableField.POLICY_NAME, width: columnWidths[_types.EndpointSortableField.POLICY_NAME], name: _i18n.i18n.translate('xpack.securitySolution.endpoint.list.policy', { defaultMessage: 'Policy' }), sortable: true, truncateText: true, render: (policyName, item) => { const policy = item.metadata.Endpoint.policy.applied; return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_eui.EuiToolTip, { content: policyName, anchorClassName: "eui-textTruncate" }, canReadPolicyManagement ? /*#__PURE__*/_react.default.createElement(_endpoint_policy_link.EndpointPolicyLink, { policyId: policy.id, className: "eui-textTruncate", "data-test-subj": "policyNameCellLink", backLink: backToEndpointList }, policyName) : /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, policyName)), policy.endpoint_policy_version && /*#__PURE__*/_react.default.createElement(_eui.EuiText, { color: "subdued", size: "xs", style: { whiteSpace: 'nowrap', ...padLeft }, className: "eui-textTruncate", "data-test-subj": "policyListRevNo" }, /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.securitySolution.endpoint.list.policy.revisionNumber", defaultMessage: "rev. {revNumber}", values: { revNumber: policy.endpoint_policy_version } })), (0, _utils.isPolicyOutOfDate)(policy, item.policy_info) && /*#__PURE__*/_react.default.createElement(_out_of_date.OutOfDate, { style: padLeft, "data-test-subj": "rowPolicyOutOfDate" })); } }, { field: _types.EndpointSortableField.POLICY_STATUS, width: columnWidths[_types.EndpointSortableField.POLICY_STATUS], name: _i18n.i18n.translate('xpack.securitySolution.endpoint.list.policyStatus', { defaultMessage: 'Policy status' }), sortable: true, render: (status, item) => { const toRoutePath = (0, _routing.getEndpointDetailsPath)({ name: 'endpointPolicyResponse', ...queryParams, selected_endpoint: item.metadata.agent.id }); const toRouteUrl = getAppUrl({ path: toRoutePath }); return /*#__PURE__*/_react.default.createElement(_eui.EuiToolTip, { content: _host_constants.POLICY_STATUS_TO_TEXT[status], anchorClassName: "eui-textTruncate" }, /*#__PURE__*/_react.default.createElement(_eui.EuiHealth, { color: _host_constants.POLICY_STATUS_TO_HEALTH_COLOR[status], className: "eui-textTruncate eui-fullWidth", "data-test-subj": "rowPolicyStatus" }, /*#__PURE__*/_react.default.createElement(_endpoint_list_nav_link.EndpointListNavLink, { name: _host_constants.POLICY_STATUS_TO_TEXT[status], href: toRouteUrl, route: toRoutePath, dataTestSubj: "policyStatusCellLink" }))); } }, { field: _types.EndpointSortableField.HOST_OS_NAME, width: columnWidths[_types.EndpointSortableField.HOST_OS_NAME], name: _i18n.i18n.translate('xpack.securitySolution.endpoint.list.os', { defaultMessage: 'OS' }), sortable: true, render: os => { return /*#__PURE__*/_react.default.createElement(_eui.EuiToolTip, { content: os, anchorClassName: "eui-textTruncate" }, /*#__PURE__*/_react.default.createElement(_eui.EuiText, { size: "s", className: "eui-textTruncate eui-fullWidth" }, /*#__PURE__*/_react.default.createElement("p", { className: "eui-displayInline eui-TextTruncate" }, os))); } }, { field: _types.EndpointSortableField.HOST_IP, width: columnWidths[_types.EndpointSortableField.HOST_IP], name: _i18n.i18n.translate('xpack.securitySolution.endpoint.list.ip', { defaultMessage: 'IP address' }), sortable: true, render: ip => { return /*#__PURE__*/_react.default.createElement(_eui.EuiToolTip, { content: ip.toString().replace(',', ', '), anchorClassName: "eui-textTruncate" }, /*#__PURE__*/_react.default.createElement(_eui.EuiText, { size: "s", className: "eui-textTruncate eui-fullWidth" }, /*#__PURE__*/_react.default.createElement("p", { className: "eui-displayInline eui-textTruncate" }, ip.toString().replace(',', ', ')))); } }, { field: _types.EndpointSortableField.AGENT_VERSION, width: columnWidths[_types.EndpointSortableField.AGENT_VERSION], name: _i18n.i18n.translate('xpack.securitySolution.endpoint.list.endpointVersion', { defaultMessage: 'Version' }), sortable: true, render: version => { return /*#__PURE__*/_react.default.createElement(_eui.EuiToolTip, { content: version, anchorClassName: "eui-textTruncate" }, /*#__PURE__*/_react.default.createElement(_eui.EuiText, { size: "s", className: "eui-textTruncate eui-fullWidth" }, /*#__PURE__*/_react.default.createElement("p", { className: "eui-displayInline eui-TextTruncate" }, version))); } }, { field: _types.EndpointSortableField.LAST_SEEN, width: columnWidths[_types.EndpointSortableField.LAST_SEEN], name: lastActiveColumnName, sortable: true, render(dateValue) { return /*#__PURE__*/_react.default.createElement(_formatted_date.FormattedDate, { fieldName: lastActiveColumnName, value: dateValue, className: "eui-textTruncate" }); } }, { field: '', width: columnWidths.actions, name: _i18n.i18n.translate('xpack.securitySolution.endpoint.list.actions', { defaultMessage: 'Actions' }), actions: [{ render: item => { return /*#__PURE__*/_react.default.createElement(_table_row_actions.TableRowActions, { endpointMetadata: item.metadata }); } }] }]; }; // FIXME: this needs refactoring - we are pulling in all selectors from endpoint, which includes many more than what the list uses const selector = (0, _reselect.createStructuredSelector)(selectors); const stateHandleDeployEndpointsClick = { onDoneNavigateTo: [_constants2.APP_UI_ID, { path: (0, _routing.getEndpointListPath)({ name: 'endpointList' }) }] }; const EndpointList = () => { const history = (0, _reactRouterDom.useHistory)(); const { listData, pageIndex, pageSize, sortField, sortDirection, totalHits: totalItemCount, listLoading: loading, listError, uiQueryParams: queryParams, hasSelectedEndpoint, policyItems, selectedPolicyId, policyItemsLoading, endpointPackageVersion, endpointsExist, autoRefreshInterval, isAutoRefreshEnabled, patternsError, metadataTransformStats } = (0, _hooks.useEndpointSelector)(selector); const getHostPendingActions = (0, _hooks.useEndpointSelector)(selectors.getEndpointPendingActionsCallback); const { canReadEndpointList, canAccessFleet, canReadPolicyManagement, loading: endpointPrivilegesLoading } = (0, _user_privileges.useUserPrivileges)().endpointPrivileges; const { search } = (0, _link_to.useFormatUrl)(_types2.SecurityPageName.administration); const { search: searchParams } = (0, _reactRouterDom.useLocation)(); const { state: routeState = {} } = (0, _reactRouterDom.useLocation)(); const { getAppUrl } = (0, _hooks2.useAppUrl)(); const dispatch = (0, _reactRedux.useDispatch)(); // cap ability to page at 10k records. (max_result_window) const maxPageCount = totalItemCount > MAX_PAGINATED_ITEM ? MAX_PAGINATED_ITEM : totalItemCount; const hasPolicyData = (0, _react.useMemo)(() => policyItems && policyItems.length > 0, [policyItems]); const hasListData = (0, _react.useMemo)(() => listData && listData.length > 0, [listData]); const refreshStyle = (0, _react.useMemo)(() => { return { display: endpointsExist ? 'flex' : 'none', maxWidth: 200 }; }, [endpointsExist]); const refreshIsPaused = (0, _react.useMemo)(() => { return !endpointsExist ? false : hasSelectedEndpoint ? true : !isAutoRefreshEnabled; }, [endpointsExist, hasSelectedEndpoint, isAutoRefreshEnabled]); const refreshInterval = (0, _react.useMemo)(() => { return !endpointsExist ? _constants.DEFAULT_POLL_INTERVAL : autoRefreshInterval; }, [endpointsExist, autoRefreshInterval]); const shouldShowKQLBar = (0, _react.useMemo)(() => { return endpointsExist && !patternsError; }, [endpointsExist, patternsError]); const paginationSetup = (0, _react.useMemo)(() => { return { pageIndex, pageSize, totalItemCount: maxPageCount, pageSizeOptions: [..._constants.MANAGEMENT_PAGE_SIZE_OPTIONS], showPerPageOptions: true }; }, [pageIndex, pageSize, maxPageCount]); const onTableChange = (0, _react.useCallback)(({ page, sort }) => { const { index, size } = page; // FIXME: PT: if endpoint details is open, table is not displaying correct number of rows history.push((0, _routing.getEndpointListPath)({ name: 'endpointList', ...queryParams, page_index: JSON.stringify(index), page_size: JSON.stringify(size), sort_direction: sort === null || sort === void 0 ? void 0 : sort.direction, sort_field: sort === null || sort === void 0 ? void 0 : sort.field })); }, [history, queryParams]); const stateHandleCreatePolicyClick = (0, _react.useMemo)(() => ({ onCancelNavigateTo: [_constants2.APP_UI_ID, { path: (0, _routing.getEndpointListPath)({ name: 'endpointList' }) }], onCancelUrl: getAppUrl({ path: (0, _routing.getEndpointListPath)({ name: 'endpointList' }) }), onSaveNavigateTo: [_constants2.APP_UI_ID, { path: (0, _routing.getEndpointListPath)({ name: 'endpointList' }) }] }), [getAppUrl]); const handleCreatePolicyClick = (0, _use_navigate_to_app_event_handler.useNavigateToAppEventHandler)('fleet', { path: `/integrations/${endpointPackageVersion ? `/endpoint-${endpointPackageVersion}` : ''}/add-integration`, state: stateHandleCreatePolicyClick }); const backToEndpointList = (0, _react.useMemo)(() => { const endpointListPath = (0, _routing.getEndpointListPath)({ name: 'endpointList' }, searchParams); return { navigateTo: [_constants2.APP_UI_ID, { path: endpointListPath }], label: _i18n.i18n.translate('xpack.securitySolution.endpoint.policy.details.backToListTitle', { defaultMessage: 'View all endpoints' }), href: getAppUrl({ path: endpointListPath }) }; }, [getAppUrl, searchParams]); const onRefresh = (0, _react.useCallback)(() => { dispatch({ type: 'appRequestedEndpointList' }); }, [dispatch]); const onRefreshChange = (0, _react.useCallback)(evt => { dispatch({ type: 'userUpdatedEndpointListRefreshOptions', payload: { isAutoRefreshEnabled: !evt.isPaused, autoRefreshInterval: evt.refreshInterval } }); }, [dispatch]); // Used for an auto-refresh super date picker version without any date/time selection const onTimeChange = (0, _react.useCallback)(() => {}, []); const handleDeployEndpointsClick = (0, _use_navigate_to_app_event_handler.useNavigateToAppEventHandler)('fleet', { path: `/policies/${selectedPolicyId}?openEnrollmentFlyout=true`, state: stateHandleDeployEndpointsClick }); const handleSelectableOnChange = (0, _react.useCallback)(changedOptions => { return changedOptions.some(option => { if ('checked' in option && option.checked === 'on') { dispatch({ type: 'userSelectedEndpointPolicy', payload: { selectedPolicyId: option.key } }); return true; } else { return false; } }); }, [dispatch]); const setTableRowProps = (0, _react.useCallback)(endpoint => { return { 'data-endpoint-id': endpoint.metadata.agent.id }; }, []); const columns = (0, _react.useMemo)(() => getEndpointListColumns({ canReadPolicyManagement, backToEndpointList, getAppUrl, getHostPendingActions, queryParams, search }), [backToEndpointList, canReadPolicyManagement, getAppUrl, getHostPendingActions, queryParams, search]); const sorting = (0, _react.useMemo)(() => ({ sort: { field: sortField, direction: sortDirection } }), [sortDirection, sortField]); const mutableListData = (0, _react.useMemo)(() => [...listData], [listData]); const renderTableOrEmptyState = (0, _react.useMemo)(() => { if (endpointsExist) { return /*#__PURE__*/_react.default.createElement(_eui.EuiBasicTable, { "data-test-subj": "endpointListTable", items: mutableListData, columns: columns, error: listError === null || listError === void 0 ? void 0 : listError.message, pagination: paginationSetup, onChange: onTableChange, loading: loading, rowProps: setTableRowProps, sorting: sorting }); } else if (canReadEndpointList && !canAccessFleet) { return /*#__PURE__*/_react.default.createElement(_management_empty_state_wrapper.ManagementEmptyStateWrapper, null, /*#__PURE__*/_react.default.createElement(_management_empty_state.PolicyEmptyState, { loading: endpointPrivilegesLoading })); } else if (!policyItemsLoading && hasPolicyData) { const selectionOptions = policyItems.map(item => { return { key: item.policy_id, label: item.name, checked: selectedPolicyId === item.policy_id ? 'on' : undefined }; }); return /*#__PURE__*/_react.default.createElement(_management_empty_state.HostsEmptyState, { loading: loading, onActionClick: handleDeployEndpointsClick, actionDisabled: !selectedPolicyId, handleSelectableOnChange: handleSelectableOnChange, selectionOptions: selectionOptions }); } else { return /*#__PURE__*/_react.default.createElement(_management_empty_state_wrapper.ManagementEmptyStateWrapper, null, /*#__PURE__*/_react.default.createElement(_management_empty_state.PolicyEmptyState, { loading: policyItemsLoading, onActionClick: handleCreatePolicyClick })); } }, [canAccessFleet, canReadEndpointList, columns, endpointsExist, endpointPrivilegesLoading, handleCreatePolicyClick, handleDeployEndpointsClick, handleSelectableOnChange, hasPolicyData, listError === null || listError === void 0 ? void 0 : listError.message, loading, mutableListData, onTableChange, paginationSetup, policyItemsLoading, policyItems, selectedPolicyId, setTableRowProps, sorting]); return /*#__PURE__*/_react.default.createElement(_administration_list_page.AdministrationListPage, { "data-test-subj": "endpointPage", hideHeader: !endpointsExist, title: /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.securitySolution.endpoint.list.pageTitle", defaultMessage: "Endpoints" }), subtitle: /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.securitySolution.endpoint.list.pageSubTitle", defaultMessage: "Hosts running Elastic Defend" }), headerBackComponent: /*#__PURE__*/_react.default.createElement(_back_to_policy_list_button.BackToPolicyListButton, { backLink: routeState.backLink }) }, hasSelectedEndpoint && /*#__PURE__*/_react.default.createElement(_details.EndpointDetailsFlyout, null), /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_transform_failed_callout.TransformFailedCallout, { metadataTransformStats: metadataTransformStats, hasNoPolicyData: !hasPolicyData }), /*#__PURE__*/_react.default.createElement(_eui.EuiFlexGroup, { gutterSize: "s", alignItems: "center" }, shouldShowKQLBar && /*#__PURE__*/_react.default.createElement(_eui.EuiFlexItem, null, /*#__PURE__*/_react.default.createElement(_search_bar.AdminSearchBar, null)), /*#__PURE__*/_react.default.createElement(_eui.EuiFlexItem, { grow: false, style: refreshStyle }, /*#__PURE__*/_react.default.createElement(StyledDatePicker, null, /*#__PURE__*/_react.default.createElement(_eui.EuiSuperDatePicker, { className: "endpointListDatePicker", onTimeChange: onTimeChange, isDisabled: hasSelectedEndpoint, onRefresh: onRefresh, isPaused: refreshIsPaused, refreshInterval: refreshInterval, onRefreshChange: onRefreshChange, isAutoRefreshOnly: true })))), /*#__PURE__*/_react.default.createElement(_eui.EuiSpacer, { size: "m" })), hasListData && /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_eui.EuiText, { color: "subdued", size: "xs", "data-test-subj": "endpointListTableTotal" }, totalItemCount > MAX_PAGINATED_ITEM + 1 ? /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.securitySolution.endpoint.list.totalCount.limited", defaultMessage: "Showing {limit} of {totalItemCount, plural, one {# endpoint} other {# endpoints}}", values: { totalItemCount, limit: MAX_PAGINATED_ITEM + 1 } }) : /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.securitySolution.endpoint.list.totalCount", defaultMessage: "Showing {totalItemCount, plural, one {# endpoint} other {# endpoints}}", values: { totalItemCount } })), /*#__PURE__*/_react.default.createElement(_eui.EuiHorizontalRule, { margin: "xs" })), renderTableOrEmptyState); }; exports.EndpointList = EndpointList;