blob: 322ececfb9f5a7e51c9eb1aa75ae35feaa1d2d0f [file] [log] [blame]
// Copyright 2014 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/signin/chrome_signin_client.h"
#include <stddef.h>
#include <memory>
#include <string>
#include <utility>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "build/buildflag.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/content_settings/cookie_settings_factory.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/profiles/profile_attributes_entry.h"
#include "chrome/browser/profiles/profile_attributes_storage.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/profiles/profile_metrics.h"
#include "chrome/browser/profiles/profile_window.h"
#include "chrome/browser/signin/account_consistency_mode_manager.h"
#include "chrome/browser/signin/force_signin_verifier.h"
#include "chrome/browser/signin/local_auth.h"
#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
#include "chrome/browser/signin/signin_manager_factory.h"
#include "chrome/browser/signin/signin_util.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/web_data_service_factory.h"
#include "chrome/common/buildflags.h"
#include "chrome/common/channel_info.h"
#include "chrome/common/pref_names.h"
#include "components/content_settings/core/browser/cookie_settings.h"
#include "components/metrics/metrics_service.h"
#include "components/prefs/pref_service.h"
#include "components/signin/core/browser/cookie_settings_util.h"
#include "components/signin/core/browser/profile_management_switches.h"
#include "components/signin/core/browser/profile_oauth2_token_service.h"
#include "components/signin/core/browser/signin_buildflags.h"
#include "components/signin/core/browser/signin_cookie_change_subscription.h"
#include "components/signin/core/browser/signin_header_helper.h"
#include "components/signin/core/browser/signin_pref_names.h"
#include "components/signin/core/browser/signin_switches.h"
#include "google_apis/gaia/gaia_constants.h"
#include "google_apis/gaia/gaia_urls.h"
#include "net/url_request/url_request_context_getter.h"
#include "url/gurl.h"
#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
#include "chrome/browser/supervised_user/supervised_user_constants.h"
#endif
#if defined(OS_CHROMEOS)
#include "chrome/browser/chromeos/net/delay_network_call.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "components/user_manager/known_user.h"
#include "components/user_manager/user_manager.h"
#else
#include "chrome/browser/ui/user_manager.h"
#endif
#if !defined(OS_ANDROID)
#include "chrome/browser/first_run/first_run.h"
#endif
ChromeSigninClient::ChromeSigninClient(
Profile* profile,
SigninErrorController* signin_error_controller)
: OAuth2TokenService::Consumer("chrome_signin_client"),
profile_(profile),
signin_error_controller_(signin_error_controller),
weak_ptr_factory_(this) {
signin_error_controller_->AddObserver(this);
#if !defined(OS_CHROMEOS)
g_browser_process->network_connection_tracker()->AddNetworkConnectionObserver(
this);
#else
// UserManager may not exist in unit_tests.
if (!user_manager::UserManager::IsInitialized())
return;
const user_manager::User* user =
chromeos::ProfileHelper::Get()->GetUserByProfile(profile_);
if (!user)
return;
const AccountId account_id = user->GetAccountId();
if (user_manager::known_user::GetDeviceId(account_id).empty()) {
const std::string legacy_device_id =
GetPrefs()->GetString(prefs::kGoogleServicesSigninScopedDeviceId);
if (!legacy_device_id.empty()) {
// Need to move device ID from the old location to the new one, if it has
// not been done yet.
user_manager::known_user::SetDeviceId(account_id, legacy_device_id);
} else {
user_manager::known_user::SetDeviceId(
account_id, GenerateSigninScopedDeviceID(
user_manager::UserManager::Get()
->IsUserNonCryptohomeDataEphemeral(account_id)));
}
}
GetPrefs()->SetString(prefs::kGoogleServicesSigninScopedDeviceId,
std::string());
#endif
}
ChromeSigninClient::~ChromeSigninClient() {
signin_error_controller_->RemoveObserver(this);
#if !defined(OS_CHROMEOS)
g_browser_process->network_connection_tracker()
->RemoveNetworkConnectionObserver(this);
#endif
}
void ChromeSigninClient::DoFinalInit() {
MaybeFetchSigninTokenHandle();
VerifySyncToken();
}
// static
bool ChromeSigninClient::ProfileAllowsSigninCookies(Profile* profile) {
content_settings::CookieSettings* cookie_settings =
CookieSettingsFactory::GetForProfile(profile).get();
return signin::SettingsAllowSigninCookies(cookie_settings);
}
PrefService* ChromeSigninClient::GetPrefs() { return profile_->GetPrefs(); }
scoped_refptr<TokenWebData> ChromeSigninClient::GetDatabase() {
return WebDataServiceFactory::GetTokenWebDataForProfile(
profile_, ServiceAccessType::EXPLICIT_ACCESS);
}
bool ChromeSigninClient::CanRevokeCredentials() {
#if defined(OS_CHROMEOS)
// UserManager may not exist in unit_tests.
if (user_manager::UserManager::IsInitialized() &&
user_manager::UserManager::Get()->IsLoggedInAsSupervisedUser()) {
// Don't allow revoking credentials for Chrome OS supervised users.
// See http://crbug.com/332032
LOG(ERROR) << "Attempt to revoke supervised user refresh "
<< "token detected, ignoring.";
return false;
}
#else
// Don't allow revoking credentials for legacy supervised users.
// See http://crbug.com/332032
if (profile_->IsLegacySupervised()) {
LOG(ERROR) << "Attempt to revoke supervised user refresh "
<< "token detected, ignoring.";
return false;
}
#endif
return true;
}
std::string ChromeSigninClient::GetSigninScopedDeviceId() {
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableSigninScopedDeviceId)) {
return std::string();
}
#if !defined(OS_CHROMEOS)
return SigninClient::GetOrCreateScopedDeviceIdPref(GetPrefs());
#else
// UserManager may not exist in unit_tests.
if (!user_manager::UserManager::IsInitialized())
return std::string();
const user_manager::User* user =
chromeos::ProfileHelper::Get()->GetUserByProfile(profile_);
if (!user)
return std::string();
const std::string signin_scoped_device_id =
user_manager::known_user::GetDeviceId(user->GetAccountId());
LOG_IF(ERROR, signin_scoped_device_id.empty())
<< "Device ID is not set for user.";
return signin_scoped_device_id;
#endif
}
void ChromeSigninClient::OnSignedOut() {
ProfileAttributesEntry* entry;
bool has_entry = g_browser_process->profile_manager()->
GetProfileAttributesStorage().
GetProfileAttributesWithPath(profile_->GetPath(), &entry);
// If sign out occurs because Sync setup was in progress and the Profile got
// deleted, then the profile's no longer in the ProfileAttributesStorage.
if (!has_entry)
return;
entry->SetLocalAuthCredentials(std::string());
entry->SetAuthInfo(std::string(), base::string16());
entry->SetIsSigninRequired(false);
}
net::URLRequestContextGetter* ChromeSigninClient::GetURLRequestContext() {
return profile_->GetRequestContext();
}
bool ChromeSigninClient::ShouldMergeSigninCredentialsIntoCookieJar() {
return !signin::IsAccountConsistencyMirrorEnabled();
}
std::string ChromeSigninClient::GetProductVersion() {
return chrome::GetVersionString();
}
bool ChromeSigninClient::IsFirstRun() const {
#if defined(OS_ANDROID)
return false;
#else
return first_run::IsChromeFirstRun();
#endif
}
base::Time ChromeSigninClient::GetInstallDate() {
return base::Time::FromTimeT(
g_browser_process->metrics_service()->GetInstallDate());
}
bool ChromeSigninClient::AreSigninCookiesAllowed() {
return ProfileAllowsSigninCookies(profile_);
}
void ChromeSigninClient::AddContentSettingsObserver(
content_settings::Observer* observer) {
HostContentSettingsMapFactory::GetForProfile(profile_)
->AddObserver(observer);
}
void ChromeSigninClient::RemoveContentSettingsObserver(
content_settings::Observer* observer) {
HostContentSettingsMapFactory::GetForProfile(profile_)
->RemoveObserver(observer);
}
std::unique_ptr<SigninClient::CookieChangeSubscription>
ChromeSigninClient::AddCookieChangeCallback(
const GURL& url,
const std::string& name,
net::CookieChangeCallback callback) {
scoped_refptr<net::URLRequestContextGetter> context_getter =
profile_->GetRequestContext();
DCHECK(context_getter.get());
return std::make_unique<SigninCookieChangeSubscription>(
context_getter, url, name, std::move(callback));
}
void ChromeSigninClient::OnSignedIn(const std::string& account_id,
const std::string& gaia_id,
const std::string& username,
const std::string& password) {
ProfileManager* profile_manager = g_browser_process->profile_manager();
ProfileAttributesEntry* entry;
if (profile_manager->GetProfileAttributesStorage().
GetProfileAttributesWithPath(profile_->GetPath(), &entry)) {
entry->SetAuthInfo(gaia_id, base::UTF8ToUTF16(username));
ProfileMetrics::UpdateReportedProfilesStatistics(profile_manager);
}
}
void ChromeSigninClient::PostSignedIn(const std::string& account_id,
const std::string& username,
const std::string& password) {
#if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
// Don't store password hash except when lock is available for the user.
if (!password.empty() && profiles::IsLockAvailable(profile_))
LocalAuth::SetLocalAuthCredentials(profile_, password);
#endif
}
void ChromeSigninClient::PreSignOut(
const base::Callback<void()>& sign_out,
signin_metrics::ProfileSignout signout_source_metric) {
#if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
// These sign out won't remove the policy cache, keep the window opened.
bool keep_window_opened =
signout_source_metric ==
signin_metrics::GOOGLE_SERVICE_NAME_PATTERN_CHANGED ||
signout_source_metric == signin_metrics::SERVER_FORCED_DISABLE ||
signout_source_metric == signin_metrics::SIGNOUT_PREF_CHANGED;
if (signin_util::IsForceSigninEnabled() && !profile_->IsSystemProfile() &&
!profile_->IsGuestSession() && !profile_->IsSupervised() &&
!keep_window_opened) {
if (signout_source_metric ==
signin_metrics::SIGNIN_PREF_CHANGED_DURING_SIGNIN) {
// SIGNIN_PREF_CHANGED_DURING_SIGNIN will be triggered when SigninManager
// is initialized before window opening, there is no need to close window.
// Call OnCloseBrowsersSuccess to continue sign out and show UserManager
// afterwards.
should_display_user_manager_ = false; // Don't show UserManager twice.
OnCloseBrowsersSuccess(sign_out, signout_source_metric,
profile_->GetPath());
} else {
BrowserList::CloseAllBrowsersWithProfile(
profile_,
base::Bind(&ChromeSigninClient::OnCloseBrowsersSuccess,
base::Unretained(this), sign_out, signout_source_metric),
base::Bind(&ChromeSigninClient::OnCloseBrowsersAborted,
base::Unretained(this)),
signout_source_metric == signin_metrics::ABORT_SIGNIN ||
signout_source_metric ==
signin_metrics::AUTHENTICATION_FAILED_WITH_FORCE_SIGNIN ||
signout_source_metric == signin_metrics::TRANSFER_CREDENTIALS);
}
} else {
#else
{
#endif
SigninClient::PreSignOut(sign_out, signout_source_metric);
}
}
void ChromeSigninClient::OnErrorChanged() {
// Some tests don't have a ProfileManager.
if (g_browser_process->profile_manager() == nullptr)
return;
ProfileAttributesEntry* entry;
if (!g_browser_process->profile_manager()->GetProfileAttributesStorage().
GetProfileAttributesWithPath(profile_->GetPath(), &entry)) {
return;
}
entry->SetIsAuthError(signin_error_controller_->HasError());
}
void ChromeSigninClient::OnGetTokenInfoResponse(
std::unique_ptr<base::DictionaryValue> token_info) {
if (!token_info->HasKey("error")) {
std::string handle;
if (token_info->GetString("token_handle", &handle)) {
ProfileAttributesEntry* entry = nullptr;
bool has_entry = g_browser_process->profile_manager()->
GetProfileAttributesStorage().
GetProfileAttributesWithPath(profile_->GetPath(), &entry);
DCHECK(has_entry);
entry->SetPasswordChangeDetectionToken(handle);
}
}
oauth_request_.reset();
}
void ChromeSigninClient::OnOAuthError() {
// Ignore the failure. It's not essential and we'll try again next time.
oauth_request_.reset();
}
void ChromeSigninClient::OnNetworkError(int response_code) {
// Ignore the failure. It's not essential and we'll try again next time.
oauth_request_.reset();
}
void ChromeSigninClient::OnGetTokenSuccess(
const OAuth2TokenService::Request* request,
const std::string& access_token,
const base::Time& expiration_time) {
// Exchange the access token for a handle that can be used for later
// verification that the token is still valid (i.e. the password has not
// been changed).
if (!oauth_client_) {
oauth_client_.reset(new gaia::GaiaOAuthClient(
profile_->GetRequestContext()));
}
oauth_client_->GetTokenInfo(access_token, 3 /* retries */, this);
}
void ChromeSigninClient::OnGetTokenFailure(
const OAuth2TokenService::Request* request,
const GoogleServiceAuthError& error) {
// Ignore the failure. It's not essential and we'll try again next time.
oauth_request_.reset();
}
#if !defined(OS_CHROMEOS)
void ChromeSigninClient::OnConnectionChanged(
network::mojom::ConnectionType type) {
if (type == network::mojom::ConnectionType::CONNECTION_NONE)
return;
for (const base::Closure& callback : delayed_callbacks_)
callback.Run();
delayed_callbacks_.clear();
}
#endif
void ChromeSigninClient::DelayNetworkCall(const base::Closure& callback) {
#if defined(OS_CHROMEOS)
chromeos::DelayNetworkCall(
base::TimeDelta::FromMilliseconds(chromeos::kDefaultNetworkRetryDelayMS),
callback);
return;
#else
// Don't bother if we don't have any kind of network connection.
network::mojom::ConnectionType type;
bool sync =
g_browser_process->network_connection_tracker()->GetConnectionType(
&type, base::BindOnce(&ChromeSigninClient::OnConnectionChanged,
weak_ptr_factory_.GetWeakPtr()));
if (!sync || type == network::mojom::ConnectionType::CONNECTION_NONE) {
// Connection type cannot be retrieved synchronously so delay the callback.
delayed_callbacks_.push_back(callback);
} else {
callback.Run();
}
#endif
}
std::unique_ptr<GaiaAuthFetcher> ChromeSigninClient::CreateGaiaAuthFetcher(
GaiaAuthConsumer* consumer,
const std::string& source,
net::URLRequestContextGetter* getter) {
return std::make_unique<GaiaAuthFetcher>(consumer, source, getter);
}
void ChromeSigninClient::VerifySyncToken() {
#if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
if (signin_util::IsForceSigninEnabled())
force_signin_verifier_ = std::make_unique<ForceSigninVerifier>(profile_);
#endif
}
void ChromeSigninClient::MaybeFetchSigninTokenHandle() {
#if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
// We get a "handle" that can be used to reference the signin token on the
// server. We fetch this if we don't have one so that later we can check
// it to know if the signin token to which it is attached has been revoked
// and thus distinguish between a password mismatch due to the password
// being changed and the user simply mis-typing it.
if (profiles::IsLockAvailable(profile_)) {
ProfileAttributesStorage& storage =
g_browser_process->profile_manager()->GetProfileAttributesStorage();
ProfileAttributesEntry* entry;
// If we don't have a token for detecting a password change, create one.
if (storage.GetProfileAttributesWithPath(profile_->GetPath(), &entry) &&
entry->GetPasswordChangeDetectionToken().empty() && !oauth_request_) {
std::string account_id = SigninManagerFactory::GetForProfile(profile_)
->GetAuthenticatedAccountId();
if (!account_id.empty()) {
ProfileOAuth2TokenService* token_service =
ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
OAuth2TokenService::ScopeSet scopes;
scopes.insert(GaiaConstants::kGoogleUserInfoEmail);
oauth_request_ = token_service->StartRequest(account_id, scopes, this);
}
}
}
#endif
}
void ChromeSigninClient::AfterCredentialsCopied() {
if (signin_util::IsForceSigninEnabled()) {
// The signout after credential copy won't open UserManager after all
// browser window are closed. Because the browser window will be opened for
// the new profile soon.
should_display_user_manager_ = false;
}
}
void ChromeSigninClient::SetReadyForDiceMigration(bool is_ready) {
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
AccountConsistencyModeManager::GetForProfile(profile_)
->SetReadyForDiceMigration(is_ready);
#else
NOTREACHED();
#endif
}
void ChromeSigninClient::OnCloseBrowsersSuccess(
const base::Callback<void()>& sign_out,
const signin_metrics::ProfileSignout signout_source_metric,
const base::FilePath& profile_path) {
#if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
if (signin_util::IsForceSigninEnabled() && force_signin_verifier_.get()) {
force_signin_verifier_->Cancel();
}
#endif
SigninClient::PreSignOut(sign_out, signout_source_metric);
LockForceSigninProfile(profile_path);
// After sign out, lock the profile and show UserManager if necessary.
if (should_display_user_manager_) {
ShowUserManager(profile_path);
} else {
should_display_user_manager_ = true;
}
}
void ChromeSigninClient::OnCloseBrowsersAborted(
const base::FilePath& profile_path) {
should_display_user_manager_ = true;
}
void ChromeSigninClient::LockForceSigninProfile(
const base::FilePath& profile_path) {
ProfileAttributesEntry* entry;
bool has_entry =
g_browser_process->profile_manager()
->GetProfileAttributesStorage()
.GetProfileAttributesWithPath(profile_->GetPath(), &entry);
if (!has_entry)
return;
entry->LockForceSigninProfile(true);
}
void ChromeSigninClient::ShowUserManager(const base::FilePath& profile_path) {
#if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
UserManager::Show(profile_path,
profiles::USER_MANAGER_SELECT_PROFILE_NO_ACTION);
#endif
}