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
This commit is contained in:
Maximilian Krambach 2018-05-28 16:52:50 +02:00
parent 7a73d88aba
commit d4adbf453d
12 changed files with 59 additions and 171 deletions

View File

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

View File

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

View File

@ -29,7 +29,6 @@ describe('Long running Encryption/Decryption', function () {
}
}
expect(result.data).to.equal(data);
context.connection.disconnect();
done();
});
});

View File

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

View File

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

View File

@ -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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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