exchange

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

exchange_api_get-aml-OFFICER_PUB-attributes-H_NORMALIZED_PAYTO.c (13580B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2023, 2024, 2025, 2026 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_get-aml-OFFICER_PUB-attributes-H_NORMALIZED_PAYTO.c
     19  * @brief Implementation of the /aml/$OFFICER_PUB/attributes request
     20  * @author Christian Grothoff
     21  */
     22 #include <microhttpd.h> /* just for HTTP status codes */
     23 #include <gnunet/gnunet_util_lib.h>
     24 #include <gnunet/gnunet_curl_lib.h>
     25 #include "taler/taler_json_lib.h"
     26 #include "exchange_api_handle.h"
     27 #include "taler/taler_signatures.h"
     28 #include "exchange_api_curl_defaults.h"
     29 #include \
     30   "taler/exchange/get-aml-OFFICER_PUB-attributes-H_NORMALIZED_PAYTO.h"
     31 
     32 
     33 /**
     34  * @brief A GET /aml/$OFFICER_PUB/attributes/$H_NORMALIZED_PAYTO Handle
     35  */
     36 struct TALER_EXCHANGE_GetAmlAttributesHandle
     37 {
     38 
     39   /**
     40    * Base URL of the exchange.
     41    */
     42   char *base_url;
     43 
     44   /**
     45    * The url for this request.
     46    */
     47   char *url;
     48 
     49   /**
     50    * Handle for the request.
     51    */
     52   struct GNUNET_CURL_Job *job;
     53 
     54   /**
     55    * Function to call with the result.
     56    */
     57   TALER_EXCHANGE_GetAmlAttributesCallback cb;
     58 
     59   /**
     60    * Closure for @e cb.
     61    */
     62   TALER_EXCHANGE_GET_AML_ATTRIBUTES_RESULT_CLOSURE *cb_cls;
     63 
     64   /**
     65    * CURL context to use.
     66    */
     67   struct GNUNET_CURL_Context *ctx;
     68 
     69   /**
     70    * Public key of the AML officer (computed from officer_priv in _create).
     71    */
     72   struct TALER_AmlOfficerPublicKeyP officer_pub;
     73 
     74   /**
     75    * Private key of the AML officer (stored for signing in _start).
     76    */
     77   struct TALER_AmlOfficerPrivateKeyP officer_priv;
     78 
     79   /**
     80    * Hash of the normalized payto URI for the account.
     81    */
     82   struct TALER_NormalizedPaytoHashP h_payto;
     83 
     84   /**
     85    * Options set for this request.
     86    */
     87   struct
     88   {
     89     /**
     90      * Limit on the number of results (negative = before offset, positive = after).
     91      * Default: -20.
     92      */
     93     int64_t limit;
     94 
     95     /**
     96      * Row offset threshold. Default: INT64_MAX.
     97      */
     98     uint64_t offset;
     99   } options;
    100 
    101 };
    102 
    103 
    104 /**
    105  * Parse AML attribute collection events from a JSON array.
    106  *
    107  * @param jdetails JSON array with AML attribute collection events
    108  * @param[out] detail_ar where to write the results
    109  * @return #GNUNET_OK on success
    110  */
    111 static enum GNUNET_GenericReturnValue
    112 parse_attributes (
    113   const json_t *jdetails,
    114   struct TALER_EXCHANGE_GetAmlAttributesCollectionEvent *detail_ar)
    115 {
    116   json_t *obj;
    117   size_t idx;
    118 
    119   json_array_foreach (jdetails, idx, obj)
    120   {
    121     struct TALER_EXCHANGE_GetAmlAttributesCollectionEvent *detail
    122       = &detail_ar[idx];
    123     bool by_aml_officer = false;
    124     struct GNUNET_JSON_Specification spec[] = {
    125       GNUNET_JSON_spec_uint64 ("rowid",
    126                                &detail->rowid),
    127       GNUNET_JSON_spec_bool ("by_aml_officer",
    128                              &by_aml_officer),
    129       GNUNET_JSON_spec_mark_optional (
    130         GNUNET_JSON_spec_object_const ("attributes",
    131                                        &detail->attributes),
    132         NULL),
    133       GNUNET_JSON_spec_timestamp ("collection_time",
    134                                   &detail->collection_time),
    135       GNUNET_JSON_spec_end ()
    136     };
    137 
    138     detail->by_aml_officer = false;
    139     detail->attributes = NULL;
    140     if (GNUNET_OK !=
    141         GNUNET_JSON_parse (obj,
    142                            spec,
    143                            NULL,
    144                            NULL))
    145     {
    146       GNUNET_break_op (0);
    147       return GNUNET_SYSERR;
    148     }
    149     detail->by_aml_officer = by_aml_officer;
    150   }
    151   return GNUNET_OK;
    152 }
    153 
    154 
    155 /**
    156  * Parse the provided data from the "200 OK" response.
    157  *
    158  * @param[in,out] aagh handle (callback may be zero'ed out)
    159  * @param json json reply with the data
    160  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
    161  */
    162 static enum GNUNET_GenericReturnValue
    163 parse_get_aml_attributes_ok (
    164   struct TALER_EXCHANGE_GetAmlAttributesHandle *aagh,
    165   const json_t *json)
    166 {
    167   struct TALER_EXCHANGE_GetAmlAttributesResponse lr = {
    168     .hr.reply = json,
    169     .hr.http_status = MHD_HTTP_OK
    170   };
    171   const json_t *jdetails;
    172   struct GNUNET_JSON_Specification spec[] = {
    173     GNUNET_JSON_spec_array_const ("details",
    174                                   &jdetails),
    175     GNUNET_JSON_spec_end ()
    176   };
    177 
    178   if (GNUNET_OK !=
    179       GNUNET_JSON_parse (json,
    180                          spec,
    181                          NULL,
    182                          NULL))
    183   {
    184     GNUNET_break_op (0);
    185     return GNUNET_SYSERR;
    186   }
    187   lr.details.ok.details_length = json_array_size (jdetails);
    188   {
    189     struct TALER_EXCHANGE_GetAmlAttributesCollectionEvent *details;
    190 
    191     if (lr.details.ok.details_length >
    192         GNUNET_MAX_MALLOC_CHECKED / sizeof (*details))
    193     {
    194       GNUNET_break_op (0);
    195       return GNUNET_SYSERR;
    196     }
    197     details = GNUNET_new_array (
    198       GNUNET_NZL (lr.details.ok.details_length),
    199       struct TALER_EXCHANGE_GetAmlAttributesCollectionEvent);
    200     lr.details.ok.details = details;
    201     if (GNUNET_OK !=
    202         parse_attributes (jdetails,
    203                           details))
    204     {
    205       GNUNET_break_op (0);
    206       GNUNET_free (details);
    207       return GNUNET_SYSERR;
    208     }
    209     aagh->cb (aagh->cb_cls,
    210               &lr);
    211     aagh->cb = NULL;
    212     GNUNET_free (details);
    213   }
    214   return GNUNET_OK;
    215 }
    216 
    217 
    218 /**
    219  * Function called when we're done processing the
    220  * HTTP GET /aml/$OFFICER_PUB/attributes/$H_NORMALIZED_PAYTO request (new API).
    221  *
    222  * @param cls the `struct TALER_EXCHANGE_GetAmlAttributesHandle`
    223  * @param response_code HTTP response code, 0 on error
    224  * @param response parsed JSON result, NULL on error
    225  */
    226 static void
    227 handle_get_aml_attributes_finished (void *cls,
    228                                     long response_code,
    229                                     const void *response)
    230 {
    231   struct TALER_EXCHANGE_GetAmlAttributesHandle *aagh = cls;
    232   const json_t *j = response;
    233   struct TALER_EXCHANGE_GetAmlAttributesResponse lr = {
    234     .hr.reply = j,
    235     .hr.http_status = (unsigned int) response_code
    236   };
    237 
    238   aagh->job = NULL;
    239   switch (response_code)
    240   {
    241   case 0:
    242     lr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    243     break;
    244   case MHD_HTTP_OK:
    245     if (GNUNET_OK !=
    246         parse_get_aml_attributes_ok (aagh,
    247                                      j))
    248     {
    249       GNUNET_break_op (0);
    250       lr.hr.http_status = 0;
    251       lr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    252       break;
    253     }
    254     GNUNET_assert (NULL == aagh->cb);
    255     TALER_EXCHANGE_get_aml_attributes_cancel (aagh);
    256     return;
    257   case MHD_HTTP_NO_CONTENT:
    258     break;
    259   case MHD_HTTP_NOT_IMPLEMENTED:
    260     lr.hr.ec = TALER_JSON_get_error_code (j);
    261     lr.hr.hint = TALER_JSON_get_error_hint (j);
    262     break;
    263   case MHD_HTTP_BAD_REQUEST:
    264     lr.hr.ec = TALER_JSON_get_error_code (j);
    265     lr.hr.hint = TALER_JSON_get_error_hint (j);
    266     /* This should never happen, either us or the exchange is buggy
    267        (or API version conflict); just pass JSON reply to the application */
    268     break;
    269   case MHD_HTTP_FORBIDDEN:
    270     lr.hr.ec = TALER_JSON_get_error_code (j);
    271     lr.hr.hint = TALER_JSON_get_error_hint (j);
    272     break;
    273   case MHD_HTTP_NOT_FOUND:
    274     lr.hr.ec = TALER_JSON_get_error_code (j);
    275     lr.hr.hint = TALER_JSON_get_error_hint (j);
    276     break;
    277   case MHD_HTTP_NOT_ACCEPTABLE:
    278     lr.hr.ec = TALER_JSON_get_error_code (j);
    279     lr.hr.hint = TALER_JSON_get_error_hint (j);
    280     break;
    281   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    282     lr.hr.ec = TALER_JSON_get_error_code (j);
    283     lr.hr.hint = TALER_JSON_get_error_hint (j);
    284     /* Server had an internal issue; we should retry, but this API
    285        leaves this to the application */
    286     break;
    287   default:
    288     /* unexpected response code */
    289     GNUNET_break_op (0);
    290     lr.hr.ec = TALER_JSON_get_error_code (j);
    291     lr.hr.hint = TALER_JSON_get_error_hint (j);
    292     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    293                 "Unexpected response code %u/%d for get AML attributes\n",
    294                 (unsigned int) response_code,
    295                 (int) lr.hr.ec);
    296     break;
    297   }
    298   if (NULL != aagh->cb)
    299     aagh->cb (aagh->cb_cls,
    300               &lr);
    301   TALER_EXCHANGE_get_aml_attributes_cancel (aagh);
    302 }
    303 
    304 
    305 struct TALER_EXCHANGE_GetAmlAttributesHandle *
    306 TALER_EXCHANGE_get_aml_attributes_create (
    307   struct GNUNET_CURL_Context *ctx,
    308   const char *url,
    309   const struct TALER_AmlOfficerPrivateKeyP *officer_priv,
    310   const struct TALER_NormalizedPaytoHashP *h_payto)
    311 {
    312   struct TALER_EXCHANGE_GetAmlAttributesHandle *aagh;
    313 
    314   aagh = GNUNET_new (struct TALER_EXCHANGE_GetAmlAttributesHandle);
    315   aagh->ctx = ctx;
    316   aagh->base_url = GNUNET_strdup (url);
    317   aagh->h_payto = *h_payto;
    318   aagh->officer_priv = *officer_priv;
    319   GNUNET_CRYPTO_eddsa_key_get_public (&officer_priv->eddsa_priv,
    320                                       &aagh->officer_pub.eddsa_pub);
    321   aagh->options.limit = -20;
    322   aagh->options.offset = INT64_MAX;
    323   return aagh;
    324 }
    325 
    326 
    327 enum GNUNET_GenericReturnValue
    328 TALER_EXCHANGE_get_aml_attributes_set_options_ (
    329   struct TALER_EXCHANGE_GetAmlAttributesHandle *aagh,
    330   unsigned int num_options,
    331   const struct TALER_EXCHANGE_GetAmlAttributesOptionValue *options)
    332 {
    333   for (unsigned int i = 0; i < num_options; i++)
    334   {
    335     const struct TALER_EXCHANGE_GetAmlAttributesOptionValue *opt = &options[i];
    336 
    337     switch (opt->option)
    338     {
    339     case TALER_EXCHANGE_GET_AML_ATTRIBUTES_OPTION_END:
    340       return GNUNET_OK;
    341     case TALER_EXCHANGE_GET_AML_ATTRIBUTES_OPTION_LIMIT:
    342       aagh->options.limit = opt->details.limit;
    343       break;
    344     case TALER_EXCHANGE_GET_AML_ATTRIBUTES_OPTION_OFFSET:
    345       if (opt->details.offset > INT64_MAX)
    346       {
    347         GNUNET_break (0);
    348         return GNUNET_NO;
    349       }
    350       aagh->options.offset = opt->details.offset;
    351       break;
    352     }
    353   }
    354   return GNUNET_OK;
    355 }
    356 
    357 
    358 enum TALER_ErrorCode
    359 TALER_EXCHANGE_get_aml_attributes_start (
    360   struct TALER_EXCHANGE_GetAmlAttributesHandle *aagh,
    361   TALER_EXCHANGE_GetAmlAttributesCallback cb,
    362   TALER_EXCHANGE_GET_AML_ATTRIBUTES_RESULT_CLOSURE *cb_cls)
    363 {
    364   struct TALER_AmlOfficerSignatureP officer_sig;
    365   char arg_str[sizeof (aagh->officer_pub) * 2
    366                + sizeof (aagh->h_payto) * 2
    367                + 32];
    368   CURL *eh;
    369 
    370   if (NULL != aagh->job)
    371   {
    372     GNUNET_break (0);
    373     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    374   }
    375   aagh->cb = cb;
    376   aagh->cb_cls = cb_cls;
    377   TALER_officer_aml_query_sign (&aagh->officer_priv,
    378                                 &officer_sig);
    379   {
    380     char payto_s[sizeof (aagh->h_payto) * 2];
    381     char pub_str[sizeof (aagh->officer_pub) * 2];
    382     char *end;
    383 
    384     end = GNUNET_STRINGS_data_to_string (
    385       &aagh->h_payto,
    386       sizeof (aagh->h_payto),
    387       payto_s,
    388       sizeof (payto_s));
    389     *end = '\0';
    390     end = GNUNET_STRINGS_data_to_string (
    391       &aagh->officer_pub,
    392       sizeof (aagh->officer_pub),
    393       pub_str,
    394       sizeof (pub_str));
    395     *end = '\0';
    396     GNUNET_snprintf (arg_str,
    397                      sizeof (arg_str),
    398                      "aml/%s/attributes/%s",
    399                      pub_str,
    400                      payto_s);
    401   }
    402   {
    403     char limit_s[24];
    404     char offset_s[24];
    405 
    406     GNUNET_snprintf (limit_s,
    407                      sizeof (limit_s),
    408                      "%lld",
    409                      (long long) aagh->options.limit);
    410     GNUNET_snprintf (offset_s,
    411                      sizeof (offset_s),
    412                      "%llu",
    413                      (unsigned long long) aagh->options.offset);
    414     aagh->url = TALER_url_join (aagh->base_url,
    415                                 arg_str,
    416                                 "limit",
    417                                 limit_s,
    418                                 "offset",
    419                                 offset_s,
    420                                 NULL);
    421   }
    422   if (NULL == aagh->url)
    423     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
    424   eh = TALER_EXCHANGE_curl_easy_get_ (aagh->url);
    425   if (NULL == eh)
    426   {
    427     GNUNET_free (aagh->url);
    428     aagh->url = NULL;
    429     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
    430   }
    431   {
    432     struct curl_slist *job_headers = NULL;
    433     char *hdr;
    434     char sig_str[sizeof (officer_sig) * 2];
    435     char *end;
    436 
    437     end = GNUNET_STRINGS_data_to_string (
    438       &officer_sig,
    439       sizeof (officer_sig),
    440       sig_str,
    441       sizeof (sig_str));
    442     *end = '\0';
    443     GNUNET_asprintf (&hdr,
    444                      "%s: %s",
    445                      TALER_AML_OFFICER_SIGNATURE_HEADER,
    446                      sig_str);
    447     job_headers = curl_slist_append (NULL,
    448                                      hdr);
    449     GNUNET_free (hdr);
    450     aagh->job = GNUNET_CURL_job_add2 (aagh->ctx,
    451                                       eh,
    452                                       job_headers,
    453                                       &handle_get_aml_attributes_finished,
    454                                       aagh);
    455     curl_slist_free_all (job_headers);
    456   }
    457   if (NULL == aagh->job)
    458   {
    459     GNUNET_free (aagh->url);
    460     aagh->url = NULL;
    461     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    462   }
    463   return TALER_EC_NONE;
    464 }
    465 
    466 
    467 void
    468 TALER_EXCHANGE_get_aml_attributes_cancel (
    469   struct TALER_EXCHANGE_GetAmlAttributesHandle *aagh)
    470 {
    471   if (NULL != aagh->job)
    472   {
    473     GNUNET_CURL_job_cancel (aagh->job);
    474     aagh->job = NULL;
    475   }
    476   GNUNET_free (aagh->url);
    477   GNUNET_free (aagh->base_url);
    478   GNUNET_free (aagh);
    479 }
    480 
    481 
    482 /* end of exchange_api_lookup_kyc_attributes.c */