diff --git a/lib/cartodb/server_options.js b/lib/cartodb/server_options.js index 518b4f1b..25456154 100644 --- a/lib/cartodb/server_options.js +++ b/lib/cartodb/server_options.js @@ -250,7 +250,7 @@ module.exports = function(){ me.afterLayergroupCreate = function(req, mapconfig, response, callback) { var token = response.layergroupid; - var username = cartoData.userFromHostname(req.headers.host); + var username = this.dbOwnerByReq(req); // cartoData.userFromHostname(req.headers.host); var tasksleft = 2; // redis key and affectedTables var errors = []; @@ -305,69 +305,94 @@ module.exports = function(){ /* X-Cache-Channel generation } */ + me.dbOwnerByReq = function(req) { + return cartoData.userFromHostname(req.headers.host); + } + + // Set db authentication parameters to those of the given username + // + // @param username the cartodb username, mapped to a database username + // via CartodbRedis metadata records + // + // @param params the parameters to set auth options into + // added params are: "dbuser" and "dbpassword" + // + // @param callback function(err) + // + me.setDBAuth = function(username, params, callback) { + + var user_params = {}; + var auth_user = global.environment.postgres_auth_user; + var auth_pass = global.environment.postgres_auth_pass; + Step( + function getId() { + cartoData.getUserId(username, this); + }, + function(err, user_id) { + if (err) throw err; + user_params['user_id'] = user_id; + var dbuser = _.template(auth_user, user_params); + _.extend(params, {dbuser:dbuser}); + + // skip looking up user_password if postgres_auth_pass + // doesn't contain the "user_password" label + if (!auth_pass || ! auth_pass.match(/\buser_password\b/) ) return null; + + cartoData.getUserDBPass(username, this); + }, + function(err, user_password) { + if (err) throw err; + user_params['user_password'] = user_password; + if ( auth_pass ) { + var dbpass = _.template(auth_pass, user_params); + _.extend(params, {dbpassword:dbpass}); + } + return true; + }, + function finish(err) { + callback(err); + } + ); + } + /** - * Get privacy for cartodb table + * Check access authorization * * @param req - standard req object. Importantly contains table and host information - * @param callback - is the table private or not? + * @param callback function(err, allowed) is access allowed not? */ - me.authorize= function(req, callback) { + me.authorize = function(req, callback) { var that = this; + var dbowner = me.dbOwnerByReq(req); Step( function(){ cartoData.checkMapKey(req, this); }, - function checkIfInternal(err, check_result){ + function checkApiKey(err, check_result){ if (err) throw err; - // if unauthorized continue to check table privacy - if (check_result !== 1) return true; + // if not authorized by api_key, continue + if (check_result !== 1) return null; - // authorized by key, login as db owner - var user_params = {}; - var auth_user = global.environment.postgres_auth_user; - var auth_pass = global.environment.postgres_auth_pass; - Step( - function getId() { - cartoData.getId(req, this); - }, - function(err, user_id) { - if (err) throw err; - user_params['user_id'] = user_id; - var dbuser = _.template(auth_user, user_params); - _.extend(req.params, {dbuser:dbuser}); - - // skip looking up user_password if postgres_auth_pass - // doesn't contain the "user_password" label - if (!auth_pass || ! auth_pass.match(/\buser_password\b/) ) return null; - - cartoData.getDatabasePassword(req, this); - }, - function(err, user_password) { - if (err) throw err; - user_params['user_password'] = user_password; - if ( auth_pass ) { - var dbpass = _.template(auth_pass, user_params); - _.extend(req.params, {dbpassword:dbpass}); - } - return true; - }, - function finish(err) { - callback(err, true); // authorized (or error) - } - ); - } - ,function getDatabase(err, data){ - if (err) throw err; - cartoData.getDatabase(req, this); + // authorized by api key, login as db owner and stop + that.setDBAuth(dbowner, req.params, function(err) { + callback(err, true); // authorized (or error) + }); }, - function getPrivacy(err, data){ + // TODO: check if authorized by layergroup signature + // should only be done for GET /layergroup + function getDatabase(err){ if (err) throw err; - cartoData.getTablePrivacy(data, req.params.table, this); + // NOTE: only used to get to table privacy + cartoData.getUserDBName(dbowner, this); }, - function(err, data){ - callback(err, data); + function getPrivacy(err, dbname){ + if (err) throw err; + cartoData.getTablePrivacy(dbname, req.params.table, this); + }, + function(err, privacy){ + callback(err, privacy); } ); }; @@ -415,11 +440,19 @@ module.exports = function(){ _.each(bad_query, function(key){ delete req.query[key]; }); req.params = _.extend({}, req.params); // shuffle things as request is a strange array/object + var signer = null; + if ( req.params.token ) { //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 ) { + // signer name defaults to the domain user + req.params.signer = tksplit[0] || req.headers.host.split('.')[0]; + req.params.token = tksplit[1]; + } } // bring all query values onto req.params object @@ -452,12 +485,14 @@ module.exports = function(){ if (req.profiler) req.profiler.done('req2params.setup'); + var dbowner = me.dbOwnerByReq(req); + Step( function getPrivacy(){ me.authorize(req, this); }, function gatekeep(err, data){ - if (req.profiler) req.profiler.done('cartoData.authorize'); + if (req.profiler) req.profiler.done('authorize'); if(err) throw err; if(data === "0") throw new Error("Sorry, you are unauthorized (permission denied)"); return data; @@ -465,21 +500,21 @@ module.exports = function(){ function getDatabaseHost(err, data){ if(err) throw err; - cartoData.getDatabaseHost(req, this); + cartoData.getUserDBHost(dbowner, this); }, function getDatabase(err, data){ if (req.profiler) req.profiler.done('cartoData.getDatabaseHost'); if(err) throw err; if ( data ) _.extend(req.params, {dbhost:data}); - cartoData.getDatabase(req, this); + cartoData.getUserDBName(dbowner, this); }, function getGeometryType(err, data){ if (req.profiler) req.profiler.done('cartoData.getDatabase'); if (err) throw err; _.extend(req.params, {dbname:data}); - cartoData.getGeometryType(req, this); + cartoData.getTableGeometryType(data, req.params.table, this); }, function finishSetup(err, data){ if (req.profiler) req.profiler.done('cartoData.getGeometryType'); diff --git a/package.json b/package.json index fc31257c..5a60edf0 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "windshaft" : "~0.14.5", "step": "0.0.x", "request": "2.9.202", - "cartodb-redis": "~0.1.0", + "cartodb-redis": "~0.2.0", "mapnik": "~0.7.22", "lzma": "~1.2.3" },