Browse files

perform rigorous validation on all API parameters, cleanup redundancy…

… in sanitize.js and validate.js - issue #1526

Signed-off-by: Lloyd Hilaiel <lloyd@hilaiel.com>
  • Loading branch information...
1 parent b170fc6 commit b0721b755abf26fa4c447b5248aa8c8d6549b95b @zaach zaach committed with lloyd Jul 10, 2012
View
11 bin/keysigner
@@ -74,13 +74,16 @@ try {
process.exit(1);
}
+
+
// and our single function
-app.post('/wsapi/cert_key', validate(["email", "pubkey", "ephemeral"]), function(req, resp) {
+app.post('/wsapi/cert_key', validate({ 'email': 'email', 'pubkey': 'pubkey', 'ephemeral': 'boolean' }), function(req, resp) {
var startTime = new Date();
+ console.log(req.params);
cc.enqueue({
- pubkey: req.body.pubkey,
- email: req.body.email,
- validityPeriod: (req.body.ephemeral ? config.get('ephemeral_session_duration_ms') : config.get('certificate_validity_ms')),
+ pubkey: req.params.pubkey,
+ email: req.params.email,
+ validityPeriod: (req.params.ephemeral ? config.get('ephemeral_session_duration_ms') : config.get('certificate_validity_ms')),
hostname: HOSTNAME
}, function (err, r) {
var reqTime = new Date - startTime;
View
44 lib/sanitize.js
@@ -1,44 +0,0 @@
-/* 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/. */
-
-// a teensy tinsy module to do parameter sanitization. A good candiate for future
-// librification.
-//
-// usage:
-//
-// const sanitize = require('sanitize');
-//
-// sanitize(value).isEmail();
-// sanitize(value).isDomain();
-
-// XXX - should review these simple regexps
-
-var logger = require('./logging.js').logger;
-
-module.exports = function (value) {
- var isEmail = function() {
-
- if (!value.toLowerCase().match(/^[\w.!#$%&'*+\-/=?\^`{|}~]+@[a-z\d_-]+(\.[a-z\d_-]+)+$/i))
- throw "not a valid email";
- };
-
- var isDomain = function() {
- if (!value.match(/^[a-z\d_-]+(\.[a-z\d-]+)+$/i)) {
- throw "not a valid domain";
- }
- };
-
- var isOrigin = function() {
- // allow single hostnames, e.g. localhost
- if (!value.match(/^https?:\/\/[a-z\d_-]+(\.[a-z\d_-]+)*(:\d+)?$/i)) {
- throw "not a valid origin";
- }
- };
-
- return {
- isEmail: isEmail,
- isDomain: isDomain,
- isOrigin: isOrigin
- };
-};
View
95 lib/validate.js
@@ -14,29 +14,104 @@
const
logger = require('./logging.js').logger,
-httputils = require('./httputils.js');
+httputils = require('./httputils.js'),
+check = require('validator').check;
+
+var types = {
+ email: function(x) {
+ check(x).isEmail();
+ },
+ password: function(x) {
+ check(x).len(8,80);
+ },
+ boolean: function(x) {
+ if (typeof x !== 'boolean') throw "boolean required";
+ },
+ token: function(x) {
+ check(x).len(48,48).isAlphanumeric();
+ },
+ assertion: function(x) {
+ check(x).len(50,10240).regex(/[0-9a-zA-Z~_-]+/);
+ },
+ pubkey: function(x) {
+ check(x).len(50,10240);
+ JSON.parse(x);
+ },
+ origin: function(x) {
+ // allow single hostnames, e.g. localhost
+ if (typeof x !== 'string' || !x.match(/^https?:\/\/[a-z\d_-]+(\.[a-z\d_-]+)*(:\d+)?$/i)) {
+ throw "not a valid origin";
+ }
+ }
+};
module.exports = function (params) {
+ // normalize the parameters description, verify all specified types are present
+ if (Array.isArray(params) || typeof params !== 'object' || typeof params === null) {
+ throw "argument to validate must be an object, not a " + (typeof params);
+ }
+
+ Object.keys(params).forEach(function(p) {
+ var v = params[p];
+ if (typeof v === 'string') {
+ v = { type: v };
+ }
+ if (typeof v.required === "undefined") v.required = true;
+
+ if (!types[v.type]) throw "unknown type specified in WSAPI:" + v.type;
+ params[p] = v;
+ });
+
return function(req, resp, next) {
- var params_in_request=null;
+ var reqParams = null;
if (req.method === "POST") {
- params_in_request = req.body;
+ reqParams = req.body;
} else {
- params_in_request = req.query;
+ reqParams = req.query;
}
+ // clear body and query to prevent wsapi handlers from accessing
+ // un-validated input parameters
+ req.body = {};
+ req.query = {};
+ req.params = {};
+
+ // now validate
try {
- params.forEach(function(k) {
- if (!params_in_request || !params_in_request.hasOwnProperty(k)) {
- throw k;
+ // allow csrf through
+ if (reqParams.csrf) {
+ req.params.csrf = reqParams.csrf;
+ delete reqParams.csrf;
+ }
+
+ Object.keys(params).forEach(function(p) {
+ if (params[p].required && !reqParams.hasOwnProperty(p)) throw "missing required parameter: '" + p + "'";
+ if (reqParams[p] === undefined) return;
+
+ // validate
+ try {
+ types[params[p].type](reqParams[p]);
+ } catch (e) {
+ throw p + ": " + e.toString();
}
+ req.params[p] = reqParams[p];
+ delete reqParams[p];
});
+
+ // if there are any keys left in reqParams, they're not allowable!
+ var extra = Object.keys(reqParams);
+ if (extra.length) throw "extra parameters are not allowed: " + extra.join(', ');
} catch(e) {
- var msg = "missing '" + e + "' argument";
- logger.warn("bad request received: " + msg);
- return httputils.badRequest(resp, msg);
+ var msg = {
+ success: false,
+ reason: e.toString()
+ };
+ logger.warn("bad request received: " + msg.reason);
+ resp.statusCode = 400;
+ return resp.json(msg);
}
+
// this is called outside the try/catch because errors
// in the handling of the request should be caught separately
next();
View
14 lib/wsapi.js
@@ -97,12 +97,6 @@ function authenticateSession(session, uid, level, duration_ms) {
}
}
-function checkPassword(pass) {
- if (!pass || pass.length < 8 || pass.length > 80) {
- return "valid passwords are between 8 and 80 chars";
- }
-}
-
function langContext(req) {
return {
lang: req.lang,
@@ -147,7 +141,6 @@ exports.clearAuthenticatedUser = clearAuthenticatedUser;
exports.isAuthed = isAuthed;
exports.bcryptPassword = bcryptPassword;
exports.authenticateSession = authenticateSession;
-exports.checkPassword = checkPassword;
exports.forwardWritesTo = undefined;
exports.langContext = langContext;
exports.databaseDown = databaseDown;
@@ -248,7 +241,8 @@ exports.setup = function(options, app) {
str += op.method.toUpperCase() + " - ";
str += (op.authed ? "" : "not ") + "authed";
if (op.args) {
- str += " - " + op.args.join(", ");
+ var keys = Array.isArray(op.args) ? op.args : Object.keys(op.args);
+ str += " - " + keys.join(", ");
}
if (op.internal) str += ' - internal';
str += ")";
@@ -271,7 +265,9 @@ exports.setup = function(options, app) {
// set up the argument validator
if (api.args) {
- if (!Array.isArray(api.args)) throw "exports.args must be an array of strings";
+ if (Array.isArray(api.args)) {
+ console.log("WARNING: you should update", operation, "it uses unvalidated arguments");
+ }
wsapis[operation].validate = validate(api.args);
} else {
wsapis[operation].validate = function(req,res,next) { next(); };
View
6 lib/wsapi/add_email_with_assertion.js
@@ -15,15 +15,17 @@ https = require('https');
exports.method = 'post';
exports.writes_db = true;
exports.authed = 'assertion';
-exports.args = ['assertion'];
+exports.args = {
+ 'assertion': 'assertion'
+};
exports.i18n = false;
// This WSAPI will be invoked when a user attempts to add a primary
// email address to their browserid account. They must already be
// authenticated.
exports.process = function(req, res) {
// first let's verify that the assertion is valid
- primary.verifyAssertion(req.body.assertion, function(err, email) {
+ primary.verifyAssertion(req.params.assertion, function(err, email) {
if (err) {
return res.json({
success: false,
View
14 lib/wsapi/address_info.js
@@ -21,19 +21,19 @@ logger = require('../logging.js').logger;
exports.method = 'get';
exports.writes_db = false;
exports.authed = false;
-exports.args = ['email'];
+exports.args = {
+ 'email': 'email'
+};
exports.i18n = false;
const emailRegex = /\@(.*)$/;
exports.process = function(req, res) {
// parse out the domain from the email
- var email = url.parse(req.url, true).query['email'];
- var m = emailRegex.exec(email);
- if (!m) {
- return httputils.badRequest(res, "invalid email address");
- }
+ var m = emailRegex.exec(req.params.email);
+
// Saftey value for production branch only
+ // (lth) ^^ what does this mean? ^^
var done = false;
primary.checkSupport(m[1], function(err, urls, publicKey, delegates) {
if (done) {
@@ -49,7 +49,7 @@ exports.process = function(req, res) {
urls.type = 'primary';
res.json(urls);
} else {
- db.emailKnown(email, function(err, known) {
+ db.emailKnown(req.params.email, function(err, known) {
if (err) {
return wsapi.databaseDown(res, err);
} else {
View
15 lib/wsapi/auth_with_assertion.js
@@ -16,7 +16,10 @@ config = require('../configuration');
exports.method = 'post';
exports.writes_db = false;
exports.authed = false;
-exports.args = ['assertion', 'ephemeral'];
+exports.args = {
+ 'assertion': 'assertion',
+ 'ephemeral': 'boolean'
+};
exports.i18n = false;
exports.process = function(req, res) {
@@ -25,7 +28,7 @@ exports.process = function(req, res) {
// create a user account if that's needed
// 1. first let's verify that the assertion is valid
- primary.verifyAssertion(req.body.assertion, function(err, email) {
+ primary.verifyAssertion(req.params.assertion, function(err, email) {
if (err) {
return res.json({
success: false,
@@ -43,7 +46,7 @@ exports.process = function(req, res) {
if (err) return wsapi.databaseDown(res, err);
if (!uid) return res.json({ success: false, reason: "internal error" });
wsapi.authenticateSession(req.session, uid, 'assertion',
- req.body.ephemeral ? config.get('ephemeral_session_duration_ms')
+ req.params.ephemeral ? config.get('ephemeral_session_duration_ms')
: config.get('authentication_duration_ms'));
return res.json({ success: true, userid: uid });
});
@@ -61,8 +64,8 @@ exports.process = function(req, res) {
var m = u.scheme === 'http' ? http : https;
var post_body = querystring.stringify({
- assertion: req.body.assertion,
- csrf: req.body.csrf
+ assertion: req.params.assertion,
+ csrf: req.params.csrf
});
var preq = m.request({
@@ -94,7 +97,7 @@ exports.process = function(req, res) {
logger.info("successfully created primary acct for " + email + " (" + r.userid + ")");
wsapi.authenticateSession(req.session, r.userid, 'assertion',
- req.body.ephemeral ? config.get('ephemeral_session_duration_ms')
+ req.params.ephemeral ? config.get('ephemeral_session_duration_ms')
: config.get('authentication_duration_ms'));
res.json({ success: true, userid: r.userid });
});
View
24 lib/wsapi/authenticate_user.js
@@ -17,18 +17,22 @@ config = require('../configuration');
exports.method = 'post';
exports.writes_db = false;
exports.authed = false;
-exports.args = ['email','pass', 'ephemeral'];
exports.i18n = false;
+exports.args = {
+ 'email': 'email',
+ 'pass': 'password',
+ 'ephemeral': 'boolean'
+};
exports.process = function(req, res) {
function fail(reason) {
var r = { success: false };
if (reason) r.reason = reason;
- logger.debug('authentication fails for user: ' + req.body.email + (reason ? (' - ' + reason) : ""));
+ logger.debug('authentication fails for user: ' + req.params.email + (reason ? (' - ' + reason) : ""));
return res.json(r);
}
- db.emailToUID(req.body.email, function(err, uid) {
+ db.emailToUID(req.params.email, function(err, uid) {
if (err) return wsapi.databaseDown(res, err);
if (typeof uid !== 'number') {
@@ -43,7 +47,7 @@ exports.process = function(req, res) {
}
var startTime = new Date();
- bcrypt.compare(req.body.pass, hash, function (err, success) {
+ bcrypt.compare(req.params.pass, hash, function (err, success) {
var reqTime = new Date - startTime;
statsd.timing('bcrypt.compare_time', reqTime);
@@ -56,13 +60,13 @@ exports.process = function(req, res) {
logger.error("error comparing passwords with bcrypt: " + err);
return fail("internal password check error");
} else if (!success) {
- return fail("password mismatch for user: " + req.body.email);
+ return fail("password mismatch for user: " + req.params.email);
} else {
if (!req.session) req.session = {};
wsapi.authenticateSession(req.session, uid, 'password',
- req.body.ephemeral ? config.get('ephemeral_session_duration_ms')
- : config.get('authentication_duration_ms'));
+ req.params.ephemeral ? config.get('ephemeral_session_duration_ms')
+ : config.get('authentication_duration_ms'));
res.json({ success: true, userid: uid });
@@ -78,9 +82,9 @@ exports.process = function(req, res) {
var m = u.scheme === 'http' ? http : https;
var post_body = querystring.stringify({
- oldpass: req.body.pass,
- newpass: req.body.pass,
- csrf: req.body.csrf
+ oldpass: req.params.pass,
+ newpass: req.params.pass,
+ csrf: req.params.csrf
});
var preq = m.request({
host: u.host,
View
12 lib/wsapi/cert_key.js
@@ -14,11 +14,15 @@ wsapi = require('../wsapi.js');
exports.method = 'post';
exports.writes_db = false;
exports.authed = 'password';
-exports.args = ['email','pubkey','ephemeral'];
+exports.args = {
+ 'email': 'email',
+ 'pubkey': 'pubkey',
+ 'ephemeral': 'boolean'
+};
exports.i18n = false;
exports.process = function(req, res) {
- db.userOwnsEmail(req.session.userid, req.body.email, function(err, owned) {
+ db.userOwnsEmail(req.session.userid, req.params.email, function(err, owned) {
if (err) return wsapi.databaseDown(res, err);
// not same account? big fat error
@@ -27,12 +31,14 @@ exports.process = function(req, res) {
// secondary addresses in the database may be "unverified". this occurs when
// a user forgets their password. We will not issue certs for unverified email
// addresses
- db.emailIsVerified(req.body.email, function(err, verified) {
+ db.emailIsVerified(req.params.email, function(err, verified) {
if (!verified) return httputils.forbidden(res, "that email requires (re)verification");
// forward to the keysigner!
var keysigner = urlparse(config.get('keysigner_url'));
keysigner.path = '/wsapi/cert_key';
+ req.body = req.params;
+ console.log('bid params', req.params);
forward(keysigner, req, res, function(err) {
if (err) {
logger.error("error forwarding request to keysigner: " + err);
View
21 lib/wsapi/complete_email_confirmation.js
@@ -18,26 +18,33 @@ httputils = require('../httputils.js');
exports.method = 'post';
exports.writes_db = true;
exports.authed = false;
-// NOTE: this API also takes a 'pass' parameter which is required
-// when a user has a null password (only primaries on their acct)
-exports.args = ['token'];
+exports.args = {
+ 'token': 'token',
+ // NOTE: 'pass' is required when a user has a null password
+ // (only primaries on their acct)
+ 'pass': {
+ type: 'password',
+ required: false
+ }
+};
exports.i18n = false;
exports.process = function(req, res) {
// in order to complete an email confirmation, one of the following must be true:
//
// 1. you must already be authenticated as the user who initiated the verification
// 2. you must provide the password of the initiator.
- db.authForVerificationSecret(req.body.token, function(err, initiator_hash, initiator_uid) {
+
+ db.authForVerificationSecret(req.params.token, function(err, initiator_hash, initiator_uid) {
if (err) {
logger.info("unknown verification secret: " + err);
return wsapi.databaseDown(res, err);
}
if (req.session.userid === initiator_uid) {
postAuthentication();
- } else if (typeof req.body.pass === 'string') {
- bcrypt.compare(req.body.pass, initiator_hash, function (err, success) {
+ } else if (typeof req.params.pass === 'string') {
+ bcrypt.compare(req.params.pass, initiator_hash, function (err, success) {
if (err) {
logger.warn("max load hit, failing on auth request with 503: " + err);
return httputils.serviceUnavailable(res, "server is too busy");
@@ -52,7 +59,7 @@ exports.process = function(req, res) {
}
function postAuthentication() {
- db.completeConfirmEmail(req.body.token, function(e, email, uid) {
+ db.completeConfirmEmail(req.params.token, function(e, email, uid) {
if (e) {
logger.warn("couldn't complete email verification: " + e);
wsapi.databaseDown(res, e);
View
20 lib/wsapi/complete_reset.js
@@ -15,7 +15,13 @@ exports.writes_db = true;
exports.authed = false;
// NOTE: this API also takes a 'pass' parameter which is required
// when a user has a null password (only primaries on their acct)
-exports.args = ['token'];
+exports.args = {
+ 'token': 'token',
+ 'pass': {
+ type: 'password',
+ required: false
+ }
+};
exports.i18n = true;
exports.process = function(req, res) {
@@ -28,18 +34,18 @@ exports.process = function(req, res) {
// is this the same browser?
if (typeof req.session.pendingReset === 'string' &&
- req.body.token === req.session.pendingReset) {
+ req.params.token === req.session.pendingReset) {
return postAuthentication();
}
// is a password provided?
- else if (typeof req.body.pass === 'string') {
- return db.authForVerificationSecret(req.body.token, function(err, hash) {
+ else if (typeof req.params.pass === 'string') {
+ return db.authForVerificationSecret(req.params.token, function(err, hash) {
if (err) {
logger.warn("couldn't get password for verification secret: " + err);
return wsapi.databaseDown(res, err);
}
- bcrypt.compare(req.body.pass, hash, function (err, success) {
+ bcrypt.compare(req.params.pass, hash, function (err, success) {
if (err) {
logger.warn("max load hit, failing on auth request with 503: " + err);
return httputils.serviceUnavailable(res, "server is too busy");
@@ -55,7 +61,7 @@ exports.process = function(req, res) {
}
function postAuthentication() {
- db.haveVerificationSecret(req.body.token, function(err, known) {
+ db.haveVerificationSecret(req.params.token, function(err, known) {
if (err) return wsapi.databaseDown(res, err);
if (!known) {
@@ -65,7 +71,7 @@ exports.process = function(req, res) {
return res.json({ success: false} );
}
- db.completePasswordReset(req.body.token, function(err, email, uid) {
+ db.completePasswordReset(req.params.token, function(err, email, uid) {
if (err) {
logger.warn("couldn't complete email verification: " + err);
wsapi.databaseDown(res, err);
View
66 lib/wsapi/complete_reverify.js
@@ -0,0 +1,66 @@
+/* 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/. */
+
+const
+db = require('../db.js'),
+logger = require('../logging.js').logger,
+wsapi = require('../wsapi.js'),
+bcrypt = require('../bcrypt.js'),
+httputils = require('../httputils.js');
+
+exports.method = 'post';
+exports.writes_db = true;
+exports.authed = false;
+exports.args = {
+ 'token': 'token',
+ // NOTE: 'pass' is required when a user is not authenticated
+ 'pass': {
+ type: 'password',
+ optional: true
+ }
+};
+exports.i18n = false;
+
+exports.process = function(req, res) {
+ // in order to complete an email re-verification, one of the following must be true:
+ //
+ // 1. you must already be authenticated as the user who initiated the verification
+ // 2. you must provide the password of the initiator.
+
+ db.authForVerificationSecret(req.params.token, function(err, initiator_hash, initiator_uid) {
+ if (err) {
+ logger.info("unknown verification secret: " + err);
+ return wsapi.databaseDown(res, err);
+ }
+
+ if (req.session.userid === initiator_uid) {
+ postAuthentication();
+ } else if (typeof req.params.pass === 'string') {
+ bcrypt.compare(req.params.pass, initiator_hash, function (err, success) {
+ if (err) {
+ logger.warn("max load hit, failing on auth request with 503: " + err);
+ return httputils.serviceUnavailable(res, "server is too busy");
+ } else if (!success) {
+ return httputils.authRequired(res, "password mismatch");
+ } else {
+ postAuthentication();
+ }
+ });
+ } else {
+ return httputils.authRequired(res, "password required");
+ }
+
+ function postAuthentication() {
+ db.completeReverify(req.params.token, function(e, email, uid) {
+ if (e) {
+ logger.warn("couldn't complete email verification: " + e);
+ wsapi.databaseDown(res, e);
+ } else {
+ wsapi.authenticateSession(req.session, uid, 'password');
+ res.json({ success: true });
+ }
+ });
+ };
+ });
+};
View
22 lib/wsapi/complete_user_creation.js
@@ -13,7 +13,15 @@ config = require('../configuration');
exports.method = 'post';
exports.writes_db = true;
exports.authed = false;
-exports.args = ['token'];
+exports.args = {
+ 'token': 'token',
+ // NOTE: 'pass' is required when a user completes on a different device
+ // than they initiate
+ 'pass': {
+ type: 'password',
+ required: false
+ }
+};
exports.i18n = false;
exports.process = function(req, res) {
@@ -31,18 +39,18 @@ exports.process = function(req, res) {
// is this the same browser?
if (typeof req.session.pendingCreation === 'string' &&
- req.body.token === req.session.pendingCreation) {
+ req.params.token === req.session.pendingCreation) {
return postAuthentication();
}
// is a password provided?
- else if (typeof req.body.pass === 'string') {
- return db.authForVerificationSecret(req.body.token, function(err, hash) {
+ else if (typeof req.params.pass === 'string') {
+ return db.authForVerificationSecret(req.params.token, function(err, hash) {
if (err) {
logger.warn("couldn't get password for verification secret: " + err);
return wsapi.databaseDown(res, err);
}
- bcrypt.compare(req.body.pass, hash, function (err, success) {
+ bcrypt.compare(req.params.pass, hash, function (err, success) {
if (err) {
logger.warn("max load hit, failing on auth request with 503: " + err);
return httputils.serviceUnavailable(res, "server is too busy");
@@ -58,7 +66,7 @@ exports.process = function(req, res) {
}
function postAuthentication() {
- db.haveVerificationSecret(req.body.token, function(err, known) {
+ db.haveVerificationSecret(req.params.token, function(err, known) {
if (err) return wsapi.databaseDown(res, err);
if (!known) {
@@ -68,7 +76,7 @@ exports.process = function(req, res) {
return res.json({ success: false} );
}
- db.completeCreateUser(req.body.token, function(err, email, uid) {
+ db.completeCreateUser(req.params.token, function(err, email, uid) {
if (err) {
logger.warn("couldn't complete email verification: " + err);
wsapi.databaseDown(res, err);
View
10 lib/wsapi/create_account_with_assertion.js
@@ -13,16 +13,20 @@ exports.method = 'post';
exports.writes_db = true;
exports.authed = false;
exports.internal = true;
-exports.args = ['assertion'];
+exports.args = {
+ assertion: 'assertion'
+};
exports.i18n = false;
exports.process = function(req, res) {
// let's (re)verify that the assertion is valid
- primary.verifyAssertion(req.body.assertion, function(err, email) {
+ primary.verifyAssertion(req.params.assertion, function(err, email) {
if (err) {
// this should not be an error, the assertion should have already been
// tested on the webhead
- logger.error('verfication of primary assertion failed unexpectedly dbwriter (' + err + '): ' + req.body.assertion);
+ logger.error('verfication of primary assertion failed unexpectedly dbwriter (' + err + '): ' +
+ req.params.assertion);
+
return httputils.serverError(res);
}
View
4 lib/wsapi/email_addition_status.js
@@ -15,11 +15,11 @@ wsapi = require('../wsapi.js');
exports.method = 'get';
exports.writes_db = false;
exports.authed = 'assertion';
-exports.args = ['email'];
+exports.args = { email: 'email' };
exports.i18n = false;
exports.process = function(req, res) {
- var email = req.query.email;
+ var email = req.params.email;
// check if the currently authenticated user has the email stored under pendingAddition
// in their acct.
View
10 lib/wsapi/email_for_token.js
@@ -21,12 +21,14 @@ logger = require('../logging.js').logger;
exports.method = 'get';
exports.writes_db = false;
exports.authed = false;
-exports.args = ['token'];
+exports.args = {
+ 'token': 'token'
+};
exports.i18n = false;
exports.process = function(req, res) {
- db.emailForVerificationSecret(req.query.token, function(err, email, uid, hash) {
+ db.emailForVerificationSecret(req.params.token, function(err, email, uid, hash) {
if (err) {
if (err === 'database unavailable') {
return httputils.serviceUnavailable(res, err);
@@ -48,11 +50,11 @@ exports.process = function(req, res) {
must_auth = false;
}
else if (!uid && typeof req.session.pendingCreation === 'string' &&
- req.query.token === req.session.pendingCreation) {
+ req.params.token === req.session.pendingCreation) {
must_auth = false;
}
else if (typeof req.session.pendingReset === 'string' &&
- req.query.token === req.session.pendingReset)
+ req.params.token === req.session.pendingReset)
{
must_auth = false;
}
View
10 lib/wsapi/email_reverify_status.js
@@ -13,21 +13,17 @@ wsapi = require('../wsapi.js');
exports.method = 'get';
exports.writes_db = false;
exports.authed = 'assertion';
-exports.args = ['email'];
+exports.args = { email: 'email' };
exports.i18n = false;
exports.process = function(req, res) {
- var email = req.query.email;
-
+
// For simplicity, all we check is if an email is verified. We do not check that
// the email is owned by the currently authenticated user, nor that the verification
// secret still exists. These checks would require more database interactions, and
// other calls will fail in such circumstances.
-
- // is the address verified?
- db.emailIsVerified(email, function(err, verified) {
+ db.emailIsVerified(req.params.email, function(err, verified) {
if (err) return wsapi.databaseDown(res, err);
-
res.json({ status: verified ? 'complete' : 'pending' });
});
};
View
7 lib/wsapi/have_email.js
@@ -12,12 +12,13 @@ url = require('url');
exports.method = 'get';
exports.writes_db = false;
exports.authed = false;
-exports.args = ['email'];
exports.i18n = false;
+exports.args = {
+ 'email': 'email'
+};
exports.process = function(req, res) {
- var email = url.parse(req.url, true).query['email'];
- db.emailKnown(email, function(err, known) {
+ db.emailKnown(req.params.email, function(err, known) {
if (err) return wsapi.databaseDown(res, err);
res.json({ email_known: known });
});
View
15 lib/wsapi/password_reset_status.js
@@ -6,25 +6,16 @@ const
db = require('../db.js'),
wsapi = require('../wsapi.js'),
logger = require('../logging.js').logger,
-httputils = require('../httputils.js'),
-sanitize = require('../sanitize.js');
+httputils = require('../httputils.js');
exports.method = 'get';
exports.writes_db = false;
exports.authed = false;
-exports.args = ['email'];
+exports.args = { email: 'email' };
exports.i18n = false;
exports.process = function(req, res) {
- var email = req.query.email;
-
- try {
- sanitize(email).isEmail();
- } catch(e) {
- var msg = "invalid arguments: " + e;
- logger.warn("bad request received: " + msg);
- return httputils.badRequest(res, msg);
- }
+ var email = req.params.email;
// if the email is in the staged table, we are not complete yet.
// if the email is not in the staged table -
View
6 lib/wsapi/remove_email.js
@@ -11,11 +11,13 @@ logger = require('../logging.js').logger;
exports.method = 'post';
exports.writes_db = true;
exports.authed = 'assertion';
-exports.args = ['email'];
+exports.args = {
+ 'email': 'email'
+};
exports.i18n = false;
exports.process = function(req, res) {
- var email = req.body.email;
+ var email = req.params.email;
db.removeEmail(req.session.userid, email, function(error) {
if (error) {
View
35 lib/wsapi/stage_email.js
@@ -8,51 +8,46 @@ wsapi = require('../wsapi.js'),
httputils = require('../httputils'),
logger = require('../logging.js').logger,
email = require('../email.js'),
-sanitize = require('../sanitize'),
config = require('../configuration');
/* Stage an email for addition to a user's account. Causes email to be sent. */
exports.method = 'post';
exports.writes_db = true;
exports.authed = 'assertion';
-exports.args = ['email','site'];
+exports.args = {
+ email: 'email',
+ site: 'origin',
+ pass: {
+ type: 'password',
+ required: false
+ }
+};
exports.i18n = true;
exports.process = function(req, res) {
// a password *must* be supplied to this call iff the user's password
// is currently NULL - this would occur in the case where this is the
// first secondary address to be added to an account
- // validate
- try {
- sanitize(req.body.email).isEmail();
- sanitize(req.body.site).isOrigin();
- } catch(e) {
- var msg = "invalid arguments: " + e;
- logger.warn("bad request received: " + msg);
- return httputils.badRequest(res, msg);
- }
-
- db.lastStaged(req.body.email, function (err, last) {
+ db.lastStaged(req.params.email, function (err, last) {
if (err) return wsapi.databaseDown(res, err);
if (last && (new Date() - last) < config.get('min_time_between_emails_ms')) {
- logger.warn('throttling request to stage email address ' + req.body.email + ', only ' +
+ logger.warn('throttling request to stage email address ' + req.params.email + ', only ' +
((new Date() - last) / 1000.0) + "s elapsed");
return httputils.throttled(res, "Too many emails sent to that address, try again later.");
}
db.checkAuth(req.session.userid, function(err, hash) {
var needs_password = !hash;
- if (!err && needs_password && !req.body.pass) {
+ if (!err && needs_password && !req.params.pass) {
err = "user must choose a password";
}
- if (!err && !needs_password && req.body.pass) {
+ if (!err && !needs_password && req.params.pass) {
err = "a password may not be set at this time";
}
- if (!err && needs_password) err = wsapi.checkPassword(req.body.pass);
if (err) {
logger.info("stage of email fails: " + err);
@@ -63,7 +58,7 @@ exports.process = function(req, res) {
}
if (needs_password) {
- wsapi.bcryptPassword(req.body.pass, function(err, hash) {
+ wsapi.bcryptPassword(req.params.pass, function(err, hash) {
if (err) {
logger.warn("couldn't bcrypt password during email verification: " + err);
return res.json({ success: false });
@@ -78,7 +73,7 @@ exports.process = function(req, res) {
function completeStage(hash) {
try {
// on failure stageEmail may throw
- db.stageEmail(req.session.userid, req.body.email, hash, function(err, secret) {
+ db.stageEmail(req.session.userid, req.params.email, hash, function(err, secret) {
if (err) return wsapi.databaseDown(res, err);
var langContext = wsapi.langContext(req);
@@ -88,7 +83,7 @@ exports.process = function(req, res) {
res.json({ success: true });
// let's now kick out a verification email!
- email.sendConfirmationEmail(req.body.email, req.body.site, secret, langContext);
+ email.sendConfirmationEmail(req.params.email, req.params.site, secret, langContext);
});
} catch(e) {
// we should differentiate tween' 400 and 500 here.
View
39 lib/wsapi/stage_reset.js
@@ -8,7 +8,6 @@ wsapi = require('../wsapi.js'),
httputils = require('../httputils'),
logger = require('../logging.js').logger,
email = require('../email.js'),
-sanitize = require('../sanitize'),
config = require('../configuration');
/* First half of account creation. Stages a user account for creation.
@@ -20,40 +19,24 @@ config = require('../configuration');
exports.method = 'post';
exports.writes_db = true;
exports.authed = false;
-exports.args = ['email','site','pass'];
+exports.args = {
+ email: 'email',
+ site: 'origin',
+ pass: 'password'
+};
exports.i18n = true;
exports.process = function(req, res) {
- // a password *must* be supplied to this call iff the user's password
- // is currently NULL - this would occur in the case where this is the
- // first secondary address to be added to an account
-
- // validate
- try {
- sanitize(req.body.email).isEmail();
- sanitize(req.body.site).isOrigin();
- } catch(e) {
- var msg = "invalid arguments: " + e;
- logger.warn("bad request received: " + msg);
- return httputils.badRequest(res, msg);
- }
-
- var err = wsapi.checkPassword(req.body.pass);
- if (err) {
- logger.warn("invalid password received: " + err);
- return httputils.badRequest(res, err);
- }
-
- db.lastStaged(req.body.email, function (err, last) {
+ db.lastStaged(req.params.email, function (err, last) {
if (err) return wsapi.databaseDown(res, err);
if (last && (new Date() - last) < config.get('min_time_between_emails_ms')) {
- logger.warn('throttling request to stage email address ' + req.body.email + ', only ' +
+ logger.warn('throttling request to stage email address ' + req.params.email + ', only ' +
((new Date() - last) / 1000.0) + "s elapsed");
return httputils.throttled(res, "Too many emails sent to that address, try again later.");
}
- db.emailToUID(req.body.email, function(err, uid) {
+ db.emailToUID(req.params.email, function(err, uid) {
if (err) {
logger.info("reset password fails: " + err);
return res.json({ success: false });
@@ -70,7 +53,7 @@ exports.process = function(req, res) {
wsapi.clearAuthenticatedUser(req.session);
// now bcrypt the password
- wsapi.bcryptPassword(req.body.pass, function (err, hash) {
+ wsapi.bcryptPassword(req.params.pass, function (err, hash) {
if (err) {
if (err.indexOf('exceeded') != -1) {
logger.warn("max load hit, failing on auth request with 503: " + err);
@@ -82,7 +65,7 @@ exports.process = function(req, res) {
// on failure stageEmail may throw
try {
- db.stageEmail(uid, req.body.email, hash, function(err, secret) {
+ db.stageEmail(uid, req.params.email, hash, function(err, secret) {
if (err) return wsapi.databaseDown(res, err);
var langContext = wsapi.langContext(req);
@@ -93,7 +76,7 @@ exports.process = function(req, res) {
res.json({ success: true });
// let's now kick out a verification email!
- email.sendForgotPasswordEmail(req.body.email, req.body.site, secret, langContext);
+ email.sendForgotPasswordEmail(req.params.email, req.params.site, secret, langContext);
});
} catch(e) {
// we should differentiate tween' 400 and 500 here.
View
24 lib/wsapi/stage_reverify.js
@@ -8,7 +8,6 @@ wsapi = require('../wsapi.js'),
httputils = require('../httputils'),
logger = require('../logging.js').logger,
email = require('../email.js'),
-sanitize = require('../sanitize'),
config = require('../configuration');
/* Stage an email for re-verification (i.e. after account password reset).
@@ -17,37 +16,30 @@ config = require('../configuration');
exports.method = 'post';
exports.writes_db = true;
exports.authed = 'assertion';
-exports.args = ['email','site'];
+exports.args = {
+ email: 'email',
+ site: 'origin'
+};
exports.i18n = true;
exports.process = function(req, res) {
- // validate
- try {
- sanitize(req.body.email).isEmail();
- sanitize(req.body.site).isOrigin();
- } catch(e) {
- var msg = "invalid arguments: " + e;
- logger.warn("bad request received: " + msg);
- return httputils.badRequest(res, msg);
- }
-
// Note, we do no throttling of emails in this case. Because this call requires
// authentication, protect a user from themselves could cause more harm than good,
// specifically we would be removing a user available workaround (i.e. a cosmic ray
// hits our email delivery, user doesn't get an email in 30s. User tries again.)
// one may only reverify an email that is owned and unverified
- db.userOwnsEmail(req.session.userid, req.body.email, function(err, owned) {
+ db.userOwnsEmail(req.session.userid, req.params.email, function(err, owned) {
if (err) return res.json({ success: false, reason: err });
if (!owned) return res.json({ success: false, reason: 'you don\'t control that email address' });
- db.emailIsVerified(req.body.email, function(err, verified) {
+ db.emailIsVerified(req.params.email, function(err, verified) {
if (err) return res.json({ success: false, reason: err });
if (verified) return res.json({ success: false, reason: 'email is already verified' });
try {
// on failure stageEmail may throw
- db.stageEmail(req.session.userid, req.body.email, undefined, function(err, secret) {
+ db.stageEmail(req.session.userid, req.params.email, undefined, function(err, secret) {
if (err) return wsapi.databaseDown(res, err);
var langContext = wsapi.langContext(req);
@@ -57,7 +49,7 @@ exports.process = function(req, res) {
res.json({ success: true });
// let's now kick out a verification email!
- email.sendConfirmationEmail(req.body.email, req.body.site, secret, langContext);
+ email.sendConfirmationEmail(req.params.email, req.params.site, secret, langContext);
});
} catch(e) {
// we should differentiate tween' 400 and 500 here.
View
34 lib/wsapi/stage_user.js
@@ -8,7 +8,6 @@ wsapi = require('../wsapi.js'),
httputils = require('../httputils'),
logger = require('../logging.js').logger,
email = require('../email.js'),
-sanitize = require('../sanitize'),
config = require('../configuration');
/* First half of account creation. Stages a user account for creation.
@@ -20,34 +19,21 @@ config = require('../configuration');
exports.method = 'post';
exports.writes_db = true;
exports.authed = false;
-exports.args = ['email','pass','site'];
+exports.args = {
+ 'email': 'email',
+ 'pass': 'password',
+ 'site': 'origin'
+};
exports.i18n = true;
exports.process = function(req, res) {
var langContext = wsapi.langContext(req);
- // validate
- try {
- sanitize(req.body.email).isEmail();
- sanitize(req.body.site).isOrigin();
- if(!req.body.pass) throw "missing pass";
- } catch(e) {
- var msg = "invalid arguments: " + e;
- logger.warn("bad request received: " + msg);
- return httputils.badRequest(res, msg);
- }
-
- var err = wsapi.checkPassword(req.body.pass);
- if (err) {
- logger.warn("invalid password received: " + err);
- return httputils.badRequest(res, err);
- }
-
- db.lastStaged(req.body.email, function (err, last) {
+ db.lastStaged(req.params.email, function (err, last) {
if (err) return wsapi.databaseDown(res, err);
if (last && (new Date() - last) < config.get('min_time_between_emails_ms')) {
- logger.warn('throttling request to stage email address ' + req.body.email + ', only ' +
+ logger.warn('throttling request to stage email address ' + req.params.email + ', only ' +
((new Date() - last) / 1000.0) + "s elapsed");
return httputils.throttled(res, "Too many emails sent to that address, try again later.");
}
@@ -56,7 +42,7 @@ exports.process = function(req, res) {
wsapi.clearAuthenticatedUser(req.session);
// now bcrypt the password
- wsapi.bcryptPassword(req.body.pass, function (err, hash) {
+ wsapi.bcryptPassword(req.params.pass, function (err, hash) {
if (err) {
if (err.indexOf('exceeded') != -1) {
logger.warn("max load hit, failing on auth request with 503: " + err);
@@ -69,7 +55,7 @@ exports.process = function(req, res) {
try {
// upon success, stage_user returns a secret (that'll get baked into a url
// and given to the user), on failure it throws
- db.stageUser(req.body.email, hash, function(err, secret) {
+ db.stageUser(req.params.email, hash, function(err, secret) {
if (err) return wsapi.databaseDown(res, err);
// store the email being registered in the session data
@@ -83,7 +69,7 @@ exports.process = function(req, res) {
res.json({ success: true });
// let's now kick out a verification email!
- email.sendNewUserEmail(req.body.email, req.body.site, secret, langContext);
+ email.sendNewUserEmail(req.params.email, req.params.site, secret, langContext);
});
} catch(e) {
// we should differentiate tween' 400 and 500 here.
View
25 lib/wsapi/update_password.js
@@ -12,27 +12,22 @@ bcrypt = require('../bcrypt');
exports.method = 'post';
exports.writes_db = true;
exports.authed = 'password';
-exports.args = ['oldpass','newpass'];
+exports.args = {
+ oldpass: 'password',
+ newpass: 'password'
+};
exports.i18n = false;
exports.process = function(req, res) {
- var err = wsapi.checkPassword(req.body.newpass);
- if (err) {
- return res.json({
- success:false,
- reason: err
- });
- }
-
db.checkAuth(req.session.userid, function(err, hash) {
if (err) return wsapi.databaseDown(res, err);
- if (typeof hash !== 'string' || typeof req.body.oldpass !== 'string')
+ if (typeof hash !== 'string' || typeof req.params.oldpass !== 'string')
{
return res.json({ success: false });
}
- bcrypt.compare(req.body.oldpass, hash, function (err, success) {
+ bcrypt.compare(req.params.oldpass, hash, function (err, success) {
if (err) {
if (err.indexOf('exceeded') != -1) {
logger.warn("max load hit, failing on auth request with 503: " + err);
@@ -48,22 +43,22 @@ exports.process = function(req, res) {
return res.json({ success: false });
}
- logger.info("updating password for email " + req.session.userid);
- wsapi.bcryptPassword(req.body.newpass, function(err, hash) {
+ logger.info("updating password for user " + req.session.userid);
+ wsapi.bcryptPassword(req.params.newpass, function(err, hash) {
if (err) {
if (err.indexOf('exceeded') != -1) {
logger.warn("max load hit, failing on auth request with 503: " + err);
res.status(503);
return res.json({ success: false, reason: "server is too busy" });
}
- logger.error("error bcrypting password for password update for " + req.body.email, err);
+ logger.error("error bcrypting password for password update for user " + req.session.userid, err);
return res.json({ success: false });
}
db.updatePassword(req.session.userid, hash, function(err) {
var success = true;
if (err) {
- logger.error("error updating bcrypted password for email " + req.body.email, err);
+ logger.error("error updating bcrypted password for user " + req.session.userid, err);
wsapi.databaseDown(res, err);
} else {
res.json({ success: success });
View
8 lib/wsapi/user_creation_status.js
@@ -9,11 +9,13 @@ wsapi = require('../wsapi.js');
exports.method = 'get';
exports.writes_db = false;
exports.authed = false;
-exports.args = ['email'];
+exports.args = {
+ 'email': 'email'
+};
exports.i18n = false;
exports.process = function(req, res) {
- var email = req.query.email;
+ var email = req.params.email;
// if the user is authenticated as the user in question, we're done
if (wsapi.isAuthed(req, 'assertion')) {
@@ -23,7 +25,7 @@ exports.process = function(req, res) {
else notAuthed();
});
} else {
- notAuthed()
+ notAuthed();
}
function notAuthed() {
View
1 package.json
@@ -32,6 +32,7 @@
"uglifycss": "0.0.5",
"underscore": "1.3.1",
"urlparse": "0.0.1",
+ "validator": "0.4.9",
"winston": "0.5.6"
},
"devDependencies": {
View
1 resources/static/test/mocks/xhr.js
@@ -238,7 +238,6 @@ BrowserID.Mocks.xhr = (function() {
}
};
-
return xhr;
}());
View
0 tests/internal-wsapi-test.js 100644 → 100755
File mode changed.
View
6 tests/password-length-test.js
@@ -44,7 +44,7 @@ suite.addBatch({
}),
"causes a HTTP error response": function(err, r) {
assert.equal(r.code, 400);
- assert.equal(r.body, "Bad Request: missing 'pass' argument");
+ assert.strictEqual(JSON.parse(r.body).success, false);
}
},
"a password that is too short": {
@@ -55,7 +55,7 @@ suite.addBatch({
}),
"causes a HTTP error response": function(err, r) {
assert.equal(r.code, 400);
- assert.equal(r.body, "Bad Request: valid passwords are between 8 and 80 chars");
+ assert.equal(JSON.parse(r.body).success, false);
}
},
"a password that is too long": {
@@ -66,7 +66,7 @@ suite.addBatch({
}),
"causes a HTTP error response": function(err, r) {
assert.equal(r.code, 400);
- assert.equal(r.body, "Bad Request: valid passwords are between 8 and 80 chars");
+ assert.equal(JSON.parse(r.body).success, false);
}
},
"but a password that is just right": {
View
2 tests/registration-status-wsapi-test.js
@@ -91,7 +91,7 @@ suite.addBatch({
assert.strictEqual(r.code, 400);
},
"returns an error string": function (err, r) {
- assert.strictEqual(r.body, "Bad Request: missing 'email' argument");
+ assert.strictEqual(JSON.parse(r.body).success, false);
}
}
});
View
181 tests/remove-email-test.js
@@ -0,0 +1,181 @@
+#!/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'),
+email = require('../lib/email.js'),
+jwcrypto = require('jwcrypto');
+
+var suite = vows.describe('forgotten-email');
+
+// algs
+require("jwcrypto/lib/algs/ds");
+require("jwcrypto/lib/algs/rs");
+
+start_stop.addStartupBatches(suite);
+
+// every time a new token is sent out, let's update the global
+// var 'token'
+var token = undefined;
+
+// create a new account via the api with (first address)
+suite.addBatch({
+ "staging an account": {
+ topic: wsapi.post('/wsapi/stage_user', {
+ email: 'first@fakeemail.com',
+ pass: 'firstfakepass',
+ site:'http://localhost:123'
+ }),
+ "works": 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({
+ "create first account": {
+ topic: function() {
+ wsapi.post('/wsapi/complete_user_creation', { token: token }).call(this);
+ },
+ "account created": function(err, r) {
+ assert.equal(r.code, 200);
+ assert.strictEqual(true, JSON.parse(r.body).success);
+ token = undefined;
+ }
+ }
+});
+
+suite.addBatch({
+ "email created": {
+ topic: wsapi.get('/wsapi/user_creation_status', { email: 'first@fakeemail.com' } ),
+ "should exist": function(err, r) {
+ assert.strictEqual(r.code, 200);
+ assert.strictEqual(JSON.parse(r.body).status, "complete");
+ }
+ }
+});
+
+// add a new email address to the account (second address)
+suite.addBatch({
+ "add a new email address to our account": {
+ topic: wsapi.post('/wsapi/stage_email', {
+ email: 'second@fakeemail.com',
+ site:'https://fakesite.foobar.bizbaz.uk'
+ }),
+ "works": 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;
+ }
+ }
+});
+
+// confirm second email email address to the account
+suite.addBatch({
+ "create second account": {
+ topic: function() {
+ wsapi.post('/wsapi/complete_email_confirmation', { token: token }).call(this);
+ },
+ "account created": function(err, r) {
+ assert.equal(r.code, 200);
+ assert.strictEqual(JSON.parse(r.body).success, true);
+ token = undefined;
+ }
+ }
+});
+
+// verify now both email addresses are known
+suite.addBatch({
+ "first email exists": {
+ topic: wsapi.get('/wsapi/have_email', { email: 'first@fakeemail.com' }),
+ "should exist": function(err, r) {
+ assert.strictEqual(JSON.parse(r.body).email_known, true);
+ }
+ },
+ "second email exists": {
+ topic: wsapi.get('/wsapi/have_email', { email: 'second@fakeemail.com' }),
+ "should exist": function(err, r) {
+ assert.strictEqual(JSON.parse(r.body).email_known, true);
+ }
+ },
+ "a random email doesn't exist": {
+ topic: wsapi.get('/wsapi/have_email', { email: 'third@fakeemail.com' }),
+ "shouldn't exist": function(err, r) {
+ assert.strictEqual(JSON.parse(r.body).email_known, false);
+ }
+ }
+});
+
+suite.addBatch({
+ "list emails API": {
+ topic: wsapi.get('/wsapi/list_emails', {}),
+ "succeeds with HTTP 200" : function(err, r) {
+ assert.strictEqual(r.code, 200);
+ },
+ "returns two emails": function(err, r) {
+ r = Object.keys(JSON.parse(r.body));
+ assert.ok(r.indexOf('first@fakeemail.com') != -1);
+ assert.ok(r.indexOf('second@fakeemail.com') != -1);
+ }
+ }
+});
+
+suite.addBatch({
+ "remove email": {
+ topic: wsapi.post('/wsapi/remove_email', { email: 'second@fakeemail.com'}),
+ "succeeds with HTTP 200" : function(err, r) {
+ assert.strictEqual(r.code, 200);
+ }
+ }
+});
+
+suite.addBatch({
+ "list emails API": {
+ topic: wsapi.get('/wsapi/list_emails', {}),
+ "succeeds with HTTP 200" : function(err, r) {
+ assert.strictEqual(r.code, 200);
+ },
+ "returns one emails": function(err, r) {
+ r = Object.keys(JSON.parse(r.body));
+ assert.ok(r.indexOf('first@fakeemail.com') !== -1);
+ assert.ok(r.indexOf('second@fakeemail.com') === -1);
+ }
+ }
+});
+
+start_stop.addShutdownBatches(suite);
+
+// run or export the suite.
+if (process.argv[1] === __filename) suite.run();
+else suite.export(module);
View
8 tests/stalled-mysql-test.js
@@ -121,15 +121,15 @@ suite.addBatch({
},
"complete_email_confirmation": {
topic: wsapi.post('/wsapi/complete_email_confirmation', {
- token: 'bogus'
+ token: 'bogusbogusbogusbogusbogusbogusbogusbogusbogusbog'
}),
"fails with 503": function(err, r) {
assert.strictEqual(r.code, 503);
}
},
"complete_user_creation": {
topic: wsapi.post('/wsapi/complete_user_creation', {
- token: 'bogus',
+ token: 'bogusbogusbogusbogusbogusbogusbogusbogusbogusbog',
pass: 'alsobogus'
}),
"fails with 503": function(err, r) {
@@ -138,7 +138,7 @@ suite.addBatch({
},
"email_for_token": {
topic: wsapi.get('/wsapi/email_for_token', {
- token: 'bogus'
+ token: 'bogusbogusbogusbogusbogusbogusbogusbogusbogusbog'
}),
"fails with 503": function(err, r) {
assert.strictEqual(r.code, 503);
@@ -229,7 +229,7 @@ suite.addBatch({
"cert_key": {
topic: wsapi.post('/wsapi/cert_key', {
email: "test@whatev.er",
- pubkey: "bogus",
+ pubkey: JSON.stringify("bogusbogusbogusbogusbogusbogusbogusbogusbogusbogusbogus"),
ephemeral: false
}),
"fails with 503": function(err, r) {

0 comments on commit b0721b7

Please sign in to comment.