mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
# 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:
parent
0a091ed260
commit
bc826427c8
20 changed files with 266 additions and 261 deletions
|
@ -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 () => {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -168,6 +168,11 @@ describe('index table', () => {
|
|||
enableIndexActions: true,
|
||||
enableIndexStats: true,
|
||||
},
|
||||
privs: {
|
||||
monitor: true,
|
||||
manageEnrich: true,
|
||||
monitorEnrich: true,
|
||||
},
|
||||
};
|
||||
|
||||
component = (
|
||||
|
|
|
@ -80,6 +80,11 @@ export interface AppDependencies {
|
|||
kibanaVersion: SemVer;
|
||||
overlays: OverlayStart;
|
||||
canUseSyntheticSource: boolean;
|
||||
privs: {
|
||||
monitor: boolean;
|
||||
manageEnrich: boolean;
|
||||
monitorEnrich: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export const AppContextProvider = ({
|
||||
|
|
|
@ -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';
|
|
@ -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>
|
||||
);
|
||||
};
|
|
@ -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,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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}
|
||||
</>
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
|
|
|
@ -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'],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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": "インデックス管理ドキュメント",
|
||||
|
|
|
@ -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": "索引管理文档",
|
||||
|
|
|
@ -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('.')],
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue