#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 {
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",
std::nullopt,
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",
std::nullopt,
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",
"http://not-a.real-domain:8082/proxy.pac",
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",
std::nullopt,
"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",
std::nullopt,
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, 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(std::nullopt,
std::nullopt,
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("not_a_real_proxy_mode",
std::nullopt,
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",
std::nullopt,
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",
std::nullopt,
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",
std::nullopt,
std::nullopt,
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()));
}
TEST_F(ProxyConfigServiceTest,
CloudPolicyOverridesPlatformPolicy_CloudAndLocalConflict) {
fake_dm_storage_->SetProxyPolicies(
"direct",
std::nullopt,
std::nullopt,
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()));
}
}