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 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 
     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 
    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 
    122 ComponentCloudPolicyService::Backend::~Backend() {}
    123 
    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 
    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 
    164 void ComponentCloudPolicyService::Backend::UpdateExternalPolicy(
    165     scoped_ptr<em::PolicyFetchResponse> response) {
    166   updater_->UpdateExternalPolicy(response.Pass());
    167 }
    168 
    169 void 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 
    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 
    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 
    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
    264 bool ComponentCloudPolicyService::SupportsDomain(PolicyDomain domain) {
    265   return ComponentCloudPolicyStore::SupportsDomain(domain);
    266 }
    267 
    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 
    277 void ComponentCloudPolicyService::OnSchemaRegistryReady() {
    278   DCHECK(CalledOnValidThread());
    279   InitializeIfReady();
    280 }
    281 
    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 
    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 
    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 
    327 void ComponentCloudPolicyService::OnRefreshSchedulerStarted(
    328     CloudPolicyCore* core) {
    329   // Ignored.
    330 }
    331 
    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 
    379 void ComponentCloudPolicyService::OnStoreError(CloudPolicyStore* store) {
    380   DCHECK(CalledOnValidThread());
    381   OnStoreLoaded(store);
    382 }
    383 
    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 
    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 
    422 void ComponentCloudPolicyService::OnClientError(CloudPolicyClient* client) {
    423   DCHECK(CalledOnValidThread());
    424   // Ignored.
    425 }
    426 
    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 
    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 
    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 
    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