CDB-3629 Uses one request to redis to retrieve all connection params

This commit is contained in:
Raul Ochoa 2014-08-05 02:29:07 +02:00
parent 04dd9c709b
commit e16f278087
6 changed files with 55 additions and 131 deletions

View File

@ -32,14 +32,7 @@ var express = require('express')
, util = require('util')
, Profiler = require('step-profiler')
, StatsD = require('node-statsd').StatsD
, Meta = require('cartodb-redis')({
host: global.settings.redis_host,
port: global.settings.redis_port,
max: global.settings.redisPool,
idleTimeoutMillis: global.settings.redisIdleTimeoutMillis,
reapIntervalMillis: global.settings.redisReapIntervalMillis
})
// global.settings.app_root + '/app/models/metadata')
, MetadataDB = require('cartodb-redis')
, oAuth = require(global.settings.app_root + '/app/models/oauth')
, PSQL = require(global.settings.app_root + '/app/models/psql')
, PSQLWrapper = require(global.settings.app_root + '/app/sql/psql_wrapper')
@ -50,8 +43,15 @@ var express = require('express')
, formats = require(global.settings.app_root + '/app/models/formats')
;
var cdbReq = new CdbRequest(Meta);
var apiKeyAuth = new ApiKeyAuth(Meta, cdbReq);
var metadataBackend = MetadataDB({
host: global.settings.redis_host,
port: global.settings.redis_port,
max: global.settings.redisPool,
idleTimeoutMillis: global.settings.redisIdleTimeoutMillis,
reapIntervalMillis: global.settings.redisReapIntervalMillis
});
var cdbReq = new CdbRequest();
var apiKeyAuth = new ApiKeyAuth(metadataBackend, cdbReq);
// Set default configuration
global.settings.db_pubuser = global.settings.db_pubuser || "publicuser";
@ -287,6 +287,7 @@ function handleQuery(req, res) {
if ( req.profiler ) req.profiler.done('init');
var dbParams;
// 1. Get database from redis via the username stored in the host header subdomain
// 2. Run the request through OAuth to get R/W user id if signed
// 3. Get the list of tables affected by the query
@ -295,54 +296,38 @@ function handleQuery(req, res) {
Step(
function getDatabaseConnectionParams() {
checkAborted('getDatabaseConnectionParams');
Meta.getUserDBConnectionParams(cdbuser, this);
metadataBackend.getAllUserDBParams(cdbuser, this);
},
function setDBConnectionParams(err, dbParams) {
function authenticate(err, userDBParams) {
if (err) {
err.http_status = 404;
err.message = "Sorry, we can't find CartoDB user '" + cdbuser
+ "'. Please check that you have entered the correct domain.";
throw err;
}
dbParams = userDBParams;
dbopts.host = dbParams.dbhost;
dbopts.dbname = dbParams.dbname;
dbopts.user = (!!dbParams.dbuser) ? dbParams.dbuser : global.settings.db_pubuser;
dbopts.user = (!!dbParams.dbpublicuser) ? dbParams.dbpublicuser : global.settings.db_pubuser;
return null;
},
function authenticate(err) {
if (err) {
throw err;
}
if (api_key) {
apiKeyAuth.verifyRequest(req, this);
apiKeyAuth.verifyRequest(req, dbParams.apikey, this);
} else {
oAuth.verifyRequest(req, this, requestProtocol);
}
},
function setUserGetDBPassword(err, userId) {
function setDBAuth(err, isAuthenticated) {
if (err) {
throw err;
}
authenticated = userId !== null;
if (authenticated) {
user_id = userId;
dbopts.user = _.template(global.settings.db_user, {user_id: userId});
Meta.getUserDBPass(cdbuser, this);
} else {
return null
}
},
function setPassword(err, password) {
if (err) {
throw err;
}
if ( authenticated ) {
if (_.isBoolean(isAuthenticated) && isAuthenticated) {
dbopts.user = _.template(global.settings.db_user, {user_id: dbParams.dbuser});
if ( global.settings.hasOwnProperty('db_user_pass') ) {
dbopts.pass = _.template(global.settings.db_user_pass, {
user_id: user_id,
user_password: password
user_id: dbParams.dbuser,
user_password: dbParams.dbpass
});
} else {
delete dbopts.pass;

View File

@ -1,87 +1,34 @@
/**
* this module allows to auth user using an pregenerated api key
*/
var _ = require('underscore')
, Step = require('step');
function ApikeyAuth(cartodb_redis, cartodb_request) {
if ( ! cartodb_redis ) throw new Error("Cannot initialize ApikeyAuth with no cartodb_request");
if ( ! cartodb_request ) throw new Error("Cannot initialize ApikeyAuth with no cartodb-redis");
this.cdbRedis = cartodb_redis;
this.cdbRequest = cartodb_request;
function ApikeyAuth() {
}
module.exports = ApikeyAuth;
var o = ApikeyAuth.prototype;
o.userByReq = function(req) {
return this.cdbRequest.userByReq(req)
};
// Check if a request is authorized by api_key
//
// @param req express request object
// @param callback function(err, authorized)
//
o.authorizedByAPIKey = function(req, callback)
{
var user = this.userByReq(req);
var that = this;
Step(
function (){
that.cdbRedis.getUserMapKey(user, this);
},
function checkApiKey(err, val){
if (err) throw err;
var valid = 0;
if ( val ) {
if ( val == req.query.map_key ) valid = 1;
else if ( val == req.query.api_key ) valid = 1;
// check also in request body
else if ( req.body && req.body.map_key && val == req.body.map_key ) valid = 1;
else if ( req.body && req.body.api_key && val == req.body.api_key ) valid = 1;
}
return valid;
},
function finish(err, authorized) {
callback(err, authorized);
}
);
};
/**
* Get id of authorized user
*
* @param req - standard req object. Importantly contains table and host information
* @param callback - err, user_id (null if no auth)
* @param {Object} req - standard req object. Importantly contains table and host information
* @param {String} requiredApi - the API associated to the user, req must contain it
* @param {Function} callback - err, boolean (whether the request is authenticated or not)
*/
o.verifyRequest = function(req, callback) {
var user = this.userByReq(req);
var that = this;
ApikeyAuth.prototype.verifyRequest = function (req, requiredApi, callback) {
Step(
// check api key
function(){
that.authorizedByAPIKey(req, this);
},
// get user id or fail
function (err, apikey_valid) {
if ( err ) throw err;
if (apikey_valid) {
that.cdbRedis.getUserId(user, this);
} else {
// no auth
callback(null, null);
}
},
function (err, user_id){
callback(err, user_id);
var valid = false;
if ( requiredApi ) {
if ( requiredApi == req.query.map_key ) {
valid = true;
} else if ( requiredApi == req.query.api_key ) {
valid = true;
// check also in request body
} else if ( req.body && req.body.map_key && requiredApi == req.body.map_key ) {
valid = true;
} else if ( req.body && req.body.api_key && requiredApi == req.body.api_key ) {
valid = true;
}
);
};
}
callback(null, valid);
};

View File

@ -3,33 +3,26 @@
* of request headers
*/
function CartodbRequest(cartodb_redis) {
this.cartodb_redis = cartodb_redis;
function CartodbRequest() {
}
module.exports = CartodbRequest;
var o = CartodbRequest.prototype;
o.re_userFromHost = new RegExp(
global.settings.user_from_host ||
'^([^\\.]+)\\.' // would extract "strk" from "strk.cartodb.com"
);
o.userByReq = function(req) {
CartodbRequest.prototype.userByReq = function(req) {
var host = req.headers.host;
var mat = host.match(this.re_userFromHost);
var mat = host.match(re_userFromHost);
if ( ! mat ) {
console.error("ERROR: user pattern '" + this.re_userFromHost
+ "' does not match hostname '" + host + "'");
console.error("ERROR: user pattern '" + re_userFromHost + "' does not match hostname '" + host + "'");
return;
}
// console.log("Matches: "); console.dir(mat);
if ( ! mat.length === 2 ) {
console.error("ERROR: pattern '" + this.re_userFromHost
+ "' gave unexpected matches against '" + host + "': " + mat);
console.error("ERROR: pattern '" + re_userFromHost + "' gave unexpected matches against '" + host + "': " + mat);
return;
}
return mat[1];
};
var re_userFromHost = new RegExp(
global.settings.user_from_host || '^([^\\.]+)\\.' // would extract "strk" from "strk.cartodb.com"
);

View File

@ -118,7 +118,6 @@ var oAuth = function(){
signature = passed_tokens.oauth_signature;
delete passed_tokens['oauth_signature'];
var base64;
var joined = {};
// remove oauth_signature from body
@ -135,7 +134,7 @@ var oAuth = function(){
if (err) throw err;
//console.log(data + " should equal the provided signature: " + signature);
callback(err, (signature === data && !_.isUndefined(data)) ? ohash.user_id : null);
callback(err, (signature === data && !_.isUndefined(data)) ? true : null);
}
);
};

4
npm-shrinkwrap.json generated
View File

@ -48,8 +48,8 @@
}
},
"cartodb-redis": {
"version": "0.5.0",
"from": "git://github.com/CartoDB/node-cartodb-redis.git#0.5.0",
"version": "0.6.0",
"from": "git://github.com/CartoDB/node-cartodb-redis.git#0.6.0",
"dependencies": {
"strftime": {
"version": "0.6.2"

View File

@ -20,7 +20,7 @@
"underscore.string": "~1.1.6",
"pg": "git://github.com/CartoDB/node-postgres.git#2.6.2-cdb1",
"express": "~2.5.11",
"cartodb-redis": "~0.5.0",
"cartodb-redis": "~0.6.0",
"step": "0.0.x",
"topojson": "0.0.8",
"oauth-client": "0.2.0",