Some additional network settings cleanup
[chromium-blink-merge.git] / chromeos / network / policy_applicator.cc
blob2c1e720d961387e96ea1ac322a3c5bd04debab3d
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chromeos/network/policy_applicator.h"
7 #include <utility>
9 #include "base/bind.h"
10 #include "base/location.h"
11 #include "base/logging.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/stl_util.h"
14 #include "base/values.h"
15 #include "chromeos/dbus/dbus_thread_manager.h"
16 #include "chromeos/dbus/shill_profile_client.h"
17 #include "chromeos/network/network_type_pattern.h"
18 #include "chromeos/network/network_ui_data.h"
19 #include "chromeos/network/onc/onc_signature.h"
20 #include "chromeos/network/onc/onc_translator.h"
21 #include "chromeos/network/policy_util.h"
22 #include "chromeos/network/shill_property_util.h"
23 #include "components/onc/onc_constants.h"
24 #include "dbus/object_path.h"
25 #include "third_party/cros_system_api/dbus/service_constants.h"
27 namespace chromeos {
29 namespace {
31 void LogErrorMessage(const tracked_objects::Location& from_where,
32 const std::string& error_name,
33 const std::string& error_message) {
34 LOG(ERROR) << from_where.ToString() << ": " << error_message;
37 const base::DictionaryValue* GetByGUID(
38 const PolicyApplicator::GuidToPolicyMap& policies,
39 const std::string& guid) {
40 PolicyApplicator::GuidToPolicyMap::const_iterator it = policies.find(guid);
41 if (it == policies.end())
42 return NULL;
43 return it->second;
46 } // namespace
48 PolicyApplicator::PolicyApplicator(
49 base::WeakPtr<ConfigurationHandler> handler,
50 const NetworkProfile& profile,
51 const GuidToPolicyMap& all_policies,
52 const base::DictionaryValue& global_network_config,
53 std::set<std::string>* modified_policies)
54 : handler_(handler), profile_(profile) {
55 global_network_config_.MergeDictionary(&global_network_config);
56 remaining_policies_.swap(*modified_policies);
57 for (GuidToPolicyMap::const_iterator it = all_policies.begin();
58 it != all_policies.end(); ++it) {
59 all_policies_.insert(std::make_pair(it->first, it->second->DeepCopy()));
63 void PolicyApplicator::Run() {
64 DBusThreadManager::Get()->GetShillProfileClient()->GetProperties(
65 dbus::ObjectPath(profile_.path),
66 base::Bind(&PolicyApplicator::GetProfilePropertiesCallback, this),
67 base::Bind(&LogErrorMessage, FROM_HERE));
70 void PolicyApplicator::GetProfilePropertiesCallback(
71 const base::DictionaryValue& profile_properties) {
72 if (!handler_) {
73 LOG(WARNING) << "Handler destructed during policy application to profile "
74 << profile_.ToDebugString();
75 return;
78 VLOG(2) << "Received properties for profile " << profile_.ToDebugString();
79 const base::ListValue* entries = NULL;
80 if (!profile_properties.GetListWithoutPathExpansion(
81 shill::kEntriesProperty, &entries)) {
82 LOG(ERROR) << "Profile " << profile_.ToDebugString()
83 << " doesn't contain the property "
84 << shill::kEntriesProperty;
85 return;
88 for (base::ListValue::const_iterator it = entries->begin();
89 it != entries->end(); ++it) {
90 std::string entry;
91 (*it)->GetAsString(&entry);
93 DBusThreadManager::Get()->GetShillProfileClient()->GetEntry(
94 dbus::ObjectPath(profile_.path),
95 entry,
96 base::Bind(&PolicyApplicator::GetEntryCallback, this, entry),
97 base::Bind(&LogErrorMessage, FROM_HERE));
101 void PolicyApplicator::GetEntryCallback(
102 const std::string& entry,
103 const base::DictionaryValue& entry_properties) {
104 if (!handler_) {
105 LOG(WARNING) << "Handler destructed during policy application to profile "
106 << profile_.ToDebugString();
107 return;
110 VLOG(2) << "Received properties for entry " << entry << " of profile "
111 << profile_.ToDebugString();
113 scoped_ptr<base::DictionaryValue> onc_part(
114 onc::TranslateShillServiceToONCPart(entry_properties,
115 ::onc::ONC_SOURCE_UNKNOWN,
116 &onc::kNetworkWithStateSignature));
118 std::string old_guid;
119 if (!onc_part->GetStringWithoutPathExpansion(::onc::network_config::kGUID,
120 &old_guid)) {
121 VLOG(1) << "Entry " << entry << " of profile " << profile_.ToDebugString()
122 << " doesn't contain a GUID.";
123 // This might be an entry of an older ChromeOS version. Assume it to be
124 // unmanaged.
127 scoped_ptr<NetworkUIData> ui_data =
128 shill_property_util::GetUIDataFromProperties(entry_properties);
129 if (!ui_data) {
130 VLOG(1) << "Entry " << entry << " of profile " << profile_.ToDebugString()
131 << " contains no or no valid UIData.";
132 // This might be an entry of an older ChromeOS version. Assume it to be
133 // unmanaged. It's an inconsistency if there is a GUID but no UIData, thus
134 // clear the GUID just in case.
135 old_guid.clear();
138 bool was_managed = !old_guid.empty() && ui_data &&
139 (ui_data->onc_source() ==
140 ::onc::ONC_SOURCE_DEVICE_POLICY ||
141 ui_data->onc_source() == ::onc::ONC_SOURCE_USER_POLICY);
143 const base::DictionaryValue* new_policy = NULL;
144 if (was_managed) {
145 // If we have a GUID that might match a current policy, do a lookup using
146 // that GUID at first. In particular this is necessary, as some networks
147 // can't be matched to policies by properties (e.g. VPN).
148 new_policy = GetByGUID(all_policies_, old_guid);
151 if (!new_policy) {
152 // If we didn't find a policy by GUID, still a new policy might match.
153 new_policy = policy_util::FindMatchingPolicy(all_policies_, *onc_part);
156 if (new_policy) {
157 std::string new_guid;
158 new_policy->GetStringWithoutPathExpansion(::onc::network_config::kGUID,
159 &new_guid);
161 VLOG_IF(1, was_managed && old_guid != new_guid)
162 << "Updating configuration previously managed by policy " << old_guid
163 << " with new policy " << new_guid << ".";
164 VLOG_IF(1, !was_managed) << "Applying policy " << new_guid
165 << " to previously unmanaged "
166 << "configuration.";
168 if (old_guid == new_guid &&
169 remaining_policies_.find(new_guid) == remaining_policies_.end()) {
170 VLOG(1) << "Not updating existing managed configuration with guid "
171 << new_guid << " because the policy didn't change.";
172 } else {
173 const base::DictionaryValue* user_settings =
174 ui_data ? ui_data->user_settings() : NULL;
175 scoped_ptr<base::DictionaryValue> new_shill_properties =
176 policy_util::CreateShillConfiguration(profile_,
177 new_guid,
178 &global_network_config_,
179 new_policy,
180 user_settings);
181 // A new policy has to be applied to this profile entry. In order to keep
182 // implicit state of Shill like "connected successfully before", keep the
183 // entry if a policy is reapplied (e.g. after reboot) or is updated.
184 // However, some Shill properties are used to identify the network and
185 // cannot be modified after initial configuration, so we have to delete
186 // the profile entry in these cases. Also, keeping Shill's state if the
187 // SSID changed might not be a good idea anyways. If the policy GUID
188 // changed, or there was no policy before, we delete the entry at first to
189 // ensure that no old configuration remains.
190 if (old_guid == new_guid &&
191 shill_property_util::DoIdentifyingPropertiesMatch(
192 *new_shill_properties, entry_properties)) {
193 VLOG(1) << "Updating previously managed configuration with the "
194 << "updated policy " << new_guid << ".";
195 } else {
196 VLOG(1) << "Deleting profile entry before writing new policy "
197 << new_guid << " because of identifying properties changed.";
198 DeleteEntry(entry);
201 // In general, old entries should at first be deleted before new
202 // configurations are written to prevent inconsistencies. Therefore, we
203 // delay the writing of the new config here until ~PolicyApplicator.
204 // E.g. one problematic case is if a policy { {GUID=X, SSID=Y} } is
205 // applied to the profile entries
206 // { ENTRY1 = {GUID=X, SSID=X, USER_SETTINGS=X},
207 // ENTRY2 = {SSID=Y, ... } }.
208 // At first ENTRY1 and ENTRY2 should be removed, then the new config be
209 // written and the result should be:
210 // { {GUID=X, SSID=Y, USER_SETTINGS=X} }
211 WriteNewShillConfiguration(
212 *new_shill_properties, *new_policy, true /* write later */);
213 remaining_policies_.erase(new_guid);
215 } else if (was_managed) {
216 VLOG(1) << "Removing configuration previously managed by policy "
217 << old_guid << ", because the policy was removed.";
219 // Remove the entry, because the network was managed but isn't anymore.
220 // Note: An alternative might be to preserve the user settings, but it's
221 // unclear which values originating the policy should be removed.
222 DeleteEntry(entry);
223 } else {
224 // The entry wasn't managed and doesn't match any current policy. Global
225 // network settings have to be applied.
226 base::DictionaryValue shill_properties_to_update;
227 policy_util::SetShillPropertiesForGlobalPolicy(
228 entry_properties, global_network_config_, &shill_properties_to_update);
229 if (shill_properties_to_update.empty()) {
230 VLOG(2) << "Ignore unmanaged entry.";
231 // Calling a SetProperties of Shill with an empty dictionary is a no op.
232 } else {
233 VLOG(2) << "Apply global network config to unmanaged entry.";
234 handler_->UpdateExistingConfigurationWithPropertiesFromPolicy(
235 entry_properties, shill_properties_to_update);
240 void PolicyApplicator::DeleteEntry(const std::string& entry) {
241 DBusThreadManager::Get()->GetShillProfileClient()->DeleteEntry(
242 dbus::ObjectPath(profile_.path),
243 entry,
244 base::Bind(&base::DoNothing),
245 base::Bind(&LogErrorMessage, FROM_HERE));
248 void PolicyApplicator::WriteNewShillConfiguration(
249 const base::DictionaryValue& shill_dictionary,
250 const base::DictionaryValue& policy,
251 bool write_later) {
252 // Ethernet (non EAP) settings, like GUID or UIData, cannot be stored per
253 // user. Abort in that case.
254 std::string type;
255 policy.GetStringWithoutPathExpansion(::onc::network_config::kType, &type);
256 if (type == ::onc::network_type::kEthernet &&
257 profile_.type() == NetworkProfile::TYPE_USER) {
258 const base::DictionaryValue* ethernet = NULL;
259 policy.GetDictionaryWithoutPathExpansion(::onc::network_config::kEthernet,
260 &ethernet);
261 std::string auth;
262 ethernet->GetStringWithoutPathExpansion(::onc::ethernet::kAuthentication,
263 &auth);
264 if (auth == ::onc::ethernet::kAuthenticationNone)
265 return;
268 if (write_later)
269 new_shill_configurations_.push_back(shill_dictionary.DeepCopy());
270 else
271 handler_->CreateConfigurationFromPolicy(shill_dictionary);
274 PolicyApplicator::~PolicyApplicator() {
275 ApplyRemainingPolicies();
276 STLDeleteValues(&all_policies_);
277 // Notify the handler about all policies being applied, so that the network
278 // lists can be updated.
279 if (handler_)
280 handler_->OnPoliciesApplied();
283 void PolicyApplicator::ApplyRemainingPolicies() {
284 if (!handler_) {
285 LOG(WARNING) << "Handler destructed during policy application to profile "
286 << profile_.ToDebugString();
287 return;
290 // Write all queued configurations now.
291 for (ScopedVector<base::DictionaryValue>::const_iterator it =
292 new_shill_configurations_.begin();
293 it != new_shill_configurations_.end();
294 ++it) {
295 handler_->CreateConfigurationFromPolicy(**it);
298 if (remaining_policies_.empty())
299 return;
301 VLOG(2) << "Create new managed network configurations in profile"
302 << profile_.ToDebugString() << ".";
303 // All profile entries were compared to policies. |remaining_policies_|
304 // contains all modified policies that didn't match any entry. For these
305 // remaining policies, new configurations have to be created.
306 for (std::set<std::string>::iterator it = remaining_policies_.begin();
307 it != remaining_policies_.end(); ++it) {
308 const base::DictionaryValue* network_policy = GetByGUID(all_policies_, *it);
309 DCHECK(network_policy);
311 VLOG(1) << "Creating new configuration managed by policy " << *it
312 << " in profile " << profile_.ToDebugString() << ".";
314 scoped_ptr<base::DictionaryValue> shill_dictionary =
315 policy_util::CreateShillConfiguration(profile_,
316 *it,
317 &global_network_config_,
318 network_policy,
319 NULL /* no user settings */);
320 WriteNewShillConfiguration(
321 *shill_dictionary, *network_policy, false /* write now */);
325 } // namespace chromeos