From 6968f5247d2d33d6a0c7037aa4c1fca0e174ea17 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Wed, 23 Oct 2024 08:57:46 +0100 Subject: [PATCH] [ML] API tests for ML's _has_privileges (#197274) Calls ML's `_has_privileges` endpoint with various users. Also ensures upgrade mode status is correctly returned. (cherry picked from commit fc812e33d85691d76e657f8056b18d79a9f860b1) --- .../apis/ml/system/has_privileges.ts | 143 ++++++++++++++++++ .../api_integration/apis/ml/system/index.ts | 1 + x-pack/test/functional/services/ml/api.ts | 28 ++++ 3 files changed, 172 insertions(+) create mode 100644 x-pack/test/api_integration/apis/ml/system/has_privileges.ts diff --git a/x-pack/test/api_integration/apis/ml/system/has_privileges.ts b/x-pack/test/api_integration/apis/ml/system/has_privileges.ts new file mode 100644 index 0000000000000..2e705240b403e --- /dev/null +++ b/x-pack/test/api_integration/apis/ml/system/has_privileges.ts @@ -0,0 +1,143 @@ +/* + * 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 expect from '@kbn/expect'; + +import { MlHasPrivilegesResponse } from '@kbn/ml-plugin/public/application/services/ml_api_service'; +import { FtrProviderContext } from '../../../ftr_provider_context'; +import { getCommonRequestHeader } from '../../../../functional/services/ml/common_api'; +import { USER } from '../../../../functional/services/ml/security_common'; + +export default ({ getService }: FtrProviderContext) => { + const esArchiver = getService('esArchiver'); + const supertest = getService('supertestWithoutAuth'); + const ml = getService('ml'); + + async function runRequest( + user: USER, + index: any, + expectedStatusCode = 200 + ): Promise { + const { body, status } = await supertest + .post(`/internal/ml/_has_privileges`) + .auth(user, ml.securityCommon.getPasswordForUser(user)) + .set(getCommonRequestHeader('1')) + .send({ index }); + ml.api.assertResponseStatusCode(expectedStatusCode, status, body); + + return body; + } + + const testData = [ + { + user: USER.ML_POWERUSER, + index: [ + { + names: ['ft_farequote_small'], + privileges: ['read'], + }, + { + names: ['ft_farequote_small'], + privileges: ['write'], + }, + ], + expectedResponse: { + hasPrivileges: { + username: 'ft_ml_poweruser', + has_all_requested: false, + cluster: {}, + index: { + ft_farequote_small: { + read: true, + write: false, + }, + }, + application: {}, + }, + upgradeInProgress: false, + }, + expectedStatusCode: 200, + }, + { + user: USER.ML_VIEWER, + index: [ + { + names: ['ft_farequote_small'], + privileges: ['read'], + }, + { + names: ['ft_farequote_small'], + privileges: ['write'], + }, + ], + expectedResponse: { + hasPrivileges: { + username: 'ft_ml_viewer', + has_all_requested: false, + cluster: {}, + index: { + ft_farequote_small: { + read: true, + write: false, + }, + }, + application: {}, + }, + upgradeInProgress: false, + }, + + expectedStatusCode: 200, + }, + { + user: USER.ML_UNAUTHORIZED, + index: [ + { + names: ['ft_farequote_small'], + privileges: ['read'], + }, + { + names: ['ft_farequote_small'], + privileges: ['write'], + }, + ], + expectedResponse: { statusCode: 403, error: 'Forbidden', message: 'Forbidden' }, + expectedStatusCode: 403, + }, + ]; + + describe("ML's _has_privileges", () => { + before(async () => { + await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote_small'); + }); + after(async () => { + await ml.api.setUpgradeMode(false); + }); + + it('should return correct privileges for test data', async () => { + for (const { user, index, expectedResponse, expectedStatusCode } of testData) { + const response = await runRequest(user, index, expectedStatusCode); + expect(response).to.eql( + expectedResponse, + `expected ${JSON.stringify(expectedResponse)}, got ${JSON.stringify(response)}` + ); + } + }); + + it('should return correct upgrade in progress', async () => { + const index = testData[0].index; + const expectedResponse = { ...testData[0].expectedResponse, upgradeInProgress: true }; + await ml.api.setUpgradeMode(true); + await ml.api.assertUpgradeMode(true); + + const response = await runRequest(USER.ML_POWERUSER, index); + expect(response).to.eql( + expectedResponse, + `expected ${JSON.stringify(expectedResponse)}, got ${JSON.stringify(response)}` + ); + }); + }); +}; diff --git a/x-pack/test/api_integration/apis/ml/system/index.ts b/x-pack/test/api_integration/apis/ml/system/index.ts index 0ddd4b80a0ee5..42e02b5fbb808 100644 --- a/x-pack/test/api_integration/apis/ml/system/index.ts +++ b/x-pack/test/api_integration/apis/ml/system/index.ts @@ -14,5 +14,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./index_exists')); loadTestFile(require.resolve('./info')); loadTestFile(require.resolve('./node_count')); + loadTestFile(require.resolve('./has_privileges')); }); } diff --git a/x-pack/test/functional/services/ml/api.ts b/x-pack/test/functional/services/ml/api.ts index 8b2c46d474c0c..0a3d988fd750c 100644 --- a/x-pack/test/functional/services/ml/api.ts +++ b/x-pack/test/functional/services/ml/api.ts @@ -1696,5 +1696,33 @@ export function MachineLearningAPIProvider({ getService }: FtrProviderContext) { this.assertResponseStatusCode(200, status, module); return module; }, + + async setUpgradeMode(enabled: boolean) { + log.debug(`Setting upgrade mode to "${enabled}"`); + const { body, status } = await esSupertest.post(`/_ml/set_upgrade_mode?enabled=${enabled}`); + this.assertResponseStatusCode(200, status, body); + + log.debug(`Upgrade mode set to "${enabled}"`); + }, + + async assertUpgradeMode(expectedMode: boolean) { + log.debug(`Asserting upgrade mode is "${expectedMode}"`); + const { body, status } = await esSupertest.get('/_ml/info'); + this.assertResponseStatusCode(200, status, body); + + expect(body.upgrade_mode).to.eql( + expectedMode, + `Expected upgrade mode to be ${expectedMode}, got ${body.upgrade_mode}` + ); + }, + + async getMlInfo() { + log.debug(`Getting ML info`); + const { body, status } = await kbnSupertest + .get(`/internal/ml/info`) + .set(getCommonRequestHeader('1')); + this.assertResponseStatusCode(200, status, body); + return body; + }, }; }