Skip to content

Commit

Permalink
Add multi datasource support for the tenanct and audit log tabs
Browse files Browse the repository at this point in the history
Signed-off-by: Derek Ho <[email protected]>
  • Loading branch information
derek-ho committed Apr 1, 2024
1 parent be3e939 commit 06a9ae9
Show file tree
Hide file tree
Showing 10 changed files with 122 additions and 32 deletions.
8 changes: 3 additions & 5 deletions public/apps/configuration/app-router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -149,15 +149,13 @@ export interface DataSourceContextType {
setDataSource: React.Dispatch<React.SetStateAction<DataSourceOption>>;
}

export const LocalCluster = { label: 'Local cluster', id: '' };

export const DataSourceContext = createContext<DataSourceContextType | null>(null);

export function AppRouter(props: AppDependencies) {
const setGlobalBreadcrumbs = flow(getBreadcrumbs, props.coreStart.chrome.setBreadcrumbs);
const [dataSource, setDataSource] = useState<DataSourceOption>({
id: '',
label: 'Local cluster',
checked: 'on',
});
const [dataSource, setDataSource] = useState<DataSourceOption>(LocalCluster);

return (
<DataSourceContext.Provider value={{ dataSource, setDataSource }}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* permissions and limitations under the License.
*/

import React from 'react';
import React, { useContext } from 'react';
import {
EuiButton,
EuiFlexGroup,
Expand All @@ -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';
Expand All @@ -44,6 +47,7 @@ export function AuditLoggingEditSettings(props: AuditLoggingEditSettingProps) {
const [editConfig, setEditConfig] = React.useState<AuditLoggingSettings>({});
const [toasts, addToast, removeToast] = useToastState();
const [invalidSettings, setInvalidSettings] = React.useState<string[]>([]);
const { dataSource, setDataSource } = useContext(DataSourceContext)!;

const handleChange = (path: string, val: boolean | string[] | SettingMapItem) => {
setEditConfig((previousEditedConfig) => {
Expand All @@ -63,15 +67,18 @@ 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);
}
};

fetchConfig();
}, [props.coreStart.http]);
}, [props.coreStart.http, dataSource.id]);

const renderSaveAndCancel = () => {
return (
Expand Down Expand Up @@ -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 = {
Expand Down Expand Up @@ -237,5 +248,15 @@ export function AuditLoggingEditSettings(props: AuditLoggingEditSettingProps) {
content = renderComplianceSetting();
}

return <div className="panel-restrict-width">{content}</div>;
return (
<div className="panel-restrict-width">
<SecurityPluginTopNavMenu
{...props}
dataSourcePickerReadOnly={true}
setDataSource={setDataSource}
selectedDataSource={dataSource}
/>
{content}
</div>
);
}
31 changes: 26 additions & 5 deletions public/apps/configuration/panels/audit-logging/audit-logging.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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;
Expand Down Expand Up @@ -134,13 +137,18 @@ export function renderComplianceSettings(config: AuditLoggingSettings) {

export function AuditLogging(props: AuditLoggingProps) {
const [configuration, setConfiguration] = React.useState<AuditLoggingSettings>({});
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) {
Expand All @@ -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.
Expand All @@ -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);

Expand Down Expand Up @@ -226,5 +237,15 @@ export function AuditLogging(props: AuditLoggingProps) {
);
}

return <div className="panel-restrict-width">{content}</div>;
return (
<div className="panel-restrict-width">
<SecurityPluginTopNavMenu
{...props}
dataSourcePickerReadOnly={false}
setDataSource={setDataSource}
selectedDataSource={dataSource}
/>
{content}
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,21 @@ exports[`Audit logs render when AuditLoggingSettings.enabled is true 1`] = `
<div
className="panel-restrict-width"
>
<SecurityPluginTopNavMenu
coreStart={
Object {
"http": 1,
}
}
dataSourcePickerReadOnly={false}
navigation={Object {}}
selectedDataSource={
Object {
"id": "test",
}
}
setDataSource={[MockFunction]}
/>
<EuiPanel>
<EuiTitle>
<h3>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down
10 changes: 9 additions & 1 deletion public/apps/configuration/panels/tenant-list/tenant-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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;
Expand Down Expand Up @@ -134,6 +136,12 @@ export function TenantList(props: TenantListProps) {

return (
<>
<SecurityPluginTopNavMenu
{...props}
dataSourcePickerReadOnly={true}
setDataSource={() => {}}
selectedDataSource={LocalCluster}
/>
<EuiPageHeader>
<EuiTitle size="l">
<h1>Multi-tenancy</h1>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ exports[`SecurityPluginTopNavMenu renders DataSourceMenu when dataSource is enab
value={
Object {
"dataSource": Object {
"checked": "on",
"id": "",
"label": "Local cluster",
},
Expand Down
17 changes: 12 additions & 5 deletions public/apps/configuration/utils/audit-logging-utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<AuditLoggingSettings> {
const rawConfiguration = await httpGet<any>({ http, url: API_ENDPOINT_AUDITLOGGING });
export async function getAuditLogging(
http: HttpStart,
query: HttpFetchQuery
): Promise<AuditLoggingSettings> {
const rawConfiguration = await httpGet<any>({ http, url: API_ENDPOINT_AUDITLOGGING, query });
return rawConfiguration?.config;
}
33 changes: 23 additions & 10 deletions server/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<IOpenSearchDashboardsResponse<any | ResponseError>> => {
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,
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit 06a9ae9

Please sign in to comment.