Windshaft-cartodb/lib/cartodb/backends/api/auth_api.js

218 lines
6.3 KiB
JavaScript
Raw Normal View History

var _ = require('underscore'); // AUTH_FALLBACK
/**
*
* @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;
2017-10-04 18:50:27 +08:00
// Check if the user is authorized by a signer
//
2017-10-04 18:50:27 +08:00
// @param res express response 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, res, callback) {
if ( ! res.locals.token || ! res.locals.signer ) {
return callback(null, false); // no signer requested
}
var self = this;
var layergroup_id = res.locals.token;
var auth_token = req.query.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);
});
};
function isValidApiKey(apikey) {
return apikey.type &&
apikey.user &&
apikey.databasePassword &&
apikey.databaseRole;
}
// Check if a request is authorized by api_key
//
// @param user
2018-02-15 19:50:42 +08:00
// @param res express response object
// @param callback function(err, authorized)
// NOTE: authorized is expected to be 0 or 1 (integer)
//
2018-02-15 19:50:42 +08:00
AuthApi.prototype.authorizedByAPIKey = function(user, res, callback) {
const apikeyToken = res.locals.api_key;
2018-03-01 02:13:49 +08:00
const basicAuthUsername = res.locals.basicAuthUsername;
2018-02-15 19:50:42 +08:00
if ( ! apikeyToken ) {
return callback(null, false); // no api key, no authorization...
}
2018-02-15 19:50:42 +08:00
this.metadataBackend.getApikey(user, apikeyToken, (err, apikey) => {
if (err) {
2018-02-20 01:48:02 +08:00
if (isNameNotFoundError(err)) {
err.http_status = 404;
}
return callback(err);
}
//Remove this block when Auth fallback is not used anymore
// AUTH_FALLBACK
2018-02-20 01:48:02 +08:00
apikey.databaseRole = composeUserDatabase(apikey);
apikey.databasePassword = composeDatabasePassword(apikey);
if ( !isValidApiKey(apikey)) {
const error = new Error('Unauthorized');
error.type = 'auth';
error.subtype = 'api-key-not-found';
error.http_status = 401;
2018-02-08 20:07:25 +08:00
return callback(error);
}
2018-03-01 02:13:49 +08:00
if (!usernameMatches(basicAuthUsername, res.locals.user)) {
const error = new Error('Forbidden');
error.type = 'auth';
error.subtype = 'api-key-username-mismatch';
error.http_status = 403;
return callback(error);
}
2018-02-08 20:07:25 +08:00
if (!apikey.grantsMaps) {
const error = new Error('Forbidden');
error.type = 'auth';
error.subtype = 'api-key-does-not-grant-access';
error.http_status = 403;
return callback(error);
}
return callback(null, true);
});
};
2018-02-20 01:48:02 +08:00
//Remove this block when Auth fallback is not used anymore
// AUTH_FALLBACK
function composeUserDatabase (apikey) {
if (shouldComposeUserDatabase(apikey)) {
return _.template(global.environment.postgres_auth_user, apikey);
}
return apikey.databaseRole;
}
//Remove this block when Auth fallback is not used anymore
// AUTH_FALLBACK
function composeDatabasePassword (apikey) {
if (shouldComposeDatabasePassword(apikey)) {
return global.environment.postgres.password;
}
return apikey.databasePassword;
}
//Remove this block when Auth fallback is not used anymore
// AUTH_FALLBACK
function shouldComposeDatabasePassword (apikey) {
return !apikey.databasePassword && global.environment.postgres.password;
}
//Remove this block when Auth fallback is not used anymore
// AUTH_FALLBACK
function shouldComposeUserDatabase(apikey) {
return !apikey.databaseRole && apikey.user_id && global.environment.postgres_auth_user;
}
function isNameNotFoundError (err) {
return err.message && -1 !== err.message.indexOf('name not found');
}
2018-03-01 02:13:49 +08:00
function usernameMatches (basicAuthUsername, requestUsername) {
return !(basicAuthUsername && (basicAuthUsername !== requestUsername));
2018-02-20 01:48:02 +08:00
}
/**
* Check access authorization
*
* @param req - standard req object. Importantly contains table and host information
2017-10-05 18:44:03 +08:00
* @param res - standard res object. Contains the auth parameters in locals
* @param callback function(err, allowed) is access allowed not?
*/
AuthApi.prototype.authorize = function(req, res, callback) {
var user = res.locals.user;
2018-02-15 19:50:42 +08:00
this.authorizedByAPIKey(user, res, (err, isAuthorizedByApikey) => {
2018-02-08 01:48:59 +08:00
if (err) {
return callback(err);
}
2018-02-08 01:48:59 +08:00
if (isAuthorizedByApikey) {
return this.pgConnection.setDBAuth(user, res.locals, 'regular', function (err) {
2018-02-08 01:48:59 +08:00
req.profiler.done('setDBAuth');
if (err) {
return callback(err);
}
2018-02-08 01:48:59 +08:00
callback(null, true);
});
2018-02-08 01:48:59 +08:00
}
this.authorizedBySigner(req, res, (err, isAuthorizedBySigner) => {
if (err) {
return callback(err);
}
2018-02-08 01:48:59 +08:00
if (isAuthorizedBySigner) {
return this.pgConnection.setDBAuth(user, res.locals, 'master', function (err) {
2018-02-08 01:48:59 +08:00
req.profiler.done('setDBAuth');
2018-02-08 01:48:59 +08:00
if (err) {
return callback(err);
}
2018-02-08 01:48:59 +08:00
callback(null, true);
});
2018-02-08 01:48:59 +08:00
}
// if no signer name was given, use default api key
2018-02-08 01:48:59 +08:00
if (!res.locals.signer) {
return this.pgConnection.setDBAuth(user, res.locals, 'default', function (err) {
req.profiler.done('setDBAuth');
if (err) {
return callback(err);
}
callback(null, true);
});
}
2018-02-08 01:48:59 +08:00
// if signer name was given, return no authorization
return callback(null, false);
});
});
};