complete oauth logic (in theory)

This commit is contained in:
Christian Grothoff 2021-11-15 20:00:45 +01:00
parent 0325a79631
commit e5ead88057
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
7 changed files with 305 additions and 71 deletions

View File

@ -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",

View File

@ -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.
*/

View File

@ -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! */
/* 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,
';')) )
{
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;
}
kpc_resume (kpc);
eh = curl_easy_init ();
if (NULL == eh)
{
GNUNET_break_op (0);
kpc->response
= TALER_MHD_make_error (
TALER_EC_GENERIC_ALLOCATION_FAILURE,
"curl_easy_init");
kpc->response_code
= MHD_HTTP_INTERNAL_SERVER_ERROR;
break;
}
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:
{
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;
kpc_resume (kpc);
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;
kpc_resume (kpc);
}
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);
}

View File

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

View File

@ -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 = {

View File

@ -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);
/**

View File

@ -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);