From 4f28b25daf5fa73a413eb5b41acb20cee1afb2fd Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Wed, 26 Jun 2019 17:33:51 +0100 Subject: [PATCH] [ML] Moving privileges checks to server (#39585) * [ML] Moving privileges checks to server * removing unused include * adding tests * adding general kibana type * adding coments and extra test * fixing type issue --- .../common.ts => common/types/kibana.ts} | 3 +- .../plugins/ml/common/types/privileges.ts | 70 +++++ .../components/utils/utils.js | 2 +- .../ml/public/privilege/check_privilege.ts | 6 +- .../ml/public/privilege/get_privileges.ts | 240 +--------------- .../public/services/ml_api_service/index.d.ts | 4 +- .../public/services/ml_api_service/index.js | 9 +- ...{upgrade_service.js => upgrade_service.ts} | 7 +- .../__mocks__/call_with_request.ts | 170 +++++++++++ .../check_privileges/check_privileges.test.ts | 267 ++++++++++++++++++ .../lib/check_privileges/check_privileges.ts | 200 +++++++++++++ .../ml/server/lib/check_privileges/index.ts | 7 + .../server/lib/check_privileges/privileges.ts | 45 +++ .../ml/server/lib/check_privileges/upgrade.ts | 33 +++ .../plugins/ml/server/lib/security_utils.d.ts | 7 + .../legacy/plugins/ml/server/routes/system.js | 18 ++ 16 files changed, 842 insertions(+), 246 deletions(-) rename x-pack/legacy/plugins/ml/{public/privilege/common.ts => common/types/kibana.ts} (71%) create mode 100644 x-pack/legacy/plugins/ml/common/types/privileges.ts rename x-pack/legacy/plugins/ml/public/services/{upgrade_service.js => upgrade_service.ts} (68%) create mode 100644 x-pack/legacy/plugins/ml/server/lib/check_privileges/__mocks__/call_with_request.ts create mode 100644 x-pack/legacy/plugins/ml/server/lib/check_privileges/check_privileges.test.ts create mode 100644 x-pack/legacy/plugins/ml/server/lib/check_privileges/check_privileges.ts create mode 100644 x-pack/legacy/plugins/ml/server/lib/check_privileges/index.ts create mode 100644 x-pack/legacy/plugins/ml/server/lib/check_privileges/privileges.ts create mode 100644 x-pack/legacy/plugins/ml/server/lib/check_privileges/upgrade.ts create mode 100644 x-pack/legacy/plugins/ml/server/lib/security_utils.d.ts diff --git a/x-pack/legacy/plugins/ml/public/privilege/common.ts b/x-pack/legacy/plugins/ml/common/types/kibana.ts similarity index 71% rename from x-pack/legacy/plugins/ml/public/privilege/common.ts rename to x-pack/legacy/plugins/ml/common/types/kibana.ts index 06c3697c332b..88e5f68fb4a3 100644 --- a/x-pack/legacy/plugins/ml/public/privilege/common.ts +++ b/x-pack/legacy/plugins/ml/common/types/kibana.ts @@ -4,5 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export type Cluster = Record; -export type Privileges = Record; +export type callWithRequestType = (action: string, params?: any) => Promise; diff --git a/x-pack/legacy/plugins/ml/common/types/privileges.ts b/x-pack/legacy/plugins/ml/common/types/privileges.ts new file mode 100644 index 000000000000..0f44adf3deba --- /dev/null +++ b/x-pack/legacy/plugins/ml/common/types/privileges.ts @@ -0,0 +1,70 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +// + +export interface Privileges { + // Anomaly Detection + canGetJobs: boolean; + canCreateJob: boolean; + canDeleteJob: boolean; + canOpenJob: boolean; + canCloseJob: boolean; + canForecastJob: boolean; + canGetDatafeeds: boolean; + canStartStopDatafeed: boolean; + canUpdateJob: boolean; + canUpdateDatafeed: boolean; + canPreviewDatafeed: boolean; + // Calendars + canGetCalendars: boolean; + canCreateCalendar: boolean; + canDeleteCalendar: boolean; + // Filters + canGetFilters: boolean; + canCreateFilter: boolean; + canDeleteFilter: boolean; + // File Data Visualizer + canFindFileStructure: boolean; + // Data Frames + canGetDataFrameJobs: boolean; + canDeleteDataFrameJob: boolean; + canPreviewDataFrameJob: boolean; + canCreateDataFrameJob: boolean; + canStartStopDataFrameJob: boolean; +} + +export function getDefaultPrivileges(): Privileges { + return { + // Anomaly Detection + canGetJobs: false, + canCreateJob: false, + canDeleteJob: false, + canOpenJob: false, + canCloseJob: false, + canForecastJob: false, + canGetDatafeeds: false, + canStartStopDatafeed: false, + canUpdateJob: false, + canUpdateDatafeed: false, + canPreviewDatafeed: false, + // Calendars + canGetCalendars: false, + canCreateCalendar: false, + canDeleteCalendar: false, + // Filters + canGetFilters: false, + canCreateFilter: false, + canDeleteFilter: false, + // File Data Visualizer + canFindFileStructure: false, + // Data Frames + canGetDataFrameJobs: false, + canDeleteDataFrameJob: false, + canPreviewDataFrameJob: false, + canCreateDataFrameJob: false, + canStartStopDataFrameJob: false, + }; +} diff --git a/x-pack/legacy/plugins/ml/public/file_datavisualizer/components/utils/utils.js b/x-pack/legacy/plugins/ml/public/file_datavisualizer/components/utils/utils.js index 701b0cc1e89b..06390a6141c2 100644 --- a/x-pack/legacy/plugins/ml/public/file_datavisualizer/components/utils/utils.js +++ b/x-pack/legacy/plugins/ml/public/file_datavisualizer/components/utils/utils.js @@ -139,6 +139,6 @@ export async function hasImportPermission(indexName) { ]; } - const resp = await ml.checkPrivilege(priv); + const resp = await ml.hasPrivileges(priv); return (resp.securityDisabled === true || resp.has_all_requested === true); } diff --git a/x-pack/legacy/plugins/ml/public/privilege/check_privilege.ts b/x-pack/legacy/plugins/ml/public/privilege/check_privilege.ts index 00a0d8dbeff1..fe8e72a6404e 100644 --- a/x-pack/legacy/plugins/ml/public/privilege/check_privilege.ts +++ b/x-pack/legacy/plugins/ml/public/privilege/check_privilege.ts @@ -9,10 +9,10 @@ import { i18n } from '@kbn/i18n'; // @ts-ignore import { hasLicenseExpired } from '../license/check_license'; -import { Privileges } from './common'; +import { Privileges, getDefaultPrivileges } from '../../common/types/privileges'; import { getPrivileges } from './get_privileges'; -let privileges: Privileges = {}; +let privileges: Privileges = getDefaultPrivileges(); export function checkGetJobsPrivilege(kbnUrl: any): Promise { return new Promise((resolve, reject) => { @@ -100,7 +100,7 @@ export function checkCreateDataFrameJobsPrivilege(kbnUrl: any): Promise { - const privileges: Privileges = { - // Anomaly Detection - canGetJobs: false, - canCreateJob: false, - canDeleteJob: false, - canOpenJob: false, - canCloseJob: false, - canForecastJob: false, - canGetDatafeeds: false, - canStartStopDatafeed: false, - canUpdateJob: false, - canUpdateDatafeed: false, - canPreviewDatafeed: false, - // Calendars - canGetCalendars: false, - canCreateCalendar: false, - canDeleteCalendar: false, - // Filters - canGetFilters: false, - canCreateFilter: false, - canDeleteFilter: false, - // File Data Visualizer - canFindFileStructure: false, - // Data Frames - canGetDataFrameJobs: false, - canDeleteDataFrameJob: false, - canPreviewDataFrameJob: false, - canCreateDataFrameJob: false, - canStartStopDataFrameJob: false, - }; - return new Promise((resolve, reject) => { - const priv = { - cluster: [ - 'cluster:monitor/data_frame/get', - 'cluster:monitor/data_frame/stats/get', - 'cluster:monitor/xpack/ml/job/get', - 'cluster:monitor/xpack/ml/job/stats/get', - 'cluster:monitor/xpack/ml/datafeeds/get', - 'cluster:monitor/xpack/ml/datafeeds/stats/get', - 'cluster:monitor/xpack/ml/calendars/get', - 'cluster:admin/data_frame/delete', - 'cluster:admin/data_frame/preview', - 'cluster:admin/data_frame/put', - 'cluster:admin/data_frame/start', - 'cluster:admin/data_frame/start_task', - 'cluster:admin/data_frame/stop', - 'cluster:admin/xpack/ml/job/put', - 'cluster:admin/xpack/ml/job/delete', - 'cluster:admin/xpack/ml/job/update', - 'cluster:admin/xpack/ml/job/open', - 'cluster:admin/xpack/ml/job/close', - 'cluster:admin/xpack/ml/job/forecast', - 'cluster:admin/xpack/ml/datafeeds/put', - 'cluster:admin/xpack/ml/datafeeds/delete', - 'cluster:admin/xpack/ml/datafeeds/start', - 'cluster:admin/xpack/ml/datafeeds/stop', - 'cluster:admin/xpack/ml/datafeeds/update', - 'cluster:admin/xpack/ml/datafeeds/preview', - 'cluster:admin/xpack/ml/calendars/put', - 'cluster:admin/xpack/ml/calendars/delete', - 'cluster:admin/xpack/ml/calendars/jobs/update', - 'cluster:admin/xpack/ml/calendars/events/post', - 'cluster:admin/xpack/ml/calendars/events/delete', - 'cluster:admin/xpack/ml/filters/put', - 'cluster:admin/xpack/ml/filters/get', - 'cluster:admin/xpack/ml/filters/update', - 'cluster:admin/xpack/ml/filters/delete', - 'cluster:monitor/xpack/ml/findfilestructure', - ], - }; - - ml.checkPrivilege(priv) - .then(resp => { - if (resp.upgradeInProgress === true) { + ml.checkMlPrivileges() + .then(({ privileges, upgradeInProgress }) => { + if (upgradeInProgress === true) { setUpgradeInProgress(true); - // only check for getting endpoints - // force all to be true if security is disabled - setGettingPrivileges(resp.cluster, privileges, resp.securityDisabled === true); - } else if (resp.securityDisabled) { - // if security has been disabled, securityDisabled is returned from the endpoint - // therefore set all privileges to true - Object.keys(privileges).forEach(k => (privileges[k] = true)); - } else { - setGettingPrivileges(resp.cluster, privileges); - setActionPrivileges(resp.cluster, privileges); } - resolve(privileges); }) .catch(() => { - reject(privileges); + reject(getDefaultPrivileges()); }); }); } - -function setGettingPrivileges( - cluster: Cluster = {}, - privileges: Privileges = {}, - forceTrue = false -) { - // Anomaly Detection - if ( - forceTrue || - (cluster['cluster:monitor/xpack/ml/job/get'] && - cluster['cluster:monitor/xpack/ml/job/stats/get']) - ) { - privileges.canGetJobs = true; - } - - if ( - forceTrue || - (cluster['cluster:monitor/xpack/ml/datafeeds/get'] && - cluster['cluster:monitor/xpack/ml/datafeeds/stats/get']) - ) { - privileges.canGetDatafeeds = true; - } - - // Calendars - if (forceTrue || cluster['cluster:monitor/xpack/ml/calendars/get']) { - privileges.canGetCalendars = true; - } - - // Filters - if (forceTrue || cluster['cluster:admin/xpack/ml/filters/get']) { - privileges.canGetFilters = true; - } - - // File Data Visualizer - if (forceTrue || cluster['cluster:monitor/xpack/ml/findfilestructure']) { - privileges.canFindFileStructure = true; - } - - // Data Frames - if ( - forceTrue || - (cluster['cluster:monitor/data_frame/get'] && cluster['cluster:monitor/data_frame/stats/get']) - ) { - privileges.canGetDataFrameJobs = true; - } -} - -function setActionPrivileges(cluster: Cluster = {}, privileges: Privileges = {}) { - // Anomaly Detection - if ( - cluster['cluster:admin/xpack/ml/job/put'] && - cluster['cluster:admin/xpack/ml/job/open'] && - cluster['cluster:admin/xpack/ml/datafeeds/put'] - ) { - privileges.canCreateJob = true; - } - - if (cluster['cluster:admin/xpack/ml/job/update']) { - privileges.canUpdateJob = true; - } - - if (cluster['cluster:admin/xpack/ml/job/open']) { - privileges.canOpenJob = true; - } - - if (cluster['cluster:admin/xpack/ml/job/close']) { - privileges.canCloseJob = true; - } - - if (cluster['cluster:admin/xpack/ml/job/forecast']) { - privileges.canForecastJob = true; - } - - if ( - cluster['cluster:admin/xpack/ml/job/delete'] && - cluster['cluster:admin/xpack/ml/datafeeds/delete'] - ) { - privileges.canDeleteJob = true; - } - - if ( - cluster['cluster:admin/xpack/ml/job/open'] && - cluster['cluster:admin/xpack/ml/datafeeds/start'] && - cluster['cluster:admin/xpack/ml/datafeeds/stop'] - ) { - privileges.canStartStopDatafeed = true; - } - - if (cluster['cluster:admin/xpack/ml/datafeeds/update']) { - privileges.canUpdateDatafeed = true; - } - - if (cluster['cluster:admin/xpack/ml/datafeeds/preview']) { - privileges.canPreviewDatafeed = true; - } - - // Calendars - if ( - cluster['cluster:admin/xpack/ml/calendars/put'] && - cluster['cluster:admin/xpack/ml/calendars/jobs/update'] && - cluster['cluster:admin/xpack/ml/calendars/events/post'] - ) { - privileges.canCreateCalendar = true; - } - - if ( - cluster['cluster:admin/xpack/ml/calendars/delete'] && - cluster['cluster:admin/xpack/ml/calendars/events/delete'] - ) { - privileges.canDeleteCalendar = true; - } - - // Filters - if ( - cluster['cluster:admin/xpack/ml/filters/put'] && - cluster['cluster:admin/xpack/ml/filters/update'] - ) { - privileges.canCreateFilter = true; - } - - if (cluster['cluster:admin/xpack/ml/filters/delete']) { - privileges.canDeleteFilter = true; - } - - // Data Frames - if (cluster['cluster:admin/data_frame/put']) { - privileges.canCreateDataFrameJob = true; - } - - if (cluster['cluster:admin/data_frame/delete']) { - privileges.canDeleteDataFrameJob = true; - } - - if (cluster['cluster:admin/data_frame/preview']) { - privileges.canPreviewDataFrameJob = true; - } - - if ( - cluster['cluster:admin/data_frame/start'] && - cluster['cluster:admin/data_frame/start_task'] && - cluster['cluster:admin/data_frame/stop'] - ) { - privileges.canStartStopDataFrameJob = true; - } -} diff --git a/x-pack/legacy/plugins/ml/public/services/ml_api_service/index.d.ts b/x-pack/legacy/plugins/ml/public/services/ml_api_service/index.d.ts index 45185481c878..f1a51b23002c 100644 --- a/x-pack/legacy/plugins/ml/public/services/ml_api_service/index.d.ts +++ b/x-pack/legacy/plugins/ml/public/services/ml_api_service/index.d.ts @@ -5,6 +5,7 @@ */ import { Annotation } from '../../../common/types/annotations'; +import { Privileges } from '../../../common/types/privileges'; // TODO This is not a complete representation of all methods of `ml.*`. // It just satisfies needs for other parts of the code area which use @@ -30,7 +31,8 @@ declare interface Ml { stopDataFrameTransformsJob(jobId: string): Promise; }; - checkPrivilege(obj: object): Promise; + hasPrivileges(obj: object): Promise; + checkMlPrivileges(): Promise<{ privileges: Privileges; upgradeInProgress: boolean }>; esSearch: any; getIndices(): Promise; diff --git a/x-pack/legacy/plugins/ml/public/services/ml_api_service/index.js b/x-pack/legacy/plugins/ml/public/services/ml_api_service/index.js index b93d2b585a6d..960b8d4208ed 100644 --- a/x-pack/legacy/plugins/ml/public/services/ml_api_service/index.js +++ b/x-pack/legacy/plugins/ml/public/services/ml_api_service/index.js @@ -211,7 +211,7 @@ export const ml = { }); }, - checkPrivilege(obj) { + hasPrivileges(obj) { return http({ url: `${basePath}/_has_privileges`, method: 'POST', @@ -219,6 +219,13 @@ export const ml = { }); }, + checkMlPrivileges() { + return http({ + url: `${basePath}/ml_privileges`, + method: 'GET', + }); + }, + getNotificationSettings() { return http({ url: `${basePath}/notification_settings`, diff --git a/x-pack/legacy/plugins/ml/public/services/upgrade_service.js b/x-pack/legacy/plugins/ml/public/services/upgrade_service.ts similarity index 68% rename from x-pack/legacy/plugins/ml/public/services/upgrade_service.js rename to x-pack/legacy/plugins/ml/public/services/upgrade_service.ts index 9087ab854ff7..23b607dd3704 100644 --- a/x-pack/legacy/plugins/ml/public/services/upgrade_service.js +++ b/x-pack/legacy/plugins/ml/public/services/upgrade_service.ts @@ -4,13 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ +let upgradeInProgress: boolean = false; -let upgradeInProgress = false; - -export function setUpgradeInProgress(show) { +export function setUpgradeInProgress(show: boolean) { upgradeInProgress = show; } -export function isUpgradeInProgress() { +export function isUpgradeInProgress(): boolean { return upgradeInProgress; } diff --git a/x-pack/legacy/plugins/ml/server/lib/check_privileges/__mocks__/call_with_request.ts b/x-pack/legacy/plugins/ml/server/lib/check_privileges/__mocks__/call_with_request.ts new file mode 100644 index 000000000000..b5650a23b8ca --- /dev/null +++ b/x-pack/legacy/plugins/ml/server/lib/check_privileges/__mocks__/call_with_request.ts @@ -0,0 +1,170 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export function callWithRequestProvider(testType: string) { + switch (testType) { + case 'partialPrivileges': + return partialPrivileges; + case 'fullPrivileges': + return fullPrivileges; + case 'upgradeWithFullPrivileges': + return upgradeWithFullPrivileges; + case 'upgradeWithPartialPrivileges': + return upgradeWithPartialPrivileges; + + default: + return fullPrivileges; + } +} + +const fullPrivileges = async (action: string, params?: any) => { + switch (action) { + case 'ml.privilegeCheck': + return { + username: 'test2', + has_all_requested: false, + cluster: fullClusterPrivileges, + index: {}, + application: {}, + }; + case 'ml.info': + return { upgrade_mode: false }; + + default: + break; + } +}; + +const partialPrivileges = async (action: string, params?: any) => { + switch (action) { + case 'ml.privilegeCheck': + return { + username: 'test2', + has_all_requested: false, + cluster: partialClusterPrivileges, + index: {}, + application: {}, + }; + case 'ml.info': + return { upgrade_mode: false }; + + default: + break; + } +}; + +const upgradeWithFullPrivileges = async (action: string, params?: any) => { + switch (action) { + case 'ml.privilegeCheck': + return { + username: 'elastic', + has_all_requested: true, + cluster: fullClusterPrivileges, + index: {}, + application: {}, + }; + case 'ml.info': + return { upgrade_mode: true }; + + default: + break; + } +}; + +const upgradeWithPartialPrivileges = async (action: string, params?: any) => { + switch (action) { + case 'ml.privilegeCheck': + return { + username: 'test2', + has_all_requested: false, + cluster: partialClusterPrivileges, + index: {}, + application: {}, + }; + case 'ml.info': + return { upgrade_mode: true }; + + default: + break; + } +}; + +const fullClusterPrivileges = { + 'cluster:admin/xpack/ml/datafeeds/delete': true, + 'cluster:admin/xpack/ml/datafeeds/update': true, + 'cluster:admin/xpack/ml/job/forecast': true, + 'cluster:monitor/xpack/ml/job/stats/get': true, + 'cluster:admin/xpack/ml/filters/delete': true, + 'cluster:admin/xpack/ml/datafeeds/preview': true, + 'cluster:admin/xpack/ml/datafeeds/start': true, + 'cluster:admin/xpack/ml/filters/put': true, + 'cluster:admin/xpack/ml/datafeeds/stop': true, + 'cluster:monitor/xpack/ml/calendars/get': true, + 'cluster:admin/xpack/ml/filters/get': true, + 'cluster:monitor/xpack/ml/datafeeds/get': true, + 'cluster:admin/data_frame/start_task': true, + 'cluster:admin/xpack/ml/filters/update': true, + 'cluster:admin/xpack/ml/calendars/events/post': true, + 'cluster:monitor/data_frame/get': true, + 'cluster:admin/xpack/ml/job/close': true, + 'cluster:monitor/xpack/ml/datafeeds/stats/get': true, + 'cluster:admin/data_frame/preview': true, + 'cluster:admin/xpack/ml/calendars/jobs/update': true, + 'cluster:admin/data_frame/put': true, + 'cluster:admin/xpack/ml/calendars/put': true, + 'cluster:monitor/data_frame/stats/get': true, + 'cluster:admin/data_frame/stop': true, + 'cluster:admin/xpack/ml/calendars/events/delete': true, + 'cluster:admin/xpack/ml/datafeeds/put': true, + 'cluster:admin/xpack/ml/job/open': true, + 'cluster:admin/xpack/ml/job/delete': true, + 'cluster:monitor/xpack/ml/job/get': true, + 'cluster:admin/data_frame/delete': true, + 'cluster:admin/xpack/ml/job/put': true, + 'cluster:admin/xpack/ml/job/update': true, + 'cluster:admin/data_frame/start': true, + 'cluster:admin/xpack/ml/calendars/delete': true, + 'cluster:monitor/xpack/ml/findfilestructure': true, +}; + +// the same as ml_user role +const partialClusterPrivileges = { + 'cluster:admin/xpack/ml/datafeeds/delete': false, + 'cluster:admin/xpack/ml/datafeeds/update': false, + 'cluster:admin/xpack/ml/job/forecast': false, + 'cluster:monitor/xpack/ml/job/stats/get': true, + 'cluster:admin/xpack/ml/filters/delete': false, + 'cluster:admin/xpack/ml/datafeeds/preview': false, + 'cluster:admin/xpack/ml/datafeeds/start': false, + 'cluster:admin/xpack/ml/filters/put': false, + 'cluster:admin/xpack/ml/datafeeds/stop': false, + 'cluster:monitor/xpack/ml/calendars/get': true, + 'cluster:admin/xpack/ml/filters/get': false, + 'cluster:monitor/xpack/ml/datafeeds/get': true, + 'cluster:admin/data_frame/start_task': false, + 'cluster:admin/xpack/ml/filters/update': false, + 'cluster:admin/xpack/ml/calendars/events/post': false, + 'cluster:monitor/data_frame/get': false, + 'cluster:admin/xpack/ml/job/close': false, + 'cluster:monitor/xpack/ml/datafeeds/stats/get': true, + 'cluster:admin/data_frame/preview': false, + 'cluster:admin/xpack/ml/calendars/jobs/update': false, + 'cluster:admin/data_frame/put': false, + 'cluster:admin/xpack/ml/calendars/put': false, + 'cluster:monitor/data_frame/stats/get': false, + 'cluster:admin/data_frame/stop': false, + 'cluster:admin/xpack/ml/calendars/events/delete': false, + 'cluster:admin/xpack/ml/datafeeds/put': false, + 'cluster:admin/xpack/ml/job/open': false, + 'cluster:admin/xpack/ml/job/delete': false, + 'cluster:monitor/xpack/ml/job/get': true, + 'cluster:admin/data_frame/delete': false, + 'cluster:admin/xpack/ml/job/put': false, + 'cluster:admin/xpack/ml/job/update': false, + 'cluster:admin/data_frame/start': false, + 'cluster:admin/xpack/ml/calendars/delete': false, + 'cluster:monitor/xpack/ml/findfilestructure': true, +}; diff --git a/x-pack/legacy/plugins/ml/server/lib/check_privileges/check_privileges.test.ts b/x-pack/legacy/plugins/ml/server/lib/check_privileges/check_privileges.test.ts new file mode 100644 index 000000000000..979ff21cdd94 --- /dev/null +++ b/x-pack/legacy/plugins/ml/server/lib/check_privileges/check_privileges.test.ts @@ -0,0 +1,267 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { callWithRequestProvider } from './__mocks__/call_with_request'; +import { privilegesProvider } from './check_privileges'; +import { mlPrivileges } from './privileges'; + +const xpackMainPluginWithSecurity = { + info: { + isAvailable: () => true, + feature: () => ({ + isEnabled: () => true, + }), + }, +}; + +const xpackMainPluginWithOutSecurity = { + info: { + isAvailable: () => true, + feature: () => ({ + isEnabled: () => false, + }), + }, +}; + +describe('check_privileges', () => { + describe('getPrivileges() - right number of privileges', () => { + test('es privileges count', async done => { + const count = mlPrivileges.cluster.length; + expect(count).toBe(35); + done(); + }); + + test('kibana privileges count', async done => { + const callWithRequest = callWithRequestProvider('partialPrivileges'); + const { getPrivileges } = privilegesProvider(callWithRequest, xpackMainPluginWithSecurity); + const { privileges } = await getPrivileges(); + const count = Object.keys(privileges).length; + expect(count).toBe(23); + done(); + }); + }); + + describe('getPrivileges() with security', () => { + test('ml_user privileges only', async done => { + const callWithRequest = callWithRequestProvider('partialPrivileges'); + const { getPrivileges } = privilegesProvider(callWithRequest, xpackMainPluginWithSecurity); + const { privileges, upgradeInProgress } = await getPrivileges(); + expect(upgradeInProgress).toBe(false); + expect(privileges.canGetJobs).toBe(true); + expect(privileges.canCreateJob).toBe(false); + expect(privileges.canDeleteJob).toBe(false); + expect(privileges.canOpenJob).toBe(false); + expect(privileges.canCloseJob).toBe(false); + expect(privileges.canForecastJob).toBe(false); + expect(privileges.canGetDatafeeds).toBe(true); + expect(privileges.canStartStopDatafeed).toBe(false); + expect(privileges.canUpdateJob).toBe(false); + expect(privileges.canUpdateDatafeed).toBe(false); + expect(privileges.canPreviewDatafeed).toBe(false); + expect(privileges.canGetCalendars).toBe(true); + expect(privileges.canCreateCalendar).toBe(false); + expect(privileges.canDeleteCalendar).toBe(false); + expect(privileges.canGetFilters).toBe(false); + expect(privileges.canCreateFilter).toBe(false); + expect(privileges.canDeleteFilter).toBe(false); + expect(privileges.canFindFileStructure).toBe(true); + expect(privileges.canGetDataFrameJobs).toBe(false); + expect(privileges.canDeleteDataFrameJob).toBe(false); + expect(privileges.canPreviewDataFrameJob).toBe(false); + expect(privileges.canCreateDataFrameJob).toBe(false); + expect(privileges.canStartStopDataFrameJob).toBe(false); + done(); + }); + + test('full privileges', async done => { + const callWithRequest = callWithRequestProvider('fullPrivileges'); + const { getPrivileges } = privilegesProvider(callWithRequest, xpackMainPluginWithSecurity); + const { privileges, upgradeInProgress } = await getPrivileges(); + expect(upgradeInProgress).toBe(false); + expect(privileges.canGetJobs).toBe(true); + expect(privileges.canCreateJob).toBe(true); + expect(privileges.canDeleteJob).toBe(true); + expect(privileges.canOpenJob).toBe(true); + expect(privileges.canCloseJob).toBe(true); + expect(privileges.canForecastJob).toBe(true); + expect(privileges.canGetDatafeeds).toBe(true); + expect(privileges.canStartStopDatafeed).toBe(true); + expect(privileges.canUpdateJob).toBe(true); + expect(privileges.canUpdateDatafeed).toBe(true); + expect(privileges.canPreviewDatafeed).toBe(true); + expect(privileges.canGetCalendars).toBe(true); + expect(privileges.canCreateCalendar).toBe(true); + expect(privileges.canDeleteCalendar).toBe(true); + expect(privileges.canGetFilters).toBe(true); + expect(privileges.canCreateFilter).toBe(true); + expect(privileges.canDeleteFilter).toBe(true); + expect(privileges.canFindFileStructure).toBe(true); + expect(privileges.canGetDataFrameJobs).toBe(true); + expect(privileges.canDeleteDataFrameJob).toBe(true); + expect(privileges.canPreviewDataFrameJob).toBe(true); + expect(privileges.canCreateDataFrameJob).toBe(true); + expect(privileges.canStartStopDataFrameJob).toBe(true); + done(); + }); + + test('upgrade in progress with full privileges', async done => { + const callWithRequest = callWithRequestProvider('upgradeWithFullPrivileges'); + const { getPrivileges } = privilegesProvider(callWithRequest, xpackMainPluginWithSecurity); + const { privileges, upgradeInProgress } = await getPrivileges(); + expect(upgradeInProgress).toBe(true); + expect(privileges.canGetJobs).toBe(true); + expect(privileges.canCreateJob).toBe(false); + expect(privileges.canDeleteJob).toBe(false); + expect(privileges.canOpenJob).toBe(false); + expect(privileges.canCloseJob).toBe(false); + expect(privileges.canForecastJob).toBe(false); + expect(privileges.canGetDatafeeds).toBe(true); + expect(privileges.canStartStopDatafeed).toBe(false); + expect(privileges.canUpdateJob).toBe(false); + expect(privileges.canUpdateDatafeed).toBe(false); + expect(privileges.canPreviewDatafeed).toBe(false); + expect(privileges.canGetCalendars).toBe(true); + expect(privileges.canCreateCalendar).toBe(false); + expect(privileges.canDeleteCalendar).toBe(false); + expect(privileges.canGetFilters).toBe(true); + expect(privileges.canCreateFilter).toBe(false); + expect(privileges.canDeleteFilter).toBe(false); + expect(privileges.canFindFileStructure).toBe(true); + expect(privileges.canGetDataFrameJobs).toBe(true); + expect(privileges.canDeleteDataFrameJob).toBe(false); + expect(privileges.canPreviewDataFrameJob).toBe(false); + expect(privileges.canCreateDataFrameJob).toBe(false); + expect(privileges.canStartStopDataFrameJob).toBe(false); + done(); + }); + + test('upgrade in progress with partial privileges', async done => { + const callWithRequest = callWithRequestProvider('upgradeWithPartialPrivileges'); + const { getPrivileges } = privilegesProvider(callWithRequest, xpackMainPluginWithSecurity); + const { privileges, upgradeInProgress } = await getPrivileges(); + expect(upgradeInProgress).toBe(true); + expect(privileges.canGetJobs).toBe(true); + expect(privileges.canCreateJob).toBe(false); + expect(privileges.canDeleteJob).toBe(false); + expect(privileges.canOpenJob).toBe(false); + expect(privileges.canCloseJob).toBe(false); + expect(privileges.canForecastJob).toBe(false); + expect(privileges.canGetDatafeeds).toBe(true); + expect(privileges.canStartStopDatafeed).toBe(false); + expect(privileges.canUpdateJob).toBe(false); + expect(privileges.canUpdateDatafeed).toBe(false); + expect(privileges.canPreviewDatafeed).toBe(false); + expect(privileges.canGetCalendars).toBe(true); + expect(privileges.canCreateCalendar).toBe(false); + expect(privileges.canDeleteCalendar).toBe(false); + expect(privileges.canGetFilters).toBe(false); + expect(privileges.canCreateFilter).toBe(false); + expect(privileges.canDeleteFilter).toBe(false); + expect(privileges.canFindFileStructure).toBe(true); + expect(privileges.canGetDataFrameJobs).toBe(false); + expect(privileges.canDeleteDataFrameJob).toBe(false); + expect(privileges.canPreviewDataFrameJob).toBe(false); + expect(privileges.canCreateDataFrameJob).toBe(false); + expect(privileges.canStartStopDataFrameJob).toBe(false); + done(); + }); + }); + + describe('getPrivileges() without security', () => { + test('ml_user privileges only', async done => { + const callWithRequest = callWithRequestProvider('partialPrivileges'); + const { getPrivileges } = privilegesProvider(callWithRequest, xpackMainPluginWithOutSecurity); + const { privileges, upgradeInProgress } = await getPrivileges(); + expect(upgradeInProgress).toBe(false); + expect(privileges.canGetJobs).toBe(true); + expect(privileges.canCreateJob).toBe(true); + expect(privileges.canDeleteJob).toBe(true); + expect(privileges.canOpenJob).toBe(true); + expect(privileges.canCloseJob).toBe(true); + expect(privileges.canForecastJob).toBe(true); + expect(privileges.canGetDatafeeds).toBe(true); + expect(privileges.canStartStopDatafeed).toBe(true); + expect(privileges.canUpdateJob).toBe(true); + expect(privileges.canUpdateDatafeed).toBe(true); + expect(privileges.canPreviewDatafeed).toBe(true); + expect(privileges.canGetCalendars).toBe(true); + expect(privileges.canCreateCalendar).toBe(true); + expect(privileges.canDeleteCalendar).toBe(true); + expect(privileges.canGetFilters).toBe(true); + expect(privileges.canCreateFilter).toBe(true); + expect(privileges.canDeleteFilter).toBe(true); + expect(privileges.canFindFileStructure).toBe(true); + expect(privileges.canGetDataFrameJobs).toBe(true); + expect(privileges.canDeleteDataFrameJob).toBe(true); + expect(privileges.canPreviewDataFrameJob).toBe(true); + expect(privileges.canCreateDataFrameJob).toBe(true); + expect(privileges.canStartStopDataFrameJob).toBe(true); + done(); + }); + + test('upgrade in progress with full privileges', async done => { + const callWithRequest = callWithRequestProvider('upgradeWithFullPrivileges'); + const { getPrivileges } = privilegesProvider(callWithRequest, xpackMainPluginWithOutSecurity); + const { privileges, upgradeInProgress } = await getPrivileges(); + expect(upgradeInProgress).toBe(true); + expect(privileges.canGetJobs).toBe(true); + expect(privileges.canCreateJob).toBe(false); + expect(privileges.canDeleteJob).toBe(false); + expect(privileges.canOpenJob).toBe(false); + expect(privileges.canCloseJob).toBe(false); + expect(privileges.canForecastJob).toBe(false); + expect(privileges.canGetDatafeeds).toBe(true); + expect(privileges.canStartStopDatafeed).toBe(false); + expect(privileges.canUpdateJob).toBe(false); + expect(privileges.canUpdateDatafeed).toBe(false); + expect(privileges.canPreviewDatafeed).toBe(false); + expect(privileges.canGetCalendars).toBe(true); + expect(privileges.canCreateCalendar).toBe(false); + expect(privileges.canDeleteCalendar).toBe(false); + expect(privileges.canGetFilters).toBe(true); + expect(privileges.canCreateFilter).toBe(false); + expect(privileges.canDeleteFilter).toBe(false); + expect(privileges.canFindFileStructure).toBe(true); + expect(privileges.canGetDataFrameJobs).toBe(true); + expect(privileges.canDeleteDataFrameJob).toBe(false); + expect(privileges.canPreviewDataFrameJob).toBe(false); + expect(privileges.canCreateDataFrameJob).toBe(false); + expect(privileges.canStartStopDataFrameJob).toBe(false); + done(); + }); + + test('upgrade in progress with partial privileges', async done => { + const callWithRequest = callWithRequestProvider('upgradeWithPartialPrivileges'); + const { getPrivileges } = privilegesProvider(callWithRequest, xpackMainPluginWithOutSecurity); + const { privileges, upgradeInProgress } = await getPrivileges(); + expect(upgradeInProgress).toBe(true); + expect(privileges.canGetJobs).toBe(true); + expect(privileges.canCreateJob).toBe(false); + expect(privileges.canDeleteJob).toBe(false); + expect(privileges.canOpenJob).toBe(false); + expect(privileges.canCloseJob).toBe(false); + expect(privileges.canForecastJob).toBe(false); + expect(privileges.canGetDatafeeds).toBe(true); + expect(privileges.canStartStopDatafeed).toBe(false); + expect(privileges.canUpdateJob).toBe(false); + expect(privileges.canUpdateDatafeed).toBe(false); + expect(privileges.canPreviewDatafeed).toBe(false); + expect(privileges.canGetCalendars).toBe(true); + expect(privileges.canCreateCalendar).toBe(false); + expect(privileges.canDeleteCalendar).toBe(false); + expect(privileges.canGetFilters).toBe(true); + expect(privileges.canCreateFilter).toBe(false); + expect(privileges.canDeleteFilter).toBe(false); + expect(privileges.canFindFileStructure).toBe(true); + expect(privileges.canGetDataFrameJobs).toBe(true); + expect(privileges.canDeleteDataFrameJob).toBe(false); + expect(privileges.canPreviewDataFrameJob).toBe(false); + expect(privileges.canCreateDataFrameJob).toBe(false); + expect(privileges.canStartStopDataFrameJob).toBe(false); + done(); + }); + }); +}); diff --git a/x-pack/legacy/plugins/ml/server/lib/check_privileges/check_privileges.ts b/x-pack/legacy/plugins/ml/server/lib/check_privileges/check_privileges.ts new file mode 100644 index 000000000000..7357c3aae7ac --- /dev/null +++ b/x-pack/legacy/plugins/ml/server/lib/check_privileges/check_privileges.ts @@ -0,0 +1,200 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Privileges, getDefaultPrivileges } from '../../../common/types/privileges'; +import { callWithRequestType } from '../../../common/types/kibana'; +import { isSecurityDisabled } from '../../lib/security_utils'; +import { upgradeCheckProvider } from './upgrade'; + +import { mlPrivileges } from './privileges'; + +type ClusterPrivilege = Record; + +interface Response { + privileges: Privileges; + upgradeInProgress: boolean; +} + +export function privilegesProvider(callWithRequest: callWithRequestType, xpackMainPlugin: any) { + const { isUpgradeInProgress } = upgradeCheckProvider(callWithRequest); + async function getPrivileges(): Promise { + // get the default privileges, forced to be false. + const privileges = getDefaultPrivileges(); + + const upgradeInProgress = await isUpgradeInProgress(); + const securityDisabled = isSecurityDisabled(xpackMainPlugin); + + if (securityDisabled === true) { + if (upgradeInProgress === true) { + // if security is disabled and an upgrade in is progress, + // force all "getting" privileges to be true + // leaving all "setting" privileges to be the default false + setGettingPrivileges({}, privileges, true); + } else { + // if no upgrade is in progress, + // get all privileges forced to true + Object.keys(privileges).forEach(k => (privileges[k as keyof Privileges] = true)); + } + } else { + // security enabled + // load all ml privileges for this user. + const { cluster } = await callWithRequest('ml.privilegeCheck', { body: mlPrivileges }); + setGettingPrivileges(cluster, privileges); + if (upgradeInProgress === false) { + // if an upgrade is in progress, don't apply the "setting" + // privileges. leave them to be the default false. + setActionPrivileges(cluster, privileges); + } + } + return { privileges, upgradeInProgress }; + } + return { getPrivileges }; +} + +function setGettingPrivileges( + cluster: ClusterPrivilege = {}, + privileges: Privileges, + forceTrue = false +) { + // Anomaly Detection + if ( + forceTrue || + (cluster['cluster:monitor/xpack/ml/job/get'] && + cluster['cluster:monitor/xpack/ml/job/stats/get']) + ) { + privileges.canGetJobs = true; + } + + if ( + forceTrue || + (cluster['cluster:monitor/xpack/ml/datafeeds/get'] && + cluster['cluster:monitor/xpack/ml/datafeeds/stats/get']) + ) { + privileges.canGetDatafeeds = true; + } + + // Calendars + if (forceTrue || cluster['cluster:monitor/xpack/ml/calendars/get']) { + privileges.canGetCalendars = true; + } + + // Filters + if (forceTrue || cluster['cluster:admin/xpack/ml/filters/get']) { + privileges.canGetFilters = true; + } + + // File Data Visualizer + if (forceTrue || cluster['cluster:monitor/xpack/ml/findfilestructure']) { + privileges.canFindFileStructure = true; + } + + // Data Frames + if ( + forceTrue || + (cluster['cluster:monitor/data_frame/get'] && cluster['cluster:monitor/data_frame/stats/get']) + ) { + privileges.canGetDataFrameJobs = true; + } +} + +function setActionPrivileges(cluster: ClusterPrivilege = {}, privileges: Privileges) { + // Anomaly Detection + if ( + cluster['cluster:admin/xpack/ml/job/put'] && + cluster['cluster:admin/xpack/ml/job/open'] && + cluster['cluster:admin/xpack/ml/datafeeds/put'] + ) { + privileges.canCreateJob = true; + } + + if (cluster['cluster:admin/xpack/ml/job/update']) { + privileges.canUpdateJob = true; + } + + if (cluster['cluster:admin/xpack/ml/job/open']) { + privileges.canOpenJob = true; + } + + if (cluster['cluster:admin/xpack/ml/job/close']) { + privileges.canCloseJob = true; + } + + if (cluster['cluster:admin/xpack/ml/job/forecast']) { + privileges.canForecastJob = true; + } + + if ( + cluster['cluster:admin/xpack/ml/job/delete'] && + cluster['cluster:admin/xpack/ml/datafeeds/delete'] + ) { + privileges.canDeleteJob = true; + } + + if ( + cluster['cluster:admin/xpack/ml/job/open'] && + cluster['cluster:admin/xpack/ml/datafeeds/start'] && + cluster['cluster:admin/xpack/ml/datafeeds/stop'] + ) { + privileges.canStartStopDatafeed = true; + } + + if (cluster['cluster:admin/xpack/ml/datafeeds/update']) { + privileges.canUpdateDatafeed = true; + } + + if (cluster['cluster:admin/xpack/ml/datafeeds/preview']) { + privileges.canPreviewDatafeed = true; + } + + // Calendars + if ( + cluster['cluster:admin/xpack/ml/calendars/put'] && + cluster['cluster:admin/xpack/ml/calendars/jobs/update'] && + cluster['cluster:admin/xpack/ml/calendars/events/post'] + ) { + privileges.canCreateCalendar = true; + } + + if ( + cluster['cluster:admin/xpack/ml/calendars/delete'] && + cluster['cluster:admin/xpack/ml/calendars/events/delete'] + ) { + privileges.canDeleteCalendar = true; + } + + // Filters + if ( + cluster['cluster:admin/xpack/ml/filters/put'] && + cluster['cluster:admin/xpack/ml/filters/update'] + ) { + privileges.canCreateFilter = true; + } + + if (cluster['cluster:admin/xpack/ml/filters/delete']) { + privileges.canDeleteFilter = true; + } + + // Data Frames + if (cluster['cluster:admin/data_frame/put']) { + privileges.canCreateDataFrameJob = true; + } + + if (cluster['cluster:admin/data_frame/delete']) { + privileges.canDeleteDataFrameJob = true; + } + + if (cluster['cluster:admin/data_frame/preview']) { + privileges.canPreviewDataFrameJob = true; + } + + if ( + cluster['cluster:admin/data_frame/start'] && + cluster['cluster:admin/data_frame/start_task'] && + cluster['cluster:admin/data_frame/stop'] + ) { + privileges.canStartStopDataFrameJob = true; + } +} diff --git a/x-pack/legacy/plugins/ml/server/lib/check_privileges/index.ts b/x-pack/legacy/plugins/ml/server/lib/check_privileges/index.ts new file mode 100644 index 000000000000..45621218c22a --- /dev/null +++ b/x-pack/legacy/plugins/ml/server/lib/check_privileges/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { privilegesProvider } from './check_privileges'; diff --git a/x-pack/legacy/plugins/ml/server/lib/check_privileges/privileges.ts b/x-pack/legacy/plugins/ml/server/lib/check_privileges/privileges.ts new file mode 100644 index 000000000000..f0de6e7ac505 --- /dev/null +++ b/x-pack/legacy/plugins/ml/server/lib/check_privileges/privileges.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export const mlPrivileges = { + cluster: [ + 'cluster:monitor/data_frame/get', + 'cluster:monitor/data_frame/stats/get', + 'cluster:monitor/xpack/ml/job/get', + 'cluster:monitor/xpack/ml/job/stats/get', + 'cluster:monitor/xpack/ml/datafeeds/get', + 'cluster:monitor/xpack/ml/datafeeds/stats/get', + 'cluster:monitor/xpack/ml/calendars/get', + 'cluster:admin/data_frame/delete', + 'cluster:admin/data_frame/preview', + 'cluster:admin/data_frame/put', + 'cluster:admin/data_frame/start', + 'cluster:admin/data_frame/start_task', + 'cluster:admin/data_frame/stop', + 'cluster:admin/xpack/ml/job/put', + 'cluster:admin/xpack/ml/job/delete', + 'cluster:admin/xpack/ml/job/update', + 'cluster:admin/xpack/ml/job/open', + 'cluster:admin/xpack/ml/job/close', + 'cluster:admin/xpack/ml/job/forecast', + 'cluster:admin/xpack/ml/datafeeds/put', + 'cluster:admin/xpack/ml/datafeeds/delete', + 'cluster:admin/xpack/ml/datafeeds/start', + 'cluster:admin/xpack/ml/datafeeds/stop', + 'cluster:admin/xpack/ml/datafeeds/update', + 'cluster:admin/xpack/ml/datafeeds/preview', + 'cluster:admin/xpack/ml/calendars/put', + 'cluster:admin/xpack/ml/calendars/delete', + 'cluster:admin/xpack/ml/calendars/jobs/update', + 'cluster:admin/xpack/ml/calendars/events/post', + 'cluster:admin/xpack/ml/calendars/events/delete', + 'cluster:admin/xpack/ml/filters/put', + 'cluster:admin/xpack/ml/filters/get', + 'cluster:admin/xpack/ml/filters/update', + 'cluster:admin/xpack/ml/filters/delete', + 'cluster:monitor/xpack/ml/findfilestructure', + ], +}; diff --git a/x-pack/legacy/plugins/ml/server/lib/check_privileges/upgrade.ts b/x-pack/legacy/plugins/ml/server/lib/check_privileges/upgrade.ts new file mode 100644 index 000000000000..9e62780c51b3 --- /dev/null +++ b/x-pack/legacy/plugins/ml/server/lib/check_privileges/upgrade.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { mlLog } from '../../client/log'; +import { callWithRequestType } from '../../../common/types/kibana'; + +export function upgradeCheckProvider(callWithRequest: callWithRequestType) { + async function isUpgradeInProgress(): Promise { + let upgradeInProgress = false; + try { + const info = await callWithRequest('ml.info'); + // if ml indices are currently being migrated, upgrade_mode will be set to true + // pass this back with the privileges to allow for the disabling of UI controls. + upgradeInProgress = info.upgrade_mode === true; + } catch (error) { + // if the ml.info check fails, it could be due to the user having insufficient privileges + // most likely they do not have the ml_user role and therefore will be blocked from using + // ML at all. However, we need to catch this error so the privilege check doesn't fail. + if (error.status === 403) { + mlLog.info( + 'Unable to determine whether upgrade is being performed due to insufficient user privileges' + ); + } else { + mlLog.warn('Unable to determine whether upgrade is being performed'); + } + } + return upgradeInProgress; + } + return { isUpgradeInProgress }; +} diff --git a/x-pack/legacy/plugins/ml/server/lib/security_utils.d.ts b/x-pack/legacy/plugins/ml/server/lib/security_utils.d.ts new file mode 100644 index 000000000000..2a333c61fb2d --- /dev/null +++ b/x-pack/legacy/plugins/ml/server/lib/security_utils.d.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export function isSecurityDisabled(xpackMainPlugin: any): boolean; diff --git a/x-pack/legacy/plugins/ml/server/routes/system.js b/x-pack/legacy/plugins/ml/server/routes/system.js index 169cca1c99d1..87c68757e6f2 100644 --- a/x-pack/legacy/plugins/ml/server/routes/system.js +++ b/x-pack/legacy/plugins/ml/server/routes/system.js @@ -8,6 +8,7 @@ import { callWithRequestFactory } from '../client/call_with_request_factory'; import { callWithInternalUserFactory } from '../client/call_with_internal_user_factory'; +import { privilegesProvider } from '../lib/check_privileges'; import { mlLog } from '../client/log'; import { wrapError } from '../client/errors'; @@ -82,6 +83,23 @@ export function systemRoutes({ commonRouteConfig, elasticsearchPlugin, route, xp } }); + route({ + method: 'GET', + path: '/api/ml/ml_privileges', + async handler(request) { + const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request); + try { + const { getPrivileges } = privilegesProvider(callWithRequest, xpackMainPlugin); + return await getPrivileges(); + } catch (error) { + return wrapError(error); + } + }, + config: { + ...commonRouteConfig + } + }); + route({ method: 'GET', path: '/api/ml/ml_node_count',