Remove ViewMsg_UpdateRect_ACK
[chromium-blink-merge.git] / chromeos / network / policy_applicator.cc
blob02001b54da61a195f539ac23eefd105d4aef4444
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_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 base::WeakPtr<ConfigurationHandler> handler,
49 const NetworkProfile& profile,
50 const GuidToPolicyMap& all_policies,
51 const base::DictionaryValue& global_network_config,
52 std::set<std::string>* modified_policies)
53 : handler_(handler), profile_(profile) {
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 void PolicyApplicator::Run() {
63 DBusThreadManager::Get()->GetShillProfileClient()->GetProperties(
64 dbus::ObjectPath(profile_.path),
65 base::Bind(&PolicyApplicator::GetProfilePropertiesCallback, this),
66 base::Bind(&LogErrorMessage, FROM_HERE));
69 void PolicyApplicator::GetProfilePropertiesCallback(
70 const base::DictionaryValue& profile_properties) {
71 if (!handler_) {
72 LOG(WARNING) << "Handler destructed during policy application to profile "
73 << profile_.ToDebugString();
74 return;
77 VLOG(2) << "Received properties for profile " << profile_.ToDebugString();
78 const base::ListValue* entries = NULL;
79 if (!profile_properties.GetListWithoutPathExpansion(
80 shill::kEntriesProperty, &entries)) {
81 LOG(ERROR) << "Profile " << profile_.ToDebugString()
82 << " doesn't contain the property "
83 << shill::kEntriesProperty;
84 return;
87 for (base::ListValue::const_iterator it = entries->begin();
88 it != entries->end(); ++it) {
89 std::string entry;
90 (*it)->GetAsString(&entry);
92 DBusThreadManager::Get()->GetShillProfileClient()->GetEntry(
93 dbus::ObjectPath(profile_.path),
94 entry,
95 base::Bind(&PolicyApplicator::GetEntryCallback, this, entry),
96 base::Bind(&LogErrorMessage, FROM_HERE));
100 void PolicyApplicator::GetEntryCallback(
101 const std::string& entry,
102 const base::DictionaryValue& entry_properties) {
103 if (!handler_) {
104 LOG(WARNING) << "Handler destructed during policy application to profile "
105 << profile_.ToDebugString();
106 return;
109 VLOG(2) << "Received properties for entry " << entry << " of profile "
110 << profile_.ToDebugString();
112 scoped_ptr<base::DictionaryValue> onc_part(
113 onc::TranslateShillServiceToONCPart(entry_properties,
114 &onc::kNetworkWithStateSignature));
116 std::string old_guid;
117 if (!onc_part->GetStringWithoutPathExpansion(::onc::network_config::kGUID,
118 &old_guid)) {
119 VLOG(1) << "Entry " << entry << " of profile " << profile_.ToDebugString()
120 << " doesn't contain a GUID.";
121 // This might be an entry of an older ChromeOS version. Assume it to be
122 // unmanaged.
125 scoped_ptr<NetworkUIData> ui_data =
126 shill_property_util::GetUIDataFromProperties(entry_properties);
127 if (!ui_data) {
128 VLOG(1) << "Entry " << entry << " of profile " << profile_.ToDebugString()
129 << " contains no or no valid UIData.";
130 // This might be an entry of an older ChromeOS version. Assume it to be
131 // unmanaged. It's an inconsistency if there is a GUID but no UIData, thus
132 // clear the GUID just in case.
133 old_guid.clear();
136 bool was_managed = !old_guid.empty() && ui_data &&
137 (ui_data->onc_source() ==
138 ::onc::ONC_SOURCE_DEVICE_POLICY ||
139 ui_data->onc_source() == ::onc::ONC_SOURCE_USER_POLICY);
141 const base::DictionaryValue* new_policy = NULL;
142 if (was_managed) {
143 // If we have a GUID that might match a current policy, do a lookup using
144 // that GUID at first. In particular this is necessary, as some networks
145 // can't be matched to policies by properties (e.g. VPN).
146 new_policy = GetByGUID(all_policies_, old_guid);
149 if (!new_policy) {
150 // If we didn't find a policy by GUID, still a new policy might match.
151 new_policy = policy_util::FindMatchingPolicy(all_policies_, *onc_part);
154 if (new_policy) {
155 std::string new_guid;
156 new_policy->GetStringWithoutPathExpansion(::onc::network_config::kGUID,
157 &new_guid);
159 VLOG_IF(1, was_managed && old_guid != new_guid)
160 << "Updating configuration previously managed by policy " << old_guid
161 << " with new policy " << new_guid << ".";
162 VLOG_IF(1, !was_managed) << "Applying policy " << new_guid
163 << " to previously unmanaged "
164 << "configuration.";
166 if (old_guid == new_guid &&
167 remaining_policies_.find(new_guid) == remaining_policies_.end()) {
168 VLOG(1) << "Not updating existing managed configuration with guid "
169 << new_guid << " because the policy didn't change.";
170 } else {
171 const base::DictionaryValue* user_settings =
172 ui_data ? ui_data->user_settings() : NULL;
173 scoped_ptr<base::DictionaryValue> new_shill_properties =
174 policy_util::CreateShillConfiguration(
175 profile_, new_guid, new_policy, user_settings);
176 // A new policy has to be applied to this profile entry. In order to keep
177 // implicit state of Shill like "connected successfully before", keep the
178 // entry if a policy is reapplied (e.g. after reboot) or is updated.
179 // However, some Shill properties are used to identify the network and
180 // cannot be modified after initial configuration, so we have to delete
181 // the profile entry in these cases. Also, keeping Shill's state if the
182 // SSID changed might not be a good idea anyways. If the policy GUID
183 // changed, or there was no policy before, we delete the entry at first to
184 // ensure that no old configuration remains.
185 if (old_guid == new_guid &&
186 shill_property_util::DoIdentifyingPropertiesMatch(
187 *new_shill_properties, entry_properties)) {
188 VLOG(1) << "Updating previously managed configuration with the "
189 << "updated policy " << new_guid << ".";
190 } else {
191 VLOG(1) << "Deleting profile entry before writing new policy "
192 << new_guid << " because of identifying properties changed.";
193 DeleteEntry(entry);
196 // In general, old entries should at first be deleted before new
197 // configurations are written to prevent inconsistencies. Therefore, we
198 // delay the writing of the new config here until ~PolicyApplicator.
199 // E.g. one problematic case is if a policy { {GUID=X, SSID=Y} } is
200 // applied to the profile entries
201 // { ENTRY1 = {GUID=X, SSID=X, USER_SETTINGS=X},
202 // ENTRY2 = {SSID=Y, ... } }.
203 // At first ENTRY1 and ENTRY2 should be removed, then the new config be
204 // written and the result should be:
205 // { {GUID=X, SSID=Y, USER_SETTINGS=X} }
206 WriteNewShillConfiguration(*new_shill_properties, *new_policy, true);
207 remaining_policies_.erase(new_guid);
209 } else if (was_managed) {
210 VLOG(1) << "Removing configuration previously managed by policy "
211 << old_guid << ", because the policy was removed.";
213 // Remove the entry, because the network was managed but isn't anymore.
214 // Note: An alternative might be to preserve the user settings, but it's
215 // unclear which values originating the policy should be removed.
216 DeleteEntry(entry);
217 } else {
218 // The entry wasn't managed and doesn't match any current policy. Global
219 // network settings have to be applied.
220 base::DictionaryValue shill_properties_to_update;
221 GetPropertiesForUnmanagedEntry(entry_properties,
222 &shill_properties_to_update);
223 if (shill_properties_to_update.empty()) {
224 VLOG(2) << "Ignore unmanaged entry.";
225 // Calling a SetProperties of Shill with an empty dictionary is a no op.
226 } else {
227 VLOG(2) << "Apply global network config to unmanaged entry.";
228 handler_->UpdateExistingConfigurationWithPropertiesFromPolicy(
229 entry_properties, shill_properties_to_update);
234 void PolicyApplicator::DeleteEntry(const std::string& entry) {
235 DBusThreadManager::Get()->GetShillProfileClient()->DeleteEntry(
236 dbus::ObjectPath(profile_.path),
237 entry,
238 base::Bind(&base::DoNothing),
239 base::Bind(&LogErrorMessage, FROM_HERE));
242 void PolicyApplicator::WriteNewShillConfiguration(
243 const base::DictionaryValue& shill_dictionary,
244 const base::DictionaryValue& policy,
245 bool write_later) {
246 // Ethernet (non EAP) settings, like GUID or UIData, cannot be stored per
247 // user. Abort in that case.
248 std::string type;
249 policy.GetStringWithoutPathExpansion(::onc::network_config::kType, &type);
250 if (type == ::onc::network_type::kEthernet &&
251 profile_.type() == NetworkProfile::TYPE_USER) {
252 const base::DictionaryValue* ethernet = NULL;
253 policy.GetDictionaryWithoutPathExpansion(::onc::network_config::kEthernet,
254 &ethernet);
255 std::string auth;
256 ethernet->GetStringWithoutPathExpansion(::onc::ethernet::kAuthentication,
257 &auth);
258 if (auth == ::onc::ethernet::kNone)
259 return;
262 if (write_later)
263 new_shill_configurations_.push_back(shill_dictionary.DeepCopy());
264 else
265 handler_->CreateConfigurationFromPolicy(shill_dictionary);
268 void PolicyApplicator::GetPropertiesForUnmanagedEntry(
269 const base::DictionaryValue& entry_properties,
270 base::DictionaryValue* properties_to_update) const {
271 // kAllowOnlyPolicyNetworksToAutoconnect is currently the only global config.
273 std::string type;
274 entry_properties.GetStringWithoutPathExpansion(shill::kTypeProperty, &type);
275 if (NetworkTypePattern::Ethernet().MatchesType(type))
276 return; // Autoconnect for Ethernet cannot be configured.
278 // By default all networks are allowed to autoconnect.
279 bool only_policy_autoconnect = false;
280 global_network_config_.GetBooleanWithoutPathExpansion(
281 ::onc::global_network_config::kAllowOnlyPolicyNetworksToAutoconnect,
282 &only_policy_autoconnect);
283 if (!only_policy_autoconnect)
284 return;
286 bool old_autoconnect = false;
287 if (entry_properties.GetBooleanWithoutPathExpansion(
288 shill::kAutoConnectProperty, &old_autoconnect) &&
289 !old_autoconnect) {
290 // Autoconnect is already explictly disabled. No need to set it again.
291 return;
293 // If autconnect is not explicitly set yet, it might automatically be enabled
294 // by Shill. To prevent that, disable it explicitly.
295 properties_to_update->SetBooleanWithoutPathExpansion(
296 shill::kAutoConnectProperty, false);
299 PolicyApplicator::~PolicyApplicator() {
300 ApplyRemainingPolicies();
301 STLDeleteValues(&all_policies_);
302 // Notify the handler about all policies being applied, so that the network
303 // lists can be updated.
304 if (handler_)
305 handler_->OnPoliciesApplied();
308 void PolicyApplicator::ApplyRemainingPolicies() {
309 if (!handler_) {
310 LOG(WARNING) << "Handler destructed during policy application to profile "
311 << profile_.ToDebugString();
312 return;
315 // Write all queued configurations now.
316 for (ScopedVector<base::DictionaryValue>::const_iterator it =
317 new_shill_configurations_.begin();
318 it != new_shill_configurations_.end();
319 ++it) {
320 handler_->CreateConfigurationFromPolicy(**it);
323 if (remaining_policies_.empty())
324 return;
326 VLOG(2) << "Create new managed network configurations in profile"
327 << profile_.ToDebugString() << ".";
328 // All profile entries were compared to policies. |remaining_policies_|
329 // contains all modified policies that didn't match any entry. For these
330 // remaining policies, new configurations have to be created.
331 for (std::set<std::string>::iterator it = remaining_policies_.begin();
332 it != remaining_policies_.end(); ++it) {
333 const base::DictionaryValue* policy = GetByGUID(all_policies_, *it);
334 DCHECK(policy);
336 VLOG(1) << "Creating new configuration managed by policy " << *it
337 << " in profile " << profile_.ToDebugString() << ".";
339 scoped_ptr<base::DictionaryValue> shill_dictionary =
340 policy_util::CreateShillConfiguration(profile_, *it, policy, NULL);
341 WriteNewShillConfiguration(*shill_dictionary, *policy, false);
345 } // namespace chromeos