Merge pull request #259 from whiteout-io/dev/WO-862

Dev/wo 862
This commit is contained in:
Tankred Hase 2015-02-03 13:38:39 +01:00
commit fef264248d
22 changed files with 336 additions and 173 deletions

View File

@ -189,6 +189,7 @@ module.exports = function(grunt) {
'test/unit/controller/login/login-ctrl-test.js', 'test/unit/controller/login/login-ctrl-test.js',
'test/unit/controller/app/dialog-ctrl-test.js', 'test/unit/controller/app/dialog-ctrl-test.js',
'test/unit/controller/app/privatekey-upload-ctrl-test.js', 'test/unit/controller/app/privatekey-upload-ctrl-test.js',
'test/unit/controller/app/publickey-import-ctrl-test.js',
'test/unit/controller/app/account-ctrl-test.js', 'test/unit/controller/app/account-ctrl-test.js',
'test/unit/controller/app/set-passphrase-ctrl-test.js', 'test/unit/controller/app/set-passphrase-ctrl-test.js',
'test/unit/controller/app/contacts-ctrl-test.js', 'test/unit/controller/app/contacts-ctrl-test.js',

View File

@ -13,6 +13,7 @@ module.exports = appCfg;
*/ */
appCfg.config = { appCfg.config = {
cloudUrl: 'https://keys.whiteout.io', cloudUrl: 'https://keys.whiteout.io',
hkpUrl: 'https://pgp.mit.edu',
privkeyServerUrl: 'https://keychain.whiteout.io', privkeyServerUrl: 'https://keychain.whiteout.io',
adminUrl: 'https://admin-node.whiteout.io', adminUrl: 'https://admin-node.whiteout.io',
settingsUrl: 'https://settings.whiteout.io/autodiscovery/', settingsUrl: 'https://settings.whiteout.io/autodiscovery/',

View File

@ -111,6 +111,7 @@ app.controller('MailListCtrl', require('./controller/app/mail-list'));
app.controller('AccountCtrl', require('./controller/app/account')); app.controller('AccountCtrl', require('./controller/app/account'));
app.controller('SetPassphraseCtrl', require('./controller/app/set-passphrase')); app.controller('SetPassphraseCtrl', require('./controller/app/set-passphrase'));
app.controller('PrivateKeyUploadCtrl', require('./controller/app/privatekey-upload')); app.controller('PrivateKeyUploadCtrl', require('./controller/app/privatekey-upload'));
app.controller('PublicKeyImportCtrl', require('./controller/app/publickey-import'));
app.controller('ContactsCtrl', require('./controller/app/contacts')); app.controller('ContactsCtrl', require('./controller/app/contacts'));
app.controller('AboutCtrl', require('./controller/app/about')); app.controller('AboutCtrl', require('./controller/app/about'));
app.controller('DialogCtrl', require('./controller/app/dialog')); app.controller('DialogCtrl', require('./controller/app/dialog'));

View File

@ -4,7 +4,7 @@
// Controller // Controller
// //
var ContactsCtrl = function($scope, $q, keychain, pgp, dialog) { var ContactsCtrl = function($scope, $q, keychain, pgp, dialog, appConfig) {
// //
// scope state // scope state
@ -13,10 +13,13 @@ var ContactsCtrl = function($scope, $q, 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.searchText = undefined;
return $scope.listKeys(); return $scope.listKeys();
} }
}; };
$scope.whiteoutKeyServer = appConfig.config.cloudUrl.replace(/http[s]?:\/\//, ''); // display key server hostname
// //
// scope functions // scope functions
// //
@ -33,6 +36,7 @@ var ContactsCtrl = function($scope, $q, keychain, pgp, dialog) {
keys.forEach(function(key) { keys.forEach(function(key) {
var params = pgp.getKeyParams(key.publicKey); var params = pgp.getKeyParams(key.publicKey);
_.extend(key, params); _.extend(key, params);
key.fullUserId = key.userIds[0].name + ' <' + key.userIds[0].emailAddress + '>';
}); });
$scope.keys = keys; $scope.keys = keys;
@ -46,39 +50,6 @@ var ContactsCtrl = function($scope, $q, keychain, pgp, dialog) {
$scope.fingerprint = formatted; $scope.fingerprint = formatted;
}; };
$scope.importKey = function(publicKeyArmored) {
var keyParams, pubkey;
// verifiy public key string
if (publicKeyArmored.indexOf('-----BEGIN PGP PUBLIC KEY BLOCK-----') < 0) {
dialog.error({
showBugReporter: false,
message: 'Invalid public key!'
});
return;
}
try {
keyParams = pgp.getKeyParams(publicKeyArmored);
} catch (e) {
dialog.error(new Error('Error reading public key params!'));
return;
}
pubkey = {
_id: keyParams._id,
userId: keyParams.userId,
userIds: keyParams.userIds,
publicKey: publicKeyArmored,
imported: true // mark manually imported keys
};
return keychain.saveLocalPublicKey(pubkey).then(function() {
// update displayed keys
return $scope.listKeys();
}).catch(dialog.error);
};
$scope.removeKey = function(key) { $scope.removeKey = function(key) {
return keychain.removeLocalPublicKey(key._id).then(function() { return keychain.removeLocalPublicKey(key._id).then(function() {
// update displayed keys // update displayed keys

View File

@ -37,6 +37,7 @@ var DialogCtrl = function($scope, dialog) {
$scope.title = options.title; $scope.title = options.title;
$scope.message = options.errMsg || options.message; $scope.message = options.errMsg || options.message;
$scope.faqLink = options.faqLink; $scope.faqLink = options.faqLink;
$scope.faqLinkTitle = options.faqLinkTitle || 'Learn more';
$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;

View File

@ -0,0 +1,78 @@
'use strict';
//
// Controller
//
var PublickeyImportCtrl = function($scope, $q, keychain, pgp, hkp, dialog, appConfig) {
//
// scope state
//
$scope.state.publickeyImport = {
toggle: function(to) {
$scope.state.lightbox = (to) ? 'publickey-import' : undefined;
}
};
//
// scope variables
//
$scope.hkpUrl = appConfig.config.hkpUrl.replace('https://', '');
//
// scope functions
//
$scope.importKey = function(publicKeyArmored) {
var keyParams, pubkey;
// verifiy public key string
if (publicKeyArmored.indexOf('-----BEGIN PGP PUBLIC KEY BLOCK-----') < 0) {
dialog.error({
showBugReporter: false,
message: 'Invalid public key!'
});
return;
}
try {
keyParams = pgp.getKeyParams(publicKeyArmored);
} catch (e) {
dialog.error(new Error('Error reading public key params!'));
return;
}
pubkey = {
_id: keyParams._id,
userId: keyParams.userId,
userIds: keyParams.userIds,
publicKey: publicKeyArmored,
imported: true // mark manually imported keys
};
return keychain.saveLocalPublicKey(pubkey).then(function() {
$scope.pastedKey = '';
// display success message
return dialog.info({
title: 'Success',
message: 'Public key ' + keyParams._id + ' for ' + keyParams.userId + ' imported successfully!'
});
}).catch(dialog.error);
};
$scope.lookupKey = function(query) {
var keyUrl = hkp.getIndexUrl(query);
return dialog.info({
title: 'Link',
message: 'Follow this link and paste the PGP key block above...',
faqLink: keyUrl,
faqLinkTitle: keyUrl
});
};
};
module.exports = PublickeyImportCtrl;

View File

@ -206,6 +206,14 @@ var WriteCtrl = function($scope, $window, $filter, $q, appConfig, auth, keychain
return; return;
} }
if (recipient.address) {
// display only email address after autocomplete
recipient.displayId = recipient.address;
} else {
// set address after manual input
recipient.address = recipient.displayId;
}
// set display to insecure while fetching keys // set display to insecure while fetching keys
recipient.key = undefined; recipient.key = undefined;
recipient.secure = false; recipient.secure = false;
@ -231,11 +239,12 @@ var WriteCtrl = function($scope, $window, $filter, $q, appConfig, auth, keychain
}).then(function(key) { }).then(function(key) {
if (key) { if (key) {
// compare again since model could have changed during the roundtrip // compare again since model could have changed during the roundtrip
var matchingUserId = _.findWhere(key.userIds, { var userIds = pgp.getKeyParams(key.publicKey).userIds;
var matchingUserId = _.findWhere(userIds, {
emailAddress: recipient.address emailAddress: recipient.address
}); });
// compare either primary userId or (if available) multiple IDs // compare either primary userId or (if available) multiple IDs
if (key.userId === recipient.address || matchingUserId) { if (matchingUserId) {
recipient.key = key; recipient.key = key;
recipient.secure = true; recipient.secure = true;
} }
@ -264,7 +273,10 @@ var WriteCtrl = function($scope, $window, $filter, $q, appConfig, auth, keychain
function check(recipient) { function check(recipient) {
// validate address // validate address
if (!util.validateEmailAddress(recipient.address)) { if (!util.validateEmailAddress(recipient.address)) {
return; return dialog.info({
title: 'Warning',
message: 'Invalid recipient address!'
});
} }
numReceivers++; numReceivers++;
if (!recipient.secure) { if (!recipient.secure) {
@ -393,8 +405,10 @@ var WriteCtrl = function($scope, $window, $filter, $q, appConfig, auth, keychain
// populate address book cache // populate address book cache
return keychain.listLocalPublicKeys().then(function(keys) { return keychain.listLocalPublicKeys().then(function(keys) {
$scope.addressBookCache = keys.map(function(key) { $scope.addressBookCache = keys.map(function(key) {
var name = pgp.getKeyParams(key.publicKey).userIds[0].name;
return { return {
address: key.userId address: key.userId,
displayId: name + ' - ' + key.userId
}; };
}); });
}); });
@ -402,7 +416,7 @@ var WriteCtrl = function($scope, $window, $filter, $q, appConfig, auth, keychain
}).then(function() { }).then(function() {
// filter the address book cache // filter the address book cache
return $scope.addressBookCache.filter(function(i) { return $scope.addressBookCache.filter(function(i) {
return i.address.indexOf(query) !== -1; return i.displayId.toLowerCase().indexOf(query.toLowerCase()) !== -1;
}); });
}).catch(dialog.error); }).catch(dialog.error);

19
src/js/service/hkp.js Normal file
View File

@ -0,0 +1,19 @@
'use strict';
var ngModule = angular.module('woServices');
ngModule.service('hkp', HKP);
module.exports = HKP;
function HKP(appConfig) {
this._appConfig = appConfig;
}
/**
* Return a url of the link to be opened in a new window
* @param {String} query Either the email address or name
* @return {String} The url of the hkp query
*/
HKP.prototype.getIndexUrl = function(query) {
var baseUrl = this._appConfig.config.hkpUrl + '/pks/lookup?op=index&search=';
return baseUrl + encodeURIComponent(query);
};

View File

@ -9,6 +9,7 @@ require('./newsletter');
require('./oauth'); require('./oauth');
require('./privatekey'); require('./privatekey');
require('./publickey'); require('./publickey');
require('./hkp');
require('./admin'); require('./admin');
require('./lawnchair'); require('./lawnchair');
require('./devicestorage'); require('./devicestorage');

View File

@ -117,13 +117,13 @@ Keychain.prototype.refreshKeyForUserId = function(options) {
// checks if the user's key has been revoked by looking up the key id // checks if the user's key has been revoked by looking up the key id
function checkKeyExists(localKey) { function checkKeyExists(localKey) {
return self._publicKeyDao.get(localKey._id).then(function(cloudKey) { return self._publicKeyDao.getByUserId(userId).then(function(cloudKey) {
if (cloudKey && cloudKey._id === localKey._id) { if (cloudKey && cloudKey._id === localKey._id) {
// the key is present on the server, all is well // the key is present on the server, all is well
return localKey; return localKey;
} }
// the key has changed, update the key // the key has changed, update the key
return updateKey(localKey); return updateKey(localKey, cloudKey);
}).catch(function(err) { }).catch(function(err) {
if (err && err.code === 42) { if (err && err.code === 42) {
@ -134,24 +134,14 @@ Keychain.prototype.refreshKeyForUserId = function(options) {
}); });
} }
function updateKey(localKey) { function updateKey(localKey, newKey) {
// look for an updated key for the user id // the public key has changed, we need to ask for permission to update the key
return self._publicKeyDao.getByUserId(userId).then(function(newKey) { if (overridePermission) {
// the public key has changed, we need to ask for permission to update the key // don't query the user, update the public key right away
if (overridePermission) { return permissionGranted(localKey, newKey);
// don't query the user, update the public key right away } else {
return permissionGranted(localKey, newKey); return requestPermission(localKey, newKey);
} else { }
return requestPermission(localKey, newKey);
}
}).catch(function(err) {
// offline?
if (err && err.code === 42) {
return localKey;
}
throw err;
});
} }
function requestPermission(localKey, newKey) { function requestPermission(localKey, newKey) {
@ -196,6 +186,7 @@ Keychain.prototype.getReceiverPublicKey = function(userId) {
// search local keyring for public key // search local keyring for public key
return self._lawnchairDAO.list(DB_PUBLICKEY, 0, null).then(function(allPubkeys) { return self._lawnchairDAO.list(DB_PUBLICKEY, 0, null).then(function(allPubkeys) {
var userIds;
// query primary email address // query primary email address
var pubkey = _.findWhere(allPubkeys, { var pubkey = _.findWhere(allPubkeys, {
userId: userId userId: userId
@ -203,7 +194,8 @@ Keychain.prototype.getReceiverPublicKey = function(userId) {
// query mutliple userIds (for imported public keys) // query mutliple userIds (for imported public keys)
if (!pubkey) { if (!pubkey) {
for (var i = 0, match; i < allPubkeys.length; i++) { for (var i = 0, match; i < allPubkeys.length; i++) {
match = _.findWhere(allPubkeys[i].userIds, { userIds = self._pgp.getKeyParams(allPubkeys[i].publicKey).userIds;
match = _.findWhere(userIds, {
emailAddress: userId emailAddress: userId
}); });
if (match) { if (match) {

View File

@ -62,11 +62,12 @@ RestDAO.prototype.get = function(options) {
/** /**
* POST (create) request * POST (create) request
*/ */
RestDAO.prototype.post = function(item, uri) { RestDAO.prototype.post = function(item, uri, type) {
return this._processRequest({ return this._processRequest({
method: 'POST', method: 'POST',
payload: item, payload: item,
uri: uri uri: uri,
type: type
}); });
}; };
@ -98,27 +99,32 @@ RestDAO.prototype.remove = function(uri) {
RestDAO.prototype._processRequest = function(options) { RestDAO.prototype._processRequest = function(options) {
var self = this; var self = this;
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
var xhr, format; var xhr, format, accept, payload;
if (typeof options.uri === 'undefined') { if (typeof options.uri === 'undefined') {
throw createError(400, 'Bad Request! URI is a mandatory parameter.'); throw createError(400, 'Bad Request! URI is a mandatory parameter.');
} }
options.type = options.type || 'json'; options.type = options.type || 'json';
payload = options.payload;
if (options.type === 'json') { if (options.type === 'json') {
format = 'application/json'; format = 'application/json';
payload = payload ? JSON.stringify(payload) : undefined;
} else if (options.type === 'xml') { } else if (options.type === 'xml') {
format = 'application/xml'; format = 'application/xml';
} else if (options.type === 'text') { } else if (options.type === 'text') {
format = 'text/plain'; format = 'text/plain';
} else if (options.type === 'form') {
format = 'application/x-www-form-urlencoded; charset=UTF-8';
accept = 'text/html; charset=UTF-8';
} else { } else {
throw createError(400, 'Bad Request! Unhandled data type.'); throw createError(400, 'Bad Request! Unhandled data type.');
} }
xhr = new XMLHttpRequest(); xhr = new XMLHttpRequest();
xhr.open(options.method, self._baseUri + options.uri); xhr.open(options.method, self._baseUri + options.uri);
xhr.setRequestHeader('Accept', format); xhr.setRequestHeader('Accept', accept || format);
xhr.setRequestHeader('Content-Type', format); xhr.setRequestHeader('Content-Type', format);
xhr.onload = function() { xhr.onload = function() {
@ -146,7 +152,7 @@ RestDAO.prototype._processRequest = function(options) {
reject(createError(42, 'Error calling ' + options.method + ' on ' + options.uri)); reject(createError(42, 'Error calling ' + options.method + ' on ' + options.uri));
}; };
xhr.send(options.payload ? JSON.stringify(options.payload) : undefined); xhr.send(payload);
}); });
}; };

View File

@ -123,6 +123,14 @@
} }
} }
.textarea {
width: 100%;
height: 100px;
border: 1px solid $color-border-light;
resize: none;
outline: none;
}
// Attention: Webkit support only! // Attention: Webkit support only!
.input-select { .input-select {
position: relative; position: relative;

View File

@ -6,10 +6,9 @@
</button> </button>
</header> </header>
<div class="lightbox__content"> <div class="lightbox__content">
<input class="u-hide-visually" id="keyfile-input" type="file" multiple accept=".asc" keyfile-input>
<div class="form-input-with-button u-gap-bottom"> <div class="form-input-with-button u-gap-bottom">
<input class="input-text" type="text" placeholder="Search..." ng-model="searchText"> <input class="input-text" type="text" placeholder="Filter..." ng-model="searchText">
<button class="btn" ng-class="{'btn--invalid': sendBtnSecure === false}" wo-click-file-input="#keyfile-input">Import keys</button> <button class="btn" wo-touch="state.contacts.toggle(false); state.publickeyImport.toggle(true)">Import keys</button>
</div> </div>
<p class="typo-paragraph u-text-center" ng-show="!keys.length"> <p class="typo-paragraph u-text-center" ng-show="!keys.length">
@ -17,7 +16,7 @@
</p> </p>
<ul class="contacts"> <ul class="contacts">
<li class="contacts__contact" ng-repeat="key in keys | orderBy:'userId' | filter:searchText" <li class="contacts__contact" ng-repeat="key in keys | orderBy:'fullUserId' | filter:searchText"
ng-class="{ 'contacts__contact--open': key.open }"> ng-class="{ 'contacts__contact--open': key.open }">
<div class="contacts__delete"> <div class="contacts__delete">
<button class="btn-icon-very-light" wo-touch="removeKey(key)"> <button class="btn-icon-very-light" wo-touch="removeKey(key)">
@ -25,7 +24,7 @@
</button> </button>
</div> </div>
<h3 class="contacts__title" wo-touch="key.open = !key.open">{{key.userId}}</h3> <h3 class="contacts__title" wo-touch="key.open = !key.open">{{key.fullUserId}}</h3>
<p class="contacts__short-description"> <p class="contacts__short-description">
{{key._id.slice(8)}} {{key._id.slice(8)}}
</p> </p>
@ -38,6 +37,9 @@
<dt>Created</dt> <dt>Created</dt>
<dd>{{key.created | date:'mediumDate'}}</dd> <dd>{{key.created | date:'mediumDate'}}</dd>
<dt>Source</dt>
<dd>{{key.imported ? 'Imported' : key.source || whiteoutKeyServer}}</dd>
</dl> </dl>
</li> </li>
</ul> </ul>

View File

@ -27,6 +27,9 @@
<div class="lightbox" ng-class="{'lightbox--show': state.lightbox === 'privatekey-upload'}" <div class="lightbox" ng-class="{'lightbox--show': state.lightbox === 'privatekey-upload'}"
ng-include="'tpl/privatekey-upload.html'"></div> ng-include="'tpl/privatekey-upload.html'"></div>
<div class="lightbox" ng-class="{'lightbox--show': state.lightbox === 'publickey-import'}"
ng-include="'tpl/publickey-import.html'"></div>
<div class="lightbox" ng-class="{'lightbox--show': state.lightbox === 'contacts'}" <div class="lightbox" ng-class="{'lightbox--show': state.lightbox === 'contacts'}"
ng-include="'tpl/contacts.html'"></div> ng-include="'tpl/contacts.html'"></div>

View File

@ -7,7 +7,7 @@
</header> </header>
<div class="lightbox__content"> <div class="lightbox__content">
<p class="typo-paragraph">{{message}} <a ng-show="faqLink" href="{{faqLink}}" target="_blank">Learn more</a></p> <p class="typo-paragraph">{{message}} <a ng-show="faqLink" href="{{faqLink}}" target="_blank">{{faqLinkTitle}}</a></p>
</div> </div>
<footer class="lightbox__controls"> <footer class="lightbox__controls">

View File

@ -19,7 +19,7 @@
<p class="form__error-message" ng-show="errMsg">{{errMsg}}</p> <p class="form__error-message" ng-show="errMsg">{{errMsg}}</p>
<div class="form__row"> <div class="form__row">
<input class="input-file" type="file" accept=".asc" file-reader tabindex="1" required> <input class="input-file" type="file" file-reader tabindex="1" required>
</div> </div>
<div class="form__row"> <div class="form__row">
<input class="input-text" type="password" ng-model="passphrase" <input class="input-text" type="password" ng-model="passphrase"

View File

@ -0,0 +1,27 @@
<div class="lightbox__body" ng-controller="PublicKeyImportCtrl">
<header class="lightbox__header">
<h2>Import PGP keys</h2>
<button class="lightbox__close" wo-touch="state.publickeyImport.toggle(false)" data-action="lightbox-close">
<svg><use xlink:href="#icon-close" /><title>Close</title></svg>
</button>
</header>
<div class="lightbox__content">
<div class="u-gap-bottom">
<p class="typo-paragraph">You can import public keys via copy/paste or as .asc files. Use the lookup function to search by name or email address on the key server. <i>Most keys are fetched automatically while reading and writing messages so most users won't need to import keys manually.</i></p>
</div>
<div class="u-gap-bottom">
<textarea class="textarea" placeholder="Paste PUBLIC PGP KEY BLOCK here..." ng-model="pastedKey" ng-change="importKey(pastedKey)"></textarea>
</div>
<form class="form-input-with-button u-gap-bottom">
<input class="input-text" type="text" placeholder="Lookup on {{hkpUrl}} by name or email address" ng-model="searchText">
<button class="btn" type="submit" ng-click="lookupKey(searchText)">Lookup</button>
</form>
<div class="form__row">
<input class="input-file" type="file" multiple keyfile-input>
</div>
</div>
</div>

View File

@ -17,8 +17,7 @@
</div> </div>
<label>To:</label> <label>To:</label>
<tags-input class="tags-input" ng-model="to" type="email" tabindex="1" add-on-space="true" add-on-enter="true" enable-editing-last-tag="true" <tags-input class="tags-input" ng-model="to" type="email" tabindex="1" add-on-space="true" add-on-enter="true" enable-editing-last-tag="true"
tag-style="tagStyle" display-property="address" on-tag-added="verify($tag)" on-tag-removed="checkSendStatus()" tag-style="tagStyle" display-property="displayId" on-tag-added="verify($tag)" on-tag-removed="checkSendStatus()"
allowed-tags-pattern='^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$'
placeholder="add recipient"> placeholder="add recipient">
<auto-complete source="lookupAddressBook($query)" min-length="1"></auto-complete> <auto-complete source="lookupAddressBook($query)" min-length="1"></auto-complete>
</tags-input> </tags-input>
@ -27,8 +26,7 @@
focus-input-on-click> focus-input-on-click>
<label>Cc:</label> <label>Cc:</label>
<tags-input class="tags-input" ng-model="cc" type="email" tabindex="1" add-on-space="true" add-on-enter="true" enable-editing-last-tag="true" <tags-input class="tags-input" ng-model="cc" type="email" tabindex="1" add-on-space="true" add-on-enter="true" enable-editing-last-tag="true"
tag-style="tagStyle" display-property="address" on-tag-added="verify($tag)" on-tag-removed="checkSendStatus()" tag-style="tagStyle" display-property="displayId" on-tag-added="verify($tag)" on-tag-removed="checkSendStatus()"
allowed-tags-pattern='^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$'
placeholder="add cc"> placeholder="add cc">
<auto-complete source="lookupAddressBook($query)" min-length="1"></auto-complete> <auto-complete source="lookupAddressBook($query)" min-length="1"></auto-complete>
</tags-input> </tags-input>
@ -37,8 +35,7 @@
focus-input-on-click> focus-input-on-click>
<label>Bcc:</label> <label>Bcc:</label>
<tags-input class="tags-input" ng-model="bcc" type="email" tabindex="1" add-on-space="true" add-on-enter="true" enable-editing-last-tag="true" <tags-input class="tags-input" ng-model="bcc" type="email" tabindex="1" add-on-space="true" add-on-enter="true" enable-editing-last-tag="true"
tag-style="tagStyle" display-property="address" on-tag-added="verify($tag)" on-tag-removed="checkSendStatus()" tag-style="tagStyle" display-property="displayId" on-tag-added="verify($tag)" on-tag-removed="checkSendStatus()"
allowed-tags-pattern='^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$'
placeholder="add bcc"> placeholder="add bcc">
<auto-complete source="lookupAddressBook($query)" min-length="1"></auto-complete> <auto-complete source="lookupAddressBook($query)" min-length="1"></auto-complete>
</tags-input> </tags-input>

View File

@ -51,7 +51,11 @@ describe('Contacts Controller unit test', function() {
_id: '12345' _id: '12345'
}])); }]));
pgpStub.getKeyParams.returns({ pgpStub.getKeyParams.returns({
fingerprint: 'asdf' fingerprint: 'asdf',
userIds: [{
name: 'Firstname Lastname',
emailAddress: 'first.last@example.com'
}]
}); });
expect(scope.keys).to.not.exist; expect(scope.keys).to.not.exist;
@ -59,6 +63,7 @@ describe('Contacts Controller unit test', 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');
expect(scope.keys[0].fullUserId).to.equal('Firstname Lastname <first.last@example.com>');
done(); done();
}); });
}); });
@ -76,65 +81,6 @@ describe('Contacts Controller unit test', function() {
}); });
}); });
describe('importKey', function() {
it('should work', function(done) {
var keyArmored = '-----BEGIN PGP PUBLIC KEY BLOCK-----';
pgpStub.getKeyParams.returns({
_id: '12345',
userId: 'max@example.com',
userIds: []
});
keychainStub.saveLocalPublicKey.withArgs({
_id: '12345',
userId: 'max@example.com',
userIds: [],
publicKey: '-----BEGIN PGP PUBLIC KEY BLOCK-----',
imported: true
}).returns(resolves());
scope.listKeys = function() {};
scope.importKey(keyArmored).then(function() {
done();
});
});
it('should fail due to invalid armored key', function() {
var keyArmored = '-----BEGIN PGP PRIVATE KEY BLOCK-----';
scope.importKey(keyArmored);
expect(dialogStub.error.calledOnce).to.be.true;
});
it('should fail due to error in pgp.getKeyParams', function() {
var keyArmored = '-----BEGIN PGP PUBLIC KEY BLOCK-----';
pgpStub.getKeyParams.throws(new Error('WAT'));
scope.importKey(keyArmored);
expect(dialogStub.error.calledOnce).to.be.true;
});
it('should fail due to error in keychain.saveLocalPublicKey', function(done) {
var keyArmored = '-----BEGIN PGP PUBLIC KEY BLOCK-----';
pgpStub.getKeyParams.returns({
_id: '12345',
userId: 'max@example.com'
});
keychainStub.saveLocalPublicKey.returns(rejects(42));
scope.importKey(keyArmored).then(function() {
expect(dialogStub.error.calledOnce).to.be.true;
done();
});
});
});
describe('removeKey', function() { describe('removeKey', function() {
it('should work', function(done) { it('should work', function(done) {
var key = { var key = {

View File

@ -0,0 +1,91 @@
'use strict';
var PublicKeyImportCtrl = require('../../../../src/js/controller/app/publickey-import'),
Keychain = require('../../../../src/js/service/keychain'),
PGP = require('../../../../src/js/crypto/pgp'),
Dialog = require('../../../../src/js/util/dialog');
describe('Public Key Import Controller unit test', function() {
var scope, ctrl, keychainStub, pgpStub, dialogStub;
beforeEach(function() {
pgpStub = sinon.createStubInstance(PGP);
keychainStub = sinon.createStubInstance(Keychain);
dialogStub = sinon.createStubInstance(Dialog);
angular.module('publickey-import', ['woServices']);
angular.mock.module('publickey-import');
angular.mock.inject(function($rootScope, $controller) {
scope = $rootScope.$new();
scope.state = {};
ctrl = $controller(PublicKeyImportCtrl, {
$scope: scope,
$q: window.qMock,
keychain: keychainStub,
pgp: pgpStub,
dialog: dialogStub
});
});
});
afterEach(function() {});
describe('importKey', function() {
it('should work', function(done) {
var keyArmored = '-----BEGIN PGP PUBLIC KEY BLOCK-----';
pgpStub.getKeyParams.returns({
_id: '12345',
userId: 'max@example.com',
userIds: []
});
keychainStub.saveLocalPublicKey.withArgs({
_id: '12345',
userId: 'max@example.com',
userIds: [],
publicKey: '-----BEGIN PGP PUBLIC KEY BLOCK-----',
imported: true
}).returns(resolves());
scope.listKeys = function() {};
scope.importKey(keyArmored).then(function() {
done();
});
});
it('should fail due to invalid armored key', function() {
var keyArmored = '-----BEGIN PGP PRIVATE KEY BLOCK-----';
scope.importKey(keyArmored);
expect(dialogStub.error.calledOnce).to.be.true;
});
it('should fail due to error in pgp.getKeyParams', function() {
var keyArmored = '-----BEGIN PGP PUBLIC KEY BLOCK-----';
pgpStub.getKeyParams.throws(new Error('WAT'));
scope.importKey(keyArmored);
expect(dialogStub.error.calledOnce).to.be.true;
});
it('should fail due to error in keychain.saveLocalPublicKey', function(done) {
var keyArmored = '-----BEGIN PGP PUBLIC KEY BLOCK-----';
pgpStub.getKeyParams.returns({
_id: '12345',
userId: 'max@example.com'
});
keychainStub.saveLocalPublicKey.returns(rejects(42));
scope.importKey(keyArmored).then(function() {
expect(dialogStub.error.calledOnce).to.be.true;
done();
});
});
});
});

View File

@ -215,6 +215,11 @@ describe('Write controller unit test', function() {
}).returns(resolves({ }).returns(resolves({
userId: 'asdf@example.com' userId: 'asdf@example.com'
})); }));
pgpMock.getKeyParams.returns({
userIds: [{
emailAddress: recipient.address
}]
});
scope.verify(recipient).then(function() { scope.verify(recipient).then(function() {
expect(recipient.key).to.deep.equal({ expect(recipient.key).to.deep.equal({
@ -232,15 +237,17 @@ describe('Write controller unit test', function() {
address: 'asdf@example.com' address: 'asdf@example.com'
}; };
var key = { var key = {
userId: 'qwer@example.com', userId: 'qwertz@example.com'
userIds: [{
emailAddress: 'asdf@example.com'
}]
}; };
keychainMock.refreshKeyForUserId.withArgs({ keychainMock.refreshKeyForUserId.withArgs({
userId: recipient.address userId: recipient.address
}).returns(resolves(key)); }).returns(resolves(key));
pgpMock.getKeyParams.returns({
userIds: [{
emailAddress: recipient.address
}]
});
scope.verify(recipient).then(function() { scope.verify(recipient).then(function() {
expect(recipient.key).to.deep.equal(key); expect(recipient.key).to.deep.equal(key);
@ -359,12 +366,18 @@ describe('Write controller unit test', function() {
userId: 'test@asdf.com', userId: 'test@asdf.com',
publicKey: 'KEY' publicKey: 'KEY'
}])); }]));
pgpMock.getKeyParams.returns({
userIds: [{
name: 'Bob'
}]
});
var result = scope.lookupAddressBook('test'); var result = scope.lookupAddressBook('test');
result.then(function(response) { result.then(function(response) {
expect(response).to.deep.equal([{ expect(response).to.deep.equal([{
address: 'test@asdf.com' address: 'test@asdf.com',
displayId: 'Bob - test@asdf.com'
}]); }]);
done(); done();
}); });
@ -372,17 +385,17 @@ describe('Write controller unit test', function() {
it('should work with cache', function(done) { it('should work with cache', function(done) {
scope.addressBookCache = [{ scope.addressBookCache = [{
address: 'test@asdf.com' address: 'test@asdf.com',
displayId: 'Bob - test@asdf.com'
}, { }, {
address: 'tes@asdf.com' address: 'tes@asdf.com',
displayId: 'Bob - tes@asdf.com'
}]; }];
var result = scope.lookupAddressBook('test'); var result = scope.lookupAddressBook('test');
result.then(function(response) { result.then(function(response) {
expect(response).to.deep.equal([{ expect(response).to.deep.equal([scope.addressBookCache[0]]);
address: 'test@asdf.com'
}]);
done(); done();
}); });
}); });

View File

@ -112,7 +112,7 @@ describe('Keychain DAO unit tests', function() {
it('should not update the key when up to date', function(done) { it('should not update the key when up to date', function(done) {
getPubKeyStub.returns(resolves(oldKey)); getPubKeyStub.returns(resolves(oldKey));
pubkeyDaoStub.get.withArgs(oldKey._id).returns(resolves(oldKey)); pubkeyDaoStub.getByUserId.withArgs(testUser).returns(resolves(oldKey));
keychainDao.refreshKeyForUserId({ keychainDao.refreshKeyForUserId({
userId: testUser userId: testUser
@ -120,7 +120,7 @@ describe('Keychain DAO unit tests', function() {
expect(key).to.to.equal(oldKey); expect(key).to.to.equal(oldKey);
expect(getPubKeyStub.calledOnce).to.be.true; expect(getPubKeyStub.calledOnce).to.be.true;
expect(pubkeyDaoStub.get.calledOnce).to.be.true; expect(pubkeyDaoStub.getByUserId.calledOnce).to.be.true;
done(); done();
}); });
@ -128,7 +128,6 @@ describe('Keychain DAO unit tests', function() {
it('should update key', function(done) { it('should update key', function(done) {
getPubKeyStub.returns(resolves(oldKey)); getPubKeyStub.returns(resolves(oldKey));
pubkeyDaoStub.get.withArgs(oldKey._id).returns(resolves());
pubkeyDaoStub.getByUserId.withArgs(testUser).returns(resolves(newKey)); pubkeyDaoStub.getByUserId.withArgs(testUser).returns(resolves(newKey));
keychainDao.requestPermissionForKeyUpdate = function(opts, cb) { keychainDao.requestPermissionForKeyUpdate = function(opts, cb) {
expect(opts.userId).to.equal(testUser); expect(opts.userId).to.equal(testUser);
@ -144,7 +143,6 @@ describe('Keychain DAO unit tests', function() {
expect(key).to.equal(newKey); expect(key).to.equal(newKey);
expect(getPubKeyStub.calledOnce).to.be.true; expect(getPubKeyStub.calledOnce).to.be.true;
expect(pubkeyDaoStub.get.calledOnce).to.be.true;
expect(pubkeyDaoStub.getByUserId.calledOnce).to.be.true; expect(pubkeyDaoStub.getByUserId.calledOnce).to.be.true;
expect(lawnchairDaoStub.remove.calledOnce).to.be.true; expect(lawnchairDaoStub.remove.calledOnce).to.be.true;
expect(lawnchairDaoStub.persist.calledOnce).to.be.true; expect(lawnchairDaoStub.persist.calledOnce).to.be.true;
@ -155,7 +153,6 @@ describe('Keychain DAO unit tests', function() {
it('should update key without approval', function(done) { it('should update key without approval', function(done) {
getPubKeyStub.returns(resolves(oldKey)); getPubKeyStub.returns(resolves(oldKey));
pubkeyDaoStub.get.withArgs(oldKey._id).returns(resolves());
pubkeyDaoStub.getByUserId.withArgs(testUser).returns(resolves(newKey)); pubkeyDaoStub.getByUserId.withArgs(testUser).returns(resolves(newKey));
lawnchairDaoStub.remove.withArgs('publickey_' + oldKey._id).returns(resolves()); lawnchairDaoStub.remove.withArgs('publickey_' + oldKey._id).returns(resolves());
lawnchairDaoStub.persist.withArgs('publickey_' + newKey._id, newKey).returns(resolves()); lawnchairDaoStub.persist.withArgs('publickey_' + newKey._id, newKey).returns(resolves());
@ -167,7 +164,6 @@ describe('Keychain DAO unit tests', function() {
expect(key).to.equal(newKey); expect(key).to.equal(newKey);
expect(getPubKeyStub.calledOnce).to.be.true; expect(getPubKeyStub.calledOnce).to.be.true;
expect(pubkeyDaoStub.get.calledOnce).to.be.true;
expect(pubkeyDaoStub.getByUserId.calledOnce).to.be.true; expect(pubkeyDaoStub.getByUserId.calledOnce).to.be.true;
expect(lawnchairDaoStub.remove.calledOnce).to.be.true; expect(lawnchairDaoStub.remove.calledOnce).to.be.true;
expect(lawnchairDaoStub.persist.calledOnce).to.be.true; expect(lawnchairDaoStub.persist.calledOnce).to.be.true;
@ -178,7 +174,7 @@ describe('Keychain DAO unit tests', function() {
it('should remove key', function(done) { it('should remove key', function(done) {
getPubKeyStub.returns(resolves(oldKey)); getPubKeyStub.returns(resolves(oldKey));
pubkeyDaoStub.get.withArgs(oldKey._id).returns(resolves()); pubkeyDaoStub.getByUserId.withArgs(testUser).returns(resolves());
pubkeyDaoStub.getByUserId.withArgs(testUser).returns(resolves()); pubkeyDaoStub.getByUserId.withArgs(testUser).returns(resolves());
keychainDao.requestPermissionForKeyUpdate = function(opts, cb) { keychainDao.requestPermissionForKeyUpdate = function(opts, cb) {
expect(opts.userId).to.equal(testUser); expect(opts.userId).to.equal(testUser);
@ -193,7 +189,6 @@ describe('Keychain DAO unit tests', function() {
expect(key).to.not.exist; expect(key).to.not.exist;
expect(getPubKeyStub.calledOnce).to.be.true; expect(getPubKeyStub.calledOnce).to.be.true;
expect(pubkeyDaoStub.get.calledOnce).to.be.true;
expect(pubkeyDaoStub.getByUserId.calledOnce).to.be.true; expect(pubkeyDaoStub.getByUserId.calledOnce).to.be.true;
expect(lawnchairDaoStub.remove.calledOnce).to.be.true; expect(lawnchairDaoStub.remove.calledOnce).to.be.true;
expect(lawnchairDaoStub.persist.called).to.be.false; expect(lawnchairDaoStub.persist.called).to.be.false;
@ -204,7 +199,7 @@ describe('Keychain DAO unit tests', function() {
it('should go offline while fetching new key', function(done) { it('should go offline while fetching new key', function(done) {
getPubKeyStub.returns(resolves(oldKey)); getPubKeyStub.returns(resolves(oldKey));
pubkeyDaoStub.get.withArgs(oldKey._id).returns(resolves()); pubkeyDaoStub.getByUserId.withArgs(testUser).returns(resolves());
pubkeyDaoStub.getByUserId.withArgs(testUser).returns(rejects({ pubkeyDaoStub.getByUserId.withArgs(testUser).returns(rejects({
code: 42 code: 42
})); }));
@ -215,7 +210,6 @@ describe('Keychain DAO unit tests', function() {
expect(key).to.to.equal(oldKey); expect(key).to.to.equal(oldKey);
expect(getPubKeyStub.calledOnce).to.be.true; expect(getPubKeyStub.calledOnce).to.be.true;
expect(pubkeyDaoStub.get.calledOnce).to.be.true;
expect(pubkeyDaoStub.getByUserId.calledOnce).to.be.true; expect(pubkeyDaoStub.getByUserId.calledOnce).to.be.true;
expect(lawnchairDaoStub.remove.called).to.be.false; expect(lawnchairDaoStub.remove.called).to.be.false;
expect(lawnchairDaoStub.persist.called).to.be.false; expect(lawnchairDaoStub.persist.called).to.be.false;
@ -226,7 +220,7 @@ describe('Keychain DAO unit tests', function() {
it('should not remove old key on user rejection', function(done) { it('should not remove old key on user rejection', function(done) {
getPubKeyStub.returns(resolves(oldKey)); getPubKeyStub.returns(resolves(oldKey));
pubkeyDaoStub.get.withArgs(oldKey._id).returns(resolves()); pubkeyDaoStub.getByUserId.withArgs(testUser).returns(resolves());
pubkeyDaoStub.getByUserId.withArgs(testUser).returns(resolves(newKey)); pubkeyDaoStub.getByUserId.withArgs(testUser).returns(resolves(newKey));
keychainDao.requestPermissionForKeyUpdate = function(opts, cb) { keychainDao.requestPermissionForKeyUpdate = function(opts, cb) {
expect(opts.userId).to.equal(testUser); expect(opts.userId).to.equal(testUser);
@ -240,7 +234,6 @@ describe('Keychain DAO unit tests', function() {
expect(key).to.equal(oldKey); expect(key).to.equal(oldKey);
expect(getPubKeyStub.calledOnce).to.be.true; expect(getPubKeyStub.calledOnce).to.be.true;
expect(pubkeyDaoStub.get.calledOnce).to.be.true;
expect(pubkeyDaoStub.getByUserId.calledOnce).to.be.true; expect(pubkeyDaoStub.getByUserId.calledOnce).to.be.true;
expect(lawnchairDaoStub.remove.called).to.be.false; expect(lawnchairDaoStub.remove.called).to.be.false;
expect(lawnchairDaoStub.persist.called).to.be.false; expect(lawnchairDaoStub.persist.called).to.be.false;
@ -266,7 +259,7 @@ describe('Keychain DAO unit tests', function() {
it('should update not the key when offline', function(done) { it('should update not the key when offline', function(done) {
getPubKeyStub.returns(resolves(oldKey)); getPubKeyStub.returns(resolves(oldKey));
pubkeyDaoStub.get.withArgs(oldKey._id).returns(rejects({ pubkeyDaoStub.getByUserId.withArgs(testUser).returns(rejects({
code: 42 code: 42
})); }));
@ -276,8 +269,7 @@ describe('Keychain DAO unit tests', function() {
expect(key).to.to.equal(oldKey); expect(key).to.to.equal(oldKey);
expect(getPubKeyStub.calledOnce).to.be.true; expect(getPubKeyStub.calledOnce).to.be.true;
expect(pubkeyDaoStub.get.calledOnce).to.be.true; expect(pubkeyDaoStub.getByUserId.calledOnce).to.be.true;
expect(pubkeyDaoStub.getByUserId.called).to.be.false;
expect(lawnchairDaoStub.remove.called).to.be.false; expect(lawnchairDaoStub.remove.called).to.be.false;
expect(lawnchairDaoStub.persist.called).to.be.false; expect(lawnchairDaoStub.persist.called).to.be.false;
@ -287,7 +279,7 @@ describe('Keychain DAO unit tests', function() {
it('should error while persisting new key', function(done) { it('should error while persisting new key', function(done) {
getPubKeyStub.returns(resolves(oldKey)); getPubKeyStub.returns(resolves(oldKey));
pubkeyDaoStub.get.withArgs(oldKey._id).returns(resolves()); pubkeyDaoStub.getByUserId.withArgs(testUser).returns(resolves());
pubkeyDaoStub.getByUserId.withArgs(testUser).returns(resolves(newKey)); pubkeyDaoStub.getByUserId.withArgs(testUser).returns(resolves(newKey));
keychainDao.requestPermissionForKeyUpdate = function(opts, cb) { keychainDao.requestPermissionForKeyUpdate = function(opts, cb) {
expect(opts.userId).to.equal(testUser); expect(opts.userId).to.equal(testUser);
@ -303,7 +295,6 @@ describe('Keychain DAO unit tests', function() {
expect(err).to.exist; expect(err).to.exist;
expect(getPubKeyStub.calledOnce).to.be.true; expect(getPubKeyStub.calledOnce).to.be.true;
expect(pubkeyDaoStub.get.calledOnce).to.be.true;
expect(pubkeyDaoStub.getByUserId.calledOnce).to.be.true; expect(pubkeyDaoStub.getByUserId.calledOnce).to.be.true;
expect(lawnchairDaoStub.remove.calledOnce).to.be.true; expect(lawnchairDaoStub.remove.calledOnce).to.be.true;
expect(lawnchairDaoStub.persist.calledOnce).to.be.true; expect(lawnchairDaoStub.persist.calledOnce).to.be.true;
@ -314,7 +305,7 @@ describe('Keychain DAO unit tests', function() {
it('should error while deleting old key', function(done) { it('should error while deleting old key', function(done) {
getPubKeyStub.returns(resolves(oldKey)); getPubKeyStub.returns(resolves(oldKey));
pubkeyDaoStub.get.withArgs(oldKey._id).returns(resolves()); pubkeyDaoStub.getByUserId.withArgs(testUser).returns(resolves());
pubkeyDaoStub.getByUserId.withArgs(testUser).returns(resolves()); pubkeyDaoStub.getByUserId.withArgs(testUser).returns(resolves());
keychainDao.requestPermissionForKeyUpdate = function(opts, cb) { keychainDao.requestPermissionForKeyUpdate = function(opts, cb) {
expect(opts.userId).to.equal(testUser); expect(opts.userId).to.equal(testUser);
@ -328,7 +319,6 @@ describe('Keychain DAO unit tests', function() {
expect(err).to.exist; expect(err).to.exist;
expect(getPubKeyStub.calledOnce).to.be.true; expect(getPubKeyStub.calledOnce).to.be.true;
expect(pubkeyDaoStub.get.calledOnce).to.be.true;
expect(lawnchairDaoStub.remove.calledOnce).to.be.true; expect(lawnchairDaoStub.remove.calledOnce).to.be.true;
expect(pubkeyDaoStub.getByUserId.calledOnce).to.be.true; expect(pubkeyDaoStub.getByUserId.calledOnce).to.be.true;
expect(lawnchairDaoStub.persist.called).to.be.false; expect(lawnchairDaoStub.persist.called).to.be.false;
@ -339,7 +329,7 @@ describe('Keychain DAO unit tests', function() {
it('should error while persisting new key', function(done) { it('should error while persisting new key', function(done) {
getPubKeyStub.returns(resolves(oldKey)); getPubKeyStub.returns(resolves(oldKey));
pubkeyDaoStub.get.withArgs(oldKey._id).returns(resolves()); pubkeyDaoStub.getByUserId.withArgs(testUser).returns(resolves());
pubkeyDaoStub.getByUserId.withArgs(testUser).returns(resolves(newKey)); pubkeyDaoStub.getByUserId.withArgs(testUser).returns(resolves(newKey));
keychainDao.requestPermissionForKeyUpdate = function(opts, cb) { keychainDao.requestPermissionForKeyUpdate = function(opts, cb) {
expect(opts.userId).to.equal(testUser); expect(opts.userId).to.equal(testUser);
@ -355,7 +345,6 @@ describe('Keychain DAO unit tests', function() {
expect(err).to.exist; expect(err).to.exist;
expect(getPubKeyStub.calledOnce).to.be.true; expect(getPubKeyStub.calledOnce).to.be.true;
expect(pubkeyDaoStub.get.calledOnce).to.be.true;
expect(pubkeyDaoStub.getByUserId.calledOnce).to.be.true; expect(pubkeyDaoStub.getByUserId.calledOnce).to.be.true;
expect(lawnchairDaoStub.remove.calledOnce).to.be.true; expect(lawnchairDaoStub.remove.calledOnce).to.be.true;
expect(lawnchairDaoStub.persist.calledOnce).to.be.true; expect(lawnchairDaoStub.persist.calledOnce).to.be.true;
@ -366,7 +355,7 @@ describe('Keychain DAO unit tests', function() {
it('should error when get failed', function(done) { it('should error when get failed', function(done) {
getPubKeyStub.returns(resolves(oldKey)); getPubKeyStub.returns(resolves(oldKey));
pubkeyDaoStub.get.withArgs(oldKey._id).returns(rejects({})); pubkeyDaoStub.getByUserId.withArgs(testUser).returns(rejects({}));
keychainDao.refreshKeyForUserId({ keychainDao.refreshKeyForUserId({
userId: testUser userId: testUser
@ -497,11 +486,13 @@ describe('Keychain DAO unit tests', function() {
lawnchairDaoStub.list.returns(resolves([{ lawnchairDaoStub.list.returns(resolves([{
_id: '12345', _id: '12345',
userId: 'not testUser', userId: 'not testUser',
userIds: [{
emailAddress: testUser
}],
publicKey: 'asdf' publicKey: 'asdf'
}])); }]));
pgpStub.getKeyParams.returns({
userIds: [{
emailAddress: testUser
}]
});
keychainDao.getReceiverPublicKey(testUser).then(function(key) { keychainDao.getReceiverPublicKey(testUser).then(function(key) {
expect(key).to.exist; expect(key).to.exist;