Created
February 27, 2013 20:19
-
-
Save jaredhirsch/5051319 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --cc bin/keysigner | |
index db7e49a,1f668ba..0000000 | |
--- a/bin/keysigner | |
+++ b/bin/keysigner | |
@@@ -22,7 -23,7 +23,11 @@@ heartbeat = require('../lib/heartbeat') | |
shutdown = require('../lib/shutdown'), | |
computecluster = require('compute-cluster'), | |
urlparse = require('urlparse'), | |
++<<<<<<< HEAD | |
+toobusy = require('../lib/busy_middleware.js'); | |
++======= | |
+ certKey = require('../lib/wsapi/cert_key.js'); | |
++>>>>>>> b2g | |
logger.info("keysigner starting up"); | |
diff --cc bin/verifier | |
index 1c108fd,3718a74..0000000 | |
--- a/bin/verifier | |
+++ b/bin/verifier | |
@@@ -19,7 -20,7 +20,11 @@@ logger = require('../lib/logging').logg | |
config = require('../lib/configuration'), | |
shutdown = require('../lib/shutdown'), | |
statsd = require('../lib/statsd'), | |
++<<<<<<< HEAD | |
+toobusy = require('../lib/busy_middleware.js'); | |
++======= | |
+ booleanQuery = require('../lib/boolean-query'); | |
++>>>>>>> b2g | |
logger.info("verifier server starting up"); | |
diff --cc lib/db/mysql.js | |
index 19997d5,5b5ebf0..0000000 | |
--- a/lib/db/mysql.js | |
+++ b/lib/db/mysql.js | |
@@@ -486,9 -489,15 +491,21 @@@ exports.completePasswordReset = functio | |
function(err) { | |
if (err) return cb(err); | |
++<<<<<<< HEAD | |
+ // update the password! | |
+ exports.updatePassword(uid, password || o.passwd, true, function(err) { | |
+ cb(err, o.email, uid); | |
++======= | |
+ | |
+ // mark this address as verified | |
+ addEmailToUser(uid, o.email, 'secondary', function(err){ | |
+ if (err) return cb(err); | |
+ | |
+ // update the password! | |
+ exports.updatePassword(uid, o.passwd, true, function(err) { | |
+ cb(err, o.email, uid); | |
+ }); | |
++>>>>>>> b2g | |
}); | |
}); | |
}); | |
diff --cc lib/static/views.js | |
index cccc015,d30b2d2..0000000 | |
--- a/lib/static/views.js | |
+++ b/lib/static/views.js | |
@@@ -54,23 -53,22 +54,32 @@@ function renderCachableView(req, res, t | |
options.enable_development_menu = config.get('enable_development_menu'); | |
- // The real version number is not ready until sometime after initial load, | |
- // until it is ready a fake randomly generated string is used. Go get | |
- // the real SHA whenever it is actually needed so that the randomly | |
- // generated SHA is not returned to the user. | |
- options.commit = version(); | |
+ // Get the SHA associated with this code. | |
+ version(function(commit) { | |
+ options.commit = commit; | |
- res.local('util', util); | |
- res.render(template, options); | |
+ res.local('util', util); | |
+ res.render(template, options); | |
+ }); | |
+} | |
+ | |
+ | |
+function isProductionEnvironment() { | |
+ return [ | |
+ 'https://login.persona.org', | |
+ 'https://login.anosrep.org' | |
+ ].indexOf(config.get('public_url')) !== -1; | |
} | |
++<<<<<<< HEAD | |
++======= | |
+ const X_FRAME_ALLOWED = [ | |
+ '/communication_iframe', | |
+ '/relay', | |
+ '/tos', | |
+ '/privacy' | |
+ ]; | |
++>>>>>>> b2g | |
exports.setup = function(app) { | |
diff --cc lib/wsapi.js | |
index 8a5ccc9,ff2c522..0000000 | |
--- a/lib/wsapi.js | |
+++ b/lib/wsapi.js | |
@@@ -300,6 -285,9 +303,12 @@@ exports.setup = function(options, app) | |
secure: overSSL | |
} | |
}); | |
++<<<<<<< HEAD | |
++======= | |
+ | |
+ app.use(booleanQuery); | |
+ | |
++>>>>>>> b2g | |
app.use(function(req, resp, next) { | |
var purl = url.parse(req.url); | |
diff --cc lib/wsapi/stage_user.js | |
index 68fb0e1,2cdac2b..0000000 | |
--- a/lib/wsapi/stage_user.js | |
+++ b/lib/wsapi/stage_user.js | |
@@@ -58,30 -64,50 +64,74 @@@ exports.process = function(req, res) | |
} | |
try { | |
++<<<<<<< HEAD | |
+ // upon success, stage_user returns a secret (that'll get baked into a url | |
+ // and given to the user), on failure it throws | |
+ db.stageUser(req.params.email, hash, function(err, secret) { | |
+ if (err) { | |
+ cef_logger.alert("DB_FAILURE", "Cannot stage user; dbwriter failure", req, {msg: err}); | |
+ return wsapi.databaseDown(res, err); | |
+ } | |
+ | |
+ // store the email being registered in the session data | |
+ if (!req.session) req.session = {}; | |
+ | |
+ // store the secret we're sending via email in the users session, as checking | |
+ // that it still exists in the database is the surest way to determine the | |
+ // status of the email verification. | |
+ req.session.pendingCreation = secret; | |
+ | |
+ res.json({ success: true }); | |
+ | |
+ // let's now kick out a verification email! | |
+ email.sendNewUserEmail(req.params.email, req.params.site, secret, langContext); | |
+ }); | |
++======= | |
+ if (allowUnverified) { | |
+ // if its ok to be unverified, then create the user right now, | |
+ // and stage their email instead. | |
+ db.createUnverifiedUser(req.params.email, hash, function(err, uid, secret) { | |
+ if (err) { | |
+ cef_logger.alert("DB_FAILURE", "Cannot create unverified user; dbwriter failure", req, {msg: err}); | |
+ return wsapi.databaseDown(res, err); | |
+ } | |
+ | |
+ if (!req.session) req.session = {}; | |
+ req.session.pendingAddition = secret; | |
+ | |
+ res.json({ success: true, unverified: true }); | |
+ | |
+ email.sendConfirmationEmail(req.params.email, req.params.site, secret, langContext); | |
+ }); | |
+ } else { | |
+ // the typical flow | |
+ | |
+ // upon success, stage_user returns a secret (that'll get baked into a url | |
+ // and given to the user), on failure it throws | |
+ db.stageUser(req.params.email, hash, function(err, secret) { | |
+ if (err) { | |
+ cef_logger.alert("DB_FAILURE", "Cannot stage user; dbwriter failure", req, {msg: err}); | |
+ return wsapi.databaseDown(res, err); | |
+ } | |
+ | |
+ // store the email being registered in the session data | |
+ if (!req.session) req.session = {}; | |
+ | |
+ // store the secret we're sending via email in the users session, as checking | |
+ // that it still exists in the database is the surest way to determine the | |
+ // status of the email verification. | |
+ req.session.pendingCreation = secret; | |
+ | |
+ res.json({ success: true }); | |
+ | |
+ // let's now kick out a verification email! | |
+ email.sendNewUserEmail(req.params.email, req.params.site, secret, langContext); | |
+ }); | |
+ } | |
++>>>>>>> b2g | |
} catch(e) { | |
// we should differentiate tween' 400 and 500 here. | |
- httputils.badRequest(res, e.toString()); | |
+ httputils.badRequest(res, String(e)); | |
} | |
}); | |
}); | |
diff --cc resources/static/common/js/network.js | |
index e3fb896,05a304a..0000000 | |
--- a/resources/static/common/js/network.js | |
+++ b/resources/static/common/js/network.js | |
@@@ -758,47 -765,13 +767,57 @@@ BrowserID.Network = (function() | |
}, | |
/** | |
++<<<<<<< HEAD | |
+ * Request that an account transitions from a primary to a secondary. Used | |
+ * whenever a user has only primary addresses and one of the addresses | |
+ * belongs to an IdP which converts to a secondary. | |
+ * @method requestTransitionToSecondary | |
+ * @param {string} email | |
+ * @param {string} password | |
+ * @param {string} origin - site user is trying to sign in to. | |
+ * @param {function} [onComplete] - Callback to call when complete. | |
+ * @param {function} [onFailure] - Called on XHR failure. | |
+ */ | |
+ requestTransitionToSecondary: function(email, password, origin, onComplete, onFailure) { | |
+ var postData = { | |
+ email: email, | |
+ pass: password, | |
+ site : origin | |
+ }; | |
+ stageAddressForVerification(postData, "/wsapi/stage_transition", onComplete, onFailure); | |
+ }, | |
+ | |
+ /** | |
+ * Complete transition to secondary | |
+ * @method completeTransitionToSecondary | |
+ * @param {string} token - token to register for. | |
+ * @param {string} password | |
+ * @param {function} [onComplete] - Called when complete. | |
+ * @param {function} [onFailure] - Called on XHR failure. | |
+ */ | |
+ completeTransitionToSecondary: completeAddressVerification.curry("/wsapi/complete_transition"), | |
+ | |
+ /** | |
+ * Check the registration status of a transition to secondary | |
+ * @method checkTransitionToSecondary | |
+ * @param {function} [onsuccess] - called when complete. | |
+ * @param {function} [onfailure] - called on xhr failure. | |
+ */ | |
+ checkTransitionToSecondary: function(email, onComplete, onFailure) { | |
+ get({ | |
+ url: "/wsapi/transition_status?email=" + encodeURIComponent(email), | |
+ success: handleAddressVerifyCheckResponse.curry(onComplete), | |
+ error: onFailure | |
+ }); | |
++======= | |
+ * Set whether the network should pass allowUnverified=true in | |
+ * its requests. | |
+ * @method setAllowUnverified | |
+ * @param {boolean} [allow] - True or false, to allow. | |
+ */ | |
+ setAllowUnverified: function(allow) { | |
+ allow_unverified = allow; | |
++>>>>>>> b2g | |
} | |
}; | |
diff --cc resources/static/common/js/user.js | |
index d077bf8,dd30480..0000000 | |
--- a/resources/static/common/js/user.js | |
+++ b/resources/static/common/js/user.js | |
@@@ -640,11 -689,11 +692,16 @@@ BrowserID.User = (function() | |
* info.reason {string} - if status false, reason of failure. | |
* @param {function} [onFailure] - Called on XHR failure. | |
*/ | |
++<<<<<<< HEAD | |
+ requestPasswordReset: function(email, onComplete, onFailure) { | |
+ User.addressInfo(email, function(info) { | |
++======= | |
+ requestPasswordReset: function(email, password, onComplete, onFailure) { | |
+ User.addressInfo(email, 'default', function(info) { | |
++>>>>>>> b2g | |
// user is not known. Can't request a password reset. | |
if (info.state === "unknown") { | |
- complete(onComplete, { success: false, reason: "invalid_user" }); | |
+ complete(onComplete, { success: false, reason: "invalid_email" }); | |
} | |
// user is trying to reset the password of a primary address. | |
else if (info.type === "primary") { | |
@@@ -1158,78 -1197,82 +1246,143 @@@ | |
* @param {function} [onComplete] - Called with assertion, null otw. | |
* @param {function} [onFailure] - Called on error. | |
*/ | |
++<<<<<<< HEAD | |
+ getAssertion: function(email, audience, onComplete, onFailure) { | |
+ function complete(status) { | |
+ onComplete && onComplete(status); | |
+ } | |
+ | |
+ var storedID = storage.getEmail(email), | |
+ assertion, | |
+ self=this; | |
+ | |
+ function createAssertion(idInfo) { | |
+ // we use the current time from the browserid servers | |
+ // to avoid issues with clock drift on user's machine. | |
+ // (issue #329) | |
+ network.serverTime(function(serverTime) { | |
+ var sk = jwcrypto.loadSecretKeyFromObject(idInfo.priv); | |
+ | |
+ // assertions are valid for 2 minutes | |
+ var expirationMS = serverTime.getTime() + (2 * 60 * 1000); | |
+ var expirationDate = new Date(expirationMS); | |
+ | |
+ // yield to the render thread, important on IE8 so we don't | |
+ // raise "script has become unresponsive" errors. | |
+ setTimeout(function() { | |
+ jwcrypto.assertion.sign( | |
+ {}, {audience: audience, expiresAt: expirationDate}, | |
+ sk, | |
+ function(err, signedAssertion) { | |
+ assertion = jwcrypto.cert.bundle([idInfo.cert], signedAssertion); | |
+ storage.site.set(audience, "email", email); | |
+ complete(assertion); | |
+ }); | |
+ }, 0); | |
+ }, onFailure); | |
+ } | |
+ | |
+ if (storedID) { | |
+ prepareDeps(); | |
+ if (storedID.priv) { | |
+ // parse the secret key | |
+ // yield to the render thread! | |
+ setTimeout(function() { | |
+ createAssertion(storedID); | |
+ }, 0); | |
+ } | |
+ else { | |
+ // first we have to get the address info, then attempt | |
+ // a provision, then if the user is provisioned, go and get an | |
+ // assertion. | |
+ User.addressInfo(email, function(info) { | |
+ if (info.type === "primary") { | |
+ User.provisionPrimaryUser(email, info, function(status) { | |
+ if (status === "primary.verified") { | |
+ User.getAssertion(email, audience, onComplete, onFailure); | |
+ } | |
+ else { | |
+ complete(null); | |
+ } | |
++======= | |
+ getAssertion: function(email, audience, forceIssuer, onComplete, onFailure) { | |
+ // we use the current time from the browserid servers | |
+ // to avoid issues with clock drift on user's machine. | |
+ // (issue #329) | |
+ function complete(status) { | |
+ onComplete && onComplete(status); | |
+ } | |
+ | |
+ var storedID, | |
+ assertion, | |
+ self=this; | |
+ | |
+ if ('default' === forceIssuer) | |
+ storedID = storage.getEmail(email); | |
+ else | |
+ storedID = storage.getForceIssuerEmail(email, forceIssuer); | |
+ | |
+ function createAssertion(idInfo) { | |
+ network.serverTime(function(serverTime) { | |
+ var sk = jwcrypto.loadSecretKeyFromObject(idInfo.priv); | |
+ | |
+ setTimeout(function() { | |
+ // assertions are valid for 2 minutes | |
+ var expirationMS = serverTime.getTime() + (2 * 60 * 1000); | |
+ var expirationDate = new Date(expirationMS); | |
+ | |
+ jwcrypto.assertion.sign( | |
+ {}, {audience: audience, expiresAt: expirationDate}, | |
+ sk, | |
+ function(err, signedAssertion) { | |
+ assertion = jwcrypto.cert.bundle([idInfo.cert], signedAssertion); | |
+ storage.site.set(audience, "email", email); | |
+ complete(assertion); | |
+ }); | |
+ }, 0); | |
+ }, onFailure); | |
+ } | |
+ | |
+ if (storedID) { | |
+ prepareDeps(); | |
+ if (storedID.priv) { | |
+ // parse the secret key | |
+ // yield to the render thread! | |
+ setTimeout(function() { | |
+ createAssertion(storedID); | |
+ }, 0); | |
+ } | |
+ else { | |
+ // TODO what will the type of forceIssuer email addresses be? | |
+ if (storedID.type === "primary" && 'default' === User.forceIssuer) { | |
+ // first we have to get the address info, then attempt | |
+ // a provision, then if the user is provisioned, go and get an | |
+ // assertion. | |
+ User.addressInfo(email, User.forceIssuer, function(info) { | |
+ User.provisionPrimaryUser(email, info, function(status) { | |
+ if (status === "primary.verified") { | |
+ User.getAssertion(email, audience, User.forceIssuer, onComplete, onFailure); | |
+ } | |
+ else { | |
+ complete(null); | |
+ } | |
+ }, onFailure); | |
++>>>>>>> b2g | |
}, onFailure); | |
} | |
else { | |
// we have no key for this identity, go generate the key, | |
// sync it and then get the assertion recursively. | |
User.syncEmailKeypair(email, function(status) { | |
- User.getAssertion(email, audience, onComplete, onFailure); | |
+ User.getAssertion(email, audience, forceIssuer, onComplete, onFailure); | |
}, onFailure); | |
} | |
- } | |
- } | |
- else { | |
- complete(null); | |
+ }, onFailure); | |
} | |
+ } | |
+ else { | |
+ complete(null); | |
+ } | |
}, | |
/** | |
diff --cc resources/static/dialog/css/style.css | |
index 4019739,96df97e..0000000 | |
--- a/resources/static/dialog/css/style.css | |
+++ b/resources/static/dialog/css/style.css | |
@@@ -76,9 -76,9 +76,13 @@@ h3 | |
.home { | |
- width: 161px; | |
+ width: 211px; | |
height: 40px; | |
++<<<<<<< HEAD | |
+ background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKEAAAAoCAQAAAA851ItAAARRElEQVRo3uVaeXBcR5n/db97bmk0M5JGsmQdtowu6/CBHceGgGMgB8QO3sVZkg0BsixQu1XL7sJSBbVbsAUFy1YoTlOksoFAgbMhCUtIwmEHcOKQyHakxJbt+JJsnTMjzfXmHd29f2hkW8qMreBUYeCNSiV199fv6998/fuObiLwx3lWXmmAH5lFv6+pZ/jCXzLQFoFxFXMRMTZsv9EKSlHp7zFIdd7KP0P+jsacz3Jcq4/ccWegSnHxBxsjl+0EHnhjlSL4nPFk+mZtUr9/Oo9+emxyGta1C2HEW39Gdf/wCRxpKrj6tkP/+3pkOu6RuScoZvm0tMqZYA/Br+ySDOcU/wm9R2LEYdrBx3hhb6omp8UsaLbHlSD1KhukoJNwv8vtawxCfyaepldBiJwYDo91rXvpwNJlVhksv9V6DN6q9vReXyx6Zscy893kWxG+dadcJb4ncqRWfgbvMsA5ATgRkBB/czfrz+5GfkvqKXFNQUglTq9KIypChfhUVW9H19Jlqt12cu+I7txt3G76cuHlFbM3Se8bCmZXRT46ODrmM9+v35tRXMoFSPFDcZ8IFZ47H559R1S60PrH/FyCwNVPJosqc9lE5LqO8FIhdEWCbHrc1A6NHXqRk66gxz6X2vBLgiDdMPR0pN6zY3hdsji7AKggAvgwHWLrvBInLrnGuHBJEHJiS5bMiSCl+1UezddOh3a1LxFERl3FgSNxadZ1lL68pj5rKOuBZSJwczAXJzfleQ0RLnEo5VKEgUAJeQr1jKc5zcnXmjuhV+a6vGz6VjdsalIjM5pTZrzGajN1U5U73qQu5aUNpIEArXrUrWAtxtHR/4q1KKFVbwn+baJN7TKOuRVtnwy06BXs5nSFGr7+AySgVW6+y38SjxpBfVa/1iAkt+1qn7gcgDPaloabAvoR5LHynO8/j/vTSpkQLS+fDo/4Mve/cmWPeSvQ++jAvTiAs7gdD4PhdoRxDI/hbrigoPgR3osDOIO7MYrnsAMjSGIjbNgY7fihew0Ad/QihNsvC2FGWdey8wR+iTlYaibf/+nhWK60A+IkL58Kn88998hSVGgHgQBAwAFIoGAQoCAABAQAUdwkc05FQJrjUTCIawrCy25kRkz/O0/iCczb1Vh0d39dXi7nmz1u3Uw41HPbkvgQLhgYXHBwOLDggsGBDRsO3GKvAwcuHNhwUEABBbgQ1xoXXs6N5JWNMf/TC8Yn3zYxq5cbL4mgtXw6WtnVib+gR4YgZb/WnELGsYjZmn6ZfbtAuVhSFhVm46R7XScGB8u/9E0xaItmICJ17ZUSlgohysdZptJ75jWNybaQndJZORlFRExnnFzXcW4oWW5M8DZJowtmEITJFrP2ssN/khBeJv+VV73y2tY4OyKXhxBQeXXOmbJ2tT/0cqL0iD7kz+ruQlfESdpIbEhuLHznWsuBr4oLqejzLRboVp9kEr98KG6w+syyyart7WWiRM2O5Gqz0ZzfUl2JS1x3KszqbMt020hj2r+L/DlxYcAM3IpvLWrbKJuGS67gFj3OshSj9p3YXap3Lkyx5Heu2BwGgITzYubps55s1NSZG7U6rUHxpwQhuQwXhgsV+Z6ag2MXW7rUxubomCKubCke1ph0ad+tLz5auiJIkFWfPfeJr7ogeLFya2fP+s8eqckF7dqUvW500P1zgVBnsdmZWzruH7rATqG3+jOVlrQEI5GEz21MWdGebQd/XgpAAg5XNeGCoD0p7zs8uOr28VNeN1I450pw58dV0TqEYfJRcVYsnCFMOvg+AQLaRuKgfB+3AaLSdlJBBCCoGOWnxCJepX7SQipAxTCfWNhH/WSlGBETAgS0nqwExDAfEYv1XkbroAMi1X7i5cwSPLIkIoWZWet2fL/Igytq66rPL8UG56SDVmMiv7zbdzhbGkQq5rMRBz3Jj9kF2efqzHDmWmlcu9Eb0Auyy2mhz5TNw+xXc/mMvFVbaRAuZUbZjL7Lz3Qr4+1+4de2tN67zuuoNuUAp3ZbXs+fdp/ixaXSuLbN8BuW4ghSWGnp+dPu49wGCGi7utlQJa5Njf2AvNXoNhzDAsxVWVr4Hi+6RFonrzFaNFO3JSaIo5ibe3P2A0P2FawQ0Fk054nP/7e2vZAO2tKSiUqChI94t5ciQ0EEBC7JNDgmszYlQhISIwDkzoq3xKbDCY0RDrhSWptsnghajwhQvKf65UQ0m9blrpFIZCqSoYLX3Of0bIssqzkXMmXuSBwUQE49H6Ydp58VF+eb1hjlAoya6qR/6oPpB3gW+LLvq2b9KCNqyN7lMSIjwYLGgII86Rvbmfw6B/WpfxXQK9MVr3pthUNQMGrKk/7zd+HbgEzKuBNOXGLKBTkYeMeFto/av4hMFfK2xnR25UItJ2m5M779BWSXBvhZXcsDnGgyIFdGrl8+EssJ6N6YJ+tMpSvNioIUPbfZ2kfQNzNO67IZK6GvGA/YwKx6i7beE6tfcT5iupT6N8cdjGaHE4qp2t+w2wHINZFNc/N5vRVeSRxPVaTDea818r7UtwW2vnr/8njOpknWKntnFM6JJAy30goVHCnbah1fGx/D8rMhiwqbFuTewEuzQStiVhR4rLd74HCZuNAlaa0m/K5Yu09L4ZkLAc3z3dvQcdQ9mBqc0PKXiw3nKjeB6g9MY285d1L8meci3xjpdAjycptvCOHtjeONaVNqb9wVQBIVv4v/5Gg0LyatVZPPcntmzK2ThM+ReU5pqX1L1SSv3h9/cyhRneeEBz/ZqB8ARbzQftjOun0vAhLCtzSNNaRtqTZ+bxgpiETPF14JpJuStpzfXNjXP73dz6YMFimYkuzf3rDan2VfPMJSXhbORVftP/7dwteNE64p98e3RcIKTtt9Xx52EgEnkm5qweGSXOiSaeOuVd2zeBpHFyR4U3gQaGtt63xP+5dOmQmPezkApap/1Mud7RFBhMwNNm/KcktoczRhOAJJbVf+kepQYdmswpPBdyfXftuBhBfWPtnKxqtziRTaxw+mbU6IUERWvrF5axJ7moM4Hn1v4LzCZ9Tro/p93bYAxSF1XTuIAwElHs7WZRR+3vuvbO2XHBAMVG7a9exw0GlIOCtO7WNIuQbRmC21198RwpNrB5/3rXv//nTI1p0a336sPvO7sf+u/XiN/2Xs6UsKDKjX3/OTdNTUnUqtTFyYl7e2dP8cQ2XQOY7junrPhz6fNdxy29mms/7PVxnfLAMgAALDfVat/JilAhLz5uKJeFZCRm6ubXusrUvNGMyk4aD3+zkIEPQ9/+neA9NVVsAMNk8enJuDk7x/62DfXgccKjYThRMIPJT+Fzhz5TSbHgQ4KFr7Q1mDWbQ+4nkwBwGgO/nDo0/olXalpeWUSp7khBOClHHHTP93bDB0Z3dO5JUKe46xHXgf+7c78JXVWRccQJf9xYO5KDHnVlLCCjmZ9HYMlQWwiFH1Q607Eid9Je3QoqngJ1uNr6BsqkZAELKbE9UZRgAiPK7fkfms6o/da64dW7nNPyEJRmtEXyVRCADmnh9nEoHfngpLRXmHRINrHi5AAOBokkYpgcElM3R3esw5LU7yDCsGHf7K4LgkLDmKXlsUS22rXszfQDKq8BdWtx46MDdjRlv34wKKFWWZF4+ZCAQ6bPpdAQ6yTKqgYeo+4KsS8464hEc2pc5Y/S+uyP7JVvu06ndL8eiE9xOt0d2XB5BAQsDRmSBECMJJVtG82+o35XE/R4x6XAIZ+3ORW2wFAkR2f5tebhJovJLQorwtFQQr2rTAjvHPGGHLYA2zmjvtz6wzt5jM+j07KGwJebHMJTClfJ4XVyvQn3yzwajMdVevnZ+RUwZRHHHpWR2BAPcpW41G3VId2ZWYmg+m53tLQagY1lK8aMPLmXqSfy2Ak8ZdbfUPIlleUgACwqJ11Y0VFgU4ImqDXmXhN30HLKignHAI3a2bDeTnglAidKa5FmELAiNGLhZwa371oY88+EplwWDL07FcXjaVjDbRkVqd+z7Nckt3ITgpcHGJeFi2IAuFhQ1a1KiUonNwSp2BGyKJyEndBRgo0R2PM98rA4tFs3LX5FKCkFVHaDNfVDnkJKXe0Nr7BMYue2BTTPC8yuZBUAAESYz2JeaL+nMnhYJE9GWLmEKXhXKpjVxca3f28M9WvufRsZPJrKmxgBO2eS6WPRsa2e48QIUkyKJlCtjcoITN29lrTelim1RZtanlTCyrcMuzIV7riWpHU8+cMIrfYAkuFOQHMx+MMwqKsaGSm7GjAZxAt2+RBVnojNLKyoZ3Hr4Cj85vRPpqrv8ZtxhY8wtLJMUyRFbur76BLVKPYOxBlAKRYfXQodN3bEOT4zlZOJE+cM6bjRW0pFk7Herx2dRgFNIleRXBMbePEwjwEgfsl8JKEdscH2+aIcSs+PQKfQBjmHp4o6mEnLJcGLCPL492c8KpK11XIhUhPOJSDmKY44mGBdIZRaq6K1k6EiwVF3LJhbtA7Tn7aJZGZeII8tP0pxad1oki1BcXfPH9Ljqz8h4JL6grG1Y2r+/63Mv1uQo7mNtSlXcLsofp7hHt4mqpqjs6J3Dk63OPL+K+hVaowFNZN6LzhNYf07/cY3NI2EkdOm/XJSCssFZNWykOQVzKSx49y5wIQOFe99J8OS/Tqn+K4L6rrXwINCeHFZgqO6k6qm2XConKSTpw0GHT4/LxgeeX32qe0ZjMfXIdnpPDlofZ0kXZ6gbdoYITU1l2mF2hHqiafgfIqOrpHttZYJ+Ul6xaa1wrvP6FWzQV+FS0XCT4Gq4QZC5PLsHhDFsHn+orZL2uL+O/Mfk4v2A30h38t/zYfGhOBMS8DyCQVpO1/P/4OQGAgaM/21VYKQQ4/PyGo0/VuHnD1dNKrz0g5rL3dYEUEVl5Q6ThvLig0UV9ivoJIggIkzkREA/TeboZVTyMCIUlpCuUXF8fgJ9YEfoa3oCiPUf/sfu2PTpTk6/JpqLOrtxLIglDbvI1Bwr5tydOC7tUQSO23pvP3Zx2C8f5CUGoGtikp2RhSstDO5M4fctHfp8J25F8dPV5wzkF3b/eb4cLDh3zfXi6xyZX0Gd9KDvld33OaLXUwYZo3Lsl6cQcQHeH1V71Dbmh4tBxzwdbIruXWlCgJe5HXbodXWz49fCGk6f8Ttu0x59YY2kS8+cj5/3WyTBfl/rNPFPR4gwUak8o2zJlS9PGbGOujUuaVZ2qzTs0GfwHuW+a4+Ch38fTE5VW64TWlO6QWdVs46zGx417VkS/waBc0Gixy5u7BNBf2GP4skE7nrTWpLdoTixZkxFwacANZDx3XqHYtSSrIdP637yp7ZHLRYKL07+cQpGXGSl3TN87OLDyybanTwTs5bPVWYcSqJwK2f8f9W0jqyGIJWWUnGLT+eX+TDtQtT/tKSzLsKwjcchc5Ta1Qv/cHNntQGDN3mc+/j/qqbGQZUyakiS8LpAI/HVr7xOrswxKUSNbuvQaQl7OKHmZERcb94/eOHCiwo6anvOmQrjCDd+uhq8diZq6+++6zAgnV3dFM6H2Na3+OV5duszbFNqqiE5JPfmjMlvHQf+eF7Zs3DSYPpCo5gBD2LMi2Evw455XOdrM2rpAtUO4s7dot5GBm/SbNgw7xzNnMzO5hN0ShtJX2UOxuzvJIWDB+OaHdyb7fpMYTUsACnRNuM+j/7RniAEQRY1alS9cPGXMdzZ52JyOvccHWjf0/HZqfHY2p/KGiu6qPkV/9EO3feNEV/St+8jXPvrqSfUq7oLbtKvuzlH87HUJ9SIIAYqTOH3ZcT60oxu8GHyfwPwpcwBrwEAwi4EF41egGXGE4MNZOHgJi0+lK7Ee8eJlnZcwcAlzz2kk42Ji24gm8Et0jGM9ahEGMIohDMBGM3biGPYQsQMdV7mTh7AHf8HP/wM69uKV4IG/LwAAAABJRU5ErkJggg==") 0 0 no-repeat; /* source: /dialog/i/persona-logo-transparent.png */ | |
++======= | |
+ background: url("/dialog/i/firefox-accounts-logo.png") 0 0 no-repeat; | |
++>>>>>>> b2g | |
text-indent: -9999px; | |
display: inline-block; | |
*display: block; | |
diff --cc resources/static/dialog/js/misc/state.js | |
index fce0a92,3dc3d1c..0000000 | |
--- a/resources/static/dialog/js/misc/state.js | |
+++ b/resources/static/dialog/js/misc/state.js | |
@@@ -199,43 -204,56 +205,77 @@@ BrowserID.State = (function() | |
complete(info.complete); | |
}); | |
+ // B2G forceIssuer on primary | |
+ handleState("new_fxaccount", function(msg, info) { | |
+ self.newFxAccountEmail = info.email; | |
+ | |
+ startAction(false, "doSetPassword", info); | |
+ complete(info.complete); | |
+ }); | |
+ | |
handleState("password_set", function(msg, info) { | |
++<<<<<<< HEAD | |
+ /* A password can be set for one of three reasons - | |
+ * 1) This is a new user | |
+ * 2) A user is adding the first secondary address to an account that | |
+ * consists only of primary addresses | |
+ * 3) A primary address was downgraded to a secondary and the user | |
+ * has no password in the DB. | |
+ * | |
+ * #1 is taken care of by newUserEmail, #2 by addEmailEmail, | |
+ * and #3 by transitionNoPassword | |
+ */ | |
+ info = _.extend({ email: self.newUserEmail || self.addEmailEmail || | |
+ self.transitionNoPassword }, info); | |
+ | |
++======= | |
+ /* A password can be set for several reasons | |
+ * 1) This is a new user | |
+ * 2) A user is adding the first secondary address to an account that | |
+ * consists only of primary addresses | |
+ * 3) an existing user has forgotten their password and wants to reset it. | |
+ * 4) A primary address was downgraded to a secondary and the user | |
+ * has no password in the DB. | |
+ * 5) RP is using forceIssuer and we have a primary email address with | |
+ * no password for the user | |
+ * #1 is taken care of by newUserEmail, #2 by addEmailEmail, #3 by resetPasswordEmail, | |
+ * #4 by transitionNoPassword and #5 by fxAccountEmail | |
+ */ | |
+ info = _.extend({ email: self.newUserEmail || self.addEmailEmail || | |
+ self.resetPasswordEmail || self.transitionNoPassword || | |
+ self.newFxAccountEmail}, info); | |
++>>>>>>> b2g | |
if(self.newUserEmail) { | |
startAction(false, "doStageUser", info); | |
} | |
else if(self.addEmailEmail) { | |
startAction(false, "doStageEmail", info); | |
} | |
- else if(self.resetPasswordEmail) { | |
- startAction(false, "doStageResetPassword", info); | |
- } | |
else if (self.transitionNoPassword) { | |
- startAction(false, "doStageResetPassword", info); | |
+ redirectToState("stage_transition_to_secondary", info); | |
} | |
+ else if(self.newFxAccountEmail) { | |
+ startAction(false, "doStageUser", info); | |
+ // TODO startAction(false, "doStageResetPassword", info); ??? | |
+ } | |
}); | |
handleState("user_staged", handleEmailStaged.curry("doConfirmUser")); | |
+ handleState("unverified_created", function(msg, info) { | |
+ startAction(false, "doAuthenticateWithUnverifiedEmail", info); | |
+ }); | |
+ | |
handleState("user_confirmed", handleEmailConfirmed); | |
+ handleState("stage_transition_to_secondary", function(msg, info) { | |
+ startAction(false, "doStageTransitionToSecondary", info); | |
+ }); | |
+ | |
+ handleState("transition_to_secondary_staged", handleEmailStaged.curry("doConfirmTransitionToSecondary")); | |
+ | |
+ handleState("transition_to_secondary_confirmed", handleEmailConfirmed); | |
+ | |
handleState("upgraded_primary_user", function (msg, info) { | |
user.usedAddressAsPrimary(info.email, function () { | |
info.state = 'known'; | |
@@@ -371,9 -415,10 +437,9 @@@ | |
startAction("doAuthenticate", info); | |
} | |
else if ("transition_no_password" === info.state) { | |
- self.transitionNoPassword = info.email; | |
- startAction("doSetPassword", _.extend({transition_no_password: true}, info)); | |
+ redirectToState("transition_no_password", info); | |
} | |
- else if (info.state === 'unverified') { | |
+ else if (info.state === 'unverified' && !self.allowUnverified) { | |
// user selected an unverified secondary email, kick them over to the | |
// verify screen. | |
redirectToState("stage_reverify_email", info); | |
diff --cc resources/static/dialog/js/modules/actions.js | |
index 5e9d7fa,fe8b586..0000000 | |
--- a/resources/static/dialog/js/modules/actions.js | |
+++ b/resources/static/dialog/js/modules/actions.js | |
@@@ -95,8 -95,19 +95,22 @@@ BrowserID.Modules.Actions = (function( | |
startService("required_email", info); | |
}, | |
++<<<<<<< HEAD | |
++======= | |
+ doAuthenticateWithUnverifiedEmail: function(info) { | |
+ var self = this; | |
+ dialogHelpers.authenticateUser.call(this, info.email, info.password, function() { | |
+ self.publish("authenticated", info); | |
+ }); | |
+ }, | |
+ | |
+ doResetPassword: function(info) { | |
+ startService("set_password", _.extend(info, { password_reset: true }), "reset_password"); | |
+ }, | |
+ | |
++>>>>>>> b2g | |
doStageResetPassword: function(info) { | |
- dialogHelpers.resetPassword.call(this, info.email, info.password, info.ready); | |
+ dialogHelpers.resetPassword.call(this, info.email, info.ready); | |
}, | |
doConfirmResetPassword: function(info) { | |
diff --cc resources/static/dialog/js/modules/authenticate.js | |
index 393009c,0595110..0000000 | |
--- a/resources/static/dialog/js/modules/authenticate.js | |
+++ b/resources/static/dialog/js/modules/authenticate.js | |
@@@ -38,8 -39,11 +39,16 @@@ BrowserID.Modules.Authenticate = (funct | |
} | |
function hasPassword(info) { | |
++<<<<<<< HEAD | |
+ return (info && info.email && info.type === "secondary" && | |
+ (info.state === "known" || info.state === "transition_to_secondary" )); | |
++======= | |
+ /*jshint validthis:true*/ | |
+ return (info && info.email && info.type === "secondary" && | |
+ (info.state === "known" || | |
+ info.state === "transition_to_secondary" || | |
+ info.state === "unverified" && this.allowUnverified)); | |
++>>>>>>> b2g | |
} | |
function initialState(info) { | |
@@@ -118,7 -127,19 +132,23 @@@ | |
} | |
} | |
++<<<<<<< HEAD | |
+ function authenticate(done) { | |
++======= | |
+ function createFxAccount(callback, forceIssuer) { | |
+ /*jshint validthis: true*/ | |
+ var self=this, | |
+ email = getEmail(); | |
+ | |
+ if (email) { | |
+ self.close("new_fxaccount", { email: email, fxaccount: true }, { email: email }); | |
+ } else { | |
+ complete(callback); | |
+ } | |
+ } | |
+ | |
+ function authenticate() { | |
++>>>>>>> b2g | |
/*jshint validthis: true*/ | |
var email = getEmail(), | |
pass = helpers.getAndValidatePassword(PASSWORD_SELECTOR), | |
diff --cc resources/static/dialog/js/modules/set_password.js | |
index 6260bac,e8f12bd..0000000 | |
--- a/resources/static/dialog/js/modules/set_password.js | |
+++ b/resources/static/dialog/js/modules/set_password.js | |
@@@ -39,7 -39,9 +39,13 @@@ BrowserID.Modules.SetPassword = (functi | |
password_reset: !!options.password_reset, | |
transition_no_password: !!options.transition_no_password, | |
domain: helpers.getDomainFromEmail(options.email), | |
++<<<<<<< HEAD | |
+ cancelable: options.cancelable !== false | |
++======= | |
+ fxaccount: !!options.fxaccount, | |
+ cancelable: options.cancelable !== false, | |
+ personaTOSPP: options.personaTOSPP | |
++>>>>>>> b2g | |
}); | |
if (options.siteTOSPP) { | |
diff --cc resources/static/dialog/views/set_password.ejs | |
index c3ae67c,377a87d..0000000 | |
--- a/resources/static/dialog/views/set_password.ejs | |
+++ b/resources/static/dialog/views/set_password.ejs | |
@@@ -10,13 -10,15 +10,20 @@@ | |
<ul class="inputs"> | |
<li> | |
<% if (!password_reset) { %> | |
++<<<<<<< HEAD | |
+ <% if (transition_no_password) { %> | |
+ <%- format(gettext("%(idp)s no longer allows you to log into Persona with your %(idp)s password. Please create a new password to use with your Persona account."), { | |
++======= | |
+ <% if (fxaccount) { %> | |
+ <%= gettext("Your email address is new to us. Please create a password to use with FirefoxOS.") %> | |
+ <% } else if (transition_no_password) { %> | |
+ <%- format(gettext("%(idp) no longer allows you to log into Persona with your %(idp) password. Please create a new password to use with your Persona account."), { | |
++>>>>>>> b2g | |
idp: "<strong>" + escape(domain) + "</strong>" | |
}) %> | |
- <% } else { %> | |
- <%= gettext("Your email address is new to us. Please create a password to use with Persona.") %> | |
<% } %> | |
+ <% } else { %> | |
+ <%= gettext("Your email address is new to us. Please create a password to use with Persona.") %> | |
<% } %> | |
</li> | |
diff --cc resources/static/include_js/_jschannel.js | |
index ff80e71,bb99304..0000000 | |
--- a/resources/static/include_js/_jschannel.js | |
+++ b/resources/static/include_js/_jschannel.js | |
@@@ -613,3 -625,695 +613,698 @@@ | |
}; | |
})(); | |
++<<<<<<< HEAD:resources/static/include_js/_jschannel.js | |
++======= | |
+ // local embedded copy of winchan: http://github.com/lloyd/winchan | |
+ // BEGIN WINCHAN | |
+ | |
+ ;WinChan = (function() { | |
+ var RELAY_FRAME_NAME = "__winchan_relay_frame"; | |
+ var CLOSE_CMD = "die"; | |
+ | |
+ // a portable addListener implementation | |
+ function addListener(w, event, cb) { | |
+ if(w.attachEvent) w.attachEvent('on' + event, cb); | |
+ else if (w.addEventListener) w.addEventListener(event, cb, false); | |
+ } | |
+ | |
+ // a portable removeListener implementation | |
+ function removeListener(w, event, cb) { | |
+ if(w.detachEvent) w.detachEvent('on' + event, cb); | |
+ else if (w.removeEventListener) w.removeEventListener(event, cb, false); | |
+ } | |
+ | |
+ // checking for IE8 or above | |
+ function isInternetExplorer() { | |
+ var rv = -1; // Return value assumes failure. | |
+ if (navigator.appName === 'Microsoft Internet Explorer') { | |
+ var ua = navigator.userAgent; | |
+ var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})"); | |
+ if (re.exec(ua) != null) | |
+ rv = parseFloat(RegExp.$1); | |
+ } | |
+ return rv >= 8; | |
+ } | |
+ | |
+ // checking Mobile Firefox (Fennec) | |
+ function isFennec() { | |
+ try { | |
+ // We must check for both XUL and Java versions of Fennec. Both have | |
+ // distinct UA strings. | |
+ var userAgent = navigator.userAgent; | |
+ return (userAgent.indexOf('Fennec/') != -1) || // XUL | |
+ (userAgent.indexOf('Firefox/') != -1 && userAgent.indexOf('Android') != -1); // Java | |
+ } catch(e) {}; | |
+ return false; | |
+ } | |
+ | |
+ // feature checking to see if this platform is supported at all | |
+ function isSupported() { | |
+ return (window.JSON && window.JSON.stringify && | |
+ window.JSON.parse && window.postMessage); | |
+ } | |
+ | |
+ // given a URL, extract the origin | |
+ function extractOrigin(url) { | |
+ if (!/^https?:\/\//.test(url)) url = window.location.href; | |
+ var m = /^(https?:\/\/[\-_a-zA-Z\.0-9:]+)/.exec(url); | |
+ if (m) return m[1]; | |
+ return url; | |
+ } | |
+ | |
+ // find the relay iframe in the opener | |
+ function findRelay() { | |
+ var loc = window.location; | |
+ var frames = window.opener.frames; | |
+ var origin = loc.protocol + '//' + loc.host; | |
+ for (var i = frames.length - 1; i >= 0; i--) { | |
+ try { | |
+ if (frames[i].location.href.indexOf(origin) === 0 && | |
+ frames[i].name === RELAY_FRAME_NAME) | |
+ { | |
+ return frames[i]; | |
+ } | |
+ } catch(e) { } | |
+ } | |
+ return; | |
+ } | |
+ | |
+ var isIE = isInternetExplorer(); | |
+ | |
+ if (isSupported()) { | |
+ /* General flow: | |
+ * 0. user clicks | |
+ * (IE SPECIFIC) 1. caller adds relay iframe (served from trusted domain) to DOM | |
+ * 2. caller opens window (with content from trusted domain) | |
+ * 3. window on opening adds a listener to 'message' | |
+ * (IE SPECIFIC) 4. window on opening finds iframe | |
+ * 5. window checks if iframe is "loaded" - has a 'doPost' function yet | |
+ * (IE SPECIFIC5) 5a. if iframe.doPost exists, window uses it to send ready event to caller | |
+ * (IE SPECIFIC5) 5b. if iframe.doPost doesn't exist, window waits for frame ready | |
+ * (IE SPECIFIC5) 5bi. once ready, window calls iframe.doPost to send ready event | |
+ * 6. caller upon reciept of 'ready', sends args | |
+ */ | |
+ return { | |
+ open: function(opts, cb) { | |
+ if (!cb) throw "missing required callback argument"; | |
+ | |
+ // test required options | |
+ var err; | |
+ if (!opts.url) err = "missing required 'url' parameter"; | |
+ if (!opts.relay_url) err = "missing required 'relay_url' parameter"; | |
+ if (err) setTimeout(function() { cb(err); }, 0); | |
+ | |
+ // supply default options | |
+ if (!opts.window_name) opts.window_name = null; | |
+ if (!opts.window_features || isFennec()) opts.window_features = undefined; | |
+ | |
+ // opts.params may be undefined | |
+ | |
+ var iframe; | |
+ | |
+ // sanity check, are url and relay_url the same origin? | |
+ var origin = extractOrigin(opts.url); | |
+ if (origin !== extractOrigin(opts.relay_url)) { | |
+ return setTimeout(function() { | |
+ cb('invalid arguments: origin of url and relay_url must match'); | |
+ }, 0); | |
+ } | |
+ | |
+ var messageTarget; | |
+ | |
+ if (isIE) { | |
+ // first we need to add a "relay" iframe to the document that's served | |
+ // from the target domain. We can postmessage into a iframe, but not a | |
+ // window | |
+ iframe = document.createElement("iframe"); | |
+ // iframe.setAttribute('name', framename); | |
+ iframe.setAttribute('src', opts.relay_url); | |
+ iframe.style.display = "none"; | |
+ iframe.setAttribute('name', RELAY_FRAME_NAME); | |
+ document.body.appendChild(iframe); | |
+ messageTarget = iframe.contentWindow; | |
+ } | |
+ | |
+ var w = window.open(opts.url, opts.window_name, opts.window_features); | |
+ | |
+ if (!messageTarget) messageTarget = w; | |
+ | |
+ var req = JSON.stringify({a: 'request', d: opts.params}); | |
+ | |
+ // cleanup on unload | |
+ function cleanup() { | |
+ if (iframe) document.body.removeChild(iframe); | |
+ iframe = undefined; | |
+ if (w) { | |
+ try { | |
+ w.close(); | |
+ } catch (securityViolation) { | |
+ // This happens in Opera 12 sometimes | |
+ // see https://github.com/mozilla/browserid/issues/1844 | |
+ messageTarget.postMessage(CLOSE_CMD, origin); | |
+ } | |
+ } | |
+ w = messageTarget = undefined; | |
+ } | |
+ | |
+ addListener(window, 'unload', cleanup); | |
+ | |
+ function onMessage(e) { | |
+ try { | |
+ var d = JSON.parse(e.data); | |
+ if (d.a === 'ready') messageTarget.postMessage(req, origin); | |
+ else if (d.a === 'error') { | |
+ if (cb) { | |
+ cb(d.d); | |
+ cb = null; | |
+ } | |
+ } else if (d.a === 'response') { | |
+ removeListener(window, 'message', onMessage); | |
+ removeListener(window, 'unload', cleanup); | |
+ cleanup(); | |
+ if (cb) { | |
+ cb(null, d.d); | |
+ cb = null; | |
+ } | |
+ } | |
+ } catch(err) { } | |
+ } | |
+ | |
+ addListener(window, 'message', onMessage); | |
+ | |
+ return { | |
+ close: cleanup, | |
+ focus: function() { | |
+ if (w) { | |
+ try { | |
+ w.focus(); | |
+ } catch (e) { | |
+ // IE7 blows up here, do nothing | |
+ } | |
+ } | |
+ } | |
+ }; | |
+ } | |
+ }; | |
+ } else { | |
+ return { | |
+ open: function(url, winopts, arg, cb) { | |
+ setTimeout(function() { cb("unsupported browser"); }, 0); | |
+ } | |
+ }; | |
+ } | |
+ })(); | |
+ | |
+ | |
+ | |
+ // END WINCHAN | |
+ | |
+ var BrowserSupport = (function() { | |
+ var win = window, | |
+ nav = navigator, | |
+ reason; | |
+ | |
+ // For unit testing | |
+ function setTestEnv(newNav, newWindow) { | |
+ nav = newNav; | |
+ win = newWindow; | |
+ } | |
+ | |
+ function getInternetExplorerVersion() { | |
+ var rv = -1; // Return value assumes failure. | |
+ if (nav.appName == 'Microsoft Internet Explorer') { | |
+ var ua = nav.userAgent; | |
+ var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})"); | |
+ if (re.exec(ua) != null) | |
+ rv = parseFloat(RegExp.$1); | |
+ } | |
+ | |
+ return rv; | |
+ } | |
+ | |
+ function checkIE() { | |
+ var ieVersion = getInternetExplorerVersion(), | |
+ ieNosupport = ieVersion > -1 && ieVersion < 8; | |
+ | |
+ if(ieNosupport) { | |
+ return "BAD_IE_VERSION"; | |
+ } | |
+ } | |
+ | |
+ function explicitNosupport() { | |
+ return checkIE(); | |
+ } | |
+ | |
+ function checkLocalStorage() { | |
+ // Firefox/Fennec/Chrome blow up when trying to access or | |
+ // write to localStorage. We must do two explicit checks, first | |
+ // whether the browser has localStorage. Second, we must check | |
+ // whether the localStorage can be written to. Firefox (at v11) | |
+ // throws an exception when querying win['localStorage'] | |
+ // when cookies are disabled. Chrome (v17) excepts when trying to | |
+ // write to localStorage when cookies are disabled. If an | |
+ // exception is thrown, then localStorage is disabled. If no | |
+ // exception is thrown, hasLocalStorage will be true if the | |
+ // browser supports localStorage and it can be written to. | |
+ try { | |
+ var hasLocalStorage = 'localStorage' in win | |
+ // Firefox will except here if cookies are disabled. | |
+ && win['localStorage'] !== null; | |
+ | |
+ if(hasLocalStorage) { | |
+ // browser has localStorage, check if it can be written to. If | |
+ // cookies are disabled, some browsers (Chrome) will except here. | |
+ win['localStorage'].setItem("test", "true"); | |
+ win['localStorage'].removeItem("test"); | |
+ } | |
+ else { | |
+ // Browser does not have local storage. | |
+ return "LOCALSTORAGE_NOT_SUPPORTED"; | |
+ } | |
+ } catch(e) { | |
+ return "LOCALSTORAGE_DISABLED"; | |
+ } | |
+ } | |
+ | |
+ function checkPostMessage() { | |
+ if(!win.postMessage) { | |
+ return "POSTMESSAGE_NOT_SUPPORTED"; | |
+ } | |
+ } | |
+ | |
+ function checkJSON() { | |
+ if(!(window.JSON && window.JSON.stringify && window.JSON.parse)) { | |
+ return "JSON_NOT_SUPPORTED"; | |
+ } | |
+ } | |
+ | |
+ function isSupported() { | |
+ reason = explicitNosupport() || checkLocalStorage() || checkPostMessage() || checkJSON(); | |
+ | |
+ return !reason; | |
+ } | |
+ | |
+ | |
+ function getNoSupportReason() { | |
+ return reason; | |
+ } | |
+ | |
+ return { | |
+ /** | |
+ * Set the test environment. | |
+ * @method setTestEnv | |
+ */ | |
+ setTestEnv: setTestEnv, | |
+ /** | |
+ * Check whether the current browser is supported | |
+ * @method isSupported | |
+ * @returns {boolean} | |
+ */ | |
+ isSupported: isSupported, | |
+ /** | |
+ * Called after isSupported, if isSupported returns false. Gets the reason | |
+ * why browser is not supported. | |
+ * @method getNoSupportReason | |
+ * @returns {string} | |
+ */ | |
+ getNoSupportReason: getNoSupportReason | |
+ }; | |
+ }()); | |
+ | |
+ if (!navigator.id) { | |
+ // Is there a native implementation on this platform? | |
+ // If so, hook navigator.id onto it. | |
+ if (navigator.mozId) { | |
+ navigator.id = navigator.mozId; | |
+ } else { | |
+ navigator.id = {}; | |
+ } | |
+ } | |
+ | |
+ if (!navigator.id.request || navigator.id._shimmed) { | |
+ var ipServer = "https://login.persona.org"; | |
+ var userAgent = navigator.userAgent; | |
+ // We must check for both XUL and Java versions of Fennec. Both have | |
+ // distinct UA strings. | |
+ var isFennec = (userAgent.indexOf('Fennec/') != -1) || // XUL | |
+ (userAgent.indexOf('Firefox/') != -1 && userAgent.indexOf('Android') != -1); // Java | |
+ | |
+ var windowOpenOpts = | |
+ (isFennec ? undefined : | |
+ "menubar=0,location=1,resizable=1,scrollbars=1,status=0,dialog=1,minimizable=1,width=700,height=375"); | |
+ | |
+ var w; | |
+ | |
+ // table of registered observers | |
+ var observers = { | |
+ login: null, | |
+ logout: null, | |
+ ready: null | |
+ }; | |
+ | |
+ var loggedInUser; | |
+ | |
+ var compatMode = undefined; | |
+ function checkCompat(requiredMode) { | |
+ if (requiredMode === true) { | |
+ // this deprecation warning should be re-enabled when the .watch and .request APIs become final. | |
+ // try { console.log("this site uses deprecated APIs (see documentation for navigator.id.request())"); } catch(e) { } | |
+ } | |
+ | |
+ if (compatMode === undefined) compatMode = requiredMode; | |
+ else if (compatMode != requiredMode) { | |
+ throw new Error("you cannot combine the navigator.id.watch() API with navigator.id.getVerifiedEmail() or navigator.id.get()" + | |
+ "this site should instead use navigator.id.request() and navigator.id.watch()"); | |
+ } | |
+ } | |
+ | |
+ var commChan, | |
+ waitingForDOM = false, | |
+ browserSupported = BrowserSupport.isSupported(); | |
+ | |
+ function domReady(callback) { | |
+ if (document.addEventListener) { | |
+ document.addEventListener('DOMContentLoaded', function contentLoaded() { | |
+ document.removeEventListener('DOMContentLoaded', contentLoaded); | |
+ callback(); | |
+ }, false); | |
+ } else if (document.attachEvent && document.readyState) { | |
+ document.attachEvent('onreadystatechange', function ready() { | |
+ var state = document.readyState; | |
+ // 'interactive' is the same as DOMContentLoaded, | |
+ // but not all browsers use it, sadly. | |
+ if (state === 'loaded' || state === 'complete' || state === 'interactive') { | |
+ document.detachEvent('onreadystatechange', ready); | |
+ callback(); | |
+ } | |
+ }); | |
+ } | |
+ } | |
+ | |
+ | |
+ // this is for calls that are non-interactive | |
+ function _open_hidden_iframe() { | |
+ // If this is an unsupported browser, do not even attempt to add the | |
+ // IFRAME as doing so will cause an exception to be thrown in IE6 and IE7 | |
+ // from within the communication_iframe. | |
+ if(!browserSupported) return; | |
+ var doc = window.document; | |
+ | |
+ // can't attach iframe and make commChan without the body | |
+ if (!doc.body) { | |
+ if (!waitingForDOM) { | |
+ domReady(_open_hidden_iframe); | |
+ waitingForDOM = true; | |
+ } | |
+ return; | |
+ } | |
+ | |
+ try { | |
+ if (!commChan) { | |
+ var iframe = doc.createElement("iframe"); | |
+ iframe.style.display = "none"; | |
+ doc.body.appendChild(iframe); | |
+ iframe.src = ipServer + "/communication_iframe"; | |
+ commChan = Channel.build({ | |
+ window: iframe.contentWindow, | |
+ origin: ipServer, | |
+ scope: "mozid_ni", | |
+ onReady: function() { | |
+ // once the channel is set up, we'll fire a loaded message. this is the | |
+ // cutoff point where we'll say if 'setLoggedInUser' was not called before | |
+ // this point, then it wont be called (XXX: optimize and improve me) | |
+ commChan.call({ | |
+ method: 'loaded', | |
+ success: function(){ | |
+ // NOTE: Do not modify without reading GH-2017 | |
+ if (observers.ready) observers.ready(); | |
+ }, error: function() { | |
+ } | |
+ }); | |
+ } | |
+ }); | |
+ | |
+ commChan.bind('logout', function(trans, params) { | |
+ if (observers.logout) observers.logout(); | |
+ }); | |
+ | |
+ commChan.bind('login', function(trans, params) { | |
+ if (observers.login) observers.login(params); | |
+ }); | |
+ | |
+ if (defined(loggedInUser)) { | |
+ commChan.notify({ | |
+ method: 'loggedInUser', | |
+ params: loggedInUser | |
+ }); | |
+ } | |
+ } | |
+ } catch(e) { | |
+ // channel building failed! let's ignore the error and allow higher | |
+ // level code to handle user messaging. | |
+ commChan = undefined; | |
+ } | |
+ } | |
+ | |
+ function defined(item) { | |
+ return typeof item !== "undefined"; | |
+ } | |
+ | |
+ function warn(message) { | |
+ try { | |
+ console.warn(message); | |
+ } catch(e) { | |
+ /* ignore error */ | |
+ } | |
+ } | |
+ | |
+ function checkDeprecated(options, field) { | |
+ if(defined(options[field])) { | |
+ warn(field + " has been deprecated"); | |
+ return true; | |
+ } | |
+ } | |
+ | |
+ function checkRenamed(options, oldName, newName) { | |
+ if (defined(options[oldName]) && | |
+ defined(options[newName])) { | |
+ throw new Error("you cannot supply *both* " + oldName + " and " + newName); | |
+ } | |
+ else if(checkDeprecated(options, oldName)) { | |
+ options[newName] = options[oldName]; | |
+ delete options[oldName]; | |
+ } | |
+ } | |
+ | |
+ function internalWatch(options) { | |
+ if (typeof options !== 'object') return; | |
+ | |
+ if (options.onlogin && typeof options.onlogin !== 'function' || | |
+ options.onlogout && typeof options.onlogout !== 'function' || | |
+ options.onready && typeof options.onready !== 'function') | |
+ { | |
+ throw new Error("non-function where function expected in parameters to navigator.id.watch()"); | |
+ } | |
+ | |
+ if (!options.onlogin) throw new Error("'onlogin' is a required argument to navigator.id.watch()"); | |
+ if (!options.onlogout) throw new Error("'onlogout' is a required argument to navigator.id.watch()"); | |
+ | |
+ observers.login = options.onlogin || null; | |
+ observers.logout = options.onlogout || null; | |
+ // NOTE: Do not modify without reading GH-2017 | |
+ observers.ready = options.onready || null; | |
+ | |
+ // back compat support for loggedInEmail | |
+ checkRenamed(options, "loggedInEmail", "loggedInUser"); | |
+ loggedInUser = options.loggedInUser; | |
+ | |
+ _open_hidden_iframe(); | |
+ } | |
+ | |
+ var api_called; | |
+ function getRPAPI() { | |
+ var rp_api = api_called; | |
+ if (rp_api === "request") { | |
+ if (observers.ready) rp_api = "watch_with_onready"; | |
+ else rp_api = "watch_without_onready"; | |
+ } | |
+ | |
+ return rp_api; | |
+ } | |
+ | |
+ function internalRequest(options) { | |
+ checkDeprecated(options, "requiredEmail"); | |
+ checkRenamed(options, "tosURL", "termsOfService"); | |
+ checkRenamed(options, "privacyURL", "privacyPolicy"); | |
+ | |
+ if (options.termsOfService && !options.privacyPolicy) { | |
+ warn("termsOfService ignored unless privacyPolicy also defined"); | |
+ } | |
+ | |
+ if (options.privacyPolicy && !options.termsOfService) { | |
+ warn("privacyPolicy ignored unless termsOfService also defined"); | |
+ } | |
+ | |
+ options.rp_api = getRPAPI(); | |
+ // reset the api_called in case the site implementor changes which api | |
+ // method called the next time around. | |
+ api_called = null; | |
+ | |
+ options.start_time = (new Date()).getTime(); | |
+ | |
+ // focus an existing window | |
+ if (w) { | |
+ try { | |
+ w.focus(); | |
+ } | |
+ catch(e) { | |
+ /* IE7 blows up here, do nothing */ | |
+ } | |
+ return; | |
+ } | |
+ | |
+ if (!BrowserSupport.isSupported()) { | |
+ var reason = BrowserSupport.getNoSupportReason(), | |
+ url = "unsupported_dialog"; | |
+ | |
+ if(reason === "LOCALSTORAGE_DISABLED") { | |
+ url = "cookies_disabled"; | |
+ } | |
+ | |
+ w = window.open( | |
+ ipServer + "/" + url, | |
+ null, | |
+ windowOpenOpts); | |
+ return; | |
+ } | |
+ | |
+ // notify the iframe that the dialog is running so we | |
+ // don't do duplicative work | |
+ if (commChan) commChan.notify({ method: 'dialog_running' }); | |
+ | |
+ w = WinChan.open({ | |
+ url: ipServer + '/sign_in', | |
+ relay_url: ipServer + '/relay', | |
+ window_features: windowOpenOpts, | |
+ window_name: '__persona_dialog', | |
+ params: { | |
+ method: "get", | |
+ params: options | |
+ } | |
+ }, function(err, r) { | |
+ // unpause the iframe to detect future changes in login state | |
+ if (commChan) { | |
+ // update the loggedInUser in the case that an assertion was generated, as | |
+ // this will prevent the comm iframe from thinking that state has changed | |
+ // and generating a new assertion. IF, however, this request is not a success, | |
+ // then we do not change the loggedInUser - and we will let the comm frame determine | |
+ // if generating a logout event is the right thing to do | |
+ if (!err && r && r.email) { | |
+ commChan.notify({ method: 'loggedInUser', params: r.email }); | |
+ } | |
+ commChan.notify({ method: 'dialog_complete' }); | |
+ } | |
+ | |
+ // clear the window handle | |
+ w = undefined; | |
+ if (!err && r && r.assertion) { | |
+ try { | |
+ if (observers.login) observers.login(r.assertion); | |
+ } catch(e) { | |
+ // client's observer threw an exception | |
+ } | |
+ } | |
+ | |
+ // if either err indicates the user canceled the signin (expected) or a | |
+ // null response was sent (unexpected), invoke the .oncancel() handler. | |
+ if (err === 'client closed window' || !r) { | |
+ if (options && options.oncancel) options.oncancel(); | |
+ delete options.oncancel; | |
+ } | |
+ }); | |
+ }; | |
+ | |
+ navigator.id = { | |
+ request: function(options) { | |
+ if (this != navigator.id) | |
+ throw new Error("all navigator.id calls must be made on the navigator.id object"); | |
+ options = options || {}; | |
+ checkCompat(false); | |
+ api_called = "request"; | |
+ // returnTo is used for post-email-verification redirect | |
+ if (!options.returnTo) options.returnTo = document.location.pathname; | |
+ return internalRequest(options); | |
+ }, | |
+ watch: function(options) { | |
+ if (this != navigator.id) | |
+ throw new Error("all navigator.id calls must be made on the navigator.id object"); | |
+ checkCompat(false); | |
+ internalWatch(options); | |
+ }, | |
+ // logout from the current website | |
+ // The callback parameter is DEPRECATED, instead you should use the | |
+ // the .onlogout observer of the .watch() api. | |
+ logout: function(callback) { | |
+ if (this != navigator.id) | |
+ throw new Error("all navigator.id calls must be made on the navigator.id object"); | |
+ // allocate iframe if it is not allocated | |
+ _open_hidden_iframe(); | |
+ // send logout message if the commChan exists | |
+ if (commChan) commChan.notify({ method: 'logout' }); | |
+ if (typeof callback === 'function') { | |
+ warn('navigator.id.logout callback argument has been deprecated.'); | |
+ setTimeout(callback, 0); | |
+ } | |
+ }, | |
+ // get an assertion | |
+ get: function(callback, passedOptions) { | |
+ var opts = {}; | |
+ passedOptions = passedOptions || {}; | |
+ opts.privacyPolicy = passedOptions.privacyPolicy || undefined; | |
+ opts.termsOfService = passedOptions.termsOfService || undefined; | |
+ opts.privacyURL = passedOptions.privacyURL || undefined; | |
+ opts.tosURL = passedOptions.tosURL || undefined; | |
+ opts.siteName = passedOptions.siteName || undefined; | |
+ opts.siteLogo = passedOptions.siteLogo || undefined; | |
+ // api_called could have been set to getVerifiedEmail already | |
+ api_called = api_called || "get"; | |
+ if (checkDeprecated(passedOptions, "silent")) { | |
+ // Silent has been deprecated, do nothing. Placing the check here | |
+ // prevents the callback from being called twice, once with null and | |
+ // once after internalWatch has been called. See issue #1532 | |
+ if (callback) setTimeout(function() { callback(null); }, 0); | |
+ return; | |
+ } | |
+ | |
+ checkCompat(true); | |
+ internalWatch({ | |
+ onlogin: function(assertion) { | |
+ if (callback) { | |
+ callback(assertion); | |
+ callback = null; | |
+ } | |
+ }, | |
+ onlogout: function() {} | |
+ }); | |
+ opts.oncancel = function() { | |
+ if (callback) { | |
+ callback(null); | |
+ callback = null; | |
+ } | |
+ observers.login = observers.logout = observers.ready = null; | |
+ }; | |
+ internalRequest(opts); | |
+ }, | |
+ // backwards compatibility with old API | |
+ getVerifiedEmail: function(callback) { | |
+ warn("navigator.id.getVerifiedEmail has been deprecated"); | |
+ checkCompat(true); | |
+ api_called = "getVerifiedEmail"; | |
+ navigator.id.get(callback); | |
+ }, | |
+ // required for forwards compatibility with native implementations | |
+ _shimmed: true | |
+ }; | |
+ } | |
+ }()); | |
++>>>>>>> b2g:resources/static/include_js/include.js | |
diff --cc resources/static/test/cases/common/js/network.js | |
index e7e141e,2b1dfc0..0000000 | |
--- a/resources/static/test/cases/common/js/network.js | |
+++ b/resources/static/test/cases/common/js/network.js | |
@@@ -493,6 -449,71 +493,74 @@@ | |
}); | |
++<<<<<<< HEAD | |
++======= | |
+ asyncTest("requestPasswordReset - true status", function() { | |
+ network.requestPasswordReset(TEST_EMAIL, "password", "origin", function onSuccess(status) { | |
+ ok(status, "password reset request success"); | |
+ start(); | |
+ }, testHelpers.unexpectedFailure); | |
+ }); | |
+ | |
+ asyncTest("requestPasswordReset with XHR failure", function() { | |
+ failureCheck(network.requestPasswordReset, TEST_EMAIL, "password", "origin"); | |
+ }); | |
+ | |
+ asyncTest("completePasswordReset with valid token, no password required", function() { | |
+ network.completePasswordReset("token", undefined, function(registered) { | |
+ ok(registered); | |
+ start(); | |
+ }, testHelpers.unexpectedFailure); | |
+ }); | |
+ | |
+ asyncTest("completePasswordReset with valid token, bad password", function() { | |
+ transport.useResult("badPassword"); | |
+ network.completePasswordReset("token", "password", | |
+ testHelpers.unexpectedSuccess, | |
+ testHelpers.expectedXHRFailure); | |
+ }); | |
+ | |
+ asyncTest("completePasswordReset with valid token, password required", function() { | |
+ network.completePasswordReset("token", "password", function(registered) { | |
+ ok(registered); | |
+ start(); | |
+ }, testHelpers.unexpectedFailure); | |
+ }); | |
+ | |
+ asyncTest("completePasswordReset with invalid token", function() { | |
+ transport.useResult("invalid"); | |
+ | |
+ network.completePasswordReset("token", "password", function(registered) { | |
+ equal(registered, false); | |
+ start(); | |
+ }, testHelpers.unexpectedFailure); | |
+ }); | |
+ | |
+ asyncTest("completePasswordReset with XHR failure", function() { | |
+ failureCheck(network.completePasswordReset, "token", "password"); | |
+ }); | |
+ | |
+ asyncTest("checkPasswordReset pending", testVerificationPending.curry("checkPasswordReset")); | |
+ asyncTest("checkPasswordReset mustAuth", testVerificationMustAuth.curry("checkPasswordReset")); | |
+ asyncTest("checkPasswordReset complete", testVerificationComplete.curry("checkPasswordReset")); | |
+ | |
+ | |
+ asyncTest("requestEmailReverify - true status", function() { | |
+ network.requestEmailReverify(TEST_EMAIL, "origin", function onSuccess(status) { | |
+ ok(status, "password reset request success"); | |
+ start(); | |
+ }, testHelpers.unexpectedFailure); | |
+ }); | |
+ | |
+ asyncTest("requestEmailReverify with XHR failure", function() { | |
+ failureCheck(network.requestEmailReverify, TEST_EMAIL, "origin"); | |
+ }); | |
+ | |
+ asyncTest("checkEmailReverify pending", testVerificationPending.curry("checkEmailReverify")); | |
+ asyncTest("checkEmailReverify mustAuth", testVerificationMustAuth.curry("checkEmailReverify")); | |
+ asyncTest("checkEmailReverify complete", testVerificationComplete.curry("checkEmailReverify")); | |
+ | |
++>>>>>>> b2g | |
asyncTest("serverTime", function() { | |
// I am forcing the server time to be 1.25 seconds off. | |
transport.setContextInfo("server_time", new Date().getTime() - 1250); | |
diff --cc resources/static/test/cases/common/js/user.js | |
index 63e61b2,6d8ca5e..0000000 | |
--- a/resources/static/test/cases/common/js/user.js | |
+++ b/resources/static/test/cases/common/js/user.js | |
@@@ -637,6 -483,117 +637,120 @@@ | |
}, testHelpers.unexpectedFailure); | |
}); | |
++<<<<<<< HEAD | |
++======= | |
+ asyncTest("requestPasswordReset with known email - true status", function() { | |
+ var returnTo = "http://samplerp.org"; | |
+ lib.setReturnTo(returnTo); | |
+ | |
+ lib.requestPasswordReset("[email protected]", "password", function(status) { | |
+ equal(status.success, true, "password reset for known user"); | |
+ equal(storage.getReturnTo(), returnTo, "RP URL is stored for verification"); | |
+ | |
+ start(); | |
+ }, testHelpers.unexpectedXHRFailure); | |
+ }); | |
+ | |
+ asyncTest("requestPasswordReset with unknown email - false status, invalid_user", function() { | |
+ lib.requestPasswordReset("[email protected]", "password", function(status) { | |
+ equal(status.success, false, "password not reset for unknown user"); | |
+ equal(status.reason, "invalid_user", "invalid_user is the reason"); | |
+ start(); | |
+ }, testHelpers.unexpectedXHRFailure); | |
+ }); | |
+ | |
+ asyncTest("requestPasswordReset with throttle - false status, throttle", function() { | |
+ xhr.useResult("throttle"); | |
+ lib.requestPasswordReset("[email protected]", "password", function(status) { | |
+ equal(status.success, false, "password not reset for throttle"); | |
+ equal(status.reason, "throttle", "password reset was throttled"); | |
+ start(); | |
+ }, testHelpers.unexpectedXHRFailure); | |
+ }); | |
+ | |
+ asyncTest("requestPasswordReset with XHR failure", function() { | |
+ failureCheck(lib.requestPasswordReset, "[email protected]", "password"); | |
+ }); | |
+ | |
+ asyncTest("completePasswordReset with a good token", function() { | |
+ storage.addEmail(TEST_EMAIL); | |
+ storage.setReturnTo(testOrigin); | |
+ | |
+ lib.completePasswordReset("token", "password", function onSuccess(info) { | |
+ testObjectValuesEqual(info, { | |
+ valid: true, | |
+ email: TEST_EMAIL, | |
+ returnTo: testOrigin | |
+ }); | |
+ | |
+ equal(storage.getReturnTo(), "", "initiating origin was removed"); | |
+ | |
+ start(); | |
+ }, testHelpers.unexpectedXHRFailure); | |
+ }); | |
+ | |
+ asyncTest("completePasswordReset with a bad token", function() { | |
+ xhr.useResult("invalid"); | |
+ | |
+ lib.completePasswordReset("token", "password", function onSuccess(info) { | |
+ equal(info.valid, false, "bad token calls onSuccess with a false validity"); | |
+ start(); | |
+ }, testHelpers.unexpectedXHRFailure); | |
+ }); | |
+ | |
+ asyncTest("completePasswordReset with an XHR failure", function() { | |
+ xhr.useResult("ajaxError"); | |
+ | |
+ lib.completePasswordReset( | |
+ "token", | |
+ "password", | |
+ testHelpers.unexpectedSuccess, | |
+ testHelpers.expectedXHRFailure | |
+ ); | |
+ }); | |
+ | |
+ asyncTest("requestEmailReverify with owned unverified email - false status", function() { | |
+ storage.addEmail(TEST_EMAIL); | |
+ | |
+ var returnTo = "http://samplerp.org"; | |
+ lib.setReturnTo(returnTo); | |
+ lib.requestEmailReverify(TEST_EMAIL, function(status) { | |
+ equal(status.success, true, "password reset for known user"); | |
+ equal(storage.getReturnTo(), returnTo, "RP URL is stored for verification"); | |
+ | |
+ start(); | |
+ }, testHelpers.unexpectedXHRFailure); | |
+ }); | |
+ | |
+ asyncTest("requestEmailReverify with unowned email - false status, invalid_user", function() { | |
+ lib.requestEmailReverify(TEST_EMAIL, function(status) { | |
+ testObjectValuesEqual(status, { | |
+ success: false, | |
+ reason: "invalid_email" | |
+ }); | |
+ start(); | |
+ }, testHelpers.unexpectedXHRFailure); | |
+ }); | |
+ | |
+ asyncTest("requestEmailReverify owned email with throttle - false status, throttle", function() { | |
+ xhr.useResult("throttle"); | |
+ storage.addEmail(TEST_EMAIL); | |
+ | |
+ lib.requestEmailReverify(TEST_EMAIL, function(status) { | |
+ testObjectValuesEqual(status, { | |
+ success: false, | |
+ reason: "throttle" | |
+ }); | |
+ start(); | |
+ }, testHelpers.unexpectedXHRFailure); | |
+ }); | |
+ | |
+ asyncTest("requestEmailReverify with XHR failure", function() { | |
+ storage.addEmail(TEST_EMAIL); | |
+ failureCheck(lib.requestEmailReverify, TEST_EMAIL); | |
+ }); | |
+ | |
++>>>>>>> b2g | |
asyncTest("authenticate with valid credentials, also syncs email with server", function() { | |
lib.authenticate(TEST_EMAIL, "testuser", function(authenticated) { | |
equal(true, authenticated, "we are authenticated!"); | |
@@@ -808,9 -781,137 +922,140 @@@ | |
}); | |
}); | |
++<<<<<<< HEAD | |
++======= | |
+ asyncTest("addEmail", function() { | |
+ var returnTo = "http://samplerp.org"; | |
+ lib.setReturnTo(returnTo); | |
+ | |
+ lib.addEmail("[email protected]", "password", function(added) { | |
+ ok(added, "user was added"); | |
+ | |
+ var identities = lib.getStoredEmailKeypairs(); | |
+ equal("[email protected]" in identities, false, "new email is not added until confirmation."); | |
+ | |
+ equal(storage.getReturnTo(), returnTo, "RP URL is stored for verification"); | |
+ | |
+ start(); | |
+ }, testHelpers.unexpectedXHRFailure); | |
+ }); | |
+ | |
+ asyncTest("addEmail with addition refused", function() { | |
+ xhr.useResult("throttle"); | |
+ | |
+ lib.addEmail("[email protected]", "password", function(added) { | |
+ equal(added, false, "user addition was refused"); | |
+ | |
+ var identities = lib.getStoredEmailKeypairs(); | |
+ equal(false, "[email protected]" in identities, "Our new email is not added until confirmation."); | |
+ | |
+ equal(typeof storage.getReturnTo(), "undefined", "initiatingOrigin is not stored"); | |
+ | |
+ start(); | |
+ }, testHelpers.unexpectedXHRFailure); | |
+ }); | |
+ | |
+ asyncTest("addEmail with XHR failure", function() { | |
+ failureCheck(lib.addEmail, "[email protected]", "password"); | |
+ }); | |
+ | |
+ | |
+ asyncTest("waitForEmailValidation with `complete` backend response, user authenticated to assertion level - expect 'mustAuth'", function() { | |
+ testAddressVerificationPoll("password", "complete", "waitForEmailValidation", "complete"); | |
+ }); | |
+ | |
+ asyncTest("waitForEmailValidation with assertion authentication, complete from backend - return mustAuth status", function() { | |
+ testAddressVerificationPoll("assertion", "complete", "waitForEmailValidation", "mustAuth"); | |
+ }); | |
+ | |
+ asyncTest("waitForEmailValidation `mustAuth` response", function() { | |
+ testAddressVerificationPoll("assertion", "mustAuth", "waitForEmailValidation", "mustAuth"); | |
+ }); | |
+ | |
+ asyncTest("waitForEmailValidation with `noRegistration` response", function() { | |
+ storage.setReturnTo(testOrigin); | |
+ xhr.useResult("noRegistration"); | |
+ | |
+ lib.waitForEmailValidation( | |
+ "[email protected]", | |
+ testHelpers.unexpectedSuccess, | |
+ function(status) { | |
+ ok(storage.getReturnTo(), "staged on behalf of is cleared when validation completes"); | |
+ ok(status, "noRegistration", "noRegistration response causes failure"); | |
+ start(); | |
+ }); | |
+ }); | |
+ | |
+ | |
+ asyncTest("waitForEmailValidation XHR failure", function() { | |
+ storage.setReturnTo(testOrigin); | |
+ xhr.useResult("ajaxError"); | |
+ | |
+ lib.waitForEmailValidation( | |
+ "[email protected]", | |
+ testHelpers.unexpectedSuccess, | |
+ testHelpers.expectedXHRFailure | |
+ ); | |
+ }); | |
+ | |
+ | |
+ asyncTest("cancelEmailValidation: ~1 second", function() { | |
+ xhr.useResult("pending"); | |
+ | |
+ storage.setReturnTo(testOrigin); | |
+ lib.waitForEmailValidation( | |
+ "[email protected]", | |
+ testHelpers.unexpectedSuccess, | |
+ testHelpers.unexpectedXHRFailure | |
+ ); | |
+ | |
+ setTimeout(function() { | |
+ lib.cancelUserValidation(); | |
+ ok(storage.getReturnTo(), "staged on behalf of is not cleared when validation cancelled"); | |
+ start(); | |
+ }, 500); | |
+ }); | |
+ | |
+ asyncTest("verifyEmail with a good token - callback with email, returnTo, valid", function() { | |
+ storage.setReturnTo(testOrigin); | |
+ storage.addEmail(TEST_EMAIL); | |
+ lib.verifyEmail("token", "password", function onSuccess(info) { | |
+ testObjectValuesEqual(info, { | |
+ valid: true, | |
+ email: TEST_EMAIL, | |
+ returnTo: testOrigin | |
+ }); | |
+ equal(storage.getReturnTo(), "", "initiating returnTo was removed"); | |
+ | |
+ start(); | |
+ }, testHelpers.unexpectedXHRFailure); | |
+ }); | |
+ | |
+ asyncTest("verifyEmail with a bad token - callback with valid: false", function() { | |
+ xhr.useResult("invalid"); | |
+ | |
+ lib.verifyEmail("token", "password", function onSuccess(info) { | |
+ equal(info.valid, false, "bad token calls onSuccess with a false validity"); | |
+ | |
+ start(); | |
+ }, testHelpers.unexpectedXHRFailure); | |
+ }); | |
+ | |
+ asyncTest("verifyEmail with an XHR failure", function() { | |
+ xhr.useResult("ajaxError"); | |
+ | |
+ lib.verifyEmail( | |
+ "token", | |
+ "password", | |
+ testHelpers.unexpectedSuccess, | |
+ testHelpers.expectedXHRFailure | |
+ ); | |
+ }); | |
+ | |
++>>>>>>> b2g | |
asyncTest("syncEmailKeypair with successful sync", function() { | |
- lib.syncEmailKeypair("[email protected]", function(keypair) { | |
- var identity = lib.getStoredEmailKeypair("[email protected]"); | |
+ lib.syncEmailKeypair("[email protected]", function(keypair) { | |
+ var identity = lib.getStoredEmailKeypair("[email protected]"); | |
ok(identity, "we have an identity"); | |
ok(identity.priv, "a private key is on the identity"); | |
diff --cc resources/static/test/mocks/xhr.js | |
index 1a48a07,04c6fbf..0000000 | |
--- a/resources/static/test/mocks/xhr.js | |
+++ b/resources/static/test/mocks/xhr.js | |
@@@ -177,32 -156,29 +177,41 @@@ BrowserID.Mocks.xhr = (function() | |
"post /wsapi/update_password valid": { success: true }, | |
"post /wsapi/update_password incorrectPassword": { success: false }, | |
"post /wsapi/update_password invalid": undefined, | |
- "get /wsapi/address_info?email=unregistered%40testuser.com invalid": undefined, | |
- "get /wsapi/address_info?email=unregistered%40testuser.com throttle": { type: "secondary", state: "unknown" }, | |
- "get /wsapi/address_info?email=unregistered%40testuser.com valid": { type: "secondary", state: "unknown" }, | |
- "get /wsapi/address_info?email=unregistered%40testuser.com unknown_secondary": { type: "secondary", state: "unknown" }, | |
- "get /wsapi/address_info?email=unregistered%40testuser.com primary": { type: "primary", state: "unknown", auth: "https://auth_url", prov: "https://prov_url" }, | |
- "get /wsapi/address_info?email=unregistered%40testuser.com primaryUnknown": { type: "primary", state: "unknown", auth: "https://auth_url", prov: "https://prov_url" }, | |
- | |
- "get /wsapi/address_info?email=registered%40testuser.com valid": { type: "secondary", state: "known" }, | |
- "get /wsapi/address_info?email=registered%40testuser.com known_secondary": { type: "secondary", state: "known" }, | |
- "get /wsapi/address_info?email=registered%40testuser.com throttle": { type: "secondary", state: "known" }, | |
- "get /wsapi/address_info?email=registered%40testuser.com primary": { type: "primary", state: "known", auth: "https://auth_url", prov: "https://prov_url" }, | |
- "get /wsapi/address_info?email=registered%40testuser.com mustAuth": { type: "secondary", state: "known" }, | |
- "get /wsapi/address_info?email=registered%40testuser.com secondaryTransition": { type: "secondary", state: "transition_to_secondary" }, | |
- "get /wsapi/address_info?email=registered%40testuser.com secondaryTransitionPassword": { type: "secondary", state: "transition_no_password" }, | |
- "get /wsapi/address_info?email=registered%40testuser.com primaryTransition": { type: "primary", state: "transition_to_primary", auth: "https://auth_url", prov: "https://prov_url" }, | |
- "get /wsapi/address_info?email=registered%40testuser.com primaryOffline": { type: "primary", state: "offline", auth: "https://auth_url", prov: "https://prov_url" }, | |
- | |
+ "get /wsapi/address_info?email=unregistered%40testuser.com&issuer=default invalid": undefined, | |
+ "get /wsapi/address_info?email=unregistered%40testuser.com&issuer=default throttle": { type: "secondary", state: "unknown" }, | |
+ "get /wsapi/address_info?email=unregistered%40testuser.com&issuer=default valid": { type: "secondary", state: "unknown" }, | |
+ "get /wsapi/address_info?email=unregistered%40testuser.com&issuer=default unknown_secondary": { type: "secondary", state: "unknown" }, | |
+ "get /wsapi/address_info?email=unregistered%40testuser.com&issuer=default primary": { type: "primary", state: "unknown", auth: "https://auth_url", prov: "https://prov_url" }, | |
+ "get /wsapi/address_info?email=unregistered%40testuser.com&issuer=default primaryUnknown": { type: "primary", state: "unknown", auth: "https://auth_url", prov: "https://prov_url" }, | |
+ | |
+ "get /wsapi/address_info?email=registered%40testuser.com&issuer=default valid": { type: "secondary", state: "known" }, | |
+ "get /wsapi/address_info?email=registered%40testuser.com&issuer=default known_secondary": { type: "secondary", state: "known" }, | |
+ "get /wsapi/address_info?email=registered%40testuser.com&issuer=default throttle": { type: "secondary", state: "known" }, | |
+ "get /wsapi/address_info?email=registered%40testuser.com&issuer=default primary": { type: "primary", state: "known", auth: "https://auth_url", prov: "https://prov_url" }, | |
+ "get /wsapi/address_info?email=registered%40testuser.com&issuer=default mustAuth": { type: "secondary", state: "known" }, | |
+ "get /wsapi/address_info?email=registered%40testuser.com&issuer=default secondaryTransition": { type: "secondary", state: "transition_to_secondary" }, | |
+ "get /wsapi/address_info?email=registered%40testuser.com&issuer=default secondaryTransitionPassword": { type: "secondary", state: "transition_no_password" }, | |
+ "get /wsapi/address_info?email=registered%40testuser.com&issuer=default primaryTransition": { type: "primary", state: "transition_to_primary", auth: "https://auth_url", prov: "https://prov_url" }, | |
+ "get /wsapi/address_info?email=registered%40testuser.com&issuer=default primaryOffline": { type: "primary", state: "offline", auth: "https://auth_url", prov: "https://prov_url" }, | |
+ | |
++<<<<<<< HEAD | |
+ "get /wsapi/address_info?email=testuser%40testuser.com valid": { type: "secondary", state: "known" }, | |
+ "get /wsapi/address_info?email=testuser2%40testuser.com valid": { type: "secondary", state: "known" }, | |
+ "get /wsapi/address_info?email=testuser%40testuser.com known_secondary": { type: "secondary", state: "known" }, | |
+ "get /wsapi/address_info?email=testuser%40testuser.com unknown_secondary": { type: "secondary", state: "unknown" }, | |
+ "get /wsapi/address_info?email=testuser%40testuser.com secondaryTransitionPassword": { type: "secondary", state: "transition_no_password" }, | |
+ "get /wsapi/address_info?email=testuser%40testuser.com primary": { type: "primary", state: "known", auth: "https://auth_url", prov: "https://prov_url" }, | |
+ "get /wsapi/address_info?email=testuser%40testuser.com primaryOffline": { type: "primary", state: "offline", auth: "https://auth_url", prov: "https://prov_url" }, | |
+ "get /wsapi/address_info?email=testuser%40testuser.com ajaxError": undefined, | |
+ "post /wsapi/used_address_as_primary valid": { success: true }, | |
++======= | |
+ "get /wsapi/address_info?email=testuser%40testuser.com&issuer=default valid": { type: "secondary", state: "known" }, | |
+ "get /wsapi/address_info?email=testuser2%40testuser.com&issuer=default valid": { type: "secondary", state: "known" }, | |
+ "get /wsapi/address_info?email=testuser%40testuser.com&issuer=default known_secondary": { type: "secondary", state: "known" }, | |
+ "get /wsapi/address_info?email=testuser%40testuser.com&issuer=default unknown_secondary": { type: "secondary", state: "unknown" }, | |
+ "get /wsapi/address_info?email=testuser%40testuser.com&issuer=default primary": { type: "primary", state: "known", auth: "https://auth_url", prov: "https://prov_url" }, | |
+ "get /wsapi/address_info?email=testuser%40testuser.com&issuer=default ajaxError": undefined, | |
++>>>>>>> b2g | |
"post /wsapi/used_address_as_primary primaryTransition": { success: true }, | |
"post /wsapi/used_address_as_primary primaryUnknown": { success: true }, | |
"post /wsapi/used_address_as_primary primary": { success: false }, |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment