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