From d62f66b1fb47f2075770d896f672748a4136e70b Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Mon, 23 Apr 2018 17:18:46 +0200 Subject: js: Key handling stubs, Error handling, refactoring -- * Error handling: introduced GPGMEJS_Error class that handles errors at a more centralized and consistent position * src/Connection.js: The nativeMessaging port now opens per session instead of per message. Some methods were added that reflect this change - added methods disconnect() and reconnect() - added connection status query * src/gpgmejs.js - stub for key deletion - error handling - high level API for changing connection status * src/gpgmejs_openpgpjs.js - added stubs for Key/Keyring handling according to current state of discussion. It is still subject to change * src/Helpers.js - toKeyIdArray creates an array of KeyIds, now accepting fingerprints, GPGMEJS_Key objects and openpgp Key objects. * Key objects (src/Key.js) Querying information about a key directly from gnupg. Currently a stub, only the Key.fingerprint is functional. * Keyring queries (src/Keyring.js): Listing and searching keys. Currently a stub. --- lang/js/src/Key.js | 201 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 lang/js/src/Key.js (limited to 'lang/js/src/Key.js') diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js new file mode 100644 index 00000000..d8f16c55 --- /dev/null +++ b/lang/js/src/Key.js @@ -0,0 +1,201 @@ +/* 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 . + * SPDX-License-Identifier: LGPL-2.1+ + */ + +/** + * The key class allows to query the information defined in gpgme Key Objects + * (see https://www.gnupg.org/documentation/manuals/gpgme/Key-objects.html) + * + * This is a stub, as the gpgme-json side is not yet implemented + * + */ + +import {isFingerprint} from './Helpers' +import {GPGMEJS_Error} from './Errors' + +export class GPGME_Key { + + constructor(fingerprint){ + if (isFingerprint(fingerprint) === true){ + this._fingerprint = fingerprint; + } else { + return new GPGMEJS_Error('WRONGPARAM', 'Key.js: invalid fingerprint'); + } + } + + get fingerprint(){ + return this._fingerprint; + } + + /** + * hasSecret returns true if a secret subkey is included in this Key + */ + get hasSecret(){ + checkKey(this._fingerprint, 'secret').then( function(result){ + return Promise.resolve(result); + }); + + } + + get isRevoked(){ + return checkKey(this._fingerprint, 'revoked'); + } + + get isExpired(){ + return checkKey(this._fingerprint, 'expired'); + } + + get isDisabled(){ + return checkKey(this._fingerprint, 'disabled'); + } + + get isInvalid(){ + return checkKey(this._fingerprint, 'invalid'); + } + + get canEncrypt(){ + return checkKey(this._fingerprint, 'can_encrypt'); + } + + get canSign(){ + return checkKey(this._fingerprint, 'can_sign'); + } + + get canCertify(){ + return checkKey(this._fingerprint, 'can_certify'); + } + + get canAuthenticate(){ + return checkKey(this._fingerprint, 'can_authenticate'); + } + + get isQualified(){ + return checkKey(this._fingerprint, 'is_qualified'); + } + + get armored(){ + let me = this; + return new Promise(function(resolve, reject){ + let conn = new Connection(); + conn.setFlag('armor', true); + conn.post('export',{'fpr': me._fingerprint}); + }); + // TODO return value not yet checked. Should result in an armored block + // in correct encoding + // TODO openpgpjs also returns secKey if private = true? + } + + /** + * TODO returns true if this is the default key used to sign + */ + get isDefault(){ + throw('NOT_YET_IMPLEMENTED'); + } + + /** + * get the Key's subkeys as GPGME_Key objects + * @returns {Array} + */ + get subkeys(){ + return checkKey(this._fingerprint, 'subkeys').then(function(result){ + // TBD expecting a list of fingerprints + if (!Array.isArray(result)){ + result = [result]; + } + let resultset = []; + for (let i=0; i < result.length; i++){ + let subkey = new GPGME_Key(result[i]); + if (subkey instanceof GPGME_Key){ + resultset.push(subkey); + } + } + return Promise.resolve(resultset); + }); + } + + /** + * creation time stamp of the key + * @returns {Date|null} TBD + */ + get timestamp(){ + return checkKey(this._fingerprint, 'timestamp'); + //TODO GPGME: -1 if the timestamp is invalid, and 0 if it is not available. + } + + /** + * The expiration timestamp of this key TBD + * @returns {Date|null} TBD + */ + get expires(){ + return checkKey(this._fingerprint, 'expires'); + // TODO convert to Date; check for 0 + } + + /** + * getter name TBD + * @returns {String|Array} The user ids associated with this key + */ + get userIds(){ + return checkKey(this._fingerprint, 'uids'); + } + + /** + * @returns {String} The public key algorithm supported by this subkey + */ + get pubkey_algo(){ + return checkKey(this._fingerprint, 'pubkey_algo'); + } +}; + +/** + * generic function to query gnupg information on a key. + * @param {*} fingerprint The identifier of the Keyring + * @param {*} property The gpgme-json property to check + * + */ +function checkKey(fingerprint, property){ + return Promise.reject(new GPGMEJS_Error('NOT_YET_IMPLEMENTED')); + + return new Promise(function(resolve, reject){ + if (!isFingerprint(fingerprint)){ + reject('not a fingerprint'); //TBD + } + let conn = new Connection(); + conn.post('getkey',{ // TODO not yet implemented in gpgme + 'fingerprint': this.fingerprint}) + .then(function(result){ + if (property !== undefined){ + if (result.hasOwnProperty(key)){ + resolve(result[property]); + } + else if (property == 'secret'){ + // property undefined means "not true" in case of secret + resolve(false); + } else { + reject('ERR_INVALID_PROPERTY') //TBD + } + } + + + resolve(result); + }, function(error){ + reject(error); + }); + }); +}; \ No newline at end of file -- cgit v1.2.3 From e2aa8066a9b3ce694169ad9fcc26cae486a804af Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Tue, 24 Apr 2018 19:29:32 +0200 Subject: js: Key object adjustments after discussion -- * src/aKey.js changed fingerprint to setter (to avoid overwrites) * src/gpgmejs_openpgpjs.js - Added a class GPGME_Key_openpgpmode, which allows for renaming and deviation from GPGME. - renamed classes *_openPGPCompatibility to *_openpgpmode. They are not fully compatible, but only offer a subset of properties. Also, the name seems less clunky --- lang/js/src/Key.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'lang/js/src/Key.js') diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js index d8f16c55..f59b9901 100644 --- a/lang/js/src/Key.js +++ b/lang/js/src/Key.js @@ -32,10 +32,12 @@ import {GPGMEJS_Error} from './Errors' export class GPGME_Key { constructor(fingerprint){ - if (isFingerprint(fingerprint) === true){ + this.fingerprint = fingerprint; + } + + set fingerprint(fpr){ + if (isFingerprint(fpr) === true && !this._fingerprint){ this._fingerprint = fingerprint; - } else { - return new GPGMEJS_Error('WRONGPARAM', 'Key.js: invalid fingerprint'); } } -- cgit v1.2.3 From c72adc00965fe4fcedd9d18609211021a091b28b Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Wed, 25 Apr 2018 10:54:24 +0200 Subject: 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. --- lang/js/src/Key.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lang/js/src/Key.js') diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js index f59b9901..5ae80438 100644 --- a/lang/js/src/Key.js +++ b/lang/js/src/Key.js @@ -172,7 +172,7 @@ export class GPGME_Key { * */ 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){ if (!isFingerprint(fingerprint)){ -- cgit v1.2.3 From 1fb310cabe578625f96fce5d84ff6f0092c08d24 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Wed, 25 Apr 2018 15:59:36 +0200 Subject: js: Configuration and Error handling -- * gpgmejs_openpgpjs - unsuported values with no negative consequences can now reject, warn or be ignored, according to config.unconsidered_params - cleanup of unsupported/supported parameters and TODOS * A src/index.js init() now accepts a configuration object * Errors will now be derived from Error, offering more info and a stacktrace. * Fixed Connection.post() timeout triggering on wrong cases * Added comments in permittedOperations.js, which gpgme interactions are still unimplemented and should be added next --- lang/js/src/Key.js | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) (limited to 'lang/js/src/Key.js') diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js index 5ae80438..f6fa7ae3 100644 --- a/lang/js/src/Key.js +++ b/lang/js/src/Key.js @@ -27,7 +27,9 @@ */ import {isFingerprint} from './Helpers' -import {GPGMEJS_Error} from './Errors' +import {gpgme_error} from './Errors' +import { GPGME_Message } from './Message'; +import { permittedOperations } from './permittedOperations'; export class GPGME_Key { @@ -172,32 +174,30 @@ export class GPGME_Key { * */ function checkKey(fingerprint, property){ - return Promise.reject(GPGMEJS_Error('NOT_YET_IMPLEMENTED')); - + return Promise.reject(gpgme_error('NOT_YET_IMPLEMENTED')); + if (!property || + permittedOperations[keyinfo].indexOf(property) < 0){ + return Promise.reject(gpgme_error('PARAM_WRONG')); + } return new Promise(function(resolve, reject){ if (!isFingerprint(fingerprint)){ - reject('not a fingerprint'); //TBD + reject('KEY_INVALID'); } - let conn = new Connection(); - conn.post('getkey',{ // TODO not yet implemented in gpgme - 'fingerprint': this.fingerprint}) - .then(function(result){ - if (property !== undefined){ - if (result.hasOwnProperty(key)){ - resolve(result[property]); - } - else if (property == 'secret'){ - // property undefined means "not true" in case of secret - resolve(false); - } else { - reject('ERR_INVALID_PROPERTY') //TBD - } + let msg = new GPGME_Message('keyinfo'); + msg.setParameter('fingerprint', this.fingerprint); + return (this.connection.post(msg)).then(function(result){ + if (result.hasOwnProperty(property)){ + resolve(result[property]); + } + else if (property == 'secret'){ + // TBD property undefined means "not true" in case of secret? + resolve(false); + } else { + reject(gpgme_error('CONN_UNEXPECTED_ANSWER')); } - - - resolve(result); }, function(error){ - reject(error); + reject({code: 'GNUPG_ERROR', + msg: error.msg}); }); }); }; \ No newline at end of file -- cgit v1.2.3 From 3685913bf510a14b8cb324d980217d90489e6453 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Wed, 25 Apr 2018 19:45:39 +0200 Subject: js: First testing and improvements -- * Introduced Mocha/chai as testsuite. After development build 'npm test' should run the unit tests. Functionality exclusive to Browsers/WebExtensions cannot be run this way, so some other testing is still needed. - package.json: Added required development packages - .babelrc indirect configuration for mocha. ES6 transpiling needs some babel configuration, but mocha has no setting for it. - test/mocha.opts Vonfiguration for mocha runs * Fixed errors: - Helpers.js toKeyIdArray; isLongId is now exported - Key.js Key constructor failed - Message.js will not throw an Error during construction, a new message is now created with createMessage, which can return an Error or a GPGME_Message object * Tests: - test/Helpers: exports from Helpers.js, GPGME_Error handling - test/Message: first init test with bad parameters --- lang/js/src/Key.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'lang/js/src/Key.js') diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js index f6fa7ae3..0b44b245 100644 --- a/lang/js/src/Key.js +++ b/lang/js/src/Key.js @@ -26,9 +26,9 @@ * */ -import {isFingerprint} from './Helpers' -import {gpgme_error} from './Errors' -import { GPGME_Message } from './Message'; +import { isFingerprint } from './Helpers' +import { gpgme_error } from './Errors' +import { createMessage } from './Message'; import { permittedOperations } from './permittedOperations'; export class GPGME_Key { @@ -39,7 +39,7 @@ export class GPGME_Key { set fingerprint(fpr){ if (isFingerprint(fpr) === true && !this._fingerprint){ - this._fingerprint = fingerprint; + this._fingerprint = fpr; } } @@ -181,9 +181,12 @@ function checkKey(fingerprint, property){ } return new Promise(function(resolve, reject){ if (!isFingerprint(fingerprint)){ - reject('KEY_INVALID'); + reject(gpgme_error('KEY_INVALID')); + } + let msg = createMessage ('keyinfo'); + if (msg instanceof Error){ + reject(gpgme_error('PARAM_WRONG')); } - let msg = new GPGME_Message('keyinfo'); msg.setParameter('fingerprint', this.fingerprint); return (this.connection.post(msg)).then(function(result){ if (result.hasOwnProperty(property)){ -- cgit v1.2.3 From fda7b13f1b673962ce34b6f429158a7eb9cef47b Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Fri, 27 Apr 2018 20:03:09 +0200 Subject: js: more testing -- * Tests: initialization of the two modes, encryption * gpgme.js: reintroduced message check before calling Connection.post() * gpgmejs_openpgp.js: Fixed openpgp mode not passing keys * index.js: fixed some confusion in parseconfig() * Inserted some TODO stubs for missing error handling --- lang/js/src/Key.js | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) (limited to 'lang/js/src/Key.js') diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js index 0b44b245..30449d63 100644 --- a/lang/js/src/Key.js +++ b/lang/js/src/Key.js @@ -51,10 +51,7 @@ export class GPGME_Key { * hasSecret returns true if a secret subkey is included in this Key */ get hasSecret(){ - checkKey(this._fingerprint, 'secret').then( function(result){ - return Promise.resolve(result); - }); - + return checkKey(this._fingerprint, 'secret'); } get isRevoked(){ @@ -130,6 +127,8 @@ export class GPGME_Key { } } return Promise.resolve(resultset); + }, function(error){ + //TODO checkKey fails }); } @@ -175,8 +174,7 @@ export class GPGME_Key { */ function checkKey(fingerprint, property){ return Promise.reject(gpgme_error('NOT_YET_IMPLEMENTED')); - if (!property || - permittedOperations[keyinfo].indexOf(property) < 0){ + if (!property || !permittedOperations[keyinfo].hasOwnProperty(property)){ return Promise.reject(gpgme_error('PARAM_WRONG')); } return new Promise(function(resolve, reject){ @@ -188,19 +186,20 @@ function checkKey(fingerprint, property){ reject(gpgme_error('PARAM_WRONG')); } msg.setParameter('fingerprint', this.fingerprint); - return (this.connection.post(msg)).then(function(result){ - if (result.hasOwnProperty(property)){ + return (this.connection.post(msg)).then(function(result, error){ + if (error){ + reject(gpgme_error('GNUPG_ERROR',error.msg)); + } else if (result.hasOwnProperty(property)){ resolve(result[property]); } else if (property == 'secret'){ - // TBD property undefined means "not true" in case of secret? - resolve(false); + // TBD property undefined means "not true" in case of secret? + resolve(false); } else { reject(gpgme_error('CONN_UNEXPECTED_ANSWER')); } }, function(error){ - reject({code: 'GNUPG_ERROR', - msg: error.msg}); + //TODO error handling }); }); }; \ No newline at end of file -- cgit v1.2.3 From 6f67814eb45725bc7f3736a2638bad0a7470f17a Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Thu, 3 May 2018 14:12:10 +0200 Subject: js: changed Key class stub -- * src/Key.js: A Key object cannot offer more than basic functionality outside a connection, so it now requires a connection to be present. --- lang/js/src/Key.js | 124 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 76 insertions(+), 48 deletions(-) (limited to 'lang/js/src/Key.js') diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js index 30449d63..1e0d3195 100644 --- a/lang/js/src/Key.js +++ b/lang/js/src/Key.js @@ -30,11 +30,42 @@ import { isFingerprint } from './Helpers' import { gpgme_error } from './Errors' import { createMessage } from './Message'; import { permittedOperations } from './permittedOperations'; +import { Connection } from './Connection'; + + +export function createKey(fingerprint, parent){ + if (!isFingerprint(fingerprint)){ + return gpgme_error('KEY_INVALID'); + } + 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); + } +} export class GPGME_Key { - constructor(fingerprint){ + constructor(fingerprint, connection){ 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._connection instanceof Connection){ + return gpgme_error('CONN_NO_CONNECT'); + } else { + return this._connection; + } } set fingerprint(fpr){ @@ -51,55 +82,56 @@ export class GPGME_Key { * hasSecret returns true if a secret subkey is included in this Key */ get hasSecret(){ - return checkKey(this._fingerprint, 'secret'); + return this.checkKey('secret'); } get isRevoked(){ - return checkKey(this._fingerprint, 'revoked'); + return this.checkKey('revoked'); } get isExpired(){ - return checkKey(this._fingerprint, 'expired'); + return this.checkKey('expired'); } get isDisabled(){ - return checkKey(this._fingerprint, 'disabled'); + return this.checkKey('disabled'); } get isInvalid(){ - return checkKey(this._fingerprint, 'invalid'); + return this.checkKey('invalid'); } get canEncrypt(){ - return checkKey(this._fingerprint, 'can_encrypt'); + return this.checkKey('can_encrypt'); } get canSign(){ - return checkKey(this._fingerprint, 'can_sign'); + return this.checkKey('can_sign'); } get canCertify(){ - return checkKey(this._fingerprint, 'can_certify'); + return this.checkKey('can_certify'); } get canAuthenticate(){ - return checkKey(this._fingerprint, 'can_authenticate'); + return this.checkKey('can_authenticate'); } get isQualified(){ - return checkKey(this._fingerprint, 'is_qualified'); + return this.checkKey('is_qualified'); } get armored(){ - let me = this; - return new Promise(function(resolve, reject){ - let conn = new Connection(); - conn.setFlag('armor', true); - conn.post('export',{'fpr': me._fingerprint}); + let msg = createMessage ('export_key'); + msg.setParameter('armor', true); + if (msg instanceof Error){ + return gpgme_error('INVALID_KEY'); + } + this.connection.post(msg).then(function(result){ + return result.data; }); // TODO return value not yet checked. Should result in an armored block // in correct encoding - // TODO openpgpjs also returns secKey if private = true? } /** @@ -114,21 +146,21 @@ export class GPGME_Key { * @returns {Array} */ get subkeys(){ - return checkKey(this._fingerprint, 'subkeys').then(function(result){ + return this.checkKey('subkeys').then(function(result){ // TBD expecting a list of fingerprints if (!Array.isArray(result)){ result = [result]; } let resultset = []; for (let i=0; i < result.length; i++){ - let subkey = new GPGME_Key(result[i]); + let subkey = new GPGME_Key(result[i], this.connection); if (subkey instanceof GPGME_Key){ resultset.push(subkey); } } return Promise.resolve(resultset); }, function(error){ - //TODO checkKey fails + //TODO this.checkKey fails }); } @@ -137,7 +169,7 @@ export class GPGME_Key { * @returns {Date|null} TBD */ get timestamp(){ - return checkKey(this._fingerprint, 'timestamp'); + return this.checkKey('timestamp'); //TODO GPGME: -1 if the timestamp is invalid, and 0 if it is not available. } @@ -146,7 +178,7 @@ export class GPGME_Key { * @returns {Date|null} TBD */ get expires(){ - return checkKey(this._fingerprint, 'expires'); + return this.checkKey('expires'); // TODO convert to Date; check for 0 } @@ -155,51 +187,47 @@ export class GPGME_Key { * @returns {String|Array} The user ids associated with this key */ get userIds(){ - return checkKey(this._fingerprint, 'uids'); + return this.checkKey('uids'); } /** * @returns {String} The public key algorithm supported by this subkey */ get pubkey_algo(){ - return checkKey(this._fingerprint, 'pubkey_algo'); + return this.checkKey('pubkey_algo'); } -}; -/** - * generic function to query gnupg information on a key. - * @param {*} fingerprint The identifier of the Keyring - * @param {*} property The gpgme-json property to check - * - */ -function checkKey(fingerprint, property){ - return Promise.reject(gpgme_error('NOT_YET_IMPLEMENTED')); - if (!property || !permittedOperations[keyinfo].hasOwnProperty(property)){ - return Promise.reject(gpgme_error('PARAM_WRONG')); - } - return new Promise(function(resolve, reject){ - if (!isFingerprint(fingerprint)){ - reject(gpgme_error('KEY_INVALID')); + /** + * generic function to query gnupg information on a key. + * @param {*} property The gpgme-json property to check. + * TODO: check if Promise.then(return) + */ + checkKey(property){ + return gpgme_error('NOT_YET_IMPLEMENTED'); + // TODO: async is not what is to be ecpected from Key information :( + if (!property || typeof(property) !== 'string' || + !permittedOperations['keyinfo'].hasOwnProperty(property)){ + return gpgme_error('PARAM_WRONG'); } let msg = createMessage ('keyinfo'); if (msg instanceof Error){ - reject(gpgme_error('PARAM_WRONG')); + return gpgme_error('PARAM_WRONG'); } msg.setParameter('fingerprint', this.fingerprint); - return (this.connection.post(msg)).then(function(result, error){ + this.connection.post(msg).then(function(result, error){ if (error){ - reject(gpgme_error('GNUPG_ERROR',error.msg)); + return gpgme_error('GNUPG_ERROR',error.msg); } else if (result.hasOwnProperty(property)){ - resolve(result[property]); + return result[property]; } else if (property == 'secret'){ - // TBD property undefined means "not true" in case of secret? - resolve(false); + // TBD property undefined means "not true" in case of secret? + return false; } else { - reject(gpgme_error('CONN_UNEXPECTED_ANSWER')); + return gpgme_error('CONN_UNEXPECTED_ANSWER'); } }, function(error){ - //TODO error handling + return gpgme_error('GENERIC_ERROR'); }); - }); + } }; \ No newline at end of file -- cgit v1.2.3 From c755287ba845c4cbbf1d50e5aafecb2e687c7ac9 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Thu, 3 May 2018 18:03:22 +0200 Subject: js: Added browser testing for unit tests -- * Added unittests to be run inside a Browser. To be able to access the non-exposed functions and classes, a testing bundle will be created, containing the tests (unittests.js) and the items to be tested. * src/Helpelpers, src/Key, src/Keyring: fixed some errors found during testing. --- lang/js/src/Key.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'lang/js/src/Key.js') diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js index 1e0d3195..6d3cf17d 100644 --- a/lang/js/src/Key.js +++ b/lang/js/src/Key.js @@ -61,6 +61,9 @@ export class GPGME_Key { } get connection(){ + if (!this._fingerprint){ + return gpgme_error('KEY_INVALID'); + } if (!this._connection instanceof Connection){ return gpgme_error('CONN_NO_CONNECT'); } else { @@ -75,6 +78,9 @@ export class GPGME_Key { } get fingerprint(){ + if (!this._fingerprint){ + return gpgme_error('KEY_INVALID'); + } return this._fingerprint; } @@ -125,7 +131,7 @@ export class GPGME_Key { let msg = createMessage ('export_key'); msg.setParameter('armor', true); if (msg instanceof Error){ - return gpgme_error('INVALID_KEY'); + return gpgme_error('KEY_INVALID'); } this.connection.post(msg).then(function(result){ return result.data; @@ -203,6 +209,9 @@ export class GPGME_Key { * TODO: check if Promise.then(return) */ checkKey(property){ + if (!this._fingerprint){ + return gpgme_error('KEY_INVALID'); + } return gpgme_error('NOT_YET_IMPLEMENTED'); // TODO: async is not what is to be ecpected from Key information :( if (!property || typeof(property) !== 'string' || -- cgit v1.2.3 From cf075846fb48c8d71937100d2c45069d37d54a38 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Fri, 4 May 2018 12:56:59 +0200 Subject: js: fixing errors found by testing -- * Key.js: Error code for wrong parameter in createKey should be "PARAM_WRONG" * Helpers.js: The property openpgpjs-like Objects were checked for in toKeyIdArray was not defined. * src/permittedOperations.js: updated more expectations and assumptions for the native API --- lang/js/src/Key.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'lang/js/src/Key.js') diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js index 6d3cf17d..075a190e 100644 --- a/lang/js/src/Key.js +++ b/lang/js/src/Key.js @@ -35,13 +35,15 @@ import { Connection } from './Connection'; export function createKey(fingerprint, parent){ if (!isFingerprint(fingerprint)){ - return gpgme_error('KEY_INVALID'); + 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'); } } -- cgit v1.2.3 From 7a73d88aba106d571f121dc3230864c81a76e5db Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Fri, 25 May 2018 19:02:18 +0200 Subject: js: implement Key handling (1) -- * Keys can now be queried for information. Onne version queries gnug directly (asynchronous Promise in javascript terms), the cached version refreshes on demand. * Small fixes: src/Connection.js joins answers that stay json properly now --- lang/js/src/Key.js | 433 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 308 insertions(+), 125 deletions(-) (limited to 'lang/js/src/Key.js') diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js index 075a190e..7d3d82b1 100644 --- a/lang/js/src/Key.js +++ b/lang/js/src/Key.js @@ -26,13 +26,19 @@ * */ -import { isFingerprint } from './Helpers' +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. + * @param {String} fingerprint + * @param {Object} parent Either a Connection, or the invoking object with a + * Connection (e.g. Keyring) + */ export function createKey(fingerprint, parent){ if (!isFingerprint(fingerprint)){ return gpgme_error('PARAM_WRONG'); @@ -47,6 +53,9 @@ export function createKey(fingerprint, parent){ } } +/** + * Representing the Keys as stored in GPG + */ export class GPGME_Key { constructor(fingerprint, connection){ @@ -63,7 +72,7 @@ export class GPGME_Key { } get connection(){ - if (!this._fingerprint){ + if (!this._data.fingerprint){ return gpgme_error('KEY_INVALID'); } if (!this._connection instanceof Connection){ @@ -74,171 +83,345 @@ export class GPGME_Key { } set fingerprint(fpr){ - if (isFingerprint(fpr) === true && !this._fingerprint){ - this._fingerprint = fpr; + if (isFingerprint(fpr) === true) { + if (this._data === undefined) { + this._data = {fingerprint: fpr}; + } else { + if (this._data.fingerprint === undefined){ + this._data.fingerprint = fpr; + } + } } } get fingerprint(){ - if (!this._fingerprint){ + if (!this._data || !this._data.fingerprint){ return gpgme_error('KEY_INVALID'); } - return this._fingerprint; + return this._data.fingerprint; } /** - * hasSecret returns true if a secret subkey is included in this Key + * + * @param {Object} data Bulk set data for this key, with the Object as sent + * by gpgme-json. + * @returns {GPGME_Key|GPGME_Error} The Key object itself after values have + * been set */ - get hasSecret(){ - return this.checkKey('secret'); - } - - get isRevoked(){ - return this.checkKey('revoked'); - } - - get isExpired(){ - return this.checkKey('expired'); - } - - get isDisabled(){ - return this.checkKey('disabled'); - } - - get isInvalid(){ - return this.checkKey('invalid'); - } - - get canEncrypt(){ - return this.checkKey('can_encrypt'); - } + setKeydata(data){ + if (this._data === undefined) { + this._data = {}; + } + if ( + typeof(data) !== 'object') { + return gpgme_error('KEY_INVALID'); + } + if (!this._data.fingerprint && isFingerprint(data.fingerprint)){ + if (data.fingerprint !== this.fingerprint){ + return gpgme_error('KEY_INVALID'); + } + this._data.fingerprint = data.fingerprint; + } else if (this._data.fingerprint !== data.fingerprint){ + return gpgme_error('KEY_INVALID'); + } - get canSign(){ - return this.checkKey('can_sign'); - } + let booleans = ['expired', 'disabled','invalid','can_encrypt', + 'can_sign','can_certify','can_authenticate','secret', + 'is_qualified']; + for (let b =0; b < booleans.length; b++) { + if ( + !data.hasOwnProperty(booleans[b]) || + typeof(data[booleans[b]]) !== 'boolean' + ){ + return gpgme_error('KEY_INVALID'); + } + this._data[booleans[b]] = data[booleans[b]]; + } + if (typeof(data.protocol) !== 'string'){ + return gpgme_error('KEY_INVALID'); + } + // TODO check valid protocols? + this._data.protocol = data.protocol; - get canCertify(){ - return this.checkKey('can_certify'); - } + if (typeof(data.owner_trust) !== 'string'){ + return gpgme_error('KEY_INVALID'); + } + // TODO check valid values? + this._data.owner_trust = data.owner_trust; - get canAuthenticate(){ - return this.checkKey('can_authenticate'); - } + // TODO: what about origin ? + if (!Number.isInteger(data.last_update)){ + return gpgme_error('KEY_INVALID'); + } + this._data.last_update = data.last_update; - get isQualified(){ - return this.checkKey('is_qualified'); - } + this._data.subkeys = []; + if (data.hasOwnProperty('subkeys')){ + if (!Array.isArray(data.subkeys)){ + return gpgme_error('KEY_INVALID'); + } + for (let i=0; i< data.subkeys.length; i++) { + this._data.subkeys.push( + new GPGME_Subkey(data.subkeys[i])); + } + } - get armored(){ - let msg = createMessage ('export_key'); - msg.setParameter('armor', true); - if (msg instanceof Error){ - return gpgme_error('KEY_INVALID'); + this._data.userids = []; + if (data.hasOwnProperty('userids')){ + if (!Array.isArray(data.userids)){ + return gpgme_error('KEY_INVALID'); + } + for (let i=0; i< data.userids.length; i++) { + this._data.userids.push( + new GPGME_UserId(data.userids[i])); + } } - this.connection.post(msg).then(function(result){ - return result.data; - }); - // TODO return value not yet checked. Should result in an armored block - // in correct encoding + return this; } /** - * TODO returns true if this is the default key used to sign + * Query any property of the Key list + * (TODO: armor not in here, might be unexpected) + * @param {String} property Key property to be retreived + * @param {*} cached (optional) if false, the data will be directly queried + * from gnupg. + * @returns {*|Promise<*>} the value, or if not cached, a Promise + * resolving on the value */ - get isDefault(){ - throw('NOT_YET_IMPLEMENTED'); + get(property, cached=true) { + if (cached === false) { + let me = this; + return new Promise(function(resolve, reject) { + me.refreshKey().then(function(key){ + resolve(key.get(property, true)); + }, function(error){ + reject(error); + }); + }); + } else { + if (!this._data.hasOwnProperty(property)){ + return gpgme_error('PARAM_WRONG'); + } else { + return (this._data[property]); + } + } } /** - * get the Key's subkeys as GPGME_Key objects - * @returns {Array} + * Reloads the Key from gnupg */ - get subkeys(){ - return this.checkKey('subkeys').then(function(result){ - // TBD expecting a list of fingerprints - if (!Array.isArray(result)){ - result = [result]; + refreshKey() { + let me = this; + return new Promise(function(resolve, reject) { + if (!me._data.fingerprint){ + reject(gpgme_error('KEY_INVALID')); } - let resultset = []; - for (let i=0; i < result.length; i++){ - let subkey = new GPGME_Key(result[i], this.connection); - if (subkey instanceof GPGME_Key){ - resultset.push(subkey); + let msg = createMessage('keylist'); + msg.setParameter('sigs', true); + msg.setParameter('keys', me._data.fingerprint); + me.connection.post(msg).then(function(result){ + if (result.keys.length === 1){ + me.setKeydata(result.keys[0]); + resolve(me); + } else { + reject(gpgme_error('KEY_NOKEY')); } - } - return Promise.resolve(resultset); - }, function(error){ - //TODO this.checkKey fails + }, function (error) { + reject(gpgme_error('GNUPG_ERROR'), error); + }) }); } + //TODO: /** - * creation time stamp of the key - * @returns {Date|null} TBD + * Get the armored block of the non- secret parts of the Key. + * @returns {String} the armored Key block. + * Notice that this may be outdated cached info. Use the async getArmor if + * you need the most current info */ - get timestamp(){ - return this.checkKey('timestamp'); - //TODO GPGME: -1 if the timestamp is invalid, and 0 if it is not available. - } + // get armor(){ TODO } /** - * The expiration timestamp of this key TBD - * @returns {Date|null} TBD + * Query the armored block of the non- secret parts of the Key directly + * from gpg. + * Async, returns Promise */ - get expires(){ - return this.checkKey('expires'); - // TODO convert to Date; check for 0 + // getArmor(){ TODO } + // + + // get hasSecret(){TODO} // confusing difference to Key.get('secret')! + // getHasSecret(){TODO async version} +} + +/** + * The subkeys of a Key. Currently, they cannot be refreshed separately + */ +class GPGME_Subkey { + + constructor(data){ + let keys = Object.keys(data); + for (let i=0; i< keys.length; i++) { + this.setProperty(keys[i], data[keys[i]]); + } + } + + setProperty(property, value){ + if (!this._data){ + this._data = {}; + } + if (validSubKeyProperties.hasOwnProperty(property)){ + if (validSubKeyProperties[property](value) === true) { + this._data[property] = value; + } + } } /** - * getter name TBD - * @returns {String|Array} The user ids associated with this key + * + * @param {String} property Information to request + * @returns {String | Number} + * TODO: date properties are numbers with Date in seconds */ - get userIds(){ - return this.checkKey('uids'); + get(property) { + if (this._data.hasOwnProperty(property)){ + return (this._data[property]); + } + } +} + +class GPGME_UserId { + + constructor(data){ + let keys = Object.keys(data); + for (let i=0; i< keys.length; i++) { + this.setProperty(keys[i], data[keys[i]]); + } + } + + setProperty(property, value){ + if (!this._data){ + this._data = {}; + } + if (validUserIdProperties.hasOwnProperty(property)){ + if (validUserIdProperties[property](value) === true) { + this._data[property] = value; + } + } } /** - * @returns {String} The public key algorithm supported by this subkey + * + * @param {String} property Information to request + * @returns {String | Number} + * TODO: date properties are numbers with Date in seconds */ - get pubkey_algo(){ - return this.checkKey('pubkey_algo'); + get(property) { + if (this._data.hasOwnProperty(property)){ + return (this._data[property]); + } } +} - /** - * generic function to query gnupg information on a key. - * @param {*} property The gpgme-json property to check. - * TODO: check if Promise.then(return) - */ - checkKey(property){ - if (!this._fingerprint){ - return gpgme_error('KEY_INVALID'); +const validUserIdProperties = { + 'revoked': function(value){ + return typeof(value) === 'boolean'; + }, + 'invalid': function(value){ + return typeof(value) === 'boolean'; + }, + 'uid': function(value){ + if (typeof(value) === 'string' || value === ''){ + return true;; } - return gpgme_error('NOT_YET_IMPLEMENTED'); - // TODO: async is not what is to be ecpected from Key information :( - if (!property || typeof(property) !== 'string' || - !permittedOperations['keyinfo'].hasOwnProperty(property)){ - return gpgme_error('PARAM_WRONG'); - } - let msg = createMessage ('keyinfo'); - if (msg instanceof Error){ - return gpgme_error('PARAM_WRONG'); - } - msg.setParameter('fingerprint', this.fingerprint); - this.connection.post(msg).then(function(result, error){ - if (error){ - return gpgme_error('GNUPG_ERROR',error.msg); - } else if (result.hasOwnProperty(property)){ - return result[property]; - } - else if (property == 'secret'){ - // TBD property undefined means "not true" in case of secret? - return false; - } else { - return gpgme_error('CONN_UNEXPECTED_ANSWER'); - } - }, function(error){ - return gpgme_error('GENERIC_ERROR'); - }); + return false; + }, + 'validity': function(value){ + if (typeof(value) === 'string'){ + return true;; + } + return false; + }, + 'name': function(value){ + if (typeof(value) === 'string' || value === ''){ + return true;; + } + return false; + }, + 'email': function(value){ + if (typeof(value) === 'string' || value === ''){ + return true;; + } + return false; + }, + 'address': function(value){ + if (typeof(value) === 'string' || value === ''){ + return true;; + } + return false; + }, + 'comment': function(value){ + if (typeof(value) === 'string' || value === ''){ + return true;; + } + return false; + }, + 'origin': function(value){ + return Number.isInteger(value); + }, + 'last_update': function(value){ + return Number.isInteger(value); } -}; \ No newline at end of file +}; + +const validSubKeyProperties = { + 'invalid': function(value){ + return typeof(value) === 'boolean'; + }, + 'can_encrypt': function(value){ + return typeof(value) === 'boolean'; + }, + 'can_sign': function(value){ + return typeof(value) === 'boolean'; + }, + 'can_certify': function(value){ + return typeof(value) === 'boolean'; + }, + 'can_authenticate': function(value){ + return typeof(value) === 'boolean'; + }, + 'secret': function(value){ + return typeof(value) === 'boolean'; + }, + 'is_qualified': function(value){ + return typeof(value) === 'boolean'; + }, + 'is_cardkey': function(value){ + return typeof(value) === 'boolean'; + }, + 'is_de_vs': function(value){ + return typeof(value) === 'boolean'; + }, + 'pubkey_algo_name': function(value){ + return typeof(value) === 'string'; + // TODO: check against list of known?[''] + }, + 'pubkey_algo_string': function(value){ + return typeof(value) === 'string'; + // TODO: check against list of known?[''] + }, + 'keyid': function(value){ + return isLongId(value); + }, + 'pubkey_algo': function(value) { + return (Number.isInteger(value) && value >= 0); + }, + 'length': function(value){ + return (Number.isInteger(value) && value > 0); + }, + 'timestamp': function(value){ + return (Number.isInteger(value) && value > 0); + }, + 'expires': function(value){ + return (Number.isInteger(value) && value > 0); + } +} -- cgit v1.2.3 From d4adbf453d39659eee378b2be1d7125315d76083 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Mon, 28 May 2018 16:52:50 +0200 Subject: 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 --- lang/js/src/Key.js | 42 ++++++------------------------------------ 1 file changed, 6 insertions(+), 36 deletions(-) (limited to 'lang/js/src/Key.js') 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); -- cgit v1.2.3 From 53ce2b94bc35243710dec9b7972c7aaaa79dbc75 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Mon, 28 May 2018 17:26:56 +0200 Subject: js: Keyring listing keys -- * implementing Keyring methods: - Keyring.getKeys: has an additional option that retrieves the armor and secret state once at the beginning. This is power hungry, but allows for Keys to be used directly (without querying gpgme-json each call) * permittedOperations.js: reflect recent changes in the native counterpart, adding more options * Key: adding two methods for retrieving the armored Key block and for finding out if the Key includes a secret subkey. --- lang/js/src/Key.js | 86 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 72 insertions(+), 14 deletions(-) (limited to 'lang/js/src/Key.js') diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js index 13c99542..f2a16b42 100644 --- a/lang/js/src/Key.js +++ b/lang/js/src/Key.js @@ -77,7 +77,7 @@ export class GPGME_Key { * @returns {GPGME_Key|GPGME_Error} The Key object itself after values have * been set */ - setKeydata(data){ + setKeyData(data){ if (this._data === undefined) { this._data = {}; } @@ -161,11 +161,17 @@ export class GPGME_Key { if (cached === false) { let me = this; return new Promise(function(resolve, reject) { - me.refreshKey().then(function(key){ - resolve(key.get(property, true)); - }, function(error){ - reject(error); - }); + if (property === 'armor'){ + resolve(me.getArmor()); + } else if (property === 'hasSecret'){ + resolve(me.getHasSecret()); + } else { + me.refreshKey().then(function(key){ + resolve(key.get(property, true)); + }, function(error){ + reject(error); + }); + } }); } else { if (!this._data.hasOwnProperty(property)){ @@ -188,10 +194,9 @@ export class GPGME_Key { let msg = createMessage('keylist'); msg.setParameter('sigs', true); msg.setParameter('keys', me._data.fingerprint); - console.log(msg); msg.post().then(function(result){ if (result.keys.length === 1){ - me.setKeydata(result.keys[0]); + me.setKeyData(result.keys[0]); resolve(me); } else { reject(gpgme_error('KEY_NOKEY')); @@ -202,25 +207,78 @@ export class GPGME_Key { }); } - //TODO: /** * Get the armored block of the non- secret parts of the Key. * @returns {String} the armored Key block. * Notice that this may be outdated cached info. Use the async getArmor if * you need the most current info */ + // get armor(){ TODO } /** * Query the armored block of the non- secret parts of the Key directly * from gpg. - * Async, returns Promise + * @returns {Promise} */ - // getArmor(){ TODO } - // + getArmor(){ + let me = this; + return new Promise(function(resolve, reject) { + if (!me._data.fingerprint){ + reject(gpgme_error('KEY_INVALID')); + } + let msg = createMessage('export'); + msg.setParameter('armor', true); + msg.setParameter('keys', me._data.fingerprint); + msg.post().then(function(result){ + me._data.armor = result.data; + resolve(result.data); + }, function(error){ + reject(error); + }); + }); + } - // get hasSecret(){TODO} // confusing difference to Key.get('secret')! - // getHasSecret(){TODO async version} + getHasSecret(){ + let me = this; + return new Promise(function(resolve, reject) { + if (!me._data.fingerprint){ + reject(gpgme_error('KEY_INVALID')); + } + let msg = createMessage('keylist'); + msg.setParameter('keys', me._data.fingerprint); + msg.setParameter('secret', true); + msg.post().then(function(result){ + me._data.hasSecret = null; + if (result.keys === undefined || result.keys.length < 1) { + me._data.hasSecret = false; + resolve(false); + } + else if (result.keys.length === 1){ + let key = result.keys[0]; + if (!key.subkeys){ + me._data.hasSecret = false; + resolve(false); + } else { + for (let i=0; i < key.subkeys.length; i++) { + if (key.subkeys[i].secret === true) { + me._data.hasSecret = true; + resolve(true); + break; + } + if (i === (key.subkeys.length -1)) { + me._data.hasSecret = false; + resolve(false); + } + } + } + } else { + reject(gpgme_error('CONN_UNEXPECTED_ANSWER')) + } + }, function(error){ + }) + }); + } } /** -- cgit v1.2.3 From 332b4adbcc52ccf337cbc1943d5abef500769e10 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Wed, 30 May 2018 17:05:54 +0200 Subject: js: more Keyring/Key handling -- * src/Keys.js - made setKeyData more consistent with other methods - added convenience methods (Key.armored, Key.hasSecret) - Added a Key delete function * src/Keyring.js: - added a getkeysArmored which allows for bulk export of public Keys gpgmejs: - removed deleteKey. It is now a method of the Key itself - Encrypt: Added some common options as parameter, and the possibility to set all allowed flags via an additional Object --- lang/js/src/Key.js | 210 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 152 insertions(+), 58 deletions(-) (limited to 'lang/js/src/Key.js') diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js index f2a16b42..454b1912 100644 --- a/lang/js/src/Key.js +++ b/lang/js/src/Key.js @@ -93,56 +93,31 @@ export class GPGME_Key { } else if (this._data.fingerprint !== data.fingerprint){ return gpgme_error('KEY_INVALID'); } - - let booleans = ['expired', 'disabled','invalid','can_encrypt', - 'can_sign','can_certify','can_authenticate','secret', - 'is_qualified']; - for (let b =0; b < booleans.length; b++) { - if ( - !data.hasOwnProperty(booleans[b]) || - typeof(data[booleans[b]]) !== 'boolean' - ){ + let dataKeys = Object.keys(data); + for (let i=0; i< dataKeys.length; i++){ + if (!validKeyProperties.hasOwnProperty(dataKeys[i])){ return gpgme_error('KEY_INVALID'); } - this._data[booleans[b]] = data[booleans[b]]; - } - if (typeof(data.protocol) !== 'string'){ - return gpgme_error('KEY_INVALID'); - } - // TODO check valid protocols? - this._data.protocol = data.protocol; - - if (typeof(data.owner_trust) !== 'string'){ - return gpgme_error('KEY_INVALID'); - } - // TODO check valid values? - this._data.owner_trust = data.owner_trust; - - // TODO: what about origin ? - if (!Number.isInteger(data.last_update)){ - return gpgme_error('KEY_INVALID'); - } - this._data.last_update = data.last_update; - - this._data.subkeys = []; - if (data.hasOwnProperty('subkeys')){ - if (!Array.isArray(data.subkeys)){ + if (validKeyProperties[dataKeys[i]](data[dataKeys[i]]) !== true ){ return gpgme_error('KEY_INVALID'); } - for (let i=0; i< data.subkeys.length; i++) { - this._data.subkeys.push( - new GPGME_Subkey(data.subkeys[i])); - } - } - - this._data.userids = []; - if (data.hasOwnProperty('userids')){ - if (!Array.isArray(data.userids)){ - return gpgme_error('KEY_INVALID'); - } - for (let i=0; i< data.userids.length; i++) { - this._data.userids.push( - new GPGME_UserId(data.userids[i])); + switch (dataKeys[i]){ + case 'subkeys': + this._data.subkeys = []; + for (let i=0; i< data.subkeys.length; i++) { + this._data.subkeys.push( + new GPGME_Subkey(data.subkeys[i])); + } + break; + case 'userids': + this._data.userids = []; + for (let i=0; i< data.userids.length; i++) { + this._data.userids.push( + new GPGME_UserId(data.userids[i])); + } + break; + default: + this._data[dataKeys[i]] = data[dataKeys[i]]; } } return this; @@ -161,7 +136,9 @@ export class GPGME_Key { if (cached === false) { let me = this; return new Promise(function(resolve, reject) { - if (property === 'armor'){ + if (!validKeyProperties.hasOwnProperty(property)){ + reject('PARAM_WRONG'); + } else if (property === 'armored'){ resolve(me.getArmor()); } else if (property === 'hasSecret'){ resolve(me.getHasSecret()); @@ -173,15 +150,23 @@ export class GPGME_Key { }); } }); - } else { - if (!this._data.hasOwnProperty(property)){ + } else { + if (!validKeyProperties.hasOwnProperty(property)){ return gpgme_error('PARAM_WRONG'); + } + if (!this._data.hasOwnProperty(property)){ + return gpgme_error('KEY_NO_INIT'); } else { return (this._data[property]); } } } + get armored () { + return this.get('armored'); + //TODO exception if empty + } + /** * Reloads the Key from gnupg */ @@ -207,15 +192,6 @@ export class GPGME_Key { }); } - /** - * Get the armored block of the non- secret parts of the Key. - * @returns {String} the armored Key block. - * Notice that this may be outdated cached info. Use the async getArmor if - * you need the most current info - */ - - // get armor(){ TODO } - /** * Query the armored block of the non- secret parts of the Key directly * from gpg. @@ -279,6 +255,49 @@ export class GPGME_Key { }) }); } + + /** + * Convenience function to be directly used as properties of the Key + * Notice that these rely on cached info and may be outdated. Use the async + * get(property, false) if you need the most current info + */ + + /** + * @returns {String} The armored public Key block + */ + get armored(){ + return this.get('armored', true); + } + + /** + * @returns {Boolean} If the key is considered a "private Key", + * i.e. owns a secret subkey. + */ + get hasSecret(){ + return this.get('hasSecret', true); + } + + /** + * Deletes the public Key from the GPG Keyring. Note that a deletion of a + * secret key is not supported by the native backend. + * @returns {Boolean} Success if key was deleted, rejects with a GPG error + * otherwise + */ + delete(){ + let me = this; + return new Promise(function(resolve, reject){ + if (!me._data.fingerprint){ + reject(gpgme_error('KEY_INVALID')); + } + let msg = createMessage('delete'); + msg.setParameter('key', me._data.fingerprint); + msg.post().then(function(result){ + resolve(result.success); + }, function(error){ + reject(error); + }) + }); + } } /** @@ -453,3 +472,78 @@ const validSubKeyProperties = { return (Number.isInteger(value) && value > 0); } } +const validKeyProperties = { + //TODO better validation? + 'fingerprint': function(value){ + return isFingerprint(value); + }, + 'armored': function(value){ + return typeof(value === 'string'); + }, + 'revoked': function(value){ + return typeof(value) === 'boolean'; + }, + 'expired': function(value){ + return typeof(value) === 'boolean'; + }, + 'disabled': function(value){ + return typeof(value) === 'boolean'; + }, + 'invalid': function(value){ + return typeof(value) === 'boolean'; + }, + 'can_encrypt': function(value){ + return typeof(value) === 'boolean'; + }, + 'can_sign': function(value){ + return typeof(value) === 'boolean'; + }, + 'can_certify': function(value){ + return typeof(value) === 'boolean'; + }, + 'can_authenticate': function(value){ + return typeof(value) === 'boolean'; + }, + 'secret': function(value){ + return typeof(value) === 'boolean'; + }, + 'is_qualified': function(value){ + return typeof(value) === 'boolean'; + }, + 'protocol': function(value){ + return typeof(value) === 'string'; + //TODO check for implemented ones + }, + 'issuer_serial': function(value){ + return typeof(value) === 'string'; + }, + 'issuer_name': function(value){ + return typeof(value) === 'string'; + }, + 'chain_id': function(value){ + return typeof(value) === 'string'; + }, + 'owner_trust': function(value){ + return typeof(value) === 'string'; + }, + 'last_update': function(value){ + return (Number.isInteger(value)); + //TODO undefined/null possible? + }, + 'origin': function(value){ + return (Number.isInteger(value)); + }, + 'subkeys': function(value){ + return (Array.isArray(value)); + }, + 'userids': function(value){ + return (Array.isArray(value)); + }, + 'tofu': function(value){ + return (Array.isArray(value)); + }, + 'hasSecret': function(value){ + return typeof(value) === 'boolean'; + } + +} -- cgit v1.2.3 From 0356a667c5a8b4fdb4404cebb57475ed3f39ade9 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Wed, 6 Jun 2018 11:57:41 +0200 Subject: js: implement import/delete Key, some fixes -- * Keyring.js - implemented importKey: importing one or more armored public key blocks. - implemented deleteKey: deleting a public Key from gpg. * Key.js renamed property Key.armor to Key.armored * Helpers.js: toKeyIDArray does not complain anymore if there are no keys. Not having Keys in e.g. signing keys in encrypt is legitimate and common, the complaints were getting spammy * Errors.js: gpgme_errors now always pass an optional additional message, for easier debugging in minified code * Connection.js: Fix in gpgme-json responses containing objects * eslintrc.json: Start using eslint. A cleanup to conform to it is not done yet * Added further tests for the new functionality --- lang/js/src/Key.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lang/js/src/Key.js') diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js index 454b1912..d85c8b6e 100644 --- a/lang/js/src/Key.js +++ b/lang/js/src/Key.js @@ -207,7 +207,7 @@ export class GPGME_Key { msg.setParameter('armor', true); msg.setParameter('keys', me._data.fingerprint); msg.post().then(function(result){ - me._data.armor = result.data; + me._data.armored = result.data; resolve(result.data); }, function(error){ reject(error); @@ -280,7 +280,7 @@ export class GPGME_Key { /** * Deletes the public Key from the GPG Keyring. Note that a deletion of a * secret key is not supported by the native backend. - * @returns {Boolean} Success if key was deleted, rejects with a GPG error + * @returns {Promise} Success if key was deleted, rejects with a GPG error * otherwise */ delete(){ -- cgit v1.2.3 From bfd3799d39df265882deedeee083fd5246a2f35d Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Wed, 6 Jun 2018 13:05:53 +0200 Subject: js: code cleanup (eslint) -- * trying to stick to eslint from now on for readability * As some attribution was lost in previous git confusions, I added my name into some of the licence headers --- lang/js/src/Key.js | 93 ++++++++++++++++++++++++++---------------------------- 1 file changed, 44 insertions(+), 49 deletions(-) (limited to 'lang/js/src/Key.js') diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js index d85c8b6e..8c8726a2 100644 --- a/lang/js/src/Key.js +++ b/lang/js/src/Key.js @@ -16,20 +16,14 @@ * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1+ - */ - -/** - * The key class allows to query the information defined in gpgme Key Objects - * (see https://www.gnupg.org/documentation/manuals/gpgme/Key-objects.html) - * - * This is a stub, as the gpgme-json side is not yet implemented * + * Author(s): + * Maximilian Krambach */ -import { isFingerprint, isLongId } from './Helpers' -import { gpgme_error } from './Errors' +import { isFingerprint, isLongId } from './Helpers'; +import { gpgme_error } from './Errors'; import { createMessage } from './Message'; -import { permittedOperations } from './permittedOperations'; /** * Validates the fingerprint. @@ -44,6 +38,11 @@ export function createKey(fingerprint){ /** * Representing the Keys as stored in GPG + * It allows to query almost all information defined in gpgme Key Objects + * Refer to validKeyProperties for available information, and the gpgme + * documentation on their meaning + * (https://www.gnupg.org/documentation/manuals/gpgme/Key-objects.html) + * */ export class GPGME_Key { @@ -102,22 +101,22 @@ export class GPGME_Key { return gpgme_error('KEY_INVALID'); } switch (dataKeys[i]){ - case 'subkeys': - this._data.subkeys = []; - for (let i=0; i< data.subkeys.length; i++) { - this._data.subkeys.push( - new GPGME_Subkey(data.subkeys[i])); - } - break; - case 'userids': - this._data.userids = []; - for (let i=0; i< data.userids.length; i++) { - this._data.userids.push( - new GPGME_UserId(data.userids[i])); - } - break; - default: - this._data[dataKeys[i]] = data[dataKeys[i]]; + case 'subkeys': + this._data.subkeys = []; + for (let i=0; i< data.subkeys.length; i++) { + this._data.subkeys.push( + new GPGME_Subkey(data.subkeys[i])); + } + break; + case 'userids': + this._data.userids = []; + for (let i=0; i< data.userids.length; i++) { + this._data.userids.push( + new GPGME_UserId(data.userids[i])); + } + break; + default: + this._data[dataKeys[i]] = data[dataKeys[i]]; } } return this; @@ -162,11 +161,6 @@ export class GPGME_Key { } } - get armored () { - return this.get('armored'); - //TODO exception if empty - } - /** * Reloads the Key from gnupg */ @@ -188,7 +182,7 @@ export class GPGME_Key { } }, function (error) { reject(gpgme_error('GNUPG_ERROR'), error); - }) + }); }); } @@ -197,7 +191,7 @@ export class GPGME_Key { * from gpg. * @returns {Promise} */ - getArmor(){ + getArmor(){ let me = this; return new Promise(function(resolve, reject) { if (!me._data.fingerprint){ @@ -249,15 +243,16 @@ export class GPGME_Key { } } } else { - reject(gpgme_error('CONN_UNEXPECTED_ANSWER')) + reject(gpgme_error('CONN_UNEXPECTED_ANSWER')); } }, function(error){ - }) + reject(error); + }); }); } /** - * Convenience function to be directly used as properties of the Key + * Convenience functions to be directly used as properties of the Key * Notice that these rely on cached info and may be outdated. Use the async * get(property, false) if you need the most current info */ @@ -280,8 +275,8 @@ export class GPGME_Key { /** * Deletes the public Key from the GPG Keyring. Note that a deletion of a * secret key is not supported by the native backend. - * @returns {Promise} Success if key was deleted, rejects with a GPG error - * otherwise + * @returns {Promise} Success if key was deleted, rejects with a + * GPG error otherwise */ delete(){ let me = this; @@ -295,7 +290,7 @@ export class GPGME_Key { resolve(result.success); }, function(error){ reject(error); - }) + }); }); } } @@ -378,37 +373,37 @@ const validUserIdProperties = { }, 'uid': function(value){ if (typeof(value) === 'string' || value === ''){ - return true;; + return true; } return false; }, 'validity': function(value){ if (typeof(value) === 'string'){ - return true;; + return true; } return false; }, 'name': function(value){ if (typeof(value) === 'string' || value === ''){ - return true;; + return true; } return false; }, 'email': function(value){ if (typeof(value) === 'string' || value === ''){ - return true;; + return true; } return false; }, 'address': function(value){ if (typeof(value) === 'string' || value === ''){ - return true;; + return true; } return false; }, 'comment': function(value){ if (typeof(value) === 'string' || value === ''){ - return true;; + return true; } return false; }, @@ -449,8 +444,8 @@ const validSubKeyProperties = { return typeof(value) === 'boolean'; }, 'pubkey_algo_name': function(value){ - return typeof(value) === 'string'; - // TODO: check against list of known?[''] + return typeof(value) === 'string'; + // TODO: check against list of known?[''] }, 'pubkey_algo_string': function(value){ return typeof(value) === 'string'; @@ -471,7 +466,7 @@ const validSubKeyProperties = { 'expires': function(value){ return (Number.isInteger(value) && value > 0); } -} +}; const validKeyProperties = { //TODO better validation? 'fingerprint': function(value){ @@ -546,4 +541,4 @@ const validKeyProperties = { return typeof(value) === 'boolean'; } -} +}; -- cgit v1.2.3 From 7a072270ac031152ee034df0f5b6ef5e8bf7d394 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Wed, 6 Jun 2018 15:29:21 +0200 Subject: js: change Keyinfo timestamps into javascript date -- * src/Key.js --- lang/js/src/Key.js | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'lang/js/src/Key.js') diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js index 8c8726a2..5986254e 100644 --- a/lang/js/src/Key.js +++ b/lang/js/src/Key.js @@ -115,6 +115,9 @@ export class GPGME_Key { new GPGME_UserId(data.userids[i])); } break; + case 'last_update': + this._data[dataKeys[i]] = new Date( data[dataKeys[i]] * 1000 ); + break; default: this._data[dataKeys[i]] = data[dataKeys[i]]; } @@ -124,7 +127,6 @@ export class GPGME_Key { /** * Query any property of the Key list - * (TODO: armor not in here, might be unexpected) * @param {String} property Key property to be retreived * @param {*} cached (optional) if false, the data will be directly queried * from gnupg. @@ -313,7 +315,11 @@ class GPGME_Subkey { } if (validSubKeyProperties.hasOwnProperty(property)){ if (validSubKeyProperties[property](value) === true) { - this._data[property] = value; + if (property === 'timestamp' || property === 'expires'){ + this._data[property] = new Date(value * 1000); + } else { + this._data[property] = value; + } } } } @@ -346,9 +352,14 @@ class GPGME_UserId { } if (validUserIdProperties.hasOwnProperty(property)){ if (validUserIdProperties[property](value) === true) { - this._data[property] = value; + if (property === 'last_update'){ + this._data[property] = new Date(value*1000); + } else { + this._data[property] = value; + } } } + } /** -- cgit v1.2.3 From aed402c5d572b60246f1f8e57ae269f8c91b0b7c Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Wed, 13 Jun 2018 15:22:03 +0200 Subject: js: getDefaultKey and verify fix -- * DemoExtension/maindemo.js - added a Demo for retrieving the default signing key * src/Errors.js - add a new Error if no default key can be determined * src/Key.js added documentation and a TODO marker for hasSecret. * src/Keyring.js implemented getDefaultKey * src/permittedOperations.js: Added missing entry for verify, added config_opt --- lang/js/src/Key.js | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'lang/js/src/Key.js') diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js index 5986254e..3e4f1c78 100644 --- a/lang/js/src/Key.js +++ b/lang/js/src/Key.js @@ -192,6 +192,7 @@ export class GPGME_Key { * Query the armored block of the non- secret parts of the Key directly * from gpg. * @returns {Promise} + * @async */ getArmor(){ let me = this; @@ -211,6 +212,13 @@ export class GPGME_Key { }); } + /** + * Find out if the Key includes a secret part + * @returns {Promise} + * + * @async + */ + // TODO: Does not work yet, result is always false getHasSecret(){ let me = this; return new Promise(function(resolve, reject) { -- cgit v1.2.3 From 780f7880c6598d4532354b348d7bd74026d162f4 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Tue, 19 Jun 2018 09:26:01 +0200 Subject: js: getDefaultKey and GenerateKey improvements -- * src/Keyring.js: added more options for key generation. * src/Key.js: GetDefaultKey now relies on the info associated with the key, as the approach of relying on a secret subkey did not work as intended * DemoExtension: Added a button for retrieval of the subkey, to test this functionality. --- lang/js/src/Key.js | 31 ++++++++----------------------- 1 file changed, 8 insertions(+), 23 deletions(-) (limited to 'lang/js/src/Key.js') diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js index 3e4f1c78..88c2b92f 100644 --- a/lang/js/src/Key.js +++ b/lang/js/src/Key.js @@ -218,7 +218,6 @@ export class GPGME_Key { * * @async */ - // TODO: Does not work yet, result is always false getHasSecret(){ let me = this; return new Promise(function(resolve, reject) { @@ -230,31 +229,17 @@ export class GPGME_Key { msg.setParameter('secret', true); msg.post().then(function(result){ me._data.hasSecret = null; - if (result.keys === undefined || result.keys.length < 1) { + if ( + result.keys && + result.keys.length === 1 && + result.keys[0].secret === true + ) { + me._data.hasSecret = true; + resolve(true); + } else { me._data.hasSecret = false; resolve(false); } - else if (result.keys.length === 1){ - let key = result.keys[0]; - if (!key.subkeys){ - me._data.hasSecret = false; - resolve(false); - } else { - for (let i=0; i < key.subkeys.length; i++) { - if (key.subkeys[i].secret === true) { - me._data.hasSecret = true; - resolve(true); - break; - } - if (i === (key.subkeys.length -1)) { - me._data.hasSecret = false; - resolve(false); - } - } - } - } else { - reject(gpgme_error('CONN_UNEXPECTED_ANSWER')); - } }, function(error){ reject(error); }); -- cgit v1.2.3 From 4015f5b4983c8a4590aa71776880d8bc42c7918d Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Tue, 10 Jul 2018 14:32:26 +0200 Subject: js: documentation -- * Fixed errors: - src/Message.js post(): Set chunksize to defined default value instead of hardcoded - src/Keys.js: added getHasSecret() to refreshKey operation. * Reviewed and updated the documentation * non-documentation changes which do not affect functionality: - src/Errors: disabled a console.warn that is only useful for debugging - helpers.js: renamed "string" to "value" in isFingerprint and isLongId to avoid confusion - src/Keyring: prepare_sync, search are both explicitly set to false by default --- lang/js/src/Key.js | 145 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 110 insertions(+), 35 deletions(-) (limited to 'lang/js/src/Key.js') diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js index 88c2b92f..30f507c1 100644 --- a/lang/js/src/Key.js +++ b/lang/js/src/Key.js @@ -26,8 +26,9 @@ import { gpgme_error } from './Errors'; import { createMessage } from './Message'; /** - * Validates the fingerprint. + * Validates the given fingerprint and creates a new {@link GPGME_Key} * @param {String} fingerprint + * @returns {GPGME_Key|GPGME_Error} */ export function createKey(fingerprint){ if (!isFingerprint(fingerprint)){ @@ -37,12 +38,13 @@ export function createKey(fingerprint){ } /** - * Representing the Keys as stored in GPG + * Represents the Keys as stored in the gnupg backend * It allows to query almost all information defined in gpgme Key Objects - * Refer to validKeyProperties for available information, and the gpgme + * Refer to {@link validKeyProperties} for available information, and the gpgme * documentation on their meaning * (https://www.gnupg.org/documentation/manuals/gpgme/Key-objects.html) * + * @class */ export class GPGME_Key { @@ -62,6 +64,9 @@ export class GPGME_Key { } } + /** + * @returns {String} The fingerprint defining this Key + */ get fingerprint(){ if (!this._data || !this._data.fingerprint){ return gpgme_error('KEY_INVALID'); @@ -70,11 +75,11 @@ export class GPGME_Key { } /** - * - * @param {Object} data Bulk set data for this key, with the Object as sent + * @param {Object} data Bulk set the data for this key, with an Object sent * by gpgme-json. - * @returns {GPGME_Key|GPGME_Error} The Key object itself after values have - * been set + * @returns {GPGME_Key|GPGME_Error} Itself after values have been set, an + * error if something went wrong + * @private */ setKeyData(data){ if (this._data === undefined) { @@ -126,12 +131,17 @@ export class GPGME_Key { } /** - * Query any property of the Key list - * @param {String} property Key property to be retreived - * @param {*} cached (optional) if false, the data will be directly queried - * from gnupg. - * @returns {*|Promise<*>} the value, or if not cached, a Promise - * resolving on the value + * Query any property of the Key listed in {@link validKeyProperties} + * @param {String} property property to be retreived + * @param {Boolean} cached (optional) if false, the data will be directly + * queried from gnupg, and the operation will be asynchronous. Else, the + * data will be fetched from the state of the initialization of the Key. + * The cached mode may contain outdated information, but can be used as + * synchronous operation, where the backend is not expected to change Keys + * during a session. The key still can be reloaded by invoking + * {@link refreshKey}. + * @returns {*|Promise<*>} the value (Boolean, String, Array, Object). + * If 'cached' is true, the value will be resolved as a Promise. */ get(property, cached=true) { if (cached === false) { @@ -164,7 +174,12 @@ export class GPGME_Key { } /** - * Reloads the Key from gnupg + * Reloads the Key information from gnupg. This is only useful if you use + * the GPGME_Keys cached. Note that this is a performance hungry operation. + * If you desire more than a few refreshs, it may be advisable to run + * {@link Keyring.getKeys} instead. + * @returns {Promise} + * @async */ refreshKey() { let me = this; @@ -178,7 +193,12 @@ export class GPGME_Key { msg.post().then(function(result){ if (result.keys.length === 1){ me.setKeyData(result.keys[0]); - resolve(me); + me.getHasSecret().then(function(){ + //TODO retrieve armored Key + resolve(me); + }, function(error){ + reject(error); + }); } else { reject(gpgme_error('KEY_NOKEY')); } @@ -189,9 +209,9 @@ export class GPGME_Key { } /** - * Query the armored block of the non- secret parts of the Key directly - * from gpg. - * @returns {Promise} + * Query the armored block of the Key directly from gnupg. Please note that + * this will not get you any export of the secret/private parts of a Key + * @returns {Promise} * @async */ getArmor(){ @@ -213,9 +233,12 @@ export class GPGME_Key { } /** - * Find out if the Key includes a secret part - * @returns {Promise} - * + * Find out if the Key includes a secret part. Note that this is a rather + * nonperformant operation, as it needs to query gnupg twice. If you want + * this inforrmation about more than a few Keys, it may be advisable to run + * {@link Keyring.getKeys} instead. + * @returns {Promise} True if a secret/private Key is + * available in the gnupg Keyring * @async */ getHasSecret(){ @@ -253,25 +276,30 @@ export class GPGME_Key { */ /** - * @returns {String} The armored public Key block + * Property for the export of armored Key. If the armored Key is not + * cached, it returns an {@link GPGME_Error} with code 'KEY_NO_INIT'. + * Running {@link refreshKey} may help in this case. + * @returns {String|GPGME_Error} The armored public Key block. */ get armored(){ return this.get('armored', true); } /** - * @returns {Boolean} If the key is considered a "private Key", - * i.e. owns a secret subkey. + * Property indicating if the Key possesses a private/secret part. If this + * information is not yet cached, it returns an {@link GPGME_Error} with + * code 'KEY_NO_INIT'. Running {@link refreshKey} may help in this case. + * @returns {Boolean} If the Key has a secret subkey. */ get hasSecret(){ return this.get('hasSecret', true); } /** - * Deletes the public Key from the GPG Keyring. Note that a deletion of a + * Deletes the (public) Key from the GPG Keyring. Note that a deletion of a * secret key is not supported by the native backend. - * @returns {Promise} Success if key was deleted, rejects with a - * GPG error otherwise + * @returns {Promise} Success if key was deleted, + * rejects with a GPG error otherwise. */ delete(){ let me = this; @@ -291,10 +319,17 @@ export class GPGME_Key { } /** - * The subkeys of a Key. Currently, they cannot be refreshed separately + * Representing a subkey of a Key. + * @class + * @protected */ class GPGME_Subkey { + /** + * Initializes with the json data sent by gpgme-json + * @param {Object} data + * @private + */ constructor(data){ let keys = Object.keys(data); for (let i=0; i< keys.length; i++) { @@ -302,6 +337,13 @@ class GPGME_Subkey { } } + /** + * Validates a subkey property against {@link validSubKeyProperties} and + * sets it if validation is successful + * @param {String} property + * @param {*} value + * @param private + */ setProperty(property, value){ if (!this._data){ this._data = {}; @@ -318,10 +360,9 @@ class GPGME_Subkey { } /** - * + * Fetches any information about this subkey * @param {String} property Information to request - * @returns {String | Number} - * TODO: date properties are numbers with Date in seconds + * @returns {String | Number | Date} */ get(property) { if (this._data.hasOwnProperty(property)){ @@ -330,15 +371,31 @@ class GPGME_Subkey { } } +/** + * Representing user attributes associated with a Key or subkey + * @class + * @protected + */ class GPGME_UserId { + /** + * Initializes with the json data sent by gpgme-json + * @param {Object} data + * @private + */ constructor(data){ let keys = Object.keys(data); for (let i=0; i< keys.length; i++) { this.setProperty(keys[i], data[keys[i]]); } } - + /** + * Validates a subkey property against {@link validUserIdProperties} and + * sets it if validation is successful + * @param {String} property + * @param {*} value + * @param private + */ setProperty(property, value){ if (!this._data){ this._data = {}; @@ -356,10 +413,9 @@ class GPGME_UserId { } /** - * + * Fetches information about the user * @param {String} property Information to request * @returns {String | Number} - * TODO: date properties are numbers with Date in seconds */ get(property) { if (this._data.hasOwnProperty(property)){ @@ -368,6 +424,13 @@ class GPGME_UserId { } } +/** + * Validation definition for userIds. Each valid userId property is represented + * as a key- Value pair, with their value being a validation function to check + * against + * @protected + * @const + */ const validUserIdProperties = { 'revoked': function(value){ return typeof(value) === 'boolean'; @@ -419,6 +482,12 @@ const validUserIdProperties = { } }; +/** + * Validation definition for subKeys. Each valid userId property is represented + * as a key-value pair, with the value being a validation function + * @protected + * @const + */ const validSubKeyProperties = { 'invalid': function(value){ return typeof(value) === 'boolean'; @@ -471,8 +540,14 @@ const validSubKeyProperties = { return (Number.isInteger(value) && value > 0); } }; + +/** + * Validation definition for Keys. Each valid Key property is represented + * as a key-value pair, with their value being a validation function + * @protected + * @const + */ const validKeyProperties = { - //TODO better validation? 'fingerprint': function(value){ return isFingerprint(value); }, -- cgit v1.2.3 From 4b343c4e339862a5faf8dd20590a3c4592fb6abb Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Tue, 24 Jul 2018 14:56:33 +0200 Subject: js: include armored Key in import callback -- * The import answer now also directly contains the armored Key as Key property, without need to refresh the Key object created in the answer. This allows for direct comparision of input and output. * BrowserTestExtension: added test for that import callback --- lang/js/src/Key.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'lang/js/src/Key.js') diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js index 30f507c1..b024a77b 100644 --- a/lang/js/src/Key.js +++ b/lang/js/src/Key.js @@ -141,7 +141,7 @@ export class GPGME_Key { * during a session. The key still can be reloaded by invoking * {@link refreshKey}. * @returns {*|Promise<*>} the value (Boolean, String, Array, Object). - * If 'cached' is true, the value will be resolved as a Promise. + * If 'cached' is false, the value will be resolved as a Promise. */ get(property, cached=true) { if (cached === false) { @@ -194,8 +194,11 @@ export class GPGME_Key { if (result.keys.length === 1){ me.setKeyData(result.keys[0]); me.getHasSecret().then(function(){ - //TODO retrieve armored Key - resolve(me); + me.getArmor().then(function(){ + resolve(me); + }, function(error){ + reject(error); + }); }, function(error){ reject(error); }); -- cgit v1.2.3 From 94ee0988d4eaac27785de6efb7c19ca9976e1e9c Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Fri, 27 Jul 2018 20:36:21 +0200 Subject: js: change the write access for js class methods -- * src/ [Connection, Error, Key, Keyring, MEssage, Signature, gpgmejs]: Functions and values that are not meant to be overwritten are now moved into their constructors, thus eliminating the possibility of overwrites after initialization. * Key: The mode of use (synchronous cached, or async promises) ivs now determined at initialization of that Key. The property Key.isAsync reflects this state. * unittests: fixed old Key syntax for testing. * Message.js isComplete is now a method and not a getter anymore. * Added some startup tests. --- lang/js/src/Key.js | 287 +++++++++++++++++++++++++---------------------------- 1 file changed, 135 insertions(+), 152 deletions(-) (limited to 'lang/js/src/Key.js') diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js index b024a77b..a7f7dd26 100644 --- a/lang/js/src/Key.js +++ b/lang/js/src/Key.js @@ -28,13 +28,16 @@ import { createMessage } from './Message'; /** * Validates the given fingerprint and creates a new {@link GPGME_Key} * @param {String} fingerprint + * @param {Boolean} async If True, Key properties (except fingerprint) will be + * queried from gnupg on each call, making the operation up-to-date, the + * answers will be Promises, and the performance will likely suffer * @returns {GPGME_Key|GPGME_Error} */ -export function createKey(fingerprint){ - if (!isFingerprint(fingerprint)){ +export function createKey(fingerprint, async = false){ + if (!isFingerprint(fingerprint) || typeof(async) !== 'boolean'){ return gpgme_error('PARAM_WRONG'); } - else return new GPGME_Key(fingerprint); + else return new GPGME_Key(fingerprint, async); } /** @@ -48,31 +51,30 @@ export function createKey(fingerprint){ */ export class GPGME_Key { - constructor(fingerprint){ - this.fingerprint = fingerprint; - } + constructor(fingerprint, async){ - set fingerprint(fpr){ - if (isFingerprint(fpr) === true) { - if (this._data === undefined) { - this._data = {fingerprint: fpr}; - } else { - if (this._data.fingerprint === undefined){ - this._data.fingerprint = fpr; - } + /** + * @property {Boolean} If true, most answers will be asynchronous + */ + this.isAsync = async; + + let _data = {fingerprint: fingerprint}; + this.getFingerprint = function(){ + if (!_data.fingerprint || !isFingerprint(_data.fingerprint)){ + return gpgme_error('KEY_INVALID'); } - } - } + return _data.fingerprint; + }; /** - * @returns {String} The fingerprint defining this Key + * Property indicating if the Key possesses a private/secret part. If this + * information is not yet cached, it returns an {@link GPGME_Error} with + * code 'KEY_NO_INIT'. Running {@link refreshKey} may help in this case. + * @returns {Boolean} If the Key has a secret subkey. */ - get fingerprint(){ - if (!this._data || !this._data.fingerprint){ - return gpgme_error('KEY_INVALID'); - } - return this._data.fingerprint; - } + this.hasSecret= function (){ + return this.get('hasSecret', true); + }; /** * @param {Object} data Bulk set the data for this key, with an Object sent @@ -81,97 +83,89 @@ export class GPGME_Key { * error if something went wrong * @private */ - setKeyData(data){ - if (this._data === undefined) { - this._data = {}; + this.setKeyData = function (data){ + if (typeof(data) !== 'object') { + return gpgme_error('KEY_INVALID'); } - if ( - typeof(data) !== 'object') { + if (!data.fingerprint || data.fingerprint !== _data.fingerprint){ return gpgme_error('KEY_INVALID'); } - if (!this._data.fingerprint && isFingerprint(data.fingerprint)){ - if (data.fingerprint !== this.fingerprint){ - return gpgme_error('KEY_INVALID'); - } - this._data.fingerprint = data.fingerprint; - } else if (this._data.fingerprint !== data.fingerprint){ + let keys = Object.keys(data); + for (let i=0; i< keys.length; i++){ + if (!validKeyProperties.hasOwnProperty(keys[i])){ return gpgme_error('KEY_INVALID'); } - let dataKeys = Object.keys(data); - for (let i=0; i< dataKeys.length; i++){ - if (!validKeyProperties.hasOwnProperty(dataKeys[i])){ - return gpgme_error('KEY_INVALID'); - } - if (validKeyProperties[dataKeys[i]](data[dataKeys[i]]) !== true ){ - return gpgme_error('KEY_INVALID'); - } - switch (dataKeys[i]){ + //running the defined validation function + if (validKeyProperties[keys[i]](data[keys[i]]) !== true ){ + return gpgme_error('KEY_INVALID'); + } + switch (keys[i]){ case 'subkeys': - this._data.subkeys = []; + _data.subkeys = []; for (let i=0; i< data.subkeys.length; i++) { - this._data.subkeys.push( + _data.subkeys.push( new GPGME_Subkey(data.subkeys[i])); } break; case 'userids': - this._data.userids = []; + _data.userids = []; for (let i=0; i< data.userids.length; i++) { - this._data.userids.push( + _data.userids.push( new GPGME_UserId(data.userids[i])); } break; case 'last_update': - this._data[dataKeys[i]] = new Date( data[dataKeys[i]] * 1000 ); + _data[keys[i]] = new Date( data[keys[i]] * 1000 ); break; default: - this._data[dataKeys[i]] = data[dataKeys[i]]; + _data[keys[i]] = data[keys[i]]; } } return this; - } + }; /** * Query any property of the Key listed in {@link validKeyProperties} * @param {String} property property to be retreived - * @param {Boolean} cached (optional) if false, the data will be directly - * queried from gnupg, and the operation will be asynchronous. Else, the - * data will be fetched from the state of the initialization of the Key. - * The cached mode may contain outdated information, but can be used as - * synchronous operation, where the backend is not expected to change Keys - * during a session. The key still can be reloaded by invoking - * {@link refreshKey}. * @returns {*|Promise<*>} the value (Boolean, String, Array, Object). * If 'cached' is false, the value will be resolved as a Promise. */ - get(property, cached=true) { - if (cached === false) { + this.get = function(property) { + if (this.isAsync === true) { let me = this; return new Promise(function(resolve, reject) { - if (!validKeyProperties.hasOwnProperty(property)){ - reject('PARAM_WRONG'); - } else if (property === 'armored'){ + if (property === 'armored'){ resolve(me.getArmor()); } else if (property === 'hasSecret'){ resolve(me.getHasSecret()); - } else { - me.refreshKey().then(function(key){ - resolve(key.get(property, true)); + } else if (validKeyProperties.hasOwnProperty(property)){ + let msg = createMessage('keylist'); + msg.setParameter('keys', _data.fingerprint); + msg.post().then(function(result){ + if (result.keys && result.keys.length === 1 && + result.keys[0].hasOwnProperty(property)){ + resolve(result.keys[0][property]); + } else { + reject(gpgme_error('CONN_UNEXPECTED_ANSWER')); + } }, function(error){ - reject(error); + reject(gpgme_error(error)); }); + } else { + reject(gpgme_error('PARAM_WRONG')); } }); } else { if (!validKeyProperties.hasOwnProperty(property)){ return gpgme_error('PARAM_WRONG'); } - if (!this._data.hasOwnProperty(property)){ + if (!_data.hasOwnProperty(property)){ return gpgme_error('KEY_NO_INIT'); } else { - return (this._data[property]); + return (_data[property]); } } - } + }; /** * Reloads the Key information from gnupg. This is only useful if you use @@ -181,15 +175,15 @@ export class GPGME_Key { * @returns {Promise} * @async */ - refreshKey() { + this.refreshKey = function() { let me = this; return new Promise(function(resolve, reject) { - if (!me._data.fingerprint){ + if (!_data.fingerprint){ reject(gpgme_error('KEY_INVALID')); } let msg = createMessage('keylist'); msg.setParameter('sigs', true); - msg.setParameter('keys', me._data.fingerprint); + msg.setParameter('keys', _data.fingerprint); msg.post().then(function(result){ if (result.keys.length === 1){ me.setKeyData(result.keys[0]); @@ -209,7 +203,7 @@ export class GPGME_Key { reject(gpgme_error('GNUPG_ERROR'), error); }); }); - } + }; /** * Query the armored block of the Key directly from gnupg. Please note that @@ -217,23 +211,22 @@ export class GPGME_Key { * @returns {Promise} * @async */ - getArmor(){ - let me = this; + this.getArmor = function(){ return new Promise(function(resolve, reject) { - if (!me._data.fingerprint){ + if (!_data.fingerprint){ reject(gpgme_error('KEY_INVALID')); } let msg = createMessage('export'); msg.setParameter('armor', true); - msg.setParameter('keys', me._data.fingerprint); + msg.setParameter('keys', _data.fingerprint); msg.post().then(function(result){ - me._data.armored = result.data; + _data.armored = result.data; resolve(result.data); }, function(error){ reject(error); }); }); - } + }; /** * Find out if the Key includes a secret part. Note that this is a rather @@ -244,59 +237,32 @@ export class GPGME_Key { * available in the gnupg Keyring * @async */ - getHasSecret(){ - let me = this; + this.getHasSecret = function (){ return new Promise(function(resolve, reject) { - if (!me._data.fingerprint){ + if (!_data.fingerprint){ reject(gpgme_error('KEY_INVALID')); } let msg = createMessage('keylist'); - msg.setParameter('keys', me._data.fingerprint); + msg.setParameter('keys', _data.fingerprint); msg.setParameter('secret', true); msg.post().then(function(result){ - me._data.hasSecret = null; + _data.hasSecret = null; if ( result.keys && result.keys.length === 1 && result.keys[0].secret === true ) { - me._data.hasSecret = true; + _data.hasSecret = true; resolve(true); } else { - me._data.hasSecret = false; + _data.hasSecret = false; resolve(false); } }, function(error){ reject(error); }); }); - } - - /** - * Convenience functions to be directly used as properties of the Key - * Notice that these rely on cached info and may be outdated. Use the async - * get(property, false) if you need the most current info - */ - - /** - * Property for the export of armored Key. If the armored Key is not - * cached, it returns an {@link GPGME_Error} with code 'KEY_NO_INIT'. - * Running {@link refreshKey} may help in this case. - * @returns {String|GPGME_Error} The armored public Key block. - */ - get armored(){ - return this.get('armored', true); - } - - /** - * Property indicating if the Key possesses a private/secret part. If this - * information is not yet cached, it returns an {@link GPGME_Error} with - * code 'KEY_NO_INIT'. Running {@link refreshKey} may help in this case. - * @returns {Boolean} If the Key has a secret subkey. - */ - get hasSecret(){ - return this.get('hasSecret', true); - } + }; /** * Deletes the (public) Key from the GPG Keyring. Note that a deletion of a @@ -304,20 +270,37 @@ export class GPGME_Key { * @returns {Promise} Success if key was deleted, * rejects with a GPG error otherwise. */ - delete(){ - let me = this; + this.delete= function (){ return new Promise(function(resolve, reject){ - if (!me._data.fingerprint){ + if (!_data.fingerprint){ reject(gpgme_error('KEY_INVALID')); } let msg = createMessage('delete'); - msg.setParameter('key', me._data.fingerprint); + msg.setParameter('key', _data.fingerprint); msg.post().then(function(result){ resolve(result.success); }, function(error){ reject(error); }); }); + }; + } + + /** + * @returns {String} The fingerprint defining this Key + */ + get fingerprint(){ + return this.getFingerprint(); + } + + /** + * Property for the export of armored Key. If the armored Key is not + * cached, it returns an {@link GPGME_Error} with code 'KEY_NO_INIT'. + * Running {@link refreshKey} may help in this case. + * @returns {String|GPGME_Error} The armored public Key block. + */ + get armored(){ + return this.get('armored', true); } } @@ -334,44 +317,45 @@ class GPGME_Subkey { * @private */ constructor(data){ + let _data = {}; let keys = Object.keys(data); - for (let i=0; i< keys.length; i++) { - this.setProperty(keys[i], data[keys[i]]); - } - } - /** + /** * Validates a subkey property against {@link validSubKeyProperties} and * sets it if validation is successful * @param {String} property * @param {*} value * @param private */ - setProperty(property, value){ - if (!this._data){ - this._data = {}; - } + const setProperty = function (property, value){ if (validSubKeyProperties.hasOwnProperty(property)){ if (validSubKeyProperties[property](value) === true) { if (property === 'timestamp' || property === 'expires'){ - this._data[property] = new Date(value * 1000); + _data[property] = new Date(value * 1000); } else { - this._data[property] = value; + _data[property] = value; } } } + }; + for (let i=0; i< keys.length; i++) { + setProperty(keys[i], data[keys[i]]); } + + /** * Fetches any information about this subkey * @param {String} property Information to request * @returns {String | Number | Date} */ - get(property) { - if (this._data.hasOwnProperty(property)){ - return (this._data[property]); + this.get = function(property) { + if (_data.hasOwnProperty(property)){ + return (_data[property]); } - } + }; +} + } /** @@ -387,11 +371,23 @@ class GPGME_UserId { * @private */ constructor(data){ + let _data = {}; let keys = Object.keys(data); + const setProperty = function(property, value){ + if (validUserIdProperties.hasOwnProperty(property)){ + if (validUserIdProperties[property](value) === true) { + if (property === 'last_update'){ + _data[property] = new Date(value*1000); + } else { + _data[property] = value; + } + } + } + }; for (let i=0; i< keys.length; i++) { - this.setProperty(keys[i], data[keys[i]]); + setProperty(keys[i], data[keys[i]]); } - } + /** * Validates a subkey property against {@link validUserIdProperties} and * sets it if validation is successful @@ -399,34 +395,21 @@ class GPGME_UserId { * @param {*} value * @param private */ - setProperty(property, value){ - if (!this._data){ - this._data = {}; - } - if (validUserIdProperties.hasOwnProperty(property)){ - if (validUserIdProperties[property](value) === true) { - if (property === 'last_update'){ - this._data[property] = new Date(value*1000); - } else { - this._data[property] = value; - } - } - } - } /** * Fetches information about the user * @param {String} property Information to request * @returns {String | Number} */ - get(property) { - if (this._data.hasOwnProperty(property)){ - return (this._data[property]); + this.get = function (property) { + if (_data.hasOwnProperty(property)){ + return (_data[property]); } - } + }; } +} /** * Validation definition for userIds. Each valid userId property is represented * as a key- Value pair, with their value being a validation function to check -- cgit v1.2.3 From 522121ea7e105acc22795b1997ca500c7b227b4f Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Fri, 27 Jul 2018 20:56:11 +0200 Subject: js: fix indentaion -- * doing the indentation changes that became neccesary in the last commit. --- lang/js/src/Key.js | 493 ++++++++++++++++++++++++++--------------------------- 1 file changed, 246 insertions(+), 247 deletions(-) (limited to 'lang/js/src/Key.js') diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js index a7f7dd26..d5873a70 100644 --- a/lang/js/src/Key.js +++ b/lang/js/src/Key.js @@ -66,224 +66,226 @@ export class GPGME_Key { return _data.fingerprint; }; - /** - * Property indicating if the Key possesses a private/secret part. If this - * information is not yet cached, it returns an {@link GPGME_Error} with - * code 'KEY_NO_INIT'. Running {@link refreshKey} may help in this case. - * @returns {Boolean} If the Key has a secret subkey. - */ - this.hasSecret= function (){ - return this.get('hasSecret', true); - }; + /** + * Property indicating if the Key possesses a private/secret part. If + * this information is not yet cached, it returns an + * {@link GPGME_Error} with code 'KEY_NO_INIT'. Running + * {@link refreshKey} may help in this case. + * @returns {Boolean} If the Key has a secret subkey. + */ + this.hasSecret= function (){ + return this.get('hasSecret', true); + }; - /** - * @param {Object} data Bulk set the data for this key, with an Object sent - * by gpgme-json. - * @returns {GPGME_Key|GPGME_Error} Itself after values have been set, an - * error if something went wrong - * @private - */ - this.setKeyData = function (data){ - if (typeof(data) !== 'object') { - return gpgme_error('KEY_INVALID'); - } - if (!data.fingerprint || data.fingerprint !== _data.fingerprint){ - return gpgme_error('KEY_INVALID'); - } - let keys = Object.keys(data); - for (let i=0; i< keys.length; i++){ - if (!validKeyProperties.hasOwnProperty(keys[i])){ - return gpgme_error('KEY_INVALID'); - } - //running the defined validation function - if (validKeyProperties[keys[i]](data[keys[i]]) !== true ){ - return gpgme_error('KEY_INVALID'); - } - switch (keys[i]){ - case 'subkeys': - _data.subkeys = []; - for (let i=0; i< data.subkeys.length; i++) { - _data.subkeys.push( - new GPGME_Subkey(data.subkeys[i])); + /** + * @param {Object} data Bulk set the data for this key, with an Object + * sent by gpgme-json. + * @returns {GPGME_Key|GPGME_Error} Itself after values have been set, + * an error if something went wrong. + * @private + */ + this.setKeyData = function (data){ + if (typeof(data) !== 'object') { + return gpgme_error('KEY_INVALID'); + } + if (!data.fingerprint || data.fingerprint !== _data.fingerprint){ + return gpgme_error('KEY_INVALID'); + } + let keys = Object.keys(data); + for (let i=0; i< keys.length; i++){ + if (!validKeyProperties.hasOwnProperty(keys[i])){ + return gpgme_error('KEY_INVALID'); } - break; - case 'userids': - _data.userids = []; - for (let i=0; i< data.userids.length; i++) { - _data.userids.push( - new GPGME_UserId(data.userids[i])); + //running the defined validation function + if (validKeyProperties[keys[i]](data[keys[i]]) !== true ){ + return gpgme_error('KEY_INVALID'); } - break; - case 'last_update': - _data[keys[i]] = new Date( data[keys[i]] * 1000 ); - break; - default: + switch (keys[i]){ + case 'subkeys': + _data.subkeys = []; + for (let i=0; i< data.subkeys.length; i++) { + _data.subkeys.push( + new GPGME_Subkey(data.subkeys[i])); + } + break; + case 'userids': + _data.userids = []; + for (let i=0; i< data.userids.length; i++) { + _data.userids.push( + new GPGME_UserId(data.userids[i])); + } + break; + case 'last_update': + _data[keys[i]] = new Date( data[keys[i]] * 1000 ); + break; + default: _data[keys[i]] = data[keys[i]]; - } - } - return this; - }; - - /** - * Query any property of the Key listed in {@link validKeyProperties} - * @param {String} property property to be retreived - * @returns {*|Promise<*>} the value (Boolean, String, Array, Object). - * If 'cached' is false, the value will be resolved as a Promise. - */ - this.get = function(property) { - if (this.isAsync === true) { - let me = this; - return new Promise(function(resolve, reject) { - if (property === 'armored'){ - resolve(me.getArmor()); - } else if (property === 'hasSecret'){ - resolve(me.getHasSecret()); - } else if (validKeyProperties.hasOwnProperty(property)){ - let msg = createMessage('keylist'); - msg.setParameter('keys', _data.fingerprint); - msg.post().then(function(result){ - if (result.keys && result.keys.length === 1 && - result.keys[0].hasOwnProperty(property)){ - resolve(result.keys[0][property]); - } else { - reject(gpgme_error('CONN_UNEXPECTED_ANSWER')); - } - }, function(error){ - reject(gpgme_error(error)); - }); - } else { - reject(gpgme_error('PARAM_WRONG')); } - }); - } else { - if (!validKeyProperties.hasOwnProperty(property)){ - return gpgme_error('PARAM_WRONG'); } - if (!_data.hasOwnProperty(property)){ - return gpgme_error('KEY_NO_INIT'); + return this; + }; + + /** + * Query any property of the Key listed in {@link validKeyProperties} + * @param {String} property property to be retreived + * @returns {*|Promise<*>} the value (Boolean, String, Array, Object). + * If 'cached' is false, the value will be resolved as a Promise. + */ + this.get = function(property) { + if (this.isAsync === true) { + let me = this; + return new Promise(function(resolve, reject) { + if (property === 'armored'){ + resolve(me.getArmor()); + } else if (property === 'hasSecret'){ + resolve(me.getHasSecret()); + } else if (validKeyProperties.hasOwnProperty(property)){ + let msg = createMessage('keylist'); + msg.setParameter('keys', _data.fingerprint); + msg.post().then(function(result){ + if (result.keys && result.keys.length === 1 && + result.keys[0].hasOwnProperty(property)){ + resolve(result.keys[0][property]); + } else { + reject(gpgme_error('CONN_UNEXPECTED_ANSWER')); + } + }, function(error){ + reject(gpgme_error(error)); + }); + } else { + reject(gpgme_error('PARAM_WRONG')); + } + }); } else { + if (!validKeyProperties.hasOwnProperty(property)){ + return gpgme_error('PARAM_WRONG'); + } + if (!_data.hasOwnProperty(property)){ + return gpgme_error('KEY_NO_INIT'); + } else { return (_data[property]); + } } - } - }; + }; - /** - * Reloads the Key information from gnupg. This is only useful if you use - * the GPGME_Keys cached. Note that this is a performance hungry operation. - * If you desire more than a few refreshs, it may be advisable to run - * {@link Keyring.getKeys} instead. - * @returns {Promise} - * @async - */ - this.refreshKey = function() { - let me = this; - return new Promise(function(resolve, reject) { - if (!_data.fingerprint){ - reject(gpgme_error('KEY_INVALID')); - } - let msg = createMessage('keylist'); - msg.setParameter('sigs', true); - msg.setParameter('keys', _data.fingerprint); - msg.post().then(function(result){ - if (result.keys.length === 1){ - me.setKeyData(result.keys[0]); - me.getHasSecret().then(function(){ - me.getArmor().then(function(){ - resolve(me); + /** + * Reloads the Key information from gnupg. This is only useful if you + * use the GPGME_Keys cached. Note that this is a performance hungry + * operation. If you desire more than a few refreshs, it may be + * advisable to run {@link Keyring.getKeys} instead. + * @returns {Promise} + * @async + */ + this.refreshKey = function() { + let me = this; + return new Promise(function(resolve, reject) { + if (!_data.fingerprint){ + reject(gpgme_error('KEY_INVALID')); + } + let msg = createMessage('keylist'); + msg.setParameter('sigs', true); + msg.setParameter('keys', _data.fingerprint); + msg.post().then(function(result){ + if (result.keys.length === 1){ + me.setKeyData(result.keys[0]); + me.getHasSecret().then(function(){ + me.getArmor().then(function(){ + resolve(me); + }, function(error){ + reject(error); + }); }, function(error){ reject(error); }); - }, function(error){ - reject(error); - }); - } else { - reject(gpgme_error('KEY_NOKEY')); - } - }, function (error) { - reject(gpgme_error('GNUPG_ERROR'), error); + } else { + reject(gpgme_error('KEY_NOKEY')); + } + }, function (error) { + reject(gpgme_error('GNUPG_ERROR'), error); + }); }); - }); - }; + }; - /** - * Query the armored block of the Key directly from gnupg. Please note that - * this will not get you any export of the secret/private parts of a Key - * @returns {Promise} - * @async - */ - this.getArmor = function(){ - return new Promise(function(resolve, reject) { - if (!_data.fingerprint){ - reject(gpgme_error('KEY_INVALID')); - } - let msg = createMessage('export'); - msg.setParameter('armor', true); - msg.setParameter('keys', _data.fingerprint); - msg.post().then(function(result){ - _data.armored = result.data; - resolve(result.data); - }, function(error){ - reject(error); + /** + * Query the armored block of the Key directly from gnupg. Please note + * that this will not get you any export of the secret/private parts of + * a Key + * @returns {Promise} + * @async + */ + this.getArmor = function(){ + return new Promise(function(resolve, reject) { + if (!_data.fingerprint){ + reject(gpgme_error('KEY_INVALID')); + } + let msg = createMessage('export'); + msg.setParameter('armor', true); + msg.setParameter('keys', _data.fingerprint); + msg.post().then(function(result){ + _data.armored = result.data; + resolve(result.data); + }, function(error){ + reject(error); + }); }); - }); - }; + }; - /** - * Find out if the Key includes a secret part. Note that this is a rather - * nonperformant operation, as it needs to query gnupg twice. If you want - * this inforrmation about more than a few Keys, it may be advisable to run - * {@link Keyring.getKeys} instead. - * @returns {Promise} True if a secret/private Key is - * available in the gnupg Keyring - * @async - */ - this.getHasSecret = function (){ - return new Promise(function(resolve, reject) { - if (!_data.fingerprint){ - reject(gpgme_error('KEY_INVALID')); - } - let msg = createMessage('keylist'); - msg.setParameter('keys', _data.fingerprint); - msg.setParameter('secret', true); - msg.post().then(function(result){ - _data.hasSecret = null; - if ( - result.keys && - result.keys.length === 1 && - result.keys[0].secret === true - ) { - _data.hasSecret = true; - resolve(true); - } else { - _data.hasSecret = false; - resolve(false); + /** + * Find out if the Key includes a secret part. Note that this is a + * rather nonperformant operation, as it needs to query gnupg twice. + * If you want this inforrmation about more than a few Keys, it may be + * advisable to run {@link Keyring.getKeys} instead. + * @returns {Promise} True if a secret/private Key + * is available in the gnupg Keyring + * @async + */ + this.getHasSecret = function (){ + return new Promise(function(resolve, reject) { + if (!_data.fingerprint){ + reject(gpgme_error('KEY_INVALID')); } - }, function(error){ - reject(error); + let msg = createMessage('keylist'); + msg.setParameter('keys', _data.fingerprint); + msg.setParameter('secret', true); + msg.post().then(function(result){ + _data.hasSecret = null; + if ( + result.keys && + result.keys.length === 1 && + result.keys[0].secret === true + ) { + _data.hasSecret = true; + resolve(true); + } else { + _data.hasSecret = false; + resolve(false); + } + }, function(error){ + reject(error); + }); }); - }); - }; + }; - /** - * Deletes the (public) Key from the GPG Keyring. Note that a deletion of a - * secret key is not supported by the native backend. - * @returns {Promise} Success if key was deleted, - * rejects with a GPG error otherwise. - */ - this.delete= function (){ - return new Promise(function(resolve, reject){ - if (!_data.fingerprint){ - reject(gpgme_error('KEY_INVALID')); - } - let msg = createMessage('delete'); - msg.setParameter('key', _data.fingerprint); - msg.post().then(function(result){ - resolve(result.success); - }, function(error){ - reject(error); + /** + * Deletes the (public) Key from the GPG Keyring. Note that a deletion + * of a secret key is not supported by the native backend. + * @returns {Promise} Success if key was deleted, + * rejects with a GPG error otherwise. + */ + this.delete= function (){ + return new Promise(function(resolve, reject){ + if (!_data.fingerprint){ + reject(gpgme_error('KEY_INVALID')); + } + let msg = createMessage('delete'); + msg.setParameter('key', _data.fingerprint); + msg.post().then(function(result){ + resolve(result.success); + }, function(error){ + reject(error); + }); }); - }); - }; + }; } /** @@ -320,42 +322,39 @@ class GPGME_Subkey { let _data = {}; let keys = Object.keys(data); - /** - * Validates a subkey property against {@link validSubKeyProperties} and - * sets it if validation is successful - * @param {String} property - * @param {*} value - * @param private - */ - const setProperty = function (property, value){ - if (validSubKeyProperties.hasOwnProperty(property)){ - if (validSubKeyProperties[property](value) === true) { - if (property === 'timestamp' || property === 'expires'){ - _data[property] = new Date(value * 1000); - } else { - _data[property] = value; + /** + * Validates a subkey property against {@link validSubKeyProperties} and + * sets it if validation is successful + * @param {String} property + * @param {*} value + * @param private + */ + const setProperty = function (property, value){ + if (validSubKeyProperties.hasOwnProperty(property)){ + if (validSubKeyProperties[property](value) === true) { + if (property === 'timestamp' || property === 'expires'){ + _data[property] = new Date(value * 1000); + } else { + _data[property] = value; + } } } + }; + for (let i=0; i< keys.length; i++) { + setProperty(keys[i], data[keys[i]]); } - }; - for (let i=0; i< keys.length; i++) { - setProperty(keys[i], data[keys[i]]); - } - - - - /** - * Fetches any information about this subkey - * @param {String} property Information to request - * @returns {String | Number | Date} - */ - this.get = function(property) { - if (_data.hasOwnProperty(property)){ - return (_data[property]); - } - }; -} + /** + * Fetches any information about this subkey + * @param {String} property Information to request + * @returns {String | Number | Date} + */ + this.get = function(property) { + if (_data.hasOwnProperty(property)){ + return (_data[property]); + } + }; + } } /** @@ -388,28 +387,28 @@ class GPGME_UserId { setProperty(keys[i], data[keys[i]]); } - /** - * Validates a subkey property against {@link validUserIdProperties} and - * sets it if validation is successful - * @param {String} property - * @param {*} value - * @param private - */ + /** + * Validates a subkey property against {@link validUserIdProperties} and + * sets it if validation is successful + * @param {String} property + * @param {*} value + * @param private + */ - /** - * Fetches information about the user - * @param {String} property Information to request - * @returns {String | Number} - */ - this.get = function (property) { - if (_data.hasOwnProperty(property)){ - return (_data[property]); - } - }; + /** + * Fetches information about the user + * @param {String} property Information to request + * @returns {String | Number} + */ + this.get = function (property) { + if (_data.hasOwnProperty(property)){ + return (_data[property]); + } + }; + } } -} /** * Validation definition for userIds. Each valid userId property is represented * as a key- Value pair, with their value being a validation function to check -- cgit v1.2.3 From e16a87e83910ebb6bfdc4148369165f121f0997e Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Mon, 30 Jul 2018 12:31:27 +0200 Subject: js: Making objects inmutable -- * An Object.freeze should stop any malicious third party from changing objects' methods once the objects are instantiated (see unittest for an approach that would have worked before) - An initialized gpgmejs- object doesn't have a '_Keyring' property anymore (it still has its 'Keyring') - The internal expect='base64' needed to be turned into a method. --- lang/js/src/Key.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'lang/js/src/Key.js') diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js index d5873a70..f431a283 100644 --- a/lang/js/src/Key.js +++ b/lang/js/src/Key.js @@ -37,7 +37,7 @@ export function createKey(fingerprint, async = false){ if (!isFingerprint(fingerprint) || typeof(async) !== 'boolean'){ return gpgme_error('PARAM_WRONG'); } - else return new GPGME_Key(fingerprint, async); + else return Object.freeze(new GPGME_Key(fingerprint, async)); } /** @@ -104,15 +104,15 @@ export class GPGME_Key { case 'subkeys': _data.subkeys = []; for (let i=0; i< data.subkeys.length; i++) { - _data.subkeys.push( - new GPGME_Subkey(data.subkeys[i])); + _data.subkeys.push(Object.freeze( + new GPGME_Subkey(data.subkeys[i]))); } break; case 'userids': _data.userids = []; for (let i=0; i< data.userids.length; i++) { - _data.userids.push( - new GPGME_UserId(data.userids[i])); + _data.userids.push(Object.freeze( + new GPGME_UserId(data.userids[i]))); } break; case 'last_update': -- cgit v1.2.3 From 9d247b7fd5edd11fb5710a057baec671276f5034 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Tue, 31 Jul 2018 16:54:43 +0200 Subject: js: Fix Key.hasSecret answer -- * The comparision result between Keyring and Keyring with secrets was set to the wrong Object which was not returned at all. --- lang/js/src/Key.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lang/js/src/Key.js') diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js index f431a283..88c49d3f 100644 --- a/lang/js/src/Key.js +++ b/lang/js/src/Key.js @@ -74,7 +74,7 @@ export class GPGME_Key { * @returns {Boolean} If the Key has a secret subkey. */ this.hasSecret= function (){ - return this.get('hasSecret', true); + return this.get('hasSecret'); }; /** -- cgit v1.2.3 From 622db0d1de665dfd93c991cd2d517078b04b3a13 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Thu, 16 Aug 2018 11:25:50 +0200 Subject: js: consistently return uppercase fingerprint -- * src/Key.js: the fingerprint returned by a Key is now always upper case hex, even if the constructor had lower case input. This is to be more consistent with gpgme and to be more readable and reliable in comparisions. --- lang/js/src/Key.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'lang/js/src/Key.js') diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js index 88c49d3f..eeb27035 100644 --- a/lang/js/src/Key.js +++ b/lang/js/src/Key.js @@ -58,7 +58,7 @@ export class GPGME_Key { */ this.isAsync = async; - let _data = {fingerprint: fingerprint}; + let _data = {fingerprint: fingerprint.toUpperCase()}; this.getFingerprint = function(){ if (!_data.fingerprint || !isFingerprint(_data.fingerprint)){ return gpgme_error('KEY_INVALID'); @@ -88,7 +88,8 @@ export class GPGME_Key { if (typeof(data) !== 'object') { return gpgme_error('KEY_INVALID'); } - if (!data.fingerprint || data.fingerprint !== _data.fingerprint){ + if (!data.fingerprint || + data.fingerprint.toUpperCase() !== _data.fingerprint){ return gpgme_error('KEY_INVALID'); } let keys = Object.keys(data); -- cgit v1.2.3 From ea43158d4043b01058afd7411c84aa38b61c2009 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Thu, 16 Aug 2018 14:40:53 +0200 Subject: js: avoid async getters -- * src/Key.js get armored was returning a promise on async keys. As getters should not do that, it returns an error in this case. --- lang/js/src/Key.js | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) (limited to 'lang/js/src/Key.js') diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js index eeb27035..aa419053 100644 --- a/lang/js/src/Key.js +++ b/lang/js/src/Key.js @@ -303,7 +303,11 @@ export class GPGME_Key { * @returns {String|GPGME_Error} The armored public Key block. */ get armored(){ - return this.get('armored', true); + if (this.isAsync === true){ + return gpgme_error('KEY_NO_INIT'); + } else { + return this.get('armored'); + } } } @@ -388,15 +392,6 @@ class GPGME_UserId { setProperty(keys[i], data[keys[i]]); } - /** - * Validates a subkey property against {@link validUserIdProperties} and - * sets it if validation is successful - * @param {String} property - * @param {*} value - * @param private - */ - - /** * Fetches information about the user * @param {String} property Information to request -- cgit v1.2.3 From 754e799d35fd62d7a979452f44342934659908c7 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Fri, 17 Aug 2018 14:40:27 +0200 Subject: js: disallow bulk set data on key from outside -- * src/Key.js Key class is not exported anymore, as it should not be used directly anywhere. setKeyData is no more a method of the Key, (optional) data are now validated and set on Key creation and on updates, both from within this module, thus no longer exposing setKeyData to the outside. * createKey now gained an optional parameter which allows to set Key data at this point. --- lang/js/src/Key.js | 142 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 82 insertions(+), 60 deletions(-) (limited to 'lang/js/src/Key.js') diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js index aa419053..8d7fd948 100644 --- a/lang/js/src/Key.js +++ b/lang/js/src/Key.js @@ -31,13 +31,22 @@ import { createMessage } from './Message'; * @param {Boolean} async If True, Key properties (except fingerprint) will be * queried from gnupg on each call, making the operation up-to-date, the * answers will be Promises, and the performance will likely suffer - * @returns {GPGME_Key|GPGME_Error} + * @param {Object} data additional initial properties this Key will have. Needs + * a full object as delivered by gpgme-json + * @returns {Object|GPGME_Error} The verified and updated data */ -export function createKey(fingerprint, async = false){ +export function createKey(fingerprint, async = false, data){ if (!isFingerprint(fingerprint) || typeof(async) !== 'boolean'){ return gpgme_error('PARAM_WRONG'); } - else return Object.freeze(new GPGME_Key(fingerprint, async)); + if (data !== undefined){ + data = validateKeyData(data); + } + if (data instanceof Error){ + return gpgme_error('KEY_INVALID'); + } else { + return Object.freeze(new GPGME_Key(fingerprint, async, data)); + } } /** @@ -49,9 +58,9 @@ export function createKey(fingerprint, async = false){ * * @class */ -export class GPGME_Key { +class GPGME_Key { - constructor(fingerprint, async){ + constructor(fingerprint, async, data){ /** * @property {Boolean} If true, most answers will be asynchronous @@ -59,6 +68,11 @@ export class GPGME_Key { this.isAsync = async; let _data = {fingerprint: fingerprint.toUpperCase()}; + if (data !== undefined + && data.fingerprint.toUpperCase() === _data.fingerprint + ) { + _data = data; + } this.getFingerprint = function(){ if (!_data.fingerprint || !isFingerprint(_data.fingerprint)){ return gpgme_error('KEY_INVALID'); @@ -77,54 +91,6 @@ export class GPGME_Key { return this.get('hasSecret'); }; - /** - * @param {Object} data Bulk set the data for this key, with an Object - * sent by gpgme-json. - * @returns {GPGME_Key|GPGME_Error} Itself after values have been set, - * an error if something went wrong. - * @private - */ - this.setKeyData = function (data){ - if (typeof(data) !== 'object') { - return gpgme_error('KEY_INVALID'); - } - if (!data.fingerprint || - data.fingerprint.toUpperCase() !== _data.fingerprint){ - return gpgme_error('KEY_INVALID'); - } - let keys = Object.keys(data); - for (let i=0; i< keys.length; i++){ - if (!validKeyProperties.hasOwnProperty(keys[i])){ - return gpgme_error('KEY_INVALID'); - } - //running the defined validation function - if (validKeyProperties[keys[i]](data[keys[i]]) !== true ){ - return gpgme_error('KEY_INVALID'); - } - switch (keys[i]){ - case 'subkeys': - _data.subkeys = []; - for (let i=0; i< data.subkeys.length; i++) { - _data.subkeys.push(Object.freeze( - new GPGME_Subkey(data.subkeys[i]))); - } - break; - case 'userids': - _data.userids = []; - for (let i=0; i< data.userids.length; i++) { - _data.userids.push(Object.freeze( - new GPGME_UserId(data.userids[i]))); - } - break; - case 'last_update': - _data[keys[i]] = new Date( data[keys[i]] * 1000 ); - break; - default: - _data[keys[i]] = data[keys[i]]; - } - } - return this; - }; /** * Query any property of the Key listed in {@link validKeyProperties} @@ -188,16 +154,22 @@ export class GPGME_Key { msg.setParameter('keys', _data.fingerprint); msg.post().then(function(result){ if (result.keys.length === 1){ - me.setKeyData(result.keys[0]); - me.getHasSecret().then(function(){ - me.getArmor().then(function(){ - resolve(me); + const newdata = validateKeyData( + _data.fingerprint, result.keys[0]); + if (newdata instanceof Error){ + reject(gpgme_error('KEY_INVALID')); + } else { + _data = newdata; + me.getHasSecret().then(function(){ + me.getArmor().then(function(){ + resolve(me); + }, function(error){ + reject(error); + }); }, function(error){ reject(error); }); - }, function(error){ - reject(error); - }); + } } else { reject(gpgme_error('KEY_NOKEY')); } @@ -602,3 +574,53 @@ const validKeyProperties = { } }; + +/** +* sets the Key data in bulk. It can only be used from inside a Key, either +* during construction or on a refresh callback. +* @param {Object} key the original internal key data. +* @param {Object} data Bulk set the data for this key, with an Object structure +* as sent by gpgme-json. +* @returns {Object|GPGME_Error} the changed data after values have been set, +* an error if something went wrong. +* @private +*/ +function validateKeyData(data){ + const key = {}; + if ( typeof(data) !== 'object' + || !data.fingerprint){ + return gpgme_error('KEY_INVALID'); + } + let props = Object.keys(data); + for (let i=0; i< props.length; i++){ + if (!validKeyProperties.hasOwnProperty(props[i])){ + return gpgme_error('KEY_INVALID'); + } + // running the defined validation function + if (validKeyProperties[props[i]](data[props[i]]) !== true ){ + return gpgme_error('KEY_INVALID'); + } + switch (props[i]){ + case 'subkeys': + key.subkeys = []; + for (let i=0; i< data.subkeys.length; i++) { + key.subkeys.push(Object.freeze( + new GPGME_Subkey(data.subkeys[i]))); + } + break; + case 'userids': + key.userids = []; + for (let i=0; i< data.userids.length; i++) { + key.userids.push(Object.freeze( + new GPGME_UserId(data.userids[i]))); + } + break; + case 'last_update': + key[props[i]] = new Date( data[props[i]] * 1000 ); + break; + default: + key[props[i]] = data[props[i]]; + } + } + return key; +} \ No newline at end of file -- cgit v1.2.3 From ad39d54d192864b54a155bf5f94d5b6bb3e8612a Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Fri, 17 Aug 2018 16:57:41 +0200 Subject: js: removed Key.armor property in synchronous use -- * src/Key.js The synchronous mode for a Key does not offer an armor/ armored property anymore. This frees up a lot of performance issues, also the armored expoort is expected to change quite often, so a cached version is not advisable. * hasSecret/getHasSecret is now refactored, to reflect their uses. With get('hasSecret') there is a method that fetches the result. * src/Key.js also some refactoring --- lang/js/src/Key.js | 218 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 136 insertions(+), 82 deletions(-) (limited to 'lang/js/src/Key.js') diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js index 8d7fd948..5d0c8160 100644 --- a/lang/js/src/Key.js +++ b/lang/js/src/Key.js @@ -80,55 +80,31 @@ class GPGME_Key { return _data.fingerprint; }; - /** - * Property indicating if the Key possesses a private/secret part. If - * this information is not yet cached, it returns an - * {@link GPGME_Error} with code 'KEY_NO_INIT'. Running - * {@link refreshKey} may help in this case. - * @returns {Boolean} If the Key has a secret subkey. - */ - this.hasSecret= function (){ - return this.get('hasSecret'); - }; - - /** * Query any property of the Key listed in {@link validKeyProperties} * @param {String} property property to be retreived - * @returns {*|Promise<*>} the value (Boolean, String, Array, Object). - * If 'cached' is false, the value will be resolved as a Promise. + * @returns {Boolean| String | Date | Array | Object |GPGME_Error} + * the value of the property. If the Key is set to Async, the value + * will be fetched from gnupg and resolved as a Promise. If Key is not + * async, the armored property is not available (it can still be + * retrieved asynchronously by {@link Key.getArmor}) */ this.get = function(property) { if (this.isAsync === true) { - let me = this; - return new Promise(function(resolve, reject) { - if (property === 'armored'){ - resolve(me.getArmor()); - } else if (property === 'hasSecret'){ - resolve(me.getHasSecret()); - } else if (validKeyProperties.hasOwnProperty(property)){ - let msg = createMessage('keylist'); - msg.setParameter('keys', _data.fingerprint); - msg.post().then(function(result){ - if (result.keys && result.keys.length === 1 && - result.keys[0].hasOwnProperty(property)){ - resolve(result.keys[0][property]); - } else { - reject(gpgme_error('CONN_UNEXPECTED_ANSWER')); - } - }, function(error){ - reject(gpgme_error(error)); - }); - } else { - reject(gpgme_error('PARAM_WRONG')); - } - }); + switch (property){ + case 'armored': + return this.getArmor(); + case 'hasSecret': + return this.getGnupgSecretState(); + default: + return getGnupgState(property); + } } else { + if (property === 'armored') { + return gpgme_error('KEY_ASYNC_ONLY'); + } if (!validKeyProperties.hasOwnProperty(property)){ return gpgme_error('PARAM_WRONG'); - } - if (!_data.hasOwnProperty(property)){ - return gpgme_error('KEY_NO_INIT'); } else { return (_data[property]); } @@ -160,7 +136,7 @@ class GPGME_Key { reject(gpgme_error('KEY_INVALID')); } else { _data = newdata; - me.getHasSecret().then(function(){ + me.getGnupgSecretState().then(function(){ me.getArmor().then(function(){ resolve(me); }, function(error){ @@ -195,7 +171,6 @@ class GPGME_Key { msg.setParameter('armor', true); msg.setParameter('keys', _data.fingerprint); msg.post().then(function(result){ - _data.armored = result.data; resolve(result.data); }, function(error){ reject(error); @@ -205,37 +180,38 @@ class GPGME_Key { /** * Find out if the Key includes a secret part. Note that this is a - * rather nonperformant operation, as it needs to query gnupg twice. + * rather nonperformant operation. * If you want this inforrmation about more than a few Keys, it may be * advisable to run {@link Keyring.getKeys} instead. * @returns {Promise} True if a secret/private Key * is available in the gnupg Keyring * @async */ - this.getHasSecret = function (){ + this.getGnupgSecretState = function (){ return new Promise(function(resolve, reject) { if (!_data.fingerprint){ reject(gpgme_error('KEY_INVALID')); + } else { + let msg = createMessage('keylist'); + msg.setParameter('keys', _data.fingerprint); + msg.setParameter('secret', true); + msg.post().then(function(result){ + _data.hasSecret = null; + if ( + result.keys && + result.keys.length === 1 && + result.keys[0].secret === true + ) { + _data.hasSecret = true; + resolve(true); + } else { + _data.hasSecret = false; + resolve(false); + } + }, function(error){ + reject(error); + }); } - let msg = createMessage('keylist'); - msg.setParameter('keys', _data.fingerprint); - msg.setParameter('secret', true); - msg.post().then(function(result){ - _data.hasSecret = null; - if ( - result.keys && - result.keys.length === 1 && - result.keys[0].secret === true - ) { - _data.hasSecret = true; - resolve(true); - } else { - _data.hasSecret = false; - resolve(false); - } - }, function(error){ - reject(error); - }); }); }; @@ -262,25 +238,11 @@ class GPGME_Key { } /** - * @returns {String} The fingerprint defining this Key + * @returns {String} The fingerprint defining this Key. Convenience getter */ get fingerprint(){ return this.getFingerprint(); } - - /** - * Property for the export of armored Key. If the armored Key is not - * cached, it returns an {@link GPGME_Error} with code 'KEY_NO_INIT'. - * Running {@link refreshKey} may help in this case. - * @returns {String|GPGME_Error} The armored public Key block. - */ - get armored(){ - if (this.isAsync === true){ - return gpgme_error('KEY_NO_INIT'); - } else { - return this.get('armored'); - } - } } /** @@ -496,7 +458,31 @@ const validSubKeyProperties = { /** * Validation definition for Keys. Each valid Key property is represented - * as a key-value pair, with their value being a validation function + * as a key-value pair, with their value being a validation function. For + * details on the meanings, please refer to the gpgme documentation + * https://www.gnupg.org/documentation/manuals/gpgme/Key-objects.html#Key-objects + * @param {String} fingerprint + * @param {Boolean} revoked + * @param {Boolean} expired + * @param {Boolean} disabled + * @param {Boolean} invalid + * @param {Boolean} can_encrypt + * @param {Boolean} can_sign + * @param {Boolean} can_certify + * @param {Boolean} can_authenticate + * @param {Boolean} secret + * @param {Boolean}is_qualified + * @param {String} protocol + * @param {String} issuer_serial + * @param {String} issuer_name + * @param {Boolean} chain_id + * @param {String} owner_trust + * @param {Date} last_update + * @param {String} origin + * @param {Array} subkeys + * @param {Array} userids + * @param {Array} tofu + * @param {Boolean} hasSecret * @protected * @const */ @@ -504,9 +490,6 @@ const validKeyProperties = { 'fingerprint': function(value){ return isFingerprint(value); }, - 'armored': function(value){ - return typeof(value === 'string'); - }, 'revoked': function(value){ return typeof(value) === 'boolean'; }, @@ -623,4 +606,75 @@ function validateKeyData(data){ } } return key; +} + +/** + * Fetches and sets properties from gnupg + * @param {String} fingerprint + * @param {String} property to search for. + * @private + * @async + */ +function getGnupgState (fingerprint, property){ + return new Promise(function(resolve, reject) { + if (!isFingerprint(fingerprint)) { + reject(gpgme_error('KEY_INVALID')); + } else { + let msg = createMessage('keylist'); + msg.setParameter('keys', fingerprint); + msg.post().then(function(result){ + if (!result.keys || result.keys.length !== 1){ + reject(gpgme_error('KEY_INVALID')); + } else { + const key = result.keys[0]; + let result; + switch (property){ + case 'subkeys': + result = []; + if (key.subkeys.length){ + for (let i=0; i < key.subkeys.length; i++) { + result.push(Object.freeze( + new GPGME_Subkey(key.subkeys[i]))); + } + } + resolve(result); + break; + case 'userids': + result = []; + if (key.userids.length){ + for (let i=0; i< key.userids.length; i++) { + result.push(Object.freeze( + new GPGME_UserId(key.userids[i]))); + } + } + resolve(result); + break; + case 'last_update': + if (key.last_update === undefined){ + reject(gpgme_error('CONN_UNEXPECTED_ANSWER')); + } else if (key.last_update !== null){ + resolve(new Date( key.last_update * 1000)); + } else { + resolve(null); + } + break; + default: + if (!validKeyProperties.hasOwnProperty(property)){ + reject(gpgme_error('PARAM_WRONG')); + } else { + if (key.hasOwnProperty(property)){ + resolve(key[property]); + } else { + reject(gpgme_error( + 'CONN_UNEXPECTED_ANSWER')); + } + } + break; + } + } + }, function(error){ + reject(gpgme_error(error)); + }); + } + }); } \ No newline at end of file -- cgit v1.2.3 From 3fb094a9b8c320fc10e537a9bb5fab34807f4e52 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Fri, 17 Aug 2018 17:14:51 +0200 Subject: js: small documentation fix -- --- lang/js/src/Key.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'lang/js/src/Key.js') diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js index 5d0c8160..ea6fd883 100644 --- a/lang/js/src/Key.js +++ b/lang/js/src/Key.js @@ -179,12 +179,13 @@ class GPGME_Key { }; /** - * Find out if the Key includes a secret part. Note that this is a - * rather nonperformant operation. - * If you want this inforrmation about more than a few Keys, it may be - * advisable to run {@link Keyring.getKeys} instead. - * @returns {Promise} True if a secret/private Key - * is available in the gnupg Keyring + * Find out if the Key is part of a Key pair including public and + * private key(s). If you want this information about more than a few + * Keys in synchronous mode, it may be advisable to run + * {@link Keyring.getKeys} instead, as it performs faster in bulk + * querying this state. + * @returns {Promise} True if a private Key is + * available in the gnupg Keyring. * @async */ this.getGnupgSecretState = function (){ -- cgit v1.2.3 From 1954d27be86b8e4eb801ca6ddcb670f8cfb149f5 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Mon, 20 Aug 2018 12:12:43 +0200 Subject: js: revert changes to class read/write restriction -- * undoes 94ee0988d4eaac27785de6efb7c19ca9976e1e9c and e16a87e83910ebb6bfdc4148369165f121f0997e. I do not fully understand why my approach was bad, but I am not in a position to argue. This revert was requested to me after a review, and I'm doing it in the assumption that more experienced people know better than me. * unittests: Also changed some outdated tests that stopped working since 754e799d35fd62d7a979452f44342934659908c7 (as GPGME_Key is not exported, one cannot check for instanceof in the tests anymore) --- lang/js/src/Key.js | 386 +++++++++++++++++++++++++++-------------------------- 1 file changed, 194 insertions(+), 192 deletions(-) (limited to 'lang/js/src/Key.js') diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js index ea6fd883..37ec7f9d 100644 --- a/lang/js/src/Key.js +++ b/lang/js/src/Key.js @@ -40,12 +40,12 @@ export function createKey(fingerprint, async = false, data){ return gpgme_error('PARAM_WRONG'); } if (data !== undefined){ - data = validateKeyData(data); + data = validateKeyData(fingerprint, data); } if (data instanceof Error){ return gpgme_error('KEY_INVALID'); } else { - return Object.freeze(new GPGME_Key(fingerprint, async, data)); + return new GPGME_Key(fingerprint, async, data); } } @@ -65,184 +65,181 @@ class GPGME_Key { /** * @property {Boolean} If true, most answers will be asynchronous */ - this.isAsync = async; + this._async = async; - let _data = {fingerprint: fingerprint.toUpperCase()}; + this._data = {fingerprint: fingerprint.toUpperCase()}; if (data !== undefined - && data.fingerprint.toUpperCase() === _data.fingerprint + && data.fingerprint.toUpperCase() === this._data.fingerprint ) { - _data = data; + this._data = data; } - this.getFingerprint = function(){ - if (!_data.fingerprint || !isFingerprint(_data.fingerprint)){ - return gpgme_error('KEY_INVALID'); - } - return _data.fingerprint; - }; + } - /** - * Query any property of the Key listed in {@link validKeyProperties} - * @param {String} property property to be retreived - * @returns {Boolean| String | Date | Array | Object |GPGME_Error} - * the value of the property. If the Key is set to Async, the value - * will be fetched from gnupg and resolved as a Promise. If Key is not - * async, the armored property is not available (it can still be - * retrieved asynchronously by {@link Key.getArmor}) - */ - this.get = function(property) { - if (this.isAsync === true) { - switch (property){ - case 'armored': - return this.getArmor(); - case 'hasSecret': - return this.getGnupgSecretState(); - default: - return getGnupgState(property); - } + /** + * Query any property of the Key listed in {@link validKeyProperties} + * @param {String} property property to be retreived + * @returns {Boolean| String | Date | Array | Object |GPGME_Error} + * the value of the property. If the Key is set to Async, the value + * will be fetched from gnupg and resolved as a Promise. If Key is not + * async, the armored property is not available (it can still be + * retrieved asynchronously by {@link Key.getArmor}) + */ + get(property) { + if (this._async === true) { + switch (property){ + case 'armored': + return this.getArmor(); + case 'hasSecret': + return this.getGnupgSecretState(); + default: + return getGnupgState(this.fingerprint, property); + } + } else { + if (property === 'armored') { + return gpgme_error('KEY_ASYNC_ONLY'); + } + if (!validKeyProperties.hasOwnProperty(property)){ + return gpgme_error('PARAM_WRONG'); } else { - if (property === 'armored') { - return gpgme_error('KEY_ASYNC_ONLY'); - } - if (!validKeyProperties.hasOwnProperty(property)){ - return gpgme_error('PARAM_WRONG'); - } else { - return (_data[property]); - } + return (this._data[property]); } - }; + } + } - /** - * Reloads the Key information from gnupg. This is only useful if you - * use the GPGME_Keys cached. Note that this is a performance hungry - * operation. If you desire more than a few refreshs, it may be - * advisable to run {@link Keyring.getKeys} instead. - * @returns {Promise} - * @async - */ - this.refreshKey = function() { - let me = this; - return new Promise(function(resolve, reject) { - if (!_data.fingerprint){ - reject(gpgme_error('KEY_INVALID')); - } - let msg = createMessage('keylist'); - msg.setParameter('sigs', true); - msg.setParameter('keys', _data.fingerprint); - msg.post().then(function(result){ - if (result.keys.length === 1){ - const newdata = validateKeyData( - _data.fingerprint, result.keys[0]); - if (newdata instanceof Error){ - reject(gpgme_error('KEY_INVALID')); - } else { - _data = newdata; - me.getGnupgSecretState().then(function(){ - me.getArmor().then(function(){ - resolve(me); - }, function(error){ - reject(error); - }); + /** + * Reloads the Key information from gnupg. This is only useful if you + * use the GPGME_Keys cached. Note that this is a performance hungry + * operation. If you desire more than a few refreshs, it may be + * advisable to run {@link Keyring.getKeys} instead. + * @returns {Promise} + * @async + */ + refreshKey() { + let me = this; + return new Promise(function(resolve, reject) { + if (!me._data.fingerprint){ + reject(gpgme_error('KEY_INVALID')); + } + let msg = createMessage('keylist'); + msg.setParameter('sigs', true); + msg.setParameter('keys', me._data.fingerprint); + msg.post().then(function(result){ + if (result.keys.length === 1){ + const newdata = validateKeyData( + me._data.fingerprint, result.keys[0]); + if (newdata instanceof Error){ + reject(gpgme_error('KEY_INVALID')); + } else { + me._data = newdata; + me.getGnupgSecretState().then(function(){ + me.getArmor().then(function(){ + resolve(me); }, function(error){ reject(error); }); - } - } else { - reject(gpgme_error('KEY_NOKEY')); + }, function(error){ + reject(error); + }); } - }, function (error) { - reject(gpgme_error('GNUPG_ERROR'), error); - }); - }); - }; - - /** - * Query the armored block of the Key directly from gnupg. Please note - * that this will not get you any export of the secret/private parts of - * a Key - * @returns {Promise} - * @async - */ - this.getArmor = function(){ - return new Promise(function(resolve, reject) { - if (!_data.fingerprint){ - reject(gpgme_error('KEY_INVALID')); + } else { + reject(gpgme_error('KEY_NOKEY')); } - let msg = createMessage('export'); - msg.setParameter('armor', true); - msg.setParameter('keys', _data.fingerprint); - msg.post().then(function(result){ - resolve(result.data); - }, function(error){ - reject(error); - }); + }, function (error) { + reject(gpgme_error('GNUPG_ERROR'), error); }); - }; + }); + } - /** - * Find out if the Key is part of a Key pair including public and - * private key(s). If you want this information about more than a few - * Keys in synchronous mode, it may be advisable to run - * {@link Keyring.getKeys} instead, as it performs faster in bulk - * querying this state. - * @returns {Promise} True if a private Key is - * available in the gnupg Keyring. - * @async - */ - this.getGnupgSecretState = function (){ - return new Promise(function(resolve, reject) { - if (!_data.fingerprint){ - reject(gpgme_error('KEY_INVALID')); - } else { - let msg = createMessage('keylist'); - msg.setParameter('keys', _data.fingerprint); - msg.setParameter('secret', true); - msg.post().then(function(result){ - _data.hasSecret = null; - if ( - result.keys && - result.keys.length === 1 && - result.keys[0].secret === true - ) { - _data.hasSecret = true; - resolve(true); - } else { - _data.hasSecret = false; - resolve(false); - } - }, function(error){ - reject(error); - }); - } + /** + * Query the armored block of the Key directly from gnupg. Please note + * that this will not get you any export of the secret/private parts of + * a Key + * @returns {Promise} + * @async + */ + getArmor() { + const me = this; + return new Promise(function(resolve, reject) { + if (!me._data.fingerprint){ + reject(gpgme_error('KEY_INVALID')); + } + let msg = createMessage('export'); + msg.setParameter('armor', true); + msg.setParameter('keys', me._data.fingerprint); + msg.post().then(function(result){ + resolve(result.data); + }, function(error){ + reject(error); }); - }; + }); + } - /** - * Deletes the (public) Key from the GPG Keyring. Note that a deletion - * of a secret key is not supported by the native backend. - * @returns {Promise} Success if key was deleted, - * rejects with a GPG error otherwise. - */ - this.delete= function (){ - return new Promise(function(resolve, reject){ - if (!_data.fingerprint){ - reject(gpgme_error('KEY_INVALID')); - } - let msg = createMessage('delete'); - msg.setParameter('key', _data.fingerprint); + /** + * Find out if the Key is part of a Key pair including public and + * private key(s). If you want this information about more than a few + * Keys in synchronous mode, it may be advisable to run + * {@link Keyring.getKeys} instead, as it performs faster in bulk + * querying this state. + * @returns {Promise} True if a private Key is + * available in the gnupg Keyring. + * @async + */ + getGnupgSecretState (){ + const me = this; + return new Promise(function(resolve, reject) { + if (!me._data.fingerprint){ + reject(gpgme_error('KEY_INVALID')); + } else { + let msg = createMessage('keylist'); + msg.setParameter('keys', me._data.fingerprint); + msg.setParameter('secret', true); msg.post().then(function(result){ - resolve(result.success); + me._data.hasSecret = null; + if ( + result.keys && + result.keys.length === 1 && + result.keys[0].secret === true + ) { + me._data.hasSecret = true; + resolve(true); + } else { + me._data.hasSecret = false; + resolve(false); + } }, function(error){ reject(error); }); + } + }); + } + + /** + * Deletes the (public) Key from the GPG Keyring. Note that a deletion + * of a secret key is not supported by the native backend. + * @returns {Promise} Success if key was deleted, + * rejects with a GPG error otherwise. + */ + delete(){ + const me = this; + return new Promise(function(resolve, reject){ + if (!me._data.fingerprint){ + reject(gpgme_error('KEY_INVALID')); + } + let msg = createMessage('delete'); + msg.setParameter('key', me._data.fingerprint); + msg.post().then(function(result){ + resolve(result.success); + }, function(error){ + reject(error); }); - }; + }); } /** * @returns {String} The fingerprint defining this Key. Convenience getter */ get fingerprint(){ - return this.getFingerprint(); + return this._data.fingerprint; } } @@ -259,8 +256,9 @@ class GPGME_Subkey { * @private */ constructor(data){ - let _data = {}; + this._data = {}; let keys = Object.keys(data); + const me = this; /** * Validates a subkey property against {@link validSubKeyProperties} and @@ -273,9 +271,9 @@ class GPGME_Subkey { if (validSubKeyProperties.hasOwnProperty(property)){ if (validSubKeyProperties[property](value) === true) { if (property === 'timestamp' || property === 'expires'){ - _data[property] = new Date(value * 1000); + me._data[property] = new Date(value * 1000); } else { - _data[property] = value; + me._data[property] = value; } } } @@ -283,18 +281,19 @@ class GPGME_Subkey { for (let i=0; i< keys.length; i++) { setProperty(keys[i], data[keys[i]]); } + } - /** - * Fetches any information about this subkey - * @param {String} property Information to request - * @returns {String | Number | Date} - */ - this.get = function(property) { - if (_data.hasOwnProperty(property)){ - return (_data[property]); - } - }; + /** + * Fetches any information about this subkey + * @param {String} property Information to request + * @returns {String | Number | Date} + */ + get(property) { + if (this._data.hasOwnProperty(property)){ + return (this._data[property]); + } } + } /** @@ -310,15 +309,16 @@ class GPGME_UserId { * @private */ constructor(data){ - let _data = {}; + this._data = {}; + const me = this; let keys = Object.keys(data); const setProperty = function(property, value){ if (validUserIdProperties.hasOwnProperty(property)){ if (validUserIdProperties[property](value) === true) { if (property === 'last_update'){ - _data[property] = new Date(value*1000); + me._data[property] = new Date(value*1000); } else { - _data[property] = value; + me._data[property] = value; } } } @@ -326,18 +326,19 @@ class GPGME_UserId { for (let i=0; i< keys.length; i++) { setProperty(keys[i], data[keys[i]]); } + } - /** - * Fetches information about the user - * @param {String} property Information to request - * @returns {String | Number} - */ - this.get = function (property) { - if (_data.hasOwnProperty(property)){ - return (_data[property]); - } - }; + /** + * Fetches information about the user + * @param {String} property Information to request + * @returns {String | Number} + */ + get(property) { + if (this._data.hasOwnProperty(property)){ + return (this._data[property]); + } } + } /** @@ -569,10 +570,11 @@ const validKeyProperties = { * an error if something went wrong. * @private */ -function validateKeyData(data){ +function validateKeyData(fingerprint, data){ const key = {}; - if ( typeof(data) !== 'object' - || !data.fingerprint){ + if (!fingerprint || typeof(data) !== 'object' || !data.fingerprint + || fingerprint !== data.fingerprint.toUpperCase() + ){ return gpgme_error('KEY_INVALID'); } let props = Object.keys(data); @@ -588,15 +590,15 @@ function validateKeyData(data){ case 'subkeys': key.subkeys = []; for (let i=0; i< data.subkeys.length; i++) { - key.subkeys.push(Object.freeze( - new GPGME_Subkey(data.subkeys[i]))); + key.subkeys.push( + new GPGME_Subkey(data.subkeys[i])); } break; case 'userids': key.userids = []; for (let i=0; i< data.userids.length; i++) { - key.userids.push(Object.freeze( - new GPGME_UserId(data.userids[i]))); + key.userids.push( + new GPGME_UserId(data.userids[i])); } break; case 'last_update': @@ -623,19 +625,19 @@ function getGnupgState (fingerprint, property){ } else { let msg = createMessage('keylist'); msg.setParameter('keys', fingerprint); - msg.post().then(function(result){ - if (!result.keys || result.keys.length !== 1){ + msg.post().then(function(res){ + if (!res.keys || res.keys.length !== 1){ reject(gpgme_error('KEY_INVALID')); } else { - const key = result.keys[0]; + const key = res.keys[0]; let result; switch (property){ case 'subkeys': result = []; if (key.subkeys.length){ for (let i=0; i < key.subkeys.length; i++) { - result.push(Object.freeze( - new GPGME_Subkey(key.subkeys[i]))); + result.push( + new GPGME_Subkey(key.subkeys[i])); } } resolve(result); @@ -644,8 +646,8 @@ function getGnupgState (fingerprint, property){ result = []; if (key.userids.length){ for (let i=0; i< key.userids.length; i++) { - result.push(Object.freeze( - new GPGME_UserId(key.userids[i]))); + result.push( + new GPGME_UserId(key.userids[i])); } } resolve(result); -- cgit v1.2.3 From dd32daad0bb21e3d5567326d0b2e548ff8510431 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Mon, 20 Aug 2018 15:12:01 +0200 Subject: js: add and apply eslint rules -- * mainly spacing, see .eslintrc.json for details --- lang/js/src/Key.js | 243 +++++++++++++++++++++++++++-------------------------- 1 file changed, 124 insertions(+), 119 deletions(-) (limited to 'lang/js/src/Key.js') diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js index 37ec7f9d..2800ae9a 100644 --- a/lang/js/src/Key.js +++ b/lang/js/src/Key.js @@ -35,8 +35,8 @@ import { createMessage } from './Message'; * a full object as delivered by gpgme-json * @returns {Object|GPGME_Error} The verified and updated data */ -export function createKey(fingerprint, async = false, data){ - if (!isFingerprint(fingerprint) || typeof(async) !== 'boolean'){ +export function createKey (fingerprint, async = false, data){ + if (!isFingerprint(fingerprint) || typeof (async) !== 'boolean'){ return gpgme_error('PARAM_WRONG'); } if (data !== undefined){ @@ -60,14 +60,14 @@ export function createKey(fingerprint, async = false, data){ */ class GPGME_Key { - constructor(fingerprint, async, data){ + constructor (fingerprint, async, data){ /** * @property {Boolean} If true, most answers will be asynchronous */ this._async = async; - this._data = {fingerprint: fingerprint.toUpperCase()}; + this._data = { fingerprint: fingerprint.toUpperCase() }; if (data !== undefined && data.fingerprint.toUpperCase() === this._data.fingerprint ) { @@ -84,7 +84,7 @@ class GPGME_Key { * async, the armored property is not available (it can still be * retrieved asynchronously by {@link Key.getArmor}) */ - get(property) { + get (property) { if (this._async === true) { switch (property){ case 'armored': @@ -98,6 +98,7 @@ class GPGME_Key { if (property === 'armored') { return gpgme_error('KEY_ASYNC_ONLY'); } + // eslint-disable-next-line no-use-before-define if (!validKeyProperties.hasOwnProperty(property)){ return gpgme_error('PARAM_WRONG'); } else { @@ -114,16 +115,16 @@ class GPGME_Key { * @returns {Promise} * @async */ - refreshKey() { + refreshKey () { let me = this; - return new Promise(function(resolve, reject) { + return new Promise(function (resolve, reject) { if (!me._data.fingerprint){ reject(gpgme_error('KEY_INVALID')); } let msg = createMessage('keylist'); msg.setParameter('sigs', true); msg.setParameter('keys', me._data.fingerprint); - msg.post().then(function(result){ + msg.post().then(function (result){ if (result.keys.length === 1){ const newdata = validateKeyData( me._data.fingerprint, result.keys[0]); @@ -131,13 +132,13 @@ class GPGME_Key { reject(gpgme_error('KEY_INVALID')); } else { me._data = newdata; - me.getGnupgSecretState().then(function(){ - me.getArmor().then(function(){ + me.getGnupgSecretState().then(function (){ + me.getArmor().then(function (){ resolve(me); - }, function(error){ + }, function (error){ reject(error); }); - }, function(error){ + }, function (error){ reject(error); }); } @@ -157,18 +158,18 @@ class GPGME_Key { * @returns {Promise} * @async */ - getArmor() { + getArmor () { const me = this; - return new Promise(function(resolve, reject) { + return new Promise(function (resolve, reject) { if (!me._data.fingerprint){ reject(gpgme_error('KEY_INVALID')); } let msg = createMessage('export'); msg.setParameter('armor', true); msg.setParameter('keys', me._data.fingerprint); - msg.post().then(function(result){ + msg.post().then(function (result){ resolve(result.data); - }, function(error){ + }, function (error){ reject(error); }); }); @@ -186,14 +187,14 @@ class GPGME_Key { */ getGnupgSecretState (){ const me = this; - return new Promise(function(resolve, reject) { + return new Promise(function (resolve, reject) { if (!me._data.fingerprint){ reject(gpgme_error('KEY_INVALID')); } else { let msg = createMessage('keylist'); msg.setParameter('keys', me._data.fingerprint); msg.setParameter('secret', true); - msg.post().then(function(result){ + msg.post().then(function (result){ me._data.hasSecret = null; if ( result.keys && @@ -206,7 +207,7 @@ class GPGME_Key { me._data.hasSecret = false; resolve(false); } - }, function(error){ + }, function (error){ reject(error); }); } @@ -219,17 +220,17 @@ class GPGME_Key { * @returns {Promise} Success if key was deleted, * rejects with a GPG error otherwise. */ - delete(){ + delete (){ const me = this; - return new Promise(function(resolve, reject){ + return new Promise(function (resolve, reject){ if (!me._data.fingerprint){ reject(gpgme_error('KEY_INVALID')); } let msg = createMessage('delete'); msg.setParameter('key', me._data.fingerprint); - msg.post().then(function(result){ + msg.post().then(function (result){ resolve(result.success); - }, function(error){ + }, function (error){ reject(error); }); }); @@ -238,7 +239,7 @@ class GPGME_Key { /** * @returns {String} The fingerprint defining this Key. Convenience getter */ - get fingerprint(){ + get fingerprint (){ return this._data.fingerprint; } } @@ -255,7 +256,7 @@ class GPGME_Subkey { * @param {Object} data * @private */ - constructor(data){ + constructor (data){ this._data = {}; let keys = Object.keys(data); const me = this; @@ -268,7 +269,9 @@ class GPGME_Subkey { * @param private */ const setProperty = function (property, value){ + // eslint-disable-next-line no-use-before-define if (validSubKeyProperties.hasOwnProperty(property)){ + // eslint-disable-next-line no-use-before-define if (validSubKeyProperties[property](value) === true) { if (property === 'timestamp' || property === 'expires'){ me._data[property] = new Date(value * 1000); @@ -288,7 +291,7 @@ class GPGME_Subkey { * @param {String} property Information to request * @returns {String | Number | Date} */ - get(property) { + get (property) { if (this._data.hasOwnProperty(property)){ return (this._data[property]); } @@ -308,12 +311,14 @@ class GPGME_UserId { * @param {Object} data * @private */ - constructor(data){ + constructor (data){ this._data = {}; const me = this; let keys = Object.keys(data); - const setProperty = function(property, value){ + const setProperty = function (property, value){ + // eslint-disable-next-line no-use-before-define if (validUserIdProperties.hasOwnProperty(property)){ + // eslint-disable-next-line no-use-before-define if (validUserIdProperties[property](value) === true) { if (property === 'last_update'){ me._data[property] = new Date(value*1000); @@ -333,7 +338,7 @@ class GPGME_UserId { * @param {String} property Information to request * @returns {String | Number} */ - get(property) { + get (property) { if (this._data.hasOwnProperty(property)){ return (this._data[property]); } @@ -349,52 +354,52 @@ class GPGME_UserId { * @const */ const validUserIdProperties = { - 'revoked': function(value){ - return typeof(value) === 'boolean'; + 'revoked': function (value){ + return typeof (value) === 'boolean'; }, - 'invalid': function(value){ - return typeof(value) === 'boolean'; + 'invalid': function (value){ + return typeof (value) === 'boolean'; }, - 'uid': function(value){ - if (typeof(value) === 'string' || value === ''){ + 'uid': function (value){ + if (typeof (value) === 'string' || value === ''){ return true; } return false; }, - 'validity': function(value){ - if (typeof(value) === 'string'){ + 'validity': function (value){ + if (typeof (value) === 'string'){ return true; } return false; }, - 'name': function(value){ - if (typeof(value) === 'string' || value === ''){ + 'name': function (value){ + if (typeof (value) === 'string' || value === ''){ return true; } return false; }, - 'email': function(value){ - if (typeof(value) === 'string' || value === ''){ + 'email': function (value){ + if (typeof (value) === 'string' || value === ''){ return true; } return false; }, - 'address': function(value){ - if (typeof(value) === 'string' || value === ''){ + 'address': function (value){ + if (typeof (value) === 'string' || value === ''){ return true; } return false; }, - 'comment': function(value){ - if (typeof(value) === 'string' || value === ''){ + 'comment': function (value){ + if (typeof (value) === 'string' || value === ''){ return true; } return false; }, - 'origin': function(value){ + 'origin': function (value){ return Number.isInteger(value); }, - 'last_update': function(value){ + 'last_update': function (value){ return Number.isInteger(value); } }; @@ -406,54 +411,54 @@ const validUserIdProperties = { * @const */ const validSubKeyProperties = { - 'invalid': function(value){ - return typeof(value) === 'boolean'; + 'invalid': function (value){ + return typeof (value) === 'boolean'; }, - 'can_encrypt': function(value){ - return typeof(value) === 'boolean'; + 'can_encrypt': function (value){ + return typeof (value) === 'boolean'; }, - 'can_sign': function(value){ - return typeof(value) === 'boolean'; + 'can_sign': function (value){ + return typeof (value) === 'boolean'; }, - 'can_certify': function(value){ - return typeof(value) === 'boolean'; + 'can_certify': function (value){ + return typeof (value) === 'boolean'; }, - 'can_authenticate': function(value){ - return typeof(value) === 'boolean'; + 'can_authenticate': function (value){ + return typeof (value) === 'boolean'; }, - 'secret': function(value){ - return typeof(value) === 'boolean'; + 'secret': function (value){ + return typeof (value) === 'boolean'; }, - 'is_qualified': function(value){ - return typeof(value) === 'boolean'; + 'is_qualified': function (value){ + return typeof (value) === 'boolean'; }, - 'is_cardkey': function(value){ - return typeof(value) === 'boolean'; + 'is_cardkey': function (value){ + return typeof (value) === 'boolean'; }, - 'is_de_vs': function(value){ - return typeof(value) === 'boolean'; + 'is_de_vs': function (value){ + return typeof (value) === 'boolean'; }, - 'pubkey_algo_name': function(value){ - return typeof(value) === 'string'; + 'pubkey_algo_name': function (value){ + return typeof (value) === 'string'; // TODO: check against list of known?[''] }, - 'pubkey_algo_string': function(value){ - return typeof(value) === 'string'; + 'pubkey_algo_string': function (value){ + return typeof (value) === 'string'; // TODO: check against list of known?[''] }, - 'keyid': function(value){ + 'keyid': function (value){ return isLongId(value); }, - 'pubkey_algo': function(value) { + 'pubkey_algo': function (value) { return (Number.isInteger(value) && value >= 0); }, - 'length': function(value){ + 'length': function (value){ return (Number.isInteger(value) && value > 0); }, - 'timestamp': function(value){ + 'timestamp': function (value){ return (Number.isInteger(value) && value > 0); }, - 'expires': function(value){ + 'expires': function (value){ return (Number.isInteger(value) && value > 0); } }; @@ -489,73 +494,73 @@ const validSubKeyProperties = { * @const */ const validKeyProperties = { - 'fingerprint': function(value){ + 'fingerprint': function (value){ return isFingerprint(value); }, - 'revoked': function(value){ - return typeof(value) === 'boolean'; + 'revoked': function (value){ + return typeof (value) === 'boolean'; }, - 'expired': function(value){ - return typeof(value) === 'boolean'; + 'expired': function (value){ + return typeof (value) === 'boolean'; }, - 'disabled': function(value){ - return typeof(value) === 'boolean'; + 'disabled': function (value){ + return typeof (value) === 'boolean'; }, - 'invalid': function(value){ - return typeof(value) === 'boolean'; + 'invalid': function (value){ + return typeof (value) === 'boolean'; }, - 'can_encrypt': function(value){ - return typeof(value) === 'boolean'; + 'can_encrypt': function (value){ + return typeof (value) === 'boolean'; }, - 'can_sign': function(value){ - return typeof(value) === 'boolean'; + 'can_sign': function (value){ + return typeof (value) === 'boolean'; }, - 'can_certify': function(value){ - return typeof(value) === 'boolean'; + 'can_certify': function (value){ + return typeof (value) === 'boolean'; }, - 'can_authenticate': function(value){ - return typeof(value) === 'boolean'; + 'can_authenticate': function (value){ + return typeof (value) === 'boolean'; }, - 'secret': function(value){ - return typeof(value) === 'boolean'; + 'secret': function (value){ + return typeof (value) === 'boolean'; }, - 'is_qualified': function(value){ - return typeof(value) === 'boolean'; + 'is_qualified': function (value){ + return typeof (value) === 'boolean'; }, - 'protocol': function(value){ - return typeof(value) === 'string'; - //TODO check for implemented ones + 'protocol': function (value){ + return typeof (value) === 'string'; + // TODO check for implemented ones }, - 'issuer_serial': function(value){ - return typeof(value) === 'string'; + 'issuer_serial': function (value){ + return typeof (value) === 'string'; }, - 'issuer_name': function(value){ - return typeof(value) === 'string'; + 'issuer_name': function (value){ + return typeof (value) === 'string'; }, - 'chain_id': function(value){ - return typeof(value) === 'string'; + 'chain_id': function (value){ + return typeof (value) === 'string'; }, - 'owner_trust': function(value){ - return typeof(value) === 'string'; + 'owner_trust': function (value){ + return typeof (value) === 'string'; }, - 'last_update': function(value){ + 'last_update': function (value){ return (Number.isInteger(value)); - //TODO undefined/null possible? + // TODO undefined/null possible? }, - 'origin': function(value){ + 'origin': function (value){ return (Number.isInteger(value)); }, - 'subkeys': function(value){ + 'subkeys': function (value){ return (Array.isArray(value)); }, - 'userids': function(value){ + 'userids': function (value){ return (Array.isArray(value)); }, - 'tofu': function(value){ + 'tofu': function (value){ return (Array.isArray(value)); }, - 'hasSecret': function(value){ - return typeof(value) === 'boolean'; + 'hasSecret': function (value){ + return typeof (value) === 'boolean'; } }; @@ -570,9 +575,9 @@ const validKeyProperties = { * an error if something went wrong. * @private */ -function validateKeyData(fingerprint, data){ +function validateKeyData (fingerprint, data){ const key = {}; - if (!fingerprint || typeof(data) !== 'object' || !data.fingerprint + if (!fingerprint || typeof (data) !== 'object' || !data.fingerprint || fingerprint !== data.fingerprint.toUpperCase() ){ return gpgme_error('KEY_INVALID'); @@ -619,13 +624,13 @@ function validateKeyData(fingerprint, data){ * @async */ function getGnupgState (fingerprint, property){ - return new Promise(function(resolve, reject) { + return new Promise(function (resolve, reject) { if (!isFingerprint(fingerprint)) { reject(gpgme_error('KEY_INVALID')); } else { let msg = createMessage('keylist'); msg.setParameter('keys', fingerprint); - msg.post().then(function(res){ + msg.post().then(function (res){ if (!res.keys || res.keys.length !== 1){ reject(gpgme_error('KEY_INVALID')); } else { @@ -675,7 +680,7 @@ function getGnupgState (fingerprint, property){ break; } } - }, function(error){ + }, function (error){ reject(gpgme_error(error)); }); } -- cgit v1.2.3 From 93f674d33d4dacb115398196a7218c28323fd708 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Wed, 22 Aug 2018 12:18:55 +0200 Subject: js: throw errors in sync functions -- * synchronous functions should throw errors if something goes wrong, Promises should reject. This commit changes some error cases that returned Error objects instead of throwing them - src/Key.js: createKey() and sync Key.get() throw errors - src/Error.js: Exporting the list of errors to be able to test and compare against these strings - src/Keyring.js: Setting a null value in pattern is not useful, and now caused an error with the new changes. - src/Message.js: createMessage and Message.setParameter now throw errors --- lang/js/src/Key.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'lang/js/src/Key.js') diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js index 2800ae9a..d0f87eda 100644 --- a/lang/js/src/Key.js +++ b/lang/js/src/Key.js @@ -33,17 +33,17 @@ import { createMessage } from './Message'; * answers will be Promises, and the performance will likely suffer * @param {Object} data additional initial properties this Key will have. Needs * a full object as delivered by gpgme-json - * @returns {Object|GPGME_Error} The verified and updated data + * @returns {Object} The verified and updated data */ export function createKey (fingerprint, async = false, data){ if (!isFingerprint(fingerprint) || typeof (async) !== 'boolean'){ - return gpgme_error('PARAM_WRONG'); + throw gpgme_error('PARAM_WRONG'); } if (data !== undefined){ data = validateKeyData(fingerprint, data); } if (data instanceof Error){ - return gpgme_error('KEY_INVALID'); + throw gpgme_error('KEY_INVALID'); } else { return new GPGME_Key(fingerprint, async, data); } @@ -78,7 +78,7 @@ class GPGME_Key { /** * Query any property of the Key listed in {@link validKeyProperties} * @param {String} property property to be retreived - * @returns {Boolean| String | Date | Array | Object |GPGME_Error} + * @returns {Boolean| String | Date | Array | Object} * the value of the property. If the Key is set to Async, the value * will be fetched from gnupg and resolved as a Promise. If Key is not * async, the armored property is not available (it can still be @@ -96,11 +96,11 @@ class GPGME_Key { } } else { if (property === 'armored') { - return gpgme_error('KEY_ASYNC_ONLY'); + throw gpgme_error('KEY_ASYNC_ONLY'); } // eslint-disable-next-line no-use-before-define if (!validKeyProperties.hasOwnProperty(property)){ - return gpgme_error('PARAM_WRONG'); + throw gpgme_error('PARAM_WRONG'); } else { return (this._data[property]); } -- cgit v1.2.3