From ae4e7ccc517c75f55933333160145d11241d76a3 Mon Sep 17 00:00:00 2001 From: Walter Rafelsberger Date: Mon, 27 Sep 2021 15:35:27 +0200 Subject: [PATCH] [ML] Transforms: Align privileges checks with ML plugin. (#112970) To check the available node count, the ML plugin has an additional privileges check before returning the result. This PR uses the same approach for the corresponding transforms node endpoint. --- x-pack/plugins/transform/common/constants.ts | 3 ++ .../server/routes/api/transforms_nodes.ts | 19 +++++++++++++ .../apis/transform/transforms_nodes.ts | 28 ++++++++++++++++++- .../services/transform/security_common.ts | 7 +++++ 4 files changed, 56 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/transform/common/constants.ts b/x-pack/plugins/transform/common/constants.ts index 423b2d001381..84e43f1f632a 100644 --- a/x-pack/plugins/transform/common/constants.ts +++ b/x-pack/plugins/transform/common/constants.ts @@ -59,6 +59,9 @@ export const APP_CLUSTER_PRIVILEGES = [ 'cluster:admin/transform/stop', ]; +// Minimum privileges required to return transform node count +export const NODES_INFO_PRIVILEGES = ['cluster:monitor/transform/get']; + // Equivalent of capabilities.canGetTransform export const APP_GET_TRANSFORM_CLUSTER_PRIVILEGES = [ 'cluster.cluster:monitor/transform/get', diff --git a/x-pack/plugins/transform/server/routes/api/transforms_nodes.ts b/x-pack/plugins/transform/server/routes/api/transforms_nodes.ts index c9a0795c3221..29a3c50b2eea 100644 --- a/x-pack/plugins/transform/server/routes/api/transforms_nodes.ts +++ b/x-pack/plugins/transform/server/routes/api/transforms_nodes.ts @@ -5,6 +5,9 @@ * 2.0. */ +import Boom from '@hapi/boom'; + +import { NODES_INFO_PRIVILEGES } from '../../../common/constants'; import { isPopulatedObject } from '../../../common/shared_imports'; import { RouteDependencies } from '../../types'; @@ -44,6 +47,22 @@ export function registerTransformNodesRoutes({ router, license }: RouteDependenc }, license.guardApiRoute(async (ctx, req, res) => { try { + // If security is enabled, check that the user has at least permission to + // view transforms before calling the _nodes endpoint with the internal user. + if (license.getStatus().isSecurityEnabled === true) { + const { + body: { has_all_requested: hasAllPrivileges }, + } = await ctx.core.elasticsearch.client.asCurrentUser.security.hasPrivileges({ + body: { + cluster: NODES_INFO_PRIVILEGES, + }, + }); + + if (!hasAllPrivileges) { + return res.customError(wrapError(new Boom.Boom('Forbidden', { statusCode: 403 }))); + } + } + const { body: { nodes }, } = await ctx.core.elasticsearch.client.asInternalUser.nodes.info({ diff --git a/x-pack/test/api_integration/apis/transform/transforms_nodes.ts b/x-pack/test/api_integration/apis/transform/transforms_nodes.ts index ca9ab8e8a728..0fc93289195d 100644 --- a/x-pack/test/api_integration/apis/transform/transforms_nodes.ts +++ b/x-pack/test/api_integration/apis/transform/transforms_nodes.ts @@ -31,7 +31,7 @@ export default ({ getService }: FtrProviderContext) => { } describe('/api/transform/transforms/_nodes', function () { - it('should return the number of available transform nodes', async () => { + it('should return the number of available transform nodes for a power user', async () => { const { body } = await supertest .get('/api/transform/transforms/_nodes') .auth( @@ -44,5 +44,31 @@ export default ({ getService }: FtrProviderContext) => { assertTransformsNodesResponseBody(body); }); + + it('should return the number of available transform nodes for a viewer user', async () => { + const { body } = await supertest + .get('/api/transform/transforms/_nodes') + .auth( + USER.TRANSFORM_VIEWER, + transform.securityCommon.getPasswordForUser(USER.TRANSFORM_VIEWER) + ) + .set(COMMON_REQUEST_HEADERS) + .send() + .expect(200); + + assertTransformsNodesResponseBody(body); + }); + + it('should not return the number of available transform nodes for an unauthorized user', async () => { + await supertest + .get('/api/transform/transforms/_nodes') + .auth( + USER.TRANSFORM_UNAUTHORIZED, + transform.securityCommon.getPasswordForUser(USER.TRANSFORM_UNAUTHORIZED) + ) + .set(COMMON_REQUEST_HEADERS) + .send() + .expect(403); + }); }); }; diff --git a/x-pack/test/functional/services/transform/security_common.ts b/x-pack/test/functional/services/transform/security_common.ts index bae31dffa141..f27de80d26b2 100644 --- a/x-pack/test/functional/services/transform/security_common.ts +++ b/x-pack/test/functional/services/transform/security_common.ts @@ -14,6 +14,7 @@ export type TransformSecurityCommon = ProvidedType