Skip to content
This repository has been archived by the owner on May 10, 2019. It is now read-only.

Commit

Permalink
Merge pull request #1640 from mozilla/issue_1638_required_email_signup
Browse files Browse the repository at this point in the history
Make the in dialog set-password flow work with requiredEmail.
  • Loading branch information
lloyd committed May 24, 2012
2 parents 164b856 + d644d50 commit 50df881
Show file tree
Hide file tree
Showing 10 changed files with 190 additions and 78 deletions.
80 changes: 55 additions & 25 deletions resources/static/dialog/controllers/required_email.js
Expand Up @@ -6,8 +6,7 @@
BrowserID.Modules.RequiredEmail = (function() {
"use strict";

var ANIMATION_TIME = 250,
bid = BrowserID,
var bid = BrowserID,
user = bid.User,
errors = bid.Errors,
helpers = bid.Helpers,
Expand Down Expand Up @@ -80,7 +79,7 @@ BrowserID.Modules.RequiredEmail = (function() {
dialogHelpers.addEmail.call(self, email);
}
else {
dialogHelpers.createUser.call(self, email);
self.close("new_user", { email: email });
}
}

Expand Down Expand Up @@ -118,9 +117,8 @@ BrowserID.Modules.RequiredEmail = (function() {
// the user is not authenticated, all addresses are wiped, meaning
// a user could not be looking at stale data and/or authenticate as
// somebody else.
var emailInfo = user.getStoredEmailKeypair(email);
//alert(auth_level + ' ' + JSON.stringify(emailInfo) + JSON.stringify(options));
if(emailInfo && emailInfo.type === "secondary") {
var storedEmailInfo = user.getStoredEmailKeypair(email);
if(storedEmailInfo && storedEmailInfo.type === "secondary") {
// secondary user, show the password field if they are not
// authenticated to the "password" level.
showTemplate({
Expand All @@ -130,46 +128,78 @@ BrowserID.Modules.RequiredEmail = (function() {
});
ready();
}
else if(emailInfo && emailInfo.cert) {
else if(storedEmailInfo && storedEmailInfo.type === "primary" && storedEmailInfo.cert) {
// primary user with valid cert, user can sign in normally.
primaryInfo = emailInfo;
primaryInfo = storedEmailInfo;
showTemplate({ signin: true, primary: true });
ready();
}
else {
// At this point, there are several possibilities:
// 1) Authenticated primary user who has valid cert.
// 2) Authenticated primary user who has an expired cert.
// 3) Authenticated user who does not control address.
// 4) Unauthenticated user.
// 1) Authenticated primary user who has an expired cert.
// 2) Authenticated user who does not control address.
// 3) Unauthenticated user.
user.addressInfo(email, function(info) {
if(info.type === "primary") primaryInfo = info;

if (info.authed) {
if (info.type === "primary" && info.authed) {
// this is a primary user who is authenticated with their IdP.
// We know the user has control of this address, give them
// a chance to hit "sign in" before we kick them off to the
// primary flow account.
showTemplate({ signin: true, primary: true });
}
else if(emailInfo || info.type === "primary") {
// If there is emailInfo, this is a primary user who has
// control of the address, but whose cert is expired and the user
// is not authenticated with their IdP.
// OR
else if(info.type === "primary" && !info.authed) {
// User who does not control a primary address.

// Kick the user down the primary user flow. User creation and
// addition will be taken care of there.
closePrimaryUser.call(self);
} else if(auth_level || !info.known) {
// user is authenticated, but does not control address
// OR
// address is unknown, make the user verify.
}
else if(info.type === "secondary" && auth_level === "password") {
// address is a secondary that the user does not control.

// user is authenticated to the password level but does not
// control the address, user is adding a secondary address to
// their account. Being authenticated to the password level
// means the account already has a password, the set_password
// step is not necessary. Show the confirmation screen before
// the verification starts.
showTemplate({ verify: true });
}
else {
// We've made it all this way. It is a user who is not logged in
// at all and the address is known. Make the user log in.
else if(info.type === "secondary" && auth_level === "assertion") {
// address is a secondary that the user does not control. At
// this point, we need to know whether the account has a password
// or not.

// If the account does not have a password, kick the user down
// the stage_email flow which will ask to set a password.
// If the account does have a password, show the user
// a confirmation screen before starting the verification. When
// the user confirms ownership of the address, they may be asked
// for their password and their authentication credentials will
// be upgraded to "password" status.
user.passwordNeededToAddSecondaryEmail(function(passwordNeeded) {
if(passwordNeeded) {
self.publish("stage_email", { email: email });
}
else {
showTemplate({ verify: true });
}
});
}
else if(info.type === "secondary" && info.known) {
// address is a known secondary but the user is not logged in.

// Make the user log in.
showTemplate({ signin: true, password: true });
}
else {
// address is an unknown secondary. User is not logged in.

// Create an account. User will have to set their password.
self.close("new_user", { email: email });
}
ready();
}, self.getErrorDialog(errors.addressInfo, ready));
}
Expand Down
3 changes: 2 additions & 1 deletion resources/static/dialog/controllers/set_password.js
Expand Up @@ -35,7 +35,8 @@ BrowserID.Modules.SetPassword = (function() {
options = options || {};

self.renderDialog("set_password", {
password_reset: !!options.password_reset
password_reset: !!options.password_reset,
cancelable: options.cancelable !== false
});

self.click("#cancel", cancel);
Expand Down
2 changes: 1 addition & 1 deletion resources/static/dialog/resources/helpers.js
Expand Up @@ -104,7 +104,7 @@
complete(callback, true);
}
else {
self.publish("add_email_submit_with_secondary", { email: email });
self.publish("stage_email", { email: email });
complete(callback, true);
}
}, self.getErrorDialog(errors.addressInfo, callback));
Expand Down
11 changes: 10 additions & 1 deletion resources/static/dialog/resources/state.js
Expand Up @@ -94,6 +94,10 @@ BrowserID.State = (function() {

handleState("new_user", function(msg, info) {
self.newUserEmail = info.email;

// cancel is disabled if the user is doing the initial password set
// for a requiredEmail.
info.cancelable = !requiredEmail;
startAction(false, "doSetPassword", info);
});

Expand Down Expand Up @@ -347,15 +351,20 @@ BrowserID.State = (function() {
startAction("doAddEmail", info);
});

handleState("add_email_submit_with_secondary", function(msg, info) {
handleState("stage_email", function(msg, info) {
user.passwordNeededToAddSecondaryEmail(function(passwordNeeded) {
if(passwordNeeded) {
self.addEmailEmail = info.email;
// cancel is disabled if the user is doing the initial password set
// for a requiredEmail.
info.cancelable = !requiredEmail;
startAction(false, "doSetPassword", info);
}
else {
startAction(false, "doStageEmail", info);
}

complete(info.complete);
});
});

Expand Down
16 changes: 10 additions & 6 deletions resources/static/dialog/views/set_password.ejs
Expand Up @@ -6,11 +6,13 @@

<div class="form_section" id="set_password">
<ul class="inputs">
<% if(!password_reset) { %>
<li>
<%= gettext("Next, choose a new password you'll use when you sign in with BrowserID.") %>
</li>
<% } %>
<li>
<% if(password_reset || !cancelable) { %>
<%= gettext("Choose a new password you'll use when you sign in with BrowserID.") %>
<% } else { %>
<%= gettext("Next, choose a new password you'll use when you sign in with BrowserID.") %>
<% } %>
</li>


<li>
Expand Down Expand Up @@ -49,6 +51,8 @@
<button tabindex="1" id="<%= password_reset ? "password_reset" : "verify_user" %>">
<%= password_reset ? gettext('reset password') : gettext('verify email') %>
</button>
<a id="cancel" class="action" href="#"><%= gettext('cancel') %></a>
<% if(cancelable) { %>
<a id="cancel" class="action" href="#"><%= gettext('cancel') %></a>
<% } %>
</div>
</div>
32 changes: 16 additions & 16 deletions resources/static/test/cases/controllers/add_email.js
Expand Up @@ -56,46 +56,46 @@
ok($("#newEmail").val(), "testuser@testuser.com", "email prepopulated");
});

asyncTest("addEmail with first valid unknown secondary email - trigger add_email_submit_with_secondary", function() {
asyncTest("addEmail with first valid unknown secondary email - trigger stage_email", function() {
createController();
xhr.useResult("unknown_secondary");

equal($("#addEmail").length, 1, "control rendered correctly");

$("#newEmail").val("unregistered@testuser.com");

register("add_email_submit_with_secondary", function(msg, info) {
equal(info.email, "unregistered@testuser.com", "add_email_submit_with_secondary called with correct email");
register("stage_email", function(msg, info) {
equal(info.email, "unregistered@testuser.com", "stage_email called with correct email");
start();
});

controller.addEmail();
});

asyncTest("addEmail with second valid unknown secondary email - trigger add_email_submit_with_secondary", function() {
asyncTest("addEmail with second valid unknown secondary email - trigger stage_email", function() {
createController();
xhr.useResult("unknown_secondary");

equal($("#addEmail").length, 1, "control rendered correctly");

$("#newEmail").val("unregistered@testuser.com");

register("add_email_submit_with_secondary", function(msg, info) {
equal(info.email, "unregistered@testuser.com", "add_email_submit_with_secondary called with correct email");
register("stage_email", function(msg, info) {
equal(info.email, "unregistered@testuser.com", "stage_email called with correct email");
start();
});

storage.addSecondaryEmail("testuser@testuser.com");
controller.addEmail();
});

asyncTest("addEmail with valid unknown secondary email with leading/trailing whitespace - allows address, triggers add_email_submit_with_secondary", function() {
asyncTest("addEmail with valid unknown secondary email with leading/trailing whitespace - allows address, triggers stage_email", function() {
createController();
xhr.useResult("unknown_secondary");

$("#newEmail").val(" unregistered@testuser.com ");
register("add_email_submit_with_secondary", function(msg, info) {
equal(info.email, "unregistered@testuser.com", "add_email_submit_with_secondary called with correct email");
register("stage_email", function(msg, info) {
equal(info.email, "unregistered@testuser.com", "stage_email called with correct email");
start();
});
controller.addEmail();
Expand All @@ -106,12 +106,12 @@

$("#newEmail").val("unregistered");
var handlerCalled = false;
register("add_email_submit_with_secondary", function(msg, info) {
register("stage_email", function(msg, info) {
handlerCalled = true;
ok(false, "add_email_submit_with_secondary should not be called on invalid email");
ok(false, "stage_email should not be called on invalid email");
});
controller.addEmail(function() {
equal(handlerCalled, false, "the add_email_submit_with_secondary handler should have never been called");
equal(handlerCalled, false, "the stage_email handler should have never been called");
start();
});
});
Expand All @@ -121,8 +121,8 @@

$("#newEmail").val("registered@testuser.com");

register("add_email_submit_with_secondary", function(msg, info) {
ok(false, "unexpected add_email_submit_with_secondary message");
register("stage_email", function(msg, info) {
ok(false, "unexpected stage_email message");
});

// simulate the email being already added.
Expand All @@ -142,8 +142,8 @@
xhr.useResult("known_secondary");

$("#newEmail").val("registered@testuser.com");
register("add_email_submit_with_secondary", function(msg, info) {
equal(info.email, "registered@testuser.com", "add_email_submit_with_secondary called with correct email");
register("stage_email", function(msg, info) {
equal(info.email, "registered@testuser.com", "stage_email called with correct email");
start();
});
controller.addEmail();
Expand Down
56 changes: 46 additions & 10 deletions resources/static/test/cases/controllers/required_email.js
Expand Up @@ -123,15 +123,17 @@
});
});

asyncTest("unknown_secondary: user who is not authenticated - user must verify", function() {
asyncTest("unknown_secondary: user who is not authenticated - kick over to new_user flow", function() {
var email = "unregistered@testuser.com";
xhr.useResult("unknown_secondary");

register("new_user", function(item, info) {
equal(info.email, email, "correct email");
start();
});

createController({
email: email,
ready: function() {
testVerify(email);
}
email: email
});
});

Expand Down Expand Up @@ -311,7 +313,7 @@
});
});

asyncTest("unknown_secondary: user who is authenticated, but email unknown - user sees verify screen", function() {
asyncTest("unknown_secondary: user who is authenticated to password level - user sees verify screen", function() {
xhr.setContextInfo("auth_level", "password");
xhr.useResult("unknown_secondary");

Expand All @@ -326,6 +328,40 @@
});
});

asyncTest("unknown_secondary: user who is authenticated to assertion level, account already has password - user sees verify screen", function() {
xhr.setContextInfo("auth_level", "assertion");
xhr.useResult("unknown_secondary");

storage.addEmail("testuser@testuser.com", { type: "secondary" });

var email = "unregistered@testuser.com";

createController({
email: email,
auth_level: "assertion",
ready: function() {
testVerify(email);
}
});
});

asyncTest("unknown_secondary: user who is authenticated to assertion level, account needs password - stage_email triggered", function() {
xhr.setContextInfo("auth_level", "assertion");
xhr.useResult("unknown_secondary");

var email = "unregistered@testuser.com";

register("stage_email", function(msg, info) {
testHelpers.testObjectValuesEqual(info, { email: email });
start();
});

createController({
email: email,
auth_level: "assertion"
});
});


asyncTest("secondary: signIn of an authenticated user - generates an assertion, redirects to assertion_generated", function() {
xhr.setContextInfo("auth_level", "password");
Expand Down Expand Up @@ -423,18 +459,18 @@

});

asyncTest("verifyAddress of authenticated user, secondary address belongs to another user - redirects to 'add_email_submit_with_secondary'", function() {
asyncTest("verifyAddress of authenticated user, secondary address belongs to another user - redirects to 'stage_email'", function() {
var email = "registered@testuser.com";
xhr.useResult("known_secondary");

testMessageReceived(email, "add_email_submit_with_secondary");
testMessageReceived(email, "stage_email");
});

asyncTest("verifyAddress of authenticated user, unknown address - redirects to 'add_email_submit_with_secondary'", function() {
asyncTest("verifyAddress of authenticated user, unknown address - redirects to 'stage_email'", function() {
var email = "unregistered@testuser.com";
xhr.useResult("unknown_secondary");

testMessageReceived(email, "add_email_submit_with_secondary");
testMessageReceived(email, "stage_email");
});

asyncTest("verifyAddress of un-authenticated user, forgot password - redirect to 'forgot_password'", function() {
Expand Down

0 comments on commit 50df881

Please sign in to comment.