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