From 266068c96cbe64d84da2c79fc6db56e8c56ba6b5 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 2 Aug 2022 12:05:57 +0200 Subject: work on KYC configuration parsing logic --- src/exchange/taler-exchange-httpd_kyc.c | 490 ++++++++++++++++++++++++++++++-- src/exchange/taler-exchange-httpd_kyc.h | 4 +- 2 files changed, 472 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/exchange/taler-exchange-httpd_kyc.c b/src/exchange/taler-exchange-httpd_kyc.c index dd5a334bd..cb7f73c65 100644 --- a/src/exchange/taler-exchange-httpd_kyc.c +++ b/src/exchange/taler-exchange-httpd_kyc.c @@ -60,7 +60,7 @@ struct TEH_KycProvider /** * Array of @e num_checks checks performed by this provider. */ - struct TEH_KycCheck *provided_checks; + struct TEH_KycCheck **provided_checks; /** * Logic to run for this provider. @@ -102,12 +102,12 @@ struct TEH_KycTrigger * Maximum amount that can be transacted until * the rule triggers. */ - struct TALER_Amount limit; + struct TALER_Amount threshold; /** * Array of @e num_checks checks to apply on this trigger. */ - struct TEH_KycCheck *required_checks; + struct TEH_KycCheck **required_checks; /** * Length of the @e checks array. @@ -125,7 +125,7 @@ struct TEH_KycTrigger /** * Array of @e num_kyc_logics KYC logic plugins we have loaded. */ -static struct TEH_KYCLOGIC_Plugin *kyc_logics; +static struct TEH_KYCLOGIC_Plugin **kyc_logics; /** * Length of the #kyc_logics array. @@ -136,7 +136,7 @@ static unsigned in num_kyc_logics; * Array of @e num_kyc_checks known types of * KYC checks. */ -static struct TEH_KycCheck *kyc_checks; +static struct TEH_KycCheck **kyc_checks; /** * Length of the #kyc_checks array. @@ -146,7 +146,7 @@ static unsigned int num_kyc_checks; /** * Array of configured triggers. */ -static struct TEH_KycTrigger *kyc_triggers; +static struct TEH_KycTrigger **kyc_triggers; /** * Length of the #kyc_triggers array. @@ -156,7 +156,7 @@ static unsigned int num_kyc_triggers; /** * Array of configured providers. */ -static struct TEH_KycProviders *kyc_providers; +static struct TEH_KycProvider *kyc_providers; /** * Length of the #kyc_providers array. @@ -168,7 +168,28 @@ enum GNUNET_GenericReturnValue TEH_kyc_trigger_from_string (const char *trigger_s, enum TEH_KycTriggerEvent *trigger) { - GNUNET_break (0); + struct + { + const char *in; + enum TEH_KycTriggerEvent out; + } map [] = { + { "withdraw", TEH_KYC_TRIGGER_WITHDRAW }, + { "deposit", TEH_KYC_TRIGGER_DEPOSIT }, + { "merge", TEH_KYC_TRIGGER_P2P_RECEIVE }, + { "balance", TEH_KYC_TRIGGER_WALLET_BALANCE }, + { NULL, 0 } + }; + + for (unsigned int i = 0; NULL != map[i].in; i++) + if (0 == strcasecmp (map[i].in, + trigger_s)) + { + *trigger = map[i].out; + return GNUNET_OK; + } + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Invalid KYC trigger `%s'\n", + trigger_s); return GNUNET_SYSERR; } @@ -176,6 +197,17 @@ TEH_kyc_trigger_from_string (const char *trigger_s, const char * TEH_kyc_trigger2s (enum TEH_KycTriggerEvent trigger) { + switch (trigger) + { + case TEH_KYC_TRIGGER_WITHDRAW: + return "withdraw"; + case TEH_KYC_TRIGGER_DEPOSIT: + return "deposit"; + case TEH_KYC_TRIGGER_P2P_RECEIVE: + return "merge"; + case TEH_KYC_TRIGGER_WALLET_BALANCE: + return "balance"; + } GNUNET_break (0); return NULL; } @@ -185,27 +217,388 @@ enum GNUNET_GenericReturnValue TEH_kyc_user_type_from_string (const char *ut_s, enum TEH_KycUserType *ut) { - GNUNET_break (0); + struct + { + const char *in; + enum TEH_KycTriggerEvent out; + } map [] = { + { "individual", TEH_KYC_UT_INDIVIDUAL }, + { "business", TEH_KYC_UT_BUSINESS }, + { NULL, 0 } + }; + + for (unsigned int i = 0; NULL != map[i].in; i++) + if (0 == strcasecmp (map[i].in, + ut_s)) + { + *ut = map[i].out; + return GNUNET_OK; + } + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Invalid user type `%s'\n", + ut_s); return GNUNET_SYSERR; } const char * TEH_kyc_user_type2s (enum TEH_KycUserType ut) +{ + switch (ut) + { + case TEH_KYC_UT_INDIVIDUAL: + return "individual"; + case TEH_KYC_UT_BUSINESS: + return "business"; + } + GNUNET_break (0); + return NULL; +} + + +/** + * Load KYC logic plugin. + * + * @param name name of the plugin + * @return NULL on error + */ +static struct TEH_KYCLOGIC_Plugin * +load_logic (const char *name) { GNUNET_break (0); return NULL; } +/** + * Add check type to global array of checks. + * First checks if the type already exists, otherwise + * adds a new one. + * + * @param check name of the check + * @return pointer into the global list + */ +static struct TEH_KycCheck * +add_check (const char *check) +{ + struct TEH_KycCheck *kc; + + for (unsigned int i = 0; iname)) + return kyc_checks[i]; + kc = GNUNET_new (struct TEH_KycCheck); + kc->name = GNUNET_strdup (check); + GNUNET_array_append (kyc_checks, + num_kyc_checks, + kc); + return kc; +} + + +/** + * Parse list of checks from @a checks and build an + * array of aliases into the global checks array + * in @a provided_checks. + * + * @param[in,out] checks list of checks; clobbered + * @param[out] p_checks where to put array of aliases + * @param[out] num_p_checks set to length of @a p_checks array + */ +static void +add_checks (char *checks, + struct TEH_KycCheck **p_checks, + unsigned int *num_p_checks) +{ + char *sptr; + struct TEH_KycCheck *rchecks = NULL; + unsigned int num_rchecks = 0; + + for (char *tok = strtok_r (checks, " ", &sptr); + NULL != tok; + tok = strtok_r (checks, NULL, &sptr)) + { + struct TEH_KycCheck *kc; + + kc = add_check (tok); + GNUNET_array_append (rchecks, + num_rchecks, + kc); + } + *p_checks = rchecks; + *num_p_checks = num_rchecks; +} + + +/** + * Parse configuration of a KYC provider. + * + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +add_provider (const char *section) +{ + unsigned long long cost; + char *logic; + char *ut_s; + enum TEH_KycUserType ut; + char *checks; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (TEH_cfg, + section, + "COST", + &cost)) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section, + "COST", + "number required"); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (TEH_cfg, + section, + "USER_TYPE", + &ut_s)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + section, + "USER_TYPE"); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + TEH_kyc_user_type_from_string (ut_s, + &ut)) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section, + "USER_TYPE", + "valid user type required"); + GNUNET_free (ut_s); + return GNUNET_SYSERR; + } + GNUNET_free (ut_s); + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (TEH_cfg, + section, + "LOGIC", + &logic)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + section, + "LOGIC"); + return GNUNET_SYSERR; + } + lp = load_logic (logic); + if (NULL == lp) + { + GNUNET_free (logic); + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section, + "LOGIC", + "logic plugin could not be loaded"); + return GNUNET_SYSERR; + } + GNUNET_free (logic); + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (TEH_cfg, + section, + "PROVIDED_CHECKS", + &checks)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + section, + "PROVIDED_CHECKS"); + return GNUNET_SYSERR; + } + { + struct TEH_KycProvider *kp; + + kp = GNUNET_new (struct TEH_KycProvider); + kp->provider_section_name = section; + kp->user_type = ut; + kp->logic = lp; + add_checks (checks, + &kp->provided_checks, + &kp->num_checks); + GNUNET_free (checks); + kp->pd = lp->load (lp->cls, + section); + if (NULL == kp->pd) + { + GNUNET_free (kp); + return GNUNET_SYSERR; + } + GNUNET_array_append (kyc_providers, + num_kyc_providers, + kp); + for (unsigned int i = 0; inum_checks; i++) + { + struct TEH_KycCheck *kc = kp->provided_checks[i]; + + GNUNET_array_append (kc->providers, + kc->num_providers, + kp); + } + } + return GNUNET_OK; +} + + +static enum GNUNET_GenericReturnValue +add_trigger (const char *section) +{ + char *ot_s; + struct TALER_Amount threshold; + struct GNUNET_TIME_Relative timeframe; + char *checks; + enum TEH_KycTriggerEvent ot; + + if (GNUNET_OK != + TALER_CONFIGURATION_get_value_amount (TEH_cfg, + section, + "THRESHOLD", + &threshold)) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section, + "THRESHOLD", + "amount required"); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (TEH_cfg, + section, + "OPERATION_TYPE", + &ot_s)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + section, + "OPERATION_TYPE"); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + TEH_kyc_trigger_from_string (ot_s, + &ot)) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section, + "OPERATION_TYPE", + "valid trigger type required"); + GNUNET_free (ot_s); + return GNUNET_SYSERR; + } + GNUNET_free (ot_s); + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_time (TEH_cfg, + section, + "TIMEFRAME", + &timeframe)) + { + if (TEH_KYC_TRIGGER_WALLET_BALANCE == ot) + { + timeframe = GNUNET_TIME_UNIT_ZERO; + } + else + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section, + "TIMEFRAME", + "duration required"); + return GNUNET_SYSERR; + } + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (TEH_cfg, + section, + "REQUIRED_CHECKS", + &checks)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + section, + "REQUIRED_CHECKS"); + return GNUNET_SYSERR; + } + + { + struct TEH_KycTrigger *kt; + + kt = GNUNET_new (struct TEH_KycTrigger); + kt->timeframe = timeframe; + kt->threshold = threshold; + kt->trigger = ot; + add_checks (checks, + &kt->required_checks, + &kt->num_checks); + GNUNET_free (checks); + GNUNET_array_append (kyc_checks, + num_kyc_checks, + kt); + } + return GNUNET_OK; +} + + +/** + * Function to iterate over configuration sections. + * + * @param cls closure, `boolean *`, set to false on failure + * @param section name of the section + */ +static void +handle_section (void *cls, + const char *section) +{ + bool *ok = cls; + + if (0 == strncasecmp (section, + "kyc-provider-", + strlen ("kyc-provider-"))) + { + if (GNUNET_OK != + add_provider (section)) + *ok = false; + return; + } + if (0 == strncasecmp (section, + "kyc-legitimization-", + strlen ("kyc-legitimization-"))) + { + if (GNUNET_OK != + add_trigger (section)) + *ok = false; + return; + } +} + + enum GNUNET_GenericReturnValue TEH_kyc_init (void) { - GNUNET_break (0); - // iterate over configuration sections, - // initialize arrays above - // sanity check: ensure at least one provider exists - // for any trigger and indidivual or business. + book ok = true; + + GNUNET_CONFIGURATION_iterate_sections (TEH_cfg, + &handle_section, + &ok); + if (! ok) + { + TEH_kyc_done (); + return GNUNET_SYSERR; + } + + /* sanity check: ensure at least one provider exists + for any trigger and indidivual or business. */ + for (unsigned int i = 0; inum_providers) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "No provider available for required KYC check `%s'\n", + kyc_checks[i]->name); + TEH_kyc_done (); + return GNUNET_SYSERR; + } return GNUNET_OK; } @@ -214,8 +607,50 @@ TEH_kyc_init (void) void TEH_kyc_done (void) { - // unload plugins - // free arrays + for (unsigned int i = 0; irequired_checks, + kt->num_checks, + 0); + GNUNET_free (kt); + } + GNUNET_array_grow (kyc_triggers, + num_kyc_triggers, + 0); + for (unsigned int i = 0; ilogic->unload (kp->pd); + GNUNET_array_grow (kp->provided_checks, + kp->num_checks, + 0); + GNUNET_free (kp); + } + GNUNET_array_grow (kyc_providers, + num_kyc_providers, + 0); + for (unsigned int i = 0; iname); + GNUNET_free (kc); + } + GNUNET_array_grow (kyc_checks, + num_kyc_checks, + 0); } @@ -227,7 +662,7 @@ TEH_kyc_test_required (enum TEH_KycTriggerEvent event, { // Check if event(s) may at all require KYC. // If so, check what provider checks are - // already satisified for h_payto (with database) + // already satisfied for h_payto (with database) // If unsatisfied checks are left, use 'ai' // to check if amount is high enough to trigger them. // If it is, find cheapest provider that satisfies @@ -243,8 +678,23 @@ TEH_kyc_get_logic (const char *provider_section_name, struct TEH_KYCLOGIC_Plugin **plugin, struct TEH_KYCLOGIC_ProviderDetails **pd) { - // lookup provider by section name in array, - // return internal plugin/pd fields. - GNUNET_break (0); + for (unsigned int i = 0; iprovider_section_name)) + continue; + *plugin = kp->logic; + *pd = kp->pd; + return GNUNET_OK; + } + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Provider `%s' unknown\n", + provider_section_name); return GNUNET_SYSERR; } + + +/* end of taler-exchange-httpd_kyc.c */ diff --git a/src/exchange/taler-exchange-httpd_kyc.h b/src/exchange/taler-exchange-httpd_kyc.h index 51883caca..0061f6584 100644 --- a/src/exchange/taler-exchange-httpd_kyc.h +++ b/src/exchange/taler-exchange-httpd_kyc.h @@ -33,12 +33,12 @@ enum TEH_KycUserType /** * KYC rule is for an individual. */ - TEH_KYC_INDIVIDUAL = 0, + TEH_KYC_UT_INDIVIDUAL = 0, /** * KYC rule is for a business. */ - TEH_KYC_BUSINESS = 1 + TEH_KYC_UT_BUSINESS = 1 }; -- cgit v1.2.3