Title: [134955] trunk/Source
Revision
134955
Author
commit-qu...@webkit.org
Date
2012-11-16 08:40:01 -0800 (Fri, 16 Nov 2012)

Log Message

[GTK] Move CredentialBackingStore usage from GtkAuthenticationDialog to ResourceHandleSoup
https://bugs.webkit.org/show_bug.cgi?id=101840

Patch by Martin Robinson <mrobin...@igalia.com> on 2012-11-16
Reviewed by Gustavo Noronha Silva.

Source/WebCore:

Make ResourceHandleSoup aware of per-session CredentialStorage and persistent CredentialStorage.
Persistent credential storage interaction is moved from GtkAuthenticationDialog, so that it can
be used whether or not GtkAuthenticationDialog is used or not. We try to properly handle redirects
in the manner that the CFNet backend does.

No new tests. There are tests for this behavior, but they cannot be activated until we finish
plumbing this through to the API layer. Once that patch lands, the tests will be turned on.

* platform/gtk/GtkAuthenticationDialog.cpp: No longer store credentials into the persistent
storage manually, instead rely on ResourceHandleSoup. Also, we no longer get proposed credentials
from the persistent storage here as well. They are pre-loaded by the ResourceHanndle.
* platform/gtk/GtkAuthenticationDialog.h: Remove callbacks and members associated with saving
credentials to the persistent credential store.
* platform/network/ResourceHandle.h:
(ResourceHandle): Add a method which is used to continue asynchronously after looking for
proposed credentials in the persistent credential store.
* platform/network/ResourceHandleInternal.h: Add a member which tracks persistent credentials to be added once we know
an authentication succeeded.
* platform/network/gtk/CredentialBackingStore.cpp:
(CredentialForChallengeAsyncReadyCallbackData): Added this data structure used for asynchronous access
of stored credentials.
(WebCore::credentialForChallengeAsyncReadyCallback): Ditto for this callback.
(WebCore::CredentialBackingStore::credentialForChallenge): Make this method asynchronous.
* platform/network/gtk/CredentialBackingStore.h:
(CredentialBackingStore): Update method signatures for for making credentialForChallenge asynchronous.
* platform/network/soup/AuthenticationChallenge.h:
(WebCore::AuthenticationChallenge::setProposedCredential): Added a setter so that ResourceHandleSoup
can set proposed credentials from the persistent credential store.
* platform/network/soup/ResourceHandleSoup.cpp:
(WebCore::gotHeadersCallback): For GTK+ save any pending credential in the persistent credential storage
if the authentication succeeded.
(WebCore::applyAuthenticationToRequest): Added this method which generically embeds stored credentials
in the request URI. This is the method that Soup uses to override any soup-stored session credential.
(WebCore::restartedCallback): Strip credentials for requests that span a security origin. Handle
authenticating requests from the session store.
(WebCore::createSoupRequestAndMessageForHandle): Make the local request reference mutable.
(WebCore::ResourceHandle::start): Remove some code which is now part of applyAuthenticationToRequest.
Call applyAuthenticationToRequest and clear the user and password members like the CFNet backend does.
(WebCore::getCredentialFromPersistentStoreCallback): Added this callback for getting persistently stored credentials.
(WebCore::ResourceHandle::continueDidReceiveAuthenticationChallenge): Split out didReceiveAuthenticationChallenge
into this asynchronous bit.
(WebCore::ResourceHandle::didReceiveAuthenticationChallenge): For GTK+ continue handling this situation after
first looking in the persistent credential store.
(WebCore::ResourceHandle::receivedCredential): Store session credentials in the session storage, which is
at the moment a redundant version of the Soup session storage and also prepare any persistent credentials
for storage later (see gotHeadersCallback).

Source/WebKit/gtk:

Enable the CredentialStore by default for the WebKit1 GTK+ port. Before this value
didn't have an bearing on whether or not the persistent credential storage was used.
Now is does.

* WebCoreSupport/FrameLoaderClientGtk.cpp:
(WebKit::FrameLoaderClient::shouldUseCredentialStorage): Enable credential storage by default.

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (134954 => 134955)


--- trunk/Source/WebCore/ChangeLog	2012-11-16 16:22:08 UTC (rev 134954)
+++ trunk/Source/WebCore/ChangeLog	2012-11-16 16:40:01 UTC (rev 134955)
@@ -1,3 +1,57 @@
+2012-11-16  Martin Robinson  <mrobin...@igalia.com>
+
+        [GTK] Move CredentialBackingStore usage from GtkAuthenticationDialog to ResourceHandleSoup
+        https://bugs.webkit.org/show_bug.cgi?id=101840
+
+        Reviewed by Gustavo Noronha Silva.
+
+        Make ResourceHandleSoup aware of per-session CredentialStorage and persistent CredentialStorage.
+        Persistent credential storage interaction is moved from GtkAuthenticationDialog, so that it can
+        be used whether or not GtkAuthenticationDialog is used or not. We try to properly handle redirects
+        in the manner that the CFNet backend does.
+
+        No new tests. There are tests for this behavior, but they cannot be activated until we finish
+        plumbing this through to the API layer. Once that patch lands, the tests will be turned on.
+
+        * platform/gtk/GtkAuthenticationDialog.cpp: No longer store credentials into the persistent
+        storage manually, instead rely on ResourceHandleSoup. Also, we no longer get proposed credentials
+        from the persistent storage here as well. They are pre-loaded by the ResourceHanndle.
+        * platform/gtk/GtkAuthenticationDialog.h: Remove callbacks and members associated with saving
+        credentials to the persistent credential store.
+        * platform/network/ResourceHandle.h:
+        (ResourceHandle): Add a method which is used to continue asynchronously after looking for
+        proposed credentials in the persistent credential store.
+        * platform/network/ResourceHandleInternal.h: Add a member which tracks persistent credentials to be added once we know
+        an authentication succeeded.
+        * platform/network/gtk/CredentialBackingStore.cpp:
+        (CredentialForChallengeAsyncReadyCallbackData): Added this data structure used for asynchronous access
+        of stored credentials.
+        (WebCore::credentialForChallengeAsyncReadyCallback): Ditto for this callback.
+        (WebCore::CredentialBackingStore::credentialForChallenge): Make this method asynchronous.
+        * platform/network/gtk/CredentialBackingStore.h:
+        (CredentialBackingStore): Update method signatures for for making credentialForChallenge asynchronous.
+        * platform/network/soup/AuthenticationChallenge.h:
+        (WebCore::AuthenticationChallenge::setProposedCredential): Added a setter so that ResourceHandleSoup
+        can set proposed credentials from the persistent credential store.
+        * platform/network/soup/ResourceHandleSoup.cpp:
+        (WebCore::gotHeadersCallback): For GTK+ save any pending credential in the persistent credential storage
+        if the authentication succeeded.
+        (WebCore::applyAuthenticationToRequest): Added this method which generically embeds stored credentials
+        in the request URI. This is the method that Soup uses to override any soup-stored session credential.
+        (WebCore::restartedCallback): Strip credentials for requests that span a security origin. Handle
+        authenticating requests from the session store.
+        (WebCore::createSoupRequestAndMessageForHandle): Make the local request reference mutable.
+        (WebCore::ResourceHandle::start): Remove some code which is now part of applyAuthenticationToRequest.
+        Call applyAuthenticationToRequest and clear the user and password members like the CFNet backend does.
+        (WebCore::getCredentialFromPersistentStoreCallback): Added this callback for getting persistently stored credentials.
+        (WebCore::ResourceHandle::continueDidReceiveAuthenticationChallenge): Split out didReceiveAuthenticationChallenge
+        into this asynchronous bit.
+        (WebCore::ResourceHandle::didReceiveAuthenticationChallenge): For GTK+ continue handling this situation after
+        first looking in the persistent credential store.
+        (WebCore::ResourceHandle::receivedCredential): Store session credentials in the session storage, which is
+        at the moment a redundant version of the Soup session storage and also prepare any persistent credentials
+        for storage later (see gotHeadersCallback).
+
 2012-11-16  Erik Arvidsson  <a...@chromium.org>
 
         Update DOMException name: TypeMismatchError

Modified: trunk/Source/WebCore/platform/gtk/GtkAuthenticationDialog.cpp (134954 => 134955)


--- trunk/Source/WebCore/platform/gtk/GtkAuthenticationDialog.cpp	2012-11-16 16:22:08 UTC (rev 134954)
+++ trunk/Source/WebCore/platform/gtk/GtkAuthenticationDialog.cpp	2012-11-16 16:40:01 UTC (rev 134955)
@@ -66,8 +66,6 @@
     , m_loginEntry(0)
     , m_passwordEntry(0)
     , m_rememberCheckButton(0)
-    , m_isSavingPassword(false)
-    , m_savePasswordHandler(0)
 {
     GtkDialog* dialog = GTK_DIALOG(m_dialog);
     gtk_dialog_add_buttons(dialog, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
@@ -190,10 +188,10 @@
 
 void GtkAuthenticationDialog::show()
 {
-    Credential savedCredential = credentialBackingStore().credentialForChallenge(m_challenge);
-    if (!savedCredential.isEmpty()) {
-        gtk_entry_set_text(GTK_ENTRY(m_loginEntry), savedCredential.user().utf8().data());
-        gtk_entry_set_text(GTK_ENTRY(m_passwordEntry), savedCredential.password().utf8().data());
+    const Credential& credentialFromPersistentStorage = m_challenge.proposedCredential();
+    if (!credentialFromPersistentStorage.isEmpty()) {
+        gtk_entry_set_text(GTK_ENTRY(m_loginEntry), credentialFromPersistentStorage.user().utf8().data());
+        gtk_entry_set_text(GTK_ENTRY(m_passwordEntry), credentialFromPersistentStorage.password().utf8().data());
         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_rememberCheckButton), TRUE);
     }
 
@@ -204,39 +202,6 @@
 void GtkAuthenticationDialog::destroy()
 {
     gtk_widget_destroy(m_dialog);
-
-    // Do not delete the object if it's still saving the password,
-    // the save password callback will delete it.
-    if (!m_isSavingPassword)
-        delete this;
-}
-
-void GtkAuthenticationDialog::savePasswordCallback(SoupMessage*, GtkAuthenticationDialog* dialog)
-{
-    dialog->savePassword();
-}
-
-void GtkAuthenticationDialog::savePassword()
-{
-    ASSERT(!m_username.isNull());
-    ASSERT(!m_password.isNull());
-
-    // Anything but 401 and 5xx means the password was accepted.
-    SoupMessage* message = m_challenge.soupMessage();
-    if (message->status_code != 401 && message->status_code < 500) {
-        Credential credentialToSave = Credential(
-            String::fromUTF8(m_username.data()),
-            String::fromUTF8(m_password.data()),
-            CredentialPersistencePermanent);
-        credentialBackingStore().storeCredentialsForChallenge(m_challenge, credentialToSave);
-    }
-
-    // Disconnect the callback. If the authentication succeeded we are done,
-    // and if it failed we'll create a new GtkAuthenticationDialog and we'll
-    // connect to 'got-headers' again in GtkAuthenticationDialog::authenticate()
-    g_signal_handler_disconnect(message, m_savePasswordHandler);
-
-    // Dialog has been already destroyed, after saving the password it should be deleted.
     delete this;
 }
 
@@ -244,16 +209,8 @@
 {
     const char *username = gtk_entry_get_text(GTK_ENTRY(m_loginEntry));
     const char *password = gtk_entry_get_text(GTK_ENTRY(m_passwordEntry));
-
-    bool rememberCredentials = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(m_rememberCheckButton));
-    if (m_rememberCheckButton && gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(m_rememberCheckButton))) {
-        m_username = username;
-        m_password = password;
-        m_isSavingPassword = true;
-        m_savePasswordHandler = g_signal_connect(m_challenge.soupMessage(), "got-headers", G_CALLBACK(savePasswordCallback), this);
-    }
-
-    CredentialPersistence persistence = rememberCredentials ? CredentialPersistencePermanent : CredentialPersistenceForSession;
+    CredentialPersistence persistence = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(m_rememberCheckButton)) ?
+        CredentialPersistencePermanent : CredentialPersistenceForSession;
     m_challenge.authenticationClient()->receivedCredential(m_challenge,
         Credential(String::fromUTF8(username), String::fromUTF8(password), persistence));
 }

Modified: trunk/Source/WebCore/platform/gtk/GtkAuthenticationDialog.h (134954 => 134955)


--- trunk/Source/WebCore/platform/gtk/GtkAuthenticationDialog.h	2012-11-16 16:22:08 UTC (rev 134954)
+++ trunk/Source/WebCore/platform/gtk/GtkAuthenticationDialog.h	2012-11-16 16:40:01 UTC (rev 134955)
@@ -42,8 +42,6 @@
 private:
     void destroy();
     void authenticate();
-    void savePassword();
-    static void savePasswordCallback(SoupMessage*, GtkAuthenticationDialog*);
     static void authenticationDialogResponseCallback(GtkWidget*, gint responseID, GtkAuthenticationDialog*);
 
     AuthenticationChallenge m_challenge;
@@ -51,10 +49,6 @@
     GtkWidget* m_loginEntry;
     GtkWidget* m_passwordEntry;
     GtkWidget* m_rememberCheckButton;
-    bool m_isSavingPassword;
-    unsigned long m_savePasswordHandler;
-    CString m_username;
-    CString m_password;
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/platform/network/ResourceHandle.h (134954 => 134955)


--- trunk/Source/WebCore/platform/network/ResourceHandle.h	2012-11-16 16:22:08 UTC (rev 134954)
+++ trunk/Source/WebCore/platform/network/ResourceHandle.h	2012-11-16 16:40:01 UTC (rev 134955)
@@ -166,6 +166,7 @@
 #endif
 
 #if USE(SOUP)
+    void continueDidReceiveAuthenticationChallenge(const Credential& credentialFromPersistentStorage);
     void sendPendingRequest();
     static SoupSession* defaultSession();
     static uint64_t getSoupRequestInitiaingPageID(SoupRequest*);

Modified: trunk/Source/WebCore/platform/network/ResourceHandleInternal.h (134954 => 134955)


--- trunk/Source/WebCore/platform/network/ResourceHandleInternal.h	2012-11-16 16:22:08 UTC (rev 134954)
+++ trunk/Source/WebCore/platform/network/ResourceHandleInternal.h	2012-11-16 16:40:01 UTC (rev 134955)
@@ -194,6 +194,12 @@
         RefPtr<NetworkingContext> m_context;
         SoupSession* soupSession();
 #endif
+#if PLATFORM(GTK)
+        struct {
+            Credential credential;
+            AuthenticationChallenge challenge;
+        } m_credentialDataToSaveInPersistentStore;
+#endif
 #if PLATFORM(QT)
         QNetworkReplyHandler* m_job;
         RefPtr<NetworkingContext> m_context;

Modified: trunk/Source/WebCore/platform/network/gtk/CredentialBackingStore.cpp (134954 => 134955)


--- trunk/Source/WebCore/platform/network/gtk/CredentialBackingStore.cpp	2012-11-16 16:22:08 UTC (rev 134954)
+++ trunk/Source/WebCore/platform/network/gtk/CredentialBackingStore.cpp	2012-11-16 16:40:01 UTC (rev 134955)
@@ -62,32 +62,58 @@
     return attributes;
 }
 
-Credential CredentialBackingStore::credentialForChallenge(const AuthenticationChallenge& challenge)
+struct CredentialForChallengeAsyncReadyCallbackData {
+    CredentialBackingStore::CredentialForChallengeCallback callback;
+    void* data;
+};
+
+static void credentialForChallengeAsyncReadyCallback(SecretService* service, GAsyncResult* asyncResult, CredentialForChallengeAsyncReadyCallbackData* callbackData)
 {
-    GOwnPtr<GList> elements(secret_service_search_sync(
-        0, // The default SecretService.
-        SECRET_SCHEMA_COMPAT_NETWORK,
-        createAttributeHashTableFromChallenge(challenge).get(),
-        static_cast<SecretSearchFlags>(SECRET_SEARCH_UNLOCK | SECRET_SEARCH_LOAD_SECRETS), // The default behavior is to only return the most recent item.
-        0, // cancellable
-        0)); // error
-    if (!elements || !elements->data)
-        return Credential();
+    CredentialBackingStore::CredentialForChallengeCallback callback = callbackData->callback;
+    void* data = ""
+    delete callbackData;
 
+    GOwnPtr<GError> error;
+    GOwnPtr<GList> elements(secret_service_search_finish(service, asyncResult, &error.outPtr()));
+    if (error || !elements || !elements->data) {
+        callback(Credential(), data);
+        return;
+    }
+
     GRefPtr<SecretItem> secretItem = adoptGRef(static_cast<SecretItem*>(elements->data));
     GRefPtr<GHashTable> attributes = adoptGRef(secret_item_get_attributes(secretItem.get()));
     String user = String::fromUTF8(static_cast<const char*>(g_hash_table_lookup(attributes.get(), "user")));
-    if (user.isEmpty())
-        return Credential();
+    if (user.isEmpty()) {
+        callback(Credential(), data);
+        return;
+    }
 
     size_t length;
     GRefPtr<SecretValue> secretValue = adoptGRef(secret_item_get_secret(secretItem.get()));
     const char* passwordData = secret_value_get(secretValue.get(), &length);
     String password = String::fromUTF8(passwordData, length);
 
-    return Credential(user, password, CredentialPersistencePermanent);
+    callback(Credential(user, password, CredentialPersistencePermanent), data);
 }
 
+void CredentialBackingStore::credentialForChallenge(const AuthenticationChallenge& challenge, CredentialForChallengeCallback callback, void* data)
+{
+    // The default flag only returns the most recent item, not all of them.
+    SecretSearchFlags searchFlags = static_cast<SecretSearchFlags>(SECRET_SEARCH_UNLOCK | SECRET_SEARCH_LOAD_SECRETS);
+    CredentialForChallengeAsyncReadyCallbackData* callbackData = new CredentialForChallengeAsyncReadyCallbackData;
+    callbackData->callback = callback;
+    callbackData->data = ""
+
+    secret_service_search(
+        0, // The default SecretService.
+        SECRET_SCHEMA_COMPAT_NETWORK,
+        createAttributeHashTableFromChallenge(challenge).get(),
+        searchFlags,
+        0, // cancellable
+        reinterpret_cast<GAsyncReadyCallback>(credentialForChallengeAsyncReadyCallback),
+        callbackData);
+}
+
 void CredentialBackingStore::storeCredentialsForChallenge(const AuthenticationChallenge& challenge, const Credential& credential)
 {
     CString utf8Password = credential.password().utf8();

Modified: trunk/Source/WebCore/platform/network/gtk/CredentialBackingStore.h (134954 => 134955)


--- trunk/Source/WebCore/platform/network/gtk/CredentialBackingStore.h	2012-11-16 16:22:08 UTC (rev 134954)
+++ trunk/Source/WebCore/platform/network/gtk/CredentialBackingStore.h	2012-11-16 16:40:01 UTC (rev 134955)
@@ -38,9 +38,10 @@
     friend CredentialBackingStore& credentialBackingStore();
     virtual ~CredentialBackingStore() { }
 
-    Credential credentialForChallenge(const AuthenticationChallenge&);
+    typedef void (*CredentialForChallengeCallback)(const Credential&, void* data);
+
+    void credentialForChallenge(const AuthenticationChallenge&, CredentialForChallengeCallback, void* data);
     void storeCredentialsForChallenge(const AuthenticationChallenge&, const Credential&);
-
 private:
     CredentialBackingStore() { }
 };

Modified: trunk/Source/WebCore/platform/network/soup/AuthenticationChallenge.h (134954 => 134955)


--- trunk/Source/WebCore/platform/network/soup/AuthenticationChallenge.h	2012-11-16 16:22:08 UTC (rev 134954)
+++ trunk/Source/WebCore/platform/network/soup/AuthenticationChallenge.h	2012-11-16 16:40:01 UTC (rev 134955)
@@ -49,6 +49,7 @@
     SoupSession* soupSession() const { return m_soupSession.get(); }
     SoupMessage* soupMessage() const { return m_soupMessage.get(); }
     SoupAuth* soupAuth() const { return m_soupAuth.get(); }
+    void setProposedCredential(const Credential& credential) { m_proposedCredential = credential; }
 
 private:
     friend class AuthenticationChallengeBase;

Modified: trunk/Source/WebCore/platform/network/soup/ResourceHandleSoup.cpp (134954 => 134955)


--- trunk/Source/WebCore/platform/network/soup/ResourceHandleSoup.cpp	2012-11-16 16:22:08 UTC (rev 134954)
+++ trunk/Source/WebCore/platform/network/soup/ResourceHandleSoup.cpp	2012-11-16 16:40:01 UTC (rev 134955)
@@ -1,11 +1,12 @@
 /*
+ * Copyright (C) 2004, 2005, 2006, 2007, 2009, 2010, 2011 Apple Inc. All rights reserved.
  * Copyright (C) 2008 Alp Toker <a...@atoker.com>
  * Copyright (C) 2008 Xan Lopez <x...@gnome.org>
  * Copyright (C) 2008, 2010 Collabora Ltd.
  * Copyright (C) 2009 Holger Hans Peter Freyther
  * Copyright (C) 2009 Gustavo Noronha Silva <g...@gnome.org>
  * Copyright (C) 2009 Christian Dywan <christ...@imendio.com>
- * Copyright (C) 2009, 2010, 2011 Igalia S.L.
+ * Copyright (C) 2009, 2010, 2011, 2012 Igalia S.L.
  * Copyright (C) 2009 John Kjellberg <john.kjellb...@power.alstom.com>
  * Copyright (C) 2012 Intel Corporation
  *
@@ -31,6 +32,7 @@
 #include "CachedResourceLoader.h"
 #include "ChromeClient.h"
 #include "CookieJarSoup.h"
+#include "CredentialStorage.h"
 #include "FileSystem.h"
 #include "Frame.h"
 #include "GOwnPtrSoup.h"
@@ -68,6 +70,10 @@
 #include "BlobStorageData.h"
 #endif
 
+#if PLATFORM(GTK)
+#include "CredentialBackingStore.h"
+#endif
+
 namespace WebCore {
 
 #define READ_BUFFER_SIZE 8192
@@ -279,7 +285,7 @@
     return session;
 }
 
-static void gotHeadersCallback(SoupMessage* msg, gpointer data)
+static void gotHeadersCallback(SoupMessage* message, gpointer data)
 {
     ResourceHandle* handle = static_cast<ResourceHandle*>(data);
     if (!handle)
@@ -293,18 +299,65 @@
         d->m_response.resourceLoadTiming()->receiveHeadersEnd = milisecondsSinceRequest(d->m_response.resourceLoadTiming()->requestTime);
 #endif
 
+#if PLATFORM(GTK)
+    // We are a bit more conservative with the persistent credential storage than the session store,
+    // since we are waiting until we know that this authentication succeeded before actually storing.
+    // This is because we want to avoid hitting the disk twice (once to add and once to remove) for
+    // incorrect credentials or polluting the keychain with invalid credentials.
+    if (message->status_code != 401 && message->status_code < 500 && !d->m_credentialDataToSaveInPersistentStore.credential.isEmpty()) {
+        credentialBackingStore().storeCredentialsForChallenge(
+            d->m_credentialDataToSaveInPersistentStore.challenge,
+            d->m_credentialDataToSaveInPersistentStore.credential);
+    }
+#endif
+
     // The original response will be needed later to feed to willSendRequest in
     // restartedCallback() in case we are redirected. For this reason, so we store it
     // here.
     ResourceResponse response;
-    response.updateFromSoupMessage(msg);
-
+    response.updateFromSoupMessage(message);
     d->m_response = response;
 }
 
+static void applyAuthenticationToRequest(ResourceHandle* handle, bool redirect)
+{
+    // m_user/m_pass are credentials given manually, for instance, by the arguments passed to XMLHttpRequest.open().
+    ResourceHandleInternal* d = handle->getInternal();
+    String user = d->m_user;
+    String password = d->m_pass;
+
+    ResourceRequest& request = d->m_firstRequest;
+    if (!handle->client() || handle->client()->shouldUseCredentialStorage(handle)) {
+        if (d->m_user.isEmpty() && d->m_pass.isEmpty())
+            d->m_initialCredential = CredentialStorage::get(request.url());
+        else if (!redirect) {
+            // If there is already a protection space known for the URL, update stored credentials
+            // before sending a request. This makes it possible to implement logout by sending an
+            // XMLHttpRequest with known incorrect credentials, and aborting it immediately (so that
+            // an authentication dialog doesn't pop up).
+            CredentialStorage::set(Credential(d->m_user, d->m_pass, CredentialPersistenceNone), request.url());
+        }
+    }
+
+    if (!d->m_initialCredential.isEmpty()) {
+        user = d->m_initialCredential.user();
+        password = d->m_initialCredential.password();
+    }
+
+    // We always put the credentials into the URL. In the CFNetwork-port HTTP family credentials are applied in
+    // the didReceiveAuthenticationChallenge callback, but libsoup requires us to use this method to override
+    // any previously remembered credentials. It has its own per-session credential storage.
+    if (!user.isEmpty() || !password.isEmpty()) {
+        KURL urlWithCredentials(request.url());
+        urlWithCredentials.setUser(d->m_user);
+        urlWithCredentials.setPass(d->m_pass);
+        request.setURL(urlWithCredentials);
+    }
+}
+
 // Called each time the message is going to be sent again except the first time.
 // It's used mostly to let webkit know about redirects.
-static void restartedCallback(SoupMessage* msg, gpointer data)
+static void restartedCallback(SoupMessage* message, gpointer data)
 {
     ResourceHandle* handle = static_cast<ResourceHandle*>(data);
     if (!handle)
@@ -313,29 +366,50 @@
     if (d->m_cancelled)
         return;
 
-    GOwnPtr<char> uri(soup_uri_to_string(soup_message_get_uri(msg), false));
+    GOwnPtr<char> uri(soup_uri_to_string(soup_message_get_uri(message), false));
     String location = String::fromUTF8(uri.get());
     KURL newURL = KURL(handle->firstRequest().url(), location);
 
     ResourceRequest request = handle->firstRequest();
     request.setURL(newURL);
-    request.setHTTPMethod(msg->method);
+    request.setHTTPMethod(message->method);
 
     // Should not set Referer after a redirect from a secure resource to non-secure one.
     if (!request.url().protocolIs("https") && protocolIs(request.httpReferrer(), "https")) {
         request.clearHTTPReferrer();
-        soup_message_headers_remove(msg->request_headers, "Referer");
+        soup_message_headers_remove(message->request_headers, "Referer");
     }
 
+    const KURL& url = ""
+    d->m_user = url.user();
+    d->m_pass = url.pass();
+    request.removeCredentials();
+
+    ResourceResponse& redirectResponse = d->m_response;
+    if (!protocolHostAndPortAreEqual(request.url(), redirectResponse.url())) {
+        // If the network layer carries over authentication headers from the original request
+        // in a cross-origin redirect, we want to clear those headers here. 
+        request.clearHTTPAuthorization();
+        soup_message_headers_remove(message->request_headers, "Authorization");
+
+        // TODO: We are losing any username and password specified in the redirect URL, as this is the 
+        // same behavior as the CFNet port. We should investigate if this is really what we want.
+    } else
+        applyAuthenticationToRequest(handle, true);
+
+    // Per-request authentication is handled via the URI-embedded username/password.
+    GOwnPtr<SoupURI> newSoupURI(soup_uri_new(request.urlStringForSoup().utf8().data()));
+    soup_message_set_uri(message, newSoupURI.get());
+
     if (d->client())
-        d->client()->willSendRequest(handle, request, d->m_response);
+        d->client()->willSendRequest(handle, request, redirectResponse);
 
     if (d->m_cancelled)
         return;
 
 #if ENABLE(WEB_TIMING)
-    d->m_response.setResourceLoadTiming(ResourceLoadTiming::create());
-    d->m_response.resourceLoadTiming()->requestTime = monotonicallyIncreasingTime();
+    redirectResponse.setResourceLoadTiming(ResourceLoadTiming::create());
+    redirectResponse.resourceLoadTiming()->requestTime = monotonicallyIncreasingTime();
 #endif
 
     // Update the first party in case the base URL changed with the redirect
@@ -749,14 +823,15 @@
     SoupRequester* requester = SOUP_REQUESTER(soup_session_get_feature(d->soupSession(), SOUP_TYPE_REQUESTER));
 
     GOwnPtr<GError> error;
-    const ResourceRequest& request = handle->firstRequest();
+    ResourceRequest& request = handle->firstRequest();
+
     d->m_soupRequest = adoptGRef(soup_requester_request(requester, request.urlStringForSoup().utf8().data(), &error.outPtr()));
     if (error) {
         d->m_soupRequest.clear();
         return false;
     }
 
-    // Non-HTTP family requests do not need a soupMessage, as it's callbacks really only apply to HTTP.
+    // SoupMessages are only applicable to HTTP-family requests.
     if (isHTTPFamilyRequest && !createSoupMessageForHandleAndRequest(handle, request)) {
         d->m_soupRequest.clear();
         return false;
@@ -780,15 +855,6 @@
     // Used to set the keep track of custom SoupSessions for ports that support it (EFL).
     d->m_context = context;
 
-    if (!(d->m_user.isEmpty() || d->m_pass.isEmpty())) {
-        // If credentials were specified for this request, add them to the url,
-        // so that they will be passed to NetworkRequest.
-        KURL urlWithCredentials(firstRequest().url());
-        urlWithCredentials.setUser(d->m_user);
-        urlWithCredentials.setPass(d->m_pass);
-        d->m_firstRequest.setURL(urlWithCredentials);
-    }
-
     // Only allow the POST and GET methods for non-HTTP requests.
     const ResourceRequest& request = firstRequest();
     bool isHTTPFamilyRequest = request.url().protocolIsInHTTPFamily();
@@ -797,6 +863,11 @@
         return true;
     }
 
+    applyAuthenticationToRequest(this, false);
+    // The CFNet backend clears these, so we do as well.
+    d->m_user = String();
+    d->m_pass = String();
+
     if (!createSoupRequestAndMessageForHandle(this, isHTTPFamilyRequest)) {
         this->scheduleFailure(InvalidURLFailure); // Error must not be reported immediately
         return true;
@@ -856,17 +927,87 @@
     gIgnoreSSLErrors = ignoreSSLErrors;
 }
 
+#if PLATFORM(GTK)
+void getCredentialFromPersistentStoreCallback(const Credential& credential, void* data)
+{
+    static_cast<ResourceHandle*>(data)->continueDidReceiveAuthenticationChallenge(credential);
+}
+#endif
+
+void ResourceHandle::continueDidReceiveAuthenticationChallenge(const Credential& credentialFromPersistentStorage)
+{
+    ASSERT(d->m_currentWebChallenge);
+    AuthenticationChallenge& challenge = d->m_currentWebChallenge;
+
+    ASSERT(challenge.soupSession());
+    ASSERT(challenge.soupMessage());
+    if (!credentialFromPersistentStorage.isEmpty())
+        challenge.setProposedCredential(credentialFromPersistentStorage);
+
+    if (!client()) {
+        soup_session_unpause_message(challenge.soupSession(), challenge.soupMessage());
+        clearAuthentication();
+        return;
+    }
+
+    ASSERT(challenge.soupSession());
+    ASSERT(challenge.soupMessage());
+    client()->didReceiveAuthenticationChallenge(this, challenge);
+}
+
 void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge& challenge)
 {
     ASSERT(d->m_currentWebChallenge.isNull());
+
+    bool shouldUseCredentialStorage = !client() || client()->shouldUseCredentialStorage(this);
+    if (!d->m_user.isNull() && !d->m_pass.isNull()) {
+        Credential credential = Credential(d->m_user, d->m_pass, CredentialPersistenceForSession);
+        if (shouldUseCredentialStorage)
+            CredentialStorage::set(credential, challenge.protectionSpace(), challenge.failureResponse().url());
+        soup_auth_authenticate(challenge.soupAuth(), credential.user().utf8().data(), credential.password().utf8().data());
+
+        return;
+    }
+
+    // FIXME: Per the specification, the user shouldn't be asked for credentials if there were incorrect ones provided explicitly.
+    if (shouldUseCredentialStorage) {
+        if (!d->m_initialCredential.isEmpty() || challenge.previousFailureCount()) {
+            // The stored credential wasn't accepted, stop using it. There is a race condition
+            // here, since a different credential might have already been stored by another
+            // ResourceHandle, but the observable effect should be very minor, if any.
+            CredentialStorage::remove(challenge.protectionSpace());
+        }
+
+        if (!challenge.previousFailureCount()) {
+            Credential credential = CredentialStorage::get(challenge.protectionSpace());
+            if (!credential.isEmpty() && credential != d->m_initialCredential) {
+                ASSERT(credential.persistence() == CredentialPersistenceNone);
+
+                // Store the credential back, possibly adding it as a default for this directory.
+                if (challenge.failureResponse().httpStatusCode() == 401)
+                    CredentialStorage::set(credential, challenge.protectionSpace(), challenge.failureResponse().url());
+
+                soup_auth_authenticate(challenge.soupAuth(), credential.user().utf8().data(), credential.password().utf8().data());
+                return;
+            }
+        }
+    }
+
     d->m_currentWebChallenge = challenge;
+    soup_session_pause_message(challenge.soupSession(), challenge.soupMessage());
 
-    if (client()) {
-        ASSERT(challenge.soupSession());
-        ASSERT(challenge.soupMessage());
-        soup_session_pause_message(challenge.soupSession(), challenge.soupMessage());
-        client()->didReceiveAuthenticationChallenge(this, challenge);
+#if PLATFORM(GTK)
+    // We could also do this before we even start the request, but that would be at the expense
+    // of all request latency, versus a one-time latency for the small subset of requests that
+    // use HTTP authentication. In the end, this doesn't matter much, because persistent credentials
+    // will become session credentials after the first use.
+    if (shouldUseCredentialStorage) {
+        credentialBackingStore().credentialForChallenge(challenge, getCredentialFromPersistentStoreCallback, this);
+        return;
     }
+#endif
+
+    continueDidReceiveAuthenticationChallenge(Credential());
 }
 
 void ResourceHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge& challenge)
@@ -885,6 +1026,26 @@
     if (challenge != d->m_currentWebChallenge)
         return;
 
+    // FIXME: Support empty credentials. Currently, an empty credential cannot be stored in WebCore credential storage, as that's empty value for its map.
+    if (credential.isEmpty()) {
+        receivedRequestToContinueWithoutCredential(challenge);
+        return;
+    }
+
+    // Eventually we will manage per-session credentials only internally or use some newly-exposed API from libsoup,
+    // because once we authenticate via libsoup, there is no way to ignore it for a particular request. Right now,
+    // we place the credentials in the store even though libsoup will never fire the authenticate signal again for
+    // this protection space.
+    if (credential.persistence() == CredentialPersistenceForSession || credential.persistence() == CredentialPersistencePermanent)
+        CredentialStorage::set(credential, challenge.protectionSpace(), challenge.failureResponse().url());
+
+#if PLATFORM(GTK)
+    if (credential.persistence() == CredentialPersistencePermanent) {
+        d->m_credentialDataToSaveInPersistentStore.credential = credential;
+        d->m_credentialDataToSaveInPersistentStore.challenge = challenge;
+    }
+#endif
+
     ASSERT(challenge.soupSession());
     ASSERT(challenge.soupMessage());
     soup_auth_authenticate(challenge.soupAuth(), credential.user().utf8().data(), credential.password().utf8().data());

Modified: trunk/Source/WebKit/gtk/ChangeLog (134954 => 134955)


--- trunk/Source/WebKit/gtk/ChangeLog	2012-11-16 16:22:08 UTC (rev 134954)
+++ trunk/Source/WebKit/gtk/ChangeLog	2012-11-16 16:40:01 UTC (rev 134955)
@@ -1,3 +1,17 @@
+2012-11-16  Martin Robinson  <mrobin...@igalia.com>
+
+        [GTK] Move CredentialBackingStore usage from GtkAuthenticationDialog to ResourceHandleSoup
+        https://bugs.webkit.org/show_bug.cgi?id=101840
+
+        Reviewed by Gustavo Noronha Silva.
+
+        Enable the CredentialStore by default for the WebKit1 GTK+ port. Before this value
+        didn't have an bearing on whether or not the persistent credential storage was used.
+        Now is does.
+
+        * WebCoreSupport/FrameLoaderClientGtk.cpp:
+        (WebKit::FrameLoaderClient::shouldUseCredentialStorage): Enable credential storage by default.
+
 2012-11-15  Gustavo Noronha Silva  <g...@gnome.org>
 
         [GTK] Split WebCore/platform into a separate library

Modified: trunk/Source/WebKit/gtk/WebCoreSupport/FrameLoaderClientGtk.cpp (134954 => 134955)


--- trunk/Source/WebKit/gtk/WebCoreSupport/FrameLoaderClientGtk.cpp	2012-11-16 16:22:08 UTC (rev 134954)
+++ trunk/Source/WebKit/gtk/WebCoreSupport/FrameLoaderClientGtk.cpp	2012-11-16 16:40:01 UTC (rev 134955)
@@ -192,11 +192,9 @@
     }
 }
 
-bool
-FrameLoaderClient::shouldUseCredentialStorage(WebCore::DocumentLoader*, unsigned long  identifier)
+bool FrameLoaderClient::shouldUseCredentialStorage(WebCore::DocumentLoader*, unsigned long  identifier)
 {
-    notImplemented();
-    return false;
+    return true;
 }
 
 void FrameLoaderClient::dispatchDidReceiveAuthenticationChallenge(WebCore::DocumentLoader*, unsigned long  identifier, const AuthenticationChallenge& challenge)
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
http://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to