From 06a9ae99ab8993286556143c4f35e2c82f656a17 Mon Sep 17 00:00:00 2001 From: Derek Ho Date: Mon, 1 Apr 2024 17:24:35 -0400 Subject: [PATCH] Add multi datasource support for the tenanct and audit log tabs Signed-off-by: Derek Ho --- public/apps/configuration/app-router.tsx | 8 ++--- .../audit-logging-edit-settings.tsx | 31 ++++++++++++++--- .../panels/audit-logging/audit-logging.tsx | 31 ++++++++++++++--- .../__snapshots__/audit-logging.test.tsx.snap | 15 +++++++++ .../test/audit-logging-edit-settings.test.tsx | 4 +++ .../audit-logging/test/audit-logging.test.tsx | 4 +++ .../panels/tenant-list/tenant-list.tsx | 10 +++++- .../__snapshots__/app-router.test.tsx.snap | 1 - .../utils/audit-logging-utils.tsx | 17 +++++++--- server/routes/index.ts | 33 +++++++++++++------ 10 files changed, 122 insertions(+), 32 deletions(-) diff --git a/public/apps/configuration/app-router.tsx b/public/apps/configuration/app-router.tsx index ea9ae73c0..491623ab3 100644 --- a/public/apps/configuration/app-router.tsx +++ b/public/apps/configuration/app-router.tsx @@ -149,15 +149,13 @@ export interface DataSourceContextType { setDataSource: React.Dispatch>; } +export const LocalCluster = { label: 'Local cluster', id: '' }; + export const DataSourceContext = createContext(null); export function AppRouter(props: AppDependencies) { const setGlobalBreadcrumbs = flow(getBreadcrumbs, props.coreStart.chrome.setBreadcrumbs); - const [dataSource, setDataSource] = useState({ - id: '', - label: 'Local cluster', - checked: 'on', - }); + const [dataSource, setDataSource] = useState(LocalCluster); return ( diff --git a/public/apps/configuration/panels/audit-logging/audit-logging-edit-settings.tsx b/public/apps/configuration/panels/audit-logging/audit-logging-edit-settings.tsx index de5d0086f..a48da106b 100644 --- a/public/apps/configuration/panels/audit-logging/audit-logging-edit-settings.tsx +++ b/public/apps/configuration/panels/audit-logging/audit-logging-edit-settings.tsx @@ -13,7 +13,7 @@ * permissions and limitations under the License. */ -import React from 'react'; +import React, { useContext } from 'react'; import { EuiButton, EuiFlexGroup, @@ -35,6 +35,9 @@ import { ResourceType } from '../../../../../common'; import { getAuditLogging, updateAuditLogging } from '../../utils/audit-logging-utils'; import { useToastState } from '../../utils/toast-utils'; import { setCrossPageToast } from '../../utils/storage-utils'; +import { SecurityPluginTopNavMenu } from '../../top-nav-menu'; +import { DataSourceContext } from '../../app-router'; +import { createDataSourceQuery } from '../../../../utils/datasource-utils'; interface AuditLoggingEditSettingProps extends AppDependencies { setting: 'general' | 'compliance'; @@ -44,6 +47,7 @@ export function AuditLoggingEditSettings(props: AuditLoggingEditSettingProps) { const [editConfig, setEditConfig] = React.useState({}); const [toasts, addToast, removeToast] = useToastState(); const [invalidSettings, setInvalidSettings] = React.useState([]); + const { dataSource, setDataSource } = useContext(DataSourceContext)!; const handleChange = (path: string, val: boolean | string[] | SettingMapItem) => { setEditConfig((previousEditedConfig) => { @@ -63,7 +67,10 @@ export function AuditLoggingEditSettings(props: AuditLoggingEditSettingProps) { React.useEffect(() => { const fetchConfig = async () => { try { - const fetchedConfig = await getAuditLogging(props.coreStart.http); + const fetchedConfig = await getAuditLogging( + props.coreStart.http, + createDataSourceQuery(dataSource.id) + ); setEditConfig(fetchedConfig); } catch (e) { console.log(e); @@ -71,7 +78,7 @@ export function AuditLoggingEditSettings(props: AuditLoggingEditSettingProps) { }; fetchConfig(); - }, [props.coreStart.http]); + }, [props.coreStart.http, dataSource.id]); const renderSaveAndCancel = () => { return ( @@ -106,7 +113,11 @@ export function AuditLoggingEditSettings(props: AuditLoggingEditSettingProps) { const saveConfig = async (configToUpdate: AuditLoggingSettings) => { try { - await updateAuditLogging(props.coreStart.http, configToUpdate); + await updateAuditLogging( + props.coreStart.http, + configToUpdate, + createDataSourceQuery(dataSource.id) + ); const addSuccessToast = (text: string) => { const successToast: Toast = { @@ -237,5 +248,15 @@ export function AuditLoggingEditSettings(props: AuditLoggingEditSettingProps) { content = renderComplianceSetting(); } - return
{content}
; + return ( +
+ + {content} +
+ ); } diff --git a/public/apps/configuration/panels/audit-logging/audit-logging.tsx b/public/apps/configuration/panels/audit-logging/audit-logging.tsx index 5ca10c84c..a3d9e4391 100644 --- a/public/apps/configuration/panels/audit-logging/audit-logging.tsx +++ b/public/apps/configuration/panels/audit-logging/audit-logging.tsx @@ -28,7 +28,7 @@ import { EuiText, EuiTitle, } from '@elastic/eui'; -import React from 'react'; +import React, { useContext } from 'react'; import { FormattedMessage } from '@osd/i18n/react'; import { AppDependencies } from '../../../types'; import { ResourceType } from '../../../../../common'; @@ -43,6 +43,9 @@ import { import { AuditLoggingSettings } from './types'; import { ViewSettingGroup } from './view-setting-group'; import { DocLinks } from '../../constants'; +import { DataSourceContext } from '../../app-router'; +import { SecurityPluginTopNavMenu } from '../../top-nav-menu'; +import { createDataSourceQuery } from '../../../../utils/datasource-utils'; interface AuditLoggingProps extends AppDependencies { fromType: string; @@ -134,13 +137,18 @@ export function renderComplianceSettings(config: AuditLoggingSettings) { export function AuditLogging(props: AuditLoggingProps) { const [configuration, setConfiguration] = React.useState({}); + const { dataSource, setDataSource } = useContext(DataSourceContext)!; const onSwitchChange = async () => { try { const updatedConfiguration = { ...configuration }; updatedConfiguration.enabled = !updatedConfiguration.enabled; - await updateAuditLogging(props.coreStart.http, updatedConfiguration); + await updateAuditLogging( + props.coreStart.http, + updatedConfiguration, + createDataSourceQuery(dataSource.id) + ); setConfiguration(updatedConfiguration); } catch (e) { @@ -151,7 +159,10 @@ export function AuditLogging(props: AuditLoggingProps) { React.useEffect(() => { const fetchData = async () => { try { - const auditLogging = await getAuditLogging(props.coreStart.http); + const auditLogging = await getAuditLogging( + props.coreStart.http, + createDataSourceQuery(dataSource.id) + ); setConfiguration(auditLogging); } catch (e) { // TODO: switch to better error handling. @@ -160,7 +171,7 @@ export function AuditLogging(props: AuditLoggingProps) { }; fetchData(); - }, [props.coreStart.http, props.fromType]); + }, [props.coreStart.http, props.fromType, dataSource.id]); const statusPanel = renderStatusPanel(onSwitchChange, configuration.enabled || false); @@ -226,5 +237,15 @@ export function AuditLogging(props: AuditLoggingProps) { ); } - return
{content}
; + return ( +
+ + {content} +
+ ); } diff --git a/public/apps/configuration/panels/audit-logging/test/__snapshots__/audit-logging.test.tsx.snap b/public/apps/configuration/panels/audit-logging/test/__snapshots__/audit-logging.test.tsx.snap index 6d8e9167f..c87a5e12b 100644 --- a/public/apps/configuration/panels/audit-logging/test/__snapshots__/audit-logging.test.tsx.snap +++ b/public/apps/configuration/panels/audit-logging/test/__snapshots__/audit-logging.test.tsx.snap @@ -241,6 +241,21 @@ exports[`Audit logs render when AuditLoggingSettings.enabled is true 1`] = `
+

diff --git a/public/apps/configuration/panels/audit-logging/test/audit-logging-edit-settings.test.tsx b/public/apps/configuration/panels/audit-logging/test/audit-logging-edit-settings.test.tsx index a7b52d428..f00bab589 100644 --- a/public/apps/configuration/panels/audit-logging/test/audit-logging-edit-settings.test.tsx +++ b/public/apps/configuration/panels/audit-logging/test/audit-logging-edit-settings.test.tsx @@ -21,6 +21,10 @@ import { buildHashUrl } from '../../../utils/url-builder'; import { ResourceType } from '../../../../../../common'; jest.mock('../../../utils/audit-logging-utils'); +jest.mock('react', () => ({ + ...jest.requireActual('react'), + useContext: jest.fn().mockReturnValue({ dataSource: { id: 'test' }, setDataSource: jest.fn() }), // Mock the useContext hook to return dummy datasource and setdatasource function +})); // eslint-disable-next-line const mockAuditLoggingUtils = require('../../../utils/audit-logging-utils'); diff --git a/public/apps/configuration/panels/audit-logging/test/audit-logging.test.tsx b/public/apps/configuration/panels/audit-logging/test/audit-logging.test.tsx index 2bc5d4881..3f5c6e8da 100644 --- a/public/apps/configuration/panels/audit-logging/test/audit-logging.test.tsx +++ b/public/apps/configuration/panels/audit-logging/test/audit-logging.test.tsx @@ -25,6 +25,10 @@ import { } from '../constants'; jest.mock('../../../utils/audit-logging-utils'); +jest.mock('react', () => ({ + ...jest.requireActual('react'), + useContext: jest.fn().mockReturnValue({ dataSource: { id: 'test' }, setDataSource: jest.fn() }), // Mock the useContext hook to return dummy datasource and setdatasource function +})); // eslint-disable-next-line const mockAuditLoggingUtils = require('../../../utils/audit-logging-utils'); diff --git a/public/apps/configuration/panels/tenant-list/tenant-list.tsx b/public/apps/configuration/panels/tenant-list/tenant-list.tsx index f1d3079b5..3fd5b89da 100644 --- a/public/apps/configuration/panels/tenant-list/tenant-list.tsx +++ b/public/apps/configuration/panels/tenant-list/tenant-list.tsx @@ -23,7 +23,7 @@ import { EuiButton, } from '@elastic/eui'; import { Route } from 'react-router-dom'; -import React, { useState, useMemo, useCallback } from 'react'; +import React, { useState, useMemo, useCallback, useContext } from 'react'; import { ManageTab } from './manage_tab'; import { ConfigureTab1 } from './configure_tab1'; import { AppDependencies } from '../../../types'; @@ -32,6 +32,8 @@ import { displayBoolean } from '../../utils/display-utils'; import { DocLinks } from '../../constants'; import { getDashboardsInfo } from '../../../../utils/dashboards-info-utils'; import { TenantInstructionView } from './tenant-instruction-view'; +import { DataSourceContext, LocalCluster } from '../../app-router'; +import { SecurityPluginTopNavMenu } from '../../top-nav-menu'; interface TenantListProps extends AppDependencies { tabID: string; @@ -134,6 +136,12 @@ export function TenantList(props: TenantListProps) { return ( <> + {}} + selectedDataSource={LocalCluster} + />

Multi-tenancy

diff --git a/public/apps/configuration/test/__snapshots__/app-router.test.tsx.snap b/public/apps/configuration/test/__snapshots__/app-router.test.tsx.snap index 68026a1c1..4696ecc12 100644 --- a/public/apps/configuration/test/__snapshots__/app-router.test.tsx.snap +++ b/public/apps/configuration/test/__snapshots__/app-router.test.tsx.snap @@ -5,7 +5,6 @@ exports[`SecurityPluginTopNavMenu renders DataSourceMenu when dataSource is enab value={ Object { "dataSource": Object { - "checked": "on", "id": "", "label": "Local cluster", }, diff --git a/public/apps/configuration/utils/audit-logging-utils.tsx b/public/apps/configuration/utils/audit-logging-utils.tsx index 72b04771d..517babf95 100644 --- a/public/apps/configuration/utils/audit-logging-utils.tsx +++ b/public/apps/configuration/utils/audit-logging-utils.tsx @@ -13,16 +13,23 @@ * permissions and limitations under the License. */ -import { HttpStart } from 'opensearch-dashboards/public'; +import { HttpFetchQuery, HttpStart } from 'opensearch-dashboards/public'; import { AuditLoggingSettings } from '../panels/audit-logging/types'; import { API_ENDPOINT_AUDITLOGGING, API_ENDPOINT_AUDITLOGGING_UPDATE } from '../constants'; import { httpGet, httpPost } from './request-utils'; -export async function updateAuditLogging(http: HttpStart, updateObject: AuditLoggingSettings) { - return await httpPost({ http, url: API_ENDPOINT_AUDITLOGGING_UPDATE, body: updateObject }); +export async function updateAuditLogging( + http: HttpStart, + updateObject: AuditLoggingSettings, + query: HttpFetchQuery +) { + return await httpPost({ http, url: API_ENDPOINT_AUDITLOGGING_UPDATE, body: updateObject, query }); } -export async function getAuditLogging(http: HttpStart): Promise { - const rawConfiguration = await httpGet({ http, url: API_ENDPOINT_AUDITLOGGING }); +export async function getAuditLogging( + http: HttpStart, + query: HttpFetchQuery +): Promise { + const rawConfiguration = await httpGet({ http, url: API_ENDPOINT_AUDITLOGGING, query }); return rawConfiguration?.config; } diff --git a/server/routes/index.ts b/server/routes/index.ts index 8d8da8c05..38d7566bb 100644 --- a/server/routes/index.ts +++ b/server/routes/index.ts @@ -674,18 +674,24 @@ export function defineRoutes(router: IRouter, dataSourceEnabled: boolean) { router.get( { path: `${API_PREFIX}/configuration/audit`, - validate: false, + validate: { + query: schema.object({ + dataSourceId: schema.maybe(schema.string()), + }), + }, }, async ( context, request, response ): Promise> => { - const client = context.security_plugin.esClient.asScoped(request); - - let esResp; try { - esResp = await client.callAsCurrentUser('opensearch_security.getAudit'); + const esResp = await wrapRouteWithDataSource( + dataSourceEnabled, + context, + request, + 'opensearch_security.getAudit' + ); return response.ok({ body: esResp, @@ -759,15 +765,22 @@ export function defineRoutes(router: IRouter, dataSourceEnabled: boolean) { path: `${API_PREFIX}/configuration/audit/config`, validate: { body: schema.any(), + query: schema.object({ + dataSourceId: schema.maybe(schema.string()), + }), }, }, async (context, request, response) => { - const client = context.security_plugin.esClient.asScoped(request); - let esResp; try { - esResp = await client.callAsCurrentUser('opensearch_security.saveAudit', { - body: request.body, - }); + const esResp = await wrapRouteWithDataSource( + dataSourceEnabled, + context, + request, + 'opensearch_security.saveAudit', + { + body: request.body, + } + ); return response.ok({ body: { message: esResp.message,