From 1c877634d1496e9740195d277e370e9a8c23a64e Mon Sep 17 00:00:00 2001 From: Tankred Hase Date: Fri, 28 Mar 2014 17:40:02 +0100 Subject: [PATCH 1/6] [WO-297] setting a passphrase is optional --- src/js/controller/login-existing.js | 23 ++++++++++++++++++++--- src/js/controller/login-initial.js | 11 ++++++----- src/js/controller/login-new-device.js | 5 ----- src/js/crypto/pgp.js | 2 +- src/tpl/login-initial.html | 13 ++++++------- 5 files changed, 33 insertions(+), 21 deletions(-) diff --git a/src/js/controller/login-existing.js b/src/js/controller/login-existing.js index f5cdbf2..0a3b9b7 100644 --- a/src/js/controller/login-existing.js +++ b/src/js/controller/login-existing.js @@ -15,6 +15,23 @@ define(function(require) { $scope.buttonEnabled = true; $scope.incorrect = false; + // + // Try unlocking without passphrase + // + + unlockCrypto(function(err) { + if (err) { + return; + } + + $location.path('/desktop'); + $scope.$apply(); + }); + + // + // Unlock using passphrase + // + $scope.change = function() { $scope.incorrect = false; }; @@ -27,10 +44,10 @@ define(function(require) { // disable button once loggin has started $scope.buttonEnabled = false; $scope.incorrect = false; - unlockCrypto(); + unlockCrypto(onUnlock); }; - function unlockCrypto() { + function unlockCrypto(callback) { var userId = emailDao._account.emailAddress; appController._emailDao._keychain.getUserKeyPair(userId, function(err, keypair) { if (err) { @@ -41,7 +58,7 @@ define(function(require) { emailDao.unlock({ keypair: keypair, passphrase: $scope.passphrase - }, onUnlock); + }, callback); }); } diff --git a/src/js/controller/login-initial.js b/src/js/controller/login-initial.js index 3c9c25c..ae720d1 100644 --- a/src/js/controller/login-initial.js +++ b/src/js/controller/login-initial.js @@ -53,12 +53,13 @@ define(function(require) { return str.substring(0, 1).toLowerCase() + str.substring(1); } - if (!passphrase || passphrase.length < 10) { - $scope.passphraseMsg = 'Too short'; + if (!passphrase) { + // no rating for empty passphrase + $scope.passphraseMsg = ''; return; } - if (SAME.test(passphrase)) { + if (passphrase.length < 8 || SAME.test(passphrase)) { $scope.passphraseMsg = 'Very weak'; return; } @@ -85,14 +86,14 @@ define(function(require) { var passphrase = $scope.state.passphrase, confirmation = $scope.state.confirmation; - if (!passphrase || passphrase !== confirmation) { + if (passphrase !== confirmation) { return; } $scope.setState(states.PROCESSING); setTimeout(function() { emailDao.unlock({ - passphrase: passphrase + passphrase: (passphrase) ? passphrase : undefined }, function(err) { if (err) { $scope.setState(states.IDLE); diff --git a/src/js/controller/login-new-device.js b/src/js/controller/login-new-device.js index b1b59cc..9125dc5 100644 --- a/src/js/controller/login-new-device.js +++ b/src/js/controller/login-new-device.js @@ -17,11 +17,6 @@ define(function(require) { $scope.incorrect = false; $scope.confirmPassphrase = function() { - if (!$scope.passphrase) { - $scope.incorrect = true; - return; - } - $scope.incorrect = false; unlockCrypto(); }; diff --git a/src/js/crypto/pgp.js b/src/js/crypto/pgp.js index 5fea558..e7af821 100644 --- a/src/js/crypto/pgp.js +++ b/src/js/crypto/pgp.js @@ -119,7 +119,7 @@ define(function(require) { var pubKeyId, privKeyId, self = this; // check options - if (typeof options.passphrase !== 'string' || !options.privateKeyArmored || !options.publicKeyArmored) { + if (!options.privateKeyArmored || !options.publicKeyArmored) { callback({ errMsg: 'Importing keys failed. Not all options set!' }); diff --git a/src/tpl/login-initial.html b/src/tpl/login-initial.html index bf60e8b..18d9e6a 100644 --- a/src/tpl/login-initial.html +++ b/src/tpl/login-initial.html @@ -6,22 +6,22 @@
-

Generate PGP key. Choose a passphrase to protect your new key. If you forget it at a later time you will not be able to read past messages.

Alternatively you can also import an existing PGP key.

+

Generate PGP key. You can set a passphrase to protect your key on disk. But you will have to enter it everytime you open the app. If not just press continue.

Alternatively you can also import an existing PGP key.


- - + +
- +
-

Generating keypair. Please stand by. This can take a while...

+

Generating key. Please stand by. This can take a while...

@@ -43,9 +43,8 @@
What is this?
-

A passphrase is like a password but longer.

+

A passphrase is like a password that protects your PGP key.

If your device is lost or stolen the passphrase protects the contents of your mailbox.

-

It must be at least 10 characters long and contain one special character or digit.

You cannot change your passphrase at a later time.

\ No newline at end of file From 4b5d367fdb780598ec2c2742e01ef102d092bab1 Mon Sep 17 00:00:00 2001 From: Tankred Hase Date: Fri, 28 Mar 2014 18:08:04 +0100 Subject: [PATCH 2/6] Login to IMAP (appcontroller.onConnect) after show mail-list --- src/js/app-controller.js | 15 +-------------- src/js/controller/login-existing.js | 23 +++-------------------- src/js/controller/login.js | 29 +++++++++++++++++++---------- src/js/controller/navigation.js | 5 ++--- src/js/crypto/pgp.js | 2 +- src/js/dao/email-dao.js | 26 ++++++++++++++++++-------- 6 files changed, 44 insertions(+), 56 deletions(-) diff --git a/src/js/app-controller.js b/src/js/app-controller.js index 27cc70c..341888f 100644 --- a/src/js/app-controller.js +++ b/src/js/app-controller.js @@ -127,11 +127,6 @@ define(function(require) { console.log('IMAP reconnecting...'); // re-init client modules on error self.onConnect(function(err) { - if (!self._initialized) { - callback(err); - return; - } - if (err) { console.error('IMAP reconnect failed!', err); return; @@ -398,15 +393,7 @@ define(function(require) { return; } - // connect tcp clients on first startup - self.onConnect(function(err) { - if (err) { - callback(err); - return; - } - - callback(null, keypair); - }); + callback(null, keypair); }); } }; diff --git a/src/js/controller/login-existing.js b/src/js/controller/login-existing.js index 0a3b9b7..f5cdbf2 100644 --- a/src/js/controller/login-existing.js +++ b/src/js/controller/login-existing.js @@ -15,23 +15,6 @@ define(function(require) { $scope.buttonEnabled = true; $scope.incorrect = false; - // - // Try unlocking without passphrase - // - - unlockCrypto(function(err) { - if (err) { - return; - } - - $location.path('/desktop'); - $scope.$apply(); - }); - - // - // Unlock using passphrase - // - $scope.change = function() { $scope.incorrect = false; }; @@ -44,10 +27,10 @@ define(function(require) { // disable button once loggin has started $scope.buttonEnabled = false; $scope.incorrect = false; - unlockCrypto(onUnlock); + unlockCrypto(); }; - function unlockCrypto(callback) { + function unlockCrypto() { var userId = emailDao._account.emailAddress; appController._emailDao._keychain.getUserKeyPair(userId, function(err, keypair) { if (err) { @@ -58,7 +41,7 @@ define(function(require) { emailDao.unlock({ keypair: keypair, passphrase: $scope.passphrase - }, callback); + }, onUnlock); }); } diff --git a/src/js/controller/login.js b/src/js/controller/login.js index 8baeb04..ba22ba1 100644 --- a/src/js/controller/login.js +++ b/src/js/controller/login.js @@ -35,7 +35,7 @@ define(function(require) { // check if account needs to be selected if (!emailAddress) { - firstLogin(); + goTo('/add-account'); return; } @@ -53,23 +53,32 @@ define(function(require) { }); } - function firstLogin() { - $location.path('/add-account'); - $scope.$apply(); - } - function redirect(availableKeys) { // redirect if needed if (typeof availableKeys === 'undefined') { // no public key available, start onboarding process - $location.path('/login-initial'); + goTo('/login-initial'); } else if (!availableKeys.privateKey) { // no private key, import key - $location.path('/login-new-device'); + goTo('/login-new-device'); } else { - // public and private key available, just login - $location.path('/login-existing'); + // public and private key available, try empty passphrase + appController._emailDao.unlock({ + keypair: availableKeys, + passphrase: undefined + }, function(err) { + if (err) { + goTo('/login-existing'); + return; + } + + goTo('/desktop'); + }); } + } + + function goTo(location) { + $location.path(location); $scope.$apply(); } }; diff --git a/src/js/controller/navigation.js b/src/js/controller/navigation.js index 73f3c5e..9e33930 100644 --- a/src/js/controller/navigation.js +++ b/src/js/controller/navigation.js @@ -19,9 +19,6 @@ define(function(require) { // attach global error handler errorUtil.attachHandler($scope); - // app controller is initialized - appController._initialized = true; - emailDao = appController._emailDao; outboxBo = appController._outboxBo; @@ -70,6 +67,8 @@ define(function(require) { initFolders(); // select inbox as the current folder on init $scope.openFolder($scope.account.folders[0]); + // connect imap/smtp clients on first startup + appController.onConnect($scope.onError); // // helper functions diff --git a/src/js/crypto/pgp.js b/src/js/crypto/pgp.js index e7af821..3ef6abb 100644 --- a/src/js/crypto/pgp.js +++ b/src/js/crypto/pgp.js @@ -19,7 +19,7 @@ define(function(require) { PGP.prototype.generateKeys = function(options, callback) { var userId; - if (!util.emailRegEx.test(options.emailAddress) || !options.keySize || typeof options.passphrase !== 'string') { + if (!util.emailRegEx.test(options.emailAddress) || !options.keySize) { callback({ errMsg: 'Crypto init failed. Not all options set!' }); diff --git a/src/js/dao/email-dao.js b/src/js/dao/email-dao.js index ce706fa..78d1fc6 100644 --- a/src/js/dao/email-dao.js +++ b/src/js/dao/email-dao.js @@ -73,7 +73,24 @@ define(function(require) { return; } - self._account.folders = folders; + // if empty (first time login) use dummy folders ... overwritten in onConnect + self._account.folders = (folders) ? folders : [{ + type: 'Inbox', + path: 'INBOX' + }, { + type: 'Sent', + path: 'SENT' + }, { + type: 'Outbox', + path: 'OUTBOX' + }, { + type: 'Drafts', + path: 'DRAFTS' + }, { + type: 'Trash', + path: 'TRASH' + }]; + callback(null, keypair); }); } @@ -114,13 +131,6 @@ define(function(require) { // set status to online self._account.online = true; - // check memory - if (self._account.folders) { - // no need to init folder again on connect... already in memory - callback(); - return; - } - // init folders self._imapListFolders(function(err, folders) { if (err) { From 6ca1ae65076af5d1ceababd32c9689d0329222dd Mon Sep 17 00:00:00 2001 From: Tankred Hase Date: Sat, 29 Mar 2014 01:13:32 +0100 Subject: [PATCH 3/6] remove key export dialog after keygen --- src/js/controller/login-initial.js | 32 +----------------------------- src/tpl/login-initial.html | 8 +------- 2 files changed, 2 insertions(+), 38 deletions(-) diff --git a/src/js/controller/login-initial.js b/src/js/controller/login-initial.js index ae720d1..12245f0 100644 --- a/src/js/controller/login-initial.js +++ b/src/js/controller/login-initial.js @@ -101,42 +101,12 @@ define(function(require) { return; } - $scope.setState(states.DONE); + $location.path('/desktop'); $scope.$apply(); }); }, 500); }; - $scope.exportKeypair = function() { - // export keys from keychain - emailDao._crypto.exportKeys(function(err, keys) { - if (err) { - $scope.onError(err); - return; - } - - var id = keys.keyId.substring(8, keys.keyId.length); - dl.createDownload({ - content: keys.publicKeyArmored + keys.privateKeyArmored, - filename: 'whiteout_mail_' + emailDao._account.emailAddress + '_' + id + '.asc', - contentType: 'text/plain' - }, onSave); - }); - - function onSave(err) { - if (err) { - $scope.onError(err); - return; - } - $scope.proceed(); - $scope.$apply(); - } - }; - - $scope.proceed = function() { - $location.path('/desktop'); - }; - $scope.setState = function(state) { $scope.state.ui = state; }; diff --git a/src/tpl/login-initial.html b/src/tpl/login-initial.html index 18d9e6a..2efa93a 100644 --- a/src/tpl/login-initial.html +++ b/src/tpl/login-initial.html @@ -6,7 +6,7 @@
-

Generate PGP key. You can set a passphrase to protect your key on disk. But you will have to enter it everytime you open the app. If not just press continue.

Alternatively you can also import an existing PGP key.

+

Generate PGP key. You can set a passphrase to protect your key on disk. This must be entered everytime you start the app. For no passphrase just press continue.

Alternatively you can also import an existing PGP key.


@@ -24,12 +24,6 @@

Generating key. Please stand by. This can take a while...

-
-

Keypair generated. Your personal keypair has been generated. You can export it (e.g. to a USB flash drive) to setup Whiteout Mail on another computer or as a backup.

- - -
-
From f0f0aa3d6349db17abe81bbc03e918e57260dec3 Mon Sep 17 00:00:00 2001 From: Tankred Hase Date: Sat, 29 Mar 2014 01:14:52 +0100 Subject: [PATCH 4/6] Fix show messages on initial login/folder-sync --- src/js/controller/navigation.js | 16 ++++++++++++++-- src/js/dao/email-dao.js | 18 +----------------- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/js/controller/navigation.js b/src/js/controller/navigation.js index 9e33930..1deb6fb 100644 --- a/src/js/controller/navigation.js +++ b/src/js/controller/navigation.js @@ -66,9 +66,21 @@ define(function(require) { // init folders initFolders(); // select inbox as the current folder on init - $scope.openFolder($scope.account.folders[0]); + if ($scope.account.folders && $scope.account.folders.length > 0) { + $scope.openFolder($scope.account.folders[0]); + } // connect imap/smtp clients on first startup - appController.onConnect($scope.onError); + appController.onConnect(function(err) { + if (err) { + $scope.onError(err); + return; + } + + // select inbox if not yet selected + if (!$scope.state.nav.currentFolder) { + $scope.openFolder($scope.account.folders[0]); + } + }); // // helper functions diff --git a/src/js/dao/email-dao.js b/src/js/dao/email-dao.js index 78d1fc6..6704f0b 100644 --- a/src/js/dao/email-dao.js +++ b/src/js/dao/email-dao.js @@ -73,23 +73,7 @@ define(function(require) { return; } - // if empty (first time login) use dummy folders ... overwritten in onConnect - self._account.folders = (folders) ? folders : [{ - type: 'Inbox', - path: 'INBOX' - }, { - type: 'Sent', - path: 'SENT' - }, { - type: 'Outbox', - path: 'OUTBOX' - }, { - type: 'Drafts', - path: 'DRAFTS' - }, { - type: 'Trash', - path: 'TRASH' - }]; + self._account.folders = folders; callback(null, keypair); }); From f3b3a4b4960c3a5845b90ea09db3022917df2c7f Mon Sep 17 00:00:00 2001 From: Tankred Hase Date: Mon, 31 Mar 2014 20:15:09 +0200 Subject: [PATCH 5/6] Add PGP api for changing a user's passphrase --- src/js/crypto/pgp.js | 49 +++++++++++++++++++++++++++++++++++++++ test/new-unit/pgp-test.js | 22 ++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/src/js/crypto/pgp.js b/src/js/crypto/pgp.js index 3ef6abb..ab53d6f 100644 --- a/src/js/crypto/pgp.js +++ b/src/js/crypto/pgp.js @@ -184,6 +184,55 @@ define(function(require) { }); }; + /** + * Change the passphrase of an ascii armored private key. + */ + PGP.prototype.changePassphrase = function(options, callback) { + var privKey, packets; + + if (!options.privateKeyArmored || + typeof options.oldPassphrase !== 'string' || + typeof options.newPassphrase !== 'string') { + callback({ + errMsg: 'Could not export keys!' + }); + return; + } + + // read armored key + try { + privKey = openpgp.key.readArmored(options.privateKeyArmored).keys[0]; + } catch (e) { + callback({ + errMsg: 'Importing key failed. Parsing error!' + }); + return; + } + + // decrypt private key with passphrase + if (!privKey.decrypt(options.oldPassphrase)) { + callback({ + errMsg: 'Old passphrase incorrect!' + }); + return; + } + + // encrypt key with new passphrase + try { + packets = privKey.getAllKeyPackets(); + for (var i = 0; i < packets.length; i++) { + packets[i].encrypt(options.newPassphrase); + } + } catch (e) { + callback({ + errMsg: 'Setting new passphrase failed!' + }); + return; + } + + callback(null, privKey.armor()); + }; + /** * Encrypt and sign a pgp message for a list of receivers */ diff --git a/test/new-unit/pgp-test.js b/test/new-unit/pgp-test.js index bcfecd8..ecb95e9 100644 --- a/test/new-unit/pgp-test.js +++ b/test/new-unit/pgp-test.js @@ -120,6 +120,28 @@ define(function(require) { }); }); + describe('Change passphrase of private key', function() { + it('should work', function(done) { + pgp.changePassphrase({ + privateKeyArmored: privkey, + oldPassphrase: passphrase, + newPassphrase: 'yxcv' + }, function(err, reEncryptedKey) { + expect(err).to.not.exist; + expect(reEncryptedKey).to.exist; + + pgp.importKeys({ + passphrase: 'yxcv', + privateKeyArmored: reEncryptedKey, + publicKeyArmored: pubkey + }, function(err) { + expect(err).to.not.exist; + done(); + }); + }); + }); + }); + describe('Encrypt/Sign/Decrypt/Verify', function() { var message = 'asdfs\n\nThursday, Nov 21, 2013 7:38 PM asdf@example.com wrote:\n' + '> asdf\n' + From f8e0c90b5bd0a030eead3041f7fe8308ca2ddd6b Mon Sep 17 00:00:00 2001 From: Tankred Hase Date: Mon, 31 Mar 2014 21:35:40 +0200 Subject: [PATCH 6/6] fix tests --- src/js/controller/login-initial.js | 3 +- test/new-unit/app-controller-test.js | 22 ------ test/new-unit/email-dao-test.js | 22 ++---- test/new-unit/login-ctrl-test.js | 50 ++++++++++++- test/new-unit/login-initial-ctrl-test.js | 83 +++------------------ test/new-unit/login-new-device-ctrl-test.js | 8 -- 6 files changed, 61 insertions(+), 127 deletions(-) diff --git a/src/js/controller/login-initial.js b/src/js/controller/login-initial.js index 12245f0..72564dd 100644 --- a/src/js/controller/login-initial.js +++ b/src/js/controller/login-initial.js @@ -2,8 +2,7 @@ define(function(require) { 'use strict'; var appController = require('js/app-controller'), - errorUtil = require('js/util/error'), - dl = require('js/util/download'); + errorUtil = require('js/util/error'); var LoginInitialCtrl = function($scope, $location) { var emailDao = appController._emailDao, diff --git a/test/new-unit/app-controller-test.js b/test/new-unit/app-controller-test.js index 5a4f05f..b3944cb 100644 --- a/test/new-unit/app-controller-test.js +++ b/test/new-unit/app-controller-test.js @@ -291,32 +291,11 @@ define(function(require) { }); }); - it('should fail due to error in onConnect', function(done) { - devicestorageStub.init.yields(); - updateHandlerStub.update.yields(); - emailDaoStub.init.yields(); - - onConnectStub.yields({}); - - controller.init({ - emailAddress: emailAddress - }, function(err) { - expect(err).to.exist; - expect(updateHandlerStub.update.calledOnce).to.be.true; - expect(emailDaoStub.init.calledOnce).to.be.true; - expect(devicestorageStub.init.calledOnce).to.be.true; - expect(onConnectStub.calledOnce).to.be.true; - done(); - }); - }); - it('should work and return a keypair', function(done) { devicestorageStub.init.withArgs(emailAddress).yields(); emailDaoStub.init.yields(null, {}); updateHandlerStub.update.yields(); - onConnectStub.yields(); - controller.init({ emailAddress: emailAddress }, function(err, keypair) { @@ -325,7 +304,6 @@ define(function(require) { expect(updateHandlerStub.update.calledOnce).to.be.true; expect(emailDaoStub.init.calledOnce).to.be.true; expect(devicestorageStub.init.calledOnce).to.be.true; - expect(onConnectStub.calledOnce).to.be.true; done(); }); }); diff --git a/test/new-unit/email-dao-test.js b/test/new-unit/email-dao-test.js index a426ba4..56eeca8 100644 --- a/test/new-unit/email-dao-test.js +++ b/test/new-unit/email-dao-test.js @@ -133,14 +133,18 @@ define(function(require) { dao._account.folders = []; imapClientStub.login.yields(); + var listFolderStub = sinon.stub(dao, '_imapListFolders').yields(null, []); + dao.onConnect({ imapClient: imapClientStub, pgpMailer: pgpMailerStub }, function(err) { expect(err).to.not.exist; expect(dao._account.online).to.be.true; + expect(dao._account.folders).to.deep.equal([]); expect(dao._imapClient).to.equal(dao._imapClient); expect(dao._smtpClient).to.equal(dao._smtpClient); + listFolderStub.restore(); done(); }); }); @@ -306,23 +310,7 @@ define(function(require) { }); }); - it('should work when folder already initiated', function(done) { - dao._account.folders = []; - imapLoginStub.yields(); - - dao.onConnect({ - imapClient: imapClientStub, - pgpMailer: pgpMailerStub - }, function(err) { - expect(err).to.not.exist; - expect(dao._account.online).to.be.true; - expect(dao._imapClient).to.equal(dao._imapClient); - expect(dao._smtpClient).to.equal(dao._smtpClient); - done(); - }); - }); - - it('should work when folder not yet initiated', function(done) { + it('should work', function(done) { var folders = []; imapLoginStub.yields(); imapListFoldersStub.yields(null, folders); diff --git a/test/new-unit/login-ctrl-test.js b/test/new-unit/login-ctrl-test.js index 357993e..7c858f7 100644 --- a/test/new-unit/login-ctrl-test.js +++ b/test/new-unit/login-ctrl-test.js @@ -60,13 +60,55 @@ define(function(require) { initStub.restore(); }); - it('should forward to existing user login', function(done) { - startAppStub.yields(); - getEmailAddressStub.yields(null, emailAddress); - initStub.yields(null, { + it('should forward directly to desktop for empty passphrase', function(done) { + var testKeys = { privateKey: 'a', publicKey: 'b' + }; + + startAppStub.yields(); + getEmailAddressStub.yields(null, emailAddress); + initStub.yields(null, testKeys); + + emailDaoMock.unlock.withArgs({ + keypair: testKeys, + passphrase: undefined + }).yields(); + + angular.module('logintest', []); + mocks.module('logintest'); + mocks.inject(function($controller, $rootScope, $location) { + location = $location; + sinon.stub(location, 'path', function(path) { + expect(path).to.equal('/desktop'); + expect(startAppStub.calledOnce).to.be.true; + expect(checkForUpdateStub.calledOnce).to.be.true; + expect(getEmailAddressStub.calledOnce).to.be.true; + done(); + }); + scope = $rootScope.$new(); + scope.state = {}; + ctrl = $controller(LoginCtrl, { + $location: location, + $scope: scope + }); }); + }); + + it('should forward to existing user login', function(done) { + var testKeys = { + privateKey: 'a', + publicKey: 'b' + }; + + startAppStub.yields(); + getEmailAddressStub.yields(null, emailAddress); + initStub.yields(null, testKeys); + + emailDaoMock.unlock.withArgs({ + keypair: testKeys, + passphrase: undefined + }).yields({}); angular.module('logintest', []); mocks.module('logintest'); diff --git a/test/new-unit/login-initial-ctrl-test.js b/test/new-unit/login-initial-ctrl-test.js index a424376..47b807b 100644 --- a/test/new-unit/login-initial-ctrl-test.js +++ b/test/new-unit/login-initial-ctrl-test.js @@ -5,7 +5,6 @@ define(function(require) { angular = require('angular'), mocks = require('angularMocks'), LoginInitialCtrl = require('js/controller/login-initial'), - dl = require('js/util/download'), PGP = require('js/crypto/pgp'), EmailDAO = require('js/dao/email-dao'), appController = require('js/app-controller'); @@ -54,8 +53,6 @@ define(function(require) { describe('initial state', function() { it('should be well defined', function() { - expect(scope.proceed).to.exist; - expect(scope.exportKeypair).to.exist; expect(scope.confirmPassphrase).to.exist; expect(scope.state.ui).to.equal(1); }); @@ -63,10 +60,10 @@ define(function(require) { describe('check passphrase quality', function() { it('should be too short', function() { - scope.state.passphrase = '&§DG36abc'; + scope.state.passphrase = '&§DG36'; scope.checkPassphraseQuality(); - expect(scope.passphraseMsg).to.equal('Too short'); + expect(scope.passphraseMsg).to.equal('Very weak'); expect(scope.passphraseRating).to.equal(0); }); @@ -112,16 +109,12 @@ define(function(require) { emailDaoMock.unlock.withArgs({ passphrase: passphrase }).yields(); - setStateStub = sinon.stub(scope, 'setState', function(state) { - if (setStateStub.calledOnce) { - expect(state).to.equal(2); - } else if (setStateStub.calledTwice) { - expect(state).to.equal(4); - expect(emailDaoMock.unlock.calledOnce).to.be.true; - scope.setState.restore(); - done(); - } - }); + + scope.$apply = function() { + expect(location.$$path).to.equal('/desktop'); + expect(emailDaoMock.unlock.calledOnce).to.be.true; + done(); + }; scope.confirmPassphrase(); }); @@ -139,6 +132,7 @@ define(function(require) { emailDaoMock.unlock.withArgs({ passphrase: passphrase }).yields(new Error('asd')); + setStateStub = sinon.stub(scope, 'setState', function(state) { if (setStateStub.calledOnce) { expect(state).to.equal(2); @@ -154,64 +148,5 @@ define(function(require) { }); }); - describe('proceed', function() { - it('should forward', function() { - var locationSpy = sinon.spy(location, 'path'); - - scope.proceed(); - - expect(locationSpy.calledWith('/desktop')).to.be.true; - }); - }); - - describe('export keypair', function() { - it('should work', function() { - var locationSpy, createDownloadMock; - - createDownloadMock = sinon.stub(dl, 'createDownload'); - cryptoMock.exportKeys.yields(null, { - publicKeyArmored: 'a', - privateKeyArmored: 'b', - keyId: keyId - }); - createDownloadMock.withArgs(sinon.match(function(arg) { - return arg.content === 'ab' && arg.filename === 'whiteout_mail_' + emailAddress + '_' + expectedKeyId + '.asc' && arg.contentType === 'text/plain'; - })).yields(); - - locationSpy = sinon.spy(location, 'path'); - - scope.exportKeypair(); - - expect(cryptoMock.exportKeys.calledOnce).to.be.true; - expect(createDownloadMock.calledOnce).to.be.true; - expect(locationSpy.calledWith('/desktop')).to.be.true; - dl.createDownload.restore(); - }); - - it('should not work when download fails', function() { - var createDownloadMock = sinon.stub(dl, 'createDownload'); - cryptoMock.exportKeys.yields(null, { - publicKeyArmored: 'a', - privateKeyArmored: 'b', - keyId: keyId - }); - createDownloadMock.yields({ - errMsg: 'snafu.' - }); - scope.exportKeypair(); - - expect(cryptoMock.exportKeys.calledOnce).to.be.true; - expect(createDownloadMock.calledOnce).to.be.true; - dl.createDownload.restore(); - }); - - it('should not work when export fails', function() { - cryptoMock.exportKeys.yields(new Error('snafu.')); - - scope.exportKeypair(); - - expect(cryptoMock.exportKeys.calledOnce).to.be.true; - }); - }); }); }); \ No newline at end of file diff --git a/test/new-unit/login-new-device-ctrl-test.js b/test/new-unit/login-new-device-ctrl-test.js index 52f2672..81985dd 100644 --- a/test/new-unit/login-new-device-ctrl-test.js +++ b/test/new-unit/login-new-device-ctrl-test.js @@ -98,14 +98,6 @@ define(function(require) { expect(keychainMock.getUserKeyPair.calledOnce).to.be.true; }); - it('should not do anything without passphrase', function() { - scope.state.passphrase = ''; - - scope.confirmPassphrase(); - - expect(scope.incorrect).to.be.true; - }); - it('should not work when keypair upload fails', function() { scope.passphrase = passphrase; scope.key = {