mirror of https://github.com/moparisthebest/mail
commit
37fb3f5f8d
95
.jshintrc
95
.jshintrc
|
@ -1,51 +1,52 @@
|
||||||
{
|
{
|
||||||
"indent": 4,
|
"indent": 4,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"globalstrict": true,
|
"globalstrict": true,
|
||||||
"node": true,
|
"node": true,
|
||||||
"browser": true,
|
"browser": true,
|
||||||
"nonew": true,
|
"nonew": true,
|
||||||
"curly": true,
|
"curly": true,
|
||||||
"eqeqeq": true,
|
"eqeqeq": true,
|
||||||
"immed": true,
|
"immed": true,
|
||||||
"newcap": true,
|
"newcap": true,
|
||||||
"regexp": true,
|
"regexp": true,
|
||||||
"evil": true,
|
"evil": true,
|
||||||
"eqnull": true,
|
"eqnull": true,
|
||||||
"expr": true,
|
"expr": true,
|
||||||
"trailing": true,
|
"trailing": true,
|
||||||
"undef": true,
|
"undef": true,
|
||||||
"unused": true,
|
"unused": true,
|
||||||
|
|
||||||
"predef": [
|
"predef": [
|
||||||
"$",
|
"$",
|
||||||
"inject",
|
"inject",
|
||||||
"Promise",
|
"Promise",
|
||||||
"self",
|
"resolves",
|
||||||
"importScripts",
|
"rejects",
|
||||||
"console",
|
"self",
|
||||||
"process",
|
"importScripts",
|
||||||
"chrome",
|
"console",
|
||||||
"Notification",
|
"process",
|
||||||
"Event",
|
"chrome",
|
||||||
"sinon",
|
"Notification",
|
||||||
"mocha",
|
"Event",
|
||||||
"chai",
|
"sinon",
|
||||||
"expect",
|
"mocha",
|
||||||
"describe",
|
"chai",
|
||||||
"it",
|
"expect",
|
||||||
"before",
|
"describe",
|
||||||
"beforeEach",
|
"it",
|
||||||
"after",
|
"before",
|
||||||
"afterEach",
|
"beforeEach",
|
||||||
"FastClick",
|
"after",
|
||||||
"angular",
|
"afterEach",
|
||||||
"forge",
|
"FastClick",
|
||||||
"Lawnchair",
|
"angular",
|
||||||
"_",
|
"forge",
|
||||||
"openpgp"
|
"Lawnchair",
|
||||||
],
|
"_",
|
||||||
|
"openpgp"
|
||||||
|
],
|
||||||
|
|
||||||
"globals": {
|
"globals": {}
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var AccountCtrl = function($scope, auth, keychain, pgp, appConfig, download, dialog) {
|
var AccountCtrl = function($scope, $q, auth, keychain, pgp, appConfig, download, dialog) {
|
||||||
var userId = auth.emailAddress;
|
var userId = auth.emailAddress;
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
return;
|
return;
|
||||||
|
@ -34,12 +34,13 @@ var AccountCtrl = function($scope, auth, keychain, pgp, appConfig, download, dia
|
||||||
//
|
//
|
||||||
|
|
||||||
$scope.exportKeyFile = function() {
|
$scope.exportKeyFile = function() {
|
||||||
keychain.getUserKeyPair(userId, function(err, keys) {
|
return $q(function(resolve) {
|
||||||
if (err) {
|
resolve();
|
||||||
dialog.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
}).then(function() {
|
||||||
|
return keychain.getUserKeyPair(userId);
|
||||||
|
|
||||||
|
}).then(function(keys) {
|
||||||
var keyId = keys.publicKey._id;
|
var keyId = keys.publicKey._id;
|
||||||
var file = 'whiteout_mail_' + userId + '_' + keyId.substring(8, keyId.length);
|
var file = 'whiteout_mail_' + userId + '_' + keyId.substring(8, keyId.length);
|
||||||
|
|
||||||
|
@ -48,7 +49,8 @@ var AccountCtrl = function($scope, auth, keychain, pgp, appConfig, download, dia
|
||||||
filename: file + '.asc',
|
filename: file + '.asc',
|
||||||
contentType: 'text/plain'
|
contentType: 'text/plain'
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
}).catch(dialog.error);
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
var JUNK_FOLDER_TYPE = 'Junk';
|
var JUNK_FOLDER_TYPE = 'Junk';
|
||||||
|
|
||||||
var ActionBarCtrl = function($scope, email, dialog, status) {
|
var ActionBarCtrl = function($scope, $q, email, dialog, status) {
|
||||||
|
|
||||||
//
|
//
|
||||||
// scope functions
|
// scope functions
|
||||||
|
@ -23,24 +23,28 @@ var ActionBarCtrl = function($scope, email, dialog, status) {
|
||||||
// show message
|
// show message
|
||||||
status.update('Moving message...');
|
status.update('Moving message...');
|
||||||
|
|
||||||
email.moveMessage({
|
return $q(function(resolve) {
|
||||||
folder: currentFolder(),
|
resolve();
|
||||||
destination: destination,
|
|
||||||
message: message
|
}).then(function() {
|
||||||
}, function(err) {
|
return email.moveMessage({
|
||||||
if (err) {
|
folder: currentFolder(),
|
||||||
// show errors where appropriate
|
destination: destination,
|
||||||
if (err.code === 42) {
|
message: message
|
||||||
$scope.select(message);
|
});
|
||||||
status.update('Unable to move message in offline mode!');
|
|
||||||
return;
|
}).then(function() {
|
||||||
}
|
status.update('Online');
|
||||||
status.update('Error during move!');
|
|
||||||
dialog.error(err);
|
}).catch(function(err) {
|
||||||
|
// show errors where appropriate
|
||||||
|
if (err.code === 42) {
|
||||||
|
$scope.select(message);
|
||||||
|
status.update('Unable to move message in offline mode!');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
status.update('Message moved.');
|
status.update('Error during move!');
|
||||||
$scope.$apply();
|
return dialog.error(err);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -83,23 +87,27 @@ var ActionBarCtrl = function($scope, email, dialog, status) {
|
||||||
status.setReading(false);
|
status.setReading(false);
|
||||||
status.update('Deleting message...');
|
status.update('Deleting message...');
|
||||||
|
|
||||||
email.deleteMessage({
|
return $q(function(resolve) {
|
||||||
folder: currentFolder(),
|
resolve();
|
||||||
message: message
|
|
||||||
}, function(err) {
|
}).then(function() {
|
||||||
if (err) {
|
return email.deleteMessage({
|
||||||
// show errors where appropriate
|
folder: currentFolder(),
|
||||||
if (err.code === 42) {
|
message: message
|
||||||
$scope.select(message);
|
});
|
||||||
status.update('Unable to delete message in offline mode!');
|
|
||||||
return;
|
}).then(function() {
|
||||||
}
|
status.update('Online');
|
||||||
status.update('Error during delete!');
|
|
||||||
dialog.error(err);
|
}).catch(function(err) {
|
||||||
|
// show errors where appropriate
|
||||||
|
if (err.code === 42) {
|
||||||
|
$scope.select(message);
|
||||||
|
status.update('Unable to delete message in offline mode!');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
status.update('Message deleted.');
|
status.update('Error during delete!');
|
||||||
$scope.$apply();
|
return dialog.error(err);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -130,25 +138,29 @@ var ActionBarCtrl = function($scope, email, dialog, status) {
|
||||||
|
|
||||||
var originalState = message.unread;
|
var originalState = message.unread;
|
||||||
message.unread = unread;
|
message.unread = unread;
|
||||||
email.setFlags({
|
|
||||||
folder: currentFolder(),
|
return $q(function(resolve) {
|
||||||
message: message
|
resolve();
|
||||||
}, function(err) {
|
|
||||||
if (err && err.code === 42) {
|
}).then(function() {
|
||||||
|
return email.setFlags({
|
||||||
|
folder: currentFolder(),
|
||||||
|
message: message
|
||||||
|
});
|
||||||
|
|
||||||
|
}).then(function() {
|
||||||
|
status.update('Online');
|
||||||
|
|
||||||
|
}).catch(function(err) {
|
||||||
|
if (err.code === 42) {
|
||||||
// offline, restore
|
// offline, restore
|
||||||
message.unread = originalState;
|
message.unread = originalState;
|
||||||
status.update('Unable to mark message in offline mode!');
|
status.update('Unable to mark message in offline mode!');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err) {
|
status.update('Error on sync!');
|
||||||
status.update('Error on sync!');
|
return dialog.error(err);
|
||||||
dialog.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
status.update('Online');
|
|
||||||
$scope.$apply();
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -176,25 +188,29 @@ var ActionBarCtrl = function($scope, email, dialog, status) {
|
||||||
|
|
||||||
var originalState = message.flagged;
|
var originalState = message.flagged;
|
||||||
message.flagged = flagged;
|
message.flagged = flagged;
|
||||||
email.setFlags({
|
|
||||||
folder: currentFolder(),
|
return $q(function(resolve) {
|
||||||
message: message
|
resolve();
|
||||||
}, function(err) {
|
|
||||||
if (err && err.code === 42) {
|
}).then(function() {
|
||||||
|
return email.setFlags({
|
||||||
|
folder: currentFolder(),
|
||||||
|
message: message
|
||||||
|
});
|
||||||
|
|
||||||
|
}).then(function() {
|
||||||
|
status.update('Online');
|
||||||
|
|
||||||
|
}).catch(function(err) {
|
||||||
|
if (err.code === 42) {
|
||||||
// offline, restore
|
// offline, restore
|
||||||
message.unread = originalState;
|
message.unread = originalState;
|
||||||
status.update('Unable to ' + (flagged ? 'add star to' : 'remove star from') + ' message in offline mode!');
|
status.update('Unable to ' + (flagged ? 'add star to' : 'remove star from') + ' message in offline mode!');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err) {
|
status.update('Error on sync!');
|
||||||
status.update('Error on sync!');
|
return dialog.error(err);
|
||||||
dialog.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
status.update('Online');
|
|
||||||
$scope.$apply();
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -208,12 +224,27 @@ var ActionBarCtrl = function($scope, email, dialog, status) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called when the user changes the searchText
|
||||||
|
*/
|
||||||
|
$scope.displaySearchResults = function(searchText) {
|
||||||
|
$scope.$root.$broadcast('search', searchText);
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// scope state
|
||||||
|
//
|
||||||
|
|
||||||
// share local scope functions with root state
|
// share local scope functions with root state
|
||||||
$scope.state.actionBar = {
|
$scope.state.actionBar = {
|
||||||
markMessage: $scope.markMessage,
|
markMessage: $scope.markMessage,
|
||||||
flagMessage: $scope.flagMessage
|
flagMessage: $scope.flagMessage
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Helper functions
|
||||||
|
//
|
||||||
|
|
||||||
function currentFolder() {
|
function currentFolder() {
|
||||||
return $scope.state.nav.currentFolder;
|
return $scope.state.nav.currentFolder;
|
||||||
}
|
}
|
||||||
|
@ -223,13 +254,6 @@ var ActionBarCtrl = function($scope, email, dialog, status) {
|
||||||
return message.checked;
|
return message.checked;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is called when the user changes the searchText
|
|
||||||
*/
|
|
||||||
$scope.displaySearchResults = function(searchText) {
|
|
||||||
$scope.$root.$broadcast('search', searchText);
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = ActionBarCtrl;
|
module.exports = ActionBarCtrl;
|
|
@ -4,7 +4,7 @@
|
||||||
// Controller
|
// Controller
|
||||||
//
|
//
|
||||||
|
|
||||||
var ContactsCtrl = function($scope, keychain, pgp, dialog) {
|
var ContactsCtrl = function($scope, $q, keychain, pgp, dialog) {
|
||||||
|
|
||||||
//
|
//
|
||||||
// scope state
|
// scope state
|
||||||
|
@ -13,7 +13,7 @@ var ContactsCtrl = function($scope, keychain, pgp, dialog) {
|
||||||
$scope.state.contacts = {
|
$scope.state.contacts = {
|
||||||
toggle: function(to) {
|
toggle: function(to) {
|
||||||
$scope.state.lightbox = (to) ? 'contacts' : undefined;
|
$scope.state.lightbox = (to) ? 'contacts' : undefined;
|
||||||
$scope.listKeys();
|
return $scope.listKeys();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -22,22 +22,21 @@ var ContactsCtrl = function($scope, keychain, pgp, dialog) {
|
||||||
//
|
//
|
||||||
|
|
||||||
$scope.listKeys = function() {
|
$scope.listKeys = function() {
|
||||||
keychain.listLocalPublicKeys(function(err, keys) {
|
return $q(function(resolve) {
|
||||||
if (err) {
|
resolve();
|
||||||
dialog.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
keys.forEach(addParams);
|
}).then(function() {
|
||||||
|
return keychain.listLocalPublicKeys();
|
||||||
|
|
||||||
$scope.keys = keys;
|
}).then(function(keys) {
|
||||||
$scope.$apply();
|
// add params to key objects
|
||||||
|
keys.forEach(function(key) {
|
||||||
function addParams(key) {
|
|
||||||
var params = pgp.getKeyParams(key.publicKey);
|
var params = pgp.getKeyParams(key.publicKey);
|
||||||
_.extend(key, params);
|
_.extend(key, params);
|
||||||
}
|
});
|
||||||
});
|
$scope.keys = keys;
|
||||||
|
|
||||||
|
}).catch(dialog.error);
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.getFingerprint = function(key) {
|
$scope.getFingerprint = function(key) {
|
||||||
|
@ -74,29 +73,18 @@ var ContactsCtrl = function($scope, keychain, pgp, dialog) {
|
||||||
imported: true // mark manually imported keys
|
imported: true // mark manually imported keys
|
||||||
};
|
};
|
||||||
|
|
||||||
keychain.saveLocalPublicKey(pubkey, function(err) {
|
return keychain.saveLocalPublicKey(pubkey).then(function() {
|
||||||
if (err) {
|
|
||||||
dialog.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update displayed keys
|
// update displayed keys
|
||||||
$scope.listKeys();
|
return $scope.listKeys();
|
||||||
});
|
}).catch(dialog.error);
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.removeKey = function(key) {
|
$scope.removeKey = function(key) {
|
||||||
keychain.removeLocalPublicKey(key._id, function(err) {
|
return keychain.removeLocalPublicKey(key._id).then(function() {
|
||||||
if (err) {
|
|
||||||
dialog.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update displayed keys
|
// update displayed keys
|
||||||
$scope.listKeys();
|
return $scope.listKeys();
|
||||||
});
|
}).catch(dialog.error);
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = ContactsCtrl;
|
module.exports = ContactsCtrl;
|
|
@ -40,6 +40,7 @@ var DialogCtrl = function($scope, dialog) {
|
||||||
$scope.positiveBtnStr = options.positiveBtnStr || 'Ok';
|
$scope.positiveBtnStr = options.positiveBtnStr || 'Ok';
|
||||||
$scope.negativeBtnStr = options.negativeBtnStr || 'Cancel';
|
$scope.negativeBtnStr = options.negativeBtnStr || 'Cancel';
|
||||||
$scope.showNegativeBtn = options.showNegativeBtn || false;
|
$scope.showNegativeBtn = options.showNegativeBtn || false;
|
||||||
|
$scope.showBugReporter = false;
|
||||||
$scope.callback = options.callback;
|
$scope.callback = options.callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ var INIT_DISPLAY_LEN = 50,
|
||||||
FOLDER_TYPE_INBOX = 'Inbox',
|
FOLDER_TYPE_INBOX = 'Inbox',
|
||||||
NOTIFICATION_INBOX_TIMEOUT = 5000;
|
NOTIFICATION_INBOX_TIMEOUT = 5000;
|
||||||
|
|
||||||
var MailListCtrl = function($scope, $timeout, $location, $filter, status, notification, email, keychain, dialog, search, dummy) {
|
var MailListCtrl = function($scope, $timeout, $location, $filter, $q, status, notification, email, keychain, dialog, search, dummy) {
|
||||||
|
|
||||||
//
|
//
|
||||||
// scope state
|
// scope state
|
||||||
|
@ -55,23 +55,26 @@ var MailListCtrl = function($scope, $timeout, $location, $filter, status, notifi
|
||||||
//
|
//
|
||||||
|
|
||||||
$scope.getBody = function(message) {
|
$scope.getBody = function(message) {
|
||||||
email.getBody({
|
return $q(function(resolve) {
|
||||||
folder: currentFolder(),
|
resolve();
|
||||||
message: message
|
|
||||||
}, function(err) {
|
|
||||||
if (err && err.code !== 42) {
|
|
||||||
dialog.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// display fetched body
|
}).then(function() {
|
||||||
$scope.$digest();
|
return email.getBody({
|
||||||
|
folder: currentFolder(),
|
||||||
|
message: message
|
||||||
|
});
|
||||||
|
|
||||||
|
}).then(function() {
|
||||||
// automatically decrypt if it's the selected message
|
// automatically decrypt if it's the selected message
|
||||||
if (message === currentMessage()) {
|
if (message === currentMessage()) {
|
||||||
email.decryptBody({
|
return email.decryptBody({
|
||||||
message: message
|
message: message
|
||||||
}, dialog.error);
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}).catch(function(err) {
|
||||||
|
if (err.code !== 42) {
|
||||||
|
dialog.error(err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -93,19 +96,20 @@ var MailListCtrl = function($scope, $timeout, $location, $filter, status, notifi
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
keychain.refreshKeyForUserId({
|
return $q(function(resolve) {
|
||||||
userId: message.from[0].address
|
resolve();
|
||||||
}, onKeyRefreshed);
|
|
||||||
|
|
||||||
function onKeyRefreshed(err) {
|
}).then(function() {
|
||||||
if (err) {
|
return keychain.refreshKeyForUserId({
|
||||||
dialog.error(err);
|
userId: message.from[0].address
|
||||||
}
|
});
|
||||||
|
|
||||||
email.decryptBody({
|
}).then(function() {
|
||||||
|
return email.decryptBody({
|
||||||
message: message
|
message: message
|
||||||
}, dialog.error);
|
});
|
||||||
|
|
||||||
|
}).then(function() {
|
||||||
// if the message is unread, please sync the new state.
|
// if the message is unread, please sync the new state.
|
||||||
// otherweise forget about it.
|
// otherweise forget about it.
|
||||||
if (!message.unread) {
|
if (!message.unread) {
|
||||||
|
@ -119,12 +123,13 @@ var MailListCtrl = function($scope, $timeout, $location, $filter, status, notifi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.state.actionBar.markMessage(message, false, true);
|
return $scope.state.actionBar.markMessage(message, false, true);
|
||||||
}
|
|
||||||
|
}).catch(dialog.error);
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.flag = function(message, flagged) {
|
$scope.flag = function(message, flagged) {
|
||||||
$scope.state.actionBar.flagMessage(message, flagged);
|
return $scope.state.actionBar.flagMessage(message, flagged);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -164,7 +169,7 @@ var MailListCtrl = function($scope, $timeout, $location, $filter, status, notifi
|
||||||
}
|
}
|
||||||
|
|
||||||
// display and select first
|
// display and select first
|
||||||
openCurrentFolder();
|
return openCurrentFolder();
|
||||||
});
|
});
|
||||||
|
|
||||||
$scope.watchMessages = $scope.$watchCollection('state.nav.currentFolder.messages', function(messages) {
|
$scope.watchMessages = $scope.$watchCollection('state.nav.currentFolder.messages', function(messages) {
|
||||||
|
@ -246,10 +251,10 @@ var MailListCtrl = function($scope, $timeout, $location, $filter, status, notifi
|
||||||
*/
|
*/
|
||||||
$scope.watchOnline = $scope.$watch('account.online', function(isOnline) {
|
$scope.watchOnline = $scope.$watch('account.online', function(isOnline) {
|
||||||
// wait one cycle for the status display controllers to init
|
// wait one cycle for the status display controllers to init
|
||||||
$timeout(function() {
|
return $timeout(function() {
|
||||||
if (isOnline) {
|
if (isOnline) {
|
||||||
status.update('Online');
|
status.update('Online');
|
||||||
openCurrentFolder();
|
return openCurrentFolder();
|
||||||
} else {
|
} else {
|
||||||
status.update('Offline mode');
|
status.update('Offline mode');
|
||||||
}
|
}
|
||||||
|
@ -265,18 +270,24 @@ var MailListCtrl = function($scope, $timeout, $location, $filter, status, notifi
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
email.openFolder({
|
return $q(function(resolve) {
|
||||||
folder: currentFolder()
|
resolve();
|
||||||
}, function(error) {
|
|
||||||
|
}).then(function() {
|
||||||
|
return email.openFolder({
|
||||||
|
folder: currentFolder()
|
||||||
|
}).catch(function(err) {
|
||||||
|
// don't display err for offline case
|
||||||
|
if (err.code !== 42) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}).then(function() {
|
||||||
// dont wait until scroll to load visible mail bodies
|
// dont wait until scroll to load visible mail bodies
|
||||||
$scope.loadVisibleBodies();
|
$scope.loadVisibleBodies();
|
||||||
|
|
||||||
// don't display error for offline case
|
}).catch(dialog.error);
|
||||||
if (error && error.code === 42) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
dialog.error(error);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function currentFolder() {
|
function currentFolder() {
|
||||||
|
|
|
@ -11,7 +11,7 @@ var NOTIFICATION_SENT_TIMEOUT = 2000;
|
||||||
// Controller
|
// Controller
|
||||||
//
|
//
|
||||||
|
|
||||||
var NavigationCtrl = function($scope, $location, account, email, outbox, notification, appConfig, dialog, dummy) {
|
var NavigationCtrl = function($scope, $location, $q, account, email, outbox, notification, appConfig, dialog, dummy) {
|
||||||
if (!$location.search().dev && !account.isLoggedIn()) {
|
if (!$location.search().dev && !account.isLoggedIn()) {
|
||||||
$location.path('/'); // init app
|
$location.path('/'); // init app
|
||||||
return;
|
return;
|
||||||
|
@ -102,18 +102,24 @@ var NavigationCtrl = function($scope, $location, account, email, outbox, notific
|
||||||
});
|
});
|
||||||
ob.count = count;
|
ob.count = count;
|
||||||
|
|
||||||
email.refreshFolder({
|
return $q(function(resolve) {
|
||||||
folder: ob
|
resolve();
|
||||||
}, dialog.error);
|
|
||||||
|
}).then(function() {
|
||||||
|
return email.refreshFolder({
|
||||||
|
folder: ob
|
||||||
|
});
|
||||||
|
|
||||||
|
}).catch(dialog.error);
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.logout = function() {
|
$scope.logout = function() {
|
||||||
dialog.confirm({
|
return dialog.confirm({
|
||||||
title: str.logoutTitle,
|
title: str.logoutTitle,
|
||||||
message: str.logoutMessage,
|
message: str.logoutMessage,
|
||||||
callback: function(confirm) {
|
callback: function(confirm) {
|
||||||
if (confirm) {
|
if (confirm) {
|
||||||
account.logout();
|
account.logout().catch(dialog.error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
var util = require('crypto-lib').util;
|
var util = require('crypto-lib').util;
|
||||||
|
|
||||||
var PrivateKeyUploadCtrl = function($scope, keychain, pgp, dialog, auth) {
|
var PrivateKeyUploadCtrl = function($scope, $q, keychain, pgp, dialog, auth) {
|
||||||
|
|
||||||
//
|
//
|
||||||
// scope state
|
// scope state
|
||||||
|
@ -19,16 +19,15 @@ var PrivateKeyUploadCtrl = function($scope, keychain, pgp, dialog, auth) {
|
||||||
// show syncing status
|
// show syncing status
|
||||||
$scope.step = 4;
|
$scope.step = 4;
|
||||||
// check if key is already synced
|
// check if key is already synced
|
||||||
$scope.checkServerForKey(function(privateKeySynced) {
|
return $scope.checkServerForKey().then(function(privateKeySynced) {
|
||||||
if (privateKeySynced) {
|
if (privateKeySynced) {
|
||||||
// close lightbox
|
// close lightbox
|
||||||
$scope.state.lightbox = undefined;
|
$scope.state.lightbox = undefined;
|
||||||
// show message
|
// show message
|
||||||
dialog.info({
|
return dialog.info({
|
||||||
title: 'Info',
|
title: 'Info',
|
||||||
message: 'Your PGP key has already been synced.'
|
message: 'Your PGP key has already been synced.'
|
||||||
});
|
});
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// show sync ui if key is not synced
|
// show sync ui if key is not synced
|
||||||
|
@ -41,24 +40,22 @@ var PrivateKeyUploadCtrl = function($scope, keychain, pgp, dialog, auth) {
|
||||||
// scope functions
|
// scope functions
|
||||||
//
|
//
|
||||||
|
|
||||||
$scope.checkServerForKey = function(callback) {
|
$scope.checkServerForKey = function() {
|
||||||
var keyParams = pgp.getKeyParams();
|
var keyParams = pgp.getKeyParams();
|
||||||
keychain.hasPrivateKey({
|
|
||||||
userId: keyParams.userId,
|
|
||||||
keyId: keyParams._id
|
|
||||||
}, function(err, privateKeySynced) {
|
|
||||||
if (err) {
|
|
||||||
dialog.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (privateKeySynced) {
|
return $q(function(resolve) {
|
||||||
callback(privateKeySynced);
|
resolve();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
callback();
|
}).then(function() {
|
||||||
});
|
return keychain.hasPrivateKey({
|
||||||
|
userId: keyParams.userId,
|
||||||
|
keyId: keyParams._id
|
||||||
|
});
|
||||||
|
|
||||||
|
}).then(function(privateKeySynced) {
|
||||||
|
return privateKeySynced ? privateKeySynced : undefined;
|
||||||
|
|
||||||
|
}).catch(dialog.error);
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.displayUploadUi = function() {
|
$scope.displayUploadUi = function() {
|
||||||
|
@ -82,29 +79,37 @@ var PrivateKeyUploadCtrl = function($scope, keychain, pgp, dialog, auth) {
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.setDeviceName = function(callback) {
|
$scope.setDeviceName = function() {
|
||||||
keychain.setDeviceName($scope.deviceName, callback);
|
return $q(function(resolve) {
|
||||||
|
resolve();
|
||||||
|
|
||||||
|
}).then(function() {
|
||||||
|
return keychain.setDeviceName($scope.deviceName);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.encryptAndUploadKey = function(callback) {
|
$scope.encryptAndUploadKey = function() {
|
||||||
var userId = auth.emailAddress;
|
var userId = auth.emailAddress;
|
||||||
var code = $scope.code;
|
var code = $scope.code;
|
||||||
|
|
||||||
// register device to keychain service
|
// register device to keychain service
|
||||||
keychain.registerDevice({
|
return $q(function(resolve) {
|
||||||
userId: userId
|
resolve();
|
||||||
}, function(err) {
|
|
||||||
if (err) {
|
|
||||||
dialog.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
}).then(function() {
|
||||||
|
// register the device
|
||||||
|
return keychain.registerDevice({
|
||||||
|
userId: userId
|
||||||
|
});
|
||||||
|
|
||||||
|
}).then(function() {
|
||||||
// encrypt private PGP key using code and upload
|
// encrypt private PGP key using code and upload
|
||||||
keychain.uploadPrivateKey({
|
return keychain.uploadPrivateKey({
|
||||||
userId: userId,
|
userId: userId,
|
||||||
code: code
|
code: code
|
||||||
}, callback);
|
});
|
||||||
});
|
|
||||||
|
}).catch(dialog.error);
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.goBack = function() {
|
$scope.goBack = function() {
|
||||||
|
@ -126,32 +131,22 @@ var PrivateKeyUploadCtrl = function($scope, keychain, pgp, dialog, auth) {
|
||||||
|
|
||||||
if ($scope.step === 3) {
|
if ($scope.step === 3) {
|
||||||
// set device name to local storage
|
// set device name to local storage
|
||||||
$scope.setDeviceName(function(err) {
|
return $scope.setDeviceName().then(function() {
|
||||||
if (err) {
|
|
||||||
dialog.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// show spinner
|
// show spinner
|
||||||
$scope.step++;
|
$scope.step++;
|
||||||
$scope.$apply();
|
|
||||||
|
|
||||||
// init key sync
|
// init key sync
|
||||||
$scope.encryptAndUploadKey(function(err) {
|
return $scope.encryptAndUploadKey();
|
||||||
if (err) {
|
|
||||||
dialog.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// close sync dialog
|
}).then(function() {
|
||||||
$scope.state.privateKeyUpload.toggle(false);
|
// close sync dialog
|
||||||
// show success message
|
$scope.state.privateKeyUpload.toggle(false);
|
||||||
dialog.info({
|
// show success message
|
||||||
title: 'Success',
|
dialog.info({
|
||||||
message: 'Whiteout Keychain setup successful!'
|
title: 'Success',
|
||||||
});
|
message: 'Whiteout Keychain setup successful!'
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
}).catch(dialog.error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
// Controller
|
// Controller
|
||||||
//
|
//
|
||||||
|
|
||||||
var ReadCtrl = function($scope, $location, email, invitation, outbox, pgp, keychain, appConfig, download, auth, dialog) {
|
var ReadCtrl = function($scope, $location, $q, email, invitation, outbox, pgp, keychain, appConfig, download, auth, dialog) {
|
||||||
|
|
||||||
var str = appConfig.string;
|
var str = appConfig.string;
|
||||||
|
|
||||||
|
@ -52,25 +52,24 @@ var ReadCtrl = function($scope, $location, email, invitation, outbox, pgp, keych
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.keyId = 'Searching...';
|
return $q(function(resolve) {
|
||||||
keychain.getReceiverPublicKey(address, function(err, pubkey) {
|
$scope.keyId = 'Searching...';
|
||||||
if (err) {
|
resolve();
|
||||||
dialog.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
}).then(function() {
|
||||||
|
return keychain.getReceiverPublicKey(address);
|
||||||
|
|
||||||
|
}).then(function(pubkey) {
|
||||||
if (!pubkey) {
|
if (!pubkey) {
|
||||||
$scope.keyId = 'User has no key. Click to invite.';
|
$scope.keyId = 'User has no key. Click to invite.';
|
||||||
$scope.$apply();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var fpr = pgp.getFingerprint(pubkey.publicKey);
|
var fpr = pgp.getFingerprint(pubkey.publicKey);
|
||||||
var formatted = fpr.slice(32);
|
var formatted = fpr.slice(32);
|
||||||
|
|
||||||
$scope.keyId = 'PGP key: ' + formatted;
|
$scope.keyId = 'PGP key: ' + formatted;
|
||||||
$scope.$apply();
|
|
||||||
});
|
}).catch(dialog.error);
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.$watch('state.mailList.selected', function(mail) {
|
$scope.$watch('state.mailList.selected', function(mail) {
|
||||||
|
@ -89,24 +88,20 @@ var ReadCtrl = function($scope, $location, email, invitation, outbox, pgp, keych
|
||||||
function checkPublicKey(user) {
|
function checkPublicKey(user) {
|
||||||
user.secure = undefined;
|
user.secure = undefined;
|
||||||
|
|
||||||
if (!keychain) {
|
return $q(function(resolve) {
|
||||||
return;
|
resolve();
|
||||||
}
|
|
||||||
|
|
||||||
keychain.getReceiverPublicKey(user.address, function(err, pubkey) {
|
}).then(function() {
|
||||||
if (err) {
|
return keychain.getReceiverPublicKey(user.address);
|
||||||
dialog.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
}).then(function(pubkey) {
|
||||||
if (pubkey && pubkey.publicKey) {
|
if (pubkey && pubkey.publicKey) {
|
||||||
user.secure = true;
|
user.secure = true;
|
||||||
} else {
|
} else {
|
||||||
user.secure = false;
|
user.secure = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.$apply();
|
}).catch(dialog.error);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.download = function(attachment) {
|
$scope.download = function(attachment) {
|
||||||
|
@ -122,11 +117,18 @@ var ReadCtrl = function($scope, $location, email, invitation, outbox, pgp, keych
|
||||||
|
|
||||||
var folder = $scope.state.nav.currentFolder;
|
var folder = $scope.state.nav.currentFolder;
|
||||||
var message = $scope.state.mailList.selected;
|
var message = $scope.state.mailList.selected;
|
||||||
email.getAttachment({
|
|
||||||
folder: folder,
|
return $q(function(resolve) {
|
||||||
uid: message.uid,
|
resolve();
|
||||||
attachment: attachment
|
|
||||||
}, dialog.error);
|
}).then(function() {
|
||||||
|
return email.getAttachment({
|
||||||
|
folder: folder,
|
||||||
|
uid: message.uid,
|
||||||
|
attachment: attachment
|
||||||
|
});
|
||||||
|
|
||||||
|
}).catch(dialog.error);
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.invite = function(user) {
|
$scope.invite = function(user) {
|
||||||
|
@ -135,20 +137,20 @@ var ReadCtrl = function($scope, $location, email, invitation, outbox, pgp, keych
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.keyId = 'Sending invitation...';
|
|
||||||
|
|
||||||
var sender = auth.emailAddress,
|
var sender = auth.emailAddress,
|
||||||
recipient = user.address;
|
recipient = user.address;
|
||||||
|
|
||||||
invitation.invite({
|
return $q(function(resolve) {
|
||||||
recipient: recipient,
|
$scope.keyId = 'Sending invitation...';
|
||||||
sender: sender
|
resolve();
|
||||||
}, function(err) {
|
|
||||||
if (err) {
|
|
||||||
dialog.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
}).then(function() {
|
||||||
|
return invitation.invite({
|
||||||
|
recipient: recipient,
|
||||||
|
sender: sender
|
||||||
|
});
|
||||||
|
|
||||||
|
}).then(function() {
|
||||||
var invitationMail = {
|
var invitationMail = {
|
||||||
from: [{
|
from: [{
|
||||||
address: sender
|
address: sender
|
||||||
|
@ -161,10 +163,10 @@ var ReadCtrl = function($scope, $location, email, invitation, outbox, pgp, keych
|
||||||
subject: str.invitationSubject,
|
subject: str.invitationSubject,
|
||||||
body: str.invitationMessage
|
body: str.invitationMessage
|
||||||
};
|
};
|
||||||
|
|
||||||
// send invitation mail
|
// send invitation mail
|
||||||
outbox.put(invitationMail, dialog.error);
|
return outbox.put(invitationMail);
|
||||||
});
|
|
||||||
|
}).catch(dialog.error);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var SetPassphraseCtrl = function($scope, pgp, keychain, dialog) {
|
var SetPassphraseCtrl = function($scope, $q, pgp, keychain, dialog) {
|
||||||
|
|
||||||
//
|
//
|
||||||
// scope variables
|
// scope variables
|
||||||
|
@ -76,52 +76,44 @@ var SetPassphraseCtrl = function($scope, pgp, keychain, dialog) {
|
||||||
|
|
||||||
$scope.setPassphrase = function() {
|
$scope.setPassphrase = function() {
|
||||||
var keyId = pgp.getKeyParams()._id;
|
var keyId = pgp.getKeyParams()._id;
|
||||||
keychain.lookupPrivateKey(keyId, function(err, savedKey) {
|
|
||||||
if (err) {
|
|
||||||
dialog.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
pgp.changePassphrase({
|
return $q(function(resolve) {
|
||||||
|
resolve();
|
||||||
|
|
||||||
|
}).then(function() {
|
||||||
|
return keychain.lookupPrivateKey(keyId);
|
||||||
|
|
||||||
|
}).then(function(savedKey) {
|
||||||
|
// change passphrase
|
||||||
|
return pgp.changePassphrase({
|
||||||
privateKeyArmored: savedKey.encryptedKey,
|
privateKeyArmored: savedKey.encryptedKey,
|
||||||
oldPassphrase: $scope.oldPassphrase,
|
oldPassphrase: $scope.oldPassphrase,
|
||||||
newPassphrase: $scope.newPassphrase
|
newPassphrase: $scope.newPassphrase
|
||||||
}, onPassphraseChanged);
|
}).catch(function(err) {
|
||||||
});
|
err.showBugReporter = false;
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
|
||||||
|
}).then(function(newPrivateKeyArmored) {
|
||||||
|
// persist new armored key
|
||||||
|
var keyParams = pgp.getKeyParams(newPrivateKeyArmored);
|
||||||
|
var privateKey = {
|
||||||
|
_id: keyParams._id,
|
||||||
|
userId: keyParams.userId,
|
||||||
|
userIds: keyParams.userIds,
|
||||||
|
encryptedKey: newPrivateKeyArmored
|
||||||
|
};
|
||||||
|
return keychain.saveLocalPrivateKey(privateKey);
|
||||||
|
|
||||||
|
}).then(function() {
|
||||||
|
$scope.state.setPassphrase.toggle(false);
|
||||||
|
return dialog.info({
|
||||||
|
title: 'Success',
|
||||||
|
message: 'Passphrase change complete.'
|
||||||
|
});
|
||||||
|
|
||||||
|
}).catch(dialog.error);
|
||||||
};
|
};
|
||||||
|
|
||||||
function onPassphraseChanged(err, newPrivateKeyArmored) {
|
|
||||||
if (err) {
|
|
||||||
err.showBugReporter = false;
|
|
||||||
dialog.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// persist new armored key
|
|
||||||
var keyParams = pgp.getKeyParams(newPrivateKeyArmored);
|
|
||||||
var privateKey = {
|
|
||||||
_id: keyParams._id,
|
|
||||||
userId: keyParams.userId,
|
|
||||||
userIds: keyParams.userIds,
|
|
||||||
encryptedKey: newPrivateKeyArmored
|
|
||||||
};
|
|
||||||
|
|
||||||
keychain.saveLocalPrivateKey(privateKey, onKeyPersisted);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onKeyPersisted(err) {
|
|
||||||
if (err) {
|
|
||||||
dialog.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.state.setPassphrase.toggle(false);
|
|
||||||
$scope.$apply();
|
|
||||||
dialog.info({
|
|
||||||
title: 'Success',
|
|
||||||
message: 'Passphrase change complete.'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = SetPassphraseCtrl;
|
module.exports = SetPassphraseCtrl;
|
|
@ -218,34 +218,31 @@ var WriteCtrl = function($scope, $window, $filter, $q, appConfig, auth, keychain
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// keychain is undefined in local dev environment
|
// check if to address is contained in known public keys
|
||||||
if (keychain) {
|
// when we write an email, we always need to work with the latest keys available
|
||||||
// check if to address is contained in known public keys
|
return $q(function(resolve) {
|
||||||
// when we write an email, we always need to work with the latest keys available
|
resolve();
|
||||||
keychain.refreshKeyForUserId({
|
|
||||||
|
}).then(function() {
|
||||||
|
return keychain.refreshKeyForUserId({
|
||||||
userId: recipient.address
|
userId: recipient.address
|
||||||
}, function(err, key) {
|
|
||||||
if (err) {
|
|
||||||
dialog.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key) {
|
|
||||||
// compare again since model could have changed during the roundtrip
|
|
||||||
var matchingUserId = _.findWhere(key.userIds, {
|
|
||||||
emailAddress: recipient.address
|
|
||||||
});
|
|
||||||
// compare either primary userId or (if available) multiple IDs
|
|
||||||
if (key.userId === recipient.address || matchingUserId) {
|
|
||||||
recipient.key = key;
|
|
||||||
recipient.secure = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.checkSendStatus();
|
|
||||||
$scope.$digest();
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
}).then(function(key) {
|
||||||
|
if (key) {
|
||||||
|
// compare again since model could have changed during the roundtrip
|
||||||
|
var matchingUserId = _.findWhere(key.userIds, {
|
||||||
|
emailAddress: recipient.address
|
||||||
|
});
|
||||||
|
// compare either primary userId or (if available) multiple IDs
|
||||||
|
if (key.userId === recipient.address || matchingUserId) {
|
||||||
|
recipient.key = key;
|
||||||
|
recipient.secure = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$scope.checkSendStatus();
|
||||||
|
|
||||||
|
}).catch(dialog.error);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -347,12 +344,13 @@ var WriteCtrl = function($scope, $window, $filter, $q, appConfig, auth, keychain
|
||||||
}
|
}
|
||||||
|
|
||||||
// persist the email to disk for later sending
|
// persist the email to disk for later sending
|
||||||
outbox.put(message, function(err) {
|
return $q(function(resolve) {
|
||||||
if (err) {
|
resolve();
|
||||||
dialog.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
}).then(function() {
|
||||||
|
return outbox.put(message);
|
||||||
|
|
||||||
|
}).then(function() {
|
||||||
// if we need to synchronize replyTo.answered = true to imap,
|
// if we need to synchronize replyTo.answered = true to imap,
|
||||||
// let's do that. otherwise, we're done
|
// let's do that. otherwise, we're done
|
||||||
if (!$scope.replyTo || $scope.replyTo.answered) {
|
if (!$scope.replyTo || $scope.replyTo.answered) {
|
||||||
|
@ -360,20 +358,16 @@ var WriteCtrl = function($scope, $window, $filter, $q, appConfig, auth, keychain
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.replyTo.answered = true;
|
$scope.replyTo.answered = true;
|
||||||
email.setFlags({
|
return email.setFlags({
|
||||||
folder: currentFolder(),
|
folder: currentFolder(),
|
||||||
message: $scope.replyTo
|
message: $scope.replyTo
|
||||||
}, function(err) {
|
|
||||||
if (err && err.code !== 42) {
|
|
||||||
dialog.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// offline or no error, let's apply the ui changes
|
|
||||||
$scope.$apply();
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
|
}).catch(function(err) {
|
||||||
|
if (err.code !== 42) {
|
||||||
|
dialog.error(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -389,37 +383,29 @@ var WriteCtrl = function($scope, $window, $filter, $q, appConfig, auth, keychain
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.lookupAddressBook = function(query) {
|
$scope.lookupAddressBook = function(query) {
|
||||||
var deferred = $q.defer();
|
return $q(function(resolve) {
|
||||||
|
resolve();
|
||||||
|
|
||||||
if (!$scope.addressBookCache) {
|
}).then(function() {
|
||||||
|
if ($scope.addressBookCache) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// populate address book cache
|
// populate address book cache
|
||||||
keychain.listLocalPublicKeys(function(err, keys) {
|
return keychain.listLocalPublicKeys().then(function(keys) {
|
||||||
if (err) {
|
|
||||||
dialog.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.addressBookCache = keys.map(function(key) {
|
$scope.addressBookCache = keys.map(function(key) {
|
||||||
return {
|
return {
|
||||||
address: key.userId
|
address: key.userId
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
filter();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
} else {
|
}).then(function() {
|
||||||
filter();
|
// filter the address book cache
|
||||||
}
|
return $scope.addressBookCache.filter(function(i) {
|
||||||
|
|
||||||
// query address book cache
|
|
||||||
function filter() {
|
|
||||||
var addresses = $scope.addressBookCache.filter(function(i) {
|
|
||||||
return i.address.indexOf(query) !== -1;
|
return i.address.indexOf(query) !== -1;
|
||||||
});
|
});
|
||||||
deferred.resolve(addresses);
|
|
||||||
}
|
|
||||||
|
|
||||||
return deferred.promise;
|
}).catch(dialog.error);
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var AddAccountCtrl = function($scope, $location, $routeParams, mailConfig, auth, dialog) {
|
var AddAccountCtrl = function($scope, $location, $routeParams, $timeout, $q, mailConfig, auth, dialog) {
|
||||||
!$routeParams.dev && !auth.isInitialized() && $location.path('/'); // init app
|
!$routeParams.dev && !auth.isInitialized() && $location.path('/'); // init app
|
||||||
|
|
||||||
$scope.getAccountSettings = function() {
|
$scope.getAccountSettings = function() {
|
||||||
|
@ -9,10 +9,15 @@ var AddAccountCtrl = function($scope, $location, $routeParams, mailConfig, auth,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.busy = true;
|
return $q(function(resolve) {
|
||||||
$scope.errMsg = undefined; // reset error msg
|
$scope.busy = true;
|
||||||
|
$scope.errMsg = undefined; // reset error msg
|
||||||
|
resolve();
|
||||||
|
|
||||||
return mailConfig.get($scope.emailAddress).then(function(config) {
|
}).then(function() {
|
||||||
|
return mailConfig.get($scope.emailAddress);
|
||||||
|
|
||||||
|
}).then(function(config) {
|
||||||
$scope.busy = false;
|
$scope.busy = false;
|
||||||
$scope.state.login = {
|
$scope.state.login = {
|
||||||
mailConfig: config,
|
mailConfig: config,
|
||||||
|
@ -22,10 +27,10 @@ var AddAccountCtrl = function($scope, $location, $routeParams, mailConfig, auth,
|
||||||
var hostname = config.imap.hostname;
|
var hostname = config.imap.hostname;
|
||||||
if (auth.useOAuth(hostname)) {
|
if (auth.useOAuth(hostname)) {
|
||||||
// check for oauth support
|
// check for oauth support
|
||||||
$scope.oauthPossible();
|
return $scope.oauthPossible();
|
||||||
} else {
|
} else {
|
||||||
// use standard password login
|
// use standard password login
|
||||||
$scope.setCredentials();
|
return $scope.setCredentials();
|
||||||
}
|
}
|
||||||
|
|
||||||
}).catch(function() {
|
}).catch(function() {
|
||||||
|
@ -36,7 +41,7 @@ var AddAccountCtrl = function($scope, $location, $routeParams, mailConfig, auth,
|
||||||
|
|
||||||
$scope.oauthPossible = function() {
|
$scope.oauthPossible = function() {
|
||||||
// ask user to use the platform's native OAuth api
|
// ask user to use the platform's native OAuth api
|
||||||
dialog.confirm({
|
return dialog.confirm({
|
||||||
title: 'Google Account Login',
|
title: 'Google Account Login',
|
||||||
message: 'You are signing into a Google account. Would you like to sign in with Google or just continue with a password login?',
|
message: 'You are signing into a Google account. Would you like to sign in with Google or just continue with a password login?',
|
||||||
positiveBtnStr: 'Google sign in',
|
positiveBtnStr: 'Google sign in',
|
||||||
|
@ -46,29 +51,28 @@ var AddAccountCtrl = function($scope, $location, $routeParams, mailConfig, auth,
|
||||||
callback: function(granted) {
|
callback: function(granted) {
|
||||||
if (granted) {
|
if (granted) {
|
||||||
// query oauth token
|
// query oauth token
|
||||||
getOAuthToken();
|
return getOAuthToken();
|
||||||
} else {
|
} else {
|
||||||
// use normal user/password login
|
// use normal user/password login
|
||||||
$scope.setCredentials();
|
$scope.setCredentials();
|
||||||
$scope.$apply();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function getOAuthToken() {
|
function getOAuthToken() {
|
||||||
// fetches the email address from the chrome identity api
|
// fetches the email address from the chrome identity api
|
||||||
auth.getOAuthToken(function(err) {
|
return auth.getOAuthToken().then(function() {
|
||||||
if (err) {
|
// continue to setting credentials
|
||||||
return dialog.error(err);
|
return $scope.setCredentials();
|
||||||
}
|
|
||||||
$scope.setCredentials();
|
}).catch(dialog.error);
|
||||||
$scope.$apply();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.setCredentials = function() {
|
$scope.setCredentials = function() {
|
||||||
$location.path('/login-set-credentials');
|
return $timeout(function() {
|
||||||
|
$location.path('/login-set-credentials');
|
||||||
|
});
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var CreateAccountCtrl = function($scope, $location, $routeParams, auth, admin, appConfig) {
|
var CreateAccountCtrl = function($scope, $location, $routeParams, $q, auth, admin, appConfig) {
|
||||||
!$routeParams.dev && !auth.isInitialized() && $location.path('/'); // init app
|
!$routeParams.dev && !auth.isInitialized() && $location.path('/'); // init app
|
||||||
|
|
||||||
$scope.createWhiteoutAccount = function() {
|
$scope.createWhiteoutAccount = function() {
|
||||||
|
@ -9,35 +9,37 @@ var CreateAccountCtrl = function($scope, $location, $routeParams, auth, admin, a
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.busy = true;
|
|
||||||
$scope.errMsg = undefined; // reset error msg
|
|
||||||
var emailAddress = $scope.user + '@' + appConfig.config.wmailDomain;
|
var emailAddress = $scope.user + '@' + appConfig.config.wmailDomain;
|
||||||
|
|
||||||
// set to state for next view
|
return $q(function(resolve) {
|
||||||
auth.setCredentials({
|
$scope.busy = true;
|
||||||
emailAddress: emailAddress,
|
$scope.errMsg = undefined; // reset error msg
|
||||||
password: $scope.pass,
|
resolve();
|
||||||
realname: $scope.realname
|
|
||||||
});
|
|
||||||
|
|
||||||
// call REST api
|
}).then(function() {
|
||||||
admin.createUser({
|
// set to state for next view
|
||||||
emailAddress: emailAddress,
|
auth.setCredentials({
|
||||||
password: $scope.pass,
|
emailAddress: emailAddress,
|
||||||
phone: $scope.phone.replace(/\s+/g, ''), // remove spaces from the phone number
|
password: $scope.pass,
|
||||||
betaCode: $scope.betaCode.toUpperCase()
|
realname: $scope.realname
|
||||||
}, function(err) {
|
});
|
||||||
|
|
||||||
|
// call REST api
|
||||||
|
return admin.createUser({
|
||||||
|
emailAddress: emailAddress,
|
||||||
|
password: $scope.pass,
|
||||||
|
phone: $scope.phone.replace(/\s+/g, ''), // remove spaces from the phone number
|
||||||
|
betaCode: $scope.betaCode.toUpperCase()
|
||||||
|
});
|
||||||
|
|
||||||
|
}).then(function() {
|
||||||
$scope.busy = false;
|
$scope.busy = false;
|
||||||
|
|
||||||
if (err) {
|
|
||||||
$scope.errMsg = err.errMsg || err.message;
|
|
||||||
$scope.$apply();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// proceed to login and keygen
|
// proceed to login and keygen
|
||||||
$location.path('/validate-phone');
|
$location.path('/validate-phone');
|
||||||
$scope.$apply();
|
|
||||||
|
}).catch(function(err) {
|
||||||
|
$scope.busy = false;
|
||||||
|
$scope.errMsg = err.errMsg || err.message;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var LoginExistingCtrl = function($scope, $location, $routeParams, email, auth, keychain) {
|
var LoginExistingCtrl = function($scope, $location, $routeParams, $q, email, auth, keychain) {
|
||||||
!$routeParams.dev && !auth.isInitialized() && $location.path('/'); // init app
|
!$routeParams.dev && !auth.isInitialized() && $location.path('/'); // init app
|
||||||
|
|
||||||
$scope.confirmPassphrase = function() {
|
$scope.confirmPassphrase = function() {
|
||||||
|
@ -9,50 +9,39 @@ var LoginExistingCtrl = function($scope, $location, $routeParams, email, auth, k
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.busy = true;
|
return $q(function(resolve) {
|
||||||
$scope.errMsg = undefined;
|
$scope.busy = true;
|
||||||
$scope.incorrect = false;
|
$scope.errMsg = undefined;
|
||||||
|
$scope.incorrect = false;
|
||||||
|
resolve();
|
||||||
|
|
||||||
unlockCrypto();
|
}).then(function() {
|
||||||
};
|
// key keypair
|
||||||
|
var userId = auth.emailAddress;
|
||||||
|
return keychain.getUserKeyPair(userId);
|
||||||
|
|
||||||
function unlockCrypto() {
|
}).then(function(keypair) {
|
||||||
var userId = auth.emailAddress;
|
// unlock email service
|
||||||
keychain.getUserKeyPair(userId, function(err, keypair) {
|
return email.unlock({
|
||||||
if (err) {
|
|
||||||
displayError(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
email.unlock({
|
|
||||||
keypair: keypair,
|
keypair: keypair,
|
||||||
passphrase: $scope.passphrase
|
passphrase: $scope.passphrase
|
||||||
}, onUnlock);
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function onUnlock(err) {
|
}).then(function() {
|
||||||
if (err) {
|
// persist credentials locally
|
||||||
displayError(err);
|
return auth.storeCredentials();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auth.storeCredentials(function(err) {
|
|
||||||
if (err) {
|
|
||||||
displayError(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
}).then(function() {
|
||||||
|
// go to main account screen
|
||||||
$location.path('/account');
|
$location.path('/account');
|
||||||
$scope.$apply();
|
|
||||||
});
|
}).catch(displayError);
|
||||||
}
|
};
|
||||||
|
|
||||||
function displayError(err) {
|
function displayError(err) {
|
||||||
$scope.busy = false;
|
$scope.busy = false;
|
||||||
$scope.incorrect = true;
|
$scope.incorrect = true;
|
||||||
$scope.errMsg = err.errMsg || err.message;
|
$scope.errMsg = err.errMsg || err.message;
|
||||||
$scope.$apply();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var LoginInitialCtrl = function($scope, $location, $routeParams, newsletter, email, auth) {
|
var LoginInitialCtrl = function($scope, $location, $routeParams, $q, newsletter, email, auth) {
|
||||||
!$routeParams.dev && !auth.isInitialized() && $location.path('/'); // init app
|
!$routeParams.dev && !auth.isInitialized() && $location.path('/'); // init app
|
||||||
|
|
||||||
var emailAddress = auth.emailAddress;
|
var emailAddress = auth.emailAddress;
|
||||||
|
@ -44,31 +44,30 @@ var LoginInitialCtrl = function($scope, $location, $routeParams, newsletter, ema
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.errMsg = undefined;
|
|
||||||
|
|
||||||
// sing up to newsletter
|
// sing up to newsletter
|
||||||
newsletter.signup(emailAddress, $scope.newsletter);
|
newsletter.signup(emailAddress, $scope.newsletter);
|
||||||
// go to set keygen screen
|
// go to set keygen screen
|
||||||
$scope.setState(states.PROCESSING);
|
$scope.setState(states.PROCESSING);
|
||||||
|
|
||||||
email.unlock({
|
return $q(function(resolve) {
|
||||||
passphrase: undefined // generate key without passphrase
|
$scope.errMsg = undefined;
|
||||||
}, function(err) {
|
resolve();
|
||||||
if (err) {
|
|
||||||
displayError(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auth.storeCredentials(function(err) {
|
}).then(function() {
|
||||||
if (err) {
|
// generate key without passphrase
|
||||||
displayError(err);
|
return email.unlock({
|
||||||
return;
|
passphrase: undefined
|
||||||
}
|
|
||||||
|
|
||||||
$location.path('/account');
|
|
||||||
$scope.$apply();
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
}).then(function() {
|
||||||
|
// persist credentials locally
|
||||||
|
return auth.storeCredentials();
|
||||||
|
|
||||||
|
}).then(function() {
|
||||||
|
// go to main account screen
|
||||||
|
$location.path('/account');
|
||||||
|
|
||||||
|
}).catch(displayError);
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.setState = function(state) {
|
$scope.setState = function(state) {
|
||||||
|
@ -78,7 +77,6 @@ var LoginInitialCtrl = function($scope, $location, $routeParams, newsletter, ema
|
||||||
function displayError(err) {
|
function displayError(err) {
|
||||||
$scope.setState(states.IDLE);
|
$scope.setState(states.IDLE);
|
||||||
$scope.errMsg = err.errMsg || err.message;
|
$scope.errMsg = err.errMsg || err.message;
|
||||||
$scope.$apply();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var LoginExistingCtrl = function($scope, $location, $routeParams, email, auth, pgp, keychain) {
|
var LoginExistingCtrl = function($scope, $location, $routeParams, $q, email, auth, pgp, keychain) {
|
||||||
!$routeParams.dev && !auth.isInitialized() && $location.path('/'); // init app
|
!$routeParams.dev && !auth.isInitialized() && $location.path('/'); // init app
|
||||||
|
|
||||||
$scope.incorrect = false;
|
$scope.incorrect = false;
|
||||||
|
@ -11,31 +11,28 @@ var LoginExistingCtrl = function($scope, $location, $routeParams, email, auth, p
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.busy = true;
|
var userId = auth.emailAddress,
|
||||||
$scope.errMsg = undefined; // reset error msg
|
keypair;
|
||||||
$scope.incorrect = false;
|
|
||||||
|
|
||||||
unlockCrypto();
|
return $q(function(resolve) {
|
||||||
};
|
$scope.busy = true;
|
||||||
|
$scope.errMsg = undefined; // reset error msg
|
||||||
|
$scope.incorrect = false;
|
||||||
|
resolve();
|
||||||
|
|
||||||
function unlockCrypto() {
|
}).then(function() {
|
||||||
var userId = auth.emailAddress;
|
// check if user already has a public key on the key server
|
||||||
// check if user already has a public key on the key server
|
return keychain.getUserKeyPair(userId);
|
||||||
keychain.getUserKeyPair(userId, function(err, keypair) {
|
|
||||||
if (err) {
|
|
||||||
$scope.displayError(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
keypair = keypair || {};
|
}).then(function(keys) {
|
||||||
|
keypair = keys || {};
|
||||||
|
|
||||||
// extract public key from private key block if missing in key file
|
// extract public key from private key block if missing in key file
|
||||||
if (!$scope.key.publicKeyArmored || $scope.key.publicKeyArmored.indexOf('-----BEGIN PGP PUBLIC KEY BLOCK-----') < 0) {
|
if (!$scope.key.publicKeyArmored || $scope.key.publicKeyArmored.indexOf('-----BEGIN PGP PUBLIC KEY BLOCK-----') < 0) {
|
||||||
try {
|
try {
|
||||||
$scope.key.publicKeyArmored = pgp.extractPublicKey($scope.key.privateKeyArmored);
|
$scope.key.publicKeyArmored = pgp.extractPublicKey($scope.key.privateKeyArmored);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
$scope.displayError(new Error('Error reading PGP key!'));
|
throw new Error('Error reading PGP key!');
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,8 +42,7 @@ var LoginExistingCtrl = function($scope, $location, $routeParams, email, auth, p
|
||||||
privKeyParams = pgp.getKeyParams($scope.key.privateKeyArmored);
|
privKeyParams = pgp.getKeyParams($scope.key.privateKeyArmored);
|
||||||
pubKeyParams = pgp.getKeyParams($scope.key.publicKeyArmored);
|
pubKeyParams = pgp.getKeyParams($scope.key.publicKeyArmored);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
$scope.displayError(new Error('Error reading key params!'));
|
throw new Error('Error reading key paramaters!');
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// set parsed private key
|
// set parsed private key
|
||||||
|
@ -68,43 +64,33 @@ var LoginExistingCtrl = function($scope, $location, $routeParams, email, auth, p
|
||||||
}
|
}
|
||||||
|
|
||||||
// import and validate keypair
|
// import and validate keypair
|
||||||
email.unlock({
|
return email.unlock({
|
||||||
keypair: keypair,
|
keypair: keypair,
|
||||||
passphrase: $scope.passphrase
|
passphrase: $scope.passphrase
|
||||||
}, function(err) {
|
}).catch(function(err) {
|
||||||
if (err) {
|
$scope.incorrect = true;
|
||||||
$scope.incorrect = true;
|
throw err;
|
||||||
$scope.displayError(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
keychain.putUserKeyPair(keypair, onUnlock);
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function onUnlock(err) {
|
}).then(function() {
|
||||||
if (err) {
|
// perist keys locally
|
||||||
$scope.displayError(err);
|
return keychain.putUserKeyPair(keypair);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auth.storeCredentials(function(err) {
|
}).then(function() {
|
||||||
if (err) {
|
// persist credentials locally
|
||||||
$scope.displayError(err);
|
return auth.storeCredentials();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
}).then(function() {
|
||||||
|
// go to main account screen
|
||||||
$location.path('/account');
|
$location.path('/account');
|
||||||
$scope.$apply();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.displayError = function(err) {
|
}).catch(displayError);
|
||||||
|
};
|
||||||
|
|
||||||
|
function displayError(err) {
|
||||||
$scope.busy = false;
|
$scope.busy = false;
|
||||||
$scope.errMsg = err.errMsg || err.message;
|
$scope.errMsg = err.errMsg || err.message;
|
||||||
$scope.$apply();
|
}
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = LoginExistingCtrl;
|
module.exports = LoginExistingCtrl;
|
|
@ -1,6 +1,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var LoginPrivateKeyDownloadCtrl = function($scope, $location, $routeParams, auth, email, keychain) {
|
var LoginPrivateKeyDownloadCtrl = function($scope, $location, $routeParams, $q, auth, email, keychain) {
|
||||||
!$routeParams.dev && !auth.isInitialized() && $location.path('/'); // init app
|
!$routeParams.dev && !auth.isInitialized() && $location.path('/'); // init app
|
||||||
|
|
||||||
$scope.step = 1;
|
$scope.step = 1;
|
||||||
|
@ -15,42 +15,32 @@ var LoginPrivateKeyDownloadCtrl = function($scope, $location, $routeParams, auth
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.busy = true;
|
|
||||||
$scope.errMsg = undefined;
|
|
||||||
|
|
||||||
$scope.verifyRecoveryToken(function() {
|
|
||||||
$scope.busy = false;
|
|
||||||
$scope.errMsg = undefined;
|
|
||||||
$scope.step++;
|
|
||||||
$scope.$apply();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.verifyRecoveryToken = function(callback) {
|
|
||||||
var userId = auth.emailAddress;
|
var userId = auth.emailAddress;
|
||||||
keychain.getUserKeyPair(userId, function(err, keypair) {
|
|
||||||
if (err) {
|
|
||||||
displayError(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
return $q(function(resolve) {
|
||||||
|
$scope.busy = true;
|
||||||
|
$scope.errMsg = undefined;
|
||||||
|
resolve();
|
||||||
|
|
||||||
|
}).then(function() {
|
||||||
|
// get public key id for reference
|
||||||
|
return keychain.getUserKeyPair(userId);
|
||||||
|
|
||||||
|
}).then(function(keypair) {
|
||||||
// remember for storage later
|
// remember for storage later
|
||||||
$scope.cachedKeypair = keypair;
|
$scope.cachedKeypair = keypair;
|
||||||
|
return keychain.downloadPrivateKey({
|
||||||
keychain.downloadPrivateKey({
|
|
||||||
userId: userId,
|
userId: userId,
|
||||||
keyId: keypair.publicKey._id,
|
keyId: keypair.publicKey._id,
|
||||||
recoveryToken: $scope.recoveryToken.toUpperCase()
|
recoveryToken: $scope.recoveryToken.toUpperCase()
|
||||||
}, function(err, encryptedPrivateKey) {
|
|
||||||
if (err) {
|
|
||||||
displayError(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.encryptedPrivateKey = encryptedPrivateKey;
|
|
||||||
callback();
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
}).then(function(encryptedPrivateKey) {
|
||||||
|
$scope.encryptedPrivateKey = encryptedPrivateKey;
|
||||||
|
$scope.busy = false;
|
||||||
|
$scope.step++;
|
||||||
|
|
||||||
|
}).catch(displayError);
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -63,47 +53,39 @@ var LoginPrivateKeyDownloadCtrl = function($scope, $location, $routeParams, auth
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.busy = true;
|
|
||||||
$scope.errMsg = undefined;
|
|
||||||
|
|
||||||
$scope.decryptAndStorePrivateKeyLocally();
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.decryptAndStorePrivateKeyLocally = function() {
|
|
||||||
var options = $scope.encryptedPrivateKey;
|
var options = $scope.encryptedPrivateKey;
|
||||||
options.code = $scope.code.toUpperCase();
|
options.code = $scope.code.toUpperCase();
|
||||||
|
|
||||||
keychain.decryptAndStorePrivateKeyLocally(options, function(err, privateKey) {
|
return $q(function(resolve) {
|
||||||
if (err) {
|
$scope.busy = true;
|
||||||
displayError(err);
|
$scope.errMsg = undefined;
|
||||||
return;
|
resolve();
|
||||||
}
|
|
||||||
|
|
||||||
|
}).then(function() {
|
||||||
|
return keychain.decryptAndStorePrivateKeyLocally(options);
|
||||||
|
|
||||||
|
}).then(function(privateKey) {
|
||||||
// add private key to cached keypair object
|
// add private key to cached keypair object
|
||||||
$scope.cachedKeypair.privateKey = privateKey;
|
$scope.cachedKeypair.privateKey = privateKey;
|
||||||
|
|
||||||
// try empty passphrase
|
// try empty passphrase
|
||||||
email.unlock({
|
return email.unlock({
|
||||||
keypair: $scope.cachedKeypair,
|
keypair: $scope.cachedKeypair,
|
||||||
passphrase: undefined
|
passphrase: undefined
|
||||||
}, function(err) {
|
}).catch(function(err) {
|
||||||
if (err) {
|
// passphrase incorrct ... go to passphrase login screen
|
||||||
// go to passphrase login screen
|
$scope.goTo('/login-existing');
|
||||||
$scope.goTo('/login-existing');
|
throw err;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// passphrase is corrent ... go to main app
|
|
||||||
auth.storeCredentials(function(err) {
|
|
||||||
if (err) {
|
|
||||||
displayError(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.goTo('/account');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
}).then(function() {
|
||||||
|
// passphrase is corrent ...
|
||||||
|
return auth.storeCredentials();
|
||||||
|
|
||||||
|
}).then(function() {
|
||||||
|
// continue to main app
|
||||||
|
$scope.goTo('/account');
|
||||||
|
|
||||||
|
}).catch(displayError);
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -112,14 +94,12 @@ var LoginPrivateKeyDownloadCtrl = function($scope, $location, $routeParams, auth
|
||||||
|
|
||||||
$scope.goTo = function(location) {
|
$scope.goTo = function(location) {
|
||||||
$location.path(location);
|
$location.path(location);
|
||||||
$scope.$apply();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function displayError(err) {
|
function displayError(err) {
|
||||||
$scope.busy = false;
|
$scope.busy = false;
|
||||||
$scope.incorrect = true;
|
$scope.incorrect = true;
|
||||||
$scope.errMsg = err.errMsg || err.message;
|
$scope.errMsg = err.errMsg || err.message;
|
||||||
$scope.$apply();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ var ENCRYPTION_METHOD_NONE = 0;
|
||||||
var ENCRYPTION_METHOD_STARTTLS = 1;
|
var ENCRYPTION_METHOD_STARTTLS = 1;
|
||||||
var ENCRYPTION_METHOD_TLS = 2;
|
var ENCRYPTION_METHOD_TLS = 2;
|
||||||
|
|
||||||
var SetCredentialsCtrl = function($scope, $location, $routeParams, auth, connectionDoctor) {
|
var SetCredentialsCtrl = function($scope, $location, $routeParams, $q, auth, connectionDoctor) {
|
||||||
!$routeParams.dev && !auth.isInitialized() && $location.path('/'); // init app
|
!$routeParams.dev && !auth.isInitialized() && $location.path('/'); // init app
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -82,19 +82,23 @@ var SetCredentialsCtrl = function($scope, $location, $routeParams, auth, connect
|
||||||
connectionDoctor.configure(credentials);
|
connectionDoctor.configure(credentials);
|
||||||
|
|
||||||
// run connection doctor test suite
|
// run connection doctor test suite
|
||||||
$scope.busy = true;
|
return $q(function(resolve) {
|
||||||
connectionDoctor.check(function(err) {
|
$scope.busy = true;
|
||||||
if (err) {
|
resolve();
|
||||||
// display the error in the settings UI
|
|
||||||
$scope.connectionError = err;
|
|
||||||
} else {
|
|
||||||
// persists the credentials and forwards to /login
|
|
||||||
auth.setCredentials(credentials);
|
|
||||||
$location.path('/login');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
}).then(function() {
|
||||||
|
return connectionDoctor.check();
|
||||||
|
|
||||||
|
}).then(function() {
|
||||||
|
// persists the credentials and forwards to /login
|
||||||
|
auth.setCredentials(credentials);
|
||||||
|
$scope.busy = false;
|
||||||
|
$location.path('/login');
|
||||||
|
|
||||||
|
}).catch(function(err) {
|
||||||
|
// display the error in the settings UI
|
||||||
|
$scope.connectionError = err;
|
||||||
$scope.busy = false;
|
$scope.busy = false;
|
||||||
$scope.$apply();
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,91 +1,74 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var LoginCtrl = function($scope, $timeout, $location, updateHandler, account, auth, email, keychain, dialog) {
|
var LoginCtrl = function($scope, $timeout, $location, updateHandler, account, auth, email, keychain, dialog, appConfig) {
|
||||||
|
|
||||||
// check for app update
|
//
|
||||||
updateHandler.checkForUpdate();
|
// Scope functions
|
||||||
// initialize the user account
|
//
|
||||||
initializeUser();
|
|
||||||
|
|
||||||
function initializeUser() {
|
$scope.init = function() {
|
||||||
// init the auth modules
|
// initialize the user account
|
||||||
auth.init(function(err) {
|
return auth.init().then(function() {
|
||||||
if (err) {
|
// get email address
|
||||||
return dialog.error(err);
|
return auth.getEmailAddress();
|
||||||
|
|
||||||
|
}).then(function(info) {
|
||||||
|
// check if account needs to be selected
|
||||||
|
if (!info.emailAddress) {
|
||||||
|
return $scope.goTo('/add-account');
|
||||||
}
|
}
|
||||||
|
|
||||||
// get OAuth token from chrome
|
// initiate the account by initializing the email dao and user storage
|
||||||
auth.getEmailAddress(function(err, info) {
|
return account.init({
|
||||||
if (err) {
|
emailAddress: info.emailAddress,
|
||||||
dialog.error(err);
|
realname: info.realname
|
||||||
return;
|
}).then(function(availableKeys) {
|
||||||
}
|
return redirect(availableKeys);
|
||||||
|
|
||||||
// check if account needs to be selected
|
|
||||||
if (!info.emailAddress) {
|
|
||||||
$scope.goTo('/add-account');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// initiate the account by initializing the email dao and user storage
|
|
||||||
account.init({
|
|
||||||
emailAddress: info.emailAddress,
|
|
||||||
realname: info.realname
|
|
||||||
}, function(err, availableKeys) {
|
|
||||||
if (err) {
|
|
||||||
dialog.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
redirect(availableKeys);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
}
|
}).catch(dialog.error);
|
||||||
|
};
|
||||||
|
|
||||||
function redirect(availableKeys) {
|
function redirect(availableKeys) {
|
||||||
if (availableKeys && availableKeys.publicKey && availableKeys.privateKey) {
|
if (availableKeys && availableKeys.publicKey && availableKeys.privateKey) {
|
||||||
// public and private key available, try empty passphrase
|
// public and private key available, try empty passphrase
|
||||||
email.unlock({
|
var passphraseIncorrect;
|
||||||
|
return email.unlock({
|
||||||
keypair: availableKeys,
|
keypair: availableKeys,
|
||||||
passphrase: undefined
|
passphrase: undefined
|
||||||
}, function(err) {
|
}).catch(function() {
|
||||||
if (err) {
|
passphraseIncorrect = true;
|
||||||
$scope.goTo('/login-existing');
|
// passphrase set... ask for passphrase
|
||||||
|
return $scope.goTo('/login-existing');
|
||||||
|
|
||||||
|
}).then(function() {
|
||||||
|
if (passphraseIncorrect) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// no passphrase set... go to main screen
|
||||||
auth.storeCredentials(function(err) {
|
return auth.storeCredentials().then(function() {
|
||||||
if (err) {
|
return $scope.goTo('/account');
|
||||||
return dialog.error(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.goTo('/account');
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
} else if (availableKeys && availableKeys.publicKey && !availableKeys.privateKey) {
|
} else if (availableKeys && availableKeys.publicKey && !availableKeys.privateKey) {
|
||||||
// check if private key is synced
|
// check if private key is synced
|
||||||
keychain.requestPrivateKeyDownload({
|
return keychain.requestPrivateKeyDownload({
|
||||||
userId: availableKeys.publicKey.userId,
|
userId: availableKeys.publicKey.userId,
|
||||||
keyId: availableKeys.publicKey._id,
|
keyId: availableKeys.publicKey._id,
|
||||||
}, function(err, privateKeySynced) {
|
}).then(function(privateKeySynced) {
|
||||||
if (err) {
|
|
||||||
dialog.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (privateKeySynced) {
|
if (privateKeySynced) {
|
||||||
// private key is synced, proceed to download
|
// private key is synced, proceed to download
|
||||||
$scope.goTo('/login-privatekey-download');
|
return $scope.goTo('/login-privatekey-download');
|
||||||
return;
|
} else {
|
||||||
|
// no private key, import key file
|
||||||
|
return $scope.goTo('/login-new-device');
|
||||||
}
|
}
|
||||||
|
|
||||||
// no private key, import key file
|
|
||||||
$scope.goTo('/login-new-device');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// no public key available, start onboarding process
|
// no public key available, start onboarding process
|
||||||
$scope.goTo('/login-initial');
|
return $scope.goTo('/login-initial');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,6 +77,19 @@ var LoginCtrl = function($scope, $timeout, $location, updateHandler, account, au
|
||||||
$location.path(location);
|
$location.path(location);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Start the app
|
||||||
|
//
|
||||||
|
|
||||||
|
// check for app update
|
||||||
|
updateHandler.checkForUpdate();
|
||||||
|
|
||||||
|
// init the app
|
||||||
|
if (!appConfig.preventAutoStart) {
|
||||||
|
$scope.init();
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = LoginCtrl;
|
module.exports = LoginCtrl;
|
|
@ -1,6 +1,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var ValidatePhoneCtrl = function($scope, $location, $routeParams, mailConfig, auth, admin) {
|
var ValidatePhoneCtrl = function($scope, $location, $routeParams, $q, mailConfig, auth, admin) {
|
||||||
!$routeParams.dev && !auth.isInitialized() && $location.path('/'); // init app
|
!$routeParams.dev && !auth.isInitialized() && $location.path('/'); // init app
|
||||||
|
|
||||||
$scope.validateUser = function() {
|
$scope.validateUser = function() {
|
||||||
|
@ -9,23 +9,25 @@ var ValidatePhoneCtrl = function($scope, $location, $routeParams, mailConfig, au
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.busy = true;
|
return $q(function(resolve) {
|
||||||
$scope.errMsg = undefined; // reset error msg
|
$scope.busy = true;
|
||||||
|
$scope.errMsg = undefined; // reset error msg
|
||||||
|
resolve();
|
||||||
|
|
||||||
// verify user to REST api
|
}).then(function() {
|
||||||
admin.validateUser({
|
// verify user to REST api
|
||||||
emailAddress: auth.emailAddress,
|
return admin.validateUser({
|
||||||
token: $scope.token.toUpperCase()
|
emailAddress: auth.emailAddress,
|
||||||
}, function(err) {
|
token: $scope.token.toUpperCase()
|
||||||
if (err) {
|
});
|
||||||
$scope.busy = false;
|
|
||||||
$scope.errMsg = err.errMsg || err.message;
|
|
||||||
$scope.$apply();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
}).then(function() {
|
||||||
// proceed to login
|
// proceed to login
|
||||||
$scope.login();
|
return $scope.login();
|
||||||
|
|
||||||
|
}).catch(function(err) {
|
||||||
|
$scope.busy = false;
|
||||||
|
$scope.errMsg = err.errMsg || err.message;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -54,10 +56,9 @@ var ValidatePhoneCtrl = function($scope, $location, $routeParams, mailConfig, au
|
||||||
$location.path('/login');
|
$location.path('/login');
|
||||||
|
|
||||||
}).catch(function() {
|
}).catch(function() {
|
||||||
$scope.busy = false;
|
throw new Error('Error fetching IMAP settings!');
|
||||||
$scope.errMsg = 'Error fetching IMAP settings!';
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = ValidatePhoneCtrl;
|
module.exports = ValidatePhoneCtrl;
|
|
@ -20,20 +20,13 @@ function Crypto() {}
|
||||||
* @param {String} plaintext The input string in UTF-16
|
* @param {String} plaintext The input string in UTF-16
|
||||||
* @param {String} key The base64 encoded key
|
* @param {String} key The base64 encoded key
|
||||||
* @param {String} iv The base64 encoded IV
|
* @param {String} iv The base64 encoded IV
|
||||||
* @param {Function} callback(error, ciphertext)
|
|
||||||
* @return {String} The base64 encoded ciphertext
|
* @return {String} The base64 encoded ciphertext
|
||||||
*/
|
*/
|
||||||
Crypto.prototype.encrypt = function(plaintext, key, iv, callback) {
|
Crypto.prototype.encrypt = function(plaintext, key, iv) {
|
||||||
var ct;
|
return new Promise(function(resolve) {
|
||||||
|
var ct = aes.encrypt(plaintext, key, iv);
|
||||||
try {
|
resolve(ct);
|
||||||
ct = aes.encrypt(plaintext, key, iv);
|
});
|
||||||
} catch (err) {
|
|
||||||
callback(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(null, ct);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -41,34 +34,26 @@ Crypto.prototype.encrypt = function(plaintext, key, iv, callback) {
|
||||||
* @param {String} ciphertext The base64 encoded ciphertext
|
* @param {String} ciphertext The base64 encoded ciphertext
|
||||||
* @param {String} key The base64 encoded key
|
* @param {String} key The base64 encoded key
|
||||||
* @param {String} iv The base64 encoded IV
|
* @param {String} iv The base64 encoded IV
|
||||||
* @param {Function} callback(error, plaintext)
|
|
||||||
* @return {String} The decrypted plaintext in UTF-16
|
* @return {String} The decrypted plaintext in UTF-16
|
||||||
*/
|
*/
|
||||||
Crypto.prototype.decrypt = function(ciphertext, key, iv, callback) {
|
Crypto.prototype.decrypt = function(ciphertext, key, iv) {
|
||||||
var pt;
|
return new Promise(function(resolve) {
|
||||||
|
var pt = aes.decrypt(ciphertext, key, iv);
|
||||||
try {
|
resolve(pt);
|
||||||
pt = aes.decrypt(ciphertext, key, iv);
|
});
|
||||||
} catch (err) {
|
|
||||||
callback(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(null, pt);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Do PBKDF2 key derivation in a WebWorker thread
|
* Do PBKDF2 key derivation in a WebWorker thread
|
||||||
*/
|
*/
|
||||||
Crypto.prototype.deriveKey = function(password, salt, keySize, callback) {
|
Crypto.prototype.deriveKey = function(password, salt, keySize) {
|
||||||
startWorker({
|
return this.startWorker({
|
||||||
script: config.workerPath + '/pbkdf2-worker.min.js',
|
script: config.workerPath + '/pbkdf2-worker.min.js',
|
||||||
args: {
|
args: {
|
||||||
password: password,
|
password: password,
|
||||||
salt: salt,
|
salt: salt,
|
||||||
keySize: keySize
|
keySize: keySize
|
||||||
},
|
},
|
||||||
callback: callback,
|
|
||||||
noWorker: function() {
|
noWorker: function() {
|
||||||
return pbkdf2.getKey(password, salt, keySize);
|
return pbkdf2.getKey(password, salt, keySize);
|
||||||
}
|
}
|
||||||
|
@ -79,43 +64,33 @@ Crypto.prototype.deriveKey = function(password, salt, keySize, callback) {
|
||||||
// helper functions
|
// helper functions
|
||||||
//
|
//
|
||||||
|
|
||||||
function startWorker(options) {
|
Crypto.prototype.startWorker = function(options) {
|
||||||
// check for WebWorker support
|
return new Promise(function(resolve, reject) {
|
||||||
if (window.Worker) {
|
// check for WebWorker support
|
||||||
// init webworker thread
|
if (window.Worker) {
|
||||||
var worker = new Worker(options.script);
|
// init webworker thread
|
||||||
worker.onmessage = function(e) {
|
var worker = new Worker(options.script);
|
||||||
if (e.data.err) {
|
worker.onmessage = function(e) {
|
||||||
options.callback(e.data.err);
|
// return result from the worker
|
||||||
return;
|
if (e.data.err) {
|
||||||
}
|
reject(e.data.err);
|
||||||
// return result from the worker
|
} else {
|
||||||
options.callback(null, e.data);
|
resolve(e.data);
|
||||||
};
|
}
|
||||||
worker.onerror = function(e) {
|
};
|
||||||
// show error message in logger
|
worker.onerror = function(e) {
|
||||||
axe.error('Error handling web worker: Line ' + e.lineno + ' in ' + e.filename + ': ' + e.message);
|
// show error message in logger
|
||||||
// return error
|
axe.error('Error handling web worker: Line ' + e.lineno + ' in ' + e.filename + ': ' + e.message);
|
||||||
options.callback({
|
// return error
|
||||||
errMsg: (e.message) ? e.message : e
|
reject(e);
|
||||||
});
|
};
|
||||||
|
// send data to the worker
|
||||||
|
worker.postMessage(options.args);
|
||||||
return;
|
return;
|
||||||
};
|
}
|
||||||
// send data to the worker
|
|
||||||
worker.postMessage(options.args);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// no WebWorker support... do synchronous call
|
// no WebWorker support... do synchronous call
|
||||||
var result;
|
var result = options.noWorker();
|
||||||
try {
|
resolve(result);
|
||||||
result = options.noWorker();
|
});
|
||||||
} catch (e) {
|
};
|
||||||
// return error
|
|
||||||
options.callback({
|
|
||||||
errMsg: (e.message) ? e.message : e
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
options.callback(null, result);
|
|
||||||
}
|
|
|
@ -17,30 +17,39 @@ function PGP() {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a key pair for the user
|
* Generate a key pair for the user
|
||||||
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
PGP.prototype.generateKeys = function(options, callback) {
|
PGP.prototype.generateKeys = function(options) {
|
||||||
var userId, passphrase;
|
return new Promise(function(resolve) {
|
||||||
|
var userId, passphrase;
|
||||||
|
|
||||||
if (!util.emailRegEx.test(options.emailAddress) || !options.keySize) {
|
if (!util.emailRegEx.test(options.emailAddress) || !options.keySize) {
|
||||||
callback(new Error('Crypto init failed. Not all options set!'));
|
throw new Error('Crypto init failed. Not all options set!');
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// generate keypair
|
// generate keypair
|
||||||
userId = 'Whiteout User <' + options.emailAddress + '>';
|
userId = 'Whiteout User <' + options.emailAddress + '>';
|
||||||
passphrase = (options.passphrase) ? options.passphrase : undefined;
|
passphrase = (options.passphrase) ? options.passphrase : undefined;
|
||||||
openpgp.generateKeyPair({
|
|
||||||
keyType: 1, // (keytype 1=RSA)
|
resolve({
|
||||||
numBits: options.keySize,
|
userId: userId,
|
||||||
userId: userId,
|
passphrase: passphrase
|
||||||
passphrase: passphrase
|
});
|
||||||
|
|
||||||
|
}).then(function(res) {
|
||||||
|
return openpgp.generateKeyPair({
|
||||||
|
keyType: 1, // (keytype 1=RSA)
|
||||||
|
numBits: options.keySize,
|
||||||
|
userId: res.userId,
|
||||||
|
passphrase: res.passphrase
|
||||||
|
});
|
||||||
}).then(function(keys) {
|
}).then(function(keys) {
|
||||||
callback(null, {
|
return {
|
||||||
keyId: keys.key.primaryKey.getKeyId().toHex().toUpperCase(),
|
keyId: keys.key.primaryKey.getKeyId().toHex().toUpperCase(),
|
||||||
privateKeyArmored: keys.privateKeyArmored,
|
privateKeyArmored: keys.privateKeyArmored,
|
||||||
publicKeyArmored: keys.publicKeyArmored
|
publicKeyArmored: keys.publicKeyArmored
|
||||||
});
|
};
|
||||||
}).catch(callback);
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -141,153 +150,156 @@ PGP.prototype.extractPublicKey = function(privateKeyArmored) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Import the user's key pair
|
* Import the user's key pair
|
||||||
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
PGP.prototype.importKeys = function(options, callback) {
|
PGP.prototype.importKeys = function(options) {
|
||||||
var pubKeyId, privKeyId, self = this;
|
var self = this;
|
||||||
|
return new Promise(function(resolve) {
|
||||||
|
var pubKeyId, privKeyId;
|
||||||
|
|
||||||
// check options
|
// check options
|
||||||
if (!options.privateKeyArmored || !options.publicKeyArmored) {
|
if (!options.privateKeyArmored || !options.publicKeyArmored) {
|
||||||
callback(new Error('Importing keys failed. Not all options set!'));
|
throw new Error('Importing keys failed. Not all options set!');
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function resetKeys() {
|
function resetKeys() {
|
||||||
self._publicKey = undefined;
|
self._publicKey = undefined;
|
||||||
self._privateKey = undefined;
|
self._privateKey = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
// read armored keys
|
// read armored keys
|
||||||
try {
|
try {
|
||||||
this._publicKey = openpgp.key.readArmored(options.publicKeyArmored).keys[0];
|
self._publicKey = openpgp.key.readArmored(options.publicKeyArmored).keys[0];
|
||||||
this._privateKey = openpgp.key.readArmored(options.privateKeyArmored).keys[0];
|
self._privateKey = openpgp.key.readArmored(options.privateKeyArmored).keys[0];
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
resetKeys();
|
resetKeys();
|
||||||
callback(new Error('Importing keys failed. Parsing error!'));
|
throw new Error('Importing keys failed. Parsing error!');
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// decrypt private key with passphrase
|
// decrypt private key with passphrase
|
||||||
if (!this._privateKey.decrypt(options.passphrase)) {
|
if (!self._privateKey.decrypt(options.passphrase)) {
|
||||||
resetKeys();
|
resetKeys();
|
||||||
callback(new Error('Incorrect passphrase!'));
|
throw new Error('Incorrect passphrase!');
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// check if keys have the same id
|
// check if keys have the same id
|
||||||
pubKeyId = this._publicKey.primaryKey.getKeyId().toHex();
|
pubKeyId = self._publicKey.primaryKey.getKeyId().toHex();
|
||||||
privKeyId = this._privateKey.primaryKey.getKeyId().toHex();
|
privKeyId = self._privateKey.primaryKey.getKeyId().toHex();
|
||||||
if (!pubKeyId || !privKeyId || pubKeyId !== privKeyId) {
|
if (!pubKeyId || !privKeyId || pubKeyId !== privKeyId) {
|
||||||
resetKeys();
|
resetKeys();
|
||||||
callback(new Error('Key IDs dont match!'));
|
throw new Error('Key IDs dont match!');
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
callback();
|
resolve();
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Export the user's key pair
|
* Export the user's key pair
|
||||||
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
PGP.prototype.exportKeys = function(callback) {
|
PGP.prototype.exportKeys = function() {
|
||||||
if (!this._publicKey || !this._privateKey) {
|
var self = this;
|
||||||
callback(new Error('Could not export keys!'));
|
return new Promise(function(resolve) {
|
||||||
return;
|
if (!self._publicKey || !self._privateKey) {
|
||||||
}
|
throw new Error('Could not export keys!');
|
||||||
|
}
|
||||||
|
|
||||||
callback(null, {
|
resolve({
|
||||||
keyId: this._publicKey.primaryKey.getKeyId().toHex().toUpperCase(),
|
keyId: self._publicKey.primaryKey.getKeyId().toHex().toUpperCase(),
|
||||||
privateKeyArmored: this._privateKey.armor(),
|
privateKeyArmored: self._privateKey.armor(),
|
||||||
publicKeyArmored: this._publicKey.armor()
|
publicKeyArmored: self._publicKey.armor()
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Change the passphrase of an ascii armored private key.
|
* Change the passphrase of an ascii armored private key.
|
||||||
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
PGP.prototype.changePassphrase = function(options, callback) {
|
PGP.prototype.changePassphrase = function(options) {
|
||||||
var privKey, packets, newPassphrase, newKeyArmored;
|
return new Promise(function(resolve) {
|
||||||
|
var privKey, packets, newPassphrase, newKeyArmored;
|
||||||
|
|
||||||
// set undefined instead of empty string as passphrase
|
// set undefined instead of empty string as passphrase
|
||||||
newPassphrase = (options.newPassphrase) ? options.newPassphrase : undefined;
|
newPassphrase = (options.newPassphrase) ? options.newPassphrase : undefined;
|
||||||
|
|
||||||
if (!options.privateKeyArmored) {
|
if (!options.privateKeyArmored) {
|
||||||
callback(new Error('Private key must be specified to change passphrase!'));
|
throw new Error('Private key must be specified to change passphrase!');
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.oldPassphrase === newPassphrase ||
|
|
||||||
(!options.oldPassphrase && !newPassphrase)) {
|
|
||||||
callback(new Error('New and old passphrase are the same!'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// read armored key
|
|
||||||
try {
|
|
||||||
privKey = openpgp.key.readArmored(options.privateKeyArmored).keys[0];
|
|
||||||
} catch (e) {
|
|
||||||
callback(new Error('Importing key failed. Parsing error!'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// decrypt private key with passphrase
|
|
||||||
if (!privKey.decrypt(options.oldPassphrase)) {
|
|
||||||
callback(new Error('Old passphrase incorrect!'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// encrypt key with new passphrase
|
|
||||||
try {
|
|
||||||
packets = privKey.getAllKeyPackets();
|
|
||||||
for (var i = 0; i < packets.length; i++) {
|
|
||||||
packets[i].encrypt(newPassphrase);
|
|
||||||
}
|
}
|
||||||
newKeyArmored = privKey.armor();
|
|
||||||
} catch (e) {
|
|
||||||
callback(new Error('Setting new passphrase failed!'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if new passphrase really works
|
if (options.oldPassphrase === newPassphrase ||
|
||||||
if (!privKey.decrypt(newPassphrase)) {
|
(!options.oldPassphrase && !newPassphrase)) {
|
||||||
callback(new Error('Decrypting key with new passphrase failed!'));
|
throw new Error('New and old passphrase are the same!');
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
callback(null, newKeyArmored);
|
// read armored key
|
||||||
|
try {
|
||||||
|
privKey = openpgp.key.readArmored(options.privateKeyArmored).keys[0];
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error('Importing key failed. Parsing error!');
|
||||||
|
}
|
||||||
|
|
||||||
|
// decrypt private key with passphrase
|
||||||
|
if (!privKey.decrypt(options.oldPassphrase)) {
|
||||||
|
throw new Error('Old passphrase incorrect!');
|
||||||
|
}
|
||||||
|
|
||||||
|
// encrypt key with new passphrase
|
||||||
|
try {
|
||||||
|
packets = privKey.getAllKeyPackets();
|
||||||
|
for (var i = 0; i < packets.length; i++) {
|
||||||
|
packets[i].encrypt(newPassphrase);
|
||||||
|
}
|
||||||
|
newKeyArmored = privKey.armor();
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error('Setting new passphrase failed!');
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if new passphrase really works
|
||||||
|
if (!privKey.decrypt(newPassphrase)) {
|
||||||
|
throw new Error('Decrypting key with new passphrase failed!');
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(newKeyArmored);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encrypt and sign a pgp message for a list of receivers
|
* Encrypt and sign a pgp message for a list of receivers
|
||||||
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
PGP.prototype.encrypt = function(plaintext, publicKeysArmored, callback) {
|
PGP.prototype.encrypt = function(plaintext, publicKeysArmored) {
|
||||||
var publicKeys;
|
var self = this;
|
||||||
|
return new Promise(function(resolve) {
|
||||||
|
var publicKeys;
|
||||||
|
|
||||||
// check keys
|
// check keys
|
||||||
if (!this._privateKey) {
|
if (!self._privateKey) {
|
||||||
callback(new Error('Error encrypting. Keys must be set!'));
|
throw new Error('Error encrypting. Keys must be set!');
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse armored public keys
|
|
||||||
try {
|
|
||||||
if (publicKeysArmored && publicKeysArmored.length) {
|
|
||||||
publicKeys = [];
|
|
||||||
publicKeysArmored.forEach(function(pubkeyArmored) {
|
|
||||||
publicKeys = publicKeys.concat(openpgp.key.readArmored(pubkeyArmored).keys);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
} catch (err) {
|
// parse armored public keys
|
||||||
callback(new Error('Error encrypting plaintext!'));
|
try {
|
||||||
return;
|
if (publicKeysArmored && publicKeysArmored.length) {
|
||||||
}
|
publicKeys = [];
|
||||||
|
publicKeysArmored.forEach(function(pubkeyArmored) {
|
||||||
|
publicKeys = publicKeys.concat(openpgp.key.readArmored(pubkeyArmored).keys);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
throw new Error('Error encrypting plaintext!');
|
||||||
|
}
|
||||||
|
resolve(publicKeys);
|
||||||
|
|
||||||
if (publicKeys) {
|
}).then(function(publicKeys) {
|
||||||
// encrypt and sign the plaintext
|
if (publicKeys) {
|
||||||
openpgp.signAndEncryptMessage(publicKeys, this._privateKey, plaintext).then(callback.bind(null, null)).catch(callback);
|
// encrypt and sign the plaintext
|
||||||
} else {
|
return openpgp.signAndEncryptMessage(publicKeys, self._privateKey, plaintext);
|
||||||
// if no public keys are available encrypt for myself
|
} else {
|
||||||
openpgp.signAndEncryptMessage([this._publicKey], this._privateKey, plaintext).then(callback.bind(null, null)).catch(callback);
|
// if no public keys are available encrypt for myself
|
||||||
}
|
return openpgp.signAndEncryptMessage([self._publicKey], self._privateKey, plaintext);
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -295,36 +307,45 @@ PGP.prototype.encrypt = function(plaintext, publicKeysArmored, callback) {
|
||||||
* @param {String} ciphertext The encrypted PGP message block
|
* @param {String} ciphertext The encrypted PGP message block
|
||||||
* @param {String} publicKeyArmored The public key used to sign the message
|
* @param {String} publicKeyArmored The public key used to sign the message
|
||||||
* @param {Function} callback(error, plaintext, signaturesValid) signaturesValid is undefined in case there are no signature, null in case there are signatures but the wrong public key or no key was used to verify, true if the signature was successfully verified, or false if the signataure verification failed.
|
* @param {Function} callback(error, plaintext, signaturesValid) signaturesValid is undefined in case there are no signature, null in case there are signatures but the wrong public key or no key was used to verify, true if the signature was successfully verified, or false if the signataure verification failed.
|
||||||
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
PGP.prototype.decrypt = function(ciphertext, publicKeyArmored, callback) {
|
PGP.prototype.decrypt = function(ciphertext, publicKeyArmored) {
|
||||||
var publicKeys, message;
|
var self = this;
|
||||||
|
return new Promise(function(resolve) {
|
||||||
|
var publicKeys, message;
|
||||||
|
|
||||||
// check keys
|
// check keys
|
||||||
if (!this._privateKey) {
|
if (!self._privateKey) {
|
||||||
callback(new Error('Error decrypting. Keys must be set!'));
|
throw new Error('Error decrypting. Keys must be set!');
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// read keys and ciphertext message
|
|
||||||
try {
|
|
||||||
if (publicKeyArmored) {
|
|
||||||
// parse public keys if available ...
|
|
||||||
publicKeys = openpgp.key.readArmored(publicKeyArmored).keys;
|
|
||||||
} else {
|
|
||||||
// use own public key to know if signatures are available
|
|
||||||
publicKeys = [this._publicKey];
|
|
||||||
}
|
}
|
||||||
message = openpgp.message.readArmored(ciphertext);
|
// read keys and ciphertext message
|
||||||
} catch (err) {
|
try {
|
||||||
callback(new Error('Error parsing encrypted PGP message!'));
|
if (publicKeyArmored) {
|
||||||
return;
|
// parse public keys if available ...
|
||||||
}
|
publicKeys = openpgp.key.readArmored(publicKeyArmored).keys;
|
||||||
|
} else {
|
||||||
|
// use own public key to know if signatures are available
|
||||||
|
publicKeys = [self._publicKey];
|
||||||
|
}
|
||||||
|
message = openpgp.message.readArmored(ciphertext);
|
||||||
|
} catch (err) {
|
||||||
|
throw new Error('Error parsing encrypted PGP message!');
|
||||||
|
}
|
||||||
|
resolve({
|
||||||
|
publicKeys: publicKeys,
|
||||||
|
message: message
|
||||||
|
});
|
||||||
|
|
||||||
// decrypt and verify pgp message
|
}).then(function(res) {
|
||||||
openpgp.decryptAndVerifyMessage(this._privateKey, publicKeys, message).then(function(decrypted) {
|
// decrypt and verify pgp message
|
||||||
|
return openpgp.decryptAndVerifyMessage(self._privateKey, res.publicKeys, res.message);
|
||||||
|
}).then(function(decrypted) {
|
||||||
// return decrypted plaintext
|
// return decrypted plaintext
|
||||||
callback(null, decrypted.text, checkSignatureValidity(decrypted.signatures));
|
return {
|
||||||
}).catch(callback);
|
decrypted: decrypted.text,
|
||||||
|
signaturesValid: checkSignatureValidity(decrypted.signatures)
|
||||||
|
};
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -332,35 +353,40 @@ PGP.prototype.decrypt = function(ciphertext, publicKeyArmored, callback) {
|
||||||
* @param {String} clearSignedText The clearsigned text, usually from a signed pgp/inline message
|
* @param {String} clearSignedText The clearsigned text, usually from a signed pgp/inline message
|
||||||
* @param {String} publicKeyArmored The public key used to signed the message
|
* @param {String} publicKeyArmored The public key used to signed the message
|
||||||
* @param {Function} callback(error, signaturesValid) signaturesValid is undefined in case there are no signature, null in case there are signatures but the wrong public key or no key was used to verify, true if the signature was successfully verified, or false if the signataure verification failed.
|
* @param {Function} callback(error, signaturesValid) signaturesValid is undefined in case there are no signature, null in case there are signatures but the wrong public key or no key was used to verify, true if the signature was successfully verified, or false if the signataure verification failed.
|
||||||
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
PGP.prototype.verifyClearSignedMessage = function(clearSignedText, publicKeyArmored, callback) {
|
PGP.prototype.verifyClearSignedMessage = function(clearSignedText, publicKeyArmored) {
|
||||||
var publicKeys,
|
var self = this;
|
||||||
message;
|
return new Promise(function(resolve) {
|
||||||
|
var publicKeys, message;
|
||||||
|
|
||||||
// check keys
|
// check keys
|
||||||
if (!this._privateKey) {
|
if (!self._privateKey) {
|
||||||
callback(new Error('Error verifying signed PGP message. Keys must be set!'));
|
throw new Error('Error verifying signed PGP message. Keys must be set!');
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// read keys and ciphertext message
|
|
||||||
try {
|
|
||||||
if (publicKeyArmored) {
|
|
||||||
// parse public keys if available ...
|
|
||||||
publicKeys = openpgp.key.readArmored(publicKeyArmored).keys;
|
|
||||||
} else {
|
|
||||||
// use own public key to know if signatures are available
|
|
||||||
publicKeys = [this._publicKey];
|
|
||||||
}
|
}
|
||||||
message = openpgp.cleartext.readArmored(clearSignedText);
|
// read keys and ciphertext message
|
||||||
} catch (err) {
|
try {
|
||||||
callback(new Error('Error verifying signed PGP message!'));
|
if (publicKeyArmored) {
|
||||||
return;
|
// parse public keys if available ...
|
||||||
}
|
publicKeys = openpgp.key.readArmored(publicKeyArmored).keys;
|
||||||
|
} else {
|
||||||
|
// use own public key to know if signatures are available
|
||||||
|
publicKeys = [self._publicKey];
|
||||||
|
}
|
||||||
|
message = openpgp.cleartext.readArmored(clearSignedText);
|
||||||
|
} catch (err) {
|
||||||
|
throw new Error('Error verifying signed PGP message!');
|
||||||
|
}
|
||||||
|
resolve({
|
||||||
|
publicKeys: publicKeys,
|
||||||
|
message: message
|
||||||
|
});
|
||||||
|
|
||||||
openpgp.verifyClearSignedMessage(publicKeys, message).then(function(result) {
|
}).then(function(res) {
|
||||||
callback(null, checkSignatureValidity(result.signatures));
|
return openpgp.verifyClearSignedMessage(res.publicKeys, res.message);
|
||||||
}).catch(callback);
|
}).then(function(result) {
|
||||||
|
return checkSignatureValidity(result.signatures);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -369,40 +395,39 @@ PGP.prototype.verifyClearSignedMessage = function(clearSignedText, publicKeyArmo
|
||||||
* @param {String} pgpSignature The detached signature, usually from a signed pgp/mime message
|
* @param {String} pgpSignature The detached signature, usually from a signed pgp/mime message
|
||||||
* @param {String} publicKeyArmored The public key used to signed the message
|
* @param {String} publicKeyArmored The public key used to signed the message
|
||||||
* @param {Function} callback(error, signaturesValid) signaturesValid is undefined in case there are no signature, null in case there are signatures but the wrong public key or no key was used to verify, true if the signature was successfully verified, or false if the signataure verification failed.
|
* @param {Function} callback(error, signaturesValid) signaturesValid is undefined in case there are no signature, null in case there are signatures but the wrong public key or no key was used to verify, true if the signature was successfully verified, or false if the signataure verification failed.
|
||||||
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
PGP.prototype.verifySignedMessage = function(message, pgpSignature, publicKeyArmored, callback) {
|
PGP.prototype.verifySignedMessage = function(message, pgpSignature, publicKeyArmored) {
|
||||||
var publicKeys;
|
var self = this;
|
||||||
|
return new Promise(function(resolve) {
|
||||||
|
var publicKeys, signatures;
|
||||||
|
|
||||||
// check keys
|
// check keys
|
||||||
if (!this._privateKey) {
|
if (!self._privateKey) {
|
||||||
callback(new Error('Error verifying signed PGP message. Keys must be set!'));
|
throw new Error('Error verifying signed PGP message. Keys must be set!');
|
||||||
return;
|
}
|
||||||
}
|
// read keys and ciphertext message
|
||||||
|
try {
|
||||||
// read keys and ciphertext message
|
if (publicKeyArmored) {
|
||||||
try {
|
// parse public keys if available ...
|
||||||
if (publicKeyArmored) {
|
publicKeys = openpgp.key.readArmored(publicKeyArmored).keys;
|
||||||
// parse public keys if available ...
|
} else {
|
||||||
publicKeys = openpgp.key.readArmored(publicKeyArmored).keys;
|
// use own public key to know if signatures are available
|
||||||
} else {
|
publicKeys = [self._publicKey];
|
||||||
// use own public key to know if signatures are available
|
}
|
||||||
publicKeys = [this._publicKey];
|
} catch (err) {
|
||||||
|
throw new Error('Error verifying signed PGP message!');
|
||||||
|
}
|
||||||
|
// check signatures
|
||||||
|
try {
|
||||||
|
var msg = openpgp.message.readSignedContent(message, pgpSignature);
|
||||||
|
signatures = msg.verify(publicKeys);
|
||||||
|
} catch (err) {
|
||||||
|
throw new Error('Error verifying signed PGP message!');
|
||||||
}
|
}
|
||||||
} catch (err) {
|
|
||||||
callback(new Error('Error verifying signed PGP message!'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var signatures;
|
resolve(checkSignatureValidity(signatures));
|
||||||
try {
|
});
|
||||||
var msg = openpgp.message.readSignedContent(message, pgpSignature);
|
|
||||||
signatures = msg.verify(publicKeys);
|
|
||||||
} catch (err) {
|
|
||||||
callback(new Error('Error verifying signed PGP message!'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(null, checkSignatureValidity(signatures));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -41,7 +41,7 @@ Account.prototype.list = function() {
|
||||||
/**
|
/**
|
||||||
* Fire up the database, retrieve the available keys for the user and initialize the email data access object
|
* Fire up the database, retrieve the available keys for the user and initialize the email data access object
|
||||||
*/
|
*/
|
||||||
Account.prototype.init = function(options, callback) {
|
Account.prototype.init = function(options) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
// account information for the email dao
|
// account information for the email dao
|
||||||
|
@ -53,68 +53,43 @@ Account.prototype.init = function(options, callback) {
|
||||||
|
|
||||||
// Pre-Flight check: don't even start to initialize stuff if the email address is not valid
|
// Pre-Flight check: don't even start to initialize stuff if the email address is not valid
|
||||||
if (!util.validateEmailAddress(options.emailAddress)) {
|
if (!util.validateEmailAddress(options.emailAddress)) {
|
||||||
return callback(new Error('The user email address is invalid!'));
|
return new Promise(function() {
|
||||||
|
throw new Error('The user email address is invalid!');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareDatabase();
|
|
||||||
|
|
||||||
// Pre-Flight check: initialize and prepare user's local database
|
// Pre-Flight check: initialize and prepare user's local database
|
||||||
function prepareDatabase() {
|
return self._accountStore.init(options.emailAddress).then(function() {
|
||||||
self._accountStore.init(options.emailAddress, function(err) {
|
// Migrate the databases if necessary
|
||||||
if (err) {
|
return self._updateHandler.update().catch(function(err) {
|
||||||
return callback(err);
|
throw new Error('Updating the internal database failed. Please reinstall the app! Reason: ' + err.message);
|
||||||
}
|
});
|
||||||
|
|
||||||
// Migrate the databases if necessary
|
}).then(function() {
|
||||||
self._updateHandler.update(function(err) {
|
// retrieve keypair fom devicestorage/cloud, refresh public key if signup was incomplete before
|
||||||
if (err) {
|
return self._keychain.getUserKeyPair(options.emailAddress);
|
||||||
return callback(new Error('Updating the internal database failed. Please reinstall the app! Reason: ' + err.message));
|
|
||||||
}
|
|
||||||
|
|
||||||
prepareKeys();
|
}).then(function(keys) {
|
||||||
|
// this is either a first start on a new device, OR a subsequent start without completing the signup,
|
||||||
|
// since we can't differenciate those cases here, do a public key refresh because it might be outdated
|
||||||
|
if (keys && keys.publicKey && !keys.privateKey) {
|
||||||
|
return self._keychain.refreshKeyForUserId({
|
||||||
|
userId: options.emailAddress,
|
||||||
|
overridePermission: true
|
||||||
|
}).then(function(publicKey) {
|
||||||
|
return {
|
||||||
|
publicKey: publicKey
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
// either signup was complete or no pubkey is available, so we're good here.
|
||||||
|
return keys;
|
||||||
|
|
||||||
});
|
}).then(function(keys) {
|
||||||
}
|
// init the email data access object
|
||||||
|
return self._emailDao.init({
|
||||||
// retrieve keypair fom devicestorage/cloud, refresh public key if signup was incomplete before
|
|
||||||
function prepareKeys() {
|
|
||||||
self._keychain.getUserKeyPair(options.emailAddress, function(err, keys) {
|
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is either a first start on a new device, OR a subsequent start without completing the signup,
|
|
||||||
// since we can't differenciate those cases here, do a public key refresh because it might be outdated
|
|
||||||
if (keys && keys.publicKey && !keys.privateKey) {
|
|
||||||
self._keychain.refreshKeyForUserId({
|
|
||||||
userId: options.emailAddress,
|
|
||||||
overridePermission: true
|
|
||||||
}, function(err, publicKey) {
|
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
initEmailDao({
|
|
||||||
publicKey: publicKey
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// either signup was complete or no pubkey is available, so we're good here.
|
|
||||||
initEmailDao(keys);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function initEmailDao(keys) {
|
|
||||||
self._emailDao.init({
|
|
||||||
account: account
|
account: account
|
||||||
}, function(err) {
|
}).then(function() {
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle offline and online gracefully ... arm dom event
|
// Handle offline and online gracefully ... arm dom event
|
||||||
window.addEventListener('online', self.onConnect.bind(self));
|
window.addEventListener('online', self.onConnect.bind(self));
|
||||||
window.addEventListener('offline', self.onDisconnect.bind(self));
|
window.addEventListener('offline', self.onDisconnect.bind(self));
|
||||||
|
@ -122,9 +97,9 @@ Account.prototype.init = function(options, callback) {
|
||||||
// add account object to the accounts array for the ng controllers
|
// add account object to the accounts array for the ng controllers
|
||||||
self._accounts.push(account);
|
self._accounts.push(account);
|
||||||
|
|
||||||
callback(null, keys);
|
return keys;
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -140,7 +115,7 @@ Account.prototype.isOnline = function() {
|
||||||
Account.prototype.onConnect = function(callback) {
|
Account.prototype.onConnect = function(callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var config = self._appConfig.config;
|
var config = self._appConfig.config;
|
||||||
|
|
||||||
callback = callback || self._dialog.error;
|
callback = callback || self._dialog.error;
|
||||||
|
|
||||||
if (!self.isOnline() || !self._emailDao || !self._emailDao._account) {
|
if (!self.isOnline() || !self._emailDao || !self._emailDao._account) {
|
||||||
|
@ -148,16 +123,8 @@ Account.prototype.onConnect = function(callback) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self._auth.getCredentials(function(err, credentials) {
|
// init imap/smtp clients
|
||||||
if (err) {
|
self._auth.getCredentials().then(function(credentials) {
|
||||||
callback(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
initClients(credentials);
|
|
||||||
});
|
|
||||||
|
|
||||||
function initClients(credentials) {
|
|
||||||
// add the maximum update batch size for imap folders to the imap configuration
|
// add the maximum update batch size for imap folders to the imap configuration
|
||||||
credentials.imap.maxUpdateSize = config.imapUpdateBatchSize;
|
credentials.imap.maxUpdateSize = config.imapUpdateBatchSize;
|
||||||
|
|
||||||
|
@ -174,12 +141,12 @@ Account.prototype.onConnect = function(callback) {
|
||||||
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), self._dialog.error);
|
||||||
|
|
||||||
// connect to clients
|
// connect to clients
|
||||||
self._emailDao.onConnect({
|
return self._emailDao.onConnect({
|
||||||
imapClient: imapClient,
|
imapClient: imapClient,
|
||||||
pgpMailer: pgpMailer,
|
pgpMailer: pgpMailer,
|
||||||
ignoreUploadOnSent: self._emailDao.checkIgnoreUploadOnSent(credentials.imap.host)
|
ignoreUploadOnSent: self._emailDao.checkIgnoreUploadOnSent(credentials.imap.host)
|
||||||
}, callback);
|
});
|
||||||
}
|
}).then(callback).catch(callback);
|
||||||
|
|
||||||
function onConnectionError(error) {
|
function onConnectionError(error) {
|
||||||
axe.debug('Connection error. Attempting reconnect in ' + config.reconnectInterval + ' ms. Error: ' + (error.errMsg || error.message) + (error.stack ? ('\n' + error.stack) : ''));
|
axe.debug('Connection error. Attempting reconnect in ' + config.reconnectInterval + ' ms. Error: ' + (error.errMsg || error.message) + (error.stack ? ('\n' + error.stack) : ''));
|
||||||
|
@ -203,7 +170,7 @@ Account.prototype.onConnect = function(callback) {
|
||||||
* Event handler that is called when the user agent goes offline.
|
* Event handler that is called when the user agent goes offline.
|
||||||
*/
|
*/
|
||||||
Account.prototype.onDisconnect = function() {
|
Account.prototype.onDisconnect = function() {
|
||||||
this._emailDao.onDisconnect();
|
return this._emailDao.onDisconnect();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -211,28 +178,18 @@ Account.prototype.onDisconnect = function() {
|
||||||
*/
|
*/
|
||||||
Account.prototype.logout = function() {
|
Account.prototype.logout = function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
// clear app config store
|
// clear app config store
|
||||||
self._auth.logout(function(err) {
|
return self._auth.logout().then(function() {
|
||||||
if (err) {
|
|
||||||
self._dialog.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// delete instance of imap-client and pgp-mailer
|
// delete instance of imap-client and pgp-mailer
|
||||||
self._emailDao.onDisconnect(function(err) {
|
return self._emailDao.onDisconnect();
|
||||||
if (err) {
|
|
||||||
self._dialog.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof window.chrome !== 'undefined' && chrome.runtime && chrome.runtime.reload) {
|
}).then(function() {
|
||||||
// reload chrome app
|
if (typeof window.chrome !== 'undefined' && chrome.runtime && chrome.runtime.reload) {
|
||||||
chrome.runtime.reload();
|
// reload chrome app
|
||||||
} else {
|
chrome.runtime.reload();
|
||||||
// navigate to login
|
} else {
|
||||||
window.location.href = '/';
|
// navigate to login
|
||||||
}
|
window.location.href = '/';
|
||||||
});
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
File diff suppressed because it is too large
Load Diff
|
@ -56,8 +56,9 @@ Outbox.prototype.stopChecking = function() {
|
||||||
* Put a email dto in the outbox for sending when ready
|
* Put a email dto in the outbox for sending when ready
|
||||||
* @param {Object} mail The Email DTO
|
* @param {Object} mail The Email DTO
|
||||||
* @param {Function} callback Invoked when the object was encrypted and persisted to disk
|
* @param {Function} callback Invoked when the object was encrypted and persisted to disk
|
||||||
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
Outbox.prototype.put = function(mail, callback) {
|
Outbox.prototype.put = function(mail) {
|
||||||
var self = this,
|
var self = this,
|
||||||
allReaders = mail.from.concat(mail.to.concat(mail.cc.concat(mail.bcc))); // all the users that should be able to read the mail
|
allReaders = mail.from.concat(mail.to.concat(mail.cc.concat(mail.bcc))); // all the users that should be able to read the mail
|
||||||
|
|
||||||
|
@ -66,67 +67,46 @@ Outbox.prototype.put = function(mail, callback) {
|
||||||
|
|
||||||
// do not encrypt mails with a bcc recipient, due to a possible privacy leak
|
// do not encrypt mails with a bcc recipient, due to a possible privacy leak
|
||||||
if (mail.bcc.length > 0) {
|
if (mail.bcc.length > 0) {
|
||||||
storeAndForward(mail);
|
return storeAndForward(mail);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
checkRecipients(allReaders);
|
return checkRecipients(allReaders).then(checkEncrypt);
|
||||||
|
|
||||||
// check if there are unregistered recipients
|
// check if there are unregistered recipients
|
||||||
function checkRecipients(recipients) {
|
function checkRecipients(recipients) {
|
||||||
var after = _.after(recipients.length, function() {
|
var pubkeyJobs = [];
|
||||||
checkEncrypt();
|
|
||||||
});
|
|
||||||
|
|
||||||
// find out if there are unregistered users
|
|
||||||
recipients.forEach(function(recipient) {
|
recipients.forEach(function(recipient) {
|
||||||
self._keychain.getReceiverPublicKey(recipient.address, function(err, key) {
|
var promise = self._keychain.getReceiverPublicKey(recipient.address).then(function(key) {
|
||||||
if (err) {
|
|
||||||
callback(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if a public key is available, add the recipient's key to the armored public keys,
|
// if a public key is available, add the recipient's key to the armored public keys,
|
||||||
// otherwise remember the recipient as unregistered for later sending
|
// otherwise remember the recipient as unregistered for later sending
|
||||||
if (key) {
|
if (key) {
|
||||||
mail.publicKeysArmored.push(key.publicKey);
|
mail.publicKeysArmored.push(key.publicKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
after();
|
|
||||||
});
|
});
|
||||||
|
pubkeyJobs.push(promise);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return Promise.all(pubkeyJobs);
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkEncrypt() {
|
function checkEncrypt() {
|
||||||
// only encrypt if all recipients have public keys
|
// only encrypt if all recipients have public keys
|
||||||
if (mail.publicKeysArmored.length < allReaders.length) {
|
if (mail.publicKeysArmored.length < allReaders.length) {
|
||||||
storeAndForward(mail);
|
return storeAndForward(mail);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// encrypts the body and attachments and persists the mail object
|
// encrypts the body and attachments and persists the mail object
|
||||||
self._emailDao.encrypt({
|
return self._emailDao.encrypt({
|
||||||
mail: mail,
|
mail: mail,
|
||||||
publicKeysArmored: mail.publicKeysArmored
|
publicKeysArmored: mail.publicKeysArmored
|
||||||
}, function(err) {
|
}).then(function() {
|
||||||
if (err) {
|
return storeAndForward(mail);
|
||||||
callback(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
storeAndForward(mail);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function storeAndForward(mail) {
|
function storeAndForward(mail) {
|
||||||
// store in outbox
|
// store in outbox
|
||||||
self._devicestorage.storeList([mail], outboxDb, function(err) {
|
return self._devicestorage.storeList([mail], outboxDb).then(function() {
|
||||||
if (err) {
|
|
||||||
callback(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
callback();
|
|
||||||
// don't wait for next round
|
// don't wait for next round
|
||||||
self._processOutbox(self._onUpdate);
|
self._processOutbox(self._onUpdate);
|
||||||
});
|
});
|
||||||
|
@ -149,83 +129,63 @@ Outbox.prototype._processOutbox = function(callback) {
|
||||||
self._outboxBusy = true;
|
self._outboxBusy = true;
|
||||||
|
|
||||||
// get pending mails from the outbox
|
// get pending mails from the outbox
|
||||||
self._devicestorage.listItems(outboxDb, 0, null, function(err, pendingMails) {
|
self._devicestorage.listItems(outboxDb, 0, null).then(function(pendingMails) {
|
||||||
// error, we're done here
|
// if we're not online, don't even bother sending mails.
|
||||||
if (err) {
|
if (!self._emailDao._account.online || _.isEmpty(pendingMails)) {
|
||||||
self._outboxBusy = false;
|
unsentMails = pendingMails.length;
|
||||||
callback(err);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we're not online, don't even bother sending mails.
|
var sendJobs = [];
|
||||||
if (!self._emailDao._account.online || _.isEmpty(pendingMails)) {
|
// send pending mails if possible
|
||||||
self._outboxBusy = false;
|
pendingMails.forEach(function(mail) {
|
||||||
callback(null, pendingMails.length);
|
sendJobs.push(send(mail));
|
||||||
return;
|
});
|
||||||
}
|
|
||||||
|
|
||||||
// we're done after all the mails have been handled
|
// we're done after all the mails have been handled
|
||||||
// update the outbox count...
|
// update the outbox count...
|
||||||
var after = _.after(pendingMails.length, function() {
|
return Promise.all(sendJobs);
|
||||||
self._outboxBusy = false;
|
|
||||||
callback(null, unsentMails);
|
|
||||||
});
|
|
||||||
|
|
||||||
// send pending mails if possible
|
}).then(function() {
|
||||||
pendingMails.forEach(function(mail) {
|
self._outboxBusy = false;
|
||||||
send(mail, after);
|
callback(null, unsentMails);
|
||||||
});
|
|
||||||
|
}).catch(function(err) {
|
||||||
|
self._outboxBusy = false;
|
||||||
|
callback(err);
|
||||||
});
|
});
|
||||||
|
|
||||||
// send the message
|
// send the message
|
||||||
function send(mail, done) {
|
function send(mail) {
|
||||||
|
|
||||||
// check is email is to be sent encrypted or as plaintex
|
// check is email is to be sent encrypted or as plaintex
|
||||||
if (mail.encrypted === true) {
|
if (mail.encrypted === true) {
|
||||||
// email was already encrypted before persisting in outbox, tell pgpmailer to send encrypted and not encrypt again
|
// email was already encrypted before persisting in outbox, tell pgpmailer to send encrypted and not encrypt again
|
||||||
self._emailDao.sendEncrypted({
|
return self._emailDao.sendEncrypted({
|
||||||
email: mail
|
email: mail
|
||||||
}, onSend);
|
}).then(onSend).catch(sendFailed);
|
||||||
} else {
|
} else {
|
||||||
// send email as plaintext
|
// send email as plaintext
|
||||||
self._emailDao.sendPlaintext({
|
return self._emailDao.sendPlaintext({
|
||||||
email: mail
|
email: mail
|
||||||
}, onSend);
|
}).then(onSend).catch(sendFailed);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSend(err) {
|
function onSend() {
|
||||||
if (err) {
|
|
||||||
self._outboxBusy = false;
|
|
||||||
if (err.code === 42) {
|
|
||||||
// offline try again later
|
|
||||||
done();
|
|
||||||
} else {
|
|
||||||
self._outboxBusy = false;
|
|
||||||
callback(err);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove the pending mail from the storage
|
|
||||||
removeFromStorage(mail, done);
|
|
||||||
|
|
||||||
// fire sent notification
|
// fire sent notification
|
||||||
if (typeof self.onSent === 'function') {
|
if (typeof self.onSent === 'function') {
|
||||||
self.onSent(mail);
|
self.onSent(mail);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// removes the mail object from disk after successfully sending it
|
// removes the mail object from disk after successfully sending it
|
||||||
function removeFromStorage(mail, done) {
|
return self._devicestorage.removeList(outboxDb + '_' + mail.uid);
|
||||||
self._devicestorage.removeList(outboxDb + '_' + mail.uid, function(err) {
|
}
|
||||||
if (err) {
|
|
||||||
self._outboxBusy = false;
|
function sendFailed(err) {
|
||||||
callback(err);
|
if (err.code === 42) {
|
||||||
|
// offline. resolve promise and try again later
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
throw err;
|
||||||
done();
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
|
@ -13,27 +13,24 @@ function Admin(adminRestDao) {
|
||||||
* @param {String} options.emailAddress The desired email address
|
* @param {String} options.emailAddress The desired email address
|
||||||
* @param {String} options.password The password to be used for the account.
|
* @param {String} options.password The password to be used for the account.
|
||||||
* @param {String} options.phone The user's mobile phone number (required for verification and password reset).
|
* @param {String} options.phone The user's mobile phone number (required for verification and password reset).
|
||||||
* @param {Function} callback(error)
|
|
||||||
*/
|
*/
|
||||||
Admin.prototype.createUser = function(options, callback) {
|
Admin.prototype.createUser = function(options) {
|
||||||
var uri;
|
var self = this;
|
||||||
|
return new Promise(function(resolve) {
|
||||||
|
if (!options.emailAddress || !options.password || !options.phone) {
|
||||||
|
throw new Error('Incomplete arguments!');
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
|
||||||
if (!options.emailAddress || !options.password || !options.phone) {
|
}).then(function() {
|
||||||
callback(new Error('Incomplete arguments!'));
|
return self._restDao.post(options, '/user');
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
uri = '/user';
|
}).catch(function(err) {
|
||||||
this._restDao.post(options, uri, function(err) {
|
|
||||||
if (err && err.code === 409) {
|
if (err && err.code === 409) {
|
||||||
callback(new Error('User name is already taken!'));
|
throw new Error('User name is already taken!');
|
||||||
return;
|
|
||||||
} else if (err) {
|
|
||||||
callback(new Error('Error creating new user!'));
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
callback();
|
throw new Error('Error creating new user!');
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -41,23 +38,25 @@ Admin.prototype.createUser = function(options, callback) {
|
||||||
* Verify a user's phone number by confirming a token to the server.
|
* Verify a user's phone number by confirming a token to the server.
|
||||||
* @param {String} options.emailAddress The desired email address
|
* @param {String} options.emailAddress The desired email address
|
||||||
* @param {String} options.token The validation token.
|
* @param {String} options.token The validation token.
|
||||||
* @param {Function} callback(error)
|
|
||||||
*/
|
*/
|
||||||
Admin.prototype.validateUser = function(options, callback) {
|
Admin.prototype.validateUser = function(options) {
|
||||||
var uri;
|
var self = this;
|
||||||
|
return new Promise(function(resolve) {
|
||||||
if (!options.emailAddress || !options.token) {
|
if (!options.emailAddress || !options.token) {
|
||||||
callback(new Error('Incomplete arguments!'));
|
throw new Error('Incomplete arguments!');
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
uri = '/user/validate';
|
|
||||||
this._restDao.post(options, uri, function(err) {
|
|
||||||
if (!err || (err && err.code === 202)) {
|
|
||||||
// success
|
|
||||||
callback();
|
|
||||||
} else {
|
|
||||||
callback(new Error('Validation failed!'));
|
|
||||||
}
|
}
|
||||||
|
resolve();
|
||||||
|
|
||||||
|
}).then(function() {
|
||||||
|
var uri = '/user/validate';
|
||||||
|
return self._restDao.post(options, uri);
|
||||||
|
|
||||||
|
}).catch(function(err) {
|
||||||
|
if (err && err.code === 202) {
|
||||||
|
// success
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('Validation failed!');
|
||||||
});
|
});
|
||||||
};
|
};
|
|
@ -30,17 +30,17 @@ function Auth(appConfigStore, oauth, pgp) {
|
||||||
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);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -57,84 +57,66 @@ 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(pt) {
|
||||||
if (err) {
|
if (!pt.signaturesValid) {
|
||||||
return callback(err);
|
throw new Error('Verifying PGP signature of encrypted password failed!');
|
||||||
}
|
}
|
||||||
|
|
||||||
self.passwordNeedsDecryption = false;
|
self.passwordNeedsDecryption = false;
|
||||||
self.password = cleartext;
|
self.password = pt.decrypted;
|
||||||
|
}).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);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -158,100 +140,69 @@ Auth.prototype.setCredentials = function(options) {
|
||||||
this.imap = options.imap; // host, port, secure, ca
|
this.imap = options.imap; // host, port, secure, ca
|
||||||
};
|
};
|
||||||
|
|
||||||
Auth.prototype.storeCredentials = function(callback) {
|
Auth.prototype.storeCredentials = function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
if (!self.credentialsDirty) {
|
if (!self.credentialsDirty) {
|
||||||
return callback();
|
// nothing to store if credentials not dirty
|
||||||
|
return new Promise(function(resolve) {
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// persist the config
|
// persist the config
|
||||||
self._appConfigStore.storeList([self.smtp], SMTP_DB_KEY, function(err) {
|
var storeSmtp = self._appConfigStore.storeList([self.smtp], SMTP_DB_KEY);
|
||||||
if (err) {
|
var storeImap = self._appConfigStore.storeList([self.imap], IMAP_DB_KEY);
|
||||||
return callback(err);
|
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();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self._appConfigStore.storeList([self.imap], IMAP_DB_KEY, function(err) {
|
if (self.passwordNeedsDecryption) {
|
||||||
if (err) {
|
// password is not decrypted yet, so no need to re-encrypt it before storing...
|
||||||
return callback(err);
|
return self._appConfigStore.storeList([self.password], PASSWD_DB_KEY).then(resolve);
|
||||||
}
|
}
|
||||||
|
return self._pgp.encrypt(self.password, undefined).then(function(ciphertext) {
|
||||||
self._appConfigStore.storeList([self.emailAddress], EMAIL_ADDR_DB_KEY, function(err) {
|
return self._appConfigStore.storeList([ciphertext], PASSWD_DB_KEY).then(resolve);
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
self._appConfigStore.storeList([self.username], USERNAME_DB_KEY, function(err) {
|
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
self._appConfigStore.storeList([self.realname], REALNAME_DB_KEY, function(err) {
|
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!self.password) {
|
|
||||||
self.credentialsDirty = false;
|
|
||||||
return callback();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self.passwordNeedsDecryption) {
|
|
||||||
// password is not decrypted yet, so no need to re-encrypt it before storing...
|
|
||||||
self._appConfigStore.storeList([self.password], PASSWD_DB_KEY, function(err) {
|
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.credentialsDirty = false;
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self._pgp.encrypt(self.password, undefined, function(err, ciphertext) {
|
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
self._appConfigStore.storeList([ciphertext], PASSWD_DB_KEY, function(err) {
|
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.credentialsDirty = false;
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return Promise.all([
|
||||||
|
storeSmtp,
|
||||||
|
storeImap,
|
||||||
|
storeEmailAddress,
|
||||||
|
storeUsername,
|
||||||
|
storeRealname,
|
||||||
|
storePassword
|
||||||
|
]).then(function() {
|
||||||
|
self.credentialsDirty = false;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
});
|
};
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -286,40 +237,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();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -327,67 +269,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];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -407,7 +326,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);
|
self.storeCredentials().then(callback).catch(callback);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -431,14 +350,9 @@ Auth.prototype.handleCertificateUpdate = function(component, onConnect, callback
|
||||||
|
|
||||||
self[component].ca = pemEncodedCert;
|
self[component].ca = pemEncodedCert;
|
||||||
self.credentialsDirty = true;
|
self.credentialsDirty = true;
|
||||||
self.storeCredentials(function(err) {
|
self.storeCredentials().then(function() {
|
||||||
if (err) {
|
|
||||||
callback(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
onConnect(callback);
|
onConnect(callback);
|
||||||
});
|
}).catch(callback);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -446,22 +360,15 @@ Auth.prototype.handleCertificateUpdate = function(component, onConnect, callback
|
||||||
/**
|
/**
|
||||||
* 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();
|
|
||||||
});
|
});
|
||||||
};
|
};
|
|
@ -28,50 +28,57 @@ function DeviceStorage(lawnchairDAO) {
|
||||||
/**
|
/**
|
||||||
* Initialize the lawnchair database
|
* Initialize the lawnchair database
|
||||||
* @param {String} dbName The name of the database
|
* @param {String} dbName The name of the database
|
||||||
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
DeviceStorage.prototype.init = function(dbName, callback) {
|
DeviceStorage.prototype.init = function(dbName) {
|
||||||
this._lawnchairDAO.init(dbName, callback);
|
return this._lawnchairDAO.init(dbName);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores a list of encrypted items in the object store
|
* Stores a list of encrypted items in the object store
|
||||||
* @param list [Array] The list of items to be persisted
|
* @param list [Array] The list of items to be persisted
|
||||||
* @param type [String] The type of item to be persisted e.g. 'email'
|
* @param type [String] The type of item to be persisted e.g. 'email'
|
||||||
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
DeviceStorage.prototype.storeList = function(list, type, callback) {
|
DeviceStorage.prototype.storeList = function(list, type) {
|
||||||
var key, items = [];
|
var self = this;
|
||||||
|
return new Promise(function(resolve) {
|
||||||
|
var key, items = [];
|
||||||
|
list = list || [];
|
||||||
|
|
||||||
// nothing to store
|
// validate type
|
||||||
if (!list || list.length === 0) {
|
if (!type) {
|
||||||
callback();
|
throw new Error('Type is not set!');
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
// validate type
|
// format items for batch storing in dao
|
||||||
if (!type) {
|
list.forEach(function(i) {
|
||||||
callback({
|
key = createKey(i, type);
|
||||||
errMsg: 'Type is not set!'
|
|
||||||
|
items.push({
|
||||||
|
key: key,
|
||||||
|
object: i
|
||||||
|
});
|
||||||
});
|
});
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// format items for batch storing in dao
|
resolve(items);
|
||||||
list.forEach(function(i) {
|
|
||||||
key = createKey(i, type);
|
|
||||||
|
|
||||||
items.push({
|
}).then(function(items) {
|
||||||
key: key,
|
// nothing to store
|
||||||
object: i
|
if (items.length === 0) {
|
||||||
});
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self._lawnchairDAO.batch(items);
|
||||||
});
|
});
|
||||||
|
|
||||||
this._lawnchairDAO.batch(items, callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes items of a certain type from storage
|
* Deletes items of a certain type from storage
|
||||||
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
DeviceStorage.prototype.removeList = function(type, callback) {
|
DeviceStorage.prototype.removeList = function(type) {
|
||||||
this._lawnchairDAO.removeList(type, callback);
|
return this._lawnchairDAO.removeList(type);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -79,17 +86,19 @@ DeviceStorage.prototype.removeList = function(type, callback) {
|
||||||
* @param type [String] The type of item e.g. 'email'
|
* @param type [String] The type of item e.g. 'email'
|
||||||
* @param offset [Number] The offset of items to fetch (0 is the last stored item)
|
* @param offset [Number] The offset of items to fetch (0 is the last stored item)
|
||||||
* @param num [Number] The number of items to fetch (null means fetch all)
|
* @param num [Number] The number of items to fetch (null means fetch all)
|
||||||
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
DeviceStorage.prototype.listItems = function(type, offset, num, callback) {
|
DeviceStorage.prototype.listItems = function(type, offset, num) {
|
||||||
// fetch all items of a certain type from the data-store
|
// fetch all items of a certain type from the data-store
|
||||||
this._lawnchairDAO.list(type, offset, num, callback);
|
return this._lawnchairDAO.list(type, offset, num);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear the whole device data-store
|
* Clear the whole device data-store
|
||||||
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
DeviceStorage.prototype.clear = function(callback) {
|
DeviceStorage.prototype.clear = function() {
|
||||||
this._lawnchairDAO.clear(callback);
|
return this._lawnchairDAO.clear();
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
|
@ -12,14 +12,6 @@ function Invitation(invitationRestDao) {
|
||||||
this._restDao = invitationRestDao;
|
this._restDao = invitationRestDao;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// Constants
|
|
||||||
//
|
|
||||||
|
|
||||||
Invitation.INVITE_MISSING = 1;
|
|
||||||
Invitation.INVITE_PENDING = 2;
|
|
||||||
Invitation.INVITE_SUCCESS = 4;
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// API
|
// API
|
||||||
//
|
//
|
||||||
|
@ -28,35 +20,18 @@ Invitation.INVITE_SUCCESS = 4;
|
||||||
* Notes an invite for the recipient by the sender in the invitation web service
|
* Notes an invite for the recipient by the sender in the invitation web service
|
||||||
* @param {String} options.recipient User ID of the recipient
|
* @param {String} options.recipient User ID of the recipient
|
||||||
* @param {String} options.sender User ID of the sender
|
* @param {String} options.sender User ID of the sender
|
||||||
* @param {Function} callback(error, status) Returns information if the invitation worked (INVITE_SUCCESS), if an invitation is already pendin (INVITE_PENDING), or information if an error occurred.
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
Invitation.prototype.invite = function(options, callback) {
|
Invitation.prototype.invite = function(options) {
|
||||||
if (typeof options !== 'object' || typeof options.recipient !== 'string' || typeof options.recipient !== 'string') {
|
var self = this;
|
||||||
callback({
|
return new Promise(function(resolve) {
|
||||||
errMsg: 'erroneous usage of api: incorrect parameters!'
|
if (typeof options !== 'object' || typeof options.recipient !== 'string' || typeof options.sender !== 'string') {
|
||||||
});
|
throw new Error('erroneous usage of api: incorrect parameters!');
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var uri = '/invitation/recipient/' + options.recipient + '/sender/' + options.sender;
|
|
||||||
this._restDao.put({}, uri, completed);
|
|
||||||
|
|
||||||
function completed(error, res, status) {
|
|
||||||
if (error) {
|
|
||||||
callback(error);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
resolve();
|
||||||
|
|
||||||
if (status === 201) {
|
}).then(function() {
|
||||||
callback(null, Invitation.INVITE_SUCCESS);
|
var uri = '/invitation/recipient/' + options.recipient + '/sender/' + options.sender;
|
||||||
return;
|
return self._restDao.put({}, uri);
|
||||||
} else if (status === 304) {
|
});
|
||||||
callback(null, Invitation.INVITE_PENDING);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
callback({
|
|
||||||
errMsg: 'unexpected invitation state'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
};
|
File diff suppressed because it is too large
Load Diff
|
@ -13,87 +13,91 @@ function LawnchairDAO() {}
|
||||||
/**
|
/**
|
||||||
* Initialize the lawnchair database
|
* Initialize the lawnchair database
|
||||||
* @param {String} dbName The name of the database
|
* @param {String} dbName The name of the database
|
||||||
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
LawnchairDAO.prototype.init = function(dbName, callback) {
|
LawnchairDAO.prototype.init = function(dbName) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
if (!dbName) {
|
||||||
|
throw new Error('Lawnchair DB name must be specified!');
|
||||||
|
}
|
||||||
|
|
||||||
if (!dbName) {
|
self._db = new Lawnchair({
|
||||||
return callback(new Error('Lawnchair DB name must be specified!'));
|
name: dbName
|
||||||
}
|
}, function(success) {
|
||||||
|
if (success) {
|
||||||
self._db = new Lawnchair({
|
resolve();
|
||||||
name: dbName
|
} else {
|
||||||
}, function(success) {
|
reject(new Error('Lawnchair initialization ' + dbName + ' failed!'));
|
||||||
callback(success ? undefined : new Error('Lawnchair initialization ' + dbName + ' failed!'));
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create or update an object
|
* Create or update an object
|
||||||
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
LawnchairDAO.prototype.persist = function(key, object, callback) {
|
LawnchairDAO.prototype.persist = function(key, object) {
|
||||||
if (!key || !object) {
|
var self = this;
|
||||||
callback({
|
return new Promise(function(resolve, reject) {
|
||||||
errMsg: 'Key and Object must be set!'
|
if (!key || !object) {
|
||||||
});
|
throw new Error('Key and Object must be set!');
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._db.save({
|
|
||||||
key: key,
|
|
||||||
object: object
|
|
||||||
}, function(persisted) {
|
|
||||||
if (persisted.key !== key) {
|
|
||||||
callback({
|
|
||||||
errMsg: 'Persisting failed!'
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
callback();
|
self._db.save({
|
||||||
|
key: key,
|
||||||
|
object: object
|
||||||
|
}, function(persisted) {
|
||||||
|
if (persisted.key !== key) {
|
||||||
|
reject(new Error('Persisting failed!'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Persist a bunch of items at once
|
* Persist a bunch of items at once
|
||||||
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
LawnchairDAO.prototype.batch = function(list, callback) {
|
LawnchairDAO.prototype.batch = function(list) {
|
||||||
if (!(list instanceof Array)) {
|
var self = this;
|
||||||
callback({
|
return new Promise(function(resolve, reject) {
|
||||||
errMsg: 'Input must be of type Array!'
|
if (!(list instanceof Array)) {
|
||||||
});
|
throw new Error('Input must be of type Array!');
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._db.batch(list, function(res) {
|
|
||||||
if (!res) {
|
|
||||||
callback({
|
|
||||||
errMsg: 'Persisting batch failed!'
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
callback();
|
self._db.batch(list, function(res) {
|
||||||
|
if (!res) {
|
||||||
|
reject(new Error('Persisting batch failed!'));
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read a single item by its key
|
* Read a single item by its key
|
||||||
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
LawnchairDAO.prototype.read = function(key, callback) {
|
LawnchairDAO.prototype.read = function(key) {
|
||||||
if (!key) {
|
var self = this;
|
||||||
callback({
|
return new Promise(function(resolve) {
|
||||||
errMsg: 'Key must be specified!'
|
if (!key) {
|
||||||
});
|
throw new Error('Key must be specified!');
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._db.get(key, function(o) {
|
|
||||||
if (o) {
|
|
||||||
callback(null, o.object);
|
|
||||||
} else {
|
|
||||||
callback();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self._db.get(key, function(o) {
|
||||||
|
if (o) {
|
||||||
|
resolve(o.object);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -102,113 +106,131 @@ LawnchairDAO.prototype.read = function(key, callback) {
|
||||||
* @param type [String] The type of item e.g. 'email'
|
* @param type [String] The type of item e.g. 'email'
|
||||||
* @param offset [Number] The offset of items to fetch (0 is the last stored item)
|
* @param offset [Number] The offset of items to fetch (0 is the last stored item)
|
||||||
* @param num [Number] The number of items to fetch (null means fetch all)
|
* @param num [Number] The number of items to fetch (null means fetch all)
|
||||||
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
LawnchairDAO.prototype.list = function(type, offset, num, callback) {
|
LawnchairDAO.prototype.list = function(type, offset, num) {
|
||||||
var self = this,
|
var self = this;
|
||||||
i, from, to,
|
return new Promise(function(resolve) {
|
||||||
matchingKeys = [],
|
var i, from, to,
|
||||||
intervalKeys = [],
|
matchingKeys = [],
|
||||||
list = [];
|
intervalKeys = [],
|
||||||
|
list = [];
|
||||||
|
|
||||||
// validate input
|
// validate input
|
||||||
if (!type || typeof offset === 'undefined' || typeof num === 'undefined') {
|
if (!type || typeof offset === 'undefined' || typeof num === 'undefined') {
|
||||||
callback({
|
throw new Error('Args not is not set!');
|
||||||
errMsg: 'Args not is not set!'
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get all keys
|
|
||||||
self._db.keys(function(keys) {
|
|
||||||
|
|
||||||
// check if key begins with type
|
|
||||||
keys.forEach(function(key) {
|
|
||||||
if (key.indexOf(type) === 0) {
|
|
||||||
matchingKeys.push(key);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// sort keys
|
|
||||||
matchingKeys.sort();
|
|
||||||
|
|
||||||
// set window of items to fetch
|
|
||||||
// if num is null, list all items
|
|
||||||
from = (num) ? matchingKeys.length - offset - num : 0;
|
|
||||||
to = matchingKeys.length - 1 - offset;
|
|
||||||
// filter items within requested interval
|
|
||||||
for (i = 0; i < matchingKeys.length; i++) {
|
|
||||||
if (i >= from && i <= to) {
|
|
||||||
intervalKeys.push(matchingKeys[i]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// return if there are no matching keys
|
// get all keys
|
||||||
if (intervalKeys.length === 0) {
|
self._db.keys(function(keys) {
|
||||||
callback(null, list);
|
// check if key begins with type
|
||||||
return;
|
keys.forEach(function(key) {
|
||||||
}
|
if (key.indexOf(type) === 0) {
|
||||||
|
matchingKeys.push(key);
|
||||||
// fetch all items from data-store with matching key
|
}
|
||||||
self._db.get(intervalKeys, function(intervalList) {
|
|
||||||
intervalList.forEach(function(item) {
|
|
||||||
list.push(item.object);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// return only the interval between offset and num
|
// sort keys
|
||||||
callback(null, list);
|
matchingKeys.sort();
|
||||||
});
|
|
||||||
|
|
||||||
|
// set window of items to fetch
|
||||||
|
// if num is null, list all items
|
||||||
|
from = (num) ? matchingKeys.length - offset - num : 0;
|
||||||
|
to = matchingKeys.length - 1 - offset;
|
||||||
|
// filter items within requested interval
|
||||||
|
for (i = 0; i < matchingKeys.length; i++) {
|
||||||
|
if (i >= from && i <= to) {
|
||||||
|
intervalKeys.push(matchingKeys[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return if there are no matching keys
|
||||||
|
if (intervalKeys.length === 0) {
|
||||||
|
resolve(list);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetch all items from data-store with matching key
|
||||||
|
self._db.get(intervalKeys, function(intervalList) {
|
||||||
|
intervalList.forEach(function(item) {
|
||||||
|
list.push(item.object);
|
||||||
|
});
|
||||||
|
|
||||||
|
// return only the interval between offset and num
|
||||||
|
resolve(list);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes an object liter from local storage by its key (delete)
|
* Removes an object liter from local storage by its key (delete)
|
||||||
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
LawnchairDAO.prototype.remove = function(key, callback) {
|
LawnchairDAO.prototype.remove = function(key) {
|
||||||
this._db.remove(key, callback);
|
var self = this;
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
self._db.remove(key, function(err) {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes an object liter from local storage by its key (delete)
|
* Removes an object liter from local storage by its key (delete)
|
||||||
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
LawnchairDAO.prototype.removeList = function(type, callback) {
|
LawnchairDAO.prototype.removeList = function(type) {
|
||||||
var self = this,
|
var self = this;
|
||||||
matchingKeys = [],
|
return new Promise(function(resolve) {
|
||||||
after;
|
var matchingKeys = [],
|
||||||
|
after;
|
||||||
|
|
||||||
// validate type
|
// validate type
|
||||||
if (!type) {
|
if (!type) {
|
||||||
callback({
|
throw new Error('Type is not set!');
|
||||||
errMsg: 'Type is not set!'
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get all keys
|
|
||||||
self._db.keys(function(keys) {
|
|
||||||
// check if key begins with type
|
|
||||||
keys.forEach(function(key) {
|
|
||||||
if (key.indexOf(type) === 0) {
|
|
||||||
matchingKeys.push(key);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (matchingKeys.length < 1) {
|
|
||||||
callback();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove all matching keys
|
// get all keys
|
||||||
after = _.after(matchingKeys.length, callback);
|
self._db.keys(function(keys) {
|
||||||
_.each(matchingKeys, function(key) {
|
// check if key begins with type
|
||||||
self._db.remove(key, after);
|
keys.forEach(function(key) {
|
||||||
|
if (key.indexOf(type) === 0) {
|
||||||
|
matchingKeys.push(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (matchingKeys.length < 1) {
|
||||||
|
resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove all matching keys
|
||||||
|
after = _.after(matchingKeys.length, resolve);
|
||||||
|
_.each(matchingKeys, function(key) {
|
||||||
|
self._db.remove(key, after);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears the whole local storage cache
|
* Clears the whole local storage cache
|
||||||
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
LawnchairDAO.prototype.clear = function(callback) {
|
LawnchairDAO.prototype.clear = function() {
|
||||||
this._db.nuke(callback);
|
var self = this;
|
||||||
};
|
return new Promise(function(resolve, reject) {
|
||||||
|
self._db.nuke(function(err) {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
|
@ -4,15 +4,13 @@ var ngModule = angular.module('woServices');
|
||||||
ngModule.service('newsletter', Newsletter);
|
ngModule.service('newsletter', Newsletter);
|
||||||
module.exports = Newsletter;
|
module.exports = Newsletter;
|
||||||
|
|
||||||
function Newsletter($q) {
|
function Newsletter() {}
|
||||||
this._q = $q;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sign up to the whiteout newsletter
|
* Sign up to the whiteout newsletter
|
||||||
*/
|
*/
|
||||||
Newsletter.prototype.signup = function(emailAddress, agree) {
|
Newsletter.prototype.signup = function(emailAddress, agree) {
|
||||||
return this._q(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
// validate email address
|
// validate email address
|
||||||
if (emailAddress.indexOf('@') < 0) {
|
if (emailAddress.indexOf('@') < 0) {
|
||||||
reject(new Error('Invalid email address!'));
|
reject(new Error('Invalid email address!'));
|
||||||
|
|
|
@ -20,33 +20,33 @@ OAuth.prototype.isSupported = function() {
|
||||||
* Request an OAuth token from chrome for gmail users
|
* Request an OAuth token from chrome for gmail users
|
||||||
* @param {String} emailAddress The user's email address (optional)
|
* @param {String} emailAddress The user's email address (optional)
|
||||||
*/
|
*/
|
||||||
OAuth.prototype.getOAuthToken = function(emailAddress, callback) {
|
OAuth.prototype.getOAuthToken = function(emailAddress) {
|
||||||
var idOptions = {
|
return new Promise(function(resolve, reject) {
|
||||||
interactive: true
|
var idOptions = {
|
||||||
};
|
interactive: true
|
||||||
|
};
|
||||||
|
|
||||||
// check which runtime the app is running under
|
// check which runtime the app is running under
|
||||||
chrome.runtime.getPlatformInfo(function(platformInfo) {
|
chrome.runtime.getPlatformInfo(function(platformInfo) {
|
||||||
if (chrome.runtime.lastError || !platformInfo) {
|
if (chrome.runtime.lastError || !platformInfo) {
|
||||||
callback(new Error('Error getting chrome platform info!'));
|
reject(new Error('Error getting chrome platform info!'));
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (emailAddress && platformInfo.os.indexOf('android') !== -1) {
|
|
||||||
// set accountHint so that native Android account picker does not show up each time
|
|
||||||
idOptions.accountHint = emailAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get OAuth Token from chrome
|
|
||||||
chrome.identity.getAuthToken(idOptions, function(token) {
|
|
||||||
if (chrome.runtime.lastError || !token) {
|
|
||||||
callback({
|
|
||||||
errMsg: 'Error fetching an OAuth token for the user!'
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(null, token);
|
if (emailAddress && platformInfo.os.indexOf('android') !== -1) {
|
||||||
|
// set accountHint so that native Android account picker does not show up each time
|
||||||
|
idOptions.accountHint = emailAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get OAuth Token from chrome
|
||||||
|
chrome.identity.getAuthToken(idOptions, function(token) {
|
||||||
|
if (chrome.runtime.lastError || !token) {
|
||||||
|
reject(new Error('Error fetching an OAuth token for the user!'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(token);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -56,20 +56,20 @@ OAuth.prototype.getOAuthToken = function(emailAddress, callback) {
|
||||||
* @param {String} options.oldToken The old token to be removed
|
* @param {String} options.oldToken The old token to be removed
|
||||||
* @param {String} options.emailAddress The user's email address (optional)
|
* @param {String} options.emailAddress The user's email address (optional)
|
||||||
*/
|
*/
|
||||||
OAuth.prototype.refreshToken = function(options, callback) {
|
OAuth.prototype.refreshToken = function(options) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
return new Promise(function(resolve) {
|
||||||
|
if (!options.oldToken) {
|
||||||
|
throw new Error('oldToken option not set!');
|
||||||
|
}
|
||||||
|
|
||||||
if (!options.oldToken) {
|
// remove cached token
|
||||||
callback(new Error('oldToken option not set!'));
|
chrome.identity.removeCachedAuthToken({
|
||||||
return;
|
token: options.oldToken
|
||||||
}
|
}, function() {
|
||||||
|
// get a new token
|
||||||
// remove cached token
|
self.getOAuthToken(options.emailAddress).then(resolve);
|
||||||
chrome.identity.removeCachedAuthToken({
|
});
|
||||||
token: options.oldToken
|
|
||||||
}, function() {
|
|
||||||
// get a new token
|
|
||||||
self.getOAuthToken(options.emailAddress, callback);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -77,25 +77,26 @@ OAuth.prototype.refreshToken = function(options, callback) {
|
||||||
* Get email address from google api
|
* Get email address from google api
|
||||||
* @param {String} token The oauth token
|
* @param {String} token The oauth token
|
||||||
*/
|
*/
|
||||||
OAuth.prototype.queryEmailAddress = function(token, callback) {
|
OAuth.prototype.queryEmailAddress = function(token) {
|
||||||
if (!token) {
|
var self = this;
|
||||||
callback({
|
return new Promise(function(resolve) {
|
||||||
errMsg: 'Invalid OAuth token!'
|
if (!token) {
|
||||||
});
|
throw new Error('Invalid OAuth token!');
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// fetch gmail user's email address from the Google Authorization Server
|
|
||||||
this._googleApi.get({
|
|
||||||
uri: '/oauth2/v3/userinfo?access_token=' + token
|
|
||||||
}, function(err, info) {
|
|
||||||
if (err || !info || !info.email) {
|
|
||||||
callback({
|
|
||||||
errMsg: 'Error looking up email address on google api!'
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(null, info.email);
|
resolve();
|
||||||
|
|
||||||
|
}).then(function() {
|
||||||
|
// fetch gmail user's email address from the Google Authorization Server
|
||||||
|
return self._googleApi.get({
|
||||||
|
uri: '/oauth2/v3/userinfo?access_token=' + token
|
||||||
|
});
|
||||||
|
|
||||||
|
}).then(function(info) {
|
||||||
|
if (!info || !info.email) {
|
||||||
|
throw new Error('Error looking up email address on google api!');
|
||||||
|
}
|
||||||
|
|
||||||
|
return info.email;
|
||||||
});
|
});
|
||||||
};
|
};
|
|
@ -16,19 +16,20 @@ function PrivateKey(privateKeyRestDao) {
|
||||||
* Request registration of a new device by fetching registration session key.
|
* Request registration of a new device by fetching registration session key.
|
||||||
* @param {String} options.userId The user's email address
|
* @param {String} options.userId The user's email address
|
||||||
* @param {String} options.deviceName The device's memorable name
|
* @param {String} options.deviceName The device's memorable name
|
||||||
* @param {Function} callback(error, regSessionKey)
|
|
||||||
* @return {Object} {encryptedRegSessionKey:[base64]}
|
* @return {Object} {encryptedRegSessionKey:[base64]}
|
||||||
*/
|
*/
|
||||||
PrivateKey.prototype.requestDeviceRegistration = function(options, callback) {
|
PrivateKey.prototype.requestDeviceRegistration = function(options) {
|
||||||
var uri;
|
var self = this;
|
||||||
|
return new Promise(function(resolve) {
|
||||||
|
if (!options.userId || !options.deviceName) {
|
||||||
|
throw new Error('Incomplete arguments!');
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
|
||||||
if (!options.userId || !options.deviceName) {
|
}).then(function() {
|
||||||
callback(new Error('Incomplete arguments!'));
|
var uri = '/device/user/' + options.userId + '/devicename/' + options.deviceName;
|
||||||
return;
|
return self._restDao.post(undefined, uri);
|
||||||
}
|
});
|
||||||
|
|
||||||
uri = '/device/user/' + options.userId + '/devicename/' + options.deviceName;
|
|
||||||
this._restDao.post(undefined, uri, callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -37,18 +38,19 @@ PrivateKey.prototype.requestDeviceRegistration = function(options, callback) {
|
||||||
* @param {String} options.deviceName The device's memorable name
|
* @param {String} options.deviceName The device's memorable name
|
||||||
* @param {String} options.encryptedDeviceSecret The base64 encoded encrypted device secret
|
* @param {String} options.encryptedDeviceSecret The base64 encoded encrypted device secret
|
||||||
* @param {String} options.iv The iv used for encryption
|
* @param {String} options.iv The iv used for encryption
|
||||||
* @param {Function} callback(error)
|
|
||||||
*/
|
*/
|
||||||
PrivateKey.prototype.uploadDeviceSecret = function(options, callback) {
|
PrivateKey.prototype.uploadDeviceSecret = function(options) {
|
||||||
var uri;
|
var self = this;
|
||||||
|
return new Promise(function(resolve) {
|
||||||
|
if (!options.userId || !options.deviceName || !options.encryptedDeviceSecret || !options.iv) {
|
||||||
|
throw new Error('Incomplete arguments!');
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
|
||||||
if (!options.userId || !options.deviceName || !options.encryptedDeviceSecret || !options.iv) {
|
}).then(function() {
|
||||||
callback(new Error('Incomplete arguments!'));
|
var uri = '/device/user/' + options.userId + '/devicename/' + options.deviceName;
|
||||||
return;
|
return self._restDao.put(options, uri);
|
||||||
}
|
});
|
||||||
|
|
||||||
uri = '/device/user/' + options.userId + '/devicename/' + options.deviceName;
|
|
||||||
this._restDao.put(options, uri, callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -58,19 +60,20 @@ PrivateKey.prototype.uploadDeviceSecret = function(options, callback) {
|
||||||
/**
|
/**
|
||||||
* Request authSessionKeys required for upload the encrypted private PGP key.
|
* Request authSessionKeys required for upload the encrypted private PGP key.
|
||||||
* @param {String} options.userId The user's email address
|
* @param {String} options.userId The user's email address
|
||||||
* @param {Function} callback(error, authSessionKey)
|
|
||||||
* @return {Object} {sessionId, encryptedAuthSessionKey:[base64 encoded], encryptedChallenge:[base64 encoded]}
|
* @return {Object} {sessionId, encryptedAuthSessionKey:[base64 encoded], encryptedChallenge:[base64 encoded]}
|
||||||
*/
|
*/
|
||||||
PrivateKey.prototype.requestAuthSessionKey = function(options, callback) {
|
PrivateKey.prototype.requestAuthSessionKey = function(options) {
|
||||||
var uri;
|
var self = this;
|
||||||
|
return new Promise(function(resolve) {
|
||||||
|
if (!options.userId) {
|
||||||
|
throw new Error('Incomplete arguments!');
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
|
||||||
if (!options.userId) {
|
}).then(function() {
|
||||||
callback(new Error('Incomplete arguments!'));
|
var uri = '/auth/user/' + options.userId;
|
||||||
return;
|
return self._restDao.post(undefined, uri);
|
||||||
}
|
});
|
||||||
|
|
||||||
uri = '/auth/user/' + options.userId;
|
|
||||||
this._restDao.post(undefined, uri, callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -79,18 +82,19 @@ PrivateKey.prototype.requestAuthSessionKey = function(options, callback) {
|
||||||
* @param {String} options.encryptedChallenge The server's base64 encoded challenge encrypted using the authSessionKey
|
* @param {String} options.encryptedChallenge The server's base64 encoded challenge encrypted using the authSessionKey
|
||||||
* @param {String} options.encryptedDeviceSecret The server's base64 encoded deviceSecret encrypted using the authSessionKey
|
* @param {String} options.encryptedDeviceSecret The server's base64 encoded deviceSecret encrypted using the authSessionKey
|
||||||
* @param {String} options.iv The iv used for encryption
|
* @param {String} options.iv The iv used for encryption
|
||||||
* @param {Function} callback(error)
|
|
||||||
*/
|
*/
|
||||||
PrivateKey.prototype.verifyAuthentication = function(options, callback) {
|
PrivateKey.prototype.verifyAuthentication = function(options) {
|
||||||
var uri;
|
var self = this;
|
||||||
|
return new Promise(function(resolve) {
|
||||||
|
if (!options.userId || !options.sessionId || !options.encryptedChallenge || !options.encryptedDeviceSecret || !options.iv) {
|
||||||
|
throw new Error('Incomplete arguments!');
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
|
||||||
if (!options.userId || !options.sessionId || !options.encryptedChallenge || !options.encryptedDeviceSecret || !options.iv) {
|
}).then(function() {
|
||||||
callback(new Error('Incomplete arguments!'));
|
var uri = '/auth/user/' + options.userId + '/session/' + options.sessionId;
|
||||||
return;
|
return self._restDao.put(options, uri);
|
||||||
}
|
});
|
||||||
|
|
||||||
uri = '/auth/user/' + options.userId + '/session/' + options.sessionId;
|
|
||||||
this._restDao.put(options, uri, callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -99,48 +103,50 @@ PrivateKey.prototype.verifyAuthentication = function(options, callback) {
|
||||||
* @param {String} options.userId The user's email address
|
* @param {String} options.userId The user's email address
|
||||||
* @param {String} options.encryptedPrivateKey The base64 encoded encrypted private PGP key
|
* @param {String} options.encryptedPrivateKey The base64 encoded encrypted private PGP key
|
||||||
* @param {String} options.sessionId The session id
|
* @param {String} options.sessionId The session id
|
||||||
* @param {Function} callback(error)
|
|
||||||
*/
|
*/
|
||||||
PrivateKey.prototype.upload = function(options, callback) {
|
PrivateKey.prototype.upload = function(options) {
|
||||||
var uri;
|
var self = this;
|
||||||
|
return new Promise(function(resolve) {
|
||||||
|
if (!options._id || !options.userId || !options.encryptedPrivateKey || !options.sessionId || !options.salt || !options.iv) {
|
||||||
|
throw new Error('Incomplete arguments!');
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
|
||||||
if (!options._id || !options.userId || !options.encryptedPrivateKey || !options.sessionId || !options.salt || !options.iv) {
|
}).then(function() {
|
||||||
callback(new Error('Incomplete arguments!'));
|
var uri = '/privatekey/user/' + options.userId + '/session/' + options.sessionId;
|
||||||
return;
|
return self._restDao.post(options, uri);
|
||||||
}
|
});
|
||||||
|
|
||||||
uri = '/privatekey/user/' + options.userId + '/session/' + options.sessionId;
|
|
||||||
this._restDao.post(options, uri, callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Query if an encrypted private PGP key exists on the server without initializing the recovery procedure.
|
* Query if an encrypted private PGP key exists on the server without initializing the recovery procedure.
|
||||||
* @param {String} options.userId The user's email address
|
* @param {String} options.userId The user's email address
|
||||||
* @param {String} options.keyId The private PGP key id
|
* @param {String} options.keyId The private PGP key id
|
||||||
* @param {Function} callback(error, found)
|
|
||||||
* @return {Boolean} whether the key was found on the server or not.
|
* @return {Boolean} whether the key was found on the server or not.
|
||||||
*/
|
*/
|
||||||
PrivateKey.prototype.hasPrivateKey = function(options, callback) {
|
PrivateKey.prototype.hasPrivateKey = function(options) {
|
||||||
if (!options.userId || !options.keyId) {
|
var self = this;
|
||||||
callback(new Error('Incomplete arguments!'));
|
return new Promise(function(resolve) {
|
||||||
return;
|
if (!options.userId || !options.keyId) {
|
||||||
}
|
throw new Error('Incomplete arguments!');
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
|
||||||
this._restDao.get({
|
}).then(function() {
|
||||||
uri: '/privatekey/user/' + options.userId + '/key/' + options.keyId + '?ignoreRecovery=true',
|
return self._restDao.get({
|
||||||
}, function(err) {
|
uri: '/privatekey/user/' + options.userId + '/key/' + options.keyId + '?ignoreRecovery=true',
|
||||||
|
});
|
||||||
|
|
||||||
|
}).then(function() {
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}).catch(function(err) {
|
||||||
// 404: there is no encrypted private key on the server
|
// 404: there is no encrypted private key on the server
|
||||||
if (err && err.code !== 200) {
|
if (err.code && err.code !== 200) {
|
||||||
callback(null, false);
|
return false;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err) {
|
throw err;
|
||||||
callback(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(null, true);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -148,30 +154,31 @@ PrivateKey.prototype.hasPrivateKey = function(options, callback) {
|
||||||
* Request download for the encrypted private PGP key.
|
* Request download for the encrypted private PGP key.
|
||||||
* @param {String} options.userId The user's email address
|
* @param {String} options.userId The user's email address
|
||||||
* @param {String} options.keyId The private PGP key id
|
* @param {String} options.keyId The private PGP key id
|
||||||
* @param {Function} callback(error, found)
|
|
||||||
* @return {Boolean} whether the key was found on the server or not.
|
* @return {Boolean} whether the key was found on the server or not.
|
||||||
*/
|
*/
|
||||||
PrivateKey.prototype.requestDownload = function(options, callback) {
|
PrivateKey.prototype.requestDownload = function(options) {
|
||||||
if (!options.userId || !options.keyId) {
|
var self = this;
|
||||||
callback(new Error('Incomplete arguments!'));
|
return new Promise(function(resolve) {
|
||||||
return;
|
if (!options.userId || !options.keyId) {
|
||||||
}
|
throw new Error('Incomplete arguments!');
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
|
||||||
this._restDao.get({
|
}).then(function() {
|
||||||
uri: '/privatekey/user/' + options.userId + '/key/' + options.keyId
|
return self._restDao.get({
|
||||||
}, function(err) {
|
uri: '/privatekey/user/' + options.userId + '/key/' + options.keyId
|
||||||
|
});
|
||||||
|
|
||||||
|
}).then(function() {
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}).catch(function(err) {
|
||||||
// 404: there is no encrypted private key on the server
|
// 404: there is no encrypted private key on the server
|
||||||
if (err && err.code !== 200) {
|
if (err.code && err.code !== 200) {
|
||||||
callback(null, false);
|
return false;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err) {
|
throw err;
|
||||||
callback(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(null, true);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -180,19 +187,19 @@ PrivateKey.prototype.requestDownload = function(options, callback) {
|
||||||
* @param {String} options.userId The user's email address
|
* @param {String} options.userId The user's email address
|
||||||
* @param {String} options.keyId The private key id
|
* @param {String} options.keyId The private key id
|
||||||
* @param {String} options.recoveryToken The token proving the user own the email account
|
* @param {String} options.recoveryToken The token proving the user own the email account
|
||||||
* @param {Function} callback(error, encryptedPrivateKey)
|
|
||||||
* @return {Object} {_id:[hex encoded capital 16 char key id], encryptedPrivateKey:[base64 encoded], encryptedUserId: [base64 encoded]}
|
* @return {Object} {_id:[hex encoded capital 16 char key id], encryptedPrivateKey:[base64 encoded], encryptedUserId: [base64 encoded]}
|
||||||
*/
|
*/
|
||||||
PrivateKey.prototype.download = function(options, callback) {
|
PrivateKey.prototype.download = function(options) {
|
||||||
var uri;
|
var self = this;
|
||||||
|
return new Promise(function(resolve) {
|
||||||
|
if (!options.userId || !options.keyId || !options.recoveryToken) {
|
||||||
|
throw new Error('Incomplete arguments!');
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
|
||||||
if (!options.userId || !options.keyId || !options.recoveryToken) {
|
}).then(function() {
|
||||||
callback(new Error('Incomplete arguments!'));
|
return self._restDao.get({
|
||||||
return;
|
uri: '/privatekey/user/' + options.userId + '/key/' + options.keyId + '/recovery/' + options.recoveryToken
|
||||||
}
|
});
|
||||||
|
});
|
||||||
uri = '/privatekey/user/' + options.userId + '/key/' + options.keyId + '/recovery/' + options.recoveryToken;
|
|
||||||
this._restDao.get({
|
|
||||||
uri: uri
|
|
||||||
}, callback);
|
|
||||||
};
|
};
|
|
@ -11,95 +11,75 @@ function PublicKey(publicKeyRestDao) {
|
||||||
/**
|
/**
|
||||||
* Verify the public key behind the given uuid
|
* Verify the public key behind the given uuid
|
||||||
*/
|
*/
|
||||||
PublicKey.prototype.verify = function(uuid, callback) {
|
PublicKey.prototype.verify = function(uuid) {
|
||||||
var uri = '/verify/' + uuid;
|
return this._restDao.get({
|
||||||
|
uri: '/verify/' + uuid,
|
||||||
this._restDao.get({
|
|
||||||
uri: uri,
|
|
||||||
type: 'text'
|
type: 'text'
|
||||||
}, function(err, res, status) {
|
}).catch(function(err) {
|
||||||
if (err && err.code === 400) {
|
if (err.code === 400) {
|
||||||
// there was an attempt to verify a non-existing public key
|
// there was an attempt to verify a non-existing public key
|
||||||
callback();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(err, res, status);
|
throw err;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the user's corresponding public key
|
* Find the user's corresponding public key
|
||||||
*/
|
*/
|
||||||
PublicKey.prototype.get = function(keyId, callback) {
|
PublicKey.prototype.get = function(keyId) {
|
||||||
var uri = '/publickey/key/' + keyId;
|
return this._restDao.get({
|
||||||
|
uri: '/publickey/key/' + keyId
|
||||||
this._restDao.get({
|
}).catch(function(err) {
|
||||||
uri: uri
|
if (err.code === 404) {
|
||||||
}, function(err, key) {
|
|
||||||
if (err && err.code === 404) {
|
|
||||||
callback();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err) {
|
throw err;
|
||||||
callback(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(null, (key && key._id) ? key : undefined);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the user's corresponding public key by email
|
* Find the user's corresponding public key by email
|
||||||
*/
|
*/
|
||||||
PublicKey.prototype.getByUserId = function(userId, callback) {
|
PublicKey.prototype.getByUserId = function(userId) {
|
||||||
var uri = '/publickey/user/' + userId;
|
return this._restDao.get({
|
||||||
|
uri: '/publickey/user/' + userId
|
||||||
this._restDao.get({
|
}).then(function(keys) {
|
||||||
uri: uri
|
|
||||||
}, function(err, keys) {
|
|
||||||
// not found
|
|
||||||
if (err && err.code === 404) {
|
|
||||||
callback();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
callback(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!keys || keys.length < 1) {
|
if (!keys || keys.length < 1) {
|
||||||
// 'No public key for that user!'
|
// 'No public key for that user!'
|
||||||
callback();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keys.length > 1) {
|
if (keys.length > 1) {
|
||||||
callback({
|
throw new Error('That user has multiple public keys!');
|
||||||
errMsg: 'That user has multiple public keys!'
|
}
|
||||||
});
|
|
||||||
|
return keys[0];
|
||||||
|
|
||||||
|
}).catch(function(err) {
|
||||||
|
// not found
|
||||||
|
if (err.code === 404) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(null, keys[0]);
|
throw err;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Persist the user's publc key
|
* Persist the user's publc key
|
||||||
*/
|
*/
|
||||||
PublicKey.prototype.put = function(pubkey, callback) {
|
PublicKey.prototype.put = function(pubkey) {
|
||||||
var uri = '/publickey/user/' + pubkey.userId + '/key/' + pubkey._id;
|
var uri = '/publickey/user/' + pubkey.userId + '/key/' + pubkey._id;
|
||||||
this._restDao.put(pubkey, uri, callback);
|
return this._restDao.put(pubkey, uri);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete the public key from the cloud storage service
|
* Delete the public key from the cloud storage service
|
||||||
*/
|
*/
|
||||||
PublicKey.prototype.remove = function(keyId, callback) {
|
PublicKey.prototype.remove = function(keyId) {
|
||||||
var uri = '/publickey/key/' + keyId;
|
var uri = '/publickey/key/' + keyId;
|
||||||
this._restDao.remove(uri, callback);
|
return this._restDao.remove(uri);
|
||||||
};
|
};
|
|
@ -54,105 +54,106 @@ RestDAO.prototype.setBaseUri = function(baseUri) {
|
||||||
* @param {String} options.uri URI relative to the base uri to perform the GET request with.
|
* @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.
|
* @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(options, callback) {
|
RestDAO.prototype.get = function(options) {
|
||||||
options.method = 'GET';
|
options.method = 'GET';
|
||||||
this._processRequest(options, callback);
|
return this._processRequest(options);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* POST (create) request
|
* POST (create) request
|
||||||
*/
|
*/
|
||||||
RestDAO.prototype.post = function(item, uri, callback) {
|
RestDAO.prototype.post = function(item, uri) {
|
||||||
this._processRequest({
|
return this._processRequest({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
payload: item,
|
payload: item,
|
||||||
uri: uri
|
uri: uri
|
||||||
}, callback);
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PUT (update) request
|
* PUT (update) request
|
||||||
*/
|
*/
|
||||||
RestDAO.prototype.put = function(item, uri, callback) {
|
RestDAO.prototype.put = function(item, uri) {
|
||||||
this._processRequest({
|
return this._processRequest({
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
payload: item,
|
payload: item,
|
||||||
uri: uri
|
uri: uri
|
||||||
}, callback);
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DELETE (remove) request
|
* DELETE (remove) request
|
||||||
*/
|
*/
|
||||||
RestDAO.prototype.remove = function(uri, callback) {
|
RestDAO.prototype.remove = function(uri) {
|
||||||
this._processRequest({
|
return this._processRequest({
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
uri: uri
|
uri: uri
|
||||||
}, callback);
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// helper functions
|
// helper functions
|
||||||
//
|
//
|
||||||
|
|
||||||
RestDAO.prototype._processRequest = function(options, callback) {
|
RestDAO.prototype._processRequest = function(options) {
|
||||||
var xhr, format;
|
var self = this;
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
var xhr, format;
|
||||||
|
|
||||||
if (typeof options.uri === 'undefined') {
|
if (typeof options.uri === 'undefined') {
|
||||||
callback({
|
throw {
|
||||||
code: 400,
|
code: 400,
|
||||||
errMsg: 'Bad Request! URI is a mandatory parameter.'
|
message: 'Bad Request! URI is a mandatory parameter.'
|
||||||
});
|
};
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
options.type = options.type || 'json';
|
|
||||||
|
|
||||||
if (options.type === 'json') {
|
|
||||||
format = 'application/json';
|
|
||||||
} else if (options.type === 'xml') {
|
|
||||||
format = 'application/xml';
|
|
||||||
} else if (options.type === 'text') {
|
|
||||||
format = 'text/plain';
|
|
||||||
} else {
|
|
||||||
callback({
|
|
||||||
code: 400,
|
|
||||||
errMsg: 'Bad Request! Unhandled data type.'
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
xhr = new XMLHttpRequest();
|
|
||||||
xhr.open(options.method, this._baseUri + options.uri);
|
|
||||||
xhr.setRequestHeader('Accept', format);
|
|
||||||
xhr.setRequestHeader('Content-Type', format);
|
|
||||||
|
|
||||||
xhr.onload = function() {
|
|
||||||
var res;
|
|
||||||
|
|
||||||
if (xhr.readyState === 4 && (xhr.status === 200 || xhr.status === 201 || xhr.status === 304)) {
|
|
||||||
if (options.type === 'json') {
|
|
||||||
res = xhr.responseText ? JSON.parse(xhr.responseText) : xhr.responseText;
|
|
||||||
} else {
|
|
||||||
res = xhr.responseText;
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(null, res, xhr.status);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
callback({
|
options.type = options.type || 'json';
|
||||||
code: xhr.status,
|
|
||||||
errMsg: xhr.statusText
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
xhr.onerror = function() {
|
if (options.type === 'json') {
|
||||||
callback({
|
format = 'application/json';
|
||||||
code: 42,
|
} else if (options.type === 'xml') {
|
||||||
errMsg: 'Error calling ' + options.method + ' on ' + options.uri
|
format = 'application/xml';
|
||||||
});
|
} else if (options.type === 'text') {
|
||||||
};
|
format = 'text/plain';
|
||||||
|
} else {
|
||||||
|
throw {
|
||||||
|
code: 400,
|
||||||
|
message: 'Bad Request! Unhandled data type.'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
xhr.send(options.payload ? JSON.stringify(options.payload) : undefined);
|
xhr = new XMLHttpRequest();
|
||||||
|
xhr.open(options.method, self._baseUri + options.uri);
|
||||||
|
xhr.setRequestHeader('Accept', format);
|
||||||
|
xhr.setRequestHeader('Content-Type', format);
|
||||||
|
|
||||||
|
xhr.onload = function() {
|
||||||
|
var res;
|
||||||
|
|
||||||
|
if (xhr.readyState === 4 && (xhr.status === 200 || xhr.status === 201 || xhr.status === 304)) {
|
||||||
|
if (options.type === 'json') {
|
||||||
|
res = xhr.responseText ? JSON.parse(xhr.responseText) : xhr.responseText;
|
||||||
|
} else {
|
||||||
|
res = xhr.responseText;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(res);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
reject({
|
||||||
|
code: xhr.status,
|
||||||
|
message: xhr.statusText
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
xhr.onerror = function() {
|
||||||
|
reject({
|
||||||
|
code: 42,
|
||||||
|
message: 'Error calling ' + options.method + ' on ' + options.uri
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
xhr.send(options.payload ? JSON.stringify(options.payload) : undefined);
|
||||||
|
});
|
||||||
};
|
};
|
|
@ -82,40 +82,25 @@ ConnectionDoctor.prototype.configure = function(credentials) {
|
||||||
* 3) Login to the server
|
* 3) Login to the server
|
||||||
* 4) Perform some basic commands (e.g. list folders)
|
* 4) Perform some basic commands (e.g. list folders)
|
||||||
* 5) Exposes error codes
|
* 5) Exposes error codes
|
||||||
*
|
|
||||||
* @param {Function} callback(error) Invoked when the test suite passed, or with an error object if something went wrong
|
|
||||||
*/
|
*/
|
||||||
ConnectionDoctor.prototype.check = function(callback) {
|
ConnectionDoctor.prototype.check = function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
return new Promise(function(resolve) {
|
||||||
if (!self.credentials) {
|
if (!self.credentials) {
|
||||||
return callback(new Error('You need to configure() the connection doctor first!'));
|
throw new Error('You need to configure() the connection doctor first!');
|
||||||
}
|
} else {
|
||||||
|
resolve();
|
||||||
self._checkOnline(function(error) {
|
|
||||||
if (error) {
|
|
||||||
return callback(error);
|
|
||||||
}
|
}
|
||||||
|
}).then(function() {
|
||||||
self._checkReachable(self.credentials.imap, function(error) {
|
return self._checkOnline();
|
||||||
if (error) {
|
}).then(function() {
|
||||||
return callback(error);
|
return self._checkReachable(self.credentials.imap);
|
||||||
}
|
}).then(function() {
|
||||||
|
return self._checkReachable(self.credentials.smtp);
|
||||||
self._checkReachable(self.credentials.smtp, function(error) {
|
}).then(function() {
|
||||||
if (error) {
|
return self._checkImap();
|
||||||
return callback(error);
|
}).then(function() {
|
||||||
}
|
return self._checkSmtp();
|
||||||
|
|
||||||
self._checkImap(function(error) {
|
|
||||||
if (error) {
|
|
||||||
return callback(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
self._checkSmtp(callback);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -126,15 +111,16 @@ ConnectionDoctor.prototype.check = function(callback) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the browser is online
|
* Checks if the browser is online
|
||||||
*
|
|
||||||
* @param {Function} callback(error) Invoked when the test suite passed, or with an error object if browser is offline
|
|
||||||
*/
|
*/
|
||||||
ConnectionDoctor.prototype._checkOnline = function(callback) {
|
ConnectionDoctor.prototype._checkOnline = function() {
|
||||||
if (navigator.onLine) {
|
var self = this;
|
||||||
callback();
|
return new Promise(function(resolve) {
|
||||||
} else {
|
if (navigator.onLine) {
|
||||||
callback(createError(OFFLINE, this._appConfig.string.connDocOffline));
|
resolve();
|
||||||
}
|
} else {
|
||||||
|
throw createError(OFFLINE, self._appConfig.string.connDocOffline);
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -143,105 +129,113 @@ ConnectionDoctor.prototype._checkOnline = function(callback) {
|
||||||
* @param {String} options.host
|
* @param {String} options.host
|
||||||
* @param {Number} options.port
|
* @param {Number} options.port
|
||||||
* @param {Boolean} options.secure
|
* @param {Boolean} options.secure
|
||||||
* @param {Function} callback(error) Invoked when the test suite passed, or with an error object if something went wrong
|
|
||||||
*/
|
*/
|
||||||
ConnectionDoctor.prototype._checkReachable = function(options, callback) {
|
ConnectionDoctor.prototype._checkReachable = function(options) {
|
||||||
var socket,
|
var self = this;
|
||||||
error, // remember the error message
|
return new Promise(function(resolve, reject) {
|
||||||
timeout, // remember the timeout object
|
var socket,
|
||||||
host = options.host + ':' + options.port,
|
error, // remember the error message
|
||||||
hasTimedOut = false, // prevents multiple callbacks
|
timeout, // remember the timeout object
|
||||||
cfg = this._appConfig.config,
|
host = options.host + ':' + options.port,
|
||||||
str = this._appConfig.string;
|
hasTimedOut = false, // prevents multiple callbacks
|
||||||
|
cfg = self._appConfig.config,
|
||||||
|
str = self._appConfig.string;
|
||||||
|
|
||||||
timeout = setTimeout(function() {
|
timeout = setTimeout(function() {
|
||||||
hasTimedOut = true;
|
hasTimedOut = true;
|
||||||
callback(createError(HOST_TIMEOUT, str.connDocHostTimeout.replace('{0}', host).replace('{1}', cfg.connDocTimeout)));
|
reject(createError(HOST_TIMEOUT, str.connDocHostTimeout.replace('{0}', host).replace('{1}', cfg.connDocTimeout)));
|
||||||
}, cfg.connDocTimeout);
|
}, cfg.connDocTimeout);
|
||||||
|
|
||||||
socket = TCPSocket.open(options.host, options.port, {
|
socket = TCPSocket.open(options.host, options.port, {
|
||||||
binaryType: 'arraybuffer',
|
binaryType: 'arraybuffer',
|
||||||
useSecureTransport: options.secure,
|
useSecureTransport: options.secure,
|
||||||
ca: options.ca,
|
ca: options.ca,
|
||||||
tlsWorkerPath: this._workerPath
|
tlsWorkerPath: self._workerPath
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.ondata = function() {}; // we don't actually care about the data
|
socket.ondata = function() {}; // we don't actually care about the data
|
||||||
|
|
||||||
// [WO-625] Mozilla forbids extensions to the TCPSocket object,
|
// [WO-625] Mozilla forbids extensions to the TCPSocket object,
|
||||||
// throws an exception when assigned unexpected callback functions.
|
// throws an exception when assigned unexpected callback functions.
|
||||||
// The exception can be safely ignored since we need the callback
|
// The exception can be safely ignored since we need the callback
|
||||||
// for the other shims
|
// for the other shims
|
||||||
try {
|
try {
|
||||||
socket.oncert = function() {
|
socket.oncert = function() {
|
||||||
if (options.ca) {
|
if (options.ca) {
|
||||||
// the certificate we already have is outdated
|
// the certificate we already have is outdated
|
||||||
error = createError(TLS_WRONG_CERT, str.connDocTlsWrongCert.replace('{0}', host));
|
error = createError(TLS_WRONG_CERT, str.connDocTlsWrongCert.replace('{0}', host));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
socket.onerror = function(e) {
|
||||||
|
if (!error) {
|
||||||
|
error = createError(HOST_UNREACHABLE, str.connDocHostUnreachable.replace('{0}', host), e.data);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} catch (e) {}
|
|
||||||
|
|
||||||
socket.onerror = function(e) {
|
socket.onopen = function() {
|
||||||
if (!error) {
|
socket.close();
|
||||||
error = createError(HOST_UNREACHABLE, str.connDocHostUnreachable.replace('{0}', host), e.data);
|
};
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
socket.onopen = function() {
|
socket.onclose = function() {
|
||||||
socket.close();
|
if (!hasTimedOut) {
|
||||||
};
|
clearTimeout(timeout);
|
||||||
|
if (error) {
|
||||||
socket.onclose = function() {
|
reject(error);
|
||||||
if (!hasTimedOut) {
|
} else {
|
||||||
clearTimeout(timeout);
|
resolve();
|
||||||
callback(error);
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if an IMAP server is reachable, accepts the credentials, can list folders and has an inbox and logs out.
|
* Checks if an IMAP server is reachable, accepts the credentials, can list folders and has an inbox and logs out.
|
||||||
* Adds the certificate to the IMAP settings if not provided.
|
* Adds the certificate to the IMAP settings if not provided.
|
||||||
*
|
|
||||||
* @param {Function} callback(error) Invoked when the test suite passed, or with an error object if something went wrong
|
|
||||||
*/
|
*/
|
||||||
ConnectionDoctor.prototype._checkImap = function(callback) {
|
ConnectionDoctor.prototype._checkImap = function() {
|
||||||
var self = this,
|
var self = this;
|
||||||
loggedIn = false,
|
return new Promise(function(resolve, reject) {
|
||||||
host = self.credentials.imap.host + ':' + self.credentials.imap.port,
|
var loggedIn = false,
|
||||||
str = this._appConfig.string;
|
host = self.credentials.imap.host + ':' + self.credentials.imap.port,
|
||||||
|
str = self._appConfig.string;
|
||||||
|
|
||||||
self._imap.onCert = function(pemEncodedCert) {
|
self._imap.onCert = function(pemEncodedCert) {
|
||||||
if (!self.credentials.imap.ca) {
|
if (!self.credentials.imap.ca) {
|
||||||
self.credentials.imap.ca = pemEncodedCert;
|
self.credentials.imap.ca = pemEncodedCert;
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// login and logout do not use error objects in the callback, but rather invoke
|
|
||||||
// the global onError handler, so we need to track if login was successful
|
|
||||||
self._imap.onError = function(error) {
|
|
||||||
if (!loggedIn) {
|
|
||||||
callback(createError(AUTH_REJECTED, str.connDocAuthRejected.replace('{0}', host), error));
|
|
||||||
} else {
|
|
||||||
callback(createError(GENERIC_ERROR, str.connDocGenericError.replace('{0}', host).replace('{1}', error.message), error));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
self._imap.login(function() {
|
|
||||||
loggedIn = true;
|
|
||||||
|
|
||||||
self._imap.listWellKnownFolders(function(error, wellKnownFolders) {
|
|
||||||
if (error) {
|
|
||||||
return callback(createError(GENERIC_ERROR, str.connDocGenericError.replace('{0}', host).replace('{1}', error.message), error));
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (wellKnownFolders.Inbox.length === 0) {
|
// login and logout do not use error objects in the callback, but rather invoke
|
||||||
// the client needs at least an inbox folder to work properly
|
// the global onError handler, so we need to track if login was successful
|
||||||
return callback(createError(NO_INBOX, str.connDocNoInbox.replace('{0}', host)));
|
self._imap.onError = function(error) {
|
||||||
|
if (!loggedIn) {
|
||||||
|
reject(createError(AUTH_REJECTED, str.connDocAuthRejected.replace('{0}', host), error));
|
||||||
|
} else {
|
||||||
|
reject(createError(GENERIC_ERROR, str.connDocGenericError.replace('{0}', host).replace('{1}', error.message), error));
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
self._imap.logout(function() {
|
self._imap.login(function() {
|
||||||
callback();
|
loggedIn = true;
|
||||||
|
|
||||||
|
self._imap.listWellKnownFolders(function(error, wellKnownFolders) {
|
||||||
|
if (error) {
|
||||||
|
reject(createError(GENERIC_ERROR, str.connDocGenericError.replace('{0}', host).replace('{1}', error.message), error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wellKnownFolders.Inbox.length === 0) {
|
||||||
|
// the client needs at least an inbox folder to work properly
|
||||||
|
reject(createError(NO_INBOX, str.connDocNoInbox.replace('{0}', host)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self._imap.logout(function() {
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -250,39 +244,39 @@ ConnectionDoctor.prototype._checkImap = function(callback) {
|
||||||
/**
|
/**
|
||||||
* Checks if an SMTP server is reachable and accepts the credentials and logs out.
|
* Checks if an SMTP server is reachable and accepts the credentials and logs out.
|
||||||
* Adds the certificate to the SMTP settings if not provided.
|
* Adds the certificate to the SMTP settings if not provided.
|
||||||
*
|
|
||||||
* @param {Function} callback(error) Invoked when the test suite passed, or with an error object if something went wrong
|
|
||||||
*/
|
*/
|
||||||
ConnectionDoctor.prototype._checkSmtp = function(callback) {
|
ConnectionDoctor.prototype._checkSmtp = function() {
|
||||||
var self = this,
|
var self = this;
|
||||||
host = self.credentials.smtp.host + ':' + self.credentials.smtp.port,
|
return new Promise(function(resolve, reject) {
|
||||||
errored = false, // tracks if we need to invoke the callback at onclose or not
|
var host = self.credentials.smtp.host + ':' + self.credentials.smtp.port,
|
||||||
str = this._appConfig.string;
|
errored = false, // tracks if we need to invoke the callback at onclose or not
|
||||||
|
str = self._appConfig.string;
|
||||||
|
|
||||||
self._smtp.oncert = function(pemEncodedCert) {
|
self._smtp.oncert = function(pemEncodedCert) {
|
||||||
if (!self.credentials.smtp.ca) {
|
if (!self.credentials.smtp.ca) {
|
||||||
self.credentials.smtp.ca = pemEncodedCert;
|
self.credentials.smtp.ca = pemEncodedCert;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
self._smtp.onerror = function(error) {
|
self._smtp.onerror = function(error) {
|
||||||
if (error) {
|
if (error) {
|
||||||
errored = true;
|
errored = true;
|
||||||
callback(createError(AUTH_REJECTED, str.connDocAuthRejected.replace('{0}', host), error));
|
reject(createError(AUTH_REJECTED, str.connDocAuthRejected.replace('{0}', host), error));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
self._smtp.onidle = function() {
|
self._smtp.onidle = function() {
|
||||||
self._smtp.quit();
|
self._smtp.quit();
|
||||||
};
|
};
|
||||||
|
|
||||||
self._smtp.onclose = function() {
|
self._smtp.onclose = function() {
|
||||||
if (!errored) {
|
if (!errored) {
|
||||||
callback();
|
resolve();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
self._smtp.connect();
|
self._smtp.connect();
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -58,5 +58,7 @@ Notif.prototype.create = function(options) {
|
||||||
};
|
};
|
||||||
|
|
||||||
Notif.prototype.close = function(notification) {
|
Notif.prototype.close = function(notification) {
|
||||||
notification.close();
|
if (notification) {
|
||||||
|
notification.close();
|
||||||
|
}
|
||||||
};
|
};
|
|
@ -25,76 +25,67 @@ function UpdateHandler(appConfigStore, accountStore, auth, dialog) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes all the necessary updates
|
* Executes all the necessary updates
|
||||||
* @param {Function} callback(error) Invoked when all the database updates were executed, or if an error occurred
|
|
||||||
*/
|
*/
|
||||||
UpdateHandler.prototype.update = function(callback) {
|
UpdateHandler.prototype.update = function() {
|
||||||
var self = this,
|
var self = this,
|
||||||
currentVersion = 0,
|
currentVersion = 0,
|
||||||
targetVersion = cfg.dbVersion,
|
targetVersion = cfg.dbVersion,
|
||||||
versionDbType = 'dbVersion';
|
versionDbType = 'dbVersion';
|
||||||
|
|
||||||
self._appConfigStorage.listItems(versionDbType, 0, null, function(err, items) {
|
return self._appConfigStorage.listItems(versionDbType, 0, null).then(function(items) {
|
||||||
if (err) {
|
|
||||||
callback(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse the database version number
|
// parse the database version number
|
||||||
if (items && items.length > 0) {
|
if (items && items.length > 0) {
|
||||||
currentVersion = parseInt(items[0], 10);
|
currentVersion = parseInt(items[0], 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
self._applyUpdate({
|
return self._applyUpdate({
|
||||||
currentVersion: currentVersion,
|
currentVersion: currentVersion,
|
||||||
targetVersion: targetVersion
|
targetVersion: targetVersion
|
||||||
}, callback);
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Schedules necessary updates and executes thom in order
|
* Schedules necessary updates and executes thom in order
|
||||||
*/
|
*/
|
||||||
UpdateHandler.prototype._applyUpdate = function(options, callback) {
|
UpdateHandler.prototype._applyUpdate = function(options) {
|
||||||
var self = this,
|
var self = this;
|
||||||
scriptOptions,
|
return new Promise(function(resolve, reject) {
|
||||||
queue = [];
|
var scriptOptions,
|
||||||
|
queue = [];
|
||||||
|
|
||||||
if (options.currentVersion >= options.targetVersion) {
|
if (options.currentVersion >= options.targetVersion) {
|
||||||
// the current database version is up to date
|
// the current database version is up to date
|
||||||
callback();
|
resolve();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
scriptOptions = {
|
|
||||||
appConfigStorage: self._appConfigStorage,
|
|
||||||
userStorage: self._userStorage,
|
|
||||||
auth: self._auth
|
|
||||||
};
|
|
||||||
|
|
||||||
// add all the necessary database updates to the queue
|
|
||||||
for (var i = options.currentVersion; i < options.targetVersion; i++) {
|
|
||||||
queue.push(self._updateScripts[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// takes the next update from the queue and executes it
|
|
||||||
function executeNextUpdate(err) {
|
|
||||||
if (err) {
|
|
||||||
callback(err);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (queue.length < 1) {
|
scriptOptions = {
|
||||||
// we're done
|
appConfigStorage: self._appConfigStorage,
|
||||||
callback();
|
userStorage: self._userStorage,
|
||||||
return;
|
auth: self._auth
|
||||||
|
};
|
||||||
|
|
||||||
|
// add all the necessary database updates to the queue
|
||||||
|
for (var i = options.currentVersion; i < options.targetVersion; i++) {
|
||||||
|
queue.push(self._updateScripts[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// process next update
|
// takes the next update from the queue and executes it
|
||||||
var script = queue.shift();
|
function executeNextUpdate() {
|
||||||
script(scriptOptions, executeNextUpdate);
|
if (queue.length < 1) {
|
||||||
}
|
// we're done
|
||||||
|
resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
executeNextUpdate();
|
// process next update
|
||||||
|
var script = queue.shift();
|
||||||
|
script(scriptOptions).then(executeNextUpdate).catch(reject);
|
||||||
|
}
|
||||||
|
|
||||||
|
executeNextUpdate();
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -7,20 +7,15 @@
|
||||||
* every non-prefixed mail in the IMAP folders would be nuked due to the implementation
|
* every non-prefixed mail in the IMAP folders would be nuked due to the implementation
|
||||||
* of the delta sync.
|
* of the delta sync.
|
||||||
*/
|
*/
|
||||||
function updateV1(options, callback) {
|
function updateV1(options) {
|
||||||
var emailDbType = 'email_',
|
var emailDbType = 'email_',
|
||||||
versionDbType = 'dbVersion',
|
versionDbType = 'dbVersion',
|
||||||
postUpdateDbVersion = 1;
|
postUpdateDbVersion = 1;
|
||||||
|
|
||||||
// remove the emails
|
// remove the emails
|
||||||
options.userStorage.removeList(emailDbType, function(err) {
|
return options.userStorage.removeList(emailDbType).then(function() {
|
||||||
if (err) {
|
|
||||||
callback(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update the database version to postUpdateDbVersion
|
// update the database version to postUpdateDbVersion
|
||||||
options.appConfigStorage.storeList([postUpdateDbVersion], versionDbType, callback);
|
return options.appConfigStorage.storeList([postUpdateDbVersion], versionDbType);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,20 +6,15 @@
|
||||||
* In database version 2, the stored email objects have to be purged, because the
|
* In database version 2, the stored email objects have to be purged, because the
|
||||||
* new data model stores information about the email structure in the property 'bodyParts'.
|
* new data model stores information about the email structure in the property 'bodyParts'.
|
||||||
*/
|
*/
|
||||||
function updateV2(options, callback) {
|
function updateV2(options) {
|
||||||
var emailDbType = 'email_',
|
var emailDbType = 'email_',
|
||||||
versionDbType = 'dbVersion',
|
versionDbType = 'dbVersion',
|
||||||
postUpdateDbVersion = 2;
|
postUpdateDbVersion = 2;
|
||||||
|
|
||||||
// remove the emails
|
// remove the emails
|
||||||
options.userStorage.removeList(emailDbType, function(err) {
|
return options.userStorage.removeList(emailDbType).then(function() {
|
||||||
if (err) {
|
|
||||||
callback(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update the database version to postUpdateDbVersion
|
// update the database version to postUpdateDbVersion
|
||||||
options.appConfigStorage.storeList([postUpdateDbVersion], versionDbType, callback);
|
return options.appConfigStorage.storeList([postUpdateDbVersion], versionDbType);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,20 +6,15 @@
|
||||||
* In database version 3, we introduced new flags to the messages, also
|
* In database version 3, we introduced new flags to the messages, also
|
||||||
* the outbox uses artificial uids
|
* the outbox uses artificial uids
|
||||||
*/
|
*/
|
||||||
function update(options, callback) {
|
function update(options) {
|
||||||
var emailDbType = 'email_',
|
var emailDbType = 'email_',
|
||||||
versionDbType = 'dbVersion',
|
versionDbType = 'dbVersion',
|
||||||
postUpdateDbVersion = 3;
|
postUpdateDbVersion = 3;
|
||||||
|
|
||||||
// remove the emails
|
// remove the emails
|
||||||
options.userStorage.removeList(emailDbType, function(err) {
|
return options.userStorage.removeList(emailDbType).then(function() {
|
||||||
if (err) {
|
|
||||||
callback(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update the database version to postUpdateDbVersion
|
// update the database version to postUpdateDbVersion
|
||||||
options.appConfigStorage.storeList([postUpdateDbVersion], versionDbType, callback);
|
return options.appConfigStorage.storeList([postUpdateDbVersion], versionDbType);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* indexeddb. only gmail was allowed as a mail service provider before,
|
* indexeddb. only gmail was allowed as a mail service provider before,
|
||||||
* so let's add this...
|
* so let's add this...
|
||||||
*/
|
*/
|
||||||
function update(options, callback) {
|
function update(options) {
|
||||||
var VERSION_DB_TYPE = 'dbVersion',
|
var VERSION_DB_TYPE = 'dbVersion',
|
||||||
EMAIL_ADDR_DB_KEY = 'emailaddress',
|
EMAIL_ADDR_DB_KEY = 'emailaddress',
|
||||||
USERNAME_DB_KEY = 'username',
|
USERNAME_DB_KEY = 'username',
|
||||||
|
@ -29,77 +29,45 @@ function update(options, callback) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// load the email address (if existing)
|
// load the email address (if existing)
|
||||||
loadFromDB(EMAIL_ADDR_DB_KEY, function(err, emailAddress) {
|
var emailAddress;
|
||||||
if (err) {
|
return loadFromDB(EMAIL_ADDR_DB_KEY).then(function(address) {
|
||||||
return callback(err);
|
emailAddress = address;
|
||||||
|
// load the provider (if existing)
|
||||||
|
return loadFromDB(PROVIDER_DB_KEY);
|
||||||
|
|
||||||
|
}).then(function(provider) {
|
||||||
|
// if there is an email address without a provider, we need to add the missing provider entry
|
||||||
|
// for any other situation, we're good.
|
||||||
|
if (!(emailAddress && !provider)) {
|
||||||
|
// update the database version to POST_UPDATE_DB_VERSION
|
||||||
|
return options.appConfigStorage.storeList([POST_UPDATE_DB_VERSION], VERSION_DB_TYPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// load the provider (if existing)
|
// add the missing provider key
|
||||||
loadFromDB(PROVIDER_DB_KEY, function(err, provider) {
|
var storeProvider = options.appConfigStorage.storeList(['gmail'], PROVIDER_DB_KEY);
|
||||||
if (err) {
|
// add the missing user name key
|
||||||
return callback(err);
|
var storeAdress = options.appConfigStorage.storeList([emailAddress], USERNAME_DB_KEY);
|
||||||
}
|
// add the missing imap host info key
|
||||||
|
var storeImap = options.appConfigStorage.storeList([imap], IMAP_DB_KEY);
|
||||||
|
// add the missing empty real name
|
||||||
|
var storeEmptyName = options.appConfigStorage.storeList([''], REALNAME_DB_KEY);
|
||||||
|
// add the missing smtp host info key
|
||||||
|
var storeSmtp = options.appConfigStorage.storeList([smtp], SMTP_DB_KEY);
|
||||||
|
|
||||||
// if there is an email address without a provider, we need to add the missing provider entry
|
return Promise.all([storeProvider, storeAdress, storeImap, storeEmptyName, storeSmtp]).then(function() {
|
||||||
// for any other situation, we're good.
|
// reload the credentials
|
||||||
|
options.auth.initialized = false;
|
||||||
|
return options.auth._loadCredentials();
|
||||||
|
|
||||||
if (!(emailAddress && !provider)) {
|
}).then(function() {
|
||||||
// update the database version to POST_UPDATE_DB_VERSION
|
// update the database version to POST_UPDATE_DB_VERSION
|
||||||
return options.appConfigStorage.storeList([POST_UPDATE_DB_VERSION], VERSION_DB_TYPE, callback);
|
return options.appConfigStorage.storeList([POST_UPDATE_DB_VERSION], VERSION_DB_TYPE);
|
||||||
}
|
|
||||||
|
|
||||||
// add the missing provider key
|
|
||||||
options.appConfigStorage.storeList(['gmail'], PROVIDER_DB_KEY, function(err) {
|
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
// add the missing user name key
|
|
||||||
options.appConfigStorage.storeList([emailAddress], USERNAME_DB_KEY, function(err) {
|
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
// add the missing imap host info key
|
|
||||||
options.appConfigStorage.storeList([imap], IMAP_DB_KEY, function(err) {
|
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
// add the missing empty real name
|
|
||||||
options.appConfigStorage.storeList([''], REALNAME_DB_KEY, function(err) {
|
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
// add the missing smtp host info key
|
|
||||||
options.appConfigStorage.storeList([smtp], SMTP_DB_KEY, function(err) {
|
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
// reload the credentials
|
|
||||||
options.auth.initialized = false;
|
|
||||||
options.auth._loadCredentials(function(err) {
|
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// update the database version to POST_UPDATE_DB_VERSION
|
|
||||||
options.appConfigStorage.storeList([POST_UPDATE_DB_VERSION], VERSION_DB_TYPE, callback);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function loadFromDB(key, callback) {
|
function loadFromDB(key) {
|
||||||
options.appConfigStorage.listItems(key, 0, null, function(err, cachedItems) {
|
return options.appConfigStorage.listItems(key, 0, null).then(function(cachedItems) {
|
||||||
callback(err, (!err && cachedItems && cachedItems[0]));
|
return cachedItems && cachedItems[0];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,14 +16,9 @@ var POST_UPDATE_DB_VERSION = 5;
|
||||||
* Due to an overlooked issue, there may be multiple folders, e.g. for sent mails.
|
* Due to an overlooked issue, there may be multiple folders, e.g. for sent mails.
|
||||||
* This removes the "duplicate" folders.
|
* This removes the "duplicate" folders.
|
||||||
*/
|
*/
|
||||||
function update(options, callback) {
|
function update(options) {
|
||||||
|
|
||||||
// remove the emails
|
// remove the emails
|
||||||
options.userStorage.listItems(FOLDER_DB_TYPE, 0, null, function(err, stored) {
|
return options.userStorage.listItems(FOLDER_DB_TYPE, 0, null).then(function(stored) {
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
var folders = stored[0] || [];
|
var folders = stored[0] || [];
|
||||||
[FOLDER_TYPE_INBOX, FOLDER_TYPE_SENT, FOLDER_TYPE_DRAFTS, FOLDER_TYPE_TRASH].forEach(function(mbxType) {
|
[FOLDER_TYPE_INBOX, FOLDER_TYPE_SENT, FOLDER_TYPE_DRAFTS, FOLDER_TYPE_TRASH].forEach(function(mbxType) {
|
||||||
var foldersForType = folders.filter(function(mbx) {
|
var foldersForType = folders.filter(function(mbx) {
|
||||||
|
@ -39,15 +34,11 @@ function update(options, callback) {
|
||||||
folders.splice(folders.indexOf(foldersForType[i]), 1);
|
folders.splice(folders.indexOf(foldersForType[i]), 1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
return options.userStorage.storeList([folders], FOLDER_DB_TYPE);
|
||||||
|
|
||||||
options.userStorage.storeList([folders], FOLDER_DB_TYPE, function(err) {
|
}).then(function() {
|
||||||
if (err) {
|
// update the database version to POST_UPDATE_DB_VERSION
|
||||||
return callback(err);
|
return options.appConfigStorage.storeList([POST_UPDATE_DB_VERSION], VERSION_DB_TYPE);
|
||||||
}
|
|
||||||
|
|
||||||
// update the database version to POST_UPDATE_DB_VERSION
|
|
||||||
options.appConfigStorage.storeList([POST_UPDATE_DB_VERSION], VERSION_DB_TYPE, callback);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* @license AngularJS v1.3.2
|
* @license AngularJS v1.3.7
|
||||||
* (c) 2010-2014 Google, Inc. http://angularjs.org
|
* (c) 2010-2014 Google, Inc. http://angularjs.org
|
||||||
* License: MIT
|
* License: MIT
|
||||||
*/
|
*/
|
||||||
|
@ -19,7 +19,7 @@
|
||||||
* # Usage
|
* # Usage
|
||||||
*
|
*
|
||||||
* To see animations in action, all that is required is to define the appropriate CSS classes
|
* To see animations in action, all that is required is to define the appropriate CSS classes
|
||||||
* or to register a JavaScript animation via the myModule.animation() function. The directives that support animation automatically are:
|
* or to register a JavaScript animation via the `myModule.animation()` function. The directives that support animation automatically are:
|
||||||
* `ngRepeat`, `ngInclude`, `ngIf`, `ngSwitch`, `ngShow`, `ngHide`, `ngView` and `ngClass`. Custom directives can take advantage of animation
|
* `ngRepeat`, `ngInclude`, `ngIf`, `ngSwitch`, `ngShow`, `ngHide`, `ngView` and `ngClass`. Custom directives can take advantage of animation
|
||||||
* by using the `$animate` service.
|
* by using the `$animate` service.
|
||||||
*
|
*
|
||||||
|
@ -161,8 +161,8 @@
|
||||||
* ### Structural transition animations
|
* ### Structural transition animations
|
||||||
*
|
*
|
||||||
* Structural transitions (such as enter, leave and move) will always apply a `0s none` transition
|
* Structural transitions (such as enter, leave and move) will always apply a `0s none` transition
|
||||||
* value to force the browser into rendering the styles defined in the setup (.ng-enter, .ng-leave
|
* value to force the browser into rendering the styles defined in the setup (`.ng-enter`, `.ng-leave`
|
||||||
* or .ng-move) class. This means that any active transition animations operating on the element
|
* or `.ng-move`) class. This means that any active transition animations operating on the element
|
||||||
* will be cut off to make way for the enter, leave or move animation.
|
* will be cut off to make way for the enter, leave or move animation.
|
||||||
*
|
*
|
||||||
* ### Class-based transition animations
|
* ### Class-based transition animations
|
||||||
|
@ -245,7 +245,7 @@
|
||||||
* You then configure `$animate` to enforce this prefix:
|
* You then configure `$animate` to enforce this prefix:
|
||||||
*
|
*
|
||||||
* ```js
|
* ```js
|
||||||
* $animateProvider.classNamePrefix(/animate-/);
|
* $animateProvider.classNameFilter(/animate-/);
|
||||||
* ```
|
* ```
|
||||||
* </div>
|
* </div>
|
||||||
*
|
*
|
||||||
|
@ -479,11 +479,12 @@ angular.module('ngAnimate', ['ng'])
|
||||||
function isMatchingElement(elm1, elm2) {
|
function isMatchingElement(elm1, elm2) {
|
||||||
return extractElementNode(elm1) == extractElementNode(elm2);
|
return extractElementNode(elm1) == extractElementNode(elm2);
|
||||||
}
|
}
|
||||||
|
var $$jqLite;
|
||||||
$provide.decorator('$animate',
|
$provide.decorator('$animate',
|
||||||
['$delegate', '$$q', '$injector', '$sniffer', '$rootElement', '$$asyncCallback', '$rootScope', '$document', '$templateRequest',
|
['$delegate', '$$q', '$injector', '$sniffer', '$rootElement', '$$asyncCallback', '$rootScope', '$document', '$templateRequest', '$$jqLite',
|
||||||
function($delegate, $$q, $injector, $sniffer, $rootElement, $$asyncCallback, $rootScope, $document, $templateRequest) {
|
function($delegate, $$q, $injector, $sniffer, $rootElement, $$asyncCallback, $rootScope, $document, $templateRequest, $$$jqLite) {
|
||||||
|
|
||||||
|
$$jqLite = $$$jqLite;
|
||||||
$rootElement.data(NG_ANIMATE_STATE, rootAnimateState);
|
$rootElement.data(NG_ANIMATE_STATE, rootAnimateState);
|
||||||
|
|
||||||
// Wait until all directive and route-related templates are downloaded and
|
// Wait until all directive and route-related templates are downloaded and
|
||||||
|
@ -877,22 +878,22 @@ angular.module('ngAnimate', ['ng'])
|
||||||
*
|
*
|
||||||
* Below is a breakdown of each step that occurs during the `animate` animation:
|
* Below is a breakdown of each step that occurs during the `animate` animation:
|
||||||
*
|
*
|
||||||
* | Animation Step | What the element class attribute looks like |
|
* | Animation Step | What the element class attribute looks like |
|
||||||
* |-------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------|
|
* |-----------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------|
|
||||||
* | 1. $animate.animate(...) is called | class="my-animation" |
|
* | 1. `$animate.animate(...)` is called | `class="my-animation"` |
|
||||||
* | 2. $animate waits for the next digest to start the animation | class="my-animation ng-animate" |
|
* | 2. `$animate` waits for the next digest to start the animation | `class="my-animation ng-animate"` |
|
||||||
* | 3. $animate runs the JavaScript-defined animations detected on the element | class="my-animation ng-animate" |
|
* | 3. `$animate` runs the JavaScript-defined animations detected on the element | `class="my-animation ng-animate"` |
|
||||||
* | 4. the className class value is added to the element | class="my-animation ng-animate className" |
|
* | 4. the `className` class value is added to the element | `class="my-animation ng-animate className"` |
|
||||||
* | 5. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate className" |
|
* | 5. `$animate` scans the element styles to get the CSS transition/animation duration and delay | `class="my-animation ng-animate className"` |
|
||||||
* | 6. $animate blocks all CSS transitions on the element to ensure the .className class styling is applied right away| class="my-animation ng-animate className" |
|
* | 6. `$animate` blocks all CSS transitions on the element to ensure the `.className` class styling is applied right away| `class="my-animation ng-animate className"` |
|
||||||
* | 7. $animate applies the provided collection of `from` CSS styles to the element | class="my-animation ng-animate className" |
|
* | 7. `$animate` applies the provided collection of `from` CSS styles to the element | `class="my-animation ng-animate className"` |
|
||||||
* | 8. $animate waits for a single animation frame (this performs a reflow) | class="my-animation ng-animate className" |
|
* | 8. `$animate` waits for a single animation frame (this performs a reflow) | `class="my-animation ng-animate className"` |
|
||||||
* | 9. $animate removes the CSS transition block placed on the element | class="my-animation ng-animate className" |
|
* | 9. `$animate` removes the CSS transition block placed on the element | `class="my-animation ng-animate className"` |
|
||||||
* | 10. the className-active class is added (this triggers the CSS transition/animation) | class="my-animation ng-animate className className-active" |
|
* | 10. the `className-active` class is added (this triggers the CSS transition/animation) | `class="my-animation ng-animate className className-active"` |
|
||||||
* | 11. $animate applies the collection of `to` CSS styles to the element which are then handled by the transition | class="my-animation ng-animate className className-active" |
|
* | 11. `$animate` applies the collection of `to` CSS styles to the element which are then handled by the transition | `class="my-animation ng-animate className className-active"` |
|
||||||
* | 12. $animate waits for the animation to complete (via events and timeout) | class="my-animation ng-animate className className-active" |
|
* | 12. `$animate` waits for the animation to complete (via events and timeout) | `class="my-animation ng-animate className className-active"` |
|
||||||
* | 13. The animation ends and all generated CSS classes are removed from the element | class="my-animation" |
|
* | 13. The animation ends and all generated CSS classes are removed from the element | `class="my-animation"` |
|
||||||
* | 14. The returned promise is resolved. | class="my-animation" |
|
* | 14. The returned promise is resolved. | `class="my-animation"` |
|
||||||
*
|
*
|
||||||
* @param {DOMElement} element the element that will be the focus of the enter animation
|
* @param {DOMElement} element the element that will be the focus of the enter animation
|
||||||
* @param {object} from a collection of CSS styles that will be applied to the element at the start of the animation
|
* @param {object} from a collection of CSS styles that will be applied to the element at the start of the animation
|
||||||
|
@ -923,21 +924,21 @@ angular.module('ngAnimate', ['ng'])
|
||||||
*
|
*
|
||||||
* Below is a breakdown of each step that occurs during enter animation:
|
* Below is a breakdown of each step that occurs during enter animation:
|
||||||
*
|
*
|
||||||
* | Animation Step | What the element class attribute looks like |
|
* | Animation Step | What the element class attribute looks like |
|
||||||
* |-------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------|
|
* |-----------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------|
|
||||||
* | 1. $animate.enter(...) is called | class="my-animation" |
|
* | 1. `$animate.enter(...)` is called | `class="my-animation"` |
|
||||||
* | 2. element is inserted into the parentElement element or beside the afterElement element | class="my-animation" |
|
* | 2. element is inserted into the `parentElement` element or beside the `afterElement` element | `class="my-animation"` |
|
||||||
* | 3. $animate waits for the next digest to start the animation | class="my-animation ng-animate" |
|
* | 3. `$animate` waits for the next digest to start the animation | `class="my-animation ng-animate"` |
|
||||||
* | 4. $animate runs the JavaScript-defined animations detected on the element | class="my-animation ng-animate" |
|
* | 4. `$animate` runs the JavaScript-defined animations detected on the element | `class="my-animation ng-animate"` |
|
||||||
* | 5. the .ng-enter class is added to the element | class="my-animation ng-animate ng-enter" |
|
* | 5. the `.ng-enter` class is added to the element | `class="my-animation ng-animate ng-enter"` |
|
||||||
* | 6. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate ng-enter" |
|
* | 6. `$animate` scans the element styles to get the CSS transition/animation duration and delay | `class="my-animation ng-animate ng-enter"` |
|
||||||
* | 7. $animate blocks all CSS transitions on the element to ensure the .ng-enter class styling is applied right away | class="my-animation ng-animate ng-enter" |
|
* | 7. `$animate` blocks all CSS transitions on the element to ensure the `.ng-enter` class styling is applied right away | `class="my-animation ng-animate ng-enter"` |
|
||||||
* | 8. $animate waits for a single animation frame (this performs a reflow) | class="my-animation ng-animate ng-enter" |
|
* | 8. `$animate` waits for a single animation frame (this performs a reflow) | `class="my-animation ng-animate ng-enter"` |
|
||||||
* | 9. $animate removes the CSS transition block placed on the element | class="my-animation ng-animate ng-enter" |
|
* | 9. `$animate` removes the CSS transition block placed on the element | `class="my-animation ng-animate ng-enter"` |
|
||||||
* | 10. the .ng-enter-active class is added (this triggers the CSS transition/animation) | class="my-animation ng-animate ng-enter ng-enter-active" |
|
* | 10. the `.ng-enter-active` class is added (this triggers the CSS transition/animation) | `class="my-animation ng-animate ng-enter ng-enter-active"` |
|
||||||
* | 11. $animate waits for the animation to complete (via events and timeout) | class="my-animation ng-animate ng-enter ng-enter-active" |
|
* | 11. `$animate` waits for the animation to complete (via events and timeout) | `class="my-animation ng-animate ng-enter ng-enter-active"` |
|
||||||
* | 12. The animation ends and all generated CSS classes are removed from the element | class="my-animation" |
|
* | 12. The animation ends and all generated CSS classes are removed from the element | `class="my-animation"` |
|
||||||
* | 13. The returned promise is resolved. | class="my-animation" |
|
* | 13. The returned promise is resolved. | `class="my-animation"` |
|
||||||
*
|
*
|
||||||
* @param {DOMElement} element the element that will be the focus of the enter animation
|
* @param {DOMElement} element the element that will be the focus of the enter animation
|
||||||
* @param {DOMElement} parentElement the parent element of the element that will be the focus of the enter animation
|
* @param {DOMElement} parentElement the parent element of the element that will be the focus of the enter animation
|
||||||
|
@ -969,21 +970,21 @@ angular.module('ngAnimate', ['ng'])
|
||||||
*
|
*
|
||||||
* Below is a breakdown of each step that occurs during leave animation:
|
* Below is a breakdown of each step that occurs during leave animation:
|
||||||
*
|
*
|
||||||
* | Animation Step | What the element class attribute looks like |
|
* | Animation Step | What the element class attribute looks like |
|
||||||
* |-------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------|
|
* |-----------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------|
|
||||||
* | 1. $animate.leave(...) is called | class="my-animation" |
|
* | 1. `$animate.leave(...)` is called | `class="my-animation"` |
|
||||||
* | 2. $animate runs the JavaScript-defined animations detected on the element | class="my-animation ng-animate" |
|
* | 2. `$animate` runs the JavaScript-defined animations detected on the element | `class="my-animation ng-animate"` |
|
||||||
* | 3. $animate waits for the next digest to start the animation | class="my-animation ng-animate" |
|
* | 3. `$animate` waits for the next digest to start the animation | `class="my-animation ng-animate"` |
|
||||||
* | 4. the .ng-leave class is added to the element | class="my-animation ng-animate ng-leave" |
|
* | 4. the `.ng-leave` class is added to the element | `class="my-animation ng-animate ng-leave"` |
|
||||||
* | 5. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate ng-leave" |
|
* | 5. `$animate` scans the element styles to get the CSS transition/animation duration and delay | `class="my-animation ng-animate ng-leave"` |
|
||||||
* | 6. $animate blocks all CSS transitions on the element to ensure the .ng-leave class styling is applied right away | class="my-animation ng-animate ng-leave” |
|
* | 6. `$animate` blocks all CSS transitions on the element to ensure the `.ng-leave` class styling is applied right away | `class="my-animation ng-animate ng-leave"` |
|
||||||
* | 7. $animate waits for a single animation frame (this performs a reflow) | class="my-animation ng-animate ng-leave" |
|
* | 7. `$animate` waits for a single animation frame (this performs a reflow) | `class="my-animation ng-animate ng-leave"` |
|
||||||
* | 8. $animate removes the CSS transition block placed on the element | class="my-animation ng-animate ng-leave” |
|
* | 8. `$animate` removes the CSS transition block placed on the element | `class="my-animation ng-animate ng-leave"` |
|
||||||
* | 9. the .ng-leave-active class is added (this triggers the CSS transition/animation) | class="my-animation ng-animate ng-leave ng-leave-active" |
|
* | 9. the `.ng-leave-active` class is added (this triggers the CSS transition/animation) | `class="my-animation ng-animate ng-leave ng-leave-active"` |
|
||||||
* | 10. $animate waits for the animation to complete (via events and timeout) | class="my-animation ng-animate ng-leave ng-leave-active" |
|
* | 10. `$animate` waits for the animation to complete (via events and timeout) | `class="my-animation ng-animate ng-leave ng-leave-active"` |
|
||||||
* | 11. The animation ends and all generated CSS classes are removed from the element | class="my-animation" |
|
* | 11. The animation ends and all generated CSS classes are removed from the element | `class="my-animation"` |
|
||||||
* | 12. The element is removed from the DOM | ... |
|
* | 12. The element is removed from the DOM | ... |
|
||||||
* | 13. The returned promise is resolved. | ... |
|
* | 13. The returned promise is resolved. | ... |
|
||||||
*
|
*
|
||||||
* @param {DOMElement} element the element that will be the focus of the leave animation
|
* @param {DOMElement} element the element that will be the focus of the leave animation
|
||||||
* @param {object=} options an optional collection of styles that will be picked up by the CSS transition/animation
|
* @param {object=} options an optional collection of styles that will be picked up by the CSS transition/animation
|
||||||
|
@ -1014,21 +1015,21 @@ angular.module('ngAnimate', ['ng'])
|
||||||
*
|
*
|
||||||
* Below is a breakdown of each step that occurs during move animation:
|
* Below is a breakdown of each step that occurs during move animation:
|
||||||
*
|
*
|
||||||
* | Animation Step | What the element class attribute looks like |
|
* | Animation Step | What the element class attribute looks like |
|
||||||
* |------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------|
|
* |----------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------|
|
||||||
* | 1. $animate.move(...) is called | class="my-animation" |
|
* | 1. `$animate.move(...)` is called | `class="my-animation"` |
|
||||||
* | 2. element is moved into the parentElement element or beside the afterElement element | class="my-animation" |
|
* | 2. element is moved into the parentElement element or beside the afterElement element | `class="my-animation"` |
|
||||||
* | 3. $animate waits for the next digest to start the animation | class="my-animation ng-animate" |
|
* | 3. `$animate` waits for the next digest to start the animation | `class="my-animation ng-animate"` |
|
||||||
* | 4. $animate runs the JavaScript-defined animations detected on the element | class="my-animation ng-animate" |
|
* | 4. `$animate` runs the JavaScript-defined animations detected on the element | `class="my-animation ng-animate"` |
|
||||||
* | 5. the .ng-move class is added to the element | class="my-animation ng-animate ng-move" |
|
* | 5. the `.ng-move` class is added to the element | `class="my-animation ng-animate ng-move"` |
|
||||||
* | 6. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate ng-move" |
|
* | 6. `$animate` scans the element styles to get the CSS transition/animation duration and delay | `class="my-animation ng-animate ng-move"` |
|
||||||
* | 7. $animate blocks all CSS transitions on the element to ensure the .ng-move class styling is applied right away | class="my-animation ng-animate ng-move” |
|
* | 7. `$animate` blocks all CSS transitions on the element to ensure the `.ng-move` class styling is applied right away | `class="my-animation ng-animate ng-move"` |
|
||||||
* | 8. $animate waits for a single animation frame (this performs a reflow) | class="my-animation ng-animate ng-move" |
|
* | 8. `$animate` waits for a single animation frame (this performs a reflow) | `class="my-animation ng-animate ng-move"` |
|
||||||
* | 9. $animate removes the CSS transition block placed on the element | class="my-animation ng-animate ng-move” |
|
* | 9. `$animate` removes the CSS transition block placed on the element | `class="my-animation ng-animate ng-move"` |
|
||||||
* | 10. the .ng-move-active class is added (this triggers the CSS transition/animation) | class="my-animation ng-animate ng-move ng-move-active" |
|
* | 10. the `.ng-move-active` class is added (this triggers the CSS transition/animation) | `class="my-animation ng-animate ng-move ng-move-active"` |
|
||||||
* | 11. $animate waits for the animation to complete (via events and timeout) | class="my-animation ng-animate ng-move ng-move-active" |
|
* | 11. `$animate` waits for the animation to complete (via events and timeout) | `class="my-animation ng-animate ng-move ng-move-active"` |
|
||||||
* | 12. The animation ends and all generated CSS classes are removed from the element | class="my-animation" |
|
* | 12. The animation ends and all generated CSS classes are removed from the element | `class="my-animation"` |
|
||||||
* | 13. The returned promise is resolved. | class="my-animation" |
|
* | 13. The returned promise is resolved. | `class="my-animation"` |
|
||||||
*
|
*
|
||||||
* @param {DOMElement} element the element that will be the focus of the move animation
|
* @param {DOMElement} element the element that will be the focus of the move animation
|
||||||
* @param {DOMElement} parentElement the parentElement element of the element that will be the focus of the move animation
|
* @param {DOMElement} parentElement the parentElement element of the element that will be the focus of the move animation
|
||||||
|
@ -1062,18 +1063,18 @@ angular.module('ngAnimate', ['ng'])
|
||||||
*
|
*
|
||||||
* Below is a breakdown of each step that occurs during addClass animation:
|
* Below is a breakdown of each step that occurs during addClass animation:
|
||||||
*
|
*
|
||||||
* | Animation Step | What the element class attribute looks like |
|
* | Animation Step | What the element class attribute looks like |
|
||||||
* |----------------------------------------------------------------------------------------------------|------------------------------------------------------------------|
|
* |--------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------|
|
||||||
* | 1. $animate.addClass(element, 'super') is called | class="my-animation" |
|
* | 1. `$animate.addClass(element, 'super')` is called | `class="my-animation"` |
|
||||||
* | 2. $animate runs the JavaScript-defined animations detected on the element | class="my-animation ng-animate" |
|
* | 2. `$animate` runs the JavaScript-defined animations detected on the element | `class="my-animation ng-animate"` |
|
||||||
* | 3. the .super-add class is added to the element | class="my-animation ng-animate super-add" |
|
* | 3. the `.super-add` class is added to the element | `class="my-animation ng-animate super-add"` |
|
||||||
* | 4. $animate waits for a single animation frame (this performs a reflow) | class="my-animation ng-animate super-add" |
|
* | 4. `$animate` waits for a single animation frame (this performs a reflow) | `class="my-animation ng-animate super-add"` |
|
||||||
* | 5. the .super and .super-add-active classes are added (this triggers the CSS transition/animation) | class="my-animation ng-animate super super-add super-add-active" |
|
* | 5. the `.super` and `.super-add-active` classes are added (this triggers the CSS transition/animation) | `class="my-animation ng-animate super super-add super-add-active"` |
|
||||||
* | 6. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate super-add" |
|
* | 6. `$animate` scans the element styles to get the CSS transition/animation duration and delay | `class="my-animation ng-animate super super-add super-add-active"` |
|
||||||
* | 7. $animate waits for the animation to complete (via events and timeout) | class="my-animation super super-add super-add-active" |
|
* | 7. `$animate` waits for the animation to complete (via events and timeout) | `class="my-animation ng-animate super super-add super-add-active"` |
|
||||||
* | 8. The animation ends and all generated CSS classes are removed from the element | class="my-animation super" |
|
* | 8. The animation ends and all generated CSS classes are removed from the element | `class="my-animation super"` |
|
||||||
* | 9. The super class is kept on the element | class="my-animation super" |
|
* | 9. The super class is kept on the element | `class="my-animation super"` |
|
||||||
* | 10. The returned promise is resolved. | class="my-animation super" |
|
* | 10. The returned promise is resolved. | `class="my-animation super"` |
|
||||||
*
|
*
|
||||||
* @param {DOMElement} element the element that will be animated
|
* @param {DOMElement} element the element that will be animated
|
||||||
* @param {string} className the CSS class that will be added to the element and then animated
|
* @param {string} className the CSS class that will be added to the element and then animated
|
||||||
|
@ -1096,17 +1097,17 @@ angular.module('ngAnimate', ['ng'])
|
||||||
*
|
*
|
||||||
* Below is a breakdown of each step that occurs during removeClass animation:
|
* Below is a breakdown of each step that occurs during removeClass animation:
|
||||||
*
|
*
|
||||||
* | Animation Step | What the element class attribute looks like |
|
* | Animation Step | What the element class attribute looks like |
|
||||||
* |------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------|
|
* |----------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------|
|
||||||
* | 1. $animate.removeClass(element, 'super') is called | class="my-animation super" |
|
* | 1. `$animate.removeClass(element, 'super')` is called | `class="my-animation super"` |
|
||||||
* | 2. $animate runs the JavaScript-defined animations detected on the element | class="my-animation super ng-animate" |
|
* | 2. `$animate` runs the JavaScript-defined animations detected on the element | `class="my-animation super ng-animate"` |
|
||||||
* | 3. the .super-remove class is added to the element | class="my-animation super ng-animate super-remove" |
|
* | 3. the `.super-remove` class is added to the element | `class="my-animation super ng-animate super-remove"` |
|
||||||
* | 4. $animate waits for a single animation frame (this performs a reflow) | class="my-animation super ng-animate super-remove" |
|
* | 4. `$animate` waits for a single animation frame (this performs a reflow) | `class="my-animation super ng-animate super-remove"` |
|
||||||
* | 5. the .super-remove-active classes are added and .super is removed (this triggers the CSS transition/animation) | class="my-animation ng-animate super-remove super-remove-active" |
|
* | 5. the `.super-remove-active` classes are added and `.super` is removed (this triggers the CSS transition/animation) | `class="my-animation ng-animate super-remove super-remove-active"` |
|
||||||
* | 6. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation super ng-animate super-remove" |
|
* | 6. `$animate` scans the element styles to get the CSS transition/animation duration and delay | `class="my-animation ng-animate super-remove super-remove-active"` |
|
||||||
* | 7. $animate waits for the animation to complete (via events and timeout) | class="my-animation ng-animate super-remove super-remove-active" |
|
* | 7. `$animate` waits for the animation to complete (via events and timeout) | `class="my-animation ng-animate super-remove super-remove-active"` |
|
||||||
* | 8. The animation ends and all generated CSS classes are removed from the element | class="my-animation" |
|
* | 8. The animation ends and all generated CSS classes are removed from the element | `class="my-animation"` |
|
||||||
* | 9. The returned promise is resolved. | class="my-animation" |
|
* | 9. The returned promise is resolved. | `class="my-animation"` |
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @param {DOMElement} element the element that will be animated
|
* @param {DOMElement} element the element that will be animated
|
||||||
|
@ -1124,19 +1125,19 @@ angular.module('ngAnimate', ['ng'])
|
||||||
* @name $animate#setClass
|
* @name $animate#setClass
|
||||||
*
|
*
|
||||||
* @description Adds and/or removes the given CSS classes to and from the element.
|
* @description Adds and/or removes the given CSS classes to and from the element.
|
||||||
* Once complete, the done() callback will be fired (if provided).
|
* Once complete, the `done()` callback will be fired (if provided).
|
||||||
*
|
*
|
||||||
* | Animation Step | What the element class attribute looks like |
|
* | Animation Step | What the element class attribute looks like |
|
||||||
* |--------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------|
|
* |----------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------|
|
||||||
* | 1. $animate.removeClass(element, ‘on’, ‘off’) is called | class="my-animation super off” |
|
* | 1. `$animate.setClass(element, 'on', 'off')` is called | `class="my-animation off"` |
|
||||||
* | 2. $animate runs the JavaScript-defined animations detected on the element | class="my-animation super ng-animate off” |
|
* | 2. `$animate` runs the JavaScript-defined animations detected on the element | `class="my-animation ng-animate off"` |
|
||||||
* | 3. the .on-add and .off-remove classes are added to the element | class="my-animation ng-animate on-add off-remove off” |
|
* | 3. the `.on-add` and `.off-remove` classes are added to the element | `class="my-animation ng-animate on-add off-remove off"` |
|
||||||
* | 4. $animate waits for a single animation frame (this performs a reflow) | class="my-animation ng-animate on-add off-remove off” |
|
* | 4. `$animate` waits for a single animation frame (this performs a reflow) | `class="my-animation ng-animate on-add off-remove off"` |
|
||||||
* | 5. the .on, .on-add-active and .off-remove-active classes are added and .off is removed (this triggers the CSS transition/animation) | class="my-animation ng-animate on on-add on-add-active off-remove off-remove-active” |
|
* | 5. the `.on`, `.on-add-active` and `.off-remove-active` classes are added and `.off` is removed (this triggers the CSS transition/animation) | `class="my-animation ng-animate on on-add on-add-active off-remove off-remove-active"` |
|
||||||
* | 6. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate on on-add on-add-active off-remove off-remove-active" |
|
* | 6. `$animate` scans the element styles to get the CSS transition/animation duration and delay | `class="my-animation ng-animate on on-add on-add-active off-remove off-remove-active"` |
|
||||||
* | 7. $animate waits for the animation to complete (via events and timeout) | class="my-animation ng-animate on on-add on-add-active off-remove off-remove-active" |
|
* | 7. `$animate` waits for the animation to complete (via events and timeout) | `class="my-animation ng-animate on on-add on-add-active off-remove off-remove-active"` |
|
||||||
* | 8. The animation ends and all generated CSS classes are removed from the element | class="my-animation on" |
|
* | 8. The animation ends and all generated CSS classes are removed from the element | `class="my-animation on"` |
|
||||||
* | 9. The returned promise is resolved. | class="my-animation on" |
|
* | 9. The returned promise is resolved. | `class="my-animation on"` |
|
||||||
*
|
*
|
||||||
* @param {DOMElement} element the element which will have its CSS classes changed
|
* @param {DOMElement} element the element which will have its CSS classes changed
|
||||||
* removed from it
|
* removed from it
|
||||||
|
@ -1274,7 +1275,7 @@ angular.module('ngAnimate', ['ng'])
|
||||||
all animations call this shared animation triggering function internally.
|
all animations call this shared animation triggering function internally.
|
||||||
The animationEvent variable refers to the JavaScript animation event that will be triggered
|
The animationEvent variable refers to the JavaScript animation event that will be triggered
|
||||||
and the className value is the name of the animation that will be applied within the
|
and the className value is the name of the animation that will be applied within the
|
||||||
CSS code. Element, parentElement and afterElement are provided DOM elements for the animation
|
CSS code. Element, `parentElement` and `afterElement` are provided DOM elements for the animation
|
||||||
and the onComplete callback will be fired once the animation is fully complete.
|
and the onComplete callback will be fired once the animation is fully complete.
|
||||||
*/
|
*/
|
||||||
function performAnimation(animationEvent, className, element, parentElement, afterElement, domOperation, options, doneCallback) {
|
function performAnimation(animationEvent, className, element, parentElement, afterElement, domOperation, options, doneCallback) {
|
||||||
|
@ -1386,10 +1387,10 @@ angular.module('ngAnimate', ['ng'])
|
||||||
|
|
||||||
//the ng-animate class does nothing, but it's here to allow for
|
//the ng-animate class does nothing, but it's here to allow for
|
||||||
//parent animations to find and cancel child animations when needed
|
//parent animations to find and cancel child animations when needed
|
||||||
element.addClass(NG_ANIMATE_CLASS_NAME);
|
$$jqLite.addClass(element, NG_ANIMATE_CLASS_NAME);
|
||||||
if (options && options.tempClasses) {
|
if (options && options.tempClasses) {
|
||||||
forEach(options.tempClasses, function(className) {
|
forEach(options.tempClasses, function(className) {
|
||||||
element.addClass(className);
|
$$jqLite.addClass(element, className);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1467,7 +1468,7 @@ angular.module('ngAnimate', ['ng'])
|
||||||
closeAnimation.hasBeenRun = true;
|
closeAnimation.hasBeenRun = true;
|
||||||
if (options && options.tempClasses) {
|
if (options && options.tempClasses) {
|
||||||
forEach(options.tempClasses, function(className) {
|
forEach(options.tempClasses, function(className) {
|
||||||
element.removeClass(className);
|
$$jqLite.removeClass(element, className);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1529,7 +1530,7 @@ angular.module('ngAnimate', ['ng'])
|
||||||
}
|
}
|
||||||
|
|
||||||
if (removeAnimations || !data.totalActive) {
|
if (removeAnimations || !data.totalActive) {
|
||||||
element.removeClass(NG_ANIMATE_CLASS_NAME);
|
$$jqLite.removeClass(element, NG_ANIMATE_CLASS_NAME);
|
||||||
element.removeData(NG_ANIMATE_STATE);
|
element.removeData(NG_ANIMATE_STATE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1770,14 +1771,14 @@ angular.module('ngAnimate', ['ng'])
|
||||||
var staggerCacheKey = cacheKey + ' ' + staggerClassName;
|
var staggerCacheKey = cacheKey + ' ' + staggerClassName;
|
||||||
var applyClasses = !lookupCache[staggerCacheKey];
|
var applyClasses = !lookupCache[staggerCacheKey];
|
||||||
|
|
||||||
applyClasses && element.addClass(staggerClassName);
|
applyClasses && $$jqLite.addClass(element, staggerClassName);
|
||||||
|
|
||||||
stagger = getElementAnimationDetails(element, staggerCacheKey);
|
stagger = getElementAnimationDetails(element, staggerCacheKey);
|
||||||
|
|
||||||
applyClasses && element.removeClass(staggerClassName);
|
applyClasses && $$jqLite.removeClass(element, staggerClassName);
|
||||||
}
|
}
|
||||||
|
|
||||||
element.addClass(className);
|
$$jqLite.addClass(element, className);
|
||||||
|
|
||||||
var formerData = element.data(NG_ANIMATE_CSS_DATA_KEY) || {};
|
var formerData = element.data(NG_ANIMATE_CSS_DATA_KEY) || {};
|
||||||
var timings = getElementAnimationDetails(element, eventCacheKey);
|
var timings = getElementAnimationDetails(element, eventCacheKey);
|
||||||
|
@ -1785,7 +1786,7 @@ angular.module('ngAnimate', ['ng'])
|
||||||
var animationDuration = timings.animationDuration;
|
var animationDuration = timings.animationDuration;
|
||||||
|
|
||||||
if (structural && transitionDuration === 0 && animationDuration === 0) {
|
if (structural && transitionDuration === 0 && animationDuration === 0) {
|
||||||
element.removeClass(className);
|
$$jqLite.removeClass(element, className);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1857,7 +1858,7 @@ angular.module('ngAnimate', ['ng'])
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!staggerTime) {
|
if (!staggerTime) {
|
||||||
element.addClass(activeClassName);
|
$$jqLite.addClass(element, activeClassName);
|
||||||
if (elementData.blockTransition) {
|
if (elementData.blockTransition) {
|
||||||
blockTransitions(node, false);
|
blockTransitions(node, false);
|
||||||
}
|
}
|
||||||
|
@ -1867,7 +1868,7 @@ angular.module('ngAnimate', ['ng'])
|
||||||
var timings = getElementAnimationDetails(element, eventCacheKey);
|
var timings = getElementAnimationDetails(element, eventCacheKey);
|
||||||
var maxDuration = Math.max(timings.transitionDuration, timings.animationDuration);
|
var maxDuration = Math.max(timings.transitionDuration, timings.animationDuration);
|
||||||
if (maxDuration === 0) {
|
if (maxDuration === 0) {
|
||||||
element.removeClass(activeClassName);
|
$$jqLite.removeClass(element, activeClassName);
|
||||||
animateClose(element, className);
|
animateClose(element, className);
|
||||||
activeAnimationComplete();
|
activeAnimationComplete();
|
||||||
return;
|
return;
|
||||||
|
@ -1889,7 +1890,7 @@ angular.module('ngAnimate', ['ng'])
|
||||||
//the jqLite object, so we're safe to use a single variable to house
|
//the jqLite object, so we're safe to use a single variable to house
|
||||||
//the styles since there is always only one element being animated
|
//the styles since there is always only one element being animated
|
||||||
var oldStyle = node.getAttribute('style') || '';
|
var oldStyle = node.getAttribute('style') || '';
|
||||||
if (oldStyle.charAt(oldStyle.length-1) !== ';') {
|
if (oldStyle.charAt(oldStyle.length - 1) !== ';') {
|
||||||
oldStyle += ';';
|
oldStyle += ';';
|
||||||
}
|
}
|
||||||
node.setAttribute('style', oldStyle + ' ' + style);
|
node.setAttribute('style', oldStyle + ' ' + style);
|
||||||
|
@ -1902,7 +1903,7 @@ angular.module('ngAnimate', ['ng'])
|
||||||
|
|
||||||
var staggerTimeout;
|
var staggerTimeout;
|
||||||
if (staggerTime > 0) {
|
if (staggerTime > 0) {
|
||||||
element.addClass(pendingClassName);
|
$$jqLite.addClass(element, pendingClassName);
|
||||||
staggerTimeout = $timeout(function() {
|
staggerTimeout = $timeout(function() {
|
||||||
staggerTimeout = null;
|
staggerTimeout = null;
|
||||||
|
|
||||||
|
@ -1913,8 +1914,8 @@ angular.module('ngAnimate', ['ng'])
|
||||||
blockAnimations(node, false);
|
blockAnimations(node, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
element.addClass(activeClassName);
|
$$jqLite.addClass(element, activeClassName);
|
||||||
element.removeClass(pendingClassName);
|
$$jqLite.removeClass(element, pendingClassName);
|
||||||
|
|
||||||
if (styles) {
|
if (styles) {
|
||||||
if (timings.transitionDuration === 0) {
|
if (timings.transitionDuration === 0) {
|
||||||
|
@ -1941,8 +1942,8 @@ angular.module('ngAnimate', ['ng'])
|
||||||
// timeout done method.
|
// timeout done method.
|
||||||
function onEnd() {
|
function onEnd() {
|
||||||
element.off(css3AnimationEvents, onAnimationProgress);
|
element.off(css3AnimationEvents, onAnimationProgress);
|
||||||
element.removeClass(activeClassName);
|
$$jqLite.removeClass(element, activeClassName);
|
||||||
element.removeClass(pendingClassName);
|
$$jqLite.removeClass(element, pendingClassName);
|
||||||
if (staggerTimeout) {
|
if (staggerTimeout) {
|
||||||
$timeout.cancel(staggerTimeout);
|
$timeout.cancel(staggerTimeout);
|
||||||
}
|
}
|
||||||
|
@ -2030,7 +2031,7 @@ angular.module('ngAnimate', ['ng'])
|
||||||
}
|
}
|
||||||
|
|
||||||
function animateClose(element, className) {
|
function animateClose(element, className) {
|
||||||
element.removeClass(className);
|
$$jqLite.removeClass(element, className);
|
||||||
var data = element.data(NG_ANIMATE_CSS_DATA_KEY);
|
var data = element.data(NG_ANIMATE_CSS_DATA_KEY);
|
||||||
if (data) {
|
if (data) {
|
||||||
if (data.running) {
|
if (data.running) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* @license AngularJS v1.3.2
|
* @license AngularJS v1.3.7
|
||||||
* (c) 2010-2014 Google, Inc. http://angularjs.org
|
* (c) 2010-2014 Google, Inc. http://angularjs.org
|
||||||
* License: MIT
|
* License: MIT
|
||||||
*/
|
*/
|
||||||
|
@ -117,7 +117,7 @@ angular.mock.$Browser = function() {
|
||||||
self.defer.now += delay;
|
self.defer.now += delay;
|
||||||
} else {
|
} else {
|
||||||
if (self.deferredFns.length) {
|
if (self.deferredFns.length) {
|
||||||
self.defer.now = self.deferredFns[self.deferredFns.length-1].time;
|
self.defer.now = self.deferredFns[self.deferredFns.length - 1].time;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('No deferred tasks to be flushed');
|
throw new Error('No deferred tasks to be flushed');
|
||||||
}
|
}
|
||||||
|
@ -428,7 +428,7 @@ angular.mock.$LogProvider = function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
if (errors.length) {
|
if (errors.length) {
|
||||||
errors.unshift("Expected $log to be empty! Either a message was logged unexpectedly, or "+
|
errors.unshift("Expected $log to be empty! Either a message was logged unexpectedly, or " +
|
||||||
"an expected log message was not checked and removed:");
|
"an expected log message was not checked and removed:");
|
||||||
errors.push('');
|
errors.push('');
|
||||||
throw new Error(errors.join('\n---------\n'));
|
throw new Error(errors.join('\n---------\n'));
|
||||||
|
@ -461,17 +461,17 @@ angular.mock.$LogProvider = function() {
|
||||||
* @returns {promise} A promise which will be notified on each iteration.
|
* @returns {promise} A promise which will be notified on each iteration.
|
||||||
*/
|
*/
|
||||||
angular.mock.$IntervalProvider = function() {
|
angular.mock.$IntervalProvider = function() {
|
||||||
this.$get = ['$rootScope', '$q',
|
this.$get = ['$browser', '$rootScope', '$q', '$$q',
|
||||||
function($rootScope, $q) {
|
function($browser, $rootScope, $q, $$q) {
|
||||||
var repeatFns = [],
|
var repeatFns = [],
|
||||||
nextRepeatId = 0,
|
nextRepeatId = 0,
|
||||||
now = 0;
|
now = 0;
|
||||||
|
|
||||||
var $interval = function(fn, delay, count, invokeApply) {
|
var $interval = function(fn, delay, count, invokeApply) {
|
||||||
var deferred = $q.defer(),
|
var iteration = 0,
|
||||||
promise = deferred.promise,
|
skipApply = (angular.isDefined(invokeApply) && !invokeApply),
|
||||||
iteration = 0,
|
deferred = (skipApply ? $$q : $q).defer(),
|
||||||
skipApply = (angular.isDefined(invokeApply) && !invokeApply);
|
promise = deferred.promise;
|
||||||
|
|
||||||
count = (angular.isDefined(count)) ? count : 0;
|
count = (angular.isDefined(count)) ? count : 0;
|
||||||
promise.then(null, null, fn);
|
promise.then(null, null, fn);
|
||||||
|
@ -494,7 +494,11 @@ angular.mock.$IntervalProvider = function() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!skipApply) $rootScope.$apply();
|
if (skipApply) {
|
||||||
|
$browser.defer.flush();
|
||||||
|
} else {
|
||||||
|
$rootScope.$apply();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
repeatFns.push({
|
repeatFns.push({
|
||||||
|
@ -581,10 +585,10 @@ function jsonStringToDate(string) {
|
||||||
tzMin = int(match[9] + match[11]);
|
tzMin = int(match[9] + match[11]);
|
||||||
}
|
}
|
||||||
date.setUTCFullYear(int(match[1]), int(match[2]) - 1, int(match[3]));
|
date.setUTCFullYear(int(match[1]), int(match[2]) - 1, int(match[3]));
|
||||||
date.setUTCHours(int(match[4]||0) - tzHour,
|
date.setUTCHours(int(match[4] || 0) - tzHour,
|
||||||
int(match[5]||0) - tzMin,
|
int(match[5] || 0) - tzMin,
|
||||||
int(match[6]||0),
|
int(match[6] || 0),
|
||||||
int(match[7]||0));
|
int(match[7] || 0));
|
||||||
return date;
|
return date;
|
||||||
}
|
}
|
||||||
return string;
|
return string;
|
||||||
|
@ -663,7 +667,7 @@ angular.mock.TzDate = function(offset, timestamp) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var localOffset = new Date(timestamp).getTimezoneOffset();
|
var localOffset = new Date(timestamp).getTimezoneOffset();
|
||||||
self.offsetDiff = localOffset*60*1000 - offset*1000*60*60;
|
self.offsetDiff = localOffset * 60 * 1000 - offset * 1000 * 60 * 60;
|
||||||
self.date = new Date(timestamp + self.offsetDiff);
|
self.date = new Date(timestamp + self.offsetDiff);
|
||||||
|
|
||||||
self.getTime = function() {
|
self.getTime = function() {
|
||||||
|
@ -815,7 +819,7 @@ angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
|
||||||
animate.queue.push({
|
animate.queue.push({
|
||||||
event: method,
|
event: method,
|
||||||
element: arguments[0],
|
element: arguments[0],
|
||||||
options: arguments[arguments.length-1],
|
options: arguments[arguments.length - 1],
|
||||||
args: arguments
|
args: arguments
|
||||||
});
|
});
|
||||||
return $delegate[method].apply($delegate, arguments);
|
return $delegate[method].apply($delegate, arguments);
|
||||||
|
@ -1115,7 +1119,7 @@ angular.mock.dump = function(object) {
|
||||||
```
|
```
|
||||||
*/
|
*/
|
||||||
angular.mock.$HttpBackendProvider = function() {
|
angular.mock.$HttpBackendProvider = function() {
|
||||||
this.$get = ['$rootScope', createHttpBackendMock];
|
this.$get = ['$rootScope', '$timeout', createHttpBackendMock];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1132,7 +1136,7 @@ angular.mock.$HttpBackendProvider = function() {
|
||||||
* @param {Object=} $browser Auto-flushing enabled if specified
|
* @param {Object=} $browser Auto-flushing enabled if specified
|
||||||
* @return {Object} Instance of $httpBackend mock
|
* @return {Object} Instance of $httpBackend mock
|
||||||
*/
|
*/
|
||||||
function createHttpBackendMock($rootScope, $delegate, $browser) {
|
function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
||||||
var definitions = [],
|
var definitions = [],
|
||||||
expectations = [],
|
expectations = [],
|
||||||
responses = [],
|
responses = [],
|
||||||
|
@ -1145,7 +1149,7 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
|
||||||
return function() {
|
return function() {
|
||||||
return angular.isNumber(status)
|
return angular.isNumber(status)
|
||||||
? [status, data, headers, statusText]
|
? [status, data, headers, statusText]
|
||||||
: [200, status, data];
|
: [200, status, data, headers];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1162,7 +1166,9 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function wrapResponse(wrapped) {
|
function wrapResponse(wrapped) {
|
||||||
if (!$browser && timeout && timeout.then) timeout.then(handleTimeout);
|
if (!$browser && timeout) {
|
||||||
|
timeout.then ? timeout.then(handleTimeout) : $timeout(handleTimeout, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
return handleResponse;
|
return handleResponse;
|
||||||
|
|
||||||
|
@ -1770,7 +1776,7 @@ angular.mock.$RAFDecorator = ['$delegate', function($delegate) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var length = queue.length;
|
var length = queue.length;
|
||||||
for (var i=0;i<length;i++) {
|
for (var i = 0; i < length; i++) {
|
||||||
queue[i]();
|
queue[i]();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2036,7 +2042,7 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
|
||||||
*/
|
*/
|
||||||
angular.mock.e2e = {};
|
angular.mock.e2e = {};
|
||||||
angular.mock.e2e.$httpBackendDecorator =
|
angular.mock.e2e.$httpBackendDecorator =
|
||||||
['$rootScope', '$delegate', '$browser', createHttpBackendMock];
|
['$rootScope', '$timeout', '$delegate', '$browser', createHttpBackendMock];
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2050,7 +2056,7 @@ angular.mock.e2e.$httpBackendDecorator =
|
||||||
*
|
*
|
||||||
* In addition to all the regular `Scope` methods, the following helper methods are available:
|
* In addition to all the regular `Scope` methods, the following helper methods are available:
|
||||||
*/
|
*/
|
||||||
angular.mock.$RootScopeDecorator = function($delegate) {
|
angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) {
|
||||||
|
|
||||||
var $rootScopePrototype = Object.getPrototypeOf($delegate);
|
var $rootScopePrototype = Object.getPrototypeOf($delegate);
|
||||||
|
|
||||||
|
@ -2122,7 +2128,7 @@ angular.mock.$RootScopeDecorator = function($delegate) {
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
};
|
}];
|
||||||
|
|
||||||
|
|
||||||
if (window.jasmine || window.mocha) {
|
if (window.jasmine || window.mocha) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* @license AngularJS v1.3.2
|
* @license AngularJS v1.3.7
|
||||||
* (c) 2010-2014 Google, Inc. http://angularjs.org
|
* (c) 2010-2014 Google, Inc. http://angularjs.org
|
||||||
* License: MIT
|
* License: MIT
|
||||||
*/
|
*/
|
||||||
|
@ -41,7 +41,7 @@ var ngRouteModule = angular.module('ngRoute', ['ng']).
|
||||||
*/
|
*/
|
||||||
function $RouteProvider() {
|
function $RouteProvider() {
|
||||||
function inherit(parent, extra) {
|
function inherit(parent, extra) {
|
||||||
return angular.extend(new (angular.extend(function() {}, {prototype:parent}))(), extra);
|
return angular.extend(Object.create(parent), extra);
|
||||||
}
|
}
|
||||||
|
|
||||||
var routes = {};
|
var routes = {};
|
||||||
|
@ -151,6 +151,9 @@ function $RouteProvider() {
|
||||||
if (angular.isUndefined(routeCopy.reloadOnSearch)) {
|
if (angular.isUndefined(routeCopy.reloadOnSearch)) {
|
||||||
routeCopy.reloadOnSearch = true;
|
routeCopy.reloadOnSearch = true;
|
||||||
}
|
}
|
||||||
|
if (angular.isUndefined(routeCopy.caseInsensitiveMatch)) {
|
||||||
|
routeCopy.caseInsensitiveMatch = this.caseInsensitiveMatch;
|
||||||
|
}
|
||||||
routes[path] = angular.extend(
|
routes[path] = angular.extend(
|
||||||
routeCopy,
|
routeCopy,
|
||||||
path && pathRegExp(path, routeCopy)
|
path && pathRegExp(path, routeCopy)
|
||||||
|
@ -158,9 +161,9 @@ function $RouteProvider() {
|
||||||
|
|
||||||
// create redirection for trailing slashes
|
// create redirection for trailing slashes
|
||||||
if (path) {
|
if (path) {
|
||||||
var redirectPath = (path[path.length-1] == '/')
|
var redirectPath = (path[path.length - 1] == '/')
|
||||||
? path.substr(0, path.length-1)
|
? path.substr(0, path.length - 1)
|
||||||
: path +'/';
|
: path + '/';
|
||||||
|
|
||||||
routes[redirectPath] = angular.extend(
|
routes[redirectPath] = angular.extend(
|
||||||
{redirectTo: path},
|
{redirectTo: path},
|
||||||
|
@ -171,6 +174,17 @@ function $RouteProvider() {
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc property
|
||||||
|
* @name $routeProvider#caseInsensitiveMatch
|
||||||
|
* @description
|
||||||
|
*
|
||||||
|
* A boolean property indicating if routes defined
|
||||||
|
* using this provider should be matched using a case insensitive
|
||||||
|
* algorithm. Defaults to `false`.
|
||||||
|
*/
|
||||||
|
this.caseInsensitiveMatch = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param path {string} path
|
* @param path {string} path
|
||||||
* @param opts {Object} options
|
* @param opts {Object} options
|
||||||
|
@ -639,11 +653,11 @@ function $RouteProvider() {
|
||||||
*/
|
*/
|
||||||
function interpolate(string, params) {
|
function interpolate(string, params) {
|
||||||
var result = [];
|
var result = [];
|
||||||
angular.forEach((string||'').split(':'), function(segment, i) {
|
angular.forEach((string || '').split(':'), function(segment, i) {
|
||||||
if (i === 0) {
|
if (i === 0) {
|
||||||
result.push(segment);
|
result.push(segment);
|
||||||
} else {
|
} else {
|
||||||
var segmentMatch = segment.match(/(\w+)(.*)/);
|
var segmentMatch = segment.match(/(\w+)(?:[?*])?(.*)/);
|
||||||
var key = segmentMatch[1];
|
var key = segmentMatch[1];
|
||||||
result.push(params[key]);
|
result.push(params[key]);
|
||||||
result.push(segmentMatch[2] || '');
|
result.push(segmentMatch[2] || '');
|
||||||
|
@ -774,7 +788,6 @@ ngRouteModule.directive('ngView', ngViewFillContentFactory);
|
||||||
.view-animate-container {
|
.view-animate-container {
|
||||||
position:relative;
|
position:relative;
|
||||||
height:100px!important;
|
height:100px!important;
|
||||||
position:relative;
|
|
||||||
background:white;
|
background:white;
|
||||||
border:1px solid black;
|
border:1px solid black;
|
||||||
height:40px;
|
height:40px;
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
22
test/main.js
22
test/main.js
|
@ -43,4 +43,24 @@ require('../src/js/app-config');
|
||||||
require('../src/js/util');
|
require('../src/js/util');
|
||||||
require('../src/js/crypto');
|
require('../src/js/crypto');
|
||||||
require('../src/js/service');
|
require('../src/js/service');
|
||||||
require('../src/js/email');
|
require('../src/js/email');
|
||||||
|
|
||||||
|
//
|
||||||
|
// Global mocks
|
||||||
|
//
|
||||||
|
|
||||||
|
window.qMock = function(res, rej) {
|
||||||
|
return new Promise(res, rej);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.resolves = function(val) {
|
||||||
|
return new Promise(function(res) {
|
||||||
|
res(val);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
window.rejects = function(val) {
|
||||||
|
return new Promise(function(res, rej) {
|
||||||
|
rej(val);
|
||||||
|
});
|
||||||
|
};
|
|
@ -43,6 +43,7 @@ describe('Account Controller unit test', function() {
|
||||||
scope.state = {};
|
scope.state = {};
|
||||||
accountCtrl = $controller(AccountCtrl, {
|
accountCtrl = $controller(AccountCtrl, {
|
||||||
$scope: scope,
|
$scope: scope,
|
||||||
|
$q: window.qMock,
|
||||||
auth: authStub,
|
auth: authStub,
|
||||||
keychain: keychainStub,
|
keychain: keychainStub,
|
||||||
pgp: pgpStub,
|
pgp: pgpStub,
|
||||||
|
@ -63,8 +64,8 @@ describe('Account Controller unit test', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('export to key file', function() {
|
describe('export to key file', function() {
|
||||||
it('should work', function() {
|
it('should work', function(done) {
|
||||||
keychainStub.getUserKeyPair.withArgs(emailAddress).yields(null, {
|
keychainStub.getUserKeyPair.withArgs(emailAddress).returns(resolves({
|
||||||
publicKey: {
|
publicKey: {
|
||||||
_id: dummyKeyId,
|
_id: dummyKeyId,
|
||||||
publicKey: 'a'
|
publicKey: 'a'
|
||||||
|
@ -72,24 +73,26 @@ describe('Account Controller unit test', function() {
|
||||||
privateKey: {
|
privateKey: {
|
||||||
encryptedKey: 'b'
|
encryptedKey: 'b'
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
downloadStub.createDownload.withArgs(sinon.match(function(arg) {
|
downloadStub.createDownload.withArgs(sinon.match(function(arg) {
|
||||||
return arg.content === 'a\r\nb' && arg.filename === 'whiteout_mail_' + emailAddress + '_' + expectedKeyId + '.asc' && arg.contentType === 'text/plain';
|
return arg.content === 'a\r\nb' && arg.filename === 'whiteout_mail_' + emailAddress + '_' + expectedKeyId + '.asc' && arg.contentType === 'text/plain';
|
||||||
})).returns();
|
})).returns();
|
||||||
|
|
||||||
scope.exportKeyFile();
|
scope.exportKeyFile().then(function() {
|
||||||
|
expect(scope.state.lightbox).to.equal(undefined);
|
||||||
expect(scope.state.lightbox).to.equal(undefined);
|
expect(keychainStub.getUserKeyPair.calledOnce).to.be.true;
|
||||||
expect(keychainStub.getUserKeyPair.calledOnce).to.be.true;
|
expect(downloadStub.createDownload.calledOnce).to.be.true;
|
||||||
expect(downloadStub.createDownload.calledOnce).to.be.true;
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not work when key export failed', function() {
|
it('should not work when key export failed', function(done) {
|
||||||
keychainStub.getUserKeyPair.yields(new Error());
|
keychainStub.getUserKeyPair.returns(rejects(new Error()));
|
||||||
|
|
||||||
scope.exportKeyFile();
|
scope.exportKeyFile().then(function() {
|
||||||
|
expect(dialogStub.error.calledOnce).to.be.true;
|
||||||
expect(dialogStub.error.calledOnce).to.be.true;
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
|
@ -33,6 +33,7 @@ describe('Action Bar Controller unit test', function() {
|
||||||
|
|
||||||
actionBarCtrl = $controller(ActionBarCtrl, {
|
actionBarCtrl = $controller(ActionBarCtrl, {
|
||||||
$scope: scope,
|
$scope: scope,
|
||||||
|
$q: window.qMock,
|
||||||
email: emailMock,
|
email: emailMock,
|
||||||
dialog: dialogMock,
|
dialog: dialogMock,
|
||||||
status: statusMock
|
status: statusMock
|
||||||
|
@ -47,13 +48,14 @@ describe('Action Bar Controller unit test', function() {
|
||||||
scope.deleteMessage();
|
scope.deleteMessage();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should delete the selected mail', function() {
|
it('should delete the selected mail', function(done) {
|
||||||
emailMock.deleteMessage.yields();
|
emailMock.deleteMessage.returns(resolves());
|
||||||
|
|
||||||
scope.deleteMessage({});
|
scope.deleteMessage({}).then(function() {
|
||||||
|
expect(statusMock.setReading.withArgs(false).calledOnce).to.be.true;
|
||||||
expect(statusMock.setReading.withArgs(false).calledOnce).to.be.true;
|
expect(emailMock.deleteMessage.calledOnce).to.be.true;
|
||||||
expect(emailMock.deleteMessage.calledOnce).to.be.true;
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -79,13 +81,14 @@ describe('Action Bar Controller unit test', function() {
|
||||||
scope.moveMessage();
|
scope.moveMessage();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should move the selected mail', function() {
|
it('should move the selected mail', function(done) {
|
||||||
emailMock.moveMessage.yields();
|
emailMock.moveMessage.returns(resolves());
|
||||||
|
|
||||||
scope.moveMessage({}, {});
|
scope.moveMessage({}, {}).then(function() {
|
||||||
|
expect(statusMock.setReading.withArgs(false).calledOnce).to.be.true;
|
||||||
expect(statusMock.setReading.withArgs(false).calledOnce).to.be.true;
|
expect(emailMock.moveMessage.calledOnce).to.be.true;
|
||||||
expect(emailMock.moveMessage.calledOnce).to.be.true;
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -144,22 +147,24 @@ describe('Action Bar Controller unit test', function() {
|
||||||
}, true);
|
}, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should mark the selected mail', function() {
|
it('should mark the selected mail', function(done) {
|
||||||
emailMock.setFlags.yields();
|
emailMock.setFlags.returns(resolves());
|
||||||
|
|
||||||
scope.markMessage({}, true);
|
scope.markMessage({}, true).then(function() {
|
||||||
|
expect(statusMock.setReading.withArgs(false).calledOnce).to.be.true;
|
||||||
expect(statusMock.setReading.withArgs(false).calledOnce).to.be.true;
|
expect(emailMock.setFlags.calledOnce).to.be.true;
|
||||||
expect(emailMock.setFlags.calledOnce).to.be.true;
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should mark the selected mail and close read mode', function() {
|
it('should mark the selected mail and close read mode', function(done) {
|
||||||
emailMock.setFlags.yields();
|
emailMock.setFlags.returns(resolves());
|
||||||
|
|
||||||
scope.markMessage({}, true, true);
|
scope.markMessage({}, true, true).then(function() {
|
||||||
|
expect(statusMock.setReading.calledOnce).to.be.false;
|
||||||
expect(statusMock.setReading.calledOnce).to.be.false;
|
expect(emailMock.setFlags.calledOnce).to.be.true;
|
||||||
expect(emailMock.setFlags.calledOnce).to.be.true;
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -196,11 +201,11 @@ describe('Action Bar Controller unit test', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should flag the selected mail', function() {
|
it('should flag the selected mail', function() {
|
||||||
emailMock.setFlags.yields();
|
emailMock.setFlags.returns(resolves());
|
||||||
|
|
||||||
scope.flagMessage({}, true);
|
scope.flagMessage({}, true).then(function() {
|
||||||
|
expect(emailMock.setFlags.calledOnce).to.be.true;
|
||||||
expect(emailMock.setFlags.calledOnce).to.be.true;
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ describe('Contacts Controller unit test', function() {
|
||||||
scope.state = {};
|
scope.state = {};
|
||||||
contactsCtrl = $controller(ContactsCtrl, {
|
contactsCtrl = $controller(ContactsCtrl, {
|
||||||
$scope: scope,
|
$scope: scope,
|
||||||
|
$q: window.qMock,
|
||||||
keychain: keychainStub,
|
keychain: keychainStub,
|
||||||
pgp: pgpStub,
|
pgp: pgpStub,
|
||||||
dialog: dialogStub
|
dialog: dialogStub
|
||||||
|
@ -36,28 +37,30 @@ describe('Contacts Controller unit test', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('listKeys', function() {
|
describe('listKeys', function() {
|
||||||
it('should fail due to error in keychain.listLocalPublicKeys', function() {
|
it('should fail due to error in keychain.listLocalPublicKeys', function(done) {
|
||||||
keychainStub.listLocalPublicKeys.yields(42);
|
keychainStub.listLocalPublicKeys.returns(rejects(42));
|
||||||
|
|
||||||
scope.listKeys();
|
scope.listKeys().then(function() {
|
||||||
|
expect(dialogStub.error.calledOnce).to.be.true;
|
||||||
expect(dialogStub.error.calledOnce).to.be.true;
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work', function() {
|
it('should work', function(done) {
|
||||||
keychainStub.listLocalPublicKeys.yields(null, [{
|
keychainStub.listLocalPublicKeys.returns(resolves([{
|
||||||
_id: '12345'
|
_id: '12345'
|
||||||
}]);
|
}]));
|
||||||
pgpStub.getKeyParams.returns({
|
pgpStub.getKeyParams.returns({
|
||||||
fingerprint: 'asdf'
|
fingerprint: 'asdf'
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(scope.keys).to.not.exist;
|
expect(scope.keys).to.not.exist;
|
||||||
scope.listKeys();
|
scope.listKeys().then(function() {
|
||||||
|
expect(scope.keys.length).to.equal(1);
|
||||||
expect(scope.keys.length).to.equal(1);
|
expect(scope.keys[0]._id).to.equal('12345');
|
||||||
expect(scope.keys[0]._id).to.equal('12345');
|
expect(scope.keys[0].fingerprint).to.equal('asdf');
|
||||||
expect(scope.keys[0].fingerprint).to.equal('asdf');
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -89,13 +92,13 @@ describe('Contacts Controller unit test', function() {
|
||||||
userIds: [],
|
userIds: [],
|
||||||
publicKey: '-----BEGIN PGP PUBLIC KEY BLOCK-----',
|
publicKey: '-----BEGIN PGP PUBLIC KEY BLOCK-----',
|
||||||
imported: true
|
imported: true
|
||||||
}).yields();
|
}).returns(resolves());
|
||||||
|
|
||||||
scope.listKeys = function() {
|
scope.listKeys = function() {};
|
||||||
|
|
||||||
|
scope.importKey(keyArmored).then(function() {
|
||||||
done();
|
done();
|
||||||
};
|
});
|
||||||
|
|
||||||
scope.importKey(keyArmored);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail due to invalid armored key', function() {
|
it('should fail due to invalid armored key', function() {
|
||||||
|
@ -115,7 +118,7 @@ describe('Contacts Controller unit test', function() {
|
||||||
expect(dialogStub.error.calledOnce).to.be.true;
|
expect(dialogStub.error.calledOnce).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail due to error in keychain.saveLocalPublicKey', function() {
|
it('should fail due to error in keychain.saveLocalPublicKey', function(done) {
|
||||||
var keyArmored = '-----BEGIN PGP PUBLIC KEY BLOCK-----';
|
var keyArmored = '-----BEGIN PGP PUBLIC KEY BLOCK-----';
|
||||||
|
|
||||||
pgpStub.getKeyParams.returns({
|
pgpStub.getKeyParams.returns({
|
||||||
|
@ -123,11 +126,12 @@ describe('Contacts Controller unit test', function() {
|
||||||
userId: 'max@example.com'
|
userId: 'max@example.com'
|
||||||
});
|
});
|
||||||
|
|
||||||
keychainStub.saveLocalPublicKey.yields(42);
|
keychainStub.saveLocalPublicKey.returns(rejects(42));
|
||||||
|
|
||||||
scope.importKey(keyArmored);
|
scope.importKey(keyArmored).then(function() {
|
||||||
|
expect(dialogStub.error.calledOnce).to.be.true;
|
||||||
expect(dialogStub.error.calledOnce).to.be.true;
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -137,25 +141,26 @@ describe('Contacts Controller unit test', function() {
|
||||||
_id: '12345'
|
_id: '12345'
|
||||||
};
|
};
|
||||||
|
|
||||||
keychainStub.removeLocalPublicKey.withArgs('12345').yields();
|
keychainStub.removeLocalPublicKey.withArgs('12345').returns(resolves());
|
||||||
|
|
||||||
scope.listKeys = function() {
|
scope.listKeys = function() {};
|
||||||
|
|
||||||
|
scope.removeKey(key).then(function() {
|
||||||
done();
|
done();
|
||||||
};
|
});
|
||||||
|
|
||||||
scope.removeKey(key);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail due to error in keychain.removeLocalPublicKey', function() {
|
it('should fail due to error in keychain.removeLocalPublicKey', function(done) {
|
||||||
var key = {
|
var key = {
|
||||||
_id: '12345'
|
_id: '12345'
|
||||||
};
|
};
|
||||||
|
|
||||||
keychainStub.removeLocalPublicKey.withArgs('12345').yields(42);
|
keychainStub.removeLocalPublicKey.withArgs('12345').returns(rejects(42));
|
||||||
|
|
||||||
scope.removeKey(key);
|
scope.removeKey(key).then(function() {
|
||||||
|
expect(dialogStub.error.calledOnce).to.be.true;
|
||||||
expect(dialogStub.error.calledOnce).to.be.true;
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
|
@ -44,6 +44,7 @@ describe('Mail List controller unit test', function() {
|
||||||
ctrl = $controller(MailListCtrl, {
|
ctrl = $controller(MailListCtrl, {
|
||||||
$scope: scope,
|
$scope: scope,
|
||||||
$location: location,
|
$location: location,
|
||||||
|
$q: window.qMock,
|
||||||
status: statusMock,
|
status: statusMock,
|
||||||
notification: notificationMock,
|
notification: notificationMock,
|
||||||
email: emailMock,
|
email: emailMock,
|
||||||
|
@ -207,15 +208,17 @@ describe('Mail List controller unit test', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getBody', function() {
|
describe('getBody', function() {
|
||||||
it('should get the mail content', function() {
|
it('should get the mail content', function(done) {
|
||||||
scope.state.nav = {
|
scope.state.nav = {
|
||||||
currentFolder: {
|
currentFolder: {
|
||||||
type: 'asd',
|
type: 'asd',
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
scope.getBody();
|
scope.getBody().then(function() {
|
||||||
expect(emailMock.getBody.calledOnce).to.be.true;
|
expect(emailMock.getBody.calledOnce).to.be.true;
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -245,7 +248,7 @@ describe('Mail List controller unit test', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('select', function() {
|
describe('select', function() {
|
||||||
it('should decrypt, focus mark an unread mail as read', function() {
|
it('should decrypt, focus mark an unread mail as read', function(done) {
|
||||||
scope.pendingNotifications = ['asd'];
|
scope.pendingNotifications = ['asd'];
|
||||||
sinon.stub(notificationMock, 'close');
|
sinon.stub(notificationMock, 'close');
|
||||||
|
|
||||||
|
@ -272,20 +275,21 @@ describe('Mail List controller unit test', function() {
|
||||||
|
|
||||||
keychainMock.refreshKeyForUserId.withArgs({
|
keychainMock.refreshKeyForUserId.withArgs({
|
||||||
userId: mail.from[0].address
|
userId: mail.from[0].address
|
||||||
}).yields();
|
}).returns(resolves());
|
||||||
|
|
||||||
scope.select(mail);
|
scope.select(mail).then(function() {
|
||||||
|
expect(emailMock.decryptBody.calledOnce).to.be.true;
|
||||||
|
expect(keychainMock.refreshKeyForUserId.calledOnce).to.be.true;
|
||||||
|
expect(scope.state.mailList.selected).to.equal(mail);
|
||||||
|
expect(notificationMock.close.calledWith('asd')).to.be.true;
|
||||||
|
expect(notificationMock.close.calledOnce).to.be.true;
|
||||||
|
|
||||||
expect(emailMock.decryptBody.calledOnce).to.be.true;
|
notificationMock.close.restore();
|
||||||
expect(keychainMock.refreshKeyForUserId.calledOnce).to.be.true;
|
done();
|
||||||
expect(scope.state.mailList.selected).to.equal(mail);
|
});
|
||||||
expect(notificationMock.close.calledWith('asd')).to.be.true;
|
|
||||||
expect(notificationMock.close.calledOnce).to.be.true;
|
|
||||||
|
|
||||||
notificationMock.close.restore();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should decrypt and focus a read mail', function() {
|
it('should decrypt and focus a read mail', function(done) {
|
||||||
var mail = {
|
var mail = {
|
||||||
from: [{
|
from: [{
|
||||||
address: 'asd'
|
address: 'asd'
|
||||||
|
@ -307,13 +311,14 @@ describe('Mail List controller unit test', function() {
|
||||||
|
|
||||||
keychainMock.refreshKeyForUserId.withArgs({
|
keychainMock.refreshKeyForUserId.withArgs({
|
||||||
userId: mail.from[0].address
|
userId: mail.from[0].address
|
||||||
}).yields();
|
}).returns(resolves());
|
||||||
|
|
||||||
scope.select(mail);
|
scope.select(mail).then(function() {
|
||||||
|
expect(emailMock.decryptBody.calledOnce).to.be.true;
|
||||||
expect(emailMock.decryptBody.calledOnce).to.be.true;
|
expect(keychainMock.refreshKeyForUserId.calledOnce).to.be.true;
|
||||||
expect(keychainMock.refreshKeyForUserId.calledOnce).to.be.true;
|
expect(scope.state.mailList.selected).to.equal(mail);
|
||||||
expect(scope.state.mailList.selected).to.equal(mail);
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,7 @@ describe('Navigation Controller unit test', function() {
|
||||||
ctrl = $controller(NavigationCtrl, {
|
ctrl = $controller(NavigationCtrl, {
|
||||||
$scope: scope,
|
$scope: scope,
|
||||||
$routeParams: {},
|
$routeParams: {},
|
||||||
|
$q: window.qMock,
|
||||||
account: accountMock,
|
account: accountMock,
|
||||||
email: emailDaoMock,
|
email: emailDaoMock,
|
||||||
outbox: outboxBoMock,
|
outbox: outboxBoMock,
|
||||||
|
|
|
@ -23,6 +23,7 @@ describe('Private Key Upload Controller unit test', function() {
|
||||||
ctrl = $controller(PrivateKeyUploadCtrl, {
|
ctrl = $controller(PrivateKeyUploadCtrl, {
|
||||||
$location: location,
|
$location: location,
|
||||||
$scope: scope,
|
$scope: scope,
|
||||||
|
$q: window.qMock,
|
||||||
keychain: keychainMock,
|
keychain: keychainMock,
|
||||||
pgp: pgpStub,
|
pgp: pgpStub,
|
||||||
dialog: dialogStub,
|
dialog: dialogStub,
|
||||||
|
@ -41,14 +42,15 @@ describe('Private Key Upload Controller unit test', function() {
|
||||||
_id: 'keyId',
|
_id: 'keyId',
|
||||||
};
|
};
|
||||||
|
|
||||||
it('should fail', function() {
|
it('should fail', function(done) {
|
||||||
pgpStub.getKeyParams.returns(keyParams);
|
pgpStub.getKeyParams.returns(keyParams);
|
||||||
keychainMock.hasPrivateKey.yields(42);
|
keychainMock.hasPrivateKey.returns(rejects(42));
|
||||||
|
|
||||||
scope.checkServerForKey();
|
scope.checkServerForKey().then(function() {
|
||||||
|
expect(dialogStub.error.calledOnce).to.be.true;
|
||||||
expect(dialogStub.error.calledOnce).to.be.true;
|
expect(keychainMock.hasPrivateKey.calledOnce).to.be.true;
|
||||||
expect(keychainMock.hasPrivateKey.calledOnce).to.be.true;
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return true', function(done) {
|
it('should return true', function(done) {
|
||||||
|
@ -56,9 +58,9 @@ describe('Private Key Upload Controller unit test', function() {
|
||||||
keychainMock.hasPrivateKey.withArgs({
|
keychainMock.hasPrivateKey.withArgs({
|
||||||
userId: keyParams.userId,
|
userId: keyParams.userId,
|
||||||
keyId: keyParams._id
|
keyId: keyParams._id
|
||||||
}).yields(null, true);
|
}).returns(resolves(true));
|
||||||
|
|
||||||
scope.checkServerForKey(function(privateKeySynced) {
|
scope.checkServerForKey().then(function(privateKeySynced) {
|
||||||
expect(privateKeySynced).to.be.true;
|
expect(privateKeySynced).to.be.true;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -69,9 +71,9 @@ describe('Private Key Upload Controller unit test', function() {
|
||||||
keychainMock.hasPrivateKey.withArgs({
|
keychainMock.hasPrivateKey.withArgs({
|
||||||
userId: keyParams.userId,
|
userId: keyParams.userId,
|
||||||
keyId: keyParams._id
|
keyId: keyParams._id
|
||||||
}).yields(null, false);
|
}).returns(resolves(false));
|
||||||
|
|
||||||
scope.checkServerForKey(function(privateKeySynced) {
|
scope.checkServerForKey().then(function(privateKeySynced) {
|
||||||
expect(privateKeySynced).to.be.undefined;
|
expect(privateKeySynced).to.be.undefined;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -110,27 +112,27 @@ describe('Private Key Upload Controller unit test', function() {
|
||||||
|
|
||||||
describe('setDeviceName', function() {
|
describe('setDeviceName', function() {
|
||||||
it('should work', function(done) {
|
it('should work', function(done) {
|
||||||
keychainMock.setDeviceName.yields();
|
keychainMock.setDeviceName.returns(resolves());
|
||||||
scope.setDeviceName(done);
|
scope.setDeviceName().then(done);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('encryptAndUploadKey', function() {
|
describe('encryptAndUploadKey', function() {
|
||||||
it('should fail due to keychain.registerDevice', function() {
|
it('should fail due to keychain.registerDevice', function(done) {
|
||||||
keychainMock.registerDevice.yields(42);
|
keychainMock.registerDevice.returns(rejects(42));
|
||||||
|
|
||||||
scope.encryptAndUploadKey();
|
scope.encryptAndUploadKey().then(function() {
|
||||||
|
expect(dialogStub.error.calledOnce).to.be.true;
|
||||||
expect(dialogStub.error.calledOnce).to.be.true;
|
expect(keychainMock.registerDevice.calledOnce).to.be.true;
|
||||||
expect(keychainMock.registerDevice.calledOnce).to.be.true;
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work', function(done) {
|
it('should work', function(done) {
|
||||||
keychainMock.registerDevice.yields();
|
keychainMock.registerDevice.returns(resolves());
|
||||||
keychainMock.uploadPrivateKey.yields();
|
keychainMock.uploadPrivateKey.returns(resolves());
|
||||||
|
|
||||||
scope.encryptAndUploadKey(function(err) {
|
scope.encryptAndUploadKey().then(function() {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(keychainMock.registerDevice.calledOnce).to.be.true;
|
expect(keychainMock.registerDevice.calledOnce).to.be.true;
|
||||||
expect(keychainMock.uploadPrivateKey.calledOnce).to.be.true;
|
expect(keychainMock.uploadPrivateKey.calledOnce).to.be.true;
|
||||||
done();
|
done();
|
||||||
|
@ -185,36 +187,39 @@ describe('Private Key Upload Controller unit test', function() {
|
||||||
expect(scope.step).to.equal(2);
|
expect(scope.step).to.equal(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail for 3 due to error in setDeviceName', function() {
|
it('should fail for 3 due to error in setDeviceName', function(done) {
|
||||||
scope.step = 3;
|
scope.step = 3;
|
||||||
setDeviceNameStub.yields(42);
|
setDeviceNameStub.returns(rejects(42));
|
||||||
|
|
||||||
scope.goForward();
|
scope.goForward().then(function() {
|
||||||
|
expect(dialogStub.error.calledOnce).to.be.true;
|
||||||
expect(dialogStub.error.calledOnce).to.be.true;
|
expect(scope.step).to.equal(3);
|
||||||
expect(scope.step).to.equal(3);
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail for 3 due to error in encryptAndUploadKey', function() {
|
it('should fail for 3 due to error in encryptAndUploadKey', function(done) {
|
||||||
scope.step = 3;
|
scope.step = 3;
|
||||||
setDeviceNameStub.yields();
|
setDeviceNameStub.returns(resolves());
|
||||||
encryptAndUploadKeyStub.yields(42);
|
encryptAndUploadKeyStub.returns(rejects(42));
|
||||||
|
|
||||||
scope.goForward();
|
scope.goForward().then(function() {
|
||||||
|
expect(dialogStub.error.calledOnce).to.be.true;
|
||||||
expect(dialogStub.error.calledOnce).to.be.true;
|
expect(scope.step).to.equal(4);
|
||||||
expect(scope.step).to.equal(4);
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work for 3', function() {
|
it('should work for 3', function(done) {
|
||||||
scope.step = 3;
|
scope.step = 3;
|
||||||
setDeviceNameStub.yields();
|
setDeviceNameStub.returns(resolves());
|
||||||
encryptAndUploadKeyStub.yields();
|
encryptAndUploadKeyStub.returns(resolves());
|
||||||
|
|
||||||
scope.goForward();
|
scope.goForward().then(function() {
|
||||||
|
expect(dialogStub.info.calledOnce).to.be.true;
|
||||||
expect(dialogStub.info.calledOnce).to.be.true;
|
expect(scope.step).to.equal(4);
|
||||||
expect(scope.step).to.equal(4);
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
|
@ -11,8 +11,7 @@ var Keychain = require('../../../../src/js/service/keychain'),
|
||||||
Download = require('../../../../src/js/util/download');
|
Download = require('../../../../src/js/util/download');
|
||||||
|
|
||||||
describe('Read Controller unit test', function() {
|
describe('Read Controller unit test', function() {
|
||||||
var scope, ctrl, keychainMock, invitationMock, emailMock, pgpMock, outboxMock, dialogMock, authMock, downloadMock,
|
var scope, ctrl, keychainMock, invitationMock, emailMock, pgpMock, outboxMock, dialogMock, authMock, downloadMock;
|
||||||
emailAddress = 'sender@example.com';
|
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
keychainMock = sinon.createStubInstance(Keychain);
|
keychainMock = sinon.createStubInstance(Keychain);
|
||||||
|
@ -31,6 +30,7 @@ describe('Read Controller unit test', function() {
|
||||||
scope.state = {};
|
scope.state = {};
|
||||||
ctrl = $controller(ReadCtrl, {
|
ctrl = $controller(ReadCtrl, {
|
||||||
$scope: scope,
|
$scope: scope,
|
||||||
|
$q: window.qMock,
|
||||||
email: emailMock,
|
email: emailMock,
|
||||||
invitation: invitationMock,
|
invitation: invitationMock,
|
||||||
outbox: outboxMock,
|
outbox: outboxMock,
|
||||||
|
@ -66,33 +66,37 @@ describe('Read Controller unit test', function() {
|
||||||
describe('getKeyId', function() {
|
describe('getKeyId', function() {
|
||||||
var address = 'asfd@asdf.com';
|
var address = 'asfd@asdf.com';
|
||||||
|
|
||||||
it('should show searching on error', function() {
|
it('should show searching on error', function(done) {
|
||||||
expect(scope.keyId).to.equal('No key found.');
|
expect(scope.keyId).to.equal('No key found.');
|
||||||
keychainMock.getReceiverPublicKey.yields(42);
|
keychainMock.getReceiverPublicKey.returns(rejects(42));
|
||||||
|
|
||||||
scope.getKeyId(address);
|
scope.getKeyId(address).then(function() {
|
||||||
|
expect(dialogMock.error.calledOnce).to.be.true;
|
||||||
expect(dialogMock.error.calledOnce).to.be.true;
|
expect(scope.keyId).to.equal('Searching...');
|
||||||
expect(scope.keyId).to.equal('Searching...');
|
done();
|
||||||
});
|
|
||||||
|
|
||||||
it('should allow invitation on empty key', function() {
|
|
||||||
keychainMock.getReceiverPublicKey.yields();
|
|
||||||
|
|
||||||
scope.getKeyId(address);
|
|
||||||
|
|
||||||
expect(scope.keyId).to.equal('User has no key. Click to invite.');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should show searching on error', function() {
|
|
||||||
keychainMock.getReceiverPublicKey.yields(null, {
|
|
||||||
publicKey: 'PUBLIC KEY'
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow invitation on empty key', function(done) {
|
||||||
|
keychainMock.getReceiverPublicKey.returns(resolves());
|
||||||
|
|
||||||
|
scope.getKeyId(address).then(function() {
|
||||||
|
expect(scope.keyId).to.equal('User has no key. Click to invite.');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show searching on error', function(done) {
|
||||||
|
keychainMock.getReceiverPublicKey.returns(resolves({
|
||||||
|
publicKey: 'PUBLIC KEY'
|
||||||
|
}));
|
||||||
|
|
||||||
pgpMock.getFingerprint.returns('XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX');
|
pgpMock.getFingerprint.returns('XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX');
|
||||||
|
|
||||||
scope.getKeyId(address);
|
scope.getKeyId(address).then(function() {
|
||||||
expect(scope.keyId).to.equal('PGP key: XXXXXXXX');
|
expect(scope.keyId).to.equal('PGP key: XXXXXXXX');
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -108,36 +112,39 @@ describe('Read Controller unit test', function() {
|
||||||
expect(scope.keyId).to.equal('No key found.');
|
expect(scope.keyId).to.equal('No key found.');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show error on invitation dao invite error', function() {
|
it('should show error on invitation dao invite error', function(done) {
|
||||||
invitationMock.invite.yields(42);
|
invitationMock.invite.returns(rejects(42));
|
||||||
|
|
||||||
scope.invite({
|
scope.invite({
|
||||||
address: 'asdf@asdf.de'
|
address: 'asdf@asdf.de'
|
||||||
|
}).then(function() {
|
||||||
|
expect(dialogMock.error.calledOnce).to.be.true;
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(dialogMock.error.calledOnce).to.be.true;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show error on outbox put error', function() {
|
it('should show error on outbox put error', function(done) {
|
||||||
invitationMock.invite.yields();
|
invitationMock.invite.returns(resolves());
|
||||||
outboxMock.put.yields(42);
|
outboxMock.put.returns(rejects(42));
|
||||||
|
|
||||||
scope.invite({
|
scope.invite({
|
||||||
address: 'asdf@asdf.de'
|
address: 'asdf@asdf.de'
|
||||||
|
}).then(function() {
|
||||||
|
expect(dialogMock.error.calledOnce).to.be.true;
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(dialogMock.error.calledOnce).to.be.true;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work', function() {
|
it('should work', function(done) {
|
||||||
invitationMock.invite.yields();
|
invitationMock.invite.returns(resolves());
|
||||||
outboxMock.put.yields();
|
outboxMock.put.returns(resolves());
|
||||||
|
|
||||||
scope.invite({
|
scope.invite({
|
||||||
address: 'asdf@asdf.de'
|
address: 'asdf@asdf.de'
|
||||||
|
}).then(function() {
|
||||||
|
expect(dialogMock.error.calledOnce).to.be.false;
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(dialogMock.error.calledOnce).to.be.true;
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,7 @@ describe('Set Passphrase Controller unit test', function() {
|
||||||
scope.state = {};
|
scope.state = {};
|
||||||
setPassphraseCtrl = $controller(SetPassphraseCtrl, {
|
setPassphraseCtrl = $controller(SetPassphraseCtrl, {
|
||||||
$scope: scope,
|
$scope: scope,
|
||||||
|
$q: window.qMock,
|
||||||
pgp: pgpStub,
|
pgp: pgpStub,
|
||||||
keychain: keychainStub,
|
keychain: keychainStub,
|
||||||
dialog: dialogStub
|
dialog: dialogStub
|
||||||
|
@ -49,31 +50,32 @@ describe('Set Passphrase Controller unit test', function() {
|
||||||
|
|
||||||
afterEach(function() {});
|
afterEach(function() {});
|
||||||
|
|
||||||
describe('setPassphrase', function() {
|
describe('setPassphrase', function(done) {
|
||||||
it('should work', function() {
|
it('should work', function() {
|
||||||
scope.oldPassphrase = 'old';
|
scope.oldPassphrase = 'old';
|
||||||
scope.newPassphrase = 'new';
|
scope.newPassphrase = 'new';
|
||||||
|
|
||||||
keychainStub.lookupPrivateKey.withArgs(dummyKeyId).yields(null, {
|
keychainStub.lookupPrivateKey.withArgs(dummyKeyId).returns(resolves({
|
||||||
encryptedKey: 'encrypted'
|
encryptedKey: 'encrypted'
|
||||||
});
|
}));
|
||||||
|
|
||||||
pgpStub.changePassphrase.withArgs({
|
pgpStub.changePassphrase.withArgs({
|
||||||
privateKeyArmored: 'encrypted',
|
privateKeyArmored: 'encrypted',
|
||||||
oldPassphrase: 'old',
|
oldPassphrase: 'old',
|
||||||
newPassphrase: 'new'
|
newPassphrase: 'new'
|
||||||
}).yields(null, 'newArmoredKey');
|
}).returns(resolves('newArmoredKey'));
|
||||||
|
|
||||||
keychainStub.saveLocalPrivateKey.withArgs({
|
keychainStub.saveLocalPrivateKey.withArgs({
|
||||||
_id: dummyKeyId,
|
_id: dummyKeyId,
|
||||||
userId: emailAddress,
|
userId: emailAddress,
|
||||||
userIds: [],
|
userIds: [],
|
||||||
encryptedKey: 'newArmoredKey'
|
encryptedKey: 'newArmoredKey'
|
||||||
}).yields();
|
}).returns(resolves());
|
||||||
|
|
||||||
scope.setPassphrase();
|
scope.setPassphrase().then(function() {
|
||||||
|
expect(dialogStub.info.calledOnce).to.be.true;
|
||||||
expect(dialogStub.info.calledOnce).to.be.true;
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ describe('Write controller unit test', function() {
|
||||||
scope.state = {};
|
scope.state = {};
|
||||||
ctrl = $controller(WriteCtrl, {
|
ctrl = $controller(WriteCtrl, {
|
||||||
$scope: scope,
|
$scope: scope,
|
||||||
|
$q: window.qMock,
|
||||||
auth: authMock,
|
auth: authMock,
|
||||||
keychain: keychainMock,
|
keychain: keychainMock,
|
||||||
pgp: pgpMock,
|
pgp: pgpMock,
|
||||||
|
@ -183,24 +184,25 @@ describe('Write controller unit test', function() {
|
||||||
expect(keychainMock.getReceiverPublicKey.called).to.be.false;
|
expect(keychainMock.getReceiverPublicKey.called).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not work for error in keychain', function() {
|
it('should not work for error in keychain', function(done) {
|
||||||
var recipient = {
|
var recipient = {
|
||||||
address: 'asds@example.com'
|
address: 'asds@example.com'
|
||||||
};
|
};
|
||||||
|
|
||||||
keychainMock.refreshKeyForUserId.withArgs({
|
keychainMock.refreshKeyForUserId.withArgs({
|
||||||
userId: recipient.address
|
userId: recipient.address
|
||||||
}).yields({
|
}).returns(rejects({
|
||||||
errMsg: '404 not found yadda yadda'
|
errMsg: '404 not found yadda yadda'
|
||||||
|
}));
|
||||||
|
|
||||||
|
scope.verify(recipient).then(function() {
|
||||||
|
expect(dialogMock.error.calledOnce).to.be.true;
|
||||||
|
expect(recipient.key).to.be.undefined;
|
||||||
|
expect(recipient.secure).to.be.false;
|
||||||
|
expect(scope.checkSendStatus.callCount).to.equal(1);
|
||||||
|
expect(keychainMock.refreshKeyForUserId.calledOnce).to.be.true;
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
scope.verify(recipient);
|
|
||||||
|
|
||||||
expect(dialogMock.error.calledOnce).to.be.true;
|
|
||||||
expect(recipient.key).to.be.undefined;
|
|
||||||
expect(recipient.secure).to.be.false;
|
|
||||||
expect(scope.checkSendStatus.callCount).to.equal(1);
|
|
||||||
expect(keychainMock.refreshKeyForUserId.calledOnce).to.be.true;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work for main userId', function(done) {
|
it('should work for main userId', function(done) {
|
||||||
|
@ -210,11 +212,11 @@ describe('Write controller unit test', function() {
|
||||||
|
|
||||||
keychainMock.refreshKeyForUserId.withArgs({
|
keychainMock.refreshKeyForUserId.withArgs({
|
||||||
userId: recipient.address
|
userId: recipient.address
|
||||||
}).yields(null, {
|
}).returns(resolves({
|
||||||
userId: 'asdf@example.com'
|
userId: 'asdf@example.com'
|
||||||
});
|
}));
|
||||||
|
|
||||||
scope.$digest = function() {
|
scope.verify(recipient).then(function() {
|
||||||
expect(recipient.key).to.deep.equal({
|
expect(recipient.key).to.deep.equal({
|
||||||
userId: 'asdf@example.com'
|
userId: 'asdf@example.com'
|
||||||
});
|
});
|
||||||
|
@ -222,9 +224,7 @@ describe('Write controller unit test', function() {
|
||||||
expect(scope.checkSendStatus.callCount).to.equal(2);
|
expect(scope.checkSendStatus.callCount).to.equal(2);
|
||||||
expect(keychainMock.refreshKeyForUserId.calledOnce).to.be.true;
|
expect(keychainMock.refreshKeyForUserId.calledOnce).to.be.true;
|
||||||
done();
|
done();
|
||||||
};
|
});
|
||||||
|
|
||||||
scope.verify(recipient);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work for secondary userId', function(done) {
|
it('should work for secondary userId', function(done) {
|
||||||
|
@ -240,17 +240,15 @@ describe('Write controller unit test', function() {
|
||||||
|
|
||||||
keychainMock.refreshKeyForUserId.withArgs({
|
keychainMock.refreshKeyForUserId.withArgs({
|
||||||
userId: recipient.address
|
userId: recipient.address
|
||||||
}).yields(null, key);
|
}).returns(resolves(key));
|
||||||
|
|
||||||
scope.$digest = function() {
|
scope.verify(recipient).then(function() {
|
||||||
expect(recipient.key).to.deep.equal(key);
|
expect(recipient.key).to.deep.equal(key);
|
||||||
expect(recipient.secure).to.be.true;
|
expect(recipient.secure).to.be.true;
|
||||||
expect(scope.checkSendStatus.callCount).to.equal(2);
|
expect(scope.checkSendStatus.callCount).to.equal(2);
|
||||||
expect(keychainMock.refreshKeyForUserId.calledOnce).to.be.true;
|
expect(keychainMock.refreshKeyForUserId.calledOnce).to.be.true;
|
||||||
done();
|
done();
|
||||||
};
|
});
|
||||||
|
|
||||||
scope.verify(recipient);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -311,7 +309,7 @@ describe('Write controller unit test', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('send to outbox', function() {
|
describe('send to outbox', function() {
|
||||||
it('should work', function() {
|
it('should work', function(done) {
|
||||||
scope.to = [{
|
scope.to = [{
|
||||||
address: 'pity@dafool'
|
address: 'pity@dafool'
|
||||||
}];
|
}];
|
||||||
|
@ -341,25 +339,26 @@ describe('Write controller unit test', function() {
|
||||||
expect(mail.sentDate).to.exist;
|
expect(mail.sentDate).to.exist;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
})).yields();
|
})).returns(resolves());
|
||||||
emailMock.setFlags.yields();
|
emailMock.setFlags.returns(resolves());
|
||||||
|
|
||||||
scope.sendToOutbox();
|
scope.sendToOutbox().then(function() {
|
||||||
|
expect(statusMock.setReading.withArgs(false).calledOnce).to.be.true;
|
||||||
expect(statusMock.setReading.withArgs(false).calledOnce).to.be.true;
|
expect(outboxMock.put.calledOnce).to.be.true;
|
||||||
expect(outboxMock.put.calledOnce).to.be.true;
|
expect(emailMock.setFlags.calledOnce).to.be.true;
|
||||||
expect(emailMock.setFlags.calledOnce).to.be.true;
|
expect(scope.state.lightbox).to.be.undefined;
|
||||||
expect(scope.state.lightbox).to.be.undefined;
|
expect(scope.replyTo.answered).to.be.true;
|
||||||
expect(scope.replyTo.answered).to.be.true;
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('lookupAddressBook', function() {
|
describe('lookupAddressBook', function() {
|
||||||
it('should work', function(done) {
|
it('should work', function(done) {
|
||||||
keychainMock.listLocalPublicKeys.yields(null, [{
|
keychainMock.listLocalPublicKeys.returns(resolves([{
|
||||||
userId: 'test@asdf.com',
|
userId: 'test@asdf.com',
|
||||||
publicKey: 'KEY'
|
publicKey: 'KEY'
|
||||||
}]);
|
}]));
|
||||||
|
|
||||||
var result = scope.lookupAddressBook('test');
|
var result = scope.lookupAddressBook('test');
|
||||||
|
|
||||||
|
@ -369,7 +368,6 @@ describe('Write controller unit test', function() {
|
||||||
}]);
|
}]);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
scope.$digest();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work with cache', function(done) {
|
it('should work with cache', function(done) {
|
||||||
|
@ -387,7 +385,6 @@ describe('Write controller unit test', function() {
|
||||||
}]);
|
}]);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
scope.$digest();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -116,16 +116,17 @@ describe('Add Account Controller unit test', function() {
|
||||||
setCredentialsStub.restore();
|
setCredentialsStub.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should use oauth', function() {
|
it('should use oauth', function(done) {
|
||||||
dialogStub.confirm = function(options) {
|
dialogStub.confirm = function(options) {
|
||||||
options.callback(true);
|
options.callback(true).then(function() {
|
||||||
|
expect(setCredentialsStub.calledOnce).to.be.true;
|
||||||
|
expect(authStub.getOAuthToken.calledOnce).to.be.true;
|
||||||
|
done();
|
||||||
|
});
|
||||||
};
|
};
|
||||||
authStub.getOAuthToken.yields();
|
authStub.getOAuthToken.returns(resolves());
|
||||||
|
|
||||||
scope.oauthPossible();
|
scope.oauthPossible();
|
||||||
|
|
||||||
expect(setCredentialsStub.calledOnce).to.be.true;
|
|
||||||
expect(authStub.getOAuthToken.calledOnce).to.be.true;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not use oauth', function() {
|
it('should not use oauth', function() {
|
||||||
|
@ -139,25 +140,29 @@ describe('Add Account Controller unit test', function() {
|
||||||
expect(authStub.getOAuthToken.called).to.be.false;
|
expect(authStub.getOAuthToken.called).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not forward to login when oauth fails', function() {
|
it('should not forward to login when oauth fails', function(done) {
|
||||||
|
dialogStub.error = function(err) {
|
||||||
|
expect(err).to.exist;
|
||||||
|
expect(setCredentialsStub.called).to.be.false;
|
||||||
|
done();
|
||||||
|
};
|
||||||
|
|
||||||
dialogStub.confirm = function(options) {
|
dialogStub.confirm = function(options) {
|
||||||
options.callback(true);
|
options.callback(true);
|
||||||
};
|
};
|
||||||
authStub.getOAuthToken.yields(new Error());
|
authStub.getOAuthToken.returns(rejects(new Error()));
|
||||||
|
|
||||||
scope.oauthPossible();
|
scope.oauthPossible();
|
||||||
|
|
||||||
expect(dialogStub.error.calledOnce).to.be.true;
|
|
||||||
expect(setCredentialsStub.called).to.be.false;
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('setCredentials', function() {
|
describe('setCredentials', function() {
|
||||||
it('should work', function() {
|
it('should work', inject(function($timeout) {
|
||||||
scope.setCredentials();
|
scope.setCredentials().then();
|
||||||
|
$timeout.flush();
|
||||||
|
|
||||||
expect(location.path.calledWith('/login-set-credentials')).to.be.true;
|
expect(location.path.calledWith('/login-set-credentials')).to.be.true;
|
||||||
});
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
|
@ -29,6 +29,7 @@ describe('Create Account Controller unit test', function() {
|
||||||
$location: location,
|
$location: location,
|
||||||
$scope: scope,
|
$scope: scope,
|
||||||
$routeParams: {},
|
$routeParams: {},
|
||||||
|
$q: window.qMock,
|
||||||
auth: authStub,
|
auth: authStub,
|
||||||
admin: adminStub
|
admin: adminStub
|
||||||
});
|
});
|
||||||
|
@ -54,16 +55,15 @@ describe('Create Account Controller unit test', function() {
|
||||||
scope.form.$invalid = false;
|
scope.form.$invalid = false;
|
||||||
scope.betaCode = 'asfd';
|
scope.betaCode = 'asfd';
|
||||||
scope.phone = '12345';
|
scope.phone = '12345';
|
||||||
adminStub.createUser.yieldsAsync(new Error('asdf'));
|
adminStub.createUser.returns(rejects(new Error('asdf')));
|
||||||
|
|
||||||
scope.$apply = function() {
|
scope.createWhiteoutAccount().then(function() {
|
||||||
expect(scope.busy).to.be.false;
|
expect(scope.busy).to.be.false;
|
||||||
expect(scope.errMsg).to.equal('asdf');
|
expect(scope.errMsg).to.equal('asdf');
|
||||||
expect(adminStub.createUser.calledOnce).to.be.true;
|
expect(adminStub.createUser.calledOnce).to.be.true;
|
||||||
done();
|
done();
|
||||||
};
|
});
|
||||||
|
|
||||||
scope.createWhiteoutAccount();
|
|
||||||
expect(scope.busy).to.be.true;
|
expect(scope.busy).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -71,16 +71,15 @@ describe('Create Account Controller unit test', function() {
|
||||||
scope.form.$invalid = false;
|
scope.form.$invalid = false;
|
||||||
scope.betaCode = 'asfd';
|
scope.betaCode = 'asfd';
|
||||||
scope.phone = '12345';
|
scope.phone = '12345';
|
||||||
adminStub.createUser.yieldsAsync();
|
adminStub.createUser.returns(resolves());
|
||||||
|
|
||||||
scope.$apply = function() {
|
scope.createWhiteoutAccount().then(function() {
|
||||||
expect(scope.busy).to.be.false;
|
expect(scope.busy).to.be.false;
|
||||||
expect(scope.errMsg).to.be.undefined;
|
expect(scope.errMsg).to.be.undefined;
|
||||||
expect(adminStub.createUser.calledOnce).to.be.true;
|
expect(adminStub.createUser.calledOnce).to.be.true;
|
||||||
done();
|
done();
|
||||||
};
|
});
|
||||||
|
|
||||||
scope.createWhiteoutAccount();
|
|
||||||
expect(scope.busy).to.be.true;
|
expect(scope.busy).to.be.true;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -26,17 +26,13 @@ describe('Login Controller unit test', function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
authMock.emailAddress = emailAddress;
|
authMock.emailAddress = emailAddress;
|
||||||
});
|
|
||||||
|
|
||||||
function createController() {
|
|
||||||
angular.module('login-test', ['woServices', 'woEmail', 'woUtil']);
|
angular.module('login-test', ['woServices', 'woEmail', 'woUtil']);
|
||||||
angular.mock.module('login-test');
|
angular.mock.module('login-test');
|
||||||
angular.mock.inject(function($rootScope, $controller) {
|
angular.mock.inject(function($rootScope, $controller) {
|
||||||
scope = $rootScope.$new();
|
scope = $rootScope.$new();
|
||||||
scope.state = {};
|
scope.state = {};
|
||||||
scope.form = {};
|
scope.form = {};
|
||||||
scope.goTo = function() {};
|
|
||||||
goToStub = sinon.stub(scope, 'goTo');
|
|
||||||
|
|
||||||
ctrl = $controller(LoginCtrl, {
|
ctrl = $controller(LoginCtrl, {
|
||||||
$scope: scope,
|
$scope: scope,
|
||||||
|
@ -46,151 +42,174 @@ describe('Login Controller unit test', function() {
|
||||||
auth: authMock,
|
auth: authMock,
|
||||||
email: emailMock,
|
email: emailMock,
|
||||||
keychain: keychainMock,
|
keychain: keychainMock,
|
||||||
dialog: dialogMock
|
dialog: dialogMock,
|
||||||
|
appConfig: {
|
||||||
|
preventAutoStart: true
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
scope.goTo = function() {};
|
||||||
|
goToStub = sinon.stub(scope, 'goTo');
|
||||||
|
});
|
||||||
|
|
||||||
afterEach(function() {});
|
afterEach(function() {});
|
||||||
|
|
||||||
it('should fail for auth.getEmailAddress', function() {
|
it('should fail for auth.getEmailAddress', function(done) {
|
||||||
authMock.init.yields();
|
authMock.init.returns(resolves());
|
||||||
authMock.getEmailAddress.yields(new Error());
|
authMock.getEmailAddress.returns(rejects(new Error()));
|
||||||
|
|
||||||
createController();
|
scope.init().then(function() {
|
||||||
|
expect(updateHandlerMock.checkForUpdate.calledOnce).to.be.true;
|
||||||
expect(updateHandlerMock.checkForUpdate.calledOnce).to.be.true;
|
expect(authMock.init.calledOnce).to.be.true;
|
||||||
expect(authMock.init.calledOnce).to.be.true;
|
expect(dialogMock.error.calledOnce).to.be.true;
|
||||||
expect(dialogMock.error.calledOnce).to.be.true;
|
done();
|
||||||
});
|
|
||||||
|
|
||||||
it('should fail for auth.init', function() {
|
|
||||||
authMock.init.yields(new Error());
|
|
||||||
authMock.getEmailAddress.yields(null, {
|
|
||||||
emailAddress: emailAddress
|
|
||||||
});
|
});
|
||||||
|
|
||||||
createController();
|
|
||||||
|
|
||||||
expect(authMock.init.calledOnce).to.be.true;
|
|
||||||
expect(accountMock.init.called).to.be.false;
|
|
||||||
expect(dialogMock.error.calledOnce).to.be.true;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should redirect to /add-account', function() {
|
it('should fail for auth.init', function(done) {
|
||||||
authMock.init.yields();
|
authMock.init.returns(rejects(new Error()));
|
||||||
authMock.getEmailAddress.yields(null, {});
|
authMock.getEmailAddress.returns(resolves({
|
||||||
|
|
||||||
createController();
|
|
||||||
|
|
||||||
expect(goToStub.withArgs('/add-account').calledOnce).to.be.true;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should redirect to /login-existing', function() {
|
|
||||||
authMock.init.yields();
|
|
||||||
authMock.getEmailAddress.yields(null, {
|
|
||||||
emailAddress: emailAddress
|
emailAddress: emailAddress
|
||||||
|
}));
|
||||||
|
|
||||||
|
scope.init().then(function() {
|
||||||
|
expect(authMock.init.calledOnce).to.be.true;
|
||||||
|
expect(accountMock.init.called).to.be.false;
|
||||||
|
expect(dialogMock.error.calledOnce).to.be.true;
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
accountMock.init.yields(null, {
|
});
|
||||||
|
|
||||||
|
it('should redirect to /add-account', function(done) {
|
||||||
|
authMock.init.returns(resolves());
|
||||||
|
authMock.getEmailAddress.returns(resolves({}));
|
||||||
|
|
||||||
|
scope.init().then(function() {
|
||||||
|
expect(goToStub.withArgs('/add-account').called).to.be.true;
|
||||||
|
expect(goToStub.calledOnce).to.be.true;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should redirect to /login-existing', function(done) {
|
||||||
|
authMock.init.returns(resolves());
|
||||||
|
authMock.getEmailAddress.returns(resolves({
|
||||||
|
emailAddress: emailAddress
|
||||||
|
}));
|
||||||
|
accountMock.init.returns(resolves({
|
||||||
publicKey: 'publicKey',
|
publicKey: 'publicKey',
|
||||||
privateKey: 'privateKey'
|
privateKey: 'privateKey'
|
||||||
|
}));
|
||||||
|
emailMock.unlock.returns(rejects(new Error()));
|
||||||
|
|
||||||
|
scope.init().then(function() {
|
||||||
|
expect(goToStub.withArgs('/login-existing').called).to.be.true;
|
||||||
|
expect(goToStub.calledOnce).to.be.true;
|
||||||
|
expect(authMock.storeCredentials.called).to.be.false;
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
emailMock.unlock.yields(new Error());
|
|
||||||
|
|
||||||
createController();
|
|
||||||
|
|
||||||
expect(goToStub.withArgs('/login-existing').calledOnce).to.be.true;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail for auth.storeCredentials', function() {
|
it('should fail for auth.storeCredentials', function(done) {
|
||||||
authMock.init.yields();
|
authMock.init.returns(resolves());
|
||||||
authMock.getEmailAddress.yields(null, {
|
authMock.getEmailAddress.returns(resolves({
|
||||||
emailAddress: emailAddress
|
emailAddress: emailAddress
|
||||||
});
|
}));
|
||||||
accountMock.init.yields(null, {
|
accountMock.init.returns(resolves({
|
||||||
publicKey: 'publicKey',
|
publicKey: 'publicKey',
|
||||||
privateKey: 'privateKey'
|
privateKey: 'privateKey'
|
||||||
|
}));
|
||||||
|
emailMock.unlock.returns(resolves());
|
||||||
|
authMock.storeCredentials.returns(rejects(new Error()));
|
||||||
|
|
||||||
|
scope.init().then(function() {
|
||||||
|
expect(dialogMock.error.calledOnce).to.be.true;
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
emailMock.unlock.yields();
|
|
||||||
authMock.storeCredentials.yields(new Error());
|
|
||||||
|
|
||||||
createController();
|
|
||||||
|
|
||||||
expect(dialogMock.error.calledOnce).to.be.true;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should redirect to /account', function() {
|
it('should redirect to /account', function(done) {
|
||||||
authMock.init.yields();
|
authMock.init.returns(resolves());
|
||||||
authMock.getEmailAddress.yields(null, {
|
authMock.getEmailAddress.returns(resolves({
|
||||||
emailAddress: emailAddress
|
emailAddress: emailAddress
|
||||||
});
|
}));
|
||||||
accountMock.init.yields(null, {
|
accountMock.init.returns(resolves({
|
||||||
publicKey: 'publicKey',
|
publicKey: 'publicKey',
|
||||||
privateKey: 'privateKey'
|
privateKey: 'privateKey'
|
||||||
|
}));
|
||||||
|
emailMock.unlock.returns(resolves());
|
||||||
|
authMock.storeCredentials.returns(resolves());
|
||||||
|
|
||||||
|
scope.init().then(function() {
|
||||||
|
expect(goToStub.withArgs('/account').called).to.be.true;
|
||||||
|
expect(goToStub.calledOnce).to.be.true;
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
emailMock.unlock.yields();
|
|
||||||
authMock.storeCredentials.yields();
|
|
||||||
|
|
||||||
createController();
|
|
||||||
|
|
||||||
expect(goToStub.withArgs('/account').calledOnce).to.be.true;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail for keychain.requestPrivateKeyDownload', function() {
|
it('should fail for keychain.requestPrivateKeyDownload', function(done) {
|
||||||
authMock.init.yields();
|
authMock.init.returns(resolves());
|
||||||
authMock.getEmailAddress.yields(null, {
|
authMock.getEmailAddress.returns(resolves({
|
||||||
emailAddress: emailAddress
|
emailAddress: emailAddress
|
||||||
});
|
}));
|
||||||
accountMock.init.yields(null, {
|
accountMock.init.returns(resolves({
|
||||||
publicKey: 'publicKey'
|
publicKey: 'publicKey'
|
||||||
|
}));
|
||||||
|
keychainMock.requestPrivateKeyDownload.returns(rejects(new Error()));
|
||||||
|
|
||||||
|
scope.init().then(function() {
|
||||||
|
expect(dialogMock.error.calledOnce).to.be.true;
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
keychainMock.requestPrivateKeyDownload.yields(new Error());
|
|
||||||
|
|
||||||
createController();
|
|
||||||
|
|
||||||
expect(dialogMock.error.calledOnce).to.be.true;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should redirect to /login-privatekey-download', function() {
|
it('should redirect to /login-privatekey-download', function(done) {
|
||||||
authMock.init.yields();
|
authMock.init.returns(resolves());
|
||||||
authMock.getEmailAddress.yields(null, {
|
authMock.getEmailAddress.returns(resolves({
|
||||||
emailAddress: emailAddress
|
emailAddress: emailAddress
|
||||||
});
|
}));
|
||||||
accountMock.init.yields(null, {
|
accountMock.init.returns(resolves({
|
||||||
publicKey: 'publicKey'
|
publicKey: 'publicKey'
|
||||||
|
}));
|
||||||
|
keychainMock.requestPrivateKeyDownload.returns(resolves(true));
|
||||||
|
|
||||||
|
scope.init().then(function() {
|
||||||
|
expect(goToStub.withArgs('/login-privatekey-download').called).to.be.true;
|
||||||
|
expect(goToStub.calledOnce).to.be.true;
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
keychainMock.requestPrivateKeyDownload.yields(null, true);
|
|
||||||
|
|
||||||
createController();
|
|
||||||
|
|
||||||
expect(goToStub.withArgs('/login-privatekey-download').calledOnce).to.be.true;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should redirect to /login-new-device', function() {
|
it('should redirect to /login-new-device', function(done) {
|
||||||
authMock.init.yields();
|
authMock.init.returns(resolves());
|
||||||
authMock.getEmailAddress.yields(null, {
|
authMock.getEmailAddress.returns(resolves({
|
||||||
emailAddress: emailAddress
|
emailAddress: emailAddress
|
||||||
});
|
}));
|
||||||
accountMock.init.yields(null, {
|
accountMock.init.returns(resolves({
|
||||||
publicKey: 'publicKey'
|
publicKey: 'publicKey'
|
||||||
|
}));
|
||||||
|
keychainMock.requestPrivateKeyDownload.returns(resolves());
|
||||||
|
|
||||||
|
scope.init().then(function() {
|
||||||
|
expect(goToStub.withArgs('/login-new-device').called).to.be.true;
|
||||||
|
expect(goToStub.calledOnce).to.be.true;
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
keychainMock.requestPrivateKeyDownload.yields();
|
|
||||||
|
|
||||||
createController();
|
|
||||||
|
|
||||||
expect(goToStub.withArgs('/login-new-device').calledOnce).to.be.true;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should redirect to /login-initial', function() {
|
it('should redirect to /login-initial', function(done) {
|
||||||
authMock.init.yields();
|
authMock.init.returns(resolves());
|
||||||
authMock.getEmailAddress.yields(null, {
|
authMock.getEmailAddress.returns(resolves({
|
||||||
emailAddress: emailAddress
|
emailAddress: emailAddress
|
||||||
|
}));
|
||||||
|
accountMock.init.returns(resolves({}));
|
||||||
|
|
||||||
|
scope.init().then(function() {
|
||||||
|
expect(goToStub.withArgs('/login-initial').called).to.be.true;
|
||||||
|
expect(goToStub.calledOnce).to.be.true;
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
accountMock.init.yields(null, {});
|
|
||||||
|
|
||||||
createController();
|
|
||||||
|
|
||||||
expect(goToStub.withArgs('/login-initial').calledOnce).to.be.true;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
|
@ -28,6 +28,7 @@ describe('Login (existing user) Controller unit test', function() {
|
||||||
ctrl = $controller(LoginExistingCtrl, {
|
ctrl = $controller(LoginExistingCtrl, {
|
||||||
$scope: scope,
|
$scope: scope,
|
||||||
$routeParams: {},
|
$routeParams: {},
|
||||||
|
$q: window.qMock,
|
||||||
email: emailDaoMock,
|
email: emailDaoMock,
|
||||||
auth: authMock,
|
auth: authMock,
|
||||||
keychain: keychainMock
|
keychain: keychainMock
|
||||||
|
@ -44,32 +45,35 @@ describe('Login (existing user) Controller unit test', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('confirm passphrase', function() {
|
describe('confirm passphrase', function() {
|
||||||
it('should unlock crypto and start', function() {
|
it('should unlock crypto and start', function(done) {
|
||||||
var keypair = {},
|
var keypair = {},
|
||||||
pathSpy = sinon.spy(location, 'path');
|
pathSpy = sinon.spy(location, 'path');
|
||||||
scope.passphrase = passphrase;
|
scope.passphrase = passphrase;
|
||||||
keychainMock.getUserKeyPair.withArgs(emailAddress).yields(null, keypair);
|
keychainMock.getUserKeyPair.withArgs(emailAddress).returns(resolves(keypair));
|
||||||
emailDaoMock.unlock.withArgs({
|
emailDaoMock.unlock.withArgs({
|
||||||
keypair: keypair,
|
keypair: keypair,
|
||||||
passphrase: passphrase
|
passphrase: passphrase
|
||||||
}).yields();
|
}).returns(resolves());
|
||||||
authMock.storeCredentials.yields();
|
authMock.storeCredentials.returns(resolves());
|
||||||
|
|
||||||
scope.confirmPassphrase();
|
scope.confirmPassphrase().then(function() {
|
||||||
|
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
expect(emailDaoMock.unlock.calledOnce).to.be.true;
|
||||||
expect(emailDaoMock.unlock.calledOnce).to.be.true;
|
expect(pathSpy.calledOnce).to.be.true;
|
||||||
expect(pathSpy.calledOnce).to.be.true;
|
expect(pathSpy.calledWith('/account')).to.be.true;
|
||||||
expect(pathSpy.calledWith('/account')).to.be.true;
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not work when keypair unavailable', function() {
|
it('should not work when keypair unavailable', function(done) {
|
||||||
scope.passphrase = passphrase;
|
scope.passphrase = passphrase;
|
||||||
keychainMock.getUserKeyPair.withArgs(emailAddress).yields(new Error('asd'));
|
keychainMock.getUserKeyPair.withArgs(emailAddress).returns(rejects(new Error('asd')));
|
||||||
|
|
||||||
scope.confirmPassphrase();
|
scope.confirmPassphrase().then(function() {
|
||||||
expect(scope.errMsg).to.exist;
|
expect(scope.errMsg).to.exist;
|
||||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
|
@ -30,6 +30,7 @@ describe('Login (initial user) Controller unit test', function() {
|
||||||
ctrl = $controller(LoginInitialCtrl, {
|
ctrl = $controller(LoginInitialCtrl, {
|
||||||
$scope: scope,
|
$scope: scope,
|
||||||
$routeParams: {},
|
$routeParams: {},
|
||||||
|
$q: window.qMock,
|
||||||
newsletter: newsletter,
|
newsletter: newsletter,
|
||||||
email: emailMock,
|
email: emailMock,
|
||||||
auth: authMock
|
auth: authMock
|
||||||
|
@ -74,36 +75,38 @@ describe('Login (initial user) Controller unit test', function() {
|
||||||
expect(newsletterStub.called).to.be.false;
|
expect(newsletterStub.called).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail due to error in emailDao.unlock', function() {
|
it('should fail due to error in emailDao.unlock', function(done) {
|
||||||
scope.agree = true;
|
scope.agree = true;
|
||||||
|
|
||||||
emailMock.unlock.withArgs({
|
emailMock.unlock.withArgs({
|
||||||
passphrase: undefined
|
passphrase: undefined
|
||||||
}).yields(new Error('asdf'));
|
}).returns(rejects(new Error('asdf')));
|
||||||
authMock.storeCredentials.yields();
|
authMock.storeCredentials.returns(resolves());
|
||||||
|
|
||||||
scope.generateKey();
|
scope.generateKey().then(function() {
|
||||||
|
expect(scope.errMsg).to.exist;
|
||||||
expect(scope.errMsg).to.exist;
|
expect(scope.state.ui).to.equal(1);
|
||||||
expect(scope.state.ui).to.equal(1);
|
expect(newsletterStub.called).to.be.true;
|
||||||
expect(newsletterStub.called).to.be.true;
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should unlock crypto', function() {
|
it('should unlock crypto', function(done) {
|
||||||
scope.agree = true;
|
scope.agree = true;
|
||||||
|
|
||||||
emailMock.unlock.withArgs({
|
emailMock.unlock.withArgs({
|
||||||
passphrase: undefined
|
passphrase: undefined
|
||||||
}).yields();
|
}).returns(resolves());
|
||||||
authMock.storeCredentials.yields();
|
authMock.storeCredentials.returns(resolves());
|
||||||
|
|
||||||
scope.generateKey();
|
scope.generateKey().then(function() {
|
||||||
|
expect(scope.errMsg).to.not.exist;
|
||||||
expect(scope.errMsg).to.not.exist;
|
expect(scope.state.ui).to.equal(2);
|
||||||
expect(scope.state.ui).to.equal(2);
|
expect(newsletterStub.called).to.be.true;
|
||||||
expect(newsletterStub.called).to.be.true;
|
expect(location.$$path).to.equal('/account');
|
||||||
expect(location.$$path).to.equal('/account');
|
expect(emailMock.unlock.calledOnce).to.be.true;
|
||||||
expect(emailMock.unlock.calledOnce).to.be.true;
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
|
@ -35,6 +35,7 @@ describe('Login (new device) Controller unit test', function() {
|
||||||
ctrl = $controller(LoginNewDeviceCtrl, {
|
ctrl = $controller(LoginNewDeviceCtrl, {
|
||||||
$scope: scope,
|
$scope: scope,
|
||||||
$routeParams: {},
|
$routeParams: {},
|
||||||
|
$q: window.qMock,
|
||||||
email: emailMock,
|
email: emailMock,
|
||||||
auth: authMock,
|
auth: authMock,
|
||||||
pgp: pgpMock,
|
pgp: pgpMock,
|
||||||
|
@ -53,7 +54,7 @@ describe('Login (new device) Controller unit test', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('confirm passphrase', function() {
|
describe('confirm passphrase', function() {
|
||||||
it('should unlock crypto with a public key on the server', function() {
|
it('should unlock crypto with a public key on the server', function(done) {
|
||||||
scope.passphrase = passphrase;
|
scope.passphrase = passphrase;
|
||||||
scope.key = {
|
scope.key = {
|
||||||
privateKeyArmored: 'b'
|
privateKeyArmored: 'b'
|
||||||
|
@ -64,20 +65,21 @@ describe('Login (new device) Controller unit test', function() {
|
||||||
userIds: []
|
userIds: []
|
||||||
});
|
});
|
||||||
|
|
||||||
keychainMock.getUserKeyPair.withArgs(emailAddress).yields(null, {
|
keychainMock.getUserKeyPair.withArgs(emailAddress).returns(resolves({
|
||||||
_id: keyId,
|
_id: keyId,
|
||||||
publicKey: 'a'
|
publicKey: 'a'
|
||||||
|
}));
|
||||||
|
emailMock.unlock.withArgs(sinon.match.any, passphrase).returns(resolves());
|
||||||
|
keychainMock.putUserKeyPair.returns(resolves());
|
||||||
|
|
||||||
|
scope.confirmPassphrase().then(function() {
|
||||||
|
expect(emailMock.unlock.calledOnce).to.be.true;
|
||||||
|
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
emailMock.unlock.withArgs(sinon.match.any, passphrase).yields();
|
|
||||||
keychainMock.putUserKeyPair.yields();
|
|
||||||
|
|
||||||
scope.confirmPassphrase();
|
|
||||||
|
|
||||||
expect(emailMock.unlock.calledOnce).to.be.true;
|
|
||||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should unlock crypto with no key on the server', function() {
|
it('should unlock crypto with no key on the server', function(done) {
|
||||||
scope.passphrase = passphrase;
|
scope.passphrase = passphrase;
|
||||||
scope.key = {
|
scope.key = {
|
||||||
privateKeyArmored: 'b',
|
privateKeyArmored: 'b',
|
||||||
|
@ -89,17 +91,18 @@ describe('Login (new device) Controller unit test', function() {
|
||||||
userIds: []
|
userIds: []
|
||||||
});
|
});
|
||||||
|
|
||||||
keychainMock.getUserKeyPair.withArgs(emailAddress).yields();
|
keychainMock.getUserKeyPair.withArgs(emailAddress).returns(resolves());
|
||||||
emailMock.unlock.withArgs(sinon.match.any, passphrase).yields();
|
emailMock.unlock.withArgs(sinon.match.any, passphrase).returns(resolves());
|
||||||
keychainMock.putUserKeyPair.yields();
|
keychainMock.putUserKeyPair.returns(resolves());
|
||||||
|
|
||||||
scope.confirmPassphrase();
|
scope.confirmPassphrase().then(function() {
|
||||||
|
expect(emailMock.unlock.calledOnce).to.be.true;
|
||||||
expect(emailMock.unlock.calledOnce).to.be.true;
|
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not work when keypair upload fails', function() {
|
it('should not work when keypair upload fails', function(done) {
|
||||||
scope.passphrase = passphrase;
|
scope.passphrase = passphrase;
|
||||||
scope.key = {
|
scope.key = {
|
||||||
privateKeyArmored: 'b'
|
privateKeyArmored: 'b'
|
||||||
|
@ -110,24 +113,25 @@ describe('Login (new device) Controller unit test', function() {
|
||||||
userIds: []
|
userIds: []
|
||||||
});
|
});
|
||||||
|
|
||||||
keychainMock.getUserKeyPair.withArgs(emailAddress).yields(null, {
|
keychainMock.getUserKeyPair.withArgs(emailAddress).returns(resolves({
|
||||||
_id: keyId,
|
_id: keyId,
|
||||||
publicKey: 'a'
|
publicKey: 'a'
|
||||||
});
|
}));
|
||||||
emailMock.unlock.yields();
|
emailMock.unlock.returns(resolves());
|
||||||
keychainMock.putUserKeyPair.yields({
|
keychainMock.putUserKeyPair.returns(rejects({
|
||||||
errMsg: 'yo mamma.'
|
errMsg: 'yo mamma.'
|
||||||
|
}));
|
||||||
|
|
||||||
|
scope.confirmPassphrase().then(function() {
|
||||||
|
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||||
|
expect(emailMock.unlock.calledOnce).to.be.true;
|
||||||
|
expect(keychainMock.putUserKeyPair.calledOnce).to.be.true;
|
||||||
|
expect(scope.errMsg).to.equal('yo mamma.');
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
scope.confirmPassphrase();
|
|
||||||
|
|
||||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
|
||||||
expect(emailMock.unlock.calledOnce).to.be.true;
|
|
||||||
expect(keychainMock.putUserKeyPair.calledOnce).to.be.true;
|
|
||||||
expect(scope.errMsg).to.equal('yo mamma.');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not work when unlock fails', function() {
|
it('should not work when unlock fails', function(done) {
|
||||||
scope.passphrase = passphrase;
|
scope.passphrase = passphrase;
|
||||||
scope.key = {
|
scope.key = {
|
||||||
privateKeyArmored: 'b'
|
privateKeyArmored: 'b'
|
||||||
|
@ -138,36 +142,38 @@ describe('Login (new device) Controller unit test', function() {
|
||||||
userIds: []
|
userIds: []
|
||||||
});
|
});
|
||||||
|
|
||||||
keychainMock.getUserKeyPair.withArgs(emailAddress).yields(null, {
|
keychainMock.getUserKeyPair.withArgs(emailAddress).returns(resolves({
|
||||||
_id: keyId,
|
_id: keyId,
|
||||||
publicKey: 'a'
|
publicKey: 'a'
|
||||||
});
|
}));
|
||||||
emailMock.unlock.yields({
|
emailMock.unlock.returns(rejects({
|
||||||
errMsg: 'yo mamma.'
|
errMsg: 'yo mamma.'
|
||||||
|
}));
|
||||||
|
|
||||||
|
scope.confirmPassphrase().then(function() {
|
||||||
|
expect(scope.incorrect).to.be.true;
|
||||||
|
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||||
|
expect(emailMock.unlock.calledOnce).to.be.true;
|
||||||
|
expect(scope.errMsg).to.equal('yo mamma.');
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
scope.confirmPassphrase();
|
|
||||||
|
|
||||||
expect(scope.incorrect).to.be.true;
|
|
||||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
|
||||||
expect(emailMock.unlock.calledOnce).to.be.true;
|
|
||||||
expect(scope.errMsg).to.equal('yo mamma.');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not work when keypair retrieval', function() {
|
it('should not work when keypair retrieval', function(done) {
|
||||||
scope.passphrase = passphrase;
|
scope.passphrase = passphrase;
|
||||||
scope.key = {
|
scope.key = {
|
||||||
privateKeyArmored: 'b'
|
privateKeyArmored: 'b'
|
||||||
};
|
};
|
||||||
|
|
||||||
keychainMock.getUserKeyPair.withArgs(emailAddress).yields({
|
keychainMock.getUserKeyPair.withArgs(emailAddress).returns(rejects({
|
||||||
errMsg: 'yo mamma.'
|
errMsg: 'yo mamma.'
|
||||||
|
}));
|
||||||
|
|
||||||
|
scope.confirmPassphrase().then(function() {
|
||||||
|
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||||
|
expect(scope.errMsg).to.equal('yo mamma.');
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
scope.confirmPassphrase();
|
|
||||||
|
|
||||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
|
||||||
expect(scope.errMsg).to.equal('yo mamma.');
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
|
@ -29,6 +29,7 @@ describe('Login Private Key Download Controller unit test', function() {
|
||||||
$location: location,
|
$location: location,
|
||||||
$scope: scope,
|
$scope: scope,
|
||||||
$routeParams: {},
|
$routeParams: {},
|
||||||
|
$q: window.qMock,
|
||||||
auth: authMock,
|
auth: authMock,
|
||||||
email: emailDaoMock,
|
email: emailDaoMock,
|
||||||
keychain: keychainMock
|
keychain: keychainMock
|
||||||
|
@ -46,74 +47,57 @@ describe('Login Private Key Download Controller unit test', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('checkToken', function() {
|
describe('checkToken', function() {
|
||||||
var verifyRecoveryTokenStub;
|
|
||||||
|
|
||||||
beforeEach(function() {
|
|
||||||
verifyRecoveryTokenStub = sinon.stub(scope, 'verifyRecoveryToken');
|
|
||||||
});
|
|
||||||
afterEach(function() {
|
|
||||||
verifyRecoveryTokenStub.restore();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should fail for empty recovery token', function() {
|
|
||||||
scope.tokenForm.$invalid = true;
|
|
||||||
|
|
||||||
scope.checkToken();
|
|
||||||
|
|
||||||
expect(verifyRecoveryTokenStub.calledOnce).to.be.false;
|
|
||||||
expect(scope.errMsg).to.exist;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should work', function() {
|
|
||||||
verifyRecoveryTokenStub.yields();
|
|
||||||
|
|
||||||
scope.checkToken();
|
|
||||||
|
|
||||||
expect(verifyRecoveryTokenStub.calledOnce).to.be.true;
|
|
||||||
expect(scope.step).to.equal(2);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('verifyRecoveryToken', function() {
|
|
||||||
var testKeypair = {
|
var testKeypair = {
|
||||||
publicKey: {
|
publicKey: {
|
||||||
_id: 'id'
|
_id: 'id'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
it('should fail in keychain.getUserKeyPair', function() {
|
it('should fail for empty recovery token', function() {
|
||||||
keychainMock.getUserKeyPair.yields(new Error('asdf'));
|
scope.tokenForm.$invalid = true;
|
||||||
|
|
||||||
scope.verifyRecoveryToken();
|
scope.checkToken();
|
||||||
|
|
||||||
|
expect(keychainMock.getUserKeyPair.calledOnce).to.be.false;
|
||||||
expect(scope.errMsg).to.exist;
|
expect(scope.errMsg).to.exist;
|
||||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail in keychain.downloadPrivateKey', function() {
|
it('should fail in keychain.getUserKeyPair', function(done) {
|
||||||
keychainMock.getUserKeyPair.yields(null, testKeypair);
|
keychainMock.getUserKeyPair.returns(rejects(new Error('asdf')));
|
||||||
keychainMock.downloadPrivateKey.yields(new Error('asdf'));
|
|
||||||
scope.recoveryToken = 'token';
|
|
||||||
|
|
||||||
scope.verifyRecoveryToken();
|
scope.checkToken().then(function() {
|
||||||
|
expect(scope.errMsg).to.exist;
|
||||||
expect(scope.errMsg).to.exist;
|
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
done();
|
||||||
expect(keychainMock.downloadPrivateKey.calledOnce).to.be.true;
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work', function() {
|
it('should fail in keychain.downloadPrivateKey', function(done) {
|
||||||
keychainMock.getUserKeyPair.yields(null, testKeypair);
|
keychainMock.getUserKeyPair.returns(resolves(testKeypair));
|
||||||
keychainMock.downloadPrivateKey.yields(null, 'encryptedPrivateKey');
|
keychainMock.downloadPrivateKey.returns(rejects(new Error('asdf')));
|
||||||
scope.recoveryToken = 'token';
|
scope.recoveryToken = 'token';
|
||||||
|
|
||||||
scope.verifyRecoveryToken(function() {});
|
scope.checkToken().then(function() {
|
||||||
|
expect(scope.errMsg).to.exist;
|
||||||
|
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||||
|
expect(keychainMock.downloadPrivateKey.calledOnce).to.be.true;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
expect(scope.encryptedPrivateKey).to.equal('encryptedPrivateKey');
|
it('should work', function(done) {
|
||||||
|
keychainMock.getUserKeyPair.returns(resolves(testKeypair));
|
||||||
|
keychainMock.downloadPrivateKey.returns(resolves('encryptedPrivateKey'));
|
||||||
|
scope.recoveryToken = 'token';
|
||||||
|
|
||||||
|
scope.checkToken().then(function() {
|
||||||
|
expect(scope.encryptedPrivateKey).to.equal('encryptedPrivateKey');
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('decryptAndStorePrivateKeyLocally', function() {
|
describe('checkCode', function() {
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
scope.code = '012345';
|
scope.code = '012345';
|
||||||
|
|
||||||
|
@ -125,48 +109,50 @@ describe('Login Private Key Download Controller unit test', function() {
|
||||||
_id: 'keyId'
|
_id: 'keyId'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
sinon.stub(scope, 'goTo');
|
||||||
|
});
|
||||||
|
afterEach(function() {
|
||||||
|
scope.goTo.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail on decryptAndStorePrivateKeyLocally', function() {
|
it('should fail on decryptAndStorePrivateKeyLocally', function(done) {
|
||||||
keychainMock.decryptAndStorePrivateKeyLocally.yields(new Error('asdf'));
|
keychainMock.decryptAndStorePrivateKeyLocally.returns(rejects(new Error('asdf')));
|
||||||
|
|
||||||
scope.decryptAndStorePrivateKeyLocally();
|
scope.checkCode().then(function() {
|
||||||
|
expect(scope.errMsg).to.exist;
|
||||||
expect(scope.errMsg).to.exist;
|
expect(keychainMock.decryptAndStorePrivateKeyLocally.calledOnce).to.be.true;
|
||||||
expect(keychainMock.decryptAndStorePrivateKeyLocally.calledOnce).to.be.true;
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should goto /login-existing on emailDao.unlock fail', function(done) {
|
it('should goto /login-existing on emailDao.unlock fail', function(done) {
|
||||||
keychainMock.decryptAndStorePrivateKeyLocally.yields(null, {
|
keychainMock.decryptAndStorePrivateKeyLocally.returns(resolves({
|
||||||
encryptedKey: 'keyArmored'
|
encryptedKey: 'keyArmored'
|
||||||
});
|
}));
|
||||||
emailDaoMock.unlock.yields(new Error('asdf'));
|
emailDaoMock.unlock.returns(rejects(new Error('asdf')));
|
||||||
|
|
||||||
scope.goTo = function(location) {
|
scope.checkCode().then(function() {
|
||||||
expect(location).to.equal('/login-existing');
|
expect(scope.goTo.withArgs('/login-existing').calledOnce).to.be.true;
|
||||||
expect(keychainMock.decryptAndStorePrivateKeyLocally.calledOnce).to.be.true;
|
expect(keychainMock.decryptAndStorePrivateKeyLocally.calledOnce).to.be.true;
|
||||||
expect(emailDaoMock.unlock.calledOnce).to.be.true;
|
expect(emailDaoMock.unlock.calledOnce).to.be.true;
|
||||||
done();
|
done();
|
||||||
};
|
});
|
||||||
|
|
||||||
scope.decryptAndStorePrivateKeyLocally();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should goto /account on emailDao.unlock success', function(done) {
|
it('should goto /account on emailDao.unlock success', function(done) {
|
||||||
keychainMock.decryptAndStorePrivateKeyLocally.yields(null, {
|
keychainMock.decryptAndStorePrivateKeyLocally.returns(resolves({
|
||||||
encryptedKey: 'keyArmored'
|
encryptedKey: 'keyArmored'
|
||||||
});
|
}));
|
||||||
emailDaoMock.unlock.yields();
|
emailDaoMock.unlock.returns(resolves());
|
||||||
authMock.storeCredentials.yields();
|
authMock.storeCredentials.returns(resolves());
|
||||||
|
|
||||||
scope.goTo = function(location) {
|
scope.checkCode().then(function() {
|
||||||
expect(location).to.equal('/account');
|
expect(scope.goTo.withArgs('/account').calledOnce).to.be.true;
|
||||||
expect(keychainMock.decryptAndStorePrivateKeyLocally.calledOnce).to.be.true;
|
expect(keychainMock.decryptAndStorePrivateKeyLocally.calledOnce).to.be.true;
|
||||||
expect(emailDaoMock.unlock.calledOnce).to.be.true;
|
expect(emailDaoMock.unlock.calledOnce).to.be.true;
|
||||||
done();
|
done();
|
||||||
};
|
});
|
||||||
|
|
||||||
scope.decryptAndStorePrivateKeyLocally();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,7 @@ describe('Login (Set Credentials) Controller unit test', function() {
|
||||||
setCredentialsCtrl = $controller(SetCredentialsCtrl, {
|
setCredentialsCtrl = $controller(SetCredentialsCtrl, {
|
||||||
$scope: scope,
|
$scope: scope,
|
||||||
$routeParams: {},
|
$routeParams: {},
|
||||||
|
$q: window.qMock,
|
||||||
auth: auth,
|
auth: auth,
|
||||||
connectionDoctor: doctor
|
connectionDoctor: doctor
|
||||||
});
|
});
|
||||||
|
@ -49,7 +50,7 @@ describe('Login (Set Credentials) Controller unit test', function() {
|
||||||
afterEach(function() {});
|
afterEach(function() {});
|
||||||
|
|
||||||
describe('set credentials', function() {
|
describe('set credentials', function() {
|
||||||
it('should work', function() {
|
it('should work', function(done) {
|
||||||
scope.emailAddress = 'emailemailemailemail';
|
scope.emailAddress = 'emailemailemailemail';
|
||||||
scope.password = 'passwdpasswdpasswdpasswd';
|
scope.password = 'passwdpasswdpasswdpasswd';
|
||||||
scope.smtpHost = 'hosthosthost';
|
scope.smtpHost = 'hosthosthost';
|
||||||
|
@ -80,14 +81,16 @@ describe('Login (Set Credentials) Controller unit test', function() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
doctor.check.yields(); // synchronous yields!
|
doctor.check.returns(resolves()); // synchronous yields!
|
||||||
|
|
||||||
scope.test();
|
scope.test().then(function() {
|
||||||
|
expect(doctor.check.calledOnce).to.be.true;
|
||||||
|
expect(doctor.configure.calledOnce).to.be.true;
|
||||||
|
expect(doctor.configure.calledWith(expectedCredentials)).to.be.true;
|
||||||
|
expect(auth.setCredentials.calledOnce).to.be.true;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
expect(doctor.check.calledOnce).to.be.true;
|
|
||||||
expect(doctor.configure.calledOnce).to.be.true;
|
|
||||||
expect(doctor.configure.calledWith(expectedCredentials)).to.be.true;
|
|
||||||
expect(auth.setCredentials.calledOnce).to.be.true;
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
|
@ -34,6 +34,7 @@ describe('Validate Phone Controller unit test', function() {
|
||||||
$location: location,
|
$location: location,
|
||||||
$scope: scope,
|
$scope: scope,
|
||||||
$routeParams: {},
|
$routeParams: {},
|
||||||
|
$q: window.qMock,
|
||||||
mailConfig: mailConfigMock,
|
mailConfig: mailConfigMock,
|
||||||
auth: authStub,
|
auth: authStub,
|
||||||
admin: adminStub
|
admin: adminStub
|
||||||
|
@ -59,55 +60,53 @@ describe('Validate Phone Controller unit test', function() {
|
||||||
it('should fail to error creating user', function(done) {
|
it('should fail to error creating user', function(done) {
|
||||||
scope.form.$invalid = false;
|
scope.form.$invalid = false;
|
||||||
scope.token = 'asfd';
|
scope.token = 'asfd';
|
||||||
adminStub.validateUser.yieldsAsync(new Error('asdf'));
|
adminStub.validateUser.returns(rejects(new Error('asdf')));
|
||||||
|
|
||||||
scope.$apply = function() {
|
scope.validateUser().then(function() {
|
||||||
expect(scope.busy).to.be.false;
|
expect(scope.busy).to.be.false;
|
||||||
expect(scope.errMsg).to.equal('asdf');
|
expect(scope.errMsg).to.equal('asdf');
|
||||||
expect(adminStub.validateUser.calledOnce).to.be.true;
|
expect(adminStub.validateUser.calledOnce).to.be.true;
|
||||||
done();
|
done();
|
||||||
};
|
});
|
||||||
|
|
||||||
scope.validateUser();
|
|
||||||
expect(scope.busy).to.be.true;
|
expect(scope.busy).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work', function(done) {
|
it('should work', function(done) {
|
||||||
scope.form.$invalid = false;
|
scope.form.$invalid = false;
|
||||||
scope.token = 'asfd';
|
scope.token = 'asfd';
|
||||||
adminStub.validateUser.yieldsAsync();
|
adminStub.validateUser.returns(resolves());
|
||||||
|
|
||||||
scope.login = function() {
|
scope.login = function() {};
|
||||||
|
|
||||||
|
scope.validateUser().then(function() {
|
||||||
expect(scope.busy).to.be.true;
|
expect(scope.busy).to.be.true;
|
||||||
expect(scope.errMsg).to.be.undefined;
|
expect(scope.errMsg).to.be.undefined;
|
||||||
expect(adminStub.validateUser.calledOnce).to.be.true;
|
expect(adminStub.validateUser.calledOnce).to.be.true;
|
||||||
done();
|
done();
|
||||||
};
|
});
|
||||||
|
|
||||||
scope.validateUser();
|
|
||||||
expect(scope.busy).to.be.true;
|
expect(scope.busy).to.be.true;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('login', function() {
|
describe('login', function() {
|
||||||
it('should work', inject(function($q, $rootScope) {
|
it('should work', function(done) {
|
||||||
scope.form.$invalid = false;
|
scope.form.$invalid = false;
|
||||||
authStub.setCredentials.returns();
|
authStub.setCredentials.returns();
|
||||||
|
|
||||||
var deferred = $q.defer();
|
sinon.stub(mailConfigMock, 'get').returns(resolves({
|
||||||
sinon.stub(mailConfigMock, 'get').returns(deferred.promise);
|
|
||||||
deferred.resolve({
|
|
||||||
imap: {},
|
imap: {},
|
||||||
smtp: {}
|
smtp: {}
|
||||||
|
}));
|
||||||
|
|
||||||
|
scope.login().then(function() {
|
||||||
|
expect(mailConfigMock.get.calledOnce).to.be.true;
|
||||||
|
expect(authStub.setCredentials.calledOnce).to.be.true;
|
||||||
|
expect(location.path.calledWith('/login')).to.be.true;
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
|
});
|
||||||
scope.login();
|
|
||||||
|
|
||||||
$rootScope.$apply();
|
|
||||||
expect(mailConfigMock.get.calledOnce).to.be.true;
|
|
||||||
expect(authStub.setCredentials.calledOnce).to.be.true;
|
|
||||||
expect(location.path.calledWith('/login')).to.be.true;
|
|
||||||
}));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
|
@ -24,16 +24,14 @@ describe('Crypto unit tests', function() {
|
||||||
var key = util.random(keySize);
|
var key = util.random(keySize);
|
||||||
var iv = util.random(ivSize);
|
var iv = util.random(ivSize);
|
||||||
|
|
||||||
crypto.encrypt(plaintext, key, iv, function(err, ciphertext) {
|
crypto.encrypt(plaintext, key, iv).then(function(ciphertext) {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(ciphertext).to.exist;
|
expect(ciphertext).to.exist;
|
||||||
|
|
||||||
crypto.decrypt(ciphertext, key, iv, function(err, decrypted) {
|
return crypto.decrypt(ciphertext, key, iv);
|
||||||
expect(err).to.not.exist;
|
}).then(function(decrypted) {
|
||||||
expect(decrypted).to.equal(plaintext);
|
expect(decrypted).to.equal(plaintext);
|
||||||
|
|
||||||
done();
|
done();
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -42,14 +40,11 @@ describe('Crypto unit tests', function() {
|
||||||
it('should work', function(done) {
|
it('should work', function(done) {
|
||||||
var salt = util.random(keySize);
|
var salt = util.random(keySize);
|
||||||
|
|
||||||
crypto.deriveKey(password, salt, keySize, function(err, key) {
|
crypto.deriveKey(password, salt, keySize).then(function(key) {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(util.base642Str(key).length * 8).to.equal(keySize);
|
expect(util.base642Str(key).length * 8).to.equal(keySize);
|
||||||
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
|
@ -50,9 +50,8 @@ describe('PGP Crypto Api unit tests', function() {
|
||||||
emailAddress: 'whiteout.test@t-onlinede',
|
emailAddress: 'whiteout.test@t-onlinede',
|
||||||
keySize: keySize,
|
keySize: keySize,
|
||||||
passphrase: passphrase
|
passphrase: passphrase
|
||||||
}, function(err, keys) {
|
}).catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err.message).to.match(/options/);
|
||||||
expect(keys).to.not.exist;
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -61,76 +60,69 @@ describe('PGP Crypto Api unit tests', function() {
|
||||||
emailAddress: 'whiteout.testt-online.de',
|
emailAddress: 'whiteout.testt-online.de',
|
||||||
keySize: keySize,
|
keySize: keySize,
|
||||||
passphrase: passphrase
|
passphrase: passphrase
|
||||||
}, function(err, keys) {
|
}).catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err.message).to.match(/options/);
|
||||||
expect(keys).to.not.exist;
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('should work with passphrase', function(done) {
|
it('should work with passphrase', function(done) {
|
||||||
|
var keyObject;
|
||||||
|
|
||||||
pgp.generateKeys({
|
pgp.generateKeys({
|
||||||
emailAddress: user,
|
emailAddress: user,
|
||||||
keySize: keySize,
|
keySize: keySize,
|
||||||
passphrase: passphrase
|
passphrase: passphrase
|
||||||
}, function(err, keys) {
|
}).then(function(keys) {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(keys.keyId).to.exist;
|
expect(keys.keyId).to.exist;
|
||||||
expect(keys.privateKeyArmored).to.exist;
|
expect(keys.privateKeyArmored).to.exist;
|
||||||
expect(keys.publicKeyArmored).to.exist;
|
expect(keys.publicKeyArmored).to.exist;
|
||||||
|
keyObject = keys;
|
||||||
|
|
||||||
// test encrypt/decrypt
|
// test encrypt/decrypt
|
||||||
pgp.importKeys({
|
return pgp.importKeys({
|
||||||
passphrase: passphrase,
|
passphrase: passphrase,
|
||||||
privateKeyArmored: keys.privateKeyArmored,
|
privateKeyArmored: keys.privateKeyArmored,
|
||||||
publicKeyArmored: keys.publicKeyArmored
|
publicKeyArmored: keys.publicKeyArmored
|
||||||
}, function(err) {
|
|
||||||
expect(err).to.not.exist;
|
|
||||||
|
|
||||||
pgp.encrypt('secret', [keys.publicKeyArmored], function(err, ct) {
|
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(ct).to.exist;
|
|
||||||
|
|
||||||
pgp.decrypt(ct, keys.publicKeyArmored, function(err, pt, signValid) {
|
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(pt).to.equal('secret');
|
|
||||||
expect(signValid).to.be.true;
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
}).then(function() {
|
||||||
|
return pgp.encrypt('secret', [keyObject.publicKeyArmored]);
|
||||||
|
}).then(function(ct) {
|
||||||
|
expect(ct).to.exist;
|
||||||
|
return pgp.decrypt(ct, keyObject.publicKeyArmored);
|
||||||
|
}).then(function(pt) {
|
||||||
|
expect(pt.decrypted).to.equal('secret');
|
||||||
|
expect(pt.signaturesValid).to.be.true;
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('should work without passphrase', function(done) {
|
it('should work without passphrase', function(done) {
|
||||||
|
var keyObject;
|
||||||
|
|
||||||
pgp.generateKeys({
|
pgp.generateKeys({
|
||||||
emailAddress: user,
|
emailAddress: user,
|
||||||
keySize: keySize,
|
keySize: keySize,
|
||||||
passphrase: ''
|
passphrase: ''
|
||||||
}, function(err, keys) {
|
}).then(function(keys) {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(keys.keyId).to.exist;
|
expect(keys.keyId).to.exist;
|
||||||
expect(keys.privateKeyArmored).to.exist;
|
expect(keys.privateKeyArmored).to.exist;
|
||||||
expect(keys.publicKeyArmored).to.exist;
|
expect(keys.publicKeyArmored).to.exist;
|
||||||
|
keyObject = keys;
|
||||||
|
|
||||||
// test encrypt/decrypt
|
// test encrypt/decrypt
|
||||||
pgp.importKeys({
|
return pgp.importKeys({
|
||||||
passphrase: undefined,
|
passphrase: passphrase,
|
||||||
privateKeyArmored: keys.privateKeyArmored,
|
privateKeyArmored: keys.privateKeyArmored,
|
||||||
publicKeyArmored: keys.publicKeyArmored
|
publicKeyArmored: keys.publicKeyArmored
|
||||||
}, function(err) {
|
|
||||||
expect(err).to.not.exist;
|
|
||||||
|
|
||||||
pgp.encrypt('secret', [keys.publicKeyArmored], function(err, ct) {
|
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(ct).to.exist;
|
|
||||||
|
|
||||||
pgp.decrypt(ct, keys.publicKeyArmored, function(err, pt, signValid) {
|
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(pt).to.equal('secret');
|
|
||||||
expect(signValid).to.be.true;
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
}).then(function() {
|
||||||
|
return pgp.encrypt('secret', [keyObject.publicKeyArmored]);
|
||||||
|
}).then(function(ct) {
|
||||||
|
expect(ct).to.exist;
|
||||||
|
return pgp.decrypt(ct, keyObject.publicKeyArmored);
|
||||||
|
}).then(function(pt) {
|
||||||
|
expect(pt.decrypted).to.equal('secret');
|
||||||
|
expect(pt.signaturesValid).to.be.true;
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -141,15 +133,13 @@ describe('PGP Crypto Api unit tests', function() {
|
||||||
passphrase: 'asd',
|
passphrase: 'asd',
|
||||||
privateKeyArmored: privkey,
|
privateKeyArmored: privkey,
|
||||||
publicKeyArmored: pubkey
|
publicKeyArmored: pubkey
|
||||||
}, function(err) {
|
}).catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
expect(err.message).to.equal('Incorrect passphrase!');
|
expect(err.message).to.equal('Incorrect passphrase!');
|
||||||
|
return pgp.exportKeys();
|
||||||
pgp.exportKeys(function(err, keys) {
|
}).catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
expect(keys).to.not.exist;
|
done();
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('should work', function(done) {
|
it('should work', function(done) {
|
||||||
|
@ -157,16 +147,13 @@ describe('PGP Crypto Api unit tests', function() {
|
||||||
passphrase: passphrase,
|
passphrase: passphrase,
|
||||||
privateKeyArmored: privkey,
|
privateKeyArmored: privkey,
|
||||||
publicKeyArmored: pubkey
|
publicKeyArmored: pubkey
|
||||||
}, function(err) {
|
}).then(function() {
|
||||||
expect(err).to.not.exist;
|
return pgp.exportKeys();
|
||||||
|
}).then(function(keys) {
|
||||||
pgp.exportKeys(function(err, keys) {
|
expect(keys.keyId).to.equal(keyId);
|
||||||
expect(err).to.not.exist;
|
expect(keys.privateKeyArmored.replace(/\r/g, '')).to.equal(privkey.replace(/\r/g, ''));
|
||||||
expect(keys.keyId).to.equal(keyId);
|
expect(keys.publicKeyArmored.replace(/\r/g, '')).to.equal(pubkey.replace(/\r/g, ''));
|
||||||
expect(keys.privateKeyArmored.replace(/\r/g, '')).to.equal(privkey.replace(/\r/g, ''));
|
done();
|
||||||
expect(keys.publicKeyArmored.replace(/\r/g, '')).to.equal(pubkey.replace(/\r/g, ''));
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -177,47 +164,38 @@ describe('PGP Crypto Api unit tests', function() {
|
||||||
privateKeyArmored: privkey,
|
privateKeyArmored: privkey,
|
||||||
oldPassphrase: passphrase,
|
oldPassphrase: passphrase,
|
||||||
newPassphrase: 'yxcv'
|
newPassphrase: 'yxcv'
|
||||||
}, function(err, reEncryptedKey) {
|
}).then(function(reEncryptedKey) {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(reEncryptedKey).to.exist;
|
expect(reEncryptedKey).to.exist;
|
||||||
|
|
||||||
pgp.importKeys({
|
return pgp.importKeys({
|
||||||
passphrase: 'yxcv',
|
passphrase: 'yxcv',
|
||||||
privateKeyArmored: reEncryptedKey,
|
privateKeyArmored: reEncryptedKey,
|
||||||
publicKeyArmored: pubkey
|
publicKeyArmored: pubkey
|
||||||
}, function(err) {
|
|
||||||
expect(err).to.not.exist;
|
|
||||||
done();
|
|
||||||
});
|
});
|
||||||
});
|
}).then(done);
|
||||||
});
|
});
|
||||||
it('should work with empty passphrase', function(done) {
|
it('should work with empty passphrase', function(done) {
|
||||||
pgp.changePassphrase({
|
pgp.changePassphrase({
|
||||||
privateKeyArmored: privkey,
|
privateKeyArmored: privkey,
|
||||||
oldPassphrase: passphrase,
|
oldPassphrase: passphrase,
|
||||||
newPassphrase: undefined
|
newPassphrase: undefined
|
||||||
}, function(err, reEncryptedKey) {
|
}).then(function(reEncryptedKey) {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(reEncryptedKey).to.exist;
|
expect(reEncryptedKey).to.exist;
|
||||||
|
|
||||||
pgp.importKeys({
|
return pgp.importKeys({
|
||||||
passphrase: undefined,
|
passphrase: undefined,
|
||||||
privateKeyArmored: reEncryptedKey,
|
privateKeyArmored: reEncryptedKey,
|
||||||
publicKeyArmored: pubkey
|
publicKeyArmored: pubkey
|
||||||
}, function(err) {
|
|
||||||
expect(err).to.not.exist;
|
|
||||||
done();
|
|
||||||
});
|
});
|
||||||
});
|
}).then(done);
|
||||||
});
|
});
|
||||||
it('should fail when passphrases are equal', function(done) {
|
it('should fail when passphrases are equal', function(done) {
|
||||||
pgp.changePassphrase({
|
pgp.changePassphrase({
|
||||||
privateKeyArmored: privkey,
|
privateKeyArmored: privkey,
|
||||||
oldPassphrase: passphrase,
|
oldPassphrase: passphrase,
|
||||||
newPassphrase: passphrase
|
newPassphrase: passphrase
|
||||||
}, function(err, reEncryptedKey) {
|
}).catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
expect(reEncryptedKey).to.not.exist;
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -226,9 +204,8 @@ describe('PGP Crypto Api unit tests', function() {
|
||||||
privateKeyArmored: privkey,
|
privateKeyArmored: privkey,
|
||||||
oldPassphrase: 'asd',
|
oldPassphrase: 'asd',
|
||||||
newPassphrase: 'yxcv'
|
newPassphrase: 'yxcv'
|
||||||
}, function(err, reEncryptedKey) {
|
}).catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
expect(reEncryptedKey).to.not.exist;
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -247,10 +224,7 @@ describe('PGP Crypto Api unit tests', function() {
|
||||||
passphrase: passphrase,
|
passphrase: passphrase,
|
||||||
privateKeyArmored: privkey,
|
privateKeyArmored: privkey,
|
||||||
publicKeyArmored: pubkey
|
publicKeyArmored: pubkey
|
||||||
}, function(err) {
|
}).then(done);
|
||||||
expect(err).to.not.exist;
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Get KeyId', function() {
|
describe('Get KeyId', function() {
|
||||||
|
@ -311,22 +285,19 @@ describe('PGP Crypto Api unit tests', function() {
|
||||||
it('should fail', function(done) {
|
it('should fail', function(done) {
|
||||||
var input = null;
|
var input = null;
|
||||||
|
|
||||||
pgp.encrypt(input, [pubkey], function(err, ct) {
|
pgp.encrypt(input, [pubkey]).catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
expect(ct).to.not.exist;
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('should work', function(done) {
|
it('should work', function(done) {
|
||||||
pgp.encrypt(message, [pubkey], function(err, ct) {
|
pgp.encrypt(message, [pubkey]).then(function(ct) {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(ct).to.exist;
|
expect(ct).to.exist;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('should encrypt to myself if public keys are empty', function(done) {
|
it('should encrypt to myself if public keys are empty', function(done) {
|
||||||
pgp.encrypt(message, undefined, function(err, ct) {
|
pgp.encrypt(message, undefined).then(function(ct) {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(ct).to.exist;
|
expect(ct).to.exist;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -337,8 +308,7 @@ describe('PGP Crypto Api unit tests', function() {
|
||||||
var ciphertext;
|
var ciphertext;
|
||||||
|
|
||||||
beforeEach(function(done) {
|
beforeEach(function(done) {
|
||||||
pgp.encrypt(message, [pubkey], function(err, ct) {
|
pgp.encrypt(message, [pubkey]).then(function(ct) {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(ct).to.exist;
|
expect(ct).to.exist;
|
||||||
ciphertext = ct;
|
ciphertext = ct;
|
||||||
done();
|
done();
|
||||||
|
@ -348,45 +318,40 @@ describe('PGP Crypto Api unit tests', function() {
|
||||||
it('should fail', function(done) {
|
it('should fail', function(done) {
|
||||||
var input = 'asdfa\rsdf';
|
var input = 'asdfa\rsdf';
|
||||||
|
|
||||||
pgp.decrypt(input, pubkey, function(err, pt) {
|
pgp.decrypt(input, pubkey).catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
expect(pt).to.not.exist;
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('should work', function(done) {
|
it('should work', function(done) {
|
||||||
pgp.decrypt(ciphertext, pubkey, function(err, pt, signValid) {
|
pgp.decrypt(ciphertext, pubkey).then(function(pt) {
|
||||||
expect(err).to.not.exist;
|
expect(pt.decrypted).to.equal(message);
|
||||||
expect(pt).to.equal(message);
|
expect(pt.signaturesValid).to.be.true;
|
||||||
expect(signValid).to.be.true;
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('should work without signature', function(done) {
|
it('should work without signature', function(done) {
|
||||||
openpgp.encryptMessage([pgp._publicKey], message).then(function(ct) {
|
openpgp.encryptMessage([pgp._publicKey], message).then(function(ct) {
|
||||||
pgp.decrypt(ct, undefined, function(err, pt, signValid) {
|
return pgp.decrypt(ct, undefined);
|
||||||
expect(err).to.not.exist;
|
}).then(function(pt) {
|
||||||
expect(pt).to.equal(message);
|
expect(pt.decrypted).to.equal(message);
|
||||||
expect(signValid).to.be.undefined;
|
expect(pt.signaturesValid).to.be.undefined;
|
||||||
done();
|
done();
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('should fail to verify if public keys are empty', function(done) {
|
it('should fail to verify if public keys are empty', function(done) {
|
||||||
// setup another public key so that signature verification fails
|
// setup another public key so that signature verification fails
|
||||||
pgp._publicKey = openpgp.key.readArmored(wrongPubkey).keys[0];
|
pgp._publicKey = openpgp.key.readArmored(wrongPubkey).keys[0];
|
||||||
pgp.decrypt(ciphertext, undefined, function(err, pt, signValid) {
|
pgp.decrypt(ciphertext, undefined).then(function(pt) {
|
||||||
expect(err).to.not.exist;
|
expect(pt.decrypted).to.equal(message);
|
||||||
expect(pt).to.equal(message);
|
expect(pt.signaturesValid).to.be.null;
|
||||||
expect(signValid).to.be.null;
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('should decrypt but signValid should be null for wrong public key', function(done) {
|
it('should decrypt but signValid should be null for wrong public key', function(done) {
|
||||||
pgp.decrypt(ciphertext, wrongPubkey, function(err, pt, signValid) {
|
pgp.decrypt(ciphertext, wrongPubkey).then(function(pt) {
|
||||||
expect(err).to.not.exist;
|
expect(pt.decrypted).to.equal(message);
|
||||||
expect(pt).to.equal(message);
|
expect(pt.signaturesValid).to.be.null;
|
||||||
expect(signValid).to.be.null;
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -403,23 +368,20 @@ describe('PGP Crypto Api unit tests', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work', function(done) {
|
it('should work', function(done) {
|
||||||
pgp.verifyClearSignedMessage(clearsigned, pubkey, function(err, signaturesValid) {
|
pgp.verifyClearSignedMessage(clearsigned, pubkey).then(function(signaturesValid) {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(signaturesValid).to.be.true;
|
expect(signaturesValid).to.be.true;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail', function(done) {
|
it('should fail', function(done) {
|
||||||
pgp.verifyClearSignedMessage(clearsigned.replace('clearsigned', 'invalid'), pubkey, function(err, signaturesValid) {
|
pgp.verifyClearSignedMessage(clearsigned.replace('clearsigned', 'invalid'), pubkey).then(function(signaturesValid) {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(signaturesValid).to.be.false;
|
expect(signaturesValid).to.be.false;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('should be null for wrong public key', function(done) {
|
it('should be null for wrong public key', function(done) {
|
||||||
pgp.verifyClearSignedMessage(clearsigned, wrongPubkey, function(err, signaturesValid) {
|
pgp.verifyClearSignedMessage(clearsigned, wrongPubkey).then(function(signaturesValid) {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(signaturesValid).to.be.null;
|
expect(signaturesValid).to.be.null;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -439,23 +401,20 @@ describe('PGP Crypto Api unit tests', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work', function(done) {
|
it('should work', function(done) {
|
||||||
pgp.verifySignedMessage(signedMessage, signature, pubkey, function(err, signaturesValid) {
|
pgp.verifySignedMessage(signedMessage, signature, pubkey).then(function(signaturesValid) {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(signaturesValid).to.be.true;
|
expect(signaturesValid).to.be.true;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail', function(done) {
|
it('should fail', function(done) {
|
||||||
pgp.verifySignedMessage(signedMessage.replace('signed', 'invalid'), signature, pubkey, function(err, signaturesValid) {
|
pgp.verifySignedMessage(signedMessage.replace('signed', 'invalid'), signature, pubkey).then(function(signaturesValid) {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(signaturesValid).to.be.false;
|
expect(signaturesValid).to.be.false;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('should be null for wrong public key', function(done) {
|
it('should be null for wrong public key', function(done) {
|
||||||
pgp.verifySignedMessage(signedMessage, signature, wrongPubkey, function(err, signaturesValid) {
|
pgp.verifySignedMessage(signedMessage, signature, wrongPubkey).then(function(signaturesValid) {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(signaturesValid).to.be.null;
|
expect(signaturesValid).to.be.null;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
|
@ -58,55 +58,45 @@ describe('Account Service unit test', function() {
|
||||||
account.init({
|
account.init({
|
||||||
emailAddress: dummyUser.replace('@'),
|
emailAddress: dummyUser.replace('@'),
|
||||||
realname: realname
|
realname: realname
|
||||||
}, onInit);
|
}).catch(function onInit(err) {
|
||||||
|
|
||||||
function onInit(err, keys) {
|
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
expect(keys).to.not.exist;
|
});
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail for _accountStore.init', function() {
|
it('should fail for _accountStore.init', function() {
|
||||||
devicestorageStub.init.yields(new Error('asdf'));
|
devicestorageStub.init.returns(rejects(new Error('asdf')));
|
||||||
|
|
||||||
account.init({
|
account.init({
|
||||||
emailAddress: dummyUser,
|
emailAddress: dummyUser,
|
||||||
realname: realname
|
realname: realname
|
||||||
}, onInit);
|
}).catch(function onInit(err) {
|
||||||
|
|
||||||
function onInit(err, keys) {
|
|
||||||
expect(err.message).to.match(/asdf/);
|
expect(err.message).to.match(/asdf/);
|
||||||
expect(keys).to.not.exist;
|
});
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail for _updateHandler.update', function() {
|
it('should fail for _updateHandler.update', function() {
|
||||||
updateHandlerStub.update.yields(new Error('asdf'));
|
devicestorageStub.init.returns(resolves());
|
||||||
|
updateHandlerStub.update.returns(rejects(new Error('asdf')));
|
||||||
|
|
||||||
account.init({
|
account.init({
|
||||||
emailAddress: dummyUser,
|
emailAddress: dummyUser,
|
||||||
realname: realname
|
realname: realname
|
||||||
}, onInit);
|
}).catch(function onInit(err) {
|
||||||
|
|
||||||
function onInit(err, keys) {
|
|
||||||
expect(err.message).to.match(/Updating/);
|
expect(err.message).to.match(/Updating/);
|
||||||
expect(keys).to.not.exist;
|
});
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail for _keychain.getUserKeyPair', function() {
|
it('should fail for _keychain.getUserKeyPair', function() {
|
||||||
updateHandlerStub.update.yields();
|
devicestorageStub.init.returns(resolves());
|
||||||
keychainStub.getUserKeyPair.yields(new Error('asdf'));
|
updateHandlerStub.update.returns(resolves());
|
||||||
|
keychainStub.getUserKeyPair.returns(rejects(new Error('asdf')));
|
||||||
|
|
||||||
account.init({
|
account.init({
|
||||||
emailAddress: dummyUser,
|
emailAddress: dummyUser,
|
||||||
realname: realname
|
realname: realname
|
||||||
}, onInit);
|
}).catch(function(err) {
|
||||||
|
|
||||||
function onInit(err, keys) {
|
|
||||||
expect(err.message).to.match(/asdf/);
|
expect(err.message).to.match(/asdf/);
|
||||||
expect(keys).to.not.exist;
|
});
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail for _keychain.refreshKeyForUserId', function() {
|
it('should fail for _keychain.refreshKeyForUserId', function() {
|
||||||
|
@ -114,19 +104,17 @@ describe('Account Service unit test', function() {
|
||||||
publicKey: 'publicKey'
|
publicKey: 'publicKey'
|
||||||
};
|
};
|
||||||
|
|
||||||
updateHandlerStub.update.yields();
|
devicestorageStub.init.returns(resolves());
|
||||||
keychainStub.getUserKeyPair.yields(null, storedKeys);
|
updateHandlerStub.update.returns(resolves());
|
||||||
keychainStub.refreshKeyForUserId.yields(new Error('asdf'));
|
keychainStub.getUserKeyPair.returns(resolves(storedKeys));
|
||||||
|
keychainStub.refreshKeyForUserId.returns(rejects(new Error('asdf')));
|
||||||
|
|
||||||
account.init({
|
account.init({
|
||||||
emailAddress: dummyUser,
|
emailAddress: dummyUser,
|
||||||
realname: realname
|
realname: realname
|
||||||
}, onInit);
|
}).catch(function(err) {
|
||||||
|
|
||||||
function onInit(err, keys) {
|
|
||||||
expect(err.message).to.match(/asdf/);
|
expect(err.message).to.match(/asdf/);
|
||||||
expect(keys).to.not.exist;
|
});
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail for _emailDao.init after _keychain.refreshKeyForUserId', function() {
|
it('should fail for _emailDao.init after _keychain.refreshKeyForUserId', function() {
|
||||||
|
@ -134,20 +122,18 @@ describe('Account Service unit test', function() {
|
||||||
publicKey: 'publicKey'
|
publicKey: 'publicKey'
|
||||||
};
|
};
|
||||||
|
|
||||||
updateHandlerStub.update.yields();
|
devicestorageStub.init.returns(resolves());
|
||||||
keychainStub.getUserKeyPair.yields(null, storedKeys);
|
updateHandlerStub.update.returns(resolves());
|
||||||
keychainStub.refreshKeyForUserId.yields(null, storedKeys);
|
keychainStub.getUserKeyPair.returns(resolves(storedKeys));
|
||||||
emailStub.init.yields(new Error('asdf'));
|
keychainStub.refreshKeyForUserId.returns(resolves(storedKeys));
|
||||||
|
emailStub.init.returns(rejects(new Error('asdf')));
|
||||||
|
|
||||||
account.init({
|
account.init({
|
||||||
emailAddress: dummyUser,
|
emailAddress: dummyUser,
|
||||||
realname: realname
|
realname: realname
|
||||||
}, onInit);
|
}).catch(function(err) {
|
||||||
|
|
||||||
function onInit(err, keys) {
|
|
||||||
expect(err.message).to.match(/asdf/);
|
expect(err.message).to.match(/asdf/);
|
||||||
expect(keys).to.not.exist;
|
});
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail for _emailDao.init', function() {
|
it('should fail for _emailDao.init', function() {
|
||||||
|
@ -156,19 +142,17 @@ describe('Account Service unit test', function() {
|
||||||
privateKey: 'privateKey'
|
privateKey: 'privateKey'
|
||||||
};
|
};
|
||||||
|
|
||||||
updateHandlerStub.update.yields();
|
devicestorageStub.init.returns(resolves());
|
||||||
keychainStub.getUserKeyPair.yields(null, storedKeys);
|
updateHandlerStub.update.returns(resolves());
|
||||||
emailStub.init.yields(new Error('asdf'));
|
keychainStub.getUserKeyPair.returns(resolves(storedKeys));
|
||||||
|
emailStub.init.returns(rejects(new Error('asdf')));
|
||||||
|
|
||||||
account.init({
|
account.init({
|
||||||
emailAddress: dummyUser,
|
emailAddress: dummyUser,
|
||||||
realname: realname
|
realname: realname
|
||||||
}, onInit);
|
}).catch(function(err) {
|
||||||
|
|
||||||
function onInit(err, keys) {
|
|
||||||
expect(err.message).to.match(/asdf/);
|
expect(err.message).to.match(/asdf/);
|
||||||
expect(keys).to.not.exist;
|
});
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work after _keychain.refreshKeyForUserId', function() {
|
it('should work after _keychain.refreshKeyForUserId', function() {
|
||||||
|
@ -176,20 +160,20 @@ describe('Account Service unit test', function() {
|
||||||
publicKey: 'publicKey'
|
publicKey: 'publicKey'
|
||||||
};
|
};
|
||||||
|
|
||||||
updateHandlerStub.update.yields();
|
devicestorageStub.init.returns(resolves());
|
||||||
keychainStub.getUserKeyPair.yields(null, storedKeys);
|
updateHandlerStub.update.returns(resolves());
|
||||||
keychainStub.refreshKeyForUserId.yields(null, 'publicKey');
|
keychainStub.getUserKeyPair.returns(resolves(storedKeys));
|
||||||
emailStub.init.yields();
|
keychainStub.refreshKeyForUserId.returns(resolves('publicKey'));
|
||||||
|
emailStub.init.returns(resolves());
|
||||||
|
|
||||||
account.init({
|
account.init({
|
||||||
emailAddress: dummyUser,
|
emailAddress: dummyUser,
|
||||||
realname: realname
|
realname: realname
|
||||||
}, onInit);
|
}, function onInit(keys) {
|
||||||
|
|
||||||
function onInit(err, keys) {
|
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(keys).to.deep.equal(storedKeys);
|
expect(keys).to.deep.equal(storedKeys);
|
||||||
}
|
expect(keychainStub.refreshKeyForUserId.calledOnce).to.be.true;
|
||||||
|
expect(emailStub.init.calledOnce).to.be.true;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work', function() {
|
it('should work', function() {
|
||||||
|
@ -198,19 +182,20 @@ describe('Account Service unit test', function() {
|
||||||
privateKey: 'privateKey'
|
privateKey: 'privateKey'
|
||||||
};
|
};
|
||||||
|
|
||||||
updateHandlerStub.update.yields();
|
devicestorageStub.init.returns(resolves());
|
||||||
keychainStub.getUserKeyPair.yields(null, storedKeys);
|
updateHandlerStub.update.returns(resolves());
|
||||||
emailStub.init.yields();
|
keychainStub.getUserKeyPair.returns(resolves(storedKeys));
|
||||||
|
emailStub.init.returns(resolves());
|
||||||
|
|
||||||
account.init({
|
account.init({
|
||||||
emailAddress: dummyUser,
|
emailAddress: dummyUser,
|
||||||
realname: realname
|
realname: realname
|
||||||
}, onInit);
|
}, function onInit(keys) {
|
||||||
|
|
||||||
function onInit(err, keys) {
|
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(keys).to.equal(storedKeys);
|
expect(keys).to.equal(storedKeys);
|
||||||
}
|
expect(keychainStub.refreshKeyForUserId.called).to.be.false;
|
||||||
|
expect(emailStub.init.calledOnce).to.be.true;
|
||||||
|
expect(account._accounts.length).to.equal(1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -227,48 +212,66 @@ describe('Account Service unit test', function() {
|
||||||
account.isOnline.restore();
|
account.isOnline.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail due to _auth.getCredentials', function() {
|
it('should fail due to _auth.getCredentials', function(done) {
|
||||||
authStub.getCredentials.yields(new Error('asdf'));
|
authStub.getCredentials.returns(rejects(new Error('asdf')));
|
||||||
|
|
||||||
|
dialogStub.error = function(err) {
|
||||||
|
expect(err.message).to.match(/asdf/);
|
||||||
|
done();
|
||||||
|
};
|
||||||
|
|
||||||
account.onConnect();
|
account.onConnect();
|
||||||
|
|
||||||
expect(dialogStub.error.calledOnce).to.be.true;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work', function() {
|
it('should fail due to _auth.getCredentials', function(done) {
|
||||||
authStub.getCredentials.yields(null, credentials);
|
authStub.getCredentials.returns(rejects(new Error('asdf')));
|
||||||
emailStub.onConnect.yields();
|
|
||||||
|
|
||||||
account.onConnect();
|
account.onConnect(function(err) {
|
||||||
|
expect(err.message).to.match(/asdf/);
|
||||||
|
expect(dialogStub.error.called).to.be.false;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
expect(emailStub.onConnect.calledOnce).to.be.true;
|
it('should work', function(done) {
|
||||||
expect(dialogStub.error.calledOnce).to.be.true;
|
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();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('onDisconnect', function() {
|
describe('onDisconnect', function() {
|
||||||
it('should work', function() {
|
it('should work', function(done) {
|
||||||
account.onDisconnect();
|
emailStub.onDisconnect.returns(resolves());
|
||||||
expect(emailStub.onDisconnect.calledOnce).to.be.true;
|
account.onDisconnect().then(done);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('logout', function() {
|
describe('logout', function() {
|
||||||
it('should fail due to _auth.logout', function() {
|
it('should fail due to _auth.logout', function(done) {
|
||||||
authStub.logout.yields(new Error());
|
authStub.logout.returns(rejects(new Error('asdf')));
|
||||||
|
|
||||||
account.logout();
|
account.logout().catch(function(err) {
|
||||||
|
expect(err.message).to.match(/asdf/);
|
||||||
expect(dialogStub.error.calledOnce).to.be.true;
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail due to _emailDao.onDisconnect', function() {
|
it('should fail due to _emailDao.onDisconnect', function(done) {
|
||||||
authStub.logout.yields();
|
authStub.logout.returns(resolves());
|
||||||
emailStub.onDisconnect.yields(new Error());
|
emailStub.onDisconnect.returns(rejects(new Error('asdf')));
|
||||||
|
|
||||||
account.logout();
|
account.logout().catch(function(err) {
|
||||||
|
expect(err.message).to.match(/asdf/);
|
||||||
expect(dialogStub.error.calledOnce).to.be.true;
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -5,7 +5,7 @@ var OutboxBO = require('../../../src/js/email/outbox'),
|
||||||
EmailDAO = require('../../../src/js/email/email'),
|
EmailDAO = require('../../../src/js/email/email'),
|
||||||
DeviceStorageDAO = require('../../../src/js/service/devicestorage');
|
DeviceStorageDAO = require('../../../src/js/service/devicestorage');
|
||||||
|
|
||||||
describe('Outbox Business Object unit test', function() {
|
describe('Outbox unit test', function() {
|
||||||
var outbox, emailDaoStub, devicestorageStub, keychainStub,
|
var outbox, emailDaoStub, devicestorageStub, keychainStub,
|
||||||
dummyUser = 'spiderpig@springfield.com';
|
dummyUser = 'spiderpig@springfield.com';
|
||||||
|
|
||||||
|
@ -42,6 +42,13 @@ describe('Outbox Business Object unit test', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('put', function() {
|
describe('put', function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
sinon.stub(outbox, '_processOutbox');
|
||||||
|
});
|
||||||
|
afterEach(function() {
|
||||||
|
outbox._processOutbox.restore();
|
||||||
|
});
|
||||||
|
|
||||||
it('should not encrypt and store a mail', function(done) {
|
it('should not encrypt and store a mail', function(done) {
|
||||||
var mail, senderKey, receiverKey;
|
var mail, senderKey, receiverKey;
|
||||||
|
|
||||||
|
@ -67,15 +74,13 @@ describe('Outbox Business Object unit test', function() {
|
||||||
bcc: []
|
bcc: []
|
||||||
};
|
};
|
||||||
|
|
||||||
keychainStub.getReceiverPublicKey.withArgs(mail.from[0].address).yieldsAsync(null, senderKey);
|
keychainStub.getReceiverPublicKey.withArgs(mail.from[0].address).returns(resolves(senderKey));
|
||||||
keychainStub.getReceiverPublicKey.withArgs(mail.to[0].address).yieldsAsync(null, receiverKey);
|
keychainStub.getReceiverPublicKey.withArgs(mail.to[0].address).returns(resolves(receiverKey));
|
||||||
keychainStub.getReceiverPublicKey.withArgs(mail.to[1].address).yieldsAsync();
|
keychainStub.getReceiverPublicKey.withArgs(mail.to[1].address).returns(resolves());
|
||||||
|
|
||||||
devicestorageStub.storeList.withArgs([mail]).yieldsAsync();
|
devicestorageStub.storeList.withArgs([mail]).returns(resolves());
|
||||||
|
|
||||||
outbox.put(mail, function(error) {
|
|
||||||
expect(error).to.not.exist;
|
|
||||||
|
|
||||||
|
outbox.put(mail).then(function() {
|
||||||
expect(mail.publicKeysArmored.length).to.equal(2);
|
expect(mail.publicKeysArmored.length).to.equal(2);
|
||||||
expect(emailDaoStub.encrypt.called).to.be.false;
|
expect(emailDaoStub.encrypt.called).to.be.false;
|
||||||
expect(devicestorageStub.storeList.calledOnce).to.be.true;
|
expect(devicestorageStub.storeList.calledOnce).to.be.true;
|
||||||
|
@ -103,11 +108,9 @@ describe('Outbox Business Object unit test', function() {
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
|
|
||||||
devicestorageStub.storeList.withArgs([mail]).yieldsAsync();
|
devicestorageStub.storeList.withArgs([mail]).returns(resolves());
|
||||||
|
|
||||||
outbox.put(mail, function(error) {
|
|
||||||
expect(error).to.not.exist;
|
|
||||||
|
|
||||||
|
outbox.put(mail).then(function() {
|
||||||
expect(mail.publicKeysArmored.length).to.equal(0);
|
expect(mail.publicKeysArmored.length).to.equal(0);
|
||||||
expect(keychainStub.getReceiverPublicKey.called).to.be.false;
|
expect(keychainStub.getReceiverPublicKey.called).to.be.false;
|
||||||
expect(emailDaoStub.encrypt.called).to.be.false;
|
expect(emailDaoStub.encrypt.called).to.be.false;
|
||||||
|
@ -142,20 +145,18 @@ describe('Outbox Business Object unit test', function() {
|
||||||
bcc: []
|
bcc: []
|
||||||
};
|
};
|
||||||
|
|
||||||
keychainStub.getReceiverPublicKey.withArgs(mail.from[0].address).yieldsAsync(null, senderKey);
|
keychainStub.getReceiverPublicKey.withArgs(mail.from[0].address).returns(resolves(senderKey));
|
||||||
keychainStub.getReceiverPublicKey.withArgs(mail.to[0].address).yieldsAsync(null, receiverKey);
|
keychainStub.getReceiverPublicKey.withArgs(mail.to[0].address).returns(resolves(receiverKey));
|
||||||
keychainStub.getReceiverPublicKey.withArgs(mail.to[1].address).yieldsAsync(null, receiverKey);
|
keychainStub.getReceiverPublicKey.withArgs(mail.to[1].address).returns(resolves(receiverKey));
|
||||||
|
|
||||||
emailDaoStub.encrypt.withArgs({
|
emailDaoStub.encrypt.withArgs({
|
||||||
mail: mail,
|
mail: mail,
|
||||||
publicKeysArmored: [senderKey.publicKey, receiverKey.publicKey, receiverKey.publicKey]
|
publicKeysArmored: [senderKey.publicKey, receiverKey.publicKey, receiverKey.publicKey]
|
||||||
}).yieldsAsync();
|
}).returns(resolves());
|
||||||
|
|
||||||
devicestorageStub.storeList.withArgs([mail]).yieldsAsync();
|
devicestorageStub.storeList.withArgs([mail]).returns(resolves());
|
||||||
|
|
||||||
outbox.put(mail, function(error) {
|
|
||||||
expect(error).to.not.exist;
|
|
||||||
|
|
||||||
|
outbox.put(mail).then(function() {
|
||||||
expect(mail.publicKeysArmored.length).to.equal(3);
|
expect(mail.publicKeysArmored.length).to.equal(3);
|
||||||
expect(emailDaoStub.encrypt.calledOnce).to.be.true;
|
expect(emailDaoStub.encrypt.calledOnce).to.be.true;
|
||||||
expect(devicestorageStub.storeList.calledOnce).to.be.true;
|
expect(devicestorageStub.storeList.calledOnce).to.be.true;
|
||||||
|
@ -230,19 +231,19 @@ describe('Outbox Business Object unit test', function() {
|
||||||
|
|
||||||
dummyMails = [member, invited, notinvited, newlyjoined];
|
dummyMails = [member, invited, notinvited, newlyjoined];
|
||||||
|
|
||||||
devicestorageStub.listItems.yieldsAsync(null, dummyMails);
|
devicestorageStub.listItems.returns(resolves(dummyMails));
|
||||||
|
|
||||||
emailDaoStub.sendPlaintext.yieldsAsync();
|
emailDaoStub.sendPlaintext.returns(resolves());
|
||||||
|
|
||||||
emailDaoStub.sendEncrypted.withArgs({
|
emailDaoStub.sendEncrypted.withArgs({
|
||||||
email: newlyjoined
|
email: newlyjoined
|
||||||
}).yieldsAsync();
|
}).returns(resolves());
|
||||||
|
|
||||||
emailDaoStub.sendEncrypted.withArgs({
|
emailDaoStub.sendEncrypted.withArgs({
|
||||||
email: member
|
email: member
|
||||||
}).yieldsAsync();
|
}).returns(resolves());
|
||||||
|
|
||||||
devicestorageStub.removeList.yieldsAsync();
|
devicestorageStub.removeList.returns(resolves());
|
||||||
|
|
||||||
function onOutboxUpdate(err, count) {
|
function onOutboxUpdate(err, count) {
|
||||||
expect(err).to.not.exist;
|
expect(err).to.not.exist;
|
||||||
|
@ -263,7 +264,7 @@ describe('Outbox Business Object unit test', function() {
|
||||||
|
|
||||||
it('should not process outbox in offline mode', function(done) {
|
it('should not process outbox in offline mode', function(done) {
|
||||||
emailDaoStub._account.online = false;
|
emailDaoStub._account.online = false;
|
||||||
devicestorageStub.listItems.yieldsAsync(null, [{}]);
|
devicestorageStub.listItems.returns(resolves([{}]));
|
||||||
|
|
||||||
outbox._processOutbox(function(err, count) {
|
outbox._processOutbox(function(err, count) {
|
||||||
expect(err).to.not.exist;
|
expect(err).to.not.exist;
|
||||||
|
|
|
@ -23,7 +23,7 @@ describe('Admin DAO unit tests', function() {
|
||||||
emailAddress: emailAddress
|
emailAddress: emailAddress
|
||||||
};
|
};
|
||||||
|
|
||||||
adminDao.createUser(opt, function(err) {
|
adminDao.createUser(opt).catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -36,11 +36,11 @@ describe('Admin DAO unit tests', function() {
|
||||||
phone: '12345'
|
phone: '12345'
|
||||||
};
|
};
|
||||||
|
|
||||||
restDaoStub.post.withArgs(opt, '/user').yields({
|
restDaoStub.post.withArgs(opt, '/user').returns(rejects({
|
||||||
code: 409
|
code: 409
|
||||||
});
|
}));
|
||||||
|
|
||||||
adminDao.createUser(opt, function(err) {
|
adminDao.createUser(opt).catch(function(err) {
|
||||||
expect(err.message).to.contain('already taken');
|
expect(err.message).to.contain('already taken');
|
||||||
expect(restDaoStub.post.calledOnce).to.be.true;
|
expect(restDaoStub.post.calledOnce).to.be.true;
|
||||||
done();
|
done();
|
||||||
|
@ -54,9 +54,9 @@ describe('Admin DAO unit tests', function() {
|
||||||
phone: '12345'
|
phone: '12345'
|
||||||
};
|
};
|
||||||
|
|
||||||
restDaoStub.post.withArgs(opt, '/user').yields(new Error());
|
restDaoStub.post.withArgs(opt, '/user').returns(rejects(new Error()));
|
||||||
|
|
||||||
adminDao.createUser(opt, function(err) {
|
adminDao.createUser(opt).catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
expect(restDaoStub.post.calledOnce).to.be.true;
|
expect(restDaoStub.post.calledOnce).to.be.true;
|
||||||
done();
|
done();
|
||||||
|
@ -70,10 +70,9 @@ describe('Admin DAO unit tests', function() {
|
||||||
phone: '12345'
|
phone: '12345'
|
||||||
};
|
};
|
||||||
|
|
||||||
restDaoStub.post.withArgs(opt, '/user').yields();
|
restDaoStub.post.withArgs(opt, '/user').returns(resolves());
|
||||||
|
|
||||||
adminDao.createUser(opt, function(err) {
|
adminDao.createUser(opt).then(function() {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(restDaoStub.post.calledOnce).to.be.true;
|
expect(restDaoStub.post.calledOnce).to.be.true;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -86,7 +85,7 @@ describe('Admin DAO unit tests', function() {
|
||||||
emailAddress: emailAddress
|
emailAddress: emailAddress
|
||||||
};
|
};
|
||||||
|
|
||||||
adminDao.validateUser(opt, function(err) {
|
adminDao.validateUser(opt).catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -98,9 +97,9 @@ describe('Admin DAO unit tests', function() {
|
||||||
token: 'H45Z6D'
|
token: 'H45Z6D'
|
||||||
};
|
};
|
||||||
|
|
||||||
restDaoStub.post.withArgs(opt, '/user/validate').yields(new Error());
|
restDaoStub.post.withArgs(opt, '/user/validate').returns(rejects(new Error()));
|
||||||
|
|
||||||
adminDao.validateUser(opt, function(err) {
|
adminDao.validateUser(opt).catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
expect(restDaoStub.post.calledOnce).to.be.true;
|
expect(restDaoStub.post.calledOnce).to.be.true;
|
||||||
done();
|
done();
|
||||||
|
@ -113,10 +112,9 @@ describe('Admin DAO unit tests', function() {
|
||||||
token: 'H45Z6D'
|
token: 'H45Z6D'
|
||||||
};
|
};
|
||||||
|
|
||||||
restDaoStub.post.withArgs(opt, '/user/validate').yields();
|
restDaoStub.post.withArgs(opt, '/user/validate').returns(resolves());
|
||||||
|
|
||||||
adminDao.validateUser(opt, function(err) {
|
adminDao.validateUser(opt).then(function() {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(restDaoStub.post.calledOnce).to.be.true;
|
expect(restDaoStub.post.calledOnce).to.be.true;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -128,12 +126,11 @@ describe('Admin DAO unit tests', function() {
|
||||||
token: 'H45Z6D'
|
token: 'H45Z6D'
|
||||||
};
|
};
|
||||||
|
|
||||||
restDaoStub.post.withArgs(opt, '/user/validate').yields({
|
restDaoStub.post.withArgs(opt, '/user/validate').returns(rejects({
|
||||||
code: 202
|
code: 202
|
||||||
});
|
}));
|
||||||
|
|
||||||
adminDao.validateUser(opt, function(err) {
|
adminDao.validateUser(opt).then(function() {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(restDaoStub.post.calledOnce).to.be.true;
|
expect(restDaoStub.post.calledOnce).to.be.true;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
|
@ -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,18 @@ 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({
|
||||||
|
decrypted: password,
|
||||||
auth.getCredentials(function(err, cred) {
|
signaturesValid: true
|
||||||
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 +136,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 +184,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 +197,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 +209,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 +223,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 +238,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 +254,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 +278,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,13 +308,13 @@ 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);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -333,9 +322,12 @@ describe('Auth unit tests', function() {
|
||||||
auth.imap = {
|
auth.imap = {
|
||||||
ca: dummyCert
|
ca: dummyCert
|
||||||
};
|
};
|
||||||
storeCredentialsStub.yields();
|
storeCredentialsStub.returns(resolves());
|
||||||
|
|
||||||
|
function callback() {}
|
||||||
|
|
||||||
|
auth.handleCertificateUpdate('imap', onConnectDummy, callback, dummyCert);
|
||||||
|
|
||||||
auth.handleCertificateUpdate('imap', onConnectDummy, onConnectDummy, dummyCert);
|
|
||||||
expect(storeCredentialsStub.callCount).to.equal(0);
|
expect(storeCredentialsStub.callCount).to.equal(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -344,7 +336,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,6 +344,7 @@ 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, callback, dummyCert);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -359,7 +352,7 @@ describe('Auth unit tests', function() {
|
||||||
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) {
|
||||||
|
@ -373,8 +366,8 @@ describe('Auth unit tests', function() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onConnect(callback) {
|
function onConnect(cb) {
|
||||||
callback();
|
cb();
|
||||||
}
|
}
|
||||||
|
|
||||||
auth.handleCertificateUpdate('imap', onConnect, callback, dummyCert);
|
auth.handleCertificateUpdate('imap', onConnect, callback, dummyCert);
|
||||||
|
@ -383,19 +376,18 @@ describe('Auth unit tests', function() {
|
||||||
|
|
||||||
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;
|
||||||
|
|
|
@ -17,21 +17,22 @@ describe('Device Storage DAO unit tests', function() {
|
||||||
afterEach(function() {});
|
afterEach(function() {});
|
||||||
|
|
||||||
describe('init', function() {
|
describe('init', function() {
|
||||||
it('should work', function() {
|
it('should work', function(done) {
|
||||||
lawnchairDaoStub.init.yields();
|
lawnchairDaoStub.init.returns(resolves());
|
||||||
|
|
||||||
storageDao.init(testUser, function(err) {
|
storageDao.init(testUser).then(function() {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(lawnchairDaoStub.init.calledOnce).to.be.true;
|
expect(lawnchairDaoStub.init.calledOnce).to.be.true;
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail', function() {
|
it('should fail', function(done) {
|
||||||
lawnchairDaoStub.init.yields(new Error());
|
lawnchairDaoStub.init.returns(rejects(new Error()));
|
||||||
|
|
||||||
storageDao.init(testUser, function(err) {
|
storageDao.init(testUser).catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
expect(lawnchairDaoStub.init.calledOnce).to.be.true;
|
expect(lawnchairDaoStub.init.calledOnce).to.be.true;
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -40,7 +41,7 @@ describe('Device Storage DAO unit tests', function() {
|
||||||
it('should fail', function(done) {
|
it('should fail', function(done) {
|
||||||
var list = [{}];
|
var list = [{}];
|
||||||
|
|
||||||
storageDao.storeList(list, '', function(err) {
|
storageDao.storeList(list, '').catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -49,21 +50,17 @@ describe('Device Storage DAO unit tests', function() {
|
||||||
it('should work with empty list', function(done) {
|
it('should work with empty list', function(done) {
|
||||||
var list = [];
|
var list = [];
|
||||||
|
|
||||||
storageDao.storeList(list, 'email', function(err) {
|
storageDao.storeList(list, 'email').then(done);
|
||||||
expect(err).to.not.exist;
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work', function(done) {
|
it('should work', function(done) {
|
||||||
lawnchairDaoStub.batch.yields();
|
lawnchairDaoStub.batch.returns(resolves());
|
||||||
|
|
||||||
var list = [{
|
var list = [{
|
||||||
foo: 'bar'
|
foo: 'bar'
|
||||||
}];
|
}];
|
||||||
|
|
||||||
storageDao.storeList(list, 'email', function(err) {
|
storageDao.storeList(list, 'email').then(function() {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(lawnchairDaoStub.batch.calledOnce).to.be.true;
|
expect(lawnchairDaoStub.batch.calledOnce).to.be.true;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -72,10 +69,9 @@ describe('Device Storage DAO unit tests', function() {
|
||||||
|
|
||||||
describe('remove list', function() {
|
describe('remove list', function() {
|
||||||
it('should work', function(done) {
|
it('should work', function(done) {
|
||||||
lawnchairDaoStub.removeList.yields();
|
lawnchairDaoStub.removeList.returns(resolves());
|
||||||
|
|
||||||
storageDao.removeList('email', function(err) {
|
storageDao.removeList('email').then(function() {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(lawnchairDaoStub.removeList.calledOnce).to.be.true;
|
expect(lawnchairDaoStub.removeList.calledOnce).to.be.true;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -84,10 +80,9 @@ describe('Device Storage DAO unit tests', function() {
|
||||||
|
|
||||||
describe('list items', function() {
|
describe('list items', function() {
|
||||||
it('should work', function(done) {
|
it('should work', function(done) {
|
||||||
lawnchairDaoStub.list.yields();
|
lawnchairDaoStub.list.returns(resolves());
|
||||||
|
|
||||||
storageDao.listItems('email', 0, null, function(err) {
|
storageDao.listItems('email', 0, null).then(function() {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(lawnchairDaoStub.list.calledOnce).to.be.true;
|
expect(lawnchairDaoStub.list.calledOnce).to.be.true;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -96,10 +91,9 @@ describe('Device Storage DAO unit tests', function() {
|
||||||
|
|
||||||
describe('clear', function() {
|
describe('clear', function() {
|
||||||
it('should work', function(done) {
|
it('should work', function(done) {
|
||||||
lawnchairDaoStub.clear.yields();
|
lawnchairDaoStub.clear.returns(resolves());
|
||||||
|
|
||||||
storageDao.clear(function(err) {
|
storageDao.clear().then(function() {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(lawnchairDaoStub.clear.calledOnce).to.be.true;
|
expect(lawnchairDaoStub.clear.calledOnce).to.be.true;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var RestDAO = require('../../../src/js/service/rest'),
|
var RestDAO = require('../../../src/js/service/rest'),
|
||||||
InvitationDAO = require('../../../src/js/service/invitation'),
|
InvitationDAO = require('../../../src/js/service/invitation');
|
||||||
appConfig = require('../../../src/js/app-config');
|
|
||||||
|
|
||||||
describe('Invitation DAO unit tests', function() {
|
describe('Invitation DAO unit tests', function() {
|
||||||
var restDaoStub, invitationDao,
|
var restDaoStub, invitationDao,
|
||||||
|
@ -12,94 +11,78 @@ describe('Invitation DAO unit tests', function() {
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
restDaoStub = sinon.createStubInstance(RestDAO);
|
restDaoStub = sinon.createStubInstance(RestDAO);
|
||||||
invitationDao = new InvitationDAO(restDaoStub, appConfig);
|
invitationDao = new InvitationDAO(restDaoStub);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('initialization', function() {
|
describe('initialization', function() {
|
||||||
it('should wire up correctly', function() {
|
it('should wire up correctly', function() {
|
||||||
expect(invitationDao._restDao).to.equal(restDaoStub);
|
expect(invitationDao._restDao).to.equal(restDaoStub);
|
||||||
expect(invitationDao.invite).to.exist;
|
expect(invitationDao.invite).to.exist;
|
||||||
expect(InvitationDAO.INVITE_MISSING).to.equal(1);
|
|
||||||
expect(InvitationDAO.INVITE_PENDING).to.equal(2);
|
|
||||||
expect(InvitationDAO.INVITE_SUCCESS).to.equal(4);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('invite', function() {
|
describe('invite', function() {
|
||||||
it('should invite the recipient', function(done) {
|
it('should invite the recipient', function(done) {
|
||||||
restDaoStub.put.yieldsAsync(null, undefined, 201);
|
restDaoStub.put.returns(resolves());
|
||||||
|
|
||||||
invitationDao.invite({
|
invitationDao.invite({
|
||||||
recipient: alice,
|
recipient: alice,
|
||||||
sender: bob
|
sender: bob
|
||||||
}, function(err, status) {
|
}).then(function() {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(status).to.equal(InvitationDAO.INVITE_SUCCESS);
|
|
||||||
expect(restDaoStub.put.calledWith({}, expectedUri)).to.be.true;
|
expect(restDaoStub.put.calledWith({}, expectedUri)).to.be.true;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should point out already invited recipient', function(done) {
|
|
||||||
restDaoStub.put.yieldsAsync(null, undefined, 304);
|
|
||||||
|
|
||||||
invitationDao.invite({
|
|
||||||
recipient: alice,
|
|
||||||
sender: bob
|
|
||||||
}, function(err, status) {
|
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(status).to.equal(InvitationDAO.INVITE_PENDING);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not work for http error', function(done) {
|
it('should not work for http error', function(done) {
|
||||||
restDaoStub.put.yieldsAsync({
|
restDaoStub.put.returns(rejects(new Error()));
|
||||||
errMsg: 'jawollja.'
|
|
||||||
});
|
|
||||||
|
|
||||||
invitationDao.invite({
|
invitationDao.invite({
|
||||||
recipient: alice,
|
recipient: alice,
|
||||||
sender: bob
|
sender: bob
|
||||||
}, function(err, status) {
|
}).catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
expect(status).to.not.exist;
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not work for unexpected response', function(done) {
|
it('should report erroneous usage', function(done) {
|
||||||
restDaoStub.put.yieldsAsync(null, undefined, 1337);
|
|
||||||
|
|
||||||
invitationDao.invite({
|
invitationDao.invite({
|
||||||
recipient: alice,
|
|
||||||
sender: bob
|
sender: bob
|
||||||
}, function(err, status) {
|
}, expectError);
|
||||||
|
|
||||||
|
invitationDao.invite('asd').catch(expectError);
|
||||||
|
|
||||||
|
function expectError(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
expect(status).to.not.exist;
|
|
||||||
done();
|
done();
|
||||||
});
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should report erroneous usage', function() {
|
it('should report erroneous usage', function(done) {
|
||||||
invitationDao.invite({
|
|
||||||
sender: bob
|
|
||||||
}, expectError);
|
|
||||||
|
|
||||||
invitationDao.invite({
|
invitationDao.invite({
|
||||||
recipient: alice,
|
recipient: alice,
|
||||||
}, expectError);
|
}, expectError);
|
||||||
|
|
||||||
|
invitationDao.invite('asd').catch(expectError);
|
||||||
|
|
||||||
|
function expectError(err) {
|
||||||
|
expect(err).to.exist;
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should report erroneous usage', function(done) {
|
||||||
invitationDao.invite({
|
invitationDao.invite({
|
||||||
recipient: 123,
|
recipient: 123,
|
||||||
sender: 123
|
sender: 123
|
||||||
}, expectError);
|
}, expectError);
|
||||||
|
|
||||||
invitationDao.invite('asd', expectError);
|
invitationDao.invite('asd').catch(expectError);
|
||||||
|
|
||||||
function expectError(err, status) {
|
function expectError(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
expect(status).to.not.exist;
|
done();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -22,23 +22,19 @@ describe('Lawnchair DAO unit tests', function() {
|
||||||
|
|
||||||
beforeEach(function(done) {
|
beforeEach(function(done) {
|
||||||
lawnchairDao = new LawnchairDAO();
|
lawnchairDao = new LawnchairDAO();
|
||||||
lawnchairDao.init(dbName, function(err) {
|
lawnchairDao.init(dbName).then(function() {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(lawnchairDao._db).to.exist;
|
expect(lawnchairDao._db).to.exist;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function(done) {
|
afterEach(function(done) {
|
||||||
lawnchairDao.clear(function(err) {
|
lawnchairDao.clear().then(done);
|
||||||
expect(err).to.not.exist;
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('read', function() {
|
describe('read', function() {
|
||||||
it('should fail', function(done) {
|
it('should fail', function(done) {
|
||||||
lawnchairDao.read(undefined, function(err) {
|
lawnchairDao.read(undefined).catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -47,7 +43,7 @@ describe('Lawnchair DAO unit tests', function() {
|
||||||
|
|
||||||
describe('list', function() {
|
describe('list', function() {
|
||||||
it('should fail', function(done) {
|
it('should fail', function(done) {
|
||||||
lawnchairDao.list(undefined, 0, null, function(err) {
|
lawnchairDao.list(undefined, 0, null).catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -56,7 +52,7 @@ describe('Lawnchair DAO unit tests', function() {
|
||||||
|
|
||||||
describe('remove list', function() {
|
describe('remove list', function() {
|
||||||
it('should fail', function(done) {
|
it('should fail', function(done) {
|
||||||
lawnchairDao.removeList(undefined, function(err) {
|
lawnchairDao.removeList(undefined).catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -65,46 +61,36 @@ describe('Lawnchair DAO unit tests', function() {
|
||||||
|
|
||||||
describe('persist/read/remove', function() {
|
describe('persist/read/remove', function() {
|
||||||
it('should fail', function(done) {
|
it('should fail', function(done) {
|
||||||
lawnchairDao.persist(undefined, data, function(err) {
|
lawnchairDao.persist(undefined, data).catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('should fail', function(done) {
|
it('should fail', function(done) {
|
||||||
lawnchairDao.persist('1234', undefined, function(err) {
|
lawnchairDao.persist('1234', undefined).catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work', function(done) {
|
it('should work', function(done) {
|
||||||
lawnchairDao.persist(key, data, function(err) {
|
lawnchairDao.persist(key, data).then(function() {
|
||||||
expect(err).to.not.exist;
|
return lawnchairDao.read(key);
|
||||||
lawnchairDao.read(key, onRead);
|
}).then(function(fetched) {
|
||||||
});
|
|
||||||
|
|
||||||
function onRead(err, fetched) {
|
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(fetched).to.deep.equal(data);
|
expect(fetched).to.deep.equal(data);
|
||||||
lawnchairDao.remove(key, onRemove);
|
return lawnchairDao.remove(key);
|
||||||
}
|
}).then(function() {
|
||||||
|
return lawnchairDao.read(key);
|
||||||
function onRemove(err) {
|
}).then(function(fetched) {
|
||||||
expect(err).to.not.exist;
|
|
||||||
lawnchairDao.read(key, onReadAgain);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onReadAgain(err, fetched) {
|
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(fetched).to.not.exist;
|
expect(fetched).to.not.exist;
|
||||||
done();
|
done();
|
||||||
}
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('batch/list/removeList', function() {
|
describe('batch/list/removeList', function() {
|
||||||
it('should fails', function(done) {
|
it('should fails', function(done) {
|
||||||
lawnchairDao.batch({}, function(err) {
|
lawnchairDao.batch({}).catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -119,29 +105,19 @@ describe('Lawnchair DAO unit tests', function() {
|
||||||
object: data2
|
object: data2
|
||||||
}];
|
}];
|
||||||
|
|
||||||
lawnchairDao.batch(list, function(err) {
|
lawnchairDao.batch(list).then(function() {
|
||||||
expect(err).to.not.exist;
|
return lawnchairDao.list('type', 0, null);
|
||||||
lawnchairDao.list('type', 0, null, onList);
|
}).then(function(fetched) {
|
||||||
});
|
|
||||||
|
|
||||||
function onList(err, fetched) {
|
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(fetched.length).to.equal(2);
|
expect(fetched.length).to.equal(2);
|
||||||
expect(fetched[0]).to.deep.equal(list[0].object);
|
expect(fetched[0]).to.deep.equal(list[0].object);
|
||||||
lawnchairDao.removeList('type', onRemoveList);
|
return lawnchairDao.removeList('type');
|
||||||
}
|
}).then(function() {
|
||||||
|
return lawnchairDao.list('type', 0, null);
|
||||||
function onRemoveList(err) {
|
}).then(function(fetched) {
|
||||||
expect(err).to.not.exist;
|
|
||||||
lawnchairDao.list('type', 0, null, onListAgain);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onListAgain(err, fetched) {
|
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(fetched).to.exist;
|
expect(fetched).to.exist;
|
||||||
expect(fetched.length).to.equal(0);
|
expect(fetched.length).to.equal(0);
|
||||||
done();
|
done();
|
||||||
}
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -29,45 +29,43 @@ describe('Newsletter Service unit test', function() {
|
||||||
xhrMock.restore();
|
xhrMock.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not signup if user has not agreed', inject(function($rootScope) {
|
it('should not signup if user has not agreed', function(done) {
|
||||||
newsletter.signup('text@example.com', false).then(function(result) {
|
newsletter.signup('text@example.com', false).then(function(result) {
|
||||||
expect(result).to.be.false;
|
expect(result).to.be.false;
|
||||||
|
expect(requests.length).to.equal(0);
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
$rootScope.$apply();
|
it('should not signup due to invalid email address', function(done) {
|
||||||
expect(requests.length).to.equal(0);
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should not signup due to invalid email address', inject(function($rootScope) {
|
|
||||||
newsletter.signup('textexample.com', true).catch(function(err) {
|
newsletter.signup('textexample.com', true).catch(function(err) {
|
||||||
expect(err.message).to.contain('Invalid');
|
expect(err.message).to.contain('Invalid');
|
||||||
|
expect(requests.length).to.equal(0);
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
$rootScope.$apply();
|
it('should fail', function(done) {
|
||||||
expect(requests.length).to.equal(0);
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should fail', inject(function($rootScope) {
|
|
||||||
newsletter.signup('text@example.com', true).catch(function(err) {
|
newsletter.signup('text@example.com', true).catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
|
expect(requests.length).to.equal(1);
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
requests[0].onerror('err');
|
requests[0].onerror('err');
|
||||||
$rootScope.$apply();
|
});
|
||||||
expect(requests.length).to.equal(1);
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should work', inject(function($rootScope) {
|
it('should work', function(done) {
|
||||||
newsletter.signup('text@example.com', true).then(function(result) {
|
newsletter.signup('text@example.com', true).then(function(result) {
|
||||||
expect(result).to.exist;
|
expect(result).to.exist;
|
||||||
|
expect(requests.length).to.equal(1);
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
requests[0].respond(200, {
|
requests[0].respond(200, {
|
||||||
"Content-Type": "text/plain"
|
"Content-Type": "text/plain"
|
||||||
}, 'foobar!');
|
}, 'foobar!');
|
||||||
$rootScope.$apply();
|
});
|
||||||
expect(requests.length).to.equal(1);
|
|
||||||
}));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
|
@ -56,15 +56,14 @@ describe('OAuth unit tests', function() {
|
||||||
it('should work', function() {
|
it('should work', function() {
|
||||||
removeCachedStub.withArgs({
|
removeCachedStub.withArgs({
|
||||||
token: 'oldToken'
|
token: 'oldToken'
|
||||||
}).yields();
|
}).returns(resolves());
|
||||||
|
|
||||||
getOAuthTokenStub.withArgs(testEmail).yields();
|
getOAuthTokenStub.withArgs(testEmail).returns(resolves());
|
||||||
|
|
||||||
oauth.refreshToken({
|
oauth.refreshToken({
|
||||||
oldToken: 'oldToken',
|
oldToken: 'oldToken',
|
||||||
emailAddress: testEmail
|
emailAddress: testEmail
|
||||||
}, function(err) {
|
}).then(function() {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(removeCachedStub.calledOnce).to.be.true;
|
expect(removeCachedStub.calledOnce).to.be.true;
|
||||||
expect(getOAuthTokenStub.calledOnce).to.be.true;
|
expect(getOAuthTokenStub.calledOnce).to.be.true;
|
||||||
});
|
});
|
||||||
|
@ -73,14 +72,13 @@ describe('OAuth unit tests', function() {
|
||||||
it('should work without email', function() {
|
it('should work without email', function() {
|
||||||
removeCachedStub.withArgs({
|
removeCachedStub.withArgs({
|
||||||
token: 'oldToken'
|
token: 'oldToken'
|
||||||
}).yields();
|
}).returns(resolves());
|
||||||
|
|
||||||
getOAuthTokenStub.withArgs(undefined).yields();
|
getOAuthTokenStub.withArgs(undefined).returns(resolves());
|
||||||
|
|
||||||
oauth.refreshToken({
|
oauth.refreshToken({
|
||||||
oldToken: 'oldToken',
|
oldToken: 'oldToken',
|
||||||
}, function(err) {
|
}).then(function() {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(removeCachedStub.calledOnce).to.be.true;
|
expect(removeCachedStub.calledOnce).to.be.true;
|
||||||
expect(getOAuthTokenStub.calledOnce).to.be.true;
|
expect(getOAuthTokenStub.calledOnce).to.be.true;
|
||||||
expect(getOAuthTokenStub.calledWith(undefined)).to.be.true;
|
expect(getOAuthTokenStub.calledWith(undefined)).to.be.true;
|
||||||
|
@ -90,7 +88,7 @@ describe('OAuth unit tests', function() {
|
||||||
it('should fail without all options', function() {
|
it('should fail without all options', function() {
|
||||||
oauth.refreshToken({
|
oauth.refreshToken({
|
||||||
emailAddress: testEmail
|
emailAddress: testEmail
|
||||||
}, function(err) {
|
}).catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
expect(removeCachedStub.called).to.be.false;
|
expect(removeCachedStub.called).to.be.false;
|
||||||
expect(getOAuthTokenStub.called).to.be.false;
|
expect(getOAuthTokenStub.called).to.be.false;
|
||||||
|
@ -107,8 +105,7 @@ describe('OAuth unit tests', function() {
|
||||||
interactive: true
|
interactive: true
|
||||||
}).yields('token');
|
}).yields('token');
|
||||||
|
|
||||||
oauth.getOAuthToken(undefined, function(err, token) {
|
oauth.getOAuthToken(undefined).then(function(token) {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(token).to.equal('token');
|
expect(token).to.equal('token');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -123,8 +120,7 @@ describe('OAuth unit tests', function() {
|
||||||
accountHint: testEmail
|
accountHint: testEmail
|
||||||
}).yields('token');
|
}).yields('token');
|
||||||
|
|
||||||
oauth.getOAuthToken(testEmail, function(err, token) {
|
oauth.getOAuthToken(testEmail).then(function(token) {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(token).to.equal('token');
|
expect(token).to.equal('token');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -138,8 +134,7 @@ describe('OAuth unit tests', function() {
|
||||||
interactive: true
|
interactive: true
|
||||||
}).yields('token');
|
}).yields('token');
|
||||||
|
|
||||||
oauth.getOAuthToken(testEmail, function(err, token) {
|
oauth.getOAuthToken(testEmail).then(function(token) {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(token).to.equal('token');
|
expect(token).to.equal('token');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -151,9 +146,8 @@ describe('OAuth unit tests', function() {
|
||||||
});
|
});
|
||||||
identityStub.yields();
|
identityStub.yields();
|
||||||
|
|
||||||
oauth.getOAuthToken(testEmail, function(err, token) {
|
oauth.getOAuthToken(testEmail).catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
expect(token).to.not.exist;
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -163,21 +157,19 @@ describe('OAuth unit tests', function() {
|
||||||
it('should work', function(done) {
|
it('should work', function(done) {
|
||||||
googleApiStub.get.withArgs({
|
googleApiStub.get.withArgs({
|
||||||
uri: '/oauth2/v3/userinfo?access_token=token'
|
uri: '/oauth2/v3/userinfo?access_token=token'
|
||||||
}).yields(null, {
|
}).returns(resolves({
|
||||||
email: 'asdf@example.com'
|
email: 'asdf@example.com'
|
||||||
});
|
}));
|
||||||
|
|
||||||
oauth.queryEmailAddress('token', function(err, emailAddress) {
|
oauth.queryEmailAddress('token').then(function(emailAddress) {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(emailAddress).to.equal('asdf@example.com');
|
expect(emailAddress).to.equal('asdf@example.com');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail due to invalid token', function(done) {
|
it('should fail due to invalid token', function(done) {
|
||||||
oauth.queryEmailAddress('', function(err, emailAddress) {
|
oauth.queryEmailAddress('').catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
expect(emailAddress).to.not.exist;
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -187,9 +179,8 @@ describe('OAuth unit tests', function() {
|
||||||
uri: '/oauth2/v3/userinfo?access_token=token'
|
uri: '/oauth2/v3/userinfo?access_token=token'
|
||||||
}).yields(new Error());
|
}).yields(new Error());
|
||||||
|
|
||||||
oauth.queryEmailAddress('token', function(err, emailAddress) {
|
oauth.queryEmailAddress('token').catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
expect(emailAddress).to.not.exist;
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -19,23 +19,21 @@ describe('Private Key DAO unit tests', function() {
|
||||||
|
|
||||||
describe('requestDeviceRegistration', function() {
|
describe('requestDeviceRegistration', function() {
|
||||||
it('should fail due to invalid args', function(done) {
|
it('should fail due to invalid args', function(done) {
|
||||||
privkeyDao.requestDeviceRegistration({}, function(err, sessionKey) {
|
privkeyDao.requestDeviceRegistration({}).catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
expect(sessionKey).to.not.exist;
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work', function(done) {
|
it('should work', function(done) {
|
||||||
restDaoStub.post.yields(null, {
|
restDaoStub.post.returns(resolves({
|
||||||
encryptedRegSessionKey: 'asdf'
|
encryptedRegSessionKey: 'asdf'
|
||||||
});
|
}));
|
||||||
|
|
||||||
privkeyDao.requestDeviceRegistration({
|
privkeyDao.requestDeviceRegistration({
|
||||||
userId: emailAddress,
|
userId: emailAddress,
|
||||||
deviceName: deviceName
|
deviceName: deviceName
|
||||||
}, function(err, sessionKey) {
|
}).then(function(sessionKey) {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(sessionKey).to.exist;
|
expect(sessionKey).to.exist;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -44,50 +42,44 @@ describe('Private Key DAO unit tests', function() {
|
||||||
|
|
||||||
describe('uploadDeviceSecret', function() {
|
describe('uploadDeviceSecret', function() {
|
||||||
it('should fail due to invalid args', function(done) {
|
it('should fail due to invalid args', function(done) {
|
||||||
privkeyDao.uploadDeviceSecret({}, function(err) {
|
privkeyDao.uploadDeviceSecret({}).catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work', function(done) {
|
it('should work', function(done) {
|
||||||
restDaoStub.put.yields();
|
restDaoStub.put.returns(resolves());
|
||||||
|
|
||||||
privkeyDao.uploadDeviceSecret({
|
privkeyDao.uploadDeviceSecret({
|
||||||
userId: emailAddress,
|
userId: emailAddress,
|
||||||
deviceName: deviceName,
|
deviceName: deviceName,
|
||||||
encryptedDeviceSecret: 'asdf',
|
encryptedDeviceSecret: 'asdf',
|
||||||
iv: 'iv'
|
iv: 'iv'
|
||||||
}, function(err) {
|
}).then(done);
|
||||||
expect(err).to.not.exist;
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('requestAuthSessionKey', function() {
|
describe('requestAuthSessionKey', function() {
|
||||||
it('should fail due to invalid args', function(done) {
|
it('should fail due to invalid args', function(done) {
|
||||||
privkeyDao.requestAuthSessionKey({}, function(err) {
|
privkeyDao.requestAuthSessionKey({}).catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work', function(done) {
|
it('should work', function(done) {
|
||||||
restDaoStub.post.withArgs(undefined, '/auth/user/' + emailAddress).yields();
|
restDaoStub.post.withArgs(undefined, '/auth/user/' + emailAddress).returns(resolves());
|
||||||
|
|
||||||
privkeyDao.requestAuthSessionKey({
|
privkeyDao.requestAuthSessionKey({
|
||||||
userId: emailAddress
|
userId: emailAddress
|
||||||
}, function(err) {
|
}).then(done);
|
||||||
expect(err).to.not.exist;
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('verifyAuthentication', function() {
|
describe('verifyAuthentication', function() {
|
||||||
it('should fail due to invalid args', function(done) {
|
it('should fail due to invalid args', function(done) {
|
||||||
privkeyDao.verifyAuthentication({}, function(err) {
|
privkeyDao.verifyAuthentication({}).catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -104,18 +96,15 @@ describe('Private Key DAO unit tests', function() {
|
||||||
iv: ' iv'
|
iv: ' iv'
|
||||||
};
|
};
|
||||||
|
|
||||||
restDaoStub.put.withArgs(options, '/auth/user/' + emailAddress + '/session/' + sessionId).yields();
|
restDaoStub.put.withArgs(options, '/auth/user/' + emailAddress + '/session/' + sessionId).returns(resolves());
|
||||||
|
|
||||||
privkeyDao.verifyAuthentication(options, function(err) {
|
privkeyDao.verifyAuthentication(options).then(done);
|
||||||
expect(err).to.not.exist;
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('upload', function() {
|
describe('upload', function() {
|
||||||
it('should fail due to invalid args', function(done) {
|
it('should fail due to invalid args', function(done) {
|
||||||
privkeyDao.upload({}, function(err) {
|
privkeyDao.upload({}).catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -131,35 +120,49 @@ describe('Private Key DAO unit tests', function() {
|
||||||
iv: 'iv'
|
iv: 'iv'
|
||||||
};
|
};
|
||||||
|
|
||||||
restDaoStub.post.withArgs(options, '/privatekey/user/' + emailAddress + '/session/' + options.sessionId).yields();
|
restDaoStub.post.withArgs(options, '/privatekey/user/' + emailAddress + '/session/' + options.sessionId).returns(resolves());
|
||||||
|
|
||||||
privkeyDao.upload(options, function(err) {
|
privkeyDao.upload(options).then(done);
|
||||||
expect(err).to.not.exist;
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('requestDownload', function() {
|
describe('requestDownload', function() {
|
||||||
it('should fail due to invalid args', function(done) {
|
it('should fail due to invalid args', function(done) {
|
||||||
privkeyDao.requestDownload({}, function(err) {
|
privkeyDao.requestDownload({}).catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not find a key', function(done) {
|
||||||
|
var keyId = '12345';
|
||||||
|
|
||||||
|
restDaoStub.get.withArgs({
|
||||||
|
uri: '/privatekey/user/' + emailAddress + '/key/' + keyId
|
||||||
|
}).returns(rejects({
|
||||||
|
code: 404
|
||||||
|
}));
|
||||||
|
|
||||||
|
privkeyDao.requestDownload({
|
||||||
|
userId: emailAddress,
|
||||||
|
keyId: keyId
|
||||||
|
}).then(function(found) {
|
||||||
|
expect(found).to.be.false;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should work', function(done) {
|
it('should work', function(done) {
|
||||||
var keyId = '12345';
|
var keyId = '12345';
|
||||||
|
|
||||||
restDaoStub.get.withArgs({
|
restDaoStub.get.withArgs({
|
||||||
uri: '/privatekey/user/' + emailAddress + '/key/' + keyId
|
uri: '/privatekey/user/' + emailAddress + '/key/' + keyId
|
||||||
}).yields();
|
}).returns(resolves());
|
||||||
|
|
||||||
privkeyDao.requestDownload({
|
privkeyDao.requestDownload({
|
||||||
userId: emailAddress,
|
userId: emailAddress,
|
||||||
keyId: keyId
|
keyId: keyId
|
||||||
}, function(err, found) {
|
}).then(function(found) {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(found).to.be.true;
|
expect(found).to.be.true;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -168,24 +171,41 @@ describe('Private Key DAO unit tests', function() {
|
||||||
|
|
||||||
describe('hasPrivateKey', function() {
|
describe('hasPrivateKey', function() {
|
||||||
it('should fail due to invalid args', function(done) {
|
it('should fail due to invalid args', function(done) {
|
||||||
privkeyDao.hasPrivateKey({}, function(err) {
|
privkeyDao.hasPrivateKey({}).catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not find a key', function(done) {
|
||||||
|
var keyId = '12345';
|
||||||
|
|
||||||
|
restDaoStub.get.withArgs({
|
||||||
|
uri: '/privatekey/user/' + emailAddress + '/key/' + keyId + '?ignoreRecovery=true'
|
||||||
|
}).returns(rejects({
|
||||||
|
code: 404
|
||||||
|
}));
|
||||||
|
|
||||||
|
privkeyDao.hasPrivateKey({
|
||||||
|
userId: emailAddress,
|
||||||
|
keyId: keyId
|
||||||
|
}).then(function(found) {
|
||||||
|
expect(found).to.be.false;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should work', function(done) {
|
it('should work', function(done) {
|
||||||
var keyId = '12345';
|
var keyId = '12345';
|
||||||
|
|
||||||
restDaoStub.get.withArgs({
|
restDaoStub.get.withArgs({
|
||||||
uri: '/privatekey/user/' + emailAddress + '/key/' + keyId + '?ignoreRecovery=true'
|
uri: '/privatekey/user/' + emailAddress + '/key/' + keyId + '?ignoreRecovery=true'
|
||||||
}).yields();
|
}).returns(resolves());
|
||||||
|
|
||||||
privkeyDao.hasPrivateKey({
|
privkeyDao.hasPrivateKey({
|
||||||
userId: emailAddress,
|
userId: emailAddress,
|
||||||
keyId: keyId
|
keyId: keyId
|
||||||
}, function(err, found) {
|
}).then(function(found) {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(found).to.be.true;
|
expect(found).to.be.true;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -194,7 +214,7 @@ describe('Private Key DAO unit tests', function() {
|
||||||
|
|
||||||
describe('download', function() {
|
describe('download', function() {
|
||||||
it('should fail due to invalid args', function(done) {
|
it('should fail due to invalid args', function(done) {
|
||||||
privkeyDao.download({}, function(err) {
|
privkeyDao.download({}).catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -207,16 +227,13 @@ describe('Private Key DAO unit tests', function() {
|
||||||
|
|
||||||
restDaoStub.get.withArgs({
|
restDaoStub.get.withArgs({
|
||||||
uri: '/privatekey/user/' + emailAddress + '/key/' + key._id + '/recovery/token'
|
uri: '/privatekey/user/' + emailAddress + '/key/' + key._id + '/recovery/token'
|
||||||
}).yields();
|
}).returns(resolves());
|
||||||
|
|
||||||
privkeyDao.download({
|
privkeyDao.download({
|
||||||
userId: emailAddress,
|
userId: emailAddress,
|
||||||
keyId: key._id,
|
keyId: key._id,
|
||||||
recoveryToken: 'token'
|
recoveryToken: 'token'
|
||||||
}, function(err) {
|
}).then(done);
|
||||||
expect(err).to.not.exist;
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var RestDAO = require('../../../src/js/service/rest'),
|
var RestDAO = require('../../../src/js/service/rest'),
|
||||||
PublicKeyDAO = require('../../../src/js/service/publickey'),
|
PublicKeyDAO = require('../../../src/js/service/publickey');
|
||||||
appConfig = require('../../../src/js/app-config');
|
|
||||||
|
|
||||||
describe('Public Key DAO unit tests', function() {
|
describe('Public Key DAO unit tests', function() {
|
||||||
|
|
||||||
|
@ -10,32 +9,29 @@ describe('Public Key DAO unit tests', function() {
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
restDaoStub = sinon.createStubInstance(RestDAO);
|
restDaoStub = sinon.createStubInstance(RestDAO);
|
||||||
pubkeyDao = new PublicKeyDAO(restDaoStub, appConfig);
|
pubkeyDao = new PublicKeyDAO(restDaoStub);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {});
|
afterEach(function() {});
|
||||||
|
|
||||||
describe('get', function() {
|
describe('get', function() {
|
||||||
it('should fail', function(done) {
|
it('should fail', function(done) {
|
||||||
restDaoStub.get.yields(42);
|
restDaoStub.get.returns(rejects(42));
|
||||||
|
|
||||||
pubkeyDao.get('id', function(err, key) {
|
pubkeyDao.get('id').catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
expect(key).to.not.exist;
|
|
||||||
expect(restDaoStub.get.calledOnce).to.be.true;
|
expect(restDaoStub.get.calledOnce).to.be.true;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work', function(done) {
|
it('should work', function(done) {
|
||||||
restDaoStub.get.yields(null, {
|
restDaoStub.get.returns(resolves({
|
||||||
_id: '12345',
|
_id: '12345',
|
||||||
publicKey: 'asdf'
|
publicKey: 'asdf'
|
||||||
});
|
}));
|
||||||
|
|
||||||
pubkeyDao.get('id', function(err, key) {
|
pubkeyDao.get('id').then(function(key) {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(key).to.exist;
|
|
||||||
expect(key._id).to.exist;
|
expect(key._id).to.exist;
|
||||||
expect(key.publicKey).to.exist;
|
expect(key.publicKey).to.exist;
|
||||||
expect(restDaoStub.get.calledOnce).to.be.true;
|
expect(restDaoStub.get.calledOnce).to.be.true;
|
||||||
|
@ -46,31 +42,27 @@ describe('Public Key DAO unit tests', function() {
|
||||||
|
|
||||||
describe('verify', function() {
|
describe('verify', function() {
|
||||||
it('should fail', function(done) {
|
it('should fail', function(done) {
|
||||||
restDaoStub.get.yields(42);
|
restDaoStub.get.returns(rejects(42));
|
||||||
|
|
||||||
pubkeyDao.verify('id', function(err) {
|
pubkeyDao.verify('id').catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not error for 400', function(done) {
|
it('should not error for 400', function(done) {
|
||||||
restDaoStub.get.yields({
|
restDaoStub.get.returns(rejects({
|
||||||
code: 400
|
code: 400
|
||||||
});
|
}));
|
||||||
|
|
||||||
pubkeyDao.verify('id', function(err) {
|
pubkeyDao.verify('id').then(done);
|
||||||
expect(err).to.not.exist;
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work', function(done) {
|
it('should work', function(done) {
|
||||||
var uuid = 'c621e328-8548-40a1-8309-adf1955e98a9';
|
var uuid = 'c621e328-8548-40a1-8309-adf1955e98a9';
|
||||||
restDaoStub.get.yields(null);
|
restDaoStub.get.returns(resolves());
|
||||||
|
|
||||||
pubkeyDao.verify(uuid, function(err) {
|
pubkeyDao.verify(uuid).then(function() {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(restDaoStub.get.calledWith(sinon.match(function(arg) {
|
expect(restDaoStub.get.calledWith(sinon.match(function(arg) {
|
||||||
return arg.uri === '/verify/' + uuid && arg.type === 'text';
|
return arg.uri === '/verify/' + uuid && arg.type === 'text';
|
||||||
}))).to.be.true;
|
}))).to.be.true;
|
||||||
|
@ -81,23 +73,21 @@ describe('Public Key DAO unit tests', function() {
|
||||||
|
|
||||||
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.returns(rejects(42));
|
||||||
|
|
||||||
pubkeyDao.getByUserId('userId', function(err, key) {
|
pubkeyDao.getByUserId('userId').catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
expect(key).to.not.exist;
|
|
||||||
expect(restDaoStub.get.calledOnce).to.be.true;
|
expect(restDaoStub.get.calledOnce).to.be.true;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should react to 404', function(done) {
|
it('should react to 404', function(done) {
|
||||||
restDaoStub.get.yields({
|
restDaoStub.get.returns(resolves({
|
||||||
code: 404
|
code: 404
|
||||||
});
|
}));
|
||||||
|
|
||||||
pubkeyDao.getByUserId('userId', function(err, key) {
|
pubkeyDao.getByUserId('userId').then(function(key) {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(key).to.not.exist;
|
expect(key).to.not.exist;
|
||||||
expect(restDaoStub.get.calledOnce).to.be.true;
|
expect(restDaoStub.get.calledOnce).to.be.true;
|
||||||
done();
|
done();
|
||||||
|
@ -105,10 +95,9 @@ describe('Public Key DAO unit tests', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return empty array', function(done) {
|
it('should return empty array', function(done) {
|
||||||
restDaoStub.get.yields(null, []);
|
restDaoStub.get.returns(resolves([]));
|
||||||
|
|
||||||
pubkeyDao.getByUserId('userId', function(err, key) {
|
pubkeyDao.getByUserId('userId').then(function(key) {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(key).to.not.exist;
|
expect(key).to.not.exist;
|
||||||
expect(restDaoStub.get.calledOnce).to.be.true;
|
expect(restDaoStub.get.calledOnce).to.be.true;
|
||||||
done();
|
done();
|
||||||
|
@ -116,14 +105,12 @@ describe('Public Key DAO unit tests', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work', function(done) {
|
it('should work', function(done) {
|
||||||
restDaoStub.get.yields(null, [{
|
restDaoStub.get.returns(resolves([{
|
||||||
_id: '12345',
|
_id: '12345',
|
||||||
publicKey: 'asdf'
|
publicKey: 'asdf'
|
||||||
}]);
|
}]));
|
||||||
|
|
||||||
pubkeyDao.getByUserId('userId', function(err, key) {
|
pubkeyDao.getByUserId('userId').then(function(key) {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(key).to.exist;
|
|
||||||
expect(key._id).to.exist;
|
expect(key._id).to.exist;
|
||||||
expect(key.publicKey).to.exist;
|
expect(key.publicKey).to.exist;
|
||||||
expect(restDaoStub.get.calledOnce).to.be.true;
|
expect(restDaoStub.get.calledOnce).to.be.true;
|
||||||
|
@ -134,13 +121,12 @@ describe('Public Key DAO unit tests', function() {
|
||||||
|
|
||||||
describe('put', function() {
|
describe('put', function() {
|
||||||
it('should fail', function(done) {
|
it('should fail', function(done) {
|
||||||
restDaoStub.put.yields();
|
restDaoStub.put.returns(resolves());
|
||||||
|
|
||||||
pubkeyDao.put({
|
pubkeyDao.put({
|
||||||
_id: '12345',
|
_id: '12345',
|
||||||
publicKey: 'asdf'
|
publicKey: 'asdf'
|
||||||
}, function(err) {
|
}).then(function() {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(restDaoStub.put.calledOnce).to.be.true;
|
expect(restDaoStub.put.calledOnce).to.be.true;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -149,10 +135,9 @@ describe('Public Key DAO unit tests', function() {
|
||||||
|
|
||||||
describe('remove', function() {
|
describe('remove', function() {
|
||||||
it('should fail', function(done) {
|
it('should fail', function(done) {
|
||||||
restDaoStub.remove.yields();
|
restDaoStub.remove.returns(resolves());
|
||||||
|
|
||||||
pubkeyDao.remove('12345', function(err) {
|
pubkeyDao.remove('12345').then(function(err) {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(restDaoStub.remove.calledOnce).to.be.true;
|
expect(restDaoStub.remove.calledOnce).to.be.true;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,7 +7,7 @@ describe('Rest DAO unit tests', function() {
|
||||||
var restDao, xhrMock, requests;
|
var restDao, xhrMock, requests;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
restDao = new RestDAO();
|
restDao = new RestDAO(window.qMock);
|
||||||
xhrMock = sinon.useFakeXMLHttpRequest();
|
xhrMock = sinon.useFakeXMLHttpRequest();
|
||||||
requests = [];
|
requests = [];
|
||||||
|
|
||||||
|
@ -31,15 +31,14 @@ describe('Rest DAO unit tests', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('get', function() {
|
describe('get', function() {
|
||||||
it('should work with json as default type', function() {
|
it('should work with json as default type', function(done) {
|
||||||
restDao.get({
|
restDao.get({
|
||||||
uri: '/asdf'
|
uri: '/asdf'
|
||||||
}, function(err, data, status) {
|
}).then(function(data) {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(data.foo).to.equal('bar');
|
expect(data.foo).to.equal('bar');
|
||||||
var req = requests[0];
|
var req = requests[0];
|
||||||
expect(req.requestHeaders.Accept).to.equal('application/json');
|
expect(req.requestHeaders.Accept).to.equal('application/json');
|
||||||
expect(status).to.equal(200);
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(requests.length).to.equal(1);
|
expect(requests.length).to.equal(1);
|
||||||
|
@ -48,16 +47,15 @@ describe('Rest DAO unit tests', function() {
|
||||||
}, '{"foo": "bar"}');
|
}, '{"foo": "bar"}');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work with jsonz', function() {
|
it('should work with jsonz', function(done) {
|
||||||
restDao.get({
|
restDao.get({
|
||||||
uri: '/asdf',
|
uri: '/asdf',
|
||||||
type: 'json'
|
type: 'json'
|
||||||
}, function(err, data, status) {
|
}).then(function(data) {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(data.foo).to.equal('bar');
|
expect(data.foo).to.equal('bar');
|
||||||
var req = requests[0];
|
var req = requests[0];
|
||||||
expect(req.requestHeaders.Accept).to.equal('application/json');
|
expect(req.requestHeaders.Accept).to.equal('application/json');
|
||||||
expect(status).to.equal(200);
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(requests.length).to.equal(1);
|
expect(requests.length).to.equal(1);
|
||||||
|
@ -66,16 +64,15 @@ describe('Rest DAO unit tests', function() {
|
||||||
}, '{"foo": "bar"}');
|
}, '{"foo": "bar"}');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work with plain text', function() {
|
it('should work with plain text', function(done) {
|
||||||
restDao.get({
|
restDao.get({
|
||||||
uri: '/asdf',
|
uri: '/asdf',
|
||||||
type: 'text'
|
type: 'text'
|
||||||
}, function(err, data, status) {
|
}).then(function(data) {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(data).to.equal('foobar!');
|
expect(data).to.equal('foobar!');
|
||||||
var req = requests[0];
|
var req = requests[0];
|
||||||
expect(req.requestHeaders.Accept).to.equal('text/plain');
|
expect(req.requestHeaders.Accept).to.equal('text/plain');
|
||||||
expect(status).to.equal(200);
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(requests.length).to.equal(1);
|
expect(requests.length).to.equal(1);
|
||||||
|
@ -84,16 +81,15 @@ describe('Rest DAO unit tests', function() {
|
||||||
}, 'foobar!');
|
}, 'foobar!');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work with xml', function() {
|
it('should work with xml', function(done) {
|
||||||
restDao.get({
|
restDao.get({
|
||||||
uri: '/asdf',
|
uri: '/asdf',
|
||||||
type: 'xml'
|
type: 'xml'
|
||||||
}, function(err, data, status) {
|
}).then(function(data) {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(data).to.equal('<foo>bar</foo>');
|
expect(data).to.equal('<foo>bar</foo>');
|
||||||
var req = requests[0];
|
var req = requests[0];
|
||||||
expect(req.requestHeaders.Accept).to.equal('application/xml');
|
expect(req.requestHeaders.Accept).to.equal('application/xml');
|
||||||
expect(status).to.equal(200);
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(requests.length).to.equal(1);
|
expect(requests.length).to.equal(1);
|
||||||
|
@ -102,32 +98,29 @@ describe('Rest DAO unit tests', function() {
|
||||||
}, '<foo>bar</foo>');
|
}, '<foo>bar</foo>');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail for missing uri parameter', function() {
|
it('should fail for missing uri parameter', function(done) {
|
||||||
restDao.get({}, function(err, data) {
|
restDao.get({}).catch(function(err) {
|
||||||
expect(err).to.exist;
|
|
||||||
expect(err.code).to.equal(400);
|
expect(err.code).to.equal(400);
|
||||||
expect(data).to.not.exist;
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail for unhandled data type', function() {
|
it('should fail for unhandled data type', function(done) {
|
||||||
restDao.get({
|
restDao.get({
|
||||||
uri: '/asdf',
|
uri: '/asdf',
|
||||||
type: 'snafu'
|
type: 'snafu'
|
||||||
}, function(err, data) {
|
}).catch(function(err) {
|
||||||
expect(err).to.exist;
|
|
||||||
expect(err.code).to.equal(400);
|
expect(err.code).to.equal(400);
|
||||||
expect(data).to.not.exist;
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail for server error', function() {
|
it('should fail for server error', function(done) {
|
||||||
restDao.get({
|
restDao.get({
|
||||||
uri: '/asdf'
|
uri: '/asdf'
|
||||||
}, function(err, data) {
|
}).catch(function(err) {
|
||||||
expect(err).to.exist;
|
|
||||||
expect(err.code).to.equal(500);
|
expect(err.code).to.equal(500);
|
||||||
expect(data).to.not.exist;
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(requests.length).to.equal(1);
|
expect(requests.length).to.equal(1);
|
||||||
|
@ -138,10 +131,10 @@ describe('Rest DAO unit tests', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('post', function() {
|
describe('post', function() {
|
||||||
it('should fail', function() {
|
it('should fail', function(done) {
|
||||||
restDao.post('/asdf', {}, function(err) {
|
restDao.post('/asdf', {}).catch(function(err) {
|
||||||
expect(err).to.exist;
|
|
||||||
expect(err.code).to.equal(500);
|
expect(err.code).to.equal(500);
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(requests.length).to.equal(1);
|
expect(requests.length).to.equal(1);
|
||||||
|
@ -150,11 +143,10 @@ describe('Rest DAO unit tests', function() {
|
||||||
}, 'Internal error');
|
}, 'Internal error');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work', function() {
|
it('should work', function(done) {
|
||||||
restDao.post('/asdf', {}, function(err, res, status) {
|
restDao.post('/asdf', {}).then(function(res) {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(res).to.equal('');
|
expect(res).to.equal('');
|
||||||
expect(status).to.equal(201);
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(requests.length).to.equal(1);
|
expect(requests.length).to.equal(1);
|
||||||
|
@ -163,10 +155,10 @@ describe('Rest DAO unit tests', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('put', function() {
|
describe('put', function() {
|
||||||
it('should fail', function() {
|
it('should fail', function(done) {
|
||||||
restDao.put('/asdf', {}, function(err) {
|
restDao.put('/asdf', {}).catch(function(err) {
|
||||||
expect(err).to.exist;
|
|
||||||
expect(err.code).to.equal(500);
|
expect(err.code).to.equal(500);
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(requests.length).to.equal(1);
|
expect(requests.length).to.equal(1);
|
||||||
|
@ -175,11 +167,10 @@ describe('Rest DAO unit tests', function() {
|
||||||
}, 'Internal error');
|
}, 'Internal error');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work', function() {
|
it('should work', function(done) {
|
||||||
restDao.put('/asdf', {}, function(err, res, status) {
|
restDao.put('/asdf', {}).then(function(res) {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(res).to.equal('');
|
expect(res).to.equal('');
|
||||||
expect(status).to.equal(201);
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(requests.length).to.equal(1);
|
expect(requests.length).to.equal(1);
|
||||||
|
@ -188,10 +179,10 @@ describe('Rest DAO unit tests', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('remove', function() {
|
describe('remove', function() {
|
||||||
it('should fail', function() {
|
it('should fail', function(done) {
|
||||||
restDao.remove('/asdf', function(err) {
|
restDao.remove('/asdf').catch(function(err) {
|
||||||
expect(err).to.exist;
|
|
||||||
expect(err.code).to.equal(500);
|
expect(err.code).to.equal(500);
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(requests.length).to.equal(1);
|
expect(requests.length).to.equal(1);
|
||||||
|
@ -200,11 +191,10 @@ describe('Rest DAO unit tests', function() {
|
||||||
}, 'Internal error');
|
}, 'Internal error');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work', function() {
|
it('should work', function(done) {
|
||||||
restDao.remove('/asdf', function(err, res, status) {
|
restDao.remove('/asdf').then(function(res) {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(res).to.equal('');
|
expect(res).to.equal('');
|
||||||
expect(status).to.equal(200);
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(requests.length).to.equal(1);
|
expect(requests.length).to.equal(1);
|
||||||
|
|
|
@ -64,15 +64,14 @@ describe('Connection Doctor', function() {
|
||||||
|
|
||||||
describe('#_checkOnline', function() {
|
describe('#_checkOnline', function() {
|
||||||
it('should check if browser is online', function(done) {
|
it('should check if browser is online', function(done) {
|
||||||
doctor._checkOnline(function(error) {
|
if (navigator.onLine) {
|
||||||
if (navigator.onLine) {
|
doctor._checkOnline().then(done);
|
||||||
expect(error).to.not.exist;
|
} else {
|
||||||
} else {
|
doctor._checkOnline().catch(function(err) {
|
||||||
expect(error).to.exist;
|
expect(err.code).to.equal(ConnectionDoctor.OFFLINE);
|
||||||
expect(error.code).to.equal(ConnectionDoctor.OFFLINE);
|
done();
|
||||||
}
|
});
|
||||||
done();
|
}
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -80,8 +79,7 @@ describe('Connection Doctor', function() {
|
||||||
it('should be able to reach the host w/o cert', function(done) {
|
it('should be able to reach the host w/o cert', function(done) {
|
||||||
credentials.imap.ca = undefined;
|
credentials.imap.ca = undefined;
|
||||||
|
|
||||||
doctor._checkReachable(credentials.imap, function(error) {
|
doctor._checkReachable(credentials.imap).then(function() {
|
||||||
expect(error).to.not.exist;
|
|
||||||
expect(TCPSocket.open.calledOnce).to.be.true;
|
expect(TCPSocket.open.calledOnce).to.be.true;
|
||||||
expect(TCPSocket.open.calledWith(credentials.imap.host, credentials.imap.port, {
|
expect(TCPSocket.open.calledWith(credentials.imap.host, credentials.imap.port, {
|
||||||
binaryType: 'arraybuffer',
|
binaryType: 'arraybuffer',
|
||||||
|
@ -105,8 +103,7 @@ describe('Connection Doctor', function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
doctor._checkReachable(credentials.imap, function(error) {
|
doctor._checkReachable(credentials.imap).then(function() {
|
||||||
expect(error).to.not.exist;
|
|
||||||
expect(TCPSocket.open.calledOnce).to.be.true;
|
expect(TCPSocket.open.calledOnce).to.be.true;
|
||||||
expect(TCPSocket.open.calledWith(credentials.imap.host, credentials.imap.port, {
|
expect(TCPSocket.open.calledWith(credentials.imap.host, credentials.imap.port, {
|
||||||
binaryType: 'arraybuffer',
|
binaryType: 'arraybuffer',
|
||||||
|
@ -122,8 +119,7 @@ describe('Connection Doctor', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail w/ wrong cert', function(done) {
|
it('should fail w/ wrong cert', function(done) {
|
||||||
doctor._checkReachable(credentials.imap, function(error) {
|
doctor._checkReachable(credentials.imap).catch(function(error) {
|
||||||
expect(error).to.exist;
|
|
||||||
expect(error.code).to.equal(ConnectionDoctor.TLS_WRONG_CERT);
|
expect(error.code).to.equal(ConnectionDoctor.TLS_WRONG_CERT);
|
||||||
expect(TCPSocket.open.calledOnce).to.be.true;
|
expect(TCPSocket.open.calledOnce).to.be.true;
|
||||||
expect(TCPSocket.open.calledWith(credentials.imap.host, credentials.imap.port, {
|
expect(TCPSocket.open.calledWith(credentials.imap.host, credentials.imap.port, {
|
||||||
|
@ -142,8 +138,7 @@ describe('Connection Doctor', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail w/ host unreachable', function(done) {
|
it('should fail w/ host unreachable', function(done) {
|
||||||
doctor._checkReachable(credentials.imap, function(error) {
|
doctor._checkReachable(credentials.imap).catch(function(error) {
|
||||||
expect(error).to.exist;
|
|
||||||
expect(error.code).to.equal(ConnectionDoctor.HOST_UNREACHABLE);
|
expect(error.code).to.equal(ConnectionDoctor.HOST_UNREACHABLE);
|
||||||
expect(TCPSocket.open.calledOnce).to.be.true;
|
expect(TCPSocket.open.calledOnce).to.be.true;
|
||||||
|
|
||||||
|
@ -160,8 +155,7 @@ describe('Connection Doctor', function() {
|
||||||
var origTimeout = cfg.connDocTimeout; // remember timeout from the config to reset it on done
|
var origTimeout = cfg.connDocTimeout; // remember timeout from the config to reset it on done
|
||||||
cfg.connDocTimeout = 20; // set to 20ms for the test
|
cfg.connDocTimeout = 20; // set to 20ms for the test
|
||||||
|
|
||||||
doctor._checkReachable(credentials.imap, function(error) {
|
doctor._checkReachable(credentials.imap).catch(function(error) {
|
||||||
expect(error).to.exist;
|
|
||||||
expect(error.code).to.equal(ConnectionDoctor.HOST_TIMEOUT);
|
expect(error.code).to.equal(ConnectionDoctor.HOST_TIMEOUT);
|
||||||
expect(TCPSocket.open.calledOnce).to.be.true;
|
expect(TCPSocket.open.calledOnce).to.be.true;
|
||||||
cfg.connDocTimeout = origTimeout;
|
cfg.connDocTimeout = origTimeout;
|
||||||
|
@ -179,8 +173,7 @@ describe('Connection Doctor', function() {
|
||||||
});
|
});
|
||||||
imapStub.logout.yieldsAsync();
|
imapStub.logout.yieldsAsync();
|
||||||
|
|
||||||
doctor._checkImap(function(error) {
|
doctor._checkImap().then(function() {
|
||||||
expect(error).to.not.exist;
|
|
||||||
expect(imapStub.login.calledOnce).to.be.true;
|
expect(imapStub.login.calledOnce).to.be.true;
|
||||||
expect(imapStub.listWellKnownFolders.calledOnce).to.be.true;
|
expect(imapStub.listWellKnownFolders.calledOnce).to.be.true;
|
||||||
expect(imapStub.logout.calledOnce).to.be.true;
|
expect(imapStub.logout.calledOnce).to.be.true;
|
||||||
|
@ -195,8 +188,7 @@ describe('Connection Doctor', function() {
|
||||||
Inbox: [{}]
|
Inbox: [{}]
|
||||||
});
|
});
|
||||||
|
|
||||||
doctor._checkImap(function(error) {
|
doctor._checkImap().catch(function(error) {
|
||||||
expect(error).to.exist;
|
|
||||||
expect(error.code).to.equal(ConnectionDoctor.GENERIC_ERROR);
|
expect(error.code).to.equal(ConnectionDoctor.GENERIC_ERROR);
|
||||||
expect(error.underlyingError).to.exist;
|
expect(error.underlyingError).to.exist;
|
||||||
expect(imapStub.login.calledOnce).to.be.true;
|
expect(imapStub.login.calledOnce).to.be.true;
|
||||||
|
@ -218,8 +210,7 @@ describe('Connection Doctor', function() {
|
||||||
Inbox: []
|
Inbox: []
|
||||||
});
|
});
|
||||||
|
|
||||||
doctor._checkImap(function(error) {
|
doctor._checkImap().catch(function(error) {
|
||||||
expect(error).to.exist;
|
|
||||||
expect(error.code).to.equal(ConnectionDoctor.NO_INBOX);
|
expect(error.code).to.equal(ConnectionDoctor.NO_INBOX);
|
||||||
expect(imapStub.login.calledOnce).to.be.true;
|
expect(imapStub.login.calledOnce).to.be.true;
|
||||||
expect(imapStub.listWellKnownFolders.calledOnce).to.be.true;
|
expect(imapStub.listWellKnownFolders.calledOnce).to.be.true;
|
||||||
|
@ -233,8 +224,7 @@ describe('Connection Doctor', function() {
|
||||||
imapStub.login.yieldsAsync();
|
imapStub.login.yieldsAsync();
|
||||||
imapStub.listWellKnownFolders.yieldsAsync(new Error());
|
imapStub.listWellKnownFolders.yieldsAsync(new Error());
|
||||||
|
|
||||||
doctor._checkImap(function(error) {
|
doctor._checkImap().catch(function(error) {
|
||||||
expect(error).to.exist;
|
|
||||||
expect(error.code).to.equal(ConnectionDoctor.GENERIC_ERROR);
|
expect(error.code).to.equal(ConnectionDoctor.GENERIC_ERROR);
|
||||||
expect(error.underlyingError).to.exist;
|
expect(error.underlyingError).to.exist;
|
||||||
expect(imapStub.login.calledOnce).to.be.true;
|
expect(imapStub.login.calledOnce).to.be.true;
|
||||||
|
@ -246,8 +236,7 @@ describe('Connection Doctor', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail w/ auth rejected', function(done) {
|
it('should fail w/ auth rejected', function(done) {
|
||||||
doctor._checkImap(function(error) {
|
doctor._checkImap().catch(function(error) {
|
||||||
expect(error).to.exist;
|
|
||||||
expect(error.code).to.equal(ConnectionDoctor.AUTH_REJECTED);
|
expect(error.code).to.equal(ConnectionDoctor.AUTH_REJECTED);
|
||||||
expect(error.underlyingError).to.exist;
|
expect(error.underlyingError).to.exist;
|
||||||
expect(imapStub.login.calledOnce).to.be.true;
|
expect(imapStub.login.calledOnce).to.be.true;
|
||||||
|
@ -266,8 +255,7 @@ describe('Connection Doctor', function() {
|
||||||
|
|
||||||
describe('#_checkSmtp', function() {
|
describe('#_checkSmtp', function() {
|
||||||
it('should perform SMTP login, logout', function(done) {
|
it('should perform SMTP login, logout', function(done) {
|
||||||
doctor._checkSmtp(function(error) {
|
doctor._checkSmtp().then(function() {
|
||||||
expect(error).to.not.exist;
|
|
||||||
expect(smtpStub.connect.calledOnce).to.be.true;
|
expect(smtpStub.connect.calledOnce).to.be.true;
|
||||||
expect(smtpStub.quit.calledOnce).to.be.true;
|
expect(smtpStub.quit.calledOnce).to.be.true;
|
||||||
|
|
||||||
|
@ -279,8 +267,7 @@ describe('Connection Doctor', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail w/ auth rejected', function(done) {
|
it('should fail w/ auth rejected', function(done) {
|
||||||
doctor._checkSmtp(function(error) {
|
doctor._checkSmtp().catch(function(error) {
|
||||||
expect(error).to.exist;
|
|
||||||
expect(error.code).to.equal(ConnectionDoctor.AUTH_REJECTED);
|
expect(error.code).to.equal(ConnectionDoctor.AUTH_REJECTED);
|
||||||
expect(error.underlyingError).to.exist;
|
expect(error.underlyingError).to.exist;
|
||||||
expect(smtpStub.connect.calledOnce).to.be.true;
|
expect(smtpStub.connect.calledOnce).to.be.true;
|
||||||
|
@ -302,14 +289,13 @@ describe('Connection Doctor', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should perform all tests', function(done) {
|
it('should perform all tests', function(done) {
|
||||||
doctor._checkOnline.yieldsAsync();
|
doctor._checkOnline.returns(resolves());
|
||||||
doctor._checkReachable.withArgs(credentials.imap).yieldsAsync();
|
doctor._checkReachable.withArgs(credentials.imap).returns(resolves());
|
||||||
doctor._checkReachable.withArgs(credentials.smtp).yieldsAsync();
|
doctor._checkReachable.withArgs(credentials.smtp).returns(resolves());
|
||||||
doctor._checkImap.yieldsAsync();
|
doctor._checkImap.returns(resolves());
|
||||||
doctor._checkSmtp.yieldsAsync();
|
doctor._checkSmtp.returns(resolves());
|
||||||
|
|
||||||
doctor.check(function(err) {
|
doctor.check().then(function() {
|
||||||
expect(err).to.not.exist;
|
|
||||||
expect(doctor._checkOnline.calledOnce).to.be.true;
|
expect(doctor._checkOnline.calledOnce).to.be.true;
|
||||||
expect(doctor._checkReachable.calledTwice).to.be.true;
|
expect(doctor._checkReachable.calledTwice).to.be.true;
|
||||||
expect(doctor._checkImap.calledOnce).to.be.true;
|
expect(doctor._checkImap.calledOnce).to.be.true;
|
||||||
|
@ -320,13 +306,13 @@ describe('Connection Doctor', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail for smtp', function(done) {
|
it('should fail for smtp', function(done) {
|
||||||
doctor._checkOnline.yieldsAsync();
|
doctor._checkOnline.returns(resolves());
|
||||||
doctor._checkReachable.withArgs(credentials.imap).yieldsAsync();
|
doctor._checkReachable.withArgs(credentials.imap).returns(resolves());
|
||||||
doctor._checkReachable.withArgs(credentials.smtp).yieldsAsync();
|
doctor._checkReachable.withArgs(credentials.smtp).returns(resolves());
|
||||||
doctor._checkImap.yieldsAsync();
|
doctor._checkImap.returns(resolves());
|
||||||
doctor._checkSmtp.yieldsAsync(new Error());
|
doctor._checkSmtp.returns(rejects(new Error()));
|
||||||
|
|
||||||
doctor.check(function(err) {
|
doctor.check().catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
expect(doctor._checkOnline.calledOnce).to.be.true;
|
expect(doctor._checkOnline.calledOnce).to.be.true;
|
||||||
expect(doctor._checkReachable.calledTwice).to.be.true;
|
expect(doctor._checkReachable.calledTwice).to.be.true;
|
||||||
|
@ -338,12 +324,12 @@ describe('Connection Doctor', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail for imap', function(done) {
|
it('should fail for imap', function(done) {
|
||||||
doctor._checkOnline.yieldsAsync();
|
doctor._checkOnline.returns(resolves());
|
||||||
doctor._checkReachable.withArgs(credentials.imap).yieldsAsync();
|
doctor._checkReachable.withArgs(credentials.imap).returns(resolves());
|
||||||
doctor._checkReachable.withArgs(credentials.smtp).yieldsAsync();
|
doctor._checkReachable.withArgs(credentials.smtp).returns(resolves());
|
||||||
doctor._checkImap.yieldsAsync(new Error());
|
doctor._checkImap.returns(rejects(new Error()));
|
||||||
|
|
||||||
doctor.check(function(err) {
|
doctor.check().catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
expect(doctor._checkOnline.calledOnce).to.be.true;
|
expect(doctor._checkOnline.calledOnce).to.be.true;
|
||||||
expect(doctor._checkReachable.calledTwice).to.be.true;
|
expect(doctor._checkReachable.calledTwice).to.be.true;
|
||||||
|
@ -355,11 +341,11 @@ describe('Connection Doctor', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail for smtp reachability', function(done) {
|
it('should fail for smtp reachability', function(done) {
|
||||||
doctor._checkOnline.yieldsAsync();
|
doctor._checkOnline.returns(resolves());
|
||||||
doctor._checkReachable.withArgs(credentials.imap).yieldsAsync();
|
doctor._checkReachable.withArgs(credentials.imap).returns(resolves());
|
||||||
doctor._checkReachable.withArgs(credentials.smtp).yieldsAsync(new Error());
|
doctor._checkReachable.withArgs(credentials.smtp).returns(rejects(new Error()));
|
||||||
|
|
||||||
doctor.check(function(err) {
|
doctor.check().catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
expect(doctor._checkOnline.calledOnce).to.be.true;
|
expect(doctor._checkOnline.calledOnce).to.be.true;
|
||||||
expect(doctor._checkReachable.calledTwice).to.be.true;
|
expect(doctor._checkReachable.calledTwice).to.be.true;
|
||||||
|
@ -371,10 +357,10 @@ describe('Connection Doctor', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail for imap reachability', function(done) {
|
it('should fail for imap reachability', function(done) {
|
||||||
doctor._checkOnline.yieldsAsync();
|
doctor._checkOnline.returns(resolves());
|
||||||
doctor._checkReachable.withArgs(credentials.imap).yieldsAsync(new Error());
|
doctor._checkReachable.withArgs(credentials.imap).returns(rejects(new Error()));
|
||||||
|
|
||||||
doctor.check(function(err) {
|
doctor.check().catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
expect(doctor._checkOnline.calledOnce).to.be.true;
|
expect(doctor._checkOnline.calledOnce).to.be.true;
|
||||||
expect(doctor._checkReachable.calledOnce).to.be.true;
|
expect(doctor._checkReachable.calledOnce).to.be.true;
|
||||||
|
@ -386,9 +372,9 @@ describe('Connection Doctor', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail for offline', function(done) {
|
it('should fail for offline', function(done) {
|
||||||
doctor._checkOnline.yieldsAsync(new Error());
|
doctor._checkOnline.returns(rejects(new Error()));
|
||||||
|
|
||||||
doctor.check(function(err) {
|
doctor.check().catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
expect(doctor._checkOnline.calledOnce).to.be.true;
|
expect(doctor._checkOnline.calledOnce).to.be.true;
|
||||||
expect(doctor._checkReachable.called).to.be.false;
|
expect(doctor._checkReachable.called).to.be.false;
|
||||||
|
@ -402,7 +388,7 @@ describe('Connection Doctor', function() {
|
||||||
it('should fail w/o config', function(done) {
|
it('should fail w/o config', function(done) {
|
||||||
doctor.credentials = doctor._imap = doctor._smtp = undefined;
|
doctor.credentials = doctor._imap = doctor._smtp = undefined;
|
||||||
|
|
||||||
doctor.check(function(err) {
|
doctor.check().catch(function(err) {
|
||||||
expect(err).to.exist;
|
expect(err).to.exist;
|
||||||
expect(doctor._checkOnline.called).to.be.false;
|
expect(doctor._checkOnline.called).to.be.false;
|
||||||
expect(doctor._checkReachable.called).to.be.false;
|
expect(doctor._checkReachable.called).to.be.false;
|
||||||
|
|
|
@ -40,10 +40,9 @@ describe('UpdateHandler', function() {
|
||||||
|
|
||||||
it('should not update when up to date', function(done) {
|
it('should not update when up to date', function(done) {
|
||||||
cfg.dbVersion = 10; // app requires database version 10
|
cfg.dbVersion = 10; // app requires database version 10
|
||||||
appConfigStorageStub.listItems.withArgs(versionDbType).yieldsAsync(null, ['10']); // database version is 10
|
appConfigStorageStub.listItems.withArgs(versionDbType).returns(resolves(['10'])); // database version is 10
|
||||||
|
|
||||||
updateHandler.update(function(error) {
|
updateHandler.update().then(function() {
|
||||||
expect(error).to.not.exist;
|
|
||||||
expect(appConfigStorageStub.listItems.calledOnce).to.be.true;
|
expect(appConfigStorageStub.listItems.calledOnce).to.be.true;
|
||||||
|
|
||||||
done();
|
done();
|
||||||
|
@ -55,7 +54,7 @@ describe('UpdateHandler', function() {
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
updateCounter = 0;
|
updateCounter = 0;
|
||||||
appConfigStorageStub.listItems.withArgs(versionDbType).yieldsAsync(null, ['2']); // database version is 0
|
appConfigStorageStub.listItems.withArgs(versionDbType).returns(resolves(['2'])); // database version is 0
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(function() {
|
||||||
|
@ -67,17 +66,16 @@ describe('UpdateHandler', function() {
|
||||||
cfg.dbVersion = 4; // app requires database version 4
|
cfg.dbVersion = 4; // app requires database version 4
|
||||||
|
|
||||||
// a simple dummy update to executed that only increments the update counter
|
// a simple dummy update to executed that only increments the update counter
|
||||||
function dummyUpdate(options, callback) {
|
function dummyUpdate() {
|
||||||
updateCounter++;
|
updateCounter++;
|
||||||
callback();
|
return resolves();
|
||||||
}
|
}
|
||||||
|
|
||||||
// inject the dummy updates instead of live ones
|
// inject the dummy updates instead of live ones
|
||||||
updateHandler._updateScripts = [dummyUpdate, dummyUpdate, dummyUpdate, dummyUpdate];
|
updateHandler._updateScripts = [dummyUpdate, dummyUpdate, dummyUpdate, dummyUpdate];
|
||||||
|
|
||||||
// execute test
|
// execute test
|
||||||
updateHandler.update(function(error) {
|
updateHandler.update().then(function() {
|
||||||
expect(error).to.not.exist;
|
|
||||||
expect(updateCounter).to.equal(2);
|
expect(updateCounter).to.equal(2);
|
||||||
|
|
||||||
done();
|
done();
|
||||||
|
@ -87,20 +85,20 @@ describe('UpdateHandler', function() {
|
||||||
it('should fail while updating to v3', function(done) {
|
it('should fail while updating to v3', function(done) {
|
||||||
cfg.dbVersion = 4; // app requires database version 4
|
cfg.dbVersion = 4; // app requires database version 4
|
||||||
|
|
||||||
function dummyUpdate(options, callback) {
|
function dummyUpdate() {
|
||||||
updateCounter++;
|
updateCounter++;
|
||||||
callback();
|
return resolves();
|
||||||
}
|
}
|
||||||
|
|
||||||
function failingUpdate(options, callback) {
|
function failingUpdate() {
|
||||||
callback({});
|
return rejects({});
|
||||||
}
|
}
|
||||||
|
|
||||||
// inject the dummy updates instead of live ones
|
// inject the dummy updates instead of live ones
|
||||||
updateHandler._updateScripts = [dummyUpdate, dummyUpdate, failingUpdate, dummyUpdate];
|
updateHandler._updateScripts = [dummyUpdate, dummyUpdate, failingUpdate, dummyUpdate];
|
||||||
|
|
||||||
// execute test
|
// execute test
|
||||||
updateHandler.update(function(error) {
|
updateHandler.update().catch(function(error) {
|
||||||
expect(error).to.exist;
|
expect(error).to.exist;
|
||||||
expect(updateCounter).to.equal(0);
|
expect(updateCounter).to.equal(0);
|
||||||
|
|
||||||
|
@ -115,7 +113,7 @@ describe('UpdateHandler', function() {
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
cfg.dbVersion = 1; // app requires database version 1
|
cfg.dbVersion = 1; // app requires database version 1
|
||||||
appConfigStorageStub.listItems.withArgs(versionDbType).yieldsAsync(); // database version is 0
|
appConfigStorageStub.listItems.withArgs(versionDbType).returns(resolves()); // database version is 0
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(function() {
|
||||||
|
@ -125,11 +123,10 @@ describe('UpdateHandler', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work', function(done) {
|
it('should work', function(done) {
|
||||||
userStorageStub.removeList.withArgs(emailDbType).yieldsAsync();
|
userStorageStub.removeList.withArgs(emailDbType).returns(resolves());
|
||||||
appConfigStorageStub.storeList.withArgs([1], versionDbType).yieldsAsync();
|
appConfigStorageStub.storeList.withArgs([1], versionDbType).returns(resolves());
|
||||||
|
|
||||||
updateHandler.update(function(error) {
|
updateHandler.update().then(function() {
|
||||||
expect(error).to.not.exist;
|
|
||||||
expect(userStorageStub.removeList.calledOnce).to.be.true;
|
expect(userStorageStub.removeList.calledOnce).to.be.true;
|
||||||
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
|
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
|
||||||
|
|
||||||
|
@ -138,10 +135,10 @@ describe('UpdateHandler', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail when persisting database version fails', function(done) {
|
it('should fail when persisting database version fails', function(done) {
|
||||||
userStorageStub.removeList.yieldsAsync();
|
userStorageStub.removeList.returns(resolves());
|
||||||
appConfigStorageStub.storeList.yieldsAsync(new Error());
|
appConfigStorageStub.storeList.returns(rejects(new Error()));
|
||||||
|
|
||||||
updateHandler.update(function(error) {
|
updateHandler.update().catch(function(error) {
|
||||||
expect(error).to.exist;
|
expect(error).to.exist;
|
||||||
expect(userStorageStub.removeList.calledOnce).to.be.true;
|
expect(userStorageStub.removeList.calledOnce).to.be.true;
|
||||||
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
|
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
|
||||||
|
@ -151,9 +148,9 @@ describe('UpdateHandler', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail when wiping emails from database fails', function(done) {
|
it('should fail when wiping emails from database fails', function(done) {
|
||||||
userStorageStub.removeList.yieldsAsync(new Error());
|
userStorageStub.removeList.returns(rejects(new Error()));
|
||||||
|
|
||||||
updateHandler.update(function(error) {
|
updateHandler.update().catch(function(error) {
|
||||||
expect(error).to.exist;
|
expect(error).to.exist;
|
||||||
expect(userStorageStub.removeList.calledOnce).to.be.true;
|
expect(userStorageStub.removeList.calledOnce).to.be.true;
|
||||||
expect(appConfigStorageStub.storeList.called).to.be.false;
|
expect(appConfigStorageStub.storeList.called).to.be.false;
|
||||||
|
@ -168,7 +165,7 @@ describe('UpdateHandler', function() {
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
cfg.dbVersion = 2; // app requires database version 2
|
cfg.dbVersion = 2; // app requires database version 2
|
||||||
appConfigStorageStub.listItems.withArgs(versionDbType).yieldsAsync(null, [1]); // database version is 0
|
appConfigStorageStub.listItems.withArgs(versionDbType).returns(resolves([1])); // database version is 0
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(function() {
|
||||||
|
@ -178,11 +175,10 @@ describe('UpdateHandler', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work', function(done) {
|
it('should work', function(done) {
|
||||||
userStorageStub.removeList.withArgs(emailDbType).yieldsAsync();
|
userStorageStub.removeList.withArgs(emailDbType).returns(resolves());
|
||||||
appConfigStorageStub.storeList.withArgs([2], versionDbType).yieldsAsync();
|
appConfigStorageStub.storeList.withArgs([2], versionDbType).returns(resolves());
|
||||||
|
|
||||||
updateHandler.update(function(error) {
|
updateHandler.update().then(function() {
|
||||||
expect(error).to.not.exist;
|
|
||||||
expect(userStorageStub.removeList.calledOnce).to.be.true;
|
expect(userStorageStub.removeList.calledOnce).to.be.true;
|
||||||
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
|
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
|
||||||
|
|
||||||
|
@ -191,10 +187,10 @@ describe('UpdateHandler', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail when persisting database version fails', function(done) {
|
it('should fail when persisting database version fails', function(done) {
|
||||||
userStorageStub.removeList.yieldsAsync();
|
userStorageStub.removeList.returns(resolves());
|
||||||
appConfigStorageStub.storeList.yieldsAsync(new Error());
|
appConfigStorageStub.storeList.returns(rejects(new Error()));
|
||||||
|
|
||||||
updateHandler.update(function(error) {
|
updateHandler.update().catch(function(error) {
|
||||||
expect(error).to.exist;
|
expect(error).to.exist;
|
||||||
expect(userStorageStub.removeList.calledOnce).to.be.true;
|
expect(userStorageStub.removeList.calledOnce).to.be.true;
|
||||||
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
|
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
|
||||||
|
@ -204,9 +200,9 @@ describe('UpdateHandler', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail when wiping emails from database fails', function(done) {
|
it('should fail when wiping emails from database fails', function(done) {
|
||||||
userStorageStub.removeList.yieldsAsync(new Error());
|
userStorageStub.removeList.returns(rejects(new Error()));
|
||||||
|
|
||||||
updateHandler.update(function(error) {
|
updateHandler.update().catch(function(error) {
|
||||||
expect(error).to.exist;
|
expect(error).to.exist;
|
||||||
expect(userStorageStub.removeList.calledOnce).to.be.true;
|
expect(userStorageStub.removeList.calledOnce).to.be.true;
|
||||||
expect(appConfigStorageStub.storeList.called).to.be.false;
|
expect(appConfigStorageStub.storeList.called).to.be.false;
|
||||||
|
@ -221,7 +217,7 @@ describe('UpdateHandler', function() {
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
cfg.dbVersion = 3; // app requires database version 2
|
cfg.dbVersion = 3; // app requires database version 2
|
||||||
appConfigStorageStub.listItems.withArgs(versionDbType).yieldsAsync(null, [2]); // database version is 0
|
appConfigStorageStub.listItems.withArgs(versionDbType).returns(resolves([2])); // database version is 0
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(function() {
|
||||||
|
@ -231,11 +227,10 @@ describe('UpdateHandler', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work', function(done) {
|
it('should work', function(done) {
|
||||||
userStorageStub.removeList.withArgs(emailDbType).yieldsAsync();
|
userStorageStub.removeList.withArgs(emailDbType).returns(resolves());
|
||||||
appConfigStorageStub.storeList.withArgs([3], versionDbType).yieldsAsync();
|
appConfigStorageStub.storeList.withArgs([3], versionDbType).returns(resolves());
|
||||||
|
|
||||||
updateHandler.update(function(error) {
|
updateHandler.update().then(function() {
|
||||||
expect(error).to.not.exist;
|
|
||||||
expect(userStorageStub.removeList.calledOnce).to.be.true;
|
expect(userStorageStub.removeList.calledOnce).to.be.true;
|
||||||
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
|
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
|
||||||
|
|
||||||
|
@ -244,10 +239,10 @@ describe('UpdateHandler', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail when persisting database version fails', function(done) {
|
it('should fail when persisting database version fails', function(done) {
|
||||||
userStorageStub.removeList.yieldsAsync();
|
userStorageStub.removeList.returns(resolves());
|
||||||
appConfigStorageStub.storeList.yieldsAsync(new Error());
|
appConfigStorageStub.storeList.returns(rejects(new Error()));
|
||||||
|
|
||||||
updateHandler.update(function(error) {
|
updateHandler.update().catch(function(error) {
|
||||||
expect(error).to.exist;
|
expect(error).to.exist;
|
||||||
expect(userStorageStub.removeList.calledOnce).to.be.true;
|
expect(userStorageStub.removeList.calledOnce).to.be.true;
|
||||||
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
|
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
|
||||||
|
@ -257,9 +252,9 @@ describe('UpdateHandler', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail when wiping emails from database fails', function(done) {
|
it('should fail when wiping emails from database fails', function(done) {
|
||||||
userStorageStub.removeList.yieldsAsync(new Error());
|
userStorageStub.removeList.returns(rejects(new Error()));
|
||||||
|
|
||||||
updateHandler.update(function(error) {
|
updateHandler.update().catch(function(error) {
|
||||||
expect(error).to.exist;
|
expect(error).to.exist;
|
||||||
expect(userStorageStub.removeList.calledOnce).to.be.true;
|
expect(userStorageStub.removeList.calledOnce).to.be.true;
|
||||||
expect(appConfigStorageStub.storeList.called).to.be.false;
|
expect(appConfigStorageStub.storeList.called).to.be.false;
|
||||||
|
@ -290,22 +285,21 @@ describe('UpdateHandler', function() {
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
cfg.dbVersion = 4; // app requires database version 4
|
cfg.dbVersion = 4; // app requires database version 4
|
||||||
appConfigStorageStub.listItems.withArgs(versionDbType).yieldsAsync(null, [3]); // database version is 3
|
appConfigStorageStub.listItems.withArgs(versionDbType).returns(resolves([3])); // database version is 3
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should add gmail as mail service provider with email address and no provider present in db', function(done) {
|
it('should add gmail as mail service provider with email address and no provider present in db', function(done) {
|
||||||
appConfigStorageStub.listItems.withArgs(EMAIL_ADDR_DB_KEY).yieldsAsync(null, [emailaddress]);
|
appConfigStorageStub.listItems.withArgs(EMAIL_ADDR_DB_KEY).returns(resolves([emailaddress]));
|
||||||
appConfigStorageStub.listItems.withArgs(PROVIDER_DB_KEY).yieldsAsync(null, []);
|
appConfigStorageStub.listItems.withArgs(PROVIDER_DB_KEY).returns(resolves([]));
|
||||||
appConfigStorageStub.storeList.withArgs([4], versionDbType).yieldsAsync();
|
appConfigStorageStub.storeList.withArgs([4], versionDbType).returns(resolves());
|
||||||
appConfigStorageStub.storeList.withArgs(['gmail'], PROVIDER_DB_KEY).yieldsAsync();
|
appConfigStorageStub.storeList.withArgs(['gmail'], PROVIDER_DB_KEY).returns(resolves());
|
||||||
appConfigStorageStub.storeList.withArgs([emailaddress], USERNAME_DB_KEY).yieldsAsync();
|
appConfigStorageStub.storeList.withArgs([emailaddress], USERNAME_DB_KEY).returns(resolves());
|
||||||
appConfigStorageStub.storeList.withArgs([imap], IMAP_DB_KEY).yieldsAsync();
|
appConfigStorageStub.storeList.withArgs([imap], IMAP_DB_KEY).returns(resolves());
|
||||||
appConfigStorageStub.storeList.withArgs([smtp], SMTP_DB_KEY).yieldsAsync();
|
appConfigStorageStub.storeList.withArgs([smtp], SMTP_DB_KEY).returns(resolves());
|
||||||
appConfigStorageStub.storeList.withArgs([''], REALNAME_DB_KEY).yieldsAsync();
|
appConfigStorageStub.storeList.withArgs([''], REALNAME_DB_KEY).returns(resolves());
|
||||||
authStub._loadCredentials.yieldsAsync();
|
authStub._loadCredentials.returns(resolves());
|
||||||
|
|
||||||
updateHandler.update(function(error) {
|
updateHandler.update().then(function() {
|
||||||
expect(error).to.not.exist;
|
|
||||||
expect(appConfigStorageStub.storeList.callCount).to.equal(6);
|
expect(appConfigStorageStub.storeList.callCount).to.equal(6);
|
||||||
expect(appConfigStorageStub.listItems.calledThrice).to.be.true;
|
expect(appConfigStorageStub.listItems.calledThrice).to.be.true;
|
||||||
expect(authStub._loadCredentials.calledOnce).to.be.true;
|
expect(authStub._loadCredentials.calledOnce).to.be.true;
|
||||||
|
@ -315,12 +309,11 @@ describe('UpdateHandler', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not add a provider when no email adress is in db', function(done) {
|
it('should not add a provider when no email adress is in db', function(done) {
|
||||||
appConfigStorageStub.listItems.withArgs(EMAIL_ADDR_DB_KEY).yieldsAsync(null, []);
|
appConfigStorageStub.listItems.withArgs(EMAIL_ADDR_DB_KEY).returns(resolves([]));
|
||||||
appConfigStorageStub.listItems.withArgs(PROVIDER_DB_KEY).yieldsAsync(null, []);
|
appConfigStorageStub.listItems.withArgs(PROVIDER_DB_KEY).returns(resolves([]));
|
||||||
appConfigStorageStub.storeList.withArgs([4], versionDbType).yieldsAsync();
|
appConfigStorageStub.storeList.withArgs([4], versionDbType).returns(resolves());
|
||||||
|
|
||||||
updateHandler.update(function(error) {
|
updateHandler.update().then(function() {
|
||||||
expect(error).to.not.exist;
|
|
||||||
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
|
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
|
||||||
expect(appConfigStorageStub.listItems.calledThrice).to.be.true;
|
expect(appConfigStorageStub.listItems.calledThrice).to.be.true;
|
||||||
|
|
||||||
|
@ -329,10 +322,10 @@ describe('UpdateHandler', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail when appConfigStore write fails', function(done) {
|
it('should fail when appConfigStore write fails', function(done) {
|
||||||
appConfigStorageStub.listItems.yieldsAsync(null, []);
|
appConfigStorageStub.listItems.returns(resolves([]));
|
||||||
appConfigStorageStub.storeList.yieldsAsync(new Error());
|
appConfigStorageStub.storeList.returns(rejects(new Error()));
|
||||||
|
|
||||||
updateHandler.update(function(error) {
|
updateHandler.update().catch(function(error) {
|
||||||
expect(error).to.exist;
|
expect(error).to.exist;
|
||||||
expect(appConfigStorageStub.listItems.calledThrice).to.be.true;
|
expect(appConfigStorageStub.listItems.calledThrice).to.be.true;
|
||||||
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
|
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
|
||||||
|
@ -342,10 +335,10 @@ describe('UpdateHandler', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail when appConfigStore read fails', function(done) {
|
it('should fail when appConfigStore read fails', function(done) {
|
||||||
appConfigStorageStub.listItems.withArgs(EMAIL_ADDR_DB_KEY).yieldsAsync(new Error());
|
appConfigStorageStub.listItems.withArgs(EMAIL_ADDR_DB_KEY).returns(rejects(new Error()));
|
||||||
appConfigStorageStub.storeList.yieldsAsync(new Error());
|
appConfigStorageStub.storeList.returns(rejects(new Error()));
|
||||||
|
|
||||||
updateHandler.update(function(error) {
|
updateHandler.update().catch(function(error) {
|
||||||
expect(error).to.exist;
|
expect(error).to.exist;
|
||||||
expect(appConfigStorageStub.listItems.calledTwice).to.be.true;
|
expect(appConfigStorageStub.listItems.calledTwice).to.be.true;
|
||||||
expect(appConfigStorageStub.storeList.called).to.be.false;
|
expect(appConfigStorageStub.storeList.called).to.be.false;
|
||||||
|
@ -368,7 +361,7 @@ describe('UpdateHandler', function() {
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
cfg.dbVersion = 5; // app requires database version 4
|
cfg.dbVersion = 5; // app requires database version 4
|
||||||
appConfigStorageStub.listItems.withArgs(VERSION_DB_TYPE).yieldsAsync(null, [4]); // database version is 4
|
appConfigStorageStub.listItems.withArgs(VERSION_DB_TYPE).returns(resolves([4])); // database version is 4
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(function() {
|
||||||
|
@ -378,7 +371,7 @@ describe('UpdateHandler', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work', function(done) {
|
it('should work', function(done) {
|
||||||
userStorageStub.listItems.withArgs(FOLDER_DB_TYPE).yieldsAsync(null, [
|
userStorageStub.listItems.withArgs(FOLDER_DB_TYPE).returns(resolves([
|
||||||
[{
|
[{
|
||||||
name: 'inbox1',
|
name: 'inbox1',
|
||||||
type: FOLDER_TYPE_INBOX
|
type: FOLDER_TYPE_INBOX
|
||||||
|
@ -404,7 +397,7 @@ describe('UpdateHandler', function() {
|
||||||
name: 'trash2',
|
name: 'trash2',
|
||||||
type: FOLDER_TYPE_TRASH
|
type: FOLDER_TYPE_TRASH
|
||||||
}]
|
}]
|
||||||
]);
|
]));
|
||||||
|
|
||||||
userStorageStub.storeList.withArgs([
|
userStorageStub.storeList.withArgs([
|
||||||
[{
|
[{
|
||||||
|
@ -420,12 +413,11 @@ describe('UpdateHandler', function() {
|
||||||
name: 'trash1',
|
name: 'trash1',
|
||||||
type: FOLDER_TYPE_TRASH
|
type: FOLDER_TYPE_TRASH
|
||||||
}]
|
}]
|
||||||
], FOLDER_DB_TYPE).yieldsAsync();
|
], FOLDER_DB_TYPE).returns(resolves());
|
||||||
|
|
||||||
appConfigStorageStub.storeList.withArgs([POST_UPDATE_DB_VERSION], VERSION_DB_TYPE).yieldsAsync();
|
appConfigStorageStub.storeList.withArgs([POST_UPDATE_DB_VERSION], VERSION_DB_TYPE).returns(resolves());
|
||||||
|
|
||||||
updateHandler.update(function(error) {
|
updateHandler.update().then(function() {
|
||||||
expect(error).to.not.exist;
|
|
||||||
expect(userStorageStub.listItems.calledOnce).to.be.true;
|
expect(userStorageStub.listItems.calledOnce).to.be.true;
|
||||||
expect(userStorageStub.storeList.calledOnce).to.be.true;
|
expect(userStorageStub.storeList.calledOnce).to.be.true;
|
||||||
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
|
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
|
||||||
|
@ -435,11 +427,11 @@ describe('UpdateHandler', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail when persisting database version fails', function(done) {
|
it('should fail when persisting database version fails', function(done) {
|
||||||
userStorageStub.listItems.yieldsAsync(null, []);
|
userStorageStub.listItems.returns(resolves([]));
|
||||||
userStorageStub.storeList.yieldsAsync();
|
userStorageStub.storeList.returns(resolves());
|
||||||
appConfigStorageStub.storeList.yieldsAsync(new Error());
|
appConfigStorageStub.storeList.returns(rejects(new Error()));
|
||||||
|
|
||||||
updateHandler.update(function(error) {
|
updateHandler.update().catch(function(error) {
|
||||||
expect(error).to.exist;
|
expect(error).to.exist;
|
||||||
expect(userStorageStub.listItems.calledOnce).to.be.true;
|
expect(userStorageStub.listItems.calledOnce).to.be.true;
|
||||||
expect(userStorageStub.storeList.calledOnce).to.be.true;
|
expect(userStorageStub.storeList.calledOnce).to.be.true;
|
||||||
|
@ -450,10 +442,10 @@ describe('UpdateHandler', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail when persisting folders fails', function(done) {
|
it('should fail when persisting folders fails', function(done) {
|
||||||
userStorageStub.listItems.yieldsAsync(null, []);
|
userStorageStub.listItems.returns(resolves([]));
|
||||||
userStorageStub.storeList.yieldsAsync(new Error());
|
userStorageStub.storeList.returns(rejects(new Error()));
|
||||||
|
|
||||||
updateHandler.update(function(error) {
|
updateHandler.update().catch(function(error) {
|
||||||
expect(error).to.exist;
|
expect(error).to.exist;
|
||||||
expect(userStorageStub.listItems.calledOnce).to.be.true;
|
expect(userStorageStub.listItems.calledOnce).to.be.true;
|
||||||
expect(userStorageStub.storeList.calledOnce).to.be.true;
|
expect(userStorageStub.storeList.calledOnce).to.be.true;
|
||||||
|
@ -464,9 +456,9 @@ describe('UpdateHandler', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail when listing folders fails', function(done) {
|
it('should fail when listing folders fails', function(done) {
|
||||||
userStorageStub.listItems.yieldsAsync(new Error());
|
userStorageStub.listItems.returns(rejects(new Error()));
|
||||||
|
|
||||||
updateHandler.update(function(error) {
|
updateHandler.update().catch(function(error) {
|
||||||
expect(error).to.exist;
|
expect(error).to.exist;
|
||||||
expect(userStorageStub.listItems.calledOnce).to.be.true;
|
expect(userStorageStub.listItems.calledOnce).to.be.true;
|
||||||
expect(userStorageStub.storeList.called).to.be.false;
|
expect(userStorageStub.storeList.called).to.be.false;
|
||||||
|
|
Loading…
Reference in New Issue