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-legitimizations.c (14620B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2025 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-legitimizations.c
     19  * @brief Implementation of the GET /aml/$OFFICER_PUB/legitimizations requests
     20  * @author Christian Grothoff
     21  */
     22 #include "taler/platform.h"
     23 #include <jansson.h>
     24 #include <microhttpd.h> /* just for HTTP status codes */
     25 #include <gnunet/gnunet_util_lib.h>
     26 #include <gnunet/gnunet_json_lib.h>
     27 #include <gnunet/gnunet_curl_lib.h>
     28 #include "taler/taler_exchange_service.h"
     29 #include "taler/taler_json_lib.h"
     30 #include "taler/taler-exchange/get-aml-OFFICER_PUB-legitimizations.h"
     31 #include "exchange_api_handle.h"
     32 #include "taler/taler_signatures.h"
     33 #include "exchange_api_curl_defaults.h"
     34 
     35 
     36 /**
     37  * Handle for an operation to GET /aml/$OFFICER_PUB/legitimizations.
     38  */
     39 struct TALER_EXCHANGE_GetAmlLegitimizationsHandle
     40 {
     41 
     42   /**
     43    * The exchange base URL for this request.
     44    */
     45   char *exchange_base_url;
     46 
     47   /**
     48    * Our execution context.
     49    */
     50   struct GNUNET_CURL_Context *ctx;
     51 
     52   /**
     53    * Handle for the request.
     54    */
     55   struct GNUNET_CURL_Job *job;
     56 
     57   /**
     58    * Signature of the AML officer.
     59    */
     60   struct TALER_AmlOfficerSignatureP officer_sig;
     61 
     62   /**
     63    * Public key of the AML officer.
     64    */
     65   struct TALER_AmlOfficerPublicKeyP officer_pub;
     66 
     67   /**
     68    * Function to call with the result.
     69    */
     70   TALER_EXCHANGE_GetAmlLegitimizationsCallback cb;
     71 
     72   /**
     73    * Closure for @a cb.
     74    */
     75   TALER_EXCHANGE_GET_AML_LEGITIMIZATIONS_RESULT_CLOSURE *cb_cls;
     76 
     77   /**
     78    * The url for this request.
     79    */
     80   char *url;
     81 
     82   /**
     83    * Request options.
     84    */
     85   struct
     86   {
     87     /**
     88      * Limit on number of results.
     89      */
     90     int64_t limit;
     91 
     92     /**
     93      * Row offset from which to return results.
     94      */
     95     uint64_t offset;
     96 
     97     /**
     98      * Hash of payto URI to filter by, NULL for no filter.
     99      */
    100     const struct TALER_NormalizedPaytoHashP *h_payto;
    101 
    102     /**
    103      * Activity filter.
    104      */
    105     enum TALER_EXCHANGE_YesNoAll active;
    106 
    107   } options;
    108 
    109 };
    110 
    111 
    112 /**
    113  * Parse a single measure details entry from JSON.
    114  *
    115  * @param md_json JSON object to parse
    116  * @param[out] md where to store the result
    117  * @return #GNUNET_OK on success
    118  */
    119 static enum GNUNET_GenericReturnValue
    120 parse_measure_details (
    121   const json_t *md_json,
    122   struct TALER_EXCHANGE_GetAmlLegitimizationsMeasureDetails *md)
    123 {
    124   struct GNUNET_JSON_Specification spec[] = {
    125     GNUNET_JSON_spec_fixed_auto ("h_payto",
    126                                  &md->h_payto),
    127     GNUNET_JSON_spec_uint64 ("rowid",
    128                              &md->rowid),
    129     GNUNET_JSON_spec_timestamp ("start_time",
    130                                 &md->start_time),
    131     GNUNET_JSON_spec_object_const ("measures",
    132                                    &md->measures),
    133     GNUNET_JSON_spec_bool ("is_finished",
    134                            &md->is_finished),
    135     GNUNET_JSON_spec_end ()
    136   };
    137 
    138   if (GNUNET_OK !=
    139       GNUNET_JSON_parse (md_json,
    140                          spec,
    141                          NULL,
    142                          NULL))
    143   {
    144     GNUNET_break_op (0);
    145     return GNUNET_SYSERR;
    146   }
    147   return GNUNET_OK;
    148 }
    149 
    150 
    151 /**
    152  * We received an #MHD_HTTP_OK status code. Handle the JSON
    153  * response.
    154  *
    155  * @param algh handle of the request
    156  * @param j JSON response
    157  * @return #GNUNET_OK on success
    158  */
    159 static enum GNUNET_GenericReturnValue
    160 handle_aml_legitimizations_get_ok (
    161   struct TALER_EXCHANGE_GetAmlLegitimizationsHandle *algh,
    162   const json_t *j)
    163 {
    164   struct TALER_EXCHANGE_GetAmlLegitimizationsResponse result = {
    165     .hr.reply = j,
    166     .hr.http_status = MHD_HTTP_OK
    167   };
    168   const json_t *measures_array;
    169   struct GNUNET_JSON_Specification spec[] = {
    170     GNUNET_JSON_spec_array_const ("measures",
    171                                   &measures_array),
    172     GNUNET_JSON_spec_end ()
    173   };
    174   struct TALER_EXCHANGE_GetAmlLegitimizationsMeasureDetails *measures;
    175 
    176   if (GNUNET_OK !=
    177       GNUNET_JSON_parse (j,
    178                          spec,
    179                          NULL,
    180                          NULL))
    181   {
    182     GNUNET_break_op (0);
    183     return GNUNET_SYSERR;
    184   }
    185 
    186   result.details.ok.measures_length = json_array_size (measures_array);
    187   if (0 == result.details.ok.measures_length)
    188   {
    189     measures = NULL;
    190   }
    191   else
    192   {
    193     measures
    194       = GNUNET_new_array (
    195           result.details.ok.measures_length,
    196           struct TALER_EXCHANGE_GetAmlLegitimizationsMeasureDetails);
    197   }
    198   for (size_t i = 0; i < result.details.ok.measures_length; i++)
    199   {
    200     const json_t *measure_json = json_array_get (measures_array,
    201                                                  i);
    202 
    203     if (GNUNET_OK !=
    204         parse_measure_details (measure_json,
    205                                &measures[i]))
    206     {
    207       GNUNET_free (measures);
    208       return GNUNET_SYSERR;
    209     }
    210   }
    211   result.details.ok.measures = measures;
    212   algh->cb (algh->cb_cls,
    213             &result);
    214   algh->cb = NULL;
    215   GNUNET_free (measures);
    216   return GNUNET_OK;
    217 }
    218 
    219 
    220 /**
    221  * Function called when we're done processing the
    222  * HTTP /aml/$OFFICER_PUB/legitimizations GET request.
    223  *
    224  * @param cls the `struct TALER_EXCHANGE_GetAmlLegitimizationsHandle`
    225  * @param response_code HTTP response code, 0 on error
    226  * @param response parsed JSON result, NULL on error
    227  */
    228 static void
    229 handle_aml_legitimizations_get_finished (void *cls,
    230                                          long response_code,
    231                                          const void *response)
    232 {
    233   struct TALER_EXCHANGE_GetAmlLegitimizationsHandle *algh = cls;
    234   const json_t *j = response;
    235   struct TALER_EXCHANGE_GetAmlLegitimizationsResponse result = {
    236     .hr.reply = j,
    237     .hr.http_status = (unsigned int) response_code
    238   };
    239 
    240   algh->job = NULL;
    241   switch (response_code)
    242   {
    243   case 0:
    244     result.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    245     break;
    246   case MHD_HTTP_OK:
    247     if (GNUNET_OK !=
    248         handle_aml_legitimizations_get_ok (algh,
    249                                            j))
    250     {
    251       result.hr.http_status = 0;
    252       result.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    253     }
    254     break;
    255   case MHD_HTTP_NO_CONTENT:
    256     /* can happen */
    257     break;
    258   case MHD_HTTP_BAD_REQUEST:
    259     /* This should never happen, either us or the exchange is buggy
    260        (or API version conflict); just pass JSON reply to the application */
    261     result.hr.ec = TALER_JSON_get_error_code (j);
    262     result.hr.hint = TALER_JSON_get_error_hint (j);
    263     break;
    264   case MHD_HTTP_UNAUTHORIZED:
    265     /* Invalid officer credentials */
    266     result.hr.ec = TALER_JSON_get_error_code (j);
    267     result.hr.hint = TALER_JSON_get_error_hint (j);
    268     break;
    269   case MHD_HTTP_FORBIDDEN:
    270     /* Officer not authorized for this operation */
    271     result.hr.ec = TALER_JSON_get_error_code (j);
    272     result.hr.hint = TALER_JSON_get_error_hint (j);
    273     break;
    274   case MHD_HTTP_NOT_FOUND:
    275     /* Officer not found */
    276     result.hr.ec = TALER_JSON_get_error_code (j);
    277     result.hr.hint = TALER_JSON_get_error_hint (j);
    278     break;
    279   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    280     /* Server had an internal issue; we should retry, but this API
    281        leaves this to the application */
    282     result.hr.ec = TALER_JSON_get_error_code (j);
    283     result.hr.hint = TALER_JSON_get_error_hint (j);
    284     break;
    285   default:
    286     /* unexpected response code */
    287     GNUNET_break_op (0);
    288     result.hr.ec = TALER_JSON_get_error_code (j);
    289     result.hr.hint = TALER_JSON_get_error_hint (j);
    290     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    291                 "Unexpected response code %u/%d for GET %s\n",
    292                 (unsigned int) response_code,
    293                 (int) result.hr.ec,
    294                 algh->url);
    295     break;
    296   }
    297   if (NULL != algh->cb)
    298   {
    299     algh->cb (algh->cb_cls,
    300               &result);
    301     algh->cb = NULL;
    302   }
    303   TALER_EXCHANGE_get_aml_legitimizations_cancel (algh);
    304 }
    305 
    306 
    307 struct TALER_EXCHANGE_GetAmlLegitimizationsHandle *
    308 TALER_EXCHANGE_get_aml_legitimizations_create (
    309   struct GNUNET_CURL_Context *ctx,
    310   const char *exchange_base_url,
    311   const struct TALER_AmlOfficerPrivateKeyP *officer_priv)
    312 {
    313   struct TALER_EXCHANGE_GetAmlLegitimizationsHandle *algh;
    314 
    315   algh = GNUNET_new (struct TALER_EXCHANGE_GetAmlLegitimizationsHandle);
    316   algh->ctx = ctx;
    317   algh->exchange_base_url = GNUNET_strdup (exchange_base_url);
    318   GNUNET_CRYPTO_eddsa_key_get_public (&officer_priv->eddsa_priv,
    319                                       &algh->officer_pub.eddsa_pub);
    320   TALER_officer_aml_query_sign (officer_priv,
    321                                 &algh->officer_sig);
    322   algh->options.limit = -20; /* Default to last 20 entries */
    323   algh->options.offset = INT64_MAX; /* Default to maximum row id */
    324   algh->options.active = TALER_EXCHANGE_YNA_ALL; /* Default to all */
    325   return algh;
    326 }
    327 
    328 
    329 enum GNUNET_GenericReturnValue
    330 TALER_EXCHANGE_get_aml_legitimizations_set_options_ (
    331   struct TALER_EXCHANGE_GetAmlLegitimizationsHandle *algh,
    332   unsigned int num_options,
    333   const struct TALER_EXCHANGE_GetAmlLegitimizationsOptionValue *options)
    334 {
    335   for (unsigned int i = 0; i < num_options; i++)
    336   {
    337     switch (options[i].option)
    338     {
    339     case TALER_EXCHANGE_GET_AML_LEGITIMIZATIONS_OPTION_END:
    340       return GNUNET_OK;
    341     case TALER_EXCHANGE_GET_AML_LEGITIMIZATIONS_OPTION_LIMIT:
    342       algh->options.limit = options[i].details.limit;
    343       break;
    344     case TALER_EXCHANGE_GET_AML_LEGITIMIZATIONS_OPTION_OFFSET:
    345       algh->options.offset = options[i].details.offset;
    346       break;
    347     case TALER_EXCHANGE_GET_AML_LEGITIMIZATIONS_OPTION_H_PAYTO:
    348       algh->options.h_payto = options[i].details.h_payto;
    349       break;
    350     case TALER_EXCHANGE_GET_AML_LEGITIMIZATIONS_OPTION_ACTIVE:
    351       algh->options.active = options[i].details.active;
    352       break;
    353     default:
    354       GNUNET_break (0);
    355       return GNUNET_NO;
    356     }
    357   }
    358   return GNUNET_OK;
    359 }
    360 
    361 
    362 enum TALER_ErrorCode
    363 TALER_EXCHANGE_get_aml_legitimizations_start (
    364   struct TALER_EXCHANGE_GetAmlLegitimizationsHandle *algh,
    365   TALER_EXCHANGE_GetAmlLegitimizationsCallback cb,
    366   TALER_EXCHANGE_GET_AML_LEGITIMIZATIONS_RESULT_CLOSURE *cb_cls)
    367 {
    368   char officer_pub_str[sizeof (struct TALER_AmlOfficerPublicKeyP) * 2];
    369   char arg_str[sizeof (officer_pub_str) + 64];
    370   char limit_str[24];
    371   char offset_str[24];
    372   char paytoh_str[sizeof (struct TALER_NormalizedPaytoHashP) * 2];
    373 
    374   if (NULL != algh->job)
    375   {
    376     GNUNET_break (0);
    377     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    378   }
    379   algh->cb = cb;
    380   algh->cb_cls = cb_cls;
    381   if (algh->options.offset > INT64_MAX)
    382   {
    383     GNUNET_break (0);
    384     algh->options.offset = INT64_MAX;
    385   }
    386   {
    387     char *end;
    388 
    389     end = GNUNET_STRINGS_data_to_string (
    390       &algh->officer_pub,
    391       sizeof (algh->officer_pub),
    392       officer_pub_str,
    393       sizeof (officer_pub_str));
    394     *end = '\0';
    395   }
    396   if (NULL != algh->options.h_payto)
    397   {
    398     char *end;
    399 
    400     end = GNUNET_STRINGS_data_to_string (
    401       algh->options.h_payto,
    402       sizeof (struct TALER_NormalizedPaytoHashP),
    403       paytoh_str,
    404       sizeof (paytoh_str));
    405     *end = '\0';
    406   }
    407   /* Build query parameters */
    408   GNUNET_snprintf (offset_str,
    409                    sizeof (offset_str),
    410                    "%llu",
    411                    (unsigned long long) algh->options.offset);
    412   GNUNET_snprintf (limit_str,
    413                    sizeof (limit_str),
    414                    "%lld",
    415                    (long long) algh->options.limit);
    416   GNUNET_snprintf (arg_str,
    417                    sizeof (arg_str),
    418                    "aml/%s/legitimizations",
    419                    officer_pub_str);
    420   algh->url = TALER_url_join (algh->exchange_base_url,
    421                               arg_str,
    422                               "limit",
    423                               limit_str,
    424                               "offset",
    425                               ( (algh->options.limit > 0) &&
    426                                 (0 == algh->options.offset) ) ||
    427                               ( (algh->options.limit <= 0) &&
    428                                 (INT64_MAX <= algh->options.offset) )
    429                               ? NULL
    430                               : offset_str,
    431                               "h_payto",
    432                               NULL == algh->options.h_payto
    433                               ? NULL
    434                               : paytoh_str,
    435                               "active",
    436                               TALER_EXCHANGE_YNA_ALL == algh->options.active
    437                               ? NULL
    438                               : TALER_yna_to_string (algh->options.active),
    439                               NULL);
    440   if (NULL == algh->url)
    441   {
    442     GNUNET_break (0);
    443     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    444   }
    445 
    446   {
    447     CURL *eh;
    448     struct curl_slist *job_headers = NULL;
    449 
    450     eh = TALER_EXCHANGE_curl_easy_get_ (algh->url);
    451     if (NULL == eh)
    452     {
    453       GNUNET_break (0);
    454       GNUNET_free (algh->url);
    455       algh->url = NULL;
    456       return TALER_EC_GENERIC_ALLOCATION_FAILURE;
    457     }
    458 
    459     /* Add authentication header for AML officer */
    460     {
    461       char *hdr;
    462       char sig_str[sizeof (algh->officer_sig) * 2];
    463       char *end;
    464 
    465       end = GNUNET_STRINGS_data_to_string (
    466         &algh->officer_sig,
    467         sizeof (algh->officer_sig),
    468         sig_str,
    469         sizeof (sig_str));
    470       *end = '\0';
    471       GNUNET_asprintf (&hdr,
    472                        "%s: %s",
    473                        TALER_AML_OFFICER_SIGNATURE_HEADER,
    474                        sig_str);
    475       job_headers = curl_slist_append (NULL,
    476                                        hdr);
    477       GNUNET_free (hdr);
    478     }
    479     algh->job
    480       = GNUNET_CURL_job_add2 (
    481           algh->ctx,
    482           eh,
    483           job_headers,
    484           &handle_aml_legitimizations_get_finished,
    485           algh);
    486     curl_slist_free_all (job_headers);
    487     if (NULL == algh->job)
    488     {
    489       GNUNET_free (algh->url);
    490       algh->url = NULL;
    491       return TALER_EC_GENERIC_ALLOCATION_FAILURE;
    492     }
    493   }
    494   return TALER_EC_NONE;
    495 }
    496 
    497 
    498 void
    499 TALER_EXCHANGE_get_aml_legitimizations_cancel (
    500   struct TALER_EXCHANGE_GetAmlLegitimizationsHandle *algh)
    501 {
    502   if (NULL != algh->job)
    503   {
    504     GNUNET_CURL_job_cancel (algh->job);
    505     algh->job = NULL;
    506   }
    507   GNUNET_free (algh->exchange_base_url);
    508   GNUNET_free (algh->url);
    509   GNUNET_free (algh);
    510 }
    511 
    512 
    513 /* end of exchange_api_aml_legitimizations_get.c */