component_cloud_policy_service.cc revision 5c02ac1a9c1b504631c0a3d2b6e737b5d738bae1
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  scoped_refptr<SchemaMap> schema_map_;
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
121ComponentCloudPolicyService::Backend::~Backend() {}
122
123void ComponentCloudPolicyService::Backend::SetCredentials(
124    const std::string& username,
125    const std::string& dm_token) {
126  if (username.empty() || dm_token.empty()) {
127    // No sign-in credentials, so drop any cached policy.
128    store_.Clear();
129  } else {
130    store_.SetCredentials(username, dm_token);
131  }
132}
133
134void ComponentCloudPolicyService::Backend::Init(
135    scoped_refptr<SchemaMap> schema_map) {
136  DCHECK(!schema_map_);
137
138  OnSchemasUpdated(schema_map, scoped_ptr<PolicyNamespaceList>());
139
140  // Read the initial policy. Note that this does not trigger notifications
141  // through OnComponentCloudPolicyStoreUpdated. Note also that the cached
142  // data may contain names or values that don't match the schema for that
143  // component; the data must be cached without modifications so that its
144  // integrity can be verified using the hash, but it must also be filtered
145  // right after a Load().
146  store_.Load();
147  scoped_ptr<PolicyBundle> bundle(new PolicyBundle);
148  bundle->CopyFrom(store_.policy());
149  schema_map_->FilterBundle(bundle.get());
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
162void ComponentCloudPolicyService::Backend::UpdateExternalPolicy(
163    scoped_ptr<em::PolicyFetchResponse> response) {
164  updater_->UpdateExternalPolicy(response.Pass());
165}
166
167void ComponentCloudPolicyService::Backend::
168    OnComponentCloudPolicyStoreUpdated() {
169  if (!schema_map_) {
170    // Ignore notifications triggered by the initial Purge or Clear.
171    return;
172  }
173
174  scoped_ptr<PolicyBundle> bundle(new PolicyBundle);
175  bundle->CopyFrom(store_.policy());
176  schema_map_->FilterBundle(bundle.get());
177  service_task_runner_->PostTask(
178      FROM_HERE,
179      base::Bind(&ComponentCloudPolicyService::OnPolicyUpdated,
180                 service_,
181                 base::Passed(&bundle)));
182}
183
184void ComponentCloudPolicyService::Backend::OnSchemasUpdated(
185    scoped_refptr<SchemaMap> schema_map,
186    scoped_ptr<PolicyNamespaceList> removed) {
187  // Purge any components that have been removed.
188  const DomainMap& domains = schema_map->GetDomains();
189  for (DomainMap::const_iterator domain = domains.begin();
190       domain != domains.end(); ++domain) {
191    store_.Purge(domain->first,
192                 base::Bind(&NotInSchemaMap, schema_map, domain->first));
193  }
194
195  // Set |schema_map_| after purging so that the notifications from the store
196  // are ignored on the first OnSchemasUpdated() call from Init().
197  schema_map_ = schema_map;
198
199  if (removed) {
200    for (size_t i = 0; i < removed->size(); ++i)
201      updater_->CancelUpdate((*removed)[i]);
202  }
203}
204
205ComponentCloudPolicyService::ComponentCloudPolicyService(
206    Delegate* delegate,
207    SchemaRegistry* schema_registry,
208    CloudPolicyCore* core,
209    scoped_ptr<ResourceCache> cache,
210    scoped_refptr<net::URLRequestContextGetter> request_context,
211    scoped_refptr<base::SequencedTaskRunner> backend_task_runner,
212    scoped_refptr<base::SequencedTaskRunner> io_task_runner)
213    : delegate_(delegate),
214      schema_registry_(schema_registry),
215      core_(core),
216      request_context_(request_context),
217      backend_task_runner_(backend_task_runner),
218      io_task_runner_(io_task_runner),
219      current_schema_map_(new SchemaMap),
220      started_loading_initial_policy_(false),
221      loaded_initial_policy_(false),
222      is_registered_for_cloud_policy_(false),
223      weak_ptr_factory_(this) {
224  external_policy_data_fetcher_backend_.reset(
225      new ExternalPolicyDataFetcherBackend(io_task_runner_, request_context));
226
227  backend_.reset(
228      new Backend(weak_ptr_factory_.GetWeakPtr(),
229                  backend_task_runner_,
230                  base::MessageLoopProxy::current(),
231                  cache.Pass(),
232                  external_policy_data_fetcher_backend_->CreateFrontend(
233                      backend_task_runner_)));
234
235  schema_registry_->AddObserver(this);
236  core_->store()->AddObserver(this);
237
238  // Wait for the store and the schema registry to become ready before
239  // initializing the backend, so that it can get the initial list of
240  // components and the cached credentials (if any) to validate the cached
241  // policies.
242  if (core_->store()->is_initialized())
243    OnStoreLoaded(core_->store());
244}
245
246ComponentCloudPolicyService::~ComponentCloudPolicyService() {
247  DCHECK(CalledOnValidThread());
248
249  schema_registry_->RemoveObserver(this);
250  core_->store()->RemoveObserver(this);
251  core_->RemoveObserver(this);
252  if (core_->client())
253    OnCoreDisconnecting(core_);
254
255  io_task_runner_->DeleteSoon(FROM_HERE,
256                              external_policy_data_fetcher_backend_.release());
257  backend_task_runner_->DeleteSoon(FROM_HERE, backend_.release());
258}
259
260// static
261bool ComponentCloudPolicyService::SupportsDomain(PolicyDomain domain) {
262  return ComponentCloudPolicyStore::SupportsDomain(domain);
263}
264
265void ComponentCloudPolicyService::ClearCache() {
266  DCHECK(CalledOnValidThread());
267  // Empty credentials will wipe the cache.
268  backend_task_runner_->PostTask(FROM_HERE,
269                                 base::Bind(&Backend::SetCredentials,
270                                            base::Unretained(backend_.get()),
271                                            std::string(), std::string()));
272}
273
274void ComponentCloudPolicyService::OnSchemaRegistryReady() {
275  DCHECK(CalledOnValidThread());
276  InitializeIfReady();
277}
278
279void ComponentCloudPolicyService::OnSchemaRegistryUpdated(
280    bool has_new_schemas) {
281  DCHECK(CalledOnValidThread());
282
283  // Ignore schema updates until the backend is initialized.
284  // OnBackendInitialized() will send the current schema to the backend again,
285  // in case it was updated before the backend initialized.
286  if (!loaded_initial_policy_)
287    return;
288
289  ReloadSchema();
290}
291
292void ComponentCloudPolicyService::OnCoreConnected(CloudPolicyCore* core) {
293  DCHECK(CalledOnValidThread());
294  DCHECK_EQ(core_, core);
295
296  core_->client()->AddObserver(this);
297
298  // Register the supported policy domains at the client.
299  core_->client()->AddNamespaceToFetch(
300      PolicyNamespaceKey(dm_protocol::kChromeExtensionPolicyType, ""));
301
302  // Immediately load any PolicyFetchResponses that the client may already
303  // have.
304  OnPolicyFetched(core_->client());
305}
306
307void ComponentCloudPolicyService::OnCoreDisconnecting(CloudPolicyCore* core) {
308  DCHECK(CalledOnValidThread());
309  DCHECK_EQ(core_, core);
310
311  core_->client()->RemoveObserver(this);
312
313  // Remove all the namespaces from the client.
314  core_->client()->RemoveNamespaceToFetch(
315      PolicyNamespaceKey(dm_protocol::kChromeExtensionPolicyType, ""));
316}
317
318void ComponentCloudPolicyService::OnRefreshSchedulerStarted(
319    CloudPolicyCore* core) {
320  // Ignored.
321}
322
323void ComponentCloudPolicyService::OnStoreLoaded(CloudPolicyStore* store) {
324  DCHECK(CalledOnValidThread());
325  DCHECK_EQ(core_->store(), store);
326
327  const bool was_registered_before = is_registered_for_cloud_policy_;
328
329  // Send the current credentials to the backend; do this whenever the store
330  // updates, to handle the case of the user registering for policy after the
331  // session starts, or the user signing out.
332  const em::PolicyData* policy = core_->store()->policy();
333  std::string username;
334  std::string request_token;
335  if (policy && policy->has_username() && policy->has_request_token()) {
336    is_registered_for_cloud_policy_ = true;
337    username = policy->username();
338    request_token = policy->request_token();
339  } else {
340    is_registered_for_cloud_policy_ = false;
341  }
342
343  // Empty credentials will wipe the cache.
344  backend_task_runner_->PostTask(FROM_HERE,
345                                 base::Bind(&Backend::SetCredentials,
346                                            base::Unretained(backend_.get()),
347                                            username,
348                                            request_token));
349
350  if (!loaded_initial_policy_) {
351    // This is the initial load; check if we're ready to initialize the
352    // backend, regardless of the signin state.
353    InitializeIfReady();
354  } else if (!was_registered_before && is_registered_for_cloud_policy_) {
355    // We are already initialized, but just sent credentials to the backend for
356    // the first time; this means that the user was not registered for cloud
357    // policy on startup but registered during the session.
358    //
359    // When that happens, OnPolicyFetched() is sent to observers before the
360    // CloudPolicyStore gets a chance to verify the user policy. In those cases,
361    // the backend gets the PolicyFetchResponses before it has the credentials
362    // and therefore the validation of those responses fails.
363    // Reload any PolicyFetchResponses that the client may have now so that
364    // validation is retried with the credentials in place.
365    if (core_->client())
366      OnPolicyFetched(core_->client());
367  }
368}
369
370void ComponentCloudPolicyService::OnStoreError(CloudPolicyStore* store) {
371  DCHECK(CalledOnValidThread());
372  OnStoreLoaded(store);
373}
374
375void ComponentCloudPolicyService::OnPolicyFetched(CloudPolicyClient* client) {
376  DCHECK(CalledOnValidThread());
377  DCHECK_EQ(core_->client(), client);
378
379  if (!is_registered_for_cloud_policy_) {
380    // Trying to load any policies now will fail validation. An OnStoreLoaded()
381    // notification should follow soon, after the main user policy has been
382    // validated and stored.
383    return;
384  }
385
386  // Pass each PolicyFetchResponse whose policy type is registered to the
387  // Backend.
388  const CloudPolicyClient::ResponseMap& responses =
389      core_->client()->responses();
390  for (CloudPolicyClient::ResponseMap::const_iterator it = responses.begin();
391       it != responses.end(); ++it) {
392    PolicyNamespace ns;
393    if (ToPolicyNamespace(it->first, &ns) &&
394        current_schema_map_->GetSchema(ns)) {
395      scoped_ptr<em::PolicyFetchResponse> response(
396          new em::PolicyFetchResponse(*it->second));
397      backend_task_runner_->PostTask(
398          FROM_HERE,
399          base::Bind(&Backend::UpdateExternalPolicy,
400                     base::Unretained(backend_.get()),
401                     base::Passed(&response)));
402    }
403  }
404}
405
406void ComponentCloudPolicyService::OnRegistrationStateChanged(
407    CloudPolicyClient* client) {
408  DCHECK(CalledOnValidThread());
409  // Ignored; the registration state is tracked by looking at the
410  // CloudPolicyStore instead.
411}
412
413void ComponentCloudPolicyService::OnClientError(CloudPolicyClient* client) {
414  DCHECK(CalledOnValidThread());
415  // Ignored.
416}
417
418void ComponentCloudPolicyService::InitializeIfReady() {
419  DCHECK(CalledOnValidThread());
420  if (started_loading_initial_policy_ || !schema_registry_->IsReady() ||
421      !core_->store()->is_initialized()) {
422    return;
423  }
424  // The initial list of components is ready. Initialize the backend now, which
425  // will call back to OnBackendInitialized.
426  backend_task_runner_->PostTask(FROM_HERE,
427                                 base::Bind(&Backend::Init,
428                                            base::Unretained(backend_.get()),
429                                            schema_registry_->schema_map()));
430  started_loading_initial_policy_ = true;
431}
432
433void ComponentCloudPolicyService::OnBackendInitialized(
434    scoped_ptr<PolicyBundle> initial_policy) {
435  DCHECK(CalledOnValidThread());
436  DCHECK(!loaded_initial_policy_);
437
438  loaded_initial_policy_ = true;
439
440  // We're now ready to serve the initial policy; notify the policy observers.
441  OnPolicyUpdated(initial_policy.Pass());
442
443  // Send the current schema to the backend, in case it has changed while the
444  // backend was initializing.
445  ReloadSchema();
446
447  // Start observing the core and tracking the state of the client.
448  core_->AddObserver(this);
449
450  if (core_->client())
451    OnCoreConnected(core_);
452}
453
454void ComponentCloudPolicyService::ReloadSchema() {
455  DCHECK(CalledOnValidThread());
456
457  scoped_ptr<PolicyNamespaceList> removed(new PolicyNamespaceList);
458  PolicyNamespaceList added;
459  const scoped_refptr<SchemaMap>& new_schema_map =
460      schema_registry_->schema_map();
461  new_schema_map->GetChanges(current_schema_map_, removed.get(), &added);
462
463  current_schema_map_ = new_schema_map;
464
465  // Schedule a policy refresh if a new managed component was added.
466  if (core_->client() && !added.empty())
467    core_->RefreshSoon();
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
478void ComponentCloudPolicyService::OnPolicyUpdated(
479    scoped_ptr<PolicyBundle> policy) {
480  DCHECK(CalledOnValidThread());
481  policy_.Swap(policy.get());
482  delegate_->OnComponentCloudPolicyUpdated();
483}
484
485}  // namespace policy
486