diff options
Diffstat (limited to 'src/reducer/anastasis_api_redux.c')
-rw-r--r-- | src/reducer/anastasis_api_redux.c | 396 |
1 files changed, 287 insertions, 109 deletions
diff --git a/src/reducer/anastasis_api_redux.c b/src/reducer/anastasis_api_redux.c index 41bbdda..4b5ad7b 100644 --- a/src/reducer/anastasis_api_redux.c +++ b/src/reducer/anastasis_api_redux.c @@ -1,16 +1,16 @@ /* This file is part of Anastasis - Copyright (C) 2020, 2021 Anastasis SARL + Copyright (C) 2020, 2021, 2022 Anastasis SARL Anastasis is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software + terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU Affero General Public License along with + You should have received a copy of the GNU General Public License along with Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> */ /** @@ -36,6 +36,12 @@ */ #define CONFIG_GENERIC_TIMEOUT GNUNET_TIME_UNIT_MINUTES +/** + * How long do we wait in a more "synchronous" + * scenaro for a /config reply from an Anastasis provider. + */ +#define CONFIG_FAST_TIMEOUT GNUNET_TIME_UNIT_SECONDS + #define GENERATE_STRING(STRING) #STRING, static const char *generic_strings[] = { @@ -132,6 +138,21 @@ struct ConfigRequest struct ConfigReduxWaiting *w_tail; /** + * When did we start? + */ + struct GNUNET_TIME_Absolute start_time; + + /** + * When do we time out? + */ + struct GNUNET_TIME_Absolute timeout_at; + + /** + * How long do we wait before trying again? + */ + struct GNUNET_TIME_Relative backoff; + + /** * Obtained status code. */ unsigned int http_status; @@ -152,11 +173,6 @@ struct ConfigRequest char *business_name; /** - * currency used by the anastasis backend. - */ - char *currency; - - /** * Array of authorization methods supported by the server. */ struct AuthorizationMethodConfig *methods; @@ -188,9 +204,9 @@ struct ConfigRequest struct TALER_Amount liability_limit; /** - * Server salt. + * Provider salt. */ - struct ANASTASIS_CRYPTO_ProviderSaltP salt; + struct ANASTASIS_CRYPTO_ProviderSaltP provider_salt; /** * Task to timeout /config requests. @@ -311,6 +327,8 @@ ANASTASIS_redux_fail_ (ANASTASIS_ActionCallback cb, GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_string ("detail", detail)), + GNUNET_JSON_pack_string ("reducer_type", + "error"), GNUNET_JSON_pack_uint64 ("code", ec), GNUNET_JSON_pack_string ("hint", @@ -364,7 +382,6 @@ free_config_request (struct ConfigRequest *cr) ANASTASIS_config_cancel (cr->co); if (NULL != cr->tt) GNUNET_SCHEDULER_cancel (cr->tt); - GNUNET_free (cr->currency); GNUNET_free (cr->url); GNUNET_free (cr->business_name); for (unsigned int i = 0; i<cr->methods_length; i++) @@ -491,13 +508,11 @@ notify_waiting (struct ConfigRequest *cr) "authentication_providers", provider_list = json_object ())); } - provider_list = json_object_get (w->state, - "authentication_providers"); - GNUNET_assert (NULL != provider_list); - if (TALER_EC_NONE != cr->ec) { prov = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("status", + "error"), GNUNET_JSON_pack_uint64 ("error_code", cr->ec), GNUNET_JSON_pack_uint64 ("http_status", @@ -523,6 +538,8 @@ notify_waiting (struct ConfigRequest *cr) mj)); } prov = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("status", + "ok"), GNUNET_JSON_pack_array_steal ("methods", methods_list), TALER_JSON_pack_amount ("annual_fee", @@ -531,14 +548,12 @@ notify_waiting (struct ConfigRequest *cr) &cr->truth_upload_fee), TALER_JSON_pack_amount ("liability_limit", &cr->liability_limit), - GNUNET_JSON_pack_string ("currency", - cr->currency), GNUNET_JSON_pack_string ("business_name", cr->business_name), GNUNET_JSON_pack_uint64 ("storage_limit_in_megabytes", cr->storage_limit_in_megabytes), - GNUNET_JSON_pack_data_auto ("salt", - &cr->salt), + GNUNET_JSON_pack_data_auto ("provider_salt", + &cr->provider_salt), GNUNET_JSON_pack_uint64 ("http_status", cr->http_status)); } @@ -551,68 +566,108 @@ notify_waiting (struct ConfigRequest *cr) w->state); abort_provider_config_cb (w); } +} + + +/** + * Notify anyone waiting on @a cr that the request is done + * (successful or failed). + * + * @param[in,out] cls request that completed + */ +static void +notify_waiting_cb (void *cls) +{ + struct ConfigRequest *cr = cls; + cr->tt = NULL; + notify_waiting (cr); } /** + * Function called when it is time to retry a + * failed /config request. + * + * @param cls the `struct ConfigRequest *` to retry. + */ +static void +retry_config (void *cls); + + +/** * Function called with the results of a #ANASTASIS_get_config(). * * @param cls closure - * @param http_status HTTP status of the request * @param acfg anastasis configuration */ static void config_cb (void *cls, - unsigned int http_status, const struct ANASTASIS_Config *acfg) { struct ConfigRequest *cr = cls; cr->co = NULL; - GNUNET_SCHEDULER_cancel (cr->tt); - cr->tt = NULL; - cr->http_status = http_status; - if (MHD_HTTP_OK != http_status) - cr->ec = TALER_EC_ANASTASIS_REDUCER_PROVIDER_CONFIG_FAILED; - if ( (MHD_HTTP_OK == http_status) && + if (NULL != cr->tt) + { + GNUNET_SCHEDULER_cancel (cr->tt); + cr->tt = NULL; + } + cr->http_status = acfg->http_status; + if (MHD_HTTP_OK != acfg->http_status) + { + if (0 == acfg->http_status) + cr->ec = TALER_EC_ANASTASIS_GENERIC_PROVIDER_UNREACHABLE; + else + cr->ec = TALER_EC_ANASTASIS_REDUCER_PROVIDER_CONFIG_FAILED; + } + if ( (MHD_HTTP_OK == acfg->http_status) && (NULL == acfg) ) { cr->http_status = MHD_HTTP_NOT_FOUND; cr->ec = TALER_EC_ANASTASIS_REDUCER_PROVIDER_CONFIG_FAILED; } - else if (NULL != acfg) + else { - if (0 == acfg->storage_limit_in_megabytes) + if (0 == acfg->details.ok.storage_limit_in_megabytes) { cr->http_status = 0; cr->ec = TALER_EC_ANASTASIS_REDUCER_PROVIDER_INVALID_CONFIG; } else { - GNUNET_free (cr->currency); - cr->currency = GNUNET_strdup (acfg->currency); + cr->ec = TALER_EC_NONE; GNUNET_free (cr->business_name); - cr->business_name = GNUNET_strdup (acfg->business_name); + cr->business_name = GNUNET_strdup (acfg->details.ok.business_name); for (unsigned int i = 0; i<cr->methods_length; i++) GNUNET_free (cr->methods[i].type); GNUNET_free (cr->methods); - cr->methods = GNUNET_new_array (acfg->methods_length, + cr->methods = GNUNET_new_array (acfg->details.ok.methods_length, struct AuthorizationMethodConfig); - for (unsigned int i = 0; i<acfg->methods_length; i++) + for (unsigned int i = 0; i<acfg->details.ok.methods_length; i++) { - cr->methods[i].type = GNUNET_strdup (acfg->methods[i].type); - cr->methods[i].usage_fee = acfg->methods[i].usage_fee; + cr->methods[i].type = GNUNET_strdup (acfg->details.ok.methods[i].type); + cr->methods[i].usage_fee = acfg->details.ok.methods[i].usage_fee; } - cr->methods_length = acfg->methods_length; - cr->storage_limit_in_megabytes = acfg->storage_limit_in_megabytes; - cr->annual_fee = acfg->annual_fee; - cr->truth_upload_fee = acfg->truth_upload_fee; - cr->liability_limit = acfg->liability_limit; - cr->salt = acfg->salt; + cr->methods_length = acfg->details.ok.methods_length; + cr->storage_limit_in_megabytes = + acfg->details.ok.storage_limit_in_megabytes; + cr->annual_fee = acfg->details.ok.annual_fee; + cr->truth_upload_fee = acfg->details.ok.truth_upload_fee; + cr->liability_limit = acfg->details.ok.liability_limit; + cr->provider_salt = acfg->details.ok.provider_salt; } } notify_waiting (cr); + if (MHD_HTTP_OK != acfg->http_status) + { + cr->backoff = GNUNET_TIME_STD_BACKOFF (cr->backoff); + GNUNET_assert (NULL == cr->tt); + GNUNET_assert (NULL != cr->url); + cr->tt = GNUNET_SCHEDULER_add_delayed (cr->backoff, + &retry_config, + cr); + } } @@ -627,22 +682,57 @@ config_request_timeout (void *cls) struct ConfigRequest *cr = cls; cr->tt = NULL; - ANASTASIS_config_cancel (cr->co); - cr->co = NULL; + if (NULL != cr->co) + { + ANASTASIS_config_cancel (cr->co); + cr->co = NULL; + } cr->http_status = 0; cr->ec = TALER_EC_GENERIC_TIMEOUT; notify_waiting (cr); + cr->backoff = GNUNET_TIME_STD_BACKOFF (cr->backoff); + GNUNET_assert (NULL == cr->tt); + GNUNET_assert (NULL != cr->url); + cr->tt = GNUNET_SCHEDULER_add_delayed (cr->backoff, + &retry_config, + cr); +} + + +static void +retry_config (void *cls) +{ + struct ConfigRequest *cr = cls; + + cr->tt = NULL; + if (NULL != cr->co) + { + ANASTASIS_config_cancel (cr->co); + cr->co = NULL; + } + cr->timeout_at = GNUNET_TIME_relative_to_absolute (CONFIG_GENERIC_TIMEOUT); + GNUNET_assert (NULL == cr->tt); + cr->tt = GNUNET_SCHEDULER_add_at (cr->timeout_at, + &config_request_timeout, + cr); + cr->co = ANASTASIS_get_config (ANASTASIS_REDUX_ctx_, + cr->url, + &config_cb, + cr); + GNUNET_break (NULL != cr->co); } /** * Schedule job to obtain Anastasis provider configuration at @a url. * + * @param timeout how long to wait for a reply * @param url base URL of Anastasis provider * @return check config handle */ static struct ConfigRequest * -check_config (const char *url) +check_config (struct GNUNET_TIME_Relative timeout, + const char *url) { struct ConfigRequest *cr; @@ -652,17 +742,47 @@ check_config (const char *url) cr->url)) continue; if (NULL != cr->co) + { + struct GNUNET_TIME_Relative duration; + struct GNUNET_TIME_Relative left; + struct GNUNET_TIME_Relative xleft; + + duration = GNUNET_TIME_absolute_get_duration (cr->start_time); + left = GNUNET_TIME_relative_subtract (timeout, + duration); + xleft = GNUNET_TIME_absolute_get_remaining (cr->timeout_at); + if (GNUNET_TIME_relative_cmp (left, + <, + xleft)) + { + /* new timeout is shorter! */ + cr->timeout_at = GNUNET_TIME_relative_to_absolute (left); + GNUNET_SCHEDULER_cancel (cr->tt); + cr->tt = GNUNET_SCHEDULER_add_at (cr->timeout_at, + &config_request_timeout, + cr); + } return cr; /* already on it */ + } break; } if (NULL == cr) { cr = GNUNET_new (struct ConfigRequest); + cr->start_time = GNUNET_TIME_absolute_get (); cr->url = GNUNET_strdup (url); GNUNET_CONTAINER_DLL_insert (cr_head, cr_tail, cr); } + if (MHD_HTTP_OK == cr->http_status) + return cr; + cr->timeout_at = GNUNET_TIME_relative_to_absolute (timeout); + if (NULL != cr->tt) + GNUNET_SCHEDULER_cancel (cr->tt); + cr->tt = GNUNET_SCHEDULER_add_at (cr->timeout_at, + &config_request_timeout, + cr); cr->co = ANASTASIS_get_config (ANASTASIS_REDUX_ctx_, cr->url, &config_cb, @@ -672,12 +792,6 @@ check_config (const char *url) GNUNET_break (0); return NULL; } - else - { - cr->tt = GNUNET_SCHEDULER_add_delayed (CONFIG_GENERIC_TIMEOUT, - &config_request_timeout, - cr); - } return cr; } @@ -685,12 +799,12 @@ check_config (const char *url) /** * Begin asynchronous check for provider configurations. * - * @param currencies the currencies to initiate the provider checks for + * @param cc country code that was selected * @param[in,out] state to set provider list for * @return #TALER_EC_NONE on success */ static enum TALER_ErrorCode -begin_provider_config_check (const json_t *currencies, +begin_provider_config_check (const char *cc, json_t *state) { if (NULL == provider_list) @@ -738,14 +852,17 @@ begin_provider_config_check (const json_t *currencies, json_array_foreach (provider_arr, index, provider) { const char *url; - const char *cur; + const char *restricted = NULL; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_string ("url", &url), - GNUNET_JSON_spec_string ("currency", - &cur), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ("restricted", + &restricted), + NULL), GNUNET_JSON_spec_end () }; + json_t *prov; if (GNUNET_OK != GNUNET_JSON_parse (provider, @@ -756,33 +873,35 @@ begin_provider_config_check (const json_t *currencies, json_decref (pl); return TALER_EC_ANASTASIS_REDUCER_RESOURCE_MALFORMED; } - + if ( (NULL != restricted) && + (0 != strcmp (restricted, + cc)) ) { - bool found = false; - json_t *cu; - size_t off; - - json_array_foreach (currencies, off, cu) - { - const char *currency; - - currency = json_string_value (cu); - if (NULL == currency) - { - json_decref (pl); - return TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID; - } - found = (0 == strcasecmp (currency, - cur)); - } - if (! found) - continue; + /* skip */ + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Skipping provider restricted to country `%s'\n", + restricted); + continue; } + if ( (NULL == restricted) && + (0 == strcmp (cc, + "xx")) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Running in demo mode, skipping unrestricted providers\n"); + /* demo mode, skipping regular providers */ + continue; + } + prov = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("status", + "not-contacted")); + GNUNET_assert (NULL != prov); GNUNET_assert (0 == json_object_set_new (pl, url, - json_object ())); - check_config (url); + prov)); + check_config (CONFIG_GENERIC_TIMEOUT, + url); } GNUNET_assert (0 == json_object_set_new (state, @@ -1010,7 +1129,6 @@ select_country (json_t *state, { const json_t *required_attrs; const json_t *country_code; - const json_t *currencies; const json_t *redux_id_attr; if (NULL == arguments) @@ -1060,23 +1178,11 @@ select_country (json_t *state, } } - currencies = json_object_get (arguments, - "currencies"); - if ( (NULL == currencies) || - (! json_is_array (currencies)) ) - { - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, - "'currencies' missing"); - return NULL; - } - /* We now have an idea of the currency, begin fetching - provider /configs (we likely need them later) */ + /* Begin fetching provider /configs (we likely need them later) */ { enum TALER_ErrorCode ec; - ec = begin_provider_config_check (currencies, + ec = begin_provider_config_check (json_string_value (country_code), state); if (TALER_EC_NONE != ec) { @@ -1116,10 +1222,6 @@ select_country (json_t *state, (json_t *) country_code)); GNUNET_assert (0 == json_object_set (state, - "currencies", - (json_t *) currencies)); - GNUNET_assert (0 == - json_object_set (state, "required_attributes", (json_t *) required_attrs)); cb (cb_cls, @@ -1163,7 +1265,8 @@ ANASTASIS_REDUX_add_provider_to_state_ (const char *url, struct ConfigRequest *cr; struct ConfigReduxWaiting *w; - cr = check_config (url); + cr = check_config (CONFIG_FAST_TIMEOUT, + url); w = GNUNET_new (struct ConfigReduxWaiting); w->cr = cr; w->state = json_incref (state); @@ -1176,8 +1279,10 @@ ANASTASIS_REDUX_add_provider_to_state_ (const char *url, w); if (NULL == cr->co) { - notify_waiting (cr); - return NULL; + if (NULL != cr->tt) + GNUNET_SCHEDULER_cancel (cr->tt); + cr->tt = GNUNET_SCHEDULER_add_now (¬ify_waiting_cb, + cr); } return &w->ra; } @@ -1248,19 +1353,22 @@ enter_user_attributes (json_t *state, const char *attribute_value; const char *regexp = NULL; const char *reglog = NULL; - int optional = false; + bool optional = false; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_string ("name", &name), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_string ("validation-regex", - ®exp)), + ®exp), + NULL), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_string ("validation-logic", - ®log)), + ®log), + NULL), GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_boolean ("optional", - &optional)), + GNUNET_JSON_spec_bool ("optional", + &optional), + NULL), GNUNET_JSON_spec_end () }; @@ -1619,9 +1727,14 @@ external_reducer_read_cb (void *cls) return; } - red_cls->action_cb (red_cls->action_cb_cls, - TALER_EC_NONE, - json); + { + enum TALER_ErrorCode ec; + ec = json_integer_value (json_object_get (json, "code")); + + red_cls->action_cb (red_cls->action_cb_cls, + ec, + json); + } cleanup_external_reducer (red_cls); return; } @@ -1994,3 +2107,68 @@ ANASTASIS_REDUX_load_continents_ () GNUNET_JSON_pack_array_steal ("continents", continents)); } + + +/** + * Lookup @a provider_salt of @a provider_url in @a state. + * + * @param state the state to inspect + * @param provider_url provider to look into + * @param[out] provider_salt value to extract + * @return #GNUNET_OK on success + */ +enum GNUNET_GenericReturnValue +ANASTASIS_reducer_lookup_salt ( + const json_t *state, + const char *provider_url, + struct ANASTASIS_CRYPTO_ProviderSaltP *provider_salt) +{ + const json_t *aps; + const json_t *cfg; + uint32_t http_status = 0; + const char *status; + bool no_salt; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("status", + &status), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_uint32 ("http_status", + &http_status), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_fixed_auto ("provider_salt", + provider_salt), + &no_salt), + GNUNET_JSON_spec_end () + }; + + aps = json_object_get (state, + "authentication_providers"); + if (NULL == aps) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + cfg = json_object_get (aps, + provider_url); + if (NULL == cfg) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + GNUNET_JSON_parse (cfg, + spec, + NULL, NULL)) + { + /* provider not working */ + GNUNET_break_op (0); + return GNUNET_NO; + } + if (0 == strcmp (status, + "disabled")) + return GNUNET_NO; + if (no_salt) + return GNUNET_NO; + return GNUNET_OK; +} |