API tests using permanent credentials should clear credentials left by previous tests
[WebKit-https.git] / Tools / TestWebKitAPI / Tests / WebKitCocoa / WKWebsiteDatastore.mm
1 /*
2  * Copyright (C) 2019 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27
28 #import "PlatformUtilities.h"
29 #import "TCPServer.h"
30 #import "Test.h"
31 #import "TestWKWebView.h"
32 #import <WebKit/WKProcessPoolPrivate.h>
33 #import <WebKit/WKWebsiteDataRecordPrivate.h>
34 #import <WebKit/WKWebsiteDataStorePrivate.h>
35 #import <WebKit/WebKit.h>
36 #import <WebKit/_WKWebsiteDataStoreConfiguration.h>
37 #import <wtf/text/WTFString.h>
38
39 static bool readyToContinue;
40
41 static RetainPtr<NSURLCredential> persistentCredential;
42 static bool usePersistentCredentialStorage = false;
43
44 @interface NavigationTestDelegate : NSObject <WKNavigationDelegate>
45 @end
46
47 @implementation NavigationTestDelegate {
48     bool _hasFinishedNavigation;
49 }
50
51 - (instancetype)init
52 {
53     if (!(self = [super init]))
54         return nil;
55     
56     _hasFinishedNavigation = false;
57     
58     return self;
59 }
60
61 - (void)waitForDidFinishNavigation
62 {
63     TestWebKitAPI::Util::run(&_hasFinishedNavigation);
64 }
65
66 - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
67 {
68     _hasFinishedNavigation = true;
69 }
70
71 - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
72 {
73     static bool firstChallenge = true;
74     if (firstChallenge) {
75         firstChallenge = false;
76         persistentCredential = adoptNS([[NSURLCredential alloc] initWithUser:@"username" password:@"password" persistence:(usePersistentCredentialStorage ? NSURLCredentialPersistencePermanent: NSURLCredentialPersistenceForSession)]);
77         return completionHandler(NSURLSessionAuthChallengeUseCredential, persistentCredential.get());
78     }
79     return completionHandler(NSURLSessionAuthChallengeUseCredential, nil);
80 }
81 @end
82
83 namespace TestWebKitAPI {
84
85 TEST(WKWebsiteDataStore, RemoveAndFetchData)
86 {
87     readyToContinue = false;
88     [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:[WKWebsiteDataStore _allWebsiteDataTypesIncludingPrivate] modifiedSince:[NSDate distantPast] completionHandler:^() {
89         readyToContinue = true;
90     }];
91     TestWebKitAPI::Util::run(&readyToContinue);
92     
93     readyToContinue = false;
94     [[WKWebsiteDataStore defaultDataStore] fetchDataRecordsOfTypes:[WKWebsiteDataStore _allWebsiteDataTypesIncludingPrivate] completionHandler:^(NSArray<WKWebsiteDataRecord *> *dataRecords) {
95         EXPECT_EQ(0u, dataRecords.count);
96         readyToContinue = true;
97     }];
98     TestWebKitAPI::Util::run(&readyToContinue);
99 }
100
101 TEST(WKWebsiteDataStore, RemoveEphemeralData)
102 {
103     auto configuration = adoptNS([WKWebViewConfiguration new]);
104     [configuration setWebsiteDataStore:[WKWebsiteDataStore nonPersistentDataStore]];
105     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
106     [webView synchronouslyLoadTestPageNamed:@"simple"];
107     __block bool done = false;
108     [[configuration websiteDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler: ^{
109         done = true;
110     }];
111     TestWebKitAPI::Util::run(&done);
112 }
113
114 TEST(WKWebsiteDataStore, FetchNonPersistentCredentials)
115 {
116     TCPServer server(TCPServer::respondWithChallengeThenOK);
117     
118     usePersistentCredentialStorage = false;
119     auto configuration = adoptNS([WKWebViewConfiguration new]);
120     auto websiteDataStore = [WKWebsiteDataStore nonPersistentDataStore];
121     [configuration setWebsiteDataStore:websiteDataStore];
122     auto navigationDelegate = adoptNS([[NavigationTestDelegate alloc] init]);
123     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
124     [webView setNavigationDelegate:navigationDelegate.get()];
125     [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://127.0.0.1:%d/", server.port()]]]];
126     [navigationDelegate waitForDidFinishNavigation];
127
128     __block bool done = false;
129     [websiteDataStore fetchDataRecordsOfTypes:[NSSet setWithObject:_WKWebsiteDataTypeCredentials] completionHandler:^(NSArray<WKWebsiteDataRecord *> *dataRecords) {
130         int credentialCount = dataRecords.count;
131         ASSERT_EQ(credentialCount, 1);
132         for (WKWebsiteDataRecord *record in dataRecords)
133             ASSERT_TRUE([[record displayName] isEqualToString:@"127.0.0.1"]);
134         done = true;
135     }];
136     TestWebKitAPI::Util::run(&done);
137 }
138
139 TEST(WKWebsiteDataStore, FetchPersistentCredentials)
140 {
141     TCPServer server(TCPServer::respondWithChallengeThenOK);
142
143     usePersistentCredentialStorage = true;
144     auto websiteDataStore = [WKWebsiteDataStore defaultDataStore];
145     auto navigationDelegate = adoptNS([[NavigationTestDelegate alloc] init]);
146     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]);
147     [webView setNavigationDelegate:navigationDelegate.get()];
148
149     // Make sure no credential left by previous tests.
150     NSURLProtectionSpace *protectionSpace = [[[NSURLProtectionSpace alloc] initWithHost:@"127.0.0.1" port:server.port() protocol:NSURLProtectionSpaceHTTP realm:@"testrealm" authenticationMethod:NSURLAuthenticationMethodHTTPBasic] autorelease];
151     [[webView configuration].processPool _clearPermanentCredentialsForProtectionSpace:protectionSpace];
152
153     [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://127.0.0.1:%d/", server.port()]]]];
154     [navigationDelegate waitForDidFinishNavigation];
155
156     __block bool done = false;
157     [websiteDataStore fetchDataRecordsOfTypes:[NSSet setWithObject:_WKWebsiteDataTypeCredentials] completionHandler:^(NSArray<WKWebsiteDataRecord *> *dataRecords) {
158         int credentialCount = dataRecords.count;
159         EXPECT_EQ(credentialCount, 0);
160         done = true;
161     }];
162     TestWebKitAPI::Util::run(&done);
163
164     // Clear persistent credentials created by this test.
165     [[webView configuration].processPool _clearPermanentCredentialsForProtectionSpace:protectionSpace];
166 }
167
168 TEST(WKWebsiteDataStore, RemoveNonPersistentCredentials)
169 {
170     TCPServer server(TCPServer::respondWithChallengeThenOK);
171
172     usePersistentCredentialStorage = false;
173     auto configuration = adoptNS([WKWebViewConfiguration new]);
174     auto websiteDataStore = [WKWebsiteDataStore nonPersistentDataStore];
175     [configuration setWebsiteDataStore:websiteDataStore];
176     auto navigationDelegate = adoptNS([[NavigationTestDelegate alloc] init]);
177     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
178     [webView setNavigationDelegate:navigationDelegate.get()];
179     [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://127.0.0.1:%d/", server.port()]]]];
180     [navigationDelegate waitForDidFinishNavigation];
181
182     __block bool done = false;
183     __block RetainPtr<WKWebsiteDataRecord> expectedRecord;
184     [websiteDataStore fetchDataRecordsOfTypes:[NSSet setWithObject:_WKWebsiteDataTypeCredentials] completionHandler:^(NSArray<WKWebsiteDataRecord *> *dataRecords) {
185         int credentialCount = dataRecords.count;
186         ASSERT_GT(credentialCount, 0);
187         for (WKWebsiteDataRecord *record in dataRecords) {
188             auto name = [record displayName];
189             if ([name isEqualToString:@"127.0.0.1"]) {
190                 expectedRecord = record;
191                 break;
192             }
193         }
194         EXPECT_TRUE(expectedRecord);
195         done = true;
196     }];
197     TestWebKitAPI::Util::run(&done);
198
199     done = false;
200     [websiteDataStore removeDataOfTypes:[NSSet setWithObject:_WKWebsiteDataTypeCredentials] forDataRecords:[NSArray arrayWithObject:expectedRecord.get()] completionHandler:^(void) {
201         done = true;
202     }];
203     TestWebKitAPI::Util::run(&done);
204
205     done = false;
206     [websiteDataStore fetchDataRecordsOfTypes:[NSSet setWithObject:_WKWebsiteDataTypeCredentials] completionHandler:^(NSArray<WKWebsiteDataRecord *> *dataRecords) {
207         bool foundLocalHostRecord = false;
208         for (WKWebsiteDataRecord *record in dataRecords) {
209             auto name = [record displayName];
210             if ([name isEqualToString:@"127.0.0.1"]) {
211                 foundLocalHostRecord = true;
212                 break;
213             }
214         }
215         EXPECT_FALSE(foundLocalHostRecord);
216         done = true;
217     }];
218     TestWebKitAPI::Util::run(&done);
219 }
220
221 TEST(WebKit, SettingNonPersistentDataStorePathsThrowsException)
222 {
223     auto configuration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] initNonPersistentConfiguration]);
224
225     auto shouldThrowExceptionWhenUsed = [](Function<void(void)>&& modifier) {
226         @try {
227             modifier();
228             EXPECT_TRUE(false);
229         } @catch (NSException *exception) {
230             EXPECT_WK_STREQ(NSInvalidArgumentException, exception.name);
231         }
232     };
233
234     NSURL *fileURL = [NSURL fileURLWithPath:@"/tmp"];
235
236     shouldThrowExceptionWhenUsed([&] {
237         [configuration _setWebStorageDirectory:fileURL];
238     });
239     shouldThrowExceptionWhenUsed([&] {
240         [configuration _setIndexedDBDatabaseDirectory:fileURL];
241     });
242     shouldThrowExceptionWhenUsed([&] {
243         [configuration _setWebSQLDatabaseDirectory:fileURL];
244     });
245     shouldThrowExceptionWhenUsed([&] {
246         [configuration _setCookieStorageFile:fileURL];
247     });
248     shouldThrowExceptionWhenUsed([&] {
249         [configuration _setResourceLoadStatisticsDirectory:fileURL];
250     });
251     shouldThrowExceptionWhenUsed([&] {
252         [configuration _setCacheStorageDirectory:fileURL];
253     });
254     shouldThrowExceptionWhenUsed([&] {
255         [configuration _setServiceWorkerRegistrationDirectory:fileURL];
256     });
257
258     // These properties shouldn't throw exceptions when set on a non-persistent data store.
259     [configuration setDeviceManagementRestrictionsEnabled:YES];
260     [configuration setHTTPProxy:[NSURL URLWithString:@"http://www.apple.com/"]];
261     [configuration setHTTPSProxy:[NSURL URLWithString:@"https://www.apple.com/"]];
262     [configuration setSourceApplicationBundleIdentifier:@"com.apple.Safari"];
263     [configuration setSourceApplicationSecondaryIdentifier:@"com.apple.Safari"];
264 }
265
266 TEST(WKWebsiteDataStore, FetchNonPersistentWebStorage)
267 {
268     auto nonPersistentDataStore = [WKWebsiteDataStore nonPersistentDataStore];
269     auto configuration = adoptNS([WKWebViewConfiguration new]);
270     [configuration setWebsiteDataStore:nonPersistentDataStore];
271     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
272     auto navigationDelegate = adoptNS([[NavigationTestDelegate alloc] init]);
273     [webView setNavigationDelegate:navigationDelegate.get()];
274     [webView loadHTMLString:@"<script>sessionStorage.setItem('session', 'storage');localStorage.setItem('local', 'storage');</script>" baseURL:[NSURL URLWithString:@"http://localhost"]];
275     [navigationDelegate waitForDidFinishNavigation];
276
277     readyToContinue = false;
278     [webView evaluateJavaScript:@"window.sessionStorage.getItem('session')" completionHandler:^(id result, NSError *) {
279         EXPECT_TRUE([@"storage" isEqualToString:result]);
280         readyToContinue = true;
281     }];
282     TestWebKitAPI::Util::run(&readyToContinue);
283
284     readyToContinue = false;
285     [webView evaluateJavaScript:@"window.localStorage.getItem('local')" completionHandler:^(id result, NSError *) {
286         EXPECT_TRUE([@"storage" isEqualToString:result]);
287         readyToContinue = true;
288     }];
289     TestWebKitAPI::Util::run(&readyToContinue);
290
291     readyToContinue = false;
292     [nonPersistentDataStore fetchDataRecordsOfTypes:[NSSet setWithObject:WKWebsiteDataTypeSessionStorage] completionHandler:^(NSArray<WKWebsiteDataRecord *> *dataRecords) {
293         EXPECT_EQ((int)dataRecords.count, 1);
294         EXPECT_TRUE([[[dataRecords objectAtIndex:0] displayName] isEqualToString:@"localhost"]);
295         readyToContinue = true;
296     }];
297     TestWebKitAPI::Util::run(&readyToContinue);
298
299     readyToContinue = false;
300     [nonPersistentDataStore fetchDataRecordsOfTypes:[NSSet setWithObject:WKWebsiteDataTypeLocalStorage] completionHandler:^(NSArray<WKWebsiteDataRecord *> *dataRecords) {
301         EXPECT_EQ((int)dataRecords.count, 1);
302         EXPECT_TRUE([[[dataRecords objectAtIndex:0] displayName] isEqualToString:@"localhost"]);
303         readyToContinue = true;
304     }];
305     TestWebKitAPI::Util::run(&readyToContinue);
306 }
307
308 }