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.
5 #include "components/policy/core/common/policy_service_impl.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"
19 typedef PolicyServiceImpl::Providers::const_iterator Iterator
;
23 const char* kProxyPolicies
[] = {
25 key::kProxyServerMode
,
28 key::kProxyBypassList
,
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.
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
]);
46 if (entry
->has_higher_priority_than(current_priority
)) {
47 proxy_settings
->Clear();
48 current_priority
= *entry
;
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());
54 policies
->Erase(kProxyPolicies
[i
]);
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(),
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
));
85 // There are no observers yet, but calls to GetPolicies() should already get
86 // the processed policy values.
87 MergeAndTriggerUpdates();
90 PolicyServiceImpl::~PolicyServiceImpl() {
91 DCHECK(thread_checker_
.CalledOnValidThread());
92 for (Iterator it
= providers_
.begin(); it
!= providers_
.end(); ++it
)
93 (*it
)->RemoveObserver(this);
94 STLDeleteValues(&observers_
);
97 void PolicyServiceImpl::AddObserver(PolicyDomain domain
,
98 PolicyService::Observer
* observer
) {
99 DCHECK(thread_checker_
.CalledOnValidThread());
100 Observers
*& list
= observers_
[domain
];
102 list
= new Observers();
103 list
->AddObserver(observer
);
106 void PolicyServiceImpl::RemoveObserver(PolicyDomain domain
,
107 PolicyService::Observer
* observer
) {
108 DCHECK(thread_checker_
.CalledOnValidThread());
109 ObserverMap::iterator it
= observers_
.find(domain
);
110 if (it
== observers_
.end()) {
114 it
->second
->RemoveObserver(observer
);
115 if (!it
->second
->might_have_observers()) {
117 observers_
.erase(it
);
121 const PolicyMap
& PolicyServiceImpl::GetPolicies(
122 const PolicyNamespace
& ns
) const {
123 DCHECK(thread_checker_
.CalledOnValidThread());
124 return policy_bundle_
.Get(ns
);
127 bool PolicyServiceImpl::IsInitializationComplete(PolicyDomain domain
) const {
128 DCHECK(thread_checker_
.CalledOnValidThread());
129 DCHECK(domain
>= 0 && domain
< POLICY_DOMAIN_SIZE
);
130 return initialization_complete_
[domain
];
133 void PolicyServiceImpl::RefreshPolicies(const base::Closure
& callback
) {
134 DCHECK(thread_checker_
.CalledOnValidThread());
136 if (!callback
.is_null())
137 refresh_callbacks_
.push_back(callback
);
139 if (providers_
.empty()) {
140 // Refresh is immediately complete if there are no providers. See the note
141 // on OnUpdatePolicy() about why this is a posted task.
142 update_task_ptr_factory_
.InvalidateWeakPtrs();
143 base::MessageLoop::current()->PostTask(
145 base::Bind(&PolicyServiceImpl::MergeAndTriggerUpdates
,
146 update_task_ptr_factory_
.GetWeakPtr()));
148 // Some providers might invoke OnUpdatePolicy synchronously while handling
149 // RefreshPolicies. Mark all as pending before refreshing.
150 for (Iterator it
= providers_
.begin(); it
!= providers_
.end(); ++it
)
151 refresh_pending_
.insert(*it
);
152 for (Iterator it
= providers_
.begin(); it
!= providers_
.end(); ++it
)
153 (*it
)->RefreshPolicies();
157 void PolicyServiceImpl::OnUpdatePolicy(ConfigurationPolicyProvider
* provider
) {
158 DCHECK_EQ(1, std::count(providers_
.begin(), providers_
.end(), provider
));
159 refresh_pending_
.erase(provider
);
161 // Note: a policy change may trigger further policy changes in some providers.
162 // For example, disabling SigninAllowed would cause the CloudPolicyManager to
163 // drop all its policies, which makes this method enter again for that
166 // Therefore this update is posted asynchronously, to prevent reentrancy in
167 // MergeAndTriggerUpdates. Also, cancel a pending update if there is any,
168 // since both will produce the same PolicyBundle.
169 update_task_ptr_factory_
.InvalidateWeakPtrs();
170 base::MessageLoop::current()->PostTask(
172 base::Bind(&PolicyServiceImpl::MergeAndTriggerUpdates
,
173 update_task_ptr_factory_
.GetWeakPtr()));
176 void PolicyServiceImpl::NotifyNamespaceUpdated(
177 const PolicyNamespace
& ns
,
178 const PolicyMap
& previous
,
179 const PolicyMap
& current
) {
180 DCHECK(thread_checker_
.CalledOnValidThread());
181 ObserverMap::iterator iterator
= observers_
.find(ns
.domain
);
182 if (iterator
!= observers_
.end()) {
183 FOR_EACH_OBSERVER(PolicyService::Observer
,
185 OnPolicyUpdated(ns
, previous
, current
));
189 void PolicyServiceImpl::MergeAndTriggerUpdates() {
190 // Merge from each provider in their order of priority.
191 const PolicyNamespace
chrome_namespace(POLICY_DOMAIN_CHROME
, std::string());
193 for (Iterator it
= providers_
.begin(); it
!= providers_
.end(); ++it
) {
194 PolicyBundle provided_bundle
;
195 provided_bundle
.CopyFrom((*it
)->policies());
196 FixDeprecatedPolicies(&provided_bundle
.Get(chrome_namespace
));
197 bundle
.MergeFrom(provided_bundle
);
200 // Swap first, so that observers that call GetPolicies() see the current
202 policy_bundle_
.Swap(&bundle
);
204 // Only notify observers of namespaces that have been modified.
205 const PolicyMap kEmpty
;
206 PolicyBundle::const_iterator it_new
= policy_bundle_
.begin();
207 PolicyBundle::const_iterator end_new
= policy_bundle_
.end();
208 PolicyBundle::const_iterator it_old
= bundle
.begin();
209 PolicyBundle::const_iterator end_old
= bundle
.end();
210 while (it_new
!= end_new
&& it_old
!= end_old
) {
211 if (it_new
->first
< it_old
->first
) {
212 // A new namespace is available.
213 NotifyNamespaceUpdated(it_new
->first
, kEmpty
, *it_new
->second
);
215 } else if (it_old
->first
< it_new
->first
) {
216 // A previously available namespace is now gone.
217 NotifyNamespaceUpdated(it_old
->first
, *it_old
->second
, kEmpty
);
220 if (!it_new
->second
->Equals(*it_old
->second
)) {
221 // An existing namespace's policies have changed.
222 NotifyNamespaceUpdated(it_new
->first
, *it_old
->second
, *it_new
->second
);
229 // Send updates for the remaining new namespaces, if any.
230 for (; it_new
!= end_new
; ++it_new
)
231 NotifyNamespaceUpdated(it_new
->first
, kEmpty
, *it_new
->second
);
233 // Sends updates for the remaining removed namespaces, if any.
234 for (; it_old
!= end_old
; ++it_old
)
235 NotifyNamespaceUpdated(it_old
->first
, *it_old
->second
, kEmpty
);
237 CheckInitializationComplete();
238 CheckRefreshComplete();
241 void PolicyServiceImpl::CheckInitializationComplete() {
242 DCHECK(thread_checker_
.CalledOnValidThread());
244 // Check if all the providers just became initialized for each domain; if so,
245 // notify that domain's observers.
246 for (int domain
= 0; domain
< POLICY_DOMAIN_SIZE
; ++domain
) {
247 if (initialization_complete_
[domain
])
250 PolicyDomain policy_domain
= static_cast<PolicyDomain
>(domain
);
252 bool all_complete
= true;
253 for (Iterator it
= providers_
.begin(); it
!= providers_
.end(); ++it
) {
254 if (!(*it
)->IsInitializationComplete(policy_domain
)) {
255 all_complete
= false;
260 initialization_complete_
[domain
] = true;
261 ObserverMap::iterator iter
= observers_
.find(policy_domain
);
262 if (iter
!= observers_
.end()) {
263 FOR_EACH_OBSERVER(PolicyService::Observer
,
265 OnPolicyServiceInitialized(policy_domain
));
271 void PolicyServiceImpl::CheckRefreshComplete() {
272 // Invoke all the callbacks if a refresh has just fully completed.
273 if (refresh_pending_
.empty() && !refresh_callbacks_
.empty()) {
274 std::vector
<base::Closure
> callbacks
;
275 callbacks
.swap(refresh_callbacks_
);
276 std::vector
<base::Closure
>::iterator it
;
277 for (it
= callbacks
.begin(); it
!= callbacks
.end(); ++it
)
282 } // namespace policy