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