[libsoup] Add API to disable using cached credentials for a particular SoupMessage



commit efcb377dde512f453ee90e31682540678f37fdae
Author: Carlos Garcia Campos <cgarcia igalia com>
Date:   Mon Nov 7 11:28:28 2016 +0100

    Add API to disable using cached credentials for a particular SoupMessage
    
    Add SOUP_MESSAGE_DO_NOT_USE_AUTH_CACHE new flag to tell the
    SoupAuthManager that it shoudn't use cached credentials to authenticate a
    particular message, and that credentials shouldn't be cached either
    after a successful authentication.
    When SOUP_MESSAGE_DO_NOT_USE_AUTH_CACHE flag is present for a message,
    we never query the cached credentials on message starting callback, so
    that the SoupAuth is not set there. The SoupAuth is now always set on
    the message after authenticate, and the Authorization header updated
    right before the message is re-queued when SOUP_MESSAGE_DO_NOT_USE_AUTH_CACHE
    is used.
    This patch also updates soup_message_set_auth() to no longer update the
    Authorization header, but just set the SoupAuth handling the case of
    setting the same SoupAuth twice.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=774033

 libsoup/soup-auth-manager.c |   99 ++++++++++++++++++++++++++++++++++++-------
 libsoup/soup-message.c      |   43 +++++++------------
 libsoup/soup-message.h      |    3 +-
 tests/auth-test.c           |   63 ++++++++++++++++++++++++---
 4 files changed, 157 insertions(+), 51 deletions(-)
---
diff --git a/libsoup/soup-auth-manager.c b/libsoup/soup-auth-manager.c
index 4e1645c..6998191 100644
--- a/libsoup/soup-auth-manager.c
+++ b/libsoup/soup-auth-manager.c
@@ -440,11 +440,35 @@ make_auto_ntlm_auth (SoupAuthManagerPrivate *priv, SoupAuthHost *host)
        return TRUE;
 }
 
+static void
+update_authorization_header (SoupMessage *msg, SoupAuth *auth, gboolean is_proxy)
+{
+       const char *authorization_header = is_proxy ? "Proxy-Authorization" : "Authorization";
+       char *token;
+
+       soup_message_headers_remove (msg->request_headers, authorization_header);
+       if (!auth)
+               return;
+
+       token = soup_auth_get_authorization (auth, msg);
+       if (!token)
+               return;
+
+       soup_message_headers_append (msg->request_headers, authorization_header, token);
+       g_free (token);
+}
+
 static SoupAuth *
 lookup_auth (SoupAuthManagerPrivate *priv, SoupMessage *msg)
 {
        SoupAuthHost *host;
        const char *path, *realm;
+       SoupAuth *auth;
+
+       /* If the message already has a ready auth, use that instead */
+       auth = soup_message_get_auth (msg);
+       if (auth && soup_auth_is_ready (auth, msg))
+               return auth;
 
        host = get_auth_host_for_uri (priv, soup_message_get_uri (msg));
        if (!host->auth_realms && !make_auto_ntlm_auth (priv, host))
@@ -456,8 +480,21 @@ lookup_auth (SoupAuthManagerPrivate *priv, SoupMessage *msg)
        realm = soup_path_map_lookup (host->auth_realms, path);
        if (realm)
                return g_hash_table_lookup (host->auths, realm);
-       else
-               return NULL;
+
+       return NULL;
+}
+
+static SoupAuth *
+lookup_proxy_auth (SoupAuthManagerPrivate *priv, SoupMessage *msg)
+{
+       SoupAuth *auth;
+
+       /* If the message already has a ready auth, use that instead */
+       auth = soup_message_get_proxy_auth (msg);
+       if (auth && soup_auth_is_ready (auth, msg))
+               return auth;
+
+       return priv->proxy_auth;
 }
 
 static void
@@ -555,7 +592,7 @@ static void
 auth_got_headers (SoupMessage *msg, gpointer manager)
 {
        SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER (manager)->priv;
-       SoupAuth *auth, *prior_auth, *new_auth;
+       SoupAuth *auth, *prior_auth;
        gboolean prior_auth_failed = FALSE;
 
        g_mutex_lock (&priv->lock);
@@ -574,13 +611,20 @@ auth_got_headers (SoupMessage *msg, gpointer manager)
                }
        }
 
-       new_auth = record_auth_for_uri (priv, soup_message_get_uri (msg),
-                                       auth, prior_auth_failed);
-       g_object_unref (auth);
+       if (!(soup_message_get_flags (msg) & SOUP_MESSAGE_DO_NOT_USE_AUTH_CACHE)) {
+               SoupAuth *new_auth;
+
+               new_auth = record_auth_for_uri (priv, soup_message_get_uri (msg),
+                                               auth, prior_auth_failed);
+               g_object_unref (auth);
+               auth = g_object_ref (new_auth);
+       }
 
        /* If we need to authenticate, try to do it. */
-       authenticate_auth (manager, new_auth, msg,
+       authenticate_auth (manager, auth, msg,
                           prior_auth_failed, FALSE, TRUE);
+       soup_message_set_auth (msg, soup_auth_is_ready (auth, msg) ? auth : NULL);
+       g_object_unref (auth);
        g_mutex_unlock (&priv->lock);
 }
 
@@ -600,6 +644,12 @@ auth_got_body (SoupMessage *msg, gpointer manager)
                        soup_message_set_flags (msg, flags & ~SOUP_MESSAGE_NEW_CONNECTION);
                }
 
+               /* When not using cached credentials, update the Authorization header
+                * right before requeuing the message.
+                */
+               if (soup_message_get_flags (msg) & SOUP_MESSAGE_DO_NOT_USE_AUTH_CACHE)
+                       update_authorization_header (msg, auth, FALSE);
+
                soup_session_requeue_message (priv->session, msg);
        }
        g_mutex_unlock (&priv->lock);
@@ -609,7 +659,7 @@ static void
 proxy_auth_got_headers (SoupMessage *msg, gpointer manager)
 {
        SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER (manager)->priv;
-       SoupAuth *prior_auth;
+       SoupAuth *auth = NULL, *prior_auth;
        gboolean prior_auth_failed = FALSE;
 
        g_mutex_lock (&priv->lock);
@@ -621,17 +671,24 @@ proxy_auth_got_headers (SoupMessage *msg, gpointer manager)
                        prior_auth_failed = TRUE;
        }
 
-       if (!priv->proxy_auth) {
-               priv->proxy_auth = create_auth (priv, msg);
-               if (!priv->proxy_auth) {
+       if (!(soup_message_get_flags (msg) & SOUP_MESSAGE_DO_NOT_USE_AUTH_CACHE))
+               auth = priv->proxy_auth ? g_object_ref (priv->proxy_auth) : NULL;
+
+       if (!auth) {
+               auth = create_auth (priv, msg);
+               if (!auth) {
                        g_mutex_unlock (&priv->lock);
                        return;
                }
+               if (!(soup_message_get_flags (msg) & SOUP_MESSAGE_DO_NOT_USE_AUTH_CACHE))
+                       priv->proxy_auth = g_object_ref (auth);
        }
 
        /* If we need to authenticate, try to do it. */
-       authenticate_auth (manager, priv->proxy_auth, msg,
+       authenticate_auth (manager, auth, msg,
                           prior_auth_failed, TRUE, TRUE);
+       soup_message_set_proxy_auth (msg, soup_auth_is_ready (auth, msg) ? auth : NULL);
+       g_object_unref (auth);
        g_mutex_unlock (&priv->lock);
 }
 
@@ -642,10 +699,17 @@ proxy_auth_got_body (SoupMessage *msg, gpointer manager)
        SoupAuth *auth;
 
        g_mutex_lock (&priv->lock);
-       auth = priv->proxy_auth;
 
-       if (auth && soup_auth_is_ready (auth, msg))
+       auth = lookup_proxy_auth (priv, msg);
+       if (auth && soup_auth_is_ready (auth, msg)) {
+               /* When not using cached credentials, update the Authorization header
+                * right before requeuing the message.
+                */
+               if (soup_message_get_flags (msg) & SOUP_MESSAGE_DO_NOT_USE_AUTH_CACHE)
+                       update_authorization_header (msg, auth, TRUE);
                soup_session_requeue_message (priv->session, msg);
+       }
+
        g_mutex_unlock (&priv->lock);
 }
 
@@ -655,6 +719,9 @@ auth_msg_starting (SoupMessage *msg, gpointer manager)
        SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER (manager)->priv;
        SoupAuth *auth;
 
+       if (soup_message_get_flags (msg) & SOUP_MESSAGE_DO_NOT_USE_AUTH_CACHE)
+               return;
+
        g_mutex_lock (&priv->lock);
 
        if (msg->method != SOUP_METHOD_CONNECT) {
@@ -665,15 +732,17 @@ auth_msg_starting (SoupMessage *msg, gpointer manager)
                                auth = NULL;
                }
                soup_message_set_auth (msg, auth);
+               update_authorization_header (msg, auth, FALSE);
        }
 
-       auth = priv->proxy_auth;
+       auth = lookup_proxy_auth (priv, msg);
        if (auth) {
                authenticate_auth (manager, auth, msg, FALSE, TRUE, FALSE);
                if (!soup_auth_is_ready (auth, msg))
                        auth = NULL;
        }
        soup_message_set_proxy_auth (msg, auth);
+       update_authorization_header (msg, auth, TRUE);
 
        g_mutex_unlock (&priv->lock);
 }
diff --git a/libsoup/soup-message.c b/libsoup/soup-message.c
index f862673..6287ac1 100644
--- a/libsoup/soup-message.c
+++ b/libsoup/soup-message.c
@@ -1304,29 +1304,18 @@ void
 soup_message_set_auth (SoupMessage *msg, SoupAuth *auth)
 {
        SoupMessagePrivate *priv;
-       char *token;
 
        g_return_if_fail (SOUP_IS_MESSAGE (msg));
        g_return_if_fail (auth == NULL || SOUP_IS_AUTH (auth));
 
        priv = SOUP_MESSAGE_GET_PRIVATE (msg);
 
-       if (priv->auth) {
-               g_object_unref (priv->auth);
-               soup_message_headers_remove (msg->request_headers,
-                                            "Authorization");
-       }
-       priv->auth = auth;
-       if (!priv->auth)
+       if (priv->auth == auth)
                return;
 
-       g_object_ref (priv->auth);
-       token = soup_auth_get_authorization (auth, msg);
-       if (token) {
-               soup_message_headers_replace (msg->request_headers,
-                                             "Authorization", token);
-               g_free (token);
-       }
+       if (priv->auth)
+               g_object_unref (priv->auth);
+       priv->auth = auth ? g_object_ref (auth) : NULL;
 }
 
 SoupAuth *
@@ -1341,27 +1330,18 @@ void
 soup_message_set_proxy_auth (SoupMessage *msg, SoupAuth *auth)
 {
        SoupMessagePrivate *priv;
-       char *token;
 
        g_return_if_fail (SOUP_IS_MESSAGE (msg));
        g_return_if_fail (auth == NULL || SOUP_IS_AUTH (auth));
 
        priv = SOUP_MESSAGE_GET_PRIVATE (msg);
 
-       if (priv->proxy_auth) {
-               g_object_unref (priv->proxy_auth);
-               soup_message_headers_remove (msg->request_headers,
-                                            "Proxy-Authorization");
-       }
-       priv->proxy_auth = auth;
-       if (!priv->proxy_auth)
+       if (priv->proxy_auth == auth)
                return;
 
-       g_object_ref (priv->proxy_auth);
-       token = soup_auth_get_authorization (auth, msg);
-       soup_message_headers_replace (msg->request_headers,
-                                     "Proxy-Authorization", token);
-       g_free (token);
+       if (priv->proxy_auth)
+               g_object_unref (priv->proxy_auth);
+       priv->proxy_auth = auth ? g_object_ref (auth) : NULL;
 }
 
 SoupAuth *
@@ -1455,6 +1435,13 @@ soup_message_cleanup_response (SoupMessage *msg)
  *   connection limits has been reached. If a dedicated connection is
  *   eventually created for this message, it will be dropped when the
  *   message finishes. Since 2.50
+ * @SOUP_MESSAGE_DO_NOT_USE_AUTH_CACHE: The #SoupAuthManager should not use
+ *   the credentials cache for this message, neither to use cached credentials
+ *   to automatically authenticate this message nor to cache the credentials
+ *   after the message is successfully authenticated. This applies to both server
+ *   and proxy authentication. Note that #SoupSession::authenticate signal will
+ *   be emitted, if you want to disable authentication for a message use
+ *   soup_message_disable_feature() passing #SOUP_TYPE_AUTH_MANAGER instead. Since 2.58
  *
  * Various flags that can be set on a #SoupMessage to alter its
  * behavior.
diff --git a/libsoup/soup-message.h b/libsoup/soup-message.h
index 1e46b28..cab2bad 100644
--- a/libsoup/soup-message.h
+++ b/libsoup/soup-message.h
@@ -137,7 +137,8 @@ typedef enum {
        SOUP_MESSAGE_CERTIFICATE_TRUSTED      = (1 << 5),
        SOUP_MESSAGE_NEW_CONNECTION           = (1 << 6),
        SOUP_MESSAGE_IDEMPOTENT               = (1 << 7),
-       SOUP_MESSAGE_IGNORE_CONNECTION_LIMITS = (1 << 8)
+       SOUP_MESSAGE_IGNORE_CONNECTION_LIMITS = (1 << 8),
+       SOUP_MESSAGE_DO_NOT_USE_AUTH_CACHE    = (1 << 9)
 } SoupMessageFlags;
 
 SOUP_AVAILABLE_IN_2_4
diff --git a/tests/auth-test.c b/tests/auth-test.c
index cf1f246..b64af07 100644
--- a/tests/auth-test.c
+++ b/tests/auth-test.c
@@ -414,13 +414,18 @@ digest_nonce_unauthorized (SoupMessage *msg, gpointer data)
 
 static void
 do_digest_nonce_test (SoupSession *session,
-                     const char *nth, const char *uri,
+                     const char *nth, const char *uri, gboolean use_auth_cache,
                      gboolean expect_401, gboolean expect_signal)
 {
        SoupMessage *msg;
        gboolean got_401;
 
        msg = soup_message_new (SOUP_METHOD_GET, uri);
+       if (!use_auth_cache) {
+               SoupMessageFlags flags = soup_message_get_flags (msg);
+
+               soup_message_set_flags (msg, flags | SOUP_MESSAGE_DO_NOT_USE_AUTH_CACHE);
+       }
        if (expect_signal) {
                g_signal_connect (session, "authenticate",
                                  G_CALLBACK (digest_nonce_authenticate),
@@ -451,15 +456,15 @@ do_digest_expiration_test (void)
        session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
 
        uri = g_strconcat (base_uri, "Digest/realm1/", NULL);
-       do_digest_nonce_test (session, "First", uri, TRUE, TRUE);
+       do_digest_nonce_test (session, "First", uri, TRUE, TRUE, TRUE);
        g_free (uri);
        sleep (2);
        uri = g_strconcat (base_uri, "Digest/realm1/expire/", NULL);
-       do_digest_nonce_test (session, "Second", uri, TRUE, FALSE);
+       do_digest_nonce_test (session, "Second", uri, TRUE, TRUE, FALSE);
        sleep (1);
-       do_digest_nonce_test (session, "Third", uri, FALSE, FALSE);
+       do_digest_nonce_test (session, "Third", uri, TRUE, FALSE, FALSE);
        sleep (1);
-       do_digest_nonce_test (session, "Fourth", uri, FALSE, FALSE);
+       do_digest_nonce_test (session, "Fourth", uri, TRUE, FALSE, FALSE);
        g_free (uri);
 
        soup_test_session_abort_unref (session);
@@ -1276,12 +1281,55 @@ do_clear_credentials_test (void)
        session = soup_test_session_new (SOUP_TYPE_SESSION, NULL);
 
        uri = g_strconcat (base_uri, "Digest/realm1/", NULL);
-       do_digest_nonce_test (session, "First", uri, TRUE, TRUE);
+       do_digest_nonce_test (session, "First", uri, TRUE, TRUE, TRUE);
+
+       manager = SOUP_AUTH_MANAGER (soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER));
+       soup_auth_manager_clear_cached_credentials (manager);
+
+       do_digest_nonce_test (session, "Second", uri, TRUE, TRUE, TRUE);
+       g_free (uri);
+
+       soup_test_session_abort_unref (session);
+}
+
+static void
+do_message_do_not_use_auth_cache_test (void)
+{
+       SoupSession *session;
+       SoupAuthManager *manager;
+       SoupURI *soup_uri;
+       char *uri;
+       char *uri_with_credentials;
+
+       SOUP_TEST_SKIP_IF_NO_APACHE;
+
+       session = soup_test_session_new (SOUP_TYPE_SESSION, NULL);
+
+       uri = g_strconcat (base_uri, "Digest/realm1/", NULL);
+
+       /* First check that cached credentials are not used */
+       do_digest_nonce_test (session, "First", uri, TRUE, TRUE, TRUE);
+       do_digest_nonce_test (session, "Second", uri, TRUE, FALSE, FALSE);
+       do_digest_nonce_test (session, "Third", uri, FALSE, TRUE, TRUE);
+
+       /* Passing credentials in the URI should always authenticate
+        * no matter whether the cache is used or not
+        */
+       soup_uri = soup_uri_new (uri);
+       soup_uri_set_user (soup_uri, "user1");
+       soup_uri_set_password (soup_uri, "realm1");
+       uri_with_credentials = soup_uri_to_string (soup_uri, FALSE);
+       soup_uri_free (soup_uri);
+       do_digest_nonce_test (session, "Fourth", uri_with_credentials, FALSE, TRUE, FALSE);
+       g_free (uri_with_credentials);
 
        manager = SOUP_AUTH_MANAGER (soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER));
        soup_auth_manager_clear_cached_credentials (manager);
 
-       do_digest_nonce_test (session, "Second", uri, TRUE, TRUE);
+       /* Now check that credentials are not stored */
+       do_digest_nonce_test (session, "First", uri, FALSE, TRUE, TRUE);
+       do_digest_nonce_test (session, "Second", uri, TRUE, TRUE, TRUE);
+       do_digest_nonce_test (session, "Third", uri, TRUE, FALSE, FALSE);
        g_free (uri);
 
        soup_test_session_abort_unref (session);
@@ -1310,6 +1358,7 @@ main (int argc, char **argv)
        g_test_add_func ("/auth/infinite-auth", do_infinite_auth_test);
        g_test_add_func ("/auth/disappearing-auth", do_disappearing_auth_test);
        g_test_add_func ("/auth/clear-credentials", do_clear_credentials_test);
+       g_test_add_func ("/auth/message-do-not-use-auth-cache", do_message_do_not_use_auth_cache_test);
 
        ret = g_test_run ();
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]