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 (13369B)


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