commit 97df3b30de69df738c8904d5c9917de77241d590
parent 012973ca977179e2d4f801aa06c77ca9052c5990
Author: Christian Grothoff <christian@grothoff.org>
Date: Fri, 26 Jul 2024 15:31:18 +0200
fixes to error handling, logging, and aml/kyc history returning feature
Diffstat:
21 files changed, 1675 insertions(+), 221 deletions(-)
diff --git a/src/exchange/taler-exchange-httpd_common_kyc.c b/src/exchange/taler-exchange-httpd_common_kyc.c
@@ -76,6 +76,16 @@ struct TEH_KycAmlTrigger
void *cb_cls;
/**
+ * Handle to fallback processing.
+ */
+ struct TEH_KycAmlFallback *fb;
+
+ /**
+ * Name of the fallback @e fb is running (or NULL).
+ */
+ char *fallback_name;
+
+ /**
* user attributes returned by the provider
*/
json_t *attributes;
@@ -120,14 +130,59 @@ struct TEH_KycAmlTrigger
/**
+ * Function called with the result of activating a
+ * fallback measure.
+ *
+ * @param cls a `struct TEH_KycAmlTrigger *`
+ * @param result true if the fallback was activated
+ * successfully
+ * @param requirement_row row of
+ * new KYC requirement that was created, 0 for none
+ */
+static void
+fallback_result_cb (void *cls,
+ bool result,
+ uint64_t requirement_row)
+{
+ struct TEH_KycAmlTrigger *kat = cls;
+ struct GNUNET_AsyncScopeSave old_scope;
+
+ kat->fb = NULL;
+ (void) requirement_row;
+ // FIXME: return new requirement_row!?
+ GNUNET_async_scope_enter (&kat->scope,
+ &old_scope);
+ if (result)
+ {
+ kat->cb (kat->cb_cls,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_MHD_make_error (
+ TALER_EC_EXCHANGE_KYC_AML_PROGRAM_FAILURE,
+ NULL));
+ }
+ else
+ {
+ kat->cb (kat->cb_cls,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_MHD_make_error (
+ TALER_EC_EXCHANGE_GENERIC_KYC_FALLBACK_FAILED,
+ kat->fallback_name));
+ }
+ TEH_kyc_finished_cancel (kat);
+ GNUNET_async_scope_restore (&old_scope);
+}
+
+
+/**
* Type of a callback that receives a JSON @a result.
*
* @param cls closure of type `struct TEH_KycAmlTrigger *`
* @param apr AML program result
*/
static void
-kyc_aml_finished (void *cls,
- const struct TALER_KYCLOGIC_AmlProgramResult *apr)
+kyc_aml_finished (
+ void *cls,
+ const struct TALER_KYCLOGIC_AmlProgramResult *apr)
{
struct TEH_KycAmlTrigger *kat = cls;
enum GNUNET_DB_QueryStatus qs;
@@ -141,8 +196,67 @@ kyc_aml_finished (void *cls,
&old_scope);
if (TALER_KYCLOGIC_AMLR_SUCCESS != apr->status)
{
- // FIXME ...
- GNUNET_break (0); // not implemented!
+ if (! TEH_kyc_failed (
+ kat->process_row,
+ &kat->account_id,
+ kat->provider_name,
+ kat->provider_user_id,
+ kat->provider_legitimization_id,
+ apr->details.failure.error_message,
+ apr->details.failure.ec))
+ {
+ /* double-bad: error during error handling */
+ GNUNET_break (0);
+ kat->cb (kat->cb_cls,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_MHD_make_error (
+ TALER_EC_GENERIC_DB_STORE_FAILED,
+ "insert_kyc_failure"));
+ TEH_kyc_finished_cancel (kat);
+ GNUNET_async_scope_restore (&old_scope);
+ return;
+ }
+ if (NULL == apr->details.failure.fallback_measure)
+ {
+ /* Not sure this can happen (fallback required?),
+ but report AML program failure to client */
+ kat->cb (kat->cb_cls,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_MHD_make_error (
+ TALER_EC_EXCHANGE_KYC_AML_PROGRAM_FAILURE,
+ NULL));
+ TEH_kyc_finished_cancel (kat);
+ GNUNET_async_scope_restore (&old_scope);
+ return;
+ }
+ kat->fallback_name
+ = GNUNET_strdup (
+ apr->details.failure.fallback_measure);
+ kat->fb
+ = TEH_kyc_fallback (
+ &kat->scope,
+ &kat->account_id,
+ kat->process_row,
+ kat->attributes,
+ kat->aml_history,
+ kat->kyc_history,
+ kat->fallback_name,
+ &fallback_result_cb,
+ kat);
+ if (NULL == kat->fb)
+ {
+ GNUNET_break (0);
+ kat->cb (kat->cb_cls,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_MHD_make_error (
+ TALER_EC_EXCHANGE_GENERIC_KYC_FALLBACK_UNKNOWN,
+ kat->fallback_name));
+ TEH_kyc_finished_cancel (kat);
+ GNUNET_async_scope_restore (&old_scope);
+ return;
+ }
+ GNUNET_async_scope_restore (&old_scope);
+ return;
}
{
const char *birthdate;
@@ -222,19 +336,151 @@ RETURN_RESULT:
}
+/**
+ * Function called to expand AML history for the account.
+ *
+ * @param cls a `json_t *` array to build
+ * @param decision_time when was the decision taken
+ * @param justification what was the given justification
+ * @param decider_pub which key signed the decision
+ * @param jproperties what are the new account properties
+ * @param jnew_rules what are the new account rules
+ * @param to_investigate should AML staff investigate
+ * after the decision
+ * @param is_active is this the active decision
+ */
+static void
+add_aml_history_entry (
+ void *cls,
+ struct GNUNET_TIME_Timestamp decision_time,
+ const char *justification,
+ const struct TALER_AmlOfficerPublicKeyP *decider_pub,
+ const json_t *jproperties,
+ const json_t *jnew_rules,
+ bool to_investigate,
+ bool is_active)
+{
+ json_t *aml_history = cls;
+ json_t *e;
+
+ e = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_timestamp ("decision_time",
+ decision_time),
+ GNUNET_JSON_pack_string ("justification",
+ justification),
+ GNUNET_JSON_pack_data_auto ("decider_pub",
+ decider_pub),
+ GNUNET_JSON_pack_object_incref ("properties",
+ (json_t *) jproperties),
+ GNUNET_JSON_pack_object_incref ("new_rules",
+ (json_t *) jnew_rules),
+ GNUNET_JSON_pack_bool ("to_investigate",
+ to_investigate),
+ GNUNET_JSON_pack_bool ("is_active",
+ is_active)
+ );
+ GNUNET_assert (0 ==
+ json_array_append_new (aml_history,
+ e));
+}
+
+
+/**
+ * Function called to expand KYC history for the account.
+ *
+ * @param cls a `json_t *` array to build
+ * @param provider_name name of the KYC provider
+ * or NULL for none
+ * @param finished did the KYC process finish
+ * @param error_code error code from the KYC process
+ * @param error_message error message from the KYC process,
+ * or NULL for none
+ * @param provider_user_id user ID at the provider
+ * or NULL for none
+ * @param provider_legitimization_id legitimization process ID at the provider
+ * or NULL for none
+ * @param collection_time when was the data collected
+ * @param expiration_time when does the collected data expire
+ * @param encrypted_attributes_len number of bytes in @a encrypted_attributes
+ * @param encrypted_attributes encrypted KYC attributes
+ */
+static void
+add_kyc_history_entry (
+ void *cls,
+ const char *provider_name,
+ bool finished,
+ enum TALER_ErrorCode error_code,
+ const char *error_message,
+ const char *provider_user_id,
+ const char *provider_legitimization_id,
+ struct GNUNET_TIME_Timestamp collection_time,
+ struct GNUNET_TIME_Absolute expiration_time,
+ size_t encrypted_attributes_len,
+ const void *encrypted_attributes)
+{
+ json_t *kyc_history = cls;
+ json_t *attributes;
+ json_t *e;
+
+ attributes = TALER_CRYPTO_kyc_attributes_decrypt (
+ &TEH_attribute_key,
+ encrypted_attributes,
+ encrypted_attributes_len);
+ e = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string (
+ "provider_name",
+ provider_name),
+ GNUNET_JSON_pack_bool (
+ "finished",
+ finished),
+ TALER_JSON_pack_ec (error_code),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string (
+ "error_message",
+ error_message)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string (
+ "provider_user_id",
+ provider_user_id)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string (
+ "provider_legitimization_id",
+ provider_legitimization_id)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_timestamp (
+ "collection_time",
+ collection_time)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_timestamp (
+ "expiration_time",
+ GNUNET_TIME_absolute_to_timestamp (
+ expiration_time))),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_object_steal (
+ "attributes",
+ attributes))
+ );
+
+ GNUNET_assert (0 ==
+ json_array_append_new (kyc_history,
+ e));
+}
+
+
struct TEH_KycAmlTrigger *
-TEH_kyc_finished (const struct GNUNET_AsyncScopeId *scope,
- uint64_t process_row,
- const struct TALER_PaytoHashP *account_id,
- const char *provider_name,
- const char *provider_user_id,
- const char *provider_legitimization_id,
- struct GNUNET_TIME_Absolute expiration,
- const json_t *attributes,
- unsigned int http_status,
- struct MHD_Response *response,
- TEH_KycAmlTriggerCallback cb,
- void *cb_cls)
+TEH_kyc_finished (
+ const struct GNUNET_AsyncScopeId *scope,
+ uint64_t process_row,
+ const struct TALER_PaytoHashP *account_id,
+ const char *provider_name,
+ const char *provider_user_id,
+ const char *provider_legitimization_id,
+ struct GNUNET_TIME_Absolute expiration,
+ const json_t *attributes,
+ unsigned int http_status,
+ struct MHD_Response *response,
+ TEH_KycAmlTriggerCallback cb,
+ void *cb_cls)
{
struct TEH_KycAmlTrigger *kat;
enum GNUNET_DB_QueryStatus qs;
@@ -276,16 +522,44 @@ TEH_kyc_finished (const struct GNUNET_AsyncScopeId *scope,
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
break;
}
-#if FIXME
+ kat->aml_history = json_array ();
+ kat->kyc_history = json_array ();
qs = TEH_plugin->lookup_aml_history (
TEH_plugin->cls,
account_id,
- &kat->aml_history,
- &kat->kyc_history);
-#else
- kat->aml_history = json_array ();
- kat->kyc_history = json_array ();
-#endif
+ &add_aml_history_entry,
+ kat->aml_history);
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ GNUNET_break (0);
+ TEH_kyc_finished_cancel (kat);
+ return NULL;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ /* empty history is fine! */
+ break;
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ break;
+ }
+ qs = TEH_plugin->lookup_kyc_history (
+ TEH_plugin->cls,
+ account_id,
+ &add_kyc_history_entry,
+ kat->kyc_history);
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ GNUNET_break (0);
+ TEH_kyc_finished_cancel (kat);
+ return NULL;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ /* empty history is fine! */
+ break;
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ break;
+ }
kat->kyc_aml
= TALER_KYCLOGIC_run_aml_program (kat->attributes,
kat->aml_history,
@@ -312,9 +586,15 @@ TEH_kyc_finished_cancel (struct TEH_KycAmlTrigger *kat)
TALER_KYCLOGIC_run_aml_program_cancel (kat->kyc_aml);
kat->kyc_aml = NULL;
}
+ if (NULL != kat->fb)
+ {
+ TEH_kyc_fallback_cancel (kat->fb);
+ kat->fb = NULL;
+ }
GNUNET_free (kat->provider_name);
GNUNET_free (kat->provider_user_id);
GNUNET_free (kat->provider_legitimization_id);
+ GNUNET_free (kat->fallback_name);
json_decref (kat->jmeasures);
json_decref (kat->attributes);
json_decref (kat->aml_history);
@@ -328,12 +608,278 @@ TEH_kyc_finished_cancel (struct TEH_KycAmlTrigger *kat)
}
+struct TEH_KycAmlFallback
+{
+
+ /**
+ * Our logging scope.
+ */
+ struct GNUNET_AsyncScopeId scope;
+
+ /**
+ * Account this is for.
+ */
+ struct TALER_PaytoHashP account_id;
+
+ /**
+ * Function to call when done.
+ */
+ TEH_KycAmlFallbackCallback cb;
+
+ /**
+ * Closure for @e cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Handle for asynchronously running AML program.
+ */
+ struct TALER_KYCLOGIC_AmlProgramRunnerHandle *aprh;
+
+ /**
+ * Task for asynchrnous returning of the result.
+ */
+ struct GNUNET_SCHEDULER_Task *task;
+
+ /**
+ * New requirement row we created, 0 if none.
+ */
+ uint64_t requirement_row;
+
+ /**
+ * Original requirement row the fallback is for.
+ */
+ uint64_t orig_requirement_row;
+
+ /**
+ * True if we failed.
+ */
+ bool failure;
+
+};
+
+
+/**
+ * Handle result from AML fallback program.
+ *
+ * @param cls a `struct TEH_KycAmlFallback`
+ * @param apr AML program result to handle
+ */
+static void
+handle_aml_fallback_result (
+ void *cls,
+ const struct TALER_KYCLOGIC_AmlProgramResult *apr)
+{
+ struct TEH_KycAmlFallback *fb = cls;
+ size_t eas;
+ void *ea;
+ enum GNUNET_DB_QueryStatus qs;
+ struct GNUNET_AsyncScopeSave old_scope;
+
+ fb->aprh = NULL;
+ GNUNET_async_scope_enter (&fb->scope,
+ &old_scope);
+ if (TALER_KYCLOGIC_AMLR_SUCCESS != apr->status)
+ {
+ if (! TEH_kyc_failed (
+ fb->orig_requirement_row,
+ &fb->account_id,
+ NULL,
+ NULL,
+ NULL,
+ apr->details.failure.error_message,
+ apr->details.failure.ec))
+ {
+ /* tripple-bad: error during error handling of fallback */
+ GNUNET_break (0);
+ fb->cb (fb->cb_cls,
+ false,
+ 0);
+ TEH_kyc_fallback_cancel (fb);
+ GNUNET_async_scope_restore (&old_scope);
+ return;
+ }
+ /* Fallback not allowed on fallback */
+ GNUNET_break (0);
+ fb->cb (fb->cb_cls,
+ false,
+ 0);
+ TEH_kyc_fallback_cancel (fb);
+ GNUNET_async_scope_restore (&old_scope);
+ return;
+ }
+
+ {
+ json_t *attributes;
+
+ /* empty attributes for fallback */
+ attributes = json_object ();
+ GNUNET_assert (NULL != attributes);
+ TALER_CRYPTO_kyc_attributes_encrypt (&TEH_attribute_key,
+ attributes,
+ &ea,
+ &eas);
+ json_decref (attributes);
+ }
+ qs = TEH_plugin->insert_kyc_attributes (
+ TEH_plugin->cls,
+ fb->orig_requirement_row,
+ &fb->account_id,
+ 0,
+ GNUNET_TIME_timestamp_get (),
+ NULL,
+ NULL,
+ NULL,
+ GNUNET_TIME_UNIT_FOREVER_ABS,
+ apr->details.success.account_properties,
+ apr->details.success.new_rules,
+ apr->details.success.to_investigate,
+ apr->details.success.num_events,
+ apr->details.success.events,
+ eas,
+ ea);
+ GNUNET_free (ea);
+ if (qs <= 0)
+ {
+ GNUNET_break (0);
+ fb->cb (fb->cb_cls,
+ false,
+ 0);
+ GNUNET_async_scope_restore (&old_scope);
+ TEH_kyc_fallback_cancel (fb);
+ return;
+ }
+ /* Finally, return result to main handler */
+ fb->cb (fb->cb_cls,
+ true,
+ 0);
+ TEH_kyc_fallback_cancel (fb);
+ GNUNET_async_scope_restore (&old_scope);
+}
+
+
+/**
+ * Helper task function to asynchronously return
+ * the result of the operation.
+ *
+ * @param cls a `struct TEH_KycAmlFallback`.
+ */
+static void
+return_fallback_result (void *cls)
+{
+ struct TEH_KycAmlFallback *fb = cls;
+ struct GNUNET_AsyncScopeSave old_scope;
+
+ fb->task = NULL;
+ GNUNET_async_scope_enter (&fb->scope,
+ &old_scope);
+ fb->cb (fb->cb_cls,
+ ! fb->failure,
+ fb->requirement_row);
+ TEH_kyc_fallback_cancel (fb);
+ GNUNET_async_scope_restore (&old_scope);
+}
+
+
+struct TEH_KycAmlFallback*
+TEH_kyc_fallback (
+ const struct GNUNET_AsyncScopeId *scope,
+ const struct TALER_PaytoHashP *account_id,
+ uint64_t orig_requirement_row,
+ const json_t *attributes,
+ const json_t *aml_history,
+ const json_t *kyc_history,
+ const char *fallback_measure,
+ TEH_KycAmlFallbackCallback cb,
+ void *cb_cls)
+{
+ struct TEH_KycAmlFallback *fb;
+ struct TALER_KYCLOGIC_KycCheckContext kcc;
+
+ if (GNUNET_OK !=
+ TALER_KYCLOGIC_get_default_measure (
+ fallback_measure,
+ &kcc))
+ {
+ /* very bad, could not find fallback measure!? */
+ GNUNET_break (0);
+ return NULL;
+ }
+ fb = GNUNET_new (struct TEH_KycAmlFallback);
+ fb->scope = *scope;
+ fb->account_id = *account_id;
+ fb->orig_requirement_row = orig_requirement_row;
+ fb->cb = cb;
+ fb->cb_cls = cb_cls;
+ if (NULL == kcc.check)
+ {
+ fb->aprh
+ = TALER_KYCLOGIC_run_aml_program2 (kcc.prog_name,
+ attributes,
+ aml_history,
+ kyc_history,
+ kcc.context,
+ &handle_aml_fallback_result,
+ fb);
+ if (NULL == fb->aprh)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Fallback AML program `%s' unknown\n",
+ kcc.prog_name);
+ TEH_kyc_fallback_cancel (fb);
+ return NULL;
+ }
+ return fb;
+ }
+ /* activate given check */
+ {
+ json_t *jmeasures;
+ enum GNUNET_DB_QueryStatus qs;
+
+ jmeasures = TALER_KYCLOGIC_check_to_measures (&kcc);
+ qs = TEH_plugin->trigger_kyc_rule_for_account (
+ TEH_plugin->cls,
+ NULL, /* account_id is already in wire targets */
+ account_id,
+ jmeasures,
+ 65536, /* high priority (does it matter?) */
+ &fb->requirement_row);
+ json_decref (jmeasures);
+ fb->failure = (qs <= 0);
+ fb->task = GNUNET_SCHEDULER_add_now (&return_fallback_result,
+ fb);
+ }
+ return fb;
+}
+
+
+void
+TEH_kyc_fallback_cancel (
+ struct TEH_KycAmlFallback *fb)
+{
+ if (NULL != fb->task)
+ {
+ GNUNET_SCHEDULER_cancel (fb->task);
+ fb->task = NULL;
+ }
+ if (NULL != fb->aprh)
+ {
+ TALER_KYCLOGIC_run_aml_program_cancel (fb->aprh);
+ fb->aprh = NULL;
+ }
+ GNUNET_free (fb);
+}
+
+
bool
-TEH_kyc_failed (uint64_t process_row,
- const struct TALER_PaytoHashP *account_id,
- const char *provider_name,
- const char *provider_user_id,
- const char *provider_legitimization_id)
+TEH_kyc_failed (
+ uint64_t process_row,
+ const struct TALER_PaytoHashP *account_id,
+ const char *provider_name,
+ const char *provider_user_id,
+ const char *provider_legitimization_id,
+ const char *error_message,
+ enum TALER_ErrorCode ec)
{
enum GNUNET_DB_QueryStatus qs;
@@ -343,7 +889,13 @@ TEH_kyc_failed (uint64_t process_row,
account_id,
provider_name,
provider_user_id,
- provider_legitimization_id);
- GNUNET_break (qs >= 0);
- return qs >= 0;
+ provider_legitimization_id,
+ error_message,
+ ec);
+ if (qs <= 0)
+ {
+ GNUNET_break (0);
+ return false;
+ }
+ return true;
}
diff --git a/src/exchange/taler-exchange-httpd_common_kyc.h b/src/exchange/taler-exchange-httpd_common_kyc.h
@@ -50,7 +50,7 @@ typedef void
*/
struct TEH_KycAmlTrigger;
-// FIXME: also pass async log context and set it!
+
/**
* We have finished a KYC process and obtained new
* @a attributes for a given @a account_id.
@@ -73,18 +73,19 @@ struct TEH_KycAmlTrigger;
* @return handle to cancel the operation
*/
struct TEH_KycAmlTrigger *
-TEH_kyc_finished (const struct GNUNET_AsyncScopeId *scope,
- uint64_t process_row,
- const struct TALER_PaytoHashP *account_id,
- const char *provider_name,
- const char *provider_user_id,
- const char *provider_legitimization_id,
- struct GNUNET_TIME_Absolute expiration,
- const json_t *attributes,
- unsigned int http_status,
- struct MHD_Response *response,
- TEH_KycAmlTriggerCallback cb,
- void *cb_cls);
+TEH_kyc_finished (
+ const struct GNUNET_AsyncScopeId *scope,
+ uint64_t process_row,
+ const struct TALER_PaytoHashP *account_id,
+ const char *provider_name,
+ const char *provider_user_id,
+ const char *provider_legitimization_id,
+ struct GNUNET_TIME_Absolute expiration,
+ const json_t *attributes,
+ unsigned int http_status,
+ struct MHD_Response *response,
+ TEH_KycAmlTriggerCallback cb,
+ void *cb_cls);
/**
@@ -97,6 +98,69 @@ TEH_kyc_finished_cancel (struct TEH_KycAmlTrigger *kat);
/**
+ * Handle for an asynchronous operation to run some
+ * fallback measure.
+ */
+struct TEH_KycAmlFallback;
+
+
+/**
+ * Function called after the KYC-AML fallback
+ * processing is done.
+ *
+ * @param cls closure
+ * @param result true if fallback handling was OK
+ * @param requirement_row row of
+ * new KYC requirement that was created, 0 for none
+ */
+typedef void
+(*TEH_KycAmlFallbackCallback) (
+ void *cls,
+ bool result,
+ uint64_t requirement_row);
+
+
+/**
+ * Activate fallback measure for the given account.
+ *
+ * @param scope the HTTP request logging scope
+ * @param account_id account to activate fallback for
+ * @param orig_requirement_row original requirement
+ * row that now triggered the fallback
+ * @param attributes attributes to run with
+ * @param aml_history AML history of the account
+ * @param kyc_history KYC history of the account
+ * @param fallback_measure fallback to activate
+ * @param cb callback to call with result
+ * @param cb_cls closure for @a cb
+ * @return handle for fallback operation, NULL
+ * if @a fallback_measure is unknown
+ */
+struct TEH_KycAmlFallback *
+TEH_kyc_fallback (
+ const struct GNUNET_AsyncScopeId *scope,
+ const struct TALER_PaytoHashP *
+ account_id,
+ uint64_t orig_requirement_row,
+ const json_t *attributes,
+ const json_t *aml_history,
+ const json_t *kyc_history,
+ const char *fallback_measure,
+ TEH_KycAmlFallbackCallback cb,
+ void *cb_cls);
+
+
+/**
+ * Cancel fallback operation.
+ *
+ * @param[in] fb operation to cancel
+ */
+void
+TEH_kyc_fallback_cancel (
+ struct TEH_KycAmlFallback *fb);
+
+
+/**
* Update state of a legitmization process to 'finished'
* (and failed, no attributes were obtained).
*
@@ -105,13 +169,18 @@ TEH_kyc_finished_cancel (struct TEH_KycAmlTrigger *kat);
* @param provider_name name KYC provider with the logic that was run
* @param provider_user_id set to user ID at the provider, or NULL if not supported or unknown
* @param provider_legitimization_id set to legitimization process ID at the provider, or NULL if not supported or unknown
- * @return true on success, false if updating the database failed
+ * @param error_message error message to log
+ * @param ec error code to log
+ * @return true if the error was handled successfully
*/
bool
-TEH_kyc_failed (uint64_t process_row,
- const struct TALER_PaytoHashP *account_id,
- const char *provider_name,
- const char *provider_user_id,
- const char *provider_legitimization_id);
+TEH_kyc_failed (
+ uint64_t process_row,
+ const struct TALER_PaytoHashP *account_id,
+ const char *provider_name,
+ const char *provider_user_id,
+ const char *provider_legitimization_id,
+ const char *error_message,
+ enum TALER_ErrorCode ec);
#endif
diff --git a/src/exchange/taler-exchange-httpd_kyc-proof.c b/src/exchange/taler-exchange-httpd_kyc-proof.c
@@ -299,28 +299,29 @@ proof_cb (
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"KYC process #%llu succeeded with KYC provider\n",
(unsigned long long) kpc->process_row);
- kpc->kat = TEH_kyc_finished (&rc->async_scope_id,
- kpc->process_row,
- &kpc->h_payto,
- kpc->provider_name,
- provider_user_id,
- provider_legitimization_id,
- expiration,
- attributes,
- http_status,
- response,
- &proof_finish,
- kpc);
+ kpc->kat = TEH_kyc_finished (
+ &rc->async_scope_id,
+ kpc->process_row,
+ &kpc->h_payto,
+ kpc->provider_name,
+ provider_user_id,
+ provider_legitimization_id,
+ expiration,
+ attributes,
+ http_status,
+ response,
+ &proof_finish,
+ kpc);
+ response = NULL; /* taken over by TEH_kyc_finished */
if (NULL == kpc->kat)
{
http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
- if (NULL != response)
- MHD_destroy_response (response);
- response = make_html_error (kpc->rc->connection,
- "kyc-proof-internal-error",
- &http_status,
- TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION,
- "[exchange] AML_KYC_TRIGGER");
+ response = make_html_error (
+ kpc->rc->connection,
+ "kyc-proof-internal-error",
+ &http_status,
+ TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION,
+ "[exchange] AML_KYC_TRIGGER");
}
break;
case TALER_KYCLOGIC_STATUS_FAILED:
@@ -345,30 +346,35 @@ proof_cb (
"Failure by KYC provider (HTTP status %u)\n",
http_status);
http_status = MHD_HTTP_BAD_GATEWAY;
- response = make_html_error (kpc->rc->connection,
- "kyc-proof-internal-error",
- &http_status,
- TALER_EC_EXCHANGE_KYC_GENERIC_PROVIDER_UNEXPECTED_REPLY,
- msg);
+ response = make_html_error (
+ kpc->rc->connection,
+ "kyc-proof-internal-error",
+ &http_status,
+ TALER_EC_EXCHANGE_KYC_GENERIC_PROVIDER_UNEXPECTED_REPLY,
+ msg);
GNUNET_free (msg);
}
else
{
- if (! TEH_kyc_failed (kpc->process_row,
- &kpc->h_payto,
- kpc->provider_name,
- provider_user_id,
- provider_legitimization_id))
+ if (! TEH_kyc_failed (
+ kpc->process_row,
+ &kpc->h_payto,
+ kpc->provider_name,
+ provider_user_id,
+ provider_legitimization_id,
+ TALER_KYCLOGIC_status2s (status),
+ TALER_EC_EXCHANGE_GENERIC_KYC_FAILED))
{
GNUNET_break (0);
if (NULL != response)
MHD_destroy_response (response);
http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
- response = make_html_error (kpc->rc->connection,
- "kyc-proof-internal-error",
- &http_status,
- TALER_EC_GENERIC_DB_STORE_FAILED,
- "insert_kyc_failure");
+ response = make_html_error (
+ kpc->rc->connection,
+ "kyc-proof-internal-error",
+ &http_status,
+ TALER_EC_GENERIC_DB_STORE_FAILED,
+ "insert_kyc_failure");
}
}
break;
@@ -440,11 +446,12 @@ TEH_handler_kyc_proof (
if (NULL == provider_name_or_logic)
{
GNUNET_break_op (0);
- return respond_html_ec (rc,
- MHD_HTTP_NOT_FOUND,
- "kyc-proof-endpoint-unknown",
- TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
- "'/kyc-proof/$PROVIDER_NAME?state=$H_PAYTO' required");
+ return respond_html_ec (
+ rc,
+ MHD_HTTP_NOT_FOUND,
+ "kyc-proof-endpoint-unknown",
+ TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
+ "'/kyc-proof/$PROVIDER_NAME?state=$H_PAYTO' required");
}
kpc = GNUNET_new (struct KycProofContext);
kpc->rc = rc;
@@ -454,17 +461,19 @@ TEH_handler_kyc_proof (
"state",
&kpc->h_payto);
if (GNUNET_OK !=
- TALER_KYCLOGIC_lookup_logic (provider_name_or_logic,
- &kpc->logic,
- &kpc->pd,
- &kpc->provider_name))
+ TALER_KYCLOGIC_lookup_logic (
+ provider_name_or_logic,
+ &kpc->logic,
+ &kpc->pd,
+ &kpc->provider_name))
{
GNUNET_break_op (0);
- return respond_html_ec (rc,
- MHD_HTTP_NOT_FOUND,
- "kyc-proof-target-unknown",
- TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_UNKNOWN,
- provider_name_or_logic);
+ return respond_html_ec (
+ rc,
+ MHD_HTTP_NOT_FOUND,
+ "kyc-proof-target-unknown",
+ TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_UNKNOWN,
+ provider_name_or_logic);
}
if (NULL != kpc->provider_name)
{
@@ -475,11 +484,12 @@ TEH_handler_kyc_proof (
kpc->provider_name))
{
GNUNET_break_op (0);
- return respond_html_ec (rc,
- MHD_HTTP_BAD_REQUEST,
- "kyc-proof-bad-request",
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "PROVIDER_NAME");
+ return respond_html_ec (
+ rc,
+ MHD_HTTP_BAD_REQUEST,
+ "kyc-proof-bad-request",
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "PROVIDER_NAME");
}
qs = TEH_plugin->lookup_kyc_process_by_account (
@@ -494,47 +504,52 @@ TEH_handler_kyc_proof (
{
case GNUNET_DB_STATUS_HARD_ERROR:
case GNUNET_DB_STATUS_SOFT_ERROR:
- return respond_html_ec (rc,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- "kyc-proof-internal-error",
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- "lookup_kyc_process_by_account");
+ return respond_html_ec (
+ rc,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ "kyc-proof-internal-error",
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "lookup_kyc_process_by_account");
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
- return respond_html_ec (rc,
- MHD_HTTP_NOT_FOUND,
- "kyc-proof-target-unknown",
- TALER_EC_EXCHANGE_KYC_PROOF_REQUEST_UNKNOWN,
- kpc->provider_name);
+ return respond_html_ec (
+ rc,
+ MHD_HTTP_NOT_FOUND,
+ "kyc-proof-target-unknown",
+ TALER_EC_EXCHANGE_KYC_PROOF_REQUEST_UNKNOWN,
+ kpc->provider_name);
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
break;
}
if (GNUNET_TIME_absolute_is_future (expiration))
{
/* KYC not required */
- return respond_html_ec (rc,
- MHD_HTTP_OK,
- "kyc-proof-already-done",
- TALER_EC_NONE,
- NULL);
+ return respond_html_ec (
+ rc,
+ MHD_HTTP_OK,
+ "kyc-proof-already-done",
+ TALER_EC_NONE,
+ NULL);
}
}
- kpc->ph = kpc->logic->proof (kpc->logic->cls,
- kpc->pd,
- rc->connection,
- &kpc->h_payto,
- kpc->process_row,
- kpc->provider_user_id,
- kpc->provider_legitimization_id,
- &proof_cb,
- kpc);
+ kpc->ph = kpc->logic->proof (
+ kpc->logic->cls,
+ kpc->pd,
+ rc->connection,
+ &kpc->h_payto,
+ kpc->process_row,
+ kpc->provider_user_id,
+ kpc->provider_legitimization_id,
+ &proof_cb,
+ kpc);
if (NULL == kpc->ph)
{
GNUNET_break (0);
- return respond_html_ec (rc,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- "kyc-proof-internal-error",
- TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
- "could not start proof with KYC logic");
+ return respond_html_ec (
+ rc,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ "kyc-proof-internal-error",
+ TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ "could not start proof with KYC logic");
}
@@ -549,11 +564,12 @@ TEH_handler_kyc_proof (
if (NULL == kpc->response)
{
GNUNET_break (0);
- return respond_html_ec (rc,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- "kyc-proof-internal-error",
- TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
- "handler resumed without response");
+ return respond_html_ec (
+ rc,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ "kyc-proof-internal-error",
+ TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ "handler resumed without response");
}
/* return response from KYC logic */
diff --git a/src/exchange/taler-exchange-httpd_kyc-upload.c b/src/exchange/taler-exchange-httpd_kyc-upload.c
@@ -523,6 +523,7 @@ TEH_handler_kyc_upload (struct TEH_RequestContext *rc,
empty_response,
&aml_trigger_callback,
uc);
+ empty_response = NULL; /* taken over by TEH_kyc_finished */
if (NULL == uc->kat)
{
GNUNET_break (0);
diff --git a/src/exchange/taler-exchange-httpd_kyc-webhook.c b/src/exchange/taler-exchange-httpd_kyc-webhook.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2022-2023 Taler Systems SA
+ Copyright (C) 2022-2024 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
@@ -240,11 +240,14 @@ webhook_finished_cb (
provider_legitimization_id,
(unsigned long long) process_row,
status);
- if (! TEH_kyc_failed (process_row,
- account_id,
- provider_name,
- provider_user_id,
- provider_legitimization_id))
+ if (! TEH_kyc_failed (
+ process_row,
+ account_id,
+ provider_name,
+ provider_user_id,
+ provider_legitimization_id,
+ TALER_KYCLOGIC_status2s (status),
+ TALER_EC_EXCHANGE_GENERIC_KYC_FAILED))
{
GNUNET_break (0);
if (NULL != response)
@@ -256,12 +259,13 @@ webhook_finished_cb (
}
break;
default:
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "KYC status of %s/%s (Row #%llu) is %d\n",
- provider_user_id,
- provider_legitimization_id,
- (unsigned long long) process_row,
- (int) status);
+ GNUNET_log (
+ GNUNET_ERROR_TYPE_INFO,
+ "KYC status of %s/%s (Row #%llu) is %d\n",
+ provider_user_id,
+ provider_legitimization_id,
+ (unsigned long long) process_row,
+ (int) status);
break;
}
GNUNET_break (NULL == kwh->kat);
@@ -327,40 +331,44 @@ handler_kyc_webhook_generic (
if ( (NULL == args[0]) ||
(GNUNET_OK !=
- TALER_KYCLOGIC_lookup_logic (args[0],
- &kwh->plugin,
- &kwh->pd,
- &kwh->provider_name)) )
+ TALER_KYCLOGIC_lookup_logic (
+ args[0],
+ &kwh->plugin,
+ &kwh->pd,
+ &kwh->provider_name)) )
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"KYC logic `%s' unknown (check KYC provider configuration)\n",
args[0]);
- return TALER_MHD_reply_with_error (rc->connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_UNKNOWN,
- args[0]);
+ return TALER_MHD_reply_with_error (
+ rc->connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_UNKNOWN,
+ args[0]);
}
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"KYC logic `%s' mapped to section %s\n",
args[0],
kwh->provider_name);
- kwh->wh = kwh->plugin->webhook (kwh->plugin->cls,
- kwh->pd,
- TEH_plugin->kyc_provider_account_lookup,
- TEH_plugin->cls,
- method,
- &args[1],
- rc->connection,
- root,
- &webhook_finished_cb,
- kwh);
+ kwh->wh = kwh->plugin->webhook (
+ kwh->plugin->cls,
+ kwh->pd,
+ TEH_plugin->kyc_provider_account_lookup,
+ TEH_plugin->cls,
+ method,
+ &args[1],
+ rc->connection,
+ root,
+ &webhook_finished_cb,
+ kwh);
if (NULL == kwh->wh)
{
GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (rc->connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
- "failed to run webhook logic");
+ return TALER_MHD_reply_with_error (
+ rc->connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ "failed to run webhook logic");
}
kwh->suspended = GNUNET_YES;
GNUNET_CONTAINER_DLL_insert (kwh_head,
@@ -385,10 +393,11 @@ handler_kyc_webhook_generic (
/* We resumed, but got no response? This should
not happen. */
GNUNET_break (0);
- return TALER_MHD_reply_with_error (rc->connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
- "resumed without response");
+ return TALER_MHD_reply_with_error (
+ rc->connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ "resumed without response");
}
@@ -397,10 +406,11 @@ TEH_handler_kyc_webhook_get (
struct TEH_RequestContext *rc,
const char *const args[])
{
- return handler_kyc_webhook_generic (rc,
- MHD_HTTP_METHOD_GET,
- NULL,
- args);
+ return handler_kyc_webhook_generic (
+ rc,
+ MHD_HTTP_METHOD_GET,
+ NULL,
+ args);
}
@@ -410,10 +420,11 @@ TEH_handler_kyc_webhook_post (
const json_t *root,
const char *const args[])
{
- return handler_kyc_webhook_generic (rc,
- MHD_HTTP_METHOD_POST,
- root,
- args);
+ return handler_kyc_webhook_generic (
+ rc,
+ MHD_HTTP_METHOD_POST,
+ root,
+ args);
}
diff --git a/src/exchangedb/0005-legitimization_processes.sql b/src/exchangedb/0005-legitimization_processes.sql
@@ -24,7 +24,9 @@ BEGIN
PERFORM create_partitioned_table(
'ALTER TABLE legitimization_processes'
' ADD COLUMN legitimization_measure_serial_id BIGINT'
- ',ADD COLUMN measure_index INT4'
+ ',ADD COLUMN measure_index INT4 DEFAULT(0)'
+ ',ADD COLUMN error_code INT4 DEFAULT (0)'
+ ',ADD COLUMN error_message TEXT DEFAULT NULL'
';'
,'legitimization_processes'
,''
@@ -45,11 +47,23 @@ BEGIN
,shard_suffix
);
PERFORM comment_partitioned_column(
- 'index of the measure in legitimization_measures that was selected for this KYC setup; NULL if legitimization_measure_serial_id is NULL; enables determination of the context data provided to the external proces'
+ 'index of the measure in legitimization_measures that was selected for this KYC setup; NULL if legitimization_measure_serial_id is NULL; enables determination of the context data provided to the external process'
,'measure_index'
,'legitimization_processes'
,shard_suffix
);
+ PERFORM comment_partitioned_column(
+ 'TALER_ErrorCode set if the process failed, otherwise NULL'
+ ,'error_code'
+ ,'legitimization_processes'
+ ,shard_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'human-readable error details set if the process failed, otherwise NULL'
+ ,'error_message'
+ ,'legitimization_processes'
+ ,shard_suffix
+ );
END
$$;
diff --git a/src/exchangedb/Makefile.am b/src/exchangedb/Makefile.am
@@ -115,6 +115,8 @@ libtaler_plugin_exchangedb_postgres_la_SOURCES = \
pg_select_withdraw_amounts_for_kyc_check.h pg_select_withdraw_amounts_for_kyc_check.c \
pg_select_merge_amounts_for_kyc_check.h pg_select_merge_amounts_for_kyc_check.c \
pg_profit_drains_set_finished.h pg_profit_drains_set_finished.c \
+ pg_lookup_aml_history.h pg_lookup_aml_history.c \
+ pg_lookup_kyc_history.h pg_lookup_kyc_history.c \
pg_profit_drains_get_pending.h pg_profit_drains_get_pending.c \
pg_get_drain_profit.h pg_get_drain_profit.c \
pg_get_purse_deposit.h pg_get_purse_deposit.c \
diff --git a/src/exchangedb/exchange_do_trigger_kyc_rule_for_account.sql b/src/exchangedb/exchange_do_trigger_kyc_rule_for_account.sql
@@ -26,7 +26,8 @@ AS $$
DECLARE
my_access_token BYTEA;
BEGIN
-
+-- Note: in_payto_uri is allowed to be NULL *if*
+-- in_h_payto is already in wire_targets
SELECT
access_token
INTO
diff --git a/src/exchangedb/pg_get_coin_transactions.c b/src/exchangedb/pg_get_coin_transactions.c
@@ -38,7 +38,7 @@
#define RETRIES 3
/**
- * Closure for callbacks called from #postgres_get_coin_transactions()
+ * Closure for callbacks called from #TEH_PG_get_coin_transactions()
*/
struct CoinHistoryContext
{
diff --git a/src/exchangedb/pg_insert_kyc_failure.c b/src/exchangedb/pg_insert_kyc_failure.c
@@ -34,9 +34,12 @@ TEH_PG_insert_kyc_failure (
const struct TALER_PaytoHashP *h_payto,
const char *provider_name,
const char *provider_account_id,
- const char *provider_legitimization_id)
+ const char *provider_legitimization_id,
+ const char *error_message,
+ enum TALER_ErrorCode ec)
{
struct PostgresClosure *pg = cls;
+ uint32_t ec32 = (uint32_t) ec;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&process_row),
GNUNET_PQ_query_param_auto_from_type (h_payto),
@@ -47,6 +50,8 @@ TEH_PG_insert_kyc_failure (
NULL != provider_legitimization_id
? GNUNET_PQ_query_param_string (provider_legitimization_id)
: GNUNET_PQ_query_param_null (),
+ GNUNET_PQ_query_param_uint32 (&ec32),
+ GNUNET_PQ_query_param_string (error_message),
GNUNET_PQ_query_param_end
};
enum GNUNET_DB_QueryStatus qs;
@@ -58,6 +63,8 @@ TEH_PG_insert_kyc_failure (
" finished=TRUE"
" ,provider_user_id=$4"
" ,provider_legitimization_id=$5"
+ " ,error_code=$6"
+ " ,error_message=$7"
" WHERE h_payto=$2"
" AND legitimization_process_serial_id=$1"
" AND provider_name=$3;");
diff --git a/src/exchangedb/pg_insert_kyc_failure.h b/src/exchangedb/pg_insert_kyc_failure.h
@@ -35,6 +35,8 @@
* @param provider_name provider that must be checked
* @param provider_account_id provider account ID
* @param provider_legitimization_id provider legitimization ID
+ * @param error_message details about what went wrong
+ * @param ec error code about the failure
* @return database transaction status
*/
enum GNUNET_DB_QueryStatus
@@ -44,7 +46,9 @@ TEH_PG_insert_kyc_failure (
const struct TALER_PaytoHashP *h_payto,
const char *provider_name,
const char *provider_account_id,
- const char *provider_legitimization_id);
+ const char *provider_legitimization_id,
+ const char *error_message,
+ enum TALER_ErrorCode ec);
#endif
diff --git a/src/exchangedb/pg_lookup_aml_history.c b/src/exchangedb/pg_lookup_aml_history.c
@@ -0,0 +1,170 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2024 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file exchangedb/pg_lookup_aml_history.c
+ * @brief Implementation of the lookup_aml_history function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_error_codes.h"
+#include "taler_dbevents.h"
+#include "taler_pq_lib.h"
+#include "pg_lookup_aml_history.h"
+#include "pg_helper.h"
+
+
+/**
+ * Closure for callbacks called from #TEH_PG_lookup_aml_history()
+ */
+struct AmlHistoryContext
+{
+
+ /**
+ * Function to call on each result.
+ */
+ TALER_EXCHANGEDB_AmlHistoryCallback cb;
+
+ /**
+ * Closure for @e cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Plugin context.
+ */
+ struct PostgresClosure *pg;
+
+ /**
+ * Set to 'true' if the transaction failed.
+ */
+ bool failed;
+
+};
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct AmlHistoryContext`
+ * @param result the postgres result
+ * @param num_results the number of results in @a result
+ */
+static void
+handle_aml_entry (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct AmlHistoryContext *ahc = cls;
+
+ for (unsigned int i = 0; i < num_results; i++)
+ {
+ struct GNUNET_TIME_Timestamp decision_time;
+ char *justification;
+ struct TALER_AmlOfficerPublicKeyP decider_pub;
+ json_t *jproperties;
+ json_t *jnew_rules;
+ bool to_investigate;
+ bool is_active;
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_timestamp ("decision_time",
+ &decision_time),
+ GNUNET_PQ_result_spec_string ("justification",
+ &justification),
+ GNUNET_PQ_result_spec_auto_from_type ("decider_pub",
+ &decider_pub),
+ TALER_PQ_result_spec_json ("properties",
+ &jproperties),
+ TALER_PQ_result_spec_json ("new_rules",
+ &jnew_rules),
+ GNUNET_PQ_result_spec_bool ("to_investigate",
+ &to_investigate),
+ GNUNET_PQ_result_spec_bool ("is_active",
+ &is_active),
+ GNUNET_PQ_result_spec_end
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ i))
+ {
+ GNUNET_break (0);
+ ahc->failed = true;
+ return;
+ }
+ ahc->cb (ahc->cb_cls,
+ decision_time,
+ justification,
+ &decider_pub,
+ jproperties,
+ jnew_rules,
+ to_investigate,
+ is_active);
+ GNUNET_PQ_cleanup_result (rs);
+ }
+}
+
+
+enum GNUNET_DB_QueryStatus
+TEH_PG_lookup_aml_history (
+ void *cls,
+ const struct TALER_PaytoHashP *h_payto,
+ TALER_EXCHANGEDB_AmlHistoryCallback cb,
+ void *cb_cls)
+{
+ struct PostgresClosure *pg = cls;
+ struct AmlHistoryContext ahc = {
+ .pg = pg,
+ .cb = cb,
+ .cb_cls = cb_cls
+ };
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (h_payto),
+ GNUNET_PQ_query_param_end
+ };
+ enum GNUNET_DB_QueryStatus qs;
+
+ PREPARE (pg,
+ "lookup_aml_history",
+ "SELECT"
+ " ah.decision_time"
+ ",ah.justification"
+ ",ah.decider_pub"
+ ",lo.jproperties"
+ ",lo.jnew_rules"
+ ",lo.to_investigate"
+ ".lo.is_active"
+ " FROM aml_history ah"
+ " JOIN legitimization_outcomes lo"
+ " USING (outcome_serial_id)"
+ " WHERE h_payto=$1"
+ " ORDER BY decision_time DESC;");
+ qs = GNUNET_PQ_eval_prepared_multi_select (
+ pg->conn,
+ "lookup_aml_history",
+ params,
+ &handle_aml_entry,
+ &ahc);
+ if (qs <= 0)
+ return qs;
+ if (ahc.failed)
+ {
+ GNUNET_break (0);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ return qs;
+}
diff --git a/src/exchangedb/pg_lookup_aml_history.h b/src/exchangedb/pg_lookup_aml_history.h
@@ -0,0 +1,47 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2024 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file exchangedb/pg_lookup_aml_history.h
+ * @brief implementation of the lookup_aml_history function for Postgres
+ * @author Christian Grothoff
+ */
+#ifndef PG_LOOKUP_AML_HISTORY_H
+#define PG_LOOKUP_AML_HISTORY_H
+
+#include "taler_util.h"
+#include "taler_json_lib.h"
+#include "taler_exchangedb_plugin.h"
+
+
+/**
+ * Lookup AML history for an account identified via
+ * @a h_payto.
+ *
+ * @param cls closure
+ * @param h_payto hash of account to lookup history for
+ * @param cb function to call on results
+ * @param cb_cls closure for @a cb
+ * @return database transaction status
+ */
+enum GNUNET_DB_QueryStatus
+TEH_PG_lookup_aml_history (
+ void *cls,
+ const struct TALER_PaytoHashP *h_payto,
+ TALER_EXCHANGEDB_AmlHistoryCallback cb,
+ void *cb_cls);
+
+
+#endif
diff --git a/src/exchangedb/pg_lookup_kyc_history.c b/src/exchangedb/pg_lookup_kyc_history.c
@@ -0,0 +1,195 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2024 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file exchangedb/pg_lookup_kyc_history.c
+ * @brief Implementation of the lookup_kyc_history function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_error_codes.h"
+#include "taler_dbevents.h"
+#include "taler_pq_lib.h"
+#include "pg_lookup_kyc_history.h"
+#include "pg_helper.h"
+
+/**
+ * Closure for callbacks called from #TEH_PG_lookup_kyc_history()
+ */
+struct KycHistoryContext
+{
+
+ /**
+ * Function to call on each result.
+ */
+ TALER_EXCHANGEDB_KycHistoryCallback cb;
+
+ /**
+ * Closure for @e cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Plugin context.
+ */
+ struct PostgresClosure *pg;
+
+ /**
+ * Set to 'true' if the transaction failed.
+ */
+ bool failed;
+
+};
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct KycHistoryContext`
+ * @param result the postgres result
+ * @param num_results the number of results in @a result
+ */
+static void
+handle_kyc_entry (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct KycHistoryContext *khc = cls;
+
+ for (unsigned int i = 0; i < num_results; i++)
+ {
+ char *provider_name = NULL;
+ bool finished;
+ uint32_t error_code;
+ char *error_message = NULL;
+ char *provider_user_id = NULL;
+ char *provider_legitimization_id = NULL;
+ struct GNUNET_TIME_Timestamp collection_time;
+ struct GNUNET_TIME_Absolute expiration_time
+ = GNUNET_TIME_UNIT_ZERO_ABS;
+ void *encrypted_attributes;
+ size_t encrypted_attributes_len;
+
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_string ("provider_name",
+ &provider_name),
+ NULL),
+ GNUNET_PQ_result_spec_bool ("finished",
+ &finished),
+ GNUNET_PQ_result_spec_uint32 ("error_code",
+ &error_code),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_string ("error_message",
+ &error_message),
+ NULL),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_string ("provider_user_id",
+ &provider_user_id),
+ NULL),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_string ("provider_legitimization_id",
+ &provider_legitimization_id),
+ NULL),
+ GNUNET_PQ_result_spec_timestamp ("collection_time",
+ &collection_time),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_absolute_time ("expiration_time",
+ &expiration_time),
+ NULL),
+ GNUNET_PQ_result_spec_variable_size ("encrypted_attributes",
+ &encrypted_attributes,
+ &encrypted_attributes_len),
+ GNUNET_PQ_result_spec_end
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ i))
+ {
+ GNUNET_break (0);
+ khc->failed = true;
+ return;
+ }
+ khc->cb (khc->cb_cls,
+ provider_name,
+ finished,
+ (enum TALER_ErrorCode) error_code,
+ error_message,
+ provider_user_id,
+ provider_legitimization_id,
+ collection_time,
+ expiration_time,
+ encrypted_attributes_len,
+ encrypted_attributes);
+ GNUNET_PQ_cleanup_result (rs);
+ }
+}
+
+
+enum GNUNET_DB_QueryStatus
+TEH_PG_lookup_kyc_history (
+ void *cls,
+ const struct TALER_PaytoHashP *h_payto,
+ TALER_EXCHANGEDB_KycHistoryCallback cb,
+ void *cb_cls)
+{
+ struct PostgresClosure *pg = cls;
+ struct KycHistoryContext khc = {
+ .pg = pg,
+ .cb = cb,
+ .cb_cls = cb_cls
+ };
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (h_payto),
+ GNUNET_PQ_query_param_end
+ };
+ enum GNUNET_DB_QueryStatus qs;
+
+ PREPARE (pg,
+ "lookup_kyc_history",
+ "SELECT"
+ " lp.provider_name"
+ ",lp.finished"
+ ",lp.error_code"
+ ",lp.error_message"
+ ",lp.provider_user_id"
+ ",lp.provider_legitimization_id"
+ ",ka.collection_time"
+ ",ka.expiration_time"
+ ",ka.encrypted_attributes"
+ " FROM kyc_attributes ka"
+ " JOIN legitimization_processes lp"
+ " ON (ka.legitimization_serial = lp.legitimization_process_serial)"
+ " WHERE ka.h_payto=$1"
+ " ORDER BY collection_time DESC;");
+
+ qs = GNUNET_PQ_eval_prepared_multi_select (
+ pg->conn,
+ "lookup_kyc_history",
+ params,
+ &handle_kyc_entry,
+ &khc);
+ if (qs <= 0)
+ return qs;
+ if (khc.failed)
+ {
+ GNUNET_break (0);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ return qs;
+}
diff --git a/src/exchangedb/pg_lookup_kyc_history.h b/src/exchangedb/pg_lookup_kyc_history.h
@@ -0,0 +1,46 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2024 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file exchangedb/pg_lookup_kyc_history.h
+ * @brief implementation of the lookup_kyc_history function for Postgres
+ * @author Christian Grothoff
+ */
+#ifndef PG_LOOKUP_KYC_HISTORY_H
+#define PG_LOOKUP_KYC_HISTORY_H
+
+#include "taler_util.h"
+#include "taler_json_lib.h"
+#include "taler_exchangedb_plugin.h"
+
+
+/**
+ * Lookup KYC history for an account identified via
+ * @a h_payto.
+ *
+ * @param cls closure
+ * @param h_payto hash of account to lookup history for
+ * @param cb function to call on results
+ * @param cb_cls closure for @a cb
+ * @return database transaction status
+ */
+enum GNUNET_DB_QueryStatus
+TEH_PG_lookup_kyc_history (
+ void *cls,
+ const struct TALER_PaytoHashP *h_payto,
+ TALER_EXCHANGEDB_KycHistoryCallback cb,
+ void *cb_cls);
+
+#endif
diff --git a/src/exchangedb/pg_trigger_kyc_rule_for_account.c b/src/exchangedb/pg_trigger_kyc_rule_for_account.c
@@ -40,7 +40,9 @@ TEH_PG_trigger_kyc_rule_for_account (
= GNUNET_TIME_absolute_get ();
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (h_payto),
- GNUNET_PQ_query_param_string (payto_uri),
+ NULL == payto_uri
+ ? GNUNET_PQ_query_param_null ()
+ : GNUNET_PQ_query_param_string (payto_uri),
GNUNET_PQ_query_param_absolute_time (&now),
TALER_PQ_query_param_json (jmeasures),
GNUNET_PQ_query_param_uint32 (&display_priority),
diff --git a/src/exchangedb/pg_trigger_kyc_rule_for_account.h b/src/exchangedb/pg_trigger_kyc_rule_for_account.h
@@ -30,7 +30,9 @@
* Insert KYC requirement for @a h_payto account into table.
*
* @param cls closure
- * @param payto_uri account that must be KYC'ed
+ * @param payto_uri account that must be KYC'ed,
+ * can be NULL if @a h_payto is already
+ * guaranteed to be in wire_targets
* @param h_payto hash of @a payto_uri
* @param jmeasures serialized MeasureSet to put in place
* @param display_priority priority of the rule
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -94,6 +94,8 @@
#include "pg_select_contract_by_purse.h"
#include "pg_insert_drain_profit.h"
#include "pg_do_reserve_purse.h"
+#include "pg_lookup_aml_history.h"
+#include "pg_lookup_kyc_history.h"
#include "pg_lookup_global_fee_by_time.h"
#include "pg_do_purse_deposit.h"
#include "pg_activate_signing_key.h"
@@ -686,6 +688,10 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
= &TEH_PG_gc;
plugin->select_coin_deposits_above_serial_id
= &TEH_PG_select_coin_deposits_above_serial_id;
+ plugin->lookup_aml_history
+ = &TEH_PG_lookup_aml_history;
+ plugin->lookup_kyc_history
+ = &TEH_PG_lookup_kyc_history;
plugin->select_purse_decisions_above_serial_id
= &TEH_PG_select_purse_decisions_above_serial_id;
plugin->select_purse_deposits_by_purse
diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h
@@ -3082,6 +3082,66 @@ typedef void
/**
+ * Function called with historic AML events of an
+ * account.
+ *
+ * @param cls closure
+ * @param decision_time when was the decision taken
+ * @param justification what was the given justification
+ * @param decider_pub which key signed the decision
+ * @param jproperties what are the new account properties
+ * @param jnew_rules what are the new account rules
+ * @param to_investigate should AML staff investigate
+ * after the decision
+ * @param is_active is this the active decision
+ */
+typedef void
+(*TALER_EXCHANGEDB_AmlHistoryCallback) (
+ void *cls,
+ struct GNUNET_TIME_Timestamp decision_time,
+ const char *justification,
+ const struct TALER_AmlOfficerPublicKeyP *decider_pub,
+ const json_t *jproperties,
+ const json_t *jnew_rules,
+ bool to_investigate,
+ bool is_active);
+
+
+/**
+ * Function called with historic KYC events of an
+ * account.
+ *
+ * @param cls closure
+ * @param provider_name name of the KYC provider
+ * @param finished did the KYC process finish
+ * @param error_code error code from the KYC process
+ * @param error_message error message from the KYC process,
+ * or NULL for none
+ * @param provider_user_id user ID at the provider
+ * or NULL for none
+ * @param provider_legitimization_id legitimization process ID at the provider
+ * or NULL for none
+ * @param collection_time when was the data collected
+ * @param expiration_time when does the collected data expire
+ * @param encrypted_attributes_len number of bytes in @a encrypted_attributes
+ * @param encrypted_attributes encrypted KYC attributes
+ */
+typedef void
+(*TALER_EXCHANGEDB_KycHistoryCallback) (
+ void *cls,
+ const char *provider_name,
+ bool finished,
+ enum TALER_ErrorCode error_code,
+ const char *error_message,
+ const char *provider_user_id,
+ const char *provider_legitimization_id,
+ struct GNUNET_TIME_Timestamp collection_time,
+ struct GNUNET_TIME_Absolute expiration_time,
+ size_t encrypted_attributes_len,
+ const void *encrypted_attributes);
+
+
+/**
* Provide information about wire fees.
*
* @param cls closure
@@ -6801,7 +6861,9 @@ struct TALER_EXCHANGEDB_Plugin
* Insert KYC requirement for @a h_payto account into table.
*
* @param cls closure
- * @param payto_uri account that must be KYC'ed
+ * @param payto_uri account that must be KYC'ed,
+ * can be NULL if @a h_payto is already
+ * guaranteed to be in wire_targets
* @param h_payto hash of @a payto_uri
* @param jmeasures serialized MeasureSet to put in place
* @param display_priority priority of the rule
@@ -7337,6 +7399,42 @@ struct TALER_EXCHANGEDB_Plugin
/**
+ * Lookup AML history for an account identified via
+ * @a h_payto.
+ *
+ * @param cls closure
+ * @param h_payto hash of account to lookup history for
+ * @param cb function to call on results
+ * @param cb_cls closure for @a cb
+ * @return database transaction status
+ */
+ enum GNUNET_DB_QueryStatus
+ (*lookup_aml_history)(
+ void *cls,
+ const struct TALER_PaytoHashP *h_payto,
+ TALER_EXCHANGEDB_AmlHistoryCallback cb,
+ void *cb_cls);
+
+
+ /**
+ * Lookup AML history for an account identified via
+ * @a h_payto.
+ *
+ * @param cls closure
+ * @param h_payto hash of account to lookup history for
+ * @param cb function to call on results
+ * @param cb_cls closure for @a cb
+ * @return database transaction status
+ */
+ enum GNUNET_DB_QueryStatus
+ (*lookup_kyc_history)(
+ void *cls,
+ const struct TALER_PaytoHashP *h_payto,
+ TALER_EXCHANGEDB_KycHistoryCallback cb,
+ void *cb_cls);
+
+
+ /**
* Lookup measure data for an active legitimization process.
*
* @param cls closure
@@ -7399,6 +7497,8 @@ struct TALER_EXCHANGEDB_Plugin
* @param provider_name provider that must be checked
* @param provider_account_id provider account ID
* @param provider_legitimization_id provider legitimization ID
+ * @param error_message details about what went wrong
+ * @param ec error code about the failure
* @return database transaction status
*/
enum GNUNET_DB_QueryStatus
@@ -7408,7 +7508,9 @@ struct TALER_EXCHANGEDB_Plugin
const struct TALER_PaytoHashP *h_payto,
const char *provider_name,
const char *provider_account_id,
- const char *provider_legitimization_id);
+ const char *provider_legitimization_id,
+ const char *error_message,
+ enum TALER_ErrorCode ec);
/**
* Function called to inject auditor triggers into the
diff --git a/src/include/taler_kyclogic_lib.h b/src/include/taler_kyclogic_lib.h
@@ -342,10 +342,32 @@ TALER_KYCLOGIC_kyc_test_required (
const struct TALER_KYCLOGIC_KycRule **triggered_rule);
+/**
+ * Get human-readable name of KYC rule.
+ *
+ * @param r rule to convert
+ * @return name of the rule
+ */
const char *
TALER_KYCLOGIC_rule2s (const struct TALER_KYCLOGIC_KycRule *r);
+/**
+ * Convert KYC status to human-readable string.
+ *
+ * @param status status to convert
+ * @return human-readable string
+ */
+const char *
+TALER_KYCLOGIC_status2s (enum TALER_KYCLOGIC_KycStatus status);
+
+
+/**
+ * Get priority of KYC rule.
+ *
+ * @param r rule to convert
+ * @return priority of the rule
+ */
uint32_t
TALER_KYCLOGIC_rule2priority (const struct TALER_KYCLOGIC_KycRule *r);
@@ -388,16 +410,56 @@ TALER_KYCLOGIC_is_enabled (void);
/**
- * A KYC rule @a r has been triggered. Convert the resulting requirements in
- * to JSON of type ``LegitimizationMeasures`` for the legitimization measures table.
+ * A KYC rule @a r has been triggered. Convert the resulting requirements into
+ * JSON of type ``LegitimizationMeasures`` for the legitimization measures table.
*
- * FIXME: not implemented!
* @param r a rule that was triggered
* @return JSON serialization of the corresponding
* ``LegitimizationMeasures``, NULL on error
*/
json_t *
-TALER_KYCLOGIC_rule_to_measures (const struct TALER_KYCLOGIC_KycRule *r);
+TALER_KYCLOGIC_rule_to_measures (
+ const struct TALER_KYCLOGIC_KycRule *r);
+
+
+/**
+ * Tuple with information about a KYC check to perform. Note that it will
+ * have references into the legitimization rule set provided to
+ * #TALER_KYCLOGIC_requirements_to_check() and thus has a lifetime that
+ * matches the legitimization rule set.
+ */
+struct TALER_KYCLOGIC_KycCheckContext
+{
+ /**
+ * KYC check to perform.
+ */
+ const struct TALER_KYCLOGIC_KycCheck *check;
+
+ /**
+ * Context for the check. Can be NULL.
+ */
+ const json_t *context;
+
+ /**
+ * Name of the AML program.
+ */
+ char *prog_name;
+};
+
+
+/**
+ * A KYC check @a kcc has been triggered. Convert the resulting singular
+ * requirement (only a single check is possible, not multiple alternatives)
+ * into JSON of type ``LegitimizationMeasures`` for the legitimization
+ * measures table.
+ *
+ * @param kcc check that was triggered
+ * @return JSON serialization of the corresponding
+ * ``LegitimizationMeasures``
+ */
+json_t *
+TALER_KYCLOGIC_check_to_measures (
+ const struct TALER_KYCLOGIC_KycCheckContext *kcc);
/**
@@ -482,28 +544,17 @@ TALER_KYCLOGIC_provider_to_logic (
/**
- * Tuple with information about a KYC check to perform. Note that it will
- * have references into the legitimization rule set provided to
- * #TALER_KYCLOGIC_requirements_to_check() and thus has a lifetime that
- * matches the legitimization rule set.
+ * Find default measure @a measure_name.
+ *
+ * @param measure_name name of measure to find
+ * @param[out] kcc initialized with KYC check data
+ * for the default measure
+ * @return #GNUNET_OK on success
*/
-struct TALER_KYCLOGIC_KycCheckContext
-{
- /**
- * KYC check to perform.
- */
- const struct TALER_KYCLOGIC_KycCheck *check;
-
- /**
- * Context for the check. Can be NULL.
- */
- const json_t *context;
-
- /**
- * Name of the AML program.
- */
- char *prog_name;
-};
+enum GNUNET_GenericReturnValue
+TALER_KYCLOGIC_get_default_measure (
+ const char *measure_name,
+ struct TALER_KYCLOGIC_KycCheckContext *kcc);
/**
@@ -741,6 +792,30 @@ TALER_KYCLOGIC_run_aml_program (
/**
+ * Run AML program @a prog_name with the given @a context.
+ *
+ * @param prog_name name of AML program to run
+ * @param attributes attributes to run with
+ * @param aml_history AML history of the account
+ * @param kyc_history KYC history of the account
+ * @param context context to run with
+ * @param aprc function to call with the result
+ * @param aprc_cls closure for @a aprc
+ * @return NULL if @a jmeasures is invalid for the
+ * selected @a measure_index or @a attributes
+ */
+struct TALER_KYCLOGIC_AmlProgramRunnerHandle *
+TALER_KYCLOGIC_run_aml_program2 (
+ const char *prog_name,
+ const json_t *attributes,
+ const json_t *aml_history,
+ const json_t *kyc_history,
+ const json_t *context,
+ TALER_KYCLOGIC_AmlProgramResultCallback aprc,
+ void *aprc_cls);
+
+
+/**
* Cancel running AML program.
*
* @param[in] aprh handle of program to cancel
diff --git a/src/kyclogic/kyclogic_api.c b/src/kyclogic/kyclogic_api.c
@@ -749,6 +749,40 @@ TALER_KYCLOGIC_rule2s (
}
+const char *
+TALER_KYCLOGIC_status2s (enum TALER_KYCLOGIC_KycStatus status)
+{
+ switch (status)
+ {
+ case TALER_KYCLOGIC_STATUS_SUCCESS:
+ return "success";
+ case TALER_KYCLOGIC_STATUS_USER:
+ return "user";
+ case TALER_KYCLOGIC_STATUS_PROVIDER:
+ return "provider";
+ case TALER_KYCLOGIC_STATUS_FAILED:
+ return "failed";
+ case TALER_KYCLOGIC_STATUS_PENDING:
+ return "pending";
+ case TALER_KYCLOGIC_STATUS_ABORTED:
+ return "aborted";
+ case TALER_KYCLOGIC_STATUS_USER_PENDING:
+ return "pending with user";
+ case TALER_KYCLOGIC_STATUS_PROVIDER_PENDING:
+ return "pending at provider";
+ case TALER_KYCLOGIC_STATUS_USER_ABORTED:
+ return "aborted by user";
+ case TALER_KYCLOGIC_STATUS_PROVIDER_FAILED:
+ return "failed by provider";
+ case TALER_KYCLOGIC_STATUS_KEEP:
+ return "keep";
+ case TALER_KYCLOGIC_STATUS_INTERNAL_ERROR:
+ return "internal error";
+ }
+ return "unknown status";
+}
+
+
json_t *
TALER_KYCLOGIC_rules_to_limits (const json_t *jrules)
{
@@ -920,6 +954,38 @@ TALER_KYCLOGIC_rule_to_measures (const struct TALER_KYCLOGIC_KycRule *r)
}
+json_t *
+TALER_KYCLOGIC_check_to_measures (
+ const struct TALER_KYCLOGIC_KycCheckContext *kcc)
+{
+ const struct TALER_KYCLOGIC_KycCheck *check
+ = kcc->check;
+ json_t *jmeasures;
+ json_t *mi;
+
+ jmeasures = json_array ();
+ GNUNET_assert (NULL != jmeasures);
+ mi = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("check_name",
+ check->check_name),
+ GNUNET_JSON_pack_string ("prog_name",
+ kcc->prog_name),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_object_incref ("context",
+ (json_t *) kcc->context)));
+ GNUNET_assert (0 ==
+ json_array_append_new (jmeasures,
+ mi));
+ return GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_array_steal ("measures",
+ jmeasures),
+ GNUNET_JSON_pack_bool ("is_and_combinator",
+ true),
+ GNUNET_JSON_pack_bool ("verboten",
+ false));
+}
+
+
uint32_t
TALER_KYCLOGIC_rule2priority (const struct TALER_KYCLOGIC_KycRule *r)
{
@@ -2322,6 +2388,49 @@ TALER_KYCLOGIC_provider_to_logic (
enum GNUNET_GenericReturnValue
+TALER_KYCLOGIC_get_default_measure (
+ const char *measure_name,
+ struct TALER_KYCLOGIC_KycCheckContext *kcc)
+{
+ const struct TALER_KYCLOGIC_Measure *measure;
+
+ measure = find_measure (&default_rules,
+ measure_name);
+ if (NULL == measure)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Default measure `%s' unknown\n",
+ measure_name);
+ return GNUNET_SYSERR;
+ }
+ if (0 == strcasecmp (measure->check_name,
+ "SKIP"))
+ {
+ kcc->check = NULL;
+ kcc->prog_name = measure->prog_name;
+ kcc->context = measure->context;
+ return GNUNET_OK;
+ }
+
+ for (unsigned int i = 0; i<num_kyc_checks; i++)
+ if (0 == strcmp (measure->check_name,
+ kyc_checks[i]->check_name))
+ {
+ kcc->check = kyc_checks[i];
+ kcc->prog_name = measure->prog_name;
+ kcc->context = measure->context;
+ return GNUNET_OK;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Check `%s' unknown (but required by measure %s)\n",
+ measure->check_name,
+ measure_name);
+ return GNUNET_SYSERR;
+}
+
+
+// FIXME: not used in 'main' exchange logic!
+enum GNUNET_GenericReturnValue
TALER_KYCLOGIC_requirements_to_check (
const struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs,
const struct TALER_KYCLOGIC_KycRule *kyc_rule,
@@ -2331,6 +2440,8 @@ TALER_KYCLOGIC_requirements_to_check (
bool found = false;
const struct TALER_KYCLOGIC_Measure *measure = NULL;
+ if (NULL == lrs)
+ lrs = &default_rules;
if (NULL == measure_name)
{
/* No measure selected, return the "new_check" */
@@ -2342,7 +2453,7 @@ TALER_KYCLOGIC_requirements_to_check (
}
else
{
- kcc->check =NULL;
+ kcc->check = NULL;
}
kcc->prog_name = measure->prog_name;
kcc->context = measure->context;
@@ -3135,9 +3246,7 @@ TALER_KYCLOGIC_run_aml_program (
TALER_KYCLOGIC_AmlProgramResultCallback aprc,
void *aprc_cls)
{
- struct TALER_KYCLOGIC_AmlProgramRunnerHandle *aprh;
const json_t *context;
- struct TALER_KYCLOGIC_AmlProgram *prog;
const char *check_name;
const char *prog_name;
@@ -3155,6 +3264,29 @@ TALER_KYCLOGIC_run_aml_program (
return NULL;
}
}
+ return TALER_KYCLOGIC_run_aml_program2 (prog_name,
+ attributes,
+ aml_history,
+ kyc_history,
+ context,
+ aprc,
+ aprc_cls);
+}
+
+
+struct TALER_KYCLOGIC_AmlProgramRunnerHandle *
+TALER_KYCLOGIC_run_aml_program2 (
+ const char *prog_name,
+ const json_t *attributes,
+ const json_t *aml_history,
+ const json_t *kyc_history,
+ const json_t *context,
+ TALER_KYCLOGIC_AmlProgramResultCallback aprc,
+ void *aprc_cls)
+{
+ struct TALER_KYCLOGIC_AmlProgramRunnerHandle *aprh;
+ struct TALER_KYCLOGIC_AmlProgram *prog;
+
prog = find_program (prog_name);
if (NULL == prog)
{