mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[index management] better privilege checking for enrich policies (#201717)
## Summary Kibana roles with only `manage_enrich` or `monitor_enrich` will now have access to the Enrich Policy tab in Index Management. --- The `registerElasticsearchFeature` api is too restrictive to use for index management as it only allows a single set of privileges to determine whether a given management app is shown AND any stated privilege is combined in an `AND` logic statement. We need `OR` - index management may cover a number of different privileges that don't overlap. The solution - use an observable to subscribe to the capabilities api and register the management app based on that. This pr focuses on Enrich Policies and removes UI elements as appropriate based on `manage_enrich` or `monitor_enrich` and leaves other index management tabs alone as these will be addressed in follow up PRs. Part of https://github.com/elastic/kibana/issues/178654
This commit is contained in:
parent
b527373431
commit
a68dbaf195
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'],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
|
|
@ -22629,10 +22629,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",
|
||||
|
|
|
@ -22600,10 +22600,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": "インデックス管理ドキュメント",
|
||||
|
|
|
@ -22208,10 +22208,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