chromium/chrome/browser/enterprise/connectors/device_trust/signals/decorators/ash/ash_signals_decorator_browsertest.cc

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/enterprise/connectors/device_trust/signals/decorators/ash/ash_signals_decorator.h"

#include "ash/constants/ash_pref_names.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
#include "chrome/browser/ash/policy/core/device_policy_cros_browser_test.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_process_platform_part_ash.h"
#include "chrome/browser/enterprise/signals/signals_common.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/test/base/testing_profile.h"
#include "chromeos/ash/components/browser_context_helper/browser_context_types.h"
#include "chromeos/ash/components/dbus/shill/shill_device_client.h"
#include "chromeos/ash/components/dbus/shill/shill_profile_client.h"
#include "chromeos/ash/components/dbus/shill/shill_service_client.h"
#include "chromeos/ash/components/network/network_state_handler.h"
#include "chromeos/ash/components/system/fake_statistics_provider.h"
#include "components/device_signals/core/browser/signals_types.h"
#include "components/device_signals/core/common/signals_constants.h"
#include "components/policy/proto/device_management_backend.pb.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/testing_pref_service.h"
#include "components/user_manager/user.h"
#include "components/user_manager/user_manager.h"
#include "content/public/test/browser_test.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/cros_system_api/dbus/shill/dbus-constants.h"

using testing::InvokeWithoutArgs;

namespace enterprise_connectors {

namespace {

constexpr char kFakeEnrollmentDomain[] = "fake.domain.google.com";
constexpr char kFakeAffilationID[] = "fake_affiliation_id";

constexpr char kFakeImei[] = "fake_imei";
constexpr char kFakeMeid[] = "fake_meid";
constexpr char kMacAddress[] = "00:00:00:00:00:00";
constexpr char kWifiDevicePath[] = "/device/stub_wifi";
constexpr char kCellularDevicePath[] = "/device/stub_cellular_device";
constexpr char kWifiServicePath[] = "/service/stub_wifi";

constexpr char kFakeSerialNumber[] = "fake_serial_number";
constexpr char kFakeDeviceHostName[] = "fake_device_host_name";

base::Value::List GetExpectedMacAddresses() {
  base::Value::List mac_addresses;
  mac_addresses.Append(kMacAddress);
  return mac_addresses;
}

void SetupFakeNetwork() {
  ash::ShillDeviceClient::TestInterface* shill_device_client =
      ash::ShillDeviceClient::Get()->GetTestInterface();
  ash::ShillServiceClient::TestInterface* shill_service_client =
      ash::ShillServiceClient::Get()->GetTestInterface();
  ash::ShillProfileClient::TestInterface* shill_profile_client =
      ash::ShillProfileClient::Get()->GetTestInterface();

  shill_service_client->ClearServices();
  shill_device_client->ClearDevices();

  shill_device_client->AddDevice(kCellularDevicePath, shill::kTypeCellular,
                                 "stub_cellular_device");
  shill_device_client->SetDeviceProperty(
      kCellularDevicePath, shill::kMeidProperty, base::Value(kFakeMeid),
      /*notify_changed=*/false);
  shill_device_client->SetDeviceProperty(
      kCellularDevicePath, shill::kImeiProperty, base::Value(kFakeImei),
      /*notify_changed=*/false);

  shill_device_client->AddDevice(kWifiDevicePath, shill::kTypeWifi,
                                 "stub_wifi_device");
  shill_device_client->SetDeviceProperty(
      kWifiDevicePath, shill::kAddressProperty, base::Value(kMacAddress),
      /*notify_changed=*/false);

  shill_service_client->AddService(kWifiServicePath, "wifi_guid",
                                   "wifi_network_name", shill::kTypeWifi,
                                   shill::kStateIdle, /*visible=*/true);
  shill_service_client->SetServiceProperty(
      kWifiServicePath, shill::kConnectableProperty, base::Value(true));

  shill_profile_client->AddService(
      ash::ShillProfileClient::GetSharedProfilePath(), kWifiServicePath);

  shill_service_client->SetServiceProperty(kWifiServicePath,
                                           shill::kStateProperty,
                                           base::Value(shill::kStateOnline));
  base::RunLoop().RunUntilIdle();
}

}  // namespace

class AshSignalsDecoratorBrowserTest
    : public policy::DevicePolicyCrosBrowserTest {
 public:
  AshSignalsDecoratorBrowserTest() {
    device_state_.set_skip_initial_policy_setup(true);
  }
  ~AshSignalsDecoratorBrowserTest() override = default;

 protected:
  void SetUpOnMainThread() override {
    // Register prefs in test pref services.
    prefs_.registry()->RegisterBooleanPref(ash::prefs::kAllowScreenLock, false);

    TestingProfile::Builder profile_builder;
    testing_profile_ = profile_builder.Build();
    connector_ =
        g_browser_process->platform_part()->browser_policy_connector_ash();
  }

  void TearDownOnMainThread() override { testing_profile_.reset(); }

  Profile* testing_profile() { return testing_profile_.get(); }

  std::unique_ptr<TestingProfile> testing_profile_;
  TestingPrefServiceSimple prefs_;
  raw_ptr<policy::BrowserPolicyConnectorAsh, DanglingUntriaged> connector_;

  ash::system::ScopedFakeStatisticsProvider fake_statistics_provider_;
};

IN_PROC_BROWSER_TEST_F(AshSignalsDecoratorBrowserTest,
                       TestStaticPolicySignals) {
  device_policy()->policy_data().set_managed_by(kFakeEnrollmentDomain);

  testing_profile()->GetPrefs()->SetBoolean(ash::prefs::kAllowScreenLock,
                                            false);
  // Set fake serial number.
  fake_statistics_provider_.SetMachineStatistic(ash::system::kSerialNumberKey,
                                                kFakeSerialNumber);
  // Set fake device hostname.
  ash::NetworkHandler::Get()->network_state_handler()->SetHostname(
      kFakeDeviceHostName);

  policy_helper()->RefreshPolicyAndWaitUntilDeviceCloudPolicyUpdated();

  base::RunLoop run_loop;
  AshSignalsDecorator decorator(connector_, testing_profile());
  base::Value::Dict signals;
  decorator.Decorate(signals, run_loop.QuitClosure());

  run_loop.Run();

  EXPECT_EQ(*signals.FindString(device_signals::names::kDeviceEnrollmentDomain),
            kFakeEnrollmentDomain);

  const auto* serial_number =
      signals.FindString(device_signals::names::kSerialNumber);
  ASSERT_TRUE(serial_number);
  EXPECT_EQ(*serial_number, kFakeSerialNumber);

  const auto* device_host_name =
      signals.FindString(device_signals::names::kDeviceHostName);
  ASSERT_TRUE(device_host_name);
  EXPECT_EQ(*device_host_name, kFakeDeviceHostName);

  auto disk_encrypted = signals.FindInt(device_signals::names::kDiskEncrypted);
  ASSERT_TRUE(disk_encrypted);
  EXPECT_EQ(disk_encrypted.value(),
            static_cast<int32_t>(enterprise_signals::SettingValue::ENABLED));

  auto screen_lock_secured =
      signals.FindInt(device_signals::names::kScreenLockSecured);
  ASSERT_TRUE(screen_lock_secured);
  EXPECT_EQ(screen_lock_secured.value(),
            static_cast<int32_t>(enterprise_signals::SettingValue::ENABLED));
}

IN_PROC_BROWSER_TEST_F(AshSignalsDecoratorBrowserTest, TestNetworkSignals) {
  device_policy()->policy_data().add_device_affiliation_ids(kFakeAffilationID);
  policy_helper()->RefreshPolicyAndWaitUntilDeviceCloudPolicyUpdated();

  Profile* profile =
      g_browser_process->profile_manager()->GetPrimaryUserProfile();
  const user_manager::User* user =
      ash::ProfileHelper::Get()->GetUserByProfile(profile);
  AshSignalsDecorator decorator(connector_, profile);

  user_manager::UserManager::Get()->SetUserAffiliated(user->GetAccountId(),
                                                      /*is_affiliated=*/true);

  // Test for no network
  {
    base::RunLoop run_loop;
    base::Value::Dict signals;
    decorator.Decorate(signals, run_loop.QuitClosure());

    run_loop.Run();

    base::Value::List* imei_list =
        signals.FindList(device_signals::names::kImei);
    ASSERT_TRUE(imei_list);
    EXPECT_TRUE(imei_list->empty());

    base::Value::List* meid_list =
        signals.FindList(device_signals::names::kMeid);
    ASSERT_TRUE(meid_list);
    EXPECT_TRUE(meid_list->empty());
  }

  // Test for fake network
  {
    SetupFakeNetwork();

    base::RunLoop run_loop;
    base::Value::Dict signals;
    decorator.Decorate(signals, run_loop.QuitClosure());

    run_loop.Run();

    const auto* mac_addresses =
        signals.FindList(device_signals::names::kMacAddresses);
    ASSERT_TRUE(mac_addresses);
    EXPECT_EQ(*mac_addresses, GetExpectedMacAddresses());

    base::Value::List* imei_list =
        signals.FindList(device_signals::names::kImei);
    EXPECT_EQ(imei_list->size(), 1u);
    EXPECT_EQ(imei_list->front(), kFakeImei);

    base::Value::List* meid_list =
        signals.FindList(device_signals::names::kMeid);
    EXPECT_EQ(meid_list->size(), 1u);
    EXPECT_EQ(meid_list->front(), kFakeMeid);
  }
}

IN_PROC_BROWSER_TEST_F(AshSignalsDecoratorBrowserTest, TestSignalTrigger) {
  // Test with user profile
  {
    base::RunLoop run_loop;
    AshSignalsDecorator decorator(connector_, testing_profile());
    base::Value::Dict signals;
    decorator.Decorate(signals, run_loop.QuitClosure());

    run_loop.Run();

    auto browser_context_type =
        signals.FindInt(device_signals::names::kTrigger);
    ASSERT_TRUE(browser_context_type);
    EXPECT_EQ(
        browser_context_type.value(),
        static_cast<int32_t>(device_signals::Trigger::kBrowserNavigation));
  }

  // Test with signin profile
  {
    base::RunLoop run_loop;
    AshSignalsDecorator decorator(
        connector_,
        ash::ProfileHelper::GetSigninProfile()->GetOriginalProfile());
    base::Value::Dict signals;
    decorator.Decorate(signals, run_loop.QuitClosure());

    run_loop.Run();

    auto browser_context_type =
        signals.FindInt(device_signals::names::kTrigger);
    ASSERT_TRUE(browser_context_type);
    EXPECT_EQ(browser_context_type.value(),
              static_cast<int32_t>(device_signals::Trigger::kLoginScreen));
  }
}

}  // namespace enterprise_connectors