summaryrefslogtreecommitdiff
path: root/src/reducer/anastasis_api_redux.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/reducer/anastasis_api_redux.c')
-rw-r--r--src/reducer/anastasis_api_redux.c396
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 (&notify_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",
- &regexp)),
+ &regexp),
+ NULL),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_string ("validation-logic",
- &reglog)),
+ &reglog),
+ 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;
+}