run-webkit-tests: Make usability test on booting simulators more robust
[WebKit-https.git] / Source / WebCore / platform / network / CredentialStorage.cpp
1 /*
2  * Copyright (C) 2009 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. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "CredentialStorage.h"
28
29 #include "NetworkStorageSession.h"
30 #include <wtf/URL.h>
31
32 #if PLATFORM(IOS_FAMILY)
33 #include "WebCoreThread.h"
34 #endif
35
36 namespace WebCore {
37
38 static String originStringFromURL(const URL& url)
39 {
40     return makeString(url.protocol(), "://", url.hostAndPort(), '/');
41 }
42
43 static String protectionSpaceMapKeyFromURL(const URL& url)
44 {
45     ASSERT(url.isValid());
46
47     // Remove the last path component that is not a directory to determine the subtree for which credentials will apply.
48     // We keep a leading slash, but remove a trailing one.
49     String directoryURL = url.string().substring(0, url.pathEnd());
50     unsigned directoryURLPathStart = url.pathStart();
51     ASSERT(directoryURL[directoryURLPathStart] == '/');
52     if (directoryURL.length() > directoryURLPathStart + 1) {
53         size_t index = directoryURL.reverseFind('/');
54         ASSERT(index != notFound);
55         directoryURL = directoryURL.substring(0, (index != directoryURLPathStart) ? index : directoryURLPathStart + 1);
56     }
57
58     return directoryURL;
59 }
60
61 void CredentialStorage::set(const String& partitionName, const Credential& credential, const ProtectionSpace& protectionSpace, const URL& url)
62 {
63     ASSERT(protectionSpace.isProxy() || protectionSpace.authenticationScheme() == ProtectionSpaceAuthenticationSchemeClientCertificateRequested || url.protocolIsInHTTPFamily());
64     ASSERT(protectionSpace.isProxy() || protectionSpace.authenticationScheme() == ProtectionSpaceAuthenticationSchemeClientCertificateRequested || url.isValid());
65
66     m_protectionSpaceToCredentialMap.set(std::make_pair(partitionName, protectionSpace), credential);
67
68     if (!protectionSpace.isProxy() && protectionSpace.authenticationScheme() != ProtectionSpaceAuthenticationSchemeClientCertificateRequested) {
69         m_originsWithCredentials.add(originStringFromURL(url));
70
71         ProtectionSpaceAuthenticationScheme scheme = protectionSpace.authenticationScheme();
72         if (scheme == ProtectionSpaceAuthenticationSchemeHTTPBasic || scheme == ProtectionSpaceAuthenticationSchemeDefault) {
73             // The map can contain both a path and its subpath - while redundant, this makes lookups faster.
74             m_pathToDefaultProtectionSpaceMap.set(protectionSpaceMapKeyFromURL(url), protectionSpace);
75         }
76     }
77 }
78
79 Credential CredentialStorage::get(const String& partitionName, const ProtectionSpace& protectionSpace)
80 {
81     return m_protectionSpaceToCredentialMap.get(std::make_pair(partitionName, protectionSpace));
82 }
83
84 void CredentialStorage::remove(const String& partitionName, const ProtectionSpace& protectionSpace)
85 {
86     m_protectionSpaceToCredentialMap.remove(std::make_pair(partitionName, protectionSpace));
87 }
88
89 void CredentialStorage::removeCredentialsWithOrigin(const SecurityOriginData& origin)
90 {
91     Vector<std::pair<String, ProtectionSpace>> keysToRemove;
92     for (auto& keyValuePair : m_protectionSpaceToCredentialMap) {
93         auto& protectionSpace = keyValuePair.key.second;
94         if (protectionSpace.host() == origin.host
95             && ((origin.port && protectionSpace.port() == *origin.port)
96                 || (!origin.port && protectionSpace.port() == 80))
97             && ((protectionSpace.serverType() == ProtectionSpaceServerHTTP && origin.protocol == "http"_s)
98                 || (protectionSpace.serverType() == ProtectionSpaceServerHTTPS && origin.protocol == "https"_s)))
99             keysToRemove.append(keyValuePair.key);
100     }
101     for (auto& key : keysToRemove)
102         remove(key.first, key.second);
103 }
104
105 HashSet<SecurityOriginData> CredentialStorage::originsWithCredentials() const
106 {
107     HashSet<SecurityOriginData> origins;
108     for (auto& keyValuePair : m_protectionSpaceToCredentialMap) {
109         auto& protectionSpace = keyValuePair.key.second;
110         if (protectionSpace.isProxy())
111             continue;
112         String protocol;
113         switch (protectionSpace.serverType()) {
114         case ProtectionSpaceServerHTTP:
115             protocol = "http"_s;
116             break;
117         case ProtectionSpaceServerHTTPS:
118             protocol = "https"_s;
119             break;
120         case ProtectionSpaceServerFTP:
121             protocol = "ftp"_s;
122             break;
123         case ProtectionSpaceServerFTPS:
124             protocol = "ftps"_s;
125             break;
126         default:
127             ASSERT_NOT_REACHED();
128             continue;
129         }
130
131         SecurityOriginData origin { protocol, protectionSpace.host(), static_cast<uint16_t>(protectionSpace.port())};
132         origins.add(WTFMove(origin));
133     }
134     return origins;
135 }
136
137 HashMap<String, ProtectionSpace>::iterator CredentialStorage::findDefaultProtectionSpaceForURL(const URL& url)
138 {
139     ASSERT(url.protocolIsInHTTPFamily());
140     ASSERT(url.isValid());
141
142     // Don't spend time iterating the path for origins that don't have any credentials.
143     if (!m_originsWithCredentials.contains(originStringFromURL(url)))
144         return m_pathToDefaultProtectionSpaceMap.end();
145
146     String directoryURL = protectionSpaceMapKeyFromURL(url);
147     unsigned directoryURLPathStart = url.pathStart();
148     while (true) {
149         PathToDefaultProtectionSpaceMap::iterator iter = m_pathToDefaultProtectionSpaceMap.find(directoryURL);
150         if (iter != m_pathToDefaultProtectionSpaceMap.end())
151             return iter;
152
153         if (directoryURL.length() == directoryURLPathStart + 1)  // path is "/" already, cannot shorten it any more
154             return m_pathToDefaultProtectionSpaceMap.end();
155
156         size_t index = directoryURL.reverseFind('/', directoryURL.length() - 2);
157         ASSERT(index != notFound);
158         directoryURL = directoryURL.substring(0, (index == directoryURLPathStart) ? index + 1 : index);
159         ASSERT(directoryURL.length() > directoryURLPathStart);
160     }
161 }
162
163 bool CredentialStorage::set(const String& partitionName, const Credential& credential, const URL& url)
164 {
165     ASSERT(url.protocolIsInHTTPFamily());
166     ASSERT(url.isValid());
167     PathToDefaultProtectionSpaceMap::iterator iter = findDefaultProtectionSpaceForURL(url);
168     if (iter == m_pathToDefaultProtectionSpaceMap.end())
169         return false;
170     ASSERT(m_originsWithCredentials.contains(originStringFromURL(url)));
171     m_protectionSpaceToCredentialMap.set(std::make_pair(partitionName, iter->value), credential);
172     return true;
173 }
174
175 Credential CredentialStorage::get(const String& partitionName, const URL& url)
176 {
177     PathToDefaultProtectionSpaceMap::iterator iter = findDefaultProtectionSpaceForURL(url);
178     if (iter == m_pathToDefaultProtectionSpaceMap.end())
179         return Credential();
180     return m_protectionSpaceToCredentialMap.get(std::make_pair(partitionName, iter->value));
181 }
182
183 void CredentialStorage::clearCredentials()
184 {
185     m_protectionSpaceToCredentialMap.clear();
186     m_originsWithCredentials.clear();
187     m_pathToDefaultProtectionSpaceMap.clear();
188 }
189
190 #if !PLATFORM(COCOA)
191 HashSet<SecurityOriginData> CredentialStorage::originsWithSessionCredentials()
192 {
193     return { };
194 }
195
196 void CredentialStorage::removeSessionCredentialsWithOrigins(const Vector<SecurityOriginData>&)
197 {
198 }
199
200 void CredentialStorage::clearSessionCredentials()
201 {
202 }
203 #endif
204
205 } // namespace WebCore