From b4936ffafa691e0bff191deb0cb4481153245d69 Mon Sep 17 00:00:00 2001 From: Sandro Santilli Date: Thu, 6 Feb 2014 12:24:14 +0100 Subject: [PATCH] Do not allow creating template with auth='token' and no valid tokens Closes #128 Includes acceptance test for both creation and update --- lib/cartodb/template_maps.js | 5 + test/acceptance/templates.js | 149 +++++++++++++++++++++++- test/unit/cartodb/template_maps.test.js | 23 ++++ 3 files changed, 175 insertions(+), 2 deletions(-) diff --git a/lib/cartodb/template_maps.js b/lib/cartodb/template_maps.js index e1019f24..76f782c9 100644 --- a/lib/cartodb/template_maps.js +++ b/lib/cartodb/template_maps.js @@ -143,6 +143,11 @@ o._checkInvalidTemplate = function(template) { } }; + // Check certificate validity + var cert = this.getTemplateCertificate(template); + var err = this.signed_maps.checkInvalidCertificate(cert); + if ( err ) return err; + // TODO: run more checks over template format ? }; diff --git a/test/acceptance/templates.js b/test/acceptance/templates.js index 08938b41..364d1d3d 100644 --- a/test/acceptance/templates.js +++ b/test/acceptance/templates.js @@ -131,6 +131,153 @@ suite('template_api', function() { ); }); + // See https://github.com/CartoDB/Windshaft-cartodb/issues/128 + test("cannot create template with auth='token' and no valid tokens", function(done) { + var tpl_id; + Step( + function postTemplate1() + { + // clone the valid one, and give it another name + var broken_template = JSON.parse(JSON.stringify(template_acceptance1)); + broken_template.name = 'broken1'; + // Set auth='token' and specify no tokens + broken_template.auth.method = 'token'; + delete broken_template.auth.tokens; + var post_request_1 = { + url: '/tiles/template?api_key=1234', + method: 'POST', + headers: {host: 'localhost', 'Content-Type': 'application/json' }, + data: JSON.stringify(broken_template) + } + var next = this; + assert.response(server, post_request_1, {}, + function(res) { next(null, res); }); + }, + function checkFailure1(err, res) + { + if ( err ) throw err; + assert.equal(res.statusCode, 400, res.body); + var parsedBody = JSON.parse(res.body); + assert.ok(parsedBody.hasOwnProperty('error'), res.body); + var re = RegExp(/invalid.*authentication.*missing/i); + assert.ok(parsedBody.error.match(re), + 'Error for invalid authentication does not match ' + re + ': ' + parsedBody.error); + return null; + }, + function postTemplate2(err) + { + if ( err ) throw err; + // clone the valid one and rename it + var broken_template = JSON.parse(JSON.stringify(template_acceptance1)); + broken_template.name = 'broken1'; + // Set auth='token' and specify no tokens + broken_template.auth.method = 'token'; + broken_template.auth.tokens = []; + var post_request_1 = { + url: '/tiles/template?api_key=1234', + method: 'POST', + headers: {host: 'localhost', 'Content-Type': 'application/json' }, + data: JSON.stringify(broken_template) + } + var next = this; + assert.response(server, post_request_1, {}, + function(res) { next(null, res); }); + }, + function checkFailure2(err, res) + { + if ( err ) throw err; + assert.equal(res.statusCode, 400, res.body); + var parsedBody = JSON.parse(res.body); + assert.ok(parsedBody.hasOwnProperty('error'), res.body); + var re = RegExp(/invalid.*authentication.*missing/i); + assert.ok(parsedBody.error.match(re), + 'Error for invalid authentication does not match ' + re + ': ' + parsedBody.error); + return null; + }, + function postTemplateValid(err) + { + if ( err ) throw err; + // clone the valid one and rename it + var broken_template = JSON.parse(JSON.stringify(template_acceptance1)); + broken_template.name = 'broken1'; + var post_request_1 = { + url: '/tiles/template?api_key=1234', + method: 'POST', + headers: {host: 'localhost', 'Content-Type': 'application/json' }, + data: JSON.stringify(broken_template) + } + var next = this; + assert.response(server, post_request_1, {}, + function(res) { next(null, res); }); + }, + function putTemplateInvalid(err, res) + { + if ( err ) throw err; + assert.equal(res.statusCode, 200, res.body); + var parsed = JSON.parse(res.body); + assert.ok(parsed.hasOwnProperty('template_id'), + "Missing 'template_id' from response body: " + res.body); + tpl_id = parsed.template_id; + // clone the valid one and rename it + var broken_template = JSON.parse(JSON.stringify(template_acceptance1)); + broken_template.name = 'broken1'; + // Set auth='token' and specify no tokens + broken_template.auth.method = 'token'; + broken_template.auth.tokens = []; + var put_request_1 = { + url: '/tiles/template/' + tpl_id + '/?api_key=1234', + method: 'PUT', + headers: {host: 'localhost', 'Content-Type': 'application/json' }, + data: JSON.stringify(broken_template) + } + var next = this; + assert.response(server, put_request_1, {}, + function(res) { next(null, res); }); + }, + function deleteTemplate(err, res) + { + if ( err ) throw err; + assert.equal(res.statusCode, 400, res.statusCode + ": " + res.body); + var parsed = JSON.parse(res.body); + assert.ok(parsed.hasOwnProperty('error'), + "Missing 'error' from response body: " + res.body); + var re = RegExp(/invalid.*authentication.*missing/i); + assert.ok(parsed.error.match(re), + 'Error for invalid authentication on PUT does not match ' + + re + ': ' + parsed.error); + var del_request = { + url: '/tiles/template/' + tpl_id + '?api_key=1234', + method: 'DELETE', + headers: {host: 'localhost'} + } + var next = this; + assert.response(server, del_request, {}, + function(res, err) { next(err, res); }); + }, + function checkDelete(err, res) { + if ( err ) throw err; + assert.equal(res.statusCode, 204, res.statusCode + ': ' + res.body); + assert.ok(!res.body, 'Unexpected body in DELETE /template response'); + return null; + }, + function finish(err) { + var errors = []; + if ( err ) errors.push(err); + redis_client.keys("map_*|localhost", function(err, keys) { + if ( err ) errors.push(err.message); + var todrop = _.map(keys, function(m) { + if ( m.match(/^map_(tpl|crt)|/) ) + return m; + }); + if ( todrop.length ) + errors.push(new Error("Unexpected keys in redis: " + todrop)); + if ( errors.length ) done(new Error(errors.join(','))); + else done(); + }); + } + ); + }); + test("instance endpoint should return CORS headers", function(done){ Step(function postTemplate1(err, res) { var next = this; @@ -1009,8 +1156,6 @@ suite('template_api', function() { ); }); - - test("template instantiation raises mapviews counter", function(done) { var layergroup = { stat_tag: 'random_tag', diff --git a/test/unit/cartodb/template_maps.test.js b/test/unit/cartodb/template_maps.test.js index 21b465d8..9bc26c48 100644 --- a/test/unit/cartodb/template_maps.test.js +++ b/test/unit/cartodb/template_maps.test.js @@ -147,6 +147,28 @@ suite('template_maps', function() { }); }); + // See http://github.com/CartoDB/Windshaft-cartodb/issues/128 + test('does not accept template with invalid token auth (undefined tokens)', + function(done) { + var tmap = new TemplateMaps(redis_pool, signed_maps); + assert.ok(tmap); + var tpl = { version:'0.0.1', + name: "invalid_auth1", placeholders: { }, + auth: { method: 'token' }, layergroup: {} }; + tmap.addTemplate('me', tpl, function(err) { + if ( ! err ) { + done(new Error("Unexpected success with invalid token auth (undefined tokens)")); + } + else if ( ! err.message.match(/invalid 'token' authentication/i) ) { + done(new Error("Unexpected error message with invalid token auth (undefined tokens): " + + err)); + } + else { + done(); + } + }); + }); + test('add, get and delete a valid template', function(done) { var tmap = new TemplateMaps(redis_pool, signed_maps); assert.ok(tmap); @@ -271,6 +293,7 @@ suite('template_maps', function() { assert.ok(err); tpl.name = 'first'; tpl.auth.method = 'token'; + tpl.auth.valid_tokens = [ 'tok1' ]; tmap.updTemplate(owner, tpl_id, tpl, this); }, function updateTemplateWithInvalid(err) {