chromium/chrome/browser/ash/system/device_disabling_browsertest.cc

// 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