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

[Logs Explorer] Fix privilege checks bug #198368

Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,19 @@
*/

import { History } from 'history';
import { CoreStart } from '@kbn/core/public';
import React from 'react';
import { AppStatus, CoreStart } from '@kbn/core/public';
import React, { useMemo } from 'react';
import ReactDOM from 'react-dom';
import { Router, Routes, Route } from '@kbn/shared-ux-router';
import { AppMountParameters } from '@kbn/core/public';
import { Storage } from '@kbn/kibana-utils-plugin/public';
import { AllDatasetsLocatorParams, ALL_DATASETS_LOCATOR_ID } from '@kbn/deeplinks-observability';
import {
AllDatasetsLocatorParams,
ALL_DATASETS_LOCATOR_ID,
OBSERVABILITY_LOGS_EXPLORER_APP_ID,
} from '@kbn/deeplinks-observability';
import useObservable from 'react-use/lib/useObservable';
import { map } from 'rxjs';
import { LinkToLogsPage } from '../pages/link_to/link_to_logs';
import { LogsPage } from '../pages/logs';
import { InfraClientStartDeps, InfraClientStartExports } from '../types';
Expand Down Expand Up @@ -57,7 +63,22 @@ const LogsApp: React.FC<{
storage: Storage;
theme$: AppMountParameters['theme$'];
}> = ({ core, history, pluginStart, plugins, setHeaderActionMenu, storage, theme$ }) => {
const { logs, discover, fleet } = core.application.capabilities;
const { logs } = core.application.capabilities;

const isLogsExplorerAppAccessible = useObservable(
useMemo(
() =>
core.application.applications$.pipe(
map(
(apps) =>
(apps.get(OBSERVABILITY_LOGS_EXPLORER_APP_ID)?.status ?? AppStatus.inaccessible) ===
AppStatus.accessible
)
),
[core.application.applications$]
),
false
);

return (
<CoreProviders core={core} pluginStart={pluginStart} plugins={plugins} theme$={theme$}>
Expand All @@ -74,7 +95,7 @@ const LogsApp: React.FC<{
toastsService={core.notifications.toasts}
>
<Routes>
{Boolean(discover?.show && fleet?.read) && (
{isLogsExplorerAppAccessible && (
<Route
path="/"
exact
Expand Down
73 changes: 39 additions & 34 deletions x-pack/plugins/observability_solution/infra/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,15 @@ import {
MetricsExplorerLocatorParams,
ObservabilityTriggerId,
} from '@kbn/observability-shared-plugin/common';
import { BehaviorSubject, combineLatest, from } from 'rxjs';
import { map } from 'rxjs';
import {
BehaviorSubject,
combineLatest,
distinctUntilChanged,
from,
of,
switchMap,
map,
} from 'rxjs';
import type { EmbeddableApiContext } from '@kbn/presentation-publishing';
import { apiCanAddNewPanel } from '@kbn/presentation-containers';
import { IncompatibleActionError, ADD_PANEL_TRIGGER } from '@kbn/ui-actions-plugin/public';
Expand Down Expand Up @@ -132,14 +139,18 @@ export class Plugin implements InfraClientPluginClass {
messageFields: this.config.sources?.default?.fields?.message,
});

const startDep$AndHostViewFlag$ = combineLatest([from(core.getStartServices())]);
const startDep$AndAccessibleFlag$ = from(core.getStartServices()).pipe(
switchMap(([{ application }]) =>
combineLatest([of(application), getLogsExplorerAccessible$(application)])
)
);

const logRoutes = getLogsAppRoutes({ isLogsStreamEnabled });

/** !! Need to be kept in sync with the deepLinks in x-pack/plugins/observability_solution/infra/public/plugin.ts */
pluginsSetup.observabilityShared.navigation.registerSections(
startDep$AndHostViewFlag$.pipe(
map(([[{ application }]]) => {
startDep$AndAccessibleFlag$.pipe(
map(([application, isLogsExplorerAccessible]) => {
const { infrastructure, logs } = application.capabilities;
return [
...(logs.show
Expand All @@ -148,7 +159,7 @@ export class Plugin implements InfraClientPluginClass {
label: logsTitle,
sortKey: 200,
entries: getLogsNavigationEntries({
application,
isLogsExplorerAccessible,
config: this.config,
routes: logRoutes,
}),
Expand Down Expand Up @@ -310,16 +321,13 @@ export class Plugin implements InfraClientPluginClass {
);
},
});

startDep$AndHostViewFlag$.subscribe(
([_startServices]: [[CoreStart, InfraClientStartDeps, InfraClientStartExports]]) => {
this.appUpdater$.next(() => ({
deepLinks: getInfraDeepLinks({
metricsExplorerEnabled: this.config.featureFlags.metricsExplorerEnabled,
}),
}));
}
);
startDep$AndAccessibleFlag$.subscribe(([_applicationStart, _isLogsExplorerAccessible]) => {
this.appUpdater$.next(() => ({
deepLinks: getInfraDeepLinks({
metricsExplorerEnabled: this.config.featureFlags.metricsExplorerEnabled,
}),
}));
});

// Setup telemetry events
this.telemetry.setup({ analytics: core.analytics });
Expand Down Expand Up @@ -382,28 +390,26 @@ export class Plugin implements InfraClientPluginClass {
}

const getLogsNavigationEntries = ({
application,
isLogsExplorerAccessible,
config,
routes,
}: {
application: CoreStart['application'];
isLogsExplorerAccessible: boolean;
config: InfraPublicConfig;
routes: LogsAppRoutes;
}) => {
const entries: NavigationEntry[] = [];

if (!config.featureFlags.logsUIEnabled) return entries;

getLogsExplorerAccessibility$(application).subscribe((isAccessible) => {
if (isAccessible) {
entries.push({
label: 'Explorer',
app: 'observability-logs-explorer',
path: '/',
isBetaFeature: true,
});
}
});
if (isLogsExplorerAccessible) {
entries.push({
label: 'Explorer',
app: 'observability-logs-explorer',
path: '/',
isBetaFeature: true,
});
}

// Display Stream nav entry when Logs Stream is enabled
if (routes.stream) entries.push(createNavEntryFromRoute(routes.stream));
Expand All @@ -416,16 +422,15 @@ const getLogsNavigationEntries = ({
return entries;
};

const getLogsExplorerAccessibility$ = (application: CoreStart['application']) => {
const { capabilities, applications$ } = application;
const getLogsExplorerAccessible$ = (application: CoreStart['application']) => {
const { applications$ } = application;
return applications$.pipe(
map(
(apps) =>
(apps.get(OBSERVABILITY_LOGS_EXPLORER_APP_ID)?.status ?? AppStatus.inaccessible) ===
AppStatus.accessible &&
capabilities.discover?.show &&
capabilities.fleet?.read
)
AppStatus.accessible
),
distinctUntilChanged()
);
};

Expand Down