Skip to content

Commit

Permalink
Merge branch 'main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
cwperks authored Jun 10, 2024
2 parents 0aa44fb + 9abc57d commit d6c989f
Show file tree
Hide file tree
Showing 55 changed files with 1,590 additions and 653 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/cypress-test-tenancy-disabled.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,4 @@ jobs:
git clone https://github.com/opensearch-project/opensearch-dashboards-functional-test.git
cd opensearch-dashboards-functional-test
npm install cypress --save-dev
yarn cypress:run-with-security --browser chrome --spec "cypress/integration/plugins/security-dashboards-plugin/inaccessible_tenancy_features.js"
yarn cypress:run-with-security --browser chrome --spec "cypress/integration/plugins/security/inaccessible_tenancy_features.js"
2 changes: 1 addition & 1 deletion .github/workflows/unit-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ ubuntu-latest , windows-latest, macos-latest ]
os: [ ubuntu-latest , windows-latest ]
runs-on: ${{ matrix.os }}

steps:
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@
"resolutions": {
"selenium-webdriver": "4.10.0",
"glob-parent": "^5.1.2",
"debug": "^4.3.4"
"debug": "^4.3.4",
"ejs": "^3.1.10",
"express": "^4.19.2"
}
}
3 changes: 3 additions & 0 deletions public/apps/account/password-reset-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ export function PasswordResetPanel(props: PasswordResetPanelProps) {
setIsCurrentPasswordInvalid(false);
}}
isInvalid={isCurrentPasswordInvalid}
type="dual"
/>
</FormRow>
<EuiSpacer />
Expand All @@ -141,6 +142,7 @@ export function PasswordResetPanel(props: PasswordResetPanelProps) {
setIsNewPasswordInvalid(false);
setIsRepeatNewPasswordInvalid(repeatNewPassword !== newPassword);
}}
type="dual"
isInvalid={isNewPasswordInvalid}
/>
</FormRow>
Expand All @@ -162,6 +164,7 @@ export function PasswordResetPanel(props: PasswordResetPanelProps) {
setRepeatNewPassword(value);
setIsRepeatNewPasswordInvalid(value !== newPassword);
}}
type="dual"
/>
</FormRow>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,23 @@
* permissions and limitations under the License.
*/

import { EuiLoadingContent, EuiPageContent } from '@elastic/eui';
import React from 'react';
import { EuiSpacer, EuiText, EuiTitle } from '@elastic/eui';

export function TenantInstructionView() {
return (
<>
<EuiTitle size="l">
<h1>Tenants</h1>
</EuiTitle>
interface AccessErrorComponentProps {
loading?: boolean;
dataSourceLabel?: string;
message?: string;
}

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

<EuiText textAlign="center">
<h2>You have not enabled multi tenancy</h2>
</EuiText>
const displayMessage = message + (dataSourceLabel ? ` for ${props.dataSourceLabel}.` : '.');

<EuiText textAlign="center" size="xs" color="subdued" className="instruction-text">
Contact your administrator to enable multi tenancy.
</EuiText>
</>
);
}
return loading ? <EuiLoadingContent /> : <EuiPageContent>{displayMessage}</EuiPageContent>;
};
76 changes: 41 additions & 35 deletions public/apps/configuration/app-router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,6 @@ const ROUTE_MAP: { [key: string]: RouteItem } = {
name: 'Tenants',
href: buildUrl(ResourceType.tenants),
},
[ResourceType.tenantsManageTab]: {
name: 'TenantsManageTab',
href: buildUrl(ResourceType.tenantsManageTab),
},
[ResourceType.tenantsConfigureTab]: {
name: '',
href: buildUrl(ResourceType.tenantsConfigureTab),
Expand All @@ -88,22 +84,27 @@ const ROUTE_MAP: { [key: string]: RouteItem } = {
},
};

const ROUTE_LIST = [
ROUTE_MAP.getStarted,
ROUTE_MAP[ResourceType.auth],
ROUTE_MAP[ResourceType.roles],
ROUTE_MAP[ResourceType.users],
ROUTE_MAP[ResourceType.serviceAccounts],
ROUTE_MAP[ResourceType.permissions],
ROUTE_MAP[ResourceType.tenants],
ROUTE_MAP[ResourceType.auditLogging],
ROUTE_MAP[ResourceType.tenantsConfigureTab],
];
const getRouteList = (multitenancyEnabled: boolean) => {
return [
ROUTE_MAP.getStarted,
ROUTE_MAP[ResourceType.auth],
ROUTE_MAP[ResourceType.roles],
ROUTE_MAP[ResourceType.users],
ROUTE_MAP[ResourceType.serviceAccounts],
ROUTE_MAP[ResourceType.permissions],
...(multitenancyEnabled ? [ROUTE_MAP[ResourceType.tenants]] : []),
ROUTE_MAP[ResourceType.auditLogging],
];
};

const allNavPanelUrls = ROUTE_LIST.map((route) => route.href).concat([
buildUrl(ResourceType.auditLogging) + SUB_URL_FOR_GENERAL_SETTINGS_EDIT,
buildUrl(ResourceType.auditLogging) + SUB_URL_FOR_COMPLIANCE_SETTINGS_EDIT,
]);
export const allNavPanelUrls = (multitenancyEnabled: boolean) =>
getRouteList(multitenancyEnabled)
.map((route) => route.href)
.concat([
buildUrl(ResourceType.auditLogging) + SUB_URL_FOR_GENERAL_SETTINGS_EDIT,
buildUrl(ResourceType.auditLogging) + SUB_URL_FOR_COMPLIANCE_SETTINGS_EDIT,
...(multitenancyEnabled ? [buildUrl(ResourceType.tenantsConfigureTab)] : []),
]);

export function getBreadcrumbs(
resourceType?: ResourceType,
Expand Down Expand Up @@ -155,6 +156,7 @@ export const LocalCluster = { label: 'Local cluster', id: '' };
export const DataSourceContext = createContext<DataSourceContextType | null>(null);

export function AppRouter(props: AppDependencies) {
const multitenancyEnabled = props.config.multitenancy.enabled;
const dataSourceEnabled = !!props.depsStart.dataSource?.dataSourceEnabled;
const setGlobalBreadcrumbs = flow(getBreadcrumbs, props.coreStart.chrome.setBreadcrumbs);
const dataSourceFromUrl = dataSourceEnabled ? getDataSourceFromUrl() : LocalCluster;
Expand All @@ -165,11 +167,11 @@ export function AppRouter(props: AppDependencies) {
<DataSourceContext.Provider value={{ dataSource, setDataSource }}>
<Router basename={props.params.appBasePath}>
<EuiPage>
{allNavPanelUrls.map((route) => (
{allNavPanelUrls(multitenancyEnabled).map((route) => (
// Create different routes to update the 'selected' nav item .
<Route key={route} path={route} exact>
<EuiPageSideBar>
<NavPanel items={ROUTE_LIST} />
<NavPanel items={getRouteList(multitenancyEnabled)} />
</EuiPageSideBar>
</Route>
))}
Expand Down Expand Up @@ -267,27 +269,31 @@ export function AppRouter(props: AppDependencies) {
return <PermissionList {...props} />;
}}
/>
<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} />;
}}
/>
<Route
path={ROUTE_MAP.getStarted.href}
render={() => {
setGlobalBreadcrumbs();
return <GetStarted {...props} />;
}}
/>
{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>
);
}
Loading

0 comments on commit d6c989f

Please sign in to comment.