Skip to content

Commit

Permalink
[ML] Fix access denied for data frames. (#37178)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
walterra committed May 28, 2019
1 parent bc37f86 commit 4095908
Show file tree
Hide file tree
Showing 6 changed files with 198 additions and 1 deletion.
2 changes: 2 additions & 0 deletions x-pack/plugins/ml/public/data_frame/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
Original file line number Diff line number Diff line change
@@ -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<string>('kbnBaseUrl');
const kbnUrl = $injector.get<any>('kbnUrl');

const goToKibana = () => {
window.location.href = uiChrome.getBasePath() + kbnBaseUrl;
};

const retry = () => {
kbnUrl.redirect('/data_frames');
};

const props = { goToKibana, retry };

ReactDOM.render(<I18nContext>{React.createElement(Page, props)}</I18nContext>, element[0]);

element.on('$destroy', () => {
ReactDOM.unmountComponentAtNode(element[0]);
scope.$destroy();
});
},
};
});
Original file line number Diff line number Diff line change
@@ -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 <Page />', () => {
test('Minimal initialization', () => {
const props = {
goToKibana: jest.fn(),
retry: jest.fn(),
};

const tree = (
<I18nProvider>
<Page {...props} />
</I18nProvider>
);

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);
});
});
93 changes: 93 additions & 0 deletions x-pack/plugins/ml/public/data_frame/pages/access_denied/page.tsx
Original file line number Diff line number Diff line change
@@ -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<PageProps> = ({ goToKibana, retry }) => (
<EuiPage>
<EuiPageBody>
<EuiPageContentHeader>
<EuiPageContentHeaderSection>
<EuiTitle>
<h1>
<FormattedMessage
id="xpack.ml.dataframe.accessDeniedTitle"
defaultMessage="Access denied"
/>
</h1>
</EuiTitle>
</EuiPageContentHeaderSection>
</EuiPageContentHeader>
<EuiPageContentBody>
<EuiSpacer size="m" />
<EuiCallOut
title={i18n.translate('xpack.ml.dataframe.noPermissionToAccessMLLabel', {
defaultMessage: 'You need permission to access Data Frames',
})}
color="danger"
iconType="cross"
>
<EuiText size="s">
<p>
<FormattedMessage
id="xpack.ml.dataframe.noGrantedPrivilegesDescription"
defaultMessage="You must have the privileges granted in the {kibanaUserParam} and {dataFrameUserParam} roles.{br}Your system admin can set these roles on the Management User page."
values={{
kibanaUserParam: <span className="text-monospace">kibana_user</span>,
dataFrameUserParam: (
<span className="text-monospace">data_frame_transforms_user</span>
),
br: <br />,
}}
/>
</p>
</EuiText>
</EuiCallOut>
<EuiSpacer size="m" />
<EuiFlexGroup gutterSize="s" alignItems="center">
<EuiFlexItem grow={false}>
<EuiButton fill onClick={goToKibana} size="s">
<FormattedMessage
id="xpack.ml.dataframe.accessDenied.backToKibanaHomeButtonLabel"
defaultMessage="Back to Kibana home"
/>
</EuiButton>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton fill onClick={retry} size="s">
<FormattedMessage
id="xpack.ml.dataframe.accessDenied.retryButtonLabel"
defaultMessage="Retry"
/>
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPageContentBody>
</EuiPageBody>
</EuiPage>
);
17 changes: 17 additions & 0 deletions x-pack/plugins/ml/public/data_frame/pages/access_denied/route.ts
Original file line number Diff line number Diff line change
@@ -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 = `<ml-nav-menu name="access-denied" /><ml-data-frame-access-denied />`;

uiRoutes.when('/data_frames/access-denied', {
template,
k7Breadcrumbs: getDataFrameBreadcrumbs,
});
2 changes: 1 addition & 1 deletion x-pack/plugins/ml/public/privilege/check_privilege.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export function checkGetDataFrameJobsPrivilege(kbnUrl: any): Promise<Privileges>
if (privileges.canGetDataFrameJobs) {
return resolve(privileges);
} else {
kbnUrl.redirect('/access-denied');
kbnUrl.redirect('/data_frames/access-denied');
return reject();
}
});
Expand Down

0 comments on commit 4095908

Please sign in to comment.