From d4adbf453d39659eee378b2be1d7125315d76083 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Mon, 28 May 2018 16:52:50 +0200 Subject: [PATCH] js: Treat a connection as a gpgme Context -- * After an operation a connection should be disconnected again. The "end of operation" is now assumed to be either an error as answer, or a message not including a "more" * GPGME, GPGME_Key, GPGME_Keyring don't require a connection anymore * Message.js: The Message.post() method will open a connection as required --- .../tests/encryptDecryptTest.js | 7 -- .../BrowserTestExtension/tests/encryptTest.js | 8 --- .../tests/longRunningTests.js | 1 - .../js/BrowserTestExtension/tests/signTest.js | 2 - lang/js/BrowserTestExtension/tests/startup.js | 3 - lang/js/src/Connection.js | 14 ++-- lang/js/src/Key.js | 42 ++---------- lang/js/src/Keyring.js | 20 +----- lang/js/src/Message.js | 18 +++++ lang/js/src/gpgmejs.js | 45 +++--------- lang/js/src/index.js | 2 +- lang/js/unittests.js | 68 +++---------------- 12 files changed, 59 insertions(+), 171 deletions(-) diff --git a/lang/js/BrowserTestExtension/tests/encryptDecryptTest.js b/lang/js/BrowserTestExtension/tests/encryptDecryptTest.js index 2fe955e6..f5d2be16 100644 --- a/lang/js/BrowserTestExtension/tests/encryptDecryptTest.js +++ b/lang/js/BrowserTestExtension/tests/encryptDecryptTest.js @@ -34,7 +34,6 @@ describe('Encryption and Decryption', function () { expect(result).to.not.be.empty; expect(result.data).to.be.a('string'); expect(result.data).to.equal(inputvalues.encrypt.good.data); - context.connection.disconnect(); done(); }); }); @@ -75,7 +74,6 @@ describe('Encryption and Decryption', function () { expect(result).to.not.be.empty; expect(result.data).to.be.a('string'); expect(result.data).to.equal(data); - context.connection.disconnect(); done(); }); @@ -108,7 +106,6 @@ describe('Encryption and Decryption', function () { expect(result).to.not.be.empty; expect(result.data).to.be.a('string'); expect(result.data).to.equal(data); - context.connection.disconnect(); done(); }); }); @@ -134,7 +131,6 @@ describe('Encryption and Decryption', function () { expect(result).to.not.be.empty; expect(result.data).to.be.a('string'); expect(result.data).to.equal(data); - context.connection.disconnect(); done(); }); }); @@ -160,7 +156,6 @@ describe('Encryption and Decryption', function () { expect(result).to.not.be.empty; expect(result.data).to.be.a('string'); expect(data).to.equal(data); - context.connection.disconnect(); done(); }); }); @@ -187,7 +182,6 @@ describe('Encryption and Decryption', function () { expect(result).to.not.be.empty; expect(result.data).to.be.a('string'); expect(result.data).to.equal(data); - context.connection.disconnect(); done(); }); }); @@ -214,7 +208,6 @@ describe('Encryption and Decryption', function () { expect(result).to.not.be.empty; expect(result.data).to.be.a('string'); expect(result.data).to.equal(b64data); - context.connection.disconnect(); done(); }); }); diff --git a/lang/js/BrowserTestExtension/tests/encryptTest.js b/lang/js/BrowserTestExtension/tests/encryptTest.js index 521ed276..a16f993c 100644 --- a/lang/js/BrowserTestExtension/tests/encryptTest.js +++ b/lang/js/BrowserTestExtension/tests/encryptTest.js @@ -28,7 +28,6 @@ describe('Encryption', function () { expect(answer.data).to.be.a("string"); expect(answer.data).to.include('BEGIN PGP MESSAGE'); expect(answer.data).to.include('END PGP MESSAGE'); - context.connection.disconnect(); done(); }); }); @@ -45,7 +44,6 @@ describe('Encryption', function () { expect(answer.data).to.be.a("string"); expect(answer.data).to.include('BEGIN PGP MESSAGE'); expect(answer.data).to.include('END PGP MESSAGE'); - context.connection.disconnect(); done(); }); }); @@ -62,7 +60,6 @@ describe('Encryption', function () { expect(answer.data).to.be.a("string"); expect(answer.data).to.include('BEGIN PGP MESSAGE'); expect(answer.data).to.include('END PGP MESSAGE'); - context.connection.disconnect(); done(); }); }); @@ -79,7 +76,6 @@ describe('Encryption', function () { expect(answer.data).to.be.a("string"); expect(answer.data).to.include('BEGIN PGP MESSAGE'); expect(answer.data).to.include('END PGP MESSAGE'); - context.connection.disconnect(); done(); }); }); @@ -95,7 +91,6 @@ describe('Encryption', function () { }, function(error){ expect(error).to.be.an('Error'); expect(error.code).to.equal('MSG_INCOMPLETE'); - context.connection.disconnect(); done(); }); }); @@ -110,7 +105,6 @@ describe('Encryption', function () { }, function (error) { expect(error).to.be.an.instanceof(Error); expect(error.code).to.equal('MSG_INCOMPLETE'); - context.connection.disconnect(); done(); }); }); @@ -127,7 +121,6 @@ describe('Encryption', function () { expect(error).to.be.an('Error'); expect(error.code).to.not.be.undefined; expect(error.code).to.equal('GNUPG_ERROR'); - context.connection.disconnect(); done(); }); }); @@ -146,7 +139,6 @@ describe('Encryption', function () { // TODO: there is a 64 MB hard limit at least in chrome at: // chromium//extensions/renderer/messaging_util.cc: // kMaxMessageLength - context.connection.disconnect(); done(); }); }); diff --git a/lang/js/BrowserTestExtension/tests/longRunningTests.js b/lang/js/BrowserTestExtension/tests/longRunningTests.js index 4e55fd26..5c588f27 100644 --- a/lang/js/BrowserTestExtension/tests/longRunningTests.js +++ b/lang/js/BrowserTestExtension/tests/longRunningTests.js @@ -29,7 +29,6 @@ describe('Long running Encryption/Decryption', function () { } } expect(result.data).to.equal(data); - context.connection.disconnect(); done(); }); }); diff --git a/lang/js/BrowserTestExtension/tests/signTest.js b/lang/js/BrowserTestExtension/tests/signTest.js index e3323721..2e5edb30 100644 --- a/lang/js/BrowserTestExtension/tests/signTest.js +++ b/lang/js/BrowserTestExtension/tests/signTest.js @@ -30,7 +30,6 @@ describe('Signing', function () { expect(answer.data).to.include('BEGIN PGP SIGNATURE'); expect(answer.data).to.include('END PGP SIGNATURE'); expect(answer.data).to.include(data); - context.connection.disconnect(); done(); }); }); @@ -49,7 +48,6 @@ describe('Signing', function () { expect(answer.data).to.include(data); expect(answer.signature).to.be.a('string'); expect(answer.signature).to.be.a('string'); - context.connection.disconnect(); done(); }); }); diff --git a/lang/js/BrowserTestExtension/tests/startup.js b/lang/js/BrowserTestExtension/tests/startup.js index ebecf4fb..7d13ea47 100644 --- a/lang/js/BrowserTestExtension/tests/startup.js +++ b/lang/js/BrowserTestExtension/tests/startup.js @@ -23,10 +23,7 @@ let prm = Gpgmejs.init(); prm.then( function(context){ - expect(context.connection).to.not.be.undefined; expect(context).to.be.an('object'); - expect(context.connection).to.be.an('object'); - expect(context.Keyring).to.be.undefined; expect(context.encrypt).to.be.a('function'); expect(context.decrypt).to.be.a('function'); done(); diff --git a/lang/js/src/Connection.js b/lang/js/src/Connection.js index 3b442622..3480d811 100644 --- a/lang/js/src/Connection.js +++ b/lang/js/src/Connection.js @@ -102,9 +102,11 @@ export class Connection{ } if (!message || !message instanceof GPGME_Message){ + this.disconnect(); return Promise.reject(gpgme_error('PARAM_WRONG'), message); } if (message.isComplete !== true){ + this.disconnect(); return Promise.reject(gpgme_error('MSG_INCOMPLETE')); } let me = this; @@ -113,25 +115,27 @@ export class Connection{ let listener = function(msg) { if (!msg){ me._connection.onMessage.removeListener(listener) + me._connection.disconnect(); reject(gpgme_error('CONN_EMPTY_GPG_ANSWER')); } else if (msg.type === "error"){ me._connection.onMessage.removeListener(listener); + me._connection.disconnect(); reject(gpgme_error('GNUPG_ERROR', msg.msg)); } else { let answer_result = answer.add(msg); if (answer_result !== true){ me._connection.onMessage.removeListener(listener); + me._connection.disconnect(); reject(answer_result); - } - if (msg.more === true){ + } else if (msg.more === true){ me._connection.postMessage({'op': 'getmore'}); } else { me._connection.onMessage.removeListener(listener) + me._connection.disconnect(); resolve(answer.message); } } }; - me._connection.onMessage.addListener(listener); if (permittedOperations[message.operation].pinentry){ return me._connection.postMessage(message.message); @@ -140,12 +144,14 @@ export class Connection{ me._connection.postMessage(message.message), function(resolve, reject){ setTimeout(function(){ + me._connection.disconnect(); reject(gpgme_error('CONN_TIMEOUT')); }, 5000); }]).then(function(result){ - return result; + return result; }, function(reject){ if(!reject instanceof Error) { + me._connection.disconnect(); return gpgme_error('GNUPG_ERROR', reject); } else { return reject; diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js index 7d3d82b1..13c99542 100644 --- a/lang/js/src/Key.js +++ b/lang/js/src/Key.js @@ -30,27 +30,16 @@ import { isFingerprint, isLongId } from './Helpers' import { gpgme_error } from './Errors' import { createMessage } from './Message'; import { permittedOperations } from './permittedOperations'; -import { Connection } from './Connection'; /** - * Validates the fingerprint, and checks for tha availability of a connection. - * If both are available, a Key will be returned. + * Validates the fingerprint. * @param {String} fingerprint - * @param {Object} parent Either a Connection, or the invoking object with a - * Connection (e.g. Keyring) */ -export function createKey(fingerprint, parent){ +export function createKey(fingerprint){ if (!isFingerprint(fingerprint)){ return gpgme_error('PARAM_WRONG'); } - if ( parent instanceof Connection){ - return new GPGME_Key(fingerprint, parent); - } else if ( parent.hasOwnProperty('connection') && - parent.connection instanceof Connection){ - return new GPGME_Key(fingerprint, parent.connection); - } else { - return gpgme_error('PARAM_WRONG'); - } + else return new GPGME_Key(fingerprint); } /** @@ -58,28 +47,8 @@ export function createKey(fingerprint, parent){ */ export class GPGME_Key { - constructor(fingerprint, connection){ + constructor(fingerprint){ this.fingerprint = fingerprint; - this.connection = connection; - } - - set connection(conn){ - if (this._connection instanceof Connection) { - gpgme_error('CONN_ALREADY_CONNECTED'); - } else if (conn instanceof Connection ) { - this._connection = conn; - } - } - - get connection(){ - if (!this._data.fingerprint){ - return gpgme_error('KEY_INVALID'); - } - if (!this._connection instanceof Connection){ - return gpgme_error('CONN_NO_CONNECT'); - } else { - return this._connection; - } } set fingerprint(fpr){ @@ -219,7 +188,8 @@ export class GPGME_Key { let msg = createMessage('keylist'); msg.setParameter('sigs', true); msg.setParameter('keys', me._data.fingerprint); - me.connection.post(msg).then(function(result){ + console.log(msg); + msg.post().then(function(result){ if (result.keys.length === 1){ me.setKeydata(result.keys[0]); resolve(me); diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index 80792f77..9abb9ec3 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -22,23 +22,9 @@ import {createMessage} from './Message' import {GPGME_Key} from './Key' import { isFingerprint } from './Helpers'; import { gpgme_error } from './Errors'; -import { Connection } from './Connection'; export class GPGME_Keyring { - constructor(connection){ - this.connection = connection; - } - - set connection(connection){ - if (!this._connection && connection instanceof Connection){ - this._connection = connection; - } - } - get connection(){ - if (this._connection instanceof Connection){ - return this._connection; - } - return gpgme_error('CONN_NO_CONNECT'); + constructor(){ } /** @@ -58,7 +44,7 @@ export class GPGME_Keyring { if (include_secret){ msg.setParameter('with-secret', true); } - me.connection.post(msg).then(function(result){ + msg.post().then(function(result){ let fpr_list = []; let resultset = []; if (!Array.isArray(result.keys)){ @@ -68,7 +54,7 @@ export class GPGME_Keyring { fpr_list = result.keys; } for (let i=0; i < fpr_list.length; i++){ - let newKey = new GPGME_Key(fpr_list[i], me._connection); + let newKey = new GPGME_Key(fpr_list[i]); if (newKey instanceof GPGME_Key){ resultset.push(newKey); } diff --git a/lang/js/src/Message.js b/lang/js/src/Message.js index 932212a6..5664f723 100644 --- a/lang/js/src/Message.js +++ b/lang/js/src/Message.js @@ -19,6 +19,7 @@ */ import { permittedOperations } from './permittedOperations' import { gpgme_error } from './Errors' +import { Connection } from './Connection'; export function createMessage(operation){ if (typeof(operation) !== 'string'){ @@ -193,4 +194,21 @@ export class GPGME_Message { } } + + post(){ + let me = this; + return new Promise(function(resolve, reject) { + if (me.isComplete === true) { + let conn = new Connection; + conn.post(me).then(function(response) { + resolve(response); + }, function(reason) { + reject(gpgme_error('GNUPG_ERROR', reason)); + }); + } + else { + reject(gpgme_error('MSG_INCOMPLETE')); + } + }); + } } diff --git a/lang/js/src/gpgmejs.js b/lang/js/src/gpgmejs.js index c182c175..88a91a60 100644 --- a/lang/js/src/gpgmejs.js +++ b/lang/js/src/gpgmejs.js @@ -18,7 +18,6 @@ * SPDX-License-Identifier: LGPL-2.1+ */ -import {Connection} from "./Connection" import {GPGME_Message, createMessage} from './Message' import {toKeyIdArray} from "./Helpers" import { gpgme_error } from "./Errors" @@ -29,31 +28,20 @@ export class GpgME { * initializes GpgME by opening a nativeMessaging port * TODO: add configuration */ - constructor(connection){ - this.connection = connection; + constructor(config){ //TODO config not parsed + this._config = config; } - set connection(conn){ - if (this._connection instanceof Connection){ - gpgme_error('CONN_ALREADY_CONNECTED'); - } else if (conn instanceof Connection){ - this._connection = conn; - } else { - gpgme_error('PARAM_WRONG'); - } - } - - get connection(){ - return this._connection; - } - - set Keyring(keyring){ + set Keyring(keyring){ if (keyring && keyring instanceof GPGME_Keyring){ this._Keyring = keyring; } } get Keyring(){ + if (!this._Keyring){ + this._Keyring = new GPGME_Keyring; + } return this._Keyring; } @@ -81,7 +69,7 @@ export class GpgME { msg.setParameter('throw-keyids', true); }; if (msg.isComplete === true){ - return this.connection.post(msg); + return msg.post(); } else { return Promise.reject(gpgme_error('MSG_INCOMPLETE')); } @@ -110,7 +98,7 @@ export class GpgME { return Promise.reject(msg); } putData(msg, data); - return this.connection.post(msg); + return msg.post(); } @@ -135,7 +123,7 @@ export class GpgME { } let me = this; return new Promise(function(resolve,reject) { - me.connection.post(msg).then( function(message) { + msg.post().then( function(message) { if (mode === 'clearsign'){ resolve({ data: message.data} @@ -174,20 +162,7 @@ export class GpgME { // TBD } if (msg.isComplete === true){ - this.connection.post(msg).then(function(success){ - // TODO: it seems that there is always errors coming back: - }, function(error){ - switch (error.msg){ - case 'ERR_NO_ERROR': - return Promise.resolve('okay'); //TBD - default: - return Promise.reject(gpgme_error('TODO') ); // - // INV_VALUE, - // GPG_ERR_NO_PUBKEY, - // GPG_ERR_AMBIGUOUS_NAME, - // GPG_ERR_CONFLICT - } - }); + return msg.post(); } else { return Promise.reject(gpgme_error('MSG_INCOMPLETE')); } diff --git a/lang/js/src/index.js b/lang/js/src/index.js index 7f969fee..220a6984 100644 --- a/lang/js/src/index.js +++ b/lang/js/src/index.js @@ -37,7 +37,7 @@ function init(config){ connection.checkConnection(false).then( function(result){ if (result === true) { - resolve(new GpgME(connection, _conf)); + resolve(new GpgME(_conf)); } else { reject(gpgme_error('CONN_NO_CONNECT')); } diff --git a/lang/js/unittests.js b/lang/js/unittests.js index bb06309d..9830a2c5 100644 --- a/lang/js/unittests.js +++ b/lang/js/unittests.js @@ -47,7 +47,6 @@ function unittests (){ expect(answer.info).to.be.an('Array'); expect(conn0.disconnect).to.be.a('function'); expect(conn0.post).to.be.a('function'); - conn0.disconnect(); done(); }); @@ -170,15 +169,11 @@ function unittests (){ describe('GPGME_Key', function(){ it('correct Key initialization', function(){ - let conn = new Connection; - let key = createKey(kp.validKeyFingerprint, conn); + let key = createKey(kp.validKeyFingerprint); expect(key).to.be.an.instanceof(GPGME_Key); - expect(key.connection).to.be.an.instanceof(Connection); - conn.disconnect(); }); it('Key has data after a first refresh', function(done) { - let conn = new Connection; - let key = createKey(kp.validKeyFingerprint, conn); + let key = createKey(kp.validKeyFingerprint); key.refreshKey().then(function(key2){ expect(key2).to.be.an.instanceof(GPGME_Key); expect(key2.get).to.be.a('function'); @@ -192,74 +187,42 @@ function unittests (){ key2.get('fingerprint')).to.equal(kp.validKeyFingerprint); expect( key2.get('fingerprint')).to.equal(key.fingerprint); - conn.disconnect(); done(); }); }); it('Non-cached key async data retrieval', function (done){ - let conn = new Connection; - let key = createKey(kp.validKeyFingerprint, conn); + let key = createKey(kp.validKeyFingerprint); key.get('can_authenticate',false).then(function(result){ expect(result).to.be.a('boolean'); - conn.disconnect(); done(); }); }) it('Querying non-existing Key returns an error', function(done) { - let conn = new Connection; - let key = createKey(kp.invalidKeyFingerprint, conn); + let key = createKey(kp.invalidKeyFingerprint); key.refreshKey().then(function(){}, function(error){ expect(error).to.be.an.instanceof(Error); expect(error.code).to.equal('KEY_NOKEY'); - conn.disconnect(); done(); }); }); - - it('Key can use the connection', function(done){ - let conn = new Connection; - let key = createKey(hp.validFingerprint, conn); - key.connection.checkConnection(false).then(function(result){ - expect(result).to.be.true; - key.connection.disconnect(); - key.connection.checkConnection(false).then(function(result2){ - expect(result2).to.be.false; - conn.disconnect(); - done(); - }); - }); - }); - it('createKey returns error if parameters are wrong', function(){ - let conn = new Connection; for (let i=0; i< 4; i++){ - let key0 = createKey(wp.four_invalid_params[i], conn); - + let key0 = createKey(wp.four_invalid_params[i]); expect(key0).to.be.an.instanceof(Error); expect(key0.code).to.equal('PARAM_WRONG'); } - for (let i=0; i< 4; i++){ - let key0 = createKey( - hp.validFingerprint, wp.four_invalid_params[i]); - - expect(key0).to.be.an.instanceof(Error); - expect(key0.code).to.equal('PARAM_WRONG'); - } - conn.disconnect(); }); it('malformed GPGME_Key cannot be used', function(){ - let conn = new Connection; for (let i=0; i < 4; i++){ - let key = new GPGME_Key(wp.four_invalid_params[i], conn); + let key = new GPGME_Key(wp.four_invalid_params[i]); expect(key.fingerprint).to.be.an.instanceof(Error); expect(key.fingerprint.code).to.equal('KEY_INVALID'); } - conn.disconnect(); }); // TODO: tests for subkeys @@ -269,27 +232,18 @@ function unittests (){ describe('GPGME_Keyring', function(){ - it('correct initialization', function(){ - let conn = new Connection; - let keyring = new GPGME_Keyring(conn); - + it('correct Keyring initialization', function(){ + let keyring = new GPGME_Keyring; expect(keyring).to.be.an.instanceof(GPGME_Keyring); - expect(keyring.connection).to.be.an.instanceof(Connection); expect(keyring.getKeys).to.be.a('function'); expect(keyring.getSubset).to.be.a('function'); }); - it('Keyring should return errors if not connected', function(){ + it('correct initialization', function(){ let keyring = new GPGME_Keyring; expect(keyring).to.be.an.instanceof(GPGME_Keyring); - expect(keyring.connection).to.be.an.instanceof(Error); - expect(keyring.connection.code).to.equal('CONN_NO_CONNECT'); - // not yet implemented: - // keyring.getKeys().then( - // function(result){}, - //function(reject){ - // expect(reject).to.be.an.instanceof(Error); - // done(); + expect(keyring.getKeys).to.be.a('function'); + expect(keyring.getSubset).to.be.a('function'); }); //TODO not yet implemented: // getKeys(pattern, include_secret) //note: pattern can be null