commit 5e142edf661bb353784e189810ce688fe2607a7b
parent 9efa30cdd3a39d95e766cd1d9bfdeeefe4f87e06
Author: Christian Grothoff <christian@grothoff.org>
Date: Fri, 13 Dec 2024 01:46:14 +0100
improve timeout handling and fallback measure invocation as well as configuration sanity-checking on load
Diffstat:
7 files changed, 299 insertions(+), 207 deletions(-)
diff --git a/src/exchange/taler-exchange-aggregator.c b/src/exchange/taler-exchange-aggregator.c
@@ -102,11 +102,6 @@ struct AggregationUnit
const struct TALER_EXCHANGEDB_AccountInfo *wa;
/**
- * Handle for asynchronously running AML program.
- */
- struct TALER_KYCLOGIC_AmlProgramRunnerHandle *amlh;
-
- /**
* Shard this aggregation unit is part of.
*/
struct Shard *shard;
@@ -268,13 +263,6 @@ static void
cleanup_au (struct AggregationUnit *au)
{
GNUNET_assert (NULL != au);
- if (NULL != au->amlh)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Aborting AML program during aggregation cleanup\n");
- TALER_KYCLOGIC_run_aml_program_cancel (au->amlh);
- au->amlh = NULL;
- }
if (NULL != au->ru)
{
GNUNET_break (0);
diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c
@@ -2196,7 +2196,9 @@ exchange_serve_process_config (const char *cfg_fn)
TALER_KYCLOGIC_kyc_init (TEH_cfg,
cfg_fn))
{
- GNUNET_break (0);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to load configuration `%s'. Exiting.\n",
+ cfg_fn);
return GNUNET_SYSERR;
}
TEH_hard_limits
diff --git a/src/exchange/taler-exchange-httpd_common_kyc.c b/src/exchange/taler-exchange-httpd_common_kyc.c
@@ -347,37 +347,6 @@ TEH_kyc_store_attributes (
}
-/**
- * Task run when an AML program takes too long and runs into a
- * timeout. Kills the AML program and reports an error.
- *
- * @param cls a `struct TEH_KycMeasureRunContext *`
- */
-static void
-kyc_aml_timeout (void *cls)
-{
- struct TEH_KycMeasureRunContext *kat = cls;
- const char *prog_name
- = TALER_KYCLOGIC_run_aml_program_get_name (kat->kyc_aml);
- struct TALER_KYCLOGIC_AmlProgramResult apr = {
- .status = TALER_KYCLOGIC_AMLR_FAILURE,
- .details.failure.fallback_measure
- = TALER_KYCLOGIC_get_aml_program_fallback (prog_name),
- .details.failure.error_message = prog_name,
- .details.failure.ec = TALER_EC_EXCHANGE_KYC_GENERIC_AML_PROGRAM_TIMEOUT
- };
-
- kat->async_task = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "AML program `%s' exceeded maximum runtime. Aborting it.\n",
- prog_name);
- TALER_KYCLOGIC_run_aml_program_cancel (kat->kyc_aml);
- kat->kyc_aml = NULL;
- kyc_aml_finished (kat,
- &apr);
-}
-
-
struct TEH_KycMeasureRunContext *
TEH_kyc_run_measure_for_attributes (
const struct GNUNET_AsyncScopeId *scope,
@@ -421,11 +390,6 @@ TEH_kyc_run_measure_for_attributes (
.attribute_key = &TEH_attribute_key
};
- GNUNET_assert (NULL == kat->async_task);
- kat->async_task
- = GNUNET_SCHEDULER_add_delayed (TEH_aml_program_timeout,
- &kyc_aml_timeout,
- kat);
kat->kyc_aml
= TALER_KYCLOGIC_run_aml_program (
kat->jmeasures,
@@ -438,6 +402,7 @@ TEH_kyc_run_measure_for_attributes (
&hbc,
&TALER_EXCHANGEDB_kyc_history_builder,
&hbc,
+ TEH_aml_program_timeout,
&kyc_aml_finished,
kat);
}
@@ -556,11 +521,6 @@ TEH_kyc_run_measure_directly (
.attribute_key = &TEH_attribute_key
};
- GNUNET_assert (NULL == kat->async_task);
- kat->async_task
- = GNUNET_SCHEDULER_add_delayed (TEH_aml_program_timeout,
- &kyc_aml_timeout,
- kat);
kat->kyc_aml
= TALER_KYCLOGIC_run_aml_program3 (
instant_ms,
@@ -572,6 +532,7 @@ TEH_kyc_run_measure_directly (
&hbc,
&TALER_EXCHANGEDB_kyc_history_builder,
&hbc,
+ TEH_aml_program_timeout,
&kyc_aml_finished,
kat);
}
@@ -784,6 +745,7 @@ TEH_kyc_fallback (
&hbc,
&TALER_EXCHANGEDB_kyc_history_builder,
&hbc,
+ TEH_aml_program_timeout,
&handle_aml_fallback_result,
fb);
if (NULL == fb->aprh)
diff --git a/src/exchangedb/exchangedb_aml.c b/src/exchangedb/exchangedb_aml.c
@@ -235,8 +235,6 @@ aml_result_callback (
enum GNUNET_GenericReturnValue res;
ru->amlh = NULL;
- GNUNET_SCHEDULER_cancel (ru->t);
- ru->t = NULL;
res = ru->plugin->start (ru->plugin->cls,
"aml-persist-aml-program-result");
if (GNUNET_OK != res)
@@ -314,94 +312,6 @@ aml_result_callback (
/**
- * Task run when an AML program takes too long and runs into a
- * timeout. Kills the AML program and reports an error.
- *
- * @param cls a `struct TEH_KycMeasureRunContext *`
- */
-static void
-aml_program_timeout (void *cls)
-{
- struct TALER_EXCHANGEDB_RuleUpdater *ru = cls;
- struct TALER_KYCLOGIC_AmlProgramResult apr = {
- .status = TALER_KYCLOGIC_AMLR_FAILURE,
- .details.failure.fallback_measure
- = TALER_KYCLOGIC_get_aml_program_fallback (ru->aml_program_name),
- .details.failure.error_message = ru->aml_program_name,
- .details.failure.ec = TALER_EC_EXCHANGE_KYC_GENERIC_AML_PROGRAM_TIMEOUT
- };
- enum GNUNET_GenericReturnValue res;
- enum GNUNET_DB_QueryStatus qs;
-
- ru->t = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "AML program hit timeout!\n");
- TALER_KYCLOGIC_run_aml_program_cancel (ru->amlh);
- ru->amlh = NULL;
- GNUNET_assert (NULL != apr.details.failure.fallback_measure);
- res = ru->plugin->start (ru->plugin->cls,
- "aml-persist-aml-program-timeout");
- if (GNUNET_OK != res)
- {
- GNUNET_break (0);
- fail_update (ru,
- TALER_EC_GENERIC_DB_START_FAILED,
- "aml-persist-aml-program-timeout");
- return;
- }
- /* Update database update based on result */
- qs = TALER_EXCHANGEDB_persist_aml_program_result (
- ru->plugin,
- 0LLU, /* 0: no existing legitimization process, creates new row */
- &ru->account,
- &apr);
- switch (qs)
- {
- case GNUNET_DB_STATUS_HARD_ERROR:
- GNUNET_break (0);
- fail_update (ru,
- TALER_EC_GENERIC_DB_STORE_FAILED,
- "persist_aml_program_timeout");
- return;
- case GNUNET_DB_STATUS_SOFT_ERROR:
- /* Bad, couldn't persist AML result. Try again... */
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Serialization issue persisting timeout of AML program. Restarting.\n");
- fail_update (ru,
- TALER_EC_GENERIC_DB_SOFT_FAILURE,
- "persist_aml_program_timeout");
- return;
- case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
- /* Strange, but let's just continue */
- break;
- case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
- /* normal case */
- break;
- }
- {
- const char *fmn = apr.details.failure.fallback_measure;
- const struct TALER_KYCLOGIC_Measure *m;
-
- m = TALER_KYCLOGIC_get_measure (ru->lrs,
- fmn);
- if (NULL == m)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Fallback measure `%s' does not exist (anymore?).\n",
- fmn);
- TALER_KYCLOGIC_rules_free (ru->lrs);
- ru->lrs = NULL;
- return_result (ru);
- return;
- }
- run_measure (ru,
- m);
- return;
- }
-}
-
-
-/**
* Entrypoint that fetches the latest rules from the database
* and starts processing them. Called without an open database
* transaction, will start one.
@@ -458,7 +368,7 @@ run_measure (struct TALER_EXCHANGEDB_RuleUpdater *ru,
}
if ( (NULL == m->check_name) ||
(0 ==
- strcasecmp ("skip",
+ strcasecmp ("SKIP",
m->check_name)) )
{
struct TALER_EXCHANGEDB_HistoryBuilderContext hbc = {
@@ -513,10 +423,6 @@ run_measure (struct TALER_EXCHANGEDB_RuleUpdater *ru,
"Check is of type 'skip', running AML program %s.\n",
m->prog_name);
GNUNET_assert (NULL == ru->t);
- ru->t = GNUNET_SCHEDULER_add_delayed (
- ru->plugin->max_aml_program_runtime,
- &aml_program_timeout,
- ru);
ru->amlh = TALER_KYCLOGIC_run_aml_program3 (
m,
&TALER_EXCHANGEDB_current_attributes_builder,
@@ -527,6 +433,7 @@ run_measure (struct TALER_EXCHANGEDB_RuleUpdater *ru,
&hbc,
&TALER_EXCHANGEDB_kyc_history_builder,
&hbc,
+ ru->plugin->max_aml_program_runtime,
&aml_result_callback,
ru);
return;
diff --git a/src/include/taler_kyclogic_lib.h b/src/include/taler_kyclogic_lib.h
@@ -1001,6 +1001,11 @@ typedef json_t *
* @param aml_history_cb_cls closure for @a aml_history_cb
* @param kyc_history_cb callback to get the KYC history of the account
* @param kyc_history_cb_cls closure for @a aml_history_cb
+ * @param timeout timeout for running the AML program;
+ * terminates the AML program and runs the fallback measure immediately
+ * once the timeout is reached; in this case, the result from the
+ * fallback measure is returned; the fallback measure is also granted
+ * the same amount of time (so after 2x @a timeout we will call @a aprc)
* @param aprc function to call with the result
* @param aprc_cls closure for @a aprc
* @return NULL if @a jmeasures is invalid for the
@@ -1018,6 +1023,7 @@ TALER_KYCLOGIC_run_aml_program (
void *aml_history_cb_cls,
TALER_KYCLOGIC_HistoryBuilderCallback kyc_history_cb,
void *kyc_history_cb_cls,
+ struct GNUNET_TIME_Relative timeout,
TALER_KYCLOGIC_AmlProgramResultCallback aprc,
void *aprc_cls);
@@ -1035,6 +1041,11 @@ TALER_KYCLOGIC_run_aml_program (
* @param aml_history_cb_cls closure for @a aml_history_cb
* @param kyc_history_cb callback to get the KYC history of the account
* @param kyc_history_cb_cls closure for @a aml_history_cb
+ * @param timeout timeout for running the AML program;
+ * terminates the AML program and runs the fallback measure immediately
+ * once the timeout is reached; in this case, the result from the
+ * fallback measure is returned; the fallback measure is also granted
+ * the same amount of time (so after 2x @a timeout we will call @a aprc)
* @param aprc function to call with the result
* @param aprc_cls closure for @a aprc
* @return NULL if @a jmeasures is invalid for the
@@ -1052,6 +1063,7 @@ TALER_KYCLOGIC_run_aml_program2 (
void *aml_history_cb_cls,
TALER_KYCLOGIC_HistoryBuilderCallback kyc_history_cb,
void *kyc_history_cb_cls,
+ struct GNUNET_TIME_Relative timeout,
TALER_KYCLOGIC_AmlProgramResultCallback aprc,
void *aprc_cls);
@@ -1070,6 +1082,11 @@ TALER_KYCLOGIC_run_aml_program2 (
* @param aml_history_cb_cls closure for @a aml_history_cb
* @param kyc_history_cb callback to get the KYC history of the account
* @param kyc_history_cb_cls closure for @a aml_history_cb
+ * @param timeout timeout for running the AML program;
+ * terminates the AML program and runs the fallback measure immediately
+ * once the timeout is reached; in this case, the result from the
+ * fallback measure is returned; the fallback measure is also granted
+ * the same amount of time (so after 2x @a timeout we will call @a aprc)
* @param aprc function to call with the result
* @param aprc_cls closure for @a aprc
* @return NULL if @a jmeasures is invalid for the
@@ -1086,6 +1103,7 @@ TALER_KYCLOGIC_run_aml_program3 (
void *aml_history_cb_cls,
TALER_KYCLOGIC_HistoryBuilderCallback kyc_history_cb,
void *kyc_history_cb_cls,
+ struct GNUNET_TIME_Relative timeout,
TALER_KYCLOGIC_AmlProgramResultCallback aprc,
void *aprc_cls);
diff --git a/src/kyclogic/kyclogic_api.c b/src/kyclogic/kyclogic_api.c
@@ -406,7 +406,7 @@ find_program (const char *program_name)
program->program_name))
return program;
}
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"AML program `%s' unknown\n",
program_name);
return NULL;
@@ -451,18 +451,6 @@ check_measure (const struct TALER_KYCLOGIC_Measure *measure)
const struct TALER_KYCLOGIC_KycCheck *check;
const struct TALER_KYCLOGIC_AmlProgram *program;
- if (0 == strcasecmp (measure->check_name,
- "SKIP"))
- return true;
- check = find_check (measure->check_name);
- if (NULL == check)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unknown check `%s' used in measure `%s'\n",
- measure->check_name,
- measure->measure_name);
- return false;
- }
program = find_program (measure->prog_name);
if (NULL == program)
{
@@ -472,22 +460,6 @@ check_measure (const struct TALER_KYCLOGIC_Measure *measure)
measure->measure_name);
return false;
}
- for (unsigned int j = 0; j<check->num_requires; j++)
- {
- const char *required_input = check->requires[j];
-
- if (NULL ==
- json_object_get (measure->context,
- required_input))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Measure `%s' lacks required context `%s' for check `%s'\n",
- measure->measure_name,
- required_input,
- check->check_name);
- return false;
- }
- }
for (unsigned int j = 0; j<program->num_required_contexts; j++)
{
const char *required_context = program->required_contexts[j];
@@ -504,6 +476,28 @@ check_measure (const struct TALER_KYCLOGIC_Measure *measure)
return false;
}
}
+ if (0 == strcasecmp (measure->check_name,
+ "SKIP"))
+ {
+ if (0 != program->num_required_attributes)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "AML program `%s' of measure `%s' has required attributes, but check is of type `SKIP' and thus cannot provide any!\n",
+ program->program_name,
+ measure->measure_name);
+ return false;
+ }
+ return true;
+ }
+ check = find_check (measure->check_name);
+ if (NULL == check)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unknown check `%s' used in measure `%s'\n",
+ measure->check_name,
+ measure->measure_name);
+ return false;
+ }
for (unsigned int j = 0; j<program->num_required_attributes; j++)
{
const char *required_attribute = program->required_attributes[j];
@@ -529,6 +523,22 @@ check_measure (const struct TALER_KYCLOGIC_Measure *measure)
return false;
}
}
+ for (unsigned int j = 0; j<check->num_requires; j++)
+ {
+ const char *required_input = check->requires[j];
+
+ if (NULL ==
+ json_object_get (measure->context,
+ required_input))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Measure `%s' lacks required context `%s' for check `%s'\n",
+ measure->measure_name,
+ required_input,
+ check->check_name);
+ return false;
+ }
+ }
return true;
}
@@ -1274,7 +1284,8 @@ TALER_KYCLOGIC_get_instant_measure (
{
continue;
}
- if (0 == strcasecmp ("SKIP", ms->check_name))
+ if (0 == strcasecmp ("SKIP",
+ ms->check_name))
{
ret = ms;
goto done;
@@ -2445,7 +2456,13 @@ add_program (const struct GNUNET_CONFIGURATION_Handle *cfg,
"FALLBACK",
&fallback))
{
- /* FIXME: Allow NULL to fall back to default rules? */
+ /* We do *not* allow NULL to fall back to default rules because fallbacks
+ are used when there is actually a serious error and thus some action
+ (usually an investigation) is always in order, and that's basically
+ never the default. And as fallbacks should be rare, we really insist on
+ them at least being explicitly configured. Otherwise these errors may
+ go undetected simply because someone forgot to configure a fallback and
+ then nothing happens. */
GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
section,
"FALLBACK",
@@ -2846,14 +2863,26 @@ TALER_KYCLOGIC_kyc_init (
jkyc_rules)
);
+ for (unsigned int i=0; i<default_rules.num_custom_measures; i++)
+ {
+ const struct TALER_KYCLOGIC_Measure *measure
+ = &default_rules.custom_measures[i];
+
+ if (! check_measure (measure))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Configuration of AML measures incorrect. Exiting.\n");
+ return GNUNET_SYSERR;
+ }
+ }
+
for (unsigned int i=0; i<num_aml_programs; i++)
{
const struct TALER_KYCLOGIC_AmlProgram *program
= aml_programs[i];
const struct TALER_KYCLOGIC_Measure *m;
+ const struct TALER_KYCLOGIC_AmlProgram *fprogram;
- if (NULL == program->fallback)
- continue; /* default */
m = find_measure (&default_rules,
program->fallback);
if (NULL == m)
@@ -2864,41 +2893,69 @@ TALER_KYCLOGIC_kyc_init (
program->program_name);
return GNUNET_SYSERR;
}
+ if (0 != strcasecmp (m->check_name,
+ "SKIP"))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Fallback measure `%s' used in AML program `%s' has a check `%s' but fallbacks must have a check of type 'SKIP'\n",
+ program->fallback,
+ program->program_name,
+ m->check_name);
+ return GNUNET_SYSERR;
+ }
+ fprogram = find_program (m->prog_name);
+ GNUNET_assert (NULL != fprogram);
+ if (API_NONE != fprogram->input_mask)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Fallback program %s of fallback measure `%s' used in AML program `%s' has required inputs, but fallback measures must not require any inputs\n",
+ m->prog_name,
+ program->program_name,
+ m->check_name);
+ return GNUNET_SYSERR;
+ }
}
for (unsigned int i = 0; i<num_kyc_checks; i++)
{
struct TALER_KYCLOGIC_KycCheck *kyc_check
= kyc_checks[i];
+ const struct TALER_KYCLOGIC_Measure *measure;
+ const struct TALER_KYCLOGIC_AmlProgram *fprogram;
- if (NULL != kyc_check->fallback)
+ measure = find_measure (&default_rules,
+ kyc_check->fallback);
+ if (NULL == measure)
{
- const struct TALER_KYCLOGIC_Measure *measure;
-
- measure = find_measure (&default_rules,
- kyc_check->fallback);
- if (NULL == measure)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unknown fallback measure `%s' used in check `%s'\n",
- kyc_check->fallback,
- kyc_check->check_name);
- return GNUNET_SYSERR;
- }
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unknown fallback measure `%s' used in check `%s'\n",
+ kyc_check->fallback,
+ kyc_check->check_name);
+ return GNUNET_SYSERR;
}
- }
-
- for (unsigned int i=0; i<default_rules.num_custom_measures; i++)
- {
- const struct TALER_KYCLOGIC_Measure *measure
- = &default_rules.custom_measures[i];
-
- if (! check_measure (measure))
+ if (0 != strcasecmp (measure->check_name,
+ "SKIP"))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Fallback measure `%s' used in KYC check `%s' has a check `%s' but fallbacks must have a check of type 'SKIP'\n",
+ kyc_check->fallback,
+ kyc_check->check_name,
+ measure->check_name);
+ return GNUNET_SYSERR;
+ }
+ fprogram = find_program (measure->prog_name);
+ GNUNET_assert (NULL != fprogram);
+ if (API_NONE != fprogram->input_mask)
{
- GNUNET_break_op (0);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "AML program `%s' used fallback measure `%s' of KYC check `%s' has required inputs, but fallback measures must not require any inputs\n",
+ measure->prog_name,
+ kyc_check->fallback,
+ kyc_check->check_name);
return GNUNET_SYSERR;
}
}
+
return GNUNET_OK;
}
@@ -3816,8 +3873,14 @@ struct TALER_KYCLOGIC_AmlProgramRunnerHandle
*/
struct TALER_KYCLOGIC_AmlProgramResult apr;
+ /**
+ * How long do we allow the AML program to run?
+ */
+ struct GNUNET_TIME_Relative timeout;
+
};
+
/**
* Function that that receives a JSON @a result from
* the AML program.
@@ -3841,6 +3904,11 @@ handle_aml_output (
const char **evs = NULL;
aprh->proc = NULL;
+ if (NULL != aprh->async_cb)
+ {
+ GNUNET_SCHEDULER_cancel (aprh->async_cb);
+ aprh->async_cb = NULL;
+ }
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"AML program output is:\n");
json_dumpf (result,
@@ -3971,8 +4039,7 @@ ready:
/**
- * Helper function to asynchronously return
- * the result.
+ * Helper function to asynchronously return the result.
*
* @param[in] cls a `struct TALER_KYCLOGIC_AmlProgramRunnerHandle` to return results for
*/
@@ -3988,6 +4055,140 @@ async_return_task (void *cls)
}
+/**
+ * Helper function called on timeout on the fallback measure.
+ *
+ * @param[in] cls a `struct TALER_KYCLOGIC_AmlProgramRunnerHandle` to return results for
+ */
+static void
+handle_aml_timeout2 (void *cls)
+{
+ struct TALER_KYCLOGIC_AmlProgramRunnerHandle *aprh = cls;
+ struct TALER_KYCLOGIC_AmlProgramResult *apr = &aprh->apr;
+ const char *fallback_measure = aprh->program->fallback;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Fallback measure %s ran into timeout (!)\n",
+ aprh->program->program_name);
+ if (NULL != aprh->proc)
+ {
+ TALER_JSON_external_conversion_stop (aprh->proc);
+ aprh->proc = NULL;
+ }
+ apr->status = TALER_KYCLOGIC_AMLR_FAILURE;
+ apr->details.failure.fallback_measure
+ = fallback_measure;
+ apr->details.failure.error_message
+ = aprh->program->program_name;
+ apr->details.failure.ec
+ = TALER_EC_EXCHANGE_KYC_GENERIC_AML_PROGRAM_TIMEOUT;
+ async_return_task (aprh);
+}
+
+
+/**
+ * Helper function called on timeout of an AML program.
+ * Runs the fallback measure.
+ *
+ * @param[in] cls a `struct TALER_KYCLOGIC_AmlProgramRunnerHandle` to return results for
+ */
+static void
+handle_aml_timeout (void *cls)
+{
+ struct TALER_KYCLOGIC_AmlProgramRunnerHandle *aprh = cls;
+ struct TALER_KYCLOGIC_AmlProgramResult *apr = &aprh->apr;
+ const char *fallback_measure = aprh->program->fallback;
+ const struct TALER_KYCLOGIC_Measure *m;
+ const struct TALER_KYCLOGIC_AmlProgram *fprogram;
+
+ aprh->async_cb = NULL;
+ GNUNET_assert (NULL != fallback_measure);
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "AML program %s ran into timeout\n",
+ aprh->program->program_name);
+ if (NULL != aprh->proc)
+ {
+ TALER_JSON_external_conversion_stop (aprh->proc);
+ aprh->proc = NULL;
+ }
+
+ m = TALER_KYCLOGIC_get_measure (&default_rules,
+ fallback_measure);
+ /* Fallback program could have "disappeared" due to configuration change,
+ as we do not check all rule sets in the database when our configuration
+ is updated... */
+ if (NULL == m)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Fallback measure `%s' does not exist (anymore?).\n",
+ fallback_measure);
+ apr->status = TALER_KYCLOGIC_AMLR_FAILURE;
+ apr->details.failure.fallback_measure
+ = fallback_measure;
+ apr->details.failure.error_message
+ = aprh->program->program_name;
+ apr->details.failure.ec
+ = TALER_EC_EXCHANGE_KYC_GENERIC_AML_PROGRAM_TIMEOUT;
+ async_return_task (aprh);
+ return;
+ }
+ /* We require fallback measures to have a 'SKIP' check */
+ GNUNET_break (0 ==
+ strcasecmp (m->check_name,
+ "SKIP"));
+ fprogram = find_program (m->prog_name);
+ /* Program associated with an original measure must exist */
+ GNUNET_assert (NULL != fprogram);
+ if (API_NONE != fprogram->input_mask)
+ {
+ /* We might not have recognized the fallback measure as such
+ because it was not used as such in the plain configuration,
+ and legitimization rule sets might have referred to an older
+ configuration. So this should be super-rare but possible. */
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Program `%s' used in fallback measure `%s' requires inputs and is thus unsuitable as a fallback measure!\n",
+ m->prog_name,
+ fallback_measure);
+ apr->status = TALER_KYCLOGIC_AMLR_FAILURE;
+ apr->details.failure.fallback_measure
+ = fallback_measure;
+ apr->details.failure.error_message
+ = aprh->program->program_name;
+ apr->details.failure.ec
+ = TALER_EC_EXCHANGE_KYC_GENERIC_AML_PROGRAM_TIMEOUT;
+ async_return_task (aprh);
+ return;
+ }
+ {
+ /* Run fallback AML program */
+ json_t *input = json_object ();
+ const char *extra_args[] = {
+ "-c",
+ cfg_filename,
+ NULL,
+ };
+ char **args;
+
+ args = split_words (fprogram->command,
+ extra_args);
+ aprh->proc = TALER_JSON_external_conversion_start (
+ input,
+ &handle_aml_output,
+ aprh,
+ args[0],
+ (const char **) args);
+ destroy_words (args);
+ json_decref (input);
+ }
+ aprh->async_cb = GNUNET_SCHEDULER_add_delayed (aprh->timeout,
+ &handle_aml_timeout2,
+ aprh);
+ aprh->aprc (aprh->aprc_cls,
+ apr);
+ TALER_KYCLOGIC_run_aml_program_cancel (aprh);
+}
+
+
struct TALER_KYCLOGIC_AmlProgramRunnerHandle *
TALER_KYCLOGIC_run_aml_program (
const json_t *jmeasures,
@@ -4000,6 +4201,7 @@ TALER_KYCLOGIC_run_aml_program (
void *aml_history_cb_cls,
TALER_KYCLOGIC_HistoryBuilderCallback kyc_history_cb,
void *kyc_history_cb_cls,
+ struct GNUNET_TIME_Relative timeout,
TALER_KYCLOGIC_AmlProgramResultCallback aprc,
void *aprc_cls)
{
@@ -4031,6 +4233,7 @@ TALER_KYCLOGIC_run_aml_program (
aml_history_cb_cls,
kyc_history_cb,
kyc_history_cb_cls,
+ timeout,
aprc,
aprc_cls);
}
@@ -4048,6 +4251,7 @@ TALER_KYCLOGIC_run_aml_program2 (
void *aml_history_cb_cls,
TALER_KYCLOGIC_HistoryBuilderCallback kyc_history_cb,
void *kyc_history_cb_cls,
+ struct GNUNET_TIME_Relative timeout,
TALER_KYCLOGIC_AmlProgramResultCallback aprc,
void *aprc_cls)
{
@@ -4210,6 +4414,10 @@ TALER_KYCLOGIC_run_aml_program2 (
destroy_words (args);
json_decref (input);
}
+ aprh->timeout = timeout;
+ aprh->async_cb = GNUNET_SCHEDULER_add_delayed (timeout,
+ &handle_aml_timeout,
+ aprh);
return aprh;
}
@@ -4225,6 +4433,7 @@ TALER_KYCLOGIC_run_aml_program3 (
void *aml_history_cb_cls,
TALER_KYCLOGIC_HistoryBuilderCallback kyc_history_cb,
void *kyc_history_cb_cls,
+ struct GNUNET_TIME_Relative timeout,
TALER_KYCLOGIC_AmlProgramResultCallback aprc,
void *aprc_cls)
{
@@ -4239,6 +4448,7 @@ TALER_KYCLOGIC_run_aml_program3 (
aml_history_cb_cls,
kyc_history_cb,
kyc_history_cb_cls,
+ timeout,
aprc,
aprc_cls);
}
diff --git a/src/testing/test_exchange_api_age_restriction.conf b/src/testing/test_exchange_api_age_restriction.conf
@@ -165,6 +165,11 @@ PROGRAM = oauth-output-check
# Context to provide for check and program; empty.
CONTEXT = {}
+[aml-program-freeze]
+DESCRIPTION = "Freeze the account"
+COMMAND = taler-exchange-helper-measure-freeze
+FALLBACK = manual-freeze
+
# This is a base-measure that is being triggered
# whenever something goes wrong. We freeze the
# account and ask AML staff to investigate.
@@ -172,7 +177,7 @@ CONTEXT = {}
CHECK_NAME = skip
# AML program that freezes the account and flags
# it for investigation.
-PROGRAM = taler-exchange-helper-measure-freeze
+PROGRAM = freeze
# Context to provide for check and program; empty.
CONTEXT = {}