blob: 1fa382c0cda43872507adcb688d6466c19c1aeec [file] [log] [blame]
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ash/login/profile_auth_data.h"
#include <memory>
#include <string>
#include <utility>
#include "base/compiler_specific.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind.h"
#include "base/time/time.h"
#include "chrome/test/base/testing_profile.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/network_service_instance.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_utils.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "net/base/network_anonymization_key.h"
#include "net/cookies/canonical_cookie.h"
#include "net/cookies/cookie_constants.h"
#include "net/http/http_auth.h"
#include "net/http/http_auth_cache.h"
#include "net/http/http_network_session.h"
#include "net/http/http_transaction_factory.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_getter.h"
#include "services/cert_verifier/public/mojom/cert_verifier_service_factory.mojom.h"
#include "services/network/network_context.h"
#include "services/network/network_service.h"
#include "services/network/public/mojom/cookie_manager.mojom.h"
#include "services/network/public/mojom/network_context.mojom-forward.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
#include "url/scheme_host_port.h"
namespace ash {
namespace {
const char kProxyAuthURL[] = "https://example.com/";
const char kProxyAuthRealm[] = "realm";
const char kProxyAuthChallenge[] = "challenge";
const char16_t kProxyAuthPassword1[] = u"password 1";
const char16_t kProxyAuthPassword2[] = u"password 2";
const char kGAIACookieURL[] = "https://google.com/";
const char kSAMLIdPCookieURL[] = "https://example.com/";
const char kCookieName[] = "cookie";
const char kCookieValue1[] = "value 1";
const char kCookieValue2[] = "value 2";
const char kGAIACookieDomain[] = "google.com";
const char kSAMLIdPCookieDomain[] = "example.com";
const char kSAMLIdPCookieDomainWithWildcard[] = ".example.com";
std::unique_ptr<network::NetworkContext>
CreateNetworkContextForDefaultStoragePartition(
network::NetworkService* network_service,
content::BrowserContext* browser_context) {
mojo::PendingRemote<network::mojom::NetworkContext> network_context_remote;
auto params = network::mojom::NetworkContextParams::New();
params->cert_verifier_params = content::GetCertVerifierParams(
cert_verifier::mojom::CertVerifierCreationParams::New());
auto network_context = std::make_unique<network::NetworkContext>(
network_service, network_context_remote.InitWithNewPipeAndPassReceiver(),
std::move(params));
browser_context->GetDefaultStoragePartition()->SetNetworkContextForTesting(
std::move(network_context_remote));
return network_context;
}
network::NetworkService* GetNetworkService() {
content::GetNetworkService();
// Wait for the Network Service to initialize on the IO thread.
content::RunAllPendingInMessageLoop(content::BrowserThread::IO);
return network::NetworkService::GetNetworkServiceForTesting();
}
} // namespace
class ProfileAuthDataTest : public testing::Test {
public:
ProfileAuthDataTest();
// testing::Test:
void SetUp() override;
void PopulateUserBrowserContext();
void Transfer(bool transfer_auth_cookies_on_first_login,
bool transfer_saml_auth_cookies_on_subsequent_login);
net::CookieList GetUserCookies();
void VerifyTransferredUserProxyAuthEntry();
void VerifyUserCookies(const std::string& expected_gaia_cookie_value,
const std::string& expected_saml_idp_cookie_value);
private:
void PopulateBrowserContext(TestingProfile* browser_context,
network::NetworkContext* network_context,
const std::u16string& proxy_auth_password,
const std::string& cookie_value);
net::HttpAuthCache* GetAuthCache(network::NetworkContext* network_context);
network::mojom::CookieManager* GetCookies(
content::BrowserContext* browser_context);
content::BrowserTaskEnvironment task_environment_;
raw_ptr<network::NetworkService> network_service_;
TestingProfile login_browser_context_;
TestingProfile user_browser_context_;
std::unique_ptr<network::NetworkContext> login_network_context_;
std::unique_ptr<network::NetworkContext> user_network_context_;
};
ProfileAuthDataTest::ProfileAuthDataTest()
: network_service_(GetNetworkService()) {
login_network_context_ = CreateNetworkContextForDefaultStoragePartition(
network_service_, &login_browser_context_);
user_network_context_ = CreateNetworkContextForDefaultStoragePartition(
network_service_, &user_browser_context_);
}
void ProfileAuthDataTest::SetUp() {
PopulateBrowserContext(&login_browser_context_, login_network_context_.get(),
kProxyAuthPassword1, kCookieValue1);
}
void ProfileAuthDataTest::PopulateUserBrowserContext() {
PopulateBrowserContext(&user_browser_context_, user_network_context_.get(),
kProxyAuthPassword2, kCookieValue2);
}
void ProfileAuthDataTest::Transfer(
bool transfer_auth_cookies_on_first_login,
bool transfer_saml_auth_cookies_on_subsequent_login) {
base::RunLoop run_loop;
ProfileAuthData::Transfer(
login_browser_context_.GetDefaultStoragePartition(),
&user_browser_context_, transfer_auth_cookies_on_first_login,
transfer_saml_auth_cookies_on_subsequent_login, run_loop.QuitClosure());
run_loop.Run();
if (!transfer_auth_cookies_on_first_login &&
!transfer_saml_auth_cookies_on_subsequent_login) {
// When only proxy auth state is being transferred, the completion callback
// is invoked before the transfer has actually completed. Spin the loop once
// more to allow the transfer to complete.
base::RunLoop().RunUntilIdle();
}
}
net::CookieList ProfileAuthDataTest::GetUserCookies() {
base::RunLoop run_loop;
net::CookieList result;
GetCookies(&user_browser_context_)
->GetAllCookies(
base::BindLambdaForTesting([&](const net::CookieList& cookie_list) {
result = cookie_list;
run_loop.Quit();
}));
run_loop.Run();
return result;
}
void ProfileAuthDataTest::VerifyTransferredUserProxyAuthEntry() {
net::HttpAuthCache::Entry* entry =
GetAuthCache(user_network_context_.get())
->Lookup(url::SchemeHostPort(GURL(kProxyAuthURL)),
net::HttpAuth::AUTH_PROXY, kProxyAuthRealm,
net::HttpAuth::AUTH_SCHEME_BASIC,
net::NetworkAnonymizationKey());
ASSERT_TRUE(entry);
EXPECT_EQ(kProxyAuthPassword1, entry->credentials().password());
}
void ProfileAuthDataTest::VerifyUserCookies(
const std::string& expected_gaia_cookie_value,
const std::string& expected_saml_idp_cookie_value) {
net::CookieList user_cookies = GetUserCookies();
ASSERT_EQ(3u, user_cookies.size());
// Cookies are returned chronoligically, in the order they were set.
net::CanonicalCookie* cookie = &user_cookies[0];
EXPECT_EQ(kCookieName, cookie->Name());
EXPECT_EQ(expected_saml_idp_cookie_value, cookie->Value());
EXPECT_EQ(kSAMLIdPCookieDomainWithWildcard, cookie->Domain());
cookie = &user_cookies[1];
EXPECT_EQ(kCookieName, cookie->Name());
EXPECT_EQ(expected_saml_idp_cookie_value, cookie->Value());
EXPECT_EQ(kSAMLIdPCookieDomain, cookie->Domain());
cookie = &user_cookies[2];
EXPECT_EQ(kCookieName, cookie->Name());
EXPECT_EQ(expected_gaia_cookie_value, cookie->Value());
EXPECT_EQ(kGAIACookieDomain, cookie->Domain());
}
void ProfileAuthDataTest::PopulateBrowserContext(
TestingProfile* browser_context,
network::NetworkContext* network_context,
const std::u16string& proxy_auth_password,
const std::string& cookie_value) {
GetAuthCache(network_context)
->Add(url::SchemeHostPort(GURL(kProxyAuthURL)), net::HttpAuth::AUTH_PROXY,
kProxyAuthRealm, net::HttpAuth::AUTH_SCHEME_BASIC,
net::NetworkAnonymizationKey(), kProxyAuthChallenge,
net::AuthCredentials(std::u16string(), proxy_auth_password),
std::string());
network::mojom::CookieManager* cookies = GetCookies(browser_context);
// Ensure `cookies` is fully initialized.
base::RunLoop run_loop;
cookies->GetAllCookies(base::BindLambdaForTesting(
[&](const net::CookieList& cookies) { run_loop.Quit(); }));
run_loop.Run();
net::CookieOptions options;
options.set_include_httponly();
cookies->SetCanonicalCookie(
*net::CanonicalCookie::CreateSanitizedCookie(
GURL(kSAMLIdPCookieURL), kCookieName, cookie_value,
kSAMLIdPCookieDomainWithWildcard, std::string(), base::Time(),
base::Time(), base::Time(), true, false,
net::CookieSameSite::NO_RESTRICTION, net::COOKIE_PRIORITY_DEFAULT,
std::nullopt, /*status=*/nullptr),
GURL(kSAMLIdPCookieURL), options, base::DoNothing());
cookies->SetCanonicalCookie(
*net::CanonicalCookie::CreateSanitizedCookie(
GURL(kSAMLIdPCookieURL), kCookieName, cookie_value, std::string(),
std::string(), base::Time(), base::Time(), base::Time(), true, false,
net::CookieSameSite::NO_RESTRICTION, net::COOKIE_PRIORITY_DEFAULT,
std::nullopt, /*status=*/nullptr),
GURL(kSAMLIdPCookieURL), options, base::DoNothing());
cookies->SetCanonicalCookie(
*net::CanonicalCookie::CreateSanitizedCookie(
GURL(kGAIACookieURL), kCookieName, cookie_value, std::string(),
std::string(), base::Time(), base::Time(), base::Time(), true, false,
net::CookieSameSite::NO_RESTRICTION, net::COOKIE_PRIORITY_DEFAULT,
std::nullopt, /*status=*/nullptr),
GURL(kGAIACookieURL), options, base::DoNothing());
}
net::HttpAuthCache* ProfileAuthDataTest::GetAuthCache(
network::NetworkContext* network_context) {
return network_context->url_request_context()
->http_transaction_factory()
->GetSession()
->http_auth_cache();
}
network::mojom::CookieManager* ProfileAuthDataTest::GetCookies(
content::BrowserContext* browser_context) {
return browser_context->GetDefaultStoragePartition()
->GetCookieManagerForBrowserProcess();
}
// Verifies that when no transfer of auth cookies is requested, only the proxy
// auth state is transferred.
TEST_F(ProfileAuthDataTest, DoNotTransfer) {
Transfer(false, false);
VerifyTransferredUserProxyAuthEntry();
EXPECT_TRUE(GetUserCookies().empty());
}
// Verifies that when the transfer of auth cookies on first login is requested,
// they do get transferred along with the proxy auth state on first login.
TEST_F(ProfileAuthDataTest, TransferOnFirstLoginWithNewProfile) {
Transfer(true, false);
VerifyTransferredUserProxyAuthEntry();
VerifyUserCookies(kCookieValue1, kCookieValue1);
}
// Verifies that even if the transfer of auth cookies on first login is
// requested, only the proxy auth state is transferred on subsequent login.
TEST_F(ProfileAuthDataTest, TransferOnFirstLoginWithExistingProfile) {
PopulateUserBrowserContext();
Transfer(true, false);
VerifyTransferredUserProxyAuthEntry();
VerifyUserCookies(kCookieValue2, kCookieValue2);
}
// Verifies that when the transfer of auth cookies set by a SAML IdP on
// subsequent login is requested, they do get transferred along with the proxy
// auth state on subsequent login.
TEST_F(ProfileAuthDataTest, TransferOnSubsequentLogin) {
PopulateUserBrowserContext();
Transfer(false, true);
VerifyTransferredUserProxyAuthEntry();
VerifyUserCookies(kCookieValue2, kCookieValue1);
}
} // namespace ash