aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
m---------contrib/gana0
-rw-r--r--src/auditor/taler-helper-auditor-coins.c1
-rw-r--r--src/benchmark/taler-aggregator-benchmark.c4
-rw-r--r--src/benchmark/taler-bank-benchmark.c2
-rw-r--r--src/exchange/taler-exchange-httpd_aml-decision.c23
-rw-r--r--src/exchange/taler-exchange-httpd_kyc-info.c189
-rw-r--r--src/exchangedb/0005-legitimization_measures.sql4
-rw-r--r--src/exchangedb/0005-legitimization_outcomes.sql2
-rw-r--r--src/exchangedb/Makefile.am2
-rw-r--r--src/exchangedb/exchange_do_insert_active_legitimization_measure.sql51
-rw-r--r--src/exchangedb/exchange_do_insert_aml_decision.sql32
-rw-r--r--src/exchangedb/exchange_do_lookup_kyc_requirement_by_row.sql10
-rw-r--r--src/exchangedb/pg_insert_active_legitimization_measure.c62
-rw-r--r--src/exchangedb/pg_insert_active_legitimization_measure.h48
-rw-r--r--src/exchangedb/pg_lookup_rules_by_access_token.c70
-rw-r--r--src/exchangedb/pg_lookup_rules_by_access_token.h44
-rw-r--r--src/exchangedb/plugin_exchangedb_postgres.c6
-rw-r--r--src/exchangedb/procedures.sql.in1
-rw-r--r--src/include/taler_exchange_service.h14
-rw-r--r--src/include/taler_exchangedb_plugin.h37
-rw-r--r--src/include/taler_kyclogic_lib.h41
-rw-r--r--src/include/taler_pq_lib.h2
-rw-r--r--src/kyclogic/kyclogic_api.c253
-rw-r--r--src/lib/exchange_api_add_aml_decision.c16
-rw-r--r--src/testing/test_kyc_api.c2
-rw-r--r--src/testing/testing_api_cmd_take_aml_decision.c12
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 (&section[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);