diff options
author | Christian Grothoff <christian@grothoff.org> | 2024-04-28 15:24:06 +0200 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2024-07-29 12:18:39 +0200 |
commit | 71f8e9cb06f76be9cf1a5be3bf0d470ee44d56a4 (patch) | |
tree | eba4670acdb6d1e2366e8c4211020224b3260b3b /src/kyclogic | |
parent | 9988229acbdb8bacb848cd48d003e01bb6685093 (diff) |
implement new configuration parser
Diffstat (limited to 'src/kyclogic')
-rw-r--r-- | src/kyclogic/kyclogic_api.c | 1297 |
1 files changed, 971 insertions, 326 deletions
diff --git a/src/kyclogic/kyclogic_api.c b/src/kyclogic/kyclogic_api.c index ddf607309..fc5f2f18e 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-2023 Taler Systems SA + Copyright (C) 2022-2024 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 @@ -25,13 +25,63 @@ * Name of the KYC check that may never be passed. Useful if some * operations/amounts are categorically forbidden. */ -#define KYC_CHECK_IMPOSSIBLE "impossible" +#define KYC_CHECK_IMPOSSIBLE "verboten" /** * Information about a KYC provider. */ -struct TALER_KYCLOGIC_KycProvider; +struct TALER_KYCLOGIC_KycProvider +{ + + /** + * Cost of running this provider's KYC process. + */ + struct TALER_Amount cost; + + /** + * Name of the provider (configuration section name). + */ + char *provider_name; + + /** + * Name of a program to run to convert output of the + * plugin into the desired set of attributes. + */ + char *converter_name; + + /** + * Logic to run for this provider. + */ + struct TALER_KYCLOGIC_Plugin *logic; + + /** + * Provider-specific details to pass to the @e logic functions. + */ + struct TALER_KYCLOGIC_ProviderDetails *pd; + +}; + + +/** + * 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. @@ -41,64 +91,107 @@ struct TALER_KYCLOGIC_KycCheck /** * Human-readable name given to the KYC check. */ - char *name; + char *check_name; /** - * Array of @e num_providers providers that offer this type of KYC check. + * Human-readable description of the check in English. */ - struct TALER_KYCLOGIC_KycProvider **providers; + char *description; /** - * Length of the @e providers array. + * Optional translations of @e description, can be + * NULL. */ - unsigned int num_providers; + 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; -struct TALER_KYCLOGIC_KycProvider -{ /** - * Name of the provider (configuration section name). + * Name of an original measure to take as a fallback + * in case the check fails. */ - const char *provider_section_name; + char *fallback; /** - * Array of @e num_checks checks performed by this provider. + * 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. */ - struct TALER_KYCLOGIC_KycCheck **provided_checks; + char **outputs; /** - * Logic to run for this provider. + * Length of the @e requires array. */ - struct TALER_KYCLOGIC_Plugin *logic; + unsigned int num_requires; /** - * @e provider_section_name specific details to - * pass to the @e logic functions. + * True if clients can voluntarily trigger this check. */ - struct TALER_KYCLOGIC_ProviderDetails *pd; + bool voluntary; /** - * Cost of running this provider's KYC. + * Type of the KYC check. */ - unsigned long long cost; + enum CheckType type; /** - * Length of the @e checks array. + * Details depending on @e type. */ - unsigned int num_checks; + 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; }; /** - * Condition that triggers a need to perform KYC. + * Rule that triggers some measure(s). */ -struct TALER_KYCLOGIC_KycTrigger +struct TALER_KYCLOGIC_KycRule { /** + * Name of the rule (configuration section name). + */ + char *rule_name; + + /** * Timeframe to consider for computing the amount * to compare against the @e limit. Zero for the * wallet balance trigger (as not applicable). @@ -112,20 +205,82 @@ struct TALER_KYCLOGIC_KycTrigger struct TALER_Amount threshold; /** - * Array of @e num_checks checks to apply on this trigger. + * Array of names of measures to apply on this trigger. */ - struct TALER_KYCLOGIC_KycCheck **required_checks; + char **next_measures; /** - * Length of the @e checks array. + * Length of the @e next_measures array. */ - unsigned int num_checks; + unsigned int num_measures; /** - * What event is this trigger for? + * What operation type is this rule for? */ enum TALER_KYCLOGIC_KycTriggerEvent trigger; + /** + * True if all @e next_measures will eventually need to + * be satisfied, False if the user has a choice between them. + */ + bool is_and_combinator; + + /** + * True if this rule and the general nature of the next measures + * should be exposed to the client. + */ + bool exposed; + +}; + + +/** + * AML programs. + */ +struct TALER_KYCLOGIC_AmlProgram +{ + + /** + * Name of the AML program configuration section. + */ + char *program_name; + + /** + * Name of the AML program (binary) to run. + */ + char *command; + + /** + * Human-readable description of what this AML helper + * program will do. + */ + char *description; + + /** + * Name of an original measure to take in case the + * @e command fails. + */ + char *fallback; + + /** + * Output of @e command "--required-context". + */ + char **required_contexts; + + /** + * Length of the @e required_contexts array. + */ + unsigned int num_required_contexts; + + /** + * Output of @e command "--required-attributes". + */ + char **required_attributes; + + /** + * Length of the @e required_attributes array. + */ + unsigned int num_required_attributes; }; @@ -140,6 +295,16 @@ static struct TALER_KYCLOGIC_Plugin **kyc_logics; static unsigned int num_kyc_logics; /** + * Array of configured providers. + */ +static struct TALER_KYCLOGIC_KycProvider **kyc_providers; + +/** + * Length of the #kyc_providers array. + */ +static unsigned int num_kyc_providers; + +/** * Array of @e num_kyc_checks known types of * KYC checks. */ @@ -151,30 +316,167 @@ static struct TALER_KYCLOGIC_KycCheck **kyc_checks; static unsigned int num_kyc_checks; /** - * Array of configured triggers. + * Array of configured rules that apply if we do + * not have an AMLA record. */ -static struct TALER_KYCLOGIC_KycTrigger **kyc_triggers; +static struct TALER_KYCLOGIC_KycRule **kyc_rules; /** - * Length of the #kyc_triggers array. + * Length of the #kyc_rules array. */ -static unsigned int num_kyc_triggers; +static unsigned int num_kyc_rules; /** - * Array of configured providers. + * Array of available AML programs. */ -static struct TALER_KYCLOGIC_KycProvider **kyc_providers; +static struct TALER_KYCLOGIC_AmlProgram **aml_programs; /** - * Length of the #kyc_providers array. + * Length of the #aml_programs array. */ -static unsigned int num_kyc_providers; +static unsigned int num_aml_programs; + + +/** + * Run @a command with @a argument and return the + * respective output from stdout. + * + * @param command binary to run + * @param argument command-line argument to pass + * @return NULL if @a command failed + */ +static char * +command_output (const char *command, + const char *argument) +{ + char *rval; + size_t sval; + size_t soff; + ssize_t ret; + int sout[2]; + pid_t chld; + + if (0 != pipe (sout)) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, + "pipe"); + return NULL; + } + chld = fork (); + if (-1 == chld) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, + "fork"); + return NULL; + } + if (0 == chld) + { + GNUNET_break (0 == + close (sout[0])); + GNUNET_break (0 == + close (STDOUT_FILENO)); + GNUNET_assert (STDOUT_FILENO == + dup2 (sout[1], + STDOUT_FILENO)); + GNUNET_break (0 == + close (sout[1])); + execlp (command, + command, + argument, + NULL); + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, + "exec", + command); + exit (EXIT_FAILURE); + } + GNUNET_break (0 == + close (sout[1])); + sval = 1024; + rval = GNUNET_malloc (sval); + soff = 0; + while (0 < (ret = read (sout[0], + rval + soff, + sval - soff)) ) + { + soff += ret; + if (soff == sval) + { + GNUNET_array_grow (rval, + sval, + sval * 2); + } + } + GNUNET_break (0 == close (sout[0])); + { + int wstatus; + + GNUNET_break (chld == + waitpid (chld, + &wstatus, + 0)); + if ( (! WIFEXITED (wstatus)) || + (0 != WEXITSTATUS (wstatus)) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Command `%s' %s failed with status %d\n", + command, + arguments, + wstatus); + GNUNET_array_grow (rval, + sval, + 0); + return NULL; + } + } + GNUNET_array_grow (rval, + sval, + soff + 1); + rval[soff] = '\0'; + return rval; +} + + +/** + * Convert check type @a ctype_s into @a ctype. + * + * @param ctype_s check type as a string + * @param[out] ctype set to check type as enum + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +check_type_from_string ( + const char *ctype_s, + enum CheckType *ctype) +{ + struct + { + const char *in; + enum CheckType out; + } map [] = { + { "INFO", CT_INFO }, + { "LINK", CT_LINK }, + { "FORM", CT_FORM }, + { NULL, 0 } + }; + + for (unsigned int i = 0; NULL != map[i].in; i++) + if (0 == strcasecmp (map[i].in, + ctype_s)) + { + *ctype = map[i].out; + return GNUNET_OK; + } + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Invalid check type `%s'\n", + ctype_s); + return GNUNET_SYSERR; +} enum GNUNET_GenericReturnValue -TALER_KYCLOGIC_kyc_trigger_from_string (const char *trigger_s, - enum TALER_KYCLOGIC_KycTriggerEvent * - trigger) +TALER_KYCLOGIC_kyc_trigger_from_string ( + const char *trigger_s, + enum TALER_KYCLOGIC_KycTriggerEvent *trigger) { struct { @@ -205,7 +507,8 @@ TALER_KYCLOGIC_kyc_trigger_from_string (const char *trigger_s, const char * -TALER_KYCLOGIC_kyc_trigger2s (enum TALER_KYCLOGIC_KycTriggerEvent trigger) +TALER_KYCLOGIC_kyc_trigger2s ( + enum TALER_KYCLOGIC_KycTriggerEvent trigger) { switch (trigger) { @@ -227,38 +530,6 @@ TALER_KYCLOGIC_kyc_trigger2s (enum TALER_KYCLOGIC_KycTriggerEvent trigger) } -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. * @@ -300,64 +571,6 @@ 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. - * - * @param check name of the check - * @return pointer into the global list - */ -static struct TALER_KYCLOGIC_KycCheck * -add_check (const char *check) -{ - struct TALER_KYCLOGIC_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 TALER_KYCLOGIC_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 TALER_KYCLOGIC_KycCheck ***p_checks, - unsigned int *num_p_checks) -{ - char *sptr; - struct TALER_KYCLOGIC_KycCheck **rchecks = NULL; - unsigned int num_rchecks = 0; - - for (char *tok = strtok_r (checks, " ", &sptr); - NULL != tok; - tok = strtok_r (NULL, " ", &sptr)) - { - struct TALER_KYCLOGIC_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. * * @param cfg configuration to parse @@ -368,49 +581,36 @@ static enum GNUNET_GenericReturnValue add_provider (const struct GNUNET_CONFIGURATION_Handle *cfg, const char *section) { - unsigned long long cost; + struct TALER_Amount cost; char *logic; - char *ut_s; - enum TALER_KYCLOGIC_KycUserType ut; - char *checks; + char *converter; struct TALER_KYCLOGIC_Plugin *lp; + struct TALER_KYCLOGIC_ProviderDetails *pd; if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (cfg, - section, - "COST", - &cost)) + TALER_config_get_amount (cfg, + section, + "COST", + &cost)) { GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, section, "COST", - "number required"); + "amount required"); return GNUNET_SYSERR; } if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, section, - "USER_TYPE", - &ut_s)) + "CONVERTER", + &converter)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, section, - "USER_TYPE"); + "CONVERTER"); return GNUNET_SYSERR; } if (GNUNET_OK != - TALER_KYCLOGIC_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 (cfg, section, "LOGIC", @@ -419,84 +619,348 @@ add_provider (const struct GNUNET_CONFIGURATION_Handle *cfg, GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, section, "LOGIC"); + GNUNET_free (converter); return GNUNET_SYSERR; } lp = load_logic (cfg, logic); if (NULL == lp) { - GNUNET_free (logic); GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, section, "LOGIC", "logic plugin could not be loaded"); + GNUNET_free (logic); + GNUNET_free (converter); return GNUNET_SYSERR; } GNUNET_free (logic); - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - section, - "PROVIDED_CHECKS", - &checks)) + pd = lp->load_configuration (lp->cls, + section); + if (NULL == pd) { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - section, - "PROVIDED_CHECKS"); + GNUNET_free (converter); return GNUNET_SYSERR; } + { struct TALER_KYCLOGIC_KycProvider *kp; kp = GNUNET_new (struct TALER_KYCLOGIC_KycProvider); - kp->provider_section_name = section; - kp->user_type = ut; - kp->logic = lp; kp->cost = cost; - add_checks (checks, - &kp->provided_checks, - &kp->num_checks); - GNUNET_free (checks); - kp->pd = lp->load_configuration (lp->cls, - section); - if (NULL == kp->pd) - { - GNUNET_free (kp); - return GNUNET_SYSERR; - } + kp->provider_name + = GNUNET_strdup (§ion[strlen ("kyc-provider-")]); + kp->converter_name = converter; + kp->logic = lp; + kp->pd = pd; GNUNET_array_append (kyc_providers, num_kyc_providers, kp); - for (unsigned int i = 0; i<kp->num_checks; i++) + } + return GNUNET_OK; +} + + +/** + * Tokenize @a input along @a token + * and build an array of the tokens. + * + * @param[in,out] input the input to tokenize; clobbered + * @param sep separator between tokens to separate @a input on + * @param[out] p_strs where to put array of tokens + * @param[out] num_p_strs set to length of @a p_strs array + */ +static void +add_tokens (char *input, + const char *sep, + char ***p_strs, + unsigned int *num_strs) +{ + char *sptr; + char **rstr = NULL; + unsigned int num_rstr = 0; + + for (char *tok = strtok_r (input, sep, &sptr); + NULL != tok; + tok = strtok_r (NULL, sep, &sptr)) + { + GNUNET_array_append (rstr, + num_rstr, + GNUNET_strdup (tok)); + } + *p_strs = rstr; + *num_strs = num_rstr; +} + + +/** + * Closure for the handle_XXX_section functions + * that parse configuration sections matching certain + * prefixes. + */ +struct SectionContext +{ + /** + * Configuration to handle. + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Result to return, set to false on failures. + */ + bool result; +}; + + +/** + * Function to iterate over configuration sections. + * + * @param cls a `struct SectionContext *` + * @param section name of the section + */ +static void +handle_provider_section (void *cls, + const char *section) +{ + struct SectionContext *sc = cls; + + if (0 == strncasecmp (section, + "kyc-provider-", + strlen ("kyc-provider-"))) + { + if (GNUNET_OK != + add_provider (sc->cfg, + section)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Setup failed in configuration section `%s'\n", + section); + sc->result = false; + } + return; + } +} + + +/** + * Parse configuration @a cfg in section @a section for + * the specification of a KYC check. + * + * @param cfg configuration to parse + * @param section configuration section to parse + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +add_check (const struct GNUNET_CONFIGURATION_Handle *cfg, + const char *section) +{ + bool voluntary; + enum CheckType ct; + char *form_name = NULL; + char *description = NULL; + json_t *description_i18n = NULL; + char *requires = NULL; + char *outputs = NULL; + char *fallback = NULL; + + voluntary = (GNUNET_YES == + GNUNET_CONFIGURATION_get_value_yesno (cfg, + section, + "VOLUNTARY")); + + { + char *type_s; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + section, + "TYPE", + &type_s)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + section, + "TYPE"); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + check_type_from_string (type_s, + &ct)) { - struct TALER_KYCLOGIC_KycCheck *kc = kp->provided_checks[i]; + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section, + "TYPE", + "valid check type required"); + GNUNET_free (type_s); + return GNUNET_SYSERR; + } + GNUNET_free (type_s); + } + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + section, + "DESCRIPTION", + &description)) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section, + "DESCRIPTION", + "description required"); + goto fail; + } + + { + char *tmp; - GNUNET_array_append (kc->providers, - kc->num_providers, - kp); + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + section, + "DESCRIPTION_I18N", + &tmp)) + { + json_error_t err; + + description_i18n = json_loads (tmp, + JSON_REJECT_DUPLICATES, + &err); + GNUNET_free (tmp); + if (NULL == description_i18n) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section, + "DESCRIPTION_I18N", + err.text); + goto fail; + } + if (! TALER_JSON_check_i18n (description_i18n) ) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section, + "DESCRIPTION_I18N", + "JSON with internationalization map required"); + goto fail; + } } } + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + section, + "REQUIRES", + &requires)) + { + /* no requirements is OK */ + requires = GNUNET_strdup (""); + } + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + section, + "OUTPUTS", + &outputs)) + { + /* no outputs is OK */ + outputs = GNUNET_strdup (""); + } + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + section, + "FALLBACK", + &fallback)) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section, + "FALLBACK", + "fallback measure required"); + goto fail; + } + + { + struct TALER_KYCLOGIC_KycCheck *kc; + + kc = GNUNET_new (struct TALER_KYCLOGIC_KycCheck); + kc->check_name = GNUNET_strdup (§ion[strlen ("kyc-check-")]); + kc->description = description; + kc->description_i18n = description_i18n; + kc->requires = requires; + kc->fallback = fallback; + kc->outputs = outputs; + add_tokens (requires, + ";", + &kc->requires, + &kc->num_requires); + GNUNET_free (requires); + add_tokens (outputs, + " ", + &kc->outputs, + &kc->num_outputs); + GNUNET_free (outputs); + GNUNET_array_append (kyc_checks, + num_kyc_checks, + kc); + } return GNUNET_OK; +fail: + GNUNET_free (form_name); + GNUNET_free (description); + json_decref (description_i18n); + GNUNET_free (requires); + GNUNET_free (outputs); + GNUNET_free (fallback); + return GNUNET_SYSERR; +} + + +/** + * Function to iterate over configuration sections. + * + * @param cls a `struct SectionContext *` + * @param section name of the section + */ +static void +handle_check_section (void *cls, + const char *section) +{ + struct SectionContext *sc = cls; + + if (0 == strncasecmp (section, + "kyc-check-", + strlen ("kyc-check-"))) + { + if (GNUNET_OK != + add_check (sc->cfg, + section)) + sc->result = false; + return; + } } /** * Parse configuration @a cfg in section @a section for - * the specification of a KYC trigger. + * the specification of a KYC rule. * * @param cfg configuration to parse * @param section configuration section to parse * @return #GNUNET_OK on success */ static enum GNUNET_GenericReturnValue -add_trigger (const struct GNUNET_CONFIGURATION_Handle *cfg, - const char *section) +add_rule (const struct GNUNET_CONFIGURATION_Handle *cfg, + const char *section) { - char *ot_s; struct TALER_Amount threshold; struct GNUNET_TIME_Relative timeframe; - char *checks; enum TALER_KYCLOGIC_KycTriggerEvent ot; - + char *measures; + bool exposed; + bool is_and; + + if (GNUNET_YES != + GNUNET_CONFIGURATION_get_value_yesno (cfg, + section, + "ENABLED")) + return GNUNET_OK; if (GNUNET_OK != TALER_config_get_amount (cfg, section, @@ -509,29 +973,55 @@ add_trigger (const struct GNUNET_CONFIGURATION_Handle *cfg, "amount required"); return GNUNET_SYSERR; } - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - section, - "OPERATION_TYPE", - &ot_s)) + if (GNUNET_YES != + GNUNET_CONFIGURATION_get_value_yesno (cfg, + section, + "EXPOSED")) + exposed = false; { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - section, - "OPERATION_TYPE"); - return GNUNET_SYSERR; + enum GNUNET_GenericReturnValue r; + + r = GNUNET_CONFIGURATION_get_value_yesno (cfg, + section, + "IS_AND_COMBINATOR"); + if (GNUNET_SYSERR == r) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section, + "IS_AND_COMBINATOR", + "YES or NO required"); + return GNUNET_SYSERR; + } + is_and = (GNUNET_YES == r); } - if (GNUNET_OK != - TALER_KYCLOGIC_kyc_trigger_from_string (ot_s, - &ot)) + { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - section, - "OPERATION_TYPE", - "valid trigger type required"); + char *ot_s; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + section, + "OPERATION_TYPE", + &ot_s)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + section, + "OPERATION_TYPE"); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + TALER_KYCLOGIC_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); - return GNUNET_SYSERR; } - GNUNET_free (ot_s); if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (cfg, @@ -555,93 +1045,57 @@ add_trigger (const struct GNUNET_CONFIGURATION_Handle *cfg, if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, section, - "REQUIRED_CHECKS", - &checks)) + "NEXT_MEASURES", + &measures)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, section, - "REQUIRED_CHECKS"); + "NEXT_MEASURES"); return GNUNET_SYSERR; } { - struct TALER_KYCLOGIC_KycTrigger *kt; + struct TALER_KYCLOGIC_KycRule *kt; - kt = GNUNET_new (struct TALER_KYCLOGIC_KycTrigger); + kt = GNUNET_new (struct TALER_KYCLOGIC_KycRule); + kt->rule_name = GNUNET_strdup (§ion[strlen ("kyc-rule-")]); 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_triggers, - num_kyc_triggers, + kt->is_and_combinator = is_and; + kt->exposed = exposed; + add_tokens (measures, + " ", + &kt->next_measures, + &kt->num_measures); + GNUNET_free (measures); + GNUNET_array_append (kyc_rules, + num_kyc_rules, 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; } /** - * Closure for #handle_section(). - */ -struct SectionContext -{ - /** - * Configuration to handle. - */ - const struct GNUNET_CONFIGURATION_Handle *cfg; - - /** - * Result to return, set to false on failures. - */ - bool result; -}; - - -/** * Function to iterate over configuration sections. * * @param cls a `struct SectionContext *` * @param section name of the section */ static void -handle_provider_section (void *cls, - const char *section) +handle_rule_section (void *cls, + const char *section) { struct SectionContext *sc = cls; if (0 == strncasecmp (section, - "kyc-provider-", - strlen ("kyc-provider-"))) + "kyc-rule-", + strlen ("kyc-rule-"))) { if (GNUNET_OK != - add_provider (sc->cfg, - section)) + add_rule (sc->cfg, + section)) sc->result = false; return; } @@ -649,24 +1103,133 @@ handle_provider_section (void *cls, /** + * Parse configuration @a cfg in section @a section for + * the specification of an AML program. + * + * @param cfg configuration to parse + * @param section configuration section to parse + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +add_program (const struct GNUNET_CONFIGURATION_Handle *cfg, + const char *section) +{ + char *command = NULL; + char *description = NULL; + char *fallback = NULL; + char *required_contexts = NULL; + char *required_attributes = NULL; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + section, + "COMMAND", + &command)) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section, + "COMMAND", + "command required"); + goto fail; + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + section, + "DESCRIPTION", + &description)) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section, + "DESCRIPTION", + "description required"); + goto fail; + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + section, + "FALLBACK", + &fallback)) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section, + "FALLBACK", + "fallback measure name required"); + goto fail; + } + + required_contexts = command_output (command, + "--required-context"); + if (NULL == required_contexts) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section, + "COMMAND", + "output for --required-context invalid"); + goto fail; + } + required_attributes = command_output (command, + "--required-attributes"); + if (NULL == required_attributes) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section, + "COMMAND", + "output for --required-attributes invalid"); + goto fail; + } + + { + struct TALER_KYCLOGIC_AmlProgram *ap; + + ap = GNUNET_new (struct TALER_KYCLOGIC_AmlProgram); + ap->program_name = GNUNET_strdup (§ion[strlen ("kyc-check-")]); + ap->command = command; + ap->description = description; + ap->fallback = fallback; + add_tokens (required_contexts, + "\n", + &ap->required_contexts, + &ap->num_required_contexts); + GNUNET_free (required_contexts); + add_tokens (required_attributes, + "\n", + &ap->required_attributes, + &ap->num_required_attributes); + GNUNET_free (required_attributes); + GNUNET_array_append (aml_programs, + num_aml_programs, + ap); + } + return GNUNET_OK; +fail: + GNUNET_free (command); + GNUNET_free (description); + GNUNET_free (required_contexts); + GNUNET_free (required_attributes); + GNUNET_free (fallback); + return GNUNET_SYSERR; +} + + +/** * Function to iterate over configuration sections. * * @param cls a `struct SectionContext *` * @param section name of the section */ static void -handle_trigger_section (void *cls, +handle_program_section (void *cls, const char *section) { struct SectionContext *sc = cls; if (0 == strncasecmp (section, - "kyc-legitimization-", - strlen ("kyc-legitimization-"))) + "aml-program-", + strlen ("aml-program-"))) { if (GNUNET_OK != - add_trigger (sc->cfg, - section)) + add_rule (sc->cfg, + section)) sc->result = false; return; } @@ -674,8 +1237,8 @@ handle_trigger_section (void *cls, /** - * Comparator for qsort. Compares two triggers - * by timeframe to sort triggers by time. + * Comparator for qsort. Compares two rules + * by timeframe to sort rules by time. * * @param p1 first trigger to compare * @param p2 second trigger to compare @@ -685,18 +1248,18 @@ static int sort_by_timeframe (const void *p1, const void *p2) { - struct TALER_KYCLOGIC_KycTrigger **t1 = (struct - TALER_KYCLOGIC_KycTrigger **) p1; - struct TALER_KYCLOGIC_KycTrigger **t2 = (struct - TALER_KYCLOGIC_KycTrigger **) p2; + struct TALER_KYCLOGIC_KycRule **r1 + = (struct TALER_KYCLOGIC_KycRule **) p1; + struct TALER_KYCLOGIC_KycRule **r2 + = (struct TALER_KYCLOGIC_KycRule **) p2; - if (GNUNET_TIME_relative_cmp ((*t1)->timeframe, + if (GNUNET_TIME_relative_cmp ((*r1)->timeframe, <, - (*t2)->timeframe)) + (*r2)->timeframe)) return -1; - if (GNUNET_TIME_relative_cmp ((*t1)->timeframe, + if (GNUNET_TIME_relative_cmp ((*r1)->timeframe, >, - (*t2)->timeframe)) + (*r2)->timeframe)) return 1; return 0; } @@ -714,30 +1277,26 @@ TALER_KYCLOGIC_kyc_init (const struct GNUNET_CONFIGURATION_Handle *cfg) &handle_provider_section, &sc); GNUNET_CONFIGURATION_iterate_sections (cfg, + &handle_check_section, + &sc); + GNUNET_CONFIGURATION_iterate_sections (cfg, &handle_trigger_section, &sc); + GNUNET_CONFIGURATION_iterate_sections (cfg, + &handle_program_section, + &sc); if (! sc.result) { TALER_KYCLOGIC_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); - TALER_KYCLOGIC_kyc_done (); - return GNUNET_SYSERR; - } if (0 != num_kyc_triggers) qsort (kyc_triggers, num_kyc_triggers, sizeof (struct TALER_KYCLOGIC_KycTrigger *), &sort_by_timeframe); + // FIXME: add configuration sanity checking! return GNUNET_OK; } @@ -745,13 +1304,16 @@ TALER_KYCLOGIC_kyc_init (const struct GNUNET_CONFIGURATION_Handle *cfg) void TALER_KYCLOGIC_kyc_done (void) { - for (unsigned int i = 0; i<num_kyc_triggers; i++) + for (unsigned int i = 0; i<num_kyc_rules; i++) { - struct TALER_KYCLOGIC_KycTrigger *kt = kyc_triggers[i]; + struct TALER_KYCLOGIC_KycRule *kt = kyc_rules[i]; - GNUNET_array_grow (kt->required_checks, - kt->num_checks, + for (unsigned int j = 0; j<kt->num_measures; j++) + GNUNET_free (kt->next_measures[j]); + GNUNET_array_grow (kt->next_measures, + kt->num_measures, 0); + GNUNET_free (kt->rule_name); GNUNET_free (kt); } GNUNET_array_grow (kyc_triggers, @@ -762,9 +1324,8 @@ TALER_KYCLOGIC_kyc_done (void) struct TALER_KYCLOGIC_KycProvider *kp = kyc_providers[i]; kp->logic->unload_configuration (kp->pd); - GNUNET_array_grow (kp->provided_checks, - kp->num_checks, - 0); + GNUNET_free (kp->provider_name); + GNUNET_free (kp->converter_name); GNUNET_free (kp); } GNUNET_array_grow (kyc_providers, @@ -787,18 +1348,67 @@ TALER_KYCLOGIC_kyc_done (void) { struct TALER_KYCLOGIC_KycCheck *kc = kyc_checks[i]; - GNUNET_array_grow (kc->providers, - kc->num_providers, + GNUNET_free (kc->check_name); + GNUNET_free (kc->description); + json_decref (kc->description_i18n); + for (unsigned int j = 0; i<num_requires; j++) + GNUNET_free (kc->requires[j]); + GNUNET_array_grow (kc->requires, + kc->num_requires, + 0); + GNUNET_free (kc->fallback); + for (unsigned int j = 0; i<num_outputs; j++) + GNUNET_free (kc->outputs[j]); + GNUNET_array_grow (kc->outputs, + kc->num_outputs, 0); - GNUNET_free (kc->name); + switch (kc->type) + { + case CT_INFO: + break; + case CT_FORM: + GNUNET_free (kc->details.form.name); + break; + case CT_LINK: + break; + } GNUNET_free (kc); } GNUNET_array_grow (kyc_checks, num_kyc_checks, 0); + for (unsigned int i = 0; i<num_aml_programs; i++) + { + struct TALER_KYCLOGIC_AmlProgram *ap = aml_programs[i]; + + GNUNET_free (ap->program_name); + GNUNET_free (ap->command); + GNUNET_free (ap->description); + GNUNET_free (ap->fallback); + for (unsigned int j = 0; i<num_required_contexts; j++) + GNUNET_free (kc->required_contexts[j]); + GNUNET_array_grow (ap->required_contexts, + ap->num_required_contexts, + 0); + for (unsigned int j = 0; i<num_required_attributes; j++) + GNUNET_free (kc->required_attributes[j]); + GNUNET_array_grow (ap->required_attributes, + ap->num_required_attributes, + 0); + GNUNET_free (ap); + } + GNUNET_array_grow (aml_programs, + num_aml_programs, + 0); } +/* end of taler-exchange-httpd_kyc.c */ + +#if 0 +// FIXME from here... + + /** * Closure for the #eval_trigger(). */ @@ -1032,13 +1642,14 @@ remove_satisfied (void *cls, 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, - char **required) +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, + char **required) { struct TALER_KYCLOGIC_KycCheck *needed[num_kyc_checks]; unsigned int needed_cnt = 0; @@ -1202,12 +1813,13 @@ TALER_KYCLOGIC_kyc_get_details ( 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, - bool *satisfied) +TALER_KYCLOGIC_check_satisfied ( + char **requirements, + const struct TALER_PaytoHashP *h_payto, + json_t **kyc_details, + TALER_KYCLOGIC_KycSatisfiedIterator ki, + void *ki_cls, + bool *satisfied) { struct TALER_KYCLOGIC_KycCheck *needed[num_kyc_checks]; unsigned int needed_cnt = 0; @@ -1292,12 +1904,12 @@ TALER_KYCLOGIC_check_satisfied (char **requirements, enum GNUNET_GenericReturnValue -TALER_KYCLOGIC_requirements_to_logic (const char *requirements, - enum TALER_KYCLOGIC_KycUserType ut, - struct TALER_KYCLOGIC_Plugin **plugin, - struct TALER_KYCLOGIC_ProviderDetails **pd - , - const char **configuration_section) +TALER_KYCLOGIC_requirements_to_logic ( + const char *requirements, + enum TALER_KYCLOGIC_KycUserType ut, + struct TALER_KYCLOGIC_Plugin **plugin, + struct TALER_KYCLOGIC_ProviderDetails **pd, + const char **configuration_section) { struct TALER_KYCLOGIC_KycCheck *needed[num_kyc_checks]; unsigned int needed_cnt = 0; @@ -1378,10 +1990,11 @@ TALER_KYCLOGIC_requirements_to_logic (const char *requirements, enum GNUNET_GenericReturnValue -TALER_KYCLOGIC_lookup_logic (const char *name, - struct TALER_KYCLOGIC_Plugin **plugin, - struct TALER_KYCLOGIC_ProviderDetails **pd, - const char **provider_section) +TALER_KYCLOGIC_lookup_logic ( + const char *name, + struct TALER_KYCLOGIC_Plugin **plugin, + struct TALER_KYCLOGIC_ProviderDetails **pd, + const char **provider_section) { for (unsigned int i = 0; i<num_kyc_providers; i++) { @@ -1463,4 +2076,36 @@ TALER_KYCLOGIC_lookup_checks (const char *section_name, } -/* end of taler-exchange-httpd_kyc.c */ +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; +} + + +#endif |