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-kyc-check-H_NORMALIZED_PAYTO.c (14017B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2021-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-kyc-check-H_NORMALIZED_PAYTO.c
     19  * @brief Implementation of the /kyc-check request
     20  * @author Christian Grothoff
     21  */
     22 #include "platform.h"  /* UNNECESSARY? */
     23 #include <microhttpd.h> /* just for HTTP check codes */
     24 #include <gnunet/gnunet_util_lib.h>
     25 #include <gnunet/gnunet_curl_lib.h>
     26 #include "taler/taler_exchange_service.h"  /* UNNECESSARY? */
     27 #include "taler/taler_json_lib.h"
     28 #include "taler/exchange/get-kyc-check-H_NORMALIZED_PAYTO.h"
     29 #include "taler/taler_signatures.h"
     30 #include "exchange_api_curl_defaults.h"
     31 
     32 
     33 /**
     34  * @brief A GET /kyc-check/$H_NORMALIZED_PAYTO handle
     35  */
     36 struct TALER_EXCHANGE_GetKycCheckHandle
     37 {
     38 
     39   /**
     40    * The base URL for this request.
     41    */
     42   char *base_url;
     43 
     44   /**
     45    * The full URL for this request, set during _start.
     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_GetKycCheckCallback cb;
     58 
     59   /**
     60    * Closure for @e cb.
     61    */
     62   TALER_EXCHANGE_GET_KYC_CHECK_RESULT_CLOSURE *cb_cls;
     63 
     64   /**
     65    * Reference to the execution context.
     66    */
     67   struct GNUNET_CURL_Context *ctx;
     68 
     69   /**
     70    * Hash of the payto URI we are checking.
     71    */
     72   struct TALER_NormalizedPaytoHashP h_payto;
     73 
     74   /**
     75    * Private key to authorize the request.
     76    */
     77   union TALER_AccountPrivateKeyP account_priv;
     78 
     79   /**
     80    * Long polling target.
     81    */
     82   enum TALER_EXCHANGE_KycLongPollTarget lpt;
     83 
     84   /**
     85    * Latest known rule generation (for long polling).
     86    */
     87   uint64_t known_rule_gen;
     88 
     89   /**
     90    * Long polling timeout.
     91    */
     92   struct GNUNET_TIME_Relative timeout;
     93 
     94 };
     95 
     96 
     97 /**
     98  * Parse an account KYC status from JSON and invoke the callback.
     99  *
    100  * @param[in,out] gkch handle
    101  * @param j JSON to parse
    102  * @param res response to fill
    103  * @param status account status field within @a res to fill
    104  * @return #GNUNET_OK on success
    105  */
    106 static enum GNUNET_GenericReturnValue
    107 parse_account_status (
    108   struct TALER_EXCHANGE_GetKycCheckHandle *gkch,
    109   const json_t *j,
    110   struct TALER_EXCHANGE_GetKycCheckResponse *res,
    111   struct TALER_EXCHANGE_AccountKycStatus *status)
    112 {
    113   const json_t *limits = NULL;
    114   struct GNUNET_JSON_Specification spec[] = {
    115     GNUNET_JSON_spec_bool ("aml_review",
    116                            &status->aml_review),
    117     GNUNET_JSON_spec_uint64 ("rule_gen",
    118                              &status->rule_gen),
    119     GNUNET_JSON_spec_fixed_auto ("access_token",
    120                                  &status->access_token),
    121     GNUNET_JSON_spec_mark_optional (
    122       GNUNET_JSON_spec_string ("tos_required",
    123                                &status->tos_required),
    124       NULL),
    125     GNUNET_JSON_spec_mark_optional (
    126       GNUNET_JSON_spec_array_const ("limits",
    127                                     &limits),
    128       NULL),
    129     GNUNET_JSON_spec_end ()
    130   };
    131 
    132   if (GNUNET_OK !=
    133       GNUNET_JSON_parse (j,
    134                          spec,
    135                          NULL, NULL))
    136   {
    137     GNUNET_break_op (0);
    138     return GNUNET_SYSERR;
    139   }
    140   if ( (NULL != limits) &&
    141        (0 != json_array_size (limits)) )
    142   {
    143     size_t limit_length = json_array_size (limits);
    144     struct TALER_EXCHANGE_AccountLimit *ala;
    145     size_t i;
    146     json_t *limit;
    147 
    148     if (limit_length >
    149         GNUNET_MAX_MALLOC_CHECKED / sizeof (*ala))
    150     {
    151       GNUNET_break_op (0);
    152       return GNUNET_SYSERR;
    153     }
    154     ala = GNUNET_new_array (limit_length,
    155                             struct TALER_EXCHANGE_AccountLimit);
    156     json_array_foreach (limits, i, limit)
    157     {
    158       struct TALER_EXCHANGE_AccountLimit *al = &ala[i];
    159       struct GNUNET_JSON_Specification ispec[] = {
    160         GNUNET_JSON_spec_mark_optional (
    161           GNUNET_JSON_spec_bool ("soft_limit",
    162                                  &al->soft_limit),
    163           NULL),
    164         GNUNET_JSON_spec_relative_time ("timeframe",
    165                                         &al->timeframe),
    166         TALER_JSON_spec_kycte ("operation_type",
    167                                &al->operation_type),
    168         TALER_JSON_spec_amount_any ("threshold",
    169                                     &al->threshold),
    170         GNUNET_JSON_spec_end ()
    171       };
    172 
    173       al->soft_limit = false;
    174       if (GNUNET_OK !=
    175           GNUNET_JSON_parse (limit,
    176                              ispec,
    177                              NULL, NULL))
    178       {
    179         GNUNET_break_op (0);
    180         GNUNET_free (ala);
    181         return GNUNET_SYSERR;
    182       }
    183     }
    184     status->limits = ala;
    185     status->limits_length = limit_length;
    186     gkch->cb (gkch->cb_cls,
    187               res);
    188     GNUNET_free (ala);
    189   }
    190   else
    191   {
    192     gkch->cb (gkch->cb_cls,
    193               res);
    194   }
    195   GNUNET_JSON_parse_free (spec);
    196   return GNUNET_OK;
    197 }
    198 
    199 
    200 /**
    201  * Function called when we're done processing the
    202  * HTTP GET /kyc-check/$H_NORMALIZED_PAYTO request.
    203  *
    204  * @param cls the `struct TALER_EXCHANGE_GetKycCheckHandle`
    205  * @param response_code HTTP response code, 0 on error
    206  * @param response parsed JSON result, NULL on error
    207  */
    208 static void
    209 handle_kyc_check_finished (void *cls,
    210                            long response_code,
    211                            const void *response)
    212 {
    213   struct TALER_EXCHANGE_GetKycCheckHandle *gkch = cls;
    214   const json_t *j = response;
    215   struct TALER_EXCHANGE_GetKycCheckResponse ks = {
    216     .hr.reply = j,
    217     .hr.http_status = (unsigned int) response_code
    218   };
    219 
    220   gkch->job = NULL;
    221   switch (response_code)
    222   {
    223   case 0:
    224     ks.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    225     break;
    226   case MHD_HTTP_OK:
    227     {
    228       if (GNUNET_OK !=
    229           parse_account_status (gkch,
    230                                 j,
    231                                 &ks,
    232                                 &ks.details.ok))
    233       {
    234         GNUNET_break_op (0);
    235         ks.hr.http_status = 0;
    236         ks.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    237         break;
    238       }
    239       TALER_EXCHANGE_get_kyc_check_cancel (gkch);
    240       return;
    241     }
    242   case MHD_HTTP_ACCEPTED:
    243     {
    244       if (GNUNET_OK !=
    245           parse_account_status (gkch,
    246                                 j,
    247                                 &ks,
    248                                 &ks.details.accepted))
    249       {
    250         GNUNET_break_op (0);
    251         ks.hr.http_status = 0;
    252         ks.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    253         break;
    254       }
    255       TALER_EXCHANGE_get_kyc_check_cancel (gkch);
    256       return;
    257     }
    258   case MHD_HTTP_NO_CONTENT:
    259     break;
    260   case MHD_HTTP_BAD_REQUEST:
    261     ks.hr.ec = TALER_JSON_get_error_code (j);
    262     break;
    263   case MHD_HTTP_FORBIDDEN:
    264     {
    265       struct GNUNET_JSON_Specification spec[] = {
    266         GNUNET_JSON_spec_fixed_auto (
    267           "expected_account_pub",
    268           &ks.details.forbidden.expected_account_pub),
    269         TALER_JSON_spec_ec ("code",
    270                             &ks.hr.ec),
    271         GNUNET_JSON_spec_end ()
    272       };
    273 
    274       if (GNUNET_OK !=
    275           GNUNET_JSON_parse (j,
    276                              spec,
    277                              NULL, NULL))
    278       {
    279         GNUNET_break_op (0);
    280         ks.hr.http_status = 0;
    281         ks.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    282         break;
    283       }
    284       break;
    285     }
    286   case MHD_HTTP_NOT_FOUND:
    287     ks.hr.ec = TALER_JSON_get_error_code (j);
    288     break;
    289   case MHD_HTTP_CONFLICT:
    290     ks.hr.ec = TALER_JSON_get_error_code (j);
    291     break;
    292   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    293     ks.hr.ec = TALER_JSON_get_error_code (j);
    294     break;
    295   default:
    296     /* unexpected response code */
    297     GNUNET_break_op (0);
    298     ks.hr.ec = TALER_JSON_get_error_code (j);
    299     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    300                 "Unexpected response code %u/%d for exchange kyc_check\n",
    301                 (unsigned int) response_code,
    302                 (int) ks.hr.ec);
    303     break;
    304   }
    305   if (NULL != gkch->cb)
    306   {
    307     gkch->cb (gkch->cb_cls,
    308               &ks);
    309     gkch->cb = NULL;
    310   }
    311   TALER_EXCHANGE_get_kyc_check_cancel (gkch);
    312 }
    313 
    314 
    315 struct TALER_EXCHANGE_GetKycCheckHandle *
    316 TALER_EXCHANGE_get_kyc_check_create (
    317   struct GNUNET_CURL_Context *ctx,
    318   const char *url,
    319   const struct TALER_NormalizedPaytoHashP *h_payto,
    320   const union TALER_AccountPrivateKeyP *pk)
    321 {
    322   struct TALER_EXCHANGE_GetKycCheckHandle *gkch;
    323 
    324   gkch = GNUNET_new (struct TALER_EXCHANGE_GetKycCheckHandle);
    325   gkch->ctx = ctx;
    326   gkch->base_url = GNUNET_strdup (url);
    327   gkch->h_payto = *h_payto;
    328   gkch->account_priv = *pk;
    329   return gkch;
    330 }
    331 
    332 
    333 enum GNUNET_GenericReturnValue
    334 TALER_EXCHANGE_get_kyc_check_set_options_ (
    335   struct TALER_EXCHANGE_GetKycCheckHandle *gkch,
    336   unsigned int num_options,
    337   const struct TALER_EXCHANGE_GetKycCheckOptionValue *options)
    338 {
    339   for (unsigned int i = 0; i < num_options; i++)
    340   {
    341     const struct TALER_EXCHANGE_GetKycCheckOptionValue *opt = &options[i];
    342 
    343     switch (opt->option)
    344     {
    345     case TALER_EXCHANGE_GET_KYC_CHECK_OPTION_END:
    346       return GNUNET_OK;
    347     case TALER_EXCHANGE_GET_KYC_CHECK_OPTION_KNOWN_RULE_GEN:
    348       gkch->known_rule_gen = opt->details.known_rule_gen;
    349       break;
    350     case TALER_EXCHANGE_GET_KYC_CHECK_OPTION_LPT:
    351       gkch->lpt = opt->details.lpt;
    352       break;
    353     case TALER_EXCHANGE_GET_KYC_CHECK_OPTION_TIMEOUT:
    354       gkch->timeout = opt->details.timeout;
    355       break;
    356     default:
    357       GNUNET_break (0);
    358       return GNUNET_SYSERR;
    359     }
    360   }
    361   return GNUNET_OK;
    362 }
    363 
    364 
    365 enum TALER_ErrorCode
    366 TALER_EXCHANGE_get_kyc_check_start (
    367   struct TALER_EXCHANGE_GetKycCheckHandle *gkch,
    368   TALER_EXCHANGE_GetKycCheckCallback cb,
    369   TALER_EXCHANGE_GET_KYC_CHECK_RESULT_CLOSURE *cb_cls)
    370 {
    371   CURL *eh;
    372   char arg_str[128];
    373   char timeout_ms[32];
    374   char lpt_str[32];
    375   char krg_str[32];
    376   struct curl_slist *job_headers = NULL;
    377   unsigned long long tms;
    378 
    379   gkch->cb = cb;
    380   gkch->cb_cls = cb_cls;
    381   {
    382     char *hps;
    383 
    384     hps = GNUNET_STRINGS_data_to_string_alloc (
    385       &gkch->h_payto,
    386       sizeof (gkch->h_payto));
    387     GNUNET_snprintf (arg_str,
    388                      sizeof (arg_str),
    389                      "kyc-check/%s",
    390                      hps);
    391     GNUNET_free (hps);
    392   }
    393   tms = gkch->timeout.rel_value_us
    394         / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us;
    395   GNUNET_snprintf (timeout_ms,
    396                    sizeof (timeout_ms),
    397                    "%llu",
    398                    tms);
    399   GNUNET_snprintf (krg_str,
    400                    sizeof (krg_str),
    401                    "%llu",
    402                    (unsigned long long) gkch->known_rule_gen);
    403   GNUNET_snprintf (lpt_str,
    404                    sizeof (lpt_str),
    405                    "%d",
    406                    (int) gkch->lpt);
    407   gkch->url
    408     = TALER_url_join (
    409         gkch->base_url,
    410         arg_str,
    411         "timeout_ms",
    412         GNUNET_TIME_relative_is_zero (gkch->timeout)
    413         ? NULL
    414         : timeout_ms,
    415         "min_rule",
    416         0 == gkch->known_rule_gen
    417         ? NULL
    418         : krg_str,
    419         "lpt",
    420         TALER_EXCHANGE_KLPT_NONE == gkch->lpt
    421         ? NULL
    422         : lpt_str,
    423         NULL);
    424   if (NULL == gkch->url)
    425     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
    426   eh = TALER_EXCHANGE_curl_easy_get_ (gkch->url);
    427   if (NULL == eh)
    428   {
    429     GNUNET_break (0);
    430     GNUNET_free (gkch->url);
    431     gkch->url = NULL;
    432     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    433   }
    434   if (0 != tms)
    435   {
    436     GNUNET_break (CURLE_OK ==
    437                   curl_easy_setopt (eh,
    438                                     CURLOPT_TIMEOUT_MS,
    439                                     (long) (tms + 500L)));
    440   }
    441   {
    442     union TALER_AccountPublicKeyP account_pub;
    443     union TALER_AccountSignatureP account_sig;
    444     char *sig_hdr;
    445     char *pub_hdr;
    446     char *hdr;
    447 
    448     GNUNET_CRYPTO_eddsa_key_get_public (
    449       &gkch->account_priv.reserve_priv.eddsa_priv,
    450       &account_pub.reserve_pub.eddsa_pub);
    451     TALER_account_kyc_auth_sign (&gkch->account_priv,
    452                                  &account_sig);
    453 
    454     sig_hdr = GNUNET_STRINGS_data_to_string_alloc (
    455       &account_sig,
    456       sizeof (account_sig));
    457     GNUNET_asprintf (&hdr,
    458                      "%s: %s",
    459                      TALER_HTTP_HEADER_ACCOUNT_OWNER_SIGNATURE,
    460                      sig_hdr);
    461     GNUNET_free (sig_hdr);
    462     job_headers = curl_slist_append (job_headers,
    463                                      hdr);
    464     GNUNET_free (hdr);
    465 
    466     pub_hdr = GNUNET_STRINGS_data_to_string_alloc (
    467       &account_pub,
    468       sizeof (account_pub));
    469     GNUNET_asprintf (&hdr,
    470                      "%s: %s",
    471                      TALER_HTTP_HEADER_ACCOUNT_OWNER_PUBKEY,
    472                      pub_hdr);
    473     GNUNET_free (pub_hdr);
    474     job_headers = curl_slist_append (job_headers,
    475                                      hdr);
    476     GNUNET_free (hdr);
    477     if (NULL == job_headers)
    478     {
    479       GNUNET_break (0);
    480       curl_easy_cleanup (eh);
    481       GNUNET_free (gkch->url);
    482       gkch->url = NULL;
    483       return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    484     }
    485   }
    486   gkch->job
    487     = GNUNET_CURL_job_add2 (gkch->ctx,
    488                             eh,
    489                             job_headers,
    490                             &handle_kyc_check_finished,
    491                             gkch);
    492   curl_slist_free_all (job_headers);
    493   if (NULL == gkch->job)
    494   {
    495     GNUNET_free (gkch->url);
    496     gkch->url = NULL;
    497     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    498   }
    499   return TALER_EC_NONE;
    500 }
    501 
    502 
    503 void
    504 TALER_EXCHANGE_get_kyc_check_cancel (
    505   struct TALER_EXCHANGE_GetKycCheckHandle *gkch)
    506 {
    507   if (NULL != gkch->job)
    508   {
    509     GNUNET_CURL_job_cancel (gkch->job);
    510     gkch->job = NULL;
    511   }
    512   GNUNET_free (gkch->url);
    513   GNUNET_free (gkch->base_url);
    514   GNUNET_free (gkch);
    515 }
    516 
    517 
    518 /* end of exchange_api_get-kyc-check-H_NORMALIZED_PAYTO.c */