LCOV - code coverage report
Current view: top level - kyclogic - plugin_kyclogic_kycaid.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 11.8 % 498 59
Test Date: 2026-03-10 12:10:57 Functions: 25.0 % 16 4

            Line data    Source code
       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           61 : kycaid_unload_configuration (struct TALER_KYCLOGIC_ProviderDetails *pd)
     306              : {
     307           61 :   curl_slist_free_all (pd->slist);
     308           61 :   GNUNET_free (pd->conversion_helper);
     309           61 :   GNUNET_free (pd->auth_token);
     310           61 :   GNUNET_free (pd->form_id);
     311           61 :   GNUNET_free (pd->section);
     312           61 :   GNUNET_free (pd);
     313           61 : }
     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           61 : kycaid_load_configuration (void *cls,
     325              :                            const char *provider_section_name)
     326              : {
     327           61 :   struct PluginState *ps = cls;
     328              :   struct TALER_KYCLOGIC_ProviderDetails *pd;
     329              : 
     330           61 :   pd = GNUNET_new (struct TALER_KYCLOGIC_ProviderDetails);
     331           61 :   pd->ps = ps;
     332           61 :   pd->section = GNUNET_strdup (provider_section_name);
     333           61 :   if (GNUNET_OK !=
     334           61 :       GNUNET_CONFIGURATION_get_value_time (ps->cfg,
     335              :                                            provider_section_name,
     336              :                                            "KYC_KYCAID_VALIDITY",
     337              :                                            &pd->validity))
     338              :   {
     339            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
     340              :                                provider_section_name,
     341              :                                "KYC_KYCAID_VALIDITY");
     342            0 :     kycaid_unload_configuration (pd);
     343            0 :     return NULL;
     344              :   }
     345           61 :   if (GNUNET_OK !=
     346           61 :       GNUNET_CONFIGURATION_get_value_string (ps->cfg,
     347              :                                              provider_section_name,
     348              :                                              "KYC_KYCAID_AUTH_TOKEN",
     349              :                                              &pd->auth_token))
     350              :   {
     351            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
     352              :                                provider_section_name,
     353              :                                "KYC_KYCAID_AUTH_TOKEN");
     354            0 :     kycaid_unload_configuration (pd);
     355            0 :     return NULL;
     356              :   }
     357           61 :   if (GNUNET_OK !=
     358           61 :       GNUNET_CONFIGURATION_get_value_string (ps->cfg,
     359              :                                              provider_section_name,
     360              :                                              "KYC_KYCAID_FORM_ID",
     361              :                                              &pd->form_id))
     362              :   {
     363            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
     364              :                                provider_section_name,
     365              :                                "KYC_KYCAID_FORM_ID");
     366            0 :     kycaid_unload_configuration (pd);
     367            0 :     return NULL;
     368              :   }
     369           61 :   if (GNUNET_OK !=
     370           61 :       GNUNET_CONFIGURATION_get_value_string (ps->cfg,
     371              :                                              provider_section_name,
     372              :                                              "KYC_KYCAID_CONVERTER_HELPER",
     373              :                                              &pd->conversion_helper))
     374              :   {
     375            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
     376              :                                provider_section_name,
     377              :                                "KYC_KYCAID_CONVERTER_HELPER");
     378            0 :     kycaid_unload_configuration (pd);
     379            0 :     return NULL;
     380              :   }
     381              :   {
     382              :     char *auth;
     383              : 
     384           61 :     GNUNET_asprintf (&auth,
     385              :                      "%s: Token %s",
     386              :                      MHD_HTTP_HEADER_AUTHORIZATION,
     387              :                      pd->auth_token);
     388           61 :     pd->slist = curl_slist_append (NULL,
     389              :                                    auth);
     390           61 :     GNUNET_free (auth);
     391              :   }
     392           61 :   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            0 : kycaid_initiate_cancel (struct TALER_KYCLOGIC_InitiateHandle *ih)
     403              : {
     404            0 :   if (NULL != ih->job)
     405              :   {
     406            0 :     GNUNET_CURL_job_cancel (ih->job);
     407            0 :     ih->job = NULL;
     408              :   }
     409            0 :   GNUNET_free (ih->url);
     410            0 :   TALER_curl_easy_post_finished (&ih->ctx);
     411            0 :   GNUNET_free (ih);
     412            0 : }
     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            0 : handle_initiate_finished (void *cls,
     425              :                           long response_code,
     426              :                           const void *response)
     427              : {
     428            0 :   struct TALER_KYCLOGIC_InitiateHandle *ih = cls;
     429            0 :   const json_t *j = response;
     430              : 
     431            0 :   ih->job = NULL;
     432            0 :   switch (response_code)
     433              :   {
     434            0 :   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            0 :         GNUNET_JSON_spec_string ("verification_id",
     441              :                                  &verification_id),
     442            0 :         GNUNET_JSON_spec_string ("form_url",
     443              :                                  &form_url),
     444            0 :         GNUNET_JSON_spec_string ("form_id",
     445              :                                  &form_id),
     446            0 :         GNUNET_JSON_spec_end ()
     447              :       };
     448              : 
     449            0 :       if (GNUNET_OK !=
     450            0 :           GNUNET_JSON_parse (j,
     451              :                              spec,
     452              :                              NULL, NULL))
     453              :       {
     454            0 :         GNUNET_break_op (0);
     455            0 :         json_dumpf (j,
     456              :                     stderr,
     457              :                     JSON_INDENT (2));
     458            0 :         ih->cb (ih->cb_cls,
     459              :                 TALER_EC_EXCHANGE_KYC_GENERIC_PROVIDER_UNEXPECTED_REPLY,
     460              :                 NULL,
     461              :                 NULL,
     462              :                 NULL,
     463            0 :                 json_string_value (json_object_get (j,
     464              :                                                     "type")));
     465            0 :         break;
     466              :       }
     467            0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     468              :                   "Started new verification `%s' using form %s\n",
     469              :                   verification_id,
     470              :                   form_id);
     471            0 :       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            0 :       GNUNET_JSON_parse_free (spec);
     478              :     }
     479            0 :     break;
     480            0 :   case MHD_HTTP_BAD_REQUEST:
     481              :   case MHD_HTTP_NOT_FOUND:
     482              :   case MHD_HTTP_CONFLICT:
     483            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     484              :                 "KYCAID failed with response %u:\n",
     485              :                 (unsigned int) response_code);
     486            0 :     json_dumpf (j,
     487              :                 stderr,
     488              :                 JSON_INDENT (2));
     489            0 :     ih->cb (ih->cb_cls,
     490              :             TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_BUG,
     491              :             NULL,
     492              :             NULL,
     493              :             NULL,
     494            0 :             json_string_value (json_object_get (j,
     495              :                                                 "type")));
     496            0 :     break;
     497            0 :   case MHD_HTTP_UNAUTHORIZED:
     498              :   case MHD_HTTP_PAYMENT_REQUIRED:
     499            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     500              :                 "Refused access with HTTP status code %u\n",
     501              :                 (unsigned int) response_code);
     502            0 :     ih->cb (ih->cb_cls,
     503              :             TALER_EC_EXCHANGE_KYC_GENERIC_PROVIDER_ACCESS_REFUSED,
     504              :             NULL,
     505              :             NULL,
     506              :             NULL,
     507            0 :             json_string_value (json_object_get (j,
     508              :                                                 "type")));
     509            0 :     break;
     510            0 :   case MHD_HTTP_REQUEST_TIMEOUT:
     511            0 :     ih->cb (ih->cb_cls,
     512              :             TALER_EC_EXCHANGE_KYC_GENERIC_PROVIDER_TIMEOUT,
     513              :             NULL,
     514              :             NULL,
     515              :             NULL,
     516            0 :             json_string_value (json_object_get (j,
     517              :                                                 "type")));
     518            0 :     break;
     519            0 :   case MHD_HTTP_UNPROCESSABLE_ENTITY: /* validation */
     520            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     521              :                 "KYCAID failed with response %u:\n",
     522              :                 (unsigned int) response_code);
     523            0 :     json_dumpf (j,
     524              :                 stderr,
     525              :                 JSON_INDENT (2));
     526            0 :     ih->cb (ih->cb_cls,
     527              :             TALER_EC_EXCHANGE_KYC_GENERIC_PROVIDER_UNEXPECTED_REPLY,
     528              :             NULL,
     529              :             NULL,
     530              :             NULL,
     531            0 :             json_string_value (json_object_get (j,
     532              :                                                 "type")));
     533            0 :     break;
     534            0 :   case MHD_HTTP_TOO_MANY_REQUESTS:
     535            0 :     ih->cb (ih->cb_cls,
     536              :             TALER_EC_EXCHANGE_KYC_GENERIC_PROVIDER_RATE_LIMIT_EXCEEDED,
     537              :             NULL,
     538              :             NULL,
     539              :             NULL,
     540            0 :             json_string_value (json_object_get (j,
     541              :                                                 "type")));
     542            0 :     break;
     543            0 :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     544            0 :     ih->cb (ih->cb_cls,
     545              :             TALER_EC_EXCHANGE_KYC_GENERIC_PROVIDER_UNEXPECTED_REPLY,
     546              :             NULL,
     547              :             NULL,
     548              :             NULL,
     549            0 :             json_string_value (json_object_get (j,
     550              :                                                 "type")));
     551            0 :     break;
     552            0 :   default:
     553            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     554              :                 "Unexpected KYCAID response %u:\n",
     555              :                 (unsigned int) response_code);
     556            0 :     json_dumpf (j,
     557              :                 stderr,
     558              :                 JSON_INDENT (2));
     559            0 :     ih->cb (ih->cb_cls,
     560              :             TALER_EC_EXCHANGE_KYC_GENERIC_PROVIDER_UNEXPECTED_REPLY,
     561              :             NULL,
     562              :             NULL,
     563              :             NULL,
     564            0 :             json_string_value (json_object_get (j,
     565              :                                                 "type")));
     566            0 :     break;
     567              :   }
     568            0 :   kycaid_initiate_cancel (ih);
     569            0 : }
     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            0 : 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            0 :   struct PluginState *ps = cls;
     594              :   struct TALER_KYCLOGIC_InitiateHandle *ih;
     595              :   json_t *body;
     596              :   CURL *eh;
     597              : 
     598              :   (void) context;
     599            0 :   eh = curl_easy_init ();
     600            0 :   if (NULL == eh)
     601              :   {
     602            0 :     GNUNET_break (0);
     603            0 :     return NULL;
     604              :   }
     605            0 :   ih = GNUNET_new (struct TALER_KYCLOGIC_InitiateHandle);
     606            0 :   ih->legitimization_uuid = legitimization_uuid;
     607            0 :   ih->cb = cb;
     608            0 :   ih->cb_cls = cb_cls;
     609            0 :   ih->h_payto = *account_id;
     610            0 :   ih->pd = pd;
     611            0 :   GNUNET_asprintf (&ih->url,
     612              :                    "https://api.kycaid.com/forms/%s/urls",
     613            0 :                    pd->form_id);
     614            0 :   body = GNUNET_JSON_PACK (
     615              :     GNUNET_JSON_pack_data64_auto ("external_applicant_id",
     616              :                                   account_id)
     617              :     );
     618            0 :   GNUNET_break (CURLE_OK ==
     619              :                 curl_easy_setopt (eh,
     620              :                                   CURLOPT_VERBOSE,
     621              :                                   0));
     622            0 :   GNUNET_assert (CURLE_OK ==
     623              :                  curl_easy_setopt (eh,
     624              :                                    CURLOPT_MAXREDIRS,
     625              :                                    1L));
     626            0 :   GNUNET_break (CURLE_OK ==
     627              :                 curl_easy_setopt (eh,
     628              :                                   CURLOPT_URL,
     629              :                                   ih->url));
     630            0 :   if (GNUNET_OK !=
     631            0 :       TALER_curl_easy_post (&ih->ctx,
     632              :                             eh,
     633              :                             body))
     634              :   {
     635            0 :     GNUNET_break (0);
     636            0 :     GNUNET_free (ih->url);
     637            0 :     GNUNET_free (ih);
     638            0 :     curl_easy_cleanup (eh);
     639            0 :     json_decref (body);
     640            0 :     return NULL;
     641              :   }
     642            0 :   json_decref (body);
     643            0 :   ih->job = GNUNET_CURL_job_add2 (ps->curl_ctx,
     644              :                                   eh,
     645            0 :                                   ih->ctx.headers,
     646              :                                   &handle_initiate_finished,
     647              :                                   ih);
     648            0 :   GNUNET_CURL_extend_headers (ih->job,
     649            0 :                               pd->slist);
     650            0 :   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            0 : kycaid_proof_cancel (struct TALER_KYCLOGIC_ProofHandle *ph)
     661              : {
     662            0 :   if (NULL != ph->task)
     663              :   {
     664            0 :     GNUNET_SCHEDULER_cancel (ph->task);
     665            0 :     ph->task = NULL;
     666              :   }
     667            0 :   GNUNET_free (ph);
     668            0 : }
     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            0 : proof_reply (void *cls)
     678              : {
     679            0 :   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            0 :   http_status = MHD_HTTP_BAD_REQUEST;
     686            0 :   body = GNUNET_JSON_PACK (
     687              :     TALER_JSON_pack_ec (TALER_EC_GENERIC_ENDPOINT_UNKNOWN));
     688            0 :   GNUNET_assert (NULL != body);
     689            0 :   ret = TALER_TEMPLATING_build (ph->connection,
     690              :                                 &http_status,
     691              :                                 "kycaid-invalid-request",
     692              :                                 NULL,
     693              :                                 NULL,
     694              :                                 body,
     695              :                                 &resp);
     696            0 :   json_decref (body);
     697            0 :   if (GNUNET_SYSERR == ret)
     698              :   {
     699            0 :     resp = NULL;
     700            0 :     GNUNET_break (0);
     701              :   }
     702              :   else
     703              :   {
     704            0 :     GNUNET_break (MHD_NO !=
     705              :                   MHD_add_response_header (resp,
     706              :                                            MHD_HTTP_HEADER_CONTENT_TYPE,
     707              :                                            "text/html"));
     708              :   }
     709            0 :   ph->cb (ph->cb_cls,
     710              :           TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
     711            0 :           ph->pd->section,
     712              :           NULL, /* user id */
     713              :           NULL, /* provider legi ID */
     714            0 :           GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
     715              :           NULL, /* attributes */
     716              :           http_status,
     717              :           resp);
     718            0 : }
     719              : 
     720              : 
     721              : /**
     722              :  * Check KYC status and return status to human. Not
     723              :  * used by KYC AID!
     724              :  *
     725              :  * @param cls the @e cls of this struct with the plugin-specific state
     726              :  * @param pd provider configuration details
     727              :  * @param connection MHD connection object (for HTTP headers)
     728              :  * @param account_id which account to trigger process for
     729              :  * @param process_row row in the legitimization processes table the legitimization is for
     730              :  * @param provider_user_id user ID (or NULL) the proof is for
     731              :  * @param provider_legitimization_id legitimization ID the proof is for
     732              :  * @param cb function to call with the result
     733              :  * @param cb_cls closure for @a cb
     734              :  * @return handle to cancel operation early
     735              :  */
     736              : static struct TALER_KYCLOGIC_ProofHandle *
     737            0 : kycaid_proof (void *cls,
     738              :               const struct TALER_KYCLOGIC_ProviderDetails *pd,
     739              :               struct MHD_Connection *connection,
     740              :               const struct TALER_NormalizedPaytoHashP *account_id,
     741              :               uint64_t process_row,
     742              :               const char *provider_user_id,
     743              :               const char *provider_legitimization_id,
     744              :               TALER_KYCLOGIC_ProofCallback cb,
     745              :               void *cb_cls)
     746              : {
     747            0 :   struct PluginState *ps = cls;
     748              :   struct TALER_KYCLOGIC_ProofHandle *ph;
     749              : 
     750            0 :   ph = GNUNET_new (struct TALER_KYCLOGIC_ProofHandle);
     751            0 :   ph->ps = ps;
     752            0 :   ph->pd = pd;
     753            0 :   ph->cb = cb;
     754            0 :   ph->cb_cls = cb_cls;
     755            0 :   ph->connection = connection;
     756            0 :   ph->task = GNUNET_SCHEDULER_add_now (&proof_reply,
     757              :                                        ph);
     758            0 :   return ph;
     759              : }
     760              : 
     761              : 
     762              : /**
     763              :  * Cancel KYC webhook execution.
     764              :  *
     765              :  * @param[in] wh handle of operation to cancel
     766              :  */
     767              : static void
     768            0 : kycaid_webhook_cancel (struct TALER_KYCLOGIC_WebhookHandle *wh)
     769              : {
     770            0 :   if (NULL != wh->task)
     771              :   {
     772            0 :     GNUNET_SCHEDULER_cancel (wh->task);
     773            0 :     wh->task = NULL;
     774              :   }
     775            0 :   if (NULL != wh->econ)
     776              :   {
     777            0 :     TALER_JSON_external_conversion_stop (wh->econ);
     778            0 :     wh->econ = NULL;
     779              :   }
     780            0 :   if (NULL != wh->job)
     781              :   {
     782            0 :     GNUNET_CURL_job_cancel (wh->job);
     783            0 :     wh->job = NULL;
     784              :   }
     785            0 :   if (NULL != wh->json_response)
     786              :   {
     787            0 :     json_decref (wh->json_response);
     788            0 :     wh->json_response = NULL;
     789              :   }
     790            0 :   GNUNET_free (wh->verification_id);
     791            0 :   GNUNET_free (wh->applicant_id);
     792            0 :   GNUNET_free (wh->url);
     793            0 :   GNUNET_free (wh);
     794            0 : }
     795              : 
     796              : 
     797              : /**
     798              :  * Extract KYC failure reasons and log those
     799              :  *
     800              :  * @param verifications JSON object with failure details
     801              :  */
     802              : static void
     803            0 : log_failure (const json_t *verifications)
     804              : {
     805              :   const json_t *member;
     806              :   const char *name;
     807              : 
     808            0 :   json_object_foreach ((json_t *) verifications, name, member)
     809              :   {
     810              :     bool iverified;
     811              :     const char *comment;
     812              :     struct GNUNET_JSON_Specification spec[] = {
     813            0 :       GNUNET_JSON_spec_bool ("verified",
     814              :                              &iverified),
     815            0 :       GNUNET_JSON_spec_string ("comment",
     816              :                                &comment),
     817            0 :       GNUNET_JSON_spec_end ()
     818              :     };
     819              : 
     820            0 :     if (GNUNET_OK !=
     821            0 :         GNUNET_JSON_parse (member,
     822              :                            spec,
     823              :                            NULL, NULL))
     824              :     {
     825            0 :       GNUNET_break_op (0);
     826            0 :       json_dumpf (member,
     827              :                   stderr,
     828              :                   JSON_INDENT (2));
     829            0 :       continue;
     830              :     }
     831            0 :     if (iverified)
     832            0 :       continue;
     833            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     834              :                 "KYC verification of attribute `%s' failed: %s\n",
     835              :                 name,
     836              :                 comment);
     837              :   }
     838            0 : }
     839              : 
     840              : 
     841              : /**
     842              :  * Type of a callback that receives a JSON @a result.
     843              :  *
     844              :  * @param cls closure our `struct TALER_KYCLOGIC_WebhookHandle *`
     845              :  * @param status_type how did the process die
     846              :  * @param code termination status code from the process
     847              :  * @param result converted attribute data, NULL on failure
     848              :  */
     849              : static void
     850            0 : webhook_conversion_cb (void *cls,
     851              :                        enum GNUNET_OS_ProcessStatusType status_type,
     852              :                        unsigned long code,
     853              :                        const json_t *result)
     854              : {
     855            0 :   struct TALER_KYCLOGIC_WebhookHandle *wh = cls;
     856              :   struct GNUNET_TIME_Absolute expiration;
     857              :   struct MHD_Response *resp;
     858              : 
     859            0 :   wh->econ = NULL;
     860            0 :   if ( (0 == code) &&
     861              :        (NULL == result) )
     862              :   {
     863              :     /* No result, but *our helper* was OK => bad input */
     864            0 :     GNUNET_break_op (0);
     865            0 :     json_dumpf (wh->json_response,
     866              :                 stderr,
     867              :                 JSON_INDENT (2));
     868            0 :     resp = TALER_MHD_MAKE_JSON_PACK (
     869              :       GNUNET_JSON_pack_uint64 ("kycaid_http_status",
     870              :                                wh->kycaid_response_code),
     871              :       GNUNET_JSON_pack_object_incref ("kycaid_body",
     872              :                                       (json_t *) wh->json_response));
     873            0 :     wh->cb (wh->cb_cls,
     874              :             wh->process_row,
     875            0 :             &wh->h_payto,
     876            0 :             wh->is_wallet,
     877            0 :             wh->pd->section,
     878            0 :             wh->applicant_id,
     879            0 :             wh->verification_id,
     880              :             TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
     881            0 :             GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
     882              :             NULL,
     883              :             MHD_HTTP_BAD_GATEWAY,
     884              :             resp);
     885            0 :     kycaid_webhook_cancel (wh);
     886            0 :     return;
     887              :   }
     888            0 :   if (NULL == result)
     889              :   {
     890              :     /* Failure in our helper */
     891            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     892              :                 "Helper exited with status code %d\n",
     893              :                 (int) code);
     894            0 :     json_dumpf (wh->json_response,
     895              :                 stderr,
     896              :                 JSON_INDENT (2));
     897            0 :     resp = TALER_MHD_MAKE_JSON_PACK (
     898              :       GNUNET_JSON_pack_uint64 ("kycaid_http_status",
     899              :                                wh->kycaid_response_code),
     900              :       GNUNET_JSON_pack_object_incref ("kycaid_body",
     901              :                                       (json_t *) wh->json_response));
     902            0 :     wh->cb (wh->cb_cls,
     903              :             wh->process_row,
     904            0 :             &wh->h_payto,
     905            0 :             wh->is_wallet,
     906            0 :             wh->pd->section,
     907            0 :             wh->applicant_id,
     908            0 :             wh->verification_id,
     909              :             TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
     910            0 :             GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
     911              :             NULL,
     912              :             MHD_HTTP_BAD_GATEWAY,
     913              :             resp);
     914            0 :     kycaid_webhook_cancel (wh);
     915            0 :     return;
     916              :   }
     917            0 :   if (! json_is_string (json_object_get (result,
     918              :                                          "FORM_ID")))
     919              :   {
     920              :     /* Failure in our helper */
     921            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     922              :                 "Mandatory FORM_ID not set in result\n");
     923            0 :     json_dumpf (result,
     924              :                 stderr,
     925              :                 JSON_INDENT (2));
     926            0 :     resp = TALER_MHD_MAKE_JSON_PACK (
     927              :       GNUNET_JSON_pack_uint64 ("kycaid_http_status",
     928              :                                wh->kycaid_response_code),
     929              :       GNUNET_JSON_pack_object_incref ("kycaid_body",
     930              :                                       (json_t *) wh->json_response));
     931            0 :     wh->cb (wh->cb_cls,
     932              :             wh->process_row,
     933            0 :             &wh->h_payto,
     934            0 :             wh->is_wallet,
     935            0 :             wh->pd->section,
     936            0 :             wh->applicant_id,
     937            0 :             wh->verification_id,
     938              :             TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
     939            0 :             GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
     940              :             NULL,
     941              :             MHD_HTTP_BAD_GATEWAY,
     942              :             resp);
     943            0 :     kycaid_webhook_cancel (wh);
     944            0 :     return;
     945              :   }
     946              : 
     947            0 :   expiration = GNUNET_TIME_relative_to_absolute (wh->pd->validity);
     948            0 :   resp = MHD_create_response_from_buffer_static (0,
     949              :                                                  "");
     950            0 :   wh->cb (wh->cb_cls,
     951              :           wh->process_row,
     952            0 :           &wh->h_payto,
     953            0 :           wh->is_wallet,
     954            0 :           wh->pd->section,
     955            0 :           wh->applicant_id,
     956            0 :           wh->verification_id,
     957              :           TALER_KYCLOGIC_STATUS_SUCCESS,
     958              :           expiration,
     959              :           result,
     960              :           MHD_HTTP_NO_CONTENT,
     961              :           resp);
     962            0 :   kycaid_webhook_cancel (wh);
     963              : }
     964              : 
     965              : 
     966              : /**
     967              :  * Function called when we're done processing the
     968              :  * HTTP "/applicants/{verification_id}" request.
     969              :  *
     970              :  * @param cls the `struct TALER_KYCLOGIC_WebhookHandle`
     971              :  * @param response_code HTTP response code, 0 on error
     972              :  * @param response parsed JSON result, NULL on error
     973              :  */
     974              : static void
     975            0 : handle_webhook_finished (void *cls,
     976              :                          long response_code,
     977              :                          const void *response)
     978              : {
     979            0 :   struct TALER_KYCLOGIC_WebhookHandle *wh = cls;
     980            0 :   const json_t *j = response;
     981              :   struct MHD_Response *resp;
     982              : 
     983            0 :   wh->job = NULL;
     984            0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     985              :               "Webhook returned with HTTP status %u\n",
     986              :               (unsigned int) response_code);
     987            0 :   wh->kycaid_response_code = response_code;
     988            0 :   wh->json_response = json_incref ((json_t *) j);
     989            0 :   switch (response_code)
     990              :   {
     991            0 :   case MHD_HTTP_OK:
     992              :     {
     993              :       const char *profile_status;
     994              : 
     995            0 :       profile_status = json_string_value (
     996            0 :         json_object_get (
     997              :           j,
     998              :           "profile_status"));
     999            0 :       if (0 != strcasecmp ("valid",
    1000              :                            profile_status))
    1001              :       {
    1002              :         enum TALER_KYCLOGIC_KycStatus ks;
    1003              : 
    1004            0 :         ks = (0 == strcasecmp ("pending",
    1005              :                                profile_status))
    1006              :           ? TALER_KYCLOGIC_STATUS_PENDING
    1007            0 :           : TALER_KYCLOGIC_STATUS_USER_ABORTED;
    1008            0 :         resp = MHD_create_response_from_buffer_static (0,
    1009              :                                                        "");
    1010            0 :         wh->cb (wh->cb_cls,
    1011              :                 wh->process_row,
    1012            0 :                 &wh->h_payto,
    1013            0 :                 wh->is_wallet,
    1014            0 :                 wh->pd->section,
    1015            0 :                 wh->applicant_id,
    1016            0 :                 wh->verification_id,
    1017              :                 ks,
    1018            0 :                 GNUNET_TIME_UNIT_ZERO_ABS,
    1019              :                 NULL,
    1020              :                 MHD_HTTP_NO_CONTENT,
    1021              :                 resp);
    1022            0 :         break;
    1023              :       }
    1024              :       {
    1025            0 :         const char *argv[] = {
    1026            0 :           wh->pd->conversion_helper,
    1027              :           "-a",
    1028            0 :           wh->pd->auth_token,
    1029              :           NULL,
    1030              :         };
    1031              : 
    1032              :         wh->econ
    1033            0 :           = TALER_JSON_external_conversion_start (
    1034              :               j,
    1035              :               &webhook_conversion_cb,
    1036              :               wh,
    1037            0 :               wh->pd->conversion_helper,
    1038              :               argv);
    1039              :       }
    1040            0 :       if (NULL == wh->econ)
    1041              :       {
    1042            0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1043              :                     "Failed to start KYCAID conversion helper `%s'\n",
    1044              :                     wh->pd->conversion_helper);
    1045            0 :         resp = TALER_MHD_make_error (
    1046              :           TALER_EC_EXCHANGE_GENERIC_KYC_CONVERTER_FAILED,
    1047              :           NULL);
    1048            0 :         wh->cb (wh->cb_cls,
    1049              :                 wh->process_row,
    1050            0 :                 &wh->h_payto,
    1051            0 :                 wh->is_wallet,
    1052            0 :                 wh->pd->section,
    1053            0 :                 wh->applicant_id,
    1054            0 :                 wh->verification_id,
    1055              :                 TALER_KYCLOGIC_STATUS_INTERNAL_ERROR,
    1056            0 :                 GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
    1057              :                 NULL,
    1058              :                 MHD_HTTP_INTERNAL_SERVER_ERROR,
    1059              :                 resp);
    1060            0 :         break;
    1061              :       }
    1062            0 :       return;
    1063              :     }
    1064              :     break;
    1065            0 :   case MHD_HTTP_BAD_REQUEST:
    1066              :   case MHD_HTTP_NOT_FOUND:
    1067              :   case MHD_HTTP_CONFLICT:
    1068            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1069              :                 "KYCAID failed with response %u:\n",
    1070              :                 (unsigned int) response_code);
    1071            0 :     json_dumpf (j,
    1072              :                 stderr,
    1073              :                 JSON_INDENT (2));
    1074            0 :     resp = TALER_MHD_MAKE_JSON_PACK (
    1075              :       GNUNET_JSON_pack_uint64 ("kycaid_http_status",
    1076              :                                response_code));
    1077            0 :     wh->cb (wh->cb_cls,
    1078              :             wh->process_row,
    1079            0 :             &wh->h_payto,
    1080            0 :             wh->is_wallet,
    1081            0 :             wh->pd->section,
    1082            0 :             wh->applicant_id,
    1083            0 :             wh->verification_id,
    1084              :             TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
    1085            0 :             GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
    1086              :             NULL,
    1087              :             MHD_HTTP_INTERNAL_SERVER_ERROR,
    1088              :             resp);
    1089            0 :     break;
    1090            0 :   case MHD_HTTP_UNAUTHORIZED:
    1091              :   case MHD_HTTP_PAYMENT_REQUIRED:
    1092            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1093              :                 "Refused access with HTTP status code %u\n",
    1094              :                 (unsigned int) response_code);
    1095            0 :     resp = TALER_MHD_MAKE_JSON_PACK (
    1096              :       GNUNET_JSON_pack_uint64 ("kycaid_http_status",
    1097              :                                response_code),
    1098              :       GNUNET_JSON_pack_object_incref ("kycaid_body",
    1099              :                                       (json_t *) j));
    1100            0 :     wh->cb (wh->cb_cls,
    1101              :             wh->process_row,
    1102            0 :             &wh->h_payto,
    1103            0 :             wh->is_wallet,
    1104            0 :             wh->pd->section,
    1105            0 :             wh->applicant_id,
    1106            0 :             wh->verification_id,
    1107              :             TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
    1108            0 :             GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
    1109              :             NULL,
    1110              :             MHD_HTTP_NETWORK_AUTHENTICATION_REQUIRED,
    1111              :             resp);
    1112            0 :     break;
    1113            0 :   case MHD_HTTP_REQUEST_TIMEOUT:
    1114            0 :     resp = TALER_MHD_MAKE_JSON_PACK (
    1115              :       GNUNET_JSON_pack_uint64 ("kycaid_http_status",
    1116              :                                response_code),
    1117              :       GNUNET_JSON_pack_object_incref ("kycaid_body",
    1118              :                                       (json_t *) j));
    1119            0 :     wh->cb (wh->cb_cls,
    1120              :             wh->process_row,
    1121            0 :             &wh->h_payto,
    1122            0 :             wh->is_wallet,
    1123            0 :             wh->pd->section,
    1124            0 :             wh->applicant_id,
    1125            0 :             wh->verification_id,
    1126              :             TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
    1127            0 :             GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
    1128              :             NULL,
    1129              :             MHD_HTTP_GATEWAY_TIMEOUT,
    1130              :             resp);
    1131            0 :     break;
    1132            0 :   case MHD_HTTP_UNPROCESSABLE_ENTITY: /* validation */
    1133            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1134              :                 "KYCAID failed with response %u:\n",
    1135              :                 (unsigned int) response_code);
    1136            0 :     json_dumpf (j,
    1137              :                 stderr,
    1138              :                 JSON_INDENT (2));
    1139            0 :     resp = TALER_MHD_MAKE_JSON_PACK (
    1140              :       GNUNET_JSON_pack_uint64 ("kycaid_http_status",
    1141              :                                response_code),
    1142              :       GNUNET_JSON_pack_object_incref ("kycaid_body",
    1143              :                                       (json_t *) j));
    1144            0 :     wh->cb (wh->cb_cls,
    1145              :             wh->process_row,
    1146            0 :             &wh->h_payto,
    1147            0 :             wh->is_wallet,
    1148            0 :             wh->pd->section,
    1149            0 :             wh->applicant_id,
    1150            0 :             wh->verification_id,
    1151              :             TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
    1152            0 :             GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
    1153              :             NULL,
    1154              :             MHD_HTTP_BAD_GATEWAY,
    1155              :             resp);
    1156            0 :     break;
    1157            0 :   case MHD_HTTP_TOO_MANY_REQUESTS:
    1158            0 :     resp = TALER_MHD_MAKE_JSON_PACK (
    1159              :       GNUNET_JSON_pack_uint64 ("kycaid_http_status",
    1160              :                                response_code),
    1161              :       GNUNET_JSON_pack_object_incref ("kycaid_body",
    1162              :                                       (json_t *) j));
    1163            0 :     wh->cb (wh->cb_cls,
    1164              :             wh->process_row,
    1165            0 :             &wh->h_payto,
    1166            0 :             wh->is_wallet,
    1167            0 :             wh->pd->section,
    1168            0 :             wh->applicant_id,
    1169            0 :             wh->verification_id,
    1170              :             TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
    1171            0 :             GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
    1172              :             NULL,
    1173              :             MHD_HTTP_SERVICE_UNAVAILABLE,
    1174              :             resp);
    1175            0 :     break;
    1176            0 :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    1177            0 :     resp = TALER_MHD_MAKE_JSON_PACK (
    1178              :       GNUNET_JSON_pack_uint64 ("kycaid_http_status",
    1179              :                                response_code),
    1180              :       GNUNET_JSON_pack_object_incref ("kycaid_body",
    1181              :                                       (json_t *) j));
    1182            0 :     wh->cb (wh->cb_cls,
    1183              :             wh->process_row,
    1184            0 :             &wh->h_payto,
    1185            0 :             wh->is_wallet,
    1186            0 :             wh->pd->section,
    1187            0 :             wh->applicant_id,
    1188            0 :             wh->verification_id,
    1189              :             TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
    1190            0 :             GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
    1191              :             NULL,
    1192              :             MHD_HTTP_BAD_GATEWAY,
    1193              :             resp);
    1194            0 :     break;
    1195            0 :   default:
    1196            0 :     resp = TALER_MHD_MAKE_JSON_PACK (
    1197              :       GNUNET_JSON_pack_uint64 ("kycaid_http_status",
    1198              :                                response_code),
    1199              :       GNUNET_JSON_pack_object_incref ("kycaid_body",
    1200              :                                       (json_t *) j));
    1201            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1202              :                 "Unexpected KYCAID response %u:\n",
    1203              :                 (unsigned int) response_code);
    1204            0 :     json_dumpf (j,
    1205              :                 stderr,
    1206              :                 JSON_INDENT (2));
    1207            0 :     wh->cb (wh->cb_cls,
    1208              :             wh->process_row,
    1209            0 :             &wh->h_payto,
    1210            0 :             wh->is_wallet,
    1211            0 :             wh->pd->section,
    1212            0 :             wh->applicant_id,
    1213            0 :             wh->verification_id,
    1214              :             TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
    1215            0 :             GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
    1216              :             NULL,
    1217              :             MHD_HTTP_BAD_GATEWAY,
    1218              :             resp);
    1219            0 :     break;
    1220              :   }
    1221            0 :   kycaid_webhook_cancel (wh);
    1222              : }
    1223              : 
    1224              : 
    1225              : /**
    1226              :  * Asynchronously return a reply for the webhook.
    1227              :  *
    1228              :  * @param cls a `struct TALER_KYCLOGIC_WebhookHandle *`
    1229              :  */
    1230              : static void
    1231            0 : async_webhook_reply (void *cls)
    1232              : {
    1233            0 :   struct TALER_KYCLOGIC_WebhookHandle *wh = cls;
    1234              : 
    1235            0 :   wh->task = NULL;
    1236            0 :   wh->cb (wh->cb_cls,
    1237              :           wh->process_row,
    1238            0 :           (0 == wh->process_row)
    1239              :           ? NULL
    1240              :           : &wh->h_payto,
    1241            0 :           wh->is_wallet,
    1242            0 :           wh->pd->section,
    1243            0 :           wh->applicant_id, /* provider user ID */
    1244            0 :           wh->verification_id, /* provider legi ID */
    1245              :           TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
    1246            0 :           GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
    1247              :           NULL,
    1248              :           wh->response_code,
    1249              :           wh->resp);
    1250            0 :   kycaid_webhook_cancel (wh);
    1251            0 : }
    1252              : 
    1253              : 
    1254              : /**
    1255              :  * Check KYC status and return result for Webhook.  We do NOT implement the
    1256              :  * authentication check proposed by the KYCAID documentation, as it would
    1257              :  * allow an attacker who learns the access token to easily bypass the KYC
    1258              :  * checks. Instead, we insist on explicitly requesting the KYC status from the
    1259              :  * provider (at least on success).
    1260              :  *
    1261              :  * @param cls the @e cls of this struct with the plugin-specific state
    1262              :  * @param pd provider configuration details
    1263              :  * @param plc callback to lookup accounts with
    1264              :  * @param plc_cls closure for @a plc
    1265              :  * @param http_method HTTP method used for the webhook
    1266              :  * @param url_path rest of the URL after `/kyc-webhook/`
    1267              :  * @param connection MHD connection object (for HTTP headers)
    1268              :  * @param body HTTP request body
    1269              :  * @param cb function to call with the result
    1270              :  * @param cb_cls closure for @a cb
    1271              :  * @return handle to cancel operation early
    1272              :  */
    1273              : static struct TALER_KYCLOGIC_WebhookHandle *
    1274            0 : kycaid_webhook (void *cls,
    1275              :                 const struct TALER_KYCLOGIC_ProviderDetails *pd,
    1276              :                 TALER_KYCLOGIC_ProviderLookupCallback plc,
    1277              :                 void *plc_cls,
    1278              :                 const char *http_method,
    1279              :                 const char *const url_path[],
    1280              :                 struct MHD_Connection *connection,
    1281              :                 const json_t *body,
    1282              :                 TALER_KYCLOGIC_WebhookCallback cb,
    1283              :                 void *cb_cls)
    1284              : {
    1285            0 :   struct PluginState *ps = cls;
    1286              :   struct TALER_KYCLOGIC_WebhookHandle *wh;
    1287              :   CURL *eh;
    1288              :   const char *request_id;
    1289              :   const char *type;
    1290              :   const char *verification_id; /* = provider_legitimization_id */
    1291              :   const char *applicant_id;
    1292              :   const char *form_id;
    1293            0 :   const char *status = NULL;
    1294            0 :   bool verified = false;
    1295            0 :   bool no_verified = true;
    1296            0 :   const json_t *verifications = NULL;
    1297              :   struct GNUNET_JSON_Specification spec[] = {
    1298            0 :     GNUNET_JSON_spec_string ("request_id",
    1299              :                              &request_id),
    1300            0 :     GNUNET_JSON_spec_string ("type",
    1301              :                              &type),
    1302            0 :     GNUNET_JSON_spec_string ("verification_id",
    1303              :                              &verification_id),
    1304            0 :     GNUNET_JSON_spec_string ("applicant_id",
    1305              :                              &applicant_id),
    1306            0 :     GNUNET_JSON_spec_string ("form_id",
    1307              :                              &form_id),
    1308            0 :     GNUNET_JSON_spec_mark_optional (
    1309              :       GNUNET_JSON_spec_string ("status",
    1310              :                                &status),
    1311              :       NULL),
    1312            0 :     GNUNET_JSON_spec_mark_optional (
    1313              :       GNUNET_JSON_spec_bool ("verified",
    1314              :                              &verified),
    1315              :       &no_verified),
    1316            0 :     GNUNET_JSON_spec_mark_optional (
    1317              :       GNUNET_JSON_spec_object_const ("verifications",
    1318              :                                      &verifications),
    1319              :       NULL),
    1320            0 :     GNUNET_JSON_spec_end ()
    1321              :   };
    1322              :   enum GNUNET_DB_QueryStatus qs;
    1323              : 
    1324            0 :   wh = GNUNET_new (struct TALER_KYCLOGIC_WebhookHandle);
    1325            0 :   wh->cb = cb;
    1326            0 :   wh->cb_cls = cb_cls;
    1327            0 :   wh->ps = ps;
    1328            0 :   wh->pd = pd;
    1329            0 :   wh->connection = connection;
    1330            0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1331              :               "KYCAID webhook of `%s' triggered with %s\n",
    1332              :               pd->section,
    1333              :               http_method);
    1334              : #if 1
    1335            0 :   if (NULL != body)
    1336            0 :     json_dumpf (body,
    1337              :                 stderr,
    1338              :                 JSON_INDENT (2));
    1339              : #endif
    1340            0 :   if (NULL == pd)
    1341              :   {
    1342            0 :     GNUNET_break_op (0);
    1343            0 :     json_dumpf (body,
    1344              :                 stderr,
    1345              :                 JSON_INDENT (2));
    1346            0 :     wh->resp = TALER_MHD_make_error (
    1347              :       TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_UNKNOWN,
    1348              :       "kycaid");
    1349            0 :     wh->response_code = MHD_HTTP_NOT_FOUND;
    1350            0 :     wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply,
    1351              :                                          wh);
    1352            0 :     return wh;
    1353              :   }
    1354              : 
    1355            0 :   if (GNUNET_OK !=
    1356            0 :       GNUNET_JSON_parse (body,
    1357              :                          spec,
    1358              :                          NULL, NULL))
    1359              :   {
    1360            0 :     GNUNET_break_op (0);
    1361            0 :     json_dumpf (body,
    1362              :                 stderr,
    1363              :                 JSON_INDENT (2));
    1364            0 :     wh->resp = TALER_MHD_MAKE_JSON_PACK (
    1365              :       GNUNET_JSON_pack_object_incref ("webhook_body",
    1366              :                                       (json_t *) body));
    1367            0 :     wh->response_code = MHD_HTTP_BAD_REQUEST;
    1368            0 :     wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply,
    1369              :                                          wh);
    1370            0 :     return wh;
    1371              :   }
    1372            0 :   qs = plc (plc_cls,
    1373            0 :             pd->section,
    1374              :             verification_id,
    1375              :             &wh->h_payto,
    1376              :             &wh->is_wallet,
    1377              :             &wh->process_row);
    1378            0 :   if (qs < 0)
    1379              :   {
    1380            0 :     wh->resp = TALER_MHD_make_error (TALER_EC_GENERIC_DB_FETCH_FAILED,
    1381              :                                      "provider-legitimization-lookup");
    1382            0 :     wh->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
    1383            0 :     wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply,
    1384              :                                          wh);
    1385            0 :     return wh;
    1386              :   }
    1387            0 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    1388              :   {
    1389            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1390              :                 "Received webhook for unknown verification ID `%s' and section `%s'\n",
    1391              :                 verification_id,
    1392              :                 pd->section);
    1393            0 :     wh->resp = TALER_MHD_make_error (
    1394              :       TALER_EC_EXCHANGE_KYC_PROOF_REQUEST_UNKNOWN,
    1395              :       verification_id);
    1396            0 :     wh->response_code = MHD_HTTP_NOT_FOUND;
    1397            0 :     wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply,
    1398              :                                          wh);
    1399            0 :     return wh;
    1400              :   }
    1401            0 :   wh->verification_id = GNUNET_strdup (verification_id);
    1402            0 :   wh->applicant_id = GNUNET_strdup (applicant_id);
    1403            0 :   if ( (0 != strcasecmp (type,
    1404            0 :                          "VERIFICATION_COMPLETED")) ||
    1405            0 :        (no_verified) ||
    1406            0 :        (! verified) )
    1407              :   {
    1408              :     /* We don't need to re-confirm the failure by
    1409              :        asking the API again. */
    1410            0 :     log_failure (verifications);
    1411            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1412              :                 "Webhook called with non-completion status: %s\n",
    1413              :                 type);
    1414            0 :     wh->response_code = MHD_HTTP_NO_CONTENT;
    1415            0 :     wh->resp = MHD_create_response_from_buffer_static (0,
    1416              :                                                        "");
    1417            0 :     wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply,
    1418              :                                          wh);
    1419            0 :     return wh;
    1420              :   }
    1421              : 
    1422            0 :   eh = curl_easy_init ();
    1423            0 :   if (NULL == eh)
    1424              :   {
    1425            0 :     GNUNET_break (0);
    1426            0 :     wh->resp = TALER_MHD_make_error (
    1427              :       TALER_EC_GENERIC_ALLOCATION_FAILURE,
    1428              :       NULL);
    1429            0 :     wh->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
    1430            0 :     wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply,
    1431              :                                          wh);
    1432            0 :     return wh;
    1433              :   }
    1434              : 
    1435            0 :   GNUNET_asprintf (&wh->url,
    1436              :                    "https://api.kycaid.com/applicants/%s",
    1437              :                    applicant_id);
    1438            0 :   GNUNET_break (CURLE_OK ==
    1439              :                 curl_easy_setopt (eh,
    1440              :                                   CURLOPT_VERBOSE,
    1441              :                                   0));
    1442            0 :   GNUNET_assert (CURLE_OK ==
    1443              :                  curl_easy_setopt (eh,
    1444              :                                    CURLOPT_MAXREDIRS,
    1445              :                                    1L));
    1446            0 :   GNUNET_break (CURLE_OK ==
    1447              :                 curl_easy_setopt (eh,
    1448              :                                   CURLOPT_URL,
    1449              :                                   wh->url));
    1450            0 :   wh->job = GNUNET_CURL_job_add2 (ps->curl_ctx,
    1451              :                                   eh,
    1452            0 :                                   pd->slist,
    1453              :                                   &handle_webhook_finished,
    1454              :                                   wh);
    1455            0 :   return wh;
    1456              : }
    1457              : 
    1458              : 
    1459              : /**
    1460              :  * Initialize kycaid logic plugin
    1461              :  *
    1462              :  * @param cls a configuration instance
    1463              :  * @return NULL on error, otherwise a `struct TALER_KYCLOGIC_Plugin`
    1464              :  */
    1465              : void *
    1466              : libtaler_plugin_kyclogic_kycaid_init (void *cls);
    1467              : 
    1468              : /* declaration to avoid compiler warning */
    1469              : void *
    1470           61 : libtaler_plugin_kyclogic_kycaid_init (void *cls)
    1471              : {
    1472           61 :   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
    1473              :   struct TALER_KYCLOGIC_Plugin *plugin;
    1474              :   struct PluginState *ps;
    1475              : 
    1476           61 :   ps = GNUNET_new (struct PluginState);
    1477           61 :   ps->cfg = cfg;
    1478           61 :   if (GNUNET_OK !=
    1479           61 :       GNUNET_CONFIGURATION_get_value_string (cfg,
    1480              :                                              "exchange",
    1481              :                                              "BASE_URL",
    1482              :                                              &ps->exchange_base_url))
    1483              :   {
    1484            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    1485              :                                "exchange",
    1486              :                                "BASE_URL");
    1487            0 :     GNUNET_free (ps);
    1488            0 :     return NULL;
    1489              :   }
    1490              : 
    1491              :   ps->curl_ctx
    1492          122 :     = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
    1493           61 :                         &ps->curl_rc);
    1494           61 :   if (NULL == ps->curl_ctx)
    1495              :   {
    1496            0 :     GNUNET_break (0);
    1497            0 :     GNUNET_free (ps->exchange_base_url);
    1498            0 :     GNUNET_free (ps);
    1499            0 :     return NULL;
    1500              :   }
    1501           61 :   ps->curl_rc = GNUNET_CURL_gnunet_rc_create (ps->curl_ctx);
    1502              : 
    1503           61 :   plugin = GNUNET_new (struct TALER_KYCLOGIC_Plugin);
    1504           61 :   plugin->cls = ps;
    1505              :   plugin->load_configuration
    1506           61 :     = &kycaid_load_configuration;
    1507              :   plugin->unload_configuration
    1508           61 :     = &kycaid_unload_configuration;
    1509              :   plugin->initiate
    1510           61 :     = &kycaid_initiate;
    1511              :   plugin->initiate_cancel
    1512           61 :     = &kycaid_initiate_cancel;
    1513              :   plugin->proof
    1514           61 :     = &kycaid_proof;
    1515              :   plugin->proof_cancel
    1516           61 :     = &kycaid_proof_cancel;
    1517              :   plugin->webhook
    1518           61 :     = &kycaid_webhook;
    1519              :   plugin->webhook_cancel
    1520           61 :     = &kycaid_webhook_cancel;
    1521           61 :   return plugin;
    1522              : }
    1523              : 
    1524              : 
    1525              : /**
    1526              :  * Unload authorization plugin
    1527              :  *
    1528              :  * @param cls a `struct TALER_KYCLOGIC_Plugin`
    1529              :  * @return NULL (always)
    1530              :  */
    1531              : void *
    1532              : libtaler_plugin_kyclogic_kycaid_done (void *cls);
    1533              : 
    1534              : /* declaration to avoid compiler warning */
    1535              : void *
    1536           61 : libtaler_plugin_kyclogic_kycaid_done (void *cls)
    1537              : {
    1538           61 :   struct TALER_KYCLOGIC_Plugin *plugin = cls;
    1539           61 :   struct PluginState *ps = plugin->cls;
    1540              : 
    1541           61 :   if (NULL != ps->curl_ctx)
    1542              :   {
    1543           61 :     GNUNET_CURL_fini (ps->curl_ctx);
    1544           61 :     ps->curl_ctx = NULL;
    1545              :   }
    1546           61 :   if (NULL != ps->curl_rc)
    1547              :   {
    1548           61 :     GNUNET_CURL_gnunet_rc_destroy (ps->curl_rc);
    1549           61 :     ps->curl_rc = NULL;
    1550              :   }
    1551           61 :   GNUNET_free (ps->exchange_base_url);
    1552           61 :   GNUNET_free (ps);
    1553           61 :   GNUNET_free (plugin);
    1554           61 :   return NULL;
    1555              : }
        

Generated by: LCOV version 2.0-1