Home | History | Annotate | Download | only in common
      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