Authentication/Authorization moves to its own entity

This commit is contained in:
Raul Ochoa
2015-07-13 15:05:03 +02:00
parent 962ac97433
commit b05701be61
3 changed files with 159 additions and 124 deletions
+141
View File
@@ -0,0 +1,141 @@
var assert = require('assert');
var step = require('step');
/**
*
* @param {PgConnection} pgConnection
* @param metadataBackend
* @param {MapStore} mapStore
* @param {TemplateMaps} templateMaps
* @constructor
* @type {AuthApi}
*/
function AuthApi(pgConnection, metadataBackend, mapStore, templateMaps) {
this.pgConnection = pgConnection;
this.metadataBackend = metadataBackend;
this.mapStore = mapStore;
this.templateMaps = templateMaps;
}
module.exports = AuthApi;
// Check if a request is authorized by a signer
//
// @param req express request object
// @param callback function(err, signed_by) signed_by will be
// null if the request is not signed by anyone
// or will be a string cartodb username otherwise.
//
AuthApi.prototype.authorizedBySigner = function(req, callback) {
if ( ! req.params.token || ! req.params.signer ) {
return callback(null, false); // no signer requested
}
var self = this;
var layergroup_id = req.params.token;
var auth_token = req.params.auth_token;
this.mapStore.load(layergroup_id, function(err, mapConfig) {
if (err) {
return callback(err);
}
var authorized = self.templateMaps.isAuthorized(mapConfig.obj().template, auth_token);
return callback(null, authorized);
});
};
// Check if a request is authorized by api_key
//
// @param user
// @param req express request object
// @param callback function(err, authorized)
// NOTE: authorized is expected to be 0 or 1 (integer)
//
AuthApi.prototype.authorizedByAPIKey = function(user, req, callback) {
var givenKey = req.query.api_key || req.query.map_key;
if ( ! givenKey && req.body ) {
// check also in request body
givenKey = req.body.api_key || req.body.map_key;
}
if ( ! givenKey ) {
return callback(null, 0); // no api key, no authorization...
}
var self = this;
step(
function () {
self.metadataBackend.getUserMapKey(user, this);
},
function checkApiKey(err, val){
assert.ifError(err);
return val && givenKey == val;
},
function finish(err, authorized) {
callback(err, authorized);
}
);
};
/**
* Check access authorization
*
* @param req - standard req object. Importantly contains table and host information
* @param callback function(err, allowed) is access allowed not?
*/
AuthApi.prototype.authorize = function(req, callback) {
var self = this;
var user = req.context.user;
step(
function () {
self.authorizedByAPIKey(user, req, this);
},
function checkApiKey(err, authorized){
if (req.profiler) {
req.profiler.done('authorizedByAPIKey');
}
assert.ifError(err);
// if not authorized by api_key, continue
if (!authorized) {
// not authorized by api_key, check if authorized by signer
return self.authorizedBySigner(req, this);
}
// authorized by api key, login as the given username and stop
self.pgConnection.setDBAuth(user, req.params, function(err) {
callback(err, true); // authorized (or error)
});
},
function checkSignAuthorized(err, authorized) {
if (err) {
return callback(err);
}
if ( ! authorized ) {
// request not authorized by signer.
// if no signer name was given, let dbparams and
// PostgreSQL do the rest.
//
if ( ! req.params.signer ) {
return callback(null, true); // authorized so far
}
// if signer name was given, return no authorization
return callback(null, false);
}
self.pgConnection.setDBAuth(user, req.params, function(err) {
if (req.profiler) {
req.profiler.done('setDBAuth');
}
callback(err, true); // authorized (or error)
});
}
);
};
+13 -6
View File
@@ -5,9 +5,16 @@ var templateName = require('../backends/template_maps').templateName;
var cors = require('../middleware/cors');
function NamedMapsAdminController(app, templateMaps) {
/**
* @param app
* @param {TemplateMaps} templateMaps
* @param {AuthApi} authApi
* @constructor
*/
function NamedMapsAdminController(app, templateMaps, authApi) {
this.app = app;
this.templateMaps = templateMaps;
this.authApi = authApi;
}
module.exports = NamedMapsAdminController;
@@ -28,7 +35,7 @@ NamedMapsAdminController.prototype.create = function(req, res) {
step(
function checkPerms(){
self.app.authorizedByAPIKey(cdbuser, req, this);
self.authApi.authorizedByAPIKey(cdbuser, req, this);
},
function addTemplate(err, authenticated) {
assert.ifError(err);
@@ -53,7 +60,7 @@ NamedMapsAdminController.prototype.update = function(req, res) {
var tpl_id;
step(
function checkPerms(){
self.app.authorizedByAPIKey(cdbuser, req, this);
self.authApi.authorizedByAPIKey(cdbuser, req, this);
},
function updateTemplate(err, authenticated) {
assert.ifError(err);
@@ -84,7 +91,7 @@ NamedMapsAdminController.prototype.retrieve = function(req, res) {
var tpl_id;
step(
function checkPerms(){
self.app.authorizedByAPIKey(cdbuser, req, this);
self.authApi.authorizedByAPIKey(cdbuser, req, this);
},
function getTemplate(err, authenticated) {
assert.ifError(err);
@@ -120,7 +127,7 @@ NamedMapsAdminController.prototype.destroy = function(req, res) {
var tpl_id;
step(
function checkPerms(){
self.app.authorizedByAPIKey(cdbuser, req, this);
self.authApi.authorizedByAPIKey(cdbuser, req, this);
},
function deleteTemplate(err, authenticated) {
assert.ifError(err);
@@ -147,7 +154,7 @@ NamedMapsAdminController.prototype.list = function(req, res) {
step(
function checkPerms(){
self.app.authorizedByAPIKey(cdbuser, req, this);
self.authApi.authorizedByAPIKey(cdbuser, req, this);
},
function listTemplates(err, authenticated) {
assert.ifError(err);
+5 -118
View File
@@ -19,6 +19,7 @@ var mapnik = windshaft.mapnik;
var TemplateMaps = require('./backends/template_maps.js');
var QueryTablesApi = require('./api/query_tables_api');
var UserLimitsApi = require('./api/user_limits_api');
var AuthApi = require('./api/auth_api');
var PgQueryRunner = require('./backends/pg_query_runner');
var PgConnection = require('./backends/pg_connection');
@@ -167,6 +168,8 @@ module.exports = function(serverOptions) {
var mapValidatorBackend = new windshaft.backend.MapValidator(tileBackend, attributesBackend);
var mapBackend = new windshaft.backend.Map(rendererCache, mapStore, mapValidatorBackend);
var authApi = new AuthApi(pgConnection, metadataBackend, mapStore, templateMaps);
app.findStatusCode = function(err) {
var statusCode;
if ( err.http_status ) {
@@ -220,7 +223,7 @@ module.exports = function(serverOptions) {
userLimitsApi
).register(app);
new controller.NamedMapsAdmin(app, templateMaps).register(app);
new controller.NamedMapsAdmin(app, templateMaps, authApi).register(app);
new controller.ServerInfo().register(app);
@@ -453,7 +456,7 @@ module.exports = function(serverOptions) {
step(
function getPrivacy(){
app.authorize(req, this);
authApi.authorize(req, this);
},
function gatekeep(err, authorized){
if (req.profiler) {
@@ -626,122 +629,6 @@ module.exports = function(serverOptions) {
});
};
// Check if a request is authorized by a signer
//
// @param req express request object
// @param callback function(err, signed_by) signed_by will be
// null if the request is not signed by anyone
// or will be a string cartodb username otherwise.
//
app.authorizedBySigner = function(req, callback) {
if ( ! req.params.token || ! req.params.signer ) {
return callback(null, false); // no signer requested
}
var layergroup_id = req.params.token;
var auth_token = req.params.auth_token;
mapStore.load(layergroup_id, function(err, mapConfig) {
if (err) {
return callback(err);
}
var authorized = templateMaps.isAuthorized(mapConfig.obj().template, auth_token);
return callback(null, authorized);
});
};
// Check if a request is authorized by api_key
//
// @param user
// @param req express request object
// @param callback function(err, authorized)
// NOTE: authorized is expected to be 0 or 1 (integer)
//
app.authorizedByAPIKey = function(user, req, callback) {
var givenKey = req.query.api_key || req.query.map_key;
if ( ! givenKey && req.body ) {
// check also in request body
givenKey = req.body.api_key || req.body.map_key;
}
if ( ! givenKey ) {
return callback(null, 0); // no api key, no authorization...
}
step(
function () {
metadataBackend.getUserMapKey(user, this);
},
function checkApiKey(err, val){
assert.ifError(err);
return val && givenKey == val;
},
function finish(err, authorized) {
callback(err, authorized);
}
);
};
/**
* Check access authorization
*
* @param req - standard req object. Importantly contains table and host information
* @param callback function(err, allowed) is access allowed not?
*/
app.authorize = function(req, callback) {
var self = this;
var user = req.context.user;
step(
function () {
self.authorizedByAPIKey(user, req, this);
},
function checkApiKey(err, authorized){
if (req.profiler) {
req.profiler.done('authorizedByAPIKey');
}
assert.ifError(err);
// if not authorized by api_key, continue
if (!authorized) {
// not authorized by api_key, check if authorized by signer
return self.authorizedBySigner(req, this);
}
// authorized by api key, login as the given username and stop
pgConnection.setDBAuth(user, req.params, function(err) {
callback(err, true); // authorized (or error)
});
},
function checkSignAuthorized(err, authorized) {
if (err) {
return callback(err);
}
if ( ! authorized ) {
// request not authorized by signer.
// if no signer name was given, let dbparams and
// PostgreSQL do the rest.
//
if ( ! req.params.signer ) {
return callback(null, true); // authorized so far
}
// if signer name was given, return no authorization
return callback(null, false);
}
pgConnection.setDBAuth(user, req.params, function(err) {
if (req.profiler) {
req.profiler.done('setDBAuth');
}
callback(err, true); // authorized (or error)
});
}
);
};
return app;
};