diff --git a/lib/db/json.js b/lib/db/json.js index b9a4c64088258d22a75452e88521af511239dc28..50bac38e283c5180ff96f80ea53225267b918e2d 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 b984dcbf438fd4a6bcaf3cb2c26f0666b66b9228..0af86c66156192da22acd8df2f1b0ae90afc5224 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 6e7dd2a4df4ce2116142fe5603d848ae43f9cccf..e7c61d66d39b071ed954d72cfd22a55667282625 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 dca109d14da9a856d1ab7fc6792ec100a932a37c..720dd3282f073eaad5a0be5a1474e90c729d530e 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 6ffe560eefd98102908f86946076a0643f99d083..c11b99823b0d828b7e3075d9845bd807468eb746 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 0408b7e76a53de41a380254feed56c9ae8282773..6d40868a6f313d0b691bccf2cd90c75f9bf2d905 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 aca86c04c874d112f634a5a0b13d8e1bbb992dbb..19a58f0ae405b8c595101759dea3e47c14d5997e 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 3bfba17e5ef1e0ffe4d14fdca5acd4a66c151937..2e79ad0fce3e76252fb56005340e49c4e81bb5ef 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 65abaffa2926f260fd0157a7ee27dbf9f36d5565..e43237858abf3ea3e8d3ec23ef73a0a0cc7799a1 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 4cceed04d5c83ee9c07d7a4e9bd3d87586d5207a..9b7e4560cfe8149e06d57272d946c9e042d3ee5d 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 d08c98dad2cd25a056a07e14afa70443cdbbb6ef..c2e6aa3823be90f8c8093e013cc5697f09d6e67e 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 09fa35775cd57cbfc53ae6fa6c475dc0839e7bce..037bed0b812225b6618a231c75f94ec25e12bf33 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 33a2db02f237d4a816503b19be3cdfbf01e611f9..1689b22946e2faeca777c710a3a7570b6f908283 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 df5eae39034b6ef26c86fe86f4c3812e491f23ab..033e9109f29269645e38ae45e1c3476881c14616 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 2294e3b306819392fdd67a96c18847ea8a5bfd81..dc7a2b02230b9dfac5438c093f1ab6a4369537a1 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 b04a4951fd7bf0f2a1161a56fc7dc02a92b41c47..1d8caefd0b2dadd92a43e0645403384b32ce69b9 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 11104396191720812da44e195cb6b2020aea0751..8f0072acafef72e6eae0cc8a8030f92855b0af9e 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 9857e9e39a80172b4a8852e79c15ccb3280a7e2d..10944b1ae2388549ddf83fad688ca8fff0996228 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 87a42ac40ce34faf8658bcf6a5b20b68b232eb61..6c2fe805a1c2a519ff1a7a9bccb434f018c06e5a 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 cedec0f0857870940ede1492dfdbad94beae19bc..232ad6320a6f7979a6f5f5d39f800eb377229a9a 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 adf193e2764b7d6adb53aa9d7603a585e1ef0dce..2df5cc4d2a2a7e1ad80cb847f233479389f5e9b5 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 0bdd2b4b4b670b034120f3f47c886d5ced31bac8..864c8722506f3d7e55715394ce5c88e3c2cc356a 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 09ea032801dc5fdff489040a1b281666efdb0a47..efa098a102eec4110daff9b8dc582218e1e2eb6d 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 = {