From 8ce72ea842d66ca9974a47c6b459e294d3e66cbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Thu, 22 Mar 2018 12:30:51 +0100 Subject: [PATCH 01/30] Do not pass `res.locals` to collaborators --- lib/cartodb/backends/analysis-status.js | 28 +++---------------------- lib/cartodb/controllers/analyses.js | 26 +++++------------------ lib/cartodb/controllers/layergroup.js | 7 ++++++- lib/cartodb/utils/database-params.js | 25 ++++++++++++++++++++++ 4 files changed, 39 insertions(+), 47 deletions(-) create mode 100644 lib/cartodb/utils/database-params.js diff --git a/lib/cartodb/backends/analysis-status.js b/lib/cartodb/backends/analysis-status.js index 97f851d2..0060f36b 100644 --- a/lib/cartodb/backends/analysis-status.js +++ b/lib/cartodb/backends/analysis-status.js @@ -5,16 +5,14 @@ function AnalysisStatusBackend() { module.exports = AnalysisStatusBackend; - -AnalysisStatusBackend.prototype.getNodeStatus = function (params, callback) { - var nodeId = params.nodeId; - +AnalysisStatusBackend.prototype.getNodeStatus = function (nodeId, dbParams, callback) { var statusQuery = [ 'SELECT node_id, status, updated_at, last_error_message as error_message', 'FROM cdb_analysis_catalog where node_id = \'' + nodeId + '\'' ].join(' '); - var pg = new PSQL(dbParamsFromReqParams(params)); + var pg = new PSQL(dbParams); + pg.query(statusQuery, function(err, result) { if (err) { return callback(err, result); @@ -36,23 +34,3 @@ AnalysisStatusBackend.prototype.getNodeStatus = function (params, callback) { return callback(null, statusResponse); }, true); // use read-only transaction }; - -function dbParamsFromReqParams(params) { - var dbParams = {}; - if ( params.dbuser ) { - dbParams.user = params.dbuser; - } - if ( params.dbpassword ) { - dbParams.pass = params.dbpassword; - } - if ( params.dbhost ) { - dbParams.host = params.dbhost; - } - if ( params.dbport ) { - dbParams.port = params.dbport; - } - if ( params.dbname ) { - dbParams.dbname = params.dbname; - } - return dbParams; -} diff --git a/lib/cartodb/controllers/analyses.js b/lib/cartodb/controllers/analyses.js index f632b394..0da18bf6 100644 --- a/lib/cartodb/controllers/analyses.js +++ b/lib/cartodb/controllers/analyses.js @@ -11,6 +11,7 @@ const rateLimit = require('../middleware/rate-limit'); const { RATE_LIMIT_ENDPOINTS_GROUPS } = rateLimit; const cacheControlHeader = require('../middleware/cache-control-header'); const sendResponse = require('../middleware/send-response'); +const dbParamsFromResLocals = require('../utils/database-params'); function AnalysesController(pgConnection, authApi, userLimitsApi) { this.pgConnection = pgConnection; @@ -46,7 +47,10 @@ AnalysesController.prototype.register = function (app) { function createPGClient () { return function createPGClientMiddleware (req, res, next) { - res.locals.pg = new PSQL(dbParamsFromReqParams(res.locals)); + const dbParams = dbParamsFromResLocals(res.locals); + + res.locals.pg = new PSQL(dbParams); + next(); }; } @@ -146,23 +150,3 @@ var tablesQueryTpl = ctx => ` FROM analysis_tables ORDER BY size DESC `; - -function dbParamsFromReqParams(params) { - var dbParams = {}; - if ( params.dbuser ) { - dbParams.user = params.dbuser; - } - if ( params.dbpassword ) { - dbParams.pass = params.dbpassword; - } - if ( params.dbhost ) { - dbParams.host = params.dbhost; - } - if ( params.dbport ) { - dbParams.port = params.dbport; - } - if ( params.dbname ) { - dbParams.dbname = params.dbname; - } - return dbParams; -} diff --git a/lib/cartodb/controllers/layergroup.js b/lib/cartodb/controllers/layergroup.js index a77a7168..28ac7103 100644 --- a/lib/cartodb/controllers/layergroup.js +++ b/lib/cartodb/controllers/layergroup.js @@ -17,6 +17,8 @@ const sendResponse = require('../middleware/send-response'); const DataviewBackend = require('../backends/dataview'); const AnalysisStatusBackend = require('../backends/analysis-status'); const MapStoreMapConfigProvider = require('../models/mapconfig/provider/map-store-provider'); +const dbParamsFromResLocals = require('../utils/database-params'); + const SUPPORTED_FORMATS = { grid_json: true, json_torque: true, @@ -383,7 +385,10 @@ function distinguishLayergroupFromStaticRoute () { function analysisNodeStatus (analysisStatusBackend) { return function analysisNodeStatusMiddleware(req, res, next) { - analysisStatusBackend.getNodeStatus(res.locals, (err, nodeStatus, stats = {}) => { + const { nodeId } = req.params; + const dbParams = dbParamsFromResLocals(res.locals); + + analysisStatusBackend.getNodeStatus(nodeId, dbParams, (err, nodeStatus, stats = {}) => { req.profiler.add(stats); if (err) { diff --git a/lib/cartodb/utils/database-params.js b/lib/cartodb/utils/database-params.js new file mode 100644 index 00000000..a2176eaf --- /dev/null +++ b/lib/cartodb/utils/database-params.js @@ -0,0 +1,25 @@ +module.exports = function getDatabaseConnectionParams (params) { + const dbParams = {}; + + if (params.dbuser) { + dbParams.user = params.dbuser; + } + + if (params.dbpassword) { + dbParams.pass = params.dbpassword; + } + + if (params.dbhost) { + dbParams.host = params.dbhost; + } + + if (params.dbport) { + dbParams.port = params.dbport; + } + + if (params.dbname) { + dbParams.dbname = params.dbname; + } + + return dbParams; +}; From 875f3c07b39940cc8e65e1f41e5381f9a2df8605 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Thu, 22 Mar 2018 17:07:38 +0100 Subject: [PATCH 02/30] Pass only needed params to MapStoreMapConfigProvider --- lib/cartodb/controllers/layergroup.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/cartodb/controllers/layergroup.js b/lib/cartodb/controllers/layergroup.js index 28ac7103..614480a6 100644 --- a/lib/cartodb/controllers/layergroup.js +++ b/lib/cartodb/controllers/layergroup.js @@ -425,9 +425,15 @@ function createMapStoreMapConfigProvider ( forcedFormat = null ) { return function createMapStoreMapConfigProviderMiddleware (req, res, next) { - const { user } = res.locals; + const { user, token, cache_buster, api_key } = res.locals; + const { dbuser, dbname, dbpassword, dbhost, dbport } = res.locals; + const { layer, z, x, y, scale_factor, format } = req.params; - const params = getRequestParams(res.locals); + const params = { + user, token, cache_buster, api_key, + dbuser, dbname, dbpassword, dbhost, dbport, + layer, z, x, y, scale_factor, format + }; if (forcedFormat) { params.format = forcedFormat; From 1059066c055a90469840ada93173d91861b441bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Thu, 22 Mar 2018 17:53:24 +0100 Subject: [PATCH 03/30] Use module to get database parameters --- lib/cartodb/backends/dataview.js | 26 ++------------------------ 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/lib/cartodb/backends/dataview.js b/lib/cartodb/backends/dataview.js index c110f328..4eebb2a2 100644 --- a/lib/cartodb/backends/dataview.js +++ b/lib/cartodb/backends/dataview.js @@ -1,13 +1,11 @@ var assert = require('assert'); - var _ = require('underscore'); var PSQL = require('cartodb-psql'); var step = require('step'); - var BBoxFilter = require('../models/filter/bbox'); - var DataviewFactory = require('../models/dataview/factory'); var DataviewFactoryWithOverviews = require('../models/dataview/overviews/factory'); +const dbParamsFromReqParams = require('../utils/database-params'); var OverviewsQueryRewriter = require('../utils/overviews_query_rewriter'); var overviewsQueryRewriter = new OverviewsQueryRewriter({ zoom_level: 'CDB_ZoomFromScale(!scale_denominator!)' @@ -48,7 +46,7 @@ DataviewBackend.prototype.getDataview = function (mapConfigProvider, user, param } var pg = new PSQL(dbParamsFromReqParams(params)); - + var query = getDataviewQuery(dataviewDefinition, ownFilter, noFilters); if (params.bbox) { var bboxFilter = new BBoxFilter({column: 'the_geom_webmercator', srid: 3857}, {bbox: params.bbox}); @@ -170,23 +168,3 @@ function getDataviewDefinition(mapConfig, dataviewName) { var dataviews = mapConfig.dataviews || {}; return dataviews[dataviewName]; } - -function dbParamsFromReqParams(params) { - var dbParams = {}; - if ( params.dbuser ) { - dbParams.user = params.dbuser; - } - if ( params.dbpassword ) { - dbParams.pass = params.dbpassword; - } - if ( params.dbhost ) { - dbParams.host = params.dbhost; - } - if ( params.dbport ) { - dbParams.port = params.dbport; - } - if ( params.dbname ) { - dbParams.dbname = params.dbname; - } - return dbParams; -} From 258d76888778ddf62f715b8a3ebeff2f49a80c02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Thu, 22 Mar 2018 17:54:40 +0100 Subject: [PATCH 04/30] Use upercase for constants --- lib/cartodb/controllers/layergroup.js | 38 +++++++++++++-------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/lib/cartodb/controllers/layergroup.js b/lib/cartodb/controllers/layergroup.js index 614480a6..6ae62433 100644 --- a/lib/cartodb/controllers/layergroup.js +++ b/lib/cartodb/controllers/layergroup.js @@ -28,6 +28,21 @@ const SUPPORTED_FORMATS = { mvt: true }; +const ALLOWED_DATAVIEW_QUERY_PARAMS = [ + 'filters', // json + 'own_filter', // 0, 1 + 'no_filters', // 0, 1 + 'bbox', // w,s,e,n + 'start', // number + 'end', // number + 'column_type', // string + 'bins', // number + 'aggregation', //string + 'offset', // number + 'q', // widgets search + 'categories', // number +]; + /** * @param {prepareContext} prepareContext * @param {PgConnection} pgConnection @@ -242,25 +257,10 @@ LayergroupController.prototype.register = function(app) { // Undocumented/non-supported API endpoint methods. // Use at your own peril. - const allowedDataviewQueryParams = [ - 'filters', // json - 'own_filter', // 0, 1 - 'no_filters', // 0, 1 - 'bbox', // w,s,e,n - 'start', // number - 'end', // number - 'column_type', // string - 'bins', // number - 'aggregation', //string - 'offset', // number - 'q', // widgets search - 'categories', // number - ]; - app.get( `${mapConfigBasePath}/:token/dataview/:dataviewName`, cors(), - cleanUpQueryParams(allowedDataviewQueryParams), + cleanUpQueryParams(ALLOWED_DATAVIEW_QUERY_PARAMS), locals(), user(), rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.DATAVIEW), @@ -285,7 +285,7 @@ LayergroupController.prototype.register = function(app) { app.get( `${mapConfigBasePath}/:token/:layer/widget/:dataviewName`, cors(), - cleanUpQueryParams(allowedDataviewQueryParams), + cleanUpQueryParams(ALLOWED_DATAVIEW_QUERY_PARAMS), locals(), user(), rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.DATAVIEW), @@ -310,7 +310,7 @@ LayergroupController.prototype.register = function(app) { app.get( `${mapConfigBasePath}/:token/dataview/:dataviewName/search`, cors(), - cleanUpQueryParams(allowedDataviewQueryParams), + cleanUpQueryParams(ALLOWED_DATAVIEW_QUERY_PARAMS), locals(), user(), rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.DATAVIEW_SEARCH), @@ -335,7 +335,7 @@ LayergroupController.prototype.register = function(app) { app.get( `${mapConfigBasePath}/:token/:layer/widget/:dataviewName/search`, cors(), - cleanUpQueryParams(allowedDataviewQueryParams), + cleanUpQueryParams(ALLOWED_DATAVIEW_QUERY_PARAMS), locals(), user(), rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.DATAVIEW_SEARCH), From 2812a5421009060deb131db3bb29d1397ad8ba64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Thu, 22 Mar 2018 17:55:15 +0100 Subject: [PATCH 05/30] Pass only needed params to dataview backend --- lib/cartodb/controllers/layergroup.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/cartodb/controllers/layergroup.js b/lib/cartodb/controllers/layergroup.js index 6ae62433..bc7026a7 100644 --- a/lib/cartodb/controllers/layergroup.js +++ b/lib/cartodb/controllers/layergroup.js @@ -456,7 +456,10 @@ function createMapStoreMapConfigProvider ( function getDataview (dataviewBackend) { return function getDataviewMiddleware (req, res, next) { const { user, mapConfigProvider } = res.locals; - const params = getRequestParams(res.locals); + const { dataviewName } = req.params; + const { dbuser, dbname, dbpassword, dbhost, dbport } = res.locals; + + const params = Object.assign({ dataviewName, dbuser, dbname, dbpassword, dbhost, dbport }, req.query); dataviewBackend.getDataview(mapConfigProvider, user, params, (err, dataview, stats = {}) => { req.profiler.add(stats); From 81706b8726b59a92858bcc2ceddb66fe064994a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Thu, 22 Mar 2018 18:03:38 +0100 Subject: [PATCH 06/30] Pass only needed params to dataview backend (search) --- lib/cartodb/controllers/layergroup.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/cartodb/controllers/layergroup.js b/lib/cartodb/controllers/layergroup.js index bc7026a7..3a967680 100644 --- a/lib/cartodb/controllers/layergroup.js +++ b/lib/cartodb/controllers/layergroup.js @@ -478,8 +478,11 @@ function getDataview (dataviewBackend) { function dataviewSearch (dataviewBackend) { return function dataviewSearchMiddleware (req, res, next) { - const { user, dataviewName, mapConfigProvider } = res.locals; - const params = getRequestParams(res.locals); + const { user, mapConfigProvider } = res.locals; + const { dataviewName } = req.params; + const { dbuser, dbname, dbpassword, dbhost, dbport } = res.locals; + + const params = Object.assign({ dbuser, dbname, dbpassword, dbhost, dbport }, req.query); dataviewBackend.search(mapConfigProvider, user, dataviewName, params, (err, searchResult, stats = {}) => { req.profiler.add(stats); From d3cbd700545c10a043c4db83fbfe5ae121d15576 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Thu, 22 Mar 2018 18:16:41 +0100 Subject: [PATCH 07/30] Pass only needed params to attributes backend backend --- lib/cartodb/controllers/layergroup.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/cartodb/controllers/layergroup.js b/lib/cartodb/controllers/layergroup.js index 3a967680..6a807474 100644 --- a/lib/cartodb/controllers/layergroup.js +++ b/lib/cartodb/controllers/layergroup.js @@ -504,7 +504,15 @@ function getFeatureAttributes (attributesBackend) { req.profiler.start('windshaft.maplayer_attribute'); const { mapConfigProvider } = res.locals; - const params = getRequestParams(res.locals); + const { token } = res.locals; + const { dbuser, dbname, dbpassword, dbhost, dbport } = res.locals; + const { layer, fid } = req.params; + + const params = { + token, + dbuser, dbname, dbpassword, dbhost, dbport, + layer, fid + }; attributesBackend.getFeatureAttributes(mapConfigProvider, params, false, (err, tile, stats = {}) => { req.profiler.add(stats); From 79955c7fac961976403b2fed3e71a5e4be83ed4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Thu, 22 Mar 2018 18:27:40 +0100 Subject: [PATCH 08/30] Pass only needed params to tile backend --- lib/cartodb/controllers/layergroup.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/cartodb/controllers/layergroup.js b/lib/cartodb/controllers/layergroup.js index 6a807474..63da4947 100644 --- a/lib/cartodb/controllers/layergroup.js +++ b/lib/cartodb/controllers/layergroup.js @@ -543,7 +543,10 @@ function getTile (tileBackend, profileLabel = 'tile') { req.profiler.start(`windshaft.${profileLabel}`); const { mapConfigProvider } = res.locals; - const params = getRequestParams(res.locals); + const { token } = res.locals; + const { layer, z, x, y, format } = req.params; + + const params = { token, layer, z, x, y, format }; tileBackend.getTile(mapConfigProvider, params, (err, tile, headers, stats = {}) => { req.profiler.add(stats); From 8523875349394d9ac98ac7face76dd89902e47e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Thu, 22 Mar 2018 18:29:00 +0100 Subject: [PATCH 09/30] Remove function thet is never used --- lib/cartodb/controllers/layergroup.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/lib/cartodb/controllers/layergroup.js b/lib/cartodb/controllers/layergroup.js index 63da4947..0930fbed 100644 --- a/lib/cartodb/controllers/layergroup.js +++ b/lib/cartodb/controllers/layergroup.js @@ -408,15 +408,6 @@ function analysisNodeStatus (analysisStatusBackend) { }; } -function getRequestParams(locals) { - const params = Object.assign({}, locals); - - delete params.mapConfigProvider; - delete params.allowedQueryParams; - - return params; -} - function createMapStoreMapConfigProvider ( mapStore, userLimitsApi, From afc608fc5da5576443ce092a9949c867eb1ff52a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Thu, 22 Mar 2018 18:57:26 +0100 Subject: [PATCH 10/30] Pass only needed params to named map map config provider --- lib/cartodb/controllers/map.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/cartodb/controllers/map.js b/lib/cartodb/controllers/map.js index cdb1515f..192479e2 100644 --- a/lib/cartodb/controllers/map.js +++ b/lib/cartodb/controllers/map.js @@ -242,7 +242,11 @@ function getTemplate ( ) { return function getTemplateMiddleware (req, res, next) { const templateParams = req.body; - const { user } = res.locals; + const { user, dbuser, dbname, dbpassword, dbhost, dbport } = res.locals; + const { template_id } = req.params; + const { auth_token } = req.query; + + const params = { dbuser, dbname, dbpassword, dbhost, dbport }; const mapConfigProvider = new NamedMapMapConfigProvider( templateMaps, @@ -252,10 +256,10 @@ function getTemplate ( mapConfigAdapter, affectedTablesCache, user, - req.params.template_id, + template_id, templateParams, - res.locals.auth_token, - res.locals + auth_token, + params ); mapConfigProvider.getMapConfig((err, mapConfig, rendererParams) => { From 4f8c184bc0146fbbe5490a5b15dd28483e9da7d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Thu, 22 Mar 2018 19:14:18 +0100 Subject: [PATCH 11/30] Pass only needed params to map config adapter --- lib/cartodb/controllers/map.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/cartodb/controllers/map.js b/lib/cartodb/controllers/map.js index 192479e2..46ef911c 100644 --- a/lib/cartodb/controllers/map.js +++ b/lib/cartodb/controllers/map.js @@ -280,7 +280,10 @@ function getTemplate ( function prepareAdapterMapConfig (mapConfigAdapter) { return function prepareAdapterMapConfigMiddleware(req, res, next) { const requestMapConfig = req.body; - const { user, dbhost, dbport, dbname, dbuser, dbpassword, api_key } = res.locals; + + const { user, api_key } = res.locals; + const { dbuser, dbname, dbpassword, dbhost, dbport } = res.locals; + const params = Object.assign({ dbuser, dbname, dbpassword, dbhost, dbport }, req.query); const context = { analysisConfiguration: { @@ -299,7 +302,7 @@ function prepareAdapterMapConfig (mapConfigAdapter) { } }; - mapConfigAdapter.getMapConfig(user, requestMapConfig, res.locals, context, (err, requestMapConfig) => { + mapConfigAdapter.getMapConfig(user, requestMapConfig, params, context, (err, requestMapConfig) => { req.profiler.done('anonymous.getMapConfig'); if (err) { return next(err); From 6b7c2675f1d717b5ed873e97ef360d56c46ff1a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Thu, 22 Mar 2018 19:20:51 +0100 Subject: [PATCH 12/30] Use database params module --- .../mapconfig/adapter/turbo-carto-adapter.js | 22 ++----------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/lib/cartodb/models/mapconfig/adapter/turbo-carto-adapter.js b/lib/cartodb/models/mapconfig/adapter/turbo-carto-adapter.js index b9487095..2bd30152 100644 --- a/lib/cartodb/models/mapconfig/adapter/turbo-carto-adapter.js +++ b/lib/cartodb/models/mapconfig/adapter/turbo-carto-adapter.js @@ -11,6 +11,8 @@ var PostgresDatasource = require('../../../backends/turbo-carto-postgres-datasou var MapConfig = require('windshaft').model.MapConfig; +const dbParamsFromReqParams = require('../../../utils/database-params'); + function TurboCartoAdapter() { } @@ -158,23 +160,3 @@ TurboCartoAdapter.prototype.process = function (psql, cartocss, sql, callback) { function shouldParseLayerCartocss(layer) { return layer && layer.options && layer.options.cartocss && layer.options.sql; } - -function dbParamsFromReqParams(params) { - var dbParams = {}; - if ( params.dbuser ) { - dbParams.user = params.dbuser; - } - if ( params.dbpassword ) { - dbParams.pass = params.dbpassword; - } - if ( params.dbhost ) { - dbParams.host = params.dbhost; - } - if ( params.dbport ) { - dbParams.port = params.dbport; - } - if ( params.dbname ) { - dbParams.dbname = params.dbname; - } - return dbParams; -} From d029f8199249b3b1a315c58c6a323ad744f0ee36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Thu, 22 Mar 2018 19:36:42 +0100 Subject: [PATCH 13/30] Pass only needed params to create layergroup map config provider --- lib/cartodb/controllers/map.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/cartodb/controllers/map.js b/lib/cartodb/controllers/map.js index 46ef911c..ef080e24 100644 --- a/lib/cartodb/controllers/map.js +++ b/lib/cartodb/controllers/map.js @@ -319,16 +319,26 @@ function prepareAdapterMapConfig (mapConfigAdapter) { function createLayergroup (mapBackend, userLimitsApi, pgConnection, affectedTablesCache) { return function createLayergroupMiddleware (req, res, next) { const requestMapConfig = req.body; - const { context, user } = res.locals; + + const { context } = res.locals; + const { user, cache_buster, api_key } = res.locals; + const { dbuser, dbname, dbpassword, dbhost, dbport } = res.locals; + + const params = { + cache_buster, api_key, + dbuser, dbname, dbpassword, dbhost, dbport + }; + const datasource = context.datasource || Datasource.EmptyDatasource(); const mapConfig = new MapConfig(requestMapConfig, datasource); + const mapConfigProvider = new CreateLayergroupMapConfigProvider( mapConfig, user, userLimitsApi, pgConnection, affectedTablesCache, - res.locals + params ); res.locals.mapConfig = mapConfig; From 4ff8d6fbc3683181b74b7af43a97c3dd225593a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Thu, 22 Mar 2018 19:37:08 +0100 Subject: [PATCH 14/30] Pass only needed params to map backend --- lib/cartodb/controllers/map.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/cartodb/controllers/map.js b/lib/cartodb/controllers/map.js index ef080e24..5eb89b58 100644 --- a/lib/cartodb/controllers/map.js +++ b/lib/cartodb/controllers/map.js @@ -344,7 +344,9 @@ function createLayergroup (mapBackend, userLimitsApi, pgConnection, affectedTabl res.locals.mapConfig = mapConfig; res.locals.analysesResults = context.analysesResults; - mapBackend.createLayergroup(mapConfig, res.locals, mapConfigProvider, (err, layergroup) => { + const mapParams = { dbuser, dbname, dbpassword, dbhost, dbport }; + + mapBackend.createLayergroup(mapConfig, mapParams, mapConfigProvider, (err, layergroup) => { req.profiler.done('createLayergroup'); if (err) { return next(err); From c31639ebbd03198ecc69161002f6039030793653 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Thu, 22 Mar 2018 19:38:56 +0100 Subject: [PATCH 15/30] Move assignments --- lib/cartodb/controllers/map.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/cartodb/controllers/map.js b/lib/cartodb/controllers/map.js index 5eb89b58..e476daf3 100644 --- a/lib/cartodb/controllers/map.js +++ b/lib/cartodb/controllers/map.js @@ -341,9 +341,6 @@ function createLayergroup (mapBackend, userLimitsApi, pgConnection, affectedTabl params ); - res.locals.mapConfig = mapConfig; - res.locals.analysesResults = context.analysesResults; - const mapParams = { dbuser, dbname, dbpassword, dbhost, dbport }; mapBackend.createLayergroup(mapConfig, mapParams, mapConfigProvider, (err, layergroup) => { @@ -353,7 +350,9 @@ function createLayergroup (mapBackend, userLimitsApi, pgConnection, affectedTabl } res.body = layergroup; + res.locals.mapConfig = mapConfig; res.locals.mapConfigProvider = mapConfigProvider; + res.locals.analysesResults = context.analysesResults; next(); }); From ebefba9e3255bd3832fba9df0e247825f5ab742e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Fri, 23 Mar 2018 10:57:35 +0100 Subject: [PATCH 16/30] Revert: move map-config assignment --- lib/cartodb/controllers/map.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/cartodb/controllers/map.js b/lib/cartodb/controllers/map.js index e476daf3..5eb89b58 100644 --- a/lib/cartodb/controllers/map.js +++ b/lib/cartodb/controllers/map.js @@ -341,6 +341,9 @@ function createLayergroup (mapBackend, userLimitsApi, pgConnection, affectedTabl params ); + res.locals.mapConfig = mapConfig; + res.locals.analysesResults = context.analysesResults; + const mapParams = { dbuser, dbname, dbpassword, dbhost, dbport }; mapBackend.createLayergroup(mapConfig, mapParams, mapConfigProvider, (err, layergroup) => { @@ -350,9 +353,7 @@ function createLayergroup (mapBackend, userLimitsApi, pgConnection, affectedTabl } res.body = layergroup; - res.locals.mapConfig = mapConfig; res.locals.mapConfigProvider = mapConfigProvider; - res.locals.analysesResults = context.analysesResults; next(); }); From 8be7ea5cc14fc63f17da390d9a780dc29f8b1854 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Fri, 23 Mar 2018 11:01:36 +0100 Subject: [PATCH 17/30] Pass only needed properties to named map provider cache --- lib/cartodb/controllers/named_maps.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/cartodb/controllers/named_maps.js b/lib/cartodb/controllers/named_maps.js index 26c5e4ad..912020f7 100644 --- a/lib/cartodb/controllers/named_maps.js +++ b/lib/cartodb/controllers/named_maps.js @@ -127,16 +127,22 @@ NamedMapsController.prototype.register = function(app) { function getNamedMapProvider ({ namedMapProviderCache, label, forcedFormat = null }) { return function getNamedMapProviderMiddleware (req, res, next) { - const { user } = res.locals; - const { config, auth_token } = req.query; - const { template_id } = req.params; + const { user, token, cache_buster, api_key } = res.locals; + const { dbuser, dbname, dbpassword, dbhost, dbport } = res.locals; + const { template_id, layer, z, x, y, format } = req.params; + + const params = { + user, token, cache_buster, api_key, + dbuser, dbname, dbpassword, dbhost, dbport, + template_id, layer, z, x, y, format + }; if (forcedFormat) { - res.locals.format = forcedFormat; - res.locals.layer = res.locals.layer || 'all'; + params.format = forcedFormat; + params.layer = params.layer || 'all'; } - const params = getRequestParams(res.locals); + const { config, auth_token } = req.query; namedMapProviderCache.get(user, template_id, config, auth_token, params, (err, namedMapProvider) => { if (err) { From 10ead27676fdbc15be44ecaa94a9371db0802077 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Fri, 23 Mar 2018 11:23:19 +0100 Subject: [PATCH 18/30] Pass only needed properties to named map provider cache (static endpoint) --- lib/cartodb/controllers/named_maps.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/cartodb/controllers/named_maps.js b/lib/cartodb/controllers/named_maps.js index 912020f7..40faf214 100644 --- a/lib/cartodb/controllers/named_maps.js +++ b/lib/cartodb/controllers/named_maps.js @@ -176,8 +176,7 @@ function getTemplate ({ label }) { function prepareLayerFilterFromPreviewLayers ({ namedMapProviderCache, label }) { return function prepareLayerFilterFromPreviewLayersMiddleware (req, res, next) { - const { user, template } = res.locals; - const { template_id } = req.params; + const { template } = res.locals; const { config, auth_token } = req.query; if (!template || !template.view || !template.view.preview_layers) { @@ -197,7 +196,15 @@ function prepareLayerFilterFromPreviewLayers ({ namedMapProviderCache, label }) return next(); } - const params = getRequestParams(res.locals); + const { user, token, cache_buster, api_key } = res.locals; + const { dbuser, dbname, dbpassword, dbhost, dbport } = res.locals; + const { template_id, format } = req.params; + + const params = { + user, token, cache_buster, api_key, + dbuser, dbname, dbpassword, dbhost, dbport, + template_id, format + }; // overwrites 'all' default filter params.layer = layerVisibilityFilter.join(','); From 97a49fab2f2398c67475bbec6fa00eea6c8c0e3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Fri, 23 Mar 2018 11:33:40 +0100 Subject: [PATCH 19/30] Remove function defined but nerver used --- lib/cartodb/controllers/named_maps.js | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/lib/cartodb/controllers/named_maps.js b/lib/cartodb/controllers/named_maps.js index 40faf214..a67befea 100644 --- a/lib/cartodb/controllers/named_maps.js +++ b/lib/cartodb/controllers/named_maps.js @@ -27,17 +27,6 @@ function numMapper(n) { return +n; } -function getRequestParams(locals) { - const params = Object.assign({}, locals); - - delete params.template; - delete params.affectedTables; - delete params.mapConfigProvider; - delete params.allowedQueryParams; - - return params; -} - function NamedMapsController ( namedMapProviderCache, tileBackend, From d3c9da6d5ff3b0f54d89cbc483630f4a2276a978 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Fri, 23 Mar 2018 11:57:28 +0100 Subject: [PATCH 20/30] Fix layer filter by query params --- lib/cartodb/controllers/named_maps.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/cartodb/controllers/named_maps.js b/lib/cartodb/controllers/named_maps.js index a67befea..5383581c 100644 --- a/lib/cartodb/controllers/named_maps.js +++ b/lib/cartodb/controllers/named_maps.js @@ -118,12 +118,13 @@ function getNamedMapProvider ({ namedMapProviderCache, label, forcedFormat = nul return function getNamedMapProviderMiddleware (req, res, next) { const { user, token, cache_buster, api_key } = res.locals; const { dbuser, dbname, dbpassword, dbhost, dbport } = res.locals; - const { template_id, layer, z, x, y, format } = req.params; + const { template_id, layer: layerFromParams, z, x, y, format } = req.params; + const { layer: layerFromQuery } = req.query; const params = { user, token, cache_buster, api_key, dbuser, dbname, dbpassword, dbhost, dbport, - template_id, layer, z, x, y, format + template_id, layer: (layerFromQuery || layerFromParams), z, x, y, format }; if (forcedFormat) { From 7ba3394508b4970a313fa4909b18b2b0b06652b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Fri, 23 Mar 2018 14:10:27 +0100 Subject: [PATCH 21/30] Do not merge req.params and req.query into res.locals (don't use locals middleware in analysis controller) --- lib/cartodb/controllers/analyses.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/cartodb/controllers/analyses.js b/lib/cartodb/controllers/analyses.js index 0da18bf6..81b1757a 100644 --- a/lib/cartodb/controllers/analyses.js +++ b/lib/cartodb/controllers/analyses.js @@ -1,7 +1,6 @@ const PSQL = require('cartodb-psql'); const cors = require('../middleware/cors'); const user = require('../middleware/user'); -const locals = require('../middleware/locals'); const cleanUpQueryParams = require('../middleware/clean-up-query-params'); const layergroupToken = require('../middleware/layergroup-token'); const credentials = require('../middleware/credentials'); @@ -28,7 +27,6 @@ AnalysesController.prototype.register = function (app) { `${mapconfigBasePath}/analyses/catalog`, cors(), cleanUpQueryParams(), - locals(), user(), rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.ANALYSIS_CATALOG), layergroupToken(), From f76606bc2632df81660fe0762aa2bbe2a0c4195e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Fri, 23 Mar 2018 14:13:27 +0100 Subject: [PATCH 22/30] Do not use locals middleware in layergroup controller --- lib/cartodb/api/auth_api.js | 6 ++--- lib/cartodb/controllers/layergroup.js | 31 +++++++++------------- lib/cartodb/middleware/error-middleware.js | 12 +++------ lib/cartodb/middleware/layergroup-token.js | 5 ++-- 4 files changed, 21 insertions(+), 33 deletions(-) diff --git a/lib/cartodb/api/auth_api.js b/lib/cartodb/api/auth_api.js index e9f10262..d27ea3e1 100644 --- a/lib/cartodb/api/auth_api.js +++ b/lib/cartodb/api/auth_api.js @@ -25,7 +25,7 @@ module.exports = AuthApi; // null if the request is not signed by anyone // or will be a string cartodb username otherwise. // -AuthApi.prototype.authorizedBySigner = function(res, callback) { +AuthApi.prototype.authorizedBySigner = function(req, res, callback) { if ( ! res.locals.token || ! res.locals.signer ) { return callback(null, false); // no signer requested } @@ -33,7 +33,7 @@ AuthApi.prototype.authorizedBySigner = function(res, callback) { var self = this; var layergroup_id = res.locals.token; - var auth_token = res.locals.auth_token; + var auth_token = req.query.auth_token; this.mapStore.load(layergroup_id, function(err, mapConfig) { if (err) { @@ -180,7 +180,7 @@ AuthApi.prototype.authorize = function(req, res, callback) { }); } - this.authorizedBySigner(res, (err, isAuthorizedBySigner) => { + this.authorizedBySigner(req, res, (err, isAuthorizedBySigner) => { if (err) { return callback(err); } diff --git a/lib/cartodb/controllers/layergroup.js b/lib/cartodb/controllers/layergroup.js index 0930fbed..da9189ef 100644 --- a/lib/cartodb/controllers/layergroup.js +++ b/lib/cartodb/controllers/layergroup.js @@ -1,7 +1,6 @@ const cors = require('../middleware/cors'); const user = require('../middleware/user'); const vectorError = require('../middleware/vector-error'); -const locals = require('../middleware/locals'); const cleanUpQueryParams = require('../middleware/clean-up-query-params'); const layergroupToken = require('../middleware/layergroup-token'); const credentials = require('../middleware/credentials'); @@ -91,7 +90,6 @@ LayergroupController.prototype.register = function(app) { `${mapConfigBasePath}/:token/:z/:x/:y@:scale_factor?x.:format`, cors(), cleanUpQueryParams(), - locals(), user(), rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.TILE), layergroupToken(), @@ -110,17 +108,16 @@ LayergroupController.prototype.register = function(app) { surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }), lastModifiedHeader(), incrementSuccessMetrics(global.statsClient), - sendResponse(), incrementErrorMetrics(global.statsClient), tileError(), - vectorError() + vectorError(), + sendResponse() ); app.get( `${mapConfigBasePath}/:token/:z/:x/:y.:format`, cors(), cleanUpQueryParams(), - locals(), user(), rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.TILE), layergroupToken(), @@ -139,10 +136,10 @@ LayergroupController.prototype.register = function(app) { surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }), lastModifiedHeader(), incrementSuccessMetrics(global.statsClient), - sendResponse(), incrementErrorMetrics(global.statsClient), tileError(), - vectorError() + vectorError(), + sendResponse() ); app.get( @@ -150,7 +147,6 @@ LayergroupController.prototype.register = function(app) { distinguishLayergroupFromStaticRoute(), cors(), cleanUpQueryParams(), - locals(), user(), rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.TILE), layergroupToken(), @@ -169,17 +165,16 @@ LayergroupController.prototype.register = function(app) { surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }), lastModifiedHeader(), incrementSuccessMetrics(global.statsClient), - sendResponse(), incrementErrorMetrics(global.statsClient), tileError(), - vectorError() + vectorError(), + sendResponse() ); app.get( `${mapConfigBasePath}/:token/:layer/attributes/:fid`, cors(), cleanUpQueryParams(), - locals(), user(), rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.ATTRIBUTES), layergroupToken(), @@ -206,7 +201,6 @@ LayergroupController.prototype.register = function(app) { `${mapConfigBasePath}/static/center/:token/:z/:lat/:lng/:width/:height.:format`, cors(), cleanUpQueryParams(['layer']), - locals(), user(), rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.STATIC), layergroupToken(), @@ -232,7 +226,6 @@ LayergroupController.prototype.register = function(app) { `${mapConfigBasePath}/static/bbox/:token/:west,:south,:east,:north/:width/:height.:format`, cors(), cleanUpQueryParams(['layer']), - locals(), user(), rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.STATIC), layergroupToken(), @@ -261,7 +254,6 @@ LayergroupController.prototype.register = function(app) { `${mapConfigBasePath}/:token/dataview/:dataviewName`, cors(), cleanUpQueryParams(ALLOWED_DATAVIEW_QUERY_PARAMS), - locals(), user(), rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.DATAVIEW), layergroupToken(), @@ -286,7 +278,6 @@ LayergroupController.prototype.register = function(app) { `${mapConfigBasePath}/:token/:layer/widget/:dataviewName`, cors(), cleanUpQueryParams(ALLOWED_DATAVIEW_QUERY_PARAMS), - locals(), user(), rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.DATAVIEW), layergroupToken(), @@ -311,7 +302,6 @@ LayergroupController.prototype.register = function(app) { `${mapConfigBasePath}/:token/dataview/:dataviewName/search`, cors(), cleanUpQueryParams(ALLOWED_DATAVIEW_QUERY_PARAMS), - locals(), user(), rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.DATAVIEW_SEARCH), layergroupToken(), @@ -336,7 +326,6 @@ LayergroupController.prototype.register = function(app) { `${mapConfigBasePath}/:token/:layer/widget/:dataviewName/search`, cors(), cleanUpQueryParams(ALLOWED_DATAVIEW_QUERY_PARAMS), - locals(), user(), rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.DATAVIEW_SEARCH), layergroupToken(), @@ -361,7 +350,6 @@ LayergroupController.prototype.register = function(app) { `${mapConfigBasePath}/:token/analysis/node/:nodeId`, cors(), cleanUpQueryParams(), - locals(), user(), rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.ANALYSIS), layergroupToken(), @@ -521,7 +509,7 @@ function getFeatureAttributes (attributesBackend) { } function getStatusCode(tile, format){ - return tile.length === 0 && format === 'mvt'? 204 : 200; + return tile.length === 0 && format === 'mvt' ? 204 : 200; } function parseFormat (format = '') { @@ -654,6 +642,11 @@ function incrementErrorMetrics (statsClient) { function tileError () { return function tileErrorMiddleware (err, req, res, next) { + if (err.message === 'Tile does not exist' && req.params.format === 'mvt') { + res.statusCode = 204; + return next(); + } + // See https://github.com/Vizzuality/Windshaft-cartodb/issues/68 let errMsg = err.message ? ( '' + err.message ) : ( '' + err ); diff --git a/lib/cartodb/middleware/error-middleware.js b/lib/cartodb/middleware/error-middleware.js index 85f38936..c02974b4 100644 --- a/lib/cartodb/middleware/error-middleware.js +++ b/lib/cartodb/middleware/error-middleware.js @@ -15,10 +15,6 @@ module.exports = function errorMiddleware (/* options */) { var statusCode = findStatusCode(err); - if (err.message === 'Tile does not exist' && res.locals.format === 'mvt') { - statusCode = 204; - } - setErrorHeader(allErrors, statusCode, res); debug('[%s ERROR] -- %d: %s, %s', label, statusCode, err, err.stack); @@ -186,15 +182,15 @@ function setErrorHeader(errors, statusCode, res) { subtype: error.subtype }; }); - + res.set('X-Tiler-Errors', stringifyForLogs(errorsLog)); } /** - * Remove problematic nested characters + * Remove problematic nested characters * from object for logs RegEx - * - * @param {Object} object + * + * @param {Object} object */ function stringifyForLogs(object) { Object.keys(object).map(key => { diff --git a/lib/cartodb/middleware/layergroup-token.js b/lib/cartodb/middleware/layergroup-token.js index 797b1b3d..c3fcec30 100644 --- a/lib/cartodb/middleware/layergroup-token.js +++ b/lib/cartodb/middleware/layergroup-token.js @@ -5,13 +5,12 @@ const authErrorMessageTemplate = function (signer, user) { module.exports = function layergroupToken () { return function layergroupTokenMiddleware (req, res, next) { - if (!res.locals.token) { + if (!req.params.token) { return next(); } const user = res.locals.user; - - const layergroupToken = LayergroupToken.parse(res.locals.token); + const layergroupToken = LayergroupToken.parse(req.params.token); res.locals.token = layergroupToken.token; res.locals.cache_buster = layergroupToken.cacheBuster; From 516b1f765ec8af5efc3e118050084d0ed9a90388 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Fri, 23 Mar 2018 16:08:52 +0100 Subject: [PATCH 23/30] Do not use middleware local in map controller --- lib/cartodb/controllers/map.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/cartodb/controllers/map.js b/lib/cartodb/controllers/map.js index 5eb89b58..9b8694f8 100644 --- a/lib/cartodb/controllers/map.js +++ b/lib/cartodb/controllers/map.js @@ -5,7 +5,6 @@ const Datasource = windshaft.model.Datasource; const ResourceLocator = require('../models/resource-locator'); const cors = require('../middleware/cors'); const user = require('../middleware/user'); -const locals = require('../middleware/locals'); const cleanUpQueryParams = require('../middleware/clean-up-query-params'); const layergroupToken = require('../middleware/layergroup-token'); const credentials = require('../middleware/credentials'); @@ -103,7 +102,6 @@ MapController.prototype.composeCreateMapMiddleware = function (endpointGroup, us return [ cors(), cleanUpQueryParams(['aggregation']), - locals(), user(), rateLimit(this.userLimitsApi, endpointGroup), layergroupToken(), @@ -214,7 +212,7 @@ function checkInstantiteLayergroup () { function checkCreateLayergroup () { return function checkCreateLayergroupMiddleware (req, res, next) { if (req.method === 'GET') { - const { config } = res.locals; + const { config } = req.query; if (!config) { return next(new Error('layergroup GET needs a "config" parameter')); From f7a23c094cc82a77b37ec25d17371dd45500b280 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Fri, 23 Mar 2018 16:16:53 +0100 Subject: [PATCH 24/30] Do not use locals middleware in named maps admin controller --- lib/cartodb/controllers/named_maps_admin.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/cartodb/controllers/named_maps_admin.js b/lib/cartodb/controllers/named_maps_admin.js index 136e9df7..d971aa45 100644 --- a/lib/cartodb/controllers/named_maps_admin.js +++ b/lib/cartodb/controllers/named_maps_admin.js @@ -1,7 +1,6 @@ const { templateName } = require('../backends/template_maps'); const cors = require('../middleware/cors'); const user = require('../middleware/user'); -const locals = require('../middleware/locals'); const credentials = require('../middleware/credentials'); const rateLimit = require('../middleware/rate-limit'); const { RATE_LIMIT_ENDPOINTS_GROUPS } = rateLimit; @@ -27,7 +26,6 @@ NamedMapsAdminController.prototype.register = function (app) { app.post( `${templateBasePath}/`, cors(), - locals(), user(), rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.NAMED_CREATE), credentials(), @@ -40,7 +38,6 @@ NamedMapsAdminController.prototype.register = function (app) { app.put( `${templateBasePath}/:template_id`, cors(), - locals(), user(), rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.NAMED_UPDATE), credentials(), @@ -53,7 +50,6 @@ NamedMapsAdminController.prototype.register = function (app) { app.get( `${templateBasePath}/:template_id`, cors(), - locals(), user(), rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.NAMED_GET), credentials(), @@ -65,7 +61,6 @@ NamedMapsAdminController.prototype.register = function (app) { app.delete( `${templateBasePath}/:template_id`, cors(), - locals(), user(), rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.NAMED_DELETE), credentials(), @@ -77,7 +72,6 @@ NamedMapsAdminController.prototype.register = function (app) { app.get( `${templateBasePath}/`, cors(), - locals(), user(), rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.NAMED_LIST), credentials(), From 5fc801f8a68e5f1faca0c181ba9372ddce5cf5a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Fri, 23 Mar 2018 16:38:55 +0100 Subject: [PATCH 25/30] Do not use locals middleware in named maps controller --- lib/cartodb/controllers/named_maps.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/cartodb/controllers/named_maps.js b/lib/cartodb/controllers/named_maps.js index 5383581c..b0d4cf23 100644 --- a/lib/cartodb/controllers/named_maps.js +++ b/lib/cartodb/controllers/named_maps.js @@ -1,6 +1,5 @@ const cors = require('../middleware/cors'); const user = require('../middleware/user'); -const locals = require('../middleware/locals'); const cleanUpQueryParams = require('../middleware/clean-up-query-params'); const layergroupToken = require('../middleware/layergroup-token'); const credentials = require('../middleware/credentials'); @@ -58,7 +57,6 @@ NamedMapsController.prototype.register = function(app) { `${templateBasePath}/:template_id/:layer/:z/:x/:y.(:format)`, cors(), cleanUpQueryParams(), - locals(), user(), rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.NAMED_TILES), layergroupToken(), @@ -86,7 +84,6 @@ NamedMapsController.prototype.register = function(app) { `${mapconfigBasePath}/static/named/:template_id/:width/:height.:format`, cors(), cleanUpQueryParams(['layer', 'zoom', 'lon', 'lat', 'bbox']), - locals(), user(), rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.STATIC_NAMED), layergroupToken(), @@ -215,9 +212,11 @@ function prepareLayerFilterFromPreviewLayers ({ namedMapProviderCache, label }) function getTile ({ tileBackend, label }) { return function getTileMiddleware (req, res, next) { - const { mapConfigProvider, format } = res.locals; + const { mapConfigProvider } = res.locals; + const { layer, z, x, y, format } = req.params; + const params = { layer, z, x, y, format }; - tileBackend.getTile(mapConfigProvider, req.params, (err, tile, headers, stats) => { + tileBackend.getTile(mapConfigProvider, params, (err, tile, headers, stats) => { req.profiler.add(stats); req.profiler.done('render-' + format); @@ -240,8 +239,10 @@ function getTile ({ tileBackend, label }) { function getStaticImageOptions ({ tablesExtentApi }) { return function getStaticImageOptionsMiddleware(req, res, next) { const { user, mapConfigProvider, template } = res.locals; + const { zoom, lon, lat, bbox } = req.query; + const params = { zoom, lon, lat, bbox }; - const imageOpts = getImageOptions(res.locals, template); + const imageOpts = getImageOptions(params, template); if (imageOpts) { res.locals.imageOpts = imageOpts; From 5bc5c0ae8674bdb76b9e75f9e1e570ef40e858a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Fri, 23 Mar 2018 16:53:00 +0100 Subject: [PATCH 26/30] Remove locals middleware --- lib/cartodb/middleware/locals.js | 7 ------- test/unit/cartodb/prepare-context.test.js | 13 ------------- 2 files changed, 20 deletions(-) delete mode 100644 lib/cartodb/middleware/locals.js diff --git a/lib/cartodb/middleware/locals.js b/lib/cartodb/middleware/locals.js deleted file mode 100644 index 2629767e..00000000 --- a/lib/cartodb/middleware/locals.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = function locals () { - return function localsMiddleware (req, res, next) { - res.locals = Object.assign({}, req.query, req.params); - - next(); - }; -}; diff --git a/test/unit/cartodb/prepare-context.test.js b/test/unit/cartodb/prepare-context.test.js index 82526fe9..ff311cc6 100644 --- a/test/unit/cartodb/prepare-context.test.js +++ b/test/unit/cartodb/prepare-context.test.js @@ -11,7 +11,6 @@ const cleanUpQueryParamsMiddleware = require('../../../lib/cartodb/middleware/cl const authorizeMiddleware = require('../../../lib/cartodb/middleware/authorize'); const dbConnSetupMiddleware = require('../../../lib/cartodb/middleware/db-conn-setup'); const credentialsMiddleware = require('../../../lib/cartodb/middleware/credentials'); -const localsMiddleware = require('../../../lib/cartodb/middleware/locals'); var windshaft = require('windshaft'); @@ -66,18 +65,6 @@ describe('prepare-context', function() { return res; } - it('res.locals are created', function(done) { - const locals = localsMiddleware(); - let req = {}; - let res = {}; - - locals(prepareRequest(req), prepareResponse(res), function(err) { - if ( err ) { done(err); return; } - assert.ok(res.hasOwnProperty('locals'), 'response has locals'); - done(); - }); - }); - it('cleans up request', function(done){ var req = {headers: { host:'localhost' }, query: {dbuser:'hacker',dbname:'secret'}}; var res = {}; From 3b1fd05940b1c17ca7a77ef53b5affa17a700125 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Fri, 23 Mar 2018 17:24:56 +0100 Subject: [PATCH 27/30] Use layergroup token middleware where it's actually needed --- lib/cartodb/controllers/analyses.js | 2 -- lib/cartodb/controllers/map.js | 2 -- lib/cartodb/controllers/named_maps.js | 3 --- lib/cartodb/middleware/layergroup-token.js | 4 ---- 4 files changed, 11 deletions(-) diff --git a/lib/cartodb/controllers/analyses.js b/lib/cartodb/controllers/analyses.js index 81b1757a..9a48e30f 100644 --- a/lib/cartodb/controllers/analyses.js +++ b/lib/cartodb/controllers/analyses.js @@ -2,7 +2,6 @@ const PSQL = require('cartodb-psql'); const cors = require('../middleware/cors'); const user = require('../middleware/user'); const cleanUpQueryParams = require('../middleware/clean-up-query-params'); -const layergroupToken = require('../middleware/layergroup-token'); const credentials = require('../middleware/credentials'); const authorize = require('../middleware/authorize'); const dbConnSetup = require('../middleware/db-conn-setup'); @@ -29,7 +28,6 @@ AnalysesController.prototype.register = function (app) { cleanUpQueryParams(), user(), rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.ANALYSIS_CATALOG), - layergroupToken(), credentials(), authorize(this.authApi), dbConnSetup(this.pgConnection), diff --git a/lib/cartodb/controllers/map.js b/lib/cartodb/controllers/map.js index 9b8694f8..0ec3a160 100644 --- a/lib/cartodb/controllers/map.js +++ b/lib/cartodb/controllers/map.js @@ -6,7 +6,6 @@ const ResourceLocator = require('../models/resource-locator'); const cors = require('../middleware/cors'); const user = require('../middleware/user'); const cleanUpQueryParams = require('../middleware/clean-up-query-params'); -const layergroupToken = require('../middleware/layergroup-token'); const credentials = require('../middleware/credentials'); const dbConnSetup = require('../middleware/db-conn-setup'); const authorize = require('../middleware/authorize'); @@ -104,7 +103,6 @@ MapController.prototype.composeCreateMapMiddleware = function (endpointGroup, us cleanUpQueryParams(['aggregation']), user(), rateLimit(this.userLimitsApi, endpointGroup), - layergroupToken(), credentials(), authorize(this.authApi), dbConnSetup(this.pgConnection), diff --git a/lib/cartodb/controllers/named_maps.js b/lib/cartodb/controllers/named_maps.js index b0d4cf23..f00acb8e 100644 --- a/lib/cartodb/controllers/named_maps.js +++ b/lib/cartodb/controllers/named_maps.js @@ -1,7 +1,6 @@ const cors = require('../middleware/cors'); const user = require('../middleware/user'); const cleanUpQueryParams = require('../middleware/clean-up-query-params'); -const layergroupToken = require('../middleware/layergroup-token'); const credentials = require('../middleware/credentials'); const dbConnSetup = require('../middleware/db-conn-setup'); const authorize = require('../middleware/authorize'); @@ -59,7 +58,6 @@ NamedMapsController.prototype.register = function(app) { cleanUpQueryParams(), user(), rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.NAMED_TILES), - layergroupToken(), credentials(), authorize(this.authApi), dbConnSetup(this.pgConnection), @@ -86,7 +84,6 @@ NamedMapsController.prototype.register = function(app) { cleanUpQueryParams(['layer', 'zoom', 'lon', 'lat', 'bbox']), user(), rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.STATIC_NAMED), - layergroupToken(), credentials(), authorize(this.authApi), dbConnSetup(this.pgConnection), diff --git a/lib/cartodb/middleware/layergroup-token.js b/lib/cartodb/middleware/layergroup-token.js index c3fcec30..1a32e413 100644 --- a/lib/cartodb/middleware/layergroup-token.js +++ b/lib/cartodb/middleware/layergroup-token.js @@ -5,10 +5,6 @@ const authErrorMessageTemplate = function (signer, user) { module.exports = function layergroupToken () { return function layergroupTokenMiddleware (req, res, next) { - if (!req.params.token) { - return next(); - } - const user = res.locals.user; const layergroupToken = LayergroupToken.parse(req.params.token); From 4cba4c7a1f0cb4d0fd36cc18e61c776f455c6043 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Fri, 23 Mar 2018 17:37:06 +0100 Subject: [PATCH 28/30] Tidy middlewares up: cleanUpQeuryParams --- lib/cartodb/controllers/analyses.js | 2 +- lib/cartodb/controllers/layergroup.js | 22 +++++++++++----------- lib/cartodb/controllers/map.js | 2 +- lib/cartodb/controllers/named_maps.js | 4 ++-- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/lib/cartodb/controllers/analyses.js b/lib/cartodb/controllers/analyses.js index 9a48e30f..9ed6e092 100644 --- a/lib/cartodb/controllers/analyses.js +++ b/lib/cartodb/controllers/analyses.js @@ -25,12 +25,12 @@ AnalysesController.prototype.register = function (app) { app.get( `${mapconfigBasePath}/analyses/catalog`, cors(), - cleanUpQueryParams(), user(), rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.ANALYSIS_CATALOG), credentials(), authorize(this.authApi), dbConnSetup(this.pgConnection), + cleanUpQueryParams(), createPGClient(), getDataFromQuery({ queryTemplate: catalogQueryTpl, key: 'catalog' }), getDataFromQuery({ queryTemplate: tablesQueryTpl, key: 'tables' }), diff --git a/lib/cartodb/controllers/layergroup.js b/lib/cartodb/controllers/layergroup.js index da9189ef..fa07f73e 100644 --- a/lib/cartodb/controllers/layergroup.js +++ b/lib/cartodb/controllers/layergroup.js @@ -89,13 +89,13 @@ LayergroupController.prototype.register = function(app) { app.get( `${mapConfigBasePath}/:token/:z/:x/:y@:scale_factor?x.:format`, cors(), - cleanUpQueryParams(), user(), rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.TILE), layergroupToken(), credentials(), authorize(this.authApi), dbConnSetup(this.pgConnection), + cleanUpQueryParams(), createMapStoreMapConfigProvider( this.mapStore, this.userLimitsApi, @@ -117,13 +117,13 @@ LayergroupController.prototype.register = function(app) { app.get( `${mapConfigBasePath}/:token/:z/:x/:y.:format`, cors(), - cleanUpQueryParams(), user(), rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.TILE), layergroupToken(), credentials(), authorize(this.authApi), dbConnSetup(this.pgConnection), + cleanUpQueryParams(), createMapStoreMapConfigProvider( this.mapStore, this.userLimitsApi, @@ -146,13 +146,13 @@ LayergroupController.prototype.register = function(app) { `${mapConfigBasePath}/:token/:layer/:z/:x/:y.(:format)`, distinguishLayergroupFromStaticRoute(), cors(), - cleanUpQueryParams(), user(), rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.TILE), layergroupToken(), credentials(), authorize(this.authApi), dbConnSetup(this.pgConnection), + cleanUpQueryParams(), createMapStoreMapConfigProvider( this.mapStore, this.userLimitsApi, @@ -174,13 +174,13 @@ LayergroupController.prototype.register = function(app) { app.get( `${mapConfigBasePath}/:token/:layer/attributes/:fid`, cors(), - cleanUpQueryParams(), user(), rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.ATTRIBUTES), layergroupToken(), credentials(), authorize(this.authApi), dbConnSetup(this.pgConnection), + cleanUpQueryParams(), createMapStoreMapConfigProvider( this.mapStore, this.userLimitsApi, @@ -200,13 +200,13 @@ LayergroupController.prototype.register = function(app) { app.get( `${mapConfigBasePath}/static/center/:token/:z/:lat/:lng/:width/:height.:format`, cors(), - cleanUpQueryParams(['layer']), user(), rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.STATIC), layergroupToken(), credentials(), authorize(this.authApi), dbConnSetup(this.pgConnection), + cleanUpQueryParams(['layer']), createMapStoreMapConfigProvider( this.mapStore, this.userLimitsApi, @@ -225,13 +225,13 @@ LayergroupController.prototype.register = function(app) { app.get( `${mapConfigBasePath}/static/bbox/:token/:west,:south,:east,:north/:width/:height.:format`, cors(), - cleanUpQueryParams(['layer']), user(), rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.STATIC), layergroupToken(), credentials(), authorize(this.authApi), dbConnSetup(this.pgConnection), + cleanUpQueryParams(['layer']), createMapStoreMapConfigProvider( this.mapStore, this.userLimitsApi, @@ -253,13 +253,13 @@ LayergroupController.prototype.register = function(app) { app.get( `${mapConfigBasePath}/:token/dataview/:dataviewName`, cors(), - cleanUpQueryParams(ALLOWED_DATAVIEW_QUERY_PARAMS), user(), rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.DATAVIEW), layergroupToken(), credentials(), authorize(this.authApi), dbConnSetup(this.pgConnection), + cleanUpQueryParams(ALLOWED_DATAVIEW_QUERY_PARAMS), createMapStoreMapConfigProvider( this.mapStore, this.userLimitsApi, @@ -277,13 +277,13 @@ LayergroupController.prototype.register = function(app) { app.get( `${mapConfigBasePath}/:token/:layer/widget/:dataviewName`, cors(), - cleanUpQueryParams(ALLOWED_DATAVIEW_QUERY_PARAMS), user(), rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.DATAVIEW), layergroupToken(), credentials(), authorize(this.authApi), dbConnSetup(this.pgConnection), + cleanUpQueryParams(ALLOWED_DATAVIEW_QUERY_PARAMS), createMapStoreMapConfigProvider( this.mapStore, this.userLimitsApi, @@ -301,13 +301,13 @@ LayergroupController.prototype.register = function(app) { app.get( `${mapConfigBasePath}/:token/dataview/:dataviewName/search`, cors(), - cleanUpQueryParams(ALLOWED_DATAVIEW_QUERY_PARAMS), user(), rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.DATAVIEW_SEARCH), layergroupToken(), credentials(), authorize(this.authApi), dbConnSetup(this.pgConnection), + cleanUpQueryParams(ALLOWED_DATAVIEW_QUERY_PARAMS), createMapStoreMapConfigProvider( this.mapStore, this.userLimitsApi, @@ -325,13 +325,13 @@ LayergroupController.prototype.register = function(app) { app.get( `${mapConfigBasePath}/:token/:layer/widget/:dataviewName/search`, cors(), - cleanUpQueryParams(ALLOWED_DATAVIEW_QUERY_PARAMS), user(), rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.DATAVIEW_SEARCH), layergroupToken(), credentials(), authorize(this.authApi), dbConnSetup(this.pgConnection), + cleanUpQueryParams(ALLOWED_DATAVIEW_QUERY_PARAMS), createMapStoreMapConfigProvider( this.mapStore, this.userLimitsApi, @@ -349,13 +349,13 @@ LayergroupController.prototype.register = function(app) { app.get( `${mapConfigBasePath}/:token/analysis/node/:nodeId`, cors(), - cleanUpQueryParams(), user(), rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.ANALYSIS), layergroupToken(), credentials(), authorize(this.authApi), dbConnSetup(this.pgConnection), + cleanUpQueryParams(), analysisNodeStatus(this.analysisStatusBackend), sendResponse() ); diff --git a/lib/cartodb/controllers/map.js b/lib/cartodb/controllers/map.js index 0ec3a160..5aea9273 100644 --- a/lib/cartodb/controllers/map.js +++ b/lib/cartodb/controllers/map.js @@ -100,12 +100,12 @@ MapController.prototype.composeCreateMapMiddleware = function (endpointGroup, us return [ cors(), - cleanUpQueryParams(['aggregation']), user(), rateLimit(this.userLimitsApi, endpointGroup), credentials(), authorize(this.authApi), dbConnSetup(this.pgConnection), + cleanUpQueryParams(['aggregation']), initProfiler(isTemplateInstantiation), checkJsonContentType(), this.getCreateMapMiddlewares(useTemplate), diff --git a/lib/cartodb/controllers/named_maps.js b/lib/cartodb/controllers/named_maps.js index f00acb8e..37794c6a 100644 --- a/lib/cartodb/controllers/named_maps.js +++ b/lib/cartodb/controllers/named_maps.js @@ -55,12 +55,12 @@ NamedMapsController.prototype.register = function(app) { app.get( `${templateBasePath}/:template_id/:layer/:z/:x/:y.(:format)`, cors(), - cleanUpQueryParams(), user(), rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.NAMED_TILES), credentials(), authorize(this.authApi), dbConnSetup(this.pgConnection), + cleanUpQueryParams(), getNamedMapProvider({ namedMapProviderCache: this.namedMapProviderCache, label: 'NAMED_MAP_TILE' @@ -81,12 +81,12 @@ NamedMapsController.prototype.register = function(app) { app.get( `${mapconfigBasePath}/static/named/:template_id/:width/:height.:format`, cors(), - cleanUpQueryParams(['layer', 'zoom', 'lon', 'lat', 'bbox']), user(), rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.STATIC_NAMED), credentials(), authorize(this.authApi), dbConnSetup(this.pgConnection), + cleanUpQueryParams(['layer', 'zoom', 'lon', 'lat', 'bbox']), getNamedMapProvider({ namedMapProviderCache: this.namedMapProviderCache, label: 'STATIC_VIZ_MAP', forcedFormat: 'png' From d3e2707fce19339f61abd6f9f105a254f890a689 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Fri, 23 Mar 2018 17:55:41 +0100 Subject: [PATCH 29/30] Tidy middlewares up: put rate limit middleware after authorization --- lib/cartodb/controllers/analyses.js | 2 +- lib/cartodb/controllers/layergroup.js | 22 ++++++++++----------- lib/cartodb/controllers/map.js | 2 +- lib/cartodb/controllers/named_maps.js | 4 ++-- lib/cartodb/controllers/named_maps_admin.js | 10 +++++----- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/lib/cartodb/controllers/analyses.js b/lib/cartodb/controllers/analyses.js index 9ed6e092..2d89a091 100644 --- a/lib/cartodb/controllers/analyses.js +++ b/lib/cartodb/controllers/analyses.js @@ -26,10 +26,10 @@ AnalysesController.prototype.register = function (app) { `${mapconfigBasePath}/analyses/catalog`, cors(), user(), - rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.ANALYSIS_CATALOG), credentials(), authorize(this.authApi), dbConnSetup(this.pgConnection), + rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.ANALYSIS_CATALOG), cleanUpQueryParams(), createPGClient(), getDataFromQuery({ queryTemplate: catalogQueryTpl, key: 'catalog' }), diff --git a/lib/cartodb/controllers/layergroup.js b/lib/cartodb/controllers/layergroup.js index fa07f73e..876630d7 100644 --- a/lib/cartodb/controllers/layergroup.js +++ b/lib/cartodb/controllers/layergroup.js @@ -90,11 +90,11 @@ LayergroupController.prototype.register = function(app) { `${mapConfigBasePath}/:token/:z/:x/:y@:scale_factor?x.:format`, cors(), user(), - rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.TILE), layergroupToken(), credentials(), authorize(this.authApi), dbConnSetup(this.pgConnection), + rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.TILE), cleanUpQueryParams(), createMapStoreMapConfigProvider( this.mapStore, @@ -118,11 +118,11 @@ LayergroupController.prototype.register = function(app) { `${mapConfigBasePath}/:token/:z/:x/:y.:format`, cors(), user(), - rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.TILE), layergroupToken(), credentials(), authorize(this.authApi), dbConnSetup(this.pgConnection), + rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.TILE), cleanUpQueryParams(), createMapStoreMapConfigProvider( this.mapStore, @@ -147,11 +147,11 @@ LayergroupController.prototype.register = function(app) { distinguishLayergroupFromStaticRoute(), cors(), user(), - rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.TILE), layergroupToken(), credentials(), authorize(this.authApi), dbConnSetup(this.pgConnection), + rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.TILE), cleanUpQueryParams(), createMapStoreMapConfigProvider( this.mapStore, @@ -175,11 +175,11 @@ LayergroupController.prototype.register = function(app) { `${mapConfigBasePath}/:token/:layer/attributes/:fid`, cors(), user(), - rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.ATTRIBUTES), layergroupToken(), credentials(), authorize(this.authApi), dbConnSetup(this.pgConnection), + rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.ATTRIBUTES), cleanUpQueryParams(), createMapStoreMapConfigProvider( this.mapStore, @@ -201,11 +201,11 @@ LayergroupController.prototype.register = function(app) { `${mapConfigBasePath}/static/center/:token/:z/:lat/:lng/:width/:height.:format`, cors(), user(), - rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.STATIC), layergroupToken(), credentials(), authorize(this.authApi), dbConnSetup(this.pgConnection), + rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.STATIC), cleanUpQueryParams(['layer']), createMapStoreMapConfigProvider( this.mapStore, @@ -226,11 +226,11 @@ LayergroupController.prototype.register = function(app) { `${mapConfigBasePath}/static/bbox/:token/:west,:south,:east,:north/:width/:height.:format`, cors(), user(), - rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.STATIC), layergroupToken(), credentials(), authorize(this.authApi), dbConnSetup(this.pgConnection), + rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.STATIC), cleanUpQueryParams(['layer']), createMapStoreMapConfigProvider( this.mapStore, @@ -254,11 +254,11 @@ LayergroupController.prototype.register = function(app) { `${mapConfigBasePath}/:token/dataview/:dataviewName`, cors(), user(), - rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.DATAVIEW), layergroupToken(), credentials(), authorize(this.authApi), dbConnSetup(this.pgConnection), + rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.DATAVIEW), cleanUpQueryParams(ALLOWED_DATAVIEW_QUERY_PARAMS), createMapStoreMapConfigProvider( this.mapStore, @@ -278,11 +278,11 @@ LayergroupController.prototype.register = function(app) { `${mapConfigBasePath}/:token/:layer/widget/:dataviewName`, cors(), user(), - rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.DATAVIEW), layergroupToken(), credentials(), authorize(this.authApi), dbConnSetup(this.pgConnection), + rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.DATAVIEW), cleanUpQueryParams(ALLOWED_DATAVIEW_QUERY_PARAMS), createMapStoreMapConfigProvider( this.mapStore, @@ -302,11 +302,11 @@ LayergroupController.prototype.register = function(app) { `${mapConfigBasePath}/:token/dataview/:dataviewName/search`, cors(), user(), - rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.DATAVIEW_SEARCH), layergroupToken(), credentials(), authorize(this.authApi), dbConnSetup(this.pgConnection), + rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.DATAVIEW_SEARCH), cleanUpQueryParams(ALLOWED_DATAVIEW_QUERY_PARAMS), createMapStoreMapConfigProvider( this.mapStore, @@ -326,11 +326,11 @@ LayergroupController.prototype.register = function(app) { `${mapConfigBasePath}/:token/:layer/widget/:dataviewName/search`, cors(), user(), - rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.DATAVIEW_SEARCH), layergroupToken(), credentials(), authorize(this.authApi), dbConnSetup(this.pgConnection), + rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.DATAVIEW_SEARCH), cleanUpQueryParams(ALLOWED_DATAVIEW_QUERY_PARAMS), createMapStoreMapConfigProvider( this.mapStore, @@ -350,11 +350,11 @@ LayergroupController.prototype.register = function(app) { `${mapConfigBasePath}/:token/analysis/node/:nodeId`, cors(), user(), - rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.ANALYSIS), layergroupToken(), credentials(), authorize(this.authApi), dbConnSetup(this.pgConnection), + rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.ANALYSIS), cleanUpQueryParams(), analysisNodeStatus(this.analysisStatusBackend), sendResponse() diff --git a/lib/cartodb/controllers/map.js b/lib/cartodb/controllers/map.js index 5aea9273..11074234 100644 --- a/lib/cartodb/controllers/map.js +++ b/lib/cartodb/controllers/map.js @@ -101,10 +101,10 @@ MapController.prototype.composeCreateMapMiddleware = function (endpointGroup, us return [ cors(), user(), - rateLimit(this.userLimitsApi, endpointGroup), credentials(), authorize(this.authApi), dbConnSetup(this.pgConnection), + rateLimit(this.userLimitsApi, endpointGroup), cleanUpQueryParams(['aggregation']), initProfiler(isTemplateInstantiation), checkJsonContentType(), diff --git a/lib/cartodb/controllers/named_maps.js b/lib/cartodb/controllers/named_maps.js index 37794c6a..2cb4f609 100644 --- a/lib/cartodb/controllers/named_maps.js +++ b/lib/cartodb/controllers/named_maps.js @@ -56,10 +56,10 @@ NamedMapsController.prototype.register = function(app) { `${templateBasePath}/:template_id/:layer/:z/:x/:y.(:format)`, cors(), user(), - rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.NAMED_TILES), credentials(), authorize(this.authApi), dbConnSetup(this.pgConnection), + rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.NAMED_TILES), cleanUpQueryParams(), getNamedMapProvider({ namedMapProviderCache: this.namedMapProviderCache, @@ -82,10 +82,10 @@ NamedMapsController.prototype.register = function(app) { `${mapconfigBasePath}/static/named/:template_id/:width/:height.:format`, cors(), user(), - rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.STATIC_NAMED), credentials(), authorize(this.authApi), dbConnSetup(this.pgConnection), + rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.STATIC_NAMED), cleanUpQueryParams(['layer', 'zoom', 'lon', 'lat', 'bbox']), getNamedMapProvider({ namedMapProviderCache: this.namedMapProviderCache, diff --git a/lib/cartodb/controllers/named_maps_admin.js b/lib/cartodb/controllers/named_maps_admin.js index d971aa45..afcaa1e3 100644 --- a/lib/cartodb/controllers/named_maps_admin.js +++ b/lib/cartodb/controllers/named_maps_admin.js @@ -27,10 +27,10 @@ NamedMapsAdminController.prototype.register = function (app) { `${templateBasePath}/`, cors(), user(), - rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.NAMED_CREATE), credentials(), checkContentType({ action: 'POST', label: 'POST TEMPLATE' }), authorizedByAPIKey({ authApi: this.authApi, action: 'create', label: 'POST TEMPLATE' }), + rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.NAMED_CREATE), createTemplate({ templateMaps: this.templateMaps }), sendResponse() ); @@ -39,10 +39,10 @@ NamedMapsAdminController.prototype.register = function (app) { `${templateBasePath}/:template_id`, cors(), user(), - rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.NAMED_UPDATE), credentials(), checkContentType({ action: 'PUT', label: 'PUT TEMPLATE' }), authorizedByAPIKey({ authApi: this.authApi, action: 'update', label: 'PUT TEMPLATE' }), + rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.NAMED_UPDATE), updateTemplate({ templateMaps: this.templateMaps }), sendResponse() ); @@ -51,9 +51,9 @@ NamedMapsAdminController.prototype.register = function (app) { `${templateBasePath}/:template_id`, cors(), user(), - rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.NAMED_GET), credentials(), authorizedByAPIKey({ authApi: this.authApi, action: 'get', label: 'GET TEMPLATE' }), + rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.NAMED_GET), retrieveTemplate({ templateMaps: this.templateMaps }), sendResponse() ); @@ -62,9 +62,9 @@ NamedMapsAdminController.prototype.register = function (app) { `${templateBasePath}/:template_id`, cors(), user(), - rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.NAMED_DELETE), credentials(), authorizedByAPIKey({ authApi: this.authApi, action: 'delete', label: 'DELETE TEMPLATE' }), + rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.NAMED_DELETE), destroyTemplate({ templateMaps: this.templateMaps }), sendResponse() ); @@ -73,9 +73,9 @@ NamedMapsAdminController.prototype.register = function (app) { `${templateBasePath}/`, cors(), user(), - rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.NAMED_LIST), credentials(), authorizedByAPIKey({ authApi: this.authApi, action: 'list', label: 'GET TEMPLATE LIST' }), + rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.NAMED_LIST), listTemplates({ templateMaps: this.templateMaps }), sendResponse() ); From c5c8dd7ad7d902858bfa8ccb2c0db7c89002f590 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Fri, 23 Mar 2018 21:20:37 +0100 Subject: [PATCH 30/30] Split layergroup controllers into small controllers --- lib/cartodb/controllers/layergroup.js | 665 ------------------ .../controllers/layergroup/analysis.js | 75 ++ .../controllers/layergroup/attributes.js | 93 +++ .../controllers/layergroup/dataview.js | 199 ++++++ lib/cartodb/controllers/layergroup/index.js | 114 +++ .../map-store-map-config-provider.js | 37 + lib/cartodb/controllers/layergroup/static.js | 161 +++++ lib/cartodb/controllers/layergroup/tile.js | 230 ++++++ 8 files changed, 909 insertions(+), 665 deletions(-) delete mode 100644 lib/cartodb/controllers/layergroup.js create mode 100644 lib/cartodb/controllers/layergroup/analysis.js create mode 100644 lib/cartodb/controllers/layergroup/attributes.js create mode 100644 lib/cartodb/controllers/layergroup/dataview.js create mode 100644 lib/cartodb/controllers/layergroup/index.js create mode 100644 lib/cartodb/controllers/layergroup/middlewares/map-store-map-config-provider.js create mode 100644 lib/cartodb/controllers/layergroup/static.js create mode 100644 lib/cartodb/controllers/layergroup/tile.js diff --git a/lib/cartodb/controllers/layergroup.js b/lib/cartodb/controllers/layergroup.js deleted file mode 100644 index 876630d7..00000000 --- a/lib/cartodb/controllers/layergroup.js +++ /dev/null @@ -1,665 +0,0 @@ -const cors = require('../middleware/cors'); -const user = require('../middleware/user'); -const vectorError = require('../middleware/vector-error'); -const cleanUpQueryParams = require('../middleware/clean-up-query-params'); -const layergroupToken = require('../middleware/layergroup-token'); -const credentials = require('../middleware/credentials'); -const dbConnSetup = require('../middleware/db-conn-setup'); -const authorize = require('../middleware/authorize'); -const rateLimit = require('../middleware/rate-limit'); -const { RATE_LIMIT_ENDPOINTS_GROUPS } = rateLimit; -const cacheControlHeader = require('../middleware/cache-control-header'); -const cacheChannelHeader = require('../middleware/cache-channel-header'); -const surrogateKeyHeader = require('../middleware/surrogate-key-header'); -const lastModifiedHeader = require('../middleware/last-modified-header'); -const sendResponse = require('../middleware/send-response'); -const DataviewBackend = require('../backends/dataview'); -const AnalysisStatusBackend = require('../backends/analysis-status'); -const MapStoreMapConfigProvider = require('../models/mapconfig/provider/map-store-provider'); -const dbParamsFromResLocals = require('../utils/database-params'); - -const SUPPORTED_FORMATS = { - grid_json: true, - json_torque: true, - torque_json: true, - png: true, - png32: true, - mvt: true -}; - -const ALLOWED_DATAVIEW_QUERY_PARAMS = [ - 'filters', // json - 'own_filter', // 0, 1 - 'no_filters', // 0, 1 - 'bbox', // w,s,e,n - 'start', // number - 'end', // number - 'column_type', // string - 'bins', // number - 'aggregation', //string - 'offset', // number - 'q', // widgets search - 'categories', // number -]; - -/** - * @param {prepareContext} prepareContext - * @param {PgConnection} pgConnection - * @param {MapStore} mapStore - * @param {TileBackend} tileBackend - * @param {PreviewBackend} previewBackend - * @param {AttributesBackend} attributesBackend - * @param {SurrogateKeysCache} surrogateKeysCache - * @param {UserLimitsApi} userLimitsApi - * @param {LayergroupAffectedTables} layergroupAffectedTables - * @param {AnalysisBackend} analysisBackend - * @constructor - */ -function LayergroupController( - pgConnection, - mapStore, - tileBackend, - previewBackend, - attributesBackend, - surrogateKeysCache, - userLimitsApi, - layergroupAffectedTablesCache, - analysisBackend, - authApi -) { - this.pgConnection = pgConnection; - this.mapStore = mapStore; - this.tileBackend = tileBackend; - this.previewBackend = previewBackend; - this.attributesBackend = attributesBackend; - this.surrogateKeysCache = surrogateKeysCache; - this.userLimitsApi = userLimitsApi; - this.layergroupAffectedTablesCache = layergroupAffectedTablesCache; - - this.dataviewBackend = new DataviewBackend(analysisBackend); - this.analysisStatusBackend = new AnalysisStatusBackend(); - this.authApi = authApi; -} - -module.exports = LayergroupController; - -LayergroupController.prototype.register = function(app) { - const { base_url_mapconfig: mapConfigBasePath } = app; - - app.get( - `${mapConfigBasePath}/:token/:z/:x/:y@:scale_factor?x.:format`, - cors(), - user(), - layergroupToken(), - credentials(), - authorize(this.authApi), - dbConnSetup(this.pgConnection), - rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.TILE), - cleanUpQueryParams(), - createMapStoreMapConfigProvider( - this.mapStore, - this.userLimitsApi, - this.pgConnection, - this.layergroupAffectedTablesCache - ), - getTile(this.tileBackend, 'map_tile'), - cacheControlHeader(), - cacheChannelHeader(), - surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }), - lastModifiedHeader(), - incrementSuccessMetrics(global.statsClient), - incrementErrorMetrics(global.statsClient), - tileError(), - vectorError(), - sendResponse() - ); - - app.get( - `${mapConfigBasePath}/:token/:z/:x/:y.:format`, - cors(), - user(), - layergroupToken(), - credentials(), - authorize(this.authApi), - dbConnSetup(this.pgConnection), - rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.TILE), - cleanUpQueryParams(), - createMapStoreMapConfigProvider( - this.mapStore, - this.userLimitsApi, - this.pgConnection, - this.layergroupAffectedTablesCache - ), - getTile(this.tileBackend, 'map_tile'), - cacheControlHeader(), - cacheChannelHeader(), - surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }), - lastModifiedHeader(), - incrementSuccessMetrics(global.statsClient), - incrementErrorMetrics(global.statsClient), - tileError(), - vectorError(), - sendResponse() - ); - - app.get( - `${mapConfigBasePath}/:token/:layer/:z/:x/:y.(:format)`, - distinguishLayergroupFromStaticRoute(), - cors(), - user(), - layergroupToken(), - credentials(), - authorize(this.authApi), - dbConnSetup(this.pgConnection), - rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.TILE), - cleanUpQueryParams(), - createMapStoreMapConfigProvider( - this.mapStore, - this.userLimitsApi, - this.pgConnection, - this.layergroupAffectedTablesCache - ), - getTile(this.tileBackend, 'maplayer_tile'), - cacheControlHeader(), - cacheChannelHeader(), - surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }), - lastModifiedHeader(), - incrementSuccessMetrics(global.statsClient), - incrementErrorMetrics(global.statsClient), - tileError(), - vectorError(), - sendResponse() - ); - - app.get( - `${mapConfigBasePath}/:token/:layer/attributes/:fid`, - cors(), - user(), - layergroupToken(), - credentials(), - authorize(this.authApi), - dbConnSetup(this.pgConnection), - rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.ATTRIBUTES), - cleanUpQueryParams(), - createMapStoreMapConfigProvider( - this.mapStore, - this.userLimitsApi, - this.pgConnection, - this.layergroupAffectedTablesCache - ), - getFeatureAttributes(this.attributesBackend), - cacheControlHeader(), - cacheChannelHeader(), - surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }), - lastModifiedHeader(), - sendResponse() - ); - - const forcedFormat = 'png'; - - app.get( - `${mapConfigBasePath}/static/center/:token/:z/:lat/:lng/:width/:height.:format`, - cors(), - user(), - layergroupToken(), - credentials(), - authorize(this.authApi), - dbConnSetup(this.pgConnection), - rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.STATIC), - cleanUpQueryParams(['layer']), - createMapStoreMapConfigProvider( - this.mapStore, - this.userLimitsApi, - this.pgConnection, - this.layergroupAffectedTablesCache, - forcedFormat - ), - getPreviewImageByCenter(this.previewBackend), - cacheControlHeader(), - cacheChannelHeader(), - surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }), - lastModifiedHeader(), - sendResponse() - ); - - app.get( - `${mapConfigBasePath}/static/bbox/:token/:west,:south,:east,:north/:width/:height.:format`, - cors(), - user(), - layergroupToken(), - credentials(), - authorize(this.authApi), - dbConnSetup(this.pgConnection), - rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.STATIC), - cleanUpQueryParams(['layer']), - createMapStoreMapConfigProvider( - this.mapStore, - this.userLimitsApi, - this.pgConnection, - this.layergroupAffectedTablesCache, - forcedFormat - ), - getPreviewImageByBoundingBox(this.previewBackend), - cacheControlHeader(), - cacheChannelHeader(), - surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }), - lastModifiedHeader(), - sendResponse() - ); - - // Undocumented/non-supported API endpoint methods. - // Use at your own peril. - - app.get( - `${mapConfigBasePath}/:token/dataview/:dataviewName`, - cors(), - user(), - layergroupToken(), - credentials(), - authorize(this.authApi), - dbConnSetup(this.pgConnection), - rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.DATAVIEW), - cleanUpQueryParams(ALLOWED_DATAVIEW_QUERY_PARAMS), - createMapStoreMapConfigProvider( - this.mapStore, - this.userLimitsApi, - this.pgConnection, - this.layergroupAffectedTablesCache - ), - getDataview(this.dataviewBackend), - cacheControlHeader(), - cacheChannelHeader(), - surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }), - lastModifiedHeader(), - sendResponse() - ); - - app.get( - `${mapConfigBasePath}/:token/:layer/widget/:dataviewName`, - cors(), - user(), - layergroupToken(), - credentials(), - authorize(this.authApi), - dbConnSetup(this.pgConnection), - rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.DATAVIEW), - cleanUpQueryParams(ALLOWED_DATAVIEW_QUERY_PARAMS), - createMapStoreMapConfigProvider( - this.mapStore, - this.userLimitsApi, - this.pgConnection, - this.layergroupAffectedTablesCache - ), - getDataview(this.dataviewBackend), - cacheControlHeader(), - cacheChannelHeader(), - surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }), - lastModifiedHeader(), - sendResponse() - ); - - app.get( - `${mapConfigBasePath}/:token/dataview/:dataviewName/search`, - cors(), - user(), - layergroupToken(), - credentials(), - authorize(this.authApi), - dbConnSetup(this.pgConnection), - rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.DATAVIEW_SEARCH), - cleanUpQueryParams(ALLOWED_DATAVIEW_QUERY_PARAMS), - createMapStoreMapConfigProvider( - this.mapStore, - this.userLimitsApi, - this.pgConnection, - this.layergroupAffectedTablesCache - ), - dataviewSearch(this.dataviewBackend), - cacheControlHeader(), - cacheChannelHeader(), - surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }), - lastModifiedHeader(), - sendResponse() - ); - - app.get( - `${mapConfigBasePath}/:token/:layer/widget/:dataviewName/search`, - cors(), - user(), - layergroupToken(), - credentials(), - authorize(this.authApi), - dbConnSetup(this.pgConnection), - rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.DATAVIEW_SEARCH), - cleanUpQueryParams(ALLOWED_DATAVIEW_QUERY_PARAMS), - createMapStoreMapConfigProvider( - this.mapStore, - this.userLimitsApi, - this.pgConnection, - this.layergroupAffectedTablesCache - ), - dataviewSearch(this.dataviewBackend), - cacheControlHeader(), - cacheChannelHeader(), - surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }), - lastModifiedHeader(), - sendResponse() - ); - - app.get( - `${mapConfigBasePath}/:token/analysis/node/:nodeId`, - cors(), - user(), - layergroupToken(), - credentials(), - authorize(this.authApi), - dbConnSetup(this.pgConnection), - rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.ANALYSIS), - cleanUpQueryParams(), - analysisNodeStatus(this.analysisStatusBackend), - sendResponse() - ); -}; - -function distinguishLayergroupFromStaticRoute () { - return function distinguishLayergroupFromStaticRouteMiddleware(req, res, next) { - if (req.params.token === 'static') { - return next('route'); - } - - next(); - }; -} - -function analysisNodeStatus (analysisStatusBackend) { - return function analysisNodeStatusMiddleware(req, res, next) { - const { nodeId } = req.params; - const dbParams = dbParamsFromResLocals(res.locals); - - analysisStatusBackend.getNodeStatus(nodeId, dbParams, (err, nodeStatus, stats = {}) => { - req.profiler.add(stats); - - if (err) { - err.label = 'GET NODE STATUS'; - return next(err); - } - - res.set({ - 'Cache-Control': 'public,max-age=5', - 'Last-Modified': new Date().toUTCString() - }); - - res.body = nodeStatus; - - next(); - }); - }; -} - -function createMapStoreMapConfigProvider ( - mapStore, - userLimitsApi, - pgConnection, - affectedTablesCache, - forcedFormat = null -) { - return function createMapStoreMapConfigProviderMiddleware (req, res, next) { - const { user, token, cache_buster, api_key } = res.locals; - const { dbuser, dbname, dbpassword, dbhost, dbport } = res.locals; - const { layer, z, x, y, scale_factor, format } = req.params; - - const params = { - user, token, cache_buster, api_key, - dbuser, dbname, dbpassword, dbhost, dbport, - layer, z, x, y, scale_factor, format - }; - - if (forcedFormat) { - params.format = forcedFormat; - params.layer = params.layer || 'all'; - } - - res.locals.mapConfigProvider = new MapStoreMapConfigProvider( - mapStore, - user, - userLimitsApi, - pgConnection, - affectedTablesCache, - params - ); - - next(); - }; -} - -function getDataview (dataviewBackend) { - return function getDataviewMiddleware (req, res, next) { - const { user, mapConfigProvider } = res.locals; - const { dataviewName } = req.params; - const { dbuser, dbname, dbpassword, dbhost, dbport } = res.locals; - - const params = Object.assign({ dataviewName, dbuser, dbname, dbpassword, dbhost, dbport }, req.query); - - dataviewBackend.getDataview(mapConfigProvider, user, params, (err, dataview, stats = {}) => { - req.profiler.add(stats); - - if (err) { - err.label = 'GET DATAVIEW'; - return next(err); - } - - res.body = dataview; - - next(); - }); - }; -} - -function dataviewSearch (dataviewBackend) { - return function dataviewSearchMiddleware (req, res, next) { - const { user, mapConfigProvider } = res.locals; - const { dataviewName } = req.params; - const { dbuser, dbname, dbpassword, dbhost, dbport } = res.locals; - - const params = Object.assign({ dbuser, dbname, dbpassword, dbhost, dbport }, req.query); - - dataviewBackend.search(mapConfigProvider, user, dataviewName, params, (err, searchResult, stats = {}) => { - req.profiler.add(stats); - - if (err) { - err.label = 'GET DATAVIEW SEARCH'; - return next(err); - } - - res.body = searchResult; - - next(); - }); - }; -} - -function getFeatureAttributes (attributesBackend) { - return function getFeatureAttributesMiddleware (req, res, next) { - req.profiler.start('windshaft.maplayer_attribute'); - - const { mapConfigProvider } = res.locals; - const { token } = res.locals; - const { dbuser, dbname, dbpassword, dbhost, dbport } = res.locals; - const { layer, fid } = req.params; - - const params = { - token, - dbuser, dbname, dbpassword, dbhost, dbport, - layer, fid - }; - - attributesBackend.getFeatureAttributes(mapConfigProvider, params, false, (err, tile, stats = {}) => { - req.profiler.add(stats); - - if (err) { - err.label = 'GET ATTRIBUTES'; - return next(err); - } - - res.body = tile; - - next(); - }); - }; -} - -function getStatusCode(tile, format){ - return tile.length === 0 && format === 'mvt' ? 204 : 200; -} - -function parseFormat (format = '') { - const prettyFormat = format.replace('.', '_'); - return SUPPORTED_FORMATS[prettyFormat] ? prettyFormat : 'invalid'; -} - -function getTile (tileBackend, profileLabel = 'tile') { - return function getTileMiddleware (req, res, next) { - req.profiler.start(`windshaft.${profileLabel}`); - - const { mapConfigProvider } = res.locals; - const { token } = res.locals; - const { layer, z, x, y, format } = req.params; - - const params = { token, layer, z, x, y, format }; - - tileBackend.getTile(mapConfigProvider, params, (err, tile, headers, stats = {}) => { - req.profiler.add(stats); - - if (err) { - return next(err); - } - - if (headers) { - res.set(headers); - } - - const formatStat = parseFormat(req.params.format); - - res.statusCode = getStatusCode(tile, formatStat); - res.body = tile; - - next(); - }); - }; -} - -function getPreviewImageByCenter (previewBackend) { - return function getPreviewImageByCenterMiddleware (req, res, next) { - const width = +req.params.width; - const height = +req.params.height; - const zoom = +req.params.z; - const center = { - lng: +req.params.lng, - lat: +req.params.lat - }; - - const format = req.params.format === 'jpg' ? 'jpeg' : 'png'; - const { mapConfigProvider: provider } = res.locals; - - previewBackend.getImage(provider, format, width, height, zoom, center, (err, image, headers, stats = {}) => { - req.profiler.done(`render-${format}`); - req.profiler.add(stats); - - if (err) { - err.label = 'STATIC_MAP'; - return next(err); - } - - if (headers) { - res.set(headers); - } - - res.set('Content-Type', headers['Content-Type'] || `image/${format}`); - - res.body = image; - - next(); - }); - }; -} - -function getPreviewImageByBoundingBox (previewBackend) { - return function getPreviewImageByBoundingBoxMiddleware (req, res, next) { - const width = +req.params.width; - const height = +req.params.height; - const bounds = { - west: +req.params.west, - north: +req.params.north, - east: +req.params.east, - south: +req.params.south - }; - const format = req.params.format === 'jpg' ? 'jpeg' : 'png'; - const { mapConfigProvider: provider } = res.locals; - - previewBackend.getImage(provider, format, width, height, bounds, (err, image, headers, stats = {}) => { - req.profiler.done(`render-${format}`); - req.profiler.add(stats); - - if (err) { - err.label = 'STATIC_MAP'; - return next(err); - } - - if (headers) { - res.set(headers); - } - - res.set('Content-Type', headers['Content-Type'] || `image/${format}`); - - res.body = image; - - next(); - }); - }; -} - -function incrementSuccessMetrics (statsClient) { - return function incrementSuccessMetricsMiddleware (req, res, next) { - const formatStat = parseFormat(req.params.format); - - statsClient.increment('windshaft.tiles.success'); - statsClient.increment(`windshaft.tiles.${formatStat}.success`); - - next(); - }; -} - -function incrementErrorMetrics (statsClient) { - return function incrementErrorMetricsMiddleware (err, req, res, next) { - const formatStat = parseFormat(req.params.format); - - statsClient.increment('windshaft.tiles.error'); - statsClient.increment(`windshaft.tiles.${formatStat}.error`); - - next(err); - }; -} - -function tileError () { - return function tileErrorMiddleware (err, req, res, next) { - if (err.message === 'Tile does not exist' && req.params.format === 'mvt') { - res.statusCode = 204; - return next(); - } - - // See https://github.com/Vizzuality/Windshaft-cartodb/issues/68 - let errMsg = err.message ? ( '' + err.message ) : ( '' + err ); - - // Rewrite mapnik parsing errors to start with layer number - const matches = errMsg.match("(.*) in style 'layer([0-9]+)'"); - - if (matches) { - errMsg = `style${matches[2]}: ${matches[1]}`; - } - - err.message = errMsg; - err.label = 'TILE RENDER'; - - next(err); - }; -} diff --git a/lib/cartodb/controllers/layergroup/analysis.js b/lib/cartodb/controllers/layergroup/analysis.js new file mode 100644 index 00000000..3022bce3 --- /dev/null +++ b/lib/cartodb/controllers/layergroup/analysis.js @@ -0,0 +1,75 @@ +const cors = require('../../middleware/cors'); +const user = require('../../middleware/user'); +const layergroupToken = require('../../middleware/layergroup-token'); +const cleanUpQueryParams = require('../../middleware/clean-up-query-params'); +const credentials = require('../../middleware/credentials'); +const dbConnSetup = require('../../middleware/db-conn-setup'); +const authorize = require('../../middleware/authorize'); +const rateLimit = require('../../middleware/rate-limit'); +const { RATE_LIMIT_ENDPOINTS_GROUPS } = rateLimit; +const sendResponse = require('../../middleware/send-response'); +const dbParamsFromResLocals = require('../../utils/database-params'); + +module.exports = class AnalysisController { + constructor ( + analysisStatusBackend, + pgConnection, + mapStore, + userLimitsApi, + layergroupAffectedTablesCache, + authApi, + surrogateKeysCache + ) { + this.analysisStatusBackend = analysisStatusBackend; + this.pgConnection = pgConnection; + this.mapStore = mapStore; + this.userLimitsApi = userLimitsApi; + this.layergroupAffectedTablesCache = layergroupAffectedTablesCache; + this.authApi = authApi; + this.surrogateKeysCache = surrogateKeysCache; + } + + register (app) { + const { base_url_mapconfig: mapConfigBasePath } = app; + + app.get( + `${mapConfigBasePath}/:token/analysis/node/:nodeId`, + cors(), + user(), + layergroupToken(), + credentials(), + authorize(this.authApi), + dbConnSetup(this.pgConnection), + rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.ANALYSIS), + cleanUpQueryParams(), + analysisNodeStatus(this.analysisStatusBackend), + sendResponse() + ); + + } +}; + +function analysisNodeStatus (analysisStatusBackend) { + return function analysisNodeStatusMiddleware(req, res, next) { + const { nodeId } = req.params; + const dbParams = dbParamsFromResLocals(res.locals); + + analysisStatusBackend.getNodeStatus(nodeId, dbParams, (err, nodeStatus, stats = {}) => { + req.profiler.add(stats); + + if (err) { + err.label = 'GET NODE STATUS'; + return next(err); + } + + res.set({ + 'Cache-Control': 'public,max-age=5', + 'Last-Modified': new Date().toUTCString() + }); + + res.body = nodeStatus; + + next(); + }); + }; +} diff --git a/lib/cartodb/controllers/layergroup/attributes.js b/lib/cartodb/controllers/layergroup/attributes.js new file mode 100644 index 00000000..07ae5da1 --- /dev/null +++ b/lib/cartodb/controllers/layergroup/attributes.js @@ -0,0 +1,93 @@ +const cors = require('../../middleware/cors'); +const user = require('../../middleware/user'); +const layergroupToken = require('../../middleware/layergroup-token'); +const cleanUpQueryParams = require('../../middleware/clean-up-query-params'); +const credentials = require('../../middleware/credentials'); +const dbConnSetup = require('../../middleware/db-conn-setup'); +const authorize = require('../../middleware/authorize'); +const rateLimit = require('../../middleware/rate-limit'); +const { RATE_LIMIT_ENDPOINTS_GROUPS } = rateLimit; +const createMapStoreMapConfigProvider = require('./middlewares/map-store-map-config-provider'); +const cacheControlHeader = require('../../middleware/cache-control-header'); +const cacheChannelHeader = require('../../middleware/cache-channel-header'); +const surrogateKeyHeader = require('../../middleware/surrogate-key-header'); +const lastModifiedHeader = require('../../middleware/last-modified-header'); +const sendResponse = require('../../middleware/send-response'); + +module.exports = class AttribitesController { + constructor ( + attributesBackend, + pgConnection, + mapStore, + userLimitsApi, + layergroupAffectedTablesCache, + authApi, + surrogateKeysCache + ) { + this.attributesBackend = attributesBackend; + this.pgConnection = pgConnection; + this.mapStore = mapStore; + this.userLimitsApi = userLimitsApi; + this.layergroupAffectedTablesCache = layergroupAffectedTablesCache; + this.authApi = authApi; + this.surrogateKeysCache = surrogateKeysCache; + } + + register (app) { + const { base_url_mapconfig: mapConfigBasePath } = app; + + app.get( + `${mapConfigBasePath}/:token/:layer/attributes/:fid`, + cors(), + user(), + layergroupToken(), + credentials(), + authorize(this.authApi), + dbConnSetup(this.pgConnection), + rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.ATTRIBUTES), + cleanUpQueryParams(), + createMapStoreMapConfigProvider( + this.mapStore, + this.userLimitsApi, + this.pgConnection, + this.layergroupAffectedTablesCache + ), + getFeatureAttributes(this.attributesBackend), + cacheControlHeader(), + cacheChannelHeader(), + surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }), + lastModifiedHeader(), + sendResponse() + ); + } +}; + +function getFeatureAttributes (attributesBackend) { + return function getFeatureAttributesMiddleware (req, res, next) { + req.profiler.start('windshaft.maplayer_attribute'); + + const { mapConfigProvider } = res.locals; + const { token } = res.locals; + const { dbuser, dbname, dbpassword, dbhost, dbport } = res.locals; + const { layer, fid } = req.params; + + const params = { + token, + dbuser, dbname, dbpassword, dbhost, dbport, + layer, fid + }; + + attributesBackend.getFeatureAttributes(mapConfigProvider, params, false, (err, tile, stats = {}) => { + req.profiler.add(stats); + + if (err) { + err.label = 'GET ATTRIBUTES'; + return next(err); + } + + res.body = tile; + + next(); + }); + }; +} diff --git a/lib/cartodb/controllers/layergroup/dataview.js b/lib/cartodb/controllers/layergroup/dataview.js new file mode 100644 index 00000000..bca27f1e --- /dev/null +++ b/lib/cartodb/controllers/layergroup/dataview.js @@ -0,0 +1,199 @@ +const cors = require('../../middleware/cors'); +const user = require('../../middleware/user'); +const layergroupToken = require('../../middleware/layergroup-token'); +const cleanUpQueryParams = require('../../middleware/clean-up-query-params'); +const credentials = require('../../middleware/credentials'); +const dbConnSetup = require('../../middleware/db-conn-setup'); +const authorize = require('../../middleware/authorize'); +const rateLimit = require('../../middleware/rate-limit'); +const { RATE_LIMIT_ENDPOINTS_GROUPS } = rateLimit; +const createMapStoreMapConfigProvider = require('./middlewares/map-store-map-config-provider'); +const cacheControlHeader = require('../../middleware/cache-control-header'); +const cacheChannelHeader = require('../../middleware/cache-channel-header'); +const surrogateKeyHeader = require('../../middleware/surrogate-key-header'); +const lastModifiedHeader = require('../../middleware/last-modified-header'); +const sendResponse = require('../../middleware/send-response'); + +const ALLOWED_DATAVIEW_QUERY_PARAMS = [ + 'filters', // json + 'own_filter', // 0, 1 + 'no_filters', // 0, 1 + 'bbox', // w,s,e,n + 'start', // number + 'end', // number + 'column_type', // string + 'bins', // number + 'aggregation', //string + 'offset', // number + 'q', // widgets search + 'categories', // number +]; + +module.exports = class DataviewController { + constructor ( + dataviewBackend, + pgConnection, + mapStore, + userLimitsApi, + layergroupAffectedTablesCache, + authApi, + surrogateKeysCache + ) { + this.dataviewBackend = dataviewBackend; + this.pgConnection = pgConnection; + this.mapStore = mapStore; + this.userLimitsApi = userLimitsApi; + this.layergroupAffectedTablesCache = layergroupAffectedTablesCache; + this.authApi = authApi; + this.surrogateKeysCache = surrogateKeysCache; + } + + register (app) { + const { base_url_mapconfig: mapConfigBasePath } = app; + + // Undocumented/non-supported API endpoint methods. + // Use at your own peril. + + app.get( + `${mapConfigBasePath}/:token/dataview/:dataviewName`, + cors(), + user(), + layergroupToken(), + credentials(), + authorize(this.authApi), + dbConnSetup(this.pgConnection), + rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.DATAVIEW), + cleanUpQueryParams(ALLOWED_DATAVIEW_QUERY_PARAMS), + createMapStoreMapConfigProvider( + this.mapStore, + this.userLimitsApi, + this.pgConnection, + this.layergroupAffectedTablesCache + ), + getDataview(this.dataviewBackend), + cacheControlHeader(), + cacheChannelHeader(), + surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }), + lastModifiedHeader(), + sendResponse() + ); + + app.get( + `${mapConfigBasePath}/:token/:layer/widget/:dataviewName`, + cors(), + user(), + layergroupToken(), + credentials(), + authorize(this.authApi), + dbConnSetup(this.pgConnection), + rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.DATAVIEW), + cleanUpQueryParams(ALLOWED_DATAVIEW_QUERY_PARAMS), + createMapStoreMapConfigProvider( + this.mapStore, + this.userLimitsApi, + this.pgConnection, + this.layergroupAffectedTablesCache + ), + getDataview(this.dataviewBackend), + cacheControlHeader(), + cacheChannelHeader(), + surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }), + lastModifiedHeader(), + sendResponse() + ); + + app.get( + `${mapConfigBasePath}/:token/dataview/:dataviewName/search`, + cors(), + user(), + layergroupToken(), + credentials(), + authorize(this.authApi), + dbConnSetup(this.pgConnection), + rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.DATAVIEW_SEARCH), + cleanUpQueryParams(ALLOWED_DATAVIEW_QUERY_PARAMS), + createMapStoreMapConfigProvider( + this.mapStore, + this.userLimitsApi, + this.pgConnection, + this.layergroupAffectedTablesCache + ), + dataviewSearch(this.dataviewBackend), + cacheControlHeader(), + cacheChannelHeader(), + surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }), + lastModifiedHeader(), + sendResponse() + ); + + app.get( + `${mapConfigBasePath}/:token/:layer/widget/:dataviewName/search`, + cors(), + user(), + layergroupToken(), + credentials(), + authorize(this.authApi), + dbConnSetup(this.pgConnection), + rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.DATAVIEW_SEARCH), + cleanUpQueryParams(ALLOWED_DATAVIEW_QUERY_PARAMS), + createMapStoreMapConfigProvider( + this.mapStore, + this.userLimitsApi, + this.pgConnection, + this.layergroupAffectedTablesCache + ), + dataviewSearch(this.dataviewBackend), + cacheControlHeader(), + cacheChannelHeader(), + surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }), + lastModifiedHeader(), + sendResponse() + ); + } +}; + +function getDataview (dataviewBackend) { + return function getDataviewMiddleware (req, res, next) { + const { user, mapConfigProvider } = res.locals; + const { dataviewName } = req.params; + const { dbuser, dbname, dbpassword, dbhost, dbport } = res.locals; + + const params = Object.assign({ dataviewName, dbuser, dbname, dbpassword, dbhost, dbport }, req.query); + + dataviewBackend.getDataview(mapConfigProvider, user, params, (err, dataview, stats = {}) => { + req.profiler.add(stats); + + if (err) { + err.label = 'GET DATAVIEW'; + return next(err); + } + + res.body = dataview; + + next(); + }); + }; +} + +function dataviewSearch (dataviewBackend) { + return function dataviewSearchMiddleware (req, res, next) { + const { user, mapConfigProvider } = res.locals; + const { dataviewName } = req.params; + const { dbuser, dbname, dbpassword, dbhost, dbport } = res.locals; + + const params = Object.assign({ dbuser, dbname, dbpassword, dbhost, dbport }, req.query); + + dataviewBackend.search(mapConfigProvider, user, dataviewName, params, (err, searchResult, stats = {}) => { + req.profiler.add(stats); + + if (err) { + err.label = 'GET DATAVIEW SEARCH'; + return next(err); + } + + res.body = searchResult; + + next(); + }); + }; +} diff --git a/lib/cartodb/controllers/layergroup/index.js b/lib/cartodb/controllers/layergroup/index.js new file mode 100644 index 00000000..e4c1770c --- /dev/null +++ b/lib/cartodb/controllers/layergroup/index.js @@ -0,0 +1,114 @@ +const DataviewBackend = require('../../backends/dataview'); +const AnalysisStatusBackend = require('../../backends/analysis-status'); + +const TileController = require('./tile'); +const AttributesController = require('./attributes'); +const StaticController = require('./static'); +const DataviewController = require('./dataview'); +const AnalysisController = require('./analysis'); + +/** + * @param {prepareContext} prepareContext + * @param {PgConnection} pgConnection + * @param {MapStore} mapStore + * @param {TileBackend} tileBackend + * @param {PreviewBackend} previewBackend + * @param {AttributesBackend} attributesBackend + * @param {SurrogateKeysCache} surrogateKeysCache + * @param {UserLimitsApi} userLimitsApi + * @param {LayergroupAffectedTables} layergroupAffectedTables + * @param {AnalysisBackend} analysisBackend + * @constructor + */ +function LayergroupController( + pgConnection, + mapStore, + tileBackend, + previewBackend, + attributesBackend, + surrogateKeysCache, + userLimitsApi, + layergroupAffectedTablesCache, + analysisBackend, + authApi +) { + this.pgConnection = pgConnection; + this.mapStore = mapStore; + this.tileBackend = tileBackend; + this.previewBackend = previewBackend; + this.attributesBackend = attributesBackend; + this.surrogateKeysCache = surrogateKeysCache; + this.userLimitsApi = userLimitsApi; + this.layergroupAffectedTablesCache = layergroupAffectedTablesCache; + + this.dataviewBackend = new DataviewBackend(analysisBackend); + this.analysisStatusBackend = new AnalysisStatusBackend(); + this.authApi = authApi; +} + +module.exports = LayergroupController; + +LayergroupController.prototype.register = function(app) { + + const tileController = new TileController( + this.tileBackend, + this.pgConnection, + this.mapStore, + this.userLimitsApi, + this.layergroupAffectedTablesCache, + this.authApi, + this.surrogateKeysCache + ); + + tileController.register(app); + + const attributesController = new AttributesController( + this.attributesBackend, + this.pgConnection, + this.mapStore, + this.userLimitsApi, + this.layergroupAffectedTablesCache, + this.authApi, + this.surrogateKeysCache + ); + + attributesController.register(app); + + const staticController = new StaticController( + this.previewBackend, + this.pgConnection, + this.mapStore, + this.userLimitsApi, + this.layergroupAffectedTablesCache, + this.authApi, + this.surrogateKeysCache + ); + + staticController.register(app); + + const dataviewController = new DataviewController( + this.dataviewBackend, + this.pgConnection, + this.mapStore, + this.userLimitsApi, + this.layergroupAffectedTablesCache, + this.authApi, + this.surrogateKeysCache + ); + + dataviewController.register(app); + + const analysisController = new AnalysisController( + this.analysisStatusBackend, + this.pgConnection, + this.mapStore, + this.userLimitsApi, + this.layergroupAffectedTablesCache, + this.authApi, + this.surrogateKeysCache + ); + + analysisController.register(app); + + +}; diff --git a/lib/cartodb/controllers/layergroup/middlewares/map-store-map-config-provider.js b/lib/cartodb/controllers/layergroup/middlewares/map-store-map-config-provider.js new file mode 100644 index 00000000..7d64cb01 --- /dev/null +++ b/lib/cartodb/controllers/layergroup/middlewares/map-store-map-config-provider.js @@ -0,0 +1,37 @@ +const MapStoreMapConfigProvider = require('../../../models/mapconfig/provider/map-store-provider'); + +module.exports = function createMapStoreMapConfigProvider ( + mapStore, + userLimitsApi, + pgConnection, + affectedTablesCache, + forcedFormat = null +) { + return function createMapStoreMapConfigProviderMiddleware (req, res, next) { + const { user, token, cache_buster, api_key } = res.locals; + const { dbuser, dbname, dbpassword, dbhost, dbport } = res.locals; + const { layer, z, x, y, scale_factor, format } = req.params; + + const params = { + user, token, cache_buster, api_key, + dbuser, dbname, dbpassword, dbhost, dbport, + layer, z, x, y, scale_factor, format + }; + + if (forcedFormat) { + params.format = forcedFormat; + params.layer = params.layer || 'all'; + } + + res.locals.mapConfigProvider = new MapStoreMapConfigProvider( + mapStore, + user, + userLimitsApi, + pgConnection, + affectedTablesCache, + params + ); + + next(); + }; +}; diff --git a/lib/cartodb/controllers/layergroup/static.js b/lib/cartodb/controllers/layergroup/static.js new file mode 100644 index 00000000..00510780 --- /dev/null +++ b/lib/cartodb/controllers/layergroup/static.js @@ -0,0 +1,161 @@ +const cors = require('../../middleware/cors'); +const user = require('../../middleware/user'); +const layergroupToken = require('../../middleware/layergroup-token'); +const cleanUpQueryParams = require('../../middleware/clean-up-query-params'); +const credentials = require('../../middleware/credentials'); +const dbConnSetup = require('../../middleware/db-conn-setup'); +const authorize = require('../../middleware/authorize'); +const rateLimit = require('../../middleware/rate-limit'); +const { RATE_LIMIT_ENDPOINTS_GROUPS } = rateLimit; +const createMapStoreMapConfigProvider = require('./middlewares/map-store-map-config-provider'); +const cacheControlHeader = require('../../middleware/cache-control-header'); +const cacheChannelHeader = require('../../middleware/cache-channel-header'); +const surrogateKeyHeader = require('../../middleware/surrogate-key-header'); +const lastModifiedHeader = require('../../middleware/last-modified-header'); +const sendResponse = require('../../middleware/send-response'); + +module.exports = class StaticController { + constructor ( + previewBackend, + pgConnection, + mapStore, + userLimitsApi, + layergroupAffectedTablesCache, + authApi, + surrogateKeysCache + ) { + this.previewBackend = previewBackend; + this.pgConnection = pgConnection; + this.mapStore = mapStore; + this.userLimitsApi = userLimitsApi; + this.layergroupAffectedTablesCache = layergroupAffectedTablesCache; + this.authApi = authApi; + this.surrogateKeysCache = surrogateKeysCache; + } + + register (app) { + const { base_url_mapconfig: mapConfigBasePath } = app; + + const forcedFormat = 'png'; + + app.get( + `${mapConfigBasePath}/static/center/:token/:z/:lat/:lng/:width/:height.:format`, + cors(), + user(), + layergroupToken(), + credentials(), + authorize(this.authApi), + dbConnSetup(this.pgConnection), + rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.STATIC), + cleanUpQueryParams(['layer']), + createMapStoreMapConfigProvider( + this.mapStore, + this.userLimitsApi, + this.pgConnection, + this.layergroupAffectedTablesCache, + forcedFormat + ), + getPreviewImageByCenter(this.previewBackend), + cacheControlHeader(), + cacheChannelHeader(), + surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }), + lastModifiedHeader(), + sendResponse() + ); + + app.get( + `${mapConfigBasePath}/static/bbox/:token/:west,:south,:east,:north/:width/:height.:format`, + cors(), + user(), + layergroupToken(), + credentials(), + authorize(this.authApi), + dbConnSetup(this.pgConnection), + rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.STATIC), + cleanUpQueryParams(['layer']), + createMapStoreMapConfigProvider( + this.mapStore, + this.userLimitsApi, + this.pgConnection, + this.layergroupAffectedTablesCache, + forcedFormat + ), + getPreviewImageByBoundingBox(this.previewBackend), + cacheControlHeader(), + cacheChannelHeader(), + surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }), + lastModifiedHeader(), + sendResponse() + ); + } +}; + +function getPreviewImageByCenter (previewBackend) { + return function getPreviewImageByCenterMiddleware (req, res, next) { + const width = +req.params.width; + const height = +req.params.height; + const zoom = +req.params.z; + const center = { + lng: +req.params.lng, + lat: +req.params.lat + }; + + const format = req.params.format === 'jpg' ? 'jpeg' : 'png'; + const { mapConfigProvider: provider } = res.locals; + + previewBackend.getImage(provider, format, width, height, zoom, center, (err, image, headers, stats = {}) => { + req.profiler.done(`render-${format}`); + req.profiler.add(stats); + + if (err) { + err.label = 'STATIC_MAP'; + return next(err); + } + + if (headers) { + res.set(headers); + } + + res.set('Content-Type', headers['Content-Type'] || `image/${format}`); + + res.body = image; + + next(); + }); + }; +} + +function getPreviewImageByBoundingBox (previewBackend) { + return function getPreviewImageByBoundingBoxMiddleware (req, res, next) { + const width = +req.params.width; + const height = +req.params.height; + const bounds = { + west: +req.params.west, + north: +req.params.north, + east: +req.params.east, + south: +req.params.south + }; + const format = req.params.format === 'jpg' ? 'jpeg' : 'png'; + const { mapConfigProvider: provider } = res.locals; + + previewBackend.getImage(provider, format, width, height, bounds, (err, image, headers, stats = {}) => { + req.profiler.done(`render-${format}`); + req.profiler.add(stats); + + if (err) { + err.label = 'STATIC_MAP'; + return next(err); + } + + if (headers) { + res.set(headers); + } + + res.set('Content-Type', headers['Content-Type'] || `image/${format}`); + + res.body = image; + + next(); + }); + }; +} diff --git a/lib/cartodb/controllers/layergroup/tile.js b/lib/cartodb/controllers/layergroup/tile.js new file mode 100644 index 00000000..07727236 --- /dev/null +++ b/lib/cartodb/controllers/layergroup/tile.js @@ -0,0 +1,230 @@ +const cors = require('../../middleware/cors'); +const user = require('../../middleware/user'); +const layergroupToken = require('../../middleware/layergroup-token'); +const cleanUpQueryParams = require('../../middleware/clean-up-query-params'); +const credentials = require('../../middleware/credentials'); +const dbConnSetup = require('../../middleware/db-conn-setup'); +const authorize = require('../../middleware/authorize'); +const rateLimit = require('../../middleware/rate-limit'); +const { RATE_LIMIT_ENDPOINTS_GROUPS } = rateLimit; +const createMapStoreMapConfigProvider = require('./middlewares/map-store-map-config-provider'); +const cacheControlHeader = require('../../middleware/cache-control-header'); +const cacheChannelHeader = require('../../middleware/cache-channel-header'); +const surrogateKeyHeader = require('../../middleware/surrogate-key-header'); +const lastModifiedHeader = require('../../middleware/last-modified-header'); +const sendResponse = require('../../middleware/send-response'); +const vectorError = require('../../middleware/vector-error'); + +const SUPPORTED_FORMATS = { + grid_json: true, + json_torque: true, + torque_json: true, + png: true, + png32: true, + mvt: true +}; + +module.exports = class TileController { + constructor ( + tileBackend, + pgConnection, + mapStore, + userLimitsApi, + layergroupAffectedTablesCache, + authApi, + surrogateKeysCache + ) { + this.tileBackend = tileBackend; + this.pgConnection = pgConnection; + this.mapStore = mapStore; + this.userLimitsApi = userLimitsApi; + this.layergroupAffectedTablesCache = layergroupAffectedTablesCache; + this.authApi = authApi; + this.surrogateKeysCache = surrogateKeysCache; + } + + register (app) { + const { base_url_mapconfig: mapConfigBasePath } = app; + + app.get( + `${mapConfigBasePath}/:token/:z/:x/:y@:scale_factor?x.:format`, + cors(), + user(), + layergroupToken(), + credentials(), + authorize(this.authApi), + dbConnSetup(this.pgConnection), + rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.TILE), + cleanUpQueryParams(), + createMapStoreMapConfigProvider( + this.mapStore, + this.userLimitsApi, + this.pgConnection, + this.layergroupAffectedTablesCache + ), + getTile(this.tileBackend, 'map_tile'), + cacheControlHeader(), + cacheChannelHeader(), + surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }), + lastModifiedHeader(), + incrementSuccessMetrics(global.statsClient), + incrementErrorMetrics(global.statsClient), + tileError(), + vectorError(), + sendResponse() + ); + + app.get( + `${mapConfigBasePath}/:token/:z/:x/:y.:format`, + cors(), + user(), + layergroupToken(), + credentials(), + authorize(this.authApi), + dbConnSetup(this.pgConnection), + rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.TILE), + cleanUpQueryParams(), + createMapStoreMapConfigProvider( + this.mapStore, + this.userLimitsApi, + this.pgConnection, + this.layergroupAffectedTablesCache + ), + getTile(this.tileBackend, 'map_tile'), + cacheControlHeader(), + cacheChannelHeader(), + surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }), + lastModifiedHeader(), + incrementSuccessMetrics(global.statsClient), + incrementErrorMetrics(global.statsClient), + tileError(), + vectorError(), + sendResponse() + ); + + app.get( + `${mapConfigBasePath}/:token/:layer/:z/:x/:y.(:format)`, + distinguishLayergroupFromStaticRoute(), + cors(), + user(), + layergroupToken(), + credentials(), + authorize(this.authApi), + dbConnSetup(this.pgConnection), + rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.TILE), + cleanUpQueryParams(), + createMapStoreMapConfigProvider( + this.mapStore, + this.userLimitsApi, + this.pgConnection, + this.layergroupAffectedTablesCache + ), + getTile(this.tileBackend, 'maplayer_tile'), + cacheControlHeader(), + cacheChannelHeader(), + surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }), + lastModifiedHeader(), + incrementSuccessMetrics(global.statsClient), + incrementErrorMetrics(global.statsClient), + tileError(), + vectorError(), + sendResponse() + ); + } +}; + +function distinguishLayergroupFromStaticRoute () { + return function distinguishLayergroupFromStaticRouteMiddleware(req, res, next) { + if (req.params.token === 'static') { + return next('route'); + } + + next(); + }; +} + +function parseFormat (format = '') { + const prettyFormat = format.replace('.', '_'); + return SUPPORTED_FORMATS[prettyFormat] ? prettyFormat : 'invalid'; +} + +function getStatusCode(tile, format){ + return tile.length === 0 && format === 'mvt' ? 204 : 200; +} + +function getTile (tileBackend, profileLabel = 'tile') { + return function getTileMiddleware (req, res, next) { + req.profiler.start(`windshaft.${profileLabel}`); + + const { mapConfigProvider } = res.locals; + const { token } = res.locals; + const { layer, z, x, y, format } = req.params; + + const params = { token, layer, z, x, y, format }; + + tileBackend.getTile(mapConfigProvider, params, (err, tile, headers, stats = {}) => { + req.profiler.add(stats); + + if (err) { + return next(err); + } + + if (headers) { + res.set(headers); + } + + const formatStat = parseFormat(req.params.format); + + res.statusCode = getStatusCode(tile, formatStat); + res.body = tile; + + next(); + }); + }; +} + +function incrementSuccessMetrics (statsClient) { + return function incrementSuccessMetricsMiddleware (req, res, next) { + const formatStat = parseFormat(req.params.format); + + statsClient.increment('windshaft.tiles.success'); + statsClient.increment(`windshaft.tiles.${formatStat}.success`); + + next(); + }; +} + +function incrementErrorMetrics (statsClient) { + return function incrementErrorMetricsMiddleware (err, req, res, next) { + const formatStat = parseFormat(req.params.format); + + statsClient.increment('windshaft.tiles.error'); + statsClient.increment(`windshaft.tiles.${formatStat}.error`); + + next(err); + }; +} + +function tileError () { + return function tileErrorMiddleware (err, req, res, next) { + if (err.message === 'Tile does not exist' && req.params.format === 'mvt') { + res.statusCode = 204; + return next(); + } + + // See https://github.com/Vizzuality/Windshaft-cartodb/issues/68 + let errMsg = err.message ? ( '' + err.message ) : ( '' + err ); + + // Rewrite mapnik parsing errors to start with layer number + const matches = errMsg.match("(.*) in style 'layer([0-9]+)'"); + + if (matches) { + errMsg = `style${matches[2]}: ${matches[1]}`; + } + + err.message = errMsg; + err.label = 'TILE RENDER'; + + next(err); + }; +}