invite user when clicking on red address label

This commit is contained in:
Tankred Hase 2014-02-27 18:14:38 +01:00
parent badea7ab8a
commit 3d6a4698ce
6 changed files with 104 additions and 50 deletions

View File

@ -53,15 +53,15 @@ define(function(require) {
subjectPrefix: '[whiteout] ',
fallbackSubject: '(no subject)',
invitationSubject: 'Invitation to a private conversation',
invitationMessage: 'Hi,\n\nI would like to invite you to a private conversation!\n\nPlease install the Whiteout Mail application. This application is used to read and write messages securely with strong encryption applied.\n\nGo to the Whiteout Networks homepage to learn more and to download the application: https://whiteout.io\n\n',
message: 'Hi,\n\nthis is a private conversation. To read my encrypted message below, simply open it in Whiteout Mail.\n\nOpen Whiteout Mail: https://chrome.google.com/webstore/detail/jjgghafhamholjigjoghcfcekhkonijg\n\n',
invitationMessage: 'Hi,\n\nI would like to invite you to a private conversation!\n\nPlease install the Whiteout Mail application. This application is used to read and write messages securely with strong encryption applied.\n\nGo to the Whiteout Networks homepage to learn more and to download the application: https://whiteout.io',
message: 'Hi,\n\nthis is a private conversation. To read my encrypted message below, simply open it in Whiteout Mail.\nOpen Whiteout Mail: https://chrome.google.com/webstore/detail/jjgghafhamholjigjoghcfcekhkonijg',
cryptPrefix: '-----BEGIN PGP MESSAGE-----',
cryptSuffix: '-----END PGP MESSAGE-----',
signature: '\n\n\n--\nSent from Whiteout Mail - get the app for easy end-to-end encryption\nhttps://whiteout.io\n\n',
signature: '\n\n\n--\nSent from Whiteout Mail - PGP encryption for the rest of us.\nhttps://whiteout.io\n\n',
webSite: 'http://whiteout.io',
verificationSubject: 'New public key uploaded',
sendBtnClear: 'Send',
sendBtnSecure: 'Secure send'
sendBtnSecure: 'Send securely'
};
return app;

View File

@ -32,8 +32,10 @@ define(function(require) {
* @param {Function} callback(error, pendingMailsCount) Callback that informs you about the count of pending mails.
*/
OutboxBO.prototype.startChecking = function(callback) {
// remember global callback
this._onError = callback;
// start periodic checking of outbox
this._intervalId = setInterval(this._processOutbox.bind(this, callback), config.checkOutboxInterval);
this._intervalId = setInterval(this._processOutbox.bind(this, this._onError), config.checkOutboxInterval);
};
/**
@ -65,7 +67,7 @@ define(function(require) {
// check if there are unregistered recipients
function checkRecipients(recipients) {
var after = _.after(recipients.length, function() {
encryptAndPersist();
checkEncrypt();
});
// find out if there are unregistered users
@ -87,11 +89,10 @@ define(function(require) {
});
}
function encryptAndPersist() {
function checkEncrypt() {
// only encrypt if all recipients have public keys
if (mail.publicKeysArmored.length < allReaders.length) {
self._devicestorage.storeList([mail], outboxDb, callback);
storeAndForward(mail);
return;
}
@ -105,7 +106,21 @@ define(function(require) {
return;
}
self._devicestorage.storeList([mail], outboxDb, callback);
storeAndForward(mail);
});
}
function storeAndForward(mail) {
// store in outbox
self._devicestorage.storeList([mail], outboxDb, function(err) {
if (err) {
callback(err);
return;
}
callback();
// don't wait for next round
self._processOutbox(self._onError);
});
}
};

View File

@ -20,7 +20,7 @@ define(function(require) {
keychain = appController._keychain;
// set default value so that the popover height is correct on init
$scope.keyId = 'XXXXXXXX';
$scope.keyId = 'No key found.';
$scope.state.read = {
open: false,
@ -34,7 +34,7 @@ define(function(require) {
};
$scope.getKeyId = function(address) {
$scope.keyId = 'unknown user';
$scope.keyId = 'Searching...';
keychain.getReceiverPublicKey(address, function(err, pubkey) {
if (err) {
$scope.onError(err);
@ -42,13 +42,15 @@ define(function(require) {
}
if (!pubkey) {
$scope.keyId = 'User has no key. Click to invite.';
$scope.$apply();
return;
}
var fpr = crypto.getFingerprint(pubkey.publicKey);
var formatted = fpr.slice(32);
$scope.keyId = formatted;
$scope.keyId = 'PGP key: ' + formatted;
$scope.$apply();
});
};
@ -116,10 +118,20 @@ define(function(require) {
}
};
$scope.inviteUser = function(address) {
$scope.invite = function(user) {
// only invite non-pgp users
if (user.secure) {
return;
}
$scope.keyId = 'Sending invitation...';
var sender = emailDao._account.emailAddress,
recipient = user.address;
invitationDao.invite({
recipient: address,
sender: emailDao._account.emailAddress
recipient: recipient,
sender: sender
}, function(err) {
if (err) {
$scope.onError(err);
@ -127,8 +139,14 @@ define(function(require) {
}
var invitationMail = {
from: [emailDao._account.emailAddress],
to: [address],
from: [{
address: sender
}],
to: [{
address: recipient
}],
cc: [],
bcc: [],
subject: str.invitationSubject,
body: str.invitationMessage
};

View File

@ -1037,7 +1037,7 @@ define(function(require) {
// mime encode, sign, encrypt and send email via smtp
self._pgpMailer.send({
encrypt: true,
cleartextMessage: str.message,
cleartextMessage: str.message + str.signature,
mail: options.email,
publicKeysArmored: options.email.publicKeysArmored
}, callback);

View File

@ -9,14 +9,14 @@
<p class="subject" ng-click="state.read.toggle(false)">{{(state.mailList.selected.subject) ? state.mailList.selected.subject : 'No subject'}}</p>
<p class="date">{{state.mailList.selected.sentDate | date:'EEEE, MMM d, yyyy h:mm a'}}</p>
<p class="address">
From: <span ng-repeat="u in state.mailList.selected.from" class="label" ng-class="{'label-primary': u.secure === false}" data-icon-append="{{(u.secure === false) ? '&#xe001;' : ''}}" ng-mouseover="getKeyId(u.address)" popover="#fingerprint-info">{{u.name || u.address}}</span>
From: <span ng-repeat="u in state.mailList.selected.from" class="label" ng-class="{'label-primary': u.secure === false}" data-icon-append="{{(u.secure === false) ? '&#xe001;' : ''}}" ng-mouseover="getKeyId(u.address)" ng-click="invite(u)" popover="#fingerprint-info">{{u.name || u.address}}</span>
</p>
<p class="address">
To: <span ng-repeat="u in state.mailList.selected.to" class="label" ng-class="{'label-primary': u.secure === false}" data-icon-append="{{(u.secure === false) ? '&#xe001;' : ''}}" ng-mouseover="getKeyId(u.address)" popover="#fingerprint-info">{{u.name || u.address}}</span>
To: <span ng-repeat="u in state.mailList.selected.to" class="label" ng-class="{'label-primary': u.secure === false}" data-icon-append="{{(u.secure === false) ? '&#xe001;' : ''}}" ng-mouseover="getKeyId(u.address)" ng-click="invite(u)" popover="#fingerprint-info">{{u.name || u.address}}</span>
</p>
<div ng-switch="state.mailList.selected.cc !== undefined">
<p class="address" ng-switch-when="true">
Cc: <span ng-repeat="u in state.mailList.selected.cc" class="label" ng-class="{'label-primary': u.secure === false}" data-icon-append="{{(u.secure === false) ? '&#xe001;' : ''}}" ng-mouseover="getKeyId(u.address)" popover="#fingerprint-info">{{u.name || u.address}}</span>
Cc: <span ng-repeat="u in state.mailList.selected.cc" class="label" ng-class="{'label-primary': u.secure === false}" data-icon-append="{{(u.secure === false) ? '&#xe001;' : ''}}" ng-mouseover="getKeyId(u.address)" ng-click="invite(u)" popover="#fingerprint-info">{{u.name || u.address}}</span>
</p>
</div>
</div><!--/.headers-->
@ -54,6 +54,6 @@
<!-- popovers -->
<div id="fingerprint-info" class="popover right" ng-controller="PopoverCtrl">
<div class="arrow"></div>
<div class="popover-content"><b>PGP key:</b> {{keyId}}</div>
<div class="popover-content">{{keyId}}</div>
</div><!--/.popover-->
</div><!--/.view-read-->

View File

@ -5,8 +5,7 @@ define(function(require) {
OutboxBO = require('js/bo/outbox'),
KeychainDAO = require('js/dao/keychain-dao'),
EmailDAO = require('js/dao/email-dao'),
DeviceStorageDAO = require('js/dao/devicestorage-dao'),
InvitationDAO = require('js/dao/invitation-dao');
DeviceStorageDAO = require('js/dao/devicestorage-dao');
chai.Assertion.includeStack = true;
@ -25,7 +24,6 @@ define(function(require) {
};
devicestorageStub = sinon.createStubInstance(DeviceStorageDAO);
keychainStub = sinon.createStubInstance(KeychainDAO);
invitationDaoStub = sinon.createStubInstance(InvitationDAO);
outbox = new OutboxBO(emailDaoStub, keychainStub, devicestorageStub, invitationDaoStub);
});
@ -46,7 +44,7 @@ define(function(require) {
});
describe('put', function() {
it('should encrypt and store a mail', function(done) {
it('should not encrypt and store a mail', function(done) {
var mail, senderKey, receiverKey;
senderKey = {
@ -75,9 +73,51 @@ define(function(require) {
keychainStub.getReceiverPublicKey.withArgs(mail.to[0].address).yieldsAsync(null, receiverKey);
keychainStub.getReceiverPublicKey.withArgs(mail.to[1].address).yieldsAsync();
devicestorageStub.storeList.withArgs([mail]).yieldsAsync();
outbox.put(mail, function(error) {
expect(error).to.not.exist;
expect(mail.publicKeysArmored.length).to.equal(2);
expect(emailDaoStub.encrypt.called).to.be.false;
expect(devicestorageStub.storeList.calledOnce).to.be.true;
done();
});
});
it('should encrypt and store a mail', function(done) {
var mail, senderKey, receiverKey;
senderKey = {
publicKey: 'SENDER PUBLIC KEY'
};
receiverKey = {
publicKey: 'RECEIVER PUBLIC KEY'
};
mail = {
from: [{
name: 'member',
address: 'member@whiteout.io'
}],
to: [{
name: 'member',
address: 'member'
}, {
name: 'notamember',
address: 'notamember'
}],
cc: [],
bcc: []
};
keychainStub.getReceiverPublicKey.withArgs(mail.from[0].address).yieldsAsync(null, senderKey);
keychainStub.getReceiverPublicKey.withArgs(mail.to[0].address).yieldsAsync(null, receiverKey);
keychainStub.getReceiverPublicKey.withArgs(mail.to[1].address).yieldsAsync(null, receiverKey);
emailDaoStub.encrypt.withArgs({
mail: mail,
publicKeysArmored: [senderKey.publicKey, receiverKey.publicKey]
publicKeysArmored: [senderKey.publicKey, receiverKey.publicKey, receiverKey.publicKey]
}).yieldsAsync();
devicestorageStub.storeList.withArgs([mail]).yieldsAsync();
@ -85,8 +125,9 @@ define(function(require) {
outbox.put(mail, function(error) {
expect(error).to.not.exist;
expect(mail.publicKeysArmored.length).to.equal(2);
expect(mail.unregisteredUsers.length).to.equal(1);
expect(mail.publicKeysArmored.length).to.equal(3);
expect(emailDaoStub.encrypt.calledOnce).to.be.true;
expect(devicestorageStub.storeList.calledOnce).to.be.true;
done();
});
@ -162,28 +203,8 @@ define(function(require) {
keychainStub.getReceiverPublicKey.withArgs(notinvited.unregisteredUsers[0].address).yieldsAsync();
keychainStub.getReceiverPublicKey.withArgs(newlyjoined.unregisteredUsers[0].address).yieldsAsync(null, newlyjoinedKey);
invitationDaoStub.check.withArgs({
recipient: invited.to[0].address,
sender: invited.from[0].address
}).yieldsAsync(null, InvitationDAO.INVITE_PENDING);
invitationDaoStub.check.withArgs({
recipient: notinvited.to[0].address,
sender: notinvited.from[0].address
}).yieldsAsync(null, InvitationDAO.INVITE_MISSING);
invitationDaoStub.invite.withArgs({
recipient: notinvited.to[0].address,
sender: notinvited.from[0].address
}).yieldsAsync(null, InvitationDAO.INVITE_SUCCESS);
emailDaoStub.sendPlaintext.yieldsAsync();
emailDaoStub.reEncrypt.withArgs({
mail: newlyjoined,
publicKeysArmored: [newlyjoinedKey.publicKey]
}).yieldsAsync(null, newlyjoined);
emailDaoStub.sendEncrypted.withArgs({
email: newlyjoined
}).yieldsAsync();