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/location.h"
11 #include "base/single_thread_task_runner.h"
12 #include "base/stl_util.h"
13 #include "base/thread_task_runner_handle.h"
14 #include "base/values.h"
15 #include "components/policy/core/common/policy_bundle.h"
16 #include "components/policy/core/common/policy_map.h"
17 #include "policy/policy_constants.h"
21 typedef PolicyServiceImpl::Providers::const_iterator Iterator
;
25 const char* kProxyPolicies
[] = {
27 key::kProxyServerMode
,
30 key::kProxyBypassList
,
33 void FixDeprecatedPolicies(PolicyMap
* policies
) {
34 // Proxy settings have been configured by 5 policies that didn't mix well
35 // together, and maps of policies had to take this into account when merging
36 // policy sources. The proxy settings will eventually be configured by a
37 // single Dictionary policy when all providers have support for that. For
38 // now, the individual policies are mapped here to a single Dictionary policy
39 // that the rest of the policy machinery uses.
41 // The highest (level, scope) pair for an existing proxy policy is determined
42 // first, and then only policies with those exact attributes are merged.
43 PolicyMap::Entry current_priority
; // Defaults to the lowest priority.
44 scoped_ptr
<base::DictionaryValue
> proxy_settings(new base::DictionaryValue
);
45 for (size_t i
= 0; i
< arraysize(kProxyPolicies
); ++i
) {
46 const PolicyMap::Entry
* entry
= policies
->Get(kProxyPolicies
[i
]);
48 if (entry
->has_higher_priority_than(current_priority
)) {
49 proxy_settings
->Clear();
50 current_priority
= *entry
;
52 if (!entry
->has_higher_priority_than(current_priority
) &&
53 !current_priority
.has_higher_priority_than(*entry
)) {
54 proxy_settings
->Set(kProxyPolicies
[i
], entry
->value
->DeepCopy());
56 policies
->Erase(kProxyPolicies
[i
]);
59 // Sets the new |proxy_settings| if kProxySettings isn't set yet, or if the
60 // new priority is higher.
61 const PolicyMap::Entry
* existing
= policies
->Get(key::kProxySettings
);
62 if (!proxy_settings
->empty() &&
63 (!existing
|| current_priority
.has_higher_priority_than(*existing
))) {
64 policies
->Set(key::kProxySettings
,
65 current_priority
.level
,
66 current_priority
.scope
,
67 proxy_settings
.release(),
74 PolicyServiceImpl::PolicyServiceImpl(const Providers
& providers
)
75 : update_task_ptr_factory_(this) {
76 for (int domain
= 0; domain
< POLICY_DOMAIN_SIZE
; ++domain
)
77 initialization_complete_
[domain
] = true;
78 providers_
= providers
;
79 for (Iterator it
= providers
.begin(); it
!= providers
.end(); ++it
) {
80 ConfigurationPolicyProvider
* provider
= *it
;
81 provider
->AddObserver(this);
82 for (int domain
= 0; domain
< POLICY_DOMAIN_SIZE
; ++domain
) {
83 initialization_complete_
[domain
] &=
84 provider
->IsInitializationComplete(static_cast<PolicyDomain
>(domain
));
87 // There are no observers yet, but calls to GetPolicies() should already get
88 // the processed policy values.
89 MergeAndTriggerUpdates();
92 PolicyServiceImpl::~PolicyServiceImpl() {
93 DCHECK(thread_checker_
.CalledOnValidThread());
94 for (Iterator it
= providers_
.begin(); it
!= providers_
.end(); ++it
)
95 (*it
)->RemoveObserver(this);
96 STLDeleteValues(&observers_
);
99 void PolicyServiceImpl::AddObserver(PolicyDomain domain
,
100 PolicyService::Observer
* observer
) {
101 DCHECK(thread_checker_
.CalledOnValidThread());
102 Observers
*& list
= observers_
[domain
];
104 list
= new Observers();
105 list
->AddObserver(observer
);
108 void PolicyServiceImpl::RemoveObserver(PolicyDomain domain
,
109 PolicyService::Observer
* observer
) {
110 DCHECK(thread_checker_
.CalledOnValidThread());
111 ObserverMap::iterator it
= observers_
.find(domain
);
112 if (it
== observers_
.end()) {
116 it
->second
->RemoveObserver(observer
);
117 if (!it
->second
->might_have_observers()) {
119 observers_
.erase(it
);
123 const PolicyMap
& PolicyServiceImpl::GetPolicies(
124 const PolicyNamespace
& ns
) const {
125 DCHECK(thread_checker_
.CalledOnValidThread());
126 return policy_bundle_
.Get(ns
);
129 bool PolicyServiceImpl::IsInitializationComplete(PolicyDomain domain
) const {
130 DCHECK(thread_checker_
.CalledOnValidThread());
131 DCHECK(domain
>= 0 && domain
< POLICY_DOMAIN_SIZE
);
132 return initialization_complete_
[domain
];
135 void PolicyServiceImpl::RefreshPolicies(const base::Closure
& callback
) {
136 DCHECK(thread_checker_
.CalledOnValidThread());
138 if (!callback
.is_null())
139 refresh_callbacks_
.push_back(callback
);
141 if (providers_
.empty()) {
142 // Refresh is immediately complete if there are no providers. See the note
143 // on OnUpdatePolicy() about why this is a posted task.
144 update_task_ptr_factory_
.InvalidateWeakPtrs();
145 base::ThreadTaskRunnerHandle::Get()->PostTask(
146 FROM_HERE
, base::Bind(&PolicyServiceImpl::MergeAndTriggerUpdates
,
147 update_task_ptr_factory_
.GetWeakPtr()));
149 // Some providers might invoke OnUpdatePolicy synchronously while handling
150 // RefreshPolicies. Mark all as pending before refreshing.
151 for (Iterator it
= providers_
.begin(); it
!= providers_
.end(); ++it
)
152 refresh_pending_
.insert(*it
);
153 for (Iterator it
= providers_
.begin(); it
!= providers_
.end(); ++it
)
154 (*it
)->RefreshPolicies();
158 void PolicyServiceImpl::OnUpdatePolicy(ConfigurationPolicyProvider
* provider
) {
159 DCHECK_EQ(1, std::count(providers_
.begin(), providers_
.end(), provider
));
160 refresh_pending_
.erase(provider
);
162 // Note: a policy change may trigger further policy changes in some providers.
163 // For example, disabling SigninAllowed would cause the CloudPolicyManager to
164 // drop all its policies, which makes this method enter again for that
167 // Therefore this update is posted asynchronously, to prevent reentrancy in
168 // MergeAndTriggerUpdates. Also, cancel a pending update if there is any,
169 // since both will produce the same PolicyBundle.
170 update_task_ptr_factory_
.InvalidateWeakPtrs();
171 base::ThreadTaskRunnerHandle::Get()->PostTask(
172 FROM_HERE
, 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