From eec7e1bf975e66cedd4afdb46fec0b16411221c7 Mon Sep 17 00:00:00 2001 From: jbiset Date: Wed, 27 Mar 2024 18:13:17 -0300 Subject: [PATCH 01/12] Migrated Server Management Cluster to embeddables without cluster controller --- plugins/main/public/components/index.js | 3 +- .../components/configuration_cards.tsx | 200 +++++++ .../cluster/components/no_results.tsx | 60 +++ .../cluster/components/overview_cards.tsx | 238 ++++++++ .../cluster/dashboard/cluster_dashboard.scss | 21 + .../cluster/dashboard/dashboard.tsx | 202 +++++++ .../dashboard_configuration_panels.ts | 86 +++ .../cluster/dashboard/dashboard_panels.ts | 122 +++++ .../management/cluster/cluster-overview.tsx | 119 ++++ .../components/management/management-main.js | 2 + .../public/controllers/management/index.js | 1 - .../templates/management/management.html | 509 ------------------ 12 files changed, 1051 insertions(+), 512 deletions(-) create mode 100644 plugins/main/public/components/management/cluster/components/configuration_cards.tsx create mode 100644 plugins/main/public/components/management/cluster/components/no_results.tsx create mode 100644 plugins/main/public/components/management/cluster/components/overview_cards.tsx create mode 100644 plugins/main/public/components/management/cluster/dashboard/cluster_dashboard.scss create mode 100644 plugins/main/public/components/management/cluster/dashboard/dashboard.tsx create mode 100644 plugins/main/public/components/management/cluster/dashboard/dashboard_configuration_panels.ts create mode 100644 plugins/main/public/components/management/cluster/dashboard/dashboard_panels.ts create mode 100644 plugins/main/public/controllers/management/components/management/cluster/cluster-overview.tsx diff --git a/plugins/main/public/components/index.js b/plugins/main/public/components/index.js index e547529f37..a2af577ff3 100644 --- a/plugins/main/public/components/index.js +++ b/plugins/main/public/components/index.js @@ -21,6 +21,7 @@ import { KibanaVisWrapper } from '../components/management/cluster/cluster-visua import { ToastNotificationsModal } from '../components/notifications/modal'; import { getAngularModule } from '../kibana-services'; import { WzUpdatesNotification } from './wz-updates-notification'; +import { ClusterOverview } from '../controllers/management/components/management/cluster/cluster-overview'; const app = getAngularModule(); @@ -29,8 +30,6 @@ app.value('WzVisualize', WzVisualize); app.value('WzMenuWrapper', WzMenuWrapper); app.value('WzAgentSelectorWrapper', WzAgentSelectorWrapper); app.value('WzBlankScreen', WzBlankScreen); -app.value('ClusterDisabled', ClusterDisabled); -app.value('ClusterTimelions', ClusterTimelions); app.value('KibanaVisualization', KibanaVisWrapper); app.value('ToastNotificationsModal', ToastNotificationsModal); app.value('WzUpdatesNotification', WzUpdatesNotification); diff --git a/plugins/main/public/components/management/cluster/components/configuration_cards.tsx b/plugins/main/public/components/management/cluster/components/configuration_cards.tsx new file mode 100644 index 0000000000..8d43b7bb93 --- /dev/null +++ b/plugins/main/public/components/management/cluster/components/configuration_cards.tsx @@ -0,0 +1,200 @@ +import React from 'react'; +import { + EuiFlexItem, + EuiButtonIcon, + EuiCard, + EuiDescriptionList, + EuiSpacer, + EuiToolTip, + EuiFlexGroup, + EuiTitle, +} from '@elastic/eui'; +import { ViewMode } from '../../../../../../../src/plugins/embeddable/public'; +import { getPlugins } from '../../../../kibana-services'; +import { DiscoverNoResults } from './no_results'; +import { getDashboardConfigurationPanels } from '../dashboard/dashboard_configuration_panels'; +import '../dashboard/cluster_dashboard.scss'; + +interface ConfigurationCardsProps { + goBack: () => void; + configuration: any; + searchBarProps: any; + results: any; + indexPatternId: string; +} + +const plugins = getPlugins(); + +const DashboardByRenderer = plugins.dashboard.DashboardContainerByValueRenderer; + +export const ConfigurationCards = ({ + goBack, + configuration, + searchBarProps, + results, + indexPatternId, +}: ConfigurationCardsProps) => { + return ( + + + + + + + + + + +

Overview

+
+
+
+
+ + + + {results?.hits?.total > 0 ? ( + + ) : ( + + )} + + + +

Configuration

+ + } + > + + + Disabled + + ), + description: configuration?.disabled ? 'true' : 'false', + }, + { + title: ( + + Hidden + + ), + description: configuration?.hidden ? 'true' : 'false', + }, + { + title: ( + + Name + + ), + description: configuration?.name, + }, + { + title: ( + + Node name + + ), + description: configuration?.node_name, + }, + { + title: ( + + Node type + + ), + description: configuration?.node_type, + }, + { + title: ( + + Bind address + + ), + description: configuration?.bind_addr, + }, + { + title: ( + + IP + + ), + description: configuration?.nodes[0] || '-', + }, + { + title: ( + + Port + + ), + description: configuration?.port, + }, + ]} + titleProps={{ + className: 'cluster-descriptionList-title', + }} + descriptionProps={{ + className: 'color-grey cluster-descriptionList-description', + }} + /> +
+
+
+
+
+ ); +}; diff --git a/plugins/main/public/components/management/cluster/components/no_results.tsx b/plugins/main/public/components/management/cluster/components/no_results.tsx new file mode 100644 index 0000000000..babfd51d32 --- /dev/null +++ b/plugins/main/public/components/management/cluster/components/no_results.tsx @@ -0,0 +1,60 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Any modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from 'react'; +import { FormattedMessage, I18nProvider } from '@osd/i18n/react'; + +import { EuiCallOut, EuiPanel } from '@elastic/eui'; + +interface Props { + message?: string; +} + +export const DiscoverNoResults = ({ message }: Props) => { + return ( + + + + ) + } + color='warning' + iconType='help' + data-test-subj='discoverNoResults' + /> + + + ); +}; diff --git a/plugins/main/public/components/management/cluster/components/overview_cards.tsx b/plugins/main/public/components/management/cluster/components/overview_cards.tsx new file mode 100644 index 0000000000..b0020fb92f --- /dev/null +++ b/plugins/main/public/components/management/cluster/components/overview_cards.tsx @@ -0,0 +1,238 @@ +import React, { useEffect, useState } from 'react'; +import { WzRequest } from '../../../../react-services'; +import { + EuiFlexItem, + EuiButtonEmpty, + EuiCard, + EuiDescriptionList, + EuiSpacer, + EuiToolTip, + EuiFlexGroup, + EuiTitle, +} from '@elastic/eui'; +import { getDashboardPanels } from '../dashboard/dashboard_panels'; +import { ViewMode } from '../../../../../../../src/plugins/embeddable/public'; +import '../dashboard/cluster_dashboard.scss'; +import { getPlugins } from '../../../../kibana-services'; +import { DiscoverNoResults } from './no_results'; + +interface OverviewCardsProps { + goAgents: () => void; + goNodes: () => void; + goConfiguration: () => void; + configuration: any; + status: any; + version: any; + nodesCount: number; + nodeList: any[]; + agentsCount: number; + searchBarProps: any; + results: any; + indexPatternId: string; + clusterName?: string; +} + +const plugins = getPlugins(); + +const DashboardByRenderer = plugins.dashboard.DashboardContainerByValueRenderer; + +export const OverviewCards = ({ + goAgents, + goNodes, + goConfiguration, + configuration, + status, + version, + nodesCount, + nodeList, + agentsCount, + searchBarProps, + results, + indexPatternId, + clusterName, +}: OverviewCardsProps) => { + return ( + <> + + + + + +

Details

+
+
+ + + View Overview + + +
+ } + > + + + + + + + Information + + } + > + + + + Nodes + + + ), + description: ( + + + {nodesCount} + + + ), + }, + { + title: ( + + + Agents + + + ), + description: ( + + + {agentsCount} + + + ), + }, + ]} + titleProps={{ + className: 'cluster-descriptionList-title', + }} + descriptionProps={{ + className: 'cluster-descriptionList-description', + }} + /> + + + + {results?.hits?.total > 0 ? ( +
+ +
+ ) : ( + + )} + + ); +}; diff --git a/plugins/main/public/components/management/cluster/dashboard/cluster_dashboard.scss b/plugins/main/public/components/management/cluster/dashboard/cluster_dashboard.scss new file mode 100644 index 0000000000..ccb47c4acb --- /dev/null +++ b/plugins/main/public/components/management/cluster/dashboard/cluster_dashboard.scss @@ -0,0 +1,21 @@ +.ct-dashboard-responsive { + margin: 0 -8px; + @media (max-width: 767px) { + .react-grid-layout { + height: auto !important; + } + .dshLayout-isMaximizedPanel { + height: 100% !important; + } + } +} + +.cluster-descriptionList-title { + width: 25% !important; + margin-top: 8px !important; +} + +.cluster-descriptionList-description { + width: 75% !important; + margin-top: 8px !important; +} diff --git a/plugins/main/public/components/management/cluster/dashboard/dashboard.tsx b/plugins/main/public/components/management/cluster/dashboard/dashboard.tsx new file mode 100644 index 0000000000..e2c05ce18d --- /dev/null +++ b/plugins/main/public/components/management/cluster/dashboard/dashboard.tsx @@ -0,0 +1,202 @@ +import React, { useState, useEffect } from 'react'; +import { getCore, getPlugins } from '../../../../kibana-services'; +import { SearchResponse } from '../../../../../../../src/core/server'; +import { IndexPattern } from '../../../../../../../src/plugins/data/common'; +import { I18nProvider } from '@osd/i18n/react'; +import useSearchBar from '../../../common/search-bar/use-search-bar'; +import { WAZUH_ALERTS_PATTERN } from '../../../../../common/constants'; +import { search } from '../../../common/search-bar/search-bar-service'; +import { + ErrorFactory, + ErrorHandler, + HttpError, +} from '../../../../react-services/error-management'; +import { LoadingSpinner } from '../../../overview/vulnerabilities/common/components/loading_spinner'; +import { withErrorBoundary } from '../../../common/hocs/error-boundary/with-error-boundary'; +import { EuiSpacer, EuiFlexItem } from '@elastic/eui'; + +import { OverviewCards } from '../components/overview_cards'; +import { endpointSummary } from '../../../../utils/applications'; +import { TabVisualizations } from '../../../../factories/tab-visualizations'; +import { WzRequest } from '../../../../react-services'; +import { ConfigurationCards } from '../components/configuration_cards'; +import { NodeList } from '../node-list'; + +const SearchBar = getPlugins().data.ui.SearchBar; + +interface DashboardCTProps { + tabVisualizations: TabVisualizations; + statusRunning: string; +} + +interface ClusterDashboardState { + showConfig: boolean; + showNodes: boolean; + nodeList: any; + configuration: any; + version: any; + nodesCount: number; + agentsCount: number; +} + +const DashboardCT: React.FC = ({ + tabVisualizations, + statusRunning, +}) => { + /* TODO: Analyze whether to use the new index pattern handler https://github.com/wazuh/wazuh-dashboard-plugins/issues/6434 + Replace WAZUH_ALERTS_PATTERN with appState.getCurrentPattern... */ + const CT_INDEX_PATTERN_ID = WAZUH_ALERTS_PATTERN; + + const { searchBarProps } = useSearchBar({ + defaultIndexPatternID: CT_INDEX_PATTERN_ID, + }); + + const { isLoading, query, indexPatterns } = searchBarProps; + const [isSearching, setIsSearching] = useState(false); + const [state, setState] = useState({ + showConfig: false, + showNodes: false, + nodeList: [], + configuration: undefined, + version: undefined, + nodesCount: 0, + agentsCount: 0, + }); + + const [results, setResults] = useState({} as SearchResponse); + + const setBooleans = (component: string | null) => { + setState({ + ...state, + showConfig: component === 'showConfig', + showNodes: component === 'showNodes', + }); + }; + + const goAgents = () => { + getCore().application.navigateToApp(endpointSummary.id, { + path: '#/agents-preview', + }); + }; + + const goBack = () => { + setBooleans(null); + tabVisualizations.assign({ + monitoring: 2, + }); + }; + + const goNodes = () => { + setBooleans('showNodes'); + tabVisualizations.assign({ + monitoring: 1, + }); + }; + + const goConfiguration = () => { + setBooleans('showConfig'); + tabVisualizations.assign({ + monitoring: 1, + }); + }; + + useEffect(() => { + if (!isLoading) { + search({ + indexPattern: indexPatterns?.[0] as IndexPattern, + filters: searchBarProps.filters ?? [], + query, + dateRange: { + from: searchBarProps.dateRangeFrom, + to: searchBarProps.dateRangeTo, + }, + }) + .then(results => { + setResults(results); + setIsSearching(false); + }) + .catch(error => { + const searchError = ErrorFactory.create(HttpError, { + error, + message: 'Error fetching results', + }); + ErrorHandler.handleError(searchError); + setIsSearching(false); + }); + } + }, [JSON.stringify(searchBarProps)]); + + useEffect(() => { + const getData = async () => { + const data = await Promise.all([ + WzRequest.apiReq('GET', '/cluster/nodes', {}), + WzRequest.apiReq('GET', '/cluster/local/config', {}), + WzRequest.apiReq('GET', '/', {}), + WzRequest.apiReq('GET', '/agents', { limit: 1 }), + WzRequest.apiReq('GET', '/cluster/healthcheck', {}), + ]); + + const nodeList = ((data[0] || {}).data || {}).data || {} || false; + const clusterConfig = ((data[1] || {}).data || {}).data || {} || false; + const agents = ((data[3] || {}).data || {}).data || {} || false; + setState({ + ...state, + configuration: clusterConfig.affected_items[0], + version: (((data[2] || {}).data || {}).data || {}).api_version || false, + nodesCount: nodeList.total_affected_items, + agentsCount: agents.total_affected_items - 1, + nodeList: nodeList?.affected_items ?? [], + }); + }; + + getData(); + }, []); + + return ( + + + {isLoading ? : null} + {!isLoading && !state.showNodes ? ( + + ) : null} + {isSearching ? : null} + + {!isLoading && !isSearching && !state.showConfig && !state.showNodes ? ( + + ) : null} + {state.showConfig ? ( + + ) : null} + {state.showNodes ? : null} + + + ); +}; + +export const ClusterDashboard = withErrorBoundary(DashboardCT); diff --git a/plugins/main/public/components/management/cluster/dashboard/dashboard_configuration_panels.ts b/plugins/main/public/components/management/cluster/dashboard/dashboard_configuration_panels.ts new file mode 100644 index 0000000000..89d5675c8f --- /dev/null +++ b/plugins/main/public/components/management/cluster/dashboard/dashboard_configuration_panels.ts @@ -0,0 +1,86 @@ +import { DashboardPanelState } from '../../../../../../../../src/plugins/dashboard/public/application'; +import { EmbeddableInput } from '../../../../../../../../src/plugins/embeddable/public'; + +/* WARNING: The panel id must be unique including general and agents visualizations. Otherwise, the visualizations will not refresh when we pin an agent, because they are cached by id */ + +/* Overview visualizations */ + +const getVisStateTop5Nodes = (indexPatternId: string) => { + return { + id: 'Wazuh-App-Cluster-monitoring-Overview-Node-Pie', + title: 'Top 5 nodes', + type: 'pie', + params: { + type: 'pie', + addTooltip: true, + addLegend: true, + legendPosition: 'right', + isDonut: true, + labels: { show: false, values: true, last_level: true, truncate: 100 }, + }, + uiState: { spy: { mode: { name: 'table' } } }, + data: { + searchSource: { + query: { + language: 'kuery', + query: '', + }, + filter: [], + index: indexPatternId, + }, + references: [ + { + name: 'kibanaSavedObjectMeta.searchSourceJSON.index', + type: 'index-pattern', + id: indexPatternId, + }, + ], + aggs: [ + { id: '1', enabled: true, type: 'count', schema: 'metric', params: {} }, + { + id: '2', + enabled: true, + type: 'terms', + schema: 'segment', + params: { + field: 'cluster.node', + otherBucket: false, + otherBucketLabel: 'Other', + missingBucket: false, + missingBucketLabel: 'Missing', + size: 5, + order: 'desc', + orderBy: '1', + }, + }, + ], + }, + }; +}; + +/* Definition of panels */ + +export const getDashboardConfigurationPanels = ( + indexPatternId: string, +): { + [panelId: string]: DashboardPanelState< + EmbeddableInput & { [k: string]: unknown } + >; +} => { + return { + '1': { + gridData: { + w: 48, + h: 13, + x: 0, + y: 0, + i: '1', + }, + type: 'visualization', + explicitInput: { + id: '1', + savedVis: getVisStateTop5Nodes(indexPatternId), + }, + }, + }; +}; diff --git a/plugins/main/public/components/management/cluster/dashboard/dashboard_panels.ts b/plugins/main/public/components/management/cluster/dashboard/dashboard_panels.ts new file mode 100644 index 0000000000..c4871aca44 --- /dev/null +++ b/plugins/main/public/components/management/cluster/dashboard/dashboard_panels.ts @@ -0,0 +1,122 @@ +import { DashboardPanelState } from '../../../../../../../../src/plugins/dashboard/public/application'; +import { EmbeddableInput } from '../../../../../../../../src/plugins/embeddable/public'; + +/* WARNING: The panel id must be unique including general and agents visualizations. Otherwise, the visualizations will not refresh when we pin an agent, because they are cached by id */ + +/* Overview visualizations */ + +const getVisStateClusterAlertsSummary = ( + indexPatternId: string, + clusterName?: string, +) => { + let expression = `.es(index=${indexPatternId},q="cluster.name: ${clusterName}").label("${clusterName} cluster")`; + expression = expression.replace(/'/g, '"'); + return { + id: 'Wazuh-App-Cluster-monitoring-Overview-Manager', + title: 'Cluster alerts summary', + type: 'timelion', + params: { expression, interval: 'auto' }, + data: { + searchSource: { + query: { + language: 'kuery', + query: '', + }, + filter: [], + index: indexPatternId, + }, + references: [ + { + name: 'kibanaSavedObjectMeta.searchSourceJSON.index', + type: 'index-pattern', + id: indexPatternId, + }, + ], + aggs: [], + }, + }; +}; + +const getVisStateAlertsByNodeSummary = ( + indexPatternId: string, + nodeList: any[], + clusterName?: string, +) => { + let expression = ''; + for (const node of nodeList) { + expression += `.es(index=${indexPatternId},q="cluster.name: ${clusterName} AND cluster.node: ${node.name}").label("${node.name}"),`; + } + expression = expression.substring(0, expression.length - 1); + expression = expression.replace(/'/g, '"'); + return { + id: 'Wazuh-App-Cluster-monitoring-Overview', + title: 'Alerts by node summary', + type: 'timelion', + params: { expression, interval: 'auto' }, + data: { + searchSource: { + query: { + language: 'kuery', + query: '', + }, + filter: [], + index: indexPatternId, + }, + references: [ + { + name: 'kibanaSavedObjectMeta.searchSourceJSON.index', + type: 'index-pattern', + id: indexPatternId, + }, + ], + aggs: [], + }, + }; +}; + +/* Definition of panels */ + +export const getDashboardPanels = ( + indexPatternId: string, + nodeList: any[], + clusterName?: string, +): { + [panelId: string]: DashboardPanelState< + EmbeddableInput & { [k: string]: unknown } + >; +} => { + return { + '1': { + gridData: { + w: 24, + h: 13, + x: 0, + y: 0, + i: '1', + }, + type: 'visualization', + explicitInput: { + id: '1', + savedVis: getVisStateClusterAlertsSummary(indexPatternId, clusterName), + }, + }, + '2': { + gridData: { + w: 24, + h: 13, + x: 24, + y: 0, + i: '2', + }, + type: 'visualization', + explicitInput: { + id: '2', + savedVis: getVisStateAlertsByNodeSummary( + indexPatternId, + nodeList, + clusterName, + ), + }, + }, + }; +}; diff --git a/plugins/main/public/controllers/management/components/management/cluster/cluster-overview.tsx b/plugins/main/public/controllers/management/components/management/cluster/cluster-overview.tsx new file mode 100644 index 0000000000..fc6ff0f550 --- /dev/null +++ b/plugins/main/public/controllers/management/components/management/cluster/cluster-overview.tsx @@ -0,0 +1,119 @@ +import React, { Component } from 'react'; +import { compose } from 'redux'; +import { + withErrorBoundary, + withGlobalBreadcrumb, + withReduxProvider, + withUserAuthorizationPrompt, +} from '../../../../../components/common/hocs'; +import { cluster, endpointSummary } from '../../../../../utils/applications'; +import { + AppState, + WazuhConfig, + WzRequest, +} from '../../../../../react-services'; +import { ShareAgent } from '../../../../../factories/share-agent'; +import { getCore } from '../../../../../kibana-services'; +import { TabVisualizations } from '../../../../../factories/tab-visualizations'; +import { ClusterDisabled } from '../../../../../components/management/cluster/cluster-disabled'; +import { ClusterDashboard } from '../../../../../components/management/cluster/dashboard/dashboard'; + +export const ClusterOverview = compose( + withErrorBoundary, + withReduxProvider, + withGlobalBreadcrumb([{ text: cluster.breadcrumbLabel }]), + withUserAuthorizationPrompt([ + { action: 'cluster:status', resource: '*:*:*' }, + ]), +)( + class ClusterOverview extends Component { + _isMount = false; + wazuhConfig: any; + shareAgent: any; + tabVisualizations: any; + nodeProps: any; + + constructor(props) { + super(props); + this.state = { + loading: true, + currentNode: null, + nodeSearchTerm: '', + authorized: true, + clusterEnabled: false, + isClusterRunning: false, + statusRunning: 'no', + }; + + this.tabVisualizations = new TabVisualizations(); + this.wazuhConfig = new WazuhConfig(); + this.shareAgent = new ShareAgent(); + this.nodeProps = { goBack: () => this.goBack() }; + } + + clusterStatus = async () => { + try { + const status = await WzRequest.apiReq('GET', '/cluster/status', {}); + this.setState({ + authorized: true, + }); + return status; + } catch (error) { + if (error === '3013 - Permission denied: Resource type: *:*') + this.setState({ + authorized: false, + }); + } + }; + + async componentDidMount() { + this._isMount = true; + const clusterEnabled = + AppState.getClusterInfo() && + AppState.getClusterInfo().status === 'enabled'; + /* $location.search('tabView', 'cluster-monitoring'); + $location.search('tab', 'monitoring'); + $location.search('_a', null); */ + this.tabVisualizations.removeAll(); + this.tabVisualizations.setTab('monitoring'); + this.tabVisualizations.assign({ + monitoring: 2, + }); + + const status = await this.clusterStatus(); + const statusRunning = status?.data.data.running; + this.setState({ + authorized: true, + clusterEnabled: clusterEnabled, + isClusterRunning: statusRunning === 'no' ? false : true, + statusRunning, + permissions: !status + ? [{ action: 'cluster:status', resource: '*:*:*' }] + : undefined, + loading: false, + }); + } + + componentWillUnmount() { + this._isMount = false; + } + + render() { + return ( + <> +
+ {!this.state?.clusterEnabled || !this.state?.isClusterRunning ? ( + + ) : null} +
+ {this.state?.clusterEnabled && this.state?.isClusterRunning ? ( + + ) : null} + + ); + } + }, +); diff --git a/plugins/main/public/controllers/management/components/management/management-main.js b/plugins/main/public/controllers/management/components/management/management-main.js index 3a6e5982b7..573b88c8c2 100644 --- a/plugins/main/public/controllers/management/components/management/management-main.js +++ b/plugins/main/public/controllers/management/components/management/management-main.js @@ -27,6 +27,7 @@ import { SECTION_DECODERS_SECTION, SECTION_RULES_SECTION, } from './common/constants'; +import { ClusterOverview } from './cluster/cluster-overview'; class WzManagementMain extends Component { constructor(props) { @@ -41,6 +42,7 @@ class WzManagementMain extends Component { {(section === 'groups' && ) || (section === 'status' && ) || + (section === 'monitoring' && ) || (section === 'reporting' && ) || (section === 'statistics' && ) || (section === 'logs' && ) || diff --git a/plugins/main/public/controllers/management/index.js b/plugins/main/public/controllers/management/index.js index 6ed7e0517c..77d1f35840 100644 --- a/plugins/main/public/controllers/management/index.js +++ b/plugins/main/public/controllers/management/index.js @@ -25,6 +25,5 @@ WzManagementConfiguration.displayName = 'WzManagementConfiguration'; app .controller('managementController', ManagementController) .controller('groupsPreviewController', GroupsController) - .controller('clusterController', ClusterController) .value('WzManagement', WzManagement) .value('WzManagementConfiguration', WzManagementConfiguration); diff --git a/plugins/main/public/templates/management/management.html b/plugins/main/public/templates/management/management.html index 8bfc57911f..d4bffc4390 100644 --- a/plugins/main/public/templates/management/management.html +++ b/plugins/main/public/templates/management/management.html @@ -30,515 +30,6 @@ - -
- -
- -
- - -
- -
- - -
-
- -
- - - - - - -
-
- - - - - - -
-
{{loadingStatus}}
-
-
- -
- - -
- - - -
-
- Details  - - -
-
- IP address - {{configuration.nodes[0] || '-'}} -
-
- Running - {{ status || 'no' }} -
-
- Version - {{version}} -
-
-
- - - -
-
- Information -
- -
- Nodes - {{nodesCount}} -
- -
- Agents - {{agentsCount}} -
-
-
- -
- - - -
-
-
- - There are no results for selected time range. Try another - one. -
-
-
- - - -
- -
- - - - - -
-
-
-

- - Overview -

-
-
- -
- -
- - - - - - - - Top 5 nodes - - - There are no results for selected time range. Try another - one. - -
- -
-
- Configuration -
- -
- Disabled - {{configuration.disabled}} -
-
- Hidden - {{configuration.hidden}} -
-
- Name - {{configuration.name}} -
-
- Node name - {{configuration.node_name}} -
-
- Node type - {{configuration.node_type}} -
-
- Bind address - {{configuration.bind_addr}} -
-
- IP - {{configuration.nodes[0] || '-'}} -
-
- Port - {{configuration.port}} -
-
-
-
-
- - - -
- -
- - -
- -
-
-

- - Node {{ currentNode.name }} -

-
-
- -
-
- - {{ currentNode.name }} alerts summary - - -
-
- - - -
- -
-
- Node information -
-
- IP - {{currentNode.healthCheck.info.ip}} -
-
- Version - {{currentNode.healthCheck.info.version}} -
-
- Type - {{currentNode.healthCheck.info.type}} -
-
- Name - {{currentNode.healthCheck.info.name}} -
-
- Active agents - {{ currentNode.healthCheck.info.n_active_agents }} -
-
-
- - -
-
- Last files integrity synchronization -
-
- Last sync - {{currentNode.healthCheck.status.last_sync_integrity.date_end_master}} -
-
- Duration - {{currentNode.healthCheck.status.last_sync_integrity.duration}} -
-
- Total shared files - {{currentNode.healthCheck.status.last_sync_integrity.total_files.shared}} -
-
- Total missing files - {{currentNode.healthCheck.status.last_sync_integrity.total_files.missing}} -
-
- Total extra but valid files - {{currentNode.healthCheck.status.last_sync_integrity.total_files.extra_valid}} -
-
- Total extra files - {{currentNode.healthCheck.status.last_sync_integrity.total_files.extra}} -
-
-
-
- - - -
- -
-
- Last agents information synchronization -
-
- Last sync - {{currentNode.healthCheck.status.last_sync_agentinfo.date_end_master}} -
-
- Duration - {{currentNode.healthCheck.status.last_sync_agentinfo.duration}} -
-
- Total agent info - {{currentNode.healthCheck.status.last_sync_agentinfo.total_agentinfo}} -
-
-
- - -
-
- Last agents groups synchronization -
-
- Last sync - {{currentNode.healthCheck.status.last_sync_agentgroups.date_end_master}} -
-
- Duration - {{currentNode.healthCheck.status.last_sync_agentgroups.duration}} -
-
- Total agent info - {{currentNode.healthCheck.status.last_sync_agentgroups.total_agentgroups}} -
-
-
-
- -
- -
- -
From e4cb89a2a91a022c3c4188538285fc60dbd3c37d Mon Sep 17 00:00:00 2001 From: jbiset Date: Wed, 27 Mar 2024 18:41:23 -0300 Subject: [PATCH 02/12] Fixed width of Cluster Configuration section --- .../management/cluster/components/configuration_cards.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/plugins/main/public/components/management/cluster/components/configuration_cards.tsx b/plugins/main/public/components/management/cluster/components/configuration_cards.tsx index 8d43b7bb93..44df3afd85 100644 --- a/plugins/main/public/components/management/cluster/components/configuration_cards.tsx +++ b/plugins/main/public/components/management/cluster/components/configuration_cards.tsx @@ -101,10 +101,7 @@ export const ConfigurationCards = ({ /> )} - + Date: Thu, 28 Mar 2024 09:46:53 -0300 Subject: [PATCH 03/12] Rendering conditions are adjusted and clean code --- .../components/configuration_cards.tsx | 6 +- .../cluster/components/overview_cards.tsx | 10 +--- .../cluster/dashboard/dashboard.tsx | 16 +---- .../management/cluster/node-list.tsx | 38 ++++++------ .../management/cluster/cluster-overview.tsx | 59 +++++++------------ 5 files changed, 42 insertions(+), 87 deletions(-) diff --git a/plugins/main/public/components/management/cluster/components/configuration_cards.tsx b/plugins/main/public/components/management/cluster/components/configuration_cards.tsx index 44df3afd85..2da756b9c1 100644 --- a/plugins/main/public/components/management/cluster/components/configuration_cards.tsx +++ b/plugins/main/public/components/management/cluster/components/configuration_cards.tsx @@ -35,11 +35,7 @@ export const ConfigurationCards = ({ indexPatternId, }: ConfigurationCardsProps) => { return ( - + diff --git a/plugins/main/public/components/management/cluster/components/overview_cards.tsx b/plugins/main/public/components/management/cluster/components/overview_cards.tsx index b0020fb92f..05ecfc3976 100644 --- a/plugins/main/public/components/management/cluster/components/overview_cards.tsx +++ b/plugins/main/public/components/management/cluster/components/overview_cards.tsx @@ -1,5 +1,4 @@ -import React, { useEffect, useState } from 'react'; -import { WzRequest } from '../../../../react-services'; +import React from 'react'; import { EuiFlexItem, EuiButtonEmpty, @@ -53,10 +52,7 @@ export const OverviewCards = ({ }: OverviewCardsProps) => { return ( <> - + = ({ - tabVisualizations, - statusRunning, -}) => { +const DashboardCT: React.FC = ({ statusRunning }) => { /* TODO: Analyze whether to use the new index pattern handler https://github.com/wazuh/wazuh-dashboard-plugins/issues/6434 Replace WAZUH_ALERTS_PATTERN with appState.getCurrentPattern... */ const CT_INDEX_PATTERN_ID = WAZUH_ALERTS_PATTERN; @@ -81,23 +76,14 @@ const DashboardCT: React.FC = ({ const goBack = () => { setBooleans(null); - tabVisualizations.assign({ - monitoring: 2, - }); }; const goNodes = () => { setBooleans('showNodes'); - tabVisualizations.assign({ - monitoring: 1, - }); }; const goConfiguration = () => { setBooleans('showConfig'); - tabVisualizations.assign({ - monitoring: 1, - }); }; useEffect(() => { diff --git a/plugins/main/public/components/management/cluster/node-list.tsx b/plugins/main/public/components/management/cluster/node-list.tsx index d4e37c834b..034a0932c4 100644 --- a/plugins/main/public/components/management/cluster/node-list.tsx +++ b/plugins/main/public/components/management/cluster/node-list.tsx @@ -58,27 +58,23 @@ export const NodeList = withErrorBoundary( render() { return ( - - - - - - this.props.goBack()} - /> - - - - -

Cluster nodes

-
-
-
+ + + + this.props.goBack()} + /> + + + + +

Cluster nodes

+
diff --git a/plugins/main/public/controllers/management/components/management/cluster/cluster-overview.tsx b/plugins/main/public/controllers/management/components/management/cluster/cluster-overview.tsx index fc6ff0f550..652ef6c8af 100644 --- a/plugins/main/public/controllers/management/components/management/cluster/cluster-overview.tsx +++ b/plugins/main/public/controllers/management/components/management/cluster/cluster-overview.tsx @@ -6,18 +6,19 @@ import { withReduxProvider, withUserAuthorizationPrompt, } from '../../../../../components/common/hocs'; -import { cluster, endpointSummary } from '../../../../../utils/applications'; -import { - AppState, - WazuhConfig, - WzRequest, -} from '../../../../../react-services'; -import { ShareAgent } from '../../../../../factories/share-agent'; -import { getCore } from '../../../../../kibana-services'; -import { TabVisualizations } from '../../../../../factories/tab-visualizations'; +import { cluster } from '../../../../../utils/applications'; +import { AppState, WzRequest } from '../../../../../react-services'; import { ClusterDisabled } from '../../../../../components/management/cluster/cluster-disabled'; import { ClusterDashboard } from '../../../../../components/management/cluster/dashboard/dashboard'; +interface ClusterOverviewState { + authorized: boolean; + clusterEnabled: boolean; + isClusterRunning: boolean; + statusRunning: string; + permissions: any; +} + export const ClusterOverview = compose( withErrorBoundary, withReduxProvider, @@ -26,29 +27,18 @@ export const ClusterOverview = compose( { action: 'cluster:status', resource: '*:*:*' }, ]), )( - class ClusterOverview extends Component { + class ClusterOverview extends Component { _isMount = false; - wazuhConfig: any; - shareAgent: any; - tabVisualizations: any; - nodeProps: any; constructor(props) { super(props); this.state = { - loading: true, - currentNode: null, - nodeSearchTerm: '', authorized: true, clusterEnabled: false, isClusterRunning: false, statusRunning: 'no', + permissions: undefined, }; - - this.tabVisualizations = new TabVisualizations(); - this.wazuhConfig = new WazuhConfig(); - this.shareAgent = new ShareAgent(); - this.nodeProps = { goBack: () => this.goBack() }; } clusterStatus = async () => { @@ -71,26 +61,13 @@ export const ClusterOverview = compose( const clusterEnabled = AppState.getClusterInfo() && AppState.getClusterInfo().status === 'enabled'; - /* $location.search('tabView', 'cluster-monitoring'); - $location.search('tab', 'monitoring'); - $location.search('_a', null); */ - this.tabVisualizations.removeAll(); - this.tabVisualizations.setTab('monitoring'); - this.tabVisualizations.assign({ - monitoring: 2, - }); - const status = await this.clusterStatus(); - const statusRunning = status?.data.data.running; + const status: any = await this.clusterStatus(); + const statusRunning = status?.data?.data?.running; this.setState({ - authorized: true, clusterEnabled: clusterEnabled, isClusterRunning: statusRunning === 'no' ? false : true, statusRunning, - permissions: !status - ? [{ action: 'cluster:status', resource: '*:*:*' }] - : undefined, - loading: false, }); } @@ -102,14 +79,18 @@ export const ClusterOverview = compose( return ( <>
- {!this.state?.clusterEnabled || !this.state?.isClusterRunning ? ( + {!this.state?.clusterEnabled || + !this.state?.isClusterRunning || + !this.state?.authorized ? ( ) : null}
- {this.state?.clusterEnabled && this.state?.isClusterRunning ? ( + {this.state?.clusterEnabled && + this.state?.isClusterRunning && + this.state?.authorized ? ( ) : null} From 4f346ce95cad6542d6a6f147f2ea6e66b3903354 Mon Sep 17 00:00:00 2001 From: jbiset Date: Thu, 28 Mar 2024 10:08:41 -0300 Subject: [PATCH 04/12] Deleted controller and visualization monitoring files --- .../public/controllers/management/index.js | 1 - .../controllers/management/monitoring.js | 337 ------------------ .../visualizations/cluster/index.ts | 4 +- .../visualizations/cluster/monitoring.ts | 192 ---------- 4 files changed, 1 insertion(+), 533 deletions(-) delete mode 100644 plugins/main/public/controllers/management/monitoring.js delete mode 100644 plugins/main/server/integration-files/visualizations/cluster/monitoring.ts diff --git a/plugins/main/public/controllers/management/index.js b/plugins/main/public/controllers/management/index.js index 77d1f35840..9802dd93f2 100644 --- a/plugins/main/public/controllers/management/index.js +++ b/plugins/main/public/controllers/management/index.js @@ -12,7 +12,6 @@ import { GroupsController } from './groups'; import { ManagementController } from './management'; -import { ClusterController } from './monitoring'; import WzManagement from './components/management/management-provider'; import WzManagementConfiguration from './components/management/configuration/configuration-main'; import { getAngularModule } from '../../kibana-services'; diff --git a/plugins/main/public/controllers/management/monitoring.js b/plugins/main/public/controllers/management/monitoring.js deleted file mode 100644 index 4c0f1f04a9..0000000000 --- a/plugins/main/public/controllers/management/monitoring.js +++ /dev/null @@ -1,337 +0,0 @@ -/* - * Wazuh app - Cluster monitoring controller - * Copyright (C) 2015-2022 Wazuh, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * Find more information about this on the LICENSE file. - */ -import { FilterHandler } from '../../utils/filter-handler'; -import { AppState } from '../../react-services/app-state'; -import { GenericRequest } from '../../react-services/generic-request'; -import { WzRequest } from '../../react-services/wz-request'; -import { ErrorHandler } from '../../react-services/error-handler'; -import { TabVisualizations } from '../../factories/tab-visualizations'; -import store from '../../redux/store'; -import { updateGlobalBreadcrumb } from '../../redux/actions/globalBreadcrumbActions'; -import { ModulesHelper } from '../../components/common/modules/modules-helper'; -import { getCore, getDataPlugin } from '../../kibana-services'; -import { cluster, endpointSummary } from '../../utils/applications'; - -export function ClusterController( - $scope, - $rootScope, - $timeout, - errorHandler, - $window, - $location, - discoverPendingUpdates, - rawVisualizations, - loadedVisualizations, - visHandlers, -) { - const tabVisualizations = new TabVisualizations(); - getDataPlugin().query.timefilter.timefilter.setRefreshInterval({ - pause: true, - value: 0, - }); - $scope.search = term => { - $scope.$broadcast('wazuhSearch', { term }); - }; - - const clusterEnabled = - AppState.getClusterInfo() && AppState.getClusterInfo().status === 'enabled'; - $scope.isClusterEnabled = clusterEnabled; - $scope.isClusterRunning = true; - $scope.authorized = true; - $location.search('tabView', 'cluster-monitoring'); - $location.search('tab', 'monitoring'); - $location.search('_a', null); - const filterHandler = new FilterHandler(AppState.getCurrentPattern()); - discoverPendingUpdates.removeAll(); - tabVisualizations.removeAll(); - rawVisualizations.removeAll(); - loadedVisualizations.removeAll(); - tabVisualizations.setTab('monitoring'); - tabVisualizations.assign({ - monitoring: 2, - }); - - $scope.loading = true; - $scope.showConfig = false; - $scope.showNodes = false; - $scope.currentNode = null; - $scope.nodeSearchTerm = ''; - - /** - * This set default boolean flags for a given component - * @param {String} component - */ - const setBooleans = component => { - $scope.showConfig = component === 'showConfig'; - $scope.showNodes = component === 'showNodes'; - $scope.currentNode = null; - }; - - /** - * This navigates to agents preview - */ - $scope.goAgents = () => { - getCore().application.navigateToApp(endpointSummary.id, { - path: '#/agents-preview', - }); - }; - - /** - * This navigates to configuration - */ - $scope.goConfiguration = () => { - setBooleans('showConfig'); - tabVisualizations.assign({ - monitoring: 1, - }); - assignFilters(); - $rootScope.$broadcast('updateVis'); - }; - - /** - * This navigates to nodes - */ - $scope.goNodes = () => { - setBooleans('showNodes'); - tabVisualizations.assign({ - monitoring: 1, - }); - assignFilters(); - $scope.nodeProps = { goBack: () => $scope.goBack() }; - $rootScope.$broadcast('updateVis'); - }; - - /** - * This navigates back - */ - $scope.goBack = () => { - setBooleans(null); - tabVisualizations.assign({ - monitoring: 2, - }); - assignFilters(); - $rootScope.$broadcast('updateVis'); - }; - - //listeners - $scope.$on('wazuhShowClusterNode', async (event, parameters) => { - try { - tabVisualizations.assign({ - monitoring: 1, - }); - $scope.currentNode = parameters.node; - const data = await WzRequest.apiReq('GET', '/cluster/healthcheck', { - node: $scope.currentNode.name, - }); - - $scope.currentNode.healthCheck = data.data.data.affected_items[0]; - - if ( - $scope.currentNode.healthCheck && - $scope.currentNode.healthCheck.status - ) { - $scope.currentNode.healthCheck.status.last_sync_integrity.duration = - 'n/a'; - $scope.currentNode.healthCheck.status.last_sync_agentinfo.duration = - 'n/a'; - $scope.currentNode.healthCheck.status.last_sync_agentgroups.duration = - 'n/a'; - - if ( - $scope.currentNode.healthCheck.status.last_sync_integrity - .date_start_master !== 'n/a' && - $scope.currentNode.healthCheck.status.last_sync_integrity - .date_end_master !== 'n/a' - ) { - const end = new Date( - $scope.currentNode.healthCheck.status.last_sync_integrity.date_end_master, - ); - const start = new Date( - $scope.currentNode.healthCheck.status.last_sync_integrity.date_start_master, - ); - $scope.currentNode.healthCheck.status.last_sync_integrity.duration = `${ - (end - start) / 1000 - }s`; - } - - if ( - $scope.currentNode.healthCheck.status.last_sync_agentinfo - .date_start_master !== 'n/a' && - $scope.currentNode.healthCheck.status.last_sync_agentinfo - .date_end_master !== 'n/a' - ) { - const end = new Date( - $scope.currentNode.healthCheck.status.last_sync_agentinfo.date_end_master, - ); - const start = new Date( - $scope.currentNode.healthCheck.status.last_sync_agentinfo.date_start_master, - ); - $scope.currentNode.healthCheck.status.last_sync_agentinfo.duration = `${ - (end - start) / 1000 - }s`; - } - - if ( - $scope.currentNode.healthCheck.status.last_sync_agentgroups - .date_start_master !== 'n/a' && - $scope.currentNode.healthCheck.status.last_sync_agentgroups - .date_end_master !== 'n/a' - ) { - const end = new Date( - $scope.currentNode.healthCheck.status.last_sync_agentgroups.date_end_master, - ); - const start = new Date( - $scope.currentNode.healthCheck.status.last_sync_agentgroups.date_start_master, - ); - $scope.currentNode.healthCheck.status.last_sync_agentgroups.duration = `${ - (end - start) / 1000 - }s`; - } - } - - assignFilters($scope.currentNode.name); - $rootScope.$broadcast('updateVis'); - - $scope.$applyAsync(); - } catch (error) { - ErrorHandler.handle(error, 'Cluster'); - } - }); - - let filters = []; - /** - * This creatie custom filters for visualizations for a given node - * @param {Object} node - */ - const assignFilters = async (node = false) => { - try { - filters = []; - filters.push( - filterHandler.managerQuery(AppState.getClusterInfo().cluster, true), - ); - if (node) { - filters.push(filterHandler.nodeQuery(node)); - } - const discoverScope = await ModulesHelper.getDiscoverScope(); - discoverScope.loadFilters(filters); - } catch (error) { - ErrorHandler.handle( - 'An error occurred while creating custom filters for visualizations', - 'Cluster', - { warning: true }, - ); - } - }; - - const clusterStatus = async () => { - try { - const status = await WzRequest.apiReq('GET', '/cluster/status', {}); - $scope.authorized = true; - return status; - } catch (error) { - if (error === '3013 - Permission denied: Resource type: *:*') - $scope.authorized = false; - } - }; - - /** - * This set some required settings at init - */ - const load = async () => { - try { - visHandlers.removeAll(); - discoverPendingUpdates.removeAll(); - rawVisualizations.removeAll(); - loadedVisualizations.removeAll(); - const status = await clusterStatus(); - if (!status) { - $scope.permissions = [{ action: 'cluster:status', resource: '*:*:*' }]; - $scope.loading = false; - $scope.$applyAsync(); - return; - } - $scope.status = status.data.data.running; - if ($scope.status === 'no') { - $scope.isClusterRunning = false; - $scope.loading = false; - return; - } - - const data = await Promise.all([ - WzRequest.apiReq('GET', '/cluster/nodes', {}), - WzRequest.apiReq('GET', '/cluster/local/config', {}), - WzRequest.apiReq('GET', '/', {}), - WzRequest.apiReq('GET', '/agents', { limit: 1 }), - WzRequest.apiReq('GET', '/cluster/healthcheck', {}), - ]); - - const nodeList = ((data[0] || {}).data || {}).data || {} || false; - const clusterConfig = ((data[1] || {}).data || {}).data || {} || false; - const version = - (((data[2] || {}).data || {}).data || {}).api_version || false; - const agents = ((data[3] || {}).data || {}).data || {} || false; - - $scope.nodesCount = nodeList.total_affected_items; - $scope.configuration = clusterConfig.affected_items[0]; - $scope.version = version; - $scope.agentsCount = agents.total_affected_items - 1; - - nodeList.name = $scope.configuration.name; - nodeList.master_node = $scope.configuration.node_name; - const { id, title } = await getDataPlugin().indexPatterns.get( - AppState.getCurrentPattern(), - ); - - const visData = await GenericRequest.request( - 'POST', - `/elastic/visualizations/cluster-monitoring/${AppState.getCurrentPattern()}`, - { nodes: nodeList, pattern: { id, title } }, - ); - - rawVisualizations.assignItems(visData.data.raw); - assignFilters(); - $rootScope.$broadcast('updateVis'); - - $scope.loading = false; - $scope.$applyAsync(); - return; - } catch (error) { - $scope.loading = false; - ErrorHandler.handle(error, 'Cluster'); - } - }; - - $scope.falseAllExpand = () => { - $scope.expandArray = [false, false]; - }; - - $scope.expand = i => { - const oldValue = $scope.expandArray[i]; - $scope.falseAllExpand(); - $scope.expandArray[i] = !oldValue; - }; - - $scope.expandArray = [false, false]; - - const breadcrumb = [{ text: cluster.breadcrumbLabel }]; - store.dispatch(updateGlobalBreadcrumb(breadcrumb)); - if (clusterEnabled) load(); - - //listeners - $scope.$on('$destroy', () => { - discoverPendingUpdates.removeAll(); - tabVisualizations.removeAll(); - rawVisualizations.removeAll(); - loadedVisualizations.removeAll(); - visHandlers.removeAll(); - }); -} diff --git a/plugins/main/server/integration-files/visualizations/cluster/index.ts b/plugins/main/server/integration-files/visualizations/cluster/index.ts index 92c5a937bf..de48bede22 100644 --- a/plugins/main/server/integration-files/visualizations/cluster/index.ts +++ b/plugins/main/server/integration-files/visualizations/cluster/index.ts @@ -9,8 +9,6 @@ * * Find more information about this on the LICENSE file. */ -import monitoring from './monitoring'; import statistics from './statistics'; -export { monitoring}; -export { statistics } +export { statistics }; diff --git a/plugins/main/server/integration-files/visualizations/cluster/monitoring.ts b/plugins/main/server/integration-files/visualizations/cluster/monitoring.ts deleted file mode 100644 index 6b59c0178f..0000000000 --- a/plugins/main/server/integration-files/visualizations/cluster/monitoring.ts +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Wazuh app - Cluster monitoring visualizations - * Copyright (C) 2015-2022 Wazuh, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * Find more information about this on the LICENSE file. - */ -export default [ - { - _id: 'Wazuh-App-Cluster-monitoring-Overview', - _type: 'visualization', - _source: { - title: 'Wazuh App Cluster Overview', - visState: JSON.stringify({ - title: 'Wazuh App Cluster Overview', - type: 'timelion', - params: { expression: '.es(*)', interval: 'auto' }, - aggs: [], - }), - uiStateJSON: '{}', - description: '', - version: 1, - kibanaSavedObjectMeta: { - searchSourceJSON: JSON.stringify({ - index: 'wazuh-alerts', - filter: [], - query: { query: '', language: 'lucene' }, - }), - }, - }, - }, - { - _id: 'Wazuh-App-Cluster-monitoring-Overview-Manager', - _type: 'visualization', - _source: { - title: 'Wazuh App Cluster Overview Manager', - visState: JSON.stringify({ - title: 'Wazuh App Cluster Overview Manager', - type: 'timelion', - params: { expression: '.es(q=agent.id:000)', interval: 'auto' }, - aggs: [], - }), - uiStateJSON: '{}', - description: '', - version: 1, - kibanaSavedObjectMeta: { - searchSourceJSON: JSON.stringify({ - index: 'wazuh-alerts', - filter: [], - query: { query: '', language: 'lucene' }, - }), - }, - }, - }, - { - _id: 'Wazuh-App-Cluster-monitoring-Overview-Node', - _source: { - title: 'Wazuh App Cluster Overview Node', - visState: JSON.stringify({ - title: 'Wazuh App Cluster Overview Node', - type: 'histogram', - params: { - type: 'histogram', - grid: { categoryLines: false, style: { color: '#eee' } }, - categoryAxes: [ - { - id: 'CategoryAxis-1', - type: 'category', - position: 'bottom', - show: true, - style: {}, - scale: { type: 'linear' }, - labels: { show: true, filter: true, truncate: 100 }, - title: {}, - }, - ], - valueAxes: [ - { - id: 'ValueAxis-1', - name: 'LeftAxis-1', - type: 'value', - position: 'left', - show: true, - style: {}, - scale: { type: 'linear', mode: 'normal' }, - labels: { show: true, rotate: 0, filter: false, truncate: 100 }, - title: { text: 'Count' }, - }, - ], - seriesParams: [ - { - show: 'true', - type: 'histogram', - mode: 'stacked', - data: { label: 'Count', id: '1' }, - valueAxis: 'ValueAxis-1', - drawLinesBetweenPoints: true, - showCircles: true, - }, - ], - addTooltip: true, - addLegend: false, - legendPosition: 'right', - times: [], - addTimeMarker: false, - }, - aggs: [ - { id: '1', enabled: true, type: 'count', schema: 'metric', params: {} }, - { - id: '2', - enabled: true, - type: 'date_histogram', - schema: 'segment', - params: { - field: 'timestamp', - interval: 'auto', - customInterval: '2h', - min_doc_count: 1, - extended_bounds: {}, - }, - }, - ], - }), - uiStateJSON: JSON.stringify({ - spy: { mode: { name: null, fill: false } }, - vis: { legendOpen: false }, - }), - description: '', - version: 1, - kibanaSavedObjectMeta: { - searchSourceJSON: JSON.stringify({ - index: 'wazuh-alerts', - filter: [], - query: { query: '', language: 'lucene' }, - }), - }, - }, - _type: 'visualization', - }, - { - _id: 'Wazuh-App-Cluster-monitoring-Overview-Node-Pie', - _type: 'visualization', - _source: { - title: 'Wazuh App Cluster Overview Node Pie', - visState: JSON.stringify({ - title: 'Wazuh App Cluster Overview Node Pie', - type: 'pie', - params: { - type: 'pie', - addTooltip: true, - addLegend: true, - legendPosition: 'right', - isDonut: true, - labels: { show: false, values: true, last_level: true, truncate: 100 }, - }, - aggs: [ - { id: '1', enabled: true, type: 'count', schema: 'metric', params: {} }, - { - id: '2', - enabled: true, - type: 'terms', - schema: 'segment', - params: { - field: 'cluster.node', - otherBucket: false, - otherBucketLabel: 'Other', - missingBucket: false, - missingBucketLabel: 'Missing', - size: 5, - order: 'desc', - orderBy: '1', - }, - }, - ], - }), - uiStateJSON: JSON.stringify({ spy: { mode: { name: 'table' } } }), - description: '', - version: 1, - kibanaSavedObjectMeta: { - searchSourceJSON: JSON.stringify({ - index: 'wazuh-alerts', - filter: [], - query: { query: '', language: 'lucene' }, - }), - }, - }, - }, -]; From e1b4690a2e6700c0bc37a98c587556f176ce408b Mon Sep 17 00:00:00 2001 From: jbiset Date: Mon, 8 Apr 2024 17:09:02 -0300 Subject: [PATCH 05/12] Integrated data-source implementation --- plugins/main/public/components/index.js | 3 - .../management/cluster/cluster-disabled.js | 89 ++++++------- .../components/configuration_cards.tsx | 9 +- .../cluster/components/no_results.tsx | 60 --------- .../cluster/components/overview_cards.tsx | 7 +- .../cluster/dashboard/dashboard.tsx | 120 ++++++++++-------- .../dashboard_configuration_panels.ts | 4 +- 7 files changed, 128 insertions(+), 164 deletions(-) delete mode 100644 plugins/main/public/components/management/cluster/components/no_results.tsx diff --git a/plugins/main/public/components/index.js b/plugins/main/public/components/index.js index a2af577ff3..38527e0751 100644 --- a/plugins/main/public/components/index.js +++ b/plugins/main/public/components/index.js @@ -15,13 +15,10 @@ import { WzVisualize } from './visualize/wz-visualize'; import { WzMenuWrapper } from '../components/wz-menu/wz-menu-wrapper'; import { WzAgentSelectorWrapper } from '../components/wz-agent-selector/wz-agent-selector-wrapper'; import { WzBlankScreen } from '../components/wz-blank-screen/wz-blank-screen'; -import { ClusterDisabled } from '../components/management/cluster/cluster-disabled'; -import { ClusterTimelions } from '../components/management/cluster/cluster-timelions'; import { KibanaVisWrapper } from '../components/management/cluster/cluster-visualization'; import { ToastNotificationsModal } from '../components/notifications/modal'; import { getAngularModule } from '../kibana-services'; import { WzUpdatesNotification } from './wz-updates-notification'; -import { ClusterOverview } from '../controllers/management/components/management/cluster/cluster-overview'; const app = getAngularModule(); diff --git a/plugins/main/public/components/management/cluster/cluster-disabled.js b/plugins/main/public/components/management/cluster/cluster-disabled.js index 396814e9b1..131805639d 100644 --- a/plugins/main/public/components/management/cluster/cluster-disabled.js +++ b/plugins/main/public/components/management/cluster/cluster-disabled.js @@ -2,47 +2,50 @@ import React, { Component, Fragment } from 'react'; import { EuiLink, EuiEmptyPrompt } from '@elastic/eui'; import { withErrorBoundary } from '../../common/hocs'; import { webDocumentationLink } from '../../../../common/services/web_documentation'; -export const ClusterDisabled = withErrorBoundary (class ClusterDisabled extends Component { - constructor(props) { - super(props); - this.state = {}; - } +export const ClusterDisabled = withErrorBoundary( + class ClusterDisabled extends Component { + constructor(props) { + super(props); + this.state = {}; + } - render() { - return ( - - {!this.props.enabled - ? 'The cluster is disabled' - : !this.props.running - ? 'The cluster is not running' - : ''} - - } - body={ - - {!this.props.enabled && ( -

- Visit the documentation on{' '} - - this link - {' '} - to learn about how to enable it. -

- )} - {!this.props.running && ( -

The cluster is enabled but it is not running.

- )} -
- } - /> - ); - } -}) + render() { + return ( + + {!this.props.enabled + ? 'The cluster is disabled' + : !this.props.running + ? 'The cluster is not running' + : ''} + + } + body={ + + {!this.props.enabled ? ( +

+ Visit the documentation on{' '} + + this link + {' '} + to learn about how to enable it. +

+ ) : !this.props.running ? ( +

The cluster is enabled but it is not running.

+ ) : null} +
+ } + /> + ); + } + }, +); diff --git a/plugins/main/public/components/management/cluster/components/configuration_cards.tsx b/plugins/main/public/components/management/cluster/components/configuration_cards.tsx index 2da756b9c1..ffa4409594 100644 --- a/plugins/main/public/components/management/cluster/components/configuration_cards.tsx +++ b/plugins/main/public/components/management/cluster/components/configuration_cards.tsx @@ -11,16 +11,18 @@ import { } from '@elastic/eui'; import { ViewMode } from '../../../../../../../src/plugins/embeddable/public'; import { getPlugins } from '../../../../kibana-services'; -import { DiscoverNoResults } from './no_results'; +import { DiscoverNoResults } from '../../../common/no-results/no-results'; import { getDashboardConfigurationPanels } from '../dashboard/dashboard_configuration_panels'; import '../dashboard/cluster_dashboard.scss'; +import { tFilter } from '../../../common/data-source'; interface ConfigurationCardsProps { goBack: () => void; configuration: any; searchBarProps: any; results: any; - indexPatternId: string; + indexPatternId?: string; + filters: tFilter[]; } const plugins = getPlugins(); @@ -33,6 +35,7 @@ export const ConfigurationCards = ({ searchBarProps, results, indexPatternId, + filters, }: ConfigurationCardsProps) => { return ( @@ -73,7 +76,7 @@ export const ConfigurationCards = ({ viewMode: ViewMode.VIEW, panels: getDashboardConfigurationPanels(indexPatternId), isFullScreenMode: false, - filters: searchBarProps.filters ?? [], + filters: filters, useMargins: true, id: 'ct-dashboard-configuration-tab', timeRange: { diff --git a/plugins/main/public/components/management/cluster/components/no_results.tsx b/plugins/main/public/components/management/cluster/components/no_results.tsx deleted file mode 100644 index babfd51d32..0000000000 --- a/plugins/main/public/components/management/cluster/components/no_results.tsx +++ /dev/null @@ -1,60 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { FormattedMessage, I18nProvider } from '@osd/i18n/react'; - -import { EuiCallOut, EuiPanel } from '@elastic/eui'; - -interface Props { - message?: string; -} - -export const DiscoverNoResults = ({ message }: Props) => { - return ( - - - - ) - } - color='warning' - iconType='help' - data-test-subj='discoverNoResults' - /> - - - ); -}; diff --git a/plugins/main/public/components/management/cluster/components/overview_cards.tsx b/plugins/main/public/components/management/cluster/components/overview_cards.tsx index 05ecfc3976..9e8874add2 100644 --- a/plugins/main/public/components/management/cluster/components/overview_cards.tsx +++ b/plugins/main/public/components/management/cluster/components/overview_cards.tsx @@ -13,7 +13,8 @@ import { getDashboardPanels } from '../dashboard/dashboard_panels'; import { ViewMode } from '../../../../../../../src/plugins/embeddable/public'; import '../dashboard/cluster_dashboard.scss'; import { getPlugins } from '../../../../kibana-services'; -import { DiscoverNoResults } from './no_results'; +import { DiscoverNoResults } from '../../../common/no-results/no-results'; +import { tFilter } from '../../../common/data-source'; interface OverviewCardsProps { goAgents: () => void; @@ -29,6 +30,7 @@ interface OverviewCardsProps { results: any; indexPatternId: string; clusterName?: string; + filters: tFilter[]; } const plugins = getPlugins(); @@ -49,6 +51,7 @@ export const OverviewCards = ({ results, indexPatternId, clusterName, + filters, }: OverviewCardsProps) => { return ( <> @@ -205,7 +208,7 @@ export const OverviewCards = ({ viewMode: ViewMode.VIEW, panels: getDashboardPanels(indexPatternId, nodeList, clusterName), isFullScreenMode: false, - filters: searchBarProps.filters ?? [], + filters: filters, useMargins: true, id: 'ct-dashboard-tab', timeRange: { diff --git a/plugins/main/public/components/management/cluster/dashboard/dashboard.tsx b/plugins/main/public/components/management/cluster/dashboard/dashboard.tsx index 93edcb9946..deff6a3e74 100644 --- a/plugins/main/public/components/management/cluster/dashboard/dashboard.tsx +++ b/plugins/main/public/components/management/cluster/dashboard/dashboard.tsx @@ -4,14 +4,12 @@ import { SearchResponse } from '../../../../../../../src/core/server'; import { IndexPattern } from '../../../../../../../src/plugins/data/common'; import { I18nProvider } from '@osd/i18n/react'; import useSearchBar from '../../../common/search-bar/use-search-bar'; -import { WAZUH_ALERTS_PATTERN } from '../../../../../common/constants'; -import { search } from '../../../common/search-bar/search-bar-service'; import { ErrorFactory, ErrorHandler, HttpError, } from '../../../../react-services/error-management'; -import { LoadingSpinner } from '../../../overview/vulnerabilities/common/components/loading_spinner'; +import { LoadingSpinner } from '../../../common/loading-spinner/loading-spinner'; import { withErrorBoundary } from '../../../common/hocs/error-boundary/with-error-boundary'; import { EuiSpacer, EuiFlexItem } from '@elastic/eui'; @@ -20,6 +18,13 @@ import { endpointSummary } from '../../../../utils/applications'; import { WzRequest } from '../../../../react-services'; import { ConfigurationCards } from '../components/configuration_cards'; import { NodeList } from '../node-list'; +import { + AlertsDataSource, + AlertsDataSourceRepository, + PatternDataSource, + tParsedIndexPattern, + useDataSource, +} from '../../../common/data-source'; const SearchBar = getPlugins().data.ui.SearchBar; @@ -38,16 +43,18 @@ interface ClusterDashboardState { } const DashboardCT: React.FC = ({ statusRunning }) => { - /* TODO: Analyze whether to use the new index pattern handler https://github.com/wazuh/wazuh-dashboard-plugins/issues/6434 - Replace WAZUH_ALERTS_PATTERN with appState.getCurrentPattern... */ - const CT_INDEX_PATTERN_ID = WAZUH_ALERTS_PATTERN; - - const { searchBarProps } = useSearchBar({ - defaultIndexPatternID: CT_INDEX_PATTERN_ID, + const { + filters, + dataSource, + fetchFilters, + isLoading: isDataSourceLoading, + fetchData, + setFilters, + } = useDataSource({ + DataSource: AlertsDataSource, + repository: new AlertsDataSourceRepository(), }); - const { isLoading, query, indexPatterns } = searchBarProps; - const [isSearching, setIsSearching] = useState(false); const [state, setState] = useState({ showConfig: false, showNodes: false, @@ -60,6 +67,36 @@ const DashboardCT: React.FC = ({ statusRunning }) => { const [results, setResults] = useState({} as SearchResponse); + const { searchBarProps } = useSearchBar({ + indexPattern: dataSource?.indexPattern as IndexPattern, + filters, + setFilters, + }); + const { query, dateRangeFrom, dateRangeTo } = searchBarProps; + + useEffect(() => { + if (isDataSourceLoading) { + return; + } + fetchData({ + query, + dateRange: { + from: dateRangeFrom, + to: dateRangeTo, + }, + }) + .then(results => { + setResults(results); + }) + .catch(error => { + const searchError = ErrorFactory.create(HttpError, { + error, + message: 'Error fetching vulnerabilities', + }); + ErrorHandler.handleError(searchError); + }); + }, [JSON.stringify(fetchFilters), JSON.stringify(query)]); + const setBooleans = (component: string | null) => { setState({ ...state, @@ -86,32 +123,6 @@ const DashboardCT: React.FC = ({ statusRunning }) => { setBooleans('showConfig'); }; - useEffect(() => { - if (!isLoading) { - search({ - indexPattern: indexPatterns?.[0] as IndexPattern, - filters: searchBarProps.filters ?? [], - query, - dateRange: { - from: searchBarProps.dateRangeFrom, - to: searchBarProps.dateRangeTo, - }, - }) - .then(results => { - setResults(results); - setIsSearching(false); - }) - .catch(error => { - const searchError = ErrorFactory.create(HttpError, { - error, - message: 'Error fetching results', - }); - ErrorHandler.handleError(searchError); - setIsSearching(false); - }); - } - }, [JSON.stringify(searchBarProps)]); - useEffect(() => { const getData = async () => { const data = await Promise.all([ @@ -141,19 +152,24 @@ const DashboardCT: React.FC = ({ statusRunning }) => { return ( - {isLoading ? : null} - {!isLoading && !state.showNodes ? ( - - ) : null} - {isSearching ? : null} + {isDataSourceLoading && !dataSource ? ( + + ) : ( +
+ +
+ )} - {!isLoading && !isSearching && !state.showConfig && !state.showNodes ? ( + {!isDataSourceLoading && + dataSource && + !state.showConfig && + !state.showNodes ? ( = ({ statusRunning }) => { agentsCount={state?.agentsCount} searchBarProps={searchBarProps} results={results} - indexPatternId={CT_INDEX_PATTERN_ID} + indexPatternId={dataSource?.id} + filters={fetchFilters ?? []} /> ) : null} {state.showConfig ? ( @@ -176,7 +193,8 @@ const DashboardCT: React.FC = ({ statusRunning }) => { configuration={state?.configuration} searchBarProps={searchBarProps} results={results} - indexPatternId={CT_INDEX_PATTERN_ID} + indexPatternId={dataSource?.id} + filters={fetchFilters ?? []} /> ) : null} {state.showNodes ? : null} diff --git a/plugins/main/public/components/management/cluster/dashboard/dashboard_configuration_panels.ts b/plugins/main/public/components/management/cluster/dashboard/dashboard_configuration_panels.ts index 89d5675c8f..81459af1cb 100644 --- a/plugins/main/public/components/management/cluster/dashboard/dashboard_configuration_panels.ts +++ b/plugins/main/public/components/management/cluster/dashboard/dashboard_configuration_panels.ts @@ -5,7 +5,7 @@ import { EmbeddableInput } from '../../../../../../../../src/plugins/embeddable/ /* Overview visualizations */ -const getVisStateTop5Nodes = (indexPatternId: string) => { +const getVisStateTop5Nodes = (indexPatternId?: string) => { return { id: 'Wazuh-App-Cluster-monitoring-Overview-Node-Pie', title: 'Top 5 nodes', @@ -61,7 +61,7 @@ const getVisStateTop5Nodes = (indexPatternId: string) => { /* Definition of panels */ export const getDashboardConfigurationPanels = ( - indexPatternId: string, + indexPatternId?: string, ): { [panelId: string]: DashboardPanelState< EmbeddableInput & { [k: string]: unknown } From 8a40b5af555cf9a1868f6f9d0c186588c922f0ca Mon Sep 17 00:00:00 2001 From: jbiset Date: Tue, 16 Apr 2024 17:02:13 -0300 Subject: [PATCH 06/12] Fixed configuration_cards itemsList, the code is improved and unnecessary code is removed --- .../components/configuration_cards.tsx | 109 +++++++----------- .../cluster/dashboard/dashboard.tsx | 12 +- .../management/cluster/cluster-overview.tsx | 29 +---- 3 files changed, 52 insertions(+), 98 deletions(-) diff --git a/plugins/main/public/components/management/cluster/components/configuration_cards.tsx b/plugins/main/public/components/management/cluster/components/configuration_cards.tsx index ffa4409594..d76fd15081 100644 --- a/plugins/main/public/components/management/cluster/components/configuration_cards.tsx +++ b/plugins/main/public/components/management/cluster/components/configuration_cards.tsx @@ -37,6 +37,41 @@ export const ConfigurationCards = ({ indexPatternId, filters, }: ConfigurationCardsProps) => { + const configurationItemsList = [ + { + title: 'Disabled', + description: String(configuration?.disabled), + }, + { + title: 'Hidden', + description: String(configuration?.hidden), + }, + { + title: 'Name', + description: configuration?.name, + }, + { + title: 'Node name', + description: configuration?.node_name, + }, + { + title: 'Node type', + description: configuration?.node_type, + }, + { + title: 'Bind address', + description: configuration?.bind_addr, + }, + { + title: 'IP', + description: configuration?.nodes[0] || '-', + }, + { + title: 'Port', + description: configuration?.port, + }, + ]; + return ( @@ -114,72 +149,14 @@ export const ConfigurationCards = ({ type='column' compressed={true} align='left' - listItems={[ - { - title: ( - - Disabled - - ), - description: configuration?.disabled ? 'true' : 'false', - }, - { - title: ( - - Hidden - - ), - description: configuration?.hidden ? 'true' : 'false', - }, - { - title: ( - - Name - - ), - description: configuration?.name, - }, - { - title: ( - - Node name - - ), - description: configuration?.node_name, - }, - { - title: ( - - Node type - - ), - description: configuration?.node_type, - }, - { - title: ( - - Bind address - - ), - description: configuration?.bind_addr, - }, - { - title: ( - - IP - - ), - description: configuration?.nodes[0] || '-', - }, - { - title: ( - - Port - - ), - description: configuration?.port, - }, - ]} + listItems={configurationItemsList.map(item => ({ + title: ( + + {item.title} + + ), + description: item.description, + }))} titleProps={{ className: 'cluster-descriptionList-title', }} diff --git a/plugins/main/public/components/management/cluster/dashboard/dashboard.tsx b/plugins/main/public/components/management/cluster/dashboard/dashboard.tsx index deff6a3e74..a4c3830851 100644 --- a/plugins/main/public/components/management/cluster/dashboard/dashboard.tsx +++ b/plugins/main/public/components/management/cluster/dashboard/dashboard.tsx @@ -133,13 +133,13 @@ const DashboardCT: React.FC = ({ statusRunning }) => { WzRequest.apiReq('GET', '/cluster/healthcheck', {}), ]); - const nodeList = ((data[0] || {}).data || {}).data || {} || false; - const clusterConfig = ((data[1] || {}).data || {}).data || {} || false; - const agents = ((data[3] || {}).data || {}).data || {} || false; + const nodeList = data[0]?.data?.data || {} || false; + const clusterConfig = data[1]?.data?.data || {} || false; + const agents = data[3]?.data?.data || {} || false; setState({ ...state, configuration: clusterConfig.affected_items[0], - version: (((data[2] || {}).data || {}).data || {}).api_version || false, + version: data[2]?.data?.data?.api_version || false, nodesCount: nodeList.total_affected_items, agentsCount: agents.total_affected_items - 1, nodeList: nodeList?.affected_items ?? [], @@ -154,7 +154,7 @@ const DashboardCT: React.FC = ({ statusRunning }) => { {isDataSourceLoading && !dataSource ? ( - ) : ( + ) : !state.showNodes ? (
= ({ statusRunning }) => { showQueryBar={true} />
- )} + ) : null} {!isDataSourceLoading && dataSource && diff --git a/plugins/main/public/controllers/management/components/management/cluster/cluster-overview.tsx b/plugins/main/public/controllers/management/components/management/cluster/cluster-overview.tsx index 652ef6c8af..0785b5051e 100644 --- a/plugins/main/public/controllers/management/components/management/cluster/cluster-overview.tsx +++ b/plugins/main/public/controllers/management/components/management/cluster/cluster-overview.tsx @@ -12,11 +12,9 @@ import { ClusterDisabled } from '../../../../../components/management/cluster/cl import { ClusterDashboard } from '../../../../../components/management/cluster/dashboard/dashboard'; interface ClusterOverviewState { - authorized: boolean; clusterEnabled: boolean; isClusterRunning: boolean; statusRunning: string; - permissions: any; } export const ClusterOverview = compose( @@ -33,36 +31,19 @@ export const ClusterOverview = compose( constructor(props) { super(props); this.state = { - authorized: true, clusterEnabled: false, isClusterRunning: false, statusRunning: 'no', - permissions: undefined, }; } - clusterStatus = async () => { - try { - const status = await WzRequest.apiReq('GET', '/cluster/status', {}); - this.setState({ - authorized: true, - }); - return status; - } catch (error) { - if (error === '3013 - Permission denied: Resource type: *:*') - this.setState({ - authorized: false, - }); - } - }; - async componentDidMount() { this._isMount = true; const clusterEnabled = AppState.getClusterInfo() && AppState.getClusterInfo().status === 'enabled'; - const status: any = await this.clusterStatus(); + const status: any = await WzRequest.apiReq('GET', '/cluster/status', {}); const statusRunning = status?.data?.data?.running; this.setState({ clusterEnabled: clusterEnabled, @@ -79,18 +60,14 @@ export const ClusterOverview = compose( return ( <>
- {!this.state?.clusterEnabled || - !this.state?.isClusterRunning || - !this.state?.authorized ? ( + {!this.state?.clusterEnabled || !this.state?.isClusterRunning ? ( ) : null}
- {this.state?.clusterEnabled && - this.state?.isClusterRunning && - this.state?.authorized ? ( + {this.state?.clusterEnabled && this.state?.isClusterRunning ? ( ) : null} From 6825819a034d9f6d95e3a6904aa2430f3b16fe68 Mon Sep 17 00:00:00 2001 From: jbiset Date: Wed, 17 Apr 2024 11:14:01 -0300 Subject: [PATCH 07/12] Added HOC withGuardAsync --- .../loading-spinner/loading-spinner.tsx | 33 +++++++++-- .../management/cluster/cluster-overview.tsx | 59 +++++++++++++++---- 2 files changed, 75 insertions(+), 17 deletions(-) diff --git a/plugins/main/public/components/common/loading-spinner/loading-spinner.tsx b/plugins/main/public/components/common/loading-spinner/loading-spinner.tsx index 5f1d8567fb..7a9ee0d576 100644 --- a/plugins/main/public/components/common/loading-spinner/loading-spinner.tsx +++ b/plugins/main/public/components/common/loading-spinner/loading-spinner.tsx @@ -1,17 +1,38 @@ import './loading-spinner.scss'; import React from 'react'; -import { EuiTitle, EuiPanel, EuiEmptyPrompt, EuiLoadingSpinner } from '@elastic/eui'; +import { + EuiTitle, + EuiPanel, + EuiEmptyPrompt, + EuiLoadingSpinner, +} from '@elastic/eui'; import { FormattedMessage } from '@osd/i18n/react'; -export function LoadingSpinner() { +interface LoadingSpinner { + message?: React.ReactNode; +} + +export function LoadingSpinner({ message }: LoadingSpinner) { return ( - + } + icon={} title={ - +

- + {message ? ( + message + ) : ( + + )}

} diff --git a/plugins/main/public/controllers/management/components/management/cluster/cluster-overview.tsx b/plugins/main/public/controllers/management/components/management/cluster/cluster-overview.tsx index 0785b5051e..b8cebd3baa 100644 --- a/plugins/main/public/controllers/management/components/management/cluster/cluster-overview.tsx +++ b/plugins/main/public/controllers/management/components/management/cluster/cluster-overview.tsx @@ -3,6 +3,7 @@ import { compose } from 'redux'; import { withErrorBoundary, withGlobalBreadcrumb, + withGuardAsync, withReduxProvider, withUserAuthorizationPrompt, } from '../../../../../components/common/hocs'; @@ -10,6 +11,8 @@ import { cluster } from '../../../../../utils/applications'; import { AppState, WzRequest } from '../../../../../react-services'; import { ClusterDisabled } from '../../../../../components/management/cluster/cluster-disabled'; import { ClusterDashboard } from '../../../../../components/management/cluster/dashboard/dashboard'; +import { LoadingSpinner } from '../../../../../components/common/loading-spinner/loading-spinner'; +import { FormattedMessage } from '@osd/i18n/react'; interface ClusterOverviewState { clusterEnabled: boolean; @@ -17,6 +20,32 @@ interface ClusterOverviewState { statusRunning: string; } +const checkClusterIsEnabledAndRunning = async () => { + try { + const clusterEnabled = + AppState.getClusterInfo() && + AppState.getClusterInfo().status === 'enabled'; + const status: any = await WzRequest.apiReq('GET', '/cluster/status', {}); + const statusRunning = status?.data?.data?.running; + const isClusterRunning = statusRunning === 'yes'; + return { + ok: !Boolean(clusterEnabled && statusRunning), + data: { + clusterEnabled, + isClusterRunning, + statusRunning, + }, + }; + } catch (error) { + return { + ok: true, + data: { + error, + }, + }; + } +}; + export const ClusterOverview = compose( withErrorBoundary, withReduxProvider, @@ -24,8 +53,24 @@ export const ClusterOverview = compose( withUserAuthorizationPrompt([ { action: 'cluster:status', resource: '*:*:*' }, ]), + withGuardAsync( + checkClusterIsEnabledAndRunning, + ({ clusterEnabled, isClusterRunning }) => ( + + ), + () => ( + + } + /> + ), + ), )( - class ClusterOverview extends Component { + class ClusterOverview extends Component { _isMount = false; constructor(props) { @@ -59,16 +104,8 @@ export const ClusterOverview = compose( render() { return ( <> -
- {!this.state?.clusterEnabled || !this.state?.isClusterRunning ? ( - - ) : null} -
- {this.state?.clusterEnabled && this.state?.isClusterRunning ? ( - + {this.props?.clusterEnabled && this.props?.isClusterRunning ? ( + ) : null} ); From 692f910894399f4e09df149c2885bc1c6b3202a2 Mon Sep 17 00:00:00 2001 From: jbiset Date: Wed, 17 Apr 2024 11:31:48 -0300 Subject: [PATCH 08/12] Changed the source of clusterEnabled information to that provided by the API --- .../components/management/cluster/cluster-overview.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/plugins/main/public/controllers/management/components/management/cluster/cluster-overview.tsx b/plugins/main/public/controllers/management/components/management/cluster/cluster-overview.tsx index b8cebd3baa..5ec5785d27 100644 --- a/plugins/main/public/controllers/management/components/management/cluster/cluster-overview.tsx +++ b/plugins/main/public/controllers/management/components/management/cluster/cluster-overview.tsx @@ -22,10 +22,8 @@ interface ClusterOverviewState { const checkClusterIsEnabledAndRunning = async () => { try { - const clusterEnabled = - AppState.getClusterInfo() && - AppState.getClusterInfo().status === 'enabled'; const status: any = await WzRequest.apiReq('GET', '/cluster/status', {}); + const clusterEnabled = status?.data?.data?.enabled; const statusRunning = status?.data?.data?.running; const isClusterRunning = statusRunning === 'yes'; return { From d31d0d2b18f06918f715ca93d3a9cb8ba6deb8b3 Mon Sep 17 00:00:00 2001 From: jbiset Date: Wed, 17 Apr 2024 12:25:01 -0300 Subject: [PATCH 09/12] Changed ClusterOverview component class to functional and fixed error on checkClusterIsEnabledAndRunning --- .../management/cluster/cluster-overview.tsx | 58 +++++-------------- 1 file changed, 15 insertions(+), 43 deletions(-) diff --git a/plugins/main/public/controllers/management/components/management/cluster/cluster-overview.tsx b/plugins/main/public/controllers/management/components/management/cluster/cluster-overview.tsx index 5ec5785d27..8f7d845067 100644 --- a/plugins/main/public/controllers/management/components/management/cluster/cluster-overview.tsx +++ b/plugins/main/public/controllers/management/components/management/cluster/cluster-overview.tsx @@ -1,4 +1,4 @@ -import React, { Component } from 'react'; +import React from 'react'; import { compose } from 'redux'; import { withErrorBoundary, @@ -8,7 +8,7 @@ import { withUserAuthorizationPrompt, } from '../../../../../components/common/hocs'; import { cluster } from '../../../../../utils/applications'; -import { AppState, WzRequest } from '../../../../../react-services'; +import { WzRequest } from '../../../../../react-services'; import { ClusterDisabled } from '../../../../../components/management/cluster/cluster-disabled'; import { ClusterDashboard } from '../../../../../components/management/cluster/dashboard/dashboard'; import { LoadingSpinner } from '../../../../../components/common/loading-spinner/loading-spinner'; @@ -38,7 +38,7 @@ const checkClusterIsEnabledAndRunning = async () => { return { ok: true, data: { - error, + error: { title: 'There was a problem', message: error.message }, }, }; } @@ -68,45 +68,17 @@ export const ClusterOverview = compose( ), ), )( - class ClusterOverview extends Component { - _isMount = false; - - constructor(props) { - super(props); - this.state = { - clusterEnabled: false, - isClusterRunning: false, - statusRunning: 'no', - }; - } - - async componentDidMount() { - this._isMount = true; - const clusterEnabled = - AppState.getClusterInfo() && - AppState.getClusterInfo().status === 'enabled'; - - const status: any = await WzRequest.apiReq('GET', '/cluster/status', {}); - const statusRunning = status?.data?.data?.running; - this.setState({ - clusterEnabled: clusterEnabled, - isClusterRunning: statusRunning === 'no' ? false : true, - statusRunning, - }); - } - - componentWillUnmount() { - this._isMount = false; - } - - render() { - return ( - <> - {this.props?.clusterEnabled && this.props?.isClusterRunning ? ( - - ) : null} - - ); - } + ({ + clusterEnabled, + isClusterRunning, + statusRunning, + }: ClusterOverviewState) => { + return ( + <> + {clusterEnabled && isClusterRunning ? ( + + ) : null} + + ); }, ); From 6a9ddc0fb0b022f36eab8e1c04f204131d536a40 Mon Sep 17 00:00:00 2001 From: jbiset Date: Thu, 18 Apr 2024 09:28:54 -0300 Subject: [PATCH 10/12] Added more dependencies in useEffect on Cluster Dashboard --- .../components/management/cluster/dashboard/dashboard.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/main/public/components/management/cluster/dashboard/dashboard.tsx b/plugins/main/public/components/management/cluster/dashboard/dashboard.tsx index a4c3830851..0db6f747c7 100644 --- a/plugins/main/public/components/management/cluster/dashboard/dashboard.tsx +++ b/plugins/main/public/components/management/cluster/dashboard/dashboard.tsx @@ -95,7 +95,12 @@ const DashboardCT: React.FC = ({ statusRunning }) => { }); ErrorHandler.handleError(searchError); }); - }, [JSON.stringify(fetchFilters), JSON.stringify(query)]); + }, [ + JSON.stringify(fetchFilters), + JSON.stringify(query), + dateRangeFrom, + dateRangeTo, + ]); const setBooleans = (component: string | null) => { setState({ From e802a440b22bb3de64602e9e447eaa14d3500860 Mon Sep 17 00:00:00 2001 From: jbiset Date: Thu, 18 Apr 2024 12:07:36 -0300 Subject: [PATCH 11/12] Added error handling to cluster-disabled component --- .../components/management/cluster/cluster-disabled.js | 8 ++++++-- .../management/cluster/cluster-overview.tsx | 11 ++++++++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/plugins/main/public/components/management/cluster/cluster-disabled.js b/plugins/main/public/components/management/cluster/cluster-disabled.js index 131805639d..e980c9b7fd 100644 --- a/plugins/main/public/components/management/cluster/cluster-disabled.js +++ b/plugins/main/public/components/management/cluster/cluster-disabled.js @@ -15,7 +15,9 @@ export const ClusterDisabled = withErrorBoundary( iconType='iInCircle' title={

- {!this.props.enabled + {this.props.error + ? this.props.error.title + : !this.props.enabled ? 'The cluster is disabled' : !this.props.running ? 'The cluster is not running' @@ -24,7 +26,9 @@ export const ClusterDisabled = withErrorBoundary( } body={ - {!this.props.enabled ? ( + {this.props.error ? ( + this.props.error.message + ) : !this.props.enabled ? (

Visit the documentation on{' '} { try { const status: any = await WzRequest.apiReq('GET', '/cluster/status', {}); const clusterEnabled = status?.data?.data?.enabled; + const isClusterEnabled = clusterEnabled === 'yes'; const statusRunning = status?.data?.data?.running; const isClusterRunning = statusRunning === 'yes'; return { - ok: !Boolean(clusterEnabled && statusRunning), + ok: !(isClusterEnabled && isClusterRunning), data: { clusterEnabled, isClusterRunning, @@ -53,8 +54,12 @@ export const ClusterOverview = compose( ]), withGuardAsync( checkClusterIsEnabledAndRunning, - ({ clusterEnabled, isClusterRunning }) => ( - + ({ clusterEnabled, isClusterRunning, error }) => ( + ), () => ( Date: Mon, 22 Apr 2024 11:40:51 -0300 Subject: [PATCH 12/12] Fixed alert.timestamp field on SampleData --- .../generate-alerts/generate-alerts-script.js | 446 +++++++++++++----- 1 file changed, 324 insertions(+), 122 deletions(-) diff --git a/plugins/main/server/lib/generate-alerts/generate-alerts-script.js b/plugins/main/server/lib/generate-alerts/generate-alerts-script.js index f718d55e47..9510171002 100644 --- a/plugins/main/server/lib/generate-alerts/generate-alerts-script.js +++ b/plugins/main/server/lib/generate-alerts/generate-alerts-script.js @@ -22,7 +22,14 @@ import { randomElements, randomArrayItem, } from './sample-data/common'; -import { PCI_DSS, GDPR, HIPAA, GPG13, NIST_800_53, tsc } from './sample-data/regulatory-compliance'; +import { + PCI_DSS, + GDPR, + HIPAA, + GPG13, + NIST_800_53, + tsc, +} from './sample-data/regulatory-compliance'; import * as Audit from './sample-data/audit'; import * as Authentication from './sample-data/authentication'; @@ -143,7 +150,9 @@ function generateAlert(params) { 'iamPolicyGrantGlobal', ]); - const beforeDate = new Date(new Date(alert.timestamp) - 3 * 24 * 60 * 60 * 1000); + const beforeDate = new Date( + new Date(alert.timestamp) - 3 * 24 * 60 * 60 * 1000, + ); switch (randomType) { case 'guarddutyPortProbe': { const typeAlert = AWS.guarddutyPortProbe; @@ -151,29 +160,39 @@ function generateAlert(params) { alert.data = { ...typeAlert.data }; alert.data.integration = 'aws'; alert.data.aws.region = randomArrayItem(AWS.region); - alert.data.aws.resource.instanceDetails = { ...randomArrayItem(AWS.instanceDetails) }; - alert.data.aws.resource.instanceDetails.iamInstanceProfile.arn = interpolateAlertProps( - typeAlert.data.aws.resource.instanceDetails.iamInstanceProfile.arn, - alert + alert.data.aws.resource.instanceDetails = { + ...randomArrayItem(AWS.instanceDetails), + }; + alert.data.aws.resource.instanceDetails.iamInstanceProfile.arn = + interpolateAlertProps( + typeAlert.data.aws.resource.instanceDetails.iamInstanceProfile.arn, + alert, + ); + alert.data.aws.title = interpolateAlertProps( + alert.data.aws.title, + alert, ); - alert.data.aws.title = interpolateAlertProps(alert.data.aws.title, alert); alert.data.aws.accountId = randomArrayItem(AWS.accountId); - alert.data.aws.service.eventFirstSeen = formatDate(beforeDate, 'Y-M-DTh:m:s.lZ'); + alert.data.aws.service.eventFirstSeen = formatDate( + beforeDate, + 'Y-M-DTh:m:s.lZ', + ); alert.data.aws.service.eventLastSeen = formatDate( new Date(alert.timestamp), - 'Y-M-DTh:m:s.lZ' + 'Y-M-DTh:m:s.lZ', ); - alert.data.aws.service.action.portProbeAction.portProbeDetails.remoteIpDetails = { - ...randomArrayItem(AWS.remoteIpDetails), - }; + alert.data.aws.service.action.portProbeAction.portProbeDetails.remoteIpDetails = + { + ...randomArrayItem(AWS.remoteIpDetails), + }; alert.data.aws.log_info = { s3bucket: randomArrayItem(AWS.buckets), log_file: `guardduty/${formatDate( new Date(alert.timestamp), - 'Y/M/D/h' + 'Y/M/D/h', )}/firehose_guardduty-1-${formatDate( new Date(alert.timestamp), - 'Y-M-D-h-m-s-l' + 'Y-M-D-h-m-s-l', )}b5b9b-ec62-4a07-85d7-b1699b9c031e.zip`, }; alert.data.aws.service.count = `${randomIntervalInteger(400, 4000)}`; @@ -181,7 +200,10 @@ function generateAlert(params) { alert.rule = { ...typeAlert.rule }; alert.rule.firedtimes = randomIntervalInteger(1, 50); - alert.rule.description = interpolateAlertProps(typeAlert.rule.description, alert); + alert.rule.description = interpolateAlertProps( + typeAlert.rule.description, + alert, + ); alert.decoder = { ...typeAlert.decoder }; alert.location = typeAlert.location; @@ -193,36 +215,49 @@ function generateAlert(params) { alert.data = { ...typeAlert.data }; alert.data.integration = 'aws'; alert.data.aws.region = randomArrayItem(AWS.region); - alert.data.aws.resource.accessKeyDetails.userName = randomArrayItem(Users); + alert.data.aws.resource.accessKeyDetails.userName = + randomArrayItem(Users); alert.data.aws.log_info = { s3bucket: randomArrayItem(AWS.buckets), log_file: `guardduty/${formatDate( new Date(alert.timestamp), - 'Y/M/D/h' + 'Y/M/D/h', )}/firehose_guardduty-1-${formatDate( new Date(alert.timestamp), - 'Y-M-D-h-m-s-l' + 'Y-M-D-h-m-s-l', )}b5b9b-ec62-4a07-85d7-b1699b9c031e.zip`, }; alert.data.aws.accountId = randomArrayItem(AWS.accountId); alert.data.aws.service.action.awsApiCallAction.remoteIpDetails = { ...randomArrayItem(AWS.remoteIpDetails), }; - alert.data.aws.service.eventFirstSeen = formatDate(beforeDate, 'Y-M-DTh:m:s.lZ'); + alert.data.aws.service.eventFirstSeen = formatDate( + beforeDate, + 'Y-M-DTh:m:s.lZ', + ); alert.data.aws.service.eventLastSeen = formatDate( new Date(alert.timestamp), - 'Y-M-DTh:m:s.lZ' + 'Y-M-DTh:m:s.lZ', ); alert.data.aws.createdAt = formatDate(beforeDate, 'Y-M-DTh:m:s.lZ'); - alert.data.aws.title = interpolateAlertProps(alert.data.aws.title, alert); - alert.data.aws.description = interpolateAlertProps(alert.data.aws.description, alert); + alert.data.aws.title = interpolateAlertProps( + alert.data.aws.title, + alert, + ); + alert.data.aws.description = interpolateAlertProps( + alert.data.aws.description, + alert, + ); const count = `${randomIntervalInteger(400, 4000)}`; alert.data.aws.service.additionalInfo.recentApiCalls.count = count; alert.data.aws.service.count = count; alert.rule = { ...typeAlert.rule }; alert.rule.firedtimes = randomIntervalInteger(1, 50); - alert.rule.description = interpolateAlertProps(typeAlert.rule.description, alert); + alert.rule.description = interpolateAlertProps( + typeAlert.rule.description, + alert, + ); alert.decoder = { ...typeAlert.decoder }; alert.location = typeAlert.location; @@ -234,28 +269,40 @@ function generateAlert(params) { alert.data = { ...typeAlert.data }; alert.data.integration = 'aws'; alert.data.aws.region = randomArrayItem(AWS.region); - alert.data.aws.resource.instanceDetails = { ...randomArrayItem(AWS.instanceDetails) }; + alert.data.aws.resource.instanceDetails = { + ...randomArrayItem(AWS.instanceDetails), + }; alert.data.aws.log_info = { s3bucket: randomArrayItem(AWS.buckets), log_file: `guardduty/${formatDate( new Date(alert.timestamp), - 'Y/M/D/h' + 'Y/M/D/h', )}/firehose_guardduty-1-${formatDate( new Date(alert.timestamp), - 'Y-M-D-h-m-s-l' + 'Y-M-D-h-m-s-l', )}b5b9b-ec62-4a07-85d7-b1699b9c031e.zip`, }; - alert.data.aws.description = interpolateAlertProps(alert.data.aws.description, alert); - alert.data.aws.title = interpolateAlertProps(alert.data.aws.title, alert); + alert.data.aws.description = interpolateAlertProps( + alert.data.aws.description, + alert, + ); + alert.data.aws.title = interpolateAlertProps( + alert.data.aws.title, + alert, + ); alert.data.aws.accountId = randomArrayItem(AWS.accountId); alert.data.aws.createdAt = formatDate(beforeDate, 'Y-M-DTh:m:s.lZ'); - alert.data.aws.service.action.networkConnectionAction.remoteIpDetails = { - ...randomArrayItem(AWS.remoteIpDetails), - }; - alert.data.aws.service.eventFirstSeen = formatDate(beforeDate, 'Y-M-DTh:m:s.lZ'); + alert.data.aws.service.action.networkConnectionAction.remoteIpDetails = + { + ...randomArrayItem(AWS.remoteIpDetails), + }; + alert.data.aws.service.eventFirstSeen = formatDate( + beforeDate, + 'Y-M-DTh:m:s.lZ', + ); alert.data.aws.service.eventLastSeen = formatDate( new Date(alert.timestamp), - 'Y-M-DTh:m:s.lZ' + 'Y-M-DTh:m:s.lZ', ); alert.data.aws.service.additionalInfo = { localPort: `${randomArrayItem(Ports)}`, @@ -266,10 +313,16 @@ function generateAlert(params) { alert.data.aws.service.count = `${randomIntervalInteger(400, 4000)}`; alert.data.aws.service.action.networkConnectionAction.localIpDetails.ipAddressV4 = alert.data.aws.resource.instanceDetails.networkInterfaces.privateIpAddress; - alert.data.aws.arn = interpolateAlertProps(typeAlert.data.aws.arn, alert); + alert.data.aws.arn = interpolateAlertProps( + typeAlert.data.aws.arn, + alert, + ); alert.rule = { ...typeAlert.rule }; alert.rule.firedtimes = randomIntervalInteger(1, 50); - alert.rule.description = interpolateAlertProps(typeAlert.rule.description, alert); + alert.rule.description = interpolateAlertProps( + typeAlert.rule.description, + alert, + ); alert.decoder = { ...typeAlert.decoder }; alert.location = typeAlert.location; @@ -281,23 +334,32 @@ function generateAlert(params) { alert.data = { ...typeAlert.data }; alert.data.integration = 'aws'; alert.data.aws.region = randomArrayItem(AWS.region); - alert.data.aws.summary.Timestamps = formatDate(beforeDate, 'Y-M-DTh:m:s.lZ'); + alert.data.aws.summary.Timestamps = formatDate( + beforeDate, + 'Y-M-DTh:m:s.lZ', + ); alert.data.aws.log_info = { s3bucket: randomArrayItem(AWS.buckets), log_file: `macie/${formatDate( new Date(alert.timestamp), - 'Y/M/D/h' + 'Y/M/D/h', )}/firehose_macie-1-${formatDate( new Date(alert.timestamp), - 'Y-M-D-h-m-s' + 'Y-M-D-h-m-s', )}-0b1ede94-f399-4e54-8815-1c6587eee3b1//firehose_guardduty-1-${formatDate( new Date(alert.timestamp), - 'Y-M-D-h-m-s-l' + 'Y-M-D-h-m-s-l', )}b5b9b-ec62-4a07-85d7-b1699b9c031e.zip`, }; alert.data.aws['created-at'] = formatDate(beforeDate, 'Y-M-DTh:m:s.lZ'); - alert.data.aws.url = interpolateAlertProps(typeAlert.data.aws.url, alert); - alert.data.aws['alert-arn'] = interpolateAlertProps(typeAlert.data.aws['alert-arn'], alert); + alert.data.aws.url = interpolateAlertProps( + typeAlert.data.aws.url, + alert, + ); + alert.data.aws['alert-arn'] = interpolateAlertProps( + typeAlert.data.aws['alert-arn'], + alert, + ); alert.rule = { ...typeAlert.rule }; alert.rule.firedtimes = randomIntervalInteger(1, 50); @@ -317,25 +379,31 @@ function generateAlert(params) { alert.agent = { id: '000', ip: alert.agent.ip, - name: alert.agent.name + name: alert.agent.name, }; if (params.manager && params.manager.name) { alert.agent.name = params.manager.name; - }; + } - const beforeDate = new Date(new Date(alert.timestamp) - 3 * 24 * 60 * 60 * 1000); + const beforeDate = new Date( + new Date(alert.timestamp) - 3 * 24 * 60 * 60 * 1000, + ); const IntraID = randomArrayItem(Office.arrayUuidOffice); const OrgID = randomArrayItem(Office.arrayUuidOffice); const objID = randomArrayItem(Office.arrayUuidOffice); const userKey = randomArrayItem(Office.arrayUuidOffice); const userID = randomArrayItem(Office.arrayUserId); const userType = randomArrayItem([0, 2, 4]); - const resultStatus = randomArrayItem(['Succeeded', 'PartiallySucceeded', 'Failed']); + const resultStatus = randomArrayItem([ + 'Succeeded', + 'PartiallySucceeded', + 'Failed', + ]); const log = randomArrayItem(Office.arrayLogs); const ruleData = Office.officeRules[log.RecordType]; - alert.agent.id = '000' + alert.agent.id = '000'; alert.rule = ruleData.rule; alert.decoder = randomArrayItem(Office.arrayDecoderOffice); alert.GeoLocation = randomArrayItem(GeoLocation); @@ -362,13 +430,30 @@ function generateAlert(params) { alert.data.gcp = { insertId: 'uk1zpe23xcj', jsonPayload: { - authAnswer: GCP.arrayAuthAnswer[Math.floor(GCP.arrayAuthAnswer.length * Math.random())], - protocol: GCP.arrayProtocol[Math.floor(GCP.arrayProtocol.length * Math.random())], - queryName: GCP.arrayQueryName[Math.floor(GCP.arrayQueryName.length * Math.random())], - queryType: GCP.arrayQueryType[Math.floor(GCP.arrayQueryType.length * Math.random())], + authAnswer: + GCP.arrayAuthAnswer[ + Math.floor(GCP.arrayAuthAnswer.length * Math.random()) + ], + protocol: + GCP.arrayProtocol[ + Math.floor(GCP.arrayProtocol.length * Math.random()) + ], + queryName: + GCP.arrayQueryName[ + Math.floor(GCP.arrayQueryName.length * Math.random()) + ], + queryType: + GCP.arrayQueryType[ + Math.floor(GCP.arrayQueryType.length * Math.random()) + ], responseCode: - GCP.arrayResponseCode[Math.floor(GCP.arrayResponseCode.length * Math.random())], - sourceIP: GCP.arraySourceIP[Math.floor(GCP.arraySourceIP.length * Math.random())], + GCP.arrayResponseCode[ + Math.floor(GCP.arrayResponseCode.length * Math.random()) + ], + sourceIP: + GCP.arraySourceIP[ + Math.floor(GCP.arraySourceIP.length * Math.random()) + ], vmInstanceId: '4980113928800839680.000000', vmInstanceName: '531339229531.instance-1', }, @@ -376,14 +461,24 @@ function generateAlert(params) { receiveTimestamp: '2019-11-11T02:42:05.05853152Z', resource: { labels: { - location: GCP.arrayLocation[Math.floor(GCP.arrayLocation.length * Math.random())], - project_id: GCP.arrayProject[Math.floor(GCP.arrayProject.length * Math.random())], - source_type: GCP.arraySourceType[Math.floor(GCP.arraySourceType.length * Math.random())], + location: + GCP.arrayLocation[ + Math.floor(GCP.arrayLocation.length * Math.random()) + ], + project_id: + GCP.arrayProject[ + Math.floor(GCP.arrayProject.length * Math.random()) + ], + source_type: + GCP.arraySourceType[ + Math.floor(GCP.arraySourceType.length * Math.random()) + ], target_type: 'external', }, type: GCP.arrayType[Math.floor(GCP.arrayType.length * Math.random())], }, - severity: GCP.arraySeverity[Math.floor(GCP.arraySeverity.length * Math.random())], + severity: + GCP.arraySeverity[Math.floor(GCP.arraySeverity.length * Math.random())], timestamp: '2019-11-11T02:42:04.34921449Z', }; @@ -459,13 +554,21 @@ function generateAlert(params) { switch (alertCategory) { case 'Rootkit': { - const rootkitCategory = randomArrayItem(Object.keys(PolicyMonitoring.rootkits)); - const rootkit = randomArrayItem(PolicyMonitoring.rootkits[rootkitCategory]); + const rootkitCategory = randomArrayItem( + Object.keys(PolicyMonitoring.rootkits), + ); + const rootkit = randomArrayItem( + PolicyMonitoring.rootkits[rootkitCategory], + ); alert.data = { - title: interpolateAlertProps(PolicyMonitoring.rootkitsData.data.title, alert, { - _rootkit_category: rootkitCategory, - _rootkit_file: rootkit, - }), + title: interpolateAlertProps( + PolicyMonitoring.rootkitsData.data.title, + alert, + { + _rootkit_category: rootkitCategory, + _rootkit_file: rootkit, + }, + ), }; alert.rule = { ...PolicyMonitoring.rootkitsData.rule }; alert.rule.firedtimes = randomIntervalInteger(1, 10); @@ -480,9 +583,13 @@ function generateAlert(params) { }; alert.rule = { ...PolicyMonitoring.trojansData.rule }; alert.rule.firedtimes = randomIntervalInteger(1, 10); - alert.full_log = interpolateAlertProps(PolicyMonitoring.trojansData.full_log, alert, { - _trojan_signature: trojan.signature, - }); + alert.full_log = interpolateAlertProps( + PolicyMonitoring.trojansData.full_log, + alert, + { + _trojan_signature: trojan.signature, + }, + ); break; } default: { @@ -497,7 +604,7 @@ function generateAlert(params) { alert.syscheck.path = randomArrayItem( alert.agent.name === 'Windows' ? IntegrityMonitoring.pathsWindows - : IntegrityMonitoring.pathsLinux + : IntegrityMonitoring.pathsLinux, ); alert.syscheck.uname_after = randomArrayItem(Users); alert.syscheck.gname_after = 'root'; @@ -513,10 +620,14 @@ function generateAlert(params) { break; case 'modified': alert.rule = IntegrityMonitoring.regulatory[1]; - alert.syscheck.mtime_before = new Date(alert.syscheck.mtime_after.getTime() - 1000 * 60); + alert.syscheck.mtime_before = new Date( + alert.syscheck.mtime_after.getTime() - 1000 * 60, + ); alert.syscheck.inode_before = randomIntervalInteger(0, 100000); alert.syscheck.sha1_after = randomElements(40, 'abcdef0123456789'); - alert.syscheck.changed_attributes = [randomArrayItem(IntegrityMonitoring.attributes)]; + alert.syscheck.changed_attributes = [ + randomArrayItem(IntegrityMonitoring.attributes), + ]; alert.syscheck.md5_after = randomElements(32, 'abcdef0123456789'); alert.syscheck.sha256_after = randomElements(60, 'abcdef0123456789'); break; @@ -560,7 +671,10 @@ function generateAlert(params) { alert.data.virustotal.source = { sha1: randomElements(40, 'abcdef0123456789'), file: randomArrayItem(Virustotal.sourceFile), - alert_id: `${randomElements(10, '0123456789')}.${randomElements(7, '0123456789')}`, + alert_id: `${randomElements(10, '0123456789')}.${randomElements( + 7, + '0123456789', + )}`, md5: randomElements(32, 'abcdef0123456789'), }; @@ -571,10 +685,13 @@ function generateAlert(params) { alert.data.virustotal.malicious + alert.data.virustotal.positives; alert.rule.description = `VirusTotal: Alert - ${alert.data.virustotal.source.file} - ${alert.data.virustotal.positives} engines detected this file`; alert.data.virustotal.permalink = randomArrayItem(Virustotal.permalink); - alert.data.virustotal.scan_date = new Date(Date.parse(alert.timestamp) - 4 * 60000); + alert.data.virustotal.scan_date = new Date( + Date.parse(alert.timestamp) - 4 * 60000, + ); } else { alert.data.virustotal.malicious = '0'; - alert.rule.description = 'VirusTotal: Alert - No records in VirusTotal database'; + alert.rule.description = + 'VirusTotal: Alert - No records in VirusTotal database'; } } @@ -605,7 +722,9 @@ function generateAlert(params) { alert.data.osquery = dataOsquery.osquery; alert.data.osquery.calendarTime = alert.timestamp; alert.rule.description = dataOsquery.rule.description; - randomIntervalInteger(0, 99) === 0 ? (alert.data.osquery.action = 'removed') : null; + randomIntervalInteger(0, 99) === 0 + ? (alert.data.osquery.action = 'removed') + : null; } } @@ -687,39 +806,56 @@ function generateAlert(params) { case 'invalidLoginPassword': { alert.location = Authentication.invalidLoginPassword.location; alert.rule = { ...Authentication.invalidLoginPassword.rule }; - alert.rule.groups = [...Authentication.invalidLoginPassword.rule.groups]; - alert.full_log = interpolateAlertProps(Authentication.invalidLoginPassword.full_log, alert); + alert.rule.groups = [ + ...Authentication.invalidLoginPassword.rule.groups, + ]; + alert.full_log = interpolateAlertProps( + Authentication.invalidLoginPassword.full_log, + alert, + ); break; } case 'invalidLoginUser': { alert.location = Authentication.invalidLoginUser.location; alert.rule = { ...Authentication.invalidLoginUser.rule }; alert.rule.groups = [...Authentication.invalidLoginUser.rule.groups]; - alert.full_log = interpolateAlertProps(Authentication.invalidLoginUser.full_log, alert); + alert.full_log = interpolateAlertProps( + Authentication.invalidLoginUser.full_log, + alert, + ); break; } case 'multipleAuthenticationFailures': { alert.location = Authentication.multipleAuthenticationFailures.location; alert.rule = { ...Authentication.multipleAuthenticationFailures.rule }; - alert.rule.groups = [...Authentication.multipleAuthenticationFailures.rule.groups]; + alert.rule.groups = [ + ...Authentication.multipleAuthenticationFailures.rule.groups, + ]; alert.rule.frequency = randomIntervalInteger(5, 50); alert.full_log = interpolateAlertProps( Authentication.multipleAuthenticationFailures.full_log, - alert + alert, ); break; } case 'windowsInvalidLoginPassword': { alert.location = Authentication.windowsInvalidLoginPassword.location; alert.rule = { ...Authentication.windowsInvalidLoginPassword.rule }; - alert.rule.groups = [...Authentication.windowsInvalidLoginPassword.rule.groups]; + alert.rule.groups = [ + ...Authentication.windowsInvalidLoginPassword.rule.groups, + ]; alert.rule.frequency = randomIntervalInteger(5, 50); - alert.data.win = { ...Authentication.windowsInvalidLoginPassword.data_win }; + alert.data.win = { + ...Authentication.windowsInvalidLoginPassword.data_win, + }; alert.data.win.eventdata.ipAddress = randomArrayItem(IPs); alert.data.win.eventdata.ipPort = randomArrayItem(Ports); alert.data.win.system.computer = randomArrayItem(Win_Hostnames); alert.data.win.system.eventID = `${randomIntervalInteger(1, 600)}`; - alert.data.win.system.eventRecordID = `${randomIntervalInteger(10000, 50000)}`; + alert.data.win.system.eventRecordID = `${randomIntervalInteger( + 10000, + 50000, + )}`; alert.data.win.system.processID = `${randomIntervalInteger(1, 1200)}`; alert.data.win.system.systemTime = alert.timestamp; alert.data.win.system.processID = `${randomIntervalInteger(1, 1200)}`; @@ -727,7 +863,7 @@ function generateAlert(params) { alert.data.win.system.threadID = `${randomIntervalInteger(1, 500)}`; alert.full_log = interpolateAlertProps( Authentication.windowsInvalidLoginPassword.full_log, - alert + alert, ); break; } @@ -743,7 +879,10 @@ function generateAlert(params) { tty: 'ssh', }; alert.decoder = { ...Authentication.userLoginFailed.decoder }; - alert.full_log = interpolateAlertProps(Authentication.userLoginFailed.full_log, alert); + alert.full_log = interpolateAlertProps( + Authentication.userLoginFailed.full_log, + alert, + ); break; } case 'passwordCheckFailed': { @@ -755,23 +894,31 @@ function generateAlert(params) { }; alert.predecoder.program_name = 'unix_chkpwd'; alert.decoder = { ...Authentication.passwordCheckFailed.decoder }; - alert.full_log = interpolateAlertProps(Authentication.passwordCheckFailed.full_log, alert); + alert.full_log = interpolateAlertProps( + Authentication.passwordCheckFailed.full_log, + alert, + ); break; } case 'nonExistentUser': { alert.location = Authentication.nonExistentUser.location; alert.rule = { ...Authentication.nonExistentUser.rule }; alert.rule.groups = [...Authentication.nonExistentUser.rule.groups]; - alert.full_log = interpolateAlertProps(Authentication.nonExistentUser.full_log, alert); + alert.full_log = interpolateAlertProps( + Authentication.nonExistentUser.full_log, + alert, + ); break; } case 'bruteForceTryingAccessSystem': { alert.location = Authentication.bruteForceTryingAccessSystem.location; alert.rule = { ...Authentication.bruteForceTryingAccessSystem.rule }; - alert.rule.groups = [...Authentication.bruteForceTryingAccessSystem.rule.groups]; + alert.rule.groups = [ + ...Authentication.bruteForceTryingAccessSystem.rule.groups, + ]; alert.full_log = interpolateAlertProps( Authentication.bruteForceTryingAccessSystem.full_log, - alert + alert, ); break; } @@ -782,25 +929,32 @@ function generateAlert(params) { alert.data = { srcip: randomArrayItem(IPs), }; - alert.full_log = interpolateAlertProps(Authentication.reverseLoockupError.full_log, alert); + alert.full_log = interpolateAlertProps( + Authentication.reverseLoockupError.full_log, + alert, + ); } case 'insecureConnectionAttempt': { alert.location = Authentication.insecureConnectionAttempt.location; alert.rule = { ...Authentication.insecureConnectionAttempt.rule }; - alert.rule.groups = [...Authentication.insecureConnectionAttempt.rule.groups]; + alert.rule.groups = [ + ...Authentication.insecureConnectionAttempt.rule.groups, + ]; alert.data = { srcip: randomArrayItem(IPs), srcport: randomArrayItem(Ports), }; alert.full_log = interpolateAlertProps( Authentication.insecureConnectionAttempt.full_log, - alert + alert, ); } case 'authenticationSuccess': { alert.location = Authentication.authenticationSuccess.location; alert.rule = { ...Authentication.authenticationSuccess.rule }; - alert.rule.groups = [...Authentication.authenticationSuccess.rule.groups]; + alert.rule.groups = [ + ...Authentication.authenticationSuccess.rule.groups, + ]; alert.data = { srcip: randomArrayItem(IPs), srcport: randomArrayItem(Ports), @@ -808,13 +962,18 @@ function generateAlert(params) { }; alert.full_log = interpolateAlertProps( Authentication.authenticationSuccess.full_log, - alert + alert, ); } case 'maximumAuthenticationAttemptsExceeded': { - alert.location = Authentication.maximumAuthenticationAttemptsExceeded.location; - alert.rule = { ...Authentication.maximumAuthenticationAttemptsExceeded.rule }; - alert.rule.groups = [...Authentication.maximumAuthenticationAttemptsExceeded.rule.groups]; + alert.location = + Authentication.maximumAuthenticationAttemptsExceeded.location; + alert.rule = { + ...Authentication.maximumAuthenticationAttemptsExceeded.rule, + }; + alert.rule.groups = [ + ...Authentication.maximumAuthenticationAttemptsExceeded.rule.groups, + ]; alert.data = { srcip: randomArrayItem(IPs), srcport: randomArrayItem(Ports), @@ -822,7 +981,7 @@ function generateAlert(params) { }; alert.full_log = interpolateAlertProps( Authentication.maximumAuthenticationAttemptsExceeded.full_log, - alert + alert, ); } default: { @@ -915,7 +1074,10 @@ function generateAlert(params) { alert.decoder = { ...Apache.decoder }; alert.full_log = interpolateAlertProps(typeAlert.full_log, alert, { - _timestamp_apache: formatDate(new Date(alert.timestamp), 'E N D h:m:s.l Y'), + _timestamp_apache: formatDate( + new Date(alert.timestamp), + 'E N D h:m:s.l Y', + ), _pi_id: randomIntervalInteger(10000, 30000), }); } @@ -951,35 +1113,51 @@ function generateAlert(params) { interpolateAlertProps(typeAlert.full_log, alert, { _user_agent: userAgent, _date: formatDate(new Date(beforeDate), 'D/N/Y:h:m:s +0000'), - }) + }), ); } alert.previous_output = previousOutput.join('\n'); } } - if (params.github){ + if (params.github) { alert.location = GitHub.LOCATION; alert.decoder = GitHub.DECODER; const alertType = randomArrayItem(GitHub.ALERT_TYPES); const actor = randomArrayItem(GitHub.ACTORS); alert.data = { - github : { ...alertType.data.github } + github: { ...alertType.data.github }, }; alert.data.github.org = randomArrayItem(GitHub.ORGANIZATION_NAMES); - alert.data.github.repo && (alert.data.github.repo = `${alert.data.github.org}/${randomArrayItem(GitHub.REPOSITORY_NAMES)}`); - alert.data.github.repository && (alert.data.github.repository = `${alert.data.github.org}/${randomArrayItem(GitHub.REPOSITORY_NAMES)}`); + alert.data.github.repo && + (alert.data.github.repo = `${alert.data.github.org}/${randomArrayItem( + GitHub.REPOSITORY_NAMES, + )}`); + alert.data.github.repository && + (alert.data.github.repository = `${ + alert.data.github.org + }/${randomArrayItem(GitHub.REPOSITORY_NAMES)}`); alert.data.github.actor = actor.name; - alert.data.github.actor_location && alert.data.github.actor_location.country_code && (alert.data.github.actor_location.country_code = actor.country_code); - alert.data.github.user && (alert.data.github.user = randomArrayItem(GitHub.USER_NAMES)); - alert.data.github.config && alert.data.github.config.url && (alert.data.github.config.url = randomArrayItem(GitHub.SERVER_ADDRESS_WEBHOOK)); + alert.data.github.actor_location && + alert.data.github.actor_location.country_code && + (alert.data.github.actor_location.country_code = actor.country_code); + alert.data.github.user && + (alert.data.github.user = randomArrayItem(GitHub.USER_NAMES)); + alert.data.github.config && + alert.data.github.config.url && + (alert.data.github.config.url = randomArrayItem( + GitHub.SERVER_ADDRESS_WEBHOOK, + )); alert.data.github['@timestamp'] = alert.timestamp; - alert.data.github.created_at && (alert.data.github.created_at = alert.timestamp); + alert.data.github.created_at && + (alert.data.github.created_at = alert.timestamp); alert.rule = { - ...alertType.rule + ...alertType.rule, }; } - + + alert['@timestamp'] = alert.timestamp; + return alert; } @@ -1037,7 +1215,8 @@ function randomDate(inf, sup) { return formatDate(lastWeek, 'Y-M-DTh:m:s.l+0000'); } -const formatterNumber = (number, zeros = 0) => ('0'.repeat(zeros) + `${number}`).slice(-zeros); +const formatterNumber = (number, zeros = 0) => + ('0'.repeat(zeros) + `${number}`).slice(-zeros); const monthNames = { long: [ 'January', @@ -1053,28 +1232,49 @@ const monthNames = { 'November', 'December', ], - short: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], + short: [ + 'Jan', + 'Feb', + 'Mar', + 'Apr', + 'May', + 'Jun', + 'Jul', + 'Aug', + 'Sep', + 'Oct', + 'Nov', + 'Dec', + ], }; const dayNames = { - long: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], + long: [ + 'Sunday', + 'Monday', + 'Tuesday', + 'Wednesday', + 'Thursday', + 'Friday', + 'Saturday', + ], short: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], }; function formatDate(date, format) { // It could use "moment" library to format strings too const tokens = { - D: (d) => formatterNumber(d.getDate(), 2), // 01-31 - A: (d) => dayNames.long[d.getDay()], // 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' - E: (d) => dayNames.short[d.getDay()], // 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' - M: (d) => formatterNumber(d.getMonth() + 1, 2), // 01-12 - J: (d) => monthNames.long[d.getMonth()], // 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' - N: (d) => monthNames.short[d.getMonth()], // 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' - Y: (d) => d.getFullYear(), // 2020 - h: (d) => formatterNumber(d.getHours(), 2), // 00-23 - m: (d) => formatterNumber(d.getMinutes(), 2), // 00-59 - s: (d) => formatterNumber(d.getSeconds(), 2), // 00-59 - l: (d) => formatterNumber(d.getMilliseconds(), 3), // 000-999 + D: d => formatterNumber(d.getDate(), 2), // 01-31 + A: d => dayNames.long[d.getDay()], // 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' + E: d => dayNames.short[d.getDay()], // 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' + M: d => formatterNumber(d.getMonth() + 1, 2), // 01-12 + J: d => monthNames.long[d.getMonth()], // 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' + N: d => monthNames.short[d.getMonth()], // 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' + Y: d => d.getFullYear(), // 2020 + h: d => formatterNumber(d.getHours(), 2), // 00-23 + m: d => formatterNumber(d.getMinutes(), 2), // 00-59 + s: d => formatterNumber(d.getSeconds(), 2), // 00-59 + l: d => formatterNumber(d.getMilliseconds(), 3), // 000-999 }; return format.split('').reduce((accum, token) => { @@ -1098,7 +1298,9 @@ function interpolateAlertProps(str, alert, extra = {}) { matches.reduce((accum, cur) => { const match = cur.match(/{([\w\._]+)}/); const items = match[1].split('.'); - const value = items.reduce((a, c) => (a && a[c]) || extra[c] || undefined, alert) || cur; + const value = + items.reduce((a, c) => (a && a[c]) || extra[c] || undefined, alert) || + cur; return accum.replace(cur, value); }, str)) || str