exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

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:
Msrc/exchange/taler-exchange-aggregator.c | 12------------
Msrc/exchange/taler-exchange-httpd.c | 4+++-
Msrc/exchange/taler-exchange-httpd_common_kyc.c | 44+++-----------------------------------------
Msrc/exchangedb/exchangedb_aml.c | 97++-----------------------------------------------------------------------------
Msrc/include/taler_kyclogic_lib.h | 18++++++++++++++++++
Msrc/kyclogic/kyclogic_api.c | 324+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Msrc/testing/test_exchange_api_age_restriction.conf | 7++++++-
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 = {}