LCOV - code coverage report
Current view: top level - kyclogic - plugin_kyclogic_kycaid.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 11.9 % 495 59
Test Date: 2026-01-09 13:26:54 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 :   GNUNET_break (GNUNET_SYSERR != ret);
     698            0 :   ph->cb (ph->cb_cls,
     699              :           TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
     700            0 :           ph->pd->section,
     701              :           NULL, /* user id */
     702              :           NULL, /* provider legi ID */
     703            0 :           GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
     704              :           NULL, /* attributes */
     705              :           http_status,
     706              :           resp);
     707            0 : }
     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            0 : 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            0 :   struct PluginState *ps = cls;
     737              :   struct TALER_KYCLOGIC_ProofHandle *ph;
     738              : 
     739            0 :   ph = GNUNET_new (struct TALER_KYCLOGIC_ProofHandle);
     740            0 :   ph->ps = ps;
     741            0 :   ph->pd = pd;
     742            0 :   ph->cb = cb;
     743            0 :   ph->cb_cls = cb_cls;
     744            0 :   ph->connection = connection;
     745            0 :   ph->task = GNUNET_SCHEDULER_add_now (&proof_reply,
     746              :                                        ph);
     747            0 :   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            0 : kycaid_webhook_cancel (struct TALER_KYCLOGIC_WebhookHandle *wh)
     758              : {
     759            0 :   if (NULL != wh->task)
     760              :   {
     761            0 :     GNUNET_SCHEDULER_cancel (wh->task);
     762            0 :     wh->task = NULL;
     763              :   }
     764            0 :   if (NULL != wh->econ)
     765              :   {
     766            0 :     TALER_JSON_external_conversion_stop (wh->econ);
     767            0 :     wh->econ = NULL;
     768              :   }
     769            0 :   if (NULL != wh->job)
     770              :   {
     771            0 :     GNUNET_CURL_job_cancel (wh->job);
     772            0 :     wh->job = NULL;
     773              :   }
     774            0 :   if (NULL != wh->json_response)
     775              :   {
     776            0 :     json_decref (wh->json_response);
     777            0 :     wh->json_response = NULL;
     778              :   }
     779            0 :   GNUNET_free (wh->verification_id);
     780            0 :   GNUNET_free (wh->applicant_id);
     781            0 :   GNUNET_free (wh->url);
     782            0 :   GNUNET_free (wh);
     783            0 : }
     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            0 : log_failure (const json_t *verifications)
     793              : {
     794              :   const json_t *member;
     795              :   const char *name;
     796              : 
     797            0 :   json_object_foreach ((json_t *) verifications, name, member)
     798              :   {
     799              :     bool iverified;
     800              :     const char *comment;
     801              :     struct GNUNET_JSON_Specification spec[] = {
     802            0 :       GNUNET_JSON_spec_bool ("verified",
     803              :                              &iverified),
     804            0 :       GNUNET_JSON_spec_string ("comment",
     805              :                                &comment),
     806            0 :       GNUNET_JSON_spec_end ()
     807              :     };
     808              : 
     809            0 :     if (GNUNET_OK !=
     810            0 :         GNUNET_JSON_parse (member,
     811              :                            spec,
     812              :                            NULL, NULL))
     813              :     {
     814            0 :       GNUNET_break_op (0);
     815            0 :       json_dumpf (member,
     816              :                   stderr,
     817              :                   JSON_INDENT (2));
     818            0 :       continue;
     819              :     }
     820            0 :     if (iverified)
     821            0 :       continue;
     822            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     823              :                 "KYC verification of attribute `%s' failed: %s\n",
     824              :                 name,
     825              :                 comment);
     826              :   }
     827            0 : }
     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            0 : webhook_conversion_cb (void *cls,
     840              :                        enum GNUNET_OS_ProcessStatusType status_type,
     841              :                        unsigned long code,
     842              :                        const json_t *result)
     843              : {
     844            0 :   struct TALER_KYCLOGIC_WebhookHandle *wh = cls;
     845              :   struct GNUNET_TIME_Absolute expiration;
     846              :   struct MHD_Response *resp;
     847              : 
     848            0 :   wh->econ = NULL;
     849            0 :   if ( (0 == code) &&
     850              :        (NULL == result) )
     851              :   {
     852              :     /* No result, but *our helper* was OK => bad input */
     853            0 :     GNUNET_break_op (0);
     854            0 :     json_dumpf (wh->json_response,
     855              :                 stderr,
     856              :                 JSON_INDENT (2));
     857            0 :     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            0 :     wh->cb (wh->cb_cls,
     863              :             wh->process_row,
     864            0 :             &wh->h_payto,
     865            0 :             wh->is_wallet,
     866            0 :             wh->pd->section,
     867            0 :             wh->applicant_id,
     868            0 :             wh->verification_id,
     869              :             TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
     870            0 :             GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
     871              :             NULL,
     872              :             MHD_HTTP_BAD_GATEWAY,
     873              :             resp);
     874            0 :     kycaid_webhook_cancel (wh);
     875            0 :     return;
     876              :   }
     877            0 :   if (NULL == result)
     878              :   {
     879              :     /* Failure in our helper */
     880            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     881              :                 "Helper exited with status code %d\n",
     882              :                 (int) code);
     883            0 :     json_dumpf (wh->json_response,
     884              :                 stderr,
     885              :                 JSON_INDENT (2));
     886            0 :     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            0 :     wh->cb (wh->cb_cls,
     892              :             wh->process_row,
     893            0 :             &wh->h_payto,
     894            0 :             wh->is_wallet,
     895            0 :             wh->pd->section,
     896            0 :             wh->applicant_id,
     897            0 :             wh->verification_id,
     898              :             TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
     899            0 :             GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
     900              :             NULL,
     901              :             MHD_HTTP_BAD_GATEWAY,
     902              :             resp);
     903            0 :     kycaid_webhook_cancel (wh);
     904            0 :     return;
     905              :   }
     906            0 :   if (! json_is_string (json_object_get (result,
     907              :                                          "FORM_ID")))
     908              :   {
     909              :     /* Failure in our helper */
     910            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     911              :                 "Mandatory FORM_ID not set in result\n");
     912            0 :     json_dumpf (result,
     913              :                 stderr,
     914              :                 JSON_INDENT (2));
     915            0 :     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            0 :     wh->cb (wh->cb_cls,
     921              :             wh->process_row,
     922            0 :             &wh->h_payto,
     923            0 :             wh->is_wallet,
     924            0 :             wh->pd->section,
     925            0 :             wh->applicant_id,
     926            0 :             wh->verification_id,
     927              :             TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
     928            0 :             GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
     929              :             NULL,
     930              :             MHD_HTTP_BAD_GATEWAY,
     931              :             resp);
     932            0 :     kycaid_webhook_cancel (wh);
     933            0 :     return;
     934              :   }
     935              : 
     936            0 :   expiration = GNUNET_TIME_relative_to_absolute (wh->pd->validity);
     937            0 :   resp = MHD_create_response_from_buffer_static (0,
     938              :                                                  "");
     939            0 :   wh->cb (wh->cb_cls,
     940              :           wh->process_row,
     941            0 :           &wh->h_payto,
     942            0 :           wh->is_wallet,
     943            0 :           wh->pd->section,
     944            0 :           wh->applicant_id,
     945            0 :           wh->verification_id,
     946              :           TALER_KYCLOGIC_STATUS_SUCCESS,
     947              :           expiration,
     948              :           result,
     949              :           MHD_HTTP_NO_CONTENT,
     950              :           resp);
     951            0 :   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            0 : handle_webhook_finished (void *cls,
     965              :                          long response_code,
     966              :                          const void *response)
     967              : {
     968            0 :   struct TALER_KYCLOGIC_WebhookHandle *wh = cls;
     969            0 :   const json_t *j = response;
     970              :   struct MHD_Response *resp;
     971              : 
     972            0 :   wh->job = NULL;
     973            0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     974              :               "Webhook returned with HTTP status %u\n",
     975              :               (unsigned int) response_code);
     976            0 :   wh->kycaid_response_code = response_code;
     977            0 :   wh->json_response = json_incref ((json_t *) j);
     978            0 :   switch (response_code)
     979              :   {
     980            0 :   case MHD_HTTP_OK:
     981              :     {
     982              :       const char *profile_status;
     983              : 
     984            0 :       profile_status = json_string_value (
     985            0 :         json_object_get (
     986              :           j,
     987              :           "profile_status"));
     988            0 :       if (0 != strcasecmp ("valid",
     989              :                            profile_status))
     990              :       {
     991              :         enum TALER_KYCLOGIC_KycStatus ks;
     992              : 
     993            0 :         ks = (0 == strcasecmp ("pending",
     994              :                                profile_status))
     995              :           ? TALER_KYCLOGIC_STATUS_PENDING
     996            0 :           : TALER_KYCLOGIC_STATUS_USER_ABORTED;
     997            0 :         resp = MHD_create_response_from_buffer_static (0,
     998              :                                                        "");
     999            0 :         wh->cb (wh->cb_cls,
    1000              :                 wh->process_row,
    1001            0 :                 &wh->h_payto,
    1002            0 :                 wh->is_wallet,
    1003            0 :                 wh->pd->section,
    1004            0 :                 wh->applicant_id,
    1005            0 :                 wh->verification_id,
    1006              :                 ks,
    1007            0 :                 GNUNET_TIME_UNIT_ZERO_ABS,
    1008              :                 NULL,
    1009              :                 MHD_HTTP_NO_CONTENT,
    1010              :                 resp);
    1011            0 :         break;
    1012              :       }
    1013              :       {
    1014            0 :         const char *argv[] = {
    1015            0 :           wh->pd->conversion_helper,
    1016              :           "-a",
    1017            0 :           wh->pd->auth_token,
    1018              :           NULL,
    1019              :         };
    1020              : 
    1021              :         wh->econ
    1022            0 :           = TALER_JSON_external_conversion_start (
    1023              :               j,
    1024              :               &webhook_conversion_cb,
    1025              :               wh,
    1026            0 :               wh->pd->conversion_helper,
    1027              :               argv);
    1028              :       }
    1029            0 :       if (NULL == wh->econ)
    1030              :       {
    1031            0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1032              :                     "Failed to start KYCAID conversion helper `%s'\n",
    1033              :                     wh->pd->conversion_helper);
    1034            0 :         resp = TALER_MHD_make_error (
    1035              :           TALER_EC_EXCHANGE_GENERIC_KYC_CONVERTER_FAILED,
    1036              :           NULL);
    1037            0 :         wh->cb (wh->cb_cls,
    1038              :                 wh->process_row,
    1039            0 :                 &wh->h_payto,
    1040            0 :                 wh->is_wallet,
    1041            0 :                 wh->pd->section,
    1042            0 :                 wh->applicant_id,
    1043            0 :                 wh->verification_id,
    1044              :                 TALER_KYCLOGIC_STATUS_INTERNAL_ERROR,
    1045            0 :                 GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
    1046              :                 NULL,
    1047              :                 MHD_HTTP_INTERNAL_SERVER_ERROR,
    1048              :                 resp);
    1049            0 :         break;
    1050              :       }
    1051            0 :       return;
    1052              :     }
    1053              :     break;
    1054            0 :   case MHD_HTTP_BAD_REQUEST:
    1055              :   case MHD_HTTP_NOT_FOUND:
    1056              :   case MHD_HTTP_CONFLICT:
    1057            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1058              :                 "KYCAID failed with response %u:\n",
    1059              :                 (unsigned int) response_code);
    1060            0 :     json_dumpf (j,
    1061              :                 stderr,
    1062              :                 JSON_INDENT (2));
    1063            0 :     resp = TALER_MHD_MAKE_JSON_PACK (
    1064              :       GNUNET_JSON_pack_uint64 ("kycaid_http_status",
    1065              :                                response_code));
    1066            0 :     wh->cb (wh->cb_cls,
    1067              :             wh->process_row,
    1068            0 :             &wh->h_payto,
    1069            0 :             wh->is_wallet,
    1070            0 :             wh->pd->section,
    1071            0 :             wh->applicant_id,
    1072            0 :             wh->verification_id,
    1073              :             TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
    1074            0 :             GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
    1075              :             NULL,
    1076              :             MHD_HTTP_INTERNAL_SERVER_ERROR,
    1077              :             resp);
    1078            0 :     break;
    1079            0 :   case MHD_HTTP_UNAUTHORIZED:
    1080              :   case MHD_HTTP_PAYMENT_REQUIRED:
    1081            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1082              :                 "Refused access with HTTP status code %u\n",
    1083              :                 (unsigned int) response_code);
    1084            0 :     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            0 :     wh->cb (wh->cb_cls,
    1090              :             wh->process_row,
    1091            0 :             &wh->h_payto,
    1092            0 :             wh->is_wallet,
    1093            0 :             wh->pd->section,
    1094            0 :             wh->applicant_id,
    1095            0 :             wh->verification_id,
    1096              :             TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
    1097            0 :             GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
    1098              :             NULL,
    1099              :             MHD_HTTP_NETWORK_AUTHENTICATION_REQUIRED,
    1100              :             resp);
    1101            0 :     break;
    1102            0 :   case MHD_HTTP_REQUEST_TIMEOUT:
    1103            0 :     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            0 :     wh->cb (wh->cb_cls,
    1109              :             wh->process_row,
    1110            0 :             &wh->h_payto,
    1111            0 :             wh->is_wallet,
    1112            0 :             wh->pd->section,
    1113            0 :             wh->applicant_id,
    1114            0 :             wh->verification_id,
    1115              :             TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
    1116            0 :             GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
    1117              :             NULL,
    1118              :             MHD_HTTP_GATEWAY_TIMEOUT,
    1119              :             resp);
    1120            0 :     break;
    1121            0 :   case MHD_HTTP_UNPROCESSABLE_ENTITY: /* validation */
    1122            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1123              :                 "KYCAID failed with response %u:\n",
    1124              :                 (unsigned int) response_code);
    1125            0 :     json_dumpf (j,
    1126              :                 stderr,
    1127              :                 JSON_INDENT (2));
    1128            0 :     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            0 :     wh->cb (wh->cb_cls,
    1134              :             wh->process_row,
    1135            0 :             &wh->h_payto,
    1136            0 :             wh->is_wallet,
    1137            0 :             wh->pd->section,
    1138            0 :             wh->applicant_id,
    1139            0 :             wh->verification_id,
    1140              :             TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
    1141            0 :             GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
    1142              :             NULL,
    1143              :             MHD_HTTP_BAD_GATEWAY,
    1144              :             resp);
    1145            0 :     break;
    1146            0 :   case MHD_HTTP_TOO_MANY_REQUESTS:
    1147            0 :     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            0 :     wh->cb (wh->cb_cls,
    1153              :             wh->process_row,
    1154            0 :             &wh->h_payto,
    1155            0 :             wh->is_wallet,
    1156            0 :             wh->pd->section,
    1157            0 :             wh->applicant_id,
    1158            0 :             wh->verification_id,
    1159              :             TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
    1160            0 :             GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
    1161              :             NULL,
    1162              :             MHD_HTTP_SERVICE_UNAVAILABLE,
    1163              :             resp);
    1164            0 :     break;
    1165            0 :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    1166            0 :     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            0 :     wh->cb (wh->cb_cls,
    1172              :             wh->process_row,
    1173            0 :             &wh->h_payto,
    1174            0 :             wh->is_wallet,
    1175            0 :             wh->pd->section,
    1176            0 :             wh->applicant_id,
    1177            0 :             wh->verification_id,
    1178              :             TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
    1179            0 :             GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
    1180              :             NULL,
    1181              :             MHD_HTTP_BAD_GATEWAY,
    1182              :             resp);
    1183            0 :     break;
    1184            0 :   default:
    1185            0 :     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            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1191              :                 "Unexpected KYCAID response %u:\n",
    1192              :                 (unsigned int) response_code);
    1193            0 :     json_dumpf (j,
    1194              :                 stderr,
    1195              :                 JSON_INDENT (2));
    1196            0 :     wh->cb (wh->cb_cls,
    1197              :             wh->process_row,
    1198            0 :             &wh->h_payto,
    1199            0 :             wh->is_wallet,
    1200            0 :             wh->pd->section,
    1201            0 :             wh->applicant_id,
    1202            0 :             wh->verification_id,
    1203              :             TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
    1204            0 :             GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
    1205              :             NULL,
    1206              :             MHD_HTTP_BAD_GATEWAY,
    1207              :             resp);
    1208            0 :     break;
    1209              :   }
    1210            0 :   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            0 : async_webhook_reply (void *cls)
    1221              : {
    1222            0 :   struct TALER_KYCLOGIC_WebhookHandle *wh = cls;
    1223              : 
    1224            0 :   wh->task = NULL;
    1225            0 :   wh->cb (wh->cb_cls,
    1226              :           wh->process_row,
    1227            0 :           (0 == wh->process_row)
    1228              :           ? NULL
    1229              :           : &wh->h_payto,
    1230            0 :           wh->is_wallet,
    1231            0 :           wh->pd->section,
    1232            0 :           wh->applicant_id, /* provider user ID */
    1233            0 :           wh->verification_id, /* provider legi ID */
    1234              :           TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
    1235            0 :           GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
    1236              :           NULL,
    1237              :           wh->response_code,
    1238              :           wh->resp);
    1239            0 :   kycaid_webhook_cancel (wh);
    1240            0 : }
    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            0 : 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            0 :   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            0 :   const char *status = NULL;
    1283            0 :   bool verified = false;
    1284            0 :   bool no_verified = true;
    1285            0 :   const json_t *verifications = NULL;
    1286              :   struct GNUNET_JSON_Specification spec[] = {
    1287            0 :     GNUNET_JSON_spec_string ("request_id",
    1288              :                              &request_id),
    1289            0 :     GNUNET_JSON_spec_string ("type",
    1290              :                              &type),
    1291            0 :     GNUNET_JSON_spec_string ("verification_id",
    1292              :                              &verification_id),
    1293            0 :     GNUNET_JSON_spec_string ("applicant_id",
    1294              :                              &applicant_id),
    1295            0 :     GNUNET_JSON_spec_string ("form_id",
    1296              :                              &form_id),
    1297            0 :     GNUNET_JSON_spec_mark_optional (
    1298              :       GNUNET_JSON_spec_string ("status",
    1299              :                                &status),
    1300              :       NULL),
    1301            0 :     GNUNET_JSON_spec_mark_optional (
    1302              :       GNUNET_JSON_spec_bool ("verified",
    1303              :                              &verified),
    1304              :       &no_verified),
    1305            0 :     GNUNET_JSON_spec_mark_optional (
    1306              :       GNUNET_JSON_spec_object_const ("verifications",
    1307              :                                      &verifications),
    1308              :       NULL),
    1309            0 :     GNUNET_JSON_spec_end ()
    1310              :   };
    1311              :   enum GNUNET_DB_QueryStatus qs;
    1312              : 
    1313            0 :   wh = GNUNET_new (struct TALER_KYCLOGIC_WebhookHandle);
    1314            0 :   wh->cb = cb;
    1315            0 :   wh->cb_cls = cb_cls;
    1316            0 :   wh->ps = ps;
    1317            0 :   wh->pd = pd;
    1318            0 :   wh->connection = connection;
    1319            0 :   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            0 :   if (NULL != body)
    1325            0 :     json_dumpf (body,
    1326              :                 stderr,
    1327              :                 JSON_INDENT (2));
    1328              : #endif
    1329            0 :   if (NULL == pd)
    1330              :   {
    1331            0 :     GNUNET_break_op (0);
    1332            0 :     json_dumpf (body,
    1333              :                 stderr,
    1334              :                 JSON_INDENT (2));
    1335            0 :     wh->resp = TALER_MHD_make_error (
    1336              :       TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_UNKNOWN,
    1337              :       "kycaid");
    1338            0 :     wh->response_code = MHD_HTTP_NOT_FOUND;
    1339            0 :     wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply,
    1340              :                                          wh);
    1341            0 :     return wh;
    1342              :   }
    1343              : 
    1344            0 :   if (GNUNET_OK !=
    1345            0 :       GNUNET_JSON_parse (body,
    1346              :                          spec,
    1347              :                          NULL, NULL))
    1348              :   {
    1349            0 :     GNUNET_break_op (0);
    1350            0 :     json_dumpf (body,
    1351              :                 stderr,
    1352              :                 JSON_INDENT (2));
    1353            0 :     wh->resp = TALER_MHD_MAKE_JSON_PACK (
    1354              :       GNUNET_JSON_pack_object_incref ("webhook_body",
    1355              :                                       (json_t *) body));
    1356            0 :     wh->response_code = MHD_HTTP_BAD_REQUEST;
    1357            0 :     wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply,
    1358              :                                          wh);
    1359            0 :     return wh;
    1360              :   }
    1361            0 :   qs = plc (plc_cls,
    1362            0 :             pd->section,
    1363              :             verification_id,
    1364              :             &wh->h_payto,
    1365              :             &wh->is_wallet,
    1366              :             &wh->process_row);
    1367            0 :   if (qs < 0)
    1368              :   {
    1369            0 :     wh->resp = TALER_MHD_make_error (TALER_EC_GENERIC_DB_FETCH_FAILED,
    1370              :                                      "provider-legitimization-lookup");
    1371            0 :     wh->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
    1372            0 :     wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply,
    1373              :                                          wh);
    1374            0 :     return wh;
    1375              :   }
    1376            0 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    1377              :   {
    1378            0 :     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            0 :     wh->resp = TALER_MHD_make_error (
    1383              :       TALER_EC_EXCHANGE_KYC_PROOF_REQUEST_UNKNOWN,
    1384              :       verification_id);
    1385            0 :     wh->response_code = MHD_HTTP_NOT_FOUND;
    1386            0 :     wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply,
    1387              :                                          wh);
    1388            0 :     return wh;
    1389              :   }
    1390            0 :   wh->verification_id = GNUNET_strdup (verification_id);
    1391            0 :   wh->applicant_id = GNUNET_strdup (applicant_id);
    1392            0 :   if ( (0 != strcasecmp (type,
    1393            0 :                          "VERIFICATION_COMPLETED")) ||
    1394            0 :        (no_verified) ||
    1395            0 :        (! verified) )
    1396              :   {
    1397              :     /* We don't need to re-confirm the failure by
    1398              :        asking the API again. */
    1399            0 :     log_failure (verifications);
    1400            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1401              :                 "Webhook called with non-completion status: %s\n",
    1402              :                 type);
    1403            0 :     wh->response_code = MHD_HTTP_NO_CONTENT;
    1404            0 :     wh->resp = MHD_create_response_from_buffer_static (0,
    1405              :                                                        "");
    1406            0 :     wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply,
    1407              :                                          wh);
    1408            0 :     return wh;
    1409              :   }
    1410              : 
    1411            0 :   eh = curl_easy_init ();
    1412            0 :   if (NULL == eh)
    1413              :   {
    1414            0 :     GNUNET_break (0);
    1415            0 :     wh->resp = TALER_MHD_make_error (
    1416              :       TALER_EC_GENERIC_ALLOCATION_FAILURE,
    1417              :       NULL);
    1418            0 :     wh->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
    1419            0 :     wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply,
    1420              :                                          wh);
    1421            0 :     return wh;
    1422              :   }
    1423              : 
    1424            0 :   GNUNET_asprintf (&wh->url,
    1425              :                    "https://api.kycaid.com/applicants/%s",
    1426              :                    applicant_id);
    1427            0 :   GNUNET_break (CURLE_OK ==
    1428              :                 curl_easy_setopt (eh,
    1429              :                                   CURLOPT_VERBOSE,
    1430              :                                   0));
    1431            0 :   GNUNET_assert (CURLE_OK ==
    1432              :                  curl_easy_setopt (eh,
    1433              :                                    CURLOPT_MAXREDIRS,
    1434              :                                    1L));
    1435            0 :   GNUNET_break (CURLE_OK ==
    1436              :                 curl_easy_setopt (eh,
    1437              :                                   CURLOPT_URL,
    1438              :                                   wh->url));
    1439            0 :   wh->job = GNUNET_CURL_job_add2 (ps->curl_ctx,
    1440              :                                   eh,
    1441            0 :                                   pd->slist,
    1442              :                                   &handle_webhook_finished,
    1443              :                                   wh);
    1444            0 :   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           61 : libtaler_plugin_kyclogic_kycaid_init (void *cls)
    1460              : {
    1461           61 :   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
    1462              :   struct TALER_KYCLOGIC_Plugin *plugin;
    1463              :   struct PluginState *ps;
    1464              : 
    1465           61 :   ps = GNUNET_new (struct PluginState);
    1466           61 :   ps->cfg = cfg;
    1467           61 :   if (GNUNET_OK !=
    1468           61 :       GNUNET_CONFIGURATION_get_value_string (cfg,
    1469              :                                              "exchange",
    1470              :                                              "BASE_URL",
    1471              :                                              &ps->exchange_base_url))
    1472              :   {
    1473            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    1474              :                                "exchange",
    1475              :                                "BASE_URL");
    1476            0 :     GNUNET_free (ps);
    1477            0 :     return NULL;
    1478              :   }
    1479              : 
    1480              :   ps->curl_ctx
    1481          122 :     = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
    1482           61 :                         &ps->curl_rc);
    1483           61 :   if (NULL == ps->curl_ctx)
    1484              :   {
    1485            0 :     GNUNET_break (0);
    1486            0 :     GNUNET_free (ps->exchange_base_url);
    1487            0 :     GNUNET_free (ps);
    1488            0 :     return NULL;
    1489              :   }
    1490           61 :   ps->curl_rc = GNUNET_CURL_gnunet_rc_create (ps->curl_ctx);
    1491              : 
    1492           61 :   plugin = GNUNET_new (struct TALER_KYCLOGIC_Plugin);
    1493           61 :   plugin->cls = ps;
    1494              :   plugin->load_configuration
    1495           61 :     = &kycaid_load_configuration;
    1496              :   plugin->unload_configuration
    1497           61 :     = &kycaid_unload_configuration;
    1498              :   plugin->initiate
    1499           61 :     = &kycaid_initiate;
    1500              :   plugin->initiate_cancel
    1501           61 :     = &kycaid_initiate_cancel;
    1502              :   plugin->proof
    1503           61 :     = &kycaid_proof;
    1504              :   plugin->proof_cancel
    1505           61 :     = &kycaid_proof_cancel;
    1506              :   plugin->webhook
    1507           61 :     = &kycaid_webhook;
    1508              :   plugin->webhook_cancel
    1509           61 :     = &kycaid_webhook_cancel;
    1510           61 :   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           61 : libtaler_plugin_kyclogic_kycaid_done (void *cls)
    1526              : {
    1527           61 :   struct TALER_KYCLOGIC_Plugin *plugin = cls;
    1528           61 :   struct PluginState *ps = plugin->cls;
    1529              : 
    1530           61 :   if (NULL != ps->curl_ctx)
    1531              :   {
    1532           61 :     GNUNET_CURL_fini (ps->curl_ctx);
    1533           61 :     ps->curl_ctx = NULL;
    1534              :   }
    1535           61 :   if (NULL != ps->curl_rc)
    1536              :   {
    1537           61 :     GNUNET_CURL_gnunet_rc_destroy (ps->curl_rc);
    1538           61 :     ps->curl_rc = NULL;
    1539              :   }
    1540           61 :   GNUNET_free (ps->exchange_base_url);
    1541           61 :   GNUNET_free (ps);
    1542           61 :   GNUNET_free (plugin);
    1543           61 :   return NULL;
    1544              : }
        

Generated by: LCOV version 2.0-1