From d7a679ba8c9f9863ae3e6d7f5a6e7fe427ba3f9b Mon Sep 17 00:00:00 2001 From: Aaron Caldwell Date: Mon, 13 Jul 2020 14:27:19 -0600 Subject: [PATCH] [Maps] Fix proxy handling issues (#71182) --- src/plugins/maps_legacy/server/index.ts | 33 +++++-- x-pack/plugins/maps/public/meta.test.js | 5 + x-pack/plugins/maps/public/meta.ts | 17 ++-- x-pack/plugins/maps/server/plugin.ts | 7 +- x-pack/plugins/maps/server/routes.js | 126 ++++++++++++------------ 5 files changed, 108 insertions(+), 80 deletions(-) diff --git a/src/plugins/maps_legacy/server/index.ts b/src/plugins/maps_legacy/server/index.ts index 18f58189fc607..5da3ce1a84408 100644 --- a/src/plugins/maps_legacy/server/index.ts +++ b/src/plugins/maps_legacy/server/index.ts @@ -17,8 +17,9 @@ * under the License. */ -import { PluginConfigDescriptor } from 'kibana/server'; -import { PluginInitializerContext } from 'kibana/public'; +import { Plugin, PluginConfigDescriptor } from 'kibana/server'; +import { PluginInitializerContext } from 'src/core/server'; +import { Observable } from 'rxjs'; import { configSchema, ConfigSchema } from '../config'; export const config: PluginConfigDescriptor = { @@ -37,13 +38,27 @@ export const config: PluginConfigDescriptor = { schema: configSchema, }; -export const plugin = (initializerContext: PluginInitializerContext) => ({ - setup() { +export interface MapsLegacyPluginSetup { + config$: Observable; +} + +export class MapsLegacyPlugin implements Plugin { + readonly _initializerContext: PluginInitializerContext; + + constructor(initializerContext: PluginInitializerContext) { + this._initializerContext = initializerContext; + } + + public setup() { // @ts-ignore - const config$ = initializerContext.config.create(); + const config$ = this._initializerContext.config.create(); return { - config: config$, + config$, }; - }, - start() {}, -}); + } + + public start() {} +} + +export const plugin = (initializerContext: PluginInitializerContext) => + new MapsLegacyPlugin(initializerContext); diff --git a/x-pack/plugins/maps/public/meta.test.js b/x-pack/plugins/maps/public/meta.test.js index 5c04a57c00058..3486bf003aee0 100644 --- a/x-pack/plugins/maps/public/meta.test.js +++ b/x-pack/plugins/maps/public/meta.test.js @@ -36,6 +36,11 @@ describe('getGlyphUrl', () => { beforeAll(() => { require('./kibana_services').getIsEmsEnabled = () => true; require('./kibana_services').getEmsFontLibraryUrl = () => EMS_FONTS_URL_MOCK; + require('./kibana_services').getHttp = () => ({ + basePath: { + prepend: (url) => url, // No need to actually prepend a dev basepath for test + }, + }); }); describe('EMS proxy enabled', () => { diff --git a/x-pack/plugins/maps/public/meta.ts b/x-pack/plugins/maps/public/meta.ts index 54c5eac7fe1b0..34c5f004fd7f3 100644 --- a/x-pack/plugins/maps/public/meta.ts +++ b/x-pack/plugins/maps/public/meta.ts @@ -30,8 +30,6 @@ import { getKibanaVersion, } from './kibana_services'; -const GIS_API_RELATIVE = `../${GIS_API_PATH}`; - export function getKibanaRegionList(): unknown[] { return getRegionmapLayers(); } @@ -69,10 +67,14 @@ export function getEMSClient(): EMSClient { const proxyElasticMapsServiceInMaps = getProxyElasticMapsServiceInMaps(); const proxyPath = ''; const tileApiUrl = proxyElasticMapsServiceInMaps - ? relativeToAbsolute(`${GIS_API_RELATIVE}/${EMS_TILES_CATALOGUE_PATH}`) + ? relativeToAbsolute( + getHttp().basePath.prepend(`/${GIS_API_PATH}/${EMS_TILES_CATALOGUE_PATH}`) + ) : getEmsTileApiUrl(); const fileApiUrl = proxyElasticMapsServiceInMaps - ? relativeToAbsolute(`${GIS_API_RELATIVE}/${EMS_FILES_CATALOGUE_PATH}`) + ? relativeToAbsolute( + getHttp().basePath.prepend(`/${GIS_API_PATH}/${EMS_FILES_CATALOGUE_PATH}`) + ) : getEmsFileApiUrl(); emsClient = new EMSClient({ @@ -101,8 +103,11 @@ export function getGlyphUrl(): string { return getHttp().basePath.prepend(`/${FONTS_API_PATH}/{fontstack}/{range}`); } return getProxyElasticMapsServiceInMaps() - ? relativeToAbsolute(`../${GIS_API_PATH}/${EMS_TILES_CATALOGUE_PATH}/${EMS_GLYPHS_PATH}`) + - `/{fontstack}/{range}` + ? relativeToAbsolute( + getHttp().basePath.prepend( + `/${GIS_API_PATH}/${EMS_TILES_CATALOGUE_PATH}/${EMS_GLYPHS_PATH}` + ) + ) + `/{fontstack}/{range}` : getEmsFontLibraryUrl(); } diff --git a/x-pack/plugins/maps/server/plugin.ts b/x-pack/plugins/maps/server/plugin.ts index dbcce50ac2b9a..7d091099c1aaa 100644 --- a/x-pack/plugins/maps/server/plugin.ts +++ b/x-pack/plugins/maps/server/plugin.ts @@ -26,12 +26,14 @@ import { initRoutes } from './routes'; import { ILicense } from '../../licensing/common/types'; import { LicensingPluginSetup } from '../../licensing/server'; import { HomeServerPluginSetup } from '../../../../src/plugins/home/server'; +import { MapsLegacyPluginSetup } from '../../../../src/plugins/maps_legacy/server'; interface SetupDeps { features: FeaturesPluginSetupContract; usageCollection: UsageCollectionSetup; home: HomeServerPluginSetup; licensing: LicensingPluginSetup; + mapsLegacy: MapsLegacyPluginSetup; } export class MapsPlugin implements Plugin { @@ -129,9 +131,10 @@ export class MapsPlugin implements Plugin { // @ts-ignore async setup(core: CoreSetup, plugins: SetupDeps) { - const { usageCollection, home, licensing, features } = plugins; + const { usageCollection, home, licensing, features, mapsLegacy } = plugins; // @ts-ignore const config$ = this._initializerContext.config.create(); + const mapsLegacyConfig = await mapsLegacy.config$.pipe(take(1)).toPromise(); const currentConfig = await config$.pipe(take(1)).toPromise(); // @ts-ignore @@ -150,7 +153,7 @@ export class MapsPlugin implements Plugin { initRoutes( core.http.createRouter(), license.uid, - currentConfig, + mapsLegacyConfig, this.kibanaVersion, this._logger ); diff --git a/x-pack/plugins/maps/server/routes.js b/x-pack/plugins/maps/server/routes.js index ad66712eb3ad6..1876c0de19c56 100644 --- a/x-pack/plugins/maps/server/routes.js +++ b/x-pack/plugins/maps/server/routes.js @@ -73,9 +73,10 @@ export function initRoutes(router, licenseUid, mapConfig, kbnVersion, logger) { validate: { query: schema.object({ id: schema.maybe(schema.string()), - x: schema.maybe(schema.number()), - y: schema.maybe(schema.number()), - z: schema.maybe(schema.number()), + elastic_tile_service_tos: schema.maybe(schema.string()), + my_app_name: schema.maybe(schema.string()), + my_app_version: schema.maybe(schema.string()), + license: schema.maybe(schema.string()), }), }, }, @@ -111,9 +112,9 @@ export function initRoutes(router, licenseUid, mapConfig, kbnVersion, logger) { path: `${ROOT}/${EMS_TILES_API_PATH}/${EMS_TILES_RASTER_TILE_PATH}`, validate: false, }, - async (context, request, { ok, badRequest }) => { + async (context, request, response) => { if (!checkEMSProxyEnabled()) { - return badRequest('map.proxyElasticMapsServiceInMaps disabled'); + return response.badRequest('map.proxyElasticMapsServiceInMaps disabled'); } if ( @@ -138,7 +139,7 @@ export function initRoutes(router, licenseUid, mapConfig, kbnVersion, logger) { .replace('{y}', request.query.y) .replace('{z}', request.query.z); - return await proxyResource({ url, contentType: 'image/png' }, { ok, badRequest }); + return await proxyResource({ url, contentType: 'image/png' }, response); } ); @@ -203,7 +204,9 @@ export function initRoutes(router, licenseUid, mapConfig, kbnVersion, logger) { }); //rewrite return ok({ - body: layers, + body: { + layers, + }, }); } ); @@ -293,7 +296,11 @@ export function initRoutes(router, licenseUid, mapConfig, kbnVersion, logger) { path: `${ROOT}/${EMS_TILES_API_PATH}/${EMS_TILES_VECTOR_STYLE_PATH}`, validate: { query: schema.object({ - id: schema.maybe(schema.string()), + id: schema.string(), + elastic_tile_service_tos: schema.maybe(schema.string()), + my_app_name: schema.maybe(schema.string()), + my_app_version: schema.maybe(schema.string()), + license: schema.maybe(schema.string()), }), }, }, @@ -302,11 +309,6 @@ export function initRoutes(router, licenseUid, mapConfig, kbnVersion, logger) { return badRequest('map.proxyElasticMapsServiceInMaps disabled'); } - if (!request.query.id) { - logger.warn('Must supply id parameter to retrieve EMS vector style'); - return null; - } - const tmsServices = await emsClient.getTMSServices(); const tmsService = tmsServices.find((layer) => layer.getId() === request.query.id); if (!tmsService) { @@ -342,8 +344,12 @@ export function initRoutes(router, licenseUid, mapConfig, kbnVersion, logger) { path: `${ROOT}/${EMS_TILES_API_PATH}/${EMS_TILES_VECTOR_SOURCE_PATH}`, validate: { query: schema.object({ - id: schema.maybe(schema.string()), + id: schema.string(), sourceId: schema.maybe(schema.string()), + elastic_tile_service_tos: schema.maybe(schema.string()), + my_app_name: schema.maybe(schema.string()), + my_app_version: schema.maybe(schema.string()), + license: schema.maybe(schema.string()), }), }, }, @@ -352,11 +358,6 @@ export function initRoutes(router, licenseUid, mapConfig, kbnVersion, logger) { return badRequest('map.proxyElasticMapsServiceInMaps disabled'); } - if (!request.query.id || !request.query.sourceId) { - logger.warn('Must supply id and sourceId parameter to retrieve EMS vector source'); - return null; - } - const tmsServices = await emsClient.getTMSServices(); const tmsService = tmsServices.find((layer) => layer.getId() === request.query.id); if (!tmsService) { @@ -381,28 +382,21 @@ export function initRoutes(router, licenseUid, mapConfig, kbnVersion, logger) { path: `${ROOT}/${EMS_TILES_API_PATH}/${EMS_TILES_VECTOR_TILE_PATH}`, validate: { query: schema.object({ - id: schema.maybe(schema.string()), - sourceId: schema.maybe(schema.string()), - x: schema.maybe(schema.number()), - y: schema.maybe(schema.number()), - z: schema.maybe(schema.number()), + id: schema.string(), + sourceId: schema.string(), + x: schema.number(), + y: schema.number(), + z: schema.number(), + elastic_tile_service_tos: schema.maybe(schema.string()), + my_app_name: schema.maybe(schema.string()), + my_app_version: schema.maybe(schema.string()), + license: schema.maybe(schema.string()), }), }, }, - async (context, request, { ok, badRequest }) => { + async (context, request, response) => { if (!checkEMSProxyEnabled()) { - return badRequest('map.proxyElasticMapsServiceInMaps disabled'); - } - - if ( - !request.query.id || - !request.query.sourceId || - typeof parseInt(request.query.x, 10) !== 'number' || - typeof parseInt(request.query.y, 10) !== 'number' || - typeof parseInt(request.query.z, 10) !== 'number' - ) { - logger.warn('Must supply id/sourceId/x/y/z parameters to retrieve EMS vector tile'); - return null; + return response.badRequest('map.proxyElasticMapsServiceInMaps disabled'); } const tmsServices = await emsClient.getTMSServices(); @@ -417,24 +411,29 @@ export function initRoutes(router, licenseUid, mapConfig, kbnVersion, logger) { .replace('{y}', request.query.y) .replace('{z}', request.query.z); - return await proxyResource({ url }, { ok, badRequest }); + return await proxyResource({ url }, response); } ); router.get( { path: `${ROOT}/${EMS_TILES_API_PATH}/${EMS_GLYPHS_PATH}/{fontstack}/{range}`, - validate: false, + validate: { + params: schema.object({ + fontstack: schema.string(), + range: schema.string(), + }), + }, }, - async (context, request, { ok, badRequest }) => { + async (context, request, response) => { if (!checkEMSProxyEnabled()) { - return badRequest('map.proxyElasticMapsServiceInMaps disabled'); + return response.badRequest('map.proxyElasticMapsServiceInMaps disabled'); } const url = mapConfig.emsFontLibraryUrl .replace('{fontstack}', request.params.fontstack) .replace('{range}', request.params.range); - return await proxyResource({ url }, { ok, badRequest }); + return await proxyResource({ url }, response); } ); @@ -442,19 +441,22 @@ export function initRoutes(router, licenseUid, mapConfig, kbnVersion, logger) { { path: `${ROOT}/${EMS_TILES_API_PATH}/${EMS_SPRITES_PATH}/{id}/sprite{scaling?}.{extension}`, validate: { + query: schema.object({ + elastic_tile_service_tos: schema.maybe(schema.string()), + my_app_name: schema.maybe(schema.string()), + my_app_version: schema.maybe(schema.string()), + license: schema.maybe(schema.string()), + }), params: schema.object({ id: schema.string(), + scaling: schema.maybe(schema.string()), + extension: schema.string(), }), }, }, - async (context, request, { ok, badRequest }) => { + async (context, request, response) => { if (!checkEMSProxyEnabled()) { - return badRequest('map.proxyElasticMapsServiceInMaps disabled'); - } - - if (!request.params.id) { - logger.warn('Must supply id parameter to retrieve EMS vector source sprite'); - return null; + return response.badRequest('map.proxyElasticMapsServiceInMaps disabled'); } const tmsServices = await emsClient.getTMSServices(); @@ -479,7 +481,7 @@ export function initRoutes(router, licenseUid, mapConfig, kbnVersion, logger) { url: proxyPathUrl, contentType: request.params.extension === 'png' ? 'image/png' : '', }, - { ok, badRequest } + response ); } ); @@ -570,25 +572,23 @@ export function initRoutes(router, licenseUid, mapConfig, kbnVersion, logger) { return proxyEMSInMaps; } - async function proxyResource({ url, contentType }, { ok, badRequest }) { + async function proxyResource({ url, contentType }, response) { try { const resource = await fetch(url); const arrayBuffer = await resource.arrayBuffer(); - const bufferedResponse = Buffer.from(arrayBuffer); - const headers = { - 'Content-Disposition': 'inline', - }; - if (contentType) { - headers['Content-type'] = contentType; - } - - return ok({ - body: bufferedResponse, - headers, + const buffer = Buffer.from(arrayBuffer); + + return response.ok({ + body: buffer, + headers: { + 'content-disposition': 'inline', + 'content-length': buffer.length, + ...(contentType ? { 'Content-type': contentType } : {}), + }, }); } catch (e) { logger.warn(`Cannot connect to EMS for resource, error: ${e.message}`); - return badRequest(`Cannot connect to EMS`); + return response.badRequest(`Cannot connect to EMS`); } } }