diff --git a/.gitignore b/.gitignore index a03c5e3933569d05747d2a3e1219fbce7da36a60..1823f2e5796b4df5d7c5694ff922d2e906b415a8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *~ \#*\# .\#* -node_modules +/node_modules +/var diff --git a/ChangeLog b/ChangeLog index a2e7f3ae24920567d2acba932cb5f5ffa8888c8a..87040e9dfcabf257e202b9adcdf208d4c74836dd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,2 +1,12 @@ +train-2011.08.04: + * when user closes dialog without clicking "cancel", properly return 'null' to the webpage (via getVerifiedEmail callback) - issue #107 + * improve checks to warn developer that prerequisite software is missing. issue #110 + * parameterize software to support multiple deployment environments (dev/beta/prod) issues #102 & #52 + * documentation updates. + * improved logging (using the winston logging framework for node.js) + * [website] fixed inclusion of youtube video (now over https to keep browsers from getting scared about mixed mode resource inclusion) + train-1: - * beginning of time, everything is new. + * beginning of time, everything is new. + * (2011.08.03) include youtube video embedding over https (issue #112) + * (2011.08.04) fix mozillalabs.com link in dialog (issue #116) diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index 54a350162cdea99ed069928217df0c21cf9814b7..36639c0b2461db12f0ddd7f25bb51f63254e929d 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -85,9 +85,17 @@ Subsequent steps use different software which you might need to install. * **curl** - used to iniate http requests from the cmd line (to kick the browserid server) * **java** - used to minify css - * **libsqlite3-dev** - database libraries + * **mysql 5.1+** - the preferred persistence backend -### 4. Set up post-update hook +### 4. Set up mysql + + 0. ensure you can connect via TCP - localhost:3306 (like, make sure skip-networking is off in my.cnf) + 1. connect to the database as user root + 2. `CREATE USER 'browserid'@'localhost' IDENTIFIED BY 'browserid';` + 3. `CREATE DATABASE browserid;` + 4. `GRANT CREATE, DELETE, INDEX, INSERT, LOCK TABLES, SELECT, UPDATE ON browserid.* TO 'browserid'@'localhost';` + +### 5. Set up post-update hook *This step is optional* - if you want to manually update code you probably skipped step #1, you can skip this one as well. All you need diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..173bdc47afb6bff9936d8c92f1742d4f21244261 --- /dev/null +++ b/LICENSE @@ -0,0 +1,5 @@ +This software is available under your choice of the following licenses: + + * MPL 1.1 or later: http://www.mozilla.org/MPL/ + * GPL 2.0 or later: http://www.gnu.org/licenses/gpl.html + * LGPL 2.1 or later: http://www.gnu.org/licenses/lgpl.html diff --git a/README.md b/README.md index e84edefb0d776fd66d4553e07bd87f77352498a3..518808162dd1ff1aad9c10ecd7bd72330778e21b 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,6 @@ Here's the software you'll need installed: * node.js (>= 0.4.5): http://nodejs.org/ * npm: http://npmjs.org/ -* sqlite (3) development libraries: http://www.sqlite.org/ * Several node.js 3rd party libraries - see `package.json` for details ## Getting started: diff --git a/browserid/app.js b/browserid/app.js index c62b6a2d8ec9dc419cb9c7a3e7d0805825691083..2f020e2bcec9d7a07493cce35f927bd31a420c1e 100644 --- a/browserid/app.js +++ b/browserid/app.js @@ -1,3 +1,38 @@ +/* ***** 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 ***** */ + const fs = require('fs'), path = require('path'); @@ -12,12 +47,17 @@ crypto = require('crypto'), wsapi = require('./lib/wsapi.js'), httputils = require('./lib/httputils.js'), webfinger = require('./lib/webfinger.js'), -sessions = require('cookie-sessions'), +//sessions = require('cookie-sessions'), +sessions = require('connect-cookie-session'), express = require('express'), secrets = require('./lib/secrets.js'), db = require('./lib/db.js'), configuration = require('../libs/configuration.js'), substitution = require('../libs/substitute.js'); +logging = require("../libs/logging.js"); + +// open the databse +db.open(configuration.get('database')); // looks unused, see run.js // const STATIC_DIR = path.join(path.dirname(__dirname), "static"); @@ -32,7 +72,7 @@ function internal_redirector(new_url) { } function router(app) { - app.set("views", __dirname + '/views'); + app.set("views", __dirname + '/views'); app.set('view options', { production: configuration.get('use_minified_resources') @@ -40,7 +80,8 @@ function router(app) { // this should probably be an internal redirect // as soon as relative paths are figured out. - app.get('/sign_in', function(req, res, next ){ + app.get('/sign_in', function(req, res, next ) { + logging.userEntry('browserid', req); res.render('dialog.ejs', { title: 'A Better Way to Sign In', layout: false, @@ -114,15 +155,31 @@ function router(app) { exports.varDir = VAR_DIR; exports.setup = function(server) { + // over SSL? + var overSSL = (configuration.get('scheme') == 'https'); + server.use(express.cookieParser()); var cookieSessionMiddleware = sessions({ secret: COOKIE_SECRET, - session_key: COOKIE_KEY, - path: '/' + // session_key: COOKIE_KEY, + key: COOKIE_KEY, + cookie: { + path: '/', + httpOnly: true, + maxAge: 14400000, + secure: overSSL + } }); + // cookie sessions server.use(function(req, resp, next) { + // we set this parameter so the connect-cookie-session + // sends the cookie even though the local connection is HTTP + // (the load balancer does SSL) + if (overSSL) + req.connection.proxySecure = true; + try { cookieSessionMiddleware(req, resp, next); } catch(e) { @@ -156,6 +213,15 @@ exports.setup = function(server) { next(); }); + // Strict Transport Security + server.use(function(req, resp, next) { + if (overSSL) { + // expires in 30 days, include subdomains like www + resp.setHeader("Strict-Transport-Security", "max-age=2592000; includeSubdomains"); + } + next(); + }); + // prevent framing server.use(function(req, resp, next) { resp.setHeader('x-frame-options', 'DENY'); diff --git a/browserid/lib/db.js b/browserid/lib/db.js index 482fad4c1d92afa2b94ae982959d540a6f880571..7baab9a6e27577257ee7b21dfa05e69c05098698 100644 --- a/browserid/lib/db.js +++ b/browserid/lib/db.js @@ -1,391 +1,111 @@ -const -sqlite = require('sqlite'), -path = require('path'); - -var VAR_DIR = path.join(path.dirname(__dirname), "var"); - -var db = new sqlite.Database(); - -// a configurable parameter if set immediately after require() of db.js -exports.dbPath = path.join(VAR_DIR, "authdb.sqlite"); +/* ***** 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 ***** */ + +var driver; var ready = false; var waiting = []; +function checkReady() { + if (!ready) throw "database not ready. did you call open()?"; +} + // async break allow database path to be configured by calling code // a touch tricky cause client must set dbPath before releasing // control of the runloop -setTimeout(function() { - db.open(exports.dbPath, function (error) { - if (error) { - console.log("Couldn't open database: " + error); - throw error; - } - db.executeScript( - "CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY, password TEXT );" + - "CREATE TABLE IF NOT EXISTS emails ( id INTEGER PRIMARY KEY, user INTEGER, address TEXT UNIQUE );" + - "CREATE TABLE IF NOT EXISTS keys ( id INTEGER PRIMARY KEY, email INTEGER, key TEXT, expires INTEGER )", - function (error) { - if (error) { - throw error; - } - ready = true; - waiting.forEach(function(f) { f() }); - waiting = []; - }); - }); -}, 0); - -// accepts a function that will be invoked once the database is ready for transactions. -// this hook is important to pause the rest of application startup until async database -// connection establishment is complete. -exports.onReady = function(f) { - setTimeout(function() { - if (ready) f(); - else waiting.push(f); - }, 0); -}; - -// XXX: g_staged and g_stagedEmails should be moved into persistent/fast storage. - -// half created user accounts (pending email verification) -// OR -// half added emails (pending verification) -var g_staged = { -}; - -// an email to secret map for efficient fulfillment of isStaged queries -var g_stagedEmails = { -}; - -function executeTransaction(statements, cb) { - function executeTransaction2(statements, cb) { - if (statements.length == 0) cb(); - else { - var s = statements.shift(); - db.execute(s[0], s[1], function(err, rows) { - if (err) cb(err); - else executeTransaction2(statements, cb); - }); - } +exports.open = function(cfg, cb) { + var driverName = "json"; + if (cfg && cfg.driver) driverName = cfg.driver; + try { + driver = require('./db_' + driverName + '.js'); + } catch(e) { + var msg = "FATAL: couldn't find database driver: " + driverName; + console.log(msg); + throw msg + ": " + e.toString(); } - db.execute('BEGIN', function(err, rows) { - executeTransaction2(statements, function(err) { - if (err) cb(err); - else db.execute('COMMIT', function(err, rows) { - cb(err); - }); - }); - }); -} - -function emailToUserID(email, cb) { - db.execute( - 'SELECT users.id FROM emails, users WHERE emails.address = ? AND users.id == emails.user', - [ email ], - function (err, rows) { - if (rows && rows.length == 1) { - cb(rows[0].id); - } else { - if (err) console.log("database error: " + err); - cb(undefined); + driver.open(cfg, function(error) { + if (error) { + if (cb) cb(error); + else { + console.log("ERROR:" + error); + process.exit(1); } - }); -} - -exports.emailKnown = function(email, cb) { - db.execute( - "SELECT id FROM emails WHERE address = ?", - [ email ], - function(error, rows) { - cb(rows.length > 0); - }); -}; - -// XXX: should be moved to async. -exports.isStaged = function(email) { - return g_stagedEmails.hasOwnProperty(email); -}; - -function generateSecret() { - var str = ""; - const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - for (var i=0; i < 48; i++) { - str += alphabet.charAt(Math.floor(Math.random() * alphabet.length)); - } - return str; -} - -function addEmailToAccount(existing_email, email, pubkey, cb) { - emailToUserID(existing_email, function(userID) { - if (userID == undefined) { - cb("no such email: " + existing_email, undefined); } else { - executeTransaction([ - [ "INSERT INTO emails (user, address) VALUES(?,?)", [ userID, email ] ], - [ "INSERT INTO keys (email, key, expires) VALUES(last_insert_rowid(),?,?)", - [ pubkey, ((new Date()).getTime() + (14 * 24 * 60 * 60 * 1000)) ] - ] - ], function (error) { - if (error) cb(error); - else cb(); - }); + ready = true; + waiting.forEach(function(f) { f() }); + waiting = []; + if (cb) cb(); } }); -} - -exports.emailsBelongToSameAccount = function(lhs, rhs, cb) { - emailToUserID(lhs, function(lhs_uid) { - emailToUserID(rhs, function(rhs_uid) { - cb(lhs_uid === rhs_uid); - }, function (error) { - cb(false); - }); - }, function (error) { - cb(false); - }); }; -exports.addKeyToEmail = function(existing_email, email, pubkey, cb) { - emailToUserID(existing_email, function(userID) { - if (userID == undefined) { - cb("no such email: " + existing_email, undefined); - return; - } - db.execute("SELECT emails.id FROM emails,users WHERE users.id = ? AND emails.address = ? AND emails.user = users.id", - [ userID, email ], - function(err, rows) { - if (err || rows.length != 1) { - cb(err); - return; - } - executeTransaction([ - [ "INSERT INTO keys (email, key, expires) VALUES(?,?,?)", - [ rows[0].id, pubkey, ((new Date()).getTime() + (14 * 24 * 60 * 60 * 1000)) ] - ] - ], function (error) { - if (error) cb(error); - else cb(); - }); - }); +exports.close = function(cb) { + driver.close(function(err) { + ready = false; + cb(err); }); -} - -/* takes an argument object including email, password hash, and pubkey. */ -exports.stageUser = function(obj) { - var secret = generateSecret(); - - // overwrite previously staged users - g_staged[secret] = { - type: "add_account", - email: obj.email, - pubkey: obj.pubkey, - pass: obj.hash - }; - - g_stagedEmails[obj.email] = secret; - return secret; }; -/* takes an argument object including email, pass, and pubkey. */ -// XXX: change to async -exports.stageEmail = function(existing_email, new_email, pubkey) { - var secret = generateSecret(); - // overwrite previously staged users - g_staged[secret] = { - type: "add_email", - existing_email: existing_email, - email: new_email, - pubkey: pubkey - }; - g_stagedEmails[new_email] = secret; - return secret; -}; - -/* invoked when a user clicks on a verification URL in their email */ -exports.gotVerificationSecret = function(secret, cb) { - if (!g_staged.hasOwnProperty(secret)) return cb("unknown secret"); - - // simply move from staged over to the emails "database" - var o = g_staged[secret]; - delete g_staged[secret]; - delete g_stagedEmails[o.email]; - if (o.type === 'add_account') { - exports.emailKnown(o.email, function(known) { - function createAccount() { - executeTransaction([ - [ "INSERT INTO users (password) VALUES(?)", [ o.pass ] ] , - [ "INSERT INTO emails (user, address) VALUES(last_insert_rowid(),?)", [ o.email ] ], - [ "INSERT INTO keys (email, key, expires) VALUES(last_insert_rowid(),?,?)", - [ o.pubkey, ((new Date()).getTime() + (14 * 24 * 60 * 60 * 1000)) ] - ] - ], function (error) { - if (error) cb(error); - else cb(); - }); - } - - // if this email address is known and a user has completed a re-verification of this email - // address, remove the email from the old account that it was associated with, and then - // create a brand new account with only this email. - // NOTE: this might be sub-optimal, but it's a dead simple approach that mitigates many attacks - // and gives us reasonable behavior (without explicitly supporting) in the face of shared email - // addresses. - if (known) { - exports.removeEmail(o.email, o.email, function (err) { - if (err) cb(err); - else createAccount(); - }); - } else { - createAccount(); - } - }); - } else if (o.type === 'add_email') { - exports.emailKnown(o.email, function(known) { - function addIt() { - addEmailToAccount(o.existing_email, o.email, o.pubkey, cb); - } - if (known) { - exports.removeEmail(o.email, o.email, function (err) { - if (err) cb(err); - else addIt(); - }); - } else { - addIt(); - } - }); - } else { - cb("internal error"); - } -}; - -// check authentication credentials for a given email address. This will invoke the -// users callback with the authentication (password/hash/whatever - the database layer -// doesn't care). callback will be passed undefined if email cannot be found -exports.checkAuth = function(email, cb) { - db.execute("SELECT users.password FROM emails, users WHERE users.id = emails.user AND emails.address = ?", - [ email ], - function (error, rows) { - cb(rows.length !== 1 ? undefined : rows[0].password); - }); +// accepts a function that will be invoked once the database is ready for transactions. +// this hook is important to pause the rest of application startup until async database +// connection establishment is complete. +exports.onReady = function(f) { + setTimeout(function() { + if (ready) f(); + else waiting.push(f); + }, 0); }; -function emailHasPubkey(email, pubkey, cb) { - db.execute( - 'SELECT keys.key FROM keys, emails WHERE emails.address = ? AND keys.email = emails.id AND keys.key = ?', - [ email, pubkey ], - function(err, rows) { - cb(rows.length === 1); - }); -} - -/* a high level operation that attempts to sync a client's view with that of the - * server. email is the identity of the authenticated channel with the user, - * identities is a map of email -> pubkey. - * We'll return an object that expresses three different types of information: - * there are several things we need to express: - * 1. emails that the client knows about but we do not - * 2. emails that we know about and the client does not - * 3. emails that we both know about but who need to be re-keyed - * NOTE: it's not neccesary to differentiate between #2 and #3, as the client action - * is the same (regen keypair and tell us about it). - */ -exports.getSyncResponse = function(email, identities, cb) { - var respBody = { - unknown_emails: [ ], - key_refresh: [ ] +[ + 'emailKnown', + 'isStaged', + 'emailsBelongToSameAccount', + 'addKeyToEmail', + 'stageUser', + 'stageEmail', + 'gotVerificationSecret', + 'checkAuth', + 'getSyncResponse', + 'pubkeysForEmail', + 'removeEmail', + 'cancelAccount' +].forEach(function(fn) { + exports[fn] = function() { + checkReady(); + driver[fn].apply(undefined, arguments); }; - - // get the user id associated with this account - emailToUserID(email, function(userID) { - if (userID === undefined) { - cb("no such email: " + email); - return; - } - db.execute( - 'SELECT address FROM emails WHERE ? = user', - [ userID ], - function (err, rows) { - if (err) cb(err); - else { - var emails = [ ]; - var keysToCheck = [ ]; - for (var i = 0; i < rows.length; i++) emails.push(rows[i].address); - - // #1 - for (var e in identities) { - if (emails.indexOf(e) == -1) respBody.unknown_emails.push(e); - else keysToCheck.push(e); - } - - // #2 - for (var e in emails) { - e = emails[e]; - if (!identities.hasOwnProperty(e)) respBody.key_refresh.push(e); - - } - - // #3 -- yes, this is sub-optimal in terms of performance. when we - // move away from public keys this will be unnec. - if (keysToCheck.length) { - var checked = 0; - keysToCheck.forEach(function(e) { - emailHasPubkey(e, identities[e], function(v) { - checked++; - if (!v) respBody.key_refresh.push(e); - if (checked === keysToCheck.length) { - cb(undefined, respBody); - } - }); - }); - } else { - cb(undefined, respBody); - } - } - }); - }); -}; - -// get all public keys associated with an email address -exports.pubkeysForEmail = function(identity, cb) { - db.execute( - 'SELECT keys.key FROM keys, emails WHERE emails.address = ? AND keys.email = emails.id', - [ identity ], - function(err, rows) { - var keys = undefined; - if (!err && rows && rows.length) { - keys = [ ]; - for (var i = 0; i < rows.length; i++) keys.push(rows[i].key); - } - cb(keys); - }); -}; - -exports.removeEmail = function(authenticated_email, email, cb) { - // figure out the user, and remove Email only from addressed - // linked to the authenticated email address - emailToUserID(authenticated_email, function(user_id) { - executeTransaction([ - [ "delete from emails where emails.address = ? and user = ?", [ email,user_id ] ] , - [ "delete from keys where email in (select address from emails where emails.address = ? and user = ?)", [ email,user_id ] ], - ], function (error) { - if (error) cb(error); - else cb(); - }); - }); -}; - -exports.cancelAccount = function(authenticated_email, cb) { - emailToUserID(authenticated_email, function(user_id) { - executeTransaction([ - [ "delete from emails where user = ?", [ user_id ] ] , - [ "delete from keys where email in (select address from emails where user = ?)", [ user_id ] ], - [ "delete from users where id = ?", [ user_id ] ], - ], function (error) { - if (error) cb(error); - else cb(); - }); - }); -}; +}); diff --git a/browserid/lib/db_json.js b/browserid/lib/db_json.js new file mode 100644 index 0000000000000000000000000000000000000000..608476895e7c57c7bc68d7b73633854551461e60 --- /dev/null +++ b/browserid/lib/db_json.js @@ -0,0 +1,357 @@ +/* ***** 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 ***** */ + +/* db_json is a json database driver. It is designed for use in + * local development, is intended to be extremely easy to maintain, + * have minimal dependencies on 3rd party libraries, and we could + * care less if it performs well with more than 10 or so users. + */ +const +path = require('path'), +fs = require('fs'), +secrets = require('./secrets'), +jsel = require('JSONSelect'); + +// a little alias for stringify +const ESC = JSON.stringify; + +var VAR_DIR = path.join(path.dirname(__dirname), "var"); + +var dbPath = path.join(VAR_DIR, "authdb.json"); + +/* The JSON database. The structure is thus: + * [ + * { + * password: "somepass", + * emails: [ + * { + * address: "lloyd@hilaiel.com", + * keys: [ + * { + * key: "SOMESTRINGOFTEXT", + * expires: 1231541615125 + * } + * ] + * } + * ] + * } + * ] + */ + +var db = []; +var stagedEmails = { }; +var staged = { }; + +function flush() { + fs.writeFileSync(dbPath, JSON.stringify(db)); +} + +// when should a key created right now expire? +function getExpiryTime() { + return ((new Date()).getTime() + (14 * 24 * 60 * 60 * 1000)); +} + +exports.open = function(cfg, cb) { + if (cfg && cfg.path) dbPath = cfg.path; + try { + db = JSON.parse(fs.readFileSync(dbPath)); + } catch(e) { + } + + setTimeout(cb, 0); +}; + +exports.close = function(cb) { + flush(); + setTimeout(cb, 0); +}; + +exports.emailKnown = function(email, cb) { + var m = jsel.match(".address:val(" + ESC(email) + ")", db); + setTimeout(function() { cb(m.length > 0) }, 0); +}; + +exports.isStaged = function(email, cb) { + if (cb) { + setTimeout(function() { + cb(stagedEmails.hasOwnProperty(email)); + }, 0); + } +}; + +exports.emailsBelongToSameAccount = function(lhs, rhs, cb) { + emailToUserID(lhs, function(lhs_uid) { + emailToUserID(rhs, function(rhs_uid) { + cb(lhs_uid === rhs_uid); + }, function (error) { + cb(false); + }); + }, function (error) { + cb(false); + }); +}; + +function addEmailToAccount(existing_email, email, pubkey, cb) { + emailToUserID(existing_email, function(userID) { + if (userID == undefined) { + cb("no such email: " + existing_email, undefined); + } else { + db[userID].emails.push({ + address: email, + keys: [ + { + key: pubkey, + expires: getExpiryTime() + } + ] + }); + flush(); + cb(); + } + }); +} + +exports.addKeyToEmail = function(existing_email, email, pubkey, cb) { + emailToUserID(existing_email, function(userID) { + if (userID == undefined) { + cb("no such email: " + existing_email, undefined); + return; + } + + if (!(db[userID].emails)) { + db[userID].emails = [ ]; + } + + var m = jsel.match("object:has(.address:val(" + ESC(email) + ")) > .keys", db[userID].emails); + + var kobj = { + key: pubkey, + expires: getExpiryTime() + }; + + if (m.length) { + m[0].push(kobj); + } else { + db[userID].emails.push({ + address: email, + keys: [ kobj ] + }); + } + + flush(); + if (cb) setTimeout(function() { cb(); }, 0); + }); +} + +exports.stageUser = function(obj, cb) { + var secret = secrets.generate(48); + + // overwrite previously staged users + staged[secret] = { + type: "add_account", + email: obj.email, + pubkey: obj.pubkey, + pass: obj.hash + }; + + stagedEmails[obj.email] = secret; + setTimeout(function() { cb(secret); }, 0); +}; + +exports.stageEmail = function(existing_email, new_email, pubkey, cb) { + var secret = secrets.generate(48); + // overwrite previously staged users + staged[secret] = { + type: "add_email", + existing_email: existing_email, + email: new_email, + pubkey: pubkey + }; + stagedEmails[new_email] = secret; + setTimeout(function() { cb(secret); }, 0); +}; + +exports.gotVerificationSecret = function(secret, cb) { + if (!staged.hasOwnProperty(secret)) return cb("unknown secret"); + + // simply move from staged over to the emails "database" + var o = staged[secret]; + delete staged[secret]; + delete stagedEmails[o.email]; + if (o.type === 'add_account') { + exports.emailKnown(o.email, function(known) { + function createAccount() { + db.push({ + password: o.pass, + emails: [ + { + address: o.email, + keys: [ { + key: o.pubkey, + expires: getExpiryTime(), + } ] + } + ] + }); + flush(); + cb(); + } + + // if this email address is known and a user has completed a re-verification of this email + // address, remove the email from the old account that it was associated with, and then + // create a brand new account with only this email. + // NOTE: this might be sub-optimal, but it's a dead simple approach that mitigates many attacks + // and gives us reasonable behavior (without explicitly supporting) in the face of shared email + // addresses. + + if (known) { + exports.removeEmail(o.email, o.email, function (err) { + if (err) cb(err); + else createAccount(); + }); + } else { + createAccount(); + } + }); + } else if (o.type === 'add_email') { + exports.emailKnown(o.email, function(known) { + function addIt() { + addEmailToAccount(o.existing_email, o.email, o.pubkey, cb); + } + if (known) { + exports.removeEmail(o.email, o.email, function (err) { + if (err) cb(err); + else addIt(); + }); + } else { + addIt(); + } + }); + } else { + cb("internal error"); + } +}; + +exports.checkAuth = function(email, cb) { + var m = jsel.match(":root > object:has(.address:val(" + ESC(email) + ")) > .password", db); + if (m.length === 0) m = undefined; + else m = m[0]; + setTimeout(function() { cb(m) }, 0); +}; + +function emailToUserID(email, cb) { + var id = undefined; + + for (var i = 0; i < db.length; i++) { + if (jsel.match(".address:val(" + JSON.stringify(email) + ")", db[i]).length) { + id = i; + break; + } + if (id !== undefined) break; + } + + setTimeout(function() { cb(id); }, 0); +} + +exports.getSyncResponse = function(email, identities, cb) { + var respBody = { + unknown_emails: [ ], + key_refresh: [ ] + }; + + // get the user id associated with this account + emailToUserID(email, function(userID) { + if (userID === undefined) { + cb("no such email: " + email); + return; + } + var emails = jsel.match(".address", db[userID]); + var keysToCheck = [ ]; + + // #1 emails that the client knows about but we do not + for (var e in identities) { + if (emails.indexOf(e) == -1) respBody.unknown_emails.push(e); + else keysToCheck.push(e); + } + + // #2 emails that we know about and the client does not + for (var e in emails) { + e = emails[e]; + if (!identities.hasOwnProperty(e)) respBody.key_refresh.push(e); + } + + // #3 emails that we both know about but who need to be re-keyed + if (keysToCheck.length) { + var checked = 0; + keysToCheck.forEach(function(e) { + if (!jsel.match(".key:val(" + ESC(identities[e]) + ")", db[userID]).length) + respBody.key_refresh.push(e); + checked++; + if (checked === keysToCheck.length) cb(undefined, respBody); + }); + } else { + cb(undefined, respBody); + } + }); +}; + +exports.pubkeysForEmail = function(identity, cb) { + var m = jsel.match(".emails object:has(.address:val(" + ESC(identity)+ ")) .key", db); + setTimeout(function() { cb(m); }, 0); +}; + +exports.removeEmail = function(authenticated_email, email, cb) { + var m = jsel.match(":root > object:has(.address:val("+ESC(authenticated_email)+")):has(.address:val("+ESC(email)+")) .emails", db); + + if (m.length) { + var emails = m[0]; + for (var i = 0; i < emails.length; i++) { + if (emails[i].address === email) { + emails.splice(i, 1); + break; + } + } + } + + setTimeout(function() { cb(); }, 0); +}; + +exports.cancelAccount = function(authenticated_email, cb) { + emailToUserID(authenticated_email, function(user_id) { + db.splice(user_id, 1); + flush(); + cb(); + }); +}; diff --git a/browserid/lib/db_mysql.js b/browserid/lib/db_mysql.js new file mode 100644 index 0000000000000000000000000000000000000000..1fb2fd8ce2e2f72a778d08b669ecc99bd09b73bd --- /dev/null +++ b/browserid/lib/db_mysql.js @@ -0,0 +1,457 @@ +/* ***** 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 ***** */ + +/* This is a mysql driver for the browserid server. It maps the data + * storage requirements of browserid onto a relational schema. This + * driver is intended to be fast and scalable. + */ + +/* + * The Schema: + * + * +--- user ------+ +--- email ----+ +--- pubkey -----+ + * |*int id | <-\ |*int id | <-\ |*int id | + * | string passwd | \- |*int user | \-- |*int email | + * +---------------+ |*string address | string pubkey | + * +--------------+ | int expires | + * +----------------+ + * + * + * +------ staged ----------+ + * |*string secret | + * | bool new_acct | + * | string existing | + * |*string email | + * | string pubkey | + * | string passwd | + * | timestamp ts | + * +------------------------+ + */ + +const +mysql = require('mysql'), +secrets = require('./secrets'), +logger = require('../../libs/logging.js'); + +var client = undefined; + +// may get defined at open() time causing a database to be dropped upon connection closing. +var drop_on_close = undefined; + +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, address VARCHAR(255) UNIQUE, INDEX(address) );", + "CREATE TABLE IF NOT EXISTS pubkey ( id INTEGER AUTO_INCREMENT PRIMARY KEY, email INTEGER, content TEXT, expiry DATETIME );", + "CREATE TABLE IF NOT EXISTS staged ( secret VARCHAR(48) PRIMARY KEY, new_acct BOOL, existing VARCHAR(255), email VARCHAR(255) UNIQUE, INDEX(email), pubkey TEXT, passwd VARCHAR(64), ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP);" +]; + +// log an unexpected database error +function logUnexpectedError(detail) { + // first, get line number of callee + var where; + try { dne; } catch (e) { where = e.stack.split('\n')[2].trim(); }; + // now log it! + logger.log('db', { type: "unexpected", message: "unexpected database failure", detail: detail, where: where }); +} + +// open & create the mysql database +exports.open = function(cfg, cb) { + if (client) throw "database is already open!"; + client = new mysql.Client(); + // mysql config requires + const defParams = { + host: '127.0.0.1', + port: "3306", + user: 'test', + password: 'pass', + unit_test: false + }; + + Object.keys(defParams).forEach(function(param) { + client[param] = cfg[param] ? cfg[param] : defParams[param]; + }); + + // let's figure out the database name + var database = cfg.database; + if (!database) database = "browserid"; + if (cfg.unit_test) { + database += "_" + secrets.generate(8); + drop_on_close = database; + } + + client.connect(function(error) { + if (error) { + logUnexpectedError(error); + cb(error); + } else { + // 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 (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(); + } + } + createNextTable(0); + }); + }); + } + }); +}; + +exports.close = function(cb) { + function endConn() { + client.end(function(err) { + client = undefined; + if (err) logUnexpectedError(err); + if (cb) cb(err); + }); + } + // when unit_test is specified at open time, we use a temporary database, + // and clean it up upon close. + if (drop_on_close) { + client.query("DROP DATABASE " + drop_on_close, function() { + endConn(); + }); + } else { + endConn(); + } +}; + +exports.emailKnown = function(email, cb) { + client.query( + "SELECT COUNT(*) as N FROM email WHERE address = ?", [ email ], + function(err, rows) { + if (err) logUnexpectedError(err); + cb(rows && rows.length > 0 && rows[0].N > 0); + } + ); +} + +exports.isStaged = function(email, cb) { + client.query( + "SELECT COUNT(*) as N FROM staged WHERE email = ?", [ email ], + function(err, rows) { + if (err) logUnexpectedError(err); + cb(rows && rows.length > 0 && rows[0].N > 0); + } + ); +} + +exports.stageUser = function(obj, cb) { + var secret = secrets.generate(48); + // overwrite previously staged users + client.query('INSERT INTO staged (secret, new_acct, email, pubkey, passwd) VALUES(?,TRUE,?,?,?) ' + + 'ON DUPLICATE KEY UPDATE secret=?, existing="", new_acct=TRUE, pubkey=?, passwd=?', + [ secret, obj.email, obj.pubkey, obj.hash, secret, obj.pubkey, obj.hash], + function(err) { + if (err) { + logUnexpectedError(err); + cb(undefined, err); + } else cb(secret); + }); +} + +exports.gotVerificationSecret = function(secret, cb) { + client.query( + "SELECT * FROM staged WHERE secret = ?", [ secret ], + function(err, rows) { + if (err) { + logUnexpectedError(err); + cb(err); + } else if (rows.length === 0) cb("unknown secret"); + else { + var o = rows[0]; + + function addEmailAndPubkey(userID) { + client.query( + "INSERT INTO email(user, address) VALUES(?, ?)", + [ userID, o.email ], + function(err, info) { + if (err) { logUnexpectedError(err); cb(err); return; } + addKeyToEmailRecord(info.insertId, o.pubkey, cb); + }); + } + + // delete the record + client.query("DELETE LOW_PRIORITY FROM staged WHERE secret = ?", [ secret ]); + + if (o.new_acct) { + // we're creating a new account, add appropriate entries into user, email, and pubkey. + client.query( + "INSERT INTO user(passwd) VALUES(?)", + [ o.passwd ], + function(err, info) { + if (err) { logUnexpectedError(err); cb(err); return; } + addEmailAndPubkey(info.insertId); + }); + } else { + // we're adding an email address to an existing user account. add appropriate entries into email and + // pubkey + client.query( + "SELECT user FROM email WHERE address = ?", [ o.existing ], + function(err, rows) { + if (err) { logUnexpectedError(err); cb(err); } + else if (rows.length === 0) cb("cannot find email address: " + o.existing); + else { + addEmailAndPubkey(rows[0].user); + } + }); + } + } + } + ); +} + +exports.emailsBelongToSameAccount = function(lhs, rhs, cb) { + client.query( + 'SELECT COUNT(*) AS n FROM email WHERE address = ? AND user = ( SELECT user FROM email WHERE address = ? );', + [ lhs, rhs ], + function (err, rows) { + if (err) cb(false); + else cb(rows.length === 1 && rows[0].n === 1); + }); +} + +function addKeyToEmailRecord(emailId, pubkey, cb) { + client.query( + // XXX: 2 weeks is wrong, but then so is keypairs. + "INSERT INTO pubkey(email, content, expiry) VALUES(?, ?, DATE_ADD(NOW(), INTERVAL 2 WEEK))", + [ emailId, pubkey ], + function(err, info) { + if (err) logUnexpectedError(err); + // smash null into undefined. + cb(err ? err : undefined); + }); +} + +exports.addKeyToEmail = function(existing_email, email, pubkey, cb) { + // this function will NOT add a new email address to a user record. The only + // way that happens is when a verification secret is provided to us. Limiting + // the code paths that result in us concluding that a user owns an email address + // is a Good Thing. + exports.emailsBelongToSameAccount(existing_email, email, function(ok) { + if (!ok) { + cb("authenticated user doesn't have permission to add a public key to " + email); + return; + } + + // now we know that the user has permission to add a key. + client.query( + "SELECT id FROM email WHERE address = ?", [ email ], + function(err, rows) { + if (err) { logUnexpectedError(err); cb(err); } + else if (rows.length === 0) cb("cannot find email address: " + email); + else { + addKeyToEmailRecord(rows[0].id, pubkey, cb); + } + }); + }); +} + +exports.stageEmail = function(existing_email, new_email, pubkey, cb) { + var secret = secrets.generate(48); + // overwrite previously staged users + client.query('INSERT INTO staged (secret, new_acct, existing, email, pubkey) VALUES(?,FALSE,?,?,?) ' + + 'ON DUPLICATE KEY UPDATE secret=?, existing=?, new_acct=FALSE, pubkey=?, passwd=""', + [ secret, existing_email, new_email, pubkey, secret, existing_email, pubkey], + function(err) { + if (err) { + logUnexpectedError(err); + cb(undefined, err); + } + else cb(secret); + }); +} + +exports.checkAuth = function(email, cb) { + client.query( + 'SELECT passwd FROM user WHERE id = ( SELECT user FROM email WHERE address = ? )', + [ email ], + function (err, rows) { + if (err) logUnexpectedError(err); + cb((rows && rows.length == 1) ? rows[0].passwd : undefined); + }); +} + +function emailHasPubkey(email, pubkey, cb) { + client.query( + 'SELECT pubkey.content FROM pubkey, email WHERE email.address = ? AND pubkey.email = email.id AND pubkey.content = ?', + [ email, pubkey ], + function(err, rows) { + if (err) logUnexpectedError(err); + cb(rows && rows.length === 1); + }); +} + +/* a high level operation that attempts to sync a client's view with that of the + * server. email is the identity of the authenticated channel with the user, + * identities is a map of email -> pubkey. + * We'll return an object that expresses three different types of information: + * there are several things we need to express: + * 1. emails that the client knows about but we do not + * 2. emails that we know about and the client does not + * 3. emails that we both know about but who need to be re-keyed + * NOTE: it's not neccesary to differentiate between #2 and #3, as the client action + * is the same (regen keypair and tell us about it). + */ +exports.getSyncResponse = function(email, identities, cb) { + var respBody = { + unknown_emails: [ ], + key_refresh: [ ] + }; + + client.query( + 'SELECT address FROM email WHERE user = ( SELECT user FROM email WHERE address = ? ) ', + [ email ], + function (err, rows) { + if (err) cb(err); + else { + var emails = [ ]; + var keysToCheck = [ ]; + for (var i = 0; i < rows.length; i++) emails.push(rows[i].address); + + // #1 + for (var e in identities) { + if (emails.indexOf(e) == -1) respBody.unknown_emails.push(e); + else keysToCheck.push(e); + } + + // #2 + for (var e in emails) { + e = emails[e]; + if (!identities.hasOwnProperty(e)) respBody.key_refresh.push(e); + } + + // #3 -- yes, this is sub-optimal in terms of performance. when we + // move away from public keys this will be unnec. + if (keysToCheck.length) { + var checked = 0; + keysToCheck.forEach(function(e) { + emailHasPubkey(e, identities[e], function(v) { + checked++; + if (!v) respBody.key_refresh.push(e); + if (checked === keysToCheck.length) { + cb(undefined, respBody); + } + }); + }); + } else { + cb(undefined, respBody); + } + } + }); +}; + + +exports.pubkeysForEmail = function(email, cb) { + client.query( + 'SELECT content FROM pubkey WHERE email = (SELECT id FROM email WHERE address = ?)', + [ email ], + function (err, rows) { + var ar = [ ]; + if (!err) rows.forEach(function(r) { ar.push(r.content); }); + else logUnexpectedError(err); + cb(ar); + }); +} + +exports.removeEmail = function(authenticated_email, email, cb) { + exports.emailsBelongToSameAccount(authenticated_email, email, function(ok) { + if (!ok) { + logger.log('security', authenticated_email + ' attempted to delete an email that doesn\'t belong to her: ' + email); + cb("authenticated user doesn't have permission to remove specified email " + email); + return; + } + + client.query( + 'DELETE FROM pubkey WHERE email = ( SELECT id FROM email WHERE address = ? )', + [ email ], + function (err, info) { + if (err) { + logUnexpectedError(err); + cb(err); + } else { + client.query( + 'DELETE FROM email WHERE address = ?', + [ email ], + function(err, info) { + if (err) logUnexpectedError(err); + // smash null into undefined + cb(err ? err : undefined); + }); + } + }); + }); +} + +exports.cancelAccount = function(email, cb) { + function reportErr(err) { if (err) logUnexpectedError(err); } + client.query( + "SELECT user FROM email WHERE address = ?", [ email ], + function (err, rows) { + if (err) { + logUnexpectedError(err) + cb(err); + return + } + var uid = rows[0].user; + client.query("DELETE LOW_PRIORITY FROM pubkey WHERE email in ( SELECT id FROM email WHERE user = ? )", [ uid ], reportErr); + client.query("DELETE LOW_PRIORITY FROM email WHERE user = ?", [ uid ], reportErr); + client.query("DELETE LOW_PRIORITY FROM user WHERE id = ?", [ uid ], reportErr); + cb(); + }); +} diff --git a/browserid/lib/email.js b/browserid/lib/email.js index 9a5f56475308def4b2b63f271dc1050aec384d01..47157e14ab16d7445485f76b0e88e4a5fffa26ed 100644 --- a/browserid/lib/email.js +++ b/browserid/lib/email.js @@ -1,3 +1,38 @@ +/* ***** 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 ***** */ + const db = require('./db'), emailer = require('nodemailer'), diff --git a/browserid/lib/httputils.js b/browserid/lib/httputils.js index 96e8a30c906dd86ae08559daeff6373626d6bddd..f543d403fa1ca703d5f55d48cf92f401f08565da 100644 --- a/browserid/lib/httputils.js +++ b/browserid/lib/httputils.js @@ -1,3 +1,38 @@ +/* ***** 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 ***** */ + // various little utilities to make crafting boilerplate responses // simple diff --git a/browserid/lib/secrets.js b/browserid/lib/secrets.js index 7aca40b986e7e82366fd531e22421f17f57b7dff..b57bf0ea3a0e57fcba6487ec13e118348bec6336 100644 --- a/browserid/lib/secrets.js +++ b/browserid/lib/secrets.js @@ -1,11 +1,46 @@ +/* ***** 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 ***** */ + const path = require('path'), fs = require('fs'); -function generateSecret() { +exports.generate = function(chars) { var str = ""; const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - for (var i=0; i < 128; i++) { + for (var i=0; i < chars; i++) { str += alphabet.charAt(Math.floor(Math.random() * alphabet.length)); } return str; @@ -19,7 +54,7 @@ exports.hydrateSecret = function(name, dir) { try{ secret = fs.readFileSync(p).toString(); } catch(e) {}; if (secret === undefined) { - secret = generateSecret(); + secret = exports.generate(128); fs.writeFileSync(p, secret); } return secret; diff --git a/browserid/lib/webfinger.js b/browserid/lib/webfinger.js index f79045b48db076db08c603613dffcece05119064..b7eb85b2d36154aa0cb4196c3efae460a15d96bf 100644 --- a/browserid/lib/webfinger.js +++ b/browserid/lib/webfinger.js @@ -1,3 +1,38 @@ +/* ***** 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 ***** */ + const db = require('./db.js'), fs = require('fs'), diff --git a/browserid/lib/wsapi.js b/browserid/lib/wsapi.js index e7d757956aa8ddae61e51ab6f7ccc4230ce0d0be..da4c3a2ce11362de844102a64e9b2c3e7880feda 100644 --- a/browserid/lib/wsapi.js +++ b/browserid/lib/wsapi.js @@ -1,3 +1,38 @@ +/* ***** 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 ***** */ + // a module which implements the authorities web server api. // it used to be that we stuffed every function in exports. // now we're using proper express function registration to deal @@ -72,25 +107,26 @@ function setup(app) { try { // upon success, stage_user returns a secret (that'll get baked into a url // and given to the user), on failure it throws - var secret = db.stageUser(stageParams); - - // store the email being registered in the session data - if (!req.session) req.session = {}; + db.stageUser(stageParams, function(secret) { + // store the email being registered in the session data + if (!req.session) req.session = {}; - // store inside the session the details of this pending verification - req.session.pendingVerification = { - email: stageParams.email, - hash: stageParams.hash // we must store both email and password to handle the case where - // a user re-creates an account - specifically, registration status - // must ensure the new credentials work to properly verify that - // the user has clicked throught the email link. note, this salted, bcrypted - // representation of a user's password will get thrust into an encrypted cookie - // served over an encrypted (SSL) session. guten, yah. - }; + // store inside the session the details of this pending verification + req.session.pendingVerification = { + email: stageParams.email, + hash: stageParams.hash // we must store both email and password to handle the case where + // a user re-creates an account - specifically, registration status + // must ensure the new credentials work to properly verify that + // the user has clicked throught the email link. note, this salted, bcrypted + // representation of a user's password will get thrust into an encrypted cookie + // served over an encrypted (SSL) session. guten, yah. + }; - resp.json(true); - email.sendVerificationEmail(stageParams.email, stageParams.site, secret); + resp.json(true); + // let's now kick out a verification email! + email.sendVerificationEmail(stageParams.email, stageParams.site, secret); + }); } catch(e) { // we should differentiate tween' 400 and 500 here. httputils.badRequest(resp, e.toString()); @@ -128,7 +164,6 @@ function setup(app) { } else { // this is a pending registration, let's check if the creds stored on the // session are good yet. - var v = req.session.pendingVerification; db.checkAuth(v.email, function(hash) { if (hash === v.hash) { @@ -161,17 +196,17 @@ function setup(app) { app.post('/wsapi/add_email', checkAuthed, checkParams(["email", "pubkey", "site"]), function (req, resp) { try { - // upon success, stage_user returns a secret (that'll get baked into a url - // and given to the user), on failure it throws - var secret = db.stageEmail(req.session.authenticatedUser, req.body.email, req.body.pubkey); + // on failure stageEmail may throw + db.stageEmail(req.session.authenticatedUser, req.body.email, req.body.pubkey, function(secret) { - // store the email being added in session data - req.session.pendingAddition = req.body.email; + // store the email being added in session data + req.session.pendingAddition = req.body.email; - resp.json(true); + resp.json(true); - // let's now kick out a verification email! - email.sendVerificationEmail(req.body.email, req.body.site, secret); + // let's now kick out a verification email! + email.sendVerificationEmail(req.body.email, req.body.site, secret); + }); } catch(e) { // we should differentiate tween' 400 and 500 here. httputils.badRequest(resp, e.toString()); diff --git a/browserid/run.js b/browserid/run.js index ed8b03dbe38bb9cde7f09640deb67fa27ce96a6c..e83192d2fdf67955d34cf8ddd0d03f0f9c01df39 100755 --- a/browserid/run.js +++ b/browserid/run.js @@ -1,5 +1,40 @@ #!/usr/bin/env node +/* ***** 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 ***** */ + var path = require("path"), fs = require("fs"), express = require("express"); diff --git a/browserid/static/css/style.css b/browserid/static/css/style.css index 0a94ef5ea99e6ee4ac4171169a60b3bd5828cf98..9944eb2906632e0782d519c3488097c03f9e13f4 100644 --- a/browserid/static/css/style.css +++ b/browserid/static/css/style.css @@ -181,6 +181,12 @@ footer .copyright { padding: 0; } +#steps a { + color: #666; + text-decoration: none; + border-bottom: 1px dotted #666; +} + .step { margin: 1em 0 2em 0; padding: 0 0 0 50px; @@ -222,7 +228,7 @@ footer .copyright { font-size: 1.2em; } - pre code { +pre code { padding: 10px 15px 10px 15px; margin: .75em; -webkit-border-radius: 10px; diff --git a/browserid/static/dialog/controllers/addemail_controller.js b/browserid/static/dialog/controllers/addemail_controller.js new file mode 100644 index 0000000000000000000000000000000000000000..035b5faff541dce04931d73caf78ad6006e1f9ad --- /dev/null +++ b/browserid/static/dialog/controllers/addemail_controller.js @@ -0,0 +1,83 @@ +/*jshint brgwser:true, jQuery: true, forin: true, laxbreak:true */ +/*global Channel:true, CryptoStubs:true, alert:true, errorOut:true, setupChannel:true, getEmails:true, clearEmails: true, console: true, _: true, pollTimeout: true, addEmail: true, removeEmail:true, BrowserIDNetwork: true, BrowserIDWait:true, BrowserIDErrors: true, PageController: 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 ***** */ +(function() { + "use strict"; + + PageController.extend("addemail", {}, { + init: function(options) { + this._super({ + bodyTemplate: "addemail.ejs", + bodyVars: { + sitename: BrowserIDNetwork.origin, + identities: getEmails() + }, + footerTemplate: "bottom-addemail.ejs", + footerVars: {} + }); + // select the first option + this.find('input:first').attr('checked', true); + }, + + validate: function() { + var email = $("#email_input").val(); + return /^[\w.!#$%&'*+\-/=?\^`{|}~]+@[a-z0-9-]+(\.[a-z0-9-]+)*$/.test(email); + }, + + submit: function() { + // add the actual email + // now we need to actually try to stage the creation of this account. + var email = $("#email_input").val(); + var keypair = CryptoStubs.genKeyPair(); + + // kick the user to waiting/status page while we talk to the server. + this.doWait(BrowserIDWait.addEmail); + + var self = this; + BrowserIDNetwork.addEmail(email, keypair, function() { + // email successfully staged, now wait for email confirmation + self.close("addemail:complete", { + email: email, + keypair: keypair + }); + }, + function() { + self.runErrorDialog(BrowserIDErrors.addEmail); + }); + } + }); + +}()); diff --git a/browserid/static/dialog/controllers/authenticate_controller.js b/browserid/static/dialog/controllers/authenticate_controller.js new file mode 100644 index 0000000000000000000000000000000000000000..62fdd0b04e303604d5cae7bdbe1364dc223715b2 --- /dev/null +++ b/browserid/static/dialog/controllers/authenticate_controller.js @@ -0,0 +1,86 @@ +/*jshint brgwser:true, jQuery: true, forin: true, laxbreak:true */ +/*global Channel:true, CryptoStubs:true, alert:true, errorOut:true, setupChannel:true, getEmails:true, clearEmails: true, console: true, _: true, pollTimeout: true, addEmail: true, removeEmail:true, BrowserIDNetwork: true, BrowserIDWait:true, BrowserIDErrors: true, PageController: 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 ***** */ +(function() { + "use strict"; + + PageController.extend("Authenticate", {}, { + init: function() { + this._super({ + bodyTemplate: "authenticate.ejs", + bodyVars: { + sitename: BrowserIDNetwork.origin + }, + footerTemplate: "bottom-signin.ejs", + footerVars: {} + }); + }, + + "#forgotpassword click": function(event) { + this.close("authenticate:forgotpassword"); + }, + + "#create click": function(event) { + this.close("authenticate:createuser"); + }, + + validate: function() { + var email = $("#email_input").val(); + var pass = $("#password_input").val(); + + return true; + }, + + submit: function() { + var email = $("#email_input").val(); + var pass = $("#password_input").val(); + + var self = this; + BrowserIDNetwork.authenticate(email, pass, function(authenticated) { + if (authenticated) { + self.doWait(BrowserIDWait.authentication); + self.close("authenticate:authenticated"); + } else { + self.find("#nosuchaccount").hide().fadeIn(400); + } + }, function(resp) { + self.runErrorDialog(BrowserIDErrors.authentication); + self.close("cancel"); + }); + } + }); + +}()); diff --git a/browserid/static/dialog/controllers/checkregistration_controller.js b/browserid/static/dialog/controllers/checkregistration_controller.js new file mode 100644 index 0000000000000000000000000000000000000000..1905523a8bf7da2bf4bcb08d0bf3ce7cb51918f5 --- /dev/null +++ b/browserid/static/dialog/controllers/checkregistration_controller.js @@ -0,0 +1,117 @@ +/*jshint brgwser:true, jQuery: true, forin: true, laxbreak:true */ +/*global Channel:true, CryptoStubs:true, alert:true, errorOut:true, setupChannel:true, getEmails:true, clearEmails: true, console: true, _: true, pollTimeout: true, addEmail: true, removeEmail:true, BrowserIDNetwork: true, BrowserIDWait:true, BrowserIDErrors: true, PageController: 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 ***** */ +(function() { + "use strict"; + + PageController.extend("Checkregistration", {}, { + init: function(options) { + this._super({ + bodyTemplate: "confirmemail.ejs", + bodyVars: { + email: options.email + }, + footerTemplate: "bottom-confirmemail.ejs", + footerVars: {} + }); + $('#continue_button').addClass('disabled'); + this.setupRegCheck(); + }, + + close: function() { + if(this.pollTimeout) { + clearTimeout(this.pollTimeout); + this.pollTimeout = null; + } + + this._super.apply(this, arguments); + }, + + setupRegCheck: function() { + // now poll every 3s waiting for the user to complete confirmation + var self=this; + function setupRegCheck() { + self.pollTimeout = setTimeout(function() { + BrowserIDNetwork.checkRegistration(function(status) { + // registration status checks the status of the last initiated registration, + // it's possible return values are: + // 'complete' - registration has been completed + // 'pending' - a registration is in progress + // 'noRegistration' - no registration is in progress + if (status === 'complete') { + // and tell the user that everything is really quite awesome. + self.find("#waiting_confirmation").hide(); + self.find("#resendit_action").hide(); + self.find("#confirmed_notice").show(); + + // enable button + $('#continue_button').removeClass('disabled'); + + self.publish("checkregistration:confirmed"); + } else if (status === 'pending') { + // try again, what else can we do? + self.setupRegCheck(); + } else { + self.runErrorDialog(BrowserIDErrors.registration); + } + }, + function(jqXHR, textStatus, errorThrown) { + self.runErrorDialog(BrowserIDErrors.registration); + }); + }, 3000); + } + + // setup the timeout + setupRegCheck(); + + }, + + validate: function() { + var valid = !$("#continue_button").hasClass("disabled"); + return valid; + }, + + submit: function() { + var self=this; + self.publish("checkregistration:complete"); + self._super(); + } + + }); + + + +}()); diff --git a/browserid/static/dialog/controllers/chooseemail_controller.js b/browserid/static/dialog/controllers/chooseemail_controller.js new file mode 100644 index 0000000000000000000000000000000000000000..873ffcee1ef5c5d967d07b04e23ac8b14d48e726 --- /dev/null +++ b/browserid/static/dialog/controllers/chooseemail_controller.js @@ -0,0 +1,71 @@ +/*jshint brgwser:true, jQuery: true, forin: true, laxbreak:true */ +/*global Channel:true, CryptoStubs:true, alert:true, errorOut:true, setupChannel:true, getEmails:true, clearEmails: true, console: true, _: true, pollTimeout: true, addEmail: true, removeEmail:true, BrowserIDNetwork: true, BrowserIDWait:true, BrowserIDErrors: true, PageController: 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 ***** */ +(function() { + "use strict"; + + PageController.extend("Chooseemail", {}, { + init: function(options) { + this._super({ + bodyTemplate: "signin.ejs", + bodyVars: { + sitename: BrowserIDNetwork.origin, + identities: getEmails() + }, + footerTemplate: "bottom-pickemail.ejs", + footerVars: {} + }); + // select the first option + this.find('input:first').attr('checked', true); + }, + + submit: function() { + var email = $("#identities input:checked").val(); + this.close("chooseemail:complete", { + email: email + }); + }, + + "#addemail click": function(event) { + this.close("chooseemail:addemail"); + }, + + "#notme click": function(event) { + this.close("chooseemail:notme"); + } + }); + +}()); diff --git a/browserid/static/dialog/controllers/createaccount_controller.js b/browserid/static/dialog/controllers/createaccount_controller.js new file mode 100644 index 0000000000000000000000000000000000000000..50ba5cdb0fae1d0970bfc8268b273cc0ec714388 --- /dev/null +++ b/browserid/static/dialog/controllers/createaccount_controller.js @@ -0,0 +1,169 @@ +/*jshint brgwser:true, jQuery: true, forin: true, laxbreak:true */ +/*global Channel:true, CryptoStubs:true, alert:true, errorOut:true, setupChannel:true, getEmails:true, clearEmails: true, console: true, _: true, pollTimeout: true, addEmail: true, removeEmail:true, BrowserIDNetwork: true, BrowserIDWait:true, BrowserIDErrors: true, PageController: 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 ***** */ +(function() { + "use strict"; + + PageController.extend("Createaccount", {}, { + init: function() { + this._super({ + bodyTemplate: "create.ejs", + bodyVars: {}, + footerTemplate: "bottom-continue.ejs", + footerVars: {} + }); + + $('#create_continue').addClass('disabled'); + + // watch input dialogs + this.setupWatchers(); + }, + + validate: function() { + if ($('#create_continue').hasClass('disabled')) + return false; + return true; + }, + + submit: function() { + // now we need to actually try to stage the creation of this account. + var email = this.find("#email_input").val(); + var pass = this.find("#password_input").val(); + var keypair = CryptoStubs.genKeyPair(); + + this.doWait(BrowserIDWait.createAccount); + + var self = this; + BrowserIDNetwork.stageUser(email, pass, keypair, function() { + self.close("createaccount:created", { + email: email, + keypair: keypair + }); + }, + function() { + self.runErrorDialog(BrowserIDErrors.createAccount); + } + ); + + + }, + + setupWatchers: function() { + var checkedEmails = {}; + var emailCheckState = null; + var nextEmailToCheck = null; + var self = this; + + function checkInput() { + // check the email address + var email = self.find("#email_input").val(); + + if (typeof email === 'string' && email.length) { + var valid = checkedEmails[email]; + if (typeof valid === 'string') { + // oh noes. we tried to check this email, but it failed. let's just not tell the + // user anything, cause this is a non-critical issue + } else if (typeof valid === 'boolean') { + if (valid) { + self.find("#email_input_note").show(); + self.find("#emailinuse_message").hide(); + } else { + $("#emailinuse_message").fadeIn(300); + self.find("#email_input_note").hide(); + $("#in_use_email").text(email); + } + } else { + // this is an email that needs to be checked! + if (emailCheckState !== 'querying') { + if (emailCheckState) { + window.clearTimeout(emailCheckState); + } + emailCheckState = setTimeout(function() { + emailCheckState = 'querying'; + var checkingNow = nextEmailToCheck; + // bounce off the server and enter the 'querying' state + BrowserIDNetwork.haveEmail(checkingNow, function(success) { + checkedEmails[checkingNow] = success; + emailCheckState = undefined; + checkInput(); + },function() { + // some kind of error was encountered. This is non-critical, we'll simply ignore it + // and mark this email check as failed. + checkedEmails[checkingNow] = "server failed"; + emailCheckState = undefined; + checkInput(); + } + ); + }, 700); + } else { + // FIXME: not sure when this comes up, not refactored + // $("#create_dialog div.note:eq(0)").html($('<span class="warning"/>').text("Checking address")); + } + } + nextEmailToCheck = email; + } + + // next let's check the password entry + var pass = $("#password_input").val(); + var match = pass === $("#password_verify_input").val(); + self.find('.passwordnote').hide(); + $('#create_continue').addClass('disabled'); + if (!match) { + self.find('#passwords_different').show(); + } else { + if (!pass) { + self.find('#enter_a_password').show(); + } else if (pass.length < 5) { + self.find('#password_too_short').show(); + } else { + self.find('#password_ok').show(); + $('#create_continue').removeClass('disabled'); + } + } + } + + self.find("input").unbind('keyup').bind('keyup', checkInput); + // do a check at load time, in case the user is using the back button (enables the continue button!) + checkInput(); + }, + + "#suggest_signin click": function(event) { + this.close("createaccount:signin"); + } + + }); + +}()); diff --git a/browserid/static/dialog/controllers/dialog_controller.js b/browserid/static/dialog/controllers/dialog_controller.js index 39d9ffc2b72b4d8f56fc55bc3b4e01d9c1d47235..1f23ac46a4ef9c0a004cdf4e2581087b56299686 100644 --- a/browserid/static/dialog/controllers/dialog_controller.js +++ b/browserid/static/dialog/controllers/dialog_controller.js @@ -1,16 +1,49 @@ -/*jshint browser:true, jQuery: true, forin: true, laxbreak:true */ -/*global Channel:true, CryptoStubs:true, alert:true, errorOut:true, setupChannel:true, runErrorDialog: true, getEmails:true, clearEmails: true, console: true, _: true, pollTimeout: true, addEmail: true, removeEmail:true */ +/*jshint brgwser:true, jQuery: true, forin: true, laxbreak:true */ +/*global Channel:true, CryptoStubs:true, alert:true, errorOut:true, setupChannel:true, getEmails:true, clearEmails: true, console: true, _: true, pollTimeout: true, addEmail: true, removeEmail:true, BrowserIDNetwork: true, BrowserIDWait:true, BrowserIDErrors: true, PageController: true, OpenAjax: 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 ***** */ // // a JMVC controller for the browserid dialog // -$.Controller("Dialog", {}, { - init: function(el) { - // FIXME: there's a small chance the CSRF token doesn't come back in time. - this.csrf = null; - this._getCSRF(); +(function() { +"use strict"; +PageController.extend("Dialog", {}, { + init: function(el) { var html = $.View("//dialog/views/body.ejs", {}); this.element.html(html); this.element.show(); @@ -19,218 +52,79 @@ $.Controller("Dialog", {}, { this.onsuccess = null; this.onerror = null; var chan = setupChannel(this); + this.stateMachine(); }, - _getCSRF: function(cb) { - // go get CSRF token - var self = this; - $.get('/csrf', {}, function(result) { - self.csrf = result; - if (cb) - cb(); - }); - }, - - setupEnterKey: function() { - $("input").keyup(function(e) { - if(e.which == 13) { - $('.submit').click(); - e.preventDefault(); - } - }); - }, - - renderTemplates: function(body, body_vars, footer, footer_vars) { - if (body) { - var bodyHtml = $.View("//dialog/views/" + body, body_vars); - $('#dialog').html(bodyHtml).hide().fadeIn(300, function() { - $('#dialog input').eq(0).focus(); - }); - } - - if (footer) { - var footerHtml = $.View("//dialog/views/" + footer, footer_vars); - $('#bottom-bar').html(footerHtml); - } - this.setupEnterKey(); - }, - - "#suggest_signin click": function(event) { - this.doAuthenticate(); - }, - - "#signin click": function(event) { - var email = $("#email_input").val(); - var pass = $("#password_input").val(); - - var self = this; - - $.ajax({ - type: "POST", - url: '/wsapi/authenticate_user', - data: { - email: email, - pass: pass, - csrf: this.csrf - }, - success: function(status, textStatus, jqXHR) { - var authenticated = JSON.parse(status); - if (!authenticated) { - self.find("#nosuchaccount").hide().fadeIn(400); - } else { - self.doWait("Finishing Sign In...", - "In just a moment you'll be signed into BrowserID."); - - self.syncIdentities(); - } - }, - error: function() { - runErrorDialog( - "serverError", - "Error Authenticating!", - "There was a technical problem while trying to log you in. Yucky!"); - } - }); - }, + getVerifiedEmail: function(origin_url, onsuccess, onerror) { + this.onsuccess = onsuccess; + this.onerror = onerror; - "#pickemail click": function(event) { - var email = $("#identities input:checked").val(); + BrowserIDNetwork.setOrigin(origin_url); - // yay! now we need to produce an assertion. - var storedID = getEmails()[email]; + this.doStart(); - var privkey = storedID.priv; - var issuer = storedID.issuer; - var audience = this.remoteOrigin.replace(/^(http|https):\/\//, ''); - var assertion = CryptoStubs.createAssertion(audience, email, privkey, issuer); - // Clear onerror before the call to onsuccess - the code to onsuccess - // calls window.close, which would trigger the onerror callback if we - // tried this afterwards. - this.onerror = null; - this.onsuccess(assertion); + var self=this; + $(window).bind("unload", function() { + self.doCancel(); + }); }, - "#addemail click": function(event) { - this.doNewEmail(); - }, - "#addemail_button click": function(event) { - // add the actual email - // now we need to actually try to stage the creation of this account. - var email = $("#email_input").val(); - var keypair = CryptoStubs.genKeyPair(); + stateMachine: function() { + var self=this, hub = OpenAjax.hub, el = this.element; - var self = this; + hub.subscribe("createaccount:created", function(msg, info) { + self.doConfirmEmail(info.email, info.keypair); + }); - // kick the user to waiting/status page while we talk to the server. - this.doWait( - "One Moment Please...", - "We're adding this email to your account, this should only take a couple seconds." - ); + hub.subscribe("createaccount:signin", function() { + self.doAuthenticate(); + }); - $.ajax({ - type: 'POST', - url: '/wsapi/add_email', - data: { - email: email, - pubkey: keypair.pub, - site: this.remoteOrigin.replace(/^(http|https):\/\//, ''), - csrf: this.csrf - }, - success: function() { - // email successfully staged, now wait for email confirmation - self.doConfirmEmail(email, keypair); - }, - error: function() { - runErrorDialog( - "serverError", - "Error Adding Address!", - "There was a technical problem while trying to add this email to your account. Yucky."); - } + hub.subscribe("authenticate:authenticated", function() { + self.syncIdentities(); }); - }, - "#notme click": function(event) { - clearEmails(); - var self = this; - $.post("/wsapi/logout", {csrf: this.csrf}, function() { - self._getCSRF(function() { - self.doAuthenticate(); - }); + hub.subscribe("authenticate:createuser", function() { + self.doCreate(); }); - }, - - "#create click": function(event) { - this.doCreate(); - }, - "#forgotpassword click": function(event) { - this.doForgotPassword(); - }, - - "#cancel click": function(event) { - this.onerror("canceled"); - }, + hub.subscribe("authenticate:forgotpassword", function() { + self.doForgotPassword(); + }); - "#back click": function(event) { - this.doStart(); - }, + hub.subscribe("checkregistration:confirmed", function() { + self.doRegistrationConfirmed(); + }); - "#continue_button click": function(event) { - if (!$("#continue_button").hasClass('disabled')) { - this.doSignIn(); - } - }, + hub.subscribe("checkregistration:complete", function() { + self.doSignIn(); + }); - "#create_continue click": function(event) { - if ($('#create_continue').hasClass('disabled')) - return; + hub.subscribe("chooseemail:complete", function(msg, info) { + self.doEmailSelected(info.email); + }); - // now we need to actually try to stage the creation of this account. - var email = this.find("#email_input").val(); - var pass = this.find("#password_input").val(); - var keypair = CryptoStubs.genKeyPair(); + hub.subscribe("chooseemail:addemail", function() { + self.doAddEmail(); + }); - this.doWait( - "One Moment Please...", - "We're creating your account, this should only take a couple seconds"); + hub.subscribe("chooseemail:notme", function() { + self.doNotMe(); + }); - var self = this; + hub.subscribe("addemail:complete", function(msg, info) { + self.doConfirmEmail(info.email, info.keypair); + }); - $.ajax({ - type: "post", - url: '/wsapi/stage_user', - data: {email: email, - pass: pass, - pubkey : keypair.pub, - site : this.remoteOrigin.replace(/^(http|https):\/\//, ''), - csrf : self.csrf}, - success: function() { - // account successfully staged, now wait for email confirmation - self.doConfirmEmail(email, keypair); - }, - error: function() { - runErrorDialog( - "serverError", - "Error Creating Account!", - "There was a technical problem while trying to create your account. Yucky."); - } - }); - }, + hub.subscribe("start", function() { + self.doStart(); + }); - getVerifiedEmail: function(origin_url, onsuccess, onerror) { - this.onsuccess = onsuccess; - this.onerror = onerror; - this.remoteOrigin = origin_url.replace(/^.*:\/\//, ""); - this.doStart(); - var me=this; - $(window).bind("unload", function() { - // In the success case, me.onerror will have been cleared before unload - // is triggered. - if (me.onerror) { - me.onerror("canceled"); - } + hub.subscribe("cancel", function() { + self.doCancel(); }); + }, doStart: function() { @@ -243,212 +137,73 @@ $.Controller("Dialog", {}, { this.doSignIn(); } else { // do we even need to authenticate? - this.checkAuth(function() { - self.syncIdentities(); - }, function() { - self.doAuthenticate(); - }); + this.doCheckAuth(); } }, - doSignIn: function() { - this.renderTemplates("signin.ejs", {sitename: this.remoteOrigin, identities: getEmails()}, - "bottom-pickemail.ejs", {}); + doCancel: function() { + var self=this; + if(self.onsuccess) { + self.onsuccess(null); + } + }, - // select the first option - this.find('input:first').attr('checked', true); + doSignIn: function() { + this.element.chooseemail(); }, doAuthenticate: function() { - this.renderTemplates("authenticate.ejs", {sitename: this.remoteOrigin}, - "bottom-signin.ejs", {}); - + this.element.authenticate(); }, doCreate: function() { - this.renderTemplates("create.ejs", {}, - "bottom-continue.ejs", {}); - - $('#create_continue').addClass('disabled'); - - var checkedEmails = {}; - var emailCheckState = null; - var nextEmailToCheck = null; - var self = this; - - function checkInput() { - // check the email address - var email = self.find("#email_input").val(); - if (typeof email === 'string' && email.length) { - var valid = checkedEmails[email]; - if (typeof valid === 'string') { - // oh noes. we tried to check this email, but it failed. let's just not tell the - // user anything, cause this is a non-critical issue - } else if (typeof valid === 'boolean') { - if (valid) { - self.find("#email_input_note").show(); - self.find("#emailinuse_message").hide(); - } else { - $("#emailinuse_message").fadeIn(300); - self.find("#email_input_note").hide(); - $("#in_use_email").text(email); - } - } else { - // this is an email that needs to be checked! - if (emailCheckState !== 'querying') { - if (emailCheckState) window.clearTimeout(emailCheckState); - emailCheckState = setTimeout(function() { - emailCheckState = 'querying'; - var checkingNow = nextEmailToCheck; - // bounce off the server and enter the 'querying' state - $.ajax({ - url: '/wsapi/have_email?email=' + encodeURIComponent(checkingNow), - success: function(data, textStatus, jqXHR) { - checkedEmails[checkingNow] = !JSON.parse(data); - emailCheckState = undefined; - checkInput(); - }, error: function(jqXHR, textStatus, errorThrown) { - // some kind of error was encountered. This is non-critical, we'll simply ignore it - // and mark this email check as failed. - checkedEmails[checkingNow] = "server failed"; - emailCheckState = undefined; - checkInput(); - } - }); - }, 700); - } else { - // FIXME: not sure when this comes up, not refactored - // $("#create_dialog div.note:eq(0)").html($('<span class="warning"/>').text("Checking address")); - } - } - nextEmailToCheck = email; - //$("#submit").addClass("disabled"); - } - - // next let's check the password entry - var pass = $("#password_input").val(); - var match = pass === $("#password_verify_input").val(); - self.find('.passwordnote').hide(); - $('#create_continue').addClass('disabled'); - if (!match) { - self.find('#passwords_different').show(); - } else { - if (!pass) { - self.find('#enter_a_password').show(); - } else if (pass.length < 5) { - self.find('#password_too_short').show(); - } else { - self.find('#password_ok').show(); - $('#create_continue').removeClass('disabled'); - } - } - } - - // watch input dialogs - self.find("input").unbind('keyup').bind('keyup', checkInput); - this.setupEnterKey(); - - // do a check at load time, in case the user is using the back button (enables the continue button!) - checkInput(); + this.element.createaccount(); }, doForgotPassword: function() { - this.renderTemplates("forgotpassword.ejs", {}, - "bottom-continue.ejs", {}); - - $('#create_continue').addClass('disabled'); - - var self=this; - function checkInput() { - var pass = $("#password_input").val(); - var match = pass === $("#password_verify_input").val(); - self.find('.passwordnote').hide(); - $('#create_continue').addClass('disabled'); - if (!match) { - self.find('#passwords_different').show(); - } else { - if (!pass) { - self.find('#enter_a_password').show(); - } else if (pass.length < 5) { - self.find('#password_too_short').show(); - } else { - self.find('#password_ok').show(); - $('#create_continue').removeClass('disabled'); - } - } - } - - // watch input dialogs - self.find("input").unbind('keyup').bind('keyup', checkInput); - this.setupEnterKey(); - - // do a check at load time, in case the user is using the back button (enables the continue button!) - checkInput(); + this.element.forgotpassword(); }, - doWait: function(title, message) { - this.renderTemplates("wait.ejs", {title: title, message: message}); + doAddEmail: function() { + this.element.addemail(); }, - doNewEmail: function() { - this.renderTemplates("addemail.ejs", {}, - "bottom-addemail.ejs", {}); + doConfirmEmail: function(email, keypair) { + this.confirmEmail = email; + this.confirmKeypair = keypair; - this.setupEnterKey(); + this.element.checkregistration({email: email}); }, - doConfirmEmail: function(email, keypair) { - this.renderTemplates("confirmemail.ejs", {email:email}, - "bottom-confirmemail.ejs", {}); + doRegistrationConfirmed: function() { + var self = this; + // this is a secondary registration from browserid.org, persist + // email, keypair, and that fact + self.persistAddressAndKeyPair(self.confirmEmail, + self.confirmKeypair, "browserid.org:443"); + self.syncIdentities(); - $('#continue_button').addClass('disabled'); + }, - var self = this; + doEmailSelected: function(email) { + var self=this, + // yay! now we need to produce an assertion. + storedID = getEmails()[email], + privkey = storedID.priv, + issuer = storedID.issuer, + audience = BrowserIDNetwork.origin, + assertion = CryptoStubs.createAssertion(audience, email, privkey, issuer); - // now poll every 3s waiting for the user to complete confirmation - function setupRegCheck() { - return setTimeout(function() { - $.ajax({ - url: '/wsapi/registration_status', - success: function(status, textStatus, jqXHR) { - // registration status checks the status of the last initiated registration, - // it's possible return values are: - // 'complete' - registration has been completed - // 'pending' - a registration is in progress - // 'noRegistration' - no registration is in progress - if (status === 'complete') { - // this is a secondary registration from browserid.org, persist - // email, keypair, and that fact - self.persistAddressAndKeyPair(email, keypair, "browserid.org:443"); - - // and tell the user that everything is really quite awesome. - self.find("#waiting_confirmation").hide(); - self.find("#resendit_action").hide(); - self.find("#confirmed_notice").show(); - - // enable button - $('#continue_button').removeClass('disabled'); - - } else if (status === 'pending') { - // try again, what else can we do? - pollTimeout = setupRegCheck(); - } else { - runErrorDialog("serverError", - "Registration Failed", - "An error was encountered and the sign up cannot be completed, please try again later."); - } - }, - error: function(jqXHR, textStatus, errorThrown) { - runErrorDialog("serverError", "Registration Failed", jqXHR.responseText); - } - }); - }, 3000); - } - - // setup the timeout - this.pollTimeout = setupRegCheck(); + // Clear onerror before the call to onsuccess - the code to onsuccess + // calls window.close, which would trigger the onerror callback if we + // tried this afterwards. + self.onerror = null; + self.onsuccess(assertion); + }, - // FIXME cancel this timeout appropriately on cancel + doNotMe: function() { + clearEmails(); + BrowserIDNetwork.logout(this.doAuthenticate.bind(this)); }, persistAddressAndKeyPair: function(email, keypair, issuer) { @@ -479,88 +234,39 @@ $.Controller("Dialog", {}, { }); var self = this; + BrowserIDNetwork.syncEmails(issued_identities, + function onKeySyncSuccess(email, keypair) { + self.persistAddressAndKeyPair(email, keypair, "browserid.org:443"); + }, + function onKeySyncFailure() { + self.runErrorDialog(BrowserIDErrors.syncAddress); + }, + function onSuccess() { + self.doSignIn(); + }, + function onFailure(jqXHR, textStatus, errorThrown) { + self.runErrorDialog(BrowserIDErrors.signIn); + } + ); - $.ajax({ - url: '/wsapi/sync_emails', - type: "post", - data: {'emails': JSON.stringify(issued_identities), - 'csrf': this.csrf}, - success: function(resp, textStatus, jqXHR) { - // first remove idenitites that the server doesn't know about - if (resp.unknown_emails) { - _(resp.unknown_emails).each(function(email_address) { - console.log("removed local identity: " + email_address); - removeEmail(email_address); - }); - } - - // now let's begin iteratively re-keying the emails mentioned in the server provided list - var emailsToAdd = resp.key_refresh; - - function addNextEmail() { - if (!emailsToAdd || !emailsToAdd.length) { - self.doSignIn(); - return; - } - - // pop the first email from the list - var email = emailsToAdd.shift(); - var keypair = CryptoStubs.genKeyPair(); - - $.ajax({ - type: 'POST', - url: '/wsapi/set_key', - data: { - email: email, - pubkey: keypair.pub, - csrf: self.csrf}, - success: function() { - // update emails list and commit to local storage, then go do the next email - self.persistAddressAndKeyPair(email, keypair, "browserid.org:443"); - addNextEmail(); - }, - error: function() { - runErrorDialog( - "serverError", - "Error Adding Address!", - "There was a technical problem while trying to synchronize your account. Yucky."); - } - }); - } - - addNextEmail(); - }, - error: function(jqXHR, textStatus, errorThrown) { - runErrorDialog("serverError", "Signin Failed", jqXHR.responseText); - } - }); - - }, - checkAuth: function(authcb, notauthcb) { - this.doWait("Communicating with server", - "Just a moment while we talk with the server."); - - $.ajax({ - url: '/wsapi/am_authed', - success: function(status, textStatus, jqXHR) { - var authenticated = JSON.parse(status); - if (!authenticated) { - notauthcb(); - } else { - authcb(); - } - }, - error: function() { - runErrorDialog( - "serverError", - "Error Communicating With Server!", - "There was a technical problem while trying to log you in. Yucky!"); - } - }); + + doCheckAuth: function() { + this.doWait(BrowserIDWait.checkAuth); + var self=this; + BrowserIDNetwork.checkAuth(function(authenticated) { + if (authenticated) { + self.syncIdentities(); + } else { + self.doAuthenticate(); + } + }, function() { + self.runErrorDialog(BrowserIDErrors.checkAuthentication); + }); } }); +}()); diff --git a/browserid/static/dialog/controllers/forgotpassword_controller.js b/browserid/static/dialog/controllers/forgotpassword_controller.js new file mode 100644 index 0000000000000000000000000000000000000000..e7037030ce96854af915f047ec94bf82168bfc05 --- /dev/null +++ b/browserid/static/dialog/controllers/forgotpassword_controller.js @@ -0,0 +1,114 @@ +/*jshint brgwser:true, jQuery: true, forin: true, laxbreak:true */ +/*global Channel:true, CryptoStubs:true, alert:true, errorOut:true, setupChannel:true, getEmails:true, clearEmails: true, console: true, _: true, pollTimeout: true, addEmail: true, removeEmail:true, BrowserIDNetwork: true, BrowserIDWait:true, BrowserIDErrors: true, PageController: 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 ***** */ +(function() { + "use strict"; + + PageController.extend("Forgotpassword", {}, { + init: function() { + this._super({ + bodyTemplate: "forgotpassword.ejs", + bodyVars: {}, + footerTemplate: "bottom-continue.ejs", + footerVars: {} + }); + + $("#create_continue").addClass("disabled"); + + this.setupWatchers(); + }, + + setupWatchers: function() { + var self=this; + function checkInput() { + var pass = $("#password_input").val(); + var match = pass === $("#password_verify_input").val(); + self.find(".passwordnote").hide(); + $("#create_continue").addClass("disabled"); + if (!match) { + self.find("#passwords_different").show(); + } else { + if (!pass) { + self.find("#enter_a_password").show(); + } else if (pass.length < 5) { + self.find("#password_too_short").show(); + } else { + self.find("#password_ok").show(); + $("#create_continue").removeClass("disabled"); + } + } + } + + // watch input dialogs + self.find("input").unbind("keyup").bind("keyup", checkInput); + + // do a check at load time, in case the user is using the back button (enables the continue button!) + checkInput(); + + }, + + validate: function() { + if ($("#create_continue").hasClass("disabled")) + return false; + return true; + }, + + submit: function() { + // now we need to actually try to stage the creation of this account. + var email = this.find("#email_input").val(); + var pass = this.find("#password_input").val(); + var keypair = CryptoStubs.genKeyPair(); + + this.doWait(BrowserIDWait.createAccount); + + var self = this; + BrowserIDNetwork.stageUser(email, pass, keypair, function() { + self.close("createaccount:created", { + email: email, + keypair: keypair + }); + }, + function() { + self.runErrorDialog(BrowserIDErrors.createAccount); + } + ); + } + + }); + +}()); + + diff --git a/browserid/static/dialog/controllers/page_controller.js b/browserid/static/dialog/controllers/page_controller.js new file mode 100644 index 0000000000000000000000000000000000000000..a1b6f0937e43b2ece83d8354ce1e8f6361e9b020 --- /dev/null +++ b/browserid/static/dialog/controllers/page_controller.js @@ -0,0 +1,135 @@ +/*jshint brgwser:true, jQuery: true, forin: true, laxbreak:true */ +/*global Channel:true, CryptoStubs:true, alert:true, errorOut:true, setupChannel:true, getEmails:true, clearEmails: true, console: true, _: true, pollTimeout: true, addEmail: true, removeEmail:true, BrowserIDNetwork: true, BrowserIDWait:true, BrowserIDErrors: 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 ***** */ +// +// a JMVC controller for the browserid dialog +// + +(function() { +"use strict"; + + $.Controller.extend("PageController", { + }, { + init: function(options) { + var bodyTemplate = options.bodyTemplate; + var bodyVars = options.bodyVars; + var footerTemplate = options.footerTemplate; + var footerVars = options.footerVars; + + this.renderTemplates(bodyTemplate, bodyVars, footerTemplate, footerVars); + $("form").bind("submit", this.onSubmit.bind(this)); + $("#cancel").click(this.onCancel.bind(this)); + $("#back").click(this.onBack.bind(this)); + }, + + destroy: function() { + $("form").unbind("submit"); + $("input").unbind("keyup"); + $("#cancel").unbind("click"); + $("#back").unbind("click"); + this._super(); + }, + + renderTemplates: function(body, body_vars, footer, footer_vars) { + if (body) { + var bodyHtml = $.View("//dialog/views/" + body, body_vars); + $("#dialog").html(bodyHtml).hide().fadeIn(300, function() { + $("#dialog input").eq(0).focus(); + }); + } + + if (footer) { + var footerHtml = $.View("//dialog/views/" + footer, footer_vars); + $("#bottom-bar").html(footerHtml); + } + }, + + onSubmit: function(event) { + event.stopPropagation(); + event.preventDefault(); + if (this.validate()) { + this.submit(); + } + return false; + }, + + validate: function() { + return true; + }, + + submit: function() { + this.close("submit"); + }, + + doWait: function(info) { + this.renderTemplates("wait.ejs", {title: info.message, message: info.description}); + }, + + close: function(message, data) { + this.destroy(); + if (message) { + this.publish(message, data); + } + }, + + runErrorDialog: function(info) { + $(".dialog").hide(); + + $("#error_dialog div.title").text(info.message); + $("#error_dialog div.content").text(info.description); + + $("#back").hide(); + $("#cancel").hide(); + $("#submit").show().unbind("click").click(function() { + }).text("Close"); + + $("#error_dialog").fadeIn(500); + }, + + onCancel: function(event) { + event.preventDefault(); + event.stopPropagation(); + this.close("cancel"); + }, + + onBack: function(event) { + event.preventDefault(); + event.stopPropagation(); + this.close("start"); + } + }); + +}()); diff --git a/browserid/static/dialog/dialog.js b/browserid/static/dialog/dialog.js index 281b52b02609314d138c318fd34cd8d3e02458f9..238dcc7cb1339b88a8b6da58531a93b319c407ac 100644 --- a/browserid/static/dialog/dialog.js +++ b/browserid/static/dialog/dialog.js @@ -1,3 +1,38 @@ +/* ***** 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 ***** */ + /*globals steal */ steal.plugins( @@ -13,11 +48,22 @@ steal.plugins( 'crypto', 'crypto-api', 'channel', - 'storage') // 3rd party script's (like jQueryUI), in resources folder + 'storage', + 'browserid-extensions', + 'browserid-network', + 'browserid-errors', + 'browserid-wait') // 3rd party script's (like jQueryUI), in resources folder .models() // loads files in models folder - .controllers('dialog') // loads files in controllers folder + .controllers('page', + 'dialog', + 'authenticate', + 'createaccount', + 'checkregistration', + 'forgotpassword', + 'chooseemail', + 'addemail') // loads files in controllers folder .views('authenticate.ejs', 'addemail.ejs', diff --git a/browserid/static/dialog/register_iframe.js b/browserid/static/dialog/register_iframe.js index c2da36defa52957ec807e68f3f6f3b1e9f2c77b8..42494702a9e5792f36c20e8630bfdf67a2f0bddb 100644 --- a/browserid/static/dialog/register_iframe.js +++ b/browserid/static/dialog/register_iframe.js @@ -1,6 +1,38 @@ -/*jshint browser:true, jQuery: true, forin: true */ -/*global Channel:true, CryptoStubs:true, alert:true, errorOut: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 ***** */ + // this is the picker code! it runs in the identity provider's domain, and // fiddles the dom expressed by picker.html (function() { diff --git a/browserid/static/dialog/resources/browserid-errors.js b/browserid/static/dialog/resources/browserid-errors.js new file mode 100644 index 0000000000000000000000000000000000000000..e4ed574ee58c214ceff0ccc18ffa25d2febae21b --- /dev/null +++ b/browserid/static/dialog/resources/browserid-errors.js @@ -0,0 +1,87 @@ +/* ***** 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 ***** */ +var BrowserIDErrors = (function(){ + "use strict"; + + var Errors = { + authentication: { + type: "serverError", + message: "Error Authenticating", + description: "There was a technical problem while trying to log you in. Yucky!" + }, + + addEmail: { + type: "serverError", + message: "Error Adding Address", + description: "There was a technical problem while trying to add this email to your account. Yucky!" + }, + + checkAuthentication: { + type: "serverError", + message: "Error Checking Authentication", + description: "There was a tenical problem while trying to log you in. Yucky!" + }, + + createAccount: { + type: "serverError", + message: "Error Creating Account", + description: "There was a technical problem while trying to create your account. Yucky!" + }, + + registration: { + type: "serverError", + message: "Registration Failed", + description: "An error was encountered and the signup cannot be completed. Yucky!" + }, + + signIn: { + type: "serverError", + message: "Signin Failed", + description: "There was an error signing in. Yucky!" + }, + + syncAddress: { + type: "serverError", + message: "Error Syncing Address", + description: "There was a technical problem while trying to synchronize your account. Yucky!" + } + + }; + + + return Errors; +}()); + + diff --git a/browserid/static/dialog/resources/browserid-extensions.js b/browserid/static/dialog/resources/browserid-extensions.js new file mode 100644 index 0000000000000000000000000000000000000000..1d45a366caec5dbdd111c8028d12846c5c388e36 --- /dev/null +++ b/browserid/static/dialog/resources/browserid-extensions.js @@ -0,0 +1,58 @@ +/* ***** 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 ***** */ + +if (!Function.prototype.bind) { + + Function.prototype.bind = function (oThis) { + + if (typeof this !== "function") // closest thing possible to the ECMAScript 5 internal IsCallable function + throw new TypeError("Function.prototype.bind - what is trying to be fBound is not callable"); + + var aArgs = Array.prototype.slice.call(arguments, 1), + fToBind = this, + fNOP = function () {}, + fBound = function () { + return fToBind.apply(this instanceof fNOP ? this : oThis || window, aArgs.concat(Array.prototype.slice.call(arguments))); + }; + + fNOP.prototype = this.prototype; + fBound.prototype = new fNOP(); + + return fBound; + + }; + +} + diff --git a/browserid/static/dialog/resources/browserid-network.js b/browserid/static/dialog/resources/browserid-network.js new file mode 100644 index 0000000000000000000000000000000000000000..6b1e4eeac5cea8d74404a5d9f3450189ace1e24c --- /dev/null +++ b/browserid/static/dialog/resources/browserid-network.js @@ -0,0 +1,218 @@ +/*jshint browsers:true, forin: true, laxbreak: true */ +/*global _: true, console: true, addEmail: true, removeEmail: true, CryptoStubs: 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 ***** */ +"use strict"; +var BrowserIDNetwork = (function() { + var Network = { + csrf: function(onSuccess) { + $.get('/csrf', {}, function(result) { + BrowserIDNetwork.csrf_token = result; + if(onSuccess) { + onSuccess(); + } + }); + }, + + setOrigin: function(origin) { + BrowserIDNetwork.origin = filterOrigin(origin); + }, + + authenticate: function(email, password, onSuccess, onFailure) { + $.ajax({ + type: "POST", + url: '/wsapi/authenticate_user', + data: { + email: email, + pass: password, + csrf: BrowserIDNetwork.csrf_token + }, + success: function(status, textStatus, jqXHR) { + if(onSuccess) { + var authenticated = JSON.parse(status); + onSuccess(authenticated); + } + }, + error: onFailure + }); + }, + + checkAuth: function(onSuccess, onFailure) { + $.ajax({ + url: '/wsapi/am_authed', + success: function(status, textStatus, jqXHR) { + var authenticated = JSON.parse(status); + onSuccess(authenticated); + }, + error: onFailure + }); + + }, + + logout: function(onSuccess) { + $.post("/wsapi/logout", { + csrf: BrowserIDNetwork.csrf_token + }, + function() { + BrowserIDNetwork.csrf(); + onSuccess(); + }); + }, + + stageUser: function(email, password, keypair, onSuccess, onFailure) { + $.ajax({ + type: "post", + url: '/wsapi/stage_user', + data: { + email: email, + pass: password, + pubkey : keypair.pub, + site : BrowserIDNetwork.origin, + csrf : BrowserIDNetwork.csrf_token + }, + success: onSuccess, + error: onFailure + }); + + }, + + addEmail: function(email, keypair, onSuccess, onFailure) { + $.ajax({ + type: 'POST', + url: '/wsapi/add_email', + data: { + email: email, + pubkey: keypair.pub, + site: BrowserIDNetwork.origin, + csrf: BrowserIDNetwork.csrf_token + }, + success: onSuccess, + error: onFailure + }); + }, + + haveEmail: function(email, onSuccess, onFailure) { + $.ajax({ + url: '/wsapi/have_email?email=' + encodeURIComponent(email), + success: function(data, textStatus, xhr) { + if(onSuccess) { + var success = !JSON.parse(data); + onSuccess(success); + } + }, + error: onFailure + }); + }, + + checkRegistration: function(onSuccess, onFailure) { + $.ajax({ + url: '/wsapi/registration_status', + success: function(status, textStatus, jqXHR) { + if(onSuccess) { + onSuccess(status); + } + }, + error: onFailure + }); + }, + + setKey: function(email, keypair, onSuccess, onError) { + $.ajax({ + type: 'POST', + url: '/wsapi/set_key', + data: { + email: email, + pubkey: keypair.pub, + csrf: BrowserIDNetwork.csrf_token + }, + success: onSuccess, + error: onError + }); + + }, + + syncEmails: function(issued_identities, onKeySyncSuccess, onKeySyncFailure, onSuccess, onFailure) { + $.ajax({ + type: "POST", + url: '/wsapi/sync_emails', + data: { + emails: JSON.stringify(issued_identities), + csrf: BrowserIDNetwork.csrf_token + }, + success: function(resp, textStatus, jqXHR) { + // first remove idenitites that the server doesn't know about + if (resp.unknown_emails) { + _(resp.unknown_emails).each(function(email_address) { + removeEmail(email_address); + }); + } + + // now let's begin iteratively re-keying the emails mentioned in the server provided list + var emailsToAdd = resp.key_refresh; + + function addNextEmail() { + if (!emailsToAdd || !emailsToAdd.length) { + onSuccess(); + return; + } + + // pop the first email from the list + var email = emailsToAdd.shift(); + var keypair = CryptoStubs.genKeyPair(); + + BrowserIDNetwork.setKey(email, keypair, function() { + // update emails list and commit to local storage, then go do the next email + onKeySyncSuccess(email, keypair); + addNextEmail(); + }, onKeySyncFailure); + } + + addNextEmail(); + }, + error: onFailure + } + ); + + + } + }; + + Network.csrf(); + return Network; + + function filterOrigin(origin) { + return origin.replace(/^.*:\/\//, ''); + } +}()); diff --git a/browserid/static/dialog/resources/browserid-wait.js b/browserid/static/dialog/resources/browserid-wait.js new file mode 100644 index 0000000000000000000000000000000000000000..6e4e6fe0849060074679ef9d218be1155b1eaa05 --- /dev/null +++ b/browserid/static/dialog/resources/browserid-wait.js @@ -0,0 +1,64 @@ +/* ***** 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 ***** */ +var BrowserIDWait = (function(){ + "use strict"; + + var Wait = { + authentication: { + message: "Finishing Sign In...", + description: "In just a moment you'll be signed into BrowserID." + }, + + addEmail: { + message: "One Moment Please...", + description: "We're adding this email to your account, this should only take a couple of seconds." + }, + + checkAuth: { + message: "Communicating with server", + description: "Just a moment while we talk with the server." + }, + + createAccount: { + message: "One Moment Please...", + description: "We're creating your account, this should only take a couple of seconds." + } + }; + + + return Wait; +}()); + + diff --git a/browserid/static/dialog/resources/channel.js b/browserid/static/dialog/resources/channel.js index 8f08018fc62acdb6c415363f7526d63a18bb00cc..43aa6703fb4c8d72d6a5b8c059c259dd4250d1d1 100644 --- a/browserid/static/dialog/resources/channel.js +++ b/browserid/static/dialog/resources/channel.js @@ -1,3 +1,38 @@ +/* ***** 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 ***** */ + /*global alert:true, setupNativeChannel:true, setupHTMLChannel:true, Channel:true */ function errorOut(trans, code) { function getVerboseMessage(code) { diff --git a/browserid/static/dialog/resources/crypto-api.js b/browserid/static/dialog/resources/crypto-api.js index 3eeec8c946f93835c19af2e97a20e923d703b81b..76b39ff1e86f7508060c4b07b791436a951f1d5e 100644 --- a/browserid/static/dialog/resources/crypto-api.js +++ b/browserid/static/dialog/resources/crypto-api.js @@ -1,3 +1,38 @@ +/* ***** 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 ***** */ + /*global CryptoStubs:true */ // This file is the cryptographic routines that are required for // BrowserID's HTML5 implementation diff --git a/browserid/static/dialog/resources/main.js b/browserid/static/dialog/resources/main.js index 6b2053d331529a77cfd7b76eedb7d57b9f7bce77..897dcb99b4281f0103b980c0e2a3c78dd657ed90 100644 --- a/browserid/static/dialog/resources/main.js +++ b/browserid/static/dialog/resources/main.js @@ -1,3 +1,38 @@ +/* ***** 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 ***** */ + // this is the picker code! it runs in the identity provider's domain, and // fiddles the dom expressed by picker.html var run = function() { diff --git a/browserid/static/dialog/resources/storage.js b/browserid/static/dialog/resources/storage.js index c8577e40c2f97631968a3651e2a6abe772b50fb7..0a97ee8149984de75bbe5463ec270fa6bbb57944 100644 --- a/browserid/static/dialog/resources/storage.js +++ b/browserid/static/dialog/resources/storage.js @@ -1,3 +1,37 @@ +/* ***** 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 ***** */ var getEmails = function() { try { diff --git a/browserid/static/dialog/style.css b/browserid/static/dialog/style.css index 2612391d98bf3653b06cae42f1f79fcda8a8d263..dbcc58785844f4208f8adae36524e8ec5ab0bb4e 100644 --- a/browserid/static/dialog/style.css +++ b/browserid/static/dialog/style.css @@ -54,7 +54,7 @@ span.sitename, span.email { font-size: .7em; } -#bottom-bar button { +#bottom-bar button, #bottom-bar input[type=submit] { height: 25px; border: 1px solid rgb(145, 145, 145); -moz-border-radius: 4px; @@ -67,13 +67,14 @@ span.sitename, span.email { margin-right: 1em; margin-top: 16px; z-index: 0; + float: left; } -#bottom-bar button.righty { +#bottom-bar button.righty, #bottom-bar input[type=submit].righty { float: right; } -#bottom-bar button.action { +#bottom-bar button.action, #bottom-bar input[type=submit] { background-color: rgb(0,128,211); color: #fff; font-weight: bold; @@ -100,14 +101,13 @@ div.actions { #identities { width: 450px; margin: auto; - list-style: none; } -#identities > ul, #identities > ul > li { - list-style: none; +#identities, #identities > li { + list-style: none; } -#identities > ul > li { +#identities > li { margin-bottom: .5em; height: 1.4em; font-size: .9em; @@ -118,11 +118,11 @@ input[type=radio], input[type=radio] + label { cursor: pointer; } -button { +button, input[type=submit] { cursor: pointer; } -button.disabled { +button.disabled, input[type=submit].disabled { cursor: default; opacity: .3; } diff --git a/browserid/static/dialog/views/addemail.ejs b/browserid/static/dialog/views/addemail.ejs index 2c2584ebd91deb1d1a333ed82de5713fe155898a..c8f21a33e95bafed00558251737fe0caef5f1072 100644 --- a/browserid/static/dialog/views/addemail.ejs +++ b/browserid/static/dialog/views/addemail.ejs @@ -1,9 +1,9 @@ <h2> Add a new email address </h2> <div class="content"> - <div class="summary">Setting up a up a new email address is easy, tell us what it is and we'll get started:</div> + <div class="summary">Setting up a new email address is easy, tell us what it is and we'll get started:</div> <div class="formRow"> <label for="email_input">Email</label> - <input type="email" id="email_input" /> + <input type="email" id="email_input" required/> <span class="note"></span> </div> </div> diff --git a/browserid/static/dialog/views/body.ejs b/browserid/static/dialog/views/body.ejs index b2952fe21134287a155a77a177fe39fa730d00bb..813ccae9848365cb1880d191a491886cf9f616f9 100644 --- a/browserid/static/dialog/views/body.ejs +++ b/browserid/static/dialog/views/body.ejs @@ -6,13 +6,15 @@ <div id="labs_logo" class="sprite"></div> </header> -<div id="dialog" class="dialog"> -</div> +<form action="" onsubmit="return false;"> <!-- using the form element to hook into with js --> + <div id="dialog" class="dialog"> + </div> -<div id="bottom-bar"> -</div> + <div id="bottom-bar"> + </div> +</form> <footer id="terms"> - BrowserID is a <a target="_blank" href="http://mozillalabs.org">Mozilla Labs</a> service. + BrowserID is a <a target="_blank" href="http://mozillalabs.com">Mozilla Labs</a> service. See our <a target="_blank" href="https://browserid.org/tos">terms</a> and <a target="_blank" href="https://browserid.org/privacy">privacy policy</a>, or <a target="_blank" href="https://browserid.org/users">learn more</a>. </footer> diff --git a/browserid/static/dialog/views/bottom-addemail.ejs b/browserid/static/dialog/views/bottom-addemail.ejs index 06c26d8a611151908f2a3d04167048e044da249b..a531ce5a16585fcf7ef858f311e4fb987278f9fa 100644 --- a/browserid/static/dialog/views/bottom-addemail.ejs +++ b/browserid/static/dialog/views/bottom-addemail.ejs @@ -1,3 +1,3 @@ - <button id="back">Go Back</button> <button id="addemail_button" class="righty action submit">Add Email</button> <button id="cancel" class="righty">Cancel</button> + <button id="back">Go Back</button> diff --git a/browserid/static/dialog/views/bottom-confirmemail.ejs b/browserid/static/dialog/views/bottom-confirmemail.ejs index a12dda8935ab5ea8c63fcfc35cb3c6ab1517143a..431593fa8c219d94b22220ecb9d6a4b90828aa6e 100644 --- a/browserid/static/dialog/views/bottom-confirmemail.ejs +++ b/browserid/static/dialog/views/bottom-confirmemail.ejs @@ -1,2 +1,2 @@ -<button class="righty action submit" id="continue_button">Continue</button> +<input type="submit" class="righty action submit" id="continue_button" value="Continue" /> <button class="righty" id="cancel">Cancel</button> diff --git a/browserid/static/dialog/views/bottom-continue.ejs b/browserid/static/dialog/views/bottom-continue.ejs index 53d61073047ae36a5e2658c8c0269ff94970fb2e..9ac23c8079ca1c126f69682ff3a5a703eb6a0b7b 100644 --- a/browserid/static/dialog/views/bottom-continue.ejs +++ b/browserid/static/dialog/views/bottom-continue.ejs @@ -1,3 +1,3 @@ - <button id="back">Go Back</button> - <button id="create_continue" class="righty action submit">Continue</button> + <input type="submit" id="create_continue" class="righty action submit" value="Continue" /> <button id="cancel" class="righty">Cancel</button> + <button id="back">Go Back</button> diff --git a/browserid/static/dialog/views/bottom-pickemail.ejs b/browserid/static/dialog/views/bottom-pickemail.ejs index debe989ff1c3cb8865cb52cead510c6f58a5a59e..b1cf8b5133ab88f7825ae323079587497eb4fba0 100644 --- a/browserid/static/dialog/views/bottom-pickemail.ejs +++ b/browserid/static/dialog/views/bottom-pickemail.ejs @@ -1,2 +1,2 @@ - <button id="pickemail" class="righty action submit">Sign In</button> + <input type="submit" id="pickemail" class="righty action submit" value="Sign In" /> <button id="cancel" class="righty">Cancel</button> diff --git a/browserid/static/dialog/views/bottom-signin.ejs b/browserid/static/dialog/views/bottom-signin.ejs index f069d5033a3d9daf7826300cd74efcdbcb4d81c3..1c235718f4f8afa0cecf608791aa7cc18b5543af 100644 --- a/browserid/static/dialog/views/bottom-signin.ejs +++ b/browserid/static/dialog/views/bottom-signin.ejs @@ -1,2 +1,2 @@ - <button id="signin" class="righty action submit">Sign In</button> + <input type="submit" id="signin" class="righty action submit" name="submit" value="Sign In"/ > <button id="cancel" class="righty">Cancel</button> diff --git a/browserid/static/dialog/views/bottom.ejs b/browserid/static/dialog/views/bottom.ejs index 5c9aca7b789465b1d9c5fc0b85e8cb90a646237c..3d07d27924b46a43fb6c5cf4f01950fac4bfdc46 100644 --- a/browserid/static/dialog/views/bottom.ejs +++ b/browserid/static/dialog/views/bottom.ejs @@ -1,3 +1,3 @@ - <button id="back">Go Back</button> <button id="submit" class="righty action submit">Sign In</button> <button id="cancel" class="righty">Cancel</button> + <button id="back">Go Back</button> diff --git a/browserid/static/dialog/views/create.ejs b/browserid/static/dialog/views/create.ejs index 07b5c1e0a650ae2be95a698eab5b38cc39cb904f..16b4c58849199d9a779542e5caadc38013c22d37 100644 --- a/browserid/static/dialog/views/create.ejs +++ b/browserid/static/dialog/views/create.ejs @@ -2,18 +2,18 @@ <div class="summary">BrowserID makes signing in <b>safer and easier</b>. To begin, please provide an email address and pick a password:</div> <div class="formRow"> <label for="email_input"> Email </label> - <input id="email_input" type="email"/> + <input id="email_input" type="email" required/> <span class="note" id="email_input_note" style="display:none;"> <span class="good">Not registered</span> </span> </div> <div class="formRow"> <label for="password_input"> Password </label> - <input id="password_input" type="password" pattern=".{5,}"/> + <input id="password_input" type="password" pattern=".{5,}" required/> </div> <div class="formRow"> <label for="password_verify_input"> Verify </label> - <input id="password_verify_input" type="password" pattern=".{5,}"/> + <input id="password_verify_input" type="password" pattern=".{5,}" required/> <span class="note passwordnote" id="enter_a_password"><span class="bad">Enter a password</span></span> <span class="note passwordnote" id="passwords_different" style="display:none;"><span class="bad">Passwords different</span></span> <span class="note passwordnote" id="password_too_short" style="display:none;"><span class="bad">Password too short</span></span> diff --git a/browserid/static/dialog/views/forgotpassword.ejs b/browserid/static/dialog/views/forgotpassword.ejs index 7f0870ec35b09b86743a0a1c6efb103ee3b3eb60..9a038b93d5220d81c01a6db54a4f05cdfb2e625b 100644 --- a/browserid/static/dialog/views/forgotpassword.ejs +++ b/browserid/static/dialog/views/forgotpassword.ejs @@ -2,16 +2,16 @@ <div class="summary"><b>Forgot your password?</b> No problem! Enter your email address, pick a new password, and we'll get you set up again!</div> <div class="formRow"> <label for="email_input"> Email </label> - <input id="email_input" type="email"/> + <input id="email_input" type="email" required/> <span class="note" id="email_input_note" style="display:none;"><span class="good">Not registered</span></span> </div> <div class="formRow"> <label for="password_input"> Password </label> - <input id="password_input" type="password" pattern=".{5,}"/> + <input id="password_input" type="password" pattern=".{5,}" required/> </div> <div class="formRow"> <label for="password_verify_input"> Verify </label> - <input id="password_verify_input" type="password" pattern=".{5,}"/> + <input id="password_verify_input" type="password" pattern=".{5,}" required/> <span class="note passwordnote" id="enter_a_password"><span class="bad">Enter a password</span></span> <span class="note passwordnote" id="passwords_different" style="display:none;"><span class="bad">Passwords different</span></span> <span class="note passwordnote" id="password_too_short" style="display:none;"><span class="bad">Password too short</span></span> diff --git a/browserid/static/dialog/views/signin.ejs b/browserid/static/dialog/views/signin.ejs index 0fda9e7e17dbe370938da09d634787c02b2efcb3..25519d6ec68f1e732d0b9149226d23da75e27f33 100644 --- a/browserid/static/dialog/views/signin.ejs +++ b/browserid/static/dialog/views/signin.ejs @@ -1,12 +1,10 @@ <div class="content"> <p class="prompt">What email address would you like to use to sign into <span class="sitename bad"><%= sitename %></span>?</p> - <form id="identities" name="identities"> - <ul> - <% _.each(identities, function(email_obj, email_address) { %> - <li><input type="radio" name="identity" value="<%=email_address%>" id="<%=email_address%>" /><label for="<%=email_address%>"><%= email_address %></label></li> - <% }); %> - </ul> - </form> + <ul id="identities"> + <% _.each(identities, function(email_obj, email_address) { %> + <li><input type="radio" name="identity" value="<%=email_address%>" id="<%=email_address%>" /><label for="<%=email_address%>"><%= email_address %></label></li> + <% }); %> + </ul> </div> <div class="actions"> <div class="action"><a id="addemail" href="#">Add a new email address</a></div> diff --git a/browserid/static/include.js b/browserid/static/include.js index 64b1ff758d4d6cb0ea8b269e08b99ba5d57702f7..b2083c867a5507225c4e0c476b86d9f629f7ed27 100644 --- a/browserid/static/include.js +++ b/browserid/static/include.js @@ -1,3 +1,38 @@ +/* ***** 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 ***** */ + // this is the file that the RP includes to shim in the // navigator.id.getVerifiedEmail() function @@ -8,8 +43,9 @@ if (!navigator.id) { if (!navigator.id.getVerifiedEmail || navigator.id._getVerifiedEmailIsShimmed) { var ipServer = "https://browserid.org"; + var isMobile = navigator.userAgent.indexOf('Fennec/') != -1; - // local embedded copy of jschannel: http://github.com/mozilla/jschannel + // local embedded copy of jschannel: http://github.com/mozilla/jschannel var Channel = (function() { // current transaction id, start out at a random *odd* number between 1 and a million // There is one current transaction counter id per page, and it's shared between @@ -538,9 +574,8 @@ if (!navigator.id.getVerifiedEmail || navigator.id._getVerifiedEmailIsShimmed) function _open_window() { return window.open( - //ipServer + "/dialog/dialog/dialog.html", "_mozid_signin", - ipServer + "/sign_in", "_mozid_signin", - "menubar=0,location=0,resizable=0,scrollbars=0,status=0,dialog=1,width=520,height=350"); + ipServer + "/sign_in", "_mozid_signin", + isMobile ? undefined : "menubar=0,location=0,resizable=0,scrollbars=0,status=0,dialog=1,width=520,height=350"); } navigator.id.getVerifiedEmail = function(callback) { @@ -608,7 +643,7 @@ if (!navigator.id.getVerifiedEmail || navigator.id._getVerifiedEmailIsShimmed) navigator.id.getSpecificVerifiedEmail = function(email, token, onsuccess, onerror) { var doc = window.document; - // if we have a token, we should not be opening a window, rather we should be + // if we have a token, we should not be opening a window, rather we should be // able to do this entirely through IFRAMEs if (token) { var iframe = _create_iframe(doc); @@ -676,7 +711,7 @@ if (!navigator.id.getVerifiedEmail || navigator.id._getVerifiedEmailIsShimmed) cleanup(); } }); - }; + }; navigator.id._getVerifiedEmailIsShimmed = true; } diff --git a/browserid/static/js/browserid.js b/browserid/static/js/browserid.js index d13546d9dfae0c83a3a9001b83ac53df7e95270a..b497d80557276a21680b67f8c01eefa0a2be7683 100644 --- a/browserid/static/js/browserid.js +++ b/browserid/static/js/browserid.js @@ -1,3 +1,38 @@ +/* ***** 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 ***** */ + $(function() { if ($('#emailList')) { display_saved_ids(); diff --git a/browserid/tests/db-test.js b/browserid/tests/db-test.js index bd7afd9145bf30bb15b9d2d0f9c6a645b1c353be..c0a492ece0dcbe822a3842be7adb4fe59e949b36 100755 --- a/browserid/tests/db-test.js +++ b/browserid/tests/db-test.js @@ -1,5 +1,40 @@ #!/usr/bin/env node +/* ***** 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 ***** */ + const assert = require('assert'), vows = require('vows'), @@ -12,364 +47,425 @@ var suite = vows.describe('db'); // disable vows (often flakey?) async error behavior suite.options.error = false; -db.dbPath = temp.path({suffix: '.sqlite'}); - -suite.addBatch({ - "waiting for the database to become ready": { - topic: function() { - var cb = this.callback; - db.onReady(function() { cb(true) }); - }, - "the database is ready": function(r) { - assert.strictEqual(r, true); - } - } -}); - -// caching of secrets between test batches. -var secret = undefined; +function addTestsForDriver(driver) { + var dbPath = temp.path({suffix: '.db'}); -suite.addBatch({ - "an email address is not reported as staged before it is": { - topic: function() { - return db.isStaged('lloyd@nowhe.re'); - }, - "isStaged returns false": function (r) { - assert.strictEqual(r, false); - } - }, - "an email address is not reported as known before it is": { - topic: function() { - db.emailKnown('lloyd@nowhe.re', this.callback); - }, - "emailKnown returns false": function (r) { - assert.strictEqual(r, false); - } - } -}); - -suite.addBatch({ - "stage a user for creation pending verification": { - topic: function() { - return secret = db.stageUser({ - email: 'lloyd@nowhe.re', - pubkey: 'fakepubkey', - hash: 'fakepasswordhash' - }); - }, - "staging returns a valid secret": function(r) { - assert.isString(secret); - assert.strictEqual(secret.length, 48); - } + if (driver === 'mysql') { + // let's check to see if we can connect and render a nice + // error message if not. For community members making casual + // contributions, we should expect that they might not want to + // set up mysql. + suite.addBatch({ + "mysql server": { + topic: function() { db.open({driver: driver, unit_test: true}, this.callback) }, + "accepting connections": function(err) { + if (err) { + console.log("MYSQL TESTS WILL FAIL cause cannot connect to a local mysql database (" + err.message + ")"); + } + }, + "connection closes": { + topic: function() { db.close(this.callback); }, + "without error": function(err) { + assert.isUndefined(err); + } + } + } + }); } -}); -suite.addBatch({ - "an email address is reported": { - topic: function() { - return db.isStaged('lloyd@nowhe.re'); + suite.addBatch({ + "onReady": { + topic: function() { db.onReady(this.callback); }, + "works": function(r) { } }, - " as staged after it is": function (r) { - assert.strictEqual(r, true); - } - }, - "an email address is not reported": { - topic: function() { - db.emailKnown('lloyd@nowhe.re', this.callback); + "onReady still": { + topic: function() { db.onReady(this.callback); }, + "works for more than one caller": function(r) { } }, - " as known when it is only staged": function (r) { - assert.strictEqual(r, false); - } - } -}); - -suite.addBatch({ - "upon receipt of a secret": { - topic: function() { - db.gotVerificationSecret(secret, this.callback); - }, - "gotVerificationSecret completes without error": function (r) { - assert.strictEqual(r, undefined); + "opening the database": { + topic: function() { + db.open({ driver: driver, unit_test: true, path: dbPath }, this.callback); + }, + "and its ready": function(r) { + assert.isUndefined(r); + }, + "doesn't prevent onReady": { + topic: function() { db.onReady(this.callback); }, + "from working": function(r) { } + } } - } -}); + }); -suite.addBatch({ - "an email address is not reported": { - topic: function() { - return db.isStaged('lloyd@nowhe.re'); - }, - "as staged immediately after its verified": function (r) { - assert.strictEqual(r, false); - } - }, - "an email address is known": { - topic: function() { - db.emailKnown('lloyd@nowhe.re', this.callback); - }, - "when it is": function (r) { - assert.strictEqual(r, true); - } - } -}); + // caching of secrets between test batches. + var secret = undefined; -suite.addBatch({ - "adding keys to email": { - topic: function() { - db.addKeyToEmail('lloyd@nowhe.re', 'lloyd@nowhe.re', 'fakepubkey2', this.callback); - }, - "works": function(r) { - assert.isUndefined(r); - } - } -}); - -suite.addBatch({ - "adding multiple keys to email": { - topic: function() { - db.addKeyToEmail('lloyd@nowhe.re', 'lloyd@nowhe.re', 'fakepubkey3', this.callback); + suite.addBatch({ + "an email address is not reported as staged before it is": { + topic: function() { + db.isStaged('lloyd@nowhe.re', this.callback); + }, + "isStaged returns false": function (r) { + assert.isFalse(r); + } }, - "works too": function(r) { - assert.isUndefined(r); + "an email address is not reported as known before it is": { + topic: function() { + db.emailKnown('lloyd@nowhe.re', this.callback); + }, + "emailKnown returns false": function (r) { + assert.isFalse(r); + } } - } -}); + }); -suite.addBatch({ - "pubkeysForEmail": { - topic: function() { - db.pubkeysForEmail('lloyd@nowhe.re', this.callback); - }, - "returns all public keys properly": function(r) { - assert.isArray(r); - assert.strictEqual(r.length, 3); + suite.addBatch({ + "stage a user for creation pending verification": { + topic: function() { + db.stageUser({ + email: 'lloyd@nowhe.re', + pubkey: 'fakepubkey', + hash: 'fakepasswordhash' + }, this.callback); + }, + "staging returns a valid secret": function(r) { + secret = r; + assert.isString(secret); + assert.strictEqual(secret.length, 48); + } } - } -}); + }); -suite.addBatch({ - "checkAuth returns": { - topic: function() { - db.checkAuth('lloyd@nowhe.re', this.callback); + suite.addBatch({ + "an email address is reported": { + topic: function() { + db.isStaged('lloyd@nowhe.re', this.callback); + }, + " as staged after it is": function (r) { + assert.strictEqual(r, true); + } }, - "the correct password": function(r) { - assert.strictEqual(r, "fakepasswordhash"); + "an email address is not reported": { + topic: function() { + db.emailKnown('lloyd@nowhe.re', this.callback); + }, + " as known when it is only staged": function (r) { + assert.strictEqual(r, false); + } } - } -}); + }); -suite.addBatch({ - "staging an email": { - topic: function() { - return db.stageEmail('lloyd@nowhe.re', 'lloyd@somewhe.re', 'fakepubkey4'); - }, - "yields a valid secret": function(secret) { - assert.isString(secret); - assert.strictEqual(secret.length, 48); - }, - "makes email addr via isStaged": { - topic: function() { return db.isStaged('lloyd@somewhe.re'); }, - "visible": function(r) { assert.isTrue(r); } - }, - "and verifying it": { - topic: function(secret) { + suite.addBatch({ + "upon receipt of a secret": { + topic: function() { db.gotVerificationSecret(secret, this.callback); }, - "returns no error": function(r) { - assert.isUndefined(r); - }, - "makes email addr via knownEmail": { - topic: function() { db.emailKnown('lloyd@somewhe.re', this.callback); }, - "visible": function(r) { assert.isTrue(r); } - }, - "makes email addr via isStaged": { - topic: function() { return db.isStaged('lloyd@somewhe.re'); }, - "not visible": function(r) { assert.isFalse(r); } + "gotVerificationSecret completes without error": function (r) { + assert.strictEqual(r, undefined); } } - } -}); + }); -// exports.emailsBelongToSameAccount -suite.addBatch({ - "emails do belong to the same account": { - "is true": { + suite.addBatch({ + "an email address is not reported": { topic: function() { - db.emailsBelongToSameAccount('lloyd@nowhe.re', 'lloyd@somewhe.re', this.callback); + db.isStaged('lloyd@nowhe.re', this.callback); }, - "when they do": function(r) { - assert.isTrue(r); + "as staged immediately after its verified": function (r) { + assert.strictEqual(r, false); } }, - "is false": { + "an email address is known": { topic: function() { - db.emailsBelongToSameAccount('lloyd@anywhe.re', 'lloyd@somewhe.re', this.callback); + db.emailKnown('lloyd@nowhe.re', this.callback); }, - "when they don't": function(r) { - assert.isFalse(r); + "when it is": function (r) { + assert.strictEqual(r, true); } } - } -}); + }); -// exports.getSyncResponse -suite.addBatch({ - "sync responses": { - "are empty": { + suite.addBatch({ + "adding keys to email": { topic: function() { - db.getSyncResponse('lloyd@nowhe.re', - { - 'lloyd@nowhe.re': 'fakepubkey', - 'lloyd@somewhe.re': 'fakepubkey4' - }, - this.callback); + db.addKeyToEmail('lloyd@nowhe.re', 'lloyd@nowhe.re', 'fakepubkey2', this.callback); }, - "when everything is in sync": function (err, resp) { - assert.isUndefined(err); - assert.isArray(resp.unknown_emails); - assert.isArray(resp.key_refresh); - assert.strictEqual(resp.unknown_emails.length, 0); - assert.strictEqual(resp.key_refresh.length, 0); + "works": function(r) { + assert.isUndefined(r); } - }, - "handles client unknown emails": { + } + }); + + suite.addBatch({ + "adding multiple keys to email": { topic: function() { - db.getSyncResponse('lloyd@nowhe.re', - { - 'lloyd@nowhe.re': 'fakepubkey' - }, - this.callback); + db.addKeyToEmail('lloyd@nowhe.re', 'lloyd@nowhe.re', 'fakepubkey3', this.callback); }, - "by returning them in the key_refresh list": function (err, resp) { - assert.isUndefined(err); - assert.isArray(resp.unknown_emails); - assert.isArray(resp.key_refresh); - assert.strictEqual(resp.unknown_emails.length, 0); - assert.strictEqual(resp.key_refresh.length, 1); - assert.strictEqual(resp.key_refresh[0], 'lloyd@somewhe.re'); + "works too": function(r) { + assert.isUndefined(r); } - }, - "handles server unknown emails": { + } + }); + + suite.addBatch({ + "pubkeysForEmail": { topic: function() { - db.getSyncResponse('lloyd@nowhe.re', - { - 'lloyd@nowhe.re': 'fakepubkey', - 'lloyd@somewhe.re': 'fakepubkey4', - 'lloyd@anywhe.re': 'nofakepubkey', - }, - this.callback); + db.pubkeysForEmail('lloyd@nowhe.re', this.callback); }, - "by returning them in the unknown_emails list": function (err, resp) { - assert.isUndefined(err); - assert.isArray(resp.unknown_emails); - assert.strictEqual(resp.unknown_emails.length, 1); - assert.strictEqual(resp.unknown_emails[0], 'lloyd@anywhe.re'); - assert.isArray(resp.key_refresh); - assert.strictEqual(resp.key_refresh.length, 0); + "returns all public keys properly": function(r) { + assert.isArray(r); + assert.strictEqual(r.length, 3); } - }, - "handles server unknown keys": { + } + }); + + suite.addBatch({ + "checkAuth returns": { topic: function() { - db.getSyncResponse('lloyd@nowhe.re', - { - 'lloyd@nowhe.re': 'fakepubkeyINVALID', - 'lloyd@somewhe.re': 'fakepubkey4' - }, - this.callback); + db.checkAuth('lloyd@nowhe.re', this.callback); }, - "by returning them in the key_refresh list": function (err, resp) { - assert.isUndefined(err); - assert.isArray(resp.unknown_emails); - assert.strictEqual(resp.unknown_emails.length, 0); - assert.isArray(resp.key_refresh); - assert.strictEqual(resp.key_refresh.length, 1); - assert.strictEqual(resp.key_refresh[0], 'lloyd@nowhe.re'); + "the correct password": function(r) { + assert.strictEqual(r, "fakepasswordhash"); } - }, - "handle more than one case at a time": { + } + }); + + suite.addBatch({ + "staging an email": { topic: function() { - db.getSyncResponse('lloyd@nowhe.re', - { - 'lloyd@somewhe.re': 'fakepubkeyINVALID', - 'lloyd@anywhe.re': 'notreally' - }, - this.callback); + db.stageEmail('lloyd@nowhe.re', 'lloyd@somewhe.re', 'fakepubkey4', this.callback); }, - "when everything is outta sync": function (err, resp) { - assert.isUndefined(err); - assert.isArray(resp.unknown_emails); - assert.strictEqual(resp.unknown_emails.length, 1); - assert.strictEqual(resp.unknown_emails[0], 'lloyd@anywhe.re'); + "yields a valid secret": function(secret) { + assert.isString(secret); + assert.strictEqual(secret.length, 48); + }, + "then": { + topic: function(secret) { + var cb = this.callback; + db.isStaged('lloyd@somewhe.re', function(r) { cb(secret, r); }); + }, + "makes it visible via isStaged": function(sekret, r) { assert.isTrue(r); }, + "and lets you verify it": { + topic: function(secret, r) { + db.gotVerificationSecret(secret, this.callback); + }, + "successfully": function(r) { + assert.isUndefined(r); + }, + "and knownEmail": { + topic: function() { db.emailKnown('lloyd@somewhe.re', this.callback); }, + "returns true": function(r) { assert.isTrue(r); } + }, + "and isStaged": { + topic: function() { db.isStaged('lloyd@somewhe.re', this.callback); }, + "returns false": function(r) { assert.isFalse(r); } + } + } + } + } + }); - assert.isArray(resp.key_refresh); - assert.strictEqual(resp.key_refresh.length, 2); - assert.strictEqual(resp.key_refresh[0], 'lloyd@nowhe.re'); - assert.strictEqual(resp.key_refresh[1], 'lloyd@somewhe.re'); + // exports.emailsBelongToSameAccount + suite.addBatch({ + "emails do belong to the same account": { + "is true": { + topic: function() { + db.emailsBelongToSameAccount('lloyd@nowhe.re', 'lloyd@somewhe.re', this.callback); + }, + "when they do": function(r) { + assert.isTrue(r); + } + }, + "is false": { + topic: function() { + db.emailsBelongToSameAccount('lloyd@anywhe.re', 'lloyd@somewhe.re', this.callback); + }, + "when they don't": function(r) { + assert.isFalse(r); + } } } - } -}); + }); -suite.addBatch({ - "removing an existing email": { - topic: function() { - db.removeEmail("lloyd@somewhe.re", "lloyd@nowhe.re", this.callback); - }, - "returns no error": function(r) { - assert.isUndefined(r); - }, - "causes emailKnown": { - topic: function() { - db.emailKnown('lloyd@nowhe.re', this.callback); + // exports.getSyncResponse + suite.addBatch({ + "sync responses": { + "are empty": { + topic: function() { + db.getSyncResponse('lloyd@nowhe.re', + { + 'lloyd@nowhe.re': 'fakepubkey', + 'lloyd@somewhe.re': 'fakepubkey4' + }, + this.callback); + }, + "when everything is in sync": function (err, resp) { + assert.isUndefined(err); + assert.isArray(resp.unknown_emails); + assert.isArray(resp.key_refresh); + assert.strictEqual(resp.unknown_emails.length, 0); + assert.strictEqual(resp.key_refresh.length, 0); + } }, - "to return false": function (r) { - assert.strictEqual(r, false); + "handles client unknown emails": { + topic: function() { + db.getSyncResponse('lloyd@nowhe.re', + { + 'lloyd@nowhe.re': 'fakepubkey' + }, + this.callback); + }, + "by returning them in the key_refresh list": function (err, resp) { + assert.isUndefined(err); + assert.isArray(resp.unknown_emails); + assert.isArray(resp.key_refresh); + assert.strictEqual(resp.unknown_emails.length, 0); + assert.strictEqual(resp.key_refresh.length, 1); + assert.strictEqual(resp.key_refresh[0], 'lloyd@somewhe.re'); + } + }, + "handles server unknown emails": { + topic: function() { + db.getSyncResponse('lloyd@nowhe.re', + { + 'lloyd@nowhe.re': 'fakepubkey', + 'lloyd@somewhe.re': 'fakepubkey4', + 'lloyd@anywhe.re': 'nofakepubkey', + }, + this.callback); + }, + "by returning them in the unknown_emails list": function (err, resp) { + assert.isUndefined(err); + assert.isArray(resp.unknown_emails); + assert.strictEqual(resp.unknown_emails.length, 1); + assert.strictEqual(resp.unknown_emails[0], 'lloyd@anywhe.re'); + assert.isArray(resp.key_refresh); + assert.strictEqual(resp.key_refresh.length, 0); + } + }, + "handles server unknown keys": { + topic: function() { + db.getSyncResponse('lloyd@nowhe.re', + { + 'lloyd@nowhe.re': 'fakepubkeyINVALID', + 'lloyd@somewhe.re': 'fakepubkey4' + }, + this.callback); + }, + "by returning them in the key_refresh list": function (err, resp) { + assert.isUndefined(err); + assert.isArray(resp.unknown_emails); + assert.strictEqual(resp.unknown_emails.length, 0); + assert.isArray(resp.key_refresh); + assert.strictEqual(resp.key_refresh.length, 1); + assert.strictEqual(resp.key_refresh[0], 'lloyd@nowhe.re'); + } + }, + "handle more than one case at a time": { + topic: function() { + db.getSyncResponse('lloyd@nowhe.re', + { + 'lloyd@somewhe.re': 'fakepubkeyINVALID', + 'lloyd@anywhe.re': 'notreally' + }, + this.callback); + }, + "when everything is outta sync": function (err, resp) { + assert.isUndefined(err); + assert.isArray(resp.unknown_emails); + assert.strictEqual(resp.unknown_emails.length, 1); + assert.strictEqual(resp.unknown_emails[0], 'lloyd@anywhe.re'); + + assert.isArray(resp.key_refresh); + assert.strictEqual(resp.key_refresh.length, 2); + assert.strictEqual(resp.key_refresh[0], 'lloyd@nowhe.re'); + assert.strictEqual(resp.key_refresh[1], 'lloyd@somewhe.re'); + } } } - } -}); + }); -suite.addBatch({ - "canceling an account": { - topic: function() { - db.cancelAccount("lloyd@somewhe.re", this.callback); - }, - "returns no error": function(r) { - assert.isUndefined(r); - }, - "causes emailKnown": { + suite.addBatch({ + "removing an existing email": { topic: function() { - db.emailKnown('lloyd@somewhe.re', this.callback); + db.removeEmail("lloyd@somewhe.re", "lloyd@nowhe.re", this.callback); }, - "to return false": function (r) { - assert.strictEqual(r, false); + "returns no error": function(r) { + assert.isUndefined(r); + }, + "causes emailKnown": { + topic: function() { + db.emailKnown('lloyd@nowhe.re', this.callback); + }, + "to return false": function (r) { + assert.strictEqual(r, false); + } } } - } -}); + }); -// exports.cancelAccount -// exports.removeEmail + suite.addBatch({ + "canceling an account": { + topic: function() { + db.cancelAccount("lloyd@somewhe.re", this.callback); + }, + "returns no error": function(r) { + assert.isUndefined(r); + }, + "causes emailKnown": { + topic: function() { + db.emailKnown('lloyd@somewhe.re', this.callback); + }, + "to return false": function (r) { + assert.strictEqual(r, false); + } + } + } + }); -suite.addBatch({ - "remove the database file": { - topic: function() { - fs.unlink(db.dbPath, this.callback); - }, - "and unlink should not error": function(err) { - assert.isNull(err); - }, - "and the file": { + suite.addBatch({ + "closing the database": { topic: function() { - path.exists(db.dbPath, this.callback); + db.close(this.callback); }, - "should be missing": function(r) { - assert.isFalse(r); + "should work": function(err) { + assert.isUndefined(err); } } + }); + + if (driver !== 'mysql') { + suite.addBatch({ + "remove the database file": { + topic: function() { + fs.unlink(dbPath, this.callback); + }, + "and unlink should not error": function(err) { + assert.isNull(err); + }, + "and the file": { + topic: function() { + path.exists(dbPath, this.callback); + }, + "should be missing": function(r) { + assert.isFalse(r); + } + } + } + }); + } +} + +// test all available drivers +files = fs.readdirSync(path.join(__dirname, "..", "lib")); + +files.forEach(function(f) { + var m = /^db_(.+)\.js$/.exec(f); + if (m) { + addTestsForDriver(m[1]); } }); // run or export the suite. if (process.argv[1] === __filename) suite.run(); else suite.export(module); + diff --git a/browserid/tests/forgotten-email-test.js b/browserid/tests/forgotten-email-test.js index 1a7d015476b3ec4076256c9fe4f3ace339a597f5..0c67fc92b39d3021a747ed5d9f768bbff77946bf 100755 --- a/browserid/tests/forgotten-email-test.js +++ b/browserid/tests/forgotten-email-test.js @@ -1,13 +1,51 @@ #!/usr/bin/env node +/* ***** 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 ***** */ + const assert = require('assert'), - vows = require('vows'), - start_stop = require('./lib/start-stop.js'), - wsapi = require('./lib/wsapi.js'), - interceptor = require('./lib/email-interceptor.js'); +vows = require('vows'), +start_stop = require('./lib/start-stop.js'), +wsapi = require('./lib/wsapi.js'), +interceptor = require('./lib/email-interceptor.js'); var suite = vows.describe('forgotten-email'); +// disable vows (often flakey?) async error behavior +suite.options.error = false; + start_stop.addStartupBatches(suite); // ever time a new token is sent out, let's update the global diff --git a/browserid/tests/lib/email-interceptor.js b/browserid/tests/lib/email-interceptor.js index 3ac9c7212ae35f6b054f5ff25ba96abc33b3b8c9..ac255fbb12455dce2a3e35dc7d76cb31f5680d58 100644 --- a/browserid/tests/lib/email-interceptor.js +++ b/browserid/tests/lib/email-interceptor.js @@ -1,3 +1,38 @@ +/* ***** 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 ***** */ + // a tiny abstraction which kludges its way into nodemailer to intercept // outbound emails for testing diff --git a/browserid/tests/lib/start-stop.js b/browserid/tests/lib/start-stop.js index 33892996f398707fa5c374a7bccd93ded08ac0fe..636c042a4c8efb27542813eeb1b26691ea6dfcf8 100644 --- a/browserid/tests/lib/start-stop.js +++ b/browserid/tests/lib/start-stop.js @@ -1,3 +1,38 @@ +/* ***** 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 ***** */ + const assert = require('assert'), fs = require('fs'), path = require('path'), diff --git a/browserid/tests/lib/wsapi.js b/browserid/tests/lib/wsapi.js index dc3a9c93c2cdc720ddab5f156984b66af2f0fb15..cd2f08e683093e4b1a49b60c3812e069f009071d 100644 --- a/browserid/tests/lib/wsapi.js +++ b/browserid/tests/lib/wsapi.js @@ -1,3 +1,38 @@ +/* ***** 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 ***** */ + const http = require('http'), querystring = require('querystring'); diff --git a/browserid/tests/registration-status-wsapi-test.js b/browserid/tests/registration-status-wsapi-test.js index 1d45c2f8d2a77985af296894640123c414d8fe4f..da482e9184483981b7fdf9d11e7be6e45e2da287 100755 --- a/browserid/tests/registration-status-wsapi-test.js +++ b/browserid/tests/registration-status-wsapi-test.js @@ -1,5 +1,40 @@ #!/usr/bin/env node +/* ***** 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 ***** */ + const assert = require('assert'), vows = require('vows'), start_stop = require('./lib/start-stop.js'), diff --git a/browserid/views/users.ejs b/browserid/views/users.ejs index dc0a4f6d09346cdf1446f0873d91beefe986b9c7..97b25729dd23968bffd40153b97c3f1000f57e48 100644 --- a/browserid/views/users.ejs +++ b/browserid/views/users.ejs @@ -3,6 +3,6 @@ As a user of BrowserID, you confirm your email addresses once. Then, you can sign into any web site that supports BrowserID with just two clicks. </p> - <center><iframe width="480" height="390" src="http://www.youtube.com/embed/l0t9yDLAmFo" frameborder="0" allowfullscreen></iframe></center> + <center><iframe width="480" height="390" src="https://www.youtube.com/embed/l0t9yDLAmFo" frameborder="0" allowfullscreen></iframe></center> </div> diff --git a/libs/configuration.js b/libs/configuration.js index b3cfa84f70ec3f3773e7f0e9503d555ae30db65d..ecddccb7be0aa2f3e4767d3267531b8eed0c0fc4 100644 --- a/libs/configuration.js +++ b/libs/configuration.js @@ -1,3 +1,38 @@ +/* ***** 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 ***** */ + /* * An abstraction which contains various pre-set deployment * environments and adjusts runtime configuration appropriate for @@ -7,7 +42,9 @@ * exports.configure(app); */ -const substitution = require('./substitute.js'); +const +substitution = require('./substitute.js'), +path = require('path'); var g_config = { }; @@ -19,31 +56,45 @@ exports.get = function(val) { return g_config[val]; } +var defaultHostedDatabaseConfig = { + driver: "mysql", + user: 'browserid', + password: 'browserid' +}; + // various deployment configurations const g_configs = { production: { hostname: 'browserid.org', port: '443', scheme: 'https', - use_minified_resources: true + use_minified_resources: true, + log_path: '/home/browserid/var/', + database: defaultHostedDatabaseConfig }, development: { hostname: 'dev.diresworb.org', port: '443', scheme: 'https', - use_minified_resources: true + use_minified_resources: true, + log_path: '/home/browserid/var/', + database: defaultHostedDatabaseConfig }, beta: { hostname: 'diresworb.org', port: '443', scheme: 'https', - use_minified_resources: true + use_minified_resources: true, + log_path: '/home/browserid/var/', + database: defaultHostedDatabaseConfig }, local: { hostname: '127.0.0.1', port: '10002', scheme: 'http', - use_minified_resources: false + use_minified_resources: false, + log_path: path.join(__dirname, "..", "var", "logs"), + database: { driver: "json" } } }; diff --git a/libs/logging.js b/libs/logging.js new file mode 100644 index 0000000000000000000000000000000000000000..0d4c3422d27fe84732274aad15652f1d75025a4f --- /dev/null +++ b/libs/logging.js @@ -0,0 +1,101 @@ +/* ***** 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 ***** */ + +const +winston = require("winston"), +configuration = require("./configuration"), +path = require('path'), +fs = require('fs'); + +// go through the configuration and determine log location +// for now we only log to one place +// FIXME: separate logs depending on purpose? + +var log_path = configuration.get('log_path'); +var LOGGERS = []; + +// simple inline function for creation of dirs +function mkdir_p(p) { + if (!path.existsSync(p)) { + mkdir_p(path.dirname(p)); + console.log("mkdir", p); + fs.mkdirSync(p, "0755"); + } +} + +function setupLogger(category) { + if (!log_path) + return console.log("no log path! Not logging!"); + else + mkdir_p(log_path); + + + // don't create the logger if it already exists + if (LOGGERS[category]) + return; + + var filename = path.join(log_path, category + "-log.txt"); + + LOGGERS[category] = new (winston.Logger)({ + transports: [new (winston.transports.File)({filename: filename})] + }); +} + +// entry is an object that will get JSON'ified +exports.log = function(category, entry) { + // entry must have at least a type + if (!entry.type) + throw new Error("every log entry needs a type"); + + // setup the logger if need be + setupLogger(category); + + // timestamp + entry.at = new Date().toUTCString(); + + // if no logger, go to console (FIXME: do we really want to log to console?) + LOGGERS[category].info(JSON.stringify(entry)); +}; + +// utility function to log a bunch of stuff at user entry point +exports.userEntry = function(category, req) { + exports.log(category, { + type: 'signin', + browser: req.headers['user-agent'], + rp: req.headers['referer'], + // IP address (this probably needs to be replaced with the X-forwarded-for value + ip: req.connection.remoteAddress + }); +}; \ No newline at end of file diff --git a/libs/substitute.js b/libs/substitute.js index ab04aeec1e4f2ff6a1bb9912b044435cb9583dd2..2c4c526aedbbaac25231c855ebddd27fcd26287b 100644 --- a/libs/substitute.js +++ b/libs/substitute.js @@ -1,3 +1,38 @@ +/* ***** 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 ***** */ + // return a function that is substitution middleware, capable // of being installed to perform textual replacement on // all server output diff --git a/package.json b/package.json index 78fdad42fca3a034200031b76e68b1a78cbccad4..4691170022a0f6cfdb923871eabe62af751055ca 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,6 @@ , "dependencies": { "express": "2.4.3" , "xml2js": "0.1.5" - , "sqlite": "1.0.3" , "nodemailer": "0.1.18" , "mustache": "0.3.1-dev" , "cookie-sessions": "0.0.2" @@ -15,5 +14,9 @@ , "temp": "0.2.0" , "express-csrf": "0.3.2" , "uglify-js": "1.0.6" + , "JSONSelect": "0.2.1" + , "winston" : "0.3.3" + , "connect-cookie-session" : "0.0.1" + , "mysql" : "0.9.1" } -} \ No newline at end of file +} diff --git a/run.js b/run.js index c9e78c4050682ad9cbd1e32674099f9a77810e0a..d710da851074ff0fb3c2d308f8889e182520fe81 100755 --- a/run.js +++ b/run.js @@ -1,5 +1,40 @@ #!/usr/bin/env node +/* ***** 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 ***** */ + // a little node webserver designed to run the unit tests herein var sys = require("sys"), diff --git a/scripts/branch_train.sh b/scripts/branch_train.sh new file mode 100755 index 0000000000000000000000000000000000000000..fe73cf673fa3ea34fc8764f80add229ef1528fcb --- /dev/null +++ b/scripts/branch_train.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +git branch train-$(date +'%Y.%m.%d') dev + diff --git a/test.sh b/test.sh new file mode 100755 index 0000000000000000000000000000000000000000..a0ef1563a8e8f5556edc3d85fce2c29c7936278f --- /dev/null +++ b/test.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +VOWS=`which vows 2> /dev/null` +if [ ! -x "$VOWS" ]; then + echo "vows not found in your path. try: npm install -g vows" + exit 1 +fi + +for file in browserid/tests/*.js ; do + vows $file + if [[ $? != 0 ]] ; then + exit 1 + fi +done diff --git a/verifier/app.js b/verifier/app.js index 2503a31be9d9a96453519c08f6a5a04623e69773..9b8f8e1989ba94e9bbb2187d89e67875bb028d87 100644 --- a/verifier/app.js +++ b/verifier/app.js @@ -1,3 +1,38 @@ +/* ***** 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 ***** */ + const path = require('path'), url = require('url'), fs = require('fs'), @@ -5,6 +40,7 @@ const path = require('path'), idassertion = require('./lib/idassertion.js'), jwt = require('./lib/jwt.js'), express = require('express'); + logging = require('../libs/logging.js'); // create the var directory if it doesn't exist var VAR_DIR = path.join(__dirname, "var"); @@ -35,6 +71,13 @@ function doVerify(req, resp, next) { .verify( audience, function(payload) { + // log it! + logging.log('verifier', { + type: 'verify', + result: 'success', + rp: payload.audience + }); + result = { status : "okay", email : payload.email, @@ -45,11 +88,21 @@ function doVerify(req, resp, next) { resp.json(result); }, function(errorObj) { + logging.log('verifier', { + type: 'verify', + result: 'failure', + rp: audience + }); resp.json({ status: "failure", reason: errorObj }); } ); } catch (e) { console.log(e.stack); + logging.log('verifier', { + type: 'verify', + result: 'failure', + rp: audience + }); resp.json({ status: "failure", reason: e.toString() }); } } diff --git a/verifier/lib/httputils.js b/verifier/lib/httputils.js index ef7abf3130a2dcb24e9cf0058a23c1c3a125e43c..aad0bd98983f83731ed9326b2cd23649c6452c6f 100644 --- a/verifier/lib/httputils.js +++ b/verifier/lib/httputils.js @@ -1,3 +1,38 @@ +/* ***** 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 ***** */ + // various little utilities to make crafting boilerplate responses // simple diff --git a/verifier/lib/idassertion.js b/verifier/lib/idassertion.js index 4845f9805c59eabb1ced138cb65d64fd52061aae..0893488a9d55f99c64f14a438aa32e04aae075dd 100644 --- a/verifier/lib/idassertion.js +++ b/verifier/lib/idassertion.js @@ -1,3 +1,38 @@ +/* ***** 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 ***** */ + /* * bug garage: * diff --git a/verifier/lib/jwt.js b/verifier/lib/jwt.js index fdf96f8c54196cb77a26316d833013d3eed2d512..a70d68ec9bc2b77662fe13fd52189d43ebc17059 100644 --- a/verifier/lib/jwt.js +++ b/verifier/lib/jwt.js @@ -1,3 +1,38 @@ +/* ***** 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 ***** */ + const crypto = require("crypto"); const rsa = require("./rsa.js"); diff --git a/verifier/lib/make_assertion.js b/verifier/lib/make_assertion.js index 54afcdd70069fd4ab2424e7fb0be4dcf508a7a30..38374a5be99d5295471f692f54117ac79cc0d1b7 100644 --- a/verifier/lib/make_assertion.js +++ b/verifier/lib/make_assertion.js @@ -1,5 +1,40 @@ #!/usr/local/bin/node +/* ***** 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 ***** */ + const fs = require("fs"); const jwt = require("./jwt.js"); const idassertion = require("./idassertion.js"); diff --git a/verifier/run.js b/verifier/run.js index 34adb7f8e4bbd9b2765a0ed395c545166a883b30..1ab4c936e1273f6f92016975af1f1283eff166ce 100755 --- a/verifier/run.js +++ b/verifier/run.js @@ -1,5 +1,40 @@ #!/usr/bin/env node +/* ***** 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 ***** */ + var sys = require("sys"), path = require("path"), fs = require("fs"), diff --git a/verifier/tests/run.js b/verifier/tests/run.js index c7c457c5c52b87f9340ac049ddd54b035a053ade..8e6d85ab85c31406b4310e0edabfa5a92960c66b 100755 --- a/verifier/tests/run.js +++ b/verifier/tests/run.js @@ -1,5 +1,40 @@ #!/usr/local/bin/node +/* ***** 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 ***** */ + const jwt = require("../lib/jwt.js"); const idassertion = require("../lib/idassertion.js"); const vows = require('vows');