From de00a20ba40d966f33e55238082057e20248d72f Mon Sep 17 00:00:00 2001
From: Shane Tomlinson <stomlinson@mozilla.com>
Date: Mon, 14 May 2012 19:27:14 +0100
Subject: [PATCH] Make users who have not yet set their password by time they
 reach the landing page set their password.

issue #1592
---
 resources/static/css/style.css                |  4 +-
 .../static/pages/verify_secondary_address.js  | 21 ++++-
 .../cases/pages/verify_secondary_address.js   | 85 +++++++++++++++++++
 resources/static/test/mocks/xhr.js            |  1 +
 resources/views/add_email_address.ejs         | 14 +++
 resources/views/verify_email_address.ejs      | 15 ++++
 6 files changed, 136 insertions(+), 4 deletions(-)

diff --git a/resources/static/css/style.css b/resources/static/css/style.css
index 86b0b219f..802295dac 100644
--- a/resources/static/css/style.css
+++ b/resources/static/css/style.css
@@ -669,7 +669,7 @@ h1 {
   margin-bottom: 10px;
 }
 
-.siteinfo, #congrats, .password_entry, .enter_password .hint, #unknown_secondary, #primary_verify, .verify_primary .submit {
+.siteinfo, #congrats, .password_entry, #verify_password, .enter_password .hint, #unknown_secondary, #primary_verify, .verify_primary .submit {
   display: none;
 }
 
@@ -677,7 +677,7 @@ h1 {
   float: left;
 }
 
-.enter_password .password_entry, .known_secondary .password_entry,
+.enter_password .password_entry, .enter_verify_password #verify_password, .known_secondary .password_entry,
 .unknown_secondary #unknown_secondary, .verify_primary #verify_primary {
   display: block;
 }
diff --git a/resources/static/pages/verify_secondary_address.js b/resources/static/pages/verify_secondary_address.js
index 2ccfb0c80..607a5621c 100644
--- a/resources/static/pages/verify_secondary_address.js
+++ b/resources/static/pages/verify_secondary_address.js
@@ -17,6 +17,7 @@ BrowserID.verifySecondaryAddress = (function() {
       validation = bid.Validation,
       token,
       sc,
+      needsPassword,
       mustAuth,
       verifyFunction;
 
@@ -36,7 +37,11 @@ BrowserID.verifySecondaryAddress = (function() {
 
   function submit(oncomplete) {
     var pass = dom.getInner("#password") || undefined,
-        valid = !mustAuth || validation.password(pass);
+        vpass = dom.getInner("#vpassword") || undefined,
+        valid = (!needsPassword ||
+                    validation.passwordAndValidationPassword(pass, vpass))
+             && (!mustAuth ||
+                    validation.password(pass));
 
     if (valid) {
       user[verifyFunction](token, pass, function(info) {
@@ -56,13 +61,25 @@ BrowserID.verifySecondaryAddress = (function() {
       if(info) {
         showRegistrationInfo(info);
 
+        needsPassword = info.needs_password;
         mustAuth = info.must_auth;
 
-        if (mustAuth) {
+        if (needsPassword) {
+          // This is a fix for legacy users who started the user creation
+          // process without setting their password in the dialog.  If the user
+          // needs a password, they must set it now.  Once all legacy users are
+          // verified or their links invalidated, this flow can be removed.
+          dom.addClass("body", "enter_password");
+          dom.addClass("body", "enter_verify_password");
+          complete(oncomplete, true);
+        }
+        else if (mustAuth) {
+          // These are users who have set their passwords inside of the dialog.
           dom.addClass("body", "enter_password");
           complete(oncomplete, true);
         }
         else {
+          // These are users who do not have to set their passwords at all.
           submit(oncomplete);
         }
       }
diff --git a/resources/static/test/cases/pages/verify_secondary_address.js b/resources/static/test/cases/pages/verify_secondary_address.js
index a770237db..d7eacaa57 100644
--- a/resources/static/test/cases/pages/verify_secondary_address.js
+++ b/resources/static/test/cases/pages/verify_secondary_address.js
@@ -116,6 +116,7 @@
     xhr.useResult("mustAuth");
     createController(config, function() {
       xhr.useResult("valid");
+      testHasClass("body", "enter_password");
       controller.submit(function(status) {
         equal(status, true, "correct status");
         testHasClass("body", "complete");
@@ -134,4 +135,88 @@
     });
   });
 
+  asyncTest("must set password, successful login", function() {
+    xhr.useResult("needsPassword");
+    createController(config, function() {
+      xhr.useResult("valid");
+
+      $("#password").val("password");
+      $("#vpassword").val("password");
+
+      testHasClass("body", "enter_password");
+      testHasClass("body", "enter_verify_password");
+
+      controller.submit(function(status) {
+        equal(status, true, "correct status");
+        testHasClass("body", "complete");
+        start();
+      });
+    });
+  });
+
+  asyncTest("must set password, too short a password", function() {
+    xhr.useResult("needsPassword");
+    createController(config, function() {
+      xhr.useResult("valid");
+
+      $("#password").val("pass");
+      $("#vpassword").val("pass");
+
+      controller.submit(function(status) {
+        equal(status, false, "correct status");
+        testHelpers.testTooltipVisible();
+        start();
+      });
+    });
+  });
+
+  asyncTest("must set password, too long a password", function() {
+    xhr.useResult("needsPassword");
+    createController(config, function() {
+      xhr.useResult("valid");
+
+      var pass = testHelpers.generateString(81);
+      $("#password").val(pass);
+      $("#vpassword").val(pass);
+
+      controller.submit(function(status) {
+        equal(status, false, "correct status");
+        testHelpers.testTooltipVisible();
+        start();
+      });
+    });
+  });
+
+  asyncTest("must set password, missing verification password", function() {
+    xhr.useResult("needsPassword");
+    createController(config, function() {
+      xhr.useResult("valid");
+
+      $("#password").val("password");
+      $("#vpassword").val("");
+
+      controller.submit(function(status) {
+        equal(status, false, "correct status");
+        testHelpers.testTooltipVisible();
+        start();
+      });
+    });
+  });
+
+  asyncTest("must set password, mismatched passwords", function() {
+    xhr.useResult("needsPassword");
+    createController(config, function() {
+      xhr.useResult("valid");
+
+      $("#password").val("password");
+      $("#vpassword").val("password1");
+
+      controller.submit(function(status) {
+        equal(status, false, "correct status");
+        testHelpers.testTooltipVisible();
+        start();
+      });
+    });
+  });
+
 }());
diff --git a/resources/static/test/mocks/xhr.js b/resources/static/test/mocks/xhr.js
index 8b505f997..12c612251 100644
--- a/resources/static/test/mocks/xhr.js
+++ b/resources/static/test/mocks/xhr.js
@@ -34,6 +34,7 @@ BrowserID.Mocks.xhr = (function() {
       "get /wsapi/session_context contextAjaxError": undefined,
       "get /wsapi/email_for_token?token=token valid": { email: "testuser@testuser.com" },
       "get /wsapi/email_for_token?token=token mustAuth": { email: "testuser@testuser.com", must_auth: true },
+      "get /wsapi/email_for_token?token=token needsPassword": { email: "testuser@testuser.com", needs_password: true },
       "get /wsapi/email_for_token?token=token invalid": { success: false },
       "post /wsapi/authenticate_user valid": { success: true, userid: 1 },
       "post /wsapi/authenticate_user invalid": { success: false },
diff --git a/resources/views/add_email_address.ejs b/resources/views/add_email_address.ejs
index fa6fbd891..45b711cec 100644
--- a/resources/views/add_email_address.ejs
+++ b/resources/views/add_email_address.ejs
@@ -31,6 +31,20 @@
                       <%= gettext('Password must be between 8 and 80 characters long.') %>
                     </div>
                 </li>
+
+                <li class="password_entry" id="verify_password">
+                    <label class="serif" for="vpassword"><%= gettext('Verify Password') %></label>
+                    <input class="sans" id="vpassword" placeholder="<%= gettext('Repeat Password') %>" type="password" maxlength="80">
+
+                    <div id="vpassword_required" class="tooltip" for="vpassword">
+                      <%= gettext('Verification password is required.') %>
+                    </div>
+
+                    <div class="tooltip" id="passwords_no_match" for="vpassword">
+                      <%= gettext ('Passwords do not match.') %>
+                    </div>
+
+                </li>
             </ul>
 
             <div class="submit cf password_entry">
diff --git a/resources/views/verify_email_address.ejs b/resources/views/verify_email_address.ejs
index a73c80ead..1f275d3fe 100644
--- a/resources/views/verify_email_address.ejs
+++ b/resources/views/verify_email_address.ejs
@@ -18,6 +18,7 @@
                     <label class="serif" for="email"><%= gettext('Email Address') %></label>
                     <input class="youraddress sans" id="email" placeholder="<%= gettext('Your Email') %>" type="email" value="" disabled="disabled" maxlength="254" />
                 </li>
+
                 <li class="password_entry">
                     <label class="serif" for="password"><%= gettext('Password') %></label>
                     <input class="sans" id="password" placeholder="<%= gettext('Your Password') %>" type="password" autofocus maxlength=80 />
@@ -30,6 +31,20 @@
                       <%= gettext('Password must be between 8 and 80 characters long.') %>
                     </div>
                 </li>
+
+                <li class="password_entry" id="verify_password">
+                    <label class="serif" for="vpassword"><%= gettext('Verify Password') %></label>
+                    <input class="sans" id="vpassword" placeholder="<%= gettext('Repeat Password') %>" type="password" maxlength="80">
+
+                    <div id="vpassword_required" class="tooltip" for="vpassword">
+                      <%= gettext('Verification password is required.') %>
+                    </div>
+
+                    <div class="tooltip" id="passwords_no_match" for="vpassword">
+                      <%= gettext ('Passwords do not match.') %>
+                    </div>
+
+                </li>
             </ul>
 
             <div class="submit cf password_entry">
-- 
GitLab