exchange

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

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:
Msrc/include/taler_exchange_service.h | 181+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Msrc/include/taler_testing_lib.h | 117++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/lib/exchange_api_lookup_aml_decisions.c | 220+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
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; } }