API tests using permanent credentials should clear credentials left by previous tests
[WebKit-https.git] / Source / WebCore / platform / network / CredentialStorage.cpp
index f624d4f..26a3b9e 100644 (file)
 #include "config.h"
 #include "CredentialStorage.h"
 
-#include "Credential.h"
-#include "KURL.h"
-#include "ProtectionSpaceHash.h"
-#include <wtf/text/WTFString.h>
-#include <wtf/text/StringHash.h>
-#include <wtf/HashMap.h>
-#include <wtf/HashSet.h>
-#include <wtf/StdLibExtras.h>
+#include "NetworkStorageSession.h"
+#include <wtf/URL.h>
 
-namespace WebCore {
-
-typedef HashMap<ProtectionSpace, Credential> ProtectionSpaceToCredentialMap;
-static ProtectionSpaceToCredentialMap& protectionSpaceToCredentialMap()
-{
-    DEFINE_STATIC_LOCAL(ProtectionSpaceToCredentialMap, map, ());
-    return map;
-}
+#if PLATFORM(IOS_FAMILY)
+#include "WebCoreThread.h"
+#endif
 
-static HashSet<String>& originsWithCredentials()
-{
-    DEFINE_STATIC_LOCAL(HashSet<String>, set, ());
-    return set;
-}
-
-typedef HashMap<String, ProtectionSpace> PathToDefaultProtectionSpaceMap;
-static PathToDefaultProtectionSpaceMap& pathToDefaultProtectionSpaceMap()
-{
-    DEFINE_STATIC_LOCAL(PathToDefaultProtectionSpaceMap, map, ());
-    return map;
-}
+namespace WebCore {
 
-static String originStringFromURL(const KURL& url)
+static String originStringFromURL(const URL& url)
 {
-    if (url.port())
-        return url.protocol() + "://" + url.host() + ':' + String::number(url.port()) + '/';
-
-    return url.protocol() + "://" + url.host() + '/';
+    return makeString(url.protocol(), "://", url.hostAndPort(), '/');
 }
 
-static String protectionSpaceMapKeyFromURL(const KURL& url)
+static String protectionSpaceMapKeyFromURL(const URL& url)
 {
     ASSERT(url.isValid());
 
@@ -83,86 +58,148 @@ static String protectionSpaceMapKeyFromURL(const KURL& url)
     return directoryURL;
 }
 
-void CredentialStorage::set(const Credential& credential, const ProtectionSpace& protectionSpace, const KURL& url)
+void CredentialStorage::set(const String& partitionName, const Credential& credential, const ProtectionSpace& protectionSpace, const URL& url)
 {
-    ASSERT(protectionSpace.isProxy() || url.protocolIsInHTTPFamily());
-    ASSERT(protectionSpace.isProxy() || url.isValid());
+    ASSERT(protectionSpace.isProxy() || protectionSpace.authenticationScheme() == ProtectionSpaceAuthenticationSchemeClientCertificateRequested || url.protocolIsInHTTPFamily());
+    ASSERT(protectionSpace.isProxy() || protectionSpace.authenticationScheme() == ProtectionSpaceAuthenticationSchemeClientCertificateRequested || url.isValid());
+
+    m_protectionSpaceToCredentialMap.set(std::make_pair(partitionName, protectionSpace), credential);
 
-    protectionSpaceToCredentialMap().set(protectionSpace, credential);
-    if (!protectionSpace.isProxy()) {
-        originsWithCredentials().add(originStringFromURL(url));
+    if (!protectionSpace.isProxy() && protectionSpace.authenticationScheme() != ProtectionSpaceAuthenticationSchemeClientCertificateRequested) {
+        m_originsWithCredentials.add(originStringFromURL(url));
 
         ProtectionSpaceAuthenticationScheme scheme = protectionSpace.authenticationScheme();
         if (scheme == ProtectionSpaceAuthenticationSchemeHTTPBasic || scheme == ProtectionSpaceAuthenticationSchemeDefault) {
             // The map can contain both a path and its subpath - while redundant, this makes lookups faster.
-            pathToDefaultProtectionSpaceMap().set(protectionSpaceMapKeyFromURL(url), protectionSpace);
+            m_pathToDefaultProtectionSpaceMap.set(protectionSpaceMapKeyFromURL(url), protectionSpace);
         }
     }
 }
 
-Credential CredentialStorage::get(const ProtectionSpace& protectionSpace)
+Credential CredentialStorage::get(const String& partitionName, const ProtectionSpace& protectionSpace)
+{
+    return m_protectionSpaceToCredentialMap.get(std::make_pair(partitionName, protectionSpace));
+}
+
+void CredentialStorage::remove(const String& partitionName, const ProtectionSpace& protectionSpace)
+{
+    m_protectionSpaceToCredentialMap.remove(std::make_pair(partitionName, protectionSpace));
+}
+
+void CredentialStorage::removeCredentialsWithOrigin(const SecurityOriginData& origin)
 {
-    return protectionSpaceToCredentialMap().get(protectionSpace);
+    Vector<std::pair<String, ProtectionSpace>> keysToRemove;
+    for (auto& keyValuePair : m_protectionSpaceToCredentialMap) {
+        auto& protectionSpace = keyValuePair.key.second;
+        if (protectionSpace.host() == origin.host
+            && ((origin.port && protectionSpace.port() == *origin.port)
+                || (!origin.port && protectionSpace.port() == 80))
+            && ((protectionSpace.serverType() == ProtectionSpaceServerHTTP && origin.protocol == "http"_s)
+                || (protectionSpace.serverType() == ProtectionSpaceServerHTTPS && origin.protocol == "https"_s)))
+            keysToRemove.append(keyValuePair.key);
+    }
+    for (auto& key : keysToRemove)
+        remove(key.first, key.second);
 }
 
-void CredentialStorage::remove(const ProtectionSpace& protectionSpace)
+HashSet<SecurityOriginData> CredentialStorage::originsWithCredentials() const
 {
-    protectionSpaceToCredentialMap().remove(protectionSpace);
+    HashSet<SecurityOriginData> origins;
+    for (auto& keyValuePair : m_protectionSpaceToCredentialMap) {
+        auto& protectionSpace = keyValuePair.key.second;
+        if (protectionSpace.isProxy())
+            continue;
+        String protocol;
+        switch (protectionSpace.serverType()) {
+        case ProtectionSpaceServerHTTP:
+            protocol = "http"_s;
+            break;
+        case ProtectionSpaceServerHTTPS:
+            protocol = "https"_s;
+            break;
+        case ProtectionSpaceServerFTP:
+            protocol = "ftp"_s;
+            break;
+        case ProtectionSpaceServerFTPS:
+            protocol = "ftps"_s;
+            break;
+        default:
+            ASSERT_NOT_REACHED();
+            continue;
+        }
+
+        SecurityOriginData origin { protocol, protectionSpace.host(), static_cast<uint16_t>(protectionSpace.port())};
+        origins.add(WTFMove(origin));
+    }
+    return origins;
 }
 
-static PathToDefaultProtectionSpaceMap::iterator findDefaultProtectionSpaceForURL(const KURL& url)
+HashMap<String, ProtectionSpace>::iterator CredentialStorage::findDefaultProtectionSpaceForURL(const URL& url)
 {
     ASSERT(url.protocolIsInHTTPFamily());
     ASSERT(url.isValid());
 
-    PathToDefaultProtectionSpaceMap& map = pathToDefaultProtectionSpaceMap();
-
     // Don't spend time iterating the path for origins that don't have any credentials.
-    if (!originsWithCredentials().contains(originStringFromURL(url)))
-        return map.end();
+    if (!m_originsWithCredentials.contains(originStringFromURL(url)))
+        return m_pathToDefaultProtectionSpaceMap.end();
 
     String directoryURL = protectionSpaceMapKeyFromURL(url);
     unsigned directoryURLPathStart = url.pathStart();
     while (true) {
-        PathToDefaultProtectionSpaceMap::iterator iter = map.find(directoryURL);
-        if (iter != map.end())
+        PathToDefaultProtectionSpaceMap::iterator iter = m_pathToDefaultProtectionSpaceMap.find(directoryURL);
+        if (iter != m_pathToDefaultProtectionSpaceMap.end())
             return iter;
 
         if (directoryURL.length() == directoryURLPathStart + 1)  // path is "/" already, cannot shorten it any more
-            return map.end();
+            return m_pathToDefaultProtectionSpaceMap.end();
 
         size_t index = directoryURL.reverseFind('/', directoryURL.length() - 2);
         ASSERT(index != notFound);
         directoryURL = directoryURL.substring(0, (index == directoryURLPathStart) ? index + 1 : index);
         ASSERT(directoryURL.length() > directoryURLPathStart);
-        ASSERT(directoryURL.length() == directoryURLPathStart + 1 || directoryURL[directoryURL.length() - 1] != '/');
     }
 }
 
-bool CredentialStorage::set(const Credential& credential, const KURL& url)
+bool CredentialStorage::set(const String& partitionName, const Credential& credential, const URL& url)
 {
     ASSERT(url.protocolIsInHTTPFamily());
     ASSERT(url.isValid());
     PathToDefaultProtectionSpaceMap::iterator iter = findDefaultProtectionSpaceForURL(url);
-    if (iter == pathToDefaultProtectionSpaceMap().end())
+    if (iter == m_pathToDefaultProtectionSpaceMap.end())
         return false;
-    ASSERT(originsWithCredentials().contains(originStringFromURL(url)));
-    protectionSpaceToCredentialMap().set(iter->second, credential);
+    ASSERT(m_originsWithCredentials.contains(originStringFromURL(url)));
+    m_protectionSpaceToCredentialMap.set(std::make_pair(partitionName, iter->value), credential);
     return true;
 }
 
-Credential CredentialStorage::get(const KURL& url)
+Credential CredentialStorage::get(const String& partitionName, const URL& url)
 {
     PathToDefaultProtectionSpaceMap::iterator iter = findDefaultProtectionSpaceForURL(url);
-    if (iter == pathToDefaultProtectionSpaceMap().end())
+    if (iter == m_pathToDefaultProtectionSpaceMap.end())
         return Credential();
-    return protectionSpaceToCredentialMap().get(iter->second);
+    return m_protectionSpaceToCredentialMap.get(std::make_pair(partitionName, iter->value));
+}
+
+void CredentialStorage::clearCredentials()
+{
+    m_protectionSpaceToCredentialMap.clear();
+    m_originsWithCredentials.clear();
+    m_pathToDefaultProtectionSpaceMap.clear();
+}
+
+#if !PLATFORM(COCOA)
+HashSet<SecurityOriginData> CredentialStorage::originsWithSessionCredentials()
+{
+    return { };
+}
+
+void CredentialStorage::removeSessionCredentialsWithOrigins(const Vector<SecurityOriginData>&)
+{
 }
 
-void CredentialStorage::setPrivateMode(bool mode)
+void CredentialStorage::clearSessionCredentials()
 {
-    if (!mode)
-        protectionSpaceToCredentialMap().clear();
 }
+#endif
 
 } // namespace WebCore