mirror of https://github.com/moparisthebest/mail
Compare commits
18 Commits
Author | SHA1 | Date |
---|---|---|
Tankred Hase | 43729833a5 | |
Tankred Hase | b05aeea342 | |
Tankred Hase | 137c8c7c24 | |
Tankred Hase | 853be194d9 | |
Tankred Hase | 8b36a719c3 | |
Tankred Hase | 8165416c5d | |
Tankred Hase | b375d81635 | |
Felix Hammerl | e56f8c2c28 | |
Felix Hammerl | ad3691fae9 | |
Tankred Hase | e0663ab8d8 | |
Tankred Hase | 263b5c13b0 | |
Tankred Hase | 8636ba201b | |
Tankred Hase | aa24881efc | |
Tankred Hase | b7b5c1bdf5 | |
Tankred Hase | 59d38f9b14 | |
Tankred Hase | c44984b2f3 | |
Felix Hammerl | c31c320e83 | |
Tankred Hase | 7f49e691db |
|
@ -1,10 +1,10 @@
|
|||
branch-defaults:
|
||||
release/prod:
|
||||
environment: mail-html5-prod
|
||||
environment: mail-prod
|
||||
release/test:
|
||||
environment: mail-html5-test
|
||||
environment: mail-test
|
||||
global:
|
||||
application_name: mail-html5
|
||||
application_name: mail
|
||||
default_ec2_keyname: null
|
||||
default_platform: Node.js
|
||||
default_region: eu-central-1
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
sudo: false
|
||||
language: node_js
|
||||
node_js:
|
||||
- "0.12"
|
||||
before_install:
|
||||
- gem install sass
|
||||
- npm install -g grunt-cli
|
||||
notifications:
|
||||
email:
|
||||
- build@whiteout.io
|
||||
|
|
|
@ -282,7 +282,6 @@ module.exports = function(grunt) {
|
|||
},
|
||||
app: {
|
||||
src: [
|
||||
'src/lib/winstore-jscompat.js',
|
||||
'src/lib/underscore/underscore.js',
|
||||
'node_modules/jquery/dist/jquery.min.js',
|
||||
'src/lib/angular/angular.js',
|
||||
|
@ -308,7 +307,7 @@ module.exports = function(grunt) {
|
|||
},
|
||||
readSandbox: {
|
||||
src: [
|
||||
'node_modules/dompurify/purify.js',
|
||||
'node_modules/dompurify/src/purify.js',
|
||||
'node_modules/iframe-resizer/js/iframeResizer.contentWindow.min.js',
|
||||
'src/js/controller/app/read-sandbox.js'
|
||||
],
|
||||
|
|
16
README.md
16
README.md
|
@ -1,4 +1,4 @@
|
|||
Whiteout Mail [![Build Status](https://travis-ci.org/whiteout-io/mail-html5.svg?branch=master)](https://travis-ci.org/whiteout-io/mail-html5) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/whiteout-io/mail-html5?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
Whiteout Mail [![Build Status](https://travis-ci.org/whiteout-io/mail.svg?branch=master)](https://travis-ci.org/whiteout-io/mail)
|
||||
==========
|
||||
|
||||
Whiteout Mail is an easy to use email client with integrated OpenPGP encryption written in pure JavaScript. Download the official version under [whiteout.io](https://whiteout.io/#product).
|
||||
|
@ -7,15 +7,17 @@ Whiteout Mail is an easy to use email client with integrated OpenPGP encryption
|
|||
|
||||
### Features
|
||||
|
||||
You can read about product features and our future roadmap in our [FAQ](https://github.com/whiteout-io/mail-html5/wiki/FAQ).
|
||||
You can read about product features and our future roadmap in our [FAQ](https://github.com/whiteout-io/mail/wiki/FAQ).
|
||||
|
||||
### Privacy and Security
|
||||
|
||||
We take the privacy of your data very seriously. Here are some of the technical details:
|
||||
|
||||
* The code has undergone a [full security audit](https://blog.whiteout.io/2015/06/11/whiteout-mail-1-0-and-security-audit-by-cure53/) by [Cure53](https://cure53.de).
|
||||
|
||||
* Messages are [encrypted end-to-end ](http://en.wikipedia.org/wiki/End-to-end_encryption) using the [OpenPGP](http://en.wikipedia.org/wiki/Pretty_Good_Privacy) standard. This means that only you and the recipient can read your mail. Your messages and private PGP key are stored only on your computer (in IndexedDB).
|
||||
|
||||
* Users have the option to use [encrypted private key sync](https://github.com/whiteout-io/mail-html5/wiki/Secure-OpenPGP-Key-Pair-Synchronization-via-IMAP) if they want to use Whiteout on multiple devices.
|
||||
* Users have the option to use [encrypted private key sync](https://github.com/whiteout-io/mail/wiki/Secure-OpenPGP-Key-Pair-Synchronization-via-IMAP) if they want to use Whiteout on multiple devices.
|
||||
|
||||
* [Content Security Policy (CSP)](http://www.html5rocks.com/en/tutorials/security/content-security-policy/) is enforced to prevent injection attacks.
|
||||
|
||||
|
@ -25,7 +27,7 @@ We take the privacy of your data very seriously. Here are some of the technical
|
|||
|
||||
* Like most native email clients, whiteout mail uses raw [TCP sockets](http://developer.chrome.com/apps/socket.html) to communicate directly with your mail server via IMAP/SMTP. TLS is used to protect your password and message data in transit.
|
||||
|
||||
* The app is deployed as a signed [Chrome Packaged App](https://developer.chrome.com/apps/about_apps.html) with [auditable static versions](https://github.com/whiteout-io/mail-html5/releases) in order to prevent [problems with host-based security](https://blog.whiteout.io/2014/04/13/heartbleed-and-javascript-crypto/).
|
||||
* The app is deployed as a signed [Chrome Packaged App](https://developer.chrome.com/apps/about_apps.html) with [auditable static versions](https://github.com/whiteout-io/mail/releases) in order to prevent [problems with host-based security](https://blog.whiteout.io/2014/04/13/heartbleed-and-javascript-crypto/).
|
||||
|
||||
* The app can also be used from any modern web browser in environments where installing an app is not possible (e.g. a locked down corporate desktop). The IMAP/SMTP TLS sessions are still terminated in the user's browser using JS crypto ([Forge](https://github.com/digitalbazaar/forge)), but the encrypted TLS payload is proxied via [socket.io](http://socket.io/), due to the lack of raw sockets in the browser. **Please keep in mind that this mode of operation is not as secure as using the signed packaged app, since users must trust the webserver to deliver the correct code. This mode will still protect user against passive attacks like wiretapping (since PGP and TLS are still applied in the user's browser), but not against active attacks from the webserver. So it's best to decide which threat model applies to you.**
|
||||
|
||||
|
@ -37,11 +39,11 @@ We take the privacy of your data very seriously. Here are some of the technical
|
|||
|
||||
* We will launch a bug bounty program later on for independent security researchers. If you find any security vulnerabilities, don't hesitate to contact us [security@whiteout.io](mailto:security@whiteout.io).
|
||||
|
||||
* You can also just create an [issue](https://github.com/whiteout-io/mail-html5/issues) on GitHub if you're missing a feature or just want to give us feedback. It would be much appreciated!
|
||||
* You can also just create an [issue](https://github.com/whiteout-io/mail/issues) on GitHub if you're missing a feature or just want to give us feedback. It would be much appreciated!
|
||||
|
||||
### Testing
|
||||
|
||||
You can download a prebuilt bundle under [releases](https://github.com/whiteout-io/mail-html5/releases) or build your own from source (requires [node.js](http://nodejs.org/download/), [grunt](http://gruntjs.com/getting-started#installing-the-cli) and [sass](http://sass-lang.com/install)):
|
||||
You can download a prebuilt bundle under [releases](https://github.com/whiteout-io/mail/releases) or build your own from source (requires [node.js](http://nodejs.org/download/), [grunt](http://gruntjs.com/getting-started#installing-the-cli) and [sass](http://sass-lang.com/install)):
|
||||
|
||||
npm install && npm test
|
||||
|
||||
|
@ -69,7 +71,7 @@ The App can be used either as a Chrome Packaged App or just by hosting it on you
|
|||
|
||||
Clone the git repository
|
||||
|
||||
git clone https://github.com/whiteout-io/mail-html5.git
|
||||
git clone https://github.com/whiteout-io/mail.git
|
||||
|
||||
Build and generate the `dist/` directory:
|
||||
|
||||
|
|
24
package.json
24
package.json
|
@ -3,21 +3,6 @@
|
|||
"description": "Mail App with integrated OpenPGP encryption.",
|
||||
"author": "Whiteout Networks",
|
||||
"homepage": "https://whiteout.io",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/whiteout-io/mail-html5.git"
|
||||
},
|
||||
"keywords": [
|
||||
"email",
|
||||
"mail",
|
||||
"client",
|
||||
"app",
|
||||
"openpgp",
|
||||
"pgp",
|
||||
"gpg",
|
||||
"imap",
|
||||
"smtp"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
},
|
||||
|
@ -40,11 +25,12 @@
|
|||
"browsersmtp": "https://github.com/whiteout-io/browsersmtp/tarball/master",
|
||||
"chai": "~1.9.2",
|
||||
"crypto-lib": "~0.2.1",
|
||||
"dompurify": "~0.6.3",
|
||||
"dompurify": "~0.7.3",
|
||||
"grunt": "~0.4.1",
|
||||
"grunt-angular-templates": "~0.5.7",
|
||||
"grunt-autoprefixer": "~0.7.2",
|
||||
"grunt-browserify": "~3.7.0",
|
||||
"grunt-browserify": "3.7.0",
|
||||
"insert-module-globals": "6.5.0",
|
||||
"grunt-contrib-clean": "~0.5.0",
|
||||
"grunt-contrib-compress": "~0.5.2",
|
||||
"grunt-contrib-concat": "^0.5.0",
|
||||
|
@ -57,7 +43,7 @@
|
|||
"grunt-csso": "~0.6.1",
|
||||
"grunt-exorcise": "^0.2.0",
|
||||
"grunt-manifest": "^0.4.0",
|
||||
"grunt-mocha-phantomjs": "^0.6.0",
|
||||
"grunt-mocha-phantomjs": "^0.7.0",
|
||||
"grunt-shell": "~1.1.1",
|
||||
"grunt-string-replace": "~1.0.0",
|
||||
"grunt-svgmin": "~1.0.0",
|
||||
|
@ -79,4 +65,4 @@
|
|||
"time-grunt": "^1.0.0",
|
||||
"wo-smtpclient": "~0.6.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,8 +52,7 @@ var CreateAccountCtrl = function($scope, $location, $routeParams, $q, auth, admi
|
|||
return admin.createUser({
|
||||
emailAddress: emailAddress,
|
||||
password: $scope.pass,
|
||||
phone: phone.internationalNumber,
|
||||
betaCode: $scope.betaCode.toUpperCase()
|
||||
phone: phone.internationalNumber
|
||||
});
|
||||
|
||||
}).then(function() {
|
||||
|
|
|
@ -102,24 +102,39 @@ PrivateKey.prototype.upload = function(options) {
|
|||
|
||||
return new Promise(function(resolve) {
|
||||
if (!options._id || !options.userId || !options.encryptedPrivateKey || !options.salt || !options.iv) {
|
||||
throw new Error('Incomplete arguments!');
|
||||
throw new Error('Incomplete arguments for key upload!');
|
||||
}
|
||||
resolve();
|
||||
|
||||
}).then(function() {
|
||||
// create imap folder
|
||||
return self._imap.createFolder({
|
||||
path: IMAP_KEYS_FOLDER
|
||||
}).then(function(fullPath) {
|
||||
|
||||
// Some servers (Exchange, Cyrus) error when creating an existing IMAP mailbox instead of
|
||||
// responding with ALREADYEXISTS. Hence we search for the folder before uploading.
|
||||
|
||||
self._axe.debug('Searching imap folder for key upload...');
|
||||
|
||||
return self._getFolder().then(function(fullPath) {
|
||||
path = fullPath;
|
||||
self._axe.debug('Successfully created imap folder ' + path);
|
||||
}).catch(function(err) {
|
||||
var prettyErr = new Error('Creating imap folder ' + IMAP_KEYS_FOLDER + ' failed: ' + err.message);
|
||||
self._axe.error(prettyErr);
|
||||
throw prettyErr;
|
||||
}).catch(function() {
|
||||
|
||||
// create imap folder
|
||||
self._axe.debug('Folder not found, creating imap folder.');
|
||||
return self._imap.createFolder({
|
||||
path: IMAP_KEYS_FOLDER
|
||||
}).then(function(fullPath) {
|
||||
path = fullPath;
|
||||
self._axe.debug('Successfully created imap folder ' + path);
|
||||
}).catch(function(err) {
|
||||
var prettyErr = new Error('Creating imap folder ' + IMAP_KEYS_FOLDER + ' failed: ' + err.message);
|
||||
self._axe.error(prettyErr);
|
||||
throw prettyErr;
|
||||
});
|
||||
});
|
||||
|
||||
}).then(createMessage).then(function(message) {
|
||||
|
||||
// upload to imap folder
|
||||
self._axe.debug('Uploading key...');
|
||||
return self._imap.uploadMessage({
|
||||
path: path,
|
||||
message: message
|
||||
|
@ -380,4 +395,4 @@ function filterBodyParts(bodyParts, type, result) {
|
|||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -1,174 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
// JavaScript Dynamic Content shim for Windows Store apps
|
||||
(function () {
|
||||
|
||||
if (window.MSApp && MSApp.execUnsafeLocalFunction) {
|
||||
|
||||
// Some nodes will have an "attributes" property which shadows the Node.prototype.attributes property
|
||||
// and means we don't actually see the attributes of the Node (interestingly the VS debug console
|
||||
// appears to suffer from the same issue).
|
||||
//
|
||||
var Element_setAttribute = Object.getOwnPropertyDescriptor(Element.prototype, "setAttribute").value;
|
||||
var Element_removeAttribute = Object.getOwnPropertyDescriptor(Element.prototype, "removeAttribute").value;
|
||||
var HTMLElement_insertAdjacentHTMLPropertyDescriptor = Object.getOwnPropertyDescriptor(HTMLElement.prototype, "insertAdjacentHTML");
|
||||
var Node_get_attributes = Object.getOwnPropertyDescriptor(Node.prototype, "attributes").get;
|
||||
var Node_get_childNodes = Object.getOwnPropertyDescriptor(Node.prototype, "childNodes").get;
|
||||
var detectionDiv = document.createElement("div");
|
||||
|
||||
function getAttributes(element) {
|
||||
return Node_get_attributes.call(element);
|
||||
}
|
||||
|
||||
function setAttribute(element, attribute, value) {
|
||||
try {
|
||||
Element_setAttribute.call(element, attribute, value);
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
function removeAttribute(element, attribute) {
|
||||
Element_removeAttribute.call(element, attribute);
|
||||
}
|
||||
|
||||
function childNodes(element) {
|
||||
return Node_get_childNodes.call(element);
|
||||
}
|
||||
|
||||
function empty(element) {
|
||||
while (element.childNodes.length) {
|
||||
element.removeChild(element.lastChild);
|
||||
}
|
||||
}
|
||||
|
||||
function insertAdjacentHTML(element, position, html) {
|
||||
HTMLElement_insertAdjacentHTMLPropertyDescriptor.value.call(element, position, html);
|
||||
}
|
||||
|
||||
function inUnsafeMode() {
|
||||
var isUnsafe = true;
|
||||
try {
|
||||
detectionDiv.innerHTML = "<test/>";
|
||||
}
|
||||
catch (ex) {
|
||||
isUnsafe = false;
|
||||
}
|
||||
|
||||
return isUnsafe;
|
||||
}
|
||||
|
||||
function cleanse(html, targetElement) {
|
||||
var cleaner = document.implementation.createHTMLDocument("cleaner");
|
||||
empty(cleaner.documentElement);
|
||||
MSApp.execUnsafeLocalFunction(function () {
|
||||
insertAdjacentHTML(cleaner.documentElement, "afterbegin", html);
|
||||
});
|
||||
|
||||
var scripts = cleaner.documentElement.querySelectorAll("script");
|
||||
Array.prototype.forEach.call(scripts, function (script) {
|
||||
switch (script.type.toLowerCase()) {
|
||||
case "":
|
||||
script.type = "text/inert";
|
||||
break;
|
||||
case "text/javascript":
|
||||
case "text/ecmascript":
|
||||
case "text/x-javascript":
|
||||
case "text/jscript":
|
||||
case "text/livescript":
|
||||
case "text/javascript1.1":
|
||||
case "text/javascript1.2":
|
||||
case "text/javascript1.3":
|
||||
script.type = "text/inert-" + script.type.slice("text/".length);
|
||||
break;
|
||||
case "application/javascript":
|
||||
case "application/ecmascript":
|
||||
case "application/x-javascript":
|
||||
script.type = "application/inert-" + script.type.slice("application/".length);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
function cleanseAttributes(element) {
|
||||
var attributes = getAttributes(element);
|
||||
if (attributes && attributes.length) {
|
||||
// because the attributes collection is live it is simpler to queue up the renames
|
||||
var events;
|
||||
for (var i = 0, len = attributes.length; i < len; i++) {
|
||||
var attribute = attributes[i];
|
||||
var name = attribute.name;
|
||||
if ((name[0] === "o" || name[0] === "O") &&
|
||||
(name[1] === "n" || name[1] === "N")) {
|
||||
events = events || [];
|
||||
events.push({ name: attribute.name, value: attribute.value });
|
||||
}
|
||||
}
|
||||
if (events) {
|
||||
for (var i = 0, len = events.length; i < len; i++) {
|
||||
var attribute = events[i];
|
||||
removeAttribute(element, attribute.name);
|
||||
setAttribute(element, "x-" + attribute.name, attribute.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
var children = childNodes(element);
|
||||
for (var i = 0, len = children.length; i < len; i++) {
|
||||
cleanseAttributes(children[i]);
|
||||
}
|
||||
}
|
||||
cleanseAttributes(cleaner.documentElement);
|
||||
|
||||
var cleanedNodes = [];
|
||||
|
||||
if (targetElement.tagName === 'HTML') {
|
||||
cleanedNodes = Array.prototype.slice.call(document.adoptNode(cleaner.documentElement).childNodes);
|
||||
} else {
|
||||
if (cleaner.head) {
|
||||
cleanedNodes = cleanedNodes.concat(Array.prototype.slice.call(document.adoptNode(cleaner.head).childNodes));
|
||||
}
|
||||
if (cleaner.body) {
|
||||
cleanedNodes = cleanedNodes.concat(Array.prototype.slice.call(document.adoptNode(cleaner.body).childNodes));
|
||||
}
|
||||
}
|
||||
|
||||
return cleanedNodes;
|
||||
}
|
||||
|
||||
function cleansePropertySetter(property, setter) {
|
||||
var propertyDescriptor = Object.getOwnPropertyDescriptor(HTMLElement.prototype, property);
|
||||
var originalSetter = propertyDescriptor.set;
|
||||
Object.defineProperty(HTMLElement.prototype, property, {
|
||||
get: propertyDescriptor.get,
|
||||
set: function (value) {
|
||||
if(window.WinJS && window.WinJS._execUnsafe && inUnsafeMode()) {
|
||||
originalSetter.call(this, value);
|
||||
} else {
|
||||
var that = this;
|
||||
var nodes = cleanse(value, that);
|
||||
MSApp.execUnsafeLocalFunction(function () {
|
||||
setter(propertyDescriptor, that, nodes);
|
||||
});
|
||||
}
|
||||
},
|
||||
enumerable: propertyDescriptor.enumerable,
|
||||
configurable: propertyDescriptor.configurable,
|
||||
});
|
||||
}
|
||||
cleansePropertySetter("innerHTML", function (propertyDescriptor, target, elements) {
|
||||
empty(target);
|
||||
for (var i = 0, len = elements.length; i < len; i++) {
|
||||
target.appendChild(elements[i]);
|
||||
}
|
||||
});
|
||||
cleansePropertySetter("outerHTML", function (propertyDescriptor, target, elements) {
|
||||
for (var i = 0, len = elements.length; i < len; i++) {
|
||||
target.insertAdjacentElement("afterend", elements[i]);
|
||||
}
|
||||
target.parentNode.removeChild(target);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}());
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"packageId": "io.whiteout.WhiteoutMail",
|
||||
"versionCode": 26,
|
||||
"versionCode": 28,
|
||||
"CFBundleVersion": "1",
|
||||
|
||||
"ios": {
|
||||
|
|
|
@ -287,9 +287,6 @@
|
|||
<input class="input-text" type="tel" ng-model="dial" required placeholder="Mobile phone (for SMS validation)" tabindex="5">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form__row">
|
||||
<input class="input-text" type="text" ng-model="betaCode" required placeholder="Invitation code" tabindex="6">
|
||||
</div>
|
||||
<div class="spinner-block" ng-show="busy">
|
||||
<span class="spinner spinner--big"></span>
|
||||
</div>
|
||||
|
@ -297,9 +294,6 @@
|
|||
<button class="btn" type="submit" ng-click="showConfirm()">Create</button>
|
||||
</div>
|
||||
</form>
|
||||
<p class="typo-paragraph">
|
||||
<a href="http://eepurl.com/ba09in" target="_blank" title="Sign up for mailbox access.">Need an invitation code?</a>
|
||||
</p>
|
||||
</main>
|
||||
<div ng-include="'tpl/page-footer.html'"></div>
|
||||
</div>
|
||||
|
|
|
@ -249,6 +249,7 @@ describe('Email DAO integration tests', function() {
|
|||
function onCleaned() {
|
||||
userStorage = accountService._accountStore;
|
||||
auth = accountService._auth;
|
||||
emailDao = accountService._emailDao;
|
||||
|
||||
auth.setCredentials({
|
||||
emailAddress: testAccount.user,
|
||||
|
@ -257,19 +258,17 @@ describe('Email DAO integration tests', function() {
|
|||
imap: {} // a preconfigured smtpclient with mocked tcp sockets
|
||||
});
|
||||
|
||||
// stub rest request to key server
|
||||
sinon.stub(emailDao._keychain._publicKeyDao, 'get').returns(resolves(mockKeyPair.publicKey));
|
||||
sinon.stub(emailDao._keychain._publicKeyDao, 'getByUserId').returns(resolves(mockKeyPair.publicKey));
|
||||
|
||||
auth.init().then(function() {
|
||||
accountService.init({
|
||||
emailAddress: testAccount.user
|
||||
}).then(function() {
|
||||
emailDao = accountService._emailDao;
|
||||
|
||||
// retrieve the pgpbuilder from the emaildao and initialize the pgpmailer with the existing pgpbuilder
|
||||
pgpMailer = new PgpMailer({}, emailDao._pgpbuilder);
|
||||
|
||||
// stub rest request to key server
|
||||
sinon.stub(emailDao._keychain._publicKeyDao, 'get').returns(resolves(mockKeyPair.publicKey));
|
||||
sinon.stub(emailDao._keychain._publicKeyDao, 'getByUserId').returns(resolves(mockKeyPair.publicKey));
|
||||
|
||||
emailDao.unlock({
|
||||
passphrase: testAccount.pass,
|
||||
keypair: mockKeyPair
|
||||
|
|
|
@ -68,6 +68,14 @@ describe('Private Key DAO unit tests', function() {
|
|||
});
|
||||
|
||||
describe('upload', function() {
|
||||
beforeEach(function() {
|
||||
sinon.stub(privkeyDao, '_getFolder');
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
privkeyDao._getFolder.restore();
|
||||
});
|
||||
|
||||
it('should fail due to invalid args', function(done) {
|
||||
privkeyDao.upload({}).catch(function(err) {
|
||||
expect(err.message).to.match(/Incomplete/);
|
||||
|
@ -75,10 +83,11 @@ describe('Private Key DAO unit tests', function() {
|
|||
});
|
||||
});
|
||||
|
||||
it('should work', function(done) {
|
||||
it('should work without existing folder', function(done) {
|
||||
var IMAP_KEYS_FOLDER = 'openpgp_keys';
|
||||
var fullPath = 'INBOX.' + IMAP_KEYS_FOLDER;
|
||||
|
||||
privkeyDao._getFolder.returns(rejects(new Error()));
|
||||
imapClientStub.createFolder.withArgs({
|
||||
path: IMAP_KEYS_FOLDER
|
||||
}).returns(resolves(fullPath));
|
||||
|
@ -95,11 +104,37 @@ describe('Private Key DAO unit tests', function() {
|
|||
salt: salt,
|
||||
iv: iv
|
||||
}).then(function() {
|
||||
expect(privkeyDao._getFolder.calledOnce).to.be.true;
|
||||
expect(imapClientStub.createFolder.calledOnce).to.be.true;
|
||||
expect(imapClientStub.uploadMessage.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should work with existing folder', function(done) {
|
||||
var IMAP_KEYS_FOLDER = 'openpgp_keys';
|
||||
var fullPath = 'INBOX.' + IMAP_KEYS_FOLDER;
|
||||
|
||||
privkeyDao._getFolder.returns(resolves(fullPath));
|
||||
imapClientStub.uploadMessage.withArgs(sinon.match(function(arg) {
|
||||
expect(arg.path).to.equal(fullPath);
|
||||
expect(arg.message).to.exist;
|
||||
return true;
|
||||
})).returns(resolves());
|
||||
|
||||
privkeyDao.upload({
|
||||
_id: keyId,
|
||||
userId: emailAddress,
|
||||
encryptedPrivateKey: encryptedPrivateKey,
|
||||
salt: salt,
|
||||
iv: iv
|
||||
}).then(function() {
|
||||
expect(privkeyDao._getFolder.calledOnce).to.be.true;
|
||||
expect(imapClientStub.createFolder.called).to.be.false;
|
||||
expect(imapClientStub.uploadMessage.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('isSynced', function() {
|
||||
|
|
Loading…
Reference in New Issue