| // Copyright 2012 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ash/login/existing_user_controller.h" |
| |
| #include <memory> |
| #include <tuple> |
| #include <utility> |
| #include <vector> |
| |
| #include "ash/components/arc/arc_util.h" |
| #include "ash/constants/ash_features.h" |
| #include "ash/constants/ash_pref_names.h" |
| #include "ash/constants/ash_switches.h" |
| #include "ash/constants/notifier_catalogs.h" |
| #include "ash/public/cpp/login_screen.h" |
| #include "ash/public/cpp/notification_utils.h" |
| #include "base/barrier_closure.h" |
| #include "base/check_is_test.h" |
| #include "base/command_line.h" |
| #include "base/compiler_specific.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/logging.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "base/scoped_observation.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "base/timer/elapsed_timer.h" |
| #include "base/values.h" |
| #include "base/version.h" |
| #include "chrome/app/vector_icons/vector_icons.h" |
| #include "chrome/browser/ash/app_mode/kiosk_app_launch_error.h" |
| #include "chrome/browser/ash/app_mode/kiosk_app_types.h" |
| #include "chrome/browser/ash/arc/arc_util.h" |
| #include "chrome/browser/ash/authpolicy/authpolicy_helper.h" |
| #include "chrome/browser/ash/boot_times_recorder.h" |
| #include "chrome/browser/ash/crosapi/browser_data_migrator.h" |
| #include "chrome/browser/ash/customization/customization_document.h" |
| #include "chrome/browser/ash/login/auth/chrome_login_performer.h" |
| #include "chrome/browser/ash/login/demo_mode/demo_session.h" |
| #include "chrome/browser/ash/login/enterprise_user_session_metrics.h" |
| #include "chrome/browser/ash/login/helper.h" |
| #include "chrome/browser/ash/login/profile_auth_data.h" |
| #include "chrome/browser/ash/login/quick_unlock/pin_salt_storage.h" |
| #include "chrome/browser/ash/login/quick_unlock/pin_storage_cryptohome.h" |
| #include "chrome/browser/ash/login/reauth_stats.h" |
| #include "chrome/browser/ash/login/screens/encryption_migration_mode.h" |
| #include "chrome/browser/ash/login/session/user_session_manager.h" |
| #include "chrome/browser/ash/login/signin/oauth2_token_initializer.h" |
| #include "chrome/browser/ash/login/signin_specifics.h" |
| #include "chrome/browser/ash/login/startup_utils.h" |
| #include "chrome/browser/ash/login/ui/login_display_host.h" |
| #include "chrome/browser/ash/login/ui/login_display_host_mojo.h" |
| #include "chrome/browser/ash/login/ui/signin_ui.h" |
| #include "chrome/browser/ash/login/ui/user_adding_screen.h" |
| #include "chrome/browser/ash/login/user_flow.h" |
| #include "chrome/browser/ash/login/users/chrome_user_manager.h" |
| #include "chrome/browser/ash/login/wizard_controller.h" |
| #include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h" |
| #include "chrome/browser/ash/policy/core/device_local_account.h" |
| #include "chrome/browser/ash/policy/core/device_local_account_policy_service.h" |
| #include "chrome/browser/ash/policy/handlers/minimum_version_policy_handler.h" |
| #include "chrome/browser/ash/profiles/profile_helper.h" |
| #include "chrome/browser/ash/settings/cros_settings.h" |
| #include "chrome/browser/ash/system/device_disabling_manager.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/browser_process_platform_part.h" |
| #include "chrome/browser/chrome_notification_types.h" |
| #include "chrome/browser/lifetime/application_lifetime_chromeos.h" |
| #include "chrome/browser/lifetime/browser_shutdown.h" |
| #include "chrome/browser/net/system_network_context_manager.h" |
| #include "chrome/browser/notifications/system_notification_helper.h" |
| #include "chrome/browser/policy/profile_policy_connector.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/signin/chrome_device_id_helper.h" |
| #include "chrome/browser/ui/ash/system_tray_client_impl.h" |
| #include "chrome/browser/ui/aura/accessibility/automation_manager_aura.h" |
| #include "chrome/browser/ui/managed_ui.h" |
| #include "chrome/browser/ui/webui/ash/login/encryption_migration_screen_handler.h" |
| #include "chrome/browser/ui/webui/ash/login/kiosk_autolaunch_screen_handler.h" |
| #include "chrome/browser/ui/webui/ash/login/kiosk_enable_screen_handler.h" |
| #include "chrome/browser/ui/webui/ash/login/l10n_util.h" |
| #include "chrome/browser/ui/webui/ash/login/tpm_error_screen_handler.h" |
| #include "chrome/browser/ui/webui/ash/login/update_required_screen_handler.h" |
| #include "chrome/common/channel_info.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/common/url_constants.h" |
| #include "chrome/grit/generated_resources.h" |
| #include "chromeos/ash/components/cryptohome/cryptohome_parameters.h" |
| #include "chromeos/ash/components/cryptohome/cryptohome_util.h" |
| #include "chromeos/ash/components/dbus/hiberman/hiberman_client.h" |
| #include "chromeos/ash/components/dbus/session_manager/session_manager_client.h" |
| #include "chromeos/ash/components/dbus/userdataauth/userdataauth_client.h" |
| #include "chromeos/ash/components/install_attributes/install_attributes.h" |
| #include "chromeos/ash/components/login/auth/public/auth_failure.h" |
| #include "chromeos/ash/components/login/auth/public/key.h" |
| #include "chromeos/ash/components/login/session/session_termination_manager.h" |
| #include "chromeos/ash/components/settings/cros_settings_names.h" |
| #include "chromeos/dbus/power/power_manager_client.h" |
| #include "chromeos/strings/grit/chromeos_strings.h" |
| #include "components/account_id/account_id.h" |
| #include "components/google/core/common/google_util.h" |
| #include "components/policy/core/common/cloud/cloud_policy_core.h" |
| #include "components/policy/core/common/cloud/device_management_service.h" |
| #include "components/policy/core/common/policy_map.h" |
| #include "components/policy/core/common/policy_service.h" |
| #include "components/policy/core/common/policy_types.h" |
| #include "components/policy/policy_constants.h" |
| #include "components/policy/proto/cloud_policy.pb.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/session_manager/core/session_manager.h" |
| #include "components/user_manager/known_user.h" |
| #include "components/user_manager/user_names.h" |
| #include "components/user_manager/user_type.h" |
| #include "components/vector_icons/vector_icons.h" |
| #include "components/version_info/version_info.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/notification_service.h" |
| #include "content/public/browser/notification_types.h" |
| #include "content/public/browser/storage_partition.h" |
| #include "google_apis/gaia/gaia_auth_util.h" |
| #include "google_apis/gaia/google_service_auth_error.h" |
| #include "services/network/public/mojom/network_context.mojom.h" |
| #include "third_party/abseil-cpp/absl/types/optional.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/base/user_activity/user_activity_detector.h" |
| #include "ui/base/user_activity/user_activity_observer.h" |
| #include "ui/message_center/public/cpp/notification.h" |
| #include "ui/message_center/public/cpp/notification_delegate.h" |
| #include "ui/views/widget/widget.h" |
| |
| // Enable VLOG level 1. |
| #undef ENABLED_VLOG_LEVEL |
| #define ENABLED_VLOG_LEVEL 1 |
| |
| namespace ash { |
| namespace { |
| |
| using RebootOnSignOutPolicy = |
| ::enterprise_management::DeviceRebootOnUserSignoutProto; |
| |
| const char kAutoLaunchNotificationId[] = |
| "chrome://managed_guest_session/auto_launch"; |
| |
| const char kAutoLaunchNotifierId[] = "ash.managed_guest_session-auto_launch"; |
| |
| // Delay for restarting the ui if safe-mode login has failed. |
| const long int kSafeModeRestartUiDelayMs = 30000; |
| |
| // Makes a call to the policy subsystem to reload the policy when we detect |
| // authentication change. |
| void RefreshPoliciesOnUIThread() { |
| if (g_browser_process->policy_service()) |
| g_browser_process->policy_service()->RefreshPolicies(base::OnceClosure()); |
| } |
| |
| void OnTranferredHttpAuthCaches() { |
| VLOG(1) << "Main request context populated with authentication data."; |
| // Last but not least tell the policy subsystem to refresh now as it might |
| // have been stuck until now too. |
| content::GetUIThreadTaskRunner({})->PostTask( |
| FROM_HERE, base::BindOnce(&RefreshPoliciesOnUIThread)); |
| } |
| |
| // Copies any authentication details that were entered in the login profile to |
| // the main profile to make sure all subsystems of Chrome can access the network |
| // with the provided authentication which are possibly for a proxy server. |
| void TransferHttpAuthCaches() { |
| content::StoragePartition* webview_storage_partition = |
| login::GetSigninPartition(); |
| base::RepeatingClosure completion_callback = |
| base::BarrierClosure(webview_storage_partition ? 2 : 1, |
| base::BindOnce(&OnTranferredHttpAuthCaches)); |
| if (webview_storage_partition) { |
| webview_storage_partition->GetNetworkContext() |
| ->SaveHttpAuthCacheProxyEntries(base::BindOnce( |
| &TransferHttpAuthCacheToSystemNetworkContext, completion_callback)); |
| } |
| |
| network::mojom::NetworkContext* default_network_context = |
| ProfileHelper::GetSigninProfile() |
| ->GetDefaultStoragePartition() |
| ->GetNetworkContext(); |
| default_network_context->SaveHttpAuthCacheProxyEntries(base::BindOnce( |
| &TransferHttpAuthCacheToSystemNetworkContext, completion_callback)); |
| } |
| |
| bool IsUpdateRequiredDeadlineReached() { |
| policy::MinimumVersionPolicyHandler* policy_handler = |
| g_browser_process->platform_part() |
| ->browser_policy_connector_ash() |
| ->GetMinimumVersionPolicyHandler(); |
| return policy_handler && policy_handler->DeadlineReached(); |
| } |
| |
| bool IsTestingMigrationUI() { |
| return base::CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kTestEncryptionMigrationUI); |
| } |
| |
| bool ShouldForceDircrypto(const AccountId& account_id) { |
| if (IsTestingMigrationUI()) |
| return true; |
| |
| // If the device is not officially supported to run ARC, we don't need to |
| // force Ext4 dircrypto. |
| if (!arc::IsArcAvailable()) |
| return false; |
| |
| // When a user is signing in as a secondary user, we don't need to force Ext4 |
| // dircrypto since the user can not run ARC. |
| if (UserAddingScreen::Get()->IsRunning()) |
| return false; |
| |
| return true; |
| } |
| |
| // Returns true if the device is enrolled to an Active Directory domain |
| // according to InstallAttributes (proxied through BrowserPolicyConnector). |
| bool IsActiveDirectoryManaged() { |
| return g_browser_process->platform_part() |
| ->browser_policy_connector_ash() |
| ->IsActiveDirectoryManaged(); |
| } |
| |
| LoginDisplayHost* GetLoginDisplayHost() { |
| return LoginDisplayHost::default_host(); |
| } |
| |
| LoginDisplay* GetLoginDisplay() { |
| return GetLoginDisplayHost()->GetLoginDisplay(); |
| } |
| |
| void SetLoginExtensionApiCanLockManagedGuestSessionPref( |
| const AccountId& account_id, |
| bool can_lock_managed_guest_session) { |
| const user_manager::User* user = |
| user_manager::UserManager::Get()->FindUser(account_id); |
| DCHECK(user); |
| Profile* profile = ProfileHelper::Get()->GetProfileByUser(user); |
| DCHECK(profile); |
| PrefService* prefs = profile->GetPrefs(); |
| prefs->SetBoolean(::prefs::kLoginExtensionApiCanLockManagedGuestSession, |
| can_lock_managed_guest_session); |
| prefs->CommitPendingWrite(); |
| } |
| |
| absl::optional<EncryptionMigrationMode> GetEncryptionMigrationMode( |
| const UserContext& user_context, |
| bool has_incomplete_migration) { |
| if (has_incomplete_migration) { |
| // If migration was incomplete, continue migration automatically. |
| return EncryptionMigrationMode::RESUME_MIGRATION; |
| } |
| |
| if (user_context.GetUserType() == user_manager::USER_TYPE_CHILD) { |
| // Force-migrate child users. |
| return EncryptionMigrationMode::START_MIGRATION; |
| } |
| |
| user_manager::KnownUser known_user(g_browser_process->local_state()); |
| const bool profile_has_policy = |
| known_user.GetProfileRequiresPolicy(user_context.GetAccountId()) == |
| user_manager::ProfileRequiresPolicy::kPolicyRequired || |
| base::CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kProfileRequiresPolicy); |
| |
| // Force-migrate all home directories if the user is known to have enterprise |
| // policy, otherwise ask the user. |
| return profile_has_policy ? EncryptionMigrationMode::START_MIGRATION |
| : EncryptionMigrationMode::ASK_USER; |
| } |
| |
| // Returns account ID if a corresponding to `auto_login_account_id` device local |
| // account exists, otherwise returns invalid account ID. |
| AccountId GetPublicSessionAutoLoginAccountId( |
| const std::vector<policy::DeviceLocalAccount>& device_local_accounts, |
| const std::string& auto_login_account_id) { |
| for (std::vector<policy::DeviceLocalAccount>::const_iterator it = |
| device_local_accounts.begin(); |
| it != device_local_accounts.end(); ++it) { |
| if (it->account_id == auto_login_account_id) { |
| VLOG(2) << "PublicSession autologin found: " << it->user_id; |
| return AccountId::FromUserEmail(it->user_id); |
| } |
| } |
| return EmptyAccountId(); |
| } |
| |
| int CountRegularUsers(const user_manager::UserList& users) { |
| // Counts regular device users that can log in. |
| int regular_users_counter = 0; |
| for (auto* user : users) { |
| // Skip kiosk apps for login screen user list. Kiosk apps as pods (aka new |
| // kiosk UI) is currently disabled and it gets the apps directly from |
| // KioskAppManager, ArcKioskAppManager and WebKioskAppManager. |
| if (user->IsKioskType()) { |
| continue; |
| } |
| // Allow offline login from the error screen if user of one of these types |
| // has already logged in. |
| if (user->GetType() == user_manager::USER_TYPE_REGULAR || |
| user->GetType() == user_manager::USER_TYPE_CHILD || |
| user->GetType() == user_manager::USER_TYPE_ACTIVE_DIRECTORY) { |
| regular_users_counter++; |
| } |
| } |
| return regular_users_counter; |
| } |
| |
| user_manager::UserList ExtractSamlLoginUsers( |
| const user_manager::UserList& users) { |
| user_manager::UserList saml_users_for_password_sync; |
| for (auto* user : users) { |
| if (user->using_saml() && user->HasGaiaAccount() && |
| user_manager::UserManager::Get()->IsGaiaUserAllowed(*user)) { |
| saml_users_for_password_sync.push_back(user); |
| } |
| } |
| return saml_users_for_password_sync; |
| } |
| |
| } // namespace |
| |
| // Utility class used to wait for a Public Session policy to be available if |
| // public session login is requested before the associated policy is loaded. |
| // When the policy is available, it will run the callback passed to the |
| // constructor. |
| class ExistingUserController::DeviceLocalAccountPolicyWaiter |
| : public policy::DeviceLocalAccountPolicyService::Observer { |
| public: |
| DeviceLocalAccountPolicyWaiter( |
| policy::DeviceLocalAccountPolicyService* policy_service, |
| base::OnceClosure callback, |
| const std::string& user_id) |
| : policy_service_(policy_service), |
| callback_(std::move(callback)), |
| user_id_(user_id) { |
| scoped_observation_.Observe(policy_service); |
| } |
| ~DeviceLocalAccountPolicyWaiter() override = default; |
| |
| DeviceLocalAccountPolicyWaiter(const DeviceLocalAccountPolicyWaiter& other) = |
| delete; |
| DeviceLocalAccountPolicyWaiter& operator=( |
| const DeviceLocalAccountPolicyWaiter& other) = delete; |
| |
| // policy::DeviceLocalAccountPolicyService::Observer: |
| void OnPolicyUpdated(const std::string& user_id) override { |
| if (user_id != user_id_ || |
| !policy_service_->IsPolicyAvailableForUser(user_id)) { |
| return; |
| } |
| scoped_observation_.Reset(); |
| std::move(callback_).Run(); |
| } |
| |
| void OnDeviceLocalAccountsChanged() override {} |
| |
| private: |
| base::raw_ptr<policy::DeviceLocalAccountPolicyService> policy_service_ = |
| nullptr; |
| base::OnceClosure callback_; |
| std::string user_id_; |
| base::ScopedObservation<policy::DeviceLocalAccountPolicyService, |
| policy::DeviceLocalAccountPolicyService::Observer> |
| scoped_observation_{this}; |
| }; |
| |
| // static |
| ExistingUserController* ExistingUserController::current_controller() { |
| auto* host = LoginDisplayHost::default_host(); |
| return host ? host->GetExistingUserController() : nullptr; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // ExistingUserController, public: |
| |
| ExistingUserController::ExistingUserController() |
| : cros_settings_(CrosSettings::Get()), |
| network_state_helper_(new login::NetworkStateHelper), |
| pin_salt_storage_(std::make_unique<quick_unlock::PinSaltStorage>()) { |
| registrar_.Add(this, chrome::NOTIFICATION_AUTH_SUPPLIED, |
| content::NotificationService::AllSources()); |
| show_user_names_subscription_ = cros_settings_->AddSettingsObserver( |
| kAccountsPrefShowUserNamesOnSignIn, |
| base::BindRepeating(&ExistingUserController::DeviceSettingsChanged, |
| base::Unretained(this))); |
| allow_guest_subscription_ = cros_settings_->AddSettingsObserver( |
| kAccountsPrefAllowGuest, |
| base::BindRepeating(&ExistingUserController::DeviceSettingsChanged, |
| base::Unretained(this))); |
| users_subscription_ = cros_settings_->AddSettingsObserver( |
| kAccountsPrefUsers, |
| base::BindRepeating(&ExistingUserController::DeviceSettingsChanged, |
| base::Unretained(this))); |
| local_account_auto_login_id_subscription_ = |
| cros_settings_->AddSettingsObserver( |
| kAccountsPrefDeviceLocalAccountAutoLoginId, |
| base::BindRepeating(&ExistingUserController::ConfigureAutoLogin, |
| base::Unretained(this))); |
| local_account_auto_login_delay_subscription_ = |
| cros_settings_->AddSettingsObserver( |
| kAccountsPrefDeviceLocalAccountAutoLoginDelay, |
| base::BindRepeating(&ExistingUserController::ConfigureAutoLogin, |
| base::Unretained(this))); |
| family_link_allowed_subscription_ = cros_settings_->AddSettingsObserver( |
| kAccountsPrefFamilyLinkAccountsAllowed, |
| base::BindRepeating(&ExistingUserController::DeviceSettingsChanged, |
| base::Unretained(this))); |
| |
| observed_user_manager_.Observe(user_manager::UserManager::Get()); |
| |
| if (ui::UserActivityDetector::Get()) { |
| ui::UserActivityDetector::Get()->AddObserver(this); |
| } else { |
| CHECK_IS_TEST(); |
| } |
| } |
| |
| void ExistingUserController::Init(const user_manager::UserList& users) { |
| timer_init_ = std::make_unique<base::ElapsedTimer>(); |
| UpdateLoginDisplay(users); |
| ConfigureAutoLogin(); |
| } |
| |
| void ExistingUserController::UpdateLoginDisplay( |
| const user_manager::UserList& users) { |
| int reboot_on_signout_policy = -1; |
| cros_settings_->GetInteger(kDeviceRebootOnUserSignout, |
| &reboot_on_signout_policy); |
| if (reboot_on_signout_policy != -1 && |
| reboot_on_signout_policy != |
| RebootOnSignOutPolicy::REBOOT_ON_SIGNOUT_MODE_UNSPECIFIED && |
| reboot_on_signout_policy != RebootOnSignOutPolicy::NEVER) { |
| SessionTerminationManager::Get()->RebootIfNecessary(); |
| } |
| bool show_users_on_signin = true; |
| cros_settings_->GetBoolean(kAccountsPrefShowUserNamesOnSignIn, |
| &show_users_on_signin); |
| AuthMetricsRecorder::Get()->OnShowUsersOnSignin(show_users_on_signin); |
| bool enable_ephemeral_users = false; |
| cros_settings_->GetBoolean(kAccountsPrefEphemeralUsersEnabled, |
| &enable_ephemeral_users); |
| AuthMetricsRecorder::Get()->OnEnableEphemeralUsers(enable_ephemeral_users); |
| // Counts regular device users that can log in. |
| const int regular_users_counter = CountRegularUsers(users); |
| // Allow offline login from the error screen if a regular user has already |
| // logged in. |
| ErrorScreen::AllowOfflineLogin(/*allowed=*/regular_users_counter > 0); |
| // Records total number of users on the login screen. |
| base::UmaHistogramCounts100("Login.NumberOfUsersOnLoginScreen", |
| regular_users_counter); |
| AuthMetricsRecorder::Get()->OnUserCount(regular_users_counter); |
| const auto saml_users_for_password_sync = ExtractSamlLoginUsers(users); |
| |
| // ExistingUserController owns PasswordSyncTokenLoginCheckers only if user |
| // pods are hidden. |
| if (!show_users_on_signin && !saml_users_for_password_sync.empty()) { |
| sync_token_checkers_ = |
| std::make_unique<PasswordSyncTokenCheckersCollection>(); |
| sync_token_checkers_->StartPasswordSyncCheckers( |
| saml_users_for_password_sync, |
| /*observer*/ nullptr); |
| } else { |
| sync_token_checkers_.reset(); |
| } |
| |
| if (LoginScreen::Get()) { |
| LoginScreen::Get()->SetAllowLoginAsGuest( |
| user_manager::UserManager::Get()->IsGuestSessionAllowed()); |
| } |
| |
| if (LoginDisplayHostMojo::Get()) { |
| auto login_users = ExtractLoginUsers(users); |
| LoginDisplayHostMojo::Get()->SetUsers(login_users); |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // ExistingUserController, content::NotificationObserver implementation: |
| // |
| |
| void ExistingUserController::Observe( |
| int type, |
| const content::NotificationSource& source, |
| const content::NotificationDetails& details) { |
| DCHECK_EQ(type, chrome::NOTIFICATION_AUTH_SUPPLIED); |
| |
| // Don't transfer http auth cache on NOTIFICATION_AUTH_SUPPLIED after user |
| // session starts. |
| if (session_manager::SessionManager::Get()->IsSessionStarted()) |
| return; |
| |
| // Possibly the user has authenticated against a proxy server and we might |
| // need the credentials for enrollment and other system requests from the |
| // main `g_browser_process` request context (see bug |
| // http://crosbug.com/24861). So we transfer any credentials to the global |
| // request context here. |
| // The issue we have here is that the NOTIFICATION_AUTH_SUPPLIED is sent |
| // just after the UI is closed but before the new credentials were stored |
| // in the profile. Therefore we have to give it some time to make sure it |
| // has been updated before we copy it. |
| // TODO(pmarko): Find a better way to do this, see https://crbug.com/796512. |
| VLOG(1) << "Authentication was entered manually, possibly for proxyauth."; |
| base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask( |
| FROM_HERE, base::BindOnce(&TransferHttpAuthCaches), |
| kAuthCacheTransferDelayMs); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // ExistingUserController, private: |
| |
| ExistingUserController::~ExistingUserController() { |
| ui::UserActivityDetector* activity_detector = ui::UserActivityDetector::Get(); |
| if (activity_detector) { |
| activity_detector->RemoveObserver(this); |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // ExistingUserController, LoginDisplay::Delegate implementation: |
| // |
| void ExistingUserController::CompleteLogin(const UserContext& user_context) { |
| if (!GetLoginDisplayHost()) { |
| // Complete login event was generated already from UI. Ignore notification. |
| return; |
| } |
| |
| if (is_login_in_progress_) |
| return; |
| |
| is_login_in_progress_ = true; |
| |
| ContinueLoginIfDeviceNotDisabled( |
| base::BindOnce(&ExistingUserController::DoCompleteLogin, |
| weak_factory_.GetWeakPtr(), user_context)); |
| } |
| |
| std::u16string ExistingUserController::GetConnectedNetworkName() const { |
| return network_state_helper_->GetCurrentNetworkName(); |
| } |
| |
| void ExistingUserController::Login(const UserContext& user_context, |
| const SigninSpecifics& specifics) { |
| if (is_login_in_progress_) { |
| // If there is another login in progress, bail out. Do not re-enable |
| // clicking on other windows and the status area. Do not start the |
| // auto-login timer. |
| return; |
| } |
| |
| is_login_in_progress_ = true; |
| |
| if (user_context.GetUserType() != user_manager::USER_TYPE_REGULAR && |
| user_manager::UserManager::Get()->IsUserLoggedIn()) { |
| // Multi-login is only allowed for regular users. If we are attempting to |
| // do multi-login as another type of user somehow, bail out. Do not |
| // re-enable clicking on other windows and the status area. Do not start the |
| // auto-login timer. |
| return; |
| } |
| |
| ContinueLoginIfDeviceNotDisabled( |
| base::BindOnce(&ExistingUserController::DoLogin, |
| weak_factory_.GetWeakPtr(), user_context, specifics)); |
| } |
| |
| void ExistingUserController::PerformLogin( |
| const UserContext& user_context, |
| LoginPerformer::AuthorizationMode auth_mode) { |
| VLOG(1) << "Setting flow from PerformLogin"; |
| |
| BootTimesRecorder::Get()->RecordLoginAttempted(); |
| |
| // Use the same LoginPerformer for subsequent login as it has state |
| // such as Authenticator instance. |
| if (!login_performer_.get() || num_login_attempts_ <= 1) { |
| // Only one instance of LoginPerformer should exist at a time. |
| login_performer_.reset(nullptr); |
| login_performer_ = std::make_unique<ChromeLoginPerformer>( |
| this, AuthMetricsRecorder::Get()); |
| } |
| if (IsActiveDirectoryManaged() && |
| user_context.GetUserType() != user_manager::USER_TYPE_ACTIVE_DIRECTORY) { |
| PerformLoginFinishedActions(false /* don't start auto login timer */); |
| ShowError(SigninError::kGoogleAccountNotAllowed, |
| "Google accounts are not allowed on this device"); |
| return; |
| } |
| if (user_context.GetAccountId().GetAccountType() == |
| AccountType::ACTIVE_DIRECTORY && |
| user_context.GetAuthFlow() == UserContext::AUTH_FLOW_OFFLINE && |
| user_context.GetKey()->GetKeyType() == Key::KEY_TYPE_PASSWORD_PLAIN) { |
| // Try to get kerberos TGT while we have user's password typed on the pod |
| // screen. Failure to get TGT here is OK - that could mean e.g. Active |
| // Directory server is not reachable. We don't want to have user wait for |
| // the Active Directory Authentication on the pod screen. |
| // AuthPolicyCredentialsManager will be created inside the user session |
| // which would get status about last authentication and handle possible |
| // failures. |
| AuthPolicyHelper::TryAuthenticateUser( |
| user_context.GetAccountId().GetUserEmail(), |
| user_context.GetAccountId().GetObjGuid(), |
| user_context.GetKey()->GetSecret()); |
| } |
| |
| // If plain text password is available, computes its salt, hash, and length, |
| // and saves them in `user_context`. They will be saved to prefs when user |
| // profile is ready. |
| UserContext new_user_context = user_context; |
| if (user_context.GetKey()->GetKeyType() == Key::KEY_TYPE_PASSWORD_PLAIN) { |
| std::u16string password( |
| base::UTF8ToUTF16(new_user_context.GetKey()->GetSecret())); |
| new_user_context.SetSyncPasswordData(password_manager::PasswordHashData( |
| user_context.GetAccountId().GetUserEmail(), password, |
| auth_mode == LoginPerformer::AuthorizationMode::kExternal)); |
| } |
| |
| if (new_user_context.IsUsingPin()) { |
| absl::optional<Key> key = |
| quick_unlock::PinStorageCryptohome::TransformPinKey( |
| pin_salt_storage_.get(), new_user_context.GetAccountId(), |
| *new_user_context.GetKey()); |
| if (key) { |
| new_user_context.SetKey(*key); |
| } else { |
| new_user_context.SetIsUsingPin(false); |
| } |
| } |
| |
| // If a regular user log in to a device which supports ARC, we should make |
| // sure that the user's cryptohome is encrypted in ext4 dircrypto to run the |
| // latest Android runtime. |
| new_user_context.SetIsForcingDircrypto( |
| ShouldForceDircrypto(new_user_context.GetAccountId())); |
| login_performer_->PerformLogin(new_user_context, auth_mode); |
| SendAccessibilityAlert( |
| l10n_util::GetStringUTF8(IDS_CHROMEOS_ACC_LOGIN_SIGNING_IN)); |
| if (timer_init_) { |
| base::UmaHistogramMediumTimes("Login.PromptToLoginTime", |
| timer_init_->Elapsed()); |
| timer_init_.reset(); |
| } |
| } |
| |
| void ExistingUserController::ContinuePerformLogin( |
| LoginPerformer::AuthorizationMode auth_mode, |
| std::unique_ptr<UserContext> user_context) { |
| CHECK(login_performer_); |
| login_performer_->LoginAuthenticated(std::move(user_context)); |
| } |
| |
| void ExistingUserController::ContinuePerformLoginWithoutMigration( |
| LoginPerformer::AuthorizationMode auth_mode, |
| std::unique_ptr<UserContext> user_context) { |
| user_context->SetIsForcingDircrypto(false); |
| ContinuePerformLogin(auth_mode, std::move(user_context)); |
| } |
| |
| void ExistingUserController::OnGaiaScreenReady() { |
| StartAutoLoginTimer(); |
| } |
| |
| void ExistingUserController::OnStartKioskEnableScreen() { |
| KioskAppManager::Get()->GetConsumerKioskAutoLaunchStatus(base::BindOnce( |
| &ExistingUserController::OnConsumerKioskAutoLaunchCheckCompleted, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void ExistingUserController::SetDisplayEmail(const std::string& email) { |
| display_email_ = email; |
| } |
| |
| void ExistingUserController::SetDisplayAndGivenName( |
| const std::string& display_name, |
| const std::string& given_name) { |
| display_name_ = base::UTF8ToUTF16(display_name); |
| given_name_ = base::UTF8ToUTF16(given_name); |
| } |
| |
| bool ExistingUserController::IsUserAllowlisted( |
| const AccountId& account_id, |
| const absl::optional<user_manager::UserType>& user_type) { |
| bool wildcard_match = false; |
| if (login_performer_.get()) { |
| return login_performer_->IsUserAllowlisted(account_id, &wildcard_match, |
| user_type); |
| } |
| |
| return cros_settings_->IsUserAllowlisted(account_id.GetUserEmail(), |
| &wildcard_match, user_type); |
| } |
| |
| bool ExistingUserController::IsSigninInProgress() const { |
| return is_login_in_progress_; |
| } |
| |
| bool ExistingUserController::IsUserSigninCompleted() const { |
| return is_signin_completed_; |
| } |
| |
| void ExistingUserController::LocalStateChanged( |
| user_manager::UserManager* user_manager) { |
| DeviceSettingsChanged(); |
| } |
| |
| void ExistingUserController::OnConsumerKioskAutoLaunchCheckCompleted( |
| KioskAppManager::ConsumerKioskAutoLaunchStatus status) { |
| if (status == KioskAppManager::ConsumerKioskAutoLaunchStatus::kConfigurable) |
| ShowKioskEnableScreen(); |
| } |
| |
| void ExistingUserController::ShowKioskEnableScreen() { |
| GetLoginDisplayHost()->StartWizard(KioskEnableScreenView::kScreenId); |
| } |
| |
| void ExistingUserController::ShowEncryptionMigrationScreen( |
| std::unique_ptr<UserContext> user_context, |
| EncryptionMigrationMode migration_mode) { |
| CHECK(login_performer_); |
| GetLoginDisplayHost()->GetSigninUI()->StartEncryptionMigration( |
| std::move(user_context), migration_mode, |
| base::BindOnce(&ExistingUserController::ContinuePerformLogin, |
| weak_factory_.GetWeakPtr(), |
| login_performer_->auth_mode())); |
| } |
| |
| void ExistingUserController::ShowTPMError() { |
| GetLoginDisplay()->SetUIEnabled(false); |
| GetLoginDisplayHost()->StartWizard(TpmErrorView::kScreenId); |
| } |
| |
| void ExistingUserController::ShowPasswordChangedDialogLegacy( |
| const UserContext& user_context) { |
| CHECK(login_performer_); |
| VLOG(1) << "Show password changed dialog" |
| << ", count=" << login_performer_->password_changed_callback_count(); |
| |
| // True if user has already made an attempt to enter old password and failed. |
| bool show_invalid_old_password_error = |
| login_performer_->password_changed_callback_count() > 1; |
| |
| GetLoginDisplayHost()->GetSigninUI()->ShowPasswordChangedDialogLegacy( |
| user_context.GetAccountId(), show_invalid_old_password_error); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // ExistingUserController, LoginPerformer::Delegate implementation: |
| // |
| |
| void ExistingUserController::OnAuthFailure(const AuthFailure& failure) { |
| guest_mode_url_ = GURL::EmptyGURL(); |
| std::string error = failure.GetErrorString(); |
| |
| PerformLoginFinishedActions(false /* don't start auto login timer */); |
| |
| if (ChromeUserManager::Get() |
| ->GetUserFlow(last_login_attempt_account_id_) |
| ->HandleLoginFailure(failure)) { |
| return; |
| } |
| |
| const bool is_known_user = user_manager::UserManager::Get()->IsKnownUser( |
| last_login_attempt_account_id_); |
| if (failure.reason() == AuthFailure::OWNER_REQUIRED) { |
| ShowError(SigninError::kOwnerRequired, error); |
| // Using Untretained here is safe because SessionTerminationManager is |
| // destroyed after the task runner, in |
| // ChromeBrowserMainParts::PostDestroyThreads(). |
| content::GetUIThreadTaskRunner({})->PostDelayedTask( |
| FROM_HERE, |
| base::BindOnce(&SessionTerminationManager::StopSession, |
| base::Unretained(SessionTerminationManager::Get()), |
| login_manager::SessionStopReason::OWNER_REQUIRED), |
| base::Milliseconds(kSafeModeRestartUiDelayMs)); |
| } else if (failure.reason() == AuthFailure::TPM_ERROR) { |
| ShowTPMError(); |
| } else if (failure.reason() == AuthFailure::TPM_UPDATE_REQUIRED) { |
| ShowError(SigninError::kTpmUpdateRequired, error); |
| } else if (last_login_attempt_account_id_ == user_manager::GuestAccountId()) { |
| StartAutoLoginTimer(); |
| } else if (is_known_user && |
| failure.reason() == AuthFailure::MISSING_CRYPTOHOME) { |
| ForceOnlineLoginForAccountId(last_login_attempt_account_id_); |
| RecordReauthReason(last_login_attempt_account_id_, |
| ReauthReason::kMissingCryptohome); |
| } else if (is_known_user && |
| failure.reason() == AuthFailure::UNRECOVERABLE_CRYPTOHOME) { |
| // TODO(chromium:1140868, dlunev): for now we route unrecoverable the same |
| // way as missing because it is removed under the hood in cryptohomed when |
| // the condition met. We should surface that up and deal with it on the |
| // chromium level, including making the decision user-driven. |
| ForceOnlineLoginForAccountId(last_login_attempt_account_id_); |
| RecordReauthReason(last_login_attempt_account_id_, |
| ReauthReason::kUnrecoverableCryptohome); |
| } else { |
| // Check networking after trying to login in case user is |
| // cached locally or the local admin account. |
| if (!network_state_helper_->IsConnected()) { |
| if (is_known_user) |
| ShowError(SigninError::kKnownUserFailedNetworkNotConnected, error); |
| else |
| ShowError(SigninError::kNewUserFailedNetworkNotConnected, error); |
| } else { |
| if (is_known_user) |
| ShowError(SigninError::kKnownUserFailedNetworkConnected, error); |
| else |
| ShowError(SigninError::kNewUserFailedNetworkConnected, error); |
| } |
| StartAutoLoginTimer(); |
| } |
| |
| // Reset user flow to default, so that special flow will not affect next |
| // attempt. |
| ChromeUserManager::Get()->ResetUserFlow(last_login_attempt_account_id_); |
| |
| for (auto& auth_status_consumer : auth_status_consumers_) |
| auth_status_consumer.OnAuthFailure(failure); |
| |
| ClearActiveDirectoryState(); |
| ClearRecordedNames(); |
| } |
| |
| void ExistingUserController::OnAuthSuccess(const UserContext& user_context) { |
| is_login_in_progress_ = false; |
| is_signin_completed_ = true; |
| |
| // Login performer will be gone so cache this value to use |
| // once profile is loaded. |
| CHECK(login_performer_); |
| password_changed_ = login_performer_->password_changed(); |
| auth_mode_ = login_performer_->auth_mode(); |
| |
| ChromeUserManager::Get() |
| ->GetUserFlow(user_context.GetAccountId()) |
| ->HandleLoginSuccess(user_context); |
| |
| StopAutoLoginTimer(); |
| |
| // The hibernate service is not supported, just continue directly. |
| ContinueAuthSuccessAfterResumeAttempt(user_context, true); |
| } |
| |
| void ExistingUserController::ContinueAuthSuccessAfterResumeAttempt( |
| const UserContext& user_context, |
| bool resume_call_success) { |
| // There are three cases that may have led to execution here, and one that |
| // won't: |
| // 1) The ENABLE_HIBERNATE buildflag is not enabled, so this function was |
| // simply called directly by OnAuthSuccess. Pretend the call out was a |
| // success by passing true for resume_call_success. |
| // 2) There was a hibernation image primed for resume, and we resumed to it. |
| // In that case execution never gets here, as the resumed image will have |
| // replaced this world. |
| // 3) There was no hibernation image primed for resume, the resume was |
| // cancelled, or the resume was aborted. In that case this will be running |
| // with resume_call_success == true, indicating the hibernate daemon was |
| // called, but opted to return control. |
| // 4) Chrome failed to make contact with the hiberman daemon at all, in which |
| // case resume_call_success is false. Print an error here, as it represents |
| // a broken link in the chain. |
| if (!resume_call_success) { |
| LOG(ERROR) << "Failed to call ResumeFromHibernate, continuing with login"; |
| } |
| |
| // Truth table of `has_auth_cookies`: |
| // Regular SAML |
| // /ServiceLogin T T |
| // /ChromeOsEmbeddedSetup F T |
| CHECK(login_performer_); |
| const bool has_auth_cookies = |
| login_performer_->auth_mode() == |
| LoginPerformer::AuthorizationMode::kExternal && |
| (user_context.GetAccessToken().empty() || |
| user_context.GetAuthFlow() == UserContext::AUTH_FLOW_GAIA_WITH_SAML); |
| |
| // LoginPerformer instance will delete itself in case of successful auth. |
| login_performer_->set_delegate(nullptr); |
| std::ignore = login_performer_.release(); |
| |
| const bool is_enterprise_managed = g_browser_process->platform_part() |
| ->browser_policy_connector_ash() |
| ->IsDeviceEnterpriseManaged(); |
| |
| // Mark device will be consumer owned if the device is not managed and this is |
| // the first user on the device. |
| if (!is_enterprise_managed && |
| user_manager::UserManager::Get()->GetUsers().empty()) { |
| DeviceSettingsService::Get()->MarkWillEstablishConsumerOwnership(); |
| } |
| |
| if (user_context.CanLockManagedGuestSession()) { |
| CHECK(user_context.GetUserType() == user_manager::USER_TYPE_PUBLIC_ACCOUNT); |
| user_manager::User* user = |
| user_manager::UserManager::Get()->FindUserAndModify( |
| user_context.GetAccountId()); |
| DCHECK(user); |
| user->AddProfileCreatedObserver( |
| base::BindOnce(&SetLoginExtensionApiCanLockManagedGuestSessionPref, |
| user_context.GetAccountId(), true)); |
| } |
| |
| if (BrowserDataMigratorImpl::MaybeForceResumeMoveMigration( |
| g_browser_process->local_state(), user_context.GetAccountId(), |
| user_context.GetUserIDHash(), |
| crosapi::browser_util::PolicyInitState::kAfterInit)) { |
| // TODO(crbug.com/1261730): Add an UMA. |
| LOG(WARNING) << "Restarting Chrome to resume move migration."; |
| return; |
| } |
| |
| UserSessionManager::StartSessionType start_session_type = |
| UserAddingScreen::Get()->IsRunning() |
| ? UserSessionManager::StartSessionType::kSecondary |
| : UserSessionManager::StartSessionType::kPrimary; |
| UserSessionManager::GetInstance()->StartSession( |
| user_context, start_session_type, has_auth_cookies, |
| false, // Start session for user. |
| AsWeakPtr()); |
| |
| // Update user's displayed email. |
| if (!display_email_.empty()) { |
| user_manager::UserManager::Get()->SaveUserDisplayEmail( |
| user_context.GetAccountId(), display_email_); |
| } |
| if (!display_name_.empty() || !given_name_.empty()) { |
| user_manager::UserManager::Get()->UpdateUserAccountData( |
| user_context.GetAccountId(), |
| user_manager::UserManager::UserAccountData(display_name_, given_name_, |
| std::string() /* locale */)); |
| } |
| ClearRecordedNames(); |
| |
| if (public_session_auto_login_account_id_.is_valid() && |
| public_session_auto_login_account_id_ == user_context.GetAccountId() && |
| last_login_attempt_was_auto_login_) { |
| const std::string& user_id = user_context.GetAccountId().GetUserEmail(); |
| policy::DeviceLocalAccountPolicyBroker* broker = |
| g_browser_process->platform_part() |
| ->browser_policy_connector_ash() |
| ->GetDeviceLocalAccountPolicyService() |
| ->GetBrokerForUser(user_id); |
| bool privacy_warnings_enabled = |
| g_browser_process->local_state()->GetBoolean( |
| prefs::kManagedGuestSessionPrivacyWarningsEnabled); |
| if (ChromeUserManager::Get()->IsFullManagementDisclosureNeeded(broker) && |
| privacy_warnings_enabled) { |
| ShowAutoLaunchManagedGuestSessionNotification(); |
| } |
| } |
| if (is_enterprise_managed) { |
| enterprise_user_session_metrics::RecordSignInEvent( |
| user_context, last_login_attempt_was_auto_login_); |
| } |
| } |
| |
| void ExistingUserController::ShowAutoLaunchManagedGuestSessionNotification() { |
| policy::BrowserPolicyConnectorAsh* connector = |
| g_browser_process->platform_part()->browser_policy_connector_ash(); |
| DCHECK(connector->IsDeviceEnterpriseManaged()); |
| message_center::RichNotificationData data; |
| data.buttons.push_back(message_center::ButtonInfo( |
| l10n_util::GetStringUTF16(IDS_AUTO_LAUNCH_NOTIFICATION_BUTTON))); |
| const std::u16string title = |
| l10n_util::GetStringUTF16(IDS_AUTO_LAUNCH_NOTIFICATION_TITLE); |
| const std::u16string message = l10n_util::GetStringFUTF16( |
| IDS_ASH_LOGIN_MANAGED_SESSION_MONITORING_FULL_WARNING, |
| base::UTF8ToUTF16(connector->GetEnterpriseDomainManager())); |
| auto delegate = |
| base::MakeRefCounted<message_center::HandleNotificationClickDelegate>( |
| base::BindRepeating([](absl::optional<int> button_index) { |
| DCHECK(button_index); |
| SystemTrayClientImpl::Get()->ShowEnterpriseInfo(); |
| })); |
| message_center::Notification notification = CreateSystemNotification( |
| message_center::NOTIFICATION_TYPE_SIMPLE, kAutoLaunchNotificationId, |
| title, message, std::u16string(), GURL(), |
| message_center::NotifierId(message_center::NotifierType::SYSTEM_COMPONENT, |
| kAutoLaunchNotifierId, |
| NotificationCatalogName::kAutoLaunch), |
| data, std::move(delegate), vector_icons::kBusinessIcon, |
| message_center::SystemNotificationWarningLevel::NORMAL); |
| notification.SetSystemPriority(); |
| notification.set_pinned(true); |
| SystemNotificationHelper::GetInstance()->Display(notification); |
| } |
| |
| void ExistingUserController::OnProfilePrepared(Profile* profile, |
| bool browser_launched) { |
| // Reenable clicking on other windows and status area. |
| GetLoginDisplay()->SetUIEnabled(true); |
| |
| profile_prepared_ = true; |
| |
| UserContext user_context = |
| UserContext(*ProfileHelper::Get()->GetUserByProfile(profile)); |
| auto* profile_connector = profile->GetProfilePolicyConnector(); |
| bool is_enterprise_managed = |
| profile_connector->IsManaged() && |
| user_context.GetUserType() != user_manager::USER_TYPE_CHILD; |
| |
| user_manager::KnownUser known_user(g_browser_process->local_state()); |
| known_user.SetIsEnterpriseManaged(user_context.GetAccountId(), |
| is_enterprise_managed); |
| |
| if (is_enterprise_managed) { |
| absl::optional<std::string> manager = |
| chrome::GetAccountManagerIdentity(profile); |
| if (manager) { |
| known_user.SetAccountManager(user_context.GetAccountId(), *manager); |
| } |
| } |
| |
| // Inform `auth_status_consumers_` about successful login. |
| // TODO(nkostylev): Pass UserContext back crbug.com/424550 |
| for (auto& auth_status_consumer : auth_status_consumers_) |
| auth_status_consumer.OnAuthSuccess(user_context); |
| } |
| |
| void ExistingUserController::OnOffTheRecordAuthSuccess() { |
| // Do not reset is_login_in_progress_ flag: |
| // CompleteGuestSessionLogin() below should result in browser restart |
| // that would actually complete the login process. |
| |
| // Mark the device as registered., i.e. the second part of OOBE as completed. |
| if (!StartupUtils::IsDeviceRegistered()) |
| StartupUtils::MarkDeviceRegistered(base::OnceClosure()); |
| |
| UserSessionManager::GetInstance()->CompleteGuestSessionLogin(guest_mode_url_); |
| |
| for (auto& auth_status_consumer : auth_status_consumers_) |
| auth_status_consumer.OnOffTheRecordAuthSuccess(); |
| } |
| |
| void ExistingUserController::OnPasswordChangeDetectedLegacy( |
| const UserContext& user_context) { |
| DCHECK(!ash::features::IsCryptohomeRecoveryEnabled()); |
| is_login_in_progress_ = false; |
| |
| // Must not proceed without signature verification. |
| if (CrosSettingsProvider::TRUSTED != |
| cros_settings_->PrepareTrustedValues(base::BindOnce( |
| &ExistingUserController::OnPasswordChangeDetectedLegacy, |
| weak_factory_.GetWeakPtr(), user_context))) { |
| // Value of owner email is still not verified. |
| // Another attempt will be invoked after verification completion. |
| return; |
| } |
| |
| for (auto& auth_status_consumer : auth_status_consumers_) |
| auth_status_consumer.OnPasswordChangeDetectedLegacy(user_context); |
| |
| ShowPasswordChangedDialogLegacy(user_context); |
| } |
| |
| void ExistingUserController::OnPasswordChangeDetected( |
| std::unique_ptr<UserContext> user_context) { |
| // Workaround for PrepareTrustedValues and need to move unique_ptr: |
| base::OnceClosure callback = |
| base::BindOnce(&ExistingUserController::OnPasswordChangeDetectedImpl, |
| weak_factory_.GetWeakPtr(), std::move(user_context)); |
| auto [continue_async, continue_now] = |
| base::SplitOnceCallback(std::move(callback)); |
| // Must not proceed without signature verification. |
| if (CrosSettingsProvider::TRUSTED != |
| cros_settings_->PrepareTrustedValues(std::move(continue_async))) { |
| // Value of owner email is still not verified. |
| // Callback will be invoked after verification completion. |
| return; |
| } |
| std::move(continue_now).Run(); |
| } |
| |
| void ExistingUserController::OnPasswordChangeDetectedImpl( |
| std::unique_ptr<UserContext> user_context) { |
| DCHECK(ash::features::IsCryptohomeRecoveryEnabled()); |
| DCHECK(user_context); |
| is_login_in_progress_ = false; |
| |
| for (auto& auth_status_consumer : auth_status_consumers_) { |
| auth_status_consumer.OnPasswordChangeDetectedFor( |
| user_context->GetAccountId()); |
| } |
| |
| GetLoginDisplayHost()->GetSigninUI()->StartCryptohomeRecovery( |
| std::move(user_context)); |
| } |
| |
| void ExistingUserController::OnOldEncryptionDetected( |
| std::unique_ptr<UserContext> user_context, |
| bool has_incomplete_migration) { |
| absl::optional<EncryptionMigrationMode> encryption_migration_mode = |
| GetEncryptionMigrationMode(*user_context, has_incomplete_migration); |
| CHECK(login_performer_); |
| if (!encryption_migration_mode.has_value()) { |
| ContinuePerformLoginWithoutMigration(login_performer_->auth_mode(), |
| std::move(user_context)); |
| return; |
| } |
| ShowEncryptionMigrationScreen(std::move(user_context), |
| encryption_migration_mode.value()); |
| } |
| |
| void ExistingUserController::ForceOnlineLoginForAccountId( |
| const AccountId& account_id) { |
| // Save the necessity to sign-in online into UserManager in case the user |
| // aborts the online flow. |
| user_manager::UserManager::Get()->SaveForceOnlineSignin(account_id, true); |
| |
| // Start online sign-in UI for the user. |
| is_login_in_progress_ = false; |
| login_performer_.reset(); |
| if (session_manager::SessionManager::Get()->IsInSecondaryLoginScreen()) { |
| // Gaia dialog is not supported on the secondary login screen. |
| return; |
| } |
| GetLoginDisplayHost()->ShowGaiaDialog(account_id); |
| } |
| |
| void ExistingUserController::AllowlistCheckFailed(const std::string& email) { |
| PerformLoginFinishedActions(true /* start auto login timer */); |
| |
| GetLoginDisplayHost()->ShowAllowlistCheckFailedError(); |
| |
| for (auto& auth_status_consumer : auth_status_consumers_) { |
| auth_status_consumer.OnAuthFailure( |
| AuthFailure(AuthFailure::ALLOWLIST_CHECK_FAILED)); |
| } |
| |
| ClearActiveDirectoryState(); |
| ClearRecordedNames(); |
| } |
| |
| void ExistingUserController::PolicyLoadFailed() { |
| ShowError(SigninError::kOwnerKeyLost, std::string()); |
| |
| PerformLoginFinishedActions(false /* don't start auto login timer */); |
| ClearActiveDirectoryState(); |
| ClearRecordedNames(); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // ExistingUserController, private: |
| |
| void ExistingUserController::DeviceSettingsChanged() { |
| // If login was already completed, we should avoid any signin screen |
| // transitions, see http://crbug.com/461604 for example. |
| if (!profile_prepared_ && !is_signin_completed_) { |
| // Signed settings or user list changed. Notify views and update them. |
| const user_manager::UserList& users = |
| UserAddingScreen::Get()->IsRunning() |
| ? user_manager::UserManager::Get()->GetUsersAllowedForMultiProfile() |
| : user_manager::UserManager::Get()->GetUsers(); |
| |
| UpdateLoginDisplay(users); |
| ConfigureAutoLogin(); |
| } |
| } |
| |
| void ExistingUserController::AddLoginStatusConsumer( |
| AuthStatusConsumer* consumer) { |
| auth_status_consumers_.AddObserver(consumer); |
| } |
| |
| void ExistingUserController::RemoveLoginStatusConsumer( |
| const AuthStatusConsumer* consumer) { |
| auth_status_consumers_.RemoveObserver(consumer); |
| } |
| |
| LoginPerformer::AuthorizationMode ExistingUserController::auth_mode() const { |
| if (login_performer_) |
| return login_performer_->auth_mode(); |
| |
| return auth_mode_; |
| } |
| |
| bool ExistingUserController::password_changed() const { |
| if (login_performer_) |
| return login_performer_->password_changed(); |
| |
| return password_changed_; |
| } |
| |
| // static |
| user_manager::UserList ExistingUserController::ExtractLoginUsers( |
| const user_manager::UserList& users) { |
| bool show_users_on_signin; |
| CrosSettings::Get()->GetBoolean(kAccountsPrefShowUserNamesOnSignIn, |
| &show_users_on_signin); |
| user_manager::UserList filtered_users; |
| for (auto* user : users) { |
| // Skip kiosk apps for login screen user list. Kiosk apps as pods (aka new |
| // kiosk UI) is currently disabled and it gets the apps directly from |
| // KioskAppManager, ArcKioskAppManager and WebKioskAppManager. |
| if (user->IsKioskType()) |
| continue; |
| const bool meets_allowlist_requirements = |
| !user->HasGaiaAccount() || |
| user_manager::UserManager::Get()->IsGaiaUserAllowed(*user); |
| // Public session accounts are always shown on login screen. |
| const bool meets_show_users_requirements = |
| show_users_on_signin || |
| user->GetType() == user_manager::USER_TYPE_PUBLIC_ACCOUNT; |
| if (meets_allowlist_requirements && meets_show_users_requirements) |
| filtered_users.push_back(user); |
| } |
| return filtered_users; |
| } |
| |
| void ExistingUserController::LoginAuthenticated( |
| std::unique_ptr<UserContext> user_context) { |
| CHECK(login_performer_); |
| login_performer_->LoginAuthenticated(std::move(user_context)); |
| } |
| |
| void ExistingUserController::LoginAsGuest() { |
| PerformPreLoginActions(UserContext(user_manager::USER_TYPE_GUEST, |
| user_manager::GuestAccountId())); |
| |
| bool allow_guest = user_manager::UserManager::Get()->IsGuestSessionAllowed(); |
| if (!allow_guest) { |
| // Disallowed. The UI should normally not show the guest session button. |
| LOG(ERROR) << "Guest login attempt when guest mode is disallowed."; |
| PerformLoginFinishedActions(true /* start auto login timer */); |
| ClearRecordedNames(); |
| return; |
| } |
| |
| // Only one instance of LoginPerformer should exist at a time. |
| login_performer_.reset(nullptr); |
| login_performer_ = |
| std::make_unique<ChromeLoginPerformer>(this, AuthMetricsRecorder::Get()); |
| login_performer_->LoginOffTheRecord(); |
| SendAccessibilityAlert( |
| l10n_util::GetStringUTF8(IDS_CHROMEOS_ACC_LOGIN_SIGNIN_OFFRECORD)); |
| } |
| |
| void ExistingUserController::LoginAsPublicSession( |
| const UserContext& user_context) { |
| VLOG(2) << "LoginAsPublicSession"; |
| PerformPreLoginActions(user_context); |
| |
| // If there is no public account with the given user ID, logging in is not |
| // possible. |
| const user_manager::User* user = |
| user_manager::UserManager::Get()->FindUser(user_context.GetAccountId()); |
| if (!user || user->GetType() != user_manager::USER_TYPE_PUBLIC_ACCOUNT) { |
| VLOG(2) << "Public session user not found"; |
| PerformLoginFinishedActions(true /* start auto login timer */); |
| return; |
| } |
| |
| // Public session login will fail if attempted if the associated policy |
| // is not ready - wait for the policy to become available before starting the |
| // auto-login timer. |
| policy::BrowserPolicyConnectorAsh* connector = |
| g_browser_process->platform_part()->browser_policy_connector_ash(); |
| policy::DeviceLocalAccountPolicyService* policy_service = |
| connector->GetDeviceLocalAccountPolicyService(); |
| const auto& user_id = user_context.GetAccountId().GetUserEmail(); |
| DCHECK(policy_service); |
| if (!policy_service->IsPolicyAvailableForUser(user_id)) { |
| VLOG(2) << "Policies are not yet available for public session"; |
| policy_waiter_ = std::make_unique<DeviceLocalAccountPolicyWaiter>( |
| policy_service, |
| base::BindOnce( |
| &ExistingUserController::LoginAsPublicSessionWhenPolicyAvailable, |
| base::Unretained(this), user_context), |
| user_id); |
| |
| return; |
| } |
| |
| LoginAsPublicSessionWhenPolicyAvailable(user_context); |
| } |
| |
| void ExistingUserController::LoginAsPublicSessionWhenPolicyAvailable( |
| const UserContext& user_context) { |
| VLOG(2) << __func__; |
| policy_waiter_.reset(); |
| |
| UserContext new_user_context = user_context; |
| std::string locale = user_context.GetPublicSessionLocale(); |
| if (locale.empty()) { |
| // When performing auto-login, no locale is chosen by the user. Check |
| // whether a list of recommended locales was set by policy. If so, use its |
| // first entry. Otherwise, `locale` will remain blank, indicating that the |
| // public session should use the current UI locale. |
| const policy::PolicyMap::Entry* entry = |
| g_browser_process->platform_part() |
| ->browser_policy_connector_ash() |
| ->GetDeviceLocalAccountPolicyService() |
| ->GetBrokerForUser(user_context.GetAccountId().GetUserEmail()) |
| ->core() |
| ->store() |
| ->policy_map() |
| .Get(policy::key::kSessionLocales); |
| if (entry && entry->level == policy::POLICY_LEVEL_RECOMMENDED && |
| entry->value(base::Value::Type::LIST)) { |
| const base::Value::List& list = |
| entry->value(base::Value::Type::LIST)->GetList(); |
| if (!list.empty() && list[0].is_string()) { |
| locale = list[0].GetString(); |
| new_user_context.SetPublicSessionLocale(locale); |
| } |
| } |
| } |
| |
| if (!locale.empty() && |
| new_user_context.GetPublicSessionInputMethod().empty()) { |
| // When `locale` is set, a suitable keyboard layout should be chosen. In |
| // most cases, this will already be the case because the UI shows a list of |
| // keyboard layouts suitable for the `locale` and ensures that one of them |
| // us selected. However, it is still possible that `locale` is set but no |
| // keyboard layout was chosen: |
| // * The list of keyboard layouts is updated asynchronously. If the user |
| // enters the public session before the list of keyboard layouts for the |
| // `locale` has been retrieved, the UI will indicate that no keyboard |
| // layout was chosen. |
| // * During auto-login, the `locale` is set in this method and a suitable |
| // keyboard layout must be chosen next. |
| // |
| // The list of suitable keyboard layouts is constructed asynchronously. Once |
| // it has been retrieved, `SetPublicSessionKeyboardLayoutAndLogin` will |
| // select the first layout from the list and continue login. |
| VLOG(2) << "Requesting keyboard layouts for public session"; |
| GetKeyboardLayoutsForLocale( |
| base::BindOnce( |
| &ExistingUserController::SetPublicSessionKeyboardLayoutAndLogin, |
| weak_factory_.GetWeakPtr(), new_user_context), |
| locale, input_method::InputMethodManager::Get()); |
| return; |
| } |
| |
| // The user chose a locale and a suitable keyboard layout or left both unset. |
| // Login can continue immediately. |
| LoginAsPublicSessionInternal(new_user_context); |
| } |
| |
| void ExistingUserController::LoginAsKioskApp(KioskAppId kiosk_app_id) { |
| GetLoginDisplayHost()->StartKiosk(kiosk_app_id, /*auto_launch*/ false); |
| } |
| |
| void ExistingUserController::ConfigureAutoLogin() { |
| std::string auto_login_account_id; |
| cros_settings_->GetString(kAccountsPrefDeviceLocalAccountAutoLoginId, |
| &auto_login_account_id); |
| VLOG(2) << "Autologin account in prefs: " << auto_login_account_id; |
| const std::vector<policy::DeviceLocalAccount> device_local_accounts = |
| policy::GetDeviceLocalAccounts(cros_settings_); |
| const bool show_update_required_screen = IsUpdateRequiredDeadlineReached(); |
| |
| public_session_auto_login_account_id_ = GetPublicSessionAutoLoginAccountId( |
| device_local_accounts, auto_login_account_id); |
| |
| const user_manager::User* public_session_user = |
| user_manager::UserManager::Get()->FindUser( |
| public_session_auto_login_account_id_); |
| if (!public_session_user || public_session_user->GetType() != |
| user_manager::USER_TYPE_PUBLIC_ACCOUNT) { |
| VLOG(2) << "PublicSession autologin user not found"; |
| public_session_auto_login_account_id_ = EmptyAccountId(); |
| } |
| |
| if (!cros_settings_->GetInteger(kAccountsPrefDeviceLocalAccountAutoLoginDelay, |
| &auto_login_delay_)) { |
| auto_login_delay_ = 0; |
| } |
| |
| // TODO(crbug.com/1105387): Part of initial screen logic. |
| if (show_update_required_screen) { |
| // Update required screen overrides public session auto login. |
| StopAutoLoginTimer(); |
| GetLoginDisplayHost()->StartWizard(UpdateRequiredView::kScreenId); |
| } else if (public_session_auto_login_account_id_.is_valid()) { |
| StartAutoLoginTimer(); |
| } else { |
| StopAutoLoginTimer(); |
| } |
| } |
| |
| void ExistingUserController::OnUserActivity(const ui::Event* event) { |
| // Only restart the auto-login timer if it's already running. |
| if (auto_login_timer_ && auto_login_timer_->IsRunning()) { |
| StopAutoLoginTimer(); |
| StartAutoLoginTimer(); |
| } |
| } |
| |
| void ExistingUserController::OnPublicSessionAutoLoginTimerFire() { |
| CHECK(public_session_auto_login_account_id_.is_valid()); |
| VLOG(2) << "Public session autologin fired"; |
| SigninSpecifics signin_specifics; |
| signin_specifics.is_auto_login = true; |
| Login(UserContext(user_manager::USER_TYPE_PUBLIC_ACCOUNT, |
| public_session_auto_login_account_id_), |
| signin_specifics); |
| } |
| |
| void ExistingUserController::StopAutoLoginTimer() { |
| VLOG(2) << "Stopping autologin timer that is " |
| << (auto_login_timer_ ? "" : "not ") << "running"; |
| if (auto_login_timer_) |
| auto_login_timer_->Stop(); |
| } |
| |
| void ExistingUserController::CancelPasswordChangedFlow() { |
| login_performer_.reset(nullptr); |
| ClearActiveDirectoryState(); |
| PerformLoginFinishedActions(true /* start auto login timer */); |
| } |
| |
| void ExistingUserController::MigrateUserData(const std::string& old_password) { |
| // LoginPerformer instance has state of the user so it should exist. |
| if (login_performer_.get()) { |
| VLOG(1) << "Migrate the existing cryptohome to new password."; |
| login_performer_->RecoverEncryptedData(old_password); |
| } |
| } |
| |
| void ExistingUserController::ResyncUserData() { |
| // LoginPerformer instance has state of the user so it should exist. |
| if (login_performer_.get()) { |
| VLOG(1) << "Create a new cryptohome and resync user data."; |
| login_performer_->ResyncEncryptedData(); |
| } |
| } |
| |
| void ExistingUserController::StartAutoLoginTimer() { |
| auto session_state = session_manager::SessionManager::Get()->session_state(); |
| if (is_login_in_progress_ || |
| !public_session_auto_login_account_id_.is_valid() || |
| (session_state == session_manager::SessionState::OOBE && |
| !DemoSession::IsDeviceInDemoMode())) { |
| VLOG(2) << "Not starting autologin timer, because:"; |
| VLOG_IF(2, is_login_in_progress_) << "* Login is in process;"; |
| VLOG_IF(2, !public_session_auto_login_account_id_.is_valid()) |
| << "* No valid autologin account;"; |
| VLOG_IF(2, session_state == session_manager::SessionState::OOBE && |
| !DemoSession::IsDeviceInDemoMode()) |
| << "* OOBE isn't completed and device isn't in demo mode;"; |
| return; |
| } |
| VLOG(2) << "Starting autologin timer with delay: " << auto_login_delay_; |
| |
| if (auto_login_timer_ && auto_login_timer_->IsRunning()) { |
| StopAutoLoginTimer(); |
| } |
| |
| // Start the auto-login timer. |
| if (!auto_login_timer_) |
| auto_login_timer_ = std::make_unique<base::OneShotTimer>(); |
| |
| VLOG(2) << "Public session autologin will be fired in " << auto_login_delay_ |
| << "ms"; |
| auto_login_timer_->Start( |
| FROM_HERE, base::Milliseconds(auto_login_delay_), |
| base::BindOnce(&ExistingUserController::OnPublicSessionAutoLoginTimerFire, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void ExistingUserController::ShowError(SigninError error, |
| const std::string& details) { |
| VLOG(1) << details; |
| auto* signin_ui = GetLoginDisplayHost()->GetSigninUI(); |
| if (!signin_ui) { |
| DCHECK(session_manager::SessionManager::Get()->IsInSecondaryLoginScreen()); |
| // Silently ignore the error on the secondary login screen. The screen is |
| // being deprecated anyway. |
| return; |
| } |
| signin_ui->ShowSigninError(error, details); |
| } |
| |
| void ExistingUserController::SendAccessibilityAlert( |
| const std::string& alert_text) { |
| AutomationManagerAura::GetInstance()->HandleAlert(alert_text); |
| } |
| |
| void ExistingUserController::SetPublicSessionKeyboardLayoutAndLogin( |
| const UserContext& user_context, |
| base::Value::List keyboard_layouts) { |
| UserContext new_user_context = user_context; |
| std::string keyboard_layout; |
| for (auto& entry : keyboard_layouts) { |
| base::Value::Dict& entry_dict = entry.GetDict(); |
| if (entry_dict.FindBool("selected").value_or(false)) { |
| const std::string* keyboard_layout_ptr = entry_dict.FindString("value"); |
| if (keyboard_layout_ptr) |
| keyboard_layout = *keyboard_layout_ptr; |
| break; |
| } |
| } |
| DCHECK(!keyboard_layout.empty()); |
| new_user_context.SetPublicSessionInputMethod(keyboard_layout); |
| |
| LoginAsPublicSessionInternal(new_user_context); |
| } |
| |
| void ExistingUserController::LoginAsPublicSessionInternal( |
| const UserContext& user_context) { |
| // Only one instance of LoginPerformer should exist at a time. |
| VLOG(2) << "LoginAsPublicSessionInternal for user: " |
| << user_context.GetAccountId(); |
| login_performer_.reset(nullptr); |
| login_performer_ = |
| std::make_unique<ChromeLoginPerformer>(this, AuthMetricsRecorder::Get()); |
| login_performer_->LoginAsPublicSession(user_context); |
| SendAccessibilityAlert( |
| l10n_util::GetStringUTF8(IDS_CHROMEOS_ACC_LOGIN_SIGNIN_PUBLIC_ACCOUNT)); |
| } |
| |
| void ExistingUserController::PerformPreLoginActions( |
| const UserContext& user_context) { |
| // Disable clicking on other windows and status tray. |
| GetLoginDisplay()->SetUIEnabled(false); |
| |
| if (last_login_attempt_account_id_ != user_context.GetAccountId()) { |
| last_login_attempt_account_id_ = user_context.GetAccountId(); |
| num_login_attempts_ = 0; |
| } |
| num_login_attempts_++; |
| |
| // Stop the auto-login timer when attempting login. |
| StopAutoLoginTimer(); |
| } |
| |
| void ExistingUserController::PerformLoginFinishedActions( |
| bool start_auto_login_timer) { |
| is_login_in_progress_ = false; |
| |
| // Reenable clicking on other windows and status area. |
| GetLoginDisplay()->SetUIEnabled(true); |
| |
| if (start_auto_login_timer) |
| StartAutoLoginTimer(); |
| } |
| |
| void ExistingUserController::ContinueLoginWhenCryptohomeAvailable( |
| base::OnceClosure continuation, |
| bool service_is_available) { |
| if (!service_is_available) { |
| LOG(ERROR) << "Cryptohome service is not available"; |
| OnAuthFailure(AuthFailure(AuthFailure::COULD_NOT_MOUNT_CRYPTOHOME)); |
| return; |
| } |
| std::move(continuation).Run(); |
| } |
| |
| void ExistingUserController::ContinueLoginIfDeviceNotDisabled( |
| base::OnceClosure continuation) { |
| // Disable clicking on other windows and status tray. |
| GetLoginDisplay()->SetUIEnabled(false); |
| |
| // Stop the auto-login timer. |
| StopAutoLoginTimer(); |
| |
| auto split_continuation = base::SplitOnceCallback(std::move(continuation)); |
| |
| // Wait for the `cros_settings_` to become either trusted or permanently |
| // untrusted. |
| const CrosSettingsProvider::TrustedStatus status = |
| cros_settings_->PrepareTrustedValues(base::BindOnce( |
| &ExistingUserController::ContinueLoginIfDeviceNotDisabled, |
| weak_factory_.GetWeakPtr(), std::move(split_continuation.first))); |
| if (status == CrosSettingsProvider::TEMPORARILY_UNTRUSTED) |
| return; |
| |
| if (status == CrosSettingsProvider::PERMANENTLY_UNTRUSTED) { |
| // If the `cros_settings_` are permanently untrusted, show an error message |
| // and refuse to log in. |
| ++num_login_attempts_; |
| ShowError(SigninError::kOwnerKeyLost, /*details=*/std::string()); |
| |
| // Re-enable clicking on other windows and the status area. Do not start the |
| // auto-login timer though. Without trusted `cros_settings_`, no auto-login |
| // can succeed. |
| GetLoginDisplay()->SetUIEnabled(true); |
| return; |
| } |
| |
| if (system::DeviceDisablingManager::IsDeviceDisabledDuringNormalOperation()) { |
| // If the device is disabled, bail out. A device disabled screen will be |
| // shown by the DeviceDisablingManager. |
| |
| // Re-enable clicking on other windows and the status area. Do not start the |
| // auto-login timer though. On a disabled device, no auto-login can succeed. |
| GetLoginDisplay()->SetUIEnabled(true); |
| return; |
| } |
| |
| UserDataAuthClient::Get()->WaitForServiceToBeAvailable(base::BindOnce( |
| &ExistingUserController::ContinueLoginWhenCryptohomeAvailable, |
| weak_factory_.GetWeakPtr(), std::move(split_continuation.second))); |
| } |
| |
| void ExistingUserController::DoCompleteLogin( |
| const UserContext& user_context_wo_device_id) { |
| UserContext user_context = user_context_wo_device_id; |
| user_manager::KnownUser known_user(g_browser_process->local_state()); |
| std::string device_id = known_user.GetDeviceId(user_context.GetAccountId()); |
| if (device_id.empty()) { |
| const bool is_ephemeral = ChromeUserManager::Get()->IsEphemeralAccountId( |
| user_context.GetAccountId()) && |
| user_context.GetAccountId() != |
| ChromeUserManager::Get()->GetOwnerAccountId(); |
| device_id = GenerateSigninScopedDeviceId(is_ephemeral); |
| } |
| user_context.SetDeviceId(device_id); |
| |
| const std::string& gaps_cookie = user_context.GetGAPSCookie(); |
| if (!gaps_cookie.empty()) { |
| known_user.SetGAPSCookie(user_context.GetAccountId(), gaps_cookie); |
| } |
| |
| PerformPreLoginActions(user_context); |
| |
| if (timer_init_) { |
| base::UmaHistogramMediumTimes("Login.PromptToCompleteLoginTime", |
| timer_init_->Elapsed()); |
| timer_init_.reset(); |
| } |
| |
| // Fetch OAuth2 tokens if we have an auth code. |
| if (!user_context.GetAuthCode().empty()) { |
| oauth2_token_initializer_ = std::make_unique<OAuth2TokenInitializer>(); |
| oauth2_token_initializer_->Start( |
| user_context, |
| base::BindOnce(&ExistingUserController::OnOAuth2TokensFetched, |
| weak_factory_.GetWeakPtr())); |
| return; |
| } |
| |
| PerformLogin(user_context, LoginPerformer::AuthorizationMode::kExternal); |
| } |
| |
| void ExistingUserController::DoLogin(const UserContext& user_context, |
| const SigninSpecifics& specifics) { |
| last_login_attempt_was_auto_login_ = specifics.is_auto_login; |
| VLOG(2) << "DoLogin with a user type: " << user_context.GetUserType(); |
| |
| if (user_context.GetUserType() == user_manager::USER_TYPE_GUEST) { |
| if (!specifics.guest_mode_url.empty()) { |
| guest_mode_url_ = GURL(specifics.guest_mode_url); |
| if (specifics.guest_mode_url_append_locale) |
| guest_mode_url_ = google_util::AppendGoogleLocaleParam( |
| guest_mode_url_, g_browser_process->GetApplicationLocale()); |
| } |
| LoginAsGuest(); |
| return; |
| } |
| |
| if (user_context.GetUserType() == user_manager::USER_TYPE_PUBLIC_ACCOUNT) { |
| LoginAsPublicSession(user_context); |
| return; |
| } |
| |
| if (user_context.GetUserType() == user_manager::USER_TYPE_KIOSK_APP) { |
| LoginAsKioskApp( |
| KioskAppId::ForChromeApp(user_context.GetAccountId().GetUserEmail())); |
| return; |
| } |
| |
| if (user_context.GetUserType() == user_manager::USER_TYPE_ARC_KIOSK_APP) { |
| LoginAsKioskApp(KioskAppId::ForArcApp(user_context.GetAccountId())); |
| return; |
| } |
| |
| if (user_context.GetUserType() == user_manager::USER_TYPE_WEB_KIOSK_APP) { |
| LoginAsKioskApp(KioskAppId::ForWebApp(user_context.GetAccountId())); |
| return; |
| } |
| |
| // Regular user or supervised user login. |
| |
| if (!user_context.HasCredentials()) { |
| // If credentials are missing, refuse to log in. |
| |
| // Ensure WebUI is loaded to allow security token dialog to pop up. |
| GetLoginDisplayHost()->GetWizardController(); |
| // Reenable clicking on other windows and status area. |
| GetLoginDisplay()->SetUIEnabled(true); |
| // Restart the auto-login timer. |
| StartAutoLoginTimer(); |
| } |
| |
| PerformPreLoginActions(user_context); |
| PerformLogin(user_context, LoginPerformer::AuthorizationMode::kInternal); |
| } |
| |
| void ExistingUserController::OnOAuth2TokensFetched( |
| bool success, |
| const UserContext& user_context) { |
| if (!success) { |
| LOG(ERROR) << "OAuth2 token fetch failed."; |
| OnAuthFailure(AuthFailure(AuthFailure::FAILED_TO_INITIALIZE_TOKEN)); |
| return; |
| } |
| PerformLogin(user_context, LoginPerformer::AuthorizationMode::kExternal); |
| } |
| |
| void ExistingUserController::ClearRecordedNames() { |
| display_email_.clear(); |
| display_name_.clear(); |
| given_name_.clear(); |
| } |
| |
| void ExistingUserController::ClearActiveDirectoryState() { |
| if (last_login_attempt_account_id_.GetAccountType() != |
| AccountType::ACTIVE_DIRECTORY) { |
| return; |
| } |
| // Clear authpolicyd state so nothing could leak from one user to another. |
| AuthPolicyHelper::Restart(); |
| } |
| |
| AccountId ExistingUserController::GetLastLoginAttemptAccountId() const { |
| return last_login_attempt_account_id_; |
| } |
| |
| } // namespace ash |