0

demo_mode: Fall back to MGS on demo account setup failure

In demo mode, the device will try to set up a demo account first and use
it to log in. If it fails to set up the demo account, it falls back to
the managed guest session.

Bug: 364214790
Change-Id: I4a233c43abf89785f3b0265cf58c3816ccf617c5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6034618
Reviewed-by: Haifan Wang <wanghaifan@google.com>
Commit-Queue: Xiqi Ruan <xiqiruan@chromium.org>
Reviewed-by: Xiyuan Xia <xiyuan@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1387178}
This commit is contained in:
Xiqi Ruan
2024-11-23 00:57:38 +00:00
committed by Chromium LUCI CQ
parent 24947a958b
commit dbff683a77
7 changed files with 124 additions and 17 deletions

@ -101,6 +101,7 @@ static_library("login") {
"//chrome/browser/ash/policy/enrollment",
"//chrome/browser/profiles:profile",
"//chromeos/ash/components/dbus/session_manager",
"//chromeos/ash/components/demo_mode",
"//chromeos/ash/components/login/auth",
"//chromeos/ash/components/login/auth/public:authpublic",
"//chromeos/ash/components/login/auth/public:challenge_response_key",

@ -15,6 +15,7 @@
#include "base/logging.h"
#include "base/uuid.h"
#include "base/values.h"
#include "chrome/browser/ash/login/existing_user_controller.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/ui/ash/login/login_display_host.h"
#include "chrome/browser/ui/ash/login/login_screen_client_impl.h"
@ -271,16 +272,16 @@ DemoLoginController::~DemoLoginController() = default;
void DemoLoginController::OnLoginScreenShown() {
// Stop observe login screen since it may get invoked in session. Demo account
// should be setup only once for each session. Follow up response will
// instruct retry or fallback to public account.
// instruct retry or fall back to public account.
scoped_observation_.Reset();
if (!demo_mode::IsDeviceInDemoMode()) {
return;
}
// Try demo account login first by disable auto-login to managed guest
// session.
demo_mode::SetShouldFallBackMGS(false);
// TODO(crbug.com/370806573): Skip auto login public account in
// `ExistingUserController::StartAutoLoginTimer` if this feature enable
// Maybe add a policy.
MaybeCleanupPreviousDemoAccount();
}
@ -321,8 +322,6 @@ void DemoLoginController::OnSetupDemoAccountComplete(
HandleSetupDemoAcountResponse(sign_in_scoped_device_id,
std::move(response_body));
} else {
// TODO(crbug.com/364214790): Handle any errors (maybe earlier for net
// connection error) and fallback to MGS.
OnSetupDemoAccountError(result);
}
}
@ -362,6 +361,12 @@ void DemoLoginController::OnSetupDemoAccountError(
if (setup_failed_callback_for_testing_) {
std::move(setup_failed_callback_for_testing_).Run(result_code);
}
// Login public account session when set up failed.
demo_mode::SetShouldFallBackMGS(true);
auto* existing_user_controller =
ash::ExistingUserController::current_controller();
existing_user_controller->ConfigureAutoLogin();
}
void DemoLoginController::MaybeCleanupPreviousDemoAccount() {

@ -4,19 +4,29 @@
#include "chrome/browser/ash/login/demo_mode/demo_login_controller.h"
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/login/test_login_screen.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/ash/login/existing_user_controller.h"
#include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
#include "chrome/browser/ash/settings/scoped_cros_settings_test_helper.h"
#include "chrome/browser/ash/settings/scoped_testing_cros_settings.h"
#include "chrome/browser/ui/ash/login/mock_login_display_host.h"
#include "chrome/test/base/scoped_testing_local_state.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chromeos/ash/components/dbus/session_manager/fake_session_manager_client.h"
#include "chromeos/ash/components/demo_mode/utils/demo_session_utils.h"
#include "chromeos/ash/components/install_attributes/stub_install_attributes.h"
#include "chromeos/ash/components/settings/cros_settings_names.h"
#include "chromeos/ash/components/system/fake_statistics_provider.h"
#include "components/account_id/account_id.h"
#include "components/policy/core/common/device_local_account_type.h"
#include "components/session_manager/core/session_manager.h"
#include "components/user_manager/scoped_user_manager.h"
#include "content/public/test/browser_task_environment.h"
#include "google_apis/google_api_keys.h"
#include "net/base/url_util.h"
@ -50,6 +60,8 @@ constexpr char kCleanUpDemoAccountUrl[] =
constexpr char kApiKeyParam[] = "key";
constexpr char kPublicAccountUserId[] = "public_session_user@localhost";
} // namespace
class DemoLoginControllerTest : public testing::Test {
@ -67,7 +79,22 @@ class DemoLoginControllerTest : public testing::Test {
}
void SetUp() override {
attributes_.Get()->SetDemoMode();
features_.InitAndEnableFeature(features::kDemoModeSignIn);
settings_helper_.InstallAttributes()->SetDemoMode();
fake_user_manager_->AddPublicAccountUser(auto_login_account_id_);
settings_helper_.ReplaceDeviceSettingsProviderWithStub();
base::Value::Dict account;
account.Set(kAccountsPrefDeviceLocalAccountsKeyId, kPublicAccountUserId);
account.Set(
kAccountsPrefDeviceLocalAccountsKeyType,
static_cast<int>(policy::DeviceLocalAccountType::kPublicSession));
base::Value::List accounts;
accounts.Append(std::move(account));
settings_helper_.Set(kAccountsPrefDeviceLocalAccounts,
base::Value(std::move(accounts)));
login_screen_client_ = std::make_unique<LoginScreenClientImpl>();
demo_login_controller_ =
std::make_unique<DemoLoginController>(login_screen_client_.get());
@ -117,17 +144,39 @@ class DemoLoginControllerTest : public testing::Test {
loop.Run();
}
void ExpectGetExistingController() {
EXPECT_CALL(login_display_host(), GetExistingUserController())
.WillRepeatedly(testing::Return(&existing_user_controller_));
}
ScopedCrosSettingsTestHelper* settings_helper() { return &settings_helper_; }
ExistingUserController* existing_user_controller() {
return &existing_user_controller_;
}
network::TestURLLoaderFactory test_url_loader_factory_;
private:
base::test::ScopedFeatureList features_;
content::BrowserTaskEnvironment task_environment_;
ScopedStubInstallAttributes attributes_;
testing::NiceMock<ash::MockLoginDisplayHost> mock_login_display_host_;
ScopedTestingLocalState local_state_{TestingBrowserProcess::GetGlobal()};
system::FakeStatisticsProvider statistics_provider_;
// Dependencies for `LoginScreenClientImpl`:
// Dependencies for `ExistingUserController`:
FakeSessionManagerClient fake_session_manager_client_;
ScopedCrosSettingsTestHelper settings_helper_;
user_manager::TypedScopedUserManager<ash::FakeChromeUserManager>
fake_user_manager_{std::make_unique<FakeChromeUserManager>()};
session_manager::SessionManager session_manager_;
const AccountId auto_login_account_id_ =
AccountId::FromUserEmail(policy::GenerateDeviceLocalAccountUserId(
kPublicAccountUserId,
policy::DeviceLocalAccountType::kPublicSession));
ExistingUserController existing_user_controller_;
// Dependencies for `LoginScreenClientImpl`:
TestLoginScreen test_login_screen_;
std::unique_ptr<LoginScreenClientImpl> login_screen_client_;
@ -163,7 +212,7 @@ TEST_F(DemoLoginControllerTest, OnSetupDemoAccountSuccessFirstTime) {
TEST_F(DemoLoginControllerTest, InValidGaia) {
test_url_loader_factory_.AddResponse(GetSetupUrl().spec(), kInValidGaiaCreds);
ExpectGetExistingController();
base::RunLoop loop;
EXPECT_CALL(login_display_host(), CompleteLogin).Times(0);
demo_login_controller()->SetSetupFailedCallbackForTest(
@ -230,6 +279,32 @@ TEST_F(DemoLoginControllerTest, CleanUpFailed) {
EXPECT_NE(new_session_id, last_session_id);
}
TEST_F(DemoLoginControllerTest, FallbackToMGS) {
// Mock setup failed by returning invalid credential.
test_url_loader_factory_.AddResponse(GetSetupUrl().spec(), kInValidGaiaCreds);
ExpectGetExistingController();
// Configure auto login settings. This is done by policy in prod env.
settings_helper()->SetString(kAccountsPrefDeviceLocalAccountAutoLoginId,
kPublicAccountUserId);
settings_helper()->SetInteger(kAccountsPrefDeviceLocalAccountAutoLoginDelay,
0);
base::RunLoop loop;
EXPECT_CALL(login_display_host(), CompleteLogin).Times(0);
demo_login_controller()->SetSetupFailedCallbackForTest(
base::BindLambdaForTesting(
[&](const DemoLoginController::ResultCode result_code) {
loop.Quit();
}));
login_display_host().StartSignInScreen();
login_screen_client()->OnLoginScreenShown();
loop.Run();
// Expect auto login managed guest session starts.
EXPECT_TRUE(existing_user_controller()->IsAutoLoginTimerRunningForTesting());
}
// TODO(crbug.com/372771485): Add more request fail test cases.
} // namespace ash

@ -44,7 +44,6 @@
#include "chrome/browser/ash/boot_times_recorder/boot_times_recorder.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"
@ -93,6 +92,7 @@
#include "chromeos/ash/components/cryptohome/cryptohome_parameters.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/demo_mode/utils/demo_session_utils.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"
@ -1286,17 +1286,20 @@ void ExistingUserController::CancelPasswordChangedFlow() {
void ExistingUserController::StartAutoLoginTimer() {
auto session_state = session_manager::SessionManager::Get()->session_state();
bool is_demo_mode = demo_mode::IsDeviceInDemoMode();
if (is_login_in_progress_ ||
!public_session_auto_login_account_id_.is_valid() ||
(session_state == session_manager::SessionState::OOBE &&
!DemoSession::IsDeviceInDemoMode())) {
(session_state == session_manager::SessionState::OOBE && !is_demo_mode) ||
(is_demo_mode && !demo_mode::ShouldFallBackToMGS())) {
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())
!is_demo_mode)
<< "* OOBE isn't completed and device isn't in demo mode;";
VLOG_IF(2, is_demo_mode && !demo_mode::ShouldFallBackToMGS())
<< "* Should not fall back to manage guest session for demo mode;";
return;
}
VLOG(2) << "Starting autologin timer with delay: " << auto_login_delay_;

@ -146,6 +146,10 @@ class ExistingUserController : public HttpAuthDialog::Observer,
// Calls login() on previously-used `login_performer_`.
void LoginAuthenticated(std::unique_ptr<UserContext> user_context);
// Retrieve public session auto-login policy and update the
// timer.
void ConfigureAutoLogin();
private:
friend class ExistingUserControllerTest;
friend class ExistingUserControllerAutoLoginTest;
@ -160,9 +164,6 @@ class ExistingUserController : public HttpAuthDialog::Observer,
void LoginAsGuest();
void LoginAsPublicSession(const UserContext& user_context);
void LoginAsKioskApp(KioskAppId kiosk_app_id);
// Retrieve public session auto-login policy and update the
// timer.
void ConfigureAutoLogin();
// Trigger public session auto-login.
void OnPublicSessionAutoLoginTimerFire();

@ -4,12 +4,17 @@
#include "chromeos/ash/components/demo_mode/utils/demo_session_utils.h"
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "chromeos/ash/components/install_attributes/install_attributes.h"
#include "components/prefs/pref_registry_simple.h"
namespace ash::demo_mode {
namespace {
bool g_should_fall_back_mgs = false;
}
bool IsDeviceInDemoMode() {
if (!InstallAttributes::IsInitialized()) {
// TODO(b/281905036): Add a log to indicate that the install
@ -39,4 +44,13 @@ void RegisterLocalStatePrefs(PrefRegistrySimple* registry) {
std::string());
}
bool ShouldFallBackToMGS() {
// Always fall back to MGS if demo mode sign in not enable.
return !features::IsDemoModeSignInEnabled() || g_should_fall_back_mgs;
}
void SetShouldFallBackMGS(bool should_fall_back_mgs) {
g_should_fall_back_mgs = should_fall_back_mgs;
}
} // namespace ash::demo_mode

@ -26,6 +26,14 @@ bool IsDeviceInDemoMode();
COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_DEMO_MODE)
void RegisterLocalStatePrefs(PrefRegistrySimple* registry);
COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_DEMO_MODE)
// Whether the device should fall back to manage guest sesison in demo mode.
bool ShouldFallBackToMGS();
COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_DEMO_MODE)
// Whether the device is in demo account session.
void SetShouldFallBackMGS(bool is_demo_account_session);
} // namespace ash::demo_mode
#endif // CHROMEOS_ASH_COMPONENTS_DEMO_MODE_UTILS_DEMO_SESSION_UTILS_H_