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