anastasis

Credential backup and recovery protocol and service
Log | Files | Refs | Submodules | README | LICENSE

anastasis_api_config.c (8637B)


      1 /*
      2   This file is part of Anastasis
      3   Copyright (C) 2020, 2021 Anastasis SARL
      4 
      5   Anastasis is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   Anastasis 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 General Public License for more details.
     12 
     13   You should have received a copy of the GNU General Public License along with
     14   Anastasis; see the file COPYING.GPL.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file restclient/anastasis_api_config.c
     18  * @brief Implementation of the /config GET
     19  * @author Christian Grothoff
     20  * @author Dennis Neufeld
     21  * @author Dominik Meister
     22  */
     23 #include "platform.h"
     24 #include <curl/curl.h>
     25 #include <microhttpd.h> /* just for HTTP status codes */
     26 #include "anastasis_service.h"
     27 #include "anastasis_api_curl_defaults.h"
     28 #include <gnunet/gnunet_json_lib.h>
     29 #include <taler/taler_json_lib.h>
     30 
     31 
     32 /**
     33  * Which version of the Taler protocol is implemented
     34  * by this library?  Used to determine compatibility.
     35  */
     36 #define ANASTASIS_PROTOCOL_CURRENT 0
     37 
     38 /**
     39  * How many versions are we backwards compatible with?
     40  */
     41 #define ANASTASIS_PROTOCOL_AGE 0
     42 
     43 
     44 struct ANASTASIS_ConfigOperation
     45 {
     46   /**
     47    * The url for this request.
     48    */
     49   char *url;
     50 
     51   /**
     52    * Handle for the request.
     53    */
     54   struct GNUNET_CURL_Job *job;
     55 
     56   /**
     57    * Reference to the execution context.
     58    */
     59   struct GNUNET_CURL_Context *ctx;
     60 
     61   /**
     62   * The callback to pass the backend response to
     63   */
     64   ANASTASIS_ConfigCallback cb;
     65 
     66   /**
     67    * Closure for @a cb.
     68    */
     69   void *cb_cls;
     70 
     71 };
     72 
     73 
     74 /**
     75  * Function called when we're done processing the
     76  * HTTP /config request.
     77  *
     78  * @param cls the `struct ANASTASIS_ConfigOperation`
     79  * @param response_code HTTP response code, 0 on error
     80  * @param response parsed JSON result, NULL on error
     81  */
     82 static void
     83 handle_config_finished (void *cls,
     84                         long response_code,
     85                         const void *response)
     86 {
     87   struct ANASTASIS_ConfigOperation *co = cls;
     88   const json_t *json = response;
     89   struct ANASTASIS_Config acfg = {
     90     .http_status = response_code,
     91     .response = json
     92   };
     93 
     94   co->job = NULL;
     95   switch (response_code)
     96   {
     97   case 0:
     98     /* No reply received */
     99     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    100                 "Backend `%s' failed to respond to GET /config\n",
    101                 co->url);
    102     break;
    103   case MHD_HTTP_OK:
    104     {
    105       const char *name;
    106       const json_t *methods;
    107       struct TALER_JSON_ProtocolVersion pv;
    108       struct GNUNET_JSON_Specification spec[] = {
    109         GNUNET_JSON_spec_string ("name",
    110                                  &name),
    111         GNUNET_JSON_spec_string ("business_name",
    112                                  &acfg.details.ok.business_name),
    113         GNUNET_JSON_spec_string ("version",
    114                                  &acfg.details.ok.version),
    115         TALER_JSON_spec_version ("version",
    116                                  &pv),
    117         GNUNET_JSON_spec_array_const ("methods",
    118                                       &methods),
    119         GNUNET_JSON_spec_uint32 ("storage_limit_in_megabytes",
    120                                  &acfg.details.ok.storage_limit_in_megabytes),
    121         TALER_JSON_spec_amount_any ("annual_fee",
    122                                     &acfg.details.ok.annual_fee),
    123         TALER_JSON_spec_amount_any ("truth_upload_fee",
    124                                     &acfg.details.ok.truth_upload_fee),
    125         TALER_JSON_spec_amount_any ("liability_limit",
    126                                     &acfg.details.ok.liability_limit),
    127         GNUNET_JSON_spec_fixed_auto ("provider_salt",
    128                                      &acfg.details.ok.provider_salt),
    129         GNUNET_JSON_spec_end ()
    130       };
    131 
    132       if (GNUNET_OK !=
    133           GNUNET_JSON_parse (json,
    134                              spec,
    135                              NULL, NULL))
    136       {
    137         GNUNET_break_op (0);
    138         json_dumpf (json,
    139                     stderr,
    140                     JSON_INDENT (2));
    141         acfg.http_status = 0;
    142         acfg.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    143         break;
    144       }
    145       if (0 != strcmp (name,
    146                        "anastasis"))
    147       {
    148         GNUNET_JSON_parse_free (spec);
    149         acfg.http_status = 0;
    150         acfg.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    151         break;
    152       }
    153       if ( (ANASTASIS_PROTOCOL_CURRENT < pv.current) &&
    154            (ANASTASIS_PROTOCOL_CURRENT < pv.current - pv.age) )
    155       {
    156         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    157                     "Provider protocol version too new\n");
    158         acfg.http_status = 0;
    159         acfg.ec = TALER_EC_GENERIC_VERSION_MALFORMED;
    160         break;
    161       }
    162       if ( (ANASTASIS_PROTOCOL_CURRENT > pv.current) &&
    163            (ANASTASIS_PROTOCOL_CURRENT - ANASTASIS_PROTOCOL_AGE > pv.current) )
    164       {
    165         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    166                     "Provider protocol version too old\n");
    167         GNUNET_break_op (0);
    168         acfg.http_status = 0;
    169         acfg.ec = TALER_EC_GENERIC_VERSION_MALFORMED;
    170         break;
    171       }
    172       acfg.details.ok.methods_length = (unsigned int) json_array_size (methods);
    173       if (((size_t) acfg.details.ok.methods_length) !=
    174           json_array_size (methods))
    175       {
    176         GNUNET_break_op (0);
    177         acfg.http_status = 0;
    178         acfg.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    179         break;
    180       }
    181       {
    182         struct ANASTASIS_AuthorizationMethodConfig mcfg[
    183           GNUNET_NZL (acfg.details.ok.methods_length)];
    184 
    185         for (unsigned int i = 0; i<acfg.details.ok.methods_length; i++)
    186         {
    187           struct ANASTASIS_AuthorizationMethodConfig *m = &mcfg[i];
    188           struct GNUNET_JSON_Specification ispec[] = {
    189             GNUNET_JSON_spec_string ("type",
    190                                      &m->type),
    191             TALER_JSON_spec_amount_any ("cost",
    192                                         &m->usage_fee),
    193             GNUNET_JSON_spec_end ()
    194           };
    195 
    196           if ( (GNUNET_OK !=
    197                 GNUNET_JSON_parse (json_array_get (methods,
    198                                                    i),
    199                                    ispec,
    200                                    NULL, NULL)) )
    201           {
    202             GNUNET_break_op (0);
    203             acfg.http_status = 0;
    204             acfg.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    205             goto end;
    206           }
    207         }
    208         acfg.details.ok.methods = mcfg;
    209         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    210                     "Good backend found at `%s'\n",
    211                     co->url);
    212         co->cb (co->cb_cls,
    213                 &acfg);
    214         ANASTASIS_config_cancel (co);
    215         return;
    216       }
    217     }
    218   case MHD_HTTP_BAD_REQUEST:
    219     /* This should never happen, either us or the anastasis server is buggy
    220        (or API version conflict); just pass JSON reply to the application */
    221     break;
    222   case MHD_HTTP_NOT_FOUND:
    223     /* Nothing really to verify */
    224     break;
    225   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    226     /* Server had an internal issue; we should retry, but this API
    227        leaves this to the application */
    228     break;
    229   default:
    230     /* unexpected response code */
    231     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    232                 "Unexpected response code %u\n",
    233                 (unsigned int) response_code);
    234     GNUNET_break_op (0);
    235     break;
    236   }
    237 end:
    238   co->cb (co->cb_cls,
    239           &acfg);
    240   ANASTASIS_config_cancel (co);
    241 }
    242 
    243 
    244 struct ANASTASIS_ConfigOperation *
    245 ANASTASIS_get_config (struct GNUNET_CURL_Context *ctx,
    246                       const char *base_url,
    247                       ANASTASIS_ConfigCallback cb,
    248                       void *cb_cls)
    249 {
    250   struct ANASTASIS_ConfigOperation *co;
    251 
    252   co = GNUNET_new (struct ANASTASIS_ConfigOperation);
    253   co->url = TALER_url_join (base_url,
    254                             "config",
    255                             NULL);
    256   co->ctx = ctx;
    257   co->cb = cb;
    258   co->cb_cls = cb_cls;
    259   {
    260     CURL *eh;
    261 
    262     eh = ANASTASIS_curl_easy_get_ (co->url);
    263     co->job = GNUNET_CURL_job_add (ctx,
    264                                    eh,
    265                                    &handle_config_finished,
    266                                    co);
    267   }
    268   if (NULL == co->job)
    269   {
    270     GNUNET_free (co->url);
    271     GNUNET_free (co);
    272     return NULL;
    273   }
    274   return co;
    275 }
    276 
    277 
    278 void
    279 ANASTASIS_config_cancel (struct ANASTASIS_ConfigOperation *co)
    280 {
    281   if (NULL != co->job)
    282   {
    283     GNUNET_CURL_job_cancel (co->job);
    284     co->job = NULL;
    285   }
    286   GNUNET_free (co->url);
    287   GNUNET_free (co);
    288 }
    289 
    290 
    291 /* end of anastasis_api_config.c */