exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

taler-exchange-httpd_management_extensions.c (8903B)


      1 /*
      2    This file is part of TALER
      3    Copyright (C) 2021 Taler Systems SA
      4 
      5    TALER is free software; you can redistribute it and/or modify it under the
      6    terms of the GNU Affero General Public License as published by the Free Software
      7    Foundation; either version 3, or (at your option) any later version.
      8 
      9    TALER is distributed in the hope that it will be useful, but WITHOUT ANY
     10    WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11    A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details.
     12 
     13    You should have received a copy of the GNU Affero General Public License along with
     14    TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15  */
     16 /**
     17  * @file taler-exchange-httpd_management_extensions.c
     18  * @brief Handle request to POST /management/extensions
     19  * @author Özgür Kesim
     20  */
     21 #include "taler/platform.h"
     22 #include <gnunet/gnunet_util_lib.h>
     23 #include <gnunet/gnunet_json_lib.h>
     24 #include <jansson.h>
     25 #include <microhttpd.h>
     26 #include "taler/taler_json_lib.h"
     27 #include "taler/taler_mhd_lib.h"
     28 #include "taler/taler_signatures.h"
     29 #include "taler-exchange-httpd_management.h"
     30 #include "taler-exchange-httpd_responses.h"
     31 #include "taler/taler_extensions.h"
     32 #include "taler/taler_dbevents.h"
     33 
     34 /**
     35  * Extension carries the necessary data for a particular extension.
     36  *
     37  */
     38 struct Extension
     39 {
     40   enum TALER_Extension_Type type;
     41   json_t *manifest;
     42 };
     43 
     44 /**
     45  * Closure for the #set_extensions transaction
     46  */
     47 struct SetExtensionsContext
     48 {
     49   uint32_t num_extensions;
     50   struct Extension *extensions;
     51   struct TALER_MasterSignatureP extensions_sig;
     52 };
     53 
     54 /**
     55  * Function implementing database transaction to set the manifests of
     56  * extensions.  It runs the transaction logic.
     57  *  - IF it returns a non-error code, the transaction logic MUST NOT queue a
     58  *    MHD response.
     59  *  - IF it returns an hard error, the transaction logic MUST queue a MHD
     60  *    response and set @a mhd_ret.
     61  *  - IF it returns the soft error code, the function MAY be called again to
     62  *    retry and MUST not queue a MHD response.
     63  *
     64  * @param cls closure with a `struct SetExtensionsContext`
     65  * @param connection MHD request which triggered the transaction
     66  * @param[out] mhd_ret set to MHD response status for @a connection,
     67  *             if transaction failed (!)
     68  * @return transaction status
     69  */
     70 static enum GNUNET_DB_QueryStatus
     71 set_extensions (void *cls,
     72                 struct MHD_Connection *connection,
     73                 MHD_RESULT *mhd_ret)
     74 {
     75   struct SetExtensionsContext *sec = cls;
     76 
     77   /* save the manifests of all extensions */
     78   for (uint32_t i = 0; i<sec->num_extensions; i++)
     79   {
     80     struct Extension *ext = &sec->extensions[i];
     81     const struct TALER_Extension *taler_ext;
     82     enum GNUNET_DB_QueryStatus qs;
     83     char *manifest;
     84 
     85     taler_ext = TALER_extensions_get_by_type (ext->type);
     86     if (NULL == taler_ext)
     87     {
     88       /* No such extension found */
     89       GNUNET_break (0);
     90       return GNUNET_DB_STATUS_HARD_ERROR;
     91     }
     92 
     93     manifest = json_dumps (ext->manifest, JSON_COMPACT | JSON_SORT_KEYS);
     94     if (NULL == manifest)
     95     {
     96       GNUNET_break (0);
     97       *mhd_ret = TALER_MHD_reply_with_error (connection,
     98                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
     99                                              TALER_EC_GENERIC_JSON_INVALID,
    100                                              "convert configuration to string");
    101       return GNUNET_DB_STATUS_HARD_ERROR;
    102     }
    103 
    104     qs = TEH_plugin->set_extension_manifest (
    105       TEH_plugin->cls,
    106       taler_ext->name,
    107       manifest);
    108 
    109     free (manifest);
    110 
    111     if (qs < 0)
    112     {
    113       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    114         return qs;
    115       GNUNET_break (0);
    116       *mhd_ret = TALER_MHD_reply_with_error (connection,
    117                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
    118                                              TALER_EC_GENERIC_DB_STORE_FAILED,
    119                                              "save extension configuration");
    120     }
    121 
    122     /* Success, trigger event */
    123     {
    124       uint32_t nbo_type = htonl (sec->extensions[i].type);
    125       struct GNUNET_DB_EventHeaderP ev = {
    126         .size = htons (sizeof (ev)),
    127         .type = htons (TALER_DBEVENT_EXCHANGE_EXTENSIONS_UPDATED)
    128       };
    129 
    130       TEH_plugin->event_notify (TEH_plugin->cls,
    131                                 &ev,
    132                                 &nbo_type,
    133                                 sizeof(nbo_type));
    134     }
    135 
    136   }
    137 
    138   /* All extensions configured, update the signature */
    139   TEH_extensions_sig = sec->extensions_sig;
    140   TEH_extensions_signed = true;
    141 
    142   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; /* only 'success', so >=0, matters here */
    143 }
    144 
    145 
    146 static enum GNUNET_GenericReturnValue
    147 verify_extensions_from_json (
    148   const json_t *extensions,
    149   struct SetExtensionsContext *sec)
    150 {
    151   const char*name;
    152   const struct TALER_Extension *extension;
    153   size_t i = 0;
    154   json_t *manifest;
    155 
    156   GNUNET_assert (NULL != extensions);
    157   GNUNET_assert (json_is_object (extensions));
    158 
    159   sec->num_extensions = json_object_size (extensions);
    160   sec->extensions = GNUNET_new_array (sec->num_extensions,
    161                                       struct Extension);
    162 
    163   json_object_foreach ((json_t *) extensions, name, manifest)
    164   {
    165     int critical = 0;
    166     json_t *config;
    167     const char *version = NULL;
    168 
    169     /* load and verify criticality, version, etc. */
    170     extension = TALER_extensions_get_by_name (name);
    171     if (NULL == extension)
    172     {
    173       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    174                   "no such extension: %s\n", name);
    175       return GNUNET_SYSERR;
    176     }
    177 
    178     if (GNUNET_OK !=
    179         TALER_extensions_parse_manifest (
    180           manifest, &critical, &version, &config))
    181       return GNUNET_SYSERR;
    182 
    183     if (critical != extension->critical
    184         || 0 != strcmp (version, extension->version) // FIXME-oec: libtool compare
    185         || NULL == config
    186         || GNUNET_OK != extension->load_config (config, NULL))
    187       return GNUNET_SYSERR;
    188 
    189     sec->extensions[i].type = extension->type;
    190     sec->extensions[i].manifest = json_copy (manifest);
    191   }
    192 
    193   return GNUNET_OK;
    194 }
    195 
    196 
    197 MHD_RESULT
    198 TEH_handler_management_post_extensions (
    199   struct MHD_Connection *connection,
    200   const json_t *root)
    201 {
    202   MHD_RESULT ret;
    203   const json_t *extensions;
    204   struct SetExtensionsContext sec = {0};
    205   struct GNUNET_JSON_Specification top_spec[] = {
    206     GNUNET_JSON_spec_object_const ("extensions",
    207                                    &extensions),
    208     GNUNET_JSON_spec_fixed_auto ("extensions_sig",
    209                                  &sec.extensions_sig),
    210     GNUNET_JSON_spec_end ()
    211   };
    212 
    213   /* Parse the top level json structure */
    214   {
    215     enum GNUNET_GenericReturnValue res;
    216 
    217     res = TALER_MHD_parse_json_data (connection,
    218                                      root,
    219                                      top_spec);
    220     if (GNUNET_SYSERR == res)
    221       return MHD_NO; /* hard failure */
    222     if (GNUNET_NO == res)
    223       return MHD_YES; /* failure */
    224   }
    225 
    226   /* Verify the signature */
    227   {
    228     struct TALER_ExtensionManifestsHashP h_manifests;
    229 
    230     if (GNUNET_OK !=
    231         TALER_JSON_extensions_manifests_hash (extensions,
    232                                               &h_manifests) ||
    233         GNUNET_OK !=
    234         TALER_exchange_offline_extension_manifests_hash_verify (
    235           &h_manifests,
    236           &TEH_master_public_key,
    237           &sec.extensions_sig))
    238     {
    239       return TALER_MHD_reply_with_error (
    240         connection,
    241         MHD_HTTP_BAD_REQUEST,
    242         TALER_EC_GENERIC_PARAMETER_MALFORMED,
    243         "invalid signuture");
    244     }
    245   }
    246 
    247   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    248               "Received /management/extensions\n");
    249 
    250   /* Now parse individual extensions and signatures from those objects. */
    251   if (GNUNET_OK !=
    252       verify_extensions_from_json (extensions, &sec))
    253   {
    254     return TALER_MHD_reply_with_error (
    255       connection,
    256       MHD_HTTP_BAD_REQUEST,
    257       TALER_EC_GENERIC_PARAMETER_MALFORMED,
    258       "invalid object");
    259   }
    260 
    261   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    262               "Received %u extensions\n",
    263               sec.num_extensions);
    264 
    265   /* now run the transaction to persist the configurations */
    266   {
    267     enum GNUNET_GenericReturnValue res;
    268 
    269     res = TEH_DB_run_transaction (connection,
    270                                   "set extensions",
    271                                   TEH_MT_REQUEST_OTHER,
    272                                   &ret,
    273                                   &set_extensions,
    274                                   &sec);
    275 
    276     if (GNUNET_SYSERR == res)
    277       goto CLEANUP;
    278   }
    279 
    280   ret = TALER_MHD_reply_static (
    281     connection,
    282     MHD_HTTP_NO_CONTENT,
    283     NULL,
    284     NULL,
    285     0);
    286 
    287 CLEANUP:
    288   for (unsigned int i = 0; i < sec.num_extensions; i++)
    289   {
    290     if (NULL != sec.extensions[i].manifest)
    291     {
    292       json_decref (sec.extensions[i].manifest);
    293     }
    294   }
    295   GNUNET_free (sec.extensions);
    296   return ret;
    297 }
    298 
    299 
    300 /* end of taler-exchange-httpd_management_management_post_extensions.c */