Compare commits
48 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6db25c3b6a | ||
|
|
88deded0fe | ||
|
|
fc0f2b5952 | ||
|
|
e211e944e5 | ||
|
|
a948038ff4 | ||
|
|
c70d192987 | ||
|
|
3fc8630634 | ||
|
|
8c013ed2d1 | ||
|
|
7a749631e8 | ||
|
|
e3a5f398e4 | ||
|
|
747f4803ba | ||
|
|
24709e8341 | ||
|
|
53861ad327 | ||
|
|
399bed34ad | ||
|
|
6b41fef96c | ||
|
|
031e2a2e0c | ||
|
|
9b4787c4b7 | ||
|
|
fe6e915c0d | ||
|
|
b5d67ec6c0 | ||
|
|
f5e0d06e2f | ||
|
|
78f69d5236 | ||
|
|
ab7d603171 | ||
|
|
b4936ffafa | ||
|
|
752e9ec655 | ||
|
|
9018e39762 | ||
|
|
a964ed5fe6 | ||
|
|
b862904506 | ||
|
|
7197cc2d62 | ||
|
|
b01570924d | ||
|
|
db478579c5 | ||
|
|
978ea9cd04 | ||
|
|
ca4f3d2025 | ||
|
|
c0020fd75a | ||
|
|
add4255bdc | ||
|
|
1f0faba71c | ||
|
|
e3f2658d53 | ||
|
|
f7cdb5f0b7 | ||
|
|
d32278b227 | ||
|
|
76acc5af99 | ||
|
|
5755e382fb | ||
|
|
95c450fe99 | ||
|
|
ad0b2ffc8e | ||
|
|
1b1b6b975e | ||
|
|
67e4e7e99b | ||
|
|
ac31c69c80 | ||
|
|
92ca447c06 | ||
|
|
bdea9f10fc | ||
|
|
dc3d36e0a5 |
46
NEWS.md
46
NEWS.md
@@ -1,3 +1,49 @@
|
||||
1.7.1 -- 2014-02-11
|
||||
-------------------
|
||||
|
||||
Enhancements:
|
||||
|
||||
* Disable debug logging unless "debug" config param evaluates to true (#137)
|
||||
* Require windshaft 0.17.2 for further reducing log noise (#137)
|
||||
|
||||
1.7.0 -- 2014-02-11
|
||||
-------------------
|
||||
|
||||
New features:
|
||||
|
||||
* Add support for torque tiles (#112)
|
||||
* Add attributes service (#118)
|
||||
* Implement Unified Map API (#126)
|
||||
* Make endpoints configurable (#127)
|
||||
|
||||
Enhancements:
|
||||
|
||||
* Allow specifying fixed sqlapi host address (#117)
|
||||
* Include template hash in template instance response, to keep caches
|
||||
of different instances separated (#105)
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* Allow space padding in template variables usage (#129)
|
||||
* Allow passing numbers as values for numeric template variables (#130)
|
||||
|
||||
|
||||
1.6.3 -- 2014-01-30
|
||||
-------------------
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* layergroup accept both map_key and api_key (#91)
|
||||
* Fix public instanciation of signed template accessing private data (#114)
|
||||
* Fix show_style in presence of complex styles
|
||||
* Fix use of maxzoom in layergroup config (via windshaft-0.15.1)
|
||||
|
||||
Enhancements:
|
||||
|
||||
* Add support for instanciating a template map with JSONP (#116)
|
||||
* Stop processing XML on renderer creation, not needed anymore since 1.6.1
|
||||
introduced on-demand XML generation.
|
||||
|
||||
1.6.2 -- 2014-01-23
|
||||
-------------------
|
||||
|
||||
|
||||
@@ -5,6 +5,22 @@ var config = {
|
||||
// Regular expression pattern to extract username
|
||||
// from hostname. Must have a single grabbing block.
|
||||
,user_from_host: '^(.*)\\.localhost'
|
||||
|
||||
// Base URLs for the APIs
|
||||
//
|
||||
// See http://github.com/CartoDB/Windshaft-cartodb/wiki/Unified-Map-API
|
||||
//
|
||||
// Base url for the Templated Maps API
|
||||
// "/maps/named" is the new API,
|
||||
// "/tiles/template" is for compatibility with versions up to 1.6.x
|
||||
,base_url_templated: '(?:/maps/named|/tiles/template)'
|
||||
// Base url for the Detached Maps API
|
||||
// "maps" is the the new API,
|
||||
// "tiles/layergroup" is for compatibility with versions up to 1.6.x
|
||||
,base_url_detached: '(?:/maps|/tiles/layergroup)'
|
||||
// Base url for the Inline Maps and Table Maps API
|
||||
,base_url_legacy: '/tiles/:table'
|
||||
|
||||
// Maximum number of connections for one process
|
||||
// 128 is a good value with a limit of 1024 open file descriptors
|
||||
,maxConnections:128
|
||||
@@ -64,8 +80,15 @@ var config = {
|
||||
}
|
||||
,sqlapi: {
|
||||
protocol: 'http',
|
||||
domain: 'localhost.lan',
|
||||
// If "host" is given, it will be used
|
||||
// to connect to the SQL-API without a
|
||||
// DNS lookup
|
||||
host: '127.0.0.1',
|
||||
port: 8080,
|
||||
// The "domain" part will be appended to
|
||||
// the cartodb username and passed to
|
||||
// SQL-API requests in the Host HTTP header
|
||||
domain: 'localhost.lan',
|
||||
version: 'v1'
|
||||
}
|
||||
,varnish: {
|
||||
|
||||
@@ -5,6 +5,22 @@ var config = {
|
||||
// Regular expression pattern to extract username
|
||||
// from hostname. Must have a single grabbing block.
|
||||
,user_from_host: '^(.*)\\.cartodb\\.com$'
|
||||
|
||||
// Base URLs for the APIs
|
||||
//
|
||||
// See http://github.com/CartoDB/Windshaft-cartodb/wiki/Unified-Map-API
|
||||
//
|
||||
// Base url for the Templated Maps API
|
||||
// "/maps/named" is the new API,
|
||||
// "/tiles/template" is for compatibility with versions up to 1.6.x
|
||||
,base_url_templated: '(?:/maps/named|/tiles/template)'
|
||||
// Base url for the Detached Maps API
|
||||
// "maps" is the the new API,
|
||||
// "tiles/layergroup" is for compatibility with versions up to 1.6.x
|
||||
,base_url_detached: '(?:/maps|/tiles/layergroup)'
|
||||
// Base url for the Inline Maps and Table Maps API
|
||||
,base_url_legacy: '/tiles/:table'
|
||||
|
||||
// Maximum number of connections for one process
|
||||
// 128 is a good value with a limit of 1024 open file descriptors
|
||||
,maxConnections:128
|
||||
@@ -58,8 +74,15 @@ var config = {
|
||||
}
|
||||
,sqlapi: {
|
||||
protocol: 'https',
|
||||
domain: 'cartodb.com',
|
||||
// If "host" is given, it will be used
|
||||
// to connect to the SQL-API without a
|
||||
// DNS lookup
|
||||
//host: '127.0.0.1',
|
||||
port: 8080,
|
||||
// The "domain" part will be appended to
|
||||
// the cartodb username and passed to
|
||||
// SQL-API requests in the Host HTTP header
|
||||
domain: 'cartodb.com',
|
||||
version: 'v2'
|
||||
}
|
||||
,varnish: {
|
||||
|
||||
@@ -5,6 +5,22 @@ var config = {
|
||||
// Regular expression pattern to extract username
|
||||
// from hostname. Must have a single grabbing block.
|
||||
,user_from_host: '^(.*)\\.cartodb\\.com$'
|
||||
|
||||
// Base URLs for the APIs
|
||||
//
|
||||
// See http://github.com/CartoDB/Windshaft-cartodb/wiki/Unified-Map-API
|
||||
//
|
||||
// Base url for the Templated Maps API
|
||||
// "/api/v1/maps/named" is the new API,
|
||||
// "/tiles/template" is for compatibility with versions up to 1.6.x
|
||||
,base_url_templated: '(?:/api/v1/maps/named|/tiles/template)'
|
||||
// Base url for the Detached Maps API
|
||||
// "/api/v1/maps" is the the new API,
|
||||
// "/tiles/layergroup" is for compatibility with versions up to 1.6.x
|
||||
,base_url_detached: '(?:/api/v1/maps|/tiles/layergroup)'
|
||||
// Base url for the Inline Maps and Table Maps API
|
||||
,base_url_legacy: '/tiles/:table'
|
||||
|
||||
// Maximum number of connections for one process
|
||||
// 128 is a good value with a limit of 1024 open file descriptors
|
||||
,maxConnections:128
|
||||
@@ -58,8 +74,15 @@ var config = {
|
||||
}
|
||||
,sqlapi: {
|
||||
protocol: 'https',
|
||||
domain: 'cartodb.com',
|
||||
// If "host" is given, it will be used
|
||||
// to connect to the SQL-API without a
|
||||
// DNS lookup
|
||||
//host: '127.0.0.1',
|
||||
port: 8080,
|
||||
// The "domain" part will be appended to
|
||||
// the cartodb username and passed to
|
||||
// SQL-API requests in the Host HTTP header
|
||||
domain: 'cartodb.com',
|
||||
version: 'v2'
|
||||
}
|
||||
,varnish: {
|
||||
|
||||
@@ -5,6 +5,22 @@ var config = {
|
||||
// Regular expression pattern to extract username
|
||||
// from hostname. Must have a single grabbing block.
|
||||
,user_from_host: '(.*)'
|
||||
|
||||
// Base URLs for the APIs
|
||||
//
|
||||
// See https://github.com/CartoDB/Windshaft-cartodb/wiki/Unified-Map-API
|
||||
//
|
||||
// Base url for the Templated Maps API
|
||||
// "/maps/named" is the new API,
|
||||
// "/tiles/template" is for compatibility with versions up to 1.6.x
|
||||
,base_url_templated: '(?:/maps/named|/tiles/template)'
|
||||
// Base url for the Detached Maps API
|
||||
// "maps" is the the new API,
|
||||
// "tiles/layergroup" is for compatibility with versions up to 1.6.x
|
||||
,base_url_detached: '(?:/maps|/tiles/layergroup)'
|
||||
// Base url for the Inline Maps and Table Maps API
|
||||
,base_url_legacy: '/tiles/:table'
|
||||
|
||||
// Maximum number of connections for one process
|
||||
// 128 is a good value with a limit of 1024 open file descriptors
|
||||
,maxConnections:128
|
||||
@@ -58,10 +74,17 @@ var config = {
|
||||
}
|
||||
,sqlapi: {
|
||||
protocol: 'http',
|
||||
domain: '',
|
||||
// If "host" is given, it will be used
|
||||
// to connect to the SQL-API without a
|
||||
// DNS lookup
|
||||
host: '127.0.0.1',
|
||||
port: 1080,
|
||||
// The "domain" part will be appended to
|
||||
// the cartodb username and passed to
|
||||
// SQL-API requests in the Host HTTP header
|
||||
domain: 'donot_look_this_up',
|
||||
// This port will be used by "make check" for testing purposes
|
||||
// It must be available
|
||||
port: 1080,
|
||||
version: 'v1'
|
||||
}
|
||||
,varnish: {
|
||||
|
||||
@@ -10,6 +10,7 @@ var _ = require('underscore')
|
||||
, Cache = require('./cache_validator');
|
||||
|
||||
var CartodbWindshaft = function(serverOptions) {
|
||||
var debug = global.environment.debug;
|
||||
|
||||
if(serverOptions.cache_enabled) {
|
||||
console.log("cache invalidation enabled, varnish on ", serverOptions.varnish_host, ' ', serverOptions.varnish_port);
|
||||
@@ -22,7 +23,7 @@ var CartodbWindshaft = function(serverOptions) {
|
||||
|
||||
serverOptions.beforeStateChange = function(req, callback) {
|
||||
var err = null;
|
||||
if ( ! req.params.hasOwnProperty('dbuser') ) {
|
||||
if ( ! req.params.hasOwnProperty('_authorizedByApiKey') ) {
|
||||
err = new Error("map state cannot be changed by unauthenticated request!");
|
||||
}
|
||||
callback(err, req);
|
||||
@@ -69,7 +70,7 @@ var CartodbWindshaft = function(serverOptions) {
|
||||
},
|
||||
function(err, data){
|
||||
if (err){
|
||||
ws.sendError(res, {error: err.message}, 500, 'GET INFOWINDOW');
|
||||
ws.sendError(res, {error: err.message}, 500, 'GET INFOWINDOW', err);
|
||||
//res.send({error: err.message}, 500);
|
||||
} else {
|
||||
res.send({infowindow: data}, 200);
|
||||
@@ -90,7 +91,7 @@ var CartodbWindshaft = function(serverOptions) {
|
||||
},
|
||||
function(err, data){
|
||||
if (err){
|
||||
ws.sendError(res, {error: err.message}, 500, 'GET MAP_METADATA');
|
||||
ws.sendError(res, {error: err.message}, 500, 'GET MAP_METADATA', err);
|
||||
//res.send(err.message, 500);
|
||||
} else {
|
||||
res.send({map_metadata: data}, 200);
|
||||
@@ -111,7 +112,7 @@ var CartodbWindshaft = function(serverOptions) {
|
||||
},
|
||||
function sendResponse(err, data){
|
||||
if (err){
|
||||
ws.sendError(res, {error: err.message}, 500, 'DELETE CACHE');
|
||||
ws.sendError(res, {error: err.message}, 500, 'DELETE CACHE', err);
|
||||
//res.send(500);
|
||||
} else {
|
||||
res.send({status: 'ok'}, 200);
|
||||
@@ -126,7 +127,11 @@ var CartodbWindshaft = function(serverOptions) {
|
||||
return serverOptions.userByReq(req);
|
||||
}
|
||||
|
||||
var template_baseurl = serverOptions.base_url_notable + '/template';
|
||||
// This is for Templated maps
|
||||
//
|
||||
// "named" is the official, "template" is for backward compatibility up to 1.6.x
|
||||
//
|
||||
var template_baseurl = global.environment.base_url_templated || '(?:/maps/named|/tiles/template)';
|
||||
|
||||
// Add a template
|
||||
ws.post(template_baseurl, function(req, res) {
|
||||
@@ -163,7 +168,7 @@ var CartodbWindshaft = function(serverOptions) {
|
||||
if ( ! _.isUndefined(err.http_status) ) {
|
||||
statusCode = err.http_status;
|
||||
}
|
||||
ws.sendError(res, response, statusCode, 'POST TEMPLATE', err.message);
|
||||
ws.sendError(res, response, statusCode, 'POST TEMPLATE', err);
|
||||
} else {
|
||||
res.send(response, 200);
|
||||
}
|
||||
@@ -216,7 +221,7 @@ var CartodbWindshaft = function(serverOptions) {
|
||||
if ( ! _.isUndefined(err.http_status) ) {
|
||||
statusCode = err.http_status;
|
||||
}
|
||||
ws.sendError(res, response, statusCode, 'PUT TEMPLATE', err.message);
|
||||
ws.sendError(res, response, statusCode, 'PUT TEMPLATE', err);
|
||||
} else {
|
||||
res.send(response, 200);
|
||||
}
|
||||
@@ -274,7 +279,7 @@ var CartodbWindshaft = function(serverOptions) {
|
||||
if ( ! _.isUndefined(err.http_status) ) {
|
||||
statusCode = err.http_status;
|
||||
}
|
||||
ws.sendError(res, response, statusCode, 'GET TEMPLATE', err.message);
|
||||
ws.sendError(res, response, statusCode, 'GET TEMPLATE', err);
|
||||
} else {
|
||||
res.send(response, 200);
|
||||
}
|
||||
@@ -324,7 +329,7 @@ var CartodbWindshaft = function(serverOptions) {
|
||||
if ( ! _.isUndefined(err.http_status) ) {
|
||||
statusCode = err.http_status;
|
||||
}
|
||||
ws.sendError(res, response, statusCode, 'DELETE TEMPLATE', err.message);
|
||||
ws.sendError(res, response, statusCode, 'DELETE TEMPLATE', err);
|
||||
} else {
|
||||
res.send('', 204);
|
||||
}
|
||||
@@ -364,7 +369,7 @@ var CartodbWindshaft = function(serverOptions) {
|
||||
if ( ! _.isUndefined(err.http_status) ) {
|
||||
statusCode = err.http_status;
|
||||
}
|
||||
ws.sendError(res, response, statusCode, 'GET TEMPLATE LIST', err.message);
|
||||
ws.sendError(res, response, statusCode, 'GET TEMPLATE LIST', err);
|
||||
} else {
|
||||
res.send(response, statusCode);
|
||||
}
|
||||
@@ -393,7 +398,7 @@ var CartodbWindshaft = function(serverOptions) {
|
||||
});
|
||||
|
||||
// Instantiate a template
|
||||
ws.post(template_baseurl + '/:template_id', function(req, res) {
|
||||
function instanciateTemplate(req, res, template_params, callback) {
|
||||
ws.doCORS(res);
|
||||
var that = this;
|
||||
var response = {};
|
||||
@@ -437,9 +442,10 @@ var CartodbWindshaft = function(serverOptions) {
|
||||
err.http_status = 401;
|
||||
throw err;
|
||||
}
|
||||
if ( ! req.headers['content-type'] || req.headers['content-type'].split(';')[0] != 'application/json' )
|
||||
/*if ( (! req.headers['content-type'] || req.headers['content-type'].split(';')[0] != 'application/json') && req.query.callback === undefined) {
|
||||
throw new Error('template POST data must be of type application/json, it is instead ');
|
||||
var template_params = req.body;
|
||||
}*/
|
||||
//var template_params = req.body;
|
||||
return templateMaps.instance(template, template_params);
|
||||
},
|
||||
function prepareParams(err, instance){
|
||||
@@ -448,8 +454,13 @@ var CartodbWindshaft = function(serverOptions) {
|
||||
fakereq = { query: {}, params: {}, headers: _.clone(req.headers) };
|
||||
ws.setDBParams(cdbuser, fakereq.params, this);
|
||||
},
|
||||
function createLayergroup(err) {
|
||||
function setApiKey(err){
|
||||
if ( err ) throw err;
|
||||
cartoData.getUserMapKey(cdbuser, this);
|
||||
},
|
||||
function createLayergroup(err, val) {
|
||||
if ( err ) throw err;
|
||||
fakereq.params.api_key = val;
|
||||
ws.createLayergroup(layergroup, fakereq, this);
|
||||
},
|
||||
function signLayergroup(err, resp) {
|
||||
@@ -473,20 +484,64 @@ var CartodbWindshaft = function(serverOptions) {
|
||||
if ( err ) throw err;
|
||||
//console.log("Response from createLayergroup: "); console.dir(response);
|
||||
// Add the signature part to the token!
|
||||
response.layergroupid = cdbuser + '@' + response.layergroupid;
|
||||
var tplhash = templateMaps.fingerPrint(template).substring(0,8);
|
||||
response.layergroupid = cdbuser + '@' + tplhash + '@' + response.layergroupid;
|
||||
return response;
|
||||
},
|
||||
function finish(err, response){
|
||||
if (err){
|
||||
var statusCode = 400;
|
||||
response = { error: ''+err };
|
||||
if ( ! _.isUndefined(err.http_status) ) {
|
||||
statusCode = err.http_status;
|
||||
}
|
||||
ws.sendError(res, response, statusCode, 'POST INSTANCE TEMPLATE', err.message);
|
||||
} else {
|
||||
res.send(response, 200);
|
||||
callback
|
||||
);
|
||||
}
|
||||
|
||||
function finish_instanciation(err, response, res) {
|
||||
if (err) {
|
||||
var statusCode = 400;
|
||||
response = { error: ''+err };
|
||||
if ( ! _.isUndefined(err.http_status) ) {
|
||||
statusCode = err.http_status;
|
||||
}
|
||||
if(debug) {
|
||||
response.stack = err.stack;
|
||||
}
|
||||
ws.sendError(res, response, statusCode, 'POST INSTANCE TEMPLATE', err);
|
||||
} else {
|
||||
res.send(response, 200);
|
||||
}
|
||||
}
|
||||
|
||||
ws.post(template_baseurl + '/:template_id', function(req, res) {
|
||||
Step(
|
||||
function() {
|
||||
if ( ! req.headers['content-type'] || req.headers['content-type'].split(';')[0] != 'application/json') {
|
||||
throw new Error('template POST data must be of type application/json, it is instead ');
|
||||
}
|
||||
instanciateTemplate(req, res, req.body, this);
|
||||
}, function(err, response) {
|
||||
finish_instanciation(err, response, res);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* jsonp endpoint, allows to instanciate a template with a json call.
|
||||
* callback query argument is mandartoy
|
||||
*/
|
||||
ws.get(template_baseurl + '/:template_id/jsonp', function(req, res) {
|
||||
Step(
|
||||
function() {
|
||||
if ( req.query.callback === undefined || req.query.callback.length === 0) {
|
||||
throw new Error('callback parameter should be present and be a function name');
|
||||
}
|
||||
var config = {};
|
||||
if(req.query.config) {
|
||||
try {
|
||||
config = JSON.parse(req.query.config);
|
||||
} catch(e) {
|
||||
throw new Error('badformed config parameter, should be a valid JSON');
|
||||
}
|
||||
}
|
||||
instanciateTemplate(req, res, config, this);
|
||||
}, function(err, response) {
|
||||
finish_instanciation(err, response, res);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
@@ -10,7 +10,11 @@ var _ = require('underscore')
|
||||
|
||||
// This is for backward compatibility with 1.3.3
|
||||
if ( _.isUndefined(global.environment.sqlapi.domain) ) {
|
||||
global.environment.sqlapi.domain = global.environment.sqlapi.host;
|
||||
// Only use "host" as "domain" if it contains alphanumeric characters
|
||||
var host = global.environment.sqlapi.host;
|
||||
if ( host && host.match(/[a-zA-Z]/) ) {
|
||||
global.environment.sqlapi.domain = host;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = function(){
|
||||
@@ -22,9 +26,24 @@ module.exports = function(){
|
||||
});
|
||||
|
||||
var me = {
|
||||
base_url: '/tiles/:table',
|
||||
base_url_notable: '/tiles',
|
||||
// This is for inline maps and table maps
|
||||
base_url: global.environment.base_url_legacy || '/tiles/:table',
|
||||
|
||||
/// @deprecated with Windshaft-0.17.0
|
||||
///base_url_notable: '/tiles',
|
||||
|
||||
// This is for Detached maps
|
||||
//
|
||||
// "maps" is the official, while
|
||||
// "tiles/layergroup" is for backward compatibility up to 1.6.x
|
||||
//
|
||||
base_url_mapconfig: global.environment.base_url_detached || '(?:/maps|/tiles/layergroup)',
|
||||
|
||||
grainstore: {
|
||||
map: {
|
||||
// TODO: allow to specify in configuration
|
||||
srid: 3857
|
||||
},
|
||||
datasource: global.environment.postgres,
|
||||
cachedir: global.environment.millstone.cache_basedir,
|
||||
mapnik_version: global.environment.mapnik_version || mapnik.versions.mapnik,
|
||||
@@ -68,9 +87,13 @@ module.exports = function(){
|
||||
var api = global.environment.sqlapi;
|
||||
|
||||
// build up api string
|
||||
var sqlapi = api.protocol + '://' + username;
|
||||
if ( api.domain ) sqlapi += '.' + api.domain;
|
||||
sqlapi += ':' + api.port + '/api/' + api.version + '/sql'
|
||||
var sqlapihostname = username;
|
||||
if ( api.domain ) sqlapihostname += '.' + api.domain;
|
||||
|
||||
var sqlapi = api.protocol + '://';
|
||||
if ( api.host && api.host != api.domain ) sqlapi += api.host;
|
||||
else sqlapi += sqlapihostname;
|
||||
sqlapi += ':' + api.port + '/api/' + api.version + '/sql';
|
||||
|
||||
var qs = { q: sql }
|
||||
|
||||
@@ -85,8 +108,11 @@ module.exports = function(){
|
||||
// TODO: use "host" header to allow IP based specification
|
||||
// of sqlapi address (and avoid a DNS lookup)
|
||||
//
|
||||
request.post({url:sqlapi, body:qs, json:true},
|
||||
function(err, res, body){
|
||||
request.post({
|
||||
url:sqlapi, body:qs, json:true,
|
||||
headers:{host: sqlapihostname}
|
||||
}, function(err, res, body)
|
||||
{
|
||||
if (err){
|
||||
console.log('ERROR connecting to SQL API on ' + sqlapi + ': ' + err);
|
||||
callback(err);
|
||||
@@ -290,7 +316,7 @@ module.exports = function(){
|
||||
|
||||
var dbName = req.params.dbname;
|
||||
var usr = this.userByReq(req);
|
||||
var key = req.params.map_key;
|
||||
var key = req.params.map_key || req.params.api_key;
|
||||
|
||||
var cacheKey = dbName + ':' + token;
|
||||
|
||||
@@ -402,7 +428,7 @@ module.exports = function(){
|
||||
if ( data ) _.extend(params, {dbhost:data});
|
||||
cartoData.getUserDBName(dbowner, this);
|
||||
},
|
||||
function getGeometryType(err, data){
|
||||
function extendParams(err, data){
|
||||
if (err) throw err;
|
||||
if ( data ) _.extend(params, {dbname:data});
|
||||
return null;
|
||||
@@ -437,7 +463,7 @@ module.exports = function(){
|
||||
var layergroup_id = req.params.token;
|
||||
var auth_token = req.params.auth_token;
|
||||
|
||||
console.log("Checking authorization from signer " + signer + " for resource " + layergroup_id + " with auth_token " + auth_token);
|
||||
//console.log("Checking authorization from signer " + signer + " for resource " + layergroup_id + " with auth_token " + auth_token);
|
||||
|
||||
me.signedMaps.isAuthorized(signer, layergroup_id, auth_token,
|
||||
function(err, authorized) {
|
||||
@@ -501,6 +527,8 @@ console.log("Checking authorization from signer " + signer + " for resource " +
|
||||
return;
|
||||
}
|
||||
|
||||
_.extend(req.params, { _authorizedByApiKey: true });
|
||||
|
||||
// authorized by api key, login as the given username and stop
|
||||
that.setDBAuth(user, req.params, function(err) {
|
||||
callback(err, true); // authorized (or error)
|
||||
@@ -585,9 +613,12 @@ console.log("Checking authorization from signer " + signer + " for resource " +
|
||||
if ( tksplit.length > 1 ) req.params.cache_buster= tksplit[1];
|
||||
tksplit = req.params.token.split('@');
|
||||
if ( tksplit.length > 1 ) {
|
||||
req.params.signer = this.userByReq(req);
|
||||
if ( tksplit[0] ) req.params.signer = tksplit[0];
|
||||
req.params.token = tksplit[1];
|
||||
req.params.signer = tksplit.shift();
|
||||
if ( ! req.params.signer ) req.params.signer = this.userByReq(req);
|
||||
if ( tksplit.length > 1 ) {
|
||||
var template_hash = tksplit.shift(); // unused
|
||||
}
|
||||
req.params.token = tksplit.shift();
|
||||
//console.log("Request for token " + req.params.token + " with signature from " + req.params.signer);
|
||||
}
|
||||
}
|
||||
@@ -598,26 +629,6 @@ console.log("Checking authorization from signer " + signer + " for resource " +
|
||||
// for cartodb, ensure interactivity is cartodb_id or user specified
|
||||
req.params.interactivity = req.params.interactivity || 'cartodb_id';
|
||||
|
||||
req.params.processXML = function(req, xml, callback) {
|
||||
|
||||
// Replace dbuser
|
||||
var dbuser = req.params.dbuser || global.environment.postgres.user;
|
||||
if ( ! me.rx_dbuser ) me.rx_dbuser = /(<Parameter name="user"><!\[CDATA\[)[^\]]*(]]><\/Parameter>)/g;
|
||||
xml = xml.replace(me.rx_dbuser, "$1" + dbuser + "$2");
|
||||
|
||||
// Replace dbpass
|
||||
var dbpass = req.params.dbpassword || global.environment.postgres.password;
|
||||
if ( ! me.rx_dbpass ) me.rx_dbpass = /(<Parameter name="password"><!\[CDATA\[)[^\]]*(]]><\/Parameter>)/g;
|
||||
xml = xml.replace(me.rx_dbpass, "$1" + dbpass + "$2");
|
||||
|
||||
// Replace or set dbhost
|
||||
var dbhost = req.params.dbhost || global.environment.postgres.host;
|
||||
if ( ! me.rx_dbhost ) me.rx_dbhost = /(<Parameter name="host"><!\[CDATA\[)[^\]]*(]]><\/Parameter>)/g;
|
||||
xml = xml.replace(me.rx_dbhost, "$1" + dbhost + "$2");
|
||||
|
||||
callback(null, xml);
|
||||
}
|
||||
|
||||
var that = this;
|
||||
|
||||
if (req.profiler) req.profiler.done('req2params.setup');
|
||||
@@ -639,17 +650,26 @@ console.log("Checking authorization from signer " + signer + " for resource " +
|
||||
that.setDBConn(user, req.params, this);
|
||||
},
|
||||
function getGeometryType(err){
|
||||
if (req.profiler) req.profiler.done('cartoData.getDatabase');
|
||||
if (req.profiler) req.profiler.done('setDBConn');
|
||||
if (err) throw err;
|
||||
cartoData.getTableGeometryType(req.params.dbname, req.params.table, this);
|
||||
},
|
||||
function finishSetup(err, data){
|
||||
if (req.profiler) req.profiler.done('cartoData.getGeometryType');
|
||||
if (req.profiler) req.profiler.done('cartoData.getTableGeometryType');
|
||||
if ( err ) { callback(err, req); return; }
|
||||
|
||||
if (!_.isNull(data))
|
||||
_.extend(req.params, {geom_type: data});
|
||||
|
||||
// Add default database connection parameters
|
||||
// if none given
|
||||
_.defaults(req.params, {
|
||||
dbuser: global.environment.postgres.user,
|
||||
dbpassword: global.environment.postgres.password,
|
||||
dbhost: global.environment.postgres.host,
|
||||
dbport: global.environment.postgres.port
|
||||
});
|
||||
|
||||
that.addCacheChannel(req, function(err) {
|
||||
if (req.profiler) req.profiler.done('addCacheChannel');
|
||||
callback(err, req);
|
||||
|
||||
@@ -2,6 +2,7 @@ var crypto = require('crypto');
|
||||
var Step = require('step');
|
||||
var _ = require('underscore');
|
||||
|
||||
var debug = global.environment.debug;
|
||||
|
||||
// Class handling map signatures and user certificates
|
||||
//
|
||||
@@ -85,28 +86,59 @@ o._redisCmd = function(redisFunc, redisArgs, callback) {
|
||||
);
|
||||
};
|
||||
|
||||
o._getAuthMethod = function(auth) {
|
||||
return auth.method || 'open';
|
||||
};
|
||||
|
||||
//--------------- PUBLIC API -------------------------------------
|
||||
|
||||
// Check if the given certificate authorizes waiver of "auth"
|
||||
o.authorizedByCert = function(cert, auth) {
|
||||
/// Check formal validity of a certificate
|
||||
//
|
||||
/// Return an Error instance if invalid, null otherwise
|
||||
///
|
||||
o.checkInvalidCertificate = function(cert) {
|
||||
//console.log("Checking cert: "); console.dir(cert);
|
||||
if ( cert.version !== "0.0.1" ) {
|
||||
throw new Error("Unsupported certificate version " + cert.version);
|
||||
return new Error("Unsupported certificate version " + cert.version);
|
||||
}
|
||||
|
||||
if ( ! cert.auth ) {
|
||||
throw new Error("No certificate authorization");
|
||||
console.log("Cert is : "); console.dir(cert);
|
||||
return new Error("No certificate authorization");
|
||||
}
|
||||
|
||||
if ( ! cert.auth.method ) {
|
||||
throw new Error("No certificate authorization method");
|
||||
var method = this._getAuthMethod(cert.auth);
|
||||
|
||||
switch ( method ) {
|
||||
case 'open':
|
||||
break;
|
||||
case 'token':
|
||||
if ( ! _.isArray(cert.auth.valid_tokens) )
|
||||
return new Error("Invalid 'token' authentication: missing valid_tokens");
|
||||
if ( ! cert.auth.valid_tokens.length )
|
||||
return new Error("Invalid 'token' authentication: no valid_tokens");
|
||||
break;
|
||||
default:
|
||||
return new Error("Unsupported authentication method: " + cert.auth.method);
|
||||
break;
|
||||
}
|
||||
|
||||
return null; // all valid
|
||||
}
|
||||
|
||||
// Check if the given certificate authorizes waiver of "auth"
|
||||
o.authorizedByCert = function(cert, auth) {
|
||||
|
||||
var err = this.checkInvalidCertificate(cert);
|
||||
if ( err ) throw err;
|
||||
|
||||
var method = this._getAuthMethod(cert.auth);
|
||||
|
||||
// Open authentication certificates are always authorized
|
||||
if ( cert.auth.method === 'open' ) return true;
|
||||
if ( method === 'open' ) return true;
|
||||
|
||||
// Token based authentication requires valid token
|
||||
if ( cert.auth.method === 'token' ) {
|
||||
if ( method === 'token' ) {
|
||||
var found = cert.auth.valid_tokens.indexOf(auth);
|
||||
//if ( found !== -1 ) {
|
||||
//console.log("Token " + auth + " is found at position " + found + " in valid tokens " + cert.auth.valid_tokens);
|
||||
@@ -133,7 +165,9 @@ o.isAuthorized = function(signer, map_id, auth, callback) {
|
||||
var authorized = false;
|
||||
var certificate_id_list;
|
||||
var missing_certificates = [];
|
||||
console.log("Check auth from signer '" + signer + "' on map '" + map_id + "' with auth '" + auth + "'");
|
||||
if ( debug ) {
|
||||
console.log("Check auth from signer '" + signer + "' on map '" + map_id + "' with auth '" + auth + "'");
|
||||
}
|
||||
Step(
|
||||
function getMapSignatures() {
|
||||
var map_sig_key = _.template(that.key_map_sig, {signer:signer, map_id:map_id});
|
||||
@@ -141,7 +175,9 @@ console.log("Check auth from signer '" + signer + "' on map '" + map_id + "' wit
|
||||
},
|
||||
function getCertificates(err, crt_lst) {
|
||||
if ( err ) throw err;
|
||||
console.log("Map '" + map_id + "' is signed by " + crt_lst.length + " certificates of user '" + signer + "': " + crt_lst);
|
||||
if ( debug ) {
|
||||
console.log("Map '" + map_id + "' is signed by " + crt_lst.length + " certificates of user '" + signer);
|
||||
}
|
||||
certificate_id_list = crt_lst;
|
||||
if ( ! crt_lst.length ) {
|
||||
// No certs, avoid calling redis with short args list.
|
||||
@@ -170,8 +206,10 @@ console.log("Check auth from signer '" + signer + "' on map '" + map_id + "' wit
|
||||
continue;
|
||||
}
|
||||
if ( authorized ) {
|
||||
console.log("Access to map '" + map_id + "' authorized by cert '"
|
||||
+ certificate_id_list[i] + "' of user '" + signer + "'");
|
||||
if ( debug ) {
|
||||
console.log("Access to map '" + map_id + "' authorized by cert '"
|
||||
+ certificate_id_list[i] + "' of user '" + signer + "'");
|
||||
}
|
||||
//console.dir(cert);
|
||||
break; // no need to further check certs
|
||||
}
|
||||
@@ -248,8 +286,10 @@ o.delCertificate = function(signer, crt_id, callback) {
|
||||
function delMapSignaturesReference(err, map_id_list) {
|
||||
if ( err ) throw err;
|
||||
signed_map_list = map_id_list;
|
||||
console.log("Certificate '" + crt_id + "' from user '" + signer
|
||||
+ "' was used to sign " + signed_map_list.length + " maps");
|
||||
if ( debug ) {
|
||||
console.log("Certificate '" + crt_id + "' from user '" + signer
|
||||
+ "' was used to sign " + signed_map_list.length + " maps");
|
||||
}
|
||||
redis_client.DEL(crt_sig_key, this);
|
||||
},
|
||||
function delMapSignatures(err) {
|
||||
@@ -266,16 +306,18 @@ o.delCertificate = function(signer, crt_id, callback) {
|
||||
},
|
||||
function reportTransaction(err, rets) {
|
||||
if ( err ) throw err;
|
||||
for (var i=0; i<signed_map_list.length; ++i) {
|
||||
var ret = rets[i];
|
||||
if ( ! ret ) {
|
||||
console.log("No signature with certificate '" + crt_id
|
||||
+ "' of user '" + signer + "' found in map '"
|
||||
+ signed_map_list[i] + "'");
|
||||
} else {
|
||||
console.log("Signature with certificate '" + crt_id
|
||||
+ "' of user '" + signer + "' removed from map '"
|
||||
+ signed_map_list[i] + "'");
|
||||
if ( debug ) {
|
||||
for (var i=0; i<signed_map_list.length; ++i) {
|
||||
var ret = rets[i];
|
||||
if ( ! ret ) {
|
||||
console.log("No signature with certificate '" + crt_id
|
||||
+ "' of user '" + signer + "' found in map '"
|
||||
+ signed_map_list[i] + "'");
|
||||
} else {
|
||||
console.log("Signature with certificate '" + crt_id
|
||||
+ "' of user '" + signer + "' removed from map '"
|
||||
+ signed_map_list[i] + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -302,7 +344,9 @@ o.signMap = function(signer, map_id, crt_id, callback) {
|
||||
Step(
|
||||
function addMapSignature() {
|
||||
var map_sig_key = _.template(that.key_map_sig, {signer:signer, map_id:map_id});
|
||||
console.log("Adding " + crt_id + " to " + map_sig_key);
|
||||
if ( debug ) {
|
||||
console.log("Adding " + crt_id + " to " + map_sig_key);
|
||||
}
|
||||
that._redisCmd('SADD', [ map_sig_key, crt_id ], this);
|
||||
},
|
||||
function addCertificateUsage(err) {
|
||||
|
||||
@@ -143,6 +143,11 @@ o._checkInvalidTemplate = function(template) {
|
||||
}
|
||||
};
|
||||
|
||||
// Check certificate validity
|
||||
var cert = this.getTemplateCertificate(template);
|
||||
var err = this.signed_maps.checkInvalidCertificate(cert);
|
||||
if ( err ) return err;
|
||||
|
||||
// TODO: run more checks over template format ?
|
||||
};
|
||||
|
||||
@@ -529,7 +534,7 @@ o._replaceVars = function(str, params) {
|
||||
if ( ! params._re ) {
|
||||
params._re = {};
|
||||
for (var k in params) {
|
||||
params._re[k] = RegExp("<%= " + k + " %>", "g");
|
||||
params._re[k] = RegExp("<%=\\s*" + k + "\\s*%>", "g");
|
||||
}
|
||||
}
|
||||
for (var k in params) str = str.replace(params._re[k], params[k]);
|
||||
@@ -552,7 +557,7 @@ o.instance = function(template, params) {
|
||||
}
|
||||
else if ( type === 'number' ) {
|
||||
// check it's a number
|
||||
if ( ! val.match(this._reNumber) ) {
|
||||
if ( typeof(val) !== 'number' && ! val.match(this._reNumber) ) {
|
||||
throw new Error("Invalid number value for template parameter '"
|
||||
+ k + "': " + val);
|
||||
}
|
||||
@@ -576,11 +581,19 @@ o.instance = function(template, params) {
|
||||
var layergroup = JSON.parse(JSON.stringify(template.layergroup));
|
||||
for (var i=0; i<layergroup.layers.length; ++i) {
|
||||
var lyropt = layergroup.layers[i].options;
|
||||
lyropt.cartocss = this._replaceVars(lyropt.cartocss, all_params);
|
||||
lyropt.sql = this._replaceVars(lyropt.sql, all_params);
|
||||
if ( lyropt.cartocss ) lyropt.cartocss = this._replaceVars(lyropt.cartocss, all_params);
|
||||
if ( lyropt.sql) lyropt.sql = this._replaceVars(lyropt.sql, all_params);
|
||||
// Anything else ?
|
||||
}
|
||||
return layergroup;
|
||||
};
|
||||
|
||||
// Return a fingerPrint of the object
|
||||
o.fingerPrint = function(template) {
|
||||
return crypto.createHash('md5')
|
||||
.update(JSON.stringify(template))
|
||||
.digest('hex')
|
||||
;
|
||||
};
|
||||
|
||||
module.exports = TemplateMaps;
|
||||
|
||||
127
npm-shrinkwrap.json
generated
127
npm-shrinkwrap.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "windshaft-cartodb",
|
||||
"version": "1.6.2",
|
||||
"version": "1.7.1",
|
||||
"dependencies": {
|
||||
"node-varnish": {
|
||||
"version": "0.1.1"
|
||||
@@ -9,44 +9,17 @@
|
||||
"version": "1.3.3"
|
||||
},
|
||||
"windshaft": {
|
||||
"version": "0.15.0",
|
||||
"version": "0.17.2",
|
||||
"from": "http://github.com/CartoDB/Windshaft/tarball/0.17.2",
|
||||
"dependencies": {
|
||||
"grainstore": {
|
||||
"version": "0.16.0",
|
||||
"version": "0.17.1",
|
||||
"dependencies": {
|
||||
"carto": {
|
||||
"version": "0.9.5-cdb2",
|
||||
"from": "http://github.com/CartoDB/carto/tarball/0.9.5-cdb2",
|
||||
"dependencies": {
|
||||
"underscore": {
|
||||
"version": "1.4.4"
|
||||
},
|
||||
"xml2js": {
|
||||
"version": "0.2.8",
|
||||
"dependencies": {
|
||||
"sax": {
|
||||
"version": "0.5.8"
|
||||
}
|
||||
}
|
||||
},
|
||||
"optimist": {
|
||||
"version": "0.6.0",
|
||||
"dependencies": {
|
||||
"wordwrap": {
|
||||
"version": "0.0.2"
|
||||
},
|
||||
"minimist": {
|
||||
"version": "0.0.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapnik-reference": {
|
||||
"version": "5.0.7"
|
||||
},
|
||||
"millstone": {
|
||||
"version": "0.6.8",
|
||||
"version": "0.6.11",
|
||||
"dependencies": {
|
||||
"underscore": {
|
||||
"version": "1.5.2"
|
||||
@@ -61,7 +34,7 @@
|
||||
"version": "5.0.0"
|
||||
},
|
||||
"forever-agent": {
|
||||
"version": "0.5.0"
|
||||
"version": "0.5.2"
|
||||
},
|
||||
"tunnel-agent": {
|
||||
"version": "0.3.0"
|
||||
@@ -121,14 +94,14 @@
|
||||
}
|
||||
},
|
||||
"async": {
|
||||
"version": "0.2.9"
|
||||
"version": "0.2.10"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"srs": {
|
||||
"version": "0.3.8"
|
||||
"version": "0.3.10"
|
||||
},
|
||||
"zipfile": {
|
||||
"version": "0.4.3"
|
||||
@@ -137,7 +110,7 @@
|
||||
"version": "2.2.0",
|
||||
"dependencies": {
|
||||
"node-pre-gyp": {
|
||||
"version": "0.2.5",
|
||||
"version": "0.2.6",
|
||||
"dependencies": {
|
||||
"nopt": {
|
||||
"version": "2.1.2",
|
||||
@@ -217,7 +190,12 @@
|
||||
}
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "1.0.24"
|
||||
"version": "1.0.25-1",
|
||||
"dependencies": {
|
||||
"string_decoder": {
|
||||
"version": "0.10.25"
|
||||
}
|
||||
}
|
||||
},
|
||||
"graceful-fs": {
|
||||
"version": "1.2.3"
|
||||
@@ -225,7 +203,7 @@
|
||||
}
|
||||
},
|
||||
"aws-sdk": {
|
||||
"version": "2.0.0-rc8",
|
||||
"version": "2.0.0-rc9",
|
||||
"dependencies": {
|
||||
"xml2js": {
|
||||
"version": "0.2.4",
|
||||
@@ -241,7 +219,7 @@
|
||||
}
|
||||
},
|
||||
"rc": {
|
||||
"version": "0.3.2",
|
||||
"version": "0.3.3",
|
||||
"dependencies": {
|
||||
"optimist": {
|
||||
"version": "0.3.7",
|
||||
@@ -252,7 +230,7 @@
|
||||
}
|
||||
},
|
||||
"deep-extend": {
|
||||
"version": "0.2.6"
|
||||
"version": "0.2.8"
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.1.0"
|
||||
@@ -260,7 +238,7 @@
|
||||
}
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "2.2.5"
|
||||
"version": "2.2.6"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -273,13 +251,13 @@
|
||||
"version": "0.3.5"
|
||||
},
|
||||
"optimist": {
|
||||
"version": "0.6.0",
|
||||
"version": "0.6.1",
|
||||
"dependencies": {
|
||||
"wordwrap": {
|
||||
"version": "0.0.2"
|
||||
},
|
||||
"minimist": {
|
||||
"version": "0.0.5"
|
||||
"version": "0.0.7"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -344,6 +322,59 @@
|
||||
},
|
||||
"lru-cache": {
|
||||
"version": "2.3.1"
|
||||
},
|
||||
"carto": {
|
||||
"version": "0.9.5-cdb2",
|
||||
"from": "http://github.com/CartoDB/carto/tarball/0.9.5-cdb2",
|
||||
"dependencies": {
|
||||
"underscore": {
|
||||
"version": "1.4.4"
|
||||
},
|
||||
"mapnik-reference": {
|
||||
"version": "5.0.7"
|
||||
},
|
||||
"xml2js": {
|
||||
"version": "0.2.8",
|
||||
"dependencies": {
|
||||
"sax": {
|
||||
"version": "0.5.8"
|
||||
}
|
||||
}
|
||||
},
|
||||
"optimist": {
|
||||
"version": "0.6.1",
|
||||
"dependencies": {
|
||||
"wordwrap": {
|
||||
"version": "0.0.2"
|
||||
},
|
||||
"minimist": {
|
||||
"version": "0.0.7"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"underscore.string": {
|
||||
"version": "1.1.6",
|
||||
"dependencies": {
|
||||
"underscore": {
|
||||
"version": "1.1.7"
|
||||
}
|
||||
}
|
||||
},
|
||||
"pg": {
|
||||
"version": "2.6.2",
|
||||
"dependencies": {
|
||||
"generic-pool": {
|
||||
"version": "2.0.3"
|
||||
},
|
||||
"buffer-writer": {
|
||||
"version": "1.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"torque.js": {
|
||||
"version": "2.2.00"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -373,19 +404,19 @@
|
||||
}
|
||||
},
|
||||
"mapnik": {
|
||||
"version": "0.7.25"
|
||||
"version": "0.7.26"
|
||||
},
|
||||
"lzma": {
|
||||
"version": "1.2.3"
|
||||
},
|
||||
"strftime": {
|
||||
"version": "0.6.2"
|
||||
"redis": {
|
||||
"version": "0.8.6"
|
||||
},
|
||||
"semver": {
|
||||
"version": "1.1.4"
|
||||
},
|
||||
"redis": {
|
||||
"version": "0.8.6"
|
||||
"strftime": {
|
||||
"version": "0.6.2"
|
||||
},
|
||||
"mocha": {
|
||||
"version": "1.14.0",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "windshaft-cartodb",
|
||||
"version": "1.6.2",
|
||||
"version": "1.7.1",
|
||||
"description": "A map tile server for CartoDB",
|
||||
"keywords": [
|
||||
"cartodb"
|
||||
@@ -24,7 +24,7 @@
|
||||
"dependencies": {
|
||||
"node-varnish": "0.1.1",
|
||||
"underscore" : "~1.3.3",
|
||||
"windshaft" : "~0.15.0",
|
||||
"windshaft" : "~0.17.2",
|
||||
"step": "0.0.x",
|
||||
"request": "2.9.202",
|
||||
"cartodb-redis": "~0.3.0",
|
||||
|
||||
@@ -58,7 +58,7 @@ suite('multilayer', function() {
|
||||
]
|
||||
};
|
||||
|
||||
var expected_token = "e34dd7e235138a062f8ba7ad051aa3a7";
|
||||
var expected_token; // = "e34dd7e235138a062f8ba7ad051aa3a7";
|
||||
Step(
|
||||
function do_post()
|
||||
{
|
||||
@@ -83,7 +83,7 @@ suite('multilayer', function() {
|
||||
if ( expected_token ) {
|
||||
assert.equal(parsedBody.layergroupid, expected_token + ':' + expected_last_updated_epoch);
|
||||
}
|
||||
else expected_token = parsedBody.layergroupid;
|
||||
else expected_token = parsedBody.layergroupid.split(':')[0];
|
||||
next(null, res);
|
||||
});
|
||||
},
|
||||
@@ -164,7 +164,7 @@ suite('multilayer', function() {
|
||||
errors.push(err.message);
|
||||
console.log("Error: " + err);
|
||||
}
|
||||
redis_client.keys("map_style|test_cartodb_user_1_db|~" + expected_token, function(err, matches) {
|
||||
redis_client.keys("map_cfg|" + expected_token, function(err, matches) {
|
||||
if ( err ) errors.push(err.message);
|
||||
assert.equal(matches.length, 1, "Missing expected token " + expected_token + " from redis: " + matches);
|
||||
redis_client.del(matches, function(err) {
|
||||
@@ -193,7 +193,7 @@ suite('multilayer', function() {
|
||||
]
|
||||
};
|
||||
|
||||
var expected_token = "6d8e4ad5458e2d25cf0eef38e38717a6";
|
||||
var expected_token; // = "6d8e4ad5458e2d25cf0eef38e38717a6";
|
||||
Step(
|
||||
function do_post()
|
||||
{
|
||||
@@ -217,7 +217,7 @@ suite('multilayer', function() {
|
||||
if ( expected_token ) {
|
||||
assert.equal(parsedBody.layergroupid, expected_token + ':' + expected_last_updated_epoch);
|
||||
}
|
||||
else expected_token = parsedBody.layergroupid;
|
||||
else expected_token = parsedBody.layergroupid.split(':')[0];
|
||||
next(null, res);
|
||||
});
|
||||
},
|
||||
@@ -329,7 +329,7 @@ suite('multilayer', function() {
|
||||
errors.push(err.message);
|
||||
console.log("Error: " + err);
|
||||
}
|
||||
redis_client.keys("map_style|test_cartodb_user_1_db|~" + expected_token, function(err, matches) {
|
||||
redis_client.keys("map_cfg|" + expected_token, function(err, matches) {
|
||||
if ( err ) errors.push(err.message);
|
||||
assert.equal(matches.length, 1, "Missing expected token " + expected_token + " from redis: " + matches);
|
||||
redis_client.del(matches, function(err) {
|
||||
@@ -425,7 +425,7 @@ suite('multilayer', function() {
|
||||
var next = this;
|
||||
// trip epoch
|
||||
expected_token = expected_token.split(':')[0];
|
||||
redis_client.keys("map_style|test_cartodb_user_1_db|~" + expected_token, function(err, matches) {
|
||||
redis_client.keys("map_cfg|" + expected_token, function(err, matches) {
|
||||
redis_client.del(matches, next);
|
||||
});
|
||||
},
|
||||
@@ -517,7 +517,7 @@ suite('multilayer', function() {
|
||||
]
|
||||
};
|
||||
|
||||
var expected_token = "b4ed64d93a411a59f330ab3d798e4009";
|
||||
var expected_token; // = "b4ed64d93a411a59f330ab3d798e4009";
|
||||
Step(
|
||||
function do_post()
|
||||
{
|
||||
@@ -542,7 +542,7 @@ suite('multilayer', function() {
|
||||
if ( expected_token ) {
|
||||
assert.equal(parsedBody.layergroupid, expected_token + ':' + expected_last_updated_epoch);
|
||||
}
|
||||
else expected_token = parsedBody.layergroupid;
|
||||
else expected_token = parsedBody.layergroupid.split(':')[0];
|
||||
next(null, res);
|
||||
});
|
||||
},
|
||||
@@ -650,7 +650,7 @@ suite('multilayer', function() {
|
||||
errors.push(err.message);
|
||||
console.log("Error: " + err);
|
||||
}
|
||||
redis_client.keys("map_style|test_cartodb_user_1_db|~" + expected_token, function(err, matches) {
|
||||
redis_client.keys("map_cfg|" + expected_token, function(err, matches) {
|
||||
if ( err ) errors.push(err.message);
|
||||
assert.equal(matches.length, 1, "Missing expected token " + expected_token + " from redis: " + matches);
|
||||
redis_client.del(matches, function(err) {
|
||||
@@ -805,7 +805,7 @@ suite('multilayer', function() {
|
||||
errors.push(err.message);
|
||||
console.log("Error: " + err);
|
||||
}
|
||||
redis_client.keys("map_style|test_cartodb_user_1_db|~" + expected_token, function(err, matches) {
|
||||
redis_client.keys("map_cfg|" + expected_token, function(err, matches) {
|
||||
if ( err ) errors.push(err.message);
|
||||
assert.equal(matches.length, 1, "Missing expected token " + expected_token + " from redis: " + matches);
|
||||
redis_client.del(matches, function(err) {
|
||||
@@ -880,7 +880,7 @@ suite('multilayer', function() {
|
||||
if ( err ) errors.push(err.message);
|
||||
if ( ! expected_token ) return null;
|
||||
var next = this;
|
||||
redis_client.keys("map_style|test_cartodb_user_1_db|~" + expected_token, function(err, matches) {
|
||||
redis_client.keys("map_cfg|" + expected_token, function(err, matches) {
|
||||
if ( err ) errors.push(err.message);
|
||||
assert.equal(matches.length, 1, "Missing expected token " + expected_token + " from redis: " + matches);
|
||||
redis_client.del(matches, function(err) {
|
||||
@@ -947,7 +947,7 @@ suite('multilayer', function() {
|
||||
if ( err ) errors.push(err.message);
|
||||
if ( ! expected_token ) return null;
|
||||
var next = this;
|
||||
redis_client.keys("map_style|test_cartodb_user_1_db|~" + expected_token, function(err, matches) {
|
||||
redis_client.keys("map_cfg|" + expected_token, function(err, matches) {
|
||||
if ( err ) errors.push(err.message);
|
||||
assert.equal(matches.length, 1, "Missing expected token " + expected_token + " from redis: " + matches);
|
||||
redis_client.del(matches, function(err) {
|
||||
@@ -964,6 +964,46 @@ suite('multilayer', function() {
|
||||
);
|
||||
});
|
||||
|
||||
// See https://github.com/CartoDB/Windshaft-cartodb/issues/133
|
||||
test("MapConfig with mapnik layer and no cartocss", function(done) {
|
||||
|
||||
var layergroup = {
|
||||
version: '1.0.0',
|
||||
layers: [
|
||||
{ options: {
|
||||
sql: 'select cartodb_id, ST_Translate(the_geom_webmercator, 5e6, 0) as the_geom_webmercator from test_table limit 2',
|
||||
interactivity: 'cartodb_id'
|
||||
} }
|
||||
]
|
||||
};
|
||||
|
||||
Step(
|
||||
function do_post()
|
||||
{
|
||||
var next = this;
|
||||
assert.response(server, {
|
||||
url: '/tiles/layergroup',
|
||||
method: 'POST',
|
||||
headers: {host: 'localhost', 'Content-Type': 'application/json' },
|
||||
data: JSON.stringify(layergroup)
|
||||
}, {}, function(res, err) { next(err, res); });
|
||||
},
|
||||
function check_post(err, res) {
|
||||
if ( err ) throw err;
|
||||
assert.equal(res.statusCode, 400, res.statusCode + ': ' + res.body);
|
||||
var parsed = JSON.parse(res.body);
|
||||
assert.ok(parsed.errors, 'Missing "errors" in response: ' + JSON.stringify(parsed));
|
||||
assert.equal(parsed.errors.length, 1);
|
||||
var msg = parsed.errors[0];
|
||||
assert.equal(msg, 'Missing cartocss for layer 0 options');
|
||||
return null;
|
||||
},
|
||||
function finish(err) {
|
||||
done(err);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
suiteTeardown(function(done) {
|
||||
|
||||
|
||||
@@ -1126,6 +1126,37 @@ suite('server', function() {
|
||||
);
|
||||
});
|
||||
|
||||
test("passes hostname header to sqlapi", function(done){
|
||||
var qo = {
|
||||
sql: "SELECT * from gadm4",
|
||||
map_key: 1234
|
||||
};
|
||||
var sqlapi;
|
||||
Step(
|
||||
function sendRequest(err) {
|
||||
var next = this;
|
||||
assert.response(server, {
|
||||
headers: {host: 'localhost'},
|
||||
url: '/tiles/gadm4/6/31/24.png?' + querystring.stringify(qo),
|
||||
method: 'GET'
|
||||
},{}, function(res) { next(null, res); });
|
||||
},
|
||||
function checkResponse(err, res) {
|
||||
if ( err ) throw err;
|
||||
assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body);
|
||||
var last_request = sqlapi_server.getLastRequest();
|
||||
assert.ok(last_request);
|
||||
var host = last_request.headers['host'];
|
||||
assert.ok(host);
|
||||
assert.equal(host, 'localhost.donot_look_this_up');
|
||||
return null;
|
||||
},
|
||||
function finish(err) {
|
||||
done(err);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test("requests to skip cache on sqlapi error", function(done){
|
||||
var qo = {
|
||||
sql: "SELECT g.cartodb_id, g.codineprov, t.the_geom_webmercator "
|
||||
@@ -1176,6 +1207,20 @@ suite('server', function() {
|
||||
});
|
||||
});
|
||||
|
||||
// See https://github.com/CartoDB/Windshaft-cartodb/issues/115
|
||||
test.skip("get'ing tile with not-strictly-valid style", function(done) {
|
||||
var style = querystring.stringify({style: '#test_table{line-color:black}}', style_version: '2.0.0'});
|
||||
assert.response(server, {
|
||||
headers: {host: 'localhost'},
|
||||
url: '/tiles/test_table/0/0/0.png?' + style, // madrid
|
||||
method: 'GET',
|
||||
encoding: 'binary'
|
||||
},{}, function(res){
|
||||
assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// DELETE CACHE
|
||||
|
||||
@@ -131,6 +131,153 @@ suite('template_api', function() {
|
||||
);
|
||||
});
|
||||
|
||||
// See https://github.com/CartoDB/Windshaft-cartodb/issues/128
|
||||
test("cannot create template with auth='token' and no valid tokens", function(done) {
|
||||
var tpl_id;
|
||||
Step(
|
||||
function postTemplate1()
|
||||
{
|
||||
// clone the valid one, and give it another name
|
||||
var broken_template = JSON.parse(JSON.stringify(template_acceptance1));
|
||||
broken_template.name = 'broken1';
|
||||
// Set auth='token' and specify no tokens
|
||||
broken_template.auth.method = 'token';
|
||||
delete broken_template.auth.tokens;
|
||||
var post_request_1 = {
|
||||
url: '/tiles/template?api_key=1234',
|
||||
method: 'POST',
|
||||
headers: {host: 'localhost', 'Content-Type': 'application/json' },
|
||||
data: JSON.stringify(broken_template)
|
||||
}
|
||||
var next = this;
|
||||
assert.response(server, post_request_1, {},
|
||||
function(res) { next(null, res); });
|
||||
},
|
||||
function checkFailure1(err, res)
|
||||
{
|
||||
if ( err ) throw err;
|
||||
assert.equal(res.statusCode, 400, res.body);
|
||||
var parsedBody = JSON.parse(res.body);
|
||||
assert.ok(parsedBody.hasOwnProperty('error'), res.body);
|
||||
var re = RegExp(/invalid.*authentication.*missing/i);
|
||||
assert.ok(parsedBody.error.match(re),
|
||||
'Error for invalid authentication does not match ' + re + ': ' + parsedBody.error);
|
||||
return null;
|
||||
},
|
||||
function postTemplate2(err)
|
||||
{
|
||||
if ( err ) throw err;
|
||||
// clone the valid one and rename it
|
||||
var broken_template = JSON.parse(JSON.stringify(template_acceptance1));
|
||||
broken_template.name = 'broken1';
|
||||
// Set auth='token' and specify no tokens
|
||||
broken_template.auth.method = 'token';
|
||||
broken_template.auth.tokens = [];
|
||||
var post_request_1 = {
|
||||
url: '/tiles/template?api_key=1234',
|
||||
method: 'POST',
|
||||
headers: {host: 'localhost', 'Content-Type': 'application/json' },
|
||||
data: JSON.stringify(broken_template)
|
||||
}
|
||||
var next = this;
|
||||
assert.response(server, post_request_1, {},
|
||||
function(res) { next(null, res); });
|
||||
},
|
||||
function checkFailure2(err, res)
|
||||
{
|
||||
if ( err ) throw err;
|
||||
assert.equal(res.statusCode, 400, res.body);
|
||||
var parsedBody = JSON.parse(res.body);
|
||||
assert.ok(parsedBody.hasOwnProperty('error'), res.body);
|
||||
var re = RegExp(/invalid.*authentication.*missing/i);
|
||||
assert.ok(parsedBody.error.match(re),
|
||||
'Error for invalid authentication does not match ' + re + ': ' + parsedBody.error);
|
||||
return null;
|
||||
},
|
||||
function postTemplateValid(err)
|
||||
{
|
||||
if ( err ) throw err;
|
||||
// clone the valid one and rename it
|
||||
var broken_template = JSON.parse(JSON.stringify(template_acceptance1));
|
||||
broken_template.name = 'broken1';
|
||||
var post_request_1 = {
|
||||
url: '/tiles/template?api_key=1234',
|
||||
method: 'POST',
|
||||
headers: {host: 'localhost', 'Content-Type': 'application/json' },
|
||||
data: JSON.stringify(broken_template)
|
||||
}
|
||||
var next = this;
|
||||
assert.response(server, post_request_1, {},
|
||||
function(res) { next(null, res); });
|
||||
},
|
||||
function putTemplateInvalid(err, res)
|
||||
{
|
||||
if ( err ) throw err;
|
||||
assert.equal(res.statusCode, 200, res.body);
|
||||
var parsed = JSON.parse(res.body);
|
||||
assert.ok(parsed.hasOwnProperty('template_id'),
|
||||
"Missing 'template_id' from response body: " + res.body);
|
||||
tpl_id = parsed.template_id;
|
||||
// clone the valid one and rename it
|
||||
var broken_template = JSON.parse(JSON.stringify(template_acceptance1));
|
||||
broken_template.name = 'broken1';
|
||||
// Set auth='token' and specify no tokens
|
||||
broken_template.auth.method = 'token';
|
||||
broken_template.auth.tokens = [];
|
||||
var put_request_1 = {
|
||||
url: '/tiles/template/' + tpl_id + '/?api_key=1234',
|
||||
method: 'PUT',
|
||||
headers: {host: 'localhost', 'Content-Type': 'application/json' },
|
||||
data: JSON.stringify(broken_template)
|
||||
}
|
||||
var next = this;
|
||||
assert.response(server, put_request_1, {},
|
||||
function(res) { next(null, res); });
|
||||
},
|
||||
function deleteTemplate(err, res)
|
||||
{
|
||||
if ( err ) throw err;
|
||||
assert.equal(res.statusCode, 400, res.statusCode + ": " + res.body);
|
||||
var parsed = JSON.parse(res.body);
|
||||
assert.ok(parsed.hasOwnProperty('error'),
|
||||
"Missing 'error' from response body: " + res.body);
|
||||
var re = RegExp(/invalid.*authentication.*missing/i);
|
||||
assert.ok(parsed.error.match(re),
|
||||
'Error for invalid authentication on PUT does not match ' +
|
||||
re + ': ' + parsed.error);
|
||||
var del_request = {
|
||||
url: '/tiles/template/' + tpl_id + '?api_key=1234',
|
||||
method: 'DELETE',
|
||||
headers: {host: 'localhost'}
|
||||
}
|
||||
var next = this;
|
||||
assert.response(server, del_request, {},
|
||||
function(res, err) { next(err, res); });
|
||||
},
|
||||
function checkDelete(err, res) {
|
||||
if ( err ) throw err;
|
||||
assert.equal(res.statusCode, 204, res.statusCode + ': ' + res.body);
|
||||
assert.ok(!res.body, 'Unexpected body in DELETE /template response');
|
||||
return null;
|
||||
},
|
||||
function finish(err) {
|
||||
var errors = [];
|
||||
if ( err ) errors.push(err);
|
||||
redis_client.keys("map_*|localhost", function(err, keys) {
|
||||
if ( err ) errors.push(err.message);
|
||||
var todrop = _.map(keys, function(m) {
|
||||
if ( m.match(/^map_(tpl|crt)|/) )
|
||||
return m;
|
||||
});
|
||||
if ( todrop.length )
|
||||
errors.push(new Error("Unexpected keys in redis: " + todrop));
|
||||
if ( errors.length ) done(new Error(errors.join(',')));
|
||||
else done();
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test("instance endpoint should return CORS headers", function(done){
|
||||
Step(function postTemplate1(err, res) {
|
||||
var next = this;
|
||||
@@ -803,6 +950,605 @@ suite('template_api', function() {
|
||||
);
|
||||
});
|
||||
|
||||
test("can instanciate a template with torque layer by id", function(done) {
|
||||
|
||||
// This map fetches data from a private table
|
||||
var template = {
|
||||
version: '0.0.1',
|
||||
name: 'acceptance1',
|
||||
auth: { method: 'token', valid_tokens: ['valid1','valid2'] },
|
||||
layergroup: {
|
||||
version: '1.1.0',
|
||||
layers: [
|
||||
{ type: 'torque', options: {
|
||||
sql: "select * from test_table_private_1 LIMIT 0",
|
||||
cartocss: "Map { -torque-frame-count:1; -torque-resolution:1; -torque-aggregation-function:'count(*)'; -torque-time-attribute:'updated_at'; }"
|
||||
} }
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
var template_params = {};
|
||||
|
||||
var errors = [];
|
||||
var expected_failure = false;
|
||||
var tpl_id;
|
||||
var layergroupid;
|
||||
Step(
|
||||
function postTemplate(err, res)
|
||||
{
|
||||
var next = this;
|
||||
var post_request = {
|
||||
url: '/tiles/template?api_key=1234',
|
||||
method: 'POST',
|
||||
headers: {host: 'localhost', 'Content-Type': 'application/json' },
|
||||
data: JSON.stringify(template)
|
||||
}
|
||||
assert.response(server, post_request, {},
|
||||
function(res) { next(null, res); });
|
||||
},
|
||||
function instanciateNoAuth(err, res)
|
||||
{
|
||||
if ( err ) throw err;
|
||||
assert.equal(res.statusCode, 200, res.body);
|
||||
var parsed = JSON.parse(res.body);
|
||||
assert.ok(parsed.hasOwnProperty('template_id'),
|
||||
"Missing 'template_id' from response body: " + res.body);
|
||||
tpl_id = parsed.template_id;
|
||||
var post_request = {
|
||||
url: '/tiles/template/' + tpl_id,
|
||||
method: 'POST',
|
||||
headers: {host: 'localhost', 'Content-Type': 'application/json' },
|
||||
data: JSON.stringify(template_params)
|
||||
}
|
||||
var next = this;
|
||||
assert.response(server, post_request, {},
|
||||
function(res) { next(null, res); });
|
||||
},
|
||||
function instanciateAuth(err, res)
|
||||
{
|
||||
if ( err ) throw err;
|
||||
assert.equal(res.statusCode, 401,
|
||||
'Unexpected success instanciating template with no auth: '
|
||||
+ res.statusCode + ': ' + res.body);
|
||||
var parsed = JSON.parse(res.body);
|
||||
assert.ok(parsed.hasOwnProperty('error'),
|
||||
"Missing 'error' from response body: " + res.body);
|
||||
assert.ok(parsed.error.match(/unauthorized/i),
|
||||
'Unexpected error for unauthorized instance : ' + parsed.error);
|
||||
var post_request = {
|
||||
url: '/tiles/template/' + tpl_id + '?auth_token=valid2',
|
||||
method: 'POST',
|
||||
headers: {host: 'localhost', 'Content-Type': 'application/json' },
|
||||
data: JSON.stringify(template_params)
|
||||
}
|
||||
var next = this;
|
||||
assert.response(server, post_request, {},
|
||||
function(res) { next(null, res); });
|
||||
},
|
||||
function fetchTileNoAuth(err, res) {
|
||||
if ( err ) throw err;
|
||||
assert.equal(res.statusCode, 200,
|
||||
'Instantiating template: ' + res.statusCode + ': ' + res.body);
|
||||
var parsed = JSON.parse(res.body);
|
||||
assert.ok(parsed.hasOwnProperty('layergroupid'),
|
||||
"Missing 'layergroupid' from response body: " + res.body);
|
||||
layergroupid = parsed.layergroupid;
|
||||
assert.ok(layergroupid.match(/^localhost@/),
|
||||
"Returned layergroupid does not start with signer name: "
|
||||
+ layergroupid);
|
||||
assert.ok(parsed.hasOwnProperty('last_updated'),
|
||||
"Missing 'last_updated' from response body: " + res.body);
|
||||
// TODO: check value of last_updated ?
|
||||
var get_request = {
|
||||
url: '/tiles/layergroup/' + layergroupid + ':cb0/0/0/0/0.json.torque',
|
||||
method: 'GET',
|
||||
headers: {host: 'localhost' },
|
||||
encoding: 'binary'
|
||||
}
|
||||
var next = this;
|
||||
assert.response(server, get_request, {},
|
||||
function(res) { next(null, res); });
|
||||
},
|
||||
function fetchTileAuth(err, res) {
|
||||
if ( err ) throw err;
|
||||
assert.equal(res.statusCode, 401,
|
||||
'Fetching tile with no auth: ' + res.statusCode + ': ' + res.body);
|
||||
var parsed = JSON.parse(res.body);
|
||||
assert.ok(parsed.hasOwnProperty('error'),
|
||||
"Missing 'error' from response body: " + res.body);
|
||||
assert.ok(parsed.error.match(/permission denied/i),
|
||||
'Unexpected error for unauthorized instance '
|
||||
+ '(expected /permission denied): ' + parsed.error);
|
||||
var get_request = {
|
||||
url: '/tiles/layergroup/' + layergroupid + ':cb1/0/0/0/0.json.torque?auth_token=valid1',
|
||||
method: 'GET',
|
||||
headers: {host: 'localhost' },
|
||||
encoding: 'binary'
|
||||
}
|
||||
var next = this;
|
||||
assert.response(server, get_request, {},
|
||||
function(res) { next(null, res); });
|
||||
},
|
||||
function checkTile(err, res) {
|
||||
if ( err ) throw err;
|
||||
assert.equal(res.statusCode, 200,
|
||||
'Unexpected error for authorized instance: '
|
||||
+ res.statusCode + ' -- ' + res.body);
|
||||
assert.equal(res.headers['content-type'], "application/json; charset=utf-8");
|
||||
return null;
|
||||
},
|
||||
function deleteTemplate(err)
|
||||
{
|
||||
if ( err ) throw err;
|
||||
var del_request = {
|
||||
url: '/tiles/template/' + tpl_id + '?api_key=1234',
|
||||
method: 'DELETE',
|
||||
headers: {host: 'localhost'}
|
||||
}
|
||||
var next = this;
|
||||
assert.response(server, del_request, {},
|
||||
function(res) { next(null, res); });
|
||||
},
|
||||
function fetchTileDeleted(err, res) {
|
||||
if ( err ) throw err;
|
||||
assert.equal(res.statusCode, 204,
|
||||
'Deleting template: ' + res.statusCode + ':' + res.body);
|
||||
var get_request = {
|
||||
url: '/tiles/layergroup/' + layergroupid + ':cb2/0/0/0/0.json.torque?auth_token=valid1',
|
||||
method: 'GET',
|
||||
headers: {host: 'localhost' },
|
||||
encoding: 'binary'
|
||||
}
|
||||
var next = this;
|
||||
assert.response(server, get_request, {},
|
||||
function(res) { next(null, res); });
|
||||
},
|
||||
function checkTileDeleted(err, res) {
|
||||
if ( err ) throw err;
|
||||
assert.equal(res.statusCode, 401,
|
||||
'Unexpected statusCode fetch tile after signature revokal: '
|
||||
+ res.statusCode + ':' + res.body);
|
||||
var parsed = JSON.parse(res.body);
|
||||
assert.ok(parsed.hasOwnProperty('error'),
|
||||
"Missing 'error' from response body: " + res.body);
|
||||
assert.ok(parsed.error.match(/permission denied/i),
|
||||
'Unexpected error for unauthorized access : ' + parsed.error);
|
||||
return null;
|
||||
},
|
||||
function finish(err) {
|
||||
if ( err ) errors.push(err);
|
||||
redis_client.keys("map_*|localhost", function(err, keys) {
|
||||
if ( err ) errors.push(err.message);
|
||||
var todrop = _.map(keys, function(m) {
|
||||
if ( m.match(/^map_(tpl|crt)|/) )
|
||||
return m;
|
||||
});
|
||||
if ( todrop.length ) {
|
||||
errors.push(new Error("Unexpected keys in redis: " + todrop));
|
||||
redis_client.del(todrop, function(err) {
|
||||
if ( err ) errors.push(err.message);
|
||||
if ( errors.length ) {
|
||||
done(new Error(errors));
|
||||
}
|
||||
else done(null);
|
||||
});
|
||||
} else {
|
||||
if ( errors.length ) {
|
||||
done(new Error(errors));
|
||||
}
|
||||
else done(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test("can instanciate a template with attribute service by id", function(done) {
|
||||
|
||||
// This map fetches data from a private table
|
||||
var template = {
|
||||
version: '0.0.1',
|
||||
name: 'acceptance1',
|
||||
auth: { method: 'token', valid_tokens: ['valid1','valid2'] },
|
||||
layergroup: {
|
||||
version: '1.1.0',
|
||||
layers: [
|
||||
{ options: {
|
||||
sql: "select * from test_table_private_1 where cartodb_id in ( 5,6 )",
|
||||
cartocss: '#layer { marker-fill:blue; marker-allow-overlap:true; }',
|
||||
cartocss_version: '2.0.2',
|
||||
attributes: { id:'cartodb_id', columns: ['name', 'address'] }
|
||||
} }
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
var template_params = {};
|
||||
|
||||
var errors = [];
|
||||
var expected_failure = false;
|
||||
var tpl_id;
|
||||
var layergroupid;
|
||||
Step(
|
||||
function postTemplate(err, res)
|
||||
{
|
||||
var next = this;
|
||||
var post_request = {
|
||||
url: '/tiles/template?api_key=1234',
|
||||
method: 'POST',
|
||||
headers: {host: 'localhost', 'Content-Type': 'application/json' },
|
||||
data: JSON.stringify(template)
|
||||
}
|
||||
assert.response(server, post_request, {},
|
||||
function(res) { next(null, res); });
|
||||
},
|
||||
function instanciateNoAuth(err, res)
|
||||
{
|
||||
if ( err ) throw err;
|
||||
assert.equal(res.statusCode, 200, res.body);
|
||||
var parsed = JSON.parse(res.body);
|
||||
assert.ok(parsed.hasOwnProperty('template_id'),
|
||||
"Missing 'template_id' from response body: " + res.body);
|
||||
tpl_id = parsed.template_id;
|
||||
var post_request = {
|
||||
url: '/tiles/template/' + tpl_id,
|
||||
method: 'POST',
|
||||
headers: {host: 'localhost', 'Content-Type': 'application/json' },
|
||||
data: JSON.stringify(template_params)
|
||||
}
|
||||
var next = this;
|
||||
assert.response(server, post_request, {},
|
||||
function(res) { next(null, res); });
|
||||
},
|
||||
function instanciateAuth(err, res)
|
||||
{
|
||||
if ( err ) throw err;
|
||||
assert.equal(res.statusCode, 401,
|
||||
'Unexpected success instanciating template with no auth: '
|
||||
+ res.statusCode + ': ' + res.body);
|
||||
var parsed = JSON.parse(res.body);
|
||||
assert.ok(parsed.hasOwnProperty('error'),
|
||||
"Missing 'error' from response body: " + res.body);
|
||||
assert.ok(parsed.error.match(/unauthorized/i),
|
||||
'Unexpected error for unauthorized instance : ' + parsed.error);
|
||||
var post_request = {
|
||||
url: '/tiles/template/' + tpl_id + '?auth_token=valid2',
|
||||
method: 'POST',
|
||||
headers: {host: 'localhost', 'Content-Type': 'application/json' },
|
||||
data: JSON.stringify(template_params)
|
||||
}
|
||||
var next = this;
|
||||
assert.response(server, post_request, {},
|
||||
function(res, err) { next(null, res); });
|
||||
},
|
||||
function fetchAttributeNoAuth(err, res) {
|
||||
if ( err ) throw err;
|
||||
assert.equal(res.statusCode, 200,
|
||||
'Instantiating template: ' + res.statusCode + ': ' + res.body);
|
||||
var parsed = JSON.parse(res.body);
|
||||
assert.ok(parsed.hasOwnProperty('layergroupid'),
|
||||
"Missing 'layergroupid' from response body: " + res.body);
|
||||
layergroupid = parsed.layergroupid;
|
||||
assert.ok(layergroupid.match(/^localhost@/),
|
||||
"Returned layergroupid does not start with signer name: "
|
||||
+ layergroupid);
|
||||
assert.ok(parsed.hasOwnProperty('last_updated'),
|
||||
"Missing 'last_updated' from response body: " + res.body);
|
||||
// TODO: check value of last_updated ?
|
||||
var get_request = {
|
||||
url: '/tiles/layergroup/' + layergroupid + ':cb0/0/attributes/5',
|
||||
method: 'GET',
|
||||
headers: {host: 'localhost' },
|
||||
encoding: 'binary'
|
||||
}
|
||||
var next = this;
|
||||
assert.response(server, get_request, {},
|
||||
function(res) { next(null, res); });
|
||||
},
|
||||
function fetchAttributeAuth(err, res) {
|
||||
if ( err ) throw err;
|
||||
assert.equal(res.statusCode, 401,
|
||||
'Fetching tile with no auth: ' + res.statusCode + ': ' + res.body);
|
||||
var parsed = JSON.parse(res.body);
|
||||
assert.ok(parsed.hasOwnProperty('error'),
|
||||
"Missing 'error' from response body: " + res.body);
|
||||
assert.ok(parsed.error.match(/permission denied/i),
|
||||
'Unexpected error for unauthorized getAttributes '
|
||||
+ '(expected /permission denied/): ' + parsed.error);
|
||||
var get_request = {
|
||||
url: '/tiles/layergroup/' + layergroupid + ':cb1/0/attributes/5?auth_token=valid2',
|
||||
method: 'GET',
|
||||
headers: {host: 'localhost' },
|
||||
encoding: 'binary'
|
||||
}
|
||||
var next = this;
|
||||
assert.response(server, get_request, {},
|
||||
function(res) { next(null, res); });
|
||||
},
|
||||
function checkAttribute(err, res) {
|
||||
if ( err ) throw err;
|
||||
assert.equal(res.statusCode, 200,
|
||||
'Unexpected error for authorized getAttributes: '
|
||||
+ res.statusCode + ' -- ' + res.body);
|
||||
assert.equal(res.headers['content-type'], "application/json; charset=utf-8");
|
||||
return null;
|
||||
},
|
||||
function deleteTemplate(err)
|
||||
{
|
||||
if ( err ) throw err;
|
||||
var del_request = {
|
||||
url: '/tiles/template/' + tpl_id + '?api_key=1234',
|
||||
method: 'DELETE',
|
||||
headers: {host: 'localhost'}
|
||||
}
|
||||
var next = this;
|
||||
assert.response(server, del_request, {},
|
||||
function(res) { next(null, res); });
|
||||
},
|
||||
function fetchAttrDeleted(err, res) {
|
||||
if ( err ) throw err;
|
||||
assert.equal(res.statusCode, 204,
|
||||
'Deleting template: ' + res.statusCode + ':' + res.body);
|
||||
var get_request = {
|
||||
url: '/tiles/layergroup/' + layergroupid + ':cb2/0/attributes/5?auth_token=valid2',
|
||||
method: 'GET',
|
||||
headers: {host: 'localhost' },
|
||||
encoding: 'binary'
|
||||
}
|
||||
var next = this;
|
||||
assert.response(server, get_request, {},
|
||||
function(res) { next(null, res); });
|
||||
},
|
||||
function checkTileDeleted(err, res) {
|
||||
if ( err ) throw err;
|
||||
assert.equal(res.statusCode, 401,
|
||||
'Unexpected statusCode fetch tile after signature revokal: '
|
||||
+ res.statusCode + ':' + res.body);
|
||||
var parsed = JSON.parse(res.body);
|
||||
assert.ok(parsed.hasOwnProperty('error'),
|
||||
"Missing 'error' from response body: " + res.body);
|
||||
assert.ok(parsed.error.match(/permission denied/i),
|
||||
'Unexpected error for unauthorized access : ' + parsed.error);
|
||||
return null;
|
||||
},
|
||||
function finish(err) {
|
||||
if ( err ) errors.push(err);
|
||||
redis_client.keys("map_*|localhost", function(err, keys) {
|
||||
if ( err ) errors.push(err.message);
|
||||
var todrop = _.map(keys, function(m) {
|
||||
if ( m.match(/^map_(tpl|crt)|/) )
|
||||
return m;
|
||||
});
|
||||
if ( todrop.length ) {
|
||||
errors.push(new Error("Unexpected keys in redis: " + todrop));
|
||||
redis_client.del(todrop, function(err) {
|
||||
if ( err ) errors.push(err.message);
|
||||
if ( errors.length ) {
|
||||
done(new Error(errors));
|
||||
}
|
||||
else done(null);
|
||||
});
|
||||
} else {
|
||||
if ( errors.length ) {
|
||||
done(new Error(errors));
|
||||
}
|
||||
else done(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test("can instanciate a template by id with open auth", function(done) {
|
||||
|
||||
// This map fetches data from a private table
|
||||
var template_acceptance_open = {
|
||||
version: '0.0.1',
|
||||
name: 'acceptance_open',
|
||||
auth: { method: 'open' },
|
||||
layergroup: {
|
||||
version: '1.0.0',
|
||||
layers: [
|
||||
{ options: {
|
||||
sql: "select * from test_table_private_1 LIMIT 0",
|
||||
cartocss: '#layer { marker-fill:blue; marker-allow-overlap:true; }',
|
||||
cartocss_version: '2.0.2',
|
||||
interactivity: 'cartodb_id'
|
||||
} }
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
var template_params = {};
|
||||
|
||||
var errors = [];
|
||||
var expected_failure = false;
|
||||
var tpl_id;
|
||||
var layergroupid;
|
||||
Step(
|
||||
function postTemplate(err, res)
|
||||
{
|
||||
var next = this;
|
||||
var post_request = {
|
||||
url: '/tiles/template?api_key=1234',
|
||||
method: 'POST',
|
||||
headers: {host: 'localhost', 'Content-Type': 'application/json' },
|
||||
data: JSON.stringify(template_acceptance_open)
|
||||
}
|
||||
assert.response(server, post_request, {},
|
||||
function(res) { next(null, res); });
|
||||
},
|
||||
function instanciateNoAuth(err, res)
|
||||
{
|
||||
if ( err ) throw err;
|
||||
assert.equal(res.statusCode, 200, res.body);
|
||||
var parsed = JSON.parse(res.body);
|
||||
assert.ok(parsed.hasOwnProperty('template_id'),
|
||||
"Missing 'template_id' from response body: " + res.body);
|
||||
tpl_id = parsed.template_id;
|
||||
var post_request = {
|
||||
url: '/tiles/template/' + tpl_id,
|
||||
method: 'POST',
|
||||
headers: {host: 'localhost', 'Content-Type': 'application/json' },
|
||||
data: JSON.stringify(template_params)
|
||||
}
|
||||
var next = this;
|
||||
assert.response(server, post_request, {},
|
||||
function(res) { next(null, res); });
|
||||
},
|
||||
function instanciateAuth(err, res)
|
||||
{
|
||||
if ( err ) throw err;
|
||||
assert.equal(res.statusCode, 200,
|
||||
'Unexpected success instanciating template with no auth: '
|
||||
+ res.statusCode + ': ' + res.body);
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test("can instanciate a template using jsonp", function(done) {
|
||||
|
||||
// This map fetches data from a private table
|
||||
var template_acceptance_open = {
|
||||
version: '0.0.1',
|
||||
name: 'acceptance_open_jsonp',
|
||||
auth: { method: 'open' },
|
||||
layergroup: {
|
||||
version: '1.0.0',
|
||||
layers: [
|
||||
{ options: {
|
||||
sql: "select * from test_table_private_1 LIMIT 0",
|
||||
cartocss: '#layer { marker-fill:blue; marker-allow-overlap:true; }',
|
||||
cartocss_version: '2.0.2',
|
||||
interactivity: 'cartodb_id'
|
||||
} }
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
var template_params = {};
|
||||
|
||||
var errors = [];
|
||||
var expected_failure = false;
|
||||
var tpl_id;
|
||||
var layergroupid;
|
||||
Step(
|
||||
function postTemplate(err, res)
|
||||
{
|
||||
var next = this;
|
||||
var post_request = {
|
||||
url: '/tiles/template?api_key=1234',
|
||||
method: 'POST',
|
||||
headers: {host: 'localhost', 'Content-Type': 'application/json' },
|
||||
data: JSON.stringify(template_acceptance_open)
|
||||
}
|
||||
assert.response(server, post_request, {},
|
||||
function(res) { next(null, res); });
|
||||
},
|
||||
function instanciateNoAuth(err, res)
|
||||
{
|
||||
if ( err ) throw err;
|
||||
assert.equal(res.statusCode, 200, res.body);
|
||||
var parsed = JSON.parse(res.body);
|
||||
assert.ok(parsed.hasOwnProperty('template_id'),
|
||||
"Missing 'template_id' from response body: " + res.body);
|
||||
tpl_id = parsed.template_id;
|
||||
var post_request = {
|
||||
url: '/tiles/template/' + tpl_id + "/jsonp?callback=test",
|
||||
method: 'GET',
|
||||
headers: {host: 'localhost' }
|
||||
}
|
||||
var next = this;
|
||||
assert.response(server, post_request, {},
|
||||
function(res) { next(null, res); });
|
||||
},
|
||||
function instanciateAuth(err, res)
|
||||
{
|
||||
if ( err ) throw err;
|
||||
assert.equal(res.statusCode, 200,
|
||||
'Unexpected success instanciating template with no auth: '
|
||||
+ res.statusCode + ': ' + res.body);
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test("can instanciate a template using jsonp with params", function(done) {
|
||||
|
||||
// This map fetches data from a private table
|
||||
var template_acceptance_open = {
|
||||
version: '0.0.1',
|
||||
name: 'acceptance_open_jsonp_params',
|
||||
auth: { method: 'open' },
|
||||
/*
|
||||
placeholders: {
|
||||
color: { type: "css_color", default: "red" }
|
||||
},*/
|
||||
layergroup: {
|
||||
version: '1.0.0',
|
||||
layers: [
|
||||
{ options: {
|
||||
sql: "select * from test_table_private_1 LIMIT 0",
|
||||
cartocss: '#layer { marker-fill: <%= color %>; marker-allow-overlap:true; }',
|
||||
cartocss_version: '2.0.2',
|
||||
interactivity: 'cartodb_id'
|
||||
} }
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
var template_params = {};
|
||||
|
||||
var errors = [];
|
||||
var expected_failure = false;
|
||||
var tpl_id;
|
||||
var layergroupid;
|
||||
Step(
|
||||
function postTemplate(err, res)
|
||||
{
|
||||
var next = this;
|
||||
var post_request = {
|
||||
url: '/tiles/template?api_key=1234',
|
||||
method: 'POST',
|
||||
headers: {host: 'localhost', 'Content-Type': 'application/json' },
|
||||
data: JSON.stringify(template_acceptance_open)
|
||||
}
|
||||
assert.response(server, post_request, {},
|
||||
function(res) { next(null, res); });
|
||||
},
|
||||
function instanciateNoAuth(err, res)
|
||||
{
|
||||
if ( err ) throw err;
|
||||
assert.equal(res.statusCode, 200, res.body);
|
||||
var parsed = JSON.parse(res.body);
|
||||
assert.ok(parsed.hasOwnProperty('template_id'),
|
||||
"Missing 'template_id' from response body: " + res.body);
|
||||
tpl_id = parsed.template_id;
|
||||
var post_request = {
|
||||
url: '/tiles/template/' + tpl_id + "/jsonp?callback=test%config=" + JSON.stringify('{color:blue}'),
|
||||
method: 'GET',
|
||||
headers: {host: 'localhost' }
|
||||
}
|
||||
var next = this;
|
||||
assert.response(server, post_request, {},
|
||||
function(res) { next(null, res); });
|
||||
},
|
||||
function instanciateAuth(err, res)
|
||||
{
|
||||
if ( err ) throw err;
|
||||
assert.equal(res.statusCode, 200,
|
||||
'Unexpected success instanciating template with no auth: '
|
||||
+ res.statusCode + ': ' + res.body);
|
||||
return null;
|
||||
},
|
||||
function finish(err) {
|
||||
done(err);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test("template instantiation raises mapviews counter", function(done) {
|
||||
var layergroup = {
|
||||
stat_tag: 'random_tag',
|
||||
@@ -911,6 +1657,147 @@ suite('template_api', function() {
|
||||
);
|
||||
});
|
||||
|
||||
test("instance map token changes with templates certificate changes", function(done) {
|
||||
|
||||
// This map fetches data from a private table
|
||||
var template_acceptance2 = {
|
||||
version: '0.0.1',
|
||||
name: 'acceptance2',
|
||||
auth: { method: 'token', valid_tokens: ['valid1','valid2'] },
|
||||
layergroup: {
|
||||
version: '1.0.0',
|
||||
layers: [
|
||||
{ options: {
|
||||
sql: "select * from test_table_private_1 LIMIT 0",
|
||||
cartocss: '#layer { marker-fill:blue; marker-allow-overlap:true; }',
|
||||
cartocss_version: '2.0.2',
|
||||
interactivity: 'cartodb_id'
|
||||
} }
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
var template_params = {};
|
||||
|
||||
var errors = [];
|
||||
var expected_failure = false;
|
||||
var tpl_id;
|
||||
var layergroupid;
|
||||
Step(
|
||||
function postTemplate(err, res)
|
||||
{
|
||||
var next = this;
|
||||
var post_request = {
|
||||
url: '/tiles/template?api_key=1234',
|
||||
method: 'POST',
|
||||
headers: {host: 'localhost', 'Content-Type': 'application/json' },
|
||||
data: JSON.stringify(template_acceptance2)
|
||||
}
|
||||
assert.response(server, post_request, {},
|
||||
function(res) { next(null, res); });
|
||||
},
|
||||
function instance1(err, res)
|
||||
{
|
||||
if ( err ) throw err;
|
||||
assert.equal(res.statusCode, 200, res.body);
|
||||
var parsed = JSON.parse(res.body);
|
||||
assert.ok(parsed.hasOwnProperty('template_id'),
|
||||
"Missing 'template_id' from response body: " + res.body);
|
||||
tpl_id = parsed.template_id;
|
||||
var post_request = {
|
||||
url: '/tiles/template/' + tpl_id + '?auth_token=valid2',
|
||||
method: 'POST',
|
||||
headers: {host: 'localhost', 'Content-Type': 'application/json' },
|
||||
data: JSON.stringify(template_params)
|
||||
}
|
||||
var next = this;
|
||||
assert.response(server, post_request, {},
|
||||
function(res, err) { next(err, res); });
|
||||
},
|
||||
function checkInstance1(err, res) {
|
||||
if ( err ) throw err;
|
||||
assert.equal(res.statusCode, 200,
|
||||
'Instantiating template: ' + res.statusCode + ': ' + res.body);
|
||||
var parsed = JSON.parse(res.body);
|
||||
assert.ok(parsed.hasOwnProperty('layergroupid'),
|
||||
"Missing 'layergroupid' from response body: " + res.body);
|
||||
layergroupid = parsed.layergroupid;
|
||||
return null;
|
||||
},
|
||||
function updateTemplate(err, res)
|
||||
{
|
||||
if ( err ) throw err;
|
||||
// clone the valid one and rename it
|
||||
var changedTemplate = JSON.parse(JSON.stringify(template_acceptance2));
|
||||
changedTemplate.auth.method = 'open';
|
||||
var post_request = {
|
||||
url: '/tiles/template/' + tpl_id + '/?api_key=1234',
|
||||
method: 'PUT',
|
||||
headers: {host: 'localhost', 'Content-Type': 'application/json' },
|
||||
data: JSON.stringify(changedTemplate)
|
||||
}
|
||||
var next = this;
|
||||
assert.response(server, post_request, {},
|
||||
function(res) { next(null, res); });
|
||||
},
|
||||
function instance2(err, res)
|
||||
{
|
||||
if ( err ) throw err;
|
||||
assert.equal(res.statusCode, 200, res.body);
|
||||
var parsed = JSON.parse(res.body);
|
||||
assert.ok(parsed.hasOwnProperty('template_id'),
|
||||
"Missing 'template_id' from response body: " + res.body);
|
||||
assert.equal(tpl_id, parsed.template_id);
|
||||
var post_request = {
|
||||
url: '/tiles/template/' + tpl_id + '?auth_token=valid2',
|
||||
method: 'POST',
|
||||
headers: {host: 'localhost', 'Content-Type': 'application/json' },
|
||||
data: JSON.stringify(template_params)
|
||||
}
|
||||
var next = this;
|
||||
assert.response(server, post_request, {},
|
||||
function(res, err) { next(err, res); });
|
||||
},
|
||||
function checkInstance2(err, res) {
|
||||
if ( err ) throw err;
|
||||
assert.equal(res.statusCode, 200,
|
||||
'Instantiating template: ' + res.statusCode + ': ' + res.body);
|
||||
var parsed = JSON.parse(res.body);
|
||||
assert.ok(parsed.hasOwnProperty('layergroupid'),
|
||||
"Missing 'layergroupid' from response body: " + res.body);
|
||||
assert.ok(layergroupid != parsed.layergroupid);
|
||||
return null;
|
||||
},
|
||||
function finish(err) {
|
||||
if ( err ) errors.push(err);
|
||||
redis_client.keys("map_*|localhost", function(err, keys) {
|
||||
if ( err ) errors.push(err.message);
|
||||
var todrop = _.map(keys, function(m) {
|
||||
if ( m.match(/^map_(tpl|crt)|/) )
|
||||
return m;
|
||||
});
|
||||
if ( todrop.length != 2 ) {
|
||||
errors.push(new Error("Unexpected keys in redis: " + todrop));
|
||||
} else {
|
||||
if ( todrop.indexOf('map_tpl|localhost') == -1 ) {
|
||||
errors.push(new Error("Missing 'map_tpl|localhost' key in redis"));
|
||||
}
|
||||
if ( todrop.indexOf('map_crt|localhost') == -1 ) {
|
||||
errors.push(new Error("Missing 'map_crt|localhost' key in redis"));
|
||||
}
|
||||
}
|
||||
redis_client.del(todrop, function(err) {
|
||||
if ( err ) errors.push(err.message);
|
||||
if ( errors.length ) {
|
||||
done(new Error(errors));
|
||||
}
|
||||
else done(null);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
suiteTeardown(function(done) {
|
||||
|
||||
// This test will add map_style records, like
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
var http = require('http');
|
||||
var url = require('url');
|
||||
var _ = require('underscore');
|
||||
|
||||
var o = function(port, cb) {
|
||||
|
||||
this.queries = [];
|
||||
var that = this;
|
||||
this.requests = [];
|
||||
|
||||
this.sqlapi_server = http.createServer(function(req,res) {
|
||||
//console.log("server got request with method " + req.method);
|
||||
var query;
|
||||
|
||||
that.requests.push(req);
|
||||
|
||||
if ( req.method == 'GET' ) {
|
||||
query = url.parse(req.url, true).query;
|
||||
that.handleQuery(query, res);
|
||||
@@ -22,7 +27,6 @@ var o = function(port, cb) {
|
||||
req.on('end', function() {
|
||||
//console.log("Data is: "); console.dir(data);
|
||||
query = JSON.parse(data);
|
||||
//console.log("Parsed is: "); console.dir(query);
|
||||
//console.log("handleQuery is " + that.handleQuery);
|
||||
that.handleQuery(query, res);
|
||||
});
|
||||
@@ -45,15 +49,20 @@ o.prototype.handleQuery = function(query, res) {
|
||||
};
|
||||
res.write(JSON.stringify({rows: [ row ]}));
|
||||
} else {
|
||||
var qs = JSON.stringify(query);
|
||||
var row = {
|
||||
// This is the structure of the known query sent by tiler
|
||||
'cdb_querytables': '{' + qs + '}',
|
||||
'max': qs
|
||||
};
|
||||
var out_obj = {rows: [ row ]};
|
||||
var out = JSON.stringify(out_obj);
|
||||
res.write(out);
|
||||
if ( query.q.match('_private_') && query.api_key === undefined) {
|
||||
res.statusCode = 403;
|
||||
res.write(JSON.stringify({'error':'forbidden: ' + JSON.stringify(query)}));
|
||||
} else {
|
||||
var qs = JSON.stringify(query);
|
||||
var row = {
|
||||
// This is the structure of the known query sent by tiler
|
||||
'cdb_querytables': '{' + qs + '}',
|
||||
'max': qs
|
||||
};
|
||||
var out_obj = {rows: [ row ]};
|
||||
var out = JSON.stringify(out_obj);
|
||||
res.write(out);
|
||||
}
|
||||
}
|
||||
res.end();
|
||||
};
|
||||
@@ -63,5 +72,9 @@ o.prototype.close = function(cb) {
|
||||
this.sqlapi_server.close(cb);
|
||||
};
|
||||
|
||||
o.prototype.getLastRequest = function() {
|
||||
return this.requests.pop();
|
||||
};
|
||||
|
||||
module.exports = o;
|
||||
|
||||
|
||||
@@ -174,5 +174,6 @@ CREATE TABLE test_table_private_1 (
|
||||
CONSTRAINT enforce_srid_the_geom CHECK ((st_srid(the_geom) = 4326)),
|
||||
CONSTRAINT enforce_srid_the_geom_webmercator CHECK ((st_srid(the_geom_webmercator) = 3857))
|
||||
);
|
||||
INSERT INTO test_table_private_1 SELECT * from test_table;
|
||||
|
||||
GRANT ALL ON TABLE test_table_private_1 TO :TESTUSER;
|
||||
|
||||
@@ -21,7 +21,7 @@ suite('req2params', function() {
|
||||
assert.ok(req.hasOwnProperty('params'), 'request has params');
|
||||
assert.ok(req.params.hasOwnProperty('interactivity'), 'request params have interactivity');
|
||||
assert.equal(req.params.dbname, 'test_cartodb_user_1_db', 'could forge dbname: '+ req.params.dbname);
|
||||
assert.ok(!req.params.hasOwnProperty('dbuser'), 'could inject dbuser ('+req.params.dbuser+')');
|
||||
assert.ok(req.params.dbuser === 'testpublicuser', 'could inject dbuser ('+req.params.dbuser+')');
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -37,7 +37,7 @@ suite('req2params', function() {
|
||||
// database_name for user "localhost" (see test/support/prepare_db.sh)
|
||||
assert.equal(req.params.dbname, 'test_cartodb_user_1_db');
|
||||
// unauthenticated request gets no dbuser
|
||||
assert.ok(!req.params.hasOwnProperty('dbuser'), 'could inject dbuser ('+req.params.dbuser+')');
|
||||
assert.ok(req.params.dbuser === 'testpublicuser', 'could inject dbuser ('+req.params.dbuser+')');
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -57,7 +57,7 @@ suite('req2params', function() {
|
||||
|
||||
opts.req2params({headers: { host:'localhost' }, query: {map_key: '1235'} }, function(err, req) {
|
||||
// wrong key resets params to no user
|
||||
assert.ok(!req.params.hasOwnProperty('dbuser'), 'could inject dbuser ('+req.params.dbuser+')');
|
||||
assert.ok(req.params.dbuser === 'testpublicuser', 'could inject dbuser ('+req.params.dbuser+')');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -81,5 +81,29 @@ suite('signed_maps', function() {
|
||||
);
|
||||
});
|
||||
|
||||
test('can validate certificates', function(done) {
|
||||
var smap = new SignedMaps(redis_pool);
|
||||
assert.ok(smap);
|
||||
Step(
|
||||
function invalidVersion() {
|
||||
var cert = { version: '-1' };
|
||||
var err = smap.checkInvalidCertificate(cert);
|
||||
assert.ok(err);
|
||||
assert.equal(err.message, "Unsupported certificate version -1");
|
||||
return null;
|
||||
},
|
||||
function invalidTokenAuth() {
|
||||
var cert = { version: '0.0.1', auth: { method:'token', valid_token:[] } };
|
||||
var err = smap.checkInvalidCertificate(cert);
|
||||
assert.ok(err);
|
||||
assert.equal(err.message, "Invalid 'token' authentication: missing valid_tokens");
|
||||
return null;
|
||||
},
|
||||
function finish(err) {
|
||||
done(err);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
@@ -147,6 +147,28 @@ suite('template_maps', function() {
|
||||
});
|
||||
});
|
||||
|
||||
// See http://github.com/CartoDB/Windshaft-cartodb/issues/128
|
||||
test('does not accept template with invalid token auth (undefined tokens)',
|
||||
function(done) {
|
||||
var tmap = new TemplateMaps(redis_pool, signed_maps);
|
||||
assert.ok(tmap);
|
||||
var tpl = { version:'0.0.1',
|
||||
name: "invalid_auth1", placeholders: { },
|
||||
auth: { method: 'token' }, layergroup: {} };
|
||||
tmap.addTemplate('me', tpl, function(err) {
|
||||
if ( ! err ) {
|
||||
done(new Error("Unexpected success with invalid token auth (undefined tokens)"));
|
||||
}
|
||||
else if ( ! err.message.match(/invalid 'token' authentication/i) ) {
|
||||
done(new Error("Unexpected error message with invalid token auth (undefined tokens): "
|
||||
+ err));
|
||||
}
|
||||
else {
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('add, get and delete a valid template', function(done) {
|
||||
var tmap = new TemplateMaps(redis_pool, signed_maps);
|
||||
assert.ok(tmap);
|
||||
@@ -271,6 +293,7 @@ suite('template_maps', function() {
|
||||
assert.ok(err);
|
||||
tpl.name = 'first';
|
||||
tpl.auth.method = 'token';
|
||||
tpl.auth.valid_tokens = [ 'tok1' ];
|
||||
tmap.updTemplate(owner, tpl_id, tpl, this);
|
||||
},
|
||||
function updateTemplateWithInvalid(err) {
|
||||
@@ -314,17 +337,18 @@ suite('template_maps', function() {
|
||||
color: { type: "css_color", default: "#a0fF9A" },
|
||||
name: { type: "sql_literal", default: "test" },
|
||||
zoom: { type: "number", default: "0" },
|
||||
test_number: { type: "number", default: 23 },
|
||||
},
|
||||
layergroup: {
|
||||
version: '1.0.0',
|
||||
global_cartocss_version: '2.0.2',
|
||||
layers: [
|
||||
{ options: {
|
||||
sql: "select '<%= name %>' || id, g from t",
|
||||
cartocss: '#layer { marker-fill:<%= fill %>; }'
|
||||
sql: "select '<%=name %>' || id, g from t",
|
||||
cartocss: '#layer { marker-fill:<%= fill %>; marker-width: <%=test_number %>; }'
|
||||
} },
|
||||
{ options: {
|
||||
sql: "select fun('<%= name %>') g from x",
|
||||
sql: "select fun('<%= name%>') g from x",
|
||||
cartocss: '#layer { line-color:<%= color %>; marker-fill:<%= color %>; }'
|
||||
} },
|
||||
{ options: {
|
||||
@@ -339,7 +363,7 @@ suite('template_maps', function() {
|
||||
|
||||
var lyr = inst.layers[0].options;
|
||||
assert.equal(lyr.sql, "select 'test' || id, g from t");
|
||||
assert.equal(lyr.cartocss, '#layer { marker-fill:red; }');
|
||||
assert.equal(lyr.cartocss, '#layer { marker-fill:red; marker-width: 23; }');
|
||||
|
||||
lyr = inst.layers[1].options;
|
||||
assert.equal(lyr.sql, "select fun('test') g from x");
|
||||
@@ -349,7 +373,7 @@ suite('template_maps', function() {
|
||||
|
||||
lyr = inst.layers[0].options;
|
||||
assert.equal(lyr.sql, "select 'it''s dangerous' || id, g from t");
|
||||
assert.equal(lyr.cartocss, '#layer { marker-fill:red; }');
|
||||
assert.equal(lyr.cartocss, '#layer { marker-fill:red; marker-width: 23; }');
|
||||
|
||||
lyr = inst.layers[1].options;
|
||||
assert.equal(lyr.sql, "select fun('it''s dangerous') g from x");
|
||||
|
||||
222
tools/show_style
222
tools/show_style
@@ -1,85 +1,147 @@
|
||||
#!/bin/sh
|
||||
#!/usr/bin/env node
|
||||
|
||||
# TODO: port to node, if you really need it
|
||||
|
||||
ENV='development';
|
||||
BASEDIR=`cd $(dirname $0)/../; pwd`
|
||||
|
||||
if test -z "$1"; then
|
||||
echo "Usage: $0 [--env <environment>] <username> [<tablename>|~<token>]" >&2
|
||||
echo " environment defaults to 'development'"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
username=""
|
||||
token=""
|
||||
|
||||
while test -n "$1"; do
|
||||
if test "$1" = "--env"; then
|
||||
shift; ENV="$1"; shift
|
||||
elif test -z "$username"; then
|
||||
username="$1"; shift
|
||||
elif test -z "$token"; then
|
||||
token="$1"; shift
|
||||
else
|
||||
echo "Unused option $1" >&2
|
||||
shift
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Using environment '${ENV}'"
|
||||
|
||||
CONFIG="${BASEDIR}/config/environments/${ENV}.js"
|
||||
REDIS_PORT=`node -e "console.log(require('${CONFIG}').redis.port)"`
|
||||
if test $? -ne 0; then
|
||||
exit 1
|
||||
fi
|
||||
var path = require('path');
|
||||
var redis = require('redis');
|
||||
var Step = require('step');
|
||||
|
||||
|
||||
dbname=`redis-cli -p ${REDIS_PORT} -n 5 hget "rails:users:${username}" "database_name"`
|
||||
if test $? -ne 0; then
|
||||
exit 1
|
||||
fi
|
||||
if test -z "${dbname}"; then
|
||||
echo "Username ${username} unknown by redis on port ${REDIS_PORT} (try CARTODB/script/restore_redis?)" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "Database name for user ${username}: ${dbname}" # only if verbose?
|
||||
if test -n "$token"; then
|
||||
rec=`redis-cli get "map_style|${dbname}|${token}"`
|
||||
if test -z "${rec}"; then
|
||||
echo "${token}: no such map style known by redis on port ${REDIS_PORT}" >&2
|
||||
exit 1
|
||||
fi
|
||||
#echo "${rec}"
|
||||
escrec=`echo "${rec}" | sed -e 's/\\\\/\\\\\\\\/g'`
|
||||
#echo "${escrec}"
|
||||
node <<EOF
|
||||
var x=JSON.parse('${escrec}');
|
||||
console.log('style: ' + x.style);
|
||||
console.log('version: ' + x.version);
|
||||
|
||||
global.environment = require('${CONFIG}');
|
||||
var serverOptions = require('${BASEDIR}/lib/cartodb/server_options'); // _after_ setting global.environment
|
||||
var grainstore = require('${BASEDIR}/node_modules/windshaft/node_modules/grainstore/lib/grainstore');
|
||||
var mml_store = new grainstore.MMLStore(serverOptions.redis, serverOptions.grainstore);
|
||||
var builderconfig = {dbname:'${dbname}'};
|
||||
if ( '${token}'.match(/^~/) ) {
|
||||
builderconfig.token = '${token}'.substring(1);
|
||||
} else {
|
||||
builderconfig.table = '${token}';
|
||||
function usage(me, exitcode) {
|
||||
console.log("Usage: " + me + " [--env <environment>] <username> [<tablename>|~<token>]");
|
||||
process.exit(exitcode);
|
||||
}
|
||||
var mml_builder = mml_store.mml_builder(builderconfig,
|
||||
function(err, payload) {
|
||||
|
||||
var node_path = process.argv.shift();
|
||||
var script_path = process.argv.shift();
|
||||
var basedir = path.dirname(script_path);
|
||||
var me = path.basename(script_path);
|
||||
|
||||
var ENV = 'development.js';
|
||||
var username, token;
|
||||
var arg;
|
||||
while ( arg = process.argv.shift() ) {
|
||||
if ( arg == '--env' ) {
|
||||
ENV = process.argv.shift();
|
||||
}
|
||||
else if ( ! username ) {
|
||||
username = arg;
|
||||
}
|
||||
else if ( ! token ) {
|
||||
token = arg;
|
||||
}
|
||||
else {
|
||||
console.warn("Unused parameter " + arg);
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! username ) usage(me, 1);
|
||||
|
||||
console.log("Using environment " + ENV);
|
||||
|
||||
global.environment = require('../config/environments/' + ENV);
|
||||
var serverOptions = require('../lib/cartodb/server_options'); // _after_ setting global.environment
|
||||
|
||||
var client;
|
||||
var dbname;
|
||||
Step(
|
||||
function getClient() {
|
||||
client = redis.createClient(serverOptions.redis.port, serverOptions.redis.host);
|
||||
client.on('connect', this);
|
||||
},
|
||||
function getUserMeta(err) {
|
||||
if ( err ) throw err;
|
||||
client.select(5);
|
||||
client.hgetall('rails:users:' + username, this);
|
||||
},
|
||||
function readDB(err, data) {
|
||||
if ( err ) throw err;
|
||||
if ( ! data )
|
||||
throw new Error('Username ' + username + ' unknown by redis on port '
|
||||
+ serverOptions.redis.port + ' (try CARTODB/script/restore_redis?)');
|
||||
//console.log("Data:"); console.dir(data);
|
||||
dbname = data['database_name'];
|
||||
console.log("Database name for user " + username + ": " + dbname);
|
||||
client.select(0);
|
||||
return null;
|
||||
},
|
||||
function showTokens(err) {
|
||||
if ( err ) throw err;
|
||||
if ( token ) return null;
|
||||
var next = this;
|
||||
Step(
|
||||
function getTokens() {
|
||||
client.keys('map_style|' + dbname + '|*', this);
|
||||
},
|
||||
function showTokens(err, data) {
|
||||
if (err) throw err;
|
||||
if ( data ) console.log(data.join('\n'));
|
||||
return null;
|
||||
},
|
||||
function showTokensFinish(err) {
|
||||
next(err);
|
||||
}
|
||||
);
|
||||
},
|
||||
function showStyle(err) {
|
||||
if ( err ) throw err;
|
||||
if ( ! token ) return null;
|
||||
var next = this;
|
||||
Step(
|
||||
function getStyle() {
|
||||
client.get('map_style|' + dbname + '|' + token, this);
|
||||
},
|
||||
function showStyle(err, data) {
|
||||
if ( err ) throw err;
|
||||
mml_builder.toXML(function(err, xml) {
|
||||
if ( err ) throw err;
|
||||
console.log('- XML - ');
|
||||
console.log(xml);
|
||||
});
|
||||
});
|
||||
EOF
|
||||
#echo "${rec}" | sed -e 's/\\n/\n/g' -e 's/\\//g'
|
||||
else
|
||||
redis-cli keys "map_style|${dbname}|*"
|
||||
fi
|
||||
if ( ! data ) {
|
||||
throw new Error(token + ': no such map style known by redis on port '
|
||||
+ serverOptions.redis.port);
|
||||
}
|
||||
//console.log("data: " + data);
|
||||
var x=JSON.parse(data);
|
||||
printMapnikStyle(x, this);
|
||||
},
|
||||
function showStyleFinish(err) {
|
||||
next(err);
|
||||
}
|
||||
);
|
||||
},
|
||||
function finish(err) {
|
||||
if ( err ) {
|
||||
console.error(err.message)
|
||||
process.exit(1);
|
||||
}
|
||||
process.exit(0);
|
||||
}
|
||||
);
|
||||
|
||||
function printMapnikStyle(x, callback) {
|
||||
console.log('style: ' + x.style);
|
||||
console.log('version: ' + x.version);
|
||||
var grainstore = require(basedir + '/../node_modules/windshaft/node_modules/grainstore/lib/grainstore');
|
||||
var mml_store = new grainstore.MMLStore(serverOptions.redis, serverOptions.grainstore);
|
||||
var builderconfig = {dbname:dbname};
|
||||
if ( token.match(/^~/) ) {
|
||||
builderconfig.token = token.substring(1);
|
||||
} else {
|
||||
builderconfig.table = token;
|
||||
}
|
||||
var mml_builder;
|
||||
Step(
|
||||
function getBuilder() {
|
||||
mml_builder = mml_store.mml_builder(builderconfig, this);
|
||||
},
|
||||
function getXML(err, builder) {
|
||||
if ( err ) throw err;
|
||||
mml_builder.toXML(this);
|
||||
},
|
||||
function showXML(err, xml) {
|
||||
if ( err ) throw err;
|
||||
console.log('- XML - ');
|
||||
console.log(xml);
|
||||
return null;
|
||||
},
|
||||
function finish(err) {
|
||||
callback(err);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user