910e62b5创建于 1月15日历史提交
// Copyright 2024 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/enterprise_companion/proxy_config_service.h"

#include <memory>
#include <optional>
#include <string>
#include <utility>

#include "base/files/file_path.h"
#include "base/memory/scoped_refptr.h"
#include "base/test/bind.h"
#include "base/test/task_environment.h"
#include "chrome/enterprise_companion/device_management_storage/dm_storage.h"
#include "chrome/updater/protos/omaha_settings.pb.h"
#include "device_management_backend.pb.h"
#include "net/base/proxy_server.h"
#include "net/base/proxy_string_util.h"
#include "net/proxy_resolution/proxy_config.h"
#include "net/proxy_resolution/proxy_config_service.h"
#include "net/proxy_resolution/proxy_config_with_annotation.h"
#include "net/proxy_resolution/proxy_list.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace enterprise_companion {

// A stub DMStorage for providing cloud policy data to the ProxyConfigService.
class FakeDMStorage : public device_management_storage::DMStorage {
 public:
  FakeDMStorage() = default;

  void SetProxyPolicies(
      std::optional<std::string> proxy_mode,
      std::optional<std::string> pac_url,
      std::optional<std::string> proxy_server,
      std::optional<bool> cloud_policy_overrides_platform_policy =
          std::nullopt) {
    wireless_android_enterprise_devicemanagement::OmahaSettingsClientProto
        omaha_settings;
    if (proxy_mode) {
      omaha_settings.set_proxy_mode(*proxy_mode);
    }
    if (pac_url) {
      omaha_settings.set_proxy_pac_url(*pac_url);
    }
    if (proxy_server) {
      omaha_settings.set_proxy_server(*proxy_server);
    }
    if (cloud_policy_overrides_platform_policy) {
      omaha_settings.set_cloud_policy_overrides_platform_policy(
          *cloud_policy_overrides_platform_policy);
    }
    policy_data_ = enterprise_management::PolicyData();
    policy_data_->set_policy_value(omaha_settings.SerializeAsString());
  }

  void WriteInvalidPolicyData() {
    policy_data_ = enterprise_management::PolicyData();
    policy_data_->set_policy_value("this is not a real serialized proto.");
  }

  std::optional<enterprise_management::PolicyData> ReadPolicyData(
      const std::string& policy_type) override {
    return policy_data_;
  }

  std::string GetDeviceID() const override { return std::string(); }
  bool IsEnrollmentMandatory() const override { return false; }
  bool StoreEnrollmentToken(const std::string& enrollment_token) override {
    return false;
  }
  bool DeleteEnrollmentToken() override { return false; }
  std::string GetEnrollmentToken() const override { return std::string(); }
  bool StoreDmToken(const std::string& dm_token) override { return false; }
  std::string GetDmToken() const override { return std::string(); }
  bool InvalidateDMToken() override { return false; }
  bool DeleteDMToken() override { return false; }
  bool IsValidDMToken() const override { return false; }
  bool IsDeviceDeregistered() const override { return false; }
  bool CanPersistPolicies() const override { return false; }
  bool PersistPolicies(
      const device_management_storage::DMPolicyMap& policy_map) const override {
    return false;
  }
  bool RemoveAllPolicies() const override { return false; }
  std::unique_ptr<device_management_storage::CachedPolicyInfo>
  GetCachedPolicyInfo() const override {
    return nullptr;
  }
  base::FilePath policy_cache_folder() const override {
    return base::FilePath();
  }

 private:
  ~FakeDMStorage() override = default;

  std::optional<enterprise_management::PolicyData> policy_data_;
};

class MockProxyConfigService final : public net::ProxyConfigService {
 public:
  void AddObserver(Observer*) override {}
  void RemoveObserver(Observer*) override {}

  MOCK_METHOD(ConfigAvailability,
              GetLatestProxyConfig,
              (net::ProxyConfigWithAnnotation*),
              (override));
};

class ProxyConfigServiceTest : public ::testing::Test {
 protected:
  base::test::TaskEnvironment environment_;
  scoped_refptr<FakeDMStorage> fake_dm_storage_ =
      base::MakeRefCounted<FakeDMStorage>();
  std::unique_ptr<MockProxyConfigService> mock_fallback_ =
      std::make_unique<MockProxyConfigService>();
  std::optional<ProxyConfigAndOverridePrecedence> system_policy_config_;

  std::unique_ptr<net::ProxyConfigService> CreateService() {
    return CreatePolicyProxyConfigService(
        scoped_refptr<device_management_storage::DMStorage>(fake_dm_storage_),
        base::BindLambdaForTesting([&] { return system_policy_config_; }),
        std::move(mock_fallback_));
  }
};

TEST_F(ProxyConfigServiceTest, ConfigFromCloudPolicy_Direct) {
  fake_dm_storage_->SetProxyPolicies("direct",
                                     /*pac_url=*/std::nullopt,
                                     /*proxy_server=*/std::nullopt);
  EXPECT_CALL(*mock_fallback_, GetLatestProxyConfig).Times(0);
  std::unique_ptr<net::ProxyConfigService> proxy_config_service =
      CreateService();

  net::ProxyConfigWithAnnotation config;
  ASSERT_EQ(proxy_config_service->GetLatestProxyConfig(&config),
            net::ProxyConfigService::ConfigAvailability::CONFIG_VALID);

  EXPECT_TRUE(config.value().Equals(net::ProxyConfig::CreateDirect()));
}

TEST_F(ProxyConfigServiceTest, ConfigFromCloudPolicy_AutoDetect) {
  fake_dm_storage_->SetProxyPolicies("auto_detect",
                                     /*pac_url=*/std::nullopt,
                                     /*proxy_server=*/std::nullopt);
  EXPECT_CALL(*mock_fallback_, GetLatestProxyConfig).Times(0);
  std::unique_ptr<net::ProxyConfigService> proxy_config_service =
      CreateService();

  net::ProxyConfigWithAnnotation config;
  ASSERT_EQ(proxy_config_service->GetLatestProxyConfig(&config),
            net::ProxyConfigService::ConfigAvailability::CONFIG_VALID);

  EXPECT_TRUE(config.value().Equals(net::ProxyConfig::CreateAutoDetect()));
}

TEST_F(ProxyConfigServiceTest, ConfigFromCloudPolicy_PAC) {
  fake_dm_storage_->SetProxyPolicies(
      "pac_script",
      /*pac_url=*/"http://not-a.real-domain:8082/proxy.pac",
      /*proxy_server=*/std::nullopt);
  EXPECT_CALL(*mock_fallback_, GetLatestProxyConfig).Times(0);
  std::unique_ptr<net::ProxyConfigService> proxy_config_service =
      CreateService();

  net::ProxyConfigWithAnnotation config;
  ASSERT_EQ(proxy_config_service->GetLatestProxyConfig(&config),
            net::ProxyConfigService::ConfigAvailability::CONFIG_VALID);

  EXPECT_TRUE(config.value().Equals(net::ProxyConfig::CreateFromCustomPacURL(
      GURL("http://not-a.real-domain:8082/proxy.pac"))));
}

TEST_F(ProxyConfigServiceTest, ConfigFromCloudPolicy_FixedServers) {
  fake_dm_storage_->SetProxyPolicies(
      "fixed_servers",
      /*pac_url=*/std::nullopt,
      /*proxy_server=*/"http://not-a.real-domain:7918");
  EXPECT_CALL(*mock_fallback_, GetLatestProxyConfig).Times(0);
  std::unique_ptr<net::ProxyConfigService> proxy_config_service =
      CreateService();

  net::ProxyConfigWithAnnotation config;
  ASSERT_EQ(proxy_config_service->GetLatestProxyConfig(&config),
            net::ProxyConfigService::ConfigAvailability::CONFIG_VALID);

  net::ProxyList proxy_list;
  proxy_list.SetSingleProxyServer(net::ProxyUriToProxyServer(
      "http://not-a.real-domain:7918", net::ProxyServer::SCHEME_HTTP));
  EXPECT_TRUE(
      config.value().Equals(net::ProxyConfig::CreateForTesting(proxy_list)));
}

TEST_F(ProxyConfigServiceTest, ConfigFromCloudPolicy_System) {
  fake_dm_storage_->SetProxyPolicies("system",
                                     /*pac_url=*/std::nullopt,
                                     /*proxy_server=*/std::nullopt);
  // The "system" mode should delegate to the fallback service.
  EXPECT_CALL(*mock_fallback_, GetLatestProxyConfig)
      .WillOnce([](net::ProxyConfigWithAnnotation* config) {
        *config = net::ProxyConfigWithAnnotation(
            net::ProxyConfig::CreateAutoDetect(), TRAFFIC_ANNOTATION_FOR_TESTS);
        return net::ProxyConfigService::ConfigAvailability::CONFIG_VALID;
      });
  std::unique_ptr<net::ProxyConfigService> proxy_config_service =
      CreateService();

  net::ProxyConfigWithAnnotation config;
  ASSERT_EQ(proxy_config_service->GetLatestProxyConfig(&config),
            net::ProxyConfigService::ConfigAvailability::CONFIG_VALID);

  EXPECT_TRUE(config.value().auto_detect());
}

TEST_F(ProxyConfigServiceTest, NoPolicies_Fallback) {
  EXPECT_CALL(*mock_fallback_, GetLatestProxyConfig)
      .WillOnce([](net::ProxyConfigWithAnnotation* config) {
        *config = net::ProxyConfigWithAnnotation(
            net::ProxyConfig::CreateAutoDetect(), TRAFFIC_ANNOTATION_FOR_TESTS);
        return net::ProxyConfigService::ConfigAvailability::CONFIG_VALID;
      });
  std::unique_ptr<net::ProxyConfigService> proxy_config_service =
      CreateService();

  net::ProxyConfigWithAnnotation config;
  ASSERT_EQ(proxy_config_service->GetLatestProxyConfig(&config),
            net::ProxyConfigService::ConfigAvailability::CONFIG_VALID);

  EXPECT_TRUE(config.value().auto_detect());
}

TEST_F(ProxyConfigServiceTest, EmptyPolicies_Fallback) {
  fake_dm_storage_->SetProxyPolicies(/*proxy_mode=*/std::nullopt,
                                     /*pac_url=*/std::nullopt,
                                     /*proxy_server=*/std::nullopt);
  EXPECT_CALL(*mock_fallback_, GetLatestProxyConfig)
      .WillOnce([](net::ProxyConfigWithAnnotation* config) {
        *config = net::ProxyConfigWithAnnotation(
            net::ProxyConfig::CreateAutoDetect(), TRAFFIC_ANNOTATION_FOR_TESTS);
        return net::ProxyConfigService::ConfigAvailability::CONFIG_VALID;
      });
  std::unique_ptr<net::ProxyConfigService> proxy_config_service =
      CreateService();

  net::ProxyConfigWithAnnotation config;
  ASSERT_EQ(proxy_config_service->GetLatestProxyConfig(&config),
            net::ProxyConfigService::ConfigAvailability::CONFIG_VALID);

  EXPECT_TRUE(config.value().auto_detect());
}

TEST_F(ProxyConfigServiceTest, UnknownProxyMode_Fallback) {
  fake_dm_storage_->SetProxyPolicies(/*proxy_mode=*/"not_a_real_proxy_mode",
                                     /*pac_url=*/std::nullopt,
                                     /*proxy_server=*/std::nullopt);
  EXPECT_CALL(*mock_fallback_, GetLatestProxyConfig)
      .WillOnce([](net::ProxyConfigWithAnnotation* config) {
        *config = net::ProxyConfigWithAnnotation(
            net::ProxyConfig::CreateAutoDetect(), TRAFFIC_ANNOTATION_FOR_TESTS);
        return net::ProxyConfigService::ConfigAvailability::CONFIG_VALID;
      });
  std::unique_ptr<net::ProxyConfigService> proxy_config_service =
      CreateService();

  net::ProxyConfigWithAnnotation config;
  ASSERT_EQ(proxy_config_service->GetLatestProxyConfig(&config),
            net::ProxyConfigService::ConfigAvailability::CONFIG_VALID);

  EXPECT_TRUE(config.value().auto_detect());
}

TEST_F(ProxyConfigServiceTest, UnparsableOmahaSettings_Fallback) {
  fake_dm_storage_->WriteInvalidPolicyData();
  EXPECT_CALL(*mock_fallback_, GetLatestProxyConfig)
      .WillOnce([](net::ProxyConfigWithAnnotation* config) {
        *config = net::ProxyConfigWithAnnotation(
            net::ProxyConfig::CreateAutoDetect(), TRAFFIC_ANNOTATION_FOR_TESTS);
        return net::ProxyConfigService::ConfigAvailability::CONFIG_VALID;
      });
  std::unique_ptr<net::ProxyConfigService> proxy_config_service =
      CreateService();

  net::ProxyConfigWithAnnotation config;
  ASSERT_EQ(proxy_config_service->GetLatestProxyConfig(&config),
            net::ProxyConfigService::ConfigAvailability::CONFIG_VALID);

  EXPECT_TRUE(config.value().auto_detect());
}

TEST_F(ProxyConfigServiceTest, ConfigFromLocalPolicy) {
  system_policy_config_ = ProxyConfigAndOverridePrecedence();
  system_policy_config_->config = net::ProxyConfig::CreateDirect();
  EXPECT_CALL(*mock_fallback_, GetLatestProxyConfig).Times(0);
  std::unique_ptr<net::ProxyConfigService> proxy_config_service =
      CreateService();

  net::ProxyConfigWithAnnotation config;
  ASSERT_EQ(proxy_config_service->GetLatestProxyConfig(&config),
            net::ProxyConfigService::ConfigAvailability::CONFIG_VALID);

  EXPECT_TRUE(config.value().Equals(net::ProxyConfig::CreateDirect()));
}

TEST_F(ProxyConfigServiceTest, LocalPolicyOverridesByDefault) {
  fake_dm_storage_->SetProxyPolicies("direct",
                                     /*pac_url=*/std::nullopt,
                                     /*proxy_server=*/std::nullopt);
  system_policy_config_ = ProxyConfigAndOverridePrecedence();
  system_policy_config_->config = net::ProxyConfig::CreateAutoDetect();
  std::unique_ptr<net::ProxyConfigService> proxy_config_service =
      CreateService();

  net::ProxyConfigWithAnnotation config;
  ASSERT_EQ(proxy_config_service->GetLatestProxyConfig(&config),
            net::ProxyConfigService::ConfigAvailability::CONFIG_VALID);

  EXPECT_TRUE(config.value().Equals(net::ProxyConfig::CreateAutoDetect()));
}

TEST_F(ProxyConfigServiceTest, CloudPolicyOverridesPlatformPolicy_ByLocal) {
  fake_dm_storage_->SetProxyPolicies("direct",
                                     /*pac_url=*/std::nullopt,
                                     /*proxy_server=*/std::nullopt);
  system_policy_config_ = ProxyConfigAndOverridePrecedence();
  system_policy_config_->config = net::ProxyConfig::CreateAutoDetect();
  system_policy_config_->cloud_policy_overrides_platform_policy = true;
  std::unique_ptr<net::ProxyConfigService> proxy_config_service =
      CreateService();

  net::ProxyConfigWithAnnotation config;
  ASSERT_EQ(proxy_config_service->GetLatestProxyConfig(&config),
            net::ProxyConfigService::ConfigAvailability::CONFIG_VALID);

  EXPECT_TRUE(config.value().Equals(net::ProxyConfig::CreateDirect()));
}

TEST_F(ProxyConfigServiceTest, CloudPolicyOverridesPlatformPolicy_ByCloud) {
  fake_dm_storage_->SetProxyPolicies(
      "direct",
      /*pac_url=*/std::nullopt,
      /*proxy_server=*/std::nullopt,
      /*cloud_policy_overrides_platform_policy=*/true);
  system_policy_config_ = ProxyConfigAndOverridePrecedence();
  system_policy_config_->config = net::ProxyConfig::CreateAutoDetect();
  std::unique_ptr<net::ProxyConfigService> proxy_config_service =
      CreateService();

  net::ProxyConfigWithAnnotation config;
  ASSERT_EQ(proxy_config_service->GetLatestProxyConfig(&config),
            net::ProxyConfigService::ConfigAvailability::CONFIG_VALID);

  EXPECT_TRUE(config.value().Equals(net::ProxyConfig::CreateDirect()));
}

// If the "CloudPolicyOverridesPlatformPolicy" from cloud policy disagrees with
// that from local policy, the cloud policy should be honored.
TEST_F(ProxyConfigServiceTest,
       CloudPolicyOverridesPlatformPolicy_CloudAndLocalConflict) {
  fake_dm_storage_->SetProxyPolicies(
      "direct",
      /*pac_url=*/std::nullopt,
      /*proxy_server=*/std::nullopt,
      /*cloud_policy_overrides_platform_policy=*/true);
  system_policy_config_ = ProxyConfigAndOverridePrecedence();
  system_policy_config_->config = net::ProxyConfig::CreateAutoDetect();
  system_policy_config_->cloud_policy_overrides_platform_policy = false;
  std::unique_ptr<net::ProxyConfigService> proxy_config_service =
      CreateService();

  net::ProxyConfigWithAnnotation config;
  ASSERT_EQ(proxy_config_service->GetLatestProxyConfig(&config),
            net::ProxyConfigService::ConfigAvailability::CONFIG_VALID);

  EXPECT_TRUE(config.value().Equals(net::ProxyConfig::CreateDirect()));
}

}  // namespace enterprise_companion