diff options
26 files changed, 764 insertions, 164 deletions
diff --git a/contrib/gana b/contrib/gana -Subproject 7d199a4a0e85592cf0fa4032d9e6a6764e53a59 +Subproject b4e33545ccb3a946a215af8a6566d7b9354bd97 diff --git a/src/auditor/taler-helper-auditor-coins.c b/src/auditor/taler-helper-auditor-coins.c index 70605f29c..6cd3aa243 100644 --- a/src/auditor/taler-helper-auditor-coins.c +++ b/src/auditor/taler-helper-auditor-coins.c @@ -2463,7 +2463,6 @@ recoup_refresh_cb (void *cls, * @param cls closure, pointer to `enum GNUNET_DB_QueryStatus` * @param denom_pub public key, sometimes NULL (!) * @param issue issuing information with value, fees and other info about the denomination. - * @return transaction status */ static void check_denomination ( diff --git a/src/benchmark/taler-aggregator-benchmark.c b/src/benchmark/taler-aggregator-benchmark.c index 228d050e4..fe484e172 100644 --- a/src/benchmark/taler-aggregator-benchmark.c +++ b/src/benchmark/taler-aggregator-benchmark.c @@ -119,7 +119,7 @@ eval_probability (float probability) * @param x pointer to data to randomize */ #define RANDOMIZE(x) \ - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, x, sizeof (*x)) + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, x, sizeof (*x)) /** @@ -342,7 +342,7 @@ add_deposit (const struct Merchant *m) } } if (GNUNET_YES == - eval_probability (((float) refund_rate) / 100.0)) + eval_probability (((float) refund_rate) / 100.0f)) return add_refund (m, &d); return true; diff --git a/src/benchmark/taler-bank-benchmark.c b/src/benchmark/taler-bank-benchmark.c index 528798424..340ee3349 100644 --- a/src/benchmark/taler-bank-benchmark.c +++ b/src/benchmark/taler-bank-benchmark.c @@ -136,7 +136,7 @@ static struct TALER_TESTING_Timer timings[] = { * @param label string to add to the table * @return same string, now stored in the table */ -const char * +static const char * add_label (char *label) { if (label_off == label_len) diff --git a/src/exchange/taler-exchange-httpd_aml-decision.c b/src/exchange/taler-exchange-httpd_aml-decision.c index 64d808896..f0049590e 100644 --- a/src/exchange/taler-exchange-httpd_aml-decision.c +++ b/src/exchange/taler-exchange-httpd_aml-decision.c @@ -40,7 +40,7 @@ TEH_handler_post_aml_decision ( { struct MHD_Connection *connection = rc->connection; const char *justification; - const char *new_measure = NULL; + const char *new_measures = NULL; bool to_investigate; struct GNUNET_TIME_Timestamp decision_time; const json_t *new_rules; @@ -50,8 +50,8 @@ TEH_handler_post_aml_decision ( struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_string ( - "new_measure", - &new_measure), + "new_measures", + &new_measures), NULL), GNUNET_JSON_spec_string ("justification", &justification), @@ -96,7 +96,7 @@ TEH_handler_post_aml_decision ( &h_payto, new_rules, properties, - new_measure, + new_measures, to_investigate, officer_pub, &officer_sig)) @@ -123,10 +123,11 @@ TEH_handler_post_aml_decision ( "legitimization rule malformed"); } expiration_time = TALER_KYCLOGIC_rules_get_expiration (lrs); - if (NULL != new_measure) + if (NULL != new_measures) { - jmeasures = TALER_KYCLOGIC_get_measure (lrs, - new_measure); + jmeasures + = TALER_KYCLOGIC_get_measures (lrs, + new_measures); if (NULL == jmeasures) { GNUNET_break_op (0); @@ -136,7 +137,7 @@ TEH_handler_post_aml_decision ( connection, MHD_HTTP_BAD_REQUEST, TALER_EC_GENERIC_PARAMETER_MALFORMED, - "new_measure/new_rules"); + "new_measures/new_rules"); } } TALER_KYCLOGIC_rules_free (lrs); @@ -148,6 +149,8 @@ TEH_handler_post_aml_decision ( bool invalid_officer = true; bool unknown_account = false; + /* We keep 'new_measures' around mostly so that + the auditor can later verify officer_sig */ qs = TEH_plugin->insert_aml_decision (TEH_plugin->cls, &h_payto, decision_time, @@ -155,7 +158,7 @@ TEH_handler_post_aml_decision ( properties, new_rules, to_investigate, - new_measure, + new_measures, jmeasures, justification, officer_pub, @@ -202,8 +205,6 @@ TEH_handler_post_aml_decision ( TALER_EC_EXCHANGE_AML_DECISION_MORE_RECENT_PRESENT, NULL); } - - } return TALER_MHD_reply_static ( connection, diff --git a/src/exchange/taler-exchange-httpd_kyc-info.c b/src/exchange/taler-exchange-httpd_kyc-info.c index b2b4a0d8d..1c7b31be0 100644 --- a/src/exchange/taler-exchange-httpd_kyc-info.c +++ b/src/exchange/taler-exchange-httpd_kyc-info.c @@ -64,7 +64,13 @@ struct KycPoller * #MHD_HTTP_HEADER_IF_NONE_MATCH Etag value sent by the client. 0 for none * (or malformed). */ - uint64_t etag_in; + uint64_t etag_outcome_in; + + /** + * #MHD_HTTP_HEADER_IF_NONE_MATCH Etag value sent by the client. 0 for none + * (or malformed). + */ + uint64_t etag_measure_in; /** * When will this request time out? @@ -78,6 +84,11 @@ struct KycPoller struct TALER_AccountAccessTokenP access_token; /** + * Payto hash of the account matching @a access_token. + */ + struct TALER_PaytoHashP h_payto; + + /** * True if we are still suspended. */ bool suspended; @@ -198,14 +209,18 @@ add_response_headers (void *cls, * the LegitimizationMeasures. * * @param[in,out] kyp request to reply on - * @param legitimization_measure_row_id etag to set for the response + * @param legitimization_measure_row_id part of etag to set for the response + * @param legitimization_outcome_row_id part of etag to set for the response * @param jmeasures measures to encode + * @param jvoluntary array of voluntary measures to encode, can be NULL * @return MHD status code */ static MHD_RESULT generate_reply (struct KycPoller *kyp, uint64_t legitimization_measure_row_id, - const json_t *jmeasures) + uint64_t legitimization_outcome_row_id, + const json_t *jmeasures, + const json_t *jvoluntary) { const json_t *measures; bool is_and_combinator = false; @@ -289,23 +304,24 @@ generate_reply (struct KycPoller *kyp, } { - char etags[64]; + char etags[128]; struct MHD_Response *resp; MHD_RESULT res; GNUNET_snprintf (etags, sizeof (etags), - "\"%llu\"", - (unsigned long long) legitimization_measure_row_id); + "\"%llu-%llu\"", + (unsigned long long) legitimization_measure_row_id, + (unsigned long long) legitimization_outcome_row_id); resp = TALER_MHD_MAKE_JSON_PACK ( GNUNET_JSON_pack_array_steal ("requirements", kris), GNUNET_JSON_pack_bool ("is_and_combinator", is_and_combinator), GNUNET_JSON_pack_allow_null ( - /* TODO: support vATTEST-9048 */ - GNUNET_JSON_pack_object_steal ("voluntary_checks", - NULL))); + GNUNET_JSON_pack_array_incref ( + "voluntary_measures", + (json_t *) jvoluntary))); GNUNET_break (MHD_YES == MHD_add_response_header (resp, MHD_HTTP_HEADER_ETAG, @@ -331,7 +347,11 @@ TEH_handler_kyc_info ( MHD_RESULT res; enum GNUNET_DB_QueryStatus qs; uint64_t legitimization_measure_last_row; - json_t *jmeasures; + uint64_t legitimization_outcome_last_row; + json_t *jmeasures = NULL; + json_t *jvoluntary = NULL; + json_t *jnew_rules; + struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs; if (NULL == kyp) { @@ -368,11 +388,13 @@ TEH_handler_kyc_info ( if (NULL != etags) { char dummy; - unsigned long long ev; + unsigned long long ev1; + unsigned long long ev2; - if (1 != sscanf (etags, - "\"%llu\"%c", - &ev, + if (2 != sscanf (etags, + "\"%llu-%llu\"%c", + &ev1, + &ev2, &dummy)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, @@ -382,24 +404,17 @@ TEH_handler_kyc_info ( } else { - kyp->etag_in = (uint64_t) ev; + kyp->etag_measure_in = (uint64_t) ev1; + kyp->etag_outcome_in = (uint64_t) ev2; } } } /* etag */ - } /* one-time initialization */ - - if ( (NULL == kyp->eh) && - GNUNET_TIME_absolute_is_future (kyp->timeout) ) - { - struct TALER_KycCompletedEventP rep = { - .header.size = htons (sizeof (rep)), - .header.type = htons (TALER_DBEVENT_EXCHANGE_KYC_COMPLETED) - }; + /* Check access token */ qs = TEH_plugin->lookup_h_payto_by_access_token ( TEH_plugin->cls, &kyp->access_token, - &rep.h_payto); + &kyp->h_payto); if (qs < 0) { GNUNET_break (0); @@ -408,16 +423,63 @@ TEH_handler_kyc_info ( TALER_EC_GENERIC_DB_FETCH_FAILED, "lookup_h_payto_by_access_token"); } + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) + { + GNUNET_break_op (0); + return TALER_MHD_REPLY_JSON_PACK ( + rc->connection, + MHD_HTTP_FORBIDDEN, + TALER_JSON_pack_ec ( + TALER_EC_EXCHANGE_KYC_INFO_AUTHORIZATION_FAILED)); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Starting DB event listening\n"); - kyp->eh = TEH_plugin->event_listen ( - TEH_plugin->cls, - GNUNET_TIME_absolute_get_remaining (kyp->timeout), - &rep.header, - &db_event_cb, - rc); + } + + if (GNUNET_TIME_absolute_is_future (kyp->timeout)) + { + struct TALER_KycCompletedEventP rep = { + .header.size = htons (sizeof (rep)), + .header.type = htons (TALER_DBEVENT_EXCHANGE_KYC_COMPLETED), + .h_payto = kyp->h_payto + }; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Starting DB event listening\n"); + kyp->eh = TEH_plugin->event_listen ( + TEH_plugin->cls, + GNUNET_TIME_absolute_get_remaining (kyp->timeout), + &rep.header, + &db_event_cb, + rc); + } + } /* end of one-time initialization */ + + qs = TEH_plugin->lookup_rules_by_access_token ( + TEH_plugin->cls, + &kyp->h_payto, + &jnew_rules, + &legitimization_outcome_last_row); + if (qs < 0) + { + GNUNET_break (0); + return TALER_MHD_reply_with_ec ( + rc->connection, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "lookup_rules_by_access_token"); + } + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) + { + /* Nothing was triggered, return the measures + that apply for any amount. */ + lrs = NULL; + } + else + { + lrs = TALER_KYCLOGIC_rules_parse (jnew_rules); + GNUNET_break (NULL != lrs); + json_decref (jnew_rules); } + jvoluntary + = TALER_KYCLOGIC_voluntary_measures (lrs); qs = TEH_plugin->lookup_kyc_status_by_token ( TEH_plugin->cls, @@ -427,6 +489,7 @@ TEH_handler_kyc_info ( if (qs < 0) { GNUNET_break (0); + TALER_KYCLOGIC_rules_free (lrs); return TALER_MHD_reply_with_ec ( rc->connection, TALER_EC_GENERIC_DB_FETCH_FAILED, @@ -434,17 +497,39 @@ TEH_handler_kyc_info ( } if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "No KYC requirement open\n"); - return TALER_MHD_REPLY_JSON_PACK ( - rc->connection, - MHD_HTTP_OK, - GNUNET_JSON_pack_allow_null ( - /* TODO: support vATTEST-9048 */ - GNUNET_JSON_pack_object_steal ("voluntary_checks", - NULL))); + jmeasures + = TALER_KYCLOGIC_zero_measures (lrs); + if (NULL == jmeasures) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "No KYC requirement open\n"); + TALER_KYCLOGIC_rules_free (lrs); + return TALER_MHD_REPLY_JSON_PACK ( + rc->connection, + MHD_HTTP_OK, + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_array_steal ("voluntary_measures", + jvoluntary))); + } + + qs = TEH_plugin->insert_active_legitimization_measure ( + TEH_plugin->cls, + &kyp->access_token, + jmeasures, + &legitimization_measure_last_row); + if (qs < 0) + { + GNUNET_break (0); + TALER_KYCLOGIC_rules_free (lrs); + return TALER_MHD_reply_with_ec ( + rc->connection, + TALER_EC_GENERIC_DB_STORE_FAILED, + "insert_active_legitimization_measure"); + } } - if ( (legitimization_measure_last_row == kyp->etag_in) && + TALER_KYCLOGIC_rules_free (lrs); + if ( (legitimization_measure_last_row == kyp->etag_measure_in) && + (legitimization_outcome_last_row == kyp->etag_outcome_in) && GNUNET_TIME_absolute_is_future (kyp->timeout) ) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, @@ -461,15 +546,20 @@ TEH_handler_kyc_info ( MHD_suspend_connection (rc->connection); return MHD_YES; } - if (legitimization_measure_last_row == kyp->etag_in) + if ( (legitimization_measure_last_row == + kyp->etag_measure_in) && + (legitimization_outcome_last_row == + kyp->etag_outcome_in) ) { - char etags[64]; + char etags[128]; json_decref (jmeasures); + json_decref (jvoluntary); GNUNET_snprintf (etags, sizeof (etags), - "\"%llu\"", - (unsigned long long) legitimization_measure_last_row); + "\"%llu-%llu\"", + (unsigned long long) legitimization_measure_last_row, + (unsigned long long) legitimization_outcome_last_row); return TEH_RESPONSE_reply_not_modified ( rc->connection, etags, @@ -478,8 +568,11 @@ TEH_handler_kyc_info ( } res = generate_reply (kyp, legitimization_measure_last_row, - jmeasures); + legitimization_outcome_last_row, + jmeasures, + jvoluntary); json_decref (jmeasures); + json_decref (jvoluntary); return res; } diff --git a/src/exchangedb/0005-legitimization_measures.sql b/src/exchangedb/0005-legitimization_measures.sql index ce4b4e23c..189e71e1f 100644 --- a/src/exchangedb/0005-legitimization_measures.sql +++ b/src/exchangedb/0005-legitimization_measures.sql @@ -27,7 +27,7 @@ BEGIN ',access_token BYTEA NOT NULL CHECK (LENGTH(access_token)=32)' ',start_time INT8 NOT NULL' ',jmeasures TEXT NOT NULL' - ',display_priority INT4 NOT NULL' + ',display_priority INT4 NOT NULL' -- DEAD? ',is_finished BOOL NOT NULL DEFAULT(FALSE)' ') %s ;' ,'legitimization_measures' @@ -64,7 +64,7 @@ BEGIN ,partition_suffix ); PERFORM comment_partitioned_column( - 'Display priority of the rule that triggered this measure; if in the meantime another rule also triggers, the measure is only replaced if the new rule has a higher display priority' + 'Display priority of the rule that triggered this measure; if in the meantime another rule also triggers, the measure is only replaced if the new rule has a higher display priority; probably not really useful, as right now there is only ever one set of legitimization_measures active at any time, might be removed in the future' ,'display_priority' ,'legitimization_measures' ,partition_suffix diff --git a/src/exchangedb/0005-legitimization_outcomes.sql b/src/exchangedb/0005-legitimization_outcomes.sql index 1c7405c99..d132b682f 100644 --- a/src/exchangedb/0005-legitimization_outcomes.sql +++ b/src/exchangedb/0005-legitimization_outcomes.sql @@ -61,7 +61,7 @@ BEGIN ,partition_suffix ); PERFORM comment_partitioned_column( - 'name of the measure to trigger immediately, NULL for none' + 'space-separated list of names of measures to trigger immediately, NULL for none, prefixed with a "+" to indicate AND combination for the measures' ,'new_measure_name' ,'legitimization_outcomes' ,partition_suffix diff --git a/src/exchangedb/Makefile.am b/src/exchangedb/Makefile.am index 724370efb..5472f41f5 100644 --- a/src/exchangedb/Makefile.am +++ b/src/exchangedb/Makefile.am @@ -149,6 +149,7 @@ libtaler_plugin_exchangedb_postgres_la_SOURCES = \ pg_iterate_active_auditors.h pg_iterate_active_auditors.c \ pg_iterate_auditor_denominations.h pg_iterate_auditor_denominations.c \ pg_reserves_get.h pg_reserves_get.c \ + pg_lookup_rules_by_access_token.h pg_lookup_rules_by_access_token.c \ pg_reserves_get_origin.h pg_reserves_get_origin.c \ pg_drain_kyc_alert.h pg_drain_kyc_alert.c \ pg_reserves_in_insert.h pg_reserves_in_insert.c \ @@ -211,6 +212,7 @@ libtaler_plugin_exchangedb_postgres_la_SOURCES = \ pg_wire_prepare_data_mark_failed.h pg_wire_prepare_data_mark_failed.c \ pg_wire_prepare_data_get.h pg_wire_prepare_data_get.c \ pg_start_deferred_wire_out.h pg_start_deferred_wire_out.c \ + pg_insert_active_legitimization_measure.h pg_insert_active_legitimization_measure.c \ pg_store_wire_transfer_out.h pg_store_wire_transfer_out.c \ pg_gc.h pg_gc.c \ pg_lookup_kyc_status_by_token.h pg_lookup_kyc_status_by_token.c \ diff --git a/src/exchangedb/exchange_do_insert_active_legitimization_measure.sql b/src/exchangedb/exchange_do_insert_active_legitimization_measure.sql new file mode 100644 index 000000000..649ed4685 --- /dev/null +++ b/src/exchangedb/exchange_do_insert_active_legitimization_measure.sql @@ -0,0 +1,51 @@ +-- +-- 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_active_legitimization_measure; +CREATE FUNCTION exchange_do_insert_active_legitimization_measure( + IN in_access_token BYTEA, + IN in_start_time INT8, + IN in_jmeasures TEXT, + OUT out_legitimization_measure_serial_id INT8) +LANGUAGE plpgsql +AS $$ +BEGIN + +UPDATE legitimization_measures + SET is_finished=TRUE + WHERE access_token=in_access_token + AND NOT is_finished; + +INSERT INTO legitimization_measures + (access_token + ,start_time + ,jmeasures + ,display_priority) + VALUES + (in_access_token + ,in_decision_time + ,in_jmeasures + ,1) + RETURNING + legitimization_measure_serial_id + INTO + out_legitimization_measure_serial_id; + +END $$; + + +COMMENT ON FUNCTION exchange_do_insert_active_legitimization_measure(BYTEA, INT8, TEXT) + IS 'Inserts legitimization measure for an account and marks all existing such measures as inactive'; diff --git a/src/exchangedb/exchange_do_insert_aml_decision.sql b/src/exchangedb/exchange_do_insert_aml_decision.sql index 4ef8dfdec..4c08d5969 100644 --- a/src/exchangedb/exchange_do_insert_aml_decision.sql +++ b/src/exchangedb/exchange_do_insert_aml_decision.sql @@ -36,7 +36,6 @@ AS $$ DECLARE my_outcome_serial_id INT8; my_access_token BYTEA; - my_max_dp INT4; BEGIN out_account_unknown=FALSE; @@ -94,28 +93,19 @@ THEN RETURN; END IF; +-- AML decision: mark all active measures finished! +UPDATE legitimization_measures + SET is_finished=TRUE + WHERE access_token=my_access_token + AND NOT is_finished; + -- Did KYC measures get prescribed? -IF in_jmeasures IS NULL +IF in_jmeasures IS NOT 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) + PERFORM + FROM legitimization_measures WHERE access_token=my_access_token AND jmeasures=in_jmeasures AND NOT is_finished; @@ -132,10 +122,10 @@ ELSE (my_access_token ,in_decision_time ,in_jmeasures - ,my_max_dp + 1); + ,1); END IF; - -- end if for where we had non-NULL in_jmeasures + -- end if for where we had in_jmeasures END IF; UPDATE legitimization_outcomes diff --git a/src/exchangedb/exchange_do_lookup_kyc_requirement_by_row.sql b/src/exchangedb/exchange_do_lookup_kyc_requirement_by_row.sql index cfd4d42d9..e9a22aa84 100644 --- a/src/exchangedb/exchange_do_lookup_kyc_requirement_by_row.sql +++ b/src/exchangedb/exchange_do_lookup_kyc_requirement_by_row.sql @@ -52,17 +52,13 @@ out_account_pub = my_wtrec.target_pub; out_access_token = my_wtrec.access_token; -- Check if there are active measures for the account. -SELECT NOT is_finished - INTO out_kyc_required +PERFORM FROM legitimization_measures WHERE access_token=out_access_token - ORDER BY start_time DESC + AND NOT is_finished LIMIT 1; -IF NOT FOUND -THEN - out_kyc_required=TRUE; -END IF; +out_kyc_required = FOUND; -- Get currently applicable rules. -- Only one should ever be active per account. diff --git a/src/exchangedb/pg_insert_active_legitimization_measure.c b/src/exchangedb/pg_insert_active_legitimization_measure.c new file mode 100644 index 000000000..0e6757b76 --- /dev/null +++ b/src/exchangedb/pg_insert_active_legitimization_measure.c @@ -0,0 +1,62 @@ +/* + 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_active_legitimization_measure.c + * @brief Implementation of the insert_active_legitimization_measure 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_active_legitimization_measure.h" +#include "pg_helper.h" + + +enum GNUNET_DB_QueryStatus +TEH_PG_insert_active_legitimization_measure ( + void *cls, + const struct TALER_AccountAccessTokenP *access_token, + const json_t *jmeasures, + uint64_t *legitimization_measure_serial_id) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_TIME_Timestamp now + = GNUNET_TIME_timestamp_get (); + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (access_token), + GNUNET_PQ_query_param_timestamp (&now), + TALER_PQ_query_param_json (jmeasures), + GNUNET_PQ_query_param_end + }; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_uint64 ("out_legitimization_measure_serial_id", + legitimization_measure_serial_id), + GNUNET_PQ_result_spec_end + }; + + PREPARE (pg, + "do_insert_active_legitimization_measure", + "SELECT" + " out_legitimization_measure_serial_id" + " FROM exchange_do_insert_active_legitimization_measure" + "($1, $2);"); + return GNUNET_PQ_eval_prepared_singleton_select ( + pg->conn, + "do_insert_active_legitimization_measure", + params, + rs); +} diff --git a/src/exchangedb/pg_insert_active_legitimization_measure.h b/src/exchangedb/pg_insert_active_legitimization_measure.h new file mode 100644 index 000000000..09b558d83 --- /dev/null +++ b/src/exchangedb/pg_insert_active_legitimization_measure.h @@ -0,0 +1,48 @@ +/* + 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_active_legitimization_measure.h + * @brief implementation of the insert_active_legitimization_measure function for Postgres + * @author Christian Grothoff + */ +#ifndef PG_INSERT_ACTIVE_LEGITIMIZATION_MEASURE_H +#define PG_INSERT_ACTIVE_LEGITIMIZATION_MEASURE_H + +#include "taler_util.h" +#include "taler_json_lib.h" +#include "taler_exchangedb_plugin.h" + +/** + * Create new active legitimization measure. + * + * + * @param cls closure + * @param access_token access token that identifies the + * account the legitimization measures apply to + * @param jmeasures new legitimization measures + * @param[out] legitimization_measure_serial_id + * set to new row in legitimization_measures table + * @return database transaction status + */ +enum GNUNET_DB_QueryStatus +TEH_PG_insert_active_legitimization_measure ( + void *cls, + const struct TALER_AccountAccessTokenP *access_token, + const json_t *jmeasures, + uint64_t *legitimization_measure_serial_id); + + +#endif diff --git a/src/exchangedb/pg_lookup_rules_by_access_token.c b/src/exchangedb/pg_lookup_rules_by_access_token.c new file mode 100644 index 000000000..0bfdeb5d2 --- /dev/null +++ b/src/exchangedb/pg_lookup_rules_by_access_token.c @@ -0,0 +1,70 @@ +/* + 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_rules_by_access_token.c + * @brief Implementation of the lookup_rules_by_access_token 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_rules_by_access_token.h" +#include "pg_helper.h" + + +enum GNUNET_DB_QueryStatus +TEH_PG_lookup_rules_by_access_token ( + void *cls, + const struct TALER_PaytoHashP *h_payto, + json_t **jnew_rules, + uint64_t *rowid) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_TIME_Absolute now; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (h_payto), + GNUNET_PQ_query_param_absolute_time (&now), + GNUNET_PQ_query_param_end + }; + struct GNUNET_PQ_ResultSpec rs[] = { + TALER_PQ_result_spec_json ( + "jnew_rules", + jnew_rules), + GNUNET_PQ_result_spec_uint64 ( + "row_id", + rowid), + GNUNET_PQ_result_spec_end + }; + + PREPARE (pg, + "lookup_rules_by_access_token", + "SELECT" + " jnew_rules" + ",outcome_serial_id AS row_id" + " FROM legitimization_outcomes" + " WHERE h_payto=$1" + " AND expiration_time>$2" + " AND is_active" + " ORDER BY expiration_time DESC" + " LIMIT 1;"); + now = GNUNET_TIME_absolute_get (); + return GNUNET_PQ_eval_prepared_singleton_select ( + pg->conn, + "lookup_rules_by_access_token", + params, + rs); +} diff --git a/src/exchangedb/pg_lookup_rules_by_access_token.h b/src/exchangedb/pg_lookup_rules_by_access_token.h new file mode 100644 index 000000000..e1824382c --- /dev/null +++ b/src/exchangedb/pg_lookup_rules_by_access_token.h @@ -0,0 +1,44 @@ +/* + 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_rules_by_access_token.h + * @brief implementation of the lookup_rules_by_access_token function for Postgres + * @author Christian Grothoff + */ +#ifndef PG_LOOKUP_RULES_BY_ACCESS_TOKEN_H +#define PG_LOOKUP_RULES_BY_ACCESS_TOKEN_H + +#include "taler_util.h" +#include "taler_json_lib.h" +#include "taler_exchangedb_plugin.h" + +/** + * Lookup KYC rules by account access token. + * + * @param cls closure + * @param h_payto account hash to look under + * @param[out] jnew_rules set to active LegitimizationRuleSet + * @param[out] rowid set to outcome_serial_id of the row + * @return database transaction status + */ +enum GNUNET_DB_QueryStatus +TEH_PG_lookup_rules_by_access_token ( + void *cls, + const struct TALER_PaytoHashP *h_payto, + json_t **jnew_rules, + uint64_t *rowid); + +#endif diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c index 57eb1b028..b5d410fc1 100644 --- a/src/exchangedb/plugin_exchangedb_postgres.c +++ b/src/exchangedb/plugin_exchangedb_postgres.c @@ -42,6 +42,7 @@ #include "pg_do_reserve_open.h" #include "pg_get_coin_transactions.h" #include "pg_get_expired_reserves.h" +#include "pg_lookup_rules_by_access_token.h" #include "pg_lookup_h_payto_by_access_token.h" #include "pg_get_purse_request.h" #include "pg_get_reserve_history.h" @@ -86,6 +87,7 @@ #include "pg_update_kyc_process_by_row.h" #include "pg_insert_kyc_requirement_process.h" #include "pg_select_withdraw_amounts_for_kyc_check.h" +#include "pg_insert_active_legitimization_measure.h" #include "pg_select_merge_amounts_for_kyc_check.h" #include "pg_profit_drains_set_finished.h" #include "pg_profit_drains_get_pending.h" @@ -585,6 +587,8 @@ libtaler_plugin_exchangedb_postgres_init (void *cls) = &TEH_PG_iterate_active_auditors; plugin->iterate_auditor_denominations = &TEH_PG_iterate_auditor_denominations; + plugin->lookup_rules_by_access_token + = &TEH_PG_lookup_rules_by_access_token; plugin->reserves_get = &TEH_PG_reserves_get; plugin->reserves_get_origin @@ -813,6 +817,8 @@ libtaler_plugin_exchangedb_postgres_init (void *cls) = &TEH_PG_test_aml_officer; plugin->lookup_aml_officer = &TEH_PG_lookup_aml_officer; + plugin->insert_active_legitimization_measure + = &TEH_PG_insert_active_legitimization_measure; plugin->trigger_aml_process = &TEH_PG_trigger_aml_process; plugin->insert_aml_decision diff --git a/src/exchangedb/procedures.sql.in b/src/exchangedb/procedures.sql.in index b7c29ae7f..0c25e8f5d 100644 --- a/src/exchangedb/procedures.sql.in +++ b/src/exchangedb/procedures.sql.in @@ -54,6 +54,7 @@ SET search_path TO exchange; #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" +#include "exchange_do_insert_active_legitimization_measure.sql" #include "exchange_do_select_aggregations_above_serial.sql" COMMIT; diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h index f49c0826a..f6907138f 100644 --- a/src/include/taler_exchange_service.h +++ b/src/include/taler_exchange_service.h @@ -6395,8 +6395,10 @@ struct TALER_EXCHANGE_AccountRule * decision is about * @param decision_time when was the decision made * @param successor_measure measure to activate after @a expiration_time if no rule applied - * @param new_check new KYC check to provide to the user, - * NULL for none + * @param new_measures space-separated list of measures + * to trigger immediately; + " "+" prefixed for AND combination; + * NULL for none * @param expiration_time when do the new rules expire * @param num_rules length of the @a rules array * @param rules new rules for the account @@ -6411,13 +6413,13 @@ struct TALER_EXCHANGE_AccountRule * @return the request handle; NULL upon error */ struct TALER_EXCHANGE_AddAmlDecision * -TALER_EXCHANGE_add_aml_decision ( +TALER_EXCHANGE_post_aml_decision ( struct GNUNET_CURL_Context *ctx, const char *url, const struct TALER_PaytoHashP *h_payto, struct GNUNET_TIME_Timestamp decision_time, const char *successor_measure, - const char *new_check, + const char *new_measures, struct GNUNET_TIME_Timestamp expiration_time, unsigned int num_rules, const struct TALER_EXCHANGE_AccountRule *rules, @@ -6432,12 +6434,12 @@ TALER_EXCHANGE_add_aml_decision ( /** - * Cancel #TALER_EXCHANGE_add_aml_decision() operation. + * Cancel #TALER_EXCHANGE_post_aml_decision() operation. * * @param rh handle of the operation to cancel */ void -TALER_EXCHANGE_add_aml_decision_cancel ( +TALER_EXCHANGE_post_aml_decision_cancel ( struct TALER_EXCHANGE_AddAmlDecision *rh); diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index 48671fc51..44e1b8cfb 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -7064,6 +7064,23 @@ struct TALER_EXCHANGEDB_Plugin /** + * Lookup KYC rules by account access token. + * + * @param cls closure + * @param h_payto account payto hash to look under + * @param[out] jnew_rules set to active LegitimizationRuleSet + * @param[out] rowid row of the last legitimization outcome + * @return database transaction status + */ + enum GNUNET_DB_QueryStatus + (*lookup_rules_by_access_token)( + void *cls, + const struct TALER_PaytoHashP *h_payto, + json_t **jnew_rules, + uint64_t *rowid); + + + /** * Lookup KYC process meta data. * * @param cls closure @@ -7612,6 +7629,26 @@ struct TALER_EXCHANGEDB_Plugin /** + * Create new active legitimization measure. + * + * + * @param cls closure + * @param access_token access token that identifies the + * account the legitimization measures apply to + * @param jmeasures new legitimization measures + * @param[out] legitimization_measure_serial_id + * set to new row in legitimization_measures table + * @return database transaction status + */ + enum GNUNET_DB_QueryStatus + (*insert_active_legitimization_measure) ( + void *cls, + const struct TALER_AccountAccessTokenP *access_token, + const json_t *jmeasures, + uint64_t *legitimization_measure_serial_id); + + + /** * Insert an AML decision. Inserts into AML history and insert or updates AML * status. * diff --git a/src/include/taler_kyclogic_lib.h b/src/include/taler_kyclogic_lib.h index 2387ac074..fdfd5e283 100644 --- a/src/include/taler_kyclogic_lib.h +++ b/src/include/taler_kyclogic_lib.h @@ -164,11 +164,6 @@ struct TALER_KYCLOGIC_KycCheck unsigned int num_outputs; /** - * True if clients can voluntarily trigger this check. - */ - bool voluntary; - - /** * Type of the KYC check. */ enum TALER_KYCLOGIC_CheckType type; @@ -383,6 +378,32 @@ TALER_KYCLOGIC_get_zero_limits (void); /** + * Obtain set of all measures that + * could be triggered at an amount of zero and that + * thus might be requested before a client even + * has performed any operation. + * + * @param lrs rule set to investigate, NULL for default + * @return LegitimizationMeasures, NULL on error + */ +json_t * +TALER_KYCLOGIC_zero_measures ( + const struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs); + + +/** + * Obtain set of all voluntary measures that + * could be triggered by clients at will. + * + * @param lrs rule set to investigate, NULL for default + * @return array of MeasureInformation, never NULL + */ +json_t * +TALER_KYCLOGIC_voluntary_measures ( + const struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs); + + +/** * Get human-readable name of KYC rule. * * @param r rule to convert @@ -564,17 +585,17 @@ TALER_KYCLOGIC_measure_to_requirement ( /** - * Lookup @a measure_name in @a lrs and create JSON - * object with the corresponding LegitimizationMeasures. + * Lookup measures from @a measures_spec in @a lrs and create JSON object with + * the corresponding LegitimizationMeasures. * * @param lrs set of legitimization rules - * @param measure_name name of a measure to trigger from @a lrs + * @param measures_spec space-separated set of a measures to trigger from @a lrs; "+"-prefixed if AND-cominbation applies * @return JSON object of type LegitimizationMeasures */ json_t * -TALER_KYCLOGIC_get_measure ( +TALER_KYCLOGIC_get_measures ( const struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs, - const char *measure_name); + const char *measures_spec); /** * Lookup the provider for the given @a check_name. diff --git a/src/include/taler_pq_lib.h b/src/include/taler_pq_lib.h index 860be0e4d..e9929a0e9 100644 --- a/src/include/taler_pq_lib.h +++ b/src/include/taler_pq_lib.h @@ -188,7 +188,7 @@ TALER_PQ_query_param_array_hash_code ( * `struct TALER_DenominationHashP` * * @param num number of elements in @e hash_codes - * @param hashes array of GNUNET_HashCode + * @param denom_hs array of denomination hashes to encode * @param db context for the db-connection */ struct GNUNET_PQ_QueryParam diff --git a/src/kyclogic/kyclogic_api.c b/src/kyclogic/kyclogic_api.c index 16cd16c72..4aaf5d014 100644 --- a/src/kyclogic/kyclogic_api.c +++ b/src/kyclogic/kyclogic_api.c @@ -155,6 +155,10 @@ struct TALER_KYCLOGIC_Measure */ json_t *context; + /** + * Can this measure be triggered voluntarily? + */ + bool voluntary; }; @@ -628,7 +632,8 @@ TALER_KYCLOGIC_rules_parse (const json_t *jlrs) GNUNET_break (0); goto cleanup; } - rule->next_measures[j] = GNUNET_strdup (str); + rule->next_measures[j] + = GNUNET_strdup (str); } } } @@ -650,6 +655,7 @@ TALER_KYCLOGIC_rules_parse (const json_t *jlrs) const char *check_name; const char *prog_name; const json_t *context = NULL; + bool voluntary = false; struct TALER_KYCLOGIC_Measure *measure = &lrs->custom_measures[off++]; struct GNUNET_JSON_Specification ispec[] = { @@ -661,6 +667,10 @@ TALER_KYCLOGIC_rules_parse (const json_t *jlrs) GNUNET_JSON_spec_array_const ("context", &context), NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_bool ("voluntary", + &voluntary), + NULL), GNUNET_JSON_spec_end () }; @@ -678,6 +688,8 @@ TALER_KYCLOGIC_rules_parse (const json_t *jlrs) = GNUNET_strdup (check_name); measure->prog_name = GNUNET_strdup (prog_name); + measure->voluntary + = voluntary; if (NULL != context) measure->context = json_incref ((json_t*) context); @@ -980,35 +992,105 @@ TALER_KYCLOGIC_rule_to_measures ( json_t * -TALER_KYCLOGIC_get_measure ( - const struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs, - const char *measure_name) +TALER_KYCLOGIC_zero_measures ( + const struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs) { - const struct TALER_KYCLOGIC_Measure *ms; - json_t *mi; - json_t *jmeasures; + json_t *zero_measures; + const struct TALER_KYCLOGIC_KycRule *rules; + unsigned int num_rules; - if (0 == strcasecmp ("verboten", - measure_name)) - { - jmeasures = json_array (); - GNUNET_assert (NULL != jmeasures); - return GNUNET_JSON_PACK ( - GNUNET_JSON_pack_array_steal ("measures", - jmeasures), - GNUNET_JSON_pack_bool ("is_and_combinator", - false), - GNUNET_JSON_pack_bool ("verboten", - true)); - } - ms = find_measure (lrs, - measure_name); - if (NULL == ms) + if (NULL == lrs) + lrs = &default_rules; + rules = lrs->kyc_rules; + num_rules = lrs->num_kyc_rules; + zero_measures = json_array (); + GNUNET_assert (NULL != zero_measures); + for (unsigned int i = 0; i<num_rules; i++) { - GNUNET_break (0); - return NULL; + const struct TALER_KYCLOGIC_KycRule *rule = &rules[i]; + + if (! rule->exposed) + continue; + if (rule->verboten) + continue; /* see: hard_limits */ + if (! TALER_amount_is_zero (&rule->threshold)) + continue; + for (unsigned int j = 0; j<rule->num_measures; j++) + { + const struct TALER_KYCLOGIC_Measure *ms; + json_t *mi; + + ms = find_measure (lrs, + rule->next_measures[j]); + if (NULL == ms) + { + GNUNET_break (0); + json_decref (zero_measures); + return NULL; + } + if (0 == strcasecmp ("verboten", + ms->check_name)) + continue; /* not a measure to be selected */ + mi = GNUNET_JSON_PACK ( + TALER_JSON_pack_kycte ("operation_type", + rule->trigger), + GNUNET_JSON_pack_string ("check_name", + ms->check_name), + GNUNET_JSON_pack_string ("prog_name", + ms->prog_name), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_object_incref ("context", + ms->context))); + GNUNET_assert (0 == + json_array_append_new (zero_measures, + mi)); + } } - mi = GNUNET_JSON_PACK ( + return GNUNET_JSON_PACK ( + GNUNET_JSON_pack_array_steal ("measures", + zero_measures), + /* Zero-measures are always OR */ + GNUNET_JSON_pack_bool ("is_and_combinator", + false), + /* OR means verboten measures do not matter */ + GNUNET_JSON_pack_bool ("verboten", + false)); +} + + +/** + * Check if @a ms is a voluntary measure, and if so + * convert to JSON and append to @a voluntary_measures. + * + * @param[in,out] voluntary_measures JSON array of MeasureInformation + * @param ms a measure to possibly append + */ +static void +append_voluntary_measure ( + json_t *voluntary_measures, + const struct TALER_KYCLOGIC_Measure *ms) +{ +#if 0 + json_t *mj; +#endif + + if (! ms->voluntary) + return; + if (0 == strcasecmp ("verboten", + ms->check_name)) + return; /* very strange configuration */ +#if 0 + /* TODO: support vATTEST-9048 (this API in kyclogic!) */ + // NOTE: need to convert ms to "KycRequirementInformation" + // *and* in particular generate "id" values that + // are then understood to refer to the voluntary measures + // by the rest of the API (which is the hard part!) + // => need to change the API to encode the + // legitimization_outcomes row ID of the lrs from + // which the voluntary 'ms' originated, and + // then update the kyc-upload/kyc-start endpoints + // to recognize the new ID format! + mj = GNUNET_JSON_PACK ( GNUNET_JSON_pack_string ("check_name", ms->check_name), GNUNET_JSON_pack_string ("prog_name", @@ -1016,18 +1098,113 @@ TALER_KYCLOGIC_get_measure ( GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_object_incref ("context", ms->context))); + GNUNET_assert (0 == + json_array_append_new (voluntary_measures, + mj)); +#endif +} + + +json_t * +TALER_KYCLOGIC_voluntary_measures ( + const struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs) +{ + json_t *voluntary_measures; + + voluntary_measures = json_array (); + GNUNET_assert (NULL != voluntary_measures); + if (NULL != lrs) + { + for (unsigned int i = 0; i<lrs->num_custom_measures; i++) + { + const struct TALER_KYCLOGIC_Measure *ms + = &lrs->custom_measures[i]; + + append_voluntary_measure (voluntary_measures, + ms); + } + } + for (unsigned int i = 0; i<default_rules.num_custom_measures; i++) + { + const struct TALER_KYCLOGIC_Measure *ms + = &default_rules.custom_measures[i]; + + append_voluntary_measure (voluntary_measures, + ms); + } + return voluntary_measures; +} + + +json_t * +TALER_KYCLOGIC_get_measures ( + const struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs, + const char *measures_spec) +{ + json_t *jmeasures; + char *nm; + bool verboten = false; + bool is_and = false; + + if ('+' == measures_spec[0]) + { + nm = GNUNET_strdup (&measures_spec[1]); + is_and = true; + } + else + { + nm = GNUNET_strdup (measures_spec); + } jmeasures = json_array (); GNUNET_assert (NULL != jmeasures); - GNUNET_assert (0 == - json_array_append_new (jmeasures, - mi)); + for (const char *tok = strtok (nm, " "); + NULL != tok; + tok = strtok (NULL, " ")) + { + const struct TALER_KYCLOGIC_Measure *ms; + json_t *mi; + + if (0 == strcasecmp ("verboten", + tok)) + { + verboten = true; + continue; + } + ms = find_measure (lrs, + tok); + if (NULL == ms) + { + GNUNET_break (0); + GNUNET_free (nm); + json_decref (jmeasures); + return NULL; + } + if (0 == strcasecmp ("verboten", + ms->check_name)) + { + verboten = true; + continue; + } + mi = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("check_name", + ms->check_name), + GNUNET_JSON_pack_string ("prog_name", + ms->prog_name), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_object_incref ("context", + ms->context))); + GNUNET_assert (0 == + json_array_append_new (jmeasures, + mi)); + } + GNUNET_free (nm); return GNUNET_JSON_PACK ( GNUNET_JSON_pack_array_steal ("measures", jmeasures), GNUNET_JSON_pack_bool ("is_and_combinator", - false), + is_and), GNUNET_JSON_pack_bool ("verboten", - false)); + verboten)); } @@ -1487,7 +1664,6 @@ static enum GNUNET_GenericReturnValue add_check (const struct GNUNET_CONFIGURATION_Handle *cfg, const char *section) { - bool voluntary; enum TALER_KYCLOGIC_CheckType ct; char *description = NULL; json_t *description_i18n = NULL; @@ -1505,11 +1681,6 @@ add_check (const struct GNUNET_CONFIGURATION_Handle *cfg, GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Parsing KYC check %s\n", section); - voluntary = (GNUNET_YES == - GNUNET_CONFIGURATION_get_value_yesno (cfg, - section, - "VOLUNTARY")); - { char *type_s; @@ -1683,7 +1854,6 @@ add_check (const struct GNUNET_CONFIGURATION_Handle *cfg, break; } kc->check_name = GNUNET_strdup (§ion[strlen ("kyc-check-")]); - kc->voluntary = voluntary; kc->description = description; kc->description_i18n = description_i18n; kc->fallback = fallback; @@ -2066,6 +2236,7 @@ static enum GNUNET_GenericReturnValue add_measure (const struct GNUNET_CONFIGURATION_Handle *cfg, const char *section) { + bool voluntary; char *check_name = NULL; char *context_str = NULL; char *program = NULL; @@ -2094,6 +2265,11 @@ add_measure (const struct GNUNET_CONFIGURATION_Handle *cfg, "PROGRAM"); goto fail; } + voluntary = (GNUNET_YES == + GNUNET_CONFIGURATION_get_value_yesno (cfg, + section, + "VOLUNTARY")); + if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, section, @@ -2125,6 +2301,7 @@ add_measure (const struct GNUNET_CONFIGURATION_Handle *cfg, m.check_name = check_name; m.prog_name = program; m.context = context; + m.voluntary = voluntary; GNUNET_array_append (default_rules.custom_measures, default_rules.num_custom_measures, m); diff --git a/src/lib/exchange_api_add_aml_decision.c b/src/lib/exchange_api_add_aml_decision.c index a9a41a0aa..abd348c4a 100644 --- a/src/lib/exchange_api_add_aml_decision.c +++ b/src/lib/exchange_api_add_aml_decision.c @@ -119,18 +119,18 @@ handle_add_aml_decision_finished (void *cls, &adr); wh->cb = NULL; } - TALER_EXCHANGE_add_aml_decision_cancel (wh); + TALER_EXCHANGE_post_aml_decision_cancel (wh); } struct TALER_EXCHANGE_AddAmlDecision * -TALER_EXCHANGE_add_aml_decision ( +TALER_EXCHANGE_post_aml_decision ( struct GNUNET_CURL_Context *ctx, const char *url, const struct TALER_PaytoHashP *h_payto, struct GNUNET_TIME_Timestamp decision_time, const char *successor_measure, - const char *new_measure, + const char *new_measures, struct GNUNET_TIME_Timestamp expiration_time, unsigned int num_rules, const struct TALER_EXCHANGE_AccountRule *rules, @@ -229,7 +229,7 @@ TALER_EXCHANGE_add_aml_decision ( h_payto, new_rules, properties, - new_measure, + new_measures, keep_investigating, officer_priv, &officer_sig); @@ -274,8 +274,8 @@ TALER_EXCHANGE_add_aml_decision ( GNUNET_JSON_pack_object_incref ("properties", (json_t *) properties), GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ("new_measure", - new_measure)), + GNUNET_JSON_pack_string ("new_measures", + new_measures)), GNUNET_JSON_pack_bool ("keep_investigating", keep_investigating), GNUNET_JSON_pack_data_auto ("officer_sig", @@ -307,7 +307,7 @@ TALER_EXCHANGE_add_aml_decision ( wh); if (NULL == wh->job) { - TALER_EXCHANGE_add_aml_decision_cancel (wh); + TALER_EXCHANGE_post_aml_decision_cancel (wh); return NULL; } return wh; @@ -315,7 +315,7 @@ TALER_EXCHANGE_add_aml_decision ( void -TALER_EXCHANGE_add_aml_decision_cancel ( +TALER_EXCHANGE_post_aml_decision_cancel ( struct TALER_EXCHANGE_AddAmlDecision *wh) { if (NULL != wh->job) diff --git a/src/testing/test_kyc_api.c b/src/testing/test_kyc_api.c index 3ee68f30e..3af972569 100644 --- a/src/testing/test_kyc_api.c +++ b/src/testing/test_kyc_api.c @@ -704,7 +704,7 @@ run (void *cls, " ,\"verboten\":false" " }" " ]" /* end new rules */ - ",\"new_measure\":\"form-measure\"" + ",\"new_measures\":\"form-measure\"" ",\"custom_measures\":" " {" " \"form-measure\":" diff --git a/src/testing/testing_api_cmd_take_aml_decision.c b/src/testing/testing_api_cmd_take_aml_decision.c index b3976b82e..ce2cd8e92 100644 --- a/src/testing/testing_api_cmd_take_aml_decision.c +++ b/src/testing/testing_api_cmd_take_aml_decision.c @@ -149,7 +149,7 @@ take_aml_decision_run (void *cls, const json_t *jmeasures = NULL; struct GNUNET_TIME_Timestamp expiration_time = GNUNET_TIME_relative_to_timestamp (ds->expiration_delay); - const char *new_measure = NULL; + const char *new_measures = NULL; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_array_const ("rules", &jrules), @@ -158,8 +158,8 @@ take_aml_decision_run (void *cls, &jmeasures), NULL), GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("new_measure", - &new_measure), + GNUNET_JSON_spec_string ("new_measures", + &new_measures), NULL), GNUNET_JSON_spec_end () }; @@ -350,13 +350,13 @@ take_aml_decision_run (void *cls, } GNUNET_assert (off == num_measures); - ds->dh = TALER_EXCHANGE_add_aml_decision ( + ds->dh = TALER_EXCHANGE_post_aml_decision ( TALER_TESTING_interpreter_get_context (is), exchange_url, h_payto, now, ds->successor_measure, - new_measure, + new_measures, expiration_time, num_rules, rules, @@ -402,7 +402,7 @@ take_aml_decision_cleanup (void *cls, { TALER_TESTING_command_incomplete (ds->is, cmd->label); - TALER_EXCHANGE_add_aml_decision_cancel (ds->dh); + TALER_EXCHANGE_post_aml_decision_cancel (ds->dh); ds->dh = NULL; } json_decref (ds->new_rules); |