diff --git a/bin/browserid b/bin/browserid index e1db01419e9a46588e9280758af75b5fdf11cfe9..3e88bd93f1a0194bf0c28d5504d7cec0dfa883dd 100755 --- a/bin/browserid +++ b/bin/browserid @@ -144,6 +144,12 @@ app.use(function(req, res, next) { next(); }); +// if we're not serving minified resources (local dev), then we should add +// .ejs to the mime table so it's properly substituted. issue #1875 +if (!config.get('use_minified_resources')) { + express.static.mime.types['ejs'] = 'text/html'; +} + app.use(express.static(static_root)); // open the databse diff --git a/example/primary/provision.html b/example/primary/provision.html index c8b7cd583c4c38008d9df3181fa2ee3539bbce92..01394cb48057b0a3ac2cdb3863b538686876e991 100644 --- a/example/primary/provision.html +++ b/example/primary/provision.html @@ -9,9 +9,6 @@ <script type="text/javascript" src="/jquery.js"></script> <script type="text/javascript"> - // an alias - var fail = navigator.id.raiseProvisioningFailure; - // begin provisioning! This both gives us indicated to browserid that we're // a well formed provisioning page and gives us the parameters of the provisioning navigator.id.beginProvisioning(function(email, cert_duration) { @@ -22,7 +19,7 @@ $.get('/api/whoami') .success(function(who) { if (user != who) { - return fail('user is not authenticated as target user'); + return navigator.id.raiseProvisioningFailure('user is not authenticated as target user'); } // Awesome! The user is authenticated as who we want to provision. let's @@ -44,13 +41,13 @@ navigator.id.registerCertificate(r.cert); }, error: function(r) { - fail("couldn't certify key"); + navigator.id.raiseProvisioningFailure("couldn't certify key"); } }); }); }) .error(function() { - fail('user is not authenticated'); + navigator.id.raiseProvisioningFailure('user is not authenticated'); }); }); </script> diff --git a/example/primary/sign_in.html b/example/primary/sign_in.html index 51a0eaccae58c47b4ffa31f88220cb0a9b2aec60..838080d2fb96fb83dfe87695538437e7269e1d4d 100644 --- a/example/primary/sign_in.html +++ b/example/primary/sign_in.html @@ -33,37 +33,30 @@ button { line-height: 20px; } </div> <script type="text/javascript" src="jquery.js"></script> +<script type="text/javascript" src="https://login.persona.org/authentication_api.js"></script> <script type="text/javascript"> -function getParameterByName(name) -{ - name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]"); - var regexS = "[\\?&]" + name + "=([^&#]*)"; - var regex = new RegExp(regexS); - var results = regex.exec(window.location.href); - if(results == null) - return ""; - else - return decodeURIComponent(results[1].replace(/\+/g, " ")); -} +var who = null; $(document).ready(function() { try { - var who = getParameterByName("email").replace(/@.*$/, ""); - $('#who').text(who); + navigator.id.beginAuthentication(function(email) { + who = /^([^@]+)@/.exec(email)[1]; + $('#who').text(who); + }); } catch(e) { alert("uh oh: " + e); } $("button").click(function(e) { $.get('/api/login', { user: who }) .success(function(r) { - window.location = getParameterByName('return_to'); + navigator.id.completeAuthentication(); }); }); $("#cancel").click(function(e) { e.preventDefault(); - window.location = getParameterByName('return_to'); + navigator.id.raiseAuthenticationFailure('cancel'); }); }); </script> diff --git a/example/rp/index.html b/example/rp/index.html index 19ddc0533b343d33ef00de82318cc0f11ddf8655..50f5808a8da6400b14728ef5d541909b156e29a3 100644 --- a/example/rp/index.html +++ b/example/rp/index.html @@ -77,9 +77,6 @@ pre { <input type="checkbox" id="returnTo"> <label for="returnTo">Supply returnTo</label><br /> </li> - </li><li> - <input type="text" id="requiredEmail" width="80"> - <label for="requiredEmail">Require a specific email</label><br /> </li> </ul> <button class="assertion">Get an assertion</button> @@ -181,15 +178,6 @@ navigator.id.watch({ $(document).ready(function() { $(".specify button.assertion").click(function() { - var requiredEmail = $.trim($('#requiredEmail').val()); - if (!requiredEmail.length) requiredEmail = undefined; - if (requiredEmail && requiredEmail.indexOf('@') === -1) { - alert('Invalid Email in "Require a specific email" field. Adding @example.com'); - $('#requiredEmail').val(requiredEmail + '@example.com'); - $('#requiredEmail').focus(); - return; - } - $(".specify button.assertion").attr('disabled', 'true'); navigator.id.request({ @@ -198,7 +186,6 @@ $(document).ready(function() { siteName: $('#siteName').attr('checked') ? "Persona Test Relying Party" : undefined, siteLogo: $('#siteLogo').attr('checked') ? "/i/logo.png" : undefined, returnTo: $('#returnTo').attr('checked') ? "/postVerificationReturn.html" : undefined, - requiredEmail: requiredEmail, oncancel: function() { loggit("oncancel"); $(".specify button.assertion").removeAttr('disabled'); diff --git a/lib/configuration.js b/lib/configuration.js index 0c5b68dce08d4963c117b79ced4bf9663e2d4c09..1d0bd7a30883b49af16ba955e5068e698a53a367 100644 --- a/lib/configuration.js +++ b/lib/configuration.js @@ -108,10 +108,6 @@ var conf = module.exports = convict({ format: 'string?', env: 'DATABASE_NAME' }, - password: { - format: 'string?', - env: 'MYSQL_PASSWORD' - }, max_query_time_ms: { format: 'integer = 5000', doc: "The maximum amount of time we'll allow a query to run before considering the database to be sick", diff --git a/lib/static_resources.js b/lib/static_resources.js index 7a6863eab6dc14be472780fe7f44b08d8859fd97..7b14d260da15c063e0260419c81c2420b4c03eb8 100644 --- a/lib/static_resources.js +++ b/lib/static_resources.js @@ -131,6 +131,9 @@ exports.resources = { '/common/css/ie8.css', '/dialog/css/ie8.css' ], + '/production/html5shim.js': [ + '/common/js/lib/html5shim.js' + ], '/production/communication_iframe.js': [ '/common/js/lib/jschannel.js', '/common/js/lib/winchan.js', diff --git a/package.json b/package.json index c1bf949a8a05aba32cba092a7433f35c5c6f2a2b..24a3bdf1e613fdced528e5c488f7be92f699ae34 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "express": "2.5.0", "iconv": "1.1.3", "mustache": "0.3.1-dev", - "jwcrypto": "0.2.2", + "jwcrypto": "0.3.2", "mysql": "0.9.5", "node-gettext": "0.1.1", "node-statsd": "https://github.com/downloads/lloyd/node-statsd/0509f85.tgz", diff --git a/resources/static/authentication_api.js b/resources/static/authentication_api.js index 5bc44cd704cabf66c811e6933da34cc720c4bc3a..ca9b16c49990977dac2d828dacfdf2a9ea669571 100644 --- a/resources/static/authentication_api.js +++ b/resources/static/authentication_api.js @@ -23,7 +23,6 @@ return decodeURIComponent(results[1].replace(/\+/g, " ")); } - if (!navigator.id.beginAuthentication || navigator.id._primaryAPIIsShimmed) { navigator.id.beginAuthentication = function(cb) { if (typeof cb !== 'function') { @@ -34,11 +33,11 @@ }; navigator.id.completeAuthentication = function(cb) { - window.location = getParameterByName('return_to'); + window.location = 'https://login.persona.org/sign_in#AUTH_RETURN'; }; navigator.id.raiseAuthenticationFailure = function(reason) { - window.location = getParameterByName('return_to'); + window.location = 'https://login.persona.org/sign_in#AUTH_RETURN_CANCEL'; }; navigator.id._primaryAPIIsShimmed = true; diff --git a/resources/static/common/css/style.css b/resources/static/common/css/style.css index c29a95bffb2b1b82c387529fbe41b5b656a71e2f..dfd8cd6d3bfbeb86b3bf7b3814a612276db5b020 100644 --- a/resources/static/common/css/style.css +++ b/resources/static/common/css/style.css @@ -415,6 +415,20 @@ footer .help { color: #333; } +.submit { + margin-top: 10px; + line-height: 14px; +} + +.submit > p { + margin-top: 10px; +} + +.tospp { + line-height: 1.2; +} + + #showDevelopment { position: absolute; top: 0; diff --git a/resources/static/common/js/lib/dom-jquery.js b/resources/static/common/js/lib/dom-jquery.js index 6438fec068304658384f58d1e86e2c0c1c309dcb..0faeb1ccc0db906754e3461716bbfd2b8c3bd1dc 100644 --- a/resources/static/common/js/lib/dom-jquery.js +++ b/resources/static/common/js/lib/dom-jquery.js @@ -313,7 +313,7 @@ BrowserID.DOM = ( function() { }, /** - * Show an element/elements + * Show an element * @method show * @param {selector || element} elementToShow */ @@ -322,15 +322,13 @@ BrowserID.DOM = ( function() { }, /** - * Hide an element/elements + * Hide an element * @method hide * @param {selector || element} elementToHide */ hide: function( elementToHide ) { return jQuery( elementToHide ).hide(); } - - }; function isValBased( target ) { diff --git a/resources/static/common/js/lib/micrajax.js b/resources/static/common/js/lib/micrajax.js index f56dbcec347c3ffc1edbbdae17a4e438f9e23951..1bf9522b13ce4025c33f926d76f4fae938403cba 100644 --- a/resources/static/common/js/lib/micrajax.js +++ b/resources/static/common/js/lib/micrajax.js @@ -1,4 +1,4 @@ -/*jshint browsers:true, forin: true, laxbreak: true */ +/*jshint browser:true, forin: true, laxbreak: true */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ @@ -17,12 +17,16 @@ window.Micrajax = (function() { function getXHRObject() { var xhrObject; - if (window.ActiveXObject) { - xhrObject = new ActiveXObject('Microsoft.XMLHTTP'); - } - else if (window.XMLHttpRequest) { + // From http://blogs.msdn.com/b/ie/archive/2011/08/31/browsing-without-plug-ins.aspx + // Best Practice: Use Native XHR, if available + if (window.XMLHttpRequest) { + // If IE7+, Gecko, WebKit: Use native object xhrObject = new XMLHttpRequest(); } + else if (window.ActiveXObject) { + // ...if not, try the ActiveX control + xhrObject = new ActiveXObject('Microsoft.XM/LHTTP'); + } return xhrObject; } diff --git a/resources/static/common/js/modules/page_module.js b/resources/static/common/js/modules/page_module.js index 36aa78c7c29a72aae98d2d6552b08100467c54b2..2afa4634e9e26725e67933e5c2ab0cd20148162e 100644 --- a/resources/static/common/js/modules/page_module.js +++ b/resources/static/common/js/modules/page_module.js @@ -119,6 +119,8 @@ BrowserID.Modules.PageModule = (function() { self.hideError(); self.hideDelay(); + dom.removeClass("body", "rptospp"); + screens.form.show(template, data); dom.focus("input:visible:not(:disabled):eq(0)"); // XXX jQuery. bleck. diff --git a/resources/static/common/js/network.js b/resources/static/common/js/network.js index 752ce427df57ff8903fea6a742ccd7b795b97703..d423f12d32fa4ee7694d5730643c95e17bf64b68 100644 --- a/resources/static/common/js/network.js +++ b/resources/static/common/js/network.js @@ -6,7 +6,8 @@ BrowserID.Network = (function() { "use strict"; - var bid = BrowserID, + var jwcrypto = require("./lib/jwcrypto"), + bid = BrowserID, complete = bid.Helpers.complete, context, server_time, @@ -45,10 +46,7 @@ BrowserID.Network = (function() { setUserID(result.userid); // seed the PRNG - // FIXME: properly abstract this out, probably by exposing a jwcrypto - // interface for randomness - // require("./libs/all").sjcl.random.addEntropy(result.random_seed); - // FIXME: this wasn't doing anything for real, so commenting this out for now + jwcrypto.addEntropy(result.random_seed); } function withContext(cb, onFailure) { diff --git a/resources/static/common/js/user.js b/resources/static/common/js/user.js index f39d3935d9bdae5f9bcdb3cf966f1212769c2318..95b7c0893c34fd7d36fab3942fa6bc589a6ab36f 100644 --- a/resources/static/common/js/user.js +++ b/resources/static/common/js/user.js @@ -264,6 +264,14 @@ BrowserID.User = (function() { return origin; }, + setOriginEmail: function(email) { + storage.site.set(origin, "email", email); + }, + + getOriginEmail: function() { + return storage.site.get(origin, "email"); + }, + /** * Get the hostname for the set origin * @method getHostname diff --git a/resources/static/dialog/css/style.css b/resources/static/dialog/css/style.css index e6b73ab45f00bf649718671635dc162ffb108947..3aa1e6dd335193524e1d94460afd5daaa9cf9488 100644 --- a/resources/static/dialog/css/style.css +++ b/resources/static/dialog/css/style.css @@ -284,24 +284,25 @@ section > .contents { #favicon img { display: block; margin: 0 auto; - max-height: 128px; - max-width: 128px; + max-height: 100px; + max-width: 100px; } #favicon h2, #favicon h3 { white-space: nowrap; text-overflow: ellipsis; - line-height: 1.3; /* the 1.3em is to keep y, g, j, etc from having their bottoms chopped off */ - overflow: hidden; + line-height: 1.3em; /* the 1.3em is to keep y, g, j, etc from having their bottoms chopped off */ + overflow: hidden; /* Use the same overflow for everything, this allows us to show the ellipsis. Trying to set overflow-x only causes a scroll bar to be shown in firefox. */ + margin: 10px 0 0 0; } #favicon h2 { margin: 10px 0 0 0; - font-size: 18px; + font-size: 20px; } #favicon h3 { - font-size: 14px; + font-size: 16px; margin-top: 0; } @@ -380,19 +381,24 @@ div#required_email { } .submit { - margin-top: 10px; color: #333; font-size: 11px; - line-height: 14px; } -.submit > p { - margin-top: 10px; -} .tospp { - color: #787878; - clear: right; + font-size: 11px; + color: #787878; +} + +#rptospp { + display: none; + margin: 10px auto; + max-width: 280px; +} + +.rptospp #rptospp { + display: block; } a.emphasize { @@ -431,6 +437,7 @@ a.emphasize:active { #back { color: #000; border-bottom: 1px dotted; + font-weight: normal; } .submit > button { diff --git a/resources/static/dialog/js/misc/helpers.js b/resources/static/dialog/js/misc/helpers.js index af595a5338e5dba843f45572329d1c648496daf7..23831652fef2a4e8c5807a1f89b2d7b354faf4da 100644 --- a/resources/static/dialog/js/misc/helpers.js +++ b/resources/static/dialog/js/misc/helpers.js @@ -11,7 +11,8 @@ complete = helpers.complete, user = bid.User, tooltip = bid.Tooltip, - errors = bid.Errors; + errors = bid.Errors, + dom = bid.DOM; function animateClose(callback) { var body = $("body"), @@ -135,6 +136,10 @@ }, self.getErrorDialog(errors.addEmail, callback)); } + function showRPTosPP() { + dom.addClass("body", "rptospp"); + } + helpers.Dialog = helpers.Dialog || {}; helpers.extend(helpers.Dialog, { @@ -145,7 +150,8 @@ addSecondaryEmail: addSecondaryEmail, resetPassword: resetPassword, cancelEvent: helpers.cancelEvent, - animateClose: animateClose + animateClose: animateClose, + showRPTosPP: showRPTosPP }); }()); diff --git a/resources/static/dialog/js/misc/state.js b/resources/static/dialog/js/misc/state.js index 032bd85f683c1f9a9698fcecf87af9a9ad2c5af5..dc228b145d1713516c67e615e9d8307266943cf7 100644 --- a/resources/static/dialog/js/misc/state.js +++ b/resources/static/dialog/js/misc/state.js @@ -1,9 +1,11 @@ -/*jshint browser:true, jQuery: true, forin: true, laxbreak:true */ +/*jshint browser:true, jquery: true, forin: true, laxbreak:true */ /*global BrowserID: true */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ BrowserID.State = (function() { + "use strict"; + var bid = BrowserID, storage = bid.Storage, network = bid.Network, @@ -42,8 +44,7 @@ BrowserID.State = (function() { handleState("start", function(msg, info) { self.hostname = info.hostname; - self.privacyURL = info.privacyURL; - self.tosURL = info.tosURL; + self.siteTOSPP = !!(info.privacyPolicy && info.termsOfService); requiredEmail = info.requiredEmail; startAction(false, "doRPInfo", info); @@ -80,8 +81,10 @@ BrowserID.State = (function() { self.email = requiredEmail; startAction("doAuthenticateWithRequiredEmail", { email: requiredEmail, - privacyURL: self.privacyURL, - tosURL: self.tosURL + // New users are handled by either the "new_user" flow or the + // "primary_user" flow. The Persona TOS/PP will be shown to users in + // these stages. + siteTOSPP: self.siteTOSPP && !user.getOriginEmail() }); } else if (authenticated) { @@ -92,8 +95,7 @@ BrowserID.State = (function() { }); handleState("authenticate", function(msg, info) { - info.privacyURL = self.privacyURL; - info.tosURL = self.tosURL; + info.siteTOSPP = self.siteTOSPP; startAction("doAuthenticate", info); }); @@ -104,9 +106,23 @@ BrowserID.State = (function() { // know when we are losing users due to the email verification. mediator.publish("kpi_data", { new_account: true }); - // cancel is disabled if the user is doing the initial password set - // for a requiredEmail. - info.cancelable = !requiredEmail; + _.extend(info, { + // cancel is disabled if the user is doing the initial password set + // for a requiredEmail. + cancelable: !requiredEmail, + + // New users in the requiredEmail flow are sent directly to the + // set_password screen. If this happens, they have not yet seen the + // TOS/PP agreement. + + // Always show the Persona TOS/PP to new requiredEmail users. + personaTOSPP: !!requiredEmail, + + // The site TOS/PP must be shown to new requiredEmail users if there is + // a site TOS/PP + siteTOSPP: !!requiredEmail && self.siteTOSPP + }); + startAction(false, "doSetPassword", info); }); @@ -176,12 +192,24 @@ BrowserID.State = (function() { }); handleState("primary_user_unauthenticated", function(msg, info) { - info = helpers.extend(info, { + // a user who lands here is not authenticated with their identity + // provider. + _.extend(info, { add: !!addPrimaryUser, email: email, requiredEmail: !!requiredEmail, - privacyURL: self.privacyURL, - tosURL: self.tosURL + + // In the requiredEmail flow, a user who is not authenticated with + // their primary will be sent directly to the "you must verify + // with your IdP" screen. + // + // Show the siteTOSPP to all requiredEmail users who have never visited + // the site before. + siteTOSPP: requiredEmail && self.siteTOSPP && !user.getOriginEmail(), + + // Show the persona TOS/PP only to requiredEmail users who are creating + // a new account. + personaTOSPP: requiredEmail && !addPrimaryUser }); if (primaryVerificationInfo) { @@ -218,8 +246,7 @@ BrowserID.State = (function() { handleState("pick_email", function() { startAction("doPickEmail", { origin: self.hostname, - privacyURL: self.privacyURL, - tosURL: self.tosURL + siteTOSPP: self.siteTOSPP && !user.getOriginEmail() }); }); @@ -260,8 +287,12 @@ BrowserID.State = (function() { startAction("doAuthenticateWithRequiredEmail", { email: email, secondary_auth: true, - privacyURL: self.privacyURL, - tosURL: self.tosURL + + // This is a user is already authenticated to the assertion + // level who has chosen a secondary email address from the + // pick_email screen. They would have been shown the + // siteTOSPP there. + siteTOSPP: false }); } else { @@ -359,12 +390,26 @@ BrowserID.State = (function() { redirectToState("email_chosen", info); }); - handleState("add_email", function(msg, info) { - info = helpers.extend(info, { - privacyURL: self.privacyURL, - tosURL: self.tosURL - }); + handleState("reset_password", function(msg, info) { + info = info || {}; + // reset_password says the user has confirmed that they want to + // reset their password. doResetPassword will attempt to invoke + // the create_user wsapi. If the wsapi call is successful, + // the user will be shown the "go verify your account" message. + + // We have to save the staged email address here for when the user + // verifies their account and user_confirmed is called. + self.stagedEmail = info.email; + startAction(false, "doResetPassword", info); + }); + handleState("add_email", function(msg, info) { + // add_email indicates the user wishes to add an email to the account, + // the add_email screen must be displayed. After the user enters the + // email address they wish to add, add_email will trigger + // either 1) primary_user or 2) email_staged. #1 occurs if the email + // address is a primary address, #2 occurs if the address is a secondary + // and the verification email has been sent. startAction("doAddEmail", info); }); @@ -372,9 +417,26 @@ BrowserID.State = (function() { 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; + + _.extend(info, { + // cancel is disabled if the user is doing the initial password set + // for a requiredEmail. + cancelable: !requiredEmail, + + // stage_email is called to add an email to an already existing + // account. Since it is an already existing account, the + // personaTOSPP does not need to be shown. + personaTOSPP: false, + + // requiredEmail users who are adding an email but must set their + // password will be redirected here without seeing any other + // screens. non-requiredEmail users will have already seen the site + // TOS/PP in the pick-email screen if it was necessary. Since + // requiredEmail users may not have seen the screen before, show it + // here if there is no originEmail. + siteTOSPP: self.siteTOSPP && requiredEmail && !user.getOriginEmail() + }); + startAction(false, "doSetPassword", info); } else { diff --git a/resources/static/dialog/js/modules/actions.js b/resources/static/dialog/js/modules/actions.js index 3a95c89155d98f12c71b9f8eb11231f0a1f82bfe..9144b37521a54ad9dae4245493476addd06cde77 100644 --- a/resources/static/dialog/js/modules/actions.js +++ b/resources/static/dialog/js/modules/actions.js @@ -57,10 +57,6 @@ BrowserID.Modules.Actions = (function() { if(data.ready) _.defer(data.ready); }, - doRPInfo: function(info) { - startService("rp_info", info); - }, - doCancel: function() { if(onsuccess) onsuccess(null); }, @@ -156,6 +152,10 @@ BrowserID.Modules.Actions = (function() { doGenerateAssertion: function(info) { startService("generate_assertion", info); + }, + + doRPInfo: function(info) { + startService("rp_info", info); } }); diff --git a/resources/static/dialog/js/modules/add_email.js b/resources/static/dialog/js/modules/add_email.js index da642e0d2ced87d3cdf769b0d2389e0b2ad46e9a..07f6242caf302bed2cb3aaa49ee7416522ffb811 100644 --- a/resources/static/dialog/js/modules/add_email.js +++ b/resources/static/dialog/js/modules/add_email.js @@ -1,5 +1,5 @@ /*jshint browser:true, jQuery: true, forin: true, laxbreak:true */ -/*global _: true, BrowserID: true, PageController: true */ +/*global _: true, BrowserID: true, PageController: true, gettext: true */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ @@ -9,6 +9,7 @@ BrowserID.Modules.AddEmail = (function() { var bid = BrowserID, dom = bid.DOM, helpers = bid.Helpers, + user = bid.User, dialogHelpers = helpers.Dialog, errors = bid.Errors, complete = helpers.complete, @@ -54,12 +55,9 @@ BrowserID.Modules.AddEmail = (function() { var Module = bid.Modules.PageModule.extend({ start: function(options) { var self=this, - templateData = helpers.extend({}, options, { - privacy_url: options.privacyURL || null, - tos_url: options.tosURL || null - }); + originEmail = user.getOriginEmail(); - self.renderDialog("add_email", templateData); + self.renderDialog("add_email", options); hideHint("addressInfo"); self.click("#cancel", cancelAddEmail); diff --git a/resources/static/dialog/js/modules/authenticate.js b/resources/static/dialog/js/modules/authenticate.js index c316e55a5e5822825c2ad2bb69f1b32a6bf106fa..d1c13818901917c5ae93120aa100ce0cc6e579c8 100644 --- a/resources/static/dialog/js/modules/authenticate.js +++ b/resources/static/dialog/js/modules/authenticate.js @@ -33,6 +33,7 @@ BrowserID.Modules.Authenticate = (function() { } else { showHint("start"); + enterEmailState.call(self); complete(info.ready); } } @@ -98,7 +99,7 @@ BrowserID.Modules.Authenticate = (function() { function showHint(showSelector, callback) { _.each(hints, function(className) { if(className != showSelector) { - $("." + className).not("." + showSelector).hide(); + dom.hide("." + className + ":not(." + showSelector + ")"); } }); @@ -112,8 +113,8 @@ BrowserID.Modules.Authenticate = (function() { }); } - function enterEmailState(el) { - if (!$("#email").is(":disabled")) { + function enterEmailState() { + if (!dom.is("#email", ":disabled")) { this.submit = checkEmail; showHint("start"); } @@ -129,6 +130,8 @@ BrowserID.Modules.Authenticate = (function() { showHint("returning", function() { dom.focus("#password"); }); + + complete(callback); } @@ -158,12 +161,19 @@ BrowserID.Modules.Authenticate = (function() { var self=this; self.renderDialog("authenticate", { sitename: user.getHostname(), - email: lastEmail, - privacy_url: options.privacyURL, - tos_url: options.tosURL + email: lastEmail }); - $(".returning,.start").hide(); + dom.hide(".returning,.start"); + + // We have to show the TOS/PP agreements to *all* users here. Users who + // are already authenticated to their IdP but do not have a Persona + // account automatically have an account created with no further + // interaction. To make sure they see the TOS/PP agreement, show it + // here. + if (options.siteTOSPP) { + dialogHelpers.showRPTosPP.call(self); + } self.bind("#email", "keyup", emailKeyUp); self.click("#forgotPassword", forgotPassword); diff --git a/resources/static/dialog/js/modules/dialog.js b/resources/static/dialog/js/modules/dialog.js index 930b7707eeb571d26fa6e2ec81ce267bc8e0fa06..8a4169fa8e5a3467885c6740109beb6b7ff1a9bc 100644 --- a/resources/static/dialog/js/modules/dialog.js +++ b/resources/static/dialog/js/modules/dialog.js @@ -12,6 +12,7 @@ BrowserID.Modules.Dialog = (function() { user = bid.User, errors = bid.Errors, dom = bid.DOM, + helpers = bid.Helpers, win = window, startExternalDependencies = true, channel, @@ -73,10 +74,6 @@ BrowserID.Modules.Dialog = (function() { channel && channel.detach(); } - function setOrigin(origin) { - user.setOrigin(origin); - } - function onWindowUnload() { this.publish("window_unload"); } @@ -139,7 +136,7 @@ BrowserID.Modules.Dialog = (function() { var self=this, hash = win.location.hash; - setOrigin(origin_url); + user.setOrigin(origin_url); if (startExternalDependencies) { @@ -159,9 +156,7 @@ BrowserID.Modules.Dialog = (function() { // verify params try { if (paramsFromRP.requiredEmail) { - if (!bid.verifyEmail(paramsFromRP.requiredEmail)) - throw "invalid requiredEmail: (" + paramsFromRP.requiredEmail + ")"; - params.requiredEmail = paramsFromRP.requiredEmail; + helpers.log("requiredEmail has been deprecated"); } // support old parameter names... @@ -169,8 +164,8 @@ BrowserID.Modules.Dialog = (function() { if (paramsFromRP.privacyURL) paramsFromRP.privacyPolicy = paramsFromRP.privacyURL; if (paramsFromRP.termsOfService && paramsFromRP.privacyPolicy) { - params.tosURL = fixupURL(origin_url, paramsFromRP.termsOfService); - params.privacyURL = fixupURL(origin_url, paramsFromRP.privacyPolicy); + params.termsOfService = fixupURL(origin_url, paramsFromRP.termsOfService); + params.privacyPolicy = fixupURL(origin_url, paramsFromRP.privacyPolicy); } if (paramsFromRP.siteLogo) { @@ -196,24 +191,19 @@ BrowserID.Modules.Dialog = (function() { user.setReturnTo(returnTo); } - - if (hash.indexOf("#CREATE_EMAIL=") === 0) { - var email = hash.replace(/#CREATE_EMAIL=/, ""); - if (!bid.verifyEmail(email)) - throw "invalid #CREATE_EMAIL= (" + email + ")"; + if (hash.indexOf("#AUTH_RETURN") === 0) { + var primaryParams = JSON.parse(win.sessionStorage.primaryVerificationFlow); + params.email = primaryParams.email; + params.add = primaryParams.add; params.type = "primary"; - params.email = email; - params.add = false; - } - else if (hash.indexOf("#ADD_EMAIL=") === 0) { - var email = hash.replace(/#ADD_EMAIL=/, ""); - if (!bid.verifyEmail(email)) - throw "invalid #ADD_EMAIL= (" + email + ")"; - params.type = "primary"; - params.email = email; - params.add = true; + + // FIXME: if it's AUTH_RETURN_CANCEL, we should short-circuit + // the attempt at provisioning. For now, we let provisioning + // be tried and fail. } + // no matter what, we clear the primary flow state for this window + win.sessionStorage.primaryVerificationFlow = undefined; } catch(e) { // note: renderError accepts HTML and cheerfully injects it into a // frame with a powerful origin. So convert 'e' first. diff --git a/resources/static/dialog/js/modules/pick_email.js b/resources/static/dialog/js/modules/pick_email.js index b666c95e90d2d95f26bb9ca71f65695f1f581466..c28a23babeeefe78540ac346f93f96e5212894b1 100644 --- a/resources/static/dialog/js/modules/pick_email.js +++ b/resources/static/dialog/js/modules/pick_email.js @@ -93,10 +93,13 @@ BrowserID.Modules.PickEmail = (function() { self.renderDialog("pick_email", { identities: identities, - siteemail: storage.site.get(origin, "email"), - privacy_url: options.privacyURL, - tos_url: options.tosURL + siteemail: user.getOriginEmail() }); + + if (options.siteTOSPP) { + dialogHelpers.showRPTosPP.call(self); + } + dom.getElements("body").css("opacity", "1"); if (dom.getElements("#selectEmail input[type=radio]:visible").length === 0) { // If there is only one email address, the radio button is never shown, diff --git a/resources/static/dialog/js/modules/required_email.js b/resources/static/dialog/js/modules/required_email.js index 6e5f97e59d25682fbda2022dba60da2a733440e8..037621924d000b0b2c5925f1ad34b22efd58f550 100644 --- a/resources/static/dialog/js/modules/required_email.js +++ b/resources/static/dialog/js/modules/required_email.js @@ -147,7 +147,13 @@ BrowserID.Modules.RequiredEmail = (function() { // 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 }); + + // Show the Persona TOS/PP to any primary user who is authed with + // their IdP but not with Persona. Unfortunately, addressInfo + // does not tell us whether a primary address already has an + // account, so we have to show the personaTOSPP to any user who + // is not authenticated. + showTemplate({ signin: true, primary: true, personaTOSPP: !auth_level }); } else if(info.type === "primary" && !info.authed) { // User who does not control a primary address. @@ -213,12 +219,15 @@ BrowserID.Modules.RequiredEmail = (function() { password: false, secondary_auth: false, primary: false, - privacy_url: options.privacyURL || null, - tos_url: options.tosURL || null + personaTOSPP: false }, templateData); self.renderDialog("required_email", templateData); + if (options.siteTOSPP) { + dialogHelpers.showRPTosPP.call(self); + } + self.click("#sign_in", signIn); self.click("#verify_address", verifyAddress); self.click("#forgotPassword", forgotPassword); diff --git a/resources/static/dialog/js/modules/rp_info.js b/resources/static/dialog/js/modules/rp_info.js index 3bc9b20edd9efb34d9466a815024b6e09c0775e9..55d91d276edf7e861841dc1c3243920fd44f536b 100644 --- a/resources/static/dialog/js/modules/rp_info.js +++ b/resources/static/dialog/js/modules/rp_info.js @@ -7,7 +7,8 @@ /** * Purpose: - * Display to the user RP related data such as hostname, name, and logo. + * Display to the user RP related data such as hostname, sitename, logo, + * TOS/PP, etc. */ BrowserID.Modules.RPInfo = (function() { "use strict"; @@ -32,7 +33,9 @@ BrowserID.Modules.RPInfo = (function() { renderer.render("#rp_info", "rp_info", { hostname: options.hostname, siteName: options.siteName, - siteLogo: options.siteLogo + siteLogo: options.siteLogo, + privacyPolicy: options.privacyPolicy, + termsOfService: options.termsOfService }); sc.start.call(this, options); diff --git a/resources/static/dialog/js/modules/set_password.js b/resources/static/dialog/js/modules/set_password.js index 7bc6848c59663f9f30ea5f8023feb0e97c9cb984..02c6de1038dd03df26c63c2dc377a9708e8af66f 100644 --- a/resources/static/dialog/js/modules/set_password.js +++ b/resources/static/dialog/js/modules/set_password.js @@ -36,9 +36,14 @@ BrowserID.Modules.SetPassword = (function() { self.renderDialog("set_password", { password_reset: !!options.password_reset, - cancelable: options.cancelable !== false + cancelable: options.cancelable !== false, + personaTOSPP: options.personaTOSPP }); + if (options.siteTOSPP) { + dialogHelpers.showRPTosPP.call(self); + } + self.click("#cancel", cancel); sc.start.call(self, options); diff --git a/resources/static/dialog/js/modules/verify_primary_user.js b/resources/static/dialog/js/modules/verify_primary_user.js index 9e38e6be13bfec2f9039451cb860310b7b03a2f3..8a35914e2665c6a07d5814f439da440e5699a752 100644 --- a/resources/static/dialog/js/modules/verify_primary_user.js +++ b/resources/static/dialog/js/modules/verify_primary_user.js @@ -12,21 +12,22 @@ BrowserID.Modules.VerifyPrimaryUser = (function() { add, email, auth_url, + dom = bid.DOM, helpers = bid.Helpers, + dialogHelpers = helpers.Dialog, complete = helpers.complete; function verify(callback) { this.publish("primary_user_authenticating"); - // replace any hashes that may be there already. - var returnTo = win.document.location.href.replace(/#.*$/, ""); - - var type = add ? "ADD_EMAIL" : "CREATE_EMAIL"; - var url = helpers.toURL(auth_url, { - email: email, - return_to: returnTo + "#" + type + "=" +email + // set up some information about what we're doing + win.sessionStorage.primaryVerificationFlow = JSON.stringify({ + add: add, + email: email }); + var url = helpers.toURL(auth_url, {email: email}); + win.document.location = url; complete(callback); @@ -47,12 +48,16 @@ BrowserID.Modules.VerifyPrimaryUser = (function() { email = data.email; auth_url = data.auth_url; - var templateData = helpers.extend({}, data, { + self.renderDialog("verify_primary_user", { + email: data.email, + auth_url: data.auth_url, requiredEmail: data.requiredEmail || false, - privacy_url: data.privacyURL || null, - tos_url: data.tosURL || null + personaTOSPP: data.personaTOSPP }); - self.renderDialog("verify_primary_user", templateData); + + if (data.siteTOSPP) { + dialogHelpers.showRPTosPP.call(self); + } self.click("#cancel", cancel); diff --git a/resources/static/dialog/js/start.js b/resources/static/dialog/js/start.js index 70c574bad3f2e645532c48aee54fdec314b886fc..3536e1f25e1b9f346debb65f5ecc0966f04d0d21 100644 --- a/resources/static/dialog/js/start.js +++ b/resources/static/dialog/js/start.js @@ -15,7 +15,7 @@ network.init(); var hash = window.location.hash || "", - continuation = hash.indexOf("#CREATE_EMAIL") > -1 || hash.indexOf("#ADD_EMAIL") > -1; + continuation = hash.indexOf("#AUTH_RETURN") > -1; moduleManager.register("interaction_data", modules.InteractionData); moduleManager.start("interaction_data", { continuation: continuation }); diff --git a/resources/static/dialog/views/add_email.ejs b/resources/static/dialog/views/add_email.ejs index b98b22ab8708478c0dd7392de90e4bd2a78632eb..15ebbad134b0779d228dc3f5cbb9102a1193fa2f 100644 --- a/resources/static/dialog/views/add_email.ejs +++ b/resources/static/dialog/views/add_email.ejs @@ -34,22 +34,8 @@ </ul> <div class="submit cf"> - <% if (privacy_url && tos_url) { %> - <p class="tospp"> - <%= format( - gettext('By clicking %s, you confirm that you accept this site\'s <a %s>Terms of Use</a> and <a %s>Privacy Policy</a>.'), - [ gettext('add'), - format(' href="%s" target="_new"', [tos_url]), - format(' href="%s" target="_new"', [privacy_url]) - ]) %> - </p> - <p> - <% } %> <button id="addNewEmail"><%= gettext('add') %></button> <a href="#" id="cancel" class="action"><%= gettext('cancel') %></a> - <% if (privacy_url && tos_url) { %> - </p> - <% } %> </div> </div> diff --git a/resources/static/dialog/views/authenticate.ejs b/resources/static/dialog/views/authenticate.ejs index 55e461d5ceaed29e52ef2f148466d2698beb7c7e..a95c45b441638b50424e8ca14dc1dc21df6fa421 100644 --- a/resources/static/dialog/views/authenticate.ejs +++ b/resources/static/dialog/views/authenticate.ejs @@ -58,16 +58,15 @@ <button class="start addressInfo" tabindex="3"><%= gettext('next') %></button> <button class="returning" tabindex="3"><%= gettext('sign in') %></button> </p> - <% if (privacy_url && tos_url) { %> - <p class="tospp"> - <%= format( - gettext('By clicking %s, you confirm that you accept this site\'s <a %s>Terms of Use</a> and <a %s>Privacy Policy</a>.'), - [ gettext('next'), - format(' href="%s" target="_new"', [tos_url]), - format(' href="%s" target="_new"', [privacy_url]) - ]) %> + + <p class="tospp"> + <%= format( + gettext('By proceeding, you agree to %s\'s <a %s>Terms</a> and <a %s>Privacy Policy</a>.'), + [ "Persona", + ' href="https://login.persona.org/tos" target="_new"', + ' href="https://login.persona.org/privacy" target="_new"', + ]) %> </p> - <% } %> </div> </div> diff --git a/resources/static/dialog/views/pick_email.ejs b/resources/static/dialog/views/pick_email.ejs index c189f521055d156ee397366df6d6306a22645f98..059072425b93d6be854125b4290d444f26c019ad 100644 --- a/resources/static/dialog/views/pick_email.ejs +++ b/resources/static/dialog/views/pick_email.ejs @@ -23,18 +23,8 @@ <div class="submit add cf"> - <p class="cf"> - <button id="signInButton"><%= gettext('sign in') %></button> - </p> - <% if (privacy_url && tos_url) { %> - <p class="tospp"> - <%= format( - gettext('By clicking %s, you confirm that you accept this site\'s <a %s>Terms of Use</a> and <a %s>Privacy Policy</a>.'), - [ gettext('sign in'), - format(' href="%s" target="_new"', [tos_url]), - format(' href="%s" target="_new"', [privacy_url]) - ]) %> + <p class="cf"> + <button id="signInButton"><%= gettext('sign in') %></button> </p> - <% } %> </div> </div> diff --git a/resources/static/dialog/views/required_email.ejs b/resources/static/dialog/views/required_email.ejs index a1e0fdace2304f322d97fcac29b3cf65a664c10e..5245319c2e48ee6849561757f1a8d9b7b9e7ef83 100644 --- a/resources/static/dialog/views/required_email.ejs +++ b/resources/static/dialog/views/required_email.ejs @@ -50,17 +50,7 @@ </ul> <div class="submit cf"> - <% if (privacy_url && tos_url) { %> - <p class="tospp"> - <%= format( - gettext('By clicking %s, you confirm that you accept this site\'s <a %s>Terms of Use</a> and <a %s>Privacy Policy</a>.'), - [ gettext('sign in'), - format(' href="%s" target="_new"', [tos_url]), - format(' href="%s" target="_new"', [privacy_url]) - ]) %> - </p> - <p> - <% } %> + <p class="cf"> <% if (signin) { %> <button id="sign_in" tabindex="3"><%= gettext("sign in") %></button> <% } else if (verify) { %> @@ -70,8 +60,16 @@ <% if (secondary_auth) { %> <a href="#" id="cancel" class="action" tabindex="4"><%= gettext("cancel") %></a> <% } %> - <% if (privacy_url && tos_url) { %> </p> - <% } %> + <% if (personaTOSPP) { %> + <p class="tospp"> + <%= format( + gettext('By proceeding, you agree to %s\'s <a %s>Terms</a> and <a %s>Privacy Policy</a>.'), + [ "Persona", + ' href="https://login.persona.org/tos" target="_new"', + ' href="https://login.persona.org/privacy" target="_new"', + ]) %> + </p> + <% } %> </div> </div> diff --git a/resources/static/dialog/views/rp_info.ejs b/resources/static/dialog/views/rp_info.ejs index 5928a18d6c945d726598017558fbb69f1f4d16e1..724a3e1925dfbf9a1c5d58d6e8bfc67278d31c44 100644 --- a/resources/static/dialog/views/rp_info.ejs +++ b/resources/static/dialog/views/rp_info.ejs @@ -11,11 +11,18 @@ <h2 id="rp_name"><%= siteName %></h2> <% } %> -<% if(hostname) { %> - <% if(siteName) { %> - <h3 id="rp_hostname"><%= hostname %></h3> - <% } else { %> - <h2 id="rp_hostname"><%= hostname %></h2> - <% } %> +<% if(siteName) { %> + <h3 id="rp_hostname"><%= hostname %></h3> +<% } else { %> + <h2 id="rp_hostname"><%= hostname %></h2> <% } %> +<% if(privacyPolicy && termsOfService) { %> + <p id="rptospp" class="tospp"> + <%= format(gettext("By proceeding, you agree to %s\'s <a %s>Terms</a> and <a %s>Privacy Policy</a>."), + [ siteName || hostname, + ' href="' + termsOfService + '" id="rp_tos" target="_blank"', + ' href="' + privacyPolicy + '" id="rp_pp" target="_blank"' + ]) %> + </p> +<% } %> diff --git a/resources/static/dialog/views/set_password.ejs b/resources/static/dialog/views/set_password.ejs index d244293925fc8e8f299af68f59774d156bc32e87..c16a1f2d9fc6378601fde28faeefe159cbb416e5 100644 --- a/resources/static/dialog/views/set_password.ejs +++ b/resources/static/dialog/views/set_password.ejs @@ -48,11 +48,23 @@ </ul> <div class="submit cf"> + <p class="cf"> <button id="<%= password_reset ? "password_reset" : "verify_user" %>"> <%= password_reset ? gettext('reset password') : gettext('verify email') %> </button> <% if(cancelable) { %> <a id="cancel" class="action" href="#"><%= gettext('cancel') %></a> <% } %> + </p> + <% if (personaTOSPP) { %> + <p id="persona_tospp" class="tospp"> + <%= format( + gettext('By proceeding, you agree to %s\'s <a %s>Terms</a> and <a %s>Privacy Policy</a>.'), + [ "Persona", + ' href="https://login.persona.org/tos" target="_new"', + ' href="https://login.persona.org/privacy" target="_new"', + ]) %> + </p> + <% } %> </div> </div> diff --git a/resources/static/dialog/views/verify_primary_user.ejs b/resources/static/dialog/views/verify_primary_user.ejs index 0a3de6851ed2412d1e3fdefbddf12548b777bb66..c2d4da92de3cdeb7a34d18a1be6a242a1823f370 100644 --- a/resources/static/dialog/views/verify_primary_user.ejs +++ b/resources/static/dialog/views/verify_primary_user.ejs @@ -4,7 +4,7 @@ <% if (requiredEmail) { %> <strong><%= gettext('The site requested you sign in using') %></strong> - <div class="form_section"> + <div id="verify_primary_user" class="cf form_section"> <ul class="inputs"> <li> @@ -15,7 +15,7 @@ </div> </li> - <li> + <li class="small"> <%= gettext("You must sign in with your email provider to verify ownership of this address. This window will be redirected to") %> <p> <strong><%= auth_url %></strong>. @@ -24,48 +24,47 @@ </ul> <div class="submit cf"> - <% if (privacy_url && tos_url) { %> - <p class="tospp"> - <%= format( - gettext('By clicking %s, you confirm that you accept this site\'s <a %s>Terms of Use</a> and <a %s>Privacy Policy</a>.'), - [ gettext('verify'), - format(' href="%s" target="_new"', [tos_url]), - format(' href="%s" target="_new"', [privacy_url]) - ]) %> - </p> - <p> - <% } %> - <button id="verifyWithPrimary"><%= gettext("verify") %></button> - <% if (privacy_url && tos_url) { %> + <p class="cf"> + <button id="verifyWithPrimary"><%= gettext("verify") %></button> </p> - <% } %> + + <% if (personaTOSPP) { %> + <p id="persona_tospp" class="tospp"> + <%= format( + gettext('By proceeding, you agree to %s\'s <a %s>Terms</a> and <a %s>Privacy Policy</a>.'), + [ "Persona", + ' href="https://login.persona.org/tos" target="_new"', + ' href="https://login.persona.org/privacy" target="_new"', + ]) %> + </p> + <% } %> </div> </div> <% } else { %> <strong><%= gettext("Verify With Email Provider") %></strong> - <div class="cf form_section"> - <%= gettext("You must sign in with your email provider to verify ownership of this address. This window will be redirected to") %> - <p> - <strong><%= auth_url %></strong>. - </p> + <div id="verify_primary_user" class="cf form_section"> + <div class="small"> + <%= gettext("You must sign in with your email provider to verify ownership of this address. This window will be redirected to") %> + <p> + <strong><%= auth_url %></strong>. + </p> + </div> <div class="submit cf"> - <% if (privacy_url && tos_url) { %> - <p class="tospp"> - <%= format( - gettext('By clicking %s, you confirm that you accept this site\'s <a %s>Terms of Use</a> and <a %s>Privacy Policy</a>.'), - [ gettext('verify'), - format(' href="%s" target="_new"', [tos_url]), - format(' href="%s" target="_new"', [privacy_url]) - ]) %> - </p> - <p> - <% } %> + <p class="cf"> <button id="verifyWithPrimary"><%= gettext("verify") %></button> <a href="#" id="cancel" class="action"><%= gettext("cancel") %></a> - <% if (privacy_url && tos_url) { %> + </p> + <% if (personaTOSPP) { %> + <p id="persona_tospp" class="tospp"> + <%= format( + gettext('By proceeding, you agree to %s\'s <a %s>Terms</a> and <a %s>Privacy Policy</a>.'), + [ "Persona", + ' href="https://login.persona.org/tos" target="_new"', + ' href="https://login.persona.org/privacy" target="_new"', + ]) %> </p> <% } %> </div> diff --git a/resources/static/include_js/include.js b/resources/static/include_js/include.js index f81e73d4356726eac2b5ebb3ba0334d0cb029f04..3516f7af707e0fc15aa87f1f4140ec8b68b34539 100644 --- a/resources/static/include_js/include.js +++ b/resources/static/include_js/include.js @@ -1027,18 +1027,42 @@ _open_hidden_iframe(); + // back compat support for loggedInEmail + if (typeof options.loggedInEmail !== 'undefined' && + typeof options.loggedInUser !== 'undefined') { + throw "you cannot supply *both* loggedInEmail and loggedInUser"; + } + else if(typeof options.loggedInEmail !== 'undefined') { + try { + console.log("loggedInEmail has been deprecated"); + } catch(e) { + /* ignore error */ + } + + options.loggedInUser = options.loggedInEmail; + delete options.loggedInEmail; + } + // check that the commChan was properly initialized before interacting with it. // on unsupported browsers commChan might still be undefined, in which case // we let the dialog display the "unsupported browser" message upon spawning. - if (typeof options.loggedInEmail !== 'undefined' && commChan) { + if (typeof options.loggedInUser !== 'undefined' && commChan) { commChan.notify({ method: 'loggedInUser', - params: options.loggedInEmail + params: options.loggedInUser }); } } function internalRequest(options) { + if (options.requiredEmail) { + try { + console.log("requiredEmail has been deprecated"); + } catch(e) { + /* ignore error */ + } + } + // focus an existing window if (w) { try { diff --git a/resources/static/pages/css/style.css b/resources/static/pages/css/style.css index 96034587943cb6247e9a382cd8149d6b020691e8..d73c65a9f1fbfe26c3f45fbffef015accc062cf3 100644 --- a/resources/static/pages/css/style.css +++ b/resources/static/pages/css/style.css @@ -447,10 +447,6 @@ button.delete:active { padding: 0; } -#signUpForm .submit { - height: 28px; -} - #signUpForm > .siteinfo { margin-bottom: 10px; } @@ -459,10 +455,18 @@ button.delete:active { display: none; } +.submit > p { + line-height: 28px; +} + .submit .remember { float: left; } +.tospp { + font-size: 13px; +} + .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/test/cases/common/js/user.js b/resources/static/test/cases/common/js/user.js index 26c47010104631cc42cdd9ac2c77caff04b6c9e5..26813f1815568a5b5626606aeedabed04e7ae751 100644 --- a/resources/static/test/cases/common/js/user.js +++ b/resources/static/test/cases/common/js/user.js @@ -92,6 +92,22 @@ var jwcrypto = require("./lib/jwcrypto"); equal(lib.getReturnTo(), returnTo, "get/setReturnTo work as expected"); }); + test("setOriginEmail/getOriginEmail", function() { + storage.addEmail("testuser@testuser.com", { type: "primary" }); + storage.addEmail("testuser2@testuser.com", { type: "primary" }); + + lib.setOrigin("http://testdomain.org"); + + lib.setOriginEmail("testuser@testuser.com"); + equal(lib.getOriginEmail(), "testuser@testuser.com", "correct email"); + + lib.setOrigin("http://othertestdomain.org"); + lib.setOriginEmail("testuser2@testuser.com"); + + lib.setOrigin("http://testdomain.org"); + equal(lib.getOriginEmail(), "testuser@testuser.com", "correct email"); + }); + test("getStoredEmailKeypairs without key - return all identities", function() { var identities = lib.getStoredEmailKeypairs(); equal("object", typeof identities, "object returned"); diff --git a/resources/static/test/cases/dialog/js/misc/state.js b/resources/static/test/cases/dialog/js/misc/state.js index 8d6850d399428fb18c5348332367ffc39a9db33d..3d384a0eb72c216073c080033acbfdf4d148d6aa 100644 --- a/resources/static/test/cases/dialog/js/misc/state.js +++ b/resources/static/test/cases/dialog/js/misc/state.js @@ -34,6 +34,14 @@ } } + function testActionStarted(actionName, requiredOptions) { + ok(actions.called[actionName], actionName + "called"); + for(var key in requiredOptions) { + equal(actions.info[actionName][key], requiredOptions[key], + actionName + " called with " + key + "=" + requiredOptions[key]); + } + } + function createMachine() { machine = bid.State.create(); actions = new ActionsMock(); @@ -127,6 +135,16 @@ equal(actions.info.doResetPassword.email, TEST_EMAIL, "correct email sent to doResetPassword"); }); + test("start - RPInfo always started", function() { + mediator.publish("start", { + termsOfService: "https://browserid.org/TOS.html", + privacyPolicy: "https://browserid.org/priv.html" + }); + + ok(actions.info.doRPInfo.termsOfService, "doRPInfo called with termsOfService set"); + ok(actions.info.doRPInfo.privacyPolicy, "doRPInfo called with privacyPolicy set"); + }); + test("user_staged - call doConfirmUser", function() { mediator.publish("user_staged", { email: TEST_EMAIL }); @@ -254,13 +272,13 @@ mediator.publish("authenticated", { email: TEST_EMAIL }); }); - test("forgot_password", function() { + test("forgot_password - call doForgotPassword with correct options", function() { + mediator.publish("start", { privacyPolicy: "priv.html", termsOfService: "tos.html" }); mediator.publish("forgot_password", { email: TEST_EMAIL, requiredEmail: true }); - equal(actions.info.doForgotPassword.email, TEST_EMAIL, "correct email passed"); - equal(actions.info.doForgotPassword.requiredEmail, true, "correct requiredEmail passed"); + testActionStarted("doForgotPassword", { email: TEST_EMAIL, requiredEmail: true }); }); test("password_reset to user_confirmed - call doUserStaged then doEmailConfirmed", function() { @@ -379,12 +397,11 @@ ok(actions.called.doNotMe, "doNotMe has been called"); }); - test("authenticate", function() { - mediator.publish("authenticate", { - email: TEST_EMAIL - }); + test("authenticate - call doAuthenticate with the correct options", function() { + mediator.publish("start", { privacyPolicy: "priv.html", termsOfService: "tos.html" }); + mediator.publish("authenticate", { email: TEST_EMAIL }); - equal(actions.info.doAuthenticate.email, TEST_EMAIL, "authenticate with testuser@testuser.com"); + testActionStarted("doAuthenticate", { email: TEST_EMAIL, siteTOSPP: true }); }); test("start with no special parameters - go straight to checking auth", function() { @@ -410,16 +427,23 @@ }); + test("add_email - call doAddEmail with correct options", function() { + mediator.publish("start", { privacyPolicy: "priv.html", termsOfService: "tos.html" }); + mediator.publish("add_email"); + testActionStarted("doAddEmail"); + }); + asyncTest("email_chosen with secondary email, user must authenticate - call doAuthenticateWithRequiredEmail", function() { var email = TEST_EMAIL; storage.addEmail(email, { type: "secondary" }); xhr.setContextInfo("auth_level", "assertion"); + mediator.publish("start", { privacyPolicy: "priv.html", termsOfService: "tos.html" }); mediator.publish("email_chosen", { email: email, complete: function() { - equal(actions.called.doAuthenticateWithRequiredEmail, true, "doAuthenticateWithRequiredEmail called"); + testActionStarted("doAuthenticateWithRequiredEmail", { siteTOSPP: false }); start(); } }); @@ -469,15 +493,14 @@ test("null assertion generated - preserve original options in doPickEmail", function() { mediator.publish("start", { hostname: "http://example.com", - privacyURL: "http://example.com/priv.html", - tosURL: "http://example.com/tos.html" + privacyPolicy: "http://example.com/priv.html", + termsOfService: "http://example.com/tos.html" }); mediator.publish("assertion_generated", { assertion: null }); equal(actions.called.doPickEmail, true, "doPickEmail callled"); equal(actions.info.doPickEmail.origin, "http://example.com", "hostname preserved"); - equal(actions.info.doPickEmail.privacyURL, "http://example.com/priv.html", "privacyURL preserved"); - equal(actions.info.doPickEmail.tosURL, "http://example.com/tos.html", "tosURL preserved"); + equal(actions.info.doPickEmail.siteTOSPP, true, "siteTOSPP preserved"); }); test("add_email - call doAddEmail", function() { diff --git a/resources/static/test/cases/dialog/js/modules/actions.js b/resources/static/test/cases/dialog/js/modules/actions.js index 8d90d69f88dfd2bcd2b72865732ff267e1593e3a..27c54f781f818759fa4527d1c1c0910675b5bcee 100644 --- a/resources/static/test/cases/dialog/js/modules/actions.js +++ b/resources/static/test/cases/dialog/js/modules/actions.js @@ -88,7 +88,6 @@ testActionStartsModule('doGenerateAssertion', { email: TEST_EMAIL }, "generate_assertion"); }); - asyncTest("doStageUser with successful creation - trigger user_staged", function() { createController({ ready: function() { @@ -109,5 +108,21 @@ asyncTest("doForgotPassword - call the set_password controller with reset_password true", function() { testActionStartsModule('doForgotPassword', { email: TEST_EMAIL }, "set_password"); }); + + asyncTest("doRPInfo - start the rp_info service", function() { + createController({ + ready: function() { + var error; + try { + controller.doRPInfo({ name: "browserid.org" }); + } catch(e) { + error = e; + } + + equal(error, "module not registered for rp_info", "correct service started"); + start(); + } + }); + }); }()); diff --git a/resources/static/test/cases/dialog/js/modules/add_email.js b/resources/static/test/cases/dialog/js/modules/add_email.js index 6d635e82286fcb606da9e1fe8dd8c99307b40b9e..43f19e385b3091fcdd8411ee30f0c6891f38a123 100644 --- a/resources/static/test/cases/dialog/js/modules/add_email.js +++ b/resources/static/test/cases/dialog/js/modules/add_email.js @@ -41,16 +41,6 @@ controller.start(options || {}); } - test("privacyURL and tosURL specified - show TOS/PP", function() { - equal($(".tospp").length, 0, "tospp has not yet been added to the DOM"); - createController({ - privacyURL: "http://testuser.com/priv.html", - tosURL: "http://testuser.com/tos.html", - }); - - equal($(".tospp").length, 1, "tospp has been added to the DOM"); - }); - test("addEmail with specified email address - fill in email", function() { createController({ email: "testuser@testuser.com" }); ok($("#newEmail").val(), "testuser@testuser.com", "email prepopulated"); diff --git a/resources/static/test/cases/dialog/js/modules/dialog.js b/resources/static/test/cases/dialog/js/modules/dialog.js index f1ddb37c6f57dfe7afc6d744d0bde0dafb1419b8..2519af8c14c3c13e0c77ae4b66b80fcd156036fc 100644 --- a/resources/static/test/cases/dialog/js/modules/dialog.js +++ b/resources/static/test/cases/dialog/js/modules/dialog.js @@ -50,6 +50,8 @@ }, navigator: {}, + + sessionStorage: {} }; function createController(config) { @@ -134,8 +136,12 @@ }); }); - asyncTest("initialization with #CREATE_EMAIL=testuser@testuser.com - trigger start with correct params", function() { - winMock.location.hash = "#CREATE_EMAIL=testuser@testuser.com"; + asyncTest("initialization with #AUTH_RETURN and add=false - trigger start with correct params", function() { + winMock.location.hash = "#AUTH_RETURN"; + winMock.sessionStorage.primaryVerificationFlow = JSON.stringify({ + add: false, + email: TESTEMAIL + }); createController({ ready: function() { @@ -157,8 +163,12 @@ }); }); - asyncTest("initialization with #ADD_EMAIL=testuser@testuser.com - trigger start with correct params", function() { - winMock.location.hash = "#ADD_EMAIL=testuser@testuser.com"; + asyncTest("initialization with #AUTH_RETURN and add=true - trigger start with correct params", function() { + winMock.location.hash = "#AUTH_RETURN"; + winMock.sessionStorage.primaryVerificationFlow = JSON.stringify({ + add: true, + email: TESTEMAIL + }); createController({ ready: function() { @@ -198,66 +208,8 @@ }); }); - asyncTest("get with invalid requiredEmail - print error screen", function() { - createController({ - ready: function() { - mediator.subscribe("start", function(msg, info) { - ok(false, "start should not have been called"); - }); - - var retval = controller.get(HTTP_TEST_DOMAIN, { - requiredEmail: "bademail" - }); - equal(retval, "invalid requiredEmail: (bademail)", "expected error"); - testErrorVisible(); - start(); - } - }); - }); - - asyncTest("get with script containing requiredEmail - print error screen", function() { - createController({ - ready: function() { - mediator.subscribe("start", function(msg, info) { - ok(false, "start should not have been called"); - }); - - var retval = controller.get(HTTP_TEST_DOMAIN, { - requiredEmail: "<script>window.scriptRun=true;</script>testuser@testuser.com" - }); - - // If requiredEmail is not properly escaped, scriptRun will be true. - equal(typeof window.scriptRun, "undefined", "script was not run"); - equal(retval, "invalid requiredEmail: (<script>window.scriptRun=true;</script>testuser@testuser.com)", "expected error"); - testErrorVisible(); - start(); - } - }); - }); - - asyncTest("get with valid requiredEmail - go to start", function() { - createController({ - ready: function() { - var startInfo; - mediator.subscribe("start", function(msg, info) { - startInfo = info; - }); - - var retval = controller.get(HTTP_TEST_DOMAIN, { - requiredEmail: TESTEMAIL - }); - - testHelpers.testObjectValuesEqual(startInfo, { - requiredEmail: TESTEMAIL - }); - equal(typeof retval, "undefined", "no error expected"); - testErrorNotVisible(); - start(); - } - }); - }); - asyncTest("get with relative tosURL & valid privacyURL - print error screen", function() { + asyncTest("get with relative termsOfService & valid privacyPolicy - print error screen", function() { createController({ ready: function() { mediator.subscribe("start", function(msg, info) { @@ -265,8 +217,8 @@ }); var retval = controller.get(HTTP_TEST_DOMAIN, { - tosURL: "relative.html", - privacyURL: "/privacy.html" + termsOfService: "relative.html", + privacyPolicy: "/privacy.html" }); equal(retval, "relative urls not allowed: (relative.html)", "expected error"); testErrorVisible(); @@ -275,7 +227,7 @@ }); }); - asyncTest("get with script containing tosURL - print error screen", function() { + asyncTest("get with script containing termsOfService - print error screen", function() { createController({ ready: function() { mediator.subscribe("start", function(msg, info) { @@ -283,11 +235,11 @@ }); var retval = controller.get(HTTP_TEST_DOMAIN, { - tosURL: "relative.html<script>window.scriptRun=true;</script>", - privacyURL: "/privacy.html" + termsOfService: "relative.html<script>window.scriptRun=true;</script>", + privacyPolicy: "/privacy.html" }); - // If tosURL is not properly escaped, scriptRun will be true. + // If termsOfService is not properly escaped, scriptRun will be true. equal(typeof window.scriptRun, "undefined", "script was not run"); equal(retval, "relative urls not allowed: (relative.html<script>window.scriptRun=true;</script>)", "expected error"); testErrorVisible(); @@ -296,7 +248,7 @@ }); }); - asyncTest("get with valid tosURL & relative privacyURL - print error screen", function() { + asyncTest("get with valid termsOfService & relative privacyPolicy - print error screen", function() { createController({ ready: function() { mediator.subscribe("start", function(msg, info) { @@ -304,8 +256,8 @@ }); var retval = controller.get(HTTP_TEST_DOMAIN, { - tosURL: "/tos.html", - privacyURL: "relative.html" + termsOfService: "/tos.html", + privacyPolicy: "relative.html" }); equal(retval, "relative urls not allowed: (relative.html)", "expected error"); testErrorVisible(); @@ -314,7 +266,7 @@ }); }); - asyncTest("get with script containing privacyURL - print error screen", function() { + asyncTest("get with script containing privacyPolicy - print error screen", function() { createController({ ready: function() { mediator.subscribe("start", function(msg, info) { @@ -322,11 +274,11 @@ }); var retval = controller.get(HTTP_TEST_DOMAIN, { - tosURL: "/tos.html", - privacyURL: "relative.html<script>window.scriptRun=true;</script>" + termsOfService: "/tos.html", + privacyPolicy: "relative.html<script>window.scriptRun=true;</script>" }); - // If privacyURL is not properly escaped, scriptRun will be true. + // If privacyPolicy is not properly escaped, scriptRun will be true. equal(typeof window.scriptRun, "undefined", "script was not run"); equal(retval, "relative urls not allowed: (relative.html<script>window.scriptRun=true;</script>)", "expected error"); testErrorVisible(); @@ -335,7 +287,7 @@ }); }); - asyncTest("get with privacyURL - print error screen", function() { + asyncTest("get with privacyPolicy - print error screen", function() { createController({ ready: function() { mediator.subscribe("start", function(msg, info) { @@ -343,11 +295,11 @@ }); var retval = controller.get(HTTP_TEST_DOMAIN, { - tosURL: "/tos.html", - privacyURL: "relative.html<script>window.scriptRun=true;</script>" + termsOfService: "/tos.html", + privacyPolicy: "relative.html<script>window.scriptRun=true;</script>" }); - // If privacyURL is not properly escaped, scriptRun will be true. + // If privacyPolicy is not properly escaped, scriptRun will be true. equal(typeof window.scriptRun, "undefined", "script was not run"); equal(retval, "relative urls not allowed: (relative.html<script>window.scriptRun=true;</script>)", "expected error"); testErrorVisible(); @@ -356,7 +308,7 @@ }); }); - asyncTest("get with javascript protocol for privacyURL - print error screen", function() { + asyncTest("get with javascript protocol for privacyPolicy - print error screen", function() { createController({ ready: function() { mediator.subscribe("start", function(msg, info) { @@ -364,8 +316,8 @@ }); var retval = controller.get(HTTP_TEST_DOMAIN, { - tosURL: "/tos.html", - privacyURL: "javascript:alert(1)" + termsOfService: "/tos.html", + privacyPolicy: "javascript:alert(1)" }); equal(retval, "relative urls not allowed: (javascript:alert(1))", "expected error"); @@ -375,7 +327,7 @@ }); }); - asyncTest("get with invalid httpg protocol for privacyURL - print error screen", function() { + asyncTest("get with invalid httpg protocol for privacyPolicy - print error screen", function() { createController({ ready: function() { mediator.subscribe("start", function(msg, info) { @@ -383,8 +335,8 @@ }); var retval = controller.get(HTTP_TEST_DOMAIN, { - tosURL: "/tos.html", - privacyURL: "httpg://testdomain.com/privacy.html" + termsOfService: "/tos.html", + privacyPolicy: "httpg://testdomain.com/privacy.html" }); equal(retval, "relative urls not allowed: (httpg://testdomain.com/privacy.html)", "expected error"); @@ -395,7 +347,7 @@ }); - asyncTest("get with valid absolute tosURL & privacyURL - go to start", function() { + asyncTest("get with valid absolute termsOfService & privacyPolicy - go to start", function() { createController({ ready: function() { var startInfo; @@ -404,13 +356,13 @@ }); var retval = controller.get(HTTP_TEST_DOMAIN, { - tosURL: "/tos.html", - privacyURL: "/privacy.html" + termsOfService: "/tos.html", + privacyPolicy: "/privacy.html" }); testHelpers.testObjectValuesEqual(startInfo, { - tosURL: HTTP_TEST_DOMAIN + "/tos.html", - privacyURL: HTTP_TEST_DOMAIN + "/privacy.html" + termsOfService: HTTP_TEST_DOMAIN + "/tos.html", + privacyPolicy: HTTP_TEST_DOMAIN + "/privacy.html" }); equal(typeof retval, "undefined", "no error expected"); @@ -420,7 +372,7 @@ }); }); - asyncTest("get with valid fully qualified http tosURL & privacyURL - go to start", function() { + asyncTest("get with valid fully qualified http termsOfService & privacyPolicy - go to start", function() { createController({ ready: function() { var startInfo; @@ -429,13 +381,13 @@ }); var retval = controller.get(HTTP_TEST_DOMAIN, { - tosURL: HTTP_TEST_DOMAIN + "/tos.html", - privacyURL: HTTP_TEST_DOMAIN + "/privacy.html" + termsOfService: HTTP_TEST_DOMAIN + "/tos.html", + privacyPolicy: HTTP_TEST_DOMAIN + "/privacy.html" }); testHelpers.testObjectValuesEqual(startInfo, { - tosURL: HTTP_TEST_DOMAIN + "/tos.html", - privacyURL: HTTP_TEST_DOMAIN + "/privacy.html" + termsOfService: HTTP_TEST_DOMAIN + "/tos.html", + privacyPolicy: HTTP_TEST_DOMAIN + "/privacy.html" }); equal(typeof retval, "undefined", "no error expected"); @@ -446,7 +398,7 @@ }); - asyncTest("get with valid fully qualified https tosURL & privacyURL - go to start", function() { + asyncTest("get with valid fully qualified https termsOfService & privacyPolicy - go to start", function() { createController({ ready: function() { var startInfo; @@ -455,13 +407,13 @@ }); var retval = controller.get(HTTP_TEST_DOMAIN, { - tosURL: HTTPS_TEST_DOMAIN + "/tos.html", - privacyURL: HTTPS_TEST_DOMAIN + "/privacy.html" + termsOfService: HTTPS_TEST_DOMAIN + "/tos.html", + privacyPolicy: HTTPS_TEST_DOMAIN + "/privacy.html" }); testHelpers.testObjectValuesEqual(startInfo, { - tosURL: HTTPS_TEST_DOMAIN + "/tos.html", - privacyURL: HTTPS_TEST_DOMAIN + "/privacy.html" + termsOfService: HTTPS_TEST_DOMAIN + "/tos.html", + privacyPolicy: HTTPS_TEST_DOMAIN + "/privacy.html" }); equal(typeof retval, "undefined", "no error expected"); testErrorNotVisible(); diff --git a/resources/static/test/cases/dialog/js/modules/required_email.js b/resources/static/test/cases/dialog/js/modules/required_email.js index 53fbed0c1c6c2290a752ec66f4c88cd2de427581..97800512f5c0df71e88c4529c20d7fc3e71b3658 100644 --- a/resources/static/test/cases/dialog/js/modules/required_email.js +++ b/resources/static/test/cases/dialog/js/modules/required_email.js @@ -95,17 +95,16 @@ } - asyncTest("privacyURL and tosURL specified - show TOS/PP", function() { + asyncTest("siteTOSPP specified - show TOS/PP", function() { var email = "registered@testuser.com"; xhr.useResult("known_secondary"); + xhr.setContextInfo("auth_level", "password"); - equal($(".tospp").length, 0, "tospp has not yet been added to the DOM"); createController({ - email: "registered@testuser.com", - privacyURL: "http://testuser.com/priv.html", - tosURL: "http://testuser.com/tos.html", + email: email, + siteTOSPP: true, ready: function() { - equal($(".tospp").length, 1, "tospp has been added to the DOM"); + testHelpers.testRPTosPPShown(); start(); } }); diff --git a/resources/static/test/cases/dialog/js/modules/rp_info.js b/resources/static/test/cases/dialog/js/modules/rp_info.js index 0b89868ab973653fcec4d665f707b5f77b5580dc..c50a3127c4253f84ef0663a2e1836b3db32ecfb5 100644 --- a/resources/static/test/cases/dialog/js/modules/rp_info.js +++ b/resources/static/test/cases/dialog/js/modules/rp_info.js @@ -13,8 +13,11 @@ register = bid.TestHelpers.register, WindowMock = bid.Mocks.WindowMock, RP_HOSTNAME = "hostname.org", - RP_NAME = "RP Name", - RP_HTTPS_LOGO = "https://en.gravatar.com/userimage/6966791/c4feac761b8544cce13e0406f36230aa.jpg"; + RP_NAME = "The Planet's Most Awesome Site", + RP_TOS_URL = "https://browserid.org/TOS.html", + RP_PP_URL = "https://browserid.org/priv.html", + RP_HTTPS_LOGO = "https://en.gravatar.com/userimage/6966791/c4feac761b8544cce13e0406f36230aa.jpg", + mediator = bid.Mediator; module("controllers/rp_info", { setup: testHelpers.setup, @@ -84,5 +87,17 @@ equal($("#rp_logo").attr("src"), RP_HTTPS_LOGO, "rp logo shown"); }); + test("privacyPolicy, termsOfService specified - show TOS/PP info", function() { + createController({ + siteName: RP_NAME, + privacyPolicy: RP_PP_URL, + termsOfService: RP_TOS_URL + }); + + equal($("#rp_name").text(), RP_NAME, "RP's name is set"); + equal($("#rp_tos").attr("href"), RP_TOS_URL, "RP's TOS is set"); + equal($("#rp_pp").attr("href"), RP_PP_URL, "RP's Privacy Policy is set"); + }); + }()); diff --git a/resources/static/test/cases/dialog/js/modules/set_password.js b/resources/static/test/cases/dialog/js/modules/set_password.js index a1ae3b3c683434ebf000678e3b77943fbaf15d82..0d64302f07c16ac0042558e60dd26840fdc97db3 100644 --- a/resources/static/test/cases/dialog/js/modules/set_password.js +++ b/resources/static/test/cases/dialog/js/modules/set_password.js @@ -10,6 +10,8 @@ el = $("body"), bid = BrowserID, testHelpers = bid.TestHelpers, + testElementExists = testHelpers.testElementExists, + testElementNotExists = testHelpers.testElementDoesNotExist, register = testHelpers.register, controller; @@ -33,22 +35,29 @@ test("create with no options - show template, user must verify email, can cancel", function() { ok($("#set_password").length, "set_password template added"); - equal($("#verify_user").length, 1, "correct button shown"); - equal($("#cancel").length, 1, "cancel button shown"); + testElementExists("#verify_user"); + testElementExists("#cancel"); + testElementNotExists("#persona_tospp"); }); test("create with password_reset option - show template, show reset password button", function() { controller.destroy(); createController({ password_reset: true }); - ok($("#set_password").length, "set_password template added"); - equal($("#password_reset").length, 1, "correct button shown"); - equal($("#cancel").length, 1, "cancel button shown"); + testElementExists("#set_password"); + testElementExists("#password_reset"); + testElementExists("#cancel"); + }); + + test("create with personaTOSPP option - show Persona TOS/PP", function() { + controller.destroy(); + createController({ personaTOSPP: true }); + testElementExists("#persona_tospp"); }); test("create with cancelable=false option - cancel button not shown", function() { controller.destroy(); createController({ cancelable: false }); - equal($("#cancel").length, 0, "cancel button not shown"); + testElementNotExists("#cancel"); }); asyncTest("submit with good password/vpassword - password_set message raised", function() { diff --git a/resources/static/test/cases/dialog/js/modules/verify_primary_user.js b/resources/static/test/cases/dialog/js/modules/verify_primary_user.js index 5203792b726a532a914f7ef376e269a4f1e86863..08083a9fe9d3d0ef7908a0d18c1e958a8bed00e9 100644 --- a/resources/static/test/cases/dialog/js/modules/verify_primary_user.js +++ b/resources/static/test/cases/dialog/js/modules/verify_primary_user.js @@ -10,6 +10,8 @@ controller, el, testHelpers = bid.TestHelpers, + testElementExists = testHelpers.testElementExists, + testElementNotExists = testHelpers.testElementDoesNotExist, WindowMock = bid.Mocks.WindowMock, win, mediator = bid.Mediator; @@ -33,41 +35,44 @@ } }); - test("create with privacyURL and tosURL defined - show TOS/PP", function() { + test("personaTOSPP true, requiredEmail: true - show TOS/PP", function() { createController({ window: win, add: false, email: "unregistered@testuser.com", auth_url: "http://testuser.com/sign_in", - privacyURL: "http://testuser.com/priv.html", - tosURL: "http://testuser.com/tos.html" + requiredEmail: true, + personaTOSPP: false }); - equal($(".tospp").length, 1, "tospp has been added to the DOM"); + testElementNotExists("#persona_tospp"); }); - test("create with requiredEmail, privacyURL and tosURL defined - show TOS/PP", function() { + test("personaTOSPP true, requiredEmail: false - show TOS/PP", function() { createController({ window: win, add: false, - requiredEmail: "unregistered@testuser.com", email: "unregistered@testuser.com", auth_url: "http://testuser.com/sign_in", - privacyURL: "http://testuser.com/priv.html", - tosURL: "http://testuser.com/tos.html" + requiredEmail: false, + personaTOSPP: false }); - equal($(".tospp").length, 1, "tospp has been added to the DOM"); + testElementNotExists("#persona_tospp"); }); - asyncTest("submit with `add: false` option opens a new tab with CREATE_EMAIL URL", function() { + asyncTest("submit with `add: false` option opens a new tab with proper URL (updated for sessionStorage)", function() { var messageTriggered = false; createController({ window: win, add: false, email: "unregistered@testuser.com", - auth_url: "http://testuser.com/sign_in" + auth_url: "http://testuser.com/sign_in", + personaTOSPP: true }); + + testElementExists("#persona_tospp"); + mediator.subscribe("primary_user_authenticating", function() { messageTriggered = true; }); @@ -77,26 +82,29 @@ win.document.location.hash = "#NATIVE"; controller.submit(function() { - equal(win.document.location, "http://testuser.com/sign_in?email=unregistered%40testuser.com&return_to=sign_in%23CREATE_EMAIL%3Dunregistered%40testuser.com"); + equal(win.document.location, "http://testuser.com/sign_in?email=unregistered%40testuser.com"); equal(messageTriggered, true, "primary_user_authenticating triggered"); start(); }); }); - asyncTest("submit with `add: true` option opens a new tab with ADD_EMAIL URL", function() { + asyncTest("submit with `add: true` option opens a new tab with proper URL (updated for sessionStorage)", function() { createController({ window: win, add: true, email: "unregistered@testuser.com", - auth_url: "http://testuser.com/sign_in" + auth_url: "http://testuser.com/sign_in", + personaTOSPP: true }); + testElementExists("#persona_tospp"); + // Also checking to make sure the NATIVE is stripped out. win.document.location.href = "sign_in"; win.document.location.hash = "#NATIVE"; controller.submit(function() { - equal(win.document.location, "http://testuser.com/sign_in?email=unregistered%40testuser.com&return_to=sign_in%23ADD_EMAIL%3Dunregistered%40testuser.com"); + equal(win.document.location, "http://testuser.com/sign_in?email=unregistered%40testuser.com"); start(); }); }); diff --git a/resources/static/test/mocks/window.js b/resources/static/test/mocks/window.js index 510514288a7897747ba90d6c00b38be5641a0ba4..a633ba0d20180139f7bcca5d6230f0d30379c1aa 100644 --- a/resources/static/test/mocks/window.js +++ b/resources/static/test/mocks/window.js @@ -15,6 +15,7 @@ BrowserID.Mocks.WindowMock = (function() { function WindowMock() { this.document = new DocumentMock(); + this.sessionStorage = {}; } WindowMock.prototype = { open: function(url, name, options) { diff --git a/resources/static/test/testHelpers/helpers.js b/resources/static/test/testHelpers/helpers.js index 119bd73f64a8f23eec6b6655e63781e11459b751..66868076a8c8cd477b25b2db0e5e1e6023595037 100644 --- a/resources/static/test/testHelpers/helpers.js +++ b/resources/static/test/testHelpers/helpers.js @@ -221,10 +221,6 @@ BrowserID.TestHelpers = (function() { } }, - testHasClass: function(selector, className, msg) { - ok($(selector).hasClass(className), msg || selector + " has className: " + className); - }, - testUndefined: function(toTest, msg) { equal(typeof toTest, "undefined", msg || "object is undefined"); }, @@ -235,8 +231,33 @@ BrowserID.TestHelpers = (function() { testVisible: function(selector, msg) { ok($(selector).is(":visible"), msg || selector + " should be visible"); - } + }, + testHasClass: function(selector, className, msg) { + ok($(selector).hasClass(className), + msg || (selector + " has className " + className)); + }, + + testNotHasClass: function(selector, className, msg) { + ok(!$(selector).hasClass(className), + msg || (selector + " does not have className " + className)); + }, + + testElementExists: function(selector, msg) { + ok($(selector).length, msg || ("element '" + selector + "' exists")); + }, + + testElementDoesNotExist: function(selector, msg) { + ok(!$(selector).length, msg || ("element '" + selector + "' does not exist")); + }, + + testRPTosPPShown: function(msg) { + TestHelpers.testHasClass("body", "rptospp", msg || "RP TOS/PP shown"); + }, + + testRPTosPPNotShown: function(msg) { + TestHelpers.testNotHasClass("body", "rptospp", msg || "RP TOS/PP not shown"); + } }; return TestHelpers; diff --git a/resources/views/about.ejs b/resources/views/about.ejs index b16b28ac52a5a143b96774b0c7622c6b9e29b890..4ea433948b01e4d7b87b5c5957fad13fcda54322 100644 --- a/resources/views/about.ejs +++ b/resources/views/about.ejs @@ -13,13 +13,13 @@ </div> <div class="graphic"> - <img src="/pages/i/one-password-graphic.png" alt="One password to rule them all."> + <img src="<%- cachify('/pages/i/one-password-graphic.png') %>" alt="One password to rule them all."> </div> </article> <article class="blurb flexible"> <div class="graphic first"> - <img src="/pages/i/flexible-graphic.png" alt="Use multiple email addresses"> + <img src="<%- cachify('/pages/i/flexible-graphic.png') %>" alt="Use multiple email addresses"> </div> <div class="info"> @@ -42,7 +42,7 @@ </article> </section> - <a href="https://developer.mozilla.org/en/BrowserID/Quick_Setup" class="developers"><img src="/pages/i/developers-link.png" alt="Persona for developers"><span>Implement Persona on your site </span>Developer guides and API documentation</a> + <a href="https://developer.mozilla.org/en/BrowserID/Quick_Setup" class="developers"><img src="<%- cachify('/pages/i/developers-link.png') %>" alt="Persona for developers"><span>Implement Persona on your site </span>Developer guides and API documentation</a> </div><!-- #dashboard --> </div> diff --git a/resources/views/layout.ejs b/resources/views/layout.ejs index bfaa83682d1ecbd45b97896476d22b672b498aef..cbc7cc078e9ea6f1f6e0dbb0f6b13347196e1094 100644 --- a/resources/views/layout.ejs +++ b/resources/views/layout.ejs @@ -8,7 +8,7 @@ <meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, width=device-width" /> <meta name="format-detection" content="email=no" /> <!--[if lt IE 9]> - <script src="/common/js/lib/html5shim.js"></script> + <%- cachify_js('/production/html5shim.js') %> <![endif]--> <%- cachify_css('/production/browserid.css') %> <!--[if lt IE 9]> diff --git a/resources/views/signup.ejs b/resources/views/signup.ejs index dcf66e0702c932803c7ca991217b509a4e69323e..9e92bd83b6e5be5865af429ece9f1b226ddd9cb6 100644 --- a/resources/views/signup.ejs +++ b/resources/views/signup.ejs @@ -82,10 +82,20 @@ </ul> <div class="submit cf forminputs"> - <button>Verify Email</button> - <div class="remember cf"> - <a class="action" href="/signin" tabindex="2">Existing account? Sign in.</a> - </div> + <p class="cf"> + <button>Verify Email</button> + <a class="action remember" href="/signin" tabindex="2">Existing account? Sign in.</a> + </p> + + <p class="tospp"> + <%- format( + gettext('By proceeding, you agree to %s\'s <a %s>Terms</a> and <a %s>Privacy Policy</a>.'), + [ "Persona", + ' href="https://login.persona.org/tos" target="_new"', + ' href="https://login.persona.org/privacy" target="_new"', + ]) %> + </p> + </div> <ul class="notifications"> diff --git a/scripts/compress b/scripts/compress index 7c8b797d6157699f19f7eb316bc7cb6ff9670e97..55e5a241f3cc1758d256c607b7c7fc233b19e871 100755 --- a/scripts/compress +++ b/scripts/compress @@ -41,7 +41,7 @@ Object.keys(all).forEach(function(resource) { // in prod '/build/templates.js' has all templates glommed into it, // and is bundled into the Big Minified Piles Of Resources we ship. // Here we sub the former with the latter. - var ix = all[resource].indexOf('/shared/templates.js'); + var ix = all[resource].indexOf('/common/js/templates.js'); if (ix != -1) all[resource].splice(ix, 1, '/build/templates.js'); cc.enqueue({ diff --git a/tests/static-resource-test.js b/tests/static-resource-test.js index ceda594e43ea9f697b56507f2b909bf9f66286f8..66a83a9ba88fb4ef3428474f635e8acfc5ac9142 100755 --- a/tests/static-resource-test.js +++ b/tests/static-resource-test.js @@ -23,7 +23,7 @@ suite.addBatch({ var res = resources.resources; assert.ok(files['/production/dialog.css'].length >= 3); // Get ride of non-localized asset bundles - ['/production/communication_iframe.js', '/production/include.js', '/production/dialog.css', '/production/browserid.css', '/production/ie8_main.css', '/production/ie8_dialog.css', '/production/relay.js'].forEach( + ['/production/communication_iframe.js', '/production/include.js', '/production/dialog.css', '/production/browserid.css', '/production/ie8_main.css', '/production/ie8_dialog.css', '/production/relay.js', '/production/html5shim.js'].forEach( function (nonLocaleAsset) { delete res[nonLocaleAsset]; delete files[nonLocaleAsset];