[WO-851] Request OAuth token on each connection request

This commit is contained in:
Felix Hammerl 2015-01-22 12:07:14 +01:00 committed by Tankred Hase
parent 86653e8700
commit 0446f8219b
7 changed files with 222 additions and 221 deletions

View File

@ -4,12 +4,9 @@ var ngModule = angular.module('woEmail');
ngModule.service('account', Account);
module.exports = Account;
var axe = require('axe-logger'),
util = require('crypto-lib').util,
PgpMailer = require('pgpmailer'),
ImapClient = require('imap-client');
var util = require('crypto-lib').util;
function Account(appConfig, auth, accountStore, email, outbox, keychain, updateHandler, pgpbuilder, dialog) {
function Account(appConfig, auth, accountStore, email, outbox, keychain, updateHandler, dialog) {
this._appConfig = appConfig;
this._auth = auth;
this._accountStore = accountStore;
@ -17,7 +14,6 @@ function Account(appConfig, auth, accountStore, email, outbox, keychain, updateH
this._outbox = outbox;
this._keychain = keychain;
this._updateHandler = updateHandler;
this._pgpbuilder = pgpbuilder;
this._dialog = dialog;
this._accounts = []; // init accounts list
}
@ -102,68 +98,16 @@ Account.prototype.init = function(options) {
});
};
/**
* Check if the user agent is online.
*/
Account.prototype.isOnline = function() {
return navigator.onLine;
};
/**
* Event that is called when the user agent goes online. This create new instances of the imap-client and pgp-mailer and connects to the mail server.
*/
Account.prototype.onConnect = function(callback) {
var self = this;
var config = self._appConfig.config;
callback = callback || self._dialog.error;
if (!self.isOnline() || !self._emailDao || !self._emailDao._account) {
if (!this._emailDao || !this._emailDao._account) {
// prevent connection infinite loop
return;
}
// init imap/smtp clients
self._auth.getCredentials().then(function(credentials) {
// add the maximum update batch size for imap folders to the imap configuration
credentials.imap.maxUpdateSize = config.imapUpdateBatchSize;
// tls socket worker path for multithreaded tls in non-native tls environments
credentials.imap.tlsWorkerPath = credentials.smtp.tlsWorkerPath = config.workerPath + '/tcp-socket-tls-worker.min.js';
var pgpMailer = new PgpMailer(credentials.smtp, self._pgpbuilder);
var imapClient = new ImapClient(credentials.imap);
imapClient.onError = onConnectionError;
pgpMailer.onError = onConnectionError;
// certificate update handling
imapClient.onCert = self._auth.handleCertificateUpdate.bind(self._auth, 'imap', self.onConnect.bind(self), self._dialog.error);
pgpMailer.onCert = self._auth.handleCertificateUpdate.bind(self._auth, 'smtp', self.onConnect.bind(self), self._dialog.error);
// connect to clients
return self._emailDao.onConnect({
imapClient: imapClient,
pgpMailer: pgpMailer,
ignoreUploadOnSent: self._emailDao.checkIgnoreUploadOnSent(credentials.imap.host)
});
}).then(callback).catch(callback);
function onConnectionError(error) {
axe.debug('Connection error. Attempting reconnect in ' + config.reconnectInterval + ' ms. Error: ' + (error.errMsg || error.message) + (error.stack ? ('\n' + error.stack) : ''));
setTimeout(function() {
axe.debug('Reconnecting...');
// re-init client modules on error
self.onConnect(function(err) {
if (err) {
axe.error('Reconnect attempt failed! ' + (err.errMsg || err.message) + (err.stack ? ('\n' + err.stack) : ''));
return;
}
axe.debug('Reconnect attempt complete.');
});
}, config.reconnectInterval);
}
this._emailDao.onConnect().then(callback).catch(callback);
};
/**

View File

@ -5,7 +5,10 @@ ngModule.service('email', Email);
module.exports = Email;
var config = require('../app-config').config,
str = require('../app-config').string;
str = require('../app-config').string,
axe = require('axe-logger'),
PgpMailer = require('pgpmailer'),
ImapClient = require('imap-client');
//
//
@ -50,13 +53,15 @@ var MSG_PART_TYPE_HTML = 'html';
* @param {Object} pgpbuilder Generates and encrypts MIME and SMTP messages
* @param {Object} mailreader Parses MIME messages received from IMAP
*/
function Email(keychain, pgp, accountStore, pgpbuilder, mailreader, dialog) {
function Email(keychain, pgp, accountStore, pgpbuilder, mailreader, dialog, appConfig, auth) {
this._keychain = keychain;
this._pgp = pgp;
this._devicestorage = accountStore;
this._pgpbuilder = pgpbuilder;
this._mailreader = mailreader;
this._dialog = dialog;
this._appConfig = appConfig;
this._auth = auth;
}
@ -896,37 +901,40 @@ Email.prototype.decryptBody = function(options) {
* Encrypted (if necessary) and sends a message with a predefined clear text greeting.
*
* @param {Object} options.email The message to be sent
* @param {Object} mailer an instance of the pgpmailer to be used for testing purposes only
*/
Email.prototype.sendEncrypted = function(options) {
Email.prototype.sendEncrypted = function(options, mailer) {
// mime encode, sign, encrypt and send email via smtp
return this._sendGeneric({
encrypt: true,
smtpclient: options.smtpclient, // filled solely in the integration test, undefined in normal usage
mail: options.email,
publicKeysArmored: options.email.publicKeysArmored
});
}, mailer);
};
/**
* Sends a signed message in the plain
*
* @param {Object} options.email The message to be sent
* @param {Object} mailer an instance of the pgpmailer to be used for testing purposes only
*/
Email.prototype.sendPlaintext = function(options) {
Email.prototype.sendPlaintext = function(options, mailer) {
// add suffix to plaintext mail
options.email.body += str.signature + config.cloudUrl + '/' + this._account.emailAddress;
// mime encode, sign and send email via smtp
return this._sendGeneric({
smtpclient: options.smtpclient, // filled solely in the integration test, undefined in normal usage
mail: options.email
});
}, mailer);
};
/**
* This funtion wraps error handling for sending via pgpMailer and uploading to imap.
* @param {Object} options.email The message to be sent
* @param {Object} mailer an instance of the pgpmailer to be used for testing purposes only
*/
Email.prototype._sendGeneric = function(options) {
Email.prototype._sendGeneric = function(options, mailer) {
var self = this;
self.busy();
return new Promise(function(resolve) {
@ -934,8 +942,33 @@ Email.prototype._sendGeneric = function(options) {
resolve();
}).then(function() {
return self._mailerSend(options);
// get the smtp credentials
return self._auth.getCredentials();
}).then(function(credentials) {
// gmail does not require you to upload to the sent items folder after successful sending, whereas most other providers do
self.ignoreUploadOnSent = self.checkIgnoreUploadOnSent(credentials.smtp.host);
// tls socket worker path for multithreaded tls in non-native tls environments
credentials.smtp.tlsWorkerPath = config.workerPath + '/tcp-socket-tls-worker.min.js';
// create a new pgpmailer
self._pgpMailer = (mailer || new PgpMailer(credentials.smtp, self._pgpbuilder));
// certificate update retriggers sending after cert update is persisted
self._pgpMailer.onCert = self._auth.handleCertificateUpdate.bind(self._auth, 'smtp', self._sendGeneric.bind(self, options), self._dialog.error);
}).then(function() {
// send the email
return new Promise(function(resolve, reject) {
self._pgpMailer.send(options, function(err, rfcText) {
if (err) {
reject(err);
} else {
resolve(rfcText);
}
});
});
}).then(function(rfcText) {
// try to upload to sent, but we don't actually care if the upload failed or not
// this should not negatively impact the process of sending
@ -987,29 +1020,45 @@ Email.prototype.encrypt = function(options) {
* given instance of the imap client. If the connection attempt was successful, it will
* update the locally available folders with the newly received IMAP folder listing.
*
* @param {Object} options.imapClient The IMAP client used to receive messages
* @param {Object} options.pgpMailer The SMTP client used to send messages
* @param {Object} imap an instance of the imap-client to be used for testing purposes only
*/
Email.prototype.onConnect = function(options) {
Email.prototype.onConnect = function(imap) {
var self = this;
self._account.loggingIn = true;
self._imapClient = options.imapClient;
self._pgpMailer = options.pgpMailer;
// init imap/smtp clients
return self._auth.getCredentials().then(function(credentials) {
// add the maximum update batch size for imap folders to the imap configuration
credentials.imap.maxUpdateSize = config.imapUpdateBatchSize;
// gmail does not require you to upload to the sent items folder after successful sending, whereas most other providers do
self.ignoreUploadOnSent = !!options.ignoreUploadOnSent;
// tls socket worker path for multithreaded tls in non-native tls environments
credentials.imap.tlsWorkerPath = config.workerPath + '/tcp-socket-tls-worker.min.js';
return imapLogin().then(function() {
self._imapClient = (imap || new ImapClient(credentials.imap));
self._imapClient.onError = onConnectionError; // connection error handling
self._imapClient.onCert = self._auth.handleCertificateUpdate.bind(self._auth, 'imap', self.onConnect.bind(self), self._dialog.error); // certificate update handling
self._imapClient.onSyncUpdate = self._onSyncUpdate.bind(self); // attach sync update handler
}).then(function() {
// imap login
return new Promise(function(resolve, reject) {
self._imapClient.login(function(err) {
if (err) {
reject(err);
} else {
resolve();
}
});
});
}).then(function() {
self._account.loggingIn = false;
// init folders
return self._initFoldersFromImap();
}).then(function() {
// attach sync update handler
self._imapClient.onSyncUpdate = self._onSyncUpdate.bind(self);
// fill the imap mailboxCache with information we have locally available:
// - highest locally available moseq (NB! JavaScript can't handle 64 bit uints, so modseq values are strings)
// - list of locally available uids
@ -1071,16 +1120,20 @@ Email.prototype.onConnect = function(options) {
});
});
function imapLogin() {
return new Promise(function(resolve, reject) {
self._imapClient.login(function(err) {
if (err) {
reject(err);
} else {
resolve();
}
});
});
function onConnectionError(error) {
axe.debug('IMAP connection error, disconnected. Reason: ' + error.message + (error.stack ? ('\n' + error.stack) : ''));
if (!self.isOnline()) {
return;
}
axe.debug('Attempting reconnect in ' + config.reconnectInterval / 1000 + ' seconds.');
setTimeout(function() {
axe.debug('Reconnecting the IMAP stack');
// re-init client modules on error
self.onConnect().catch(self._dialog.error);
}, config.reconnectInterval);
}
};
@ -1667,24 +1720,6 @@ Email.prototype._parse = function(options) {
});
};
/**
* Send email via smtp
* @param {Object} options The options to be passed to the pgpMailer
* @return {Promise}
*/
Email.prototype._mailerSend = function(options) {
var self = this;
return new Promise(function(resolve, reject) {
self._pgpMailer.send(options, function(err, rfcText) {
if (err) {
reject(err);
} else {
resolve(rfcText);
}
});
});
};
/**
* Uploads a message to the sent folder, if necessary.
* Calls back immediately if ignoreUploadOnSent == true or not sent folder was found.
@ -1758,6 +1793,13 @@ Email.prototype.checkIgnoreUploadOnSent = function(hostname) {
};
/**
* Check if the user agent is online.
*/
Email.prototype.isOnline = function() {
return navigator.onLine;
};
//
//
// Helper Functions

View File

@ -240,16 +240,8 @@ Auth.prototype.useOAuth = function(hostname) {
Auth.prototype.getOAuthToken = function() {
var self = this;
if (self.oauthToken) {
// removed cached token and get a new one
return self._oauth.refreshToken({
emailAddress: self.emailAddress,
oldToken: self.oauthToken
}).then(onToken);
} else {
// get a fresh oauth token
return self._oauth.getOAuthToken(self.emailAddress).then(onToken);
}
// get a fresh oauth token
return self._oauth.getOAuthToken(self.emailAddress).then(onToken);
function onToken(oauthToken) {
// shortcut if the email address is already known
@ -317,7 +309,7 @@ Auth.prototype._loadCredentials = function() {
* @param {Function} callback The error handler
* @param {[type]} pemEncodedCert The PEM encoded SSL certificate
*/
Auth.prototype.handleCertificateUpdate = function(component, onConnect, callback, pemEncodedCert) {
Auth.prototype.handleCertificateUpdate = function(component, reconnectCallback, callback, pemEncodedCert) {
var self = this;
axe.debug('new ssl certificate received: ' + pemEncodedCert);
@ -351,7 +343,7 @@ Auth.prototype.handleCertificateUpdate = function(component, onConnect, callback
self[component].ca = pemEncodedCert;
self.credentialsDirty = true;
self.storeCredentials().then(function() {
onConnect(callback);
reconnectCallback(callback);
}).catch(callback);
}
});

View File

@ -14,7 +14,7 @@ var ImapClient = require('imap-client'),
describe('Email DAO integration tests', function() {
this.timeout(100000);
var accountService, emailDao, imapClient, imapMessages, imapFolders, imapServer, smtpServer, smtpClient, userStorage, auth,
var accountService, emailDao, imapClient, pgpMailer, imapMessages, imapFolders, imapServer, smtpServer, smtpClient, userStorage, auth,
mockKeyPair, inbox, spam;
var testAccount = {
@ -209,6 +209,7 @@ describe('Email DAO integration tests', function() {
});
function initAccountService() {
// create imap/smtp clients with stubbed tcp sockets
imapClient = new ImapClient({
auth: {
@ -231,25 +232,10 @@ describe('Email DAO integration tests', function() {
user: testAccount.user,
xoauth2: testAccount.xoauth2
},
secure: true,
ca: ['random string'],
onError: console.error
secure: true
});
smtpClient._TCPSocket = smtpServer.createTCPSocket();
// stub the onConnect function to inject the test imap/smtp clients
sinon.stub(accountService, 'onConnect', function(cb) {
accountService._emailDao.onConnect({
imapClient: imapClient,
pgpMailer: new PgpMailer({
tls: {
ca: 'random string'
}
}, accountService._pgpbuilder)
}).then(cb).catch(cb);
});
// clear the local database before each test
var cleanup = new DeviceStorageDAO(new LawnchairDAO());
cleanup.init(testAccount.user).then(function() {
@ -260,12 +246,22 @@ describe('Email DAO integration tests', function() {
userStorage = accountService._accountStore;
auth = accountService._auth;
auth.setCredentials({
emailAddress: testAccount.user,
password: 'asd',
smtp: {}, // host and port don't matter here since we're using
imap: {} // a preconfigured smtpclient with mocked tcp sockets
});
auth.init().then(function() {
accountService.init({
emailAddress: testAccount.user
}).then(function() {
emailDao = accountService._emailDao;
// retrieve the pgpbuilder from the emaildao and initialize the pgpmailer with the existing pgpbuilder
pgpMailer = new PgpMailer({}, emailDao._pgpbuilder);
// stub rest request to key server
sinon.stub(emailDao._keychain._publicKeyDao, 'get').returns(resolves(mockKeyPair.publicKey));
sinon.stub(emailDao._keychain._publicKeyDao, 'getByUserId').returns(resolves(mockKeyPair.publicKey));
@ -297,9 +293,7 @@ describe('Email DAO integration tests', function() {
passphrase: testAccount.pass,
keypair: mockKeyPair
}).then(function() {
accountService.onConnect(function(err) {
expect(err).to.not.exist;
});
accountService._emailDao.onConnect(imapClient);
});
});
});
@ -310,7 +304,6 @@ describe('Email DAO integration tests', function() {
afterEach(function(done) {
openpgp.initWorker.restore();
mailreader.startWorker.restore();
accountService.onConnect.restore();
imapClient.stopListeningForChanges(function() {
imapClient.logout(function() {
@ -701,7 +694,7 @@ describe('Email DAO integration tests', function() {
subject: 'plaintext test',
body: 'hello world!'
}
}).then(function() {
}, pgpMailer).then(function() {
expect(smtpServer.onmail.callCount).to.equal(1);
done();
});
@ -726,7 +719,7 @@ describe('Email DAO integration tests', function() {
body: 'hello world!',
publicKeysArmored: [mockKeyPair.publicKey.publicKey]
}
}).then(function() {
}, pgpMailer).then(function() {
expect(smtpServer.onmail.callCount).to.equal(1);
done();
});
@ -771,7 +764,41 @@ describe('Email DAO integration tests', function() {
subject: 'plaintext test',
body: expectedBody
}
}).then(function() {});
}, pgpMailer).then(function() {});
});
it('should send & receive a signed encrypted message', function(done) {
var expectedBody = "asdasdasdasdasdasdasdasdasdasdasdasd asdasdasdasdasdasdasdasdasdasdasdasd";
emailDao.onIncomingMessage = function(messages) {
emailDao.getBody({
folder: inbox,
message: messages[0]
}).then(function(message) {
return emailDao.decryptBody({
message: message
});
}).then(function(message) {
expect(message.encrypted).to.be.true;
expect(message.signed).to.be.true;
expect(message.signaturesValid).to.be.true;
expect(message.attachments.length).to.equal(0);
expect(message.body).to.equal(expectedBody);
done();
});
};
emailDao.sendEncrypted({
smtpclient: smtpClient,
email: {
from: [testAccount.user],
to: [testAccount.user],
subject: 'plaintext test',
body: expectedBody,
publicKeysArmored: [mockKeyPair.publicKey.publicKey]
}
}, pgpMailer).then(function() {});
});
});
});

View File

@ -11,7 +11,7 @@ var Account = require('../../../src/js/email/account'),
Dialog = require('../../../src/js/util/dialog');
describe('Account Service unit test', function() {
var account, authStub, outboxStub, emailStub, devicestorageStub, keychainStub, updateHandlerStub, pgpbuilderStub, dialogStub,
var account, authStub, outboxStub, emailStub, devicestorageStub, keychainStub, updateHandlerStub, dialogStub,
realname = 'John Doe',
dummyUser = 'spiderpig@springfield.com';
@ -25,9 +25,8 @@ describe('Account Service unit test', function() {
outboxStub = sinon.createStubInstance(Outbox);
keychainStub = sinon.createStubInstance(Keychain);
updateHandlerStub = sinon.createStubInstance(UpdateHandler);
pgpbuilderStub = {};
dialogStub = sinon.createStubInstance(Dialog);
account = new Account(appConfig, authStub, devicestorageStub, emailStub, outboxStub, keychainStub, updateHandlerStub, pgpbuilderStub, dialogStub);
account = new Account(appConfig, authStub, devicestorageStub, emailStub, outboxStub, keychainStub, updateHandlerStub, dialogStub);
});
afterEach(function() {});
@ -200,47 +199,15 @@ describe('Account Service unit test', function() {
});
describe('onConnect', function() {
var credentials = {
imap: {},
smtp: {}
};
beforeEach(function() {
emailStub._account = {};
sinon.stub(account, 'isOnline').returns(true);
});
afterEach(function() {
account.isOnline.restore();
});
it('should fail due to _auth.getCredentials', function(done) {
authStub.getCredentials.returns(rejects(new Error('asdf')));
dialogStub.error = function(err) {
expect(err.message).to.match(/asdf/);
done();
};
account.onConnect();
});
it('should fail due to _auth.getCredentials', function(done) {
authStub.getCredentials.returns(rejects(new Error('asdf')));
account.onConnect(function(err) {
expect(err.message).to.match(/asdf/);
expect(dialogStub.error.called).to.be.false;
done();
});
});
it('should work', function(done) {
authStub.getCredentials.returns(resolves(credentials));
authStub.handleCertificateUpdate.returns(resolves());
emailStub.onConnect.returns(resolves());
account.onConnect(function(err) {
expect(err).to.not.exist;
expect(dialogStub.error.called).to.be.false;
expect(emailStub.onConnect.calledOnce).to.be.true;
done();
});

View File

@ -9,6 +9,8 @@ var mailreader = require('mailreader'),
KeychainDAO = require('../../../src/js/service/keychain'),
PGP = require('../../../src/js/crypto/pgp'),
DeviceStorageDAO = require('../../../src/js/service/devicestorage'),
appConfig = require('../../../src/js/app-config'),
Auth = require('../../../src/js/service/auth'),
Dialog = require('../../../src/js/util/dialog');
@ -20,7 +22,7 @@ describe('Email DAO unit tests', function() {
var dao;
// mocks
var keychainStub, imapClientStub, pgpMailerStub, pgpBuilderStub, pgpStub, devicestorageStub, parseStub, dialogStub;
var keychainStub, imapClientStub, pgpMailerStub, pgpBuilderStub, pgpStub, devicestorageStub, parseStub, dialogStub, authStub;
// config
var emailAddress, passphrase, asymKeySize, account;
@ -118,11 +120,12 @@ describe('Email DAO unit tests', function() {
parseStub = sinon.stub(mailreader, 'parse');
devicestorageStub = sinon.createStubInstance(DeviceStorageDAO);
dialogStub = sinon.createStubInstance(Dialog);
authStub = sinon.createStubInstance(Auth);
//
// setup the SUT
//
dao = new EmailDAO(keychainStub, pgpStub, devicestorageStub, pgpBuilderStub, mailreader, dialogStub);
dao = new EmailDAO(keychainStub, pgpStub, devicestorageStub, pgpBuilderStub, mailreader, dialogStub, appConfig, authStub);
dao._account = account;
dao._pgpMailer = pgpMailerStub;
dao._imapClient = imapClientStub;
@ -1616,11 +1619,23 @@ describe('Email DAO unit tests', function() {
});
describe('#sendEncrypted', function() {
var publicKeys = ["PUBLIC KEY"],
var credentials,
publicKeys,
dummyMail,
msg;
beforeEach(function() {
credentials = {
smtp: {
host: 'foo.io'
}
};
publicKeys = ["PUBLIC KEY"];
dummyMail = {
publicKeysArmored: publicKeys
},
};
msg = 'wow. such message. much rfc2822.';
});
it('should send encrypted and upload to sent', function(done) {
imapClientStub.uploadMessage.withArgs({
@ -1628,6 +1643,7 @@ describe('Email DAO unit tests', function() {
message: msg
}).yields();
authStub.getCredentials.returns(resolves(credentials));
pgpMailerStub.send.withArgs({
encrypt: true,
mail: dummyMail,
@ -1637,17 +1653,20 @@ describe('Email DAO unit tests', function() {
dao.sendEncrypted({
email: dummyMail
}).then(function() {
}, pgpMailerStub).then(function() {
expect(authStub.getCredentials.calledOnce).to.be.true;
expect(pgpMailerStub.send.calledOnce).to.be.true;
expect(imapClientStub.uploadMessage.calledOnce).to.be.true;
expect(dao.ignoreUploadOnSent).to.be.false;
done();
});
});
it('should send encrypted and not upload to sent', function(done) {
dao.ignoreUploadOnSent = true;
credentials.smtp.host = 'smtp.gmail.com';
authStub.getCredentials.returns(resolves(credentials));
pgpMailerStub.send.withArgs({
encrypt: true,
mail: dummyMail,
@ -1657,9 +1676,11 @@ describe('Email DAO unit tests', function() {
dao.sendEncrypted({
email: dummyMail
}).then(function() {
}, pgpMailerStub).then(function() {
expect(authStub.getCredentials.calledOnce).to.be.true;
expect(pgpMailerStub.send.calledOnce).to.be.true;
expect(imapClientStub.uploadMessage.called).to.be.false;
expect(dao.ignoreUploadOnSent).to.be.true;
done();
});
@ -1668,12 +1689,14 @@ describe('Email DAO unit tests', function() {
it('should send encrypted and ignore error on upload', function(done) {
imapClientStub.uploadMessage.yields(new Error());
pgpMailerStub.send.yieldsAsync(null, msg);
authStub.getCredentials.returns(resolves(credentials));
dao.sendEncrypted({
email: dummyMail
}).then(function() {
}, pgpMailerStub).then(function() {
expect(pgpMailerStub.send.calledOnce).to.be.true;
expect(imapClientStub.uploadMessage.calledOnce).to.be.true;
expect(authStub.getCredentials.calledOnce).to.be.true;
done();
});
@ -1681,12 +1704,14 @@ describe('Email DAO unit tests', function() {
it('should not send when pgpmailer fails', function(done) {
pgpMailerStub.send.yieldsAsync({});
authStub.getCredentials.returns(resolves(credentials));
dao.sendEncrypted({
email: dummyMail
}).catch(function(err) {
}, pgpMailerStub).catch(function(err) {
expect(err).to.exist;
expect(authStub.getCredentials.calledOnce).to.be.true;
expect(pgpMailerStub.send.calledOnce).to.be.true;
expect(imapClientStub.uploadMessage.called).to.be.false;
@ -1699,8 +1724,9 @@ describe('Email DAO unit tests', function() {
dao.sendEncrypted({
email: dummyMail
}).catch(function(err) {
}, pgpMailerStub).catch(function(err) {
expect(err.code).to.equal(42);
expect(authStub.getCredentials.called).to.be.false;
expect(pgpMailerStub.send.called).to.be.false;
expect(imapClientStub.uploadMessage.called).to.be.false;
done();
@ -1710,14 +1736,26 @@ describe('Email DAO unit tests', function() {
});
describe('#sendPlaintext', function() {
var dummyMail = {};
var msg = 'wow. such message. much rfc2822.';
var credentials,
dummyMail,
msg;
beforeEach(function() {
credentials = {
smtp: {
host: 'foo.io'
}
};
dummyMail = {};
msg = 'wow. such message. much rfc2822.';
});
it('should send in the plain and upload to sent', function(done) {
pgpMailerStub.send.withArgs({
smtpclient: undefined,
mail: dummyMail
}).yieldsAsync(null, msg);
authStub.getCredentials.returns(resolves(credentials));
imapClientStub.uploadMessage.withArgs({
path: sentFolder.path,
@ -1726,7 +1764,8 @@ describe('Email DAO unit tests', function() {
dao.sendPlaintext({
email: dummyMail
}).then(function() {
}, pgpMailerStub).then(function() {
expect(authStub.getCredentials.calledOnce).to.be.true;
expect(pgpMailerStub.send.calledOnce).to.be.true;
expect(imapClientStub.uploadMessage.calledOnce).to.be.true;
done();
@ -1735,15 +1774,18 @@ describe('Email DAO unit tests', function() {
it('should send in the plain and not upload to sent', function(done) {
dao.ignoreUploadOnSent = true;
credentials.smtp.host = 'smtp.gmail.com';
pgpMailerStub.send.withArgs({
smtpclient: undefined,
mail: dummyMail
}).yieldsAsync(null, msg);
authStub.getCredentials.returns(resolves(credentials));
dao.sendPlaintext({
email: dummyMail
}).then(function() {
}, pgpMailerStub).then(function() {
expect(authStub.getCredentials.calledOnce).to.be.true;
expect(pgpMailerStub.send.calledOnce).to.be.true;
expect(imapClientStub.uploadMessage.called).to.be.false;
done();
@ -1753,10 +1795,12 @@ describe('Email DAO unit tests', function() {
it('should send and ignore error on upload', function(done) {
imapClientStub.uploadMessage.yields(new Error());
pgpMailerStub.send.yieldsAsync(null, msg);
authStub.getCredentials.returns(resolves(credentials));
dao.sendEncrypted({
dao.sendPlaintext({
email: dummyMail
}).then(function() {
}, pgpMailerStub).then(function() {
expect(authStub.getCredentials.calledOnce).to.be.true;
expect(pgpMailerStub.send.calledOnce).to.be.true;
expect(imapClientStub.uploadMessage.calledOnce).to.be.true;
@ -1766,11 +1810,13 @@ describe('Email DAO unit tests', function() {
it('should not send due to error', function(done) {
pgpMailerStub.send.yieldsAsync({});
authStub.getCredentials.returns(resolves(credentials));
dao.sendPlaintext({
email: dummyMail
}).catch(function(err) {
}, pgpMailerStub).catch(function(err) {
expect(err).to.exist;
expect(authStub.getCredentials.calledOnce).to.be.true;
expect(pgpMailerStub.send.calledOnce).to.be.true;
expect(imapClientStub.uploadMessage.called).to.be.false;
done();
@ -1782,8 +1828,9 @@ describe('Email DAO unit tests', function() {
dao.sendPlaintext({
email: dummyMail
}).catch(function(err) {
}, pgpMailerStub).catch(function(err) {
expect(err.code).to.equal(42);
expect(authStub.getCredentials.called).to.be.false;
expect(pgpMailerStub.send.called).to.be.false;
expect(imapClientStub.uploadMessage.called).to.be.false;
done();
@ -1805,12 +1852,15 @@ describe('Email DAO unit tests', function() {
describe('event handlers', function() {
describe('#onConnect', function() {
var initFoldersStub;
var initFoldersStub, credentials;
beforeEach(function() {
initFoldersStub = sinon.stub(dao, '_initFoldersFromImap');
delete dao._imapClient;
delete dao._pgpMailer;
credentials = {
imap: {}
};
});
it('should connect', function(done) {
@ -1818,16 +1868,13 @@ describe('Email DAO unit tests', function() {
uid: 123,
modseq: '123'
}];
authStub.getCredentials.returns(resolves(credentials));
imapClientStub.login.yieldsAsync();
imapClientStub.selectMailbox.yields();
imapClientStub.listenForChanges.yields();
initFoldersStub.returns(resolves());
dao.onConnect({
imapClient: imapClientStub,
pgpMailer: pgpMailerStub
}).then(function() {
expect(dao.ignoreUploadOnSent).to.be.false;
dao.onConnect(imapClientStub).then(function() {
expect(imapClientStub.login.calledOnce).to.be.true;
expect(imapClientStub.selectMailbox.calledOnce).to.be.true;
expect(initFoldersStub.calledOnce).to.be.true;

View File

@ -177,24 +177,6 @@ describe('Auth unit tests', function() {
});
describe('#getOAuthToken', function() {
it('should refresh token with known email address', function(done) {
auth.emailAddress = emailAddress;
auth.oauthToken = 'oldToken';
oauthStub.refreshToken.withArgs({
emailAddress: emailAddress,
oldToken: 'oldToken'
}).returns(resolves(oauthToken));
auth.getOAuthToken().then(function() {
expect(auth.emailAddress).to.equal(emailAddress);
expect(auth.oauthToken).to.equal(oauthToken);
expect(oauthStub.refreshToken.calledOnce).to.be.true;
done();
});
});
it('should fetch token with known email address', function(done) {
auth.emailAddress = emailAddress;
oauthStub.getOAuthToken.withArgs(emailAddress).returns(resolves(oauthToken));