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 "platform.h"
22 : #include "taler_attributes.h"
23 : #include "taler_kyclogic_lib.h"
24 : #include "taler_kyclogic_plugin.h"
25 : #include "taler_mhd_lib.h"
26 : #include "taler_curl_lib.h"
27 : #include "taler_json_lib.h"
28 : #include "taler_templating_lib.h"
29 : #include <regex.h>
30 : #include "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 76 : kycaid_unload_configuration (struct TALER_KYCLOGIC_ProviderDetails *pd)
306 : {
307 76 : curl_slist_free_all (pd->slist);
308 76 : GNUNET_free (pd->conversion_helper);
309 76 : GNUNET_free (pd->auth_token);
310 76 : GNUNET_free (pd->form_id);
311 76 : GNUNET_free (pd->section);
312 76 : GNUNET_free (pd);
313 76 : }
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 76 : kycaid_load_configuration (void *cls,
325 : const char *provider_section_name)
326 : {
327 76 : struct PluginState *ps = cls;
328 : struct TALER_KYCLOGIC_ProviderDetails *pd;
329 :
330 76 : pd = GNUNET_new (struct TALER_KYCLOGIC_ProviderDetails);
331 76 : pd->ps = ps;
332 76 : pd->section = GNUNET_strdup (provider_section_name);
333 76 : if (GNUNET_OK !=
334 76 : 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 76 : if (GNUNET_OK !=
346 76 : 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 76 : if (GNUNET_OK !=
358 76 : 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 76 : if (GNUNET_OK !=
370 76 : 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 76 : GNUNET_asprintf (&auth,
385 : "%s: Token %s",
386 : MHD_HTTP_HEADER_AUTHORIZATION,
387 : pd->auth_token);
388 76 : pd->slist = curl_slist_append (NULL,
389 : auth);
390 76 : GNUNET_free (auth);
391 : }
392 76 : 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 : GNUNET_break (GNUNET_SYSERR != ret);
698 0 : ph->cb (ph->cb_cls,
699 : TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
700 0 : ph->pd->section,
701 : NULL, /* user id */
702 : NULL, /* provider legi ID */
703 0 : GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
704 : NULL, /* attributes */
705 : http_status,
706 : resp);
707 0 : }
708 :
709 :
710 : /**
711 : * Check KYC status and return status to human. Not
712 : * used by KYC AID!
713 : *
714 : * @param cls the @e cls of this struct with the plugin-specific state
715 : * @param pd provider configuration details
716 : * @param connection MHD connection object (for HTTP headers)
717 : * @param account_id which account to trigger process for
718 : * @param process_row row in the legitimization processes table the legitimization is for
719 : * @param provider_user_id user ID (or NULL) the proof is for
720 : * @param provider_legitimization_id legitimization ID the proof is for
721 : * @param cb function to call with the result
722 : * @param cb_cls closure for @a cb
723 : * @return handle to cancel operation early
724 : */
725 : static struct TALER_KYCLOGIC_ProofHandle *
726 0 : kycaid_proof (void *cls,
727 : const struct TALER_KYCLOGIC_ProviderDetails *pd,
728 : struct MHD_Connection *connection,
729 : const struct TALER_NormalizedPaytoHashP *account_id,
730 : uint64_t process_row,
731 : const char *provider_user_id,
732 : const char *provider_legitimization_id,
733 : TALER_KYCLOGIC_ProofCallback cb,
734 : void *cb_cls)
735 : {
736 0 : struct PluginState *ps = cls;
737 : struct TALER_KYCLOGIC_ProofHandle *ph;
738 :
739 0 : ph = GNUNET_new (struct TALER_KYCLOGIC_ProofHandle);
740 0 : ph->ps = ps;
741 0 : ph->pd = pd;
742 0 : ph->cb = cb;
743 0 : ph->cb_cls = cb_cls;
744 0 : ph->connection = connection;
745 0 : ph->task = GNUNET_SCHEDULER_add_now (&proof_reply,
746 : ph);
747 0 : return ph;
748 : }
749 :
750 :
751 : /**
752 : * Cancel KYC webhook execution.
753 : *
754 : * @param[in] wh handle of operation to cancel
755 : */
756 : static void
757 0 : kycaid_webhook_cancel (struct TALER_KYCLOGIC_WebhookHandle *wh)
758 : {
759 0 : if (NULL != wh->task)
760 : {
761 0 : GNUNET_SCHEDULER_cancel (wh->task);
762 0 : wh->task = NULL;
763 : }
764 0 : if (NULL != wh->econ)
765 : {
766 0 : TALER_JSON_external_conversion_stop (wh->econ);
767 0 : wh->econ = NULL;
768 : }
769 0 : if (NULL != wh->job)
770 : {
771 0 : GNUNET_CURL_job_cancel (wh->job);
772 0 : wh->job = NULL;
773 : }
774 0 : if (NULL != wh->json_response)
775 : {
776 0 : json_decref (wh->json_response);
777 0 : wh->json_response = NULL;
778 : }
779 0 : GNUNET_free (wh->verification_id);
780 0 : GNUNET_free (wh->applicant_id);
781 0 : GNUNET_free (wh->url);
782 0 : GNUNET_free (wh);
783 0 : }
784 :
785 :
786 : /**
787 : * Extract KYC failure reasons and log those
788 : *
789 : * @param verifications JSON object with failure details
790 : */
791 : static void
792 0 : log_failure (const json_t *verifications)
793 : {
794 : const json_t *member;
795 : const char *name;
796 :
797 0 : json_object_foreach ((json_t *) verifications, name, member)
798 : {
799 : bool iverified;
800 : const char *comment;
801 : struct GNUNET_JSON_Specification spec[] = {
802 0 : GNUNET_JSON_spec_bool ("verified",
803 : &iverified),
804 0 : GNUNET_JSON_spec_string ("comment",
805 : &comment),
806 0 : GNUNET_JSON_spec_end ()
807 : };
808 :
809 0 : if (GNUNET_OK !=
810 0 : GNUNET_JSON_parse (member,
811 : spec,
812 : NULL, NULL))
813 : {
814 0 : GNUNET_break_op (0);
815 0 : json_dumpf (member,
816 : stderr,
817 : JSON_INDENT (2));
818 0 : continue;
819 : }
820 0 : if (iverified)
821 0 : continue;
822 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
823 : "KYC verification of attribute `%s' failed: %s\n",
824 : name,
825 : comment);
826 : }
827 0 : }
828 :
829 :
830 : /**
831 : * Type of a callback that receives a JSON @a result.
832 : *
833 : * @param cls closure our `struct TALER_KYCLOGIC_WebhookHandle *`
834 : * @param status_type how did the process die
835 : * @param code termination status code from the process
836 : * @param result converted attribute data, NULL on failure
837 : */
838 : static void
839 0 : webhook_conversion_cb (void *cls,
840 : enum GNUNET_OS_ProcessStatusType status_type,
841 : unsigned long code,
842 : const json_t *result)
843 : {
844 0 : struct TALER_KYCLOGIC_WebhookHandle *wh = cls;
845 : struct GNUNET_TIME_Absolute expiration;
846 : struct MHD_Response *resp;
847 :
848 0 : wh->econ = NULL;
849 0 : if ( (0 == code) &&
850 : (NULL == result) )
851 : {
852 : /* No result, but *our helper* was OK => bad input */
853 0 : GNUNET_break_op (0);
854 0 : json_dumpf (wh->json_response,
855 : stderr,
856 : JSON_INDENT (2));
857 0 : resp = TALER_MHD_MAKE_JSON_PACK (
858 : GNUNET_JSON_pack_uint64 ("kycaid_http_status",
859 : wh->kycaid_response_code),
860 : GNUNET_JSON_pack_object_incref ("kycaid_body",
861 : (json_t *) wh->json_response));
862 0 : wh->cb (wh->cb_cls,
863 : wh->process_row,
864 0 : &wh->h_payto,
865 0 : wh->is_wallet,
866 0 : wh->pd->section,
867 0 : wh->applicant_id,
868 0 : wh->verification_id,
869 : TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
870 0 : GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
871 : NULL,
872 : MHD_HTTP_BAD_GATEWAY,
873 : resp);
874 0 : kycaid_webhook_cancel (wh);
875 0 : return;
876 : }
877 0 : if (NULL == result)
878 : {
879 : /* Failure in our helper */
880 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
881 : "Helper exited with status code %d\n",
882 : (int) code);
883 0 : json_dumpf (wh->json_response,
884 : stderr,
885 : JSON_INDENT (2));
886 0 : resp = TALER_MHD_MAKE_JSON_PACK (
887 : GNUNET_JSON_pack_uint64 ("kycaid_http_status",
888 : wh->kycaid_response_code),
889 : GNUNET_JSON_pack_object_incref ("kycaid_body",
890 : (json_t *) wh->json_response));
891 0 : wh->cb (wh->cb_cls,
892 : wh->process_row,
893 0 : &wh->h_payto,
894 0 : wh->is_wallet,
895 0 : wh->pd->section,
896 0 : wh->applicant_id,
897 0 : wh->verification_id,
898 : TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
899 0 : GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
900 : NULL,
901 : MHD_HTTP_BAD_GATEWAY,
902 : resp);
903 0 : kycaid_webhook_cancel (wh);
904 0 : return;
905 : }
906 0 : expiration = GNUNET_TIME_relative_to_absolute (wh->pd->validity);
907 0 : resp = MHD_create_response_from_buffer_static (0,
908 : "");
909 0 : wh->cb (wh->cb_cls,
910 : wh->process_row,
911 0 : &wh->h_payto,
912 0 : wh->is_wallet,
913 0 : wh->pd->section,
914 0 : wh->applicant_id,
915 0 : wh->verification_id,
916 : TALER_KYCLOGIC_STATUS_SUCCESS,
917 : expiration,
918 : result,
919 : MHD_HTTP_NO_CONTENT,
920 : resp);
921 0 : kycaid_webhook_cancel (wh);
922 : }
923 :
924 :
925 : /**
926 : * Function called when we're done processing the
927 : * HTTP "/applicants/{verification_id}" request.
928 : *
929 : * @param cls the `struct TALER_KYCLOGIC_WebhookHandle`
930 : * @param response_code HTTP response code, 0 on error
931 : * @param response parsed JSON result, NULL on error
932 : */
933 : static void
934 0 : handle_webhook_finished (void *cls,
935 : long response_code,
936 : const void *response)
937 : {
938 0 : struct TALER_KYCLOGIC_WebhookHandle *wh = cls;
939 0 : const json_t *j = response;
940 : struct MHD_Response *resp;
941 :
942 0 : wh->job = NULL;
943 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
944 : "Webhook returned with HTTP status %u\n",
945 : (unsigned int) response_code);
946 0 : wh->kycaid_response_code = response_code;
947 0 : wh->json_response = json_incref ((json_t *) j);
948 0 : switch (response_code)
949 : {
950 0 : case MHD_HTTP_OK:
951 : {
952 : const char *profile_status;
953 :
954 0 : profile_status = json_string_value (
955 0 : json_object_get (
956 : j,
957 : "profile_status"));
958 0 : if (0 != strcasecmp ("valid",
959 : profile_status))
960 : {
961 : enum TALER_KYCLOGIC_KycStatus ks;
962 :
963 0 : ks = (0 == strcasecmp ("pending",
964 : profile_status))
965 : ? TALER_KYCLOGIC_STATUS_PENDING
966 0 : : TALER_KYCLOGIC_STATUS_USER_ABORTED;
967 0 : resp = MHD_create_response_from_buffer_static (0,
968 : "");
969 0 : wh->cb (wh->cb_cls,
970 : wh->process_row,
971 0 : &wh->h_payto,
972 0 : wh->is_wallet,
973 0 : wh->pd->section,
974 0 : wh->applicant_id,
975 0 : wh->verification_id,
976 : ks,
977 0 : GNUNET_TIME_UNIT_ZERO_ABS,
978 : NULL,
979 : MHD_HTTP_NO_CONTENT,
980 : resp);
981 0 : break;
982 : }
983 : {
984 0 : const char *argv[] = {
985 0 : wh->pd->conversion_helper,
986 : "-a",
987 0 : wh->pd->auth_token,
988 : NULL,
989 : };
990 :
991 : wh->econ
992 0 : = TALER_JSON_external_conversion_start (
993 : j,
994 : &webhook_conversion_cb,
995 : wh,
996 0 : wh->pd->conversion_helper,
997 : argv);
998 : }
999 0 : if (NULL == wh->econ)
1000 : {
1001 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1002 : "Failed to start KYCAID conversion helper `%s'\n",
1003 : wh->pd->conversion_helper);
1004 0 : resp = TALER_MHD_make_error (
1005 : TALER_EC_EXCHANGE_GENERIC_KYC_CONVERTER_FAILED,
1006 : NULL);
1007 0 : wh->cb (wh->cb_cls,
1008 : wh->process_row,
1009 0 : &wh->h_payto,
1010 0 : wh->is_wallet,
1011 0 : wh->pd->section,
1012 0 : wh->applicant_id,
1013 0 : wh->verification_id,
1014 : TALER_KYCLOGIC_STATUS_INTERNAL_ERROR,
1015 0 : GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
1016 : NULL,
1017 : MHD_HTTP_INTERNAL_SERVER_ERROR,
1018 : resp);
1019 0 : break;
1020 : }
1021 0 : return;
1022 : }
1023 : break;
1024 0 : case MHD_HTTP_BAD_REQUEST:
1025 : case MHD_HTTP_NOT_FOUND:
1026 : case MHD_HTTP_CONFLICT:
1027 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1028 : "KYCAID failed with response %u:\n",
1029 : (unsigned int) response_code);
1030 0 : json_dumpf (j,
1031 : stderr,
1032 : JSON_INDENT (2));
1033 0 : resp = TALER_MHD_MAKE_JSON_PACK (
1034 : GNUNET_JSON_pack_uint64 ("kycaid_http_status",
1035 : response_code));
1036 0 : wh->cb (wh->cb_cls,
1037 : wh->process_row,
1038 0 : &wh->h_payto,
1039 0 : wh->is_wallet,
1040 0 : wh->pd->section,
1041 0 : wh->applicant_id,
1042 0 : wh->verification_id,
1043 : TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
1044 0 : GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
1045 : NULL,
1046 : MHD_HTTP_INTERNAL_SERVER_ERROR,
1047 : resp);
1048 0 : break;
1049 0 : case MHD_HTTP_UNAUTHORIZED:
1050 : case MHD_HTTP_PAYMENT_REQUIRED:
1051 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1052 : "Refused access with HTTP status code %u\n",
1053 : (unsigned int) response_code);
1054 0 : resp = TALER_MHD_MAKE_JSON_PACK (
1055 : GNUNET_JSON_pack_uint64 ("kycaid_http_status",
1056 : response_code),
1057 : GNUNET_JSON_pack_object_incref ("kycaid_body",
1058 : (json_t *) j));
1059 0 : wh->cb (wh->cb_cls,
1060 : wh->process_row,
1061 0 : &wh->h_payto,
1062 0 : wh->is_wallet,
1063 0 : wh->pd->section,
1064 0 : wh->applicant_id,
1065 0 : wh->verification_id,
1066 : TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
1067 0 : GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
1068 : NULL,
1069 : MHD_HTTP_NETWORK_AUTHENTICATION_REQUIRED,
1070 : resp);
1071 0 : break;
1072 0 : case MHD_HTTP_REQUEST_TIMEOUT:
1073 0 : resp = TALER_MHD_MAKE_JSON_PACK (
1074 : GNUNET_JSON_pack_uint64 ("kycaid_http_status",
1075 : response_code),
1076 : GNUNET_JSON_pack_object_incref ("kycaid_body",
1077 : (json_t *) j));
1078 0 : wh->cb (wh->cb_cls,
1079 : wh->process_row,
1080 0 : &wh->h_payto,
1081 0 : wh->is_wallet,
1082 0 : wh->pd->section,
1083 0 : wh->applicant_id,
1084 0 : wh->verification_id,
1085 : TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
1086 0 : GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
1087 : NULL,
1088 : MHD_HTTP_GATEWAY_TIMEOUT,
1089 : resp);
1090 0 : break;
1091 0 : case MHD_HTTP_UNPROCESSABLE_ENTITY: /* validation */
1092 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1093 : "KYCAID failed with response %u:\n",
1094 : (unsigned int) response_code);
1095 0 : json_dumpf (j,
1096 : stderr,
1097 : JSON_INDENT (2));
1098 0 : resp = TALER_MHD_MAKE_JSON_PACK (
1099 : GNUNET_JSON_pack_uint64 ("kycaid_http_status",
1100 : response_code),
1101 : GNUNET_JSON_pack_object_incref ("kycaid_body",
1102 : (json_t *) j));
1103 0 : wh->cb (wh->cb_cls,
1104 : wh->process_row,
1105 0 : &wh->h_payto,
1106 0 : wh->is_wallet,
1107 0 : wh->pd->section,
1108 0 : wh->applicant_id,
1109 0 : wh->verification_id,
1110 : TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
1111 0 : GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
1112 : NULL,
1113 : MHD_HTTP_BAD_GATEWAY,
1114 : resp);
1115 0 : break;
1116 0 : case MHD_HTTP_TOO_MANY_REQUESTS:
1117 0 : resp = TALER_MHD_MAKE_JSON_PACK (
1118 : GNUNET_JSON_pack_uint64 ("kycaid_http_status",
1119 : response_code),
1120 : GNUNET_JSON_pack_object_incref ("kycaid_body",
1121 : (json_t *) j));
1122 0 : wh->cb (wh->cb_cls,
1123 : wh->process_row,
1124 0 : &wh->h_payto,
1125 0 : wh->is_wallet,
1126 0 : wh->pd->section,
1127 0 : wh->applicant_id,
1128 0 : wh->verification_id,
1129 : TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
1130 0 : GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
1131 : NULL,
1132 : MHD_HTTP_SERVICE_UNAVAILABLE,
1133 : resp);
1134 0 : break;
1135 0 : case MHD_HTTP_INTERNAL_SERVER_ERROR:
1136 0 : resp = TALER_MHD_MAKE_JSON_PACK (
1137 : GNUNET_JSON_pack_uint64 ("kycaid_http_status",
1138 : response_code),
1139 : GNUNET_JSON_pack_object_incref ("kycaid_body",
1140 : (json_t *) j));
1141 0 : wh->cb (wh->cb_cls,
1142 : wh->process_row,
1143 0 : &wh->h_payto,
1144 0 : wh->is_wallet,
1145 0 : wh->pd->section,
1146 0 : wh->applicant_id,
1147 0 : wh->verification_id,
1148 : TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
1149 0 : GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
1150 : NULL,
1151 : MHD_HTTP_BAD_GATEWAY,
1152 : resp);
1153 0 : break;
1154 0 : default:
1155 0 : resp = TALER_MHD_MAKE_JSON_PACK (
1156 : GNUNET_JSON_pack_uint64 ("kycaid_http_status",
1157 : response_code),
1158 : GNUNET_JSON_pack_object_incref ("kycaid_body",
1159 : (json_t *) j));
1160 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1161 : "Unexpected KYCAID response %u:\n",
1162 : (unsigned int) response_code);
1163 0 : json_dumpf (j,
1164 : stderr,
1165 : JSON_INDENT (2));
1166 0 : wh->cb (wh->cb_cls,
1167 : wh->process_row,
1168 0 : &wh->h_payto,
1169 0 : wh->is_wallet,
1170 0 : wh->pd->section,
1171 0 : wh->applicant_id,
1172 0 : wh->verification_id,
1173 : TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
1174 0 : GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
1175 : NULL,
1176 : MHD_HTTP_BAD_GATEWAY,
1177 : resp);
1178 0 : break;
1179 : }
1180 0 : kycaid_webhook_cancel (wh);
1181 : }
1182 :
1183 :
1184 : /**
1185 : * Asynchronously return a reply for the webhook.
1186 : *
1187 : * @param cls a `struct TALER_KYCLOGIC_WebhookHandle *`
1188 : */
1189 : static void
1190 0 : async_webhook_reply (void *cls)
1191 : {
1192 0 : struct TALER_KYCLOGIC_WebhookHandle *wh = cls;
1193 :
1194 0 : wh->task = NULL;
1195 0 : wh->cb (wh->cb_cls,
1196 : wh->process_row,
1197 0 : (0 == wh->process_row)
1198 : ? NULL
1199 : : &wh->h_payto,
1200 0 : wh->is_wallet,
1201 0 : wh->pd->section,
1202 0 : wh->applicant_id, /* provider user ID */
1203 0 : wh->verification_id, /* provider legi ID */
1204 : TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
1205 0 : GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
1206 : NULL,
1207 : wh->response_code,
1208 : wh->resp);
1209 0 : kycaid_webhook_cancel (wh);
1210 0 : }
1211 :
1212 :
1213 : /**
1214 : * Check KYC status and return result for Webhook. We do NOT implement the
1215 : * authentication check proposed by the KYCAID documentation, as it would
1216 : * allow an attacker who learns the access token to easily bypass the KYC
1217 : * checks. Instead, we insist on explicitly requesting the KYC status from the
1218 : * provider (at least on success).
1219 : *
1220 : * @param cls the @e cls of this struct with the plugin-specific state
1221 : * @param pd provider configuration details
1222 : * @param plc callback to lookup accounts with
1223 : * @param plc_cls closure for @a plc
1224 : * @param http_method HTTP method used for the webhook
1225 : * @param url_path rest of the URL after `/kyc-webhook/`
1226 : * @param connection MHD connection object (for HTTP headers)
1227 : * @param body HTTP request body
1228 : * @param cb function to call with the result
1229 : * @param cb_cls closure for @a cb
1230 : * @return handle to cancel operation early
1231 : */
1232 : static struct TALER_KYCLOGIC_WebhookHandle *
1233 0 : kycaid_webhook (void *cls,
1234 : const struct TALER_KYCLOGIC_ProviderDetails *pd,
1235 : TALER_KYCLOGIC_ProviderLookupCallback plc,
1236 : void *plc_cls,
1237 : const char *http_method,
1238 : const char *const url_path[],
1239 : struct MHD_Connection *connection,
1240 : const json_t *body,
1241 : TALER_KYCLOGIC_WebhookCallback cb,
1242 : void *cb_cls)
1243 : {
1244 0 : struct PluginState *ps = cls;
1245 : struct TALER_KYCLOGIC_WebhookHandle *wh;
1246 : CURL *eh;
1247 : const char *request_id;
1248 : const char *type;
1249 : const char *verification_id; /* = provider_legitimization_id */
1250 : const char *applicant_id;
1251 : const char *form_id;
1252 0 : const char *status = NULL;
1253 0 : bool verified = false;
1254 0 : bool no_verified = true;
1255 0 : const json_t *verifications = NULL;
1256 : struct GNUNET_JSON_Specification spec[] = {
1257 0 : GNUNET_JSON_spec_string ("request_id",
1258 : &request_id),
1259 0 : GNUNET_JSON_spec_string ("type",
1260 : &type),
1261 0 : GNUNET_JSON_spec_string ("verification_id",
1262 : &verification_id),
1263 0 : GNUNET_JSON_spec_string ("applicant_id",
1264 : &applicant_id),
1265 0 : GNUNET_JSON_spec_string ("form_id",
1266 : &form_id),
1267 0 : GNUNET_JSON_spec_mark_optional (
1268 : GNUNET_JSON_spec_string ("status",
1269 : &status),
1270 : NULL),
1271 0 : GNUNET_JSON_spec_mark_optional (
1272 : GNUNET_JSON_spec_bool ("verified",
1273 : &verified),
1274 : &no_verified),
1275 0 : GNUNET_JSON_spec_mark_optional (
1276 : GNUNET_JSON_spec_object_const ("verifications",
1277 : &verifications),
1278 : NULL),
1279 0 : GNUNET_JSON_spec_end ()
1280 : };
1281 : enum GNUNET_DB_QueryStatus qs;
1282 :
1283 0 : wh = GNUNET_new (struct TALER_KYCLOGIC_WebhookHandle);
1284 0 : wh->cb = cb;
1285 0 : wh->cb_cls = cb_cls;
1286 0 : wh->ps = ps;
1287 0 : wh->pd = pd;
1288 0 : wh->connection = connection;
1289 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1290 : "KYCAID webhook of `%s' triggered with %s\n",
1291 : pd->section,
1292 : http_method);
1293 : #if 1
1294 0 : if (NULL != body)
1295 0 : json_dumpf (body,
1296 : stderr,
1297 : JSON_INDENT (2));
1298 : #endif
1299 0 : if (NULL == pd)
1300 : {
1301 0 : GNUNET_break_op (0);
1302 0 : json_dumpf (body,
1303 : stderr,
1304 : JSON_INDENT (2));
1305 0 : wh->resp = TALER_MHD_make_error (
1306 : TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_UNKNOWN,
1307 : "kycaid");
1308 0 : wh->response_code = MHD_HTTP_NOT_FOUND;
1309 0 : wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply,
1310 : wh);
1311 0 : return wh;
1312 : }
1313 :
1314 0 : if (GNUNET_OK !=
1315 0 : GNUNET_JSON_parse (body,
1316 : spec,
1317 : NULL, NULL))
1318 : {
1319 0 : GNUNET_break_op (0);
1320 0 : json_dumpf (body,
1321 : stderr,
1322 : JSON_INDENT (2));
1323 0 : wh->resp = TALER_MHD_MAKE_JSON_PACK (
1324 : GNUNET_JSON_pack_object_incref ("webhook_body",
1325 : (json_t *) body));
1326 0 : wh->response_code = MHD_HTTP_BAD_REQUEST;
1327 0 : wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply,
1328 : wh);
1329 0 : return wh;
1330 : }
1331 0 : qs = plc (plc_cls,
1332 0 : pd->section,
1333 : verification_id,
1334 : &wh->h_payto,
1335 : &wh->is_wallet,
1336 : &wh->process_row);
1337 0 : if (qs < 0)
1338 : {
1339 0 : wh->resp = TALER_MHD_make_error (TALER_EC_GENERIC_DB_FETCH_FAILED,
1340 : "provider-legitimization-lookup");
1341 0 : wh->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1342 0 : wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply,
1343 : wh);
1344 0 : return wh;
1345 : }
1346 0 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
1347 : {
1348 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1349 : "Received webhook for unknown verification ID `%s' and section `%s'\n",
1350 : verification_id,
1351 : pd->section);
1352 0 : wh->resp = TALER_MHD_make_error (
1353 : TALER_EC_EXCHANGE_KYC_PROOF_REQUEST_UNKNOWN,
1354 : verification_id);
1355 0 : wh->response_code = MHD_HTTP_NOT_FOUND;
1356 0 : wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply,
1357 : wh);
1358 0 : return wh;
1359 : }
1360 0 : wh->verification_id = GNUNET_strdup (verification_id);
1361 0 : wh->applicant_id = GNUNET_strdup (applicant_id);
1362 0 : if ( (0 != strcasecmp (type,
1363 0 : "VERIFICATION_COMPLETED")) ||
1364 0 : (no_verified) ||
1365 0 : (! verified) )
1366 : {
1367 : /* We don't need to re-confirm the failure by
1368 : asking the API again. */
1369 0 : log_failure (verifications);
1370 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1371 : "Webhook called with non-completion status: %s\n",
1372 : type);
1373 0 : wh->response_code = MHD_HTTP_NO_CONTENT;
1374 0 : wh->resp = MHD_create_response_from_buffer_static (0,
1375 : "");
1376 0 : wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply,
1377 : wh);
1378 0 : return wh;
1379 : }
1380 :
1381 0 : eh = curl_easy_init ();
1382 0 : if (NULL == eh)
1383 : {
1384 0 : GNUNET_break (0);
1385 0 : wh->resp = TALER_MHD_make_error (
1386 : TALER_EC_GENERIC_ALLOCATION_FAILURE,
1387 : NULL);
1388 0 : wh->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1389 0 : wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply,
1390 : wh);
1391 0 : return wh;
1392 : }
1393 :
1394 0 : GNUNET_asprintf (&wh->url,
1395 : "https://api.kycaid.com/applicants/%s",
1396 : applicant_id);
1397 0 : GNUNET_break (CURLE_OK ==
1398 : curl_easy_setopt (eh,
1399 : CURLOPT_VERBOSE,
1400 : 0));
1401 0 : GNUNET_assert (CURLE_OK ==
1402 : curl_easy_setopt (eh,
1403 : CURLOPT_MAXREDIRS,
1404 : 1L));
1405 0 : GNUNET_break (CURLE_OK ==
1406 : curl_easy_setopt (eh,
1407 : CURLOPT_URL,
1408 : wh->url));
1409 0 : wh->job = GNUNET_CURL_job_add2 (ps->curl_ctx,
1410 : eh,
1411 0 : pd->slist,
1412 : &handle_webhook_finished,
1413 : wh);
1414 0 : return wh;
1415 : }
1416 :
1417 :
1418 : /**
1419 : * Initialize kycaid logic plugin
1420 : *
1421 : * @param cls a configuration instance
1422 : * @return NULL on error, otherwise a `struct TALER_KYCLOGIC_Plugin`
1423 : */
1424 : void *
1425 : libtaler_plugin_kyclogic_kycaid_init (void *cls);
1426 :
1427 : /* declaration to avoid compiler warning */
1428 : void *
1429 76 : libtaler_plugin_kyclogic_kycaid_init (void *cls)
1430 : {
1431 76 : const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
1432 : struct TALER_KYCLOGIC_Plugin *plugin;
1433 : struct PluginState *ps;
1434 :
1435 76 : ps = GNUNET_new (struct PluginState);
1436 76 : ps->cfg = cfg;
1437 76 : if (GNUNET_OK !=
1438 76 : GNUNET_CONFIGURATION_get_value_string (cfg,
1439 : "exchange",
1440 : "BASE_URL",
1441 : &ps->exchange_base_url))
1442 : {
1443 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1444 : "exchange",
1445 : "BASE_URL");
1446 0 : GNUNET_free (ps);
1447 0 : return NULL;
1448 : }
1449 :
1450 : ps->curl_ctx
1451 152 : = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
1452 76 : &ps->curl_rc);
1453 76 : if (NULL == ps->curl_ctx)
1454 : {
1455 0 : GNUNET_break (0);
1456 0 : GNUNET_free (ps->exchange_base_url);
1457 0 : GNUNET_free (ps);
1458 0 : return NULL;
1459 : }
1460 76 : ps->curl_rc = GNUNET_CURL_gnunet_rc_create (ps->curl_ctx);
1461 :
1462 76 : plugin = GNUNET_new (struct TALER_KYCLOGIC_Plugin);
1463 76 : plugin->cls = ps;
1464 : plugin->load_configuration
1465 76 : = &kycaid_load_configuration;
1466 : plugin->unload_configuration
1467 76 : = &kycaid_unload_configuration;
1468 : plugin->initiate
1469 76 : = &kycaid_initiate;
1470 : plugin->initiate_cancel
1471 76 : = &kycaid_initiate_cancel;
1472 : plugin->proof
1473 76 : = &kycaid_proof;
1474 : plugin->proof_cancel
1475 76 : = &kycaid_proof_cancel;
1476 : plugin->webhook
1477 76 : = &kycaid_webhook;
1478 : plugin->webhook_cancel
1479 76 : = &kycaid_webhook_cancel;
1480 76 : return plugin;
1481 : }
1482 :
1483 :
1484 : /**
1485 : * Unload authorization plugin
1486 : *
1487 : * @param cls a `struct TALER_KYCLOGIC_Plugin`
1488 : * @return NULL (always)
1489 : */
1490 : void *
1491 : libtaler_plugin_kyclogic_kycaid_done (void *cls);
1492 :
1493 : /* declaration to avoid compiler warning */
1494 : void *
1495 76 : libtaler_plugin_kyclogic_kycaid_done (void *cls)
1496 : {
1497 76 : struct TALER_KYCLOGIC_Plugin *plugin = cls;
1498 76 : struct PluginState *ps = plugin->cls;
1499 :
1500 76 : if (NULL != ps->curl_ctx)
1501 : {
1502 76 : GNUNET_CURL_fini (ps->curl_ctx);
1503 76 : ps->curl_ctx = NULL;
1504 : }
1505 76 : if (NULL != ps->curl_rc)
1506 : {
1507 76 : GNUNET_CURL_gnunet_rc_destroy (ps->curl_rc);
1508 76 : ps->curl_rc = NULL;
1509 : }
1510 76 : GNUNET_free (ps->exchange_base_url);
1511 76 : GNUNET_free (ps);
1512 76 : GNUNET_free (plugin);
1513 76 : return NULL;
1514 : }
|