From 23910ac3a644e4a2cd5c47d527d1f6280de0c405 Mon Sep 17 00:00:00 2001
From: Lloyd Hilaiel <lloyd@hilaiel.com>
Date: Tue, 15 May 2012 15:01:16 -0600
Subject: [PATCH] issue #1592 - update complete_user_creation to accept a
 password in the case that email verification was started on a previous
 version of code.

---
 lib/wsapi/complete_user_creation.js | 79 +++++++++++++++++++++++++----
 1 file changed, 68 insertions(+), 11 deletions(-)

diff --git a/lib/wsapi/complete_user_creation.js b/lib/wsapi/complete_user_creation.js
index e507e0f95..9be9daae5 100644
--- a/lib/wsapi/complete_user_creation.js
+++ b/lib/wsapi/complete_user_creation.js
@@ -28,6 +28,16 @@ exports.process = function(req, res) {
   // and then control a browserid account that they can use to prove they own
   // the email address of the attacked.
 
+  // TRANSITIONAL CODE COMMENT
+  // for issue 1000 we moved initial password selection to the browserid dialog (from
+  // the verification page).  Rolling out this change causes some temporal pain.
+  // Outstannding verification links sent before the change was deployed will have
+  // new user requests without passwords.  When the verification page is loaded for
+  // these links, we prompt the user for a password.  That password is sent up with
+  // the request.  this code and comment should all be purged after the new code
+  // has been in production for 2 weeks.
+  // END TRANSITIONAL CODE COMMENT
+
   // is this the same browser?
   if (typeof req.session.pendingCreation === 'string' &&
       req.body.token === req.session.pendingCreation) {
@@ -36,10 +46,18 @@ exports.process = function(req, res) {
   // is a password provided?
   else if (typeof req.body.pass === 'string') {
     return db.authForVerificationSecret(req.body.token, function(err, hash) {
+      // TRANSITIONAL CODE
+      // if hash is null, no password was provided during verification and
+      // this is an old-style verification.  We accept the password and will
+      // update it after the verification is complete.
+      if (err == 'no password for user' || !hash) return postAuthentication();
+      // END TRANSITIONAL CODE
+
       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) {
         if (err) {
           logger.warn("max load hit, failing on auth request with 503: " + err);
@@ -65,19 +83,58 @@ exports.process = function(req, res) {
 
       if (!known) return res.json({ success: false} );
 
-      db.gotVerificationSecret(req.body.token, 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 });
+      // TRANSITIONAL CODE
+      // user is authorized (1 or 2 above) OR user has no password set, in which
+      // case for a short time we'll accept the password provided with the verification
+      // link, and set it as theirs.
+      var transitionalPassword = null;
+
+      db.authForVerificationSecret(req.body.token, function(err, hash) {
+        if (err == 'no password for user' || !hash) {
+          if (!req.body.pass) return httputils.authRequired(res, "password required");
+          var err = wsapi.checkPassword(req.body.pass);
+          if (err) {
+            logger.warn("invalid password received: " + err);
+            return httputils.badRequest(res, err);
+          }
+          transitionalPassword = req.body.pass;
         }
+        completeCreation();
       });
+      // END TRANSITIONAL CODE
+
+      function completeCreation() {
+        db.gotVerificationSecret(req.body.token, 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 });
+
+            // TRANSITIONAL CODE
+            if (transitionalPassword) {
+              wsapi.bcryptPassword(transitionalPassword, function(err, hash) {
+                if (err) {
+                  logger.warn("couldn't bcrypt pass for old verification link: " + err);
+                  return;
+                }
+
+                db.updatePassword(uid, hash, function(err) {
+                  if (err) {
+                    logger.warn("couldn't bcrypt pass for old verification link: " + err);
+                  }
+                });
+              });
+            }
+            // END TRANSITIONAL CODE
+          }
+        });
+      }
     });
   }
 };
-- 
GitLab