js: use version operation for connection checks

--

* 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
This commit is contained in:
Maximilian Krambach 2018-05-25 11:53:24 +02:00
parent a4ba80c553
commit eff27d6387
8 changed files with 162 additions and 108 deletions

View File

@ -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();
});
});
});
});

View 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();
});
});
});

View 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');

View 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'

View 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);
}
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;
}
}
return Promise.resolve(resultset);
}, function(error){
//TODO error handling
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);
});
});
}

View 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){

View 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);
});
});
}

View 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