1 // Copyright (c) 2012 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 "components/policy/core/common/policy_service_impl.h" 6 7 #include <algorithm> 8 9 #include "base/bind.h" 10 #include "base/message_loop/message_loop.h" 11 #include "base/stl_util.h" 12 #include "base/values.h" 13 #include "components/policy/core/common/policy_bundle.h" 14 #include "components/policy/core/common/policy_map.h" 15 #include "policy/policy_constants.h" 16 17 namespace policy { 18 19 typedef PolicyServiceImpl::Providers::const_iterator Iterator; 20 21 namespace { 22 23 const char* kProxyPolicies[] = { 24 key::kProxyMode, 25 key::kProxyServerMode, 26 key::kProxyServer, 27 key::kProxyPacUrl, 28 key::kProxyBypassList, 29 }; 30 31 void FixDeprecatedPolicies(PolicyMap* policies) { 32 // Proxy settings have been configured by 5 policies that didn't mix well 33 // together, and maps of policies had to take this into account when merging 34 // policy sources. The proxy settings will eventually be configured by a 35 // single Dictionary policy when all providers have support for that. For 36 // now, the individual policies are mapped here to a single Dictionary policy 37 // that the rest of the policy machinery uses. 38 39 // The highest (level, scope) pair for an existing proxy policy is determined 40 // first, and then only policies with those exact attributes are merged. 41 PolicyMap::Entry current_priority; // Defaults to the lowest priority. 42 scoped_ptr<base::DictionaryValue> proxy_settings(new base::DictionaryValue); 43 for (size_t i = 0; i < arraysize(kProxyPolicies); ++i) { 44 const PolicyMap::Entry* entry = policies->Get(kProxyPolicies[i]); 45 if (entry) { 46 if (entry->has_higher_priority_than(current_priority)) { 47 proxy_settings->Clear(); 48 current_priority = *entry; 49 } 50 if (!entry->has_higher_priority_than(current_priority) && 51 !current_priority.has_higher_priority_than(*entry)) { 52 proxy_settings->Set(kProxyPolicies[i], entry->value->DeepCopy()); 53 } 54 policies->Erase(kProxyPolicies[i]); 55 } 56 } 57 // Sets the new |proxy_settings| if kProxySettings isn't set yet, or if the 58 // new priority is higher. 59 const PolicyMap::Entry* existing = policies->Get(key::kProxySettings); 60 if (!proxy_settings->empty() && 61 (!existing || current_priority.has_higher_priority_than(*existing))) { 62 policies->Set(key::kProxySettings, 63 current_priority.level, 64 current_priority.scope, 65 proxy_settings.release(), 66 NULL); 67 } 68 } 69 70 } // namespace 71 72 PolicyServiceImpl::PolicyServiceImpl(const Providers& providers) 73 : update_task_ptr_factory_(this) { 74 for (int domain = 0; domain < POLICY_DOMAIN_SIZE; ++domain) 75 initialization_complete_[domain] = true; 76 providers_ = providers; 77 for (Iterator it = providers.begin(); it != providers.end(); ++it) { 78 ConfigurationPolicyProvider* provider = *it; 79 provider->AddObserver(this); 80 for (int domain = 0; domain < POLICY_DOMAIN_SIZE; ++domain) { 81 initialization_complete_[domain] &= 82 provider->IsInitializationComplete(static_cast<PolicyDomain>(domain)); 83 } 84 } 85 // There are no observers yet, but calls to GetPolicies() should already get 86 // the processed policy values. 87 MergeAndTriggerUpdates(); 88 } 89 90 PolicyServiceImpl::~PolicyServiceImpl() { 91 for (Iterator it = providers_.begin(); it != providers_.end(); ++it) 92 (*it)->RemoveObserver(this); 93 STLDeleteValues(&observers_); 94 } 95 96 void PolicyServiceImpl::AddObserver(PolicyDomain domain, 97 PolicyService::Observer* observer) { 98 Observers*& list = observers_[domain]; 99 if (!list) 100 list = new Observers(); 101 list->AddObserver(observer); 102 } 103 104 void PolicyServiceImpl::RemoveObserver(PolicyDomain domain, 105 PolicyService::Observer* observer) { 106 ObserverMap::iterator it = observers_.find(domain); 107 if (it == observers_.end()) { 108 NOTREACHED(); 109 return; 110 } 111 it->second->RemoveObserver(observer); 112 if (!it->second->might_have_observers()) { 113 delete it->second; 114 observers_.erase(it); 115 } 116 } 117 118 const PolicyMap& PolicyServiceImpl::GetPolicies( 119 const PolicyNamespace& ns) const { 120 return policy_bundle_.Get(ns); 121 } 122 123 bool PolicyServiceImpl::IsInitializationComplete(PolicyDomain domain) const { 124 DCHECK(domain >= 0 && domain < POLICY_DOMAIN_SIZE); 125 return initialization_complete_[domain]; 126 } 127 128 void PolicyServiceImpl::RefreshPolicies(const base::Closure& callback) { 129 if (!callback.is_null()) 130 refresh_callbacks_.push_back(callback); 131 132 if (providers_.empty()) { 133 // Refresh is immediately complete if there are no providers. See the note 134 // on OnUpdatePolicy() about why this is a posted task. 135 update_task_ptr_factory_.InvalidateWeakPtrs(); 136 base::MessageLoop::current()->PostTask( 137 FROM_HERE, 138 base::Bind(&PolicyServiceImpl::MergeAndTriggerUpdates, 139 update_task_ptr_factory_.GetWeakPtr())); 140 } else { 141 // Some providers might invoke OnUpdatePolicy synchronously while handling 142 // RefreshPolicies. Mark all as pending before refreshing. 143 for (Iterator it = providers_.begin(); it != providers_.end(); ++it) 144 refresh_pending_.insert(*it); 145 for (Iterator it = providers_.begin(); it != providers_.end(); ++it) 146 (*it)->RefreshPolicies(); 147 } 148 } 149 150 void PolicyServiceImpl::OnUpdatePolicy(ConfigurationPolicyProvider* provider) { 151 DCHECK_EQ(1, std::count(providers_.begin(), providers_.end(), provider)); 152 refresh_pending_.erase(provider); 153 154 // Note: a policy change may trigger further policy changes in some providers. 155 // For example, disabling SigninAllowed would cause the CloudPolicyManager to 156 // drop all its policies, which makes this method enter again for that 157 // provider. 158 // 159 // Therefore this update is posted asynchronously, to prevent reentrancy in 160 // MergeAndTriggerUpdates. Also, cancel a pending update if there is any, 161 // since both will produce the same PolicyBundle. 162 update_task_ptr_factory_.InvalidateWeakPtrs(); 163 base::MessageLoop::current()->PostTask( 164 FROM_HERE, 165 base::Bind(&PolicyServiceImpl::MergeAndTriggerUpdates, 166 update_task_ptr_factory_.GetWeakPtr())); 167 } 168 169 void PolicyServiceImpl::NotifyNamespaceUpdated( 170 const PolicyNamespace& ns, 171 const PolicyMap& previous, 172 const PolicyMap& current) { 173 ObserverMap::iterator iterator = observers_.find(ns.domain); 174 if (iterator != observers_.end()) { 175 FOR_EACH_OBSERVER(PolicyService::Observer, 176 *iterator->second, 177 OnPolicyUpdated(ns, previous, current)); 178 } 179 } 180 181 void PolicyServiceImpl::MergeAndTriggerUpdates() { 182 // Merge from each provider in their order of priority. 183 const PolicyNamespace chrome_namespace(POLICY_DOMAIN_CHROME, std::string()); 184 PolicyBundle bundle; 185 for (Iterator it = providers_.begin(); it != providers_.end(); ++it) { 186 PolicyBundle provided_bundle; 187 provided_bundle.CopyFrom((*it)->policies()); 188 FixDeprecatedPolicies(&provided_bundle.Get(chrome_namespace)); 189 bundle.MergeFrom(provided_bundle); 190 } 191 192 // Swap first, so that observers that call GetPolicies() see the current 193 // values. 194 policy_bundle_.Swap(&bundle); 195 196 // Only notify observers of namespaces that have been modified. 197 const PolicyMap kEmpty; 198 PolicyBundle::const_iterator it_new = policy_bundle_.begin(); 199 PolicyBundle::const_iterator end_new = policy_bundle_.end(); 200 PolicyBundle::const_iterator it_old = bundle.begin(); 201 PolicyBundle::const_iterator end_old = bundle.end(); 202 while (it_new != end_new && it_old != end_old) { 203 if (it_new->first < it_old->first) { 204 // A new namespace is available. 205 NotifyNamespaceUpdated(it_new->first, kEmpty, *it_new->second); 206 ++it_new; 207 } else if (it_old->first < it_new->first) { 208 // A previously available namespace is now gone. 209 NotifyNamespaceUpdated(it_old->first, *it_old->second, kEmpty); 210 ++it_old; 211 } else { 212 if (!it_new->second->Equals(*it_old->second)) { 213 // An existing namespace's policies have changed. 214 NotifyNamespaceUpdated(it_new->first, *it_old->second, *it_new->second); 215 } 216 ++it_new; 217 ++it_old; 218 } 219 } 220 221 // Send updates for the remaining new namespaces, if any. 222 for (; it_new != end_new; ++it_new) 223 NotifyNamespaceUpdated(it_new->first, kEmpty, *it_new->second); 224 225 // Sends updates for the remaining removed namespaces, if any. 226 for (; it_old != end_old; ++it_old) 227 NotifyNamespaceUpdated(it_old->first, *it_old->second, kEmpty); 228 229 CheckInitializationComplete(); 230 CheckRefreshComplete(); 231 } 232 233 void PolicyServiceImpl::CheckInitializationComplete() { 234 // Check if all the providers just became initialized for each domain; if so, 235 // notify that domain's observers. 236 for (int domain = 0; domain < POLICY_DOMAIN_SIZE; ++domain) { 237 if (initialization_complete_[domain]) 238 continue; 239 240 PolicyDomain policy_domain = static_cast<PolicyDomain>(domain); 241 242 bool all_complete = true; 243 for (Iterator it = providers_.begin(); it != providers_.end(); ++it) { 244 if (!(*it)->IsInitializationComplete(policy_domain)) { 245 all_complete = false; 246 break; 247 } 248 } 249 if (all_complete) { 250 initialization_complete_[domain] = true; 251 ObserverMap::iterator iter = observers_.find(policy_domain); 252 if (iter != observers_.end()) { 253 FOR_EACH_OBSERVER(PolicyService::Observer, 254 *iter->second, 255 OnPolicyServiceInitialized(policy_domain)); 256 } 257 } 258 } 259 } 260 261 void PolicyServiceImpl::CheckRefreshComplete() { 262 // Invoke all the callbacks if a refresh has just fully completed. 263 if (refresh_pending_.empty() && !refresh_callbacks_.empty()) { 264 std::vector<base::Closure> callbacks; 265 callbacks.swap(refresh_callbacks_); 266 std::vector<base::Closure>::iterator it; 267 for (it = callbacks.begin(); it != callbacks.end(); ++it) 268 it->Run(); 269 } 270 } 271 272 } // namespace policy 273