[git] GPGME - branch, javascript-binding, updated. gpgme-1.11.1-95-gad39d54

by Maximilian Krambach cvs at cvs.gnupg.org
Fri Aug 17 16:56:06 CEST 2018


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GnuPG Made Easy".

The branch, javascript-binding has been updated
       via  ad39d54d192864b54a155bf5f94d5b6bb3e8612a (commit)
       via  754e799d35fd62d7a979452f44342934659908c7 (commit)
      from  90cb4a684211fe5630f209ba61510e8be3129eae (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit ad39d54d192864b54a155bf5f94d5b6bb3e8612a
Author: Maximilian Krambach <maximilian.krambach at intevation.de>
Date:   Fri Aug 17 16:57:41 2018 +0200

    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

diff --git a/lang/js/src/Errors.js b/lang/js/src/Errors.js
index 39e3a74..b22eca7 100644
--- a/lang/js/src/Errors.js
+++ b/lang/js/src/Errors.js
@@ -81,6 +81,10 @@ const err_list = {
         msg:'This property has not been retrieved yet from GPG',
         type: 'error'
     },
+    'KEY_ASYNC_ONLY': {
+        msg: 'This property cannot be used in synchronous calls',
+        type: 'error'
+    },
     'KEY_NO_DEFAULT': {
         msg:'A default key could not be established. Please check yout gpg ' +
             'configuration',
diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js
index 8d7fd94..5d0c816 100644
--- a/lang/js/src/Key.js
+++ b/lang/js/src/Key.js
@@ -81,54 +81,30 @@ class GPGME_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.
-         */
-        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<Boolean|GPGME_Error>} 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<GPGME_Subkey>} subkeys
+ * @param {Array<GPGME_UserId>} userids
+ * @param {Array<String>} 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
diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js
index 43ab96c..766bab1 100644
--- a/lang/js/src/Keyring.js
+++ b/lang/js/src/Keyring.js
@@ -38,12 +38,11 @@ export class GPGME_Keyring {
          *
          * @param {String | Array<String>} pattern (optional) A pattern to
          * search for in userIds or KeyIds.
-         * @param {Boolean} prepare_sync (optional) if set to true, the
-         * 'hasSecret' and 'armored' properties will be fetched for the Keys as
-         * well. These require additional calls to gnupg, resulting in a
-         * performance hungry operation. Calling them here enables direct,
-         * synchronous use of these properties for all keys, without having to
-         * resort to a refresh() first.
+         * @param {Boolean} prepare_sync (optional) if set to true, most data
+         * (with the exception of armored Key blocks) will be cached for the
+         * Keys. This enables direct, synchronous use of these properties for
+         * all keys. It does not check for changes on the backend. The cached
+         * information can be updated with the {@link Key.refresh} method.
          * @param {Boolean} search (optional) retrieve Keys from external
          * servers with the method(s) defined in gnupg (e.g. WKD/HKP lookup)
          * @returns {Promise<Array<GPGME_Key>>}
@@ -97,7 +96,6 @@ export class GPGME_Keyring {
                                                 break;
                                             }
                                         }
-                                        // TODO getArmor() to be used in sync
                                     }
                                 }
                                 let k = createKey(result.keys[i].fingerprint,
diff --git a/lang/js/unittests.js b/lang/js/unittests.js
index 3304b1e..25023bc 100644
--- a/lang/js/unittests.js
+++ b/lang/js/unittests.js
@@ -304,10 +304,6 @@ function unittests (){
                         expect(result).to.be.an('array');
                         expect(result[0]).to.be.an.instanceof(GPGME_Key);
                         expect(result[0].get('hasSecret')).to.be.a('boolean');
-                        // TODO: preparing sync for armored is still in discussion
-                        // expect(result[0].get('armored')).to.be.a('string');
-                        // expect(result[0].get('armored')).to.include(
-                        //     '-----END PGP PUBLIC KEY BLOCK-----');
                         done();
                     }
                 );

commit 754e799d35fd62d7a979452f44342934659908c7
Author: Maximilian Krambach <maximilian.krambach at intevation.de>
Date:   Fri Aug 17 14:40:27 2018 +0200

    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.

diff --git a/lang/js/src/Helpers.js b/lang/js/src/Helpers.js
index 0fd1499..accc2af 100644
--- a/lang/js/src/Helpers.js
+++ b/lang/js/src/Helpers.js
@@ -22,7 +22,6 @@
  */
 
 import { gpgme_error } from './Errors';
-import { GPGME_Key } from './Key';
 
 /**
  * Tries to return an array of fingerprints, either from input fingerprints or
@@ -50,7 +49,7 @@ export function toKeyIdArray(input){
             }
         } else if (typeof(input[i]) === 'object'){
             let fpr = '';
-            if (input[i] instanceof GPGME_Key){
+            if (input[i].hasOwnProperty('fingerprint')){
                 fpr = input[i].fingerprint;
             } else if (input[i].hasOwnProperty('primaryKey') &&
                 input[i].primaryKey.hasOwnProperty('getFingerprint')){
diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js
index aa41905..8d7fd94 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
diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js
index a678798..43ab96c 100644
--- a/lang/js/src/Keyring.js
+++ b/lang/js/src/Keyring.js
@@ -100,8 +100,8 @@ export class GPGME_Keyring {
                                         // TODO getArmor() to be used in sync
                                     }
                                 }
-                                let k = createKey(result.keys[i].fingerprint);
-                                k.setKeyData(result.keys[i]);
+                                let k = createKey(result.keys[i].fingerprint,
+                                    !prepare_sync, result.keys[i]);
                                 resultset.push(k);
                             }
                             resolve(resultset);
@@ -170,7 +170,7 @@ export class GPGME_Keyring {
          * @async
          * @static
          */
-        this.getDefaultKey = function() {
+        this.getDefaultKey = function(prepare_sync = false) {
             let me = this;
             return new Promise(function(resolve, reject){
                 let msg = createMessage('config_opt');
@@ -202,8 +202,9 @@ export class GPGME_Keyring {
                                 for (let i=0; i< result.keys.length; i++ ) {
                                     if (result.keys[i].invalid === false) {
                                         let k = createKey(
-                                            result.keys[i].fingerprint);
-                                        k.setKeyData(result.keys[i]);
+                                            result.keys[i].fingerprint,
+                                            !prepare_sync,
+                                            result.keys[i]);
                                         resolve(k);
                                         break;
                                     } else if (i === result.keys.length - 1){

-----------------------------------------------------------------------

Summary of changes:
 lang/js/src/Errors.js  |   4 +
 lang/js/src/Helpers.js |   3 +-
 lang/js/src/Key.js     | 358 ++++++++++++++++++++++++++++++-------------------
 lang/js/src/Keyring.js |  23 ++--
 lang/js/unittests.js   |   4 -
 5 files changed, 233 insertions(+), 159 deletions(-)


hooks/post-receive
-- 
GnuPG Made Easy
http://git.gnupg.org




More information about the Gnupg-commits mailing list