From d965c7fc8295e18fad135e8f6185444a42d65959 Mon Sep 17 00:00:00 2001 From: mohamedhamed-ahmed Date: Wed, 30 Oct 2024 08:43:35 +0000 Subject: [PATCH] [Logs Explorer] Fix Privileges Accessibility (#193894) closes https://github.com/elastic/kibana/issues/192062 This PR adds privileges checks for `Logs Explorerer` it checks for `Discover & Fleet` privileges before allowing the user access to `Logs Explorer`. Clicking on the `Logs` tab from the side nav defaults to `Stream`, as long as its not depricated, in case the user doesn't have access to `Logs Explorer` https://github.com/user-attachments/assets/a4105ec0-7681-40ee-b2fd-e39b9c178dcf (cherry picked from commit dbfd4f0879aa89c49b379cc2c6c5feb74f5c16c7) --- .../components/logs_deprecation_callout.tsx | 26 +++- .../infra/public/plugin.ts | 144 +++++++++--------- 2 files changed, 92 insertions(+), 78 deletions(-) diff --git a/x-pack/plugins/observability_solution/infra/public/components/logs_deprecation_callout.tsx b/x-pack/plugins/observability_solution/infra/public/components/logs_deprecation_callout.tsx index 63107f4a4d031..21e61c08d281b 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/logs_deprecation_callout.tsx +++ b/x-pack/plugins/observability_solution/infra/public/components/logs_deprecation_callout.tsx @@ -9,13 +9,17 @@ import { EuiCallOut } from '@elastic/eui'; import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButton } from '@elastic/eui'; -import { AllDatasetsLocatorParams, ALL_DATASETS_LOCATOR_ID } from '@kbn/deeplinks-observability'; +import { + AllDatasetsLocatorParams, + ALL_DATASETS_LOCATOR_ID, + DatasetLocatorParams, +} from '@kbn/deeplinks-observability'; import { getRouterLinkProps } from '@kbn/router-utils'; import useLocalStorage from 'react-use/lib/useLocalStorage'; import { euiThemeVars } from '@kbn/ui-theme'; import { css } from '@emotion/css'; -import { SharePublicStart } from '@kbn/share-plugin/public/plugin'; +import { LocatorPublic } from '@kbn/share-plugin/common'; import { useKibanaContextForPlugin } from '../hooks/use_kibana'; const pageConfigurations = { @@ -44,14 +48,22 @@ interface LogsDeprecationCalloutProps { export const LogsDeprecationCallout = ({ page }: LogsDeprecationCalloutProps) => { const { - services: { share }, + services: { + share, + application: { + capabilities: { discover, fleet }, + }, + }, } = useKibanaContextForPlugin(); const { dismissalStorageKey, message } = pageConfigurations[page]; const [isDismissed, setDismissed] = useLocalStorage(dismissalStorageKey, false); - if (isDismissed) { + const allDatasetLocator = + share.url.locators.get(ALL_DATASETS_LOCATOR_ID); + + if (isDismissed || !(allDatasetLocator && discover?.show && fleet?.read)) { return null; } @@ -71,7 +83,7 @@ export const LogsDeprecationCallout = ({ page }: LogsDeprecationCalloutProps) => fill data-test-subj="infraLogsDeprecationCalloutTryLogsExplorerButton" color="warning" - {...getLogsExplorerLinkProps(share)} + {...getLogsExplorerLinkProps(allDatasetLocator)} > {i18n.translate('xpack.infra.logsDeprecationCallout.tryLogsExplorerButtonLabel', { defaultMessage: 'Try Logs Explorer', @@ -81,9 +93,7 @@ export const LogsDeprecationCallout = ({ page }: LogsDeprecationCalloutProps) => ); }; -const getLogsExplorerLinkProps = (share: SharePublicStart) => { - const locator = share.url.locators.get(ALL_DATASETS_LOCATOR_ID)!; - +const getLogsExplorerLinkProps = (locator: LocatorPublic) => { return getRouterLinkProps({ href: locator.getRedirectUrl({}), onClick: () => locator.navigate({}), diff --git a/x-pack/plugins/observability_solution/infra/public/plugin.ts b/x-pack/plugins/observability_solution/infra/public/plugin.ts index daaa3510e1660..b1744d0c98169 100644 --- a/x-pack/plugins/observability_solution/infra/public/plugin.ts +++ b/x-pack/plugins/observability_solution/infra/public/plugin.ts @@ -13,6 +13,7 @@ import { DEFAULT_APP_CATEGORIES, PluginInitializerContext, AppDeepLinkLocations, + AppStatus, } from '@kbn/core/public'; import { i18n } from '@kbn/i18n'; import { enableInfrastructureHostsView } from '@kbn/observability-plugin/public'; @@ -35,6 +36,7 @@ import { } from '@kbn/observability-shared-plugin/common'; import { OBSERVABILITY_ENABLE_LOGS_STREAM } from '@kbn/management-settings-ids'; import { NavigationEntry } from '@kbn/observability-shared-plugin/public'; +import { OBSERVABILITY_LOGS_EXPLORER_APP_ID } from '@kbn/deeplinks-observability/constants'; import type { InfraPublicConfig } from '../common/plugin_config_types'; import { createInventoryMetricRuleType } from './alerting/inventory'; import { createLogThresholdRuleType } from './alerting/log_threshold'; @@ -141,66 +143,53 @@ export class Plugin implements InfraClientPluginClass { /** !! 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: { capabilities }, - }, - ], - isInfrastructureHostsViewEnabled, - ]) => { - const { infrastructure, logs } = capabilities; - return [ - ...(logs.show - ? [ - { - label: logsTitle, - sortKey: 200, - entries: getLogsNavigationEntries({ - capabilities, - config: this.config, - routes: logRoutes, - }), - }, - ] - : []), - ...(infrastructure.show - ? [ - { - label: metricsTitle, - sortKey: 300, - entries: [ - { - label: inventoryTitle, - app: 'metrics', - path: '/inventory', - }, - ...(this.config.featureFlags.metricsExplorerEnabled - ? [ - { - label: metricsExplorerTitle, - app: 'metrics', - path: '/explorer', - }, - ] - : []), - ...(isInfrastructureHostsViewEnabled - ? [ - { - label: hostsTitle, - app: 'metrics', - path: '/hosts', - }, - ] - : []), - ], - }, - ] - : []), - ]; - } - ) + map(([[{ application }]]) => { + const { infrastructure, logs } = application.capabilities; + return [ + ...(logs.show + ? [ + { + label: logsTitle, + sortKey: 200, + entries: getLogsNavigationEntries({ + application, + config: this.config, + routes: logRoutes, + }), + }, + ] + : []), + ...(infrastructure.show + ? [ + { + label: metricsTitle, + sortKey: 300, + entries: [ + { + label: inventoryTitle, + app: 'metrics', + path: '/inventory', + }, + ...(this.config.featureFlags.metricsExplorerEnabled + ? [ + { + label: metricsExplorerTitle, + app: 'metrics', + path: '/explorer', + }, + ] + : []), + { + label: hostsTitle, + app: 'metrics', + path: '/hosts', + }, + ], + }, + ] + : []), + ]; + }) ) ); @@ -408,11 +397,11 @@ export class Plugin implements InfraClientPluginClass { } const getLogsNavigationEntries = ({ - capabilities, + application, config, routes, }: { - capabilities: CoreStart['application']['capabilities']; + application: CoreStart['application']; config: InfraPublicConfig; routes: LogsAppRoutes; }) => { @@ -420,14 +409,16 @@ const getLogsNavigationEntries = ({ if (!config.featureFlags.logsUIEnabled) return entries; - if (capabilities.discover?.show && capabilities.fleet?.read) { - entries.push({ - label: 'Explorer', - app: 'observability-logs-explorer', - path: '/', - isBetaFeature: true, - }); - } + getLogsExplorerAccessibility$(application).subscribe((isAccessible) => { + if (isAccessible) { + 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)); @@ -440,6 +431,19 @@ const getLogsNavigationEntries = ({ return entries; }; +const getLogsExplorerAccessibility$ = (application: CoreStart['application']) => { + const { capabilities, 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 + ) + ); +}; + const createNavEntryFromRoute = ({ path, title }: LogsRoute): NavigationEntry => ({ app: 'logs', label: title,