[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.
This commit is contained in:
Walter Rafelsberger 2021-09-27 15:35:27 +02:00 committed by GitHub
parent 314227d259
commit ae4e7ccc51
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 56 additions and 1 deletions

View file

@ -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',

View file

@ -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<undefined, undefined, undefined>(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({

View file

@ -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);
});
});
};

View file

@ -14,6 +14,7 @@ export type TransformSecurityCommon = ProvidedType<typeof TransformSecurityCommo
export enum USER {
TRANSFORM_POWERUSER = 'transform_poweruser',
TRANSFORM_VIEWER = 'transform_viewer',
TRANSFORM_UNAUTHORIZED = 'transform_unauthorized',
}
export function TransformSecurityCommonProvider({ getService }: FtrProviderContext) {
@ -69,6 +70,12 @@ export function TransformSecurityCommonProvider({ getService }: FtrProviderConte
password: 'tfv001',
roles: ['kibana_admin', 'transform_user', 'transform_dest_readonly'],
},
{
name: 'transform_unauthorized',
full_name: 'Transform Unauthorized',
password: 'tfu001',
roles: ['kibana_admin'],
},
];
return {