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

Generated by: LCOV version 2.0-1