diff options
Diffstat (limited to 'src/kyclogic/kyclogic_api.c')
-rw-r--r-- | src/kyclogic/kyclogic_api.c | 268 |
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; + } } |