exchange

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

plugin_kyclogic_kycaid.c (43992B)


      1 /*
      2   This file is part of GNU Taler
      3   Copyright (C) 2022--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 Affero 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 Affero General Public License for more details.
     12 
     13   You should have received a copy of the GNU Affero General Public License along with
     14   Taler; see the file COPYING.GPL.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file plugin_kyclogic_kycaid.c
     18  * @brief kycaid for an authentication flow logic
     19  * @author Christian Grothoff
     20  */
     21 #include "taler/platform.h"
     22 #include "taler/taler_attributes.h"
     23 #include "taler/taler_kyclogic_lib.h"
     24 #include "taler/taler_kyclogic_plugin.h"
     25 #include "taler/taler_mhd_lib.h"
     26 #include "taler/taler_curl_lib.h"
     27 #include "taler/taler_json_lib.h"
     28 #include "taler/taler_templating_lib.h"
     29 #include <regex.h>
     30 #include "taler/taler_util.h"
     31 
     32 
     33 /**
     34  * Saves the state of a plugin.
     35  */
     36 struct PluginState
     37 {
     38 
     39   /**
     40    * Our base URL.
     41    */
     42   char *exchange_base_url;
     43 
     44   /**
     45    * Our global configuration.
     46    */
     47   const struct GNUNET_CONFIGURATION_Handle *cfg;
     48 
     49   /**
     50    * Context for CURL operations (useful to the event loop)
     51    */
     52   struct GNUNET_CURL_Context *curl_ctx;
     53 
     54   /**
     55    * Context for integrating @e curl_ctx with the
     56    * GNUnet event loop.
     57    */
     58   struct GNUNET_CURL_RescheduleContext *curl_rc;
     59 
     60 };
     61 
     62 
     63 /**
     64  * Keeps the plugin-specific state for
     65  * a given configuration section.
     66  */
     67 struct TALER_KYCLOGIC_ProviderDetails
     68 {
     69 
     70   /**
     71    * Overall plugin state.
     72    */
     73   struct PluginState *ps;
     74 
     75   /**
     76    * Configuration section that configured us.
     77    */
     78   char *section;
     79 
     80   /**
     81    * Authorization token to use when talking
     82    * to the service.
     83    */
     84   char *auth_token;
     85 
     86   /**
     87    * Form ID for the KYC check to perform.
     88    */
     89   char *form_id;
     90 
     91   /**
     92    * Helper binary to convert attributes returned by
     93    * KYCAID into our internal format.
     94    */
     95   char *conversion_helper;
     96 
     97   /**
     98    * Validity time for a successful KYC process.
     99    */
    100   struct GNUNET_TIME_Relative validity;
    101 
    102   /**
    103    * Curl-ready authentication header to use.
    104    */
    105   struct curl_slist *slist;
    106 
    107 };
    108 
    109 
    110 /**
    111  * Handle for an initiation operation.
    112  */
    113 struct TALER_KYCLOGIC_InitiateHandle
    114 {
    115 
    116   /**
    117    * Hash of the payto:// URI we are initiating
    118    * the KYC for.
    119    */
    120   struct TALER_NormalizedPaytoHashP h_payto;
    121 
    122   /**
    123    * UUID being checked.
    124    */
    125   uint64_t legitimization_uuid;
    126 
    127   /**
    128    * Our configuration details.
    129    */
    130   const struct TALER_KYCLOGIC_ProviderDetails *pd;
    131 
    132   /**
    133    * Continuation to call.
    134    */
    135   TALER_KYCLOGIC_InitiateCallback cb;
    136 
    137   /**
    138    * Closure for @a cb.
    139    */
    140   void *cb_cls;
    141 
    142   /**
    143    * Context for #TEH_curl_easy_post(). Keeps the data that must
    144    * persist for Curl to make the upload.
    145    */
    146   struct TALER_CURL_PostContext ctx;
    147 
    148   /**
    149    * Handle for the request.
    150    */
    151   struct GNUNET_CURL_Job *job;
    152 
    153   /**
    154    * URL of the cURL request.
    155    */
    156   char *url;
    157 
    158 };
    159 
    160 
    161 /**
    162  * Handle for an KYC proof operation.
    163  */
    164 struct TALER_KYCLOGIC_ProofHandle
    165 {
    166 
    167   /**
    168    * Overall plugin state.
    169    */
    170   struct PluginState *ps;
    171 
    172   /**
    173    * Our configuration details.
    174    */
    175   const struct TALER_KYCLOGIC_ProviderDetails *pd;
    176 
    177   /**
    178    * Continuation to call.
    179    */
    180   TALER_KYCLOGIC_ProofCallback cb;
    181 
    182   /**
    183    * Closure for @e cb.
    184    */
    185   void *cb_cls;
    186 
    187   /**
    188    * Connection we are handling.
    189    */
    190   struct MHD_Connection *connection;
    191 
    192   /**
    193    * Task for asynchronous execution.
    194    */
    195   struct GNUNET_SCHEDULER_Task *task;
    196 };
    197 
    198 
    199 /**
    200  * Handle for an KYC Web hook operation.
    201  */
    202 struct TALER_KYCLOGIC_WebhookHandle
    203 {
    204 
    205   /**
    206    * Continuation to call when done.
    207    */
    208   TALER_KYCLOGIC_WebhookCallback cb;
    209 
    210   /**
    211    * Closure for @a cb.
    212    */
    213   void *cb_cls;
    214 
    215   /**
    216    * Task for asynchronous execution.
    217    */
    218   struct GNUNET_SCHEDULER_Task *task;
    219 
    220   /**
    221    * Overall plugin state.
    222    */
    223   struct PluginState *ps;
    224 
    225   /**
    226    * Handle to helper process to extract attributes
    227    * we care about.
    228    */
    229   struct TALER_JSON_ExternalConversion *econ;
    230 
    231   /**
    232    * Our configuration details.
    233    */
    234   const struct TALER_KYCLOGIC_ProviderDetails *pd;
    235 
    236   /**
    237    * Connection we are handling.
    238    */
    239   struct MHD_Connection *connection;
    240 
    241   /**
    242    * JSON response we got back, or NULL for none.
    243    */
    244   json_t *json_response;
    245 
    246   /**
    247    * Verification ID from the service.
    248    */
    249   char *verification_id;
    250 
    251   /**
    252    * Applicant ID from the service.
    253    */
    254   char *applicant_id;
    255 
    256   /**
    257    * URL of the cURL request.
    258    */
    259   char *url;
    260 
    261   /**
    262    * Handle for the request.
    263    */
    264   struct GNUNET_CURL_Job *job;
    265 
    266   /**
    267    * Response to return asynchronously.
    268    */
    269   struct MHD_Response *resp;
    270 
    271   /**
    272    * Our account ID.
    273    */
    274   struct TALER_NormalizedPaytoHashP h_payto;
    275 
    276   /**
    277    * Row in legitimizations for the given
    278    * @e verification_id.
    279    */
    280   uint64_t process_row;
    281 
    282   /**
    283    * HTTP response code we got from KYCAID.
    284    */
    285   unsigned int kycaid_response_code;
    286 
    287   /**
    288    * HTTP response code to return asynchronously.
    289    */
    290   unsigned int response_code;
    291 
    292   /**
    293    * True if @e h_payto is for a wallet.
    294    */
    295   bool is_wallet;
    296 };
    297 
    298 
    299 /**
    300  * Release configuration resources previously loaded
    301  *
    302  * @param[in] pd configuration to release
    303  */
    304 static void
    305 kycaid_unload_configuration (struct TALER_KYCLOGIC_ProviderDetails *pd)
    306 {
    307   curl_slist_free_all (pd->slist);
    308   GNUNET_free (pd->conversion_helper);
    309   GNUNET_free (pd->auth_token);
    310   GNUNET_free (pd->form_id);
    311   GNUNET_free (pd->section);
    312   GNUNET_free (pd);
    313 }
    314 
    315 
    316 /**
    317  * Load the configuration of the KYC provider.
    318  *
    319  * @param cls closure
    320  * @param provider_section_name configuration section to parse
    321  * @return NULL if configuration is invalid
    322  */
    323 static struct TALER_KYCLOGIC_ProviderDetails *
    324 kycaid_load_configuration (void *cls,
    325                            const char *provider_section_name)
    326 {
    327   struct PluginState *ps = cls;
    328   struct TALER_KYCLOGIC_ProviderDetails *pd;
    329 
    330   pd = GNUNET_new (struct TALER_KYCLOGIC_ProviderDetails);
    331   pd->ps = ps;
    332   pd->section = GNUNET_strdup (provider_section_name);
    333   if (GNUNET_OK !=
    334       GNUNET_CONFIGURATION_get_value_time (ps->cfg,
    335                                            provider_section_name,
    336                                            "KYC_KYCAID_VALIDITY",
    337                                            &pd->validity))
    338   {
    339     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    340                                provider_section_name,
    341                                "KYC_KYCAID_VALIDITY");
    342     kycaid_unload_configuration (pd);
    343     return NULL;
    344   }
    345   if (GNUNET_OK !=
    346       GNUNET_CONFIGURATION_get_value_string (ps->cfg,
    347                                              provider_section_name,
    348                                              "KYC_KYCAID_AUTH_TOKEN",
    349                                              &pd->auth_token))
    350   {
    351     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    352                                provider_section_name,
    353                                "KYC_KYCAID_AUTH_TOKEN");
    354     kycaid_unload_configuration (pd);
    355     return NULL;
    356   }
    357   if (GNUNET_OK !=
    358       GNUNET_CONFIGURATION_get_value_string (ps->cfg,
    359                                              provider_section_name,
    360                                              "KYC_KYCAID_FORM_ID",
    361                                              &pd->form_id))
    362   {
    363     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    364                                provider_section_name,
    365                                "KYC_KYCAID_FORM_ID");
    366     kycaid_unload_configuration (pd);
    367     return NULL;
    368   }
    369   if (GNUNET_OK !=
    370       GNUNET_CONFIGURATION_get_value_string (ps->cfg,
    371                                              provider_section_name,
    372                                              "KYC_KYCAID_CONVERTER_HELPER",
    373                                              &pd->conversion_helper))
    374   {
    375     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    376                                provider_section_name,
    377                                "KYC_KYCAID_CONVERTER_HELPER");
    378     kycaid_unload_configuration (pd);
    379     return NULL;
    380   }
    381   {
    382     char *auth;
    383 
    384     GNUNET_asprintf (&auth,
    385                      "%s: Token %s",
    386                      MHD_HTTP_HEADER_AUTHORIZATION,
    387                      pd->auth_token);
    388     pd->slist = curl_slist_append (NULL,
    389                                    auth);
    390     GNUNET_free (auth);
    391   }
    392   return pd;
    393 }
    394 
    395 
    396 /**
    397  * Cancel KYC check initiation.
    398  *
    399  * @param[in] ih handle of operation to cancel
    400  */
    401 static void
    402 kycaid_initiate_cancel (struct TALER_KYCLOGIC_InitiateHandle *ih)
    403 {
    404   if (NULL != ih->job)
    405   {
    406     GNUNET_CURL_job_cancel (ih->job);
    407     ih->job = NULL;
    408   }
    409   GNUNET_free (ih->url);
    410   TALER_curl_easy_post_finished (&ih->ctx);
    411   GNUNET_free (ih);
    412 }
    413 
    414 
    415 /**
    416  * Function called when we're done processing the
    417  * HTTP "/forms/{form_id}/urls" request.
    418  *
    419  * @param cls the `struct TALER_KYCLOGIC_InitiateHandle`
    420  * @param response_code HTTP response code, 0 on error
    421  * @param response parsed JSON result, NULL on error
    422  */
    423 static void
    424 handle_initiate_finished (void *cls,
    425                           long response_code,
    426                           const void *response)
    427 {
    428   struct TALER_KYCLOGIC_InitiateHandle *ih = cls;
    429   const json_t *j = response;
    430 
    431   ih->job = NULL;
    432   switch (response_code)
    433   {
    434   case MHD_HTTP_OK:
    435     {
    436       const char *verification_id;
    437       const char *form_url;
    438       const char *form_id;
    439       struct GNUNET_JSON_Specification spec[] = {
    440         GNUNET_JSON_spec_string ("verification_id",
    441                                  &verification_id),
    442         GNUNET_JSON_spec_string ("form_url",
    443                                  &form_url),
    444         GNUNET_JSON_spec_string ("form_id",
    445                                  &form_id),
    446         GNUNET_JSON_spec_end ()
    447       };
    448 
    449       if (GNUNET_OK !=
    450           GNUNET_JSON_parse (j,
    451                              spec,
    452                              NULL, NULL))
    453       {
    454         GNUNET_break_op (0);
    455         json_dumpf (j,
    456                     stderr,
    457                     JSON_INDENT (2));
    458         ih->cb (ih->cb_cls,
    459                 TALER_EC_EXCHANGE_KYC_GENERIC_PROVIDER_UNEXPECTED_REPLY,
    460                 NULL,
    461                 NULL,
    462                 NULL,
    463                 json_string_value (json_object_get (j,
    464                                                     "type")));
    465         break;
    466       }
    467       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    468                   "Started new verification `%s' using form %s\n",
    469                   verification_id,
    470                   form_id);
    471       ih->cb (ih->cb_cls,
    472               TALER_EC_NONE,
    473               form_url,
    474               NULL, /* no provider_user_id */
    475               verification_id,
    476               NULL /* no error */);
    477       GNUNET_JSON_parse_free (spec);
    478     }
    479     break;
    480   case MHD_HTTP_BAD_REQUEST:
    481   case MHD_HTTP_NOT_FOUND:
    482   case MHD_HTTP_CONFLICT:
    483     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    484                 "KYCAID failed with response %u:\n",
    485                 (unsigned int) response_code);
    486     json_dumpf (j,
    487                 stderr,
    488                 JSON_INDENT (2));
    489     ih->cb (ih->cb_cls,
    490             TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_BUG,
    491             NULL,
    492             NULL,
    493             NULL,
    494             json_string_value (json_object_get (j,
    495                                                 "type")));
    496     break;
    497   case MHD_HTTP_UNAUTHORIZED:
    498   case MHD_HTTP_PAYMENT_REQUIRED:
    499     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    500                 "Refused access with HTTP status code %u\n",
    501                 (unsigned int) response_code);
    502     ih->cb (ih->cb_cls,
    503             TALER_EC_EXCHANGE_KYC_GENERIC_PROVIDER_ACCESS_REFUSED,
    504             NULL,
    505             NULL,
    506             NULL,
    507             json_string_value (json_object_get (j,
    508                                                 "type")));
    509     break;
    510   case MHD_HTTP_REQUEST_TIMEOUT:
    511     ih->cb (ih->cb_cls,
    512             TALER_EC_EXCHANGE_KYC_GENERIC_PROVIDER_TIMEOUT,
    513             NULL,
    514             NULL,
    515             NULL,
    516             json_string_value (json_object_get (j,
    517                                                 "type")));
    518     break;
    519   case MHD_HTTP_UNPROCESSABLE_ENTITY: /* validation */
    520     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    521                 "KYCAID failed with response %u:\n",
    522                 (unsigned int) response_code);
    523     json_dumpf (j,
    524                 stderr,
    525                 JSON_INDENT (2));
    526     ih->cb (ih->cb_cls,
    527             TALER_EC_EXCHANGE_KYC_GENERIC_PROVIDER_UNEXPECTED_REPLY,
    528             NULL,
    529             NULL,
    530             NULL,
    531             json_string_value (json_object_get (j,
    532                                                 "type")));
    533     break;
    534   case MHD_HTTP_TOO_MANY_REQUESTS:
    535     ih->cb (ih->cb_cls,
    536             TALER_EC_EXCHANGE_KYC_GENERIC_PROVIDER_RATE_LIMIT_EXCEEDED,
    537             NULL,
    538             NULL,
    539             NULL,
    540             json_string_value (json_object_get (j,
    541                                                 "type")));
    542     break;
    543   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    544     ih->cb (ih->cb_cls,
    545             TALER_EC_EXCHANGE_KYC_GENERIC_PROVIDER_UNEXPECTED_REPLY,
    546             NULL,
    547             NULL,
    548             NULL,
    549             json_string_value (json_object_get (j,
    550                                                 "type")));
    551     break;
    552   default:
    553     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    554                 "Unexpected KYCAID response %u:\n",
    555                 (unsigned int) response_code);
    556     json_dumpf (j,
    557                 stderr,
    558                 JSON_INDENT (2));
    559     ih->cb (ih->cb_cls,
    560             TALER_EC_EXCHANGE_KYC_GENERIC_PROVIDER_UNEXPECTED_REPLY,
    561             NULL,
    562             NULL,
    563             NULL,
    564             json_string_value (json_object_get (j,
    565                                                 "type")));
    566     break;
    567   }
    568   kycaid_initiate_cancel (ih);
    569 }
    570 
    571 
    572 /**
    573  * Initiate KYC check.
    574  *
    575  * @param cls the @e cls of this struct with the plugin-specific state
    576  * @param pd provider configuration details
    577  * @param account_id which account to trigger process for
    578  * @param legitimization_uuid unique ID for the legitimization process
    579  * @param context additional contextual information for the legi process
    580  * @param cb function to call with the result
    581  * @param cb_cls closure for @a cb
    582  * @return handle to cancel operation early
    583  */
    584 static struct TALER_KYCLOGIC_InitiateHandle *
    585 kycaid_initiate (void *cls,
    586                  const struct TALER_KYCLOGIC_ProviderDetails *pd,
    587                  const struct TALER_NormalizedPaytoHashP *account_id,
    588                  uint64_t legitimization_uuid,
    589                  const json_t *context,
    590                  TALER_KYCLOGIC_InitiateCallback cb,
    591                  void *cb_cls)
    592 {
    593   struct PluginState *ps = cls;
    594   struct TALER_KYCLOGIC_InitiateHandle *ih;
    595   json_t *body;
    596   CURL *eh;
    597 
    598   (void) context;
    599   eh = curl_easy_init ();
    600   if (NULL == eh)
    601   {
    602     GNUNET_break (0);
    603     return NULL;
    604   }
    605   ih = GNUNET_new (struct TALER_KYCLOGIC_InitiateHandle);
    606   ih->legitimization_uuid = legitimization_uuid;
    607   ih->cb = cb;
    608   ih->cb_cls = cb_cls;
    609   ih->h_payto = *account_id;
    610   ih->pd = pd;
    611   GNUNET_asprintf (&ih->url,
    612                    "https://api.kycaid.com/forms/%s/urls",
    613                    pd->form_id);
    614   body = GNUNET_JSON_PACK (
    615     GNUNET_JSON_pack_data64_auto ("external_applicant_id",
    616                                   account_id)
    617     );
    618   GNUNET_break (CURLE_OK ==
    619                 curl_easy_setopt (eh,
    620                                   CURLOPT_VERBOSE,
    621                                   0));
    622   GNUNET_assert (CURLE_OK ==
    623                  curl_easy_setopt (eh,
    624                                    CURLOPT_MAXREDIRS,
    625                                    1L));
    626   GNUNET_break (CURLE_OK ==
    627                 curl_easy_setopt (eh,
    628                                   CURLOPT_URL,
    629                                   ih->url));
    630   if (GNUNET_OK !=
    631       TALER_curl_easy_post (&ih->ctx,
    632                             eh,
    633                             body))
    634   {
    635     GNUNET_break (0);
    636     GNUNET_free (ih->url);
    637     GNUNET_free (ih);
    638     curl_easy_cleanup (eh);
    639     json_decref (body);
    640     return NULL;
    641   }
    642   json_decref (body);
    643   ih->job = GNUNET_CURL_job_add2 (ps->curl_ctx,
    644                                   eh,
    645                                   ih->ctx.headers,
    646                                   &handle_initiate_finished,
    647                                   ih);
    648   GNUNET_CURL_extend_headers (ih->job,
    649                               pd->slist);
    650   return ih;
    651 }
    652 
    653 
    654 /**
    655  * Cancel KYC proof.
    656  *
    657  * @param[in] ph handle of operation to cancel
    658  */
    659 static void
    660 kycaid_proof_cancel (struct TALER_KYCLOGIC_ProofHandle *ph)
    661 {
    662   if (NULL != ph->task)
    663   {
    664     GNUNET_SCHEDULER_cancel (ph->task);
    665     ph->task = NULL;
    666   }
    667   GNUNET_free (ph);
    668 }
    669 
    670 
    671 /**
    672  * Call @a ph callback with HTTP error response.
    673  *
    674  * @param cls proof handle to generate reply for
    675  */
    676 static void
    677 proof_reply (void *cls)
    678 {
    679   struct TALER_KYCLOGIC_ProofHandle *ph = cls;
    680   struct MHD_Response *resp;
    681   enum GNUNET_GenericReturnValue ret;
    682   json_t *body;
    683   unsigned int http_status;
    684 
    685   http_status = MHD_HTTP_BAD_REQUEST;
    686   body = GNUNET_JSON_PACK (
    687     TALER_JSON_pack_ec (TALER_EC_GENERIC_ENDPOINT_UNKNOWN));
    688   GNUNET_assert (NULL != body);
    689   ret = TALER_TEMPLATING_build (ph->connection,
    690                                 &http_status,
    691                                 "kycaid-invalid-request",
    692                                 NULL,
    693                                 NULL,
    694                                 body,
    695                                 &resp);
    696   json_decref (body);
    697   GNUNET_break (GNUNET_SYSERR != ret);
    698   ph->cb (ph->cb_cls,
    699           TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
    700           ph->pd->section,
    701           NULL, /* user id */
    702           NULL, /* provider legi ID */
    703           GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
    704           NULL, /* attributes */
    705           http_status,
    706           resp);
    707 }
    708 
    709 
    710 /**
    711  * Check KYC status and return status to human. Not
    712  * used by KYC AID!
    713  *
    714  * @param cls the @e cls of this struct with the plugin-specific state
    715  * @param pd provider configuration details
    716  * @param connection MHD connection object (for HTTP headers)
    717  * @param account_id which account to trigger process for
    718  * @param process_row row in the legitimization processes table the legitimization is for
    719  * @param provider_user_id user ID (or NULL) the proof is for
    720  * @param provider_legitimization_id legitimization ID the proof is for
    721  * @param cb function to call with the result
    722  * @param cb_cls closure for @a cb
    723  * @return handle to cancel operation early
    724  */
    725 static struct TALER_KYCLOGIC_ProofHandle *
    726 kycaid_proof (void *cls,
    727               const struct TALER_KYCLOGIC_ProviderDetails *pd,
    728               struct MHD_Connection *connection,
    729               const struct TALER_NormalizedPaytoHashP *account_id,
    730               uint64_t process_row,
    731               const char *provider_user_id,
    732               const char *provider_legitimization_id,
    733               TALER_KYCLOGIC_ProofCallback cb,
    734               void *cb_cls)
    735 {
    736   struct PluginState *ps = cls;
    737   struct TALER_KYCLOGIC_ProofHandle *ph;
    738 
    739   ph = GNUNET_new (struct TALER_KYCLOGIC_ProofHandle);
    740   ph->ps = ps;
    741   ph->pd = pd;
    742   ph->cb = cb;
    743   ph->cb_cls = cb_cls;
    744   ph->connection = connection;
    745   ph->task = GNUNET_SCHEDULER_add_now (&proof_reply,
    746                                        ph);
    747   return ph;
    748 }
    749 
    750 
    751 /**
    752  * Cancel KYC webhook execution.
    753  *
    754  * @param[in] wh handle of operation to cancel
    755  */
    756 static void
    757 kycaid_webhook_cancel (struct TALER_KYCLOGIC_WebhookHandle *wh)
    758 {
    759   if (NULL != wh->task)
    760   {
    761     GNUNET_SCHEDULER_cancel (wh->task);
    762     wh->task = NULL;
    763   }
    764   if (NULL != wh->econ)
    765   {
    766     TALER_JSON_external_conversion_stop (wh->econ);
    767     wh->econ = NULL;
    768   }
    769   if (NULL != wh->job)
    770   {
    771     GNUNET_CURL_job_cancel (wh->job);
    772     wh->job = NULL;
    773   }
    774   if (NULL != wh->json_response)
    775   {
    776     json_decref (wh->json_response);
    777     wh->json_response = NULL;
    778   }
    779   GNUNET_free (wh->verification_id);
    780   GNUNET_free (wh->applicant_id);
    781   GNUNET_free (wh->url);
    782   GNUNET_free (wh);
    783 }
    784 
    785 
    786 /**
    787  * Extract KYC failure reasons and log those
    788  *
    789  * @param verifications JSON object with failure details
    790  */
    791 static void
    792 log_failure (const json_t *verifications)
    793 {
    794   const json_t *member;
    795   const char *name;
    796 
    797   json_object_foreach ((json_t *) verifications, name, member)
    798   {
    799     bool iverified;
    800     const char *comment;
    801     struct GNUNET_JSON_Specification spec[] = {
    802       GNUNET_JSON_spec_bool ("verified",
    803                              &iverified),
    804       GNUNET_JSON_spec_string ("comment",
    805                                &comment),
    806       GNUNET_JSON_spec_end ()
    807     };
    808 
    809     if (GNUNET_OK !=
    810         GNUNET_JSON_parse (member,
    811                            spec,
    812                            NULL, NULL))
    813     {
    814       GNUNET_break_op (0);
    815       json_dumpf (member,
    816                   stderr,
    817                   JSON_INDENT (2));
    818       continue;
    819     }
    820     if (iverified)
    821       continue;
    822     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    823                 "KYC verification of attribute `%s' failed: %s\n",
    824                 name,
    825                 comment);
    826   }
    827 }
    828 
    829 
    830 /**
    831  * Type of a callback that receives a JSON @a result.
    832  *
    833  * @param cls closure our `struct TALER_KYCLOGIC_WebhookHandle *`
    834  * @param status_type how did the process die
    835  * @param code termination status code from the process
    836  * @param result converted attribute data, NULL on failure
    837  */
    838 static void
    839 webhook_conversion_cb (void *cls,
    840                        enum GNUNET_OS_ProcessStatusType status_type,
    841                        unsigned long code,
    842                        const json_t *result)
    843 {
    844   struct TALER_KYCLOGIC_WebhookHandle *wh = cls;
    845   struct GNUNET_TIME_Absolute expiration;
    846   struct MHD_Response *resp;
    847 
    848   wh->econ = NULL;
    849   if ( (0 == code) &&
    850        (NULL == result) )
    851   {
    852     /* No result, but *our helper* was OK => bad input */
    853     GNUNET_break_op (0);
    854     json_dumpf (wh->json_response,
    855                 stderr,
    856                 JSON_INDENT (2));
    857     resp = TALER_MHD_MAKE_JSON_PACK (
    858       GNUNET_JSON_pack_uint64 ("kycaid_http_status",
    859                                wh->kycaid_response_code),
    860       GNUNET_JSON_pack_object_incref ("kycaid_body",
    861                                       (json_t *) wh->json_response));
    862     wh->cb (wh->cb_cls,
    863             wh->process_row,
    864             &wh->h_payto,
    865             wh->is_wallet,
    866             wh->pd->section,
    867             wh->applicant_id,
    868             wh->verification_id,
    869             TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
    870             GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
    871             NULL,
    872             MHD_HTTP_BAD_GATEWAY,
    873             resp);
    874     kycaid_webhook_cancel (wh);
    875     return;
    876   }
    877   if (NULL == result)
    878   {
    879     /* Failure in our helper */
    880     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    881                 "Helper exited with status code %d\n",
    882                 (int) code);
    883     json_dumpf (wh->json_response,
    884                 stderr,
    885                 JSON_INDENT (2));
    886     resp = TALER_MHD_MAKE_JSON_PACK (
    887       GNUNET_JSON_pack_uint64 ("kycaid_http_status",
    888                                wh->kycaid_response_code),
    889       GNUNET_JSON_pack_object_incref ("kycaid_body",
    890                                       (json_t *) wh->json_response));
    891     wh->cb (wh->cb_cls,
    892             wh->process_row,
    893             &wh->h_payto,
    894             wh->is_wallet,
    895             wh->pd->section,
    896             wh->applicant_id,
    897             wh->verification_id,
    898             TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
    899             GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
    900             NULL,
    901             MHD_HTTP_BAD_GATEWAY,
    902             resp);
    903     kycaid_webhook_cancel (wh);
    904     return;
    905   }
    906   if (! json_is_string (json_object_get (result,
    907                                          "FORM_ID")))
    908   {
    909     /* Failure in our helper */
    910     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    911                 "Mandatory FORM_ID not set in result\n");
    912     json_dumpf (result,
    913                 stderr,
    914                 JSON_INDENT (2));
    915     resp = TALER_MHD_MAKE_JSON_PACK (
    916       GNUNET_JSON_pack_uint64 ("kycaid_http_status",
    917                                wh->kycaid_response_code),
    918       GNUNET_JSON_pack_object_incref ("kycaid_body",
    919                                       (json_t *) wh->json_response));
    920     wh->cb (wh->cb_cls,
    921             wh->process_row,
    922             &wh->h_payto,
    923             wh->is_wallet,
    924             wh->pd->section,
    925             wh->applicant_id,
    926             wh->verification_id,
    927             TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
    928             GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
    929             NULL,
    930             MHD_HTTP_BAD_GATEWAY,
    931             resp);
    932     kycaid_webhook_cancel (wh);
    933     return;
    934   }
    935 
    936   expiration = GNUNET_TIME_relative_to_absolute (wh->pd->validity);
    937   resp = MHD_create_response_from_buffer_static (0,
    938                                                  "");
    939   wh->cb (wh->cb_cls,
    940           wh->process_row,
    941           &wh->h_payto,
    942           wh->is_wallet,
    943           wh->pd->section,
    944           wh->applicant_id,
    945           wh->verification_id,
    946           TALER_KYCLOGIC_STATUS_SUCCESS,
    947           expiration,
    948           result,
    949           MHD_HTTP_NO_CONTENT,
    950           resp);
    951   kycaid_webhook_cancel (wh);
    952 }
    953 
    954 
    955 /**
    956  * Function called when we're done processing the
    957  * HTTP "/applicants/{verification_id}" request.
    958  *
    959  * @param cls the `struct TALER_KYCLOGIC_WebhookHandle`
    960  * @param response_code HTTP response code, 0 on error
    961  * @param response parsed JSON result, NULL on error
    962  */
    963 static void
    964 handle_webhook_finished (void *cls,
    965                          long response_code,
    966                          const void *response)
    967 {
    968   struct TALER_KYCLOGIC_WebhookHandle *wh = cls;
    969   const json_t *j = response;
    970   struct MHD_Response *resp;
    971 
    972   wh->job = NULL;
    973   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    974               "Webhook returned with HTTP status %u\n",
    975               (unsigned int) response_code);
    976   wh->kycaid_response_code = response_code;
    977   wh->json_response = json_incref ((json_t *) j);
    978   switch (response_code)
    979   {
    980   case MHD_HTTP_OK:
    981     {
    982       const char *profile_status;
    983 
    984       profile_status = json_string_value (
    985         json_object_get (
    986           j,
    987           "profile_status"));
    988       if (0 != strcasecmp ("valid",
    989                            profile_status))
    990       {
    991         enum TALER_KYCLOGIC_KycStatus ks;
    992 
    993         ks = (0 == strcasecmp ("pending",
    994                                profile_status))
    995           ? TALER_KYCLOGIC_STATUS_PENDING
    996           : TALER_KYCLOGIC_STATUS_USER_ABORTED;
    997         resp = MHD_create_response_from_buffer_static (0,
    998                                                        "");
    999         wh->cb (wh->cb_cls,
   1000                 wh->process_row,
   1001                 &wh->h_payto,
   1002                 wh->is_wallet,
   1003                 wh->pd->section,
   1004                 wh->applicant_id,
   1005                 wh->verification_id,
   1006                 ks,
   1007                 GNUNET_TIME_UNIT_ZERO_ABS,
   1008                 NULL,
   1009                 MHD_HTTP_NO_CONTENT,
   1010                 resp);
   1011         break;
   1012       }
   1013       {
   1014         const char *argv[] = {
   1015           wh->pd->conversion_helper,
   1016           "-a",
   1017           wh->pd->auth_token,
   1018           NULL,
   1019         };
   1020 
   1021         wh->econ
   1022           = TALER_JSON_external_conversion_start (
   1023               j,
   1024               &webhook_conversion_cb,
   1025               wh,
   1026               wh->pd->conversion_helper,
   1027               argv);
   1028       }
   1029       if (NULL == wh->econ)
   1030       {
   1031         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1032                     "Failed to start KYCAID conversion helper `%s'\n",
   1033                     wh->pd->conversion_helper);
   1034         resp = TALER_MHD_make_error (
   1035           TALER_EC_EXCHANGE_GENERIC_KYC_CONVERTER_FAILED,
   1036           NULL);
   1037         wh->cb (wh->cb_cls,
   1038                 wh->process_row,
   1039                 &wh->h_payto,
   1040                 wh->is_wallet,
   1041                 wh->pd->section,
   1042                 wh->applicant_id,
   1043                 wh->verification_id,
   1044                 TALER_KYCLOGIC_STATUS_INTERNAL_ERROR,
   1045                 GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
   1046                 NULL,
   1047                 MHD_HTTP_INTERNAL_SERVER_ERROR,
   1048                 resp);
   1049         break;
   1050       }
   1051       return;
   1052     }
   1053     break;
   1054   case MHD_HTTP_BAD_REQUEST:
   1055   case MHD_HTTP_NOT_FOUND:
   1056   case MHD_HTTP_CONFLICT:
   1057     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1058                 "KYCAID failed with response %u:\n",
   1059                 (unsigned int) response_code);
   1060     json_dumpf (j,
   1061                 stderr,
   1062                 JSON_INDENT (2));
   1063     resp = TALER_MHD_MAKE_JSON_PACK (
   1064       GNUNET_JSON_pack_uint64 ("kycaid_http_status",
   1065                                response_code));
   1066     wh->cb (wh->cb_cls,
   1067             wh->process_row,
   1068             &wh->h_payto,
   1069             wh->is_wallet,
   1070             wh->pd->section,
   1071             wh->applicant_id,
   1072             wh->verification_id,
   1073             TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
   1074             GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
   1075             NULL,
   1076             MHD_HTTP_INTERNAL_SERVER_ERROR,
   1077             resp);
   1078     break;
   1079   case MHD_HTTP_UNAUTHORIZED:
   1080   case MHD_HTTP_PAYMENT_REQUIRED:
   1081     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1082                 "Refused access with HTTP status code %u\n",
   1083                 (unsigned int) response_code);
   1084     resp = TALER_MHD_MAKE_JSON_PACK (
   1085       GNUNET_JSON_pack_uint64 ("kycaid_http_status",
   1086                                response_code),
   1087       GNUNET_JSON_pack_object_incref ("kycaid_body",
   1088                                       (json_t *) j));
   1089     wh->cb (wh->cb_cls,
   1090             wh->process_row,
   1091             &wh->h_payto,
   1092             wh->is_wallet,
   1093             wh->pd->section,
   1094             wh->applicant_id,
   1095             wh->verification_id,
   1096             TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
   1097             GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
   1098             NULL,
   1099             MHD_HTTP_NETWORK_AUTHENTICATION_REQUIRED,
   1100             resp);
   1101     break;
   1102   case MHD_HTTP_REQUEST_TIMEOUT:
   1103     resp = TALER_MHD_MAKE_JSON_PACK (
   1104       GNUNET_JSON_pack_uint64 ("kycaid_http_status",
   1105                                response_code),
   1106       GNUNET_JSON_pack_object_incref ("kycaid_body",
   1107                                       (json_t *) j));
   1108     wh->cb (wh->cb_cls,
   1109             wh->process_row,
   1110             &wh->h_payto,
   1111             wh->is_wallet,
   1112             wh->pd->section,
   1113             wh->applicant_id,
   1114             wh->verification_id,
   1115             TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
   1116             GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
   1117             NULL,
   1118             MHD_HTTP_GATEWAY_TIMEOUT,
   1119             resp);
   1120     break;
   1121   case MHD_HTTP_UNPROCESSABLE_ENTITY: /* validation */
   1122     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1123                 "KYCAID failed with response %u:\n",
   1124                 (unsigned int) response_code);
   1125     json_dumpf (j,
   1126                 stderr,
   1127                 JSON_INDENT (2));
   1128     resp = TALER_MHD_MAKE_JSON_PACK (
   1129       GNUNET_JSON_pack_uint64 ("kycaid_http_status",
   1130                                response_code),
   1131       GNUNET_JSON_pack_object_incref ("kycaid_body",
   1132                                       (json_t *) j));
   1133     wh->cb (wh->cb_cls,
   1134             wh->process_row,
   1135             &wh->h_payto,
   1136             wh->is_wallet,
   1137             wh->pd->section,
   1138             wh->applicant_id,
   1139             wh->verification_id,
   1140             TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
   1141             GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
   1142             NULL,
   1143             MHD_HTTP_BAD_GATEWAY,
   1144             resp);
   1145     break;
   1146   case MHD_HTTP_TOO_MANY_REQUESTS:
   1147     resp = TALER_MHD_MAKE_JSON_PACK (
   1148       GNUNET_JSON_pack_uint64 ("kycaid_http_status",
   1149                                response_code),
   1150       GNUNET_JSON_pack_object_incref ("kycaid_body",
   1151                                       (json_t *) j));
   1152     wh->cb (wh->cb_cls,
   1153             wh->process_row,
   1154             &wh->h_payto,
   1155             wh->is_wallet,
   1156             wh->pd->section,
   1157             wh->applicant_id,
   1158             wh->verification_id,
   1159             TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
   1160             GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
   1161             NULL,
   1162             MHD_HTTP_SERVICE_UNAVAILABLE,
   1163             resp);
   1164     break;
   1165   case MHD_HTTP_INTERNAL_SERVER_ERROR:
   1166     resp = TALER_MHD_MAKE_JSON_PACK (
   1167       GNUNET_JSON_pack_uint64 ("kycaid_http_status",
   1168                                response_code),
   1169       GNUNET_JSON_pack_object_incref ("kycaid_body",
   1170                                       (json_t *) j));
   1171     wh->cb (wh->cb_cls,
   1172             wh->process_row,
   1173             &wh->h_payto,
   1174             wh->is_wallet,
   1175             wh->pd->section,
   1176             wh->applicant_id,
   1177             wh->verification_id,
   1178             TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
   1179             GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
   1180             NULL,
   1181             MHD_HTTP_BAD_GATEWAY,
   1182             resp);
   1183     break;
   1184   default:
   1185     resp = TALER_MHD_MAKE_JSON_PACK (
   1186       GNUNET_JSON_pack_uint64 ("kycaid_http_status",
   1187                                response_code),
   1188       GNUNET_JSON_pack_object_incref ("kycaid_body",
   1189                                       (json_t *) j));
   1190     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1191                 "Unexpected KYCAID response %u:\n",
   1192                 (unsigned int) response_code);
   1193     json_dumpf (j,
   1194                 stderr,
   1195                 JSON_INDENT (2));
   1196     wh->cb (wh->cb_cls,
   1197             wh->process_row,
   1198             &wh->h_payto,
   1199             wh->is_wallet,
   1200             wh->pd->section,
   1201             wh->applicant_id,
   1202             wh->verification_id,
   1203             TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
   1204             GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
   1205             NULL,
   1206             MHD_HTTP_BAD_GATEWAY,
   1207             resp);
   1208     break;
   1209   }
   1210   kycaid_webhook_cancel (wh);
   1211 }
   1212 
   1213 
   1214 /**
   1215  * Asynchronously return a reply for the webhook.
   1216  *
   1217  * @param cls a `struct TALER_KYCLOGIC_WebhookHandle *`
   1218  */
   1219 static void
   1220 async_webhook_reply (void *cls)
   1221 {
   1222   struct TALER_KYCLOGIC_WebhookHandle *wh = cls;
   1223 
   1224   wh->task = NULL;
   1225   wh->cb (wh->cb_cls,
   1226           wh->process_row,
   1227           (0 == wh->process_row)
   1228           ? NULL
   1229           : &wh->h_payto,
   1230           wh->is_wallet,
   1231           wh->pd->section,
   1232           wh->applicant_id, /* provider user ID */
   1233           wh->verification_id, /* provider legi ID */
   1234           TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
   1235           GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
   1236           NULL,
   1237           wh->response_code,
   1238           wh->resp);
   1239   kycaid_webhook_cancel (wh);
   1240 }
   1241 
   1242 
   1243 /**
   1244  * Check KYC status and return result for Webhook.  We do NOT implement the
   1245  * authentication check proposed by the KYCAID documentation, as it would
   1246  * allow an attacker who learns the access token to easily bypass the KYC
   1247  * checks. Instead, we insist on explicitly requesting the KYC status from the
   1248  * provider (at least on success).
   1249  *
   1250  * @param cls the @e cls of this struct with the plugin-specific state
   1251  * @param pd provider configuration details
   1252  * @param plc callback to lookup accounts with
   1253  * @param plc_cls closure for @a plc
   1254  * @param http_method HTTP method used for the webhook
   1255  * @param url_path rest of the URL after `/kyc-webhook/`
   1256  * @param connection MHD connection object (for HTTP headers)
   1257  * @param body HTTP request body
   1258  * @param cb function to call with the result
   1259  * @param cb_cls closure for @a cb
   1260  * @return handle to cancel operation early
   1261  */
   1262 static struct TALER_KYCLOGIC_WebhookHandle *
   1263 kycaid_webhook (void *cls,
   1264                 const struct TALER_KYCLOGIC_ProviderDetails *pd,
   1265                 TALER_KYCLOGIC_ProviderLookupCallback plc,
   1266                 void *plc_cls,
   1267                 const char *http_method,
   1268                 const char *const url_path[],
   1269                 struct MHD_Connection *connection,
   1270                 const json_t *body,
   1271                 TALER_KYCLOGIC_WebhookCallback cb,
   1272                 void *cb_cls)
   1273 {
   1274   struct PluginState *ps = cls;
   1275   struct TALER_KYCLOGIC_WebhookHandle *wh;
   1276   CURL *eh;
   1277   const char *request_id;
   1278   const char *type;
   1279   const char *verification_id; /* = provider_legitimization_id */
   1280   const char *applicant_id;
   1281   const char *form_id;
   1282   const char *status = NULL;
   1283   bool verified = false;
   1284   bool no_verified = true;
   1285   const json_t *verifications = NULL;
   1286   struct GNUNET_JSON_Specification spec[] = {
   1287     GNUNET_JSON_spec_string ("request_id",
   1288                              &request_id),
   1289     GNUNET_JSON_spec_string ("type",
   1290                              &type),
   1291     GNUNET_JSON_spec_string ("verification_id",
   1292                              &verification_id),
   1293     GNUNET_JSON_spec_string ("applicant_id",
   1294                              &applicant_id),
   1295     GNUNET_JSON_spec_string ("form_id",
   1296                              &form_id),
   1297     GNUNET_JSON_spec_mark_optional (
   1298       GNUNET_JSON_spec_string ("status",
   1299                                &status),
   1300       NULL),
   1301     GNUNET_JSON_spec_mark_optional (
   1302       GNUNET_JSON_spec_bool ("verified",
   1303                              &verified),
   1304       &no_verified),
   1305     GNUNET_JSON_spec_mark_optional (
   1306       GNUNET_JSON_spec_object_const ("verifications",
   1307                                      &verifications),
   1308       NULL),
   1309     GNUNET_JSON_spec_end ()
   1310   };
   1311   enum GNUNET_DB_QueryStatus qs;
   1312 
   1313   wh = GNUNET_new (struct TALER_KYCLOGIC_WebhookHandle);
   1314   wh->cb = cb;
   1315   wh->cb_cls = cb_cls;
   1316   wh->ps = ps;
   1317   wh->pd = pd;
   1318   wh->connection = connection;
   1319   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1320               "KYCAID webhook of `%s' triggered with %s\n",
   1321               pd->section,
   1322               http_method);
   1323 #if 1
   1324   if (NULL != body)
   1325     json_dumpf (body,
   1326                 stderr,
   1327                 JSON_INDENT (2));
   1328 #endif
   1329   if (NULL == pd)
   1330   {
   1331     GNUNET_break_op (0);
   1332     json_dumpf (body,
   1333                 stderr,
   1334                 JSON_INDENT (2));
   1335     wh->resp = TALER_MHD_make_error (
   1336       TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_UNKNOWN,
   1337       "kycaid");
   1338     wh->response_code = MHD_HTTP_NOT_FOUND;
   1339     wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply,
   1340                                          wh);
   1341     return wh;
   1342   }
   1343 
   1344   if (GNUNET_OK !=
   1345       GNUNET_JSON_parse (body,
   1346                          spec,
   1347                          NULL, NULL))
   1348   {
   1349     GNUNET_break_op (0);
   1350     json_dumpf (body,
   1351                 stderr,
   1352                 JSON_INDENT (2));
   1353     wh->resp = TALER_MHD_MAKE_JSON_PACK (
   1354       GNUNET_JSON_pack_object_incref ("webhook_body",
   1355                                       (json_t *) body));
   1356     wh->response_code = MHD_HTTP_BAD_REQUEST;
   1357     wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply,
   1358                                          wh);
   1359     return wh;
   1360   }
   1361   qs = plc (plc_cls,
   1362             pd->section,
   1363             verification_id,
   1364             &wh->h_payto,
   1365             &wh->is_wallet,
   1366             &wh->process_row);
   1367   if (qs < 0)
   1368   {
   1369     wh->resp = TALER_MHD_make_error (TALER_EC_GENERIC_DB_FETCH_FAILED,
   1370                                      "provider-legitimization-lookup");
   1371     wh->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
   1372     wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply,
   1373                                          wh);
   1374     return wh;
   1375   }
   1376   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
   1377   {
   1378     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1379                 "Received webhook for unknown verification ID `%s' and section `%s'\n",
   1380                 verification_id,
   1381                 pd->section);
   1382     wh->resp = TALER_MHD_make_error (
   1383       TALER_EC_EXCHANGE_KYC_PROOF_REQUEST_UNKNOWN,
   1384       verification_id);
   1385     wh->response_code = MHD_HTTP_NOT_FOUND;
   1386     wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply,
   1387                                          wh);
   1388     return wh;
   1389   }
   1390   wh->verification_id = GNUNET_strdup (verification_id);
   1391   wh->applicant_id = GNUNET_strdup (applicant_id);
   1392   if ( (0 != strcasecmp (type,
   1393                          "VERIFICATION_COMPLETED")) ||
   1394        (no_verified) ||
   1395        (! verified) )
   1396   {
   1397     /* We don't need to re-confirm the failure by
   1398        asking the API again. */
   1399     log_failure (verifications);
   1400     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1401                 "Webhook called with non-completion status: %s\n",
   1402                 type);
   1403     wh->response_code = MHD_HTTP_NO_CONTENT;
   1404     wh->resp = MHD_create_response_from_buffer_static (0,
   1405                                                        "");
   1406     wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply,
   1407                                          wh);
   1408     return wh;
   1409   }
   1410 
   1411   eh = curl_easy_init ();
   1412   if (NULL == eh)
   1413   {
   1414     GNUNET_break (0);
   1415     wh->resp = TALER_MHD_make_error (
   1416       TALER_EC_GENERIC_ALLOCATION_FAILURE,
   1417       NULL);
   1418     wh->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
   1419     wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply,
   1420                                          wh);
   1421     return wh;
   1422   }
   1423 
   1424   GNUNET_asprintf (&wh->url,
   1425                    "https://api.kycaid.com/applicants/%s",
   1426                    applicant_id);
   1427   GNUNET_break (CURLE_OK ==
   1428                 curl_easy_setopt (eh,
   1429                                   CURLOPT_VERBOSE,
   1430                                   0));
   1431   GNUNET_assert (CURLE_OK ==
   1432                  curl_easy_setopt (eh,
   1433                                    CURLOPT_MAXREDIRS,
   1434                                    1L));
   1435   GNUNET_break (CURLE_OK ==
   1436                 curl_easy_setopt (eh,
   1437                                   CURLOPT_URL,
   1438                                   wh->url));
   1439   wh->job = GNUNET_CURL_job_add2 (ps->curl_ctx,
   1440                                   eh,
   1441                                   pd->slist,
   1442                                   &handle_webhook_finished,
   1443                                   wh);
   1444   return wh;
   1445 }
   1446 
   1447 
   1448 /**
   1449  * Initialize kycaid logic plugin
   1450  *
   1451  * @param cls a configuration instance
   1452  * @return NULL on error, otherwise a `struct TALER_KYCLOGIC_Plugin`
   1453  */
   1454 void *
   1455 libtaler_plugin_kyclogic_kycaid_init (void *cls);
   1456 
   1457 /* declaration to avoid compiler warning */
   1458 void *
   1459 libtaler_plugin_kyclogic_kycaid_init (void *cls)
   1460 {
   1461   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
   1462   struct TALER_KYCLOGIC_Plugin *plugin;
   1463   struct PluginState *ps;
   1464 
   1465   ps = GNUNET_new (struct PluginState);
   1466   ps->cfg = cfg;
   1467   if (GNUNET_OK !=
   1468       GNUNET_CONFIGURATION_get_value_string (cfg,
   1469                                              "exchange",
   1470                                              "BASE_URL",
   1471                                              &ps->exchange_base_url))
   1472   {
   1473     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   1474                                "exchange",
   1475                                "BASE_URL");
   1476     GNUNET_free (ps);
   1477     return NULL;
   1478   }
   1479 
   1480   ps->curl_ctx
   1481     = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
   1482                         &ps->curl_rc);
   1483   if (NULL == ps->curl_ctx)
   1484   {
   1485     GNUNET_break (0);
   1486     GNUNET_free (ps->exchange_base_url);
   1487     GNUNET_free (ps);
   1488     return NULL;
   1489   }
   1490   ps->curl_rc = GNUNET_CURL_gnunet_rc_create (ps->curl_ctx);
   1491 
   1492   plugin = GNUNET_new (struct TALER_KYCLOGIC_Plugin);
   1493   plugin->cls = ps;
   1494   plugin->load_configuration
   1495     = &kycaid_load_configuration;
   1496   plugin->unload_configuration
   1497     = &kycaid_unload_configuration;
   1498   plugin->initiate
   1499     = &kycaid_initiate;
   1500   plugin->initiate_cancel
   1501     = &kycaid_initiate_cancel;
   1502   plugin->proof
   1503     = &kycaid_proof;
   1504   plugin->proof_cancel
   1505     = &kycaid_proof_cancel;
   1506   plugin->webhook
   1507     = &kycaid_webhook;
   1508   plugin->webhook_cancel
   1509     = &kycaid_webhook_cancel;
   1510   return plugin;
   1511 }
   1512 
   1513 
   1514 /**
   1515  * Unload authorization plugin
   1516  *
   1517  * @param cls a `struct TALER_KYCLOGIC_Plugin`
   1518  * @return NULL (always)
   1519  */
   1520 void *
   1521 libtaler_plugin_kyclogic_kycaid_done (void *cls);
   1522 
   1523 /* declaration to avoid compiler warning */
   1524 void *
   1525 libtaler_plugin_kyclogic_kycaid_done (void *cls)
   1526 {
   1527   struct TALER_KYCLOGIC_Plugin *plugin = cls;
   1528   struct PluginState *ps = plugin->cls;
   1529 
   1530   if (NULL != ps->curl_ctx)
   1531   {
   1532     GNUNET_CURL_fini (ps->curl_ctx);
   1533     ps->curl_ctx = NULL;
   1534   }
   1535   if (NULL != ps->curl_rc)
   1536   {
   1537     GNUNET_CURL_gnunet_rc_destroy (ps->curl_rc);
   1538     ps->curl_rc = NULL;
   1539   }
   1540   GNUNET_free (ps->exchange_base_url);
   1541   GNUNET_free (ps);
   1542   GNUNET_free (plugin);
   1543   return NULL;
   1544 }