From 5149047d55e105eef5b97eef7ce8b42967661eaa Mon Sep 17 00:00:00 2001 From: Walter Rafelsberger Date: Tue, 28 May 2019 17:09:12 +0200 Subject: [PATCH] [ML] Fix access denied for data frames. (#37178) With insufficient privileges, a user would be redirected to the ML plugin's access-denied page which mentions the required user roles to access the pages. Since data frames introduces new user roles these messages were not correct. This PR fixes it by redirecting to a specific access-denied page for data frames. To avoid to much refactoring as a fix, the page is a copy and port to React of the original one. In a follow up for 7.3, we should merge the two pages and it should have options to display required user roles given a certain context like anomaly detection or data frames. --- x-pack/plugins/ml/public/data_frame/index.ts | 2 + .../pages/access_denied/directive.tsx | 47 ++++++++++ .../pages/access_denied/page.test.tsx | 38 ++++++++ .../data_frame/pages/access_denied/page.tsx | 93 +++++++++++++++++++ .../data_frame/pages/access_denied/route.ts | 17 ++++ .../ml/public/privilege/check_privilege.ts | 2 +- 6 files changed, 198 insertions(+), 1 deletion(-) create mode 100644 x-pack/plugins/ml/public/data_frame/pages/access_denied/directive.tsx create mode 100644 x-pack/plugins/ml/public/data_frame/pages/access_denied/page.test.tsx create mode 100644 x-pack/plugins/ml/public/data_frame/pages/access_denied/page.tsx create mode 100644 x-pack/plugins/ml/public/data_frame/pages/access_denied/route.ts diff --git a/x-pack/plugins/ml/public/data_frame/index.ts b/x-pack/plugins/ml/public/data_frame/index.ts index fb8c8fadd3eccb..b493d1459655e6 100644 --- a/x-pack/plugins/ml/public/data_frame/index.ts +++ b/x-pack/plugins/ml/public/data_frame/index.ts @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import './pages/access_denied/directive'; +import './pages/access_denied/route'; import './pages/job_management/directive'; import './pages/job_management/route'; import './pages/data_frame_new_pivot/directive'; diff --git a/x-pack/plugins/ml/public/data_frame/pages/access_denied/directive.tsx b/x-pack/plugins/ml/public/data_frame/pages/access_denied/directive.tsx new file mode 100644 index 00000000000000..fd8b3bc480ec58 --- /dev/null +++ b/x-pack/plugins/ml/public/data_frame/pages/access_denied/directive.tsx @@ -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 React from 'react'; +import ReactDOM from 'react-dom'; + +// @ts-ignore +import { uiModules } from 'ui/modules'; +import uiChrome from 'ui/chrome'; + +const module = uiModules.get('apps/ml', ['react']); + +import { I18nContext } from 'ui/i18n'; +import { InjectorService } from '../../../../common/types/angular'; + +import { Page } from './page'; + +module.directive('mlDataFrameAccessDenied', ($injector: InjectorService) => { + return { + scope: {}, + restrict: 'E', + link: (scope: ng.IScope, element: ng.IAugmentedJQuery) => { + const kbnBaseUrl = $injector.get('kbnBaseUrl'); + const kbnUrl = $injector.get('kbnUrl'); + + const goToKibana = () => { + window.location.href = uiChrome.getBasePath() + kbnBaseUrl; + }; + + const retry = () => { + kbnUrl.redirect('/data_frames'); + }; + + const props = { goToKibana, retry }; + + ReactDOM.render({React.createElement(Page, props)}, element[0]); + + element.on('$destroy', () => { + ReactDOM.unmountComponentAtNode(element[0]); + scope.$destroy(); + }); + }, + }; +}); diff --git a/x-pack/plugins/ml/public/data_frame/pages/access_denied/page.test.tsx b/x-pack/plugins/ml/public/data_frame/pages/access_denied/page.test.tsx new file mode 100644 index 00000000000000..d38cf18b4a78d1 --- /dev/null +++ b/x-pack/plugins/ml/public/data_frame/pages/access_denied/page.test.tsx @@ -0,0 +1,38 @@ +/* + * 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 { render, fireEvent, cleanup } from 'react-testing-library'; + +import { I18nProvider } from '@kbn/i18n/react'; + +import { Page } from './page'; + +afterEach(cleanup); + +describe('Data Frame: Access denied ', () => { + test('Minimal initialization', () => { + const props = { + goToKibana: jest.fn(), + retry: jest.fn(), + }; + + const tree = ( + + + + ); + + const { getByText } = render(tree); + + fireEvent.click(getByText(/Back to Kibana home/i)); + fireEvent.click(getByText(/Retry/i)); + + expect(props.goToKibana).toHaveBeenCalledTimes(1); + expect(props.retry).toHaveBeenCalledTimes(1); + }); +}); diff --git a/x-pack/plugins/ml/public/data_frame/pages/access_denied/page.tsx b/x-pack/plugins/ml/public/data_frame/pages/access_denied/page.tsx new file mode 100644 index 00000000000000..fa41b5490b7cd7 --- /dev/null +++ b/x-pack/plugins/ml/public/data_frame/pages/access_denied/page.tsx @@ -0,0 +1,93 @@ +/* + * 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, { SFC } from 'react'; + +import { FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; + +import { + EuiButton, + EuiCallOut, + EuiFlexGroup, + EuiFlexItem, + EuiPage, + EuiPageBody, + EuiPageContentBody, + EuiPageContentHeader, + EuiPageContentHeaderSection, + EuiSpacer, + EuiText, + EuiTitle, +} from '@elastic/eui'; + +interface PageProps { + goToKibana: () => void; + retry: () => void; +} +export const Page: SFC = ({ goToKibana, retry }) => ( + + + + + +

+ +

+
+
+
+ + + + +

+ kibana_user, + dataFrameUserParam: ( + data_frame_transforms_user + ), + br:
, + }} + /> +

+
+
+ + + + + + + + + + + + + +
+
+
+); diff --git a/x-pack/plugins/ml/public/data_frame/pages/access_denied/route.ts b/x-pack/plugins/ml/public/data_frame/pages/access_denied/route.ts new file mode 100644 index 00000000000000..63689b4ec551e1 --- /dev/null +++ b/x-pack/plugins/ml/public/data_frame/pages/access_denied/route.ts @@ -0,0 +1,17 @@ +/* + * 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 uiRoutes from 'ui/routes'; + +// @ts-ignore +import { getDataFrameBreadcrumbs } from '../../breadcrumbs'; + +const template = ``; + +uiRoutes.when('/data_frames/access-denied', { + template, + k7Breadcrumbs: getDataFrameBreadcrumbs, +}); diff --git a/x-pack/plugins/ml/public/privilege/check_privilege.ts b/x-pack/plugins/ml/public/privilege/check_privilege.ts index be46aebbeb90e2..00a0d8dbeff16a 100644 --- a/x-pack/plugins/ml/public/privilege/check_privilege.ts +++ b/x-pack/plugins/ml/public/privilege/check_privilege.ts @@ -71,7 +71,7 @@ export function checkGetDataFrameJobsPrivilege(kbnUrl: any): Promise if (privileges.canGetDataFrameJobs) { return resolve(privileges); } else { - kbnUrl.redirect('/access-denied'); + kbnUrl.redirect('/data_frames/access-denied'); return reject(); } });