| Index: chromeos/network/auto_connect_handler_unittest.cc
|
| diff --git a/chromeos/network/auto_connect_handler_unittest.cc b/chromeos/network/auto_connect_handler_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..dda7ecc0ca660cab60645775db042622bb242f57
|
| --- /dev/null
|
| +++ b/chromeos/network/auto_connect_handler_unittest.cc
|
| @@ -0,0 +1,475 @@
|
| +// Copyright 2014 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "chromeos/network/auto_connect_handler.h"
|
| +
|
| +#include <string>
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/callback.h"
|
| +#include "base/files/file_util.h"
|
| +#include "base/json/json_reader.h"
|
| +#include "base/memory/scoped_ptr.h"
|
| +#include "base/message_loop/message_loop.h"
|
| +#include "base/run_loop.h"
|
| +#include "base/strings/stringprintf.h"
|
| +#include "chromeos/cert_loader.h"
|
| +#include "chromeos/dbus/dbus_thread_manager.h"
|
| +#include "chromeos/dbus/shill_device_client.h"
|
| +#include "chromeos/dbus/shill_manager_client.h"
|
| +#include "chromeos/dbus/shill_profile_client.h"
|
| +#include "chromeos/dbus/shill_service_client.h"
|
| +#include "chromeos/network/client_cert_resolver.h"
|
| +#include "chromeos/network/managed_network_configuration_handler_impl.h"
|
| +#include "chromeos/network/network_configuration_handler.h"
|
| +#include "chromeos/network/network_profile_handler.h"
|
| +#include "chromeos/network/network_state_handler.h"
|
| +#include "chromeos/network/onc/onc_utils.h"
|
| +#include "components/onc/onc_constants.h"
|
| +#include "crypto/scoped_nss_types.h"
|
| +#include "crypto/scoped_test_nss_db.h"
|
| +#include "net/base/net_errors.h"
|
| +#include "net/base/test_data_directory.h"
|
| +#include "net/cert/nss_cert_database_chromeos.h"
|
| +#include "net/cert/x509_certificate.h"
|
| +#include "net/test/cert_test_util.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +#include "third_party/cros_system_api/dbus/service_constants.h"
|
| +
|
| +// http://crbug.com/418369
|
| +#ifdef NDEBUG
|
| +
|
| +namespace chromeos {
|
| +
|
| +namespace {
|
| +
|
| +const char* kUserHash = "user_hash";
|
| +
|
| +void ConfigureCallback(const dbus::ObjectPath& result) {
|
| +}
|
| +
|
| +void FailErrorCallback(const std::string& error_name,
|
| + const std::string& error_message) {
|
| + // This function is not expected to be called.
|
| + EXPECT_TRUE(false);
|
| +}
|
| +
|
| +class TestCertResolveObserver : public ClientCertResolver::Observer {
|
| + public:
|
| + explicit TestCertResolveObserver(ClientCertResolver* cert_resolver)
|
| + : changed_network_properties_(false), cert_resolver_(cert_resolver) {
|
| + cert_resolver_->AddObserver(this);
|
| + }
|
| +
|
| + void ResolveRequestCompleted(bool changed_network_properties) override {
|
| + cert_resolver_->RemoveObserver(this);
|
| + changed_network_properties_ = changed_network_properties;
|
| + }
|
| +
|
| + bool DidNetworkPropertiesChange() { return changed_network_properties_; }
|
| +
|
| + private:
|
| + bool changed_network_properties_;
|
| + ClientCertResolver* cert_resolver_;
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +class AutoConnectHandlerTest : public testing::Test {
|
| + public:
|
| + AutoConnectHandlerTest()
|
| + : test_manager_client_(nullptr), test_service_client_(nullptr) {}
|
| +
|
| + void SetUp() override {
|
| + ASSERT_TRUE(test_nssdb_.is_open());
|
| +
|
| + // Use the same DB for public and private slot.
|
| + test_nsscertdb_.reset(new net::NSSCertDatabaseChromeOS(
|
| + crypto::ScopedPK11Slot(PK11_ReferenceSlot(test_nssdb_.slot())),
|
| + crypto::ScopedPK11Slot(PK11_ReferenceSlot(test_nssdb_.slot()))));
|
| + test_nsscertdb_->SetSlowTaskRunnerForTest(message_loop_.task_runner());
|
| +
|
| + CertLoader::Initialize();
|
| + CertLoader::ForceHardwareBackedForTesting();
|
| +
|
| + DBusThreadManager::Initialize();
|
| + DBusThreadManager* dbus_manager = DBusThreadManager::Get();
|
| + test_manager_client_ =
|
| + dbus_manager->GetShillManagerClient()->GetTestInterface();
|
| + test_service_client_ =
|
| + dbus_manager->GetShillServiceClient()->GetTestInterface();
|
| +
|
| + test_manager_client_->AddTechnology(shill::kTypeWifi, true /* enabled */);
|
| + dbus_manager->GetShillDeviceClient()->GetTestInterface()->AddDevice(
|
| + "/device/wifi1", shill::kTypeWifi, "wifi_device1");
|
| + test_manager_client_->AddTechnology(shill::kTypeCellular,
|
| + true /* enabled */);
|
| + dbus_manager->GetShillProfileClient()->GetTestInterface()->AddProfile(
|
| + "shared_profile_path", std::string() /* shared profile */);
|
| + dbus_manager->GetShillProfileClient()->GetTestInterface()->AddProfile(
|
| + "user_profile_path", kUserHash);
|
| +
|
| + base::RunLoop().RunUntilIdle();
|
| + LoginState::Initialize();
|
| + network_state_handler_.reset(NetworkStateHandler::InitializeForTest());
|
| + network_config_handler_.reset(
|
| + NetworkConfigurationHandler::InitializeForTest(
|
| + network_state_handler_.get()));
|
| +
|
| + network_profile_handler_.reset(new NetworkProfileHandler());
|
| + network_profile_handler_->Init();
|
| +
|
| + managed_config_handler_.reset(new ManagedNetworkConfigurationHandlerImpl());
|
| + managed_config_handler_->Init(
|
| + network_state_handler_.get(), network_profile_handler_.get(),
|
| + network_config_handler_.get(), nullptr /* network_device_handler */);
|
| +
|
| + client_cert_resolver_.reset(new ClientCertResolver());
|
| + client_cert_resolver_->Init(network_state_handler_.get(),
|
| + managed_config_handler_.get());
|
| + client_cert_resolver_->SetSlowTaskRunnerForTest(
|
| + message_loop_.task_runner());
|
| +
|
| + auto_connect_handler_.reset(new AutoConnectHandler());
|
| + auto_connect_handler_->Init(client_cert_resolver_.get(),
|
| + nullptr, // no connection handler
|
| + network_state_handler_.get(),
|
| + managed_config_handler_.get());
|
| +
|
| + base::RunLoop().RunUntilIdle();
|
| + }
|
| +
|
| + void TearDown() override {
|
| + auto_connect_handler_.reset();
|
| + client_cert_resolver_.reset();
|
| + managed_config_handler_.reset();
|
| + network_profile_handler_.reset();
|
| + network_config_handler_.reset();
|
| + network_state_handler_.reset();
|
| + CertLoader::Shutdown();
|
| + LoginState::Shutdown();
|
| + DBusThreadManager::Shutdown();
|
| + }
|
| +
|
| + protected:
|
| + bool Configure(const std::string& json_string) {
|
| + scoped_ptr<base::DictionaryValue> json_dict =
|
| + onc::ReadDictionaryFromJson(json_string);
|
| + if (!json_dict) {
|
| + LOG(ERROR) << "Error parsing json: " << json_string;
|
| + return false;
|
| + }
|
| + DBusThreadManager::Get()->GetShillManagerClient()->ConfigureService(
|
| + *json_dict, base::Bind(&ConfigureCallback),
|
| + base::Bind(&FailErrorCallback));
|
| + base::RunLoop().RunUntilIdle();
|
| + return true;
|
| + }
|
| +
|
| + std::string GetServiceState(const std::string& service_path) {
|
| + const base::DictionaryValue* properties =
|
| + test_service_client_->GetServiceProperties(service_path);
|
| + std::string result;
|
| + if (properties)
|
| + properties->GetStringWithoutPathExpansion(shill::kStateProperty, &result);
|
| + return result;
|
| + }
|
| +
|
| + void StartCertLoader() {
|
| + CertLoader::Get()->StartWithNSSDB(test_nsscertdb_.get());
|
| + base::RunLoop().RunUntilIdle();
|
| + }
|
| +
|
| + void LoginToRegularUser() {
|
| + LoginState::Get()->SetLoggedInState(LoginState::LOGGED_IN_ACTIVE,
|
| + LoginState::LOGGED_IN_USER_REGULAR);
|
| + base::RunLoop().RunUntilIdle();
|
| + }
|
| +
|
| + scoped_refptr<net::X509Certificate> ImportTestClientCert() {
|
| + net::CertificateList ca_cert_list = net::CreateCertificateListFromFile(
|
| + net::GetTestCertsDirectory(), "client_1_ca.pem",
|
| + net::X509Certificate::FORMAT_AUTO);
|
| + if (ca_cert_list.empty()) {
|
| + LOG(ERROR) << "No CA cert loaded.";
|
| + return nullptr;
|
| + }
|
| + net::NSSCertDatabase::ImportCertFailureList failures;
|
| + EXPECT_TRUE(test_nsscertdb_->ImportCACerts(
|
| + ca_cert_list, net::NSSCertDatabase::TRUST_DEFAULT, &failures));
|
| + if (!failures.empty()) {
|
| + LOG(ERROR) << net::ErrorToString(failures[0].net_error);
|
| + return nullptr;
|
| + }
|
| +
|
| + // Import a client cert signed by that CA.
|
| + scoped_refptr<net::X509Certificate> client_cert(
|
| + net::ImportClientCertAndKeyFromFile(net::GetTestCertsDirectory(),
|
| + "client_1.pem", "client_1.pk8",
|
| + test_nssdb_.slot()));
|
| + return client_cert;
|
| + }
|
| +
|
| + void SetupPolicy(const std::string& network_configs_json,
|
| + const base::DictionaryValue& global_config,
|
| + bool user_policy) {
|
| + scoped_ptr<base::ListValue> network_configs(new base::ListValue);
|
| + if (!network_configs_json.empty()) {
|
| + std::string error;
|
| + base::Value* network_configs_value = base::JSONReader::ReadAndReturnError(
|
| + network_configs_json, base::JSON_ALLOW_TRAILING_COMMAS, nullptr,
|
| + &error);
|
| + ASSERT_TRUE(network_configs_value) << error;
|
| + base::ListValue* network_configs_list = nullptr;
|
| + ASSERT_TRUE(network_configs_value->GetAsList(&network_configs_list));
|
| + network_configs.reset(network_configs_list);
|
| + }
|
| +
|
| + if (user_policy) {
|
| + managed_config_handler_->SetPolicy(::onc::ONC_SOURCE_USER_POLICY,
|
| + kUserHash, *network_configs,
|
| + global_config);
|
| + } else {
|
| + managed_config_handler_->SetPolicy(::onc::ONC_SOURCE_DEVICE_POLICY,
|
| + std::string(), // no username hash
|
| + *network_configs, global_config);
|
| + }
|
| + base::RunLoop().RunUntilIdle();
|
| + }
|
| +
|
| + scoped_ptr<AutoConnectHandler> auto_connect_handler_;
|
| + scoped_ptr<ClientCertResolver> client_cert_resolver_;
|
| + scoped_ptr<NetworkStateHandler> network_state_handler_;
|
| + scoped_ptr<NetworkConfigurationHandler> network_config_handler_;
|
| + scoped_ptr<ManagedNetworkConfigurationHandlerImpl> managed_config_handler_;
|
| + scoped_ptr<NetworkProfileHandler> network_profile_handler_;
|
| + ShillManagerClient::TestInterface* test_manager_client_;
|
| + ShillServiceClient::TestInterface* test_service_client_;
|
| + crypto::ScopedTestNSSDB test_nssdb_;
|
| + scoped_ptr<net::NSSCertDatabaseChromeOS> test_nsscertdb_;
|
| + base::MessageLoopForUI message_loop_;
|
| +
|
| + private:
|
| + DISALLOW_COPY_AND_ASSIGN(AutoConnectHandlerTest);
|
| +};
|
| +
|
| +namespace {
|
| +
|
| +const char* kConfigUnmanagedSharedConnected =
|
| + "{ \"GUID\": \"wifi0\", \"Type\": \"wifi\", \"State\": \"online\", "
|
| + " \"Security\": \"wpa\" }";
|
| +const char* kConfigManagedSharedConnectable =
|
| + "{ \"GUID\": \"wifi1\", \"Type\": \"wifi\", \"State\": \"idle\", "
|
| + " \"Connectable\": true, \"Security\": \"wpa\" }";
|
| +
|
| +const char* kPolicy =
|
| + "[ { \"GUID\": \"wifi1\","
|
| + " \"Name\": \"wifi1\","
|
| + " \"Type\": \"WiFi\","
|
| + " \"WiFi\": {"
|
| + " \"Security\": \"WPA-PSK\","
|
| + " \"SSID\": \"wifi1\","
|
| + " \"Passphrase\": \"passphrase\""
|
| + " }"
|
| + "} ]";
|
| +
|
| +const char* kPolicyCertPattern =
|
| + "[ { \"GUID\": \"wifi1\","
|
| + " \"Name\": \"wifi1\","
|
| + " \"Type\": \"WiFi\","
|
| + " \"WiFi\": {"
|
| + " \"Security\": \"WPA-EAP\","
|
| + " \"SSID\": \"wifi1\","
|
| + " \"EAP\": {"
|
| + " \"Outer\": \"EAP-TLS\","
|
| + " \"ClientCertType\": \"Pattern\","
|
| + " \"ClientCertPattern\": {"
|
| + " \"Issuer\": {"
|
| + " \"CommonName\": \"B CA\""
|
| + " }"
|
| + " }"
|
| + " }"
|
| + " }"
|
| + "} ]";
|
| +} // namespace
|
| +
|
| +TEST_F(AutoConnectHandlerTest, ReconnectOnCertLoading) {
|
| + EXPECT_TRUE(Configure(kConfigUnmanagedSharedConnected));
|
| + EXPECT_TRUE(Configure(kConfigManagedSharedConnectable));
|
| + test_manager_client_->SetBestServiceToConnect("wifi1");
|
| +
|
| + // User login shouldn't trigger any change until the certificates and policy
|
| + // are loaded.
|
| + LoginToRegularUser();
|
| + EXPECT_EQ(shill::kStateOnline, GetServiceState("wifi0"));
|
| + EXPECT_EQ(shill::kStateIdle, GetServiceState("wifi1"));
|
| +
|
| + // Applying the policy which restricts autoconnect should disconnect from the
|
| + // shared, unmanaged network.
|
| + base::DictionaryValue global_config;
|
| + global_config.SetBooleanWithoutPathExpansion(
|
| + ::onc::global_network_config::kAllowOnlyPolicyNetworksToAutoconnect,
|
| + true);
|
| +
|
| + SetupPolicy(std::string(), // no network configs
|
| + base::DictionaryValue(), // no global config
|
| + true); // load as user policy
|
| + SetupPolicy(kPolicy, global_config, false /* load as device policy */);
|
| + EXPECT_EQ(shill::kStateIdle, GetServiceState("wifi0"));
|
| + EXPECT_EQ(shill::kStateIdle, GetServiceState("wifi1"));
|
| +
|
| + // Certificate loading should trigger connecting to the 'best' network.
|
| + StartCertLoader();
|
| + EXPECT_EQ(shill::kStateIdle, GetServiceState("wifi0"));
|
| + EXPECT_EQ(shill::kStateOnline, GetServiceState("wifi1"));
|
| +}
|
| +
|
| +TEST_F(AutoConnectHandlerTest, ReconnectOnCertPatternResolved) {
|
| + EXPECT_TRUE(Configure(kConfigUnmanagedSharedConnected));
|
| + EXPECT_TRUE(Configure(kConfigManagedSharedConnectable));
|
| + test_manager_client_->SetBestServiceToConnect("wifi0");
|
| +
|
| + SetupPolicy(std::string(), // no device policy
|
| + base::DictionaryValue(), // no global config
|
| + false); // load as device policy
|
| + LoginToRegularUser();
|
| + StartCertLoader();
|
| + SetupPolicy(kPolicyCertPattern,
|
| + base::DictionaryValue(), // no global config
|
| + true); // load as user policy
|
| +
|
| + EXPECT_EQ(shill::kStateOnline, GetServiceState("wifi0"));
|
| + EXPECT_EQ(shill::kStateIdle, GetServiceState("wifi1"));
|
| +
|
| + test_manager_client_->SetBestServiceToConnect("wifi1");
|
| + TestCertResolveObserver observer(client_cert_resolver_.get());
|
| +
|
| + scoped_refptr<net::X509Certificate> cert = ImportTestClientCert();
|
| + ASSERT_TRUE(cert.get());
|
| +
|
| + base::RunLoop().RunUntilIdle();
|
| + EXPECT_TRUE(observer.DidNetworkPropertiesChange());
|
| +
|
| + EXPECT_EQ(shill::kStateIdle, GetServiceState("wifi0"));
|
| + EXPECT_EQ(shill::kStateOnline, GetServiceState("wifi1"));
|
| +}
|
| +
|
| +// Ensure that resolving of certificate patterns only triggers a reconnect if at
|
| +// least one pattern was resolved.
|
| +TEST_F(AutoConnectHandlerTest, NoReconnectIfNoCertResolved) {
|
| + EXPECT_TRUE(Configure(kConfigUnmanagedSharedConnected));
|
| + EXPECT_TRUE(Configure(kConfigManagedSharedConnectable));
|
| + test_manager_client_->SetBestServiceToConnect("wifi0");
|
| +
|
| + SetupPolicy(std::string(), // no device policy
|
| + base::DictionaryValue(), // no global config
|
| + false); // load as device policy
|
| + LoginToRegularUser();
|
| + StartCertLoader();
|
| + SetupPolicy(kPolicy,
|
| + base::DictionaryValue(), // no global config
|
| + true); // load as user policy
|
| +
|
| + EXPECT_EQ(shill::kStateOnline, GetServiceState("wifi0"));
|
| + EXPECT_EQ(shill::kStateIdle, GetServiceState("wifi1"));
|
| +
|
| + test_manager_client_->SetBestServiceToConnect("wifi1");
|
| + TestCertResolveObserver observer(client_cert_resolver_.get());
|
| + scoped_refptr<net::X509Certificate> cert = ImportTestClientCert();
|
| + ASSERT_TRUE(cert.get());
|
| +
|
| + base::RunLoop().RunUntilIdle();
|
| + EXPECT_FALSE(observer.DidNetworkPropertiesChange());
|
| +
|
| + EXPECT_EQ(shill::kStateOnline, GetServiceState("wifi0"));
|
| + EXPECT_EQ(shill::kStateIdle, GetServiceState("wifi1"));
|
| +}
|
| +
|
| +TEST_F(AutoConnectHandlerTest, DisconnectOnPolicyLoading) {
|
| + EXPECT_TRUE(Configure(kConfigUnmanagedSharedConnected));
|
| + EXPECT_TRUE(Configure(kConfigManagedSharedConnectable));
|
| +
|
| + // User login and certificate loading shouldn't trigger any change until the
|
| + // policy is loaded.
|
| + LoginToRegularUser();
|
| + StartCertLoader();
|
| + EXPECT_EQ(shill::kStateOnline, GetServiceState("wifi0"));
|
| + EXPECT_EQ(shill::kStateIdle, GetServiceState("wifi1"));
|
| +
|
| + base::DictionaryValue global_config;
|
| + global_config.SetBooleanWithoutPathExpansion(
|
| + ::onc::global_network_config::kAllowOnlyPolicyNetworksToAutoconnect,
|
| + true);
|
| +
|
| + // Applying the policy which restricts autoconnect should disconnect from the
|
| + // shared, unmanaged network.
|
| + // Because no best service is set, the fake implementation of
|
| + // ConnectToBestServices will be a no-op.
|
| + SetupPolicy(kPolicy, global_config, false /* load as device policy */);
|
| + EXPECT_EQ(shill::kStateIdle, GetServiceState("wifi0"));
|
| + EXPECT_EQ(shill::kStateIdle, GetServiceState("wifi1"));
|
| +}
|
| +
|
| +// After login a reconnect is triggered even if there is no managed network.
|
| +TEST_F(AutoConnectHandlerTest, ReconnectAfterLogin) {
|
| + EXPECT_TRUE(Configure(kConfigUnmanagedSharedConnected));
|
| + EXPECT_TRUE(Configure(kConfigManagedSharedConnectable));
|
| + test_manager_client_->SetBestServiceToConnect("wifi1");
|
| +
|
| + // User login and certificate loading shouldn't trigger any change until the
|
| + // policy is loaded.
|
| + LoginToRegularUser();
|
| + StartCertLoader();
|
| + EXPECT_EQ(shill::kStateOnline, GetServiceState("wifi0"));
|
| + EXPECT_EQ(shill::kStateIdle, GetServiceState("wifi1"));
|
| +
|
| + // Applying an empty device policy will not trigger anything yet, until also
|
| + // the user policy is applied.
|
| + SetupPolicy(std::string(), // no network configs
|
| + base::DictionaryValue(), // no global config
|
| + false); // load as device policy
|
| + EXPECT_EQ(shill::kStateOnline, GetServiceState("wifi0"));
|
| + EXPECT_EQ(shill::kStateIdle, GetServiceState("wifi1"));
|
| +
|
| + // Applying also an empty user policy should trigger connecting to the 'best'
|
| + // network.
|
| + SetupPolicy(std::string(), // no network configs
|
| + base::DictionaryValue(), // no global config
|
| + true); // load as user policy
|
| + EXPECT_EQ(shill::kStateIdle, GetServiceState("wifi0"));
|
| + EXPECT_EQ(shill::kStateOnline, GetServiceState("wifi1"));
|
| +}
|
| +
|
| +TEST_F(AutoConnectHandlerTest, ManualConnectAbortsReconnectAfterLogin) {
|
| + EXPECT_TRUE(Configure(kConfigUnmanagedSharedConnected));
|
| + EXPECT_TRUE(Configure(kConfigManagedSharedConnectable));
|
| + test_manager_client_->SetBestServiceToConnect("wifi1");
|
| +
|
| + // User login and certificate loading shouldn't trigger any change until the
|
| + // policy is loaded.
|
| + LoginToRegularUser();
|
| + StartCertLoader();
|
| + SetupPolicy(std::string(), // no network configs
|
| + base::DictionaryValue(), // no global config
|
| + false); // load as device policy
|
| +
|
| + EXPECT_EQ(shill::kStateOnline, GetServiceState("wifi0"));
|
| + EXPECT_EQ(shill::kStateIdle, GetServiceState("wifi1"));
|
| +
|
| + // A manual connect request should prevent a reconnect after login.
|
| + auto_connect_handler_->ConnectToNetworkRequested(
|
| + std::string() /* service_path */);
|
| +
|
| + // Applying the user policy after login would usually trigger connecting to
|
| + // the 'best' network. But the manual connect prevents this.
|
| + SetupPolicy(std::string(), // no network configs
|
| + base::DictionaryValue(), // no global config
|
| + true); // load as user policy
|
| + EXPECT_EQ(shill::kStateOnline, GetServiceState("wifi0"));
|
| + EXPECT_EQ(shill::kStateIdle, GetServiceState("wifi1"));
|
| +}
|
| +
|
| +} // namespace chromeos
|
| +
|
| +#endif
|
|
|