Home | History | Annotate | Download | only in cloud
      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 
     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
    516