mirror of
https://github.com/moparisthebest/mail
synced 2024-11-26 19:02:20 -05:00
Merge pull request #49 from whiteout-io/dev/change-passphrase
[WO-296] implement change passphrase ui
This commit is contained in:
commit
580baa34d2
@ -7,6 +7,7 @@ requirejs([
|
||||
'js/controller/popover',
|
||||
'js/controller/add-account',
|
||||
'js/controller/account',
|
||||
'js/controller/set-passphrase',
|
||||
'js/controller/contacts',
|
||||
'js/controller/login',
|
||||
'js/controller/login-initial',
|
||||
@ -26,6 +27,7 @@ requirejs([
|
||||
PopoverCtrl,
|
||||
AddAccountCtrl,
|
||||
AccountCtrl,
|
||||
SetPassphraseCtrl,
|
||||
ContactsCtrl,
|
||||
LoginCtrl,
|
||||
LoginInitialCtrl,
|
||||
@ -92,6 +94,7 @@ requirejs([
|
||||
app.controller('WriteCtrl', WriteCtrl);
|
||||
app.controller('MailListCtrl', MailListCtrl);
|
||||
app.controller('AccountCtrl', AccountCtrl);
|
||||
app.controller('SetPassphraseCtrl', SetPassphraseCtrl);
|
||||
app.controller('ContactsCtrl', ContactsCtrl);
|
||||
app.controller('DialogCtrl', DialogCtrl);
|
||||
app.controller('PopoverCtrl', PopoverCtrl);
|
||||
|
@ -3,14 +3,16 @@ define(function(require) {
|
||||
|
||||
var appController = require('js/app-controller'),
|
||||
dl = require('js/util/download'),
|
||||
emailDao;
|
||||
pgp, keychain, userId;
|
||||
|
||||
//
|
||||
// Controller
|
||||
//
|
||||
|
||||
var AccountCtrl = function($scope) {
|
||||
emailDao = appController._emailDao;
|
||||
userId = appController._emailDao._account.emailAddress;
|
||||
keychain = appController._keychain;
|
||||
pgp = appController._crypto;
|
||||
|
||||
$scope.state.account = {
|
||||
open: false,
|
||||
@ -23,32 +25,49 @@ define(function(require) {
|
||||
// scope variables
|
||||
//
|
||||
|
||||
var fpr = emailDao._crypto.getFingerprint(),
|
||||
keyId = emailDao._crypto.getKeyId();
|
||||
$scope.eMail = emailDao._account.emailAddress;
|
||||
$scope.keyId = keyId.slice(8);
|
||||
var keyParams = pgp.getKeyParams();
|
||||
|
||||
$scope.eMail = userId;
|
||||
$scope.keyId = keyParams._id.slice(8);
|
||||
var fpr = keyParams.fingerprint;
|
||||
$scope.fingerprint = 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.keysize = emailDao._account.asymKeySize;
|
||||
$scope.keysize = keyParams.bitSize;
|
||||
|
||||
//
|
||||
// scope functions
|
||||
//
|
||||
|
||||
$scope.exportKeyFile = function() {
|
||||
emailDao._crypto.exportKeys(function(err, keys) {
|
||||
keychain.getUserKeyPair(userId, function(err, keys) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
var id = 'whiteout_mail_' + emailDao._account.emailAddress + '_' + keys.keyId.substring(8, keys.keyId.length);
|
||||
var keyId = keys.publicKey._id;
|
||||
var file = 'whiteout_mail_' + userId + '_' + keyId.substring(8, keyId.length);
|
||||
|
||||
dl.createDownload({
|
||||
content: keys.publicKeyArmored + keys.privateKeyArmored,
|
||||
filename: id + '.asc',
|
||||
content: keys.publicKey.publicKey + keys.privateKey.encryptedKey,
|
||||
filename: file + '.asc',
|
||||
contentType: 'text/plain'
|
||||
}, $scope.onError);
|
||||
}, onExport);
|
||||
});
|
||||
};
|
||||
|
||||
function onExport(err) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.state.account.toggle(false);
|
||||
$scope.$apply();
|
||||
$scope.onError({
|
||||
title: 'Success',
|
||||
message: 'Exported keypair to file.'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return AccountCtrl;
|
||||
|
83
src/js/controller/set-passphrase.js
Normal file
83
src/js/controller/set-passphrase.js
Normal file
@ -0,0 +1,83 @@
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var appController = require('js/app-controller'),
|
||||
pgp, keychain;
|
||||
|
||||
//
|
||||
// Controller
|
||||
//
|
||||
|
||||
var SetPassphraseCtrl = function($scope) {
|
||||
keychain = appController._keychain;
|
||||
pgp = appController._crypto;
|
||||
|
||||
$scope.state.setPassphrase = {
|
||||
open: false,
|
||||
toggle: function(to) {
|
||||
this.open = to;
|
||||
|
||||
$scope.newPassphrase = undefined;
|
||||
$scope.oldPassphrase = undefined;
|
||||
$scope.confirmation = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// scope variables
|
||||
//
|
||||
|
||||
//
|
||||
// scope functions
|
||||
//
|
||||
|
||||
$scope.setPassphrase = function() {
|
||||
var keyId = pgp.getKeyParams()._id;
|
||||
keychain.lookupPrivateKey(keyId, function(err, savedKey) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
pgp.changePassphrase({
|
||||
privateKeyArmored: savedKey.encryptedKey,
|
||||
oldPassphrase: $scope.oldPassphrase,
|
||||
newPassphrase: $scope.newPassphrase
|
||||
}, onPassphraseChanged);
|
||||
});
|
||||
};
|
||||
|
||||
function onPassphraseChanged(err, newPrivateKeyArmored) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// persist new armored key
|
||||
var keyParams = pgp.getKeyParams(newPrivateKeyArmored);
|
||||
var privateKey = {
|
||||
_id: keyParams._id,
|
||||
userId: keyParams.userId,
|
||||
encryptedKey: newPrivateKeyArmored
|
||||
};
|
||||
|
||||
keychain.saveLocalPrivateKey(privateKey, onKeyPersisted);
|
||||
}
|
||||
|
||||
function onKeyPersisted(err) {
|
||||
if (err) {
|
||||
$scope.onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.state.setPassphrase.toggle(false);
|
||||
$scope.$apply();
|
||||
$scope.onError({
|
||||
title: 'Success',
|
||||
message: 'Passphrase change complete.'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return SetPassphraseCtrl;
|
||||
});
|
@ -17,7 +17,7 @@ define(function(require) {
|
||||
* Generate a key pair for the user
|
||||
*/
|
||||
PGP.prototype.generateKeys = function(options, callback) {
|
||||
var userId;
|
||||
var userId, passphrase;
|
||||
|
||||
if (!util.emailRegEx.test(options.emailAddress) || !options.keySize) {
|
||||
callback({
|
||||
@ -28,7 +28,8 @@ define(function(require) {
|
||||
|
||||
// generate keypair (keytype 1=RSA)
|
||||
userId = 'Whiteout User <' + options.emailAddress + '>';
|
||||
openpgp.generateKeyPair(1, options.keySize, userId, options.passphrase, onGenerated);
|
||||
passphrase = (options.passphrase) ? options.passphrase : undefined;
|
||||
openpgp.generateKeyPair(1, options.keySize, userId, passphrase, onGenerated);
|
||||
|
||||
function onGenerated(err, keys) {
|
||||
if (err) {
|
||||
@ -99,7 +100,17 @@ define(function(require) {
|
||||
* Read all relevant params of an armored key.
|
||||
*/
|
||||
PGP.prototype.getKeyParams = function(keyArmored) {
|
||||
var key = openpgp.key.readArmored(keyArmored).keys[0],
|
||||
var key, packet;
|
||||
|
||||
// process armored key input
|
||||
if (keyArmored) {
|
||||
key = openpgp.key.readArmored(keyArmored).keys[0];
|
||||
} else if (this._publicKey) {
|
||||
key = this._publicKey;
|
||||
} else {
|
||||
throw new Error('Cannot read key params... keys not set!');
|
||||
}
|
||||
|
||||
packet = key.getKeyPacket();
|
||||
|
||||
return {
|
||||
@ -188,17 +199,24 @@ define(function(require) {
|
||||
* Change the passphrase of an ascii armored private key.
|
||||
*/
|
||||
PGP.prototype.changePassphrase = function(options, callback) {
|
||||
var privKey, packets;
|
||||
var privKey, packets, newPassphrase, newKeyArmored;
|
||||
|
||||
if (!options.privateKeyArmored ||
|
||||
typeof options.oldPassphrase !== 'string' ||
|
||||
typeof options.newPassphrase !== 'string') {
|
||||
// set undefined instead of empty string as passphrase
|
||||
newPassphrase = (options.newPassphrase) ? options.newPassphrase : undefined;
|
||||
|
||||
if (!options.privateKeyArmored) {
|
||||
callback({
|
||||
errMsg: 'Could not export keys!'
|
||||
errMsg: '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];
|
||||
@ -221,8 +239,9 @@ define(function(require) {
|
||||
try {
|
||||
packets = privKey.getAllKeyPackets();
|
||||
for (var i = 0; i < packets.length; i++) {
|
||||
packets[i].encrypt(options.newPassphrase);
|
||||
packets[i].encrypt(newPassphrase);
|
||||
}
|
||||
newKeyArmored = privKey.armor();
|
||||
} catch (e) {
|
||||
callback({
|
||||
errMsg: 'Setting new passphrase failed!'
|
||||
@ -230,7 +249,15 @@ define(function(require) {
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, privKey.armor());
|
||||
// check if new passphrase really works
|
||||
if (!privKey.decrypt(newPassphrase)) {
|
||||
callback({
|
||||
errMsg: 'Decrypting key with new passphrase failed!'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, newKeyArmored);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -10,7 +10,11 @@ define(function() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (options.stack) {
|
||||
console.error(options.stack);
|
||||
} else {
|
||||
console.error(options);
|
||||
}
|
||||
|
||||
scope.state.dialog = {
|
||||
open: true,
|
||||
|
@ -15,6 +15,7 @@
|
||||
@import "components/icons";
|
||||
@import "components/lightbox";
|
||||
@import "components/nav";
|
||||
@import "components/dialog";
|
||||
@import "components/mail-list";
|
||||
@import "components/layout";
|
||||
@import "components/popover";
|
||||
@ -24,6 +25,7 @@
|
||||
@import "views/shared";
|
||||
@import "views/add-account";
|
||||
@import "views/account";
|
||||
@import "views/set-passphrase";
|
||||
@import "views/contacts";
|
||||
@import "views/dialog";
|
||||
@import "views/navigation";
|
||||
|
17
src/sass/components/_dialog.scss
Normal file
17
src/sass/components/_dialog.scss
Normal file
@ -0,0 +1,17 @@
|
||||
.dialog {
|
||||
padding: 0px;
|
||||
color: $color-grey-dark;
|
||||
|
||||
@include respond-to(mobile) {
|
||||
top: 0;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.control {
|
||||
float: right;
|
||||
|
||||
button {
|
||||
border: 0!important;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,13 +1,7 @@
|
||||
.view-account {
|
||||
padding: 0px;
|
||||
color: $color-grey-dark;
|
||||
|
||||
@include respond-to(mobile) {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
table {
|
||||
margin: 50px auto 100px auto;
|
||||
margin: 50px auto 60px auto;
|
||||
|
||||
td {
|
||||
padding-top: 15px;
|
||||
@ -20,13 +14,4 @@
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
border: 0!important;
|
||||
}
|
||||
|
||||
.export-control {
|
||||
position: absolute;
|
||||
bottom: 15px;
|
||||
right: 15px;
|
||||
}
|
||||
}
|
@ -1,29 +1,17 @@
|
||||
.view-dialog {
|
||||
padding: 0px;
|
||||
color: $color-grey-dark;
|
||||
max-width: 350px;
|
||||
height: auto;
|
||||
top: 30%;
|
||||
|
||||
@include respond-to(mobile) {
|
||||
top: 0;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
p {
|
||||
text-align: center;
|
||||
max-width: 80%;
|
||||
margin: 30px auto 70px auto;
|
||||
margin: 30px auto;
|
||||
}
|
||||
|
||||
.control {
|
||||
position: absolute;
|
||||
bottom: $lightbox-padding;
|
||||
right: $lightbox-padding;
|
||||
|
||||
button {
|
||||
width: 100px;
|
||||
border: 0!important;
|
||||
}
|
||||
}
|
||||
}
|
25
src/sass/views/_set-passphrase.scss
Normal file
25
src/sass/views/_set-passphrase.scss
Normal file
@ -0,0 +1,25 @@
|
||||
.view-set-passphrase {
|
||||
|
||||
.inputs {
|
||||
margin: 40px 60px 30px;
|
||||
|
||||
div {
|
||||
margin: 5px 0;
|
||||
}
|
||||
}
|
||||
|
||||
table {
|
||||
margin: 50px auto 60px auto;
|
||||
|
||||
td {
|
||||
padding-top: 15px;
|
||||
|
||||
&:first-child {
|
||||
text-align: right;
|
||||
padding-right: 15px;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -5,7 +5,8 @@
|
||||
</header>
|
||||
|
||||
<div class="content">
|
||||
<div class="view-account">
|
||||
<div class="dialog view-account">
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
@ -26,9 +27,12 @@
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="export-control">
|
||||
|
||||
<div class="control">
|
||||
<button ng-click="state.account.toggle(false); state.setPassphrase.toggle(true)" class="btn btn-alt">Set passphrase</button>
|
||||
<button ng-click="exportKeyFile()" class="btn">Export keypair</button>
|
||||
</div>
|
||||
|
||||
</div><!-- /.view-account -->
|
||||
</div><!-- /.content -->
|
||||
</div><!-- /.lightbox-body -->
|
@ -25,9 +25,12 @@
|
||||
<div class="lightbox-overlay" ng-class="{'show': state.account.open}">
|
||||
<div class="lightbox lightbox-effect" ng-include="'tpl/account.html'"></div>
|
||||
</div>
|
||||
<div class="lightbox-overlay" ng-class="{'show': state.setPassphrase.open}">
|
||||
<div class="lightbox lightbox-effect" ng-include="'tpl/set-passphrase.html'"></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 lightbox-effect view-dialog" ng-include="'tpl/dialog.html'"></div>
|
||||
<div class="lightbox lightbox-effect dialog view-dialog" ng-include="'tpl/dialog.html'"></div>
|
||||
</div>
|
@ -39,6 +39,5 @@
|
||||
<div class="popover-content">
|
||||
<p>A passphrase is like a password that protects your PGP key.</p>
|
||||
<p>If your device is lost or stolen the passphrase protects the contents of your mailbox.</p>
|
||||
<p>You cannot change your passphrase at a later time.</p>
|
||||
</div>
|
||||
</div><!--/.popover-->
|
@ -40,7 +40,7 @@
|
||||
<div class="arrow"></div>
|
||||
<div class="popover-title"><b>What is this?</b></div>
|
||||
<div class="popover-content">
|
||||
<p>The passphrase protects your encrypted mailbox.</p>
|
||||
<p>A passphrase is like a password that protects your PGP key.</p>
|
||||
<p>There is no way to access your messages without your passphrase.</p>
|
||||
<p>If you have forgotten your passphrase, please request an account reset by sending an email to <b>support@whiteout.io</b>. You will not be able to read previous messages after a reset.</p>
|
||||
</div>
|
||||
|
33
src/tpl/set-passphrase.html
Normal file
33
src/tpl/set-passphrase.html
Normal file
@ -0,0 +1,33 @@
|
||||
<div class="lightbox-body" ng-controller="SetPassphraseCtrl">
|
||||
<header>
|
||||
<h2>Set passphrase</h2>
|
||||
<button class="close" ng-click="state.setPassphrase.toggle(false)" data-action="lightbox-close"></button>
|
||||
</header>
|
||||
|
||||
<div class="content">
|
||||
<div class="dialog view-set-passphrase">
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><label>Current passphrase</label></td>
|
||||
<td><input class="input-text" type="password" ng-model="oldPassphrase" ng-change="checkPassphraseQuality()" tabindex="1" focus-me="true"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label>New passphrase</label></td>
|
||||
<td><input class="input-text" type="password" ng-model="newPassphrase" ng-change="checkPassphraseQuality()" tabindex="2"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label>Confirm passphrase</label></td>
|
||||
<td><input class="input-text" type="password" ng-model="confirmation" ng-class="{'input-text-error': (confirmation || newPassphrase) && confirmation !== newPassphrase}" tabindex="3"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="control">
|
||||
<button ng-click="setPassphrase()" class="btn" ng-disabled="(confirmation || newPassphrase) && confirmation !== newPassphrase" tabindex="4">Set passphrase</button>
|
||||
</div>
|
||||
|
||||
</div><!-- /.view-set-passphrase -->
|
||||
</div><!-- /.content -->
|
||||
</div><!-- /.lightbox-body -->
|
@ -5,23 +5,20 @@ define(function(require) {
|
||||
angular = require('angular'),
|
||||
mocks = require('angularMocks'),
|
||||
AccountCtrl = require('js/controller/account'),
|
||||
EmailDAO = require('js/dao/email-dao'),
|
||||
PGP = require('js/crypto/pgp'),
|
||||
dl = require('js/util/download'),
|
||||
appController = require('js/app-controller');
|
||||
appController = require('js/app-controller'),
|
||||
KeychainDAO = require('js/dao/keychain-dao');
|
||||
|
||||
describe('Account Controller unit test', function() {
|
||||
var scope, accountCtrl, origEmailDao, emailDaoMock,
|
||||
var scope, accountCtrl,
|
||||
dummyFingerprint, expectedFingerprint,
|
||||
dummyKeyId, expectedKeyId,
|
||||
emailAddress,
|
||||
keySize,
|
||||
cryptoMock;
|
||||
emailAddress, keySize, cryptoMock, keychainMock;
|
||||
|
||||
beforeEach(function() {
|
||||
origEmailDao = appController._emailDao;
|
||||
appController._emailDao = emailDaoMock = sinon.createStubInstance(EmailDAO);
|
||||
emailDaoMock._crypto = cryptoMock = sinon.createStubInstance(PGP);
|
||||
appController._crypto = cryptoMock = sinon.createStubInstance(PGP);
|
||||
appController._keychain = keychainMock = sinon.createStubInstance(KeychainDAO);
|
||||
|
||||
dummyFingerprint = '3A2D39B4E1404190B8B949DE7D7E99036E712926';
|
||||
expectedFingerprint = '3A2D 39B4 E140 4190 B8B9 49DE 7D7E 9903 6E71 2926';
|
||||
@ -31,10 +28,18 @@ define(function(require) {
|
||||
cryptoMock.getKeyId.returns(dummyKeyId);
|
||||
emailAddress = 'fred@foo.com';
|
||||
keySize = 1234;
|
||||
emailDaoMock._account = {
|
||||
appController._emailDao = {
|
||||
_account: {
|
||||
emailAddress: emailAddress,
|
||||
asymKeySize: keySize
|
||||
}
|
||||
};
|
||||
cryptoMock.getKeyParams.returns({
|
||||
_id: dummyKeyId,
|
||||
fingerprint: dummyFingerprint,
|
||||
userId: emailAddress,
|
||||
bitSize: keySize
|
||||
});
|
||||
|
||||
angular.module('accounttest', []);
|
||||
mocks.module('accounttest');
|
||||
@ -47,10 +52,7 @@ define(function(require) {
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
// restore the module
|
||||
appController._emailDao = origEmailDao;
|
||||
});
|
||||
afterEach(function() {});
|
||||
|
||||
describe('scope variables', function() {
|
||||
it('should be set correctly', function() {
|
||||
@ -63,16 +65,22 @@ define(function(require) {
|
||||
describe('export to key file', function() {
|
||||
it('should work', function(done) {
|
||||
var createDownloadMock = sinon.stub(dl, 'createDownload');
|
||||
cryptoMock.exportKeys.yields(null, {
|
||||
publicKeyArmored: 'a',
|
||||
privateKeyArmored: 'b',
|
||||
keyId: dummyKeyId
|
||||
keychainMock.getUserKeyPair.withArgs(emailAddress).yields(null, {
|
||||
publicKey: {
|
||||
_id: dummyKeyId,
|
||||
publicKey: 'a'
|
||||
},
|
||||
privateKey: {
|
||||
encryptedKey: 'b'
|
||||
}
|
||||
});
|
||||
createDownloadMock.withArgs(sinon.match(function(arg) {
|
||||
return arg.content === 'ab' && arg.filename === 'whiteout_mail_' + emailAddress + '_' + expectedKeyId + '.asc' && arg.contentType === 'text/plain';
|
||||
})).yields();
|
||||
scope.onError = function() {
|
||||
expect(cryptoMock.exportKeys.calledOnce).to.be.true;
|
||||
scope.onError = function(err) {
|
||||
expect(err.title).to.equal('Success');
|
||||
expect(scope.state.account.open).to.be.false;
|
||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||
expect(dl.createDownload.calledOnce).to.be.true;
|
||||
dl.createDownload.restore();
|
||||
done();
|
||||
@ -82,9 +90,10 @@ define(function(require) {
|
||||
});
|
||||
|
||||
it('should not work when key export failed', function(done) {
|
||||
cryptoMock.exportKeys.yields(new Error('asdasd'));
|
||||
scope.onError = function() {
|
||||
expect(cryptoMock.exportKeys.calledOnce).to.be.true;
|
||||
keychainMock.getUserKeyPair.yields(new Error('Boom!'));
|
||||
scope.onError = function(err) {
|
||||
expect(err.message).to.equal('Boom!');
|
||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||
done();
|
||||
};
|
||||
|
||||
@ -93,14 +102,19 @@ define(function(require) {
|
||||
|
||||
it('should not work when create download failed', function(done) {
|
||||
var createDownloadMock = sinon.stub(dl, 'createDownload');
|
||||
cryptoMock.exportKeys.yields(null, {
|
||||
publicKeyArmored: 'a',
|
||||
privateKeyArmored: 'b',
|
||||
keyId: dummyKeyId
|
||||
keychainMock.getUserKeyPair.withArgs(emailAddress).yields(null, {
|
||||
publicKey: {
|
||||
_id: dummyKeyId,
|
||||
publicKey: 'a'
|
||||
},
|
||||
privateKey: {
|
||||
encryptedKey: 'b'
|
||||
}
|
||||
});
|
||||
createDownloadMock.withArgs().yields(new Error('asdasd'));
|
||||
scope.onError = function() {
|
||||
expect(cryptoMock.exportKeys.calledOnce).to.be.true;
|
||||
scope.onError = function(err) {
|
||||
expect(err.message).to.equal('asdasd');
|
||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||
expect(dl.createDownload.calledOnce).to.be.true;
|
||||
dl.createDownload.restore();
|
||||
done();
|
||||
|
@ -42,6 +42,7 @@ function startTests() {
|
||||
'test/new-unit/dialog-ctrl-test',
|
||||
'test/new-unit/add-account-ctrl-test',
|
||||
'test/new-unit/account-ctrl-test',
|
||||
'test/new-unit/set-passphrase-ctrl-test',
|
||||
'test/new-unit/contacts-ctrl-test',
|
||||
'test/new-unit/login-existing-ctrl-test',
|
||||
'test/new-unit/login-initial-ctrl-test',
|
||||
|
@ -140,6 +140,30 @@ define(function(require) {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail when passphrases are equal', function(done) {
|
||||
pgp.changePassphrase({
|
||||
privateKeyArmored: privkey,
|
||||
oldPassphrase: passphrase,
|
||||
newPassphrase: passphrase
|
||||
}, function(err, reEncryptedKey) {
|
||||
expect(err).to.exist;
|
||||
expect(reEncryptedKey).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail when old passphrase is incorrect', function(done) {
|
||||
pgp.changePassphrase({
|
||||
privateKeyArmored: privkey,
|
||||
oldPassphrase: 'asd',
|
||||
newPassphrase: 'yxcv'
|
||||
}, function(err, reEncryptedKey) {
|
||||
expect(err).to.exist;
|
||||
expect(reEncryptedKey).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Encrypt/Sign/Decrypt/Verify', function() {
|
||||
@ -193,6 +217,15 @@ define(function(require) {
|
||||
expect(params.userId).to.equal("whiteout.test@t-online.de");
|
||||
expect(params.algorithm).to.equal("rsa_encrypt_sign");
|
||||
});
|
||||
|
||||
it('should work without param', function() {
|
||||
var params = pgp.getKeyParams();
|
||||
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() {
|
||||
|
81
test/new-unit/set-passphrase-ctrl-test.js
Normal file
81
test/new-unit/set-passphrase-ctrl-test.js
Normal file
@ -0,0 +1,81 @@
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var expect = chai.expect,
|
||||
angular = require('angular'),
|
||||
mocks = require('angularMocks'),
|
||||
SetPassphraseCtrl = require('js/controller/set-passphrase'),
|
||||
PGP = require('js/crypto/pgp'),
|
||||
appController = require('js/app-controller'),
|
||||
KeychainDAO = require('js/dao/keychain-dao');
|
||||
|
||||
describe('Set Passphrase Controller unit test', function() {
|
||||
var scope, setPassphraseCtrl,
|
||||
dummyFingerprint, expectedFingerprint,
|
||||
dummyKeyId, expectedKeyId,
|
||||
emailAddress, keySize, cryptoMock, keychainMock;
|
||||
|
||||
beforeEach(function() {
|
||||
appController._crypto = cryptoMock = sinon.createStubInstance(PGP);
|
||||
appController._keychain = keychainMock = sinon.createStubInstance(KeychainDAO);
|
||||
|
||||
dummyFingerprint = '3A2D39B4E1404190B8B949DE7D7E99036E712926';
|
||||
expectedFingerprint = '3A2D 39B4 E140 4190 B8B9 49DE 7D7E 9903 6E71 2926';
|
||||
dummyKeyId = '9FEB47936E712926';
|
||||
expectedKeyId = '6E712926';
|
||||
cryptoMock.getFingerprint.returns(dummyFingerprint);
|
||||
cryptoMock.getKeyId.returns(dummyKeyId);
|
||||
emailAddress = 'fred@foo.com';
|
||||
keySize = 1234;
|
||||
|
||||
cryptoMock.getKeyParams.returns({
|
||||
_id: dummyKeyId,
|
||||
fingerprint: dummyFingerprint,
|
||||
userId: emailAddress,
|
||||
bitSize: keySize
|
||||
});
|
||||
|
||||
angular.module('setpassphrasetest', []);
|
||||
mocks.module('setpassphrasetest');
|
||||
mocks.inject(function($rootScope, $controller) {
|
||||
scope = $rootScope.$new();
|
||||
scope.state = {};
|
||||
setPassphraseCtrl = $controller(SetPassphraseCtrl, {
|
||||
$scope: scope
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {});
|
||||
|
||||
describe('setPassphrase', function() {
|
||||
it('should work', function(done) {
|
||||
scope.oldPassphrase = 'old';
|
||||
scope.newPassphrase = 'new';
|
||||
|
||||
keychainMock.lookupPrivateKey.withArgs(dummyKeyId).yields(null, {
|
||||
encryptedKey: 'encrypted'
|
||||
});
|
||||
|
||||
cryptoMock.changePassphrase.withArgs({
|
||||
privateKeyArmored: 'encrypted',
|
||||
oldPassphrase: 'old',
|
||||
newPassphrase: 'new'
|
||||
}).yields(null, 'newArmoredKey');
|
||||
|
||||
keychainMock.saveLocalPrivateKey.withArgs({
|
||||
_id: dummyKeyId,
|
||||
userId: emailAddress,
|
||||
encryptedKey: 'newArmoredKey'
|
||||
}).yields();
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err.title).to.equal('Success');
|
||||
done();
|
||||
};
|
||||
|
||||
scope.setPassphrase();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user