Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chromeos / network / policy_applicator.cc
bloba5dd9fff32afff78288880cfc7345107343020e2
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 "chromeos/dbus/dbus_thread_manager.h"
15 #include "chromeos/dbus/shill_profile_client.h"
16 #include "chromeos/network/network_type_pattern.h"
17 #include "chromeos/network/network_ui_data.h"
18 #include "chromeos/network/onc/onc_signature.h"
19 #include "chromeos/network/onc/onc_translator.h"
20 #include "chromeos/network/policy_util.h"
21 #include "chromeos/network/shill_property_util.h"
22 #include "components/onc/onc_constants.h"
23 #include "dbus/object_path.h"
24 #include "third_party/cros_system_api/dbus/service_constants.h"
26 namespace chromeos {
28 namespace {
30 void LogErrorMessage(const tracked_objects::Location& from_where,
31 const std::string& error_name,
32 const std::string& error_message) {
33 LOG(ERROR) << from_where.ToString() << ": " << error_message;
36 const base::DictionaryValue* GetByGUID(
37 const PolicyApplicator::GuidToPolicyMap& policies,
38 const std::string& guid) {
39 PolicyApplicator::GuidToPolicyMap::const_iterator it = policies.find(guid);
40 if (it == policies.end())
41 return NULL;
42 return it->second;
45 } // namespace
47 PolicyApplicator::PolicyApplicator(
48 const NetworkProfile& profile,
49 const GuidToPolicyMap& all_policies,
50 const base::DictionaryValue& global_network_config,
51 ConfigurationHandler* handler,
52 std::set<std::string>* modified_policies)
53 : handler_(handler), profile_(profile), weak_ptr_factory_(this) {
54 global_network_config_.MergeDictionary(&global_network_config);
55 remaining_policies_.swap(*modified_policies);
56 for (GuidToPolicyMap::const_iterator it = all_policies.begin();
57 it != all_policies.end(); ++it) {
58 all_policies_.insert(std::make_pair(it->first, it->second->DeepCopy()));
62 PolicyApplicator::~PolicyApplicator() {
63 STLDeleteValues(&all_policies_);
64 VLOG(1) << "Destroying PolicyApplicator for " << profile_.userhash;
67 void PolicyApplicator::Run() {
68 DBusThreadManager::Get()->GetShillProfileClient()->GetProperties(
69 dbus::ObjectPath(profile_.path),
70 base::Bind(&PolicyApplicator::GetProfilePropertiesCallback,
71 weak_ptr_factory_.GetWeakPtr()),
72 base::Bind(&PolicyApplicator::GetProfilePropertiesError,
73 weak_ptr_factory_.GetWeakPtr()));
76 void PolicyApplicator::ProfileEntryFinished(const std::string& entry) {
77 pending_get_entry_calls_.erase(entry);
78 if (pending_get_entry_calls_.empty()) {
79 ApplyRemainingPolicies();
80 NotifyConfigurationHandlerAndFinish();
84 void PolicyApplicator::GetProfilePropertiesCallback(
85 const base::DictionaryValue& profile_properties) {
86 VLOG(2) << "Received properties for profile " << profile_.ToDebugString();
87 const base::ListValue* entries = NULL;
88 if (!profile_properties.GetListWithoutPathExpansion(
89 shill::kEntriesProperty, &entries)) {
90 LOG(ERROR) << "Profile " << profile_.ToDebugString()
91 << " doesn't contain the property "
92 << shill::kEntriesProperty;
93 NotifyConfigurationHandlerAndFinish();
94 return;
97 for (base::ListValue::const_iterator it = entries->begin();
98 it != entries->end(); ++it) {
99 std::string entry;
100 (*it)->GetAsString(&entry);
102 pending_get_entry_calls_.insert(entry);
103 DBusThreadManager::Get()->GetShillProfileClient()->GetEntry(
104 dbus::ObjectPath(profile_.path),
105 entry,
106 base::Bind(&PolicyApplicator::GetEntryCallback,
107 weak_ptr_factory_.GetWeakPtr(),
108 entry),
109 base::Bind(&PolicyApplicator::GetEntryError,
110 weak_ptr_factory_.GetWeakPtr(),
111 entry));
113 if (pending_get_entry_calls_.empty()) {
114 ApplyRemainingPolicies();
115 NotifyConfigurationHandlerAndFinish();
119 void PolicyApplicator::GetProfilePropertiesError(
120 const std::string& error_name,
121 const std::string& error_message) {
122 LOG(ERROR) << "Could not retrieve properties of profile " << profile_.path
123 << ": " << error_message;
124 NotifyConfigurationHandlerAndFinish();
127 void PolicyApplicator::GetEntryCallback(
128 const std::string& entry,
129 const base::DictionaryValue& entry_properties) {
130 VLOG(2) << "Received properties for entry " << entry << " of profile "
131 << profile_.ToDebugString();
133 scoped_ptr<base::DictionaryValue> onc_part(
134 onc::TranslateShillServiceToONCPart(
135 entry_properties, ::onc::ONC_SOURCE_UNKNOWN,
136 &onc::kNetworkWithStateSignature, nullptr /* network_state */));
138 std::string old_guid;
139 if (!onc_part->GetStringWithoutPathExpansion(::onc::network_config::kGUID,
140 &old_guid)) {
141 VLOG(1) << "Entry " << entry << " of profile " << profile_.ToDebugString()
142 << " doesn't contain a GUID.";
143 // This might be an entry of an older ChromeOS version. Assume it to be
144 // unmanaged.
147 scoped_ptr<NetworkUIData> ui_data =
148 shill_property_util::GetUIDataFromProperties(entry_properties);
149 if (!ui_data) {
150 VLOG(1) << "Entry " << entry << " of profile " << profile_.ToDebugString()
151 << " contains no or no valid UIData.";
152 // This might be an entry of an older ChromeOS version. Assume it to be
153 // unmanaged. It's an inconsistency if there is a GUID but no UIData, thus
154 // clear the GUID just in case.
155 old_guid.clear();
158 bool was_managed = !old_guid.empty() && ui_data &&
159 (ui_data->onc_source() ==
160 ::onc::ONC_SOURCE_DEVICE_POLICY ||
161 ui_data->onc_source() == ::onc::ONC_SOURCE_USER_POLICY);
163 const base::DictionaryValue* new_policy = NULL;
164 if (was_managed) {
165 // If we have a GUID that might match a current policy, do a lookup using
166 // that GUID at first. In particular this is necessary, as some networks
167 // can't be matched to policies by properties (e.g. VPN).
168 new_policy = GetByGUID(all_policies_, old_guid);
171 if (!new_policy) {
172 // If we didn't find a policy by GUID, still a new policy might match.
173 new_policy = policy_util::FindMatchingPolicy(all_policies_, *onc_part);
176 if (new_policy) {
177 std::string new_guid;
178 new_policy->GetStringWithoutPathExpansion(::onc::network_config::kGUID,
179 &new_guid);
181 VLOG_IF(1, was_managed && old_guid != new_guid)
182 << "Updating configuration previously managed by policy " << old_guid
183 << " with new policy " << new_guid << ".";
184 VLOG_IF(1, !was_managed) << "Applying policy " << new_guid
185 << " to previously unmanaged "
186 << "configuration.";
188 if (old_guid == new_guid &&
189 remaining_policies_.find(new_guid) == remaining_policies_.end()) {
190 VLOG(1) << "Not updating existing managed configuration with guid "
191 << new_guid << " because the policy didn't change.";
192 } else {
193 const base::DictionaryValue* user_settings =
194 ui_data ? ui_data->user_settings() : NULL;
195 scoped_ptr<base::DictionaryValue> new_shill_properties =
196 policy_util::CreateShillConfiguration(profile_,
197 new_guid,
198 &global_network_config_,
199 new_policy,
200 user_settings);
201 // A new policy has to be applied to this profile entry. In order to keep
202 // implicit state of Shill like "connected successfully before", keep the
203 // entry if a policy is reapplied (e.g. after reboot) or is updated.
204 // However, some Shill properties are used to identify the network and
205 // cannot be modified after initial configuration, so we have to delete
206 // the profile entry in these cases. Also, keeping Shill's state if the
207 // SSID changed might not be a good idea anyways. If the policy GUID
208 // changed, or there was no policy before, we delete the entry at first to
209 // ensure that no old configuration remains.
210 if (old_guid == new_guid &&
211 shill_property_util::DoIdentifyingPropertiesMatch(
212 *new_shill_properties, entry_properties)) {
213 VLOG(1) << "Updating previously managed configuration with the "
214 << "updated policy " << new_guid << ".";
215 } else {
216 VLOG(1) << "Deleting profile entry before writing new policy "
217 << new_guid << " because of identifying properties changed.";
218 DeleteEntry(entry);
221 // In general, old entries should at first be deleted before new
222 // configurations are written to prevent inconsistencies. Therefore, we
223 // delay the writing of the new config here until ~PolicyApplicator.
224 // E.g. one problematic case is if a policy { {GUID=X, SSID=Y} } is
225 // applied to the profile entries
226 // { ENTRY1 = {GUID=X, SSID=X, USER_SETTINGS=X},
227 // ENTRY2 = {SSID=Y, ... } }.
228 // At first ENTRY1 and ENTRY2 should be removed, then the new config be
229 // written and the result should be:
230 // { {GUID=X, SSID=Y, USER_SETTINGS=X} }
231 WriteNewShillConfiguration(
232 *new_shill_properties, *new_policy, true /* write later */);
233 remaining_policies_.erase(new_guid);
235 } else if (was_managed) {
236 VLOG(1) << "Removing configuration previously managed by policy "
237 << old_guid << ", because the policy was removed.";
239 // Remove the entry, because the network was managed but isn't anymore.
240 // Note: An alternative might be to preserve the user settings, but it's
241 // unclear which values originating the policy should be removed.
242 DeleteEntry(entry);
243 } else {
244 // The entry wasn't managed and doesn't match any current policy. Global
245 // network settings have to be applied.
246 base::DictionaryValue shill_properties_to_update;
247 policy_util::SetShillPropertiesForGlobalPolicy(
248 entry_properties, global_network_config_, &shill_properties_to_update);
249 if (shill_properties_to_update.empty()) {
250 VLOG(2) << "Ignore unmanaged entry.";
251 // Calling a SetProperties of Shill with an empty dictionary is a no op.
252 } else {
253 VLOG(2) << "Apply global network config to unmanaged entry.";
254 handler_->UpdateExistingConfigurationWithPropertiesFromPolicy(
255 entry_properties, shill_properties_to_update);
259 ProfileEntryFinished(entry);
262 void PolicyApplicator::GetEntryError(const std::string& entry,
263 const std::string& error_name,
264 const std::string& error_message) {
265 LOG(ERROR) << "Could not retrieve entry " << entry << " of profile "
266 << profile_.path << ": " << error_message;
267 ProfileEntryFinished(entry);
270 void PolicyApplicator::DeleteEntry(const std::string& entry) {
271 DBusThreadManager::Get()->GetShillProfileClient()->DeleteEntry(
272 dbus::ObjectPath(profile_.path),
273 entry,
274 base::Bind(&base::DoNothing),
275 base::Bind(&LogErrorMessage, FROM_HERE));
278 void PolicyApplicator::WriteNewShillConfiguration(
279 const base::DictionaryValue& shill_dictionary,
280 const base::DictionaryValue& policy,
281 bool write_later) {
282 // Ethernet (non EAP) settings, like GUID or UIData, cannot be stored per
283 // user. Abort in that case.
284 std::string type;
285 policy.GetStringWithoutPathExpansion(::onc::network_config::kType, &type);
286 if (type == ::onc::network_type::kEthernet &&
287 profile_.type() == NetworkProfile::TYPE_USER) {
288 const base::DictionaryValue* ethernet = NULL;
289 policy.GetDictionaryWithoutPathExpansion(::onc::network_config::kEthernet,
290 &ethernet);
291 std::string auth;
292 ethernet->GetStringWithoutPathExpansion(::onc::ethernet::kAuthentication,
293 &auth);
294 if (auth == ::onc::ethernet::kAuthenticationNone)
295 return;
298 if (write_later)
299 new_shill_configurations_.push_back(shill_dictionary.DeepCopy());
300 else
301 handler_->CreateConfigurationFromPolicy(shill_dictionary);
304 void PolicyApplicator::ApplyRemainingPolicies() {
305 DCHECK(pending_get_entry_calls_.empty());
307 // Write all queued configurations now.
308 for (ScopedVector<base::DictionaryValue>::const_iterator it =
309 new_shill_configurations_.begin();
310 it != new_shill_configurations_.end();
311 ++it) {
312 handler_->CreateConfigurationFromPolicy(**it);
314 new_shill_configurations_.clear();
316 VLOG_IF(2, !remaining_policies_.empty())
317 << "Create new managed network configurations in profile"
318 << profile_.ToDebugString() << ".";
320 // All profile entries were compared to policies. |remaining_policies_|
321 // contains all modified policies that didn't match any entry. For these
322 // remaining policies, new configurations have to be created.
323 for (std::set<std::string>::iterator it = remaining_policies_.begin();
324 it != remaining_policies_.end(); ++it) {
325 const base::DictionaryValue* network_policy = GetByGUID(all_policies_, *it);
326 DCHECK(network_policy);
328 VLOG(1) << "Creating new configuration managed by policy " << *it
329 << " in profile " << profile_.ToDebugString() << ".";
331 scoped_ptr<base::DictionaryValue> shill_dictionary =
332 policy_util::CreateShillConfiguration(profile_,
333 *it,
334 &global_network_config_,
335 network_policy,
336 NULL /* no user settings */);
337 WriteNewShillConfiguration(
338 *shill_dictionary, *network_policy, false /* write now */);
340 remaining_policies_.clear();
343 void PolicyApplicator::NotifyConfigurationHandlerAndFinish() {
344 weak_ptr_factory_.InvalidateWeakPtrs();
345 handler_->OnPoliciesApplied(profile_);
348 } // namespace chromeos