Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

integrating train 2011.10.27

  • Loading branch information...
commit 44ff6a982c4a2f5525bf3c07c766f04feded19e6 2 parents ba3c534 + d02dca4
@lloyd lloyd authored
Showing with 2,038 additions and 648 deletions.
  1. +2 −0  .gitignore
  2. +13 −0 ChangeLog
  3. +7 −7 DEPLOYMENT.md
  4. +6 −4 README.md
  5. +45 −0 browserid.spec
  6. +11 −5 browserid/app.js
  7. +47 −26 browserid/lib/db_mysql.js
  8. +2 −2 browserid/lib/wsapi.js
  9. +73 −222 browserid/static/css/style.css
  10. +5 −9 browserid/static/dialog/controllers/authenticate_controller.js
  11. +41 −16 browserid/static/dialog/controllers/dialog_controller.js
  12. +11 −5 browserid/static/dialog/controllers/page_controller.js
  13. +8 −6 browserid/static/dialog/controllers/pickemail_controller.js
  14. +14 −1 browserid/static/dialog/css/m.css
  15. +35 −3 browserid/static/dialog/css/popup.css
  16. +20 −12 browserid/static/dialog/qunit.html
  17. +118 −0 browserid/static/dialog/resources/browser-support.js
  18. +32 −2 browserid/static/dialog/resources/error-messages.js
  19. +113 −101 browserid/static/dialog/resources/network.js
  20. +78 −14 browserid/static/dialog/resources/user.js
  21. +28 −6 browserid/static/dialog/test/qunit/controllers/page_controller_unit_test.js
  22. +52 −0 browserid/static/dialog/test/qunit/include_unit_test.js
  23. +112 −0 browserid/static/dialog/test/qunit/pages/add_email_address_test.js
  24. +8 −1 browserid/static/dialog/test/qunit/qunit.js
  25. +102 −0 browserid/static/dialog/test/qunit/resources/browser-support_unit_test.js
  26. +251 −8 browserid/static/dialog/test/qunit/resources/network_unit_test.js
  27. +363 −27 browserid/static/dialog/test/qunit/resources/user_unit_test.js
  28. BIN  browserid/static/i/firefox_logo.png
  29. +85 −46 browserid/static/include.js
  30. +20 −13 browserid/static/js/pages/add_email_address.js
  31. 0  browserid/static/ping.txt
  32. +1 −1  browserid/tests/lib/start-stop.js
  33. +116 −0 browserid/tests/page-requests-test.js
  34. +29 −84 browserid/views/dialog.ejs
  35. +58 −0 browserid/views/dialog_layout.ejs
  36. +28 −0 browserid/views/unsupported_dialog.ejs
  37. +14 −4 browserid/views/verifyemail.ejs
  38. +13 −6 libs/configuration.js
  39. +7 −0 libs/heartbeat.js
  40. +2 −0  libs/secrets.js
  41. +31 −0 scripts/rpmbuild.sh
  42. +18 −0 scripts/update_database.sql
  43. +0 −3  test.sh
  44. +9 −12 verifier/app.js
  45. +10 −2 verifier/lib/certassertion.js
View
2  .gitignore
@@ -3,3 +3,5 @@
.\#*
/node_modules
/var
+/rpmbuild
+
View
13 ChangeLog
@@ -1,3 +1,16 @@
+train-2011.10.27:
+ * link fixing ('need help?' to point to SUMO): #378
+ * unit tests repaired: #469 (broken in fix to #82)
+ * improve handling of network errors: #448
+ * improve styling and language of email confirmation page: #349
+ * logging improvements: #455
+ * RPM generation script created (for installation of browserid on redhat [moz prod] boxes): #478
+ * SCHEMA CHANGES to improve database performance and scalability: #480
+ * change the health check call from '/ping.txt' to '/__heartbeat__': #481
+ * remove application level network timeouts (let the network stack do its job, the user can cancel if they get sick of it): #485
+ * improve messaging for unsupported browsers: #273, #484
+ * developer documentation improvements: #496
+
train-2011.10.20:
* android < 3.0 now supported: #461
* properly set assertion expiration time to when they expire, not when they're issued: #433, #457, #458
View
14 DEPLOYMENT.md
@@ -156,16 +156,16 @@ post update hook, annotated to help you follow along:
### 5. get node servers running
At this point, pushing code to gitolite will cause /home/browserid/code to be updated. Now
-we need to get the servers running! Manually we can verify that the servers will run.
+we need to get the servers running! Manually we can verify that the servers will run.
For the browser id server:
- cd /home/browserid/code/browserid && sudo -u www-data ./run.js
+ cd /home/browserid/code/browserid && sudo -u www-data ./run.js
And for the verifier:
- cd /home/browserid/code/verifier && sudo -u www-data ./run.js
+ cd /home/browserid/code/verifier && sudo -u www-data ./run.js
-Now let's set up [monit] to restart the node.js servers:
+Now let's set up [monit] to restart the node.js servers:
1. install monit: `sudo apt-get install monit`
2. enable monit by editing `/etc/default/monit`
@@ -181,7 +181,7 @@ include /etc/monit.d/*
<pre>
#!/bin/bash
-/usr/local/bin/node $1 > $(dirname $1)/error.log 2>&1 &
+/usr/local/bin/node $1 > $(dirname $1)/error.log 2>&1 &
</pre>
5. create a file to run the verifier at `/etc/monit.d/verifier`:
@@ -192,7 +192,7 @@ check host verifier with address 127.0.0.1
as uid "www-data" and gid "www-data"
stop program = "/usr/bin/pkill -f '/usr/local/bin/node /home/browserid/code/verifier/run.js'"
if failed port 62800 protocol HTTP
- request /ping.txt
+ request /__heartbeat__
with timeout 10 seconds
then restart
</pre>
@@ -205,7 +205,7 @@ check host browserid.org with address 127.0.0.1
as uid "www-data" and gid "www-data"
stop program = "/usr/bin/pkill -f '/usr/local/bin/node /home/browserid/code/browserid/run.js'"
if failed port 62700 protocol HTTP
- request /ping.txt
+ request /__heartbeat__
with timeout 10 seconds
then restart
</pre>
View
10 README.md
@@ -23,10 +23,12 @@ or changes will be made.
2. Boot up the VM:
- $ cd browserid
- $ vagrant up
- $ vagrant ssh
- vagrant@lucid32:browserid$ node ./run.js
+```
+cd browserid
+vagrant up
+vagrant ssh vagrant@lucid32:browserid
+node ./run.js
+```
`vagrant up` will take a while. Go get a cup of coffee. This is because it downloads the 500MB VM.
View
45 browserid.spec
@@ -0,0 +1,45 @@
+%define _rootdir /opt/browserid
+
+Name: browserid-server
+Version: 0.2011.10.13
+Release: 1%{?dist}
+Summary: BrowserID server
+Packager: Pete Fritchman <petef@mozilla.com>
+Group: Development/Libraries
+License: MPL 1.1+/GPL 2.0+/LGPL 2.1+
+URL: https://github.com/mozilla/browserid
+Source0: %{name}.tar.gz
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
+AutoReqProv: no
+Requires: openssl nodejs
+BuildRequires: gcc-c++ git jre make npm openssl-devel
+
+%description
+browserid server & web home for browserid.org
+
+%prep
+%setup -q -n browserid
+
+%build
+npm install
+export PATH=$PWD/node_modules/.bin:$PATH
+(cd browserid && ./compress.sh)
+git log -1 --oneline > browserid/static/ver.txt
+
+%install
+rm -rf %{buildroot}
+mkdir -p %{buildroot}%{_rootdir}
+for f in browserid libs node_modules verifier *.json *.js; do
+ cp -rp $f %{buildroot}%{_rootdir}/$dir
+done
+
+%clean
+rm -rf %{buildroot}
+
+%files
+%defattr(-,root,root,-)
+%{_rootdir}
+
+%changelog
+* Tue Oct 18 2011 Pete Fritchman <petef@mozilla.com>
+- Initial version
View
16 browserid/app.js
@@ -45,6 +45,7 @@ express = require('express'),
secrets = require('../libs/secrets.js'),
db = require('./lib/db.js'),
configuration = require('../libs/configuration.js'),
+heartbeat = require('../libs/heartbeat.js'),
substitution = require('../libs/substitute.js');
metrics = require("../libs/metrics.js"),
logger = require("../libs/logging.js").logger;
@@ -79,11 +80,16 @@ function router(app) {
metrics.userEntry(req);
res.render('dialog.ejs', {
title: 'A Better Way to Sign In',
- layout: false,
+ layout: 'dialog_layout.ejs',
+ useJavascript: true,
production: configuration.get('use_minified_resources')
});
});
+ app.get("/unsupported_dialog", function(req,res) {
+ res.render('unsupported_dialog.ejs', {layout: 'dialog_layout.ejs', useJavascript: false});
+ });
+
// simple redirects (internal for now)
app.get('/register_iframe', internal_redirector('/dialog/register_iframe.html',true));
@@ -102,9 +108,6 @@ function router(app) {
res.render('index.ejs', {title: 'A Better Way to Sign In', fullpage: true});
});
- // BA removed .html URLs. If we have 404s,
- // we should set up some redirects
-
app.get("/signup", function(req, res) {
res.render('signup.ejs', {title: 'Sign Up', fullpage: false});
});
@@ -141,7 +144,7 @@ function router(app) {
REDIRECTS = {
"/manage": "/",
"/users": "/",
- "/users/": "/",
+ "/users/": "/",
"/primaries" : "/developers",
"/primaries/" : "/developers",
"/developers" : "https://github.com/mozilla/browserid/wiki/How-to-Use-BrowserID-on-Your-Site"
@@ -160,6 +163,9 @@ function router(app) {
// register all the WSAPI handlers
wsapi.setup(app);
+ // setup health check / heartbeat
+ heartbeat.setup(app);
+
// the public key
app.get("/pk", function(req, res) {
res.json(ca.PUBLIC_KEY.toSimpleObject());
View
73 browserid/lib/db_mysql.js
@@ -49,6 +49,7 @@
*
*
* +------ staged ----------+
+ * |*int id |
* |*string secret |
* | bool new_acct |
* | string existing |
@@ -67,10 +68,28 @@ var client = undefined;
// may get defined at open() time causing a database to be dropped upon connection closing.
var drop_on_close = undefined;
+// If you change these schemas, please notify <services-ops@mozilla.com>
const schemas = [
- "CREATE TABLE IF NOT EXISTS user ( id INTEGER AUTO_INCREMENT PRIMARY KEY, passwd VARCHAR(64) );",
- "CREATE TABLE IF NOT EXISTS email ( id INTEGER AUTO_INCREMENT PRIMARY KEY, user INTEGER, INDEX(user), address VARCHAR(255) UNIQUE, INDEX(address) );",
- "CREATE TABLE IF NOT EXISTS staged ( secret VARCHAR(48) PRIMARY KEY, new_acct BOOL, existing VARCHAR(255), email VARCHAR(255) UNIQUE, INDEX(email), ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP);"
+ "CREATE TABLE IF NOT EXISTS user (" +
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY," +
+ "passwd CHAR(64) NOT NULL" +
+ ") ENGINE=InnoDB;",
+
+ "CREATE TABLE IF NOT EXISTS email (" +
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY," +
+ "user BIGINT NOT NULL," +
+ "address VARCHAR(255) UNIQUE NOT NULL," +
+ "FOREIGN KEY user_fkey (user) REFERENCES user(id)" +
+ ") ENGINE=InnoDB;",
+
+ "CREATE TABLE IF NOT EXISTS staged (" +
+ "id BIGINT AUTO_INCREMENT PRIMARY KEY," +
+ "secret CHAR(48) UNIQUE NOT NULL," +
+ "new_acct BOOL NOT NULL," +
+ "existing VARCHAR(255)," +
+ "email VARCHAR(255) UNIQUE NOT NULL," +
+ "ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL" +
+ ") ENGINE=InnoDB;",
];
// log an unexpected database error
@@ -111,37 +130,39 @@ exports.open = function(cfg, cb) {
}
// now create the databse
- client.query("CREATE DATABASE IF NOT EXISTS " + database, function(err) {
- if (err) {
- logUnexpectedError(err);
- cb(err);
- return;
- }
- client.useDatabase(database, function(err) {
+ if (cfg.create_schema || cfg.unit_test) {
+ client.query("CREATE DATABASE IF NOT EXISTS " + database, function(err) {
if (err) {
logUnexpectedError(err);
cb(err);
return;
}
+ client.useDatabase(database, function(err) {
+ if (err) {
+ logUnexpectedError(err);
+ cb(err);
+ return;
+ }
- // now create tables
- function createNextTable(i) {
- if (i < schemas.length) {
- client.query(schemas[i], function(err) {
- if (err) {
- logUnexpectedError(err);
- cb(err);
- } else {
- createNextTable(i+1);
- }
- });
- } else {
- cb();
+ // now create tables
+ function createNextTable(i) {
+ if (i < schemas.length) {
+ client.query(schemas[i], function(err) {
+ if (err) {
+ logUnexpectedError(err);
+ cb(err);
+ } else {
+ createNextTable(i+1);
+ }
+ });
+ } else {
+ cb();
+ }
}
- }
- createNextTable(0);
+ createNextTable(0);
+ });
});
- });
+ };
};
exports.close = function(cb) {
View
4 browserid/lib/wsapi.js
@@ -278,7 +278,7 @@ function setup(app) {
db.gotVerificationSecret(req.body.token, hash, function(err, email) {
if (err) {
- logger.error("error completing the verification: " + err);
+ logger.warn("couldn't complete email verification: " + err);
resp.json({ success: false });
} else {
// FIXME: not sure if we want to do this (ba)
@@ -360,7 +360,7 @@ function setup(app) {
app.post('/wsapi/complete_email_addition', checkParams(["token"]), function(req, resp) {
db.gotVerificationSecret(req.body.token, undefined, function(e) {
if (e) {
- logger.error("error completing the verification: " + e);
+ logger.warn("couldn't complete email verification: " + e);
resp.json({ success: false });
} else {
resp.json({ success: true });
View
295 browserid/static/css/style.css
@@ -21,30 +21,24 @@ body {
overflow-y: scroll;
}
-/*header {
- border-top: 4px solid #333;
- background-color: #333;
- padding: 0 20% 0 20%;
- margin: 0;
- background: #008;
- height: 230px;
- color #fff;
- min-width: 800px;
- display: block;
+/* for floats */
+.cf:after {
+ content: ".";
+ display: block;
+ clear: both;
+ visibility: hidden;
+ line-height: 0;
+ height: 0;
}
-footer {
- background-color: #F1F1F1;
- border-top: 2px solid #ddd;
- margin: 0;
- margin-top: 100px;
- padding: 0;
- height: 200px;
- font-size: 1.1em;
- display: block;
+html[xmlns] .cf {
+ display: block;
+}
+
+* html .cf {
+ height: 1%;
}
-*/
.sans {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
@@ -115,11 +109,6 @@ hr {
display: none;
}
-/*
-.authenticated #content {
- display: block;
-}
-*/
#about {
font-family: 'Droid Serif', Georgia, serif;
font-size: 14px;
@@ -623,7 +612,7 @@ h1 {
display: inline-block;
}
-#signUpForm {
+#signUpForm, #congrats {
padding: 20px;
background-color: rgba(0,0,0,0.035);
@@ -656,33 +645,6 @@ h1 {
float: left;
}
-a.forgot {
- color: #888784;
- text-shadow: 1px 1px 0 rgba(255, 255, 255, 0.5);
- font-size: 11px;
-}
-
-.notifications {
- list-style-type: none;
-}
-
-.notifications > .notification {
- margin-top: 20px;
- padding: 5px;
- line-height: 16px;
- -webkit-border-radius: 3px;
- -moz-border-radius: 3px;
- -o-border-radius: 3px;
- border-radius: 3px;
- display: none;
- text-align: center;
-}
-
-.notifications .notification.error {
- color: red;
- background-color: rgba(255,0,0,0.25);
-}
-
#signUpForm .red {
color: red;
}
@@ -748,6 +710,64 @@ a.forgot {
float: right;
}
+#signUpForm .error {
+ margin-top: 20px;
+ color: red;
+ background-color: rgba(255,0,0,0.25);
+ padding: 5px;
+ line-height: 16px;
+
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ -o-border-radius: 3px;
+ border-radius: 3px;
+}
+
+
+#congrats #siteinfo, #congrats {
+ display: none;
+}
+
+#congrats p {
+ color: #62615F;
+ display: block;
+ text-shadow: 1px 1px 0 rgba(255, 255, 255, 0.5);
+}
+
+#congrats .website {
+ display: block;
+ text-align: center;
+}
+
+
+a.forgot {
+ color: #888784;
+ text-shadow: 1px 1px 0 rgba(255, 255, 255, 0.5);
+ font-size: 11px;
+}
+
+.notifications {
+ list-style-type: none;
+}
+
+.notifications > .notification {
+ margin-top: 20px;
+ padding: 5px;
+ line-height: 16px;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ -o-border-radius: 3px;
+ border-radius: 3px;
+ display: none;
+ text-align: center;
+}
+
+.notifications .notification.error {
+ color: red;
+ background-color: rgba(255,0,0,0.25);
+}
+
+
#header {
position: absolute;
top: 0;
@@ -830,172 +850,3 @@ a.forgot {
color: #aaa;
}
-
-/* for floats */
-.cf:after {
- content: ".";
- display: block;
- clear: both;
- visibility: hidden;
- line-height: 0;
- height: 0;
-}
-
-html[xmlns] .cf {
- display: block;
-}
-
-* html .cf {
- height: 1%;
-}
-
-#signUpFormWrap {
- margin: 122px 160px;
-}
-
-#signUpFormWrap a.signUpIn {
- color: #549FDC;
- text-shadow: 1px 1px 0 rgba(255, 255, 255, 0.5);
-}
-
-#signUpFormWrap a.space {
- margin: 10px 20px 0;
- display: inline-block;
-}
-
-#signUpForm,
-#congrats {
- padding: 20px;
- background-color: rgba(0,0,0,0.035);
-
- -webkit-border-radius: 5px;
- -moz-border-radius: 5px;
- -o-border-radius: 5px;
- border-radius: 5px;
-}
-
-#congrats {
- display: none;
-}
-
-#congrats p {
- color: #62615F;
- display: block;
- text-shadow: 1px 1px 0 rgba(255, 255, 255, 0.5);
-}
-
-#signUpForm label/*,
-#signUpForm p.hint*/ {
- display: block;
- color: #62615F;
- text-shadow: 1px 1px 0 rgba(255,255,255,0.5);
-}
-
-/*
-
-#signUpForm p.hint {
- margin: -20px -20px 20px;
- padding: 10px 20px 10px 45px;
- background-color: rgba(0,0,0,0.05);
- background-repeat: no-repeat;
- background-position: 20px center;
- color: #222;
-
- -webkit-border-radius: 5px 5px 0 0;
- -moz-border-radius: 5px 5px 0 0;
- -o-border-radius: 5px 5px 0 0;
- border-radius: 5px 5px 0 0;
-}
-*/
-#signUpForm label.half,
-.half {
- width: 50%;
- display: inline-block;
- float: left;
-}
-
-a.forgot {
- color: #888784;
- text-shadow: 1px 1px 0 rgba(255, 255, 255, 0.5);
- font-size: 11px;
-}
-
-#signUpForm .error {
- margin-top: 20px;
- color: red;
- background-color: rgba(255,0,0,0.25);
- padding: 5px;
- line-height: 16px;
-
- -webkit-border-radius: 3px;
- -moz-border-radius: 3px;
- -o-border-radius: 3px;
- border-radius: 3px;
-}
-
-#signUpForm .red {
- color: red;
-}
-
-#signUpForm input[type=email],
-#signUpForm input[type=password] {
- width: 100%;
- font-size: 14px;
- padding: 5px;
- border-width: 1px;
- border-style: solid;
- border-color: #A3A29D #C6C3B4 #C6C3B4 #A3A29D;
- outline: none;
-
- -webkit-border-radius: 3px;
- -moz-border-radius: 3px;
- -o-border-radius: 3px;
- border-radius: 3px;
-
- -webkit-box-shadow: 1px 1px 0 rgba(255,255,255,0.5);
- -moz-box-shadow: 1px 1px 0 rgba(255,255,255,0.5);
- -o-box-shadow: 1px 1px 0 rgba(255,255,255,0.5);
- box-shadow: 1px 1px 0 rgba(255,255,255,0.5);
-}
-
-#signUpForm input[type=email]:focus,
-#signUpForm input[type=password]:focus {
- border: 1px solid #549FDC;
-
- -webkit-border-radius: 0;
- -moz-border-radius: 0;
- -o-border-radius: 0;
- border-radius: 0;
-
- -webkit-box-shadow: 0 0 0 1px #549FDC inset;
- -moz-box-shadow: 0 0 0 1px #549FDC inset;
- -o-box-shadow: 0 0 0 1px #549FDC inset;
- box-shadow: 0 0 0 1px #549FDC inset;
-}
-
-#signUpForm .submit {
- height: 28px;
-}
-
-#signUpForm .remember {
- display: inline-block;
- line-height: 28px;
-}
-
-#signUpForm .remember .checkAlign {
- float: left;
-}
-
-#signUpForm .remember label {
- margin-left: 5px;
- float: left;
-}
-
-#signUpForm .create {
- font-size: 14px;
- height: 28px;
- padding: 0 10px;
- float: right;
-}
-
-
View
14 browserid/static/dialog/controllers/authenticate_controller.js
@@ -40,6 +40,7 @@
var ANIMATION_TIME = 250,
bid = BrowserID,
user = bid.User,
+ errors = bid.Errors,
validation = bid.Validation,
lastEmail = "";
@@ -60,7 +61,7 @@
else {
createUserState.call(self);
}
- });
+ }, self.getErrorDialog(errors.isEmailRegistered));
}
function createUser(el, event) {
@@ -83,7 +84,7 @@
else {
// XXX can't register this email address.
}
- }, self.getErrorDialog(bid.Errors.createAccount));
+ }, self.getErrorDialog(errors.createUser));
}
function authenticate(el, event) {
@@ -106,10 +107,7 @@
} else {
bid.Tooltip.showTooltip("#cannot_authenticate");
}
- },
- self.getErrorDialog(bid.Errors.authentication)
- );
-
+ }, self.getErrorDialog(errors.authenticate));
}
function resetPassword(el, event) {
@@ -122,9 +120,7 @@
self.close("reset_password", {
email: email
});
- }, function() {
- // XXX TODO error screen!
- });
+ }, self.getErrorDialog(errors.requestPasswordReset));
}
function animateSwap(fadeOutSelector, fadeInSelector, callback) {
View
57 browserid/static/dialog/controllers/dialog_controller.js
@@ -42,33 +42,40 @@
(function() {
"use strict";
- var user = BrowserID.User;
+ var bid = BrowserID,
+ user = bid.User,
+ errors = bid.Errors,
+ offline = false;
PageController.extend("Dialog", {}, {
init: function(el) {
var self=this;
- //this.element.show();
// keep track of where we are and what we do on success and error
self.onsuccess = null;
self.onerror = null;
setupChannel(self);
self.stateMachine();
+
},
getVerifiedEmail: function(origin_url, onsuccess, onerror) {
- this.onsuccess = onsuccess;
- this.onerror = onerror;
+ var self=this;
+ self.onsuccess = onsuccess;
+ self.onerror = onerror;
+
+ if('onLine' in navigator && !navigator.onLine) {
+ self.doOffline();
+ return;
+ }
user.setOrigin(origin_url);
-
- // get the cleaned origin.
$("#sitename").text(user.getHostname());
- this.doCheckAuth();
+ self.doCheckAuth();
- var self=this;
$(window).bind("unload", function() {
+ bid.Storage.setStagedOnBehalfOf("");
self.doCancel();
});
},
@@ -78,7 +85,15 @@
var self=this,
hub = OpenAjax.hub,
el = this.element;
-
+
+ hub.subscribe("offline", function(msg, info) {
+ self.doOffline();
+ });
+
+ hub.subscribe("xhrError", function(msg, info) {
+ //self.doXHRError(info);
+ // XXX how are we going to handle this?
+ });
hub.subscribe("user_staged", function(msg, info) {
self.doConfirmUser(info.email);
@@ -100,7 +115,7 @@
});
hub.subscribe("assertion_generated", function(msg, info) {
- if(info.assertion !== null) {
+ if (info.assertion !== null) {
self.doAssertionGenerated(info.assertion);
}
else {
@@ -138,6 +153,15 @@
},
+ doOffline: function() {
+ this.renderError("wait.ejs", errors.offline);
+ offline = true;
+ },
+
+ doXHRError: function(info) {
+ if (!offline) this.renderError("wait.ejs", errors.offline);
+ },
+
doConfirmUser: function(email) {
this.confirmEmail = email;
@@ -150,7 +174,7 @@
doCancel: function() {
var self=this;
- if(self.onsuccess) {
+ if (self.onsuccess) {
self.onsuccess(null);
}
},
@@ -182,7 +206,8 @@
doEmailConfirmed: function() {
var self=this;
// yay! now we need to produce an assertion.
- user.getAssertion(this.confirmEmail, self.doAssertionGenerated.bind(self));
+ user.getAssertion(this.confirmEmail, self.doAssertionGenerated.bind(self),
+ self.getErrorDialog(errors.getAssertion));
},
doAssertionGenerated: function(assertion) {
@@ -195,16 +220,16 @@
},
doNotMe: function() {
- user.logoutUser(this.doAuthenticate.bind(this));
+ var self=this;
+ user.logoutUser(self.doAuthenticate.bind(self), self.getErrorDialog(errors.logoutUser));
},
syncEmails: function() {
var self = this;
user.syncEmails(self.doPickEmail.bind(self),
- self.getErrorDialog(BrowserID.Errors.signIn));
+ self.getErrorDialog(errors.signIn));
},
-
doCheckAuth: function() {
var self=this;
user.checkAuthenticationAndSync(function onSuccess() {},
@@ -215,7 +240,7 @@
self.doAuthenticate();
}
},
- self.getErrorDialog(BrowserID.Errors.checkAuthentication));
+ self.getErrorDialog(errors.checkAuthentication));
}
});
View
16 browserid/static/dialog/controllers/page_controller.js
@@ -48,6 +48,8 @@
var me=this,
bodyTemplate = options.bodyTemplate,
bodyVars = options.bodyVars,
+ errorTemplate = options.errorTemplate,
+ errorVars = options.errorVars,
waitTemplate = options.waitTemplate,
waitVars = options.waitVars;
@@ -60,6 +62,10 @@
me.renderWait(waitTemplate, waitVars);
}
+ if(errorTemplate) {
+ me.renderError(errorTemplate, errorVars);
+ }
+
// XXX move all of these, bleck.
$("form").bind("submit", me.onSubmit.bind(me));
$("#cancel").click(me.onCancel.bind(me));
@@ -95,12 +101,12 @@
renderWait: function(body, body_vars) {
this.renderTemplates("#wait", body, body_vars);
- $("body").removeClass("error").removeClass("form").addClass("waiting");
- $("#wait").stop().css('opacity', 1).hide().fadeIn(ANIMATION_TIME);
+ $("body").removeClass("error").removeClass("form").addClass("waiting").css('opacity', 1);
+ $("#wait").stop().hide().fadeIn(ANIMATION_TIME);
},
- renderError: function(error_vars) {
- this.renderTemplates("#error", "wait.ejs", error_vars);
+ renderError: function(body, body_vars) {
+ this.renderTemplates("#error", body, body_vars);
$("body").removeClass("waiting").removeClass("form").addClass("error");
$("#error").stop().css('opacity', 1).hide().fadeIn(ANIMATION_TIME);
},
@@ -144,7 +150,7 @@
*/
getErrorDialog: function(info) {
var self=this;
- return self.renderError.bind(self, info);
+ return self.renderError.bind(self, "wait.ejs", info);
},
onCancel: function(event) {
View
14 browserid/static/dialog/controllers/pickemail_controller.js
@@ -40,6 +40,7 @@
var ANIMATION_TIME = 250,
bid = BrowserID,
user = bid.User,
+ errors = bid.Errors,
body = $("body"),
animationComplete = body.innerWidth() < 640,
assertion;
@@ -109,13 +110,13 @@
var self=this;
user.getAssertion(email, function(assert) {
assertion = assert || null;
- tryClose.call(self);
- });
+ startAnimation.call(self);
+ }, self.getErrorDialog(errors.getAssertion));
}
function startAnimation() {
+ var self=this;
if(!animationComplete) {
- var self=this;
$("#signIn").animate({"width" : "685px"}, "slow", function () {
// post animation
body.delay(500).animate({ "opacity" : "0.5"}, "fast", function () {
@@ -124,6 +125,9 @@
});
});
}
+ else {
+ tryClose.call(self);
+ }
}
@@ -134,9 +138,7 @@
var valid = checkEmail.call(self, email);
if (valid) {
-// self.doWait(bid.Wait.generateKey);
getAssertion.call(self, email);
- startAnimation.call(self);
}
}
@@ -168,7 +170,7 @@
bid.Tooltip.showTooltip("#could_not_add");
});
}
- });
+ }, self.getErrorDialog(errors.isEmailRegistered));
}
View
15 browserid/static/dialog/css/m.css
@@ -111,6 +111,19 @@
height: 250px;
}
-}
+ #error .vertical {
+ width: auto;
+ }
+ #error .vertical > div {
+ display: block;
+ height: auto;
+ padding: 10px;
+ }
+
+ #error #borderbox {
+ border-left: none;
+ padding: 0;
+ }
+
View
38 browserid/static/dialog/css/popup.css
@@ -18,6 +18,7 @@ body {
font-size: 13px;
line-height: 21px;
background-image: url('/i/bg.png');
+ color: #000;
}
@@ -118,16 +119,17 @@ section > .contents {
#wait, #error {
text-align: center;
- background-image: url("/i/bg.png");
}
#wait {
z-index: 1;
+ background-image: url("/i/bg.png");
}
#error {
display: none;
z-index: 2;
+ background-color: #fff;
}
#wait strong, #error strong {
@@ -135,10 +137,40 @@ section > .contents {
font-weight: bold;
}
-#error {
- z-index: 2;
+
+#error .vertical {
+ width: 630px;
+ margin: 0 auto;
+ display: block;
+}
+
+
+#error .vertical > div {
+ display: table-cell;
+ vertical-align: middle;
+ padding: 0 10px;
+ height: 250px;
+}
+
+#error #alternative a {
+ color: #549FDC;
+ text-decoration: underline;
+}
+
+#error #borderbox {
+ border-left: 1px solid #777;
+ padding: 20px 0;
}
+#error #borderbox img {
+ border: none;
+}
+
+#error #alternative .lighter {
+ color: #777;
+}
+
+
#formWrap {
background-color: #fff;
background-image: none;
View
32 browserid/static/dialog/qunit.html
@@ -12,23 +12,31 @@ <h2 id="qunit-banner"></h2>
<div id="qunit-testrunner-toolbar"></div>
<h2 id="qunit-userAgent"></h2>
<div id="test-content">
- <div id="page_controller">
+ </div>
+ <ol id="qunit-tests"></ol>
+ <div id="qunit-test-area"></div>
- <div id="formWrap">
- <div class="contents"></div>
- </div>
+ <h3>Content below here is test content that can be ignored</h3>
- <div id="wait">
- <div class="contents"></div>
- </div>
+ <div id="controller_head">
- <div id="error">
- <div class="contents"></div>
- </div>
+ <div id="formWrap">
+ <div class="contents"></div>
+ </div>
+ <div id="wait">
+ <div class="contents"></div>
</div>
+
+ <div id="error">
+ <div class="contents"></div>
+ </div>
+
+ <span id="email"></span>
+ <span id="cannotconfirm" class="error">Cannot confirm</span>
+ <span id="cannotcommunicate" class="error">Cannot communicate</span>
+ <span id="siteinfo" class="error"><span class="website"></span></span>
+ <span class=".hint">Hint</span>
</div>
- <ol id="qunit-tests"></ol>
- <div id="qunit-test-area"></div>
</body>
</html>
View
118 browserid/static/dialog/resources/browser-support.js
@@ -0,0 +1,118 @@
+/*globals BrowserID: true */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla BrowserID.
+ *
+ * The Initial Developer of the Original Code is Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+BrowserID.BrowserSupport = (function() {
+ var bid = BrowserID,
+ 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 < 9;
+
+ if(ieNosupport) {
+ return "IE_VERSION";
+ }
+ }
+
+ function explicitNosupport() {
+ return checkIE();
+ }
+
+ function checkLocalStorage() {
+ var localStorage = 'localStorage' in win && win['localStorage'] !== null;
+ if(!localStorage) {
+ return "LOCALSTORAGE";
+ }
+ }
+
+ function checkPostMessage() {
+ if(!win.postMessage) {
+ return "POSTMESSAGE";
+ }
+ }
+
+ function isSupported() {
+ reason = checkLocalStorage() || checkPostMessage() || explicitNosupport();
+
+ 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
+ };
+
+}());
+
View
34 browserid/static/dialog/resources/error-messages.js
@@ -36,7 +36,7 @@ BrowserID.Errors = (function(){
"use strict";
var Errors = {
- authentication: {
+ authenticate: {
type: "serverError",
title: "Error Authenticating",
message: "There was a technical problem while trying to log you in. Yucky!"
@@ -54,18 +54,48 @@ BrowserID.Errors = (function(){
message: "There was a technical problem while trying to log you in. Yucky!"
},
- createAccount: {
+ createUser: {
type: "serverError",
title: "Error Creating Account",
message: "There was a technical problem while trying to create your account. Yucky!"
},
+ getAssertion: {
+ type: "serverError",
+ title: "Error Getting Assertion",
+ message: "There was a technical problem while trying to authenticate you. Yucky!"
+ },
+
+ isEmailRegistered: {
+ type: "serverError",
+ title: "Error Checking Email Address",
+ message: "There was a technical problem while trying to check that email address. Yucky!"
+ },
+
+ logoutUser: {
+ type: "serverError",
+ title: "Logout Failed",
+ message: "An error was encountered while signing you out. Yucky!"
+ },
+
+ offline: {
+ type: "networkError",
+ title: "You are offline!",
+ message: "Unfortunately, BrowserID cannot communicate while offline!"
+ },
+
registration: {
type: "serverError",
title: "Registration Failed",
message: "An error was encountered and the signup cannot be completed. Yucky!"
},
+ requestPasswordReset: {
+ type: "serverError",
+ title: "Error Resetting Password",
+ message: "There was a technical problem while trying to reset your password."
+ },
+
signIn: {
type: "serverError",
title: "Signin Failed",
View
214 browserid/static/dialog/resources/network.js
@@ -37,37 +37,13 @@
BrowserID.Network = (function() {
"use strict";
- var csrf_token;
- var xhr = $;
- var server_time;
- var auth_status;
+ var csrf_token,
+ xhr = $,
+ server_time,
+ auth_status,
+ hub = window.OpenAjax && OpenAjax.hub;
- function withContext(cb) {
- if (typeof auth_status === 'boolean' && typeof csrf_token !== 'undefined') cb();
- else {
- xhr.ajax({
- url: "/wsapi/session_context",
- type: "GET",
- success: function(result) {
- csrf_token = result.csrf_token;
- server_time = {
- remote: result.server_time,
- local: (new Date()).getTime()
- };
- auth_status = result.authenticated;
- _.defer(cb);
- },
- dataType: "json"
- });
- }
- }
-
- function clearContext() {
- var undef;
- csrf_token = server_time = auth_status = undef;
- }
-
- function createDeferred(cb) {
+ function deferResponse(cb) {
if (cb) {
return function() {
var args = _.toArray(arguments);
@@ -78,6 +54,26 @@ BrowserID.Network = (function() {
}
}
+ function xhrError(cb, errorMessage) {
+ return function() {
+ if (cb) cb();
+ hub && hub.publish("xhrError", errorMessage);
+ };
+ }
+
+ function get(options) {
+ xhr.ajax({
+ type: "GET",
+ url: options.url,
+ // We defer the responses because otherwise jQuery eats any exceptions
+ // that are thrown in the response handlers and it becomes very difficult
+ // to debug.
+ success: deferResponse(options.success),
+ error: deferResponse(xhrError(options.error, options.errorMessage)),
+ dataType: "json"
+ });
+ }
+
function post(options) {
withContext(function() {
var data = options.data || {};
@@ -90,12 +86,44 @@ BrowserID.Network = (function() {
type: "POST",
url: options.url,
data: data,
- success: options.success,
- error: options.error
+ // We defer the responses because otherwise jQuery eats any exceptions
+ // that are thrown in the response handlers and it becomes very difficult
+ // to debug.
+ success: deferResponse(options.success),
+ error: deferResponse(xhrError(options.error, options.errorMessage))
});
- });
+ }, options.error);
}
+ function withContext(cb, onFailure) {
+ if (typeof auth_status === 'boolean' && typeof csrf_token !== 'undefined') cb();
+ else {
+ xhr.ajax({
+ url: "/wsapi/session_context",
+ success: function(result) {
+ csrf_token = result.csrf_token;
+ server_time = {
+ remote: result.server_time,
+ local: (new Date()).getTime()
+ };
+ auth_status = result.authenticated;
+ cb();
+ },
+ error: deferResponse(xhrError(onFailure))
+ });
+ }
+ }
+
+ function clearContext() {
+ var undef;
+ csrf_token = server_time = auth_status = undef;
+ }
+
+ // Not really part of the Network API, but related to networking
+ $(document).bind("offline", function() {
+ hub.publish("offline");
+ });
+
var Network = {
/**
* Set the XHR object and clear all context info. Used for testing.
@@ -133,7 +161,7 @@ BrowserID.Network = (function() {
// session, let's set it to perhaps save a network request
// (to fetch session context).
auth_status = authenticated;
- _.delay(onSuccess, 0, authenticated);
+ if(onSuccess) onSuccess(authenticated);
} catch (e) {
onFailure("unexpected server response: " + e);
}
@@ -154,19 +182,20 @@ BrowserID.Network = (function() {
withContext(function() {
try {
if (typeof auth_status !== 'boolean') throw "can't get authentication status!";
- _.delay(onSuccess, 0, auth_status);
+ if (onSuccess) onSuccess(auth_status);
} catch(e) {
if (onFailure) onFailure(e.toString());
}
- });
+ }, onFailure);
},
/**
* Log the authenticated user out
* @method logout
* @param {function} [onSuccess] - called on completion
+ * @param {function} [onFailure] - Called on XHR failure.
*/
- logout: function(onSuccess) {
+ logout: function(onSuccess, onFailure) {
post({
url: "/wsapi/logout",
success: function() {
@@ -176,8 +205,9 @@ BrowserID.Network = (function() {
// FIXME: we should return a confirmation that the
// user was successfully logged out.
auth_status = false;
- if (onSuccess) _.defer(onSuccess);
- }
+ if (onSuccess) onSuccess();
+ },
+ error: onFailure
});
},
@@ -197,10 +227,7 @@ BrowserID.Network = (function() {
site : origin
},
success: function(status) {
- var staged = status.success;
- // why a delay here? Because of the test harness?
- // shouldn't the delay be in the test harness?
- _.delay(onSuccess, 0, staged);
+ if (onSuccess) onSuccess(status.success);
},
error: onFailure
});
@@ -215,10 +242,10 @@ BrowserID.Network = (function() {
* I think so (BA).
*/
emailForVerificationToken: function(token, onSuccess, onFailure) {
- xhr.ajax({
+ get({
url : "/wsapi/email_for_token?token=" + encodeURIComponent(token),
success: function(data) {
- onSuccess(data.email);
+ if (onSuccess) onSuccess(data.email);
},
error: onFailure
});
@@ -231,12 +258,10 @@ BrowserID.Network = (function() {
* @param {function} [onFailure] - Called on XHR failure.
*/
checkUserRegistration: function(email, onSuccess, onFailure) {
- xhr.ajax({
+ get({
url: "/wsapi/user_creation_status?email=" + encodeURIComponent(email),
success: function(status, textStatus, jqXHR) {
- if (onSuccess) {
- _.delay(onSuccess, 0, status.status);
- }
+ if (onSuccess) onSuccess(status.status);
},
error: onFailure
});
@@ -258,9 +283,28 @@ BrowserID.Network = (function() {
pass: password
},
success: function(status, textStatus, jqXHR) {
- if (onSuccess) {
- _.delay(onSuccess, 0, status.success);
- }
+ if (onSuccess) onSuccess(status.success);
+ },
+ error: onFailure
+ });
+ },
+
+ /**
+ * Call with a token to prove an email address ownership.
+ * @method completeEmailRegistration
+ * @param {string} token - token proving email ownership.
+ * @param {function} [onSuccess] - Callback to call when complete. Called
+ * with one boolean parameter that specifies the validity of the token.
+ * @param {function} [onFailure] - Called on XHR failure.
+ */
+ completeEmailRegistration: function(token, onSuccess, onFailure) {
+ post({
+ url: "/wsapi/complete_email_addition",
+ data: {
+ token: token
+ },
+ success: function(status, textStatus, jqXHR) {
+ if (onSuccess) onSuccess(status.success);
},
error: onFailure
});
@@ -275,7 +319,7 @@ BrowserID.Network = (function() {
*/
requestPasswordReset: function(email, origin, onSuccess, onFailure) {
if (email) {
- this.createUser(email, origin, onSuccess, onFailure);
+ Network.createUser(email, origin, onSuccess, onFailure);
} else {
// TODO: if no email is provided, then what?
throw "no email provided to password reset";
@@ -291,9 +335,7 @@ BrowserID.Network = (function() {
*/
resetPassword: function(password, onSuccess, onFailure) {
// XXX fill this in.
- if (onSuccess) {
- _.defer(onSuccess);
- }
+ if (onSuccess) onSuccess();
},
/**
@@ -308,32 +350,10 @@ BrowserID.Network = (function() {
changePassword: function(oldPassword, newPassword, onSuccess, onFailure) {
// XXX fill this in
if (onSuccess) {
- _.delay(onSuccess, 0, true);
+ onSuccess(true);
}
},
- /**
- * Call with a token to prove an email address ownership.
- * @method completeEmailRegistration
- * @param {string} token - token proving email ownership.
- * @param {function} [onSuccess] - Callback to call when complete. Called
- * with one boolean parameter that specifies the validity of the token.
- * @param {function} [onFailure] - Called on XHR failure.
- */
- completeEmailRegistration: function(token, onSuccess, onFailure) {
- post({
- url: "/wsapi/complete_email_addition",
- data: {
- token: token
- },
- success: function(status, textStatus, jqXHR) {
- if (onSuccess) {
- _.delay(onSuccess, 0, status.success);
- }
- },
- error: onFailure
- });
- },
/**
* Cancel the current user"s account.
@@ -344,7 +364,7 @@ BrowserID.Network = (function() {
cancelUser: function(onSuccess, onFailure) {
post({
url: "/wsapi/account_cancel",
- success: createDeferred(onSuccess),
+ success: onSuccess,
error: onFailure
});
},
@@ -365,8 +385,7 @@ BrowserID.Network = (function() {
site: origin
},
success: function(status) {
- var staged = status.success;
- _.delay(onSuccess, 0, staged);
+ if (onSuccess) onSuccess(status.success);
},
error: onFailure
});
@@ -380,12 +399,10 @@ BrowserID.Network = (function() {
* @param {function} [onfailure] - called on xhr failure.
*/
checkEmailRegistration: function(email, onSuccess, onFailure) {
- xhr.ajax({
+ get({
url: "/wsapi/email_addition_status?email=" + encodeURIComponent(email),
success: function(status, textStatus, jqXHR) {
- if (onSuccess) {
- _.delay(onSuccess, 0, status.status);
- }
+ if (onSuccess) onSuccess(status.status);
},
error: onFailure
});
@@ -401,12 +418,10 @@ BrowserID.Network = (function() {
* @param {function} [onFailure] - Called on XHR failure.
*/
emailRegistered: function(email, onSuccess, onFailure) {
- xhr.ajax({
+ get({
url: "/wsapi/have_email?email=" + encodeURIComponent(email),
success: function(data, textStatus, xhr) {
- if(onSuccess) {
- _.delay(onSuccess, 0, data.email_known);
- }
+ if(onSuccess) onSuccess(data.email_known);
},
error: onFailure
});
@@ -426,11 +441,9 @@ BrowserID.Network = (function() {
email: email
},
success: function(status, textStatus, jqXHR) {
- if (onSuccess) {
- _.delay(onSuccess, 0, status.success);
- }
+ if (onSuccess) onSuccess(status.success);
},
- failure: onFailure
+ error: onFailure
});
},
@@ -438,15 +451,15 @@ BrowserID.Network = (function() {
* Certify the public key for the email address.
* @method certKey
*/
- certKey: function(email, pubkey, onSuccess, onError) {
+ certKey: function(email, pubkey, onSuccess, onFailure) {
post({
url: "/wsapi/cert_key",
data: {
email: email,
pubkey: pubkey.serialize()
},
- success: createDeferred(onSuccess),
- error: onError
+ success: onSuccess,
+ error: onFailure
});
},
@@ -455,10 +468,9 @@ BrowserID.Network = (function() {
* @method listEmails
*/
listEmails: function(onSuccess, onFailure) {
- xhr.ajax({
- type: "GET",
+ get({
url: "/wsapi/list_emails",
- success: createDeferred(onSuccess),
+ success: onSuccess,
error: onFailure
});
},
@@ -482,7 +494,7 @@ BrowserID.Network = (function() {
} catch(e) {
onFailure(e.toString());
}
- });
+ }, onFailure);
}
};
View
92 browserid/static/dialog/resources/user.js
@@ -108,6 +108,10 @@ BrowserID.User = (function() {
// 'mustAuth' - user must authenticate
// 'noRegistration' - no registration is in progress
if (status === "complete" || status === "mustAuth") {
+ // As soon as the registration comes back as complete, we should
+ // ensure that the stagedOnBehalfOf is cleared so there is no stale
+ // data.
+ storage.setStagedOnBehalfOf("");
if (onSuccess) {
onSuccess(status);
}
@@ -118,7 +122,7 @@ BrowserID.User = (function() {
else if (onFailure) {
onFailure(status);
}
- });
+ }, onFailure);
};
poll();
@@ -230,7 +234,7 @@ BrowserID.User = (function() {
var self=this;
// remember this for later
- storage.setStagedOnBehalfOf(origin);
+ storage.setStagedOnBehalfOf(self.getHostname());
network.createUser(email, origin, function(created) {
if (onSuccess) {
@@ -251,6 +255,35 @@ BrowserID.User = (function() {
},
/**
+ * Verify a user
+ * @method verifyUser
+ * @param {string} token - token to verify.
+ * @param {string} password - password to set for account.
+ * @param {function} [onSuccess] - Called to give status updates.
+ * @param {function} [onFailure] - Called on error.
+ */
+ verifyUser: function(token, password, onSuccess, onFailure) {
+ network.emailForVerificationToken(token, function (email) {
+ var invalidInfo = { valid: false };
+ if (email) {
+ network.completeUserRegistration(token, password, function (valid) {
+ var info = valid ? {
+ valid: valid,
+ email: email,
+ origin: storage.getStagedOnBehalfOf()
+ } : invalidInfo;
+
+ storage.setStagedOnBehalfOf("");
+
+ if (onSuccess) onSuccess(info);
+ }, onFailure);
+ } else if(onSuccess) {
+ onSuccess(invalidInfo);
+ }
+ }, onFailure);
+ },
+
+ /**
* Set the password of the current user.
* @method setPassword
* @param {string} password - password to set
@@ -277,7 +310,7 @@ BrowserID.User = (function() {
* identity.
* @method cancelUser
* @param {function} [onSuccess] - Called whenever complete.
- * @param {function} [onFailure] - called on failure.
+ * @param {function} [onFailure] - called on error.
*/
cancelUser: function(onSuccess, onFailure) {
network.cancelUser(function() {
@@ -285,7 +318,7 @@ BrowserID.User = (function() {
if (onSuccess) {
onSuccess();
}
- });
+ }, onFailure);
},
@@ -293,7 +326,7 @@ BrowserID.User = (function() {
* Log the current user out.
* @method logoutUser
* @param {function} [onSuccess] - Called whenever complete.
- * @param {function} [onFailure] - called on failure.
+ * @param {function} [onFailure] - called on error.
*/
logoutUser: function(onSuccess, onFailure) {
network.logout(function() {
@@ -301,7 +334,7 @@ BrowserID.User = (function() {
if (onSuccess) {
onSuccess();
}
- });
+ }, onFailure);
},
/**
@@ -309,7 +342,7 @@ BrowserID.User = (function() {
* be called.
* @method syncEmails
* @param {function} [onSuccess] - Called whenever complete.
- * @param {function} [onFailure] - Called on failure.
+ * @param {function} [onFailure] - Called on error.
*/
syncEmails: function(onSuccess, onFailure) {
cleanupIdentities();
@@ -356,7 +389,7 @@ BrowserID.User = (function() {
}
addNextEmail();
- });
+ }, onFailure);
},
/**
@@ -365,7 +398,7 @@ BrowserID.User = (function() {
* @param {function} [onSuccess] - Called when check is complete with one
* boolean parameter, authenticated. authenticated will be true if user is
* authenticated, false otw.
- * @param {function} [onFailure] - Called on failure.
+ * @param {function} [onFailure] - Called on error.
*/
checkAuthentication: function(onSuccess, onFailure) {
network.checkAuth(function(authenticated) {
@@ -384,7 +417,7 @@ BrowserID.User = (function() {
* but before sync starts. Useful for displaying status messages about the
* sync taking a moment.
* @param {function} [onComplete] - Called on sync completion.
- * @param {function} [onFailure] - Called on failure.
+ * @param {function} [onFailure] - Called on error.
*/
checkAuthenticationAndSync: function(onSuccess, onComplete, onFailure) {
var self=this;
@@ -413,7 +446,7 @@ BrowserID.User = (function() {
* @param {string} email - Email address to authenticate.
* @param {string} password - Password.
* @param {function} [onComplete] - Called on sync completion.
- * @param {function} [onFailure] - Called on failure.
+ * @param {function} [onFailure] - Called on error.
*/
authenticate: function(email, password, onComplete, onFailure) {
var self=this;
@@ -452,6 +485,7 @@ BrowserID.User = (function() {
var self = this;
network.addEmail(email, origin, function(added) {
if (added) {
+ storage.setStagedOnBehalfOf(self.getHostname());
// we no longer send the keypair, since we will certify it later.
if (onSuccess) {
onSuccess(added);
@@ -472,11 +506,41 @@ BrowserID.User = (function() {
},
/**
+ * Verify a users email address given by the token
+ * @method verifyEmail
+ * @param {string} token
+ * @param {function} [onSuccess] - Called on success.
+ * Called with an object with valid, email, and origin if valid, called
+ * with only valid otw.
+ * @param {function} [onFailure] - Called on error.
+ */
+ verifyEmail: function(token, onSuccess, onFailure) {
+ network.emailForVerificationToken(token, function (email) {
+ var invalidInfo = { valid: false };
+ if (email) {
+ network.completeEmailRegistration(token, function (valid) {
+ var info = valid ? {
+ valid: valid,
+ email: email,
+ origin: storage.getStagedOnBehalfOf()
+ } : invalidInfo;
+
+ storage.setStagedOnBehalfOf("");
+
+ if (onSuccess) onSuccess(info);
+ }, onFailure);
+ } else if(onSuccess) {
+ onSuccess(invalidInfo);
+ }
+ }, onFailure);
+ },
+
+ /**
* Remove an email address.
* @method removeEmail
* @param {string} email - Email address to remove.
* @param {function} [onSuccess] - Called when complete.
- * @param {function} [onFailure] - Called on failure.
+ * @param {function} [onFailure] - Called on error.
*/
removeEmail: function(email, onSuccess, onFailure) {
if(storage.getEmail(email)) {
@@ -513,7 +577,7 @@ BrowserID.User = (function() {
* @method getAssertion
* @param {string} email - Email to get assertion for.
* @param {function} [onSuccess] - Called with assertion on success.
- * @param {function} [onFailure] - Called on failure.
+ * @param {function} [onFailure] - Called on error.
*/
getAssertion: function(email, onSuccess, onFailure) {
// we use the current time from the browserid servers
@@ -533,7 +597,7 @@ BrowserID.User = (function() {
if (onSuccess) {
onSuccess(assertion);
}
- });
+ }, onFailure);
}
if (storedID) {
View
34 browserid/static/dialog/test/qunit/controllers/page_controller_unit_test.js
@@ -41,16 +41,21 @@ steal.plugins("jquery").then("/dialog/controllers/page_controller", function() {
bodyTemplate = "testBodyTemplate.ejs",
waitTemplate = "wait.ejs";
+ function reset() {
+ el = $("#controller_head");
+ el.find("#formWrap .contents").html("");
+ el.find("#wait .contents").html("");
+ el.find("#error .contents").html("");
+ }
+
module("PageController", {
setup: function() {
- el = $("#page_controller");
+ reset();
},
teardown: function() {
- el.find("#formWrap .contents").html("");
- el.find("#wait .contents").html("");
- el.find("#error .contents").html("");
controller.destroy();
+ reset();
}
});
@@ -76,10 +81,11 @@ steal.plugins("jquery").then("/dialog/controllers/page_controller", function() {
var html = el.find("#formWrap .contents").html();
ok(html.length, "with template specified, form text is loaded");
+/*
var input = el.find("input").eq(0);
ok(input.is(":focus"), "make sure the first input is focused");
-
+*/
html = el.find("#wait .contents").html();
equal(html, "", "with body template specified, wait text is not loaded");
});
@@ -100,6 +106,22 @@ steal.plugins("jquery").then("/dialog/controllers/page_controller", function() {
ok(html.length, "with wait template specified, wait text is loaded");
});
+ test("page controller with error template renders in #error .contents", function() {
+ controller = el.page({
+ errorTemplate: waitTemplate,
+ errorVars: {
+ title: "Test title",
+ message: "Test message"
+ }
+ }).controller();
+
+ var html = el.find("#formWrap .contents").html();
+ equal(html, "", "with error template specified, form is ignored");
+
+ html = el.find("#error .contents").html();
+ ok(html.length, "with error template specified, error text is loaded");
+ });
+
test("renderError renders an error message", function() {
controller = el.page({
waitTemplate: waitTemplate,
@@ -109,7 +131,7 @@ steal.plugins("jquery").then("/dialog/controllers/page_controller", function() {
}
}).controller();
- controller.renderError({
+ controller.renderError("wait.ejs", {
title: "error title",
message: "error message"
});
View
52 browserid/static/dialog/test/qunit/include_unit_test.js
@@ -0,0 +1,52 @@
+/*jshint browsers:true, forin: true, laxbreak: true */
+/*global steal: true, test: true, start: true, stop: true, module: true, ok: true, equal: true, BrowserID: true */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla BrowserID.
+ *
+ * The Initial Developer of the Original Code is Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+steal.plugins("jquery", "funcunit/qunit").then("/include.js", function() {
+ "use strict";
+
+ module("include.js");
+
+ test("navigator.id is available", function() {
+ equal(typeof navigator.id, "object", "navigator.id namespace is available");
+ });
+
+ test("navigator.id.getVerifiedEmail is available", function() {
+ equal(typeof navigator.id.getVerifiedEmail, "function", "navigator.id.getVerifiedEmail is available");
+ });
+
+
+});
+
View
112 browserid/static/dialog/test/qunit/pages/add_email_address_test.js
@@ -0,0 +1,112 @@
+/*jshint browsers:true, forin: true, laxbreak: true */
+/*global steal: true, test: true, start: true, stop: true, module: true, ok: true, equal: true, BrowserID:true */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla BrowserID.
+ *
+ * The Initial Developer of the Original Code is Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+steal.plugins("jquery").then("/js/pages/add_email_address", function() {
+ "use strict";
+
+ var bid = BrowserID,
+ network = bid.Network,
+ storage = bid.Storage,
+ emailForVerificationTokenFailure = false,
+ completeEmailRegistrationFailure = false,
+ validToken = true;
+
+ var netMock = {
+ emailForVerificationToken: function(token, onSuccess, onFailure) {
+ emailForVerificationTokenFailure ? onFailure() : onSuccess("testuser@testuser.com");
+ },
+
+ completeEmailRegistration: function(token, onSuccess, onFailure) {
+ completeEmailRegistrationFailure ? onFailure() : onSuccess(validToken);
+ }
+ };
+
+ module("pages/add_email_address", {
+ setup: function() {
+ BrowserID.User.setNetwork(netMock);
+ emailForVerificationTokenFailure = completeEmailRegistrationFailure = false;
+ validToken = true;
+ $(".error").stop().hide();
+ $(".website").text("");
+ },
+ teardown: function() {
+ BrowserID.User.setNetwork(network);
+ $(".error").stop().hide();
+ $(".website").text("");
+ }
+ });
+
+ test("addEmailAddress with good token and site", function() {
+ storage.setStagedOnBehalfOf("browserid.org");
+
+ bid.addEmailAddress("token");
+
+ equal($("#email").text(), "testuser@testuser.com", "email set");
+ ok($("#siteinfo").is(":visible"), "siteinfo is visible when we say what it is");
+ equal($("#siteinfo .website").text(), "browserid.org", "origin is updated");
+ });
+
+ test("addEmailAddress with good token and nosite", function() {
+ bid.addEmailAddress("token");
+
+ equal($("#email").text(), "testuser@testuser.com", "email set");
+ equal($("#siteinfo").is(":visible"), false, "siteinfo is not visible without having it");
+ equal($("#siteinfo .website").text(), "", "origin is not updated");
+ });
+
+ test("addEmailAddress with bad token", function() {
+ validToken = false;
+
+ bid.addEmailAddress("token");
+ ok($("#cannotconfirm").is(":visible"), "cannot confirm box is visible");
+ });
+