mirror of
https://github.com/moparisthebest/mail
synced 2024-12-23 07:48:48 -05:00
Merge pull request #35 from whiteout-io/dev/import-pubkey
Dev/import pubkey
This commit is contained in:
commit
45e6b7834a
@ -7,6 +7,7 @@ requirejs([
|
|||||||
'js/controller/popover',
|
'js/controller/popover',
|
||||||
'js/controller/add-account',
|
'js/controller/add-account',
|
||||||
'js/controller/account',
|
'js/controller/account',
|
||||||
|
'js/controller/contacts',
|
||||||
'js/controller/login',
|
'js/controller/login',
|
||||||
'js/controller/login-initial',
|
'js/controller/login-initial',
|
||||||
'js/controller/login-new-device',
|
'js/controller/login-new-device',
|
||||||
@ -24,6 +25,7 @@ requirejs([
|
|||||||
PopoverCtrl,
|
PopoverCtrl,
|
||||||
AddAccountCtrl,
|
AddAccountCtrl,
|
||||||
AccountCtrl,
|
AccountCtrl,
|
||||||
|
ContactsCtrl,
|
||||||
LoginCtrl,
|
LoginCtrl,
|
||||||
LoginInitialCtrl,
|
LoginInitialCtrl,
|
||||||
LoginNewDeviceCtrl,
|
LoginNewDeviceCtrl,
|
||||||
@ -32,14 +34,25 @@ requirejs([
|
|||||||
ReadCtrl,
|
ReadCtrl,
|
||||||
WriteCtrl,
|
WriteCtrl,
|
||||||
NavigationCtrl,
|
NavigationCtrl,
|
||||||
util) {
|
util
|
||||||
|
) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
// reset window.name
|
// reset window.name
|
||||||
window.name = util.UUID();
|
window.name = util.UUID();
|
||||||
|
|
||||||
// init main angular module including dependencies
|
// init main angular module including dependencies
|
||||||
var app = angular.module('mail', ['ngRoute', 'ngTouch', 'navigation', 'mail-list', 'write', 'read', 'login-new-device', 'popover']);
|
var app = angular.module('mail', [
|
||||||
|
'ngRoute',
|
||||||
|
'ngTouch',
|
||||||
|
'navigation',
|
||||||
|
'mail-list',
|
||||||
|
'write',
|
||||||
|
'read',
|
||||||
|
'contacts',
|
||||||
|
'login-new-device',
|
||||||
|
'popover'
|
||||||
|
]);
|
||||||
|
|
||||||
// set router paths
|
// set router paths
|
||||||
app.config(function($routeProvider) {
|
app.config(function($routeProvider) {
|
||||||
@ -77,6 +90,7 @@ requirejs([
|
|||||||
app.controller('WriteCtrl', WriteCtrl);
|
app.controller('WriteCtrl', WriteCtrl);
|
||||||
app.controller('MailListCtrl', MailListCtrl);
|
app.controller('MailListCtrl', MailListCtrl);
|
||||||
app.controller('AccountCtrl', AccountCtrl);
|
app.controller('AccountCtrl', AccountCtrl);
|
||||||
|
app.controller('ContactsCtrl', ContactsCtrl);
|
||||||
app.controller('DialogCtrl', DialogCtrl);
|
app.controller('DialogCtrl', DialogCtrl);
|
||||||
app.controller('PopoverCtrl', PopoverCtrl);
|
app.controller('PopoverCtrl', PopoverCtrl);
|
||||||
|
|
||||||
|
140
src/js/controller/contacts.js
Normal file
140
src/js/controller/contacts.js
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
define(function(require) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var angular = require('angular'),
|
||||||
|
_ = require('underscore'),
|
||||||
|
appController = require('js/app-controller'),
|
||||||
|
keychain, pgp;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Controller
|
||||||
|
//
|
||||||
|
|
||||||
|
var ContactsCtrl = function($scope) {
|
||||||
|
keychain = appController._keychain,
|
||||||
|
pgp = appController._crypto;
|
||||||
|
|
||||||
|
$scope.state.contacts = {
|
||||||
|
open: false,
|
||||||
|
toggle: function(to) {
|
||||||
|
this.open = to;
|
||||||
|
$scope.listKeys();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// set default value so that the popover height is correct on init
|
||||||
|
$scope.fingerprint = 'XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX';
|
||||||
|
|
||||||
|
//
|
||||||
|
// scope functions
|
||||||
|
//
|
||||||
|
|
||||||
|
$scope.listKeys = function() {
|
||||||
|
keychain.listLocalPublicKeys(function(err, keys) {
|
||||||
|
if (err) {
|
||||||
|
$scope.onError(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
keys.forEach(addParams);
|
||||||
|
|
||||||
|
$scope.keys = keys;
|
||||||
|
$scope.$apply();
|
||||||
|
|
||||||
|
function addParams(key) {
|
||||||
|
var params = pgp.getKeyParams(key.publicKey);
|
||||||
|
_.extend(key, params);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.getFingerprint = function(key) {
|
||||||
|
var fpr = key.fingerprint;
|
||||||
|
var formatted = fpr.slice(0, 4) + ' ' + fpr.slice(4, 8) + ' ' + fpr.slice(8, 12) + ' ' + fpr.slice(12, 16) + ' ' + fpr.slice(16, 20) + ' ... ' + fpr.slice(20, 24) + ' ' + fpr.slice(24, 28) + ' ' + fpr.slice(28, 32) + ' ' + fpr.slice(32, 36) + ' ' + fpr.slice(36);
|
||||||
|
|
||||||
|
$scope.fingerprint = formatted;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.importKey = function(publicKeyArmored) {
|
||||||
|
var keyParams, pubkey;
|
||||||
|
|
||||||
|
// verifiy public key string
|
||||||
|
if (publicKeyArmored.indexOf('-----BEGIN PGP PUBLIC KEY BLOCK-----') < 0) {
|
||||||
|
$scope.onError({
|
||||||
|
errMsg: 'Invalid public key!'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
keyParams = pgp.getKeyParams(publicKeyArmored);
|
||||||
|
} catch (e) {
|
||||||
|
$scope.onError(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pubkey = {
|
||||||
|
_id: keyParams._id,
|
||||||
|
userId: keyParams.userId,
|
||||||
|
publicKey: publicKeyArmored
|
||||||
|
};
|
||||||
|
|
||||||
|
keychain.saveLocalPublicKey(pubkey, function(err) {
|
||||||
|
if (err) {
|
||||||
|
$scope.onError(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update displayed keys
|
||||||
|
$scope.listKeys();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.removeKey = function(key) {
|
||||||
|
keychain.removeLocalPublicKey(key._id, function(err) {
|
||||||
|
if (err) {
|
||||||
|
$scope.onError(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update displayed keys
|
||||||
|
$scope.listKeys();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Directives
|
||||||
|
//
|
||||||
|
|
||||||
|
var ngModule = angular.module('contacts', []);
|
||||||
|
|
||||||
|
ngModule.directive('keyfileInput', function() {
|
||||||
|
return function(scope, elm) {
|
||||||
|
elm.on('change', function(e) {
|
||||||
|
for (var i = 0; i < e.target.files.length; i++) {
|
||||||
|
importKey(e.target.files.item(i));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function importKey(file) {
|
||||||
|
var reader = new FileReader();
|
||||||
|
reader.onload = function(e) {
|
||||||
|
scope.importKey(e.target.result);
|
||||||
|
};
|
||||||
|
reader.readAsText(file);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
ngModule.directive('keyfileBtn', function() {
|
||||||
|
return function(scope, elm) {
|
||||||
|
elm.on('click touchstart', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
document.querySelector('#keyfile-input').click();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return ContactsCtrl;
|
||||||
|
});
|
@ -172,6 +172,11 @@ define(function(require) {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
scope.state.account.toggle(false);
|
scope.state.account.toggle(false);
|
||||||
|
|
||||||
|
} else if (e.keyCode === 27 && scope.state.contacts.open) {
|
||||||
|
// escape -> close contacts view
|
||||||
|
e.preventDefault();
|
||||||
|
scope.state.contacts.toggle(false);
|
||||||
|
|
||||||
} else if (e.keyCode === 27 && scope.state.nav.open) {
|
} else if (e.keyCode === 27 && scope.state.nav.open) {
|
||||||
// escape -> close nav view
|
// escape -> close nav view
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
@ -68,13 +68,8 @@ define(function(require) {
|
|||||||
return fingerprint(this._publicKey);
|
return fingerprint(this._publicKey);
|
||||||
};
|
};
|
||||||
|
|
||||||
PGP.prototype.getUserId = function(keyArmored) {
|
|
||||||
var key = openpgp.key.readArmored(keyArmored).keys[0];
|
|
||||||
return key.getUserIds()[0];
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show a user's key id
|
* Show a user's key id.
|
||||||
*/
|
*/
|
||||||
PGP.prototype.getKeyId = function(keyArmored) {
|
PGP.prototype.getKeyId = function(keyArmored) {
|
||||||
var key, pubKeyId, privKeyId;
|
var key, pubKeyId, privKeyId;
|
||||||
@ -100,6 +95,23 @@ define(function(require) {
|
|||||||
return pubKeyId;
|
return pubKeyId;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read all relevant params of an armored key.
|
||||||
|
*/
|
||||||
|
PGP.prototype.getKeyParams = function(keyArmored) {
|
||||||
|
var key = openpgp.key.readArmored(keyArmored).keys[0],
|
||||||
|
packet = key.getKeyPacket();
|
||||||
|
|
||||||
|
return {
|
||||||
|
_id: packet.getKeyId().toHex().toUpperCase(),
|
||||||
|
userId: key.getUserIds()[0].split('<')[1].split('>')[0],
|
||||||
|
fingerprint: util.hexstrdump(packet.getFingerprint()).toUpperCase(),
|
||||||
|
algorithm: packet.algorithm,
|
||||||
|
bitSize: packet.getBitSize(),
|
||||||
|
created: packet.created,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Import the user's key pair
|
* Import the user's key pair
|
||||||
*/
|
*/
|
||||||
|
@ -156,8 +156,8 @@ define(function(require) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check if the key's user ID matches the current account
|
// check if the key's user ID matches the current account
|
||||||
pubUserID = self._crypto.getUserId(keypair.publicKey.publicKey);
|
pubUserID = self._crypto.getKeyParams(keypair.publicKey.publicKey).userId;
|
||||||
privUserID = self._crypto.getUserId(keypair.privateKey.encryptedKey);
|
privUserID = self._crypto.getKeyParams(keypair.privateKey.encryptedKey).userId;
|
||||||
if (pubUserID.indexOf(self._account.emailAddress) === -1 || privUserID.indexOf(self._account.emailAddress) === -1) {
|
if (pubUserID.indexOf(self._account.emailAddress) === -1 || privUserID.indexOf(self._account.emailAddress) === -1) {
|
||||||
callback({
|
callback({
|
||||||
errMsg: 'User IDs dont match!'
|
errMsg: 'User IDs dont match!'
|
||||||
|
@ -256,31 +256,43 @@ define(function(require) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pubkey) {
|
if (pubkey) {
|
||||||
// fetch from cloud storage
|
callback(null, pubkey);
|
||||||
self._publicKeyDao.get(id, function(err, cloudPubkey) {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetch from cloud storage
|
||||||
|
self._publicKeyDao.get(id, function(err, cloudPubkey) {
|
||||||
|
if (err) {
|
||||||
|
callback(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// cache public key in cache
|
||||||
|
self.saveLocalPublicKey(cloudPubkey, function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
callback(err);
|
callback(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// cache public key in cache
|
callback(null, cloudPubkey);
|
||||||
self.saveLocalPublicKey(cloudPubkey, function(err) {
|
|
||||||
if (err) {
|
|
||||||
callback(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(null, cloudPubkey);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
} else {
|
|
||||||
callback(null, pubkey);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List all the locally stored public keys
|
||||||
|
*/
|
||||||
|
KeychainDAO.prototype.listLocalPublicKeys = function(callback) {
|
||||||
|
// search local keyring for public key
|
||||||
|
this._localDbDao.list('publickey', 0, null, callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
KeychainDAO.prototype.removeLocalPublicKey = function(id, callback) {
|
||||||
|
this._localDbDao.remove('publickey_' + id, callback);
|
||||||
|
};
|
||||||
|
|
||||||
KeychainDAO.prototype.lookupPrivateKey = function(id, callback) {
|
KeychainDAO.prototype.lookupPrivateKey = function(id, callback) {
|
||||||
// lookup in local storage
|
// lookup in local storage
|
||||||
this._localDbDao.read('privatekey_' + id, callback);
|
this._localDbDao.read('privatekey_' + id, callback);
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
@import "views/shared";
|
@import "views/shared";
|
||||||
@import "views/add-account";
|
@import "views/add-account";
|
||||||
@import "views/account";
|
@import "views/account";
|
||||||
|
@import "views/contacts";
|
||||||
@import "views/dialog";
|
@import "views/dialog";
|
||||||
@import "views/navigation";
|
@import "views/navigation";
|
||||||
@import "views/mail-list";
|
@import "views/mail-list";
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
transition: background-color 0.3s;
|
transition: background-color 0.3s;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
|
outline: 0;
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&:focus {
|
&:focus {
|
||||||
@ -28,7 +29,6 @@
|
|||||||
|
|
||||||
&:active,
|
&:active,
|
||||||
&.active {
|
&.active {
|
||||||
outline: 0;
|
|
||||||
background-image: none;
|
background-image: none;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
top: 1px;
|
top: 1px;
|
||||||
|
42
src/sass/views/_contacts.scss
Normal file
42
src/sass/views/_contacts.scss
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
.view-contacts {
|
||||||
|
|
||||||
|
.key-controls {
|
||||||
|
margin: 30px;
|
||||||
|
|
||||||
|
input[type=text] {
|
||||||
|
line-height: 23px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=file] {
|
||||||
|
visibility: hidden;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.key-list {
|
||||||
|
max-height: 400px;
|
||||||
|
margin: 20px;
|
||||||
|
overflow-y: scroll;
|
||||||
|
|
||||||
|
table {
|
||||||
|
th, td {
|
||||||
|
padding: 5px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.remove {
|
||||||
|
font-family: $font-family-icons;
|
||||||
|
font-size: 0.75em;
|
||||||
|
color: $color-grey-input;
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -79,6 +79,8 @@
|
|||||||
margin-top: em(50, 16);
|
margin-top: em(50, 16);
|
||||||
padding: 0 $nav-padding;
|
padding: 0 $nav-padding;
|
||||||
li {
|
li {
|
||||||
|
margin-bottom: 0.25em;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
font-size: $font-size-big;
|
font-size: $font-size-big;
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="view-account">
|
<div class="view-account">
|
||||||
<table summary="Kontakt">
|
<table>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Email</td>
|
<td>Email</td>
|
||||||
|
45
src/tpl/contacts.html
Normal file
45
src/tpl/contacts.html
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<div class="lightbox-body" ng-controller="ContactsCtrl">
|
||||||
|
<header>
|
||||||
|
<h2>Contacts</h2>
|
||||||
|
<button class="close" ng-click="state.contacts.toggle(false)" data-action="lightbox-close"></button>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="content">
|
||||||
|
<div class="view-contacts">
|
||||||
|
|
||||||
|
<div class="key-controls">
|
||||||
|
<input class="input-text" type="text" placeholder="Search..." ng-model="searchText">
|
||||||
|
<span>
|
||||||
|
<input id="keyfile-input" type="file" multiple accept=".asc" keyfile-input>
|
||||||
|
<button class="btn" ng-class="{'btn-primary': sendBtnSecure === false}" keyfile-btn>Import public keys</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="key-list">
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Key ID</th>
|
||||||
|
<th>Email</th>
|
||||||
|
<th>Created</th>
|
||||||
|
<th>Size</th>
|
||||||
|
</tr>
|
||||||
|
<tr ng-repeat="key in keys | orderBy:'userId' | filter:searchText">
|
||||||
|
<td class="hover" ng-mouseover="getFingerprint(key)" popover="#fingerprint-contact">{{key._id.slice(8)}}</td>
|
||||||
|
<td>{{key.userId}}</td>
|
||||||
|
<td>{{key.created | date:'mediumDate'}}</td>
|
||||||
|
<td>{{key.bitSize}} bit</td>
|
||||||
|
<td><button class="remove" ng-click="removeKey(key)"></button></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div><!-- /.view-contacts -->
|
||||||
|
</div><!-- /.content -->
|
||||||
|
|
||||||
|
<!-- popovers -->
|
||||||
|
<div id="fingerprint-contact" class="popover right" ng-controller="PopoverCtrl">
|
||||||
|
<div class="popover-title"><b>Fingerprint</b></div>
|
||||||
|
<div class="popover-content">{{fingerprint}}</div>
|
||||||
|
</div><!--/.popover-->
|
||||||
|
|
||||||
|
</div><!-- /.lightbox-body -->
|
@ -25,6 +25,9 @@
|
|||||||
<div class="lightbox-overlay" ng-class="{'show': state.account.open}">
|
<div class="lightbox-overlay" ng-class="{'show': state.account.open}">
|
||||||
<div class="lightbox lightbox-effect" ng-include="'tpl/account.html'"></div>
|
<div class="lightbox lightbox-effect" ng-include="'tpl/account.html'"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="lightbox-overlay" ng-class="{'show': state.contacts.open}">
|
||||||
|
<div class="lightbox lightbox-effect" ng-include="'tpl/contacts.html'"></div>
|
||||||
|
</div>
|
||||||
<div class="lightbox-overlay" ng-class="{'show': state.dialog.open}">
|
<div class="lightbox-overlay" ng-class="{'show': state.dialog.open}">
|
||||||
<div class="lightbox lightbox-effect view-dialog" ng-include="'tpl/dialog.html'"></div>
|
<div class="lightbox lightbox-effect view-dialog" ng-include="'tpl/dialog.html'"></div>
|
||||||
</div>
|
</div>
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
<form>
|
<form>
|
||||||
<div>
|
<div>
|
||||||
<input type="file" file-reader tabindex="1">
|
<input type="file" accept=".asc" file-reader tabindex="1">
|
||||||
<span class="popover-info" data-icon-append="" popover="#keyfile-info"></span>
|
<span class="popover-info" data-icon-append="" popover="#keyfile-info"></span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
<div class="list-wrapper" ng-iscroll="filteredMessages.length">
|
<div class="list-wrapper" ng-iscroll="filteredMessages.length">
|
||||||
<ul class="mail-list">
|
<ul class="mail-list">
|
||||||
<li ng-class="{'mail-list-active': email === state.mailList.selected, 'mail-list-attachment': email.attachments !== undefined && email.attachments.length > 0, 'mail-list-unread': email.unread, 'mail-list-replied': !email.unread && email.answered}" ng-click="select(email)" ng-repeat="email in (filteredMessages = (state.nav.currentFolder.messages | orderBy:'uid':true | filter:searchText))">
|
<li ng-class="{'mail-list-active': email === state.mailList.selected, 'mail-list-attachment': email.attachments !== undefined && email.attachments.length > 0, 'mail-list-unread': email.unread, 'mail-list-replied': !email.unread && email.answered}" ng-click="select(email)" ng-repeat="email in (filteredMessages = (state.nav.currentFolder.messages | filter:searchText | orderBy:'uid':true | limitTo:100))">
|
||||||
<h3>{{email.from[0].name || email.from[0].address}}</h3>
|
<h3>{{email.from[0].name || email.from[0].address}}</h3>
|
||||||
<div class="encrypted" data-icon="{{email.encrypted ? '' : ''}}"></div>
|
<div class="encrypted" data-icon="{{email.encrypted ? '' : ''}}"></div>
|
||||||
<div class="head">
|
<div class="head">
|
||||||
|
@ -16,7 +16,8 @@
|
|||||||
|
|
||||||
<ul class="nav-secondary">
|
<ul class="nav-secondary">
|
||||||
<li><a href="#" ng-click="state.account.toggle(true); $event.preventDefault()">Account</a></li>
|
<li><a href="#" ng-click="state.account.toggle(true); $event.preventDefault()">Account</a></li>
|
||||||
<li><a href="http://whiteout.io" target="_blank">About whiteout.io</a></li>
|
<li><a href="#" ng-click="state.contacts.toggle(true); $event.preventDefault()">Contacts</a></li>
|
||||||
|
<li><a href="http://whiteout.io" target="_blank">About</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
|
@ -20,10 +20,8 @@ define(function(require) {
|
|||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
origEmailDao = appController._emailDao;
|
origEmailDao = appController._emailDao;
|
||||||
cryptoMock = sinon.createStubInstance(PGP);
|
appController._emailDao = emailDaoMock = sinon.createStubInstance(EmailDAO);
|
||||||
emailDaoMock = sinon.createStubInstance(EmailDAO);
|
emailDaoMock._crypto = cryptoMock = sinon.createStubInstance(PGP);
|
||||||
emailDaoMock._crypto = cryptoMock;
|
|
||||||
appController._emailDao = emailDaoMock;
|
|
||||||
|
|
||||||
dummyFingerprint = '3A2D39B4E1404190B8B949DE7D7E99036E712926';
|
dummyFingerprint = '3A2D39B4E1404190B8B949DE7D7E99036E712926';
|
||||||
expectedFingerprint = '3A2D 39B4 E140 4190 B8B9 49DE 7D7E 9903 6E71 2926';
|
expectedFingerprint = '3A2D 39B4 E140 4190 B8B9 49DE 7D7E 9903 6E71 2926';
|
||||||
|
188
test/new-unit/contacts-ctrl-test.js
Normal file
188
test/new-unit/contacts-ctrl-test.js
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
define(function(require) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var expect = chai.expect,
|
||||||
|
angular = require('angular'),
|
||||||
|
mocks = require('angularMocks'),
|
||||||
|
ContactsCtrl = require('js/controller/contacts'),
|
||||||
|
appController = require('js/app-controller'),
|
||||||
|
KeychainDAO = require('js/dao/keychain-dao'),
|
||||||
|
PGP = require('js/crypto/pgp');
|
||||||
|
|
||||||
|
describe('Contacts Controller unit test', function() {
|
||||||
|
var scope, contactsCtrl,
|
||||||
|
origKeychain, keychainMock,
|
||||||
|
origCrypto, cryptoMock;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
origCrypto = appController._crypto;
|
||||||
|
appController._crypto = cryptoMock = sinon.createStubInstance(PGP);
|
||||||
|
origKeychain = appController._keychain;
|
||||||
|
appController._keychain = keychainMock = sinon.createStubInstance(KeychainDAO);
|
||||||
|
|
||||||
|
angular.module('contactstest', []);
|
||||||
|
mocks.module('contactstest');
|
||||||
|
mocks.inject(function($rootScope, $controller) {
|
||||||
|
scope = $rootScope.$new();
|
||||||
|
scope.state = {};
|
||||||
|
contactsCtrl = $controller(ContactsCtrl, {
|
||||||
|
$scope: scope
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function() {
|
||||||
|
// restore the module
|
||||||
|
appController._crypto = origCrypto;
|
||||||
|
appController._keychain = origKeychain;
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('scope variables', function() {
|
||||||
|
it('should be set correctly', function() {
|
||||||
|
expect(scope.fingerprint).to.equal('XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX');
|
||||||
|
expect(scope.state.contacts.open).to.be.false;
|
||||||
|
expect(scope.state.contacts.toggle).to.exist;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('listKeys', function() {
|
||||||
|
it('should fail due to error in keychain.listLocalPublicKeys', function(done) {
|
||||||
|
keychainMock.listLocalPublicKeys.yields(42);
|
||||||
|
|
||||||
|
scope.onError = function(err) {
|
||||||
|
expect(err).to.equal(42);
|
||||||
|
done();
|
||||||
|
};
|
||||||
|
|
||||||
|
scope.listKeys();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work', function(done) {
|
||||||
|
keychainMock.listLocalPublicKeys.yields(null, [{
|
||||||
|
_id: '12345'
|
||||||
|
}]);
|
||||||
|
cryptoMock.getKeyParams.returns({
|
||||||
|
fingerprint: 'asdf'
|
||||||
|
});
|
||||||
|
|
||||||
|
scope.$apply = function() {
|
||||||
|
expect(scope.keys.length).to.equal(1);
|
||||||
|
expect(scope.keys[0]._id).to.equal('12345');
|
||||||
|
expect(scope.keys[0].fingerprint).to.equal('asdf');
|
||||||
|
done();
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(scope.keys).to.not.exist;
|
||||||
|
scope.listKeys();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getFingerprint', function() {
|
||||||
|
it('should work', function() {
|
||||||
|
var key = {
|
||||||
|
fingerprint: 'YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY'
|
||||||
|
};
|
||||||
|
|
||||||
|
scope.getFingerprint(key);
|
||||||
|
|
||||||
|
expect(scope.fingerprint).to.equal('YYYY YYYY YYYY YYYY YYYY ... YYYY YYYY YYYY YYYY YYYY');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('importKey', function() {
|
||||||
|
it('should work', function(done) {
|
||||||
|
var keyArmored = '-----BEGIN PGP PUBLIC KEY BLOCK-----';
|
||||||
|
|
||||||
|
cryptoMock.getKeyParams.returns({
|
||||||
|
_id: '12345',
|
||||||
|
userId: 'max@example.com'
|
||||||
|
});
|
||||||
|
|
||||||
|
keychainMock.saveLocalPublicKey.withArgs({
|
||||||
|
_id: '12345',
|
||||||
|
userId: 'max@example.com',
|
||||||
|
publicKey: '-----BEGIN PGP PUBLIC KEY BLOCK-----'
|
||||||
|
}).yields();
|
||||||
|
|
||||||
|
scope.listKeys = function() {
|
||||||
|
done();
|
||||||
|
};
|
||||||
|
|
||||||
|
scope.importKey(keyArmored);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail due to invalid armored key', function(done) {
|
||||||
|
var keyArmored = '-----BEGIN PGP PRIVATE KEY BLOCK-----';
|
||||||
|
|
||||||
|
scope.onError = function(err) {
|
||||||
|
expect(err).to.exist;
|
||||||
|
done();
|
||||||
|
};
|
||||||
|
|
||||||
|
scope.importKey(keyArmored);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail due to error in pgp.getKeyParams', function(done) {
|
||||||
|
var keyArmored = '-----BEGIN PGP PUBLIC KEY BLOCK-----';
|
||||||
|
|
||||||
|
cryptoMock.getKeyParams.throws(new Error('WAT'));
|
||||||
|
|
||||||
|
scope.onError = function(err) {
|
||||||
|
expect(err).to.exist;
|
||||||
|
done();
|
||||||
|
};
|
||||||
|
|
||||||
|
scope.importKey(keyArmored);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail due to error in keychain.saveLocalPublicKey', function(done) {
|
||||||
|
var keyArmored = '-----BEGIN PGP PUBLIC KEY BLOCK-----';
|
||||||
|
|
||||||
|
cryptoMock.getKeyParams.returns({
|
||||||
|
_id: '12345',
|
||||||
|
userId: 'max@example.com'
|
||||||
|
});
|
||||||
|
|
||||||
|
keychainMock.saveLocalPublicKey.yields(42);
|
||||||
|
|
||||||
|
scope.onError = function(err) {
|
||||||
|
expect(err).to.equal(42);
|
||||||
|
done();
|
||||||
|
};
|
||||||
|
|
||||||
|
scope.importKey(keyArmored);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('removeKey', function() {
|
||||||
|
it('should work', function(done) {
|
||||||
|
var key = {
|
||||||
|
_id: '12345'
|
||||||
|
};
|
||||||
|
|
||||||
|
keychainMock.removeLocalPublicKey.withArgs('12345').yields();
|
||||||
|
|
||||||
|
scope.listKeys = function() {
|
||||||
|
done();
|
||||||
|
};
|
||||||
|
|
||||||
|
scope.removeKey(key);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail due to error in keychain.removeLocalPublicKey', function(done) {
|
||||||
|
var key = {
|
||||||
|
_id: '12345'
|
||||||
|
};
|
||||||
|
|
||||||
|
keychainMock.removeLocalPublicKey.withArgs('12345').yields(42);
|
||||||
|
|
||||||
|
scope.onError = function(err) {
|
||||||
|
expect(err).to.equal(42);
|
||||||
|
done();
|
||||||
|
};
|
||||||
|
|
||||||
|
scope.removeKey(key);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -354,7 +354,9 @@ define(function(require) {
|
|||||||
_pgpbuilder: {}
|
_pgpbuilder: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
pgpStub.getUserId.returns('Whiteout User <' + emailAddress + '>');
|
pgpStub.getKeyParams.returns({
|
||||||
|
userId: emailAddress
|
||||||
|
});
|
||||||
|
|
||||||
pgpStub.importKeys.withArgs({
|
pgpStub.importKeys.withArgs({
|
||||||
passphrase: passphrase,
|
passphrase: passphrase,
|
||||||
|
@ -32,6 +32,30 @@ define(function(require) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('listLocalPublicKeys', function() {
|
||||||
|
it('should work', function(done) {
|
||||||
|
lawnchairDaoStub.list.withArgs('publickey', 0, null).yields();
|
||||||
|
|
||||||
|
keychainDao.listLocalPublicKeys(function() {
|
||||||
|
expect(lawnchairDaoStub.list.callCount).to.equal(1);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('removeLocalPublicKey', function() {
|
||||||
|
it('should work', function(done) {
|
||||||
|
var id = 'asdf';
|
||||||
|
|
||||||
|
lawnchairDaoStub.remove.withArgs('publickey_' + id).yields();
|
||||||
|
|
||||||
|
keychainDao.removeLocalPublicKey(id, function() {
|
||||||
|
expect(lawnchairDaoStub.remove.callCount).to.equal(1);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('lookup public key', function() {
|
describe('lookup public key', function() {
|
||||||
it('should fail', function(done) {
|
it('should fail', function(done) {
|
||||||
keychainDao.lookupPublicKey(undefined, function(err, key) {
|
keychainDao.lookupPublicKey(undefined, function(err, key) {
|
||||||
|
@ -39,6 +39,7 @@ function startTests() {
|
|||||||
'test/new-unit/dialog-ctrl-test',
|
'test/new-unit/dialog-ctrl-test',
|
||||||
'test/new-unit/add-account-ctrl-test',
|
'test/new-unit/add-account-ctrl-test',
|
||||||
'test/new-unit/account-ctrl-test',
|
'test/new-unit/account-ctrl-test',
|
||||||
|
'test/new-unit/contacts-ctrl-test',
|
||||||
'test/new-unit/login-existing-ctrl-test',
|
'test/new-unit/login-existing-ctrl-test',
|
||||||
'test/new-unit/login-initial-ctrl-test',
|
'test/new-unit/login-initial-ctrl-test',
|
||||||
'test/new-unit/login-new-device-ctrl-test',
|
'test/new-unit/login-new-device-ctrl-test',
|
||||||
|
@ -5,7 +5,7 @@ define(function(require) {
|
|||||||
expect = chai.expect;
|
expect = chai.expect;
|
||||||
|
|
||||||
describe('PGP Crypto Api unit tests', function() {
|
describe('PGP Crypto Api unit tests', function() {
|
||||||
this.timeout(10000);
|
this.timeout(20000);
|
||||||
|
|
||||||
var pgp,
|
var pgp,
|
||||||
user = 'whiteout.test@t-online.de',
|
user = 'whiteout.test@t-online.de',
|
||||||
@ -150,13 +150,6 @@ define(function(require) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Get UserId', function() {
|
|
||||||
it('should work with param', function() {
|
|
||||||
var userId = pgp.getUserId(pubkey);
|
|
||||||
expect(userId).to.contain(user);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Get Fingerprint', function() {
|
describe('Get Fingerprint', function() {
|
||||||
it('should work without param', function() {
|
it('should work without param', function() {
|
||||||
var fingerprint = pgp.getFingerprint();
|
var fingerprint = pgp.getFingerprint();
|
||||||
@ -169,6 +162,17 @@ define(function(require) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getKeyParams', function() {
|
||||||
|
it('should work with param', function() {
|
||||||
|
var params = pgp.getKeyParams(pubkey);
|
||||||
|
expect(params.fingerprint).to.equal('5856CEF789C3A307E8A1B976F6F60E9B42CDFF4C');
|
||||||
|
expect(params._id).to.equal("F6F60E9B42CDFF4C");
|
||||||
|
expect(params.bitSize).to.equal(keySize);
|
||||||
|
expect(params.userId).to.equal("whiteout.test@t-online.de");
|
||||||
|
expect(params.algorithm).to.equal("rsa_encrypt_sign");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('Encrypt and sign', function() {
|
describe('Encrypt and sign', function() {
|
||||||
it('should fail', function(done) {
|
it('should fail', function(done) {
|
||||||
var input = null;
|
var input = null;
|
||||||
|
Loading…
Reference in New Issue
Block a user