diff --git a/lib/cartodb/cartodb_windshaft.js b/lib/cartodb/cartodb_windshaft.js index 25d35b09..46a855f7 100644 --- a/lib/cartodb/cartodb_windshaft.js +++ b/lib/cartodb/cartodb_windshaft.js @@ -1,7 +1,6 @@ var _ = require('underscore') , Step = require('step') , Windshaft = require('windshaft') - , SignedMaps = require('./signed_maps.js') , TemplateMaps = require('./template_maps.js') , Cache = require('./cache_validator') , os = require('os') @@ -49,13 +48,10 @@ var CartodbWindshaft = function(serverOptions) { // var template_baseurl = global.environment.base_url_templated || '(?:/maps/named|/tiles/template)'; - var signedMaps = new SignedMaps(redisPool); - serverOptions.signedMaps = signedMaps; - var templateMapsOpts = { max_user_templates: global.environment.maxUserTemplates }; - var templateMaps = new TemplateMaps(redisPool, signedMaps, templateMapsOpts); + var templateMaps = new TemplateMaps(redisPool, templateMapsOpts); serverOptions.templateMaps = templateMaps; // boot @@ -142,7 +138,7 @@ var CartodbWindshaft = function(serverOptions) { var TemplateMapsController = require('./controllers/template_maps'), templateMapsController = new TemplateMapsController( - ws, serverOptions, templateMaps, serverOptions.signedMaps, cartoData, template_baseurl + ws, serverOptions, templateMaps, cartoData, template_baseurl ); templateMapsController.register(ws); diff --git a/lib/cartodb/controllers/template_maps.js b/lib/cartodb/controllers/template_maps.js index 485cb5f0..3ee87bf9 100644 --- a/lib/cartodb/controllers/template_maps.js +++ b/lib/cartodb/controllers/template_maps.js @@ -1,11 +1,10 @@ var Step = require('step'); var _ = require('underscore'); -function TemplateMapsController(app, serverOptions, templateMaps, signedMaps, metadataBackend, templateBaseUrl) { +function TemplateMapsController(app, serverOptions, templateMaps, metadataBackend, templateBaseUrl) { this.app = app; this.serverOptions = serverOptions; this.templateMaps = templateMaps; - this.signedMaps = signedMaps; this.metadataBackend = metadataBackend; this.templateBaseUrl = templateBaseUrl; } @@ -350,7 +349,6 @@ TemplateMapsController.prototype.instantiateTemplate = function(req, res, templa this.app.doCORS(res); - var response = {}; var template; var layergroup; var fakereq; // used for call to createLayergroup @@ -373,20 +371,20 @@ TemplateMapsController.prototype.instantiateTemplate = function(req, res, templa function getTemplate(){ self.templateMaps.getTemplate(cdbuser, tpl_id, this); }, - function checkAuthorized(err, data) { + function checkAuthorized(err, templateValue) { if ( req.profiler ) req.profiler.done('getTemplate'); if ( err ) throw err; - if ( ! data ) { + if ( ! templateValue ) { err = new Error("Template '" + tpl_id + "' of user '" + cdbuser + "' not found"); err.http_status = 404; throw err; } - template = data; - var cert = self.templateMaps.getTemplateCertificate(template); + + template = templateValue; + var authorized = false; try { - // authorizedByCert will throw if unauthorized - authorized = self.signedMaps.authorizedByCert(cert, auth_token); + authorized = self.templateMaps.isAuthorized(template, auth_token); } catch (err) { // we catch to add http_status err.http_status = 403; @@ -397,11 +395,11 @@ TemplateMapsController.prototype.instantiateTemplate = function(req, res, templa err.http_status = 403; throw err; } - /*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; - if ( req.profiler ) req.profiler.done('authorizedByCert'); + + if (req.profiler) { + req.profiler.done('authorizedByCert'); + } + return self.templateMaps.instance(template, template_params); }, function prepareParams(err, instance){ @@ -426,34 +424,13 @@ TemplateMapsController.prototype.instantiateTemplate = function(req, res, templa fakereq.params.api_key = val; self.app.createLayergroup(layergroup, fakereq, this); }, - function signLayergroup(err, resp) { - // NOTE: createLayergroup uses profiler.start()/end() internally - //if ( req.profiler ) req.profiler.done('createLayergroup'); - if ( err ) throw err; - response = resp; - var signer = cdbuser; - var map_id = response.layergroupid.split(':')[0]; // dropping last_updated - var crt_id = template.auth_id; // check ? - if ( ! crt_id ) { - var errmsg = "Template '" + tpl_id + "' of user '" + cdbuser + "' has no signature"; - // Is this really illegal ? - // Maybe we could just return an unsigned layergroupid - // in this case... - err = new Error(errmsg); - err.http_status = 403; // Forbidden, we refuse to respond to this + function prepareResponse(err, layergroup) { + if ( err ) { throw err; } - self.signedMaps.signMap(signer, map_id, crt_id, this); - }, - function prepareResponse(err) { - if ( req.profiler ) req.profiler.done('signMap'); - if ( err ) throw err; - //console.log("Response from createLayergroup: "); console.dir(response); - // Add the signature part to the token! var tplhash = self.templateMaps.fingerPrint(template).substring(0,8); - if ( req.profiler ) req.profiler.done('fingerPrint'); - response.layergroupid = cdbuser + '@' + tplhash + '@' + response.layergroupid; - return response; + layergroup.layergroupid = cdbuser + '@' + tplhash + '@' + layergroup.layergroupid; + return layergroup; }, callback ); diff --git a/lib/cartodb/server_options.js b/lib/cartodb/server_options.js index 2ac87baa..636a355a 100644 --- a/lib/cartodb/server_options.js +++ b/lib/cartodb/server_options.js @@ -515,11 +515,20 @@ module.exports = function(redisPool) { var auth_token = req.params.auth_token; //console.log("Checking authorization from signer " + signer + " for resource " + layergroup_id + " with auth_token " + auth_token); + var mapStore = req.app.mapStore; + if (!mapStore) { + throw new Error('Unable to retrieve map configuration token'); + } + + mapStore.load(layergroup_id, function(err, mapConfig) { + if (err) { + throw err; + } + + var authorized = me.templateMaps.isAuthorized(mapConfig.obj().template, auth_token); + callback(null, authorized ? signer : null); + }); - me.signedMaps.isAuthorized(signer, layergroup_id, auth_token, - function(err, authorized) { - callback(err, authorized ? signer : null); - }); }; // Check if a request is authorized by api_key diff --git a/lib/cartodb/template_maps.js b/lib/cartodb/template_maps.js index 9b4cfba4..01940d9f 100644 --- a/lib/cartodb/template_maps.js +++ b/lib/cartodb/template_maps.js @@ -11,16 +11,12 @@ var crypto = require('crypto'), // See https://github.com/CartoDB/node-redis-mpool // Needs version 0.x.x of the API. // -// @param signed_maps an instance of a "signed_maps" class, -// See signed_maps.js -// // @param opts TemplateMap options. Supported elements: // 'max_user_templates' limit on the number of per-user // // -function TemplateMaps(redis_pool, signed_maps, opts) { +function TemplateMaps(redis_pool, opts) { this.redis_pool = redis_pool; - this.signed_maps = signed_maps; this.opts = opts || {}; // Database containing templates @@ -152,12 +148,23 @@ o._checkInvalidTemplate = function(template) { } } - // Check certificate validity - var cert = this.getTemplateCertificate(template); - var err = this.signed_maps.checkInvalidCertificate(cert); - if ( err ) return err; + var auth = template.auth || {}; - // TODO: run more checks over template format ? + switch ( auth.method ) { + case 'open': + break; + case 'token': + if ( ! _.isArray(auth.valid_tokens) ) + return new Error("Invalid 'token' authentication: missing valid_tokens"); + if ( ! auth.valid_tokens.length ) + return new Error("Invalid 'token' authentication: no valid_tokens"); + break; + default: + return new Error("Unsupported authentication method: " + auth.method); + break; + } + + return false; }; //--------------- PUBLIC API ------------------------------------- @@ -175,6 +182,15 @@ o.getTemplateCertificate = function(template) { }; }; +function templateDefaults(template) { + var templateAuth = _.defaults({}, template.auth || {}, { + method: 'open' + }); + return _.defaults({ auth: templateAuth }, template, { + placeholders: {} + }); +} + // Add a template // // NOTE: locks user+template_name or fails @@ -188,102 +204,54 @@ o.getTemplateCertificate = function(template) { // Return template identifier (only valid for given user) // o.addTemplate = function(owner, template, callback) { - var invalidError = this._checkInvalidTemplate(template); - if ( invalidError ) { - callback(invalidError); - return; - } - var tplname = template.name; + var self = this; - // Procedure: - // - // - Check against limit - // 0. Obtain a lock for user+template_name, fail if impossible - // 1. Check no other template exists with the same name - // 2. Install certificate extracted from template, extending - // it to contain a name to properly salt things out. - // 3. Modify the template object to reference certificate by id - // 4. Install template - // 5. Release lock - // - // + template = templateDefaults(template); - var usr_tpl_key = this.key_usr_tpl({owner:owner}); - var gotLock = false; - var that = this; - var limit = that._userTemplateLimit(); - Step( - function checkLimit() { - if ( ! limit ) return 0; - that._redisCmd('HLEN', [ usr_tpl_key ], this); - }, - // try to obtain a lock - function obtainLock(err, len) { - if ( err ) throw err; - if ( limit && len >= limit ) { - throw new Error("User '" + owner + "' reached limit on number of templates (" + len + "/" + limit + ")"); - } - that._obtainTemplateLock(owner, tplname, this); - }, - function getExistingTemplate(err, locked) { - if ( err ) throw err; - if ( ! locked ) { - // Already locked - throw new Error("Template '" + tplname + "' of user '" + owner + "' is locked"); - } - gotLock = true; - that._redisCmd('HEXISTS', [ usr_tpl_key, tplname ], this); - }, - function installCertificate(err, exists) { - if ( err ) throw err; - if ( exists ) { - throw new Error("Template '" + tplname + "' of user '" + owner + "' already exists"); - } - var cert = that.getTemplateCertificate(template); - that.signed_maps.addCertificate(owner, cert, this); - }, - function installTemplate(err, crt_id) { - if ( err ) throw err; - template.auth_id = crt_id; - var tpl_val = JSON.stringify(template); - that._redisCmd('HSET', [ usr_tpl_key, tplname, tpl_val ], this); - }, - function releaseLock(err, newfield) { - if ( ! err && ! newfield ) { - console.log("ERROR: addTemplate overridden existing template '" - + tplname + "' of '" + owner - + "' -- HSET returned " + overridden + ": someone added it without locking ?"); - // TODO: how to recover this ?! - } - - if ( err && ! gotLock ) throw err; - - // release the lock - var next = this; - that._releaseTemplateLock(owner, tplname, function(e, d) { - if ( e ) { - console.log("Error removing lock on template '" + tplname - + "' of user '" + owner + "': " + e); - } else if ( ! d ) { - console.log("ERROR: lock on template '" + tplname - + "' of user '" + owner + "' externally removed during insert!"); - } - next(err); - }); - }, - function finish(err) { - callback(err, tplname); + var invalidError = this._checkInvalidTemplate(template); + if ( invalidError ) { + return callback(invalidError); } + + var templateName = template.name; + var userTemplatesKey = this.key_usr_tpl({ owner:owner }); + var limit = this._userTemplateLimit(); + + Step( + function checkLimit() { + if ( ! limit ) { + return 0; + } + self._redisCmd('HLEN', [ userTemplatesKey ], this); + }, + function installTemplateIfDoesNotExist(err, numberOfTemplates) { + if ( err ) { + throw err; + } + if ( limit && numberOfTemplates >= limit ) { + throw new Error("User '" + owner + "' reached limit on number of templates " + + "("+ numberOfTemplates + "/" + limit + ")"); + } + self._redisCmd('HSETNX', [ userTemplatesKey, templateName, JSON.stringify(template) ], this); + }, + function validateInstallation(err, wasSet) { + if ( err ) { + throw err; + } + if ( ! wasSet ) { + throw new Error("Template '" + templateName + "' of user '" + owner + "' already exists"); + } + + return true; + }, + function finish(err) { + callback(err, templateName); + } ); }; // Delete a template // -// NOTE: locks user+template_name or fails -// -// Also deletes associated authentication certificate, which -// in turn deletes all instance signatures -// // @param owner cartodb username of the template owner // // @param tpl_id template identifier as returned @@ -292,82 +260,24 @@ o.addTemplate = function(owner, template, callback) { // @param callback function(err) // o.delTemplate = function(owner, tpl_id, callback) { - var usr_tpl_key = this.key_usr_tpl({owner:owner}); - var gotLock = false; - var that = this; - Step( - // try to obtain a lock - function obtainLock() { - that._obtainTemplateLock(owner, tpl_id, this); - }, - function getExistingTemplate(err, locked) { - if ( err ) throw err; - if ( ! locked ) { - // Already locked - throw new Error("Template '" + tpl_id + "' of user '" + owner + "' is locked"); - } - gotLock = true; - that._redisCmd('HGET', [ usr_tpl_key, tpl_id ], this); - }, - function delCertificate(err, tplval) { - if ( err ) throw err; - if ( ! tplval ) { - throw new Error("Template '" + tpl_id + "' of user '" + owner + "' does not exist"); - } - var tpl = JSON.parse(tplval); - if ( ! tpl.auth_id ) { - // not sure this is an error, in case we'll ever - // allow unsigned templates... - console.log("ERROR: installed template '" + tpl_id - + "' of user '" + owner + "' has no auth_id reference: "); console.dir(tpl); - return null; - } - var next = this; - that.signed_maps.delCertificate(owner, tpl.auth_id, function(err) { - if ( err ) { - var msg = "ERROR: could not delete certificate '" - + tpl.auth_id + "' associated with template '" - + tpl_id + "' of user '" + owner + "': " + err; - // I'm actually not sure we want this event to be fatal - // (avoiding a deletion of the template itself) - next(new Error(msg)); - } else { - next(); + var self = this; + Step( + function deleteTemplate() { + self._redisCmd('HDEL', [ self.key_usr_tpl({ owner:owner }), tpl_id ], this); + }, + function handleDeletion(err, deleted) { + if (err) { + throw err; + } + if (!deleted) { + throw new Error("Template '" + tpl_id + "' of user '" + owner + "' does not exist"); + } + return true; + }, + function finish(err) { + callback(err); } - }); - }, - function delTemplate(err) { - if ( err ) throw err; - that._redisCmd('HDEL', [ usr_tpl_key, tpl_id ], this); - }, - function releaseLock(err, deleted) { - if ( ! err && ! deleted ) { - console.log("ERROR: template '" + tpl_id - + "' of user '" + owner + "' externally removed during delete!"); - } - - if ( ! gotLock ) { - if ( err ) throw err; - return null; - } - - // release the lock - var next = this; - that._releaseTemplateLock(owner, tpl_id, function(e, d) { - if ( e ) { - console.log("Error removing lock on template '" + tpl_id - + "' of user '" + owner + "': " + e); - } else if ( ! d ) { - console.log("ERROR: lock on template '" + tpl_id - + "' of user '" + owner + "' externally removed during delete!"); - } - next(err); - }); - }, - function finish(err) { - callback(err); - } - ); + ); }; // Update a template @@ -387,104 +297,50 @@ o.delTemplate = function(owner, tpl_id, callback) { // @param callback function(err) // o.updTemplate = function(owner, tpl_id, template, callback) { + var self = this; - var invalidError = this._checkInvalidTemplate(template); - if ( invalidError ) { - callback(invalidError); - return; - } + template = templateDefaults(template); - var tplname = template.name; + var invalidError = this._checkInvalidTemplate(template); - if ( tpl_id != tplname ) { - callback(new Error("Cannot update name of a map template ('" + tpl_id + "' != '" + tplname + "')")); - return; - } - - var usr_tpl_key = this.key_usr_tpl({owner:owner}); - var gotLock = false; - var that = this; - Step( - // try to obtain a lock - function obtainLock() { - that._obtainTemplateLock(owner, tpl_id, this); - }, - function getExistingTemplate(err, locked) { - if ( err ) throw err; - if ( ! locked ) { - // Already locked - throw new Error("Template '" + tpl_id + "' of user '" + owner + "' is locked"); - } - gotLock = true; - that._redisCmd('HGET', [ usr_tpl_key, tpl_id ], this); - }, - function delOldCertificate(err, tplval) { - if ( err ) throw err; - if ( ! tplval ) { - throw new Error("Template '" + tpl_id + "' of user '" - + owner +"' does not exist"); - } - var tpl = JSON.parse(tplval); - if ( ! tpl.auth_id ) { - // not sure this is an error, in case we'll ever - // allow unsigned templates... - console.log("ERROR: installed template '" + tpl_id - + "' of user '" + owner + "' has no auth_id reference: "); console.dir(tpl); - return null; - } - var next = this; - that.signed_maps.delCertificate(owner, tpl.auth_id, function(err) { - if ( err ) { - var msg = "ERROR: could not delete certificate '" - + tpl.auth_id + "' associated with template '" - + tpl_id + "' of user '" + owner + "': " + err; - // I'm actually not sure we want this event to be fatal - // (avoiding a deletion of the template itself) - next(new Error(msg)); - } else { - next(); - } - }); - }, - function installNewCertificate(err) { - if ( err ) throw err; - var cert = that.getTemplateCertificate(template); - that.signed_maps.addCertificate(owner, cert, this); - }, - function updTemplate(err, crt_id) { - if ( err ) throw err; - template.auth_id = crt_id; - var tpl_val = JSON.stringify(template); - that._redisCmd('HSET', [ usr_tpl_key, tplname, tpl_val ], this); - }, - function releaseLock(err, newfield) { - if ( ! err && newfield ) { - console.log("ERROR: template '" + tpl_id - + "' of user '" + owner + "' externally removed during update!"); - } - - if ( ! gotLock ) { - if ( err ) throw err; - return null; - } - - // release the lock - var next = this; - that._releaseTemplateLock(owner, tpl_id, function(e, d) { - if ( e ) { - console.log("Error removing lock on template '" + tpl_id - + "' of user '" + owner + "': " + e); - } else if ( ! d ) { - console.log("ERROR: lock on template '" + tpl_id - + "' of user '" + owner + "' externally removed during update!"); - } - next(err); - }); - }, - function finish(err) { - callback(err); + if ( invalidError ) { + return callback(invalidError); } - ); + + var templateName = template.name; + + if ( tpl_id != templateName ) { + return callback(new Error("Cannot update name of a map template ('" + tpl_id + "' != '" + templateName + "')")); + } + + var userTemplatesKey = this.key_usr_tpl({ owner:owner }); + + Step( + function getExistingTemplate() { + self._redisCmd('HGET', [ userTemplatesKey, tpl_id ], this); + }, + function updateTemplate(err, currentTemplate) { + if (err) { + throw err; + } + if (!currentTemplate) { + throw new Error("Template '" + tpl_id + "' of user '" + owner + "' does not exist"); + } + self._redisCmd('HSET', [ userTemplatesKey, templateName, JSON.stringify(template) ], this); + }, + function handleTemplateUpdate(err, didSetNewField) { + if (err) { + throw err; + } + if (didSetNewField) { + console.warn('New template created on update operation'); + } + return true; + }, + function finish(err) { + callback(err); + } + ); }; // List user templates @@ -509,20 +365,43 @@ o.listTemplates = function(owner, callback) { // Return full template definition // o.getTemplate = function(owner, tpl_id, callback) { - var that = this; - Step( - function getTemplate() { - that._redisCmd('HGET', [ that.key_usr_tpl({owner:owner}), tpl_id ], this); - }, - function parseTemplate(err, tpl_val) { - if ( err ) throw err; - // Should we strip auth_id ? - return JSON.parse(tpl_val); - }, - function finish(err, tpl) { - callback(err, tpl); + var self = this; + Step( + function getTemplate() { + self._redisCmd('HGET', [ self.key_usr_tpl({owner:owner}), tpl_id ], this); + }, + function parseTemplate(err, tpl_val) { + if ( err ) throw err; + return JSON.parse(tpl_val); + }, + function finish(err, tpl) { + callback(err, tpl); + } + ); +}; + +o.isAuthorized = function(template, authTokens) { + if (!template) { + return false; } - ); + + authTokens = _.isArray(authTokens) ? authTokens : [authTokens]; + + var templateAuth = template.auth; + + if (!templateAuth) { + return false; + } + + if (templateAuth.method === 'open') { + return true; + } + + if (templateAuth.method === 'token') { + return _.intersection(templateAuth.valid_tokens, authTokens).length > 0; + } + + return false; }; // Perform placeholder substitutions on a template @@ -594,6 +473,13 @@ o.instance = function(template, params) { if ( lyropt.sql) lyropt.sql = _replaceVars(lyropt.sql, all_params); // Anything else ? } + + // extra information about the template + layergroup.template = { + name: template.name, + auth: template.auth + }; + return layergroup; }; diff --git a/test/acceptance/templates.js b/test/acceptance/templates.js index d51db6f9..ced90a40 100644 --- a/test/acceptance/templates.js +++ b/test/acceptance/templates.js @@ -57,6 +57,29 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio } }; + function makeTemplate(templateName) { + return { + version: '0.0.1', + name: templateName || 'acceptance1', + auth: { method: 'open' }, + 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 offset 2', + cartocss: '#layer { marker-fill:blue; marker-allow-overlap:true; }', + cartocss_version: '2.0.2', + interactivity: 'cartodb_id' + } } + ] + } + }; + } + + function extendDefaultsTemplate(template) { + return _.extend({}, template, {auth: {method: 'open'}, placeholders: {}}); + } + test("can add template, returning id", function(done) { var errors = []; @@ -118,15 +141,12 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio if ( m.match(/^map_(tpl|crt)|/) ) return m; }); - if ( todrop.length != 2 ) { + if ( todrop.length !== 1 ) { 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); @@ -457,15 +477,12 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio if ( m.match(/^map_(tpl|crt)|/) ) return m; }); - if ( todrop.length != 2 ) { + if ( todrop.length !== 1 ) { 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); @@ -492,7 +509,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio url: '/tiles/template?api_key=1234', method: 'POST', headers: {host: 'localhost', 'Content-Type': 'application/json' }, - data: JSON.stringify(template_acceptance1) + data: JSON.stringify(makeTemplate()) } assert.response(server, post_request, {}, function(res) { next(null, res); }); @@ -530,7 +547,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio url: '/tiles/template/unexistent/?api_key=1234', method: 'PUT', headers: {host: 'localhost', 'Content-Type': 'application/json' }, - data: JSON.stringify(template_acceptance1) + data: JSON.stringify(makeTemplate()) } var next = this; assert.response(server, put_request, {}, @@ -548,7 +565,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio url: '/tiles/template/' + tpl_id + '/?api_key=1234', method: 'PUT', headers: {host: 'localhost', 'Content-Type': 'application/json' }, - data: JSON.stringify(template_acceptance1) + data: JSON.stringify(makeTemplate()) } var next = this; assert.response(server, put_request, {}, @@ -572,15 +589,12 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio if ( m.match(/^map_(tpl|crt)|/) ) return m; }); - if ( todrop.length != 2 ) { + if ( todrop.length !== 1 ) { 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); @@ -607,7 +621,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio url: '/tiles/template?api_key=1234', method: 'POST', headers: {host: 'localhost', 'Content-Type': 'application/json' }, - data: JSON.stringify(template_acceptance1) + data: JSON.stringify(makeTemplate()) } assert.response(server, post_request, {}, function(res) { next(null, res); }); @@ -653,7 +667,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio var parsed = JSON.parse(res.body); assert.ok(parsed.hasOwnProperty('template'), "Missing 'template' from response body: " + res.body); - assert.deepEqual(template_acceptance1, parsed.template); + assert.deepEqual(extendDefaultsTemplate(makeTemplate()), parsed.template); return null; }, function finish(err) { @@ -664,15 +678,12 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio if ( m.match(/^map_(tpl|crt)|/) ) return m; }); - if ( todrop.length != 2 ) { + if ( todrop.length !== 1 ) { 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); @@ -699,7 +710,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio url: '/tiles/template?api_key=1234', method: 'POST', headers: {host: 'localhost', 'Content-Type': 'application/json' }, - data: JSON.stringify(template_acceptance1) + data: JSON.stringify(makeTemplate()) } assert.response(server, post_request, {}, function(res) { next(null, res); }); @@ -728,7 +739,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio var parsed = JSON.parse(res.body); assert.ok(parsed.hasOwnProperty('template'), "Missing 'template' from response body: " + res.body); - assert.deepEqual(template_acceptance1, parsed.template); + assert.deepEqual(extendDefaultsTemplate(makeTemplate()), parsed.template); var del_request = { url: '/tiles/template/' + tpl_id, method: 'DELETE', @@ -1011,16 +1022,10 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio assert.response(server, get_request, {}, function(res) { next(null, res); }); }, - function checkTileDeleted(err, res) { + function checkTileAvailable(err, res) { if ( err ) throw err; - assert.equal(res.statusCode, 403, - '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); + assert.equal(res.statusCode, 200, 'Tile should be accessible'); + assert.equal(res.headers['content-type'], "image/png"); return null; }, function finish(err) { @@ -1230,16 +1235,10 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio assert.response(server, get_request, {}, function(res) { next(null, res); }); }, - function checkTileDeleted(err, res) { + function checkTorqueTileAvailable(err, res) { if ( err ) throw err; - assert.equal(res.statusCode, 403, - '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); + assert.equal(res.statusCode, 200, 'Torque tile should be accessible'); + assert.equal(res.headers['content-type'], "application/json; charset=utf-8"); return null; }, function finish(err) { @@ -1426,16 +1425,10 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio assert.response(server, get_request, {}, function(res) { next(null, res); }); }, - function checkTileDeleted(err, res) { + function checkLayerAttributesAvailable(err, res) { if ( err ) throw err; - assert.equal(res.statusCode, 403, - '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); + assert.equal(res.statusCode, 200, 'Layer attributes should be accessible'); + assert.equal(res.headers['content-type'], "application/json; charset=utf-8"); return null; }, function finish(err) { @@ -1906,15 +1899,12 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio if ( m.match(/^map_(tpl|crt)|/) ) return m; }); - if ( todrop.length != 2 ) { + if ( todrop.length !== 1 ) { 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); diff --git a/test/unit/cartodb/template_maps.test.js b/test/unit/cartodb/template_maps.test.js index 928d68fe..efe80d53 100644 --- a/test/unit/cartodb/template_maps.test.js +++ b/test/unit/cartodb/template_maps.test.js @@ -1,17 +1,16 @@ var assert = require('assert') //, _ = require('underscore') , RedisPool = require('redis-mpool') - , SignedMaps = require('../../../lib/cartodb/signed_maps.js') , TemplateMaps = require('../../../lib/cartodb/template_maps.js') , test_helper = require('../../support/test_helper') , Step = require('step') + , _ = require('underscore') , tests = module.exports = {}; suite('template_maps', function() { // configure redis pool instance to use in tests var redis_pool = RedisPool(global.environment.redis); - var signed_maps = new SignedMaps(redis_pool); var validTemplate = { version:'0.0.1', @@ -22,7 +21,7 @@ suite('template_maps', function() { var owner = 'me'; test('does not accept template with unsupported version', function(done) { - var tmap = new TemplateMaps(redis_pool, signed_maps); + var tmap = new TemplateMaps(redis_pool); assert.ok(tmap); var tpl = { version:'6.6.6', name:'k', auth: {}, layergroup: {} }; @@ -42,7 +41,7 @@ suite('template_maps', function() { }); test('does not accept template with missing name', function(done) { - var tmap = new TemplateMaps(redis_pool, signed_maps); + var tmap = new TemplateMaps(redis_pool); assert.ok(tmap); var tpl = { version:'0.0.1', auth: {}, layergroup: {} }; @@ -62,7 +61,7 @@ suite('template_maps', function() { }); test('does not accept template with invalid name', function(done) { - var tmap = new TemplateMaps(redis_pool, signed_maps); + var tmap = new TemplateMaps(redis_pool); assert.ok(tmap); var tpl = { version:'0.0.1', auth: {}, layergroup: {} }; @@ -88,7 +87,7 @@ suite('template_maps', function() { }); test('does not accept template with invalid placeholder name', function(done) { - var tmap = new TemplateMaps(redis_pool, signed_maps); + var tmap = new TemplateMaps(redis_pool); assert.ok(tmap); var tpl = { version:'0.0.1', name: "valid", placeholders: {}, @@ -116,7 +115,7 @@ suite('template_maps', function() { }); test('does not accept template with missing placeholder default', function(done) { - var tmap = new TemplateMaps(redis_pool, signed_maps); + var tmap = new TemplateMaps(redis_pool); assert.ok(tmap); var tpl = { version:'0.0.1', name: "valid", placeholders: { v: {} }, @@ -136,7 +135,7 @@ suite('template_maps', function() { }); test('does not accept template with missing placeholder type', function(done) { - var tmap = new TemplateMaps(redis_pool, signed_maps); + var tmap = new TemplateMaps(redis_pool); assert.ok(tmap); var tpl = { version:'0.0.1', name: "valid", placeholders: { v: { default:1 } }, @@ -158,7 +157,7 @@ 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); + var tmap = new TemplateMaps(redis_pool); assert.ok(tmap); var tpl = { version:'0.0.1', name: "invalid_auth1", placeholders: { }, @@ -178,7 +177,7 @@ suite('template_maps', function() { }); test('add, get and delete a valid template', function(done) { - var tmap = new TemplateMaps(redis_pool, signed_maps); + var tmap = new TemplateMaps(redis_pool); assert.ok(tmap); var expected_failure = false; var tpl_id; @@ -204,7 +203,7 @@ suite('template_maps', function() { }, function delTemplate(err, got_tpl) { if ( err ) throw err; - assert.deepEqual(got_tpl, tpl); + assert.deepEqual(got_tpl, _.extend({}, tpl, {auth: {method: 'open'}, placeholders: {}})); tmap.delTemplate('me', tpl_id, this); }, function finish(err) { @@ -214,7 +213,7 @@ suite('template_maps', function() { }); test('add multiple templates, list them', function(done) { - var tmap = new TemplateMaps(redis_pool, signed_maps); + var tmap = new TemplateMaps(redis_pool); assert.ok(tmap); var expected_failure = false; var tpl1 = { version:'0.0.1', name: 'first', auth: {}, layergroup: {} }; @@ -273,7 +272,7 @@ suite('template_maps', function() { }); test('update templates', function(done) { - var tmap = new TemplateMaps(redis_pool, signed_maps); + var tmap = new TemplateMaps(redis_pool); assert.ok(tmap); var expected_failure = false; var owner = 'me'; @@ -333,7 +332,7 @@ suite('template_maps', function() { }); test('instanciate templates', function() { - var tmap = new TemplateMaps(redis_pool, signed_maps); + var tmap = new TemplateMaps(redis_pool); assert.ok(tmap); var tpl1 = { @@ -431,7 +430,7 @@ suite('template_maps', function() { // Can set a limit on the number of user templates test('can limit number of user templates', function(done) { - var tmap = new TemplateMaps(redis_pool, signed_maps, { + var tmap = new TemplateMaps(redis_pool, { max_user_templates: 2 }); assert.ok(tmap); @@ -530,7 +529,7 @@ suite('template_maps', function() { test('_obtainTemplateLock with no previous value, happy case', function(done) { runWithRedisStubbed({hget: null, hset: 1}, function() { - var templateMaps = new TemplateMaps(redis_pool, signed_maps); + var templateMaps = new TemplateMaps(redis_pool); templateMaps._obtainTemplateLock(owner, validTemplate.name, function(err, gotLock) { assert.ok(!err); @@ -542,7 +541,7 @@ suite('template_maps', function() { test('_obtainTemplateLock no lock for non expired ttl, simulates obtaining two locks at same time', function(done) { runWithRedisStubbed({hget: Date.now()}, function() { - var templateMaps = new TemplateMaps(redis_pool, signed_maps); + var templateMaps = new TemplateMaps(redis_pool); templateMaps._obtainTemplateLock(owner, validTemplate.name, function(err, gotLock) { assert.ok(!!err); @@ -561,7 +560,7 @@ suite('template_maps', function() { }; var lockTtl = 1000; runWithRedisStubbed({hget: Date.now() - lockTtl, hset: true}, function() { - var templateMaps = new TemplateMaps(redis_pool, signed_maps, {lock_ttl: lockTtl}); + var templateMaps = new TemplateMaps(redis_pool, {lock_ttl: lockTtl}); templateMaps._obtainTemplateLock(owner, validTemplate.name, function(err, gotLock) { assert.ok(!!err); @@ -582,7 +581,7 @@ suite('template_maps', function() { }; var lockTtl = 1000; runWithRedisStubbed({hget: Date.now() - lockTtl - 1, hset: true}, function() { - var templateMaps = new TemplateMaps(redis_pool, signed_maps, {lock_ttl: lockTtl}); + var templateMaps = new TemplateMaps(redis_pool, {lock_ttl: lockTtl}); templateMaps._obtainTemplateLock(owner, validTemplate.name, function(err, gotLock) { assert.ok(!err);