exchange

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

commit 16f967fe07ab1dac37a2a0622999efd01f49858e
parent c97633adb8bdfc6f6f2d574e2ada7127c7ba949d
Author: Christian Grothoff <christian@grothoff.org>
Date:   Sat, 27 Jul 2024 22:59:40 +0200

-work on bugs

Diffstat:
Msrc/exchange/taler-exchange-httpd_kyc-start.c | 24+++++++++++++++++++++---
Msrc/exchange/taler-exchange-httpd_kyc-upload.c | 59+++++++++++++++++++++++++++++++++++++++++++++--------------
Msrc/exchangedb/pg_lookup_pending_legitimization.c | 26+++++++++++++++++++++++---
Msrc/exchangedb/pg_lookup_pending_legitimization.h | 13+++++++++++--
Msrc/exchangedb/pg_update_kyc_process_by_row.c | 15++++++++++++++-
Msrc/exchangedb/pg_update_kyc_process_by_row.h | 8+++++++-
Msrc/include/taler_exchangedb_plugin.h | 20++++++++++++++++++--
Msrc/include/taler_kyclogic_lib.h | 16++++++++++++++++
Msrc/kyclogic/kyclogic_api.c | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
9 files changed, 212 insertions(+), 26 deletions(-)

diff --git a/src/exchange/taler-exchange-httpd_kyc-start.c b/src/exchange/taler-exchange-httpd_kyc-start.c @@ -228,7 +228,6 @@ initiate_cb ( { kyp->hint = GNUNET_strdup (error_msg_hint); } - // FIXME: also store errors! qs = TEH_plugin->update_kyc_process_by_row ( TEH_plugin->cls, kyp->process_row, @@ -237,7 +236,10 @@ initiate_cb ( provider_user_id, provider_legitimization_id, redirect_url, - GNUNET_TIME_UNIT_ZERO_ABS); + GNUNET_TIME_UNIT_ZERO_ABS, + ec, + error_msg_hint, + TALER_EC_NONE != ec); if (qs <= 0) GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "KYC requirement update failed for %s with status %d at %s:%u\n", @@ -270,6 +272,9 @@ TEH_handler_kyc_start ( enum GNUNET_DB_QueryStatus qs; const struct TALER_KYCLOGIC_KycProvider *provider; struct TALER_KYCLOGIC_ProviderDetails *pd; + bool is_finished; + size_t enc_len; + void *enc = NULL; kyp = GNUNET_new (struct KycPoller); kyp->connection = rc->connection; @@ -323,7 +328,10 @@ TEH_handler_kyc_start ( kyp->legitimization_measure_serial_id, &kyp->access_token, &kyp->h_payto, - &kyp->jmeasures); + &kyp->jmeasures, + &is_finished, + &enc_len, + &enc); if (qs < 0) { GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs); @@ -341,6 +349,16 @@ TEH_handler_kyc_start ( TALER_EC_GENERIC_ENDPOINT_UNKNOWN, rc->url); } + GNUNET_free (enc); + if (is_finished) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error ( + rc->connection, + MHD_HTTP_CONFLICT, + -1, // FIXME: TALER_EC_..._ALREADY_FINISHED + rc->url); + } { struct TALER_KycMeasureAuthorizationHash shv2; diff --git a/src/exchange/taler-exchange-httpd_kyc-upload.c b/src/exchange/taler-exchange-httpd_kyc-upload.c @@ -229,6 +229,8 @@ finish_key (struct UploadContext *uc) /** * Function called to clean up upload context. + * + * @param[in,out] rc context to clean up */ static void upload_cleaner (struct TEH_RequestContext *rc) @@ -445,13 +447,20 @@ TEH_handler_kyc_upload (struct TEH_RequestContext *rc, enum GNUNET_DB_QueryStatus qs; json_t *jmeasures; struct MHD_Response *empty_response; + bool is_finished = false; + size_t enc_attributes_len; + void *enc_attributes; + json_t *xattributes; qs = TEH_plugin->lookup_pending_legitimization ( TEH_plugin->cls, uc->legitimization_measure_serial_id, &uc->access_token, &h_payto, - &jmeasures); + &jmeasures, + &is_finished, + &enc_attributes_len, + &enc_attributes); if (qs < 0) { GNUNET_break (0); @@ -463,27 +472,49 @@ TEH_handler_kyc_upload (struct TEH_RequestContext *rc, } if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) { - // FIXME: should check for idempotency! + GNUNET_break_op (0); + return TALER_MHD_reply_with_error ( + rc->connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_EXCHANGE_KYC_CHECK_REQUEST_UNKNOWN, + NULL); + } + + if (is_finished) + { + // FIXME: should check for idempotency: + // enc_attributes -> xattributes + // json_equal (xattributes, attributes)? + // => return OK (idempotent) + // or fail (conflicting submission) /* Note: we do not distinguish between row ID unknown and access token wrong here; this is on purpose to minimize information leakage (but we could distinguish the two in the future to help diagnose issues) */ + + GNUNET_free (enc_attributes); + return TALER_MHD_reply_with_error ( + rc->connection, + MHD_HTTP_CONFLICT, + -1, // FIXME: TALER_EC_EXCHANGE_KYC_FORM_ALREADY_SUBMITTED + NULL); + + } + GNUNET_free (enc_attributes); + if (GNUNET_OK != + TALER_KYCLOGIC_check_form (jmeasures, + uc->measure_index, + uc->result)) + { GNUNET_break_op (0); + json_decref (jmeasures); return TALER_MHD_reply_with_error ( rc->connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_EXCHANGE_KYC_CHECK_REQUEST_UNKNOWN, + MHD_HTTP_CONFLICT, + -1, // FIXME: TALER_EC_EXCHANGE_KYC_FORM_MEASURE_MISMATCH NULL); } - // FIXME: Do sanity checks on jmeasures vs. POSTed data: - // - // assert ! jmeasures.verboten - // MeasureInformation mi = jmeasures.measures[measure_index] - // Have: mi.{check_name,prog_name,context} - // assert kyc_checks[check_name].type == form - // assert input data matches form requirements... - json_decref (jmeasures); /* Setup KYC process (which we will then immediately 'finish') */ @@ -492,7 +523,7 @@ TEH_handler_kyc_upload (struct TEH_RequestContext *rc, &h_payto, uc->measure_index, uc->legitimization_measure_serial_id, - "FORM", // FIXME: correct??? or allow NULL? + "FORM", NULL, /* provider account ID */ NULL, /* provider legi ID */ &legi_process_row); @@ -514,7 +545,7 @@ TEH_handler_kyc_upload (struct TEH_RequestContext *rc, &rc->async_scope_id, legi_process_row, &h_payto, - NULL /* provider name */, + "FORM", NULL /* provider account */, NULL /* provider legi ID */, GNUNET_TIME_UNIT_FOREVER_ABS, /* expiration time */ diff --git a/src/exchangedb/pg_lookup_pending_legitimization.c b/src/exchangedb/pg_lookup_pending_legitimization.c @@ -32,7 +32,11 @@ TEH_PG_lookup_pending_legitimization ( uint64_t legitimization_measure_serial_id, struct TALER_AccountAccessTokenP *access_token, struct TALER_PaytoHashP *h_payto, - json_t **jmeasures) + json_t **jmeasures, + bool *is_finished, + size_t *encrypted_attributes_len, + void **encrypted_attributes + ) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { @@ -49,20 +53,36 @@ TEH_PG_lookup_pending_legitimization ( GNUNET_PQ_result_spec_auto_from_type ( "access_token", access_token), + GNUNET_PQ_result_spec_bool ( + "is_finished", + is_finished), + GNUNET_PQ_result_spec_allow_null ( + GNUNET_PQ_result_spec_variable_size ( + "encrypted_attributes", + encrypted_attributes, + encrypted_attributes_len), + NULL), GNUNET_PQ_result_spec_end }; + *encrypted_attributes_len = 0; + *encrypted_attributes = NULL; PREPARE (pg, "lookup_pending_legitimization", "SELECT " " lm.jmeasures" ",wt.wire_target_h_payto" ",lm.access_token" + ",lm.is_finished" + ",ka.encrypted_attributes" " FROM legitimization_measures lm" " JOIN wire_targets wt" " ON (lm.access_token = wt.access_token)" - " WHERE lm.legitimization_measure_serial_id=$1" - " AND NOT lm.is_finished;"); + " LEFT JOIN legitimization_processes lp" + " ON (lm.legitimization_measure_serial_id = lp.legitimization_measure_serial_id)" + " LEFT JOIN kyc_attributes ka" + " ON (ka.legitimization_serial = lp.legitimization_process_serial)" + " WHERE lm.legitimization_measure_serial_id=$1;"); return GNUNET_PQ_eval_prepared_singleton_select ( pg->conn, "lookup_pending_legitimization", diff --git a/src/exchangedb/pg_lookup_pending_legitimization.h b/src/exchangedb/pg_lookup_pending_legitimization.h @@ -38,6 +38,13 @@ * payto URI of the account undergoing legitimization * @param[out] jmeasures set to the legitimization * measures that were put on the account + * @param[out] is_finished set to true if the legitimization was + * already finished + * @param[out] encrypted_attributes_len set to length of + * @a encrypted_attributes + * @param[out] encrypted_attributes set to the attributes + * obtained for the legitimization process, if it + * succeeded, otherwise set to NULL * @return database transaction status */ enum GNUNET_DB_QueryStatus @@ -46,7 +53,9 @@ TEH_PG_lookup_pending_legitimization ( uint64_t legitimization_measure_serial_id, struct TALER_AccountAccessTokenP *access_token, struct TALER_PaytoHashP *h_payto, - json_t **jmeasures); - + json_t **jmeasures, + bool *is_finished, + size_t *encrypted_attributes_len, + void **encrypted_attributes); #endif diff --git a/src/exchangedb/pg_update_kyc_process_by_row.c b/src/exchangedb/pg_update_kyc_process_by_row.c @@ -35,9 +35,13 @@ TEH_PG_update_kyc_process_by_row ( const char *provider_account_id, const char *provider_legitimization_id, const char *redirect_url, - struct GNUNET_TIME_Absolute expiration) + struct GNUNET_TIME_Absolute expiration, + enum TALER_ErrorCode ec, + const char *error_message_hint, + bool finished) { struct PostgresClosure *pg = cls; + uint32_t ec32 = (uint32_t) ec; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_uint64 (&process_row), GNUNET_PQ_query_param_string (provider_name), @@ -52,6 +56,12 @@ TEH_PG_update_kyc_process_by_row ( ? GNUNET_PQ_query_param_string (redirect_url) : GNUNET_PQ_query_param_null (), GNUNET_PQ_query_param_absolute_time (&expiration), + GNUNET_PQ_query_param_string (provider_name), + GNUNET_PQ_query_param_uint32 (&ec32), + (NULL != error_message_hint) + ? GNUNET_PQ_query_param_string (error_message_hint) + : GNUNET_PQ_query_param_null (), + GNUNET_PQ_query_param_bool (finished), GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; @@ -67,6 +77,9 @@ TEH_PG_update_kyc_process_by_row ( " ,provider_legitimization_id=$5" " ,redirect_url=$6" " ,expiration_time=GREATEST(expiration_time,$7)" + " ,error_code=$8" + " ,error_message=$9" + " ,finished=$10" " WHERE" " h_payto=$3" " AND legitimization_process_serial_id=$1" diff --git a/src/exchangedb/pg_update_kyc_process_by_row.h b/src/exchangedb/pg_update_kyc_process_by_row.h @@ -37,6 +37,9 @@ * @param provider_legitimization_id provider legitimization ID * @param redirect_url where the user should be redirected to start the KYC process * @param expiration how long is this KYC check set to be valid (in the past if invalid) + * @param ec error code, #TALER_EC_NONE on success + * @param error_message_hint human-readable error message details (in addition to @a ec, NULL on success) + * @param finished true to mark the process as done * @return database transaction status */ enum GNUNET_DB_QueryStatus @@ -48,6 +51,9 @@ TEH_PG_update_kyc_process_by_row ( const char *provider_account_id, const char *provider_legitimization_id, const char *redirect_url, - struct GNUNET_TIME_Absolute expiration); + struct GNUNET_TIME_Absolute expiration, + enum TALER_ErrorCode ec, + const char *error_message_hint, + bool finished); #endif diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h @@ -6937,6 +6937,9 @@ struct TALER_EXCHANGEDB_Plugin * @param provider_legitimization_id provider legitimization ID * @param redirect_url where the user should be redirected to start the KYC process * @param expiration how long is this KYC check set to be valid (in the past if invalid) + * @param ec error code, #TALER_EC_NONE on success + * @param error_message_hint human-readable error message details (in addition to @a ec, NULL on success) + * @param finished true to mark the process as done * @return database transaction status */ enum GNUNET_DB_QueryStatus @@ -6948,7 +6951,10 @@ struct TALER_EXCHANGEDB_Plugin const char *provider_account_id, const char *provider_legitimization_id, const char *redirect_url, - struct GNUNET_TIME_Absolute expiration); + struct GNUNET_TIME_Absolute expiration, + enum TALER_ErrorCode ec, + const char *error_message_hint, + bool finished); /** @@ -7387,6 +7393,13 @@ struct TALER_EXCHANGEDB_Plugin * payto URI of the account undergoing legitimization * @param[out] jmeasures set to the legitimization * measures that were put on the account + * @param[out] is_finished set to true if the legitimization was + * already finished + * @param[out] encrypted_attributes_len set to length of + * @a encrypted_attributes + * @param[out] encrypted_attributes set to the attributes + * obtained for the legitimization process, if it + * succeeded, otherwise set to NULL * @return database transaction status */ enum GNUNET_DB_QueryStatus @@ -7395,7 +7408,10 @@ struct TALER_EXCHANGEDB_Plugin uint64_t legitimization_measure_serial_id, struct TALER_AccountAccessTokenP *access_token, struct TALER_PaytoHashP *h_payto, - json_t **jmeasures); + json_t **jmeasures, + bool *is_finished, + size_t *encrypted_attributes_len, + void **encrypted_attributes); /** diff --git a/src/include/taler_kyclogic_lib.h b/src/include/taler_kyclogic_lib.h @@ -496,6 +496,22 @@ TALER_KYCLOGIC_select_measure ( /** + * Check if the form data matches the requirements + * of the selected measure. + * + * @param jmeasures a LegitimizationMeasures object + * @param measure_index an index into the measures + * @param form_data form data submitted for the measure + * @return #GNUNET_OK if the form data matches the measure + */ +enum GNUNET_GenericReturnValue +TALER_KYCLOGIC_check_form ( + const json_t *jmeasures, + size_t measure_index, + const json_t *form_data); + + +/** * Convert MeasureInformation into the * KycRequirementInformation used by the client. * diff --git a/src/kyclogic/kyclogic_api.c b/src/kyclogic/kyclogic_api.c @@ -3016,6 +3016,63 @@ TALER_KYCLOGIC_select_measure ( } +enum GNUNET_GenericReturnValue +TALER_KYCLOGIC_check_form ( + const json_t *jmeasures, + size_t measure_index, + const json_t *form_data) +{ + const char *check_name; + const char *prog_name; + const json_t *context; + struct TALER_KYCLOGIC_KycCheck *kc; + struct TALER_KYCLOGIC_AmlProgram *prog; + + if (TALER_EC_NONE != + TALER_KYCLOGIC_select_measure (jmeasures, + measure_index, + &check_name, + &prog_name, + &context)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + kc = find_check (check_name); + if (NULL == kc) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if (TALER_KYCLOGIC_CT_FORM != kc->type) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + prog = find_program (prog_name); + if (NULL == prog) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + for (unsigned int i = 0; i<prog->num_required_attributes; i++) + { + const char *rattr = prog->required_attributes[i]; + + if (NULL == json_object_get (form_data, + rattr)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Form data lacks required attribute `%s' for AML program %s\n", + rattr, + prog_name); + return GNUNET_NO; + } + } + return GNUNET_OK; +} + + const struct TALER_KYCLOGIC_KycProvider * TALER_KYCLOGIC_check_to_provider (const char *check_name) {