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.
This commit is contained in:
Maximilian Krambach 2018-08-17 14:40:27 +02:00
parent 90cb4a6842
commit 754e799d35
3 changed files with 89 additions and 67 deletions

View File

@ -22,7 +22,6 @@
*/ */
import { gpgme_error } from './Errors'; import { gpgme_error } from './Errors';
import { GPGME_Key } from './Key';
/** /**
* Tries to return an array of fingerprints, either from input fingerprints or * 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'){ } else if (typeof(input[i]) === 'object'){
let fpr = ''; let fpr = '';
if (input[i] instanceof GPGME_Key){ if (input[i].hasOwnProperty('fingerprint')){
fpr = input[i].fingerprint; fpr = input[i].fingerprint;
} else if (input[i].hasOwnProperty('primaryKey') && } else if (input[i].hasOwnProperty('primaryKey') &&
input[i].primaryKey.hasOwnProperty('getFingerprint')){ input[i].primaryKey.hasOwnProperty('getFingerprint')){

View File

@ -31,13 +31,22 @@ import { createMessage } from './Message';
* @param {Boolean} async If True, Key properties (except fingerprint) will be * @param {Boolean} async If True, Key properties (except fingerprint) will be
* queried from gnupg on each call, making the operation up-to-date, the * queried from gnupg on each call, making the operation up-to-date, the
* answers will be Promises, and the performance will likely suffer * 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'){ if (!isFingerprint(fingerprint) || typeof(async) !== 'boolean'){
return gpgme_error('PARAM_WRONG'); 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 * @class
*/ */
export class GPGME_Key { class GPGME_Key {
constructor(fingerprint, async){ constructor(fingerprint, async, data){
/** /**
* @property {Boolean} If true, most answers will be asynchronous * @property {Boolean} If true, most answers will be asynchronous
@ -59,6 +68,11 @@ export class GPGME_Key {
this.isAsync = async; this.isAsync = async;
let _data = {fingerprint: fingerprint.toUpperCase()}; let _data = {fingerprint: fingerprint.toUpperCase()};
if (data !== undefined
&& data.fingerprint.toUpperCase() === _data.fingerprint
) {
_data = data;
}
this.getFingerprint = function(){ this.getFingerprint = function(){
if (!_data.fingerprint || !isFingerprint(_data.fingerprint)){ if (!_data.fingerprint || !isFingerprint(_data.fingerprint)){
return gpgme_error('KEY_INVALID'); return gpgme_error('KEY_INVALID');
@ -77,54 +91,6 @@ export class GPGME_Key {
return this.get('hasSecret'); 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} * Query any property of the Key listed in {@link validKeyProperties}
@ -188,7 +154,12 @@ export class GPGME_Key {
msg.setParameter('keys', _data.fingerprint); msg.setParameter('keys', _data.fingerprint);
msg.post().then(function(result){ msg.post().then(function(result){
if (result.keys.length === 1){ if (result.keys.length === 1){
me.setKeyData(result.keys[0]); 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.getHasSecret().then(function(){
me.getArmor().then(function(){ me.getArmor().then(function(){
resolve(me); resolve(me);
@ -198,6 +169,7 @@ export class GPGME_Key {
}, function(error){ }, function(error){
reject(error); reject(error);
}); });
}
} else { } else {
reject(gpgme_error('KEY_NOKEY')); 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;
}

View File

@ -100,8 +100,8 @@ export class GPGME_Keyring {
// TODO getArmor() to be used in sync // TODO getArmor() to be used in sync
} }
} }
let k = createKey(result.keys[i].fingerprint); let k = createKey(result.keys[i].fingerprint,
k.setKeyData(result.keys[i]); !prepare_sync, result.keys[i]);
resultset.push(k); resultset.push(k);
} }
resolve(resultset); resolve(resultset);
@ -170,7 +170,7 @@ export class GPGME_Keyring {
* @async * @async
* @static * @static
*/ */
this.getDefaultKey = function() { this.getDefaultKey = function(prepare_sync = false) {
let me = this; let me = this;
return new Promise(function(resolve, reject){ return new Promise(function(resolve, reject){
let msg = createMessage('config_opt'); let msg = createMessage('config_opt');
@ -202,8 +202,9 @@ export class GPGME_Keyring {
for (let i=0; i< result.keys.length; i++ ) { for (let i=0; i< result.keys.length; i++ ) {
if (result.keys[i].invalid === false) { if (result.keys[i].invalid === false) {
let k = createKey( let k = createKey(
result.keys[i].fingerprint); result.keys[i].fingerprint,
k.setKeyData(result.keys[i]); !prepare_sync,
result.keys[i]);
resolve(k); resolve(k);
break; break;
} else if (i === result.keys.length - 1){ } else if (i === result.keys.length - 1){