Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conform to Navigation changes from OSD core #2022

Merged
merged 11 commits into from
Jul 19, 2024
26 changes: 13 additions & 13 deletions public/apps/configuration/app-router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ import { Action, RouteItem, SubAction } from './types';
import { ResourceType } from '../../../common';
import { buildHashUrl, buildUrl } from './utils/url-builder';
import { CrossPageToast } from './cross-page-toast';
import { getDataSourceFromUrl } from '../../utils/datasource-utils';
import { getDataSourceFromUrl, LocalCluster } from '../../utils/datasource-utils';

const LANDING_PAGE_URL = '/getstarted';

const ROUTE_MAP: { [key: string]: RouteItem } = {
export const ROUTE_MAP: { [key: string]: RouteItem } = {
getStarted: {
name: 'Get Started',
href: LANDING_PAGE_URL,
Expand Down Expand Up @@ -145,11 +145,10 @@ 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) {
console.log(props.redirect);
derek-ho marked this conversation as resolved.
Show resolved Hide resolved
const multitenancyEnabled = props.config.multitenancy.enabled;
const dataSourceEnabled = !!props.depsStart.dataSource?.dataSourceEnabled;
const setGlobalBreadcrumbs = flow(getBreadcrumbs, props.coreStart.chrome.setBreadcrumbs);
Expand All @@ -161,14 +160,15 @@ export function AppRouter(props: AppDependencies) {
<DataSourceContext.Provider value={{ dataSource, setDataSource }}>
<Router>
<EuiPage>
{allNavPanelUrls(multitenancyEnabled).map((route) => (
// Create different routes to update the 'selected' nav item .
<Route key={route} path={route} exact>
<EuiPageSideBar>
<NavPanel items={getRouteList(multitenancyEnabled)} />
</EuiPageSideBar>
</Route>
))}
{!props.coreStart.chrome.navGroup.getNavGroupEnabled() &&
allNavPanelUrls(multitenancyEnabled).map((route) => (
// Create different routes to update the 'selected' nav item .
<Route key={route} path={route} exact>
<EuiPageSideBar>
<NavPanel items={getRouteList(multitenancyEnabled)} />
</EuiPageSideBar>
</Route>
))}
<EuiPageBody>
<Switch>
<Route
Expand Down Expand Up @@ -281,7 +281,7 @@ export function AppRouter(props: AppDependencies) {
}}
/>
)}
<Redirect exact from="/" to={LANDING_PAGE_URL} />
<Redirect exact from="/" to={props.redirect} />
</Switch>
</EuiPageBody>
<CrossPageToast />
Expand Down
2 changes: 2 additions & 0 deletions public/apps/configuration/configuration-app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export function renderApp(
depsStart: SecurityPluginStartDependencies,
params: AppMountParameters,
config: ClientConfigType,
redirect: string,
dataSourceManagement?: DataSourceManagementPluginSetup
) {
const deps = {
Expand All @@ -36,6 +37,7 @@ export function renderApp(
params,
config,
dataSourceManagement,
redirect,
};
ReactDOM.render(
<I18nProvider>
Expand Down
6 changes: 5 additions & 1 deletion public/apps/configuration/top-nav-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ import React from 'react';
import { DataSourceSelectableConfig } from 'src/plugins/data_source_management/public';
import { DataSourceOption } from 'src/plugins/data_source_management/public/components/data_source_menu/types';
import { AppDependencies } from '../types';
import { setDataSourceInUrl } from '../../utils/datasource-utils';
import {
setDataSourceInUrl,
setDataSource as setDataSourceInSubscription,
} from '../../utils/datasource-utils';

export interface TopNavMenuProps extends AppDependencies {
dataSourcePickerReadOnly: boolean;
Expand All @@ -44,6 +47,7 @@ export const SecurityPluginTopNavMenu = React.memo(
const wrapSetDataSourceWithUpdateUrl = (dataSources: DataSourceOption[]) => {
setDataSourceInUrl(dataSources[0]);
setDataSource(dataSources[0]);
setDataSourceInSubscription(dataSources[0]);
};

return dataSourceEnabled ? (
Expand Down
1 change: 1 addition & 0 deletions public/apps/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export interface AppDependencies {
config: ClientConfigType;
dashboardsInfo: DashboardsInfo;
dataSourceManagement: DataSourceManagementPluginSetup;
redirect?: string;
}

export interface BreadcrumbsPageDependencies extends AppDependencies {
Expand Down
159 changes: 157 additions & 2 deletions public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,17 @@ import { BehaviorSubject } from 'rxjs';
import { SavedObjectsManagementColumn } from 'src/plugins/saved_objects_management/public';
import { i18n } from '@osd/i18n';
import {
AppCategory,
AppMountParameters,
AppStatus,
AppUpdater,
CoreSetup,
CoreStart,
DEFAULT_APP_CATEGORIES,
DEFAULT_NAV_GROUPS,
Plugin,
PluginInitializerContext,
WorkspaceAvailability,
} from '../../../src/core/public';
import { APP_ID_LOGIN, CUSTOM_ERROR_PAGE_URI, LOGIN_PAGE_URI, PLUGIN_NAME } from '../common';
import { APP_ID_CUSTOMERROR } from '../common';
Expand All @@ -50,6 +53,11 @@ import { addTenantToShareURL } from './services/shared-link';
import { interceptError } from './utils/logout-utils';
import { tenantColumn, getNamespacesToRegister } from './apps/configuration/utils/tenant-utils';
import { getDashboardsInfoSafe } from './utils/dashboards-info-utils';
import {
dataSource$,
getDataSourceEnabledUrl,
getDataSourceFromUrl,
} from './utils/datasource-utils';

async function hasApiPermission(core: CoreSetup): Promise<boolean | undefined> {
try {
Expand All @@ -68,8 +76,13 @@ const APP_ID_DASHBOARDS = 'dashboards';
// OpenSearchDashboards app is for legacy url migration
const APP_ID_OPENSEARCH_DASHBOARDS = 'kibana';
const APP_LIST_FOR_READONLY_ROLE = [APP_ID_HOME, APP_ID_DASHBOARDS, APP_ID_OPENSEARCH_DASHBOARDS];
const GLOBAL_TENANT_RENDERING_TEXT = 'Global';
const PRIVATE_TENANT_RENDERING_TEXT = 'Private';

const dataAccessUsersCategory: AppCategory & { group?: AppCategory } = {
id: 'dataAccessAndUsers',
label: 'Data Access and Users',
order: 9000,
euiIconType: 'managementApp',
};

export class SecurityPlugin
implements
Expand All @@ -82,6 +95,15 @@ export class SecurityPlugin
// @ts-ignore : initializerContext not used
constructor(private readonly initializerContext: PluginInitializerContext) {}

private updateDefaultRouteOfSecurityApplications: AppUpdater = () => {
const url = getDataSourceEnabledUrl(getDataSourceFromUrl());
return {
defaultPath: `?${url.searchParams.toString()}`,
};
};

private appStateUpdater = new BehaviorSubject(this.updateDefaultRouteOfSecurityApplications);

public async setup(
core: CoreSetup,
deps: SecurityPluginSetupDependencies
Expand All @@ -97,11 +119,33 @@ export class SecurityPlugin
(config.readonly_mode?.roles || DEFAULT_READONLY_ROLES).includes(role)
);

const mountWrapper = async (params: AppMountParameters, redirect: string) => {
const { renderApp } = await import('./apps/configuration/configuration-app');
const [coreStart, depsStart] = await core.getStartServices();

// merge OpenSearchDashboards yml configuration
includeClusterPermissions(config.clusterPermissions.include);
includeIndexPermissions(config.indexPermissions.include);

excludeFromDisabledTransportCategories(config.disabledTransportCategories.exclude);
excludeFromDisabledRestCategories(config.disabledRestCategories.exclude);

return renderApp(
coreStart,
depsStart as SecurityPluginStartDependencies,
params,
config,
redirect,
deps.dataSourceManagement
);
};

if (mdsEnabled || apiPermission) {
core.application.register({
id: PLUGIN_NAME,
title: 'Security',
order: 9050,
workspaceAvailability: WorkspaceAvailability.outsideWorkspace,
derek-ho marked this conversation as resolved.
Show resolved Hide resolved
mount: async (params: AppMountParameters) => {
const { renderApp } = await import('./apps/configuration/configuration-app');
const [coreStart, depsStart] = await core.getStartServices();
Expand All @@ -118,12 +162,117 @@ export class SecurityPlugin
depsStart as SecurityPluginStartDependencies,
params,
config,
'/getstarted',
deps.dataSourceManagement
);
},
category: DEFAULT_APP_CATEGORIES.management,
});

if (core.chrome.navGroup.getNavGroupEnabled()) {
core.application.register({
id: `security-dashboards-plugin_getstarted`,
title: 'Get Started',
order: 8040,
workspaceAvailability: WorkspaceAvailability.outsideWorkspace,
updater$: this.appStateUpdater,
mount: async (params: AppMountParameters) => {
return mountWrapper(params, '/getstarted');
},
});
core.application.register({
id: `security-dashboards-plugin_auth`,
title: 'Authentication',
order: 8040,
workspaceAvailability: WorkspaceAvailability.outsideWorkspace,
updater$: this.appStateUpdater,
mount: async (params: AppMountParameters) => {
return mountWrapper(params, '/auth');
},
});
core.application.register({
id: `security-dashboards-plugin_roles`,
title: 'Roles',
order: 8040,
workspaceAvailability: WorkspaceAvailability.outsideWorkspace,
updater$: this.appStateUpdater,
mount: async (params: AppMountParameters) => {
return mountWrapper(params, '/roles');
},
});
core.application.register({
id: `security-dashboards-plugin_users`,
title: 'Internal users',
order: 8040,
workspaceAvailability: WorkspaceAvailability.outsideWorkspace,
updater$: this.appStateUpdater,
mount: async (params: AppMountParameters) => {
return mountWrapper(params, '/users');
},
});
core.application.register({
id: `security-dashboards-plugin_permissions`,
title: 'Permissions',
order: 8040,
workspaceAvailability: WorkspaceAvailability.outsideWorkspace,
updater$: this.appStateUpdater,
mount: async (params: AppMountParameters) => {
return mountWrapper(params, '/permissions');
},
});
core.application.register({
id: `security-dashboards-plugin_tenants`,
title: 'Tenants',
order: 8040,
workspaceAvailability: WorkspaceAvailability.outsideWorkspace,
updater$: this.appStateUpdater,
mount: async (params: AppMountParameters) => {
return mountWrapper(params, '/tenants');
},
});
core.application.register({
id: `security-dashboards-plugin_auditlog`,
title: 'Audit logs',
order: 8040,
workspaceAvailability: WorkspaceAvailability.outsideWorkspace,
updater$: this.appStateUpdater,
mount: async (params: AppMountParameters) => {
return mountWrapper(params, '/auditLogging');
},
});
}

core.chrome.navGroup.addNavLinksToGroup(DEFAULT_NAV_GROUPS.dataAdministration, [
{
id: `security-dashboards-plugin_getstarted`,
category: dataAccessUsersCategory,
},
{
id: `security-dashboards-plugin_auth`,
category: dataAccessUsersCategory,
},
{
id: `security-dashboards-plugin_roles`,
category: dataAccessUsersCategory,
},
{
id: `security-dashboards-plugin_users`,
category: dataAccessUsersCategory,
},
{
id: `security-dashboards-plugin_permissions`,
category: dataAccessUsersCategory,
},
{
id: `security-dashboards-plugin_tenants`,
category: dataAccessUsersCategory,
},
{
id: `security-dashboards-plugin_auditlog`,
category: dataAccessUsersCategory,
},
]);

if (deps.managementOverview) {
deps.managementOverview.register({
id: PLUGIN_NAME,
Expand All @@ -135,6 +284,12 @@ export class SecurityPlugin
}),
});
}

dataSource$.subscribe((dataSourceOption) => {
if (dataSourceOption) {
this.appStateUpdater.next(this.updateDefaultRouteOfSecurityApplications);
}
});
}

core.application.register({
Expand Down
19 changes: 17 additions & 2 deletions public/utils/datasource-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
* permissions and limitations under the License.
*/

import { BehaviorSubject } from 'rxjs';
import { DataSourceOption } from 'src/plugins/data_source_management/public/components/data_source_menu/types';

const DATASOURCEURLKEY = 'dataSource';
Expand All @@ -35,8 +36,22 @@ export function getDataSourceFromUrl(): DataSourceOption {
}
}

export function setDataSourceInUrl(dataSource: DataSourceOption) {
export function getDataSourceEnabledUrl(dataSource: DataSourceOption) {
const url = new URL(window.location.href);
url.searchParams.set(DATASOURCEURLKEY, JSON.stringify(dataSource));
window.history.replaceState({}, '', url.toString());
return url;
}

export function setDataSourceInUrl(dataSource: DataSourceOption) {
window.history.replaceState({}, '', getDataSourceEnabledUrl(dataSource).toString());
}

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

export const dataSource$ = new BehaviorSubject<DataSourceOption>(
getDataSourceFromUrl() || LocalCluster
);

export function setDataSource(dataSource: DataSourceOption) {
dataSource$.next(dataSource);
}
Loading