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