commit cbc42d229bdd1b33fee821d0877daf74c098f9c4
parent 10d8da9c34cf1a41d9a4cfa4b040eed945b9e944
Author: Christian Grothoff <christian@grothoff.org>
Date: Sun, 23 Jun 2024 11:26:22 +0200
handle POST aml /decision
Diffstat:
5 files changed, 148 insertions(+), 270 deletions(-)
diff --git a/src/exchange/taler-exchange-httpd_aml-decision.c b/src/exchange/taler-exchange-httpd_aml-decision.c
@@ -15,7 +15,7 @@
*/
/**
* @file taler-exchange-httpd_aml-decision.c
- * @brief Handle request about an AML decision.
+ * @brief Handle POST request about an AML decision.
* @author Christian Grothoff
*/
#include "platform.h"
@@ -31,205 +31,6 @@
#include "taler-exchange-httpd_responses.h"
-/**
- * Closure for #make_aml_decision()
- */
-struct DecisionContext
-{
- /**
- * Justification given for the decision.
- */
- const char *justification;
-
- /**
- * When was the decision taken.
- */
- struct GNUNET_TIME_Timestamp decision_time;
-
- /**
- * New rules after the decision.
- */
- const json_t *new_rules;
-
- /**
- * Hash of payto://-URI of affected account.
- */
- struct TALER_PaytoHashP h_payto;
-
- /**
- * Signature affirming the decision.
- */
- struct TALER_AmlOfficerSignatureP officer_sig;
-
- /**
- * Public key of the AML officer.
- */
- const struct TALER_AmlOfficerPublicKeyP *officer_pub;
-
-};
-
-
-/**
- * Function implementing AML decision database transaction.
- *
- * Runs the transaction logic; IF it returns a non-error code, the
- * transaction logic MUST NOT queue a MHD response. IF it returns an hard
- * error, the transaction logic MUST queue a MHD response and set @a mhd_ret.
- * IF it returns the soft error code, the function MAY be called again to
- * retry and MUST not queue a MHD response.
- *
- * @param cls closure with a `struct DecisionContext`
- * @param connection MHD request which triggered the transaction
- * @param[out] mhd_ret set to MHD response status for @a connection,
- * if transaction failed (!)
- * @return transaction status
- */
-static enum GNUNET_DB_QueryStatus
-make_aml_decision (void *cls,
- struct MHD_Connection *connection,
- MHD_RESULT *mhd_ret)
-{
- struct DecisionContext *dc = cls;
- struct GNUNET_TIME_Timestamp last_date;
- bool invalid_officer = -1;
-
-#if FIXME
- enum GNUNET_DB_QueryStatus qs;
- uint64_t requirement_row = 0;
-
- if ( (NULL != dc->kyc_requirements) &&
- (0 != json_array_size (dc->kyc_requirements)) )
- {
- char *res = NULL;
- size_t idx;
- json_t *req;
- bool satisfied;
-
- json_array_foreach (dc->kyc_requirements, idx, req)
- {
- const char *r = json_string_value (req);
-
- if (NULL == res)
- {
- res = GNUNET_strdup (r);
- }
- else
- {
- char *tmp;
-
- GNUNET_asprintf (&tmp,
- "%s %s",
- res,
- r);
- GNUNET_free (res);
- res = tmp;
- }
- }
-
- {
- json_t *kyc_details = NULL;
-
- qs = TALER_KYCLOGIC_check_satisfied (
- &res,
- &dc->h_payto,
- &kyc_details,
- TEH_plugin->select_satisfied_kyc_processes,
- TEH_plugin->cls,
- &satisfied);
- json_decref (kyc_details);
- }
- if (qs < 0)
- {
- if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
- {
- GNUNET_break (0);
- *mhd_ret = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- "select_satisfied_kyc_processes")
- ;
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- return qs;
- }
- if (! satisfied)
- {
- qs = TEH_plugin->insert_kyc_requirement_for_account (
- TEH_plugin->cls,
- res,
- &dc->h_payto,
- NULL, /* not a reserve */
- &requirement_row);
- if (qs < 0)
- {
- if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
- {
- GNUNET_break (0);
- *mhd_ret = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_STORE_FAILED,
- "insert_kyc_requirement_for_account");
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- return qs;
- }
- }
- GNUNET_free (res);
- }
-
- qs = TEH_plugin->insert_aml_decision (TEH_plugin->cls,
- &dc->h_payto,
- &dc->new_threshold,
- dc->new_state,
- dc->decision_time,
- dc->justification,
- dc->kyc_requirements,
- requirement_row,
- dc->officer_pub,
- &dc->officer_sig,
- &invalid_officer,
- &last_date);
- if (qs <= 0)
- {
- if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
- {
- GNUNET_break (0);
- *mhd_ret = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_STORE_FAILED,
- "insert_aml_decision");
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- return qs;
- }
-#endif
- if (invalid_officer)
- {
- GNUNET_break_op (0);
- *mhd_ret = TALER_MHD_reply_with_error (
- connection,
- MHD_HTTP_FORBIDDEN,
- TALER_EC_EXCHANGE_AML_DECISION_INVALID_OFFICER,
- NULL);
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- if (GNUNET_TIME_timestamp_cmp (last_date,
- >=,
- dc->decision_time))
- {
- GNUNET_break_op (0);
- *mhd_ret = TALER_MHD_reply_with_error (
- connection,
- MHD_HTTP_CONFLICT,
- TALER_EC_EXCHANGE_AML_DECISION_MORE_RECENT_PRESENT,
- NULL);
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
-
- return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
-}
-
-
MHD_RESULT
TEH_handler_post_aml_decision (
struct TEH_RequestContext *rc,
@@ -237,22 +38,33 @@ TEH_handler_post_aml_decision (
const json_t *root)
{
struct MHD_Connection *connection = rc->connection;
- struct DecisionContext dc = {
- .officer_pub = officer_pub
- };
+ const char *justification;
+ bool to_investigate;
+ struct GNUNET_TIME_Timestamp decision_time;
+ const json_t *new_rules;
+ const json_t *properties = NULL;
+ struct TALER_PaytoHashP h_payto;
+ struct TALER_AmlOfficerSignatureP officer_sig;
struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_fixed_auto ("officer_sig",
- &dc.officer_sig),
+ GNUNET_JSON_spec_string ("justification",
+ &justification),
GNUNET_JSON_spec_fixed_auto ("h_payto",
- &dc.h_payto),
+ &h_payto),
GNUNET_JSON_spec_object_const ("new_rules",
- &dc.new_rules),
- GNUNET_JSON_spec_string ("justification",
- &dc.justification),
+ &new_rules),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_object_const ("properties",
+ &properties),
+ NULL),
+ GNUNET_JSON_spec_bool ("keep_investigating",
+ &to_investigate),
+ GNUNET_JSON_spec_fixed_auto ("officer_sig",
+ &officer_sig),
GNUNET_JSON_spec_timestamp ("decision_time",
- &dc.decision_time),
+ &decision_time),
GNUNET_JSON_spec_end ()
};
+ struct GNUNET_TIME_Timestamp expiration_time;
{
enum GNUNET_GenericReturnValue res;
@@ -271,12 +83,14 @@ TEH_handler_post_aml_decision (
TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
if (GNUNET_OK !=
TALER_officer_aml_decision_verify (
- dc.justification,
- dc.decision_time,
- &dc.h_payto,
- dc.new_rules,
- dc.officer_pub,
- &dc.officer_sig))
+ justification,
+ decision_time,
+ &h_payto,
+ new_rules,
+ properties,
+ to_investigate,
+ officer_pub,
+ &officer_sig))
{
GNUNET_break_op (0);
return TALER_MHD_reply_with_error (
@@ -286,52 +100,70 @@ TEH_handler_post_aml_decision (
NULL);
}
-#if 0
- if (NULL != dc.kyc_requirements)
{
- size_t index;
- json_t *elem;
+ struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs;
- json_array_foreach (dc.kyc_requirements, index, elem)
+ lrs = TALER_KYCLOGIC_rules_parse (new_rules);
+ if (NULL == lrs)
{
- const char *val;
-
- if (! json_is_string (elem))
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (
- connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "kyc_requirements array members must be strings");
- }
- val = json_string_value (elem);
- if (GNUNET_SYSERR ==
- TALER_KYCLOGIC_check_satisfiable (val))
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (
- connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_EXCHANGE_AML_DECISION_UNKNOWN_CHECK,
- val);
- }
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "legitimization rule malformed");
}
+ expiration_time = TALER_KYCLOGIC_rules_get_expiration (lrs);
+
+ TALER_KYCLOGIC_rules_free (lrs);
}
-#endif
{
- MHD_RESULT mhd_ret;
-
- if (GNUNET_OK !=
- TEH_DB_run_transaction (connection,
- "make-aml-decision",
- TEH_MT_REQUEST_OTHER,
- &mhd_ret,
- &make_aml_decision,
- &dc))
+ enum GNUNET_DB_QueryStatus qs;
+ struct GNUNET_TIME_Timestamp last_date;
+ bool invalid_officer = true;
+
+ qs = TEH_plugin->insert_aml_decision (TEH_plugin->cls,
+ &h_payto,
+ decision_time,
+ expiration_time,
+ properties,
+ new_rules,
+ to_investigate,
+ justification,
+ officer_pub,
+ &officer_sig,
+ &invalid_officer,
+ &last_date);
+ if (qs <= 0)
+ {
+ GNUNET_break (0);
+ return TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_STORE_FAILED,
+ "insert_aml_decision");
+ }
+ if (invalid_officer)
{
- return mhd_ret;
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_FORBIDDEN,
+ TALER_EC_EXCHANGE_AML_DECISION_INVALID_OFFICER,
+ NULL);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ if (GNUNET_TIME_timestamp_cmp (last_date,
+ >=,
+ decision_time))
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_CONFLICT,
+ TALER_EC_EXCHANGE_AML_DECISION_MORE_RECENT_PRESENT,
+ NULL);
}
}
return TALER_MHD_reply_static (
diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h
@@ -2667,6 +2667,8 @@ TALER_officer_aml_query_verify (
* decision is about
* @param new_rules new KYC rules to apply to the account
* Must be a "LegitimizationRuleSet".
+ * @param properties properties of the account, can be NULL
+ * @param to_investigate true if the account should be investigated by AML staff
* @param officer_priv private key of AML officer
* @param[out] officer_sig where to write the signature
*/
@@ -2676,6 +2678,8 @@ TALER_officer_aml_decision_sign (
struct GNUNET_TIME_Timestamp decision_time,
const struct TALER_PaytoHashP *h_payto,
const json_t *new_rules,
+ const json_t *properties,
+ bool to_investigate,
const struct TALER_AmlOfficerPrivateKeyP *officer_priv,
struct TALER_AmlOfficerSignatureP *officer_sig);
@@ -2688,6 +2692,8 @@ TALER_officer_aml_decision_sign (
* @param h_payto payto URI hash of the account the
* decision is about
* @param new_rules new KYC rules to apply to the account
+ * @param properties properties of the account, can be NULL
+ * @param to_investigate true if the account should be investigated by AML staff
* @param officer_pub public key of AML officer
* @param officer_sig signature to verify
* @return #GNUNET_OK if the signature is valid
@@ -2698,6 +2704,8 @@ TALER_officer_aml_decision_verify (
struct GNUNET_TIME_Timestamp decision_time,
const struct TALER_PaytoHashP *h_payto,
const json_t *new_rules,
+ const json_t *properties,
+ bool to_investigate,
const struct TALER_AmlOfficerPublicKeyP *officer_pub,
const struct TALER_AmlOfficerSignatureP *officer_sig);
diff --git a/src/include/taler_kyclogic_lib.h b/src/include/taler_kyclogic_lib.h
@@ -516,6 +516,17 @@ TALER_KYCLOGIC_lookup_logic (
/**
+ * Return expiration time for the given @a lrs
+ *
+ * @param lrs legitimization rules to inspect
+ * @return expiration time
+ */
+struct GNUNET_TIME_Timestamp
+TALER_KYCLOGIC_rules_get_expiration (
+ const struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs);
+
+
+/**
* Function called with the provider details and
* associated plugin closures for matching logics.
*
diff --git a/src/kyclogic/kyclogic_api.c b/src/kyclogic/kyclogic_api.c
@@ -301,6 +301,14 @@ static struct TALER_KYCLOGIC_AmlProgram **aml_programs;
static unsigned int num_aml_programs;
+struct GNUNET_TIME_Timestamp
+TALER_KYCLOGIC_rules_get_expiration (
+ const struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs)
+{
+ return lrs->expiration_time;
+}
+
+
/**
* Lookup a KYC check by @a check_name
*
@@ -334,20 +342,20 @@ TALER_KYCLOGIC_rules_parse (const json_t *jlrs)
const json_t *jrules;
const json_t *jcustom_measures = NULL;
struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_timestamp (
+ "expiration_time",
+ &expiration_time),
GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_object_const ("custom_measures",
- &jcustom_measures),
+ GNUNET_JSON_spec_string (
+ "successor_measure",
+ &successor_measure),
NULL),
GNUNET_JSON_spec_array_const ("rules",
&jrules),
GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string (
- "successor_measure",
- &successor_measure),
+ GNUNET_JSON_spec_object_const ("custom_measures",
+ &jcustom_measures),
NULL),
- GNUNET_JSON_spec_timestamp (
- "expiration_time",
- &expiration_time),
GNUNET_JSON_spec_end ()
};
struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs;
diff --git a/src/util/aml_signatures.c b/src/util/aml_signatures.c
@@ -52,10 +52,19 @@ struct TALER_AmlDecisionPS
struct GNUNET_HashCode h_justification GNUNET_PACKED;
/**
+ * Hash over the justification text.
+ */
+ struct GNUNET_HashCode h_properties GNUNET_PACKED;
+
+ /**
* Hash over JSON object with new KYC rules.
*/
struct GNUNET_HashCode h_new_rules;
+ /**
+ * 0: no investigation, 1: yes investigation.
+ */
+ uint64_t flags;
};
GNUNET_NETWORK_STRUCT_END
@@ -66,6 +75,8 @@ TALER_officer_aml_decision_sign (
struct GNUNET_TIME_Timestamp decision_time,
const struct TALER_PaytoHashP *h_payto,
const json_t *new_rules,
+ const json_t *properties,
+ bool to_investigate,
const struct TALER_AmlOfficerPrivateKeyP *officer_priv,
struct TALER_AmlOfficerSignatureP *officer_sig)
{
@@ -73,15 +84,18 @@ TALER_officer_aml_decision_sign (
.purpose.purpose = htonl (TALER_SIGNATURE_AML_DECISION),
.purpose.size = htonl (sizeof (ad)),
.decision_time = GNUNET_TIME_timestamp_hton (decision_time),
- .h_payto = *h_payto
+ .h_payto = *h_payto,
+ .flags = GNUNET_htonll (to_investigate ? 1 : 0)
};
GNUNET_CRYPTO_hash (justification,
strlen (justification),
&ad.h_justification);
- if (NULL != new_rules)
- TALER_json_hash (new_rules,
- &ad.h_new_rules);
+ if (NULL != properties)
+ TALER_json_hash (properties,
+ &ad.h_properties);
+ TALER_json_hash (new_rules,
+ &ad.h_new_rules);
GNUNET_CRYPTO_eddsa_sign (&officer_priv->eddsa_priv,
&ad,
&officer_sig->eddsa_signature);
@@ -94,6 +108,8 @@ TALER_officer_aml_decision_verify (
struct GNUNET_TIME_Timestamp decision_time,
const struct TALER_PaytoHashP *h_payto,
const json_t *new_rules,
+ const json_t *properties,
+ bool to_investigate,
const struct TALER_AmlOfficerPublicKeyP *officer_pub,
const struct TALER_AmlOfficerSignatureP *officer_sig)
{
@@ -101,15 +117,18 @@ TALER_officer_aml_decision_verify (
.purpose.purpose = htonl (TALER_SIGNATURE_AML_DECISION),
.purpose.size = htonl (sizeof (ad)),
.decision_time = GNUNET_TIME_timestamp_hton (decision_time),
- .h_payto = *h_payto
+ .h_payto = *h_payto,
+ .flags = GNUNET_htonll (to_investigate ? 1 : 0)
};
GNUNET_CRYPTO_hash (justification,
strlen (justification),
&ad.h_justification);
- if (NULL != new_rules)
- TALER_json_hash (new_rules,
- &ad.h_new_rules);
+ if (NULL != properties)
+ TALER_json_hash (properties,
+ &ad.h_properties);
+ TALER_json_hash (new_rules,
+ &ad.h_new_rules);
return GNUNET_CRYPTO_eddsa_verify (
TALER_SIGNATURE_AML_DECISION,
&ad,