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

[8.x] [Logs ML] Check permissions before granting access to Logs ML pages (#195278) #195759

Merged
merged 1 commit into from
Oct 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { UserManagementLink } from './user_management_link';

export const MissingResultsPrivilegesPrompt: React.FunctionComponent = () => (
<EmptyPrompt
data-test-subj="logsMissingMLReadPrivileges"
title={<h2>{missingMlPrivilegesTitle}</h2>}
body={<p>{missingMlResultsPrivilegesDescription}</p>}
actions={<UserManagementLink />}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { UserManagementLink } from './user_management_link';

export const MissingSetupPrivilegesPrompt: React.FunctionComponent = () => (
<EmptyPrompt
data-test-subj="logsMissingMLAllPrivileges"
title={<h2>{missingMlPrivilegesTitle}</h2>}
body={<p>{missingMlSetupPrivilegesDescription}</p>}
actions={<UserManagementLink />}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@

import { EuiErrorBoundary } from '@elastic/eui';
import React from 'react';
import { MissingResultsPrivilegesPrompt } from '../../../components/logging/log_analysis_setup';
import { useLogAnalysisCapabilitiesContext } from '../../../containers/logs/log_analysis';
import { SubscriptionSplashPage } from '../../../components/subscription_splash_content';
import { useLogsBreadcrumbs } from '../../../hooks/use_logs_breadcrumbs';
import { LogEntryCategoriesPageContent } from './page_content';
import { CategoriesPageTemplate, LogEntryCategoriesPageContent } from './page_content';
import { LogEntryCategoriesPageProviders } from './page_providers';
import { logCategoriesTitle } from '../../../translations';
import { LogMlJobIdFormatsShimProvider } from '../shared/use_log_ml_job_id_formats_shim';
Expand All @@ -20,6 +23,28 @@ export const LogEntryCategoriesPage = () => {
},
]);

const { hasLogAnalysisReadCapabilities, hasLogAnalysisCapabilites } =
useLogAnalysisCapabilitiesContext();

if (!hasLogAnalysisCapabilites) {
return (
<SubscriptionSplashPage
data-test-subj="logsLogEntryCategoriesPage"
pageHeader={{
pageTitle: logCategoriesTitle,
}}
/>
);
}

if (!hasLogAnalysisReadCapabilities) {
return (
<CategoriesPageTemplate isEmptyState={true}>
<MissingResultsPrivilegesPrompt />
</CategoriesPageTemplate>
);
}

return (
<EuiErrorBoundary>
<LogMlJobIdFormatsShimProvider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,12 @@ import { isJobStatusWithResults, logEntryCategoriesJobType } from '../../../../c
import { LoadingPage } from '../../../components/loading_page';
import {
LogAnalysisSetupStatusUnknownPrompt,
MissingResultsPrivilegesPrompt,
MissingSetupPrivilegesPrompt,
} from '../../../components/logging/log_analysis_setup';
import {
LogAnalysisSetupFlyout,
useLogAnalysisSetupFlyoutStateContext,
} from '../../../components/logging/log_analysis_setup/setup_flyout';
import { SubscriptionSplashPage } from '../../../components/subscription_splash_content';
import { useLogAnalysisCapabilitiesContext } from '../../../containers/logs/log_analysis';
import { useLogEntryCategoriesModuleContext } from '../../../containers/logs/log_analysis/modules/log_entry_categories';
import { LogsPageTemplate } from '../shared/page_template';
Expand All @@ -33,11 +31,8 @@ const logCategoriesTitle = i18n.translate('xpack.infra.logs.logCategoriesTitle',
});

export const LogEntryCategoriesPageContent = () => {
const {
hasLogAnalysisCapabilites,
hasLogAnalysisReadCapabilities,
hasLogAnalysisSetupCapabilities,
} = useLogAnalysisCapabilitiesContext();
const { hasLogAnalysisReadCapabilities, hasLogAnalysisSetupCapabilities } =
useLogAnalysisCapabilitiesContext();

const { fetchJobStatus, setupStatus, jobStatus } = useLogEntryCategoriesModuleContext();

Expand All @@ -55,22 +50,7 @@ export const LogEntryCategoriesPageContent = () => {

const { idFormats } = useLogMlJobIdFormatsShimContext();

if (!hasLogAnalysisCapabilites) {
return (
<SubscriptionSplashPage
data-test-subj="logsLogEntryCategoriesPage"
pageHeader={{
pageTitle: logCategoriesTitle,
}}
/>
);
} else if (!hasLogAnalysisReadCapabilities) {
return (
<CategoriesPageTemplate isEmptyState={true}>
<MissingResultsPrivilegesPrompt />
</CategoriesPageTemplate>
);
} else if (setupStatus.type === 'initializing') {
if (setupStatus.type === 'initializing') {
return (
<LoadingPage
message={i18n.translate('xpack.infra.logs.logEntryCategories.jobStatusLoadingMessage', {
Expand Down Expand Up @@ -115,7 +95,7 @@ export const LogEntryCategoriesPageContent = () => {

const allowedSetupModules = ['logs_ui_categories' as const];

const CategoriesPageTemplate: React.FC<LazyObservabilityPageTemplateProps> = ({
export const CategoriesPageTemplate: React.FC<LazyObservabilityPageTemplateProps> = ({
children,
...rest
}) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@

import { EuiErrorBoundary } from '@elastic/eui';
import React from 'react';
import { LogEntryRatePageContent } from './page_content';
import { SubscriptionSplashPage } from '../../../components/subscription_splash_content';
import { MissingResultsPrivilegesPrompt } from '../../../components/logging/log_analysis_setup';
import { useLogAnalysisCapabilitiesContext } from '../../../containers/logs/log_analysis';
import { AnomaliesPageTemplate, LogEntryRatePageContent } from './page_content';
import { LogEntryRatePageProviders } from './page_providers';
import { useLogsBreadcrumbs } from '../../../hooks/use_logs_breadcrumbs';
import { logsAnomaliesTitle } from '../../../translations';
Expand All @@ -19,6 +22,29 @@ export const LogEntryRatePage = () => {
text: logsAnomaliesTitle,
},
]);

const { hasLogAnalysisReadCapabilities, hasLogAnalysisCapabilites } =
useLogAnalysisCapabilitiesContext();

if (!hasLogAnalysisCapabilites) {
return (
<SubscriptionSplashPage
data-test-subj="logsLogEntryRatePage"
pageHeader={{
pageTitle: logsAnomaliesTitle,
}}
/>
);
}

if (!hasLogAnalysisReadCapabilities) {
return (
<AnomaliesPageTemplate isEmptyState={true}>
<MissingResultsPrivilegesPrompt />
</AnomaliesPageTemplate>
);
}

return (
<EuiErrorBoundary>
<LogMlJobIdFormatsShimProvider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,12 @@ import {
import { LoadingPage } from '../../../components/loading_page';
import {
LogAnalysisSetupStatusUnknownPrompt,
MissingResultsPrivilegesPrompt,
MissingSetupPrivilegesPrompt,
} from '../../../components/logging/log_analysis_setup';
import {
LogAnalysisSetupFlyout,
useLogAnalysisSetupFlyoutStateContext,
} from '../../../components/logging/log_analysis_setup/setup_flyout';
import { SubscriptionSplashPage } from '../../../components/subscription_splash_content';
import { useLogAnalysisCapabilitiesContext } from '../../../containers/logs/log_analysis';
import { useLogEntryCategoriesModuleContext } from '../../../containers/logs/log_analysis/modules/log_entry_categories';
import { useLogEntryRateModuleContext } from '../../../containers/logs/log_analysis/modules/log_entry_rate';
Expand All @@ -41,11 +39,8 @@ const logsAnomaliesTitle = i18n.translate('xpack.infra.logs.anomaliesPageTitle',
});

export const LogEntryRatePageContent = memo(() => {
const {
hasLogAnalysisCapabilites,
hasLogAnalysisReadCapabilities,
hasLogAnalysisSetupCapabilities,
} = useLogAnalysisCapabilitiesContext();
const { hasLogAnalysisReadCapabilities, hasLogAnalysisSetupCapabilities } =
useLogAnalysisCapabilitiesContext();

const {
fetchJobStatus: fetchLogEntryCategoriesJobStatus,
Expand Down Expand Up @@ -96,22 +91,7 @@ export const LogEntryRatePageContent = memo(() => {

const { idFormats } = useLogMlJobIdFormatsShimContext();

if (!hasLogAnalysisCapabilites) {
return (
<SubscriptionSplashPage
data-test-subj="logsLogEntryRatePage"
pageHeader={{
pageTitle: logsAnomaliesTitle,
}}
/>
);
} else if (!hasLogAnalysisReadCapabilities) {
return (
<AnomaliesPageTemplate isEmptyState={true}>
<MissingResultsPrivilegesPrompt />
</AnomaliesPageTemplate>
);
} else if (
if (
logEntryCategoriesSetupStatus.type === 'initializing' ||
logEntryRateSetupStatus.type === 'initializing'
) {
Expand Down Expand Up @@ -159,7 +139,7 @@ export const LogEntryRatePageContent = memo(() => {
}
});

const AnomaliesPageTemplate: React.FC<LazyObservabilityPageTemplateProps> = ({
export const AnomaliesPageTemplate: React.FC<LazyObservabilityPageTemplateProps> = ({
children,
...rest
}) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,54 @@ import expect from '@kbn/expect';

import { FtrProviderContext } from '../../../ftr_provider_context';

export default ({ getService }: FtrProviderContext) => {
export default ({ getPageObjects, getService }: FtrProviderContext) => {
const PageObjects = getPageObjects(['security']);
const esArchiver = getService('esArchiver');
const logsUi = getService('logsUi');
const retry = getService('retry');
const security = getService('security');

describe('Log Entry Categories Tab', function () {
this.tags('includeFirefox');

const loginWithMLPrivileges = async (privileges: Record<string, string[]>) => {
await security.role.create('global_logs_role', {
elasticsearch: {
cluster: ['all'],
indices: [{ names: ['*'], privileges: ['read', 'view_index_metadata'] }],
},
kibana: [
{
feature: {
logs: ['read'],
...privileges,
},
spaces: ['*'],
},
],
});

await security.user.create('global_logs_read_user', {
password: 'global_logs_read_user-password',
roles: ['global_logs_role'],
full_name: 'logs test user',
});

await PageObjects.security.forceLogout();

await PageObjects.security.login('global_logs_read_user', 'global_logs_read_user-password', {
expectSpaceSelector: false,
});
};

const logoutAndDeleteUser = async () => {
await PageObjects.security.forceLogout();
await Promise.all([
security.role.delete('global_logs_role'),
security.user.delete('global_logs_read_user'),
]);
};

describe('with a trial license', () => {
it('Shows no data page when indices do not exist', async () => {
await logsUi.logEntryCategoriesPage.navigateTo();
Expand All @@ -26,14 +66,42 @@ export default ({ getService }: FtrProviderContext) => {
});
});

it('shows setup page when indices exist', async () => {
await esArchiver.load('x-pack/test/functional/es_archives/infra/simple_logs');
await logsUi.logEntryCategoriesPage.navigateTo();
describe('when indices exists', () => {
before(async () => {
await esArchiver.load('x-pack/test/functional/es_archives/infra/metrics_and_logs');
});

await retry.try(async () => {
expect(await logsUi.logEntryCategoriesPage.getSetupScreen()).to.be.ok();
after(async () => {
await esArchiver.unload('x-pack/test/functional/es_archives/infra/metrics_and_logs');
});

it('shows setup page when indices exist', async () => {
await logsUi.logEntryCategoriesPage.navigateTo();

await retry.try(async () => {
expect(await logsUi.logEntryCategoriesPage.getSetupScreen()).to.be.ok();
});
});

it('shows required ml read privileges prompt when the user has not any ml privileges', async () => {
await loginWithMLPrivileges({});
await logsUi.logEntryCategoriesPage.navigateTo();

await retry.try(async () => {
expect(await logsUi.logEntryCategoriesPage.getNoMlReadPrivilegesPrompt()).to.be.ok();
});
await logoutAndDeleteUser();
});

it('shows required ml all privileges prompt when the user has only ml read privileges', async () => {
await loginWithMLPrivileges({ ml: ['read'] });
await logsUi.logEntryCategoriesPage.navigateTo();

await retry.try(async () => {
expect(await logsUi.logEntryCategoriesPage.getNoMlAllPrivilegesPrompt()).to.be.ok();
});
await logoutAndDeleteUser();
});
await esArchiver.unload('x-pack/test/functional/es_archives/infra/simple_logs');
});
});
});
Expand Down
Loading
Loading