Skip to content

Commit

Permalink
Respect external URL allow list in TSVB (elastic#114093)
Browse files Browse the repository at this point in the history
* Respect external URL allow list in TSVB

* Remove showExternalUrlErrorModal and onContextMenu handler for table

* Update modal message

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
DianaDerevyankina and kibanamachine committed Oct 19, 2021
1 parent 8284376 commit e66c6d8
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import React from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import {
EuiButton,
EuiModal,
EuiModalBody,
EuiModalFooter,
EuiModalHeader,
EuiModalHeaderTitle,
EuiTextColor,
} from '@elastic/eui';

interface ExternalUrlErrorModalProps {
url: string;
handleClose: () => void;
}

export const ExternalUrlErrorModal = ({ url, handleClose }: ExternalUrlErrorModalProps) => (
<EuiModal onClose={handleClose}>
<EuiModalHeader>
<EuiModalHeaderTitle>
<FormattedMessage
id="visTypeTimeseries.externalUrlErrorModal.headerTitle"
defaultMessage="Access to this external URL is not yet enabled"
/>
</EuiModalHeaderTitle>
</EuiModalHeader>
<EuiModalBody>
<FormattedMessage
id="visTypeTimeseries.externalUrlErrorModal.bodyMessage"
defaultMessage="Configure {externalUrlPolicy} in your {kibanaConfigFileName} to allow access to {url}."
values={{
url: (
<EuiTextColor color="warning" component="span">
{url}
</EuiTextColor>
),
externalUrlPolicy: 'externalUrl.policy',
kibanaConfigFileName: 'kibana.yml',
}}
/>
</EuiModalBody>
<EuiModalFooter>
<EuiButton onClick={handleClose} fill>
<FormattedMessage
id="visTypeTimeseries.externalUrlErrorModal.closeButtonLabel"
defaultMessage="Close"
/>
</EuiButton>
</EuiModalFooter>
</EuiModal>
);
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { createFieldFormatter } from '../../lib/create_field_formatter';
import { isSortable } from './is_sortable';
import { EuiToolTip, EuiIcon } from '@elastic/eui';
import { replaceVars } from '../../lib/replace_vars';
import { ExternalUrlErrorModal } from '../../lib/external_url_error_modal';
import { FIELD_FORMAT_IDS } from '../../../../../../../../plugins/field_formats/common';
import { FormattedMessage } from '@kbn/i18n/react';
import { getFieldFormats, getCoreStart } from '../../../../services';
Expand Down Expand Up @@ -53,12 +54,26 @@ class TableVis extends Component {
const DateFormat = fieldFormatsService.getType(FIELD_FORMAT_IDS.DATE);

this.dateFormatter = new DateFormat({}, this.props.getConfig);

this.state = {
accessDeniedDrilldownUrl: null,
};
}

get visibleSeries() {
return get(this.props, 'model.series', []).filter((series) => !series.hidden);
}

createDrilldownUrlClickHandler = (url) => (event) => {
const validatedUrl = getCoreStart().http.externalUrl.validateUrl(url);
if (validatedUrl) {
this.setState({ accessDeniedDrilldownUrl: null });
} else {
event.preventDefault();
this.setState({ accessDeniedDrilldownUrl: url });
}
};

renderRow = (row) => {
const { model, fieldFormatMap, getConfig } = this.props;

Expand All @@ -74,7 +89,16 @@ class TableVis extends Component {

if (model.drilldown_url) {
const url = replaceVars(model.drilldown_url, {}, { key: row.key });
rowDisplay = <a href={sanitizeUrl(url)}>{rowDisplay}</a>;
const handleDrilldownUrlClick = this.createDrilldownUrlClickHandler(url);
rowDisplay = (
<a
href={sanitizeUrl(url)}
onClick={handleDrilldownUrlClick}
onContextMenu={handleDrilldownUrlClick}
>
{rowDisplay}
</a>
);
}

const columns = row.series
Expand Down Expand Up @@ -213,8 +237,11 @@ class TableVis extends Component {
);
}

closeExternalUrlErrorModal = () => this.setState({ accessDeniedDrilldownUrl: null });

render() {
const { visData, model } = this.props;
const { accessDeniedDrilldownUrl } = this.state;
const header = this.renderHeader();
let rows;

Expand All @@ -239,16 +266,24 @@ class TableVis extends Component {
);
}
return (
<RedirectAppLinks
application={getCoreStart().application}
className="tvbVis"
data-test-subj="tableView"
>
<table className="table">
<thead>{header}</thead>
<tbody>{rows}</tbody>
</table>
</RedirectAppLinks>
<>
<RedirectAppLinks
application={getCoreStart().application}
className="tvbVis"
data-test-subj="tableView"
>
<table className="table">
<thead>{header}</thead>
<tbody>{rows}</tbody>
</table>
</RedirectAppLinks>
{accessDeniedDrilldownUrl && (
<ExternalUrlErrorModal
url={accessDeniedDrilldownUrl}
handleClose={this.closeExternalUrlErrorModal}
/>
)}
</>
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ import { getLastValue } from '../../../../../common/last_value_utils';
import { isBackgroundInverted } from '../../../lib/set_is_reversed';
import { replaceVars } from '../../lib/replace_vars';
import PropTypes from 'prop-types';
import React from 'react';
import React, { useState, useCallback } from 'react';
import { sortBy, first, get } from 'lodash';
import { DATA_FORMATTERS } from '../../../../../common/enums';
import { getOperator, shouldOperate } from '../../../../../common/operators_utils';
import { ExternalUrlErrorModal } from '../../lib/external_url_error_modal';

function sortByDirection(data, direction, fn) {
if (direction === 'desc') {
Expand All @@ -41,6 +42,8 @@ function sortSeries(visData, model) {
}

function TopNVisualization(props) {
const [accessDeniedDrilldownUrl, setAccessDeniedDrilldownUrl] = useState(null);
const coreStart = getCoreStart();
const { backgroundColor, model, visData, fieldFormatMap, getConfig } = props;

const series = sortSeries(visData, model).map((item) => {
Expand Down Expand Up @@ -83,13 +86,27 @@ function TopNVisualization(props) {
if (model.drilldown_url) {
params.onClick = (item) => {
const url = replaceVars(model.drilldown_url, {}, { key: item.label });
getCoreStart().application.navigateToUrl(url);
const validatedUrl = coreStart.http.externalUrl.validateUrl(url);
if (validatedUrl) {
setAccessDeniedDrilldownUrl(null);
coreStart.application.navigateToUrl(url);
} else {
setAccessDeniedDrilldownUrl(url);
}
};
}

const closeExternalUrlErrorModal = useCallback(() => setAccessDeniedDrilldownUrl(null), []);

return (
<div className="tvbVis" style={style}>
<TopN {...params} />
{accessDeniedDrilldownUrl && (
<ExternalUrlErrorModal
url={accessDeniedDrilldownUrl}
handleClose={closeExternalUrlErrorModal}
/>
)}
</div>
);
}
Expand Down

0 comments on commit e66c6d8

Please sign in to comment.