blob: 9bdcdbcb97508ed8436a387ac80e0f7a123887a0 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/login/profile_auth_data.h"
#include "base/barrier_closure.h"
#include "base/bind.h"
#include "base/callback.h"
#include "components/google/core/common/google_util.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/storage_partition.h"
#include "net/cookies/canonical_cookie.h"
#include "net/cookies/cookie_util.h"
#include "services/network/public/mojom/cookie_manager.mojom.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "url/gurl.h"
using content::BrowserThread;
namespace chromeos {
namespace {
// Callback that receives the key for from_partition's saved HttpAuthCache.
void OnTargetHttpAuthCacheSaved(base::RepeatingClosure completion_callback,
content::StoragePartition* to_partition,
const base::UnguessableToken& cache_key) {
to_partition->GetNetworkContext()->LoadHttpAuthCache(cache_key,
completion_callback);
}
// Starts tranferring |from_partition|'s HttpAuthCache into |to_partition|.
void TransferHttpAuthCache(base::RepeatingClosure completion_callback,
content::StoragePartition* from_partition,
content::StoragePartition* to_partition) {
// |to_partition| will outlive the call to |completion_callback|.
// See ProfileAuthData::Transfer.
from_partition->GetNetworkContext()->SaveHttpAuthCache(
base::BindOnce(&OnTargetHttpAuthCacheSaved, completion_callback,
base::Unretained(to_partition)));
}
// Given a |cookie| set during login, returns true if the cookie may have been
// set by GAIA. The main criterion is the |cookie|'s domain. If the domain
// is *google.<TLD> or *youtube.<TLD>, the cookie is considered to have been set
// by GAIA as well.
bool IsGAIACookie(const net::CanonicalCookie& cookie) {
GURL cookie_url =
net::cookie_util::CookieOriginToURL(cookie.Domain(), cookie.IsSecure());
return google_util::IsGoogleDomainUrl(
cookie_url, google_util::ALLOW_SUBDOMAIN,
google_util::ALLOW_NON_STANDARD_PORTS) ||
google_util::IsYoutubeDomainUrl(cookie_url,
google_util::ALLOW_SUBDOMAIN,
google_util::ALLOW_NON_STANDARD_PORTS);
}
void OnCookieSet(base::RepeatingClosure completion_callback,
net::CanonicalCookie::CookieInclusionStatus status) {
completion_callback.Run();
}
// Imports |cookies| into |to_partition|'s cookie jar. |cookie.IsCanonical()|
// must be true for all cookies in |cookies|.
void ImportCookies(base::RepeatingClosure completion_callback,
content::StoragePartition* to_partition,
const net::CookieList& cookies) {
if (cookies.empty()) {
completion_callback.Run();
return;
}
network::mojom::CookieManager* cookie_manager =
to_partition->GetCookieManagerForBrowserProcess();
base::RepeatingClosure cookie_completion_callback =
base::BarrierClosure(cookies.size(), completion_callback);
for (const auto& cookie : cookies) {
// Assume secure_source - since the cookies are being restored from
// another store, they have already gone through the strict secure check.
// Likewise for permitting same-site marked cookies.
DCHECK(cookie.IsCanonical());
net::CookieOptions options;
options.set_include_httponly();
options.set_same_site_cookie_context(
net::CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT);
cookie_manager->SetCanonicalCookie(
cookie, "https", options,
base::BindOnce(&OnCookieSet, cookie_completion_callback));
}
}
// Callback that receives the contents of |from_partition|'s cookie jar.
// Transfers the necessary cookies to |to_partition|'s cookie jar.
void OnCookiesToTransferRetrieved(base::RepeatingClosure completion_callback,
content::StoragePartition* to_partition,
bool first_login,
const net::CookieList& cookies) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (first_login) {
ImportCookies(completion_callback, to_partition, cookies);
} else {
net::CookieList non_gaia_cookies;
for (const auto& cookie : cookies) {
if (!IsGAIACookie(cookie))
non_gaia_cookies.push_back(cookie);
}
ImportCookies(completion_callback, to_partition, non_gaia_cookies);
}
}
// Callback that receives the content of |to_partition|'s cookie jar. Checks
// whether this is the user's first login, based on the state of the cookie
// jar, and starts retrieval of the data that should be transfered.
void OnTargetCookieJarContentsRetrieved(
base::RepeatingClosure completion_callback,
content::StoragePartition* from_partition,
content::StoragePartition* to_partition,
bool transfer_auth_cookies_on_first_login,
bool transfer_saml_auth_cookies_on_subsequent_login,
const net::CookieList& target_cookies) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
bool transfer_auth_cookies;
bool first_login = target_cookies.empty();
if (first_login) {
// On first login, transfer all auth cookies if
// |transfer_auth_cookies_on_first_login| is true.
transfer_auth_cookies = transfer_auth_cookies_on_first_login;
} else {
// On subsequent login, transfer auth cookies set by the SAML IdP if
// |transfer_saml_auth_cookies_on_subsequent_login| is true.
transfer_auth_cookies = transfer_saml_auth_cookies_on_subsequent_login;
}
if (!transfer_auth_cookies) {
completion_callback.Run();
return;
}
// Retrieve the contents of |from_partition|'s cookie jar. When the retrieval
// finishes, OnCookiesToTransferRetrieved will be called with the result.
network::mojom::CookieManager* from_manager =
from_partition->GetCookieManagerForBrowserProcess();
from_manager->GetAllCookies(
base::BindOnce(&OnCookiesToTransferRetrieved, completion_callback,
base::Unretained(to_partition), first_login));
}
// Starts the process of transferring cookies from |from_partition| to
// |to_partition|.
void TransferCookies(base::RepeatingClosure completion_callback,
content::StoragePartition* from_partition,
content::StoragePartition* to_partition,
bool transfer_auth_cookies_on_first_login,
bool transfer_saml_auth_cookies_on_subsequent_login) {
if (transfer_auth_cookies_on_first_login ||
transfer_saml_auth_cookies_on_subsequent_login) {
// Retrieve the contents of |to_partition_|'s cookie jar.
network::mojom::CookieManager* to_manager =
to_partition->GetCookieManagerForBrowserProcess();
to_manager->GetAllCookies(base::BindOnce(
&OnTargetCookieJarContentsRetrieved, completion_callback,
base::Unretained(from_partition), base::Unretained(to_partition),
transfer_auth_cookies_on_first_login,
transfer_saml_auth_cookies_on_subsequent_login));
} else {
completion_callback.Run();
}
}
} // namespace
void ProfileAuthData::Transfer(
content::StoragePartition* from_partition,
content::StoragePartition* to_partition,
bool transfer_auth_cookies_on_first_login,
bool transfer_saml_auth_cookies_on_subsequent_login,
base::OnceClosure completion_callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// The BarrierClosure will call |completion_callback| after the 2 async
// transfers have finished.
base::RepeatingClosure task_completion_callback =
base::BarrierClosure(2, std::move(completion_callback));
// Transfer the proxy auth cache from |from_context| to |to_context|. If
// the user was required to authenticate with a proxy during login, this
// authentication information will be transferred into the user's session.
TransferHttpAuthCache(task_completion_callback, from_partition, to_partition);
TransferCookies(task_completion_callback, from_partition, to_partition,
transfer_auth_cookies_on_first_login,
transfer_saml_auth_cookies_on_subsequent_login);
}
} // namespace chromeos