diff --git a/NEWS.md b/NEWS.md index 16ac526c..54de0a71 100644 --- a/NEWS.md +++ b/NEWS.md @@ -22,6 +22,7 @@ Announcements: * Middlewarify query controller. * Set a hard limit on the size of the X-SQLAPI-Log header. * Update cartodb-psql to 0.14.0 and use the timeout parameter for pg.query. +* Expose rate limit headers for CORS requests. ## 3.0.0 Released 2019-02-22 diff --git a/app/middlewares/cors.js b/app/middlewares/cors.js index 286ced9c..d7699345 100644 --- a/app/middlewares/cors.js +++ b/app/middlewares/cors.js @@ -1,15 +1,25 @@ 'use strict'; -module.exports = function cors(extraHeaders) { - return function(req, res, next) { - var baseHeaders = 'X-Requested-With, X-Prototype-Version, X-CSRF-Token, Authorization'; +module.exports = function cors(extraHeaders = []) { + return function (req, res, next) { + const headers = [ + 'X-Requested-With', + 'X-Prototype-Version', + 'X-CSRF-Token', + 'Authorization', + ...extraHeaders + ]; - if(extraHeaders) { - baseHeaders += ', ' + extraHeaders; - } + const exposedHeaders = [ + 'Carto-Rate-Limit-Limit', + 'Carto-Rate-Limit-Remaining', + 'Carto-Rate-Limit-Reset', + 'Retry-After' + ]; res.header('Access-Control-Allow-Origin', '*'); - res.header('Access-Control-Allow-Headers', baseHeaders); + res.header('Access-Control-Allow-Headers', headers.join(', ')); + res.header('Access-Control-Expose-Headers', exposedHeaders.join(', ')); next(); }; diff --git a/test/acceptance/app-configuration.js b/test/acceptance/app-configuration.js index 418ccd35..d42cf5a4 100644 --- a/test/acceptance/app-configuration.js +++ b/test/acceptance/app-configuration.js @@ -5,6 +5,19 @@ require('../helper'); var server = require('../../app/server')(); var assert = require('../support/assert'); +const accessControlHeaders = [ + 'X-Requested-With', + 'X-Prototype-Version', + 'X-CSRF-Token', + 'Authorization' +].join(', '); + +const exposedHeaders = [ + 'Carto-Rate-Limit-Limit', + 'Carto-Rate-Limit-Remaining', + 'Carto-Rate-Limit-Reset', + 'Retry-After' +].join(', '); describe('app-configuration', function() { @@ -61,7 +74,11 @@ describe('app-configuration', function() { }, RESPONSE_OK, function(err, res) { assert.equal( res.headers['access-control-allow-headers'], - 'X-Requested-With, X-Prototype-Version, X-CSRF-Token, Authorization' + accessControlHeaders + ); + assert.equal( + res.headers['access-control-expose-headers'], + exposedHeaders ); assert.equal(res.headers['access-control-allow-origin'], '*'); done(); @@ -78,7 +95,11 @@ describe('app-configuration', function() { assert.equal(res.body, ''); assert.equal( res.headers['access-control-allow-headers'], - 'X-Requested-With, X-Prototype-Version, X-CSRF-Token, Authorization' + accessControlHeaders + ); + assert.equal( + res.headers['access-control-expose-headers'], + exposedHeaders ); assert.equal(res.headers['access-control-allow-origin'], '*'); done(); @@ -160,7 +181,11 @@ describe('app-configuration', function() { assert.equal(res.headers['access-control-allow-origin'], '*'); assert.equal( res.headers['access-control-allow-headers'], - "X-Requested-With, X-Prototype-Version, X-CSRF-Token, Authorization" + accessControlHeaders + ); + assert.equal( + res.headers['access-control-expose-headers'], + exposedHeaders ); done(); });