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