// Copyright 2011 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/test/simple_test_clock.h" #include "base/test/simple_test_tick_clock.h" #include "base/time/time.h" #include "net/base/net_errors.h" #include "net/base/network_anonymization_key.h" #include "net/base/registry_controlled_domains/registry_controlled_domain.h" #include "net/base/schemeful_site.h" #include "net/http/http_auth_cache.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" #include "url/scheme_host_port.h" using base::ASCIIToUTF16; namespace net { namespace { const char kRealm1[] = "Realm1"; const char kRealm2[] = "Realm2"; const char kRealm3[] = "Realm3"; const char kRealm4[] = "Realm4"; const char kRealm5[] = "Realm5"; const std::u16string k123(u"123"); const std::u16string k1234(u"1234"); const std::u16string k12345(u"12345"); const std::u16string kAdmin(u"admin"); const std::u16string kAlice(u"alice"); const std::u16string kAlice2(u"alice2"); const std::u16string kAlice3(u"alice3"); const std::u16string kPassword(u"password"); const std::u16string kRoot(u"root"); const std::u16string kUsername(u"username"); const std::u16string kWileCoyote(u"wilecoyote"); AuthCredentials CreateASCIICredentials(const char* username, const char* password) { return AuthCredentials(ASCIIToUTF16(username), ASCIIToUTF16(password)); } bool DoesUrlMatchFilter(const std::set& domains, const GURL& url) { std::string url_registerable_domain = net::registry_controlled_domains::GetDomainAndRegistry( url, net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES); bool found_domain = (domains.find(url_registerable_domain != "" ? url_registerable_domain : url.host()) != domains.end()); return found_domain; } } // namespace // Test adding and looking-up cache entries (both by realm and by path). TEST(HttpAuthCacheTest, Basic) { url::SchemeHostPort scheme_host_port(GURL("http://www.google.com")); url::SchemeHostPort scheme_host_port2(GURL("http://www.foobar.com")); HttpAuthCache cache(false /* key_entries_by_network_anonymization_key */); HttpAuthCache::Entry* entry; // Add cache entries for 4 realms: "Realm1", "Realm2", "Realm3" and // "Realm4" cache.Add(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), "Basic realm=Realm1", CreateASCIICredentials("realm1-user", "realm1-password"), "/foo/bar/index.html"); cache.Add(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm2, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), "Basic realm=Realm2", CreateASCIICredentials("realm2-user", "realm2-password"), "/foo2/index.html"); cache.Add( scheme_host_port, HttpAuth::AUTH_SERVER, kRealm3, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), "Basic realm=Realm3", CreateASCIICredentials("realm3-basic-user", "realm3-basic-password"), std::string()); cache.Add( scheme_host_port, HttpAuth::AUTH_SERVER, kRealm3, HttpAuth::AUTH_SCHEME_DIGEST, NetworkAnonymizationKey(), "Digest realm=Realm3", CreateASCIICredentials("realm3-digest-user", "realm3-digest-password"), "/baz/index.html"); cache.Add( scheme_host_port, HttpAuth::AUTH_SERVER, kRealm4, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), "Basic realm=Realm4", CreateASCIICredentials("realm4-basic-user", "realm4-basic-password"), "/"); cache.Add(scheme_host_port2, HttpAuth::AUTH_SERVER, kRealm5, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), "Basic realm=Realm5", CreateASCIICredentials("realm5-user", "realm5-password"), "/"); cache.Add( scheme_host_port2, HttpAuth::AUTH_SERVER, kRealm3, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), "Basic realm=Realm3", CreateASCIICredentials("realm3-basic-user", "realm3-basic-password"), std::string()); // There is no Realm5 in `scheme_host_port`. entry = cache.Lookup(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm5, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey()); EXPECT_FALSE(entry); // While Realm3 does exist, the scheme is wrong. entry = cache.Lookup(url::SchemeHostPort(GURL("https://www.google.com")), HttpAuth::AUTH_SERVER, kRealm3, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey()); EXPECT_FALSE(entry); // Realm, scheme ok, authentication scheme wrong entry = cache.Lookup(url::SchemeHostPort(GURL("https://www.google.com")), HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_DIGEST, NetworkAnonymizationKey()); EXPECT_FALSE(entry); // Valid lookup by SchemeHostPort, realm, scheme. entry = cache.Lookup(url::SchemeHostPort(GURL("http://www.google.com:80")), HttpAuth::AUTH_SERVER, kRealm3, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey()); ASSERT_TRUE(entry); EXPECT_EQ(HttpAuth::AUTH_SCHEME_BASIC, entry->scheme()); EXPECT_EQ(kRealm3, entry->realm()); EXPECT_EQ("Basic realm=Realm3", entry->auth_challenge()); EXPECT_EQ(u"realm3-basic-user", entry->credentials().username()); EXPECT_EQ(u"realm3-basic-password", entry->credentials().password()); // Same realm, scheme with different SchemeHostPorts. HttpAuthCache::Entry* entry2 = cache.Lookup(url::SchemeHostPort(GURL("http://www.foobar.com:80")), HttpAuth::AUTH_SERVER, kRealm3, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey()); ASSERT_TRUE(entry2); EXPECT_NE(entry, entry2); // Valid lookup by SchemeHostPort, realm, scheme when there's a duplicate // SchemeHostPort, realm in the cache. entry = cache.Lookup(url::SchemeHostPort(GURL("http://www.google.com:80")), HttpAuth::AUTH_SERVER, kRealm3, HttpAuth::AUTH_SCHEME_DIGEST, NetworkAnonymizationKey()); ASSERT_TRUE(entry); EXPECT_EQ(HttpAuth::AUTH_SCHEME_DIGEST, entry->scheme()); EXPECT_EQ(kRealm3, entry->realm()); EXPECT_EQ("Digest realm=Realm3", entry->auth_challenge()); EXPECT_EQ(u"realm3-digest-user", entry->credentials().username()); EXPECT_EQ(u"realm3-digest-password", entry->credentials().password()); // Valid lookup by realm. entry = cache.Lookup(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm2, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey()); ASSERT_TRUE(entry); EXPECT_EQ(HttpAuth::AUTH_SCHEME_BASIC, entry->scheme()); EXPECT_EQ(kRealm2, entry->realm()); EXPECT_EQ("Basic realm=Realm2", entry->auth_challenge()); EXPECT_EQ(u"realm2-user", entry->credentials().username()); EXPECT_EQ(u"realm2-password", entry->credentials().password()); // Check that subpaths are recognized. HttpAuthCache::Entry* p_realm2_entry = cache.Lookup(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm2, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey()); HttpAuthCache::Entry* p_realm4_entry = cache.Lookup(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm4, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey()); EXPECT_TRUE(p_realm2_entry); EXPECT_TRUE(p_realm4_entry); HttpAuthCache::Entry realm2_entry = *p_realm2_entry; HttpAuthCache::Entry realm4_entry = *p_realm4_entry; // Realm4 applies to '/' and Realm2 applies to '/foo2/'. // LookupByPath() should return the closest enclosing path. // Positive tests: entry = cache.LookupByPath(scheme_host_port, HttpAuth::AUTH_SERVER, NetworkAnonymizationKey(), "/foo2/index.html"); EXPECT_TRUE(realm2_entry.IsEqualForTesting(*entry)); entry = cache.LookupByPath(scheme_host_port, HttpAuth::AUTH_SERVER, NetworkAnonymizationKey(), "/foo2/foobar.html"); EXPECT_TRUE(realm2_entry.IsEqualForTesting(*entry)); entry = cache.LookupByPath(scheme_host_port, HttpAuth::AUTH_SERVER, NetworkAnonymizationKey(), "/foo2/bar/index.html"); EXPECT_TRUE(realm2_entry.IsEqualForTesting(*entry)); entry = cache.LookupByPath(scheme_host_port, HttpAuth::AUTH_SERVER, NetworkAnonymizationKey(), "/foo2/"); EXPECT_TRUE(realm2_entry.IsEqualForTesting(*entry)); entry = cache.LookupByPath(scheme_host_port, HttpAuth::AUTH_SERVER, NetworkAnonymizationKey(), "/foo2"); EXPECT_TRUE(realm4_entry.IsEqualForTesting(*entry)); entry = cache.LookupByPath(scheme_host_port, HttpAuth::AUTH_SERVER, NetworkAnonymizationKey(), "/"); EXPECT_TRUE(realm4_entry.IsEqualForTesting(*entry)); // Negative tests: entry = cache.LookupByPath(scheme_host_port, HttpAuth::AUTH_SERVER, NetworkAnonymizationKey(), "/foo3/index.html"); EXPECT_FALSE(realm2_entry.IsEqualForTesting(*entry)); entry = cache.LookupByPath(scheme_host_port, HttpAuth::AUTH_SERVER, NetworkAnonymizationKey(), std::string()); EXPECT_FALSE(realm2_entry.IsEqualForTesting(*entry)); // Confirm we find the same realm, different auth scheme by path lookup HttpAuthCache::Entry* p_realm3_digest_entry = cache.Lookup(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm3, HttpAuth::AUTH_SCHEME_DIGEST, NetworkAnonymizationKey()); EXPECT_TRUE(p_realm3_digest_entry); HttpAuthCache::Entry realm3_digest_entry = *p_realm3_digest_entry; entry = cache.LookupByPath(scheme_host_port, HttpAuth::AUTH_SERVER, NetworkAnonymizationKey(), "/baz/index.html"); EXPECT_TRUE(realm3_digest_entry.IsEqualForTesting(*entry)); entry = cache.LookupByPath(scheme_host_port, HttpAuth::AUTH_SERVER, NetworkAnonymizationKey(), "/baz/"); EXPECT_TRUE(realm3_digest_entry.IsEqualForTesting(*entry)); entry = cache.LookupByPath(scheme_host_port, HttpAuth::AUTH_SERVER, NetworkAnonymizationKey(), "/baz"); EXPECT_FALSE(realm3_digest_entry.IsEqualForTesting(*entry)); // Confirm we find the same realm, different auth scheme by path lookup HttpAuthCache::Entry* p_realm3DigestEntry = cache.Lookup(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm3, HttpAuth::AUTH_SCHEME_DIGEST, NetworkAnonymizationKey()); EXPECT_TRUE(p_realm3DigestEntry); HttpAuthCache::Entry realm3DigestEntry = *p_realm3DigestEntry; entry = cache.LookupByPath(scheme_host_port, HttpAuth::AUTH_SERVER, NetworkAnonymizationKey(), "/baz/index.html"); EXPECT_TRUE(realm3DigestEntry.IsEqualForTesting(*entry)); entry = cache.LookupByPath(scheme_host_port, HttpAuth::AUTH_SERVER, NetworkAnonymizationKey(), "/baz/"); EXPECT_TRUE(realm3DigestEntry.IsEqualForTesting(*entry)); entry = cache.LookupByPath(scheme_host_port, HttpAuth::AUTH_SERVER, NetworkAnonymizationKey(), "/baz"); EXPECT_FALSE(realm3DigestEntry.IsEqualForTesting(*entry)); // Lookup using empty path (may be used for proxy). entry = cache.LookupByPath(scheme_host_port, HttpAuth::AUTH_SERVER, NetworkAnonymizationKey(), std::string()); EXPECT_TRUE(entry); EXPECT_EQ(HttpAuth::AUTH_SCHEME_BASIC, entry->scheme()); EXPECT_EQ(kRealm3, entry->realm()); } // Make sure server and proxy credentials are treated separately. TEST(HttpAuthCacheTest, SeparateByTarget) { const std::u16string kServerUser = u"server_user"; const std::u16string kServerPass = u"server_pass"; const std::u16string kProxyUser = u"proxy_user"; const std::u16string kProxyPass = u"proxy_pass"; const char kServerPath[] = "/foo/bar/index.html"; url::SchemeHostPort scheme_host_port(GURL("http://www.google.com")); HttpAuthCache cache(false /* key_entries_by_network_anonymization_key */); HttpAuthCache::Entry* entry; // Add AUTH_SERVER entry. cache.Add(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), "Basic realm=Realm1", AuthCredentials(kServerUser, kServerPass), kServerPath); // Make sure credentials are only accessible with AUTH_SERVER target. entry = cache.Lookup(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey()); ASSERT_TRUE(entry); EXPECT_EQ(entry->credentials().username(), kServerUser); EXPECT_EQ(entry->credentials().password(), kServerPass); EXPECT_EQ(entry, cache.LookupByPath(scheme_host_port, HttpAuth::AUTH_SERVER, NetworkAnonymizationKey(), kServerPath)); EXPECT_FALSE(cache.Lookup(scheme_host_port, HttpAuth::AUTH_PROXY, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey())); EXPECT_FALSE(cache.LookupByPath(scheme_host_port, HttpAuth::AUTH_PROXY, NetworkAnonymizationKey(), kServerPath)); // Add AUTH_PROXY entry with same SchemeHostPort and realm but different // credentials. cache.Add(scheme_host_port, HttpAuth::AUTH_PROXY, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), "Basic realm=Realm1", AuthCredentials(kProxyUser, kProxyPass), "/"); // Make sure credentials are only accessible with the corresponding target. entry = cache.Lookup(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey()); ASSERT_TRUE(entry); EXPECT_EQ(entry->credentials().username(), kServerUser); EXPECT_EQ(entry->credentials().password(), kServerPass); EXPECT_EQ(entry, cache.LookupByPath(scheme_host_port, HttpAuth::AUTH_SERVER, NetworkAnonymizationKey(), kServerPath)); entry = cache.Lookup(scheme_host_port, HttpAuth::AUTH_PROXY, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey()); ASSERT_TRUE(entry); EXPECT_EQ(entry->credentials().username(), kProxyUser); EXPECT_EQ(entry->credentials().password(), kProxyPass); EXPECT_EQ(entry, cache.LookupByPath(scheme_host_port, HttpAuth::AUTH_PROXY, NetworkAnonymizationKey(), "/")); // Remove the AUTH_SERVER entry. EXPECT_TRUE(cache.Remove(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), AuthCredentials(kServerUser, kServerPass))); // Verify that only the AUTH_SERVER entry was removed. EXPECT_FALSE(cache.Lookup(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey())); EXPECT_FALSE(cache.LookupByPath(scheme_host_port, HttpAuth::AUTH_SERVER, NetworkAnonymizationKey(), kServerPath)); entry = cache.Lookup(scheme_host_port, HttpAuth::AUTH_PROXY, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey()); ASSERT_TRUE(entry); EXPECT_EQ(entry->credentials().username(), kProxyUser); EXPECT_EQ(entry->credentials().password(), kProxyPass); EXPECT_EQ(entry, cache.LookupByPath(scheme_host_port, HttpAuth::AUTH_PROXY, NetworkAnonymizationKey(), "/")); // Remove the AUTH_PROXY entry. EXPECT_TRUE(cache.Remove(scheme_host_port, HttpAuth::AUTH_PROXY, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), AuthCredentials(kProxyUser, kProxyPass))); // Verify that neither entry remains. EXPECT_FALSE(cache.Lookup(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey())); EXPECT_FALSE(cache.LookupByPath(scheme_host_port, HttpAuth::AUTH_SERVER, NetworkAnonymizationKey(), kServerPath)); EXPECT_FALSE(cache.Lookup(scheme_host_port, HttpAuth::AUTH_PROXY, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey())); EXPECT_FALSE(cache.LookupByPath(scheme_host_port, HttpAuth::AUTH_PROXY, NetworkAnonymizationKey(), "/")); } // Make sure server credentials with different NetworkAnonymizationKeys are // treated separately if |key_entries_by_network_anonymization_key| is set to // true. TEST(HttpAuthCacheTest, SeparateServersByNetworkAnonymizationKey) { const SchemefulSite kSite1(GURL("https://foo.test/")); auto kNetworkAnonymizationKey1 = net::NetworkAnonymizationKey::CreateSameSite(kSite1); const SchemefulSite kSite2(GURL("https://bar.test/")); auto kNetworkAnonymizationKey2 = net::NetworkAnonymizationKey::CreateSameSite(kSite2); url::SchemeHostPort kSchemeHostPort(GURL("http://www.google.com")); const char kPath[] = "/"; const std::u16string kUser1 = u"user1"; const std::u16string kPass1 = u"pass1"; const std::u16string kUser2 = u"user2"; const std::u16string kPass2 = u"pass2"; for (bool key_entries_by_network_anonymization_key : {false, true}) { HttpAuthCache cache(key_entries_by_network_anonymization_key); HttpAuthCache::Entry* entry; // Add entry for kNetworkAnonymizationKey1. cache.Add(kSchemeHostPort, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, kNetworkAnonymizationKey1, "Basic realm=Realm1", AuthCredentials(kUser1, kPass1), kPath); entry = cache.Lookup(kSchemeHostPort, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, kNetworkAnonymizationKey1); ASSERT_TRUE(entry); EXPECT_EQ(entry->credentials().username(), kUser1); EXPECT_EQ(entry->credentials().password(), kPass1); EXPECT_EQ(entry, cache.LookupByPath(kSchemeHostPort, HttpAuth::AUTH_SERVER, kNetworkAnonymizationKey1, kPath)); if (key_entries_by_network_anonymization_key) { EXPECT_FALSE(cache.Lookup(kSchemeHostPort, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, kNetworkAnonymizationKey2)); EXPECT_FALSE(cache.LookupByPath(kSchemeHostPort, HttpAuth::AUTH_SERVER, kNetworkAnonymizationKey2, kPath)); } else { EXPECT_EQ(entry, cache.Lookup(kSchemeHostPort, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, kNetworkAnonymizationKey2)); EXPECT_EQ(entry, cache.LookupByPath(kSchemeHostPort, HttpAuth::AUTH_SERVER, kNetworkAnonymizationKey2, kPath)); } // Add entry for kNetworkAnonymizationKey2. cache.Add(kSchemeHostPort, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, kNetworkAnonymizationKey2, "Basic realm=Realm1", AuthCredentials(kUser2, kPass2), kPath); entry = cache.Lookup(kSchemeHostPort, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, kNetworkAnonymizationKey2); ASSERT_TRUE(entry); EXPECT_EQ(entry->credentials().username(), kUser2); EXPECT_EQ(entry->credentials().password(), kPass2); EXPECT_EQ(entry, cache.LookupByPath(kSchemeHostPort, HttpAuth::AUTH_SERVER, kNetworkAnonymizationKey2, kPath)); entry = cache.Lookup(kSchemeHostPort, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, kNetworkAnonymizationKey1); ASSERT_TRUE(entry); EXPECT_EQ(entry, cache.LookupByPath(kSchemeHostPort, HttpAuth::AUTH_SERVER, kNetworkAnonymizationKey1, kPath)); if (key_entries_by_network_anonymization_key) { EXPECT_EQ(entry->credentials().username(), kUser1); EXPECT_EQ(entry->credentials().password(), kPass1); } else { EXPECT_EQ(entry->credentials().username(), kUser2); EXPECT_EQ(entry->credentials().password(), kPass2); } // Remove the entry that was just added. EXPECT_TRUE(cache.Remove(kSchemeHostPort, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, kNetworkAnonymizationKey2, AuthCredentials(kUser2, kPass2))); EXPECT_FALSE(cache.Lookup(kSchemeHostPort, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, kNetworkAnonymizationKey2)); EXPECT_FALSE(cache.LookupByPath(kSchemeHostPort, HttpAuth::AUTH_SERVER, kNetworkAnonymizationKey2, kPath)); if (key_entries_by_network_anonymization_key) { entry = cache.Lookup(kSchemeHostPort, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, kNetworkAnonymizationKey1); ASSERT_TRUE(entry); EXPECT_EQ(entry->credentials().username(), kUser1); EXPECT_EQ(entry->credentials().password(), kPass1); EXPECT_EQ(entry, cache.LookupByPath(kSchemeHostPort, HttpAuth::AUTH_SERVER, kNetworkAnonymizationKey1, kPath)); } else { EXPECT_FALSE(cache.Lookup(kSchemeHostPort, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, kNetworkAnonymizationKey1)); EXPECT_FALSE(cache.LookupByPath(kSchemeHostPort, HttpAuth::AUTH_SERVER, kNetworkAnonymizationKey1, kPath)); } } } // Make sure added proxy credentials ignore NetworkAnonymizationKey, even if if // |key_entries_by_network_anonymization_key| is set to true. TEST(HttpAuthCacheTest, NeverSeparateProxiesByNetworkAnonymizationKey) { const SchemefulSite kSite1(GURL("https://foo.test/")); auto kNetworkAnonymizationKey1 = net::NetworkAnonymizationKey::CreateSameSite(kSite1); const SchemefulSite kSite2(GURL("https://bar.test/")); auto kNetworkAnonymizationKey2 = net::NetworkAnonymizationKey::CreateSameSite(kSite2); url::SchemeHostPort kSchemeHostPort(GURL("http://www.google.com")); const char kPath[] = "/"; const std::u16string kUser1 = u"user1"; const std::u16string kPass1 = u"pass1"; const std::u16string kUser2 = u"user2"; const std::u16string kPass2 = u"pass2"; for (bool key_entries_by_network_anonymization_key : {false, true}) { HttpAuthCache cache(key_entries_by_network_anonymization_key); HttpAuthCache::Entry* entry; // Add entry for kNetworkAnonymizationKey1. cache.Add(kSchemeHostPort, HttpAuth::AUTH_PROXY, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, kNetworkAnonymizationKey1, "Basic realm=Realm1", AuthCredentials(kUser1, kPass1), kPath); entry = cache.Lookup(kSchemeHostPort, HttpAuth::AUTH_PROXY, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, kNetworkAnonymizationKey1); ASSERT_TRUE(entry); EXPECT_EQ(entry->credentials().username(), kUser1); EXPECT_EQ(entry->credentials().password(), kPass1); EXPECT_EQ(entry, cache.LookupByPath(kSchemeHostPort, HttpAuth::AUTH_PROXY, kNetworkAnonymizationKey1, kPath)); EXPECT_EQ(entry, cache.Lookup(kSchemeHostPort, HttpAuth::AUTH_PROXY, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, kNetworkAnonymizationKey2)); EXPECT_EQ(entry, cache.LookupByPath(kSchemeHostPort, HttpAuth::AUTH_PROXY, kNetworkAnonymizationKey2, kPath)); // Add entry for kNetworkAnonymizationKey2. It should overwrite the entry // for kNetworkAnonymizationKey1. cache.Add(kSchemeHostPort, HttpAuth::AUTH_PROXY, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, kNetworkAnonymizationKey2, "Basic realm=Realm1", AuthCredentials(kUser2, kPass2), kPath); entry = cache.Lookup(kSchemeHostPort, HttpAuth::AUTH_PROXY, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, kNetworkAnonymizationKey2); ASSERT_TRUE(entry); EXPECT_EQ(entry->credentials().username(), kUser2); EXPECT_EQ(entry->credentials().password(), kPass2); EXPECT_EQ(entry, cache.LookupByPath(kSchemeHostPort, HttpAuth::AUTH_PROXY, kNetworkAnonymizationKey2, kPath)); EXPECT_EQ(entry, cache.Lookup(kSchemeHostPort, HttpAuth::AUTH_PROXY, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, kNetworkAnonymizationKey1)); EXPECT_EQ(entry, cache.LookupByPath(kSchemeHostPort, HttpAuth::AUTH_PROXY, kNetworkAnonymizationKey1, kPath)); // Remove the entry that was just added using an empty // NetworkAnonymizationKey. EXPECT_TRUE(cache.Remove(kSchemeHostPort, HttpAuth::AUTH_PROXY, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), AuthCredentials(kUser2, kPass2))); EXPECT_FALSE(cache.Lookup(kSchemeHostPort, HttpAuth::AUTH_PROXY, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, kNetworkAnonymizationKey2)); EXPECT_FALSE(cache.LookupByPath(kSchemeHostPort, HttpAuth::AUTH_PROXY, kNetworkAnonymizationKey2, kPath)); EXPECT_FALSE(cache.Lookup(kSchemeHostPort, HttpAuth::AUTH_PROXY, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, kNetworkAnonymizationKey1)); EXPECT_FALSE(cache.LookupByPath(kSchemeHostPort, HttpAuth::AUTH_PROXY, kNetworkAnonymizationKey1, kPath)); } } // Test that SetKeyServerEntriesByNetworkAnonymizationKey() deletes server // credentials when it toggles the setting. This test uses an empty // NetworkAnonymizationKey() for all entries, as the interesting part of this // method is what type entries are deleted, which doesn't depend on the // NetworkAnonymizationKey the entries use. TEST(HttpAuthCacheTest, SetKeyServerEntriesByNetworkAnonymizationKey) { const url::SchemeHostPort kSchemeHostPort(GURL("http://www.google.com")); const char kPath[] = "/"; const std::u16string kUser1 = u"user1"; const std::u16string kPass1 = u"pass1"; const std::u16string kUser2 = u"user2"; const std::u16string kPass2 = u"pass2"; for (bool initially_key_entries_by_network_anonymization_key : {false, true}) { for (bool to_key_entries_by_network_anonymization_key : {false, true}) { HttpAuthCache cache(initially_key_entries_by_network_anonymization_key); EXPECT_EQ(initially_key_entries_by_network_anonymization_key, cache.key_server_entries_by_network_anonymization_key()); cache.Add(kSchemeHostPort, HttpAuth::AUTH_PROXY, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), "Basic realm=Realm1", AuthCredentials(kUser1, kPass1), kPath); cache.Add(kSchemeHostPort, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), "Basic realm=Realm1", AuthCredentials(kUser2, kPass2), kPath); EXPECT_TRUE(cache.Lookup(kSchemeHostPort, HttpAuth::AUTH_PROXY, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey())); EXPECT_TRUE(cache.Lookup(kSchemeHostPort, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey())); cache.SetKeyServerEntriesByNetworkAnonymizationKey( to_key_entries_by_network_anonymization_key); EXPECT_EQ(to_key_entries_by_network_anonymization_key, cache.key_server_entries_by_network_anonymization_key()); // AUTH_PROXY credentials should always remain in the cache. HttpAuthCache::Entry* entry = cache.LookupByPath(kSchemeHostPort, HttpAuth::AUTH_PROXY, NetworkAnonymizationKey(), kPath); ASSERT_TRUE(entry); EXPECT_EQ(entry->credentials().username(), kUser1); EXPECT_EQ(entry->credentials().password(), kPass1); entry = cache.LookupByPath(kSchemeHostPort, HttpAuth::AUTH_SERVER, NetworkAnonymizationKey(), kPath); // AUTH_SERVER credentials should only remain in the cache if the proxy // configuration changes. EXPECT_EQ(initially_key_entries_by_network_anonymization_key == to_key_entries_by_network_anonymization_key, !!entry); if (entry) { EXPECT_EQ(entry->credentials().username(), kUser2); EXPECT_EQ(entry->credentials().password(), kPass2); } } } } TEST(HttpAuthCacheTest, AddPath) { HttpAuthCache::Entry entry; // All of these paths have a common root /1/2/2/4/5/ entry.AddPath("/1/2/3/4/5/x.txt"); entry.AddPath("/1/2/3/4/5/y.txt"); entry.AddPath("/1/2/3/4/5/z.txt"); EXPECT_EQ(1U, entry.paths_.size()); EXPECT_EQ("/1/2/3/4/5/", entry.paths_.front()); // Add a new entry (not a subpath). entry.AddPath("/1/XXX/q"); EXPECT_EQ(2U, entry.paths_.size()); EXPECT_EQ("/1/XXX/", entry.paths_.front()); EXPECT_EQ("/1/2/3/4/5/", entry.paths_.back()); // Add containing paths of /1/2/3/4/5/ -- should swallow up the deeper paths. entry.AddPath("/1/2/3/4/x.txt"); EXPECT_EQ(2U, entry.paths_.size()); EXPECT_EQ("/1/2/3/4/", entry.paths_.front()); EXPECT_EQ("/1/XXX/", entry.paths_.back()); entry.AddPath("/1/2/3/x"); EXPECT_EQ(2U, entry.paths_.size()); EXPECT_EQ("/1/2/3/", entry.paths_.front()); EXPECT_EQ("/1/XXX/", entry.paths_.back()); entry.AddPath("/index.html"); EXPECT_EQ(1U, entry.paths_.size()); EXPECT_EQ("/", entry.paths_.front()); } // Calling Add when the realm entry already exists, should append that // path. TEST(HttpAuthCacheTest, AddToExistingEntry) { HttpAuthCache cache(false /* key_entries_by_network_anonymization_key */); url::SchemeHostPort scheme_host_port(GURL("http://www.foobar.com:70")); const std::string kAuthChallenge = "Basic realm=MyRealm"; const std::string kRealm = "MyRealm"; HttpAuthCache::Entry* orig_entry = cache.Add( scheme_host_port, HttpAuth::AUTH_SERVER, kRealm, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), kAuthChallenge, CreateASCIICredentials("user1", "password1"), "/x/y/z/"); cache.Add(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), kAuthChallenge, CreateASCIICredentials("user2", "password2"), "/z/y/x/"); cache.Add(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), kAuthChallenge, CreateASCIICredentials("user3", "password3"), "/z/y"); HttpAuthCache::Entry* entry = cache.Lookup(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey()); EXPECT_TRUE(entry == orig_entry); EXPECT_EQ(u"user3", entry->credentials().username()); EXPECT_EQ(u"password3", entry->credentials().password()); EXPECT_EQ(2U, entry->paths_.size()); EXPECT_EQ("/z/", entry->paths_.front()); EXPECT_EQ("/x/y/z/", entry->paths_.back()); } TEST(HttpAuthCacheTest, Remove) { url::SchemeHostPort scheme_host_port(GURL("http://foobar2.com")); HttpAuthCache cache(false /* key_entries_by_network_anonymization_key */); cache.Add(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), "basic realm=Realm1", AuthCredentials(kAlice, k123), "/"); cache.Add(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm2, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), "basic realm=Realm2", CreateASCIICredentials("bob", "princess"), "/"); cache.Add(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm3, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), "basic realm=Realm3", AuthCredentials(kAdmin, kPassword), "/"); cache.Add(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm3, HttpAuth::AUTH_SCHEME_DIGEST, NetworkAnonymizationKey(), "digest realm=Realm3", AuthCredentials(kRoot, kWileCoyote), "/"); // Fails, because there is no realm "Realm5". EXPECT_FALSE(cache.Remove(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm5, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), AuthCredentials(kAlice, k123))); // Fails because the origin is wrong. EXPECT_FALSE( cache.Remove(url::SchemeHostPort(GURL("http://foobar2.com:100")), HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), AuthCredentials(kAlice, k123))); // Fails because the username is wrong. EXPECT_FALSE(cache.Remove(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), AuthCredentials(kAlice2, k123))); // Fails because the password is wrong. EXPECT_FALSE(cache.Remove(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), AuthCredentials(kAlice, k1234))); // Fails because the authentication type is wrong. EXPECT_FALSE(cache.Remove(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_DIGEST, NetworkAnonymizationKey(), AuthCredentials(kAlice, k123))); // Succeeds. EXPECT_TRUE(cache.Remove(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), AuthCredentials(kAlice, k123))); // Fails because we just deleted the entry! EXPECT_FALSE(cache.Remove(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), AuthCredentials(kAlice, k123))); // Succeed when there are two authentication types for the same origin,realm. EXPECT_TRUE(cache.Remove(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm3, HttpAuth::AUTH_SCHEME_DIGEST, NetworkAnonymizationKey(), AuthCredentials(kRoot, kWileCoyote))); // Succeed as above, but when entries were added in opposite order cache.Add(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm3, HttpAuth::AUTH_SCHEME_DIGEST, NetworkAnonymizationKey(), "digest realm=Realm3", AuthCredentials(kRoot, kWileCoyote), "/"); EXPECT_TRUE(cache.Remove(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm3, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), AuthCredentials(kAdmin, kPassword))); // Make sure that removing one entry still leaves the other available for // lookup. HttpAuthCache::Entry* entry = cache.Lookup(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm3, HttpAuth::AUTH_SCHEME_DIGEST, NetworkAnonymizationKey()); EXPECT_FALSE(nullptr == entry); } TEST(HttpAuthCacheTest, ClearEntriesAddedBetween) { url::SchemeHostPort scheme_host_port(GURL("http://foobar.com")); base::Time start_time; ASSERT_TRUE(base::Time::FromString("30 May 2018 12:00:00", &start_time)); base::SimpleTestClock test_clock; test_clock.SetNow(start_time); HttpAuthCache cache(false /* key_entries_by_network_anonymization_key */); cache.set_clock_for_testing(&test_clock); cache.Add(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), "basic realm=Realm1", AuthCredentials(kAlice, k123), "/"); cache.Add(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm2, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), "basic realm=Realm2", AuthCredentials(kRoot, kWileCoyote), "/"); test_clock.Advance(base::Seconds(10)); // Time now 12:00:10 cache.Add(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm3, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), "basic realm=Realm3", AuthCredentials(kAlice2, k1234), "/"); cache.Add(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm4, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), "basic realm=Realm4", AuthCredentials(kUsername, kPassword), "/"); // Add path to existing entry. cache.Add(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm2, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), "basic realm=Realm2", AuthCredentials(kAdmin, kPassword), "/baz/"); test_clock.Advance(base::Seconds(10)); // Time now 12:00:20 cache.Add(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm5, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), "basic realm=Realm5", AuthCredentials(kAlice3, k12345), "/"); base::Time test_time1; ASSERT_TRUE(base::Time::FromString("30 May 2018 12:00:05", &test_time1)); base::Time test_time2; ASSERT_TRUE(base::Time::FromString("30 May 2018 12:00:15", &test_time2)); cache.ClearEntriesAddedBetween(test_time1, test_time2, base::RepeatingCallback()); // Realms 1 and 2 are older than 12:00:05 and should not be cleared EXPECT_NE(nullptr, cache.Lookup(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey())); EXPECT_NE(nullptr, cache.Lookup(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm2, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey())); // Realms 5 is newer than 12:00:15 and should not be cleared EXPECT_NE(nullptr, cache.Lookup(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm5, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey())); // Creation time is set for a whole entry rather than for a particular path. // Path added within the requested duration isn't be removed. EXPECT_NE(nullptr, cache.LookupByPath(scheme_host_port, HttpAuth::AUTH_SERVER, NetworkAnonymizationKey(), "/baz/")); // Realms 3 and 4 are between 12:00:05 and 12:00:10 and should be cleared. EXPECT_EQ(nullptr, cache.Lookup(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm3, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey())); EXPECT_EQ(nullptr, cache.Lookup(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm4, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey())); cache.ClearEntriesAddedBetween(start_time - base::Seconds(1), base::Time::Max(), base::RepeatingCallback()); EXPECT_EQ(nullptr, cache.Lookup(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey())); EXPECT_EQ(nullptr, cache.Lookup(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm2, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey())); EXPECT_EQ(nullptr, cache.LookupByPath(scheme_host_port, HttpAuth::AUTH_SERVER, NetworkAnonymizationKey(), "/baz/")); } TEST(HttpAuthCacheTest, ClearEntriesAddedBetweenByFilter) { url::SchemeHostPort scheme_host_port_1(GURL("http://foobar.com")); url::SchemeHostPort scheme_host_port_2(GURL("http://foobar2.com")); base::SimpleTestClock test_clock; test_clock.SetNow(base::Time::Now()); HttpAuthCache cache(false /* key_entries_by_network_anonymization_key */); cache.set_clock_for_testing(&test_clock); cache.Add(scheme_host_port_1, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), "basic realm=Realm1", AuthCredentials(kAlice, k123), "/"); cache.Add(scheme_host_port_2, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), "basic realm=Realm1", AuthCredentials(kRoot, kWileCoyote), "/"); cache.ClearEntriesAddedBetween( base::Time::Min(), base::Time::Max(), base::BindRepeating(&DoesUrlMatchFilter, std::set({scheme_host_port_1.host()}))); // Only foobar.com should be cleared while foobar2.com remains. EXPECT_EQ(nullptr, cache.Lookup(scheme_host_port_1, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey())); EXPECT_NE(nullptr, cache.Lookup(scheme_host_port_2, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey())); } TEST(HttpAuthCacheTest, ClearEntriesAddedBetweenWithAllTimeValues) { url::SchemeHostPort scheme_host_port(GURL("http://foobar.com")); base::SimpleTestClock test_clock; test_clock.SetNow(base::Time::Now()); HttpAuthCache cache(false /* key_entries_by_network_anonymization_key */); cache.set_clock_for_testing(&test_clock); cache.Add(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), "basic realm=Realm1", AuthCredentials(kAlice, k123), "/"); cache.Add(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm2, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), "basic realm=Realm2", AuthCredentials(kRoot, kWileCoyote), "/"); test_clock.Advance(base::Seconds(10)); cache.Add(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm3, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), "basic realm=Realm3", AuthCredentials(kAlice2, k1234), "/"); cache.Add(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm4, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), "basic realm=Realm4", AuthCredentials(kUsername, kPassword), "/"); // Add path to existing entry. cache.Add(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm2, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), "basic realm=Realm2", AuthCredentials(kAdmin, kPassword), "/baz/"); cache.ClearEntriesAddedBetween(base::Time::Min(), base::Time::Max(), base::RepeatingCallback()); // All entries should be cleared. EXPECT_EQ(nullptr, cache.Lookup(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey())); EXPECT_EQ(nullptr, cache.Lookup(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm2, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey())); EXPECT_EQ(nullptr, cache.LookupByPath(scheme_host_port, HttpAuth::AUTH_SERVER, NetworkAnonymizationKey(), "/baz/")); EXPECT_EQ(nullptr, cache.Lookup(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm3, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey())); EXPECT_EQ(nullptr, cache.Lookup(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm4, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey())); } TEST(HttpAuthCacheTest, ClearAllEntries) { url::SchemeHostPort scheme_host_port(GURL("http://foobar.com")); base::SimpleTestClock test_clock; test_clock.SetNow(base::Time::Now()); HttpAuthCache cache(false /* key_entries_by_network_anonymization_key */); cache.set_clock_for_testing(&test_clock); cache.Add(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), "basic realm=Realm1", AuthCredentials(kAlice, k123), "/"); cache.Add(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm2, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), "basic realm=Realm2", AuthCredentials(kRoot, kWileCoyote), "/"); test_clock.Advance(base::Seconds(10)); cache.Add(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm3, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), "basic realm=Realm3", AuthCredentials(kAlice2, k1234), "/"); cache.Add(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm4, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), "basic realm=Realm4", AuthCredentials(kUsername, kPassword), "/"); // Add path to existing entry. cache.Add(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm2, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), "basic realm=Realm2", AuthCredentials(kAdmin, kPassword), "/baz/"); test_clock.Advance(base::Seconds(55)); cache.ClearAllEntries(); // All entries should be cleared. EXPECT_EQ(nullptr, cache.Lookup(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey())); EXPECT_EQ(nullptr, cache.Lookup(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm2, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey())); EXPECT_EQ(nullptr, cache.LookupByPath(scheme_host_port, HttpAuth::AUTH_SERVER, NetworkAnonymizationKey(), "/baz/")); EXPECT_EQ(nullptr, cache.Lookup(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm3, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey())); EXPECT_EQ(nullptr, cache.Lookup(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm4, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey())); } TEST(HttpAuthCacheTest, UpdateStaleChallenge) { HttpAuthCache cache(false /* key_entries_by_network_anonymization_key */); url::SchemeHostPort scheme_host_port(GURL("http://foobar2.com")); HttpAuthCache::Entry* entry_pre = cache.Add( scheme_host_port, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_DIGEST, NetworkAnonymizationKey(), "Digest realm=Realm1," "nonce=\"s3MzvFhaBAA=4c520af5acd9d8d7ae26947529d18c8eae1e98f4\"", CreateASCIICredentials("realm-digest-user", "realm-digest-password"), "/baz/index.html"); ASSERT_TRUE(entry_pre != nullptr); EXPECT_EQ(2, entry_pre->IncrementNonceCount()); EXPECT_EQ(3, entry_pre->IncrementNonceCount()); EXPECT_EQ(4, entry_pre->IncrementNonceCount()); bool update_success = cache.UpdateStaleChallenge( scheme_host_port, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_DIGEST, NetworkAnonymizationKey(), "Digest realm=Realm1," "nonce=\"claGgoRXBAA=7583377687842fdb7b56ba0555d175baa0b800e3\"," "stale=\"true\""); EXPECT_TRUE(update_success); // After the stale update, the entry should still exist in the cache and // the nonce count should be reset to 0. HttpAuthCache::Entry* entry_post = cache.Lookup(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_DIGEST, NetworkAnonymizationKey()); ASSERT_TRUE(entry_post != nullptr); EXPECT_EQ(2, entry_post->IncrementNonceCount()); // UpdateStaleChallenge will fail if an entry doesn't exist in the cache. bool update_failure = cache.UpdateStaleChallenge( scheme_host_port, HttpAuth::AUTH_SERVER, kRealm2, HttpAuth::AUTH_SCHEME_DIGEST, NetworkAnonymizationKey(), "Digest realm=Realm2," "nonce=\"claGgoRXBAA=7583377687842fdb7b56ba0555d175baa0b800e3\"," "stale=\"true\""); EXPECT_FALSE(update_failure); } TEST(HttpAuthCacheTest, CopyProxyEntriesFrom) { url::SchemeHostPort scheme_host_port(GURL("http://example.com")); std::string path("/some/path"); std::string another_path("/another/path"); HttpAuthCache first_cache( false /* key_entries_by_network_anonymization_key */); HttpAuthCache::Entry* entry; first_cache.Add(scheme_host_port, HttpAuth::AUTH_PROXY, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), "basic realm=Realm1", AuthCredentials(kAlice, k123), path); first_cache.Add(scheme_host_port, HttpAuth::AUTH_PROXY, kRealm2, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), "basic realm=Realm2", AuthCredentials(kAlice2, k1234), path); first_cache.Add(scheme_host_port, HttpAuth::AUTH_PROXY, kRealm3, HttpAuth::AUTH_SCHEME_DIGEST, NetworkAnonymizationKey(), "digest realm=Realm3", AuthCredentials(kRoot, kWileCoyote), path); entry = first_cache.Add(scheme_host_port, HttpAuth::AUTH_PROXY, kRealm3, HttpAuth::AUTH_SCHEME_DIGEST, NetworkAnonymizationKey(), "digest realm=Realm3", AuthCredentials(kRoot, kWileCoyote), another_path); EXPECT_EQ(2, entry->IncrementNonceCount()); // Server entry, which should not be copied. first_cache.Add(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), "basic realm=Realm1", AuthCredentials(kAlice, k123), path); HttpAuthCache second_cache( false /* key_entries_by_network_anonymization_key */); // Will be overwritten by kRoot:kWileCoyote. second_cache.Add(scheme_host_port, HttpAuth::AUTH_PROXY, kRealm3, HttpAuth::AUTH_SCHEME_DIGEST, NetworkAnonymizationKey(), "digest realm=Realm3", AuthCredentials(kAlice2, k1234), path); // Should be left intact. second_cache.Add(scheme_host_port, HttpAuth::AUTH_PROXY, kRealm4, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), "basic realm=Realm4", AuthCredentials(kAdmin, kRoot), path); second_cache.CopyProxyEntriesFrom(first_cache); // Copied from first_cache. entry = second_cache.Lookup(scheme_host_port, HttpAuth::AUTH_PROXY, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey()); EXPECT_TRUE(nullptr != entry); EXPECT_EQ(kAlice, entry->credentials().username()); EXPECT_EQ(k123, entry->credentials().password()); // Copied from first_cache. entry = second_cache.Lookup(scheme_host_port, HttpAuth::AUTH_PROXY, kRealm2, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey()); EXPECT_TRUE(nullptr != entry); EXPECT_EQ(kAlice2, entry->credentials().username()); EXPECT_EQ(k1234, entry->credentials().password()); // Overwritten from first_cache. entry = second_cache.Lookup(scheme_host_port, HttpAuth::AUTH_PROXY, kRealm3, HttpAuth::AUTH_SCHEME_DIGEST, NetworkAnonymizationKey()); EXPECT_TRUE(nullptr != entry); EXPECT_EQ(kRoot, entry->credentials().username()); EXPECT_EQ(kWileCoyote, entry->credentials().password()); // Nonce count should get copied. EXPECT_EQ(3, entry->IncrementNonceCount()); // All paths should get copied. entry = second_cache.LookupByPath(scheme_host_port, HttpAuth::AUTH_PROXY, NetworkAnonymizationKey(), another_path); EXPECT_TRUE(nullptr != entry); EXPECT_EQ(kRoot, entry->credentials().username()); EXPECT_EQ(kWileCoyote, entry->credentials().password()); // Left intact in second_cache. entry = second_cache.Lookup(scheme_host_port, HttpAuth::AUTH_PROXY, kRealm4, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey()); EXPECT_TRUE(nullptr != entry); EXPECT_EQ(kAdmin, entry->credentials().username()); EXPECT_EQ(kRoot, entry->credentials().password()); // AUTH_SERVER entry should not have been copied from first_cache. EXPECT_TRUE(first_cache.Lookup(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey())); EXPECT_FALSE(second_cache.Lookup(scheme_host_port, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey())); } // Test fixture class for eviction tests (contains helpers for bulk // insertion and existence testing). class HttpAuthCacheEvictionTest : public testing::Test { protected: HttpAuthCacheEvictionTest() : scheme_host_port_(GURL("http://www.google.com")), cache_(false /* key_entries_by_network_anonymization_key */) {} std::string GenerateRealm(int realm_i) { return base::StringPrintf("Realm %d", realm_i); } std::string GeneratePath(int realm_i, int path_i) { return base::StringPrintf("/%d/%d/x/y", realm_i, path_i); } void AddRealm(int realm_i) { AddPathToRealm(realm_i, 0); } void AddPathToRealm(int realm_i, int path_i) { cache_.Add(scheme_host_port_, HttpAuth::AUTH_SERVER, GenerateRealm(realm_i), HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(), std::string(), AuthCredentials(kUsername, kPassword), GeneratePath(realm_i, path_i)); } void CheckRealmExistence(int realm_i, bool exists) { const HttpAuthCache::Entry* entry = cache_.Lookup( scheme_host_port_, HttpAuth::AUTH_SERVER, GenerateRealm(realm_i), HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey()); if (exists) { EXPECT_FALSE(entry == nullptr); EXPECT_EQ(GenerateRealm(realm_i), entry->realm()); } else { EXPECT_TRUE(entry == nullptr); } } void CheckPathExistence(int realm_i, int path_i, bool exists) { const HttpAuthCache::Entry* entry = cache_.LookupByPath( scheme_host_port_, HttpAuth::AUTH_SERVER, NetworkAnonymizationKey(), GeneratePath(realm_i, path_i)); if (exists) { EXPECT_FALSE(entry == nullptr); EXPECT_EQ(GenerateRealm(realm_i), entry->realm()); } else { EXPECT_TRUE(entry == nullptr); } } url::SchemeHostPort scheme_host_port_; HttpAuthCache cache_; static const int kMaxPaths = HttpAuthCache::kMaxNumPathsPerRealmEntry; static const int kMaxRealms = HttpAuthCache::kMaxNumRealmEntries; }; // Add the maxinim number of realm entries to the cache. Each of these entries // must still be retrievable. Next add three more entries -- since the cache is // full this causes FIFO eviction of the first three entries by time of last // use. TEST_F(HttpAuthCacheEvictionTest, RealmEntryEviction) { base::SimpleTestTickClock test_clock; test_clock.SetNowTicks(base::TimeTicks::Now()); cache_.set_tick_clock_for_testing(&test_clock); for (int i = 0; i < kMaxRealms; ++i) { AddRealm(i); test_clock.Advance(base::Seconds(1)); } for (int i = 0; i < kMaxRealms; ++i) { CheckRealmExistence(i, true); test_clock.Advance(base::Seconds(1)); } for (int i = 0; i < 3; ++i) { AddRealm(i + kMaxRealms); test_clock.Advance(base::Seconds(1)); } for (int i = 0; i < 3; ++i) { CheckRealmExistence(i, false); test_clock.Advance(base::Seconds(1)); } for (int i = 0; i < kMaxRealms; ++i) { CheckRealmExistence(i + 3, true); test_clock.Advance(base::Seconds(1)); } cache_.set_tick_clock_for_testing(nullptr); } // Add the maximum number of paths to a single realm entry. Each of these // paths should be retrievable. Next add 3 more paths -- since the cache is // full this causes FIFO eviction of the first three paths. TEST_F(HttpAuthCacheEvictionTest, RealmPathEviction) { for (int i = 0; i < kMaxPaths; ++i) AddPathToRealm(0, i); for (int i = 1; i < kMaxRealms; ++i) AddRealm(i); for (int i = 0; i < 3; ++i) AddPathToRealm(0, i + kMaxPaths); for (int i = 0; i < 3; ++i) CheckPathExistence(0, i, false); for (int i = 0; i < kMaxPaths; ++i) CheckPathExistence(0, i + 3, true); for (int i = 0; i < kMaxRealms; ++i) CheckRealmExistence(i, true); } } // namespace net