From d75055a91e3e2583f45f2194f7daea5b387491fe Mon Sep 17 00:00:00 2001 From: Lloyd Hilaiel <lloyd@hilaiel.com> Date: Wed, 20 Jul 2011 15:08:22 -0600 Subject: [PATCH] basic testing of all db.js apis complete, also completed implementation of identitysync to check installed pubkeys. --- browserid/lib/db.js | 30 +++++-- browserid/tests/db-test.js | 166 +++++++++++++++++++++++++++++++++++-- 2 files changed, 184 insertions(+), 12 deletions(-) diff --git a/browserid/lib/db.js b/browserid/lib/db.js index de5548e2c..955d8da77 100644 --- a/browserid/lib/db.js +++ b/browserid/lib/db.js @@ -272,6 +272,15 @@ exports.checkAuth = function(email, cb) { }); }; +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. @@ -302,23 +311,34 @@ exports.getSyncResponse = function(email, identities, cb) { 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 - // XXX todo - - cb(undefined, respBody); + // #3 -- yes, this is sub-optimal in terms of performance. when we + // move away from public keys this will be unnec. + 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); + } + }); + }); } }); }); @@ -339,8 +359,6 @@ exports.pubkeysForEmail = function(identity, cb) { }); }; - -// FIXME: I'm not sure I'm using this data model properly exports.removeEmail = function(authenticated_email, email, cb) { // figure out the user, and remove Email only from addressed // linked to the authenticated email address diff --git a/browserid/tests/db-test.js b/browserid/tests/db-test.js index 45e19f3eb..bd7afd914 100755 --- a/browserid/tests/db-test.js +++ b/browserid/tests/db-test.js @@ -53,7 +53,7 @@ suite.addBatch({ topic: function() { return secret = db.stageUser({ email: 'lloyd@nowhe.re', - pubkey: 'fakepublickey', + pubkey: 'fakepubkey', hash: 'fakepasswordhash' }); }, @@ -116,7 +116,7 @@ suite.addBatch({ suite.addBatch({ "adding keys to email": { topic: function() { - db.addKeyToEmail('lloyd@nowhe.re', 'lloyd@nowhe.re', 'fakepublickey2', this.callback); + db.addKeyToEmail('lloyd@nowhe.re', 'lloyd@nowhe.re', 'fakepubkey2', this.callback); }, "works": function(r) { assert.isUndefined(r); @@ -127,7 +127,7 @@ suite.addBatch({ suite.addBatch({ "adding multiple keys to email": { topic: function() { - db.addKeyToEmail('lloyd@nowhe.re', 'lloyd@nowhe.re', 'fakepublickey3', this.callback); + db.addKeyToEmail('lloyd@nowhe.re', 'lloyd@nowhe.re', 'fakepubkey3', this.callback); }, "works too": function(r) { assert.isUndefined(r); @@ -190,12 +190,166 @@ suite.addBatch({ } }); -// XXX: remaining APIs to test -// exports.cancelAccount // 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); + } + } + } +}); + // 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); + } + }, + "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({ + "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); + }, + "to return false": function (r) { + assert.strictEqual(r, false); + } + } + } +}); + +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); + } + } + } +}); + +// exports.cancelAccount // exports.removeEmail -// exports.stageEmail suite.addBatch({ "remove the database file": { -- GitLab