Skip to content

Commit

Permalink
Merge branch 'main' into view-password
Browse files Browse the repository at this point in the history
  • Loading branch information
DarshitChanpura authored Jun 3, 2024
2 parents a8f2fa3 + b3e444f commit 1b4003e
Show file tree
Hide file tree
Showing 36 changed files with 1,321 additions and 340 deletions.
35 changes: 35 additions & 0 deletions public/apps/configuration/access-error-component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright OpenSearch Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* or in the "license" file accompanying this file. This file 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 { EuiLoadingContent, EuiPageContent } from '@elastic/eui';
import React from 'react';

interface AccessErrorComponentProps {
loading?: boolean;
dataSourceLabel?: string;
message?: string;
}

export const AccessErrorComponent: React.FC<AccessErrorComponentProps> = (props) => {
const {
loading = false,
dataSourceLabel,
message = 'You do not have permissions to view this data',
} = props;

const displayMessage = message + (dataSourceLabel ? ` for ${props.dataSourceLabel}.` : '.');

return loading ? <EuiLoadingContent /> : <EuiPageContent>{displayMessage}</EuiPageContent>;
};
43 changes: 18 additions & 25 deletions public/apps/configuration/app-router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -163,30 +163,6 @@ export function AppRouter(props: AppDependencies) {

const [dataSource, setDataSource] = useState<DataSourceOption>(dataSourceFromUrl);

function getTenancyRoutes() {
if (multitenancyEnabled) {
return (
<>
<Route
path={ROUTE_MAP.tenants.href}
render={() => {
setGlobalBreadcrumbs(ResourceType.tenants);
return <TenantList tabID={'Manage'} {...props} />;
}}
/>
<Route
path={ROUTE_MAP.tenantsConfigureTab.href}
render={() => {
setGlobalBreadcrumbs(ResourceType.tenants);
return <TenantList tabID={'Configure'} {...props} />;
}}
/>
</>
);
}
return null;
}

return (
<DataSourceContext.Provider value={{ dataSource, setDataSource }}>
<Router basename={props.params.appBasePath}>
Expand Down Expand Up @@ -300,7 +276,24 @@ export function AppRouter(props: AppDependencies) {
return <GetStarted {...props} />;
}}
/>
{getTenancyRoutes()}
{multitenancyEnabled && (
<Route
path={ROUTE_MAP.tenants.href}
render={() => {
setGlobalBreadcrumbs(ResourceType.tenants);
return <TenantList tabID={'Manage'} {...props} />;
}}
/>
)}
{multitenancyEnabled && (
<Route
path={ROUTE_MAP.tenantsConfigureTab.href}
render={() => {
setGlobalBreadcrumbs(ResourceType.tenants);
return <TenantList tabID={'Configure'} {...props} />;
}}
/>
)}
<Redirect exact from="/" to={LANDING_PAGE_URL} />
</Switch>
</EuiPageBody>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export function AuditLoggingEditSettings(props: AuditLoggingEditSettingProps) {
};

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

const renderSaveAndCancel = () => {
return (
Expand Down
43 changes: 35 additions & 8 deletions public/apps/configuration/panels/audit-logging/audit-logging.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import {
EuiForm,
EuiFormRow,
EuiHorizontalRule,
EuiLoadingContent,
EuiPageHeader,
EuiPanel,
EuiSpacer,
EuiSwitch,
Expand All @@ -30,6 +32,7 @@ import {
} from '@elastic/eui';
import React, { useContext } from 'react';
import { FormattedMessage } from '@osd/i18n/react';
import { DataSourceOption } from 'src/plugins/data_source_management/public';
import { AppDependencies } from '../../../types';
import { ResourceType } from '../../../../../common';
import { getAuditLogging, updateAuditLogging } from '../../utils/audit-logging-utils';
Expand All @@ -45,6 +48,7 @@ import { ViewSettingGroup } from './view-setting-group';
import { DocLinks } from '../../constants';
import { DataSourceContext } from '../../app-router';
import { SecurityPluginTopNavMenu } from '../../top-nav-menu';
import { AccessErrorComponent } from '../../access-error-component';

interface AuditLoggingProps extends AppDependencies {
fromType: string;
Expand All @@ -53,10 +57,6 @@ interface AuditLoggingProps extends AppDependencies {
function renderStatusPanel(onSwitchChange: () => void, auditLoggingEnabled: boolean) {
return (
<EuiPanel>
<EuiTitle>
<h3>Audit logging</h3>
</EuiTitle>
<EuiHorizontalRule margin="m" />
<EuiForm>
<EuiDescribedFormGroup title={<h3>Storage location</h3>} className="described-form-group">
<EuiFormRow className="form-row">
Expand Down Expand Up @@ -93,6 +93,16 @@ function renderStatusPanel(onSwitchChange: () => void, auditLoggingEnabled: bool
);
}

function renderAccessErrorPanel(loading: boolean, dataSource: DataSourceOption) {
return (
<AccessErrorComponent
loading={loading}
dataSourceLabel={dataSource && dataSource.label}
message="You do not have permissions to configure audit logging settings"
/>
);
}

export function renderGeneralSettings(config: AuditLoggingSettings) {
return (
<>
Expand Down Expand Up @@ -137,6 +147,8 @@ export function renderComplianceSettings(config: AuditLoggingSettings) {
export function AuditLogging(props: AuditLoggingProps) {
const [configuration, setConfiguration] = React.useState<AuditLoggingSettings>({});
const { dataSource, setDataSource } = useContext(DataSourceContext)!;
const [loading, setLoading] = React.useState(false);
const [accessErrorFlag, setAccessErrorFlag] = React.useState(false);

const onSwitchChange = async () => {
try {
Expand All @@ -154,29 +166,38 @@ export function AuditLogging(props: AuditLoggingProps) {
React.useEffect(() => {
const fetchData = async () => {
try {
setLoading(true);
const auditLogging = await getAuditLogging(props.coreStart.http, dataSource.id);
setConfiguration(auditLogging);
setAccessErrorFlag(false);
} catch (e) {
// TODO: switch to better error handling.
console.log(e);
// requests with existing credentials but insufficient permissions result in 403, remote data-source requests with non-existing credentials result in 400
if (e.response && [400, 403].includes(e.response.status)) {
setAccessErrorFlag(true);
}
} finally {
setLoading(false);
}
};

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

const statusPanel = renderStatusPanel(onSwitchChange, configuration.enabled || false);

let content;

if (!configuration.enabled) {
if (accessErrorFlag) {
content = renderAccessErrorPanel(loading, dataSource);
} else if (!configuration.enabled) {
content = statusPanel;
} else {
content = (
<>
{statusPanel}
<EuiSpacer />

<EuiPanel data-test-subj="general-settings">
<EuiFlexGroup>
<EuiFlexItem>
Expand Down Expand Up @@ -237,7 +258,13 @@ export function AuditLogging(props: AuditLoggingProps) {
setDataSource={setDataSource}
selectedDataSource={dataSource}
/>
{content}
<EuiPageHeader>
<EuiTitle size="l">
<h1>Audit Logging</h1>
</EuiTitle>
</EuiPageHeader>
<EuiSpacer />
{loading ? <EuiLoadingContent /> : content}
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -256,15 +256,17 @@ exports[`Audit logs render when AuditLoggingSettings.enabled is true 1`] = `
}
setDataSource={[MockFunction]}
/>
<EuiPanel>
<EuiTitle>
<h3>
Audit logging
</h3>
<EuiPageHeader>
<EuiTitle
size="l"
>
<h1>
Audit Logging
</h1>
</EuiTitle>
<EuiHorizontalRule
margin="m"
/>
</EuiPageHeader>
<EuiSpacer />
<EuiPanel>
<EuiForm>
<EuiDescribedFormGroup
className="described-form-group"
Expand Down Expand Up @@ -644,3 +646,39 @@ exports[`Audit logs render when AuditLoggingSettings.enabled is true 1`] = `
</EuiPanel>
</div>
`;

exports[`Audit logs should load access error component 1`] = `
<div
className="panel-restrict-width"
>
<Memo()
coreStart={
Object {
"http": 1,
}
}
dataSourcePickerReadOnly={false}
navigation={Object {}}
selectedDataSource={
Object {
"id": "test",
}
}
setDataSource={[MockFunction]}
/>
<EuiPageHeader>
<EuiTitle
size="l"
>
<h1>
Audit Logging
</h1>
</EuiTitle>
</EuiPageHeader>
<EuiSpacer />
<AccessErrorComponent
loading={false}
message="You do not have permissions to configure audit logging settings"
/>
</div>
`;
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,11 @@ describe('Audit logs', () => {
};

beforeEach(() => {
jest.spyOn(React, 'useState').mockImplementation((initialValue) => [initialValue, setState]);
jest.spyOn(React, 'useState').mockRestore();
jest
.spyOn(React, 'useState')
.mockImplementationOnce(() => [[], setState])
.mockImplementationOnce(() => [false, jest.fn()]);
jest.spyOn(React, 'useEffect').mockImplementationOnce((f) => f());
});

Expand Down Expand Up @@ -140,7 +144,11 @@ describe('Audit logs', () => {

it('render when AuditLoggingSettings.enabled is true', () => {
const auditLoggingSettings = { enabled: true };
jest.spyOn(React, 'useState').mockImplementation(() => [auditLoggingSettings, setState]);
jest.spyOn(React, 'useState').mockRestore();
jest
.spyOn(React, 'useState')
.mockImplementationOnce(() => [auditLoggingSettings, setState])
.mockImplementationOnce(() => [false, jest.fn()]);
const component = shallow(
<AuditLogging coreStart={mockCoreStart as any} navigation={{} as any} />
);
Expand All @@ -149,7 +157,11 @@ describe('Audit logs', () => {

it('Click Configure button of general setting section', () => {
const auditLoggingSettings = { enabled: true };
jest.spyOn(React, 'useState').mockImplementation(() => [auditLoggingSettings, setState]);
jest.spyOn(React, 'useState').mockRestore();
jest
.spyOn(React, 'useState')
.mockImplementationOnce(() => [auditLoggingSettings, setState])
.mockImplementationOnce(() => [false, jest.fn()]);
const component = shallow(
<AuditLogging coreStart={mockCoreStart as any} navigation={{} as any} />
);
Expand All @@ -161,7 +173,11 @@ describe('Audit logs', () => {

it('Click Configure button of Compliance settings section', () => {
const auditLoggingSettings = { enabled: true };
jest.spyOn(React, 'useState').mockImplementation(() => [auditLoggingSettings, setState]);
jest.spyOn(React, 'useState').mockRestore();
jest
.spyOn(React, 'useState')
.mockImplementationOnce(() => [auditLoggingSettings, setState])
.mockImplementationOnce(() => [false, jest.fn()]);
const component = shallow(
<AuditLogging coreStart={mockCoreStart as any} navigation={{} as any} />
);
Expand All @@ -170,4 +186,20 @@ describe('Audit logs', () => {
buildHashUrl(ResourceType.auditLogging) + SUB_URL_FOR_COMPLIANCE_SETTINGS_EDIT
);
});

it('should load access error component', () => {
const auditLoggingSettings = { enabled: true };
mockAuditLoggingUtils.getAuditLogging = jest
.fn()
.mockRejectedValue({ response: { status: 403 } });
jest
.spyOn(React, 'useState')
.mockImplementationOnce(() => [auditLoggingSettings, setState])
.mockImplementationOnce(() => [false, jest.fn()])
.mockImplementationOnce(() => [true, jest.fn()]);
const component = shallow(
<AuditLogging coreStart={mockCoreStart as any} navigation={{} as any} />
);
expect(component).toMatchSnapshot();
});
});
Loading

0 comments on commit 1b4003e

Please sign in to comment.