diff options
author | pneubeck@chromium.org <pneubeck@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-04-19 20:35:07 +0000 |
---|---|---|
committer | pneubeck@chromium.org <pneubeck@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-04-19 20:35:07 +0000 |
commit | 79affb7effb751deed7336a1828de3cdc1fdde04 (patch) | |
tree | 8cf54f6061fd99dd1d4d43f491ea3f3164965613 /chromeos | |
parent | eda0a0b16697dab1fc2d45d6cfe38652044b88d5 (diff) | |
download | chromium_src-79affb7effb751deed7336a1828de3cdc1fdde04.zip chromium_src-79affb7effb751deed7336a1828de3cdc1fdde04.tar.gz chromium_src-79affb7effb751deed7336a1828de3cdc1fdde04.tar.bz2 |
Adding policy support to the new network configuration stack.
Adapts in particular the ManagedNetworkConfigurationHandler, the
networkingPrivate extension API and the network configuration extension.
BUG=223869
TBR=thestig@chromium.org (for chrome_browser_chromeos.gypi)
Review URL: https://chromiumcodereview.appspot.com/12676017
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@195267 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chromeos')
40 files changed, 1701 insertions, 165 deletions
diff --git a/chromeos/chromeos.gyp b/chromeos/chromeos.gyp index 4831537..5f9df89 100644 --- a/chromeos/chromeos.gyp +++ b/chromeos/chromeos.gyp @@ -365,6 +365,8 @@ 'dbus/mock_system_clock_client.h', 'dbus/mock_update_engine_client.cc', 'dbus/mock_update_engine_client.h', + 'dbus/shill_profile_client_stub.cc', + 'dbus/shill_profile_client_stub.h', 'disks/mock_disk_mount_manager.cc', 'disks/mock_disk_mount_manager.h', 'ime/mock_component_extension_ime_manager_delegate.cc', @@ -461,6 +463,7 @@ 'login/login_state_unittest.cc', 'network/cros_network_functions_unittest.cc', 'network/geolocation_handler_unittest.cc', + 'network/managed_network_configuration_handler_unittest.cc', 'network/network_change_notifier_chromeos_unittest.cc', 'network/network_configuration_handler_unittest.cc', 'network/network_event_log_unittest.cc', diff --git a/chromeos/dbus/mock_shill_manager_client.cc b/chromeos/dbus/mock_shill_manager_client.cc index 2016e2b..309b48e 100644 --- a/chromeos/dbus/mock_shill_manager_client.cc +++ b/chromeos/dbus/mock_shill_manager_client.cc @@ -4,6 +4,8 @@ #include "chromeos/dbus/mock_shill_manager_client.h" +#include "dbus/object_path.h" + namespace chromeos { MockShillManagerClient::MockShillManagerClient() {} diff --git a/chromeos/dbus/mock_shill_manager_client.h b/chromeos/dbus/mock_shill_manager_client.h index 57f2449..d507762 100644 --- a/chromeos/dbus/mock_shill_manager_client.h +++ b/chromeos/dbus/mock_shill_manager_client.h @@ -41,6 +41,11 @@ class MockShillManagerClient : public ShillManagerClient { MOCK_METHOD3(ConfigureService, void(const base::DictionaryValue& properties, const ObjectPathCallback& callback, const ErrorCallback& error_callback)); + MOCK_METHOD4(ConfigureServiceForProfile, + void(const dbus::ObjectPath& profile_path, + const base::DictionaryValue& properties, + const ObjectPathCallback& callback, + const ErrorCallback& error_callback)); MOCK_METHOD3(GetService, void(const base::DictionaryValue& properties, const ObjectPathCallback& callback, const ErrorCallback& error_callback)); diff --git a/chromeos/dbus/mock_shill_profile_client.h b/chromeos/dbus/mock_shill_profile_client.h index 078bb60..b18a843 100644 --- a/chromeos/dbus/mock_shill_profile_client.h +++ b/chromeos/dbus/mock_shill_profile_client.h @@ -39,6 +39,7 @@ class MockShillProfileClient : public ShillProfileClient { const std::string& entry_path, const base::Closure& callback, const ErrorCallback& error_callback)); + MOCK_METHOD0(GetTestInterface, TestInterface*()); }; } // namespace chromeos diff --git a/chromeos/dbus/shill_device_client.h b/chromeos/dbus/shill_device_client.h index e865753..6a3fb35 100644 --- a/chromeos/dbus/shill_device_client.h +++ b/chromeos/dbus/shill_device_client.h @@ -55,7 +55,7 @@ class CHROMEOS_EXPORT ShillDeviceClient { virtual std::string GetDevicePathForType(const std::string& type) = 0; protected: - ~TestInterface() {} + virtual ~TestInterface() {} }; virtual ~ShillDeviceClient(); diff --git a/chromeos/dbus/shill_manager_client.cc b/chromeos/dbus/shill_manager_client.cc index 1be4bf2..c5f05ff 100644 --- a/chromeos/dbus/shill_manager_client.cc +++ b/chromeos/dbus/shill_manager_client.cc @@ -6,6 +6,7 @@ #include "base/bind.h" #include "base/chromeos/chromeos_version.h" +#include "base/logging.h" #include "base/message_loop.h" #include "base/values.h" #include "chromeos/dbus/shill_manager_client_stub.h" @@ -21,13 +22,41 @@ namespace chromeos { namespace { +const char kIncompleteServiceProperties[] = "Error.IncompleteServiceProperties"; +const char kIncompleteServicePropertiesMessage[] = + "Service properties are incomplete."; + // Returns whether the properties have the required keys or not. -bool AreServicePropertiesValid(const base::DictionaryValue& properties) { - if (properties.HasKey(flimflam::kGuidProperty)) +bool AreServicePropertiesValidWithMode( + const base::DictionaryValue& properties, + const ShillManagerClient::ErrorCallback& error_callback) { + if (properties.HasKey(flimflam::kGuidProperty) || + (properties.HasKey(flimflam::kTypeProperty) && + properties.HasKey(flimflam::kSecurityProperty) && + properties.HasKey(flimflam::kModeProperty) && + properties.HasKey(flimflam::kSSIDProperty))) { return true; - return properties.HasKey(flimflam::kTypeProperty) && - properties.HasKey(flimflam::kSecurityProperty) && - properties.HasKey(flimflam::kSSIDProperty); + } + error_callback.Run(kIncompleteServiceProperties, + kIncompleteServicePropertiesMessage); + return false; +} + +// DEPRECATED: Keep this only for backward compatibility with NetworkLibrary. +// Returns whether the properties have the required keys or not. +// TODO(pneubeck): remove this once NetworkLibrary is gone (crbug/230799). +bool AreServicePropertiesValid( + const base::DictionaryValue& properties, + const ShillManagerClient::ErrorCallback& error_callback) { + if (properties.HasKey(flimflam::kGuidProperty) || + (properties.HasKey(flimflam::kTypeProperty) && + properties.HasKey(flimflam::kSecurityProperty) && + properties.HasKey(flimflam::kSSIDProperty))) { + return true; + } + error_callback.Run(kIncompleteServiceProperties, + kIncompleteServicePropertiesMessage); + return false; } // Appends a string-to-variant dictionary to the writer. @@ -146,7 +175,10 @@ class ShillManagerClientImpl : public ShillManagerClient { const base::DictionaryValue& properties, const ObjectPathCallback& callback, const ErrorCallback& error_callback) OVERRIDE { - DCHECK(AreServicePropertiesValid(properties)); + if (!AreServicePropertiesValid(properties, error_callback)) { + NOTREACHED() << kIncompleteServicePropertiesMessage; + return; + } dbus::MethodCall method_call(flimflam::kFlimflamManagerInterface, flimflam::kConfigureServiceFunction); dbus::MessageWriter writer(&method_call); @@ -156,6 +188,25 @@ class ShillManagerClientImpl : public ShillManagerClient { error_callback); } + virtual void ConfigureServiceForProfile( + const dbus::ObjectPath& profile_path, + const base::DictionaryValue& properties, + const ObjectPathCallback& callback, + const ErrorCallback& error_callback) OVERRIDE { + if (!AreServicePropertiesValidWithMode(properties, error_callback)) { + NOTREACHED() << kIncompleteServicePropertiesMessage; + return; + } + dbus::MethodCall method_call(flimflam::kFlimflamManagerInterface, + shill::kConfigureServiceForProfileFunction); + dbus::MessageWriter writer(&method_call); + writer.AppendObjectPath(dbus::ObjectPath(profile_path)); + AppendServicePropertiesDictionary(&writer, properties); + helper_.CallObjectPathMethodWithErrorCallback(&method_call, + callback, + error_callback); + } + virtual void GetService( const base::DictionaryValue& properties, const ObjectPathCallback& callback, diff --git a/chromeos/dbus/shill_manager_client.h b/chromeos/dbus/shill_manager_client.h index a67113d..80116bd 100644 --- a/chromeos/dbus/shill_manager_client.h +++ b/chromeos/dbus/shill_manager_client.h @@ -8,14 +8,15 @@ #include <string> #include "base/basictypes.h" -#include "base/callback.h" #include "chromeos/chromeos_export.h" #include "chromeos/dbus/dbus_client_implementation_type.h" +#include "chromeos/dbus/dbus_method_call_status.h" #include "chromeos/dbus/shill_client_helper.h" namespace dbus { class Bus; +class ObjectPath; } // namespace dbus @@ -59,7 +60,7 @@ class CHROMEOS_EXPORT ShillManagerClient { virtual void ClearProperties() = 0; protected: - ~TestInterface() {} + virtual ~TestInterface() {} }; virtual ~ShillManagerClient(); @@ -125,6 +126,14 @@ class CHROMEOS_EXPORT ShillManagerClient { const ObjectPathCallback& callback, const ErrorCallback& error_callback) = 0; + // Calls ConfigureServiceForProfile method. + // |callback| is called with the created service if the method call succeeds. + virtual void ConfigureServiceForProfile( + const dbus::ObjectPath& profile_path, + const base::DictionaryValue& properties, + const ObjectPathCallback& callback, + const ErrorCallback& error_callback) = 0; + // Calls GetService method. // |callback| is called after the method call succeeds. virtual void GetService(const base::DictionaryValue& properties, diff --git a/chromeos/dbus/shill_manager_client_stub.cc b/chromeos/dbus/shill_manager_client_stub.cc index 9350076..d77dab6 100644 --- a/chromeos/dbus/shill_manager_client_stub.cc +++ b/chromeos/dbus/shill_manager_client_stub.cc @@ -12,6 +12,7 @@ #include "chromeos/chromeos_switches.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/shill_device_client.h" +#include "chromeos/dbus/shill_profile_client.h" #include "chromeos/dbus/shill_property_changed_observer.h" #include "chromeos/dbus/shill_service_client.h" #include "dbus/bus.h" @@ -182,9 +183,6 @@ void ShillManagerClientStub::ConfigureService( if (callback.is_null()) return; - // For the purposes of this stub, we're going to assume that the GUID property - // is set to the service path because we don't want to re-implement Shill's - // property matching magic here. ShillServiceClient::TestInterface* service_client = DBusThreadManager::Get()->GetShillServiceClient()->GetTestInterface(); @@ -199,34 +197,57 @@ void ShillManagerClientStub::ConfigureService( return; } + // For the purposes of this stub, we're going to assume that the GUID property + // is set to the service path because we don't want to re-implement Shill's + // property matching magic here. + std::string service_path = guid; + std::string ipconfig_path; properties.GetString(shill::kIPConfigProperty, &ipconfig_path); - // Add the service to the service client stub if not already there. - service_client->AddServiceWithIPConfig(guid, guid, type, flimflam::kStateIdle, - ipconfig_path, true); // Merge the new properties with existing properties, if any. - scoped_ptr<base::DictionaryValue> merged_properties; const base::DictionaryValue* existing_properties = - service_client->GetServiceProperties(guid); - if (existing_properties) { - merged_properties.reset(existing_properties->DeepCopy()); - } else { - merged_properties.reset(new base::DictionaryValue); + service_client->GetServiceProperties(service_path); + if (!existing_properties) { + // Add a new service to the service client stub because none exists, yet. + service_client->AddServiceWithIPConfig(service_path, guid, type, + flimflam::kStateIdle, ipconfig_path, + true); // Add service to watch list. + existing_properties = service_client->GetServiceProperties(service_path); } + + scoped_ptr<base::DictionaryValue> merged_properties( + existing_properties->DeepCopy()); merged_properties->MergeDictionary(&properties); // Now set all the properties. for (base::DictionaryValue::Iterator iter(*merged_properties); !iter.IsAtEnd(); iter.Advance()) { - service_client->SetServiceProperty(guid, iter.key(), iter.value()); + service_client->SetServiceProperty(service_path, iter.key(), iter.value()); } + ShillProfileClient::TestInterface* profile_test = + DBusThreadManager::Get()->GetShillProfileClient()->GetTestInterface(); + profile_test->AddService(service_path); + MessageLoop::current()->PostTask( - FROM_HERE, base::Bind(callback, dbus::ObjectPath(guid))); + FROM_HERE, base::Bind(callback, dbus::ObjectPath(service_path))); +} + +void ShillManagerClientStub::ConfigureServiceForProfile( + const dbus::ObjectPath& profile_path, + const base::DictionaryValue& properties, + const ObjectPathCallback& callback, + const ErrorCallback& error_callback) { + std::string profile_property; + properties.GetStringWithoutPathExpansion(flimflam::kProfileProperty, + &profile_property); + CHECK(profile_property == profile_path.value()); + ConfigureService(properties, callback, error_callback); } + void ShillManagerClientStub::GetService( const base::DictionaryValue& properties, const ObjectPathCallback& callback, diff --git a/chromeos/dbus/shill_manager_client_stub.h b/chromeos/dbus/shill_manager_client_stub.h index f80362f..314713f 100644 --- a/chromeos/dbus/shill_manager_client_stub.h +++ b/chromeos/dbus/shill_manager_client_stub.h @@ -50,6 +50,11 @@ class ShillManagerClientStub : public ShillManagerClient, const base::DictionaryValue& properties, const ObjectPathCallback& callback, const ErrorCallback& error_callback) OVERRIDE; + virtual void ConfigureServiceForProfile( + const dbus::ObjectPath& profile_path, + const base::DictionaryValue& properties, + const ObjectPathCallback& callback, + const ErrorCallback& error_callback) OVERRIDE; virtual void GetService( const base::DictionaryValue& properties, const ObjectPathCallback& callback, diff --git a/chromeos/dbus/shill_profile_client.cc b/chromeos/dbus/shill_profile_client.cc index a86ff78..511a22e 100644 --- a/chromeos/dbus/shill_profile_client.cc +++ b/chromeos/dbus/shill_profile_client.cc @@ -20,12 +20,10 @@ namespace chromeos { namespace { -// The ShillProfileClient implementation. class ShillProfileClientImpl : public ShillProfileClient { public: explicit ShillProfileClientImpl(dbus::Bus* bus); - ///////////////////////////////////// // ShillProfileClient overrides. virtual void AddPropertyChangedObserver( const dbus::ObjectPath& profile_path, @@ -51,6 +49,10 @@ class ShillProfileClientImpl : public ShillProfileClient { const base::Closure& callback, const ErrorCallback& error_callback) OVERRIDE; + virtual TestInterface* GetTestInterface() OVERRIDE { + return NULL; + } + private: typedef std::map<std::string, ShillClientHelper*> HelperMap; diff --git a/chromeos/dbus/shill_profile_client.h b/chromeos/dbus/shill_profile_client.h index 2c45ac7..4d653ff 100644 --- a/chromeos/dbus/shill_profile_client.h +++ b/chromeos/dbus/shill_profile_client.h @@ -41,6 +41,20 @@ class CHROMEOS_EXPORT ShillProfileClient { DictionaryValueCallbackWithoutStatus; typedef ShillClientHelper::ErrorCallback ErrorCallback; + // Interface for setting up services for testing. Accessed through + // GetTestInterface(), only implemented in the stub implementation. + class TestInterface { + public: + virtual void AddProfile(const std::string& profile_path) = 0; + virtual void AddEntry(const std::string& profile_path, + const std::string& entry_path, + const base::DictionaryValue& properties) = 0; + virtual bool AddService(const std::string& service_path) = 0; + + protected: + virtual ~TestInterface() {} + }; + virtual ~ShillProfileClient(); // Factory function, creates a new instance which is owned by the caller. @@ -79,6 +93,9 @@ class CHROMEOS_EXPORT ShillProfileClient { const base::Closure& callback, const ErrorCallback& error_callback) = 0; + // Returns an interface for testing (stub only), or returns NULL. + virtual TestInterface* GetTestInterface() = 0; + protected: // Create() should be used instead. ShillProfileClient(); diff --git a/chromeos/dbus/shill_profile_client_stub.cc b/chromeos/dbus/shill_profile_client_stub.cc index 9b427bb..e249cd5 100644 --- a/chromeos/dbus/shill_profile_client_stub.cc +++ b/chromeos/dbus/shill_profile_client_stub.cc @@ -5,10 +5,13 @@ #include "chromeos/dbus/shill_profile_client_stub.h" #include "base/bind.h" +#include "base/bind_helpers.h" #include "base/message_loop.h" #include "base/stl_util.h" #include "base/values.h" +#include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/shill_property_changed_observer.h" +#include "chromeos/dbus/shill_service_client.h" #include "dbus/bus.h" #include "dbus/message.h" #include "dbus/object_path.h" @@ -17,7 +20,38 @@ namespace chromeos { -ShillProfileClientStub::ShillProfileClientStub() : weak_ptr_factory_(this) { +namespace { + +void PassEmptyDictionary( + const ShillProfileClient::DictionaryValueCallbackWithoutStatus& callback) { + base::DictionaryValue dictionary; + if (callback.is_null()) + return; + callback.Run(dictionary); +} + +void PassDictionary( + const ShillProfileClient::DictionaryValueCallbackWithoutStatus& callback, + const base::DictionaryValue* dictionary) { + if (callback.is_null()) + return; + callback.Run(*dictionary); +} + +base::DictionaryValue* GetOrCreateDictionary(const std::string& key, + base::DictionaryValue* dict) { + base::DictionaryValue* nested_dict = NULL; + dict->GetDictionaryWithoutPathExpansion(key, &nested_dict); + if (!nested_dict) { + nested_dict = new base::DictionaryValue; + dict->SetWithoutPathExpansion(key, nested_dict); + } + return nested_dict; +} + +} // namespace + +ShillProfileClientStub::ShillProfileClientStub() { } ShillProfileClientStub::~ShillProfileClientStub() { @@ -37,13 +71,21 @@ void ShillProfileClientStub::GetProperties( const dbus::ObjectPath& profile_path, const DictionaryValueCallbackWithoutStatus& callback, const ErrorCallback& error_callback) { - if (callback.is_null()) + base::DictionaryValue* profile = GetProfile(profile_path, error_callback); + if (!profile) return; + + scoped_ptr<base::DictionaryValue> properties(new base::DictionaryValue); + base::ListValue* entry_paths = new base::ListValue; + properties->SetWithoutPathExpansion(flimflam::kEntriesProperty, entry_paths); + for (base::DictionaryValue::Iterator it(*profile); !it.IsAtEnd(); + it.Advance()) { + entry_paths->AppendString(it.key()); + } + MessageLoop::current()->PostTask( FROM_HERE, - base::Bind(&ShillProfileClientStub::PassEmptyDictionaryValue, - weak_ptr_factory_.GetWeakPtr(), - callback)); + base::Bind(&PassDictionary, callback, base::Owned(properties.release()))); } void ShillProfileClientStub::GetEntry( @@ -51,28 +93,83 @@ void ShillProfileClientStub::GetEntry( const std::string& entry_path, const DictionaryValueCallbackWithoutStatus& callback, const ErrorCallback& error_callback) { - if (callback.is_null()) + base::DictionaryValue* profile = GetProfile(profile_path, error_callback); + if (!profile) + return; + + base::DictionaryValue* entry = NULL; + profile->GetDictionaryWithoutPathExpansion(entry_path, &entry); + if (!entry) { + error_callback.Run("Error.InvalidProfileEntry", "Invalid profile entry"); return; + } + MessageLoop::current()->PostTask( FROM_HERE, - base::Bind(&ShillProfileClientStub::PassEmptyDictionaryValue, - weak_ptr_factory_.GetWeakPtr(), - callback)); + base::Bind(&PassDictionary, callback, base::Owned(entry->DeepCopy()))); } void ShillProfileClientStub::DeleteEntry(const dbus::ObjectPath& profile_path, const std::string& entry_path, const base::Closure& callback, const ErrorCallback& error_callback) { - if (callback.is_null()) + base::DictionaryValue* profile = GetProfile(profile_path, error_callback); + if (!profile) return; + + if (!profile->RemoveWithoutPathExpansion(entry_path, NULL)) { + error_callback.Run("Error.InvalidProfileEntry", "Invalid profile entry"); + return; + } + MessageLoop::current()->PostTask(FROM_HERE, callback); } -void ShillProfileClientStub::PassEmptyDictionaryValue( - const DictionaryValueCallbackWithoutStatus& callback) const { - base::DictionaryValue dictionary; - callback.Run(dictionary); +ShillProfileClient::TestInterface* ShillProfileClientStub::GetTestInterface() { + return this; +} + +void ShillProfileClientStub::AddProfile(const std::string& profile_path) { + profile_entries_.SetWithoutPathExpansion(profile_path, + new base::DictionaryValue); +} + +void ShillProfileClientStub::AddEntry(const std::string& profile_path, + const std::string& entry_path, + const base::DictionaryValue& properties) { + base::DictionaryValue* profile = GetOrCreateDictionary(profile_path, + &profile_entries_); + profile->SetWithoutPathExpansion(entry_path, + properties.DeepCopy()); +} + +bool ShillProfileClientStub::AddService(const std::string& service_path) { + ShillServiceClient::TestInterface* service_test = + DBusThreadManager::Get()->GetShillServiceClient()->GetTestInterface(); + const base::DictionaryValue* properties = + service_test->GetServiceProperties(service_path); + std::string profile_path; + if (!properties) + return false; + + properties->GetStringWithoutPathExpansion(flimflam::kProfileProperty, + &profile_path); + if (profile_path.empty()) + return false; + + AddEntry(profile_path, service_path, *properties); + return true; +} + +base::DictionaryValue* ShillProfileClientStub::GetProfile( + const dbus::ObjectPath& profile_path, + const ErrorCallback& error_callback) { + base::DictionaryValue* profile = NULL; + profile_entries_.GetDictionaryWithoutPathExpansion(profile_path.value(), + &profile); + if (!profile) + error_callback.Run("Error.InvalidProfile", "Invalid profile"); + return profile; } } // namespace chromeos diff --git a/chromeos/dbus/shill_profile_client_stub.h b/chromeos/dbus/shill_profile_client_stub.h index b26b5a7..efc86ce 100644 --- a/chromeos/dbus/shill_profile_client_stub.h +++ b/chromeos/dbus/shill_profile_client_stub.h @@ -13,7 +13,8 @@ namespace chromeos { // A stub implementation of ShillProfileClient. -class ShillProfileClientStub : public ShillProfileClient { +class ShillProfileClientStub : public ShillProfileClient, + public ShillProfileClient::TestInterface { public: ShillProfileClientStub(); virtual ~ShillProfileClientStub(); @@ -37,14 +38,21 @@ class ShillProfileClientStub : public ShillProfileClient { const std::string& entry_path, const base::Closure& callback, const ErrorCallback& error_callback) OVERRIDE; + virtual ShillProfileClient::TestInterface* GetTestInterface() OVERRIDE; + + // ShillProfileClient::TestInterface overrides. + virtual void AddProfile(const std::string& profile_path) OVERRIDE; + virtual void AddEntry(const std::string& profile_path, + const std::string& entry_path, + const base::DictionaryValue& properties) OVERRIDE; + virtual bool AddService(const std::string& service_path) OVERRIDE; private: - void PassEmptyDictionaryValue( - const DictionaryValueCallbackWithoutStatus& callback) const; + base::DictionaryValue* GetProfile(const dbus::ObjectPath& profile_path, + const ErrorCallback& error_callback); - // Note: This should remain the last member so it'll be destroyed and - // invalidate its weak pointers before any other members are destroyed. - base::WeakPtrFactory<ShillProfileClientStub> weak_ptr_factory_; + // This maps profile path -> entry path -> Shill properties. + base::DictionaryValue profile_entries_; DISALLOW_COPY_AND_ASSIGN(ShillProfileClientStub); }; diff --git a/chromeos/dbus/shill_service_client.h b/chromeos/dbus/shill_service_client.h index 05713be..1a96fa4 100644 --- a/chromeos/dbus/shill_service_client.h +++ b/chromeos/dbus/shill_service_client.h @@ -64,7 +64,7 @@ class CHROMEOS_EXPORT ShillServiceClient { virtual void ClearServices() = 0; protected: - ~TestInterface() {} + virtual ~TestInterface() {} }; virtual ~ShillServiceClient(); diff --git a/chromeos/dbus/shill_service_client_stub.cc b/chromeos/dbus/shill_service_client_stub.cc index 3b8f1de..677f9d6 100644 --- a/chromeos/dbus/shill_service_client_stub.cc +++ b/chromeos/dbus/shill_service_client_stub.cc @@ -32,6 +32,13 @@ void PassStubListValue(const ShillServiceClient::ListValueCallback& callback, callback.Run(*value); } +void PassStubServiceProperties( + const ShillServiceClient::DictionaryValueCallback& callback, + DBusMethodCallStatus call_status, + const base::DictionaryValue* properties) { + callback.Run(call_status, *properties); +} + } // namespace ShillServiceClientStub::ShillServiceClientStub() : weak_ptr_factory_(this) { @@ -62,12 +69,29 @@ void ShillServiceClientStub::GetProperties( const DictionaryValueCallback& callback) { if (callback.is_null()) return; + + base::DictionaryValue* nested_dict = NULL; + scoped_ptr<base::DictionaryValue> result_properties; + DBusMethodCallStatus call_status; + stub_services_.GetDictionaryWithoutPathExpansion(service_path.value(), + &nested_dict); + if (nested_dict) { + result_properties.reset(nested_dict->DeepCopy()); + // Remove credentials that Shill wouldn't send. + result_properties->RemoveWithoutPathExpansion(flimflam::kPassphraseProperty, + NULL); + call_status = DBUS_METHOD_CALL_SUCCESS; + } else { + result_properties.reset(new base::DictionaryValue); + call_status = DBUS_METHOD_CALL_FAILURE; + } + MessageLoop::current()->PostTask( FROM_HERE, - base::Bind(&ShillServiceClientStub::PassStubServiceProperties, - weak_ptr_factory_.GetWeakPtr(), - service_path, - callback)); + base::Bind(&PassStubServiceProperties, + callback, + call_status, + base::Owned(result_properties.release()))); } void ShillServiceClientStub::SetProperty(const dbus::ObjectPath& service_path, @@ -77,7 +101,7 @@ void ShillServiceClientStub::SetProperty(const dbus::ObjectPath& service_path, const ErrorCallback& error_callback) { base::DictionaryValue* dict = NULL; if (!stub_services_.GetDictionaryWithoutPathExpansion( - service_path.value(), &dict)) { + service_path.value(), &dict)) { error_callback.Run("Error.InvalidService", "Invalid Service"); return; } @@ -374,19 +398,6 @@ void ShillServiceClientStub::SetDefaultProperties() { add_to_watchlist); } -void ShillServiceClientStub::PassStubServiceProperties( - const dbus::ObjectPath& service_path, - const DictionaryValueCallback& callback) { - base::DictionaryValue* dict = NULL; - if (!stub_services_.GetDictionaryWithoutPathExpansion( - service_path.value(), &dict)) { - base::DictionaryValue empty_dictionary; - callback.Run(DBUS_METHOD_CALL_FAILURE, empty_dictionary); - return; - } - callback.Run(DBUS_METHOD_CALL_SUCCESS, *dict); -} - void ShillServiceClientStub::NotifyObserversPropertyChanged( const dbus::ObjectPath& service_path, const std::string& property) { diff --git a/chromeos/dbus/shill_service_client_stub.h b/chromeos/dbus/shill_service_client_stub.h index 507f95f..2e9bfdd 100644 --- a/chromeos/dbus/shill_service_client_stub.h +++ b/chromeos/dbus/shill_service_client_stub.h @@ -91,8 +91,6 @@ class ShillServiceClientStub : public ShillServiceClient, typedef ObserverList<ShillPropertyChangedObserver> PropertyObserverList; void SetDefaultProperties(); - void PassStubServiceProperties(const dbus::ObjectPath& service_path, - const DictionaryValueCallback& callback); void NotifyObserversPropertyChanged(const dbus::ObjectPath& service_path, const std::string& property); base::DictionaryValue* GetModifiableServiceProperties( diff --git a/chromeos/network/managed_network_configuration_handler.cc b/chromeos/network/managed_network_configuration_handler.cc index c88a15e..e41d011 100644 --- a/chromeos/network/managed_network_configuration_handler.cc +++ b/chromeos/network/managed_network_configuration_handler.cc @@ -9,21 +9,30 @@ #include "base/bind.h" #include "base/guid.h" +#include "base/json/json_writer.h" +#include "base/location.h" #include "base/logging.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" +#include "base/stl_util.h" #include "base/values.h" #include "chromeos/dbus/dbus_method_call_status.h" #include "chromeos/dbus/dbus_thread_manager.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/network_configuration_handler.h" #include "chromeos/network/network_event_log.h" +#include "chromeos/network/network_handler_callbacks.h" #include "chromeos/network/network_state.h" #include "chromeos/network/network_state_handler.h" +#include "chromeos/network/network_ui_data.h" #include "chromeos/network/onc/onc_constants.h" +#include "chromeos/network/onc/onc_merger.h" #include "chromeos/network/onc/onc_signature.h" #include "chromeos/network/onc/onc_translator.h" +#include "chromeos/network/onc/onc_utils.h" +#include "chromeos/network/onc/onc_validator.h" #include "dbus/object_path.h" #include "third_party/cros_system_api/dbus/service_constants.h" @@ -37,13 +46,35 @@ const char kLogModule[] = "ManagedNetworkConfigurationHandler"; // These are error strings used for error callbacks. None of these error // messages are user-facing: they should only appear in logs. +const char kInvalidUserSettingsMessage[] = "User settings are invalid."; +const char kInvalidUserSettings[] = "Error.InvalidUserSettings"; +const char kNetworkAlreadyConfiguredMessage[] = + "Network is already configured."; +const char kNetworkAlreadyConfigured[] = "Error.NetworkAlreadyConfigured"; +const char kPoliciesNotInitializedMessage[] = "Policies not initialized."; +const char kPoliciesNotInitialized[] = "Error.PoliciesNotInitialized"; const char kServicePath[] = "servicePath"; const char kSetOnUnconfiguredNetworkMessage[] = "Unable to modify properties of an unconfigured network."; const char kSetOnUnconfiguredNetwork[] = "Error.SetCalledOnUnconfiguredNetwork"; +const char kUIDataErrorMessage[] = "UI data contains errors."; +const char kUIDataError[] = "Error.UIData"; const char kUnknownServicePathMessage[] = "Service path is unknown."; const char kUnknownServicePath[] = "Error.UnknownServicePath"; +enum ProfileType { + PROFILE_NONE, // Not in any profile. + PROFILE_SHARED, // In the shared profile, shared by all users on device. + PROFILE_USER // In the user profile, not visible to other users. +}; + +const char kSharedProfilePath[] = "/profile/default"; +const char kUserProfilePath[] = "/profile/chronos/shill"; + +// This fake credential contains a random postfix which is extremly unlikely to +// be used by any user. +const char kFakeCredential[] = "FAKE_CREDENTIAL_VPaJDV9x"; + void RunErrorCallback(const std::string& service_path, const std::string& error_name, const std::string& error_message, @@ -57,7 +88,212 @@ void RunErrorCallback(const std::string& service_path, error_message))); } -void TranslatePropertiesAndRunCallback( +// Returns the NetworkUIData parsed from the UIData property of +// |shill_dictionary|. If parsing fails or the field doesn't exist, returns +// NULL. +scoped_ptr<NetworkUIData> GetUIData( + const base::DictionaryValue& shill_dictionary) { + std::string ui_data_blob; + if (shill_dictionary.GetStringWithoutPathExpansion( + flimflam::kUIDataProperty, + &ui_data_blob) && + !ui_data_blob.empty()) { + scoped_ptr<base::DictionaryValue> ui_data_dict = + onc::ReadDictionaryFromJson(ui_data_blob); + if (ui_data_dict) + return make_scoped_ptr(new NetworkUIData(*ui_data_dict)); + else + LOG(ERROR) << "UIData is not a valid JSON dictionary."; + } + return scoped_ptr<NetworkUIData>(); +} + +// Sets the UIData property in |shill_dictionary| to the serialization of +// |ui_data|. +void SetUIData(const NetworkUIData& ui_data, + base::DictionaryValue* shill_dictionary) { + base::DictionaryValue ui_data_dict; + ui_data.FillDictionary(&ui_data_dict); + std::string ui_data_blob; + base::JSONWriter::Write(&ui_data_dict, &ui_data_blob); + shill_dictionary->SetStringWithoutPathExpansion(flimflam::kUIDataProperty, + ui_data_blob); +} + +// A dummy callback to ignore the result of Shill calls. +void IgnoreString(const std::string& str) { +} + +void LogErrorWithDict(const tracked_objects::Location& from_where, + const std::string& error_name, + const scoped_ptr<base::DictionaryValue> error_data) { + LOG(ERROR) << from_where.ToString() << ": " << error_name; +} + +void LogErrorMessage(const tracked_objects::Location& from_where, + const std::string& error_name, + const std::string& error_message) { + LOG(ERROR) << from_where.ToString() << ": " << error_message; +} + +// Removes all kFakeCredential values from sensitive fields (determined by +// onc::FieldIsCredential) of |onc_object|. +void RemoveFakeCredentials( + const onc::OncValueSignature& signature, + base::DictionaryValue* onc_object) { + base::DictionaryValue::Iterator it(*onc_object); + while (!it.IsAtEnd()) { + base::Value* value = NULL; + std::string field_name = it.key(); + // We need the non-const entry to remove nested values but DictionaryValue + // has no non-const iterator. + onc_object->GetWithoutPathExpansion(field_name, &value); + // Advance before delete. + it.Advance(); + + // If |value| is a dictionary, recurse. + base::DictionaryValue* nested_object = NULL; + if (value->GetAsDictionary(&nested_object)) { + const onc::OncFieldSignature* field_signature = + onc::GetFieldSignature(signature, field_name); + + RemoveFakeCredentials(*field_signature->value_signature, + nested_object); + continue; + } + + // If |value| is a string, check if it is a fake credential. + std::string string_value; + if (value->GetAsString(&string_value) && + onc::FieldIsCredential(signature, field_name)) { + if (string_value == kFakeCredential) { + // The value wasn't modified by the UI, thus we remove the field to keep + // the existing value that is stored in Shill. + onc_object->RemoveWithoutPathExpansion(field_name, NULL); + } + // Otherwise, the value is set and modified by the UI, thus we keep that + // value to overwrite whatever is stored in Shill. + } + } +} + +// Creates a Shill property dictionary from the given arguments. The resulting +// dictionary will be sent to Shill by the caller. Depending on the profile +// path, |policy| is interpreted as the user or device policy and |settings| as +// the user or shared settings. +scoped_ptr<base::DictionaryValue> CreateShillConfiguration( + const std::string& profile_path, + const std::string& guid, + const base::DictionaryValue* policy, + const base::DictionaryValue* settings) { + scoped_ptr<base::DictionaryValue> effective; + + onc::ONCSource onc_source; + if (policy) { + if (profile_path == kSharedProfilePath) { + effective = onc::MergeSettingsAndPoliciesToEffective( + NULL, // no user policy + policy, // device policy + NULL, // no user settings + settings); // shared settings + onc_source = onc::ONC_SOURCE_DEVICE_POLICY; + } else { + effective = onc::MergeSettingsAndPoliciesToEffective( + policy, // user policy + NULL, // no device policy + settings, // user settings + NULL); // no shared settings + onc_source = onc::ONC_SOURCE_USER_POLICY; + } + } else if (settings) { + effective.reset(settings->DeepCopy()); + // TODO(pneubeck): change to source ONC_SOURCE_USER + onc_source = onc::ONC_SOURCE_NONE; + } else { + NOTREACHED(); + onc_source = onc::ONC_SOURCE_NONE; + } + + RemoveFakeCredentials(onc::kNetworkConfigurationSignature, + effective.get()); + + effective->SetStringWithoutPathExpansion(onc::network_config::kGUID, guid); + + scoped_ptr<base::DictionaryValue> shill_dictionary( + onc::TranslateONCObjectToShill(&onc::kNetworkConfigurationSignature, + *effective)); + + shill_dictionary->SetStringWithoutPathExpansion(flimflam::kProfileProperty, + profile_path); + + scoped_ptr<NetworkUIData> ui_data; + if (policy) + ui_data = CreateUIDataFromONC(onc_source, *policy); + else + ui_data.reset(new NetworkUIData()); + + if (settings) { + // Shill doesn't know that sensitive data is contained in the UIData + // property and might write it into logs or other insecure places. Thus, we + // have to remove or mask credentials. + // + // Shill's GetProperties doesn't return credentials. Masking credentials + // instead of just removing them, allows remembering if a credential is set + // or not. + scoped_ptr<base::DictionaryValue> sanitized_settings( + onc::MaskCredentialsInOncObject(onc::kNetworkConfigurationSignature, + *settings, + kFakeCredential)); + ui_data->set_user_settings(sanitized_settings.Pass()); + } + + SetUIData(*ui_data, shill_dictionary.get()); + + VLOG(2) << "Created Shill properties: " << *shill_dictionary; + + return shill_dictionary.Pass(); +} + +// Returns true if |policy| matches |onc_network_part|. This is should be the +// only such matching function within Chrome. Shill does such matching in +// several functions for network identification. For compatibility, we currently +// should stick to Shill's matching behavior. +bool IsPolicyMatching(const base::DictionaryValue& policy, + const base::DictionaryValue& onc_network_part) { + std::string policy_type; + policy.GetStringWithoutPathExpansion(onc::network_config::kType, + &policy_type); + std::string network_type; + onc_network_part.GetStringWithoutPathExpansion(onc::network_config::kType, + &network_type); + if (policy_type != network_type) + return false; + + if (network_type != onc::network_type::kWiFi) + return false; + + std::string policy_ssid; + policy.GetStringWithoutPathExpansion(onc::wifi::kSSID, &policy_ssid); + std::string network_ssid; + onc_network_part.GetStringWithoutPathExpansion(onc::wifi::kSSID, + &network_ssid); + return (policy_ssid == network_ssid); +} + +// Returns the policy of |policies| matching |onc_network_part|, if any +// exists. Returns NULL otherwise. +const base::DictionaryValue* FindMatchingPolicy( + const ManagedNetworkConfigurationHandler::PolicyMap &policies, + const base::DictionaryValue& onc_network_part) { + for (ManagedNetworkConfigurationHandler::PolicyMap::const_iterator it = + policies.begin(); it != policies.end(); ++it) { + if (IsPolicyMatching(*it->second, onc_network_part)) + return it->second; + } + return NULL; +} + +void TranslatePropertiesToOncAndRunCallback( const network_handler::DictionaryResultCallback& callback, const std::string& service_path, const base::DictionaryValue& shill_properties) { @@ -94,20 +330,107 @@ ManagedNetworkConfigurationHandler* ManagedNetworkConfigurationHandler::Get() { return g_configuration_handler_instance; } +void ManagedNetworkConfigurationHandler::GetManagedProperties( + const std::string& service_path, + const network_handler::DictionaryResultCallback& callback, + const network_handler::ErrorCallback& error_callback) { + if (!user_policies_initialized_ || !device_policies_initialized_) { + RunErrorCallback(service_path, + kPoliciesNotInitialized, + kPoliciesNotInitializedMessage, + error_callback); + return; + } + NetworkConfigurationHandler::Get()->GetProperties( + service_path, + base::Bind( + &ManagedNetworkConfigurationHandler::GetManagedPropertiesCallback, + weak_ptr_factory_.GetWeakPtr(), + callback, + error_callback), + error_callback); +} + +void ManagedNetworkConfigurationHandler::GetManagedPropertiesCallback( + const network_handler::DictionaryResultCallback& callback, + const network_handler::ErrorCallback& error_callback, + const std::string& service_path, + const base::DictionaryValue& shill_properties) { + std::string profile_path; + ProfileType profile_type = PROFILE_NONE; + if (shill_properties.GetStringWithoutPathExpansion( + flimflam::kProfileProperty, &profile_path)) { + if (profile_path == kSharedProfilePath) + profile_type = PROFILE_SHARED; + else if (!profile_path.empty()) + profile_type = PROFILE_USER; + } else { + VLOG(1) << "No profile path for service " << service_path << "."; + } + + scoped_ptr<NetworkUIData> ui_data = GetUIData(shill_properties); + + const base::DictionaryValue* user_settings = NULL; + const base::DictionaryValue* shared_settings = NULL; + + if (ui_data) { + if (profile_type == PROFILE_SHARED) + shared_settings = ui_data->user_settings(); + else if (profile_type == PROFILE_USER) + user_settings = ui_data->user_settings(); + } else if (profile_type != PROFILE_NONE) { + LOG(WARNING) << "Service " << service_path << " of profile " + << profile_path << " contains no or no valid UIData."; + // TODO(pneubeck): add a conversion of user configured entries of old + // ChromeOS versions. We will have to use a heuristic to determine which + // properties _might_ be user configured. + } + + scoped_ptr<base::DictionaryValue> active_settings( + onc::TranslateShillServiceToONCPart( + shill_properties, + &onc::kNetworkWithStateSignature)); + + std::string guid; + active_settings->GetStringWithoutPathExpansion(onc::network_config::kGUID, + &guid); + + const base::DictionaryValue* user_policy = NULL; + const base::DictionaryValue* device_policy = NULL; + if (!guid.empty()) { + // We already checked that the policies were initialized. No need to do that + // again. + if (profile_type == PROFILE_SHARED) + device_policy = device_policies_by_guid_[guid]; + else if (profile_type == PROFILE_USER) + user_policy = user_policies_by_guid_[guid]; + } + + // This call also removes credentials from policies. + scoped_ptr<base::DictionaryValue> augmented_properties = + onc::MergeSettingsAndPoliciesToAugmented( + onc::kNetworkConfigurationSignature, + user_policy, + device_policy, + user_settings, + shared_settings, + active_settings.get()); + callback.Run(service_path, *augmented_properties); +} + void ManagedNetworkConfigurationHandler::GetProperties( const std::string& service_path, const network_handler::DictionaryResultCallback& callback, const network_handler::ErrorCallback& error_callback) const { - // TODO(pneubeck): Merge with policies. NetworkConfigurationHandler::Get()->GetProperties( service_path, - base::Bind(&TranslatePropertiesAndRunCallback, callback), + base::Bind(&TranslatePropertiesToOncAndRunCallback, callback), error_callback); } void ManagedNetworkConfigurationHandler::SetProperties( const std::string& service_path, - const base::DictionaryValue& properties, + const base::DictionaryValue& user_settings, const base::Closure& callback, const network_handler::ErrorCallback& error_callback) const { const NetworkState* state = @@ -118,20 +441,70 @@ void ManagedNetworkConfigurationHandler::SetProperties( kUnknownServicePath, kUnknownServicePathMessage, error_callback); + return; } + std::string guid = state->guid(); if (guid.empty()) { + // TODO(pneubeck): create an initial configuration in this case. As for + // CreateConfiguration, user settings from older ChromeOS versions have to + // determined here. RunErrorCallback(service_path, kSetOnUnconfiguredNetwork, kSetOnUnconfiguredNetworkMessage, error_callback); + return; + } + + // Validate the ONC dictionary. We are liberal and ignore unknown field + // names. User settings are only partial ONC, thus we ignore missing fields. + onc::Validator validator(false, // Ignore unknown fields. + false, // Ignore invalid recommended field names. + false, // Ignore missing fields. + false); // This ONC does not comes from policy. + + onc::Validator::Result validation_result; + scoped_ptr<base::DictionaryValue> validated_user_settings = + validator.ValidateAndRepairObject( + &onc::kNetworkConfigurationSignature, + user_settings, + &validation_result); + + if (validation_result == onc::Validator::INVALID) { + LOG(ERROR) << "ONC user settings are invalid and couldn't be repaired."; + RunErrorCallback(service_path, + kInvalidUserSettings, + kInvalidUserSettingsMessage, + error_callback); + return; } + if (validation_result == onc::Validator::VALID_WITH_WARNINGS) + LOG(WARNING) << "Validation of ONC user settings produced warnings."; - // TODO(pneubeck): Enforce policies. + VLOG(2) << "SetProperties: Found GUID " << guid << " and profile " + << state->profile_path(); + + const PolicyMap* policies_by_guid = + GetPoliciesForProfile(state->profile_path()); + + if (!policies_by_guid) { + RunErrorCallback(service_path, + kPoliciesNotInitialized, + kPoliciesNotInitializedMessage, + error_callback); + return; + } + + const base::DictionaryValue* policy = NULL; + PolicyMap::const_iterator it = policies_by_guid->find(guid); + if (it != policies_by_guid->end()) + policy = it->second; + + VLOG(2) << "This configuration is " << (policy ? "" : "not ") << "managed."; scoped_ptr<base::DictionaryValue> shill_dictionary( - onc::TranslateONCObjectToShill(&onc::kNetworkConfigurationSignature, - properties)); + CreateShillConfiguration(state->profile_path(), guid, policy, + &user_settings)); NetworkConfigurationHandler::Get()->SetProperties(service_path, *shill_dictionary, @@ -143,8 +516,6 @@ void ManagedNetworkConfigurationHandler::Connect( const std::string& service_path, const base::Closure& callback, const network_handler::ErrorCallback& error_callback) const { - // TODO(pneubeck): Update the user profile with tracked/followed settings of - // the shared profile. NetworkConfigurationHandler::Get()->Connect(service_path, callback, error_callback); @@ -163,25 +534,35 @@ void ManagedNetworkConfigurationHandler::CreateConfiguration( const base::DictionaryValue& properties, const network_handler::StringResultCallback& callback, const network_handler::ErrorCallback& error_callback) const { - scoped_ptr<base::DictionaryValue> modified_properties( - properties.DeepCopy()); + std::string profile_path = kUserProfilePath; + const PolicyMap* policies_by_guid = GetPoliciesForProfile(profile_path); - // If there isn't already a GUID attached to these properties, then - // generate one and add it. - std::string guid; - if (!properties.GetString(onc::network_config::kGUID, &guid)) { - guid = base::GenerateGUID(); - modified_properties->SetStringWithoutPathExpansion( - onc::network_config::kGUID, guid); - } else { - NOTREACHED(); // TODO(pneubeck): Return an error using error_callback. + if (!policies_by_guid) { + RunErrorCallback("", + kPoliciesNotInitialized, + kPoliciesNotInitializedMessage, + error_callback); + return; + } + + if (FindMatchingPolicy(*policies_by_guid, properties)) { + RunErrorCallback("", + kNetworkAlreadyConfigured, + kNetworkAlreadyConfiguredMessage, + error_callback); } - // TODO(pneubeck): Enforce policies. + // TODO(pneubeck): In case of WiFi, check that no other configuration for the + // same {SSID, mode, security} exists. We don't support such multiple + // configurations, yet. + + // Generate a new GUID for this configuration. Ignore the maybe provided GUID + // in |properties| as it is not our own and from an untrusted source. + std::string guid = base::GenerateGUID(); scoped_ptr<base::DictionaryValue> shill_dictionary( - onc::TranslateONCObjectToShill(&onc::kNetworkConfigurationSignature, - properties)); + CreateShillConfiguration(profile_path, guid, NULL /*no policy*/, + &properties)); NetworkConfigurationHandler::Get()->CreateConfiguration(*shill_dictionary, callback, @@ -197,10 +578,337 @@ void ManagedNetworkConfigurationHandler::RemoveConfiguration( error_callback); } -ManagedNetworkConfigurationHandler::ManagedNetworkConfigurationHandler() { +// This class compares (entry point is Run()) |modified_policies| with the +// existing entries in the provided Shill profile |profile|. It fetches all +// entries in parallel (GetProfileProperties), compares each entry with the +// current policies (GetEntry) and adds all missing policies +// (~PolicyApplicator). +class ManagedNetworkConfigurationHandler::PolicyApplicator + : public base::RefCounted<PolicyApplicator> { + public: + typedef ManagedNetworkConfigurationHandler::PolicyMap PolicyMap; + + // |modified_policies| must not be NULL and will be empty afterwards. + PolicyApplicator(base::WeakPtr<ManagedNetworkConfigurationHandler> handler, + const std::string& profile, + std::set<std::string>* modified_policies) + : handler_(handler), + profile_path_(profile) { + remaining_policies_.swap(*modified_policies); + } + + void Run() { + DBusThreadManager::Get()->GetShillProfileClient()->GetProperties( + dbus::ObjectPath(profile_path_), + base::Bind(&PolicyApplicator::GetProfileProperties, this), + base::Bind(&LogErrorMessage, FROM_HERE)); + } + + private: + friend class base::RefCounted<PolicyApplicator>; + + void GetProfileProperties(const base::DictionaryValue& profile_properties) { + if (!handler_) { + LOG(WARNING) << "Handler destructed during policy application to profile " + << profile_path_; + return; + } + + VLOG(2) << "Received properties for profile " << profile_path_; + const base::ListValue* entries = NULL; + if (!profile_properties.GetListWithoutPathExpansion( + flimflam::kEntriesProperty, &entries)) { + LOG(ERROR) << "Profile " << profile_path_ + << " doesn't contain the property " + << flimflam::kEntriesProperty; + return; + } + + for (base::ListValue::const_iterator it = entries->begin(); + it != entries->end(); ++it) { + std::string entry; + (*it)->GetAsString(&entry); + + std::ostringstream entry_failure; + DBusThreadManager::Get()->GetShillProfileClient()->GetEntry( + dbus::ObjectPath(profile_path_), + entry, + base::Bind(&PolicyApplicator::GetEntry, this, entry), + base::Bind(&LogErrorMessage, FROM_HERE)); + } + } + + void GetEntry(const std::string& entry, + const base::DictionaryValue& entry_properties) { + if (!handler_) { + LOG(WARNING) << "Handler destructed during policy application to profile " + << profile_path_; + return; + } + + VLOG(2) << "Received properties for entry " << entry << " of profile " + << profile_path_; + + scoped_ptr<base::DictionaryValue> onc_part( + onc::TranslateShillServiceToONCPart( + entry_properties, + &onc::kNetworkWithStateSignature)); + + std::string old_guid; + if (!onc_part->GetStringWithoutPathExpansion(onc::network_config::kGUID, + &old_guid)) { + LOG(WARNING) << "Entry " << entry << " of profile " << profile_path_ + << " doesn't contain a GUID."; + // This might be an entry of an older ChromeOS version. Assume it to be + // unmanaged. + return; + } + + scoped_ptr<NetworkUIData> ui_data = GetUIData(entry_properties); + if (!ui_data) { + VLOG(1) << "Entry " << entry << " of profile " << profile_path_ + << " contains no or no valid UIData."; + // This might be an entry of an older ChromeOS version. Assume it to be + // unmanaged. + return; + } + + bool was_managed = + (ui_data->onc_source() == onc::ONC_SOURCE_DEVICE_POLICY || + ui_data->onc_source() == onc::ONC_SOURCE_USER_POLICY); + + // The relevant policy must have been initialized, otherwise we hadn't Run + // this PolicyApplicator. + const PolicyMap& policies_by_guid = + *handler_->GetPoliciesForProfile(profile_path_); + + const base::DictionaryValue* new_policy = NULL; + if (was_managed) { + // If we have a GUID that might match a current policy, do a lookup using + // that GUID at first. In particular this is necessary, as some networks + // can't be matched to policies by properties (e.g. VPN). + PolicyMap::const_iterator it = policies_by_guid.find(old_guid); + if (it != policies_by_guid.end()) + new_policy = it->second; + } + + if (!new_policy) { + // If we didn't find a policy by GUID, still a new policy might match. + new_policy = FindMatchingPolicy(policies_by_guid, *onc_part); + } + + if (new_policy) { + std::string new_guid; + new_policy->GetStringWithoutPathExpansion(onc::network_config::kGUID, + &new_guid); + + VLOG_IF(1, was_managed && old_guid != new_guid) + << "Updating configuration previously managed by policy " << old_guid + << " with new policy " << new_guid << "."; + VLOG_IF(1, !was_managed) + << "Applying policy " << new_guid << " to previously unmanaged " + << "configuration."; + + if (old_guid == new_guid && + remaining_policies_.find(new_guid) == remaining_policies_.end()) { + VLOG(1) << "Not updating existing managed configuration with guid " + << new_guid << " because the policy didn't change."; + } else { + VLOG_IF(1, old_guid == new_guid) + << "Updating previously managed configuration with the updated " + << "policy " << new_guid << "."; + + // Update the existing configuration with the maybe changed + // policy. Thereby the GUID might change. + scoped_ptr<base::DictionaryValue> shill_dictionary = + CreateShillConfiguration(profile_path_, new_guid, new_policy, + ui_data->user_settings()); + NetworkConfigurationHandler::Get()->CreateConfiguration( + *shill_dictionary, + base::Bind(&IgnoreString), + base::Bind(&LogErrorWithDict, FROM_HERE)); + remaining_policies_.erase(new_guid); + } + } else if (was_managed) { + VLOG(1) << "Removing configuration previously managed by policy " + << old_guid << ", because the policy was removed."; + + // Remove the entry, because the network was managed but isn't anymore. + // Note: An alternative might be to preserve the user settings, but it's + // unclear which values originating the policy should be removed. + DeleteEntry(entry); + } else { + VLOG(2) << "Ignore unmanaged entry."; + + // The entry wasn't managed and doesn't match any current policy. Thus + // leave it as it is. + } + } + + void DeleteEntry(const std::string& entry) { + DBusThreadManager::Get()->GetShillProfileClient()->DeleteEntry( + dbus::ObjectPath(profile_path_), + entry, + base::Bind(&base::DoNothing), + base::Bind(&LogErrorMessage, FROM_HERE)); + } + + virtual ~PolicyApplicator() { + if (remaining_policies_.empty()) + return; + + VLOG(2) << "Create new managed network configurations in profile" + << profile_path_ << "."; + // All profile entries were compared to policies. |configureGUIDs_| contains + // all matched policies. From the remainder of policies, new configurations + // have to be created. + + // The relevant policy must have been initialized, otherwise we hadn't Run + // this PolicyApplicator. + const PolicyMap& policies_by_guid = + *handler_->GetPoliciesForProfile(profile_path_); + + for (std::set<std::string>::iterator it = remaining_policies_.begin(); + it != remaining_policies_.end(); ++it) { + PolicyMap::const_iterator policy_it = policies_by_guid.find(*it); + if (policy_it == policies_by_guid.end()) { + LOG(ERROR) << "Policy " << *it << " doesn't exist anymore."; + continue; + } + + const base::DictionaryValue* policy = policy_it->second; + + VLOG(1) << "Creating new configuration managed by policy " << *it + << " in profile " << profile_path_ << "."; + + scoped_ptr<base::DictionaryValue> shill_dictionary = + CreateShillConfiguration(profile_path_, *it, policy, NULL); + NetworkConfigurationHandler::Get()->CreateConfiguration( + *shill_dictionary, + base::Bind(&IgnoreString), + base::Bind(&LogErrorWithDict, FROM_HERE)); + } + } + + std::set<std::string> remaining_policies_; + base::WeakPtr<ManagedNetworkConfigurationHandler> handler_; + std::string profile_path_; + + DISALLOW_COPY_AND_ASSIGN(PolicyApplicator); +}; + +void ManagedNetworkConfigurationHandler::SetPolicy( + onc::ONCSource onc_source, + const base::DictionaryValue& toplevel_onc) { + VLOG(1) << "Setting policies for ONC source " + << onc::GetSourceAsString(onc_source) << "."; + + // Validate the ONC dictionary. We are liberal and ignore unknown field + // names and ignore invalid field names in kRecommended arrays. + onc::Validator validator(false, // Ignore unknown fields. + false, // Ignore invalid recommended field names. + true, // Fail on missing fields. + true); // This ONC comes from policy. + validator.SetOncSource(onc_source); + + onc::Validator::Result validation_result; + scoped_ptr<base::DictionaryValue> onc_validated = + validator.ValidateAndRepairObject( + &onc::kToplevelConfigurationSignature, + toplevel_onc, + &validation_result); + + if (validation_result == onc::Validator::VALID_WITH_WARNINGS) { + LOG(WARNING) << "ONC from " << onc::GetSourceAsString(onc_source) + << " produced warnings."; + } else if (validation_result == onc::Validator::INVALID || + onc_validated == NULL) { + LOG(ERROR) << "ONC from " << onc::GetSourceAsString(onc_source) + << " is invalid and couldn't be repaired."; + return; + } + + PolicyMap* policies; + std::string profile; + if (onc_source == chromeos::onc::ONC_SOURCE_USER_POLICY) { + policies = &user_policies_by_guid_; + profile = kUserProfilePath; + user_policies_initialized_ = true; + } else { + policies = &device_policies_by_guid_; + profile = kSharedProfilePath; + device_policies_initialized_ = true; + } + + PolicyMap old_policies; + policies->swap(old_policies); + + // This stores all GUIDs of policies that have changed or are new. + std::set<std::string> modified_policies; + + base::ListValue* network_configurations = NULL; + onc_validated->GetListWithoutPathExpansion( + onc::toplevel_config::kNetworkConfigurations, + &network_configurations); + + if (network_configurations) { + while (!network_configurations->empty()) { + base::Value* network_value = NULL; + // Passes ownership of network_value. + network_configurations->Remove(network_configurations->GetSize() - 1, + &network_value); + const base::DictionaryValue* network = NULL; + network_value->GetAsDictionary(&network); + std::string guid; + network->GetStringWithoutPathExpansion(onc::network_config::kGUID, + &guid); + + const base::DictionaryValue* old_entry = old_policies[guid]; + const base::DictionaryValue*& new_entry = (*policies)[guid]; + if (new_entry) { + LOG(ERROR) << "ONC from " << onc::GetSourceAsString(onc_source) + << " contains several entries for the same GUID " + << guid << "."; + delete new_entry; + } + new_entry = network; + + if (!old_entry || !old_entry->Equals(new_entry)) { + modified_policies.insert(guid); + } + } + } + + STLDeleteValues(&old_policies); + + scoped_refptr<PolicyApplicator> applicator = new PolicyApplicator( + weak_ptr_factory_.GetWeakPtr(), + profile, + &modified_policies); + applicator->Run(); +} + +const ManagedNetworkConfigurationHandler::PolicyMap* +ManagedNetworkConfigurationHandler::GetPoliciesForProfile( + const std::string& profile) const { + if (profile == kSharedProfilePath) { + if (device_policies_initialized_) + return &device_policies_by_guid_; + } else if (user_policies_initialized_) { + return &user_policies_by_guid_; + } + return NULL; +} + +ManagedNetworkConfigurationHandler::ManagedNetworkConfigurationHandler() + : user_policies_initialized_(false), + device_policies_initialized_(false), + weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { } ManagedNetworkConfigurationHandler::~ManagedNetworkConfigurationHandler() { + STLDeleteValues(&user_policies_by_guid_); + STLDeleteValues(&device_policies_by_guid_); } } // namespace chromeos diff --git a/chromeos/network/managed_network_configuration_handler.h b/chromeos/network/managed_network_configuration_handler.h index 76fd02c..5b53fff 100644 --- a/chromeos/network/managed_network_configuration_handler.h +++ b/chromeos/network/managed_network_configuration_handler.h @@ -5,14 +5,16 @@ #ifndef CHROMEOS_NETWORK_MANAGED_NETWORK_CONFIGURATION_HANDLER_H_ #define CHROMEOS_NETWORK_MANAGED_NETWORK_CONFIGURATION_HANDLER_H_ +#include <map> #include <string> -#include <vector> #include "base/basictypes.h" #include "base/callback.h" #include "base/gtest_prod_util.h" +#include "base/memory/weak_ptr.h" #include "chromeos/chromeos_export.h" #include "chromeos/network/network_handler_callbacks.h" +#include "chromeos/network/onc/onc_constants.h" namespace base { class DictionaryValue; @@ -21,7 +23,7 @@ class DictionaryValue; namespace chromeos { // The ManagedNetworkConfigurationHandler class is used to create and configure -// networks in ChromeOS using ONC. +// networks in ChromeOS using ONC and takes care of network policies. // // Its interface exposes only ONC and should decouple users from Shill. // Internally it translates ONC to Shill dictionaries and calls through to the @@ -44,11 +46,11 @@ namespace chromeos { // error, including a symbolic name for the error and often some error message // that is suitable for logging. None of the error message text is meant for // user consumption. -// -// TODO(pneubeck): Enforce network policies. class CHROMEOS_EXPORT ManagedNetworkConfigurationHandler { public: + typedef std::map<std::string, const base::DictionaryValue*> PolicyMap; + // Initializes the singleton. static void Initialize(); @@ -67,6 +69,13 @@ class CHROMEOS_EXPORT ManagedNetworkConfigurationHandler { const network_handler::DictionaryResultCallback& callback, const network_handler::ErrorCallback& error_callback) const; + // Provides the managed properties of the network with |service_path| to + // |callback|. + void GetManagedProperties( + const std::string& service_path, + const network_handler::DictionaryResultCallback& callback, + const network_handler::ErrorCallback& error_callback); + // Sets the user's settings of an already configured network with // |service_path|. A network can be initially configured by calling // CreateConfiguration or if it is managed by a policy. The given properties @@ -74,7 +83,7 @@ class CHROMEOS_EXPORT ManagedNetworkConfigurationHandler { // properties. void SetProperties( const std::string& service_path, - const base::DictionaryValue& properties, + const base::DictionaryValue& user_settings, const base::Closure& callback, const network_handler::ErrorCallback& error_callback) const; @@ -109,10 +118,38 @@ class CHROMEOS_EXPORT ManagedNetworkConfigurationHandler { const base::Closure& callback, const network_handler::ErrorCallback& error_callback) const; + // Only to be called by NetworkConfigurationUpdater or from tests. + // Sets |toplevel_onc| as the current policy of |onc_source|. The network + // configurations of the policy will be applied (not necessarily immediately) + // to Shill's profiles and enforced in future configurations until the policy + // associated with |onc_source| is changed again with this function. + void SetPolicy(onc::ONCSource onc_source, + const base::DictionaryValue& toplevel_onc); + private: + class PolicyApplicator; + ManagedNetworkConfigurationHandler(); ~ManagedNetworkConfigurationHandler(); + void GetManagedPropertiesCallback( + const network_handler::DictionaryResultCallback& callback, + const network_handler::ErrorCallback& error_callback, + const std::string& service_path, + const base::DictionaryValue& shill_properties); + + const PolicyMap* GetPoliciesForProfile(const std::string& profile) const; + + // The entries of these maps are owned by this class and are explicitly + // deleted where necessary. + PolicyMap user_policies_by_guid_; + PolicyMap device_policies_by_guid_; + bool user_policies_initialized_; + bool device_policies_initialized_; + + // For Shill client callbacks + base::WeakPtrFactory<ManagedNetworkConfigurationHandler> weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(ManagedNetworkConfigurationHandler); }; diff --git a/chromeos/network/managed_network_configuration_handler_unittest.cc b/chromeos/network/managed_network_configuration_handler_unittest.cc new file mode 100644 index 0000000..49c150a --- /dev/null +++ b/chromeos/network/managed_network_configuration_handler_unittest.cc @@ -0,0 +1,314 @@ +// Copyright (c) 2013 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/managed_network_configuration_handler.h" + +#include <iostream> +#include <sstream> + +#include "base/message_loop.h" +#include "chromeos/dbus/dbus_thread_manager.h" +#include "chromeos/dbus/mock_dbus_thread_manager.h" +#include "chromeos/dbus/mock_shill_manager_client.h" +#include "chromeos/dbus/mock_shill_profile_client.h" +#include "chromeos/dbus/mock_shill_service_client.h" +#include "chromeos/dbus/shill_profile_client_stub.h" +#include "chromeos/network/network_configuration_handler.h" +#include "chromeos/network/onc/onc_test_utils.h" +#include "chromeos/network/onc/onc_utils.h" +#include "dbus/object_path.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +using ::testing::Invoke; +using ::testing::Mock; +using ::testing::Pointee; +using ::testing::Return; +using ::testing::SaveArg; +using ::testing::StrEq; +using ::testing::StrictMock; +using ::testing::_; + +namespace test_utils = ::chromeos::onc::test_utils; + +namespace { + +std::string ValueToString(const base::Value* value) { + std::stringstream str; + str << *value; + return str.str(); +} + +// Matcher to match base::Value. +MATCHER_P(IsEqualTo, + value, + std::string(negation ? "isn't" : "is") + " equal to " + + ValueToString(value)) { + return value->Equals(&arg); +} + +} // namespace + + +namespace chromeos { + +class ManagedNetworkConfigurationHandlerTest : public testing::Test { + public: + ManagedNetworkConfigurationHandlerTest() { + } + + virtual ~ManagedNetworkConfigurationHandlerTest() { + } + + virtual void SetUp() OVERRIDE { + MockDBusThreadManager* dbus_thread_manager = new MockDBusThreadManager; + EXPECT_CALL(*dbus_thread_manager, GetSystemBus()) + .WillRepeatedly(Return(static_cast<dbus::Bus*>(NULL))); + DBusThreadManager::InitializeForTesting(dbus_thread_manager); + + EXPECT_CALL(*dbus_thread_manager, GetShillManagerClient()) + .WillRepeatedly(Return(&mock_manager_client_)); + EXPECT_CALL(*dbus_thread_manager, GetShillServiceClient()) + .WillRepeatedly(Return(&mock_service_client_)); + EXPECT_CALL(*dbus_thread_manager, GetShillProfileClient()) + .WillRepeatedly(Return(&mock_profile_client_)); + + ON_CALL(mock_profile_client_, GetProperties(_,_,_)) + .WillByDefault(Invoke(&stub_profile_client_, + &ShillProfileClientStub::GetProperties)); + + ON_CALL(mock_profile_client_, GetEntry(_,_,_,_)) + .WillByDefault(Invoke(&stub_profile_client_, + &ShillProfileClientStub::GetEntry)); + + NetworkConfigurationHandler::Initialize(); + ManagedNetworkConfigurationHandler::Initialize(); + message_loop_.RunUntilIdle(); + } + + virtual void TearDown() OVERRIDE { + ManagedNetworkConfigurationHandler::Shutdown(); + NetworkConfigurationHandler::Shutdown(); + DBusThreadManager::Shutdown(); + } + + void SetUpEntry(const std::string& path_to_shill_json, + const std::string& profile_path, + const std::string& entry_path) { + stub_profile_client_.AddEntry( + profile_path, + entry_path, + *test_utils::ReadTestDictionary(path_to_shill_json)); + } + + void SetPolicy(onc::ONCSource onc_source, + const std::string& path_to_onc) { + scoped_ptr<base::DictionaryValue> policy; + if (path_to_onc.empty()) + policy = onc::ReadDictionaryFromJson(onc::kEmptyUnencryptedConfiguration); + else + policy = test_utils::ReadTestDictionary(path_to_onc); + + managed_handler()->SetPolicy(onc::ONC_SOURCE_USER_POLICY, *policy); + } + + ManagedNetworkConfigurationHandler* managed_handler() { + return ManagedNetworkConfigurationHandler::Get(); + } + + protected: + StrictMock<MockShillManagerClient> mock_manager_client_; + StrictMock<MockShillServiceClient> mock_service_client_; + StrictMock<MockShillProfileClient> mock_profile_client_; + ShillProfileClientStub stub_profile_client_; + MessageLoop message_loop_; + + private: + DISALLOW_COPY_AND_ASSIGN(ManagedNetworkConfigurationHandlerTest); +}; + +TEST_F(ManagedNetworkConfigurationHandlerTest, SetPolicyManageUnconfigured) { + scoped_ptr<base::DictionaryValue> expected_shill_properties = + test_utils::ReadTestDictionary( + "policy/shill_policy_on_unconfigured_wifi1.json"); + + EXPECT_CALL(mock_profile_client_, + GetProperties(dbus::ObjectPath("/profile/chronos/shill"), + _, _)).Times(1); + + EXPECT_CALL(mock_manager_client_, + ConfigureServiceForProfile( + dbus::ObjectPath("/profile/chronos/shill"), + IsEqualTo(expected_shill_properties.get()), + _, _)).Times(1); + + SetPolicy(onc::ONC_SOURCE_USER_POLICY, "policy/policy_wifi1.onc"); + message_loop_.RunUntilIdle(); +} + +TEST_F(ManagedNetworkConfigurationHandlerTest, SetPolicyIgnoreUnmodified) { + EXPECT_CALL(mock_profile_client_, + GetProperties(_, _, _)).Times(1); + + EXPECT_CALL(mock_manager_client_, + ConfigureServiceForProfile(_, _, _, _)).Times(1); + + SetPolicy(onc::ONC_SOURCE_USER_POLICY, "policy/policy_wifi1.onc"); + message_loop_.RunUntilIdle(); + Mock::VerifyAndClearExpectations(&mock_profile_client_); + Mock::VerifyAndClearExpectations(&mock_manager_client_); + + SetUpEntry("policy/shill_policy_on_unmanaged_user_wifi1.json", + "/profile/chronos/shill", + "some_entry_path"); + + EXPECT_CALL(mock_profile_client_, + GetProperties(_, _, _)).Times(1); + + EXPECT_CALL(mock_profile_client_, + GetEntry(dbus::ObjectPath("/profile/chronos/shill"), + "some_entry_path", + _, _)).Times(1); + + SetPolicy(onc::ONC_SOURCE_USER_POLICY, "policy/policy_wifi1.onc"); + message_loop_.RunUntilIdle(); +} + +TEST_F(ManagedNetworkConfigurationHandlerTest, SetPolicyManageUnmanaged) { + SetUpEntry("policy/shill_unmanaged_user_wifi1.json", + "/profile/chronos/shill", + "old_entry_path"); + + scoped_ptr<base::DictionaryValue> expected_shill_properties = + test_utils::ReadTestDictionary( + "policy/shill_policy_on_unmanaged_user_wifi1.json"); + + EXPECT_CALL(mock_profile_client_, + GetProperties(dbus::ObjectPath("/profile/chronos/shill"), + _, _)).Times(1); + + EXPECT_CALL(mock_profile_client_, + GetEntry(dbus::ObjectPath("/profile/chronos/shill"), + "old_entry_path", + _, _)).Times(1); + + EXPECT_CALL(mock_manager_client_, + ConfigureServiceForProfile( + dbus::ObjectPath("/profile/chronos/shill"), + IsEqualTo(expected_shill_properties.get()), + _, _)).Times(1); + + SetPolicy(onc::ONC_SOURCE_USER_POLICY, "policy/policy_wifi1.onc"); + message_loop_.RunUntilIdle(); +} + +TEST_F(ManagedNetworkConfigurationHandlerTest, SetPolicyUpdateManagedNewGUID) { + SetUpEntry("policy/shill_managed_wifi1.json", + "/profile/chronos/shill", + "old_entry_path"); + + scoped_ptr<base::DictionaryValue> expected_shill_properties = + test_utils::ReadTestDictionary( + "policy/shill_policy_on_unmanaged_user_wifi1.json"); + + // The passphrase isn't sent again, because it's configured by the user and + // Shill doesn't sent it on GetProperties calls. + expected_shill_properties->RemoveWithoutPathExpansion( + flimflam::kPassphraseProperty, NULL); + + EXPECT_CALL(mock_profile_client_, + GetProperties(dbus::ObjectPath("/profile/chronos/shill"), + _, _)).Times(1); + + EXPECT_CALL(mock_profile_client_, + GetEntry(dbus::ObjectPath("/profile/chronos/shill"), + "old_entry_path", + _, _)).Times(1); + + EXPECT_CALL(mock_manager_client_, + ConfigureServiceForProfile( + dbus::ObjectPath("/profile/chronos/shill"), + IsEqualTo(expected_shill_properties.get()), + _, _)).Times(1); + + SetPolicy(onc::ONC_SOURCE_USER_POLICY, "policy/policy_wifi1.onc"); + message_loop_.RunUntilIdle(); +} + +TEST_F(ManagedNetworkConfigurationHandlerTest, SetPolicyReapplyToManaged) { + SetUpEntry("policy/shill_policy_on_unmanaged_user_wifi1.json", + "/profile/chronos/shill", + "old_entry_path"); + + scoped_ptr<base::DictionaryValue> expected_shill_properties = + test_utils::ReadTestDictionary( + "policy/shill_policy_on_unmanaged_user_wifi1.json"); + + // The passphrase isn't sent again, because it's configured by the user and + // Shill doesn't sent it on GetProperties calls. + expected_shill_properties->RemoveWithoutPathExpansion( + flimflam::kPassphraseProperty, NULL); + + EXPECT_CALL(mock_profile_client_, + GetProperties(dbus::ObjectPath("/profile/chronos/shill"), + _, _)).Times(1); + + EXPECT_CALL(mock_profile_client_, + GetEntry(dbus::ObjectPath("/profile/chronos/shill"), + "old_entry_path", + _, _)).Times(1); + + EXPECT_CALL(mock_manager_client_, + ConfigureServiceForProfile( + dbus::ObjectPath("/profile/chronos/shill"), + IsEqualTo(expected_shill_properties.get()), + _, _)).Times(1); + + SetPolicy(onc::ONC_SOURCE_USER_POLICY, "policy/policy_wifi1.onc"); + message_loop_.RunUntilIdle(); +} + +TEST_F(ManagedNetworkConfigurationHandlerTest, SetPolicyUnmanageManaged) { + SetUpEntry("policy/shill_policy_on_unmanaged_user_wifi1.json", + "/profile/chronos/shill", + "old_entry_path"); + + EXPECT_CALL(mock_profile_client_, + GetProperties(dbus::ObjectPath("/profile/chronos/shill"), + _, _)).Times(1); + + EXPECT_CALL(mock_profile_client_, + GetEntry(dbus::ObjectPath("/profile/chronos/shill"), + "old_entry_path", + _, _)).Times(1); + + EXPECT_CALL(mock_profile_client_, + DeleteEntry(dbus::ObjectPath("/profile/chronos/shill"), + "old_entry_path", + _, _)).Times(1); + + SetPolicy(onc::ONC_SOURCE_USER_POLICY, ""); + message_loop_.RunUntilIdle(); +} + +TEST_F(ManagedNetworkConfigurationHandlerTest, SetPolicyIgnoreUnmanaged) { + SetUpEntry("policy/shill_unmanaged_user_wifi1.json", + "/profile/chronos/shill", + "old_entry_path"); + + EXPECT_CALL(mock_profile_client_, + GetProperties(dbus::ObjectPath("/profile/chronos/shill"), + _, _)).Times(1); + + EXPECT_CALL(mock_profile_client_, + GetEntry(dbus::ObjectPath("/profile/chronos/shill"), + "old_entry_path", + _, _)).Times(1); + + SetPolicy(onc::ONC_SOURCE_USER_POLICY, ""); + message_loop_.RunUntilIdle(); +} + +} // namespace chromeos diff --git a/chromeos/network/network_configuration_handler.cc b/chromeos/network/network_configuration_handler.cc index 17c691f..ee950d3 100644 --- a/chromeos/network/network_configuration_handler.cc +++ b/chromeos/network/network_configuration_handler.cc @@ -17,6 +17,7 @@ #include "chromeos/dbus/shill_manager_client.h" #include "chromeos/dbus/shill_service_client.h" #include "dbus/object_path.h" +#include "third_party/cros_system_api/dbus/service_constants.h" namespace chromeos { @@ -193,11 +194,32 @@ void NetworkConfigurationHandler::CreateConfiguration( const base::DictionaryValue& properties, const network_handler::StringResultCallback& callback, const network_handler::ErrorCallback& error_callback) const { - DBusThreadManager::Get()->GetShillManagerClient()->GetService( - properties, - base::Bind(&RunCreateNetworkCallback, callback), - base::Bind(&network_handler::ShillErrorCallbackFunction, - kLogModule, "", error_callback)); + ShillManagerClient* manager = + DBusThreadManager::Get()->GetShillManagerClient(); + + std::string type; + properties.GetStringWithoutPathExpansion(flimflam::kTypeProperty, &type); + // Shill supports ConfigureServiceForProfile only for network type WiFi. In + // all other cases, we have to rely on GetService for now. This is + // unproblematic for VPN (user profile only), but will lead to inconsistencies + // with WiMax, for example. + if (type == flimflam::kTypeWifi) { + std::string profile; + properties.GetStringWithoutPathExpansion(flimflam::kProfileProperty, + &profile); + manager->ConfigureServiceForProfile( + dbus::ObjectPath(profile), + properties, + base::Bind(&RunCreateNetworkCallback, callback), + base::Bind(&network_handler::ShillErrorCallbackFunction, + kLogModule, "", error_callback)); + } else { + manager->GetService( + properties, + base::Bind(&RunCreateNetworkCallback, callback), + base::Bind(&network_handler::ShillErrorCallbackFunction, + kLogModule, "", error_callback)); + } } void NetworkConfigurationHandler::RemoveConfiguration( diff --git a/chromeos/network/network_state.cc b/chromeos/network/network_state.cc index 8355028..4ab8b17 100644 --- a/chromeos/network/network_state.cc +++ b/chromeos/network/network_state.cc @@ -75,6 +75,8 @@ bool NetworkState::PropertyChanged(const std::string& key, return GetStringValue(key, value, &device_path_); } else if (key == flimflam::kGuidProperty) { return GetStringValue(key, value, &guid_); + } else if (key == flimflam::kProfileProperty) { + return GetStringValue(key, value, &profile_path_); } else if (key == shill::kActivateOverNonCellularNetworkProperty) { return GetBooleanValue(key, value, &activate_over_non_cellular_networks_); } else if (key == shill::kOutOfCreditsProperty) { @@ -121,6 +123,8 @@ void NetworkState::GetProperties(base::DictionaryValue* dictionary) const { dictionary->SetStringWithoutPathExpansion(flimflam::kDeviceProperty, device_path()); dictionary->SetStringWithoutPathExpansion(flimflam::kGuidProperty, guid()); + dictionary->SetStringWithoutPathExpansion(flimflam::kProfileProperty, + profile_path()); dictionary->SetBooleanWithoutPathExpansion( shill::kActivateOverNonCellularNetworkProperty, activate_over_non_cellular_networks()); diff --git a/chromeos/network/network_state.h b/chromeos/network/network_state.h index d43def6..3b7d283 100644 --- a/chromeos/network/network_state.h +++ b/chromeos/network/network_state.h @@ -43,6 +43,7 @@ class CHROMEOS_EXPORT NetworkState : public ManagedState { const std::string& device_path() const { return device_path_; } const std::string& guid() const { return guid_; } const std::string& connection_state() const { return connection_state_; } + const std::string& profile_path() const { return profile_path_; } const std::string& error() const { return error_; } bool auto_connect() const { return auto_connect_; } bool favorite() const { return favorite_; } @@ -86,6 +87,7 @@ class CHROMEOS_EXPORT NetworkState : public ManagedState { std::string device_path_; std::string guid_; std::string connection_state_; + std::string profile_path_; std::string error_; bool auto_connect_; bool favorite_; diff --git a/chromeos/network/network_ui_data.cc b/chromeos/network/network_ui_data.cc index 3345895..a80ee3e 100644 --- a/chromeos/network/network_ui_data.cc +++ b/chromeos/network/network_ui_data.cc @@ -14,6 +14,7 @@ namespace chromeos { const char NetworkUIData::kKeyONCSource[] = "onc_source"; const char NetworkUIData::kKeyCertificatePattern[] = "certificate_pattern"; const char NetworkUIData::kKeyCertificateType[] = "certificate_type"; +const char NetworkUIData::kKeyUserSettings[] = "user_settings"; namespace { @@ -61,13 +62,27 @@ Enum StringToEnum(const StringEnumEntry<Enum>(& table)[N], return fallback; } -} +} // namespace NetworkUIData::NetworkUIData() : onc_source_(onc::ONC_SOURCE_NONE), certificate_type_(CLIENT_CERT_TYPE_NONE) { } +NetworkUIData::NetworkUIData(const NetworkUIData& other) { + *this = other; +} + +NetworkUIData& NetworkUIData::operator=(const NetworkUIData& other) { + certificate_pattern_ = other.certificate_pattern_; + onc_source_ = other.onc_source_; + certificate_type_ = other.certificate_type_; + if (other.user_settings_) + user_settings_.reset(other.user_settings_->DeepCopy()); + policy_guid_ = other.policy_guid_; + return *this; +} + NetworkUIData::NetworkUIData(const DictionaryValue& dict) { std::string source; dict.GetString(kKeyONCSource, &source); @@ -88,6 +103,10 @@ NetworkUIData::NetworkUIData(const DictionaryValue& dict) { certificate_type_ = CLIENT_CERT_TYPE_NONE; } } + + const DictionaryValue* user_settings = NULL; + if (dict.GetDictionary(kKeyUserSettings, &user_settings)) + user_settings_.reset(user_settings->DeepCopy()); } NetworkUIData::~NetworkUIData() { @@ -110,6 +129,9 @@ void NetworkUIData::FillDictionary(base::DictionaryValue* dict) const { certificate_pattern_.CreateAsDictionary()); } } + if (user_settings_) + dict->SetWithoutPathExpansion(kKeyUserSettings, + user_settings_->DeepCopy()); } namespace { diff --git a/chromeos/network/network_ui_data.h b/chromeos/network/network_ui_data.h index 2c93a9f..b65987e 100644 --- a/chromeos/network/network_ui_data.h +++ b/chromeos/network/network_ui_data.h @@ -25,8 +25,6 @@ enum ClientCertType { CLIENT_CERT_TYPE_PATTERN = 2 }; -class NetworkPropertyUIData; - // Helper for accessing and setting values in the network's UI data dictionary. // Accessing values is done via static members that take the network as an // argument. In order to fill a UI data dictionary, construct an instance, set @@ -39,6 +37,8 @@ class NetworkPropertyUIData; class CHROMEOS_EXPORT NetworkUIData { public: NetworkUIData(); + NetworkUIData(const NetworkUIData& other); + NetworkUIData& operator=(const NetworkUIData& other); explicit NetworkUIData(const base::DictionaryValue& dict); ~NetworkUIData(); @@ -61,26 +61,41 @@ class CHROMEOS_EXPORT NetworkUIData { return onc_source_ == onc::ONC_SOURCE_DEVICE_POLICY || onc_source_ == onc::ONC_SOURCE_USER_POLICY; } + const base::DictionaryValue* user_settings() const { + return user_settings_.get(); + } + void set_user_settings(scoped_ptr<base::DictionaryValue> dict) { + user_settings_ = dict.Pass(); + } + const std::string& policy_guid() const { + return policy_guid_; + } + void set_policy_guid(const std::string& guid) { + policy_guid_ = guid; + } // Fills in |dict| with the currently configured values. This will write the // keys appropriate for Network::ui_data() as defined below (kKeyXXX). void FillDictionary(base::DictionaryValue* dict) const; - // Key for storing source of the ONC network, which is an integer according to - // enum ONCSource. + // Key for storing source of the ONC network. static const char kKeyONCSource[]; - // Key for storing certificate pattern for this network (if any). + // Key for storing the certificate pattern. static const char kKeyCertificatePattern[]; - // Key for storing certificate type for this network (if any), which is one of - // "pattern", "ref", or "none", according to ClientCertType. + // Key for storing the certificate type. static const char kKeyCertificateType[]; + // Key for storing the user settings. + static const char kKeyUserSettings[]; + private: CertificatePattern certificate_pattern_; onc::ONCSource onc_source_; ClientCertType certificate_type_; + scoped_ptr<base::DictionaryValue> user_settings_; + std::string policy_guid_; }; // Creates a NetworkUIData object from |onc_network|, which has to be a valid diff --git a/chromeos/network/onc/onc_constants.cc b/chromeos/network/onc/onc_constants.cc index 0fbc7a5..46414bf 100644 --- a/chromeos/network/onc/onc_constants.cc +++ b/chromeos/network/onc/onc_constants.cc @@ -9,7 +9,9 @@ namespace chromeos { // Constants for ONC properties. namespace onc { +const char kAugmentationActiveSetting[] = "Active"; const char kAugmentationEffectiveSetting[] = "Effective"; +const char kAugmentationUnmanaged[] = "Unmanaged"; const char kAugmentationUserPolicy[] = "UserPolicy"; const char kAugmentationDevicePolicy[] = "DevicePolicy"; const char kAugmentationUserSetting[] = "UserSetting"; diff --git a/chromeos/network/onc/onc_constants.h b/chromeos/network/onc/onc_constants.h index 1d1ccf9..76325b1 100644 --- a/chromeos/network/onc/onc_constants.h +++ b/chromeos/network/onc/onc_constants.h @@ -21,7 +21,15 @@ enum ONCSource { // These keys are used to augment the dictionary resulting from merging the // different settings and policies. + +// The setting that Shill declared to be using. For example, if no policy and no +// user setting exists, Shill might still report a property like network +// security options or a SSID. +CHROMEOS_EXPORT extern const char kAugmentationActiveSetting[]; +// The one of different setting sources (user/device policy, user/shared +// settings) that has highest priority over the others. CHROMEOS_EXPORT extern const char kAugmentationEffectiveSetting[]; +CHROMEOS_EXPORT extern const char kAugmentationUnmanaged[]; CHROMEOS_EXPORT extern const char kAugmentationUserPolicy[]; CHROMEOS_EXPORT extern const char kAugmentationDevicePolicy[]; CHROMEOS_EXPORT extern const char kAugmentationUserSetting[]; diff --git a/chromeos/network/onc/onc_merger.cc b/chromeos/network/onc/onc_merger.cc index 8403f91..e7703d3 100644 --- a/chromeos/network/onc/onc_merger.cc +++ b/chromeos/network/onc/onc_merger.cc @@ -12,6 +12,7 @@ #include "base/logging.h" #include "base/values.h" #include "chromeos/network/onc/onc_constants.h" +#include "chromeos/network/onc/onc_signature.h" namespace chromeos { namespace onc { @@ -96,7 +97,7 @@ class MergeListOfDictionaries { (*it_inner)->GetDictionaryWithoutPathExpansion(key, &nested_dict); nested_dicts.push_back(nested_dict); } - DictionaryPtr merged_dict(MergeDictionaries(nested_dicts)); + DictionaryPtr merged_dict(MergeNestedDictionaries(key, nested_dicts)); if (!merged_dict->empty()) merged_value = merged_dict.Pass(); } else { @@ -108,7 +109,7 @@ class MergeListOfDictionaries { (*it_inner)->GetWithoutPathExpansion(key, &value); values.push_back(value); } - merged_value = MergeListOfValues(values); + merged_value = MergeListOfValues(key, values); } if (merged_value) @@ -124,8 +125,14 @@ class MergeListOfDictionaries { // values is the same as of the given dictionaries |dicts|. If a dictionary // doesn't contain a path then it's value is NULL. virtual scoped_ptr<base::Value> MergeListOfValues( + const std::string& key, const std::vector<const base::Value*>& values) = 0; + virtual DictionaryPtr MergeNestedDictionaries(const std::string& key, + const DictPtrs &dicts) { + return MergeDictionaries(dicts); + } + private: DISALLOW_COPY_AND_ASSIGN(MergeListOfDictionaries); }; @@ -136,12 +143,15 @@ class MergeSettingsAndPolicies : public MergeListOfDictionaries { struct ValueParams { const base::Value* user_policy; const base::Value* device_policy; - const base::Value* user_settings; - const base::Value* shared_settings; + const base::Value* user_setting; + const base::Value* shared_setting; + const base::Value* active_setting; bool user_editable; bool device_editable; }; + MergeSettingsAndPolicies() {} + // Merge the provided dictionaries. For each path in any of the dictionaries, // MergeValues is called. Its results are collected in a new dictionary which // is then returned. The resulting dictionary never contains empty @@ -150,7 +160,8 @@ class MergeSettingsAndPolicies : public MergeListOfDictionaries { const base::DictionaryValue* user_policy, const base::DictionaryValue* device_policy, const base::DictionaryValue* user_settings, - const base::DictionaryValue* shared_settings) { + const base::DictionaryValue* shared_settings, + const base::DictionaryValue* active_settings) { hasUserPolicy_ = (user_policy != NULL); hasDevicePolicy_ = (device_policy != NULL); @@ -167,6 +178,7 @@ class MergeSettingsAndPolicies : public MergeListOfDictionaries { dicts[kDevicePolicyIndex] = device_policy; dicts[kUserSettingsIndex] = user_settings; dicts[kSharedSettingsIndex] = shared_settings; + dicts[kActiveSettingsIndex] = active_settings; dicts[kUserEditableIndex] = user_editable.get(); dicts[kDeviceEditableIndex] = device_editable.get(); return MergeListOfDictionaries::MergeDictionaries(dicts); @@ -176,7 +188,8 @@ class MergeSettingsAndPolicies : public MergeListOfDictionaries { // This function is called by MergeDictionaries for each list of values that // are located at the same path in each of the dictionaries. Implementations // can use the Has*Policy functions. - virtual scoped_ptr<base::Value> MergeValues(ValueParams values) = 0; + virtual scoped_ptr<base::Value> MergeValues(const std::string& key, + const ValueParams& values) = 0; // Whether a user policy was provided. bool HasUserPolicy() { @@ -190,6 +203,7 @@ class MergeSettingsAndPolicies : public MergeListOfDictionaries { // MergeListOfDictionaries override. virtual scoped_ptr<base::Value> MergeListOfValues( + const std::string& key, const std::vector<const base::Value*>& values) OVERRIDE { bool user_editable = !HasUserPolicy(); if (values[kUserEditableIndex]) @@ -202,11 +216,12 @@ class MergeSettingsAndPolicies : public MergeListOfDictionaries { ValueParams params; params.user_policy = values[kUserPolicyIndex]; params.device_policy = values[kDevicePolicyIndex]; - params.user_settings = values[kUserSettingsIndex]; - params.shared_settings = values[kSharedSettingsIndex]; + params.user_setting = values[kUserSettingsIndex]; + params.shared_setting = values[kSharedSettingsIndex]; + params.active_setting = values[kActiveSettingsIndex]; params.user_editable = user_editable; params.device_editable = device_editable; - return MergeValues(params); + return MergeValues(key, params); } private: @@ -215,17 +230,24 @@ class MergeSettingsAndPolicies : public MergeListOfDictionaries { kDevicePolicyIndex, kUserSettingsIndex, kSharedSettingsIndex, + kActiveSettingsIndex, kUserEditableIndex, kDeviceEditableIndex, kLastIndex }; bool hasUserPolicy_, hasDevicePolicy_; + + DISALLOW_COPY_AND_ASSIGN(MergeSettingsAndPolicies); }; // Call MergeDictionaries to merge policies and settings to the effective -// values. See the description of MergeSettingsAndPoliciesToEffective. +// values. This ignores the active settings of Shill. See the description of +// MergeSettingsAndPoliciesToEffective. class MergeToEffective : public MergeSettingsAndPolicies { + public: + MergeToEffective() {} + protected: // Merges |values| to the effective value (Mandatory policy overwrites user // settings overwrites shared settings overwrites recommended policy). |which| @@ -234,7 +256,9 @@ class MergeToEffective : public MergeSettingsAndPolicies { // pointer and set |which| to kAugmentationUserPolicy, which means that the // user policy didn't set a value but also didn't recommend it, thus enforcing // the empty value. - scoped_ptr<base::Value> MergeValues(ValueParams values, std::string* which) { + scoped_ptr<base::Value> MergeValues(const std::string& key, + const ValueParams& values, + std::string* which) { const base::Value* result = NULL; which->clear(); if (!values.user_editable) { @@ -243,11 +267,11 @@ class MergeToEffective : public MergeSettingsAndPolicies { } else if (!values.device_editable) { result = values.device_policy; *which = kAugmentationDevicePolicy; - } else if (values.user_settings) { - result = values.user_settings; + } else if (values.user_setting) { + result = values.user_setting; *which = kAugmentationUserSetting; - } else if (values.shared_settings) { - result = values.shared_settings; + } else if (values.shared_setting) { + result = values.shared_setting; *which = kAugmentationSharedSetting; } else if (values.user_policy) { result = values.user_policy; @@ -266,53 +290,129 @@ class MergeToEffective : public MergeSettingsAndPolicies { // MergeSettingsAndPolicies override. virtual scoped_ptr<base::Value> MergeValues( - ValueParams values) OVERRIDE { + const std::string& key, + const ValueParams& values) OVERRIDE { std::string which; - return MergeValues(values, &which); + return MergeValues(key, values, &which); } + + private: + DISALLOW_COPY_AND_ASSIGN(MergeToEffective); }; // Call MergeDictionaries to merge policies and settings to an augmented // dictionary which contains a dictionary for each value in the original // dictionaries. See the description of MergeSettingsAndPoliciesToAugmented. class MergeToAugmented : public MergeToEffective { + public: + MergeToAugmented() {} + + DictionaryPtr MergeDictionaries( + const OncValueSignature& signature, + const base::DictionaryValue* user_policy, + const base::DictionaryValue* device_policy, + const base::DictionaryValue* user_settings, + const base::DictionaryValue* shared_settings, + const base::DictionaryValue* active_settings) { + signature_ = &signature; + return MergeToEffective::MergeDictionaries(user_policy, + device_policy, + user_settings, + shared_settings, + active_settings); + } + protected: // MergeSettingsAndPolicies override. virtual scoped_ptr<base::Value> MergeValues( - ValueParams values) OVERRIDE { + const std::string& key, + const ValueParams& values) OVERRIDE { scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue); - std::string which_effective; - MergeToEffective::MergeValues(values, &which_effective).reset(); - if (!which_effective.empty()) { - result->SetStringWithoutPathExpansion(kAugmentationEffectiveSetting, - which_effective); - } - if (values.user_policy) { - result->SetWithoutPathExpansion(kAugmentationUserPolicy, - values.user_policy->DeepCopy()); - } - if (values.device_policy) { - result->SetWithoutPathExpansion(kAugmentationDevicePolicy, - values.device_policy->DeepCopy()); - } - if (values.user_settings) { - result->SetWithoutPathExpansion(kAugmentationUserSetting, - values.user_settings->DeepCopy()); - } - if (values.shared_settings) { - result->SetWithoutPathExpansion(kAugmentationSharedSetting, - values.shared_settings->DeepCopy()); + if (values.active_setting) { + result->SetWithoutPathExpansion(kAugmentationActiveSetting, + values.active_setting->DeepCopy()); } - if (HasUserPolicy() && values.user_editable) { - result->SetBooleanWithoutPathExpansion(kAugmentationUserEditable, - values.user_editable); - } - if (HasDevicePolicy() && values.device_editable) { - result->SetBooleanWithoutPathExpansion(kAugmentationDeviceEditable, - values.device_editable); + + const OncFieldSignature* field = NULL; + if (signature_) + field = GetFieldSignature(*signature_, key); + + if (field) { + // This field is part of the provided ONCSignature, thus it can be + // controlled by policy. + std::string which_effective; + MergeToEffective::MergeValues(key, values, &which_effective).reset(); + if (!which_effective.empty()) { + result->SetStringWithoutPathExpansion(kAugmentationEffectiveSetting, + which_effective); + } + bool is_credential = onc::FieldIsCredential(*signature_, key); + + // Prevent credentials from being forwarded in cleartext to + // UI. User/shared credentials are not stored separately, so they cannot + // leak here. + if (!is_credential) { + if (values.user_policy) { + result->SetWithoutPathExpansion(kAugmentationUserPolicy, + values.user_policy->DeepCopy()); + } + if (values.device_policy) { + result->SetWithoutPathExpansion(kAugmentationDevicePolicy, + values.device_policy->DeepCopy()); + } + } + if (values.user_setting) { + result->SetWithoutPathExpansion(kAugmentationUserSetting, + values.user_setting->DeepCopy()); + } + if (values.shared_setting) { + result->SetWithoutPathExpansion(kAugmentationSharedSetting, + values.shared_setting->DeepCopy()); + } + if (HasUserPolicy() && values.user_editable) { + result->SetBooleanWithoutPathExpansion(kAugmentationUserEditable, + true); + } + if (HasDevicePolicy() && values.device_editable) { + result->SetBooleanWithoutPathExpansion(kAugmentationDeviceEditable, + true); + } + } else { + // This field is not part of the provided ONCSignature, thus it cannot be + // controlled by policy. + result->SetStringWithoutPathExpansion(kAugmentationEffectiveSetting, + kAugmentationUnmanaged); } + if (result->empty()) + result.reset(); return result.PassAs<base::Value>(); } + + // MergeListOfDictionaries override. + virtual DictionaryPtr MergeNestedDictionaries( + const std::string& key, + const DictPtrs &dicts) OVERRIDE { + DictionaryPtr result; + if (signature_) { + const OncValueSignature* enclosing_signature = signature_; + signature_ = NULL; + + const OncFieldSignature* field = + GetFieldSignature(*enclosing_signature, key); + if (field) + signature_ = field->value_signature; + result = MergeToEffective::MergeNestedDictionaries(key, dicts); + + signature_ = enclosing_signature; + } else { + result = MergeToEffective::MergeNestedDictionaries(key, dicts); + } + return result.Pass(); + } + + private: + const OncValueSignature* signature_; + DISALLOW_COPY_AND_ASSIGN(MergeToAugmented); }; } // namespace @@ -324,17 +424,20 @@ DictionaryPtr MergeSettingsAndPoliciesToEffective( const base::DictionaryValue* shared_settings) { MergeToEffective merger; return merger.MergeDictionaries( - user_policy, device_policy, user_settings, shared_settings); + user_policy, device_policy, user_settings, shared_settings, NULL); } DictionaryPtr MergeSettingsAndPoliciesToAugmented( + const OncValueSignature& signature, const base::DictionaryValue* user_policy, const base::DictionaryValue* device_policy, const base::DictionaryValue* user_settings, - const base::DictionaryValue* shared_settings) { + const base::DictionaryValue* shared_settings, + const base::DictionaryValue* active_settings) { MergeToAugmented merger; return merger.MergeDictionaries( - user_policy, device_policy, user_settings, shared_settings); + signature, user_policy, device_policy, user_settings, shared_settings, + active_settings); } } // namespace onc diff --git a/chromeos/network/onc/onc_merger.h b/chromeos/network/onc/onc_merger.h index b3d4e43..35e97fd 100644 --- a/chromeos/network/onc/onc_merger.h +++ b/chromeos/network/onc/onc_merger.h @@ -15,6 +15,8 @@ class DictionaryValue; namespace chromeos { namespace onc { +struct OncValueSignature; + // Merges the given |user_settings| and |shared_settings| settings with the // given |user_policy| and |device_policy| settings. Each can be omitted by // providing a NULL pointer. Each dictionary has to be part of a valid ONC @@ -36,13 +38,16 @@ MergeSettingsAndPoliciesToEffective( // dictionaries contains the onc::kAugmentations* fields (see onc_constants.h) // for which a value is available. The onc::kAugmentationEffectiveSetting field // contains the field name of the field containing the effective field that -// overrides all other values. +// overrides all other values. Credentials from policies are not written to the +// result. CHROMEOS_EXPORT scoped_ptr<base::DictionaryValue> MergeSettingsAndPoliciesToAugmented( + const OncValueSignature& signature, const base::DictionaryValue* user_policy, const base::DictionaryValue* device_policy, const base::DictionaryValue* user_settings, - const base::DictionaryValue* shared_settings); + const base::DictionaryValue* shared_settings, + const base::DictionaryValue* active_settings); } // namespace onc } // namespace chromeos diff --git a/chromeos/network/onc/onc_merger_unittest.cc b/chromeos/network/onc/onc_merger_unittest.cc index b6187fd..f0dbdc5 100644 --- a/chromeos/network/onc/onc_merger_unittest.cc +++ b/chromeos/network/onc/onc_merger_unittest.cc @@ -9,6 +9,7 @@ #include "base/logging.h" #include "base/values.h" #include "chromeos/network/onc/onc_constants.h" +#include "chromeos/network/onc/onc_signature.h" #include "chromeos/network/onc/onc_test_utils.h" #include "testing/gtest/include/gtest/gtest.h" @@ -146,7 +147,8 @@ TEST_F(ONCMergerTest, MergeToAugmented) { scoped_ptr<base::DictionaryValue> expected_augmented = test_utils::ReadTestDictionary("augmented_merge.json"); scoped_ptr<base::DictionaryValue> merged(MergeSettingsAndPoliciesToAugmented( - policy_.get(), device_policy_.get(), user_.get(), NULL)); + kNetworkConfigurationSignature, policy_.get(), device_policy_.get(), + user_.get(), NULL, NULL)); EXPECT_TRUE(test_utils::Equals(expected_augmented.get(), merged.get())); } diff --git a/chromeos/network/onc/onc_signature.cc b/chromeos/network/onc/onc_signature.cc index 964eb4d..aee54bb 100644 --- a/chromeos/network/onc/onc_signature.cc +++ b/chromeos/network/onc/onc_signature.cc @@ -195,7 +195,7 @@ const OncFieldSignature wifi_with_state_fields[] = { const OncFieldSignature cellular_with_state_fields[] = { { kRecommended, &kRecommendedSignature }, - { cellular::kActivateOverNonCellularNetwork, &kStringSignature }, + { cellular::kActivateOverNonCellularNetwork, &kBoolSignature }, { cellular::kActivationState, &kStringSignature }, { cellular::kAllowRoaming, &kStringSignature }, { cellular::kAPN, &kStringSignature }, diff --git a/chromeos/network/onc/onc_translation_tables.cc b/chromeos/network/onc/onc_translation_tables.cc index 74a2f94..8496aeb 100644 --- a/chromeos/network/onc/onc_translation_tables.cc +++ b/chromeos/network/onc/onc_translation_tables.cc @@ -190,11 +190,13 @@ const StringTranslationEntry kVPNTypeTable[] = { { NULL } }; +// The first matching line is chosen. const StringTranslationEntry kWiFiSecurityTable[] = { { wifi::kNone, flimflam::kSecurityNone }, { wifi::kWEP_PSK, flimflam::kSecurityWep }, { wifi::kWPA_PSK, flimflam::kSecurityPsk }, { wifi::kWPA_EAP, flimflam::kSecurity8021x }, + { wifi::kWPA_PSK, flimflam::kSecurityRsn }, { NULL } }; diff --git a/chromeos/network/onc/onc_utils.cc b/chromeos/network/onc/onc_utils.cc index b8c42ec..c14a2a0 100644 --- a/chromeos/network/onc/onc_utils.cc +++ b/chromeos/network/onc/onc_utils.cc @@ -11,6 +11,7 @@ #include "base/values.h" #include "chromeos/network/network_event_log.h" #include "chromeos/network/onc/onc_mapper.h" +#include "chromeos/network/onc/onc_signature.h" #include "crypto/encryptor.h" #include "crypto/hmac.h" #include "crypto/symmetric_key.h" diff --git a/chromeos/network/onc/onc_utils.h b/chromeos/network/onc/onc_utils.h index c431466..1d4e28f 100644 --- a/chromeos/network/onc/onc_utils.h +++ b/chromeos/network/onc/onc_utils.h @@ -11,7 +11,6 @@ #include "base/memory/scoped_ptr.h" #include "chromeos/chromeos_export.h" #include "chromeos/network/onc/onc_constants.h" -#include "chromeos/network/onc/onc_signature.h" namespace base { class DictionaryValue; @@ -20,6 +19,8 @@ class DictionaryValue; namespace chromeos { namespace onc { +struct OncValueSignature; + // A valid but empty (no networks and no certificates) and unencrypted // configuration. CHROMEOS_EXPORT extern const char kEmptyUnencryptedConfiguration[]; @@ -68,7 +69,7 @@ CHROMEOS_EXPORT void ExpandStringsInOncObject( // by |mask|. To find sensitive fields, signature and field name are checked // with the function FieldIsCredential(). CHROMEOS_EXPORT scoped_ptr<base::DictionaryValue> MaskCredentialsInOncObject( - const onc::OncValueSignature& signature, + const OncValueSignature& signature, const base::DictionaryValue& onc_object, const std::string& mask); diff --git a/chromeos/network/onc/onc_utils_unittest.cc b/chromeos/network/onc/onc_utils_unittest.cc index 98ef4df..97a0cb2 100644 --- a/chromeos/network/onc/onc_utils_unittest.cc +++ b/chromeos/network/onc/onc_utils_unittest.cc @@ -7,6 +7,7 @@ #include <string> #include "base/values.h" +#include "chromeos/network/onc/onc_signature.h" #include "chromeos/network/onc/onc_test_utils.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/chromeos/test/data/network/augmented_merge.json b/chromeos/test/data/network/augmented_merge.json index 60ae5b0..fbd90ba 100644 --- a/chromeos/test/data/network/augmented_merge.json +++ b/chromeos/test/data/network/augmented_merge.json @@ -61,9 +61,7 @@ "UserPolicy": 1 }, "PSK": { - "DevicePolicy": "sharedkey", "Effective": "UserPolicy", - "UserPolicy": "sharedkey" } }, "OpenVPN": { diff --git a/chromeos/test/data/network/policy/policy_wifi1.onc b/chromeos/test/data/network/policy/policy_wifi1.onc new file mode 100644 index 0000000..e601c95 --- /dev/null +++ b/chromeos/test/data/network/policy/policy_wifi1.onc @@ -0,0 +1,16 @@ +{ + "NetworkConfigurations": [ + { + "GUID": "policy_wifi1", + "Type": "WiFi", + "Name": "Managed wifi1", + "WiFi": { + "Passphrase": "policy's passphrase", + "Recommended": [ "AutoConnect", "Passphrase" ], + "SSID": "wifi1", + "Security": "WPA-PSK" + } + } + ], + "Type": "UnencryptedConfiguration" +} diff --git a/chromeos/test/data/network/policy/shill_managed_wifi1.json b/chromeos/test/data/network/policy/shill_managed_wifi1.json new file mode 100644 index 0000000..031e5cb --- /dev/null +++ b/chromeos/test/data/network/policy/shill_managed_wifi1.json @@ -0,0 +1,13 @@ +{ + "EAP.EAP": "TLS", + "EAP.Identity": "${LOGIN_ID}@my.domain.com", + "EAP.UseSystemCAs": true, + "GUID": "policy2_wifi1", + "Mode": "managed", + "Passphrase": "user's passphrase", + "Profile": "/profile/chronos/shill", + "SSID": "wifi1", + "Security": "802_1x", + "Type": "wifi", + "UIData": "{\"onc_source\":\"user_policy\",\"user_settings\":{\"WiFi\":{\"Passphrase\":\"FAKE_CREDENTIAL_VPaJDV9x\"}}}" +} diff --git a/chromeos/test/data/network/policy/shill_policy_on_unconfigured_wifi1.json b/chromeos/test/data/network/policy/shill_policy_on_unconfigured_wifi1.json new file mode 100644 index 0000000..2a01694 --- /dev/null +++ b/chromeos/test/data/network/policy/shill_policy_on_unconfigured_wifi1.json @@ -0,0 +1,10 @@ +{ + "GUID": "policy_wifi1", + "Mode": "managed", + "Passphrase": "policy's passphrase", + "Profile": "/profile/chronos/shill", + "SSID": "wifi1", + "Security": "psk", + "Type": "wifi", + "UIData": "{\"onc_source\":\"user_policy\"}" +} diff --git a/chromeos/test/data/network/policy/shill_policy_on_unmanaged_user_wifi1.json b/chromeos/test/data/network/policy/shill_policy_on_unmanaged_user_wifi1.json new file mode 100644 index 0000000..0d0cbcc --- /dev/null +++ b/chromeos/test/data/network/policy/shill_policy_on_unmanaged_user_wifi1.json @@ -0,0 +1,10 @@ +{ + "GUID": "policy_wifi1", + "Mode": "managed", + "Passphrase": "user's passphrase", + "Profile": "/profile/chronos/shill", + "SSID": "wifi1", + "Security": "psk", + "Type": "wifi", + "UIData": "{\"onc_source\":\"user_policy\",\"user_settings\":{\"WiFi\":{\"Passphrase\":\"FAKE_CREDENTIAL_VPaJDV9x\"}}}" +} diff --git a/chromeos/test/data/network/policy/shill_unmanaged_user_wifi1.json b/chromeos/test/data/network/policy/shill_unmanaged_user_wifi1.json new file mode 100644 index 0000000..45cbe90 --- /dev/null +++ b/chromeos/test/data/network/policy/shill_unmanaged_user_wifi1.json @@ -0,0 +1,10 @@ +{ + "GUID": "{unmanaged_user_wifi1}", + "Mode": "managed", + "Passphrase": "user's passphrase", + "Profile": "/profile/chronos/shill", + "SSID": "wifi1", + "Security": "psk", + "Type": "wifi", + "UIData": "{\"user_settings\":{\"WiFi\":{\"Passphrase\":\"user's passphrase\"}}}" +} |