[8.x] [index management] better privilege checking for enrich policies (#201717) (#202592)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[index management] better privilege checking for enrich policies
(#201717)](https://github.com/elastic/kibana/pull/201717)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Matthew
Kime","email":"matt@mattki.me"},"sourceCommit":{"committedDate":"2024-12-02T21:52:33Z","message":"[index
management] better privilege checking for enrich policies
(#201717)\n\n## Summary\r\n\r\nKibana roles with only `manage_enrich` or
`monitor_enrich` will now have\r\naccess to the Enrich Policy tab in
Index Management.\r\n\r\n---\r\n\r\nThe `registerElasticsearchFeature`
api is too restrictive to use for\r\nindex management as it only allows
a single set of privileges to\r\ndetermine whether a given management
app is shown AND any stated\r\nprivilege is combined in an `AND` logic
statement. We need `OR` - index\r\nmanagement may cover a number of
different privileges that don't\r\noverlap. The solution - use an
observable to subscribe to the\r\ncapabilities api and register the
management app based on that.\r\n\r\nThis pr focuses on Enrich Policies
and removes UI elements as\r\nappropriate based on `manage_enrich` or
`monitor_enrich` and leaves\r\nother index management tabs alone as
these will be addressed in follow\r\nup PRs.\r\n\r\nPart of
https://github.com/elastic/kibana/issues/178654","sha":"a68dbaf1950ac3466d70b953838f00ffdeb73e32","branchLabelMapping":{"^v9.0.0$":"main","^v8.18.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["Feature:Index
Management","Team:Kibana
Management","release_note:skip","v9.0.0","backport:prev-minor"],"title":"[index
management] better privilege checking for enrich
policies","number":201717,"url":"https://github.com/elastic/kibana/pull/201717","mergeCommit":{"message":"[index
management] better privilege checking for enrich policies
(#201717)\n\n## Summary\r\n\r\nKibana roles with only `manage_enrich` or
`monitor_enrich` will now have\r\naccess to the Enrich Policy tab in
Index Management.\r\n\r\n---\r\n\r\nThe `registerElasticsearchFeature`
api is too restrictive to use for\r\nindex management as it only allows
a single set of privileges to\r\ndetermine whether a given management
app is shown AND any stated\r\nprivilege is combined in an `AND` logic
statement. We need `OR` - index\r\nmanagement may cover a number of
different privileges that don't\r\noverlap. The solution - use an
observable to subscribe to the\r\ncapabilities api and register the
management app based on that.\r\n\r\nThis pr focuses on Enrich Policies
and removes UI elements as\r\nappropriate based on `manage_enrich` or
`monitor_enrich` and leaves\r\nother index management tabs alone as
these will be addressed in follow\r\nup PRs.\r\n\r\nPart of
https://github.com/elastic/kibana/issues/178654","sha":"a68dbaf1950ac3466d70b953838f00ffdeb73e32"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/201717","number":201717,"mergeCommit":{"message":"[index
management] better privilege checking for enrich policies
(#201717)\n\n## Summary\r\n\r\nKibana roles with only `manage_enrich` or
`monitor_enrich` will now have\r\naccess to the Enrich Policy tab in
Index Management.\r\n\r\n---\r\n\r\nThe `registerElasticsearchFeature`
api is too restrictive to use for\r\nindex management as it only allows
a single set of privileges to\r\ndetermine whether a given management
app is shown AND any stated\r\nprivilege is combined in an `AND` logic
statement. We need `OR` - index\r\nmanagement may cover a number of
different privileges that don't\r\noverlap. The solution - use an
observable to subscribe to the\r\ncapabilities api and register the
management app based on that.\r\n\r\nThis pr focuses on Enrich Policies
and removes UI elements as\r\nappropriate based on `manage_enrich` or
`monitor_enrich` and leaves\r\nother index management tabs alone as
these will be addressed in follow\r\nup PRs.\r\n\r\nPart of
https://github.com/elastic/kibana/issues/178654","sha":"a68dbaf1950ac3466d70b953838f00ffdeb73e32"}}]}]
BACKPORT-->

Co-authored-by: Matthew Kime <matt@mattki.me>
This commit is contained in:
Kibana Machine 2024-12-03 10:44:44 +11:00 committed by GitHub
parent 0a091ed260
commit bc826427c8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 266 additions and 261 deletions

View file

@ -57,10 +57,6 @@ describe('Create enrich policy', () => {
beforeEach(async () => {
httpRequestsMockHelpers.setGetMatchingIndices(getMatchingIndices());
httpRequestsMockHelpers.setGetPrivilegesResponse({
hasAllPrivileges: true,
missingPrivileges: { cluster: [] },
});
httpRequestsMockHelpers.setGetMatchingDataStreams(getMatchingDataStreams());
await act(async () => {

View file

@ -188,9 +188,6 @@ const registerHttpRequestMockHelpers = (
error
);
const setGetPrivilegesResponse = (response?: HttpResponse, error?: ResponseError) =>
mockResponse('GET', `${INTERNAL_API_BASE_PATH}/enrich_policies/privileges`, response, error);
const setCreateEnrichPolicy = (response?: HttpResponse, error?: ResponseError) =>
mockResponse('POST', `${INTERNAL_API_BASE_PATH}/enrich_policies`, response, error);
@ -253,7 +250,6 @@ const registerHttpRequestMockHelpers = (
setCreateIndexResponse,
setGetMatchingIndices,
setGetFieldsFromIndices,
setGetPrivilegesResponse,
setCreateEnrichPolicy,
setInferenceModels,
setGetMatchingDataStreams,

View file

@ -91,6 +91,11 @@ const appDependencies = {
overlays: {
openConfirm: jest.fn(),
},
privs: {
monitor: true,
manageEnrich: true,
monitorEnrich: true,
},
} as any;
export const kibanaVersion = new SemVer(MAJOR_VERSION);

View file

@ -38,11 +38,6 @@ describe('Enrich policies tab', () => {
describe('empty states', () => {
beforeEach(async () => {
setDelayResponse(false);
httpRequestsMockHelpers.setGetPrivilegesResponse({
hasAllPrivileges: true,
missingPrivileges: { cluster: [] },
});
});
test('displays a loading prompt', async () => {
@ -82,24 +77,6 @@ describe('Enrich policies tab', () => {
});
});
describe('permissions check', () => {
it('shows a permissions error when the user does not have sufficient privileges', async () => {
httpRequestsMockHelpers.setGetPrivilegesResponse({
hasAllPrivileges: false,
missingPrivileges: { cluster: ['manage_enrich'] },
});
testBed = await setup(httpSetup);
await act(async () => {
testBed.actions.goToEnrichPoliciesTab();
});
testBed.component.update();
expect(testBed.exists('enrichPoliciesInsuficientPrivileges')).toBe(true);
});
});
describe('policies list', () => {
let testPolicy: ReturnType<typeof createTestEnrichPolicy>;
beforeEach(async () => {
@ -110,11 +87,6 @@ describe('Enrich policies tab', () => {
createTestEnrichPolicy('policy-range', 'range'),
]);
httpRequestsMockHelpers.setGetPrivilegesResponse({
hasAllPrivileges: true,
missingPrivileges: { cluster: [] },
});
testBed = await setup(httpSetup);
await act(async () => {
testBed.actions.goToEnrichPoliciesTab();

View file

@ -168,6 +168,11 @@ describe('index table', () => {
enableIndexActions: true,
enableIndexStats: true,
},
privs: {
monitor: true,
manageEnrich: true,
monitorEnrich: true,
},
};
component = (

View file

@ -80,6 +80,11 @@ export interface AppDependencies {
kibanaVersion: SemVer;
overlays: OverlayStart;
canUseSyntheticSource: boolean;
privs: {
monitor: boolean;
manageEnrich: boolean;
monitorEnrich: boolean;
};
}
export const AppContextProvider = ({

View file

@ -1,9 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
export { EnrichPoliciesWithPrivileges } from './with_privileges';
export { EnrichPoliciesAuthProvider } from './auth_provider';

View file

@ -1,82 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { FormattedMessage } from '@kbn/i18n-react';
import React, { FunctionComponent } from 'react';
import {
PageLoading,
PageError,
useAuthorizationContext,
WithPrivileges,
NotAuthorizedSection,
} from '../../../shared_imports';
import { ENRICH_POLICIES_REQUIRED_PRIVILEGES } from '../../constants';
export const EnrichPoliciesWithPrivileges: FunctionComponent<{
children?: React.ReactNode;
}> = ({ children }) => {
const { apiError } = useAuthorizationContext();
if (apiError) {
return (
<PageError
title={
<FormattedMessage
id="xpack.idxMgmt.home.enrichPolicies.checkingPrivilegesErrorMessage"
defaultMessage="Error fetching user privileges from the server."
/>
}
error={apiError}
/>
);
}
return (
<WithPrivileges
privileges={ENRICH_POLICIES_REQUIRED_PRIVILEGES.map((privilege) => `cluster.${privilege}`)}
>
{({ isLoading, hasPrivileges, privilegesMissing }) => {
if (isLoading) {
return (
<PageLoading>
<FormattedMessage
id="xpack.idxMgmt.home.enrichPolicies.checkingPrivilegesDescription"
defaultMessage="Checking privileges…"
/>
</PageLoading>
);
}
if (!hasPrivileges) {
return (
<NotAuthorizedSection
dataTestSubj="enrichPoliciesInsuficientPrivileges"
title={
<FormattedMessage
id="xpack.idxMgmt.home.enrichPolicies.deniedPrivilegeTitle"
defaultMessage="Manage enrich privileges required"
/>
}
message={
<FormattedMessage
id="xpack.idxMgmt.home.enrichPolicies.deniedPrivilegeDescription"
defaultMessage="To use Enrich Policies, you must have the following cluster privileges: {missingPrivileges}."
values={{
missingPrivileges: privilegesMissing.cluster!.join(', '),
}}
/>
}
/>
);
}
return <>{children}</>;
}}
</WithPrivileges>
);
};

View file

@ -73,6 +73,8 @@ export function getIndexManagementDependencies({
}): AppDependencies {
const { docLinks, application, uiSettings, settings } = core;
const { url } = startDependencies.share;
const { monitor, manageEnrich, monitorEnrich } = application.capabilities.index_management;
return {
core: {
getUrlForApp: application.getUrlForApp,
@ -103,6 +105,11 @@ export function getIndexManagementDependencies({
kibanaVersion,
overlays: core.overlays,
canUseSyntheticSource,
privs: {
monitor: !!monitor,
manageEnrich: !!manageEnrich,
monitorEnrich: !!monitorEnrich,
},
};
}

View file

@ -14,12 +14,8 @@ import { breadcrumbService, IndexManagementBreadcrumb } from '../../services/bre
import { CreatePolicyWizard } from './create_policy_wizard';
import { CreatePolicyContextProvider } from './create_policy_context';
import {
EnrichPoliciesAuthProvider,
EnrichPoliciesWithPrivileges,
} from '../../components/enrich_policies';
const CreateView: React.FunctionComponent<RouteComponentProps> = () => {
export const EnrichPolicyCreate: React.FunctionComponent<RouteComponentProps> = () => {
useEffect(() => {
breadcrumbService.setBreadcrumbs(IndexManagementBreadcrumb.enrichPoliciesCreate);
}, []);
@ -64,11 +60,3 @@ const CreateView: React.FunctionComponent<RouteComponentProps> = () => {
</CreatePolicyContextProvider>
);
};
export const EnrichPolicyCreate: React.FunctionComponent<RouteComponentProps> = (props) => (
<EnrichPoliciesAuthProvider>
<EnrichPoliciesWithPrivileges>
<CreateView {...props} />
</EnrichPoliciesWithPrivileges>
</EnrichPoliciesAuthProvider>
);

View file

@ -17,10 +17,6 @@ import { APP_WRAPPER_CLASS, useExecutionContext } from '../../../../shared_impor
import { useAppContext } from '../../../app_context';
import { useRedirectPath } from '../../../hooks/redirect_path';
import {
EnrichPoliciesAuthProvider,
EnrichPoliciesWithPrivileges,
} from '../../../components/enrich_policies';
import { breadcrumbService, IndexManagementBreadcrumb } from '../../../services/breadcrumbs';
import { documentationService } from '../../../services/documentation';
import { useLoadEnrichPolicies } from '../../../services/api';
@ -34,9 +30,13 @@ const getEnrichPolicyNameFromLocation = (location: Location) => {
return policy;
};
const ListView: React.FunctionComponent<RouteComponentProps> = ({ history, location }) => {
export const EnrichPoliciesList: React.FunctionComponent<RouteComponentProps> = ({
history,
location,
}) => {
const {
core: { executionContext },
privs,
} = useAppContext();
const redirectTo = useRedirectPath(history);
@ -79,7 +79,7 @@ const ListView: React.FunctionComponent<RouteComponentProps> = ({ history, locat
return <ErrorState error={error} resendRequest={reloadPolicies} />;
}
if (policies?.length === 0) {
if (privs.manageEnrich && policies?.length === 0) {
return <EmptyState />;
}
@ -151,11 +151,3 @@ const ListView: React.FunctionComponent<RouteComponentProps> = ({ history, locat
</div>
);
};
export const EnrichPoliciesList: React.FunctionComponent<RouteComponentProps> = (props) => (
<EnrichPoliciesAuthProvider>
<EnrichPoliciesWithPrivileges>
<ListView {...props} />
</EnrichPoliciesWithPrivileges>
</EnrichPoliciesAuthProvider>
);

View file

@ -33,43 +33,84 @@ export const PoliciesTable: FunctionComponent<Props> = ({
onDeletePolicyClick,
onExecutePolicyClick,
}) => {
const { history } = useAppContext();
const { history, privs } = useAppContext();
const renderToolsRight = () => {
return [
<EuiButton
key="reloadPolicies"
data-test-subj="reloadPoliciesButton"
iconType="refresh"
color="success"
onClick={onReloadClick}
>
<FormattedMessage
id="xpack.idxMgmt.enrichPolicies.table.reloadButton"
defaultMessage="Reload"
/>
</EuiButton>,
<EuiButton
key="createPolicy"
fill
iconType="plusInCircle"
{...reactRouterNavigate(history, '/enrich_policies/create')}
>
<FormattedMessage
id="xpack.idxMgmt.enrichPolicies.table.createPolicyButton"
defaultMessage="Create enrich policy"
/>
</EuiButton>,
];
};
const createBtn = (
<EuiButton
key="createPolicy"
fill
iconType="plusInCircle"
data-test-subj="createPolicyButton"
{...reactRouterNavigate(history, '/enrich_policies/create')}
>
<FormattedMessage
id="xpack.idxMgmt.enrichPolicies.table.createPolicyButton"
defaultMessage="Create enrich policy"
/>
</EuiButton>
);
const toolsRight = [
<EuiButton
key="reloadPolicies"
data-test-subj="reloadPoliciesButton"
iconType="refresh"
color="success"
onClick={onReloadClick}
>
<FormattedMessage
id="xpack.idxMgmt.enrichPolicies.table.reloadButton"
defaultMessage="Reload"
/>
</EuiButton>,
];
if (privs.manageEnrich) {
toolsRight.push(createBtn);
}
const search: EuiSearchBarProps = {
toolsRight: renderToolsRight(),
toolsRight,
box: {
incremental: true,
},
};
const actions: EuiBasicTableColumn<SerializedEnrichPolicy> = {
name: i18n.translate('xpack.idxMgmt.enrichPolicies.table.actionsField', {
defaultMessage: 'Actions',
}),
actions: [
{
isPrimary: true,
name: i18n.translate('xpack.idxMgmt.enrichPolicies.table.executeAction', {
defaultMessage: 'Execute',
}),
description: i18n.translate('xpack.idxMgmt.enrichPolicies.table.executeDescription', {
defaultMessage: 'Execute this enrich policy',
}),
type: 'icon',
icon: 'play',
'data-test-subj': 'executePolicyButton',
onClick: ({ name }) => onExecutePolicyClick(name),
},
{
isPrimary: true,
name: i18n.translate('xpack.idxMgmt.enrichPolicies.table.deleteAction', {
defaultMessage: 'Delete',
}),
description: i18n.translate('xpack.idxMgmt.enrichPolicies.table.deleteDescription', {
defaultMessage: 'Delete this enrich policy',
}),
type: 'icon',
icon: 'trash',
color: 'danger',
'data-test-subj': 'deletePolicyButton',
onClick: ({ name }) => onDeletePolicyClick(name),
},
],
};
const columns: Array<EuiBasicTableColumn<SerializedEnrichPolicy>> = [
{
field: 'name',
@ -120,42 +161,12 @@ export const PoliciesTable: FunctionComponent<Props> = ({
truncateText: true,
render: (fields: string[]) => <span className="eui-textTruncate">{fields.join(', ')}</span>,
},
{
name: i18n.translate('xpack.idxMgmt.enrichPolicies.table.actionsField', {
defaultMessage: 'Actions',
}),
actions: [
{
isPrimary: true,
name: i18n.translate('xpack.idxMgmt.enrichPolicies.table.executeAction', {
defaultMessage: 'Execute',
}),
description: i18n.translate('xpack.idxMgmt.enrichPolicies.table.executeDescription', {
defaultMessage: 'Execute this enrich policy',
}),
type: 'icon',
icon: 'play',
'data-test-subj': 'executePolicyButton',
onClick: ({ name }) => onExecutePolicyClick(name),
},
{
isPrimary: true,
name: i18n.translate('xpack.idxMgmt.enrichPolicies.table.deleteAction', {
defaultMessage: 'Delete',
}),
description: i18n.translate('xpack.idxMgmt.enrichPolicies.table.deleteDescription', {
defaultMessage: 'Delete this enrich policy',
}),
type: 'icon',
icon: 'trash',
color: 'danger',
'data-test-subj': 'deletePolicyButton',
onClick: ({ name }) => onDeletePolicyClick(name),
},
],
},
];
if (privs.manageEnrich) {
columns.push(actions);
}
const { pageSize, sorting, onTableChange } = useEuiTablePersist<SerializedEnrichPolicy>({
tableId: 'enrichPolicies',
initialPageSize: 50,

View file

@ -41,6 +41,7 @@ export const IndexManagementHome: React.FunctionComponent<RouteComponentProps<Ma
}) => {
const {
plugins: { console: consolePlugin },
privs,
} = useAppContext();
const tabs = [
{
@ -74,7 +75,10 @@ export const IndexManagementHome: React.FunctionComponent<RouteComponentProps<Ma
/>
),
},
{
];
if (privs.monitorEnrich) {
tabs.push({
id: Section.EnrichPolicies,
name: (
<FormattedMessage
@ -82,8 +86,8 @@ export const IndexManagementHome: React.FunctionComponent<RouteComponentProps<Ma
defaultMessage="Enrich Policies"
/>
),
},
];
});
}
const onSectionChange = (newSection: Section) => {
history.push(`/${newSection}`);
@ -143,7 +147,9 @@ export const IndexManagementHome: React.FunctionComponent<RouteComponentProps<Ma
]}
component={ComponentTemplateList}
/>
<Route exact path={`/${Section.EnrichPolicies}`} component={EnrichPoliciesList} />
{privs.monitorEnrich && (
<Route exact path={`/${Section.EnrichPolicies}`} component={EnrichPoliciesList} />
)}
</Routes>
{consolePlugin?.EmbeddableConsole ? <consolePlugin.EmbeddableConsole /> : null}
</>

View file

@ -6,6 +6,7 @@
*/
import { i18n } from '@kbn/i18n';
import { Subject } from 'rxjs';
import SemVer from 'semver/classes/semver';
import {
@ -14,6 +15,7 @@ import {
Plugin,
PluginInitializerContext,
ScopedHistory,
Capabilities,
} from '@kbn/core/public';
import {
IndexManagementPluginSetup,
@ -61,6 +63,8 @@ export class IndexMgmtUIPlugin
private canUseSyntheticSource: boolean = false;
private licensingSubscription?: Subscription;
private capabilities$ = new Subject<Capabilities>();
constructor(ctx: PluginInitializerContext) {
// Temporary hack to provide the service instances in module files in order to avoid a big refactor
// For the selectors we should expose them through app dependencies and read them from there on each container component.
@ -98,29 +102,34 @@ export class IndexMgmtUIPlugin
coreSetup: CoreSetup<StartDependencies>,
plugins: SetupDependencies
): IndexManagementPluginSetup {
if (this.config.isIndexManagementUiEnabled) {
const { fleet, usageCollection, management, cloud } = plugins;
const { fleet, usageCollection, management, cloud } = plugins;
management.sections.section.data.registerApp({
id: PLUGIN.id,
title: i18n.translate('xpack.idxMgmt.appTitle', { defaultMessage: 'Index Management' }),
order: 0,
mount: async (params) => {
const { mountManagementSection } = await import('./application/mount_management_section');
return mountManagementSection({
coreSetup,
usageCollection,
params,
extensionsService: this.extensionsService,
isFleetEnabled: Boolean(fleet),
kibanaVersion: this.kibanaVersion,
config: this.config,
cloud,
canUseSyntheticSource: this.canUseSyntheticSource,
});
},
});
}
this.capabilities$.subscribe((capabilities) => {
const { monitor, manageEnrich, monitorEnrich } = capabilities.index_management;
if (this.config.isIndexManagementUiEnabled && (monitor || manageEnrich || monitorEnrich)) {
management.sections.section.data.registerApp({
id: PLUGIN.id,
title: i18n.translate('xpack.idxMgmt.appTitle', { defaultMessage: 'Index Management' }),
order: 0,
mount: async (params) => {
const { mountManagementSection } = await import(
'./application/mount_management_section'
);
return mountManagementSection({
coreSetup,
usageCollection,
params,
extensionsService: this.extensionsService,
isFleetEnabled: Boolean(fleet),
kibanaVersion: this.kibanaVersion,
config: this.config,
cloud,
canUseSyntheticSource: this.canUseSyntheticSource,
});
},
});
}
});
this.locator = plugins.share.url.locators.create(
new IndexManagementLocatorDefinition({
@ -138,6 +147,8 @@ export class IndexMgmtUIPlugin
public start(coreStart: CoreStart, plugins: StartDependencies): IndexManagementPluginStart {
const { fleet, usageCollection, cloud, share, console, ml, licensing } = plugins;
this.capabilities$.next(coreStart.application.capabilities);
this.licensingSubscription = licensing?.license$.subscribe((next) => {
this.canUseSyntheticSource = next.hasAtLeast('enterprise');
});

View file

@ -37,15 +37,20 @@ export class IndexMgmtServerPlugin implements Plugin<IndexManagementPluginSetup,
): IndexManagementPluginSetup {
features.registerElasticsearchFeature({
id: PLUGIN.id,
management: {
data: ['index_management'],
},
privileges: [
{
requiredClusterPrivileges: ['monitor_enrich'],
ui: ['monitorEnrich'],
},
{
requiredClusterPrivileges: ['manage_enrich'],
ui: ['manageEnrich'],
},
{
// manage_index_templates is also required, but we will disable specific parts of the
// UI if this privilege is missing.
requiredClusterPrivileges: ['monitor'],
ui: [],
ui: ['monitor'],
},
],
});

View file

@ -22667,10 +22667,6 @@
"xpack.idxMgmt.home.componentTemplates.list.loadingMessage": "Chargement des modèles de composants en cours…",
"xpack.idxMgmt.home.componentTemplatesTabTitle": "Modèles de composants",
"xpack.idxMgmt.home.dataStreamsTabTitle": "Flux de données",
"xpack.idxMgmt.home.enrichPolicies.checkingPrivilegesDescription": "Vérification des privilèges…",
"xpack.idxMgmt.home.enrichPolicies.checkingPrivilegesErrorMessage": "Erreur lors de la récupération des privilèges utilisateur depuis le serveur.",
"xpack.idxMgmt.home.enrichPolicies.deniedPrivilegeDescription": "Pour utiliser les Politiques d'enrichissement, vous devez disposer des privilèges de cluster suivants : {missingPrivileges}.",
"xpack.idxMgmt.home.enrichPolicies.deniedPrivilegeTitle": "Gestion des privilèges d'enrichissement requise",
"xpack.idxMgmt.home.enrichPoliciesTabTitle": "Politiques d'enrichissement",
"xpack.idxMgmt.home.idxMgmtDescription": "Mettez à jour vos index Elasticsearch individuellement ou par lots. {learnMoreLink}",
"xpack.idxMgmt.home.idxMgmtDocsLinkText": "Documentation sur la gestion des index",

View file

@ -22638,10 +22638,6 @@
"xpack.idxMgmt.home.componentTemplates.list.loadingMessage": "コンポーネントテンプレートを読み込んでいます…",
"xpack.idxMgmt.home.componentTemplatesTabTitle": "コンポーネントテンプレート",
"xpack.idxMgmt.home.dataStreamsTabTitle": "データストリーム",
"xpack.idxMgmt.home.enrichPolicies.checkingPrivilegesDescription": "権限を確認中…",
"xpack.idxMgmt.home.enrichPolicies.checkingPrivilegesErrorMessage": "サーバーからユーザー特権を取得中にエラーが発生。",
"xpack.idxMgmt.home.enrichPolicies.deniedPrivilegeDescription": "エンリッチポリシーを使用するには、以下のクラスター権限が必要です:{missingPrivileges}。",
"xpack.idxMgmt.home.enrichPolicies.deniedPrivilegeTitle": "必要なエンリッチ権限を管理",
"xpack.idxMgmt.home.enrichPoliciesTabTitle": "エンリッチポリシー",
"xpack.idxMgmt.home.idxMgmtDescription": "Elasticsearch インデックスを個々に、または一斉に更新します。{learnMoreLink}",
"xpack.idxMgmt.home.idxMgmtDocsLinkText": "インデックス管理ドキュメント",

View file

@ -22690,10 +22690,6 @@
"xpack.idxMgmt.home.componentTemplates.list.loadingMessage": "正在加载组件模板……",
"xpack.idxMgmt.home.componentTemplatesTabTitle": "组件模板",
"xpack.idxMgmt.home.dataStreamsTabTitle": "数据流",
"xpack.idxMgmt.home.enrichPolicies.checkingPrivilegesDescription": "正在检查权限……",
"xpack.idxMgmt.home.enrichPolicies.checkingPrivilegesErrorMessage": "从服务器获取用户权限时出错。",
"xpack.idxMgmt.home.enrichPolicies.deniedPrivilegeDescription": "要使用扩充策略,必须具有以下集群权限:{missingPrivileges}。",
"xpack.idxMgmt.home.enrichPolicies.deniedPrivilegeTitle": "管理所需的扩充权限",
"xpack.idxMgmt.home.enrichPoliciesTabTitle": "扩充策略",
"xpack.idxMgmt.home.idxMgmtDescription": "分别或批量更新您的 Elasticsearch 索引。{learnMoreLink}",
"xpack.idxMgmt.home.idxMgmtDocsLinkText": "索引管理文档",

View file

@ -10,8 +10,90 @@ import { FtrConfigProviderContext } from '@kbn/test';
export default async function ({ readConfigFile }: FtrConfigProviderContext) {
const functionalConfig = await readConfigFile(require.resolve('../../config.base.js'));
const baseConfig = functionalConfig.getAll();
baseConfig.security.roles.index_management_manage_index_templates = {
elasticsearch: {
cluster: ['manage_index_templates'],
indices: [
{
names: ['*'],
privileges: ['all'],
},
],
},
kibana: [
{
feature: {
advancedSettings: ['read'],
},
spaces: ['*'],
},
],
};
baseConfig.security.roles.index_management_monitor_only = {
elasticsearch: {
cluster: ['monitor'],
indices: [
{
names: ['*'],
privileges: ['all'],
},
],
},
kibana: [
{
feature: {
advancedSettings: ['read'],
},
spaces: ['*'],
},
],
};
baseConfig.security.roles.index_management_manage_enrich_only = {
elasticsearch: {
cluster: ['manage_enrich'],
indices: [
{
names: ['*'],
privileges: ['all'],
},
],
},
kibana: [
{
feature: {
advancedSettings: ['read'],
},
spaces: ['*'],
},
],
};
baseConfig.security.roles.index_management_monitor_enrich_only = {
elasticsearch: {
cluster: ['monitor_enrich'],
indices: [
{
names: ['*'],
privileges: ['all'],
},
],
},
kibana: [
{
feature: {
advancedSettings: ['read'],
},
spaces: ['*'],
},
],
};
return {
...functionalConfig.getAll(),
...baseConfig,
testFiles: [require.resolve('.')],
};
}

View file

@ -23,6 +23,12 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
describe('Enrich policies tab', function () {
before(async () => {
await log.debug('Creating required index and enrich policy');
try {
await es.indices.delete({ index: ENRICH_INDEX_NAME });
// eslint-disable-next-line no-empty
} catch (e) {}
try {
await es.indices.create({
index: ENRICH_INDEX_NAME,
@ -94,7 +100,22 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
expect(await successToast.getVisibleText()).to.contain(`Executed ${ENRICH_POLICY_NAME}`);
});
it('read only access', async () => {
await security.testUser.setRoles(['index_management_monitor_enrich_only']);
await pageObjects.common.navigateToApp('indexManagement');
await pageObjects.indexManagement.changeTabs('enrich_policiesTab');
await pageObjects.header.waitUntilLoadingHasFinished();
await testSubjects.missingOrFail('createPolicyButton');
await testSubjects.missingOrFail('deletePolicyButton');
});
it('can delete a policy', async () => {
await security.testUser.setRoles(['index_management_user']);
await pageObjects.common.navigateToApp('indexManagement');
// Navigate to the enrich policies tab
await pageObjects.indexManagement.changeTabs('enrich_policiesTab');
await pageObjects.header.waitUntilLoadingHasFinished();
// Since we disabled wait_for_completion in the server request, we dont know when
// a given policy will finish executing. Until that happens the policy cannot
// be deleted. 2s seems to be plenty enough to guarantee that, at least for this
@ -104,8 +125,14 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
await pageObjects.indexManagement.clickDeleteEnrichPolicyAt(0);
await pageObjects.indexManagement.clickConfirmModalButton();
const successToast = await toasts.getElementByIndex(2);
const successToast = await toasts.getElementByIndex(1);
expect(await successToast.getVisibleText()).to.contain(`Deleted ${ENRICH_POLICY_NAME}`);
});
it('no access', async () => {
await security.testUser.setRoles(['index_management_monitor_only']);
await pageObjects.common.navigateToApp('indexManagement');
await testSubjects.missingOrFail('enrich_policiesTab');
});
});
};