commit 2cc18b1b5db9b2b6f59bf0aa51055affff8cb21d
parent b51932dc73673a733de9f2bd0d12001737926a11
Author: Christian Grothoff <christian@grothoff.org>
Date: Sun, 5 May 2024 14:07:26 +0200
complete exchange_api_lookup_aml_decisions.c logic
Diffstat:
3 files changed, 394 insertions(+), 124 deletions(-)
diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h
@@ -5804,6 +5804,144 @@ TALER_EXCHANGE_kyc_get_statistics_cancel (
/**
+ * KYC rule that determines limits for an account.
+ */
+struct TALER_EXCHANGE_KycRule
+{
+ /**
+ * Type of operation to which the rule applies.
+ */
+ enum TALER_KYCLOGIC_KycTriggerEvent operation_type;
+
+ /**
+ * The measures will be taken if the given
+ * threshold is crossed over the given timeframe.
+ */
+ struct TALER_Amount threshold;
+
+ /**
+ * Over which duration should the @e threshold be
+ * computed. All amounts of the respective
+ * @e operation_type will be added up for this
+ * duration and the sum compared to the @e threshold.
+ */
+ struct GNUNET_TIME_Relative timeframe;
+
+ /**
+ * Array of names of measures to apply.
+ * Names listed can be original measures or
+ * custom measures from the `AmlOutcome`.
+ */
+ const char **measures;
+
+ /**
+ * Length of the measures array.
+ */
+ unsigned int measures_length;
+
+ /**
+ * True if crossing these limits is simply categorically
+ * forbidden (no measure will be triggered, the request
+ * will just be always denied).
+ */
+ bool verboten;
+
+ /**
+ * True if the rule (specifically, @e operation_type,
+ * @e threshold and @e timeframe) and the general nature of
+ * the measures (@e verboten)
+ * should be exposed to the client.
+ */
+ bool exposed;
+
+ /**
+ * True if all the measures will eventually need to
+ * be satisfied, false if any of the measures should
+ * do. Primarily used by the SPA to indicate how
+ * the measures apply when showing them to the user;
+ * in the end, AML programs will decide after each
+ * measure what to do next.
+ */
+ bool is_and_combinator;
+
+ /**
+ * If multiple rules apply to the same account
+ * at the same time, the number with the highest
+ * rule determines which set of measures will
+ * be activated and thus become visible for the
+ * user.
+ */
+ uint32_t display_priority;
+};
+
+
+/**
+ * Information about a (custom) measure.
+ */
+struct TALER_EXCHANGE_MeasureInformation
+{
+ /**
+ * Name of the measure.
+ */
+ const char *measure_name;
+
+ /**
+ * Name of the check.
+ */
+ const char *check_name;
+
+ /**
+ * Name of the AML program.
+ */
+ const char *prog_name;
+
+ /**
+ * Context for the check, can be NULL.
+ */
+ const json_t *context;
+
+};
+
+
+/**
+ * Set of legitimization rules with expiration data.
+ */
+struct TALER_EXCHANGE_LegitimizationRuleSet
+{
+
+ /**
+ * What successor measure applies to the account?
+ */
+ const char *successor_measure;
+
+ /**
+ * What are the current rules for the account?
+ */
+ const struct TALER_EXCHANGE_KycRule *rules;
+
+ /**
+ * What are custom measures that @e rules may refer to?
+ */
+ const struct TALER_EXCHANGE_MeasureInformation *measures;
+
+ /**
+ * When will this decision expire?
+ */
+ struct GNUNET_TIME_Timestamp expiration_time;
+
+ /**
+ * Length of the @e rules array.
+ */
+ unsigned int rules_length;
+
+ /**
+ * Length of the @e measures array.
+ */
+ unsigned int measures_length;
+};
+
+
+/**
* Data about an AML decision.
*/
struct TALER_EXCHANGE_AmlDecision
@@ -5819,14 +5957,14 @@ struct TALER_EXCHANGE_AmlDecision
uint64_t rowid;
/**
- * When was the decision taken?
+ * When was the decision made?
*/
struct GNUNET_TIME_Timestamp decision_time;
/**
- * When will this decision expire?
+ * What are the new rules?
*/
- struct GNUNET_TIME_Timestamp expiration_time;
+ struct TALER_EXCHANGE_LegitimizationRuleSet limits;
/**
* Properties set for the account.
@@ -5834,16 +5972,6 @@ struct TALER_EXCHANGE_AmlDecision
const json_t *jproperties;
/**
- * What are the current limits for the account?
- */
- const struct TALER_EXCHANGE_AccountLimit *limits;
-
- /**
- * Length of the @e limits array.
- */
- unsigned int limits_length;
-
- /**
* Should AML staff investigate this account?
*/
bool to_investigate;
@@ -6096,33 +6224,6 @@ typedef void
/**
- * Information about a possible measure.
- */
-struct TALER_EXCHANGE_MeasureInformation
-{
- /**
- * Name of the measure.
- */
- const char *measure_name;
-
- /**
- * Name of the check triggered by the measure.
- */
- const char *check_name;
-
- /**
- * Name of the AML program to run after the measure.
- */
- const char *prog_name;
-
- /**
- * Context for the check and the AML program.
- */
- const json_t *context;
-};
-
-
-/**
* Rule that applies for an account, specifies the
* trigger and measures to apply.
*/
diff --git a/src/include/taler_testing_lib.h b/src/include/taler_testing_lib.h
@@ -315,10 +315,10 @@ struct TALER_TESTING_Command
* @return #GNUNET_OK on success
*/
enum GNUNET_GenericReturnValue
- (*traits)(void *cls,
- const void **ret,
- const char *trait,
- unsigned int index);
+ (*traits)(void *cls,
+ const void **ret,
+ const char *trait,
+ unsigned int index);
/**
* When did the execution of this command start?
@@ -2429,7 +2429,6 @@ TALER_TESTING_cmd_take_aml_decision (
const char *ref_operation,
const char *new_threshold,
const char *justification,
- enum TALER_AmlDecisionState new_state,
const char *kyc_requirement,
unsigned int expected_response);
@@ -2467,7 +2466,6 @@ struct TALER_TESTING_Command
TALER_TESTING_cmd_check_aml_decisions (
const char *label,
const char *ref_officer,
- enum TALER_AmlDecisionState filter,
unsigned int expected_http_status);
@@ -2565,7 +2563,7 @@ TALER_TESTING_get_trait (const struct TALER_TESTING_Trait *traits,
enum GNUNET_GenericReturnValue \
TALER_TESTING_get_trait_ ## name ( \
const struct TALER_TESTING_Command *cmd, \
- type **ret); \
+ type * *ret); \
struct TALER_TESTING_Trait \
TALER_TESTING_make_trait_ ## name ( \
type * value);
@@ -2608,11 +2606,11 @@ TALER_TESTING_get_trait (const struct TALER_TESTING_Trait *traits,
TALER_TESTING_get_trait_ ## name ( \
const struct TALER_TESTING_Command *cmd, \
unsigned int index, \
- type **ret); \
+ type * *ret); \
struct TALER_TESTING_Trait \
TALER_TESTING_make_trait_ ## name ( \
unsigned int index, \
- type *value);
+ type * value);
/**
@@ -2654,53 +2652,56 @@ TALER_TESTING_get_trait (const struct TALER_TESTING_Trait *traits,
op (officer_pub, const struct TALER_AmlOfficerPublicKeyP) \
op (officer_priv, const struct TALER_AmlOfficerPrivateKeyP) \
op (officer_name, const char) \
- op (aml_decision, enum TALER_AmlDecisionState) \
- op (aml_justification, const char) \
- op (auditor_priv, const struct TALER_AuditorPrivateKeyP) \
- op (auditor_pub, const struct TALER_AuditorPublicKeyP) \
- op (master_priv, const struct TALER_MasterPrivateKeyP) \
- op (master_pub, const struct TALER_MasterPublicKeyP) \
- op (purse_priv, const struct TALER_PurseContractPrivateKeyP) \
- op (purse_pub, const struct TALER_PurseContractPublicKeyP) \
- op (merge_priv, const struct TALER_PurseMergePrivateKeyP) \
- op (merge_pub, const struct TALER_PurseMergePublicKeyP) \
- op (contract_priv, const struct TALER_ContractDiffiePrivateP) \
- op (reserve_priv, const struct TALER_ReservePrivateKeyP) \
- op (reserve_sig, const struct TALER_ReserveSignatureP) \
- op (h_payto, const struct TALER_PaytoHashP) \
- op (planchet_secret, const struct TALER_PlanchetMasterSecretP) \
- op (refresh_secret, const struct TALER_RefreshMasterSecretP) \
- op (reserve_pub, const struct TALER_ReservePublicKeyP) \
- op (merchant_priv, const struct TALER_MerchantPrivateKeyP) \
- op (merchant_pub, const struct TALER_MerchantPublicKeyP) \
- op (merchant_sig, const struct TALER_MerchantSignatureP) \
- op (wtid, const struct TALER_WireTransferIdentifierRawP) \
- op (bank_auth_data, const struct TALER_BANK_AuthenticationData) \
- op (contract_terms, const json_t) \
- op (wire_details, const json_t) \
- op (exchange_url, const char) \
- op (auditor_url, const char) \
- op (exchange_bank_account_url, const char) \
- op (taler_uri, const char) \
- op (payto_uri, const char) \
- op (kyc_url, const char) \
- op (web_url, const char) \
- op (row, const uint64_t) \
- op (legi_requirement_row, const uint64_t) \
- op (array_length, const unsigned int) \
- op (credit_payto_uri, const char) \
- op (debit_payto_uri, const char) \
- op (order_id, const char) \
- op (amount, const struct TALER_Amount) \
- op (amount_with_fee, const struct TALER_Amount) \
- op (batch_cmds, struct TALER_TESTING_Command) \
- op (uuid, const struct GNUNET_Uuid) \
- op (fresh_coins, const struct TALER_TESTING_FreshCoinData *) \
- op (claim_token, const struct TALER_ClaimTokenP) \
- op (relative_time, const struct GNUNET_TIME_Relative) \
- op (fakebank, struct TALER_FAKEBANK_Handle) \
- op (keys, struct TALER_EXCHANGE_Keys) \
- op (process, struct GNUNET_OS_Process *)
+ << << << < HEAD
+op (aml_decision, enum TALER_AmlDecisionState) \
+ ====== =
+ >> >> >> > f54d4f98 (complete exchange_api_lookup_aml_decisions.c logic)
+ op (aml_justification, const char) \
+ op (auditor_priv, const struct TALER_AuditorPrivateKeyP) \
+ op (auditor_pub, const struct TALER_AuditorPublicKeyP) \
+ op (master_priv, const struct TALER_MasterPrivateKeyP) \
+ op (master_pub, const struct TALER_MasterPublicKeyP) \
+ op (purse_priv, const struct TALER_PurseContractPrivateKeyP) \
+ op (purse_pub, const struct TALER_PurseContractPublicKeyP) \
+ op (merge_priv, const struct TALER_PurseMergePrivateKeyP) \
+ op (merge_pub, const struct TALER_PurseMergePublicKeyP) \
+ op (contract_priv, const struct TALER_ContractDiffiePrivateP) \
+ op (reserve_priv, const struct TALER_ReservePrivateKeyP) \
+ op (reserve_sig, const struct TALER_ReserveSignatureP) \
+ op (h_payto, const struct TALER_PaytoHashP) \
+ op (planchet_secret, const struct TALER_PlanchetMasterSecretP) \
+ op (refresh_secret, const struct TALER_RefreshMasterSecretP) \
+ op (reserve_pub, const struct TALER_ReservePublicKeyP) \
+ op (merchant_priv, const struct TALER_MerchantPrivateKeyP) \
+ op (merchant_pub, const struct TALER_MerchantPublicKeyP) \
+ op (merchant_sig, const struct TALER_MerchantSignatureP) \
+ op (wtid, const struct TALER_WireTransferIdentifierRawP) \
+ op (bank_auth_data, const struct TALER_BANK_AuthenticationData) \
+ op (contract_terms, const json_t) \
+ op (wire_details, const json_t) \
+ op (exchange_url, const char) \
+ op (auditor_url, const char) \
+ op (exchange_bank_account_url, const char) \
+ op (taler_uri, const char) \
+ op (payto_uri, const char) \
+ op (kyc_url, const char) \
+ op (web_url, const char) \
+ op (row, const uint64_t) \
+ op (legi_requirement_row, const uint64_t) \
+ op (array_length, const unsigned int) \
+ op (credit_payto_uri, const char) \
+ op (debit_payto_uri, const char) \
+ op (order_id, const char) \
+ op (amount, const struct TALER_Amount) \
+ op (amount_with_fee, const struct TALER_Amount) \
+ op (batch_cmds, struct TALER_TESTING_Command) \
+ op (uuid, const struct GNUNET_Uuid) \
+ op (fresh_coins, const struct TALER_TESTING_FreshCoinData *) \
+ op (claim_token, const struct TALER_ClaimTokenP) \
+ op (relative_time, const struct GNUNET_TIME_Relative) \
+ op (fakebank, struct TALER_FAKEBANK_Handle) \
+ op (keys, struct TALER_EXCHANGE_Keys) \
+ op (process, struct GNUNET_OS_Process *)
/**
@@ -2731,9 +2732,9 @@ TALER_TESTING_get_trait (const struct TALER_TESTING_Trait *traits,
op (blinding_key, const union GNUNET_CRYPTO_BlindingSecretP) \
op (h_blinded_coin, const struct TALER_BlindedCoinHashP)
-TALER_TESTING_SIMPLE_TRAITS (TALER_TESTING_MAKE_DECL_SIMPLE_TRAIT)
+ TALER_TESTING_SIMPLE_TRAITS (TALER_TESTING_MAKE_DECL_SIMPLE_TRAIT)
-TALER_TESTING_INDEXED_TRAITS (TALER_TESTING_MAKE_DECL_INDEXED_TRAIT)
+ TALER_TESTING_INDEXED_TRAITS (TALER_TESTING_MAKE_DECL_INDEXED_TRAIT)
#endif
diff --git a/src/lib/exchange_api_lookup_aml_decisions.c b/src/lib/exchange_api_lookup_aml_decisions.c
@@ -60,54 +60,220 @@ struct TALER_EXCHANGE_LookupAmlDecisions
* HTTP headers for the job.
*/
struct curl_slist *job_headers;
+
+ /**
+ * Array with measure information.
+ */
+ struct TALER_EXCHANGE_MeasureInformation *mip;
+
+ /**
+ * Array with rule information.
+ */
+ struct TALER_EXCHANGE_KycRule *rp;
+
+ /**
+ * Array with all the measures (of all the rules!).
+ */
+ const char **mp;
};
/**
* Parse AML limits array.
*
+ * @param[in,out] lh handle to use for allocations
* @param jlimits JSON array with AML rules
* @param[out] decision_ar where to write the result
* @return #GNUNET_OK on success
*/
static enum GNUNET_GenericReturnValue
-parse_limits (const json_t *jlimits,
+parse_limits (struct TALER_EXCHANGE_LookupAmlDecisions *lh,
+ const json_t *jlimits,
struct TALER_EXCHANGE_AmlDecision *ds)
{
- json_t *obj;
- size_t idx;
+ struct TALER_EXCHANGE_LegitimizationRuleSet *limits
+ = &ds->limits;
+ const json_t *jrules;
+ const json_t *jmeasures;
+ size_t mip_len;
+ size_t rule_len;
+ size_t total;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_timestamp ("expiration_time",
+ &limits->expiration_time),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("successor_measure",
+ &limits->successor_measure),
+ NULL),
+ GNUNET_JSON_spec_array_const ("rules",
+ &jrules),
+ GNUNET_JSON_spec_object_const ("custom_measures",
+ &jmeasures),
+ GNUNET_JSON_spec_end ()
+ };
- json_array_foreach (jlimits, idx, obj)
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (jlimits,
+ spec,
+ NULL,
+ NULL))
{
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_end ()
- };
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
- if (GNUNET_OK !=
- GNUNET_JSON_parse (obj,
- spec,
- NULL,
- NULL))
+ mip_len = json_object_size (jmeasures);
+ lh->mip = GNUNET_new_array (mip_len,
+ struct TALER_EXCHANGE_MeasureInformation);
+ limits->measures = lh->mip;
+ limits->measures_length = mip_len;
+
+ {
+ const char *measure_name;
+ const json_t *jmeasure;
+
+ json_object_foreach ((json_t*) jmeasures,
+ measure_name,
+ jmeasure)
{
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
+ struct TALER_EXCHANGE_MeasureInformation *mi
+ = &lh->mip[--mip_len];
+ struct GNUNET_JSON_Specification ispec[] = {
+ GNUNET_JSON_spec_string ("check_name",
+ &mi->check_name),
+ GNUNET_JSON_spec_string ("prog_name",
+ &mi->prog_name),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_object_const ("context",
+ &mi->context),
+ NULL),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (jmeasure,
+ ispec,
+ NULL,
+ NULL))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ mi->measure_name = measure_name;
+ }
+ }
+
+ total = 0;
+
+ {
+ const json_t *rule;
+ size_t idx;
+
+ json_array_foreach ((json_t *) jrules,
+ idx,
+ rule)
+ {
+ total += json_array_size (json_object_get (rule,
+ "measures"));
+ }
+ }
+
+ rule_len = json_array_size (jrules);
+ lh->rp = GNUNET_new_array (rule_len,
+ struct TALER_EXCHANGE_KycRule);
+ lh->mp = GNUNET_new_array (total,
+ const char *);
+
+ {
+ const json_t *rule;
+ size_t idx;
+
+ json_array_foreach ((json_t *) jrules,
+ idx,
+ rule)
+ {
+ const json_t *smeasures;
+ struct TALER_EXCHANGE_KycRule *r
+ = &lh->rp[--rule_len];
+ struct GNUNET_JSON_Specification ispec[] = {
+ TALER_JSON_spec_kycte ("operation_type",
+ &r->operation_type),
+ TALER_JSON_spec_amount_any ("threshold",
+ &r->threshold),
+ GNUNET_JSON_spec_relative_time ("timeframe",
+ &r->timeframe),
+ GNUNET_JSON_spec_array_const ("measures",
+ &smeasures),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_bool ("exposed",
+ &r->exposed),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_bool ("is_and_combinator",
+ &r->is_and_combinator),
+ NULL),
+ GNUNET_JSON_spec_uint32 ("display_priority",
+ &r->display_priority),
+ GNUNET_JSON_spec_end ()
+ };
+ size_t mlen;
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (rule,
+ ispec,
+ NULL,
+ NULL))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+
+ mlen = json_array_size (smeasures);
+ GNUNET_assert (mlen <= total);
+ total -= mlen;
+
+ {
+ size_t midx;
+ const json_t *smeasure;
+
+ json_array_foreach (smeasures,
+ midx,
+ smeasure)
+ {
+ const char *sval = json_string_value (smeasure);
+
+ if (NULL == sval)
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ lh->mp[total + midx] = sval;
+ if (0 == strcasecmp (sval,
+ "verboten"))
+ r->verboten = true;
+ }
+ }
+ r->measures = &lh->mp[total];
+ r->measures_length = r->verboten ? 0 : total;
}
}
- // FIXME...
- return GNUNET_SYSERR;
+ return GNUNET_OK;
}
/**
* Parse AML decision summary array.
*
+ * @param[in,out] lh handle to use for allocations
* @param decisions JSON array with AML decision summaries
* @param[out] decision_ar where to write the result
* @return #GNUNET_OK on success
*/
static enum GNUNET_GenericReturnValue
-parse_aml_decisions (const json_t *decisions,
- struct TALER_EXCHANGE_AmlDecision *decision_ar)
+parse_aml_decisions (
+ struct TALER_EXCHANGE_LookupAmlDecisions *lh,
+ const json_t *decisions,
+ struct TALER_EXCHANGE_AmlDecision *decision_ar)
{
json_t *obj;
size_t idx;
@@ -115,7 +281,7 @@ parse_aml_decisions (const json_t *decisions,
json_array_foreach (decisions, idx, obj)
{
struct TALER_EXCHANGE_AmlDecision *decision = &decision_ar[idx];
- const json_t *jlimits; // FIXME: not used!!!
+ const json_t *jlimits;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("h_payto",
&decision->h_payto),
@@ -123,14 +289,12 @@ parse_aml_decisions (const json_t *decisions,
&decision->rowid),
GNUNET_JSON_spec_timestamp ("decision_time",
&decision->decision_time),
- GNUNET_JSON_spec_timestamp ("expiration_time",
- &decision->expiration_time),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_object_const ("properties",
&decision->jproperties),
NULL),
- GNUNET_JSON_spec_array_const ("limits",
- &jlimits),
+ GNUNET_JSON_spec_object_const ("limits",
+ &jlimits),
GNUNET_JSON_spec_bool ("to_investigate",
&decision->to_investigate),
GNUNET_JSON_spec_bool ("is_active",
@@ -148,7 +312,8 @@ parse_aml_decisions (const json_t *decisions,
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
- parse_limits (jlimits,
+ parse_limits (lh,
+ jlimits,
decision))
{
GNUNET_break_op (0);
@@ -201,7 +366,8 @@ parse_decisions_ok (struct TALER_EXCHANGE_LookupAmlDecisions *lh,
0,
sizeof (decisions));
lr.details.ok.decisions = decisions;
- ret = parse_aml_decisions (records,
+ ret = parse_aml_decisions (lh,
+ records,
decisions);
if (GNUNET_OK == ret)
{
@@ -209,7 +375,9 @@ parse_decisions_ok (struct TALER_EXCHANGE_LookupAmlDecisions *lh,
&lr);
lh->decisions_cb = NULL;
}
- // FIXME: free limits!
+ GNUNET_free (lh->mip);
+ GNUNET_free (lh->rp);
+ GNUNET_free (lh->mp);
return ret;
}
}