From 25aa967146663679053afd35fb7ba8dee4703d52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Fri, 6 Apr 2018 15:26:11 +0200 Subject: [PATCH] Forbid access to named map admin resources for everyone but master --- lib/cartodb/api/auth_api.js | 2 +- lib/cartodb/controllers/named_maps_admin.js | 11 +++++- test/acceptance/auth/authorization.js | 44 ++++++++++++++++++--- test/support/test-client.js | 30 ++++++++++++++ 4 files changed, 79 insertions(+), 8 deletions(-) diff --git a/lib/cartodb/api/auth_api.js b/lib/cartodb/api/auth_api.js index d27ea3e1..ff42c9df 100644 --- a/lib/cartodb/api/auth_api.js +++ b/lib/cartodb/api/auth_api.js @@ -109,7 +109,7 @@ AuthApi.prototype.authorizedByAPIKey = function(user, res, callback) { return callback(error); } - return callback(null, true); + return callback(null, true, apikey); }); }; diff --git a/lib/cartodb/controllers/named_maps_admin.js b/lib/cartodb/controllers/named_maps_admin.js index afcaa1e3..df3ac766 100644 --- a/lib/cartodb/controllers/named_maps_admin.js +++ b/lib/cartodb/controllers/named_maps_admin.js @@ -102,7 +102,7 @@ function authorizedByAPIKey ({ authApi, action, label }) { return function authorizedByAPIKeyMiddleware (req, res, next) { const { user } = res.locals; - authApi.authorizedByAPIKey(user, res, (err, authenticated) => { + authApi.authorizedByAPIKey(user, res, (err, authenticated, apikey) => { if (err) { return next(err); } @@ -114,6 +114,15 @@ function authorizedByAPIKey ({ authApi, action, label }) { return next(error); } + if (apikey.type !== 'master') { + const error = new Error('Forbidden'); + error.type = 'auth'; + error.subtype = 'api-key-does-not-grant-access'; + error.http_status = 403; + + return next(error); + } + next(); }); }; diff --git a/test/acceptance/auth/authorization.js b/test/acceptance/auth/authorization.js index d7df8b32..02668251 100644 --- a/test/acceptance/auth/authorization.js +++ b/test/acceptance/auth/authorization.js @@ -37,7 +37,7 @@ describe('authorization', function() { }); }); - it('should create and get a named map tile using a regular apikey token', function (done) { + it.skip('should create and get a named map tile using a regular apikey token', function (done) { const apikeyToken = 'regular1'; const mapConfig = { version: '1.7.0', @@ -61,7 +61,7 @@ describe('authorization', function() { testClient.drain(done); }); - }); + }); it('should fail getting a named map tile with default apikey token', function (done) { const apikeyTokenCreate = 'regular1'; @@ -203,7 +203,7 @@ describe('authorization', function() { assert.ok(layergroupResult.hasOwnProperty('errors')); assert.equal(layergroupResult.errors.length, 1); assert.ok(layergroupResult.errors[0].match(/permission denied/), layergroupResult.errors[0]); - + testClient.drain(done); }); }); @@ -257,7 +257,7 @@ describe('authorization', function() { type: 'source', params: { query: 'select * from populated_places_simple_reduced' - } + } } ] }; @@ -354,7 +354,39 @@ describe('authorization', function() { }); }); - it('should create and get a named map tile using a regular apikey token', function (done) { + it('should fail while listing named maps with a regular apikey token', function (done) { + const apikeyToken = 'regular1'; + + const testClient = new TestClient({}, apikeyToken); + + testClient.getNamedMapList({ response: {status: 403 }}, function (err, res, body) { + assert.ifError(err); + + assert.equal(res.statusCode, 403); + + assert.equal(body.errors.length, 1); + assert.ok(body.errors[0].match(/Forbidden/), body.errors[0]); + + testClient.drain(done); + }); + }); + + it('should list named maps with master apikey token', function (done) { + const apikeyToken = 1234; + + const testClient = new TestClient({}, apikeyToken); + + testClient.getNamedMapList({}, function (err, res, body) { + assert.ifError(err); + + assert.equal(res.statusCode, 200); + assert.ok(Array.isArray(body.template_ids)); + + testClient.drain(done); + }); + }); + + it.skip('should create and get a named map tile using a regular apikey token', function (done) { const apikeyToken = 'regular1'; const template = { @@ -391,7 +423,7 @@ describe('authorization', function() { }); }); - it('should fail creating a named map using a regular apikey token and a private table', function (done) { + it.skip('should fail creating a named map using a regular apikey token and a private table', function (done) { const apikeyToken = 'regular1'; const template = { diff --git a/test/support/test-client.js b/test/support/test-client.js index 3e919175..7b068901 100644 --- a/test/support/test-client.js +++ b/test/support/test-client.js @@ -1254,6 +1254,36 @@ TestClient.prototype.getAnalysesCatalog = function (params, callback) { ); }; +TestClient.prototype.getNamedMapList = function(params, callback) { + const request = { + url: `/api/v1/map/named?${qs.stringify({ api_key: this.apiKey })}`, + method: 'GET', + headers: { + host: 'localhost', + 'Content-Type': 'application/json' + } + }; + + let expectedResponse = { + status: 200, + headers: { + 'Content-Type': 'application/json; charset=utf-8' + } + }; + + if (params.response) { + expectedResponse = Object.assign(expectedResponse, params.response); + } + + assert.response(this.server, request, expectedResponse, (res, err) => { + if (err) { + return callback(err); + } + const body = JSON.parse(res.body); + return callback(null, res, body); + }); +}; + TestClient.prototype.getNamedTile = function (name, z, x, y, format, options, callback) { const { params } = options;