| // 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/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)); |
| } |
| // 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::UserManager::Get()->IsCurrentUserNew() || |
| profile->IsNewProfile()) { |
| SetFirstLoginPrefs(profile, user_context.GetPublicSessionLocale(), |
| user_context.GetPublicSessionInputMethod()); |
| } |
| |
| if (user_manager::UserManager::Get()->IsLoggedInAsSupervisedUser()) { |
| user_manager::User* active_user = |
| user_manager::UserManager::Get()->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::UserManager::Get() |
| ->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::UserManager::Get()->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 |