diff options
Diffstat (limited to 'src/exchange/taler-exchange-httpd_kyc-check.c')
-rw-r--r-- | src/exchange/taler-exchange-httpd_kyc-check.c | 274 |
1 files changed, 189 insertions, 85 deletions
diff --git a/src/exchange/taler-exchange-httpd_kyc-check.c b/src/exchange/taler-exchange-httpd_kyc-check.c index 61d1abf8e..362c20a2e 100644 --- a/src/exchange/taler-exchange-httpd_kyc-check.c +++ b/src/exchange/taler-exchange-httpd_kyc-check.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2021-2022 Taler Systems SA + Copyright (C) 2021-2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software @@ -72,9 +72,14 @@ struct KycPoller struct GNUNET_DB_EventHandler *eh; /** - * UUID being checked. + * Row of the requirement being checked. */ - uint64_t legitimization_uuid; + uint64_t requirement_row; + + /** + * Row of KYC process being initiated. + */ + uint64_t process_row; /** * Hash of the payto:// URI we are confirming to @@ -88,9 +93,9 @@ struct KycPoller struct GNUNET_TIME_Absolute timeout; /** - * Type of KYC check required for this client. + * If the KYC complete, what kind of data was collected? */ - char *required; + json_t *kyc_details; /** * Set to starting URL of KYC process if KYC is required. @@ -103,19 +108,34 @@ struct KycPoller char *hint; /** + * Name of the section of the provider in the configuration. + */ + const char *section_name; + + /** + * Set to AML status of the account. + */ + enum TALER_AmlDecisionState aml_status; + + /** * Set to error encountered with KYC logic, if any. */ enum TALER_ErrorCode ec; /** + * What kind of entity is doing the KYC check? + */ + enum TALER_KYCLOGIC_KycUserType ut; + + /** * True if we are still suspended. */ bool suspended; /** - * True if KYC was required but is fully satisfied. + * False if KYC is not required. */ - bool found; + bool kyc_required; /** * True if we once tried the KYC initiation. @@ -185,9 +205,9 @@ kyp_cleanup (struct TEH_RequestContext *rc) kyp->ih_logic->initiate_cancel (kyp->ih); kyp->ih = NULL; } + json_decref (kyp->kyc_details); GNUNET_free (kyp->kyc_url); GNUNET_free (kyp->hint); - GNUNET_free (kyp->required); GNUNET_free (kyp); } @@ -218,7 +238,8 @@ initiate_cb ( kyp->ih = NULL; kyp->ih_done = true; GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "KYC initiation completed with status %d (%s)\n", + "KYC initiation `%s' completed with ec=%d (%s)\n", + provider_legitimization_id, ec, (TALER_EC_NONE == ec) ? redirect_url @@ -232,15 +253,16 @@ initiate_cb ( { kyp->hint = GNUNET_strdup (error_msg_hint); } - qs = TEH_plugin->update_kyc_requirement_by_row ( + qs = TEH_plugin->update_kyc_process_by_row ( TEH_plugin->cls, - kyp->legitimization_uuid, - kyp->required, + kyp->process_row, + kyp->section_name, &kyp->h_payto, provider_user_id, provider_legitimization_id, + redirect_url, GNUNET_TIME_UNIT_ZERO_ABS); - if (qs < 0) + if (qs <= 0) GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "KYC requirement update failed for %s with status %d at %s:%u\n", TALER_B2S (&kyp->h_payto), @@ -281,23 +303,21 @@ kyc_check (void *cls, struct TALER_KYCLOGIC_ProviderDetails *pd; enum GNUNET_GenericReturnValue ret; struct TALER_PaytoHashP h_payto; - struct GNUNET_TIME_Absolute expiration; - char *provider_account_id; - char *provider_legitimization_id; + char *requirements; + char *redirect_url; + bool satisfied; qs = TEH_plugin->lookup_kyc_requirement_by_row ( TEH_plugin->cls, - kyp->legitimization_uuid, - &kyp->required, - &h_payto, - &expiration, - &provider_account_id, - &provider_legitimization_id); + kyp->requirement_row, + &requirements, + &kyp->aml_status, + &h_payto); if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "No KYC requirements open for %llu\n", - (unsigned long long) kyp->legitimization_uuid); + (unsigned long long) kyp->requirement_row); return qs; } if (qs < 0) @@ -305,52 +325,122 @@ kyc_check (void *cls, GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs); return qs; } - GNUNET_free (provider_account_id); - GNUNET_free (provider_legitimization_id); if (0 != GNUNET_memcmp (&kyp->h_payto, &h_payto)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Account %llu provided, but h_payto does not match\n", - (unsigned long long) kyp->legitimization_uuid); + "Requirement %llu provided, but h_payto does not match\n", + (unsigned long long) kyp->requirement_row); GNUNET_break_op (0); *mhd_ret = TALER_MHD_reply_with_error (connection, MHD_HTTP_FORBIDDEN, TALER_EC_EXCHANGE_KYC_CHECK_AUTHORIZATION_FAILED, "h_payto"); + GNUNET_free (requirements); return GNUNET_DB_STATUS_HARD_ERROR; } - kyp->found = true; - if (GNUNET_TIME_absolute_is_future (expiration)) + qs = TALER_KYCLOGIC_check_satisfied ( + &requirements, + &h_payto, + &kyp->kyc_details, + TEH_plugin->select_satisfied_kyc_processes, + TEH_plugin->cls, + &satisfied); + if (qs < 0) { - /* kyc not required, we are done */ - return qs; + if (GNUNET_DB_STATUS_SOFT_ERROR == qs) + return qs; + GNUNET_break (0); + *mhd_ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "kyc_test_required"); + GNUNET_free (requirements); + return GNUNET_DB_STATUS_HARD_ERROR; + } + if (satisfied) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "KYC requirements `%s' already satisfied\n", + requirements); + GNUNET_free (requirements); + return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; } - ret = TALER_KYCLOGIC_kyc_get_logic (kyp->required, - &kyp->ih_logic, - &pd); + kyp->kyc_required = true; + ret = TALER_KYCLOGIC_requirements_to_logic (requirements, + kyp->ut, + &kyp->ih_logic, + &pd, + &kyp->section_name); if (GNUNET_OK != ret) { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "KYC logic for `%s' not configured but used in database!\n", - kyp->required); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "KYC requirements `%s' cannot be checked, but are set as required in database!\n", + requirements); *mhd_ret = TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_GONE, - kyp->required); + requirements); + GNUNET_free (requirements); return GNUNET_DB_STATUS_HARD_ERROR; } + GNUNET_free (requirements); + if (kyp->ih_done) return qs; + qs = TEH_plugin->get_pending_kyc_requirement_process ( + TEH_plugin->cls, + &h_payto, + kyp->section_name, + &redirect_url); + if (qs < 0) + { + if (GNUNET_DB_STATUS_SOFT_ERROR == qs) + return 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_process"); + return GNUNET_DB_STATUS_HARD_ERROR; + } + if ( (qs > 0) && + (NULL != redirect_url) ) + { + kyp->kyc_url = redirect_url; + return qs; + } + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) + { + /* set up new requirement process */ + qs = TEH_plugin->insert_kyc_requirement_process ( + TEH_plugin->cls, + &h_payto, + kyp->section_name, + NULL, + NULL, + &kyp->process_row); + if (qs < 0) + { + if (GNUNET_DB_STATUS_SOFT_ERROR == qs) + return 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_process"); + return GNUNET_DB_STATUS_HARD_ERROR; + } + } GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Initiating KYC check with logic %s\n", - kyp->required); + kyp->ih_logic->name); kyp->ih = kyp->ih_logic->initiate (kyp->ih_logic->cls, pd, &h_payto, - kyp->legitimization_uuid, + kyp->process_row, &initiate_cb, kyp); GNUNET_break (NULL != kyp->ih); @@ -401,7 +491,7 @@ db_event_cb (void *cls, MHD_RESULT TEH_handler_kyc_check ( struct TEH_RequestContext *rc, - const char *const args[2]) + const char *const args[3]) { struct KycPoller *kyp = rc->rh_ctx; MHD_RESULT res; @@ -416,22 +506,22 @@ TEH_handler_kyc_check ( rc->rh_cleaner = &kyp_cleanup; { - unsigned long long legitimization_uuid; + unsigned long long requirement_row; char dummy; if (1 != sscanf (args[0], "%llu%c", - &legitimization_uuid, + &requirement_row, &dummy)) { GNUNET_break_op (0); return TALER_MHD_reply_with_error (rc->connection, MHD_HTTP_BAD_REQUEST, TALER_EC_GENERIC_PARAMETER_MALFORMED, - "legitimization_uuid"); + "requirement_row"); } - kyp->legitimization_uuid = (uint64_t) legitimization_uuid; + kyp->requirement_row = (uint64_t) requirement_row; } if (GNUNET_OK != @@ -447,34 +537,30 @@ TEH_handler_kyc_check ( "h_payto"); } + if (GNUNET_OK != + TALER_KYCLOGIC_kyc_user_type_from_string (args[2], + &kyp->ut)) { - const char *ts; - - ts = MHD_lookup_connection_value (rc->connection, - MHD_GET_ARGUMENT_KIND, - "timeout_ms"); - if (NULL != ts) - { - char dummy; - unsigned long long tms; - - if (1 != - sscanf (ts, - "%llu%c", - &tms, - &dummy)) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "timeout_ms"); - } - kyp->timeout = GNUNET_TIME_relative_to_absolute ( - GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, - tms)); - } + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "usertype"); } + + TALER_MHD_parse_request_timeout (rc->connection, + &kyp->timeout); + } + /* KYC plugin generated reply? */ + if (NULL != kyp->kyc_url) + { + return TALER_MHD_REPLY_JSON_PACK ( + rc->connection, + MHD_HTTP_ACCEPTED, + GNUNET_JSON_pack_uint64 ("aml_status", + kyp->aml_status), + GNUNET_JSON_pack_string ("kyc_url", + kyp->kyc_url)); } if ( (NULL == kyp->eh) && @@ -509,14 +595,36 @@ TEH_handler_kyc_check ( "Transaction failed.\n"); return res; } + /* KYC plugin generated reply? */ + if (NULL != kyp->kyc_url) + { + return TALER_MHD_REPLY_JSON_PACK ( + rc->connection, + MHD_HTTP_ACCEPTED, + GNUNET_JSON_pack_uint64 ("aml_status", + kyp->aml_status), + GNUNET_JSON_pack_string ("kyc_url", + kyp->kyc_url)); + } if ( (NULL == kyp->ih) && - (! kyp->found) ) + (! kyp->kyc_required) ) { + if (TALER_AML_NORMAL != kyp->aml_status) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "KYC is OK, but AML active: %d\n", + (int) kyp->aml_status); + return TALER_MHD_REPLY_JSON_PACK ( + rc->connection, + MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS, + GNUNET_JSON_pack_uint64 ("aml_status", + kyp->aml_status)); + } /* KYC not required */ GNUNET_log (GNUNET_ERROR_TYPE_INFO, "KYC not required %llu\n", - (unsigned long long) kyp->legitimization_uuid); + (unsigned long long) kyp->requirement_row); return TALER_MHD_reply_static ( rc->connection, MHD_HTTP_NO_CONTENT, @@ -538,16 +646,17 @@ TEH_handler_kyc_check ( } /* long polling? */ - if ( (NULL != kyp->required) && + if ( (NULL != kyp->section_name) && GNUNET_TIME_absolute_is_future (kyp->timeout)) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Suspending HTTP request on timeout (%s) now...\n", - GNUNET_TIME_relative2s (GNUNET_TIME_absolute_get_duration ( + GNUNET_TIME_relative2s (GNUNET_TIME_absolute_get_remaining ( kyp->timeout), true)); GNUNET_assert (NULL != kyp->eh); kyp->suspended = true; + kyp->section_name = NULL; GNUNET_CONTAINER_DLL_insert (kyp_head, kyp_tail, kyp); @@ -555,16 +664,6 @@ TEH_handler_kyc_check ( return MHD_YES; } - /* KYC plugin generated reply? */ - if (NULL != kyp->kyc_url) - { - return TALER_MHD_REPLY_JSON_PACK ( - rc->connection, - MHD_HTTP_ACCEPTED, - GNUNET_JSON_pack_string ("kyc_url", - kyp->kyc_url)); - } - if (TALER_EC_NONE != kyp->ec) { return TALER_MHD_reply_with_ec (rc->connection, @@ -582,6 +681,7 @@ TEH_handler_kyc_check ( (ec = TALER_exchange_online_account_setup_success_sign ( &TEH_keys_exchange_sign_, &kyp->h_payto, + kyp->kyc_details, now, &pub, &sig))) @@ -597,6 +697,10 @@ TEH_handler_kyc_check ( &sig), GNUNET_JSON_pack_data_auto ("exchange_pub", &pub), + GNUNET_JSON_pack_uint64 ("aml_status", + kyp->aml_status), + GNUNET_JSON_pack_object_incref ("kyc_details", + kyp->kyc_details), GNUNET_JSON_pack_timestamp ("now", now)); } |