plugin_kyclogic_kycaid.c (45442B)
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 kycaid_unload_configuration (struct TALER_KYCLOGIC_ProviderDetails *pd) 312 { 313 curl_slist_free_all (pd->slist); 314 GNUNET_free (pd->conversion_helper); 315 GNUNET_free (pd->auth_token); 316 GNUNET_free (pd->form_id); 317 GNUNET_free (pd->section); 318 GNUNET_free (pd->provider_name); 319 GNUNET_free (pd); 320 } 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 kycaid_load_configuration (void *cls, 332 const char *provider_section_name) 333 { 334 struct PluginState *ps = cls; 335 struct TALER_KYCLOGIC_ProviderDetails *pd; 336 337 pd = GNUNET_new (struct TALER_KYCLOGIC_ProviderDetails); 338 pd->ps = ps; 339 pd->section = GNUNET_strdup (provider_section_name); 340 pd->provider_name 341 = 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 if (GNUNET_OK != 348 GNUNET_CONFIGURATION_get_value_time (ps->cfg, 349 provider_section_name, 350 "KYC_KYCAID_VALIDITY", 351 &pd->validity)) 352 { 353 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 354 provider_section_name, 355 "KYC_KYCAID_VALIDITY"); 356 kycaid_unload_configuration (pd); 357 return NULL; 358 } 359 if (GNUNET_OK != 360 GNUNET_CONFIGURATION_get_value_string (ps->cfg, 361 provider_section_name, 362 "KYC_KYCAID_AUTH_TOKEN", 363 &pd->auth_token)) 364 { 365 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 366 provider_section_name, 367 "KYC_KYCAID_AUTH_TOKEN"); 368 kycaid_unload_configuration (pd); 369 return NULL; 370 } 371 if (GNUNET_OK != 372 GNUNET_CONFIGURATION_get_value_string (ps->cfg, 373 provider_section_name, 374 "KYC_KYCAID_FORM_ID", 375 &pd->form_id)) 376 { 377 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 378 provider_section_name, 379 "KYC_KYCAID_FORM_ID"); 380 kycaid_unload_configuration (pd); 381 return NULL; 382 } 383 if (GNUNET_OK != 384 GNUNET_CONFIGURATION_get_value_string (ps->cfg, 385 provider_section_name, 386 "KYC_KYCAID_CONVERTER_HELPER", 387 &pd->conversion_helper)) 388 { 389 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 390 provider_section_name, 391 "KYC_KYCAID_CONVERTER_HELPER"); 392 kycaid_unload_configuration (pd); 393 return NULL; 394 } 395 { 396 char *auth; 397 398 GNUNET_asprintf (&auth, 399 "%s: Token %s", 400 MHD_HTTP_HEADER_AUTHORIZATION, 401 pd->auth_token); 402 pd->slist = curl_slist_append (NULL, 403 auth); 404 GNUNET_free (auth); 405 } 406 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 kycaid_initiate_cancel (struct TALER_KYCLOGIC_InitiateHandle *ih) 417 { 418 if (NULL != ih->job) 419 { 420 GNUNET_CURL_job_cancel (ih->job); 421 ih->job = NULL; 422 } 423 GNUNET_free (ih->url); 424 TALER_curl_easy_post_finished (&ih->ctx); 425 GNUNET_free (ih); 426 } 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 handle_initiate_finished (void *cls, 439 long response_code, 440 const void *response) 441 { 442 struct TALER_KYCLOGIC_InitiateHandle *ih = cls; 443 const json_t *j = response; 444 445 ih->job = NULL; 446 switch (response_code) 447 { 448 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 GNUNET_JSON_spec_string ("verification_id", 455 &verification_id), 456 GNUNET_JSON_spec_string ("form_url", 457 &form_url), 458 GNUNET_JSON_spec_string ("form_id", 459 &form_id), 460 GNUNET_JSON_spec_end () 461 }; 462 463 if (GNUNET_OK != 464 GNUNET_JSON_parse (j, 465 spec, 466 NULL, NULL)) 467 { 468 GNUNET_break_op (0); 469 #if DEBUG 470 json_dumpf (j, 471 stderr, 472 JSON_INDENT (2)); 473 #endif 474 ih->cb (ih->cb_cls, 475 TALER_EC_EXCHANGE_KYC_GENERIC_PROVIDER_UNEXPECTED_REPLY, 476 NULL, 477 NULL, 478 NULL, 479 json_string_value (json_object_get (j, 480 "type"))); 481 break; 482 } 483 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 484 "Started new verification `%s' using form %s\n", 485 verification_id, 486 form_id); 487 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 GNUNET_JSON_parse_free (spec); 494 } 495 break; 496 case MHD_HTTP_BAD_REQUEST: 497 case MHD_HTTP_NOT_FOUND: 498 case MHD_HTTP_CONFLICT: 499 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 ih->cb (ih->cb_cls, 508 TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_BUG, 509 NULL, 510 NULL, 511 NULL, 512 json_string_value (json_object_get (j, 513 "type"))); 514 break; 515 case MHD_HTTP_UNAUTHORIZED: 516 case MHD_HTTP_PAYMENT_REQUIRED: 517 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 518 "Refused access with HTTP status code %u\n", 519 (unsigned int) response_code); 520 ih->cb (ih->cb_cls, 521 TALER_EC_EXCHANGE_KYC_GENERIC_PROVIDER_ACCESS_REFUSED, 522 NULL, 523 NULL, 524 NULL, 525 json_string_value (json_object_get (j, 526 "type"))); 527 break; 528 case MHD_HTTP_REQUEST_TIMEOUT: 529 ih->cb (ih->cb_cls, 530 TALER_EC_EXCHANGE_KYC_GENERIC_PROVIDER_TIMEOUT, 531 NULL, 532 NULL, 533 NULL, 534 json_string_value (json_object_get (j, 535 "type"))); 536 break; 537 case MHD_HTTP_UNPROCESSABLE_CONTENT: /* validation */ 538 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 ih->cb (ih->cb_cls, 547 TALER_EC_EXCHANGE_KYC_GENERIC_PROVIDER_UNEXPECTED_REPLY, 548 NULL, 549 NULL, 550 NULL, 551 json_string_value (json_object_get (j, 552 "type"))); 553 break; 554 case MHD_HTTP_TOO_MANY_REQUESTS: 555 ih->cb (ih->cb_cls, 556 TALER_EC_EXCHANGE_KYC_GENERIC_PROVIDER_RATE_LIMIT_EXCEEDED, 557 NULL, 558 NULL, 559 NULL, 560 json_string_value (json_object_get (j, 561 "type"))); 562 break; 563 case MHD_HTTP_INTERNAL_SERVER_ERROR: 564 ih->cb (ih->cb_cls, 565 TALER_EC_EXCHANGE_KYC_GENERIC_PROVIDER_UNEXPECTED_REPLY, 566 NULL, 567 NULL, 568 NULL, 569 json_string_value (json_object_get (j, 570 "type"))); 571 break; 572 default: 573 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 ih->cb (ih->cb_cls, 582 TALER_EC_EXCHANGE_KYC_GENERIC_PROVIDER_UNEXPECTED_REPLY, 583 NULL, 584 NULL, 585 NULL, 586 json_string_value (json_object_get (j, 587 "type"))); 588 break; 589 } 590 kycaid_initiate_cancel (ih); 591 } 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 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 struct PluginState *ps = cls; 616 struct TALER_KYCLOGIC_InitiateHandle *ih; 617 json_t *body; 618 CURL *eh; 619 620 (void) context; 621 eh = curl_easy_init (); 622 if (NULL == eh) 623 { 624 GNUNET_break (0); 625 return NULL; 626 } 627 ih = GNUNET_new (struct TALER_KYCLOGIC_InitiateHandle); 628 ih->legitimization_uuid = legitimization_uuid; 629 ih->cb = cb; 630 ih->cb_cls = cb_cls; 631 ih->h_payto = *account_id; 632 ih->pd = pd; 633 GNUNET_asprintf (&ih->url, 634 "https://api.kycaid.com/forms/%s/urls", 635 pd->form_id); 636 body = GNUNET_JSON_PACK ( 637 GNUNET_JSON_pack_data64_auto ("external_applicant_id", 638 account_id) 639 ); 640 GNUNET_break (CURLE_OK == 641 curl_easy_setopt (eh, 642 CURLOPT_VERBOSE, 643 0)); 644 GNUNET_assert (CURLE_OK == 645 curl_easy_setopt (eh, 646 CURLOPT_MAXREDIRS, 647 1L)); 648 GNUNET_break (CURLE_OK == 649 curl_easy_setopt (eh, 650 CURLOPT_URL, 651 ih->url)); 652 if (GNUNET_OK != 653 TALER_curl_easy_post (&ih->ctx, 654 eh, 655 body)) 656 { 657 GNUNET_break (0); 658 GNUNET_free (ih->url); 659 GNUNET_free (ih); 660 curl_easy_cleanup (eh); 661 json_decref (body); 662 return NULL; 663 } 664 json_decref (body); 665 ih->job = GNUNET_CURL_job_add2 (ps->curl_ctx, 666 eh, 667 ih->ctx.headers, 668 &handle_initiate_finished, 669 ih); 670 GNUNET_CURL_extend_headers (ih->job, 671 pd->slist); 672 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 kycaid_proof_cancel (struct TALER_KYCLOGIC_ProofHandle *ph) 683 { 684 if (NULL != ph->task) 685 { 686 GNUNET_SCHEDULER_cancel (ph->task); 687 ph->task = NULL; 688 } 689 GNUNET_free (ph); 690 } 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 proof_reply (void *cls) 700 { 701 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 http_status = MHD_HTTP_BAD_REQUEST; 708 body = GNUNET_JSON_PACK ( 709 TALER_JSON_pack_ec (TALER_EC_GENERIC_ENDPOINT_UNKNOWN)); 710 GNUNET_assert (NULL != body); 711 ret = TALER_TEMPLATING_build (ph->connection, 712 &http_status, 713 "kycaid-invalid-request", 714 NULL, 715 NULL, 716 body, 717 &resp); 718 json_decref (body); 719 if (GNUNET_SYSERR == ret) 720 { 721 resp = NULL; 722 GNUNET_break (0); 723 } 724 else 725 { 726 GNUNET_break (MHD_NO != 727 MHD_add_response_header (resp, 728 MHD_HTTP_HEADER_CONTENT_TYPE, 729 "text/html")); 730 } 731 ph->cb (ph->cb_cls, 732 TALER_KYCLOGIC_STATUS_PROVIDER_FAILED, 733 ph->pd->provider_name, 734 NULL, /* user id */ 735 NULL, /* provider legi ID */ 736 GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */ 737 NULL, /* attributes */ 738 http_status, 739 resp); 740 } 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 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 struct PluginState *ps = cls; 770 struct TALER_KYCLOGIC_ProofHandle *ph; 771 772 ph = GNUNET_new (struct TALER_KYCLOGIC_ProofHandle); 773 ph->ps = ps; 774 ph->pd = pd; 775 ph->cb = cb; 776 ph->cb_cls = cb_cls; 777 ph->connection = connection; 778 ph->task = GNUNET_SCHEDULER_add_now (&proof_reply, 779 ph); 780 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 kycaid_webhook_cancel (struct TALER_KYCLOGIC_WebhookHandle *wh) 791 { 792 if (NULL != wh->task) 793 { 794 GNUNET_SCHEDULER_cancel (wh->task); 795 wh->task = NULL; 796 } 797 if (NULL != wh->econ) 798 { 799 TALER_JSON_external_conversion_stop (wh->econ); 800 wh->econ = NULL; 801 } 802 if (NULL != wh->job) 803 { 804 GNUNET_CURL_job_cancel (wh->job); 805 wh->job = NULL; 806 } 807 if (NULL != wh->json_response) 808 { 809 json_decref (wh->json_response); 810 wh->json_response = NULL; 811 } 812 GNUNET_free (wh->verification_id); 813 GNUNET_free (wh->applicant_id); 814 GNUNET_free (wh->url); 815 GNUNET_free (wh); 816 } 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 log_failure (const json_t *verifications) 826 { 827 const json_t *member; 828 const char *name; 829 830 json_object_foreach ((json_t *) verifications, name, member) 831 { 832 bool iverified; 833 const char *comment; 834 struct GNUNET_JSON_Specification spec[] = { 835 GNUNET_JSON_spec_bool ("verified", 836 &iverified), 837 GNUNET_JSON_spec_string ("comment", 838 &comment), 839 GNUNET_JSON_spec_end () 840 }; 841 842 if (GNUNET_OK != 843 GNUNET_JSON_parse (member, 844 spec, 845 NULL, NULL)) 846 { 847 GNUNET_break_op (0); 848 #if DEBUG 849 json_dumpf (member, 850 stderr, 851 JSON_INDENT (2)); 852 #endif 853 continue; 854 } 855 if (iverified) 856 continue; 857 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 858 "KYC verification of attribute `%s' failed: %s\n", 859 name, 860 comment); 861 } 862 } 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 webhook_conversion_cb (void *cls, 875 enum GNUNET_OS_ProcessStatusType status_type, 876 unsigned long code, 877 const json_t *result) 878 { 879 struct TALER_KYCLOGIC_WebhookHandle *wh = cls; 880 struct GNUNET_TIME_Absolute expiration; 881 struct MHD_Response *resp; 882 883 wh->econ = NULL; 884 if ( (GNUNET_OS_PROCESS_EXITED == status_type) && 885 (0 == code) && 886 (NULL == result) ) 887 { 888 /* No result, but *our helper* was OK => bad input */ 889 GNUNET_break_op (0); 890 #if DEBUG 891 json_dumpf (wh->json_response, 892 stderr, 893 JSON_INDENT (2)); 894 #endif 895 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 wh->cb (wh->cb_cls, 901 wh->process_row, 902 &wh->h_payto, 903 wh->is_wallet, 904 wh->pd->provider_name, 905 wh->applicant_id, 906 wh->verification_id, 907 TALER_KYCLOGIC_STATUS_PROVIDER_FAILED, 908 GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */ 909 NULL, 910 MHD_HTTP_BAD_GATEWAY, 911 resp); 912 kycaid_webhook_cancel (wh); 913 return; 914 } 915 if ( (NULL == result) || 916 (GNUNET_OS_PROCESS_EXITED != status_type) || 917 (0 != code) ) 918 { 919 /* Failure in our helper */ 920 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 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 wh->cb (wh->cb_cls, 935 wh->process_row, 936 &wh->h_payto, 937 wh->is_wallet, 938 wh->pd->provider_name, 939 wh->applicant_id, 940 wh->verification_id, 941 TALER_KYCLOGIC_STATUS_PROVIDER_FAILED, 942 GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */ 943 NULL, 944 MHD_HTTP_BAD_GATEWAY, 945 resp); 946 kycaid_webhook_cancel (wh); 947 return; 948 } 949 if (! json_is_string (json_object_get (result, 950 "FORM_ID"))) 951 { 952 /* Failure in our helper */ 953 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 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 wh->cb (wh->cb_cls, 966 wh->process_row, 967 &wh->h_payto, 968 wh->is_wallet, 969 wh->pd->provider_name, 970 wh->applicant_id, 971 wh->verification_id, 972 TALER_KYCLOGIC_STATUS_PROVIDER_FAILED, 973 GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */ 974 NULL, 975 MHD_HTTP_BAD_GATEWAY, 976 resp); 977 kycaid_webhook_cancel (wh); 978 return; 979 } 980 981 expiration = GNUNET_TIME_relative_to_absolute (wh->pd->validity); 982 resp = MHD_create_response_from_buffer_static (0, 983 ""); 984 wh->cb (wh->cb_cls, 985 wh->process_row, 986 &wh->h_payto, 987 wh->is_wallet, 988 wh->pd->provider_name, 989 wh->applicant_id, 990 wh->verification_id, 991 TALER_KYCLOGIC_STATUS_SUCCESS, 992 expiration, 993 result, 994 MHD_HTTP_NO_CONTENT, 995 resp); 996 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 handle_webhook_finished (void *cls, 1010 long response_code, 1011 const void *response) 1012 { 1013 struct TALER_KYCLOGIC_WebhookHandle *wh = cls; 1014 const json_t *j = response; 1015 struct MHD_Response *resp; 1016 1017 wh->job = NULL; 1018 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1019 "Webhook returned with HTTP status %u\n", 1020 (unsigned int) response_code); 1021 wh->kycaid_response_code = response_code; 1022 wh->json_response = json_incref ((json_t *) j); 1023 switch (response_code) 1024 { 1025 case MHD_HTTP_OK: 1026 { 1027 const char *profile_status; 1028 1029 profile_status = json_string_value ( 1030 json_object_get ( 1031 j, 1032 "profile_status")); 1033 if (NULL == profile_status) 1034 { 1035 GNUNET_break_op (0); 1036 profile_status = "<invalid>"; 1037 } 1038 if (0 != strcasecmp ("valid", 1039 profile_status)) 1040 { 1041 enum TALER_KYCLOGIC_KycStatus ks; 1042 1043 ks = (0 == strcasecmp ("pending", 1044 profile_status)) 1045 ? TALER_KYCLOGIC_STATUS_PENDING 1046 : TALER_KYCLOGIC_STATUS_USER_ABORTED; 1047 resp = MHD_create_response_from_buffer_static (0, 1048 ""); 1049 wh->cb (wh->cb_cls, 1050 wh->process_row, 1051 &wh->h_payto, 1052 wh->is_wallet, 1053 wh->pd->provider_name, 1054 wh->applicant_id, 1055 wh->verification_id, 1056 ks, 1057 GNUNET_TIME_UNIT_ZERO_ABS, 1058 NULL, 1059 MHD_HTTP_NO_CONTENT, 1060 resp); 1061 break; 1062 } 1063 { 1064 const char *argv[] = { 1065 wh->pd->conversion_helper, 1066 "-a", 1067 wh->pd->auth_token, 1068 NULL, 1069 }; 1070 1071 wh->econ 1072 = TALER_JSON_external_conversion_start ( 1073 j, 1074 &webhook_conversion_cb, 1075 wh, 1076 wh->pd->conversion_helper, 1077 argv); 1078 } 1079 if (NULL == wh->econ) 1080 { 1081 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1082 "Failed to start KYCAID conversion helper `%s'\n", 1083 wh->pd->conversion_helper); 1084 resp = TALER_MHD_make_error ( 1085 TALER_EC_EXCHANGE_GENERIC_KYC_CONVERTER_FAILED, 1086 NULL); 1087 wh->cb (wh->cb_cls, 1088 wh->process_row, 1089 &wh->h_payto, 1090 wh->is_wallet, 1091 wh->pd->provider_name, 1092 wh->applicant_id, 1093 wh->verification_id, 1094 TALER_KYCLOGIC_STATUS_INTERNAL_ERROR, 1095 GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */ 1096 NULL, 1097 MHD_HTTP_INTERNAL_SERVER_ERROR, 1098 resp); 1099 break; 1100 } 1101 return; 1102 } 1103 break; 1104 case MHD_HTTP_BAD_REQUEST: 1105 case MHD_HTTP_NOT_FOUND: 1106 case MHD_HTTP_CONFLICT: 1107 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 resp = TALER_MHD_MAKE_JSON_PACK ( 1116 GNUNET_JSON_pack_uint64 ("kycaid_http_status", 1117 response_code)); 1118 wh->cb (wh->cb_cls, 1119 wh->process_row, 1120 &wh->h_payto, 1121 wh->is_wallet, 1122 wh->pd->provider_name, 1123 wh->applicant_id, 1124 wh->verification_id, 1125 TALER_KYCLOGIC_STATUS_PROVIDER_FAILED, 1126 GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */ 1127 NULL, 1128 MHD_HTTP_INTERNAL_SERVER_ERROR, 1129 resp); 1130 break; 1131 case MHD_HTTP_UNAUTHORIZED: 1132 case MHD_HTTP_PAYMENT_REQUIRED: 1133 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1134 "Refused access with HTTP status code %u\n", 1135 (unsigned int) response_code); 1136 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 wh->cb (wh->cb_cls, 1142 wh->process_row, 1143 &wh->h_payto, 1144 wh->is_wallet, 1145 wh->pd->provider_name, 1146 wh->applicant_id, 1147 wh->verification_id, 1148 TALER_KYCLOGIC_STATUS_PROVIDER_FAILED, 1149 GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */ 1150 NULL, 1151 MHD_HTTP_NETWORK_AUTHENTICATION_REQUIRED, 1152 resp); 1153 break; 1154 case MHD_HTTP_REQUEST_TIMEOUT: 1155 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 wh->cb (wh->cb_cls, 1161 wh->process_row, 1162 &wh->h_payto, 1163 wh->is_wallet, 1164 wh->pd->provider_name, 1165 wh->applicant_id, 1166 wh->verification_id, 1167 TALER_KYCLOGIC_STATUS_PROVIDER_FAILED, 1168 GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */ 1169 NULL, 1170 MHD_HTTP_GATEWAY_TIMEOUT, 1171 resp); 1172 break; 1173 case MHD_HTTP_UNPROCESSABLE_CONTENT: /* validation */ 1174 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 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 wh->cb (wh->cb_cls, 1188 wh->process_row, 1189 &wh->h_payto, 1190 wh->is_wallet, 1191 wh->pd->provider_name, 1192 wh->applicant_id, 1193 wh->verification_id, 1194 TALER_KYCLOGIC_STATUS_PROVIDER_FAILED, 1195 GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */ 1196 NULL, 1197 MHD_HTTP_BAD_GATEWAY, 1198 resp); 1199 break; 1200 case MHD_HTTP_TOO_MANY_REQUESTS: 1201 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 wh->cb (wh->cb_cls, 1207 wh->process_row, 1208 &wh->h_payto, 1209 wh->is_wallet, 1210 wh->pd->provider_name, 1211 wh->applicant_id, 1212 wh->verification_id, 1213 TALER_KYCLOGIC_STATUS_PROVIDER_FAILED, 1214 GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */ 1215 NULL, 1216 MHD_HTTP_SERVICE_UNAVAILABLE, 1217 resp); 1218 break; 1219 case MHD_HTTP_INTERNAL_SERVER_ERROR: 1220 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 wh->cb (wh->cb_cls, 1226 wh->process_row, 1227 &wh->h_payto, 1228 wh->is_wallet, 1229 wh->pd->provider_name, 1230 wh->applicant_id, 1231 wh->verification_id, 1232 TALER_KYCLOGIC_STATUS_PROVIDER_FAILED, 1233 GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */ 1234 NULL, 1235 MHD_HTTP_BAD_GATEWAY, 1236 resp); 1237 break; 1238 default: 1239 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 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 wh->cb (wh->cb_cls, 1253 wh->process_row, 1254 &wh->h_payto, 1255 wh->is_wallet, 1256 wh->pd->provider_name, 1257 wh->applicant_id, 1258 wh->verification_id, 1259 TALER_KYCLOGIC_STATUS_PROVIDER_FAILED, 1260 GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */ 1261 NULL, 1262 MHD_HTTP_BAD_GATEWAY, 1263 resp); 1264 break; 1265 } 1266 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 async_webhook_reply (void *cls) 1277 { 1278 struct TALER_KYCLOGIC_WebhookHandle *wh = cls; 1279 1280 wh->task = NULL; 1281 wh->cb (wh->cb_cls, 1282 wh->process_row, 1283 (0 == wh->process_row) 1284 ? NULL 1285 : &wh->h_payto, 1286 wh->is_wallet, 1287 wh->pd->provider_name, 1288 wh->applicant_id, /* provider user ID */ 1289 wh->verification_id, /* provider legi ID */ 1290 TALER_KYCLOGIC_STATUS_PROVIDER_FAILED, 1291 GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */ 1292 NULL, 1293 wh->response_code, 1294 wh->resp); 1295 kycaid_webhook_cancel (wh); 1296 } 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 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 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 const char *status = NULL; 1339 bool verified = false; 1340 bool no_verified = true; 1341 const json_t *verifications = NULL; 1342 struct GNUNET_JSON_Specification spec[] = { 1343 GNUNET_JSON_spec_string ("request_id", 1344 &request_id), 1345 GNUNET_JSON_spec_string ("type", 1346 &type), 1347 GNUNET_JSON_spec_string ("verification_id", 1348 &verification_id), 1349 GNUNET_JSON_spec_string ("applicant_id", 1350 &applicant_id), 1351 GNUNET_JSON_spec_string ("form_id", 1352 &form_id), 1353 GNUNET_JSON_spec_mark_optional ( 1354 GNUNET_JSON_spec_string ("status", 1355 &status), 1356 NULL), 1357 GNUNET_JSON_spec_mark_optional ( 1358 GNUNET_JSON_spec_bool ("verified", 1359 &verified), 1360 &no_verified), 1361 GNUNET_JSON_spec_mark_optional ( 1362 GNUNET_JSON_spec_object_const ("verifications", 1363 &verifications), 1364 NULL), 1365 GNUNET_JSON_spec_end () 1366 }; 1367 enum GNUNET_DB_QueryStatus qs; 1368 1369 wh = GNUNET_new (struct TALER_KYCLOGIC_WebhookHandle); 1370 wh->cb = cb; 1371 wh->cb_cls = cb_cls; 1372 wh->ps = ps; 1373 wh->pd = pd; 1374 wh->connection = connection; 1375 #if DEBUG 1376 if (NULL != body) 1377 json_dumpf (body, 1378 stderr, 1379 JSON_INDENT (2)); 1380 #endif 1381 if (NULL == pd) 1382 { 1383 GNUNET_break_op (0); 1384 #if DEBUG 1385 json_dumpf (body, 1386 stderr, 1387 JSON_INDENT (2)); 1388 #endif 1389 wh->resp = TALER_MHD_make_error ( 1390 TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_UNKNOWN, 1391 "kycaid"); 1392 wh->response_code = MHD_HTTP_NOT_FOUND; 1393 wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply, 1394 wh); 1395 return wh; 1396 } 1397 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1398 "KYCAID webhook of `%s' triggered with %s\n", 1399 pd->section, 1400 http_method); 1401 if (GNUNET_OK != 1402 GNUNET_JSON_parse (body, 1403 spec, 1404 NULL, NULL)) 1405 { 1406 GNUNET_break_op (0); 1407 #if DEBUG 1408 json_dumpf (body, 1409 stderr, 1410 JSON_INDENT (2)); 1411 #endif 1412 wh->resp = TALER_MHD_MAKE_JSON_PACK ( 1413 GNUNET_JSON_pack_object_incref ("webhook_body", 1414 (json_t *) body)); 1415 wh->response_code = MHD_HTTP_BAD_REQUEST; 1416 wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply, 1417 wh); 1418 return wh; 1419 } 1420 qs = plc (plc_cls, 1421 pd->provider_name, 1422 verification_id, 1423 &wh->h_payto, 1424 &wh->is_wallet, 1425 &wh->process_row); 1426 if (qs < 0) 1427 { 1428 wh->resp = TALER_MHD_make_error (TALER_EC_GENERIC_DB_FETCH_FAILED, 1429 "provider-legitimization-lookup"); 1430 wh->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; 1431 wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply, 1432 wh); 1433 return wh; 1434 } 1435 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 1436 { 1437 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 wh->resp = TALER_MHD_make_error ( 1442 TALER_EC_EXCHANGE_KYC_PROOF_REQUEST_UNKNOWN, 1443 verification_id); 1444 wh->response_code = MHD_HTTP_NOT_FOUND; 1445 wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply, 1446 wh); 1447 return wh; 1448 } 1449 wh->verification_id = GNUNET_strdup (verification_id); 1450 wh->applicant_id = GNUNET_strdup (applicant_id); 1451 if ( (0 != strcasecmp (type, 1452 "VERIFICATION_COMPLETED")) || 1453 (no_verified) || 1454 (! verified) ) 1455 { 1456 /* We don't need to re-confirm the failure by 1457 asking the API again. */ 1458 log_failure (verifications); 1459 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1460 "Webhook called with non-completion status: %s\n", 1461 type); 1462 wh->response_code = MHD_HTTP_NO_CONTENT; 1463 wh->resp = MHD_create_response_from_buffer_static (0, 1464 ""); 1465 wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply, 1466 wh); 1467 return wh; 1468 } 1469 1470 eh = curl_easy_init (); 1471 if (NULL == eh) 1472 { 1473 GNUNET_break (0); 1474 wh->resp = TALER_MHD_make_error ( 1475 TALER_EC_GENERIC_ALLOCATION_FAILURE, 1476 NULL); 1477 wh->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; 1478 wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply, 1479 wh); 1480 return wh; 1481 } 1482 1483 { 1484 char *applicant_id_encoded; 1485 1486 applicant_id_encoded = TALER_urlencode (applicant_id); 1487 GNUNET_asprintf (&wh->url, 1488 "https://api.kycaid.com/applicants/%s", 1489 applicant_id_encoded); 1490 GNUNET_free (applicant_id_encoded); 1491 } 1492 GNUNET_break (CURLE_OK == 1493 curl_easy_setopt (eh, 1494 CURLOPT_VERBOSE, 1495 0)); 1496 GNUNET_assert (CURLE_OK == 1497 curl_easy_setopt (eh, 1498 CURLOPT_MAXREDIRS, 1499 1L)); 1500 GNUNET_break (CURLE_OK == 1501 curl_easy_setopt (eh, 1502 CURLOPT_URL, 1503 wh->url)); 1504 wh->job = GNUNET_CURL_job_add2 (ps->curl_ctx, 1505 eh, 1506 pd->slist, 1507 &handle_webhook_finished, 1508 wh); 1509 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 libtaler_plugin_kyclogic_kycaid_init (void *cls) 1525 { 1526 const struct GNUNET_CONFIGURATION_Handle *cfg = cls; 1527 struct TALER_KYCLOGIC_Plugin *plugin; 1528 struct PluginState *ps; 1529 1530 ps = GNUNET_new (struct PluginState); 1531 ps->cfg = cfg; 1532 if (GNUNET_OK != 1533 GNUNET_CONFIGURATION_get_value_string (cfg, 1534 "exchange", 1535 "BASE_URL", 1536 &ps->exchange_base_url)) 1537 { 1538 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 1539 "exchange", 1540 "BASE_URL"); 1541 GNUNET_free (ps); 1542 return NULL; 1543 } 1544 1545 ps->curl_ctx 1546 = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, 1547 &ps->curl_rc); 1548 if (NULL == ps->curl_ctx) 1549 { 1550 GNUNET_break (0); 1551 GNUNET_free (ps->exchange_base_url); 1552 GNUNET_free (ps); 1553 return NULL; 1554 } 1555 ps->curl_rc = GNUNET_CURL_gnunet_rc_create (ps->curl_ctx); 1556 1557 plugin = GNUNET_new (struct TALER_KYCLOGIC_Plugin); 1558 plugin->cls = ps; 1559 plugin->load_configuration 1560 = &kycaid_load_configuration; 1561 plugin->unload_configuration 1562 = &kycaid_unload_configuration; 1563 plugin->initiate 1564 = &kycaid_initiate; 1565 plugin->initiate_cancel 1566 = &kycaid_initiate_cancel; 1567 plugin->proof 1568 = &kycaid_proof; 1569 plugin->proof_cancel 1570 = &kycaid_proof_cancel; 1571 plugin->webhook 1572 = &kycaid_webhook; 1573 plugin->webhook_cancel 1574 = &kycaid_webhook_cancel; 1575 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 libtaler_plugin_kyclogic_kycaid_done (void *cls) 1591 { 1592 struct TALER_KYCLOGIC_Plugin *plugin = cls; 1593 struct PluginState *ps = plugin->cls; 1594 1595 if (NULL != ps->curl_ctx) 1596 { 1597 GNUNET_CURL_fini (ps->curl_ctx); 1598 ps->curl_ctx = NULL; 1599 } 1600 if (NULL != ps->curl_rc) 1601 { 1602 GNUNET_CURL_gnunet_rc_destroy (ps->curl_rc); 1603 ps->curl_rc = NULL; 1604 } 1605 GNUNET_free (ps->exchange_base_url); 1606 GNUNET_free (ps); 1607 GNUNET_free (plugin); 1608 return NULL; 1609 }