exchange

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

exchange_api_lookup_kyc_attributes.c (10863B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2023, 2024 Taler Systems SA
      4 
      5   TALER 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   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 General Public License for more details.
     12 
     13   You should have received a copy of the GNU General Public License along with
     14   TALER; see the file COPYING.  If not, see
     15   <http://www.gnu.org/licenses/>
     16 */
     17 /**
     18  * @file lib/exchange_api_lookup_kyc_attributes.c
     19  * @brief Implementation of the /aml/$OFFICER_PUB/attributes request
     20  * @author Christian Grothoff
     21  */
     22 #include "taler/platform.h"
     23 #include <microhttpd.h> /* just for HTTP status codes */
     24 #include <gnunet/gnunet_util_lib.h>
     25 #include <gnunet/gnunet_curl_lib.h>
     26 #include "taler/taler_exchange_service.h"
     27 #include "taler/taler_json_lib.h"
     28 #include "exchange_api_handle.h"
     29 #include "taler/taler_signatures.h"
     30 #include "exchange_api_curl_defaults.h"
     31 
     32 
     33 /**
     34  * @brief A GET /aml/$OFFICER_PUB/attributes Handle
     35  */
     36 struct TALER_EXCHANGE_LookupKycAttributes
     37 {
     38 
     39   /**
     40    * The url for this request.
     41    */
     42   char *url;
     43 
     44   /**
     45    * Handle for the request.
     46    */
     47   struct GNUNET_CURL_Job *job;
     48 
     49   /**
     50    * Function to call with the result.
     51    */
     52   TALER_EXCHANGE_LookupKycAttributesCallback attributes_cb;
     53 
     54   /**
     55    * Closure for @e cb.
     56    */
     57   void *attributes_cb_cls;
     58 
     59   /**
     60    * HTTP headers for the job.
     61    */
     62   struct curl_slist *job_headers;
     63 
     64 };
     65 
     66 
     67 /**
     68  * Parse AML decision summary array.
     69  *
     70  * @param[in,out] lh handle to use for allocations
     71  * @param jdetails JSON array with AML decision summaries
     72  * @param[out] detail_ar where to write the result
     73  * @return #GNUNET_OK on success
     74  */
     75 static enum GNUNET_GenericReturnValue
     76 parse_kyc_attributes (
     77   struct TALER_EXCHANGE_LookupKycAttributes *lh,
     78   const json_t *jdetails,
     79   struct TALER_EXCHANGE_KycAttributeDetail *detail_ar)
     80 {
     81   json_t *obj;
     82   size_t idx;
     83 
     84   json_array_foreach (jdetails, idx, obj)
     85   {
     86     struct TALER_EXCHANGE_KycAttributeDetail *detail
     87       = &detail_ar[idx];
     88     struct GNUNET_JSON_Specification spec[] = {
     89       GNUNET_JSON_spec_uint64 ("rowid",
     90                                &detail->row_id),
     91       GNUNET_JSON_spec_mark_optional (
     92         GNUNET_JSON_spec_string ("provider_name",
     93                                  &detail->provider_name),
     94         NULL),
     95       GNUNET_JSON_spec_object_const ("attributes",
     96                                      &detail->attributes),
     97       GNUNET_JSON_spec_timestamp ("collection_time",
     98                                   &detail->collection_time),
     99       GNUNET_JSON_spec_end ()
    100     };
    101 
    102     if (GNUNET_OK !=
    103         GNUNET_JSON_parse (obj,
    104                            spec,
    105                            NULL,
    106                            NULL))
    107     {
    108       GNUNET_break_op (0);
    109       return GNUNET_SYSERR;
    110     }
    111   }
    112   return GNUNET_OK;
    113 }
    114 
    115 
    116 /**
    117  * Parse the provided decision data from the "200 OK" response.
    118  *
    119  * @param[in,out] lh handle (callback may be zero'ed out)
    120  * @param json json reply with the data for one coin
    121  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
    122  */
    123 static enum GNUNET_GenericReturnValue
    124 parse_attributes_ok (struct TALER_EXCHANGE_LookupKycAttributes *lh,
    125                      const json_t *json)
    126 {
    127   struct TALER_EXCHANGE_KycAttributesResponse lr = {
    128     .hr.reply = json,
    129     .hr.http_status = MHD_HTTP_OK
    130   };
    131   const json_t *jdetails;
    132   struct GNUNET_JSON_Specification spec[] = {
    133     GNUNET_JSON_spec_array_const ("details",
    134                                   &jdetails),
    135     GNUNET_JSON_spec_end ()
    136   };
    137 
    138   if (GNUNET_OK !=
    139       GNUNET_JSON_parse (json,
    140                          spec,
    141                          NULL,
    142                          NULL))
    143   {
    144     GNUNET_break_op (0);
    145     return GNUNET_SYSERR;
    146   }
    147   lr.details.ok.kyc_attributes_length
    148     = json_array_size (jdetails);
    149   {
    150     struct TALER_EXCHANGE_KycAttributeDetail details[
    151       GNUNET_NZL (lr.details.ok.kyc_attributes_length)];
    152     enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
    153 
    154     memset (details,
    155             0,
    156             sizeof (details));
    157     lr.details.ok.kyc_attributes = details;
    158     ret = parse_kyc_attributes (lh,
    159                                 jdetails,
    160                                 details);
    161     if (GNUNET_OK == ret)
    162     {
    163       lh->attributes_cb (lh->attributes_cb_cls,
    164                          &lr);
    165       lh->attributes_cb = NULL;
    166     }
    167     return ret;
    168   }
    169 }
    170 
    171 
    172 /**
    173  * Function called when we're done processing the
    174  * HTTP /aml/$OFFICER_PUB/attributes request.
    175  *
    176  * @param cls the `struct TALER_EXCHANGE_LookupKycAttributes`
    177  * @param response_code HTTP response code, 0 on error
    178  * @param response parsed JSON result, NULL on error
    179  */
    180 static void
    181 handle_lookup_finished (void *cls,
    182                         long response_code,
    183                         const void *response)
    184 {
    185   struct TALER_EXCHANGE_LookupKycAttributes *lh = cls;
    186   const json_t *j = response;
    187   struct TALER_EXCHANGE_KycAttributesResponse lr = {
    188     .hr.reply = j,
    189     .hr.http_status = (unsigned int) response_code
    190   };
    191 
    192   lh->job = NULL;
    193   switch (response_code)
    194   {
    195   case 0:
    196     lr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    197     break;
    198   case MHD_HTTP_OK:
    199     if (GNUNET_OK !=
    200         parse_attributes_ok (lh,
    201                              j))
    202     {
    203       GNUNET_break_op (0);
    204       lr.hr.http_status = 0;
    205       lr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    206       break;
    207     }
    208     GNUNET_assert (NULL == lh->attributes_cb);
    209     TALER_EXCHANGE_lookup_kyc_attributes_cancel (lh);
    210     return;
    211   case MHD_HTTP_NO_CONTENT:
    212     break;
    213   case MHD_HTTP_BAD_REQUEST:
    214     lr.hr.ec = TALER_JSON_get_error_code (j);
    215     lr.hr.hint = TALER_JSON_get_error_hint (j);
    216     /* This should never happen, either us or the exchange is buggy
    217        (or API version conflict); just pass JSON reply to the application */
    218     break;
    219   case MHD_HTTP_FORBIDDEN:
    220     lr.hr.ec = TALER_JSON_get_error_code (j);
    221     lr.hr.hint = TALER_JSON_get_error_hint (j);
    222     /* Nothing really to verify, exchange says this coin was not melted; we
    223        should pass the JSON reply to the application */
    224     break;
    225   case MHD_HTTP_NOT_FOUND:
    226     lr.hr.ec = TALER_JSON_get_error_code (j);
    227     lr.hr.hint = TALER_JSON_get_error_hint (j);
    228     /* Nothing really to verify, exchange says this coin was not melted; we
    229        should pass the JSON reply to the application */
    230     break;
    231   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    232     lr.hr.ec = TALER_JSON_get_error_code (j);
    233     lr.hr.hint = TALER_JSON_get_error_hint (j);
    234     /* Server had an internal issue; we should retry, but this API
    235        leaves this to the application */
    236     break;
    237   default:
    238     /* unexpected response code */
    239     GNUNET_break_op (0);
    240     lr.hr.ec = TALER_JSON_get_error_code (j);
    241     lr.hr.hint = TALER_JSON_get_error_hint (j);
    242     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    243                 "Unexpected response code %u/%d for lookup AML attributes\n",
    244                 (unsigned int) response_code,
    245                 (int) lr.hr.ec);
    246     break;
    247   }
    248   if (NULL != lh->attributes_cb)
    249     lh->attributes_cb (lh->attributes_cb_cls,
    250                        &lr);
    251   TALER_EXCHANGE_lookup_kyc_attributes_cancel (lh);
    252 }
    253 
    254 
    255 struct TALER_EXCHANGE_LookupKycAttributes *
    256 TALER_EXCHANGE_lookup_kyc_attributes (
    257   struct GNUNET_CURL_Context *ctx,
    258   const char *exchange_url,
    259   const struct TALER_NormalizedPaytoHashP *h_payto,
    260   uint64_t offset,
    261   int64_t limit,
    262   const struct TALER_AmlOfficerPrivateKeyP *officer_priv,
    263   TALER_EXCHANGE_LookupKycAttributesCallback cb,
    264   void *cb_cls)
    265 {
    266   struct TALER_EXCHANGE_LookupKycAttributes *lh;
    267   CURL *eh;
    268   struct TALER_AmlOfficerPublicKeyP officer_pub;
    269   struct TALER_AmlOfficerSignatureP officer_sig;
    270   char arg_str[sizeof (officer_pub) * 2
    271                + sizeof (*h_payto) * 2
    272                + 32];
    273 
    274   GNUNET_CRYPTO_eddsa_key_get_public (&officer_priv->eddsa_priv,
    275                                       &officer_pub.eddsa_pub);
    276   TALER_officer_aml_query_sign (officer_priv,
    277                                 &officer_sig);
    278 
    279   {
    280     char payto_s[sizeof (*h_payto) * 2];
    281     char pub_str[sizeof (officer_pub) * 2];
    282     char *end;
    283 
    284     end = GNUNET_STRINGS_data_to_string (
    285       h_payto,
    286       sizeof (*h_payto),
    287       payto_s,
    288       sizeof (payto_s));
    289     *end = '\0';
    290     end = GNUNET_STRINGS_data_to_string (
    291       &officer_pub,
    292       sizeof (officer_pub),
    293       pub_str,
    294       sizeof (pub_str));
    295     *end = '\0';
    296     GNUNET_snprintf (arg_str,
    297                      sizeof (arg_str),
    298                      "aml/%s/attributes/%s",
    299                      pub_str,
    300                      payto_s);
    301   }
    302   lh = GNUNET_new (struct TALER_EXCHANGE_LookupKycAttributes);
    303   lh->attributes_cb = cb;
    304   lh->attributes_cb_cls = cb_cls;
    305   {
    306     char limit_s[24];
    307     char offset_s[24];
    308 
    309     GNUNET_snprintf (limit_s,
    310                      sizeof (limit_s),
    311                      "%lld",
    312                      (long long) limit);
    313     GNUNET_snprintf (offset_s,
    314                      sizeof (offset_s),
    315                      "%llu",
    316                      (unsigned long long) offset);
    317     lh->url = TALER_url_join (
    318       exchange_url,
    319       arg_str,
    320       "limit",
    321       limit_s,
    322       "offset",
    323       offset_s,
    324       "h_payto",
    325       NULL);
    326   }
    327   if (NULL == lh->url)
    328   {
    329     GNUNET_free (lh);
    330     return NULL;
    331   }
    332   eh = TALER_EXCHANGE_curl_easy_get_ (lh->url);
    333   if (NULL == eh)
    334   {
    335     GNUNET_break (0);
    336     GNUNET_free (lh->url);
    337     GNUNET_free (lh);
    338     return NULL;
    339   }
    340   {
    341     char *hdr;
    342     char sig_str[sizeof (officer_sig) * 2];
    343     char *end;
    344 
    345     end = GNUNET_STRINGS_data_to_string (
    346       &officer_sig,
    347       sizeof (officer_sig),
    348       sig_str,
    349       sizeof (sig_str));
    350     *end = '\0';
    351 
    352     GNUNET_asprintf (&hdr,
    353                      "%s: %s",
    354                      TALER_AML_OFFICER_SIGNATURE_HEADER,
    355                      sig_str);
    356     lh->job_headers = curl_slist_append (NULL,
    357                                          hdr);
    358     GNUNET_free (hdr);
    359     lh->job_headers = curl_slist_append (lh->job_headers,
    360                                          "Content-type: application/json");
    361     lh->job = GNUNET_CURL_job_add2 (ctx,
    362                                     eh,
    363                                     lh->job_headers,
    364                                     &handle_lookup_finished,
    365                                     lh);
    366   }
    367   return lh;
    368 }
    369 
    370 
    371 void
    372 TALER_EXCHANGE_lookup_kyc_attributes_cancel (
    373   struct TALER_EXCHANGE_LookupKycAttributes *lh)
    374 {
    375   if (NULL != lh->job)
    376   {
    377     GNUNET_CURL_job_cancel (lh->job);
    378     lh->job = NULL;
    379   }
    380   curl_slist_free_all (lh->job_headers);
    381   GNUNET_free (lh->url);
    382   GNUNET_free (lh);
    383 }
    384 
    385 
    386 /* end of exchange_api_lookup_kyc_attributes.c */