js: use version operation for connection checks
authorMaximilian Krambach <maximilian.krambach@intevation.de>
Fri, 25 May 2018 09:53:24 +0000 (11:53 +0200)
committerMaximilian Krambach <maximilian.krambach@intevation.de>
Fri, 25 May 2018 09:53:24 +0000 (11:53 +0200)
--

* src/Connection.js: isConnected was renamed to checkConnection, that
  returns a promise with either version information or Boolean
* Connection checks have been adapted to reflect that checkConnection
  returns a Promise
* BrowsertestExtension: tests/signTest.js was missing from my last
  commit

lang/js/BrowserTestExtension/tests/signTest.js [new file with mode: 0644]
lang/js/BrowserTestExtension/tests/startup.js
lang/js/src/Connection.js
lang/js/src/Errors.js
lang/js/src/Keyring.js
lang/js/src/gpgmejs.js
lang/js/src/index.js
lang/js/unittests.js

diff --git a/lang/js/BrowserTestExtension/tests/signTest.js b/lang/js/BrowserTestExtension/tests/signTest.js
new file mode 100644 (file)
index 0000000..e332372
--- /dev/null
@@ -0,0 +1,58 @@
+/* gpgme.js - Javascript integration for gpgme
+ * Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
+ *
+ * This file is part of GPGME.
+ *
+ * GPGME is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * GPGME is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+describe('Signing', function () {
+    it('Sign a message', function (done) {
+        let prm = Gpgmejs.init();
+        prm.then(function (context) {
+            let data = bigString(100);
+            context.sign(
+                data,
+                inputvalues.encrypt.good.fingerprint).then(function (answer) {
+                    expect(answer).to.not.be.empty;
+                    expect(answer.data).to.be.a('string');
+                    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();
+            });
+        });
+    });
+    it('Detached sign a message', function (done) {
+        let prm = Gpgmejs.init();
+        prm.then(function (context) {
+            let data = bigString(100);
+            context.sign(
+                data,
+                inputvalues.encrypt.good.fingerprint,
+                'detached'
+            ).then(function (answer) {
+                    expect(answer).to.not.be.empty;
+                    expect(answer.data).to.be.a('string');
+                    expect(answer.data).to.include(data);
+                    expect(answer.signature).to.be.a('string');
+                    expect(answer.signature).to.be.a('string');
+                    context.connection.disconnect();
+                    done();
+            });
+        });
+    });
+
+});
index 5de70a6..ebecf4f 100644 (file)
@@ -30,9 +30,6 @@
             expect(context.encrypt).to.be.a('function');
             expect(context.decrypt).to.be.a('function');
             done();
-        }, function(errorr){
-             expect(error).to.be.undefined;
-             done();
         });
     });
 });
index 9c2a642..07df5de 100644 (file)
@@ -25,7 +25,7 @@
  */
 import { permittedOperations } from './permittedOperations'
 import { gpgme_error } from "./Errors"
-import { GPGME_Message } from "./Message";
+import { GPGME_Message, createMessage } from "./Message";
 
 /**
  * A Connection handles the nativeMessaging interaction.
@@ -34,18 +34,42 @@ export class Connection{
 
     constructor(){
         this.connect();
-        let me = this;
     }
 
     /**
-     * (Simple) Connection check.
-     * @returns {Boolean} true if the onDisconnect event has not been fired.
-     * Please note that the event listener of the port takes some time
-     * (5 ms seems enough) to react after the port is created. Then this will
-     * return undefined
+     * Retrieves the information about the backend.
+     * @param {Boolean} details (optional) If set to false, the promise will
+     *  just return a connection status
+     * @returns {Promise<Object>}
+     *      {String} The property 'gpgme': Version number of gpgme
+     *      {Array<Object>} 'info' Further information about the backends.
+     *      Example:
+     *          "protocol":     "OpenPGP",
+     *          "fname":        "/usr/bin/gpg",
+     *          "version":      "2.2.6",
+     *          "req_version":  "1.4.0",
+     *          "homedir":      "default"
      */
-    get isConnected(){
-        return this._isConnected;
+    checkConnection(details = true){
+        if (details === true) {
+            return this.post(createMessage('version'));
+        } else {
+            let me = this;
+            return new Promise(function(resolve,reject) {
+                Promise.race([
+                    me.post(createMessage('version')),
+                    new Promise(function(resolve, reject){
+                        setTimeout(function(){
+                            reject(gpgme_error('CONN_TIMEOUT'));
+                        }, 500);
+                    })
+                ]).then(function(result){
+                        resolve(true);
+                }, function(reject){
+                    resolve(false);
+                });
+            });
+        }
     }
 
     /**
@@ -54,6 +78,7 @@ export class Connection{
     disconnect() {
         if (this._connection){
             this._connection.disconnect();
+            this._connection = null;
         }
     }
 
@@ -61,17 +86,8 @@ export class Connection{
      * Opens a nativeMessaging port.
      */
     connect(){
-        if (this._isConnected === true){
-            gpgme_error('CONN_ALREADY_CONNECTED');
-        } else {
-            this._isConnected = true;
+        if (!this._connection){
             this._connection = chrome.runtime.connectNative('gpgmejson');
-            let me = this;
-            this._connection.onDisconnect.addListener(
-                function(){
-                    me._isConnected = false;
-                }
-            );
         }
     }
 
@@ -82,8 +98,8 @@ export class Connection{
      * information.
      */
     post(message){
-        if (!this.isConnected){
-            return Promise.reject(gpgme_error('CONN_DISCONNECTED'));
+        if (!this._connection) {
+
         }
         if (!message || !message instanceof GPGME_Message){
             return Promise.reject(gpgme_error('PARAM_WRONG'), message);
@@ -199,7 +215,7 @@ class Answer{
                         if (!this._response.hasOwnProperty(key)){
                             this._response[key] = [];
                         }
-                        this._response.push(msg[key]);
+                        this._response[key].push(msg[key]);
                     }
                     else {
                         return gpgme_error('CONN_UNEXPECTED_ANSWER');
index bfe3a2f..7e98f31 100644 (file)
@@ -25,10 +25,6 @@ const err_list = {
             + ' established.',
         type: 'error'
     },
-    'CONN_DISCONNECTED': {
-        msg:'Connection with the nativeMessaging host was lost.',
-        type: 'error'
-    },
     'CONN_EMPTY_GPG_ANSWER':{
         msg: 'The nativeMessaging answer was empty.',
         type: 'error'
index 4596035..80792f7 100644 (file)
@@ -36,10 +36,7 @@ export class GPGME_Keyring {
     }
     get connection(){
         if (this._connection instanceof Connection){
-            if (this._connection.isConnected){
-                return this._connection;
-            }
-            return gpgme_error('CONN_DISCONNECTED');
+            return this._connection;
         }
         return gpgme_error('CONN_NO_CONNECT');
     }
@@ -51,36 +48,35 @@ export class GPGME_Keyring {
      *
      */
     getKeys(pattern, include_secret){
-        let msg = createMessage('listkeys');
-        if (msg instanceof Error){
-            return Promise.reject(msg);
-        }
-        if (pattern && typeof(pattern) === 'string'){
-            msg.setParameter('pattern', pattern);
-        }
-        if (include_secret){
-            msg.setParameter('with-secret', true);
-        }
         let me = this;
-
-        this.connection.post(msg).then(function(result){
-            let fpr_list = [];
-            let resultset = [];
-            if (!Array.isArray(result.keys)){
-            //TODO check assumption keys = Array<String fingerprints>
-                fpr_list = [result.keys];
-            } else {
-                fpr_list = result.keys;
+        return new Promise(function(resolve, reject) {
+            let msg;
+            msg = createMessage('listkeys');
+            if (pattern && typeof(pattern) === 'string'){
+                msg.setParameter('pattern', pattern);
             }
-            for (let i=0; i < fpr_list.length; i++){
-                let newKey = new GPGME_Key(fpr_list[i], me._connection);
-                if (newKey instanceof GPGME_Key){
-                    resultset.push(newKey);
-                }
+            if (include_secret){
+                msg.setParameter('with-secret', true);
             }
-            return Promise.resolve(resultset);
-        }, function(error){
-            //TODO error handling
+            me.connection.post(msg).then(function(result){
+                let fpr_list = [];
+                let resultset = [];
+                if (!Array.isArray(result.keys)){
+                //TODO check assumption keys = Array<String fingerprints>
+                    fpr_list = [result.keys];
+                } else {
+                    fpr_list = result.keys;
+                }
+                for (let i=0; i < fpr_list.length; i++){
+                    let newKey = new GPGME_Key(fpr_list[i], me._connection);
+                    if (newKey instanceof GPGME_Key){
+                        resultset.push(newKey);
+                    }
+                }
+                resolve(resultset);
+            }, function(error){
+                reject(error);
+            });
         });
     }
 
index 1e76655..c182c17 100644 (file)
@@ -44,13 +44,7 @@ export class GpgME {
     }
 
     get connection(){
-        if (this._connection){
-            if (this._connection.isConnected === true){
-                return this._connection;
-            }
-            return undefined;
-        }
-        return undefined;
+        return this._connection;
     }
 
     set Keyring(keyring){
index 8527b3f..7f969fe 100644 (file)
@@ -34,20 +34,16 @@ function init(config){
     }
     return new Promise(function(resolve, reject){
         let connection = new Connection;
-        // TODO: Delayed reaction is ugly. We need to listen to the port's
-        // event listener in isConnected, but in some cases this takes some
-        // time (<5ms) to disconnect if there is no successfull connection.
-        let delayedreaction = function(){
-            if (connection === undefined) {
+        connection.checkConnection(false).then(
+            function(result){
+                if (result === true) {
+                    resolve(new GpgME(connection, _conf));
+                } else {
+                    reject(gpgme_error('CONN_NO_CONNECT'));
+                }
+            }, function(error){
                 reject(gpgme_error('CONN_NO_CONNECT'));
-            }
-            if (connection.isConnected === true){
-                resolve(new GpgME(connection, _conf));
-            } else {
-                reject(gpgme_error('CONN_NO_CONNECT'));
-            }
-        };
-        setTimeout(delayedreaction, 5);
+        });
     });
 }
 
index c437d59..06b2b23 100644 (file)
@@ -39,34 +39,29 @@ function unittests (){
 
         it('Connecting', function(done) {
             let conn0 = new Connection;
-            let delayed = function(){
-                expect(conn0.isConnected).to.be.true;
-                expect(conn0.connect).to.be.a('function');
+            conn0.checkConnection().then(function(answer) {
+                expect(answer).to.not.be.empty;
+                expect(answer.gpgme).to.not.be.undefined;
+                expect(answer.gpgme).to.be.a('string');
+                expect(answer.info).to.be.an('Array');
                 expect(conn0.disconnect).to.be.a('function');
                 expect(conn0.post).to.be.a('function');
                 done();
-            };
-            setTimeout(delayed, 5);
+            });
 
         });
 
         it('Disconnecting', function(done) {
             let conn0 = new Connection;
-            let delayed = function(){
-                conn0.disconnect(); // TODO fails!
-                expect(conn0.isConnected).to.be.false;
-                done();
-            };
-            setTimeout(delayed, 5);
+            conn0.checkConnection(false).then(function(answer) {
+                expect(answer).to.be.true;
+                conn0.disconnect();
+                conn0.checkConnection(false).then(function(result) {
+                    expect(result).to.be.false;
+                    done();
+                });
+            });
         });
-
-        // broken
-        // it('Connect info still only available after a delay', function(done){
-        //     // if false, all delayed connections can be refactored
-        //     let conn0 = new Connection;
-        //     expect(conn0.isConnected).to.be.undefined;
-        //  //
-        // })
     });
 
     describe('Error Object handling', function(){
@@ -181,14 +176,17 @@ function unittests (){
             // TODO not implemented yet: Further Key functionality
         });
 
-        it('Key can use the connection', function(){
+        it('Key can use the connection', function(done){
             let conn = new Connection;
             let key = createKey(hp.validFingerprint, conn);
-
-            expect(key.connection.isConnected).to.be.true;
-
-            key.connection.disconnect();
-            expect(key.connection.isConnected).to.be.false;
+            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;
+                    done();
+                });
+            });
         });
 
         it('createKey returns error if parameters are wrong', function(){
@@ -232,12 +230,15 @@ function unittests (){
 
         it('Keyring should return errors if not connected', 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');
-            expect(keyring.getKeys).to.be.an.instanceof(Error);
-            expect(keyring.getkeys.code).to.equal('CONN_NO_CONNECT');
+            // not yet implemented:
+            // keyring.getKeys().then(
+                // function(result){},
+                //function(reject){
+                    // expect(reject).to.be.an.instanceof(Error);
+                    // done();
         });
             //TODO not yet implemented:
             //  getKeys(pattern, include_secret) //note: pattern can be null