gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-exchange] branch master updated: untested draft of webhook logic


From: gnunet
Subject: [taler-exchange] branch master updated: untested draft of webhook logic for persona
Date: Thu, 18 Aug 2022 15:39:42 +0200

This is an automated email from the git hooks/post-receive script.

grothoff pushed a commit to branch master
in repository exchange.

The following commit(s) were added to refs/heads/master by this push:
     new 3194ccab untested draft of webhook logic for persona
3194ccab is described below

commit 3194ccabc1fa0ed52d59167668a7f546dbdbf377
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Thu Aug 18 15:39:28 2022 +0200

    untested draft of webhook logic for persona
---
 contrib/gana                           |   2 +-
 src/include/taler_kyclogic_lib.h       |  31 ++
 src/include/taler_kyclogic_plugin.h    |   8 +
 src/kyclogic/Makefile.am               |   4 +-
 src/kyclogic/kyclogic-persona.conf     |  33 ++
 src/kyclogic/kyclogic_api.c            |  37 +++
 src/kyclogic/plugin_kyclogic_kycaid.c  |  15 +
 src/kyclogic/plugin_kyclogic_persona.c | 588 ++++++++++++++++++++++-----------
 8 files changed, 521 insertions(+), 197 deletions(-)

diff --git a/contrib/gana b/contrib/gana
index ce901edb..2e264e28 160000
--- a/contrib/gana
+++ b/contrib/gana
@@ -1 +1 @@
-Subproject commit ce901edbaf496244f50f45b221d0c2c929c47637
+Subproject commit 2e264e2856ee1f490d894a64d36bd4eac71802eb
diff --git a/src/include/taler_kyclogic_lib.h b/src/include/taler_kyclogic_lib.h
index fedb26a9..df547c3d 100644
--- a/src/include/taler_kyclogic_lib.h
+++ b/src/include/taler_kyclogic_lib.h
@@ -237,6 +237,37 @@ TALER_KYCLOGIC_kyc_iterate_thresholds (
   void *it_cls);
 
 
+/**
+ * Function called with the provider details and
+ * associated plugin closures for matching logics.
+ *
+ * @param cls closure
+ * @param pd provider details of a matching logic
+ * @param plugin_cls closure of the plugin
+ * @return #GNUNET_OK to continue to iterate
+ */
+typedef enum GNUNET_GenericReturnValue
+(*TALER_KYCLOGIC_DetailsCallback)(
+  void *cls,
+  const struct TALER_KYCLOGIC_ProviderDetails *pd,
+  void *plugin_cls);
+
+
+/**
+ * Call @a cb for all logics with name @a logic_name,
+ * providing the plugin closure and the @a pd configurations.
+ *
+ * @param logic_name name of the logic to match
+ * @param cb function to call on matching results
+ * @param cb_cls closure for @a cb
+ */
+void
+TALER_KYCLOGIC_kyc_get_details (
+  const char *logic_name,
+  TALER_KYCLOGIC_DetailsCallback cb,
+  void *cb_cls);
+
+
 /**
  * Obtain the provider logic for a given @a provider_section_name.
  *
diff --git a/src/include/taler_kyclogic_plugin.h 
b/src/include/taler_kyclogic_plugin.h
index 7c0ebbc4..a4c166ab 100644
--- a/src/include/taler_kyclogic_plugin.h
+++ b/src/include/taler_kyclogic_plugin.h
@@ -77,6 +77,8 @@ enum TALER_KYCLOGIC_KycStatus
    * The provider is still checking.
    */
   TALER_KYCLOGIC_STATUS_PROVIDER_PENDING
+
+
     = TALER_KYCLOGIC_STATUS_PROVIDER
       | TALER_KYCLOGIC_STATUS_PENDING,
 
@@ -240,6 +242,12 @@ struct TALER_KYCLOGIC_Plugin
    */
   char *library_name;
 
+  /**
+   * Name of the logic, for webhook matching. Set by the
+   * plugin loader.
+   */
+  char *name;
+
   /**
    * Load the configuration of the KYC provider.
    *
diff --git a/src/kyclogic/Makefile.am b/src/kyclogic/Makefile.am
index 7b442bcf..280eda33 100644
--- a/src/kyclogic/Makefile.am
+++ b/src/kyclogic/Makefile.am
@@ -11,7 +11,8 @@ pkgcfgdir = $(prefix)/share/taler/config.d/
 pkgcfg_DATA = \
   kyclogic.conf \
   kyclogic-kycaid.conf \
-  kyclogic-oauth2.conf
+  kyclogic-oauth2.conf \
+  kyclogic-persona.conf
 
 EXTRA_DIST = \
   kyclogic.conf \
@@ -97,6 +98,7 @@ libtaler_plugin_kyclogic_persona_la_LIBADD = \
   $(LTLIBINTL)
 libtaler_plugin_kyclogic_persona_la_LDFLAGS = \
   $(TALER_PLUGIN_LDFLAGS) \
+  libtalerkyclogic.la \
   $(top_builddir)/src/json/libtalerjson.la \
   $(top_builddir)/src/curl/libtalercurl.la \
   $(top_builddir)/src/util/libtalerutil.la \
diff --git a/src/kyclogic/kyclogic-persona.conf 
b/src/kyclogic/kyclogic-persona.conf
new file mode 100644
index 00000000..de90238c
--- /dev/null
+++ b/src/kyclogic/kyclogic-persona.conf
@@ -0,0 +1,33 @@
+# This file is in the public domain.
+
+# FIXME: add to taler.conf man page!
+
+# Example persona provider configuration.
+
+[kyclogic-persona]
+
+# Optional authorization token for the webhook
+#WEBHOOK_AUTH_TOKEN = wbhsec_698b5a19-c790-47f6-b396-deb572ec82f9
+
+
+[kyc-provider-example-persona]
+
+COST = 42
+LOGIC = persona
+USER_TYPE = INDIVIDUAL
+PROVIDED_CHECKS = EXAMPLE_DO_NOT_USE
+
+# How long is the KYC check valid?
+PERSONA_VALIDITY = forever
+
+# Which subdomain is used for our API?
+PERSONA_SUBDOMAIN = taler
+
+# Authentication token to use.
+PERSONA_AUTH_TOKEN = persona_sandbox_42
+
+# Form to use.
+PERSONA_TEMPLATE_ID = itempl_Uj6Xxxxx
+
+# Where do we redirect to after KYC finished successfully.
+KYC_POST_URL = https://taler.net/
diff --git a/src/kyclogic/kyclogic_api.c b/src/kyclogic/kyclogic_api.c
index 18707a18..30386980 100644
--- a/src/kyclogic/kyclogic_api.c
+++ b/src/kyclogic/kyclogic_api.c
@@ -294,6 +294,7 @@ load_logic (const struct GNUNET_CONFIGURATION_Handle *cfg,
     return NULL;
   }
   plugin->library_name = lib_name;
+  plugin->name = GNUNET_strdup (name);
   GNUNET_array_append (kyc_logics,
                        num_kyc_logics,
                        plugin);
@@ -737,6 +738,7 @@ TALER_KYCLOGIC_kyc_done (void)
     struct TALER_KYCLOGIC_Plugin *lp = kyc_logics[i];
     char *lib_name = lp->library_name;
 
+    GNUNET_free (lp->name);
     GNUNET_assert (NULL == GNUNET_PLUGIN_unload (lib_name,
                                                  lp));
     GNUNET_free (lib_name);
@@ -1092,6 +1094,29 @@ TALER_KYCLOGIC_kyc_test_required (enum 
TALER_KYCLOGIC_KycTriggerEvent event,
 }
 
 
+void
+TALER_KYCLOGIC_kyc_get_details (
+  const char *logic_name,
+  TALER_KYCLOGIC_DetailsCallback cb,
+  void *cb_cls)
+{
+  for (unsigned int i = 0; i<num_kyc_providers; i++)
+  {
+    struct TALER_KYCLOGIC_KycProvider *kp = kyc_providers[i];
+
+    if (0 !=
+        strcmp (kp->logic->name,
+                logic_name))
+      continue;
+    if (GNUNET_OK !=
+        cb (cb_cls,
+            kp->pd,
+            kp->logic->cls))
+      return;
+  }
+}
+
+
 enum GNUNET_GenericReturnValue
 TALER_KYCLOGIC_kyc_get_logic (const char *provider_section_name,
                               struct TALER_KYCLOGIC_Plugin **plugin,
@@ -1109,6 +1134,18 @@ TALER_KYCLOGIC_kyc_get_logic (const char 
*provider_section_name,
     *pd = kp->pd;
     return GNUNET_OK;
   }
+  for (unsigned int i = 0; i<num_kyc_logics; i++)
+  {
+    struct TALER_KYCLOGIC_Plugin *logic = kyc_logics[i];
+
+    if (0 !=
+        strcasecmp (logic->name,
+                    provider_section_name))
+      continue;
+    *plugin = logic;
+    *pd = NULL;
+    return GNUNET_OK;
+  }
   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
               "Provider `%s' unknown\n",
               provider_section_name);
diff --git a/src/kyclogic/plugin_kyclogic_kycaid.c 
b/src/kyclogic/plugin_kyclogic_kycaid.c
index 8a5714e7..05bcb4f6 100644
--- a/src/kyclogic/plugin_kyclogic_kycaid.c
+++ b/src/kyclogic/plugin_kyclogic_kycaid.c
@@ -1081,6 +1081,21 @@ kycaid_webhook (void *cls,
   wh->pd = pd;
   wh->connection = connection;
 
+  if (NULL == pd)
+  {
+    GNUNET_break_op (0);
+    json_dumpf (body,
+                stderr,
+                JSON_INDENT (2));
+    wh->resp = TALER_MHD_make_error (
+      TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_UNKNOWN,
+      "kycaid");
+    wh->response_code = MHD_HTTP_NOT_FOUND;
+    wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply,
+                                         wh);
+    return wh;
+  }
+
   if (GNUNET_OK !=
       GNUNET_JSON_parse (body,
                          spec,
diff --git a/src/kyclogic/plugin_kyclogic_persona.c 
b/src/kyclogic/plugin_kyclogic_persona.c
index c8040f82..2ce8e6a1 100644
--- a/src/kyclogic/plugin_kyclogic_persona.c
+++ b/src/kyclogic/plugin_kyclogic_persona.c
@@ -23,6 +23,7 @@
 #include "taler_mhd_lib.h"
 #include "taler_curl_lib.h"
 #include "taler_json_lib.h"
+#include "taler_kyclogic_lib.h"
 #include "taler_templating_lib.h"
 #include <regex.h>
 #include "taler_util.h"
@@ -60,6 +61,13 @@ struct PluginState
    */
   struct GNUNET_CURL_RescheduleContext *curl_rc;
 
+  /**
+ * Authorization token to use when receiving webhooks from the Persona 
service.  Optional.  Note that
+ * webhooks are *global* and not per template.
+ */
+  char *webhook_token;
+
+
 };
 
 
@@ -233,7 +241,7 @@ struct TALER_KYCLOGIC_ProofHandle
   /**
    * Inquiry ID at the provider.
    */
-  char *provider_legitimization_id;
+  char *inquiry_id;
 };
 
 
@@ -293,16 +301,21 @@ struct TALER_KYCLOGIC_WebhookHandle
    */
   struct MHD_Response *resp;
 
+  /**
+   * ID of the template the webhook is about,
+   * according to the service.
+   */
+  const char *template_id;
+
   /**
    * Our account ID.
    */
   struct TALER_PaytoHashP h_payto;
 
   /**
-   * Row in legitimizations for the given
-   * @e verification_id.
+   * UUID being checked.
    */
-  uint64_t legi_row;
+  uint64_t legitimization_uuid;
 
   /**
    * HTTP response code to return asynchronously.
@@ -807,7 +820,7 @@ persona_proof_cancel (struct TALER_KYCLOGIC_ProofHandle *ph)
   }
   GNUNET_free (ph->url);
   GNUNET_free (ph->provider_user_id);
-  GNUNET_free (ph->provider_legitimization_id);
+  GNUNET_free (ph->inquiry_id);
   GNUNET_free (ph);
 }
 
@@ -835,7 +848,12 @@ proof_generic_reply (struct TALER_KYCLOGIC_ProofHandle *ph,
 {
   struct MHD_Response *resp;
   enum GNUNET_GenericReturnValue ret;
+  struct GNUNET_TIME_Absolute expiration;
 
+  if (TALER_KYCLOGIC_STATUS_SUCCESS == status)
+    expiration = GNUNET_TIME_relative_to_absolute (ph->pd->validity);
+  else
+    expiration = GNUNET_TIME_UNIT_ZERO_ABS;
   ret = TALER_TEMPLATING_build (ph->connection,
                                 &http_status,
                                 template,
@@ -852,7 +870,7 @@ proof_generic_reply (struct TALER_KYCLOGIC_ProofHandle *ph,
           status,
           account_id,
           inquiry_id,
-          GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
+          expiration,
           http_status,
           resp);
 }
@@ -1033,7 +1051,7 @@ handle_proof_finished (void *cls,
         }
 
         if (0 != strcmp (inquiry_id,
-                         ph->provider_legitimization_id))
+                         ph->inquiry_id))
         {
           GNUNET_break_op (0);
           proof_reply_error (ph,
@@ -1146,7 +1164,7 @@ handle_proof_finished (void *cls,
                 stderr,
                 JSON_INDENT (2));
     proof_reply_error (ph,
-                       ph->provider_legitimization_id,
+                       ph->inquiry_id,
                        MHD_HTTP_BAD_GATEWAY,
                        "persona-logic-failure",
                        GNUNET_JSON_PACK (
@@ -1166,7 +1184,7 @@ handle_proof_finished (void *cls,
                 "Refused access with HTTP status code %u\n",
                 (unsigned int) response_code);
     proof_reply_error (ph,
-                       ph->provider_legitimization_id,
+                       ph->inquiry_id,
                        MHD_HTTP_INTERNAL_SERVER_ERROR,
                        "persona-exchange-unauthorized",
                        GNUNET_JSON_PACK (
@@ -1186,7 +1204,7 @@ handle_proof_finished (void *cls,
                 (unsigned int) response_code);
 
     proof_reply_error (ph,
-                       ph->provider_legitimization_id,
+                       ph->inquiry_id,
                        MHD_HTTP_INTERNAL_SERVER_ERROR,
                        "persona-exchange-unpaid",
                        GNUNET_JSON_PACK (
@@ -1208,7 +1226,7 @@ handle_proof_finished (void *cls,
                 stderr,
                 JSON_INDENT (2));
     proof_reply_error (ph,
-                       ph->provider_legitimization_id,
+                       ph->inquiry_id,
                        MHD_HTTP_GATEWAY_TIMEOUT,
                        "persona-network-timeout",
                        GNUNET_JSON_PACK (
@@ -1230,7 +1248,7 @@ handle_proof_finished (void *cls,
                 stderr,
                 JSON_INDENT (2));
     proof_reply_error (ph,
-                       ph->provider_legitimization_id,
+                       ph->inquiry_id,
                        MHD_HTTP_SERVICE_UNAVAILABLE,
                        "persona-load-failure",
                        GNUNET_JSON_PACK (
@@ -1252,7 +1270,7 @@ handle_proof_finished (void *cls,
                 stderr,
                 JSON_INDENT (2));
     proof_reply_error (ph,
-                       ph->provider_legitimization_id,
+                       ph->inquiry_id,
                        MHD_HTTP_BAD_GATEWAY,
                        "persona-provider-failure",
                        GNUNET_JSON_PACK (
@@ -1274,7 +1292,7 @@ handle_proof_finished (void *cls,
                 stderr,
                 JSON_INDENT (2));
     proof_reply_error (ph,
-                       ph->provider_legitimization_id,
+                       ph->inquiry_id,
                        MHD_HTTP_BAD_GATEWAY,
                        "persona-invalid-response",
                        GNUNET_JSON_PACK (
@@ -1304,7 +1322,7 @@ handle_proof_finished (void *cls,
  * @param account_id which account to trigger process for
  * @param legi_row row in the table the legitimization is for
  * @param provider_user_id user ID (or NULL) the proof is for
- * @param provider_legitimization_id legitimization ID the proof is for
+ * @param inquiry_id legitimization ID the proof is for
  * @param cb function to call with the result
  * @param cb_cls closure for @a cb
  * @return handle to cancel operation early
@@ -1317,7 +1335,7 @@ persona_proof (void *cls,
                const struct TALER_PaytoHashP *account_id,
                uint64_t legi_row,
                const char *provider_user_id,
-               const char *provider_legitimization_id,
+               const char *inquiry_id,
                TALER_KYCLOGIC_ProofCallback cb,
                void *cb_cls)
 {
@@ -1339,15 +1357,14 @@ persona_proof (void *cls,
   ph->connection = connection;
   ph->legitimization_uuid = legi_row;
   ph->h_payto = *account_id;
-  /* NOTE: we do not expect this to be non-NULL */
+  /* Note: we do not expect this to be non-NULL */
   if (NULL != provider_user_id)
     ph->provider_user_id = GNUNET_strdup (provider_user_id);
-  /* This should be the inquiry ID; FIXME: rename variable? */
-  if (NULL != provider_legitimization_id)
-    ph->provider_legitimization_id = GNUNET_strdup 
(provider_legitimization_id);
+  if (NULL != inquiry_id)
+    ph->inquiry_id = GNUNET_strdup (inquiry_id);
   GNUNET_asprintf (&ph->url,
                    "https://withpersona.com/api/v1/inquiries/%s";,
-                   provider_legitimization_id);
+                   inquiry_id);
   GNUNET_break (CURLE_OK ==
                 curl_easy_setopt (eh,
                                   CURLOPT_VERBOSE,
@@ -1393,6 +1410,70 @@ persona_webhook_cancel (struct 
TALER_KYCLOGIC_WebhookHandle *wh)
 }
 
 
+/**
+ * Call @a wh callback with the operation result.
+ *
+ * @param wh proof handle to generate reply for
+ * @param status status to return
+ * @param account_id account to return
+ * @param inquiry_id inquiry ID to supply
+ * @param http_status HTTP status to use
+ * @param template template to instantiate
+ * @param[in] body body for the template to use (reference
+ *         is consumed)
+ */
+static void
+webhook_generic_reply (struct TALER_KYCLOGIC_WebhookHandle *wh,
+                       enum TALER_KYCLOGIC_KycStatus status,
+                       const char *account_id,
+                       const char *inquiry_id,
+                       unsigned int http_status)
+{
+  struct MHD_Response *resp;
+  struct GNUNET_TIME_Absolute expiration;
+
+  if (TALER_KYCLOGIC_STATUS_SUCCESS == status)
+    expiration = GNUNET_TIME_relative_to_absolute (wh->pd->validity);
+  else
+    expiration = GNUNET_TIME_UNIT_ZERO_ABS;
+  resp = MHD_create_response_from_buffer (0,
+                                          "",
+                                          MHD_RESPMEM_PERSISTENT);
+  wh->cb (wh->cb_cls,
+          wh->legitimization_uuid,
+          &wh->h_payto,
+          account_id,
+          inquiry_id,
+          status,
+          expiration,
+          http_status,
+          resp);
+}
+
+
+/**
+ * Call @a wh callback with HTTP error response.
+ *
+ * @param wh proof handle to generate reply for
+ * @param inquiry_id inquiry ID to supply
+ * @param http_status HTTP status to use
+ * @param template template to instantiate
+ * @param[in] body body for the template to use (reference
+ *         is consumed)
+ */
+static void
+webhook_reply_error (struct TALER_KYCLOGIC_WebhookHandle *wh,
+                     const char *inquiry_id,
+                     unsigned int http_status)
+{
+  webhook_generic_reply (wh,
+                         TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
+                         NULL, /* user id */
+                         inquiry_id,
+                         http_status);
+}
+
+
 /**
  * Function called when we're done processing the
  * HTTP "/verifications/{verification_id}" request.
@@ -1408,234 +1489,243 @@ handle_webhook_finished (void *cls,
 {
   struct TALER_KYCLOGIC_WebhookHandle *wh = cls;
   const json_t *j = response;
+  const json_t *data = json_object_get (j,
+                                        "data");
 
   wh->job = NULL;
   json_dumpf (j,
               stderr,
               JSON_INDENT (2));
-#if 0
-  struct MHD_Response *resp;
-
   switch (response_code)
   {
   case MHD_HTTP_OK:
     {
-      const char *applicant_id;
-      const char *verification_id;
-      const char *status;
-      bool verified;
-      json_t *verifications;
+      const char *inquiry_id;
+      const char *account_id;
+      const char *type = NULL;
+      json_t *attributes;
       struct GNUNET_JSON_Specification spec[] = {
-        GNUNET_JSON_spec_string ("applicant_id",
-                                 &applicant_id),
-        GNUNET_JSON_spec_string ("verification_id",
-                                 &verification_id),
-        GNUNET_JSON_spec_string ("status",
-                                 &status), /* completed, pending, ... */
-        GNUNET_JSON_spec_bool ("verified",
-                               &verified),
-        GNUNET_JSON_spec_json ("verifications",
-                               &verifications),
+        GNUNET_JSON_spec_string ("type",
+                                 &type),
+        GNUNET_JSON_spec_string ("id",
+                                 &inquiry_id),
+        GNUNET_JSON_spec_json ("attributes",
+                               &attributes),
         GNUNET_JSON_spec_end ()
       };
-      struct GNUNET_TIME_Absolute expiration;
 
-      if (GNUNET_OK !=
-          GNUNET_JSON_parse (j,
-                             spec,
-                             NULL, NULL))
+      if ( (NULL == data) ||
+           (GNUNET_OK !=
+            GNUNET_JSON_parse (data,
+                               spec,
+                               NULL, NULL)) ||
+           (0 != strcmp (type,
+                         "inquiry")) )
       {
         GNUNET_break_op (0);
         json_dumpf (j,
                     stderr,
                     JSON_INDENT (2));
-        resp = TALER_MHD_MAKE_JSON_PACK (
-          GNUNET_JSON_pack_uint64 ("persona_http_status",
-                                   response_code),
-          GNUNET_JSON_pack_object_incref ("persona_body",
-                                          (json_t *) j));
-        wh->cb (wh->cb_cls,
-                wh->legi_row,
-                &wh->h_payto,
-                wh->applicant_id,
-                wh->verification_id,
-                TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
-                GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
-                MHD_HTTP_BAD_GATEWAY,
-                resp);
+        webhook_reply_error (wh,
+                             inquiry_id,
+                             MHD_HTTP_BAD_GATEWAY);
         break;
       }
-      if (! verified)
-      {
-        log_failure (verifications);
-      }
-      resp = MHD_create_response_from_buffer (0,
-                                              "",
-                                              MHD_RESPMEM_PERSISTENT);
-      if (verified)
-      {
-        expiration = GNUNET_TIME_relative_to_absolute (wh->pd->validity);
-        wh->cb (wh->cb_cls,
-                wh->legi_row,
-                &wh->h_payto,
-                wh->applicant_id,
-                wh->verification_id,
-                TALER_KYCLOGIC_STATUS_SUCCESS,
-                expiration,
-                MHD_HTTP_NO_CONTENT,
-                resp);
-      }
-      else
+
       {
-        wh->cb (wh->cb_cls,
-                wh->legi_row,
-                &wh->h_payto,
-                wh->applicant_id,
-                wh->verification_id,
-                TALER_KYCLOGIC_STATUS_USER_ABORTED,
-                GNUNET_TIME_UNIT_ZERO_ABS,
-                MHD_HTTP_NO_CONTENT,
-                resp);
+        const char *status; /* "completed", what else? */
+        const char *reference_id; /* or legitimization number */
+        const char *expired_at = NULL; /* often 'null' format: 
"2022-08-18T10:14:26.000Z" */
+        struct GNUNET_JSON_Specification ispec[] = {
+          GNUNET_JSON_spec_string ("status",
+                                   &status),
+          GNUNET_JSON_spec_string ("reference_id",
+                                   &reference_id),
+          GNUNET_JSON_spec_mark_optional (
+            GNUNET_JSON_spec_string ("expired_at",
+                                     &expired_at),
+            NULL),
+          GNUNET_JSON_spec_end ()
+        };
+
+        if (GNUNET_OK !=
+            GNUNET_JSON_parse (attributes,
+                               ispec,
+                               NULL, NULL))
+        {
+          GNUNET_break_op (0);
+          json_dumpf (j,
+                      stderr,
+                      JSON_INDENT (2));
+          webhook_reply_error (wh,
+                               inquiry_id,
+                               MHD_HTTP_BAD_GATEWAY);
+          GNUNET_JSON_parse_free (ispec);
+          GNUNET_JSON_parse_free (spec);
+          break;
+        }
+        {
+          unsigned long long idr;
+          char dummy;
+
+          if ( (1 != sscanf (reference_id,
+                             "%llu%c",
+                             &idr,
+                             &dummy)) ||
+               (idr != wh->legitimization_uuid) )
+          {
+            GNUNET_break_op (0);
+            webhook_reply_error (wh,
+                                 inquiry_id,
+                                 MHD_HTTP_BAD_GATEWAY);
+            GNUNET_JSON_parse_free (ispec);
+            GNUNET_JSON_parse_free (spec);
+            break;
+          }
+        }
+
+        if (0 != strcmp (inquiry_id,
+                         wh->inquiry_id))
+        {
+          GNUNET_break_op (0);
+          webhook_reply_error (wh,
+                               inquiry_id,
+                               MHD_HTTP_BAD_GATEWAY);
+          GNUNET_JSON_parse_free (ispec);
+          GNUNET_JSON_parse_free (spec);
+          break;
+        }
+
+        account_id = json_string_value (
+          json_object_get (
+            json_object_get (
+              json_object_get (
+                json_object_get (
+                  data,
+                  "relationships"),
+                "account"),
+              "data"),
+            "id"));
+
+        if (0 != strcmp (status,
+                         "completed"))
+        {
+          webhook_generic_reply (wh,
+                                 TALER_KYCLOGIC_STATUS_FAILED,
+                                 account_id,
+                                 inquiry_id,
+                                 MHD_HTTP_OK);
+          GNUNET_JSON_parse_free (ispec);
+          GNUNET_JSON_parse_free (spec);
+          break;
+        }
+
+        if (NULL == account_id)
+        {
+          GNUNET_break_op (0);
+          json_dumpf (data,
+                      stderr,
+                      JSON_INDENT (2));
+          webhook_reply_error (wh,
+                               inquiry_id,
+                               MHD_HTTP_BAD_GATEWAY);
+          break;
+        }
+
+        webhook_generic_reply (wh,
+                               TALER_KYCLOGIC_STATUS_SUCCESS,
+                               account_id,
+                               inquiry_id,
+                               MHD_HTTP_OK);
+        GNUNET_JSON_parse_free (ispec);
       }
       GNUNET_JSON_parse_free (spec);
+      break;
     }
-    break;
   case MHD_HTTP_BAD_REQUEST:
   case MHD_HTTP_NOT_FOUND:
   case MHD_HTTP_CONFLICT:
+  case MHD_HTTP_UNPROCESSABLE_ENTITY:
+    /* These are errors with this code */
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                 "PERSONA failed with response %u:\n",
                 (unsigned int) response_code);
     json_dumpf (j,
                 stderr,
                 JSON_INDENT (2));
-    resp = TALER_MHD_MAKE_JSON_PACK (
-      GNUNET_JSON_pack_uint64 ("persona_http_status",
-                               response_code));
-    wh->cb (wh->cb_cls,
-            wh->legi_row,
-            &wh->h_payto,
-            wh->applicant_id,
-            wh->verification_id,
-            TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
-            GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
-            MHD_HTTP_INTERNAL_SERVER_ERROR,
-            resp);
+    webhook_reply_error (wh,
+                         wh->inquiry_id,
+                         MHD_HTTP_BAD_GATEWAY);
     break;
   case MHD_HTTP_UNAUTHORIZED:
+    /* These are failures of the exchange operator */
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Refused access with HTTP status code %u\n",
+                (unsigned int) response_code);
+    webhook_reply_error (wh,
+                         wh->inquiry_id,
+                         MHD_HTTP_INTERNAL_SERVER_ERROR);
+    break;
   case MHD_HTTP_PAYMENT_REQUIRED:
+    /* These are failures of the exchange operator */
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                 "Refused access with HTTP status code %u\n",
                 (unsigned int) response_code);
-    resp = TALER_MHD_MAKE_JSON_PACK (
-      GNUNET_JSON_pack_uint64 ("persona_http_status",
-                               response_code),
-      GNUNET_JSON_pack_object_incref ("persona_body",
-                                      (json_t *) j));
-    wh->cb (wh->cb_cls,
-            wh->legi_row,
-            &wh->h_payto,
-            wh->applicant_id,
-            wh->verification_id,
-            TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
-            GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
-            MHD_HTTP_NETWORK_AUTHENTICATION_REQUIRED,
-            resp);
+
+    webhook_reply_error (wh,
+                         wh->inquiry_id,
+                         MHD_HTTP_INTERNAL_SERVER_ERROR);
     break;
   case MHD_HTTP_REQUEST_TIMEOUT:
-    resp = TALER_MHD_MAKE_JSON_PACK (
-      GNUNET_JSON_pack_uint64 ("persona_http_status",
-                               response_code),
-      GNUNET_JSON_pack_object_incref ("persona_body",
-                                      (json_t *) j));
-    wh->cb (wh->cb_cls,
-            wh->legi_row,
-            &wh->h_payto,
-            wh->applicant_id,
-            wh->verification_id,
-            TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
-            GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
-            MHD_HTTP_GATEWAY_TIMEOUT,
-            resp);
-    break;
-  case MHD_HTTP_UNPROCESSABLE_ENTITY: /* validation */
+    /* These are networking issues */
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                 "PERSONA failed with response %u:\n",
                 (unsigned int) response_code);
     json_dumpf (j,
                 stderr,
                 JSON_INDENT (2));
-    resp = TALER_MHD_MAKE_JSON_PACK (
-      GNUNET_JSON_pack_uint64 ("persona_http_status",
-                               response_code),
-      GNUNET_JSON_pack_object_incref ("persona_body",
-                                      (json_t *) j));
-    wh->cb (wh->cb_cls,
-            wh->legi_row,
-            &wh->h_payto,
-            wh->applicant_id,
-            wh->verification_id,
-            TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
-            GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
-            MHD_HTTP_BAD_GATEWAY,
-            resp);
+    webhook_reply_error (wh,
+                         wh->inquiry_id,
+                         MHD_HTTP_GATEWAY_TIMEOUT);
     break;
   case MHD_HTTP_TOO_MANY_REQUESTS:
-    resp = TALER_MHD_MAKE_JSON_PACK (
-      GNUNET_JSON_pack_uint64 ("persona_http_status",
-                               response_code),
-      GNUNET_JSON_pack_object_incref ("persona_body",
-                                      (json_t *) j));
-    wh->cb (wh->cb_cls,
-            wh->legi_row,
-            &wh->h_payto,
-            wh->applicant_id,
-            wh->verification_id,
-            TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
-            GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
-            MHD_HTTP_SERVICE_UNAVAILABLE,
-            resp);
+    /* This is a load issue */
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "PERSONA failed with response %u:\n",
+                (unsigned int) response_code);
+    json_dumpf (j,
+                stderr,
+                JSON_INDENT (2));
+    webhook_reply_error (wh,
+                         wh->inquiry_id,
+                         MHD_HTTP_SERVICE_UNAVAILABLE);
     break;
   case MHD_HTTP_INTERNAL_SERVER_ERROR:
-    resp = TALER_MHD_MAKE_JSON_PACK (
-      GNUNET_JSON_pack_uint64 ("persona_http_status",
-                               response_code),
-      GNUNET_JSON_pack_object_incref ("persona_body",
-                                      (json_t *) j));
-    wh->cb (wh->cb_cls,
-            wh->legi_row,
-            &wh->h_payto,
-            wh->applicant_id,
-            wh->verification_id,
-            TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
-            GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
-            MHD_HTTP_BAD_GATEWAY,
-            resp);
+    /* This is an issue with Persona */
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "PERSONA failed with response %u:\n",
+                (unsigned int) response_code);
+    json_dumpf (j,
+                stderr,
+                JSON_INDENT (2));
+    webhook_reply_error (wh,
+                         wh->inquiry_id,
+                         MHD_HTTP_BAD_GATEWAY);
     break;
   default:
-    resp = TALER_MHD_MAKE_JSON_PACK (
-      GNUNET_JSON_pack_uint64 ("persona_http_status",
-                               response_code),
-      GNUNET_JSON_pack_object_incref ("persona_body",
-                                      (json_t *) j));
+    /* This is an issue with Persona */
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected PERSONA response %u:\n",
+                "PERSONA failed with response %u:\n",
                 (unsigned int) response_code);
     json_dumpf (j,
                 stderr,
                 JSON_INDENT (2));
-    wh->cb (wh->cb_cls,
-            wh->legi_row,
-            &wh->h_payto,
-            wh->applicant_id,
-            wh->verification_id,
-            TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
-            GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
-            MHD_HTTP_BAD_GATEWAY,
-            resp);
+    webhook_reply_error (wh,
+                         wh->inquiry_id,
+                         MHD_HTTP_BAD_GATEWAY);
     break;
   }
-#endif
+
   persona_webhook_cancel (wh);
 }
 
@@ -1651,8 +1741,8 @@ async_webhook_reply (void *cls)
   struct TALER_KYCLOGIC_WebhookHandle *wh = cls;
 
   wh->cb (wh->cb_cls,
-          wh->legi_row,
-          (0 == wh->legi_row)
+          wh->legitimization_uuid,
+          (0 == wh->legitimization_uuid)
           ? NULL
           : &wh->h_payto,
           NULL, /* FIXME: never known here, but maybe prevent clearing it in 
the DB as it should already be there? */
@@ -1665,6 +1755,35 @@ async_webhook_reply (void *cls)
 }
 
 
+/**
+ * Function called with the provider details and
+ * associated plugin closures for matching logics.
+ *
+ * @param cls closure
+ * @param pd provider details of a matching logic
+ * @param plugin_cls closure of the plugin
+ * @return #GNUNET_OK to continue to iterate
+ */
+static enum GNUNET_GenericReturnValue
+locate_details_cb (
+  void *cls,
+  const struct TALER_KYCLOGIC_ProviderDetails *pd,
+  void *plugin_cls)
+{
+  struct TALER_KYCLOGIC_WebhookHandle *wh = cls;
+
+  /* This type-checks 'pd' */
+  GNUNET_assert (plugin_cls == wh->ps);
+  if (0 == strcmp (pd->template_id,
+                   wh->template_id))
+  {
+    wh->pd = pd;
+    return GNUNET_NO;
+  }
+  return GNUNET_OK;
+}
+
+
 /**
  * Check KYC status and return result for Webhook.  We do NOT implement the
  * authentication check proposed by the PERSONA documentation, as it would
@@ -1701,14 +1820,79 @@ persona_webhook (void *cls,
   CURL *eh;
   enum GNUNET_DB_QueryStatus qs;
   const char *persona_inquiry_id;
+  const char *auth_header;
 
-  // FIXME: check webhook 'Authorization' header first!
+  /* Persona webhooks are expected by logic, not by template */
+  GNUNET_break_op (NULL == pd);
   wh = GNUNET_new (struct TALER_KYCLOGIC_WebhookHandle);
   wh->cb = cb;
   wh->cb_cls = cb_cls;
   wh->ps = ps;
-  wh->pd = pd;
   wh->connection = connection;
+  wh->pd = pd;
+
+  auth_header = MHD_lookup_connection_value (connection,
+                                             MHD_HEADER_KIND,
+                                             MHD_HTTP_HEADER_AUTHORIZATION);
+  if ( (NULL != ps->webhook_token) &&
+       (0 != strcmp (ps->webhook_token,
+                     auth_header)) )
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Invalid authorization header `%s' received for Persona 
webhook\n",
+                auth_header);
+    wh->resp = TALER_MHD_MAKE_JSON_PACK (
+      TALER_JSON_pack_ec (
+        TALER_EC_EXCHANGE_KYC_WEBHOOK_UNAUTHORIZED),
+      GNUNET_JSON_pack_string ("detail",
+                               "unexpected 'Authorization' header"));
+    wh->response_code = MHD_HTTP_UNAUTHORIZED;
+    wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply,
+                                         wh);
+    return wh;
+  }
+
+  wh->template_id
+    = json_string_value (
+        json_object_get (
+          json_object_get (
+            json_object_get (
+              json_object_get (
+                json_object_get (
+                  json_object_get (
+                    json_object_get (
+                      json_object_get (
+                        body,
+                        "data"),
+                      "attributes"),
+                    "payload"),
+                  "data"),
+                "relationships"),
+              "template"),
+            "data"),
+          "id"));
+  TALER_KYCLOGIC_kyc_get_details ("persona",
+                                  &locate_details_cb,
+                                  wh);
+  if (NULL == wh->pd)
+  {
+    GNUNET_break_op (0);
+    json_dumpf (body,
+                stderr,
+                JSON_INDENT (2));
+    wh->resp = TALER_MHD_MAKE_JSON_PACK (
+      TALER_JSON_pack_ec (
+        TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_UNKNOWN),
+      GNUNET_JSON_pack_string ("detail",
+                               wh->template_id),
+      GNUNET_JSON_pack_object_incref ("webhook_body",
+                                      (json_t *) body));
+    wh->response_code = MHD_HTTP_BAD_REQUEST;
+    wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply,
+                                         wh);
+    return wh;
+  }
+
 
   persona_inquiry_id
     = json_string_value (
@@ -1730,6 +1914,10 @@ persona_webhook (void *cls,
                 stderr,
                 JSON_INDENT (2));
     wh->resp = TALER_MHD_MAKE_JSON_PACK (
+      TALER_JSON_pack_ec (
+        TALER_EC_EXCHANGE_KYC_GENERIC_PROVIDER_UNEXPECTED_REPLY),
+      GNUNET_JSON_pack_string ("detail",
+                               "data-attributes-payload-data-id"),
       GNUNET_JSON_pack_object_incref ("webhook_body",
                                       (json_t *) body));
     wh->response_code = MHD_HTTP_BAD_REQUEST;
@@ -1741,7 +1929,7 @@ persona_webhook (void *cls,
             pd->section,
             persona_inquiry_id,
             &wh->h_payto,
-            &wh->legi_row);
+            &wh->legitimization_uuid);
   if (qs < 0)
   {
     wh->resp = TALER_MHD_make_error (TALER_EC_GENERIC_DB_FETCH_FAILED,
@@ -1754,7 +1942,7 @@ persona_webhook (void *cls,
   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                "Received webhook for unknown verification ID `%s'\n",
+                "Received Persona kyc-webhook for unknown verification ID 
`%s'\n",
                 persona_inquiry_id);
     wh->resp = TALER_MHD_make_error (
       TALER_EC_EXCHANGE_KYC_PROOF_REQUEST_UNKNOWN,
@@ -1830,6 +2018,15 @@ libtaler_plugin_kyclogic_persona_init (void *cls)
     GNUNET_free (ps);
     return NULL;
   }
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_string (ps->cfg,
+                                             "kyclogic-persona",
+                                             "WEBHOOK_AUTH_TOKEN",
+                                             &ps->webhook_token))
+  {
+    /* optional */
+    ps->webhook_token = NULL;
+  }
 
   ps->curl_ctx
     = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
@@ -1888,6 +2085,7 @@ libtaler_plugin_kyclogic_persona_done (void *cls)
     ps->curl_rc = NULL;
   }
   GNUNET_free (ps->exchange_base_url);
+  GNUNET_free (ps->webhook_token);
   GNUNET_free (ps);
   GNUNET_free (plugin);
   return NULL;

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]