From 85d9446c993bb86e6aeef91377fc9740954eb7a7 Mon Sep 17 00:00:00 2001 From: Shane Tomlinson <stomlinson@mozilla.com> Date: Mon, 16 Apr 2012 11:40:48 +0100 Subject: [PATCH] Updates to set the password in dialog on account creation, add secondary email to account. * Update the "stage_user" and "stage_email" to take a password where appropriate. * Remove pass from calls to "complete_user_creation" and "complete_email_addition" * Update database drivers to set the account password where appropriate. * Update unit tests. --- lib/db/json.js | 21 ++++- lib/db/mysql.js | 33 +++++--- lib/wsapi/complete_email_addition.js | 14 ++-- lib/wsapi/complete_user_creation.js | 41 +++------ lib/wsapi/stage_email.js | 69 ++++++++++++---- lib/wsapi/stage_user.js | 59 ++++++++----- tests/add-email-with-assertion-test.js | 3 +- tests/cert-emails-test.js | 4 +- tests/db-test.js | 10 +-- tests/email-throttling-test.js | 4 +- tests/forgotten-email-test.js | 8 +- tests/list-emails-wsapi-test.js | 3 +- tests/no-cookie-test.js | 3 +- tests/password-bcrypt-update-test.js | 4 +- tests/password-length-test.js | 61 +++++--------- tests/password-update-test.js | 1 + tests/primary-then-secondary-test.js | 105 +++++++++++------------- tests/registration-status-wsapi-test.js | 6 +- tests/session-context-test.js | 6 +- tests/session-duration-test.js | 4 +- tests/session-prolong-test.js | 4 +- tests/stalled-mysql-test.js | 9 +- tests/verifier-test.js | 6 +- 23 files changed, 264 insertions(+), 214 deletions(-) diff --git a/lib/db/json.js b/lib/db/json.js index b9a4c6408..50bac38e2 100644 --- a/lib/db/json.js +++ b/lib/db/json.js @@ -175,13 +175,14 @@ function addEmailToAccount(userID, email, type, cb) { }); } -exports.stageUser = function(email, cb) { +exports.stageUser = function(email, hash, cb) { secrets.generate(48, function(secret) { // overwrite previously staged users sync(); db.staged[secret] = { type: "add_account", email: email, + passwd: hash, when: (new Date()).getTime() }; db.stagedEmails[email] = secret; @@ -190,7 +191,7 @@ exports.stageUser = function(email, cb) { }); }; -exports.stageEmail = function(existing_user, new_email, cb) { +exports.stageEmail = function(existing_user, new_email, hash, cb) { secrets.generate(48, function(secret) { // overwrite previously staged users sync(); @@ -198,6 +199,7 @@ exports.stageEmail = function(existing_user, new_email, cb) { type: "add_email", existing_user: existing_user, email: new_email, + passwd: hash, when: (new Date()).getTime() }; db.stagedEmails[new_email] = secret; @@ -250,7 +252,7 @@ exports.verificationSecretForEmail = function(email, cb) { }, 0); }; -exports.gotVerificationSecret = function(secret, hash, cb) { +exports.gotVerificationSecret = function(secret, cb) { sync(); if (!db.staged.hasOwnProperty(secret)) return cb("unknown secret"); @@ -265,6 +267,7 @@ exports.gotVerificationSecret = function(secret, hash, cb) { var emailVal = {}; emailVal[o.email] = { type: 'secondary' }; var uid = getNextUserID(); + var hash = o.passwd; db.users.push({ id: uid, password: hash, @@ -293,7 +296,17 @@ exports.gotVerificationSecret = function(secret, hash, cb) { exports.emailKnown(o.email, function(err, known) { function addIt() { addEmailToAccount(o.existing_user, o.email, 'secondary', function(e) { - cb(e, o.email, o.existing_user); + var hash = o.passwd; + if(e || hash === null) return cb(e, o.email, o.existing_user); + + // a hash was specified, update the password for the user + exports.emailToUID(o.email, function(err, uid) { + if(err) return cb(err, o.email, o.existing_user); + + exports.updatePassword(uid, hash, function(err) { + cb(err || null, o.email, o.existing_user); + }); + }); }); } if (known) { diff --git a/lib/db/mysql.js b/lib/db/mysql.js index b984dcbf4..0af86c661 100644 --- a/lib/db/mysql.js +++ b/lib/db/mysql.js @@ -24,6 +24,7 @@ * | bool new_acct | * | int existing_user | * |*string email | + * |*string passwd | * | timestamp ts | * +------------------------+ */ @@ -78,6 +79,7 @@ const schemas = [ "new_acct BOOL NOT NULL," + "existing_user BIGINT," + "email VARCHAR(255) UNIQUE NOT NULL," + + "passwd CHAR(64)," + "ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL," + "FOREIGN KEY existing_user_fkey (existing_user) REFERENCES user(id)" + ") ENGINE=InnoDB;", @@ -240,12 +242,12 @@ exports.lastStaged = function(email, cb) { ); }; -exports.stageUser = function(email, cb) { +exports.stageUser = function(email, hash, cb) { secrets.generate(48, function(secret) { // overwrite previously staged users - client.query('INSERT INTO staged (secret, new_acct, email) VALUES(?,TRUE,?) ' + + client.query('INSERT INTO staged (secret, new_acct, email, passwd) VALUES(?,TRUE,?,?) ' + 'ON DUPLICATE KEY UPDATE secret=VALUES(secret), existing_user=NULL, new_acct=TRUE, ts=NOW()', - [ secret, email ], + [ secret, email, hash ], function(err) { cb(err, err ? undefined : secret); }); @@ -334,7 +336,7 @@ function addEmailToUser(userID, email, type, cb) { } -exports.gotVerificationSecret = function(secret, hash, cb) { +exports.gotVerificationSecret = function(secret, cb) { client.query( "SELECT * FROM staged WHERE secret = ?", [ secret ], function(err, rows) { @@ -349,6 +351,7 @@ exports.gotVerificationSecret = function(secret, hash, cb) { client.query("DELETE LOW_PRIORITY FROM staged WHERE secret = ?", [ secret ]); if (o.new_acct) { + var hash = o.passwd; // we're creating a new account, add appropriate entries into user and email tables. client.query( "INSERT INTO user(passwd) VALUES(?)", @@ -362,17 +365,25 @@ exports.gotVerificationSecret = function(secret, hash, cb) { // address. the new schema has an 'existing_user' field which is a userid. // this is transitional code so outstanding verification links continue working // and can be removed in feb 2012 some time. maybe for valentines day? - if (typeof o.existing_user === 'number') doAddEmail(o.existing_user); + if (typeof o.existing_user === 'number') doAddEmailSetPassword(o.existing_user); else if (typeof o.existing === 'string') { exports.emailToUID(o.existing, function(uid) { if (err || uid === undefined) return cb('acct associated with staged email doesn\'t exist'); - doAddEmail(uid); + doAddEmailSetPassword(uid); }); } - function doAddEmail(uid) { + function doAddEmailSetPassword(uid) { // we're adding an email address to an existing user account. add appropriate entries into // email table - addEmailToUser(uid, o.email, 'secondary', cb); + var hash = o.passwd; + if (hash) { + exports.updatePassword(uid, hash, function(err) { + if (err) return cb('could not set user\'s password'); + addEmailToUser(uid, o.email, 'secondary', cb); + }); + } else { + addEmailToUser(uid, o.email, 'secondary', cb); + } } }; } @@ -420,12 +431,12 @@ exports.userOwnsEmail = function(uid, email, cb) { }); } -exports.stageEmail = function(existing_user, new_email, cb) { +exports.stageEmail = function(existing_user, new_email, hash, cb) { secrets.generate(48, function(secret) { // overwrite previously staged users - client.query('INSERT INTO staged (secret, new_acct, existing_user, email) VALUES(?,FALSE,?,?) ' + + client.query('INSERT INTO staged (secret, new_acct, existing_user, email, passwd) VALUES(?,FALSE,?,?,?) ' + 'ON DUPLICATE KEY UPDATE secret=VALUES(secret), existing_user=VALUES(existing_user), new_acct=FALSE, ts=NOW()', - [ secret, existing_user, new_email ], + [ secret, existing_user, new_email, hash ], function(err) { cb(err, err ? undefined : secret); }); diff --git a/lib/wsapi/complete_email_addition.js b/lib/wsapi/complete_email_addition.js index 6e7dd2a4d..e7c61d66d 100644 --- a/lib/wsapi/complete_email_addition.js +++ b/lib/wsapi/complete_email_addition.js @@ -17,9 +17,6 @@ exports.args = ['token']; exports.i18n = false; 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 db.emailForVerificationSecret(req.body.token, function(err, r) { if (err === 'database unavailable') { return wsapi.databaseDown(res, err); @@ -51,11 +48,12 @@ exports.process = function(req, res) { } else { // now do we need to set the password? if (r.needs_password && req.body.pass) { - // requiring the client to wait until the bcrypt process is complete here - // exacerbates race conditions in front-end code. We'll return success early, - // here, then update the password after the fact. The worst thing that could - // happen is that password update could fail (due to extreme load), and the - // user will have to reset their password. + // requiring the client to wait until the bcrypt process is complete + // here exacerbates race conditions in front-end code. We'll return + // success early, here, then update the password after the fact. + // The worst thing that could happen is that password update could + // fail (due to extreme load), and the user will have to reset + // their password. wsapi.authenticateSession(req.session, uid, 'password'); res.json({ success: true }); diff --git a/lib/wsapi/complete_user_creation.js b/lib/wsapi/complete_user_creation.js index dca109d14..720dd3282 100644 --- a/lib/wsapi/complete_user_creation.js +++ b/lib/wsapi/complete_user_creation.js @@ -11,50 +11,31 @@ logger = require('../logging.js').logger; exports.method = 'post'; exports.writes_db = true; exports.authed = false; -exports.args = ['token','pass']; +exports.args = ['token']; exports.i18n = false; exports.process = function(req, res) { - // issue #155, valid password length is between 8 and 80 chars. - var err = wsapi.checkPassword(req.body.pass); - if (err) return httputils.badRequest(res, err); - // at the time the email verification is performed, we'll clear the pendingCreation // data on the session. delete req.session.pendingCreation; - // We should check to see if the verification secret is valid *before* - // bcrypting the password (which is expensive), to prevent a possible - // DoS attack. db.haveVerificationSecret(req.body.token, function(err, known) { if (err) return wsapi.databaseDown(res, err); if (!known) return res.json({ success: false} ); - // now bcrypt the password - wsapi.bcryptPassword(req.body.pass, function (err, hash) { + db.gotVerificationSecret(req.body.token, function(err, email, uid) { if (err) { - if (err.indexOf('exceeded') != -1) { - logger.warn("max load hit, failing on auth request with 503: " + err); - return httputils.serviceUnavailable("server is too busy"); - } - logger.error("can't bcrypt: " + err); - return res.json({ success: false }); + logger.warn("couldn't complete email verification: " + err); + wsapi.databaseDown(res, err); + } else { + // FIXME: not sure if we want to do this (ba) + // at this point the user has set a password associated with an email address + // that they've verified. We create an authenticated session. + wsapi.authenticateSession(req.session, uid, 'password', + config.get('ephemeral_session_duration_ms')); + res.json({ success: true }); } - - db.gotVerificationSecret(req.body.token, hash, function(err, email, uid) { - if (err) { - logger.warn("couldn't complete email verification: " + err); - wsapi.databaseDown(res, err); - } else { - // FIXME: not sure if we want to do this (ba) - // at this point the user has set a password associated with an email address - // that they've verified. We create an authenticated session. - wsapi.authenticateSession(req.session, uid, 'password', - config.get('ephemeral_session_duration_ms')); - res.json({ success: true }); - } - }); }); }); }; diff --git a/lib/wsapi/stage_email.js b/lib/wsapi/stage_email.js index 6ffe560ee..c11b99823 100644 --- a/lib/wsapi/stage_email.js +++ b/lib/wsapi/stage_email.js @@ -23,6 +23,10 @@ exports.args = ['email','site']; 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(); @@ -42,23 +46,58 @@ exports.process = function(req, res) { return httputils.throttled(res, "Too many emails sent to that address, try again later."); } - try { - // on failure stageEmail may throw - db.stageEmail(req.session.userid, req.body.email, function(err, secret) { - if (err) return wsapi.databaseDown(res, err); + db.checkAuth(req.session.userid, function(err, hash) { + var needs_password = !hash; - var langContext = wsapi.langContext(req); + if (!err && needs_password && !req.body.pass) { + err = "user must choose a password"; + } + if (!err && !needs_password && req.body.pass) { + err = "a password may not be set at this time"; + } + if (!err && needs_password) err = wsapi.checkPassword(req.body.pass); - // store the email being added in session data - req.session.pendingAddition = secret; + if (err) { + logger.info("stage of email fails: " + err); + return res.json({ + success: false, + reason: err + }); + } - res.json({ success: true }); - // let's now kick out a verification email! - email.sendAddAddressEmail(req.body.email, req.body.site, secret, langContext); - }); - } catch(e) { - // we should differentiate tween' 400 and 500 here. - httputils.badRequest(res, e.toString()); - } + if (needs_password) { + wsapi.bcryptPassword(req.body.pass, function(err, hash) { + if (err) { + logger.warn("couldn't bcrypt password during email verification: " + err); + return res.json({ success: false }); + } + completeStage(hash); + }); + } + else { + completeStage(null); + } + + function completeStage(hash) { + try { + // on failure stageEmail may throw + db.stageEmail(req.session.userid, req.body.email, hash, function(err, secret) { + if (err) return wsapi.databaseDown(res, err); + + var langContext = wsapi.langContext(req); + + // store the email being added in session data + req.session.pendingAddition = secret; + + res.json({ success: true }); + // let's now kick out a verification email! + email.sendAddAddressEmail(req.body.email, req.body.site, secret, langContext); + }); + } catch(e) { + // we should differentiate tween' 400 and 500 here. + httputils.badRequest(res, e.toString()); + } + } + }); }); }; diff --git a/lib/wsapi/stage_user.js b/lib/wsapi/stage_user.js index 0408b7e76..6d40868a6 100644 --- a/lib/wsapi/stage_user.js +++ b/lib/wsapi/stage_user.js @@ -19,7 +19,7 @@ sanitize = require('../sanitize'); exports.method = 'post'; exports.writes_db = true; exports.authed = false; -exports.args = ['email','site']; +exports.args = ['email','pass','site']; exports.i18n = true; exports.process = function(req, resp) { @@ -32,12 +32,19 @@ exports.process = function(req, resp) { 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(resp, msg); } + var err = wsapi.checkPassword(req.body.pass); + if (err) { + logger.warn("invalid password received: " + err); + return httputils.badRequest(resp, err); + } + db.lastStaged(req.body.email, function (err, last) { if (err) return wsapi.databaseDown(resp, err); @@ -47,28 +54,40 @@ exports.process = function(req, resp) { return httputils.throttled(resp, "Too many emails sent to that address, try again later."); } - 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, function(err, secret) { - if (err) return wsapi.databaseDown(resp, err); + // now bcrypt the password + wsapi.bcryptPassword(req.body.pass, function (err, hash) { + if (err) { + if (err.indexOf('exceeded') != -1) { + logger.warn("max load hit, failing on auth request with 503: " + err); + return httputils.serviceUnavailable("server is too busy"); + } + logger.error("can't bcrypt: " + err); + return res.json({ success: false }); + } - // store the email being registered in the session data - if (!req.session) req.session = {}; + 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) { + if (err) return wsapi.databaseDown(resp, err); - // store the secret we're sending via email in the users session, as checking - // that it still exists in the database is the surest way to determine the - // status of the email verification. - req.session.pendingCreation = secret; + // store the email being registered in the session data + if (!req.session) req.session = {}; - resp.json({ success: true }); + // store the secret we're sending via email in the users session, as checking + // that it still exists in the database is the surest way to determine the + // status of the email verification. + req.session.pendingCreation = secret; - // let's now kick out a verification email! - email.sendNewUserEmail(req.body.email, req.body.site, secret, langContext); - }); - } catch(e) { - // we should differentiate tween' 400 and 500 here. - httputils.badRequest(resp, e.toString()); - } + resp.json({ success: true }); + + // let's now kick out a verification email! + email.sendNewUserEmail(req.body.email, req.body.site, secret, langContext); + }); + } catch(e) { + // we should differentiate tween' 400 and 500 here. + httputils.badRequest(resp, e.toString()); + } + }); }); }; diff --git a/tests/add-email-with-assertion-test.js b/tests/add-email-with-assertion-test.js index aca86c04c..19a58f0ae 100755 --- a/tests/add-email-with-assertion-test.js +++ b/tests/add-email-with-assertion-test.js @@ -112,6 +112,7 @@ suite.addBatch({ "stage an account": { topic: wsapi.post('/wsapi/stage_user', { email: TEST_FIRST_ACCT, + pass: 'fakepass', site:'http://fakesite.com:652' }), "works": function(err, r) { @@ -126,7 +127,7 @@ suite.addBatch({ }, "can be used": { topic: function(token) { - wsapi.post('/wsapi/complete_user_creation', { token: token, pass: 'fakepass' }).call(this); + wsapi.post('/wsapi/complete_user_creation', { token: token }).call(this); }, "to verify email ownership": function(err, r) { assert.equal(r.code, 200); diff --git a/tests/cert-emails-test.js b/tests/cert-emails-test.js index 3bfba17e5..2e79ad0fc 100755 --- a/tests/cert-emails-test.js +++ b/tests/cert-emails-test.js @@ -32,6 +32,8 @@ suite.addBatch({ "staging an account": { topic: wsapi.post('/wsapi/stage_user', { email: 'syncer@somehost.com', + pass: 'fakepass', + // TODO: is this pubkey needed? pubkey: 'fakekey', site:'http://fakesite.com' }), @@ -57,7 +59,7 @@ suite.addBatch({ suite.addBatch({ "verifying account ownership": { topic: function() { - wsapi.post('/wsapi/complete_user_creation', { token: token, pass: 'fakepass' }).call(this); + wsapi.post('/wsapi/complete_user_creation', { token: token }).call(this); }, "works": function(err, r) { assert.equal(r.code, 200); diff --git a/tests/db-test.js b/tests/db-test.js index 65abaffa2..e43237858 100755 --- a/tests/db-test.js +++ b/tests/db-test.js @@ -73,7 +73,7 @@ suite.addBatch({ suite.addBatch({ "stage a user for creation pending verification": { topic: function() { - db.stageUser('lloyd@nowhe.re', this.callback); + db.stageUser('lloyd@nowhe.re', 'biglonghashofapassword', this.callback); }, "staging returns a valid secret": function(err, r) { assert.isNull(err); @@ -125,7 +125,7 @@ suite.addBatch({ suite.addBatch({ "upon receipt of a secret": { topic: function() { - db.gotVerificationSecret(secret, 'fakepasswordhash', this.callback); + db.gotVerificationSecret(secret, this.callback); }, "gotVerificationSecret completes without error": function (err, r) { assert.isNull(err); @@ -164,7 +164,7 @@ suite.addBatch({ }, "the correct password": function(err, r) { assert.isNull(err); - assert.strictEqual(r, "fakepasswordhash"); + assert.strictEqual(r, "biglonghashofapassword"); } } }); @@ -200,7 +200,7 @@ suite.addBatch({ }, "then staging an email": { topic: function(err, uid) { - db.stageEmail(uid, 'lloyd@somewhe.re', this.callback); + db.stageEmail(uid, 'lloyd@somewhe.re', 'biglonghashofapassword', this.callback); }, "yields a valid secret": function(err, secret) { assert.isNull(err); @@ -215,7 +215,7 @@ suite.addBatch({ "makes it visible via isStaged": function(sekret, r) { assert.isTrue(r); }, "lets you verify it": { topic: function(secret, r) { - db.gotVerificationSecret(secret, undefined, this.callback); + db.gotVerificationSecret(secret, this.callback); }, "successfully": function(err, r) { assert.isNull(err); diff --git a/tests/email-throttling-test.js b/tests/email-throttling-test.js index 4cceed04d..9b7e4560c 100755 --- a/tests/email-throttling-test.js +++ b/tests/email-throttling-test.js @@ -24,6 +24,7 @@ suite.addBatch({ "staging a registration": { topic: wsapi.post('/wsapi/stage_user', { email: 'first@fakeemail.com', + pass: 'firstfakepass', site:'https://fakesite.com:443' }), "returns 200": function(err, r) { @@ -49,6 +50,7 @@ suite.addBatch({ "immediately staging another": { topic: wsapi.post('/wsapi/stage_user', { email: 'first@fakeemail.com', + pass: 'firstfakepass', site:'http://fakesite.com:80' }), "is throttled": function(err, r) { @@ -60,7 +62,7 @@ suite.addBatch({ suite.addBatch({ "finishing creating the first account": { topic: function() { - wsapi.post('/wsapi/complete_user_creation', { token: token, pass: 'firstfakepass' }).call(this); + wsapi.post('/wsapi/complete_user_creation', { token: token }).call(this); }, "works": function(err, r) { assert.equal(r.code, 200); diff --git a/tests/forgotten-email-test.js b/tests/forgotten-email-test.js index d08c98dad..c2e6aa382 100755 --- a/tests/forgotten-email-test.js +++ b/tests/forgotten-email-test.js @@ -25,6 +25,7 @@ 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) { @@ -49,7 +50,7 @@ suite.addBatch({ suite.addBatch({ "create first account": { topic: function() { - wsapi.post('/wsapi/complete_user_creation', { token: token, pass: 'firstfakepass' }).call(this); + wsapi.post('/wsapi/complete_user_creation', { token: token }).call(this); }, "account created": function(err, r) { assert.equal(r.code, 200); @@ -137,6 +138,7 @@ suite.addBatch({ "re-stage first account": { topic: wsapi.post('/wsapi/stage_user', { email: 'first@fakeemail.com', + pass: 'secondfakepass', site:'https://otherfakesite.com' }), "works": function(err, r) { @@ -187,7 +189,7 @@ suite.addBatch({ suite.addBatch({ "re-create first email address": { topic: function() { - wsapi.post('/wsapi/complete_user_creation', { token: token, pass: 'secondfakepass' }).call(this); + wsapi.post('/wsapi/complete_user_creation', { token: token }).call(this); }, "account created": function(err, r) { assert.equal(r.code, 200); @@ -196,7 +198,7 @@ suite.addBatch({ } }); -// now we should be able to sign into the first email address with the second +// now we should be able to sign into the first email address with the first // password, and all other combinations should fail suite.addBatch({ "first email, first pass bad": { diff --git a/tests/list-emails-wsapi-test.js b/tests/list-emails-wsapi-test.js index 09fa35775..037bed0b8 100755 --- a/tests/list-emails-wsapi-test.js +++ b/tests/list-emails-wsapi-test.js @@ -27,6 +27,7 @@ suite.addBatch({ "stage an account": { topic: wsapi.post('/wsapi/stage_user', { email: 'syncer@somehost.com', + pass: 'fakepass', site:'https://foobar.fakesite.com' }), "works": function(err, r) { @@ -51,7 +52,7 @@ suite.addBatch({ suite.addBatch({ "verifying account ownership": { topic: function() { - wsapi.post('/wsapi/complete_user_creation', { token: token, pass: 'fakepass' }).call(this); + wsapi.post('/wsapi/complete_user_creation', { token: token }).call(this); }, "works": function(err, r) { assert.equal(r.code, 200); diff --git a/tests/no-cookie-test.js b/tests/no-cookie-test.js index 33a2db02f..1689b2294 100755 --- a/tests/no-cookie-test.js +++ b/tests/no-cookie-test.js @@ -27,6 +27,7 @@ suite.addBatch({ "start registration": { topic: wsapi.post('/wsapi/stage_user', { email: 'first@fakeemail.com', + pass: 'firstfakepass', site:'http://fakesite.com:123' }), "returns 200": function(err, r) { @@ -51,7 +52,7 @@ suite.addBatch({ suite.addBatch({ "completing user creation": { topic: function() { - wsapi.post('/wsapi/complete_user_creation', { token: token, pass: 'firstfakepass' }).call(this); + wsapi.post('/wsapi/complete_user_creation', { token: token }).call(this); }, "works": function(err, r) { assert.equal(r.code, 200); diff --git a/tests/password-bcrypt-update-test.js b/tests/password-bcrypt-update-test.js index df5eae390..033e9109f 100755 --- a/tests/password-bcrypt-update-test.js +++ b/tests/password-bcrypt-update-test.js @@ -46,6 +46,7 @@ suite.addBatch({ "account staging": { topic: wsapi.post('/wsapi/stage_user', { email: TEST_EMAIL, + pass: TEST_PASSWORD, site:'https://fakesite.com' }), "works": function(err, r) { @@ -72,8 +73,7 @@ suite.addBatch({ "setting password": { topic: function() { wsapi.post('/wsapi/complete_user_creation', { - token: token, - pass: TEST_PASSWORD + token: token }).call(this); }, "works just fine": function(err, r) { diff --git a/tests/password-length-test.js b/tests/password-length-test.js index 2294e3b30..dc7a2b022 100755 --- a/tests/password-length-test.js +++ b/tests/password-length-test.js @@ -37,69 +37,50 @@ suite.addBatch({ // first stage the account suite.addBatch({ - "account staging": { + "a password that is non-existent": { topic: wsapi.post('/wsapi/stage_user', { email: 'first@fakeemail.com', site:'https://fakesite.com:123' }), - "works": function(err, r) { - assert.equal(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; + "causes a HTTP error response": function(err, r) { + assert.equal(r.code, 400); + assert.equal(r.body, "Bad Request: missing 'pass' argument"); } - } -}); - - -// create a new account via the api with (first address) -suite.addBatch({ + }, "a password that is too short": { - topic: function() { - wsapi.post('/wsapi/complete_user_creation', { - token: token, - pass: '0123456' // less than 8 chars, invalid - }).call(this) - }, + topic: wsapi.post('/wsapi/stage_user', { + email: 'first@fakeemail.com', + pass: '0123456', // less than 8 chars, invalid + site:'https://fakesite.com:123' + }), "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"); } }, "a password that is too long": { - topic: function() { - wsapi.post('/wsapi/complete_user_creation', { - token: token, - pass: '012345678901234567890123456789012345678901234567890123456789012345678901234567891', // more than 81 chars, invalid. - }).call(this); - }, + topic: wsapi.post('/wsapi/stage_user', { + email: 'first@fakeemail.com', + pass: '012345678901234567890123456789012345678901234567890123456789012345678901234567891', // more than 81 chars, invalid. + site:'https://fakesite.com:123' + }), "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"); } }, "but a password that is just right": { - topic: function() { - wsapi.post('/wsapi/complete_user_creation', { - token: token, - pass: 'ahhh. this is just right.' - }).call(this); - }, + topic: wsapi.post('/wsapi/stage_user', { + email: 'first@fakeemail.com', + pass: 'ahhh. this is just right.', + site:'https://fakesite.com:123' + }), "works just fine": function(err, r) { assert.equal(r.code, 200); } } }); + start_stop.addShutdownBatches(suite); // run or export the suite. diff --git a/tests/password-update-test.js b/tests/password-update-test.js index b04a4951f..1d8caefd0 100755 --- a/tests/password-update-test.js +++ b/tests/password-update-test.js @@ -34,6 +34,7 @@ suite.addBatch({ "account staging": { topic: wsapi.post('/wsapi/stage_user', { email: TEST_EMAIL, + pass: OLD_PASSWORD, site: 'https://fakesite.com:123' }), "works": function(err, r) { diff --git a/tests/primary-then-secondary-test.js b/tests/primary-then-secondary-test.js index 111043961..8f0072aca 100755 --- a/tests/primary-then-secondary-test.js +++ b/tests/primary-then-secondary-test.js @@ -75,7 +75,6 @@ suite.addBatch({ }); var token; - // now we have a new account. let's add a secondary to it suite.addBatch({ "add a new email address to our account": { @@ -83,40 +82,37 @@ suite.addBatch({ email: SECONDARY_EMAIL, site:'https://fakesite.com' }), - "works": function(err, r) { + "fails without a password": function(err, r) { assert.strictEqual(r.code, 200); + assert.strictEqual(JSON.parse(r.body).success, false); }, - "and get a token": { - topic: function() { - start_stop.waitForToken(this.callback); - }, - "successfully": function (t) { - this._token = t; - assert.strictEqual(typeof t, 'string'); + "but with a password": { + topic: wsapi.post('/wsapi/stage_email', { + email: SECONDARY_EMAIL, + pass: TEST_PASS, + site:'https://fakesite.com' + }), + "succeeds": function(err, r) { + assert.strictEqual(r.code, 200); }, - "and complete": { - topic: function(t) { - wsapi.get('/wsapi/email_for_token', { - token: t - }).call(this); + "and get a token": { + topic: function() { + start_stop.waitForToken(this.callback); }, - "we need to set our password": function (err, r) { - r = JSON.parse(r.body); - assert.ok(r.needs_password); + "successfully": function (t) { + this._token = t; + assert.strictEqual(typeof t, 'string'); }, - "with": { - topic: function() { - wsapi.post('/wsapi/complete_email_addition', { token: this._token }).call(this); - }, - "no password fails": function(err, r) { - assert.equal(r.code, 200); - assert.strictEqual(JSON.parse(r.body).success, false); + "and complete": { + topic: function(t) { + wsapi.get('/wsapi/email_for_token', { + token: t + }).call(this); }, - "a password": { + "which then": { topic: function() { wsapi.post('/wsapi/complete_email_addition', { - token: this._token, - pass: TEST_PASS + token: this._token }).call(this); }, "succeeds": function(err, r) { @@ -140,7 +136,6 @@ suite.addBatch({ } }); - // after a small delay, we can authenticate with our password suite.addBatch({ "after a small delay": { @@ -161,41 +156,42 @@ suite.addBatch({ // adding a second secondary will not let us set the password suite.addBatch({ - "add a new email address to our account": { + "add a second secondary to account with": { topic: wsapi.post('/wsapi/stage_email', { email: SECOND_SECONDARY_EMAIL, + pass: TEST_PASS, site:'http://fakesite.com:123' }), - "works": function(err, r) { + "a password fails": function(err, r) { assert.strictEqual(r.code, 200); + var body = JSON.parse(r.body); + assert.strictEqual(body.success, false); + assert.strictEqual(body.reason, 'a password may not be set at this time'); }, - "and get a token": { - topic: function() { - start_stop.waitForToken(this.callback); - }, - "successfully": function (t) { - this._token = t; - assert.strictEqual(typeof t, 'string'); + "but with no password specified": { + topic: wsapi.post('/wsapi/stage_email', { + email: SECOND_SECONDARY_EMAIL, + site:'http://fakesite.com:123' + }), + "succeeds": function(err, r) { + assert.strictEqual(r.code, 200); + assert.strictEqual(JSON.parse(r.body).success, true); }, - "and to complete": { - topic: function(t) { - wsapi.get('/wsapi/email_for_token', { - token: t - }).call(this); + "and get a token": { + topic: function() { + start_stop.waitForToken(this.callback); }, - "we do not need to set our password": function (err, r) { - r = JSON.parse(r.body); - assert.isFalse(r.needs_password); + "successfully": function (t) { + this._token = t; + assert.strictEqual(typeof t, 'string'); }, - "with": { - topic: function() { - wsapi.post('/wsapi/complete_email_addition', { token: this._token, pass: TEST_PASS }).call(this); + "and to complete": { + topic: function(t) { + wsapi.get('/wsapi/email_for_token', { + token: t + }).call(this); }, - "a password fails": function(err, r) { - assert.equal(r.code, 200); - assert.strictEqual(JSON.parse(r.body).success, false); - }, - "no password succeeds": { + "with a token": { topic: function() { wsapi.post('/wsapi/complete_email_addition', { token: this._token @@ -231,11 +227,10 @@ suite.addBatch({ }), "works": function(err, r) { assert.strictEqual(r.code, 200); - }, + } } }); - // shut the server down and cleanup start_stop.addShutdownBatches(suite); diff --git a/tests/registration-status-wsapi-test.js b/tests/registration-status-wsapi-test.js index 9857e9e39..10944b1ae 100755 --- a/tests/registration-status-wsapi-test.js +++ b/tests/registration-status-wsapi-test.js @@ -51,6 +51,7 @@ suite.addBatch({ "start registration": { topic: wsapi.post('/wsapi/stage_user', { email: 'first@fakeemail.com', + pass: 'firstfakepass', site:'https://fakesite.com' }), "returns 200": function(err, r) { @@ -110,7 +111,7 @@ suite.addBatch({ suite.addBatch({ "completing user creation": { topic: function() { - wsapi.post('/wsapi/complete_user_creation', { token: token, pass: 'firstfakepass' }).call(this); + wsapi.post('/wsapi/complete_user_creation', { token: token }).call(this); }, "works": function(err, r) { assert.equal(r.code, 200); @@ -170,6 +171,7 @@ suite.addBatch({ "re-registering an existing email": { topic: wsapi.post('/wsapi/stage_user', { email: 'first@fakeemail.com', + pass: 'secondfakepass', site:'http://secondfakesite.com' }), "yields a HTTP 200": function (err, r) { @@ -206,7 +208,7 @@ suite.addBatch({ suite.addBatch({ "proving email ownership causes account re-creation": { topic: function() { - wsapi.post('/wsapi/complete_user_creation', { token: token, pass: 'secondfakepass' }).call(this); + wsapi.post('/wsapi/complete_user_creation', { token: token }).call(this); }, "and returns a 200 code": function(err, r) { assert.equal(r.code, 200); diff --git a/tests/session-context-test.js b/tests/session-context-test.js index 87a42ac40..6c2fe805a 100755 --- a/tests/session-context-test.js +++ b/tests/session-context-test.js @@ -32,6 +32,7 @@ suite.addBatch({ "account staging": { topic: wsapi.post('/wsapi/stage_user', { email: TEST_EMAIL, + pass: PASSWORD, site: 'https://fakesite.com' }), "works": function(err, r) { @@ -58,8 +59,7 @@ suite.addBatch({ "setting password": { topic: function() { wsapi.post('/wsapi/complete_user_creation', { - token: token, - pass: PASSWORD + token: token }).call(this); }, "works just fine": function(err, r) { @@ -89,7 +89,7 @@ suite.addBatch({ var resp = JSON.parse(r.body); assert.strictEqual(typeof resp.csrf_token, 'string'); var serverTime = new Date(resp.server_time); - assert.ok(new Date() - serverTime < 5000); + assert.ok(new Date() - serverTime < 5000); assert.strictEqual(resp.authenticated, true); assert.strictEqual(resp.auth_level, 'password'); var domainKeyCreation = new Date(resp.domain_key_creation_time); diff --git a/tests/session-duration-test.js b/tests/session-duration-test.js index cedec0f08..232ad6320 100755 --- a/tests/session-duration-test.js +++ b/tests/session-duration-test.js @@ -106,6 +106,7 @@ suite.addBatch({ "account staging": { topic: wsapi.post('/wsapi/stage_user', { email: TEST_EMAIL, + pass: PASSWORD, site: 'http://a.really.fakesite123.com:999' }), "works": function(err, r) { @@ -132,8 +133,7 @@ suite.addBatch({ "setting password": { topic: function() { wsapi.post('/wsapi/complete_user_creation', { - token: token, - pass: PASSWORD + token: token }).call(this); }, "works just fine": function(err, r) { diff --git a/tests/session-prolong-test.js b/tests/session-prolong-test.js index adf193e27..2df5cc4d2 100755 --- a/tests/session-prolong-test.js +++ b/tests/session-prolong-test.js @@ -32,6 +32,7 @@ suite.addBatch({ "account staging": { topic: wsapi.post('/wsapi/stage_user', { email: TEST_EMAIL, + pass: PASSWORD, site: 'http://fakesite.com' }), "works": function(err, r) { @@ -58,8 +59,7 @@ suite.addBatch({ "setting password": { topic: function() { wsapi.post('/wsapi/complete_user_creation', { - token: token, - pass: PASSWORD + token: token }).call(this); }, "works just fine": function(err, r) { diff --git a/tests/stalled-mysql-test.js b/tests/stalled-mysql-test.js index 0bdd2b4b4..864c87225 100755 --- a/tests/stalled-mysql-test.js +++ b/tests/stalled-mysql-test.js @@ -129,8 +129,7 @@ suite.addBatch({ }, "complete_user_creation": { topic: wsapi.post('/wsapi/complete_user_creation', { - token: 'bogus', - pass: 'fakefake' + token: 'bogus' }), "fails with 503": function(err, r) { assert.strictEqual(r.code, 503); @@ -147,6 +146,7 @@ suite.addBatch({ "stage_user": { topic: wsapi.post('/wsapi/stage_user', { email: 'bogus@bogus.edu', + pass: 'a_password', site: 'https://whatev.er' }), "fails with 503": function(err, r) { @@ -176,6 +176,7 @@ suite.addBatch({ "account staging": { topic: wsapi.post('/wsapi/stage_user', { email: "stalltest@whatev.er", + pass: 'a_password', site: 'http://fakesite.com' }), "works": function(err, r) { @@ -195,8 +196,7 @@ suite.addBatch({ "setting password": { topic: function(token) { wsapi.post('/wsapi/complete_user_creation', { - token: token, - pass: "somepass" + token: token }).call(this); }, "works just fine": function(err, r) { @@ -266,6 +266,7 @@ suite.addBatch({ "stage_email": { topic: wsapi.post('/wsapi/stage_email', { email: "test2@whatev.er", + pass: 'a_password', site: "https://foo.com" }), "fails with 503": function(err, r) { diff --git a/tests/verifier-test.js b/tests/verifier-test.js index 09ea03280..efa098a10 100755 --- a/tests/verifier-test.js +++ b/tests/verifier-test.js @@ -41,6 +41,7 @@ suite.addBatch({ "account staging": { topic: wsapi.post('/wsapi/stage_user', { email: TEST_EMAIL, + pass: TEST_PASSWORD, site: TEST_ORIGIN }), "works": function(err, r) { @@ -65,8 +66,7 @@ suite.addBatch({ "setting password and creating the account": { topic: function() { wsapi.post('/wsapi/complete_user_creation', { - token: token, - pass: TEST_PASSWORD + token: token }).call(this); }, "works just fine": function(err, r) { @@ -714,7 +714,7 @@ suite.addBatch({ }); // now verify that assertions from a primary who does not have browserid support -// will fail to verify +// will fail to verify function make_other_issuer_tests(new_style) { var title = "generating an assertion from a cert signed by some other domain with " + (new_style ? "new style" : "old style"); var tests = { -- GitLab