// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include <string>
#include "ash/constants/ash_switches.h"
#include "ash/public/cpp/login_screen_test_api.h"
#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/memory/scoped_refptr.h"
#include "base/run_loop.h"
#include "chrome/browser/ash/login/login_wizard.h"
#include "chrome/browser/ash/login/test/device_state_mixin.h"
#include "chrome/browser/ash/login/test/login_manager_mixin.h"
#include "chrome/browser/ash/login/test/network_portal_detector_mixin.h"
#include "chrome/browser/ash/login/test/oobe_base_test.h"
#include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
#include "chrome/browser/ash/login/test/scoped_policy_update.h"
#include "chrome/browser/ash/login/ui/login_display_host.h"
#include "chrome/browser/ash/login/wizard_controller.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/ui/webui/ash/login/device_disabled_screen_handler.h"
#include "chrome/browser/ui/webui/ash/login/network_state_informer.h"
#include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
#include "chromeos/ash/components/dbus/session_manager/fake_session_manager_client.h"
#include "chromeos/ash/components/dbus/shill/shill_manager_client.h"
#include "chromeos/ash/components/dbus/shill/shill_service_client.h"
#include "chromeos/ash/components/settings/cros_settings.h"
#include "chromeos/ash/components/settings/cros_settings_names.h"
#include "components/policy/proto/device_management_backend.pb.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "dbus/object_path.h"
namespace ash {
namespace system {
namespace {
void ErrorCallbackFunction(const std::string& error_name,
const std::string& error_message) {
LOG(ERROR) << "Shill Error: " << error_name << " : " << error_message;
}
bool DeviceDisabledScreenShown() {
WizardController* const wizard_controller =
WizardController::default_controller();
EXPECT_TRUE(wizard_controller);
return wizard_controller &&
wizard_controller->current_screen() ==
wizard_controller->GetScreen(DeviceDisabledScreenView::kScreenId);
}
} // namespace
class DeviceDisablingTest
: public OobeBaseTest,
public NetworkStateInformer::NetworkStateInformerObserver {
public:
DeviceDisablingTest() = default;
DeviceDisablingTest(const DeviceDisablingTest&) = delete;
DeviceDisablingTest& operator=(const DeviceDisablingTest&) = delete;
// Sets up a device state blob that indicates the device is disabled.
void SetDeviceDisabledPolicy();
// Sets up a device state blob that indicates the device is disabled, triggers
// a policy plus device state fetch and waits for it to succeed.
void MarkDisabledAndWaitForPolicyFetch();
std::string GetCurrentScreenName(content::WebContents* web_contents);
protected:
// OobeBaseTest:
void SetUpOnMainThread() override;
// NetworkStateInformer::NetworkStateInformerObserver:
void UpdateState(NetworkError::ErrorReason reason) override;
std::unique_ptr<base::RunLoop> network_state_change_wait_run_loop_;
NetworkPortalDetectorMixin network_portal_detector_{&mixin_host_};
private:
DeviceStateMixin device_state_{
&mixin_host_, DeviceStateMixin::State::OOBE_COMPLETED_CLOUD_ENROLLED};
};
void DeviceDisablingTest::SetDeviceDisabledPolicy() {
// Prepare a policy fetch response that indicates the device is disabled.
std::unique_ptr<ScopedDevicePolicyUpdate> policy_update =
device_state_.RequestDevicePolicyUpdate();
policy_update->policy_data()->mutable_device_state()->set_device_mode(
enterprise_management::DeviceState::DEVICE_MODE_DISABLED);
}
void DeviceDisablingTest::MarkDisabledAndWaitForPolicyFetch() {
base::RunLoop run_loop;
// Set up an observer that will wait for the disabled setting to change.
base::CallbackListSubscription subscription =
CrosSettings::Get()->AddSettingsObserver(kDeviceDisabled,
run_loop.QuitClosure());
SetDeviceDisabledPolicy();
// Trigger a policy fetch.
FakeSessionManagerClient::Get()->OnPropertyChangeComplete(true);
// Wait for the policy fetch to complete and the disabled setting to change.
run_loop.Run();
}
void DeviceDisablingTest::SetUpOnMainThread() {
network_state_change_wait_run_loop_ = std::make_unique<base::RunLoop>();
OobeBaseTest::SetUpOnMainThread();
// Set up fake networks.
ShillManagerClient::Get()->GetTestInterface()->SetupDefaultEnvironment();
}
void DeviceDisablingTest::UpdateState(NetworkError::ErrorReason reason) {
network_state_change_wait_run_loop_->Quit();
}
IN_PROC_BROWSER_TEST_F(DeviceDisablingTest, DisableDuringNormalOperation) {
MarkDisabledAndWaitForPolicyFetch();
// Check for WizardController state.
OobeScreenWaiter(DeviceDisabledScreenView::kScreenId).Wait();
EXPECT_TRUE(ash::LoginScreenTestApi::IsOobeDialogVisible());
}
// Verifies that device disabling works when the ephemeral users policy is
// enabled. This case warrants its own test because the UI behaves somewhat
// differently when the policy is set: A background job runs on startup that
// causes the UI to try and show the login screen after some delay. It must
// be ensured that the login screen does not show and does not clobber the
// disabled screen.
IN_PROC_BROWSER_TEST_F(DeviceDisablingTest, DisableWithEphemeralUsers) {
// Connect to the fake Ethernet network. This ensures that Chrome OS will not
// try to show the offline error screen.
base::RunLoop connect_run_loop;
ShillServiceClient::Get()->Connect(dbus::ObjectPath("/service/eth1"),
connect_run_loop.QuitClosure(),
base::BindOnce(&ErrorCallbackFunction));
connect_run_loop.Run();
// Skip to the login screen.
OobeScreenWaiter(GetFirstSigninScreen()).Wait();
// Mark the device as disabled and wait until cros settings update.
MarkDisabledAndWaitForPolicyFetch();
// Check for WizardController state.
OobeScreenWaiter(DeviceDisabledScreenView::kScreenId).Wait();
// Disconnect from the fake Ethernet network.
const LoginDisplayHost* host = LoginDisplayHost::default_host();
OobeUI* const oobe_ui = host->GetOobeUI();
ASSERT_TRUE(oobe_ui);
const scoped_refptr<NetworkStateInformer> network_state_informer =
oobe_ui->network_state_informer_for_test();
ASSERT_TRUE(network_state_informer);
network_state_informer->AddObserver(this);
network_portal_detector_.SimulateDefaultNetworkState(
NetworkPortalDetectorMixin::NetworkStatus::kOffline);
network_state_change_wait_run_loop_->Run();
network_state_informer->RemoveObserver(this);
base::RunLoop().RunUntilIdle();
// Verify that the offline error screen was not shown and the device disabled
// screen is still being shown instead.
// Check for WizardController state.
OobeScreenWaiter(DeviceDisabledScreenView::kScreenId).Wait();
}
class DeviceDisablingWithUsersTest : public DeviceDisablingTest {
public:
DeviceDisablingWithUsersTest() { login_manager_.AppendRegularUsers(2); }
private:
LoginManagerMixin login_manager_{&mixin_host_};
};
// Checks that OOBE dialog is not hidden when the device disabled screen is
// shown and "StartSignInScreen" is called.
IN_PROC_BROWSER_TEST_F(DeviceDisablingWithUsersTest, DialogNotHidden) {
EXPECT_TRUE(ash::LoginScreenTestApi::ClickAddUserButton());
EXPECT_TRUE(ash::LoginScreenTestApi::IsOobeDialogVisible());
OobeScreenWaiter(GetFirstSigninScreen()).Wait();
MarkDisabledAndWaitForPolicyFetch();
OobeScreenWaiter(DeviceDisabledScreenView::kScreenId).Wait();
LoginDisplayHost::default_host()->StartSignInScreen();
// Dialog should not be hidden.
EXPECT_TRUE(ash::LoginScreenTestApi::IsOobeDialogVisible());
}
// Sets the device disabled policy before the browser is started.
class PresetPolicyDeviceDisablingTest : public DeviceDisablingTest {
public:
PresetPolicyDeviceDisablingTest() = default;
PresetPolicyDeviceDisablingTest(const PresetPolicyDeviceDisablingTest&) =
delete;
PresetPolicyDeviceDisablingTest& operator=(
const PresetPolicyDeviceDisablingTest&) = delete;
protected:
// DeviceDisablingTest:
void SetUpInProcessBrowserTestFixture() override {
DeviceDisablingTest::SetUpInProcessBrowserTestFixture();
SetDeviceDisabledPolicy();
}
};
// Same test as the one in DeviceDisablingTest, except the policy is being set
// before Chrome process is started. This test covers a crash (crbug.com/709518)
// in DeviceDisabledScreen where it would try to access DeviceDisablingManager
// even though it wasn't yet constructed fully.
IN_PROC_BROWSER_TEST_F(PresetPolicyDeviceDisablingTest,
DisableBeforeStartup) {
EXPECT_TRUE(DeviceDisabledScreenShown());
EXPECT_TRUE(ash::LoginScreenTestApi::IsOobeDialogVisible());
}
class DeviceDisablingBeforeLoginHostCreated
: public PresetPolicyDeviceDisablingTest {
public:
DeviceDisablingBeforeLoginHostCreated() {
// Start with user pods.
login_mixin_.AppendManagedUsers(2);
}
bool SetUpUserDataDirectory() override {
// LoginManagerMixin sets up command line in the SetUpUserDataDirectory.
if (!PresetPolicyDeviceDisablingTest::SetUpUserDataDirectory())
return false;
// Postpone login host creation.
base::CommandLine::ForCurrentProcess()->RemoveSwitch(
switches::kForceLoginManagerInTests);
return true;
}
bool ShouldWaitForOobeUI() override { return false; }
protected:
LoginManagerMixin login_mixin_{&mixin_host_};
};
// Sometimes LoginHost creation postponed (e.g. due to language switch
// https://crbug.com/1065569). This tests checks this flow.
IN_PROC_BROWSER_TEST_F(DeviceDisablingBeforeLoginHostCreated,
ShowsDisabledScreen) {
EXPECT_TRUE(
system::DeviceDisablingManager::IsDeviceDisabledDuringNormalOperation());
EXPECT_EQ(nullptr, LoginDisplayHost::default_host());
EXPECT_NE(nullptr,
g_browser_process->platform_part()->device_disabling_manager());
ShowLoginWizard(ash::OOBE_SCREEN_UNKNOWN);
// Check for WizardController state.
OobeScreenWaiter(DeviceDisabledScreenView::kScreenId).Wait();
EXPECT_TRUE(ash::LoginScreenTestApi::IsOobeDialogVisible());
}
} // namespace system
} // namespace ash