policy_applicator.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
14e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
24e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
34e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// found in the LICENSE file.
44e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
54e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "chromeos/network/policy_applicator.h"
64e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
74e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include <utility>
84e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
94e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "base/bind.h"
104e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "base/location.h"
114e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "base/logging.h"
12f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "base/memory/scoped_ptr.h"
134e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "base/stl_util.h"
144e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "base/values.h"
154e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "chromeos/dbus/dbus_thread_manager.h"
164e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "chromeos/dbus/shill_profile_client.h"
174e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "chromeos/network/network_ui_data.h"
184e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "chromeos/network/onc/onc_signature.h"
194e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "chromeos/network/onc/onc_translator.h"
204e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "chromeos/network/policy_util.h"
211320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "chromeos/network/shill_property_util.h"
221320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "components/onc/onc_constants.h"
231320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "dbus/object_path.h"
24f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "third_party/cros_system_api/dbus/service_constants.h"
25f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
2623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)namespace chromeos {
2723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
2823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)namespace {
29f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivoid LogErrorMessage(const tracked_objects::Location& from_where,
314e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                     const std::string& error_name,
32f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                     const std::string& error_message) {
33f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  LOG(ERROR) << from_where.ToString() << ": " << error_message;
34f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
35f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
36f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)const base::DictionaryValue* GetByGUID(
37f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    const PolicyApplicator::GuidToPolicyMap& policies,
38f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    const std::string& guid) {
394e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  PolicyApplicator::GuidToPolicyMap::const_iterator it = policies.find(guid);
404e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (it == policies.end())
411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return NULL;
424e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  return it->second;
4323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)}
44f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
45f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}  // namespace
464e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
474e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)PolicyApplicator::PolicyApplicator(
484e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    base::WeakPtr<ConfigurationHandler> handler,
494e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    const NetworkProfile& profile,
504e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    const GuidToPolicyMap& all_policies,
514e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    const base::DictionaryValue& global_network_config,
524e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    std::set<std::string>* modified_policies)
534e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    : handler_(handler), profile_(profile) {
544e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  global_network_config_.MergeDictionary(&global_network_config);
554e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  remaining_policies_.swap(*modified_policies);
56cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  for (GuidToPolicyMap::const_iterator it = all_policies.begin();
578bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)       it != all_policies.end(); ++it) {
58cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    all_policies_.insert(std::make_pair(it->first, it->second->DeepCopy()));
594e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  }
604e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
614e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
624e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)void PolicyApplicator::Run() {
63cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  DBusThreadManager::Get()->GetShillProfileClient()->GetProperties(
648bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      dbus::ObjectPath(profile_.path),
65cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      base::Bind(&PolicyApplicator::GetProfilePropertiesCallback, this),
664e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      base::Bind(&LogErrorMessage, FROM_HERE));
674e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
684e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
694e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)void PolicyApplicator::GetProfilePropertiesCallback(
704e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    const base::DictionaryValue& profile_properties) {
71cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (!handler_) {
724e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    LOG(WARNING) << "Handler destructed during policy application to profile "
738bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)                 << profile_.ToDebugString();
744e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    return;
75cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
764e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
774e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  VLOG(2) << "Received properties for profile " << profile_.ToDebugString();
784e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  const base::ListValue* entries = NULL;
794e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (!profile_properties.GetListWithoutPathExpansion(
804e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)           shill::kEntriesProperty, &entries)) {
814e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    LOG(ERROR) << "Profile " << profile_.ToDebugString()
824e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)               << " doesn't contain the property "
834e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)               << shill::kEntriesProperty;
844e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    return;
854e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  }
86cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
874e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  for (base::ListValue::const_iterator it = entries->begin();
888bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)       it != entries->end(); ++it) {
894e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    std::string entry;
90cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    (*it)->GetAsString(&entry);
91cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
924e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    DBusThreadManager::Get()->GetShillProfileClient()->GetEntry(
938bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)        dbus::ObjectPath(profile_.path),
944e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        entry,
95cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        base::Bind(&PolicyApplicator::GetEntryCallback, this, entry),
964e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        base::Bind(&LogErrorMessage, FROM_HERE));
974e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  }
984e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
994e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
1004e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)void PolicyApplicator::GetEntryCallback(
1014e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    const std::string& entry,
1024e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    const base::DictionaryValue& entry_properties) {
103cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (!handler_) {
1044e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    LOG(WARNING) << "Handler destructed during policy application to profile "
1058bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)                 << profile_.ToDebugString();
1064e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    return;
107cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
108cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
1094e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  VLOG(2) << "Received properties for entry " << entry << " of profile "
1108bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)          << profile_.ToDebugString();
1114e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
112cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  scoped_ptr<base::DictionaryValue> onc_part(
1134e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      onc::TranslateShillServiceToONCPart(entry_properties,
1144e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                                          &onc::kNetworkWithStateSignature));
1154e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
1164e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  std::string old_guid;
1174e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (!onc_part->GetStringWithoutPathExpansion(::onc::network_config::kGUID,
1184e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                                               &old_guid)) {
1194e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    VLOG(1) << "Entry " << entry << " of profile " << profile_.ToDebugString()
1204e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)            << " doesn't contain a GUID.";
121f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    // This might be an entry of an older ChromeOS version. Assume it to be
122f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    // unmanaged.
123f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  }
124f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
125f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  scoped_ptr<NetworkUIData> ui_data =
126f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      shill_property_util::GetUIDataFromProperties(entry_properties);
127cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (!ui_data) {
128f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    VLOG(1) << "Entry " << entry << " of profile " << profile_.ToDebugString()
129f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)            << " contains no or no valid UIData.";
130f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    // This might be an entry of an older ChromeOS version. Assume it to be
131cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    // unmanaged. It's an inconsistency if there is a GUID but no UIData, thus
132f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    // clear the GUID just in case.
133f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    old_guid.clear();
134f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  }
135f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
136f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  bool was_managed = !old_guid.empty() && ui_data &&
137f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                     (ui_data->onc_source() ==
138f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                          ::onc::ONC_SOURCE_DEVICE_POLICY ||
139f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                      ui_data->onc_source() == ::onc::ONC_SOURCE_USER_POLICY);
140f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
141f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  const base::DictionaryValue* new_policy = NULL;
142f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (was_managed) {
143f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    // If we have a GUID that might match a current policy, do a lookup using
144f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    // that GUID at first. In particular this is necessary, as some networks
145f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    // can't be matched to policies by properties (e.g. VPN).
146cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    new_policy = GetByGUID(all_policies_, old_guid);
147cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
148cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
149cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (!new_policy) {
150cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    // If we didn't find a policy by GUID, still a new policy might match.
151cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    new_policy = policy_util::FindMatchingPolicy(all_policies_, *onc_part);
152cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
153cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
154cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (new_policy) {
155cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    std::string new_guid;
156cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    new_policy->GetStringWithoutPathExpansion(::onc::network_config::kGUID,
157cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                                              &new_guid);
158cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
159cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    VLOG_IF(1, was_managed && old_guid != new_guid)
160cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        << "Updating configuration previously managed by policy " << old_guid
161cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        << " with new policy " << new_guid << ".";
162cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    VLOG_IF(1, !was_managed) << "Applying policy " << new_guid
163cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                             << " to previously unmanaged "
164cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                             << "configuration.";
165cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
166cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    if (old_guid == new_guid &&
167cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        remaining_policies_.find(new_guid) == remaining_policies_.end()) {
168cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      VLOG(1) << "Not updating existing managed configuration with guid "
169cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)              << new_guid << " because the policy didn't change.";
170cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    } else {
171cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      const base::DictionaryValue* user_settings =
172cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)          ui_data ? ui_data->user_settings() : NULL;
173cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      scoped_ptr<base::DictionaryValue> new_shill_properties =
174cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)          policy_util::CreateShillConfiguration(
175cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)              profile_, new_guid, new_policy, user_settings);
176cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      // A new policy has to be applied to this profile entry. In order to keep
1774e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      // implicit state of Shill like "connected successfully before", keep the
1784e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      // 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);
194      }
195
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);
208    }
209  } else if (was_managed) {
210    VLOG(1) << "Removing configuration previously managed by policy "
211            << old_guid << ", because the policy was removed.";
212
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);
230    }
231  }
232}
233
234void 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));
240}
241
242void 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;
260  }
261
262  if (write_later)
263    new_shill_configurations_.push_back(shill_dictionary.DeepCopy());
264  else
265    handler_->CreateConfigurationFromPolicy(shill_dictionary);
266}
267
268void PolicyApplicator::GetPropertiesForUnmanagedEntry(
269    const base::DictionaryValue& entry_properties,
270    base::DictionaryValue* properties_to_update) const {
271  // kAllowOnlyPolicyNetworksToAutoconnect is currently the only global config.
272
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.
277
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;
285
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;
292  }
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);
297}
298
299PolicyApplicator::~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  handler_->OnPoliciesApplied();
305}
306
307void PolicyApplicator::ApplyRemainingPolicies() {
308  if (!handler_) {
309    LOG(WARNING) << "Handler destructed during policy application to profile "
310                 << profile_.ToDebugString();
311    return;
312  }
313
314  // Write all queued configurations now.
315  for (ScopedVector<base::DictionaryValue>::const_iterator it =
316           new_shill_configurations_.begin();
317       it != new_shill_configurations_.end();
318       ++it) {
319    handler_->CreateConfigurationFromPolicy(**it);
320  }
321
322  if (remaining_policies_.empty())
323    return;
324
325  VLOG(2) << "Create new managed network configurations in profile"
326          << profile_.ToDebugString() << ".";
327  // All profile entries were compared to policies. |remaining_policies_|
328  // contains all modified policies that didn't match any entry. For these
329  // remaining policies, new configurations have to be created.
330  for (std::set<std::string>::iterator it = remaining_policies_.begin();
331       it != remaining_policies_.end(); ++it) {
332    const base::DictionaryValue* policy = GetByGUID(all_policies_, *it);
333    DCHECK(policy);
334
335    VLOG(1) << "Creating new configuration managed by policy " << *it
336            << " in profile " << profile_.ToDebugString() << ".";
337
338    scoped_ptr<base::DictionaryValue> shill_dictionary =
339        policy_util::CreateShillConfiguration(profile_, *it, policy, NULL);
340    WriteNewShillConfiguration(*shill_dictionary, *policy, false);
341  }
342}
343
344}  // namespace chromeos
345