summaryrefslogtreecommitdiff
path: root/src/kyclogic/kyclogic_api.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/kyclogic/kyclogic_api.c')
-rw-r--r--src/kyclogic/kyclogic_api.c268
1 files changed, 226 insertions, 42 deletions
diff --git a/src/kyclogic/kyclogic_api.c b/src/kyclogic/kyclogic_api.c
index 0c4a51124..186799dbb 100644
--- a/src/kyclogic/kyclogic_api.c
+++ b/src/kyclogic/kyclogic_api.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2022 Taler Systems SA
+ Copyright (C) 2022-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
@@ -22,6 +22,12 @@
#include "taler_kyclogic_lib.h"
/**
+ * Name of the KYC check that may never be passed. Useful if some
+ * operations/amounts are categorically forbidden.
+ */
+#define KYC_CHECK_IMPOSSIBLE "impossible"
+
+/**
* Information about a KYC provider.
*/
struct TALER_KYCLOGIC_KycProvider;
@@ -180,6 +186,7 @@ TALER_KYCLOGIC_kyc_trigger_from_string (const char *trigger_s,
enum TALER_KYCLOGIC_KycTriggerEvent out;
} map [] = {
{ "withdraw", TALER_KYCLOGIC_KYC_TRIGGER_WITHDRAW },
+ { "age-withdraw", TALER_KYCLOGIC_KYC_TRIGGER_AGE_WITHDRAW },
{ "deposit", TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT },
{ "merge", TALER_KYCLOGIC_KYC_TRIGGER_P2P_RECEIVE },
{ "balance", TALER_KYCLOGIC_KYC_TRIGGER_WALLET_BALANCE },
@@ -208,6 +215,8 @@ TALER_KYCLOGIC_kyc_trigger2s (enum TALER_KYCLOGIC_KycTriggerEvent trigger)
{
case TALER_KYCLOGIC_KYC_TRIGGER_WITHDRAW:
return "withdraw";
+ case TALER_KYCLOGIC_KYC_TRIGGER_AGE_WITHDRAW:
+ return "age-withdraw";
case TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT:
return "deposit";
case TALER_KYCLOGIC_KYC_TRIGGER_P2P_RECEIVE:
@@ -265,6 +274,38 @@ TALER_KYCLOGIC_kyc_user_type2s (enum TALER_KYCLOGIC_KycUserType ut)
}
+enum GNUNET_GenericReturnValue
+TALER_KYCLOGIC_check_satisfiable (
+ const char *check_name)
+{
+ for (unsigned int i = 0; i<num_kyc_checks; i++)
+ if (0 == strcmp (check_name,
+ kyc_checks[i]->name))
+ return GNUNET_OK;
+ if (0 == strcmp (check_name,
+ KYC_CHECK_IMPOSSIBLE))
+ return GNUNET_NO;
+ return GNUNET_SYSERR;
+}
+
+
+json_t *
+TALER_KYCLOGIC_get_satisfiable ()
+{
+ json_t *requirements;
+
+ requirements = json_array ();
+ GNUNET_assert (NULL != requirements);
+ for (unsigned int i = 0; i<num_kyc_checks; i++)
+ GNUNET_assert (
+ 0 ==
+ json_array_append_new (
+ requirements,
+ json_string (kyc_checks[i]->name)));
+ return requirements;
+}
+
+
/**
* Load KYC logic plugin.
*
@@ -306,9 +347,8 @@ load_logic (const struct GNUNET_CONFIGURATION_Handle *cfg,
/**
- * Add check type to global array of checks.
- * First checks if the type already exists, otherwise
- * adds a new one.
+ * 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
@@ -332,9 +372,8 @@ add_check (const char *check)
/**
- * Parse list of checks from @a checks and build an
- * array of aliases into the global checks array
- * in @a provided_checks.
+ * 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
@@ -586,6 +625,29 @@ add_trigger (const struct GNUNET_CONFIGURATION_Handle *cfg,
GNUNET_array_append (kyc_triggers,
num_kyc_triggers,
kt);
+ for (unsigned int i = 0; i<kt->num_checks; i++)
+ {
+ const struct TALER_KYCLOGIC_KycCheck *ck = kt->required_checks[i];
+
+ if (0 != ck->num_providers)
+ continue;
+ if (0 == strcmp (ck->name,
+ KYC_CHECK_IMPOSSIBLE))
+ continue;
+ {
+ char *msg;
+
+ GNUNET_asprintf (&msg,
+ "Required check `%s' cannot be satisfied: not provided by any provider",
+ ck->name);
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ section,
+ "REQUIRED_CHECKS",
+ msg);
+ GNUNET_free (msg);
+ }
+ return GNUNET_SYSERR;
+ }
}
return GNUNET_OK;
}
@@ -615,8 +677,8 @@ struct SectionContext
* @param section name of the section
*/
static void
-handle_section (void *cls,
- const char *section)
+handle_provider_section (void *cls,
+ const char *section)
{
struct SectionContext *sc = cls;
@@ -630,6 +692,21 @@ handle_section (void *cls,
sc->result = false;
return;
}
+}
+
+
+/**
+ * Function to iterate over configuration sections.
+ *
+ * @param cls a `struct SectionContext *`
+ * @param section name of the section
+ */
+static void
+handle_trigger_section (void *cls,
+ const char *section)
+{
+ struct SectionContext *sc = cls;
+
if (0 == strncasecmp (section,
"kyc-legitimization-",
strlen ("kyc-legitimization-")))
@@ -681,7 +758,10 @@ TALER_KYCLOGIC_kyc_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
};
GNUNET_CONFIGURATION_iterate_sections (cfg,
- &handle_section,
+ &handle_provider_section,
+ &sc);
+ GNUNET_CONFIGURATION_iterate_sections (cfg,
+ &handle_trigger_section,
&sc);
if (! sc.result)
{
@@ -700,10 +780,11 @@ TALER_KYCLOGIC_kyc_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
TALER_KYCLOGIC_kyc_done ();
return GNUNET_SYSERR;
}
- qsort (kyc_triggers,
- num_kyc_triggers,
- sizeof (struct TALER_KYCLOGIC_KycTrigger *),
- &sort_by_timeframe);
+ if (0 != num_kyc_triggers)
+ qsort (kyc_triggers,
+ num_kyc_triggers,
+ sizeof (struct TALER_KYCLOGIC_KycTrigger *),
+ &sort_by_timeframe);
return GNUNET_OK;
}
@@ -934,6 +1015,10 @@ struct RemoveContext
*/
unsigned int *needed_cnt;
+ /**
+ * Object with information about collected KYC data.
+ */
+ json_t *kyc_details;
};
@@ -967,6 +1052,14 @@ remove_satisfied (void *cls,
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Provider satisfies check `%s'\n",
kc->name);
+ if (NULL != rc->kyc_details)
+ {
+ GNUNET_assert (0 ==
+ json_object_set_new (
+ rc->kyc_details,
+ kc->name,
+ json_object ()));
+ }
for (unsigned int k = 0; k<*rc->needed_cnt; k++)
if (kc == rc->needed[k])
{
@@ -985,13 +1078,14 @@ remove_satisfied (void *cls,
}
-const char *
+enum GNUNET_DB_QueryStatus
TALER_KYCLOGIC_kyc_test_required (enum TALER_KYCLOGIC_KycTriggerEvent event,
const struct TALER_PaytoHashP *h_payto,
TALER_KYCLOGIC_KycSatisfiedIterator ki,
void *ki_cls,
TALER_KYCLOGIC_KycAmountIterator ai,
- void *ai_cls)
+ void *ai_cls,
+ char **required)
{
struct TALER_KYCLOGIC_KycCheck *needed[num_kyc_checks];
unsigned int needed_cnt = 0;
@@ -1024,7 +1118,10 @@ TALER_KYCLOGIC_kyc_test_required (enum TALER_KYCLOGIC_KycTriggerEvent event,
&ttc);
}
if (0 == needed_cnt)
- return NULL;
+ {
+ *required = NULL;
+ return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
+ }
timeframe = GNUNET_TIME_UNIT_ZERO;
for (unsigned int i = 0; i<num_kyc_triggers; i++)
{
@@ -1051,7 +1148,10 @@ TALER_KYCLOGIC_kyc_test_required (enum TALER_KYCLOGIC_KycTriggerEvent event,
&ttc);
}
if (0 == needed_cnt)
- return NULL;
+ {
+ *required = NULL;
+ return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
+ }
{
struct RemoveContext rc = {
.needed = needed,
@@ -1065,10 +1165,17 @@ TALER_KYCLOGIC_kyc_test_required (enum TALER_KYCLOGIC_KycTriggerEvent event,
h_payto,
&remove_satisfied,
&rc);
- GNUNET_break (qs >= 0); // FIXME: handle DB failure more nicely?
+ if (qs < 0)
+ {
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ return qs;
+ }
}
if (0 == needed_cnt)
- return NULL;
+ {
+ *required = NULL;
+ return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
+ }
{
struct RemoveContext rc = {
.needed = needed,
@@ -1082,10 +1189,17 @@ TALER_KYCLOGIC_kyc_test_required (enum TALER_KYCLOGIC_KycTriggerEvent event,
h_payto,
&remove_satisfied,
&rc);
- GNUNET_break (qs >= 0); // FIXME: handle DB failure more nicely?
+ if (qs < 0)
+ {
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ return qs;
+ }
}
if (0 == needed_cnt)
- return NULL;
+ {
+ *required = NULL;
+ return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
+ }
ret = NULL;
for (unsigned int k = 0; k<needed_cnt; k++)
{
@@ -1106,7 +1220,8 @@ TALER_KYCLOGIC_kyc_test_required (enum TALER_KYCLOGIC_KycTriggerEvent event,
GNUNET_free (tmp);
}
}
- return ret;
+ *required = ret;
+ return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
}
@@ -1133,43 +1248,93 @@ TALER_KYCLOGIC_kyc_get_details (
}
-bool
-TALER_KYCLOGIC_check_satisfied (const char *requirements,
+enum GNUNET_DB_QueryStatus
+TALER_KYCLOGIC_check_satisfied (char **requirements,
const struct TALER_PaytoHashP *h_payto,
+ json_t **kyc_details,
TALER_KYCLOGIC_KycSatisfiedIterator ki,
- void *ki_cls)
+ void *ki_cls,
+ bool *satisfied)
{
struct TALER_KYCLOGIC_KycCheck *needed[num_kyc_checks];
unsigned int needed_cnt = 0;
if (NULL == requirements)
- return true;
{
- char *req = GNUNET_strdup (requirements);
+ *satisfied = true;
+ return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
+ }
+ {
+ char *req = *requirements;
for (const char *tok = strtok (req, " ");
NULL != tok;
tok = strtok (NULL, " "))
needed[needed_cnt++] = add_check (tok);
GNUNET_free (req);
+ *requirements = NULL;
}
{
struct RemoveContext rc = {
.needed = needed,
- .needed_cnt = &needed_cnt
+ .needed_cnt = &needed_cnt,
};
enum GNUNET_DB_QueryStatus qs;
+ rc.kyc_details = json_object ();
+ GNUNET_assert (NULL != rc.kyc_details);
+
/* Check what provider checks are already satisfied for h_payto (with
database), remove those from the 'needed' array. */
qs = ki (ki_cls,
h_payto,
&remove_satisfied,
&rc);
- GNUNET_break (qs >= 0); // FIXME: handle DB failure more nicely?
+ if (qs < 0)
+ {
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ *satisfied = false;
+ return qs;
+ }
+ if (0 != needed_cnt)
+ {
+ json_decref (rc.kyc_details);
+ *kyc_details = NULL;
+ }
+ else
+ {
+ *kyc_details = rc.kyc_details;
+ }
+ }
+ *satisfied = (0 == needed_cnt);
+
+ {
+ char *res = NULL;
+
+ for (unsigned int i = 0; i<needed_cnt; i++)
+ {
+ const struct TALER_KYCLOGIC_KycCheck *need = needed[i];
+
+ if (NULL == res)
+ {
+ res = GNUNET_strdup (need->name);
+ }
+ else
+ {
+ char *tmp;
+
+ GNUNET_asprintf (&tmp,
+ "%s %s",
+ res,
+ need->name);
+ GNUNET_free (res);
+ res = tmp;
+ }
+ }
+ *requirements = res;
}
- return (0 == needed_cnt);
+ return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
}
@@ -1186,7 +1351,6 @@ TALER_KYCLOGIC_requirements_to_logic (const char *requirements,
unsigned int max_checks = 0;
const struct TALER_KYCLOGIC_KycProvider *kp_best = NULL;
- // FIXME: use 'ut' to filter providers!
if (NULL == requirements)
return GNUNET_NO;
{
@@ -1206,6 +1370,8 @@ TALER_KYCLOGIC_requirements_to_logic (const char *requirements,
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];
@@ -1229,6 +1395,8 @@ TALER_KYCLOGIC_requirements_to_logic (const char *requirements,
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];
@@ -1247,6 +1415,7 @@ TALER_KYCLOGIC_requirements_to_logic (const char *requirements,
kp_best = kp;
}
}
+ GNUNET_assert (NULL != kp_best);
*plugin = kp_best->logic;
*pd = kp_best->pd;
*configuration_section = kp_best->provider_section_name;
@@ -1311,17 +1480,32 @@ TALER_KYCLOGIC_kyc_iterate_thresholds (
}
-enum TALER_ErrorCode
-TALER_KYCLOGIC_user_to_attributes (const char *provider_section,
- const char *provider_user_id,
- const char *legitimization_id,
- struct GNUNET_TIME_Timestamp *attr_expiration,
- json_t **attrs)
+void
+TALER_KYCLOGIC_lookup_checks (const char *section_name,
+ unsigned int *num_checks,
+ char ***provided_checks)
{
- GNUNET_break (0); // FIXME: not yet implemented!!!
- *attrs = json_object ();
- *attr_expiration = GNUNET_TIME_UNIT_ZERO_TS;
- return TALER_EC_NONE;
+ *num_checks = 0;
+ *provided_checks = NULL;
+ for (unsigned int i = 0; i<num_kyc_providers; i++)
+ {
+ struct TALER_KYCLOGIC_KycProvider *kp = kyc_providers[i];
+
+ if (0 !=
+ strcasecmp (section_name,
+ kp->provider_section_name))
+ continue;
+ *num_checks = kp->num_checks;
+ if (0 != kp->num_checks)
+ {
+ char **pc = GNUNET_new_array (kp->num_checks,
+ char *);
+ for (unsigned int i = 0; i<kp->num_checks; i++)
+ pc[i] = GNUNET_strdup (kp->provided_checks[i]->name);
+ *provided_checks = pc;
+ }
+ return;
+ }
}