commit
35e5170907
@ -1,5 +1,4 @@
|
|||||||
var assert = require('assert');
|
var _ = require('underscore'); // AUTH_FALLBACK
|
||||||
var step = require('step');
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -47,39 +46,113 @@ AuthApi.prototype.authorizedBySigner = function(res, callback) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function isValidApiKey(apikey) {
|
||||||
|
return apikey.type &&
|
||||||
|
apikey.user &&
|
||||||
|
apikey.databasePassword &&
|
||||||
|
apikey.databaseRole;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if a request is authorized by api_key
|
// Check if a request is authorized by api_key
|
||||||
//
|
//
|
||||||
// @param user
|
// @param user
|
||||||
// @param req express request object
|
// @param res express response object
|
||||||
// @param callback function(err, authorized)
|
// @param callback function(err, authorized)
|
||||||
// NOTE: authorized is expected to be 0 or 1 (integer)
|
// NOTE: authorized is expected to be 0 or 1 (integer)
|
||||||
//
|
//
|
||||||
AuthApi.prototype.authorizedByAPIKey = function(user, req, callback) {
|
AuthApi.prototype.authorizedByAPIKey = function(user, res, callback) {
|
||||||
var givenKey = req.query.api_key || req.query.map_key;
|
const apikeyToken = res.locals.api_key;
|
||||||
if ( ! givenKey && req.body ) {
|
const apikeyUsername = res.locals.apikeyUsername;
|
||||||
// check also in request body
|
|
||||||
givenKey = req.body.api_key || req.body.map_key;
|
if ( ! apikeyToken ) {
|
||||||
}
|
return callback(null, false); // no api key, no authorization...
|
||||||
if ( ! givenKey ) {
|
|
||||||
return callback(null, 0); // no api key, no authorization...
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var self = this;
|
this.metadataBackend.getApikey(user, apikeyToken, (err, apikey) => {
|
||||||
|
if (err) {
|
||||||
|
if (isNameNotFoundError(err)) {
|
||||||
|
err.http_status = 404;
|
||||||
|
}
|
||||||
|
|
||||||
step(
|
return callback(err);
|
||||||
function () {
|
|
||||||
self.metadataBackend.getUserMapKey(user, this);
|
|
||||||
},
|
|
||||||
function checkApiKey(err, val){
|
|
||||||
assert.ifError(err);
|
|
||||||
return val && givenKey === val;
|
|
||||||
},
|
|
||||||
function finish(err, authorized) {
|
|
||||||
callback(err, authorized);
|
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
//Remove this block when Auth fallback is not used anymore
|
||||||
|
// AUTH_FALLBACK
|
||||||
|
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;
|
||||||
|
|
||||||
|
return callback(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!usernameMatches(apikeyUsername, res.locals.user)) {
|
||||||
|
const error = new Error('Forbidden');
|
||||||
|
error.type = 'auth';
|
||||||
|
error.subtype = 'api-key-username-mismatch';
|
||||||
|
error.http_status = 403;
|
||||||
|
|
||||||
|
return callback(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//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');
|
||||||
|
}
|
||||||
|
|
||||||
|
function usernameMatches (apikeyUsername, requestUsername) {
|
||||||
|
return !(apikeyUsername && (apikeyUsername !== requestUsername));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check access authorization
|
* Check access authorization
|
||||||
*
|
*
|
||||||
@ -88,51 +161,57 @@ AuthApi.prototype.authorizedByAPIKey = function(user, req, callback) {
|
|||||||
* @param callback function(err, allowed) is access allowed not?
|
* @param callback function(err, allowed) is access allowed not?
|
||||||
*/
|
*/
|
||||||
AuthApi.prototype.authorize = function(req, res, callback) {
|
AuthApi.prototype.authorize = function(req, res, callback) {
|
||||||
var self = this;
|
|
||||||
var user = res.locals.user;
|
var user = res.locals.user;
|
||||||
|
|
||||||
step(
|
this.authorizedByAPIKey(user, res, (err, isAuthorizedByApikey) => {
|
||||||
function () {
|
if (err) {
|
||||||
self.authorizedByAPIKey(user, req, this);
|
return callback(err);
|
||||||
},
|
}
|
||||||
function checkApiKey(err, authorized){
|
|
||||||
req.profiler.done('authorizedByAPIKey');
|
|
||||||
assert.ifError(err);
|
|
||||||
|
|
||||||
// if not authorized by api_key, continue
|
if (isAuthorizedByApikey) {
|
||||||
if (!authorized) {
|
return this.pgConnection.setDBAuth(user, res.locals, 'regular', function (err) {
|
||||||
// not authorized by api_key, check if authorized by signer
|
req.profiler.done('setDBAuth');
|
||||||
return self.authorizedBySigner(res, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// authorized by api key, login as the given username and stop
|
if (err) {
|
||||||
self.pgConnection.setDBAuth(user, res.locals, function(err) {
|
return callback(err);
|
||||||
callback(err, true); // authorized (or error)
|
}
|
||||||
|
|
||||||
|
callback(null, true);
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
function checkSignAuthorized(err, authorized) {
|
|
||||||
|
this.authorizedBySigner(res, (err, isAuthorizedBySigner) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! authorized ) {
|
if (isAuthorizedBySigner) {
|
||||||
// request not authorized by signer.
|
return this.pgConnection.setDBAuth(user, res.locals, 'master', function (err) {
|
||||||
|
req.profiler.done('setDBAuth');
|
||||||
|
|
||||||
// if no signer name was given, let dbparams and
|
if (err) {
|
||||||
// PostgreSQL do the rest.
|
return callback(err);
|
||||||
//
|
}
|
||||||
if ( ! res.locals.signer ) {
|
|
||||||
return callback(null, true); // authorized so far
|
|
||||||
}
|
|
||||||
|
|
||||||
// if signer name was given, return no authorization
|
callback(null, true);
|
||||||
return callback(null, false);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
self.pgConnection.setDBAuth(user, res.locals, function(err) {
|
// if no signer name was given, use default api key
|
||||||
req.profiler.done('setDBAuth');
|
if (!res.locals.signer) {
|
||||||
callback(err, true); // authorized (or error)
|
return this.pgConnection.setDBAuth(user, res.locals, 'default', function (err) {
|
||||||
});
|
req.profiler.done('setDBAuth');
|
||||||
}
|
|
||||||
);
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(null, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// if signer name was given, return no authorization
|
||||||
|
return callback(null, false);
|
||||||
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
var assert = require('assert');
|
|
||||||
var step = require('step');
|
|
||||||
var PSQL = require('cartodb-psql');
|
var PSQL = require('cartodb-psql');
|
||||||
var _ = require('underscore');
|
var _ = require('underscore');
|
||||||
|
const debug = require('debug')('cachechan');
|
||||||
|
|
||||||
function PgConnection(metadataBackend) {
|
function PgConnection(metadataBackend) {
|
||||||
this.metadataBackend = metadataBackend;
|
this.metadataBackend = metadataBackend;
|
||||||
@ -20,45 +19,85 @@ module.exports = PgConnection;
|
|||||||
//
|
//
|
||||||
// @param callback function(err)
|
// @param callback function(err)
|
||||||
//
|
//
|
||||||
PgConnection.prototype.setDBAuth = function(username, params, callback) {
|
PgConnection.prototype.setDBAuth = function(username, params, apikeyType, callback) {
|
||||||
var self = this;
|
if (apikeyType === 'master') {
|
||||||
|
this.metadataBackend.getMasterApikey(username, (err, apikey) => {
|
||||||
var user_params = {};
|
if (err) {
|
||||||
var auth_user = global.environment.postgres_auth_user;
|
if (isNameNotFoundError(err)) {
|
||||||
var auth_pass = global.environment.postgres_auth_pass;
|
err.http_status = 404;
|
||||||
step(
|
}
|
||||||
function getId() {
|
return callback(err);
|
||||||
self.metadataBackend.getUserId(username, this);
|
|
||||||
},
|
|
||||||
function(err, user_id) {
|
|
||||||
assert.ifError(err);
|
|
||||||
user_params.user_id = user_id;
|
|
||||||
var dbuser = _.template(auth_user, user_params);
|
|
||||||
_.extend(params, {dbuser:dbuser});
|
|
||||||
|
|
||||||
// skip looking up user_password if postgres_auth_pass
|
|
||||||
// doesn't contain the "user_password" label
|
|
||||||
if (!auth_pass || ! auth_pass.match(/\buser_password\b/) ) {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.metadataBackend.getUserDBPass(username, this);
|
params.dbuser = apikey.databaseRole;
|
||||||
},
|
params.dbpassword = apikey.databasePassword;
|
||||||
function(err, user_password) {
|
|
||||||
assert.ifError(err);
|
//Remove this block when Auth fallback is not used anymore
|
||||||
user_params.user_password = user_password;
|
// AUTH_FALLBACK
|
||||||
if ( auth_pass ) {
|
if (!params.dbuser && apikey.user_id && global.environment.postgres_auth_user) {
|
||||||
var dbpass = _.template(auth_pass, user_params);
|
params.dbuser = _.template(global.environment.postgres_auth_user, apikey);
|
||||||
_.extend(params, {dbpassword:dbpass});
|
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
},
|
return callback();
|
||||||
function finish(err) {
|
});
|
||||||
callback(err);
|
} else if (apikeyType === 'regular') { //Actually it can be any type of api key
|
||||||
}
|
this.metadataBackend.getApikey(username, params.api_key, (err, apikey) => {
|
||||||
);
|
if (err) {
|
||||||
|
if (isNameNotFoundError(err)) {
|
||||||
|
err.http_status = 404;
|
||||||
|
}
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
params.dbuser = apikey.databaseRole;
|
||||||
|
params.dbpassword = apikey.databasePassword;
|
||||||
|
|
||||||
|
//Remove this block when Auth fallback is not used anymore
|
||||||
|
// AUTH_FALLBACK
|
||||||
|
// master apikey has been recreated from user's metadata
|
||||||
|
if (!params.dbuser && apikey.user_id && apikey.type === 'master' && global.environment.postgres_auth_user) {
|
||||||
|
params.dbuser = _.template(global.environment.postgres_auth_user, apikey);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Remove this block when Auth fallback is not used anymore
|
||||||
|
// AUTH_FALLBACK
|
||||||
|
// default apikey has been recreated from user's metadata
|
||||||
|
if (!params.dbpassword && global.environment.postgres.password) {
|
||||||
|
params.dbpassword = global.environment.postgres.password;
|
||||||
|
}
|
||||||
|
|
||||||
|
return callback();
|
||||||
|
});
|
||||||
|
} else if (apikeyType === 'default') {
|
||||||
|
this.metadataBackend.getApikey(username, 'default_public', (err, apikey) => {
|
||||||
|
if (err) {
|
||||||
|
if (isNameNotFoundError(err)) {
|
||||||
|
err.http_status = 404;
|
||||||
|
}
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
params.dbuser = apikey.databaseRole;
|
||||||
|
params.dbpassword = apikey.databasePassword;
|
||||||
|
|
||||||
|
//Remove this block when Auth fallback is not used anymore
|
||||||
|
// AUTH_FALLBACK
|
||||||
|
if (!params.dbpassword && global.environment.postgres.password) {
|
||||||
|
params.dbpassword = global.environment.postgres.password;
|
||||||
|
}
|
||||||
|
|
||||||
|
return callback();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return callback(new Error(`Invalid Apikey type: ${apikeyType}, valid ones: master, regular, default`));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function isNameNotFoundError (err) {
|
||||||
|
return err.message && -1 !== err.message.indexOf('name not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Set db connection parameters to those for the given username
|
// Set db connection parameters to those for the given username
|
||||||
//
|
//
|
||||||
// @param dbowner cartodb username of database owner,
|
// @param dbowner cartodb username of database owner,
|
||||||
@ -71,36 +110,30 @@ PgConnection.prototype.setDBAuth = function(username, params, callback) {
|
|||||||
// @param callback function(err)
|
// @param callback function(err)
|
||||||
//
|
//
|
||||||
PgConnection.prototype.setDBConn = function(dbowner, params, callback) {
|
PgConnection.prototype.setDBConn = function(dbowner, params, callback) {
|
||||||
var self = this;
|
|
||||||
// Add default database connection parameters
|
|
||||||
// if none given
|
|
||||||
_.defaults(params, {
|
_.defaults(params, {
|
||||||
dbuser: global.environment.postgres.user,
|
// dbuser: global.environment.postgres.user,
|
||||||
dbpassword: global.environment.postgres.password,
|
// dbpassword: global.environment.postgres.password,
|
||||||
dbhost: global.environment.postgres.host,
|
dbhost: global.environment.postgres.host,
|
||||||
dbport: global.environment.postgres.port
|
dbport: global.environment.postgres.port
|
||||||
});
|
});
|
||||||
step(
|
|
||||||
function getConnectionParams() {
|
|
||||||
self.metadataBackend.getUserDBConnectionParams(dbowner, this);
|
|
||||||
},
|
|
||||||
function extendParams(err, dbParams){
|
|
||||||
assert.ifError(err);
|
|
||||||
// we don't want null values or overwrite a non public user
|
|
||||||
if (params.dbuser !== 'publicuser' || !dbParams.dbuser) {
|
|
||||||
delete dbParams.dbuser;
|
|
||||||
}
|
|
||||||
if ( dbParams ) {
|
|
||||||
_.extend(params, dbParams);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
function finish(err) {
|
|
||||||
callback(err);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
this.metadataBackend.getUserDBConnectionParams(dbowner, (err, dbParams) => {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
// we don’t want null values or overwrite a non public user
|
||||||
|
if (params.dbuser !== 'publicuser' || !dbParams.dbuser) {
|
||||||
|
delete dbParams.dbuser;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dbParams) {
|
||||||
|
_.extend(params, dbParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a `cartodb-psql` object for a given username.
|
* Returns a `cartodb-psql` object for a given username.
|
||||||
@ -109,28 +142,37 @@ PgConnection.prototype.setDBConn = function(dbowner, params, callback) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
PgConnection.prototype.getConnection = function(username, callback) {
|
PgConnection.prototype.getConnection = function(username, callback) {
|
||||||
var self = this;
|
debug("getConn1");
|
||||||
|
|
||||||
var params = {};
|
this.getDatabaseParams(username, (err, databaseParams) => {
|
||||||
|
if (err) {
|
||||||
require('debug')('cachechan')("getConn1");
|
return callback(err);
|
||||||
step(
|
|
||||||
function setAuth() {
|
|
||||||
self.setDBAuth(username, params, this);
|
|
||||||
},
|
|
||||||
function setConn(err) {
|
|
||||||
assert.ifError(err);
|
|
||||||
self.setDBConn(username, params, this);
|
|
||||||
},
|
|
||||||
function openConnection(err) {
|
|
||||||
assert.ifError(err);
|
|
||||||
return callback(err, new PSQL({
|
|
||||||
user: params.dbuser,
|
|
||||||
pass: params.dbpass,
|
|
||||||
host: params.dbhost,
|
|
||||||
port: params.dbport,
|
|
||||||
dbname: params.dbname
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
);
|
return callback(err, new PSQL({
|
||||||
|
user: databaseParams.dbuser,
|
||||||
|
pass: databaseParams.dbpass,
|
||||||
|
host: databaseParams.dbhost,
|
||||||
|
port: databaseParams.dbport,
|
||||||
|
dbname: databaseParams.dbname
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
PgConnection.prototype.getDatabaseParams = function(username, callback) {
|
||||||
|
const databaseParams = {};
|
||||||
|
|
||||||
|
this.setDBAuth(username, databaseParams, 'master', err => {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setDBConn(username, databaseParams, err => {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(null, databaseParams);
|
||||||
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
var assert = require('assert');
|
|
||||||
var PSQL = require('cartodb-psql');
|
var PSQL = require('cartodb-psql');
|
||||||
var step = require('step');
|
|
||||||
|
|
||||||
function PgQueryRunner(pgConnection) {
|
function PgQueryRunner(pgConnection) {
|
||||||
this.pgConnection = pgConnection;
|
this.pgConnection = pgConnection;
|
||||||
@ -16,31 +14,23 @@ module.exports = PgQueryRunner;
|
|||||||
* @param {Function} callback function({Error}, {Array}) second argument is guaranteed to be an array
|
* @param {Function} callback function({Error}, {Array}) second argument is guaranteed to be an array
|
||||||
*/
|
*/
|
||||||
PgQueryRunner.prototype.run = function(username, query, callback) {
|
PgQueryRunner.prototype.run = function(username, query, callback) {
|
||||||
var self = this;
|
|
||||||
|
|
||||||
var params = {};
|
this.pgConnection.getDatabaseParams(username, (err, databaseParams) => {
|
||||||
|
if (err) {
|
||||||
step(
|
return callback(err);
|
||||||
function setAuth() {
|
|
||||||
self.pgConnection.setDBAuth(username, params, this);
|
|
||||||
},
|
|
||||||
function setConn(err) {
|
|
||||||
assert.ifError(err);
|
|
||||||
self.pgConnection.setDBConn(username, params, this);
|
|
||||||
},
|
|
||||||
function executeQuery(err) {
|
|
||||||
assert.ifError(err);
|
|
||||||
var psql = new PSQL({
|
|
||||||
user: params.dbuser,
|
|
||||||
pass: params.dbpass,
|
|
||||||
host: params.dbhost,
|
|
||||||
port: params.dbport,
|
|
||||||
dbname: params.dbname
|
|
||||||
});
|
|
||||||
psql.query(query, function(err, resultSet) {
|
|
||||||
resultSet = resultSet || {};
|
|
||||||
return callback(err, resultSet.rows || []);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
const psql = new PSQL({
|
||||||
|
user: databaseParams.dbuser,
|
||||||
|
pass: databaseParams.dbpass,
|
||||||
|
host: databaseParams.dbhost,
|
||||||
|
port: databaseParams.dbport,
|
||||||
|
dbname: databaseParams.dbname
|
||||||
|
});
|
||||||
|
|
||||||
|
psql.query(query, function (err, resultSet) {
|
||||||
|
resultSet = resultSet || {};
|
||||||
|
return callback(err, resultSet.rows || []);
|
||||||
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
@ -283,7 +283,7 @@ LayergroupController.prototype.tileOrLayer = function (req, res, next) {
|
|||||||
function mapController$getTileOrGrid() {
|
function mapController$getTileOrGrid() {
|
||||||
self.tileBackend.getTile(
|
self.tileBackend.getTile(
|
||||||
new MapStoreMapConfigProvider(self.mapStore, res.locals.user, self.userLimitsApi, res.locals),
|
new MapStoreMapConfigProvider(self.mapStore, res.locals.user, self.userLimitsApi, res.locals),
|
||||||
req.params, this
|
res.locals, this
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
function mapController$finalize(err, tile, headers, stats) {
|
function mapController$finalize(err, tile, headers, stats) {
|
||||||
|
@ -1,6 +1,13 @@
|
|||||||
const { templateName } = require('../backends/template_maps');
|
const { templateName } = require('../backends/template_maps');
|
||||||
const cors = require('../middleware/cors');
|
const cors = require('../middleware/cors');
|
||||||
const userMiddleware = require('../middleware/user');
|
const userMiddleware = require('../middleware/user');
|
||||||
|
const localsMiddleware = require('../middleware/context/locals');
|
||||||
|
const apikeyCredentialsMiddleware = require('../middleware/context/apikey-credentials');
|
||||||
|
|
||||||
|
const apikeyMiddleware = [
|
||||||
|
localsMiddleware,
|
||||||
|
apikeyCredentialsMiddleware(),
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {AuthApi} authApi
|
* @param {AuthApi} authApi
|
||||||
@ -22,6 +29,7 @@ NamedMapsAdminController.prototype.register = function (app) {
|
|||||||
`${base_url_templated}/`,
|
`${base_url_templated}/`,
|
||||||
cors(),
|
cors(),
|
||||||
userMiddleware,
|
userMiddleware,
|
||||||
|
apikeyMiddleware,
|
||||||
this.checkContentType('POST', 'POST TEMPLATE'),
|
this.checkContentType('POST', 'POST TEMPLATE'),
|
||||||
this.authorizedByAPIKey('create', 'POST TEMPLATE'),
|
this.authorizedByAPIKey('create', 'POST TEMPLATE'),
|
||||||
this.create()
|
this.create()
|
||||||
@ -31,6 +39,7 @@ NamedMapsAdminController.prototype.register = function (app) {
|
|||||||
`${base_url_templated}/:template_id`,
|
`${base_url_templated}/:template_id`,
|
||||||
cors(),
|
cors(),
|
||||||
userMiddleware,
|
userMiddleware,
|
||||||
|
apikeyMiddleware,
|
||||||
this.checkContentType('PUT', 'PUT TEMPLATE'),
|
this.checkContentType('PUT', 'PUT TEMPLATE'),
|
||||||
this.authorizedByAPIKey('update', 'PUT TEMPLATE'),
|
this.authorizedByAPIKey('update', 'PUT TEMPLATE'),
|
||||||
this.update()
|
this.update()
|
||||||
@ -40,6 +49,7 @@ NamedMapsAdminController.prototype.register = function (app) {
|
|||||||
`${base_url_templated}/:template_id`,
|
`${base_url_templated}/:template_id`,
|
||||||
cors(),
|
cors(),
|
||||||
userMiddleware,
|
userMiddleware,
|
||||||
|
apikeyMiddleware,
|
||||||
this.authorizedByAPIKey('get', 'GET TEMPLATE'),
|
this.authorizedByAPIKey('get', 'GET TEMPLATE'),
|
||||||
this.retrieve()
|
this.retrieve()
|
||||||
);
|
);
|
||||||
@ -48,6 +58,7 @@ NamedMapsAdminController.prototype.register = function (app) {
|
|||||||
`${base_url_templated}/:template_id`,
|
`${base_url_templated}/:template_id`,
|
||||||
cors(),
|
cors(),
|
||||||
userMiddleware,
|
userMiddleware,
|
||||||
|
apikeyMiddleware,
|
||||||
this.authorizedByAPIKey('delete', 'DELETE TEMPLATE'),
|
this.authorizedByAPIKey('delete', 'DELETE TEMPLATE'),
|
||||||
this.destroy()
|
this.destroy()
|
||||||
);
|
);
|
||||||
@ -56,6 +67,7 @@ NamedMapsAdminController.prototype.register = function (app) {
|
|||||||
`${base_url_templated}/`,
|
`${base_url_templated}/`,
|
||||||
cors(),
|
cors(),
|
||||||
userMiddleware,
|
userMiddleware,
|
||||||
|
apikeyMiddleware,
|
||||||
this.authorizedByAPIKey('list', 'GET TEMPLATE LIST'),
|
this.authorizedByAPIKey('list', 'GET TEMPLATE LIST'),
|
||||||
this.list()
|
this.list()
|
||||||
);
|
);
|
||||||
@ -69,8 +81,7 @@ NamedMapsAdminController.prototype.register = function (app) {
|
|||||||
NamedMapsAdminController.prototype.authorizedByAPIKey = function (action, label) {
|
NamedMapsAdminController.prototype.authorizedByAPIKey = function (action, label) {
|
||||||
return function authorizedByAPIKeyMiddleware (req, res, next) {
|
return function authorizedByAPIKeyMiddleware (req, res, next) {
|
||||||
const { user } = res.locals;
|
const { user } = res.locals;
|
||||||
|
this.authApi.authorizedByAPIKey(user, res, (err, authenticated) => {
|
||||||
this.authApi.authorizedByAPIKey(user, req, (err, authenticated) => {
|
|
||||||
if (err) {
|
if (err) {
|
||||||
return next(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
|
86
lib/cartodb/middleware/context/apikey-credentials.js
Normal file
86
lib/cartodb/middleware/context/apikey-credentials.js
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports = function apikeyToken () {
|
||||||
|
return function apikeyTokenMiddleware(req, res, next) {
|
||||||
|
const apikeyCredentials = getApikeyCredentialsFromRequest(req);
|
||||||
|
res.locals.api_key = apikeyCredentials.token;
|
||||||
|
res.locals.apikeyUsername = apikeyCredentials.username;
|
||||||
|
return next();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
const basicAuth = require('basic-auth');
|
||||||
|
|
||||||
|
function getApikeyCredentialsFromRequest(req) {
|
||||||
|
let apikeyCredentials = {
|
||||||
|
token: null,
|
||||||
|
username: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (let getter of apikeyGetters) {
|
||||||
|
apikeyCredentials = getter(req);
|
||||||
|
if (apikeyTokenFound(apikeyCredentials)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return apikeyCredentials;
|
||||||
|
}
|
||||||
|
|
||||||
|
const apikeyGetters = [
|
||||||
|
getApikeyTokenFromHeaderAuthorization,
|
||||||
|
getApikeyTokenFromRequestQueryString,
|
||||||
|
getApikeyTokenFromRequestBody,
|
||||||
|
];
|
||||||
|
|
||||||
|
function getApikeyTokenFromHeaderAuthorization(req) {
|
||||||
|
const credentials = basicAuth(req);
|
||||||
|
|
||||||
|
if (credentials) {
|
||||||
|
return {
|
||||||
|
username: credentials.username,
|
||||||
|
token: credentials.pass
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
username: null,
|
||||||
|
token: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getApikeyTokenFromRequestQueryString(req) {
|
||||||
|
let token = null;
|
||||||
|
|
||||||
|
if (req.query && req.query.api_key) {
|
||||||
|
token = req.query.api_key;
|
||||||
|
} else if (req.query && req.query.map_key) {
|
||||||
|
token = req.query.map_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
username: null,
|
||||||
|
token: token,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getApikeyTokenFromRequestBody(req) {
|
||||||
|
let token = null;
|
||||||
|
|
||||||
|
if (req.body && req.body.api_key) {
|
||||||
|
token = req.body.api_key;
|
||||||
|
} else if (req.body && req.body.map_key) {
|
||||||
|
token = req.body.map_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
username: null,
|
||||||
|
token: token,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function apikeyTokenFound(apikey) {
|
||||||
|
return !!apikey && !!apikey.token;
|
||||||
|
}
|
@ -12,8 +12,6 @@ module.exports = function dbConnSetupMiddleware(pgConnection) {
|
|||||||
return next(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add default database connection parameters
|
|
||||||
// if none given
|
|
||||||
_.defaults(res.locals, {
|
_.defaults(res.locals, {
|
||||||
dbuser: global.environment.postgres.user,
|
dbuser: global.environment.postgres.user,
|
||||||
dbpassword: global.environment.postgres.password,
|
dbpassword: global.environment.postgres.password,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
const locals = require('./locals');
|
const locals = require('./locals');
|
||||||
const cleanUpQueryParams = require('./clean-up-query-params');
|
const cleanUpQueryParams = require('./clean-up-query-params');
|
||||||
const layergroupToken = require('./layergroup-token');
|
const layergroupToken = require('./layergroup-token');
|
||||||
|
const apikeyCredentials = require('./apikey-credentials');
|
||||||
const authorize = require('./authorize');
|
const authorize = require('./authorize');
|
||||||
const dbConnSetup = require('./db-conn-setup');
|
const dbConnSetup = require('./db-conn-setup');
|
||||||
|
|
||||||
@ -9,6 +10,7 @@ module.exports = function prepareContextMiddleware(authApi, pgConnection) {
|
|||||||
locals,
|
locals,
|
||||||
cleanUpQueryParams(),
|
cleanUpQueryParams(),
|
||||||
layergroupToken,
|
layergroupToken,
|
||||||
|
apikeyCredentials(),
|
||||||
authorize(authApi),
|
authorize(authApi),
|
||||||
dbConnSetup(pgConnection)
|
dbConnSetup(pgConnection)
|
||||||
];
|
];
|
||||||
|
@ -108,8 +108,7 @@ MapConfigNamedLayersAdapter.prototype.getMapConfig = function (user, requestMapC
|
|||||||
var dbAuth = {};
|
var dbAuth = {};
|
||||||
|
|
||||||
if (_.some(layers, isNamedTypeLayer)) {
|
if (_.some(layers, isNamedTypeLayer)) {
|
||||||
// Lazy load dbAuth
|
this.pgConnection.setDBAuth(user, dbAuth, 'master', function(err) {
|
||||||
this.pgConnection.setDBAuth(user, dbAuth, function(err) {
|
|
||||||
if (err) {
|
if (err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
|
@ -232,19 +232,19 @@ function configHash(config) {
|
|||||||
module.exports.configHash = configHash;
|
module.exports.configHash = configHash;
|
||||||
|
|
||||||
NamedMapMapConfigProvider.prototype.setDBParams = function(cdbuser, params, callback) {
|
NamedMapMapConfigProvider.prototype.setDBParams = function(cdbuser, params, callback) {
|
||||||
var self = this;
|
this.pgConnection.getDatabaseParams(cdbuser, (err, databaseParams) => {
|
||||||
step(
|
if (err) {
|
||||||
function setAuth() {
|
return callback(err);
|
||||||
self.pgConnection.setDBAuth(cdbuser, params, this);
|
|
||||||
},
|
|
||||||
function setConn(err) {
|
|
||||||
assert.ifError(err);
|
|
||||||
self.pgConnection.setDBConn(cdbuser, params, this);
|
|
||||||
},
|
|
||||||
function finish(err) {
|
|
||||||
callback(err);
|
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
params.dbuser = databaseParams.dbuser;
|
||||||
|
params.dbpass = databaseParams.dbpass;
|
||||||
|
params.dbhost = databaseParams.dbhost;
|
||||||
|
params.dbport = databaseParams.dbport;
|
||||||
|
params.dbname = databaseParams.dbname;
|
||||||
|
|
||||||
|
callback();
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
NamedMapMapConfigProvider.prototype.getTemplateName = function() {
|
NamedMapMapConfigProvider.prototype.getTemplateName = function() {
|
||||||
|
@ -24,11 +24,12 @@
|
|||||||
"Simon Martin <simon@carto.com>"
|
"Simon Martin <simon@carto.com>"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"basic-auth": "^2.0.0",
|
||||||
"body-parser": "^1.18.2",
|
"body-parser": "^1.18.2",
|
||||||
"camshaft": "0.61.2",
|
"camshaft": "0.61.2",
|
||||||
"cartodb-psql": "0.10.2",
|
"cartodb-psql": "0.10.2",
|
||||||
"cartodb-query-tables": "0.3.0",
|
"cartodb-query-tables": "0.3.0",
|
||||||
"cartodb-redis": "0.15.0",
|
"cartodb-redis": "0.16.0",
|
||||||
"debug": "^3.1.0",
|
"debug": "^3.1.0",
|
||||||
"dot": "~1.0.2",
|
"dot": "~1.0.2",
|
||||||
"express": "~4.16.0",
|
"express": "~4.16.0",
|
||||||
|
@ -188,7 +188,7 @@ describe('analysis-layers error cases', function() {
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
var testClient = new TestClient(mapConfig, 11111);
|
var testClient = new TestClient(mapConfig); //No apikey provided -> using default public apikey
|
||||||
|
|
||||||
testClient.getLayergroup({ response: AUTH_ERROR_RESPONSE }, function(err, layergroupResult) {
|
testClient.getLayergroup({ response: AUTH_ERROR_RESPONSE }, function(err, layergroupResult) {
|
||||||
assert.ok(!err, err);
|
assert.ok(!err, err);
|
||||||
|
181
test/acceptance/auth/authorization-fallback.js
Normal file
181
test/acceptance/auth/authorization-fallback.js
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
//Remove this file when Auth fallback is not used anymore
|
||||||
|
// AUTH_FALLBACK
|
||||||
|
|
||||||
|
const assert = require('../../support/assert');
|
||||||
|
const testHelper = require('../../support/test_helper');
|
||||||
|
const CartodbWindshaft = require('../../../lib/cartodb/server');
|
||||||
|
const serverOptions = require('../../../lib/cartodb/server_options');
|
||||||
|
const server = new CartodbWindshaft(serverOptions);
|
||||||
|
var LayergroupToken = require('../../../lib/cartodb/models/layergroup-token');
|
||||||
|
|
||||||
|
function singleLayergroupConfig(sql, cartocss) {
|
||||||
|
return {
|
||||||
|
version: '1.7.0',
|
||||||
|
layers: [
|
||||||
|
{
|
||||||
|
type: 'mapnik',
|
||||||
|
options: {
|
||||||
|
sql: sql,
|
||||||
|
cartocss: cartocss,
|
||||||
|
cartocss_version: '2.3.0'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createRequest(layergroup, userHost, apiKey) {
|
||||||
|
var url = layergroupUrl;
|
||||||
|
if (apiKey) {
|
||||||
|
url += '?api_key=' + apiKey;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
url: url,
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
host: userHost || 'localhost',
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
data: JSON.stringify(layergroup)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var layergroupUrl = '/api/v1/map';
|
||||||
|
var pointSqlMaster = "select * from test_table_private_1";
|
||||||
|
var pointSqlPublic = "select * from test_table";
|
||||||
|
var keysToDelete;
|
||||||
|
|
||||||
|
describe('authorization fallback', function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
keysToDelete = {};
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function (done) {
|
||||||
|
testHelper.deleteRedisKeys(keysToDelete, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("succeed with master", function (done) {
|
||||||
|
var layergroup = singleLayergroupConfig(pointSqlMaster, '#layer { marker-fill:red; }');
|
||||||
|
|
||||||
|
assert.response(server,
|
||||||
|
createRequest(layergroup, 'user_previous_to_project_auth', '4444'),
|
||||||
|
{
|
||||||
|
status: 200
|
||||||
|
},
|
||||||
|
function (res, err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
|
||||||
|
var parsed = JSON.parse(res.body);
|
||||||
|
assert.ok(parsed.layergroupid);
|
||||||
|
assert.equal(res.headers['x-layergroup-id'], parsed.layergroupid);
|
||||||
|
|
||||||
|
keysToDelete['map_cfg|' + LayergroupToken.parse(parsed.layergroupid).token] = 0;
|
||||||
|
keysToDelete['user:user_previous_to_project_auth:mapviews:global'] = 5;
|
||||||
|
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it("succeed with default - sending default_public", function (done) {
|
||||||
|
var layergroup = singleLayergroupConfig(pointSqlPublic, '#layer { marker-fill:red; }');
|
||||||
|
|
||||||
|
assert.response(server,
|
||||||
|
createRequest(layergroup, 'user_previous_to_project_auth', 'default_public'),
|
||||||
|
{
|
||||||
|
status: 200
|
||||||
|
},
|
||||||
|
function (res, err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
|
||||||
|
var parsed = JSON.parse(res.body);
|
||||||
|
assert.ok(parsed.layergroupid);
|
||||||
|
assert.equal(res.headers['x-layergroup-id'], parsed.layergroupid);
|
||||||
|
|
||||||
|
keysToDelete['map_cfg|' + LayergroupToken.parse(parsed.layergroupid).token] = 0;
|
||||||
|
keysToDelete['user:user_previous_to_project_auth:mapviews:global'] = 5;
|
||||||
|
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("succeed with default - sending no api key token", function (done) {
|
||||||
|
var layergroup = singleLayergroupConfig(pointSqlPublic, '#layer { marker-fill:red; }');
|
||||||
|
|
||||||
|
assert.response(server,
|
||||||
|
createRequest(layergroup, 'user_previous_to_project_auth'),
|
||||||
|
{
|
||||||
|
status: 200
|
||||||
|
},
|
||||||
|
function (res, err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
|
||||||
|
var parsed = JSON.parse(res.body);
|
||||||
|
assert.ok(parsed.layergroupid);
|
||||||
|
assert.equal(res.headers['x-layergroup-id'], parsed.layergroupid);
|
||||||
|
|
||||||
|
keysToDelete['map_cfg|' + LayergroupToken.parse(parsed.layergroupid).token] = 0;
|
||||||
|
keysToDelete['user:user_previous_to_project_auth:mapviews:global'] = 5;
|
||||||
|
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("succeed with non-existent api key - defaults to default", function (done) {
|
||||||
|
var layergroup = singleLayergroupConfig(pointSqlPublic, '#layer { marker-fill:red; }');
|
||||||
|
|
||||||
|
assert.response(server,
|
||||||
|
createRequest(layergroup, 'user_previous_to_project_auth', 'THIS-API-KEY-DOESNT-EXIST'),
|
||||||
|
{
|
||||||
|
status: 200
|
||||||
|
},
|
||||||
|
function (res, err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
|
||||||
|
var parsed = JSON.parse(res.body);
|
||||||
|
assert.ok(parsed.layergroupid);
|
||||||
|
assert.equal(res.headers['x-layergroup-id'], parsed.layergroupid);
|
||||||
|
|
||||||
|
keysToDelete['map_cfg|' + LayergroupToken.parse(parsed.layergroupid).token] = 0;
|
||||||
|
keysToDelete['user:user_previous_to_project_auth:mapviews:global'] = 5;
|
||||||
|
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("fail with default", function (done) {
|
||||||
|
var layergroup = singleLayergroupConfig(pointSqlMaster, '#layer { marker-fill:red; }');
|
||||||
|
|
||||||
|
assert.response(server,
|
||||||
|
createRequest(layergroup, 'user_previous_to_project_auth', 'default_public'),
|
||||||
|
{
|
||||||
|
status: 403
|
||||||
|
},
|
||||||
|
function (res, err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("fail with non-existent api key - defaults to default", function (done) {
|
||||||
|
var layergroup = singleLayergroupConfig(pointSqlMaster, '#layer { marker-fill:red; }');
|
||||||
|
|
||||||
|
assert.response(server,
|
||||||
|
createRequest(layergroup, 'user_previous_to_project_auth', 'THIS-API-KEY-DOESNT-EXIST'),
|
||||||
|
{
|
||||||
|
status: 403
|
||||||
|
},
|
||||||
|
function (res, err) {
|
||||||
|
assert.ifError(err);
|
||||||
|
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
431
test/acceptance/auth/authorization.js
Normal file
431
test/acceptance/auth/authorization.js
Normal file
@ -0,0 +1,431 @@
|
|||||||
|
require('../../support/test_helper');
|
||||||
|
|
||||||
|
const assert = require('../../support/assert');
|
||||||
|
const TestClient = require('../../support/test-client');
|
||||||
|
const mapnik = require('windshaft').mapnik;
|
||||||
|
|
||||||
|
const PERMISSION_DENIED_RESPONSE = {
|
||||||
|
status: 403,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json; charset=utf-8'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('authorization', function() {
|
||||||
|
it('should create a layergroup with regular apikey token', function(done) {
|
||||||
|
const apikeyToken = 'regular1';
|
||||||
|
const mapConfig = {
|
||||||
|
version: '1.7.0',
|
||||||
|
layers: [
|
||||||
|
{
|
||||||
|
options: {
|
||||||
|
sql: 'select * FROM test_table_localhost_regular1',
|
||||||
|
cartocss: TestClient.CARTOCSS.POINTS,
|
||||||
|
cartocss_version: '2.3.0'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
const testClient = new TestClient(mapConfig, apikeyToken);
|
||||||
|
|
||||||
|
testClient.getLayergroup(function (err, layergroupResult) {
|
||||||
|
assert.ifError(err);
|
||||||
|
|
||||||
|
assert.ok(layergroupResult.layergroupid);
|
||||||
|
|
||||||
|
testClient.drain(done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create and get a named map tile using a regular apikey token', function (done) {
|
||||||
|
const apikeyToken = 'regular1';
|
||||||
|
const mapConfig = {
|
||||||
|
version: '1.7.0',
|
||||||
|
layers: [
|
||||||
|
{
|
||||||
|
options: {
|
||||||
|
sql: 'select * FROM test_table_localhost_regular1',
|
||||||
|
cartocss: TestClient.CARTOCSS.POINTS,
|
||||||
|
cartocss_version: '2.3.0'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
const testClient = new TestClient(mapConfig, apikeyToken);
|
||||||
|
|
||||||
|
testClient.getTile(0, 0, 0, function (err, res, tile) {
|
||||||
|
assert.ifError(err);
|
||||||
|
|
||||||
|
assert.equal(res.statusCode, 200);
|
||||||
|
assert.ok(tile instanceof mapnik.Image);
|
||||||
|
|
||||||
|
testClient.drain(done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail getting a named map tile with default apikey token', function (done) {
|
||||||
|
const apikeyTokenCreate = 'regular1';
|
||||||
|
const apikeyTokenGet = 'default_public';
|
||||||
|
const mapConfig = {
|
||||||
|
version: '1.7.0',
|
||||||
|
layers: [
|
||||||
|
{
|
||||||
|
options: {
|
||||||
|
sql: 'select * FROM test_table_localhost_regular1',
|
||||||
|
cartocss: TestClient.CARTOCSS.POINTS,
|
||||||
|
cartocss_version: '2.3.0'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const testClientCreate = new TestClient(mapConfig, apikeyTokenCreate);
|
||||||
|
testClientCreate.getLayergroup(function (err, layergroupResult) {
|
||||||
|
assert.ifError(err);
|
||||||
|
const layergroupId = layergroupResult.layergroupid;
|
||||||
|
|
||||||
|
const testClientGet = new TestClient({}, apikeyTokenGet);
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
layergroupid: layergroupId,
|
||||||
|
response: PERMISSION_DENIED_RESPONSE
|
||||||
|
};
|
||||||
|
|
||||||
|
testClientGet.getTile(0, 0, 0, params, function(err, res, body) {
|
||||||
|
|
||||||
|
assert.ifError(err);
|
||||||
|
|
||||||
|
assert.ok(body.hasOwnProperty('errors'));
|
||||||
|
assert.equal(body.errors.length, 1);
|
||||||
|
assert.ok(body.errors[0].match(/permission denied/), body.errors[0]);
|
||||||
|
|
||||||
|
testClientGet.drain(done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail creating a layergroup with default apikey token', function (done) {
|
||||||
|
const apikeyToken = 'default_public';
|
||||||
|
const mapConfig = {
|
||||||
|
version: '1.7.0',
|
||||||
|
layers: [
|
||||||
|
{
|
||||||
|
options: {
|
||||||
|
sql: 'select * FROM test_table_localhost_regular1',
|
||||||
|
cartocss: TestClient.CARTOCSS.POINTS,
|
||||||
|
cartocss_version: '2.3.0'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
const testClient = new TestClient(mapConfig, apikeyToken);
|
||||||
|
|
||||||
|
testClient.getLayergroup({ response: { status: 403 } }, function (err, layergroupResult) {
|
||||||
|
assert.ifError(err);
|
||||||
|
|
||||||
|
assert.ok(layergroupResult.hasOwnProperty('errors'));
|
||||||
|
assert.equal(layergroupResult.errors.length, 1);
|
||||||
|
assert.ok(layergroupResult.errors[0].match(/permission denied/), layergroupResult.errors[0]);
|
||||||
|
|
||||||
|
testClient.drain(done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create a layergroup with default apikey token', function (done) {
|
||||||
|
const apikeyToken = 'default_public';
|
||||||
|
const mapConfig = {
|
||||||
|
version: '1.7.0',
|
||||||
|
layers: [
|
||||||
|
{
|
||||||
|
options: {
|
||||||
|
sql: 'select * FROM test_table',
|
||||||
|
cartocss: TestClient.CARTOCSS.POINTS,
|
||||||
|
cartocss_version: '2.3.0'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
const testClient = new TestClient(mapConfig, apikeyToken);
|
||||||
|
|
||||||
|
testClient.getLayergroup(function (err, layergroupResult) {
|
||||||
|
assert.ifError(err);
|
||||||
|
|
||||||
|
assert.ok(layergroupResult.layergroupid);
|
||||||
|
|
||||||
|
testClient.drain(done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create and get a tile with default apikey token', function (done) {
|
||||||
|
const apikeyToken = 'default_public';
|
||||||
|
const mapConfig = {
|
||||||
|
version: '1.7.0',
|
||||||
|
layers: [
|
||||||
|
{
|
||||||
|
options: {
|
||||||
|
sql: 'select * FROM test_table',
|
||||||
|
cartocss: TestClient.CARTOCSS.POINTS,
|
||||||
|
cartocss_version: '2.3.0'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
const testClient = new TestClient(mapConfig, apikeyToken);
|
||||||
|
|
||||||
|
testClient.getTile(0, 0, 0, function (err, res, tile) {
|
||||||
|
assert.ifError(err);
|
||||||
|
|
||||||
|
assert.equal(res.statusCode, 200);
|
||||||
|
assert.ok(tile instanceof mapnik.Image);
|
||||||
|
|
||||||
|
testClient.drain(done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail if apikey does not grant access to table', function (done) {
|
||||||
|
const mapConfig = {
|
||||||
|
version: '1.7.0',
|
||||||
|
layers: [
|
||||||
|
{
|
||||||
|
options: {
|
||||||
|
sql: 'select * FROM test_table_localhost_regular1',
|
||||||
|
cartocss: TestClient.CARTOCSS.POINTS,
|
||||||
|
cartocss_version: '2.3.0'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
const testClient = new TestClient(mapConfig); //no apikey provided, using default
|
||||||
|
|
||||||
|
testClient.getLayergroup({ response: { status: 403 } }, function (err, layergroupResult) { //TODO 401
|
||||||
|
assert.ifError(err);
|
||||||
|
|
||||||
|
assert.ok(layergroupResult.hasOwnProperty('errors'));
|
||||||
|
assert.equal(layergroupResult.errors.length, 1);
|
||||||
|
assert.ok(layergroupResult.errors[0].match(/permission denied/), layergroupResult.errors[0]);
|
||||||
|
|
||||||
|
testClient.drain(done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should forbide access to API if API key does not grant access', function (done) {
|
||||||
|
const apikeyToken = 'regular2';
|
||||||
|
const mapConfig = {
|
||||||
|
version: '1.7.0',
|
||||||
|
layers: [
|
||||||
|
{
|
||||||
|
options: {
|
||||||
|
sql: 'select * FROM test_table_localhost_regular1',
|
||||||
|
cartocss: TestClient.CARTOCSS.POINTS,
|
||||||
|
cartocss_version: '2.3.0'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
const testClient = new TestClient(mapConfig, apikeyToken);
|
||||||
|
|
||||||
|
testClient.getLayergroup({ response: { status: 403 } }, function (err, layergroupResult) {
|
||||||
|
assert.ifError(err);
|
||||||
|
|
||||||
|
assert.ok(layergroupResult.hasOwnProperty('errors'));
|
||||||
|
assert.equal(layergroupResult.errors.length, 1);
|
||||||
|
assert.ok(layergroupResult.errors[0].match(/Forbidden/), layergroupResult.errors[0]);
|
||||||
|
|
||||||
|
testClient.drain(done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create a layergroup with a source analysis using a default apikey token', function (done) {
|
||||||
|
const apikeyToken = 'default_public';
|
||||||
|
const mapConfig = {
|
||||||
|
version: '1.7.0',
|
||||||
|
layers: [
|
||||||
|
{
|
||||||
|
type: 'cartodb',
|
||||||
|
options: {
|
||||||
|
source: {
|
||||||
|
id: 'HEAD'
|
||||||
|
},
|
||||||
|
cartocss: TestClient.CARTOCSS.POINTS,
|
||||||
|
cartocss_version: '2.3.0'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
analyses: [
|
||||||
|
{
|
||||||
|
id: 'HEAD',
|
||||||
|
type: 'source',
|
||||||
|
params: {
|
||||||
|
query: 'select * from populated_places_simple_reduced'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
const testClient = new TestClient(mapConfig, apikeyToken);
|
||||||
|
|
||||||
|
testClient.getLayergroup(function (err, layergroupResult) {
|
||||||
|
assert.ifError(err);
|
||||||
|
|
||||||
|
assert.ok(layergroupResult.layergroupid);
|
||||||
|
|
||||||
|
testClient.drain(done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create a layergroup with a source analysis using a regular apikey token', function (done) {
|
||||||
|
const apikeyToken = 'regular1';
|
||||||
|
const mapConfig = {
|
||||||
|
version: '1.7.0',
|
||||||
|
layers: [
|
||||||
|
{
|
||||||
|
type: 'cartodb',
|
||||||
|
options: {
|
||||||
|
source: {
|
||||||
|
id: 'HEAD'
|
||||||
|
},
|
||||||
|
cartocss: TestClient.CARTOCSS.POINTS,
|
||||||
|
cartocss_version: '2.3.0'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
analyses: [
|
||||||
|
{
|
||||||
|
id: 'HEAD',
|
||||||
|
type: 'source',
|
||||||
|
params: {
|
||||||
|
query: 'select * from test_table_localhost_regular1'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
const testClient = new TestClient(mapConfig, apikeyToken);
|
||||||
|
|
||||||
|
testClient.getLayergroup(function (err, layergroupResult) {
|
||||||
|
assert.ifError(err);
|
||||||
|
|
||||||
|
assert.ok(layergroupResult.layergroupid);
|
||||||
|
|
||||||
|
testClient.drain(done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Warning: TBA
|
||||||
|
it('should create a layergroup with a buffer analysis using a regular apikey token', function (done) {
|
||||||
|
const apikeyToken = 'regular1';
|
||||||
|
const mapConfig = {
|
||||||
|
version: '1.7.0',
|
||||||
|
layers: [
|
||||||
|
{
|
||||||
|
type: 'cartodb',
|
||||||
|
options: {
|
||||||
|
source: {
|
||||||
|
id: 'HEAD1'
|
||||||
|
},
|
||||||
|
cartocss: TestClient.CARTOCSS.POINTS,
|
||||||
|
cartocss_version: '2.3.0'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
analyses: [
|
||||||
|
{
|
||||||
|
id: "HEAD1",
|
||||||
|
type: "buffer",
|
||||||
|
params: {
|
||||||
|
source: {
|
||||||
|
id: 'HEAD2',
|
||||||
|
type: 'source',
|
||||||
|
params: {
|
||||||
|
query: 'select * from test_table_localhost_regular1'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
radius: 50000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
const testClient = new TestClient(mapConfig, apikeyToken);
|
||||||
|
|
||||||
|
testClient.getLayergroup(function (err, layergroupResult) {
|
||||||
|
assert.ifError(err);
|
||||||
|
|
||||||
|
assert.ok(layergroupResult.layergroupid);
|
||||||
|
|
||||||
|
testClient.drain(done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create and get a named map tile using a regular apikey token', function (done) {
|
||||||
|
const apikeyToken = 'regular1';
|
||||||
|
|
||||||
|
const template = {
|
||||||
|
version: '0.0.1',
|
||||||
|
name: 'auth-api-template',
|
||||||
|
placeholders: {
|
||||||
|
buffersize: {
|
||||||
|
type: 'number',
|
||||||
|
default: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
layergroup: {
|
||||||
|
version: '1.7.0',
|
||||||
|
layers: [{
|
||||||
|
type: 'cartodb',
|
||||||
|
options: {
|
||||||
|
sql: 'select * from test_table_localhost_regular1',
|
||||||
|
cartocss: TestClient.CARTOCSS.POINTS,
|
||||||
|
cartocss_version: '2.3.0',
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const testClient = new TestClient(template, apikeyToken);
|
||||||
|
|
||||||
|
testClient.getTile(0, 0, 0, function (err, res, tile) {
|
||||||
|
assert.ifError(err);
|
||||||
|
|
||||||
|
assert.equal(res.statusCode, 200);
|
||||||
|
assert.ok(tile instanceof mapnik.Image);
|
||||||
|
|
||||||
|
testClient.drain(done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail creating a named map using a regular apikey token and a private table', function (done) {
|
||||||
|
const apikeyToken = 'regular1';
|
||||||
|
|
||||||
|
const template = {
|
||||||
|
version: '0.0.1',
|
||||||
|
name: 'auth-api-template-private',
|
||||||
|
placeholders: {
|
||||||
|
buffersize: {
|
||||||
|
type: 'number',
|
||||||
|
default: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
layergroup: {
|
||||||
|
version: '1.7.0',
|
||||||
|
layers: [{
|
||||||
|
type: 'cartodb',
|
||||||
|
options: {
|
||||||
|
sql: 'select * from populated_places_simple_reduced_private',
|
||||||
|
cartocss: TestClient.CARTOCSS.POINTS,
|
||||||
|
cartocss_version: '2.3.0',
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const testClient = new TestClient(template, apikeyToken);
|
||||||
|
|
||||||
|
testClient.getTile(0, 0, 0, { response: PERMISSION_DENIED_RESPONSE }, function (err, res, body) {
|
||||||
|
assert.ifError(err);
|
||||||
|
|
||||||
|
assert.ok(body.hasOwnProperty('errors'));
|
||||||
|
assert.equal(body.errors.length, 1);
|
||||||
|
assert.ok(body.errors[0].match(/permission denied/), body.errors[0]);
|
||||||
|
|
||||||
|
testClient.drain(done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -131,6 +131,19 @@ HMSET rails:users:cartodb250user id ${TESTUSERID} \
|
|||||||
map_key 4321
|
map_key 4321
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|
||||||
|
# Remove this block when Auth fallback is not used anymore
|
||||||
|
# AUTH_FALLBACK
|
||||||
|
# A user to test auth fallback to no api keys mode
|
||||||
|
cat <<EOF | redis-cli -p ${REDIS_PORT} -n 5
|
||||||
|
HMSET rails:users:user_previous_to_project_auth id ${TESTUSERID} \
|
||||||
|
database_name "${TEST_DB}" \
|
||||||
|
database_host "localhost" \
|
||||||
|
database_password "${TESTPASS}" \
|
||||||
|
database_publicuser "${PUBLICUSER}"\
|
||||||
|
map_key 4444
|
||||||
|
EOF
|
||||||
|
|
||||||
cat <<EOF | redis-cli -p ${REDIS_PORT} -n 0
|
cat <<EOF | redis-cli -p ${REDIS_PORT} -n 0
|
||||||
HSET rails:${TEST_DB}:my_table infowindow "this, that, the other"
|
HSET rails:${TEST_DB}:my_table infowindow "this, that, the other"
|
||||||
HSET rails:${TEST_DB}:test_table_private_1 privacy "0"
|
HSET rails:${TEST_DB}:test_table_private_1 privacy "0"
|
||||||
@ -138,4 +151,77 @@ EOF
|
|||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# API keys ==============================
|
||||||
|
|
||||||
|
# User localhost -----------------------
|
||||||
|
|
||||||
|
# API Key Master
|
||||||
|
cat <<EOF | redis-cli -p ${REDIS_PORT} -n 5
|
||||||
|
HMSET api_keys:localhost:1234 \
|
||||||
|
user "localhost" \
|
||||||
|
type "master" \
|
||||||
|
grants_sql "true" \
|
||||||
|
grants_maps "true" \
|
||||||
|
database_role "${TESTUSER}" \
|
||||||
|
database_password "${TESTPASS}"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# API Key Default public
|
||||||
|
cat <<EOF | redis-cli -p ${REDIS_PORT} -n 5
|
||||||
|
HMSET api_keys:localhost:default_public \
|
||||||
|
user "localhost" \
|
||||||
|
type "default" \
|
||||||
|
grants_sql "true" \
|
||||||
|
grants_maps "true" \
|
||||||
|
database_role "test_windshaft_publicuser" \
|
||||||
|
database_password "public"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# API Key Regular
|
||||||
|
cat <<EOF | redis-cli -p ${REDIS_PORT} -n 5
|
||||||
|
HMSET api_keys:localhost:regular1 \
|
||||||
|
user "localhost" \
|
||||||
|
type "regular" \
|
||||||
|
grants_sql "true" \
|
||||||
|
grants_maps "true" \
|
||||||
|
database_role "test_windshaft_regular1" \
|
||||||
|
database_password "regular1"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# API Key Regular 2 no Maps API access, only to check grants permissions to the API
|
||||||
|
cat <<EOF | redis-cli -p ${REDIS_PORT} -n 5
|
||||||
|
HMSET api_keys:localhost:regular2 \
|
||||||
|
user "localhost" \
|
||||||
|
type "regular" \
|
||||||
|
grants_sql "true" \
|
||||||
|
grants_maps "false" \
|
||||||
|
database_role "test_windshaft_publicuser" \
|
||||||
|
database_password "public"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# User cartodb250user -----------------------
|
||||||
|
|
||||||
|
# API Key Master
|
||||||
|
cat <<EOF | redis-cli -p ${REDIS_PORT} -n 5
|
||||||
|
HMSET api_keys:cartodb250user:4321 \
|
||||||
|
user "localhost" \
|
||||||
|
type "master" \
|
||||||
|
grants_sql "true" \
|
||||||
|
grants_maps "true" \
|
||||||
|
database_role "${TESTUSER}" \
|
||||||
|
database_password "${TESTPASS}"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# API Key Default
|
||||||
|
cat <<EOF | redis-cli -p ${REDIS_PORT} -n 5
|
||||||
|
HMSET api_keys:cartodb250user:default_public \
|
||||||
|
user "localhost" \
|
||||||
|
type "default" \
|
||||||
|
grants_sql "true" \
|
||||||
|
grants_maps "true" \
|
||||||
|
database_role "test_windshaft_publicuser" \
|
||||||
|
database_password "public"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
|
||||||
echo "Finished preparing data. Ready to run tests"
|
echo "Finished preparing data. Ready to run tests"
|
||||||
|
@ -23,6 +23,12 @@ CREATE USER :PUBLICUSER WITH PASSWORD ':PUBLICPASS';
|
|||||||
DROP USER IF EXISTS :TESTUSER;
|
DROP USER IF EXISTS :TESTUSER;
|
||||||
CREATE USER :TESTUSER WITH PASSWORD ':TESTPASS';
|
CREATE USER :TESTUSER WITH PASSWORD ':TESTPASS';
|
||||||
|
|
||||||
|
-- regular user role 1
|
||||||
|
DROP USER IF EXISTS test_windshaft_regular1;
|
||||||
|
CREATE USER test_windshaft_regular1 WITH PASSWORD 'regular1';
|
||||||
|
|
||||||
|
GRANT test_windshaft_regular1 to :TESTUSER;
|
||||||
|
|
||||||
-- first table
|
-- first table
|
||||||
CREATE TABLE test_table (
|
CREATE TABLE test_table (
|
||||||
updated_at timestamp without time zone DEFAULT now(),
|
updated_at timestamp without time zone DEFAULT now(),
|
||||||
@ -189,6 +195,7 @@ INSERT INTO CDB_TableMetadata (tabname, updated_at) VALUES ('test_table_private_
|
|||||||
|
|
||||||
-- GRANT SELECT ON CDB_TableMetadata TO :PUBLICUSER;
|
-- GRANT SELECT ON CDB_TableMetadata TO :PUBLICUSER;
|
||||||
GRANT SELECT ON CDB_TableMetadata TO :TESTUSER;
|
GRANT SELECT ON CDB_TableMetadata TO :TESTUSER;
|
||||||
|
GRANT SELECT ON CDB_TableMetadata TO test_windshaft_regular1; -- for analysis. Warning: TBA
|
||||||
|
|
||||||
-- long name table
|
-- long name table
|
||||||
CREATE TABLE
|
CREATE TABLE
|
||||||
@ -412,6 +419,52 @@ INSERT INTO _vovw_1_test_special_float_values_table_overviews VALUES
|
|||||||
(3, 'El Rey del Tallarín', 'Plaza Conde de Toreno 2, Madrid, Spain', 'NaN'::float, '0101000020E610000021C8410933AD0DC0CB0EF10F5B364440', '0101000020110F000053E71AC64D3419C10F664E4659CC5241', 1),
|
(3, 'El Rey del Tallarín', 'Plaza Conde de Toreno 2, Madrid, Spain', 'NaN'::float, '0101000020E610000021C8410933AD0DC0CB0EF10F5B364440', '0101000020110F000053E71AC64D3419C10F664E4659CC5241', 1),
|
||||||
(4, 'El Lacón', 'Manuel Fernández y González 8, Madrid, Spain', 'infinity'::float, '0101000020E6100000BC5983F755990DC07D923B6C22354440', '0101000020110F00005DACDB056F2319C1EC41A980FCCA5241', 2);
|
(4, 'El Lacón', 'Manuel Fernández y González 8, Madrid, Spain', 'infinity'::float, '0101000020E6100000BC5983F755990DC07D923B6C22354440', '0101000020110F00005DACDB056F2319C1EC41A980FCCA5241', 2);
|
||||||
|
|
||||||
|
-- auth tables --------------------------------------------
|
||||||
|
|
||||||
|
CREATE TABLE test_table_localhost_regular1 (
|
||||||
|
updated_at timestamp without time zone DEFAULT now(),
|
||||||
|
created_at timestamp without time zone DEFAULT now(),
|
||||||
|
cartodb_id integer NOT NULL,
|
||||||
|
name character varying,
|
||||||
|
address character varying,
|
||||||
|
the_geom geometry,
|
||||||
|
the_geom_webmercator geometry,
|
||||||
|
CONSTRAINT enforce_dims_the_geom CHECK ((st_ndims(the_geom) = 2)),
|
||||||
|
CONSTRAINT enforce_dims_the_geom_webmercator CHECK ((st_ndims(the_geom_webmercator) = 2)),
|
||||||
|
CONSTRAINT enforce_geotype_the_geom CHECK (((geometrytype(the_geom) = 'POINT'::text) OR (the_geom IS NULL))),
|
||||||
|
CONSTRAINT enforce_geotype_the_geom_webmercator CHECK (((geometrytype(the_geom_webmercator) = 'POINT'::text) OR (the_geom_webmercator IS NULL))),
|
||||||
|
CONSTRAINT enforce_srid_the_geom CHECK ((st_srid(the_geom) = 4326)),
|
||||||
|
CONSTRAINT enforce_srid_the_geom_webmercator CHECK ((st_srid(the_geom_webmercator) = 3857))
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE SEQUENCE test_table_localhost_regular1_cartodb_id_seq
|
||||||
|
START WITH 1
|
||||||
|
INCREMENT BY 1
|
||||||
|
NO MINVALUE
|
||||||
|
NO MAXVALUE
|
||||||
|
CACHE 1;
|
||||||
|
|
||||||
|
ALTER SEQUENCE test_table_localhost_regular1_cartodb_id_seq OWNED BY test_table_localhost_regular1.cartodb_id;
|
||||||
|
|
||||||
|
SELECT pg_catalog.setval('test_table_localhost_regular1_cartodb_id_seq', 60, true);
|
||||||
|
|
||||||
|
ALTER TABLE test_table_localhost_regular1 ALTER COLUMN cartodb_id SET DEFAULT nextval('test_table_localhost_regular1_cartodb_id_seq'::regclass);
|
||||||
|
|
||||||
|
INSERT INTO test_table_localhost_regular1 VALUES
|
||||||
|
('2011-09-21 14:02:21.358706', '2011-09-21 14:02:21.314252', 1, 'Hawai', 'Calle de Pérez Galdós 9, Madrid, Spain', '0101000020E6100000A6B73F170D990DC064E8D84125364440', '0101000020110F000076491621312319C122D4663F1DCC5241'),
|
||||||
|
('2011-09-21 14:02:21.358706', '2011-09-21 14:02:21.319101', 2, 'El Estocolmo', 'Calle de la Palma 72, Madrid, Spain', '0101000020E6100000C90567F0F7AB0DC0AB07CC43A6364440', '0101000020110F0000C4356B29423319C15DD1092DADCC5241'),
|
||||||
|
('2011-09-21 14:02:21.358706', '2011-09-21 14:02:21.324', 3, 'El Rey del Tallarín', 'Plaza Conde de Toreno 2, Madrid, Spain', '0101000020E610000021C8410933AD0DC0CB0EF10F5B364440', '0101000020110F000053E71AC64D3419C10F664E4659CC5241'),
|
||||||
|
('2011-09-21 14:02:21.358706', '2011-09-21 14:02:21.329509', 4, 'El Lacón', 'Manuel Fernández y González 8, Madrid, Spain', '0101000020E6100000BC5983F755990DC07D923B6C22354440', '0101000020110F00005DACDB056F2319C1EC41A980FCCA5241'),
|
||||||
|
('2011-09-21 14:02:21.358706', '2011-09-21 14:02:21.334931', 5, 'El Pico', 'Calle Divino Pastor 12, Madrid, Spain', '0101000020E61000003B6D8D08C6A10DC0371B2B31CF364440', '0101000020110F00005F716E91992A19C17DAAA4D6DACC5241');
|
||||||
|
|
||||||
|
ALTER TABLE ONLY test_table_localhost_regular1 ADD CONSTRAINT test_table_localhost_regular1_pkey PRIMARY KEY (cartodb_id);
|
||||||
|
|
||||||
|
CREATE INDEX test_table_localhost_regular1_the_geom_idx ON test_table_localhost_regular1 USING gist (the_geom);
|
||||||
|
CREATE INDEX test_table_localhost_regular1_the_geom_webmercator_idx ON test_table_localhost_regular1 USING gist (the_geom_webmercator);
|
||||||
|
|
||||||
|
GRANT ALL ON TABLE test_table_localhost_regular1 TO :TESTUSER;
|
||||||
|
GRANT ALL ON TABLE test_table_localhost_regular1 TO test_windshaft_regular1;
|
||||||
|
|
||||||
-- analysis tables -----------------------------------------------
|
-- analysis tables -----------------------------------------------
|
||||||
|
|
||||||
ALTER TABLE cdb_analysis_catalog OWNER TO :TESTUSER;
|
ALTER TABLE cdb_analysis_catalog OWNER TO :TESTUSER;
|
||||||
@ -705,6 +758,7 @@ GRANT SELECT ON TABLE analysis_rent_listings TO :PUBLICUSER;
|
|||||||
|
|
||||||
--
|
--
|
||||||
GRANT SELECT, UPDATE, INSERT, DELETE ON cdb_analysis_catalog TO :TESTUSER;
|
GRANT SELECT, UPDATE, INSERT, DELETE ON cdb_analysis_catalog TO :TESTUSER;
|
||||||
|
GRANT SELECT, UPDATE, INSERT, DELETE ON cdb_analysis_catalog TO test_windshaft_regular1; -- for analysis. Warning: TBA
|
||||||
|
|
||||||
DROP EXTENSION IF EXISTS crankshaft;
|
DROP EXTENSION IF EXISTS crankshaft;
|
||||||
CREATE SCHEMA IF NOT EXISTS cdb_crankshaft;
|
CREATE SCHEMA IF NOT EXISTS cdb_crankshaft;
|
||||||
|
@ -113,7 +113,14 @@ afterEach(function(done) {
|
|||||||
'rails:test_windshaft_cartodb_user_1_db:my_table': true,
|
'rails:test_windshaft_cartodb_user_1_db:my_table': true,
|
||||||
'rails:users:localhost:map_key': true,
|
'rails:users:localhost:map_key': true,
|
||||||
'rails:users:cartodb250user': true,
|
'rails:users:cartodb250user': true,
|
||||||
'rails:users:localhost': true
|
'rails:users:localhost': true,
|
||||||
|
'rails:users:user_previous_to_project_auth': true, // AUTH_FALLBACK
|
||||||
|
'api_keys:localhost:1234': true,
|
||||||
|
'api_keys:localhost:default_public': true,
|
||||||
|
'api_keys:cartodb250user:4321': true,
|
||||||
|
'api_keys:cartodb250user:default_public': true,
|
||||||
|
'api_keys:localhost:regular1': true,
|
||||||
|
'api_keys:localhost:regular2': true,
|
||||||
};
|
};
|
||||||
var databasesTasks = { 0: 'users', 5: 'meta'};
|
var databasesTasks = { 0: 'users', 5: 'meta'};
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ var TemplateMaps = require('../../../lib/cartodb/backends/template_maps');
|
|||||||
const cleanUpQueryParamsMiddleware = require('../../../lib/cartodb/middleware/context/clean-up-query-params');
|
const cleanUpQueryParamsMiddleware = require('../../../lib/cartodb/middleware/context/clean-up-query-params');
|
||||||
const authorizeMiddleware = require('../../../lib/cartodb/middleware/context/authorize');
|
const authorizeMiddleware = require('../../../lib/cartodb/middleware/context/authorize');
|
||||||
const dbConnSetupMiddleware = require('../../../lib/cartodb/middleware/context/db-conn-setup');
|
const dbConnSetupMiddleware = require('../../../lib/cartodb/middleware/context/db-conn-setup');
|
||||||
|
const apikeyCredentialsMiddleware = require('../../../lib/cartodb/middleware/context/apikey-credentials');
|
||||||
const localsMiddleware = require('../../../lib/cartodb/middleware/context/locals');
|
const localsMiddleware = require('../../../lib/cartodb/middleware/context/locals');
|
||||||
|
|
||||||
var windshaft = require('windshaft');
|
var windshaft = require('windshaft');
|
||||||
@ -23,6 +24,7 @@ describe('prepare-context', function() {
|
|||||||
let cleanUpQueryParams;
|
let cleanUpQueryParams;
|
||||||
let dbConnSetup;
|
let dbConnSetup;
|
||||||
let authorize;
|
let authorize;
|
||||||
|
let setApikeyCredentials;
|
||||||
|
|
||||||
before(function() {
|
before(function() {
|
||||||
var redisPool = new RedisPool(global.environment.redis);
|
var redisPool = new RedisPool(global.environment.redis);
|
||||||
@ -35,6 +37,7 @@ describe('prepare-context', function() {
|
|||||||
cleanUpQueryParams = cleanUpQueryParamsMiddleware();
|
cleanUpQueryParams = cleanUpQueryParamsMiddleware();
|
||||||
authorize = authorizeMiddleware(authApi);
|
authorize = authorizeMiddleware(authApi);
|
||||||
dbConnSetup = dbConnSetupMiddleware(pgConnection);
|
dbConnSetup = dbConnSetupMiddleware(pgConnection);
|
||||||
|
setApikeyCredentials = apikeyCredentialsMiddleware();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -103,8 +106,20 @@ describe('prepare-context', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('sets also dbuser for authenticated requests', function(done){
|
it('sets also dbuser for authenticated requests', function(done){
|
||||||
var req = { headers: { host: 'localhost' }, query: { map_key: '1234' }};
|
var req = {
|
||||||
var res = { set: function () {} };
|
headers: {
|
||||||
|
host: 'localhost'
|
||||||
|
},
|
||||||
|
query: {
|
||||||
|
api_key: '1234'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var res = {
|
||||||
|
set: function () {},
|
||||||
|
locals: {
|
||||||
|
api_key: '1234'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// FIXME: review authorize-pgconnsetup workflow, It might we are doing authorization twice.
|
// FIXME: review authorize-pgconnsetup workflow, It might we are doing authorization twice.
|
||||||
authorize(prepareRequest(req), prepareResponse(res), function (err) {
|
authorize(prepareRequest(req), prepareResponse(res), function (err) {
|
||||||
@ -168,4 +183,66 @@ describe('prepare-context', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Set apikey token', function(){
|
||||||
|
it('from query param', function (done) {
|
||||||
|
var req = {
|
||||||
|
headers: {
|
||||||
|
host: 'localhost'
|
||||||
|
},
|
||||||
|
query: {
|
||||||
|
api_key: '1234',
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var res = {};
|
||||||
|
setApikeyCredentials(prepareRequest(req), prepareResponse(res), function (err) {
|
||||||
|
if (err) {
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
var query = res.locals;
|
||||||
|
|
||||||
|
assert.equal('1234', query.api_key);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('from body param', function (done) {
|
||||||
|
var req = {
|
||||||
|
headers: {
|
||||||
|
host: 'localhost'
|
||||||
|
},
|
||||||
|
body: {
|
||||||
|
api_key: '1234',
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var res = {};
|
||||||
|
setApikeyCredentials(prepareRequest(req), prepareResponse(res), function (err) {
|
||||||
|
if (err) {
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
var query = res.locals;
|
||||||
|
|
||||||
|
assert.equal('1234', query.api_key);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('from http header', function (done) {
|
||||||
|
var req = {
|
||||||
|
headers: {
|
||||||
|
host: 'localhost',
|
||||||
|
authorization: 'Basic bG9jYWxob3N0OjEyMzQ=', // user: localhost, password: 1234
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var res = {};
|
||||||
|
setApikeyCredentials(prepareRequest(req), prepareResponse(res), function (err) {
|
||||||
|
if (err) {
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
var query = res.locals;
|
||||||
|
|
||||||
|
assert.equal('1234', query.api_key);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
12
yarn.lock
12
yarn.lock
@ -143,6 +143,12 @@ balanced-match@^1.0.0:
|
|||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||||
|
|
||||||
|
basic-auth@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.0.tgz#015db3f353e02e56377755f962742e8981e7bbba"
|
||||||
|
dependencies:
|
||||||
|
safe-buffer "5.1.1"
|
||||||
|
|
||||||
bcrypt-pbkdf@^1.0.0:
|
bcrypt-pbkdf@^1.0.0:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d"
|
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d"
|
||||||
@ -295,9 +301,9 @@ cartodb-query-tables@0.3.0:
|
|||||||
version "0.3.0"
|
version "0.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/cartodb-query-tables/-/cartodb-query-tables-0.3.0.tgz#56e18d869666eb2e8e2cb57d0baf3acc923f8756"
|
resolved "https://registry.yarnpkg.com/cartodb-query-tables/-/cartodb-query-tables-0.3.0.tgz#56e18d869666eb2e8e2cb57d0baf3acc923f8756"
|
||||||
|
|
||||||
cartodb-redis@0.15.0:
|
cartodb-redis@0.16.0:
|
||||||
version "0.15.0"
|
version "0.16.0"
|
||||||
resolved "https://registry.yarnpkg.com/cartodb-redis/-/cartodb-redis-0.15.0.tgz#509ab9f62b8cae0838bcb8db1cb9d6355704ace3"
|
resolved "https://registry.yarnpkg.com/cartodb-redis/-/cartodb-redis-0.16.0.tgz#969312fd329b24a76bf6e5a4dd961445f2eda734"
|
||||||
dependencies:
|
dependencies:
|
||||||
dot "~1.0.2"
|
dot "~1.0.2"
|
||||||
redis-mpool "^0.5.0"
|
redis-mpool "^0.5.0"
|
||||||
|
Loading…
Reference in New Issue
Block a user