blob: 0f20487c4f89b09f18f6e9795f2fdc73012ba080 [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/chromeos/login/session/user_session_manager.h"
#include <stddef.h>
#include <algorithm>
#include <map>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include "base/base_paths.h"
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/command_line.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_macros.h"
#include "base/path_service.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string16.h"
#include "base/sys_info.h"
#include "base/task/post_task.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chrome/browser/about_flags.h"
#include "chrome/browser/app_mode/app_mode_utils.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_process_platform_part_chromeos.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/chromeos/arc/arc_migration_guide_notification.h"
#include "chrome/browser/chromeos/arc/arc_service_launcher.h"
#include "chrome/browser/chromeos/arc/arc_util.h"
#include "chrome/browser/chromeos/base/locale_util.h"
#include "chrome/browser/chromeos/boot_times_recorder.h"
#include "chrome/browser/chromeos/child_accounts/consumer_status_reporting_service_factory.h"
#include "chrome/browser/chromeos/child_accounts/screen_time_controller_factory.h"
#include "chrome/browser/chromeos/crostini/crostini_manager.h"
#include "chrome/browser/chromeos/first_run/first_run.h"
#include "chrome/browser/chromeos/first_run/goodies_displayer.h"
#include "chrome/browser/chromeos/lock_screen_apps/state_controller.h"
#include "chrome/browser/chromeos/logging.h"
#include "chrome/browser/chromeos/login/auth/chrome_cryptohome_authenticator.h"
#include "chrome/browser/chromeos/login/chrome_restart_request.h"
#include "chrome/browser/chromeos/login/demo_mode/demo_app_launcher.h"
#include "chrome/browser/chromeos/login/demo_mode/demo_session.h"
#include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_key_manager.h"
#include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_service.h"
#include "chrome/browser/chromeos/login/existing_user_controller.h"
#include "chrome/browser/chromeos/login/helper.h"
#include "chrome/browser/chromeos/login/lock/screen_locker.h"
#include "chrome/browser/chromeos/login/profile_auth_data.h"
#include "chrome/browser/chromeos/login/quick_unlock/pin_backend.h"
#include "chrome/browser/chromeos/login/saml/saml_offline_signin_limiter.h"
#include "chrome/browser/chromeos/login/saml/saml_offline_signin_limiter_factory.h"
#include "chrome/browser/chromeos/login/screens/sync_consent_screen.h"
#include "chrome/browser/chromeos/login/signin/oauth2_login_manager.h"
#include "chrome/browser/chromeos/login/signin/oauth2_login_manager_factory.h"
#include "chrome/browser/chromeos/login/signin/token_handle_fetcher.h"
#include "chrome/browser/chromeos/login/startup_utils.h"
#include "chrome/browser/chromeos/login/ui/input_events_blocker.h"
#include "chrome/browser/chromeos/login/ui/login_display_host.h"
#include "chrome/browser/chromeos/login/user_flow.h"
#include "chrome/browser/chromeos/login/users/chrome_user_manager.h"
#include "chrome/browser/chromeos/login/users/supervised_user_manager.h"
#include "chrome/browser/chromeos/login/wizard_controller.h"
#include "chrome/browser/chromeos/policy/app_install_event_log_manager_wrapper.h"
#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/chromeos/settings/cros_settings.h"
#include "chrome/browser/chromeos/settings/install_attributes.h"
#include "chrome/browser/chromeos/tether/tether_service.h"
#include "chrome/browser/chromeos/tpm_firmware_update_notification.h"
#include "chrome/browser/component_updater/crl_set_component_installer.h"
#include "chrome/browser/component_updater/sth_set_component_installer.h"
#include "chrome/browser/first_run/first_run.h"
#include "chrome/browser/google/google_brand_chromeos.h"
#include "chrome/browser/lifetime/application_lifetime.h"
#include "chrome/browser/lifetime/browser_shutdown.h"
#include "chrome/browser/net/nss_context.h"
#include "chrome/browser/password_manager/password_store_factory.h"
#include "chrome/browser/prefs/session_startup_pref.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/signin/account_tracker_service_factory.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/signin/signin_error_controller_factory.h"
#include "chrome/browser/signin/signin_manager_factory.h"
#include "chrome/browser/supervised_user/child_accounts/child_account_service.h"
#include "chrome/browser/supervised_user/child_accounts/child_account_service_factory.h"
#include "chrome/browser/ui/app_list/app_list_client_impl.h"
#include "chrome/browser/ui/startup/startup_browser_creator.h"
#include "chrome/browser/ui/webui/chromeos/login/discover/discover_manager.h"
#include "chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_pin_setup.h"
#include "chrome/common/channel_info.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/logging_chrome.h"
#include "chrome/common/pref_names.h"
#include "chromeos/assistant/buildflags.h"
#include "chromeos/chromeos_switches.h"
#include "chromeos/cryptohome/cryptohome_parameters.h"
#include "chromeos/dbus/cryptohome_client.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/session_manager_client.h"
#include "chromeos/dbus/util/tpm_util.h"
#include "chromeos/login/auth/stub_authenticator.h"
#include "chromeos/network/network_cert_loader.h"
#include "chromeos/network/portal_detector/network_portal_detector.h"
#include "chromeos/network/portal_detector/network_portal_detector_strategy.h"
#include "chromeos/settings/cros_settings_names.h"
#include "components/account_id/account_id.h"
#include "components/component_updater/component_updater_service.h"
#include "components/flags_ui/pref_service_flags_storage.h"
#include "components/language/core/browser/pref_names.h"
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
#include "components/password_manager/core/browser/password_store.h"
#include "components/policy/core/common/cloud/cloud_policy_constants.h"
#include "components/prefs/pref_member.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/quirks/quirks_manager.h"
#include "components/session_manager/core/session_manager.h"
#include "components/signin/core/browser/account_tracker_service.h"
#include "components/signin/core/browser/signin_error_controller.h"
#include "components/signin/core/browser/signin_manager_base.h"
#include "components/user_manager/known_user.h"
#include "components/user_manager/user.h"
#include "components/user_manager/user_manager.h"
#include "components/user_manager/user_names.h"
#include "components/user_manager/user_type.h"
#include "components/version_info/version_info.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/content_switches.h"
#include "extensions/common/features/feature_session_type.h"
#include "rlz/buildflags/buildflags.h"
#include "services/identity/public/cpp/identity_manager.h"
#include "third_party/cros_system_api/switches/chrome_switches.h"
#include "ui/base/ime/chromeos/input_method_descriptor.h"
#include "ui/base/ime/chromeos/input_method_manager.h"
#include "ui/base/ime/chromeos/input_method_util.h"
#include "url/gurl.h"
#if BUILDFLAG(ENABLE_RLZ)
#include "chrome/browser/rlz/chrome_rlz_tracker_delegate.h"
#include "components/rlz/rlz_tracker.h"
#endif
#if BUILDFLAG(ENABLE_CROS_ASSISTANT)
#include "chrome/browser/ui/ash/assistant/assistant_client.h"
#endif
namespace chromeos {
namespace {
// Milliseconds until we timeout our attempt to fetch flags from the child
// account service.
static const int kFlagsFetchingLoginTimeoutMs = 1000;
void InitLocaleAndInputMethodsForNewUser(
UserSessionManager* session_manager,
Profile* profile,
const std::string& public_session_locale,
const std::string& public_session_input_method) {
PrefService* prefs = profile->GetPrefs();
std::string locale;
if (!public_session_locale.empty()) {
// If this is a public session and the user chose a |public_session_locale|,
// write it to |prefs| so that the UI switches to it.
locale = public_session_locale;
prefs->SetString(language::prefs::kApplicationLocale, locale);
// Suppress the locale change dialog.
prefs->SetString(prefs::kApplicationLocaleAccepted, locale);
} else {
// Otherwise, assume that the session will use the current UI locale.
locale = g_browser_process->GetApplicationLocale();
}
// First, we'll set kLanguagePreloadEngines.
input_method::InputMethodManager* manager =
input_method::InputMethodManager::Get();
input_method::InputMethodDescriptor preferred_input_method;
if (!public_session_input_method.empty()) {
// If this is a public session and the user chose a valid
// |public_session_input_method|, use it as the |preferred_input_method|.
const input_method::InputMethodDescriptor* const descriptor =
manager->GetInputMethodUtil()->GetInputMethodDescriptorFromId(
public_session_input_method);
if (descriptor) {
preferred_input_method = *descriptor;
} else {
LOG(WARNING) << "Public session is initialized with an invalid IME"
<< ", id=" << public_session_input_method;
}
}
// If |preferred_input_method| is not set, use the currently active input
// method.
if (preferred_input_method.id().empty()) {
preferred_input_method =
session_manager->GetDefaultIMEState(profile)->GetCurrentInputMethod();
const input_method::InputMethodDescriptor* descriptor =
manager->GetInputMethodUtil()->GetInputMethodDescriptorFromId(
manager->GetInputMethodUtil()->GetHardwareInputMethodIds()[0]);
// If the hardware input method's keyboard layout is the same as the
// default input method (e.g. from GaiaScreen), use the hardware input
// method. Note that the hardware input method can be non-login-able.
// Refer to the issue chrome-os-partner:48623.
if (descriptor && descriptor->GetPreferredKeyboardLayout() ==
preferred_input_method.GetPreferredKeyboardLayout()) {
preferred_input_method = *descriptor;
}
}
// Derive kLanguagePreloadEngines from |locale| and |preferred_input_method|.
std::vector<std::string> input_method_ids;
manager->GetInputMethodUtil()->GetFirstLoginInputMethodIds(
locale, preferred_input_method, &input_method_ids);
// Save the input methods in the user's preferences.
StringPrefMember language_preload_engines;
language_preload_engines.Init(prefs::kLanguagePreloadEngines, prefs);
language_preload_engines.SetValue(base::JoinString(input_method_ids, ","));
BootTimesRecorder::Get()->AddLoginTimeMarker("IMEStarted", false);
// Second, we'll set kLanguagePreferredLanguages.
std::vector<std::string> language_codes;
// The current locale should be on the top.
language_codes.push_back(locale);
// Add input method IDs based on the input methods, as there may be
// input methods that are unrelated to the current locale. Example: the
// hardware keyboard layout xkb:us::eng is used for logging in, but the
// UI language is set to French. In this case, we should set "fr,en"
// to the preferred languages preference.
std::vector<std::string> candidates;
manager->GetInputMethodUtil()->GetLanguageCodesFromInputMethodIds(
input_method_ids, &candidates);
for (size_t i = 0; i < candidates.size(); ++i) {
const std::string& candidate = candidates[i];
// Add a candidate if it's not yet in language_codes and is allowed.
if (std::count(language_codes.begin(), language_codes.end(), candidate) ==
0 &&
locale_util::IsAllowedLanguage(candidate, prefs)) {
language_codes.push_back(candidate);
}
}
// Save the preferred languages in the user's preferences.
prefs->SetString(prefs::kLanguagePreferredLanguages,
base::JoinString(language_codes, ","));
// Indicate that we need to merge the syncable input methods when we sync,
// since we have not applied the synced prefs before.
prefs->SetBoolean(prefs::kLanguageShouldMergeInputMethods, true);
}
#if BUILDFLAG(ENABLE_RLZ)
// Flag file that disables RLZ tracking, when present.
const base::FilePath::CharType kRLZDisabledFlagName[] =
FILE_PATH_LITERAL(".rlz_disabled");
base::FilePath GetRlzDisabledFlagPath() {
base::FilePath homedir;
base::PathService::Get(base::DIR_HOME, &homedir);
return homedir.Append(kRLZDisabledFlagName);
}
#endif
// Callback to GetNSSCertDatabaseForProfile. It passes the user-specific NSS
// database to NetworkCertLoader. It must be called for primary user only.
void OnGetNSSCertDatabaseForUser(net::NSSCertDatabase* database) {
if (!NetworkCertLoader::IsInitialized())
return;
NetworkCertLoader::Get()->SetUserNSSDB(database);
}
// Returns new CommandLine with per-user flags.
base::CommandLine CreatePerSessionCommandLine(Profile* profile) {
base::CommandLine user_flags(base::CommandLine::NO_PROGRAM);
flags_ui::PrefServiceFlagsStorage flags_storage(profile->GetPrefs());
about_flags::ConvertFlagsToSwitches(&flags_storage, &user_flags,
flags_ui::kAddSentinels);
UserSessionManager::MaybeAppendPolicySwitches(profile->GetPrefs(),
&user_flags);
return user_flags;
}
// Returns true if restart is needed to apply per-session flags.
bool NeedRestartToApplyPerSessionFlags(
const base::CommandLine& user_flags,
std::set<base::CommandLine::StringType>* out_command_line_difference) {
// Don't restart browser if it is not first profile in session.
if (user_manager::UserManager::Get()->GetLoggedInUsers().size() != 1)
return false;
// Only restart if needed and if not going into managed mode.
if (user_manager::UserManager::Get()->IsLoggedInAsSupervisedUser())
return false;
auto* current_command_line = base::CommandLine::ForCurrentProcess();
if (about_flags::AreSwitchesIdenticalToCurrentCommandLine(
user_flags, *current_command_line, out_command_line_difference)) {
return false;
}
return true;
}
bool CanPerformEarlyRestart() {
if (!ChromeUserManager::Get()
->GetCurrentUserFlow()
->SupportsEarlyRestartToApplyFlags()) {
return false;
}
const ExistingUserController* controller =
ExistingUserController::current_controller();
if (!controller)
return true;
// Early restart is possible only if OAuth token is up to date.
if (controller->password_changed())
return false;
if (controller->auth_mode() != LoginPerformer::AUTH_MODE_INTERNAL)
return false;
// No early restart if Easy unlock key needs to be updated.
if (UserSessionManager::GetInstance()->NeedsToUpdateEasyUnlockKeys())
return false;
return true;
}
void LogCustomSwitches(const std::set<std::string>& switches) {
if (!VLOG_IS_ON(1))
return;
for (std::set<std::string>::const_iterator it = switches.begin();
it != switches.end(); ++it) {
VLOG(1) << "Switch leading to restart: '" << *it << "'";
}
}
// Calls the real AttemptRestart method. This is used to avoid taking a function
// pointer to chrome::AttemptRestart directly.
void CallChromeAttemptRestart() {
chrome::AttemptRestart();
}
bool IsRunningTest() {
return base::CommandLine::ForCurrentProcess()->HasSwitch(
::switches::kTestName) ||
base::CommandLine::ForCurrentProcess()->HasSwitch(
::switches::kTestType);
}
#if BUILDFLAG(ENABLE_RLZ)
UserSessionManager::RlzInitParams CollectRlzParams() {
UserSessionManager::RlzInitParams params;
params.disabled = base::PathExists(GetRlzDisabledFlagPath());
params.time_since_oobe_completion =
chromeos::StartupUtils::GetTimeSinceOobeFlagFileCreation();
return params;
}
#endif
bool IsOnlineSignin(const UserContext& user_context) {
return user_context.GetAuthFlow() == UserContext::AUTH_FLOW_GAIA_WITH_SAML ||
user_context.GetAuthFlow() == UserContext::AUTH_FLOW_GAIA_WITHOUT_SAML;
}
} // namespace
UserSessionManagerDelegate::~UserSessionManagerDelegate() {}
void UserSessionStateObserver::PendingUserSessionsRestoreFinished() {}
UserSessionStateObserver::~UserSessionStateObserver() {}
// static
UserSessionManager* UserSessionManager::GetInstance() {
return base::Singleton<UserSessionManager, base::DefaultSingletonTraits<
UserSessionManager>>::get();
}
// static
void UserSessionManager::OverrideHomedir() {
// Override user homedir, check for ProfileManager being initialized as
// it may not exist in unit tests.
if (g_browser_process->profile_manager()) {
user_manager::UserManager* user_manager = user_manager::UserManager::Get();
if (user_manager->GetLoggedInUsers().size() == 1) {
base::FilePath homedir = ProfileHelper::GetProfilePathByUserIdHash(
user_manager->GetPrimaryUser()->username_hash());
// This path has been either created by cryptohome (on real Chrome OS
// device) or by ProfileManager (on chromeos=1 desktop builds).
base::PathService::OverrideAndCreateIfNeeded(base::DIR_HOME, homedir,
true /* path is absolute */,
false /* don't create */);
}
}
}
// static
void UserSessionManager::RegisterPrefs(PrefRegistrySimple* registry) {
registry->RegisterStringPref(prefs::kRLZBrand, std::string());
registry->RegisterBooleanPref(prefs::kRLZDisabled, false);
registry->RegisterBooleanPref(prefs::kCanShowOobeGoodiesPage, true);
}
// static
void UserSessionManager::MaybeAppendPolicySwitches(
PrefService* user_profile_prefs,
base::CommandLine* user_flags) {
// Get target values for --site-per-process and --isolate-origins for the user
// session according to policy. Values from command-line flags should not be
// honored at this point, so check |IsManaged()|.
const PrefService::Preference* site_per_process_pref =
user_profile_prefs->FindPreference(prefs::kSitePerProcess);
const PrefService::Preference* isolate_origins_pref =
user_profile_prefs->FindPreference(prefs::kIsolateOrigins);
bool site_per_process = site_per_process_pref->IsManaged() &&
site_per_process_pref->GetValue()->GetBool();
std::string isolate_origins =
isolate_origins_pref->IsManaged()
? isolate_origins_pref->GetValue()->GetString()
: std::string();
// The admin should also be able to use these policies to override trials that
// will try to turn site isolation on per default.
// Note that disabling either SitePerProcess or IsolateOrigins via policy will
// disable both types of field trials.
bool disable_site_isolation_trials =
(site_per_process_pref->IsManaged() &&
!site_per_process_pref->GetValue()->GetBool()) ||
(isolate_origins_pref->IsManaged() && isolate_origins.empty());
// Append sentinels indicating that these values originate from policy.
// This is important, because only command-line switches between the
// |"--policy-switches-begin"| / |"--policy-switches-end"| and the
// |"--flag-switches-begin"| / |"--flag-switches-end"| sentinels will be
// compared when comparing the current command line and the user session
// command line in order to decide if chrome should be restarted.
// We use the policy-style sentinels because these values originate from
// policy, and because login_manager uses the same sentinels when adding the
// login-screen site isolation flags.
bool use_policy_sentinels = site_per_process || !isolate_origins.empty() ||
disable_site_isolation_trials;
if (use_policy_sentinels)
user_flags->AppendSwitch(chromeos::switches::kPolicySwitchesBegin);
// Inject site isolation and isolate origins command line switch from
// user policy.
if (site_per_process) {
user_flags->AppendSwitch(::switches::kSitePerProcess);
}
if (!isolate_origins.empty()) {
user_flags->AppendSwitchASCII(
::switches::kIsolateOrigins,
user_profile_prefs->GetString(prefs::kIsolateOrigins));
}
if (disable_site_isolation_trials) {
user_flags->AppendSwitch(::switches::kDisableSiteIsolationTrials);
}
if (use_policy_sentinels) {
user_flags->AppendSwitch(chromeos::switches::kPolicySwitchesEnd);
}
}
UserSessionManager::UserSessionManager()
: delegate_(nullptr),
authenticator_(nullptr),
has_auth_cookies_(false),
user_sessions_restored_(false),
user_sessions_restore_in_progress_(false),
session_restore_strategy_(
OAuth2LoginManager::RESTORE_FROM_SAVED_OAUTH2_REFRESH_TOKEN),
running_easy_unlock_key_ops_(false),
should_obtain_handles_(true),
should_launch_browser_(true),
waiting_for_child_account_status_(false),
attempt_restart_closure_(base::BindRepeating(&CallChromeAttemptRestart)),
weak_factory_(this) {
net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
user_manager::UserManager::Get()->AddSessionStateObserver(this);
user_manager::UserManager::Get()->AddObserver(this);
}
UserSessionManager::~UserSessionManager() {
// UserManager is destroyed before singletons, so we need to check if it
// still exists.
// TODO(nkostylev): fix order of destruction of UserManager
// / UserSessionManager objects.
if (user_manager::UserManager::IsInitialized()) {
user_manager::UserManager::Get()->RemoveSessionStateObserver(this);
user_manager::UserManager::Get()->RemoveObserver(this);
}
net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
}
void UserSessionManager::SetShouldObtainHandleInTests(
bool should_obtain_handles) {
should_obtain_handles_ = should_obtain_handles;
if (!should_obtain_handles_) {
token_handle_fetcher_.reset();
}
}
void UserSessionManager::SetAttemptRestartClosureInTests(
const base::RepeatingClosure& attempt_restart_closure) {
attempt_restart_closure_ = attempt_restart_closure;
}
void UserSessionManager::CompleteGuestSessionLogin(const GURL& start_url) {
VLOG(1) << "Completing guest session login";
// For guest session we ask session_manager to restart Chrome with --bwsi
// flag. We keep only some of the arguments of this process.
const base::CommandLine& browser_command_line =
*base::CommandLine::ForCurrentProcess();
base::CommandLine command_line(browser_command_line.GetProgram());
GetOffTheRecordCommandLine(start_url, StartupUtils::IsOobeCompleted(),
browser_command_line, &command_line);
// This makes sure that Chrome restarts with no per-session flags. The guest
// profile will always have empty set of per-session flags. If this is not
// done and device owner has some per-session flags, when Chrome is relaunched
// the guest profile session flags will not match the current command line and
// another restart will be attempted in order to reset the user flags for the
// guest user.
const base::CommandLine user_flags(base::CommandLine::NO_PROGRAM);
if (!about_flags::AreSwitchesIdenticalToCurrentCommandLine(
user_flags, *base::CommandLine::ForCurrentProcess(), NULL)) {
DBusThreadManager::Get()->GetSessionManagerClient()->SetFlagsForUser(
cryptohome::CreateAccountIdentifierFromAccountId(
user_manager::GuestAccountId()),
base::CommandLine::StringVector());
}
RestartChrome(command_line);
}
scoped_refptr<Authenticator> UserSessionManager::CreateAuthenticator(
AuthStatusConsumer* consumer) {
// Screen locker needs new Authenticator instance each time.
if (ScreenLocker::default_screen_locker()) {
if (authenticator_.get())
authenticator_->SetConsumer(NULL);
authenticator_ = NULL;
}
if (authenticator_.get() == NULL) {
if (injected_user_context_) {
authenticator_ =
new StubAuthenticator(consumer, *injected_user_context_.get());
} else {
authenticator_ = new ChromeCryptohomeAuthenticator(consumer);
}
} else {
// TODO(nkostylev): Fix this hack by improving Authenticator dependencies.
authenticator_->SetConsumer(consumer);
}
return authenticator_;
}
void UserSessionManager::StartSession(const UserContext& user_context,
StartSessionType start_session_type,
bool has_auth_cookies,
bool has_active_session,
UserSessionManagerDelegate* delegate) {
easy_unlock_key_ops_finished_ = false;
delegate_ = delegate;
start_session_type_ = start_session_type;
VLOG(1) << "Starting user session.";
PreStartSession();
CreateUserSession(user_context, has_auth_cookies);
if (!has_active_session)
StartCrosSession();
if (!user_context.GetDeviceId().empty()) {
user_manager::known_user::SetDeviceId(user_context.GetAccountId(),
user_context.GetDeviceId());
}
InitDemoSessionIfNeeded(base::BindOnce(
&UserSessionManager::UpdateArcFileSystemCompatibilityAndPrepareProfile,
AsWeakPtr()));
}
void UserSessionManager::DelegateDeleted(UserSessionManagerDelegate* delegate) {
if (delegate_ == delegate)
delegate_ = nullptr;
}
void UserSessionManager::PerformPostUserLoggedInActions() {
user_manager::UserManager* user_manager = user_manager::UserManager::Get();
if (user_manager->GetLoggedInUsers().size() == 1) {
if (network_portal_detector::IsInitialized()) {
network_portal_detector::GetInstance()->SetStrategy(
PortalDetectorStrategy::STRATEGY_ID_SESSION);
}
InitNonKioskExtensionFeaturesSessionType(user_manager->GetPrimaryUser());
}
}
void UserSessionManager::RestoreAuthenticationSession(Profile* user_profile) {
user_manager::UserManager* user_manager = user_manager::UserManager::Get();
// We need to restore session only for logged in GAIA (regular) users.
// Note: stub user is a special case that is used for tests, running
// linux_chromeos build on dev workstations w/o user_id parameters.
// Stub user is considered to be a regular GAIA user but it has special
// user_id (kStubUser) and certain services like restoring OAuth session are
// explicitly disabled for it.
if (!user_manager->IsUserLoggedIn() ||
!user_manager->IsLoggedInAsUserWithGaiaAccount() ||
user_manager->IsLoggedInAsStub()) {
return;
}
const user_manager::User* user =
ProfileHelper::Get()->GetUserByProfile(user_profile);
const SigninManagerBase* signin_manager =
SigninManagerFactory::GetForProfile(user_profile);
const bool account_id_valid =
signin_manager && !signin_manager->GetAuthenticatedAccountId().empty();
if (!account_id_valid)
LOG(ERROR) << "No account is associated with sign-in manager on restore.";
UMA_HISTOGRAM_BOOLEAN("UserSessionManager.RestoreOnCrash.AccountIdValid",
account_id_valid);
DCHECK(user);
if (!net::NetworkChangeNotifier::IsOffline()) {
pending_signin_restore_sessions_.erase(user->GetAccountId().GetUserEmail());
RestoreAuthSessionImpl(user_profile, false /* has_auth_cookies */);
} else {
// Even if we're online we should wait till initial
// OnConnectionTypeChanged() call. Otherwise starting fetchers too early may
// end up canceling all request when initial network connection type is
// processed. See http://crbug.com/121643.
pending_signin_restore_sessions_.insert(
user->GetAccountId().GetUserEmail());
}
}
void UserSessionManager::RestoreActiveSessions() {
user_sessions_restore_in_progress_ = true;
DBusThreadManager::Get()->GetSessionManagerClient()->RetrieveActiveSessions(
base::BindOnce(&UserSessionManager::OnRestoreActiveSessions,
AsWeakPtr()));
}
bool UserSessionManager::UserSessionsRestored() const {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
return user_sessions_restored_;
}
bool UserSessionManager::UserSessionsRestoreInProgress() const {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
return user_sessions_restore_in_progress_;
}
void UserSessionManager::InitRlz(Profile* profile) {
#if BUILDFLAG(ENABLE_RLZ)
if (!g_browser_process->local_state()->HasPrefPath(prefs::kRLZBrand)) {
// Read brand code asynchronously from an OEM data and repost ourselves.
google_brand::chromeos::InitBrand(
base::Bind(&UserSessionManager::InitRlz, AsWeakPtr(), profile));
return;
}
base::PostTaskWithTraitsAndReplyWithResult(
FROM_HERE,
{base::MayBlock(), base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
base::Bind(&CollectRlzParams),
base::Bind(&UserSessionManager::InitRlzImpl, AsWeakPtr(), profile));
#endif
}
void UserSessionManager::InitNonKioskExtensionFeaturesSessionType(
const user_manager::User* user) {
// Kiosk session should be set as part of kiosk user session initialization
// in normal circumstances (to be able to properly determine whether kiosk
// was auto-launched); in case of user session restore, feature session
// type has be set before kiosk app controller takes over, as at that point
// kiosk app profile would already be initialized - feature session type
// should be set before that.
if (user->GetType() == user_manager::USER_TYPE_KIOSK_APP) {
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kLoginUser)) {
// For kiosk session crash recovery, feature session type has be set
// before kiosk app controller takes over, as at that point iosk app
// profile would already be initialized - feature session type
// should be set before that.
bool auto_launched = base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kAppAutoLaunched);
extensions::SetCurrentFeatureSessionType(
auto_launched ? extensions::FeatureSessionType::AUTOLAUNCHED_KIOSK
: extensions::FeatureSessionType::KIOSK);
}
return;
}
extensions::SetCurrentFeatureSessionType(
user->HasGaiaAccount() ? extensions::FeatureSessionType::REGULAR
: extensions::FeatureSessionType::UNKNOWN);
}
void UserSessionManager::SetFirstLoginPrefs(
Profile* profile,
const std::string& public_session_locale,
const std::string& public_session_input_method) {
VLOG(1) << "Setting first login prefs";
InitLocaleAndInputMethodsForNewUser(this, profile, public_session_locale,
public_session_input_method);
// Since there is no images after first login, set the parameter to true to
// avoid camera media migration.
profile->GetPrefs()->SetBoolean(prefs::kCameraMediaConsolidated, true);
}
bool UserSessionManager::GetAppModeChromeClientOAuthInfo(
std::string* chrome_client_id,
std::string* chrome_client_secret) {
if (!chrome::IsRunningInForcedAppMode() || chrome_client_id_.empty() ||
chrome_client_secret_.empty()) {
return false;
}
*chrome_client_id = chrome_client_id_;
*chrome_client_secret = chrome_client_secret_;
return true;
}
void UserSessionManager::SetAppModeChromeClientOAuthInfo(
const std::string& chrome_client_id,
const std::string& chrome_client_secret) {
if (!chrome::IsRunningInForcedAppMode())
return;
chrome_client_id_ = chrome_client_id;
chrome_client_secret_ = chrome_client_secret;
}
void UserSessionManager::DoBrowserLaunch(Profile* profile,
LoginDisplayHost* login_host) {
session_manager::SessionManager::Get()->SetSessionState(
session_manager::SessionState::LOGGED_IN_NOT_ACTIVE);
ui_shown_time_ = base::Time::Now();
DoBrowserLaunchInternal(profile, login_host, false /* locale_pref_checked */);
}
bool UserSessionManager::RespectLocalePreference(
Profile* profile,
const user_manager::User* user,
const locale_util::SwitchLanguageCallback& callback) const {
// TODO(alemate): http://crbug.com/288941 : Respect preferred language list in
// the Google user profile.
if (g_browser_process == NULL)
return false;
user_manager::UserManager* user_manager = user_manager::UserManager::Get();
if (!user || (user_manager->IsUserLoggedIn() &&
user != user_manager->GetPrimaryUser())) {
return false;
}
// In case of multi-profiles session we don't apply profile locale
// because it is unsafe.
if (user_manager->GetLoggedInUsers().size() != 1)
return false;
PrefService* prefs = profile->GetPrefs();
if (prefs == NULL)
return false;
std::string pref_locale;
const std::string pref_app_locale =
prefs->GetString(language::prefs::kApplicationLocale);
const std::string pref_bkup_locale =
prefs->GetString(prefs::kApplicationLocaleBackup);
pref_locale = pref_app_locale;
if (pref_locale.empty())
pref_locale = pref_bkup_locale;
const std::string* account_locale = NULL;
if (pref_locale.empty() && user->has_gaia_account() &&
prefs->GetList(prefs::kAllowedLanguages)->GetList().empty()) {
if (user->GetAccountLocale() == NULL)
return false; // wait until Account profile is loaded.
account_locale = user->GetAccountLocale();
pref_locale = *account_locale;
}
const std::string global_app_locale =
g_browser_process->GetApplicationLocale();
if (pref_locale.empty())
pref_locale = global_app_locale;
DCHECK(!pref_locale.empty());
VLOG(1) << "RespectLocalePreference: "
<< "app_locale='" << pref_app_locale << "', "
<< "bkup_locale='" << pref_bkup_locale << "', "
<< (account_locale != NULL
? (std::string("account_locale='") + (*account_locale) +
"'. ")
: (std::string("account_locale - unused. ")))
<< " Selected '" << pref_locale << "'";
Profile::AppLocaleChangedVia app_locale_changed_via =
user->GetType() == user_manager::USER_TYPE_PUBLIC_ACCOUNT
? Profile::APP_LOCALE_CHANGED_VIA_PUBLIC_SESSION_LOGIN
: Profile::APP_LOCALE_CHANGED_VIA_LOGIN;
// check if pref_locale is allowed by policy (AllowedLanguages)
if (!chromeos::locale_util::IsAllowedUILanguage(pref_locale, prefs)) {
pref_locale = chromeos::locale_util::GetAllowedFallbackUILanguage(prefs);
app_locale_changed_via = Profile::APP_LOCALE_CHANGED_VIA_POLICY;
}
profile->ChangeAppLocale(pref_locale, app_locale_changed_via);
// Here we don't enable keyboard layouts for normal users. Input methods
// are set up when the user first logs in. Then the user may customize the
// input methods. Hence changing input methods here, just because the user's
// UI language is different from the login screen UI language, is not
// desirable. Note that input method preferences are synced, so users can use
// their farovite input methods as soon as the preferences are synced.
//
// For Guest mode, user locale preferences will never get initialized.
// So input methods should be enabled somewhere.
const bool enable_layouts =
user_manager::UserManager::Get()->IsLoggedInAsGuest();
locale_util::SwitchLanguage(pref_locale, enable_layouts,
false /* login_layouts_only */, callback,
profile);
return true;
}
bool UserSessionManager::RestartToApplyPerSessionFlagsIfNeed(
Profile* profile,
bool early_restart) {
SessionManagerClient* session_manager_client =
DBusThreadManager::Get()->GetSessionManagerClient();
if (!session_manager_client->SupportsRestartToApplyUserFlags())
return false;
if (ProfileHelper::IsSigninProfile(profile) ||
ProfileHelper::IsLockScreenAppProfile(profile)) {
return false;
}
// Kiosk sessions keeps the startup flags.
if (user_manager::UserManager::Get() &&
user_manager::UserManager::Get()->IsLoggedInAsKioskApp()) {
return false;
}
if (early_restart && !CanPerformEarlyRestart())
return false;
// We can't really restart if we've already restarted as a part of
// user session restore after crash of in case when flags were changed inside
// user session.
if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kLoginUser))
return false;
// We can't restart if that's a second user sign in that is happening.
if (user_manager::UserManager::Get()->GetLoggedInUsers().size() > 1)
return false;
const base::CommandLine user_flags(CreatePerSessionCommandLine(profile));
std::set<base::CommandLine::StringType> command_line_difference;
if (!NeedRestartToApplyPerSessionFlags(user_flags, &command_line_difference))
return false;
LogCustomSwitches(command_line_difference);
about_flags::ReportAboutFlagsHistogram(
"Login.CustomFlags", command_line_difference, std::set<std::string>());
base::CommandLine::StringVector flags;
// argv[0] is the program name |base::CommandLine::NO_PROGRAM|.
flags.assign(user_flags.argv().begin() + 1, user_flags.argv().end());
LOG(WARNING) << "Restarting to apply per-session flags...";
SetSwitchesForUser(
user_manager::UserManager::Get()->GetActiveUser()->GetAccountId(),
CommandLineSwitchesType::kPolicyAndFlagsAndKioskControl, flags);
attempt_restart_closure_.Run();
return true;
}
bool UserSessionManager::NeedsToUpdateEasyUnlockKeys() const {
return user_context_.GetAccountId().is_valid() &&
user_manager::User::TypeHasGaiaAccount(user_context_.GetUserType()) &&
user_context_.GetKey() && !user_context_.GetKey()->GetSecret().empty();
}
void UserSessionManager::AddSessionStateObserver(
chromeos::UserSessionStateObserver* observer) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
session_state_observer_list_.AddObserver(observer);
}
void UserSessionManager::RemoveSessionStateObserver(
chromeos::UserSessionStateObserver* observer) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
session_state_observer_list_.RemoveObserver(observer);
}
void UserSessionManager::OnSessionRestoreStateChanged(
Profile* user_profile,
OAuth2LoginManager::SessionRestoreState state) {
user_manager::User::OAuthTokenStatus user_status =
user_manager::User::OAUTH_TOKEN_STATUS_UNKNOWN;
OAuth2LoginManager* login_manager =
OAuth2LoginManagerFactory::GetInstance()->GetForProfile(user_profile);
bool connection_error = false;
switch (state) {
case OAuth2LoginManager::SESSION_RESTORE_DONE:
// Session restore done does not always mean valid token because the
// merge session operation could be skipped when the first account in
// Gaia cookies matches the primary account in TokenService. However
// the token could still be invalid in some edge cases. See
// http://crbug.com/760610
user_status =
SigninErrorControllerFactory::GetForProfile(user_profile)->HasError()
? user_manager::User::OAUTH2_TOKEN_STATUS_INVALID
: user_manager::User::OAUTH2_TOKEN_STATUS_VALID;
break;
case OAuth2LoginManager::SESSION_RESTORE_FAILED:
user_status = user_manager::User::OAUTH2_TOKEN_STATUS_INVALID;
break;
case OAuth2LoginManager::SESSION_RESTORE_CONNECTION_FAILED:
connection_error = true;
break;
case OAuth2LoginManager::SESSION_RESTORE_NOT_STARTED:
case OAuth2LoginManager::SESSION_RESTORE_PREPARING:
case OAuth2LoginManager::SESSION_RESTORE_IN_PROGRESS:
return;
}
// We should not be clearing existing token state if that was a connection
// error. http://crbug.com/295245
if (!connection_error) {
// We are in one of "done" states here.
user_manager::UserManager::Get()->SaveUserOAuthStatus(
user_manager::UserManager::Get()->GetActiveUser()->GetAccountId(),
user_status);
}
login_manager->RemoveObserver(this);
// Terminate user session if merge session fails for an online sign-in.
// Otherwise, auth token dependent code would be in an invalid state.
// Important piece such as policy code might be broken because of this and
// subject to an exploit. See http://crbug.com/677312.
if (IsOnlineSignin(user_context_) &&
state == OAuth2LoginManager::SESSION_RESTORE_FAILED) {
LOG(ERROR)
<< "Session restore failed for online sign-in, terminating session.";
chrome::AttemptUserExit();
return;
}
// Schedule another flush after session restore for non-ephemeral profile
// if not restarting.
if (!ProfileHelper::IsEphemeralUserProfile(user_profile))
ProfileHelper::Get()->FlushProfile(user_profile);
}
void UserSessionManager::OnNetworkChanged(
net::NetworkChangeNotifier::ConnectionType type) {
user_manager::UserManager* user_manager = user_manager::UserManager::Get();
if (type == net::NetworkChangeNotifier::CONNECTION_NONE ||
!user_manager->IsUserLoggedIn() ||
!user_manager->IsLoggedInAsUserWithGaiaAccount() ||
user_manager->IsLoggedInAsStub() || IsRunningTest()) {
return;
}
// Need to iterate over all users and their OAuth2 session state.
const user_manager::UserList& users = user_manager->GetLoggedInUsers();
for (user_manager::UserList::const_iterator it = users.begin();
it != users.end(); ++it) {
if (!(*it)->is_profile_created())
continue;
Profile* user_profile = ProfileHelper::Get()->GetProfileByUserUnsafe(*it);
bool should_restore_session = pending_signin_restore_sessions_.find(
(*it)->GetAccountId().GetUserEmail()) !=
pending_signin_restore_sessions_.end();
OAuth2LoginManager* login_manager =
OAuth2LoginManagerFactory::GetInstance()->GetForProfile(user_profile);
if (login_manager->SessionRestoreIsRunning()) {
// If we come online for the first time after successful offline login,
// we need to kick off OAuth token verification process again.
login_manager->ContinueSessionRestore();
} else if (should_restore_session) {
pending_signin_restore_sessions_.erase(
(*it)->GetAccountId().GetUserEmail());
RestoreAuthSessionImpl(user_profile, false /* has_auth_cookies */);
}
}
}
void UserSessionManager::OnProfilePrepared(Profile* profile,
bool browser_launched) {
if (!IsRunningTest()) {
// Did not log in (we crashed or are debugging), need to restore Sync.
// TODO(nkostylev): Make sure that OAuth state is restored correctly for all
// users once it is fully multi-profile aware. http://crbug.com/238987
// For now if we have other user pending sessions they'll override OAuth
// session restore for previous users.
RestoreAuthenticationSession(profile);
}
// Restore other user sessions if any.
RestorePendingUserSessions();
}
void UserSessionManager::OnUsersSignInConstraintsChanged() {
const user_manager::UserManager* user_manager =
user_manager::UserManager::Get();
const user_manager::UserList& logged_in_users =
user_manager->GetLoggedInUsers();
for (auto* user : logged_in_users) {
if (user->GetType() != user_manager::USER_TYPE_REGULAR &&
user->GetType() != user_manager::USER_TYPE_GUEST &&
user->GetType() != user_manager::USER_TYPE_SUPERVISED &&
user->GetType() != user_manager::USER_TYPE_CHILD) {
continue;
}
if (!user_manager->IsUserAllowed(*user)) {
LOG(ERROR) << "The current user is not allowed, terminating the session.";
chrome::AttemptUserExit();
}
}
}
void UserSessionManager::ChildAccountStatusReceivedCallback(Profile* profile) {
StopChildStatusObserving(profile);
}
void UserSessionManager::StopChildStatusObserving(Profile* profile) {
if (waiting_for_child_account_status_ &&
!SessionStartupPref::TypeIsManaged(profile->GetPrefs())) {
InitializeStartUrls();
}
waiting_for_child_account_status_ = false;
}
void UserSessionManager::CreateUserSession(const UserContext& user_context,
bool has_auth_cookies) {
user_context_ = user_context;
has_auth_cookies_ = has_auth_cookies;
InitSessionRestoreStrategy();
StoreUserContextDataBeforeProfileIsCreated();
session_manager::SessionManager::Get()->CreateSession(
user_context_.GetAccountId(), user_context_.GetUserIDHash(),
user_context.GetUserType() == user_manager::USER_TYPE_CHILD);
}
void UserSessionManager::PreStartSession() {
// Switch log file as soon as possible.
logging::RedirectChromeLogging(*base::CommandLine::ForCurrentProcess());
}
void UserSessionManager::StoreUserContextDataBeforeProfileIsCreated() {
user_manager::known_user::UpdateId(user_context_.GetAccountId());
}
void UserSessionManager::StartCrosSession() {
BootTimesRecorder* btl = BootTimesRecorder::Get();
btl->AddLoginTimeMarker("StartSession-Start", false);
DBusThreadManager::Get()->GetSessionManagerClient()->StartSession(
cryptohome::CreateAccountIdentifierFromAccountId(
user_context_.GetAccountId()));
btl->AddLoginTimeMarker("StartSession-End", false);
}
void UserSessionManager::OnUserNetworkPolicyParsed(bool send_password) {
if (send_password) {
if (user_context_.GetPasswordKey()->GetSecret().size() > 0) {
DBusThreadManager::Get()->GetSessionManagerClient()->SaveLoginPassword(
user_context_.GetPasswordKey()->GetSecret());
} else {
LOG(WARNING) << "Not saving password because password is empty.";
}
}
user_context_.GetMutablePasswordKey()->ClearSecret();
}
void UserSessionManager::InitDemoSessionIfNeeded(base::OnceClosure callback) {
chromeos::DemoSession* demo_session =
chromeos::DemoSession::StartIfInDemoMode();
if (!demo_session || !demo_session->started()) {
std::move(callback).Run();
return;
}
should_launch_browser_ = false;
demo_session->EnsureOfflineResourcesLoaded(std::move(callback));
}
void UserSessionManager::UpdateArcFileSystemCompatibilityAndPrepareProfile() {
arc::UpdateArcFileSystemCompatibilityPrefIfNeeded(
user_context_.GetAccountId(),
ProfileHelper::GetProfilePathByUserIdHash(user_context_.GetUserIDHash()),
base::BindOnce(&UserSessionManager::PrepareProfile, AsWeakPtr()));
}
void UserSessionManager::PrepareProfile() {
const bool is_demo_session =
DemoAppLauncher::IsDemoAppSession(user_context_.GetAccountId());
// TODO(nkostylev): Figure out whether demo session is using the right profile
// path or not. See https://codereview.chromium.org/171423009
g_browser_process->profile_manager()->CreateProfileAsync(
ProfileHelper::GetProfilePathByUserIdHash(user_context_.GetUserIDHash()),
base::Bind(&UserSessionManager::OnProfileCreated, AsWeakPtr(),
user_context_, is_demo_session),
base::string16(), std::string());
}
void UserSessionManager::OnProfileCreated(const UserContext& user_context,
bool is_incognito_profile,
Profile* profile,
Profile::CreateStatus status) {
switch (status) {
case Profile::CREATE_STATUS_CREATED:
CHECK(profile);
// Profile created but before initializing extensions and promo resources.
InitProfilePreferences(profile, user_context);
break;
case Profile::CREATE_STATUS_INITIALIZED:
CHECK(profile);
// Profile is created, extensions and promo resources are initialized.
// At this point all other Chrome OS services will be notified that it is
// safe to use this profile.
UserProfileInitialized(profile, is_incognito_profile,
user_context.GetAccountId());
break;
case Profile::CREATE_STATUS_LOCAL_FAIL:
case Profile::CREATE_STATUS_REMOTE_FAIL:
case Profile::CREATE_STATUS_CANCELED:
case Profile::MAX_CREATE_STATUS:
NOTREACHED();
break;
}
}
void UserSessionManager::InitProfilePreferences(
Profile* profile,
const UserContext& user_context) {
const user_manager::User* user =
ProfileHelper::Get()->GetUserByProfile(profile);
if (user->GetType() == user_manager::USER_TYPE_KIOSK_APP &&
profile->IsNewProfile()) {
ChromeUserManager::Get()->SetIsCurrentUserNew(true);
}
if (user->is_active()) {
input_method::InputMethodManager* manager =
input_method::InputMethodManager::Get();
manager->SetState(GetDefaultIMEState(profile));
}
user_manager::UserManager* user_manager = user_manager::UserManager::Get();
// Set initial prefs if the user is new, or if the user was already present on
// the device and the profile was re-created. This can happen e.g. in ext4
// migration in wipe mode.
if (user_manager->IsCurrentUserNew() || profile->IsNewProfile()) {
SetFirstLoginPrefs(profile, user_context.GetPublicSessionLocale(),
user_context.GetPublicSessionInputMethod());
if (user_manager->GetPrimaryUser() == user &&
user->GetType() == user_manager::USER_TYPE_REGULAR &&
!user_manager->IsUserNonCryptohomeDataEphemeral(user->GetAccountId())) {
chromeos::DiscoverManager::Get()
->GetModule<chromeos::DiscoverModulePinSetup>()
->SetPrimaryUserPassword(user_context.GetPasswordKey()->GetSecret());
}
}
if (user_manager->IsLoggedInAsSupervisedUser()) {
user_manager::User* active_user = user_manager->GetActiveUser();
std::string supervised_user_sync_id =
ChromeUserManager::Get()->GetSupervisedUserManager()->GetUserSyncId(
active_user->GetAccountId().GetUserEmail());
profile->GetPrefs()->SetString(prefs::kSupervisedUserId,
supervised_user_sync_id);
} else if (user_manager->IsLoggedInAsUserWithGaiaAccount()) {
// Get the Gaia ID from the user context. If it's not available, this may
// not be available when unlocking a previously opened profile, or when
// creating a supervised users. However, in these cases the gaia_id should
// be already available in the account tracker.
std::string gaia_id = user_context.GetGaiaID();
if (gaia_id.empty()) {
AccountTrackerService* account_tracker =
AccountTrackerServiceFactory::GetForProfile(profile);
const AccountInfo info = account_tracker->FindAccountInfoByEmail(
user_context.GetAccountId().GetUserEmail());
gaia_id = info.gaia;
// Use a fake gaia id for tests that do not have it.
if (IsRunningTest() && gaia_id.empty())
gaia_id = "fake_gaia_id_" + user_context.GetAccountId().GetUserEmail();
DCHECK(!gaia_id.empty());
}
// Make sure that the google service username is properly set (we do this
// on every sign in, not just the first login, to deal with existing
// profiles that might not have it set yet).
// TODO(https://crbug.com/814787): Change this flow to go through a
// mainstream Identity Service API once that API exists. Note that this
// might require supplying a valid refresh token here as opposed to an
// empty string.
identity::IdentityManager* identity_manager =
IdentityManagerFactory::GetForProfile(profile);
identity_manager->SetPrimaryAccountSynchronously(
gaia_id, user_context.GetAccountId().GetUserEmail(),
/*refresh_token=*/std::string());
std::string account_id = identity_manager->GetPrimaryAccountId();
const user_manager::User* user =
user_manager->FindUser(user_context.GetAccountId());
bool is_child = user->GetType() == user_manager::USER_TYPE_CHILD;
DCHECK(is_child ==
(user_context.GetUserType() == user_manager::USER_TYPE_CHILD));
AccountTrackerService* account_tracker =
AccountTrackerServiceFactory::GetForProfile(profile);
account_tracker->SetIsChildAccount(account_id, is_child);
VLOG(1) << "Seed IdentityManager and SigninManagerBase with the "
<< "authenticated account info, success="
<< SigninManagerFactory::GetForProfile(profile)->IsAuthenticated();
if (IsOnlineSignin(user_context)) {
account_tracker->SetIsAdvancedProtectionAccount(
account_id, user_context.IsUnderAdvancedProtection());
}
// Backfill GAIA ID in user prefs stored in Local State.
std::string tmp_gaia_id;
if (!user_manager::known_user::FindGaiaID(user_context.GetAccountId(),
&tmp_gaia_id) &&
!gaia_id.empty()) {
user_manager::known_user::UpdateGaiaID(user_context.GetAccountId(),
gaia_id);
}
}
}
void UserSessionManager::UserProfileInitialized(Profile* profile,
bool is_incognito_profile,
const AccountId& account_id) {
// Demo user signed in.
if (is_incognito_profile) {
profile->OnLogin();
// Send the notification before creating the browser so additional objects
// that need the profile (e.g. the launcher) can be created first.
content::NotificationService::current()->Notify(
chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED,
content::NotificationService::AllSources(),
content::Details<Profile>(profile));
if (delegate_)
delegate_->OnProfilePrepared(profile, false);
return;
}
BootTimesRecorder* btl = BootTimesRecorder::Get();
btl->AddLoginTimeMarker("UserProfileGotten", false);
// Associates AppListClient with the current active profile.
// Make sure AppListClient is active when AppListSyncableService builds model
// to avoid oem folder being created with invalid position. Note we should put
// this call before OAuth check in case of gaia sign in.
AppListClientImpl::GetInstance()->UpdateProfile();
if (user_context_.IsUsingOAuth()) {
// Retrieve the policy that indicates whether to continue copying
// authentication cookies set by a SAML IdP on subsequent logins after the
// first.
bool transfer_saml_auth_cookies_on_subsequent_login = false;
if (has_auth_cookies_) {
const user_manager::User* user =
user_manager::UserManager::Get()->FindUser(account_id);
if (user->IsAffiliated()) {
CrosSettings::Get()->GetBoolean(
kAccountsPrefTransferSAMLCookies,
&transfer_saml_auth_cookies_on_subsequent_login);
}
}
// Transfers authentication-related data from the profile that was used for
// authentication to the user's profile. The proxy authentication state is
// transferred unconditionally. If the user authenticated via an auth
// extension, authentication cookies and channel IDs will be transferred as
// well when the user's cookie jar is empty. If the cookie jar is not empty,
// the authentication states in the browser context and the user's profile
// must be merged using /MergeSession instead. Authentication cookies set by
// a SAML IdP will also be transferred when the user's cookie jar is not
// empty if |transfer_saml_auth_cookies_on_subsequent_login| is true.
const bool transfer_auth_cookies_and_channel_ids_on_first_login =
has_auth_cookies_;
content::StoragePartition* signin_partition = login::GetSigninPartition();
// Authentication request context may be missing especially if user didn't
// sign in using GAIA (webview) and webview didn't yet initialize.
if (signin_partition) {
ProfileAuthData::Transfer(
signin_partition,
content::BrowserContext::GetDefaultStoragePartition(profile),
transfer_auth_cookies_and_channel_ids_on_first_login,
transfer_saml_auth_cookies_on_subsequent_login,
base::Bind(
&UserSessionManager::CompleteProfileCreateAfterAuthTransfer,
AsWeakPtr(), profile));
} else {
// We need to post task so that OnProfileCreated() caller sends out
// NOTIFICATION_PROFILE_CREATED which marks user profile as initialized.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(
&UserSessionManager::CompleteProfileCreateAfterAuthTransfer,
AsWeakPtr(), profile));
}
return;
}
if (user_context_.GetAuthFlow() == UserContext::AUTH_FLOW_ACTIVE_DIRECTORY) {
// Call FinalizePrepareProfile directly and skip RestoreAuthSessionImpl
// because there is no need to merge session for Active Directory users.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&UserSessionManager::PrepareTpmDeviceAndFinalizeProfile,
AsWeakPtr(), profile));
return;
}
PrepareTpmDeviceAndFinalizeProfile(profile);
}
void UserSessionManager::CompleteProfileCreateAfterAuthTransfer(
Profile* profile) {
RestoreAuthSessionImpl(profile, has_auth_cookies_);
PrepareTpmDeviceAndFinalizeProfile(profile);
}
void UserSessionManager::PrepareTpmDeviceAndFinalizeProfile(Profile* profile) {
BootTimesRecorder::Get()->AddLoginTimeMarker("TPMOwn-Start", false);
if (!tpm_util::TpmIsEnabled() || tpm_util::TpmIsBeingOwned()) {
FinalizePrepareProfile(profile);
return;
}
// Make sure TPM ownership gets established and the owner password cleared
// (if no longer needed) whenever a user logs in. This is so the TPM is in
// locked down state after initial setup, which ensures that some decisions
// (e.g. NVRAM spaces) are unchangeable until next hardware reset (powerwash,
// recovery, etc.).
//
// Ownership is normally taken when showing the EULA screen, but in case
// this gets interrupted TPM ownership might not be established yet. The code
// here runs on every login and ensures that the TPM gets into the desired
// state eventually.
auto callback =
base::BindOnce(&UserSessionManager::OnCryptohomeOperationCompleted,
AsWeakPtr(), profile);
CryptohomeClient* client = DBusThreadManager::Get()->GetCryptohomeClient();
if (tpm_util::TpmIsOwned())
client->TpmClearStoredPassword(std::move(callback));
else
client->TpmCanAttemptOwnership(std::move(callback));
}
void UserSessionManager::OnCryptohomeOperationCompleted(Profile* profile,
bool result) {
DCHECK(result);
FinalizePrepareProfile(profile);
}
void UserSessionManager::FinalizePrepareProfile(Profile* profile) {
BootTimesRecorder::Get()->AddLoginTimeMarker("TPMOwn-End", false);
user_manager::UserManager* user_manager = user_manager::UserManager::Get();
if (user_manager->IsLoggedInAsUserWithGaiaAccount()) {
if (user_context_.GetAuthFlow() == UserContext::AUTH_FLOW_GAIA_WITH_SAML)
user_manager::known_user::UpdateUsingSAML(user_context_.GetAccountId(),
true);
SAMLOfflineSigninLimiter* saml_offline_signin_limiter =
SAMLOfflineSigninLimiterFactory::GetForProfile(profile);
if (saml_offline_signin_limiter)
saml_offline_signin_limiter->SignedIn(user_context_.GetAuthFlow());
}
profile->OnLogin();
// Send the notification before creating the browser so additional objects
// that need the profile (e.g. the launcher) can be created first.
content::NotificationService::current()->Notify(
chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED,
content::NotificationService::AllSources(),
content::Details<Profile>(profile));
// Initialize various services only for primary user.
const user_manager::User* user =
ProfileHelper::Get()->GetUserByProfile(profile);
if (user_manager->GetPrimaryUser() == user) {
InitRlz(profile);
InitializeCerts(profile);
InitializeCRLSetFetcher(user);
InitializeCertificateTransparencyComponents(user);
if (lock_screen_apps::StateController::IsEnabled())
lock_screen_apps::StateController::Get()->SetPrimaryProfile(profile);
if (user->GetType() == user_manager::USER_TYPE_REGULAR) {
// App install logs are uploaded via the user's communication channel with
// the management server. This channel exists for regular users only.
// The |AppInstallEventLogManagerWrapper| manages its own lifetime and
// self-destructs on logout.
policy::AppInstallEventLogManagerWrapper::CreateForProfile(profile);
}
arc::ArcServiceLauncher::Get()->OnPrimaryUserProfilePrepared(profile);
crostini::CrostiniManager* crostini_manager =
crostini::CrostiniManager::GetForProfile(profile);
if (crostini_manager)
crostini_manager->MaybeUpgradeCrostini();
TetherService* tether_service = TetherService::Get(profile);
if (tether_service)
tether_service->StartTetherIfPossible();
if (user->GetType() == user_manager::USER_TYPE_CHILD) {
ScreenTimeControllerFactory::GetForBrowserContext(profile);
ConsumerStatusReportingServiceFactory::GetForBrowserContext(profile);
}
// PrefService is ready, check whether we need to force a VPN connection.
always_on_vpn_manager_ =
std::make_unique<arc::AlwaysOnVpnManager>(profile->GetPrefs());
}
UpdateEasyUnlockKeys(user_context_);
quick_unlock::PinBackend::GetInstance()->MigrateToCryptohome(
profile, *user_context_.GetKey());
// Save sync password hash and salt to profile prefs if they are available.
// These will be used to detect Gaia password reuses.
if (user_context_.GetSyncPasswordData().has_value()) {
login::SaveSyncPasswordDataToProfile(user_context_, profile);
}
user_context_.ClearSecrets();
if (TokenHandlesEnabled()) {
CreateTokenUtilIfMissing();
if (token_handle_util_->ShouldObtainHandle(user->GetAccountId())) {
if (!token_handle_fetcher_.get()) {
token_handle_fetcher_.reset(new TokenHandleFetcher(
token_handle_util_.get(), user->GetAccountId()));
token_handle_fetcher_->BackfillToken(
profile, base::Bind(&UserSessionManager::OnTokenHandleObtained,
weak_factory_.GetWeakPtr()));
}
}
}
// Now that profile is ready, proceed to either alternative login flows or
// launch browser.
bool browser_launched = InitializeUserSession(profile);
// Only allow Quirks downloads after login is finished.
quirks::QuirksManager::Get()->OnLoginCompleted();
// If needed, create browser observer to display first run OOBE Goodies page.
first_run::GoodiesDisplayer::Init();
// Schedule a flush if profile is not ephemeral.
if (!ProfileHelper::IsEphemeralUserProfile(profile))
ProfileHelper::Get()->FlushProfile(profile);
// TODO(nkostylev): This pointer should probably never be NULL, but it looks
// like OnProfileCreated() may be getting called before
// UserSessionManager::PrepareProfile() has set |delegate_| when Chrome is
// killed during shutdown in tests -- see http://crosbug.com/18269. Replace
// this 'if' statement with a CHECK(delegate_) once the underlying issue is
// resolved.
if (delegate_)
delegate_->OnProfilePrepared(profile, browser_launched);
}
void UserSessionManager::ActivateWizard(OobeScreen screen) {
LoginDisplayHost* host = LoginDisplayHost::default_host();
CHECK(host);
host->StartWizard(screen);
}
void UserSessionManager::InitializeStartUrls() const {
// Child account status should be known by the time of this call.
std::vector<std::string> start_urls;
user_manager::UserManager* user_manager = user_manager::UserManager::Get();
bool can_show_getstarted_guide = user_manager->GetActiveUser()->GetType() ==
user_manager::USER_TYPE_REGULAR;
// Only show getting started guide for a new user.
const bool should_show_getstarted_guide = user_manager->IsCurrentUserNew();
if (can_show_getstarted_guide && should_show_getstarted_guide) {
// Don't open default Chrome window if we're going to launch the first-run
// app. Because we don't want the first-run app to be hidden in the
// background.
base::CommandLine::ForCurrentProcess()->AppendSwitch(
::switches::kSilentLaunch);
first_run::MaybeLaunchDialogAfterSessionStart();
} else {
for (size_t i = 0; i < start_urls.size(); ++i) {
base::CommandLine::ForCurrentProcess()->AppendArg(start_urls[i]);
}
}
}
bool UserSessionManager::InitializeUserSession(Profile* profile) {
ChildAccountService* child_service =
ChildAccountServiceFactory::GetForProfile(profile);
child_service->AddChildStatusReceivedCallback(
base::BindOnce(&UserSessionManager::ChildAccountStatusReceivedCallback,
weak_factory_.GetWeakPtr(), profile));
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&UserSessionManager::StopChildStatusObserving,
weak_factory_.GetWeakPtr(), profile),
base::TimeDelta::FromMilliseconds(kFlagsFetchingLoginTimeoutMs));
user_manager::UserManager* user_manager = user_manager::UserManager::Get();
// Kiosk apps has their own session initialization pipeline.
if (user_manager->IsLoggedInAsKioskApp() ||
user_manager->IsLoggedInAsArcKioskApp()) {
return false;
}
ProfileHelper::Get()->ProfileStartup(profile);
if (start_session_type_ == PRIMARY_USER_SESSION) {
#if BUILDFLAG(ENABLE_CROS_ASSISTANT)
// Initialize Assistant early to be used in post login Oobe steps.
if (chromeos::switches::IsAssistantEnabled()) {
AssistantClient::Get()->MaybeInit(
content::BrowserContext::GetConnectorFor(profile));
}
#endif
UserFlow* user_flow = ChromeUserManager::Get()->GetCurrentUserFlow();
WizardController* oobe_controller = WizardController::default_controller();
base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
bool skip_post_login_screens =
user_flow->ShouldSkipPostLoginScreens() ||
(oobe_controller && oobe_controller->skip_post_login_screens()) ||
cmdline->HasSwitch(chromeos::switches::kOobeSkipPostLogin);
if (user_manager->IsCurrentUserNew() && !skip_post_login_screens) {
// Don't specify start URLs if the administrator has configured the start
// URLs via policy.
if (!SessionStartupPref::TypeIsManaged(profile->GetPrefs())) {
if (child_service->IsChildAccountStatusKnown())
InitializeStartUrls();
else
waiting_for_child_account_status_ = true;
}
// Mark the device as registered., i.e. the second part of OOBE as
// completed.
if (!StartupUtils::IsDeviceRegistered())
StartupUtils::MarkDeviceRegistered(base::Closure());
ActivateWizard(OobeScreen::SCREEN_TERMS_OF_SERVICE);
return false;
}
}
DoBrowserLaunch(profile, LoginDisplayHost::default_host());
return true;
}
void UserSessionManager::InitSessionRestoreStrategy() {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
bool in_app_mode = chrome::IsRunningInForcedAppMode();
// Are we in kiosk app mode?
if (in_app_mode) {
if (command_line->HasSwitch(::switches::kAppModeOAuth2Token)) {
user_context_.SetRefreshToken(
command_line->GetSwitchValueASCII(::switches::kAppModeOAuth2Token));
}
if (command_line->HasSwitch(::switches::kAppModeAuthCode)) {
user_context_.SetAuthCode(
command_line->GetSwitchValueASCII(::switches::kAppModeAuthCode));
}
DCHECK(!has_auth_cookies_);
}
if (!user_context_.GetRefreshToken().empty()) {
session_restore_strategy_ =
OAuth2LoginManager::RESTORE_FROM_PASSED_OAUTH2_REFRESH_TOKEN;
} else {
session_restore_strategy_ =
OAuth2LoginManager::RESTORE_FROM_SAVED_OAUTH2_REFRESH_TOKEN;
}
}
void UserSessionManager::RestoreAuthSessionImpl(
Profile* profile,
bool restore_from_auth_cookies) {
CHECK((authenticator_.get() && authenticator_->authentication_context()) ||
!restore_from_auth_cookies);
if (chrome::IsRunningInForcedAppMode() ||
base::CommandLine::ForCurrentProcess()->HasSwitch(
chromeos::switches::kDisableGaiaServices)) {
return;
}
// Remove legacy OAuth1 token if we have one. If it's valid, we should already
// have OAuth2 refresh token in OAuth2TokenService that could be used to
// retrieve all other tokens and user_context.
OAuth2LoginManager* login_manager =
OAuth2LoginManagerFactory::GetInstance()->GetForProfile(profile);
login_manager->AddObserver(this);
scoped_refptr<network::SharedURLLoaderFactory> auth_url_loader_factory =
GetAuthURLLoaderFactory();
// Authentication URLLoaderFactory may not be available if user was not
// signing in with GAIA webview (i.e. webview instance hasn't been
// initialized at all). Use fallback URLLoaderFactory if authenticator was
// provided.
// Authenticator instance may not be initialized for session
// restore case when Chrome is restarting after crash or to apply custom user
// flags. In that case auth_url_loader_factory will be nullptr which is
// accepted by RestoreSession() for session restore case.
if (!auth_url_loader_factory &&
(authenticator_.get() && authenticator_->authentication_context())) {
auth_url_loader_factory =
content::BrowserContext::GetDefaultStoragePartition(
authenticator_->authentication_context())
->GetURLLoaderFactoryForBrowserProcess();
}
login_manager->RestoreSession(
auth_url_loader_factory, session_restore_strategy_,
user_context_.GetRefreshToken(), user_context_.GetAccessToken());
}
void UserSessionManager::InitRlzImpl(Profile* profile,
const RlzInitParams& params) {
#if BUILDFLAG(ENABLE_RLZ)
// RLZ is disabled if disabled explicitly or if the device is not yet locked.
PrefService* local_state = g_browser_process->local_state();
if (params.disabled || !InstallAttributes::Get()->IsDeviceLocked()) {
// Empty brand code means an organic install (no RLZ pings are sent).
google_brand::chromeos::ClearBrandForCurrentSession();
}
if (params.disabled != local_state->GetBoolean(prefs::kRLZDisabled)) {
// When switching to RLZ enabled/disabled state, clear all recorded events.
rlz::RLZTracker::ClearRlzState();
local_state->SetBoolean(prefs::kRLZDisabled, params.disabled);
}
// Init the RLZ library.
int ping_delay = profile->GetPrefs()->GetInteger(prefs::kRlzPingDelaySeconds);
// Negative ping delay means to send ping immediately after a first search is
// recorded.
bool send_ping_immediately = ping_delay < 0;
base::TimeDelta delay = base::TimeDelta::FromSeconds(abs(ping_delay)) -
params.time_since_oobe_completion;
rlz::RLZTracker::SetRlzDelegate(
base::WrapUnique(new ChromeRLZTrackerDelegate));
rlz::RLZTracker::InitRlzDelayed(
user_manager::UserManager::Get()->IsCurrentUserNew(),
send_ping_immediately, delay,
ChromeRLZTrackerDelegate::IsGoogleDefaultSearch(profile),
ChromeRLZTrackerDelegate::IsGoogleHomepage(profile),
ChromeRLZTrackerDelegate::IsGoogleInStartpages(profile));
#endif
}
void UserSessionManager::InitializeCerts(Profile* profile) {
// Now that the user profile has been initialized
// |GetNSSCertDatabaseForProfile| is safe to be used.
if (NetworkCertLoader::IsInitialized() &&
base::SysInfo::IsRunningOnChromeOS()) {
GetNSSCertDatabaseForProfile(profile,
base::Bind(&OnGetNSSCertDatabaseForUser));
}
}
void UserSessionManager::InitializeCRLSetFetcher(
const user_manager::User* user) {
const std::string username_hash = user->username_hash();
if (!username_hash.empty()) {
base::FilePath path;
path = ProfileHelper::GetProfilePathByUserIdHash(username_hash);
component_updater::ComponentUpdateService* cus =
g_browser_process->component_updater();
if (cus)
component_updater::RegisterCRLSetComponent(cus, path);
}
}
void UserSessionManager::InitializeCertificateTransparencyComponents(
const user_manager::User* user) {
const std::string username_hash = user->username_hash();
component_updater::ComponentUpdateService* cus =
g_browser_process->component_updater();
if (!username_hash.empty() && cus) {
const base::FilePath path =
ProfileHelper::GetProfilePathByUserIdHash(username_hash);
// STH set fetcher.
RegisterSTHSetComponent(cus, path);
}
}
void UserSessionManager::OnRestoreActiveSessions(
base::Optional<SessionManagerClient::ActiveSessionsMap> sessions) {
if (!sessions.has_value()) {
LOG(ERROR) << "Could not get list of active user sessions after crash.";
// If we could not get list of active user sessions it is safer to just
// sign out so that we don't get in the inconsistent state.
DBusThreadManager::Get()->GetSessionManagerClient()->StopSession();
return;
}
// One profile has been already loaded on browser start.
user_manager::UserManager* user_manager = user_manager::UserManager::Get();
DCHECK_EQ(1u, user_manager->GetLoggedInUsers().size());
DCHECK(user_manager->GetActiveUser());
const cryptohome::Identification active_cryptohome_id(
user_manager->GetActiveUser()->GetAccountId());
for (auto& item : sessions.value()) {
cryptohome::Identification id =
cryptohome::Identification::FromString(item.first);
if (active_cryptohome_id == id)
continue;
pending_user_sessions_[id.GetAccountId()] = std::move(item.second);
}
RestorePendingUserSessions();
}
void UserSessionManager::RestorePendingUserSessions() {
if (pending_user_sessions_.empty()) {
// '>1' ignores "restart on signin" because of browser flags difference.
// In this case, last_session_active_account_id_ can carry account_id
// from the previous browser session.
if (user_manager::UserManager::Get()->GetLoggedInUsers().size() > 1)
user_manager::UserManager::Get()->SwitchToLastActiveUser();
NotifyPendingUserSessionsRestoreFinished();
return;
}
// Get next user to restore sessions and delete it from list.
PendingUserSessions::const_iterator it = pending_user_sessions_.begin();
const AccountId account_id = it->first;
std::string user_id_hash = it->second;
DCHECK(account_id.is_valid());
DCHECK(!user_id_hash.empty());
pending_user_sessions_.erase(account_id);
// Check that this user is not logged in yet.
// TODO(alemate): Investigate whether this could be simplified by enforcing
// session restore to existing users only. Currently this breakes some tests
// (namely CrashRestoreComplexTest.RestoreSessionForThreeUsers), but
// it may be test-specific and could probably be changed.
user_manager::UserList logged_in_users =
user_manager::UserManager::Get()->GetLoggedInUsers();
bool user_already_logged_in = false;
for (user_manager::UserList::const_iterator it = logged_in_users.begin();
it != logged_in_users.end(); ++it) {
const user_manager::User* user = (*it);
if (user->GetAccountId() == account_id) {
user_already_logged_in = true;
break;
}
}
DCHECK(!user_already_logged_in);
if (!user_already_logged_in) {
const user_manager::User* const user =
user_manager::UserManager::Get()->FindUser(account_id);
UserContext user_context =
user ? UserContext(*user)
: UserContext(user_manager::UserType::USER_TYPE_REGULAR,
account_id);
user_context.SetUserIDHash(user_id_hash);
user_context.SetIsUsingOAuth(false);
// Will call OnProfilePrepared() once profile has been loaded.
// Only handling secondary users here since primary user profile
// (and session) has been loaded on Chrome startup.
StartSession(user_context, SECONDARY_USER_SESSION_AFTER_CRASH,
false, // has_auth_cookies
true, // has_active_session, this is restart after crash
this);
} else {
RestorePendingUserSessions();
}
}
void UserSessionManager::NotifyPendingUserSessionsRestoreFinished() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
user_sessions_restored_ = true;
user_sessions_restore_in_progress_ = false;
for (auto& observer : session_state_observer_list_)
observer.PendingUserSessionsRestoreFinished();
}
void UserSessionManager::UpdateEasyUnlockKeys(const UserContext& user_context) {
easy_unlock_key_ops_finished_ = false;
// Skip key update because FakeCryptohomeClient always return success
// and RefreshKeys op expects a failure to stop. As a result, some tests would
// timeout.
// TODO(xiyuan): Revisit this when adding tests.
if (!base::SysInfo::IsRunningOnChromeOS()) {
NotifyEasyUnlockKeyOpsFinished();
return;
}
// Only update Easy unlock keys for regular user.
// TODO(xiyuan): Fix inconsistency user type of |user_context| introduced in
// authenticator.
const user_manager::User* user =
user_manager::UserManager::Get()->FindUser(user_context.GetAccountId());
if (!user || !user->HasGaiaAccount()) {
NotifyEasyUnlockKeyOpsFinished();
return;
}
// Bail if |user_context| does not have secret.
if (user_context.GetKey()->GetSecret().empty()) {
NotifyEasyUnlockKeyOpsFinished();
return;
}
// Skip key update when using PIN. The keys should wrap password instead of
// PIN.
if (user_context.IsUsingPin()) {
NotifyEasyUnlockKeyOpsFinished();
return;
}
const base::ListValue* device_list = nullptr;
EasyUnlockService* easy_unlock_service = EasyUnlockService::GetForUser(*user);
if (easy_unlock_service) {
device_list = easy_unlock_service->IsChromeOSLoginEnabled()
? easy_unlock_service->GetRemoteDevices()
: nullptr;
easy_unlock_service->SetHardlockState(
EasyUnlockScreenlockStateHandler::NO_HARDLOCK);
}
base::ListValue empty_list;
if (!device_list)
device_list = &empty_list;
EasyUnlockKeyManager* key_manager = GetEasyUnlockKeyManager();
running_easy_unlock_key_ops_ = true;
key_manager->RefreshKeys(
user_context, *device_list,
base::Bind(&UserSessionManager::OnEasyUnlockKeyOpsFinished, AsWeakPtr(),
user_context.GetAccountId().GetUserEmail()));
}
scoped_refptr<network::SharedURLLoaderFactory>
UserSessionManager::GetAuthURLLoaderFactory() const {
content::StoragePartition* signin_partition = login::GetSigninPartition();
if (!signin_partition)
return nullptr;
return signin_partition->GetURLLoaderFactoryForBrowserProcess();
}
void UserSessionManager::OnEasyUnlockKeyOpsFinished(const std::string& user_id,
bool success) {
const user_manager::User* user = user_manager::UserManager::Get()->FindUser(
AccountId::FromUserEmail(user_id));
EasyUnlockService* easy_unlock_service = EasyUnlockService::GetForUser(*user);
easy_unlock_service->CheckCryptohomeKeysAndMaybeHardlock();
NotifyEasyUnlockKeyOpsFinished();
}
void UserSessionManager::ActiveUserChanged(
const user_manager::User* active_user) {
if (!user_manager::UserManager::Get()->IsCurrentUserNew())
SendUserPodsMetrics();
Profile* profile = ProfileHelper::Get()->GetProfileByUser(active_user);
// If profile has not yet been initialized, delay initialization of IME.
if (!profile)
return;
input_method::InputMethodManager* manager =
input_method::InputMethodManager::Get();
// |manager| might not be available in some unit tests.
if (!manager)
return;
manager->SetState(
GetDefaultIMEState(ProfileHelper::Get()->GetProfileByUser(active_user)));
manager->MaybeNotifyImeMenuActivationChanged();
}
scoped_refptr<input_method::InputMethodManager::State>
UserSessionManager::GetDefaultIMEState(Profile* profile) {
scoped_refptr<input_method::InputMethodManager::State> state =
default_ime_states_[profile];
if (!state.get()) {
// Profile can be NULL in tests.
state = input_method::InputMethodManager::Get()->CreateNewState(profile);
default_ime_states_[profile] = state;
}
return state;
}
void UserSessionManager::CheckEolStatus(Profile* profile) {
if (!EolNotification::ShouldShowEolNotification())
return;
std::map<Profile*, std::unique_ptr<EolNotification>, ProfileCompare>::iterator
iter = eol_notification_handler_.find(profile);
if (iter == eol_notification_handler_.end()) {
auto eol_notification = std::make_unique<EolNotification>(profile);
iter = eol_notification_handler_
.insert(std::make_pair(profile, std::move(eol_notification)))
.first;
}
iter->second->CheckEolStatus();
}
EasyUnlockKeyManager* UserSessionManager::GetEasyUnlockKeyManager() {
if (!easy_unlock_key_manager_)
easy_unlock_key_manager_.reset(new EasyUnlockKeyManager);
return easy_unlock_key_manager_.get();
}
void UserSessionManager::DoBrowserLaunchInternal(Profile* profile,
LoginDisplayHost* login_host,
bool locale_pref_checked) {
if (browser_shutdown::IsTryingToQuit())
return;
if (!locale_pref_checked) {
RespectLocalePreferenceWrapper(
profile,
base::Bind(&UserSessionManager::DoBrowserLaunchInternal, AsWeakPtr(),
profile, login_host, true /* locale_pref_checked */));
return;
}
if (!ChromeUserManager::Get()->GetCurrentUserFlow()->ShouldLaunchBrowser()) {
ChromeUserManager::Get()->GetCurrentUserFlow()->LaunchExtraSteps(profile);
return;
}
if (RestartToApplyPerSessionFlagsIfNeed(profile, false))
return;
if (login_host) {
login_host->SetStatusAreaVisible(true);
login_host->BeforeSessionStart();
}
BootTimesRecorder::Get()->AddLoginTimeMarker("BrowserLaunched", false);
VLOG(1) << "Launching browser...";
TRACE_EVENT0("login", "LaunchBrowser");
if (should_launch_browser_) {
StartupBrowserCreator browser_creator;
chrome::startup::IsFirstRun first_run =
::first_run::IsChromeFirstRun() ? chrome::startup::IS_FIRST_RUN
: chrome::startup::IS_NOT_FIRST_RUN;
browser_creator.LaunchBrowser(
*base::CommandLine::ForCurrentProcess(), profile, base::FilePath(),
chrome::startup::IS_PROCESS_STARTUP, first_run);
} else {
LOG(WARNING) << "Browser hasn't been launched, should_launch_browser_"
<< " is false. This is normal in some tests.";
}
if (HatsNotificationController::ShouldShowSurveyToProfile(profile))
hats_notification_controller_ = new HatsNotificationController(profile);
base::OnceClosure login_host_finalized_callback = base::BindOnce(
[] { session_manager::SessionManager::Get()->SessionStarted(); });
// Mark login host for deletion after browser starts. This
// guarantees that the message loop will be referenced by the
// browser before it is dereferenced by the login host.
if (login_host) {
login_host->Finalize(std::move(login_host_finalized_callback));
} else {
base::ResetAndReturn(&login_host_finalized_callback).Run();
}
chromeos::BootTimesRecorder::Get()->LoginDone(
user_manager::UserManager::Get()->IsCurrentUserNew());
// Check to see if this profile should show EndOfLife Notification and show
// the message accordingly.
CheckEolStatus(profile);
// Check to see if this profile should show TPM Firmware Update Notification
// and show the message accordingly.
tpm_firmware_update::ShowNotificationIfNeeded(profile);
// Show the one-time notification and update the relevant pref about the
// completion of the file system migration necessary for ARC, when needed.
arc::ShowArcMigrationSuccessNotificationIfNeeded(profile);
if (should_launch_browser_)
SyncConsentScreen::MaybeLaunchSyncConstentSettings(profile);
}
void UserSessionManager::RespectLocalePreferenceWrapper(
Profile* profile,
const base::Closure& callback) {
if (browser_shutdown::IsTryingToQuit())
return;
const user_manager::User* const user =
ProfileHelper::Get()->GetUserByProfile(profile);
locale_util::SwitchLanguageCallback locale_switched_callback(base::Bind(
&UserSessionManager::RunCallbackOnLocaleLoaded, callback,
base::Owned(new InputEventsBlocker))); // Block UI events until
// the ResourceBundle is
// reloaded.
if (!RespectLocalePreference(profile, user, locale_switched_callback))
callback.Run();
}
// static
void UserSessionManager::RunCallbackOnLocaleLoaded(
const base::Closure& callback,
InputEventsBlocker* /* input_events_blocker */,
const locale_util::LanguageSwitchResult& /* result */) {
callback.Run();
}
void UserSessionManager::RemoveProfileForTesting(Profile* profile) {
default_ime_states_.erase(profile);
}
void UserSessionManager::InjectStubUserContext(
const UserContext& user_context) {
injected_user_context_.reset(new UserContext(user_context));
authenticator_ = NULL;
}
void UserSessionManager::SendUserPodsMetrics() {
bool show_users_on_signin;
CrosSettings::Get()->GetBoolean(kAccountsPrefShowUserNamesOnSignIn,
&show_users_on_signin);
bool is_enterprise_managed = g_browser_process->platform_part()
->browser_policy_connector_chromeos()
->IsEnterpriseManaged();
UserPodsDisplay display;
if (show_users_on_signin) {
if (is_enterprise_managed)
display = USER_PODS_DISPLAY_ENABLED_MANAGED;
else
display = USER_PODS_DISPLAY_ENABLED_REGULAR;
} else {
if (is_enterprise_managed)
display = USER_PODS_DISPLAY_DISABLED_MANAGED;
else
display = USER_PODS_DISPLAY_DISABLED_REGULAR;
}
UMA_HISTOGRAM_ENUMERATION("UserSessionManager.UserPodsDisplay", display,
NUM_USER_PODS_DISPLAY);
}
void UserSessionManager::OnOAuth2TokensFetched(UserContext context) {
if (!TokenHandlesEnabled())
return;
CreateTokenUtilIfMissing();
if (!token_handle_util_->HasToken(context.GetAccountId())) {
token_handle_fetcher_.reset(new TokenHandleFetcher(token_handle_util_.get(),
context.GetAccountId()));
token_handle_fetcher_->FillForNewUser(
context.GetAccessToken(),
base::Bind(&UserSessionManager::OnTokenHandleObtained,
weak_factory_.GetWeakPtr()));
}
}
void UserSessionManager::OnTokenHandleObtained(const AccountId& account_id,
bool success) {
if (!success)
LOG(ERROR) << "OAuth2 token handle fetch failed.";
token_handle_fetcher_.reset();
}
bool UserSessionManager::TokenHandlesEnabled() {
if (!should_obtain_handles_)
return false;
bool ephemeral_users_enabled = false;
bool show_names_on_signin = true;
auto* cros_settings = CrosSettings::Get();
cros_settings->GetBoolean(kAccountsPrefEphemeralUsersEnabled,
&ephemeral_users_enabled);
cros_settings->GetBoolean(kAccountsPrefShowUserNamesOnSignIn,
&show_names_on_signin);
return show_names_on_signin && !ephemeral_users_enabled;
}
void UserSessionManager::Shutdown() {
token_handle_fetcher_.reset();
token_handle_util_.reset();
first_run::GoodiesDisplayer::Delete();
always_on_vpn_manager_.reset();
}
void UserSessionManager::SetSwitchesForUser(
const AccountId& account_id,
CommandLineSwitchesType switches_type,
const std::vector<std::string>& switches) {
// TODO(pmarko): Introduce a CHECK that |account_id| is the primary user
// (https://crbug.com/832857).
command_line_switches_[switches_type] = switches;
// Apply all command-line switch types in session manager as a flat list.
std::vector<std::string> all_switches;
for (const auto& pair : command_line_switches_) {
all_switches.insert(all_switches.end(), pair.second.begin(),
pair.second.end());
}
SessionManagerClient* session_manager_client =
DBusThreadManager::Get()->GetSessionManagerClient();
session_manager_client->SetFlagsForUser(
cryptohome::CreateAccountIdentifierFromAccountId(account_id),
all_switches);
}
void UserSessionManager::CreateTokenUtilIfMissing() {
if (!token_handle_util_.get())
token_handle_util_.reset(new TokenHandleUtil());
}
void UserSessionManager::NotifyEasyUnlockKeyOpsFinished() {
DCHECK(!easy_unlock_key_ops_finished_);
running_easy_unlock_key_ops_ = false;
easy_unlock_key_ops_finished_ = true;
for (auto& callback : easy_unlock_key_ops_finished_callbacks_) {
std::move(callback).Run();
}
easy_unlock_key_ops_finished_callbacks_.clear();
}
void UserSessionManager::WaitForEasyUnlockKeyOpsFinished(
base::OnceClosure callback) {
if (easy_unlock_key_ops_finished_) {
std::move(callback).Run();
return;
}
easy_unlock_key_ops_finished_callbacks_.push_back(std::move(callback));
}
} // namespace chromeos