• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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