component_cloud_policy_service.cc revision 116680a4aac90f2aa7413d9095a592090648e557
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
26namespace em = enterprise_management;
27
28namespace policy {
29
30namespace {
31
32bool 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
38bool 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
47ComponentCloudPolicyService::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.
52class 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
108ComponentCloudPolicyService::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
122ComponentCloudPolicyService::Backend::~Backend() {}
123
124void 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
135void 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
164void ComponentCloudPolicyService::Backend::UpdateExternalPolicy(
165    scoped_ptr<em::PolicyFetchResponse> response) {
166  updater_->UpdateExternalPolicy(response.Pass());
167}
168
169void ComponentCloudPolicyService::Backend::
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
185void 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
202ComponentCloudPolicyService::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
249ComponentCloudPolicyService::~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
264bool ComponentCloudPolicyService::SupportsDomain(PolicyDomain domain) {
265  return ComponentCloudPolicyStore::SupportsDomain(domain);
266}
267
268void 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
277void ComponentCloudPolicyService::OnSchemaRegistryReady() {
278  DCHECK(CalledOnValidThread());
279  InitializeIfReady();
280}
281
282void 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
300void 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
316void 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
327void ComponentCloudPolicyService::OnRefreshSchedulerStarted(
328    CloudPolicyCore* core) {
329  // Ignored.
330}
331
332void 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
379void ComponentCloudPolicyService::OnStoreError(CloudPolicyStore* store) {
380  DCHECK(CalledOnValidThread());
381  OnStoreLoaded(store);
382}
383
384void 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
415void ComponentCloudPolicyService::OnRegistrationStateChanged(
416    CloudPolicyClient* client) {
417  DCHECK(CalledOnValidThread());
418  // Ignored; the registration state is tracked by looking at the
419  // CloudPolicyStore instead.
420}
421
422void ComponentCloudPolicyService::OnClientError(CloudPolicyClient* client) {
423  DCHECK(CalledOnValidThread());
424  // Ignored.
425}
426
427void 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
443void 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
458void 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
486void 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