Add support for reading user-specific database_password from redis
This commits adds support for CartoDB-2.5.0 model. Closes #89. Change is backward compatible.
This commit is contained in:
parent
c7494c3c73
commit
baa95a62d1
6
NEWS.md
6
NEWS.md
@ -1,9 +1,9 @@
|
||||
1.5.0 -- 2013-MM-DD
|
||||
-------------------
|
||||
|
||||
* Add support for specifying database connection passwords
|
||||
* Add support for reading user-specific database_host from redis,
|
||||
as per CartoDB-2.5.0 model (#88)
|
||||
* Add support for configuring database connection passwords
|
||||
* Optionally read user-specific database_host and database_password
|
||||
from redis as per CartoDB-2.5.0 model (#88, #89)
|
||||
* Do not force ending dot in SQL-API hostname, for easier testing
|
||||
|
||||
1.4.1 -- 2013-11-08
|
||||
|
@ -11,7 +11,8 @@ var config = {
|
||||
,cache_enabled: false
|
||||
,log_format: '[:date] :req[X-Real-IP] :method :req[Host]:url :status :response-time ms -> :res[Content-Type] (:res[X-Tiler-Profiler])'
|
||||
,postgres_auth_user: 'development_cartodb_user_<%= user_id %>'
|
||||
,postgres_auth_pass: 'development_cartodb_user_<%= user_id %>_pass'
|
||||
// Supported labels: 'user_id', 'user_password' (both read from redis)
|
||||
,postgres_auth_pass: '<%= user_password %>'
|
||||
,postgres: {
|
||||
// Parameters to pass to datasource plugin of mapnik
|
||||
// See http://github.com/mapnik/mapnik/wiki/PostGIS
|
||||
|
@ -11,7 +11,8 @@ var config = {
|
||||
,cache_enabled: true
|
||||
,log_format: '[:date] :req[X-Real-IP] :method :req[Host]:url :status :response-time ms -> :res[Content-Type] (:res[X-Tiler-Profiler])'
|
||||
,postgres_auth_user: 'cartodb_user_<%= user_id %>'
|
||||
,postgres_auth_pass: 'cartodb_user_<%= user_id %>_pass'
|
||||
// Supported labels: 'user_id', 'user_password' (both read from redis)
|
||||
,postgres_auth_pass: '<%= user_password %>'
|
||||
,postgres: {
|
||||
// Parameters to pass to datasource plugin of mapnik
|
||||
// See http://github.com/mapnik/mapnik/wiki/PostGIS
|
||||
|
@ -11,7 +11,8 @@ var config = {
|
||||
,cache_enabled: true
|
||||
,log_format: '[:date] :req[X-Real-IP] :method :req[Host]:url :status :response-time ms (:res[X-Tiler-Profiler]) -> :res[Content-Type]'
|
||||
,postgres_auth_user: 'cartodb_staging_user_<%= user_id %>'
|
||||
,postgres_auth_pass: 'cartodb_staging_user_<%= user_id %>_pass'
|
||||
// Supported labels: 'user_id', 'user_password' (both read from redis)
|
||||
,postgres_auth_pass: '<%= user_password %>'
|
||||
,postgres: {
|
||||
// Parameters to pass to datasource plugin of mapnik
|
||||
// See http://github.com/mapnik/mapnik/wiki/PostGIS
|
||||
|
@ -11,6 +11,7 @@ var config = {
|
||||
,cache_enabled: false
|
||||
,log_format: '[:date] :req[X-Real-IP] :method :req[Host]:url :status :response-time ms -> :res[Content-Type] (:res[X-Tiler-Profiler])'
|
||||
,postgres_auth_user: 'test_cartodb_user_<%= user_id %>'
|
||||
// Supported labels: 'user_id', 'user_password' (both read from redis)
|
||||
,postgres_auth_pass: 'test_cartodb_user_<%= user_id %>_pass'
|
||||
,postgres: {
|
||||
// Parameters to pass to datasource plugin of mapnik
|
||||
|
@ -123,6 +123,30 @@ module.exports = function() {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the database password for this particular subdomain/username
|
||||
*
|
||||
* @param req - standard express req object. importantly contains host information
|
||||
* @param callback
|
||||
*/
|
||||
me.getDatabasePassword= function(req, callback) {
|
||||
// strip subdomain from header host
|
||||
var username = me.userFromHostname(req.headers.host);
|
||||
var redisKey = _.template(this.user_key, {username: username});
|
||||
|
||||
this.retrieve(this.user_metadata_db, redisKey, 'database_password', function(err, data) {
|
||||
if ( err ) callback(err, null);
|
||||
else {
|
||||
if ( data === null ) {
|
||||
/* database_password was introduced in cartodb-2.5.0,
|
||||
* for older versions we'll just use configured password */
|
||||
//console.log("WARNING: missing " + username + "'s database_password in redis (try CARTODB/script/restore_redis)");
|
||||
}
|
||||
callback(err, data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Check the user map key for this particular subdomain/username
|
||||
*
|
||||
@ -161,27 +185,49 @@ module.exports = function() {
|
||||
},
|
||||
function checkIfInternal(err, check_result){
|
||||
if (err) throw err;
|
||||
if (check_result === 1) {
|
||||
// authorized by key, login as db owner
|
||||
that.getId(req, function(err, user_id) {
|
||||
if (err) { callback(err); return; }
|
||||
var dbuser = _.template(global.settings.postgres_auth_user, {user_id: user_id});
|
||||
_.extend(req.params, {dbuser:dbuser});
|
||||
if ( global.settings.postgres_auth_pass ) {
|
||||
var dbpass = _.template(global.settings.postgres_auth_pass, {user_id: user_id});
|
||||
_.extend(req.params, {dbpassword:dbpass});
|
||||
}
|
||||
callback(err, true);
|
||||
});
|
||||
} else {
|
||||
return true; // continue to check if the table is public/private
|
||||
}
|
||||
|
||||
// if unauthorized continue to check table privacy
|
||||
if (check_result !== 1) return true;
|
||||
|
||||
// authorized by key, login as db owner
|
||||
var user_params = {};
|
||||
var auth_user = global.settings.postgres_auth_user;
|
||||
var auth_pass = global.settings.postgres_auth_pass;
|
||||
Step(
|
||||
function getId() {
|
||||
that.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.match(/\buser_password\b/) ) return null;
|
||||
|
||||
that.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 (err, data){
|
||||
,function getDatabase(err, data){
|
||||
if (err) throw err;
|
||||
that.getDatabase(req, this);
|
||||
},
|
||||
function(err, data){
|
||||
function getPrivacy(err, data){
|
||||
if (err) throw err;
|
||||
var redisKey = _.template(that.table_key, {database_name: data, table_name: req.params.table});
|
||||
|
||||
@ -189,7 +235,7 @@ module.exports = function() {
|
||||
},
|
||||
function(err, data){
|
||||
callback(err, data);
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -807,6 +807,43 @@ suite('server', function() {
|
||||
);
|
||||
});
|
||||
|
||||
// See https://github.com/CartoDB/Windshaft-cartodb/issues/XXX
|
||||
test("get'ing a tile with a user-specific database password", function(done){
|
||||
var style = querystring.stringify({style: test_style_black_200, style_version: '2.0.0'});
|
||||
var backupDBPass = global.settings.postgres_auth_pass;
|
||||
global.settings.postgres_auth_pass = '<%= user_password %>';
|
||||
Step (
|
||||
function() {
|
||||
var next = this;
|
||||
assert.response(server, {
|
||||
headers: {host: 'cartodb250user'},
|
||||
url: '/tiles/test_table/15/16046/12354.png?'
|
||||
+ 'cache_buster=4.20&api_key=4321&' + style,
|
||||
method: 'GET',
|
||||
encoding: 'binary'
|
||||
},{}, function(res){
|
||||
next(null, res);
|
||||
});
|
||||
},
|
||||
function checkRes(err, res) {
|
||||
assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body);
|
||||
var ct = res.headers['content-type'];
|
||||
assert.equal(ct, 'image/png');
|
||||
assert.imageEqualsFile(res.body,
|
||||
'./test/fixtures/test_table_15_16046_12354_styled_black.png',
|
||||
2, this);
|
||||
},
|
||||
function checkImage(err, similarity) {
|
||||
if (err) throw err;
|
||||
return null
|
||||
},
|
||||
function finish(err) {
|
||||
global.settings.postgres_auth_pass = backupDBPass;
|
||||
done(err);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test("get'ing a tile with url specified 2.1.0 style should return an expected tile", function(done){
|
||||
var style = querystring.stringify({style: test_style_black_210, style_version: '2.1.0'});
|
||||
assert.response(server, {
|
||||
|
@ -73,6 +73,7 @@ echo "HSET rails:users:cartodb250user id ${TESTUSERID}" | redis-cli -p ${REDIS_P
|
||||
echo 'HSET rails:users:cartodb250user database_name "'${TEST_DB}'"' | redis-cli -p ${REDIS_PORT} -n 5
|
||||
echo 'HSET rails:users:cartodb250user database_host "localhost"' | redis-cli -p ${REDIS_PORT} -n 5
|
||||
echo 'HSET rails:users:cartodb250user database_password "'${TESTPASS}'"' | redis-cli -p ${REDIS_PORT} -n 5
|
||||
echo "HSET rails:users:cartodb250user map_key 4321" | redis-cli -p ${REDIS_PORT} -n 5
|
||||
|
||||
echo 'HSET rails:'"${TEST_DB}"':my_table infowindow "this, that, the other"' | redis-cli -p ${REDIS_PORT} -n 0
|
||||
echo 'HSET rails:'"${TEST_DB}"':test_table_private_1 privacy "0"' | redis-cli -p ${REDIS_PORT} -n 0
|
||||
|
Loading…
Reference in New Issue
Block a user