work on KYC configuration parsing logic

This commit is contained in:
Christian Grothoff 2022-08-02 12:05:57 +02:00
parent 0835669986
commit 266068c96c
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
2 changed files with 472 additions and 22 deletions

View File

@ -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,7 +217,26 @@ 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;
}
@ -193,19 +244,361 @@ TEH_kyc_user_type_from_string (const char *ut_s,
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; i<num_kyc_checks; i++)
if (0 == strcasecmp (check,
kyc_checks[i]->name))
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; i<kp->num_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; i<num_kyc_checks; i++)
if (0 == kyc_checks[i]->num_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; i<num_kyc_triggers; i++)
{
struct TEH_KycTrigger *kt = kyc_triggers[i];
GNUNET_array_grow (kt->required_checks,
kt->num_checks,
0);
GNUNET_free (kt);
}
GNUNET_array_grow (kyc_triggers,
num_kyc_triggers,
0);
for (unsigned int i = 0; i<num_kyc_providers; i++)
{
struct TEH_KycProvider *kp = kyc_providers[i];
kp->logic->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; i<num_kyc_logics; i++)
{
struct TEH_KYCLOGIC_Plugin *lp = kyc_logics[i];
unload_plugin (lp);
}
GNUNET_array_grow (kyc_logics,
num_kyc_logics,
0);
for (unsigned int i = 0; i<num_kyc_checks; i++)
{
struct TEH_KycCheck *kc = kyc_checks[i];
GNUNET_free (kc->name);
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; i<num_kyc_providers; i++)
{
struct TEH_KycProvider *kp = kyc_providers[i];
if (0 !=
strcasecmp (provider_section_name,
kp->provider_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 */

View File

@ -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
};