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 14 : TMH_force_kyc_resume ()
276 : {
277 14 : for (struct KycContext *kc = kc_head;
278 14 : 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 14 : }
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 : GNUNET_assert (GNUNET_YES == kc->suspended);
379 1 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
380 : "Resuming KYC with gateway timeout\n");
381 1 : kc->suspended = GNUNET_NO;
382 1 : MHD_resume_connection (kc->connection);
383 1 : TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */
384 1 : }
385 :
386 :
387 : /**
388 : * Pack the given @a limit into the JSON @a limits array.
389 : *
390 : * @param limit account limit to pack
391 : * @param[in,out] limits JSON array to extend
392 : */
393 : static void
394 0 : pack_limit (const struct TALER_EXCHANGE_AccountLimit *limit,
395 : json_t *limits)
396 : {
397 : json_t *jl;
398 :
399 0 : jl = GNUNET_JSON_PACK (
400 : TALER_JSON_pack_kycte ("operation_type",
401 : limit->operation_type),
402 : GNUNET_JSON_pack_time_rel ("timeframe",
403 : limit->timeframe),
404 : TALER_JSON_pack_amount ("threshold",
405 : &limit->threshold),
406 : GNUNET_JSON_pack_bool ("soft_limit",
407 : limit->soft_limit)
408 : );
409 0 : GNUNET_assert (0 ==
410 : json_array_append_new (limits,
411 : jl));
412 0 : }
413 :
414 :
415 : /**
416 : * Return JSON array with AccountLimit objects giving
417 : * the current limits for this exchange.
418 : *
419 : * @param[in,out] ekr overall request context
420 : */
421 : static json_t *
422 9 : get_exchange_limits (
423 : struct ExchangeKycRequest *ekr)
424 : {
425 9 : const struct TALER_EXCHANGE_Keys *keys = ekr->keys;
426 : json_t *limits;
427 :
428 9 : if (NULL != ekr->jlimits)
429 : {
430 4 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
431 : "Returning custom KYC limits\n");
432 4 : return json_incref (ekr->jlimits);
433 : }
434 5 : if (NULL == keys)
435 : {
436 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
437 : "No keys, thus no default KYC limits known\n");
438 0 : return NULL;
439 : }
440 5 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
441 : "Returning default KYC limits (%u/%u)\n",
442 : keys->hard_limits_length,
443 : keys->zero_limits_length);
444 5 : limits = json_array ();
445 5 : GNUNET_assert (NULL != limits);
446 5 : for (unsigned int i = 0; i<keys->hard_limits_length; i++)
447 : {
448 0 : const struct TALER_EXCHANGE_AccountLimit *limit
449 0 : = &keys->hard_limits[i];
450 :
451 0 : pack_limit (limit,
452 : limits);
453 : }
454 7 : for (unsigned int i = 0; i<keys->zero_limits_length; i++)
455 : {
456 2 : const struct TALER_EXCHANGE_ZeroLimitedOperation *zlimit
457 2 : = &keys->zero_limits[i];
458 : json_t *jl;
459 : struct TALER_Amount zero;
460 :
461 2 : GNUNET_assert (GNUNET_OK ==
462 : TALER_amount_set_zero (keys->currency,
463 : &zero));
464 2 : jl = GNUNET_JSON_PACK (
465 : TALER_JSON_pack_kycte ("operation_type",
466 : zlimit->operation_type),
467 : GNUNET_JSON_pack_time_rel ("timeframe",
468 : GNUNET_TIME_UNIT_ZERO),
469 : TALER_JSON_pack_amount ("threshold",
470 : &zero),
471 : GNUNET_JSON_pack_bool ("soft_limit",
472 : true)
473 : );
474 2 : GNUNET_assert (0 ==
475 : json_array_append_new (limits,
476 : jl));
477 : }
478 5 : return limits;
479 : }
480 :
481 :
482 : /**
483 : * Maps @a ekr to a status code for clients to interpret the
484 : * overall result.
485 : *
486 : * @param ekr request summary
487 : * @return status of the KYC state as a string
488 : */
489 : static const char *
490 9 : map_to_status (const struct ExchangeKycRequest *ekr)
491 : {
492 9 : if (ekr->no_keys)
493 : {
494 0 : return "no-exchange-keys";
495 : }
496 9 : if (ekr->kyc_ok)
497 : {
498 7 : if (NULL != ekr->jlimits)
499 : {
500 : size_t off;
501 : json_t *limit;
502 4 : json_array_foreach (ekr->jlimits, off, limit)
503 : {
504 : struct TALER_Amount threshold;
505 : enum TALER_KYCLOGIC_KycTriggerEvent operation_type;
506 4 : bool soft = false;
507 : struct GNUNET_JSON_Specification spec[] = {
508 4 : TALER_JSON_spec_kycte ("operation_type",
509 : &operation_type),
510 4 : TALER_JSON_spec_amount_any ("threshold",
511 : &threshold),
512 4 : GNUNET_JSON_spec_mark_optional (
513 : GNUNET_JSON_spec_bool ("soft_limit",
514 : &soft),
515 : NULL),
516 4 : GNUNET_JSON_spec_end ()
517 : };
518 :
519 4 : if (GNUNET_OK !=
520 4 : GNUNET_JSON_parse (limit,
521 : spec,
522 : NULL, NULL))
523 : {
524 0 : GNUNET_break (0);
525 4 : return "merchant-internal-error";
526 : }
527 4 : if (! TALER_amount_is_zero (&threshold))
528 0 : continue; /* only care about zero-limits */
529 4 : if (! soft)
530 0 : continue; /* only care about soft limits */
531 4 : if ( (operation_type == TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT) ||
532 4 : (operation_type == TALER_KYCLOGIC_KYC_TRIGGER_AGGREGATE) ||
533 0 : (operation_type == TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION) )
534 : {
535 4 : if (! ekr->auth_ok)
536 : {
537 0 : if (ekr->kyc_auth_conflict)
538 0 : return "kyc-wire-impossible";
539 0 : return "kyc-wire-required";
540 : }
541 4 : return "kyc-required";
542 : }
543 : }
544 : }
545 3 : if (NULL == ekr->jlimits)
546 : {
547 : /* check default limits */
548 3 : const struct TALER_EXCHANGE_Keys *keys = ekr->keys;
549 :
550 3 : for (unsigned int i = 0; i < keys->zero_limits_length; i++)
551 : {
552 0 : enum TALER_KYCLOGIC_KycTriggerEvent operation_type
553 0 : = keys->zero_limits[i].operation_type;
554 :
555 0 : if ( (operation_type == TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT) ||
556 0 : (operation_type == TALER_KYCLOGIC_KYC_TRIGGER_AGGREGATE) ||
557 : (operation_type == TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION) )
558 : {
559 0 : if (! ekr->auth_ok)
560 : {
561 0 : if (ekr->kyc_auth_conflict)
562 0 : return "kyc-wire-impossible";
563 0 : return "kyc-wire-required";
564 : }
565 0 : return "kyc-required";
566 : }
567 : }
568 : }
569 3 : return "ready";
570 : }
571 2 : if (! ekr->auth_ok)
572 : {
573 2 : if (ekr->kyc_auth_conflict)
574 0 : return "kyc-wire-impossible";
575 2 : return "kyc-wire-required";
576 : }
577 0 : if (ekr->in_aml_review)
578 0 : return "awaiting-aml-review";
579 0 : switch (ekr->last_http_status)
580 : {
581 0 : case 0:
582 0 : return "exchange-unreachable";
583 0 : case MHD_HTTP_OK:
584 : /* then we should have kyc_ok */
585 0 : GNUNET_break (0);
586 0 : return NULL;
587 0 : case MHD_HTTP_ACCEPTED:
588 : /* Then KYC is really what is needed */
589 0 : return "kyc-required";
590 0 : case MHD_HTTP_NO_CONTENT:
591 : /* then we should have had kyc_ok! */
592 0 : GNUNET_break (0);
593 0 : return NULL;
594 0 : case MHD_HTTP_FORBIDDEN:
595 : /* then we should have had ! auth_ok */
596 0 : GNUNET_break (0);
597 0 : return NULL;
598 0 : case MHD_HTTP_NOT_FOUND:
599 : /* then we should have had ! auth_ok */
600 0 : GNUNET_break (0);
601 0 : return NULL;
602 0 : case MHD_HTTP_CONFLICT:
603 : /* then we should have had ! auth_ok */
604 0 : GNUNET_break (0);
605 0 : return NULL;
606 0 : case MHD_HTTP_INTERNAL_SERVER_ERROR:
607 0 : return "exchange-internal-error";
608 0 : case MHD_HTTP_GATEWAY_TIMEOUT:
609 0 : return "exchange-gateway-timeout";
610 0 : default:
611 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
612 : "Exchange responded with unexpected HTTP status %u to /kyc-check request!\n",
613 : ekr->last_http_status);
614 0 : break;
615 : }
616 0 : return "exchange-status-invalid";
617 : }
618 :
619 :
620 : /**
621 : * Take data from @a ekr to expand our response.
622 : *
623 : * @param ekr exchange we are done inspecting
624 : */
625 : static void
626 9 : ekr_expand_response (struct ExchangeKycRequest *ekr)
627 : {
628 : const char *status;
629 :
630 9 : status = map_to_status (ekr);
631 9 : if (NULL == status)
632 : {
633 0 : GNUNET_break (0);
634 0 : status = "logic-bug";
635 : }
636 9 : GNUNET_assert (
637 : 0 ==
638 : json_array_append_new (
639 : ekr->kc->kycs_data,
640 : GNUNET_JSON_PACK (
641 : TALER_JSON_pack_full_payto (
642 : "payto_uri",
643 : ekr->payto_uri),
644 : GNUNET_JSON_pack_data_auto (
645 : "h_wire",
646 : &ekr->h_wire),
647 : GNUNET_JSON_pack_string (
648 : "status",
649 : status),
650 : GNUNET_JSON_pack_string (
651 : "exchange_url",
652 : ekr->exchange_url),
653 : GNUNET_JSON_pack_bool ("no_keys",
654 : ekr->no_keys),
655 : GNUNET_JSON_pack_bool ("auth_conflict",
656 : ekr->kyc_auth_conflict),
657 : GNUNET_JSON_pack_uint64 ("exchange_http_status",
658 : ekr->last_http_status),
659 : (TALER_EC_NONE == ekr->last_ec)
660 : ? GNUNET_JSON_pack_allow_null (
661 : GNUNET_JSON_pack_string (
662 : "dummy",
663 : NULL))
664 : : GNUNET_JSON_pack_uint64 ("exchange_code",
665 : ekr->last_ec),
666 : ekr->auth_ok
667 : ? GNUNET_JSON_pack_data_auto (
668 : "access_token",
669 : &ekr->access_token)
670 : : GNUNET_JSON_pack_allow_null (
671 : GNUNET_JSON_pack_string (
672 : "dummy",
673 : NULL)),
674 : GNUNET_JSON_pack_allow_null (
675 : GNUNET_JSON_pack_array_steal (
676 : "limits",
677 : get_exchange_limits (ekr))),
678 : GNUNET_JSON_pack_allow_null (
679 : GNUNET_JSON_pack_array_incref ("payto_kycauths",
680 : ekr->pkaa))
681 : )));
682 9 : }
683 :
684 :
685 : /**
686 : * We are done with the KYC request @a ekr. Remove it from the work list and
687 : * check if we are done overall.
688 : *
689 : * @param[in] ekr key request that is done (and will be freed)
690 : */
691 : static void
692 9 : ekr_finished (struct ExchangeKycRequest *ekr)
693 : {
694 9 : struct KycContext *kc = ekr->kc;
695 :
696 9 : ekr_expand_response (ekr);
697 9 : GNUNET_CONTAINER_DLL_remove (kc->exchange_pending_head,
698 : kc->exchange_pending_tail,
699 : ekr);
700 9 : json_decref (ekr->jlimits);
701 9 : json_decref (ekr->pkaa);
702 9 : if (NULL != ekr->keys)
703 5 : TALER_EXCHANGE_keys_decref (ekr->keys);
704 9 : GNUNET_free (ekr->exchange_url);
705 9 : GNUNET_free (ekr->payto_uri.full_payto);
706 9 : GNUNET_free (ekr);
707 :
708 9 : if (NULL != kc->exchange_pending_head)
709 1 : return; /* wait for more */
710 :
711 8 : if ( (! kc->return_immediately) &&
712 1 : (! GNUNET_TIME_absolute_is_past (kc->timeout)) )
713 : {
714 1 : if (GNUNET_NO == kc->suspended)
715 : {
716 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
717 : "Suspending: long poll target %d not reached\n",
718 : kc->lpt);
719 0 : MHD_suspend_connection (kc->connection);
720 0 : kc->suspended = GNUNET_YES;
721 : }
722 : else
723 : {
724 1 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
725 : "Remaining suspended: long poll target %d not reached\n",
726 : kc->lpt);
727 : }
728 1 : return;
729 : }
730 : /* All exchange requests done, create final
731 : big response from cumulated replies */
732 7 : resume_kyc_with_response (kc);
733 : }
734 :
735 :
736 : /**
737 : * Figure out which exchange accounts from @a keys could
738 : * be used for a KYC auth wire transfer from the account
739 : * that @a ekr is checking. Will set the "pkaa" array
740 : * in @a ekr.
741 : *
742 : * @param[in,out] ekr request we are processing
743 : */
744 : static void
745 5 : determine_eligible_accounts (
746 : struct ExchangeKycRequest *ekr)
747 : {
748 5 : struct KycContext *kc = ekr->kc;
749 5 : const struct TALER_EXCHANGE_Keys *keys = ekr->keys;
750 : struct TALER_Amount kyc_amount;
751 : char *merchant_pub_str;
752 : struct TALER_NormalizedPayto np;
753 :
754 5 : ekr->pkaa = json_array ();
755 5 : GNUNET_assert (NULL != ekr->pkaa);
756 : {
757 : const struct TALER_EXCHANGE_GlobalFee *gf;
758 :
759 5 : gf = TALER_EXCHANGE_get_global_fee (keys,
760 : GNUNET_TIME_timestamp_get ());
761 5 : if (NULL == gf)
762 : {
763 0 : GNUNET_assert (GNUNET_OK ==
764 : TALER_amount_set_zero (keys->currency,
765 : &kyc_amount));
766 : }
767 : else
768 : {
769 : /* FIXME-#9427: history fee should be globally renamed to KYC fee... */
770 5 : kyc_amount = gf->fees.history;
771 : }
772 : }
773 :
774 : merchant_pub_str
775 5 : = GNUNET_STRINGS_data_to_string_alloc (
776 5 : &kc->mi->merchant_pub,
777 : sizeof (kc->mi->merchant_pub));
778 : /* For all accounts of the exchange */
779 5 : np = TALER_payto_normalize (ekr->payto_uri);
780 10 : for (unsigned int i = 0; i<keys->accounts_len; i++)
781 : {
782 5 : const struct TALER_EXCHANGE_WireAccount *account
783 5 : = &keys->accounts[i];
784 :
785 : /* KYC auth transfers are never supported with conversion */
786 5 : if (NULL != account->conversion_url)
787 0 : continue;
788 : /* filter by source account by credit_restrictions */
789 5 : if (GNUNET_YES !=
790 5 : TALER_EXCHANGE_test_account_allowed (account,
791 : true, /* credit */
792 : np))
793 0 : continue;
794 : /* exchange account is allowed, add it */
795 : {
796 5 : const char *exchange_account_payto
797 : = account->fpayto_uri.full_payto;
798 : char *payto_kycauth;
799 :
800 5 : if (TALER_amount_is_zero (&kyc_amount))
801 0 : GNUNET_asprintf (&payto_kycauth,
802 : "%s%cmessage=KYC:%s",
803 : exchange_account_payto,
804 0 : (NULL == strchr (exchange_account_payto,
805 : '?'))
806 : ? '?'
807 : : '&',
808 : merchant_pub_str);
809 : else
810 10 : GNUNET_asprintf (&payto_kycauth,
811 : "%s%camount=%s&message=KYC:%s",
812 : exchange_account_payto,
813 5 : (NULL == strchr (exchange_account_payto,
814 : '?'))
815 : ? '?'
816 : : '&',
817 : TALER_amount2s (&kyc_amount),
818 : merchant_pub_str);
819 5 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
820 : "Found account %s where KYC auth is possible\n",
821 : payto_kycauth);
822 5 : GNUNET_assert (0 ==
823 : json_array_append_new (ekr->pkaa,
824 : json_string (payto_kycauth)));
825 5 : GNUNET_free (payto_kycauth);
826 : }
827 : }
828 5 : GNUNET_free (np.normalized_payto);
829 5 : GNUNET_free (merchant_pub_str);
830 5 : }
831 :
832 :
833 : /**
834 : * Function called with the result of a #TMH_EXCHANGES_keys4exchange()
835 : * operation. Runs the KYC check against the exchange.
836 : *
837 : * @param cls closure with our `struct ExchangeKycRequest *`
838 : * @param keys keys of the exchange context
839 : * @param exchange representation of the exchange
840 : */
841 : static void
842 5 : kyc_with_exchange (void *cls,
843 : struct TALER_EXCHANGE_Keys *keys,
844 : struct TMH_Exchange *exchange)
845 : {
846 5 : struct ExchangeKycRequest *ekr = cls;
847 :
848 : (void) exchange;
849 5 : ekr->fo = NULL;
850 5 : if (NULL == keys)
851 : {
852 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
853 : "Failed to download `%skeys`\n",
854 : ekr->exchange_url);
855 0 : ekr->no_keys = true;
856 0 : ekr_finished (ekr);
857 0 : return;
858 : }
859 5 : ekr->keys = TALER_EXCHANGE_keys_incref (keys);
860 5 : if (! ekr->auth_ok)
861 : {
862 5 : determine_eligible_accounts (ekr);
863 5 : if (0 == json_array_size (ekr->pkaa))
864 : {
865 : /* No KYC auth wire transfers are possible to this exchange from
866 : our merchant bank account, so we cannot use this account with
867 : this exchange if it has any KYC requirements! */
868 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
869 : "KYC auth to `%s' impossible for merchant account `%s'\n",
870 : ekr->exchange_url,
871 : ekr->payto_uri.full_payto);
872 0 : ekr->kyc_auth_conflict = true;
873 : }
874 : }
875 5 : ekr_finished (ekr);
876 : }
877 :
878 :
879 : /**
880 : * Function called from account_kyc_get_status() with KYC status information
881 : * for this merchant.
882 : *
883 : * @param cls our `struct KycContext *`
884 : * @param h_wire hash of the wire account
885 : * @param payto_uri payto:// URI of the merchant's bank account
886 : * @param exchange_url base URL of the exchange for which this is a status
887 : * @param last_check when did we last get an update on our KYC status from the exchange
888 : * @param kyc_ok true if we satisfied the KYC requirements
889 : * @param access_token access token for the KYC SPA, NULL if we cannot access it yet (need KYC auth wire transfer)
890 : * @param last_http_status last HTTP status from /kyc-check
891 : * @param last_ec last Taler error code from /kyc-check
892 : * @param in_aml_review true if the account is pending review
893 : * @param jlimits JSON array of applicable AccountLimits, or NULL if unknown (like defaults apply)
894 : */
895 : static void
896 9 : kyc_status_cb (
897 : void *cls,
898 : const struct TALER_MerchantWireHashP *h_wire,
899 : struct TALER_FullPayto payto_uri,
900 : const char *exchange_url,
901 : struct GNUNET_TIME_Timestamp last_check,
902 : bool kyc_ok,
903 : const struct TALER_AccountAccessTokenP *access_token,
904 : unsigned int last_http_status,
905 : enum TALER_ErrorCode last_ec,
906 : bool in_aml_review,
907 : const json_t *jlimits)
908 : {
909 9 : struct KycContext *kc = cls;
910 : struct ExchangeKycRequest *ekr;
911 :
912 9 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
913 : "KYC status for `%s' at `%s' is %u/%s/%s/%s\n",
914 : payto_uri.full_payto,
915 : exchange_url,
916 : last_http_status,
917 : kyc_ok ? "KYC OK" : "KYC NEEDED",
918 : in_aml_review ? "IN AML REVIEW" : "NO AML REVIEW",
919 : NULL == jlimits ? "DEFAULT LIMITS" : "CUSTOM LIMITS");
920 9 : switch (kc->lpt)
921 : {
922 4 : case TALER_EXCHANGE_KLPT_NONE:
923 4 : break;
924 4 : case TALER_EXCHANGE_KLPT_KYC_AUTH_TRANSFER:
925 4 : if (NULL != access_token)
926 3 : kc->return_immediately = true;
927 4 : break;
928 0 : case TALER_EXCHANGE_KLPT_INVESTIGATION_DONE:
929 0 : if (! in_aml_review)
930 0 : kc->return_immediately = true;
931 0 : break;
932 1 : case TALER_EXCHANGE_KLPT_KYC_OK:
933 1 : if (kyc_ok)
934 1 : kc->return_immediately = true;
935 1 : break;
936 : }
937 9 : ekr = GNUNET_new (struct ExchangeKycRequest);
938 9 : GNUNET_CONTAINER_DLL_insert (kc->exchange_pending_head,
939 : kc->exchange_pending_tail,
940 : ekr);
941 9 : ekr->last_http_status = last_http_status;
942 9 : ekr->last_ec = last_ec;
943 9 : if (NULL != jlimits)
944 4 : ekr->jlimits = json_incref ((json_t *) jlimits);
945 9 : ekr->h_wire = *h_wire;
946 9 : ekr->exchange_url = GNUNET_strdup (exchange_url);
947 : ekr->payto_uri.full_payto
948 9 : = GNUNET_strdup (payto_uri.full_payto);
949 9 : ekr->last_check = last_check;
950 9 : ekr->kyc_ok = kyc_ok;
951 9 : ekr->kc = kc;
952 9 : ekr->in_aml_review = in_aml_review;
953 9 : ekr->auth_ok = (NULL != access_token);
954 9 : if ( (! ekr->auth_ok) ||
955 4 : (NULL == ekr->jlimits) )
956 : {
957 : /* Figure out wire transfer instructions */
958 5 : if (GNUNET_NO == kc->suspended)
959 : {
960 4 : MHD_suspend_connection (kc->connection);
961 4 : kc->suspended = GNUNET_YES;
962 : }
963 5 : ekr->fo = TMH_EXCHANGES_keys4exchange (
964 : exchange_url,
965 : false,
966 : &kyc_with_exchange,
967 : ekr);
968 5 : if (NULL == ekr->fo)
969 : {
970 0 : GNUNET_break (0);
971 0 : ekr_finished (ekr);
972 0 : return;
973 : }
974 5 : return;
975 : }
976 4 : ekr->access_token = *access_token;
977 4 : ekr_finished (ekr);
978 : }
979 :
980 :
981 : /**
982 : * Check the KYC status of an instance.
983 : *
984 : * @param mi instance to check KYC status of
985 : * @param connection the MHD connection to handle
986 : * @param[in,out] hc context with further information about the request
987 : * @return MHD result code
988 : */
989 : static MHD_RESULT
990 13 : get_instances_ID_kyc (
991 : struct TMH_MerchantInstance *mi,
992 : struct MHD_Connection *connection,
993 : struct TMH_HandlerContext *hc)
994 : {
995 13 : struct KycContext *kc = hc->ctx;
996 :
997 13 : if (NULL == kc)
998 : {
999 9 : kc = GNUNET_new (struct KycContext);
1000 9 : kc->mi = mi;
1001 9 : hc->ctx = kc;
1002 9 : hc->cc = &kyc_context_cleanup;
1003 9 : GNUNET_CONTAINER_DLL_insert (kc_head,
1004 : kc_tail,
1005 : kc);
1006 9 : kc->connection = connection;
1007 9 : kc->hc = hc;
1008 9 : kc->kycs_data = json_array ();
1009 9 : GNUNET_assert (NULL != kc->kycs_data);
1010 9 : TALER_MHD_parse_request_timeout (connection,
1011 : &kc->timeout);
1012 : {
1013 9 : uint64_t num = 0;
1014 : int val;
1015 :
1016 9 : TALER_MHD_parse_request_number (connection,
1017 : "lpt",
1018 : &num);
1019 9 : val = (int) num;
1020 9 : if ( (val < 0) ||
1021 : (val > TALER_EXCHANGE_KLPT_MAX) )
1022 : {
1023 : /* Protocol violation, but we can be graceful and
1024 : just ignore the long polling! */
1025 0 : GNUNET_break_op (0);
1026 0 : val = TALER_EXCHANGE_KLPT_NONE;
1027 : }
1028 9 : kc->lpt = (enum TALER_EXCHANGE_KycLongPollTarget) val;
1029 : }
1030 : kc->return_immediately
1031 9 : = (TALER_EXCHANGE_KLPT_NONE == kc->lpt);
1032 : /* process 'exchange_url' argument */
1033 9 : kc->exchange_url = MHD_lookup_connection_value (
1034 : connection,
1035 : MHD_GET_ARGUMENT_KIND,
1036 : "exchange_url");
1037 9 : if ( (NULL != kc->exchange_url) &&
1038 7 : ( (! TALER_url_valid_charset (kc->exchange_url)) ||
1039 7 : (! TALER_is_web_url (kc->exchange_url)) ) )
1040 : {
1041 0 : GNUNET_break_op (0);
1042 0 : return TALER_MHD_reply_with_error (
1043 : connection,
1044 : MHD_HTTP_BAD_REQUEST,
1045 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
1046 : "exchange_url must be a valid HTTP(s) URL");
1047 : }
1048 :
1049 9 : TALER_MHD_parse_request_arg_auto (connection,
1050 : "h_wire",
1051 : &kc->h_wire,
1052 : kc->have_h_wire);
1053 :
1054 9 : if ( (TALER_EXCHANGE_KLPT_NONE != kc->lpt) &&
1055 4 : (! GNUNET_TIME_absolute_is_past (kc->timeout)) )
1056 : {
1057 4 : if (kc->have_h_wire)
1058 : {
1059 2 : struct TALER_MERCHANTDB_MerchantKycStatusChangeEventP ev = {
1060 2 : .header.size = htons (sizeof (ev)),
1061 2 : .header.type = htons (
1062 : TALER_DBEVENT_MERCHANT_EXCHANGE_KYC_STATUS_CHANGED
1063 : ),
1064 : .h_wire = kc->h_wire
1065 : };
1066 :
1067 4 : kc->eh = TMH_db->event_listen (
1068 2 : TMH_db->cls,
1069 : &ev.header,
1070 : GNUNET_TIME_absolute_get_remaining (kc->timeout),
1071 : &kyc_change_cb,
1072 : kc);
1073 : }
1074 : else
1075 : {
1076 2 : struct GNUNET_DB_EventHeaderP hdr = {
1077 2 : .size = htons (sizeof (hdr)),
1078 2 : .type = htons (TALER_DBEVENT_MERCHANT_KYC_STATUS_CHANGED)
1079 : };
1080 :
1081 4 : kc->eh = TMH_db->event_listen (
1082 2 : TMH_db->cls,
1083 : &hdr,
1084 : GNUNET_TIME_absolute_get_remaining (kc->timeout),
1085 : &kyc_change_cb,
1086 : kc);
1087 : }
1088 : } /* end register LISTEN hooks */
1089 : } /* end 1st time initialization */
1090 :
1091 13 : if (GNUNET_SYSERR == kc->suspended)
1092 0 : return MHD_NO; /* during shutdown, we don't generate any more replies */
1093 13 : GNUNET_assert (GNUNET_NO == kc->suspended);
1094 :
1095 13 : if (NULL != kc->response)
1096 3 : return MHD_queue_response (connection,
1097 : kc->response_code,
1098 : kc->response);
1099 :
1100 : /* Check our database */
1101 : {
1102 : enum GNUNET_DB_QueryStatus qs;
1103 :
1104 10 : GNUNET_break (0 ==
1105 : json_array_clear (kc->kycs_data));
1106 10 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1107 : "Checking KYC status for %s (%d/%s)\n",
1108 : mi->settings.id,
1109 : kc->have_h_wire,
1110 : kc->exchange_url);
1111 10 : qs = TMH_db->account_kyc_get_status (
1112 10 : TMH_db->cls,
1113 10 : mi->settings.id,
1114 10 : kc->have_h_wire
1115 : ? &kc->h_wire
1116 : : NULL,
1117 : kc->exchange_url,
1118 : &kyc_status_cb,
1119 : kc);
1120 10 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1121 : "account_kyc_get_status returned %d records\n",
1122 : (int) qs);
1123 10 : if (qs < 0)
1124 : {
1125 : /* Database error */
1126 0 : GNUNET_break (0);
1127 0 : if (GNUNET_YES == kc->suspended)
1128 : {
1129 : /* must have suspended before DB error, resume! */
1130 0 : MHD_resume_connection (connection);
1131 0 : kc->suspended = GNUNET_NO;
1132 : }
1133 0 : return TALER_MHD_reply_with_ec (
1134 : connection,
1135 : TALER_EC_GENERIC_DB_FETCH_FAILED,
1136 : "account_kyc_get_status");
1137 : }
1138 10 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
1139 : {
1140 : /* no matching accounts, could not have suspended */
1141 2 : GNUNET_assert (GNUNET_NO == kc->suspended);
1142 2 : return TALER_MHD_reply_static (connection,
1143 : MHD_HTTP_NO_CONTENT,
1144 : NULL,
1145 : NULL,
1146 : 0);
1147 : }
1148 : }
1149 8 : if (GNUNET_YES == kc->suspended)
1150 4 : return MHD_YES;
1151 : /* Should have generated a response */
1152 4 : GNUNET_break (NULL != kc->response);
1153 4 : return MHD_queue_response (connection,
1154 : kc->response_code,
1155 : kc->response);
1156 : }
1157 :
1158 :
1159 : MHD_RESULT
1160 13 : TMH_private_get_instances_ID_kyc (
1161 : const struct TMH_RequestHandler *rh,
1162 : struct MHD_Connection *connection,
1163 : struct TMH_HandlerContext *hc)
1164 : {
1165 13 : struct TMH_MerchantInstance *mi = hc->instance;
1166 :
1167 : (void) rh;
1168 13 : return get_instances_ID_kyc (mi,
1169 : connection,
1170 : hc);
1171 : }
1172 :
1173 :
1174 : MHD_RESULT
1175 0 : TMH_private_get_instances_default_ID_kyc (
1176 : const struct TMH_RequestHandler *rh,
1177 : struct MHD_Connection *connection,
1178 : struct TMH_HandlerContext *hc)
1179 : {
1180 : struct TMH_MerchantInstance *mi;
1181 :
1182 : (void) rh;
1183 0 : mi = TMH_lookup_instance (hc->infix);
1184 0 : if (NULL == mi)
1185 : {
1186 0 : return TALER_MHD_reply_with_error (
1187 : connection,
1188 : MHD_HTTP_NOT_FOUND,
1189 : TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN,
1190 0 : hc->infix);
1191 : }
1192 0 : return get_instances_ID_kyc (mi,
1193 : connection,
1194 : hc);
1195 : }
1196 :
1197 :
1198 : /* end of taler-merchant-httpd_private-get-instances-ID-kyc.c */
|