REGRESSION(r230681) Do not use stored credentials if WKBundlePageResourceLoadClient...
authorachristensen@apple.com <achristensen@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 22 Apr 2019 23:05:48 +0000 (23:05 +0000)
committerachristensen@apple.com <achristensen@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 22 Apr 2019 23:05:48 +0000 (23:05 +0000)
https://bugs.webkit.org/show_bug.cgi?id=197093
<rdar://problem/49708268>

Reviewed by Chris Dumez.

Source/WebKit:

Only get the StoredCredentialsPolicy from the NetworkLoadChecker if we haven't already been told not to use credentials.
Also add some test infrastructure for clearing persistent credentials added by the test.

* NetworkProcess/NetworkProcess.cpp:
(WebKit::NetworkProcess::removeCredential):
* NetworkProcess/NetworkProcess.h:
* NetworkProcess/NetworkProcess.messages.in:
* NetworkProcess/NetworkResourceLoader.cpp:
(WebKit::NetworkResourceLoader::startNetworkLoad):
* NetworkProcess/cocoa/NetworkProcessCocoa.mm:
(WebKit::NetworkProcess::removeCredential):
* UIProcess/API/Cocoa/WKProcessPool.mm:
(-[WKProcessPool _removeCredential:forProtectionSpace:completionHandler:]):
* UIProcess/API/Cocoa/WKProcessPoolPrivate.h:
* UIProcess/WebProcessPool.cpp:
(WebKit::WebProcessPool::removeCredential):
* UIProcess/WebProcessPool.h:

Tools:

Add a test that does two loads.  The first load shouldUseCredentialStorage returns true and we provide a persistent credential.
The second load shouldUseCredentialStorage returns false and we verify that a challenge is received with no suggested credential.
We also need to make the TCPServer able to handle more than one connection because we need these two loads to come from the same protection space,
and our current Cocoa implementation of NetworkSession uses two NSURLSessions that don't share a connection cache, one for loads with credentials
and one for loads without credentials, so there are two TCP connections to the same server in this test.

* TestWebKitAPI/TCPServer.cpp:
(TestWebKitAPI::TCPServer::TCPServer):
(TestWebKitAPI::TCPServer::~TCPServer):
(TestWebKitAPI::TCPServer::socketBindListen):
(TestWebKitAPI::TCPServer::waitForAndReplyToRequests): Deleted.
* TestWebKitAPI/TCPServer.h:
* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WebKitCocoa/BasicProposedCredentialPlugIn.mm: Added.
(-[BasicProposedCredentialPlugIn webProcessPlugIn:didCreateBrowserContextController:]):
* TestWebKitAPI/Tests/WebKitCocoa/Challenge.mm:
(respondWithChallengeThenOK):
(TestWebKitAPI::TEST):
(-[ProposedCredentialDelegate webView:didFinishNavigation:]):
(-[ProposedCredentialDelegate webView:didReceiveAuthenticationChallenge:completionHandler:]):
(TEST):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@244521 268f45cc-cd09-0410-ab3c-d52691b4dbfc

16 files changed:
Source/WebKit/ChangeLog
Source/WebKit/NetworkProcess/NetworkProcess.cpp
Source/WebKit/NetworkProcess/NetworkProcess.h
Source/WebKit/NetworkProcess/NetworkProcess.messages.in
Source/WebKit/NetworkProcess/NetworkResourceLoader.cpp
Source/WebKit/NetworkProcess/cocoa/NetworkProcessCocoa.mm
Source/WebKit/UIProcess/API/Cocoa/WKProcessPool.mm
Source/WebKit/UIProcess/API/Cocoa/WKProcessPoolPrivate.h
Source/WebKit/UIProcess/WebProcessPool.cpp
Source/WebKit/UIProcess/WebProcessPool.h
Tools/ChangeLog
Tools/TestWebKitAPI/TCPServer.cpp
Tools/TestWebKitAPI/TCPServer.h
Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
Tools/TestWebKitAPI/Tests/WebKitCocoa/BasicProposedCredentialPlugIn.mm [new file with mode: 0644]
Tools/TestWebKitAPI/Tests/WebKitCocoa/Challenge.mm

index 8ea303a..5aa1e65 100644 (file)
@@ -1,3 +1,29 @@
+2019-04-22  Alex Christensen  <achristensen@webkit.org>
+
+        REGRESSION(r230681) Do not use stored credentials if WKBundlePageResourceLoadClient.shouldUseCredentialStorage returns false
+        https://bugs.webkit.org/show_bug.cgi?id=197093
+        <rdar://problem/49708268>
+
+        Reviewed by Chris Dumez.
+
+        Only get the StoredCredentialsPolicy from the NetworkLoadChecker if we haven't already been told not to use credentials.
+        Also add some test infrastructure for clearing persistent credentials added by the test.
+
+        * NetworkProcess/NetworkProcess.cpp:
+        (WebKit::NetworkProcess::removeCredential):
+        * NetworkProcess/NetworkProcess.h:
+        * NetworkProcess/NetworkProcess.messages.in:
+        * NetworkProcess/NetworkResourceLoader.cpp:
+        (WebKit::NetworkResourceLoader::startNetworkLoad):
+        * NetworkProcess/cocoa/NetworkProcessCocoa.mm:
+        (WebKit::NetworkProcess::removeCredential):
+        * UIProcess/API/Cocoa/WKProcessPool.mm:
+        (-[WKProcessPool _removeCredential:forProtectionSpace:completionHandler:]):
+        * UIProcess/API/Cocoa/WKProcessPoolPrivate.h:
+        * UIProcess/WebProcessPool.cpp:
+        (WebKit::WebProcessPool::removeCredential):
+        * UIProcess/WebProcessPool.h:
+
 2019-04-22  Chris Dumez  <cdumez@apple.com>
 
         Delayed WebProcessLaunch may break the _relatedWebView SPI
index 3d752c6..fabc9ea 100644 (file)
@@ -2470,6 +2470,11 @@ StorageQuotaManager& NetworkProcess::storageQuotaManager(PAL::SessionID sessionI
 }
 
 #if !PLATFORM(COCOA)
+void NetworkProcess::removeCredential(WebCore::Credential&&, WebCore::ProtectionSpace&&, CompletionHandler<void()>&& completionHandler)
+{
+    completionHandler();
+}
+
 void NetworkProcess::initializeProcess(const AuxiliaryProcessInitializationParameters&)
 {
 }
index 89c2985..d234be3 100644 (file)
@@ -72,6 +72,7 @@ namespace WebCore {
 class CertificateInfo;
 class CurlProxySettings;
 class DownloadID;
+class ProtectionSpace;
 class StorageQuotaManager;
 class NetworkStorageSession;
 class ResourceError;
@@ -426,6 +427,8 @@ private:
 
     void platformSyncAllCookies(CompletionHandler<void()>&&);
 
+    void removeCredential(WebCore::Credential&&, WebCore::ProtectionSpace&&, CompletionHandler<void()>&&);
+
     void registerURLSchemeAsSecure(const String&) const;
     void registerURLSchemeAsBypassingContentSecurityPolicy(const String&) const;
     void registerURLSchemeAsLocal(const String&) const;
index 1bdd5bd..568dbf8 100644 (file)
@@ -171,4 +171,5 @@ messages -> NetworkProcess LegacyReceiver {
     ClearAdClickAttribution(PAL::SessionID sessionID) -> () Async
     SetAdClickAttributionOverrideTimerForTesting(PAL::SessionID sessionID, bool value) -> () Async
     SetAdClickAttributionConversionURLForTesting(PAL::SessionID sessionID, URL url) -> () Async
+    RemoveCredential(WebCore::Credential credential, WebCore::ProtectionSpace protectionSpace) -> () Async
 }
index cb1794d..f9d0f5e 100644 (file)
@@ -270,7 +270,7 @@ void NetworkResourceLoader::startNetworkLoad(ResourceRequest&& request, FirstLoa
 
     NetworkLoadParameters parameters = m_parameters;
     parameters.networkActivityTracker = m_networkActivityTracker;
-    if (m_networkLoadChecker)
+    if (parameters.storedCredentialsPolicy == WebCore::StoredCredentialsPolicy::Use && m_networkLoadChecker)
         parameters.storedCredentialsPolicy = m_networkLoadChecker->storedCredentialsPolicy();
 
     if (request.url().protocolIsBlob())
index c8ad73e..e159875 100644 (file)
@@ -205,6 +205,18 @@ void NetworkProcess::clearDiskCache(WallTime modifiedSince, CompletionHandler<vo
     }).get());
 }
 
+void NetworkProcess::removeCredential(WebCore::Credential&& credential, WebCore::ProtectionSpace&& protectionSpace, CompletionHandler<void()>&& completionHandler)
+{
+    NSURLProtectionSpace *nsSpace = protectionSpace.nsSpace();
+    NSURLCredential *nsCredential = [[[NSURLCredentialStorage sharedCredentialStorage] credentialsForProtectionSpace:nsSpace] objectForKey:credential.user()];
+    RELEASE_ASSERT(nsCredential);
+    RELEASE_ASSERT([nsCredential.user isEqualToString:credential.user()]);
+    RELEASE_ASSERT([nsCredential.password isEqualToString:credential.password()]);
+    [[NSURLCredentialStorage sharedCredentialStorage] removeCredential:nsCredential forProtectionSpace:nsSpace];
+    RELEASE_ASSERT(![[[NSURLCredentialStorage sharedCredentialStorage] credentialsForProtectionSpace:nsSpace] objectForKey:credential.user()]);
+    completionHandler();
+}
+
 #if PLATFORM(MAC)
 void NetworkProcess::setSharedHTTPCookieStorage(const Vector<uint8_t>& identifier)
 {
index 1688a0a..79c2164 100644 (file)
@@ -519,6 +519,13 @@ static NSDictionary *policiesHashMapToDictionary(const HashMap<String, HashMap<S
     _processPool->preconnectToServer(serverURL);
 }
 
+- (void)_removeCredential:(NSURLCredential *)credential forProtectionSpace:(NSURLProtectionSpace *)protectionSpace completionHandler:(void(^)())completionHandler
+{
+    _processPool->removeCredential(WebCore::Credential(credential), WebCore::ProtectionSpace(protectionSpace), [completionHandler = makeBlockPtr(completionHandler)] {
+        completionHandler();
+    });
+}
+
 - (size_t)_pluginProcessCount
 {
 #if !PLATFORM(IOS_FAMILY)
index 0e1f06d..82a6c3e 100644 (file)
 - (void)_preconnectToServer:(NSURL *)serverURL WK_API_AVAILABLE(macos(10.13.4), ios(11.3));
 
 // Test only.
+- (void)_removeCredential:(NSURLCredential *)credential forProtectionSpace:(NSURLProtectionSpace *)protectionSpace completionHandler:(void(^)(void))completionHandler WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
+
+// Test only.
 - (void)_setAllowsAnySSLCertificateForServiceWorker:(BOOL)allows WK_API_AVAILABLE(macos(10.13.4), ios(11.3));
 - (void)_registerURLSchemeServiceWorkersCanHandle:(NSString *)scheme WK_API_AVAILABLE(macos(10.13.4), ios(11.3));
 - (void)_getActivePagesOriginsInWebProcessForTesting:(pid_t)pid completionHandler:(void(^)(NSArray<NSString *> *))completionHandler WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
index 9d64c02..28f8c34 100644 (file)
@@ -1721,6 +1721,11 @@ void WebProcessPool::useTestingNetworkSession()
     m_shouldUseTestingNetworkSession = true;
 }
 
+void WebProcessPool::removeCredential(WebCore::Credential&& credential, WebCore::ProtectionSpace&& protectionSpace, CompletionHandler<void()>&& completionHandler)
+{
+    m_networkProcess->sendWithAsyncReply(Messages::NetworkProcess::RemoveCredential(credential, protectionSpace), WTFMove(completionHandler));
+}
+
 template<typename T, typename U>
 void WebProcessPool::sendSyncToNetworkingProcess(T&& message, U&& reply)
 {
index 49ef972..c578bfe 100644 (file)
@@ -507,6 +507,8 @@ public:
 
     void disableDelayedWebProcessLaunch() { m_isDelayedWebProcessLaunchDisabled = true; }
 
+    void removeCredential(WebCore::Credential&&, WebCore::ProtectionSpace&&, CompletionHandler<void()>&&);
+    
 private:
     void platformInitialize();
 
index a36626e..c766ffd 100644 (file)
@@ -1,3 +1,33 @@
+2019-04-22  Alex Christensen  <achristensen@webkit.org>
+
+        REGRESSION(r230681) Do not use stored credentials if WKBundlePageResourceLoadClient.shouldUseCredentialStorage returns false
+        https://bugs.webkit.org/show_bug.cgi?id=197093
+        <rdar://problem/49708268>
+
+        Reviewed by Chris Dumez.
+
+        Add a test that does two loads.  The first load shouldUseCredentialStorage returns true and we provide a persistent credential.
+        The second load shouldUseCredentialStorage returns false and we verify that a challenge is received with no suggested credential.
+        We also need to make the TCPServer able to handle more than one connection because we need these two loads to come from the same protection space,
+        and our current Cocoa implementation of NetworkSession uses two NSURLSessions that don't share a connection cache, one for loads with credentials
+        and one for loads without credentials, so there are two TCP connections to the same server in this test.
+
+        * TestWebKitAPI/TCPServer.cpp:
+        (TestWebKitAPI::TCPServer::TCPServer):
+        (TestWebKitAPI::TCPServer::~TCPServer):
+        (TestWebKitAPI::TCPServer::socketBindListen):
+        (TestWebKitAPI::TCPServer::waitForAndReplyToRequests): Deleted.
+        * TestWebKitAPI/TCPServer.h:
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/WebKitCocoa/BasicProposedCredentialPlugIn.mm: Added.
+        (-[BasicProposedCredentialPlugIn webProcessPlugIn:didCreateBrowserContextController:]):
+        * TestWebKitAPI/Tests/WebKitCocoa/Challenge.mm:
+        (respondWithChallengeThenOK):
+        (TestWebKitAPI::TEST):
+        (-[ProposedCredentialDelegate webView:didFinishNavigation:]):
+        (-[ProposedCredentialDelegate webView:didReceiveAuthenticationChallenge:completionHandler:]):
+        (TEST):
+
 2019-04-22  Chris Dumez  <cdumez@apple.com>
 
         Delayed WebProcessLaunch may break the _relatedWebView SPI
index f6dedee..44ac1e3 100644 (file)
 #include <netinet/in.h>
 #include <thread>
 #include <unistd.h>
-#include <vector>
+#include <wtf/Optional.h>
 
 namespace TestWebKitAPI {
 
-TCPServer::TCPServer(Function<void(Socket)>&& socketHandler)
-    : m_socketHandler(WTFMove(socketHandler))
+TCPServer::TCPServer(Function<void(Socket)>&& connectionHandler, size_t connections)
+    : m_connectionHandler(WTFMove(connectionHandler))
 {
-    socketBindListen();
-    m_thread = std::thread(&TCPServer::waitForAndReplyToRequests, this);
+    auto listeningSocket = socketBindListen(connections);
+    ASSERT(listeningSocket);
+    m_listeningThread = std::thread([this, listeningSocket = *listeningSocket, connections] {
+        for (size_t i = 0; i < connections; ++i) {
+            Socket connectionSocket = accept(listeningSocket, nullptr, nullptr);
+            m_connectionThreads.append(std::thread([this, connectionSocket] {
+                m_connectionHandler(connectionSocket);
+                shutdown(connectionSocket, SHUT_RDWR);
+                close(connectionSocket);
+            }));
+        }
+    });
 }
 
 TCPServer::~TCPServer()
 {
-    m_thread.join();
-    if (m_listeningSocket != InvalidSocket) {
-        close(m_listeningSocket);
-        m_listeningSocket = InvalidSocket;
-    }
-    if (m_connectionSocket != InvalidSocket) {
-        close(m_connectionSocket);
-        m_connectionSocket = InvalidSocket;
-    }
+    m_listeningThread.join();
+    for (auto& connectionThreads : m_connectionThreads)
+        connectionThreads.join();
 }
 
-void TCPServer::socketBindListen()
+auto TCPServer::socketBindListen(size_t connections) -> Optional<Socket>
 {
-    m_listeningSocket = socket(PF_INET, SOCK_STREAM, 0);
-    if (m_listeningSocket == InvalidSocket)
-        return;
+    Socket listeningSocket = socket(PF_INET, SOCK_STREAM, 0);
+    if (listeningSocket == -1)
+        return WTF::nullopt;
     
     // Ports 49152-65535 are unallocated ports. Try until we find one that's free.
     for (Port port = 49152; port; port++) {
@@ -66,34 +70,22 @@ void TCPServer::socketBindListen()
         name.sin_family = AF_INET;
         name.sin_port = htons(port);
         name.sin_addr.s_addr = htonl(INADDR_ANY);
-        if (bind(m_listeningSocket, reinterpret_cast<sockaddr*>(&name), sizeof(name)) < 0) {
+        if (bind(listeningSocket, reinterpret_cast<sockaddr*>(&name), sizeof(name)) < 0) {
             // This port is busy. Try the next port.
             continue;
         }
-        const unsigned maxConnections = 1;
-        if (listen(m_listeningSocket, maxConnections) == -1) {
+        if (listen(listeningSocket, connections) == -1) {
             // Listening failed.
-            close(m_listeningSocket);
-            m_listeningSocket = InvalidSocket;
-            return;
+            close(listeningSocket);
+            return WTF::nullopt;
         }
         m_port = port;
-        return; // Successfully set up listening port.
+        return listeningSocket; // Successfully set up listening port.
     }
     
     // Couldn't find an available port.
-    close(m_listeningSocket);
-    m_listeningSocket = InvalidSocket;
-}
-
-void TCPServer::waitForAndReplyToRequests()
-{
-    if (m_listeningSocket == InvalidSocket)
-        return;
-    
-    m_connectionSocket = accept(m_listeningSocket, nullptr, nullptr);
-    m_socketHandler(m_connectionSocket);
-    shutdown(m_connectionSocket, SHUT_RDWR);
+    close(listeningSocket);
+    return WTF::nullopt;
 }
 
 } // namespace TestWebKitAPI
index 73abc10..b9a23f3 100644 (file)
 
 #include <thread>
 #include <wtf/Function.h>
+#include <wtf/Vector.h>
 
 namespace TestWebKitAPI {
 
 class TCPServer {
 public:
     using Socket = int;
-    static constexpr Socket InvalidSocket = -1;
     using Port = uint16_t;
     static constexpr Port InvalidPort = 0;
     
-    TCPServer(Function<void(Socket)>&&);
+    TCPServer(Function<void(Socket)>&&, size_t connections = 1);
     ~TCPServer();
     
     Port port() const { return m_port; }
     
 private:
-    void socketBindListen();
-    void waitForAndReplyToRequests();
+    Optional<Socket> socketBindListen(size_t connections);
 
     Port m_port { InvalidPort };
-    Socket m_listeningSocket { InvalidSocket };
-    Socket m_connectionSocket { InvalidSocket };
-    std::thread m_thread;
-    Function<void(Socket)> m_socketHandler;
+    std::thread m_listeningThread;
+    Vector<std::thread> m_connectionThreads;
+    Function<void(Socket)> m_connectionHandler;
 };
 
 } // namespace TestWebKitAPI
index 2dd651d..f329665 100644 (file)
                5C23DF0B2246015800F454B6 /* Challenge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5C23DF0A2245C9D700F454B6 /* Challenge.mm */; };
                5C2936931D5BF70D00DEAB1E /* CookieAcceptPolicy.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5C2936911D5BF63E00DEAB1E /* CookieAcceptPolicy.mm */; };
                5C2936961D5C00ED00DEAB1E /* CookieMessage.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 5C2936941D5BFD1900DEAB1E /* CookieMessage.html */; };
+               5C4259462266A68A0039AA7A /* BasicProposedCredentialPlugIn.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5C42594422669E9B0039AA7A /* BasicProposedCredentialPlugIn.mm */; };
                5C4A84951F7EEFFC00ACFC54 /* Configuration.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5C4A84941F7EEFD400ACFC54 /* Configuration.mm */; };
                5C69BDD51F82A7EF000F4F4B /* JavaScriptDuringNavigation.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5C69BDD41F82A7EB000F4F4B /* JavaScriptDuringNavigation.mm */; };
                5C6E27A7224EEBEA00128736 /* URLCanonicalization.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5C6E27A6224EEBEA00128736 /* URLCanonicalization.mm */; };
                5C23DF0A2245C9D700F454B6 /* Challenge.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Challenge.mm; sourceTree = "<group>"; };
                5C2936911D5BF63E00DEAB1E /* CookieAcceptPolicy.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CookieAcceptPolicy.mm; sourceTree = "<group>"; };
                5C2936941D5BFD1900DEAB1E /* CookieMessage.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = CookieMessage.html; sourceTree = "<group>"; };
+               5C42594422669E9B0039AA7A /* BasicProposedCredentialPlugIn.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = BasicProposedCredentialPlugIn.mm; sourceTree = "<group>"; };
                5C4A84941F7EEFD400ACFC54 /* Configuration.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Configuration.mm; sourceTree = "<group>"; };
                5C5E633D1D0B67940085A025 /* UniqueRef.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UniqueRef.cpp; sourceTree = "<group>"; };
                5C69BDD41F82A7EB000F4F4B /* JavaScriptDuringNavigation.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = JavaScriptDuringNavigation.mm; sourceTree = "<group>"; };
                                754CEC801F6722DC00D0039A /* AutoFillAvailable.mm */,
                                2DD355351BD08378005DF4A7 /* AutoLayoutIntegration.mm */,
                                07CD32F52065B5420064A4BE /* AVFoundationPreference.mm */,
+                               5C42594422669E9B0039AA7A /* BasicProposedCredentialPlugIn.mm */,
                                374B7A5E1DF36EEE00ACCB6C /* BundleEditingDelegate.mm */,
                                374B7A5F1DF36EEE00ACCB6C /* BundleEditingDelegatePlugIn.mm */,
                                374B7A621DF3734C00ACCB6C /* BundleEditingDelegateProtocol.h */,
                        files = (
                                37E7DD671EA071F3009B396D /* AdditionalReadAccessAllowedURLsPlugin.mm in Sources */,
                                754CEC811F6722F200D0039A /* AutoFillAvailable.mm in Sources */,
+                               5C4259462266A68A0039AA7A /* BasicProposedCredentialPlugIn.mm in Sources */,
                                374B7A611DF371CF00ACCB6C /* BundleEditingDelegatePlugIn.mm in Sources */,
                                A13EBBB01B87436F00097110 /* BundleParametersPlugIn.mm in Sources */,
                                37A709AF1E3EA97E00CA5969 /* BundleRangeHandlePlugIn.mm in Sources */,
diff --git a/Tools/TestWebKitAPI/Tests/WebKitCocoa/BasicProposedCredentialPlugIn.mm b/Tools/TestWebKitAPI/Tests/WebKitCocoa/BasicProposedCredentialPlugIn.mm
new file mode 100644 (file)
index 0000000..2600658
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "config.h"
+
+#import <WebKit/WKBundlePage.h>
+#import <WebKit/WKBundlePageResourceLoadClient.h>
+#import <WebKit/WKWebProcessPlugIn.h>
+#import <WebKit/WKWebProcessPlugInBrowserContextControllerPrivate.h>
+
+@interface BasicProposedCredentialPlugIn : NSObject<WKWebProcessPlugIn>
+@end
+
+@implementation BasicProposedCredentialPlugIn
+
+- (void)webProcessPlugIn:(WKWebProcessPlugInController *)plugInController didCreateBrowserContextController:(WKWebProcessPlugInBrowserContextController *)browserContextController
+{
+    WKBundlePageResourceLoadClientV1 client;
+    memset(&client, 0, sizeof(client));
+    client.base.version = 1;
+    client.shouldUseCredentialStorage = [](auto...) {
+        static size_t queries { 0 };
+        ASSERT(queries < 2);
+        return !queries++;
+    };
+    WKBundlePageSetResourceLoadClient([browserContextController _bundlePageRef], &client.base);
+}
+
+@end
index 8a7913b..cd2b09a 100644 (file)
 #import "TCPServer.h"
 #import "Test.h"
 #import "TestWKWebView.h"
+#import "WKWebViewConfigurationExtras.h"
+#import <WebKit/WKProcessPoolPrivate.h>
+
+static bool navigationFinished;
+
+static void respondWithChallengeThenOK(int socket)
+{
+    char readBuffer[1000];
+    auto bytesRead = ::read(socket, readBuffer, sizeof(readBuffer));
+    EXPECT_GT(bytesRead, 0);
+    EXPECT_TRUE(static_cast<size_t>(bytesRead) < sizeof(readBuffer));
+    
+    const char* challengeHeader =
+    "HTTP/1.1 401 Unauthorized\r\n"
+    "Date: Sat, 23 Mar 2019 06:29:01 GMT\r\n"
+    "Content-Length: 0\r\n"
+    "WWW-Authenticate: Basic realm=\"testrealm\"\r\n\r\n";
+    auto bytesWritten = ::write(socket, challengeHeader, strlen(challengeHeader));
+    EXPECT_EQ(static_cast<size_t>(bytesWritten), strlen(challengeHeader));
+    
+    bytesRead = ::read(socket, readBuffer, sizeof(readBuffer));
+    EXPECT_GT(bytesRead, 0);
+    EXPECT_TRUE(static_cast<size_t>(bytesRead) < sizeof(readBuffer));
+    
+    const char* responseHeader =
+    "HTTP/1.1 200 OK\r\n"
+    "Content-Length: 13\r\n\r\n"
+    "Hello, World!";
+    bytesWritten = ::write(socket, responseHeader, strlen(responseHeader));
+    EXPECT_EQ(static_cast<size_t>(bytesWritten), strlen(responseHeader));
+}
 
 #if PLATFORM(MAC)
 
@@ -170,7 +201,6 @@ static std::pair<RetainPtr<NSURLCredential>, RetainPtr<NSString>> credentialWith
     };
 }
 
-static bool navigationFinished;
 static RetainPtr<NSString> keychainPath;
 
 @interface ChallengeDelegate : NSObject <WKNavigationDelegate>
@@ -208,31 +238,7 @@ namespace TestWebKitAPI {
 
 TEST(Challenge, SecIdentity)
 {
-    TCPServer server([] (auto socket) {
-        char readBuffer[1000];
-        auto bytesRead = ::read(socket, readBuffer, sizeof(readBuffer));
-        EXPECT_GT(bytesRead, 0);
-        EXPECT_TRUE(static_cast<size_t>(bytesRead) < sizeof(readBuffer));
-        
-        const char* challengeHeader =
-        "HTTP/1.1 401 Unauthorized\r\n"
-        "Date: Sat, 23 Mar 2019 06:29:01 GMT\r\n"
-        "Content-Length: 0\r\n"
-        "WWW-Authenticate: Basic realm=\"testrealm\"\r\n\r\n";
-        auto bytesWritten = ::write(socket, challengeHeader, strlen(challengeHeader));
-        EXPECT_EQ(static_cast<size_t>(bytesWritten), strlen(challengeHeader));
-        
-        bytesRead = ::read(socket, readBuffer, sizeof(readBuffer));
-        EXPECT_GT(bytesRead, 0);
-        EXPECT_TRUE(static_cast<size_t>(bytesRead) < sizeof(readBuffer));
-        
-        const char* responseHeader =
-        "HTTP/1.1 200 OK\r\n"
-        "Content-Length: 13\r\n\r\n"
-        "Hello, World!";
-        bytesWritten = ::write(socket, responseHeader, strlen(responseHeader));
-        EXPECT_EQ(static_cast<size_t>(bytesWritten), strlen(responseHeader));
-    });
+    TCPServer server(respondWithChallengeThenOK);
 
     auto webView = adoptNS([WKWebView new]);
     auto delegate = adoptNS([ChallengeDelegate new]);
@@ -250,3 +256,55 @@ TEST(Challenge, SecIdentity)
 } // namespace TestWebKitAPI
 
 #endif
+
+static bool receivedSecondChallenge;
+static RetainPtr<NSURLCredential> persistentCredential;
+
+@interface ProposedCredentialDelegate : NSObject <WKNavigationDelegate>
+@end
+
+@implementation ProposedCredentialDelegate
+
+- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation
+{
+    navigationFinished = true;
+}
+
+- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
+{
+    static bool firstChallenge = true;
+    if (firstChallenge) {
+        firstChallenge = false;
+        persistentCredential = adoptNS([[NSURLCredential alloc] initWithUser:@"testuser" password:@"testpassword" persistence:NSURLCredentialPersistencePermanent]);
+        return completionHandler(NSURLSessionAuthChallengeUseCredential, persistentCredential.get());
+        
+    }
+    receivedSecondChallenge = true;
+    return completionHandler(NSURLSessionAuthChallengeUseCredential, nil);
+}
+
+@end
+
+TEST(Challenge, BasicProposedCredential)
+{
+    using namespace TestWebKitAPI;
+    TCPServer server(respondWithChallengeThenOK, 2);
+    auto configuration = retainPtr([WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"BasicProposedCredentialPlugIn"]);
+    auto webView = adoptNS([[WKWebView alloc] initWithFrame:CGRectZero configuration:configuration.get()]);
+    auto delegate = adoptNS([ProposedCredentialDelegate new]);
+    [webView setNavigationDelegate:delegate.get()];
+    RetainPtr<NSURLRequest> request = [NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://127.0.0.1:%d/", server.port()]]];
+    [webView loadRequest:request.get()];
+    Util::run(&navigationFinished);
+    navigationFinished = false;
+    [webView loadRequest:request.get()];
+    Util::run(&navigationFinished);
+    EXPECT_TRUE(receivedSecondChallenge);
+    
+    NSURLProtectionSpace *protectionSpace = [[[NSURLProtectionSpace alloc] initWithHost:@"127.0.0.1" port:server.port() protocol:NSURLProtectionSpaceHTTP realm:@"testrealm" authenticationMethod:NSURLAuthenticationMethodHTTPBasic] autorelease];
+    __block bool removedCredential = false;
+    [[webView configuration].processPool _removeCredential:persistentCredential.get() forProtectionSpace:protectionSpace completionHandler:^{
+        removedCredential = true;
+    }];
+    Util::run(&removedCredential);
+}