taler-exchange-httpd_get-kyc-check-H_NORMALIZED_PAYTO.c (15980B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2021-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-httpd_get-kyc-check-H_NORMALIZED_PAYTO.c 18 * @brief Handle request for generic KYC check. 19 * @author Christian Grothoff 20 */ 21 #include <gnunet/gnunet_util_lib.h> 22 #include <gnunet/gnunet_json_lib.h> 23 #include <jansson.h> 24 #include <microhttpd.h> 25 #include <pthread.h> 26 #include "taler/taler_json_lib.h" 27 #include "taler/taler_kyclogic_lib.h" 28 #include "taler/taler_mhd_lib.h" 29 #include "taler/taler_dbevents.h" 30 #include "taler-exchange-httpd_get-keys.h" 31 #include "taler-exchange-httpd_get-kyc-check-H_NORMALIZED_PAYTO.h" 32 #include "taler-exchange-httpd_post-kyc-wallet.h" 33 #include "taler-exchange-httpd_responses.h" 34 #include "exchange-database/get_legitimization_requirement_by_row.h" 35 #include "exchange-database/event_listen.h" 36 #include "exchange-database/event_listen_cancel.h" 37 38 /** 39 * Reserve GET request that is long-polling. 40 */ 41 struct KycPoller 42 { 43 /** 44 * Kept in a DLL. 45 */ 46 struct KycPoller *next; 47 48 /** 49 * Kept in a DLL. 50 */ 51 struct KycPoller *prev; 52 53 /** 54 * Our request context. 55 */ 56 struct TEH_RequestContext *rc; 57 58 /** 59 * Connection we are handling. 60 */ 61 struct MHD_Connection *connection; 62 63 /** 64 * Subscription for the database event we are 65 * waiting for. 66 */ 67 struct GNUNET_DB_EventHandler *eh; 68 69 /** 70 * Account for which we perform the KYC check. 71 */ 72 struct TALER_NormalizedPaytoHashP h_payto; 73 74 /** 75 * When will this request time out? 76 */ 77 struct GNUNET_TIME_Absolute timeout; 78 79 /** 80 * Signature by the account owner authorizing this 81 * operation. 82 */ 83 union TALER_AccountSignatureP account_sig; 84 85 /** 86 * Public key from the account owner authorizing this 87 * operation. 88 */ 89 union TALER_AccountPublicKeyP account_pub; 90 91 /** 92 * Generation of KYC rules already known to the client 93 * (when long-polling). Do not send these rules again. 94 */ 95 uint64_t min_rule; 96 97 /** 98 * What are we long-polling for (if anything)? 99 */ 100 enum TALER_EXCHANGE_KycLongPollTarget lpt; 101 102 /** 103 * True if we are still suspended. 104 */ 105 bool suspended; 106 107 }; 108 109 110 /** 111 * Head of list of requests in long polling. 112 */ 113 static struct KycPoller *kyp_head; 114 115 /** 116 * Tail of list of requests in long polling. 117 */ 118 static struct KycPoller *kyp_tail; 119 120 121 void 122 TEH_kyc_check_cleanup () 123 { 124 struct KycPoller *kyp; 125 126 while (NULL != (kyp = kyp_head)) 127 { 128 GNUNET_CONTAINER_DLL_remove (kyp_head, 129 kyp_tail, 130 kyp); 131 if (kyp->suspended) 132 { 133 struct GNUNET_AsyncScopeSave old_scope; 134 135 GNUNET_async_scope_enter (&kyp->rc->async_scope_id, 136 &old_scope); 137 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 138 "Resuming connection on shutdown\n"); 139 kyp->suspended = false; 140 MHD_resume_connection (kyp->connection); 141 GNUNET_async_scope_restore (&old_scope); 142 } 143 } 144 } 145 146 147 /** 148 * Function called once a connection is done to 149 * clean up the `struct ReservePoller` state. 150 * 151 * @param rc context to clean up for 152 */ 153 static void 154 kyp_cleanup (struct TEH_RequestContext *rc) 155 { 156 struct KycPoller *kyp = rc->rh_ctx; 157 158 GNUNET_assert (! kyp->suspended); 159 if (NULL != kyp->eh) 160 { 161 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 162 "Cancelling DB event listening (KYP cleanup)\n"); 163 TALER_EXCHANGEDB_event_listen_cancel (TEH_pg, 164 kyp->eh); 165 kyp->eh = NULL; 166 } 167 GNUNET_free (kyp); 168 } 169 170 171 /** 172 * Function called on events received from Postgres. 173 * Wakes up long pollers. 174 * 175 * @param cls the `struct TEH_RequestContext *` 176 * @param extra additional event data provided 177 * @param extra_size number of bytes in @a extra 178 */ 179 static void 180 db_event_cb (void *cls, 181 const void *extra, 182 size_t extra_size) 183 { 184 struct TEH_RequestContext *rc = cls; 185 struct KycPoller *kyp = rc->rh_ctx; 186 struct GNUNET_AsyncScopeSave old_scope; 187 188 (void) extra; 189 (void) extra_size; 190 if (! kyp->suspended) 191 return; /* event triggered while main transaction 192 was still running, or got multiple wake-up events */ 193 kyp->suspended = false; 194 GNUNET_async_scope_enter (&rc->async_scope_id, 195 &old_scope); 196 TEH_check_invariants (); 197 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 198 "Resuming from long-polling on KYC status\n"); 199 GNUNET_CONTAINER_DLL_remove (kyp_head, 200 kyp_tail, 201 kyp); 202 MHD_resume_connection (kyp->connection); 203 TALER_MHD_daemon_trigger (); 204 TEH_check_invariants (); 205 GNUNET_async_scope_restore (&old_scope); 206 } 207 208 209 enum MHD_Result 210 TEH_handler_kyc_check ( 211 struct TEH_RequestContext *rc, 212 const char *const args[1]) 213 { 214 struct KycPoller *kyp = rc->rh_ctx; 215 json_t *jrules = NULL; 216 json_t *jlimits = NULL; 217 struct TALER_AccountAccessTokenP access_token; 218 bool aml_review; 219 bool kyc_required; 220 bool access_ok = false; 221 bool tos_required; 222 enum GNUNET_GenericReturnValue is_wallet; 223 uint64_t rule_gen = 0; 224 225 if (NULL == kyp) 226 { 227 bool sig_required = true; 228 bool acc_required = true; 229 230 kyp = GNUNET_new (struct KycPoller); 231 kyp->connection = rc->connection; 232 kyp->rc = rc; 233 rc->rh_ctx = kyp; 234 rc->rh_cleaner = &kyp_cleanup; 235 236 if (GNUNET_OK != 237 GNUNET_STRINGS_string_to_data (args[0], 238 strlen (args[0]), 239 &kyp->h_payto, 240 sizeof (kyp->h_payto))) 241 { 242 GNUNET_break_op (0); 243 return TALER_MHD_reply_with_error ( 244 rc->connection, 245 MHD_HTTP_BAD_REQUEST, 246 TALER_EC_GENERIC_PATH_SEGMENT_MALFORMED, 247 "h_payto"); 248 } 249 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 250 "Checking KYC status for normalized payto hash %s\n", 251 args[0]); 252 TALER_MHD_parse_request_header_auto ( 253 rc->connection, 254 TALER_HTTP_HEADER_ACCOUNT_OWNER_SIGNATURE, 255 &kyp->account_sig, 256 sig_required); 257 TALER_MHD_parse_request_header_auto ( 258 rc->connection, 259 TALER_HTTP_HEADER_ACCOUNT_OWNER_PUBKEY, 260 &kyp->account_pub, 261 acc_required); 262 TALER_MHD_parse_request_timeout (rc->connection, 263 &kyp->timeout); 264 { 265 uint64_t num = 0; 266 int val; 267 268 TALER_MHD_parse_request_number (rc->connection, 269 "lpt", 270 &num); 271 val = (int) num; 272 if ( (val < 0) || 273 (val > TALER_EXCHANGE_KLPT_MAX) ) 274 { 275 /* Protocol violation, but we can be graceful and 276 just ignore the long polling! */ 277 GNUNET_break_op (0); 278 val = TALER_EXCHANGE_KLPT_NONE; 279 } 280 kyp->lpt = (enum TALER_EXCHANGE_KycLongPollTarget) val; 281 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 282 "Long polling for target %d with timeout %s\n", 283 val, 284 GNUNET_TIME_relative2s ( 285 GNUNET_TIME_absolute_get_remaining ( 286 kyp->timeout), 287 true)); 288 } 289 TALER_MHD_parse_request_number (rc->connection, 290 "min_rule", 291 &kyp->min_rule); 292 /* long polling needed? */ 293 if (GNUNET_TIME_absolute_is_future (kyp->timeout)) 294 { 295 struct TALER_EXCHANGEDB_KycCompletedEventP rep = { 296 .header.size = htons (sizeof (rep)), 297 .header.type = htons (TALER_DBEVENT_EXCHANGE_KYC_COMPLETED), 298 .h_payto = kyp->h_payto 299 }; 300 301 kyp->eh = TALER_EXCHANGEDB_event_listen ( 302 TEH_pg, 303 GNUNET_TIME_absolute_get_remaining (kyp->timeout), 304 &rep.header, 305 &db_event_cb, 306 rc); 307 } 308 } /* end initialization */ 309 310 if (! TEH_enable_kyc) 311 { 312 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 313 "KYC not enabled\n"); 314 return TALER_MHD_reply_static ( 315 rc->connection, 316 MHD_HTTP_NO_CONTENT, 317 NULL, 318 NULL, 319 0); 320 } 321 322 { 323 enum GNUNET_DB_QueryStatus qs; 324 bool do_suspend; 325 326 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 327 "Looking up KYC requirements for account %s\n", 328 TALER_B2S (&kyp->h_payto)); 329 qs = TALER_EXCHANGEDB_get_legitimization_requirement_by_row ( 330 TEH_pg, 331 &kyp->h_payto, 332 &kyp->account_pub, 333 &is_wallet, 334 &access_token, 335 &rule_gen, 336 &jrules, 337 &aml_review, 338 &kyc_required); 339 if (qs < 0) 340 { 341 GNUNET_break (0); 342 return TALER_MHD_reply_with_ec ( 343 rc->connection, 344 TALER_EC_GENERIC_DB_STORE_FAILED, 345 "lookup_kyc_requirement_by_row"); 346 } 347 348 do_suspend = false; 349 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 350 { 351 /* account unknown */ 352 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 353 "Account unknown!\n"); 354 if ( (TALER_EXCHANGE_KLPT_NONE != kyp->lpt) && 355 (TALER_EXCHANGE_KLPT_KYC_OK != kyp->lpt) && 356 (GNUNET_TIME_absolute_is_future (kyp->timeout)) ) 357 { 358 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 359 "Suspending due to account unknown!\n"); 360 do_suspend = true; 361 access_ok = true; /* for now */ 362 } 363 } 364 else 365 { 366 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 367 "Found rule %llu (client wants > %llu, %d)\n", 368 (unsigned long long) rule_gen, 369 (unsigned long long) kyp->min_rule, 370 (int) kyp->lpt); 371 access_ok = 372 (GNUNET_OK == 373 TALER_account_kyc_auth_verify (&kyp->account_pub, 374 &kyp->account_sig)); 375 if (GNUNET_TIME_absolute_is_future (kyp->timeout) && 376 (rule_gen <= kyp->min_rule) ) 377 { 378 switch (kyp->lpt) 379 { 380 case TALER_EXCHANGE_KLPT_NONE: 381 /* If KLPT is not given, just go for rule generation */ 382 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 383 "Suspending until rule generation changes\n"); 384 do_suspend = true; 385 break; 386 case TALER_EXCHANGE_KLPT_KYC_AUTH_TRANSFER: 387 if (! access_ok) 388 { 389 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 390 "Waiting for auth transfer, access NOT OK, suspending again\n"); 391 do_suspend = true; 392 } 393 break; 394 case TALER_EXCHANGE_KLPT_INVESTIGATION_DONE: 395 if (! aml_review) 396 { 397 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 398 "Waiting for investigation complete, investigation ongoing, suspending again\n"); 399 do_suspend = true; 400 } 401 break; 402 case TALER_EXCHANGE_KLPT_KYC_OK: 403 if (kyc_required) 404 { 405 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 406 "Waiting for KYC OK, but KYC NOT OK, suspending again\n"); 407 do_suspend = true; 408 } 409 break; 410 } 411 } 412 } 413 414 if (do_suspend && 415 (access_ok || 416 (TALER_EXCHANGE_KLPT_KYC_AUTH_TRANSFER == kyp->lpt) ) ) 417 { 418 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 419 "Suspending HTTP request on timeout (%s) for %d\n", 420 GNUNET_TIME_relative2s (GNUNET_TIME_absolute_get_remaining ( 421 kyp->timeout), 422 true), 423 (int) kyp->lpt); 424 GNUNET_assert (NULL != kyp->eh); 425 GNUNET_assert (! kyp->suspended); 426 kyp->suspended = true; 427 GNUNET_CONTAINER_DLL_insert (kyp_head, 428 kyp_tail, 429 kyp); 430 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 431 "Telling MHD to suspend connection\n"); 432 MHD_suspend_connection (kyp->connection); 433 json_decref (jrules); 434 return MHD_YES; 435 } 436 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 437 { 438 GNUNET_break (NULL == jrules); 439 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 440 "Returning account unknown\n"); 441 return TALER_MHD_reply_with_error ( 442 rc->connection, 443 MHD_HTTP_NOT_FOUND, 444 TALER_EC_EXCHANGE_KYC_CHECK_REQUEST_UNKNOWN, 445 NULL); 446 } 447 } 448 449 if (! access_ok) 450 { 451 json_decref (jrules); 452 jrules = NULL; 453 if (GNUNET_is_zero (&kyp->account_pub)) 454 { 455 GNUNET_break_op (0); 456 return TALER_MHD_reply_with_error ( 457 rc->connection, 458 MHD_HTTP_CONFLICT, 459 TALER_EC_EXCHANGE_KYC_CHECK_AUTHORIZATION_KEY_UNKNOWN, 460 NULL); 461 } 462 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 463 "Returning authorization failed\n"); 464 return TALER_MHD_REPLY_JSON_PACK ( 465 rc->connection, 466 MHD_HTTP_FORBIDDEN, 467 TALER_JSON_pack_ec ( 468 TALER_EC_EXCHANGE_KYC_CHECK_AUTHORIZATION_FAILED), 469 GNUNET_JSON_pack_data_auto ("expected_account_pub", 470 &kyp->account_pub)); 471 } 472 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 473 "KYC rules apply to %s:\n", 474 (GNUNET_SYSERR == is_wallet) 475 ? "unknown account type" 476 : ( (GNUNET_YES == is_wallet) 477 ? "wallet" 478 : "account")); 479 tos_required = false; 480 if ( (TEH_kyc_swap_tos_acceptance) && 481 (NULL != TEH_tos_etag) ) 482 { 483 /* #11183: With the terms-of-service / KYC-auth swap enabled, and if 484 the rule set that applies to this account actually requires the 485 client to accept our terms of service, surface that requirement 486 up-front by returning the current ToS ETag below. This lets the 487 merchant backend submit a previously collected acceptance on the 488 user's behalf (via the accept-tos requirement it finds under 489 /kyc-info) instead of forcing the user to interact again. We 490 inspect the rules precisely (rather than just whether *some* KYC 491 is required), as the rule set may demand entirely different 492 measures here. */ 493 tos_required = 494 TALER_KYCLOGIC_rules_require_tos_acceptance (jrules); 495 } 496 jlimits = TALER_KYCLOGIC_rules_to_limits (jrules, 497 is_wallet); 498 if (NULL == jlimits) 499 { 500 GNUNET_break_op (0); 501 json_decref (jrules); 502 jrules = NULL; 503 return TALER_MHD_reply_with_error ( 504 rc->connection, 505 MHD_HTTP_INTERNAL_SERVER_ERROR, 506 TALER_EC_GENERIC_DB_INVARIANT_FAILURE, 507 "/kyc-check: rules_to_limits failed"); 508 } 509 json_decref (jrules); 510 jrules = NULL; 511 512 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 513 "Returning KYC %s\n", 514 kyc_required ? "required" : "optional"); 515 return TALER_MHD_REPLY_JSON_PACK ( 516 rc->connection, 517 kyc_required 518 ? MHD_HTTP_ACCEPTED 519 : MHD_HTTP_OK, 520 GNUNET_JSON_pack_bool ("aml_review", 521 aml_review), 522 GNUNET_JSON_pack_uint64 ("rule_gen", 523 rule_gen), 524 GNUNET_JSON_pack_data_auto ("access_token", 525 &access_token), 526 GNUNET_JSON_pack_allow_null ( 527 GNUNET_JSON_pack_string ("tos_required", 528 tos_required 529 ? TEH_tos_etag 530 : NULL)), 531 GNUNET_JSON_pack_allow_null ( 532 GNUNET_JSON_pack_array_steal ("limits", 533 jlimits))); 534 } 535 536 537 /* end of taler-exchange-httpd_kyc-check.c */