Line data Source code
1 : /*
2 : This file is part of GNU Taler
3 : (C) 2021-2024 Taler Systems SA
4 :
5 : GNU Taler is free software; you can redistribute it and/or modify
6 : it under the terms of the GNU Affero General Public License as
7 : published by the Free Software Foundation; either version 3,
8 : or (at your option) any later version.
9 :
10 : GNU Taler is distributed in the hope that it will be useful, but
11 : WITHOUT ANY WARRANTY; without even the implied warranty of
12 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 : GNU General Public License for more details.
14 :
15 : You should have received a copy of the GNU General Public
16 : License along with TALER; see the file COPYING. If not,
17 : see <http://www.gnu.org/licenses/>
18 : */
19 :
20 : /**
21 : * @file taler-merchant-httpd_private-get-instances-ID-kyc.c
22 : * @brief implementing GET /instances/$ID/kyc request handling
23 : * @author Christian Grothoff
24 : */
25 : #include "platform.h"
26 : #include "taler-merchant-httpd_private-get-instances-ID-kyc.h"
27 : #include "taler-merchant-httpd_helper.h"
28 : #include "taler-merchant-httpd_exchanges.h"
29 : #include <taler/taler_json_lib.h>
30 : #include <taler/taler_dbevents.h>
31 : #include <regex.h>
32 :
33 : /**
34 : * Information we keep per /kyc request.
35 : */
36 : struct KycContext;
37 :
38 :
39 : /**
40 : * Structure for tracking requests to the exchange's
41 : * ``/kyc-check`` API.
42 : */
43 : struct ExchangeKycRequest
44 : {
45 : /**
46 : * Kept in a DLL.
47 : */
48 : struct ExchangeKycRequest *next;
49 :
50 : /**
51 : * Kept in a DLL.
52 : */
53 : struct ExchangeKycRequest *prev;
54 :
55 : /**
56 : * Find operation where we connect to the respective exchange.
57 : */
58 : struct TMH_EXCHANGES_KeysOperation *fo;
59 :
60 : /**
61 : * JSON array of payto-URIs with KYC auth wire transfer
62 : * instructions. Provided if @e auth_ok is false and
63 : * @e kyc_auth_conflict is false.
64 : */
65 : json_t *pkaa;
66 :
67 : /**
68 : * The keys of the exchange.
69 : */
70 : struct TALER_EXCHANGE_Keys *keys;
71 :
72 : /**
73 : * KYC request this exchange request is made for.
74 : */
75 : struct KycContext *kc;
76 :
77 : /**
78 : * JSON array of AccountLimits that apply, NULL if
79 : * unknown (and likely defaults apply).
80 : */
81 : json_t *jlimits;
82 :
83 : /**
84 : * Our account's payto URI.
85 : */
86 : struct TALER_FullPayto payto_uri;
87 :
88 : /**
89 : * Base URL of the exchange.
90 : */
91 : char *exchange_url;
92 :
93 : /**
94 : * Hash of the wire account (with salt) we are checking.
95 : */
96 : struct TALER_MerchantWireHashP h_wire;
97 :
98 : /**
99 : * Current access token for the KYC SPA. Only set
100 : * if @e auth_ok is true.
101 : */
102 : struct TALER_AccountAccessTokenP access_token;
103 :
104 : /**
105 : * Timestamp when we last got a reply from the exchange.
106 : */
107 : struct GNUNET_TIME_Timestamp last_check;
108 :
109 : /**
110 : * Last HTTP status code obtained via /kyc-check from
111 : * the exchange.
112 : */
113 : unsigned int last_http_status;
114 :
115 : /**
116 : * Last Taler error code returned from /kyc-check.
117 : */
118 : enum TALER_ErrorCode last_ec;
119 :
120 : /**
121 : * True if this account
122 : * cannot work at this exchange because KYC auth is
123 : * impossible.
124 : */
125 : bool kyc_auth_conflict;
126 :
127 : /**
128 : * We could not get /keys from the exchange.
129 : */
130 : bool no_keys;
131 :
132 : /**
133 : * True if @e access_token is available.
134 : */
135 : bool auth_ok;
136 :
137 : /**
138 : * True if we believe no KYC is currently required
139 : * for this account at this exchange.
140 : */
141 : bool kyc_ok;
142 :
143 : /**
144 : * True if the exchange exposed to us that the account
145 : * is currently under AML review.
146 : */
147 : bool in_aml_review;
148 :
149 :
150 : };
151 :
152 :
153 : /**
154 : * Information we keep per /kyc request.
155 : */
156 : struct KycContext
157 : {
158 : /**
159 : * Stored in a DLL.
160 : */
161 : struct KycContext *next;
162 :
163 : /**
164 : * Stored in a DLL.
165 : */
166 : struct KycContext *prev;
167 :
168 : /**
169 : * Connection we are handling.
170 : */
171 : struct MHD_Connection *connection;
172 :
173 : /**
174 : * Instance we are serving.
175 : */
176 : struct TMH_MerchantInstance *mi;
177 :
178 : /**
179 : * Our handler context.
180 : */
181 : struct TMH_HandlerContext *hc;
182 :
183 : /**
184 : * Response to return, NULL if we don't have one yet.
185 : */
186 : struct MHD_Response *response;
187 :
188 : /**
189 : * JSON array where we are building up the array with
190 : * pending KYC operations.
191 : */
192 : json_t *kycs_data;
193 :
194 : /**
195 : * Head of DLL of requests we are making to an
196 : * exchange to inquire about the latest KYC status.
197 : */
198 : struct ExchangeKycRequest *exchange_pending_head;
199 :
200 : /**
201 : * Tail of DLL of requests we are making to an
202 : * exchange to inquire about the latest KYC status.
203 : */
204 : struct ExchangeKycRequest *exchange_pending_tail;
205 :
206 : /**
207 : * Set to the exchange URL, or NULL to not filter by
208 : * exchange.
209 : */
210 : const char *exchange_url;
211 :
212 : /**
213 : * Notification handler from database on changes
214 : * to the KYC status.
215 : */
216 : struct GNUNET_DB_EventHandler *eh;
217 :
218 : /**
219 : * Set to the h_wire of the merchant account if
220 : * @a have_h_wire is true, used to filter by account.
221 : */
222 : struct TALER_MerchantWireHashP h_wire;
223 :
224 : /**
225 : * How long are we willing to wait for the exchange(s)?
226 : */
227 : struct GNUNET_TIME_Absolute timeout;
228 :
229 : /**
230 : * HTTP status code to use for the reply, i.e 200 for "OK".
231 : * Special value UINT_MAX is used to indicate hard errors
232 : * (no reply, return #MHD_NO).
233 : */
234 : unsigned int response_code;
235 :
236 : /**
237 : * #GNUNET_NO if the @e connection was not suspended,
238 : * #GNUNET_YES if the @e connection was suspended,
239 : * #GNUNET_SYSERR if @e connection was resumed to as
240 : * part of #MH_force_pc_resume during shutdown.
241 : */
242 : enum GNUNET_GenericReturnValue suspended;
243 :
244 : /**
245 : * What state are we long-polling for?
246 : */
247 : enum TALER_EXCHANGE_KycLongPollTarget lpt;
248 :
249 : /**
250 : * True if @e h_wire was given.
251 : */
252 : bool have_h_wire;
253 :
254 : /**
255 : * We're still waiting on the exchange to determine
256 : * the KYC status of our deposit(s).
257 : */
258 : bool return_immediately;
259 :
260 : };
261 :
262 :
263 : /**
264 : * Head of DLL.
265 : */
266 : static struct KycContext *kc_head;
267 :
268 : /**
269 : * Tail of DLL.
270 : */
271 : static struct KycContext *kc_tail;
272 :
273 :
274 : void
275 15 : TMH_force_kyc_resume ()
276 : {
277 15 : for (struct KycContext *kc = kc_head;
278 15 : NULL != kc;
279 0 : kc = kc->next)
280 : {
281 0 : if (GNUNET_YES == kc->suspended)
282 : {
283 0 : kc->suspended = GNUNET_SYSERR;
284 0 : MHD_resume_connection (kc->connection);
285 : }
286 : }
287 15 : }
288 :
289 :
290 : /**
291 : * Custom cleanup routine for a `struct KycContext`.
292 : *
293 : * @param cls the `struct KycContext` to clean up.
294 : */
295 : static void
296 9 : kyc_context_cleanup (void *cls)
297 : {
298 9 : struct KycContext *kc = cls;
299 : struct ExchangeKycRequest *ekr;
300 :
301 9 : while (NULL != (ekr = kc->exchange_pending_head))
302 : {
303 0 : GNUNET_CONTAINER_DLL_remove (kc->exchange_pending_head,
304 : kc->exchange_pending_tail,
305 : ekr);
306 0 : if (NULL != ekr->fo)
307 : {
308 0 : TMH_EXCHANGES_keys4exchange_cancel (ekr->fo);
309 0 : ekr->fo = NULL;
310 : }
311 0 : json_decref (ekr->pkaa);
312 0 : json_decref (ekr->jlimits);
313 0 : if (NULL != ekr->keys)
314 0 : TALER_EXCHANGE_keys_decref (ekr->keys);
315 0 : GNUNET_free (ekr->exchange_url);
316 0 : GNUNET_free (ekr->payto_uri.full_payto);
317 0 : GNUNET_free (ekr);
318 : }
319 9 : if (NULL != kc->eh)
320 : {
321 4 : TMH_db->event_listen_cancel (kc->eh);
322 4 : kc->eh = NULL;
323 : }
324 9 : if (NULL != kc->response)
325 : {
326 7 : MHD_destroy_response (kc->response);
327 7 : kc->response = NULL;
328 : }
329 9 : GNUNET_CONTAINER_DLL_remove (kc_head,
330 : kc_tail,
331 : kc);
332 9 : json_decref (kc->kycs_data);
333 9 : GNUNET_free (kc);
334 9 : }
335 :
336 :
337 : /**
338 : * Resume the given KYC context and send the final response. Stores the
339 : * response in the @a kc and signals MHD to resume the connection. Also
340 : * ensures MHD runs immediately.
341 : *
342 : * @param kc KYC context
343 : */
344 : static void
345 7 : resume_kyc_with_response (struct KycContext *kc)
346 : {
347 7 : kc->response_code = MHD_HTTP_OK;
348 7 : kc->response = TALER_MHD_MAKE_JSON_PACK (
349 : GNUNET_JSON_pack_array_incref ("kyc_data",
350 : kc->kycs_data));
351 7 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
352 : "Resuming /kyc handling as exchange interaction is done (%u)\n",
353 : MHD_HTTP_OK);
354 7 : if (GNUNET_YES == kc->suspended)
355 : {
356 3 : kc->suspended = GNUNET_NO;
357 3 : MHD_resume_connection (kc->connection);
358 3 : TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */
359 : }
360 7 : }
361 :
362 :
363 : /**
364 : * Handle a DB event about an update relevant
365 : * for the processing of the kyc request.
366 : *
367 : * @param cls our `struct KycContext`
368 : * @param extra additional event data provided
369 : * @param extra_size number of bytes in @a extra
370 : */
371 : static void
372 1 : kyc_change_cb (void *cls,
373 : const void *extra,
374 : size_t extra_size)
375 : {
376 1 : struct KycContext *kc = cls;
377 :
378 1 : if (GNUNET_YES == kc->suspended)
379 : {
380 1 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
381 : "Resuming KYC with gateway timeout\n");
382 1 : kc->suspended = GNUNET_NO;
383 1 : MHD_resume_connection (kc->connection);
384 1 : TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */
385 : }
386 1 : }
387 :
388 :
389 : /**
390 : * Pack the given @a limit into the JSON @a limits array.
391 : *
392 : * @param limit account limit to pack
393 : * @param[in,out] limits JSON array to extend
394 : */
395 : static void
396 0 : pack_limit (const struct TALER_EXCHANGE_AccountLimit *limit,
397 : json_t *limits)
398 : {
399 : json_t *jl;
400 :
401 0 : jl = GNUNET_JSON_PACK (
402 : TALER_JSON_pack_kycte ("operation_type",
403 : limit->operation_type),
404 : GNUNET_JSON_pack_time_rel ("timeframe",
405 : limit->timeframe),
406 : TALER_JSON_pack_amount ("threshold",
407 : &limit->threshold),
408 : GNUNET_JSON_pack_bool ("soft_limit",
409 : limit->soft_limit)
410 : );
411 0 : GNUNET_assert (0 ==
412 : json_array_append_new (limits,
413 : jl));
414 0 : }
415 :
416 :
417 : /**
418 : * Return JSON array with AccountLimit objects giving
419 : * the current limits for this exchange.
420 : *
421 : * @param[in,out] ekr overall request context
422 : */
423 : static json_t *
424 9 : get_exchange_limits (
425 : struct ExchangeKycRequest *ekr)
426 : {
427 9 : const struct TALER_EXCHANGE_Keys *keys = ekr->keys;
428 : json_t *limits;
429 :
430 9 : if (NULL != ekr->jlimits)
431 : {
432 4 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
433 : "Returning custom KYC limits\n");
434 4 : return json_incref (ekr->jlimits);
435 : }
436 5 : if (NULL == keys)
437 : {
438 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
439 : "No keys, thus no default KYC limits known\n");
440 0 : return NULL;
441 : }
442 5 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
443 : "Returning default KYC limits (%u/%u)\n",
444 : keys->hard_limits_length,
445 : keys->zero_limits_length);
446 5 : limits = json_array ();
447 5 : GNUNET_assert (NULL != limits);
448 5 : for (unsigned int i = 0; i<keys->hard_limits_length; i++)
449 : {
450 0 : const struct TALER_EXCHANGE_AccountLimit *limit
451 0 : = &keys->hard_limits[i];
452 :
453 0 : pack_limit (limit,
454 : limits);
455 : }
456 7 : for (unsigned int i = 0; i<keys->zero_limits_length; i++)
457 : {
458 2 : const struct TALER_EXCHANGE_ZeroLimitedOperation *zlimit
459 2 : = &keys->zero_limits[i];
460 : json_t *jl;
461 : struct TALER_Amount zero;
462 :
463 2 : GNUNET_assert (GNUNET_OK ==
464 : TALER_amount_set_zero (keys->currency,
465 : &zero));
466 2 : jl = GNUNET_JSON_PACK (
467 : TALER_JSON_pack_kycte ("operation_type",
468 : zlimit->operation_type),
469 : GNUNET_JSON_pack_time_rel ("timeframe",
470 : GNUNET_TIME_UNIT_ZERO),
471 : TALER_JSON_pack_amount ("threshold",
472 : &zero),
473 : GNUNET_JSON_pack_bool ("soft_limit",
474 : true)
475 : );
476 2 : GNUNET_assert (0 ==
477 : json_array_append_new (limits,
478 : jl));
479 : }
480 5 : return limits;
481 : }
482 :
483 :
484 : /**
485 : * Maps @a ekr to a status code for clients to interpret the
486 : * overall result.
487 : *
488 : * @param ekr request summary
489 : * @return status of the KYC state as a string
490 : */
491 : static const char *
492 9 : map_to_status (const struct ExchangeKycRequest *ekr)
493 : {
494 9 : if (ekr->no_keys)
495 : {
496 0 : return "no-exchange-keys";
497 : }
498 9 : if (ekr->kyc_ok)
499 : {
500 7 : if (NULL != ekr->jlimits)
501 : {
502 : size_t off;
503 : json_t *limit;
504 4 : json_array_foreach (ekr->jlimits, off, limit)
505 : {
506 : struct TALER_Amount threshold;
507 : enum TALER_KYCLOGIC_KycTriggerEvent operation_type;
508 4 : bool soft = false;
509 : struct GNUNET_JSON_Specification spec[] = {
510 4 : TALER_JSON_spec_kycte ("operation_type",
511 : &operation_type),
512 4 : TALER_JSON_spec_amount_any ("threshold",
513 : &threshold),
514 4 : GNUNET_JSON_spec_mark_optional (
515 : GNUNET_JSON_spec_bool ("soft_limit",
516 : &soft),
517 : NULL),
518 4 : GNUNET_JSON_spec_end ()
519 : };
520 :
521 4 : if (GNUNET_OK !=
522 4 : GNUNET_JSON_parse (limit,
523 : spec,
524 : NULL, NULL))
525 : {
526 0 : GNUNET_break (0);
527 4 : return "merchant-internal-error";
528 : }
529 4 : if (! TALER_amount_is_zero (&threshold))
530 0 : continue; /* only care about zero-limits */
531 4 : if (! soft)
532 0 : continue; /* only care about soft limits */
533 4 : if ( (operation_type == TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT) ||
534 4 : (operation_type == TALER_KYCLOGIC_KYC_TRIGGER_AGGREGATE) ||
535 0 : (operation_type == TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION) )
536 : {
537 4 : if (! ekr->auth_ok)
538 : {
539 0 : if (ekr->kyc_auth_conflict)
540 0 : return "kyc-wire-impossible";
541 0 : return "kyc-wire-required";
542 : }
543 4 : return "kyc-required";
544 : }
545 : }
546 : }
547 3 : if (NULL == ekr->jlimits)
548 : {
549 : /* check default limits */
550 3 : const struct TALER_EXCHANGE_Keys *keys = ekr->keys;
551 :
552 3 : for (unsigned int i = 0; i < keys->zero_limits_length; i++)
553 : {
554 0 : enum TALER_KYCLOGIC_KycTriggerEvent operation_type
555 0 : = keys->zero_limits[i].operation_type;
556 :
557 0 : if ( (operation_type == TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT) ||
558 0 : (operation_type == TALER_KYCLOGIC_KYC_TRIGGER_AGGREGATE) ||
559 : (operation_type == TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION) )
560 : {
561 0 : if (! ekr->auth_ok)
562 : {
563 0 : if (ekr->kyc_auth_conflict)
564 0 : return "kyc-wire-impossible";
565 0 : return "kyc-wire-required";
566 : }
567 0 : return "kyc-required";
568 : }
569 : }
570 : }
571 3 : return "ready";
572 : }
573 2 : if (! ekr->auth_ok)
574 : {
575 2 : if (ekr->kyc_auth_conflict)
576 0 : return "kyc-wire-impossible";
577 2 : return "kyc-wire-required";
578 : }
579 0 : if (ekr->in_aml_review)
580 0 : return "awaiting-aml-review";
581 0 : switch (ekr->last_http_status)
582 : {
583 0 : case 0:
584 0 : return "exchange-unreachable";
585 0 : case MHD_HTTP_OK:
586 : /* then we should have kyc_ok */
587 0 : GNUNET_break (0);
588 0 : return NULL;
589 0 : case MHD_HTTP_ACCEPTED:
590 : /* Then KYC is really what is needed */
591 0 : return "kyc-required";
592 0 : case MHD_HTTP_NO_CONTENT:
593 : /* then we should have had kyc_ok! */
594 0 : GNUNET_break (0);
595 0 : return NULL;
596 0 : case MHD_HTTP_FORBIDDEN:
597 : /* then we should have had ! auth_ok */
598 0 : GNUNET_break (0);
599 0 : return NULL;
600 0 : case MHD_HTTP_NOT_FOUND:
601 : /* then we should have had ! auth_ok */
602 0 : GNUNET_break (0);
603 0 : return NULL;
604 0 : case MHD_HTTP_CONFLICT:
605 : /* then we should have had ! auth_ok */
606 0 : GNUNET_break (0);
607 0 : return NULL;
608 0 : case MHD_HTTP_INTERNAL_SERVER_ERROR:
609 0 : return "exchange-internal-error";
610 0 : case MHD_HTTP_GATEWAY_TIMEOUT:
611 0 : return "exchange-gateway-timeout";
612 0 : default:
613 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
614 : "Exchange responded with unexpected HTTP status %u to /kyc-check request!\n",
615 : ekr->last_http_status);
616 0 : break;
617 : }
618 0 : return "exchange-status-invalid";
619 : }
620 :
621 :
622 : /**
623 : * Take data from @a ekr to expand our response.
624 : *
625 : * @param ekr exchange we are done inspecting
626 : */
627 : static void
628 9 : ekr_expand_response (struct ExchangeKycRequest *ekr)
629 : {
630 : const char *status;
631 :
632 9 : status = map_to_status (ekr);
633 9 : if (NULL == status)
634 : {
635 0 : GNUNET_break (0);
636 0 : status = "logic-bug";
637 : }
638 9 : GNUNET_assert (
639 : 0 ==
640 : json_array_append_new (
641 : ekr->kc->kycs_data,
642 : GNUNET_JSON_PACK (
643 : TALER_JSON_pack_full_payto (
644 : "payto_uri",
645 : ekr->payto_uri),
646 : GNUNET_JSON_pack_data_auto (
647 : "h_wire",
648 : &ekr->h_wire),
649 : GNUNET_JSON_pack_string (
650 : "status",
651 : status),
652 : GNUNET_JSON_pack_string (
653 : "exchange_url",
654 : ekr->exchange_url),
655 : GNUNET_JSON_pack_bool ("no_keys",
656 : ekr->no_keys),
657 : GNUNET_JSON_pack_bool ("auth_conflict",
658 : ekr->kyc_auth_conflict),
659 : GNUNET_JSON_pack_uint64 ("exchange_http_status",
660 : ekr->last_http_status),
661 : (TALER_EC_NONE == ekr->last_ec)
662 : ? GNUNET_JSON_pack_allow_null (
663 : GNUNET_JSON_pack_string (
664 : "dummy",
665 : NULL))
666 : : GNUNET_JSON_pack_uint64 ("exchange_code",
667 : ekr->last_ec),
668 : ekr->auth_ok
669 : ? GNUNET_JSON_pack_data_auto (
670 : "access_token",
671 : &ekr->access_token)
672 : : GNUNET_JSON_pack_allow_null (
673 : GNUNET_JSON_pack_string (
674 : "dummy",
675 : NULL)),
676 : GNUNET_JSON_pack_allow_null (
677 : GNUNET_JSON_pack_array_steal (
678 : "limits",
679 : get_exchange_limits (ekr))),
680 : GNUNET_JSON_pack_allow_null (
681 : GNUNET_JSON_pack_array_incref ("payto_kycauths",
682 : ekr->pkaa))
683 : )));
684 9 : }
685 :
686 :
687 : /**
688 : * We are done with the KYC request @a ekr. Remove it from the work list and
689 : * check if we are done overall.
690 : *
691 : * @param[in] ekr key request that is done (and will be freed)
692 : */
693 : static void
694 9 : ekr_finished (struct ExchangeKycRequest *ekr)
695 : {
696 9 : struct KycContext *kc = ekr->kc;
697 :
698 9 : ekr_expand_response (ekr);
699 9 : GNUNET_CONTAINER_DLL_remove (kc->exchange_pending_head,
700 : kc->exchange_pending_tail,
701 : ekr);
702 9 : json_decref (ekr->jlimits);
703 9 : json_decref (ekr->pkaa);
704 9 : if (NULL != ekr->keys)
705 5 : TALER_EXCHANGE_keys_decref (ekr->keys);
706 9 : GNUNET_free (ekr->exchange_url);
707 9 : GNUNET_free (ekr->payto_uri.full_payto);
708 9 : GNUNET_free (ekr);
709 :
710 9 : if (NULL != kc->exchange_pending_head)
711 1 : return; /* wait for more */
712 :
713 8 : if ( (! kc->return_immediately) &&
714 1 : (! GNUNET_TIME_absolute_is_past (kc->timeout)) )
715 : {
716 1 : if (GNUNET_NO == kc->suspended)
717 : {
718 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
719 : "Suspending: long poll target %d not reached\n",
720 : kc->lpt);
721 0 : MHD_suspend_connection (kc->connection);
722 0 : kc->suspended = GNUNET_YES;
723 : }
724 : else
725 : {
726 1 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
727 : "Remaining suspended: long poll target %d not reached\n",
728 : kc->lpt);
729 : }
730 1 : return;
731 : }
732 : /* All exchange requests done, create final
733 : big response from cumulated replies */
734 7 : resume_kyc_with_response (kc);
735 : }
736 :
737 :
738 : /**
739 : * Figure out which exchange accounts from @a keys could
740 : * be used for a KYC auth wire transfer from the account
741 : * that @a ekr is checking. Will set the "pkaa" array
742 : * in @a ekr.
743 : *
744 : * @param[in,out] ekr request we are processing
745 : */
746 : static void
747 5 : determine_eligible_accounts (
748 : struct ExchangeKycRequest *ekr)
749 : {
750 5 : struct KycContext *kc = ekr->kc;
751 5 : const struct TALER_EXCHANGE_Keys *keys = ekr->keys;
752 : struct TALER_Amount kyc_amount;
753 : char *merchant_pub_str;
754 : struct TALER_NormalizedPayto np;
755 :
756 5 : ekr->pkaa = json_array ();
757 5 : GNUNET_assert (NULL != ekr->pkaa);
758 : {
759 : const struct TALER_EXCHANGE_GlobalFee *gf;
760 :
761 5 : gf = TALER_EXCHANGE_get_global_fee (keys,
762 : GNUNET_TIME_timestamp_get ());
763 5 : if (NULL == gf)
764 : {
765 0 : GNUNET_assert (GNUNET_OK ==
766 : TALER_amount_set_zero (keys->currency,
767 : &kyc_amount));
768 : }
769 : else
770 : {
771 : /* FIXME-#9427: history fee should be globally renamed to KYC fee... */
772 5 : kyc_amount = gf->fees.history;
773 : }
774 : }
775 :
776 : merchant_pub_str
777 5 : = GNUNET_STRINGS_data_to_string_alloc (
778 5 : &kc->mi->merchant_pub,
779 : sizeof (kc->mi->merchant_pub));
780 : /* For all accounts of the exchange */
781 5 : np = TALER_payto_normalize (ekr->payto_uri);
782 10 : for (unsigned int i = 0; i<keys->accounts_len; i++)
783 : {
784 5 : const struct TALER_EXCHANGE_WireAccount *account
785 5 : = &keys->accounts[i];
786 :
787 : /* KYC auth transfers are never supported with conversion */
788 5 : if (NULL != account->conversion_url)
789 0 : continue;
790 : /* filter by source account by credit_restrictions */
791 5 : if (GNUNET_YES !=
792 5 : TALER_EXCHANGE_test_account_allowed (account,
793 : true, /* credit */
794 : np))
795 0 : continue;
796 : /* exchange account is allowed, add it */
797 : {
798 5 : const char *exchange_account_payto
799 : = account->fpayto_uri.full_payto;
800 : char *payto_kycauth;
801 :
802 5 : if (TALER_amount_is_zero (&kyc_amount))
803 0 : GNUNET_asprintf (&payto_kycauth,
804 : "%s%cmessage=KYC:%s",
805 : exchange_account_payto,
806 0 : (NULL == strchr (exchange_account_payto,
807 : '?'))
808 : ? '?'
809 : : '&',
810 : merchant_pub_str);
811 : else
812 10 : GNUNET_asprintf (&payto_kycauth,
813 : "%s%camount=%s&message=KYC:%s",
814 : exchange_account_payto,
815 5 : (NULL == strchr (exchange_account_payto,
816 : '?'))
817 : ? '?'
818 : : '&',
819 : TALER_amount2s (&kyc_amount),
820 : merchant_pub_str);
821 5 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
822 : "Found account %s where KYC auth is possible\n",
823 : payto_kycauth);
824 5 : GNUNET_assert (0 ==
825 : json_array_append_new (ekr->pkaa,
826 : json_string (payto_kycauth)));
827 5 : GNUNET_free (payto_kycauth);
828 : }
829 : }
830 5 : GNUNET_free (np.normalized_payto);
831 5 : GNUNET_free (merchant_pub_str);
832 5 : }
833 :
834 :
835 : /**
836 : * Function called with the result of a #TMH_EXCHANGES_keys4exchange()
837 : * operation. Runs the KYC check against the exchange.
838 : *
839 : * @param cls closure with our `struct ExchangeKycRequest *`
840 : * @param keys keys of the exchange context
841 : * @param exchange representation of the exchange
842 : */
843 : static void
844 5 : kyc_with_exchange (void *cls,
845 : struct TALER_EXCHANGE_Keys *keys,
846 : struct TMH_Exchange *exchange)
847 : {
848 5 : struct ExchangeKycRequest *ekr = cls;
849 :
850 : (void) exchange;
851 5 : ekr->fo = NULL;
852 5 : if (NULL == keys)
853 : {
854 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
855 : "Failed to download `%skeys`\n",
856 : ekr->exchange_url);
857 0 : ekr->no_keys = true;
858 0 : ekr_finished (ekr);
859 0 : return;
860 : }
861 5 : ekr->keys = TALER_EXCHANGE_keys_incref (keys);
862 5 : if (! ekr->auth_ok)
863 : {
864 5 : determine_eligible_accounts (ekr);
865 5 : if (0 == json_array_size (ekr->pkaa))
866 : {
867 : /* No KYC auth wire transfers are possible to this exchange from
868 : our merchant bank account, so we cannot use this account with
869 : this exchange if it has any KYC requirements! */
870 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
871 : "KYC auth to `%s' impossible for merchant account `%s'\n",
872 : ekr->exchange_url,
873 : ekr->payto_uri.full_payto);
874 0 : ekr->kyc_auth_conflict = true;
875 : }
876 : }
877 5 : ekr_finished (ekr);
878 : }
879 :
880 :
881 : /**
882 : * Function called from account_kyc_get_status() with KYC status information
883 : * for this merchant.
884 : *
885 : * @param cls our `struct KycContext *`
886 : * @param h_wire hash of the wire account
887 : * @param payto_uri payto:// URI of the merchant's bank account
888 : * @param exchange_url base URL of the exchange for which this is a status
889 : * @param last_check when did we last get an update on our KYC status from the exchange
890 : * @param kyc_ok true if we satisfied the KYC requirements
891 : * @param access_token access token for the KYC SPA, NULL if we cannot access it yet (need KYC auth wire transfer)
892 : * @param last_http_status last HTTP status from /kyc-check
893 : * @param last_ec last Taler error code from /kyc-check
894 : * @param in_aml_review true if the account is pending review
895 : * @param jlimits JSON array of applicable AccountLimits, or NULL if unknown (like defaults apply)
896 : */
897 : static void
898 9 : kyc_status_cb (
899 : void *cls,
900 : const struct TALER_MerchantWireHashP *h_wire,
901 : struct TALER_FullPayto payto_uri,
902 : const char *exchange_url,
903 : struct GNUNET_TIME_Timestamp last_check,
904 : bool kyc_ok,
905 : const struct TALER_AccountAccessTokenP *access_token,
906 : unsigned int last_http_status,
907 : enum TALER_ErrorCode last_ec,
908 : bool in_aml_review,
909 : const json_t *jlimits)
910 : {
911 9 : struct KycContext *kc = cls;
912 : struct ExchangeKycRequest *ekr;
913 :
914 9 : if (! TMH_EXCHANGES_check_trusted (exchange_url))
915 : {
916 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
917 : "Skipping exchange `%s': not trusted\n",
918 : exchange_url);
919 0 : return;
920 : }
921 9 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
922 : "KYC status for `%s' at `%s' is %u/%s/%s/%s\n",
923 : payto_uri.full_payto,
924 : exchange_url,
925 : last_http_status,
926 : kyc_ok ? "KYC OK" : "KYC NEEDED",
927 : in_aml_review ? "IN AML REVIEW" : "NO AML REVIEW",
928 : NULL == jlimits ? "DEFAULT LIMITS" : "CUSTOM LIMITS");
929 9 : switch (kc->lpt)
930 : {
931 4 : case TALER_EXCHANGE_KLPT_NONE:
932 4 : break;
933 4 : case TALER_EXCHANGE_KLPT_KYC_AUTH_TRANSFER:
934 4 : if (NULL != access_token)
935 3 : kc->return_immediately = true;
936 4 : break;
937 0 : case TALER_EXCHANGE_KLPT_INVESTIGATION_DONE:
938 0 : if (! in_aml_review)
939 0 : kc->return_immediately = true;
940 0 : break;
941 1 : case TALER_EXCHANGE_KLPT_KYC_OK:
942 1 : if (kyc_ok)
943 1 : kc->return_immediately = true;
944 1 : break;
945 : }
946 9 : ekr = GNUNET_new (struct ExchangeKycRequest);
947 9 : GNUNET_CONTAINER_DLL_insert (kc->exchange_pending_head,
948 : kc->exchange_pending_tail,
949 : ekr);
950 9 : ekr->last_http_status = last_http_status;
951 9 : ekr->last_ec = last_ec;
952 9 : if (NULL != jlimits)
953 4 : ekr->jlimits = json_incref ((json_t *) jlimits);
954 9 : ekr->h_wire = *h_wire;
955 9 : ekr->exchange_url = GNUNET_strdup (exchange_url);
956 : ekr->payto_uri.full_payto
957 9 : = GNUNET_strdup (payto_uri.full_payto);
958 9 : ekr->last_check = last_check;
959 9 : ekr->kyc_ok = kyc_ok;
960 9 : ekr->kc = kc;
961 9 : ekr->in_aml_review = in_aml_review;
962 9 : ekr->auth_ok = (NULL != access_token);
963 9 : if ( (! ekr->auth_ok) ||
964 4 : (NULL == ekr->jlimits) )
965 : {
966 : /* Figure out wire transfer instructions */
967 5 : if (GNUNET_NO == kc->suspended)
968 : {
969 4 : MHD_suspend_connection (kc->connection);
970 4 : kc->suspended = GNUNET_YES;
971 : }
972 5 : ekr->fo = TMH_EXCHANGES_keys4exchange (
973 : exchange_url,
974 : false,
975 : &kyc_with_exchange,
976 : ekr);
977 5 : if (NULL == ekr->fo)
978 : {
979 0 : GNUNET_break (0);
980 0 : ekr_finished (ekr);
981 0 : return;
982 : }
983 5 : return;
984 : }
985 4 : ekr->access_token = *access_token;
986 4 : ekr_finished (ekr);
987 : }
988 :
989 :
990 : /**
991 : * Check the KYC status of an instance.
992 : *
993 : * @param mi instance to check KYC status of
994 : * @param connection the MHD connection to handle
995 : * @param[in,out] hc context with further information about the request
996 : * @return MHD result code
997 : */
998 : static MHD_RESULT
999 13 : get_instances_ID_kyc (
1000 : struct TMH_MerchantInstance *mi,
1001 : struct MHD_Connection *connection,
1002 : struct TMH_HandlerContext *hc)
1003 : {
1004 13 : struct KycContext *kc = hc->ctx;
1005 :
1006 13 : if (NULL == kc)
1007 : {
1008 9 : kc = GNUNET_new (struct KycContext);
1009 9 : kc->mi = mi;
1010 9 : hc->ctx = kc;
1011 9 : hc->cc = &kyc_context_cleanup;
1012 9 : GNUNET_CONTAINER_DLL_insert (kc_head,
1013 : kc_tail,
1014 : kc);
1015 9 : kc->connection = connection;
1016 9 : kc->hc = hc;
1017 9 : kc->kycs_data = json_array ();
1018 9 : GNUNET_assert (NULL != kc->kycs_data);
1019 9 : TALER_MHD_parse_request_timeout (connection,
1020 : &kc->timeout);
1021 : {
1022 9 : uint64_t num = 0;
1023 : int val;
1024 :
1025 9 : TALER_MHD_parse_request_number (connection,
1026 : "lpt",
1027 : &num);
1028 9 : val = (int) num;
1029 9 : if ( (val < 0) ||
1030 : (val > TALER_EXCHANGE_KLPT_MAX) )
1031 : {
1032 : /* Protocol violation, but we can be graceful and
1033 : just ignore the long polling! */
1034 0 : GNUNET_break_op (0);
1035 0 : val = TALER_EXCHANGE_KLPT_NONE;
1036 : }
1037 9 : kc->lpt = (enum TALER_EXCHANGE_KycLongPollTarget) val;
1038 : }
1039 : kc->return_immediately
1040 9 : = (TALER_EXCHANGE_KLPT_NONE == kc->lpt);
1041 : /* process 'exchange_url' argument */
1042 9 : kc->exchange_url = MHD_lookup_connection_value (
1043 : connection,
1044 : MHD_GET_ARGUMENT_KIND,
1045 : "exchange_url");
1046 9 : if ( (NULL != kc->exchange_url) &&
1047 7 : ( (! TALER_url_valid_charset (kc->exchange_url)) ||
1048 7 : (! TALER_is_web_url (kc->exchange_url)) ) )
1049 : {
1050 0 : GNUNET_break_op (0);
1051 0 : return TALER_MHD_reply_with_error (
1052 : connection,
1053 : MHD_HTTP_BAD_REQUEST,
1054 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
1055 : "exchange_url must be a valid HTTP(s) URL");
1056 : }
1057 :
1058 9 : TALER_MHD_parse_request_arg_auto (connection,
1059 : "h_wire",
1060 : &kc->h_wire,
1061 : kc->have_h_wire);
1062 :
1063 9 : if ( (TALER_EXCHANGE_KLPT_NONE != kc->lpt) &&
1064 4 : (! GNUNET_TIME_absolute_is_past (kc->timeout)) )
1065 : {
1066 4 : if (kc->have_h_wire)
1067 : {
1068 2 : struct TALER_MERCHANTDB_MerchantKycStatusChangeEventP ev = {
1069 2 : .header.size = htons (sizeof (ev)),
1070 2 : .header.type = htons (
1071 : TALER_DBEVENT_MERCHANT_EXCHANGE_KYC_STATUS_CHANGED
1072 : ),
1073 : .h_wire = kc->h_wire
1074 : };
1075 :
1076 4 : kc->eh = TMH_db->event_listen (
1077 2 : TMH_db->cls,
1078 : &ev.header,
1079 : GNUNET_TIME_absolute_get_remaining (kc->timeout),
1080 : &kyc_change_cb,
1081 : kc);
1082 : }
1083 : else
1084 : {
1085 2 : struct GNUNET_DB_EventHeaderP hdr = {
1086 2 : .size = htons (sizeof (hdr)),
1087 2 : .type = htons (TALER_DBEVENT_MERCHANT_KYC_STATUS_CHANGED)
1088 : };
1089 :
1090 4 : kc->eh = TMH_db->event_listen (
1091 2 : TMH_db->cls,
1092 : &hdr,
1093 : GNUNET_TIME_absolute_get_remaining (kc->timeout),
1094 : &kyc_change_cb,
1095 : kc);
1096 : }
1097 : } /* end register LISTEN hooks */
1098 : } /* end 1st time initialization */
1099 :
1100 13 : if (GNUNET_SYSERR == kc->suspended)
1101 0 : return MHD_NO; /* during shutdown, we don't generate any more replies */
1102 13 : GNUNET_assert (GNUNET_NO == kc->suspended);
1103 :
1104 13 : if (NULL != kc->response)
1105 3 : return MHD_queue_response (connection,
1106 : kc->response_code,
1107 : kc->response);
1108 :
1109 : /* Check our database */
1110 : {
1111 : enum GNUNET_DB_QueryStatus qs;
1112 :
1113 10 : GNUNET_break (0 ==
1114 : json_array_clear (kc->kycs_data));
1115 10 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1116 : "Checking KYC status for %s (%d/%s)\n",
1117 : mi->settings.id,
1118 : kc->have_h_wire,
1119 : kc->exchange_url);
1120 10 : qs = TMH_db->account_kyc_get_status (
1121 10 : TMH_db->cls,
1122 10 : mi->settings.id,
1123 10 : kc->have_h_wire
1124 : ? &kc->h_wire
1125 : : NULL,
1126 : kc->exchange_url,
1127 : &kyc_status_cb,
1128 : kc);
1129 10 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1130 : "account_kyc_get_status returned %d records\n",
1131 : (int) qs);
1132 10 : if (qs < 0)
1133 : {
1134 : /* Database error */
1135 0 : GNUNET_break (0);
1136 0 : if (GNUNET_YES == kc->suspended)
1137 : {
1138 : /* must have suspended before DB error, resume! */
1139 0 : MHD_resume_connection (connection);
1140 0 : kc->suspended = GNUNET_NO;
1141 : }
1142 0 : return TALER_MHD_reply_with_ec (
1143 : connection,
1144 : TALER_EC_GENERIC_DB_FETCH_FAILED,
1145 : "account_kyc_get_status");
1146 : }
1147 10 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
1148 : {
1149 : /* no matching accounts, could not have suspended */
1150 2 : GNUNET_assert (GNUNET_NO == kc->suspended);
1151 2 : return TALER_MHD_reply_static (connection,
1152 : MHD_HTTP_NO_CONTENT,
1153 : NULL,
1154 : NULL,
1155 : 0);
1156 : }
1157 : }
1158 8 : if (GNUNET_YES == kc->suspended)
1159 4 : return MHD_YES;
1160 : /* Should have generated a response */
1161 4 : GNUNET_break (NULL != kc->response);
1162 4 : return MHD_queue_response (connection,
1163 : kc->response_code,
1164 : kc->response);
1165 : }
1166 :
1167 :
1168 : MHD_RESULT
1169 13 : TMH_private_get_instances_ID_kyc (
1170 : const struct TMH_RequestHandler *rh,
1171 : struct MHD_Connection *connection,
1172 : struct TMH_HandlerContext *hc)
1173 : {
1174 13 : struct TMH_MerchantInstance *mi = hc->instance;
1175 :
1176 : (void) rh;
1177 13 : return get_instances_ID_kyc (mi,
1178 : connection,
1179 : hc);
1180 : }
1181 :
1182 :
1183 : MHD_RESULT
1184 0 : TMH_private_get_instances_default_ID_kyc (
1185 : const struct TMH_RequestHandler *rh,
1186 : struct MHD_Connection *connection,
1187 : struct TMH_HandlerContext *hc)
1188 : {
1189 : struct TMH_MerchantInstance *mi;
1190 :
1191 : (void) rh;
1192 0 : mi = TMH_lookup_instance (hc->infix);
1193 0 : if (NULL == mi)
1194 : {
1195 0 : return TALER_MHD_reply_with_error (
1196 : connection,
1197 : MHD_HTTP_NOT_FOUND,
1198 : TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN,
1199 0 : hc->infix);
1200 : }
1201 0 : return get_instances_ID_kyc (mi,
1202 : connection,
1203 : hc);
1204 : }
1205 :
1206 :
1207 : /* end of taler-merchant-httpd_private-get-instances-ID-kyc.c */
|