diff --git a/lib/cartodb/controllers/base.js b/lib/cartodb/controllers/base.js index 90934b7d..ab6587c2 100644 --- a/lib/cartodb/controllers/base.js +++ b/lib/cartodb/controllers/base.js @@ -36,7 +36,7 @@ function BaseController(authApi, pgConnection) { module.exports = BaseController; -// jshint maxcomplexity:9 +// jshint maxcomplexity:10 /** * Whitelist input and get database name & default geometry type from * subdomain/user metadata held in CartoDB Redis @@ -75,7 +75,11 @@ BaseController.prototype.req2params = function(req, callback){ return; } - req.query = _.pick(req.query, REQUEST_QUERY_PARAMS_WHITELIST); + var allowedQueryParams = REQUEST_QUERY_PARAMS_WHITELIST; + if (Array.isArray(req.context.allowedQueryParams)) { + allowedQueryParams = allowedQueryParams.concat(req.context.allowedQueryParams); + } + req.query = _.pick(req.query, allowedQueryParams); req.params = _.extend({}, req.params); // shuffle things as request is a strange array/object var user = req.context.user; diff --git a/lib/cartodb/controllers/layergroup.js b/lib/cartodb/controllers/layergroup.js index f7215943..4119655f 100644 --- a/lib/cartodb/controllers/layergroup.js +++ b/lib/cartodb/controllers/layergroup.js @@ -6,6 +6,7 @@ var BaseController = require('./base'); var cors = require('../middleware/cors'); var userMiddleware = require('../middleware/user'); +var allowQueryParams = require('../middleware/allow-query-params'); var DataviewBackend = require('../backends/dataview'); var AnalysisStatusBackend = require('../backends/analysis-status'); @@ -67,11 +68,13 @@ LayergroupController.prototype.register = function(app) { this.attributes.bind(this)); app.get(app.base_url_mapconfig + - '/static/center/:token/:z/:lat/:lng/:width/:height.:format', cors(), userMiddleware, + '/static/center/:token/:z/:lat/:lng/:width/:height.:format', + cors(), userMiddleware, allowQueryParams(['layer']), this.center.bind(this)); app.get(app.base_url_mapconfig + - '/static/bbox/:token/:west,:south,:east,:north/:width/:height.:format', cors(), userMiddleware, + '/static/bbox/:token/:west,:south,:east,:north/:width/:height.:format', + cors(), userMiddleware, allowQueryParams(['layer']), this.bbox.bind(this)); // Undocumented/non-supported API endpoint methods. diff --git a/lib/cartodb/controllers/named_maps.js b/lib/cartodb/controllers/named_maps.js index f8f9b89c..bde3827d 100644 --- a/lib/cartodb/controllers/named_maps.js +++ b/lib/cartodb/controllers/named_maps.js @@ -8,6 +8,7 @@ var BaseController = require('./base'); var cors = require('../middleware/cors'); var userMiddleware = require('../middleware/user'); +var allowQueryParams = require('../middleware/allow-query-params'); function NamedMapsController(authApi, pgConnection, namedMapProviderCache, tileBackend, previewBackend, surrogateKeysCache, tablesExtentApi, metadataBackend) { @@ -31,7 +32,7 @@ NamedMapsController.prototype.register = function(app) { this.tile.bind(this)); app.get(app.base_url_mapconfig + - '/static/named/:template_id/:width/:height.:format', cors(), userMiddleware, + '/static/named/:template_id/:width/:height.:format', cors(), userMiddleware, allowQueryParams(['layer']), this.staticMap.bind(this)); }; diff --git a/lib/cartodb/middleware/allow-query-params.js b/lib/cartodb/middleware/allow-query-params.js new file mode 100644 index 00000000..04a27033 --- /dev/null +++ b/lib/cartodb/middleware/allow-query-params.js @@ -0,0 +1,9 @@ +module.exports = function allowQueryParams(params) { + if (!Array.isArray(params)) { + throw new Error('allowQueryParams must receive an Array of params'); + } + return function allowQueryParamsMiddleware(req, res, next) { + req.context.allowedQueryParams = params; + next(); + }; +}; diff --git a/test/acceptance/named_maps_static_view.js b/test/acceptance/named_maps_static_view.js index 91026226..c7ddff2f 100644 --- a/test/acceptance/named_maps_static_view.js +++ b/test/acceptance/named_maps_static_view.js @@ -21,7 +21,7 @@ describe('named maps static view', function() { var IMAGE_TOLERANCE = 20; - function createTemplate(view) { + function createTemplate(view, layers) { return { version: '0.0.1', name: templateName, @@ -36,7 +36,7 @@ describe('named maps static view', function() { }, view: view, layergroup: { - layers: [ + layers: layers || [ { type: 'mapnik', options: { @@ -198,4 +198,43 @@ describe('named maps static view', function() { }); }); + it('should allow to select the layers to render', function (done) { + var view = { + bounds: { + west: 0, + south: 0, + east: 45, + north: 45 + } + }; + + var layers = [ + { + type: 'mapnik', + options: { + sql: 'select * from populated_places_simple_reduced', + cartocss: '#layer { marker-fill: <%= color %>; }', + cartocss_version: '2.3.0' + } + }, + { + type: 'mapnik', + options: { + sql: 'select ST_Transform(ST_MakeEnvelope(-45, -45, 45, 45, 4326), 3857) the_geom_webmercator', + cartocss: '#layer { polygon-fill: <%= color %>; }', + cartocss_version: '2.3.0' + } + } + ]; + templateMaps.addTemplate(username, createTemplate(view, layers), function (err) { + if (err) { + return done(err); + } + getStaticMap({ layer: 0 }, function(err, img) { + assert.ok(!err); + assert.imageIsSimilarToFile(img, previewFixture('bounds'), IMAGE_TOLERANCE, done); + }); + }); + }); + });