mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[eem] return 403 if user is not authorized to enable entity discovery (#189423)
Return 403 if user is not authorized to enable or disable entity discovery
This commit is contained in:
parent
1949fa6c4b
commit
c0fa819927
8 changed files with 55 additions and 36 deletions
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
import React, { useState } from 'react';
|
||||
import { useKibana } from '@kbn/kibana-react-plugin/public';
|
||||
import { ERROR_USER_NOT_AUTHORIZED } from '@kbn/entityManager-plugin/public';
|
||||
import useToggle from 'react-use/lib/useToggle';
|
||||
import {
|
||||
EuiButtonIcon,
|
||||
|
@ -22,6 +21,7 @@ import {
|
|||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { IHttpFetchError, ResponseErrorBody } from '@kbn/core/public';
|
||||
import { EntityManagerUnauthorizedError } from '@kbn/entityManager-plugin/public';
|
||||
import { TechnicalPreviewBadge } from '../technical_preview_badge';
|
||||
import { ApmPluginStartDeps } from '../../../plugin';
|
||||
import { useEntityManagerEnablementContext } from '../../../context/entity_manager_context/use_entity_manager_enablement_context';
|
||||
|
@ -77,16 +77,16 @@ export function EntityEnablement({ label, tooltip }: { label: string; tooltip?:
|
|||
}
|
||||
refetch();
|
||||
} else {
|
||||
if (response.reason === ERROR_USER_NOT_AUTHORIZED) {
|
||||
setIsLoading(false);
|
||||
setsIsUnauthorizedModalVisible(true);
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Error(response.message);
|
||||
}
|
||||
} catch (error) {
|
||||
setIsLoading(false);
|
||||
|
||||
if (error instanceof EntityManagerUnauthorizedError) {
|
||||
setsIsUnauthorizedModalVisible(true);
|
||||
return;
|
||||
}
|
||||
|
||||
const err = error as Error | IHttpFetchError<ResponseErrorBody>;
|
||||
notifications.toasts.danger({
|
||||
title: i18n.translate('xpack.apm.eemEnablement.errorTitle', {
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
export const ERROR_API_KEY_NOT_FOUND = 'api_key_not_found';
|
||||
export const ERROR_API_KEY_NOT_VALID = 'api_key_not_valid';
|
||||
export const ERROR_USER_NOT_AUTHORIZED = 'user_not_authorized';
|
||||
export const ERROR_API_KEY_SERVICE_DISABLED = 'api_key_service_disabled';
|
||||
export const ERROR_PARTIAL_BUILTIN_INSTALLATION = 'partial_builtin_installation';
|
||||
export const ERROR_DEFINITION_STOPPED = 'error_definition_stopped';
|
||||
|
|
|
@ -22,8 +22,9 @@ export type EntityManagerAppId = 'entityManager';
|
|||
export {
|
||||
ERROR_API_KEY_NOT_FOUND,
|
||||
ERROR_API_KEY_NOT_VALID,
|
||||
ERROR_USER_NOT_AUTHORIZED,
|
||||
ERROR_API_KEY_SERVICE_DISABLED,
|
||||
ERROR_PARTIAL_BUILTIN_INSTALLATION,
|
||||
ERROR_DEFINITION_STOPPED,
|
||||
} from '../common/errors';
|
||||
|
||||
export { EntityManagerUnauthorizedError } from './lib/errors';
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
import { HttpStart } from '@kbn/core/public';
|
||||
import { EntityManagerUnauthorizedError } from './errors';
|
||||
import { IEntityClient } from '../types';
|
||||
import {
|
||||
ManagedEntityEnabledResponse,
|
||||
|
@ -21,10 +22,24 @@ export class EntityClient implements IEntityClient {
|
|||
}
|
||||
|
||||
async enableManagedEntityDiscovery(): Promise<EnableManagedEntityResponse> {
|
||||
return await this.http.put('/internal/entities/managed/enablement');
|
||||
try {
|
||||
return await this.http.put('/internal/entities/managed/enablement');
|
||||
} catch (err) {
|
||||
if (err.body?.statusCode === 403) {
|
||||
throw new EntityManagerUnauthorizedError(err.body.message);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
async disableManagedEntityDiscovery(): Promise<DisableManagedEntityResponse> {
|
||||
return await this.http.delete('/internal/entities/managed/enablement');
|
||||
try {
|
||||
return await this.http.delete('/internal/entities/managed/enablement');
|
||||
} catch (err) {
|
||||
if (err.body?.statusCode === 403) {
|
||||
throw new EntityManagerUnauthorizedError(err.body.message);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* 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 class EntityManagerUnauthorizedError extends Error {
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
}
|
||||
}
|
|
@ -9,7 +9,6 @@ import { RequestHandlerContext } from '@kbn/core/server';
|
|||
import { schema } from '@kbn/config-schema';
|
||||
import { SetupRouteOptions } from '../types';
|
||||
import { deleteEntityDiscoveryAPIKey, readEntityDiscoveryAPIKey } from '../../lib/auth';
|
||||
import { ERROR_USER_NOT_AUTHORIZED } from '../../../common/errors';
|
||||
import { uninstallBuiltInEntityDefinitions } from '../../lib/entities/uninstall_entity_definition';
|
||||
import { canDisableEntityDiscovery } from '../../lib/auth/privileges';
|
||||
import { EntityDiscoveryApiKeyType } from '../../saved_objects';
|
||||
|
@ -33,10 +32,8 @@ export function disableEntityDiscoveryRoute<T extends RequestHandlerContext>({
|
|||
const esClient = (await context.core).elasticsearch.client.asCurrentUser;
|
||||
const canDisable = await canDisableEntityDiscovery(esClient);
|
||||
if (!canDisable) {
|
||||
return res.ok({
|
||||
return res.forbidden({
|
||||
body: {
|
||||
success: false,
|
||||
reason: ERROR_USER_NOT_AUTHORIZED,
|
||||
message:
|
||||
'Current Kibana user does not have the required permissions to disable entity discovery',
|
||||
},
|
||||
|
|
|
@ -18,7 +18,7 @@ import {
|
|||
} from '../../lib/auth';
|
||||
import { builtInDefinitions } from '../../lib/entities/built_in';
|
||||
import { installBuiltInEntityDefinitions } from '../../lib/entities/install_entity_definition';
|
||||
import { ERROR_API_KEY_SERVICE_DISABLED, ERROR_USER_NOT_AUTHORIZED } from '../../../common/errors';
|
||||
import { ERROR_API_KEY_SERVICE_DISABLED } from '../../../common/errors';
|
||||
import { EntityDiscoveryApiKeyType } from '../../saved_objects';
|
||||
|
||||
export function enableEntityDiscoveryRoute<T extends RequestHandlerContext>({
|
||||
|
@ -48,10 +48,8 @@ export function enableEntityDiscoveryRoute<T extends RequestHandlerContext>({
|
|||
const esClient = (await context.core).elasticsearch.client.asCurrentUser;
|
||||
const canEnable = await canEnableEntityDiscovery(esClient);
|
||||
if (!canEnable) {
|
||||
return res.ok({
|
||||
return res.forbidden({
|
||||
body: {
|
||||
success: false,
|
||||
reason: ERROR_USER_NOT_AUTHORIZED,
|
||||
message:
|
||||
'Current Kibana user does not have the required permissions to enable entity discovery',
|
||||
},
|
||||
|
@ -75,7 +73,6 @@ export function enableEntityDiscoveryRoute<T extends RequestHandlerContext>({
|
|||
}
|
||||
|
||||
const apiKey = await generateEntityDiscoveryAPIKey(server, req);
|
||||
|
||||
if (apiKey === undefined) {
|
||||
return res.customError({
|
||||
statusCode: 500,
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import { ERROR_USER_NOT_AUTHORIZED } from '@kbn/entityManager-plugin/common/errors';
|
||||
import { builtInDefinitions } from '@kbn/entityManager-plugin/server/lib/entities/built_in';
|
||||
import { EntityDefinitionWithState } from '@kbn/entityManager-plugin/server/lib/entities/types';
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
@ -18,13 +17,14 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
const supertest = getService('supertestWithoutAuth');
|
||||
|
||||
const enablementRequest =
|
||||
(method: 'get' | 'put' | 'delete') => async (auth: Auth, query?: { [key: string]: any }) => {
|
||||
(method: 'get' | 'put' | 'delete') =>
|
||||
async (auth: Auth, expectedCode: number, query: { [key: string]: any } = {}) => {
|
||||
const response = await supertest[method]('/internal/entities/managed/enablement')
|
||||
.auth(auth.username, auth.password)
|
||||
.query(query)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send()
|
||||
.expect(200);
|
||||
.expect(expectedCode);
|
||||
return response.body;
|
||||
};
|
||||
|
||||
|
@ -45,7 +45,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
|
||||
describe('with authorized user', () => {
|
||||
it('should enable and disable entity discovery', async () => {
|
||||
const enableResponse = await enableEntityDiscovery(authorizedUser);
|
||||
const enableResponse = await enableEntityDiscovery(authorizedUser, 200);
|
||||
expect(enableResponse.success).to.eql(true, "authorized user can't enable EEM");
|
||||
|
||||
let definitionsResponse = await getInstalledDefinitions(supertest, authorizedUser);
|
||||
|
@ -64,19 +64,21 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
})
|
||||
).to.eql(true, 'all builtin definitions are not installed/running');
|
||||
|
||||
let stateResponse = await entityDiscoveryState(authorizedUser);
|
||||
let stateResponse = await entityDiscoveryState(authorizedUser, 200);
|
||||
expect(stateResponse.enabled).to.eql(
|
||||
true,
|
||||
`EEM is not enabled; response: ${JSON.stringify(stateResponse)}`
|
||||
);
|
||||
|
||||
const disableResponse = await disableEntityDiscovery(authorizedUser, { deleteData: false });
|
||||
const disableResponse = await disableEntityDiscovery(authorizedUser, 200, {
|
||||
deleteData: false,
|
||||
});
|
||||
expect(disableResponse.success).to.eql(
|
||||
true,
|
||||
`authorized user failed to disable EEM; response: ${JSON.stringify(disableResponse)}`
|
||||
);
|
||||
|
||||
stateResponse = await entityDiscoveryState(authorizedUser);
|
||||
stateResponse = await entityDiscoveryState(authorizedUser, 200);
|
||||
expect(stateResponse.enabled).to.eql(false, 'EEM is not disabled');
|
||||
|
||||
definitionsResponse = await getInstalledDefinitions(supertest, authorizedUser);
|
||||
|
@ -86,11 +88,9 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
|
||||
describe('with unauthorized user', () => {
|
||||
it('should fail to enable entity discovery', async () => {
|
||||
const enableResponse = await enableEntityDiscovery(unauthorizedUser);
|
||||
expect(enableResponse.success).to.eql(false, 'unauthorized user can enable EEM');
|
||||
expect(enableResponse.reason).to.eql(ERROR_USER_NOT_AUTHORIZED);
|
||||
await enableEntityDiscovery(unauthorizedUser, 403);
|
||||
|
||||
const stateResponse = await entityDiscoveryState(unauthorizedUser);
|
||||
const stateResponse = await entityDiscoveryState(unauthorizedUser, 200);
|
||||
expect(stateResponse.enabled).to.eql(false, 'EEM is enabled');
|
||||
|
||||
const definitionsResponse = await getInstalledDefinitions(supertest, unauthorizedUser);
|
||||
|
@ -98,14 +98,12 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
it('should fail to disable entity discovery', async () => {
|
||||
const enableResponse = await enableEntityDiscovery(authorizedUser);
|
||||
const enableResponse = await enableEntityDiscovery(authorizedUser, 200);
|
||||
expect(enableResponse.success).to.eql(true, "authorized user can't enable EEM");
|
||||
|
||||
let disableResponse = await disableEntityDiscovery(unauthorizedUser);
|
||||
expect(disableResponse.success).to.eql(false, 'unauthorized user can disable EEM');
|
||||
expect(disableResponse.reason).to.eql(ERROR_USER_NOT_AUTHORIZED);
|
||||
let disableResponse = await disableEntityDiscovery(unauthorizedUser, 403);
|
||||
|
||||
disableResponse = await disableEntityDiscovery(authorizedUser);
|
||||
disableResponse = await disableEntityDiscovery(authorizedUser, 200);
|
||||
expect(disableResponse.success).to.eql(true, "authorized user can't disable EEM");
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue