exchange

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

commit 8823084f2ae0f3d5b40795c91a6714d0259cd5e3
parent fd3a61012d093663d77ebf0bb4cac3a011c981b6
Author: Christian Grothoff <christian@grothoff.org>
Date:   Tue, 14 May 2024 20:15:08 +0200

work on kyc API

Diffstat:
Msrc/include/taler_kyclogic_lib.h | 223++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Msrc/kyclogic/kyclogic_api.c | 231++++++++++---------------------------------------------------------------------
Msrc/kyclogic/taler-exchange-kyc-tester.c | 139+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
3 files changed, 293 insertions(+), 300 deletions(-)

diff --git a/src/include/taler_kyclogic_lib.h b/src/include/taler_kyclogic_lib.h @@ -71,6 +71,142 @@ enum TALER_KYCLOGIC_KycTriggerEvent /** + * Types of KYC checks. + */ +enum CheckType +{ + /** + * Wait for staff or contact staff out-of-band. + */ + CT_INFO, + + /** + * SPA should show an inline form. + */ + CT_FORM, + + /** + * SPA may start external KYC process. + */ + CT_LINK +}; + + +/** + * Information about a KYC provider. + */ +struct TALER_KYCLOGIC_KycProvider; + + +/** + * Abstract representation of a KYC check. + */ +struct TALER_KYCLOGIC_KycCheck +{ + /** + * Human-readable name given to the KYC check. + */ + char *check_name; + + /** + * Human-readable description of the check in English. + */ + char *description; + + /** + * Optional translations of @e description, can be + * NULL. + */ + json_t *description_i18n; + + /** + * Array of fields that the context must provide as + * inputs for this check. + */ + char **requires; + + /** + * Name of an original measure to take as a fallback + * in case the check fails. + */ + char *fallback; + + /** + * Array of outputs provided by the check. Names of the attributes provided + * by the check for the AML program. Either from the configuration or + * obtained via the converter. + */ + char **outputs; + + /** + * Length of the @e requires array. + */ + unsigned int num_requires; + + /** + * Length of the @e outputs array. + */ + unsigned int num_outputs; + + /** + * True if clients can voluntarily trigger this check. + */ + bool voluntary; + + /** + * Type of the KYC check. + */ + enum CheckType type; + + /** + * Details depending on @e type. + */ + union + { + + /** + * Fields present only if @e type is #CT_FORM. + */ + struct + { + + /** + * Name of the form to render. + */ + char *name; + + } form; + + /** + * Fields present only if @e type is CT_LINK. + */ + struct + { + + /** + * Provider used. + */ + const struct TALER_KYCLOGIC_KycProvider *provider; + + } link; + + } details; + +}; + + +/** + * Rule that triggers some measure(s). + */ +struct TALER_KYCLOGIC_KycRule; + +/** + * Set of rules that applies to an account. + */ +struct TALER_KYCLOGIC_LegitimizationRuleSet; + + +/** * Parse KYC trigger string value from a string * into enumeration value. * @@ -162,17 +298,6 @@ typedef void /** - * Rule that triggers some measure(s). - */ -struct TALER_KYCLOGIC_KycRule; - -/** - * Set of rules that applies to an account. - */ -struct TALER_KYCLOGIC_LegitimizationRuleSet; - - -/** * Parse set of legitimization rules that applies to an account. * * @param jlrs JSON representation to parse @@ -261,29 +386,65 @@ TALER_KYCLOGIC_is_satisfiable ( /** + * Extract logic data from a KYC @a provider. + * + * @param provider provider to get logic data from + * @param[out] plugin set to the KYC logic API + * @param[out] pd set to the specific operation context + * @param[out] provider_name set to the name + * of the KYC provider + */ +void +TALER_KYCLOGIC_provider_to_logic ( + const struct TALER_KYCLOGIC_KycProvider *provider, + struct TALER_KYCLOGIC_Plugin **plugin, + struct TALER_KYCLOGIC_ProviderDetails **pd, + const char **provider_name); + + +/** + * Tuple with information about a KYC check to perform. Note that it will + * have references into the legitimization rule set provided to + * #TALER_KYCLOGIC_requirements_to_check() and thus has a lifetime that + * matches the legitimization rule set. + */ +struct TALER_KYCLOGIC_KycCheckContext +{ + /** + * KYC check to perform. + */ + const struct TALER_KYCLOGIC_KycCheck *check; + + /** + * Context for the check. Can be NULL. + */ + const json_t *context; + + /** + * Name of the AML program. + */ + char *prog_name; +}; + + +/** * Obtain the provider logic for a given set of @a lrs * and a specific @a kyc_rule from @a lrs that was * triggered and the choosen @a measure_name from the * list of measures of that @a kyc_rule. * - * FIXME: we probably want to instead set up the logic - * with the context instead of just returning it here! - * * @param lrs rule set * @param kyc_rule rule that was triggered * @param measure_name selected measure - * @param[out] plugin set to the KYC logic API - * @param[out] pd set to the specific operation context - * @param[out] configuration_section set to the name of the KYC logic configuration section * @return #GNUNET_OK on success + * @param[out] kcc set to check to run + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error */ enum GNUNET_GenericReturnValue -TALER_KYCLOGIC_requirements_to_logic ( +TALER_KYCLOGIC_requirements_to_check ( const struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs, const struct TALER_KYCLOGIC_KycRule *kyc_rule, const char *measure_name, - struct TALER_KYCLOGIC_Plugin **plugin, - struct TALER_KYCLOGIC_ProviderDetails **pd, - const char **configuration_section); + struct TALER_KYCLOGIC_KycCheckContext *kcc); /** @@ -303,26 +464,6 @@ TALER_KYCLOGIC_lookup_logic ( const char **configuration_section); -// FIXME: we probably want to instead have some -// functionality that returns information that -// is more directly applicable for /keys or /config -// and not this: -/** - * Obtain array of KYC checks provided by the provider - * configured in @a section_name. - * - * @param section_name configuration section name - * @param[out] num_checks set to the length of the array - * @param[out] provided_checks set to an array with the - * names of the checks provided by this KYC provider - */ -void -TALER_KYCLOGIC_lookup_checks ( - const char *section_name, - unsigned int *num_checks, - char ***provided_checks); - - /** * 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 @@ -64,124 +64,6 @@ struct TALER_KYCLOGIC_KycProvider /** - * Types of KYC checks. - */ -enum CheckType -{ - /** - * Wait for staff or contact staff out-of-band. - */ - CT_INFO, - - /** - * SPA should show an inline form. - */ - CT_FORM, - - /** - * SPA may start external KYC process. - */ - CT_LINK -}; - -/** - * Abstract representation of a KYC check. - */ -struct TALER_KYCLOGIC_KycCheck -{ - /** - * Human-readable name given to the KYC check. - */ - char *check_name; - - /** - * Human-readable description of the check in English. - */ - char *description; - - /** - * Optional translations of @e description, can be - * NULL. - */ - json_t *description_i18n; - - /** - * Array of fields that the context must provide as - * inputs for this check. - */ - char **requires; - - /** - * Length of the @e requires array. - */ - unsigned int num_requires; - - /** - * Name of an original measure to take as a fallback - * in case the check fails. - */ - char *fallback; - - /** - * Array of outputs provided by the check. Names of the attributes provided - * by the check for the AML program. Either from the configuration or - * obtained via the converter. - */ - char **outputs; - - /** - * Length of the @e outputs array. - */ - unsigned int num_outputs; - - /** - * True if clients can voluntarily trigger this check. - */ - bool voluntary; - - /** - * Type of the KYC check. - */ - enum CheckType type; - - /** - * Details depending on @e type. - */ - union - { - - /** - * Fields present only if @e type is #CT_FORM. - */ - struct - { - - /** - * Name of the form to render. - */ - char *name; - - } form; - - /** - * Fields present only if @e type is CT_LINK. - */ - struct - { - - /** - * Provider used. - */ - const struct TALER_KYCLOGIC_KycProvider *provider; - - } link; - - } details; - -}; - - -/** * Rule that triggers some measure(s). */ struct TALER_KYCLOGIC_KycRule @@ -1755,19 +1637,30 @@ TALER_KYCLOGIC_kyc_done (void) } +void +TALER_KYCLOGIC_provider_to_logic ( + const struct TALER_KYCLOGIC_KycProvider *provider, + struct TALER_KYCLOGIC_Plugin **plugin, + struct TALER_KYCLOGIC_ProviderDetails **pd, + const char **provider_name) +{ + *plugin = provider->logic; + *pd = provider->pd; + *provider_name = provider->provider_name; +} + + enum GNUNET_GenericReturnValue -TALER_KYCLOGIC_requirements_to_logic ( +TALER_KYCLOGIC_requirements_to_check ( const struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs, const struct TALER_KYCLOGIC_KycRule *kyc_rule, const char *measure_name, - struct TALER_KYCLOGIC_Plugin **plugin, - struct TALER_KYCLOGIC_ProviderDetails **pd, - const char **configuration_section) + struct TALER_KYCLOGIC_KycCheckContext *kcc) { bool found = false; const struct TALER_KYCLOGIC_Measure *measure = NULL; - for (unsigned int i=0; i<kyc_rule->num_measures; i++) + for (unsigned int i = 0; i<kyc_rule->num_measures; i++) { if (0 != strcmp (measure_name, kyc_rule->next_measures[i])) @@ -1791,7 +1684,7 @@ TALER_KYCLOGIC_requirements_to_logic ( } if (NULL != lrs) { - for (unsigned int i=0; i<lrs->num_custom_measures; i++) + for (unsigned int i = 0; i<lrs->num_custom_measures; i++) { const struct TALER_KYCLOGIC_Measure *cm = &lrs->custom_measures[i]; @@ -1806,7 +1699,7 @@ TALER_KYCLOGIC_requirements_to_logic ( if (NULL == measure) { /* Try measures from default rules */ - for (unsigned int i=0; i<default_rules.num_custom_measures; i++) + for (unsigned int i = 0; i<default_rules.num_custom_measures; i++) { const struct TALER_KYCLOGIC_Measure *cm = &default_rules.custom_measures[i]; @@ -1827,86 +1720,20 @@ TALER_KYCLOGIC_requirements_to_logic ( return GNUNET_SYSERR; } -#if FIXME - struct TALER_KYCLOGIC_KycCheck *needed[num_kyc_checks]; - unsigned int needed_cnt = 0; - unsigned long long min_cost = ULLONG_MAX; - unsigned int max_checks = 0; - const struct TALER_KYCLOGIC_KycProvider *kp_best = NULL; - - if (NULL == requirements) - return GNUNET_NO; - { - char *req = GNUNET_strdup (requirements); - - for (const char *tok = strtok (req, " "); - NULL != tok; - tok = strtok (NULL, " ")) - needed[needed_cnt++] = add_check (tok); - GNUNET_free (req); - } - - /* Count maximum number of remaining checks covered by any - provider */ - for (unsigned int i = 0; i<num_kyc_providers; i++) - { - const struct TALER_KYCLOGIC_KycProvider *kp = kyc_providers[i]; - unsigned int matched = 0; - - if (kp->user_type != ut) - continue; - for (unsigned int j = 0; j<kp->num_checks; j++) - { - const struct TALER_KYCLOGIC_KycCheck *kc = kp->provided_checks[j]; - - for (unsigned int k = 0; k<needed_cnt; k++) - if (kc == needed[k]) - { - matched++; - break; - } - } - max_checks = GNUNET_MAX (max_checks, - matched); - } - if (0 == max_checks) - return GNUNET_SYSERR; - - /* Find min-cost provider covering max_checks. */ - for (unsigned int i = 0; i<num_kyc_providers; i++) - { - const struct TALER_KYCLOGIC_KycProvider *kp = kyc_providers[i]; - unsigned int matched = 0; - - if (kp->user_type != ut) - continue; - for (unsigned int j = 0; j<kp->num_checks; j++) - { - const struct TALER_KYCLOGIC_KycCheck *kc = kp->provided_checks[j]; - - for (unsigned int k = 0; k<needed_cnt; k++) - if (kc == needed[k]) - { - matched++; - break; - } - } - if ( (max_checks == matched) && - (kp->cost < min_cost) ) + for (unsigned int i = 0; i<num_kyc_checks; i++) + if (0 == strcmp (measure->check_name, + kyc_checks[i]->check_name)) { - min_cost = kp->cost; - kp_best = kp; + kcc->check = kyc_checks[i]; + kcc->prog_name = measure->prog_name; + kcc->context = measure->context; + return GNUNET_OK; } - } - GNUNET_assert (NULL != kp_best); - *plugin = kp_best->logic; - *pd = kp_best->pd; - *configuration_section = kp_best->provider_section_name; - return GNUNET_OK; -#else - GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Check `%s' unknown (but required by measure %s)\n", + measure->check_name, + measure_name); return GNUNET_SYSERR; -#endif } diff --git a/src/kyclogic/taler-exchange-kyc-tester.c b/src/kyclogic/taler-exchange-kyc-tester.c @@ -252,12 +252,6 @@ static char *cmd_provider_user_id; static char *cmd_provider_legitimization_id; /** - * Name of the configuration section with the - * configuration data of the selected provider. - */ -static const char *provider_section_name; - -/** * Custom legitimization rule in JSON given as * a string. */ @@ -961,10 +955,11 @@ proceed_with_handler (struct TEKT_RequestContext *rc, /deposits/). The value should be adjusted if we ever define protocol endpoints with plausibly longer inputs. */ GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_URI_TOO_LONG, - TALER_EC_GENERIC_URI_TOO_LONG, - url); + return TALER_MHD_reply_with_error ( + rc->connection, + MHD_HTTP_URI_TOO_LONG, + TALER_EC_GENERIC_URI_TOO_LONG, + url); } /* All POST endpoints come with a body in JSON format. So we parse @@ -975,11 +970,12 @@ proceed_with_handler (struct TEKT_RequestContext *rc, { enum GNUNET_GenericReturnValue res; - res = TALER_MHD_parse_post_json (rc->connection, - &rc->opaque_post_parsing_context, - upload_data, - upload_data_size, - &rc->root); + res = TALER_MHD_parse_post_json ( + rc->connection, + &rc->opaque_post_parsing_context, + upload_data, + upload_data_size, + &rc->root); if (GNUNET_SYSERR == res) { GNUNET_assert (NULL == rc->root); @@ -1026,10 +1022,11 @@ proceed_with_handler (struct TEKT_RequestContext *rc, rh->url, url); GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_EXCHANGE_GENERIC_WRONG_NUMBER_OF_SEGMENTS, - emsg); + return TALER_MHD_reply_with_error ( + rc->connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_EXCHANGE_GENERIC_WRONG_NUMBER_OF_SEGMENTS, + emsg); } GNUNET_assert (NULL == args[i - 1]); @@ -1257,12 +1254,14 @@ handle_mhd_request (void *cls, allowed = tmp; } } - reply = TALER_MHD_make_error (TALER_EC_GENERIC_METHOD_INVALID, - method); - GNUNET_break (MHD_YES == - MHD_add_response_header (reply, - MHD_HTTP_HEADER_ALLOW, - allowed)); + reply = TALER_MHD_make_error ( + TALER_EC_GENERIC_METHOD_INVALID, + method); + GNUNET_break ( + MHD_YES == + MHD_add_response_header (reply, + MHD_HTTP_HEADER_ALLOW, + allowed)); GNUNET_free (allowed); ret = MHD_queue_response (connection, MHD_HTTP_METHOD_NOT_ALLOWED, @@ -1613,11 +1612,11 @@ run (void *cls, if (NULL != rule) { - struct TALER_KYCLOGIC_ProviderDetails *pd; + struct TALER_KYCLOGIC_KycCheckContext kcc; if (NULL == measure) { - // FIXME: print rule! + // FIXME: print rule with possible measures! global_ret = EXIT_SUCCESS; GNUNET_SCHEDULER_shutdown (); @@ -1625,12 +1624,10 @@ run (void *cls, } if (GNUNET_OK != - TALER_KYCLOGIC_requirements_to_logic (lrs, + TALER_KYCLOGIC_requirements_to_check (lrs, rule, measure, - &ih_logic, - &pd, - &provider_section_name)) + &kcc)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not initiate KYC for measure `%s' (configuration error?)\n", @@ -1639,16 +1636,43 @@ run (void *cls, GNUNET_SCHEDULER_shutdown (); return; } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Initiating KYC at provider `%s'\n", - provider_section_name); - ih = ih_logic->initiate (ih_logic->cls, - pd, - &cmd_line_h_payto, - kyc_row_id, - &initiate_cb, - NULL); - GNUNET_break (NULL != ih); + switch (kcc.check->type) + { + case CT_INFO: + GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, + "KYC information is `%s'\n", + kcc.check->description); + break; + case CT_FORM: + GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, + "Would initiate KYC check `%s' with form `%s'\n", + kcc.check->check_name, + kcc.check->details.form.name); + break; + case CT_LINK: + { + struct TALER_KYCLOGIC_ProviderDetails *pd; + const char *provider_name; + + TALER_KYCLOGIC_provider_to_logic ( + kcc.check->details.link.provider, + &ih_logic, + &pd, + &provider_name); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Initiating KYC check `%s' at provider `%s'\n", + kcc.check->check_name, + provider_name); + ih = ih_logic->initiate (ih_logic->cls, + pd, + &cmd_line_h_payto, + kyc_row_id, + &initiate_cb, + NULL); + GNUNET_break (NULL != ih); + break; + } + } } if (run_webservice) { @@ -1675,22 +1699,23 @@ run (void *cls, GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Starting daemon on port %u\n", (unsigned int) serve_port); - mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME - | MHD_USE_PIPE_FOR_SHUTDOWN - | MHD_USE_DEBUG | MHD_USE_DUAL_STACK - | MHD_USE_TCP_FASTOPEN, - (-1 == fh) ? serve_port : 0, - NULL, NULL, - &handle_mhd_request, NULL, - MHD_OPTION_LISTEN_SOCKET, - fh, - MHD_OPTION_EXTERNAL_LOGGER, - &TALER_MHD_handle_logs, - NULL, - MHD_OPTION_NOTIFY_COMPLETED, - &handle_mhd_completion_callback, - NULL, - MHD_OPTION_END); + mhd = MHD_start_daemon ( + MHD_USE_SUSPEND_RESUME + | MHD_USE_PIPE_FOR_SHUTDOWN + | MHD_USE_DEBUG | MHD_USE_DUAL_STACK + | MHD_USE_TCP_FASTOPEN, + (-1 == fh) ? serve_port : 0, + NULL, NULL, + &handle_mhd_request, NULL, + MHD_OPTION_LISTEN_SOCKET, + fh, + MHD_OPTION_EXTERNAL_LOGGER, + &TALER_MHD_handle_logs, + NULL, + MHD_OPTION_NOTIFY_COMPLETED, + &handle_mhd_completion_callback, + NULL, + MHD_OPTION_END); if (NULL == mhd) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR,