BaseController to encapsulate req2params method

All controllers now extending BaseController
- Most of the acceptance ported tests will be broken
This commit is contained in:
Raul Ochoa 2015-09-16 16:18:26 +02:00
parent 1c6571d1db
commit 352dc6b311
6 changed files with 201 additions and 138 deletions

View File

@ -0,0 +1,150 @@
var assert = require('assert');
var _ = require('underscore');
var step = require('step');
var LZMA = require('lzma').LZMA;
var lzmaWorker = new LZMA();
// Whitelist query parameters and attach format
var REQUEST_QUERY_PARAMS_WHITELIST = [
'config',
'map_key',
'api_key',
'auth_token',
'callback'
];
function BaseController(authApi, pgConnection) {
this.authApi = authApi;
this.pgConnection = pgConnection;
}
module.exports = BaseController;
// jshint maxcomplexity:9
/**
* Whitelist input and get database name & default geometry type from
* subdomain/user metadata held in CartoDB Redis
* @param req - standard express request obj. Should have host & table
* @param callback
*/
BaseController.prototype.req2params = function(req, callback){
var self = this;
if ( req.query.lzma ) {
// Decode (from base64)
var lzma = new Buffer(req.query.lzma, 'base64')
.toString('binary')
.split('')
.map(function(c) {
return c.charCodeAt(0) - 128;
});
// Decompress
lzmaWorker.decompress(
lzma,
function(result) {
if (req.profiler) {
req.profiler.done('lzma');
}
try {
delete req.query.lzma;
_.extend(req.query, JSON.parse(result));
self.req2params(req, callback);
} catch (err) {
req.profiler.done('req2params');
callback(new Error('Error parsing lzma as JSON: ' + err));
}
}
);
return;
}
req.query = _.pick(req.query, REQUEST_QUERY_PARAMS_WHITELIST);
req.params = _.extend({}, req.params); // shuffle things as request is a strange array/object
var user = req.context.user;
if ( req.params.token ) {
// Token might match the following patterns:
// - {user}@{tpl_id}@{token}:{cache_buster}
//console.log("Request parameters include token " + req.params.token);
var tksplit = req.params.token.split(':');
req.params.token = tksplit[0];
if ( tksplit.length > 1 ) {
req.params.cache_buster= tksplit[1];
}
tksplit = req.params.token.split('@');
if ( tksplit.length > 1 ) {
req.params.signer = tksplit.shift();
if ( ! req.params.signer ) {
req.params.signer = user;
}
else if ( req.params.signer !== user ) {
var err = new Error(
'Cannot use map signature of user "' + req.params.signer + '" on db of user "' + user + '"'
);
err.http_status = 403;
req.profiler.done('req2params');
callback(err);
return;
}
if ( tksplit.length > 1 ) {
/*var template_hash = */tksplit.shift(); // unused
}
req.params.token = tksplit.shift();
//console.log("Request for token " + req.params.token + " with signature from " + req.params.signer);
}
}
// bring all query values onto req.params object
_.extend(req.params, req.query);
if (req.profiler) {
req.profiler.done('req2params.setup');
}
step(
function getPrivacy(){
self.authApi.authorize(req, this);
},
function validateAuthorization(err, authorized) {
if (req.profiler) {
req.profiler.done('authorize');
}
assert.ifError(err);
if(!authorized) {
err = new Error("Sorry, you are unauthorized (permission denied)");
err.http_status = 403;
throw err;
}
return null;
},
function getDatabase(err){
assert.ifError(err);
self.pgConnection.setDBConn(user, req.params, this);
},
function finishSetup(err) {
if ( err ) {
req.profiler.done('req2params');
return callback(err, req);
}
// Add default database connection parameters
// if none given
_.defaults(req.params, {
dbuser: global.environment.postgres.user,
dbpassword: global.environment.postgres.password,
dbhost: global.environment.postgres.host,
dbport: global.environment.postgres.port
});
req.profiler.done('req2params');
callback(null, req);
}
);
};
// jshint maxcomplexity:6

View File

@ -1,6 +1,9 @@
var assert = require('assert');
var step = require('step');
var util = require('util');
var BaseController = require('./base');
var cors = require('../middleware/cors');
var MapStoreMapConfigProvider = require('../models/mapconfig/map_store_provider');
@ -8,6 +11,8 @@ var TablesCacheEntry = require('../cache/model/database_tables_entry');
/**
* @param app
* @param {AuthApi} authApi
* @param {PgConnection} pgConnection
* @param {MapStore} mapStore
* @param {TileBackend} tileBackend
* @param {PreviewBackend} previewBackend
@ -18,8 +23,10 @@ var TablesCacheEntry = require('../cache/model/database_tables_entry');
* @param {LayergroupAffectedTables} layergroupAffectedTables
* @constructor
*/
function LayergroupController(app, mapStore, tileBackend, previewBackend, attributesBackend, surrogateKeysCache,
userLimitsApi, queryTablesApi, layergroupAffectedTables) {
function LayergroupController(app, authApi, pgConnection, mapStore, tileBackend, previewBackend, attributesBackend,
surrogateKeysCache, userLimitsApi, queryTablesApi, layergroupAffectedTables) {
BaseController.call(this, authApi, pgConnection);
this.app = app;
this.mapStore = mapStore;
this.tileBackend = tileBackend;
@ -31,6 +38,8 @@ function LayergroupController(app, mapStore, tileBackend, previewBackend, attrib
this.layergroupAffectedTables = layergroupAffectedTables;
}
util.inherits(LayergroupController, BaseController);
module.exports = LayergroupController;
@ -52,7 +61,7 @@ LayergroupController.prototype.attributes = function(req, res) {
step(
function setupParams() {
self.app.req2params(req, this);
self.req2params(req, this);
},
function retrieveFeatureAttributes(err) {
assert.ifError(err);
@ -95,7 +104,7 @@ LayergroupController.prototype.tileOrLayer = function (req, res) {
step(
function mapController$prepareParams() {
self.app.req2params(req, this);
self.req2params(req, this);
},
function mapController$getTileOrGrid(err) {
assert.ifError(err);
@ -181,7 +190,7 @@ LayergroupController.prototype.staticMap = function(req, res, width, height, zoo
step(
function() {
self.app.req2params(req, this);
self.req2params(req, this);
},
function(err) {
assert.ifError(err);

View File

@ -3,6 +3,9 @@ var assert = require('assert');
var step = require('step');
var windshaft = require('windshaft');
var util = require('util');
var BaseController = require('./base');
var cors = require('../middleware/cors');
var MapConfig = windshaft.model.MapConfig;
@ -17,6 +20,7 @@ var CreateLayergroupMapConfigProvider = require('../models/mapconfig/create_laye
/**
* @param app
* @param {AuthApi} authApi
* @param {PgConnection} pgConnection
* @param {TemplateMaps} templateMaps
* @param {MapBackend} mapBackend
@ -27,8 +31,11 @@ var CreateLayergroupMapConfigProvider = require('../models/mapconfig/create_laye
* @param {LayergroupAffectedTables} layergroupAffectedTables
* @constructor
*/
function MapController(app, pgConnection, templateMaps, mapBackend, metadataBackend, queryTablesApi,
function MapController(app, authApi, pgConnection, templateMaps, mapBackend, metadataBackend, queryTablesApi,
surrogateKeysCache, userLimitsApi, layergroupAffectedTables) {
BaseController.call(this, authApi, pgConnection);
this.app = app;
this.pgConnection = pgConnection;
this.templateMaps = templateMaps;
@ -42,6 +49,8 @@ function MapController(app, pgConnection, templateMaps, mapBackend, metadataBack
this.namedLayersAdapter = new MapConfigNamedLayersAdapter(templateMaps);
}
util.inherits(MapController, BaseController);
module.exports = MapController;
@ -121,7 +130,7 @@ MapController.prototype.create = function(req, res, prepareConfigFn) {
step(
function setupParams(){
self.app.req2params(req, this);
self.req2params(req, this);
},
prepareConfigFn,
function beforeLayergroupCreate(err, requestMapConfig) {
@ -174,7 +183,7 @@ MapController.prototype.instantiateTemplate = function(req, res, prepareParamsFn
step(
function setupParams(){
self.app.req2params(req, this);
self.req2params(req, this);
},
function getTemplateParams() {
prepareParamsFn(this);

View File

@ -2,12 +2,18 @@ var step = require('step');
var assert = require('assert');
var _ = require('underscore');
var NamedMapsCacheEntry = require('../cache/model/named_maps_entry');
var util = require('util');
var BaseController = require('./base');
var cors = require('../middleware/cors');
var TablesCacheEntry = require('../cache/model/database_tables_entry');
function NamedMapsController(app, namedMapProviderCache, tileBackend, previewBackend, surrogateKeysCache,
function NamedMapsController(app, authApi, pgConnection, namedMapProviderCache, tileBackend, previewBackend, surrogateKeysCache,
tablesExtentApi) {
BaseController.call(this, authApi, pgConnection);
this.app = app;
this.namedMapProviderCache = namedMapProviderCache;
this.tileBackend = tileBackend;
@ -16,6 +22,8 @@ function NamedMapsController(app, namedMapProviderCache, tileBackend, previewBac
this.tablesExtentApi = tablesExtentApi;
}
util.inherits(NamedMapsController, BaseController);
module.exports = NamedMapsController;
NamedMapsController.prototype.register = function(app) {
@ -73,7 +81,7 @@ NamedMapsController.prototype.tile = function(req, res) {
var namedMapProvider;
step(
function reqParams() {
self.app.req2params(req, this);
self.req2params(req, this);
},
function getTile() {
namedMapProvider = self.namedMapProviderCache.get(
@ -110,7 +118,7 @@ NamedMapsController.prototype.staticMap = function(req, res) {
var namedMapProvider;
step(
function reqParams() {
self.app.req2params(req, this);
self.req2params(req, this);
},
function getTemplate(err) {
assert.ifError(err);

View File

@ -1,15 +1,22 @@
var step = require('step');
var assert = require('assert');
var templateName = require('../backends/template_maps').templateName;
var util = require('util');
var BaseController = require('./base');
var cors = require('../middleware/cors');
/**
* @param {TemplateMaps} templateMaps
* @param {AuthApi} authApi
* @param {PgConnection} pgConnection
* @constructor
*/
function NamedMapsAdminController(templateMaps, authApi) {
function NamedMapsAdminController(templateMaps, authApi, pgConnection) {
BaseController.call(this, authApi, pgConnection);
this.templateMaps = templateMaps;
this.authApi = authApi;
}

View File

@ -178,6 +178,8 @@ module.exports = function(serverOptions) {
new controller.Layergroup(
app,
authApi,
pgConnection,
mapStore,
tileBackend,
previewBackend,
@ -190,6 +192,7 @@ module.exports = function(serverOptions) {
new controller.Map(
app,
authApi,
pgConnection,
templateMaps,
mapBackend,
@ -202,6 +205,8 @@ module.exports = function(serverOptions) {
new controller.NamedMaps(
app,
authApi,
pgConnection,
namedMapProviderCache,
tileBackend,
previewBackend,
@ -209,7 +214,7 @@ module.exports = function(serverOptions) {
tablesExtentApi
).register(app);
new controller.NamedMapsAdmin(templateMaps, authApi).register(app);
new controller.NamedMapsAdmin(templateMaps, authApi, pgConnection).register(app);
new controller.ServerInfo().register(app);
@ -217,131 +222,6 @@ module.exports = function(serverOptions) {
* END Routing
******************************************************************************************************************/
// jshint maxcomplexity:10
/**
* Whitelist input and get database name & default geometry type from
* subdomain/user metadata held in CartoDB Redis
* @param req - standard express request obj. Should have host & table
* @param callback
*/
app.req2params = function(req, callback){
if ( req.query.lzma ) {
// Decode (from base64)
var lzma = new Buffer(req.query.lzma, 'base64')
.toString('binary')
.split('')
.map(function(c) {
return c.charCodeAt(0) - 128;
});
// Decompress
lzmaWorker.decompress(
lzma,
function(result) {
if (req.profiler) {
req.profiler.done('lzma');
}
try {
delete req.query.lzma;
_.extend(req.query, JSON.parse(result));
app.req2params(req, callback);
} catch (err) {
req.profiler.done('req2params');
callback(new Error('Error parsing lzma as JSON: ' + err));
}
}
);
return;
}
req.query = _.pick(req.query, REQUEST_QUERY_PARAMS_WHITELIST);
req.params = _.extend({}, req.params); // shuffle things as request is a strange array/object
var user = req.context.user;
if ( req.params.token ) {
// Token might match the following patterns:
// - {user}@{tpl_id}@{token}:{cache_buster}
//console.log("Request parameters include token " + req.params.token);
var tksplit = req.params.token.split(':');
req.params.token = tksplit[0];
if ( tksplit.length > 1 ) {
req.params.cache_buster= tksplit[1];
}
tksplit = req.params.token.split('@');
if ( tksplit.length > 1 ) {
req.params.signer = tksplit.shift();
if ( ! req.params.signer ) {
req.params.signer = user;
}
else if ( req.params.signer !== user ) {
var err = new Error(
'Cannot use map signature of user "' + req.params.signer + '" on db of user "' + user + '"'
);
err.http_status = 403;
req.profiler.done('req2params');
callback(err);
return;
}
if ( tksplit.length > 1 ) {
/*var template_hash = */tksplit.shift(); // unused
}
req.params.token = tksplit.shift();
//console.log("Request for token " + req.params.token + " with signature from " + req.params.signer);
}
}
// bring all query values onto req.params object
_.extend(req.params, req.query);
if (req.profiler) {
req.profiler.done('req2params.setup');
}
step(
function getPrivacy(){
authApi.authorize(req, this);
},
function validateAuthorization(err, authorized) {
if (req.profiler) {
req.profiler.done('authorize');
}
assert.ifError(err);
if(!authorized) {
err = new Error("Sorry, you are unauthorized (permission denied)");
err.http_status = 403;
throw err;
}
return null;
},
function getDatabase(err){
assert.ifError(err);
pgConnection.setDBConn(user, req.params, this);
},
function finishSetup(err) {
if ( err ) {
req.profiler.done('req2params');
return callback(err, req);
}
// Add default database connection parameters
// if none given
_.defaults(req.params, {
dbuser: global.environment.postgres.user,
dbpassword: global.environment.postgres.password,
dbhost: global.environment.postgres.host,
dbport: global.environment.postgres.port
});
req.profiler.done('req2params');
callback(null, req);
}
);
};
// jshint maxcomplexity:6
return app;
};