summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2021-11-15 20:00:45 +0100
committerChristian Grothoff <christian@grothoff.org>2021-11-15 20:00:45 +0100
commite5ead880579cbac93353b72e96221c84206a7403 (patch)
tree0f40cdc235a9e75f137fe3ac927f43f263b02826
parent0325a79631d1abba7fdf414748a34c0e8bca55c3 (diff)
downloadexchange-e5ead880579cbac93353b72e96221c84206a7403.tar.gz
exchange-e5ead880579cbac93353b72e96221c84206a7403.tar.bz2
exchange-e5ead880579cbac93353b72e96221c84206a7403.zip
complete oauth logic (in theory)
-rw-r--r--src/exchange/taler-exchange-httpd.c28
-rw-r--r--src/exchange/taler-exchange-httpd.h7
-rw-r--r--src/exchange/taler-exchange-httpd_kyc-proof.c301
-rw-r--r--src/exchangedb/exchange-0001.sql2
-rw-r--r--src/exchangedb/plugin_exchangedb_postgres.c8
-rw-r--r--src/include/taler_exchangedb_plugin.h4
-rw-r--r--src/testing/testing_api_cmd_oauth.c22
7 files changed, 303 insertions, 69 deletions
diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c
index 08f2ba471..c29984e2d 100644
--- a/src/exchange/taler-exchange-httpd.c
+++ b/src/exchange/taler-exchange-httpd.c
@@ -1197,6 +1197,34 @@ parse_kyc_oauth_cfg (void)
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",
"KYC_OAUTH2_CLIENT_ID",
&s))
{
diff --git a/src/exchange/taler-exchange-httpd.h b/src/exchange/taler-exchange-httpd.h
index f66626b3d..d52ce1a0f 100644
--- a/src/exchange/taler-exchange-httpd.h
+++ b/src/exchange/taler-exchange-httpd.h
@@ -91,11 +91,16 @@ 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.
*/
char *client_id;
diff --git a/src/exchange/taler-exchange-httpd_kyc-proof.c b/src/exchange/taler-exchange-httpd_kyc-proof.c
index fdf5ade54..7bd9fdaa6 100644
--- a/src/exchange/taler-exchange-httpd_kyc-proof.c
+++ b/src/exchange/taler-exchange-httpd_kyc-proof.c
@@ -73,6 +73,11 @@ struct KycProofContext
char *post_body;
/**
+ * User ID extracted from the OAuth 2.0 service, or NULL.
+ */
+ char *id;
+
+ /**
* Payment target this is about.
*/
unsigned long long payment_target_uuid;
@@ -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 439521a72..73a371f3b 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 6e77cb232..a5066e883 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 420e1e1e0..bf8a099f7 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 b71cc8386..64cb6c031 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);