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_post-management-extensions.c (9000B)


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