LCOV - code coverage report
Current view: top level - kyclogic - plugin_kyclogic_kycaid.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 59 482 12.2 %
Date: 2025-06-05 21:03:14 Functions: 4 16 25.0 %

          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 "platform.h"
      22             : #include "taler_attributes.h"
      23             : #include "taler_kyclogic_lib.h"
      24             : #include "taler_kyclogic_plugin.h"
      25             : #include "taler_mhd_lib.h"
      26             : #include "taler_curl_lib.h"
      27             : #include "taler_json_lib.h"
      28             : #include "taler_templating_lib.h"
      29             : #include <regex.h>
      30             : #include "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          76 : kycaid_unload_configuration (struct TALER_KYCLOGIC_ProviderDetails *pd)
     306             : {
     307          76 :   curl_slist_free_all (pd->slist);
     308          76 :   GNUNET_free (pd->conversion_helper);
     309          76 :   GNUNET_free (pd->auth_token);
     310          76 :   GNUNET_free (pd->form_id);
     311          76 :   GNUNET_free (pd->section);
     312          76 :   GNUNET_free (pd);
     313          76 : }
     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          76 : kycaid_load_configuration (void *cls,
     325             :                            const char *provider_section_name)
     326             : {
     327          76 :   struct PluginState *ps = cls;
     328             :   struct TALER_KYCLOGIC_ProviderDetails *pd;
     329             : 
     330          76 :   pd = GNUNET_new (struct TALER_KYCLOGIC_ProviderDetails);
     331          76 :   pd->ps = ps;
     332          76 :   pd->section = GNUNET_strdup (provider_section_name);
     333          76 :   if (GNUNET_OK !=
     334          76 :       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          76 :   if (GNUNET_OK !=
     346          76 :       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          76 :   if (GNUNET_OK !=
     358          76 :       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          76 :   if (GNUNET_OK !=
     370          76 :       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          76 :     GNUNET_asprintf (&auth,
     385             :                      "%s: Token %s",
     386             :                      MHD_HTTP_HEADER_AUTHORIZATION,
     387             :                      pd->auth_token);
     388          76 :     pd->slist = curl_slist_append (NULL,
     389             :                                    auth);
     390          76 :     GNUNET_free (auth);
     391             :   }
     392          76 :   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 :   expiration = GNUNET_TIME_relative_to_absolute (wh->pd->validity);
     907           0 :   resp = MHD_create_response_from_buffer_static (0,
     908             :                                                  "");
     909           0 :   wh->cb (wh->cb_cls,
     910             :           wh->process_row,
     911           0 :           &wh->h_payto,
     912           0 :           wh->is_wallet,
     913           0 :           wh->pd->section,
     914           0 :           wh->applicant_id,
     915           0 :           wh->verification_id,
     916             :           TALER_KYCLOGIC_STATUS_SUCCESS,
     917             :           expiration,
     918             :           result,
     919             :           MHD_HTTP_NO_CONTENT,
     920             :           resp);
     921           0 :   kycaid_webhook_cancel (wh);
     922             : }
     923             : 
     924             : 
     925             : /**
     926             :  * Function called when we're done processing the
     927             :  * HTTP "/applicants/{verification_id}" request.
     928             :  *
     929             :  * @param cls the `struct TALER_KYCLOGIC_WebhookHandle`
     930             :  * @param response_code HTTP response code, 0 on error
     931             :  * @param response parsed JSON result, NULL on error
     932             :  */
     933             : static void
     934           0 : handle_webhook_finished (void *cls,
     935             :                          long response_code,
     936             :                          const void *response)
     937             : {
     938           0 :   struct TALER_KYCLOGIC_WebhookHandle *wh = cls;
     939           0 :   const json_t *j = response;
     940             :   struct MHD_Response *resp;
     941             : 
     942           0 :   wh->job = NULL;
     943           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     944             :               "Webhook returned with HTTP status %u\n",
     945             :               (unsigned int) response_code);
     946           0 :   wh->kycaid_response_code = response_code;
     947           0 :   wh->json_response = json_incref ((json_t *) j);
     948           0 :   switch (response_code)
     949             :   {
     950           0 :   case MHD_HTTP_OK:
     951             :     {
     952             :       const char *profile_status;
     953             : 
     954           0 :       profile_status = json_string_value (
     955           0 :         json_object_get (
     956             :           j,
     957             :           "profile_status"));
     958           0 :       if (0 != strcasecmp ("valid",
     959             :                            profile_status))
     960             :       {
     961             :         enum TALER_KYCLOGIC_KycStatus ks;
     962             : 
     963           0 :         ks = (0 == strcasecmp ("pending",
     964             :                                profile_status))
     965             :           ? TALER_KYCLOGIC_STATUS_PENDING
     966           0 :           : TALER_KYCLOGIC_STATUS_USER_ABORTED;
     967           0 :         resp = MHD_create_response_from_buffer_static (0,
     968             :                                                        "");
     969           0 :         wh->cb (wh->cb_cls,
     970             :                 wh->process_row,
     971           0 :                 &wh->h_payto,
     972           0 :                 wh->is_wallet,
     973           0 :                 wh->pd->section,
     974           0 :                 wh->applicant_id,
     975           0 :                 wh->verification_id,
     976             :                 ks,
     977           0 :                 GNUNET_TIME_UNIT_ZERO_ABS,
     978             :                 NULL,
     979             :                 MHD_HTTP_NO_CONTENT,
     980             :                 resp);
     981           0 :         break;
     982             :       }
     983             :       {
     984           0 :         const char *argv[] = {
     985           0 :           wh->pd->conversion_helper,
     986             :           "-a",
     987           0 :           wh->pd->auth_token,
     988             :           NULL,
     989             :         };
     990             : 
     991             :         wh->econ
     992           0 :           = TALER_JSON_external_conversion_start (
     993             :               j,
     994             :               &webhook_conversion_cb,
     995             :               wh,
     996           0 :               wh->pd->conversion_helper,
     997             :               argv);
     998             :       }
     999           0 :       if (NULL == wh->econ)
    1000             :       {
    1001           0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1002             :                     "Failed to start KYCAID conversion helper `%s'\n",
    1003             :                     wh->pd->conversion_helper);
    1004           0 :         resp = TALER_MHD_make_error (
    1005             :           TALER_EC_EXCHANGE_GENERIC_KYC_CONVERTER_FAILED,
    1006             :           NULL);
    1007           0 :         wh->cb (wh->cb_cls,
    1008             :                 wh->process_row,
    1009           0 :                 &wh->h_payto,
    1010           0 :                 wh->is_wallet,
    1011           0 :                 wh->pd->section,
    1012           0 :                 wh->applicant_id,
    1013           0 :                 wh->verification_id,
    1014             :                 TALER_KYCLOGIC_STATUS_INTERNAL_ERROR,
    1015           0 :                 GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
    1016             :                 NULL,
    1017             :                 MHD_HTTP_INTERNAL_SERVER_ERROR,
    1018             :                 resp);
    1019           0 :         break;
    1020             :       }
    1021           0 :       return;
    1022             :     }
    1023             :     break;
    1024           0 :   case MHD_HTTP_BAD_REQUEST:
    1025             :   case MHD_HTTP_NOT_FOUND:
    1026             :   case MHD_HTTP_CONFLICT:
    1027           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1028             :                 "KYCAID failed with response %u:\n",
    1029             :                 (unsigned int) response_code);
    1030           0 :     json_dumpf (j,
    1031             :                 stderr,
    1032             :                 JSON_INDENT (2));
    1033           0 :     resp = TALER_MHD_MAKE_JSON_PACK (
    1034             :       GNUNET_JSON_pack_uint64 ("kycaid_http_status",
    1035             :                                response_code));
    1036           0 :     wh->cb (wh->cb_cls,
    1037             :             wh->process_row,
    1038           0 :             &wh->h_payto,
    1039           0 :             wh->is_wallet,
    1040           0 :             wh->pd->section,
    1041           0 :             wh->applicant_id,
    1042           0 :             wh->verification_id,
    1043             :             TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
    1044           0 :             GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
    1045             :             NULL,
    1046             :             MHD_HTTP_INTERNAL_SERVER_ERROR,
    1047             :             resp);
    1048           0 :     break;
    1049           0 :   case MHD_HTTP_UNAUTHORIZED:
    1050             :   case MHD_HTTP_PAYMENT_REQUIRED:
    1051           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1052             :                 "Refused access with HTTP status code %u\n",
    1053             :                 (unsigned int) response_code);
    1054           0 :     resp = TALER_MHD_MAKE_JSON_PACK (
    1055             :       GNUNET_JSON_pack_uint64 ("kycaid_http_status",
    1056             :                                response_code),
    1057             :       GNUNET_JSON_pack_object_incref ("kycaid_body",
    1058             :                                       (json_t *) j));
    1059           0 :     wh->cb (wh->cb_cls,
    1060             :             wh->process_row,
    1061           0 :             &wh->h_payto,
    1062           0 :             wh->is_wallet,
    1063           0 :             wh->pd->section,
    1064           0 :             wh->applicant_id,
    1065           0 :             wh->verification_id,
    1066             :             TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
    1067           0 :             GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
    1068             :             NULL,
    1069             :             MHD_HTTP_NETWORK_AUTHENTICATION_REQUIRED,
    1070             :             resp);
    1071           0 :     break;
    1072           0 :   case MHD_HTTP_REQUEST_TIMEOUT:
    1073           0 :     resp = TALER_MHD_MAKE_JSON_PACK (
    1074             :       GNUNET_JSON_pack_uint64 ("kycaid_http_status",
    1075             :                                response_code),
    1076             :       GNUNET_JSON_pack_object_incref ("kycaid_body",
    1077             :                                       (json_t *) j));
    1078           0 :     wh->cb (wh->cb_cls,
    1079             :             wh->process_row,
    1080           0 :             &wh->h_payto,
    1081           0 :             wh->is_wallet,
    1082           0 :             wh->pd->section,
    1083           0 :             wh->applicant_id,
    1084           0 :             wh->verification_id,
    1085             :             TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
    1086           0 :             GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
    1087             :             NULL,
    1088             :             MHD_HTTP_GATEWAY_TIMEOUT,
    1089             :             resp);
    1090           0 :     break;
    1091           0 :   case MHD_HTTP_UNPROCESSABLE_ENTITY: /* validation */
    1092           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1093             :                 "KYCAID failed with response %u:\n",
    1094             :                 (unsigned int) response_code);
    1095           0 :     json_dumpf (j,
    1096             :                 stderr,
    1097             :                 JSON_INDENT (2));
    1098           0 :     resp = TALER_MHD_MAKE_JSON_PACK (
    1099             :       GNUNET_JSON_pack_uint64 ("kycaid_http_status",
    1100             :                                response_code),
    1101             :       GNUNET_JSON_pack_object_incref ("kycaid_body",
    1102             :                                       (json_t *) j));
    1103           0 :     wh->cb (wh->cb_cls,
    1104             :             wh->process_row,
    1105           0 :             &wh->h_payto,
    1106           0 :             wh->is_wallet,
    1107           0 :             wh->pd->section,
    1108           0 :             wh->applicant_id,
    1109           0 :             wh->verification_id,
    1110             :             TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
    1111           0 :             GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
    1112             :             NULL,
    1113             :             MHD_HTTP_BAD_GATEWAY,
    1114             :             resp);
    1115           0 :     break;
    1116           0 :   case MHD_HTTP_TOO_MANY_REQUESTS:
    1117           0 :     resp = TALER_MHD_MAKE_JSON_PACK (
    1118             :       GNUNET_JSON_pack_uint64 ("kycaid_http_status",
    1119             :                                response_code),
    1120             :       GNUNET_JSON_pack_object_incref ("kycaid_body",
    1121             :                                       (json_t *) j));
    1122           0 :     wh->cb (wh->cb_cls,
    1123             :             wh->process_row,
    1124           0 :             &wh->h_payto,
    1125           0 :             wh->is_wallet,
    1126           0 :             wh->pd->section,
    1127           0 :             wh->applicant_id,
    1128           0 :             wh->verification_id,
    1129             :             TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
    1130           0 :             GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
    1131             :             NULL,
    1132             :             MHD_HTTP_SERVICE_UNAVAILABLE,
    1133             :             resp);
    1134           0 :     break;
    1135           0 :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    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->section,
    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_BAD_GATEWAY,
    1152             :             resp);
    1153           0 :     break;
    1154           0 :   default:
    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 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1161             :                 "Unexpected KYCAID response %u:\n",
    1162             :                 (unsigned int) response_code);
    1163           0 :     json_dumpf (j,
    1164             :                 stderr,
    1165             :                 JSON_INDENT (2));
    1166           0 :     wh->cb (wh->cb_cls,
    1167             :             wh->process_row,
    1168           0 :             &wh->h_payto,
    1169           0 :             wh->is_wallet,
    1170           0 :             wh->pd->section,
    1171           0 :             wh->applicant_id,
    1172           0 :             wh->verification_id,
    1173             :             TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
    1174           0 :             GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
    1175             :             NULL,
    1176             :             MHD_HTTP_BAD_GATEWAY,
    1177             :             resp);
    1178           0 :     break;
    1179             :   }
    1180           0 :   kycaid_webhook_cancel (wh);
    1181             : }
    1182             : 
    1183             : 
    1184             : /**
    1185             :  * Asynchronously return a reply for the webhook.
    1186             :  *
    1187             :  * @param cls a `struct TALER_KYCLOGIC_WebhookHandle *`
    1188             :  */
    1189             : static void
    1190           0 : async_webhook_reply (void *cls)
    1191             : {
    1192           0 :   struct TALER_KYCLOGIC_WebhookHandle *wh = cls;
    1193             : 
    1194           0 :   wh->task = NULL;
    1195           0 :   wh->cb (wh->cb_cls,
    1196             :           wh->process_row,
    1197           0 :           (0 == wh->process_row)
    1198             :           ? NULL
    1199             :           : &wh->h_payto,
    1200           0 :           wh->is_wallet,
    1201           0 :           wh->pd->section,
    1202           0 :           wh->applicant_id, /* provider user ID */
    1203           0 :           wh->verification_id, /* provider legi ID */
    1204             :           TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
    1205           0 :           GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
    1206             :           NULL,
    1207             :           wh->response_code,
    1208             :           wh->resp);
    1209           0 :   kycaid_webhook_cancel (wh);
    1210           0 : }
    1211             : 
    1212             : 
    1213             : /**
    1214             :  * Check KYC status and return result for Webhook.  We do NOT implement the
    1215             :  * authentication check proposed by the KYCAID documentation, as it would
    1216             :  * allow an attacker who learns the access token to easily bypass the KYC
    1217             :  * checks. Instead, we insist on explicitly requesting the KYC status from the
    1218             :  * provider (at least on success).
    1219             :  *
    1220             :  * @param cls the @e cls of this struct with the plugin-specific state
    1221             :  * @param pd provider configuration details
    1222             :  * @param plc callback to lookup accounts with
    1223             :  * @param plc_cls closure for @a plc
    1224             :  * @param http_method HTTP method used for the webhook
    1225             :  * @param url_path rest of the URL after `/kyc-webhook/`
    1226             :  * @param connection MHD connection object (for HTTP headers)
    1227             :  * @param body HTTP request body
    1228             :  * @param cb function to call with the result
    1229             :  * @param cb_cls closure for @a cb
    1230             :  * @return handle to cancel operation early
    1231             :  */
    1232             : static struct TALER_KYCLOGIC_WebhookHandle *
    1233           0 : kycaid_webhook (void *cls,
    1234             :                 const struct TALER_KYCLOGIC_ProviderDetails *pd,
    1235             :                 TALER_KYCLOGIC_ProviderLookupCallback plc,
    1236             :                 void *plc_cls,
    1237             :                 const char *http_method,
    1238             :                 const char *const url_path[],
    1239             :                 struct MHD_Connection *connection,
    1240             :                 const json_t *body,
    1241             :                 TALER_KYCLOGIC_WebhookCallback cb,
    1242             :                 void *cb_cls)
    1243             : {
    1244           0 :   struct PluginState *ps = cls;
    1245             :   struct TALER_KYCLOGIC_WebhookHandle *wh;
    1246             :   CURL *eh;
    1247             :   const char *request_id;
    1248             :   const char *type;
    1249             :   const char *verification_id; /* = provider_legitimization_id */
    1250             :   const char *applicant_id;
    1251             :   const char *form_id;
    1252           0 :   const char *status = NULL;
    1253           0 :   bool verified = false;
    1254           0 :   bool no_verified = true;
    1255           0 :   const json_t *verifications = NULL;
    1256             :   struct GNUNET_JSON_Specification spec[] = {
    1257           0 :     GNUNET_JSON_spec_string ("request_id",
    1258             :                              &request_id),
    1259           0 :     GNUNET_JSON_spec_string ("type",
    1260             :                              &type),
    1261           0 :     GNUNET_JSON_spec_string ("verification_id",
    1262             :                              &verification_id),
    1263           0 :     GNUNET_JSON_spec_string ("applicant_id",
    1264             :                              &applicant_id),
    1265           0 :     GNUNET_JSON_spec_string ("form_id",
    1266             :                              &form_id),
    1267           0 :     GNUNET_JSON_spec_mark_optional (
    1268             :       GNUNET_JSON_spec_string ("status",
    1269             :                                &status),
    1270             :       NULL),
    1271           0 :     GNUNET_JSON_spec_mark_optional (
    1272             :       GNUNET_JSON_spec_bool ("verified",
    1273             :                              &verified),
    1274             :       &no_verified),
    1275           0 :     GNUNET_JSON_spec_mark_optional (
    1276             :       GNUNET_JSON_spec_object_const ("verifications",
    1277             :                                      &verifications),
    1278             :       NULL),
    1279           0 :     GNUNET_JSON_spec_end ()
    1280             :   };
    1281             :   enum GNUNET_DB_QueryStatus qs;
    1282             : 
    1283           0 :   wh = GNUNET_new (struct TALER_KYCLOGIC_WebhookHandle);
    1284           0 :   wh->cb = cb;
    1285           0 :   wh->cb_cls = cb_cls;
    1286           0 :   wh->ps = ps;
    1287           0 :   wh->pd = pd;
    1288           0 :   wh->connection = connection;
    1289           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1290             :               "KYCAID webhook of `%s' triggered with %s\n",
    1291             :               pd->section,
    1292             :               http_method);
    1293             : #if 1
    1294           0 :   if (NULL != body)
    1295           0 :     json_dumpf (body,
    1296             :                 stderr,
    1297             :                 JSON_INDENT (2));
    1298             : #endif
    1299           0 :   if (NULL == pd)
    1300             :   {
    1301           0 :     GNUNET_break_op (0);
    1302           0 :     json_dumpf (body,
    1303             :                 stderr,
    1304             :                 JSON_INDENT (2));
    1305           0 :     wh->resp = TALER_MHD_make_error (
    1306             :       TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_UNKNOWN,
    1307             :       "kycaid");
    1308           0 :     wh->response_code = MHD_HTTP_NOT_FOUND;
    1309           0 :     wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply,
    1310             :                                          wh);
    1311           0 :     return wh;
    1312             :   }
    1313             : 
    1314           0 :   if (GNUNET_OK !=
    1315           0 :       GNUNET_JSON_parse (body,
    1316             :                          spec,
    1317             :                          NULL, NULL))
    1318             :   {
    1319           0 :     GNUNET_break_op (0);
    1320           0 :     json_dumpf (body,
    1321             :                 stderr,
    1322             :                 JSON_INDENT (2));
    1323           0 :     wh->resp = TALER_MHD_MAKE_JSON_PACK (
    1324             :       GNUNET_JSON_pack_object_incref ("webhook_body",
    1325             :                                       (json_t *) body));
    1326           0 :     wh->response_code = MHD_HTTP_BAD_REQUEST;
    1327           0 :     wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply,
    1328             :                                          wh);
    1329           0 :     return wh;
    1330             :   }
    1331           0 :   qs = plc (plc_cls,
    1332           0 :             pd->section,
    1333             :             verification_id,
    1334             :             &wh->h_payto,
    1335             :             &wh->is_wallet,
    1336             :             &wh->process_row);
    1337           0 :   if (qs < 0)
    1338             :   {
    1339           0 :     wh->resp = TALER_MHD_make_error (TALER_EC_GENERIC_DB_FETCH_FAILED,
    1340             :                                      "provider-legitimization-lookup");
    1341           0 :     wh->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
    1342           0 :     wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply,
    1343             :                                          wh);
    1344           0 :     return wh;
    1345             :   }
    1346           0 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    1347             :   {
    1348           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1349             :                 "Received webhook for unknown verification ID `%s' and section `%s'\n",
    1350             :                 verification_id,
    1351             :                 pd->section);
    1352           0 :     wh->resp = TALER_MHD_make_error (
    1353             :       TALER_EC_EXCHANGE_KYC_PROOF_REQUEST_UNKNOWN,
    1354             :       verification_id);
    1355           0 :     wh->response_code = MHD_HTTP_NOT_FOUND;
    1356           0 :     wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply,
    1357             :                                          wh);
    1358           0 :     return wh;
    1359             :   }
    1360           0 :   wh->verification_id = GNUNET_strdup (verification_id);
    1361           0 :   wh->applicant_id = GNUNET_strdup (applicant_id);
    1362           0 :   if ( (0 != strcasecmp (type,
    1363           0 :                          "VERIFICATION_COMPLETED")) ||
    1364           0 :        (no_verified) ||
    1365           0 :        (! verified) )
    1366             :   {
    1367             :     /* We don't need to re-confirm the failure by
    1368             :        asking the API again. */
    1369           0 :     log_failure (verifications);
    1370           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1371             :                 "Webhook called with non-completion status: %s\n",
    1372             :                 type);
    1373           0 :     wh->response_code = MHD_HTTP_NO_CONTENT;
    1374           0 :     wh->resp = MHD_create_response_from_buffer_static (0,
    1375             :                                                        "");
    1376           0 :     wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply,
    1377             :                                          wh);
    1378           0 :     return wh;
    1379             :   }
    1380             : 
    1381           0 :   eh = curl_easy_init ();
    1382           0 :   if (NULL == eh)
    1383             :   {
    1384           0 :     GNUNET_break (0);
    1385           0 :     wh->resp = TALER_MHD_make_error (
    1386             :       TALER_EC_GENERIC_ALLOCATION_FAILURE,
    1387             :       NULL);
    1388           0 :     wh->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
    1389           0 :     wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply,
    1390             :                                          wh);
    1391           0 :     return wh;
    1392             :   }
    1393             : 
    1394           0 :   GNUNET_asprintf (&wh->url,
    1395             :                    "https://api.kycaid.com/applicants/%s",
    1396             :                    applicant_id);
    1397           0 :   GNUNET_break (CURLE_OK ==
    1398             :                 curl_easy_setopt (eh,
    1399             :                                   CURLOPT_VERBOSE,
    1400             :                                   0));
    1401           0 :   GNUNET_assert (CURLE_OK ==
    1402             :                  curl_easy_setopt (eh,
    1403             :                                    CURLOPT_MAXREDIRS,
    1404             :                                    1L));
    1405           0 :   GNUNET_break (CURLE_OK ==
    1406             :                 curl_easy_setopt (eh,
    1407             :                                   CURLOPT_URL,
    1408             :                                   wh->url));
    1409           0 :   wh->job = GNUNET_CURL_job_add2 (ps->curl_ctx,
    1410             :                                   eh,
    1411           0 :                                   pd->slist,
    1412             :                                   &handle_webhook_finished,
    1413             :                                   wh);
    1414           0 :   return wh;
    1415             : }
    1416             : 
    1417             : 
    1418             : /**
    1419             :  * Initialize kycaid logic plugin
    1420             :  *
    1421             :  * @param cls a configuration instance
    1422             :  * @return NULL on error, otherwise a `struct TALER_KYCLOGIC_Plugin`
    1423             :  */
    1424             : void *
    1425             : libtaler_plugin_kyclogic_kycaid_init (void *cls);
    1426             : 
    1427             : /* declaration to avoid compiler warning */
    1428             : void *
    1429          76 : libtaler_plugin_kyclogic_kycaid_init (void *cls)
    1430             : {
    1431          76 :   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
    1432             :   struct TALER_KYCLOGIC_Plugin *plugin;
    1433             :   struct PluginState *ps;
    1434             : 
    1435          76 :   ps = GNUNET_new (struct PluginState);
    1436          76 :   ps->cfg = cfg;
    1437          76 :   if (GNUNET_OK !=
    1438          76 :       GNUNET_CONFIGURATION_get_value_string (cfg,
    1439             :                                              "exchange",
    1440             :                                              "BASE_URL",
    1441             :                                              &ps->exchange_base_url))
    1442             :   {
    1443           0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    1444             :                                "exchange",
    1445             :                                "BASE_URL");
    1446           0 :     GNUNET_free (ps);
    1447           0 :     return NULL;
    1448             :   }
    1449             : 
    1450             :   ps->curl_ctx
    1451         152 :     = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
    1452          76 :                         &ps->curl_rc);
    1453          76 :   if (NULL == ps->curl_ctx)
    1454             :   {
    1455           0 :     GNUNET_break (0);
    1456           0 :     GNUNET_free (ps->exchange_base_url);
    1457           0 :     GNUNET_free (ps);
    1458           0 :     return NULL;
    1459             :   }
    1460          76 :   ps->curl_rc = GNUNET_CURL_gnunet_rc_create (ps->curl_ctx);
    1461             : 
    1462          76 :   plugin = GNUNET_new (struct TALER_KYCLOGIC_Plugin);
    1463          76 :   plugin->cls = ps;
    1464             :   plugin->load_configuration
    1465          76 :     = &kycaid_load_configuration;
    1466             :   plugin->unload_configuration
    1467          76 :     = &kycaid_unload_configuration;
    1468             :   plugin->initiate
    1469          76 :     = &kycaid_initiate;
    1470             :   plugin->initiate_cancel
    1471          76 :     = &kycaid_initiate_cancel;
    1472             :   plugin->proof
    1473          76 :     = &kycaid_proof;
    1474             :   plugin->proof_cancel
    1475          76 :     = &kycaid_proof_cancel;
    1476             :   plugin->webhook
    1477          76 :     = &kycaid_webhook;
    1478             :   plugin->webhook_cancel
    1479          76 :     = &kycaid_webhook_cancel;
    1480          76 :   return plugin;
    1481             : }
    1482             : 
    1483             : 
    1484             : /**
    1485             :  * Unload authorization plugin
    1486             :  *
    1487             :  * @param cls a `struct TALER_KYCLOGIC_Plugin`
    1488             :  * @return NULL (always)
    1489             :  */
    1490             : void *
    1491             : libtaler_plugin_kyclogic_kycaid_done (void *cls);
    1492             : 
    1493             : /* declaration to avoid compiler warning */
    1494             : void *
    1495          76 : libtaler_plugin_kyclogic_kycaid_done (void *cls)
    1496             : {
    1497          76 :   struct TALER_KYCLOGIC_Plugin *plugin = cls;
    1498          76 :   struct PluginState *ps = plugin->cls;
    1499             : 
    1500          76 :   if (NULL != ps->curl_ctx)
    1501             :   {
    1502          76 :     GNUNET_CURL_fini (ps->curl_ctx);
    1503          76 :     ps->curl_ctx = NULL;
    1504             :   }
    1505          76 :   if (NULL != ps->curl_rc)
    1506             :   {
    1507          76 :     GNUNET_CURL_gnunet_rc_destroy (ps->curl_rc);
    1508          76 :     ps->curl_rc = NULL;
    1509             :   }
    1510          76 :   GNUNET_free (ps->exchange_base_url);
    1511          76 :   GNUNET_free (ps);
    1512          76 :   GNUNET_free (plugin);
    1513          76 :   return NULL;
    1514             : }

Generated by: LCOV version 1.16