[evolution-data-server] ESoupSession: Change handling of credentials
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server] ESoupSession: Change handling of credentials
- Date: Fri, 24 Jun 2022 10:12:43 +0000 (UTC)
commit 0f0da970393c922820c916926c29b0c9e3a9b825
Author: Milan Crha <mcrha redhat com>
Date: Fri Jun 24 11:22:02 2022 +0200
ESoupSession: Change handling of credentials
Update the SoupSession's auth manager's cache only if the credentials
passed to the function changed. This can help when re-using the ESoupSession
by multiple sources, which all use the same host and credentials.
src/libedataserver/e-data-server-util.c | 49 ++++++++++++++++++++++++++
src/libedataserver/e-data-server-util.h | 2 ++
src/libedataserver/e-soup-session.c | 62 +++++++++++++++++++++++++++++----
3 files changed, 106 insertions(+), 7 deletions(-)
---
diff --git a/src/libedataserver/e-data-server-util.c b/src/libedataserver/e-data-server-util.c
index 190b57921..0851118c7 100644
--- a/src/libedataserver/e-data-server-util.c
+++ b/src/libedataserver/e-data-server-util.c
@@ -2651,6 +2651,55 @@ e_named_parameters_get_name (const ENamedParameters *parameters,
return g_strndup (name_and_value, colon - name_and_value);
}
+/**
+ * e_named_parameters_equal:
+ * @parameters1: the first #ENamedParameters
+ * @parameters2: the second #ENamedParameters
+ *
+ * Compares the two parameters objects and returns whether they equal.
+ * Note a %NULL and empty parameters are also considered equal.
+ *
+ * Returns: whether the two parameters are equal
+ *
+ * Since: 3.46
+ **/
+gboolean
+e_named_parameters_equal (const ENamedParameters *parameters1,
+ const ENamedParameters *parameters2)
+{
+ GPtrArray *arr1, *arr2;
+ guint ii, jj;
+
+ if (parameters1 == parameters2 ||
+ (!parameters1 && e_named_parameters_count (parameters2) == 0) ||
+ (!parameters2 && e_named_parameters_count (parameters1) == 0))
+ return TRUE;
+
+ if (!parameters1 || !parameters2 ||
+ e_named_parameters_count (parameters1) != e_named_parameters_count (parameters1))
+ return FALSE;
+
+ arr1 = (GPtrArray *) parameters1;
+ arr2 = (GPtrArray *) parameters2;
+
+ for (ii = 0; ii < arr1->len; ii++) {
+ const gchar *name_and_value1 = g_ptr_array_index (arr1, ii);
+
+ for (jj = 0; jj < arr2->len; jj++) {
+ const gchar *name_and_value2 = g_ptr_array_index (arr2, jj);
+
+ if (g_strcmp0 (name_and_value1, name_and_value2) == 0)
+ break;
+ }
+
+ /* went through all the items, none matched */
+ if (jj == arr2->len)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
static ENamedParameters *
e_named_parameters_ref (ENamedParameters *params)
{
diff --git a/src/libedataserver/e-data-server-util.h b/src/libedataserver/e-data-server-util.h
index 33acbd6b6..2842438d6 100644
--- a/src/libedataserver/e-data-server-util.h
+++ b/src/libedataserver/e-data-server-util.h
@@ -205,6 +205,8 @@ gboolean e_named_parameters_exists (const ENamedParameters *parameters,
guint e_named_parameters_count (const ENamedParameters *parameters);
gchar * e_named_parameters_get_name (const ENamedParameters *parameters,
gint index);
+gboolean e_named_parameters_equal (const ENamedParameters *parameters1,
+ const ENamedParameters *parameters2);
#define e_named_timeout_add(interval, function, data) \
(e_timeout_add_with_name ( \
diff --git a/src/libedataserver/e-soup-session.c b/src/libedataserver/e-soup-session.c
index 7e811464a..d5c1b4f6f 100644
--- a/src/libedataserver/e-soup-session.c
+++ b/src/libedataserver/e-soup-session.c
@@ -44,6 +44,7 @@ G_DEFINE_QUARK (e-soup-session-error-quark, e_soup_session_error)
struct _ESoupSessionPrivate {
GMutex property_lock;
GRecMutex session_lock; /* libsoup3 has no thread safety */
+ GHashTable *using_auths; /* guarded by the session_lock, gchar *uri ~> gchar *authtype, as set in the
SoupAuthManager */
ESource *source;
ENamedParameters *credentials;
@@ -67,6 +68,38 @@ enum {
G_DEFINE_TYPE_WITH_PRIVATE (ESoupSession, e_soup_session, SOUP_TYPE_SESSION)
+/* Hold the session lock when calling this */
+static gboolean
+e_soup_session_auth_already_set_locked (ESoupSession *session,
+ GUri *g_uri,
+ SoupAuth *soup_auth,
+ gboolean *out_auth_was_set)
+{
+ gchar *uri_str;
+ const gchar *auth_type;
+ const gchar *current_auth_type;
+ gboolean same_types;
+
+ uri_str = g_uri_to_string_partial (g_uri, G_URI_HIDE_PASSWORD);
+ auth_type = G_OBJECT_TYPE_NAME (soup_auth);
+ current_auth_type = g_hash_table_lookup (session->priv->using_auths, uri_str);
+ *out_auth_was_set = current_auth_type != NULL;
+ same_types = g_strcmp0 (auth_type, current_auth_type) == 0;
+
+ if (!same_types) {
+ /* Because the caller calls soup_auth_manager_clear_cached_credentials() in this case */
+ if (*out_auth_was_set)
+ g_hash_table_remove_all (session->priv->using_auths);
+
+ g_hash_table_insert (session->priv->using_auths, uri_str, g_strdup (auth_type));
+ uri_str = NULL;
+ }
+
+ g_free (uri_str);
+
+ return same_types;
+}
+
static void
e_soup_session_ensure_auth_usage (ESoupSession *session,
GUri *in_g_uri,
@@ -77,6 +110,7 @@ e_soup_session_ensure_auth_usage (ESoupSession *session,
SoupSessionFeature *feature;
GUri *g_uri;
GType auth_type;
+ gboolean auth_was_set = FALSE;
g_return_if_fail (E_IS_SOUP_SESSION (session));
g_return_if_fail (SOUP_IS_AUTH (soup_auth));
@@ -121,10 +155,16 @@ e_soup_session_ensure_auth_usage (ESoupSession *session,
auth_manager = SOUP_AUTH_MANAGER (feature);
- /* This will make sure the 'soup_auth' is used regardless of the current 'auth_manager' state.
+ /* This will make sure the 'soup_auth' is used regardless of the current 'auth_manager' state,
+ but do not set the same 'soup_auth' when it's already set (which can happen, when the session
+ is reused by multiple sources, which connect to the same server, with the same user.
See https://gitlab.gnome.org/GNOME/libsoup/-/issues/196 for more information. */
- soup_auth_manager_clear_cached_credentials (auth_manager);
- soup_auth_manager_use_auth (auth_manager, g_uri, soup_auth);
+ if (!e_soup_session_auth_already_set_locked (session, g_uri, soup_auth, &auth_was_set)) {
+ if (auth_was_set)
+ soup_auth_manager_clear_cached_credentials (auth_manager);
+
+ soup_auth_manager_use_auth (auth_manager, g_uri, soup_auth);
+ }
g_rec_mutex_unlock (&session->priv->session_lock);
@@ -316,7 +356,8 @@ e_soup_session_maybe_prepare_auth (ESoupSession *session,
if (!soup_session_get_feature (soup_session, SOUP_TYPE_AUTH_NEGOTIATE))
soup_session_add_feature_by_type (soup_session, SOUP_TYPE_AUTH_NEGOTIATE);
- soup_session_remove_feature_by_type (soup_session, SOUP_TYPE_AUTH_BASIC);
+ if (soup_session_get_feature (soup_session, SOUP_TYPE_AUTH_BASIC))
+ soup_session_remove_feature_by_type (soup_session, SOUP_TYPE_AUTH_BASIC);
g_rec_mutex_unlock (&session->priv->session_lock);
} else if (g_strcmp0 (auth_method, "NTLM") == 0) {
@@ -356,6 +397,8 @@ e_soup_session_authenticate_cb (SoupMessage *message,
g_return_val_if_fail (E_IS_SOUP_SESSION (session), FALSE);
+ g_mutex_lock (&session->priv->property_lock);
+
if (E_IS_SOUP_AUTH_BEARER (auth)) {
g_object_ref (auth);
g_warn_if_fail ((gpointer) session->priv->using_bearer_auth == (gpointer) auth);
@@ -363,7 +406,6 @@ e_soup_session_authenticate_cb (SoupMessage *message,
session->priv->using_bearer_auth = E_SOUP_AUTH_BEARER (auth);
}
- g_mutex_lock (&session->priv->property_lock);
if (retrying && !session->priv->auth_prefilled) {
g_mutex_unlock (&session->priv->property_lock);
return FALSE;
@@ -407,8 +449,11 @@ e_soup_session_authenticate_cb (SoupMessage *message,
}
if (username && *username && credentials &&
- e_named_parameters_exists (credentials, E_SOURCE_CREDENTIAL_PASSWORD))
+ e_named_parameters_exists (credentials, E_SOURCE_CREDENTIAL_PASSWORD)) {
soup_auth_authenticate (auth, username, e_named_parameters_get (credentials,
E_SOURCE_CREDENTIAL_PASSWORD));
+ } else if (g_strcmp0 (soup_auth_get_scheme_name (auth), "NTLM") == 0) {
+ soup_auth_cancel (auth);
+ }
e_named_parameters_free (credentials);
g_free (auth_user);
@@ -485,6 +530,7 @@ e_soup_session_finalize (GObject *object)
g_clear_object (&session->priv->using_bearer_auth);
g_clear_pointer (&session->priv->credentials, e_named_parameters_free);
g_clear_pointer (&session->priv->ssl_certificate_pem, g_free);
+ g_clear_pointer (&session->priv->using_auths, g_hash_table_unref);
g_mutex_clear (&session->priv->property_lock);
g_rec_mutex_clear (&session->priv->session_lock);
@@ -546,6 +592,7 @@ static void
e_soup_session_init (ESoupSession *session)
{
session->priv = e_soup_session_get_instance_private (session);
+ session->priv->using_auths = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
session->priv->ssl_info_set = FALSE;
session->priv->log_level = SOUP_LOGGER_LOG_NONE;
session->priv->auth_prefilled = FALSE;
@@ -694,7 +741,7 @@ e_soup_session_set_credentials (ESoupSession *session,
g_mutex_lock (&session->priv->property_lock);
- if (credentials == session->priv->credentials) {
+ if (e_named_parameters_equal (credentials, session->priv->credentials)) {
g_mutex_unlock (&session->priv->property_lock);
return;
}
@@ -713,6 +760,7 @@ e_soup_session_set_credentials (ESoupSession *session,
g_rec_mutex_lock (&session->priv->session_lock);
feature = soup_session_get_feature (SOUP_SESSION (session), SOUP_TYPE_AUTH_MANAGER);
soup_auth_manager_clear_cached_credentials (SOUP_AUTH_MANAGER (feature));
+ g_hash_table_remove_all (session->priv->using_auths);
g_rec_mutex_unlock (&session->priv->session_lock);
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]