diff --git a/x-pack/plugins/ml/public/components/upgrade/index.js b/x-pack/plugins/ml/public/components/upgrade/index.js new file mode 100644 index 0000000000000..2b248fef39923 --- /dev/null +++ b/x-pack/plugins/ml/public/components/upgrade/index.js @@ -0,0 +1,8 @@ +/* + * 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 { UpgradeWarning } from './upgrade_warning'; diff --git a/x-pack/plugins/ml/public/components/upgrade/upgrade_warning.js b/x-pack/plugins/ml/public/components/upgrade/upgrade_warning.js new file mode 100644 index 0000000000000..30008265bea5f --- /dev/null +++ b/x-pack/plugins/ml/public/components/upgrade/upgrade_warning.js @@ -0,0 +1,48 @@ +/* + * 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 React from 'react'; + +import { + EuiCallOut, + EuiSpacer, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; + +import { isUpgradeInProgress } from '../../services/upgrade_service'; + +export function UpgradeWarning() { + if (isUpgradeInProgress() === true) { + return ( + + )} + color="warning" + iconType="alert" + > +

+ +
+ +

+
+ +
+ ); + } + + return null; +} diff --git a/x-pack/plugins/ml/public/jobs/jobs_list/components/jobs_list_view/jobs_list_view.js b/x-pack/plugins/ml/public/jobs/jobs_list/components/jobs_list_view/jobs_list_view.js index 5f82cd407655c..d0683eef0531f 100644 --- a/x-pack/plugins/ml/public/jobs/jobs_list/components/jobs_list_view/jobs_list_view.js +++ b/x-pack/plugins/ml/public/jobs/jobs_list/components/jobs_list_view/jobs_list_view.js @@ -20,6 +20,7 @@ import { MultiJobActions } from '../multi_job_actions'; import { NewJobButton } from '../new_job_button'; import { JobStatsBar } from '../jobs_stats_bar'; import { NodeAvailableWarning } from '../node_available_warning'; +import { UpgradeWarning } from '../../../../components/upgrade'; import { RefreshJobsListButton } from '../refresh_jobs_list_button'; import { isEqual } from 'lodash'; @@ -390,6 +391,7 @@ export class JobsListView extends Component { />
+
diff --git a/x-pack/plugins/ml/public/privilege/get_privileges.js b/x-pack/plugins/ml/public/privilege/get_privileges.js index 568fc8962d74d..c6f54899b97c3 100644 --- a/x-pack/plugins/ml/public/privilege/get_privileges.js +++ b/x-pack/plugins/ml/public/privilege/get_privileges.js @@ -6,6 +6,7 @@ import { ml } from 'plugins/ml/services/ml_api_service'; +import { setUpgradeInProgress } from '../services/upgrade_service'; export function getPrivileges() { const privileges = { @@ -64,94 +65,20 @@ export function getPrivileges() { ml.checkPrivilege(priv) .then((resp) => { - // if security has been disabled, securityDisabled is returned from the endpoint - // therefore set all privileges to true - if (resp.securityDisabled) { + + if(resp.upgradeInProgress === true) { + setUpgradeInProgress(true); + // only check for getting endpoints + // force all to be true if security is disabled + setGettingPrivileges(resp.cluster, privileges, (resp.securityDisabled === true)); + } + else if (resp.securityDisabled) { + // if security has been disabled, securityDisabled is returned from the endpoint + // therefore set all privileges to true Object.keys(privileges).forEach(k => privileges[k] = true); } else { - if (resp.cluster['cluster:monitor/xpack/ml/job/get'] && - resp.cluster['cluster:monitor/xpack/ml/job/stats/get']) { - privileges.canGetJobs = true; - } - - if (resp.cluster['cluster:monitor/xpack/ml/datafeeds/get'] && - resp.cluster['cluster:monitor/xpack/ml/datafeeds/stats/get']) { - privileges.canGetDatafeeds = true; - } - - if (resp.cluster['cluster:admin/xpack/ml/job/put'] && - resp.cluster['cluster:admin/xpack/ml/job/open'] && - resp.cluster['cluster:admin/xpack/ml/datafeeds/put']) { - privileges.canCreateJob = true; - } - - if (resp.cluster['cluster:admin/xpack/ml/job/update']) { - privileges.canUpdateJob = true; - } - - if (resp.cluster['cluster:admin/xpack/ml/job/open']) { - privileges.canOpenJob = true; - } - - if (resp.cluster['cluster:admin/xpack/ml/job/close']) { - privileges.canCloseJob = true; - } - - if (resp.cluster['cluster:admin/xpack/ml/job/forecast']) { - privileges.canForecastJob = true; - } - - if (resp.cluster['cluster:admin/xpack/ml/job/delete'] && - resp.cluster['cluster:admin/xpack/ml/datafeeds/delete']) { - privileges.canDeleteJob = true; - } - - if (resp.cluster['cluster:admin/xpack/ml/job/open'] && - resp.cluster['cluster:admin/xpack/ml/datafeeds/start'] && - resp.cluster['cluster:admin/xpack/ml/datafeeds/stop']) { - privileges.canStartStopDatafeed = true; - } - - if (resp.cluster['cluster:admin/xpack/ml/datafeeds/update']) { - privileges.canUpdateDatafeed = true; - } - - if (resp.cluster['cluster:admin/xpack/ml/datafeeds/preview']) { - privileges.canPreviewDatafeed = true; - } - - if (resp.cluster['cluster:monitor/xpack/ml/calendars/get']) { - privileges.canGetCalendars = true; - } - - if (resp.cluster['cluster:admin/xpack/ml/calendars/put'] && - resp.cluster['cluster:admin/xpack/ml/calendars/jobs/update'] && - resp.cluster['cluster:admin/xpack/ml/calendars/events/post']) { - privileges.canCreateCalendar = true; - } - - if (resp.cluster['cluster:admin/xpack/ml/calendars/delete'] && - resp.cluster['cluster:admin/xpack/ml/calendars/events/delete']) { - privileges.canDeleteCalendar = true; - } - - if (resp.cluster['cluster:admin/xpack/ml/filters/get']) { - privileges.canGetFilters = true; - } - - if (resp.cluster['cluster:admin/xpack/ml/filters/put'] && - resp.cluster['cluster:admin/xpack/ml/filters/update']) { - privileges.canCreateFilter = true; - } - - if (resp.cluster['cluster:admin/xpack/ml/filters/delete']) { - privileges.canDeleteFilter = true; - } - - if (resp.cluster['cluster:monitor/xpack/ml/findfilestructure']) { - privileges.canFindFileStructure = true; - } - + setGettingPrivileges(resp.cluster, privileges); + setActionPrivileges(resp.cluster, privileges); } resolve(privileges); @@ -162,3 +89,95 @@ export function getPrivileges() { }); } +function setGettingPrivileges(cluster = {}, privileges = {}, forceTrue = false) { + 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; + } + + if (forceTrue || cluster['cluster:monitor/xpack/ml/calendars/get']) { + privileges.canGetCalendars = true; + } + + if (forceTrue || cluster['cluster:admin/xpack/ml/filters/get']) { + privileges.canGetFilters = true; + } + + if (forceTrue || cluster['cluster:monitor/xpack/ml/findfilestructure']) { + privileges.canFindFileStructure = true; + } +} + +function setActionPrivileges(cluster = {}, privileges = {}) { + if (cluster['cluster:admin/xpack/ml/job/put'] && + cluster['cluster:admin/xpack/ml/job/open'] && + cluster['cluster:admin/xpack/ml/datafeeds/put']) { + privileges.canCreateJob = true; + } + + if (cluster['cluster:admin/xpack/ml/job/update']) { + privileges.canUpdateJob = true; + } + + if (cluster['cluster:admin/xpack/ml/job/open']) { + privileges.canOpenJob = true; + } + + if (cluster['cluster:admin/xpack/ml/job/close']) { + privileges.canCloseJob = true; + } + + if (cluster['cluster:admin/xpack/ml/job/forecast']) { + privileges.canForecastJob = true; + } + + if (cluster['cluster:admin/xpack/ml/job/delete'] && + cluster['cluster:admin/xpack/ml/datafeeds/delete']) { + privileges.canDeleteJob = true; + } + + if (cluster['cluster:admin/xpack/ml/job/open'] && + cluster['cluster:admin/xpack/ml/datafeeds/start'] && + cluster['cluster:admin/xpack/ml/datafeeds/stop']) { + privileges.canStartStopDatafeed = true; + } + + if (cluster['cluster:admin/xpack/ml/datafeeds/update']) { + privileges.canUpdateDatafeed = true; + } + + if (cluster['cluster:admin/xpack/ml/datafeeds/preview']) { + privileges.canPreviewDatafeed = true; + } + + if (cluster['cluster:admin/xpack/ml/calendars/put'] && + cluster['cluster:admin/xpack/ml/calendars/jobs/update'] && + cluster['cluster:admin/xpack/ml/calendars/events/post']) { + privileges.canCreateCalendar = true; + } + + if (cluster['cluster:admin/xpack/ml/calendars/delete'] && + cluster['cluster:admin/xpack/ml/calendars/events/delete']) { + privileges.canDeleteCalendar = true; + } + + if (cluster['cluster:admin/xpack/ml/filters/put'] && + cluster['cluster:admin/xpack/ml/filters/update']) { + privileges.canCreateFilter = true; + } + + if (cluster['cluster:admin/xpack/ml/filters/delete']) { + privileges.canDeleteFilter = true; + } +} diff --git a/x-pack/plugins/ml/public/services/upgrade_service.js b/x-pack/plugins/ml/public/services/upgrade_service.js new file mode 100644 index 0000000000000..9087ab854ff7d --- /dev/null +++ b/x-pack/plugins/ml/public/services/upgrade_service.js @@ -0,0 +1,16 @@ +/* + * 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. + */ + + +let upgradeInProgress = false; + +export function setUpgradeInProgress(show) { + upgradeInProgress = show; +} + +export function isUpgradeInProgress() { + return upgradeInProgress; +} diff --git a/x-pack/plugins/ml/server/routes/system.js b/x-pack/plugins/ml/server/routes/system.js index e723defdabf01..1ff37b879e5c5 100644 --- a/x-pack/plugins/ml/server/routes/system.js +++ b/x-pack/plugins/ml/server/routes/system.js @@ -39,20 +39,33 @@ export function systemRoutes(server, commonRouteConfig) { server.route({ method: 'POST', path: '/api/ml/_has_privileges', - handler(request) { + async handler(request) { const callWithRequest = callWithRequestFactory(server, request); - // isSecurityDisabled will return true if it is a basic license - // this will cause the subsequent ml.privilegeCheck to fail. - // therefore, check for a basic license first and report that security - // is disabled because its not available on basic - if (isBasicLicense(server) || isSecurityDisabled(server)) { - // if xpack.security.enabled has been explicitly set to false - // return that security is disabled and don't call the privilegeCheck endpoint - return { securityDisabled: true }; - } else { - const body = request.payload; - return callWithRequest('ml.privilegeCheck', { body }) - .catch(resp => wrapError(resp)); + try { + const info = await callWithRequest('ml.info'); + // if ml indices are currently being migrated, upgrade_mode will be set to true + // pass this back with the privileges to allow for the disabling of UI controls. + const upgradeInProgress = (info.upgrade_mode === true); + + // isSecurityDisabled will return true if it is a basic license + // this will cause the subsequent ml.privilegeCheck to fail. + // therefore, check for a basic license first and report that security + // is disabled because its not available on basic + if (isBasicLicense(server) || isSecurityDisabled(server)) { + // if xpack.security.enabled has been explicitly set to false + // return that security is disabled and don't call the privilegeCheck endpoint + return { + securityDisabled: true, + upgradeInProgress + }; + } else { + const body = request.payload; + const resp = await callWithRequest('ml.privilegeCheck', { body }); + resp.upgradeInProgress = upgradeInProgress; + return resp; + } + } catch (error) { + return wrapError(error); } }, config: {