improvements in extension handling

- extensions_sig is needed globally
- keep original json with config of extension
- fixed various bugs re: extension handling
This commit is contained in:
Özgür Kesim 2022-01-22 00:26:43 +01:00
parent 0b56de6c99
commit 1962ed6b0b
Signed by: oec
GPG Key ID: 3D76A56D79EDD9D7
7 changed files with 171 additions and 102 deletions

View File

@ -152,6 +152,13 @@ bool TEH_suicide;
*/
struct TALER_Extension **TEH_extensions;
/**
* Signature of the configuration of all enabled extensions,
* signed by the exchange's offline master key with purpose
* TALER_SIGNATURE_MASTER_EXTENSION.
*/
struct TALER_MasterSignatureP TEH_extensions_sig;
/**
* Value to return from main()
*/

View File

@ -206,6 +206,11 @@ extern struct GNUNET_CURL_Context *TEH_curl_ctx;
*/
extern struct TALER_Extension **TEH_extensions;
/*
* Signature of the offline master key of all enabled extensions' configuration
*/
extern struct TALER_MasterSignatureP TEH_extensions_sig;
/* TODO: this will not work anymore, once we have plugable extensions */
#define TEH_extension_enabled(ext) (0 <= ext && TALER_Extension_MaxPredefined > \
ext && \

View File

@ -27,29 +27,58 @@
#include "taler_extensions.h"
#include <jansson.h>
/**
* @brief implements the TALER_Extension.disable interface.
*/
void
age_restriction_disable (struct TALER_Extension *this)
{
if (NULL == this)
return;
this->config = NULL;
if (NULL != this->config_json)
{
json_decref (this->config_json);
this->config_json = NULL;
}
}
/**
* @brief implements the TALER_Extension.parse_and_set_config interface.
* @param this if NULL, only tests the configuration
* @param config the configuration as json
*/
static enum GNUNET_GenericReturnValue
age_restriction_parse_and_set_config (struct TALER_Extension *this,
const json_t *config)
json_t *config)
{
enum GNUNET_GenericReturnValue ret;
struct TALER_AgeMask mask = {0};
enum GNUNET_GenericReturnValue ret;
ret = TALER_agemask_parse_json (config, &mask);
if (GNUNET_OK != ret)
return ret;
if (this != NULL && TALER_Extension_AgeRestriction == this->type)
{
if (NULL != this->config)
{
GNUNET_free (this->config);
}
this->config = GNUNET_malloc (sizeof(struct TALER_AgeMask));
GNUNET_memcpy (this->config, &mask, sizeof(struct TALER_AgeMask));
}
/* only testing the parser */
if (this == NULL)
return GNUNET_OK;
if (TALER_Extension_AgeRestriction != this->type)
return GNUNET_SYSERR;
if (NULL != this->config)
GNUNET_free (this->config);
this->config = GNUNET_malloc (sizeof(struct TALER_AgeMask));
GNUNET_memcpy (this->config, &mask, sizeof(struct TALER_AgeMask));
if (NULL != this->config_json)
json_decref (this->config_json);
this->config_json = config;
return GNUNET_OK;
}
@ -61,28 +90,9 @@ age_restriction_parse_and_set_config (struct TALER_Extension *this,
static enum GNUNET_GenericReturnValue
age_restriction_test_config (const json_t *config)
{
return age_restriction_parse_and_set_config (NULL, config);
}
struct TALER_AgeMask mask = {0};
/**
* @brief implements the TALER_Extension.config_to_json interface.
*/
static json_t *
age_restriction_config_to_json (const struct TALER_Extension *this)
{
const struct TALER_AgeMask *mask;
if (NULL == this || TALER_Extension_AgeRestriction != this->type)
return NULL;
mask = (struct TALER_AgeMask *) this->config;
json_t *config = GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("extension", this->name),
GNUNET_JSON_pack_string ("mask",
TALER_age_mask_to_string (mask))
);
return config;
return TALER_agemask_parse_json (config, &mask);
}
@ -91,26 +101,17 @@ static struct TALER_Extension extension_age_restriction = {
.type = TALER_Extension_AgeRestriction,
.name = "age_restriction",
.critical = false,
.version = "1",
.config = NULL, // disabled per default
.config_json = NULL,
.disable = &age_restriction_disable,
.test_config = &age_restriction_test_config,
.parse_and_set_config = &age_restriction_parse_and_set_config,
.config_to_json = &age_restriction_config_to_json,
};
/* TODO: The extension for peer2peer */
static struct TALER_Extension extension_peer2peer = {
.type = TALER_Extension_Peer2Peer,
.name = "peer2peer",
.critical = false,
.config = NULL, // disabled per default
.test_config = NULL, // TODO
.parse_and_set_config = NULL, // TODO
.config_to_json = NULL, // TODO
};
/**
* Create a list with the extensions for Age Restriction and Peer2Peer
* Create a list with the extensions for Age Restriction (and later Peer2Peer,
* ...)
*/
static struct TALER_Extension **
get_known_extensions ()
@ -120,7 +121,6 @@ get_known_extensions ()
TALER_Extension_MaxPredefined + 1,
struct TALER_Extension *);
list[TALER_Extension_AgeRestriction] = &extension_age_restriction;
list[TALER_Extension_Peer2Peer] = &extension_peer2peer;
list[TALER_Extension_MaxPredefined] = NULL;
return list;
@ -172,14 +172,13 @@ extension_update_event_cb (void *cls,
// Get the config from the database as string
{
char *config_str;
char *config_str = NULL;
enum GNUNET_DB_QueryStatus qs;
struct TALER_Extension *extension;
json_error_t err;
json_t *config;
enum GNUNET_GenericReturnValue ret;
// TODO: make this a safe lookup
extension = TEH_extensions[type];
qs = TEH_plugin->get_extension_config (TEH_plugin->cls,
@ -194,6 +193,13 @@ extension_update_event_cb (void *cls,
return;
}
// No config found -> extension is disabled
if (NULL == config_str)
{
extension->disable (extension);
return;
}
// Parse the string as JSON
config = json_loads (config_str, JSON_DECODE_ANY, &err);
if (NULL == config)
@ -223,25 +229,29 @@ extension_update_event_cb (void *cls,
enum GNUNET_GenericReturnValue
TEH_extensions_init ()
{
/* Populate the known extensions. */
TEH_extensions = get_known_extensions ();
/* Set the event handler for updates */
struct GNUNET_DB_EventHeaderP ev = {
.size = htons (sizeof (ev)),
.type = htons (TALER_DBEVENT_EXCHANGE_EXTENSIONS_UPDATED),
};
extensions_eh = TEH_plugin->event_listen (TEH_plugin->cls,
GNUNET_TIME_UNIT_FOREVER_REL,
&ev,
&extension_update_event_cb,
NULL);
if (NULL == extensions_eh)
{
struct GNUNET_DB_EventHeaderP ev = {
.size = htons (sizeof (ev)),
.type = htons (TALER_DBEVENT_EXCHANGE_EXTENSIONS_UPDATED),
};
extensions_eh = TEH_plugin->event_listen (TEH_plugin->cls,
GNUNET_TIME_UNIT_FOREVER_REL,
&ev,
&extension_update_event_cb,
NULL);
if (NULL == extensions_eh)
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
GNUNET_break (0);
return GNUNET_SYSERR;
}
/* Trigger the initial load of configuration from the db */
for (struct TALER_Extension **it = TEH_extensions; NULL != *it; it++)
extension_update_event_cb (NULL, &(*it)->type, sizeof((*it)->type));
return GNUNET_OK;
}

View File

@ -1703,29 +1703,79 @@ create_krd (struct TEH_KeyStateHandle *ksh,
&TEH_kyc_config.wallet_balance_limit)));
}
// Signal support for the age-restriction extension, if so configured, and
// add the array of age-restricted denominations.
// Signal support for the configured, enabled extensions.
{
json_t *extensions = json_object ();
bool has_extensions;
/* Fill in the configurations of the enabled extensions */
for (struct TALER_Extension **it = TEH_extensions;
NULL != *it;
it++)
{
const struct TALER_Extension *extension = *it;
json_t *ext;
json_t *config_json;
int r;
/* skip if not configured == disabled */
if (NULL == extension->config)
continue;
has_extensions = true;
GNUNET_assert (NULL != extension->config_json);
config_json = json_copy (extension->config_json);
GNUNET_assert (NULL != config_json);
ext = GNUNET_JSON_PACK (
GNUNET_JSON_pack_bool ("critical",
extension->critical),
GNUNET_JSON_pack_string ("version",
extension->version),
GNUNET_JSON_pack_object_steal ("config",
config_json)
);
GNUNET_assert (NULL != ext);
r = json_object_set_new (
extensions,
extension->name,
ext);
GNUNET_assert (0 == r);
}
/* Update the keys object with the extensions */
if (has_extensions)
{
json_t *sig;
int r;
r = json_object_set_new (
keys,
"extensions",
extensions);
GNUNET_assert (0 == r);
/* add extensions_sig */
sig = GNUNET_JSON_PACK (
GNUNET_JSON_pack_data_auto ("extensions_sig",
&TEH_extensions_sig));
/* update the keys object with extensions_sig */
r = json_object_update (keys, sig);
GNUNET_assert (0 == r);
}
}
// Special case for age restrictions: if enabled, provide the lits of
// age-restricted denominations.
if (TEH_extension_enabled (TALER_Extension_AgeRestriction) &&
NULL != age_restricted_denoms)
{
struct TALER_AgeMask *mask;
json_t *config;
mask = (struct
TALER_AgeMask *) TEH_extensions[TALER_Extension_AgeRestriction]->
config;
config = GNUNET_JSON_PACK (
GNUNET_JSON_pack_bool ("critical", false),
GNUNET_JSON_pack_string ("version", "1"),
GNUNET_JSON_pack_string ("age_groups", TALER_age_mask_to_string (mask)));
GNUNET_assert (NULL != config);
GNUNET_assert (
0 ==
json_object_set_new (
keys,
"age_restriction",
config));
GNUNET_assert (
0 ==
json_object_set_new (
@ -1734,8 +1784,6 @@ create_krd (struct TEH_KeyStateHandle *ksh,
age_restricted_denoms));
}
// TODO: signal support and configuration for the P2P extension, once
// implemented.
{
char *keys_json;

View File

@ -49,6 +49,7 @@ struct SetExtensionsContext
{
uint32_t num_extensions;
struct Extension *extensions;
struct TALER_MasterSignatureP extensions_sig;
};
/**
@ -132,6 +133,9 @@ set_extensions (void *cls,
}
/* All extensions configured, update the signature */
TEH_extensions_sig = sec->extensions_sig;
return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; /* only 'success', so >=0, matters here */
}
@ -143,15 +147,14 @@ TEH_handler_management_post_extensions (
{
MHD_RESULT ret;
json_t *extensions;
struct TALER_MasterSignatureP sig = {0};
struct SetExtensionsContext sec = {0};
struct GNUNET_JSON_Specification top_spec[] = {
GNUNET_JSON_spec_json ("extensions",
&extensions),
GNUNET_JSON_spec_fixed_auto ("extensions_sig",
&sig),
&sec.extensions_sig),
GNUNET_JSON_spec_end ()
};
struct SetExtensionsContext sec = {0};
/* Parse the top level json structure */
{
@ -193,7 +196,7 @@ TEH_handler_management_post_extensions (
if (GNUNET_OK != TALER_exchange_offline_extension_config_hash_verify (
&h_config,
&TEH_master_public_key,
&sig))
&sec.extensions_sig))
{
GNUNET_JSON_parse_free (top_spec);
return TALER_MHD_reply_with_error (
@ -248,7 +251,6 @@ TEH_handler_management_post_extensions (
TALER_EC_GENERIC_PARAMETER_MALFORMED,
"invalid configuration for extension");
goto CLEANUP;
}
/* We have a validly signed JSON object for the extension. Increment its

View File

@ -11455,6 +11455,7 @@ postgres_get_extension_config (void *cls,
GNUNET_PQ_query_param_end
};
bool is_null;
*config = NULL;
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_allow_null (
GNUNET_PQ_result_spec_string ("config", config),
@ -11467,10 +11468,6 @@ postgres_get_extension_config (void *cls,
"get_extension_config",
params,
rs);
if (is_null)
{
*config = NULL;
}
return qs;
}

View File

@ -31,26 +31,27 @@
enum TALER_Extension_Type
{
TALER_Extension_AgeRestriction = 0,
TALER_Extension_Peer2Peer = 1,
TALER_Extension_MaxPredefined = 2 // Must be last
TALER_Extension_MaxPredefined = 1 // Must be last of the predefined
};
/*
* Represents the implementation of an extension.
* TODO: add documentation
*/
struct TALER_Extension
{
enum TALER_Extension_Type type;
char *name;
bool critical;
bool enabled;
char *version;
void *config;
json_t *config_json;
void (*disable)(struct TALER_Extension *this);
enum GNUNET_GenericReturnValue (*test_config)(const json_t *config);
enum GNUNET_GenericReturnValue (*parse_and_set_config)(struct
TALER_Extension *this,
const json_t *config);
json_t *(*config_to_json)(const struct TALER_Extension *this);
json_t *config);
};
/**
@ -132,8 +133,7 @@ TALER_get_age_mask (const struct GNUNET_CONFIGURATION_Handle *cfg,
/*
* TALER Peer2Peer Extension
* TODO oec
* TODO: Add Peer2Peer Extension
*/
#endif