diff --git a/lib/browserid/views.js b/lib/browserid/views.js index 1fc696fb6c90cd63d222e648afaec5f9364efeb9..ae9f3ecf22f8fd4f09e52c22880cdea5ce437568 100644 --- a/lib/browserid/views.js +++ b/lib/browserid/views.js @@ -173,7 +173,7 @@ exports.setup = function(app) { } else { // this is stage or production, explicitly disable all resources under /test app.get(/^\/test/, function(req, res) { - httputils.notFound("Cannot " + req.method + " " + req.url); + httputils.notFound(res, "Cannot " + req.method + " " + req.url); }); } diff --git a/lib/wsapi/stage_user.js b/lib/wsapi/stage_user.js index ff1dd24bf03c10cee69671ba8ef0b80786c56db3..909d4d3dbb101daed1808699bd6b162028149fe3 100644 --- a/lib/wsapi/stage_user.js +++ b/lib/wsapi/stage_user.js @@ -59,7 +59,7 @@ exports.process = function(req, res) { if (err) { if (err.indexOf('exceeded') != -1) { logger.warn("max load hit, failing on auth request with 503: " + err); - return httputils.serviceUnavailable("server is too busy"); + return httputils.serviceUnavailable(res, "server is too busy"); } logger.error("can't bcrypt: " + err); return res.json({ success: false }); diff --git a/resources/static/dialog/css/popup.css b/resources/static/dialog/css/popup.css index 8a655b7def5fd70a18016314e84683171402ec65..c55543e572188e2ce35d2a14b4f0102f31cd295b 100644 --- a/resources/static/dialog/css/popup.css +++ b/resources/static/dialog/css/popup.css @@ -51,9 +51,6 @@ header { * overflow the dialog box. */ *padding: 10px 0; - /* - font-weight: bold; - */ border-bottom: 1px solid #c7c6c1; /*-ms-filter through zoom: 1 are fixes for IE6 and IE7 so they show the header @@ -74,6 +71,8 @@ header { background: url("/i/persona-logo-transparent.png") 0 0 no-repeat; text-indent: -9999px; display: inline-block; + *display: block; + zoom: 1; } footer { @@ -153,10 +152,10 @@ section > .contents { /* Fix for IE6 not displaying the unsupported dialog correctly. IE6 by * default sets the height and width of the element to 0 meaning nothing * shows up on the screen. - * Note, these are magic numbers that depend on the width and height of the + * Note, height is a magic number that depend on the height of the * dialog. The height also depends on the height of the header and footer. */ - _width: 682px; + _width: 100%; _height: 250px; } @@ -441,3 +440,7 @@ a.emphasize:active { min-width: 4em; } +.unsupported, .cookies_disabled { + text-align: center; +} + diff --git a/resources/static/shared/network.js b/resources/static/shared/network.js index ed2cc792620a933b3ecee39784c11dd95e4a0eae..752ce427df57ff8903fea6a742ccd7b795b97703 100644 --- a/resources/static/shared/network.js +++ b/resources/static/shared/network.js @@ -94,6 +94,10 @@ BrowserID.Network = (function() { // Any time the context info changes, we want to know about it. mediator.subscribe('context_info', onContextChange); + // BEGIN TEST API + this.cookiesEnabledOverride = config && config.cookiesEnabledOverride; + // END TEST API + clearContext(); }, @@ -677,6 +681,12 @@ BrowserID.Network = (function() { enabled = false; } + // BEGIN TESTING API + if (typeof Network.cookiesEnabledOverride === "boolean") { + enabled = Network.cookiesEnabledOverride; + } + // END TESTING API + complete(onComplete, enabled); }, onFailure); }, diff --git a/resources/static/shared/user.js b/resources/static/shared/user.js index 1679488400632061aab85e12603afc5b273393ac..0695c836c9d1b045c3b35415067205c4415eb69b 100644 --- a/resources/static/shared/user.js +++ b/resources/static/shared/user.js @@ -679,6 +679,7 @@ BrowserID.User = (function() { if(cookiesEnabled) { network.checkAuth(function(authenticated) { setAuthenticationStatus(authenticated); + if (!authenticated) authenticated = false; complete(onComplete, authenticated); }, onFailure); } @@ -692,12 +693,13 @@ BrowserID.User = (function() { * Check whether the current user is authenticated. If authenticated, sync * identities. * @method checkAuthenticationAndSync - * @param {function} [onComplete] - Called on sync completion. + * @param {function} [onComplete] - Called on sync completion with one + * boolean parameter, authenticated. authenticated will be true if user + * is authenticated, false otw. * @param {function} [onFailure] - Called on error. */ checkAuthenticationAndSync: function(onComplete, onFailure) { - network.checkAuth(function(authenticated) { - setAuthenticationStatus(authenticated); + User.checkAuthentication(function(authenticated) { if (authenticated) { User.syncEmails(function() { onComplete && onComplete(authenticated); @@ -1053,7 +1055,7 @@ BrowserID.User = (function() { * Get an assertion for the current domain if the user is signed into it * @method getPersistentSigninAssertion * @param {function} onComplete - called on completion. Called with an - * assertion if successful, null otw. + * an email and assertion if successful, null otw. * @param {function} onFailure - called on XHR failure. */ getSilentAssertion: function(siteSpecifiedEmail, onComplete, onFailure) { @@ -1065,7 +1067,7 @@ BrowserID.User = (function() { // so if we rely on localstorage only and check authentication status // only when we know a network request will be required, we very well // might have fewer race conditions and do fewer network requests. - User.checkAuthentication(function(authenticated) { + User.checkAuthenticationAndSync(function(authenticated) { if (authenticated) { var loggedInEmail = storage.getLoggedIn(origin); if (loggedInEmail !== siteSpecifiedEmail) { diff --git a/resources/static/test/cases/shared/network.js b/resources/static/test/cases/shared/network.js index ef310d932c20905bea77f4aca7dbe8e263c7ca85..4ce7680923dd3d0f03d8c57f771d69d461ae7e53 100644 --- a/resources/static/test/cases/shared/network.js +++ b/resources/static/test/cases/shared/network.js @@ -20,7 +20,6 @@ module("shared/network", { setup: function() { testHelpers.setup(); - network.init(); }, teardown: function() { testHelpers.teardown(); @@ -593,12 +592,28 @@ }); asyncTest("cookiesEnabled with cookies enabled - return true status", function() { + network.init({ cookiesEnabledOverride: true }); network.cookiesEnabled(function(status) { equal(status, true, "cookies are enabled, correct status"); start(); }, testHelpers.unexpectedXHRFailure); }); + asyncTest("cookiesEnabled with cookies disabled - return true status", function() { + network.init({ cookiesEnabledOverride: false }); + network.cookiesEnabled(function(status) { + equal(status, false, "cookies are disabled, correct status"); + start(); + }, testHelpers.unexpectedXHRFailure); + }); + + asyncTest("cookiesEnabled with browser defined cookie status - wait and see", function() { + network.cookiesEnabled(function(status) { + equal(status, true, "hopefully cookies are enabled, correct status"); + start(); + }, testHelpers.unexpectedXHRFailure); + }); + asyncTest("cookiesEnabled with onComplete exception thrown - should not call onComplete a second time", function() { // Since we are manually throwing an exception, it must be caught // below. diff --git a/resources/static/test/cases/shared/user.js b/resources/static/test/cases/shared/user.js index c44fe0cd719b806924b091a621d1f2a0e0af5cfa..a07a5febb04c5527c7a5ff9980d62b994828bf96 100644 --- a/resources/static/test/cases/shared/user.js +++ b/resources/static/test/cases/shared/user.js @@ -14,6 +14,8 @@ var jwcrypto = require("./lib/jwcrypto"); testHelpers = bid.TestHelpers, testOrigin = testHelpers.testOrigin, failureCheck = testHelpers.failureCheck, + testUndefined = testHelpers.testUndefined, + testNotUndefined = testHelpers.testNotUndefined, provisioning = bid.Mocks.Provisioning, TEST_EMAIL = "testuser@testuser.com"; @@ -512,24 +514,40 @@ var jwcrypto = require("./lib/jwcrypto"); asyncTest("checkAuthentication with valid authentication", function() { + storage.addSecondaryEmail(TEST_EMAIL); xhr.setContextInfo("auth_level", "primary"); + lib.checkAuthentication(function(authenticated) { equal(authenticated, "primary", "We are authenticated!"); + testNotUndefined(storage.getEmail(TEST_EMAIL), "localStorage is not cleared"); start(); }); }); - asyncTest("checkAuthentication with invalid authentication", function() { + asyncTest("checkAuthentication with invalid authentication - localStorage cleared", function() { + storage.addSecondaryEmail(TEST_EMAIL); xhr.setContextInfo("auth_level", undefined); + lib.checkAuthentication(function(authenticated) { - equal(authenticated, undefined, "We are not authenticated!"); + equal(authenticated, false, "We are not authenticated!"); + testUndefined(storage.getEmail(TEST_EMAIL), "localStorage was cleared"); start(); }); }); + asyncTest("checkAuthentication with cookies disabled - localStorage is not cleared, user can enable their cookies and try again", function() { + storage.addSecondaryEmail(TEST_EMAIL); + network.init({ cookiesEnabledOverride: false }); + + lib.checkAuthentication(function(authenticated) { + equal(authenticated, false, "We are not authenticated!"); + testNotUndefined(storage.getEmail(TEST_EMAIL), "localStorage is not cleared"); + start(); + }); + }); asyncTest("checkAuthentication with XHR failure", function() { xhr.useResult("contextAjaxError"); @@ -548,12 +566,24 @@ var jwcrypto = require("./lib/jwcrypto"); }); - - asyncTest("checkAuthenticationAndSync with invalid authentication", function() { + asyncTest("checkAuthenticationAndSync with invalid authentication - localStorage cleared", function() { + storage.addSecondaryEmail(TEST_EMAIL); xhr.setContextInfo("auth_level", undefined); lib.checkAuthenticationAndSync(function onComplete(authenticated) { - equal(authenticated, undefined, "We are not authenticated!"); + equal(authenticated, false, "We are not authenticated!"); + testUndefined(storage.getEmail(TEST_EMAIL), "localStorage was cleared"); + start(); + }, testHelpers.unexpectedXHRFailure); + }); + + asyncTest("checkAuthenticationAndSync with cookies disabled - localStorage not cleared, user can enable their cookies and try again", function() { + storage.addSecondaryEmail(TEST_EMAIL); + network.init({ cookiesEnabledOverride: false }); + + lib.checkAuthenticationAndSync(function onComplete(authenticated) { + equal(authenticated, false, "We are not authenticated!"); + testNotUndefined(storage.getEmail(TEST_EMAIL), "localStorage is not cleared"); start(); }, testHelpers.unexpectedXHRFailure); }); @@ -942,6 +972,34 @@ var jwcrypto = require("./lib/jwcrypto"); }); + asyncTest("getSilentAssertion with logged in user, emails match - user already logged in, call callback with email and null assertion", function() { + var LOGGED_IN_EMAIL = TEST_EMAIL; + xhr.setContextInfo("auth_level", "password"); + + lib.syncEmailKeypair(LOGGED_IN_EMAIL, function() { + storage.setLoggedIn(lib.getOrigin(), LOGGED_IN_EMAIL); + lib.getSilentAssertion(LOGGED_IN_EMAIL, function(email, assertion) { + equal(email, LOGGED_IN_EMAIL, "correct email"); + strictEqual(assertion, null, "correct assertion"); + start(); + }, testHelpers.unexpectedXHRFailure); + }, testHelpers.unexpectedXHRFailure); + }); + + asyncTest("getSilentAssertion with logged in user, request email different from logged in email, valid cert for logged in email - logged in user needs assertion - call callback with email and assertion.", function() { + xhr.setContextInfo("auth_level", "password"); + var LOGGED_IN_EMAIL = TEST_EMAIL; + var REQUESTED_EMAIL = "requested@testuser.com"; + + lib.syncEmailKeypair(LOGGED_IN_EMAIL, function() { + storage.setLoggedIn(lib.getOrigin(), LOGGED_IN_EMAIL); + lib.getSilentAssertion(REQUESTED_EMAIL, function(email, assertion) { + equal(email, LOGGED_IN_EMAIL, "correct email"); + testAssertion(assertion, start); + }, testHelpers.unexpectedXHRFailure); + }, testHelpers.unexpectedXHRFailure); + }); + asyncTest("logoutUser", function(onSuccess) { lib.authenticate(TEST_EMAIL, "testuser", function(authenticated) { lib.syncEmails(function() { diff --git a/resources/static/test/testHelpers/helpers.js b/resources/static/test/testHelpers/helpers.js index 2c77f42019d666b16005680e32cd1afc4aab1bc6..c309deeb16f250d20aa848d01b1d5d5b0f0e3b90 100644 --- a/resources/static/test/testHelpers/helpers.js +++ b/resources/static/test/testHelpers/helpers.js @@ -68,7 +68,7 @@ BrowserID.TestHelpers = (function() { transport.setContextInfo("cookies_enabled", true); transport.useResult("valid"); - network.init(); + network.init({ forceCookieStatus: undefined }); clearStorage(); $("body").stop().show(); @@ -223,6 +223,14 @@ BrowserID.TestHelpers = (function() { testHasClass: function(selector, className, msg) { ok($(selector).hasClass(className), selector + " has className " + className + " - " + msg); + }, + + testUndefined: function(toTest, msg) { + equal(typeof toTest, "undefined", msg || "object is undefined"); + }, + + testNotUndefined: function(toTest, msg) { + notEqual(typeof toTest, "undefined", msg || "object is defined"); } }; diff --git a/resources/views/layout.ejs b/resources/views/layout.ejs index 8c8d01cd0879b4067b1bd866bfd4a1bb6b1a2723..1373793786f965237d79b2ee474adae56e4bbe05 100644 --- a/resources/views/layout.ejs +++ b/resources/views/layout.ejs @@ -16,7 +16,7 @@ <%- cachify_css('/production/ie8_main.css') %> <![endif]--> <%- cachify_js(util.format('/production/%s/browserid.js', locale)) %> - <title><%= format(gettext("Mozilla Person: %s"), [title]) %></title> + <title><%= format(gettext("Mozilla Persona: %s"), [title]) %></title> </head> <body class="loading"> <a href="#" id="showDevelopment"> </a> diff --git a/resources/views/signin.ejs b/resources/views/signin.ejs index e9fa2170922d4c83219ab6451ff7d02e25c9f407..a0ed5a5fc8300f55d29493db7f345dd0bf23f23e 100644 --- a/resources/views/signin.ejs +++ b/resources/views/signin.ejs @@ -42,7 +42,7 @@ </div> <div id="cannot_authenticate" class="tooltip" for="password"> - The account cannot be logged in with this username and password.') + The account cannot be logged in with this username and password. </li> </ul>