gnunet-svn
[Top][All Lists]
Advanced

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

[taler-exchange] 117/130: complete oauth logic (in theory)


From: gnunet
Subject: [taler-exchange] 117/130: complete oauth logic (in theory)
Date: Wed, 17 Nov 2021 12:26:05 +0100

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

grothoff pushed a commit to branch master
in repository exchange.

commit e5ead880579cbac93353b72e96221c84206a7403
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Mon Nov 15 20:00:45 2021 +0100

    complete oauth logic (in theory)
---
 src/exchange/taler-exchange-httpd.c           |  28 +++
 src/exchange/taler-exchange-httpd.h           |   7 +-
 src/exchange/taler-exchange-httpd_kyc-proof.c | 301 ++++++++++++++++++++------
 src/exchangedb/exchange-0001.sql              |   2 +-
 src/exchangedb/plugin_exchangedb_postgres.c   |   8 +-
 src/include/taler_exchangedb_plugin.h         |   4 +-
 src/testing/testing_api_cmd_oauth.c           |  22 ++
 7 files changed, 303 insertions(+), 69 deletions(-)

diff --git a/src/exchange/taler-exchange-httpd.c 
b/src/exchange/taler-exchange-httpd.c
index 08f2ba47..c29984e2 100644
--- a/src/exchange/taler-exchange-httpd.c
+++ b/src/exchange/taler-exchange-httpd.c
@@ -1194,6 +1194,34 @@ parse_kyc_oauth_cfg (void)
   }
   TEH_kyc_config.details.oauth2.url = s;
 
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
+                                             "exchange-kyc-oauth2",
+                                             "KYC_INFO_URL",
+                                             &s))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                               "exchange-kyc-oauth2",
+                               "KYC_INFO_URL");
+    return GNUNET_SYSERR;
+  }
+  if ( (! TALER_url_valid_charset (s)) ||
+       ( (0 != strncasecmp (s,
+                            "http://";,
+                            strlen ("http://";))) &&
+         (0 != strncasecmp (s,
+                            "https://";,
+                            strlen ("https://";))) ) )
+  {
+    GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+                               "exchange-kyc-oauth2",
+                               "KYC_INFO_URL",
+                               "not a valid URL");
+    GNUNET_free (s);
+    return GNUNET_SYSERR;
+  }
+  TEH_kyc_config.details.oauth2.info_url = s;
+
   if (GNUNET_OK !=
       GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
                                              "exchange-kyc-oauth2",
diff --git a/src/exchange/taler-exchange-httpd.h 
b/src/exchange/taler-exchange-httpd.h
index f66626b3..d52ce1a0 100644
--- a/src/exchange/taler-exchange-httpd.h
+++ b/src/exchange/taler-exchange-httpd.h
@@ -91,10 +91,15 @@ struct TEH_KycOptions
     {
 
       /**
-       * URL of tue OAuth2.0 endpoint for KYC checks.
+       * URL of the OAuth2.0 endpoint for KYC checks.
        */
       char *url;
 
+      /**
+       * URL of the user info access endpoint.
+       */
+      char *info_url;
+
       /**
        * Our client ID for OAuth2.0.
        */
diff --git a/src/exchange/taler-exchange-httpd_kyc-proof.c 
b/src/exchange/taler-exchange-httpd_kyc-proof.c
index fdf5ade5..7bd9fdaa 100644
--- a/src/exchange/taler-exchange-httpd_kyc-proof.c
+++ b/src/exchange/taler-exchange-httpd_kyc-proof.c
@@ -72,6 +72,11 @@ struct KycProofContext
    */
   char *post_body;
 
+  /**
+   * User ID extracted from the OAuth 2.0 service, or NULL.
+   */
+  char *id;
+
   /**
    * Payment target this is about.
    */
@@ -165,13 +170,186 @@ persist_kyc_ok (void *cls,
   struct KycProofContext *kpc = cls;
 
   return TEH_plugin->set_kyc_ok (TEH_plugin->cls,
-                                 kpc->payment_target_uuid);
+                                 kpc->payment_target_uuid,
+                                 kpc->id);
+}
+
+
+/**
+ * The request for @a kpc failed. We may have gotten a useful error
+ * message in @a j. Generate a failure response.
+ *
+ * @param[in,out] kpc request that failed
+ * @param j reply from the server (or NULL)
+ */
+static void
+handle_error (struct KycProofContext *kpc,
+              const json_t *j)
+{
+  const char *msg;
+  const char *desc;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_string ("error",
+                             &msg),
+    GNUNET_JSON_spec_string ("error_description",
+                             &desc),
+    GNUNET_JSON_spec_end ()
+  };
+
+  {
+    enum GNUNET_GenericReturnValue res;
+    const char *emsg;
+    unsigned int line;
+
+    res = GNUNET_JSON_parse (j,
+                             spec,
+                             &emsg,
+                             &line);
+    if (GNUNET_OK != res)
+    {
+      GNUNET_break_op (0);
+      kpc->response
+        = TALER_MHD_make_error (
+            TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE,
+            "Unexpected response from KYC gateway");
+      kpc->response_code
+        = MHD_HTTP_BAD_GATEWAY;
+      return;
+    }
+  }
+  /* case TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_AUTHORZATION_FAILED,
+     we MAY want to in the future look at the requested content type
+     and possibly respond in JSON if indicated. */
+  {
+    char *reply;
+
+    GNUNET_asprintf (&reply,
+                     
"<html><head><title>%s</title></head><body><h1>%s</h1><p>%s</p></body></html>",
+                     msg,
+                     msg,
+                     desc);
+    kpc->response
+      = MHD_create_response_from_buffer (strlen (reply),
+                                         reply,
+                                         MHD_RESPMEM_MUST_COPY);
+    GNUNET_assert (NULL != kpc->response);
+    GNUNET_free (reply);
+  }
+  kpc->response_code = MHD_HTTP_FORBIDDEN;
+}
+
+
+/**
+ * The request for @a kpc succeeded (presumably).
+ * Parse the user ID and store it in @a kpc (if possible).
+ *
+ * @param[in,out] kpc request that succeeded
+ * @param j reply from the server
+ */
+static void
+parse_success_reply (struct KycProofContext *kpc,
+                     const json_t *j)
+{
+  const char *state;
+  json_t *data;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_string ("status",
+                             &state),
+    GNUNET_JSON_spec_json ("data",
+                           &data),
+    GNUNET_JSON_spec_end ()
+  };
+  enum GNUNET_GenericReturnValue res;
+  const char *emsg;
+  unsigned int line;
+
+  res = GNUNET_JSON_parse (j,
+                           spec,
+                           &emsg,
+                           &line);
+  if (GNUNET_OK != res)
+  {
+    GNUNET_break_op (0);
+    kpc->response
+      = TALER_MHD_make_error (
+          TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE,
+          "Unexpected response from KYC gateway");
+    kpc->response_code
+      = MHD_HTTP_BAD_GATEWAY;
+    return;
+  }
+  if (0 != strcasecmp (state,
+                       "success"))
+  {
+    GNUNET_break_op (0);
+    handle_error (kpc,
+                  j);
+    return;
+  }
+  {
+    const char *id;
+    struct GNUNET_JSON_Specification ispec[] = {
+      GNUNET_JSON_spec_string ("id",
+                               &id),
+      GNUNET_JSON_spec_end ()
+    };
+
+    res = GNUNET_JSON_parse (data,
+                             ispec,
+                             &emsg,
+                             &line);
+    if (GNUNET_OK != res)
+    {
+      GNUNET_break_op (0);
+      kpc->response
+        = TALER_MHD_make_error (
+            TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE,
+            "Unexpected response from KYC gateway");
+      kpc->response_code
+        = MHD_HTTP_BAD_GATEWAY;
+      return;
+    }
+    kpc->id = GNUNET_strdup (id);
+  }
 }
 
 
 /**
  * After we are done with the CURL interaction we
- * need to update our database state.
+ * need to update our database state with the information
+ * retrieved.
+ *
+ * @param cls our `struct KycProofContext`
+ * @param response_code HTTP response code from server, 0 on hard error
+ * @param response in JSON, NULL if response was not in JSON format
+ */
+static void
+handle_curl_fetch_finished (void *cls,
+                            long response_code,
+                            const void *response)
+{
+  struct KycProofContext *kpc = cls;
+  const json_t *j = response;
+
+  kpc->job = NULL;
+  switch (response_code)
+  {
+  case MHD_HTTP_OK:
+    parse_success_reply (kpc,
+                         j);
+    break;
+  default:
+    handle_error (kpc,
+                  j);
+    break;
+  }
+  kpc_resume (kpc);
+}
+
+
+/**
+ * After we are done with the CURL interaction we
+ * need to fetch the user's account details.
  *
  * @param cls our `struct KycProofContext`
  * @param response_code HTTP response code from server, 0 on hard error
@@ -205,6 +383,7 @@ handle_curl_login_finished (void *cls,
                                  &refresh_token),
         GNUNET_JSON_spec_end ()
       };
+      CURL *eh;
 
       {
         enum GNUNET_GenericReturnValue res;
@@ -224,8 +403,7 @@ handle_curl_login_finished (void *cls,
                 "Unexpected response from KYC gateway");
           kpc->response_code
             = MHD_HTTP_BAD_GATEWAY;
-          kpc_resume (kpc);
-          return;
+          break;
         }
       }
       if (0 != strcasecmp (token_type,
@@ -238,74 +416,72 @@ handle_curl_login_finished (void *cls,
               "Unexpected token type in response from KYC gateway");
         kpc->response_code
           = MHD_HTTP_BAD_GATEWAY;
-        kpc_resume (kpc);
-        return;
+        break;
       }
 
-      /* TODO: Here we might want to keep something to persist in the DB, and
-         possibly use the access_token to download information we should
-         persist; then continue! */
-
-      kpc_resume (kpc);
-      return;
-    }
-  default:
-    {
-      const char *msg;
-      const char *desc;
-      struct GNUNET_JSON_Specification spec[] = {
-        GNUNET_JSON_spec_string ("error",
-                                 &msg),
-        GNUNET_JSON_spec_string ("error_description",
-                                 &desc),
-        GNUNET_JSON_spec_end ()
-      };
-
+      /* We guard against a few characters that could
+         conceivably be abused to mess with the HTTP header */
+      if ( (NULL != strchr (access_token,
+                            '\n')) ||
+           (NULL != strchr (access_token,
+                            '\r')) ||
+           (NULL != strchr (access_token,
+                            ' ')) ||
+           (NULL != strchr (access_token,
+                            ';')) )
       {
-        enum GNUNET_GenericReturnValue res;
-        const char *emsg;
-        unsigned int line;
-
-        res = GNUNET_JSON_parse (j,
-                                 spec,
-                                 &emsg,
-                                 &line);
-        if (GNUNET_OK != res)
-        {
-          GNUNET_break_op (0);
-          kpc->response
-            = TALER_MHD_make_error (
-                TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE,
-                "Unexpected response from KYC gateway");
-          kpc->response_code
-            = MHD_HTTP_BAD_GATEWAY;
-          kpc_resume (kpc);
-          return;
-        }
+        GNUNET_break_op (0);
+        kpc->response
+          = TALER_MHD_make_error (
+              TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE,
+              "Illegal character in access token");
+        kpc->response_code
+          = MHD_HTTP_BAD_GATEWAY;
+        break;
       }
-      /* case TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_AUTHORZATION_FAILED,
-         we MAY want to in the future look at the requested content type
-         and possibly respond in JSON if indicated. */
-      {
-        char *reply;
 
-        GNUNET_asprintf (&reply,
-                         
"<html><head><title>%s</title></head><body><h1>%s</h1><p>%s</p></body></html>",
-                         msg,
-                         msg,
-                         desc);
+      eh = curl_easy_init ();
+      if (NULL == eh)
+      {
+        GNUNET_break_op (0);
         kpc->response
-          = MHD_create_response_from_buffer (strlen (reply),
-                                             reply,
-                                             MHD_RESPMEM_MUST_COPY);
-        GNUNET_assert (NULL != kpc->response);
-        GNUNET_free (reply);
+          = TALER_MHD_make_error (
+              TALER_EC_GENERIC_ALLOCATION_FAILURE,
+              "curl_easy_init");
+        kpc->response_code
+          = MHD_HTTP_INTERNAL_SERVER_ERROR;
+        break;
       }
-      kpc->response_code = MHD_HTTP_FORBIDDEN;
-      kpc_resume (kpc);
+      GNUNET_assert (CURLE_OK ==
+                     curl_easy_setopt (eh,
+                                       CURLOPT_URL,
+                                       
TEH_kyc_config.details.oauth2.info_url));
+      {
+        char *hdr;
+        struct curl_slist *slist;
+
+        GNUNET_asprintf (&hdr,
+                         "%s: Bearer %s",
+                         MHD_HTTP_HEADER_AUTHORIZATION,
+                         access_token);
+        slist = curl_slist_append (NULL,
+                                   hdr);
+        kpc->job = GNUNET_CURL_job_add2 (TEH_curl_ctx,
+                                         eh,
+                                         slist,
+                                         &handle_curl_fetch_finished,
+                                         kpc);
+        curl_slist_free_all (slist);
+        GNUNET_free (hdr);
+      }
+      return;
     }
+  default:
+    handle_error (kpc,
+                  j);
     break;
   }
+  kpc_resume (kpc);
 }
 
 
@@ -331,6 +507,7 @@ clean_kpc (struct TEH_RequestContext *rc)
   }
   GNUNET_free (kpc->post_body);
   GNUNET_free (kpc->token_url);
+  GNUNET_free (kpc->id);
   GNUNET_free (kpc);
 }
 
diff --git a/src/exchangedb/exchange-0001.sql b/src/exchangedb/exchange-0001.sql
index 439521a7..73a371f3 100644
--- a/src/exchangedb/exchange-0001.sql
+++ b/src/exchangedb/exchange-0001.sql
@@ -71,7 +71,7 @@ CREATE TABLE IF NOT EXISTS wire_targets
 ,h_payto BYTEA NOT NULL CHECK (LENGTH(h_payto)=64)
 ,payto_uri VARCHAR NOT NULL
 ,kyc_ok BOOLEAN NOT NULL DEFAULT (FALSE)
-,oauth_username VARCHAR
+,external_id VARCHAR
 ,PRIMARY KEY (h_payto)
 );
 COMMENT ON TABLE wire_targets
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c 
b/src/exchangedb/plugin_exchangedb_postgres.c
index 6e77cb23..a5066e88 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -383,8 +383,9 @@ prepare_statements (struct PostgresClosure *pg)
       "set_kyc_ok",
       "UPDATE wire_targets"
       " SET kyc_ok=TRUE"
+      ",external_id=$2"
       " WHERE wire_target_serial_id=$1",
-      1),
+      2),
     GNUNET_PQ_make_prepare (
       "get_kyc_h_payto",
       "SELECT"
@@ -3799,17 +3800,18 @@ postgres_reserves_get (void *cls,
  *
  * @param cls the @e cls of this struct with the plugin-specific state
  * @param payment_target_uuid which account has been checked
- * @param ... possibly additional data to persist (TODO)
+ * @param id external ID to persist
  * @return transaction status
  */
 static enum GNUNET_DB_QueryStatus
 postgres_set_kyc_ok (void *cls,
                      uint64_t payment_target_uuid,
-                     ...)
+                     const char *id)
 {
   struct PostgresClosure *pg = cls;
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_uint64 (&payment_target_uuid),
+    GNUNET_PQ_query_param_string (id),
     GNUNET_PQ_query_param_end
   };
   struct TALER_KycCompletedEventP rep = {
diff --git a/src/include/taler_exchangedb_plugin.h 
b/src/include/taler_exchangedb_plugin.h
index 420e1e1e..bf8a099f 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -2376,13 +2376,13 @@ struct TALER_EXCHANGEDB_Plugin
    *
    * @param cls the @e cls of this struct with the plugin-specific state
    * @param payment_target_uuid which account has been checked
-   * @param ... possibly additional data to persist (TODO)
+   * @param id ID data to persist
    * @return transaction status
    */
   enum GNUNET_DB_QueryStatus
   (*set_kyc_ok)(void *cls,
                 uint64_t payment_target_uuid,
-                ...);
+                const char *id);
 
 
   /**
diff --git a/src/testing/testing_api_cmd_oauth.c 
b/src/testing/testing_api_cmd_oauth.c
index b71cc838..64cb6c03 100644
--- a/src/testing/testing_api_cmd_oauth.c
+++ b/src/testing/testing_api_cmd_oauth.c
@@ -169,6 +169,28 @@ handler_cb (void *cls,
   unsigned int hc;
   json_t *body;
 
+  if (0 == strcasecmp (method,
+                       MHD_HTTP_METHOD_GET))
+  {
+    body = GNUNET_JSON_PACK (
+      GNUNET_JSON_pack_string (
+        "status",
+        "success"),
+      GNUNET_JSON_pack_object_steal (
+        "data",
+        GNUNET_JSON_PACK (
+          GNUNET_JSON_pack_string ("id",
+                                   "XXXID12345678"))));
+    return TALER_MHD_reply_json_steal (connection,
+                                       body,
+                                       MHD_HTTP_OK);
+  }
+  if (0 != strcasecmp (method,
+                       MHD_HTTP_METHOD_POST))
+  {
+    GNUNET_break (0);
+    return MHD_NO;
+  }
   if (NULL == rc)
   {
     rc = GNUNET_new (struct RequestCtx);

-- 
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]