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/cloud/component_cloud_policy_service.h" | |
6 | |
7 #include <string> | |
8 | |
9 #include "base/bind.h" | |
10 #include "base/bind_helpers.h" | |
11 #include "base/location.h" | |
12 #include "base/logging.h" | |
13 #include "base/message_loop/message_loop_proxy.h" | |
14 #include "base/sequenced_task_runner.h" | |
15 #include "chrome/browser/policy/cloud/cloud_policy_constants.h" | |
16 #include "chrome/browser/policy/cloud/cloud_policy_refresh_scheduler.h" | |
17 #include "chrome/browser/policy/cloud/component_cloud_policy_store.h" | |
18 #include "chrome/browser/policy/cloud/component_cloud_policy_updater.h" | |
19 #include "chrome/browser/policy/cloud/external_policy_data_fetcher.h" | |
20 #include "chrome/browser/policy/cloud/resource_cache.h" | |
21 #include "chrome/browser/policy/proto/cloud/device_management_backend.pb.h" | |
22 #include "components/policy/core/common/schema.h" | |
23 #include "components/policy/core/common/schema_map.h" | |
24 #include "net/url_request/url_request_context_getter.h" | |
25 | |
26 namespace em = enterprise_management; | |
27 | |
28 namespace policy { | |
29 | |
30 namespace { | |
31 | |
32 bool NotInSchemaMap(const scoped_refptr<SchemaMap> schema_map, | |
33 PolicyDomain domain, | |
34 const std::string& component_id) { | |
35 return schema_map->GetSchema(PolicyNamespace(domain, component_id)) == NULL; | |
36 } | |
37 | |
38 bool ToPolicyNamespaceKey(const PolicyNamespace& ns, PolicyNamespaceKey* key) { | |
39 if (!ComponentCloudPolicyStore::GetPolicyType(ns.domain, &key->first)) | |
40 return false; | |
41 key->second = ns.component_id; | |
42 return true; | |
43 } | |
44 | |
45 bool ToPolicyNamespace(const PolicyNamespaceKey& key, PolicyNamespace* ns) { | |
46 if (!ComponentCloudPolicyStore::GetPolicyDomain(key.first, &ns->domain)) | |
47 return false; | |
48 ns->component_id = key.second; | |
49 return true; | |
50 } | |
51 | |
52 } // namespace | |
53 | |
54 ComponentCloudPolicyService::Delegate::~Delegate() {} | |
55 | |
56 // Owns the objects that live on the background thread, and posts back to the | |
57 // thread that the ComponentCloudPolicyService runs on whenever the policy | |
58 // changes. | |
59 class ComponentCloudPolicyService::Backend | |
60 : public ComponentCloudPolicyStore::Delegate { | |
61 public: | |
62 // This class can be instantiated on any thread but from then on, may be | |
63 // accessed via the |task_runner_| only. Policy changes are posted to the | |
64 // |service| via the |service_task_runner|. The |cache| is used to load and | |
65 // store local copies of the downloaded policies. | |
66 Backend(base::WeakPtr<ComponentCloudPolicyService> service, | |
67 scoped_refptr<base::SequencedTaskRunner> task_runner, | |
68 scoped_refptr<base::SequencedTaskRunner> service_task_runner, | |
69 scoped_ptr<ResourceCache> cache, | |
70 scoped_ptr<ExternalPolicyDataFetcher> external_policy_data_fetcher); | |
71 | |
72 virtual ~Backend(); | |
73 | |
74 // |username| and |dm_token| will be used to validate the cached policies. | |
75 void SetCredentials(const std::string& username, const std::string& dm_token); | |
76 | |
77 // Loads the |store_| and starts downloading updates. | |
78 void Init(scoped_refptr<SchemaMap> schema_map); | |
79 | |
80 // Passes a policy protobuf to the backend, to start its validation and | |
81 // eventual download of the policy data on the background thread. | |
82 void UpdateExternalPolicy(scoped_ptr<em::PolicyFetchResponse> response); | |
83 | |
84 // ComponentCloudPolicyStore::Delegate implementation: | |
85 virtual void OnComponentCloudPolicyStoreUpdated() OVERRIDE; | |
86 | |
87 // Passes the current SchemaMap so that the disk cache can purge components | |
88 // that aren't being tracked anymore. | |
89 // |removed| is a list of namespaces that were present in the previous | |
90 // schema and have been removed in the updated version. | |
91 void OnSchemasUpdated(scoped_refptr<SchemaMap> schema_map, | |
92 scoped_ptr<PolicyNamespaceList> removed); | |
93 | |
94 private: | |
95 // The ComponentCloudPolicyService that owns |this|. Used to inform the | |
96 // |service_| when policy changes. | |
97 base::WeakPtr<ComponentCloudPolicyService> service_; | |
98 | |
99 // The thread that |this| runs on. Used to post tasks to be run by |this|. | |
100 scoped_refptr<base::SequencedTaskRunner> task_runner_; | |
101 | |
102 // The thread that the |service_| runs on. Used to post policy changes to the | |
103 // right thread. | |
104 scoped_refptr<base::SequencedTaskRunner> service_task_runner_; | |
105 | |
106 scoped_ptr<ResourceCache> cache_; | |
107 scoped_ptr<ExternalPolicyDataFetcher> external_policy_data_fetcher_; | |
108 ComponentCloudPolicyStore store_; | |
109 scoped_ptr<ComponentCloudPolicyUpdater> updater_; | |
110 scoped_refptr<SchemaMap> schema_map_; | |
111 | |
112 DISALLOW_COPY_AND_ASSIGN(Backend); | |
113 }; | |
114 | |
115 ComponentCloudPolicyService::Backend::Backend( | |
116 base::WeakPtr<ComponentCloudPolicyService> service, | |
117 scoped_refptr<base::SequencedTaskRunner> task_runner, | |
118 scoped_refptr<base::SequencedTaskRunner> service_task_runner, | |
119 scoped_ptr<ResourceCache> cache, | |
120 scoped_ptr<ExternalPolicyDataFetcher> external_policy_data_fetcher) | |
121 : service_(service), | |
122 task_runner_(task_runner), | |
123 service_task_runner_(service_task_runner), | |
124 cache_(cache.Pass()), | |
125 external_policy_data_fetcher_(external_policy_data_fetcher.Pass()), | |
126 store_(this, cache_.get()) {} | |
127 | |
128 ComponentCloudPolicyService::Backend::~Backend() {} | |
129 | |
130 void ComponentCloudPolicyService::Backend::SetCredentials( | |
131 const std::string& username, | |
132 const std::string& dm_token) { | |
133 if (username.empty() || dm_token.empty()) { | |
134 // No sign-in credentials, so drop any cached policy. | |
135 store_.Clear(); | |
136 } else { | |
137 store_.SetCredentials(username, dm_token); | |
138 } | |
139 } | |
140 | |
141 void ComponentCloudPolicyService::Backend::Init( | |
142 scoped_refptr<SchemaMap> schema_map) { | |
143 DCHECK(!schema_map_); | |
144 | |
145 OnSchemasUpdated(schema_map, scoped_ptr<PolicyNamespaceList>()); | |
146 | |
147 // Read the initial policy. Note that this does not trigger notifications | |
148 // through OnComponentCloudPolicyStoreUpdated. Note also that the cached | |
149 // data may contain names or values that don't match the schema for that | |
150 // component; the data must be cached without modifications so that its | |
151 // integrity can be verified using the hash, but it must also be filtered | |
152 // right after a Load(). | |
153 store_.Load(); | |
154 scoped_ptr<PolicyBundle> bundle(new PolicyBundle); | |
155 bundle->CopyFrom(store_.policy()); | |
156 schema_map_->FilterBundle(bundle.get()); | |
157 | |
158 // Start downloading any pending data. | |
159 updater_.reset(new ComponentCloudPolicyUpdater( | |
160 task_runner_, external_policy_data_fetcher_.Pass(), &store_)); | |
161 | |
162 service_task_runner_->PostTask( | |
163 FROM_HERE, | |
164 base::Bind(&ComponentCloudPolicyService::OnBackendInitialized, | |
165 service_, | |
166 base::Passed(&bundle))); | |
167 } | |
168 | |
169 void ComponentCloudPolicyService::Backend::UpdateExternalPolicy( | |
170 scoped_ptr<em::PolicyFetchResponse> response) { | |
171 updater_->UpdateExternalPolicy(response.Pass()); | |
172 } | |
173 | |
174 void ComponentCloudPolicyService::Backend:: | |
175 OnComponentCloudPolicyStoreUpdated() { | |
176 if (!schema_map_) { | |
177 // Ignore notifications triggered by the initial Purge or Clear. | |
178 return; | |
179 } | |
180 | |
181 scoped_ptr<PolicyBundle> bundle(new PolicyBundle); | |
182 bundle->CopyFrom(store_.policy()); | |
183 schema_map_->FilterBundle(bundle.get()); | |
184 service_task_runner_->PostTask( | |
185 FROM_HERE, | |
186 base::Bind(&ComponentCloudPolicyService::OnPolicyUpdated, | |
187 service_, | |
188 base::Passed(&bundle))); | |
189 } | |
190 | |
191 void ComponentCloudPolicyService::Backend::OnSchemasUpdated( | |
192 scoped_refptr<SchemaMap> schema_map, | |
193 scoped_ptr<PolicyNamespaceList> removed) { | |
194 // Purge any components that have been removed. | |
195 const DomainMap& domains = schema_map->GetDomains(); | |
196 for (DomainMap::const_iterator domain = domains.begin(); | |
197 domain != domains.end(); ++domain) { | |
198 store_.Purge(domain->first, | |
199 base::Bind(&NotInSchemaMap, schema_map, domain->first)); | |
200 } | |
201 | |
202 // Set |schema_map_| after purging so that the notifications from the store | |
203 // are ignored on the first OnSchemasUpdated() call from Init(). | |
204 schema_map_ = schema_map; | |
205 | |
206 if (removed) { | |
207 for (size_t i = 0; i < removed->size(); ++i) | |
208 updater_->CancelUpdate((*removed)[i]); | |
209 } | |
210 } | |
211 | |
212 ComponentCloudPolicyService::ComponentCloudPolicyService( | |
213 Delegate* delegate, | |
214 SchemaRegistry* schema_registry, | |
215 CloudPolicyCore* core, | |
216 scoped_ptr<ResourceCache> cache, | |
217 scoped_refptr<net::URLRequestContextGetter> request_context, | |
218 scoped_refptr<base::SequencedTaskRunner> backend_task_runner, | |
219 scoped_refptr<base::SequencedTaskRunner> io_task_runner) | |
220 : delegate_(delegate), | |
221 schema_registry_(schema_registry), | |
222 core_(core), | |
223 request_context_(request_context), | |
224 backend_task_runner_(backend_task_runner), | |
225 io_task_runner_(io_task_runner), | |
226 current_schema_map_(new SchemaMap), | |
227 started_loading_initial_policy_(false), | |
228 loaded_initial_policy_(false), | |
229 is_registered_for_cloud_policy_(false), | |
230 weak_ptr_factory_(this) { | |
231 external_policy_data_fetcher_backend_.reset( | |
232 new ExternalPolicyDataFetcherBackend(io_task_runner_, request_context)); | |
233 | |
234 backend_.reset( | |
235 new Backend(weak_ptr_factory_.GetWeakPtr(), | |
236 backend_task_runner_, | |
237 base::MessageLoopProxy::current(), | |
238 cache.Pass(), | |
239 external_policy_data_fetcher_backend_->CreateFrontend( | |
240 backend_task_runner_))); | |
241 | |
242 schema_registry_->AddObserver(this); | |
243 core_->store()->AddObserver(this); | |
244 | |
245 // Wait for the store and the schema registry to become ready before | |
246 // initializing the backend, so that it can get the initial list of | |
247 // components and the cached credentials (if any) to validate the cached | |
248 // policies. | |
249 if (core_->store()->is_initialized()) | |
250 OnStoreLoaded(core_->store()); | |
251 } | |
252 | |
253 ComponentCloudPolicyService::~ComponentCloudPolicyService() { | |
254 DCHECK(CalledOnValidThread()); | |
255 | |
256 schema_registry_->RemoveObserver(this); | |
257 core_->store()->RemoveObserver(this); | |
258 core_->RemoveObserver(this); | |
259 if (core_->client()) | |
260 OnCoreDisconnecting(core_); | |
261 | |
262 io_task_runner_->DeleteSoon(FROM_HERE, | |
263 external_policy_data_fetcher_backend_.release()); | |
264 backend_task_runner_->DeleteSoon(FROM_HERE, backend_.release()); | |
265 } | |
266 | |
267 // static | |
268 bool ComponentCloudPolicyService::SupportsDomain(PolicyDomain domain) { | |
269 return ComponentCloudPolicyStore::SupportsDomain(domain); | |
270 } | |
271 | |
272 void ComponentCloudPolicyService::ClearCache() { | |
273 DCHECK(CalledOnValidThread()); | |
274 // Empty credentials will wipe the cache. | |
275 backend_task_runner_->PostTask(FROM_HERE, | |
276 base::Bind(&Backend::SetCredentials, | |
277 base::Unretained(backend_.get()), | |
278 std::string(), std::string())); | |
279 } | |
280 | |
281 void ComponentCloudPolicyService::OnSchemaRegistryReady() { | |
282 DCHECK(CalledOnValidThread()); | |
283 InitializeIfReady(); | |
284 } | |
285 | |
286 void ComponentCloudPolicyService::OnSchemaRegistryUpdated( | |
287 bool has_new_schemas) { | |
288 DCHECK(CalledOnValidThread()); | |
289 | |
290 // Ignore schema updates until the backend is initialized. | |
291 // OnBackendInitialized() will send the current schema to the backend again, | |
292 // in case it was updated before the backend initialized. | |
293 if (!loaded_initial_policy_) | |
294 return; | |
295 | |
296 SetCurrentSchema(); | |
297 } | |
298 | |
299 void ComponentCloudPolicyService::OnCoreConnected(CloudPolicyCore* core) { | |
300 DCHECK(CalledOnValidThread()); | |
301 DCHECK_EQ(core_, core); | |
302 | |
303 core_->client()->AddObserver(this); | |
304 | |
305 // Immediately load any PolicyFetchResponses that the client may already | |
306 // have. | |
307 OnPolicyFetched(core_->client()); | |
308 | |
309 // Register the current namespaces at the client. | |
310 current_schema_map_ = new SchemaMap(); | |
311 SetCurrentSchema(); | |
312 } | |
313 | |
314 void ComponentCloudPolicyService::OnCoreDisconnecting(CloudPolicyCore* core) { | |
315 DCHECK(CalledOnValidThread()); | |
316 DCHECK_EQ(core_, core); | |
317 | |
318 core_->client()->RemoveObserver(this); | |
319 | |
320 // Remove all the namespaces from the client. | |
321 scoped_refptr<SchemaMap> empty = new SchemaMap(); | |
322 PolicyNamespaceList removed; | |
323 PolicyNamespaceList added; | |
324 empty->GetChanges(current_schema_map_, &removed, &added); | |
325 for (size_t i = 0; i < removed.size(); ++i) { | |
326 PolicyNamespaceKey key; | |
327 if (ToPolicyNamespaceKey(removed[i], &key)) | |
328 core_->client()->RemoveNamespaceToFetch(key); | |
329 } | |
330 } | |
331 | |
332 void ComponentCloudPolicyService::OnRefreshSchedulerStarted( | |
333 CloudPolicyCore* core) { | |
334 // Ignored. | |
335 } | |
336 | |
337 void ComponentCloudPolicyService::OnStoreLoaded(CloudPolicyStore* store) { | |
338 DCHECK(CalledOnValidThread()); | |
339 DCHECK_EQ(core_->store(), store); | |
340 | |
341 const bool was_registered_before = is_registered_for_cloud_policy_; | |
342 | |
343 // Send the current credentials to the backend; do this whenever the store | |
344 // updates, to handle the case of the user registering for policy after the | |
345 // session starts, or the user signing out. | |
346 const em::PolicyData* policy = core_->store()->policy(); | |
347 std::string username; | |
348 std::string request_token; | |
349 if (policy && policy->has_username() && policy->has_request_token()) { | |
350 is_registered_for_cloud_policy_ = true; | |
351 username = policy->username(); | |
352 request_token = policy->request_token(); | |
353 } else { | |
354 is_registered_for_cloud_policy_ = false; | |
355 } | |
356 | |
357 // Empty credentials will wipe the cache. | |
358 backend_task_runner_->PostTask(FROM_HERE, | |
359 base::Bind(&Backend::SetCredentials, | |
360 base::Unretained(backend_.get()), | |
361 username, | |
362 request_token)); | |
363 | |
364 if (!loaded_initial_policy_) { | |
365 // This is the initial load; check if we're ready to initialize the | |
366 // backend, regardless of the signin state. | |
367 InitializeIfReady(); | |
368 } else if (!was_registered_before && is_registered_for_cloud_policy_) { | |
369 // We are already initialized, but just sent credentials to the backend for | |
370 // the first time; this means that the user was not registered for cloud | |
371 // policy on startup but registered during the session. | |
372 // | |
373 // When that happens, OnPolicyFetched() is sent to observers before the | |
374 // CloudPolicyStore gets a chance to verify the user policy. In those cases, | |
375 // the backend gets the PolicyFetchResponses before it has the credentials | |
376 // and therefore the validation of those responses fails. | |
377 // Reload any PolicyFetchResponses that the client may have now so that | |
378 // validation is retried with the credentials in place. | |
379 if (core_->client()) | |
380 OnPolicyFetched(core_->client()); | |
381 } | |
382 } | |
383 | |
384 void ComponentCloudPolicyService::OnStoreError(CloudPolicyStore* store) { | |
385 DCHECK(CalledOnValidThread()); | |
386 OnStoreLoaded(store); | |
387 } | |
388 | |
389 void ComponentCloudPolicyService::OnPolicyFetched(CloudPolicyClient* client) { | |
390 DCHECK(CalledOnValidThread()); | |
391 DCHECK_EQ(core_->client(), client); | |
392 | |
393 if (!is_registered_for_cloud_policy_) { | |
394 // Trying to load any policies now will fail validation. An OnStoreLoaded() | |
395 // notification should follow soon, after the main user policy has been | |
396 // validated and stored. | |
397 return; | |
398 } | |
399 | |
400 // Pass each PolicyFetchResponse whose policy type is registered to the | |
401 // Backend. | |
402 const CloudPolicyClient::ResponseMap& responses = | |
403 core_->client()->responses(); | |
404 for (CloudPolicyClient::ResponseMap::const_iterator it = responses.begin(); | |
405 it != responses.end(); ++it) { | |
406 PolicyNamespace ns; | |
407 if (ToPolicyNamespace(it->first, &ns) && | |
408 current_schema_map_->GetSchema(ns)) { | |
409 scoped_ptr<em::PolicyFetchResponse> response( | |
410 new em::PolicyFetchResponse(*it->second)); | |
411 backend_task_runner_->PostTask( | |
412 FROM_HERE, | |
413 base::Bind(&Backend::UpdateExternalPolicy, | |
414 base::Unretained(backend_.get()), | |
415 base::Passed(&response))); | |
416 } | |
417 } | |
418 } | |
419 | |
420 void ComponentCloudPolicyService::OnRegistrationStateChanged( | |
421 CloudPolicyClient* client) { | |
422 DCHECK(CalledOnValidThread()); | |
423 // Ignored; the registration state is tracked by looking at the | |
424 // CloudPolicyStore instead. | |
425 } | |
426 | |
427 void ComponentCloudPolicyService::OnClientError(CloudPolicyClient* client) { | |
428 DCHECK(CalledOnValidThread()); | |
429 // Ignored. | |
430 } | |
431 | |
432 void ComponentCloudPolicyService::InitializeIfReady() { | |
433 DCHECK(CalledOnValidThread()); | |
434 if (started_loading_initial_policy_ || !schema_registry_->IsReady() || | |
435 !core_->store()->is_initialized()) { | |
436 return; | |
437 } | |
438 // The initial list of components is ready. Initialize the backend now, which | |
439 // will call back to OnBackendInitialized. | |
440 backend_task_runner_->PostTask(FROM_HERE, | |
441 base::Bind(&Backend::Init, | |
442 base::Unretained(backend_.get()), | |
443 schema_registry_->schema_map())); | |
444 started_loading_initial_policy_ = true; | |
445 } | |
446 | |
447 void ComponentCloudPolicyService::OnBackendInitialized( | |
448 scoped_ptr<PolicyBundle> initial_policy) { | |
449 DCHECK(CalledOnValidThread()); | |
450 DCHECK(!loaded_initial_policy_); | |
451 | |
452 loaded_initial_policy_ = true; | |
453 | |
454 // We're now ready to serve the initial policy; notify the policy observers. | |
455 OnPolicyUpdated(initial_policy.Pass()); | |
456 | |
457 // Start observing the core and tracking the state of the client. | |
458 core_->AddObserver(this); | |
459 | |
460 if (core_->client()) { | |
461 OnCoreConnected(core_); | |
462 } else { | |
463 // Send the current schema to the backend, in case it has changed while the | |
464 // backend was initializing. OnCoreConnected() also does this if a client is | |
465 // already connected. | |
466 SetCurrentSchema(); | |
467 } | |
468 } | |
469 | |
470 void ComponentCloudPolicyService::SetCurrentSchema() { | |
471 DCHECK(CalledOnValidThread()); | |
472 | |
473 scoped_ptr<PolicyNamespaceList> removed(new PolicyNamespaceList); | |
474 PolicyNamespaceList added; | |
475 const scoped_refptr<SchemaMap>& new_schema_map = | |
476 schema_registry_->schema_map(); | |
477 new_schema_map->GetChanges(current_schema_map_, removed.get(), &added); | |
478 | |
479 current_schema_map_ = new_schema_map; | |
480 | |
481 if (core_->client()) { | |
482 for (size_t i = 0; i < removed->size(); ++i) { | |
483 PolicyNamespaceKey key; | |
484 if (ToPolicyNamespaceKey((*removed)[i], &key)) | |
485 core_->client()->RemoveNamespaceToFetch(key); | |
486 } | |
487 | |
488 bool added_namespaces_to_client = false; | |
489 for (size_t i = 0; i < added.size(); ++i) { | |
490 PolicyNamespaceKey key; | |
491 if (ToPolicyNamespaceKey(added[i], &key)) { | |
492 core_->client()->AddNamespaceToFetch(key); | |
493 added_namespaces_to_client = true; | |
494 } | |
495 } | |
496 | |
497 if (added_namespaces_to_client) | |
498 core_->RefreshSoon(); | |
499 } | |
500 | |
501 backend_task_runner_->PostTask(FROM_HERE, | |
502 base::Bind(&Backend::OnSchemasUpdated, | |
503 base::Unretained(backend_.get()), | |
504 current_schema_map_, | |
505 base::Passed(&removed))); | |
506 } | |
507 | |
508 void ComponentCloudPolicyService::OnPolicyUpdated( | |
509 scoped_ptr<PolicyBundle> policy) { | |
510 DCHECK(CalledOnValidThread()); | |
511 policy_.Swap(policy.get()); | |
512 delegate_->OnComponentCloudPolicyUpdated(); | |
513 } | |
514 | |
515 } // namespace policy | |
OLD | NEW |