From b3c70027990dce1d8a9bfd3ce659f8635f961f92 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Fri, 24 Apr 2020 12:08:19 +0100 Subject: [PATCH] [ML] Moving to kibana capabilities (#64057) * [ML] Moving to kibana capabilities * fixing types * renaming privilges * renaming privileges to capabilities * renaming resolvers * correcting admin capabilities * fixing includes * removing any types * renaming type * readding spaces * adding capabilities switcher * updating comment * removing unnecessary failing tests * adding error to log --- .../uptime/public/state/actions/ml_anomaly.ts | 4 +- .../uptime/public/state/api/ml_anomaly.ts | 4 +- .../public/state/reducers/ml_anomaly.ts | 4 +- x-pack/plugins/ml/common/license/index.ts | 9 +- .../plugins/ml/common/license/ml_license.ts | 12 +- .../plugins/ml/common/types/capabilities.ts | 66 +++ x-pack/plugins/ml/common/types/privileges.ts | 75 --- .../check_capabilities.ts} | 58 +- .../get_capabilities.ts} | 14 +- .../anomalies_table/anomalies_table.test.js | 6 +- .../anomalies_table_columns.js | 2 +- .../components/anomalies_table/links_menu.js | 2 +- .../rule_editor/rule_editor_flyout.js | 2 +- .../rule_editor/rule_editor_flyout.test.js | 2 +- .../components/rule_editor/scope_section.js | 2 +- .../rule_editor/scope_section.test.js | 2 +- .../analytics_list/action_delete.test.tsx | 4 +- .../analytics_list/action_delete.tsx | 2 +- .../analytics_list/action_start.tsx | 2 +- .../components/analytics_list/actions.tsx | 2 +- .../analytics_list/analytics_list.tsx | 2 +- .../create_analytics_button.tsx | 2 +- .../hooks/use_create_analytics_form/state.ts | 2 +- .../results_links/results_links.tsx | 2 +- .../datavisualizer/index_based/page.tsx | 2 +- .../components/job_actions/management.js | 2 +- .../job_details/datafeed_preview_tab.js | 2 +- .../multi_job_actions/actions_menu.js | 2 +- .../group_selector/group_selector.js | 2 +- .../new_job_button/new_job_button.js | 2 +- .../jobs_list_page/jobs_list_page.tsx | 4 +- .../application/overview/overview_page.tsx | 2 +- .../public/application/routing/resolvers.ts | 4 +- .../routes/datavisualizer/datavisualizer.tsx | 4 +- .../routes/datavisualizer/file_based.tsx | 4 +- .../routes/datavisualizer/index_based.tsx | 4 +- .../routes/new_job/index_or_search.tsx | 4 +- .../routing/routes/new_job/wizard.tsx | 4 +- .../application/routing/routes/overview.tsx | 4 +- .../routing/routes/settings/calendar_list.tsx | 7 +- .../routes/settings/calendar_new_edit.tsx | 7 +- .../routing/routes/settings/filter_list.tsx | 7 +- .../routes/settings/filter_list_new_edit.tsx | 7 +- .../routing/routes/settings/settings.tsx | 7 +- .../services/ml_api_service/index.ts | 11 +- .../calendars/edit/new_calendar.test.js | 6 +- .../calendars/list/calendars_list.test.js | 6 +- .../delete_filter_list_modal.test.js | 2 +- .../filter_lists/list/filter_lists.test.js | 2 +- .../settings/filter_lists/list/table.test.js | 2 +- .../forecasting_modal/run_controls.js | 2 +- .../capabilities/__mocks__/ml_capabilities.ts | 29 + .../lib/capabilities/capabilities_switcher.ts | 58 ++ .../capabilities/check_capabilities.test.ts | 255 +++++++++ .../lib/capabilities/check_capabilities.ts | 47 ++ .../index.ts | 3 +- .../upgrade.ts | 0 .../__mocks__/call_with_request.ts | 154 ------ .../check_privileges/check_privileges.test.ts | 515 ------------------ .../lib/check_privileges/check_privileges.ts | 269 --------- .../server/lib/check_privileges/privileges.ts | 37 -- x-pack/plugins/ml/server/plugin.ts | 34 +- x-pack/plugins/ml/server/routes/apidoc.json | 3 +- .../plugins/ml/server/routes/job_service.ts | 31 +- x-pack/plugins/ml/server/routes/system.ts | 26 +- .../shared_services/providers/system.ts | 31 +- .../server/shared_services/shared_services.ts | 13 +- x-pack/plugins/ml/server/types.ts | 6 + 68 files changed, 690 insertions(+), 1215 deletions(-) create mode 100644 x-pack/plugins/ml/common/types/capabilities.ts delete mode 100644 x-pack/plugins/ml/common/types/privileges.ts rename x-pack/plugins/ml/public/application/{privilege/check_privilege.ts => capabilities/check_capabilities.ts} (70%) rename x-pack/plugins/ml/public/application/{privilege/get_privileges.ts => capabilities/get_capabilities.ts} (68%) create mode 100644 x-pack/plugins/ml/server/lib/capabilities/__mocks__/ml_capabilities.ts create mode 100644 x-pack/plugins/ml/server/lib/capabilities/capabilities_switcher.ts create mode 100644 x-pack/plugins/ml/server/lib/capabilities/check_capabilities.test.ts create mode 100644 x-pack/plugins/ml/server/lib/capabilities/check_capabilities.ts rename x-pack/plugins/ml/server/lib/{check_privileges => capabilities}/index.ts (65%) rename x-pack/plugins/ml/server/lib/{check_privileges => capabilities}/upgrade.ts (100%) delete mode 100644 x-pack/plugins/ml/server/lib/check_privileges/__mocks__/call_with_request.ts delete mode 100644 x-pack/plugins/ml/server/lib/check_privileges/check_privileges.test.ts delete mode 100644 x-pack/plugins/ml/server/lib/check_privileges/check_privileges.ts delete mode 100644 x-pack/plugins/ml/server/lib/check_privileges/privileges.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/actions/ml_anomaly.ts b/x-pack/legacy/plugins/uptime/public/state/actions/ml_anomaly.ts index 2e83490b71b5..67c75314a730 100644 --- a/x-pack/legacy/plugins/uptime/public/state/actions/ml_anomaly.ts +++ b/x-pack/legacy/plugins/uptime/public/state/actions/ml_anomaly.ts @@ -6,7 +6,7 @@ import { createAction } from 'redux-actions'; import { createAsyncAction } from './utils'; -import { PrivilegesResponse } from '../../../../../../plugins/ml/common/types/privileges'; +import { MlCapabilitiesResponse } from '../../../../../../plugins/ml/common/types/capabilities'; import { AnomaliesTableRecord } from '../../../../../../plugins/ml/common/types/anomalies'; import { CreateMLJobSuccess, @@ -27,7 +27,7 @@ export const createMLJobAction = createAsyncAction< CreateMLJobSuccess | null >('CREATE_ML_JOB'); -export const getMLCapabilitiesAction = createAsyncAction( +export const getMLCapabilitiesAction = createAsyncAction( 'GET_ML_CAPABILITIES' ); diff --git a/x-pack/legacy/plugins/uptime/public/state/api/ml_anomaly.ts b/x-pack/legacy/plugins/uptime/public/state/api/ml_anomaly.ts index f10745a50f56..16b90e992142 100644 --- a/x-pack/legacy/plugins/uptime/public/state/api/ml_anomaly.ts +++ b/x-pack/legacy/plugins/uptime/public/state/api/ml_anomaly.ts @@ -8,7 +8,7 @@ import moment from 'moment'; import { apiService } from './utils'; import { AnomalyRecords, AnomalyRecordsParams } from '../actions'; import { API_URLS, ML_JOB_ID, ML_MODULE_ID } from '../../../common/constants'; -import { PrivilegesResponse } from '../../../../../../plugins/ml/common/types/privileges'; +import { MlCapabilitiesResponse } from '../../../../../../plugins/ml/common/types/capabilities'; import { CreateMLJobSuccess, DeleteJobResults, @@ -20,7 +20,7 @@ import { JobExistResult } from '../../../../../../plugins/ml/common/types/data_r export const getMLJobId = (monitorId: string) => `${monitorId}_${ML_JOB_ID}`.toLowerCase(); -export const getMLCapabilities = async (): Promise => { +export const getMLCapabilities = async (): Promise => { return await apiService.get(API_URLS.ML_CAPABILITIES); }; diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/ml_anomaly.ts b/x-pack/legacy/plugins/uptime/public/state/reducers/ml_anomaly.ts index 9d59d3963432..df5e825c3488 100644 --- a/x-pack/legacy/plugins/uptime/public/state/reducers/ml_anomaly.ts +++ b/x-pack/legacy/plugins/uptime/public/state/reducers/ml_anomaly.ts @@ -17,7 +17,7 @@ import { import { getAsyncInitialState, handleAsyncAction } from './utils'; import { IHttpFetchError } from '../../../../../../../target/types/core/public/http'; import { AsyncInitialState } from './types'; -import { PrivilegesResponse } from '../../../../../../plugins/ml/common/types/privileges'; +import { MlCapabilitiesResponse } from '../../../../../../plugins/ml/common/types/capabilities'; import { CreateMLJobSuccess, DeleteJobResults } from '../actions/types'; import { JobExistResult } from '../../../../../../plugins/ml/common/types/data_recognizer'; @@ -26,7 +26,7 @@ export interface MLJobState { createJob: AsyncInitialState; deleteJob: AsyncInitialState; anomalies: AsyncInitialState; - mlCapabilities: AsyncInitialState; + mlCapabilities: AsyncInitialState; } const initialState: MLJobState = { diff --git a/x-pack/plugins/ml/common/license/index.ts b/x-pack/plugins/ml/common/license/index.ts index e901a9545897..e87986a26a3b 100644 --- a/x-pack/plugins/ml/common/license/index.ts +++ b/x-pack/plugins/ml/common/license/index.ts @@ -4,4 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -export { MlLicense, LicenseStatus, MINIMUM_FULL_LICENSE, MINIMUM_LICENSE } from './ml_license'; +export { + MlLicense, + LicenseStatus, + MINIMUM_FULL_LICENSE, + MINIMUM_LICENSE, + isFullLicense, + isMinimumLicense, +} from './ml_license'; diff --git a/x-pack/plugins/ml/common/license/ml_license.ts b/x-pack/plugins/ml/common/license/ml_license.ts index 2a6088731044..25b5b4992b22 100644 --- a/x-pack/plugins/ml/common/license/ml_license.ts +++ b/x-pack/plugins/ml/common/license/ml_license.ts @@ -38,8 +38,8 @@ export class MlLicense { this._isSecurityEnabled = securityIsEnabled; this._hasLicenseExpired = this._license.status === 'expired'; this._isMlEnabled = this._license.getFeature(PLUGIN_ID).isEnabled; - this._isMinimumLicense = this._license.check(PLUGIN_ID, MINIMUM_LICENSE).state === 'valid'; - this._isFullLicense = this._license.check(PLUGIN_ID, MINIMUM_FULL_LICENSE).state === 'valid'; + this._isMinimumLicense = isMinimumLicense(this._license); + this._isFullLicense = isFullLicense(this._license); if (this._initialized === false && postInitFunctions !== undefined) { postInitFunctions.forEach(f => f(this)); @@ -74,3 +74,11 @@ export class MlLicense { return this._isFullLicense; } } + +export function isFullLicense(license: ILicense) { + return license.check(PLUGIN_ID, MINIMUM_FULL_LICENSE).state === 'valid'; +} + +export function isMinimumLicense(license: ILicense) { + return license.check(PLUGIN_ID, MINIMUM_LICENSE).state === 'valid'; +} diff --git a/x-pack/plugins/ml/common/types/capabilities.ts b/x-pack/plugins/ml/common/types/capabilities.ts new file mode 100644 index 000000000000..2a449c95faa5 --- /dev/null +++ b/x-pack/plugins/ml/common/types/capabilities.ts @@ -0,0 +1,66 @@ +/* + * 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 { KibanaRequest } from 'kibana/server'; + +export const userMlCapabilities = { + // Anomaly Detection + canGetJobs: false, + canGetDatafeeds: false, + // Calendars + canGetCalendars: false, + // File Data Visualizer + canFindFileStructure: false, + // Filters + canGetFilters: false, + // Data Frame Analytics + canGetDataFrameAnalytics: false, +}; + +export const adminMlCapabilities = { + // Anomaly Detection + canCreateJob: false, + canDeleteJob: false, + canOpenJob: false, + canCloseJob: false, + canForecastJob: false, + canStartStopDatafeed: false, + canUpdateJob: false, + canUpdateDatafeed: false, + canPreviewDatafeed: false, + // Calendars + canCreateCalendar: false, + canDeleteCalendar: false, + // Filters + canCreateFilter: false, + canDeleteFilter: false, + // Data Frame Analytics + canDeleteDataFrameAnalytics: false, + canCreateDataFrameAnalytics: false, + canStartStopDataFrameAnalytics: false, +}; + +export type UserMlCapabilities = typeof userMlCapabilities; +export type AdminMlCapabilities = typeof adminMlCapabilities; +export type MlCapabilities = UserMlCapabilities & AdminMlCapabilities; + +export const basicLicenseMlCapabilities = ['canFindFileStructure'] as Array; + +export function getDefaultCapabilities(): MlCapabilities { + return { + ...userMlCapabilities, + ...adminMlCapabilities, + }; +} + +export interface MlCapabilitiesResponse { + capabilities: MlCapabilities; + upgradeInProgress: boolean; + isPlatinumOrTrialLicense: boolean; + mlFeatureEnabledInSpace: boolean; +} + +export type ResolveMlCapabilities = (request: KibanaRequest) => Promise; diff --git a/x-pack/plugins/ml/common/types/privileges.ts b/x-pack/plugins/ml/common/types/privileges.ts deleted file mode 100644 index d9089c751b81..000000000000 --- a/x-pack/plugins/ml/common/types/privileges.ts +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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 Frame Analytics - canGetDataFrameAnalytics: boolean; - canDeleteDataFrameAnalytics: boolean; - canCreateDataFrameAnalytics: boolean; - canStartStopDataFrameAnalytics: 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 Frame Analytics - canGetDataFrameAnalytics: false, - canDeleteDataFrameAnalytics: false, - canCreateDataFrameAnalytics: false, - canStartStopDataFrameAnalytics: false, - }; -} - -export interface PrivilegesResponse { - capabilities: Privileges; - upgradeInProgress: boolean; - isPlatinumOrTrialLicense: boolean; - mlFeatureEnabledInSpace: boolean; -} diff --git a/x-pack/plugins/ml/public/application/privilege/check_privilege.ts b/x-pack/plugins/ml/public/application/capabilities/check_capabilities.ts similarity index 70% rename from x-pack/plugins/ml/public/application/privilege/check_privilege.ts rename to x-pack/plugins/ml/public/application/capabilities/check_capabilities.ts index 4de8c6eb703f..1ca176d8d09c 100644 --- a/x-pack/plugins/ml/public/application/privilege/check_privilege.ts +++ b/x-pack/plugins/ml/public/application/capabilities/check_capabilities.ts @@ -8,19 +8,19 @@ import { i18n } from '@kbn/i18n'; import { hasLicenseExpired } from '../license'; -import { Privileges, getDefaultPrivileges } from '../../../common/types/privileges'; -import { getPrivileges, getManageMlPrivileges } from './get_privileges'; +import { MlCapabilities, getDefaultCapabilities } from '../../../common/types/capabilities'; +import { getCapabilities, getManageMlCapabilities } from './get_capabilities'; import { ACCESS_DENIED_PATH } from '../management/management_urls'; -let privileges: Privileges = getDefaultPrivileges(); -// manage_ml requires all monitor and admin cluster privileges: https://github.com/elastic/elasticsearch/blob/664a29c8905d8ce9ba8c18aa1ed5c5de93a0eabc/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilege.java#L53 -export function checkGetManagementMlJobs() { +let _capabilities: MlCapabilities = getDefaultCapabilities(); + +export function checkGetManagementMlJobsResolver() { return new Promise<{ mlFeatureEnabledInSpace: boolean }>((resolve, reject) => { - getManageMlPrivileges().then( + getManageMlCapabilities().then( ({ capabilities, isPlatinumOrTrialLicense, mlFeatureEnabledInSpace }) => { - privileges = capabilities; - // Loop through all privileges to ensure they are all set to true. - const isManageML = Object.values(privileges).every(p => p === true); + _capabilities = capabilities; + // Loop through all capabilities to ensure they are all set to true. + const isManageML = Object.values(_capabilities).every(p => p === true); if (isManageML === true && isPlatinumOrTrialLicense === true) { return resolve({ mlFeatureEnabledInSpace }); @@ -33,17 +33,17 @@ export function checkGetManagementMlJobs() { }); } -export function checkGetJobsPrivilege(): Promise { +export function checkGetJobsCapabilitiesResolver(): Promise { return new Promise((resolve, reject) => { - getPrivileges().then(({ capabilities, isPlatinumOrTrialLicense }) => { - privileges = capabilities; + getCapabilities().then(({ capabilities, isPlatinumOrTrialLicense }) => { + _capabilities = capabilities; // the minimum privilege for using ML with a platinum or trial license is being able to get the transforms list. - // all other functionality is controlled by the return privileges object. + // all other functionality is controlled by the return capabilities object. // if the license is basic (isPlatinumOrTrialLicense === false) then do not redirect, // allow the promise to resolve as the separate license check will redirect then user to // a basic feature - if (privileges.canGetJobs || isPlatinumOrTrialLicense === false) { - return resolve(privileges); + if (_capabilities.canGetJobs || isPlatinumOrTrialLicense === false) { + return resolve(_capabilities); } else { window.location.href = '#/access-denied'; return reject(); @@ -52,15 +52,15 @@ export function checkGetJobsPrivilege(): Promise { }); } -export function checkCreateJobsPrivilege(): Promise { +export function checkCreateJobsCapabilitiesResolver(): Promise { return new Promise((resolve, reject) => { - getPrivileges().then(({ capabilities, isPlatinumOrTrialLicense }) => { - privileges = capabilities; + getCapabilities().then(({ capabilities, isPlatinumOrTrialLicense }) => { + _capabilities = capabilities; // if the license is basic (isPlatinumOrTrialLicense === false) then do not redirect, // allow the promise to resolve as the separate license check will redirect then user to // a basic feature - if (privileges.canCreateJob || isPlatinumOrTrialLicense === false) { - return resolve(privileges); + if (_capabilities.canCreateJob || isPlatinumOrTrialLicense === false) { + return resolve(_capabilities); } else { // if the user has no permission to create a job, // redirect them back to the Transforms Management page @@ -71,14 +71,14 @@ export function checkCreateJobsPrivilege(): Promise { }); } -export function checkFindFileStructurePrivilege(): Promise { +export function checkFindFileStructurePrivilegeResolver(): Promise { return new Promise((resolve, reject) => { - getPrivileges().then(({ capabilities }) => { - privileges = capabilities; + getCapabilities().then(({ capabilities }) => { + _capabilities = capabilities; // the minimum privilege for using ML with a basic license is being able to use the datavisualizer. - // all other functionality is controlled by the return privileges object - if (privileges.canFindFileStructure) { - return resolve(privileges); + // all other functionality is controlled by the return _capabilities object + if (_capabilities.canFindFileStructure) { + return resolve(_capabilities); } else { window.location.href = '#/access-denied'; return reject(); @@ -89,14 +89,14 @@ export function checkFindFileStructurePrivilege(): Promise { // check the privilege type and the license to see whether a user has permission to access a feature. // takes the name of the privilege variable as specified in get_privileges.js -export function checkPermission(privilegeType: keyof Privileges) { +export function checkPermission(capability: keyof MlCapabilities) { const licenseHasExpired = hasLicenseExpired(); - return privileges[privilegeType] === true && licenseHasExpired !== true; + return _capabilities[capability] === true && licenseHasExpired !== true; } // create the text for the button's tooltips if the user's license has // expired or if they don't have the privilege to press that button -export function createPermissionFailureMessage(privilegeType: keyof Privileges) { +export function createPermissionFailureMessage(privilegeType: keyof MlCapabilities) { let message = ''; const licenseHasExpired = hasLicenseExpired(); if (licenseHasExpired) { diff --git a/x-pack/plugins/ml/public/application/privilege/get_privileges.ts b/x-pack/plugins/ml/public/application/capabilities/get_capabilities.ts similarity index 68% rename from x-pack/plugins/ml/public/application/privilege/get_privileges.ts rename to x-pack/plugins/ml/public/application/capabilities/get_capabilities.ts index a3811779333d..c9561ed25454 100644 --- a/x-pack/plugins/ml/public/application/privilege/get_privileges.ts +++ b/x-pack/plugins/ml/public/application/capabilities/get_capabilities.ts @@ -7,12 +7,12 @@ import { ml } from '../services/ml_api_service'; import { setUpgradeInProgress } from '../services/upgrade_service'; -import { PrivilegesResponse } from '../../../common/types/privileges'; +import { MlCapabilitiesResponse } from '../../../common/types/capabilities'; -export function getPrivileges(): Promise { +export function getCapabilities(): Promise { return new Promise((resolve, reject) => { - ml.checkMlPrivileges() - .then((resp: PrivilegesResponse) => { + ml.checkMlCapabilities() + .then((resp: MlCapabilitiesResponse) => { if (resp.upgradeInProgress === true) { setUpgradeInProgress(true); } @@ -24,10 +24,10 @@ export function getPrivileges(): Promise { }); } -export function getManageMlPrivileges(): Promise { +export function getManageMlCapabilities(): Promise { return new Promise((resolve, reject) => { - ml.checkManageMLPrivileges() - .then((resp: PrivilegesResponse) => { + ml.checkManageMLCapabilities() + .then((resp: MlCapabilitiesResponse) => { if (resp.upgradeInProgress === true) { setUpgradeInProgress(true); } diff --git a/x-pack/plugins/ml/public/application/components/anomalies_table/anomalies_table.test.js b/x-pack/plugins/ml/public/application/components/anomalies_table/anomalies_table.test.js index b881bfe4f1fe..7696143a9ce7 100644 --- a/x-pack/plugins/ml/public/application/components/anomalies_table/anomalies_table.test.js +++ b/x-pack/plugins/ml/public/application/components/anomalies_table/anomalies_table.test.js @@ -8,14 +8,14 @@ import React from 'react'; import mockAnomaliesTableData from '../../explorer/__mocks__/mock_anomalies_table_data.json'; import { getColumns } from './anomalies_table_columns'; -jest.mock('../../privilege/check_privilege', () => ({ +jest.mock('../../capabilities/check_capabilities', () => ({ checkPermission: () => false, })); jest.mock('../../license', () => ({ hasLicenseExpired: () => false, })); -jest.mock('../../privilege/get_privileges', () => ({ - getPrivileges: () => {}, +jest.mock('../../capabilities/get_capabilities', () => ({ + getCapabilities: () => {}, })); jest.mock('../../services/field_format_service', () => ({ getFieldFormat: () => {}, diff --git a/x-pack/plugins/ml/public/application/components/anomalies_table/anomalies_table_columns.js b/x-pack/plugins/ml/public/application/components/anomalies_table/anomalies_table_columns.js index 0c6c95992714..8f79ce4a6c08 100644 --- a/x-pack/plugins/ml/public/application/components/anomalies_table/anomalies_table_columns.js +++ b/x-pack/plugins/ml/public/application/components/anomalies_table/anomalies_table_columns.js @@ -23,7 +23,7 @@ import { DetectorCell } from './detector_cell'; import { EntityCell } from '../entity_cell'; import { InfluencersCell } from './influencers_cell'; import { LinksMenu } from './links_menu'; -import { checkPermission } from '../../privilege/check_privilege'; +import { checkPermission } from '../../capabilities/check_capabilities'; import { mlFieldFormatService } from '../../services/field_format_service'; import { isRuleSupported } from '../../../../common/util/anomaly_utils'; import { formatValue } from '../../formatters/format_value'; diff --git a/x-pack/plugins/ml/public/application/components/anomalies_table/links_menu.js b/x-pack/plugins/ml/public/application/components/anomalies_table/links_menu.js index 2a34f12330a7..5d2b8e35fbc0 100644 --- a/x-pack/plugins/ml/public/application/components/anomalies_table/links_menu.js +++ b/x-pack/plugins/ml/public/application/components/anomalies_table/links_menu.js @@ -17,7 +17,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { withKibana } from '../../../../../../../src/plugins/kibana_react/public'; import { ES_FIELD_TYPES } from '../../../../../../../src/plugins/data/public'; -import { checkPermission } from '../../privilege/check_privilege'; +import { checkPermission } from '../../capabilities/check_capabilities'; import { SEARCH_QUERY_LANGUAGE } from '../../../../common/constants/search'; import { isRuleSupported } from '../../../../common/util/anomaly_utils'; import { parseInterval } from '../../../../common/util/parse_interval'; diff --git a/x-pack/plugins/ml/public/application/components/rule_editor/rule_editor_flyout.js b/x-pack/plugins/ml/public/application/components/rule_editor/rule_editor_flyout.js index f8868ec09998..f259a1f1ffb0 100644 --- a/x-pack/plugins/ml/public/application/components/rule_editor/rule_editor_flyout.js +++ b/x-pack/plugins/ml/public/application/components/rule_editor/rule_editor_flyout.js @@ -30,7 +30,7 @@ import { import { DetectorDescriptionList } from './components/detector_description_list'; import { ActionsSection } from './actions_section'; -import { checkPermission } from '../../privilege/check_privilege'; +import { checkPermission } from '../../capabilities/check_capabilities'; import { ConditionsSection } from './conditions_section'; import { ScopeSection } from './scope_section'; import { SelectRuleAction } from './select_rule_action'; diff --git a/x-pack/plugins/ml/public/application/components/rule_editor/rule_editor_flyout.test.js b/x-pack/plugins/ml/public/application/components/rule_editor/rule_editor_flyout.test.js index 5c43c558a333..3e8a17eeb861 100644 --- a/x-pack/plugins/ml/public/application/components/rule_editor/rule_editor_flyout.test.js +++ b/x-pack/plugins/ml/public/application/components/rule_editor/rule_editor_flyout.test.js @@ -45,7 +45,7 @@ jest.mock('../../services/job_service', () => ({ }, })); jest.mock('../../services/ml_api_service', () => 'ml'); -jest.mock('../../privilege/check_privilege', () => ({ +jest.mock('../../capabilities/check_capabilities', () => ({ checkPermission: () => true, })); diff --git a/x-pack/plugins/ml/public/application/components/rule_editor/scope_section.js b/x-pack/plugins/ml/public/application/components/rule_editor/scope_section.js index 8dca6db96d45..48e0da72f067 100644 --- a/x-pack/plugins/ml/public/application/components/rule_editor/scope_section.js +++ b/x-pack/plugins/ml/public/application/components/rule_editor/scope_section.js @@ -14,7 +14,7 @@ import React from 'react'; import { EuiCallOut, EuiCheckbox, EuiLink, EuiSpacer, EuiTitle } from '@elastic/eui'; import { ScopeExpression } from './scope_expression'; -import { checkPermission } from '../../privilege/check_privilege'; +import { checkPermission } from '../../capabilities/check_capabilities'; import { getScopeFieldDefaults } from './utils'; import { FormattedMessage } from '@kbn/i18n/react'; diff --git a/x-pack/plugins/ml/public/application/components/rule_editor/scope_section.test.js b/x-pack/plugins/ml/public/application/components/rule_editor/scope_section.test.js index 3d7861fc7de0..189fb6a35d3e 100644 --- a/x-pack/plugins/ml/public/application/components/rule_editor/scope_section.test.js +++ b/x-pack/plugins/ml/public/application/components/rule_editor/scope_section.test.js @@ -11,7 +11,7 @@ jest.mock('../../services/job_service.js', () => 'mlJobService'); // The mock is hoisted to the top, so need to prefix the mock function // with 'mock' so it can be used lazily. const mockCheckPermission = jest.fn(() => true); -jest.mock('../../privilege/check_privilege', () => ({ +jest.mock('../../capabilities/check_capabilities', () => ({ checkPermission: privilege => mockCheckPermission(privilege), })); diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_delete.test.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_delete.test.tsx index 6d1db5033863..3e5224b76329 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_delete.test.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_delete.test.tsx @@ -7,13 +7,13 @@ import React from 'react'; import { render } from '@testing-library/react'; -import * as CheckPrivilige from '../../../../../privilege/check_privilege'; +import * as CheckPrivilige from '../../../../../capabilities/check_capabilities'; import { DeleteAction } from './action_delete'; import mockAnalyticsListItem from './__mocks__/analytics_list_item.json'; -jest.mock('../../../../../privilege/check_privilege', () => ({ +jest.mock('../../../../../capabilities/check_capabilities', () => ({ checkPermission: jest.fn(() => false), createPermissionFailureMessage: jest.fn(), })); diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_delete.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_delete.tsx index 47fc84cf450c..2923938ae68a 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_delete.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_delete.tsx @@ -19,7 +19,7 @@ import { deleteAnalytics } from '../../services/analytics_service'; import { checkPermission, createPermissionFailureMessage, -} from '../../../../../privilege/check_privilege'; +} from '../../../../../capabilities/check_capabilities'; import { isDataFrameAnalyticsRunning, DataFrameAnalyticsListRow } from './common'; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_start.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_start.tsx index 40664a141384..bbbda85c45e4 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_start.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_start.tsx @@ -19,7 +19,7 @@ import { startAnalytics } from '../../services/analytics_service'; import { checkPermission, createPermissionFailureMessage, -} from '../../../../../privilege/check_privilege'; +} from '../../../../../capabilities/check_capabilities'; import { DataFrameAnalyticsListRow, isCompletedAnalyticsJob } from './common'; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/actions.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/actions.tsx index 4e19df9ae22a..72514c91ff58 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/actions.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/actions.tsx @@ -12,7 +12,7 @@ import { DeepReadonly } from '../../../../../../../common/types/common'; import { checkPermission, createPermissionFailureMessage, -} from '../../../../../privilege/check_privilege'; +} from '../../../../../capabilities/check_capabilities'; import { getAnalysisType, diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/analytics_list.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/analytics_list.tsx index d2e5f582d23f..58bc75bd7309 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/analytics_list.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/analytics_list.tsx @@ -18,7 +18,7 @@ import { } from '@elastic/eui'; import { DataFrameAnalyticsId, useRefreshAnalyticsList } from '../../../../common'; -import { checkPermission } from '../../../../../privilege/check_privilege'; +import { checkPermission } from '../../../../../capabilities/check_capabilities'; import { getTaskStateBadge } from './columns'; import { diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_button/create_analytics_button.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_button/create_analytics_button.tsx index e5054e8a6ad2..3c8c3c3b3aa5 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_button/create_analytics_button.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_button/create_analytics_button.tsx @@ -7,7 +7,7 @@ import React, { FC } from 'react'; import { EuiButton, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { createPermissionFailureMessage } from '../../../../../privilege/check_privilege'; +import { createPermissionFailureMessage } from '../../../../../capabilities/check_capabilities'; import { CreateAnalyticsFormProps } from '../../hooks/use_create_analytics_form'; export const CreateAnalyticsButton: FC = props => { diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/state.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/state.ts index e121268e65e8..70840a442f6f 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/state.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/state.ts @@ -6,7 +6,7 @@ import { EuiComboBoxOptionOption } from '@elastic/eui'; import { DeepPartial, DeepReadonly } from '../../../../../../../common/types/common'; -import { checkPermission } from '../../../../../privilege/check_privilege'; +import { checkPermission } from '../../../../../capabilities/check_capabilities'; import { mlNodesAvailable } from '../../../../../ml_nodes_check'; import { newJobCapsService } from '../../../../../services/new_job_capabilities_service'; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/results_links/results_links.tsx b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/results_links/results_links.tsx index dddf64ce2cfd..c05ca63df3a7 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/results_links/results_links.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/results_links/results_links.tsx @@ -10,7 +10,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { EuiFlexGroup, EuiFlexItem, EuiCard, EuiIcon } from '@elastic/eui'; import { ml } from '../../../../services/ml_api_service'; import { isFullLicense } from '../../../../license'; -import { checkPermission } from '../../../../privilege/check_privilege'; +import { checkPermission } from '../../../../capabilities/check_capabilities'; import { mlNodesAvailable } from '../../../../ml_nodes_check/check_ml_nodes'; import { useMlKibana } from '../../../../contexts/kibana'; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/page.tsx b/x-pack/plugins/ml/public/application/datavisualizer/index_based/page.tsx index 3a37274edbc1..86ffc4a2614b 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/index_based/page.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/index_based/page.tsx @@ -33,7 +33,7 @@ import { NavigationMenu } from '../../components/navigation_menu'; import { ML_JOB_FIELD_TYPES } from '../../../../common/constants/field_types'; import { SEARCH_QUERY_LANGUAGE } from '../../../../common/constants/search'; import { isFullLicense } from '../../license'; -import { checkPermission } from '../../privilege/check_privilege'; +import { checkPermission } from '../../capabilities/check_capabilities'; import { mlNodesAvailable } from '../../ml_nodes_check/check_ml_nodes'; import { FullTimeRangeSelector } from '../../components/full_time_range_selector'; import { mlTimefilterRefresh$ } from '../../services/timefilter_refresh_service'; diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_actions/management.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_actions/management.js index 92ad842686b0..bb4bed93f922 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_actions/management.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_actions/management.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { checkPermission } from '../../../../privilege/check_privilege'; +import { checkPermission } from '../../../../capabilities/check_capabilities'; import { mlNodesAvailable } from '../../../../ml_nodes_check/check_ml_nodes'; import { getIndexPatternNames } from '../../../../util/index_utils'; diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/datafeed_preview_tab.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/datafeed_preview_tab.js index 216c416f30a6..9406f1b3456c 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/datafeed_preview_tab.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/datafeed_preview_tab.js @@ -10,7 +10,7 @@ import React, { Component } from 'react'; import { EuiSpacer, EuiCallOut, EuiLoadingSpinner } from '@elastic/eui'; import { mlJobService } from '../../../../services/job_service'; -import { checkPermission } from '../../../../privilege/check_privilege'; +import { checkPermission } from '../../../../capabilities/check_capabilities'; import { ML_DATA_PREVIEW_COUNT } from '../../../../../../common/util/job_utils'; import { MLJobEditor } from '../ml_job_editor'; import { FormattedMessage } from '@kbn/i18n/react'; diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/multi_job_actions/actions_menu.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/multi_job_actions/actions_menu.js index a5509c0f79a3..2e530a66cd83 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/multi_job_actions/actions_menu.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/multi_job_actions/actions_menu.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { checkPermission } from '../../../../privilege/check_privilege'; +import { checkPermission } from '../../../../capabilities/check_capabilities'; import { mlNodesAvailable } from '../../../../ml_nodes_check/check_ml_nodes'; import PropTypes from 'prop-types'; import React, { Component } from 'react'; diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/multi_job_actions/group_selector/group_selector.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/multi_job_actions/group_selector/group_selector.js index 5f91ba9b6f10..e7b6e3a771a8 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/multi_job_actions/group_selector/group_selector.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/multi_job_actions/group_selector/group_selector.js @@ -22,7 +22,7 @@ import { import { cloneDeep } from 'lodash'; import { ml } from '../../../../../services/ml_api_service'; -import { checkPermission } from '../../../../../privilege/check_privilege'; +import { checkPermission } from '../../../../../capabilities/check_capabilities'; import { GroupList } from './group_list'; import { NewGroupInput } from './new_group_input'; import { mlMessageBarService } from '../../../../../components/messagebar'; diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/new_job_button/new_job_button.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/new_job_button/new_job_button.js index 1297ca5b9afd..fdffa8b38ae0 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/new_job_button/new_job_button.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/new_job_button/new_job_button.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { checkPermission } from '../../../../privilege/check_privilege'; +import { checkPermission } from '../../../../capabilities/check_capabilities'; import { mlNodesAvailable } from '../../../../ml_nodes_check/check_ml_nodes'; import React from 'react'; diff --git a/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx b/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx index dca1235a96cb..4a41f3e45001 100644 --- a/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx +++ b/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx @@ -20,7 +20,7 @@ import { EuiTitle, } from '@elastic/eui'; -import { checkGetManagementMlJobs } from '../../../../privilege/check_privilege'; +import { checkGetManagementMlJobsResolver } from '../../../../capabilities/check_capabilities'; import { getDocLinks } from '../../../../util/dependency_cache'; // @ts-ignore undeclared module @@ -75,7 +75,7 @@ export const JobsListPage: FC<{ I18nContext: CoreStart['i18n']['Context'] }> = ( const check = async () => { try { - const checkPrivilege = await checkGetManagementMlJobs(); + const checkPrivilege = await checkGetManagementMlJobsResolver(); setInitialized(true); setIsMlEnabledInSpace(checkPrivilege.mlFeatureEnabledInSpace); } catch (e) { diff --git a/x-pack/plugins/ml/public/application/overview/overview_page.tsx b/x-pack/plugins/ml/public/application/overview/overview_page.tsx index 7c995c8a568d..9a852c491ee2 100644 --- a/x-pack/plugins/ml/public/application/overview/overview_page.tsx +++ b/x-pack/plugins/ml/public/application/overview/overview_page.tsx @@ -6,7 +6,7 @@ import React, { Fragment, FC } from 'react'; import { EuiFlexGroup, EuiPage, EuiPageBody } from '@elastic/eui'; -import { checkPermission } from '../privilege/check_privilege'; +import { checkPermission } from '../capabilities/check_capabilities'; import { mlNodesAvailable } from '../ml_nodes_check/check_ml_nodes'; import { NavigationMenu } from '../components/navigation_menu'; import { OverviewSideBar } from './components/sidebar'; diff --git a/x-pack/plugins/ml/public/application/routing/resolvers.ts b/x-pack/plugins/ml/public/application/routing/resolvers.ts index 776f0727389d..958221df8a63 100644 --- a/x-pack/plugins/ml/public/application/routing/resolvers.ts +++ b/x-pack/plugins/ml/public/application/routing/resolvers.ts @@ -6,7 +6,7 @@ import { loadIndexPatterns, loadSavedSearches } from '../util/index_utils'; import { checkFullLicense } from '../license'; -import { checkGetJobsPrivilege } from '../privilege/check_privilege'; +import { checkGetJobsCapabilitiesResolver } from '../capabilities/check_capabilities'; import { getMlNodeCount } from '../ml_nodes_check/check_ml_nodes'; import { loadMlServerInfo } from '../services/ml_server_info'; @@ -28,6 +28,6 @@ export const basicResolvers = ({ indexPatterns }: BasicResolverDependencies): Re getMlNodeCount, loadMlServerInfo, loadIndexPatterns: () => loadIndexPatterns(indexPatterns), - checkGetJobsPrivilege, + checkGetJobsCapabilities: checkGetJobsCapabilitiesResolver, loadSavedSearches, }); diff --git a/x-pack/plugins/ml/public/application/routing/routes/datavisualizer/datavisualizer.tsx b/x-pack/plugins/ml/public/application/routing/routes/datavisualizer/datavisualizer.tsx index d257a9c080c3..fc2d517b2edb 100644 --- a/x-pack/plugins/ml/public/application/routing/routes/datavisualizer/datavisualizer.tsx +++ b/x-pack/plugins/ml/public/application/routing/routes/datavisualizer/datavisualizer.tsx @@ -16,7 +16,7 @@ import { useResolver } from '../../use_resolver'; import { DatavisualizerSelector } from '../../../datavisualizer'; import { checkBasicLicense } from '../../../license'; -import { checkFindFileStructurePrivilege } from '../../../privilege/check_privilege'; +import { checkFindFileStructurePrivilegeResolver } from '../../../capabilities/check_capabilities'; import { DATA_VISUALIZER_BREADCRUMB, ML_BREADCRUMB } from '../../breadcrumbs'; const breadcrumbs = [ML_BREADCRUMB, DATA_VISUALIZER_BREADCRUMB]; @@ -30,7 +30,7 @@ export const selectorRoute: MlRoute = { const PageWrapper: FC = ({ location, deps }) => { const { context } = useResolver(undefined, undefined, deps.config, { checkBasicLicense, - checkFindFileStructurePrivilege, + checkFindFileStructurePrivilege: checkFindFileStructurePrivilegeResolver, }); return ( diff --git a/x-pack/plugins/ml/public/application/routing/routes/datavisualizer/file_based.tsx b/x-pack/plugins/ml/public/application/routing/routes/datavisualizer/file_based.tsx index 174b3e3b4b33..1115a3887082 100644 --- a/x-pack/plugins/ml/public/application/routing/routes/datavisualizer/file_based.tsx +++ b/x-pack/plugins/ml/public/application/routing/routes/datavisualizer/file_based.tsx @@ -17,7 +17,7 @@ import { useResolver } from '../../use_resolver'; import { FileDataVisualizerPage } from '../../../datavisualizer/file_based'; import { checkBasicLicense } from '../../../license'; -import { checkFindFileStructurePrivilege } from '../../../privilege/check_privilege'; +import { checkFindFileStructurePrivilegeResolver } from '../../../capabilities/check_capabilities'; import { loadIndexPatterns } from '../../../util/index_utils'; import { DATA_VISUALIZER_BREADCRUMB, ML_BREADCRUMB } from '../../breadcrumbs'; @@ -43,7 +43,7 @@ const PageWrapper: FC = ({ location, deps }) => { const { context } = useResolver('', undefined, deps.config, { checkBasicLicense, loadIndexPatterns: () => loadIndexPatterns(deps.indexPatterns), - checkFindFileStructurePrivilege, + checkFindFileStructurePrivilege: checkFindFileStructurePrivilegeResolver, }); return ( diff --git a/x-pack/plugins/ml/public/application/routing/routes/datavisualizer/index_based.tsx b/x-pack/plugins/ml/public/application/routing/routes/datavisualizer/index_based.tsx index a3dbc9f97124..1ec73fced82f 100644 --- a/x-pack/plugins/ml/public/application/routing/routes/datavisualizer/index_based.tsx +++ b/x-pack/plugins/ml/public/application/routing/routes/datavisualizer/index_based.tsx @@ -12,7 +12,7 @@ import { useResolver } from '../../use_resolver'; import { Page } from '../../../datavisualizer/index_based'; import { checkBasicLicense } from '../../../license'; -import { checkGetJobsPrivilege } from '../../../privilege/check_privilege'; +import { checkGetJobsCapabilitiesResolver } from '../../../capabilities/check_capabilities'; import { loadIndexPatterns } from '../../../util/index_utils'; import { checkMlNodesAvailable } from '../../../ml_nodes_check'; import { DATA_VISUALIZER_BREADCRUMB, ML_BREADCRUMB } from '../../breadcrumbs'; @@ -39,7 +39,7 @@ const PageWrapper: FC = ({ location, deps }) => { const { context } = useResolver(index, savedSearchId, deps.config, { checkBasicLicense, loadIndexPatterns: () => loadIndexPatterns(deps.indexPatterns), - checkGetJobsPrivilege, + checkGetJobsCapabilities: checkGetJobsCapabilitiesResolver, checkMlNodesAvailable, }); diff --git a/x-pack/plugins/ml/public/application/routing/routes/new_job/index_or_search.tsx b/x-pack/plugins/ml/public/application/routing/routes/new_job/index_or_search.tsx index 9411b415e4e4..b630b09b1a46 100644 --- a/x-pack/plugins/ml/public/application/routing/routes/new_job/index_or_search.tsx +++ b/x-pack/plugins/ml/public/application/routing/routes/new_job/index_or_search.tsx @@ -13,7 +13,7 @@ import { Page, preConfiguredJobRedirect } from '../../../jobs/new_job/pages/inde import { ANOMALY_DETECTION_BREADCRUMB, ML_BREADCRUMB } from '../../breadcrumbs'; import { checkBasicLicense } from '../../../license'; import { loadIndexPatterns } from '../../../util/index_utils'; -import { checkGetJobsPrivilege } from '../../../privilege/check_privilege'; +import { checkGetJobsCapabilitiesResolver } from '../../../capabilities/check_capabilities'; import { checkMlNodesAvailable } from '../../../ml_nodes_check'; enum MODE { @@ -71,7 +71,7 @@ const PageWrapper: FC = ({ nextStepPath, deps, mode }) = const dataVizResolvers = { checkBasicLicense, loadIndexPatterns: () => loadIndexPatterns(deps.indexPatterns), - checkGetJobsPrivilege, + checkGetJobsCapabilities: checkGetJobsCapabilitiesResolver, checkMlNodesAvailable, }; diff --git a/x-pack/plugins/ml/public/application/routing/routes/new_job/wizard.tsx b/x-pack/plugins/ml/public/application/routing/routes/new_job/wizard.tsx index b1256e21888d..1c91d7e94b24 100644 --- a/x-pack/plugins/ml/public/application/routing/routes/new_job/wizard.tsx +++ b/x-pack/plugins/ml/public/application/routing/routes/new_job/wizard.tsx @@ -15,7 +15,7 @@ import { Page } from '../../../jobs/new_job/pages/new_job'; import { JOB_TYPE } from '../../../../../common/constants/new_job'; import { mlJobService } from '../../../services/job_service'; import { loadNewJobCapabilities } from '../../../services/new_job_capabilities_service'; -import { checkCreateJobsPrivilege } from '../../../privilege/check_privilege'; +import { checkCreateJobsCapabilitiesResolver } from '../../../capabilities/check_capabilities'; import { ANOMALY_DETECTION_BREADCRUMB, ML_BREADCRUMB } from '../../breadcrumbs'; interface WizardPageProps extends PageProps { @@ -115,7 +115,7 @@ const PageWrapper: FC = ({ location, jobType, deps }) => { const { index, savedSearchId }: Record = parse(location.search, { sort: false }); const { context, results } = useResolver(index, savedSearchId, deps.config, { ...basicResolvers(deps), - privileges: checkCreateJobsPrivilege, + privileges: checkCreateJobsCapabilitiesResolver, jobCaps: () => loadNewJobCapabilities(index, savedSearchId, deps.indexPatterns), existingJobsAndGroups: mlJobService.getJobAndGroupIds, }); diff --git a/x-pack/plugins/ml/public/application/routing/routes/overview.tsx b/x-pack/plugins/ml/public/application/routing/routes/overview.tsx index ccb99985cb70..9b08bbf35c44 100644 --- a/x-pack/plugins/ml/public/application/routing/routes/overview.tsx +++ b/x-pack/plugins/ml/public/application/routing/routes/overview.tsx @@ -13,7 +13,7 @@ import { useResolver } from '../use_resolver'; import { OverviewPage } from '../../overview'; import { checkFullLicense } from '../../license'; -import { checkGetJobsPrivilege } from '../../privilege/check_privilege'; +import { checkGetJobsCapabilitiesResolver } from '../../capabilities/check_capabilities'; import { getMlNodeCount } from '../../ml_nodes_check'; import { loadMlServerInfo } from '../../services/ml_server_info'; import { useTimefilter } from '../../contexts/kibana'; @@ -38,7 +38,7 @@ export const overviewRoute: MlRoute = { const PageWrapper: FC = ({ deps }) => { const { context } = useResolver(undefined, undefined, deps.config, { checkFullLicense, - checkGetJobsPrivilege, + checkGetJobsCapabilities: checkGetJobsCapabilitiesResolver, getMlNodeCount, loadMlServerInfo, }); diff --git a/x-pack/plugins/ml/public/application/routing/routes/settings/calendar_list.tsx b/x-pack/plugins/ml/public/application/routing/routes/settings/calendar_list.tsx index 9d5c4e9c0b0a..e015a3292acc 100644 --- a/x-pack/plugins/ml/public/application/routing/routes/settings/calendar_list.tsx +++ b/x-pack/plugins/ml/public/application/routing/routes/settings/calendar_list.tsx @@ -17,7 +17,10 @@ import { useResolver } from '../../use_resolver'; import { useTimefilter } from '../../../contexts/kibana'; import { checkFullLicense } from '../../../license'; -import { checkGetJobsPrivilege, checkPermission } from '../../../privilege/check_privilege'; +import { + checkGetJobsCapabilitiesResolver, + checkPermission, +} from '../../../capabilities/check_capabilities'; import { getMlNodeCount } from '../../../ml_nodes_check/check_ml_nodes'; import { CalendarsList } from '../../../settings/calendars'; import { SETTINGS, ML_BREADCRUMB } from '../../breadcrumbs'; @@ -42,7 +45,7 @@ export const calendarListRoute: MlRoute = { const PageWrapper: FC = ({ deps }) => { const { context } = useResolver(undefined, undefined, deps.config, { checkFullLicense, - checkGetJobsPrivilege, + checkGetJobsCapabilities: checkGetJobsCapabilitiesResolver, getMlNodeCount, }); diff --git a/x-pack/plugins/ml/public/application/routing/routes/settings/calendar_new_edit.tsx b/x-pack/plugins/ml/public/application/routing/routes/settings/calendar_new_edit.tsx index bf039e3bd235..ebd58120853a 100644 --- a/x-pack/plugins/ml/public/application/routing/routes/settings/calendar_new_edit.tsx +++ b/x-pack/plugins/ml/public/application/routing/routes/settings/calendar_new_edit.tsx @@ -17,7 +17,10 @@ import { useResolver } from '../../use_resolver'; import { useTimefilter } from '../../../contexts/kibana'; import { checkFullLicense } from '../../../license'; -import { checkGetJobsPrivilege, checkPermission } from '../../../privilege/check_privilege'; +import { + checkGetJobsCapabilitiesResolver, + checkPermission, +} from '../../../capabilities/check_capabilities'; import { checkMlNodesAvailable } from '../../../ml_nodes_check/check_ml_nodes'; import { NewCalendar } from '../../../settings/calendars'; import { SETTINGS, ML_BREADCRUMB } from '../../breadcrumbs'; @@ -74,7 +77,7 @@ const PageWrapper: FC = ({ location, mode, deps }) => { const { context } = useResolver(undefined, undefined, deps.config, { checkFullLicense, - checkGetJobsPrivilege, + checkGetJobsCapabilities: checkGetJobsCapabilitiesResolver, checkMlNodesAvailable, }); diff --git a/x-pack/plugins/ml/public/application/routing/routes/settings/filter_list.tsx b/x-pack/plugins/ml/public/application/routing/routes/settings/filter_list.tsx index 6839ad833cb0..25bded1a52db 100644 --- a/x-pack/plugins/ml/public/application/routing/routes/settings/filter_list.tsx +++ b/x-pack/plugins/ml/public/application/routing/routes/settings/filter_list.tsx @@ -17,7 +17,10 @@ import { useResolver } from '../../use_resolver'; import { useTimefilter } from '../../../contexts/kibana'; import { checkFullLicense } from '../../../license'; -import { checkGetJobsPrivilege, checkPermission } from '../../../privilege/check_privilege'; +import { + checkGetJobsCapabilitiesResolver, + checkPermission, +} from '../../../capabilities/check_capabilities'; import { getMlNodeCount } from '../../../ml_nodes_check/check_ml_nodes'; import { FilterLists } from '../../../settings/filter_lists'; @@ -43,7 +46,7 @@ export const filterListRoute: MlRoute = { const PageWrapper: FC = ({ deps }) => { const { context } = useResolver(undefined, undefined, deps.config, { checkFullLicense, - checkGetJobsPrivilege, + checkGetJobsCapabilities: checkGetJobsCapabilitiesResolver, getMlNodeCount, }); diff --git a/x-pack/plugins/ml/public/application/routing/routes/settings/filter_list_new_edit.tsx b/x-pack/plugins/ml/public/application/routing/routes/settings/filter_list_new_edit.tsx index 7b8bd6c3c81a..2f4ccecf2f1a 100644 --- a/x-pack/plugins/ml/public/application/routing/routes/settings/filter_list_new_edit.tsx +++ b/x-pack/plugins/ml/public/application/routing/routes/settings/filter_list_new_edit.tsx @@ -17,7 +17,10 @@ import { useResolver } from '../../use_resolver'; import { useTimefilter } from '../../../contexts/kibana'; import { checkFullLicense } from '../../../license'; -import { checkGetJobsPrivilege, checkPermission } from '../../../privilege/check_privilege'; +import { + checkGetJobsCapabilitiesResolver, + checkPermission, +} from '../../../capabilities/check_capabilities'; import { checkMlNodesAvailable } from '../../../ml_nodes_check/check_ml_nodes'; import { EditFilterList } from '../../../settings/filter_lists'; import { SETTINGS, ML_BREADCRUMB } from '../../breadcrumbs'; @@ -74,7 +77,7 @@ const PageWrapper: FC = ({ location, mode, deps }) => { const { context } = useResolver(undefined, undefined, deps.config, { checkFullLicense, - checkGetJobsPrivilege, + checkGetJobsCapabilities: checkGetJobsCapabilitiesResolver, checkMlNodesAvailable, }); diff --git a/x-pack/plugins/ml/public/application/routing/routes/settings/settings.tsx b/x-pack/plugins/ml/public/application/routing/routes/settings/settings.tsx index 10ccc0987fe5..7cb943c091c4 100644 --- a/x-pack/plugins/ml/public/application/routing/routes/settings/settings.tsx +++ b/x-pack/plugins/ml/public/application/routing/routes/settings/settings.tsx @@ -16,7 +16,10 @@ import { useResolver } from '../../use_resolver'; import { useTimefilter } from '../../../contexts/kibana'; import { checkFullLicense } from '../../../license'; -import { checkGetJobsPrivilege, checkPermission } from '../../../privilege/check_privilege'; +import { + checkGetJobsCapabilitiesResolver, + checkPermission, +} from '../../../capabilities/check_capabilities'; import { getMlNodeCount } from '../../../ml_nodes_check/check_ml_nodes'; import { Settings } from '../../../settings'; import { ML_BREADCRUMB, SETTINGS } from '../../breadcrumbs'; @@ -32,7 +35,7 @@ export const settingsRoute: MlRoute = { const PageWrapper: FC = ({ deps }) => { const { context } = useResolver(undefined, undefined, deps.config, { checkFullLicense, - checkGetJobsPrivilege, + checkGetJobsCapabilities: checkGetJobsCapabilitiesResolver, getMlNodeCount, }); diff --git a/x-pack/plugins/ml/public/application/services/ml_api_service/index.ts b/x-pack/plugins/ml/public/application/services/ml_api_service/index.ts index e16012683380..6e3fd08e90e3 100644 --- a/x-pack/plugins/ml/public/application/services/ml_api_service/index.ts +++ b/x-pack/plugins/ml/public/application/services/ml_api_service/index.ts @@ -15,7 +15,7 @@ import { jobs } from './jobs'; import { fileDatavisualizer } from './datavisualizer'; import { MlServerDefaults, MlServerLimits } from '../../../../common/types/ml_server_info'; -import { PrivilegesResponse } from '../../../../common/types/privileges'; +import { MlCapabilitiesResponse } from '../../../../common/types/capabilities'; import { Calendar, CalendarId, UpdateCalendar } from '../../../../common/types/calendars'; import { Job, @@ -300,18 +300,17 @@ export const ml = { }); }, - checkMlPrivileges() { - return http({ + checkMlCapabilities() { + return http({ path: `${basePath()}/ml_capabilities`, method: 'GET', }); }, - checkManageMLPrivileges() { - return http({ + checkManageMLCapabilities() { + return http({ path: `${basePath()}/ml_capabilities`, method: 'GET', - query: { ignoreSpaces: true }, }); }, diff --git a/x-pack/plugins/ml/public/application/settings/calendars/edit/new_calendar.test.js b/x-pack/plugins/ml/public/application/settings/calendars/edit/new_calendar.test.js index 7e2d6814c0b2..7f5ade64e7f1 100644 --- a/x-pack/plugins/ml/public/application/settings/calendars/edit/new_calendar.test.js +++ b/x-pack/plugins/ml/public/application/settings/calendars/edit/new_calendar.test.js @@ -7,15 +7,15 @@ jest.mock('../../../components/navigation_menu', () => ({ NavigationMenu: () =>
, })); -jest.mock('../../../privilege/check_privilege', () => ({ +jest.mock('../../../capabilities/check_capabilities', () => ({ checkPermission: () => true, })); jest.mock('../../../license', () => ({ hasLicenseExpired: () => false, isFullLicense: () => false, })); -jest.mock('../../../privilege/get_privileges', () => ({ - getPrivileges: () => {}, +jest.mock('../../../capabilities/get_capabilities', () => ({ + getCapabilities: () => {}, })); jest.mock('../../../ml_nodes_check/check_ml_nodes', () => ({ mlNodesAvailable: () => true, diff --git a/x-pack/plugins/ml/public/application/settings/calendars/list/calendars_list.test.js b/x-pack/plugins/ml/public/application/settings/calendars/list/calendars_list.test.js index 8750927ac1ee..b2fce2c1474c 100644 --- a/x-pack/plugins/ml/public/application/settings/calendars/list/calendars_list.test.js +++ b/x-pack/plugins/ml/public/application/settings/calendars/list/calendars_list.test.js @@ -13,15 +13,15 @@ import { CalendarsList } from './calendars_list'; jest.mock('../../../components/navigation_menu', () => ({ NavigationMenu: () =>
, })); -jest.mock('../../../privilege/check_privilege', () => ({ +jest.mock('../../../capabilities/check_capabilities', () => ({ checkPermission: () => true, })); jest.mock('../../../license', () => ({ hasLicenseExpired: () => false, isFullLicense: () => false, })); -jest.mock('../../../privilege/get_privileges', () => ({ - getPrivileges: () => {}, +jest.mock('../../../capabilities/get_capabilities', () => ({ + getCapabilities: () => {}, })); jest.mock('../../../ml_nodes_check/check_ml_nodes', () => ({ mlNodesAvailable: () => true, diff --git a/x-pack/plugins/ml/public/application/settings/filter_lists/components/delete_filter_list_modal/delete_filter_list_modal.test.js b/x-pack/plugins/ml/public/application/settings/filter_lists/components/delete_filter_list_modal/delete_filter_list_modal.test.js index 8b1ab853676c..0266bc2a5531 100644 --- a/x-pack/plugins/ml/public/application/settings/filter_lists/components/delete_filter_list_modal/delete_filter_list_modal.test.js +++ b/x-pack/plugins/ml/public/application/settings/filter_lists/components/delete_filter_list_modal/delete_filter_list_modal.test.js @@ -8,7 +8,7 @@ // The mock is hoisted to the top, so need to prefix the mock function // with 'mock' so it can be used lazily. const mockCheckPermission = jest.fn(() => true); -jest.mock('../../../../privilege/check_privilege', () => ({ +jest.mock('../../../../capabilities/check_capabilities', () => ({ checkPermission: privilege => mockCheckPermission(privilege), })); jest.mock('../../../../services/ml_api_service', () => 'ml'); diff --git a/x-pack/plugins/ml/public/application/settings/filter_lists/list/filter_lists.test.js b/x-pack/plugins/ml/public/application/settings/filter_lists/list/filter_lists.test.js index dbc815b5fc09..c1bcee4acdd3 100644 --- a/x-pack/plugins/ml/public/application/settings/filter_lists/list/filter_lists.test.js +++ b/x-pack/plugins/ml/public/application/settings/filter_lists/list/filter_lists.test.js @@ -12,7 +12,7 @@ import { FilterLists } from './filter_lists'; jest.mock('../../../components/navigation_menu', () => ({ NavigationMenu: () =>
, })); -jest.mock('../../../privilege/check_privilege', () => ({ +jest.mock('../../../capabilities/check_capabilities', () => ({ checkPermission: () => true, })); diff --git a/x-pack/plugins/ml/public/application/settings/filter_lists/list/table.test.js b/x-pack/plugins/ml/public/application/settings/filter_lists/list/table.test.js index a5c1c872bfa5..29b1185ddd4a 100644 --- a/x-pack/plugins/ml/public/application/settings/filter_lists/list/table.test.js +++ b/x-pack/plugins/ml/public/application/settings/filter_lists/list/table.test.js @@ -6,7 +6,7 @@ // Create a mock for the privilege check used within the table to // enable/disable the 'New Filter' button. -jest.mock('../../../privilege/check_privilege', () => ({ +jest.mock('../../../capabilities/check_capabilities', () => ({ checkPermission: () => true, })); jest.mock('../../../services/ml_api_service', () => 'ml'); diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/run_controls.js b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/run_controls.js index c04ac40b962d..7dd06268f7f8 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/run_controls.js +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/run_controls.js @@ -32,7 +32,7 @@ import { mlNodesAvailable } from '../../../ml_nodes_check/check_ml_nodes'; import { checkPermission, createPermissionFailureMessage, -} from '../../../privilege/check_privilege'; +} from '../../../capabilities/check_capabilities'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; diff --git a/x-pack/plugins/ml/server/lib/capabilities/__mocks__/ml_capabilities.ts b/x-pack/plugins/ml/server/lib/capabilities/__mocks__/ml_capabilities.ts new file mode 100644 index 000000000000..8e350b838227 --- /dev/null +++ b/x-pack/plugins/ml/server/lib/capabilities/__mocks__/ml_capabilities.ts @@ -0,0 +1,29 @@ +/* + * 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 { + adminMlCapabilities, + userMlCapabilities, + MlCapabilities, + getDefaultCapabilities, +} from '../../../../common/types/capabilities'; + +export function getAdminCapabilities() { + const caps: any = {}; + Object.keys(adminMlCapabilities).forEach(k => { + caps[k] = true; + }); + return { ...getUserCapabilities(), ...caps } as MlCapabilities; +} + +export function getUserCapabilities() { + const caps: any = {}; + Object.keys(userMlCapabilities).forEach(k => { + caps[k] = true; + }); + + return { ...getDefaultCapabilities(), ...caps } as MlCapabilities; +} diff --git a/x-pack/plugins/ml/server/lib/capabilities/capabilities_switcher.ts b/x-pack/plugins/ml/server/lib/capabilities/capabilities_switcher.ts new file mode 100644 index 000000000000..aef22debf364 --- /dev/null +++ b/x-pack/plugins/ml/server/lib/capabilities/capabilities_switcher.ts @@ -0,0 +1,58 @@ +/* + * 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 { cloneDeep } from 'lodash'; +import { Observable } from 'rxjs'; +import { take } from 'rxjs/operators'; +import { CapabilitiesSwitcher, CoreSetup, Logger } from 'src/core/server'; +import { ILicense } from '../../../../licensing/common/types'; +import { isFullLicense, isMinimumLicense } from '../../../common/license'; +import { MlCapabilities, basicLicenseMlCapabilities } from '../../../common/types/capabilities'; + +export const setupCapabilitiesSwitcher = ( + coreSetup: CoreSetup, + license$: Observable, + logger: Logger +) => { + coreSetup.capabilities.registerSwitcher(getSwitcher(license$, logger)); +}; + +function getSwitcher(license$: Observable, logger: Logger): CapabilitiesSwitcher { + return async (request, capabilities) => { + const isAnonymousRequest = !request.route.options.authRequired; + + if (isAnonymousRequest) { + return capabilities; + } + + try { + const license = await license$.pipe(take(1)).toPromise(); + + // full license, leave capabilities as they were + if (isFullLicense(license)) { + return capabilities; + } + + const mlCaps = capabilities.ml as MlCapabilities; + const originalCapabilities = cloneDeep(mlCaps); + + // not full licence, switch off all capabilities + Object.keys(mlCaps).forEach(k => { + mlCaps[k as keyof MlCapabilities] = false; + }); + + // for a basic license, reapply the original capabilities for the basic license features + if (isMinimumLicense(license)) { + basicLicenseMlCapabilities.forEach(c => (mlCaps[c] = originalCapabilities[c])); + } + + return capabilities; + } catch (e) { + logger.debug(`Error updating capabilities for ML based on licensing: ${e}`); + return capabilities; + } + }; +} diff --git a/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.test.ts b/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.test.ts new file mode 100644 index 000000000000..5093801d2d18 --- /dev/null +++ b/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.test.ts @@ -0,0 +1,255 @@ +/* + * 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 { getAdminCapabilities, getUserCapabilities } from './__mocks__/ml_capabilities'; +import { capabilitiesProvider } from './check_capabilities'; +import { MlLicense } from '../../../common/license'; +import { getDefaultCapabilities } from '../../../common/types/capabilities'; + +const mlLicense = { + isSecurityEnabled: () => true, + isFullLicense: () => true, +} as MlLicense; + +const mlLicenseBasic = { + isSecurityEnabled: () => true, + isFullLicense: () => false, +} as MlLicense; + +const mlIsEnabled = async () => true; +const mlIsNotEnabled = async () => false; + +const callWithRequestNonUpgrade = async () => ({ upgrade_mode: false }); +const callWithRequestUpgrade = async () => ({ upgrade_mode: true }); + +describe('check_capabilities', () => { + describe('getCapabilities() - right number of capabilities', () => { + test('kibana capabilities count', async done => { + const { getCapabilities } = capabilitiesProvider( + callWithRequestNonUpgrade, + getAdminCapabilities(), + mlLicense, + mlIsEnabled + ); + const { capabilities } = await getCapabilities(); + const count = Object.keys(capabilities).length; + expect(count).toBe(22); + done(); + }); + }); + + describe('getCapabilities() with security', () => { + test('ml_user capabilities only', async done => { + const { getCapabilities } = capabilitiesProvider( + callWithRequestNonUpgrade, + getUserCapabilities(), + mlLicense, + mlIsEnabled + ); + const { capabilities, upgradeInProgress, mlFeatureEnabledInSpace } = await getCapabilities(); + expect(upgradeInProgress).toBe(false); + expect(mlFeatureEnabledInSpace).toBe(true); + expect(capabilities.canGetJobs).toBe(true); + expect(capabilities.canCreateJob).toBe(false); + expect(capabilities.canDeleteJob).toBe(false); + expect(capabilities.canOpenJob).toBe(false); + expect(capabilities.canCloseJob).toBe(false); + expect(capabilities.canForecastJob).toBe(false); + expect(capabilities.canGetDatafeeds).toBe(true); + expect(capabilities.canStartStopDatafeed).toBe(false); + expect(capabilities.canUpdateJob).toBe(false); + expect(capabilities.canUpdateDatafeed).toBe(false); + expect(capabilities.canPreviewDatafeed).toBe(false); + expect(capabilities.canGetCalendars).toBe(true); + expect(capabilities.canCreateCalendar).toBe(false); + expect(capabilities.canDeleteCalendar).toBe(false); + expect(capabilities.canGetFilters).toBe(true); + expect(capabilities.canCreateFilter).toBe(false); + expect(capabilities.canDeleteFilter).toBe(false); + expect(capabilities.canFindFileStructure).toBe(true); + expect(capabilities.canGetDataFrameAnalytics).toBe(true); + expect(capabilities.canDeleteDataFrameAnalytics).toBe(false); + expect(capabilities.canCreateDataFrameAnalytics).toBe(false); + expect(capabilities.canStartStopDataFrameAnalytics).toBe(false); + done(); + }); + + test('full capabilities', async done => { + const { getCapabilities } = capabilitiesProvider( + callWithRequestNonUpgrade, + getAdminCapabilities(), + mlLicense, + mlIsEnabled + ); + const { capabilities, upgradeInProgress, mlFeatureEnabledInSpace } = await getCapabilities(); + expect(upgradeInProgress).toBe(false); + expect(mlFeatureEnabledInSpace).toBe(true); + expect(capabilities.canGetJobs).toBe(true); + expect(capabilities.canCreateJob).toBe(true); + expect(capabilities.canDeleteJob).toBe(true); + expect(capabilities.canOpenJob).toBe(true); + expect(capabilities.canCloseJob).toBe(true); + expect(capabilities.canForecastJob).toBe(true); + expect(capabilities.canGetDatafeeds).toBe(true); + expect(capabilities.canStartStopDatafeed).toBe(true); + expect(capabilities.canUpdateJob).toBe(true); + expect(capabilities.canUpdateDatafeed).toBe(true); + expect(capabilities.canPreviewDatafeed).toBe(true); + expect(capabilities.canGetCalendars).toBe(true); + expect(capabilities.canCreateCalendar).toBe(true); + expect(capabilities.canDeleteCalendar).toBe(true); + expect(capabilities.canGetFilters).toBe(true); + expect(capabilities.canCreateFilter).toBe(true); + expect(capabilities.canDeleteFilter).toBe(true); + expect(capabilities.canFindFileStructure).toBe(true); + expect(capabilities.canGetDataFrameAnalytics).toBe(true); + expect(capabilities.canDeleteDataFrameAnalytics).toBe(true); + expect(capabilities.canCreateDataFrameAnalytics).toBe(true); + expect(capabilities.canStartStopDataFrameAnalytics).toBe(true); + done(); + }); + + test('upgrade in progress with full capabilities', async done => { + const { getCapabilities } = capabilitiesProvider( + callWithRequestUpgrade, + getAdminCapabilities(), + mlLicense, + mlIsEnabled + ); + const { capabilities, upgradeInProgress, mlFeatureEnabledInSpace } = await getCapabilities(); + expect(upgradeInProgress).toBe(true); + expect(mlFeatureEnabledInSpace).toBe(true); + expect(capabilities.canGetJobs).toBe(true); + expect(capabilities.canCreateJob).toBe(false); + expect(capabilities.canDeleteJob).toBe(false); + expect(capabilities.canOpenJob).toBe(false); + expect(capabilities.canCloseJob).toBe(false); + expect(capabilities.canForecastJob).toBe(false); + expect(capabilities.canGetDatafeeds).toBe(true); + expect(capabilities.canStartStopDatafeed).toBe(false); + expect(capabilities.canUpdateJob).toBe(false); + expect(capabilities.canUpdateDatafeed).toBe(false); + expect(capabilities.canPreviewDatafeed).toBe(false); + expect(capabilities.canGetCalendars).toBe(true); + expect(capabilities.canCreateCalendar).toBe(false); + expect(capabilities.canDeleteCalendar).toBe(false); + expect(capabilities.canGetFilters).toBe(true); + expect(capabilities.canCreateFilter).toBe(false); + expect(capabilities.canDeleteFilter).toBe(false); + expect(capabilities.canFindFileStructure).toBe(true); + expect(capabilities.canGetDataFrameAnalytics).toBe(true); + expect(capabilities.canDeleteDataFrameAnalytics).toBe(false); + expect(capabilities.canCreateDataFrameAnalytics).toBe(false); + expect(capabilities.canStartStopDataFrameAnalytics).toBe(false); + done(); + }); + + test('upgrade in progress with partial capabilities', async done => { + const { getCapabilities } = capabilitiesProvider( + callWithRequestUpgrade, + getUserCapabilities(), + mlLicense, + mlIsEnabled + ); + const { capabilities, upgradeInProgress, mlFeatureEnabledInSpace } = await getCapabilities(); + expect(upgradeInProgress).toBe(true); + expect(mlFeatureEnabledInSpace).toBe(true); + expect(capabilities.canGetJobs).toBe(true); + expect(capabilities.canCreateJob).toBe(false); + expect(capabilities.canDeleteJob).toBe(false); + expect(capabilities.canOpenJob).toBe(false); + expect(capabilities.canCloseJob).toBe(false); + expect(capabilities.canForecastJob).toBe(false); + expect(capabilities.canGetDatafeeds).toBe(true); + expect(capabilities.canStartStopDatafeed).toBe(false); + expect(capabilities.canUpdateJob).toBe(false); + expect(capabilities.canUpdateDatafeed).toBe(false); + expect(capabilities.canPreviewDatafeed).toBe(false); + expect(capabilities.canGetCalendars).toBe(true); + expect(capabilities.canCreateCalendar).toBe(false); + expect(capabilities.canDeleteCalendar).toBe(false); + expect(capabilities.canGetFilters).toBe(true); + expect(capabilities.canCreateFilter).toBe(false); + expect(capabilities.canDeleteFilter).toBe(false); + expect(capabilities.canFindFileStructure).toBe(true); + expect(capabilities.canGetDataFrameAnalytics).toBe(true); + expect(capabilities.canDeleteDataFrameAnalytics).toBe(false); + expect(capabilities.canCreateDataFrameAnalytics).toBe(false); + expect(capabilities.canStartStopDataFrameAnalytics).toBe(false); + done(); + }); + + test('full capabilities, ml disabled in space', async done => { + const { getCapabilities } = capabilitiesProvider( + callWithRequestNonUpgrade, + getDefaultCapabilities(), + mlLicense, + mlIsNotEnabled + ); + const { capabilities, upgradeInProgress, mlFeatureEnabledInSpace } = await getCapabilities(); + expect(upgradeInProgress).toBe(false); + expect(mlFeatureEnabledInSpace).toBe(false); + expect(capabilities.canGetJobs).toBe(false); + expect(capabilities.canCreateJob).toBe(false); + expect(capabilities.canDeleteJob).toBe(false); + expect(capabilities.canOpenJob).toBe(false); + expect(capabilities.canCloseJob).toBe(false); + expect(capabilities.canForecastJob).toBe(false); + expect(capabilities.canGetDatafeeds).toBe(false); + expect(capabilities.canStartStopDatafeed).toBe(false); + expect(capabilities.canUpdateJob).toBe(false); + expect(capabilities.canUpdateDatafeed).toBe(false); + expect(capabilities.canPreviewDatafeed).toBe(false); + expect(capabilities.canGetCalendars).toBe(false); + expect(capabilities.canCreateCalendar).toBe(false); + expect(capabilities.canDeleteCalendar).toBe(false); + expect(capabilities.canGetFilters).toBe(false); + expect(capabilities.canCreateFilter).toBe(false); + expect(capabilities.canDeleteFilter).toBe(false); + expect(capabilities.canFindFileStructure).toBe(false); + expect(capabilities.canGetDataFrameAnalytics).toBe(false); + expect(capabilities.canDeleteDataFrameAnalytics).toBe(false); + expect(capabilities.canCreateDataFrameAnalytics).toBe(false); + expect(capabilities.canStartStopDataFrameAnalytics).toBe(false); + done(); + }); + }); + + test('full capabilities, basic license, ml disabled in space', async done => { + const { getCapabilities } = capabilitiesProvider( + callWithRequestNonUpgrade, + getDefaultCapabilities(), + mlLicenseBasic, + mlIsNotEnabled + ); + const { capabilities, upgradeInProgress, mlFeatureEnabledInSpace } = await getCapabilities(); + expect(upgradeInProgress).toBe(false); + expect(mlFeatureEnabledInSpace).toBe(false); + expect(capabilities.canGetJobs).toBe(false); + expect(capabilities.canCreateJob).toBe(false); + expect(capabilities.canDeleteJob).toBe(false); + expect(capabilities.canOpenJob).toBe(false); + expect(capabilities.canCloseJob).toBe(false); + expect(capabilities.canForecastJob).toBe(false); + expect(capabilities.canGetDatafeeds).toBe(false); + expect(capabilities.canStartStopDatafeed).toBe(false); + expect(capabilities.canUpdateJob).toBe(false); + expect(capabilities.canUpdateDatafeed).toBe(false); + expect(capabilities.canPreviewDatafeed).toBe(false); + expect(capabilities.canGetCalendars).toBe(false); + expect(capabilities.canCreateCalendar).toBe(false); + expect(capabilities.canDeleteCalendar).toBe(false); + expect(capabilities.canGetFilters).toBe(false); + expect(capabilities.canCreateFilter).toBe(false); + expect(capabilities.canDeleteFilter).toBe(false); + expect(capabilities.canFindFileStructure).toBe(false); + expect(capabilities.canGetDataFrameAnalytics).toBe(false); + expect(capabilities.canDeleteDataFrameAnalytics).toBe(false); + expect(capabilities.canCreateDataFrameAnalytics).toBe(false); + expect(capabilities.canStartStopDataFrameAnalytics).toBe(false); + done(); + }); +}); diff --git a/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.ts b/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.ts new file mode 100644 index 000000000000..a2ad83c5522d --- /dev/null +++ b/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.ts @@ -0,0 +1,47 @@ +/* + * 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 { IScopedClusterClient } from 'kibana/server'; +import { + MlCapabilities, + adminMlCapabilities, + MlCapabilitiesResponse, +} from '../../../common/types/capabilities'; +import { upgradeCheckProvider } from './upgrade'; +import { MlLicense } from '../../../common/license'; + +export function capabilitiesProvider( + callAsCurrentUser: IScopedClusterClient['callAsCurrentUser'], + capabilities: MlCapabilities, + mlLicense: MlLicense, + isMlEnabledInSpace: () => Promise +) { + const { isUpgradeInProgress } = upgradeCheckProvider(callAsCurrentUser); + async function getCapabilities(): Promise { + const upgradeInProgress = await isUpgradeInProgress(); + const isPlatinumOrTrialLicense = mlLicense.isFullLicense(); + const mlFeatureEnabledInSpace = await isMlEnabledInSpace(); + + if (upgradeInProgress === true) { + // if an upgrade is in progress, set all admin capabilities to false + disableAdminPrivileges(capabilities); + } + + return { + capabilities, + upgradeInProgress, + isPlatinumOrTrialLicense, + mlFeatureEnabledInSpace, + }; + } + return { getCapabilities }; +} + +function disableAdminPrivileges(capabilities: MlCapabilities) { + Object.keys(adminMlCapabilities).forEach(k => { + capabilities[k as keyof MlCapabilities] = false; + }); +} diff --git a/x-pack/plugins/ml/server/lib/check_privileges/index.ts b/x-pack/plugins/ml/server/lib/capabilities/index.ts similarity index 65% rename from x-pack/plugins/ml/server/lib/check_privileges/index.ts rename to x-pack/plugins/ml/server/lib/capabilities/index.ts index 67b435116aa0..b73c6b87f683 100644 --- a/x-pack/plugins/ml/server/lib/check_privileges/index.ts +++ b/x-pack/plugins/ml/server/lib/capabilities/index.ts @@ -4,4 +4,5 @@ * you may not use this file except in compliance with the Elastic License. */ -export { privilegesProvider, MlCapabilities } from './check_privileges'; +export { capabilitiesProvider } from './check_capabilities'; +export { setupCapabilitiesSwitcher } from './capabilities_switcher'; diff --git a/x-pack/plugins/ml/server/lib/check_privileges/upgrade.ts b/x-pack/plugins/ml/server/lib/capabilities/upgrade.ts similarity index 100% rename from x-pack/plugins/ml/server/lib/check_privileges/upgrade.ts rename to x-pack/plugins/ml/server/lib/capabilities/upgrade.ts diff --git a/x-pack/plugins/ml/server/lib/check_privileges/__mocks__/call_with_request.ts b/x-pack/plugins/ml/server/lib/check_privileges/__mocks__/call_with_request.ts deleted file mode 100644 index ef82003ec80d..000000000000 --- a/x-pack/plugins/ml/server/lib/check_privileges/__mocks__/call_with_request.ts +++ /dev/null @@ -1,154 +0,0 @@ -/* - * 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/xpack/ml/filters/update': true, - 'cluster:admin/xpack/ml/calendars/events/post': true, - 'cluster:admin/xpack/ml/job/close': true, - 'cluster:monitor/xpack/ml/datafeeds/stats/get': true, - 'cluster:admin/xpack/ml/calendars/jobs/update': true, - 'cluster:admin/xpack/ml/calendars/put': 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/xpack/ml/job/put': true, - 'cluster:admin/xpack/ml/job/update': 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/xpack/ml/filters/update': false, - 'cluster:admin/xpack/ml/calendars/events/post': false, - 'cluster:admin/xpack/ml/job/close': false, - 'cluster:monitor/xpack/ml/datafeeds/stats/get': true, - 'cluster:admin/xpack/ml/calendars/jobs/update': false, - 'cluster:admin/xpack/ml/calendars/put': 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/xpack/ml/job/put': false, - 'cluster:admin/xpack/ml/job/update': false, - 'cluster:admin/xpack/ml/calendars/delete': false, - 'cluster:monitor/xpack/ml/findfilestructure': true, -}; diff --git a/x-pack/plugins/ml/server/lib/check_privileges/check_privileges.test.ts b/x-pack/plugins/ml/server/lib/check_privileges/check_privileges.test.ts deleted file mode 100644 index d8435e902625..000000000000 --- a/x-pack/plugins/ml/server/lib/check_privileges/check_privileges.test.ts +++ /dev/null @@ -1,515 +0,0 @@ -/* - * 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'; -import { MlLicense } from '../../../common/license'; - -const mlLicenseWithSecurity = { - isSecurityEnabled: () => true, - isFullLicense: () => true, -} as MlLicense; - -const mlLicenseWithOutSecurity = { - isSecurityEnabled: () => false, - isFullLicense: () => true, -} as MlLicense; - -const mlLicenseWithOutSecurityBasicLicense = { - isSecurityEnabled: () => false, - isFullLicense: () => false, -} as MlLicense; - -const mlLicenseWithSecurityBasicLicense = { - isSecurityEnabled: () => true, - isFullLicense: () => false, -} as MlLicense; - -const mlIsEnabled = async () => true; -const mlIsNotEnabled = async () => false; - -describe('check_privileges', () => { - describe('getPrivileges() - right number of capabilities', () => { - test('es capabilities count', async done => { - const count = mlPrivileges.cluster.length; - expect(count).toBe(27); - done(); - }); - - test('kibana capabilities count', async done => { - const callWithRequest = callWithRequestProvider('partialPrivileges'); - const { getPrivileges } = privilegesProvider( - callWithRequest, - mlLicenseWithSecurity, - mlIsEnabled - ); - const { capabilities } = await getPrivileges(); - const count = Object.keys(capabilities).length; - expect(count).toBe(22); - done(); - }); - }); - - describe('getPrivileges() with security', () => { - test('ml_user capabilities only', async done => { - const callWithRequest = callWithRequestProvider('partialPrivileges'); - const { getPrivileges } = privilegesProvider( - callWithRequest, - mlLicenseWithSecurity, - mlIsEnabled - ); - const { capabilities, upgradeInProgress, mlFeatureEnabledInSpace } = await getPrivileges(); - expect(upgradeInProgress).toBe(false); - expect(mlFeatureEnabledInSpace).toBe(true); - expect(capabilities.canGetJobs).toBe(true); - expect(capabilities.canCreateJob).toBe(false); - expect(capabilities.canDeleteJob).toBe(false); - expect(capabilities.canOpenJob).toBe(false); - expect(capabilities.canCloseJob).toBe(false); - expect(capabilities.canForecastJob).toBe(false); - expect(capabilities.canGetDatafeeds).toBe(true); - expect(capabilities.canStartStopDatafeed).toBe(false); - expect(capabilities.canUpdateJob).toBe(false); - expect(capabilities.canUpdateDatafeed).toBe(false); - expect(capabilities.canPreviewDatafeed).toBe(false); - expect(capabilities.canGetCalendars).toBe(true); - expect(capabilities.canCreateCalendar).toBe(false); - expect(capabilities.canDeleteCalendar).toBe(false); - expect(capabilities.canGetFilters).toBe(false); - expect(capabilities.canCreateFilter).toBe(false); - expect(capabilities.canDeleteFilter).toBe(false); - expect(capabilities.canFindFileStructure).toBe(true); - expect(capabilities.canGetDataFrameAnalytics).toBe(true); - expect(capabilities.canDeleteDataFrameAnalytics).toBe(false); - expect(capabilities.canCreateDataFrameAnalytics).toBe(false); - expect(capabilities.canStartStopDataFrameAnalytics).toBe(false); - done(); - }); - - test('full capabilities', async done => { - const callWithRequest = callWithRequestProvider('fullPrivileges'); - const { getPrivileges } = privilegesProvider( - callWithRequest, - mlLicenseWithSecurity, - mlIsEnabled - ); - const { capabilities, upgradeInProgress, mlFeatureEnabledInSpace } = await getPrivileges(); - expect(upgradeInProgress).toBe(false); - expect(mlFeatureEnabledInSpace).toBe(true); - expect(capabilities.canGetJobs).toBe(true); - expect(capabilities.canCreateJob).toBe(true); - expect(capabilities.canDeleteJob).toBe(true); - expect(capabilities.canOpenJob).toBe(true); - expect(capabilities.canCloseJob).toBe(true); - expect(capabilities.canForecastJob).toBe(true); - expect(capabilities.canGetDatafeeds).toBe(true); - expect(capabilities.canStartStopDatafeed).toBe(true); - expect(capabilities.canUpdateJob).toBe(true); - expect(capabilities.canUpdateDatafeed).toBe(true); - expect(capabilities.canPreviewDatafeed).toBe(true); - expect(capabilities.canGetCalendars).toBe(true); - expect(capabilities.canCreateCalendar).toBe(true); - expect(capabilities.canDeleteCalendar).toBe(true); - expect(capabilities.canGetFilters).toBe(true); - expect(capabilities.canCreateFilter).toBe(true); - expect(capabilities.canDeleteFilter).toBe(true); - expect(capabilities.canFindFileStructure).toBe(true); - expect(capabilities.canGetDataFrameAnalytics).toBe(true); - expect(capabilities.canDeleteDataFrameAnalytics).toBe(true); - expect(capabilities.canCreateDataFrameAnalytics).toBe(true); - expect(capabilities.canStartStopDataFrameAnalytics).toBe(true); - done(); - }); - - test('upgrade in progress with full capabilities', async done => { - const callWithRequest = callWithRequestProvider('upgradeWithFullPrivileges'); - const { getPrivileges } = privilegesProvider( - callWithRequest, - mlLicenseWithSecurity, - mlIsEnabled - ); - const { capabilities, upgradeInProgress, mlFeatureEnabledInSpace } = await getPrivileges(); - expect(upgradeInProgress).toBe(true); - expect(mlFeatureEnabledInSpace).toBe(true); - expect(capabilities.canGetJobs).toBe(true); - expect(capabilities.canCreateJob).toBe(false); - expect(capabilities.canDeleteJob).toBe(false); - expect(capabilities.canOpenJob).toBe(false); - expect(capabilities.canCloseJob).toBe(false); - expect(capabilities.canForecastJob).toBe(false); - expect(capabilities.canGetDatafeeds).toBe(true); - expect(capabilities.canStartStopDatafeed).toBe(false); - expect(capabilities.canUpdateJob).toBe(false); - expect(capabilities.canUpdateDatafeed).toBe(false); - expect(capabilities.canPreviewDatafeed).toBe(false); - expect(capabilities.canGetCalendars).toBe(true); - expect(capabilities.canCreateCalendar).toBe(false); - expect(capabilities.canDeleteCalendar).toBe(false); - expect(capabilities.canGetFilters).toBe(true); - expect(capabilities.canCreateFilter).toBe(false); - expect(capabilities.canDeleteFilter).toBe(false); - expect(capabilities.canFindFileStructure).toBe(true); - expect(capabilities.canGetDataFrameAnalytics).toBe(true); - expect(capabilities.canDeleteDataFrameAnalytics).toBe(false); - expect(capabilities.canCreateDataFrameAnalytics).toBe(false); - expect(capabilities.canStartStopDataFrameAnalytics).toBe(false); - done(); - }); - - test('upgrade in progress with partial capabilities', async done => { - const callWithRequest = callWithRequestProvider('upgradeWithPartialPrivileges'); - const { getPrivileges } = privilegesProvider( - callWithRequest, - mlLicenseWithSecurity, - mlIsEnabled - ); - const { capabilities, upgradeInProgress, mlFeatureEnabledInSpace } = await getPrivileges(); - expect(upgradeInProgress).toBe(true); - expect(mlFeatureEnabledInSpace).toBe(true); - expect(capabilities.canGetJobs).toBe(true); - expect(capabilities.canCreateJob).toBe(false); - expect(capabilities.canDeleteJob).toBe(false); - expect(capabilities.canOpenJob).toBe(false); - expect(capabilities.canCloseJob).toBe(false); - expect(capabilities.canForecastJob).toBe(false); - expect(capabilities.canGetDatafeeds).toBe(true); - expect(capabilities.canStartStopDatafeed).toBe(false); - expect(capabilities.canUpdateJob).toBe(false); - expect(capabilities.canUpdateDatafeed).toBe(false); - expect(capabilities.canPreviewDatafeed).toBe(false); - expect(capabilities.canGetCalendars).toBe(true); - expect(capabilities.canCreateCalendar).toBe(false); - expect(capabilities.canDeleteCalendar).toBe(false); - expect(capabilities.canGetFilters).toBe(false); - expect(capabilities.canCreateFilter).toBe(false); - expect(capabilities.canDeleteFilter).toBe(false); - expect(capabilities.canFindFileStructure).toBe(true); - expect(capabilities.canGetDataFrameAnalytics).toBe(true); - expect(capabilities.canDeleteDataFrameAnalytics).toBe(false); - expect(capabilities.canCreateDataFrameAnalytics).toBe(false); - expect(capabilities.canStartStopDataFrameAnalytics).toBe(false); - done(); - }); - - test('ml_user capabilities with security with basic license', async done => { - const callWithRequest = callWithRequestProvider('partialPrivileges'); - const { getPrivileges } = privilegesProvider( - callWithRequest, - mlLicenseWithSecurityBasicLicense, - mlIsEnabled - ); - const { capabilities, upgradeInProgress, mlFeatureEnabledInSpace } = await getPrivileges(); - expect(upgradeInProgress).toBe(false); - expect(mlFeatureEnabledInSpace).toBe(true); - expect(capabilities.canGetJobs).toBe(false); - expect(capabilities.canCreateJob).toBe(false); - expect(capabilities.canDeleteJob).toBe(false); - expect(capabilities.canOpenJob).toBe(false); - expect(capabilities.canCloseJob).toBe(false); - expect(capabilities.canForecastJob).toBe(false); - expect(capabilities.canGetDatafeeds).toBe(false); - expect(capabilities.canStartStopDatafeed).toBe(false); - expect(capabilities.canUpdateJob).toBe(false); - expect(capabilities.canUpdateDatafeed).toBe(false); - expect(capabilities.canPreviewDatafeed).toBe(false); - expect(capabilities.canGetCalendars).toBe(false); - expect(capabilities.canCreateCalendar).toBe(false); - expect(capabilities.canDeleteCalendar).toBe(false); - expect(capabilities.canGetFilters).toBe(false); - expect(capabilities.canCreateFilter).toBe(false); - expect(capabilities.canDeleteFilter).toBe(false); - expect(capabilities.canFindFileStructure).toBe(true); - expect(capabilities.canGetDataFrameAnalytics).toBe(false); - expect(capabilities.canDeleteDataFrameAnalytics).toBe(false); - expect(capabilities.canCreateDataFrameAnalytics).toBe(false); - expect(capabilities.canStartStopDataFrameAnalytics).toBe(false); - done(); - }); - - test('full user with security with basic license', async done => { - const callWithRequest = callWithRequestProvider('fullPrivileges'); - const { getPrivileges } = privilegesProvider( - callWithRequest, - mlLicenseWithSecurityBasicLicense, - mlIsEnabled - ); - const { capabilities, upgradeInProgress, mlFeatureEnabledInSpace } = await getPrivileges(); - expect(upgradeInProgress).toBe(false); - expect(mlFeatureEnabledInSpace).toBe(true); - expect(capabilities.canGetJobs).toBe(false); - expect(capabilities.canCreateJob).toBe(false); - expect(capabilities.canDeleteJob).toBe(false); - expect(capabilities.canOpenJob).toBe(false); - expect(capabilities.canCloseJob).toBe(false); - expect(capabilities.canForecastJob).toBe(false); - expect(capabilities.canGetDatafeeds).toBe(false); - expect(capabilities.canStartStopDatafeed).toBe(false); - expect(capabilities.canUpdateJob).toBe(false); - expect(capabilities.canUpdateDatafeed).toBe(false); - expect(capabilities.canPreviewDatafeed).toBe(false); - expect(capabilities.canGetCalendars).toBe(false); - expect(capabilities.canCreateCalendar).toBe(false); - expect(capabilities.canDeleteCalendar).toBe(false); - expect(capabilities.canGetFilters).toBe(false); - expect(capabilities.canCreateFilter).toBe(false); - expect(capabilities.canDeleteFilter).toBe(false); - expect(capabilities.canFindFileStructure).toBe(true); - expect(capabilities.canGetDataFrameAnalytics).toBe(false); - expect(capabilities.canDeleteDataFrameAnalytics).toBe(false); - expect(capabilities.canCreateDataFrameAnalytics).toBe(false); - expect(capabilities.canStartStopDataFrameAnalytics).toBe(false); - done(); - }); - - test('full capabilities, ml disabled in space', async done => { - const callWithRequest = callWithRequestProvider('fullPrivileges'); - const { getPrivileges } = privilegesProvider( - callWithRequest, - mlLicenseWithSecurity, - mlIsNotEnabled - ); - const { capabilities, upgradeInProgress, mlFeatureEnabledInSpace } = await getPrivileges(); - expect(upgradeInProgress).toBe(false); - expect(mlFeatureEnabledInSpace).toBe(false); - expect(capabilities.canGetJobs).toBe(false); - expect(capabilities.canCreateJob).toBe(false); - expect(capabilities.canDeleteJob).toBe(false); - expect(capabilities.canOpenJob).toBe(false); - expect(capabilities.canCloseJob).toBe(false); - expect(capabilities.canForecastJob).toBe(false); - expect(capabilities.canGetDatafeeds).toBe(false); - expect(capabilities.canStartStopDatafeed).toBe(false); - expect(capabilities.canUpdateJob).toBe(false); - expect(capabilities.canUpdateDatafeed).toBe(false); - expect(capabilities.canPreviewDatafeed).toBe(false); - expect(capabilities.canGetCalendars).toBe(false); - expect(capabilities.canCreateCalendar).toBe(false); - expect(capabilities.canDeleteCalendar).toBe(false); - expect(capabilities.canGetFilters).toBe(false); - expect(capabilities.canCreateFilter).toBe(false); - expect(capabilities.canDeleteFilter).toBe(false); - expect(capabilities.canFindFileStructure).toBe(false); - expect(capabilities.canGetDataFrameAnalytics).toBe(false); - expect(capabilities.canDeleteDataFrameAnalytics).toBe(false); - expect(capabilities.canCreateDataFrameAnalytics).toBe(false); - expect(capabilities.canStartStopDataFrameAnalytics).toBe(false); - done(); - }); - }); - - describe('getPrivileges() without security', () => { - test('ml_user capabilities only', async done => { - const callWithRequest = callWithRequestProvider('partialPrivileges'); - const { getPrivileges } = privilegesProvider( - callWithRequest, - mlLicenseWithOutSecurity, - mlIsEnabled - ); - const { capabilities, upgradeInProgress, mlFeatureEnabledInSpace } = await getPrivileges(); - expect(upgradeInProgress).toBe(false); - expect(mlFeatureEnabledInSpace).toBe(true); - expect(capabilities.canGetJobs).toBe(true); - expect(capabilities.canCreateJob).toBe(true); - expect(capabilities.canDeleteJob).toBe(true); - expect(capabilities.canOpenJob).toBe(true); - expect(capabilities.canCloseJob).toBe(true); - expect(capabilities.canForecastJob).toBe(true); - expect(capabilities.canGetDatafeeds).toBe(true); - expect(capabilities.canStartStopDatafeed).toBe(true); - expect(capabilities.canUpdateJob).toBe(true); - expect(capabilities.canUpdateDatafeed).toBe(true); - expect(capabilities.canPreviewDatafeed).toBe(true); - expect(capabilities.canGetCalendars).toBe(true); - expect(capabilities.canCreateCalendar).toBe(true); - expect(capabilities.canDeleteCalendar).toBe(true); - expect(capabilities.canGetFilters).toBe(true); - expect(capabilities.canCreateFilter).toBe(true); - expect(capabilities.canDeleteFilter).toBe(true); - expect(capabilities.canFindFileStructure).toBe(true); - expect(capabilities.canGetDataFrameAnalytics).toBe(true); - expect(capabilities.canDeleteDataFrameAnalytics).toBe(true); - expect(capabilities.canCreateDataFrameAnalytics).toBe(true); - expect(capabilities.canStartStopDataFrameAnalytics).toBe(true); - done(); - }); - - test('upgrade in progress with full capabilities', async done => { - const callWithRequest = callWithRequestProvider('upgradeWithFullPrivileges'); - const { getPrivileges } = privilegesProvider( - callWithRequest, - mlLicenseWithOutSecurity, - mlIsEnabled - ); - const { capabilities, upgradeInProgress, mlFeatureEnabledInSpace } = await getPrivileges(); - expect(upgradeInProgress).toBe(true); - expect(mlFeatureEnabledInSpace).toBe(true); - expect(capabilities.canGetJobs).toBe(true); - expect(capabilities.canCreateJob).toBe(false); - expect(capabilities.canDeleteJob).toBe(false); - expect(capabilities.canOpenJob).toBe(false); - expect(capabilities.canCloseJob).toBe(false); - expect(capabilities.canForecastJob).toBe(false); - expect(capabilities.canGetDatafeeds).toBe(true); - expect(capabilities.canStartStopDatafeed).toBe(false); - expect(capabilities.canUpdateJob).toBe(false); - expect(capabilities.canUpdateDatafeed).toBe(false); - expect(capabilities.canPreviewDatafeed).toBe(false); - expect(capabilities.canGetCalendars).toBe(true); - expect(capabilities.canCreateCalendar).toBe(false); - expect(capabilities.canDeleteCalendar).toBe(false); - expect(capabilities.canGetFilters).toBe(true); - expect(capabilities.canCreateFilter).toBe(false); - expect(capabilities.canDeleteFilter).toBe(false); - expect(capabilities.canFindFileStructure).toBe(true); - expect(capabilities.canGetDataFrameAnalytics).toBe(true); - expect(capabilities.canDeleteDataFrameAnalytics).toBe(false); - expect(capabilities.canCreateDataFrameAnalytics).toBe(false); - expect(capabilities.canStartStopDataFrameAnalytics).toBe(false); - done(); - }); - - test('upgrade in progress with partial capabilities', async done => { - const callWithRequest = callWithRequestProvider('upgradeWithPartialPrivileges'); - const { getPrivileges } = privilegesProvider( - callWithRequest, - mlLicenseWithOutSecurity, - mlIsEnabled - ); - const { capabilities, upgradeInProgress, mlFeatureEnabledInSpace } = await getPrivileges(); - expect(upgradeInProgress).toBe(true); - expect(mlFeatureEnabledInSpace).toBe(true); - expect(capabilities.canGetJobs).toBe(true); - expect(capabilities.canCreateJob).toBe(false); - expect(capabilities.canDeleteJob).toBe(false); - expect(capabilities.canOpenJob).toBe(false); - expect(capabilities.canCloseJob).toBe(false); - expect(capabilities.canForecastJob).toBe(false); - expect(capabilities.canGetDatafeeds).toBe(true); - expect(capabilities.canStartStopDatafeed).toBe(false); - expect(capabilities.canUpdateJob).toBe(false); - expect(capabilities.canUpdateDatafeed).toBe(false); - expect(capabilities.canPreviewDatafeed).toBe(false); - expect(capabilities.canGetCalendars).toBe(true); - expect(capabilities.canCreateCalendar).toBe(false); - expect(capabilities.canDeleteCalendar).toBe(false); - expect(capabilities.canGetFilters).toBe(true); - expect(capabilities.canCreateFilter).toBe(false); - expect(capabilities.canDeleteFilter).toBe(false); - expect(capabilities.canFindFileStructure).toBe(true); - expect(capabilities.canGetDataFrameAnalytics).toBe(true); - expect(capabilities.canDeleteDataFrameAnalytics).toBe(false); - expect(capabilities.canCreateDataFrameAnalytics).toBe(false); - expect(capabilities.canStartStopDataFrameAnalytics).toBe(false); - done(); - }); - - test('ml_user capabilities without security with basic license', async done => { - const callWithRequest = callWithRequestProvider('partialPrivileges'); - const { getPrivileges } = privilegesProvider( - callWithRequest, - mlLicenseWithOutSecurityBasicLicense, - mlIsEnabled - ); - const { capabilities, upgradeInProgress, mlFeatureEnabledInSpace } = await getPrivileges(); - expect(upgradeInProgress).toBe(false); - expect(mlFeatureEnabledInSpace).toBe(true); - expect(capabilities.canGetJobs).toBe(false); - expect(capabilities.canCreateJob).toBe(false); - expect(capabilities.canDeleteJob).toBe(false); - expect(capabilities.canOpenJob).toBe(false); - expect(capabilities.canCloseJob).toBe(false); - expect(capabilities.canForecastJob).toBe(false); - expect(capabilities.canGetDatafeeds).toBe(false); - expect(capabilities.canStartStopDatafeed).toBe(false); - expect(capabilities.canUpdateJob).toBe(false); - expect(capabilities.canUpdateDatafeed).toBe(false); - expect(capabilities.canPreviewDatafeed).toBe(false); - expect(capabilities.canGetCalendars).toBe(false); - expect(capabilities.canCreateCalendar).toBe(false); - expect(capabilities.canDeleteCalendar).toBe(false); - expect(capabilities.canGetFilters).toBe(false); - expect(capabilities.canCreateFilter).toBe(false); - expect(capabilities.canDeleteFilter).toBe(false); - expect(capabilities.canFindFileStructure).toBe(true); - expect(capabilities.canGetDataFrameAnalytics).toBe(false); - expect(capabilities.canDeleteDataFrameAnalytics).toBe(false); - expect(capabilities.canCreateDataFrameAnalytics).toBe(false); - expect(capabilities.canStartStopDataFrameAnalytics).toBe(false); - done(); - }); - - test('full user without security with basic license', async done => { - const callWithRequest = callWithRequestProvider('fullPrivileges'); - const { getPrivileges } = privilegesProvider( - callWithRequest, - mlLicenseWithOutSecurityBasicLicense, - mlIsEnabled - ); - const { capabilities, upgradeInProgress, mlFeatureEnabledInSpace } = await getPrivileges(); - expect(upgradeInProgress).toBe(false); - expect(mlFeatureEnabledInSpace).toBe(true); - expect(capabilities.canGetJobs).toBe(false); - expect(capabilities.canCreateJob).toBe(false); - expect(capabilities.canDeleteJob).toBe(false); - expect(capabilities.canOpenJob).toBe(false); - expect(capabilities.canCloseJob).toBe(false); - expect(capabilities.canForecastJob).toBe(false); - expect(capabilities.canGetDatafeeds).toBe(false); - expect(capabilities.canStartStopDatafeed).toBe(false); - expect(capabilities.canUpdateJob).toBe(false); - expect(capabilities.canUpdateDatafeed).toBe(false); - expect(capabilities.canPreviewDatafeed).toBe(false); - expect(capabilities.canGetCalendars).toBe(false); - expect(capabilities.canCreateCalendar).toBe(false); - expect(capabilities.canDeleteCalendar).toBe(false); - expect(capabilities.canGetFilters).toBe(false); - expect(capabilities.canCreateFilter).toBe(false); - expect(capabilities.canDeleteFilter).toBe(false); - expect(capabilities.canFindFileStructure).toBe(true); - expect(capabilities.canGetDataFrameAnalytics).toBe(false); - expect(capabilities.canDeleteDataFrameAnalytics).toBe(false); - expect(capabilities.canCreateDataFrameAnalytics).toBe(false); - expect(capabilities.canStartStopDataFrameAnalytics).toBe(false); - done(); - }); - - test('ml_user capabilities only, ml disabled in space', async done => { - const callWithRequest = callWithRequestProvider('partialPrivileges'); - const { getPrivileges } = privilegesProvider( - callWithRequest, - mlLicenseWithOutSecurity, - mlIsNotEnabled - ); - const { capabilities, upgradeInProgress, mlFeatureEnabledInSpace } = await getPrivileges(); - expect(upgradeInProgress).toBe(false); - expect(mlFeatureEnabledInSpace).toBe(false); - expect(capabilities.canGetJobs).toBe(false); - expect(capabilities.canCreateJob).toBe(false); - expect(capabilities.canDeleteJob).toBe(false); - expect(capabilities.canOpenJob).toBe(false); - expect(capabilities.canCloseJob).toBe(false); - expect(capabilities.canForecastJob).toBe(false); - expect(capabilities.canGetDatafeeds).toBe(false); - expect(capabilities.canStartStopDatafeed).toBe(false); - expect(capabilities.canUpdateJob).toBe(false); - expect(capabilities.canUpdateDatafeed).toBe(false); - expect(capabilities.canPreviewDatafeed).toBe(false); - expect(capabilities.canGetCalendars).toBe(false); - expect(capabilities.canCreateCalendar).toBe(false); - expect(capabilities.canDeleteCalendar).toBe(false); - expect(capabilities.canGetFilters).toBe(false); - expect(capabilities.canCreateFilter).toBe(false); - expect(capabilities.canDeleteFilter).toBe(false); - expect(capabilities.canFindFileStructure).toBe(false); - expect(capabilities.canGetDataFrameAnalytics).toBe(false); - expect(capabilities.canDeleteDataFrameAnalytics).toBe(false); - expect(capabilities.canCreateDataFrameAnalytics).toBe(false); - expect(capabilities.canStartStopDataFrameAnalytics).toBe(false); - done(); - }); - }); -}); diff --git a/x-pack/plugins/ml/server/lib/check_privileges/check_privileges.ts b/x-pack/plugins/ml/server/lib/check_privileges/check_privileges.ts deleted file mode 100644 index df61ad0111a0..000000000000 --- a/x-pack/plugins/ml/server/lib/check_privileges/check_privileges.ts +++ /dev/null @@ -1,269 +0,0 @@ -/* - * 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 { IScopedClusterClient } from 'kibana/server'; -import { Privileges, getDefaultPrivileges } from '../../../common/types/privileges'; -import { upgradeCheckProvider } from './upgrade'; -import { MlLicense } from '../../../common/license'; - -import { mlPrivileges } from './privileges'; - -type ClusterPrivilege = Record; - -export interface MlCapabilities { - capabilities: Privileges; - upgradeInProgress: boolean; - isPlatinumOrTrialLicense: boolean; - mlFeatureEnabledInSpace: boolean; -} - -export function privilegesProvider( - callAsCurrentUser: IScopedClusterClient['callAsCurrentUser'], - mlLicense: MlLicense, - isMlEnabledInSpace: () => Promise, - ignoreSpaces: boolean = false -) { - const { isUpgradeInProgress } = upgradeCheckProvider(callAsCurrentUser); - async function getPrivileges(): Promise { - // get the default privileges, forced to be false. - const privileges = getDefaultPrivileges(); - - const upgradeInProgress = await isUpgradeInProgress(); - const isSecurityEnabled = mlLicense.isSecurityEnabled(); - - const isPlatinumOrTrialLicense = mlLicense.isFullLicense(); - const mlFeatureEnabledInSpace = await isMlEnabledInSpace(); - - const setGettingPrivileges = isPlatinumOrTrialLicense - ? setFullGettingPrivileges - : setBasicGettingPrivileges; - - const setActionPrivileges = isPlatinumOrTrialLicense - ? setFullActionPrivileges - : setBasicActionPrivileges; - - if (mlFeatureEnabledInSpace === false && ignoreSpaces === false) { - // if ML isn't enabled in the current space, - // return with the default privileges (all false) - return { - capabilities: privileges, - upgradeInProgress, - isPlatinumOrTrialLicense, - mlFeatureEnabledInSpace, - }; - } - - if (isSecurityEnabled === false) { - 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 - setGettingPrivileges({}, privileges, true); - setActionPrivileges({}, privileges, true); - } - } else { - // security enabled - // load all ml privileges for this user. - const { cluster } = await callAsCurrentUser('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 { - capabilities: privileges, - upgradeInProgress, - isPlatinumOrTrialLicense, - mlFeatureEnabledInSpace, - }; - } - return { getPrivileges }; -} - -function setFullGettingPrivileges( - 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 Frame Analytics - if ( - forceTrue || - (cluster['cluster:monitor/xpack/ml/job/get'] && - cluster['cluster:monitor/xpack/ml/job/stats/get']) - ) { - privileges.canGetDataFrameAnalytics = true; - } -} - -function setFullActionPrivileges( - cluster: ClusterPrivilege = {}, - privileges: Privileges, - forceTrue = false -) { - // Anomaly Detection - if ( - forceTrue || - (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 (forceTrue || cluster['cluster:admin/xpack/ml/job/update']) { - privileges.canUpdateJob = true; - } - - if (forceTrue || cluster['cluster:admin/xpack/ml/job/open']) { - privileges.canOpenJob = true; - } - - if (forceTrue || cluster['cluster:admin/xpack/ml/job/close']) { - privileges.canCloseJob = true; - } - - if (forceTrue || cluster['cluster:admin/xpack/ml/job/forecast']) { - privileges.canForecastJob = true; - } - - if ( - forceTrue || - (cluster['cluster:admin/xpack/ml/job/delete'] && - cluster['cluster:admin/xpack/ml/datafeeds/delete']) - ) { - privileges.canDeleteJob = true; - } - - if ( - forceTrue || - (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 (forceTrue || cluster['cluster:admin/xpack/ml/datafeeds/update']) { - privileges.canUpdateDatafeed = true; - } - - if (forceTrue || cluster['cluster:admin/xpack/ml/datafeeds/preview']) { - privileges.canPreviewDatafeed = true; - } - - // Calendars - if ( - forceTrue || - (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 ( - forceTrue || - (cluster['cluster:admin/xpack/ml/calendars/delete'] && - cluster['cluster:admin/xpack/ml/calendars/events/delete']) - ) { - privileges.canDeleteCalendar = true; - } - - // Filters - if ( - forceTrue || - (cluster['cluster:admin/xpack/ml/filters/put'] && - cluster['cluster:admin/xpack/ml/filters/update']) - ) { - privileges.canCreateFilter = true; - } - - if (forceTrue || cluster['cluster:admin/xpack/ml/filters/delete']) { - privileges.canDeleteFilter = true; - } - - // Data Frame Analytics - if ( - forceTrue || - (cluster['cluster:admin/xpack/ml/job/put'] && - cluster['cluster:admin/xpack/ml/job/open'] && - cluster['cluster:admin/xpack/ml/datafeeds/put']) - ) { - privileges.canCreateDataFrameAnalytics = true; - } - - if ( - forceTrue || - (cluster['cluster:admin/xpack/ml/job/delete'] && - cluster['cluster:admin/xpack/ml/datafeeds/delete']) - ) { - privileges.canDeleteDataFrameAnalytics = true; - } - - if ( - forceTrue || - (cluster['cluster:admin/xpack/ml/job/open'] && - cluster['cluster:admin/xpack/ml/datafeeds/start'] && - cluster['cluster:admin/xpack/ml/datafeeds/stop']) - ) { - privileges.canStartStopDataFrameAnalytics = true; - } -} - -function setBasicGettingPrivileges( - cluster: ClusterPrivilege = {}, - privileges: Privileges, - forceTrue = false -) { - // File Data Visualizer - if (forceTrue || cluster['cluster:monitor/xpack/ml/findfilestructure']) { - privileges.canFindFileStructure = true; - } -} - -function setBasicActionPrivileges( - cluster: ClusterPrivilege = {}, - privileges: Privileges, - forceTrue = false -) {} diff --git a/x-pack/plugins/ml/server/lib/check_privileges/privileges.ts b/x-pack/plugins/ml/server/lib/check_privileges/privileges.ts deleted file mode 100644 index 9fcd28dd6810..000000000000 --- a/x-pack/plugins/ml/server/lib/check_privileges/privileges.ts +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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/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/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/plugins/ml/server/plugin.ts b/x-pack/plugins/ml/server/plugin.ts index c7add12be142..67e80a3bc44c 100644 --- a/x-pack/plugins/ml/server/plugin.ts +++ b/x-pack/plugins/ml/server/plugin.ts @@ -7,14 +7,18 @@ import { i18n } from '@kbn/i18n'; import { CoreSetup, + CoreStart, Plugin, IScopedClusterClient, + KibanaRequest, Logger, PluginInitializerContext, ICustomClusterClient, + CapabilitiesStart, } from 'kibana/server'; import { PluginsSetup, RouteInitialization } from './types'; import { PLUGIN_ID, PLUGIN_ICON } from '../common/constants/app'; +import { MlCapabilities } from '../common/types/capabilities'; import { elasticsearchJsPlugin } from './client/elasticsearch_ml'; import { initMlTelemetry } from './lib/telemetry'; @@ -41,6 +45,8 @@ import { systemRoutes } from './routes/system'; import { MlLicense } from '../common/license'; import { MlServerLicense } from './lib/license'; import { createSharedServices, SharedServices } from './shared_services'; +import { userMlCapabilities, adminMlCapabilities } from '../common/types/capabilities'; +import { setupCapabilitiesSwitcher } from './lib/capabilities'; declare module 'kibana/server' { interface RequestHandlerContext { @@ -59,6 +65,7 @@ export class MlServerPlugin implements Plugin initSampleDataSets(mlLicense, plugins), ]); + // initialize capabilities switcher to add license filter to ml capabilities + setupCapabilitiesSwitcher(coreSetup, plugins.licensing.license$, this.log); + // Can access via router's handler function 'context' parameter - context.ml.mlClient const mlClient = coreSetup.elasticsearch.createClient(PLUGIN_ID, { plugins: [elasticsearchJsPlugin], @@ -132,6 +145,14 @@ export class MlServerPlugin implements Plugin { + if (this.capabilities === null) { + return null; + } + const capabilities = await this.capabilities.resolveCapabilities(request); + return capabilities.ml as MlCapabilities; + }; + annotationRoutes(routeInit, plugins.security); calendars(routeInit); dataFeedRoutes(routeInit); @@ -144,24 +165,27 @@ export class MlServerPlugin implements Plugin { try { - const ignoreSpaces = request.query && request.query.ignoreSpaces === 'true'; // if spaces is disabled force isMlEnabledInSpace to be true const { isMlEnabledInSpace } = spaces !== undefined ? spacesUtilsProvider(spaces, (request as unknown) as Request) : { isMlEnabledInSpace: async () => true }; - const { getPrivileges } = privilegesProvider( + const mlCapabilities = await resolveMlCapabilities(request); + if (mlCapabilities === null) { + return response.customError(wrapError(new Error('resolveMlCapabilities is not defined'))); + } + + const { getCapabilities } = capabilitiesProvider( context.ml!.mlClient.callAsCurrentUser, + mlCapabilities, mlLicense, - isMlEnabledInSpace, - ignoreSpaces + isMlEnabledInSpace ); return response.ok({ - body: await getPrivileges(), + body: await getCapabilities(), }); } catch (error) { return response.customError(wrapError(error)); diff --git a/x-pack/plugins/ml/server/shared_services/providers/system.ts b/x-pack/plugins/ml/server/shared_services/providers/system.ts index cedf18e80906..698ac8e6261e 100644 --- a/x-pack/plugins/ml/server/shared_services/providers/system.ts +++ b/x-pack/plugins/ml/server/shared_services/providers/system.ts @@ -4,23 +4,24 @@ * you may not use this file except in compliance with the Elastic License. */ -import { APICaller } from 'kibana/server'; +import { APICaller, KibanaRequest } from 'kibana/server'; import { SearchResponse, SearchParams } from 'elasticsearch'; import { MlServerLicense } from '../../lib/license'; import { CloudSetup } from '../../../../cloud/server'; import { LicenseCheck } from '../license_checks'; -import { spacesUtilsProvider, RequestFacade } from '../../lib/spaces_utils'; +import { spacesUtilsProvider } from '../../lib/spaces_utils'; import { SpacesPluginSetup } from '../../../../spaces/server'; -import { privilegesProvider, MlCapabilities } from '../../lib/check_privileges'; +import { capabilitiesProvider } from '../../lib/capabilities'; import { MlInfoResponse } from '../../../common/types/ml_server_info'; import { ML_RESULTS_INDEX_PATTERN } from '../../../common/constants/index_patterns'; +import { MlCapabilitiesResponse, ResolveMlCapabilities } from '../../../common/types/capabilities'; export interface MlSystemProvider { mlSystemProvider( callAsCurrentUser: APICaller, - request: RequestFacade + request: KibanaRequest ): { - mlCapabilities(ignoreSpaces?: boolean): Promise; + mlCapabilities(): Promise; mlInfo(): Promise; mlSearch(searchParams: SearchParams): Promise>; }; @@ -31,12 +32,13 @@ export function getMlSystemProvider( isFullLicense: LicenseCheck, mlLicense: MlServerLicense, spaces: SpacesPluginSetup | undefined, - cloud: CloudSetup | undefined + cloud: CloudSetup | undefined, + resolveMlCapabilities: ResolveMlCapabilities ): MlSystemProvider { return { - mlSystemProvider(callAsCurrentUser: APICaller, request: RequestFacade) { + mlSystemProvider(callAsCurrentUser: APICaller, request: KibanaRequest) { return { - mlCapabilities(ignoreSpaces?: boolean) { + async mlCapabilities() { isMinimumLicense(); const { isMlEnabledInSpace } = @@ -44,13 +46,18 @@ export function getMlSystemProvider( ? spacesUtilsProvider(spaces, request) : { isMlEnabledInSpace: async () => true }; - const { getPrivileges } = privilegesProvider( + const mlCapabilities = await resolveMlCapabilities(request); + if (mlCapabilities === null) { + throw new Error('resolveMlCapabilities is not defined'); + } + + const { getCapabilities } = capabilitiesProvider( callAsCurrentUser, + mlCapabilities, mlLicense, - isMlEnabledInSpace, - ignoreSpaces + isMlEnabledInSpace ); - return getPrivileges(); + return getCapabilities(); }, async mlInfo(): Promise { isMinimumLicense(); diff --git a/x-pack/plugins/ml/server/shared_services/shared_services.ts b/x-pack/plugins/ml/server/shared_services/shared_services.ts index f08eb5c23b27..f2d20a72444b 100644 --- a/x-pack/plugins/ml/server/shared_services/shared_services.ts +++ b/x-pack/plugins/ml/server/shared_services/shared_services.ts @@ -17,6 +17,7 @@ import { AnomalyDetectorsProvider, getAnomalyDetectorsProvider, } from './providers/anomaly_detectors'; +import { ResolveMlCapabilities } from '../../common/types/capabilities'; export type SharedServices = JobServiceProvider & AnomalyDetectorsProvider & @@ -27,14 +28,22 @@ export type SharedServices = JobServiceProvider & export function createSharedServices( mlLicense: MlServerLicense, spaces: SpacesPluginSetup | undefined, - cloud: CloudSetup + cloud: CloudSetup, + resolveMlCapabilities: ResolveMlCapabilities ): SharedServices { const { isFullLicense, isMinimumLicense } = licenseChecks(mlLicense); return { ...getJobServiceProvider(isFullLicense), ...getAnomalyDetectorsProvider(isFullLicense), - ...getMlSystemProvider(isMinimumLicense, isFullLicense, mlLicense, spaces, cloud), + ...getMlSystemProvider( + isMinimumLicense, + isFullLicense, + mlLicense, + spaces, + cloud, + resolveMlCapabilities + ), ...getModulesProvider(isFullLicense), ...getResultsServiceProvider(isFullLicense), }; diff --git a/x-pack/plugins/ml/server/types.ts b/x-pack/plugins/ml/server/types.ts index ff4d07bd79e4..d4cd61a7fa4f 100644 --- a/x-pack/plugins/ml/server/types.ts +++ b/x-pack/plugins/ml/server/types.ts @@ -13,6 +13,7 @@ import { PluginSetupContract as FeaturesPluginSetup } from '../../features/serve import { LicensingPluginSetup } from '../../licensing/server'; import { SpacesPluginSetup } from '../../spaces/server'; import { MlServerLicense } from './lib/license'; +import { ResolveMlCapabilities } from '../common/types/capabilities'; export interface LicenseCheckResult { isAvailable: boolean; @@ -26,6 +27,11 @@ export interface LicenseCheckResult { export interface SystemRouteDeps { cloud: CloudSetup; spaces?: SpacesPluginSetup; + resolveMlCapabilities: ResolveMlCapabilities; +} + +export interface JobServiceRouteDeps { + resolveMlCapabilities: ResolveMlCapabilities; } export interface PluginsSetup {