mirror of
https://github.com/moparisthebest/mail
synced 2024-12-23 15:58:49 -05:00
Merge branch 'dev/verification'
This commit is contained in:
commit
df39ff41a8
@ -1,8 +1,6 @@
|
|||||||
language: node_js
|
language: node_js
|
||||||
node_js:
|
node_js:
|
||||||
- "0.11"
|
|
||||||
- "0.10"
|
- "0.10"
|
||||||
- "0.8"
|
|
||||||
before_install:
|
before_install:
|
||||||
- gem install sass
|
- gem install sass
|
||||||
- npm install -g grunt-cli
|
- npm install -g grunt-cli
|
||||||
|
@ -44,5 +44,14 @@ define([], function() {
|
|||||||
webSite: 'http://whiteout.io'
|
webSite: 'http://whiteout.io'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contants are maintained here.
|
||||||
|
*/
|
||||||
|
app.constants = {
|
||||||
|
verificationSubject: 'New public key uploaded',
|
||||||
|
verificationUrlPrefix: 'https://keys.whiteout.io/verify/',
|
||||||
|
verificationUuidLength: 36
|
||||||
|
};
|
||||||
|
|
||||||
return app;
|
return app;
|
||||||
});
|
});
|
@ -3,7 +3,8 @@ define(function(require) {
|
|||||||
|
|
||||||
var _ = require('underscore'),
|
var _ = require('underscore'),
|
||||||
util = require('cryptoLib/util'),
|
util = require('cryptoLib/util'),
|
||||||
str = require('js/app-config').string;
|
str = require('js/app-config').string,
|
||||||
|
consts = require('js/app-config').constants;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A high-level Data-Access Api for handling Email synchronization
|
* A high-level Data-Access Api for handling Email synchronization
|
||||||
@ -208,7 +209,7 @@ define(function(require) {
|
|||||||
*/
|
*/
|
||||||
EmailDAO.prototype.listMessages = function(options, callback) {
|
EmailDAO.prototype.listMessages = function(options, callback) {
|
||||||
var self = this,
|
var self = this,
|
||||||
encryptedList = [];
|
cleartextList = [];
|
||||||
|
|
||||||
// validate options
|
// validate options
|
||||||
if (!options.folder) {
|
if (!options.folder) {
|
||||||
@ -227,63 +228,124 @@ define(function(require) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// find encrypted items
|
if (emails.length === 0) {
|
||||||
emails.forEach(function(i) {
|
callback(null, cleartextList);
|
||||||
if (typeof i.body === 'string' && i.body.indexOf(str.cryptPrefix) !== -1 && i.body.indexOf(str.cryptSuffix) !== -1) {
|
|
||||||
// parse ct object from ascii armored message block
|
|
||||||
encryptedList.push(parseMessageBlock(i));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (encryptedList.length === 0) {
|
|
||||||
callback(null, []);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// decrypt items
|
var after = _.after(emails.length, function() {
|
||||||
decryptList(encryptedList, callback);
|
callback(null, cleartextList);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
_.each(emails, function(email) {
|
||||||
|
handleMail(email, after);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleMail(email, localCallback) {
|
||||||
|
// remove subject filter prefix
|
||||||
|
email.subject = email.subject.split(str.subjectPrefix)[1];
|
||||||
|
|
||||||
|
// encrypted mail
|
||||||
|
if (isPGPMail(email)) {
|
||||||
|
email = parseMessageBlock(email);
|
||||||
|
decrypt(email, localCallback);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// verification mail
|
||||||
|
if (isVerificationMail(email)) {
|
||||||
|
verify(email, localCallback);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleartext mail
|
||||||
|
cleartextList.push(email);
|
||||||
|
localCallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPGPMail(email) {
|
||||||
|
return typeof email.body === 'string' && email.body.indexOf(str.cryptPrefix) !== -1 && email.body.indexOf(str.cryptSuffix) !== -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isVerificationMail(email) {
|
||||||
|
return email.subject === consts.verificationSubject;
|
||||||
|
}
|
||||||
|
|
||||||
function parseMessageBlock(email) {
|
function parseMessageBlock(email) {
|
||||||
var messageBlock;
|
var messageBlock;
|
||||||
|
|
||||||
// parse email body for encrypted message block
|
// parse email body for encrypted message block
|
||||||
try {
|
|
||||||
// get ascii armored message block by prefix and suffix
|
// get ascii armored message block by prefix and suffix
|
||||||
messageBlock = email.body.split(str.cryptPrefix)[1].split(str.cryptSuffix)[0];
|
messageBlock = email.body.split(str.cryptPrefix)[1].split(str.cryptSuffix)[0];
|
||||||
// add prefix and suffix again
|
// add prefix and suffix again
|
||||||
email.body = str.cryptPrefix + messageBlock + str.cryptSuffix;
|
email.body = str.cryptPrefix + messageBlock + str.cryptSuffix;
|
||||||
email.subject = email.subject.split(str.subjectPrefix)[1];
|
|
||||||
} catch (e) {
|
|
||||||
callback({
|
|
||||||
errMsg: 'Error parsing encrypted message block!'
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
return email;
|
return email;
|
||||||
}
|
}
|
||||||
|
|
||||||
function decryptList(list, callback) {
|
function decrypt(email, localCallback) {
|
||||||
var after = _.after(list.length, function() {
|
// fetch public key required to verify signatures
|
||||||
callback(null, list);
|
var sender = email.from[0].address;
|
||||||
});
|
|
||||||
|
|
||||||
list.forEach(function(i) {
|
|
||||||
// gather public keys required to verify signatures
|
|
||||||
var sender = i.from[0].address;
|
|
||||||
self._keychain.getReceiverPublicKey(sender, function(err, senderPubkey) {
|
self._keychain.getReceiverPublicKey(sender, function(err, senderPubkey) {
|
||||||
|
|
||||||
// decrypt and verfiy signatures
|
|
||||||
self._crypto.decrypt(i.body, senderPubkey.publicKey, function(err, decrypted) {
|
|
||||||
if (err) {
|
if (err) {
|
||||||
callback(err);
|
callback(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
i.body = decrypted;
|
// decrypt and verfiy signatures
|
||||||
|
self._crypto.decrypt(email.body, senderPubkey.publicKey, function(err, decrypted) {
|
||||||
|
if (err) {
|
||||||
|
callback(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
after();
|
email.body = decrypted;
|
||||||
|
cleartextList.push(email);
|
||||||
|
localCallback();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function verify(email, localCallback) {
|
||||||
|
var uuid, index;
|
||||||
|
|
||||||
|
if (!email.unread) {
|
||||||
|
// don't bother if the email was already marked as read
|
||||||
|
localCallback();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
index = email.body.indexOf(consts.verificationUrlPrefix);
|
||||||
|
if (index === -1) {
|
||||||
|
localCallback();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uuid = email.body.substr(index + consts.verificationUrlPrefix.length, consts.verificationUuidLength);
|
||||||
|
self._keychain.verifyPublicKey(uuid, function(err) {
|
||||||
|
if (err) {
|
||||||
|
console.error('Unable to verify public key: ' + err.errMsg);
|
||||||
|
localCallback();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// public key has been verified, mark the message as read, delete it, and ignore it in the future
|
||||||
|
self.imapMarkMessageRead({
|
||||||
|
folder: options.folder,
|
||||||
|
uid: email.uid
|
||||||
|
}, function(err) {
|
||||||
|
if (err) {
|
||||||
|
// if marking the mail as read failed, don't bother
|
||||||
|
localCallback();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.imapDeleteMessage({
|
||||||
|
folder: options.folder,
|
||||||
|
uid: email.uid
|
||||||
|
}, function() {
|
||||||
|
localCallback();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -363,11 +425,6 @@ define(function(require) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof message.body !== 'string' || message.body.indexOf(str.cryptPrefix) === -1 || message.body.indexOf(str.cryptSuffix) === -1) {
|
|
||||||
after();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// set gotten attributes like body to message object containing list meta data like 'unread' or 'replied'
|
// set gotten attributes like body to message object containing list meta data like 'unread' or 'replied'
|
||||||
header.id = message.id;
|
header.id = message.id;
|
||||||
header.body = message.body;
|
header.body = message.body;
|
||||||
|
@ -12,6 +12,15 @@ define(function(require) {
|
|||||||
this._publicKeyDao = publicKeyDao;
|
this._publicKeyDao = publicKeyDao;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies the public key of a user o nthe public key store
|
||||||
|
* @param {String} uuid The uuid to verify the key
|
||||||
|
* @param {Function} callback(error) Callback with an optional error object when the verification is done. If the was an error, the error object contains the information for it.
|
||||||
|
*/
|
||||||
|
KeychainDAO.prototype.verifyPublicKey = function(uuid, callback) {
|
||||||
|
this._publicKeyDao.verify(uuid, callback);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an array of public keys by looking in local storage and
|
* Get an array of public keys by looking in local storage and
|
||||||
* fetching missing keys from the cloud service.
|
* fetching missing keys from the cloud service.
|
||||||
|
@ -5,13 +5,34 @@ define(function() {
|
|||||||
this._restDao = restDao;
|
this._restDao = restDao;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify the public key behind the given uuid
|
||||||
|
*/
|
||||||
|
PublicKeyDAO.prototype.verify = function(uuid, callback) {
|
||||||
|
var uri = '/verify/' + uuid;
|
||||||
|
|
||||||
|
this._restDao.get({
|
||||||
|
uri: uri,
|
||||||
|
type: 'text'
|
||||||
|
}, function(err) {
|
||||||
|
if (err) {
|
||||||
|
callback(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the user's corresponding public key
|
* Find the user's corresponding public key
|
||||||
*/
|
*/
|
||||||
PublicKeyDAO.prototype.get = function(keyId, callback) {
|
PublicKeyDAO.prototype.get = function(keyId, callback) {
|
||||||
var uri = '/publickey/key/' + keyId;
|
var uri = '/publickey/key/' + keyId;
|
||||||
|
|
||||||
this._restDao.get(uri, function(err, key) {
|
this._restDao.get({
|
||||||
|
uri: uri
|
||||||
|
}, function(err, key) {
|
||||||
if (err) {
|
if (err) {
|
||||||
callback(err);
|
callback(err);
|
||||||
return;
|
return;
|
||||||
@ -34,7 +55,9 @@ define(function() {
|
|||||||
PublicKeyDAO.prototype.getByUserId = function(userId, callback) {
|
PublicKeyDAO.prototype.getByUserId = function(userId, callback) {
|
||||||
var uri = '/publickey/user/' + userId;
|
var uri = '/publickey/user/' + userId;
|
||||||
|
|
||||||
this._restDao.get(uri, function(err, keys) {
|
this._restDao.get({
|
||||||
|
uri: uri
|
||||||
|
}, function(err, keys) {
|
||||||
// not found
|
// not found
|
||||||
if (err && err.code === 404) {
|
if (err && err.code === 404) {
|
||||||
callback();
|
callback();
|
||||||
|
@ -14,14 +14,42 @@ define(function(require) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* GET (read) request
|
* GET (read) request
|
||||||
|
* @param {String} options.uri URI relative to the base uri to perform the GET request with.
|
||||||
|
* @param {String} options.type (optional) The type of data that you're expecting back from the server: json, xml, text. Default: json.
|
||||||
*/
|
*/
|
||||||
RestDAO.prototype.get = function(uri, callback) {
|
RestDAO.prototype.get = function(options, callback) {
|
||||||
|
var acceptHeader;
|
||||||
|
|
||||||
|
if (typeof options.uri === 'undefined') {
|
||||||
|
callback({
|
||||||
|
code: 400,
|
||||||
|
errMsg: 'Bad Request! URI is a mandatory parameter.'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
options.type = options.type || 'json';
|
||||||
|
|
||||||
|
if (options.type === 'json') {
|
||||||
|
acceptHeader = 'application/json';
|
||||||
|
} else if (options.type === 'xml') {
|
||||||
|
acceptHeader = 'application/xml';
|
||||||
|
} else if (options.type === 'text') {
|
||||||
|
acceptHeader = 'text/plain';
|
||||||
|
} else {
|
||||||
|
callback({
|
||||||
|
code: 400,
|
||||||
|
errMsg: 'Bad Request! Unhandled data type.'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: this._baseUri + uri,
|
url: this._baseUri + options.uri,
|
||||||
type: 'GET',
|
type: 'GET',
|
||||||
dataType: 'json',
|
dataType: options.type,
|
||||||
headers: {
|
headers: {
|
||||||
'Accept': 'application/json',
|
'Accept': acceptHeader
|
||||||
},
|
},
|
||||||
success: function(res) {
|
success: function(res) {
|
||||||
callback(null, res);
|
callback(null, res);
|
||||||
|
@ -16,7 +16,7 @@ define(function(require) {
|
|||||||
asymKeySize: 512
|
asymKeySize: 512
|
||||||
};
|
};
|
||||||
|
|
||||||
var dummyMail;
|
var dummyMail, verificationMail, plaintextMail;
|
||||||
|
|
||||||
var publicKey = "-----BEGIN PUBLIC KEY-----\r\n" + "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxy+Te5dyeWd7g0P+8LNO7fZDQ\r\n" + "g96xTb1J6pYE/pPTMlqhB6BRItIYjZ1US5q2vk5Zk/5KasBHAc9RbCqvh9v4XFEY\r\n" + "JVmTXC4p8ft1LYuNWIaDk+R3dyYXmRNct/JC4tks2+8fD3aOvpt0WNn3R75/FGBt\r\n" + "h4BgojAXDE+PRQtcVQIDAQAB\r\n" + "-----END PUBLIC KEY-----";
|
var publicKey = "-----BEGIN PUBLIC KEY-----\r\n" + "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxy+Te5dyeWd7g0P+8LNO7fZDQ\r\n" + "g96xTb1J6pYE/pPTMlqhB6BRItIYjZ1US5q2vk5Zk/5KasBHAc9RbCqvh9v4XFEY\r\n" + "JVmTXC4p8ft1LYuNWIaDk+R3dyYXmRNct/JC4tks2+8fD3aOvpt0WNn3R75/FGBt\r\n" + "h4BgojAXDE+PRQtcVQIDAQAB\r\n" + "-----END PUBLIC KEY-----";
|
||||||
|
|
||||||
@ -37,6 +37,28 @@ define(function(require) {
|
|||||||
}], // list of receivers
|
}], // list of receivers
|
||||||
subject: "[whiteout] Hello", // Subject line
|
subject: "[whiteout] Hello", // Subject line
|
||||||
body: "Hello world" // plaintext body
|
body: "Hello world" // plaintext body
|
||||||
|
}, verificationMail = {
|
||||||
|
from: [{
|
||||||
|
name: 'Whiteout Test',
|
||||||
|
address: 'whiteout.test@t-online.de'
|
||||||
|
}], // sender address
|
||||||
|
to: [{
|
||||||
|
address: 'safewithme.testuser@gmail.com'
|
||||||
|
}], // list of receivers
|
||||||
|
subject: "[whiteout] New public key uploaded", // Subject line
|
||||||
|
body: "https://keys.whiteout.io/verify/OMFG_FUCKING_BASTARD_UUID_FROM_HELL!", // plaintext body
|
||||||
|
unread: true
|
||||||
|
}, plaintextMail = {
|
||||||
|
from: [{
|
||||||
|
name: 'Whiteout Test',
|
||||||
|
address: 'whiteout.test@t-online.de'
|
||||||
|
}], // sender address
|
||||||
|
to: [{
|
||||||
|
address: 'safewithme.testuser@gmail.com'
|
||||||
|
}], // list of receivers
|
||||||
|
subject: "OMG SO PLAIN TEXT", // Subject line
|
||||||
|
body: "yo dawg, we be all plaintext and stuff...", // plaintext body
|
||||||
|
unread: true
|
||||||
};
|
};
|
||||||
|
|
||||||
account = {
|
account = {
|
||||||
@ -578,7 +600,7 @@ define(function(require) {
|
|||||||
describe('IMAP: list messages from local storage', function() {
|
describe('IMAP: list messages from local storage', function() {
|
||||||
it('should work', function(done) {
|
it('should work', function(done) {
|
||||||
dummyMail.body = app.string.cryptPrefix + btoa('asdf') + app.string.cryptSuffix;
|
dummyMail.body = app.string.cryptPrefix + btoa('asdf') + app.string.cryptSuffix;
|
||||||
devicestorageStub.listItems.yields(null, [dummyMail, dummyMail]);
|
devicestorageStub.listItems.yields(null, [dummyMail]);
|
||||||
keychainStub.getReceiverPublicKey.yields(null, {
|
keychainStub.getReceiverPublicKey.yields(null, {
|
||||||
_id: "fcf8b4aa-5d09-4089-8b4f-e3bc5091daf3",
|
_id: "fcf8b4aa-5d09-4089-8b4f-e3bc5091daf3",
|
||||||
userId: "safewithme.testuser@gmail.com",
|
userId: "safewithme.testuser@gmail.com",
|
||||||
@ -589,13 +611,124 @@ define(function(require) {
|
|||||||
emailDao.listMessages({
|
emailDao.listMessages({
|
||||||
folder: 'INBOX',
|
folder: 'INBOX',
|
||||||
offset: 0,
|
offset: 0,
|
||||||
num: 2
|
num: 1
|
||||||
}, function(err, emails) {
|
}, function(err, emails) {
|
||||||
expect(err).to.not.exist;
|
expect(err).to.not.exist;
|
||||||
expect(devicestorageStub.listItems.calledOnce).to.be.true;
|
expect(devicestorageStub.listItems.calledOnce).to.be.true;
|
||||||
expect(keychainStub.getReceiverPublicKey.calledTwice).to.be.true;
|
expect(keychainStub.getReceiverPublicKey.calledOnce).to.be.true;
|
||||||
expect(pgpStub.decrypt.calledTwice).to.be.true;
|
expect(pgpStub.decrypt.calledOnce).to.be.true;
|
||||||
expect(emails.length).to.equal(2);
|
expect(emails.length).to.equal(1);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Plain text', function() {
|
||||||
|
it('should display plaintext mails with [whiteout] prefix', function(done) {
|
||||||
|
devicestorageStub.listItems.yields(null, [plaintextMail]);
|
||||||
|
|
||||||
|
emailDao.listMessages({
|
||||||
|
folder: 'INBOX',
|
||||||
|
offset: 0,
|
||||||
|
num: 1
|
||||||
|
}, function(err, emails) {
|
||||||
|
expect(err).to.not.exist;
|
||||||
|
expect(emails.length).to.equal(1);
|
||||||
|
expect(devicestorageStub.listItems.calledOnce).to.be.true;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Verification', function() {
|
||||||
|
it('should verify pending public keys', function(done) {
|
||||||
|
devicestorageStub.listItems.yields(null, [verificationMail]);
|
||||||
|
keychainStub.verifyPublicKey.yields();
|
||||||
|
imapClientStub.updateFlags.yields();
|
||||||
|
imapClientStub.deleteMessage.yields();
|
||||||
|
devicestorageStub.removeList.yields();
|
||||||
|
|
||||||
|
emailDao.listMessages({
|
||||||
|
folder: 'INBOX',
|
||||||
|
offset: 0,
|
||||||
|
num: 1
|
||||||
|
}, function(err, emails) {
|
||||||
|
expect(err).to.not.exist;
|
||||||
|
expect(emails.length).to.equal(0);
|
||||||
|
expect(devicestorageStub.listItems.calledOnce).to.be.true;
|
||||||
|
expect(keychainStub.verifyPublicKey.calledOnce).to.be.true;
|
||||||
|
expect(imapClientStub.updateFlags.calledOnce).to.be.true;
|
||||||
|
expect(imapClientStub.deleteMessage.calledOnce).to.be.true;
|
||||||
|
expect(devicestorageStub.removeList.calledOnce).to.be.true;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not verify pending public keys if the mail was read', function(done) {
|
||||||
|
verificationMail.unread = false;
|
||||||
|
devicestorageStub.listItems.yields(null, [verificationMail]);
|
||||||
|
|
||||||
|
emailDao.listMessages({
|
||||||
|
folder: 'INBOX',
|
||||||
|
offset: 0,
|
||||||
|
num: 1
|
||||||
|
}, function(err) {
|
||||||
|
expect(err).to.not.exist;
|
||||||
|
expect(devicestorageStub.listItems.calledOnce).to.be.true;
|
||||||
|
verificationMail.unread = true;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not verify pending public keys if the mail contains erroneous links', function(done) {
|
||||||
|
var properBody = verificationMail.body;
|
||||||
|
verificationMail.body = 'UGA UGA!';
|
||||||
|
devicestorageStub.listItems.yields(null, [verificationMail]);
|
||||||
|
|
||||||
|
emailDao.listMessages({
|
||||||
|
folder: 'INBOX',
|
||||||
|
offset: 0,
|
||||||
|
num: 1
|
||||||
|
}, function(err) {
|
||||||
|
expect(err).to.not.exist;
|
||||||
|
expect(devicestorageStub.listItems.calledOnce).to.be.true;
|
||||||
|
verificationMail.body = properBody;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should not mark verification mails read if verification fails', function(done) {
|
||||||
|
devicestorageStub.listItems.yields(null, [verificationMail]);
|
||||||
|
keychainStub.verifyPublicKey.yields({
|
||||||
|
errMsg: 'snafu.'
|
||||||
|
});
|
||||||
|
|
||||||
|
emailDao.listMessages({
|
||||||
|
folder: 'INBOX',
|
||||||
|
offset: 0,
|
||||||
|
num: 1
|
||||||
|
}, function(err) {
|
||||||
|
expect(err).to.not.exist;
|
||||||
|
expect(devicestorageStub.listItems.calledOnce).to.be.true;
|
||||||
|
expect(keychainStub.verifyPublicKey.calledOnce).to.be.true;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should not delete verification mails read if marking read fails', function(done) {
|
||||||
|
devicestorageStub.listItems.yields(null, [verificationMail]);
|
||||||
|
keychainStub.verifyPublicKey.yields();
|
||||||
|
imapClientStub.updateFlags.yields({
|
||||||
|
errMsg: 'snafu.'
|
||||||
|
});
|
||||||
|
|
||||||
|
emailDao.listMessages({
|
||||||
|
folder: 'INBOX',
|
||||||
|
offset: 0,
|
||||||
|
num: 1
|
||||||
|
}, function(err) {
|
||||||
|
expect(err).to.not.exist;
|
||||||
|
expect(devicestorageStub.listItems.calledOnce).to.be.true;
|
||||||
|
expect(keychainStub.verifyPublicKey.calledOnce).to.be.true;
|
||||||
|
expect(imapClientStub.updateFlags.calledOnce).to.be.true;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -20,6 +20,18 @@ define(function(require) {
|
|||||||
|
|
||||||
afterEach(function() {});
|
afterEach(function() {});
|
||||||
|
|
||||||
|
describe('verify public key', function() {
|
||||||
|
it('should verify public key', function(done) {
|
||||||
|
var uuid = 'asdfasdfasdfasdf';
|
||||||
|
pubkeyDaoStub.verify.yields();
|
||||||
|
|
||||||
|
keychainDao.verifyPublicKey(uuid, function() {
|
||||||
|
expect(pubkeyDaoStub.verify.calledWith(uuid)).to.be.true;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('lookup public key', function() {
|
describe('lookup public key', function() {
|
||||||
it('should fail', function(done) {
|
it('should fail', function(done) {
|
||||||
keychainDao.lookupPublicKey(undefined, function(err, key) {
|
keychainDao.lookupPublicKey(undefined, function(err, key) {
|
||||||
|
@ -45,6 +45,30 @@ define(function(require) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('verify', function() {
|
||||||
|
it('should fail', function(done) {
|
||||||
|
restDaoStub.get.yields(42);
|
||||||
|
|
||||||
|
pubkeyDao.get('id', function(err) {
|
||||||
|
expect(err).to.exist;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work', function(done) {
|
||||||
|
var uuid = 'c621e328-8548-40a1-8309-adf1955e98a9';
|
||||||
|
restDaoStub.get.yields(null);
|
||||||
|
|
||||||
|
pubkeyDao.verify(uuid, function(err) {
|
||||||
|
expect(err).to.not.exist;
|
||||||
|
expect(restDaoStub.get.calledWith(sinon.match(function(arg){
|
||||||
|
return arg.uri === '/verify/' + uuid && arg.type === 'text';
|
||||||
|
}))).to.be.true;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('get by userId', function() {
|
describe('get by userId', function() {
|
||||||
it('should fail', function(done) {
|
it('should fail', function(done) {
|
||||||
restDaoStub.get.yields(42);
|
restDaoStub.get.yields(42);
|
||||||
|
@ -39,7 +39,100 @@ define(function(require) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('get', function() {
|
describe('get', function() {
|
||||||
it('should fail', function(done) {
|
it('should work with json as default type', function(done) {
|
||||||
|
$.ajax.restore();
|
||||||
|
var spy = sinon.stub($, 'ajax').yieldsTo('success', {
|
||||||
|
foo: 'bar'
|
||||||
|
});
|
||||||
|
|
||||||
|
restDao.get({
|
||||||
|
uri: '/asdf',
|
||||||
|
type: 'json'
|
||||||
|
}, function(err, data) {
|
||||||
|
expect(err).to.not.exist;
|
||||||
|
expect(data.foo).to.equal('bar');
|
||||||
|
expect(spy.calledWith(sinon.match(function(request) {
|
||||||
|
return request.headers.Accept === 'application/json' && request.dataType === 'json';
|
||||||
|
}))).to.be.true;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work with json', function(done) {
|
||||||
|
$.ajax.restore();
|
||||||
|
var spy = sinon.stub($, 'ajax').yieldsTo('success', {
|
||||||
|
foo: 'bar'
|
||||||
|
});
|
||||||
|
|
||||||
|
restDao.get({
|
||||||
|
uri: '/asdf',
|
||||||
|
type: 'json'
|
||||||
|
}, function(err, data) {
|
||||||
|
expect(err).to.not.exist;
|
||||||
|
expect(data.foo).to.equal('bar');
|
||||||
|
expect(spy.calledWith(sinon.match(function(request) {
|
||||||
|
return request.headers.Accept === 'application/json' && request.dataType === 'json';
|
||||||
|
}))).to.be.true;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work with plain text', function(done) {
|
||||||
|
$.ajax.restore();
|
||||||
|
var spy = sinon.stub($, 'ajax').yieldsTo('success', 'foobar!');
|
||||||
|
|
||||||
|
restDao.get({
|
||||||
|
uri: '/asdf',
|
||||||
|
type: 'text'
|
||||||
|
}, function(err, data) {
|
||||||
|
expect(err).to.not.exist;
|
||||||
|
expect(data).to.equal('foobar!');
|
||||||
|
expect(spy.calledWith(sinon.match(function(request) {
|
||||||
|
return request.headers.Accept === 'text/plain' && request.dataType === 'text';
|
||||||
|
}))).to.be.true;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work with xml', function(done) {
|
||||||
|
$.ajax.restore();
|
||||||
|
var spy = sinon.stub($, 'ajax').yieldsTo('success', '<foo>bar</foo>');
|
||||||
|
|
||||||
|
restDao.get({
|
||||||
|
uri: '/asdf',
|
||||||
|
type: 'xml'
|
||||||
|
}, function(err, data) {
|
||||||
|
expect(err).to.not.exist;
|
||||||
|
expect(data).to.equal('<foo>bar</foo>'); // that's probably not right, but in the unit test, it is :)
|
||||||
|
expect(spy.calledWith(sinon.match(function(request) {
|
||||||
|
return request.headers.Accept === 'application/xml' && request.dataType === 'xml';
|
||||||
|
}))).to.be.true;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail for missing uri parameter', function(done) {
|
||||||
|
restDao.get({}, function(err, data) {
|
||||||
|
expect(err).to.exist;
|
||||||
|
expect(err.code).to.equal(400);
|
||||||
|
expect(data).to.not.exist;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail for unhandled data type', function(done) {
|
||||||
|
restDao.get({
|
||||||
|
uri: '/asdf',
|
||||||
|
type: 'snafu'
|
||||||
|
}, function(err, data) {
|
||||||
|
expect(err).to.exist;
|
||||||
|
expect(err.code).to.equal(400);
|
||||||
|
expect(data).to.not.exist;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail for server error', function(done) {
|
||||||
$.ajax.restore();
|
$.ajax.restore();
|
||||||
sinon.stub($, 'ajax').yieldsTo('error', {
|
sinon.stub($, 'ajax').yieldsTo('error', {
|
||||||
status: 500
|
status: 500
|
||||||
@ -47,21 +140,15 @@ define(function(require) {
|
|||||||
statusText: 'Internal error'
|
statusText: 'Internal error'
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
restDao.get('/asdf', function(err, data) {
|
restDao.get({
|
||||||
|
uri: '/asdf'
|
||||||
|
}, function(err, data) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
expect(err.code).to.equal(500);
|
expect(err.code).to.equal(500);
|
||||||
expect(data).to.not.exist;
|
expect(data).to.not.exist;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work', function(done) {
|
|
||||||
restDao.get('/asdf', function(err, data) {
|
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(data.foo).to.equal('bar');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('put', function() {
|
describe('put', function() {
|
||||||
|
Loading…
Reference in New Issue
Block a user