Skip to content

Commit

Permalink
Load configuration from EMS-metadata in region-maps (#70888)
Browse files Browse the repository at this point in the history
  • Loading branch information
thomasneirynck authored Jul 8, 2020
1 parent 637a0d9 commit 9cf1dec
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,48 @@
"zh-tw": "國家"
}
},
{
"layer_id": "world_countries_with_compromised_attribution",
"created_at": "2017-04-26T17:12:15.978370",
"attribution": [
{
"label": {
"en": "<div onclick='alert(1')>Made with NaturalEarth</div>"
},
"url": {
"en": "http://www.naturalearthdata.com/about/terms-of-use"
}
},
{
"label": {
"en": "Elastic Maps Service"
},
"url": {
"en": "javascript:alert('foobar')"
}
}
],
"formats": [
{
"type": "geojson",
"url": "/files/world_countries_v1.geo.json",
"legacy_default": true
}
],
"fields": [
{
"type": "id",
"id": "iso2",
"label": {
"en": "ISO 3166-1 alpha-2 code"
}
}
],
"legacy_ids": [],
"layer_name": {
"en": "World Countries (compromised)"
}
},
{
"layer_id": "australia_states",
"created_at": "2018-06-27T23:47:32.202380",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
{ "label": { "en": "OpenMapTiles" }, "url": { "en": "https://openmaptiles.org" } },
{ "label": { "en": "MapTiler" }, "url": { "en": "https://www.maptiler.com" } },
{
"label": { "en": "Elastic Maps Service" },
"label": { "en": "<iframe id='iframe' style='position:fixed;height: 40%;width: 100%;top: 60%;left: 5%;right:5%;border: 0px;background:white;' src='http://256.256.256.256'></iframe>" },
"url": { "en": "https://www.elastic.co/elastic-maps-service" }
}
],
Expand Down
66 changes: 46 additions & 20 deletions src/plugins/maps_legacy/public/map/service_settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,28 +89,31 @@ export class ServiceSettings {
};
}

_backfillSettings = (fileLayer) => {
// Older version of Kibana stored EMS state in the URL-params
// Creates object literal with required parameters as key-value pairs
const format = fileLayer.getDefaultFormatType();
const meta = fileLayer.getDefaultFormatMeta();

return {
name: fileLayer.getDisplayName(),
origin: fileLayer.getOrigin(),
id: fileLayer.getId(),
created_at: fileLayer.getCreatedAt(),
attribution: getAttributionString(fileLayer),
fields: fileLayer.getFieldsInLanguage(),
format: format, //legacy: format and meta are split up
meta: meta, //legacy, format and meta are split up
};
};

async getFileLayers() {
if (!this._mapConfig.includeElasticMapsService) {
return [];
}

const fileLayers = await this._emsClient.getFileLayers();
return fileLayers.map((fileLayer) => {
//backfill to older settings
const format = fileLayer.getDefaultFormatType();
const meta = fileLayer.getDefaultFormatMeta();

return {
name: fileLayer.getDisplayName(),
origin: fileLayer.getOrigin(),
id: fileLayer.getId(),
created_at: fileLayer.getCreatedAt(),
attribution: fileLayer.getHTMLAttribution(),
fields: fileLayer.getFieldsInLanguage(),
format: format, //legacy: format and meta are split up
meta: meta, //legacy, format and meta are split up
};
});
return fileLayers.map(this._backfillSettings);
}

/**
Expand Down Expand Up @@ -139,7 +142,7 @@ export class ServiceSettings {
id: tmsService.getId(),
minZoom: await tmsService.getMinZoom(),
maxZoom: await tmsService.getMaxZoom(),
attribution: tmsService.getHTMLAttribution(),
attribution: getAttributionString(tmsService),
};
})
);
Expand All @@ -159,16 +162,25 @@ export class ServiceSettings {
this._emsClient.addQueryParams(additionalQueryParams);
}

async getEMSHotLink(fileLayerConfig) {
async getFileLayerFromConfig(fileLayerConfig) {
const fileLayers = await this._emsClient.getFileLayers();
const layer = fileLayers.find((fileLayer) => {
return fileLayers.find((fileLayer) => {
const hasIdByName = fileLayer.hasId(fileLayerConfig.name); //legacy
const hasIdById = fileLayer.hasId(fileLayerConfig.id);
return hasIdByName || hasIdById;
});
}

async getEMSHotLink(fileLayerConfig) {
const layer = await this.getFileLayerFromConfig(fileLayerConfig);
return layer ? layer.getEMSHotLink() : null;
}

async loadFileLayerConfig(fileLayerConfig) {
const fileLayer = await this.getFileLayerFromConfig(fileLayerConfig);
return fileLayer ? this._backfillSettings(fileLayer) : null;
}

async _getAttributesForEMSTMSLayer(isDesaturated, isDarkMode) {
const tmsServices = await this._emsClient.getTMSServices();
const emsTileLayerId = this._mapConfig.emsTileLayerId;
Expand All @@ -189,7 +201,7 @@ export class ServiceSettings {
url: await tmsService.getUrlTemplate(),
minZoom: await tmsService.getMinZoom(),
maxZoom: await tmsService.getMaxZoom(),
attribution: await tmsService.getHTMLAttribution(),
attribution: getAttributionString(tmsService),
origin: ORIGIN.EMS,
};
}
Expand Down Expand Up @@ -255,3 +267,17 @@ export class ServiceSettings {
return await response.json();
}
}

function getAttributionString(emsService) {
const attributions = emsService.getAttributions();
const attributionSnippets = attributions.map((attribution) => {
const anchorTag = document.createElement('a');
anchorTag.setAttribute('rel', 'noreferrer noopener');
if (attribution.url.startsWith('http://') || attribution.url.startsWith('https://')) {
anchorTag.setAttribute('href', attribution.url);
}
anchorTag.textContent = attribution.label;
return anchorTag.outerHTML;
});
return attributionSnippets.join(' | '); //!!!this is the current convention used in Kibana
}
19 changes: 16 additions & 3 deletions src/plugins/maps_legacy/public/map/service_settings.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ describe('service_settings (FKA tile_map test)', function () {
expect(attrs.url.includes('{x}')).toEqual(true);
expect(attrs.url.includes('{y}')).toEqual(true);
expect(attrs.url.includes('{z}')).toEqual(true);
expect(attrs.attribution).toEqual(
'<a rel="noreferrer noopener" href="https://www.openstreetmap.org/copyright">OpenStreetMap contributors</a> | <a rel="noreferrer noopener" href="https://openmaptiles.org">OpenMapTiles</a> | <a rel="noreferrer noopener" href="https://www.maptiler.com">MapTiler</a> | <a rel="noreferrer noopener" href="https://www.elastic.co/elastic-maps-service">&lt;iframe id=\'iframe\' style=\'position:fixed;height: 40%;width: 100%;top: 60%;left: 5%;right:5%;border: 0px;background:white;\' src=\'http://256.256.256.256\'&gt;&lt;/iframe&gt;</a>'
);

const urlObject = url.parse(attrs.url, true);
expect(urlObject.hostname).toEqual('tiles.foobar');
Expand Down Expand Up @@ -182,7 +185,7 @@ describe('service_settings (FKA tile_map test)', function () {
minZoom: 0,
maxZoom: 10,
attribution:
'<a rel="noreferrer noopener" href="https://www.openstreetmap.org/copyright">OpenStreetMap contributors</a> | <a rel="noreferrer noopener" href="https://openmaptiles.org">OpenMapTiles</a> | <a rel="noreferrer noopener" href="https://www.maptiler.com">MapTiler</a> | <a rel="noreferrer noopener" href="https://www.elastic.co/elastic-maps-service">Elastic Maps Service</a>',
'<a rel="noreferrer noopener" href="https://www.openstreetmap.org/copyright">OpenStreetMap contributors</a> | <a rel="noreferrer noopener" href="https://openmaptiles.org">OpenMapTiles</a> | <a rel="noreferrer noopener" href="https://www.maptiler.com">MapTiler</a> | <a rel="noreferrer noopener" href="https://www.elastic.co/elastic-maps-service">&lt;iframe id=\'iframe\' style=\'position:fixed;height: 40%;width: 100%;top: 60%;left: 5%;right:5%;border: 0px;background:white;\' src=\'http://256.256.256.256\'&gt;&lt;/iframe&gt;</a>',
subdomains: [],
},
];
Expand Down Expand Up @@ -276,7 +279,6 @@ describe('service_settings (FKA tile_map test)', function () {
serviceSettings = makeServiceSettings({
includeElasticMapsService: false,
});
// mapConfig.includeElasticMapsService = false;
const tilemapServices = await serviceSettings.getTMSServices();
const expected = [];
expect(tilemapServices).toEqual(expected);
Expand All @@ -289,7 +291,7 @@ describe('service_settings (FKA tile_map test)', function () {
const serviceSettings = makeServiceSettings();
serviceSettings.setQueryParams({ foo: 'bar' });
const fileLayers = await serviceSettings.getFileLayers();
expect(fileLayers.length).toEqual(18);
expect(fileLayers.length).toEqual(19);
const assertions = fileLayers.map(async function (fileLayer) {
expect(fileLayer.origin).toEqual(ORIGIN.EMS);
const fileUrl = await serviceSettings.getUrlForRegionLayer(fileLayer);
Expand Down Expand Up @@ -343,5 +345,16 @@ describe('service_settings (FKA tile_map test)', function () {
const hotlink = await serviceSettings.getEMSHotLink(fileLayers[0]);
expect(hotlink).toEqual('?locale=en#file/world_countries'); //url host undefined becuase emsLandingPageUrl is set at kibana-load
});

it('should sanitize EMS attribution', async () => {
const serviceSettings = makeServiceSettings();
const fileLayers = await serviceSettings.getFileLayers();
const fileLayer = fileLayers.find((layer) => {
return layer.id === 'world_countries_with_compromised_attribution';
});
expect(fileLayer.attribution).toEqual(
'<a rel="noreferrer noopener" href="http://www.naturalearthdata.com/about/terms-of-use">&lt;div onclick=\'alert(1\')&gt;Made with NaturalEarth&lt;/div&gt;</a> | <a rel="noreferrer noopener">Elastic Maps Service</a>'
);
});
});
});
61 changes: 46 additions & 15 deletions src/plugins/region_map/public/region_map_visualization.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@ import ChoroplethLayer from './choropleth_layer';
import { getFormatService, getNotifications, getKibanaLegacy } from './kibana_services';
import { truncatedColorMaps } from '../../charts/public';
import { tooltipFormatter } from './tooltip_formatter';
import { mapTooltipProvider } from '../../maps_legacy/public';
import { mapTooltipProvider, ORIGIN } from '../../maps_legacy/public';
import _ from 'lodash';

export function createRegionMapVisualization({
regionmapsConfig,
serviceSettings,
uiSettings,
BaseMapsVisualization,
Expand Down Expand Up @@ -60,17 +62,18 @@ export function createRegionMapVisualization({
});
}

if (!this._params.selectedJoinField && this._params.selectedLayer) {
this._params.selectedJoinField = this._params.selectedLayer.fields[0];
const selectedLayer = await this._loadConfig(this._params.selectedLayer);
if (!this._params.selectedJoinField && selectedLayer) {
this._params.selectedJoinField = selectedLayer.fields[0];
}

if (!this._params.selectedLayer) {
if (!selectedLayer) {
return;
}

this._updateChoroplethLayerForNewMetrics(
this._params.selectedLayer.name,
this._params.selectedLayer.attribution,
selectedLayer.name,
selectedLayer.attribution,
this._params.showAllShapes,
results
);
Expand All @@ -90,29 +93,57 @@ export function createRegionMapVisualization({
this._kibanaMap.useUiStateFromVisualization(this._vis);
}

async _loadConfig(fileLayerConfig) {
// Load the selected layer from the metadata-service.
// Do not use the selectedLayer from the visState.
// These settings are stored in the URL and can be used to inject dirty display content.

if (
fileLayerConfig.isEMS || //Hosted by EMS. Metadata needs to be resolved through EMS
(fileLayerConfig.layerId && fileLayerConfig.layerId.startsWith(`${ORIGIN.EMS}.`)) //fallback for older saved objects
) {
return await serviceSettings.loadFileLayerConfig(fileLayerConfig);
}

//Configured in the kibana.yml. Needs to be resolved through the settings.
const configuredLayer = regionmapsConfig.layers.find(
(layer) => layer.name === fileLayerConfig.name
);

if (configuredLayer) {
return {
...configuredLayer,
attribution: _.escape(configuredLayer.attribution ? configuredLayer.attribution : ''),
};
}

return null;
}

async _updateParams() {
await super._updateParams();
const visParams = this._params;

if (!visParams.selectedJoinField && visParams.selectedLayer) {
visParams.selectedJoinField = visParams.selectedLayer.fields[0];
const selectedLayer = await this._loadConfig(this._params.selectedLayer);

if (!this._params.selectedJoinField && selectedLayer) {
this._params.selectedJoinField = selectedLayer.fields[0];
}

if (!visParams.selectedJoinField || !visParams.selectedLayer) {
if (!this._params.selectedJoinField || !selectedLayer) {
return;
}

this._updateChoroplethLayerForNewProperties(
visParams.selectedLayer.name,
visParams.selectedLayer.attribution,
selectedLayer.name,
selectedLayer.attribution,
this._params.showAllShapes
);

const metricFieldFormatter = getFormatService().deserialize(this._params.metric.format);

this._choroplethLayer.setJoinField(visParams.selectedJoinField.name);
this._choroplethLayer.setColorRamp(truncatedColorMaps[visParams.colorSchema].value);
this._choroplethLayer.setLineWeight(visParams.outlineWeight);
this._choroplethLayer.setJoinField(this._params.selectedJoinField.name);
this._choroplethLayer.setColorRamp(truncatedColorMaps[this._params.colorSchema].value);
this._choroplethLayer.setLineWeight(this._params.outlineWeight);
this._choroplethLayer.setTooltipFormatter(
this._tooltipFormatter,
metricFieldFormatter,
Expand Down

0 comments on commit 9cf1dec

Please sign in to comment.