From 7b1e9b4cbdd960297587e28fe1450878567fcfb2 Mon Sep 17 00:00:00 2001 From: "Matteo V." Date: Mon, 19 Aug 2024 16:04:23 +0200 Subject: [PATCH] Fix #10506 Adding possibility to fetch legends images using Bearer token (#10507) --- web/client/components/misc/SecureImage.jsx | 69 +++++++++++++++++++ web/client/plugins/TOC/components/Legend.jsx | 50 +++++++++----- .../TOC/components/__tests__/Legend-test.jsx | 35 +++++----- 3 files changed, 119 insertions(+), 35 deletions(-) create mode 100644 web/client/components/misc/SecureImage.jsx diff --git a/web/client/components/misc/SecureImage.jsx b/web/client/components/misc/SecureImage.jsx new file mode 100644 index 0000000000..dc6bc3810d --- /dev/null +++ b/web/client/components/misc/SecureImage.jsx @@ -0,0 +1,69 @@ +/** + * Copyright 2024, GeoSolutions Sas. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React, { useEffect, useState } from 'react'; +import axios from 'axios'; + +import { getAuthenticationMethod } from '../../utils/SecurityUtils'; + + +const SecureImage = ({ + alt, + src, + ...props +}) => { + const [imageSrc, setImageSrc] = useState(''); + + // Function to validate the image once it loads + const validateImg = (imgElement) => { + // Implement your validation logic here + // For example, check image dimensions, aspect ratio, etc. + if (imgElement.naturalWidth === 0 || imgElement.naturalHeight === 0) { + console.error('Image validation failed: Image is not valid.'); + } + }; + + useEffect(() => { + const authMethod = getAuthenticationMethod(src); + + if (authMethod === "bearer") { + axios.get(src, { + responseType: 'blob' + }) + .then((response) => { + const imageUrl = URL.createObjectURL(response.data); + setImageSrc(imageUrl); + }) + .catch((error) => { + console.error('Error fetching image:', error); + }); + } else { + setImageSrc(src); + } + + // Clean up the URL object when the component unmounts + return () => { + if (imageSrc) { + URL.revokeObjectURL(imageSrc); + } + }; + }, [src]); + + return ( + {alt} validateImg(e.target)} + src={imageSrc} + style={props.style} + {...props} + /> + ); +}; + +export default SecureImage; diff --git a/web/client/plugins/TOC/components/Legend.jsx b/web/client/plugins/TOC/components/Legend.jsx index 4e55fb53c9..4274a356b8 100644 --- a/web/client/plugins/TOC/components/Legend.jsx +++ b/web/client/plugins/TOC/components/Legend.jsx @@ -14,11 +14,12 @@ import PropTypes from 'prop-types'; import React from 'react'; import { - addAuthenticationParameter, addAuthenticationToSLD, clearNilValuesForParams } from '../../../utils/SecurityUtils'; import Message from '../../../components/I18N/Message'; +import SecureImage from '../../../components/misc/SecureImage'; + import { randomInt } from '../../../utils/RandomUtils'; /** @@ -85,23 +86,26 @@ class Legend extends React.Component { const cleanParams = clearNilValuesForParams(layer.params); const scale = this.getScale(props); - let query = assign({}, { - service: "WMS", - request: "GetLegendGraphic", - format: "image/png", - height: props.legendHeight, - width: props.legendWidth, - layer: layer.name, - style: layer.style || null, - version: layer.version || "1.3.0", - SLD_VERSION: "1.1.0", - LEGEND_OPTIONS: props.legendOptions - }, layer.legendParams || {}, - props.language && layer.localizedLayerStyles ? {LANGUAGE: props.language} : {}, - addAuthenticationToSLD(cleanParams || {}, props.layer), - cleanParams && cleanParams.SLD_BODY ? {SLD_BODY: cleanParams.SLD_BODY} : {}, - scale !== null ? { SCALE: scale } : {}); - addAuthenticationParameter(url, query); + let query = assign( + {}, + { + service: "WMS", + request: "GetLegendGraphic", + format: "image/png", + height: props.legendHeight, + width: props.legendWidth, + layer: layer.name, + style: layer.style || null, + version: layer.version || "1.3.0", + SLD_VERSION: "1.1.0", + LEGEND_OPTIONS: props.legendOptions + }, + layer.legendParams || {}, + props.language && layer.localizedLayerStyles ? {LANGUAGE: props.language} : {}, + addAuthenticationToSLD(cleanParams || {}, props.layer), + cleanParams && cleanParams.SLD_BODY ? {SLD_BODY: cleanParams.SLD_BODY} : {}, + scale !== null ? { SCALE: scale } : {} + ); return urlUtil.format({ host: urlObj.host, @@ -114,7 +118,15 @@ class Legend extends React.Component { } render() { if (!this.state.error && this.props.layer && this.props.layer.type === "wms" && this.props.layer.url) { - return this.validateImg(e.target)} src={this.getUrl(this.props)} style={this.props.style}/>; + const url = this.getUrl(this.props); + return ( + this.validateImg(e.target)} + src={url} + style={this.props.style} + /> + ); } return ; } diff --git a/web/client/plugins/TOC/components/__tests__/Legend-test.jsx b/web/client/plugins/TOC/components/__tests__/Legend-test.jsx index 999a0db999..48080a7411 100644 --- a/web/client/plugins/TOC/components/__tests__/Legend-test.jsx +++ b/web/client/plugins/TOC/components/__tests__/Legend-test.jsx @@ -11,11 +11,10 @@ import React from 'react'; import ReactDOM from 'react-dom'; import Rx from 'rxjs'; import url from 'url'; +import * as TestUtils from 'react-dom/test-utils'; import Legend from '../Legend'; -import * as TestUtils from 'react-dom/test-utils'; - describe("test the Layer legend", () => { beforeEach((done) => { document.body.innerHTML = '
'; @@ -176,13 +175,15 @@ describe("test the Layer legend", () => { "name": "layer3", "format": "image/png" }; - ReactDOM.render( - , - document.getElementById("container")); + TestUtils.act(() => { + ReactDOM.render( + , + document.getElementById("container")); + }); const legendImage = document.querySelector("img"); expect(legendImage).toBeTruthy(); const { query } = url.parse(legendImage.getAttribute('src'), true); @@ -197,13 +198,15 @@ describe("test the Layer legend", () => { "name": "layer3", "format": "image/png" }; - ReactDOM.render( - , - document.getElementById("container")); + TestUtils.act(() => { + ReactDOM.render( + , + document.getElementById("container")); + }); const legendImage = document.querySelector("img"); expect(legendImage).toBeTruthy(); const { query } = url.parse(legendImage.getAttribute('src'), true);