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; 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() * 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; 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 */ /* TODO: this will not work anymore, once we have plugable extensions */
#define TEH_extension_enabled(ext) (0 <= ext && TALER_Extension_MaxPredefined > \ #define TEH_extension_enabled(ext) (0 <= ext && TALER_Extension_MaxPredefined > \
ext && \ ext && \

View File

@ -27,29 +27,58 @@
#include "taler_extensions.h" #include "taler_extensions.h"
#include <jansson.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. * @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 static enum GNUNET_GenericReturnValue
age_restriction_parse_and_set_config (struct TALER_Extension *this, 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}; struct TALER_AgeMask mask = {0};
enum GNUNET_GenericReturnValue ret;
ret = TALER_agemask_parse_json (config, &mask); ret = TALER_agemask_parse_json (config, &mask);
if (GNUNET_OK != ret) if (GNUNET_OK != ret)
return ret; return ret;
if (this != NULL && TALER_Extension_AgeRestriction == this->type) /* only testing the parser */
{ if (this == NULL)
if (NULL != this->config) return GNUNET_OK;
{
GNUNET_free (this->config); if (TALER_Extension_AgeRestriction != this->type)
} return GNUNET_SYSERR;
this->config = GNUNET_malloc (sizeof(struct TALER_AgeMask));
GNUNET_memcpy (this->config, &mask, sizeof(struct TALER_AgeMask)); 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; return GNUNET_OK;
} }
@ -61,28 +90,9 @@ age_restriction_parse_and_set_config (struct TALER_Extension *this,
static enum GNUNET_GenericReturnValue static enum GNUNET_GenericReturnValue
age_restriction_test_config (const json_t *config) age_restriction_test_config (const json_t *config)
{ {
return age_restriction_parse_and_set_config (NULL, config); struct TALER_AgeMask mask = {0};
}
return TALER_agemask_parse_json (config, &mask);
/**
* @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;
} }
@ -91,26 +101,17 @@ static struct TALER_Extension extension_age_restriction = {
.type = TALER_Extension_AgeRestriction, .type = TALER_Extension_AgeRestriction,
.name = "age_restriction", .name = "age_restriction",
.critical = false, .critical = false,
.version = "1",
.config = NULL, // disabled per default .config = NULL, // disabled per default
.config_json = NULL,
.disable = &age_restriction_disable,
.test_config = &age_restriction_test_config, .test_config = &age_restriction_test_config,
.parse_and_set_config = &age_restriction_parse_and_set_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 ** static struct TALER_Extension **
get_known_extensions () get_known_extensions ()
@ -120,7 +121,6 @@ get_known_extensions ()
TALER_Extension_MaxPredefined + 1, TALER_Extension_MaxPredefined + 1,
struct TALER_Extension *); struct TALER_Extension *);
list[TALER_Extension_AgeRestriction] = &extension_age_restriction; list[TALER_Extension_AgeRestriction] = &extension_age_restriction;
list[TALER_Extension_Peer2Peer] = &extension_peer2peer;
list[TALER_Extension_MaxPredefined] = NULL; list[TALER_Extension_MaxPredefined] = NULL;
return list; return list;
@ -172,14 +172,13 @@ extension_update_event_cb (void *cls,
// Get the config from the database as string // Get the config from the database as string
{ {
char *config_str; char *config_str = NULL;
enum GNUNET_DB_QueryStatus qs; enum GNUNET_DB_QueryStatus qs;
struct TALER_Extension *extension; struct TALER_Extension *extension;
json_error_t err; json_error_t err;
json_t *config; json_t *config;
enum GNUNET_GenericReturnValue ret; enum GNUNET_GenericReturnValue ret;
// TODO: make this a safe lookup
extension = TEH_extensions[type]; extension = TEH_extensions[type];
qs = TEH_plugin->get_extension_config (TEH_plugin->cls, qs = TEH_plugin->get_extension_config (TEH_plugin->cls,
@ -194,6 +193,13 @@ extension_update_event_cb (void *cls,
return; return;
} }
// No config found -> extension is disabled
if (NULL == config_str)
{
extension->disable (extension);
return;
}
// Parse the string as JSON // Parse the string as JSON
config = json_loads (config_str, JSON_DECODE_ANY, &err); config = json_loads (config_str, JSON_DECODE_ANY, &err);
if (NULL == config) if (NULL == config)
@ -223,25 +229,29 @@ extension_update_event_cb (void *cls,
enum GNUNET_GenericReturnValue enum GNUNET_GenericReturnValue
TEH_extensions_init () TEH_extensions_init ()
{ {
/* Populate the known extensions. */
TEH_extensions = get_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 = { GNUNET_break (0);
.size = htons (sizeof (ev)), return GNUNET_SYSERR;
.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;
}
} }
/* 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; return GNUNET_OK;
} }

View File

@ -1703,29 +1703,79 @@ create_krd (struct TEH_KeyStateHandle *ksh,
&TEH_kyc_config.wallet_balance_limit))); &TEH_kyc_config.wallet_balance_limit)));
} }
// Signal support for the age-restriction extension, if so configured, and // Signal support for the configured, enabled extensions.
// add the array of age-restricted denominations. {
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) && if (TEH_extension_enabled (TALER_Extension_AgeRestriction) &&
NULL != age_restricted_denoms) 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 ( GNUNET_assert (
0 == 0 ==
json_object_set_new ( json_object_set_new (
@ -1734,8 +1784,6 @@ create_krd (struct TEH_KeyStateHandle *ksh,
age_restricted_denoms)); age_restricted_denoms));
} }
// TODO: signal support and configuration for the P2P extension, once
// implemented.
{ {
char *keys_json; char *keys_json;

View File

@ -49,6 +49,7 @@ struct SetExtensionsContext
{ {
uint32_t num_extensions; uint32_t num_extensions;
struct Extension *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 */ 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; MHD_RESULT ret;
json_t *extensions; json_t *extensions;
struct TALER_MasterSignatureP sig = {0}; struct SetExtensionsContext sec = {0};
struct GNUNET_JSON_Specification top_spec[] = { struct GNUNET_JSON_Specification top_spec[] = {
GNUNET_JSON_spec_json ("extensions", GNUNET_JSON_spec_json ("extensions",
&extensions), &extensions),
GNUNET_JSON_spec_fixed_auto ("extensions_sig", GNUNET_JSON_spec_fixed_auto ("extensions_sig",
&sig), &sec.extensions_sig),
GNUNET_JSON_spec_end () GNUNET_JSON_spec_end ()
}; };
struct SetExtensionsContext sec = {0};
/* Parse the top level json structure */ /* 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 ( if (GNUNET_OK != TALER_exchange_offline_extension_config_hash_verify (
&h_config, &h_config,
&TEH_master_public_key, &TEH_master_public_key,
&sig)) &sec.extensions_sig))
{ {
GNUNET_JSON_parse_free (top_spec); GNUNET_JSON_parse_free (top_spec);
return TALER_MHD_reply_with_error ( return TALER_MHD_reply_with_error (
@ -248,7 +251,6 @@ TEH_handler_management_post_extensions (
TALER_EC_GENERIC_PARAMETER_MALFORMED, TALER_EC_GENERIC_PARAMETER_MALFORMED,
"invalid configuration for extension"); "invalid configuration for extension");
goto CLEANUP; goto CLEANUP;
} }
/* We have a validly signed JSON object for the extension. Increment its /* 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 GNUNET_PQ_query_param_end
}; };
bool is_null; bool is_null;
*config = NULL;
struct GNUNET_PQ_ResultSpec rs[] = { struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_allow_null ( GNUNET_PQ_result_spec_allow_null (
GNUNET_PQ_result_spec_string ("config", config), GNUNET_PQ_result_spec_string ("config", config),
@ -11467,10 +11468,6 @@ postgres_get_extension_config (void *cls,
"get_extension_config", "get_extension_config",
params, params,
rs); rs);
if (is_null)
{
*config = NULL;
}
return qs; return qs;
} }

View File

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