Skip to content

Commit

Permalink
Merge pull request #626 from joola/feature/#625
Browse files Browse the repository at this point in the history
#625 redis and rabbitmq no longer mandatory
  • Loading branch information
itayw committed Nov 10, 2014
2 parents d947133 + 97f877a commit f535a31
Show file tree
Hide file tree
Showing 17 changed files with 641 additions and 450 deletions.
7 changes: 6 additions & 1 deletion config/default.yml
Expand Up @@ -26,13 +26,15 @@ interfaces:
store:
config:
redis:
# enabled: true
dsn: redis://127.0.0.1
## if dsn not specific then these are required
# host: 127.0.0.1
# port: 6379
# db: 0
# auth: null
dispatch:
# enabled: true
redis:
dsn: redis://127.0.0.1
stomp:
Expand All @@ -45,9 +47,11 @@ store:
websocket:
http:
redis:
#enabled: true
dsn: redis://127.0.0.1
https:
redis:
#enabled: true
dsn: redis://127.0.0.1
logger:
console:
Expand All @@ -68,7 +72,8 @@ dispatch:
expires: 60
authentication:
basicauth:
enabled: false
enabled: true
enable_with_http: true
tokens:
expireafter: 1200000
ratelimits:
Expand Down
11 changes: 11 additions & 0 deletions config/test2.yml
@@ -0,0 +1,11 @@
store:
datastore:
mongodb:
enabled: false
influxdb:
enabled: true
hosts: [{host: 'localhost', port: 8086}]

username: root
password: root
database: joola
184 changes: 131 additions & 53 deletions lib/common/auth.js
Expand Up @@ -80,15 +80,24 @@ auth.extendToken = function (req, token, callback) {
token.expires = timestamp.setMilliseconds(timestamp.getMilliseconds() + joola.config.get('authentication:tokens:expireafter') || 20 * 60 * 1000 /*20 minutes*/);

var expiration = parseInt((token.expires - token.last) / 1000, 10);
joola.redis.expire('auth:tokens:' + token._, expiration, function (err, a) {
if (err)
joola.logger.warn({category: 'security'}, '[auth] failed extending security token for [' + token.user.username + ']: ' + err);
else
joola.logger.trace({category: 'security'}, '[auth] extended security token for [' + token.user.username + ']');
if (joola.redis) {
joola.redis.expire('auth:tokens:' + token._, expiration, function (err, a) {
if (err)
joola.logger.warn({category: 'security'}, '[auth] failed extending security token for [' + token.user.username + ']: ' + err);
else
joola.logger.trace({category: 'security'}, '[auth] extended security token for [' + token.user.username + ']');
return process.nextTick(function () {
return callback(err, token);
});
});
}
else {
joola.memory.set('auth:tokens:' + token._, joola.memory.get('auth:tokens:' + token._), expiration * 1000);
joola.logger.trace({category: 'security'}, '[auth] extended security token for [' + token.user.username + ']');
return process.nextTick(function () {
return callback(err, token);
return callback(null, token);
});
});
}
};

/**
Expand All @@ -100,11 +109,18 @@ auth.extendToken = function (req, token, callback) {
* - `err` if occured, an error object, else null.
*/
auth.expireToken = function (token, callback) {
joola.redis.del('auth:tokens:' + token._, function (err) {
if (joola.redis) {
joola.redis.del('auth:tokens:' + token._, function (err) {
return process.nextTick(function () {
return callback(err);
});
});
} else {
joola.memory.set('auth:tokens:' + token._, null);
return process.nextTick(function () {
return callback(err);
return callback(null);
});
});
}
};

/**
Expand Down Expand Up @@ -153,7 +169,21 @@ auth.generateToken = function (req, user, callback) {

token.user = JSON.stringify(token.user);

joola.redis.hmset('auth:tokens:' + token._, token, function (err) {
if (joola.redis) {
joola.redis.hmset('auth:tokens:' + token._, token, function (err) {
token.user = JSON.parse(token.user);
joola.logger.trace({category: 'security'}, '[auth] generated token [' + token._ + '] for [' + token.user.username + '].');
auth.extendToken(token, function (err) {
return process.nextTick(function () {

if (typeof callback === 'function')
return callback(err, ce.clone(token));
});
});
});
}
else {
joola.memory.set('auth:tokens:' + token._, token);
token.user = JSON.parse(token.user);
joola.logger.trace({category: 'security'}, '[auth] generated token [' + token._ + '] for [' + token.user.username + '].');
auth.extendToken(token, function (err) {
Expand All @@ -163,7 +193,8 @@ auth.generateToken = function (req, user, callback) {
return callback(err, ce.clone(token));
});
});
});

}
});
};

Expand All @@ -177,6 +208,7 @@ auth.generateToken = function (req, user, callback) {
* - `result` contains a {Object} with the validated Token.
*/
auth.validateToken = function (req, token, APIToken, callback) {

if (!callback) {
callback = APIToken;
APIToken = token;
Expand All @@ -190,7 +222,6 @@ auth.validateToken = function (req, token, APIToken, callback) {
return process.nextTick(function () {
return callback(new Error('Missing token for validation'));
});

if (APIToken) {
var cachedUser = joola.memory.get('APIToken:' + APIToken);
if (joola.memory.get('APIToken:' + APIToken))
Expand All @@ -202,13 +233,12 @@ auth.validateToken = function (req, token, APIToken, callback) {
return process.nextTick(function () {
return callback(err);
});

//return auth.generateToken(user, callback);
joola.memory.set('APIToken:' + APIToken, user, 1000);
return callback(null, user);
});
}
else {
else {
if (typeof token === 'object')
token = token._;

Expand All @@ -228,10 +258,24 @@ auth.validateToken = function (req, token, APIToken, callback) {
}

//fetch the token
joola.redis.hgetall('auth:tokens:' + token, function (err, _token) {
if (joola.redis) {
joola.redis.hgetall('auth:tokens:' + token, function (err, _token) {
if (_token) {
//all is good
_token.user = JSON.parse(_token.user);
//extend the token
joola.memory.set('token:' + _token._, _token);
return auth.extendToken(req, _token, callback);
}
else
return process.nextTick(function () {
return callback(new AuthErrorTemplate('Failed to validate token [1] [' + token + ']'));
});
});
}
else {
var _token = joola.memory.get('auth:tokens:' + token);
if (_token) {
//all is good
_token.user = JSON.parse(_token.user);
//extend the token
joola.memory.set('token:' + _token._, _token);
return auth.extendToken(req, _token, callback);
Expand All @@ -240,7 +284,8 @@ auth.validateToken = function (req, token, APIToken, callback) {
return process.nextTick(function () {
return callback(new AuthErrorTemplate('Failed to validate token [1] [' + token + ']'));
});
});

}
}
};

Expand Down Expand Up @@ -344,7 +389,6 @@ auth.validateAction = function (action, req, res, callback) {
return process.nextTick(function () {
return callback(new auth.AuthErrorTemplate('Request for action by unauthenticated user.'), false);
});

if (typeof req.user === 'string')
req.user = JSON.parse(req.user);

Expand Down Expand Up @@ -430,31 +474,51 @@ auth.checkRateLimits = function (req, res, callback) {
};
}
var redisKey = 'ratelimits:' + limitKey;
joola.redis.incr(redisKey, function (err, result) {
if (err)
return callback(err);
if (joola.redis) {
joola.redis.incr(redisKey, function (err, result) {
if (err)
return callback(err);

if (result === 1)
joola.redis.pexpire(redisKey, 60 * 60 * 1000);
if (result === 1)
joola.redis.pexpire(redisKey, 60 * 60 * 1000);

req.limits.remaining = req.limits.limit - result;
req.limits.remaining = req.limits.limit - result;

joola.redis.pttl(redisKey, function (err, ttl) {
if (err)
return callback(err);
joola.redis.pttl(redisKey, function (err, ttl) {
if (err)
return callback(err);

req.limits.reset = Math.floor((new Date().getTime() + ttl ) / 1000);
res.header("X-RateLimit-Limit", req.limits.limit);
res.header("X-RateLimit-Remaining", req.limits.remaining < 0 ? 0 : req.limits.remaining);
res.header("X-RateLimit-Reset", req.limits.reset);
res.header("Retry-After", Math.floor(ttl / 1000));
req.limits.reset = Math.floor((new Date().getTime() + ttl ) / 1000);
res.header("X-RateLimit-Limit", req.limits.limit);
res.header("X-RateLimit-Remaining", req.limits.remaining < 0 ? 0 : req.limits.remaining);
res.header("X-RateLimit-Reset", req.limits.reset);
res.header("Retry-After", Math.floor(ttl / 1000));

if (req.limits.remaining < 0)
return callback(new Error('Limit exceeded'));
if (req.limits.remaining < 0)
return callback(new Error('Limit exceeded'));

return callback(null);
return callback(null);
});
});
});
}
else {
joola.memory.set(redisKey, parseInt(joola.memory.get(redisKey)) + 1);
var counter = joola.memory.get(redisKey);
if (counter === 1)
joola.memory.set(redisKey, counter, 60 * 60 * 1000);

//req.limits.remaining = req.limits.limit - result;
//req.limits.reset = Math.floor((new Date().getTime() + ttl ) / 1000);
res.header("X-RateLimit-Limit", req.limits.limit);
res.header("X-RateLimit-Remaining", req.limits.remaining < 0 ? 0 : req.limits.remaining);
res.header("X-RateLimit-Reset", 0);
res.header("Retry-After", 0);

if (req.limits.remaining < 0)
return callback(new Error('Limit exceeded'));

return callback(null);
}
};

/**
Expand All @@ -480,7 +544,6 @@ auth.middleware = function (req, res, next) {
var debug = {};
var parts = url.parse(req.url);


//TODO: Add header to SDK
//if (req.headers['content-type'] !== 'application/json' && (req.method !== 'GET' && req.method !== 'OPTIONS'))
// return router.responseError(415, new Error('Unsupported Media Type'), req, res);
Expand Down Expand Up @@ -515,7 +578,6 @@ auth.middleware = function (req, res, next) {
return next();
}


//check if we have a valid token as part of the request
//check query string for `token`
var token = req.query.token;
Expand Down Expand Up @@ -561,7 +623,6 @@ auth.middleware = function (req, res, next) {
APIToken = headerToken;
}
}

var modulename = req.endpointRoute.module;
var action = req.endpointRoute.action;

Expand Down Expand Up @@ -712,11 +773,36 @@ auth.getUserByToken = function (token, callback) {
if (!err)
return callback(null, user);

joola.redis.hgetall('auth:tokens:' + token, function (err, _token) {
if (err)
if (joola.redis) {
joola.redis.hgetall('auth:tokens:' + token, function (err, _token) {
if (err)
return process.nextTick(function () {
return callback(err);
});
if (!_token)
return process.nextTick(function () {
return callback(new Error('Token not found'));
});
if (!_token.user)
return process.nextTick(function () {
return callback(new Error('Failed to translate user from token'));
});
try {
_token.user = JSON.parse(_token.user);
}
catch (ex) {
return process.nextTick(function () {
return callback(ex);
});
}

return process.nextTick(function () {
return callback(err);
return callback(null, _token.user);
});
});
}
else {
var _token = joola.memory.get('auth:tokens:' + token);
if (!_token)
return process.nextTick(function () {
return callback(new Error('Token not found'));
Expand All @@ -725,19 +811,11 @@ auth.getUserByToken = function (token, callback) {
return process.nextTick(function () {
return callback(new Error('Failed to translate user from token'));
});
try {
_token.user = JSON.parse(_token.user);
}
catch (ex) {
return process.nextTick(function () {
return callback(ex);
});
}

return process.nextTick(function () {
return callback(null, _token.user);
});
});
}
});
};

Expand Down

0 comments on commit f535a31

Please sign in to comment.