gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-exchange] branch master updated: [age_restriction] progress 13/n


From: gnunet
Subject: [taler-exchange] branch master updated: [age_restriction] progress 13/n
Date: Sun, 23 Jan 2022 01:41:24 +0100

This is an automated email from the git hooks/post-receive script.

oec pushed a commit to branch master
in repository exchange.

The following commit(s) were added to refs/heads/master by this push:
     new 8684a9bf [age_restriction] progress 13/n
8684a9bf is described below

commit 8684a9bfea9223808e33edca9f91b8bd76379fd0
Author: Özgür Kesim <oec-taler@kesim.org>
AuthorDate: Sun Jan 23 01:31:02 2022 +0100

    [age_restriction] progress 13/n
    
    - major refactoring of extensions
      - extensions live now in a separate library, libtalerextensions
      - refactored all components using age_restriction accordingly
      - plumbing for plugin support for extensions roughly layed down
---
 configure.ac                                       |   1 +
 src/Makefile.am                                    |   1 +
 src/benchmark/Makefile.am                          |   1 +
 src/exchange-tools/Makefile.am                     |   1 +
 src/exchange-tools/taler-exchange-offline.c        | 196 ++----------
 src/exchange/Makefile.am                           |   1 +
 src/exchange/taler-exchange-httpd.c                |   5 -
 src/exchange/taler-exchange-httpd.h                |  10 -
 src/exchange/taler-exchange-httpd_extensions.c     | 131 ++------
 src/exchange/taler-exchange-httpd_keys.c           |  49 +--
 .../taler-exchange-httpd_management_extensions.c   |  38 +--
 src/extensions/Makefile.am                         |  30 ++
 src/extensions/extension_age_restriction.c         | 321 ++++++++++++++++++++
 src/extensions/extensions.c                        | 333 +++++++++++++++++++++
 src/include/taler_exchange_service.h               |   1 -
 src/include/taler_extensions.h                     | 150 ++++++++--
 src/include/taler_json_lib.h                       |   6 +-
 src/json/json.c                                    |   4 +-
 src/json/json_helper.c                             |   2 +-
 src/lib/Makefile.am                                |   1 +
 src/lib/exchange_api_handle.c                      |  71 ++---
 src/util/Makefile.am                               |   2 -
 src/util/extension_age_restriction.c               | 169 -----------
 src/util/extensions.c                              |  49 ---
 24 files changed, 936 insertions(+), 637 deletions(-)

diff --git a/configure.ac b/configure.ac
index 6995b9b3..99d2e534 100644
--- a/configure.ac
+++ b/configure.ac
@@ -541,6 +541,7 @@ AC_CONFIG_FILES([Makefile
                  src/exchange/Makefile
                  src/exchangedb/Makefile
                  src/exchange-tools/Makefile
+                 src/extensions/Makefile
                  src/lib/Makefile
                  src/testing/Makefile
                  src/benchmark/Makefile
diff --git a/src/Makefile.am b/src/Makefile.am
index 64d6020f..5d46850c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -18,6 +18,7 @@ SUBDIRS = \
   include \
   util \
   json \
+  extensions \
   curl \
   $(PQ_DIR) \
   $(SQ_DIR) \
diff --git a/src/benchmark/Makefile.am b/src/benchmark/Makefile.am
index 7c5ceefd..87f1e7e5 100644
--- a/src/benchmark/Makefile.am
+++ b/src/benchmark/Makefile.am
@@ -54,6 +54,7 @@ taler_exchange_benchmark_LDADD = \
   $(top_builddir)/src/bank-lib/libtalerbank.la \
   $(top_builddir)/src/json/libtalerjson.la \
   $(top_builddir)/src/util/libtalerutil.la \
+  $(top_builddir)/src/extensions/libtalerextensions.la \
   -lgnunetjson \
   -lgnunetcurl \
   -lgnunetutil \
diff --git a/src/exchange-tools/Makefile.am b/src/exchange-tools/Makefile.am
index b233a4b3..ae53cb30 100644
--- a/src/exchange-tools/Makefile.am
+++ b/src/exchange-tools/Makefile.am
@@ -25,6 +25,7 @@ taler_exchange_offline_LDADD = \
   $(top_builddir)/src/lib/libtalerexchange.la \
   $(top_builddir)/src/json/libtalerjson.la \
   $(top_builddir)/src/util/libtalerutil.la \
+  $(top_builddir)/src/extensions/libtalerextensions.la \
   -lgnunetjson \
   -lgnunetcurl \
   -ljansson \
diff --git a/src/exchange-tools/taler-exchange-offline.c 
b/src/exchange-tools/taler-exchange-offline.c
index 8db1fc9f..6ad345eb 100644
--- a/src/exchange-tools/taler-exchange-offline.c
+++ b/src/exchange-tools/taler-exchange-offline.c
@@ -1798,7 +1798,8 @@ upload_extensions (const char *exchange_url,
   {
     struct TALER_ExtensionConfigHash h_config;
 
-    if (GNUNET_OK != TALER_extension_config_hash (extensions, &h_config))
+    if (GNUNET_OK !=
+        TALER_JSON_extensions_config_hash (extensions, &h_config))
     {
       GNUNET_JSON_parse_free (spec);
       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -3506,176 +3507,30 @@ do_setup (char *const *args)
 }
 
 
-/**
- * struct extension carries the information about an extension together with
- * callbacks to parse the configuration and marshal it as JSON
+/*
+ * Print the current extensions as configured
  */
-struct extension
-{
-  char *name;
-  bool enabled;
-  bool critical;
-  char *version;
-  void *config;
-
-  enum GNUNET_GenericReturnValue (*parse_config)(struct extension *this,
-                                                 const char *section);
-  json_t *(*config_json)(const struct extension *this);
-};
-
-#define EXT_PREFIX "exchange-extension-"
-
-#define DEFAULT_AGE_GROUPS "8:10:12:14:16:18:21"
-
-static enum GNUNET_GenericReturnValue
-age_restriction_parse_config (struct extension *this, const char *section)
-{
-  char *age_groups = NULL;
-  struct TALER_AgeMask mask = {0};
-  enum GNUNET_GenericReturnValue ret;
-
-  ret = GNUNET_CONFIGURATION_get_value_yesno (kcfg, section, "ENABLED");
-
-  this->enabled = (GNUNET_YES == ret);
-
-  if (! this->enabled)
-    return GNUNET_OK;
-
-  if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (kcfg,
-                                                          section,
-                                                          "AGE_GROUPS",
-                                                          &age_groups))
-    age_groups = DEFAULT_AGE_GROUPS;
-
-  if (GNUNET_OK != TALER_parse_age_group_string (age_groups, &mask))
-  {
-    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
-                               section,
-                               "AGE_GROUPS");
-    test_shutdown ();
-    global_ret = EXIT_NOTCONFIGURED;
-    return GNUNET_SYSERR;
-  }
-
-  /* Don't look here. We just store the mask in/as the pointer .*/
-  this->config = (void *) (size_t) mask.mask;
-  return GNUNET_OK;
-}
-
-
-static json_t *
-age_restriction_json (const struct extension *this)
-{
-  struct TALER_AgeMask mask;
-  json_t *conf;
-
-  if (! this->enabled)
-    return NULL;
-
-  /* Don't look here. We just restore the mask from/as the pointer .*/
-  mask.mask = (uint32_t) (size_t) this->config;
-
-  conf = GNUNET_JSON_PACK (
-    GNUNET_JSON_pack_string (
-      "age_groups",
-      TALER_age_mask_to_string (&mask)));
-
-  return GNUNET_JSON_PACK (
-    GNUNET_JSON_pack_bool ("critical",
-                           this->critical),
-    GNUNET_JSON_pack_string ("version",
-                             this->version),
-    GNUNET_JSON_pack_object_steal ("config", conf));
-}
-
-
-static struct extension extensions[] = {
-  {
-    .name = "age_restriction",
-    .version = "1",
-    .config = 0,
-    .parse_config = &age_restriction_parse_config,
-    .config_json = &age_restriction_json,
-  },
-  /* TODO: add p2p here */
-  {0},
-};
-
-
-static const struct extension*
-get_extension (const char *extension)
-{
-  for (const struct extension *known = extensions;
-       NULL != known->name;
-       known++)
-  {
-    if (0 == strncasecmp (extension,
-                          known->name,
-                          strlen (known->name)))
-      return known;
-  }
-  return NULL;
-}
-
-
 static void
-collect_extensions (void *cls, const char *section)
+do_extensions_show (char *const *args)
 {
-  json_t *obj = (json_t *) cls;
-  const char *name;
-  const struct extension *extension;
-
-  if (0 != global_ret)
-    return;
-
-  if (0 != strncasecmp (section,
-                        EXT_PREFIX,
-                        sizeof(EXT_PREFIX) - 1))
-  {
-    return;
-  }
 
-  name = section + sizeof(EXT_PREFIX) - 1;
+  json_t *obj = json_object ();
+  json_t *exts = json_object ();
+  const struct TALER_Extension *it;
 
-  if (NULL == (extension = get_extension (name)))
+  TALER_extensions_init ();
+  if (GNUNET_OK != TALER_extensions_load_taler_config (kcfg))
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unsupported extension `%s` (section [%s]).\n", name,
-                section);
-    test_shutdown ();
-    global_ret = EXIT_NOTCONFIGURED;
+                "error while loading taler config for extensions\n");
     return;
   }
 
-  if (GNUNET_OK != extension->parse_config ((struct extension *) extension,
-                                            section))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Couldn't parse configuration for extension `%s` (section 
[%s]).\n",
-                name,
-                section);
-    test_shutdown ();
-    global_ret = EXIT_NOTCONFIGURED;
-    return;
-  }
-
-  json_object_set (obj, name, extension->config_json (extension));
-}
-
-
-/*
- * Print the current extensions as configured
- */
-static void
-do_extensions_show (char *const *args)
-{
+  for (it = TALER_extensions_get_head ();
+       NULL != it;
+       it = it->next)
+    json_object_set (exts, it->name, it->config_to_json (it));
 
-  json_t *obj = json_object ();
-  json_t *exts = json_object ();
-
-  GNUNET_CONFIGURATION_iterate_sections (kcfg,
-                                         &collect_extensions,
-                                         exts);
   json_object_set (obj, "extensions", exts);
 
   GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "%s\n",
@@ -3695,13 +3550,24 @@ do_extensions_sign (char *const *args)
   json_t *extensions = json_object ();
   struct TALER_ExtensionConfigHash h_config;
   struct TALER_MasterSignatureP sig;
+  const struct TALER_Extension *it;
+
+  TALER_extensions_init ();
+
+  if (GNUNET_OK != TALER_extensions_load_taler_config (kcfg))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "error while loading taler config for extensions\n");
+    return;
+  }
 
-  GNUNET_CONFIGURATION_iterate_sections (kcfg,
-                                         &collect_extensions,
-                                         extensions);
+  for (it = TALER_extensions_get_head ();
+       NULL != it;
+       it = it->next)
+    json_object_set (extensions, it->name, it->config_to_json (it));
 
-  // TODO: check size of extensions?
-  if (GNUNET_OK != TALER_extension_config_hash (extensions, &h_config))
+  if (GNUNET_OK !=
+      TALER_JSON_extensions_config_hash (extensions, &h_config))
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                 "error while hashing config for extensions\n");
diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am
index e7688f73..44487a3a 100644
--- a/src/exchange/Makefile.am
+++ b/src/exchange/Makefile.am
@@ -119,6 +119,7 @@ taler_exchange_httpd_LDADD = \
   $(top_builddir)/src/json/libtalerjson.la \
   $(top_builddir)/src/exchangedb/libtalerexchangedb.la \
   $(top_builddir)/src/util/libtalerutil.la \
+  $(top_builddir)/src/extensions/libtalerextensions.la \
   -lmicrohttpd \
   -lgnunetcurl \
   -lgnunetutil \
diff --git a/src/exchange/taler-exchange-httpd.c 
b/src/exchange/taler-exchange-httpd.c
index 5fe70730..ae5847d1 100644
--- a/src/exchange/taler-exchange-httpd.c
+++ b/src/exchange/taler-exchange-httpd.c
@@ -147,11 +147,6 @@ int TEH_check_invariants_flag;
  */
 bool TEH_suicide;
 
-/**
- * Global register of extensions
- */
-struct TALER_Extension **TEH_extensions;
-
 /**
  * Signature of the configuration of all enabled extensions,
  * signed by the exchange's offline master key with purpose
diff --git a/src/exchange/taler-exchange-httpd.h 
b/src/exchange/taler-exchange-httpd.h
index 017d5520..d3b1ba84 100644
--- a/src/exchange/taler-exchange-httpd.h
+++ b/src/exchange/taler-exchange-httpd.h
@@ -201,21 +201,11 @@ extern volatile bool MHD_terminating;
  */
 extern struct GNUNET_CURL_Context *TEH_curl_ctx;
 
-/**
- * The manifest of the available extensions, NULL terminated
- */
-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 && \
-                                    NULL != TEH_extensions[ext]->config)
-
 /**
  * @brief Struct describing an URL and the handler for it.
  */
diff --git a/src/exchange/taler-exchange-httpd_extensions.c 
b/src/exchange/taler-exchange-httpd_extensions.c
index 0245797d..8edb24d4 100644
--- a/src/exchange/taler-exchange-httpd_extensions.c
+++ b/src/exchange/taler-exchange-httpd_extensions.c
@@ -27,106 +27,6 @@
 #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,
-                                      json_t *config)
-{
-  struct TALER_AgeMask mask = {0};
-  enum GNUNET_GenericReturnValue ret;
-
-  ret = TALER_agemask_parse_json (config, &mask);
-  if (GNUNET_OK != ret)
-    return ret;
-
-  /* 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;
-}
-
-
-/**
- * @brief implements the TALER_Extension.test_config interface.
- */
-static enum GNUNET_GenericReturnValue
-age_restriction_test_config (const json_t *config)
-{
-  struct TALER_AgeMask mask = {0};
-
-  return TALER_agemask_parse_json (config, &mask);
-}
-
-
-/* The extension for age restriction */
-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,
-};
-
-/**
- * Create a list with the extensions for Age Restriction (and later Peer2Peer,
- * ...)
- */
-static struct TALER_Extension **
-get_known_extensions ()
-{
-
-  struct TALER_Extension **list = GNUNET_new_array (
-    TALER_Extension_MaxPredefined + 1,
-    struct TALER_Extension *);
-  list[TALER_Extension_AgeRestriction] = &extension_age_restriction;
-  list[TALER_Extension_MaxPredefined] = NULL;
-
-  return list;
-}
-
-
 /**
  * Handler listening for extensions updates by other exchange
  * services.
@@ -148,6 +48,7 @@ extension_update_event_cb (void *cls,
 {
   (void) cls;
   enum TALER_Extension_Type type;
+  const struct TALER_Extension *extension;
 
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Received extensions update event\n");
@@ -161,12 +62,15 @@ extension_update_event_cb (void *cls,
   }
 
   type = *(enum TALER_Extension_Type *) extra;
-  /* TODO: This check will not work once we have plugable extensions */
-  if (type <0 || type >= TALER_Extension_MaxPredefined)
+
+
+  /* Get the corresponding extension */
+  extension = TALER_extensions_get_by_type (type);
+  if (NULL == extension)
   {
     GNUNET_break (0);
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Oops, incorrect type for TALER_Extension_type\n");
+                "Oops, unknown extension type: %d\n", type);
     return;
   }
 
@@ -174,13 +78,10 @@ extension_update_event_cb (void *cls,
   {
     char *config_str = NULL;
     enum GNUNET_DB_QueryStatus qs;
-    struct TALER_Extension *extension;
     json_error_t err;
     json_t *config;
     enum GNUNET_GenericReturnValue ret;
 
-    extension  = TEH_extensions[type];
-
     qs = TEH_plugin->get_extension_config (TEH_plugin->cls,
                                            extension->name,
                                            &config_str);
@@ -193,10 +94,10 @@ extension_update_event_cb (void *cls,
       return;
     }
 
-    // No config found -> extension is disabled
+    // No config found -> disable extension
     if (NULL == config_str)
     {
-      extension->disable (extension);
+      extension->disable ((struct TALER_Extension *) extension);
       return;
     }
 
@@ -214,7 +115,10 @@ extension_update_event_cb (void *cls,
     }
 
     // Call the parser for the extension
-    ret = extension->parse_and_set_config (extension, config);
+    ret = extension->load_json_config (
+      (struct TALER_Extension *) extension,
+      config);
+
     if (GNUNET_OK != ret)
     {
       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -229,8 +133,7 @@ extension_update_event_cb (void *cls,
 enum GNUNET_GenericReturnValue
 TEH_extensions_init ()
 {
-  /* Populate the known extensions. */
-  TEH_extensions = get_known_extensions ();
+  TALER_extensions_init ();
 
   /* Set the event handler for updates */
   struct GNUNET_DB_EventHeaderP ev = {
@@ -249,8 +152,10 @@ TEH_extensions_init ()
   }
 
   /* 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));
+  for (const struct TALER_Extension *it = TALER_extensions_get_head ();
+       NULL != it->next;
+       it = it->next)
+    extension_update_event_cb (NULL, &it->type, sizeof(it->type));
 
   return GNUNET_OK;
 }
diff --git a/src/exchange/taler-exchange-httpd_keys.c 
b/src/exchange/taler-exchange-httpd_keys.c
index 7c64cdb7..de9b81cd 100644
--- a/src/exchange/taler-exchange-httpd_keys.c
+++ b/src/exchange/taler-exchange-httpd_keys.c
@@ -744,14 +744,14 @@ load_age_mask (const char*section_name)
 {
   static const struct TALER_AgeMask null_mask = {0};
   struct TALER_AgeMask age_mask = {0};
+  /* TODO: optimize by putting this into global? */
   const struct TALER_Extension *age_ext =
-    TEH_extensions[TALER_Extension_AgeRestriction];
+    TALER_extensions_get_by_type (TALER_Extension_AgeRestriction);
 
   // Get the age mask from the extension, if configured
-  if (NULL != age_ext->config)
-  {
+  /* TODO: optimize by putting this into global? */
+  if (TALER_extensions_is_enabled (age_ext))
     age_mask = *(struct TALER_AgeMask *) age_ext->config;
-  }
 
   if (age_mask.mask == 0)
   {
@@ -1706,14 +1706,14 @@ create_krd (struct TEH_KeyStateHandle *ksh,
   // Signal support for the configured, enabled extensions.
   {
     json_t *extensions = json_object ();
-    bool has_extensions;
+    bool has_extensions = false;
+    bool age_restriction_enabled = false;
 
     /* Fill in the configurations of the enabled extensions */
-    for (struct TALER_Extension **it = TEH_extensions;
-         NULL != *it;
-         it++)
+    for (const struct TALER_Extension *extension = TALER_extensions_get_head 
();
+         NULL != extension;
+         extension = extension->next)
     {
-      const struct TALER_Extension *extension = *it;
       json_t *ext;
       json_t *config_json;
       int r;
@@ -1722,7 +1722,10 @@ create_krd (struct TEH_KeyStateHandle *ksh,
       if (NULL == extension->config)
         continue;
 
+      /* flag our findings so far */
       has_extensions = true;
+      age_restriction_enabled = (extension->type ==
+                                 TALER_Extension_AgeRestriction);
 
       GNUNET_assert (NULL != extension->config_json);
 
@@ -1743,7 +1746,6 @@ create_krd (struct TEH_KeyStateHandle *ksh,
         extensions,
         extension->name,
         ext);
-
       GNUNET_assert (0 == r);
     }
 
@@ -1768,20 +1770,20 @@ create_krd (struct TEH_KeyStateHandle *ksh,
       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 (age_restriction_enabled &&
+        NULL != age_restricted_denoms)
+    {
+      GNUNET_assert (
+        0 ==
+        json_object_set_new (
+          keys,
+          "age_restricted_denoms",
+          age_restricted_denoms));
+    }
 
-  // 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)
-  {
-    GNUNET_assert (
-      0 ==
-      json_object_set_new (
-        keys,
-        "age_restricted_denoms",
-        age_restricted_denoms));
   }
 
 
@@ -1884,7 +1886,8 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
   GNUNET_assert (NULL != denoms);
 
   // If age restriction is enabled, initialize the array of age restricted 
denoms.
-  if (TEH_extension_enabled (TALER_Extension_AgeRestriction))
+  /* TODO: optimize by putting this into global? */
+  if (TALER_extensions_is_enabled_type (TALER_Extension_AgeRestriction))
   {
     age_restricted_denoms = json_array ();
     GNUNET_assert (NULL != age_restricted_denoms);
diff --git a/src/exchange/taler-exchange-httpd_management_extensions.c 
b/src/exchange/taler-exchange-httpd_management_extensions.c
index 17db8e8c..17b00006 100644
--- a/src/exchange/taler-exchange-httpd_management_extensions.c
+++ b/src/exchange/taler-exchange-httpd_management_extensions.c
@@ -79,14 +79,14 @@ set_extensions (void *cls,
   for (uint32_t i = 0; i<sec->num_extensions; i++)
   {
     struct Extension *ext = &sec->extensions[i];
+    const struct TALER_Extension *taler_ext;
     enum GNUNET_DB_QueryStatus qs;
     char *config;
 
-    /* Sanity check.
-     * TODO: This will not work anymore, once we have plugable extensions
-     */
-    if (0 > ext->type || TALER_Extension_MaxPredefined <= ext->type)
+    taler_ext = TALER_extensions_get_by_type (ext->type);
+    if (NULL == taler_ext)
     {
+      /* No such extension found */
       GNUNET_break (0);
       return GNUNET_DB_STATUS_HARD_ERROR;
     }
@@ -104,7 +104,7 @@ set_extensions (void *cls,
 
     qs = TEH_plugin->set_extension_config (
       TEH_plugin->cls,
-      TEH_extensions[ext->type]->name,
+      taler_ext->name,
       config);
 
     if (qs < 0)
@@ -183,17 +183,11 @@ TEH_handler_management_post_extensions (
   /* Verify the signature */
   {
     struct TALER_ExtensionConfigHash h_config;
-    if (GNUNET_OK != TALER_extension_config_hash (extensions, &h_config))
-    {
-      GNUNET_JSON_parse_free (top_spec);
-      return TALER_MHD_reply_with_error (
-        connection,
-        MHD_HTTP_BAD_REQUEST,
-        TALER_EC_GENERIC_PARAMETER_MALFORMED,
-        "invalid object, non-hashable");
-    }
 
-    if (GNUNET_OK != TALER_exchange_offline_extension_config_hash_verify (
+    if (GNUNET_OK !=
+        TALER_JSON_extensions_config_hash (extensions, &h_config) ||
+        GNUNET_OK !=
+        TALER_exchange_offline_extension_config_hash_verify (
           &h_config,
           &TEH_master_public_key,
           &sec.extensions_sig))
@@ -207,7 +201,6 @@ TEH_handler_management_post_extensions (
     }
   }
 
-
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Received /management/extensions\n");
 
@@ -217,7 +210,7 @@ TEH_handler_management_post_extensions (
 
   /* Now parse individual extensions and signatures from those objects. */
   {
-    const struct TALER_Extension *extension;
+    const struct TALER_Extension *extension = NULL;
     const char *name;
     json_t *config;
     int idx = 0;
@@ -225,11 +218,8 @@ TEH_handler_management_post_extensions (
     json_object_foreach (extensions, name, config){
 
       /* 1. Make sure name refers to a supported extension */
-      if (GNUNET_OK != TALER_extension_get_by_name (name,
-                                                    (const struct
-                                                     TALER_Extension **)
-                                                    TEH_extensions,
-                                                    &extension))
+      extension = TALER_extensions_get_by_name (name);
+      if (NULL == extension)
       {
         ret = TALER_MHD_reply_with_error (
           connection,
@@ -243,7 +233,9 @@ TEH_handler_management_post_extensions (
       sec.extensions[idx].type = extension->type;
 
       /* 2. Make sure the config is sound */
-      if (GNUNET_OK != extension->test_config (sec.extensions[idx].config))
+      if (GNUNET_OK !=
+          extension->test_json_config (
+            sec.extensions[idx].config))
       {
         ret = TALER_MHD_reply_with_error (
           connection,
diff --git a/src/extensions/Makefile.am b/src/extensions/Makefile.am
new file mode 100644
index 00000000..792b7eeb
--- /dev/null
+++ b/src/extensions/Makefile.am
@@ -0,0 +1,30 @@
+# This Makefile.am is in the public domain
+
+AM_CPPFLAGS = \
+  -I$(top_srcdir)/src/include \
+  $(LIBGCRYPT_CFLAGS) \
+  $(POSTGRESQL_CPPFLAGS)
+
+if USE_COVERAGE
+  AM_CFLAGS = --coverage -O0
+  XLIB = -lgcov
+endif
+
+
+# Libraries
+
+lib_LTLIBRARIES = \
+  libtalerextensions.la
+
+libtalerextensions_la_LDFLAGS = \
+  -version-info 0:0:0 \
+  -no-undefined
+
+libtalerextensions_la_SOURCES = \
+  extensions.c \
+  extension_age_restriction.c
+
+libtalerextensions_la_LIBADD = \
+  -lgnunetjson \
+  -ljansson \
+  $(XLIB)
diff --git a/src/extensions/extension_age_restriction.c 
b/src/extensions/extension_age_restriction.c
new file mode 100644
index 00000000..a9ffb7f1
--- /dev/null
+++ b/src/extensions/extension_age_restriction.c
@@ -0,0 +1,321 @@
+/*
+   This file is part of TALER
+   Copyright (C) 2021-2022 Taler Systems SA
+
+   TALER is free software; you can redistribute it and/or modify it under the
+   terms of the GNU General Public License as published by the Free Software
+   Foundation; either version 3, or (at your option) any later version.
+
+   TALER 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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License along with
+   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file extension_age_restriction.c
+ * @brief Utility functions regarding age restriction
+ * @author Özgür Kesim
+ */
+#include "platform.h"
+#include "taler_util.h"
+#include "taler_extensions.h"
+#include "stdint.h"
+
+
+/**
+ * @param groups String representation of the age groups. Must be of the form
+ *  a:b:...:n:m
+ * with
+ *  0 < a < b <...< n < m < 32
+ * @param[out] mask Bit representation of the age groups.
+ * @return Error if string was invalid, OK otherwise.
+ */
+enum GNUNET_GenericReturnValue
+TALER_parse_age_group_string (
+  const char *groups,
+  struct TALER_AgeMask *mask)
+{
+
+  const char *pos = groups;
+  unsigned int prev = 0;
+  unsigned int val = 0;
+  char c;
+
+  while (*pos)
+  {
+    c = *pos++;
+    if (':' == c)
+    {
+      if (prev >= val)
+        return GNUNET_SYSERR;
+
+      mask->mask |= 1 << val;
+      prev = val;
+      val = 0;
+      continue;
+    }
+
+    if ('0'>c || '9'<c)
+      return GNUNET_SYSERR;
+
+    val = 10 * val + c - '0';
+
+    if (0>=val || 32<=val)
+      return GNUNET_SYSERR;
+  }
+
+  if (0>val || 32<=val || prev>=val)
+    return GNUNET_SYSERR;
+
+  mask->mask |= (1 << val);
+  mask->mask |= 1; // mark zeroth group, too
+
+  return GNUNET_OK;
+}
+
+
+/**
+ * Encodes the age mask into a string, like "8:10:12:14:16:18:21"
+ *
+ * @param mask Age mask
+ * @return String representation of the age mask, allocated by GNUNET_malloc.
+ *         Can be used as value in the TALER config.
+ */
+char *
+TALER_age_mask_to_string (
+  const struct TALER_AgeMask *m)
+{
+  uint32_t mask = m->mask;
+  unsigned int n = 0;
+  char *buf = GNUNET_malloc (32 * 3); // max characters possible
+  char *pos = buf;
+
+  if (NULL == buf)
+  {
+    return buf;
+  }
+
+  while (mask != 0)
+  {
+    mask >>= 1;
+    n++;
+    if (0 == (mask & 1))
+    {
+      continue;
+    }
+
+    if (n > 9)
+    {
+      *(pos++) = '0' + n / 10;
+    }
+    *(pos++) = '0' + n % 10;
+
+    if (0 != (mask >> 1))
+    {
+      *(pos++) = ':';
+    }
+  }
+  return buf;
+}
+
+
+/* ==================================================
+ *
+ * Age Restriction  TALER_Extension imlementation
+ *
+ * ==================================================
+ */
+
+
+/**
+ * @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.load_taler_config interface.
+ * @param cfg Handle to the GNUNET configuration
+ * @param[out] enabled Set to true if age restriction is enabled in the 
config, false otherwise.
+ * @param[out] mask Mask for age restriction. Will be 0 if age restriction was 
not enabled in the config.
+ * @return Error if extension for age restriction was set, but age groups were
+ *         invalid, OK otherwise.
+ */
+static enum GNUNET_GenericReturnValue
+age_restriction_load_taler_config (
+  struct TALER_Extension *this,
+  const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+  char *groups = NULL;
+  enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
+  struct TALER_AgeMask mask = {0};
+
+  if ((GNUNET_YES !=
+       GNUNET_CONFIGURATION_have_value (cfg,
+                                        
TALER_EXTENSION_SECTION_AGE_RESTRICTION,
+                                        "ENABLED"))
+      ||
+      (GNUNET_YES !=
+       GNUNET_CONFIGURATION_get_value_yesno (cfg,
+                                             
TALER_EXTENSION_SECTION_AGE_RESTRICTION,
+                                             "ENABLED")))
+  {
+    /* Age restriction is not enabled */
+    this->config = NULL;
+    this->config_json = NULL;
+    return GNUNET_OK;
+  }
+
+  /* Age restriction is enabled, extract age groups */
+  if ((GNUNET_YES ==
+       GNUNET_CONFIGURATION_have_value (cfg,
+                                        
TALER_EXTENSION_SECTION_AGE_RESTRICTION,
+                                        "AGE_GROUPS"))
+      &&
+      (GNUNET_YES !=
+       GNUNET_CONFIGURATION_get_value_string (cfg,
+                                              
TALER_EXTENSION_SECTION_AGE_RESTRICTION,
+                                              "AGE_GROUPS",
+                                              &groups)))
+    return GNUNET_SYSERR;
+
+
+  mask.mask = TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_MASK;
+
+  ret = GNUNET_OK;
+
+  if (groups != NULL)
+  {
+    ret = TALER_parse_age_group_string (groups, &mask);
+    if (GNUNET_OK != ret)
+      mask.mask = TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_MASK;
+  }
+
+  if (GNUNET_OK == ret)
+    this->config = (void *) (size_t) mask.mask;
+
+  GNUNET_free (groups);
+  return ret;
+}
+
+
+/**
+ * @brief implements the TALER_Extension.load_json_config interface.
+ * @param this if NULL, only tests the configuration
+ * @param config the configuration as json
+ */
+static enum GNUNET_GenericReturnValue
+age_restriction_load_json_config (
+  struct TALER_Extension *this,
+  json_t *config)
+{
+  struct TALER_AgeMask mask = {0};
+  enum GNUNET_GenericReturnValue ret;
+
+  ret = TALER_JSON_parse_agemask (config, &mask);
+  if (GNUNET_OK != ret)
+    return ret;
+
+  /* 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;
+}
+
+
+/**
+ * @brief implements the TALER_Extension.load_json_config interface.
+ * @param this if NULL, only tests the configuration
+ * @param config the configuration as json
+ */
+json_t *
+age_restriction_config_to_json (
+  const struct TALER_Extension *this)
+{
+  struct TALER_AgeMask mask;
+  char *mask_str;
+  json_t *conf;
+
+  GNUNET_assert (NULL != this);
+  GNUNET_assert (NULL != this->config);
+
+  if (NULL != this->config_json)
+  {
+    return json_copy (this->config_json);
+  }
+
+  mask.mask = (uint32_t) (size_t) this->config;
+  mask_str = TALER_age_mask_to_string (&mask);
+  conf = GNUNET_JSON_PACK (
+    GNUNET_JSON_pack_string ("age_groups", mask_str)
+    );
+
+  return GNUNET_JSON_PACK (
+    GNUNET_JSON_pack_bool ("critical", this->critical),
+    GNUNET_JSON_pack_string ("version", this->version),
+    GNUNET_JSON_pack_object_steal ("config", conf)
+    );
+}
+
+
+/**
+ * @brief implements the TALER_Extension.test_json_config interface.
+ */
+static enum GNUNET_GenericReturnValue
+age_restriction_test_json_config (
+  const json_t *config)
+{
+  struct TALER_AgeMask mask = {0};
+
+  return TALER_JSON_parse_agemask (config, &mask);
+}
+
+
+/* The extension for age restriction */
+struct TALER_Extension _extension_age_restriction = {
+  .next = NULL,
+  .type = TALER_Extension_AgeRestriction,
+  .name = "age_restriction",
+  .critical = false,
+  .version = "1",
+  .config = NULL,   // disabled per default
+  .config_json = NULL,
+  .disable = &age_restriction_disable,
+  .test_json_config = &age_restriction_test_json_config,
+  .load_json_config = &age_restriction_load_json_config,
+  .config_to_json = &age_restriction_config_to_json,
+  .load_taler_config = &age_restriction_load_taler_config,
+};
+
+/* end of extension_age_restriction.c */
diff --git a/src/extensions/extensions.c b/src/extensions/extensions.c
new file mode 100644
index 00000000..55d970c5
--- /dev/null
+++ b/src/extensions/extensions.c
@@ -0,0 +1,333 @@
+/*
+   This file is part of TALER
+   Copyright (C) 2021-2022 Taler Systems SA
+
+   TALER is free software; you can redistribute it and/or modify it under the
+   terms of the GNU General Public License as published by the Free Software
+   Foundation; either version 3, or (at your option) any later version.
+
+   TALER 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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License along with
+   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file extensions.c
+ * @brief Utility functions for extensions
+ * @author Özgür Kesim
+ */
+#include "platform.h"
+#include "taler_util.h"
+#include "taler_signatures.h"
+#include "taler_extensions.h"
+#include "stdint.h"
+
+
+/* head of the list of all registered extensions */
+static struct TALER_Extension *_extensions = NULL;
+static bool _initialized = false;
+
+void
+TALER_extensions_init ()
+{
+  extern struct TALER_Extension _extension_age_restriction;
+  if (! _initialized)
+    _extensions = &_extension_age_restriction;
+
+  _initialized = true;
+}
+
+
+const struct TALER_Extension *
+TALER_extensions_get_head ()
+{
+  return _extensions;
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_extensions_add (
+  const struct TALER_Extension *new)
+{
+  struct TALER_Extension *ext;
+
+  if (_initialized)
+    return GNUNET_SYSERR;
+
+  GNUNET_assert (NULL != _extensions);
+
+  /* Sanity checks */
+  if (NULL == new ||
+      NULL == new->name ||
+      NULL == new->version ||
+      NULL == new->disable ||
+      NULL == new->test_json_config ||
+      NULL == new->load_json_config ||
+      NULL == new->config_to_json ||
+      NULL == new->load_taler_config ||
+      NULL == new->next)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "invalid extension\n");
+    return GNUNET_SYSERR;
+  }
+
+  /* Check for collisions */
+  for (ext = _extensions; NULL != ext; ext = ext->next)
+  {
+    if (new->type == ext->type ||
+        0 == strcmp (new->name, ext->name))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "extension collision\n");
+      return GNUNET_NO;
+    }
+  }
+
+  /* No collisions found, so add this extension to the list */
+  ext->next = (struct TALER_Extension *) new;
+
+  return GNUNET_OK;
+}
+
+
+const struct TALER_Extension *
+TALER_extensions_get_by_type (
+  enum TALER_Extension_Type type)
+{
+
+  for (const struct TALER_Extension *it = _extensions;
+       NULL != it;
+       it = it->next)
+  {
+    if (it->type == type)
+      return it;
+  }
+
+  /* No extension found. */
+  return NULL;
+}
+
+
+bool
+TALER_extensions_is_enabled_type (
+  enum TALER_Extension_Type type)
+{
+  const struct TALER_Extension *ext =
+    TALER_extensions_get_by_type (type);
+
+  return (NULL != ext &&
+          TALER_extensions_is_enabled (ext));
+}
+
+
+const struct TALER_Extension *
+TALER_extensions_get_by_name (
+  const char *name)
+{
+  for (const struct TALER_Extension *it = _extensions;
+       NULL != it;
+       it = it->next)
+  {
+    if (0 == strcmp (name, it->name))
+      return it;
+  }
+  /* No extension found. */
+  return NULL;
+}
+
+
+enum GNUNET_GenericReturnValue
+config_hash_verify (
+  const struct TALER_ExtensionConfigHash *h_config,
+  const struct TALER_MasterPublicKeyP *master_pub,
+  const struct TALER_MasterSignatureP *master_sig
+  )
+{
+  struct TALER_MasterExtensionConfigurationPS ec = {
+    .purpose.purpose = htonl (TALER_SIGNATURE_MASTER_EXTENSION),
+    .purpose.size = htonl (sizeof(ec)),
+    .h_config = *h_config
+  };
+
+  return GNUNET_CRYPTO_eddsa_verify (
+    TALER_SIGNATURE_MASTER_EXTENSION,
+    &ec,
+    &master_sig->eddsa_signature,
+    &master_pub->eddsa_pub);
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_extensions_verify_json_config_signature (
+  json_t *extensions,
+  struct TALER_MasterSignatureP *extensions_sig,
+  struct TALER_MasterPublicKeyP *master_pub)
+{
+  struct TALER_ExtensionConfigHash h_config;
+
+  if (GNUNET_OK !=
+      TALER_JSON_extensions_config_hash (extensions, &h_config))
+    return GNUNET_SYSERR;
+
+  if (GNUNET_OK != config_hash_verify (
+        &h_config,
+        master_pub,
+        extensions_sig))
+    return GNUNET_NO;
+
+  return GNUNET_OK;
+}
+
+
+struct load_conf_closure
+{
+  const struct GNUNET_CONFIGURATION_Handle *cfg;
+  enum GNUNET_GenericReturnValue error;
+};
+
+static void
+collect_extensions (
+  void *cls,
+  const char *section)
+{
+  struct load_conf_closure *col = cls;
+  const char *name;
+  const struct TALER_Extension *extension;
+
+  if (GNUNET_OK != col->error)
+    return;
+
+  if (0 != strncasecmp (section,
+                        TALER_EXTENSION_SECTION_PREFIX,
+                        sizeof(TALER_EXTENSION_SECTION_PREFIX) - 1))
+  {
+    return;
+  }
+
+  name = section + sizeof(TALER_EXTENSION_SECTION_PREFIX) - 1;
+
+  if (NULL == (extension = TALER_extensions_get_by_name (name)))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Unsupported extension `%s` (section [%s]).\n", name,
+                section);
+    col->error = GNUNET_SYSERR;
+    return;
+  }
+
+  if (GNUNET_OK !=
+      extension->load_taler_config (
+        (struct TALER_Extension *) extension,
+        col->cfg))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Couldn't parse configuration for extension `%s` (section 
[%s]).\n",
+                name,
+                section);
+    col->error = GNUNET_SYSERR;
+    return;
+  }
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_extensions_load_taler_config (
+  const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+  struct load_conf_closure col = {
+    .cfg = cfg,
+    .error = GNUNET_OK,
+  };
+
+  GNUNET_CONFIGURATION_iterate_sections (cfg,
+                                         &collect_extensions,
+                                         &col);
+  return col.error;
+}
+
+
+static enum GNUNET_GenericReturnValue
+is_json_extension_config (
+  json_t *obj,
+  int *critical,
+  const char **version,
+  json_t **config)
+{
+  enum GNUNET_GenericReturnValue ret;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_boolean ("critical",
+                              critical),
+    GNUNET_JSON_spec_string ("version",
+                             version),
+    GNUNET_JSON_spec_json ("config",
+                           config),
+    GNUNET_JSON_spec_end ()
+  };
+
+  ret = GNUNET_JSON_parse (obj, spec, NULL, NULL);
+  if (GNUNET_OK == ret)
+    GNUNET_JSON_parse_free (spec);
+
+  return ret;
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_extensions_load_json_config (
+  json_t *extensions)
+{
+  const char*name;
+  json_t *blob;
+
+  GNUNET_assert (NULL != extensions);
+  GNUNET_assert (json_is_object (extensions));
+
+  json_object_foreach (extensions, name, blob)
+  {
+    int critical;
+    const char *version;
+    json_t *config;
+    const struct TALER_Extension *extension =
+      TALER_extensions_get_by_name (name);
+
+    if (NULL == extension)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "no such extension: %s\n", name);
+      return GNUNET_SYSERR;
+    }
+
+    /* load and verify criticality, version, etc. */
+    if (GNUNET_OK !=
+        is_json_extension_config (
+          blob, &critical, &version, &config))
+      return GNUNET_SYSERR;
+
+    if (critical != extension->critical
+        || 0 != strcmp (version, extension->version) // TODO: libtool compare?
+        || NULL == config
+        || GNUNET_OK != extension->test_json_config (config))
+      return GNUNET_SYSERR;
+
+    /* This _should_ work now */
+    if (GNUNET_OK !=
+        extension->load_json_config ((struct TALER_Extension *) extension,
+                                     config))
+      return GNUNET_SYSERR;
+  }
+
+  /* make sure to disable all extensions that weren't mentioned in the json */
+  for (const struct TALER_Extension *it = TALER_extensions_get_head ();
+       NULL != it;
+       it = it->next)
+  {
+    if (NULL == json_object_get (extensions, it->name))
+      it->disable ((struct TALER_Extension *) it);
+  }
+
+  return GNUNET_OK;
+}
+
+
+/* end of extensions.c */
diff --git a/src/include/taler_exchange_service.h 
b/src/include/taler_exchange_service.h
index 5bc87cf4..caa61c5f 100644
--- a/src/include/taler_exchange_service.h
+++ b/src/include/taler_exchange_service.h
@@ -3159,5 +3159,4 @@ void
 TALER_EXCHANGE_add_auditor_denomination_cancel (
   struct TALER_EXCHANGE_AuditorAddDenominationHandle *ah);
 
-
 #endif  /* _TALER_EXCHANGE_SERVICE_H */
diff --git a/src/include/taler_extensions.h b/src/include/taler_extensions.h
index 7199304d..f00f3ed5 100644
--- a/src/include/taler_extensions.h
+++ b/src/include/taler_extensions.h
@@ -40,6 +40,9 @@ enum TALER_Extension_Type
  */
 struct TALER_Extension
 {
+  /* simple linked list */
+  struct TALER_Extension *next;
+
   enum TALER_Extension_Type type;
   char *name;
   bool critical;
@@ -48,28 +51,125 @@ struct TALER_Extension
   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,
-                                                         json_t *config);
+
+  enum GNUNET_GenericReturnValue (*test_json_config)(
+    const json_t *config);
+
+  enum GNUNET_GenericReturnValue (*load_json_config)(
+    struct TALER_Extension *this,
+    json_t *config);
+
+  json_t *(*config_to_json)(
+    const struct TALER_Extension *this);
+
+  enum GNUNET_GenericReturnValue (*load_taler_config)(
+    struct TALER_Extension *this,
+    const struct GNUNET_CONFIGURATION_Handle *cfg);
 };
 
 /**
  * Generic functions for extensions
  */
 
+void
+TALER_extensions_init ();
+
+/*
+ * Sets the configuration of the extensions from the given TALER configuration
+ *
+ * @param cfg Handle to the TALER configuration
+ * @return GNUNET_OK on success, GNUNET_SYSERR if unknown extensions were found
+ *         or any particular configuration couldn't be parsed.
+ */
+enum GNUNET_GenericReturnValue
+TALER_extensions_load_taler_config (
+  const struct GNUNET_CONFIGURATION_Handle *cfg);
+
+/*
+ * Returns the head of the linked list of extensions
+ */
+const struct TALER_Extension *
+TALER_extensions_get_head ();
+
+/*
+ * Adds an extension to the linked list of extensions
+ *
+ * @param new_extension the new extension to be added
+ * @return GNUNET_OK on success, GNUNET_SYSERR if the extension is invalid
+ * (missing fields), GNUNET_NO if there is already an extension with that name
+ * or type.
+ */
+enum GNUNET_GenericReturnValue
+TALER_extensions_add (
+  const struct TALER_Extension *new_extension);
+
+/**
+ * Finds and returns a supported extension by a given type.
+ *
+ * @param type type of the extension to lookup
+ * @return extension found, or NULL (should not happen!)
+ */
+const struct TALER_Extension *
+TALER_extensions_get_by_type (
+  enum TALER_Extension_Type type);
+
+
 /**
  * Finds and returns a supported extension by a given name.
  *
  * @param name name of the extension to lookup
- * @param extensions list of TALER_Extensions as haystack, terminated by a 
NULL-entry
- * @param[out] ext set to the extension, if found, NULL otherwise
- * @return GNUNET_OK if extension was found, GNUNET_NO otherwise
+ * @return the extension, if found, NULL otherwise
+ */
+const struct TALER_Extension *
+TALER_extensions_get_by_name (
+  const char *name);
+
+#define TALER_extensions_is_enabled(ext) (NULL != (ext)->config)
+
+/**
+ * Check if a given type of an extension is enabled
+ *
+ * @param type type of to check
+ * @return true enabled, false if not enabled, will assert if type is not 
found.
+ */
+bool
+TALER_extensions_is_enabled_type (
+  enum TALER_Extension_Type type);
+
+
+/*
+ * Verify the signature of a given JSON object for extensions with the master
+ * key of the exchange.
+ *
+ * The JSON object must be of type ExchangeKeysResponse as described in
+ * https://docs.taler.net/design-documents/006-extensions.html#exchange
+ *
+ * @param extensions JSON object with the extension configuration
+ * @param extensions_sig signature of the hash of the JSON object
+ * @param master_pub public key to verify the signature
+ * @return GNUNET_OK on success, GNUNET_SYSERR when hashing of the JSON fails
+ * and GNUNET_NO if the signature couldn't be verified.
+ */
+enum GNUNET_GenericReturnValue
+TALER_extensions_verify_json_config_signature (
+  json_t *extensions,
+  struct TALER_MasterSignatureP *extensions_sig,
+  struct TALER_MasterPublicKeyP *master_pub);
+
+/*
+ * Sets the configuration of the extensions from a given JSON object.
+ *
+ * The JSON object must be of type ExchangeKeysResponse as described in
+ * https://docs.taler.net/design-documents/006-extensions.html#exchange
+ *
+ * @param cfg Handle to the TALER configuration
+ * @return GNUNET_OK on success, GNUNET_SYSERR if unknown extensions were found
+ *         or any particular configuration couldn't be parsed.
  */
 enum GNUNET_GenericReturnValue
-TALER_extension_get_by_name (const char *name,
-                             const struct TALER_Extension **extensions,
-                             const struct TALER_Extension **ext);
+TALER_extensions_load_json_config (
+  json_t *extensions);
+
 
 /*
  * TALER Age Restriction Extension
@@ -82,9 +182,11 @@ TALER_extension_get_by_name (const char *name,
  * The default age mask represents the age groups
  * 0-7, 8-9, 10-11, 12-13, 14-15, 16-17, 18-20, 21-...
  */
-#define TALER_EXTENSION_DEFAULT_AGE_MASK (1 | 1 << 8 | 1 << 10 | 1 << 12 | 1   
 \
-                                                << 14 | 1 << 16 | 1 << 18 | 1 \
-                                                << 21)
+#define TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_MASK (1 | 1 << 8 | 1 << 10 
\
+                                                          | 1 << 12 | 1 << 14 \
+                                                          | 1 << 16 | 1 << 18 \
+                                                          | 1 << 21)
+#define TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_GROUPS 
"8:10:12:14:16:18:21"
 
 /**
  * @brief Parses a string as a list of age groups.
@@ -104,8 +206,9 @@ TALER_extension_get_by_name (const char *name,
  * @return Error, if age groups were invalid, OK otherwise.
  */
 enum GNUNET_GenericReturnValue
-TALER_parse_age_group_string (const char *groups,
-                              struct TALER_AgeMask *mask);
+TALER_parse_age_group_string (
+  const char *groups,
+  struct TALER_AgeMask *mask);
 
 /**
  * Encodes the age mask into a string, like "8:10:12:14:16:18:21"
@@ -115,21 +218,8 @@ TALER_parse_age_group_string (const char *groups,
  *         Can be used as value in the TALER config.
  */
 char *
-TALER_age_mask_to_string (const struct TALER_AgeMask *mask);
-
-
-/**
- * @brief Reads the age groups from the configuration and sets the
- * corresponding age mask.
- *
- * @param cfg
- * @param[out] mask for age restriction, will be set to 0 if age restriction 
is disabled.
- * @return Error if extension for age restriction was set but age groups were
- *         invalid, OK otherwise.
- */
-enum GNUNET_GenericReturnValue
-TALER_get_age_mask (const struct GNUNET_CONFIGURATION_Handle *cfg,
-                    struct TALER_AgeMask *mask);
+TALER_age_mask_to_string (
+  const struct TALER_AgeMask *mask);
 
 
 /*
diff --git a/src/include/taler_json_lib.h b/src/include/taler_json_lib.h
index 102b3a6f..2a101d26 100644
--- a/src/include/taler_json_lib.h
+++ b/src/include/taler_json_lib.h
@@ -549,8 +549,8 @@ TALER_deposit_extension_hash (const json_t *extensions,
  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
  */
 enum GNUNET_GenericReturnValue
-TALER_extension_config_hash (const json_t *config,
-                             struct TALER_ExtensionConfigHash *eh);
+TALER_JSON_extensions_config_hash (const json_t *config,
+                                   struct TALER_ExtensionConfigHash *eh);
 
 /**
  * Parses a JSON object { "extension": "age_restriction", "mask": <uint32> }.
@@ -560,7 +560,7 @@ TALER_extension_config_hash (const json_t *config,
  * @return #GNUNET_OK on success and #GNUNET_SYSERR on failure.
  */
 enum GNUNET_GenericReturnValue
-TALER_agemask_parse_json (const json_t *root,
+TALER_JSON_parse_agemask (const json_t *root,
                           struct TALER_AgeMask *mask);
 
 #endif /* TALER_JSON_LIB_H_ */
diff --git a/src/json/json.c b/src/json/json.c
index 956aad1a..705cfe92 100644
--- a/src/json/json.c
+++ b/src/json/json.c
@@ -1010,8 +1010,8 @@ TALER_deposit_extension_hash (const json_t *extensions,
 
 
 enum GNUNET_GenericReturnValue
-TALER_extension_config_hash (const json_t *config,
-                             struct TALER_ExtensionConfigHash *ech)
+TALER_JSON_extensions_config_hash (const json_t *config,
+                                   struct TALER_ExtensionConfigHash *ech)
 {
   return dump_and_hash (config,
                         "taler-extension-configuration",
diff --git a/src/json/json_helper.c b/src/json/json_helper.c
index 3b4da559..1942d09b 100644
--- a/src/json/json_helper.c
+++ b/src/json/json_helper.c
@@ -660,7 +660,7 @@ TALER_JSON_spec_i18n_str (const char *name,
 
 
 enum GNUNET_GenericReturnValue
-TALER_agemask_parse_json (const json_t *root,
+TALER_JSON_parse_agemask (const json_t *root,
                           struct TALER_AgeMask *mask)
 {
   const char *name;
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index dd4c527d..3398bdf1 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -57,6 +57,7 @@ libtalerexchange_la_LIBADD = \
   $(top_builddir)/src/json/libtalerjson.la \
   $(top_builddir)/src/curl/libtalercurl.la \
   $(top_builddir)/src/util/libtalerutil.la \
+  $(top_builddir)/src/extensions/libtalerextensions.la \
   -lgnunetcurl \
   -lgnunetjson \
   -lgnunetutil \
diff --git a/src/lib/exchange_api_handle.c b/src/lib/exchange_api_handle.c
index aea09a81..cf3d69d6 100644
--- a/src/lib/exchange_api_handle.c
+++ b/src/lib/exchange_api_handle.c
@@ -795,50 +795,39 @@ decode_keys_json (const json_t *resp_obj,
   }
 
   /* Parse the supported extension(s): age-restriction. */
-  /* TODO: maybe lift this into a FP in TALER_Extension ? */
+  /* TODO: maybe lift all this into a FP in TALER_Extension ? */
   {
-    json_t *age_restriction = json_object_get (resp_obj,
-                                               "age_restriction");
-
-    if (NULL != age_restriction)
-    {
-      bool critical;
-      const char *version;
-      const char *age_groups;
-      struct GNUNET_JSON_Specification spec[] = {
-        GNUNET_JSON_spec_bool ("critical",
-                               &critical),
-        GNUNET_JSON_spec_string ("version",
-                                 &version),
-        GNUNET_JSON_spec_string ("age_groups",
-                                 &age_groups),
-        GNUNET_JSON_spec_end ()
-      };
-
-      if (GNUNET_OK !=
-          GNUNET_JSON_parse (age_restriction,
-                             spec,
-                             NULL, NULL))
-      {
-        GNUNET_break_op (0);
-        return GNUNET_SYSERR;
-      }
+    struct TALER_MasterSignatureP extensions_sig = {0};
+    json_t *extensions = NULL;
+    struct GNUNET_JSON_Specification ext_spec[] = {
+      GNUNET_JSON_spec_mark_optional (
+        GNUNET_JSON_spec_json ("extensions",
+                               &extensions)),
+      GNUNET_JSON_spec_mark_optional (
+        GNUNET_JSON_spec_fixed_auto (
+          "extensions_sig",
+          &extensions_sig)),
+      GNUNET_JSON_spec_end ()
+    };
 
-      if (critical || // do we care?
-          0 != strncmp (version, "1", 1) ) /* TODO: better compatibility check 
*/
-      {
-        GNUNET_break_op (0);
-        return GNUNET_SYSERR;
-      }
+    /* 1. Search for extensions in the response to /keys */
+    EXITIF (GNUNET_OK !=
+            GNUNET_JSON_parse (resp_obj,
+                               ext_spec,
+                               NULL, NULL));
 
-      if (GNUNET_OK !=
-          TALER_parse_age_group_string (age_groups,
-                                        &key_data->age_mask))
-      {
-        // TODO: print more specific error?
-        GNUNET_break_op (0);
-        return GNUNET_SYSERR;
-      }
+    if (NULL != extensions)
+    {
+      /* 2. We have an extensions object. Verify its signature. */
+      EXITIF (GNUNET_OK !=
+              TALER_extensions_verify_json_config_signature (
+                extensions,
+                &extensions_sig,
+                &key_data->master_pub));
+
+      /* 3. Parse and set the the configuration of the extensions accordingly 
*/
+      EXITIF (GNUNET_OK !=
+              TALER_extensions_load_json_config (extensions));
     }
   }
 
diff --git a/src/util/Makefile.am b/src/util/Makefile.am
index 55ebb4df..35e58034 100644
--- a/src/util/Makefile.am
+++ b/src/util/Makefile.am
@@ -72,8 +72,6 @@ libtalerutil_la_SOURCES = \
   crypto_wire.c \
   denom.c \
   exchange_signatures.c \
-  extensions.c \
-  extension_age_restriction.c \
   getopt.c \
   lang.c \
   iban.c \
diff --git a/src/util/extension_age_restriction.c 
b/src/util/extension_age_restriction.c
deleted file mode 100644
index 0b04c7d7..00000000
--- a/src/util/extension_age_restriction.c
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
-   This file is part of TALER
-   Copyright (C) 2014-2020 Taler Systems SA
-
-   TALER is free software; you can redistribute it and/or modify it under the
-   terms of the GNU General Public License as published by the Free Software
-   Foundation; either version 3, or (at your option) any later version.
-
-   TALER 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 General Public License for more details.
-
-   You should have received a copy of the GNU General Public License along with
-   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-/**
- * @file extension_age_restriction.c
- * @brief Utility functions regarding age restriction
- * @author Özgür Kesim
- */
-#include "platform.h"
-#include "taler_util.h"
-#include "taler_extensions.h"
-#include "stdint.h"
-
-/**
- *
- * @param cfg Handle to the GNUNET configuration
- * @param[out] Mask for age restriction. Will be 0 if age restriction was not 
enabled in the config.
- * @return Error if extension for age restriction was set, but age groups were
- *         invalid, OK otherwise.
- */
-enum GNUNET_GenericReturnValue
-TALER_get_age_mask (const struct GNUNET_CONFIGURATION_Handle *cfg,
-                    struct TALER_AgeMask *mask)
-{
-  char *groups;
-  enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
-
-  if ((GNUNET_YES != GNUNET_CONFIGURATION_have_value (cfg,
-                                                      
TALER_EXTENSION_SECTION_AGE_RESTRICTION,
-                                                      "ENABLED")) ||
-      (GNUNET_YES != GNUNET_CONFIGURATION_get_value_yesno (cfg,
-                                                           
TALER_EXTENSION_SECTION_AGE_RESTRICTION,
-                                                           "ENABLED")))
-  {
-    /* Age restriction is not enabled */
-    mask->mask = 0;
-    return GNUNET_OK;
-  }
-
-  /* Age restriction is enabled, extract age groups */
-  if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
-                                                          
TALER_EXTENSION_SECTION_AGE_RESTRICTION,
-                                                          "AGE_GROUPS",
-                                                          &groups))
-  {
-    /* FIXME: log error? */
-    return GNUNET_SYSERR;
-  }
-  if (groups == NULL)
-  {
-    /* No groups defined in config, return default_age_mask */
-    mask->mask = TALER_EXTENSION_DEFAULT_AGE_MASK;
-    return GNUNET_OK;
-  }
-
-  ret = TALER_parse_age_group_string (groups, mask);
-  GNUNET_free (groups);
-  return ret;
-}
-
-
-/**
- * @param groups String representation of the age groups. Must be of the form
- *  a:b:...:n:m
- * with
- *  0 < a < b <...< n < m < 32
- * @param[out] mask Bit representation of the age groups.
- * @return Error if string was invalid, OK otherwise.
- */
-enum GNUNET_GenericReturnValue
-TALER_parse_age_group_string (const char *groups,
-                              struct TALER_AgeMask *mask)
-{
-
-  const char *pos = groups;
-  unsigned int prev = 0;
-  unsigned int val = 0;
-  char c;
-
-  while (*pos)
-  {
-    c = *pos++;
-    if (':' == c)
-    {
-      if (prev >= val)
-        return GNUNET_SYSERR;
-
-      mask->mask |= 1 << val;
-      prev = val;
-      val = 0;
-      continue;
-    }
-
-    if ('0'>c || '9'<c)
-      return GNUNET_SYSERR;
-
-    val = 10 * val + c - '0';
-
-    if (0>=val || 32<=val)
-      return GNUNET_SYSERR;
-  }
-
-  if (0>val || 32<=val || prev>=val)
-    return GNUNET_SYSERR;
-
-  mask->mask |= (1 << val);
-  mask->mask |= 1; // mark zeroth group, too
-
-  return GNUNET_OK;
-}
-
-
-/**
- * Encodes the age mask into a string, like "8:10:12:14:16:18:21"
- *
- * @param mask Age mask
- * @return String representation of the age mask, allocated by GNUNET_malloc.
- *         Can be used as value in the TALER config.
- */
-char *
-TALER_age_mask_to_string (const struct TALER_AgeMask *m)
-{
-  uint32_t mask = m->mask;
-  unsigned int n = 0;
-  char *buf = GNUNET_malloc (32 * 3); // max characters possible
-  char *pos = buf;
-
-  if (NULL == buf)
-  {
-    return buf;
-  }
-
-  while (mask != 0)
-  {
-    mask >>= 1;
-    n++;
-    if (0 == (mask & 1))
-    {
-      continue;
-    }
-
-    if (n > 9)
-    {
-      *(pos++) = '0' + n / 10;
-    }
-    *(pos++) = '0' + n % 10;
-
-    if (0 != (mask >> 1))
-    {
-      *(pos++) = ':';
-    }
-  }
-  return buf;
-}
-
-
-/* end of extension_age_restriction.c */
diff --git a/src/util/extensions.c b/src/util/extensions.c
deleted file mode 100644
index 87dd16b4..00000000
--- a/src/util/extensions.c
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
-   This file is part of TALER
-   Copyright (C) 2014-2021 Taler Systems SA
-
-   TALER is free software; you can redistribute it and/or modify it under the
-   terms of the GNU General Public License as published by the Free Software
-   Foundation; either version 3, or (at your option) any later version.
-
-   TALER 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 General Public License for more details.
-
-   You should have received a copy of the GNU General Public License along with
-   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-/**
- * @file extensions.c
- * @brief Utility functions for extensions
- * @author Özgür Kesim
- */
-#include "platform.h"
-#include "taler_util.h"
-#include "taler_extensions.h"
-#include "stdint.h"
-
-enum GNUNET_GenericReturnValue
-TALER_extension_get_by_name (const char *name,
-                             const struct TALER_Extension **extensions,
-                             const struct TALER_Extension **ext)
-{
-
-  const struct TALER_Extension *it = *extensions;
-
-  for (; NULL != it; it++)
-  {
-    if (0 == strncmp (name,
-                      it->name,
-                      strlen (it->name)))
-    {
-      *ext = it;
-      return GNUNET_OK;
-    }
-  }
-
-  return GNUNET_NO;
-}
-
-
-/* end of extensions.c */

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]