Compare commits

..

49 Commits
1.3.6 ... 1.5.2

Author SHA1 Message Date
Sandro Santilli
a660fdee12 Release 1.5.2 2013-12-05 14:05:41 +01:00
Sandro Santilli
e3b0813230 Fix use of old layergroups on mapnik upgrade (#97) 2013-12-05 14:02:16 +01:00
Sandro Santilli
8347dd4257 Set grainstore's GC run probability, for documentation purpose
It sets it to the current grainstore default, so nothing changes.
2013-12-05 11:11:13 +01:00
Sandro Santilli
dac89b30cb Allow requesting run_test.sh to prepare redis but not postgresql
Adds --nocreate-pg, --nocreate-redis, --nodrop-pg, --nodrop-redis
NOTE that dropping pg is still unimplemented
2013-12-05 11:07:12 +01:00
Sandro Santilli
5951608d2e Add backward-compatibility fix item in NEWS (#96) 2013-11-29 13:33:53 +01:00
Sandro Santilli
13c39f598b Prepare for 1.5.2 2013-11-29 13:32:28 +01:00
Sandro Santilli
6ca3135a3c Revert "fixed #91" -- the fix was for an unconfirmed bug
This reverts commit 9155724082.
See #38 for further action
2013-11-29 13:25:10 +01:00
Sandro Santilli
f582b04b23 Enable test for fetcing tiles of private tables using api_key
See #39 and #91
2013-11-29 13:20:21 +01:00
Sandro Santilli
8a1539af7f Add test for fetching tile of private table showing api_key
See #38 and #91
2013-11-29 13:14:43 +01:00
javi
9155724082 fixed #91 2013-11-29 12:46:45 +01:00
javi
75b0ab5c87 fixed #96 2013-11-29 12:46:25 +01:00
Sandro Santilli
046c570f5a Release 1.5.1 2013-11-28 18:52:11 +01:00
Sandro Santilli
f10d7cd93f Accept unused CartoCSS directives
Closes #93

An example unused CartoCSS directive is
"point-transform" without "point-file"
or "point-url". Unused means it has no effect.

It used to be accepted but regressed in release 1.5.0
2013-11-28 18:47:45 +01:00
Sandro Santilli
364c6e4910 Fix test for invalid font usage after Windshaft update (#90)
NOTE: the error is less friendly now, see
      http://github.com/mapbox/carto/issues/242
2013-11-28 18:41:45 +01:00
Sandro Santilli
c67199b3eb Survive presence of malformed CartoCSS in redis
Closes #94, enable relative testcase
2013-11-28 18:31:37 +01:00
Sandro Santilli
87a9e0ca79 Add package keywords 2013-11-28 11:17:00 +01:00
Sandro Santilli
87e18da45b Set test redis port to 6335 2013-11-27 12:16:43 +01:00
Sandro Santilli
1712829808 Add (pending) test for getting unrenderable stored styles (#94)
Required upgrading mocha tester to ~0.14.0
2013-11-27 09:47:33 +01:00
Sandro Santilli
f1f9f5f233 Notify travis builds on #cartodb @ freenode.irc 2013-11-26 18:11:49 +01:00
Sandro Santilli
19512b776f Use a variable to hold the name of test database 2013-11-26 11:27:38 +01:00
Sandro Santilli
82956e0b7c Reduce ppa and explicit package usage
Should fix travis builds despite package compatibilit bugs
(https://travis-ci.org/CartoDB/Windshaft-cartodb/builds/14314805)
2013-11-21 18:17:58 +01:00
Sandro Santilli
0a30a6cd56 Add note about new directives in the 1.5.0 section 2013-11-21 16:00:47 +01:00
Sandro Santilli
a9151b856c Improve documentation for postgres_auth_* configuration directives 2013-11-21 15:55:20 +01:00
Sandro Santilli
d864441f74 Prepare for 1.5.1 2013-11-19 15:45:33 +01:00
Sandro Santilli
6b39864a1b Release 1.5.0 2013-11-19 15:43:11 +01:00
Sandro Santilli
dcc75dbf5f Drop unneeded include from outermost app 2013-11-18 10:07:28 +01:00
Sandro Santilli
4c16087ea8 Update to cartodb-redis 0.1.0 2013-11-15 19:14:00 +01:00
Sandro Santilli
d61c373fad CartoDB redis interaction delegated to "cartodb-redis" module 2013-11-15 15:49:04 +01:00
Sandro Santilli
2e4526ce8e Require windshaft-0.14.3 to get 3 new bugfixes:
- Return CORS headers when creating layergroups via GET
 - Fix http status on database authentication error
 - Ensure bogus text-face-name error raises at layergroup creation
2013-11-14 16:26:05 +01:00
Sandro Santilli
f1045078e1 Remember per-environment ./configure parameters
This is to avoid breaking test.js configuration while switching
between branches.
2013-11-14 16:21:40 +01:00
Sandro Santilli
ab3e76ce36 Fix ticket reference 2013-11-13 11:24:07 +01:00
Sandro Santilli
88daefe353 Add support for reading user-specific database_password from redis
This commits adds support for CartoDB-2.5.0 model.
Closes #89.
Change is backward compatible.
2013-11-12 23:20:50 +01:00
Sandro Santilli
bcb6807306 Avoid caches during test for user-specific database_host 2013-11-12 22:38:42 +01:00
Sandro Santilli
2897c0c8bf Do not force ending dot in SQL-API hostname, for easier testing 2013-11-12 17:41:10 +01:00
Sandro Santilli
eb9dfed731 Improve tests robustness on failure 2013-11-12 17:29:57 +01:00
Sandro Santilli
6567ea5090 Add NEWS item about CartoDB-2.5.0+ user-specific database_host (#88) 2013-11-11 17:49:57 +01:00
Sandro Santilli
b428a7be9a Add test for redis-specifid database_host. Closes #88 2013-11-11 17:49:05 +01:00
Sandro Santilli
759ebadfc8 Read user's database_host from redis, when available (#88)
Still lacks a testcase
2013-11-11 17:23:10 +01:00
Sandro Santilli
69f15a5086 Indent fixes 2013-11-11 16:55:51 +01:00
Sandro Santilli
4b12295862 Tweak error messages on missing redis variables, update tests 2013-11-11 15:56:58 +01:00
Luis Bosque
0508203ba3 Add function to read database host from redis 2013-11-11 15:47:13 +01:00
Sandro Santilli
4eb3368c99 Style only change 2013-11-11 15:45:34 +01:00
Sandro Santilli
357702d61b Add support for specifying database connection passwords 2013-11-11 00:50:03 +01:00
Sandro Santilli
4baff6e018 Release 1.14.1 2013-11-08 12:43:47 +01:00
Sandro Santilli
5b4184c2db Fix support for exponential notation in CartoCSS filter values
Closes #87.
Includes testcase
2013-11-08 12:34:34 +01:00
Sandro Santilli
895aa3b977 Prepare for 1.4.1 2013-10-31 16:02:40 +01:00
Sandro Santilli
d9d2adf5d8 Add Support for Mapnik-2.2.0. Closes #78. 2013-10-31 15:54:15 +01:00
Sandro Santilli
9b18ad2637 Prepare for mapnik-2.2.0 support (#78)
- Tolerate change in CartoCSS error message between 0.9.3 and 0.9.5
- Expect default style to be different for mapnik-2.2.0+ target
2013-10-29 20:45:15 +01:00
Sandro Santilli
78ba279eb8 Prepare for 1.3.7 2013-10-11 10:06:38 +02:00
23 changed files with 803 additions and 580 deletions

1
.gitignore vendored
View File

@@ -1,4 +1,5 @@
node_modules*
config.status*
config/environments/*.js
.idea
tools/munin/windshaft.conf

View File

@@ -1,8 +1,7 @@
before_install:
- sudo apt-add-repository --yes ppa:mapnik/v2.1.0
- sudo apt-add-repository --yes ppa:ubuntugis/ppa
- sudo apt-get update -q
- sudo apt-get install -q libmapnik libmapnik-dev postgresql-9.1-postgis libsigc++-dev
- sudo apt-get install -q libmapnik-dev
- createdb template_postgis
- psql -c "CREATE EXTENSION postgis" template_postgis
@@ -12,3 +11,9 @@ env:
language: node_js
node_js:
- "0.8"
notifications:
irc:
channels:
- "irc.freenode.org#cartodb"
use_notice: true

View File

@@ -6,12 +6,17 @@ all:
clean:
rm -rf node_modules/*
config/environments/test.js: config/environments/test.js.example Makefile
distclean: clean
rm config.status*
config.status--test:
./configure --environment=test
config/environments/test.js: config.status--test
./config.status--test
check-local: config/environments/test.js
./run_tests.sh ${RUNTESTFLAGS} \
test/unit/cartodb/redis_pool.test.js \
test/unit/cartodb/req2params.test.js \
test/acceptance/cache_validator.js \
test/acceptance/server.js \

51
NEWS.md
View File

@@ -1,3 +1,54 @@
1.5.2 -- 2013-12-05
-------------------
Bug fixes:
* Fix configuration-level compatibility with versions prior to 1.5 (#96)
* Fix use of old layergroups on mapnik upgrade (#97)
1.5.1 -- 2013-11-28
-------------------
Bug fixes:
* Survive presence of malformed CartoCSS in redis (#94)
* Accept useless point-transform:scale directives (#93)
1.5.0 -- 2013-11-19
-------------------
NOTE: new configuration directives `postgres_auth_pass` and
`postgres.password` added; see config/environments/*.example
for documentation.
Improvements:
* Add support for configuring database connection passwords
* Optionally read user-specific database_host and database_password
from redis as per CartoDB-2.5.0 model (#88, #89)
* Do not force ending dot in SQL-API hostname, for easier testing
Bug fixes:
* Return CORS headers when creating layergroups via GET (windshaft/#92)
* Fix http status on database authentication error (windshaft/#94)
* Fix text-face-name error at layergroup creation (windshaft/#93)
Other changes:
* CartoDB redis interaction delegated to "cartodb-redis" module
1.4.1 -- 2013-11-08
-------------------
* Fix support for exponential notation in CartoCSS filter values (#87)
1.4.0 -- 2013-10-31
-------------------
* Add Support for Mapnik-2.2.0 (#78)
1.3.6 -- 2013-10-11
-------------------

4
app.js
View File

@@ -25,10 +25,6 @@ global.settings = require(__dirname + '/config/settings');
global.environment = require(__dirname + '/config/environments/' + ENV);
_.extend(global.settings, global.environment);
// Include cart_data.js only _after_ the "global" variable is set
// See https://github.com/Vizzuality/Windshaft-cartodb/issues/28
var cartoData = require('./lib/cartodb/carto_data');
var Windshaft = require('windshaft');
var serverOptions = require('./lib/cartodb/server_options');

View File

@@ -10,12 +10,18 @@ var config = {
,enable_cors: true
,cache_enabled: false
,log_format: '[:date] :req[X-Real-IP] :method :req[Host]:url :status :response-time ms -> :res[Content-Type] (:res[X-Tiler-Profiler])'
// Templated database username for authorized user
// Supported labels: 'user_id' (read from redis)
,postgres_auth_user: 'development_cartodb_user_<%= user_id %>'
// Templated database password for authorized user
// Supported labels: 'user_id', 'user_password' (both read from redis)
,postgres_auth_pass: '<%= user_password %>'
,postgres: {
// Parameters to pass to datasource plugin of mapnik
// See http://github.com/mapnik/mapnik/wiki/PostGIS
type: "postgis",
user: "publicuser",
password: "public",
host: '127.0.0.1',
port: 5432,
extent: "-20037508.3,-20037508.3,20037508.3,20037508.3",

View File

@@ -10,11 +10,17 @@ var config = {
,enable_cors: true
,cache_enabled: true
,log_format: '[:date] :req[X-Real-IP] :method :req[Host]:url :status :response-time ms -> :res[Content-Type] (:res[X-Tiler-Profiler])'
// Templated database username for authorized user
// Supported labels: 'user_id' (read from redis)
,postgres_auth_user: 'cartodb_user_<%= user_id %>'
// Templated database password for authorized user
// Supported labels: 'user_id', 'user_password' (both read from redis)
,postgres_auth_pass: '<%= user_password %>'
,postgres: {
// Parameters to pass to datasource plugin of mapnik
// See http://github.com/mapnik/mapnik/wiki/PostGIS
user: "publicuser",
password: "public",
host: '127.0.0.1',
port: 6432,
extent: "-20037508.3,-20037508.3,20037508.3,20037508.3",

View File

@@ -10,11 +10,17 @@ var config = {
,enable_cors: true
,cache_enabled: true
,log_format: '[:date] :req[X-Real-IP] :method :req[Host]:url :status :response-time ms (:res[X-Tiler-Profiler]) -> :res[Content-Type]'
// Templated database username for authorized user
// Supported labels: 'user_id' (read from redis)
,postgres_auth_user: 'cartodb_staging_user_<%= user_id %>'
// Templated database password for authorized user
// Supported labels: 'user_id', 'user_password' (both read from redis)
,postgres_auth_pass: '<%= user_password %>'
,postgres: {
// Parameters to pass to datasource plugin of mapnik
// See http://github.com/mapnik/mapnik/wiki/PostGIS
user: "publicuser",
password: "public",
host: '127.0.0.1',
port: 6432,
extent: "-20037508.3,-20037508.3,20037508.3,20037508.3",

View File

@@ -10,11 +10,17 @@ var config = {
,enable_cors: true
,cache_enabled: false
,log_format: '[:date] :req[X-Real-IP] :method :req[Host]:url :status :response-time ms -> :res[Content-Type] (:res[X-Tiler-Profiler])'
// Templated database username for authorized user
// Supported labels: 'user_id' (read from redis)
,postgres_auth_user: 'test_cartodb_user_<%= user_id %>'
// Templated database password for authorized user
// Supported labels: 'user_id', 'user_password' (both read from redis)
,postgres_auth_pass: 'test_cartodb_user_<%= user_id %>_pass'
,postgres: {
// Parameters to pass to datasource plugin of mapnik
// See http://github.com/mapnik/mapnik/wiki/PostGIS
user: "publicuser",
user: "testpublicuser",
password: "public",
host: '127.0.0.1',
port: 5432,
extent: "-20037508.3,-20037508.3,20037508.3,20037508.3",
@@ -35,7 +41,7 @@ var config = {
}
,redis: {
host: '127.0.0.1',
port: 6333,
port: 6335,
// Max number of connections in each pool.
// Users will be put on a queue when the limit is hit.
// Set to maxConnection to have no possible queues.

32
configure vendored
View File

@@ -17,11 +17,15 @@
# --strk(2012-07-23)
#
PGPORT=5432
SQLAPI_PORT=8080
ENVDIR=config/environments
PGPORT=
SQLAPI_PORT=
MAPNIK_VERSION=
ENVIRONMENT=development
STATUS="$0 $*"
usage() {
echo "Usage: $0 [OPTION]"
echo
@@ -59,20 +63,30 @@ while test -n "$1"; do
shift
done
ENVEX=./${ENVDIR}/${ENVIRONMENT}.js.example
if [ -z "$PGPORT" ]; then
PGPORT=`node -e "console.log(require('${ENVEX}').postgres.port)"`
fi
if [ -z "$SQLAPI_PORT" ]; then
SQLAPI_PORT=`node -e "console.log(require('${ENVEX}').sqlapi.port)"`
fi
echo "PGPORT: $PGPORT"
echo "SQLAPI_PORT: $SQLAPI_PORT"
echo "MAPNIK_VERSION: $MAPNIK_VERSION"
echo "ENVIRONMENT: $ENVIRONMENT"
# TODO: allow specifying configuration settings !
for f in config/environments/${ENVIRONMENT}.js.example; do
o=`dirname "$f"`/`basename "$f" .example`
echo "Writing $o"
o=`dirname "${ENVEX}"`/`basename "${ENVEX}" .example`
echo "Writing $o"
# See http://austinmatzko.com/2008/04/26/sed-multi-line-search-and-replace/
sed -n "1h;1!H;\${;g;s/\(,postgres: {[^}]*port: *'\?\)[^',]*\('\?,\)/\1$PGPORT\2/;p;}" < "$f" \
# See http://austinmatzko.com/2008/04/26/sed-multi-line-search-and-replace/
sed -n "1h;1!H;\${;g;s/\(,postgres: {[^}]*port: *'\?\)[^',]*\('\?,\)/\1$PGPORT\2/;p;}" < "${ENVEX}" \
| sed "s/mapnik_version:.*/mapnik_version: '$MAPNIK_VERSION'/" \
| sed -n "1h;1!H;\${;g;s/\(,sqlapi: {[^}]*port: *'\?\)[^',]*\('\?,\)/\1$SQLAPI_PORT\2/;p;}" \
> "$o"
done
STATUSFILE=config.status--${ENVIRONMENT}
echo "Writing ${STATUSFILE}"
echo ${STATUS} > ${STATUSFILE} && chmod +x ${STATUSFILE}

View File

@@ -1,279 +0,0 @@
/**
* User: simon
* Date: 30/08/2011
* Time: 21:10
* Desc: CartoDB helper.
* Retrieves dbname (based on subdomain/username)
* and geometry type from the redis stores of cartodb
*/
var strftime = require('strftime');
var RedisPool = require("./redis_pool")
, _ = require('underscore')
, Step = require('step');
module.exports = function() {
var redis_pool = new RedisPool(global.environment.redis);
var me = {
user_metadata_db: 5,
table_metadata_db: 0,
user_key: "rails:users:<%= username %>",
table_key: "rails:<%= database_name %>:<%= table_name %>",
global_mapview_key: "user:<%= username %>:mapviews:global",
tagged_mapview_key: "user:<%= username %>:mapviews:stat_tag:<%= stat_tag %>"
};
/**
* Get the database name for this particular subdomain/username
*
* @param req - standard express req object. importantly contains host information
* @param callback - gets called with args(err, dbname)
*/
me.getDatabase = function(req, callback) {
// strip subdomain from header host
var username = req.headers.host.split('.')[0]
var redisKey = _.template(this.user_key, {username: username});
this.retrieve(this.user_metadata_db, redisKey, 'database_name', function(err, dbname) {
if ( err ) callback(err, null);
else if ( dbname === null ) {
callback(new Error("missing " + username + "'s dbname in redis (try CARTODB/script/restore_redis)"), null);
}
else callback(err, dbname);
});
};
me.userFromHostname = function(hostname) {
return hostname.split('.')[0];
}
/**
* Increment mapview count for a user
*
* @param username
* @param stat_tag
* @param callback will be called with the new value
*/
me.incMapviewCount = function(username, stat_tag, callback) {
var that = this;
var now = strftime("%Y%m%d", new Date());
var redisKey;
Step (
function incrementGlobal() {
redisKey = _.template(that.global_mapview_key, {username: username});
that.redisCmd(me.user_metadata_db, 'ZINCRBY', [redisKey, 1, now], this);
},
function incrementTag(err, val) {
if ( err ) throw err;
if ( _.isUndefined(stat_tag) ) return 1;
redisKey = _.template(that.tagged_mapview_key, {username: username, stat_tag: stat_tag});
that.redisCmd(me.user_metadata_db, 'ZINCRBY', [redisKey, 1, now], this);
},
function finish(err, val) {
if ( callback ) callback(err);
}
);
};
/**
* Get the user id for this particular subdomain/username
*
* @param req - standard express req object. importantly contains host information
* @param callback
*/
me.getId= function(req, callback) {
// strip subdomain from header host
var username = req.headers.host.split('.')[0];
var redisKey = _.template(this.user_key, {username: username});
this.retrieve(this.user_metadata_db, redisKey, 'id', function(err, dbname) {
if ( err ) callback(err, null);
else if ( dbname === null ) {
callback(new Error("missing " + username + "'s dbuser in redis (try CARTODB/script/restore_redis)"), null);
}
else callback(err, dbname);
});
};
/**
* Check the user map key for this particular subdomain/username
*
* @param req - standard express req object. importantly contains host information
* @param callback
*/
me.checkMapKey = function(req, callback) {
// strip subdomain from header host
var username = req.headers.host.split('.')[0];
var redisKey = "rails:users:" + username;
this.retrieve(this.user_metadata_db, redisKey, "map_key", function(err, val) {
var valid = 0;
if ( val ) {
if ( val == req.query.map_key ) valid = 1;
else if ( val == req.query.api_key ) valid = 1;
// check also in request body
else if ( req.body && req.body.map_key && val == req.body.map_key ) valid = 1;
else if ( req.body && req.body.api_key && val == req.body.api_key ) valid = 1;
}
callback(err, valid);
});
};
/**
* Get privacy for cartodb table
*
* @param req - standard req object. Importantly contains table and host information
* @param callback - is the table private or not?
*/
me.authorize= function(req, callback) {
var that = this;
Step(
function(){
that.checkMapKey(req, this);
},
function checkIfInternal(err, check_result){
if (err) throw err;
if (check_result === 1) {
// authorized by key, login as db owner
that.getId(req, function(err, user_id) {
if (err) throw err;
var dbuser = _.template(global.settings.postgres_auth_user, {user_id: user_id});
_.extend(req.params, {dbuser:dbuser});
callback(err, true);
});
} else {
return true; // continue to check if the table is public/private
}
}
,function (err, data){
if (err) throw err;
that.getDatabase(req, this);
},
function(err, data){
if (err) throw err;
var redisKey = _.template(that.table_key, {database_name: data, table_name: req.params.table});
that.retrieve(that.table_metadata_db, redisKey, 'privacy', this);
},
function(err, data){
callback(err, data);
}
);
};
/**
* Get the geometry type for this particular table;
* @param req - standard req object. Importantly contains table and host information
* @param callback
*/
me.getGeometryType = function(req, callback){
var that = this;
Step(
function(){
that.getDatabase(req, this)
},
function(err, data){
if (err) throw err;
var redisKey = _.template(that.table_key, {database_name: data, table_name: req.params.table});
that.retrieve(that.table_metadata_db, redisKey, 'the_geom_type', this);
},
function(err, data){
callback(err, data);
}
);
};
me.getInfowindow = function(req, callback){
var that = this;
Step(
function(){
that.getDatabase(req, this);
},
function(err, data) {
if (err) throw err;
var redisKey = _.template(that.table_key, {database_name: data, table_name: req.params.table});
that.retrieve(that.table_metadata_db, redisKey, 'infowindow', this);
},
function(err, data){
callback(err, data);
}
);
};
me.getMapMetadata = function(req, callback){
var that = this;
Step(
function(){
that.getDatabase(req, this);
},
function(err, data) {
if (err) throw err;
var redisKey = _.template(that.table_key, {database_name: data, table_name: req.params.table});
that.retrieve(that.table_metadata_db, redisKey, 'map_metadata', this);
},
function(err, data){
callback(err, data);
}
);
};
// Redis Hash lookup
// @param callback will be invoked with args (err, reply)
// note that reply is null when the key is missing
me.retrieve = function(db, redisKey, hashKey, callback) {
this.redisCmd(db,'HGET',[redisKey, hashKey], callback);
};
// Redis Set member check
me.inSet = function(db, setKey, member, callback) {
this.redisCmd(db,'SISMEMBER',[setKey, member], callback);
};
// Redis INCREMENT
me.increment = function(db, key, callback) {
this.redisCmd(db,'INCR', key, callback);
};
/**
* Use Redis
*
* @param db - redis database number
* @param redisFunc - the redis function to execute
* @param redisArgs - the arguments for the redis function in an array
* @param callback - function to pass results too.
*/
me.redisCmd = function(db, redisFunc, redisArgs, callback) {
var redisClient;
Step(
function getRedisClient() {
redis_pool.acquire(db, this);
},
function executeQuery(err, data) {
if ( err ) throw err;
redisClient = data;
redisArgs.push(this);
redisClient[redisFunc.toUpperCase()].apply(redisClient, redisArgs);
},
function releaseRedisClient(err, data) {
if ( ! _.isUndefined(redisClient) ) redis_pool.release(db, redisClient);
callback(err, data);
}
);
};
return me;
}();

View File

@@ -1,80 +0,0 @@
/**
* RedisPool. A database specific redis pooling lib
*
*/
var redis = require('redis')
, _ = require('underscore')
, Pool = require('generic-pool').Pool;
// constructor.
//
// - `opts` {Object} optional config for redis and pooling
var RedisPool = function(opts){
var opts = opts || {};
var defaults = {
host: '127.0.0.1',
port: '6379',
max: 50,
idleTimeoutMillis: 10000,
reapIntervalMillis: 1000,
log: false
};
var options = _.defaults(opts, defaults)
var me = {
pools: {} // cached pools by DB name
};
// Acquire resource.
//
// - `database` {String} redis database name
// - `callback` {Function} callback to call once acquired. Takes the form
// `callback(err, resource)`
me.acquire = function(database, callback) {
if (!this.pools[database]) {
this.pools[database] = this.makePool(database);
}
this.pools[database].acquire(function(err,resource) {
callback(err, resource);
});
};
// Release resource.
//
// - `database` {String} redis database name
// - `resource` {Object} resource object to release
me.release = function(database, resource) {
this.pools[database] && this.pools[database].release(resource);
};
// Factory for pool objects.
me.makePool = function(database) {
return Pool({
name: database,
create: function(callback){
var client = redis.createClient(options.port, options.host);
client.on('connect', function () {
client.send_anyway = true;
client.select(database);
client.send_anyway = false;
callback(null, client);
});
client.on('error', function (err) {
callback(err, null);
});
},
destroy: function(client) {
return client.quit();
},
max: options.max,
idleTimeoutMillis: options.idleTimeoutMillis,
reapIntervalMillis: options.reapIntervalMillis,
log: options.log
});
};
return me;
};
module.exports = RedisPool;

View File

@@ -1,6 +1,6 @@
var _ = require('underscore')
, Step = require('step')
, cartoData = require('./carto_data')
, cartoData = require('cartodb-redis')(global.environment.redis)
, Cache = require('./cache_validator')
, mapnik = require('mapnik')
, crypto = require('crypto')
@@ -28,7 +28,8 @@ module.exports = function(){
datasource: global.environment.postgres,
cachedir: global.environment.millstone.cache_basedir,
mapnik_version: global.environment.mapnik_version || mapnik.versions.mapnik,
default_layergroup_ttl: 7200 // seconds (defaultis 300)
default_layergroup_ttl: 7200, // seconds (default is 300)
gc_prob: 0.01 // default is 0.01 TODO: make configurable via env config
},
mapnik: {
metatile: rendererConfig.metatile,
@@ -67,7 +68,9 @@ module.exports = function(){
var api = global.environment.sqlapi;
// build up api string
var sqlapi = api.protocol + '://' + username + '.' + api.domain + ':' + api.port + '/api/' + api.version + '/sql'
var sqlapi = api.protocol + '://' + username;
if ( api.domain ) sqlapi += '.' + api.domain;
sqlapi += ':' + api.port + '/api/' + api.version + '/sql'
var qs = { q: sql }
@@ -302,6 +305,73 @@ module.exports = function(){
/* X-Cache-Channel generation } */
/**
* Get privacy for cartodb table
*
* @param req - standard req object. Importantly contains table and host information
* @param callback - is the table private or not?
*/
me.authorize= function(req, callback) {
var that = this;
Step(
function(){
cartoData.checkMapKey(req, this);
},
function checkIfInternal(err, check_result){
if (err) throw err;
// if unauthorized continue to check table privacy
if (check_result !== 1) return true;
// authorized by key, login as db owner
var user_params = {};
var auth_user = global.environment.postgres_auth_user;
var auth_pass = global.environment.postgres_auth_pass;
Step(
function getId() {
cartoData.getId(req, this);
},
function(err, user_id) {
if (err) throw err;
user_params['user_id'] = user_id;
var dbuser = _.template(auth_user, user_params);
_.extend(req.params, {dbuser:dbuser});
// skip looking up user_password if postgres_auth_pass
// doesn't contain the "user_password" label
if (!auth_pass || ! auth_pass.match(/\buser_password\b/) ) return null;
cartoData.getDatabasePassword(req, this);
},
function(err, user_password) {
if (err) throw err;
user_params['user_password'] = user_password;
if ( auth_pass ) {
var dbpass = _.template(auth_pass, user_params);
_.extend(req.params, {dbpassword:dbpass});
}
return true;
},
function finish(err) {
callback(err, true); // authorized (or error)
}
);
}
,function getDatabase(err, data){
if (err) throw err;
cartoData.getDatabase(req, this);
},
function getPrivacy(err, data){
if (err) throw err;
cartoData.getTablePrivacy(data, req.params.table, this);
},
function(err, data){
callback(err, data);
}
);
};
/**
* Whitelist input and get database name & default geometry type from
* subdomain/user metadata held in CartoDB Redis
@@ -359,9 +429,22 @@ module.exports = function(){
req.params.interactivity = req.params.interactivity || 'cartodb_id';
req.params.processXML = function(req, xml, callback) {
var dbuser = req.params.dbuser || global.settings.postgres.user;
// 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);
}
@@ -371,35 +454,42 @@ module.exports = function(){
Step(
function getPrivacy(){
cartoData.authorize(req, this);
me.authorize(req, this);
},
function gatekeep(err, data){
if (req.profiler) req.profiler.done('cartoData.authorize');
if (req.profiler) req.profiler.done('cartoData.authorize');
if(err) throw err;
if(data === "0") throw new Error("Sorry, you are unauthorized (permission denied)");
return data;
},
function getDatabase(err, data){
function getDatabaseHost(err, data){
if(err) throw err;
cartoData.getDatabaseHost(req, this);
},
function getDatabase(err, data){
if (req.profiler) req.profiler.done('cartoData.getDatabaseHost');
if(err) throw err;
if ( data ) _.extend(req.params, {dbhost:data});
cartoData.getDatabase(req, this);
},
function getGeometryType(err, data){
if (req.profiler) req.profiler.done('cartoData.getDatabase');
if (req.profiler) req.profiler.done('cartoData.getDatabase');
if (err) throw err;
_.extend(req.params, {dbname:data});
cartoData.getGeometryType(req, this);
},
function finishSetup(err, data){
if (req.profiler) req.profiler.done('cartoData.getGeometryType');
if (req.profiler) req.profiler.done('cartoData.getGeometryType');
if ( err ) { callback(err, req); return; }
if (!_.isNull(data))
_.extend(req.params, {geom_type: data});
that.addCacheChannel(req, function(err) {
if (req.profiler) req.profiler.done('addCacheChannel');
if (req.profiler) req.profiler.done('addCacheChannel');
callback(err, req);
});
}

132
npm-shrinkwrap.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "windshaft-cartodb",
"version": "1.3.6",
"version": "1.5.2",
"dependencies": {
"node-varnish": {
"version": "0.1.1"
@@ -9,34 +9,44 @@
"version": "1.3.3"
},
"windshaft": {
"version": "0.13.7",
"version": "0.14.5",
"dependencies": {
"grainstore": {
"version": "0.13.12",
"version": "0.15.2",
"dependencies": {
"carto": {
"version": "0.9.3-cdb6",
"from": "git://github.com/CartoDB/carto.git#0.9.3-cdb6",
"version": "0.9.5-cdb2",
"from": "git://github.com/CartoDB/carto.git#0.9.5-cdb2",
"dependencies": {
"mapnik-reference": {
"version": "5.0.0-cdb1",
"from": "git://github.com/CartoDB/mapnik-reference.git#cdb-5.0"
"underscore": {
"version": "1.4.4"
},
"xml2js": {
"version": "0.1.14",
"version": "0.2.8",
"dependencies": {
"sax": {
"version": "0.5.5"
}
}
},
"optimist": {
"version": "0.6.0",
"dependencies": {
"wordwrap": {
"version": "0.0.2"
},
"minimist": {
"version": "0.0.5"
}
}
}
}
},
"mapnik-reference": {
"version": "5.0.6"
"version": "5.0.7"
},
"millstone": {
"version": "0.6.4",
"version": "0.6.8",
"dependencies": {
"underscore": {
"version": "1.5.2"
@@ -45,7 +55,7 @@
"version": "2.26.0",
"dependencies": {
"qs": {
"version": "0.6.5"
"version": "0.6.6"
},
"json-stringify-safe": {
"version": "5.0.0"
@@ -118,25 +128,22 @@
}
},
"srs": {
"version": "0.3.2"
"version": "0.3.8"
},
"zipfile": {
"version": "0.4.1"
"version": "0.4.2"
},
"sqlite3": {
"version": "2.1.17",
"version": "2.1.19",
"dependencies": {
"progress": {
"version": "1.0.1"
},
"tar.gz": {
"version": "0.1.1",
"dependencies": {
"fstream": {
"version": "0.1.24",
"version": "0.1.25",
"dependencies": {
"rimraf": {
"version": "2.2.2"
"version": "2.2.4"
},
"graceful-fs": {
"version": "2.0.1"
@@ -190,6 +197,9 @@
}
}
},
"generic-pool": {
"version": "2.0.4"
},
"express": {
"version": "2.5.11",
"dependencies": {
@@ -229,8 +239,7 @@
}
},
"tilelive-mapnik": {
"version": "0.5.0",
"from": "git://github.com/Vizzuality/tilelive-mapnik.git#5908346",
"version": "0.6.4",
"dependencies": {
"eio": {
"version": "0.2.2"
@@ -251,11 +260,31 @@
"step": {
"version": "0.0.5"
},
"generic-pool": {
"version": "2.0.3"
"request": {
"version": "2.9.202"
},
"cartodb-redis": {
"version": "0.1.0",
"dependencies": {
"generic-pool": {
"version": "2.0.4"
}
}
},
"mapnik": {
"version": "0.7.25"
},
"lzma": {
"version": "1.2.3"
},
"semver": {
"version": "1.1.4"
},
"strftime": {
"version": "0.6.2"
},
"redis": {
"version": "0.8.3"
"version": "0.8.6"
},
"hiredis": {
"version": "0.1.15",
@@ -265,43 +294,56 @@
}
}
},
"request": {
"version": "2.9.202"
},
"mapnik": {
"version": "0.7.22"
},
"strftime": {
"version": "0.6.0"
},
"lzma": {
"version": "1.2.3"
},
"semver": {
"version": "1.1.4"
},
"mocha": {
"version": "1.2.1",
"version": "1.14.0",
"dependencies": {
"commander": {
"version": "0.6.1"
"version": "2.0.0"
},
"growl": {
"version": "1.5.1"
"version": "1.7.0"
},
"jade": {
"version": "0.26.3",
"dependencies": {
"commander": {
"version": "0.6.1"
},
"mkdirp": {
"version": "0.3.0"
}
}
},
"diff": {
"version": "1.0.2"
"version": "1.0.7"
},
"debug": {
"version": "0.7.2"
"version": "0.7.4"
},
"mkdirp": {
"version": "0.3.5"
},
"glob": {
"version": "3.2.3",
"dependencies": {
"minimatch": {
"version": "0.2.12",
"dependencies": {
"lru-cache": {
"version": "2.5.0"
},
"sigmund": {
"version": "1.0.0"
}
}
},
"graceful-fs": {
"version": "2.0.1"
},
"inherits": {
"version": "2.0.1"
}
}
}
}
}

View File

@@ -1,8 +1,11 @@
{
"private": true,
"name": "windshaft-cartodb",
"version": "1.3.6",
"version": "1.5.2",
"description": "A map tile server for CartoDB",
"keywords": [
"cartodb"
],
"url": "https://github.com/CartoDB/Windshaft-cartodb",
"licenses": [{
"type": "BSD",
@@ -21,18 +24,17 @@
"dependencies": {
"node-varnish": "0.1.1",
"underscore" : "~1.3.3",
"windshaft" : "~0.13.7",
"windshaft" : "~0.14.5",
"step": "0.0.x",
"generic-pool": "~2.0.3",
"redis": "~0.8.3",
"hiredis": "~0.1.14",
"request": "2.9.202",
"mapnik": "~0.7.14",
"strftime": "~0.6.0",
"cartodb-redis": "~0.1.0",
"mapnik": "~0.7.22",
"lzma": "~1.2.3"
},
"devDependencies": {
"mocha": "1.2.1",
"mocha": "1.14.0",
"redis": "~0.8.3",
"strftime": "~0.6.0",
"semver": "~1.1.0"
},
"scripts": {

View File

@@ -1,7 +1,9 @@
#!/bin/sh
OPT_CREATE=yes # create the test environment
OPT_DROP=yes # drop the test environment
OPT_CREATE_REDIS=yes # create the redis test environment
OPT_CREATE_PGSQL=yes # create the PostgreSQL test environment
OPT_DROP_REDIS=yes # drop the redis test environment
OPT_DROP_PGSQL=yes # drop the PostgreSQL test environment
cd $(dirname $0)
BASEDIR=$(pwd)
@@ -11,7 +13,7 @@ REDIS_PORT=`node -e "console.log(require('${BASEDIR}/config/environments/test.js
export REDIS_PORT
cleanup() {
if test x"$OPT_DROP" = xyes; then
if test x"$OPT_DROP_REDIS" = xyes; then
if test x"$PID_REDIS" = x; then
PID_REDIS=$(cat ${BASEDIR}/redis.pid)
if test x"$PID_REDIS" = x; then
@@ -19,9 +21,13 @@ cleanup() {
return;
fi
fi
echo "Cleaning up"
echo "Killing test redis pid ${PID_REDIS}"
kill ${PID_REDIS}
fi
if test x"$OPT_DROP_PGSQL" = xyes; then
# TODO: drop postgresql ?
echo "Dropping PostgreSQL test database isn't implemented yet"
fi
}
cleanup_and_exit() {
@@ -39,12 +45,32 @@ die() {
trap 'cleanup_and_exit' 1 2 3 5 9 13
while [ -n "$1" ]; do
# This is kept for backward compatibility
if test "$1" = "--nodrop"; then
OPT_DROP=no
OPT_DROP_REDIS=no
OPT_DROP_PGSQL=no
shift
continue
elif test "$1" = "--nodrop-pg"; then
OPT_DROP_PGSQL=no
shift
continue
elif test "$1" = "--nodrop-redis"; then
OPT_DROP_REDIS=no
shift
continue
elif test "$1" = "--nocreate-pg"; then
OPT_CREATE_PGSQL=no
shift
continue
elif test "$1" = "--nocreate-redis"; then
OPT_CREATE_REDIS=no
shift
continue
# This is kept for backward compatibility
elif test "$1" = "--nocreate"; then
OPT_CREATE=no
OPT_CREATE_REDIS=no
OPT_CREATE_PGSQL=no
shift
continue
else
@@ -62,16 +88,26 @@ fi
TESTS=$@
if test x"$OPT_CREATE" = xyes; then
if test x"$OPT_CREATE_REDIS" = xyes; then
echo "Starting redis on port ${REDIS_PORT}"
echo "port ${REDIS_PORT}" | redis-server - > ${BASEDIR}/test.log &
PID_REDIS=$!
echo ${PID_REDIS} > ${BASEDIR}/redis.pid
echo "Preparing the environment"
cd ${BASEDIR}/test/support; sh prepare_db.sh || die "database preparation failure"; cd -
fi
PREPARE_DB_OPTS=
if test x"$OPT_CREATE_PGSQL" != xyes; then
PREPARE_DB_OPTS="$PREPARE_DB_OPTS --skip-pg"
fi
if test x"$OPT_CREATE_REDIS" != xyes; then
PREPARE_DB_OPTS="$PREPARE_DB_OPTS --skip-redis"
fi
echo "Preparing the environment"
cd ${BASEDIR}/test/support
sh prepare_db.sh ${PREPARE_DB_OPTS} || die "database preparation failure"
cd -
PATH=node_modules/.bin/:$PATH
echo "Running tests"

View File

@@ -99,7 +99,7 @@ suite('multilayer', function() {
// Check X-Cache-Channel
cc = res.headers['x-cache-channel'];
assert.ok(cc);
var dbname = 'cartodb_test_user_1_db'
var dbname = 'test_cartodb_user_1_db'
assert.equal(cc.substring(0, dbname.length), dbname);
var jsonquery = cc.substring(dbname.length+1);
var sentquery = JSON.parse(jsonquery);
@@ -156,7 +156,7 @@ suite('multilayer', function() {
errors.push(err.message);
console.log("Error: " + err);
}
redis_client.keys("map_style|cartodb_test_user_1_db|~" + expected_token, function(err, matches) {
redis_client.keys("map_style|test_cartodb_user_1_db|~" + 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) {
@@ -229,7 +229,7 @@ suite('multilayer', function() {
// Check X-Cache-Channel
var cc = res.headers['x-cache-channel'];
assert.ok(cc);
var dbname = 'cartodb_test_user_1_db'
var dbname = 'test_cartodb_user_1_db'
assert.equal(cc.substring(0, dbname.length), dbname);
var jsonquery = cc.substring(dbname.length+1);
var sentquery = JSON.parse(jsonquery);
@@ -262,7 +262,7 @@ suite('multilayer', function() {
// Check X-Cache-Channel
var cc = res.headers['x-cache-channel'];
assert.ok(cc);
var dbname = 'cartodb_test_user_1_db'
var dbname = 'test_cartodb_user_1_db'
assert.equal(cc.substring(0, dbname.length), dbname);
var jsonquery = cc.substring(dbname.length+1);
var sentquery = JSON.parse(jsonquery);
@@ -321,7 +321,7 @@ suite('multilayer', function() {
errors.push(err.message);
console.log("Error: " + err);
}
redis_client.keys("map_style|cartodb_test_user_1_db|~" + expected_token, function(err, matches) {
redis_client.keys("map_style|test_cartodb_user_1_db|~" + 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) {
@@ -417,7 +417,7 @@ suite('multilayer', function() {
var next = this;
// trip epoch
expected_token = expected_token.split(':')[0];
redis_client.keys("map_style|cartodb_test_user_1_db|~" + expected_token, function(err, matches) {
redis_client.keys("map_style|test_cartodb_user_1_db|~" + expected_token, function(err, matches) {
redis_client.del(matches, next);
});
},
@@ -525,7 +525,7 @@ suite('multilayer', function() {
// Check X-Cache-Channel
var cc = res.headers['x-cache-channel'];
assert.ok(cc);
var dbname = 'cartodb_test_user_1_db'
var dbname = 'test_cartodb_user_1_db'
assert.equal(cc.substring(0, dbname.length), dbname);
next(err);
});
@@ -613,7 +613,7 @@ suite('multilayer', function() {
errors.push(err.message);
console.log("Error: " + err);
}
redis_client.keys("map_style|cartodb_test_user_1_db|~" + expected_token, function(err, matches) {
redis_client.keys("map_style|test_cartodb_user_1_db|~" + 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) {
@@ -648,7 +648,9 @@ suite('multilayer', function() {
}, {}, function(res) {
assert.equal(res.statusCode, 400, res.statusCode + ': ' + res.body);
var parsed = JSON.parse(res.body);
assert.deepEqual(parsed, {"errors":["style0:1:10 Invalid value for text-name, the type expression is expected. cartodb_id (of type keyword) was given."]});
assert.equal(parsed.errors.length, 1);
var errmsg = parsed.errors[0];
assert.ok(errmsg.match(/text-face-name.*Dejagnu/), parsed.errors.toString());
done();
});
});
@@ -682,6 +684,185 @@ suite('multilayer', function() {
});
});
// See https://github.com/CartoDB/Windshaft-cartodb/issues/87
test("exponential notation in CartoCSS filter values", function(done) {
var layergroup = {
version: '1.0.1',
layers: [
{ options: {
sql: "select .4 as n, 'SRID=3857;POINT(0 0)'::geometry as the_geom_webmercator",
cartocss: '#s [n<=.2e-2] { marker-fill:red; }',
cartocss_version: '2.1.0',
} }
]
};
assert.response(server, {
url: '/tiles/layergroup?',
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(layergroup)
}, {}, function(res) {
assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body);
done();
});
});
// See https://github.com/CartoDB/Windshaft-cartodb/issues/93
test("accepts unused directives", function(done) {
var layergroup = {
version: '1.0.0',
layers: [
{ options: {
sql: "select 'SRID=3857;POINT(0 0)'::geometry as the_geom_webmercator",
cartocss: '#layer { point-transform:"scale(20)"; }',
cartocss_version: '2.0.1'
} }
]
};
var expected_token; // = "e34dd7e235138a062f8ba7ad051aa3a7";
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) {
assert.equal(res.statusCode, 200, res.body);
var parsedBody = JSON.parse(res.body);
var expectedBody = { layergroupid: expected_token };
if ( expected_token ) {
assert.equal(parsedBody.layergroupid, expected_token + ':' + expected_last_updated_epoch);
}
else {
var token_components = parsedBody.layergroupid.split(':');
expected_token = token_components[0];
expected_last_updated_epoch = token_components[1];
}
next(null, res);
});
},
function do_get_tile(err)
{
if ( err ) throw err;
var next = this;
assert.response(server, {
url: '/tiles/layergroup/' + expected_token + ':cb0/0/0/0.png',
method: 'GET',
headers: {host: 'localhost' },
encoding: 'binary'
}, {}, function(res) {
assert.equal(res.statusCode, 200, res.body);
assert.equal(res.headers['content-type'], "image/png");
assert.imageEqualsFile(res.body, windshaft_fixtures + '/test_default_mapnik_point.png', 2,
function(err, similarity) {
next(err);
});
});
},
function finish(err) {
var errors = [];
if ( err ) {
errors.push(err.message);
console.log("Error: " + err);
}
redis_client.keys("map_style|test_cartodb_user_1_db|~" + 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) {
if ( err ) errors.push(err.message);
if ( errors.length ) done(new Error(errors));
else done(null);
});
});
}
);
});
// See https://github.com/CartoDB/Windshaft-cartodb/issues/91
// and https://github.com/CartoDB/Windshaft-cartodb/issues/38
test("tiles for private tables can be fetched with api_key", function(done) {
var errors = [];
var layergroup = {
version: '1.0.0',
layers: [
{ options: {
sql: "select * from test_table_private_1 LIMIT 0",
cartocss: '#layer { marker-fill:red; }',
cartocss_version: '2.0.1'
} }
]
};
var expected_token; // = "e34dd7e235138a062f8ba7ad051aa3a7";
Step(
function do_post()
{
var next = this;
assert.response(server, {
url: '/tiles/layergroup?api_key=1234',
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(layergroup)
}, {}, function(res) { next(null, res); });
},
function check_result(err, res) {
if ( err ) throw err;
var next = this;
assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body);
var parsedBody = JSON.parse(res.body);
if ( expected_token ) {
assert.equal(parsedBody.layergroupid, expected_token + ':' + expected_last_updated_epoch);
}
else {
var token_components = parsedBody.layergroupid.split(':');
expected_token = token_components[0];
expected_last_updated_epoch = token_components[1];
}
next(null, res);
},
function do_get_tile(err)
{
if ( err ) throw err;
var next = this;
assert.response(server, {
url: '/tiles/layergroup/' + expected_token + ':cb0/0/0/0.png?api_key=1234',
method: 'GET',
headers: {host: 'localhost' },
encoding: 'binary'
}, {}, function(res) { next(null, res); });
},
function check_get_tile(err, res) {
if ( err ) throw err;
var next = this;
assert.equal(res.statusCode, 200, res.body);
return null;
},
function cleanup(err) {
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) {
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) {
if ( err ) errors.push(err.message);
next();
});
});
},
function finish(err) {
if ( err ) {
errors.push(err.message);
console.log("Error: " + err);
}
if ( errors.length ) done(new Error(errors));
else done(null);
}
);
});
suiteTeardown(function(done) {
// This test will add map_style records, like

View File

@@ -21,13 +21,21 @@ suite('server', function() {
var redis_client = redis.createClient(global.environment.redis.port);
var sqlapi_server;
var default_style = semver.satisfies(mapnik.versions.mapnik, '<2.1.0')
?
var mapnik_version = global.environment.mapnik_version || mapnik.versions.mapnik;
var test_database = 'test_cartodb_user_1_db';
var default_style;
if ( semver.satisfies(mapnik_version, '<2.1.0') ) {
// 2.0.0 default
'#<%= table %>{marker-fill: #FF6600;marker-opacity: 1;marker-width: 8;marker-line-color: white;marker-line-width: 3;marker-line-opacity: 0.9;marker-placement: point;marker-type: ellipse;marker-allow-overlap: true;}'
:
default_style = '#<%= table %>{marker-fill: #FF6600;marker-opacity: 1;marker-width: 8;marker-line-color: white;marker-line-width: 3;marker-line-opacity: 0.9;marker-placement: point;marker-type: ellipse;marker-allow-overlap: true;}';
}
else if ( semver.satisfies(mapnik_version, '<2.2.0') ) {
// 2.1.0 default
'#<%= table %>[mapnik-geometry-type=1] {marker-fill: #FF6600;marker-opacity: 1;marker-width: 16;marker-line-color: white;marker-line-width: 3;marker-line-opacity: 0.9;marker-placement: point;marker-type: ellipse;marker-allow-overlap: true;}#<%= table %>[mapnik-geometry-type=2] {line-color:#FF6600; line-width:1; line-opacity: 0.7;}#<%= table %>[mapnik-geometry-type=3] {polygon-fill:#FF6600; polygon-opacity: 0.7; line-opacity:1; line-color: #FFFFFF;}';
default_style = '#<%= table %>[mapnik-geometry-type=1] {marker-fill: #FF6600;marker-opacity: 1;marker-width: 16;marker-line-color: white;marker-line-width: 3;marker-line-opacity: 0.9;marker-placement: point;marker-type: ellipse;marker-allow-overlap: true;}#<%= table %>[mapnik-geometry-type=2] {line-color:#FF6600; line-width:1; line-opacity: 0.7;}#<%= table %>[mapnik-geometry-type=3] {polygon-fill:#FF6600; polygon-opacity: 0.7; line-opacity:1; line-color: #FFFFFF;}';
}
else {
// 2.2.0+ default
default_style = '#<%= table %>["mapnik::geometry_type"=1] {marker-fill: #FF6600;marker-opacity: 1;marker-width: 16;marker-line-color: white;marker-line-width: 3;marker-line-opacity: 0.9;marker-placement: point;marker-type: ellipse;marker-allow-overlap: true;}#<%= table %>["mapnik::geometry_type"=2] {line-color:#FF6600; line-width:1; line-opacity: 0.7;}#<%= table %>["mapnik::geometry_type"=3] {polygon-fill:#FF6600; polygon-opacity: 0.7; line-opacity:1; line-color: #FFFFFF;}';
}
// A couple of styles to use during testing
var test_style_black_200 = "#test_table{marker-fill:black;marker-line-color:red;marker-width:10}";
@@ -95,7 +103,7 @@ suite('server', function() {
method: 'GET'
},{
status: 200,
headers: { 'X-Cache-Channel': 'cartodb_test_user_1_db:my_table' },
headers: { 'X-Cache-Channel': test_database+':my_table' },
}, function(res) {
var parsed = JSON.parse(res.body);
assert.equal(parsed.style, _.template(default_style, {table: 'my_table'}));
@@ -133,7 +141,7 @@ suite('server', function() {
// FIXME: should be 401 Unauthorized
assert.equal(res.statusCode, 400, res.statusCode + ': ' + res.body);
assert.deepEqual(JSON.parse(res.body),
{error:"missing unknown_user's dbname in redis (try CARTODB/script/restore_redis)"});
{error:"missing unknown_user's database_name in redis (try CARTODB/script/restore_redis)"});
done();
});
});
@@ -155,6 +163,43 @@ suite('server', function() {
});
});
// See https://github.com/CartoDB/Windshaft-cartodb/issues/94
test("get'ing unrenderable style", function(done) {
var base_key = 'map_style|'+test_database+'|issue94';
var style = '#s{bogus}';
Step(
function checkRedis() {
redis_client.keys(base_key+'*', this);
},
function setupRedisBase(err, matches) {
if ( err ) throw err;
assert.equal(matches.length, 0);
redis_client.set(base_key,
JSON.stringify({ style: style }),
this);
},
function getStyle(err) {
if ( err ) throw err;
var next = this;
assert.response(server, {
headers: {host: 'localhost'},
url: '/tiles/issue94/style',
method: 'GET'
}, {}, function(res) { next(null, res); });
},
function checkStyle(err, res) {
if ( err ) throw err;
assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body);
var parsed = JSON.parse(res.body);
assert.equal(parsed.style, style);
return null
},
function finish(err) {
done(err);
}
);
});
/////////////////////////////////////////////////////////////////////////////////
//
// POST STYLE
@@ -190,10 +235,11 @@ suite('server', function() {
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/x-www-form-urlencoded' },
data: querystring.stringify({style: '#my_table3{'})
},{
status: 400,
body: /Missing closing/
}, function() { done(); });
},{}, function(res) {
assert.equal(res.statusCode, 400, res.statusCode + ': ' + res.body);
assert.ok( RegExp(/missing closing/i).test(res.body) );
done();
});
});
test("post'ing multiple bad styles returns 400 with error array", function(done){
@@ -432,7 +478,7 @@ suite('server', function() {
method: 'GET'
},{
status: 200,
headers: { 'X-Cache-Channel': 'cartodb_test_user_1_db:my_tablez' },
headers: { 'X-Cache-Channel': test_database+':my_tablez' },
body: '{"infowindow":null}'
}, function() { done(); });
});
@@ -485,7 +531,7 @@ suite('server', function() {
// FIXME: should be 401 Unauthorized
assert.equal(res.statusCode, 500, res.statusCode + ': ' + res.body);
assert.deepEqual(JSON.parse(res.body),
{error:"missing unknown_user's dbname in redis (try CARTODB/script/restore_redis)"});
{error:"missing unknown_user's database_name in redis (try CARTODB/script/restore_redis)"});
done();
});
});
@@ -516,7 +562,7 @@ suite('server', function() {
},{
status: 200,
headers: { 'Content-Type': 'text/javascript; charset=utf-8; charset=utf-8',
'X-Cache-Channel': 'cartodb_test_user_1_db:gadm4' }
'X-Cache-Channel': test_database+':gadm4' }
}, function() { done(); });
});
@@ -568,7 +614,7 @@ suite('server', function() {
// FIXME: should be 401 Unauthorized
assert.equal(res.statusCode, 400, res.statusCode + ': ' + res.body);
assert.deepEqual(JSON.parse(res.body),
{error:"missing unknown_user's dbname in redis (try CARTODB/script/restore_redis)"});
{error:"missing unknown_user's database_name in redis (try CARTODB/script/restore_redis)"});
done();
});
});
@@ -631,7 +677,7 @@ suite('server', function() {
method: 'GET'
},{
status: 200,
headers: { 'Content-Type': 'image/png', 'X-Cache-Channel': 'cartodb_test_user_1_db:gadm4' }
headers: { 'Content-Type': 'image/png', 'X-Cache-Channel': test_database+':gadm4' }
}, function() { done(); });
});
@@ -719,7 +765,7 @@ suite('server', function() {
// FIXME: should be 401 Unauthorized
assert.equal(res.statusCode, 400, res.statusCode + ': ' + res.body);
assert.deepEqual(JSON.parse(res.body),
{error:"missing unknown_user's dbname in redis (try CARTODB/script/restore_redis)"});
{error:"missing unknown_user's database_name in redis (try CARTODB/script/restore_redis)"});
done();
});
});
@@ -763,6 +809,79 @@ suite('server', function() {
});
});
// See https://github.com/CartoDB/Windshaft-cartodb/issues/88
test("get'ing a tile from a user-specific database should return an expected tile", function(done){
var style = querystring.stringify({style: test_style_black_200, style_version: '2.0.0'});
var backupDBHost = global.environment.postgres.host;
global.environment.postgres.host = '6.6.6.6';
Step (
function() {
var next = this;
assert.response(server, {
headers: {host: 'cartodb250user'},
url: '/tiles/test_table/15/16046/12354.png?cache_buster=4.10&' + style,
method: 'GET',
encoding: 'binary'
},{}, function(res){
next(null, res);
});
},
function checkRes(err, res) {
assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body);
var ct = res.headers['content-type'];
assert.equal(ct, 'image/png');
assert.imageEqualsFile(res.body,
'./test/fixtures/test_table_15_16046_12354_styled_black.png',
2, this);
},
function checkImage(err, similarity) {
if (err) throw err;
return null
},
function finish(err) {
global.environment.postgres.host = backupDBHost;
done(err);
}
);
});
// See https://github.com/CartoDB/Windshaft-cartodb/issues/89
test("get'ing a tile with a user-specific database password", function(done){
var style = querystring.stringify({style: test_style_black_200, style_version: '2.0.0'});
var backupDBPass = global.settings.postgres_auth_pass;
global.settings.postgres_auth_pass = '<%= user_password %>';
Step (
function() {
var next = this;
assert.response(server, {
headers: {host: 'cartodb250user'},
url: '/tiles/test_table/15/16046/12354.png?'
+ 'cache_buster=4.20&api_key=4321&' + style,
method: 'GET',
encoding: 'binary'
},{}, function(res){
next(null, res);
});
},
function checkRes(err, res) {
assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body);
var ct = res.headers['content-type'];
assert.equal(ct, 'image/png');
assert.imageEqualsFile(res.body,
'./test/fixtures/test_table_15_16046_12354_styled_black.png',
2, this);
},
function checkImage(err, similarity) {
if (err) throw err;
return null
},
function finish(err) {
global.settings.postgres_auth_pass = backupDBPass;
done(err);
}
);
});
test("get'ing a tile with url specified 2.1.0 style should return an expected tile", function(done){
var style = querystring.stringify({style: test_style_black_210, style_version: '2.1.0'});
assert.response(server, {
@@ -954,8 +1073,7 @@ suite('server', function() {
});
},
function finish(err) {
if ( err ) done(err);
else done();
done(err);
}
);
});
@@ -970,24 +1088,30 @@ suite('server', function() {
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'
},{}, this);
},{}, function(res) { next(null, res); });
},
function checkResponse(res) {
function checkResponse(err, res) {
if ( err ) throw err;
assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body);
var ct = res.headers['content-type'];
assert.equal(ct, 'image/png');
var cc = res.headers['x-cache-channel'];
var dbname = 'cartodb_test_user_1_db'
assert(cc);
var dbname = 'test_cartodb_user_1_db'
assert.equal(cc.substring(0, dbname.length), dbname);
var jsonquery = cc.substring(dbname.length+1);
var sentquery = JSON.parse(jsonquery);
assert.equal(sentquery.api_key, qo.map_key);
assert.equal(sentquery.q, 'SELECT CDB_QueryTables($windshaft$' + qo.sql + '$windshaft$)');
done();
return null;
},
function finish(err) {
done(err);
}
);
});
@@ -1003,19 +1127,24 @@ suite('server', function() {
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'
},{}, this);
},{}, function(res) { next(null, res); });
},
function checkResponse(res) {
function checkResponse(err, res) {
if ( err ) throw err;
assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body);
var ct = res.headers['content-type'];
assert.equal(ct, 'image/png');
// does NOT send an x-cache-channel
assert.ok(!res.headers.hasOwnProperty('x-cache-channel'));
done();
return null;
},
function finish(err) {
done(err);
}
);
});

View File

@@ -10,28 +10,95 @@
# TODO: fix that
#
PREPARE_REDIS=yes
PREPARE_PGSQL=yes
while [ -n "$1" ]; do
if test "$1" = "--skip-pg"; then
PREPARE_PGSQL=no
shift; continue
elif test "$1" = "--skip-redis"; then
PREPARE_REDIS=no
shift; continue
fi
done
die() {
msg=$1
echo "${msg}" >&2
exit 1
}
TEST_DB="cartodb_test_user_1_db"
# This is where postgresql connection parameters are read from
TESTENV=../../config/environments/test.js
if [ \! -r ${TESTENV} ]; then
echo "Cannot read ${TESTENV}" >&2
exit 1
fi
TESTUSERID=1
TESTUSER=`node -e "console.log(require('${TESTENV}').postgres_auth_user || '')"`
if test -z "$TESTUSER"; then
echo "Missing postgres_auth_user from ${TESTENV}" >&2
exit 1
fi
TESTUSER=`echo ${TESTUSER} | sed "s/<%= user_id %>/${TESTUSERID}/"`
TESTPASS=`node -e "console.log(require('${TESTENV}').postgres_auth_pass || 'test')"`
# TODO: should postgres_auth_pass be optional ?
if test -z "$TESTPASS"; then
echo "Missing postgres_auth_pass from ${TESTENV}" >&2
exit 1
fi
TESTPASS=`echo ${TESTPASS} | sed "s/<%= user_id %>/${TESTUSERID}/"`
TEST_DB="${TESTUSER}_db"
# NOTE: will be set by caller trough environment
if test -z "$REDIS_PORT"; then REDIS_PORT=6333; fi
echo "preparing postgres..."
dropdb "${TEST_DB}"
createdb -Ttemplate_postgis -EUTF8 "${TEST_DB}" || die "Could not create test database"
psql "${TEST_DB}" < ./sql/windshaft.test.sql
psql "${TEST_DB}" < ./sql/gadm4.sql
PUBLICUSER=`node -e "console.log(require('${TESTENV}').postgres.user || 'xxx')"`
PUBLICPASS=`node -e "console.log(require('${TESTENV}').postgres.password || 'xxx')"`
echo "PUBLICUSER: ${PUBLICUSER}"
echo "PUBLICPASS: ${PUBLICPASS}"
echo "TESTUSER: ${TESTUSER}"
echo "TESTPASS: ${TESTPASS}"
echo "preparing redis..."
echo "HSET rails:users:localhost id 1" | redis-cli -p ${REDIS_PORT} -n 5
echo 'HSET rails:users:localhost database_name "'"${TEST_DB}"'"' | redis-cli -p ${REDIS_PORT} -n 5
echo "HSET rails:users:localhost map_key 1234" | redis-cli -p ${REDIS_PORT} -n 5
echo "SADD rails:users:localhost:map_key 1235" | redis-cli -p ${REDIS_PORT} -n 5
echo 'HSET rails:'"${TEST_DB}"':my_table infowindow "this, that, the other"' | redis-cli -p ${REDIS_PORT} -n 0
echo 'HSET rails:'"${TEST_DB}"':test_table_private_1 privacy "0"' | redis-cli -p ${REDIS_PORT} -n 0
if test x"$PREPARE_PGSQL" = xyes; then
echo "preparing postgres..."
dropdb "${TEST_DB}"
createdb -Ttemplate_postgis -EUTF8 "${TEST_DB}" || die "Could not create test database"
cat sql/windshaft.test.sql sql/gadm4.sql |
sed "s/:PUBLICUSER/${PUBLICUSER}/" |
sed "s/:PUBLICPASS/${PUBLICPASS}/" |
sed "s/:TESTUSER/${TESTUSER}/" |
sed "s/:TESTPASS/${TESTPASS}/" |
psql ${TEST_DB}
fi
if test x"$PREPARE_REDIS" = xyes; then
echo "preparing redis..."
echo "HSET rails:users:localhost id ${TESTUSERID}" | redis-cli -p ${REDIS_PORT} -n 5
echo 'HSET rails:users:localhost database_name "'"${TEST_DB}"'"' | redis-cli -p ${REDIS_PORT} -n 5
echo "HSET rails:users:localhost map_key 1234" | redis-cli -p ${REDIS_PORT} -n 5
echo "SADD rails:users:localhost:map_key 1235" | redis-cli -p ${REDIS_PORT} -n 5
# A user configured as with cartodb-2.5.0+
echo "HSET rails:users:cartodb250user id ${TESTUSERID}" | redis-cli -p ${REDIS_PORT} -n 5
echo 'HSET rails:users:cartodb250user database_name "'${TEST_DB}'"' | redis-cli -p ${REDIS_PORT} -n 5
echo 'HSET rails:users:cartodb250user database_host "localhost"' | redis-cli -p ${REDIS_PORT} -n 5
echo 'HSET rails:users:cartodb250user database_password "'${TESTPASS}'"' | redis-cli -p ${REDIS_PORT} -n 5
echo "HSET rails:users:cartodb250user map_key 4321" | redis-cli -p ${REDIS_PORT} -n 5
echo 'HSET rails:'"${TEST_DB}"':my_table infowindow "this, that, the other"' | redis-cli -p ${REDIS_PORT} -n 0
echo 'HSET rails:'"${TEST_DB}"':test_table_private_1 privacy "0"' | redis-cli -p ${REDIS_PORT} -n 0
fi
echo "Finished preparing data. Ready to run tests"

View File

@@ -22,15 +22,15 @@ CREATE TABLE gadm4 (
the_geom_webmercator geometry(MultiPolygon,3857)
);
GRANT ALL ON TABLE gadm4 TO postgres;
GRANT ALL ON TABLE gadm4 TO publicuser;
--GRANT ALL ON TABLE gadm4 TO postgres;
GRANT ALL ON TABLE gadm4 TO :PUBLICUSER;
CREATE SEQUENCE gadm4_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER TABLE public.gadm4_seq OWNER TO postgres;
--ALTER TABLE public.gadm4_seq OWNER TO postgres;
SELECT pg_catalog.setval('gadm4_seq', 58, false);
ALTER TABLE gadm4 ALTER COLUMN cartodb_id SET DEFAULT nextval('gadm4_seq'::regclass);
@@ -98,6 +98,6 @@ ALTER TABLE ONLY gadm4
CREATE INDEX bdll25_provincias_4326_2_the_geom_webmercator_idx ON gadm4 USING gist (the_geom_webmercator);
GRANT ALL ON TABLE gadm4 TO test_cartodb_user_1;
GRANT SELECT ON TABLE gadm4 TO publicuser;
GRANT ALL ON TABLE gadm4 TO :TESTUSER;
GRANT SELECT ON TABLE gadm4 TO :PUBLICUSER;

View File

@@ -15,11 +15,13 @@ SET search_path = public, pg_catalog;
SET default_tablespace = '';
SET default_with_oids = false;
-- publicuser role
CREATE USER publicuser;
-- public user role
DROP USER IF EXISTS :PUBLICUSER;
CREATE USER :PUBLICUSER WITH PASSWORD ':PUBLICPASS';
-- db owner role
CREATE USER test_cartodb_user_1;
DROP USER IF EXISTS :TESTUSER;
CREATE USER :TESTUSER WITH PASSWORD ':TESTPASS';
-- first table
CREATE TABLE test_table (
@@ -63,8 +65,8 @@ ALTER TABLE ONLY test_table ADD CONSTRAINT test_table_pkey PRIMARY KEY (cartodb_
CREATE INDEX test_table_the_geom_idx ON test_table USING gist (the_geom);
CREATE INDEX test_table_the_geom_webmercator_idx ON test_table USING gist (the_geom_webmercator);
GRANT ALL ON TABLE test_table TO test_cartodb_user_1;
GRANT SELECT ON TABLE test_table TO publicuser;
GRANT ALL ON TABLE test_table TO :TESTUSER;
GRANT SELECT ON TABLE test_table TO :PUBLICUSER;
-- second table
CREATE TABLE test_table_2 (
@@ -108,8 +110,8 @@ ALTER TABLE ONLY test_table_2 ADD CONSTRAINT test_table_2_pkey PRIMARY KEY (cart
CREATE INDEX test_table_2_the_geom_idx ON test_table_2 USING gist (the_geom);
CREATE INDEX test_table_2_the_geom_webmercator_idx ON test_table_2 USING gist (the_geom_webmercator);
GRANT ALL ON TABLE test_table_2 TO test_cartodb_user_1;
GRANT SELECT ON TABLE test_table_2 TO publicuser;
GRANT ALL ON TABLE test_table_2 TO :TESTUSER;
GRANT SELECT ON TABLE test_table_2 TO :PUBLICUSER;
-- third table
CREATE TABLE test_table_3 (
@@ -153,8 +155,8 @@ ALTER TABLE ONLY test_table_3 ADD CONSTRAINT test_table_3_pkey PRIMARY KEY (cart
CREATE INDEX test_table_3_the_geom_idx ON test_table_3 USING gist (the_geom);
CREATE INDEX test_table_3_the_geom_webmercator_idx ON test_table_3 USING gist (the_geom_webmercator);
GRANT ALL ON TABLE test_table_3 TO test_cartodb_user_1;
GRANT SELECT ON TABLE test_table_3 TO publicuser;
GRANT ALL ON TABLE test_table_3 TO :TESTUSER;
GRANT SELECT ON TABLE test_table_3 TO :PUBLICUSER;
-- private table
CREATE TABLE test_table_private_1 (
@@ -173,4 +175,4 @@ CREATE TABLE test_table_private_1 (
CONSTRAINT enforce_srid_the_geom_webmercator CHECK ((st_srid(the_geom_webmercator) = 3857))
);
GRANT ALL ON TABLE test_table_private_1 TO test_cartodb_user_1;
GRANT ALL ON TABLE test_table_private_1 TO :TESTUSER;

View File

@@ -1,63 +0,0 @@
var assert = require('../../support/assert')
, _ = require('underscore')
, RedisPool = require('../../../lib/cartodb/redis_pool')
, tests = module.exports = {};
suite('redis_pool', function() {
// configure redis pool instance to use in tests
var test_opts = require('../../support/config').redis_pool;
var redis_pool = new RedisPool(test_opts);
test('RedisPool object exists', function(done){
assert.ok(RedisPool);
done();
});
test('RedisPool can create new redis_pool objects with default settings', function(done){
var redis_pool = new RedisPool();
done();
});
test('RedisPool can create new redis_pool objects with specific settings', function(done){
var redis_pool = new RedisPool(_.extend({host:'127.0.0.1', port: '6379'}, test_opts));
done();
});
test('pool object has an acquire function', function(done){
var found=false;
var functions = _.functions(redis_pool);
for (var i=0; i<functions.length; ++i) {
if ( functions[i] == 'acquire' ) { found=true; break; }
}
assert.ok(found);
done();
});
test('calling aquire returns a redis client object that can get/set', function(done){
redis_pool.acquire(0, function(err, client){
if ( err ) { done(err); return; }
client.set("key","value");
client.get("key", function(err,data){
assert.equal(data, "value");
redis_pool.release(0, client); // needed to exit tests
done();
})
});
});
test('calling aquire on another DB returns a redis client object that can get/set', function(done){
redis_pool.acquire(2, function(err, client){
if ( err ) { done(err); return; }
client.set("key","value");
client.get("key", function(err,data){
assert.equal(data, "value");
redis_pool.release(2, client); // needed to exit tests
done();
})
});
});
});

View File

@@ -20,7 +20,7 @@ suite('req2params', function() {
assert.ok(!req.query.hasOwnProperty('dbuser'), 'dbuser was removed from query');
assert.ok(req.hasOwnProperty('params'), 'request has params');
assert.ok(req.params.hasOwnProperty('interactivity'), 'request params have interactivity');
assert.equal(req.params.dbname, 'cartodb_test_user_1_db', 'could forge dbname: '+ req.params.dbname);
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+')');
done();
});
@@ -35,7 +35,7 @@ suite('req2params', function() {
assert.ok(req.hasOwnProperty('params'), 'request has params');
assert.ok(req.params.hasOwnProperty('interactivity'), 'request params have interactivity');
// database_name for user "localhost" (see test/support/prepare_db.sh)
assert.equal(req.params.dbname, 'cartodb_test_user_1_db');
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+')');
done();
@@ -51,7 +51,7 @@ suite('req2params', function() {
assert.ok(req.hasOwnProperty('params'), 'request has params');
assert.ok(req.params.hasOwnProperty('interactivity'), 'request params have interactivity');
// database_name for user "localhost" (see test/support/prepare_db.sh)
assert.equal(req.params.dbname, 'cartodb_test_user_1_db');
assert.equal(req.params.dbname, 'test_cartodb_user_1_db');
// id for user "localhost" (see test/support/prepare_db.sh)
assert.equal(req.params.dbuser, 'test_cartodb_user_1');