OLD | NEW |
| (Empty) |
1 // Copyright (c) 2013 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 "chrome/browser/policy/component_cloud_policy_service.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/bind_helpers.h" | |
9 #include "base/location.h" | |
10 #include "base/logging.h" | |
11 #include "base/message_loop_proxy.h" | |
12 #include "base/pickle.h" | |
13 #include "base/sequenced_task_runner.h" | |
14 #include "base/stl_util.h" | |
15 #include "chrome/browser/policy/component_cloud_policy_store.h" | |
16 #include "chrome/browser/policy/component_cloud_policy_updater.h" | |
17 #include "chrome/browser/policy/proto/device_management_backend.pb.h" | |
18 #include "chrome/browser/policy/resource_cache.h" | |
19 #include "content/public/browser/browser_thread.h" | |
20 #include "net/url_request/url_request_context_getter.h" | |
21 | |
22 namespace em = enterprise_management; | |
23 | |
24 namespace policy { | |
25 | |
26 const char ComponentCloudPolicyService::kComponentNamespaceCache[] = | |
27 "component-namespace-cache"; | |
28 | |
29 ComponentCloudPolicyService::Delegate::~Delegate() {} | |
30 | |
31 // Owns the objects that live on the background thread, and posts back to UI | |
32 // to the service whenever the policy changes. | |
33 class ComponentCloudPolicyService::Backend | |
34 : public ComponentCloudPolicyStore::Delegate { | |
35 public: | |
36 Backend(base::WeakPtr<ComponentCloudPolicyService> service, | |
37 scoped_refptr<base::SequencedTaskRunner> task_runner, | |
38 scoped_ptr<ResourceCache> cache); | |
39 ~Backend(); | |
40 | |
41 // This is invoked right after the constructor but on the backend background | |
42 // thread. Used to create the store on the right thread. | |
43 void Init(); | |
44 | |
45 // Reads the initial list of components and the initial policy. | |
46 void FinalizeInit(); | |
47 | |
48 // Creates the backend updater. | |
49 void Connect(scoped_refptr<net::URLRequestContextGetter> request_context); | |
50 | |
51 // Stops updating remote data. Cached policies are still served. | |
52 void Disconnect(); | |
53 | |
54 // Loads the initial policies from the store. |username| and |dm_token| are | |
55 // used to validate the cached policies. | |
56 void SetCredentials(const std::string& username, const std::string& dm_token); | |
57 | |
58 // Passes a policy protobuf to the backend, to start its validation and | |
59 // eventual download of the policy data on the background thread. | |
60 // This is ignored if the backend isn't connected. | |
61 void UpdateExternalPolicy(scoped_ptr<em::PolicyFetchResponse> response); | |
62 | |
63 // ComponentCloudPolicyStore::Delegate implementation: | |
64 virtual void OnComponentCloudPolicyStoreUpdated() OVERRIDE; | |
65 | |
66 // Passes the current list of components in |domain|, so that the disk cache | |
67 // can purge components that aren't being tracked anymore. | |
68 void SetCurrentComponents(PolicyDomain domain, const StringSet* components); | |
69 | |
70 private: | |
71 scoped_ptr<ComponentMap> ReadCachedComponents(); | |
72 | |
73 base::WeakPtr<ComponentCloudPolicyService> service_; | |
74 scoped_refptr<base::SequencedTaskRunner> task_runner_; | |
75 scoped_ptr<ResourceCache> cache_; | |
76 scoped_ptr<ComponentCloudPolicyStore> store_; | |
77 scoped_ptr<ComponentCloudPolicyUpdater> updater_; | |
78 | |
79 DISALLOW_COPY_AND_ASSIGN(Backend); | |
80 }; | |
81 | |
82 ComponentCloudPolicyService::Backend::Backend( | |
83 base::WeakPtr<ComponentCloudPolicyService> service, | |
84 scoped_refptr<base::SequencedTaskRunner> task_runner, | |
85 scoped_ptr<ResourceCache> cache) | |
86 : service_(service), | |
87 task_runner_(task_runner), | |
88 cache_(cache.Pass()) {} | |
89 | |
90 ComponentCloudPolicyService::Backend::~Backend() {} | |
91 | |
92 void ComponentCloudPolicyService::Backend::Init() { | |
93 DCHECK(!store_); | |
94 store_.reset(new ComponentCloudPolicyStore(this, cache_.get())); | |
95 } | |
96 | |
97 void ComponentCloudPolicyService::Backend::FinalizeInit() { | |
98 // Read the components that were cached in the last SetCurrentComponents() | |
99 // calls for each domain. | |
100 scoped_ptr<ComponentMap> components = ReadCachedComponents(); | |
101 | |
102 // Read the initial policy. | |
103 store_->Load(); | |
104 scoped_ptr<PolicyBundle> policy(new PolicyBundle); | |
105 policy->CopyFrom(store_->policy()); | |
106 | |
107 content::BrowserThread::PostTask( | |
108 content::BrowserThread::UI, FROM_HERE, | |
109 base::Bind(&ComponentCloudPolicyService::OnBackendInitialized, | |
110 service_, | |
111 base::Passed(&components), | |
112 base::Passed(&policy))); | |
113 } | |
114 | |
115 void ComponentCloudPolicyService::Backend::SetCredentials( | |
116 const std::string& username, | |
117 const std::string& dm_token) { | |
118 store_->SetCredentials(username, dm_token); | |
119 } | |
120 | |
121 void ComponentCloudPolicyService::Backend::Connect( | |
122 scoped_refptr<net::URLRequestContextGetter> request_context) { | |
123 updater_.reset(new ComponentCloudPolicyUpdater( | |
124 task_runner_, request_context, store_.get())); | |
125 } | |
126 | |
127 void ComponentCloudPolicyService::Backend::Disconnect() { | |
128 updater_.reset(); | |
129 } | |
130 | |
131 void ComponentCloudPolicyService::Backend::UpdateExternalPolicy( | |
132 scoped_ptr<em::PolicyFetchResponse> response) { | |
133 if (updater_) | |
134 updater_->UpdateExternalPolicy(response.Pass()); | |
135 } | |
136 | |
137 void ComponentCloudPolicyService::Backend:: | |
138 OnComponentCloudPolicyStoreUpdated() { | |
139 scoped_ptr<PolicyBundle> bundle(new PolicyBundle); | |
140 bundle->CopyFrom(store_->policy()); | |
141 content::BrowserThread::PostTask( | |
142 content::BrowserThread::UI, FROM_HERE, | |
143 base::Bind(&ComponentCloudPolicyService::OnPolicyUpdated, | |
144 service_, | |
145 base::Passed(&bundle))); | |
146 } | |
147 | |
148 void ComponentCloudPolicyService::Backend::SetCurrentComponents( | |
149 PolicyDomain domain, | |
150 const StringSet* components) { | |
151 // Store the current list of components in the cache. | |
152 std::string policy_type; | |
153 if (ComponentCloudPolicyStore::GetPolicyType(domain, &policy_type)) { | |
154 Pickle pickle; | |
155 for (StringSet::const_iterator it = components->begin(); | |
156 it != components->end(); ++it) { | |
157 pickle.WriteString(*it); | |
158 } | |
159 std::string data(reinterpret_cast<const char*>(pickle.data()), | |
160 pickle.size()); | |
161 cache_->Store(kComponentNamespaceCache, policy_type, data); | |
162 } | |
163 | |
164 // Purge any components that have been removed. | |
165 if (store_) | |
166 store_->Purge(domain, *components); | |
167 } | |
168 | |
169 scoped_ptr<ComponentCloudPolicyService::ComponentMap> | |
170 ComponentCloudPolicyService::Backend::ReadCachedComponents() { | |
171 scoped_ptr<ComponentMap> components(new ComponentMap); | |
172 std::map<std::string, std::string> contents; | |
173 cache_->LoadAllSubkeys(kComponentNamespaceCache, &contents); | |
174 for (std::map<std::string, std::string>::iterator it = contents.begin(); | |
175 it != contents.end(); ++it) { | |
176 PolicyDomain domain; | |
177 if (ComponentCloudPolicyStore::GetPolicyDomain(it->first, &domain)) { | |
178 StringSet& set = (*components)[domain]; | |
179 const Pickle pickle(it->second.data(), it->second.size()); | |
180 PickleIterator pickit(pickle); | |
181 std::string id; | |
182 while (pickit.ReadString(&id)) | |
183 set.insert(id); | |
184 } else { | |
185 cache_->Delete(kComponentNamespaceCache, it->first); | |
186 } | |
187 } | |
188 return components.Pass(); | |
189 } | |
190 | |
191 ComponentCloudPolicyService::ComponentCloudPolicyService( | |
192 Delegate* delegate, | |
193 CloudPolicyStore* store, | |
194 scoped_ptr<ResourceCache> cache) | |
195 : delegate_(delegate), | |
196 backend_(NULL), | |
197 client_(NULL), | |
198 store_(store), | |
199 is_initialized_(false), | |
200 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) { | |
201 store_->AddObserver(this); | |
202 | |
203 // TODO(joaodasilva): this can't currently live on the blocking pool because | |
204 // creating URLFetchers requires a MessageLoop. | |
205 backend_task_runner_ = content::BrowserThread::GetMessageLoopProxyForThread( | |
206 content::BrowserThread::FILE); | |
207 backend_ = new Backend(weak_ptr_factory_.GetWeakPtr(), | |
208 backend_task_runner_, | |
209 cache.Pass()); | |
210 backend_task_runner_->PostTask( | |
211 FROM_HERE, base::Bind(&Backend::Init, base::Unretained(backend_))); | |
212 | |
213 if (store_->is_initialized()) | |
214 InitializeBackend(); | |
215 } | |
216 | |
217 ComponentCloudPolicyService::~ComponentCloudPolicyService() { | |
218 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
219 store_->RemoveObserver(this); | |
220 if (client_) | |
221 client_->RemoveObserver(this); | |
222 backend_task_runner_->DeleteSoon(FROM_HERE, backend_); | |
223 backend_ = NULL; | |
224 } | |
225 | |
226 // static | |
227 bool ComponentCloudPolicyService::SupportsDomain(PolicyDomain domain) { | |
228 return ComponentCloudPolicyStore::SupportsDomain(domain); | |
229 } | |
230 | |
231 void ComponentCloudPolicyService::Connect( | |
232 CloudPolicyClient* client, | |
233 scoped_refptr<net::URLRequestContextGetter> request_context) { | |
234 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
235 DCHECK(!client_); | |
236 client_ = client; | |
237 client_->AddObserver(this); | |
238 // Create the updater in the backend. | |
239 backend_task_runner_->PostTask(FROM_HERE, | |
240 base::Bind(&Backend::Connect, | |
241 base::Unretained(backend_), | |
242 request_context)); | |
243 if (is_initialized()) | |
244 InitializeClient(); | |
245 } | |
246 | |
247 void ComponentCloudPolicyService::Disconnect() { | |
248 if (client_) { | |
249 // Unregister all the current components. | |
250 for (ComponentMap::iterator it = registered_components_.begin(); | |
251 it != registered_components_.end(); ++it) { | |
252 RemoveNamespacesToFetch(it->first, it->second); | |
253 } | |
254 | |
255 client_->RemoveObserver(this); | |
256 client_ = NULL; | |
257 | |
258 backend_task_runner_->PostTask( | |
259 FROM_HERE, | |
260 base::Bind(&Backend::Disconnect, base::Unretained(backend_))); | |
261 } | |
262 } | |
263 | |
264 void ComponentCloudPolicyService::RegisterPolicyDomain( | |
265 PolicyDomain domain, | |
266 const std::set<std::string>& current_ids) { | |
267 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
268 DCHECK(SupportsDomain(domain)); | |
269 | |
270 // Send the new set to the backend, to purge the cache. | |
271 backend_task_runner_->PostTask( | |
272 FROM_HERE, | |
273 base::Bind(&Backend::SetCurrentComponents, | |
274 base::Unretained(backend_), | |
275 domain, | |
276 base::Owned(new StringSet(current_ids)))); | |
277 | |
278 // Register the current list of components for |domain| at the |client_|. | |
279 StringSet& registered_ids = registered_components_[domain]; | |
280 if (client_ && is_initialized()) { | |
281 if (UpdateClientNamespaces(domain, registered_ids, current_ids)) | |
282 delegate_->OnComponentCloudPolicyRefreshNeeded(); | |
283 } | |
284 registered_ids = current_ids; | |
285 } | |
286 | |
287 void ComponentCloudPolicyService::OnPolicyFetched(CloudPolicyClient* client) { | |
288 DCHECK_EQ(client_, client); | |
289 // Pass each PolicyFetchResponse whose policy type is registered to the | |
290 // Backend. | |
291 const CloudPolicyClient::ResponseMap& responses = client_->responses(); | |
292 for (CloudPolicyClient::ResponseMap::const_iterator it = responses.begin(); | |
293 it != responses.end(); ++it) { | |
294 const PolicyNamespaceKey& key(it->first); | |
295 PolicyDomain domain; | |
296 if (ComponentCloudPolicyStore::GetPolicyDomain(key.first, &domain) && | |
297 ContainsKey(registered_components_[domain], key.second)) { | |
298 scoped_ptr<em::PolicyFetchResponse> response( | |
299 new em::PolicyFetchResponse(*it->second)); | |
300 backend_task_runner_->PostTask(FROM_HERE, | |
301 base::Bind(&Backend::UpdateExternalPolicy, | |
302 base::Unretained(backend_), | |
303 base::Passed(&response))); | |
304 } | |
305 } | |
306 } | |
307 | |
308 void ComponentCloudPolicyService::OnRegistrationStateChanged( | |
309 CloudPolicyClient* client) { | |
310 // Ignored. | |
311 } | |
312 | |
313 void ComponentCloudPolicyService::OnClientError(CloudPolicyClient* client) { | |
314 // Ignored. | |
315 } | |
316 | |
317 void ComponentCloudPolicyService::OnStoreLoaded(CloudPolicyStore* store) { | |
318 DCHECK_EQ(store_, store); | |
319 if (store_->is_initialized()) { | |
320 if (is_initialized()) { | |
321 // The backend is already initialized; update the credentials, in case | |
322 // a new dmtoken or server key became available. | |
323 SetCredentialsAndReloadClient(); | |
324 } else { | |
325 // The |store_| just became ready; initialize the backend now. | |
326 InitializeBackend(); | |
327 } | |
328 } | |
329 } | |
330 | |
331 void ComponentCloudPolicyService::OnStoreError(CloudPolicyStore* store) { | |
332 OnStoreLoaded(store); | |
333 } | |
334 | |
335 void ComponentCloudPolicyService::InitializeBackend() { | |
336 DCHECK(!is_initialized()); | |
337 DCHECK(store_->is_initialized()); | |
338 | |
339 // Set the credentials for the initial policy load, if available. | |
340 SetCredentialsAndReloadClient(); | |
341 | |
342 backend_task_runner_->PostTask( | |
343 FROM_HERE, | |
344 base::Bind(&Backend::FinalizeInit, base::Unretained(backend_))); | |
345 } | |
346 | |
347 void ComponentCloudPolicyService::OnBackendInitialized( | |
348 scoped_ptr<ComponentMap> cached_components, | |
349 scoped_ptr<PolicyBundle> initial_policy) { | |
350 // InitializeBackend() may be called multiple times if the |store_| fires | |
351 // events while the backend is loading. | |
352 if (is_initialized()) | |
353 return; | |
354 | |
355 // RegisterPolicyDomain() may have been called while the backend was | |
356 // initializing; only update |registered_components_| from |cached_components| | |
357 // for domains that haven't registered yet. | |
358 for (ComponentMap::iterator it = cached_components->begin(); | |
359 it != cached_components->end(); ++it) { | |
360 // Lookup without inserting an empty set. | |
361 if (registered_components_.find(it->first) != registered_components_.end()) | |
362 continue; // Ignore the cached list if a more recent one was registered. | |
363 registered_components_[it->first].swap(it->second); | |
364 } | |
365 | |
366 // A client may have already connected while the backend was initializing. | |
367 if (client_) | |
368 InitializeClient(); | |
369 | |
370 // Set the initial policy, and send the initial update callback. | |
371 is_initialized_ = true; | |
372 OnPolicyUpdated(initial_policy.Pass()); | |
373 } | |
374 | |
375 void ComponentCloudPolicyService::InitializeClient() { | |
376 // Register all the current components. | |
377 bool added = false; | |
378 for (ComponentMap::iterator it = registered_components_.begin(); | |
379 it != registered_components_.end(); ++it) { | |
380 added |= !it->second.empty(); | |
381 AddNamespacesToFetch(it->first, it->second); | |
382 } | |
383 // The client may already have PolicyFetchResponses for registered components; | |
384 // load them now. | |
385 OnPolicyFetched(client_); | |
386 if (added && is_initialized()) | |
387 delegate_->OnComponentCloudPolicyRefreshNeeded(); | |
388 } | |
389 | |
390 void ComponentCloudPolicyService::OnPolicyUpdated( | |
391 scoped_ptr<PolicyBundle> policy) { | |
392 policy_.Swap(policy.get()); | |
393 // Don't propagate updates until the initial store Load() has been done. | |
394 if (is_initialized()) | |
395 delegate_->OnComponentCloudPolicyUpdated(); | |
396 } | |
397 | |
398 void ComponentCloudPolicyService::SetCredentialsAndReloadClient() { | |
399 const em::PolicyData* policy = store_->policy(); | |
400 if (!policy || !policy->has_username() || !policy->has_request_token()) | |
401 return; | |
402 backend_task_runner_->PostTask(FROM_HERE, | |
403 base::Bind(&Backend::SetCredentials, | |
404 base::Unretained(backend_), | |
405 policy->username(), | |
406 policy->request_token())); | |
407 // If this was the initial register, or if the signing key changed, then the | |
408 // previous OnPolicyFetched() call had its PolicyFetchResponses rejected | |
409 // because the credentials weren't updated yet. Reload all the responses in | |
410 // the client now to handle those cases; if those responses have already been | |
411 // validated then they will be ignored. | |
412 if (client_) | |
413 OnPolicyFetched(client_); | |
414 } | |
415 | |
416 bool ComponentCloudPolicyService::UpdateClientNamespaces( | |
417 PolicyDomain domain, | |
418 const StringSet& old_set, | |
419 const StringSet& new_set) { | |
420 StringSet added = base::STLSetDifference<StringSet>(new_set, old_set); | |
421 StringSet removed = base::STLSetDifference<StringSet>(old_set, new_set); | |
422 AddNamespacesToFetch(domain, added); | |
423 RemoveNamespacesToFetch(domain, removed); | |
424 return !added.empty(); | |
425 } | |
426 | |
427 void ComponentCloudPolicyService::AddNamespacesToFetch(PolicyDomain domain, | |
428 const StringSet& set) { | |
429 std::string policy_type; | |
430 if (ComponentCloudPolicyStore::GetPolicyType(domain, &policy_type)) { | |
431 for (StringSet::const_iterator it = set.begin(); it != set.end(); ++it) | |
432 client_->AddNamespaceToFetch(PolicyNamespaceKey(policy_type, *it)); | |
433 } | |
434 } | |
435 | |
436 void ComponentCloudPolicyService::RemoveNamespacesToFetch( | |
437 PolicyDomain domain, | |
438 const StringSet& set) { | |
439 std::string policy_type; | |
440 if (ComponentCloudPolicyStore::GetPolicyType(domain, &policy_type)) { | |
441 for (StringSet::const_iterator it = set.begin(); it != set.end(); ++it) | |
442 client_->RemoveNamespaceToFetch(PolicyNamespaceKey(policy_type, *it)); | |
443 } | |
444 } | |
445 | |
446 } // namespace policy | |
OLD | NEW |