plugin_kyclogic_kycaid.c (44251B)
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 if (GNUNET_SYSERR == ret) 698 { 699 resp = NULL; 700 GNUNET_break (0); 701 } 702 else 703 { 704 GNUNET_break (MHD_NO != 705 MHD_add_response_header (resp, 706 MHD_HTTP_HEADER_CONTENT_TYPE, 707 "text/html")); 708 } 709 ph->cb (ph->cb_cls, 710 TALER_KYCLOGIC_STATUS_PROVIDER_FAILED, 711 ph->pd->section, 712 NULL, /* user id */ 713 NULL, /* provider legi ID */ 714 GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */ 715 NULL, /* attributes */ 716 http_status, 717 resp); 718 } 719 720 721 /** 722 * Check KYC status and return status to human. Not 723 * used by KYC AID! 724 * 725 * @param cls the @e cls of this struct with the plugin-specific state 726 * @param pd provider configuration details 727 * @param connection MHD connection object (for HTTP headers) 728 * @param account_id which account to trigger process for 729 * @param process_row row in the legitimization processes table the legitimization is for 730 * @param provider_user_id user ID (or NULL) the proof is for 731 * @param provider_legitimization_id legitimization ID the proof is for 732 * @param cb function to call with the result 733 * @param cb_cls closure for @a cb 734 * @return handle to cancel operation early 735 */ 736 static struct TALER_KYCLOGIC_ProofHandle * 737 kycaid_proof (void *cls, 738 const struct TALER_KYCLOGIC_ProviderDetails *pd, 739 struct MHD_Connection *connection, 740 const struct TALER_NormalizedPaytoHashP *account_id, 741 uint64_t process_row, 742 const char *provider_user_id, 743 const char *provider_legitimization_id, 744 TALER_KYCLOGIC_ProofCallback cb, 745 void *cb_cls) 746 { 747 struct PluginState *ps = cls; 748 struct TALER_KYCLOGIC_ProofHandle *ph; 749 750 ph = GNUNET_new (struct TALER_KYCLOGIC_ProofHandle); 751 ph->ps = ps; 752 ph->pd = pd; 753 ph->cb = cb; 754 ph->cb_cls = cb_cls; 755 ph->connection = connection; 756 ph->task = GNUNET_SCHEDULER_add_now (&proof_reply, 757 ph); 758 return ph; 759 } 760 761 762 /** 763 * Cancel KYC webhook execution. 764 * 765 * @param[in] wh handle of operation to cancel 766 */ 767 static void 768 kycaid_webhook_cancel (struct TALER_KYCLOGIC_WebhookHandle *wh) 769 { 770 if (NULL != wh->task) 771 { 772 GNUNET_SCHEDULER_cancel (wh->task); 773 wh->task = NULL; 774 } 775 if (NULL != wh->econ) 776 { 777 TALER_JSON_external_conversion_stop (wh->econ); 778 wh->econ = NULL; 779 } 780 if (NULL != wh->job) 781 { 782 GNUNET_CURL_job_cancel (wh->job); 783 wh->job = NULL; 784 } 785 if (NULL != wh->json_response) 786 { 787 json_decref (wh->json_response); 788 wh->json_response = NULL; 789 } 790 GNUNET_free (wh->verification_id); 791 GNUNET_free (wh->applicant_id); 792 GNUNET_free (wh->url); 793 GNUNET_free (wh); 794 } 795 796 797 /** 798 * Extract KYC failure reasons and log those 799 * 800 * @param verifications JSON object with failure details 801 */ 802 static void 803 log_failure (const json_t *verifications) 804 { 805 const json_t *member; 806 const char *name; 807 808 json_object_foreach ((json_t *) verifications, name, member) 809 { 810 bool iverified; 811 const char *comment; 812 struct GNUNET_JSON_Specification spec[] = { 813 GNUNET_JSON_spec_bool ("verified", 814 &iverified), 815 GNUNET_JSON_spec_string ("comment", 816 &comment), 817 GNUNET_JSON_spec_end () 818 }; 819 820 if (GNUNET_OK != 821 GNUNET_JSON_parse (member, 822 spec, 823 NULL, NULL)) 824 { 825 GNUNET_break_op (0); 826 json_dumpf (member, 827 stderr, 828 JSON_INDENT (2)); 829 continue; 830 } 831 if (iverified) 832 continue; 833 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 834 "KYC verification of attribute `%s' failed: %s\n", 835 name, 836 comment); 837 } 838 } 839 840 841 /** 842 * Type of a callback that receives a JSON @a result. 843 * 844 * @param cls closure our `struct TALER_KYCLOGIC_WebhookHandle *` 845 * @param status_type how did the process die 846 * @param code termination status code from the process 847 * @param result converted attribute data, NULL on failure 848 */ 849 static void 850 webhook_conversion_cb (void *cls, 851 enum GNUNET_OS_ProcessStatusType status_type, 852 unsigned long code, 853 const json_t *result) 854 { 855 struct TALER_KYCLOGIC_WebhookHandle *wh = cls; 856 struct GNUNET_TIME_Absolute expiration; 857 struct MHD_Response *resp; 858 859 wh->econ = NULL; 860 if ( (0 == code) && 861 (NULL == result) ) 862 { 863 /* No result, but *our helper* was OK => bad input */ 864 GNUNET_break_op (0); 865 json_dumpf (wh->json_response, 866 stderr, 867 JSON_INDENT (2)); 868 resp = TALER_MHD_MAKE_JSON_PACK ( 869 GNUNET_JSON_pack_uint64 ("kycaid_http_status", 870 wh->kycaid_response_code), 871 GNUNET_JSON_pack_object_incref ("kycaid_body", 872 (json_t *) wh->json_response)); 873 wh->cb (wh->cb_cls, 874 wh->process_row, 875 &wh->h_payto, 876 wh->is_wallet, 877 wh->pd->section, 878 wh->applicant_id, 879 wh->verification_id, 880 TALER_KYCLOGIC_STATUS_PROVIDER_FAILED, 881 GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */ 882 NULL, 883 MHD_HTTP_BAD_GATEWAY, 884 resp); 885 kycaid_webhook_cancel (wh); 886 return; 887 } 888 if (NULL == result) 889 { 890 /* Failure in our helper */ 891 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 892 "Helper exited with status code %d\n", 893 (int) code); 894 json_dumpf (wh->json_response, 895 stderr, 896 JSON_INDENT (2)); 897 resp = TALER_MHD_MAKE_JSON_PACK ( 898 GNUNET_JSON_pack_uint64 ("kycaid_http_status", 899 wh->kycaid_response_code), 900 GNUNET_JSON_pack_object_incref ("kycaid_body", 901 (json_t *) wh->json_response)); 902 wh->cb (wh->cb_cls, 903 wh->process_row, 904 &wh->h_payto, 905 wh->is_wallet, 906 wh->pd->section, 907 wh->applicant_id, 908 wh->verification_id, 909 TALER_KYCLOGIC_STATUS_PROVIDER_FAILED, 910 GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */ 911 NULL, 912 MHD_HTTP_BAD_GATEWAY, 913 resp); 914 kycaid_webhook_cancel (wh); 915 return; 916 } 917 if (! json_is_string (json_object_get (result, 918 "FORM_ID"))) 919 { 920 /* Failure in our helper */ 921 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 922 "Mandatory FORM_ID not set in result\n"); 923 json_dumpf (result, 924 stderr, 925 JSON_INDENT (2)); 926 resp = TALER_MHD_MAKE_JSON_PACK ( 927 GNUNET_JSON_pack_uint64 ("kycaid_http_status", 928 wh->kycaid_response_code), 929 GNUNET_JSON_pack_object_incref ("kycaid_body", 930 (json_t *) wh->json_response)); 931 wh->cb (wh->cb_cls, 932 wh->process_row, 933 &wh->h_payto, 934 wh->is_wallet, 935 wh->pd->section, 936 wh->applicant_id, 937 wh->verification_id, 938 TALER_KYCLOGIC_STATUS_PROVIDER_FAILED, 939 GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */ 940 NULL, 941 MHD_HTTP_BAD_GATEWAY, 942 resp); 943 kycaid_webhook_cancel (wh); 944 return; 945 } 946 947 expiration = GNUNET_TIME_relative_to_absolute (wh->pd->validity); 948 resp = MHD_create_response_from_buffer_static (0, 949 ""); 950 wh->cb (wh->cb_cls, 951 wh->process_row, 952 &wh->h_payto, 953 wh->is_wallet, 954 wh->pd->section, 955 wh->applicant_id, 956 wh->verification_id, 957 TALER_KYCLOGIC_STATUS_SUCCESS, 958 expiration, 959 result, 960 MHD_HTTP_NO_CONTENT, 961 resp); 962 kycaid_webhook_cancel (wh); 963 } 964 965 966 /** 967 * Function called when we're done processing the 968 * HTTP "/applicants/{verification_id}" request. 969 * 970 * @param cls the `struct TALER_KYCLOGIC_WebhookHandle` 971 * @param response_code HTTP response code, 0 on error 972 * @param response parsed JSON result, NULL on error 973 */ 974 static void 975 handle_webhook_finished (void *cls, 976 long response_code, 977 const void *response) 978 { 979 struct TALER_KYCLOGIC_WebhookHandle *wh = cls; 980 const json_t *j = response; 981 struct MHD_Response *resp; 982 983 wh->job = NULL; 984 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 985 "Webhook returned with HTTP status %u\n", 986 (unsigned int) response_code); 987 wh->kycaid_response_code = response_code; 988 wh->json_response = json_incref ((json_t *) j); 989 switch (response_code) 990 { 991 case MHD_HTTP_OK: 992 { 993 const char *profile_status; 994 995 profile_status = json_string_value ( 996 json_object_get ( 997 j, 998 "profile_status")); 999 if (0 != strcasecmp ("valid", 1000 profile_status)) 1001 { 1002 enum TALER_KYCLOGIC_KycStatus ks; 1003 1004 ks = (0 == strcasecmp ("pending", 1005 profile_status)) 1006 ? TALER_KYCLOGIC_STATUS_PENDING 1007 : TALER_KYCLOGIC_STATUS_USER_ABORTED; 1008 resp = MHD_create_response_from_buffer_static (0, 1009 ""); 1010 wh->cb (wh->cb_cls, 1011 wh->process_row, 1012 &wh->h_payto, 1013 wh->is_wallet, 1014 wh->pd->section, 1015 wh->applicant_id, 1016 wh->verification_id, 1017 ks, 1018 GNUNET_TIME_UNIT_ZERO_ABS, 1019 NULL, 1020 MHD_HTTP_NO_CONTENT, 1021 resp); 1022 break; 1023 } 1024 { 1025 const char *argv[] = { 1026 wh->pd->conversion_helper, 1027 "-a", 1028 wh->pd->auth_token, 1029 NULL, 1030 }; 1031 1032 wh->econ 1033 = TALER_JSON_external_conversion_start ( 1034 j, 1035 &webhook_conversion_cb, 1036 wh, 1037 wh->pd->conversion_helper, 1038 argv); 1039 } 1040 if (NULL == wh->econ) 1041 { 1042 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1043 "Failed to start KYCAID conversion helper `%s'\n", 1044 wh->pd->conversion_helper); 1045 resp = TALER_MHD_make_error ( 1046 TALER_EC_EXCHANGE_GENERIC_KYC_CONVERTER_FAILED, 1047 NULL); 1048 wh->cb (wh->cb_cls, 1049 wh->process_row, 1050 &wh->h_payto, 1051 wh->is_wallet, 1052 wh->pd->section, 1053 wh->applicant_id, 1054 wh->verification_id, 1055 TALER_KYCLOGIC_STATUS_INTERNAL_ERROR, 1056 GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */ 1057 NULL, 1058 MHD_HTTP_INTERNAL_SERVER_ERROR, 1059 resp); 1060 break; 1061 } 1062 return; 1063 } 1064 break; 1065 case MHD_HTTP_BAD_REQUEST: 1066 case MHD_HTTP_NOT_FOUND: 1067 case MHD_HTTP_CONFLICT: 1068 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1069 "KYCAID failed with response %u:\n", 1070 (unsigned int) response_code); 1071 json_dumpf (j, 1072 stderr, 1073 JSON_INDENT (2)); 1074 resp = TALER_MHD_MAKE_JSON_PACK ( 1075 GNUNET_JSON_pack_uint64 ("kycaid_http_status", 1076 response_code)); 1077 wh->cb (wh->cb_cls, 1078 wh->process_row, 1079 &wh->h_payto, 1080 wh->is_wallet, 1081 wh->pd->section, 1082 wh->applicant_id, 1083 wh->verification_id, 1084 TALER_KYCLOGIC_STATUS_PROVIDER_FAILED, 1085 GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */ 1086 NULL, 1087 MHD_HTTP_INTERNAL_SERVER_ERROR, 1088 resp); 1089 break; 1090 case MHD_HTTP_UNAUTHORIZED: 1091 case MHD_HTTP_PAYMENT_REQUIRED: 1092 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1093 "Refused access with HTTP status code %u\n", 1094 (unsigned int) response_code); 1095 resp = TALER_MHD_MAKE_JSON_PACK ( 1096 GNUNET_JSON_pack_uint64 ("kycaid_http_status", 1097 response_code), 1098 GNUNET_JSON_pack_object_incref ("kycaid_body", 1099 (json_t *) j)); 1100 wh->cb (wh->cb_cls, 1101 wh->process_row, 1102 &wh->h_payto, 1103 wh->is_wallet, 1104 wh->pd->section, 1105 wh->applicant_id, 1106 wh->verification_id, 1107 TALER_KYCLOGIC_STATUS_PROVIDER_FAILED, 1108 GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */ 1109 NULL, 1110 MHD_HTTP_NETWORK_AUTHENTICATION_REQUIRED, 1111 resp); 1112 break; 1113 case MHD_HTTP_REQUEST_TIMEOUT: 1114 resp = TALER_MHD_MAKE_JSON_PACK ( 1115 GNUNET_JSON_pack_uint64 ("kycaid_http_status", 1116 response_code), 1117 GNUNET_JSON_pack_object_incref ("kycaid_body", 1118 (json_t *) j)); 1119 wh->cb (wh->cb_cls, 1120 wh->process_row, 1121 &wh->h_payto, 1122 wh->is_wallet, 1123 wh->pd->section, 1124 wh->applicant_id, 1125 wh->verification_id, 1126 TALER_KYCLOGIC_STATUS_PROVIDER_FAILED, 1127 GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */ 1128 NULL, 1129 MHD_HTTP_GATEWAY_TIMEOUT, 1130 resp); 1131 break; 1132 case MHD_HTTP_UNPROCESSABLE_ENTITY: /* validation */ 1133 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1134 "KYCAID failed with response %u:\n", 1135 (unsigned int) response_code); 1136 json_dumpf (j, 1137 stderr, 1138 JSON_INDENT (2)); 1139 resp = TALER_MHD_MAKE_JSON_PACK ( 1140 GNUNET_JSON_pack_uint64 ("kycaid_http_status", 1141 response_code), 1142 GNUNET_JSON_pack_object_incref ("kycaid_body", 1143 (json_t *) j)); 1144 wh->cb (wh->cb_cls, 1145 wh->process_row, 1146 &wh->h_payto, 1147 wh->is_wallet, 1148 wh->pd->section, 1149 wh->applicant_id, 1150 wh->verification_id, 1151 TALER_KYCLOGIC_STATUS_PROVIDER_FAILED, 1152 GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */ 1153 NULL, 1154 MHD_HTTP_BAD_GATEWAY, 1155 resp); 1156 break; 1157 case MHD_HTTP_TOO_MANY_REQUESTS: 1158 resp = TALER_MHD_MAKE_JSON_PACK ( 1159 GNUNET_JSON_pack_uint64 ("kycaid_http_status", 1160 response_code), 1161 GNUNET_JSON_pack_object_incref ("kycaid_body", 1162 (json_t *) j)); 1163 wh->cb (wh->cb_cls, 1164 wh->process_row, 1165 &wh->h_payto, 1166 wh->is_wallet, 1167 wh->pd->section, 1168 wh->applicant_id, 1169 wh->verification_id, 1170 TALER_KYCLOGIC_STATUS_PROVIDER_FAILED, 1171 GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */ 1172 NULL, 1173 MHD_HTTP_SERVICE_UNAVAILABLE, 1174 resp); 1175 break; 1176 case MHD_HTTP_INTERNAL_SERVER_ERROR: 1177 resp = TALER_MHD_MAKE_JSON_PACK ( 1178 GNUNET_JSON_pack_uint64 ("kycaid_http_status", 1179 response_code), 1180 GNUNET_JSON_pack_object_incref ("kycaid_body", 1181 (json_t *) j)); 1182 wh->cb (wh->cb_cls, 1183 wh->process_row, 1184 &wh->h_payto, 1185 wh->is_wallet, 1186 wh->pd->section, 1187 wh->applicant_id, 1188 wh->verification_id, 1189 TALER_KYCLOGIC_STATUS_PROVIDER_FAILED, 1190 GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */ 1191 NULL, 1192 MHD_HTTP_BAD_GATEWAY, 1193 resp); 1194 break; 1195 default: 1196 resp = TALER_MHD_MAKE_JSON_PACK ( 1197 GNUNET_JSON_pack_uint64 ("kycaid_http_status", 1198 response_code), 1199 GNUNET_JSON_pack_object_incref ("kycaid_body", 1200 (json_t *) j)); 1201 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1202 "Unexpected KYCAID response %u:\n", 1203 (unsigned int) response_code); 1204 json_dumpf (j, 1205 stderr, 1206 JSON_INDENT (2)); 1207 wh->cb (wh->cb_cls, 1208 wh->process_row, 1209 &wh->h_payto, 1210 wh->is_wallet, 1211 wh->pd->section, 1212 wh->applicant_id, 1213 wh->verification_id, 1214 TALER_KYCLOGIC_STATUS_PROVIDER_FAILED, 1215 GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */ 1216 NULL, 1217 MHD_HTTP_BAD_GATEWAY, 1218 resp); 1219 break; 1220 } 1221 kycaid_webhook_cancel (wh); 1222 } 1223 1224 1225 /** 1226 * Asynchronously return a reply for the webhook. 1227 * 1228 * @param cls a `struct TALER_KYCLOGIC_WebhookHandle *` 1229 */ 1230 static void 1231 async_webhook_reply (void *cls) 1232 { 1233 struct TALER_KYCLOGIC_WebhookHandle *wh = cls; 1234 1235 wh->task = NULL; 1236 wh->cb (wh->cb_cls, 1237 wh->process_row, 1238 (0 == wh->process_row) 1239 ? NULL 1240 : &wh->h_payto, 1241 wh->is_wallet, 1242 wh->pd->section, 1243 wh->applicant_id, /* provider user ID */ 1244 wh->verification_id, /* provider legi ID */ 1245 TALER_KYCLOGIC_STATUS_PROVIDER_FAILED, 1246 GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */ 1247 NULL, 1248 wh->response_code, 1249 wh->resp); 1250 kycaid_webhook_cancel (wh); 1251 } 1252 1253 1254 /** 1255 * Check KYC status and return result for Webhook. We do NOT implement the 1256 * authentication check proposed by the KYCAID documentation, as it would 1257 * allow an attacker who learns the access token to easily bypass the KYC 1258 * checks. Instead, we insist on explicitly requesting the KYC status from the 1259 * provider (at least on success). 1260 * 1261 * @param cls the @e cls of this struct with the plugin-specific state 1262 * @param pd provider configuration details 1263 * @param plc callback to lookup accounts with 1264 * @param plc_cls closure for @a plc 1265 * @param http_method HTTP method used for the webhook 1266 * @param url_path rest of the URL after `/kyc-webhook/` 1267 * @param connection MHD connection object (for HTTP headers) 1268 * @param body HTTP request body 1269 * @param cb function to call with the result 1270 * @param cb_cls closure for @a cb 1271 * @return handle to cancel operation early 1272 */ 1273 static struct TALER_KYCLOGIC_WebhookHandle * 1274 kycaid_webhook (void *cls, 1275 const struct TALER_KYCLOGIC_ProviderDetails *pd, 1276 TALER_KYCLOGIC_ProviderLookupCallback plc, 1277 void *plc_cls, 1278 const char *http_method, 1279 const char *const url_path[], 1280 struct MHD_Connection *connection, 1281 const json_t *body, 1282 TALER_KYCLOGIC_WebhookCallback cb, 1283 void *cb_cls) 1284 { 1285 struct PluginState *ps = cls; 1286 struct TALER_KYCLOGIC_WebhookHandle *wh; 1287 CURL *eh; 1288 const char *request_id; 1289 const char *type; 1290 const char *verification_id; /* = provider_legitimization_id */ 1291 const char *applicant_id; 1292 const char *form_id; 1293 const char *status = NULL; 1294 bool verified = false; 1295 bool no_verified = true; 1296 const json_t *verifications = NULL; 1297 struct GNUNET_JSON_Specification spec[] = { 1298 GNUNET_JSON_spec_string ("request_id", 1299 &request_id), 1300 GNUNET_JSON_spec_string ("type", 1301 &type), 1302 GNUNET_JSON_spec_string ("verification_id", 1303 &verification_id), 1304 GNUNET_JSON_spec_string ("applicant_id", 1305 &applicant_id), 1306 GNUNET_JSON_spec_string ("form_id", 1307 &form_id), 1308 GNUNET_JSON_spec_mark_optional ( 1309 GNUNET_JSON_spec_string ("status", 1310 &status), 1311 NULL), 1312 GNUNET_JSON_spec_mark_optional ( 1313 GNUNET_JSON_spec_bool ("verified", 1314 &verified), 1315 &no_verified), 1316 GNUNET_JSON_spec_mark_optional ( 1317 GNUNET_JSON_spec_object_const ("verifications", 1318 &verifications), 1319 NULL), 1320 GNUNET_JSON_spec_end () 1321 }; 1322 enum GNUNET_DB_QueryStatus qs; 1323 1324 wh = GNUNET_new (struct TALER_KYCLOGIC_WebhookHandle); 1325 wh->cb = cb; 1326 wh->cb_cls = cb_cls; 1327 wh->ps = ps; 1328 wh->pd = pd; 1329 wh->connection = connection; 1330 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1331 "KYCAID webhook of `%s' triggered with %s\n", 1332 pd->section, 1333 http_method); 1334 #if 1 1335 if (NULL != body) 1336 json_dumpf (body, 1337 stderr, 1338 JSON_INDENT (2)); 1339 #endif 1340 if (NULL == pd) 1341 { 1342 GNUNET_break_op (0); 1343 json_dumpf (body, 1344 stderr, 1345 JSON_INDENT (2)); 1346 wh->resp = TALER_MHD_make_error ( 1347 TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_UNKNOWN, 1348 "kycaid"); 1349 wh->response_code = MHD_HTTP_NOT_FOUND; 1350 wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply, 1351 wh); 1352 return wh; 1353 } 1354 1355 if (GNUNET_OK != 1356 GNUNET_JSON_parse (body, 1357 spec, 1358 NULL, NULL)) 1359 { 1360 GNUNET_break_op (0); 1361 json_dumpf (body, 1362 stderr, 1363 JSON_INDENT (2)); 1364 wh->resp = TALER_MHD_MAKE_JSON_PACK ( 1365 GNUNET_JSON_pack_object_incref ("webhook_body", 1366 (json_t *) body)); 1367 wh->response_code = MHD_HTTP_BAD_REQUEST; 1368 wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply, 1369 wh); 1370 return wh; 1371 } 1372 qs = plc (plc_cls, 1373 pd->section, 1374 verification_id, 1375 &wh->h_payto, 1376 &wh->is_wallet, 1377 &wh->process_row); 1378 if (qs < 0) 1379 { 1380 wh->resp = TALER_MHD_make_error (TALER_EC_GENERIC_DB_FETCH_FAILED, 1381 "provider-legitimization-lookup"); 1382 wh->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; 1383 wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply, 1384 wh); 1385 return wh; 1386 } 1387 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 1388 { 1389 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1390 "Received webhook for unknown verification ID `%s' and section `%s'\n", 1391 verification_id, 1392 pd->section); 1393 wh->resp = TALER_MHD_make_error ( 1394 TALER_EC_EXCHANGE_KYC_PROOF_REQUEST_UNKNOWN, 1395 verification_id); 1396 wh->response_code = MHD_HTTP_NOT_FOUND; 1397 wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply, 1398 wh); 1399 return wh; 1400 } 1401 wh->verification_id = GNUNET_strdup (verification_id); 1402 wh->applicant_id = GNUNET_strdup (applicant_id); 1403 if ( (0 != strcasecmp (type, 1404 "VERIFICATION_COMPLETED")) || 1405 (no_verified) || 1406 (! verified) ) 1407 { 1408 /* We don't need to re-confirm the failure by 1409 asking the API again. */ 1410 log_failure (verifications); 1411 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1412 "Webhook called with non-completion status: %s\n", 1413 type); 1414 wh->response_code = MHD_HTTP_NO_CONTENT; 1415 wh->resp = MHD_create_response_from_buffer_static (0, 1416 ""); 1417 wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply, 1418 wh); 1419 return wh; 1420 } 1421 1422 eh = curl_easy_init (); 1423 if (NULL == eh) 1424 { 1425 GNUNET_break (0); 1426 wh->resp = TALER_MHD_make_error ( 1427 TALER_EC_GENERIC_ALLOCATION_FAILURE, 1428 NULL); 1429 wh->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; 1430 wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply, 1431 wh); 1432 return wh; 1433 } 1434 1435 GNUNET_asprintf (&wh->url, 1436 "https://api.kycaid.com/applicants/%s", 1437 applicant_id); 1438 GNUNET_break (CURLE_OK == 1439 curl_easy_setopt (eh, 1440 CURLOPT_VERBOSE, 1441 0)); 1442 GNUNET_assert (CURLE_OK == 1443 curl_easy_setopt (eh, 1444 CURLOPT_MAXREDIRS, 1445 1L)); 1446 GNUNET_break (CURLE_OK == 1447 curl_easy_setopt (eh, 1448 CURLOPT_URL, 1449 wh->url)); 1450 wh->job = GNUNET_CURL_job_add2 (ps->curl_ctx, 1451 eh, 1452 pd->slist, 1453 &handle_webhook_finished, 1454 wh); 1455 return wh; 1456 } 1457 1458 1459 /** 1460 * Initialize kycaid logic plugin 1461 * 1462 * @param cls a configuration instance 1463 * @return NULL on error, otherwise a `struct TALER_KYCLOGIC_Plugin` 1464 */ 1465 void * 1466 libtaler_plugin_kyclogic_kycaid_init (void *cls); 1467 1468 /* declaration to avoid compiler warning */ 1469 void * 1470 libtaler_plugin_kyclogic_kycaid_init (void *cls) 1471 { 1472 const struct GNUNET_CONFIGURATION_Handle *cfg = cls; 1473 struct TALER_KYCLOGIC_Plugin *plugin; 1474 struct PluginState *ps; 1475 1476 ps = GNUNET_new (struct PluginState); 1477 ps->cfg = cfg; 1478 if (GNUNET_OK != 1479 GNUNET_CONFIGURATION_get_value_string (cfg, 1480 "exchange", 1481 "BASE_URL", 1482 &ps->exchange_base_url)) 1483 { 1484 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 1485 "exchange", 1486 "BASE_URL"); 1487 GNUNET_free (ps); 1488 return NULL; 1489 } 1490 1491 ps->curl_ctx 1492 = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, 1493 &ps->curl_rc); 1494 if (NULL == ps->curl_ctx) 1495 { 1496 GNUNET_break (0); 1497 GNUNET_free (ps->exchange_base_url); 1498 GNUNET_free (ps); 1499 return NULL; 1500 } 1501 ps->curl_rc = GNUNET_CURL_gnunet_rc_create (ps->curl_ctx); 1502 1503 plugin = GNUNET_new (struct TALER_KYCLOGIC_Plugin); 1504 plugin->cls = ps; 1505 plugin->load_configuration 1506 = &kycaid_load_configuration; 1507 plugin->unload_configuration 1508 = &kycaid_unload_configuration; 1509 plugin->initiate 1510 = &kycaid_initiate; 1511 plugin->initiate_cancel 1512 = &kycaid_initiate_cancel; 1513 plugin->proof 1514 = &kycaid_proof; 1515 plugin->proof_cancel 1516 = &kycaid_proof_cancel; 1517 plugin->webhook 1518 = &kycaid_webhook; 1519 plugin->webhook_cancel 1520 = &kycaid_webhook_cancel; 1521 return plugin; 1522 } 1523 1524 1525 /** 1526 * Unload authorization plugin 1527 * 1528 * @param cls a `struct TALER_KYCLOGIC_Plugin` 1529 * @return NULL (always) 1530 */ 1531 void * 1532 libtaler_plugin_kyclogic_kycaid_done (void *cls); 1533 1534 /* declaration to avoid compiler warning */ 1535 void * 1536 libtaler_plugin_kyclogic_kycaid_done (void *cls) 1537 { 1538 struct TALER_KYCLOGIC_Plugin *plugin = cls; 1539 struct PluginState *ps = plugin->cls; 1540 1541 if (NULL != ps->curl_ctx) 1542 { 1543 GNUNET_CURL_fini (ps->curl_ctx); 1544 ps->curl_ctx = NULL; 1545 } 1546 if (NULL != ps->curl_rc) 1547 { 1548 GNUNET_CURL_gnunet_rc_destroy (ps->curl_rc); 1549 ps->curl_rc = NULL; 1550 } 1551 GNUNET_free (ps->exchange_base_url); 1552 GNUNET_free (ps); 1553 GNUNET_free (plugin); 1554 return NULL; 1555 }