aboutsummaryrefslogtreecommitdiff
path: root/src/kyclogic
diff options
context:
space:
mode:
Diffstat (limited to 'src/kyclogic')
-rw-r--r--src/kyclogic/Makefile.am1
-rw-r--r--src/kyclogic/kyclogic-kycaid.conf4
-rw-r--r--src/kyclogic/kyclogic-oauth2.conf6
-rw-r--r--src/kyclogic/plugin_kyclogic_kycaid.c395
-rw-r--r--src/kyclogic/plugin_kyclogic_oauth2.c229
-rw-r--r--src/kyclogic/taler-exchange-kyc-kycaid-converter.sh86
6 files changed, 456 insertions, 265 deletions
diff --git a/src/kyclogic/Makefile.am b/src/kyclogic/Makefile.am
index 20430a4ea..75ea13b6f 100644
--- a/src/kyclogic/Makefile.am
+++ b/src/kyclogic/Makefile.am
@@ -20,6 +20,7 @@ EXTRA_DIST = \
persona-sample-reply.json
bin_SCRIPTS = \
+ taler-exchange-kyc-kycaid-converter.sh \
taler-exchange-kyc-persona-converter.sh
lib_LTLIBRARIES = \
diff --git a/src/kyclogic/kyclogic-kycaid.conf b/src/kyclogic/kyclogic-kycaid.conf
index 0e1fe96ef..753fb689d 100644
--- a/src/kyclogic/kyclogic-kycaid.conf
+++ b/src/kyclogic/kyclogic-kycaid.conf
@@ -12,6 +12,10 @@ PROVIDED_CHECKS = EXAMPLE_DO_NOT_USE
# How long is the KYC check valid?
KYC_KYCAID_VALIDITY = forever
+# Program that converts Persona KYC data into the
+# GNU Taler format.
+KYC_KYCAID_CONVERTER_HELPER = taler-exchange-kyc-kycaid-converter.sh
+
# Authentication token to use.
KYC_KYCAID_AUTH_TOKEN = XXX
diff --git a/src/kyclogic/kyclogic-oauth2.conf b/src/kyclogic/kyclogic-oauth2.conf
index 6f83c0e44..61b38367f 100644
--- a/src/kyclogic/kyclogic-oauth2.conf
+++ b/src/kyclogic/kyclogic-oauth2.conf
@@ -13,11 +13,11 @@ PROVIDED_CHECKS = EXAMPLE_DO_NOT_USE
KYC_OAUTH2_VALIDITY = forever
# URL where we initiate the user's login process
-KYC_OAUTH2_LOGIN_URL = http://kyc.example.com/login
+KYC_OAUTH2_AUTHORIZE_URL = https://kyc.example.com/authorize
# URL where we send the user's authentication information
-KYC_OAUTH2_AUTH_URL = http://kyc.example.com/auth
+KYC_OAUTH2_TOKEN_URL = https://kyc.example.com/token
# URL of the user info access point.
-KYC_OAUTH2_INFO_URL = http://kyc.example.com/info
+KYC_OAUTH2_INFO_URL = https://kyc.example.com/info
# Where does the client get redirected upon completion?
KYC_OAUTH2_POST_URL = http://example.com/thank-you
diff --git a/src/kyclogic/plugin_kyclogic_kycaid.c b/src/kyclogic/plugin_kyclogic_kycaid.c
index 3273a51f1..95dc4bb78 100644
--- a/src/kyclogic/plugin_kyclogic_kycaid.c
+++ b/src/kyclogic/plugin_kyclogic_kycaid.c
@@ -88,6 +88,12 @@ struct TALER_KYCLOGIC_ProviderDetails
char *form_id;
/**
+ * Helper binary to convert attributes returned by
+ * KYCAID into our internal format.
+ */
+ char *conversion_helper;
+
+ /**
* Validity time for a successful KYC process.
*/
struct GNUNET_TIME_Relative validity;
@@ -216,6 +222,12 @@ struct TALER_KYCLOGIC_WebhookHandle
struct PluginState *ps;
/**
+ * Handle to helper process to extract attributes
+ * we care about.
+ */
+ struct TALER_JSON_ExternalConversion *econ;
+
+ /**
* Our configuration details.
*/
const struct TALER_KYCLOGIC_ProviderDetails *pd;
@@ -226,6 +238,11 @@ struct TALER_KYCLOGIC_WebhookHandle
struct MHD_Connection *connection;
/**
+ * JSON response we got back, or NULL for none.
+ */
+ json_t *json_response;
+
+ /**
* Verification ID from the service.
*/
char *verification_id;
@@ -262,6 +279,11 @@ struct TALER_KYCLOGIC_WebhookHandle
uint64_t process_row;
/**
+ * HTTP response code we got from KYCAID.
+ */
+ unsigned int kycaid_response_code;
+
+ /**
* HTTP response code to return asynchronously.
*/
unsigned int response_code;
@@ -277,6 +299,7 @@ static void
kycaid_unload_configuration (struct TALER_KYCLOGIC_ProviderDetails *pd)
{
curl_slist_free_all (pd->slist);
+ GNUNET_free (pd->conversion_helper);
GNUNET_free (pd->auth_token);
GNUNET_free (pd->form_id);
GNUNET_free (pd->section);
@@ -337,6 +360,18 @@ kycaid_load_configuration (void *cls,
kycaid_unload_configuration (pd);
return NULL;
}
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_filename (ps->cfg,
+ provider_section_name,
+ "KYC_KYCAID_CONVERTER_HELPER",
+ &pd->conversion_helper))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ provider_section_name,
+ "KYC_KYCAID_CONVERTER_HELPER");
+ kycaid_unload_configuration (pd);
+ return NULL;
+ }
{
char *auth;
@@ -695,11 +730,21 @@ kycaid_webhook_cancel (struct TALER_KYCLOGIC_WebhookHandle *wh)
GNUNET_SCHEDULER_cancel (wh->task);
wh->task = NULL;
}
+ if (NULL != wh->econ)
+ {
+ TALER_JSON_external_conversion_stop (wh->econ);
+ wh->econ = NULL;
+ }
if (NULL != wh->job)
{
GNUNET_CURL_job_cancel (wh->job);
wh->job = NULL;
}
+ if (NULL != wh->json_response)
+ {
+ json_decref (wh->json_response);
+ wh->json_response = NULL;
+ }
GNUNET_free (wh->verification_id);
GNUNET_free (wh->applicant_id);
GNUNET_free (wh->url);
@@ -751,6 +796,97 @@ log_failure (json_t *verifications)
/**
+ * Type of a callback that receives a JSON @a result.
+ *
+ * @param cls closure our `struct TALER_KYCLOGIC_WebhookHandle *`
+ * @param status_type how did the process die
+ * @param code termination status code from the process
+ * @param result converted attribute data, NULL on failure
+ */
+static void
+webhook_conversion_cb (void *cls,
+ enum GNUNET_OS_ProcessStatusType status_type,
+ unsigned long code,
+ const json_t *result)
+{
+ struct TALER_KYCLOGIC_WebhookHandle *wh = cls;
+ struct GNUNET_TIME_Absolute expiration;
+ struct MHD_Response *resp;
+
+ wh->econ = NULL;
+ if ( (0 == code) ||
+ (NULL == result) )
+ {
+ /* No result, but *our helper* was OK => bad input */
+ GNUNET_break_op (0);
+ json_dumpf (wh->json_response,
+ stderr,
+ JSON_INDENT (2));
+ resp = TALER_MHD_MAKE_JSON_PACK (
+ GNUNET_JSON_pack_uint64 ("kycaid_http_status",
+ wh->kycaid_response_code),
+ GNUNET_JSON_pack_object_incref ("kycaid_body",
+ (json_t *) wh->json_response));
+ wh->cb (wh->cb_cls,
+ wh->process_row,
+ &wh->h_payto,
+ wh->pd->section,
+ wh->applicant_id,
+ wh->verification_id,
+ TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
+ GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
+ NULL,
+ MHD_HTTP_BAD_GATEWAY,
+ resp);
+ kycaid_webhook_cancel (wh);
+ return;
+ }
+ if (NULL == result)
+ {
+ /* Failure in our helper */
+ GNUNET_break (0);
+ json_dumpf (wh->json_response,
+ stderr,
+ JSON_INDENT (2));
+ resp = TALER_MHD_MAKE_JSON_PACK (
+ GNUNET_JSON_pack_uint64 ("kycaid_http_status",
+ wh->kycaid_response_code),
+ GNUNET_JSON_pack_object_incref ("kycaid_body",
+ (json_t *) wh->json_response));
+ wh->cb (wh->cb_cls,
+ wh->process_row,
+ &wh->h_payto,
+ wh->pd->section,
+ wh->applicant_id,
+ wh->verification_id,
+ TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
+ GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
+ NULL,
+ MHD_HTTP_BAD_GATEWAY,
+ resp);
+ kycaid_webhook_cancel (wh);
+ return;
+ }
+ expiration = GNUNET_TIME_relative_to_absolute (wh->pd->validity);
+ resp = MHD_create_response_from_buffer (0,
+ "",
+ MHD_RESPMEM_PERSISTENT);
+ wh->cb (wh->cb_cls,
+ wh->process_row,
+ &wh->h_payto,
+ wh->pd->section,
+ wh->applicant_id,
+ wh->verification_id,
+ TALER_KYCLOGIC_STATUS_SUCCESS,
+ expiration,
+ result,
+ MHD_HTTP_NO_CONTENT,
+ resp);
+ kycaid_webhook_cancel (wh);
+}
+
+
+/**
* Function called when we're done processing the
* HTTP "/applicants/{verification_id}" request.
*
@@ -768,253 +904,30 @@ handle_webhook_finished (void *cls,
struct MHD_Response *resp;
wh->job = NULL;
+ wh->kycaid_response_code = response_code;
+ wh->json_response = json_incref ((json_t *) j);
switch (response_code)
{
case MHD_HTTP_OK:
{
- const char *type;
const char *profile_status;
- const char *first_name = NULL;
- const char *last_name = NULL;
- const char *middle_name = NULL;
- const char *dob = NULL;
- const char *residence_country = NULL;
- const char *gender = NULL;
- bool pep = false;
- bool no_pep = false;
- const char *company_name = NULL;
- const char *business_activity_id = NULL;
- const char *registration_country = NULL;
- const char *email = NULL;
- const char *phone = NULL;
- json_t *addresses = NULL;
- json_t *documents = NULL;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_string ("type",
- &type),
- GNUNET_JSON_spec_string ("profile_status",
- &profile_status), /* valid, invalid, pending */
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("email",
- &email),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("phone",
- &phone),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_json ("addresses",
- &addresses),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_json ("documents",
- &documents),
- NULL),
- GNUNET_JSON_spec_end ()
- };
- struct GNUNET_JSON_Specification bspec[] = {
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("company_name",
- &company_name),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("business_activity_id",
- &business_activity_id),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("registration_country",
- &registration_country),
- NULL),
- GNUNET_JSON_spec_end ()
- };
- struct GNUNET_JSON_Specification pspec[] = {
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("first_name",
- &first_name),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("middle_name",
- &middle_name),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("last_name",
- &last_name),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("dob",
- &dob),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("residence_country",
- &residence_country),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("gender",
- &gender),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_bool ("pep",
- &pep),
- &no_pep),
- GNUNET_JSON_spec_end ()
- };
- struct GNUNET_JSON_Specification *ispec = NULL;
- struct GNUNET_TIME_Absolute expiration;
- bool no_parse;
- enum TALER_KYCLOGIC_KycUserType ut;
-
- no_parse = (GNUNET_OK !=
- GNUNET_JSON_parse (j,
- spec,
- NULL, NULL));
- if (! no_parse)
- {
- ut = (0 == strcasecmp ("person",
- type))
- ? TALER_KYCLOGIC_KYC_UT_INDIVIDUAL
- : TALER_KYCLOGIC_KYC_UT_BUSINESS;
- ispec = (ut == TALER_KYCLOGIC_KYC_UT_INDIVIDUAL)
- ? pspec
- : bspec;
- no_parse = (GNUNET_OK !=
- GNUNET_JSON_parse (j,
- ispec,
- NULL, NULL));
- }
- if (no_parse)
- {
- GNUNET_break_op (0);
- json_dumpf (j,
- stderr,
- JSON_INDENT (2));
- resp = TALER_MHD_MAKE_JSON_PACK (
- GNUNET_JSON_pack_uint64 ("kycaid_http_status",
- response_code),
- GNUNET_JSON_pack_object_incref ("kycaid_body",
- (json_t *) j));
- wh->cb (wh->cb_cls,
- wh->process_row,
- &wh->h_payto,
- wh->pd->section,
- wh->applicant_id,
- wh->verification_id,
- TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
- GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
- NULL,
- MHD_HTTP_BAD_GATEWAY,
- resp);
- break;
- }
- if (0 == strcasecmp ("valid",
- profile_status))
- {
- log_failure (json_object_get (j,
- "decline_reasons"));
- }
- resp = MHD_create_response_from_buffer (0,
- "",
- MHD_RESPMEM_PERSISTENT);
- if (0 == strcasecmp ("valid",
+
+ profile_status = json_string_value (
+ json_object_get (
+ j,
+ "profile_status"));
+ if (0 != strcasecmp ("valid",
profile_status))
{
- json_t *attr;
-
- if (ut == TALER_KYCLOGIC_KYC_UT_INDIVIDUAL)
- {
- char *name = NULL;
-
- if ( (NULL != last_name) ||
- (NULL != first_name) ||
- (NULL != middle_name) )
- {
- GNUNET_asprintf (&name,
- "%s, %s %s",
- (NULL != last_name)
- ? last_name
- : "",
- (NULL != first_name)
- ? first_name
- : "",
- (NULL != middle_name)
- ? middle_name
- : "");
- }
- attr = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string (
- TALER_ATTRIBUTE_BIRTHDATE,
- dob)),
- GNUNET_JSON_pack_allow_null (
- no_pep
- ? GNUNET_JSON_pack_string (
- TALER_ATTRIBUTE_PEP,
- NULL)
- : GNUNET_JSON_pack_bool (
- TALER_ATTRIBUTE_PEP,
- pep)),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string (
- TALER_ATTRIBUTE_FULL_NAME,
- name)),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string (
- TALER_ATTRIBUTE_PHONE,
- phone)),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string (
- TALER_ATTRIBUTE_EMAIL,
- email)),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string (
- TALER_ATTRIBUTE_RESIDENCES,
- residence_country))
- );
- GNUNET_free (name);
- }
- else
- {
- attr = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string (
- TALER_ATTRIBUTE_COMPANY_NAME,
- company_name)),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string (
- TALER_ATTRIBUTE_PHONE,
- phone)),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string (
- TALER_ATTRIBUTE_EMAIL,
- email)),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string (
- TALER_ATTRIBUTE_REGISTRATION_COUNTRY,
- residence_country))
- );
- }
- // FIXME: do something about addresses & documents!
- expiration = GNUNET_TIME_relative_to_absolute (wh->pd->validity);
- wh->cb (wh->cb_cls,
- wh->process_row,
- &wh->h_payto,
- wh->pd->section,
- wh->applicant_id,
- wh->verification_id,
- TALER_KYCLOGIC_STATUS_SUCCESS,
- expiration,
- attr,
- MHD_HTTP_NO_CONTENT,
- resp);
- json_decref (attr);
- }
- else
- {
enum TALER_KYCLOGIC_KycStatus ks;
ks = (0 == strcasecmp ("pending",
profile_status))
? TALER_KYCLOGIC_STATUS_PENDING
: TALER_KYCLOGIC_STATUS_USER_ABORTED;
+ resp = MHD_create_response_from_buffer (0,
+ "",
+ MHD_RESPMEM_PERSISTENT);
wh->cb (wh->cb_cls,
wh->process_row,
&wh->h_payto,
@@ -1026,9 +939,19 @@ handle_webhook_finished (void *cls,
NULL,
MHD_HTTP_NO_CONTENT,
resp);
+ break;
}
- GNUNET_JSON_parse_free (ispec);
- GNUNET_JSON_parse_free (spec);
+ wh->econ
+ = TALER_JSON_external_conversion_start (
+ j,
+ &webhook_conversion_cb,
+ wh,
+ wh->pd->conversion_helper,
+ wh->pd->conversion_helper,
+ "-a",
+ wh->pd->auth_token,
+ NULL);
+ return;
}
break;
case MHD_HTTP_BAD_REQUEST:
diff --git a/src/kyclogic/plugin_kyclogic_oauth2.c b/src/kyclogic/plugin_kyclogic_oauth2.c
index c72b04b7c..e7350f01c 100644
--- a/src/kyclogic/plugin_kyclogic_oauth2.c
+++ b/src/kyclogic/plugin_kyclogic_oauth2.c
@@ -1,6 +1,6 @@
/*
This file is part of GNU Taler
- Copyright (C) 2022 Taler Systems SA
+ Copyright (C) 2022-2023 Taler Systems SA
Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@@ -75,15 +75,21 @@ struct TALER_KYCLOGIC_ProviderDetails
char *section;
/**
+ * URL of the Challenger ``/setup`` endpoint for
+ * approving address validations. NULL if not used.
+ */
+ char *setup_url;
+
+ /**
* URL of the OAuth2.0 endpoint for KYC checks.
- * (token/auth)
*/
- char *auth_url;
+ char *authorize_url;
/**
* URL of the OAuth2.0 endpoint for KYC checks.
+ * (token/auth)
*/
- char *login_url;
+ char *token_url;
/**
* URL of the user info access endpoint.
@@ -148,6 +154,11 @@ struct TALER_KYCLOGIC_InitiateHandle
struct GNUNET_SCHEDULER_Task *task;
/**
+ * Handle for the OAuth 2.0 setup request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
* Continuation to call.
*/
TALER_KYCLOGIC_InitiateCallback cb;
@@ -283,8 +294,9 @@ static void
oauth2_unload_configuration (struct TALER_KYCLOGIC_ProviderDetails *pd)
{
GNUNET_free (pd->section);
- GNUNET_free (pd->auth_url);
- GNUNET_free (pd->login_url);
+ GNUNET_free (pd->token_url);
+ GNUNET_free (pd->setup_url);
+ GNUNET_free (pd->authorize_url);
GNUNET_free (pd->info_url);
GNUNET_free (pd->client_id);
GNUNET_free (pd->client_secret);
@@ -327,12 +339,12 @@ oauth2_load_configuration (void *cls,
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (ps->cfg,
provider_section_name,
- "KYC_OAUTH2_AUTH_URL",
+ "KYC_OAUTH2_TOKEN_URL",
&s))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
provider_section_name,
- "KYC_OAUTH2_AUTH_URL");
+ "KYC_OAUTH2_TOKEN_URL");
oauth2_unload_configuration (pd);
return NULL;
}
@@ -346,23 +358,23 @@ oauth2_load_configuration (void *cls,
{
GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
provider_section_name,
- "KYC_OAUTH2_AUTH_URL",
+ "KYC_OAUTH2_TOKEN_URL",
"not a valid URL");
GNUNET_free (s);
oauth2_unload_configuration (pd);
return NULL;
}
- pd->auth_url = s;
+ pd->token_url = s;
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (ps->cfg,
provider_section_name,
- "KYC_OAUTH2_LOGIN_URL",
+ "KYC_OAUTH2_AUTHORIZE_URL",
&s))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
provider_section_name,
- "KYC_OAUTH2_LOGIN_URL");
+ "KYC_OAUTH2_AUTHORIZE_URL");
oauth2_unload_configuration (pd);
return NULL;
}
@@ -376,13 +388,41 @@ oauth2_load_configuration (void *cls,
{
GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
provider_section_name,
- "KYC_OAUTH2_LOGIN_URL",
+ "KYC_OAUTH2_AUTHORIZE_URL",
"not a valid URL");
oauth2_unload_configuration (pd);
GNUNET_free (s);
return NULL;
}
- pd->login_url = s;
+ if (NULL != strchr (s, '#'))
+ {
+ const char *extra = strchr (s, '#');
+ const char *slash = strrchr (s, '/');
+
+ if ( (0 != strcmp (extra,
+ "#setup")) ||
+ (NULL == slash) )
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ provider_section_name,
+ "KYC_OAUTH2_AUTHORIZE_URL",
+ "not a valid authorze URL (bad fragment)");
+ oauth2_unload_configuration (pd);
+ GNUNET_free (s);
+ return NULL;
+ }
+ pd->authorize_url = GNUNET_strndup (s,
+ extra - s);
+ GNUNET_asprintf (&pd->setup_url,
+ "%.*s/setup",
+ (int) (slash - s),
+ s);
+ GNUNET_free (s);
+ }
+ else
+ {
+ pd->authorize_url = s;
+ }
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (ps->cfg,
@@ -480,19 +520,20 @@ oauth2_load_configuration (void *cls,
* how to begin the OAuth2.0 checking process to
* the client.
*
- * @param cls a `struct TALER_KYCLOGIC_InitiateHandle *`
+ * @param ih process to redirect for
+ * @param authorize_url authorization URL to use
*/
static void
-initiate_task (void *cls)
+initiate_with_url (struct TALER_KYCLOGIC_InitiateHandle *ih,
+ const char *authorize_url)
{
- struct TALER_KYCLOGIC_InitiateHandle *ih = cls;
+
const struct TALER_KYCLOGIC_ProviderDetails *pd = ih->pd;
struct PluginState *ps = pd->ps;
char *hps;
char *url;
char legi_s[42];
- ih->task = NULL;
GNUNET_snprintf (legi_s,
sizeof (legi_s),
"%llu",
@@ -515,16 +556,11 @@ initiate_task (void *cls)
}
GNUNET_asprintf (&url,
"%s?response_type=code&client_id=%s&redirect_uri=%s",
- pd->login_url,
+ authorize_url,
pd->client_id,
redirect_uri_encoded);
GNUNET_free (redirect_uri_encoded);
}
- /* FIXME-API: why do we *redirect* the client here,
- instead of making the HTTP request *ourselves*
- and forwarding the response? This prevents us
- from using authentication on initiation,
- (which is desirable for challenger!) */
ih->cb (ih->cb_cls,
TALER_EC_NONE,
url,
@@ -538,6 +574,142 @@ initiate_task (void *cls)
/**
+ * After we are done with the CURL interaction we
+ * need to update our database state with the information
+ * retrieved.
+ *
+ * @param cls a `struct TALER_KYCLOGIC_InitiateHandle *`
+ * @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_setup_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_KYCLOGIC_InitiateHandle *ih = cls;
+ const struct TALER_KYCLOGIC_ProviderDetails *pd = ih->pd;
+ const json_t *j = response;
+
+ ih->job = NULL;
+ switch (response_code)
+ {
+ case MHD_HTTP_OK:
+ {
+ const char *nonce;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("nonce",
+ &nonce),
+ GNUNET_JSON_spec_end ()
+ };
+ enum GNUNET_GenericReturnValue res;
+ const char *emsg;
+ unsigned int line;
+ char *url;
+
+ res = GNUNET_JSON_parse (j,
+ spec,
+ &emsg,
+ &line);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break_op (0);
+ json_dumpf (j,
+ stderr,
+ JSON_INDENT (2));
+ ih->cb (ih->cb_cls,
+ TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE,
+ NULL,
+ NULL,
+ NULL,
+ "Unexpected response from KYC gateway: setup must return a nonce");
+ GNUNET_free (ih);
+ return;
+ }
+ GNUNET_asprintf (&url,
+ "%s/%s",
+ pd->setup_url,
+ nonce);
+ initiate_with_url (ih,
+ url);
+ GNUNET_free (url);
+ return;
+ }
+ break;
+ default:
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "/setup URL returned HTTP status %u\n",
+ (unsigned int) response_code);
+ ih->cb (ih->cb_cls,
+ TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE,
+ NULL,
+ NULL,
+ NULL,
+ "/setup request to OAuth 2.0 backend returned unexpected HTTP status code");
+ GNUNET_free (ih);
+ return;
+ }
+}
+
+
+/**
+ * Logic to asynchronously return the response for how to begin the OAuth2.0
+ * checking process to the client. May first request a dynamic URL via
+ * ``/setup`` if configured to use a client-authenticated setup process.
+ *
+ * @param cls a `struct TALER_KYCLOGIC_InitiateHandle *`
+ */
+static void
+initiate_task (void *cls)
+{
+ struct TALER_KYCLOGIC_InitiateHandle *ih = cls;
+ const struct TALER_KYCLOGIC_ProviderDetails *pd = ih->pd;
+ struct PluginState *ps = pd->ps;
+ char *hdr;
+ struct curl_slist *slist;
+ CURL *eh;
+
+ ih->task = NULL;
+ if (NULL == pd->setup_url)
+ {
+ initiate_with_url (ih,
+ pd->authorize_url);
+ return;
+ }
+ eh = curl_easy_init ();
+ if (NULL == eh)
+ {
+ GNUNET_break (0);
+ ih->cb (ih->cb_cls,
+ TALER_EC_GENERIC_ALLOCATION_FAILURE,
+ NULL,
+ NULL,
+ NULL,
+ "curl_easy_init() failed");
+ GNUNET_free (ih);
+ return;
+ }
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_URL,
+ pd->setup_url));
+ GNUNET_asprintf (&hdr,
+ "%s: Bearer %s",
+ MHD_HTTP_HEADER_AUTHORIZATION,
+ pd->client_secret);
+ slist = curl_slist_append (NULL,
+ hdr);
+ ih->job = GNUNET_CURL_job_add2 (ps->curl_ctx,
+ eh,
+ slist,
+ &handle_curl_setup_finished,
+ ih);
+ curl_slist_free_all (slist);
+ GNUNET_free (hdr);
+}
+
+
+/**
* Initiate KYC check.
*
* @param cls the @e cls of this struct with the plugin-specific state
@@ -584,6 +756,11 @@ oauth2_initiate_cancel (struct TALER_KYCLOGIC_InitiateHandle *ih)
GNUNET_SCHEDULER_cancel (ih->task);
ih->task = NULL;
}
+ if (NULL != ih->job)
+ {
+ GNUNET_CURL_job_cancel (ih->job);
+ ih->job = NULL;
+ }
GNUNET_free (ih);
}
@@ -1002,7 +1179,7 @@ handle_curl_login_finished (void *cls,
eh = curl_easy_init ();
if (NULL == eh)
{
- GNUNET_break_op (0);
+ GNUNET_break (0);
ph->response
= TALER_MHD_make_error (
TALER_EC_GENERIC_ALLOCATION_FAILURE,
@@ -1129,7 +1306,7 @@ oauth2_proof (void *cls,
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (ph->eh,
CURLOPT_URL,
- pd->auth_url));
+ pd->token_url));
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (ph->eh,
CURLOPT_POST,
diff --git a/src/kyclogic/taler-exchange-kyc-kycaid-converter.sh b/src/kyclogic/taler-exchange-kyc-kycaid-converter.sh
new file mode 100644
index 000000000..96aca2b80
--- /dev/null
+++ b/src/kyclogic/taler-exchange-kyc-kycaid-converter.sh
@@ -0,0 +1,86 @@
+#!/bin/bash
+# This file is in the public domain.
+#
+# This code converts (some of) the JSON output from KYCAID into the GNU Taler
+# specific KYC attribute data (again in JSON format). We may need to download
+# and inline file data in the process, for authorization pass "-a" with the
+# respective bearer token.
+#
+
+# Die if anything goes wrong.
+set -eu
+
+# Parse command-line options
+while getopts ':a:' OPTION; do
+ case "$OPTION" in
+ a)
+ TOKEN="$OPTARG"
+ ;;
+ ?)
+ echo "Unrecognized command line option"
+ exit 1
+ ;;
+ esac
+done
+
+# First, extract everything from stdin.
+J=$(jq '{"type":.type,"email":.email,"phone":.phone,"first_name":.first_name,"name-middle":.middle_name,"last_name":.last_name,"dob":.dob,"residence_country":.residence_country,"gender":.gender,"pep":.pep,"addresses":.addresses,"documents":.documents,"company_name":.company_name,"business_activity_id":.business_activity_id,"registration_country":.registration_country,"documents":.documents,"decline_reasons":.decline_reasons}')
+
+# TODO:
+# log_failure (json_object_get (j, "decline_reasons"));
+
+TYPE=$(echo "$J" | jq -r '.person')
+
+N=0
+DOCS_RAW=""
+DOCS_JSON=""
+for ID in $(jq -r '.documents[]|select(.status=="valid")|.id')
+do
+ TYPE=$(jq -r ".documents[]|select(.id==\"$ID\")|.type")
+ EXPIRY=$(jq -r ".documents[]|select(.id==\"$ID\")|.expiry_date")
+ DOCUMENT_FILE=$(mktemp -t tmp.XXXXXXXXXX)
+ # Authoriazation: Token $TOKEN
+ DOCUMENT_URL="https://api.kycaid.com/documents/$ID"
+ if [ -z "${TOKEN:-}" ]
+ then
+ wget -q --output-document=- "$DOCUMENT_URL" \
+ | gnunet-base32 > ${DOCUMENT_FILE}
+ else
+ wget -q --output-document=- "$DOCUMENT_URL" \
+ --header "Authorization: Token $TOKEN" \
+ | gnunet-base32 > ${DOCUMENT_FILE}
+ fi
+ DOCS_RAW="$DOCS_RAW --rawfile photo$N \"${DOCUMENT_FILE}\""
+ if [ "$N" = 0 ]
+ then
+ DOCS_JSON="{\"type\":\"$TYPE\",\"image\":\$photo$N}"
+ else
+ DOCS_JSON="{\"type\":\"$TYPE\",\"image\":\$photo$N},$DOCS_JSON"
+ fi
+ N=$(expr $N + 1)
+done
+
+
+if [ "person" = "${TYPE}" ]
+then
+
+ # Next, combine some fields into larger values.
+ FULLNAME=$(echo "$J" | jq -r '[.first_name,.middle_name,.last_name]|join(" ")')
+# STREET=$(echo $J | jq -r '[."street-1",."street-2"]|join(" ")')
+# CITY=$(echo $J | jq -r '[.postcode,.city,."address-subdivision,.cc"]|join(" ")')
+
+ # Combine into final result for individual.
+ # FIXME: does jq tolerate 'pep = NULL' here?
+ echo "$J" | jq \
+ --arg full_name "${FULLNAME}" \
+ '{$full_name,"birthdate":.dob,"pep":.pep,"phone":."phone","email",.email,"residences":.residence_country}'
+
+else
+ # Combine into final result for business.
+ echo "$J" | jq \
+ --arg full_name "${FULLNAME}" \
+ $DOCS_RAW \
+ "{\"company_name\":.company_name,\"phone\":.phone,\"email\":.email,\"registration_country\":.registration_country,\"documents\":[${DOCS_JSON}]}"
+fi
+
+exit 0