blob: 1053ec89939d46ee3443b364d1624406cbcd9414 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ash/login/existing_user_controller.h"
#include <memory>
#include <utility>
#include <vector>
#include "ash/components/arc/arc_util.h"
#include "ash/components/arc/enterprise/arc_data_snapshotd_manager.h"
#include "ash/components/login/auth/key.h"
#include "ash/components/login/session/session_termination_manager.h"
#include "ash/components/settings/cros_settings_names.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/constants/ash_switches.h"
#include "ash/public/cpp/login_screen.h"
#include "ash/public/cpp/notification_utils.h"
#include "base/barrier_closure.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/callback_helpers.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/ignore_result.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/threading/sequenced_task_runner_handle.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/easy_unlock/easy_unlock_service.h"
#include "chrome/browser/ash/login/enterprise_user_session_metrics.h"
#include "chrome/browser/ash/login/helper.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/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/policy/handlers/powerwash_requirements_checker.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.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/profiles/profile_manager.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/chromeos/login/encryption_migration_screen_handler.h"
#include "chrome/browser/ui/webui/chromeos/login/kiosk_autolaunch_screen_handler.h"
#include "chrome/browser/ui/webui/chromeos/login/kiosk_enable_screen_handler.h"
#include "chrome/browser/ui/webui/chromeos/login/l10n_util.h"
#include "chrome/browser/ui/webui/chromeos/login/tpm_error_screen_handler.h"
#include "chrome/browser/ui/webui/chromeos/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/cryptohome/cryptohome_parameters.h"
#include "chromeos/cryptohome/cryptohome_util.h"
#include "chromeos/dbus/power/power_manager_client.h"
#include "chromeos/dbus/session_manager/session_manager_client.h"
#include "chromeos/dbus/userdataauth/userdataauth_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/cloud_policy_store.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/message_center/public/cpp/notification.h"
#include "ui/message_center/public/cpp/notification_delegate.h"
#include "ui/views/widget/widget.h"
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 transferring the auth cache to the system profile.
const long int kAuthCacheTransferDelayMs = 2000;
// 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));
}
void TransferHttpAuthCacheToSystemNetworkContext(
base::RepeatingClosure completion_callback,
const base::UnguessableToken& cache_key) {
network::mojom::NetworkContext* system_network_context =
g_browser_process->system_network_context_manager()->GetContext();
system_network_context->LoadHttpAuthCacheProxyEntries(cache_key,
completion_callback);
}
// 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));
}
// Record UMA for password login of regular user when Signin with Smart Lock is
// enabled. Excludes signins in the multi-signin context; only records for the
// signin screen context.
void RecordPasswordLoginEvent(const UserContext& user_context) {
// If a user is already logged in, this is a multi-signin attempt. Disregard.
if (session_manager::SessionManager::Get()->IsInSecondaryLoginScreen())
return;
EasyUnlockService* easy_unlock_service =
EasyUnlockService::Get(ProfileHelper::GetSigninProfile());
if (user_context.GetUserType() == user_manager::USER_TYPE_REGULAR &&
user_context.GetAuthFlow() == UserContext::AUTH_FLOW_OFFLINE &&
easy_unlock_service) {
easy_unlock_service->RecordPasswordLoginEvent(user_context.GetAccountId());
}
}
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(
chromeos::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 SetLoginExtensionApiLaunchExtensionIdPref(const AccountId& account_id,
const std::string extension_id) {
const user_manager::User* user =
user_manager::UserManager::Get()->FindUser(account_id);
DCHECK(user);
Profile* profile = chromeos::ProfileHelper::Get()->GetProfileByUser(user);
DCHECK(profile);
PrefService* prefs = profile->GetPrefs();
prefs->SetString(::prefs::kLoginExtensionApiLaunchExtensionId, extension_id);
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;
}
const bool profile_has_policy =
user_manager::known_user::GetProfileRequiresPolicy(
user_context.GetAccountId()) ==
user_manager::ProfileRequiresPolicy::kPolicyRequired ||
base::CommandLine::ForCurrentProcess()->HasSwitch(
chromeos::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 of a public session account if it is unique, otherwise
// returns invalid account ID.
AccountId GetArcDataSnapshotAutoLoginAccountId(
const std::vector<policy::DeviceLocalAccount>& device_local_accounts) {
AccountId auto_login_account_id = EmptyAccountId();
for (std::vector<policy::DeviceLocalAccount>::const_iterator it =
device_local_accounts.begin();
it != device_local_accounts.end(); ++it) {
if (it->type == policy::DeviceLocalAccount::TYPE_PUBLIC_SESSION) {
// Do not perform ARC data snapshot auto-login if more than one public
// session account is configured.
if (auto_login_account_id.is_valid())
return EmptyAccountId();
auto_login_account_id = AccountId::FromUserEmail(it->user_id);
VLOG(2) << "PublicSession autologin found: " << it->user_id;
}
}
return auto_login_account_id;
}
// 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();
}
} // namespace
// Utility class used to wait for a Public Session policy store load if public
// session login is requested before the associated policy store is loaded.
// When the store gets loaded, it will run the callback passed to the
// constructor.
class ExistingUserController::PolicyStoreLoadWaiter
: public policy::CloudPolicyStore::Observer {
public:
PolicyStoreLoadWaiter(policy::CloudPolicyStore* store,
base::OnceClosure callback)
: callback_(std::move(callback)) {
DCHECK(!store->is_initialized());
scoped_observation_.Observe(store);
}
~PolicyStoreLoadWaiter() override = default;
PolicyStoreLoadWaiter(const PolicyStoreLoadWaiter& other) = delete;
PolicyStoreLoadWaiter& operator=(const PolicyStoreLoadWaiter& other) = delete;
// policy::CloudPolicyStore::Observer:
void OnStoreLoaded(policy::CloudPolicyStore* store) override {
scoped_observation_.Reset();
std::move(callback_).Run();
}
void OnStoreError(policy::CloudPolicyStore* store) override {
// If store load fails, run the callback to unblock public session login
// attempt, which will likely fail.
scoped_observation_.Reset();
std::move(callback_).Run();
}
private:
base::OnceClosure callback_;
base::ScopedObservation<policy::CloudPolicyStore,
policy::CloudPolicyStore::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) {
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_new_user_subscription_ = cros_settings_->AddSettingsObserver(
kAccountsPrefAllowNewUser,
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());
}
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();
// Initialize PowerwashRequirementsChecker so its instances will be able to
// use stored cryptohome powerwash state later
policy::PowerwashRequirementsChecker::Initialize();
}
bool show_users_on_signin;
user_manager::UserList saml_users_for_password_sync;
cros_settings_->GetBoolean(kAccountsPrefShowUserNamesOnSignIn,
&show_users_on_signin);
user_manager::UserManager* const user_manager =
user_manager::UserManager::Get();
// By default disable offline login from the error screen.
ErrorScreen::AllowOfflineLogin(false /* allowed */);
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) {
ErrorScreen::AllowOfflineLogin(true /* allowed */);
}
const bool meets_allowlist_requirements =
!user->HasGaiaAccount() ||
user_manager::UserManager::Get()->IsGaiaUserAllowed(*user);
if (meets_allowlist_requirements && user->using_saml())
saml_users_for_password_sync.push_back(user);
}
auto login_users = ExtractLoginUsers(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 no user pods are visible, fallback to single new user pod which will
// have guest session link.
bool show_guest = user_manager->IsGuestSessionAllowed();
show_users_on_signin |= !login_users.empty();
bool allow_new_user = true;
cros_settings_->GetBoolean(kAccountsPrefAllowNewUser, &allow_new_user);
GetLoginDisplay()->Init(login_users, show_guest, show_users_on_signin,
allow_new_user);
GetLoginDisplayHost()->OnPreferencesChanged();
}
////////////////////////////////////////////////////////////////////////////////
// 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::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, base::BindOnce(&TransferHttpAuthCaches),
base::Milliseconds(kAuthCacheTransferDelayMs));
}
////////////////////////////////////////////////////////////////////////////////
// ExistingUserController, private:
ExistingUserController::~ExistingUserController() {
if (browser_shutdown::IsTryingToQuit() || chrome::IsAttemptingShutdown())
return;
CHECK(UserSessionManager::GetInstance());
UserSessionManager::GetInstance()->DelegateDeleted(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();
}
bool ExistingUserController::IsSigninInProgress() const {
return is_login_in_progress_;
}
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);
}
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::TransformKey(
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);
RecordPasswordLoginEvent(new_user_context);
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,
const UserContext& user_context) {
login_performer_->PerformLogin(user_context, auth_mode);
}
void ExistingUserController::ContinuePerformLoginWithoutMigration(
LoginPerformer::AuthorizationMode auth_mode,
const UserContext& user_context) {
UserContext user_context_ecryptfs = user_context;
user_context_ecryptfs.SetIsForcingDircrypto(false);
ContinuePerformLogin(auth_mode, user_context_ecryptfs);
}
void ExistingUserController::OnSigninScreenReady() {
// Used to debug crbug.com/902315. Feel free to remove after that is fixed.
VLOG(1) << "OnSigninScreenReady";
StartAutoLoginTimer();
}
void ExistingUserController::OnGaiaScreenReady() {
// Used to debug crbug.com/902315. Feel free to remove after that is fixed.
VLOG(1) << "OnGaiaScreenReady";
StartAutoLoginTimer();
}
void ExistingUserController::OnStartEnterpriseEnrollment() {
if (KioskAppManager::Get()->IsConsumerKioskDeviceWithAutoLaunch()) {
LOG(WARNING) << "Enterprise enrollment is not available after kiosk auto "
"launch is set.";
return;
}
DeviceSettingsService::Get()->GetOwnershipStatusAsync(base::BindOnce(
&ExistingUserController::OnEnrollmentOwnershipCheckCompleted,
weak_factory_.GetWeakPtr()));
}
void ExistingUserController::OnStartKioskEnableScreen() {
KioskAppManager::Get()->GetConsumerKioskAutoLaunchStatus(base::BindOnce(
&ExistingUserController::OnConsumerKioskAutoLaunchCheckCompleted,
weak_factory_.GetWeakPtr()));
}
void ExistingUserController::OnStartKioskAutolaunchScreen() {
ShowKioskAutolaunchScreen();
}
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);
}
void ExistingUserController::LocalStateChanged(
user_manager::UserManager* user_manager) {
DeviceSettingsChanged();
}
void ExistingUserController::OnConsumerKioskAutoLaunchCheckCompleted(
KioskAppManager::ConsumerKioskAutoLaunchStatus status) {
if (status == KioskAppManager::ConsumerKioskAutoLaunchStatus::kConfigurable)
ShowKioskEnableScreen();
}
void ExistingUserController::OnEnrollmentOwnershipCheckCompleted(
DeviceSettingsService::OwnershipStatus status) {
VLOG(1) << "OnEnrollmentOwnershipCheckCompleted status=" << status;
if (status == DeviceSettingsService::OWNERSHIP_NONE) {
ShowEnrollmentScreen();
} else if (status == DeviceSettingsService::OWNERSHIP_TAKEN) {
// On a device that is already owned we might want to allow users to
// re-enroll if the policy information is invalid.
CrosSettingsProvider::TrustedStatus trusted_status =
CrosSettings::Get()->PrepareTrustedValues(base::BindOnce(
&ExistingUserController::OnEnrollmentOwnershipCheckCompleted,
weak_factory_.GetWeakPtr(), status));
if (trusted_status == CrosSettingsProvider::PERMANENTLY_UNTRUSTED) {
VLOG(1) << "Showing enrollment because device is PERMANENTLY_UNTRUSTED";
ShowEnrollmentScreen();
}
} else {
// OwnershipService::GetStatusAsync is supposed to return either
// OWNERSHIP_NONE or OWNERSHIP_TAKEN.
NOTREACHED();
}
}
void ExistingUserController::ShowEnrollmentScreen() {
GetLoginDisplayHost()->StartWizard(EnrollmentScreenView::kScreenId);
}
void ExistingUserController::ShowKioskEnableScreen() {
GetLoginDisplayHost()->StartWizard(KioskEnableScreenView::kScreenId);
}
void ExistingUserController::ShowKioskAutolaunchScreen() {
GetLoginDisplayHost()->StartWizard(KioskAutolaunchScreenView::kScreenId);
}
void ExistingUserController::ShowEncryptionMigrationScreen(
const UserContext& user_context,
EncryptionMigrationMode migration_mode) {
GetLoginDisplayHost()->GetSigninUI()->StartEncryptionMigration(
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::ShowPasswordChangedDialog(
const UserContext& user_context) {
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()->ShowPasswordChangedDialog(
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()) {
// Show no errors, just re-enable input.
GetLoginDisplay()->ClearAndEnablePassword();
StartAutoLoginTimer();
} else if (is_known_user &&
failure.reason() == AuthFailure::MISSING_CRYPTOHOME) {
ForceOnlineLoginForAccountId(last_login_attempt_account_id_);
RecordReauthReason(last_login_attempt_account_id_,
ReauthReason::MISSING_CRYPTOHOME);
} 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::UNRECOVERABLE_CRYPTOHOME);
} 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);
}
GetLoginDisplay()->ClearAndEnablePassword();
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;
GetLoginDisplay()->set_signin_completed(true);
// Login performer will be gone so cache this value to use
// once profile is loaded.
password_changed_ = login_performer_->password_changed();
auth_mode_ = login_performer_->auth_mode();
ChromeUserManager::Get()
->GetUserFlow(user_context.GetAccountId())
->HandleLoginSuccess(user_context);
StopAutoLoginTimer();
// Truth table of `has_auth_cookies`:
// Regular SAML
// /ServiceLogin T T
// /ChromeOsEmbeddedSetup F T
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);
ignore_result(login_performer_.release());
if (user_context.GetAuthFlow() == UserContext::AUTH_FLOW_OFFLINE) {
base::UmaHistogramCounts100("Login.OfflineSuccess.Attempts",
num_login_attempts_);
}
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.IsLockableManagedGuestSession()) {
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(
&SetLoginExtensionApiLaunchExtensionIdPref, user_context.GetAccountId(),
user_context.GetManagedGuestSessionLaunchExtensionId()));
}
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.
this);
// 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();
}));
std::unique_ptr<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),
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::known_user::SetIsEnterpriseManaged(user_context.GetAccountId(),
is_enterprise_managed);
if (is_enterprise_managed) {
absl::optional<std::string> manager =
chrome::GetAccountManagerIdentity(profile);
if (manager) {
user_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() {
is_login_in_progress_ = false;
// 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::OnPasswordChangeDetected(
const UserContext& user_context) {
is_login_in_progress_ = false;
// Must not proceed without signature verification.
if (CrosSettingsProvider::TRUSTED !=
cros_settings_->PrepareTrustedValues(
base::BindOnce(&ExistingUserController::OnPasswordChangeDetected,
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.OnPasswordChangeDetected(user_context);
ShowPasswordChangedDialog(user_context);
}
void ExistingUserController::OnOldEncryptionDetected(
const UserContext& user_context,
bool has_incomplete_migration) {
absl::optional<EncryptionMigrationMode> encryption_migration_mode =
GetEncryptionMigrationMode(user_context, has_incomplete_migration);
if (!encryption_migration_mode.has_value()) {
ContinuePerformLoginWithoutMigration(login_performer_->auth_mode(),
user_context);
return;
}
ShowEncryptionMigrationScreen(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);
GetLoginDisplayHost()->OnPreferencesChanged();
// 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 */);
GetLoginDisplay()->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_ && GetLoginDisplay() &&
!GetLoginDisplay()->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::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);
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 store
// is not initialized - wait for the policy store load before starting the
// auto-login timer.
policy::CloudPolicyStore* policy_store =
g_browser_process->platform_part()
->browser_policy_connector_ash()
->GetDeviceLocalAccountPolicyService()
->GetBrokerForUser(user->GetAccountId().GetUserEmail())
->core()
->store();
if (!policy_store->is_initialized()) {
VLOG(2) << "Public session policy store not yet initialized";
policy_store_waiter_ = std::make_unique<PolicyStoreLoadWaiter>(
policy_store,
base::BindOnce(
&ExistingUserController::LoginAsPublicSessionWithPolicyStoreReady,
base::Unretained(this), user_context));
return;
}
LoginAsPublicSessionWithPolicyStoreReady(user_context);
}
void ExistingUserController::LoginAsPublicSessionWithPolicyStoreReady(
const UserContext& user_context) {
VLOG(2) << "LoginAsPublicSessionWithPolicyStoreReady";
policy_store_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() && entry->value()->is_list()) {
base::Value::ConstListView list = entry->value()->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();
auto* data_snapshotd_manager =
arc::data_snapshotd::ArcDataSnapshotdManager::Get();
bool is_arc_data_snapshot_autologin =
(data_snapshotd_manager &&
data_snapshotd_manager->IsAutoLoginConfigured());
if (is_arc_data_snapshot_autologin) {
public_session_auto_login_account_id_ =
GetArcDataSnapshotAutoLoginAccountId(device_local_accounts);
} else {
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 (is_arc_data_snapshot_autologin ||
!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::ResetAutoLoginTimer() {
// 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() {
if (is_login_in_progress_ ||
!public_session_auto_login_account_id_.is_valid()) {
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;";
return;
}
VLOG(2) << "Starting autologin timer with delay: " << auto_login_delay_;
if (auto_login_timer_ && auto_login_timer_->IsRunning()) {
StopAutoLoginTimer();
}
// Block auto-login flow until ArcDataSnapshotdManager is ready to enter an
// auto-login session.
// ArcDataSnapshotdManager stores a reset auto-login callback to fire it once
// it is ready.
auto* data_snapshotd_manager =
arc::data_snapshotd::ArcDataSnapshotdManager::Get();
if (data_snapshotd_manager && !data_snapshotd_manager->IsAutoLoginAllowed() &&
data_snapshotd_manager->IsAutoLoginConfigured()) {
data_snapshotd_manager->set_reset_autologin_callback(
base::BindOnce(&ExistingUserController::StartAutoLoginTimer,
weak_factory_.GetWeakPtr()));
return;
}
// 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,
std::unique_ptr<base::ListValue> keyboard_layouts) {
UserContext new_user_context = user_context;
std::string keyboard_layout;
for (size_t i = 0; i < keyboard_layouts->GetList().size(); ++i) {
base::DictionaryValue* entry = nullptr;
keyboard_layouts->GetDictionary(i, &entry);
if (entry->FindBoolKey("selected").value_or(false)) {
entry->GetString("value", &keyboard_layout);
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);
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;
std::string device_id =
user_manager::known_user::GetDeviceId(user_context.GetAccountId());
if (device_id.empty()) {
bool is_ephemeral = ChromeUserManager::Get()->AreEphemeralUsersEnabled() &&
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()) {
user_manager::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