diff options
author | Christian Grothoff <christian@grothoff.org> | 2024-08-07 19:53:23 +0200 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2024-08-07 19:53:23 +0200 |
commit | c08266947f14ee390cbc37a8dd70746215361e8e (patch) | |
tree | f8c01835f7380a1c25c0c66f5349f3ab93f94dcb | |
parent | de42b35703ad010040bbbc932ca4aeaa0c582036 (diff) |
implement logic to deal with expiration of measures; untested, but could theoretically work
-rw-r--r-- | src/exchange/taler-exchange-httpd_common_kyc.c | 295 | ||||
-rw-r--r-- | src/exchangedb/Makefile.am | 1 | ||||
-rw-r--r-- | src/exchangedb/exchange_do_insert_aml_decision.sql | 5 | ||||
-rw-r--r-- | src/exchangedb/exchange_do_insert_programmatic_legitimization_outcome.sql | 189 | ||||
-rw-r--r-- | src/exchangedb/pg_insert_programmatic_legitimization_outcome.c | 92 | ||||
-rw-r--r-- | src/exchangedb/pg_insert_programmatic_legitimization_outcome.h | 56 | ||||
-rw-r--r-- | src/exchangedb/plugin_exchangedb_postgres.c | 3 | ||||
-rw-r--r-- | src/exchangedb/procedures.sql.in | 1 | ||||
-rw-r--r-- | src/include/taler_exchangedb_plugin.h | 29 | ||||
-rw-r--r-- | src/include/taler_kyclogic_lib.h | 3 | ||||
-rw-r--r-- | src/kyclogic/kyclogic_api.c | 59 |
11 files changed, 688 insertions, 45 deletions
diff --git a/src/exchange/taler-exchange-httpd_common_kyc.c b/src/exchange/taler-exchange-httpd_common_kyc.c index a3239fdea..b01d5fbe6 100644 --- a/src/exchange/taler-exchange-httpd_common_kyc.c +++ b/src/exchange/taler-exchange-httpd_common_kyc.c @@ -1013,25 +1013,30 @@ struct TEH_LegitimizationCheckHandle struct TEH_KycAmlTrigger *kat; /** - * Our request scope for logging. + * Payto-URI of the account. */ - struct GNUNET_AsyncScopeId scope; + char *payto_uri; /** - * Legitimization result we have been building and - * should return. + * Amount iterator to call to check for amounts. */ - struct TEH_LegitimizationCheckResult lcr; + TALER_KYCLOGIC_KycAmountIterator ai; /** - * Event we were triggered for. + * Closure for @e ai. */ - enum TALER_KYCLOGIC_KycTriggerEvent et; + void *ai_cls; /** - * Payto-URI of the account. + * Used to keep around the name of the AML program + * that we are running via @e aprh. */ - char *payto_uri; + char *aml_program; + + /** + * Handle to AML program we are running, or NULL for none. + */ + struct TALER_KYCLOGIC_AmlProgramRunnerHandle *aprh; /** * Hash of @e payto_uri. @@ -1044,14 +1049,20 @@ struct TEH_LegitimizationCheckHandle union TALER_AccountPublicKeyP account_pub; /** - * Amount iterator to call to check for amounts. + * Our request scope for logging. */ - TALER_KYCLOGIC_KycAmountIterator ai; + struct GNUNET_AsyncScopeId scope; /** - * Closure for @e ai. + * Legitimization result we have been building and + * should return. */ - void *ai_cls; + struct TEH_LegitimizationCheckResult lcr; + + /** + * Event we were triggered for. + */ + enum TALER_KYCLOGIC_KycTriggerEvent et; /** * Number of instant rule triggers we have experienced @@ -1196,6 +1207,225 @@ TEH_legitimization_check ( } +/** + * Type of function called after AML program was run. + * + * @param cls closure + * @param apr result of the AML program. + */ +static void +legi_check_aml_program_cb ( + void *cls, + const struct TALER_KYCLOGIC_AmlProgramResult *apr) +{ + struct TEH_LegitimizationCheckHandle *lch = cls; + + lch->aprh = NULL; + switch (apr->status) + { + case TALER_KYCLOGIC_AMLR_SUCCESS: + { + enum GNUNET_DB_QueryStatus qs; + struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs; + struct GNUNET_TIME_Timestamp expiration_time; + + /* persist outcome of AML program */ + lrs = TALER_KYCLOGIC_rules_parse ( + apr->details.success.new_rules); + if (NULL == lrs) + { + GNUNET_break (0); + legi_fail ( + lch, + TALER_EC_EXCHANGE_KYC_AML_PROGRAM_FAILURE, + lch->aml_program); + return; + } + expiration_time + = TALER_KYCLOGIC_rules_get_expiration (lrs); + TALER_KYCLOGIC_rules_free (lrs); + qs = TEH_plugin->insert_programmatic_legitimization_outcome ( + TEH_plugin->cls, + &lch->h_payto, + GNUNET_TIME_timestamp_get (), + expiration_time.abs_time, + apr->details.success.account_properties, + apr->details.success.to_investigate, + apr->details.success.new_rules, + apr->details.success.num_events, + apr->details.success.events); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + legi_fail (lch, + TALER_EC_GENERIC_DB_STORE_FAILED, + "insert_programmatic_aml_decision"); + return; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + GNUNET_break (0); + legi_fail (lch, + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, + "insert_programmatic_aml_decision"); + return; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + break; + } + /* Now run with new_rules! */ + GNUNET_free (lch->aml_program); + legitimization_check_run (lch); + return; + } + case TALER_KYCLOGIC_AMLR_FAILURE: + GNUNET_break (0); + legi_fail (lch, + TALER_EC_EXCHANGE_KYC_AML_PROGRAM_FAILURE, + lch->aml_program); + return; + } + GNUNET_break (0); +} + + +/** + * We need to run the check from @a kcc for @a lch. + * + * @param lch legitimization process handle + * @param kcc check to trigger + */ +static void +run_check ( + struct TEH_LegitimizationCheckHandle *lch, + const struct TALER_KYCLOGIC_KycCheckContext *kcc) +{ + json_t *jmeasures; + + jmeasures + = TALER_KYCLOGIC_check_to_measures (kcc); + if (NULL == kcc->check) + { + /* check was skip; directly run AML program */ + enum GNUNET_DB_QueryStatus qs; + json_t *attributes; + json_t *aml_history; + json_t *kyc_history; + + aml_history = json_array (); + kyc_history = json_array (); + qs = TEH_plugin->lookup_aml_history ( + TEH_plugin->cls, + &lch->h_payto, + &add_aml_history_entry, + aml_history); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + json_decref (aml_history); + json_decref (kyc_history); + legi_fail (lch, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "lookup_aml_history"); + return; + 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, + &lch->h_payto, + &add_kyc_history_entry, + kyc_history); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + json_decref (aml_history); + json_decref (kyc_history); + legi_fail (lch, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "lookup_kyc_history"); + return; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + /* empty history is fine! */ + break; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + break; + } + + attributes + = json_object (); /* instant: empty attributes */ + GNUNET_assert (NULL != attributes); + lch->aml_program + = GNUNET_strdup (kcc->prog_name); + lch->aprh + = TALER_KYCLOGIC_run_aml_program ( + attributes, + aml_history, + kyc_history, + jmeasures, + 0, /* measure index */ + &legi_check_aml_program_cb, + lch); + json_decref (aml_history); + json_decref (kyc_history); + json_decref (attributes); + if (NULL == lch->aprh) + { + GNUNET_break (0); + legi_fail (lch, + TALER_EC_EXCHANGE_KYC_AML_PROGRAM_FAILURE, + NULL); + goto cleanup; + } + } + else + { + enum GNUNET_DB_QueryStatus qs; + + /* require kcc.check! */ + qs = TEH_plugin->trigger_kyc_rule_for_account ( + TEH_plugin->cls, + lch->payto_uri, + &lch->h_payto, + lch->have_account_pub ? &lch->account_pub : NULL, + jmeasures, + 0, /* no particular priority */ + &lch->lcr.kyc.requirement_row); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + legi_fail (lch, + TALER_EC_GENERIC_DB_STORE_FAILED, + "trigger_kyc_rule_for_account"); + goto cleanup; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + GNUNET_break (0); + legi_fail (lch, + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, + "trigger_kyc_rule_for_account"); + goto cleanup; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + break; + } + /* return success! */ + lch->async_task + = GNUNET_SCHEDULER_add_now ( + &async_return_legi_result, + lch); + } +cleanup: + json_decref (jmeasures); +} + + static void legitimization_check_run ( struct TEH_LegitimizationCheckHandle *lch) @@ -1230,6 +1460,38 @@ legitimization_check_run ( } } + if (NULL != lrs) + { + struct GNUNET_TIME_Timestamp ts; + + ts = TALER_KYCLOGIC_rules_get_expiration (lrs); + if (GNUNET_TIME_absolute_is_past (ts.abs_time)) + { + const char *successor; + struct TALER_KYCLOGIC_KycCheckContext kcc; + + successor + = TALER_KYCLOGIC_rules_get_successor (lrs); + if (GNUNET_OK != + TALER_KYCLOGIC_requirements_to_check (lrs, + NULL, + successor, + &kcc)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Successor measure `%s' unknown, falling back to default rules!\n", + successor); + TALER_KYCLOGIC_rules_free (lrs); + lrs = NULL; + } + else + { + run_check (lch, + &kcc); + } + } + } + qs = TALER_KYCLOGIC_kyc_test_required ( lch->et, lrs, @@ -1260,7 +1522,6 @@ legitimization_check_run ( GNUNET_log (GNUNET_ERROR_TYPE_INFO, "KYC requirement is %s\n", TALER_KYCLOGIC_rule2s (requirement)); - instant_ms = TALER_KYCLOGIC_rule_get_instant_measure ( requirement); @@ -1357,11 +1618,17 @@ TEH_legitimization_check_cancel ( TEH_kyc_finished_cancel (lch->kat); lch->kat = NULL; } + if (NULL != lch->aprh) + { + TALER_KYCLOGIC_run_aml_program_cancel (lch->aprh); + lch->aprh = NULL; + } if (NULL != lch->lcr.response) { MHD_destroy_response (lch->lcr.response); lch->lcr.response = NULL; } GNUNET_free (lch->payto_uri); + GNUNET_free (lch->aml_program); GNUNET_free (lch); } diff --git a/src/exchangedb/Makefile.am b/src/exchangedb/Makefile.am index 5bfa7a9a9..1407c2f8b 100644 --- a/src/exchangedb/Makefile.am +++ b/src/exchangedb/Makefile.am @@ -127,6 +127,7 @@ libtaler_plugin_exchangedb_postgres_la_SOURCES = \ pg_insert_drain_profit.h pg_insert_drain_profit.c \ pg_insert_kyc_failure.h pg_insert_kyc_failure.c \ pg_inject_auditor_triggers.h pg_inject_auditor_triggers.c \ + pg_insert_programmatic_legitimization_outcome.h pg_insert_programmatic_legitimization_outcome.c \ pg_create_tables.h pg_create_tables.c \ pg_event_listen.h pg_event_listen.c \ pg_event_listen_cancel.h pg_event_listen_cancel.c \ diff --git a/src/exchangedb/exchange_do_insert_aml_decision.sql b/src/exchangedb/exchange_do_insert_aml_decision.sql index 360444d26..cf5ff530e 100644 --- a/src/exchangedb/exchange_do_insert_aml_decision.sql +++ b/src/exchangedb/exchange_do_insert_aml_decision.sql @@ -188,9 +188,10 @@ INSERT INTO kyc_alerts (h_payto ,trigger_type) VALUES - (in_h_payto,1); + (in_h_payto,1) + ON CONFLICT DO NOTHING; - EXECUTE FORMAT ( +EXECUTE FORMAT ( 'NOTIFY %s' ,in_notify_s); diff --git a/src/exchangedb/exchange_do_insert_programmatic_legitimization_outcome.sql b/src/exchangedb/exchange_do_insert_programmatic_legitimization_outcome.sql new file mode 100644 index 000000000..196632b44 --- /dev/null +++ b/src/exchangedb/exchange_do_insert_programmatic_legitimization_outcome.sql @@ -0,0 +1,189 @@ +-- +-- This file is part of TALER +-- Copyright (C) 2023, 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/> +-- + +DROP FUNCTION IF EXISTS exchange_do_insert_programmatic_legitimization_outcome; +CREATE OR REPLACE FUNCTION exchange_do_insert_programmatic_legitimization_outcome( + IN in_h_payto BYTEA, + IN in_decision_time INT8, + IN in_expiration_time INT8, + IN in_properties TEXT, + IN in_new_rules TEXT, + IN in_to_investigate BOOLEAN, + IN ina_events TEXT[], + IN in_notify_s TEXT, + OUT out_account_unknown BOOLEAN) +LANGUAGE plpgsql +AS $$ +DECLARE + my_outcome_serial_id INT8; + my_access_token BYTEA; + my_max_dp INT4; + my_i INT4; + ini_event TEXT; +BEGIN + +out_account_unknown=FALSE; + +-- Note: in_payto_uri is allowed to be NULL *if* +-- in_h_payto is already in wire_targets +SELECT access_token + INTO my_access_token + FROM wire_targets + WHERE wire_target_h_payto=in_h_payto; + +-- Very strange, should never happen that we +-- take an AML decision on an unknown account! +IF NOT FOUND +THEN + out_account_unknown=TRUE; + RETURN; +END IF; +out_account_unknown=FALSE; + +-- Did KYC measures get prescribed? +IF in_jmeasures IS NULL +THEN + -- AML decision without measure: mark all + -- active measures finished! + UPDATE legitimization_measures + SET is_finished=TRUE + WHERE access_token=my_access_token + AND NOT is_finished; +ELSE + -- Find current maximum DP + SELECT COALESCE(MAX(display_priority),0) + INTO my_max_dp + FROM legitimization_measures + WHERE access_token=my_access_token + AND NOT is_finished; + + -- First check if a perfectly equivalent legi measure + -- already exists, to avoid creating tons of duplicates. + UPDATE legitimization_measures + SET display_priority=GREATEST(my_max_dp,display_priority) + WHERE access_token=my_access_token + AND jmeasures=in_jmeasures + AND NOT is_finished; + + IF NOT FOUND + THEN + -- Enable new legitimization measure + INSERT INTO legitimization_measures + (access_token + ,start_time + ,jmeasures + ,display_priority) + VALUES + (my_access_token + ,in_decision_time + ,in_jmeasures + ,my_max_dp + 1); + END IF; + + -- end if for where we had non-NULL in_jmeasures +END IF; + +UPDATE legitimization_outcomes + SET is_active=FALSE + WHERE h_payto=in_h_payto + -- this clause is a minor optimization to avoid + -- updating outcomes that have long expired. + AND expiration_time >= in_decision_time; + +INSERT INTO legitimization_outcomes + (h_payto + ,decision_time + ,expiration_time + ,jproperties + ,new_measure_name + ,to_investigate + ,jnew_rules + ) + VALUES + (in_h_payto + ,in_decision_time + ,in_expiration_time + ,in_properties + ,in_new_measure_name + ,in_to_investigate + ,in_new_rules + ) + RETURNING + outcome_serial_id + INTO + my_outcome_serial_id; + +-- FIXME: do we want/need programmatic +-- decisions in the AML history? We +-- have no justification or decider, +-- so IF we would need to change the table +-- significantly, which may break other things... + +--INSERT INTO aml_history +-- (h_payto +-- ,outcome_serial_id +-- ,justification +-- ,decider_pub +-- ,decider_sig +-- ) VALUES +-- (in_h_payto +-- ,my_outcome_serial_id +-- ,in_justification +-- ,in_decider_pub +-- ,in_decider_sig +-- ); + +-- wake up taler-exchange-aggregator +INSERT INTO kyc_alerts + (h_payto + ,trigger_type) + VALUES + (in_h_payto,1) + ON CONFLICT DO NOTHING; + +IF in_to_investigate +THEN + INSERT INTO aml_status + (h_payto + ,status) + VALUES + (in_h_payto + ,1) + ON CONFLICT (h_payto) DO + UPDATE SET status=EXCLUDED.status | 1; +END IF; + + +FOR i IN 1..COALESCE(array_length(ina_events,1),0) +LOOP + ini_event = ina_events[i]; + INSERT INTO kyc_events + (event_timestamp + ,event_type) + VALUES + (in_collection_time_ts + ,ini_event); +END LOOP; + +EXECUTE FORMAT ( + 'NOTIFY %s' + ,in_notify_s); + +END $$; + + +COMMENT ON FUNCTION exchange_do_insert_programmatic_legitimization_outcome(BYTEA, INT8, INT8, TEXT, TEXT, BOOLEAN, TEXT[], TEXT) + IS 'Inserts an AML decision that was taken automatically by an AML program into the database'; diff --git a/src/exchangedb/pg_insert_programmatic_legitimization_outcome.c b/src/exchangedb/pg_insert_programmatic_legitimization_outcome.c new file mode 100644 index 000000000..1e2b0ac15 --- /dev/null +++ b/src/exchangedb/pg_insert_programmatic_legitimization_outcome.c @@ -0,0 +1,92 @@ +/* + 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_insert_programmatic_legitimization_outcome.c + * @brief Implementation of the insert_programmatic_legitimization_outcome 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_insert_programmatic_legitimization_outcome.h" +#include "pg_helper.h" + + +enum GNUNET_DB_QueryStatus +TEH_PG_insert_programmatic_legitimization_outcome ( + void *cls, + const struct TALER_PaytoHashP *h_payto, + struct GNUNET_TIME_Timestamp decision_time, + struct GNUNET_TIME_Absolute expiration_time, + const json_t *account_properties, + bool to_investigate, + const json_t *new_rules, + unsigned int num_events, + const char **events) +{ + struct PostgresClosure *pg = cls; + struct TALER_KycCompletedEventP rep = { + .header.size = htons (sizeof (rep)), + .header.type = htons (TALER_DBEVENT_EXCHANGE_KYC_COMPLETED), + .h_payto = *h_payto + }; + + char *notify_s + = GNUNET_PQ_get_event_notify_channel (&rep.header); + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (h_payto), + GNUNET_PQ_query_param_timestamp (&decision_time), + GNUNET_PQ_query_param_absolute_time (&expiration_time), + NULL != account_properties + ? TALER_PQ_query_param_json (account_properties) + : GNUNET_PQ_query_param_null (), + TALER_PQ_query_param_json (new_rules), + GNUNET_PQ_query_param_bool (to_investigate), + GNUNET_PQ_query_param_array_ptrs_string (num_events, + events, + pg->conn), + GNUNET_PQ_query_param_string (notify_s), + GNUNET_PQ_query_param_end + }; + bool unknown_account; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_bool ("out_account_unknown", + &unknown_account), + GNUNET_PQ_result_spec_end + }; + enum GNUNET_DB_QueryStatus qs; + + PREPARE (pg, + "do_insert_programmatic_legitimization_outcome", + "SELECT" + " out_account_unknown" + " FROM exchange_do_insert_programmatic_legitimization_decision" + "($1, $2, $3, $4, $5, $6, $7, $8);"); + qs = GNUNET_PQ_eval_prepared_singleton_select ( + pg->conn, + "do_insert_programmatic_legitimization_outcome", + params, + rs); + GNUNET_PQ_cleanup_query_params_closures (params); + GNUNET_free (notify_s); + GNUNET_PQ_event_do_poll (pg->conn); + if (qs <= 0) + return qs; + if (unknown_account) + return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; + return qs; +} diff --git a/src/exchangedb/pg_insert_programmatic_legitimization_outcome.h b/src/exchangedb/pg_insert_programmatic_legitimization_outcome.h new file mode 100644 index 000000000..82b78c9b5 --- /dev/null +++ b/src/exchangedb/pg_insert_programmatic_legitimization_outcome.h @@ -0,0 +1,56 @@ +/* + 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_insert_programmatic_legitimization_outcome.h + * @brief implementation of the insert_programmatic_legitimization_outcome function for Postgres + * @author Christian Grothoff + */ +#ifndef PG_INSERT_PROGRAMMATIC_LEGITIMIZATION_OUTCOME_H +#define PG_INSERT_PROGRAMMATIC_LEGITIMIZATION_OUTCOME_H + +#include "taler_util.h" +#include "taler_json_lib.h" +#include "taler_exchangedb_plugin.h" + + +/** + * Store automated legitimization outcome. + * + * @param cls closure + * @param h_payto account for which the attribute data is stored + * @param decision_time when was the decision taken + * @param expiration_time when does the data expire + * @param account_properties new account properties + * @param to_investigate true to flag account for investigation + * @param new_rules new KYC rules to apply to the account + * @param num_events length of the @a events array + * @param events array of KYC events to trigger + * @param require_aml true to trigger AML + * @return database transaction status + */ +enum GNUNET_DB_QueryStatus +TEH_PG_insert_programmatic_legitimization_outcome ( + void *cls, + const struct TALER_PaytoHashP *h_payto, + struct GNUNET_TIME_Timestamp decision_time, + struct GNUNET_TIME_Absolute expiration_time, + const json_t *account_properties, + bool to_investigate, + const json_t *new_rules, + unsigned int num_events, + const char **events); + +#endif diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c index 87414125e..dee515a0c 100644 --- a/src/exchangedb/plugin_exchangedb_postgres.c +++ b/src/exchangedb/plugin_exchangedb_postgres.c @@ -47,6 +47,7 @@ #include "pg_get_unfinished_close_requests.h" #include "pg_insert_close_request.h" #include "pg_insert_records_by_table.h" +#include "pg_insert_programmatic_legitimization_outcome.h" #include "pg_insert_reserve_open_deposit.h" #include "pg_get_pending_kyc_requirement_process.h" #include "pg_iterate_kyc_reference.h" @@ -774,6 +775,8 @@ libtaler_plugin_exchangedb_postgres_init (void *cls) = &TEH_PG_abort_shard; plugin->insert_kyc_failure = &TEH_PG_insert_kyc_failure; + plugin->insert_programmatic_legitimization_outcome + = &TEH_PG_insert_programmatic_legitimization_outcome; plugin->complete_shard = &TEH_PG_complete_shard; plugin->release_revolving_shard diff --git a/src/exchangedb/procedures.sql.in b/src/exchangedb/procedures.sql.in index a92ae77d6..99b932951 100644 --- a/src/exchangedb/procedures.sql.in +++ b/src/exchangedb/procedures.sql.in @@ -52,5 +52,6 @@ SET search_path TO exchange; #include "exchange_do_kycauth_in_insert.sql" #include "exchange_do_trigger_kyc_rule_for_account.sql" #include "exchange_do_lookup_kyc_requirement_by_row.sql" +#include "exchange_do_insert_programmatic_legitimization_outcome.sql" COMMIT; diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index aa0339d0b..492823ab8 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -7159,6 +7159,34 @@ struct TALER_EXCHANGEDB_Plugin /** + * Store automated legitimization outcome. + * + * @param cls closure + * @param h_payto account for which the attribute data is stored + * @param decision_time when was the decision taken + * @param expiration_time when does the data expire + * @param account_properties new account properties + * @param to_investigate true to flag account for investigation + * @param new_rules new KYC rules to apply to the account + * @param num_events length of the @a events array + * @param events array of KYC events to trigger + * @param require_aml true to trigger AML + * @return database transaction status + */ + enum GNUNET_DB_QueryStatus + (*insert_programmatic_legitimization_outcome)( + void *cls, + const struct TALER_PaytoHashP *h_payto, + struct GNUNET_TIME_Timestamp decision_time, + struct GNUNET_TIME_Absolute expiration_time, + const json_t *account_properties, + bool to_investigate, + const json_t *new_rules, + unsigned int num_events, + const char **events); + + + /** * Store KYC attribute data, update KYC process status and * AML status for the given account. * @@ -7178,7 +7206,6 @@ struct TALER_EXCHANGEDB_Plugin * @param events array of KYC events to trigger * @param enc_attributes_size number of bytes in @a enc_attributes * @param enc_attributes encrypted attribute data - * @param require_aml true to trigger AML * @return database transaction status */ enum GNUNET_DB_QueryStatus diff --git a/src/include/taler_kyclogic_lib.h b/src/include/taler_kyclogic_lib.h index f7fb9ed92..9dbcf92fd 100644 --- a/src/include/taler_kyclogic_lib.h +++ b/src/include/taler_kyclogic_lib.h @@ -597,7 +597,8 @@ TALER_KYCLOGIC_get_original_measure ( * no trigger has been hit. * * @param lrs rule set - * @param kyc_rule rule that was triggered + * @param kyc_rule rule that was triggered, NULL + * to merely lookup the measure without any trigger * @param measure_name selected measure, * NULL to return the "new_check" set by the @a lrs * @param[out] kcc set to check to run; diff --git a/src/kyclogic/kyclogic_api.c b/src/kyclogic/kyclogic_api.c index 56affd010..7c52b93c3 100644 --- a/src/kyclogic/kyclogic_api.c +++ b/src/kyclogic/kyclogic_api.c @@ -857,9 +857,10 @@ TALER_KYCLOGIC_rules_to_limits (const json_t *jrules) * @param measure_name name of measure to find * @return NULL if not found, otherwise the measure */ -static const struct TALER_KYCLOGIC_Measure * -find_measure (const struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs, - const char *measure_name) +const struct TALER_KYCLOGIC_Measure * +find_measure ( + const struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs, + const char *measure_name) { if (NULL != lrs) { @@ -1033,8 +1034,6 @@ TALER_KYCLOGIC_check_to_measures ( 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), @@ -1043,6 +1042,8 @@ TALER_KYCLOGIC_check_to_measures ( GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_object_incref ("context", (json_t *) kcc->context))); + jmeasures = json_array (); + GNUNET_assert (NULL != jmeasures); GNUNET_assert (0 == json_array_append_new (jmeasures, mi)); @@ -2500,7 +2501,6 @@ TALER_KYCLOGIC_get_original_measure ( } -// FIXME: not used in 'main' exchange logic! enum GNUNET_GenericReturnValue TALER_KYCLOGIC_requirements_to_check ( const struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs, @@ -2518,27 +2518,30 @@ TALER_KYCLOGIC_requirements_to_check ( GNUNET_break (0); return GNUNET_SYSERR; } - for (unsigned int i = 0; i<kyc_rule->num_measures; i++) - { - if (0 != strcmp (measure_name, - kyc_rule->next_measures[i])) - continue; - found = true; - break; - } - if (! found) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Measure `%s' not allowed for rule `%s'\n", - measure_name, - kyc_rule->rule_name); - return GNUNET_SYSERR; - } - if (kyc_rule->verboten) + if (NULL != kyc_rule) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Rule says operation is categorically is verboten, cannot take measures\n"); - return GNUNET_SYSERR; + for (unsigned int i = 0; i<kyc_rule->num_measures; i++) + { + if (0 != strcmp (measure_name, + kyc_rule->next_measures[i])) + continue; + found = true; + break; + } + if (! found) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Measure `%s' not allowed for rule `%s'\n", + measure_name, + kyc_rule->rule_name); + return GNUNET_SYSERR; + } + if (kyc_rule->verboten) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Rule says operation is categorically is verboten, cannot take measures\n"); + return GNUNET_SYSERR; + } } measure = find_measure (lrs, measure_name); @@ -2547,7 +2550,9 @@ TALER_KYCLOGIC_requirements_to_check ( GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Measure `%s' unknown (but allowed by rule `%s')\n", measure_name, - kyc_rule->rule_name); + NULL != kyc_rule + ? kyc_rule->rule_name + : "<NONE>"); return GNUNET_SYSERR; } |