taler-exchange-kyc-tester.c (51792B)
1 /* 2 This file is part of 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. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file taler-exchange-kyc-tester.c 18 * @brief tool to test KYC integrations 19 * @author Christian Grothoff 20 * @defgroup request Request handling routines 21 */ 22 #include "platform.h" 23 #include <gnunet/gnunet_util_lib.h> 24 #include <jansson.h> 25 #include <microhttpd.h> 26 #include <sched.h> 27 #include <sys/resource.h> 28 #include <limits.h> 29 #include "taler/taler_mhd_lib.h" 30 #include "taler/taler_json_lib.h" 31 #include "taler/taler_templating_lib.h" 32 #include "taler/taler_util.h" 33 #include "taler/taler_kyclogic_lib.h" 34 #include "taler/taler_kyclogic_plugin.h" 35 #include <gnunet/gnunet_mhd_compat.h> 36 37 38 /** 39 * @brief Context in which the exchange is processing 40 * all requests 41 */ 42 struct TEKT_RequestContext 43 { 44 45 /** 46 * Opaque parsing context. 47 */ 48 void *opaque_post_parsing_context; 49 50 /** 51 * Request handler responsible for this request. 52 */ 53 const struct TEKT_RequestHandler *rh; 54 55 /** 56 * Request URL (for logging). 57 */ 58 const char *url; 59 60 /** 61 * Connection we are processing. 62 */ 63 struct MHD_Connection *connection; 64 65 /** 66 * HTTP response to return (or NULL). 67 */ 68 struct MHD_Response *response; 69 70 /** 71 * @e rh-specific cleanup routine. Function called 72 * upon completion of the request that should 73 * clean up @a rh_ctx. Can be NULL. 74 */ 75 void 76 (*rh_cleaner)(struct TEKT_RequestContext *rc); 77 78 /** 79 * @e rh-specific context. Place where the request 80 * handler can associate state with this request. 81 * Can be NULL. 82 */ 83 void *rh_ctx; 84 85 /** 86 * Uploaded JSON body, if any. 87 */ 88 json_t *root; 89 90 /** 91 * HTTP status to return upon resume if @e response 92 * is non-NULL. 93 */ 94 unsigned int http_status; 95 96 }; 97 98 99 /** 100 * @brief Struct describing an URL and the handler for it. 101 */ 102 struct TEKT_RequestHandler 103 { 104 105 /** 106 * URL the handler is for (first part only). 107 */ 108 const char *url; 109 110 /** 111 * Method the handler is for. 112 */ 113 const char *method; 114 115 /** 116 * Callbacks for handling of the request. Which one is used 117 * depends on @e method. 118 */ 119 union 120 { 121 /** 122 * Function to call to handle a GET requests (and those 123 * with @e method NULL). 124 * 125 * @param rc context for the request 126 * @param mime_type the @e mime_type for the reply (hint, can be NULL) 127 * @param args array of arguments, needs to be of length @e args_expected 128 * @return MHD result code 129 */ 130 enum MHD_Result 131 (*get)(struct TEKT_RequestContext *rc, 132 const char *const args[]); 133 134 135 /** 136 * Function to call to handle a POST request. 137 * 138 * @param rc context for the request 139 * @param json uploaded JSON data 140 * @param args array of arguments, needs to be of length @e args_expected 141 * @return MHD result code 142 */ 143 enum MHD_Result 144 (*post)(struct TEKT_RequestContext *rc, 145 const json_t *root, 146 const char *const args[]); 147 148 } handler; 149 150 /** 151 * Number of arguments this handler expects in the @a args array. 152 */ 153 unsigned int nargs; 154 155 /** 156 * Is the number of arguments given in @e nargs only an upper bound, 157 * and calling with fewer arguments could be OK? 158 */ 159 bool nargs_is_upper_bound; 160 161 /** 162 * Mime type to use in reply (hint, can be NULL). 163 */ 164 const char *mime_type; 165 166 /** 167 * Raw data for the @e handler, can be NULL for none provided. 168 */ 169 const void *data; 170 171 /** 172 * Number of bytes in @e data, 0 for data is 0-terminated (!). 173 */ 174 size_t data_size; 175 176 /** 177 * Default response code. 0 for none provided. 178 */ 179 unsigned int response_code; 180 }; 181 182 183 /** 184 * Information we track per ongoing kyc-proof request. 185 */ 186 struct ProofRequestState 187 { 188 /** 189 * Kept in a DLL. 190 */ 191 struct ProofRequestState *next; 192 193 /** 194 * Kept in a DLL. 195 */ 196 struct ProofRequestState *prev; 197 198 /** 199 * Handle for operation with the plugin. 200 */ 201 struct TALER_KYCLOGIC_ProofHandle *ph; 202 203 /** 204 * Logic plugin we are using. 205 */ 206 struct TALER_KYCLOGIC_Plugin *logic; 207 208 /** 209 * HTTP request details. 210 */ 211 struct TEKT_RequestContext *rc; 212 213 }; 214 215 /** 216 * Head of DLL. 217 */ 218 static struct ProofRequestState *rs_head; 219 220 /** 221 * Tail of DLL. 222 */ 223 static struct ProofRequestState *rs_tail; 224 225 /** 226 * The exchange's configuration (global) 227 */ 228 static const struct GNUNET_CONFIGURATION_Handle *TEKT_cfg; 229 230 /** 231 * Our base URL. 232 */ 233 static char *TEKT_base_url; 234 235 /** 236 * Payto set via command-line (or otherwise random). 237 */ 238 static struct TALER_NormalizedPaytoHashP cmd_line_h_payto; 239 240 /** 241 * Provider user ID to use. 242 */ 243 static char *cmd_provider_user_id; 244 245 /** 246 * Provider legitimization ID to use. 247 */ 248 static char *cmd_provider_legitimization_id; 249 250 /** 251 * Custom legitimization rule in JSON given as 252 * a string. 253 */ 254 static char *lrs_s; 255 256 /** 257 * Type of the operation that triggers legitimization. 258 */ 259 static char *operation_s; 260 261 /** 262 * Amount threshold crossed that triggers some rule. 263 */ 264 static struct TALER_Amount trigger_amount; 265 266 /** 267 * Row ID to use, override with '-r' 268 */ 269 static unsigned int kyc_row_id = 42; 270 271 /** 272 * -P command-line option. 273 */ 274 static int print_h_payto; 275 276 /** 277 * -W command-line option. 278 */ 279 static int cmd_line_is_wallet; 280 281 /** 282 * -w command-line option. 283 */ 284 static int run_webservice; 285 286 /** 287 * -M command-line option. 288 */ 289 static int list_measures; 290 291 /** 292 * Value to return from main() 293 */ 294 static int global_ret; 295 296 /** 297 * -m command-line flag. 298 */ 299 static char *measure; 300 301 /** 302 * Legitimization rule set parsed from the command-line, 303 * or NULL if none was given. 304 */ 305 static struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs; 306 307 /** 308 * Handle for ongoing initiation operation. 309 */ 310 static struct TALER_KYCLOGIC_InitiateHandle *ih; 311 312 /** 313 * KYC logic running for @e ih. 314 */ 315 static struct TALER_KYCLOGIC_Plugin *ih_logic; 316 317 /** 318 * True if we started any daemon. 319 */ 320 static bool have_daemons; 321 322 /** 323 * Context for all CURL operations (useful to the event loop) 324 */ 325 static struct GNUNET_CURL_Context *TEKT_curl_ctx; 326 327 /** 328 * Context for integrating #TEKT_curl_ctx with the 329 * GNUnet event loop. 330 */ 331 static struct GNUNET_CURL_RescheduleContext *exchange_curl_rc; 332 333 334 /** 335 * Context for the webhook. 336 */ 337 struct KycWebhookContext 338 { 339 340 /** 341 * Kept in a DLL while suspended. 342 */ 343 struct KycWebhookContext *next; 344 345 /** 346 * Kept in a DLL while suspended. 347 */ 348 struct KycWebhookContext *prev; 349 350 /** 351 * Details about the connection we are processing. 352 */ 353 struct TEKT_RequestContext *rc; 354 355 /** 356 * Plugin responsible for the webhook. 357 */ 358 struct TALER_KYCLOGIC_Plugin *plugin; 359 360 /** 361 * Configuration for the specific action. 362 */ 363 struct TALER_KYCLOGIC_ProviderDetails *pd; 364 365 /** 366 * Webhook activity. 367 */ 368 struct TALER_KYCLOGIC_WebhookHandle *wh; 369 370 /** 371 * HTTP response to return. 372 */ 373 struct MHD_Response *response; 374 375 /** 376 * Name of the configuration 377 * section defining the KYC logic. 378 */ 379 const char *section_name; 380 381 /** 382 * HTTP response code to return. 383 */ 384 unsigned int response_code; 385 386 /** 387 * #GNUNET_YES if we are suspended, 388 * #GNUNET_NO if not. 389 * #GNUNET_SYSERR if we had some error. 390 */ 391 enum GNUNET_GenericReturnValue suspended; 392 393 }; 394 395 396 /** 397 * Contexts are kept in a DLL while suspended. 398 */ 399 static struct KycWebhookContext *kwh_head; 400 401 /** 402 * Contexts are kept in a DLL while suspended. 403 */ 404 static struct KycWebhookContext *kwh_tail; 405 406 407 /** 408 * Resume processing the @a kwh request. 409 * 410 * @param kwh request to resume 411 */ 412 static void 413 kwh_resume (struct KycWebhookContext *kwh) 414 { 415 GNUNET_assert (GNUNET_YES == kwh->suspended); 416 kwh->suspended = GNUNET_NO; 417 GNUNET_CONTAINER_DLL_remove (kwh_head, 418 kwh_tail, 419 kwh); 420 MHD_resume_connection (kwh->rc->connection); 421 } 422 423 424 static void 425 kyc_webhook_cleanup (void) 426 { 427 struct KycWebhookContext *kwh; 428 429 while (NULL != (kwh = kwh_head)) 430 { 431 if (NULL != kwh->wh) 432 { 433 kwh->plugin->webhook_cancel (kwh->wh); 434 kwh->wh = NULL; 435 } 436 kwh_resume (kwh); 437 } 438 } 439 440 441 /** 442 * Function called with the result of a webhook operation. 443 * 444 * @param cls closure 445 * @param process_row legitimization process request the webhook was about 446 * @param account_id account the webhook was about 447 * @param is_wallet true if @a account_id is for a wallet 448 * @param provider_section configuration section of the logic 449 * @param provider_user_id set to user ID at the provider, or NULL if not supported or unknown 450 * @param provider_legitimization_id set to legitimization process ID at the provider, or NULL if not supported or unknown 451 * @param status KYC status 452 * @param expiration until when is the KYC check valid 453 * @param attributes user attributes returned by the provider 454 * @param http_status HTTP status code of @a response 455 * @param[in] response to return to the HTTP client 456 */ 457 static void 458 webhook_finished_cb ( 459 void *cls, 460 uint64_t process_row, 461 const struct TALER_NormalizedPaytoHashP *account_id, 462 bool is_wallet, 463 const char *provider_section, 464 const char *provider_user_id, 465 const char *provider_legitimization_id, 466 enum TALER_KYCLOGIC_KycStatus status, 467 struct GNUNET_TIME_Absolute expiration, 468 const json_t *attributes, 469 unsigned int http_status, 470 struct MHD_Response *response) 471 { 472 struct KycWebhookContext *kwh = cls; 473 474 (void) expiration; 475 (void) provider_section; 476 (void) is_wallet; 477 kwh->wh = NULL; 478 if ( (NULL != account_id) && 479 (0 != GNUNET_memcmp (account_id, 480 &cmd_line_h_payto)) ) 481 { 482 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 483 "Received webhook for unexpected account\n"); 484 } 485 if ( (NULL != provider_user_id) && 486 (NULL != cmd_provider_user_id) && 487 (0 != strcmp (provider_user_id, 488 cmd_provider_user_id)) ) 489 { 490 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 491 "Received webhook for unexpected provider user ID (%s)\n", 492 provider_user_id); 493 } 494 if ( (NULL != provider_legitimization_id) && 495 (NULL != cmd_provider_legitimization_id) && 496 (0 != strcmp (provider_legitimization_id, 497 cmd_provider_legitimization_id)) ) 498 { 499 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 500 "Received webhook for unexpected provider legitimization ID (%s)\n", 501 provider_legitimization_id); 502 } 503 switch (status) 504 { 505 case TALER_KYCLOGIC_STATUS_SUCCESS: 506 /* _successfully_ resumed case */ 507 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 508 "KYC successful for user `%s' (legi: %s)\n", 509 provider_user_id, 510 provider_legitimization_id); 511 GNUNET_break (NULL != attributes); 512 fprintf (stderr, 513 "Extracted attributes:\n"); 514 json_dumpf (attributes, 515 stderr, 516 JSON_INDENT (2)); 517 break; 518 default: 519 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 520 "KYC status of %s/%s (process #%llu) is %d\n", 521 provider_user_id, 522 provider_legitimization_id, 523 (unsigned long long) process_row, 524 status); 525 break; 526 } 527 kwh->response = response; 528 kwh->response_code = http_status; 529 kwh_resume (kwh); 530 TALER_MHD_daemon_trigger (); 531 } 532 533 534 /** 535 * Function called to clean up a context. 536 * 537 * @param rc request context 538 */ 539 static void 540 clean_kwh (struct TEKT_RequestContext *rc) 541 { 542 struct KycWebhookContext *kwh = rc->rh_ctx; 543 544 if (NULL != kwh->wh) 545 { 546 kwh->plugin->webhook_cancel (kwh->wh); 547 kwh->wh = NULL; 548 } 549 if (NULL != kwh->response) 550 { 551 MHD_destroy_response (kwh->response); 552 kwh->response = NULL; 553 } 554 GNUNET_free (kwh); 555 } 556 557 558 /** 559 * Function the plugin can use to lookup an 560 * @a h_payto by @a provider_legitimization_id. 561 * 562 * @param pg database connection, NULL 563 * @param provider_section 564 * @param provider_legitimization_id legi to look up 565 * @param[out] h_payto where to write the result 566 * @param[out] is_wallet set to true if @a h_payto is for a wallet 567 * @param[out] legi_row where to write the row ID for the legitimization ID 568 * @return database transaction status 569 */ 570 static enum GNUNET_DB_QueryStatus 571 kyc_provider_account_lookup ( 572 struct TALER_EXCHANGEDB_PostgresContext *pg, 573 const char *provider_section, 574 const char *provider_legitimization_id, 575 struct TALER_NormalizedPaytoHashP *h_payto, 576 bool *is_wallet, 577 uint64_t *legi_row) 578 { 579 (void) pg; 580 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 581 "Simulated account lookup using `%s/%s'\n", 582 provider_section, 583 provider_legitimization_id); 584 *h_payto = cmd_line_h_payto; 585 *legi_row = kyc_row_id; 586 *is_wallet = (0 != cmd_line_is_wallet); 587 return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; 588 } 589 590 591 /** 592 * Handle a (GET or POST) "/kyc-webhook" request. 593 * 594 * @param rc request to handle 595 * @param method HTTP request method used by the client 596 * @param root uploaded JSON body (can be NULL) 597 * @param args one argument with the legitimization_uuid 598 * @return MHD result code 599 */ 600 static enum MHD_Result 601 handler_kyc_webhook_generic ( 602 struct TEKT_RequestContext *rc, 603 const char *method, 604 const json_t *root, 605 const char *const args[]) 606 { 607 struct KycWebhookContext *kwh = rc->rh_ctx; 608 609 if (NULL == kwh) 610 { /* first time */ 611 kwh = GNUNET_new (struct KycWebhookContext); 612 kwh->rc = rc; 613 rc->rh_ctx = kwh; 614 rc->rh_cleaner = &clean_kwh; 615 616 if ( (NULL == args[0]) || 617 (GNUNET_OK != 618 TALER_KYCLOGIC_lookup_logic (args[0], 619 &kwh->plugin, 620 &kwh->pd, 621 &kwh->section_name)) ) 622 { 623 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 624 "KYC logic `%s' unknown (check KYC provider configuration)\n", 625 args[0]); 626 return TALER_MHD_reply_with_error (rc->connection, 627 MHD_HTTP_NOT_FOUND, 628 TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_UNKNOWN, 629 args[0]); 630 } 631 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 632 "Calling KYC provider specific webhook\n"); 633 kwh->wh = kwh->plugin->webhook (kwh->plugin->cls, 634 kwh->pd, 635 &kyc_provider_account_lookup, 636 NULL, 637 method, 638 &args[1], 639 rc->connection, 640 root, 641 &webhook_finished_cb, 642 kwh); 643 if (NULL == kwh->wh) 644 { 645 GNUNET_break_op (0); 646 return TALER_MHD_reply_with_error (rc->connection, 647 MHD_HTTP_INTERNAL_SERVER_ERROR, 648 TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, 649 "failed to run webhook logic"); 650 } 651 kwh->suspended = GNUNET_YES; 652 GNUNET_CONTAINER_DLL_insert (kwh_head, 653 kwh_tail, 654 kwh); 655 MHD_suspend_connection (rc->connection); 656 return MHD_YES; 657 } 658 659 if (NULL != kwh->response) 660 { 661 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 662 "Returning queued reply for KWH\n"); 663 /* handle _failed_ resumed cases */ 664 return MHD_queue_response (rc->connection, 665 kwh->response_code, 666 kwh->response); 667 } 668 669 /* We resumed, but got no response? This should 670 not happen. */ 671 GNUNET_assert (0); 672 return TALER_MHD_reply_with_error (rc->connection, 673 MHD_HTTP_INTERNAL_SERVER_ERROR, 674 TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, 675 "resumed without response"); 676 } 677 678 679 /** 680 * Handle a GET "/kyc-webhook" request. 681 * 682 * @param rc request to handle 683 * @param args one argument with the legitimization_uuid 684 * @return MHD result code 685 */ 686 static enum MHD_Result 687 handler_kyc_webhook_get ( 688 struct TEKT_RequestContext *rc, 689 const char *const args[]) 690 { 691 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 692 "Webhook GET triggered\n"); 693 return handler_kyc_webhook_generic (rc, 694 MHD_HTTP_METHOD_GET, 695 NULL, 696 args); 697 } 698 699 700 /** 701 * Handle a POST "/kyc-webhook" request. 702 * 703 * @param rc request to handle 704 * @param root uploaded JSON body (can be NULL) 705 * @param args one argument with the legitimization_uuid 706 * @return MHD result code 707 */ 708 static enum MHD_Result 709 handler_kyc_webhook_post ( 710 struct TEKT_RequestContext *rc, 711 const json_t *root, 712 const char *const args[]) 713 { 714 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 715 "Webhook POST triggered\n"); 716 return handler_kyc_webhook_generic (rc, 717 MHD_HTTP_METHOD_POST, 718 root, 719 args); 720 } 721 722 723 /** 724 * Function called with the result of a proof check operation. 725 * 726 * Note that the "decref" for the @a response 727 * will be done by the callee and MUST NOT be done by the plugin. 728 * 729 * @param cls closure with the `struct ProofRequestState` 730 * @param status KYC status 731 * @param provider_name name of the KYC provider 732 * @param provider_user_id set to user ID at the provider, or NULL if not supported or unknown 733 * @param provider_legitimization_id set to legitimization process ID at the provider, or NULL if not supported or unknown 734 * @param expiration until when is the KYC check valid 735 * @param attributes attributes about the user 736 * @param http_status HTTP status code of @a response 737 * @param[in] response to return to the HTTP client 738 */ 739 static void 740 proof_cb ( 741 void *cls, 742 enum TALER_KYCLOGIC_KycStatus status, 743 const char *provider_name, 744 const char *provider_user_id, 745 const char *provider_legitimization_id, 746 struct GNUNET_TIME_Absolute expiration, 747 const json_t *attributes, 748 unsigned int http_status, 749 struct MHD_Response *response) 750 { 751 struct ProofRequestState *rs = cls; 752 753 (void) expiration; 754 (void) provider_name; 755 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 756 "KYC legitimization %s completed with status %d (%u) for %s\n", 757 provider_legitimization_id, 758 status, 759 http_status, 760 provider_user_id); 761 if (TALER_KYCLOGIC_STATUS_SUCCESS == status) 762 { 763 GNUNET_break (NULL != attributes); 764 fprintf (stderr, 765 "Extracted attributes:\n"); 766 json_dumpf (attributes, 767 stderr, 768 JSON_INDENT (2)); 769 } 770 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 771 "Returning response %p with status %u\n", 772 response, 773 http_status); 774 rs->rc->response = response; 775 rs->rc->http_status = http_status; 776 GNUNET_CONTAINER_DLL_remove (rs_head, 777 rs_tail, 778 rs); 779 MHD_resume_connection (rs->rc->connection); 780 TALER_MHD_daemon_trigger (); 781 GNUNET_free (rs); 782 } 783 784 785 /** 786 * Function called when we receive a 'GET' to the 787 * '/kyc-proof' endpoint. 788 * 789 * @param rc request context 790 * @param args remaining URL arguments; 791 * args[0] should be the logic plugin name 792 */ 793 static enum MHD_Result 794 handler_kyc_proof_get ( 795 struct TEKT_RequestContext *rc, 796 const char *const args[1]) 797 { 798 struct TALER_NormalizedPaytoHashP h_payto; 799 struct TALER_KYCLOGIC_ProviderDetails *pd; 800 struct TALER_KYCLOGIC_Plugin *logic; 801 struct ProofRequestState *rs; 802 const char *section_name; 803 const char *h_paytos; 804 805 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 806 "GET /kyc-proof triggered\n"); 807 if (NULL == args[0]) 808 { 809 GNUNET_break_op (0); 810 return TALER_MHD_reply_with_error (rc->connection, 811 MHD_HTTP_NOT_FOUND, 812 TALER_EC_GENERIC_ENDPOINT_UNKNOWN, 813 "'/kyc-proof/$PROVIDER_SECTION?state=$H_PAYTO' required"); 814 } 815 h_paytos = MHD_lookup_connection_value (rc->connection, 816 MHD_GET_ARGUMENT_KIND, 817 "state"); 818 if (NULL == h_paytos) 819 { 820 GNUNET_break_op (0); 821 return TALER_MHD_reply_with_error (rc->connection, 822 MHD_HTTP_BAD_REQUEST, 823 TALER_EC_GENERIC_PARAMETER_MISSING, 824 "h_payto"); 825 } 826 if (GNUNET_OK != 827 GNUNET_STRINGS_string_to_data (h_paytos, 828 strlen (h_paytos), 829 &h_payto, 830 sizeof (h_payto))) 831 { 832 GNUNET_break_op (0); 833 return TALER_MHD_reply_with_error (rc->connection, 834 MHD_HTTP_BAD_REQUEST, 835 TALER_EC_GENERIC_PARAMETER_MALFORMED, 836 "h_payto"); 837 } 838 if (0 != 839 GNUNET_memcmp (&h_payto, 840 &cmd_line_h_payto)) 841 { 842 GNUNET_break_op (0); 843 return TALER_MHD_reply_with_error (rc->connection, 844 MHD_HTTP_NOT_FOUND, 845 TALER_EC_EXCHANGE_KYC_PROOF_REQUEST_UNKNOWN, 846 "h_payto"); 847 } 848 849 if (GNUNET_OK != 850 TALER_KYCLOGIC_lookup_logic (args[0], 851 &logic, 852 &pd, 853 §ion_name)) 854 { 855 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 856 "Could not initiate KYC with provider `%s' (configuration error?)\n", 857 args[0]); 858 return TALER_MHD_reply_with_error (rc->connection, 859 MHD_HTTP_NOT_FOUND, 860 TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_UNKNOWN, 861 args[0]); 862 } 863 rs = GNUNET_new (struct ProofRequestState); 864 rs->rc = rc; 865 rs->logic = logic; 866 MHD_suspend_connection (rc->connection); 867 GNUNET_CONTAINER_DLL_insert (rs_head, 868 rs_tail, 869 rs); 870 rs->ph = logic->proof (logic->cls, 871 pd, 872 rc->connection, 873 &h_payto, 874 kyc_row_id, 875 cmd_provider_user_id, 876 cmd_provider_legitimization_id, 877 &proof_cb, 878 rs); 879 GNUNET_assert (NULL != rs->ph); 880 return MHD_YES; 881 } 882 883 884 /** 885 * Function called whenever MHD is done with a request. If the 886 * request was a POST, we may have stored a `struct Buffer *` in the 887 * @a con_cls that might still need to be cleaned up. Call the 888 * respective function to free the memory. 889 * 890 * @param cls client-defined closure 891 * @param connection connection handle 892 * @param con_cls value as set by the last call to 893 * the #MHD_AccessHandlerCallback 894 * @param toe reason for request termination 895 * @see #MHD_OPTION_NOTIFY_COMPLETED 896 * @ingroup request 897 */ 898 static void 899 handle_mhd_completion_callback (void *cls, 900 struct MHD_Connection *connection, 901 void **con_cls, 902 enum MHD_RequestTerminationCode toe) 903 { 904 struct TEKT_RequestContext *rc = *con_cls; 905 906 (void) cls; 907 if (NULL == rc) 908 return; 909 if (NULL != rc->rh_cleaner) 910 rc->rh_cleaner (rc); 911 { 912 #if MHD_VERSION >= 0x00097304 913 const union MHD_ConnectionInfo *ci; 914 unsigned int http_status = 0; 915 916 ci = MHD_get_connection_info (connection, 917 MHD_CONNECTION_INFO_HTTP_STATUS); 918 if (NULL != ci) 919 http_status = ci->http_status; 920 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 921 "Request for `%s' completed with HTTP status %u (%d)\n", 922 rc->url, 923 http_status, 924 toe); 925 #else 926 (void) connection; 927 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 928 "Request for `%s' completed (%d)\n", 929 rc->url, 930 toe); 931 #endif 932 } 933 934 TALER_MHD_parse_post_cleanup_callback (rc->opaque_post_parsing_context); 935 /* Sanity-check that we didn't leave any transactions hanging */ 936 if (NULL != rc->root) 937 json_decref (rc->root); 938 GNUNET_free (rc); 939 *con_cls = NULL; 940 } 941 942 943 /** 944 * We found a request handler responsible for handling a request. Parse the 945 * @a upload_data (if applicable) and the @a url and call the 946 * handler. 947 * 948 * @param rc request context 949 * @param url rest of the URL to parse 950 * @param upload_data upload data to parse (if available) 951 * @param[in,out] upload_data_size number of bytes in @a upload_data 952 * @return MHD result code 953 */ 954 static enum MHD_Result 955 proceed_with_handler (struct TEKT_RequestContext *rc, 956 const char *url, 957 const char *upload_data, 958 size_t *upload_data_size) 959 { 960 const struct TEKT_RequestHandler *rh = rc->rh; 961 const char *args[rh->nargs + 2]; 962 size_t ulen = strlen (url) + 1; 963 enum MHD_Result ret; 964 965 /* We do check for "ulen" here, because we'll later stack-allocate a buffer 966 of that size and don't want to enable malicious clients to cause us 967 huge stack allocations. */ 968 if (ulen > 512) 969 { 970 /* 512 is simply "big enough", as it is bigger than "6 * 54", 971 which is the longest URL format we ever get (for 972 /deposits/). The value should be adjusted if we ever define protocol 973 endpoints with plausibly longer inputs. */ 974 GNUNET_break_op (0); 975 return TALER_MHD_reply_with_error ( 976 rc->connection, 977 MHD_HTTP_URI_TOO_LONG, 978 TALER_EC_GENERIC_URI_TOO_LONG, 979 url); 980 } 981 982 /* All POST endpoints come with a body in JSON format. So we parse 983 the JSON here. */ 984 if ( (NULL == rc->root) && 985 (0 == strcasecmp (rh->method, 986 MHD_HTTP_METHOD_POST)) ) 987 { 988 enum GNUNET_GenericReturnValue res; 989 990 res = TALER_MHD_parse_post_json ( 991 rc->connection, 992 &rc->opaque_post_parsing_context, 993 upload_data, 994 upload_data_size, 995 &rc->root); 996 if (GNUNET_SYSERR == res) 997 { 998 GNUNET_assert (NULL == rc->root); 999 GNUNET_break (0); 1000 return MHD_NO; /* bad upload, could not even generate error */ 1001 } 1002 if ( (GNUNET_NO == res) || 1003 (NULL == rc->root) ) 1004 { 1005 GNUNET_assert (NULL == rc->root); 1006 return MHD_YES; /* so far incomplete upload or parser error */ 1007 } 1008 } 1009 1010 { 1011 char d[ulen]; 1012 unsigned int i; 1013 char *sp; 1014 1015 /* Parse command-line arguments */ 1016 /* make a copy of 'url' because 'strtok_r()' will modify */ 1017 GNUNET_memcpy (d, 1018 url, 1019 ulen); 1020 i = 0; 1021 args[i++] = strtok_r (d, "/", &sp); 1022 while ( (NULL != args[i - 1]) && 1023 (i <= rh->nargs + 1) ) 1024 args[i++] = strtok_r (NULL, "/", &sp); 1025 /* make sure above loop ran nicely until completion, and also 1026 that there is no excess data in 'd' afterwards */ 1027 if ( ( (rh->nargs_is_upper_bound) && 1028 (i - 1 > rh->nargs) ) || 1029 ( (! rh->nargs_is_upper_bound) && 1030 (i - 1 != rh->nargs) ) ) 1031 { 1032 char emsg[128 + 512]; 1033 1034 GNUNET_snprintf (emsg, 1035 sizeof (emsg), 1036 "Got %u+/%u segments for `%s' request (`%s')", 1037 i - 1, 1038 rh->nargs, 1039 rh->url, 1040 url); 1041 GNUNET_break_op (0); 1042 return TALER_MHD_reply_with_error ( 1043 rc->connection, 1044 MHD_HTTP_NOT_FOUND, 1045 TALER_EC_EXCHANGE_GENERIC_WRONG_NUMBER_OF_SEGMENTS, 1046 emsg); 1047 } 1048 GNUNET_assert (NULL == args[i - 1]); 1049 1050 /* Above logic ensures that 'root' is exactly non-NULL for POST operations, 1051 so we test for 'root' to decide which handler to invoke. */ 1052 if (NULL != rc->root) 1053 ret = rh->handler.post (rc, 1054 rc->root, 1055 args); 1056 else /* We also only have "POST" or "GET" in the API for at this point 1057 (OPTIONS/HEAD are taken care of earlier) */ 1058 ret = rh->handler.get (rc, 1059 args); 1060 } 1061 return ret; 1062 } 1063 1064 1065 static void 1066 rh_cleaner_cb (struct TEKT_RequestContext *rc) 1067 { 1068 if (NULL != rc->response) 1069 { 1070 MHD_destroy_response (rc->response); 1071 rc->response = NULL; 1072 } 1073 if (NULL != rc->root) 1074 { 1075 json_decref (rc->root); 1076 rc->root = NULL; 1077 } 1078 } 1079 1080 1081 /** 1082 * Handle incoming HTTP request. 1083 * 1084 * @param cls closure for MHD daemon (unused) 1085 * @param connection the connection 1086 * @param url the requested url 1087 * @param method the method (POST, GET, ...) 1088 * @param version HTTP version (ignored) 1089 * @param upload_data request data 1090 * @param upload_data_size size of @a upload_data in bytes 1091 * @param con_cls closure for request (a `struct TEKT_RequestContext *`) 1092 * @return MHD result code 1093 */ 1094 static enum MHD_Result 1095 handle_mhd_request (void *cls, 1096 struct MHD_Connection *connection, 1097 const char *url, 1098 const char *method, 1099 const char *version, 1100 const char *upload_data, 1101 size_t *upload_data_size, 1102 void **con_cls) 1103 { 1104 static struct TEKT_RequestHandler handlers[] = { 1105 /* simulated KYC endpoints */ 1106 { 1107 .url = "kyc-proof", 1108 .method = MHD_HTTP_METHOD_GET, 1109 .handler.get = &handler_kyc_proof_get, 1110 .nargs = 1 1111 }, 1112 { 1113 .url = "kyc-webhook", 1114 .method = MHD_HTTP_METHOD_POST, 1115 .handler.post = &handler_kyc_webhook_post, 1116 .nargs = 128, 1117 .nargs_is_upper_bound = true 1118 }, 1119 { 1120 .url = "kyc-webhook", 1121 .method = MHD_HTTP_METHOD_GET, 1122 .handler.get = &handler_kyc_webhook_get, 1123 .nargs = 128, 1124 .nargs_is_upper_bound = true 1125 }, 1126 /* mark end of list */ 1127 { 1128 .url = NULL 1129 } 1130 }; 1131 struct TEKT_RequestContext *rc = *con_cls; 1132 1133 (void) cls; 1134 (void) version; 1135 if (NULL == rc) 1136 { 1137 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1138 "Handling new request\n"); 1139 /* We're in a new async scope! */ 1140 rc = *con_cls = GNUNET_new (struct TEKT_RequestContext); 1141 rc->url = url; 1142 rc->connection = connection; 1143 rc->rh_cleaner = &rh_cleaner_cb; 1144 } 1145 if (NULL != rc->response) 1146 { 1147 return MHD_queue_response (rc->connection, 1148 rc->http_status, 1149 rc->response); 1150 } 1151 1152 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1153 "Handling request (%s) for URL '%s'\n", 1154 method, 1155 url); 1156 /* on repeated requests, check our cache first */ 1157 if (NULL != rc->rh) 1158 { 1159 const char *start; 1160 1161 if ('\0' == url[0]) 1162 /* strange, should start with '/', treat as just "/" */ 1163 url = "/"; 1164 start = strchr (url + 1, '/'); 1165 if (NULL == start) 1166 start = ""; 1167 return proceed_with_handler (rc, 1168 start, 1169 upload_data, 1170 upload_data_size); 1171 } 1172 if (0 == strcasecmp (method, 1173 MHD_HTTP_METHOD_HEAD)) 1174 method = MHD_HTTP_METHOD_GET; /* treat HEAD as GET here, MHD will do the rest */ 1175 1176 /* parse first part of URL */ 1177 { 1178 bool found = false; 1179 size_t tok_size; 1180 const char *tok; 1181 const char *rest; 1182 1183 if ('\0' == url[0]) 1184 /* strange, should start with '/', treat as just "/" */ 1185 url = "/"; 1186 tok = url + 1; 1187 rest = strchr (tok, '/'); 1188 if (NULL == rest) 1189 { 1190 tok_size = strlen (tok); 1191 } 1192 else 1193 { 1194 tok_size = rest - tok; 1195 rest++; /* skip over '/' */ 1196 } 1197 for (unsigned int i = 0; NULL != handlers[i].url; i++) 1198 { 1199 struct TEKT_RequestHandler *rh = &handlers[i]; 1200 1201 if ( (0 != strncmp (tok, 1202 rh->url, 1203 tok_size)) || 1204 (tok_size != strlen (rh->url) ) ) 1205 continue; 1206 found = true; 1207 /* The URL is a match! What we now do depends on the method. */ 1208 if (0 == strcasecmp (method, 1209 MHD_HTTP_METHOD_OPTIONS)) 1210 { 1211 return TALER_MHD_reply_cors_preflight (connection); 1212 } 1213 GNUNET_assert (NULL != rh->method); 1214 if (0 != strcasecmp (method, 1215 rh->method)) 1216 { 1217 found = true; 1218 continue; 1219 } 1220 /* cache to avoid the loop next time */ 1221 rc->rh = rh; 1222 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1223 "Handler found for %s '%s'\n", 1224 method, 1225 url); 1226 return MHD_YES; 1227 } 1228 1229 if (found) 1230 { 1231 /* we found a matching address, but the method is wrong */ 1232 struct MHD_Response *reply; 1233 enum MHD_Result ret; 1234 char *allowed = NULL; 1235 1236 GNUNET_break_op (0); 1237 for (unsigned int i = 0; NULL != handlers[i].url; i++) 1238 { 1239 struct TEKT_RequestHandler *rh = &handlers[i]; 1240 1241 if ( (0 != strncmp (tok, 1242 rh->url, 1243 tok_size)) || 1244 (tok_size != strlen (rh->url) ) ) 1245 continue; 1246 if (NULL == allowed) 1247 { 1248 allowed = GNUNET_strdup (rh->method); 1249 } 1250 else 1251 { 1252 char *tmp; 1253 1254 GNUNET_asprintf (&tmp, 1255 "%s, %s", 1256 allowed, 1257 rh->method); 1258 GNUNET_free (allowed); 1259 allowed = tmp; 1260 } 1261 if (0 == strcasecmp (rh->method, 1262 MHD_HTTP_METHOD_GET)) 1263 { 1264 char *tmp; 1265 1266 GNUNET_asprintf (&tmp, 1267 "%s, %s", 1268 allowed, 1269 MHD_HTTP_METHOD_HEAD); 1270 GNUNET_free (allowed); 1271 allowed = tmp; 1272 } 1273 } 1274 reply = TALER_MHD_make_error ( 1275 TALER_EC_GENERIC_METHOD_INVALID, 1276 method); 1277 GNUNET_break ( 1278 MHD_YES == 1279 MHD_add_response_header (reply, 1280 MHD_HTTP_HEADER_ALLOW, 1281 allowed)); 1282 GNUNET_free (allowed); 1283 ret = MHD_queue_response (connection, 1284 MHD_HTTP_METHOD_NOT_ALLOWED, 1285 reply); 1286 MHD_destroy_response (reply); 1287 return ret; 1288 } 1289 } 1290 1291 /* No handler matches, generate not found */ 1292 return TALER_MHD_reply_with_error (connection, 1293 MHD_HTTP_NOT_FOUND, 1294 TALER_EC_GENERIC_ENDPOINT_UNKNOWN, 1295 url); 1296 } 1297 1298 1299 /** 1300 * Load configuration parameters for the exchange 1301 * server into the corresponding global variables. 1302 * 1303 * @return #GNUNET_OK on success 1304 */ 1305 static enum GNUNET_GenericReturnValue 1306 exchange_serve_process_config (void) 1307 { 1308 if (GNUNET_OK != 1309 GNUNET_CONFIGURATION_get_value_string (TEKT_cfg, 1310 "exchange", 1311 "BASE_URL", 1312 &TEKT_base_url)) 1313 { 1314 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 1315 "exchange", 1316 "BASE_URL"); 1317 return GNUNET_SYSERR; 1318 } 1319 if (! TALER_url_valid_charset (TEKT_base_url)) 1320 { 1321 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 1322 "exchange", 1323 "BASE_URL", 1324 "invalid URL"); 1325 return GNUNET_SYSERR; 1326 } 1327 1328 return GNUNET_OK; 1329 } 1330 1331 1332 /** 1333 * Function run on shutdown. 1334 * 1335 * @param cls NULL 1336 */ 1337 static void 1338 do_shutdown (void *cls) 1339 { 1340 struct ProofRequestState *rs; 1341 1342 (void) cls; 1343 while (NULL != (rs = rs_head)) 1344 { 1345 GNUNET_CONTAINER_DLL_remove (rs_head, 1346 rs_tail, 1347 rs); 1348 rs->logic->proof_cancel (rs->ph); 1349 MHD_resume_connection (rs->rc->connection); 1350 GNUNET_free (rs); 1351 } 1352 if (NULL != ih) 1353 { 1354 ih_logic->initiate_cancel (ih); 1355 ih = NULL; 1356 } 1357 kyc_webhook_cleanup (); 1358 TALER_KYCLOGIC_rules_free (lrs); 1359 lrs = NULL; 1360 TALER_KYCLOGIC_kyc_done (); 1361 TALER_MHD_daemons_halt (); 1362 TALER_MHD_daemons_destroy (); 1363 if (NULL != TEKT_curl_ctx) 1364 { 1365 GNUNET_CURL_fini (TEKT_curl_ctx); 1366 TEKT_curl_ctx = NULL; 1367 } 1368 if (NULL != exchange_curl_rc) 1369 { 1370 GNUNET_CURL_gnunet_rc_destroy (exchange_curl_rc); 1371 exchange_curl_rc = NULL; 1372 } 1373 TALER_TEMPLATING_done (); 1374 } 1375 1376 1377 /** 1378 * Function called with the result of a KYC initiation 1379 * operation. 1380 * 1381 * @param cls closure 1382 * @param ec #TALER_EC_NONE on success 1383 * @param redirect_url set to where to redirect the user on success, NULL on failure 1384 * @param provider_user_id set to user ID at the provider, or NULL if not supported or unknown 1385 * @param provider_legitimization_id set to legitimization process ID at the provider, or NULL if not supported or unknown 1386 * @param error_msg_hint set to additional details to return to user, NULL on success 1387 */ 1388 static void 1389 initiate_cb ( 1390 void *cls, 1391 enum TALER_ErrorCode ec, 1392 const char *redirect_url, 1393 const char *provider_user_id, 1394 const char *provider_legitimization_id, 1395 const char *error_msg_hint) 1396 { 1397 (void) cls; 1398 ih = NULL; 1399 if (TALER_EC_NONE != ec) 1400 { 1401 fprintf (stderr, 1402 "Failed to start KYC process: %s (#%d)\n", 1403 error_msg_hint, 1404 ec); 1405 global_ret = EXIT_FAILURE; 1406 GNUNET_SCHEDULER_shutdown (); 1407 return; 1408 } 1409 { 1410 char *s; 1411 1412 s = GNUNET_STRINGS_data_to_string_alloc (&cmd_line_h_payto, 1413 sizeof (cmd_line_h_payto)); 1414 if (NULL != provider_user_id) 1415 { 1416 fprintf (stdout, 1417 "Visit `%s' to begin KYC process.\nAlso use: taler-exchange-kyc-tester -w -u '%s' -U '%s' -p %s\n", 1418 redirect_url, 1419 provider_user_id, 1420 provider_legitimization_id, 1421 s); 1422 } 1423 else 1424 { 1425 fprintf (stdout, 1426 "Visit `%s' to begin KYC process.\nAlso use: taler-exchange-kyc-tester -w -U '%s' -p %s\n", 1427 redirect_url, 1428 provider_legitimization_id, 1429 s); 1430 } 1431 GNUNET_free (s); 1432 } 1433 GNUNET_free (cmd_provider_user_id); 1434 GNUNET_free (cmd_provider_legitimization_id); 1435 if (NULL != provider_user_id) 1436 cmd_provider_user_id = GNUNET_strdup (provider_user_id); 1437 if (NULL != provider_legitimization_id) 1438 cmd_provider_legitimization_id = GNUNET_strdup (provider_legitimization_id); 1439 if (! run_webservice) 1440 GNUNET_SCHEDULER_shutdown (); 1441 } 1442 1443 1444 /** 1445 * Function called to iterate over KYC-relevant 1446 * transaction amounts for a particular time range. 1447 * Called within a database transaction, so must 1448 * not start a new one. 1449 * 1450 * @param cls closure, identifies the event type and 1451 * account to iterate over events for 1452 * @param limit maximum time-range for which events 1453 * should be fetched (timestamp in the past) 1454 * @param cb function to call on each event found, 1455 * events must be returned in reverse chronological 1456 * order 1457 * @param cb_cls closure for @a cb 1458 * @return transaction status 1459 */ 1460 static enum GNUNET_DB_QueryStatus 1461 amount_iterator ( 1462 void *cls, 1463 struct GNUNET_TIME_Absolute limit, 1464 TALER_KYCLOGIC_KycAmountCallback cb, 1465 void *cb_cls) 1466 { 1467 const struct TALER_Amount *amount = cls; 1468 struct GNUNET_TIME_Absolute date; 1469 enum GNUNET_GenericReturnValue ret; 1470 1471 date = GNUNET_TIME_absolute_subtract (limit, 1472 GNUNET_TIME_UNIT_SECONDS); 1473 1474 ret = cb (cb_cls, 1475 amount, 1476 date); 1477 GNUNET_break (GNUNET_SYSERR != ret); 1478 if (GNUNET_OK != ret) 1479 return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; 1480 return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; 1481 } 1482 1483 1484 /** 1485 * Callback invoked on every listen socket to start the 1486 * respective MHD HTTP daemon. 1487 * 1488 * @param cls unused 1489 * @param lsock the listen socket 1490 */ 1491 static void 1492 start_daemon (void *cls, 1493 int lsock) 1494 { 1495 struct MHD_Daemon *mhd; 1496 1497 (void) cls; 1498 GNUNET_assert (-1 != lsock); 1499 mhd = MHD_start_daemon ( 1500 MHD_USE_SUSPEND_RESUME 1501 | MHD_USE_PIPE_FOR_SHUTDOWN 1502 | MHD_USE_DEBUG | MHD_USE_DUAL_STACK 1503 | MHD_USE_TCP_FASTOPEN, 1504 0, 1505 NULL, NULL, 1506 &handle_mhd_request, NULL, 1507 MHD_OPTION_LISTEN_SOCKET, 1508 lsock, 1509 MHD_OPTION_EXTERNAL_LOGGER, 1510 &TALER_MHD_handle_logs, 1511 NULL, 1512 MHD_OPTION_NOTIFY_COMPLETED, 1513 &handle_mhd_completion_callback, 1514 NULL, 1515 MHD_OPTION_END); 1516 if (NULL == mhd) 1517 { 1518 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1519 "Failed to launch HTTP service. Is the port in use?\n"); 1520 GNUNET_SCHEDULER_shutdown (); 1521 return; 1522 } 1523 have_daemons = true; 1524 TALER_MHD_daemon_start (mhd); 1525 } 1526 1527 1528 /** 1529 * Main function that will be run by the scheduler. 1530 * 1531 * @param cls closure 1532 * @param args remaining command-line arguments 1533 * @param cfgfile name of the configuration file used (for saving, can be 1534 * NULL!) 1535 * @param config configuration 1536 */ 1537 static void 1538 run (void *cls, 1539 char *const *args, 1540 const char *cfgfile, 1541 const struct GNUNET_CONFIGURATION_Handle *config) 1542 { 1543 enum TALER_KYCLOGIC_KycTriggerEvent event; 1544 const struct TALER_KYCLOGIC_KycRule *rule = NULL; 1545 1546 (void) cls; 1547 (void) args; 1548 (void ) cfgfile; 1549 if (GNUNET_OK != 1550 TALER_TEMPLATING_init (TALER_EXCHANGE_project_data ())) 1551 { 1552 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1553 "Could not load templates. Installation broken.\n"); 1554 global_ret = EXIT_FAILURE; 1555 return; 1556 } 1557 if (NULL != operation_s) 1558 { 1559 if (GNUNET_OK != 1560 TALER_KYCLOGIC_kyc_trigger_from_string (operation_s, 1561 &event)) 1562 { 1563 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1564 "Malformed operation type `%s'\n", 1565 operation_s); 1566 global_ret = EXIT_FAILURE; 1567 return; 1568 } 1569 } 1570 1571 if (NULL != lrs_s) 1572 { 1573 json_t *jlrs; 1574 json_error_t err; 1575 1576 jlrs = json_loads (lrs_s, 1577 JSON_REJECT_DUPLICATES, 1578 &err); 1579 if (NULL == jlrs) 1580 { 1581 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1582 "Malformed JSON for legitimization rule set: %s at %d\n", 1583 err.text, 1584 err.position); 1585 global_ret = EXIT_INVALIDARGUMENT; 1586 return; 1587 } 1588 lrs = TALER_KYCLOGIC_rules_parse (jlrs); 1589 json_decref (jlrs); 1590 if (NULL == lrs) 1591 { 1592 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1593 "Malformed legitimization rule set `%s'\n", 1594 lrs_s); 1595 global_ret = EXIT_INVALIDARGUMENT; 1596 return; 1597 } 1598 } 1599 1600 if (print_h_payto) 1601 { 1602 char *s; 1603 1604 s = GNUNET_STRINGS_data_to_string_alloc (&cmd_line_h_payto, 1605 sizeof (cmd_line_h_payto)); 1606 fprintf (stdout, 1607 "%s\n", 1608 s); 1609 GNUNET_free (s); 1610 } 1611 TALER_MHD_setup (TALER_MHD_GO_NONE); 1612 TEKT_cfg = config; 1613 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, 1614 NULL); 1615 if (GNUNET_OK != 1616 TALER_KYCLOGIC_kyc_init (config, 1617 cfgfile)) 1618 { 1619 global_ret = EXIT_NOTCONFIGURED; 1620 GNUNET_SCHEDULER_shutdown (); 1621 return; 1622 } 1623 if (GNUNET_OK != 1624 exchange_serve_process_config ()) 1625 { 1626 global_ret = EXIT_NOTCONFIGURED; 1627 GNUNET_SCHEDULER_shutdown (); 1628 return; 1629 } 1630 global_ret = EXIT_SUCCESS; 1631 if (NULL != operation_s) 1632 { 1633 enum GNUNET_DB_QueryStatus qs; 1634 1635 if (GNUNET_OK != 1636 TALER_amount_is_valid (&trigger_amount)) 1637 { 1638 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1639 "Trigger amount command-line option (-t) required\n"); 1640 global_ret = EXIT_INVALIDARGUMENT; 1641 GNUNET_SCHEDULER_shutdown (); 1642 return; 1643 } 1644 { 1645 struct TALER_Amount next_threshold; 1646 1647 qs = TALER_KYCLOGIC_kyc_test_required ( 1648 event, 1649 lrs, 1650 &amount_iterator, 1651 &trigger_amount, 1652 &rule, 1653 &next_threshold); 1654 } 1655 switch (qs) 1656 { 1657 case GNUNET_DB_STATUS_HARD_ERROR: 1658 GNUNET_break (0); 1659 global_ret = EXIT_NOTCONFIGURED; 1660 GNUNET_SCHEDULER_shutdown (); 1661 return; 1662 case GNUNET_DB_STATUS_SOFT_ERROR: 1663 GNUNET_break (0); 1664 global_ret = EXIT_NOTCONFIGURED; 1665 GNUNET_SCHEDULER_shutdown (); 1666 return; 1667 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 1668 fprintf (stdout, 1669 "KYC not required for the given operation type and amount\n"); 1670 global_ret = EXIT_SUCCESS; 1671 GNUNET_SCHEDULER_shutdown (); 1672 return; 1673 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 1674 break; 1675 } 1676 } 1677 1678 if (NULL != rule) 1679 { 1680 struct TALER_KYCLOGIC_KycCheckContext kcc; 1681 1682 if (0 != list_measures) 1683 { 1684 // FIXME: print rule with possible measures! 1685 GNUNET_break (0); 1686 global_ret = EXIT_SUCCESS; 1687 GNUNET_SCHEDULER_shutdown (); 1688 return; 1689 } 1690 1691 if (GNUNET_OK != 1692 TALER_KYCLOGIC_requirements_to_check (lrs, 1693 rule, 1694 measure, 1695 &kcc)) 1696 { 1697 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1698 "Could not initiate KYC for measure `%s' (configuration error?)\n", 1699 measure); 1700 global_ret = EXIT_NOTCONFIGURED; 1701 GNUNET_SCHEDULER_shutdown (); 1702 return; 1703 } 1704 if (NULL == kcc.check) 1705 { 1706 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 1707 "SKIP check selected, nothing to do here\n"); 1708 global_ret = EXIT_SUCCESS; 1709 GNUNET_SCHEDULER_shutdown (); 1710 return; 1711 } 1712 switch (kcc.check->type) 1713 { 1714 case TALER_KYCLOGIC_CT_INFO: 1715 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 1716 "KYC information is `%s'\n", 1717 kcc.check->description); 1718 break; 1719 case TALER_KYCLOGIC_CT_FORM: 1720 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 1721 "Would initiate KYC check `%s' with form `%s'\n", 1722 kcc.check->check_name, 1723 kcc.check->details.form.name); 1724 break; 1725 case TALER_KYCLOGIC_CT_LINK: 1726 { 1727 struct TALER_KYCLOGIC_ProviderDetails *pd; 1728 const char *provider_name; 1729 1730 TALER_KYCLOGIC_provider_to_logic ( 1731 kcc.check->details.link.provider, 1732 &ih_logic, 1733 &pd, 1734 &provider_name); 1735 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1736 "Initiating KYC check `%s' at provider `%s'\n", 1737 kcc.check->check_name, 1738 provider_name); 1739 ih = ih_logic->initiate (ih_logic->cls, 1740 pd, 1741 &cmd_line_h_payto, 1742 kyc_row_id, 1743 NULL, /* FIXME: support passing context*/ 1744 &initiate_cb, 1745 NULL); 1746 GNUNET_break (NULL != ih); 1747 break; 1748 } 1749 } 1750 } 1751 if (run_webservice) 1752 { 1753 enum GNUNET_GenericReturnValue ret; 1754 1755 TEKT_curl_ctx 1756 = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, 1757 &exchange_curl_rc); 1758 if (NULL == TEKT_curl_ctx) 1759 { 1760 GNUNET_break (0); 1761 global_ret = EXIT_FAILURE; 1762 GNUNET_SCHEDULER_shutdown (); 1763 return; 1764 } 1765 exchange_curl_rc = GNUNET_CURL_gnunet_rc_create (TEKT_curl_ctx); 1766 ret = TALER_MHD_listen_bind (TEKT_cfg, 1767 "exchange", 1768 &start_daemon, 1769 NULL); 1770 switch (ret) 1771 { 1772 case GNUNET_SYSERR: 1773 global_ret = EXIT_NOTCONFIGURED; 1774 GNUNET_SCHEDULER_shutdown (); 1775 return; 1776 case GNUNET_NO: 1777 if (! have_daemons) 1778 { 1779 global_ret = EXIT_NOTCONFIGURED; 1780 GNUNET_SCHEDULER_shutdown (); 1781 return; 1782 } 1783 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1784 "Could not open all configured listen sockets\n"); 1785 break; 1786 case GNUNET_OK: 1787 break; 1788 } 1789 } 1790 } 1791 1792 1793 /** 1794 * The main function of the taler-exchange-kyc-tester, a tool for 1795 * testing KYC processes. 1796 * 1797 * @param argc number of arguments from the command line 1798 * @param argv command line arguments 1799 * @return 0 ok, non-zero on error 1800 */ 1801 int 1802 main (int argc, 1803 char *const *argv) 1804 { 1805 const struct GNUNET_GETOPT_CommandLineOption options[] = { 1806 GNUNET_GETOPT_option_help ( 1807 TALER_EXCHANGE_project_data (), 1808 "tool to test KYC provider integrations"), 1809 GNUNET_GETOPT_option_flag ( 1810 'M', 1811 "list-measures", 1812 "list available measures", 1813 &list_measures), 1814 GNUNET_GETOPT_option_string ( 1815 'm', 1816 "measure", 1817 "MEASURE_NAME", 1818 "initiate KYC check for the selected measure", 1819 &measure), 1820 GNUNET_GETOPT_option_string ( 1821 'o', 1822 "operation", 1823 "OPERATION_TYPE", 1824 "name of the operation that triggers legitimization (WITHDRAW, DEPOSIT, etc.)", 1825 &operation_s), 1826 GNUNET_GETOPT_option_flag ( 1827 'P', 1828 "print-payto-hash", 1829 "output the hash of the (normalized) payto://-URI", 1830 &print_h_payto), 1831 GNUNET_GETOPT_option_base32_fixed_size ( 1832 'p', 1833 "payto-hash", 1834 "HASH", 1835 "base32 encoding of the hash of a payto://-URI to use for the account (otherwise a random value will be used)", 1836 &cmd_line_h_payto, 1837 sizeof (cmd_line_h_payto)), 1838 GNUNET_GETOPT_option_string ( 1839 'R', 1840 "ruleset", 1841 "JSON", 1842 "use the given legitimization rule set (otherwise defaults from configuration are used)", 1843 &lrs_s), 1844 GNUNET_GETOPT_option_uint ( 1845 'r', 1846 "rowid", 1847 "NUMBER", 1848 "override row ID to use in simulation (default: 42)", 1849 &kyc_row_id), 1850 TALER_getopt_get_amount ( 1851 't', 1852 "trigger", 1853 "AMOUNT", 1854 "threshold crossed that would trigger some legitimization rule", 1855 &trigger_amount), 1856 GNUNET_GETOPT_option_string ( 1857 'U', 1858 "legitimization", 1859 "ID", 1860 "use the given provider legitimization ID (overridden if -i is also used)", 1861 &cmd_provider_legitimization_id), 1862 GNUNET_GETOPT_option_string ( 1863 'u', 1864 "user", 1865 "ID", 1866 "use the given provider user ID (overridden if -i is also used)", 1867 &cmd_provider_user_id), 1868 GNUNET_GETOPT_option_flag ( 1869 'w', 1870 "run-webservice", 1871 "run the integrated HTTP service", 1872 &run_webservice), 1873 GNUNET_GETOPT_option_flag ( 1874 'W', 1875 "wallet-payto", 1876 "simulate that the address the KYC process is about is a wallet", 1877 &cmd_line_is_wallet), 1878 GNUNET_GETOPT_OPTION_END 1879 }; 1880 enum GNUNET_GenericReturnValue ret; 1881 1882 GNUNET_CRYPTO_random_block (&cmd_line_h_payto, 1883 sizeof (cmd_line_h_payto)); 1884 ret = GNUNET_PROGRAM_run (TALER_EXCHANGE_project_data (), 1885 argc, argv, 1886 "taler-exchange-kyc-tester", 1887 "tool to test KYC provider integrations", 1888 options, 1889 &run, NULL); 1890 if (GNUNET_SYSERR == ret) 1891 return EXIT_INVALIDARGUMENT; 1892 if (GNUNET_NO == ret) 1893 return EXIT_SUCCESS; 1894 return global_ret; 1895 } 1896 1897 1898 /* end of taler-exchange-kyc-tester.c */