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