-[WKWebsiteDataStore fetchDataRecordsOfTypes:completionHandler:] never returns _WKWeb...
authorpvollan@apple.com <pvollan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 25 Apr 2019 19:04:46 +0000 (19:04 +0000)
committerpvollan@apple.com <pvollan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 25 Apr 2019 19:04:46 +0000 (19:04 +0000)
https://bugs.webkit.org/show_bug.cgi?id=196991
<rdar://problem/45507423>

Reviewed by Alex Christensen.

Source/WebCore:

Add method to get all origins with persistent credentials from credential storage.

API tests: WKWebsiteDataStore.FetchNonPersistentCredentials
           WKWebsiteDataStore.FetchPersistentCredentials

* platform/network/CredentialStorage.h:
* platform/network/mac/CredentialStorageMac.mm:
(WebCore::CredentialStorage::originsWithPersistentCredentials):

Tools:

* TestWebKitAPI/Tests/WebKitCocoa/WKWebsiteDatastore.mm:
(-[NavigationTestDelegate init]):
(-[NavigationTestDelegate waitForDidFinishNavigation]):
(-[NavigationTestDelegate webView:didFinishNavigation:]):
(-[NavigationTestDelegate webView:didReceiveAuthenticationChallenge:completionHandler:]):
(TestWebKitAPI::TEST):
(TEST): Deleted.

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

Source/WebCore/ChangeLog
Source/WebCore/platform/network/CredentialStorage.h
Source/WebCore/platform/network/mac/CredentialStorageMac.mm
Source/WebKit/NetworkProcess/NetworkProcess.cpp
Source/WebKit/NetworkProcess/NetworkProcess.h
Source/WebKit/NetworkProcess/NetworkProcess.messages.in
Source/WebKit/NetworkProcess/cocoa/NetworkProcessCocoa.mm
Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp
Tools/ChangeLog
Tools/TestWebKitAPI/Tests/WebKitCocoa/WKWebsiteDatastore.mm

index 2945759..ae76575 100644 (file)
@@ -1,3 +1,20 @@
+2019-04-25  Per Arne Vollan  <pvollan@apple.com>
+
+        -[WKWebsiteDataStore fetchDataRecordsOfTypes:completionHandler:] never returns _WKWebsiteDataTypeCredentials
+        https://bugs.webkit.org/show_bug.cgi?id=196991
+        <rdar://problem/45507423>
+
+        Reviewed by Alex Christensen.
+
+        Add method to get all origins with persistent credentials from credential storage.
+
+        API tests: WKWebsiteDataStore.FetchNonPersistentCredentials
+                   WKWebsiteDataStore.FetchPersistentCredentials
+
+        * platform/network/CredentialStorage.h:
+        * platform/network/mac/CredentialStorageMac.mm:
+        (WebCore::CredentialStorage::originsWithPersistentCredentials):
+
 2019-04-25  Alex Christensen  <achristensen@webkit.org>
 
         Fix MSVC build after r244653
index 52f7a44..e409032 100644 (file)
@@ -27,6 +27,7 @@
 
 #include "Credential.h"
 #include "ProtectionSpaceHash.h"
+#include "SecurityOriginData.h"
 #include <wtf/HashMap.h>
 #include <wtf/HashSet.h>
 #include <wtf/text/StringHash.h>
@@ -45,6 +46,7 @@ public:
 
     // OS persistent storage.
     WEBCORE_EXPORT static Credential getFromPersistentStorage(const ProtectionSpace&);
+    WEBCORE_EXPORT static Vector<SecurityOriginData> originsWithPersistentCredentials();
 
     WEBCORE_EXPORT void clearCredentials();
 
index 4ed223c..65b5151 100644 (file)
@@ -38,4 +38,13 @@ Credential CredentialStorage::getFromPersistentStorage(const ProtectionSpace& pr
     return credential ? Credential(credential) : Credential();
 }
 
+Vector<WebCore::SecurityOriginData> CredentialStorage::originsWithPersistentCredentials()
+{
+    Vector<WebCore::SecurityOriginData> origins;
+    auto allCredentials = [[NSURLCredentialStorage sharedCredentialStorage] allCredentials];
+    for (NSURLProtectionSpace* key in allCredentials.keyEnumerator)
+        origins.append(WebCore::SecurityOriginData { String(key.protocol), String(key.host), key.port });
+    return origins;
+}
+
 } // namespace WebCore
index 930b183..7d70c7f 100644 (file)
@@ -2487,6 +2487,11 @@ void NetworkProcess::removeCredential(WebCore::Credential&&, WebCore::Protection
     completionHandler();
 }
 
+void NetworkProcess::originsWithPersistentCredentials(CompletionHandler<void(Vector<WebCore::SecurityOriginData>)>&& completionHandler)
+{
+    completionHandler(Vector<WebCore::SecurityOriginData>());
+}
+    
 void NetworkProcess::initializeProcess(const AuxiliaryProcessInitializationParameters&)
 {
 }
index 1324212..c0449a1 100644 (file)
@@ -430,6 +430,8 @@ private:
 
     void removeCredential(WebCore::Credential&&, WebCore::ProtectionSpace&&, CompletionHandler<void()>&&);
 
+    void originsWithPersistentCredentials(CompletionHandler<void(Vector<WebCore::SecurityOriginData>)>&&);
+    
     void registerURLSchemeAsSecure(const String&) const;
     void registerURLSchemeAsBypassingContentSecurityPolicy(const String&) const;
     void registerURLSchemeAsLocal(const String&) const;
index 962ae2e..d139ffe 100644 (file)
@@ -173,4 +173,5 @@ messages -> NetworkProcess LegacyReceiver {
     SetAdClickAttributionConversionURLForTesting(PAL::SessionID sessionID, URL url) -> () Async
     MarkAdClickAttributionsAsExpiredForTesting(PAL::SessionID sessionID) -> () Async
     RemoveCredential(WebCore::Credential credential, WebCore::ProtectionSpace protectionSpace) -> () Async
+    OriginsWithPersistentCredentials() -> (Vector<WebCore::SecurityOriginData> persistentCredentials) Async
 }
index e159875..2e4c50a 100644 (file)
@@ -217,6 +217,11 @@ void NetworkProcess::removeCredential(WebCore::Credential&& credential, WebCore:
     completionHandler();
 }
 
+void NetworkProcess::originsWithPersistentCredentials(CompletionHandler<void(Vector<WebCore::SecurityOriginData>)>&& completionHandler)
+{
+    completionHandler(WebCore::CredentialStorage::originsWithPersistentCredentials());
+}
+
 #if PLATFORM(MAC)
 void NetworkProcess::setSharedHTTPCookieStorage(const Vector<uint8_t>& identifier)
 {
index de1aa08..0d30b77 100644 (file)
@@ -44,6 +44,7 @@
 #include "WebsiteDataStoreClient.h"
 #include "WebsiteDataStoreParameters.h"
 #include <WebCore/ApplicationCacheStorage.h>
+#include <WebCore/CredentialStorage.h>
 #include <WebCore/DatabaseTracker.h>
 #include <WebCore/HTMLMediaElement.h>
 #include <WebCore/OriginLock.h>
@@ -238,8 +239,8 @@ static ProcessAccessType computeNetworkProcessAccessTypeForDataFetch(OptionSet<W
             processAccessType = std::max(processAccessType, ProcessAccessType::Launch);
     }
 
-    if (dataTypes.contains(WebsiteDataType::Credentials) && !isNonPersistentStore)
-        processAccessType = std::max(processAccessType, ProcessAccessType::Launch);
+    if (dataTypes.contains(WebsiteDataType::Credentials) && isNonPersistentStore)
+        processAccessType = std::max(processAccessType, ProcessAccessType::OnlyIfLaunched);
 
     if (dataTypes.contains(WebsiteDataType::DiskCache) && !isNonPersistentStore)
         processAccessType = std::max(processAccessType, ProcessAccessType::Launch);
@@ -356,8 +357,13 @@ void WebsiteDataStore::fetchDataAndApply(OptionSet<WebsiteDataType> dataTypes, O
 #endif
 
             for (auto& origin : websiteData.originsWithCredentials) {
-                auto& record = m_websiteDataRecords.add(origin, WebsiteDataRecord { }).iterator->value;
-                
+                auto displayName = WebsiteDataRecord::displayNameForOrigin(WebCore::SecurityOriginData::fromURL(URL(URL(), origin)));
+                ASSERT(!displayName.isEmpty());
+
+                auto& record = m_websiteDataRecords.add(displayName, WebsiteDataRecord { }).iterator->value;
+                if (!record.displayName)
+                    record.displayName = WTFMove(displayName);
+
                 record.addOriginWithCredential(origin);
             }
 
@@ -570,6 +576,24 @@ void WebsiteDataStore::fetchDataAndApply(OptionSet<WebsiteDataType> dataTypes, O
         });
     }
 
+#if PLATFORM(COCOA)
+    if (dataTypes.contains(WebsiteDataType::Credentials) && isPersistent()) {
+        for (auto& processPool : processPools()) {
+            if (!processPool->networkProcess())
+                continue;
+            
+            callbackAggregator->addPendingCallback();
+            WTF::CompletionHandler<void(Vector<WebCore::SecurityOriginData>&&)> completionHandler = [callbackAggregator](Vector<WebCore::SecurityOriginData>&& origins) mutable {
+                WebsiteData websiteData;
+                for (auto& origin : origins)
+                    websiteData.entries.append(WebsiteData::Entry { origin, WebsiteDataType::Credentials, 0 });
+                callbackAggregator->removePendingCallback(WTFMove(websiteData));
+            };
+            processPool->networkProcess()->sendWithAsyncReply(Messages::NetworkProcess::OriginsWithPersistentCredentials(), WTFMove(completionHandler));
+        }
+    }
+#endif
+
 #if ENABLE(NETSCAPE_PLUGIN_API)
     if (dataTypes.contains(WebsiteDataType::PlugInData) && isPersistent()) {
         class State {
index c1b53e7..55dbd7b 100644 (file)
@@ -1,3 +1,19 @@
+2019-04-25  Per Arne Vollan  <pvollan@apple.com>
+
+        -[WKWebsiteDataStore fetchDataRecordsOfTypes:completionHandler:] never returns _WKWebsiteDataTypeCredentials
+        https://bugs.webkit.org/show_bug.cgi?id=196991
+        <rdar://problem/45507423>
+
+        Reviewed by Alex Christensen.
+
+        * TestWebKitAPI/Tests/WebKitCocoa/WKWebsiteDatastore.mm:
+        (-[NavigationTestDelegate init]):
+        (-[NavigationTestDelegate waitForDidFinishNavigation]):
+        (-[NavigationTestDelegate webView:didFinishNavigation:]):
+        (-[NavigationTestDelegate webView:didReceiveAuthenticationChallenge:completionHandler:]):
+        (TestWebKitAPI::TEST):
+        (TEST): Deleted.
+
 2019-04-25  Alex Christensen  <achristensen@webkit.org>
 
         Revert some code accidentally committed with r244653
index 183233a..bb9fcde 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 Apple Inc. All rights reserved.
+ * 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
 #import "config.h"
 
 #import "PlatformUtilities.h"
+#import "TCPServer.h"
 #import "Test.h"
 #import "TestWKWebView.h"
+#import <WebKit/WKProcessPoolPrivate.h>
+#import <WebKit/WKWebsiteDataRecordPrivate.h>
 #import <WebKit/WKWebsiteDataStorePrivate.h>
 #import <WebKit/WebKit.h>
 #import <wtf/text/WTFString.h>
 
 static bool readyToContinue;
 
+static RetainPtr<NSURLCredential> persistentCredential;
+static bool usePersistentCredentialStorage = false;
+
+@interface NavigationTestDelegate : NSObject <WKNavigationDelegate>
+@end
+
+@implementation NavigationTestDelegate {
+    bool _hasFinishedNavigation;
+}
+
+- (instancetype)init
+{
+    if (!(self = [super init]))
+        return nil;
+    
+    _hasFinishedNavigation = false;
+    
+    return self;
+}
+
+- (void)waitForDidFinishNavigation
+{
+    TestWebKitAPI::Util::run(&_hasFinishedNavigation);
+}
+
+- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
+{
+    _hasFinishedNavigation = 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:@"username" password:@"password" persistence:(usePersistentCredentialStorage ? NSURLCredentialPersistencePermanent: NSURLCredentialPersistenceForSession)]);
+        return completionHandler(NSURLSessionAuthChallengeUseCredential, persistentCredential.get());
+    }
+    return completionHandler(NSURLSessionAuthChallengeUseCredential, nil);
+}
+@end
+
+namespace TestWebKitAPI {
+
 TEST(WKWebsiteDataStore, RemoveAndFetchData)
 {
     readyToContinue = false;
@@ -62,3 +109,92 @@ TEST(WKWebsiteDataStore, RemoveEphemeralData)
     }];
     TestWebKitAPI::Util::run(&done);
 }
+
+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));
+}
+    
+TEST(WKWebsiteDataStore, FetchNonPersistentCredentials)
+{
+    TCPServer server(respondWithChallengeThenOK);
+    
+    auto configuration = adoptNS([WKWebViewConfiguration new]);
+    auto websiteDataStore = [WKWebsiteDataStore nonPersistentDataStore];
+    [configuration setWebsiteDataStore:websiteDataStore];
+    auto navigationDelegate = adoptNS([[NavigationTestDelegate alloc] init]);
+    auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
+    [webView setNavigationDelegate:navigationDelegate.get()];
+    [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://127.0.0.1:%d/", server.port()]]]];
+    [navigationDelegate waitForDidFinishNavigation];
+
+    __block bool done = false;
+    [websiteDataStore fetchDataRecordsOfTypes:[NSSet setWithObject:_WKWebsiteDataTypeCredentials] completionHandler:^(NSArray<WKWebsiteDataRecord *> *dataRecords) {
+        int credentialCount = dataRecords.count;
+        ASSERT_EQ(credentialCount, 1);
+        for (WKWebsiteDataRecord *record in dataRecords)
+            ASSERT_TRUE([[record displayName] isEqualToString:@"127.0.0.1"]);
+        done = true;
+    }];
+    TestWebKitAPI::Util::run(&done);
+}
+
+TEST(WKWebsiteDataStore, FetchPersistentCredentials)
+{
+    TCPServer server(respondWithChallengeThenOK);
+    
+    usePersistentCredentialStorage = true;
+    auto websiteDataStore = [WKWebsiteDataStore defaultDataStore];
+    auto navigationDelegate = adoptNS([[NavigationTestDelegate alloc] init]);
+    auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]);
+    [webView setNavigationDelegate:navigationDelegate.get()];
+    [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://127.0.0.1:%d/", server.port()]]]];
+    [navigationDelegate waitForDidFinishNavigation];
+
+    __block bool done = false;
+    [websiteDataStore fetchDataRecordsOfTypes:[NSSet setWithObject:_WKWebsiteDataTypeCredentials] completionHandler:^(NSArray<WKWebsiteDataRecord *> *dataRecords) {
+        int credentialCount = dataRecords.count;
+        ASSERT_GT(credentialCount, 0);
+        bool foundExpectedRecord = false;
+        for (WKWebsiteDataRecord *record in dataRecords) {
+            auto name = [record displayName];
+            if ([name isEqualToString:@"127.0.0.1"]) {
+                foundExpectedRecord = true;
+                break;
+            }
+        }
+        EXPECT_TRUE(foundExpectedRecord);
+        done = true;
+    }];
+    TestWebKitAPI::Util::run(&done);
+    
+    __block bool removedCredential = false;
+    [[[webView configuration] processPool] _removeCredential:persistentCredential.get() forProtectionSpace:[[[NSURLProtectionSpace alloc] initWithHost:@"127.0.0.1" port:server.port() protocol:NSURLProtectionSpaceHTTP realm:@"testrealm" authenticationMethod:NSURLAuthenticationMethodHTTPBasic] autorelease] completionHandler:^{
+        removedCredential = true;
+    }];
+    TestWebKitAPI::Util::run(&removedCredential);
+}
+
+}