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 for (Iterator it
= providers_
.begin(); it
!= providers_
.end(); ++it
)
92 (*it
)->RemoveObserver(this);
93 STLDeleteValues(&observers_
);
96 void PolicyServiceImpl::AddObserver(PolicyDomain domain
,
97 PolicyService::Observer
* observer
) {
98 Observers
*& list
= observers_
[domain
];
100 list
= new Observers();
101 list
->AddObserver(observer
);
104 void PolicyServiceImpl::RemoveObserver(PolicyDomain domain
,
105 PolicyService::Observer
* observer
) {
106 ObserverMap::iterator it
= observers_
.find(domain
);
107 if (it
== observers_
.end()) {
111 it
->second
->RemoveObserver(observer
);
112 if (!it
->second
->might_have_observers()) {
114 observers_
.erase(it
);
118 const PolicyMap
& PolicyServiceImpl::GetPolicies(
119 const PolicyNamespace
& ns
) const {
120 return policy_bundle_
.Get(ns
);
123 bool PolicyServiceImpl::IsInitializationComplete(PolicyDomain domain
) const {
124 DCHECK(domain
>= 0 && domain
< POLICY_DOMAIN_SIZE
);
125 return initialization_complete_
[domain
];
128 void PolicyServiceImpl::RefreshPolicies(const base::Closure
& callback
) {
129 if (!callback
.is_null())
130 refresh_callbacks_
.push_back(callback
);
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(
138 base::Bind(&PolicyServiceImpl::MergeAndTriggerUpdates
,
139 update_task_ptr_factory_
.GetWeakPtr()));
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();
150 void PolicyServiceImpl::OnUpdatePolicy(ConfigurationPolicyProvider
* provider
) {
151 DCHECK_EQ(1, std::count(providers_
.begin(), providers_
.end(), provider
));
152 refresh_pending_
.erase(provider
);
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
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(
165 base::Bind(&PolicyServiceImpl::MergeAndTriggerUpdates
,
166 update_task_ptr_factory_
.GetWeakPtr()));
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
,
177 OnPolicyUpdated(ns
, previous
, current
));
181 void PolicyServiceImpl::MergeAndTriggerUpdates() {
182 // Merge from each provider in their order of priority.
183 const PolicyNamespace
chrome_namespace(POLICY_DOMAIN_CHROME
, std::string());
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
);
192 // Swap first, so that observers that call GetPolicies() see the current
194 policy_bundle_
.Swap(&bundle
);
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
);
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
);
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
);
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
);
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
);
229 CheckInitializationComplete();
230 CheckRefreshComplete();
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
])
240 PolicyDomain policy_domain
= static_cast<PolicyDomain
>(domain
);
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;
250 initialization_complete_
[domain
] = true;
251 ObserverMap::iterator iter
= observers_
.find(policy_domain
);
252 if (iter
!= observers_
.end()) {
253 FOR_EACH_OBSERVER(PolicyService::Observer
,
255 OnPolicyServiceInitialized(policy_domain
));
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
)
272 } // namespace policy