diff --git a/CHANGELOG.md b/CHANGELOG.md index 402a0adc61..4e427ef19c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ All notable changes to the Wazuh app project will be documented in this file. - Changed the usage of the endpoint GET /groups/{group_id}/files/{file_name} [#6385](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6385) - Refactoring and redesign endpoints summary visualizations [#6268](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6268) - Move the AngularJS controller and template of blank screen to ReactJS component [#6538](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6538) +- Moved the registry data to in-memory cache [#6481](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6481) - Remove AngularJS controller for manage groups [#6543](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6543) - Remove some branding references across the application. [#6155](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6155) @@ -32,6 +33,12 @@ All notable changes to the Wazuh app project will be documented in this file. - Fixed the scripted fields disappear when the fields of the events index pattern was refreshed [#6237](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6237) +### Removed + +- Removed API endpoint GET /api/timestamp [#6481](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6481) +- Removed API endpoint PUT /api/update-hostname/{id} [#6481](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6481) +- Removed API endpoint DELETE /hosts/remove-orphan-entries [#6481](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6481) + ## Wazuh v4.8.2 - OpenSearch Dashboards 2.10.0 - Revision 00 ### Added diff --git a/plugins/main/public/components/settings/about/__snapshots__/appInfo.test.tsx.snap b/plugins/main/public/components/settings/about/__snapshots__/appInfo.test.tsx.snap index 2f9cbfd69f..90a783019b 100644 --- a/plugins/main/public/components/settings/about/__snapshots__/appInfo.test.tsx.snap +++ b/plugins/main/public/components/settings/about/__snapshots__/appInfo.test.tsx.snap @@ -20,7 +20,8 @@ exports[`SettingsAboutAppInfo component should render version, revision, install
- App version: + App version: + 4.8.0 @@ -32,22 +33,10 @@ exports[`SettingsAboutAppInfo component should render version, revision, install
- App revision: - - 01 - -
-
-
-
- Install date: + App revision: - Sep 25, 2023 @ 14:03:40.816 + 01
diff --git a/plugins/main/public/components/settings/about/appInfo.test.tsx b/plugins/main/public/components/settings/about/appInfo.test.tsx index 20573642ef..568a5f57ac 100644 --- a/plugins/main/public/components/settings/about/appInfo.test.tsx +++ b/plugins/main/public/components/settings/about/appInfo.test.tsx @@ -14,9 +14,8 @@ describe('SettingsAboutAppInfo component', () => { appInfo={{ 'app-version': '4.8.0', revision: '01', - installationDate: 'Sep 25, 2023 @ 14:03:40.816', }} - /> + />, ); expect(container).toMatchSnapshot(); @@ -25,7 +24,5 @@ describe('SettingsAboutAppInfo component', () => { expect(getByText('4.8.0')).toBeInTheDocument(); expect(getByText('App revision:')).toBeInTheDocument(); expect(getByText('01')).toBeInTheDocument(); - expect(getByText('Install date:')).toBeInTheDocument(); - expect(getByText('Sep 25, 2023 @ 14:03:40.816')).toBeInTheDocument(); }); }); diff --git a/plugins/main/public/components/settings/about/appInfo.tsx b/plugins/main/public/components/settings/about/appInfo.tsx index 0958f90447..d8cd7838c5 100644 --- a/plugins/main/public/components/settings/about/appInfo.tsx +++ b/plugins/main/public/components/settings/about/appInfo.tsx @@ -1,33 +1,33 @@ import { EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; import React from 'react'; -import { formatUIDate } from '../../../react-services/time-service'; interface SettingsAboutAppInfoProps { appInfo?: { 'app-version': string; - installationDate: string; revision: string; }; } -export const SettingsAboutAppInfo = ({ appInfo }: SettingsAboutAppInfoProps) => { +export const SettingsAboutAppInfo = ({ + appInfo, +}: SettingsAboutAppInfoProps) => { return ( - + - App version: {appInfo?.['app-version'] ? appInfo['app-version'] : ''} + App version:{' '} + {appInfo?.['app-version'] ? appInfo['app-version'] : ''} - App revision: {appInfo?.['revision'] ? appInfo['revision'] : ''} - - - - - Install date:{' '} - {appInfo?.['installationDate'] ? formatUIDate(appInfo['installationDate']) : ''} + App revision:{' '} + {appInfo?.['revision'] ? appInfo['revision'] : ''} diff --git a/plugins/main/public/components/settings/about/index.test.tsx b/plugins/main/public/components/settings/about/index.test.tsx index 5e05548cbb..f618e8899f 100644 --- a/plugins/main/public/components/settings/about/index.test.tsx +++ b/plugins/main/public/components/settings/about/index.test.tsx @@ -18,7 +18,6 @@ describe('SettingsAbout component', () => { appInfo={{ 'app-version': '4.8.0', revision: '01', - installationDate: 'Sep 25, 2023 @ 14:03:40.816', }} pluginAppName='Dashboard' />, diff --git a/plugins/main/public/components/settings/about/index.tsx b/plugins/main/public/components/settings/about/index.tsx index 133c099a2a..c8e7c8bb05 100644 --- a/plugins/main/public/components/settings/about/index.tsx +++ b/plugins/main/public/components/settings/about/index.tsx @@ -6,7 +6,6 @@ import { SettingsAboutGeneralInfo } from './generalInfo'; interface SettingsAboutProps { appInfo?: { 'app-version': string; - installationDate: string; revision: string; }; pluginAppName: string; @@ -16,10 +15,10 @@ export const SettingsAbout = (props: SettingsAboutProps) => { const { appInfo, pluginAppName } = props; return ( - + - + diff --git a/plugins/main/public/components/settings/api/api-table.js b/plugins/main/public/components/settings/api/api-table.js index c3b6de9671..c2a467196e 100644 --- a/plugins/main/public/components/settings/api/api-table.js +++ b/plugins/main/public/components/settings/api/api-table.js @@ -147,10 +147,6 @@ export const ApiTable = compose( true, ); APIConnection.cluster_info = response.data; - // Updates the cluster-information in the registry - await GenericRequest.request('PUT', `/hosts/update-hostname/${id}`, { - cluster_info: APIConnection.cluster_info, - }); APIConnection.status = 'online'; APIConnection.allow_run_as = response.data.allow_run_as; !silent && ErrorHandler.info('Connection success', 'Settings'); @@ -218,14 +214,7 @@ export const ApiTable = compose( const clusterInfo = data.data || {}; APIconnection.status = 'online'; APIconnection.cluster_info = clusterInfo; - //Updates the cluster info in the registry - await GenericRequest.request( - 'PUT', - `/hosts/update-hostname/${APIconnection.id}`, - { - cluster_info: clusterInfo, - }, - ); + APIconnection.allow_run_as = clusterInfo.allow_run_as; if (options?.selectAPIHostOnAvailable) { this.setDefault(entry); } diff --git a/plugins/main/public/components/wz-menu/wz-menu.js b/plugins/main/public/components/wz-menu/wz-menu.js index 88ac359cc7..114509588d 100644 --- a/plugins/main/public/components/wz-menu/wz-menu.js +++ b/plugins/main/public/components/wz-menu/wz-menu.js @@ -354,22 +354,6 @@ export const WzMenu = withWindowSize( }); }; - /** - * @param {String} id - * @param {Object} clusterInfo - * Updates the wazuh registry of an specific api id - */ - updateClusterInfoInRegistry = async (id, clusterInfo) => { - try { - const url = `/hosts/update-hostname/${id}`; - await this.genericReq.request('PUT', url, { - cluster_info: clusterInfo, - }); - } catch (error) { - return Promise.reject(error); - } - }; - changeAPI = async event => { try { const apiId = event.target[event.target.selectedIndex]; @@ -382,7 +366,6 @@ export const WzMenu = withWindowSize( return item.id === apiId.value; }); - this.updateClusterInfoInRegistry(apiId.value, clusterInfo); apiData[0].cluster_info = clusterInfo; AppState.setClusterInfo(apiData[0].cluster_info); diff --git a/plugins/main/public/controllers/settings/settings.js b/plugins/main/public/controllers/settings/settings.js index a0c633b5e5..b2533377ad 100644 --- a/plugins/main/public/controllers/settings/settings.js +++ b/plugins/main/public/controllers/settings/settings.js @@ -125,8 +125,6 @@ export class SettingsController { checkManager: entry => this.checkManager(entry), getHosts: () => this.getHosts(), testApi: (entry, force) => ApiCheck.checkApi(entry, force), - updateClusterInfoInRegistry: (id, clusterInfo) => - this.updateClusterInfoInRegistry(id, clusterInfo), copyToClipBoard: msg => this.copyToClipBoard(msg), }; @@ -306,28 +304,9 @@ export class SettingsController { }; getErrorOrchestrator().handleError(options); } - // Every time that the API entries are required in the settings the registry will be checked in order to remove orphan host entries - await this.genericReq.request('POST', '/hosts/remove-orphan-entries', { - entries: this.apiEntries, - }); return; } - /** - * @param {String} id - * @param {Object} clusterInfo - */ - async updateClusterInfoInRegistry(id, clusterInfo) { - try { - const url = `/hosts/update-hostname/${id}`; - await this.genericReq.request('PUT', url, { - cluster_info: clusterInfo, - }); - } catch (error) { - return Promise.reject(error); - } - } - // Check manager connectivity async checkManager(item, isIndex, silent = false) { try { @@ -351,7 +330,6 @@ export class SettingsController { tmpData.cluster_info = data.data; const { cluster_info } = tmpData; // Updates the cluster-information in the registry - await this.updateClusterInfoInRegistry(id, cluster_info); this.$scope.$emit('updateAPI', { cluster_info }); this.apiEntries[index].cluster_info = cluster_info; this.apiEntries[index].status = 'online'; @@ -399,7 +377,6 @@ export class SettingsController { const response = data.data.data; this.appInfo = { 'app-version': response['app-version'], - installationDate: response['installationDate'], revision: response['revision'], }; diff --git a/plugins/main/public/services/resolves/check-timestamp.js b/plugins/main/public/services/resolves/check-timestamp.js deleted file mode 100644 index bb46e811a3..0000000000 --- a/plugins/main/public/services/resolves/check-timestamp.js +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Wazuh app - Module to check cookie consistence - * 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 { AppState } from '../../react-services/app-state'; - -export async function checkTimestamp(genericReq, $location, wzMisc) { - try { - const data = await genericReq.request('GET', '/api/timestamp'); - const current = AppState.getCreatedAt(); - if (data && data.data) { - if (!current) AppState.setCreatedAt(data.data.lastRestart); - wzMisc.setLastRestart(data.data.lastRestart); - } else { - wzMisc.setBlankScr('Your wazuh-registry.json is empty or corrupt.'); - $location.search('tab', null); - $location.path('/blank-screen'); - } - return; - } catch (error) { - wzMisc.setBlankScr(error.message || error); - $location.search('tab', null); - $location.path('/blank-screen'); - throw error; - } -} diff --git a/plugins/main/public/services/resolves/index.js b/plugins/main/public/services/resolves/index.js index 47c9ab8632..1fc9225d3f 100644 --- a/plugins/main/public/services/resolves/index.js +++ b/plugins/main/public/services/resolves/index.js @@ -9,18 +9,10 @@ * * Find more information about this on the LICENSE file. */ -import { checkTimestamp } from './check-timestamp'; import { healthCheck } from './health-check'; import { settingsWizard } from './settings-wizard'; import { getSavedSearch } from './get-saved-search'; import { getIp } from './get-ip'; import { getWzConfig } from './get-config'; -export { - checkTimestamp, - healthCheck, - settingsWizard, - getSavedSearch, - getIp, - getWzConfig, -}; +export { healthCheck, settingsWizard, getSavedSearch, getIp, getWzConfig }; diff --git a/plugins/main/public/utils/check-plugin-version.tsx b/plugins/main/public/utils/check-plugin-version.tsx index a7ba8e1afc..22c544f693 100644 --- a/plugins/main/public/utils/check-plugin-version.tsx +++ b/plugins/main/public/utils/check-plugin-version.tsx @@ -12,7 +12,10 @@ import { GenericRequest } from '../react-services/generic-request'; import { AxiosResponse } from 'axios'; import _ from 'lodash'; -import { version as appVersion, revision as appRevision } from '../../package.json'; +import { + version as appVersion, + revision as appRevision, +} from '../../package.json'; import { getCookies, getToasts } from '../kibana-services'; import { ErrorToastOptions } from 'opensearch_dashboards/public'; import React from 'react'; @@ -32,10 +35,8 @@ type TAppInfoResponse = { export const checkPluginVersion = async () => { try { - const response: AxiosResponse = await GenericRequest.request( - 'GET', - '/api/setup' - ); + const response: AxiosResponse = + await GenericRequest.request('GET', '/api/setup'); const { revision, 'app-version': appRevision } = response.data.data; return checkClientAppVersion({ revision, 'app-version': appRevision }); } catch (error) { @@ -44,13 +45,18 @@ export const checkPluginVersion = async () => { }; const checkClientAppVersion = (appInfo: TAppInfo) => { - if (appInfo['app-version'] !== appVersion || appInfo.revision !== appRevision) { + if ( + appInfo['app-version'] !== appVersion || + appInfo.revision !== appRevision + ) { const toastOptions: ErrorToastOptions = { title: `Conflict with the ${PLUGIN_APP_NAME} version`, toastLifeTimeMs: 50000, toastMessage: `The version of the ${PLUGIN_APP_NAME} in your browser does not correspond with the app version installed in ${PLUGIN_PLATFORM_NAME}. Please, clear your browser cache. For more info check the full error.`, }; - const troubleshootingUrl = webDocumentationLink('user-manual/elasticsearch/troubleshooting.html'); + const troubleshootingUrl = webDocumentationLink( + 'user-manual/elasticsearch/troubleshooting.html', + ); const message: ReactNode = ( <>

@@ -58,7 +64,8 @@ const checkClientAppVersion = (appInfo: TAppInfo) => { {appVersion} - {appRevision} {' '} - does not correspond with the version installed in {PLUGIN_PLATFORM_NAME}{' '} + does not correspond with the version installed in{' '} + {PLUGIN_PLATFORM_NAME}{' '} {appInfo['app-version']} - {appInfo.revision} @@ -70,8 +77,8 @@ const checkClientAppVersion = (appInfo: TAppInfo) => { For more information check our troubleshooting section{' '} here. @@ -119,7 +126,7 @@ This message should not be displayed again.`; clearBrowserInfo(appInfo); } else { if (window.history.state == 'refreshed') { - window.history.replaceState('', 'wazuh'); + window.history.replaceState('', 'wazuh'); // TODO: this seems to redirect to old plugin path } const storeAppInfo = localStorage.getItem('appInfo'); !storeAppInfo && updateAppInfo(appInfo); @@ -130,7 +137,7 @@ function clearBrowserInfo(appInfo: TAppInfo) { console.warn('Clearing browser cache'); //remove cookies const cookies = getCookies().getAll(); - Object.keys(cookies).forEach((cookie) => getCookies().remove(cookie)); + Object.keys(cookies).forEach(cookie => getCookies().remove(cookie)); //remove cache if (window.caches) { diff --git a/plugins/main/server/controllers/wazuh-api.ts b/plugins/main/server/controllers/wazuh-api.ts index 182213dd13..b350fca828 100644 --- a/plugins/main/server/controllers/wazuh-api.ts +++ b/plugins/main/server/controllers/wazuh-api.ts @@ -18,7 +18,6 @@ import { ApiErrorEquivalence } from '../lib/api-errors-equivalence'; import apiRequestList from '../../common/api-info/endpoints'; import { HTTP_STATUS_CODES } from '../../common/constants'; import { addJobToQueue } from '../start/queue'; -import fs from 'fs'; import jwtDecode from 'jwt-decode'; import { OpenSearchDashboardsRequest, @@ -26,6 +25,10 @@ import { OpenSearchDashboardsResponseFactory, } from 'src/core/server'; import { getCookieValueByName } from '../lib/cookie'; +import { + version as pluginVersion, + revision as pluginRevision, +} from '../../package.json'; export class WazuhApiCtrl { constructor() {} @@ -72,13 +75,7 @@ export class WazuhApiCtrl { } } let token; - if ( - (await context.wazuh_core.manageHosts.cacheAPIUserAllowRunAs.canUse( - idHost, - )) === - context.wazuh_core.manageHosts.cacheAPIUserAllowRunAs - .API_USER_STATUS_RUN_AS.ENABLED - ) { + if (context.wazuh_core.manageHosts.isEnabledAuthWithRunAs(idHost)) { token = await context.wazuh.api.client.asCurrentUser.authenticate( idHost, ); @@ -232,7 +229,7 @@ export class WazuhApiCtrl { if (api.cluster_info) { // Update cluster information in the wazuh-registry.json - await context.wazuh_core.updateRegistry.updateClusterInfo( + await context.wazuh_core.manageHosts.updateRegistryByHost( id, api.cluster_info, ); @@ -409,106 +406,11 @@ export class WazuhApiCtrl { responseManagerInfo.status === HTTP_STATUS_CODES.OK && responseManagerInfo.data ) { - let responseAgents = - await context.wazuh.api.client.asInternalUser.request( - 'GET', - `/agents`, - { params: { agents_list: '000' } }, - { apiHostID: request.body.id }, - ); - - if (responseAgents.status === HTTP_STATUS_CODES.OK) { - const managerName = - responseAgents.data.data.affected_items[0].manager; - - let responseCluster = - await context.wazuh.api.client.asInternalUser.request( - 'GET', - `/cluster/status`, - {}, - { apiHostID: request.body.id }, - ); - - // Check the run_as for the API user and update it - let apiUserAllowRunAs = - context.wazuh_core.manageHosts.cacheAPIUserAllowRunAs - .API_USER_STATUS_RUN_AS.ALL_DISABLED; - const responseApiUserAllowRunAs = - await context.wazuh.api.client.asInternalUser.request( - 'GET', - `/security/users/me`, - {}, - { apiHostID: request.body.id }, - ); - if (responseApiUserAllowRunAs.status === HTTP_STATUS_CODES.OK) { - const allow_run_as = - responseApiUserAllowRunAs.data.data.affected_items[0] - .allow_run_as; - - if (allow_run_as && apiAvailable && apiAvailable.run_as) - // HOST AND USER ENABLED - apiUserAllowRunAs = - context.wazuh_core.manageHosts.cacheAPIUserAllowRunAs - .API_USER_STATUS_RUN_AS.ENABLED; - else if (!allow_run_as && apiAvailable && apiAvailable.run_as) - // HOST ENABLED AND USER DISABLED - apiUserAllowRunAs = - context.wazuh_core.manageHosts.cacheAPIUserAllowRunAs - .API_USER_STATUS_RUN_AS.USER_NOT_ALLOWED; - else if (allow_run_as && (!apiAvailable || !apiAvailable.run_as)) - // USER ENABLED AND HOST DISABLED - apiUserAllowRunAs = - context.wazuh_core.manageHosts.cacheAPIUserAllowRunAs - .API_USER_STATUS_RUN_AS.HOST_DISABLED; - else if (!allow_run_as && (!apiAvailable || !apiAvailable.run_as)) - // HOST AND USER DISABLED - apiUserAllowRunAs = - context.wazuh_core.manageHosts.cacheAPIUserAllowRunAs - .API_USER_STATUS_RUN_AS.ALL_DISABLED; - } - context.wazuh_core.manageHosts.cacheAPIUserAllowRunAs.set( - request.body.id, - apiAvailable.username, - apiUserAllowRunAs, - ); - - if (responseCluster.status === HTTP_STATUS_CODES.OK) { - context.wazuh.logger.debug('Server API response is valid'); - if (responseCluster.data.data.enabled === 'yes') { - // If cluster mode is active - let responseClusterLocal = - await context.wazuh.api.client.asInternalUser.request( - 'GET', - `/cluster/local/info`, - {}, - { apiHostID: request.body.id }, - ); - - if (responseClusterLocal.status === HTTP_STATUS_CODES.OK) { - return response.ok({ - body: { - manager: managerName, - node: responseClusterLocal.data.data.affected_items[0].node, - cluster: - responseClusterLocal.data.data.affected_items[0].cluster, - status: 'enabled', - allow_run_as: apiUserAllowRunAs, - }, - }); - } - } else { - // Cluster mode is not active - return response.ok({ - body: { - manager: managerName, - cluster: 'Disabled', - status: 'disabled', - allow_run_as: apiUserAllowRunAs, - }, - }); - } - } - } + const result = + await context.wazuh_core.manageHosts.getRegistryDataByHost(data); + return response.ok({ + body: result, + }); } } catch (error) { context.wazuh.logger.warn(error.message || error); @@ -1105,46 +1007,6 @@ export class WazuhApiCtrl { }); } - /** - * This get the timestamp field - * @param {Object} context - * @param {Object} request - * @param {Object} response - * @returns {Object} timestamp field or ErrorResponse - */ - getTimeStamp( - context: RequestHandlerContext, - request: OpenSearchDashboardsRequest, - response: OpenSearchDashboardsResponseFactory, - ) { - try { - const source = JSON.parse( - fs.readFileSync(context.wazuh_core.updateRegistry.file, 'utf8'), - ); - if (source.installationDate && source.lastRestart) { - context.wazuh.logger.debug( - `Installation date: ${source.installationDate}. Last restart: ${source.lastRestart}`, - ); - return response.ok({ - body: { - installationDate: source.installationDate, - lastRestart: source.lastRestart, - }, - }); - } else { - throw new Error('Could not fetch wazuh-version registry'); - } - } catch (error) { - context.wazuh.logger.error(error.message || error); - return ErrorResponse( - error.message || 'Could not fetch wazuh-version registry', - 4001, - HTTP_STATUS_CODES.INTERNAL_SERVER_ERROR, - response, - ); - } - } - /** * This get the wazuh setup settings * @param {Object} context @@ -1158,13 +1020,13 @@ export class WazuhApiCtrl { response: OpenSearchDashboardsResponseFactory, ) { try { - const source = JSON.parse( - fs.readFileSync(context.wazuh_core.updateRegistry.file, 'utf8'), - ); return response.ok({ body: { statusCode: HTTP_STATUS_CODES.OK, - data: !Object.values(source).length ? '' : source, + data: { + 'app-version': pluginVersion, + revision: pluginRevision, + }, }, }); } catch (error) { diff --git a/plugins/main/server/controllers/wazuh-hosts.ts b/plugins/main/server/controllers/wazuh-hosts.ts index 48e531270e..25cc0b9654 100644 --- a/plugins/main/server/controllers/wazuh-hosts.ts +++ b/plugins/main/server/controllers/wazuh-hosts.ts @@ -46,73 +46,6 @@ export class WazuhHostsCtrl { } } - /** - * This update an API hostname - * @param {Object} context - * @param {Object} request - * @param {Object} response - * Status response or ErrorResponse - */ - async updateClusterInfo( - context: RequestHandlerContext, - request: OpenSearchDashboardsRequest, - response: OpenSearchDashboardsResponseFactory, - ) { - try { - const { id } = request.params; - const { cluster_info } = request.body; - await context.wazuh_core.updateRegistry.updateClusterInfo( - id, - cluster_info, - ); - context.wazuh.logger.info(`Server API host entry ${id} updated`); - return response.ok({ - body: { statusCode: 200, message: 'ok' }, - }); - } catch (error) { - context.wazuh.logger.error(error.message || error); - return ErrorResponse( - `Could not update data in wazuh-registry.json due to ${ - error.message || error - }`, - 2012, - 500, - response, - ); - } - } - - /** - * Remove the orphan host entries in the registry - * @param {Object} context - * @param {Object} request - * @param {Object} response - */ - async removeOrphanEntries( - context: RequestHandlerContext, - request: OpenSearchDashboardsRequest, - response: OpenSearchDashboardsResponseFactory, - ) { - try { - const { entries } = request.body; - context.wazuh.logger.debug('Cleaning registry file'); - await context.wazuh_core.updateRegistry.removeOrphanEntries(entries); - return response.ok({ - body: { statusCode: 200, message: 'ok' }, - }); - } catch (error) { - context.wazuh.logger.error(error.message || error); - return ErrorResponse( - `Could not clean entries in the wazuh-registry.json due to ${ - error.message || error - }`, - 2013, - 500, - response, - ); - } - } - /** * Create or update the API host data stored in the configuration. * Allow partial updates. diff --git a/plugins/main/server/routes/wazuh-api.test.ts b/plugins/main/server/routes/wazuh-api.test.ts index f6613cfa4f..964af66fe6 100644 --- a/plugins/main/server/routes/wazuh-api.test.ts +++ b/plugins/main/server/routes/wazuh-api.test.ts @@ -198,7 +198,6 @@ describe.skip('Wazuh API', () => { expect(typeof response.data[0].endpoints[0]).toBe('object'); expect( Object.keys(response.data[0].endpoints[0]).every(key => [ - 'name', 'documentation', 'description', 'summary', @@ -220,22 +219,15 @@ describe.skip('Wazuh API', () => { expect(typeof response.data.data).toBe('object'); expect( Object.keys(response.data.data).every(key => [ - 'name', 'app-version', 'revision', - 'installationDate', - 'lastRestart', 'hosts', ]), ).toBe(true); expect( - [ - 'name', - 'app-version', - 'revision', - 'installationDate', - 'lastRestart', - ].every(key => typeof response.data.data[key] === 'string'), + ['app-version', 'revision'].every( + key => typeof response.data.data[key] === 'string', + ), ).toBe(true); expect(typeof response.data.data.hosts).toBe('object'); }) diff --git a/plugins/main/server/routes/wazuh-api.ts b/plugins/main/server/routes/wazuh-api.ts index 464d716a06..9e0e2cc1bd 100644 --- a/plugins/main/server/routes/wazuh-api.ts +++ b/plugins/main/server/routes/wazuh-api.ts @@ -104,16 +104,6 @@ export function WazuhApiRoutes(router: IRouter) { ctrl.getRequestList(context, request, response), ); - // Useful to check cookie consistence - router.get( - { - path: '/api/timestamp', - validate: false, - }, - async (context, request, response) => - ctrl.getTimeStamp(context, request, response), - ); - // Return Wazuh Appsetup info router.get( { diff --git a/plugins/main/server/routes/wazuh-hosts.ts b/plugins/main/server/routes/wazuh-hosts.ts index a6205e8de4..bf3d1ebe9e 100644 --- a/plugins/main/server/routes/wazuh-hosts.ts +++ b/plugins/main/server/routes/wazuh-hosts.ts @@ -26,23 +26,6 @@ export function WazuhHostsRoutes(router: IRouter, services) { ctrl.getHostsEntries(context, request, response), ); - // Updates the cluster-info or manager-info - router.put( - { - path: '/hosts/update-hostname/{id}', - validate: { - params: schema.object({ - id: schema.string(), - }), - body: schema.object({ - cluster_info: schema.any(), - }), - }, - }, - async (context, request, response) => - ctrl.updateClusterInfo(context, request, response), - ); - // Create the API host entry router.post( { @@ -144,18 +127,4 @@ export function WazuhHostsRoutes(router: IRouter, services) { async (context, request, response) => ctrl.deleteAPIHost(context, request, response), ); - - // Checks the orphan hosts in the registry in order to delete them - router.post( - { - path: '/hosts/remove-orphan-entries', - validate: { - body: schema.object({ - entries: schema.arrayOf(schema.any()), - }), - }, - }, - async (context, request, response) => - ctrl.removeOrphanEntries(context, request, response), - ); } diff --git a/plugins/main/server/start/initialize/index.test.ts b/plugins/main/server/start/initialize/index.test.ts deleted file mode 100644 index 595d28eb99..0000000000 --- a/plugins/main/server/start/initialize/index.test.ts +++ /dev/null @@ -1,322 +0,0 @@ -import fs from 'fs'; -import { execSync } from 'child_process'; -import { jobInitializeRun } from './index'; -import { - createDataDirectoryIfNotExists, - createDirectoryIfNotExists, -} from '../../lib/filesystem'; -import { - WAZUH_DATA_ABSOLUTE_PATH, - WAZUH_DATA_CONFIG_DIRECTORY_PATH, - WAZUH_DATA_CONFIG_REGISTRY_PATH, -} from '../../../common/constants'; -import packageInfo from '../../../package.json'; - -function mockContextCreator(loggerLevel: string) { - const logs = []; - const levels = ['debug', 'info', 'warn', 'error']; - - function createLogger(level: string) { - return jest.fn(function (message: string) { - const levelLogIncluded: number = levels.findIndex( - level => level === loggerLevel, - ); - levelLogIncluded > -1 && - levels.slice(levelLogIncluded).includes(level) && - logs.push({ level, message }); - }); - } - - const ctx = { - wazuh: { - logger: { - info: createLogger('info'), - warn: createLogger('warn'), - error: createLogger('error'), - debug: createLogger('debug'), - }, - }, - server: { - config: { - opensearchDashboards: { - index: '.kibana', - }, - }, - }, - core: { - opensearch: { - client: { - asInternalUser: { - indices: { - exists: jest.fn(() => ({ body: true })), - }, - }, - }, - }, - }, - /* Mocked logs getter. It is only for testing purpose.*/ - _getLogs(logLevel: string) { - return logLevel ? logs.filter(({ level }) => level === logLevel) : logs; - }, - }; - return ctx; -} - -beforeAll(() => { - // Create /data/wazuh directory. - createDataDirectoryIfNotExists(); - // Create /data/wazuh/config directory. - createDirectoryIfNotExists(WAZUH_DATA_CONFIG_DIRECTORY_PATH); -}); - -afterAll(() => { - // Remove /data/wazuh directory. - execSync(`rm -rf ${WAZUH_DATA_ABSOLUTE_PATH}`); -}); - -describe('[initialize] `wazuh-registry.json` not created', () => { - let mockContext = mockContextCreator('debug'); - afterEach(() => { - // Remove /data/wazuh/config/wazuh-registry file. - execSync( - `rm ${WAZUH_DATA_ABSOLUTE_PATH}/config/wazuh-registry.json || echo ""`, - ); - }); - - it('Create registry file with plugin data and empty hosts', async () => { - // Migrate the directories - await jobInitializeRun(mockContext); - const contentRegistry = JSON.parse( - fs.readFileSync(WAZUH_DATA_CONFIG_REGISTRY_PATH, 'utf8'), - ); - - expect(contentRegistry.name).toMatch('dashboard'); - expect(contentRegistry['app-version']).toMatch(packageInfo.version); - expect(contentRegistry['revision']).toMatch(packageInfo.revision); - expect(typeof contentRegistry.installationDate).toBe('string'); - expect(typeof contentRegistry.lastRestart).toBe('string'); - expect(Object.keys(contentRegistry.hosts)).toHaveLength(0); - }); -}); - -describe('[initialize] `wazuh-registry.json` created', () => { - let testID = 0; - const contentRegistryFile = [ - { - before: { - name: 'dashboard', - 'app-version': packageInfo.version, - revision: packageInfo.revision, - installationDate: '2022-07-25T13:55:04.363Z', - lastRestart: '2022-07-25T13:55:04.363Z', - hosts: {}, - }, - after: { - name: 'dashboard', - 'app-version': packageInfo.version, - revision: packageInfo.revision, - installationDate: '2022-07-25T13:55:04.363Z', - lastRestart: '2022-07-25T13:55:04.363Z', - hosts: {}, - }, - }, - { - before: { - name: 'dashboard', - 'app-version': '0.0.0', - revision: '0', - installationDate: '2022-07-25T13:55:04.363Z', - lastRestart: '2022-07-25T13:55:04.363Z', - hosts: {}, - }, - after: { - name: 'dashboard', - 'app-version': packageInfo.version, - revision: packageInfo.revision, - installationDate: '2022-07-25T13:55:04.363Z', - lastRestart: '2022-07-25T13:55:04.363Z', - hosts: {}, - }, - }, - { - before: { - name: 'dashboard', - 'app-version': '0.0.0', - revision: '0', - installationDate: '2022-07-25T13:55:04.363Z', - lastRestart: '2022-07-25T13:55:04.363Z', - hosts: { - default: { - extensions: { - pci: true, - gdpr: true, - hipaa: true, - nist: true, - tsc: true, - audit: true, - oscap: false, - ciscat: false, - aws: false, - office: false, - github: false, - gcp: false, - virustotal: false, - osquery: false, - docker: false, - }, - }, - }, - }, - after: { - name: 'dashboard', - 'app-version': packageInfo.version, - revision: packageInfo.revision, - installationDate: '2022-07-25T13:55:04.363Z', - lastRestart: '2022-07-25T13:55:04.363Z', - hosts: { - default: {}, - }, - }, - }, - { - before: { - name: 'dashboard', - 'app-version': '0.0.0', - revision: '0', - installationDate: '2022-07-25T13:55:04.363Z', - lastRestart: '2022-07-25T13:55:04.363Z', - hosts: { - default: { - extensions: { - pci: true, - gdpr: true, - hipaa: true, - nist: true, - tsc: true, - audit: true, - oscap: false, - ciscat: false, - aws: false, - office: false, - github: false, - gcp: false, - virustotal: false, - osquery: false, - docker: false, - }, - }, - default2: { - prop1: 3, - prop2: 8, - extensions: { - pci: true, - gdpr: true, - hipaa: true, - nist: true, - tsc: true, - audit: true, - oscap: false, - ciscat: false, - aws: false, - office: false, - github: false, - gcp: false, - virustotal: false, - osquery: false, - docker: false, - }, - }, - custom: { - extensions: { - pci: true, - gdpr: true, - hipaa: true, - nist: true, - tsc: true, - audit: true, - oscap: false, - ciscat: false, - aws: false, - office: false, - github: false, - gcp: false, - virustotal: false, - osquery: false, - docker: false, - }, - }, - }, - }, - after: { - name: 'dashboard', - 'app-version': packageInfo.version, - revision: packageInfo.revision, - installationDate: '2022-07-25T13:55:04.363Z', - lastRestart: '2022-07-25T13:55:04.363Z', - hosts: { - default: {}, - custom: {}, - default2: { - prop1: 3, - prop2: 8, - }, - }, - }, - }, - ]; - - beforeEach(() => { - // Remove /data/wazuh/config/wazuh-registry.json. - execSync( - `rm ${WAZUH_DATA_ABSOLUTE_PATH}/config/wazuh-registry.json || echo ""`, - ); - // Create the wazuh-registry.json file. - fs.writeFileSync( - WAZUH_DATA_CONFIG_REGISTRY_PATH, - JSON.stringify(contentRegistryFile[testID].before), - 'utf8', - ); - testID++; - }); - - it.each` - titleTest | contentRegistryFile - ${'Registry file is not rebuilt due version and revision match'} | ${JSON.stringify(contentRegistryFile[0].after)} - ${'Registry file is rebuilt due to version/revision changed'} | ${JSON.stringify(contentRegistryFile[1].after)} - ${'Registry file is rebuilt due to version/revision changed and host extensions has been deleted'} | ${JSON.stringify(contentRegistryFile[2].after)} - ${'Registry file is rebuilt due to version/revision changed and host(multiples) extensions has been deleted'} | ${JSON.stringify(contentRegistryFile[3].after)} - `( - `$titleTest: - content: $contentRegistryFile`, - async ({ contentRegistryFile: content }) => { - const mockContext = mockContextCreator('debug'); - - const contentRegistryExpected = JSON.parse(content); - await jobInitializeRun(mockContext); - const contentRegistryFile = JSON.parse( - fs.readFileSync(WAZUH_DATA_CONFIG_REGISTRY_PATH, 'utf8'), - ); - - expect(contentRegistryFile.name).toMatch('dashboard'); - expect(contentRegistryFile['app-version']).toMatch( - contentRegistryExpected['app-version'], - ); - expect(contentRegistryFile['revision']).toMatch( - contentRegistryExpected.revision, - ); - expect(typeof contentRegistryFile.installationDate).toBe('string'); - expect(typeof contentRegistryFile.lastRestart).toBe('string'); - expect(Object.keys(contentRegistryFile.hosts)).toHaveLength( - Object.keys(contentRegistryExpected.hosts).length, - ); - Object.keys(contentRegistryFile.hosts).forEach(element => { - expect( - contentRegistryFile.hosts[element]['extensions'], - ).toBeUndefined(); - expect(contentRegistryFile.hosts[element]).toEqual( - contentRegistryExpected.hosts[element], - ); - }); - }, - ); -}); diff --git a/plugins/main/server/start/initialize/index.ts b/plugins/main/server/start/initialize/index.ts index a90df44dc5..0fb15db05f 100644 --- a/plugins/main/server/start/initialize/index.ts +++ b/plugins/main/server/start/initialize/index.ts @@ -12,17 +12,10 @@ import packageJSON from '../../../package.json'; import { pluginPlatformTemplate } from '../../integration-files/kibana-template'; import { totalmem } from 'os'; -import fs from 'fs'; import { - WAZUH_DATA_CONFIG_REGISTRY_PATH, WAZUH_PLUGIN_PLATFORM_TEMPLATE_NAME, - WAZUH_DATA_PLUGIN_PLATFORM_BASE_ABSOLUTE_PATH, PLUGIN_PLATFORM_NAME, - PLUGIN_PLATFORM_INSTALLATION_USER_GROUP, - PLUGIN_PLATFORM_INSTALLATION_USER, - PLUGIN_APP_NAME, } from '../../../common/constants'; -import { createDataDirectoryIfNotExists } from '../../lib/filesystem'; import _ from 'lodash'; export function jobInitializeRun(context) { @@ -44,107 +37,6 @@ export function jobInitializeRun(context) { ); } - // Save Wazuh App setup - const saveConfiguration = async (hosts = {}) => { - try { - const commonDate = new Date().toISOString(); - const configuration = { - name: PLUGIN_APP_NAME, - 'app-version': packageJSON.version, - revision: packageJSON.revision, - installationDate: commonDate, - lastRestart: commonDate, - hosts, - }; - context.wazuh.logger.debug('Saving the configuration'); - createDataDirectoryIfNotExists(); - createDataDirectoryIfNotExists('config'); - context.wazuh.logger.debug( - `Saving configuration in registry file: ${JSON.stringify( - configuration, - )}`, - ); - await fs.writeFileSync( - WAZUH_DATA_CONFIG_REGISTRY_PATH, - JSON.stringify(configuration), - 'utf8', - ); - context.wazuh.logger.info('Configuration registry saved.'); - } catch (error) { - context.wazuh.logger.error( - `Error creating the registry file: ${error.message}`, - ); - } - }; - - /** - * Checks if the .wazuh-registry.json file exists: - * - yes: check the plugin version and revision match the values stored in the registry file. - * If not, then it migrates the data rebuilding the registry file. - * - no: create the file with empty hosts - */ - const checkWazuhRegistry = async () => { - context.wazuh.logger.debug('Checking the existence app data directory.'); - - if (!fs.existsSync(WAZUH_DATA_PLUGIN_PLATFORM_BASE_ABSOLUTE_PATH)) { - throw new Error( - `The data directory is missing in the ${PLUGIN_PLATFORM_NAME} root instalation. Create the directory in ${WAZUH_DATA_PLUGIN_PLATFORM_BASE_ABSOLUTE_PATH} and give it the required permissions (sudo mkdir ${WAZUH_DATA_PLUGIN_PLATFORM_BASE_ABSOLUTE_PATH};sudo chown -R ${PLUGIN_PLATFORM_INSTALLATION_USER}:${PLUGIN_PLATFORM_INSTALLATION_USER_GROUP} ${WAZUH_DATA_PLUGIN_PLATFORM_BASE_ABSOLUTE_PATH}). After restart the ${PLUGIN_PLATFORM_NAME} service.`, - ); - } - - context.wazuh.logger.debug('Checking the existence of registry file.'); - - if (!fs.existsSync(WAZUH_DATA_CONFIG_REGISTRY_PATH)) { - context.wazuh.logger.debug( - 'Registry file does not exist. Initializing configuration.', - ); - - // Create the app registry file for the very first time - await saveConfiguration(); - } else { - context.wazuh.logger.debug('Reading the registry file'); - // If this function fails, it throws an exception - const source = JSON.parse( - fs.readFileSync(WAZUH_DATA_CONFIG_REGISTRY_PATH, 'utf8'), - ); - context.wazuh.logger.debug('The registry file was read'); - - // Check if the stored revision differs from the package.json revision - const isUpgradedApp = - packageJSON.revision !== source.revision || - packageJSON.version !== source['app-version']; - - // Rebuild the registry file if revision or version fields are differents - if (isUpgradedApp) { - context.wazuh.logger.info( - 'App revision or version changed, regenerating registry file', - ); - // Generate the hosts data. - const registryHostsData = Object.entries(source.hosts).reduce( - (accum, [hostID, hostData]) => { - // Migration: Remove the extensions property of the hosts data. - if (hostData.extensions) { - delete hostData.extensions; - } - accum[hostID] = hostData; - return accum; - }, - {}, - ); - - // Rebuild the registry file with the migrated host data - await saveConfiguration(registryHostsData); - - context.wazuh.logger.info('Migrated the registry file.'); - } - } - }; - - // Init function. Check for wazuh-registry.json file exists. - const init = async () => { - await checkWazuhRegistry(); - }; - const createKibanaTemplate = () => { context.wazuh.logger.debug( `Creating template for ${PLUGIN_PLATFORM_INDEX}`, @@ -171,7 +63,6 @@ export function jobInitializeRun(context) { index: PLUGIN_PLATFORM_INDEX, }); context.wazuh.logger.info(`${PLUGIN_PLATFORM_INDEX} index created`); - await init(); } catch (error) { throw new Error( `Error creating ${PLUGIN_PLATFORM_INDEX} index: ${error.message}`, @@ -222,8 +113,6 @@ export function jobInitializeRun(context) { }); if (response.body) { context.wazuh.logger.debug(`${PLUGIN_PLATFORM_INDEX} index exist`); - // It exists, initialize! - await init(); } else { context.wazuh.logger.debug( `${PLUGIN_PLATFORM_INDEX} index does not exist`, diff --git a/plugins/wazuh-core/server/plugin.ts b/plugins/wazuh-core/server/plugin.ts index 19709814d0..8b4933a52d 100644 --- a/plugins/wazuh-core/server/plugin.ts +++ b/plugins/wazuh-core/server/plugin.ts @@ -99,14 +99,9 @@ export class WazuhCorePlugin this.services.configuration.setup(); - this.services.updateRegistry = new UpdateRegistry( - this.logger.get('update-registry'), - ); - this.services.manageHosts = new ManageHosts( this.logger.get('manage-hosts'), this.services.configuration, - this.services.updateRegistry, ); this.services.serverAPIClient = new ServerAPIClient( @@ -162,6 +157,8 @@ export class WazuhCorePlugin logger: this.logger.get(MigrationConfigFile.name), }); + await this.services.manageHosts.start(); + return { ...this.services, api: { diff --git a/plugins/wazuh-core/server/services/cache-api-user-has-run-as.ts b/plugins/wazuh-core/server/services/cache-api-user-has-run-as.ts deleted file mode 100644 index 2e365c3196..0000000000 --- a/plugins/wazuh-core/server/services/cache-api-user-has-run-as.ts +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Wazuh app - Service which caches the API user allow run as - * 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 { Logger } from 'opensearch-dashboards/server'; -import { ManageHosts } from './manage-hosts'; -import { API_USER_STATUS_RUN_AS } from '../../common/api-user-status-run-as'; - -// Object.freeze(API_USER_STATUS_RUN_AS); -/** - * This service caches the status of API host internal user allows the run as option. - */ -export class CacheAPIUserAllowRunAs { - readonly API_USER_STATUS_RUN_AS; - private _cache: any; - constructor(private logger: Logger, private manageHosts: ManageHosts) { - // Private variable to save the cache - this._cache = {}; - this.API_USER_STATUS_RUN_AS = API_USER_STATUS_RUN_AS; - } - // Set an entry with API ID, username and allow_run_as - set(apiID: string, username: string, allow_run_as: number): void { - if (!this._cache[apiID]) { - this._cache[apiID] = {}; // Create a API ID entry if it doesn't exist in cache object - } - this._cache[apiID][username] = allow_run_as; - } - // Get the value of an entry with API ID and username from cache - get(apiID: string, username: string): number { - return this._cache[apiID] && - typeof this._cache[apiID][username] !== 'undefined' - ? this._cache[apiID][username] - : API_USER_STATUS_RUN_AS.ALL_DISABLED; - } - // Check if it exists the API ID and username in the cache - has(apiID: string, username: string): boolean { - return this._cache[apiID] && - typeof this._cache[apiID][username] !== 'undefined' - ? true - : false; - } - async check(apiId: string): Promise { - try { - const api = await this.manageHosts.get(apiId, { excludePassword: true }); - this.logger.debug( - `Check if API user ${api.username} (${apiId}) has run_as`, - ); - // Check if api.run_as is false or undefined, then it set to false in cache - if (!api.run_as) { - this.set(apiId, api.username, API_USER_STATUS_RUN_AS.HOST_DISABLED); - } - // Check if the API user is cached and returns it - if (this.has(apiId, api.username)) { - return this.get(apiId, api.username); - } - const response = - await this.manageHosts.serverAPIClient.asInternalUser.request( - 'GET', - '/security/users/me', - {}, - { apiHostID: apiId }, - ); - const statusUserAllowRunAs = response.data.data.affected_items[0] - .allow_run_as - ? API_USER_STATUS_RUN_AS.ENABLED - : API_USER_STATUS_RUN_AS.USER_NOT_ALLOWED; - - // Cache the run_as for the API user - this.set(apiId, api.username, statusUserAllowRunAs); - return statusUserAllowRunAs; - } catch (error) { - this.logger.error(error.message || error); - return API_USER_STATUS_RUN_AS.ALL_DISABLED; - } - } - async canUse(apiId: string): Promise { - const ApiUserCanUseStatus = await this.check(apiId); - if (ApiUserCanUseStatus === API_USER_STATUS_RUN_AS.USER_NOT_ALLOWED) { - const api = await this.manageHosts.get(apiId, { - excludePassword: true, - }); - throw new Error( - `API with host ID [${apiId}] misconfigured. The server API user [${api.username}] is not allowed to use [run_as]. Allow it in the user configuration or set [run_as] host setting with [false] value.`, - ); - } - return ApiUserCanUseStatus; - } -} diff --git a/plugins/wazuh-core/server/services/index.ts b/plugins/wazuh-core/server/services/index.ts index 0e7288224c..8a794e559f 100644 --- a/plugins/wazuh-core/server/services/index.ts +++ b/plugins/wazuh-core/server/services/index.ts @@ -10,11 +10,9 @@ * Find more information about this on the LICENSE file. */ -export * from './cache-api-user-has-run-as'; export * from './configuration-store'; export * from './cookie'; export * from './filesystem'; export * from './manage-hosts'; export * from './security-factory'; export * from './server-api-client'; -export * from './update-registry'; diff --git a/plugins/wazuh-core/server/services/manage-hosts.ts b/plugins/wazuh-core/server/services/manage-hosts.ts index 33fb757f6f..a3f792d115 100644 --- a/plugins/wazuh-core/server/services/manage-hosts.ts +++ b/plugins/wazuh-core/server/services/manage-hosts.ts @@ -11,8 +11,9 @@ */ import { Logger } from 'opensearch-dashboards/server'; import { IConfiguration } from '../../common/services/configuration'; -import { CacheAPIUserAllowRunAs } from './cache-api-user-has-run-as'; import { ServerAPIClient } from './server-api-client'; +import { API_USER_STATUS_RUN_AS } from '../../common/api-user-status-run-as'; +import { HTTP_STATUS_CODES } from '../../common/constants'; interface IAPIHost { id: string; @@ -22,25 +23,28 @@ interface IAPIHost { run_as: boolean; } +interface IAPIHostRegistry { + manager: string | null; + node: string | null; + status: string; + cluster: string; + allow_run_as: API_USER_STATUS_RUN_AS; +} + /** * This service manages the API connections. * Get API hosts configuration - * Get API host entries (combine configuration and registry file) - * Set API host + * Get API host entries (combine configuration and registry data) + * Create API host + * Update API host * Delete API host - * Cache the allow_run_as value for API ID and username + * Cache the registry data for API hosts * Ability to get if the configured user is allowed to use run as */ export class ManageHosts { - public cacheAPIUserAllowRunAs: CacheAPIUserAllowRunAs; public serverAPIClient: ServerAPIClient | null = null; - constructor( - private logger: Logger, - private configuration: IConfiguration, - private updateRegistry, - ) { - this.cacheAPIUserAllowRunAs = new CacheAPIUserAllowRunAs(this.logger, this); - } + private cacheRegistry: Map = new Map(); + constructor(private logger: Logger, private configuration: IConfiguration) {} setServerAPIClient(client: ServerAPIClient) { this.serverAPIClient = client; @@ -130,6 +134,10 @@ export class ManageHosts { await this.configuration.set({ hosts: updatedHosts, }); + const host = (await this.get(hostID, { + excludePassword: true, + })) as IAPIHost; + this.getRegistryDataByHost(host); this.logger.info(`API connection with ID [${hostID}] was created`); return data; } catch (error) { @@ -172,6 +180,10 @@ export class ManageHosts { await this.configuration.set({ hosts: updatedHosts, }); + const host = (await this.get(updatedHostID, { + excludePassword: true, + })) as IAPIHost; + this.getRegistryDataByHost(host); this.logger.info(`API connection with ID [${updatedHostID}] was updated`); return data; } catch (error) { @@ -205,6 +217,7 @@ export class ManageHosts { await this.configuration.set({ hosts: updatedHosts, }); + this.deleteRegistryByHost(hostID); this.logger.info(`API connection with ID [${hostID}] was removed`); } catch (error) { this.logger.error(error.message); @@ -223,40 +236,178 @@ export class ManageHosts { options: { excludePassword: boolean } = { excludePassword: false }, ) { try { - const hosts = await this.get(undefined, options); - const registry = await this.updateRegistry.getHosts(); - const result = await this.joinHostRegistry(hosts, registry); - return result; + this.logger.debug('Getting the API connections'); + const hosts = (await this.get(undefined, options)) as IAPIHost[]; + this.logger.debug('Getting registry'); + const registry = Object.entries(this.cacheRegistry.entries()); + return hosts.map(host => { + const { id } = host; + return { ...host, ...registry[id] }; + }); } catch (error) { - this.logger.error(error); + this.logger.error(error.message); throw error; } } + private isServerAPIClientResponseOk(response: { status: number }) { + return response.status === HTTP_STATUS_CODES.OK; + } + /** - * Joins the hosts with the related information in the registry - * @param {Object} hosts - * @param {Object} registry - * @param {Boolean} removePassword + * Get the cluster info and allow_run_as values for the API host and store into the registry cache + * @param host + * @returns */ - private async joinHostRegistry(hosts: any, registry: any) { + private async getRegistryDataByHost( + host: IAPIHost, + ): Promise { + const apiHostID = host.id; + this.logger.debug(`Getting registry data from host [${apiHostID}]`); + // Get cluster info data + + let manager = null, + node = null, + status = 'disabled', + cluster = 'Disabled', + allow_run_as = API_USER_STATUS_RUN_AS.ALL_DISABLED; + try { - if (!Array.isArray(hosts)) { - throw new Error('API connections is not a list'); + const responseAgents = await this.serverAPIClient.asInternalUser.request( + 'GET', + `/agents`, + { params: { agents_list: '000' } }, + { apiHostID }, + ); + + if (this.isServerAPIClientResponseOk(responseAgents)) { + manager = responseAgents.data.data.affected_items[0].manager; + } + + // Get allow_run_as + if (!host.run_as) { + allow_run_as = API_USER_STATUS_RUN_AS.HOST_DISABLED; + } else { + const responseAllowRunAs = + await this.serverAPIClient.asInternalUser.request( + 'GET', + '/security/users/me', + {}, + { apiHostID }, + ); + if (this.isServerAPIClientResponseOk(responseAllowRunAs)) { + allow_run_as = responseAllowRunAs.data.data.affected_items[0] + .allow_run_as + ? API_USER_STATUS_RUN_AS.ENABLED + : API_USER_STATUS_RUN_AS.USER_NOT_ALLOWED; + } + } + + const responseClusterStatus = + await this.serverAPIClient.asInternalUser.request( + 'GET', + `/cluster/status`, + {}, + { apiHostID }, + ); + + if (this.isServerAPIClientResponseOk(responseClusterStatus)) { + status = 'enabled'; + + const responseClusterLocal = + await this.serverAPIClient.asInternalUser.request( + 'GET', + `/cluster/local/info`, + {}, + { apiHostID }, + ); + + if (this.isServerAPIClientResponseOk(responseClusterStatus)) { + node = responseClusterLocal.data.data.affected_items[0].node; + cluster = responseClusterLocal.data.data.affected_items[0].cluster; + } } + } catch (error) {} - return await Promise.all( - hosts.map(async h => { - const { id } = h; - const host = { ...h, ...registry[id] }; - // Add to run_as from API user. Use the cached value or get it doing a request - host.allow_run_as = await this.cacheAPIUserAllowRunAs.check(id); - return host; - }), + const data = { + manager, + node, + status, + cluster, + allow_run_as, + }; + this.updateRegistryByHost(apiHostID, data); + return data; + } + + /** + * Initialize the service on plugin start. + * - Get the registry data for the configured API hosts and store into the in memory cache + * @returns + */ + async start() { + try { + this.logger.debug('Start'); + const hosts = (await this.get(undefined, { + excludePassword: true, + })) as IAPIHost[]; + if (!hosts.length) { + this.logger.debug('No hosts found. Skip.'); + return; + } + + await Promise.all( + hosts.map(host => + (async () => [host.id, await this.getRegistryDataByHost(host)])(), + ), ); + this.logger.debug('API hosts data stored in the registry'); } catch (error) { this.logger.error(error.message); throw error; } } + + private getRegistryByHost(hostID: string) { + this.logger.debug(`Getting cache for API host [${hostID}]`); + const result = this.cacheRegistry.get(hostID); + this.logger.debug(`Get cache for APIhost [${hostID}]`); + return result; + } + + private updateRegistryByHost(hostID: string, data: any) { + this.logger.debug(`Updating cache for APIhost [${hostID}]`); + const result = this.cacheRegistry.set(hostID, data); + this.logger.debug(`Updated cache for APIhost [${hostID}]`); + return result; + } + + private deleteRegistryByHost(hostID: string) { + this.logger.debug(`Deleting cache for API host [${hostID}]`); + const result = this.cacheRegistry.delete(hostID); + this.logger.debug(`Deleted cache for API host [${hostID}]`); + return result; + } + + /** + * Check if the authentication with run_as is enabled and the API user can use it + * @param apiId + * @returns + */ + isEnabledAuthWithRunAs(apiId: string): boolean { + this.logger.debug(`Checking if the API host [${apiId}] can use the run_as`); + + const registryHost = this.getRegistryByHost(apiId); + if (!registryHost) { + throw new Error( + `API host with ID [${apiId}] was not found in the registry. This could be caused by a problem getting and storing the registry data or the API host was removed.`, + ); + } + if (registryHost.allow_run_as === API_USER_STATUS_RUN_AS.USER_NOT_ALLOWED) { + throw new Error( + `API host with host ID [${apiId}] misconfigured. The configurated API user is not allowed to use [run_as]. Allow it in the API user configuration or set [run_as] host setting with [false] value.`, + ); + } + return registryHost.allow_run_as === API_USER_STATUS_RUN_AS.ENABLED; + } } diff --git a/plugins/wazuh-core/server/services/update-registry.ts b/plugins/wazuh-core/server/services/update-registry.ts deleted file mode 100644 index 5511760e4d..0000000000 --- a/plugins/wazuh-core/server/services/update-registry.ts +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Wazuh app - Module to update the configuration file - * 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 fs from 'fs'; -import { WAZUH_DATA_CONFIG_REGISTRY_PATH } from '../../common/constants'; -import { Logger } from 'opensearch-dashboards/server'; - -/** - * This service updates the registry file - */ -export class UpdateRegistry { - busy: boolean; - file: string; - constructor(private logger: Logger) { - this.busy = false; - this.file = WAZUH_DATA_CONFIG_REGISTRY_PATH; - } - - /** - * Reads the Wazuh registry content - */ - async readContent() { - try { - this.logger.debug('Reading registry file'); - const content = await fs.readFileSync(this.file, { encoding: 'utf-8' }); - return JSON.parse(content); - } catch (error) { - this.logger.error(error.message || error); - return Promise.reject(error); - } - } - - /** - * Get the hosts and their cluster info stored in the registry - */ - async getHosts() { - try { - this.logger.debug('Getting hosts from registry', 'debug'); - const content = await this.readContent(); - return content.hosts || {}; - } catch (error) { - this.logger.error(error.message || error); - return Promise.reject(error); - } - } - - /** - * Returns the cluster information associated to an API id - * @param {String} id - */ - async getHostById(id) { - try { - if (!id) throw new Error('API id is missing'); - const hosts = await this.getHosts(); - return hosts.id || {}; - } catch (error) { - this.logger.error(error.message || error); - return Promise.reject(error); - } - } - - /** - * Writes the wazuh-registry.json - * @param {Object} content - */ - async writeContent(content) { - try { - this.logger.debug('Writing wazuh-registry.json content'); - if (this.busy) { - throw new Error('Another process is updating the registry file'); - } - this.busy = true; - await fs.writeFileSync(this.file, JSON.stringify(content)); - this.busy = false; - } catch (error) { - this.logger.error(error.message || error); - return Promise.reject(error); - } - } - - /** - * Checks if the host exist in order to update the data, otherwise creates it - * @param {String} id - * @param {Object} hosts - */ - checkHost(id, hosts) { - try { - return Object.keys(hosts).includes(id); - } catch (error) { - this.logger.error(error.message || error); - return Promise.reject(error); - } - } - - /** - * Migrates the cluster information and extensions associated to an API id - * @param {String} id - * @param {Object} clusterInfo - * @param {Object} clusterExtensions - */ - async migrateToRegistry(id, clusterInfo, clusterExtensions) { - try { - const content = await this.readContent(); - if (!Object.keys(content).includes('hosts')) - Object.assign(content, { hosts: {} }); - const info = { cluster_info: clusterInfo, extensions: clusterExtensions }; - content.hosts[id] = info; - await this.writeContent(content); - this.logger.info(`API ${id} was properly migrated`); - return info; - } catch (error) { - this.logger.error(error.message || error); - return Promise.reject(error); - } - } - - /** - * Updates the cluster-information or manager-information in the registry - * @param {String} id - * @param {Object} clusterInfo - */ - async updateClusterInfo(id, clusterInfo) { - try { - const content = await this.readContent(); - // Checks if not exists in order to create - if (!content.hosts[id]) content.hosts[id] = {}; - content.hosts[id].cluster_info = clusterInfo; - await this.writeContent(content); - this.logger.debug(`API ${id} information was properly updated`); - return id; - } catch (error) { - this.logger.error(error.message || error); - return Promise.reject(error); - } - } - - /** - * Updates the cluster-information or manager-information in the registry - * @param {String} id - * @param {Object} clusterInfo - */ - async updateAPIExtensions(id, extensions) { - try { - const content = await this.readContent(); - if (content.hosts[id]) content.hosts[id].extensions = extensions; - await this.writeContent(content); - this.logger.info(`API ${id} extensions were properly updated`); - return id; - } catch (error) { - this.logger.error(error.message || error); - return Promise.reject(error); - } - } - - /** - * Remove the given ids from the registry host entries - * @param {Array} ids - */ - async removeHostEntries(ids) { - try { - this.logger.debug('Removing entry'); - const content = await this.readContent(); - ids.forEach(id => delete content.hosts[id]); - await this.writeContent(content); - } catch (error) { - this.logger.error(error.message || error); - return Promise.reject(error); - } - } - - /** - * Compare the hosts from wazuh.yml and the host in the wazuh-registry.json file in order to remove the orphan registry register - * @param {Array} hosts - */ - async removeOrphanEntries(hosts) { - try { - this.logger.debug('Checking orphan registry entries'); - const entries = await this.getHosts(); - const hostsKeys = hosts.map(h => { - return h.id; - }); - const entriesKeys = Object.keys(entries); - const diff = entriesKeys.filter(e => { - return !hostsKeys.includes(e); - }); - await this.removeHostEntries(diff); - } catch (error) { - this.logger.error(error.message || error); - return Promise.reject(error); - } - } - - /** - * Returns the token information associated to an API id - * @param {String} id - */ - async getTokenById(id) { - try { - if (!id) throw new Error('API id is missing'); - const hosts = await this.getHosts(); - return hosts[id] ? hosts[id].token || null : null; - } catch (error) { - this.logger.error(error.message || error); - return Promise.reject(error); - } - } - - /** - * Updates the token in the registry - * @param {String} id - * @param {String} token - */ - async updateTokenByHost(id, token) { - try { - const content = await this.readContent(); - // Checks if not exists in order to create - if (!content.hosts[id]) content.hosts[id] = {}; - content.hosts[id].token = token; - await this.writeContent(content); - this.logger.info(`API ${id} information was properly updated`); - return id; - } catch (error) { - this.logger.debug(error.message || error); - return Promise.reject(error); - } - } -} diff --git a/plugins/wazuh-core/server/start/tasks/config-file.ts b/plugins/wazuh-core/server/start/tasks/config-file.ts index 06bf5ac51a..c7a867fe55 100644 --- a/plugins/wazuh-core/server/start/tasks/config-file.ts +++ b/plugins/wazuh-core/server/start/tasks/config-file.ts @@ -36,8 +36,8 @@ export default { ); if (savedObjectIsCreated) { - logger.info( - `The saved object exists so the configuration defined at the file [${configurationFileLocation}] will not be migrated. Skip.`, + logger.warn( + `The configuration saved object exists so the configuration defined at the file [${configurationFileLocation}] will not be migrated. Skip.`, ); return; } diff --git a/plugins/wazuh-core/server/types.ts b/plugins/wazuh-core/server/types.ts index 364bceacc9..0cc8dedbee 100644 --- a/plugins/wazuh-core/server/types.ts +++ b/plugins/wazuh-core/server/types.ts @@ -4,7 +4,6 @@ import { ServerAPIClient, ServerAPIInternalUserClient, ServerAPIScopedUserClient, - UpdateRegistry, } from './services'; import { Configuration } from '../common/services/configuration'; @@ -14,7 +13,6 @@ export interface WazuhCorePluginSetup { configuration: Configuration; manageHosts: ManageHosts; serverAPIClient: ServerAPIClient; - updateRegistry: UpdateRegistry; api: { client: { asInternalUser: ServerAPIInternalUserClient; @@ -28,7 +26,6 @@ export interface WazuhCorePluginStart { configuration: Configuration; manageHosts: ManageHosts; serverAPIClient: ServerAPIClient; - updateRegistry: UpdateRegistry; api: { client: { asInternalUser: ServerAPIInternalUserClient;