js: change in Error behaviour

--

* Error objects will now return the error code if defined as error type
  in src/Errors.js, or do a console.log if it is a warning. Errors from
  the native gpgme-json will be marked as GNUPG_ERROR.
This commit is contained in:
Maximilian Krambach 2018-04-25 10:54:24 +02:00
parent 30c47d80a2
commit c72adc0096
8 changed files with 132 additions and 156 deletions

View File

@ -1,5 +1,3 @@
import { GPGME_Message } from "./Message";
/* gpgme.js - Javascript integration for gpgme /* gpgme.js - Javascript integration for gpgme
* Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik * Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
* *
@ -27,6 +25,7 @@ import { GPGME_Message } from "./Message";
*/ */
import { permittedOperations } from './permittedOperations' import { permittedOperations } from './permittedOperations'
import { GPGMEJS_Error } from "./Errors" import { GPGMEJS_Error } from "./Errors"
import { GPGME_Message } from "./Message";
/** /**
* A Connection handles the nativeMessaging interaction. * A Connection handles the nativeMessaging interaction.
@ -60,12 +59,11 @@ export class Connection{
/** /**
* Opens a nativeMessaging port. * Opens a nativeMessaging port.
* TODO: Error handling ALREADY_CONNECTED
*/ */
connect(){ connect(){
if (this._isConnected === true){ if (this._isConnected === true){
return new GPGMEJS_Error('ALREADY_CONNECTED'); GPGMEJS_Error('CONN_ALREADY_CONNECTED');
} } else {
this._isConnected = true; this._isConnected = true;
this._connection = chrome.runtime.connectNative('gpgmejson'); this._connection = chrome.runtime.connectNative('gpgmejson');
let me = this; let me = this;
@ -75,6 +73,7 @@ export class Connection{
} }
); );
} }
}
/** /**
* Sends a message and resolves with the answer. * Sends a message and resolves with the answer.
@ -84,28 +83,31 @@ export class Connection{
*/ */
post(message){ post(message){
if (!this.isConnected){ if (!this.isConnected){
return Promise.reject(new GPGMEJS_Error('NO_CONNECT')); return Promise.reject(GPGMEJS_Error('CONN_NO_CONNECT'));
} }
if (!message || !message instanceof GPGME_Message){ if (!message || !message instanceof GPGME_Message){
return Promise.reject(new GPGMEJS_Error('WRONGPARAM')); return Promise.reject(GPGMEJS_Error('PARAM_WRONG'), message);
} }
if (message.isComplete !== true){ if (message.isComplete !== true){
return Promise.reject(new GPGMEJS_Error('MSG_INCOMPLETE')); return Promise.reject(GPGMEJS_Error('MSG_INCOMPLETE'));
} }
// let timeout = 5000; //TODO config
let me = this; let me = this;
return new Promise(function(resolve, reject){ return new Promise(function(resolve, reject){
let answer = new Answer(message.operation); let answer = new Answer(message.operation);
let listener = function(msg) { let listener = function(msg) {
if (!msg){ if (!msg){
me._connection.onMessage.removeListener(listener) me._connection.onMessage.removeListener(listener)
reject(new GPGMEJS_Error('EMPTY_GPG_ANSWER')); reject(GPGMEJS_Error('CONN_EMPTY_GPG_ANSWER'));
} else if (msg.type === "error"){ } else if (msg.type === "error"){
me._connection.onMessage.removeListener(listener) me._connection.onMessage.removeListener(listener)
//TODO: GPGMEJS_Error? reject(
reject(msg.msg); {code: 'GNUPG_ERROR',
msg: msg.msg} );
} else { } else {
answer.add(msg); let answer_result = answer.add(msg);
if (answer_result !== true){
reject(answer_result);
}
if (msg.more === true){ if (msg.more === true){
me._connection.postMessage({'op': 'getmore'}); me._connection.postMessage({'op': 'getmore'});
} else { } else {
@ -117,11 +119,12 @@ export class Connection{
me._connection.onMessage.addListener(listener); me._connection.onMessage.addListener(listener);
me._connection.postMessage(message.message); me._connection.postMessage(message.message);
//TBD: needs to be aware if there is a pinentry pending //TBD: needs to be aware if there is a pinentry pending
// setTimeout( // setTimeout(
// function(){ // function(){
// me.disconnect(); // me.disconnect();
// reject(new GPGMEJS_Error('TIMEOUT', 5000)); // reject(GPGMEJS_Error('CONN_TIMEOUT'));
// }, timeout); // }, timeout);
}); });
} }
@ -141,6 +144,7 @@ class Answer{
/** /**
* Add the information to the answer * Add the information to the answer
* @param {Object} msg The message as received with nativeMessaging * @param {Object} msg The message as received with nativeMessaging
* returns true if successfull, GPGMEJS_Error otherwise
*/ */
add(msg){ add(msg){
if (this._response === undefined){ if (this._response === undefined){
@ -148,12 +152,15 @@ class Answer{
} }
let messageKeys = Object.keys(msg); let messageKeys = Object.keys(msg);
let poa = permittedOperations[this.operation].answer; let poa = permittedOperations[this.operation].answer;
if (messageKeys.length === 0){
return GPGMEJS_Error('CONN_UNEXPECTED_ANSWER');
}
for (let i= 0; i < messageKeys.length; i++){ for (let i= 0; i < messageKeys.length; i++){
let key = messageKeys[i]; let key = messageKeys[i];
switch (key) { switch (key) {
case 'type': case 'type':
if ( msg.type !== 'error' && poa.type.indexOf(msg.type) < 0){ if ( msg.type !== 'error' && poa.type.indexOf(msg.type) < 0){
return new GPGMEJS_Error('UNEXPECTED_ANSWER'); return GPGMEJS_Error('CONN_UNEXPECTED_ANSWER');
} }
break; break;
case 'more': case 'more':
@ -172,7 +179,7 @@ class Answer{
this._response[key] = msg[key]; this._response[key] = msg[key];
} }
else if (this._response[key] !== msg[key]){ else if (this._response[key] !== msg[key]){
return new GPGMEJS_Error('UNEXPECTED_ANSWER',msg[key]); return GPGMEJS_Error('CONN_UNEXPECTED_ANSWER',msg[key]);
} }
} }
//infos may be json objects etc. Not yet defined. //infos may be json objects etc. Not yet defined.
@ -184,11 +191,12 @@ class Answer{
this._response.push(msg[key]); this._response.push(msg[key]);
} }
else { else {
return new GPGMEJS_Error('UNEXPECTED_ANSWER', key); return GPGMEJS_Error('CONN_UNEXPECTED_ANSWER', key);
} }
break; break;
} }
} }
return true;
} }
/** /**

View File

@ -18,131 +18,99 @@
* SPDX-License-Identifier: LGPL-2.1+ * SPDX-License-Identifier: LGPL-2.1+
*/ */
// This is a preliminary collection of erors and warnings to be thrown and implemented. /**
* Checks the given error code and returns some information about it's meaning
// general idea: if throw , throw the NAME * @param {String} code The error code
// return false || 'return' property * @returns {Object} An object containing string properties code and msg
* TODO: error-like objects with the code 'GNUPG_ERROR' are errors sent
//TODO: Connection.NOCONNECT promise * directly by gnupg as answer in Connection.post()
//connection.timeout: Be aware of pinentry */
export function GPGMEJS_Error(code = 'GENERIC_ERROR'){
export class GPGMEJS_Error { if (!typeof(code) === 'string'){
code = 'GENERIC_ERROR';
constructor(code = 'GENERIC_ERROR', details){ }
let config = { //TODO TEMP
debug: 'console', // |'alert'
throw: 'default' // | 'always' | 'never'
};
let errors = { //TODO: someplace else let errors = { //TODO: someplace else
//Connection errors // Connection
'ALREADY_CONNECTED':{ 'CONN_NO_CONNECT': {
msg: 'The connection was already established. The action would overwrite the context', msg:'Connection with the nativeMessaging host could not be'
throw: true + ' established.',
type: 'error'
}, },
'NO_CONNECT': { 'CONN_EMPTY_GPG_ANSWER':{
msg:'Connection with the nativeMessaging host could not be established.', msg: 'The nativeMessaging answer was empty.',
throw: true type: 'error'
}, },
'EMPTY_GPG_ANSWER':{ 'CONN_TIMEOUT': {
msg: 'The nativeMesaging answer was empty', msg: 'A connection timeout was exceeded.',
throw: true type: 'error'
}, },
'TIMEOUT': { 'CONN_UNEXPECTED_ANSWER': {
msg: 'A timeout was exceeded.', msg: 'The answer from gnupg was not as expected.',
throw: false type: 'error'
}, },
'CONN_ALREADY_CONNECTED':{
'UNEXPECTED_ANSWER': { msg: 'A connection was already established.',
msg: 'The answer from gnupg was not as expected', type: 'warn'
throw: true
},
// Message/Data Errors
'NO_KEYS' : {
msg: 'There were no valid keys provided.',
throw: true
},
'NOT_A_FPR': {
msg: 'The String is not an accepted fingerprint',
throw: false
}, },
// Message/Data
'MSG_INCOMPLETE': { 'MSG_INCOMPLETE': {
msg: 'The Message did not match the minimum requirements for the interaction', msg: 'The Message did not match the minimum requirements for'
throw: true + ' the interaction.',
type: 'error'
}, },
'EMPTY_MSG' : { 'MSG_EMPTY' : {
msg: 'The Message has no data.', msg: 'The Message is empty.',
throw: true type: 'error'
},
'MSG_NODATA':{
msg: 'The data sent is empty. This may be unintentional.',
throw: false
}, },
'MSG_OP_PENDING': { 'MSG_OP_PENDING': {
msg: 'There is no operation specified yet. The parameter cannot be set', msg: 'There is no operation specified yet. The parameter cannot'
throw: false + ' be set',
type: 'warning'
}, },
'WRONG_OP': { 'MSG_WRONG_OP': {
msg: "The operation requested could not be found", msg: 'The operation requested could not be found',
throw: true type: 'warning'
},
'MSG_NO_KEYS' : {
msg: 'There were no valid keys provided.',
type: 'warn'
},
'MSG_NOT_A_FPR': {
msg: 'The String is not an accepted fingerprint',
type: 'warn'
}, },
//generic errors // generic
'PARAM_WRONG':{
'WRONGPARAM':{
msg: 'invalid parameter was found', msg: 'invalid parameter was found',
throw: true type: 'error'
},
'WRONGTYPE':{
msg: 'invalid parameter type was found',
throw: true
}, },
'NOT_IMPLEMENTED': { 'NOT_IMPLEMENTED': {
msg: 'A openpgpjs parameter was submitted that is not implemented', msg: 'A openpgpjs parameter was submitted that is not implemented',
throw: true type: 'error'
},
'NOT_YET_IMPLEMENTED': {
msg: 'Support of this is probable, but it is not implemented yet',
type: 'error'
}, },
'GENERIC_ERROR': { 'GENERIC_ERROR': {
msg: 'Unspecified error', msg: 'Unspecified error',
throw: true type: 'error'
}, },
// hopefully temporary errors
'NOT_YET_IMPLEMENTED': {
msg: 'Support of this is probable, but it is not implemented yet',
throw: false
} }
if (code === 'TODO'){
alert('TODO_Error!');
} }
if (!errors.hasOwnProperty(code)){ if (errors.hasOwnProperty(code)){
throw('GENERIC_ERROR'); code = 'GENERIC_ERROR';
} }
let msg = code; if (error.type === 'error'){
if (errors[code].msg !== undefined){ return {code: 'code',
msg = msg + ': ' + errors[code].msg; msg: errors[code].msg
} };
if (details){
msg = msg + ' ' + details;
}
if (config.debug === 'console'){
console.log(msg);
} else if (config.debug === 'alert'){
alert(msg);
}
switch (config.throw) {
case 'default':
if (errors[code].throw === true){
throw(code);
}
break;
case 'always':
throw(code);
break;
case 'never':
break;
default:
throw('GENERIC_ERROR');
} }
if (error.type === 'warning'){
console.log(code + ': ' + error[code].msg);
} }
return undefined;
} }

View File

@ -1,5 +1,3 @@
import { GPGMEJS_Error } from "./Errors";
/* gpgme.js - Javascript integration for gpgme /* gpgme.js - Javascript integration for gpgme
* Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik * Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
* *
@ -19,18 +17,19 @@ import { GPGMEJS_Error } from "./Errors";
* License along with this program; if not, see <http://www.gnu.org/licenses/>. * License along with this program; if not, see <http://www.gnu.org/licenses/>.
* SPDX-License-Identifier: LGPL-2.1+ * SPDX-License-Identifier: LGPL-2.1+
*/ */
import { GPGMEJS_Error } from "./Errors";
/** /**
* Tries to return an array of fingerprints, either from input fingerprints or * Tries to return an array of fingerprints, either from input fingerprints or
* from Key objects * from Key objects
* @param {Key |Array<Key>| GPGME_Key | Array<GPGME_Key>|String|Array<String>} input * @param {Key |Array<Key>| GPGME_Key | Array<GPGME_Key>|String|Array<String>} input
* @param {Boolean} nocheck if set, an empty result is acceptable
* @returns {Array<String>} Array of fingerprints. * @returns {Array<String>} Array of fingerprints.
*/ */
export function toKeyIdArray(input, nocheck){ export function toKeyIdArray(input, nocheck){
if (!input){ if (!input){
return (nocheck ===true)? [] : new GPGMEJS_Error('NO_KEYS'); GPGMEJS_Error('MSG_NO_KEYS');
return [];
} }
if (!Array.isArray(input)){ if (!Array.isArray(input)){
input = [input]; input = [input];
@ -41,7 +40,7 @@ export function toKeyIdArray(input, nocheck){
if (isFingerprint(input[i]) === true){ if (isFingerprint(input[i]) === true){
result.push(input[i]); result.push(input[i]);
} else { } else {
GPGMEJS_Error GPGMEJS_Error('MSG_NOT_A_FPR');
} }
} else if (typeof(input[i]) === 'object'){ } else if (typeof(input[i]) === 'object'){
let fpr = ''; let fpr = '';
@ -53,13 +52,16 @@ export function toKeyIdArray(input, nocheck){
} }
if (isFingerprint(fpr) === true){ if (isFingerprint(fpr) === true){
result.push(fpr); result.push(fpr);
} else {
GPGMEJS_Error('MSG_NOT_A_FPR');
} }
} else { } else {
return new GPGMEJS_Error('WRONGTYPE'); return GPGMEJS_Error('PARAM_WRONG');
} }
} }
if (result.length === 0){ if (result.length === 0){
return (nocheck===true)? [] : new GPGMEJS_Error('NO_KEYS'); GPGMEJS_Error('MSG_NO_KEYS');
return [];
} else { } else {
return result; return result;
} }

View File

@ -172,7 +172,7 @@ export class GPGME_Key {
* *
*/ */
function checkKey(fingerprint, property){ function checkKey(fingerprint, property){
return Promise.reject(new GPGMEJS_Error('NOT_YET_IMPLEMENTED')); return Promise.reject(GPGMEJS_Error('NOT_YET_IMPLEMENTED'));
return new Promise(function(resolve, reject){ return new Promise(function(resolve, reject){
if (!isFingerprint(fingerprint)){ if (!isFingerprint(fingerprint)){

View File

@ -39,20 +39,20 @@ export class GPGME_Message {
*/ */
setParameter(param,value){ setParameter(param,value){
if (!param || typeof(param) !== 'string'){ if (!param || typeof(param) !== 'string'){
return new GPGMEJS_Error('WRONGPARAM', 'type check failed'); return GPGMEJS_Error('PARAM_WRONG');
} }
if (!this._msg || !this._msg.op){ if (!this._msg || !this._msg.op){
return new GPGMEJS_Error('MSG_OP_PENDING'); return GPGMEJS_Error('MSG_OP_PENDING');
} }
let po = permittedOperations[this._msg.op]; let po = permittedOperations[this._msg.op];
if (!po){ if (!po){
return new GPGMEJS_Error('WRONG_OP', param); return GPGMEJS_Error('MSG_WRONG_OP');
} }
if (po.required.indexOf(param) >= 0 || po.optional.indexOf(param) >= 0){ if (po.required.indexOf(param) >= 0 || po.optional.indexOf(param) >= 0){
this._msg[param] = value; this._msg[param] = value;
return true; return true;
} }
return new GPGMEJS_Error('WRONGPARAM', param); return GPGMEJS_Error('PARAM_WRONG');
} }
/** /**
@ -98,7 +98,7 @@ export class GPGME_Message {
*/ */
function setOperation (scope, operation){ function setOperation (scope, operation){
if (!operation || typeof(operation) !== 'string'){ if (!operation || typeof(operation) !== 'string'){
return new GPGMEJS_Error('WRONGTYPE'); return GPGMEJS_Error('PARAM_WRONG');
} }
if (permittedOperations.hasOwnProperty(operation)){ if (permittedOperations.hasOwnProperty(operation)){
if (!scope._msg){ if (!scope._msg){
@ -106,6 +106,6 @@ function setOperation (scope, operation){
} }
scope._msg.op = operation; scope._msg.op = operation;
} else { } else {
return new GPGMEJS_Error('WRONG_OP'); return GPGMEJS_Error('MSG_WRONG_OP');
} }
} }

View File

@ -95,9 +95,8 @@ export class GpgME {
*/ */
decrypt(data){ decrypt(data){
if (data === undefined){ if (data === undefined){
return Promise.reject(new GPGMEJS_Error ('EMPTY_MSG')); return Promise.reject(GPGMEJS_Error('MSG_EMPTY'));
} }
let msg = new GPGME_Message('decrypt'); let msg = new GPGME_Message('decrypt');
putData(msg, data); putData(msg, data);
@ -106,7 +105,7 @@ export class GpgME {
} }
deleteKey(key, delete_secret = false, no_confirm = false){ deleteKey(key, delete_secret = false, no_confirm = false){
return Promise.reject(new GPGMEJS_Error ('NOT_YET_IMPLEMENTED')); return Promise.reject(GPGMEJS_Error('NOT_YET_IMPLEMENTED'));
let msg = new GPGME_Message('deletekey'); let msg = new GPGME_Message('deletekey');
let key_arr = toKeyIdArray(key); let key_arr = toKeyIdArray(key);
if (key_arr.length !== 1){ if (key_arr.length !== 1){
@ -127,7 +126,7 @@ export class GpgME {
case 'ERR_NO_ERROR': case 'ERR_NO_ERROR':
return Promise.resolve('okay'); //TBD return Promise.resolve('okay'); //TBD
default: default:
return Promise.reject(new GPGMEJS_Error); return Promise.reject(GPGMEJS_Error('TODO') ); //
// INV_VALUE, // INV_VALUE,
// GPG_ERR_NO_PUBKEY, // GPG_ERR_NO_PUBKEY,
// GPG_ERR_AMBIGUOUS_NAME, // GPG_ERR_AMBIGUOUS_NAME,
@ -146,11 +145,9 @@ export class GpgME {
*/ */
function putData(message, data){ function putData(message, data){
if (!message || !message instanceof GPGME_Message ) { if (!message || !message instanceof GPGME_Message ) {
return new GPGMEJS_Error('WRONGPARAM'); return GPGMEJS_Error('PARAM_WRONG');
} }
if (!data){ if (!data){
//TODO Debug only! No data is legitimate
console.log('Warning. no data in message');
message.setParameter('data', ''); message.setParameter('data', '');
} else if (data instanceof Uint8Array){ } else if (data instanceof Uint8Array){
let decoder = new TextDecoder('utf8'); let decoder = new TextDecoder('utf8');
@ -167,6 +164,6 @@ function putData(message, data){
message.setParameter ('data', decoder.decode(txt)); message.setParameter ('data', decoder.decode(txt));
} }
} else { } else {
return new GPGMEJS_Error('WRONGPARAM'); return GPGMEJS_Error('PARAM_WRONG');
} }
} }

View File

@ -88,14 +88,14 @@
|| signature !== null || signature !== null
|| returnSessionKey !== null || returnSessionKey !== null
|| date !== null){ || date !== null){
return Promise.reject(new GPMGEJS_Error('NOT_IMPLEMENTED')); return Promise.reject(GPMGEJS_Error('NOT_IMPLEMENTED'));
} }
if ( privateKeys if ( privateKeys
|| filename || filename
|| compression || compression
|| armor === false || armor === false
|| detached == true){ || detached == true){
return Promise.reject(new GPGMEJS_Error('NOT_YET_IMPLEMENTED')); return Promise.reject(GPGMEJS_Error('NOT_YET_IMPLEMENTED'));
} }
return this.GpgME.encrypt(data, translateKeyInput(publicKeys), wildcard); return this.GpgME.encrypt(data, translateKeyInput(publicKeys), wildcard);
} }
@ -123,14 +123,14 @@
if (passwords !== undefined if (passwords !== undefined
|| sessionKeys || sessionKeys
|| date){ || date){
return Promise.reject(new GPGMEJS_Error('NOT_IMPLEMENTED')); return Promise.reject(GPGMEJS_Error('NOT_IMPLEMENTED'));
} }
if ( privateKeys if ( privateKeys
|| publicKeys || publicKeys
|| format !== 'utf8' || format !== 'utf8'
|| signature || signature
){ ){
return Promise.reject(new GPGMEJS_Error('NOT_YET_IMPLEMENTED')); return Promise.reject(GPGMEJS_Error('NOT_YET_IMPLEMENTED'));
} }
return this.GpgME.decrypt(message); return this.GpgME.decrypt(message);
// TODO: translate between: // TODO: translate between:
@ -185,7 +185,7 @@ class GPGME_Keyring_openpgpmode {
else { else {
// TODO: Can there be "no default key"? // TODO: Can there be "no default key"?
// TODO: Can there be several default keys? // TODO: Can there be several default keys?
return new GPGMEJS_Error; //TODO return GPGMEJS_Error('TODO');
} }
}); });
} }
@ -202,10 +202,10 @@ class GPGME_Keyring_openpgpmode {
*/ */
deleteKey(key){ deleteKey(key){
if (typeof(key) !== "object"){ if (typeof(key) !== "object"){
return Promise.reject(new GPGMEJS_Error('WRONGPARAM')); return Promise.reject(GPGMEJS_Error('PARAM_WRONG'));
} }
if ( !key.fingerprint || ! isFingerprint(key.fingerprint)){ if ( !key.fingerprint || ! isFingerprint(key.fingerprint)){
return Promise.reject(new GPGMEJS_Error('WRONGPARAM')); return Promise.reject(GPGMEJS_Error('PARAM_WRONG'));
} }
let key_to_delete = new GPGME_Key(key.fingerprint); let key_to_delete = new GPGME_Key(key.fingerprint);
return key_to_delete.deleteKey(key.secret); return key_to_delete.deleteKey(key.secret);

View File

@ -19,6 +19,7 @@
*/ */
import { GpgME } from "./gpgmejs"; import { GpgME } from "./gpgmejs";
import { GPGMEJS_Error } from "./Errors";
import { GpgME_openpgpmode } from "./gpgmejs_openpgpjs"; import { GpgME_openpgpmode } from "./gpgmejs_openpgpjs";
import { Connection } from "./Connection"; import { Connection } from "./Connection";
@ -45,7 +46,7 @@ function init( config = {
resolve(new GpgME(connection)); resolve(new GpgME(connection));
} }
} else { } else {
reject('NO_CONNECT'); reject(GPGMEJS_Error('CONN_NO_CONNECT'));
} }
}; };
setTimeout(delayedreaction, 5); setTimeout(delayedreaction, 5);