Refactor auth

This commit is contained in:
Tankred Hase 2014-12-11 20:27:55 +01:00
parent 15b902acf6
commit 26553e49d7
3 changed files with 193 additions and 274 deletions

View File

@ -170,8 +170,8 @@ Account.prototype.onConnect = function(callback) {
pgpMailer.onError = onConnectionError; pgpMailer.onError = onConnectionError;
// certificate update handling // certificate update handling
imapClient.onCert = self._auth.handleCertificateUpdate.bind(self._auth, 'imap', self.onConnect.bind(self), self._dialog.error); imapClient.onCert = self._auth.handleCertificateUpdate.bind(self._auth, 'imap', self.onConnect.bind(self)).catch(self._dialog.error);
pgpMailer.onCert = self._auth.handleCertificateUpdate.bind(self._auth, 'smtp', self.onConnect.bind(self), self._dialog.error); pgpMailer.onCert = self._auth.handleCertificateUpdate.bind(self._auth, 'smtp', self.onConnect.bind(self)).catch(self._dialog.error);
// connect to clients // connect to clients
self._emailDao.onConnect({ self._emailDao.onConnect({

View File

@ -26,22 +26,21 @@ var SMTP_DB_KEY = 'smtp';
* auth.getCredentials(...); // called to gather all the information to connect to IMAP/SMTP, * auth.getCredentials(...); // called to gather all the information to connect to IMAP/SMTP,
* username, password / oauth token, IMAP/SMTP server host names, ... * username, password / oauth token, IMAP/SMTP server host names, ...
*/ */
function Auth($q, appConfigStore, oauth, pgp) { function Auth(appConfigStore, oauth, pgp) {
this._q = $q;
this._appConfigStore = appConfigStore; this._appConfigStore = appConfigStore;
this._oauth = oauth; this._oauth = oauth;
this._pgp = pgp; this._pgp = pgp;
this._initialized = false;
} }
/** /**
* Initialize the service * Initialize the service
*/ */
Auth.prototype.init = function(callback) { Auth.prototype.init = function() {
var self = this; var self = this;
return self._appConfigStore.init(APP_CONFIG_DB_NAME).then(function() {
self._appConfigStore.init(APP_CONFIG_DB_NAME, function(error) { self._initialized = true;
self._initialized = !error;
callback(error);
}); });
}; };
@ -58,84 +57,62 @@ Auth.prototype.isInitialized = function() {
* 2 a) ... in an oauth setting, retrieves a fresh oauth token from the Chrome Identity API. * 2 a) ... in an oauth setting, retrieves a fresh oauth token from the Chrome Identity API.
* 2 b) ... in a user/passwd setting, does not need to do additional work. * 2 b) ... in a user/passwd setting, does not need to do additional work.
* 3) Loads the intermediate certs from the configuration. * 3) Loads the intermediate certs from the configuration.
*
* @param {Function} callback(err, credentials)
*/ */
Auth.prototype.getCredentials = function(callback) { Auth.prototype.getCredentials = function() {
var self = this; var self = this;
if (!self.emailAddress) { if (!self.emailAddress) {
// we're not yet initialized, so let's load our stuff from disk // we're not yet initialized, so let's load our stuff from disk
self._loadCredentials(function(err) { return self._loadCredentials().then(chooseLogin);
if (err) {
return callback(err);
}
chooseLogin();
});
return;
} }
chooseLogin(); return chooseLogin();
function chooseLogin() { function chooseLogin() {
if (self.useOAuth(self.imap.host) && !self.password) { if (self.useOAuth(self.imap.host) && !self.password) {
// oauth login // oauth login
self.getOAuthToken(function(err) { return self.getOAuthToken().then(done);
if (err) {
return callback(err);
}
done();
});
return;
} }
if (self.passwordNeedsDecryption) { if (self.passwordNeedsDecryption) {
// decrypt password // decrypt password
self._pgp.decrypt(self.password, undefined, function(err, cleartext) { return self._pgp.decrypt(self.password, undefined).then(function(cleartext) {
if (err) {
return callback(err);
}
self.passwordNeedsDecryption = false; self.passwordNeedsDecryption = false;
self.password = cleartext; self.password = cleartext;
}).then(done);
done();
});
return;
} }
done(); return done();
} }
function done() { function done() {
var credentials = { return new Promise(function(resolve) {
imap: { var credentials = {
secure: self.imap.secure, imap: {
port: self.imap.port, secure: self.imap.secure,
host: self.imap.host, port: self.imap.port,
ca: self.imap.ca, host: self.imap.host,
auth: { ca: self.imap.ca,
user: self.username, auth: {
xoauth2: self.oauthToken, // password or oauthToken is undefined user: self.username,
pass: self.password xoauth2: self.oauthToken, // password or oauthToken is undefined
pass: self.password
}
},
smtp: {
secure: self.smtp.secure,
port: self.smtp.port,
host: self.smtp.host,
ca: self.smtp.ca,
auth: {
user: self.username,
xoauth2: self.oauthToken,
pass: self.password // password or oauthToken is undefined
}
} }
}, };
smtp: { resolve(credentials);
secure: self.smtp.secure, });
port: self.smtp.port,
host: self.smtp.host,
ca: self.smtp.ca,
auth: {
user: self.username,
xoauth2: self.oauthToken,
pass: self.password // password or oauthToken is undefined
}
}
};
callback(null, credentials);
} }
}; };
@ -161,35 +138,43 @@ Auth.prototype.setCredentials = function(options) {
Auth.prototype.storeCredentials = function() { Auth.prototype.storeCredentials = function() {
var self = this; var self = this;
return self._q(function(resolve) {
if (!self.credentialsDirty) { if (!self.credentialsDirty) {
// nothing to store if credentials not dirty
return new Promise(function(resolve) {
resolve();
});
}
// persist the config
var storeSmtp = self._appConfigStore.storeList([self.smtp], SMTP_DB_KEY);
var storeImap = self._appConfigStore.storeList([self.imap], IMAP_DB_KEY);
var storeEmailAddress = self._appConfigStore.storeList([self.emailAddress], EMAIL_ADDR_DB_KEY);
var storeUsername = self._appConfigStore.storeList([self.username], USERNAME_DB_KEY);
var storeRealname = self._appConfigStore.storeList([self.realname], REALNAME_DB_KEY);
var storePassword = new Promise(function(resolve) {
if (!self.password) {
resolve(); resolve();
return; return;
} }
// persist the config if (self.passwordNeedsDecryption) {
var storeSmtp = self._appConfigStore.storeList([self.smtp], SMTP_DB_KEY); // password is not decrypted yet, so no need to re-encrypt it before storing...
var storeImap = self._appConfigStore.storeList([self.imap], IMAP_DB_KEY); return self._appConfigStore.storeList([self.password], PASSWD_DB_KEY).then(resolve);
var storeEmailAddress = self._appConfigStore.storeList([self.emailAddress], EMAIL_ADDR_DB_KEY); }
var storeUsername = self._appConfigStore.storeList([self.username], USERNAME_DB_KEY); return self._pgp.encrypt(self.password, undefined).then(function(ciphertext) {
var storeRealname = self._appConfigStore.storeList([self.realname], REALNAME_DB_KEY); return self._appConfigStore.storeList([ciphertext], PASSWD_DB_KEY).then(resolve);
var storePassword = self._q(function(resolve) {
if (!self.password) {
resolve();
return;
}
if (self.passwordNeedsDecryption) {
// password is not decrypted yet, so no need to re-encrypt it before storing...
return self._appConfigStore.storeList([self.password], PASSWD_DB_KEY).then(resolve);
}
return self._pgp.encrypt(self.password, undefined).then(function(ciphertext) {
return self._appConfigStore.storeList([ciphertext], PASSWD_DB_KEY).then(resolve);
});
}); });
});
Promise.all([storeSmtp, storeImap, storeEmailAddress, storeUsername, storeRealname, storePassword]).then(resolve); return Promise.all([
}).then(function() { storeSmtp,
storeImap,
storeEmailAddress,
storeUsername,
storeRealname,
storePassword
]).then(function() {
self.credentialsDirty = false; self.credentialsDirty = false;
}); });
}; };
@ -197,25 +182,23 @@ Auth.prototype.storeCredentials = function() {
/** /**
* Returns the email address. Loads it from disk, if necessary * Returns the email address. Loads it from disk, if necessary
*/ */
Auth.prototype.getEmailAddress = function(callback) { Auth.prototype.getEmailAddress = function() {
var self = this; var self = this;
if (self.emailAddress) { if (self.emailAddress) {
return callback(null, { return new Promise(function(resolve) {
emailAddress: self.emailAddress, resolve({
realname: self.realname emailAddress: self.emailAddress,
realname: self.realname
});
}); });
} }
self._loadCredentials(function(err) { return self._loadCredentials().then(function() {
if (err) { return {
return callback(err);
}
callback(null, {
emailAddress: self.emailAddress, emailAddress: self.emailAddress,
realname: self.realname realname: self.realname
}); };
}); });
}; };
@ -250,40 +233,31 @@ Auth.prototype.useOAuth = function(hostname) {
* is android only, since the desktop chrome will query the user that is logged into chrome * is android only, since the desktop chrome will query the user that is logged into chrome
* 3) fetch the email address for the oauth token from the chrome identity api * 3) fetch the email address for the oauth token from the chrome identity api
*/ */
Auth.prototype.getOAuthToken = function(callback) { Auth.prototype.getOAuthToken = function() {
var self = this; var self = this;
if (self.oauthToken) { if (self.oauthToken) {
// removed cached token and get a new one // removed cached token and get a new one
self._oauth.refreshToken({ return self._oauth.refreshToken({
emailAddress: self.emailAddress, emailAddress: self.emailAddress,
oldToken: self.oauthToken oldToken: self.oauthToken
}, onToken); }).then(onToken);
} else { } else {
// get a fresh oauth token // get a fresh oauth token
self._oauth.getOAuthToken(self.emailAddress, onToken); return self._oauth.getOAuthToken(self.emailAddress).then(onToken);
} }
function onToken(err, oauthToken) { function onToken(oauthToken) {
if (err) {
return callback(err);
}
// shortcut if the email address is already known // shortcut if the email address is already known
if (self.emailAddress) { if (self.emailAddress) {
self.oauthToken = oauthToken; self.oauthToken = oauthToken;
return callback(); return;
} }
// query the email address // query the email address
self._oauth.queryEmailAddress(oauthToken, function(err, emailAddress) { return self._oauth.queryEmailAddress(oauthToken).then(function(emailAddress) {
if (err) {
return callback(err);
}
self.oauthToken = oauthToken; self.oauthToken = oauthToken;
self.emailAddress = emailAddress; self.emailAddress = emailAddress;
callback();
}); });
} }
}; };
@ -291,67 +265,44 @@ Auth.prototype.getOAuthToken = function(callback) {
/** /**
* Loads email address, password, ... from disk and sets them on `this` * Loads email address, password, ... from disk and sets them on `this`
*/ */
Auth.prototype._loadCredentials = function(callback) { Auth.prototype._loadCredentials = function() {
var self = this; var self = this;
if (self.initialized) { if (self.initialized) {
callback(); return new Promise(function(resolve) {
resolve();
});
} }
loadFromDB(SMTP_DB_KEY, function(err, smtp) { return loadFromDB(SMTP_DB_KEY).then(function(smtp) {
if (err) { self.smtp = smtp;
return callback(err); return loadFromDB(IMAP_DB_KEY);
}
}).then(function(imap) {
self.imap = imap;
return loadFromDB(USERNAME_DB_KEY);
loadFromDB(IMAP_DB_KEY, function(err, imap) { }).then(function(username) {
if (err) { self.username = username;
return callback(err); return loadFromDB(REALNAME_DB_KEY);
}
}).then(function(realname) {
self.realname = realname;
return loadFromDB(EMAIL_ADDR_DB_KEY);
loadFromDB(USERNAME_DB_KEY, function(err, username) { }).then(function(emailAddress) {
if (err) { self.emailAddress = emailAddress;
return callback(err); return loadFromDB(PASSWD_DB_KEY);
}
}).then(function(password) {
loadFromDB(REALNAME_DB_KEY, function(err, realname) { self.password = password;
if (err) { self.passwordNeedsDecryption = !!password;
return callback(err); self.initialized = true;
}
loadFromDB(EMAIL_ADDR_DB_KEY, function(err, emailAddress) {
if (err) {
return callback(err);
}
loadFromDB(PASSWD_DB_KEY, function(err, password) {
if (err) {
return callback(err);
}
self.emailAddress = emailAddress;
self.password = password;
self.passwordNeedsDecryption = !!password;
self.username = username;
self.realname = realname;
self.smtp = smtp;
self.imap = imap;
self.initialized = true;
callback();
});
});
});
});
});
}); });
function loadFromDB(key, callback) { function loadFromDB(key) {
self._appConfigStore.listItems(key, 0, null, function(err, cachedItems) { return self._appConfigStore.listItems(key, 0, null).then(function(cachedItems) {
callback(err, (!err && cachedItems && cachedItems[0])); return cachedItems && cachedItems[0];
}); });
} }
}; };
@ -359,10 +310,9 @@ Auth.prototype._loadCredentials = function(callback) {
/** /**
* Handles certificate updates and errors by notifying the user. * Handles certificate updates and errors by notifying the user.
* @param {String} component Either imap or smtp * @param {String} component Either imap or smtp
* @param {Function} callback The error handler
* @param {[type]} pemEncodedCert The PEM encoded SSL certificate * @param {[type]} pemEncodedCert The PEM encoded SSL certificate
*/ */
Auth.prototype.handleCertificateUpdate = function(component, onConnect, callback, pemEncodedCert) { Auth.prototype.handleCertificateUpdate = function(component, onConnect, pemEncodedCert) {
var self = this; var self = this;
axe.debug('new ssl certificate received: ' + pemEncodedCert); axe.debug('new ssl certificate received: ' + pemEncodedCert);
@ -371,8 +321,7 @@ Auth.prototype.handleCertificateUpdate = function(component, onConnect, callback
// no previous ssl cert, trust on first use // no previous ssl cert, trust on first use
self[component].ca = pemEncodedCert; self[component].ca = pemEncodedCert;
self.credentialsDirty = true; self.credentialsDirty = true;
self.storeCredentials(callback); return self.storeCredentials();
return;
} }
if (self[component].ca === pemEncodedCert) { if (self[component].ca === pemEncodedCert) {
@ -381,51 +330,41 @@ Auth.prototype.handleCertificateUpdate = function(component, onConnect, callback
} }
// previous ssl cert known, does not match: query user and certificate // previous ssl cert known, does not match: query user and certificate
callback({ return new Promise(function() {
title: str.updateCertificateTitle, throw {
message: str.updateCertificateMessage.replace('{0}', self[component].host), title: str.updateCertificateTitle,
positiveBtnStr: str.updateCertificatePosBtn, message: str.updateCertificateMessage.replace('{0}', self[component].host),
negativeBtnStr: str.updateCertificateNegBtn, positiveBtnStr: str.updateCertificatePosBtn,
showNegativeBtn: true, negativeBtnStr: str.updateCertificateNegBtn,
faqLink: str.certificateFaqLink, showNegativeBtn: true,
callback: function(granted) { faqLink: str.certificateFaqLink,
if (!granted) { callback: function(granted) {
return; if (!granted) {
}
self[component].ca = pemEncodedCert;
self.credentialsDirty = true;
self.storeCredentials(function(err) {
if (err) {
callback(err);
return; return;
} }
onConnect(callback); self[component].ca = pemEncodedCert;
}); self.credentialsDirty = true;
} return self.storeCredentials().then(function() {
return onConnect();
});
}
};
}); });
}; };
/** /**
* Logout of the app by clearing the app config store and in memory credentials * Logout of the app by clearing the app config store and in memory credentials
*/ */
Auth.prototype.logout = function(callback) { Auth.prototype.logout = function() {
var self = this; var self = this;
// clear app config db // clear app config db
self._appConfigStore.clear(function(err) { return self._appConfigStore.clear().then(function() {
if (err) {
callback(err);
return;
}
// clear in memory cache // clear in memory cache
self.setCredentials({}); self.setCredentials({});
self.initialized = undefined; self.initialized = undefined;
self.credentialsDirty = undefined; self.credentialsDirty = undefined;
self.passwordNeedsDecryption = undefined; self.passwordNeedsDecryption = undefined;
callback();
}); });
}; };

View File

@ -50,16 +50,15 @@ describe('Auth unit tests', function() {
describe('#init', function() { describe('#init', function() {
it('should initialize a user db', function(done) { it('should initialize a user db', function(done) {
storageStub.init.withArgs(APP_CONFIG_DB_NAME).yields(); storageStub.init.withArgs(APP_CONFIG_DB_NAME).returns(resolves());
auth.init(function(err) { auth.init().then(function() {
expect(err).to.not.exist;
expect(auth._initialized).to.be.true; expect(auth._initialized).to.be.true;
done(); done();
}); });
}); });
it('should initialize a user db', function(done) { it('should initialize a user db', function(done) {
storageStub.init.withArgs(APP_CONFIG_DB_NAME).yields(new Error()); storageStub.init.withArgs(APP_CONFIG_DB_NAME).returns(rejects(new Error()));
auth.init(function(err) { auth.init().catch(function(err) {
expect(err).to.exist; expect(err).to.exist;
expect(auth._initialized).to.be.false; expect(auth._initialized).to.be.false;
done(); done();
@ -69,17 +68,15 @@ describe('Auth unit tests', function() {
describe('#getCredentials', function() { describe('#getCredentials', function() {
it('should load credentials and retrieve credentials from cfg', function(done) { it('should load credentials and retrieve credentials from cfg', function(done) {
storageStub.listItems.withArgs(EMAIL_ADDR_DB_KEY, 0, null).yieldsAsync(null, [emailAddress]); storageStub.listItems.withArgs(EMAIL_ADDR_DB_KEY, 0, null).returns(resolves([emailAddress]));
storageStub.listItems.withArgs(PASSWD_DB_KEY, 0, null).yieldsAsync(null, [encryptedPassword]); storageStub.listItems.withArgs(PASSWD_DB_KEY, 0, null).returns(resolves([encryptedPassword]));
storageStub.listItems.withArgs(USERNAME_DB_KEY, 0, null).yieldsAsync(null, [username]); storageStub.listItems.withArgs(USERNAME_DB_KEY, 0, null).returns(resolves([username]));
storageStub.listItems.withArgs(REALNAME_DB_KEY, 0, null).yieldsAsync(null, [realname]); storageStub.listItems.withArgs(REALNAME_DB_KEY, 0, null).returns(resolves([realname]));
storageStub.listItems.withArgs(IMAP_DB_KEY, 0, null).yieldsAsync(null, [imap]); storageStub.listItems.withArgs(IMAP_DB_KEY, 0, null).returns(resolves([imap]));
storageStub.listItems.withArgs(SMTP_DB_KEY, 0, null).yieldsAsync(null, [smtp]); storageStub.listItems.withArgs(SMTP_DB_KEY, 0, null).returns(resolves([smtp]));
pgpStub.decrypt.withArgs(encryptedPassword, undefined).yields(null, password); pgpStub.decrypt.withArgs(encryptedPassword, undefined).returns(resolves(password));
auth.getCredentials(function(err, cred) {
expect(err).to.not.exist;
auth.getCredentials().then(function(cred) {
expect(auth.emailAddress).to.equal(emailAddress); expect(auth.emailAddress).to.equal(emailAddress);
expect(auth.password).to.equal(password); expect(auth.password).to.equal(password);
@ -136,17 +133,15 @@ describe('Auth unit tests', function() {
auth.smtp = smtp; auth.smtp = smtp;
auth.imap = imap; auth.imap = imap;
storageStub.storeList.withArgs([encryptedPassword], PASSWD_DB_KEY).yieldsAsync(); storageStub.storeList.withArgs([encryptedPassword], PASSWD_DB_KEY).returns(resolves());
storageStub.storeList.withArgs([emailAddress], EMAIL_ADDR_DB_KEY).yieldsAsync(); storageStub.storeList.withArgs([emailAddress], EMAIL_ADDR_DB_KEY).returns(resolves());
storageStub.storeList.withArgs([username], USERNAME_DB_KEY).yieldsAsync(); storageStub.storeList.withArgs([username], USERNAME_DB_KEY).returns(resolves());
storageStub.storeList.withArgs([realname], REALNAME_DB_KEY).yieldsAsync(); storageStub.storeList.withArgs([realname], REALNAME_DB_KEY).returns(resolves());
storageStub.storeList.withArgs([imap], IMAP_DB_KEY).yieldsAsync(); storageStub.storeList.withArgs([imap], IMAP_DB_KEY).returns(resolves());
storageStub.storeList.withArgs([smtp], SMTP_DB_KEY).yieldsAsync(); storageStub.storeList.withArgs([smtp], SMTP_DB_KEY).returns(resolves());
pgpStub.encrypt.withArgs(password).yields(null, encryptedPassword); pgpStub.encrypt.withArgs(password).returns(resolves(encryptedPassword));
auth.storeCredentials(function(err) {
expect(err).to.not.exist;
auth.storeCredentials().then(function() {
expect(storageStub.storeList.callCount).to.equal(6); expect(storageStub.storeList.callCount).to.equal(6);
expect(pgpStub.encrypt.calledOnce).to.be.true; expect(pgpStub.encrypt.calledOnce).to.be.true;
@ -186,13 +181,11 @@ describe('Auth unit tests', function() {
oauthStub.refreshToken.withArgs({ oauthStub.refreshToken.withArgs({
emailAddress: emailAddress, emailAddress: emailAddress,
oldToken: 'oldToken' oldToken: 'oldToken'
}).yieldsAsync(null, oauthToken); }).returns(resolves(oauthToken));
auth.getOAuthToken(function(err) { auth.getOAuthToken().then(function() {
expect(err).to.not.exist;
expect(auth.emailAddress).to.equal(emailAddress); expect(auth.emailAddress).to.equal(emailAddress);
expect(auth.oauthToken).to.equal(oauthToken); expect(auth.oauthToken).to.equal(oauthToken);
expect(oauthStub.refreshToken.calledOnce).to.be.true; expect(oauthStub.refreshToken.calledOnce).to.be.true;
done(); done();
@ -201,13 +194,11 @@ describe('Auth unit tests', function() {
it('should fetch token with known email address', function(done) { it('should fetch token with known email address', function(done) {
auth.emailAddress = emailAddress; auth.emailAddress = emailAddress;
oauthStub.getOAuthToken.withArgs(emailAddress).yieldsAsync(null, oauthToken); oauthStub.getOAuthToken.withArgs(emailAddress).returns(resolves(oauthToken));
auth.getOAuthToken(function(err) { auth.getOAuthToken().then(function() {
expect(err).to.not.exist;
expect(auth.emailAddress).to.equal(emailAddress); expect(auth.emailAddress).to.equal(emailAddress);
expect(auth.oauthToken).to.equal(oauthToken); expect(auth.oauthToken).to.equal(oauthToken);
expect(oauthStub.getOAuthToken.calledOnce).to.be.true; expect(oauthStub.getOAuthToken.calledOnce).to.be.true;
done(); done();
@ -215,14 +206,12 @@ describe('Auth unit tests', function() {
}); });
it('should fetch token with unknown email address', function(done) { it('should fetch token with unknown email address', function(done) {
oauthStub.getOAuthToken.withArgs(undefined).yieldsAsync(null, oauthToken); oauthStub.getOAuthToken.withArgs(undefined).returns(resolves(oauthToken));
oauthStub.queryEmailAddress.withArgs(oauthToken).yieldsAsync(null, emailAddress); oauthStub.queryEmailAddress.withArgs(oauthToken).returns(resolves(emailAddress));
auth.getOAuthToken(function(err) { auth.getOAuthToken().then(function() {
expect(err).to.not.exist;
expect(auth.emailAddress).to.equal(emailAddress); expect(auth.emailAddress).to.equal(emailAddress);
expect(auth.oauthToken).to.equal(oauthToken); expect(auth.oauthToken).to.equal(oauthToken);
expect(oauthStub.getOAuthToken.calledOnce).to.be.true; expect(oauthStub.getOAuthToken.calledOnce).to.be.true;
expect(oauthStub.queryEmailAddress.calledOnce).to.be.true; expect(oauthStub.queryEmailAddress.calledOnce).to.be.true;
@ -231,14 +220,13 @@ describe('Auth unit tests', function() {
}); });
it('should fail when email address fetch fails', function(done) { it('should fail when email address fetch fails', function(done) {
oauthStub.getOAuthToken.yieldsAsync(null, oauthToken); oauthStub.getOAuthToken.returns(resolves(oauthToken));
oauthStub.queryEmailAddress.yieldsAsync(new Error()); oauthStub.queryEmailAddress.returns(rejects(new Error()));
auth.getOAuthToken(function(err) { auth.getOAuthToken().catch(function(err) {
expect(err).to.exist; expect(err).to.exist;
expect(auth.emailAddress).to.not.exist; expect(auth.emailAddress).to.not.exist;
expect(auth.oauthToken).to.not.exist; expect(auth.oauthToken).to.not.exist;
expect(oauthStub.getOAuthToken.calledOnce).to.be.true; expect(oauthStub.getOAuthToken.calledOnce).to.be.true;
expect(oauthStub.queryEmailAddress.calledOnce).to.be.true; expect(oauthStub.queryEmailAddress.calledOnce).to.be.true;
@ -247,13 +235,12 @@ describe('Auth unit tests', function() {
}); });
it('should fail when oauth fetch fails', function(done) { it('should fail when oauth fetch fails', function(done) {
oauthStub.getOAuthToken.yieldsAsync(new Error()); oauthStub.getOAuthToken.returns(rejects(new Error()));
auth.getOAuthToken(function(err) { auth.getOAuthToken().catch(function(err) {
expect(err).to.exist; expect(err).to.exist;
expect(auth.emailAddress).to.not.exist; expect(auth.emailAddress).to.not.exist;
expect(auth.oauthToken).to.not.exist; expect(auth.oauthToken).to.not.exist;
expect(oauthStub.getOAuthToken.calledOnce).to.be.true; expect(oauthStub.getOAuthToken.calledOnce).to.be.true;
expect(oauthStub.queryEmailAddress.called).to.be.false; expect(oauthStub.queryEmailAddress.called).to.be.false;
@ -264,15 +251,14 @@ describe('Auth unit tests', function() {
describe('#_loadCredentials', function() { describe('#_loadCredentials', function() {
it('should work', function(done) { it('should work', function(done) {
storageStub.listItems.withArgs(EMAIL_ADDR_DB_KEY, 0, null).yieldsAsync(null, [emailAddress]); storageStub.listItems.withArgs(EMAIL_ADDR_DB_KEY, 0, null).returns(resolves([emailAddress]));
storageStub.listItems.withArgs(PASSWD_DB_KEY, 0, null).yieldsAsync(null, [encryptedPassword]); storageStub.listItems.withArgs(PASSWD_DB_KEY, 0, null).returns(resolves([encryptedPassword]));
storageStub.listItems.withArgs(USERNAME_DB_KEY, 0, null).yieldsAsync(null, [username]); storageStub.listItems.withArgs(USERNAME_DB_KEY, 0, null).returns(resolves([username]));
storageStub.listItems.withArgs(REALNAME_DB_KEY, 0, null).yieldsAsync(null, [realname]); storageStub.listItems.withArgs(REALNAME_DB_KEY, 0, null).returns(resolves([realname]));
storageStub.listItems.withArgs(IMAP_DB_KEY, 0, null).yieldsAsync(null, [imap]); storageStub.listItems.withArgs(IMAP_DB_KEY, 0, null).returns(resolves([imap]));
storageStub.listItems.withArgs(SMTP_DB_KEY, 0, null).yieldsAsync(null, [smtp]); storageStub.listItems.withArgs(SMTP_DB_KEY, 0, null).returns(resolves([smtp]));
auth._loadCredentials(function(err) { auth._loadCredentials().then(function() {
expect(err).to.not.exist;
expect(auth.emailAddress).to.equal(emailAddress); expect(auth.emailAddress).to.equal(emailAddress);
expect(auth.password).to.equal(encryptedPassword); expect(auth.password).to.equal(encryptedPassword);
expect(auth.imap).to.equal(imap); expect(auth.imap).to.equal(imap);
@ -289,9 +275,9 @@ describe('Auth unit tests', function() {
}); });
it('should fail', function(done) { it('should fail', function(done) {
storageStub.listItems.yieldsAsync(new Error()); storageStub.listItems.returns(rejects(new Error()));
auth._loadCredentials(function(err) { auth._loadCredentials().catch(function(err) {
expect(err).to.exist; expect(err).to.exist;
expect(auth.emailAddress).to.not.exist; expect(auth.emailAddress).to.not.exist;
expect(auth.password).to.not.exist; expect(auth.password).to.not.exist;
@ -319,23 +305,22 @@ describe('Auth unit tests', function() {
it('should work for Trust on first use', function(done) { it('should work for Trust on first use', function(done) {
auth.imap = {}; auth.imap = {};
storeCredentialsStub.yields(); storeCredentialsStub.returns(resolves());
function callback(err) { function callback() {
expect(err).to.not.exist;
expect(storeCredentialsStub.callCount).to.equal(1); expect(storeCredentialsStub.callCount).to.equal(1);
done(); done();
} }
auth.handleCertificateUpdate('imap', onConnectDummy, callback, dummyCert); auth.handleCertificateUpdate('imap', onConnectDummy, callback, dummyCert).then(callback);
}); });
it('should work for stored cert', function() { it('should work for stored cert', function() {
auth.imap = { auth.imap = {
ca: dummyCert ca: dummyCert
}; };
storeCredentialsStub.yields(); storeCredentialsStub.returns(resolves());
auth.handleCertificateUpdate('imap', onConnectDummy, onConnectDummy, dummyCert); auth.handleCertificateUpdate('imap', onConnectDummy, dummyCert);
expect(storeCredentialsStub.callCount).to.equal(0); expect(storeCredentialsStub.callCount).to.equal(0);
}); });
@ -344,7 +329,7 @@ describe('Auth unit tests', function() {
ca: 'other', ca: 'other',
pinned: true pinned: true
}; };
storeCredentialsStub.yields(); storeCredentialsStub.returns(resolves());
function callback(err) { function callback(err) {
expect(err).to.exist; expect(err).to.exist;
@ -352,50 +337,45 @@ describe('Auth unit tests', function() {
expect(storeCredentialsStub.callCount).to.equal(0); expect(storeCredentialsStub.callCount).to.equal(0);
done(); done();
} }
auth.handleCertificateUpdate('imap', onConnectDummy, callback, dummyCert); auth.handleCertificateUpdate('imap', onConnectDummy, dummyCert).catch(callback);
}); });
it('should work for updated cert', function(done) { it('should work for updated cert', function(done) {
auth.imap = { auth.imap = {
ca: 'other' ca: 'other'
}; };
storeCredentialsStub.yields(); storeCredentialsStub.returns(resolves());
function callback(err) { function callback(err) {
if (err && err.callback) { if (err && err.callback) {
expect(err).to.exist; expect(err).to.exist;
expect(err.message).to.exist; expect(err.message).to.exist;
expect(storeCredentialsStub.callCount).to.equal(0); expect(storeCredentialsStub.callCount).to.equal(0);
err.callback(true); err.callback(true).then(function() {
} else { expect(storeCredentialsStub.callCount).to.equal(1);
expect(storeCredentialsStub.callCount).to.equal(1); done();
done(); });
} }
} }
function onConnect(callback) { auth.handleCertificateUpdate('imap', onConnectDummy, dummyCert).catch(callback);
callback();
}
auth.handleCertificateUpdate('imap', onConnect, callback, dummyCert);
}); });
}); });
describe('#logout', function() { describe('#logout', function() {
it('should fail to to error in calling db clear', function(done) { it('should fail to to error in calling db clear', function(done) {
storageStub.clear.yields(new Error()); storageStub.clear.returns(rejects(new Error()));
auth.logout(function(err) { auth.logout().catch(function(err) {
expect(err).to.exist; expect(err).to.exist;
done(); done();
}); });
}); });
it('should work', function(done) { it('should work', function(done) {
storageStub.clear.yields(); storageStub.clear.returns(resolves());
auth.logout(function(err) { auth.logout().then(function() {
expect(err).to.not.exist;
expect(auth.password).to.be.undefined; expect(auth.password).to.be.undefined;
expect(auth.initialized).to.be.undefined; expect(auth.initialized).to.be.undefined;
expect(auth.credentialsDirty).to.be.undefined; expect(auth.credentialsDirty).to.be.undefined;