diff --git a/CHANGELOG.md b/CHANGELOG.md index a8e78416f..783674883 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ### master +* [ENHANCEMENT] Support CSRF hook route configuration [#2366](https://github.com/balderdashy/sails/issues/2366) * [BUGFIX] Fix [RangeError: Maximum call stack size exceeded] error in PubSub hook * [ENHANCEMENT] Support layout for Ractive template engine diff --git a/lib/hooks/csrf/index.js b/lib/hooks/csrf/index.js index 6b12e8937..3e580cd1d 100644 --- a/lib/hooks/csrf/index.js +++ b/lib/hooks/csrf/index.js @@ -27,7 +27,8 @@ module.exports = function(sails) { grantTokenViaAjax: true, protectionEnabled: true, origin: '-', - routesDisabled: '-' + routesDisabled: '-', + route: '/csrfToken' }; } else if (sails.config.csrf === false) { @@ -35,7 +36,8 @@ module.exports = function(sails) { grantTokenViaAjax: false, protectionEnabled: false, origin: '-', - routesDisabled: '-' + routesDisabled: '-', + route: '/csrfToken' }; } // If user provides ANY object (including empty object), enable all default @@ -45,11 +47,21 @@ module.exports = function(sails) { grantTokenViaAjax: true, protectionEnabled: true, origin: '-', - routesDisabled: '-' + routesDisabled: '-', + route: '/csrfToken' }); } + // Create a route path for getting _csrf parameter + var csrfRoute = {}; + csrfRoute[sails.config.csrf.route] = { + target: csrfToken, + cors: { + origin: sails.config.csrf.origin, + credentials: true + } + }; // Add the csrfToken directly to the config'd routes, so that the CORS hook can process it - sails.config.routes["/csrfToken"] = {target: csrfToken, cors: {origin: sails.config.csrf.origin, credentials:true}}; + sails.config.routes = sails.util.extend(csrfRoute, sails.config.routes); }, initialize: function(cb) { @@ -105,7 +117,12 @@ module.exports = function(sails) { }); sails.on('router:after', function() { - sails.router.bind('/csrfToken', csrfToken, 'get', {cors: {origin: sails.config.csrf.origin, credentials: true}}); + sails.router.bind(sails.config.csrf.route, csrfToken, 'get', { + cors: { + origin: sails.config.csrf.origin, + credentials: true + } + }); }); cb(); diff --git a/test/integration/hook.cors_csrf.test.js b/test/integration/hook.cors_csrf.test.js index 7099dccf0..48bdcbddb 100644 --- a/test/integration/hook.cors_csrf.test.js +++ b/test/integration/hook.cors_csrf.test.js @@ -754,6 +754,75 @@ describe('CORS and CSRF ::', function() { }); }); + describe("with CSRF set to {route: '/anotherCsrf'}", function() { + before(function() { + fs.writeFileSync(path.resolve('../', appName, 'config/csrf.js'), "module.exports.csrf = {route: '/anotherCsrf'};"); + }); + + it("a request to /csrfToken should respond with a 404", function(done) { + httpHelper.testRoute("get", 'csrftoken', function (err, response) { + if (err) return done(new Error(err)); + assert.equal(response.statusCode, 404); + done(); + }); + + }); + + it("a request to /anotherCsrf should respond with a _csrf token", function(done) { + httpHelper.testRoute("get", 'anotherCsrf', function (err, response) { + if (err) return done(new Error(err)); + try { + var body = JSON.parse(response.body); + assert(body._csrf, response.body); + done(); + } catch (e) { + done(new Error('Unexpected response: '+response.body)); + } + }); + }); + + it("a POST request without a CSRF token should result in a 403 response", function (done) { + + httpHelper.testRoute("post", 'user', function (err, response) { + + if (err) return done(new Error(err)); + assert.equal(response.statusCode, 403); + done(); + + }); + + }); + + it("a POST request with a valid CSRF token should result in a 201 response", function (done) { + + httpHelper.testRoute("get", 'anotherCsrf', function (err, response) { + if (err) return done(new Error(err)); + try { + var body = JSON.parse(response.body); + var sid = response.headers['set-cookie'][0].split(';')[0].substr(10); + httpHelper.testRoute("post", { + url: 'user', + headers: { + 'Content-type': 'application/json', + 'cookie': 'sails.sid='+sid + }, + body: '{"_csrf":"'+body._csrf+'"}' + }, function (err, response) { + + if (err) return done(new Error(err)); + + assert.equal(response.statusCode, 201); + done(); + + }); + } catch (e) { + done(e); + } + }); + }); + + }); + describe("with CSRF set to {protectionEnabled: true, grantTokenViaAjax: false}", function() { before(function() {