diff --git a/lib/wsapi.js b/lib/wsapi.js index 8b7c2881a750fa4c94462e883f3ac69a8d83052b..8df4ff9aca5077991229e8474787774bb9ac602e 100644 --- a/lib/wsapi.js +++ b/lib/wsapi.js @@ -179,29 +179,20 @@ exports.setup = function(options, app) { cookieSessionMiddleware(req, resp, function() { // only on POSTs if (req.method === "POST") { - var denied = false; - if (req.session === undefined) { // there must be a session - denied = true; - logger.warn("CSRF validation failure: POST calls to /wsapi require an active session"); - } - - // the session must have a csrf token - else if (typeof req.session.csrf !== 'string') { - denied = true; - logger.warn("CSRF validation failure: POST calls to /wsapi require an csrf token to be set"); + if (req.session === undefined || typeof req.session.csrf !== 'string') { // there must be a session + logger.warn("POST calls to /wsapi require a cookie to be sent, this user may have cookies disabled"); + return httputils.badRequest(resp, "no cookie"); } // and the token must match what is sent in the post body else if (!req.body || !req.session || !req.session.csrf || req.body.csrf != req.session.csrf) { - denied = true; // if any of these things are false, then we'll block the request var b = req.body ? req.body.csrf : "<none>"; var s = req.session ? req.session.csrf : "<none>"; logger.warn("CSRF validation failure, token mismatch. got:" + b + " want:" + s); + return httputils.badRequest(resp, "CSRF violation"); } - - if (denied) return httputils.badRequest(resp, "CSRF violation"); } return next(); }); diff --git a/tests/lib/wsapi.js b/tests/lib/wsapi.js index 0ec1997ab0d7984c84eed5f1384166ba211eee94..0bfd9989cce0914e027b4603a3cfdf0a61a73c1b 100644 --- a/tests/lib/wsapi.js +++ b/tests/lib/wsapi.js @@ -32,3 +32,10 @@ exports.post = function (path, postArgs) { wcli.post(configuration, path, context, postArgs, this.callback); }; }; + +exports.getCSRF = function() { + if (context && context.session && context.session.csrf_token) { + return context.session.csrf_token; + } + return null; +}; \ No newline at end of file diff --git a/tests/no-cookie-test.js b/tests/no-cookie-test.js new file mode 100755 index 0000000000000000000000000000000000000000..6a030d218fd27499e63d6746c5591b90419bc431 --- /dev/null +++ b/tests/no-cookie-test.js @@ -0,0 +1,104 @@ +#!/usr/bin/env node + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +require('./lib/test_env.js'); + +const +assert = require('assert'), +vows = require('vows'), +start_stop = require('./lib/start-stop.js'), +wsapi = require('./lib/wsapi.js'), +http = require('http'); + +var suite = vows.describe('registration-status-wsapi'); + +// ever time a new token is sent out, let's update the global +// var 'token' +var token = undefined; + +// start up a pristine server +start_stop.addStartupBatches(suite); + +// now start a registration +suite.addBatch({ + "start registration": { + topic: wsapi.post('/wsapi/stage_user', { + email: 'first@fakeemail.com', + site:'fakesite.com' + }), + "returns 200": function(err, r) { + assert.strictEqual(r.code, 200); + } + } +}); + +// wait for the token +suite.addBatch({ + "a token": { + topic: function() { + start_stop.waitForToken(this.callback); + }, + "is obtained": function (t) { + assert.strictEqual(typeof t, 'string'); + token = t; + } + } +}); + +suite.addBatch({ + "completing user creation": { + topic: function() { + wsapi.post('/wsapi/complete_user_creation', { token: token, pass: 'firstfakepass' }).call(this); + }, + "works": function(err, r) { + assert.equal(r.code, 200); + token = undefined; + } + } +}); + +suite.addBatch({ + "attempt to auth without cookie": { + topic: function() { + var cb = this.callback; + + var req = http.request({ + host: '127.0.0.1', + port: 10002, + path: '/wsapi/authenticate_user', + headers: { 'Content-Type': 'application/json' }, + method: "POST", + agent: false // disable node.js connection pooling + }, function(res) { + var body = ''; + res.on('data', function(chunk) { body += chunk; }) + .on('end', function() { + cb(null, {code: res.statusCode, headers: res.headers, body: body}); + }); + }).on('error', function (e) { + cb(e); + }); + req.write(JSON.stringify({ + csrf: wsapi.getCSRF(), + email: 'first@fakeemail.com', + pass: 'firstfakepass' + })); + req.end(); + }, + "returns a 400 with 'no cookie' as the body": function(err, r) { + assert.equal(err, null); + assert.equal(r.code, 400); + assert.equal(r.body, 'Bad Request: no cookie'); + } + } +}); + +// shut the server down and cleanup +start_stop.addShutdownBatches(suite); + +// run or export the suite. +if (process.argv[1] === __filename) suite.run(); +else suite.export(module);