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