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 "chrome/browser/policy/cloud/component_cloud_policy_service.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/bind_helpers.h"
      9 #include "base/location.h"
     10 #include "base/logging.h"
     11 #include "base/message_loop/message_loop_proxy.h"
     12 #include "base/pickle.h"
     13 #include "base/sequenced_task_runner.h"
     14 #include "base/stl_util.h"
     15 #include "chrome/browser/policy/cloud/component_cloud_policy_store.h"
     16 #include "chrome/browser/policy/cloud/component_cloud_policy_updater.h"
     17 #include "chrome/browser/policy/cloud/resource_cache.h"
     18 #include "chrome/browser/policy/policy_domain_descriptor.h"
     19 #include "chrome/browser/policy/proto/cloud/device_management_backend.pb.h"
     20 #include "content/public/browser/browser_thread.h"
     21 #include "net/url_request/url_request_context_getter.h"
     22 
     23 namespace em = enterprise_management;
     24 
     25 namespace policy {
     26 
     27 namespace {
     28 
     29 void GetComponentIds(scoped_refptr<const PolicyDomainDescriptor>& descriptor,
     30                      std::set<std::string>* set) {
     31   const PolicyDomainDescriptor::SchemaMap& map = descriptor->components();
     32   for (PolicyDomainDescriptor::SchemaMap::const_iterator it = map.begin();
     33        it != map.end(); ++it) {
     34     set->insert(it->first);
     35   }
     36 }
     37 
     38 }  // namespace
     39 
     40 const char ComponentCloudPolicyService::kComponentNamespaceCache[] =
     41     "component-namespace-cache";
     42 
     43 ComponentCloudPolicyService::Delegate::~Delegate() {}
     44 
     45 // Owns the objects that live on the background thread, and posts back to UI
     46 // to the service whenever the policy changes.
     47 class ComponentCloudPolicyService::Backend
     48     : public ComponentCloudPolicyStore::Delegate {
     49  public:
     50   Backend(base::WeakPtr<ComponentCloudPolicyService> service,
     51           scoped_refptr<base::SequencedTaskRunner> task_runner,
     52           scoped_ptr<ResourceCache> cache);
     53   virtual ~Backend();
     54 
     55   // This is invoked right after the constructor but on the backend background
     56   // thread. Used to create the store on the right thread.
     57   void Init();
     58 
     59   // Reads the initial list of components and the initial policy.
     60   void FinalizeInit();
     61 
     62   // Creates the backend updater.
     63   void Connect(scoped_refptr<net::URLRequestContextGetter> request_context);
     64 
     65   // Stops updating remote data. Cached policies are still served.
     66   void Disconnect();
     67 
     68   // Loads the initial policies from the store. |username| and |dm_token| are
     69   // used to validate the cached policies.
     70   void SetCredentials(const std::string& username, const std::string& dm_token);
     71 
     72   // Passes a policy protobuf to the backend, to start its validation and
     73   // eventual download of the policy data on the background thread.
     74   // This is ignored if the backend isn't connected.
     75   void UpdateExternalPolicy(scoped_ptr<em::PolicyFetchResponse> response);
     76 
     77   // ComponentCloudPolicyStore::Delegate implementation:
     78   virtual void OnComponentCloudPolicyStoreUpdated() OVERRIDE;
     79 
     80   // Passes the current descriptor of a domain, so that the disk cache
     81   // can purge components that aren't being tracked anymore.
     82   void RegisterPolicyDomain(
     83       scoped_refptr<const PolicyDomainDescriptor> descriptor);
     84 
     85  private:
     86   typedef std::map<PolicyDomain, scoped_refptr<const PolicyDomainDescriptor> >
     87       DomainMap;
     88 
     89   scoped_ptr<ComponentMap> ReadCachedComponents();
     90 
     91   base::WeakPtr<ComponentCloudPolicyService> service_;
     92   scoped_refptr<base::SequencedTaskRunner> task_runner_;
     93   scoped_ptr<ResourceCache> cache_;
     94   scoped_ptr<ComponentCloudPolicyStore> store_;
     95   scoped_ptr<ComponentCloudPolicyUpdater> updater_;
     96   DomainMap domain_map_;
     97 
     98   DISALLOW_COPY_AND_ASSIGN(Backend);
     99 };
    100 
    101 ComponentCloudPolicyService::Backend::Backend(
    102     base::WeakPtr<ComponentCloudPolicyService> service,
    103     scoped_refptr<base::SequencedTaskRunner> task_runner,
    104     scoped_ptr<ResourceCache> cache)
    105     : service_(service),
    106       task_runner_(task_runner),
    107       cache_(cache.Pass()) {}
    108 
    109 ComponentCloudPolicyService::Backend::~Backend() {}
    110 
    111 void ComponentCloudPolicyService::Backend::Init() {
    112   DCHECK(!store_);
    113   store_.reset(new ComponentCloudPolicyStore(this, cache_.get()));
    114 }
    115 
    116 void ComponentCloudPolicyService::Backend::FinalizeInit() {
    117   // Read the components that were cached in the last RegisterPolicyDomain()
    118   // calls for each domain.
    119   scoped_ptr<ComponentMap> components = ReadCachedComponents();
    120 
    121   // Read the initial policy.
    122   store_->Load();
    123   scoped_ptr<PolicyBundle> policy(new PolicyBundle);
    124   policy->CopyFrom(store_->policy());
    125 
    126   content::BrowserThread::PostTask(
    127       content::BrowserThread::UI, FROM_HERE,
    128       base::Bind(&ComponentCloudPolicyService::OnBackendInitialized,
    129                  service_,
    130                  base::Passed(&components),
    131                  base::Passed(&policy)));
    132 }
    133 
    134 void ComponentCloudPolicyService::Backend::SetCredentials(
    135     const std::string& username,
    136     const std::string& dm_token) {
    137   store_->SetCredentials(username, dm_token);
    138 }
    139 
    140 void ComponentCloudPolicyService::Backend::Connect(
    141     scoped_refptr<net::URLRequestContextGetter> request_context) {
    142   updater_.reset(new ComponentCloudPolicyUpdater(
    143       task_runner_, request_context, store_.get()));
    144 }
    145 
    146 void ComponentCloudPolicyService::Backend::Disconnect() {
    147   updater_.reset();
    148 }
    149 
    150 void ComponentCloudPolicyService::Backend::UpdateExternalPolicy(
    151     scoped_ptr<em::PolicyFetchResponse> response) {
    152   if (updater_)
    153     updater_->UpdateExternalPolicy(response.Pass());
    154 }
    155 
    156 void ComponentCloudPolicyService::Backend::
    157     OnComponentCloudPolicyStoreUpdated() {
    158   scoped_ptr<PolicyBundle> bundle(new PolicyBundle);
    159   bundle->CopyFrom(store_->policy());
    160   for (DomainMap::iterator it = domain_map_.begin();
    161        it != domain_map_.end(); ++it) {
    162     it->second->FilterBundle(bundle.get());
    163   }
    164 
    165   content::BrowserThread::PostTask(
    166       content::BrowserThread::UI, FROM_HERE,
    167       base::Bind(&ComponentCloudPolicyService::OnPolicyUpdated,
    168                  service_,
    169                  base::Passed(&bundle)));
    170 }
    171 
    172 void ComponentCloudPolicyService::Backend::RegisterPolicyDomain(
    173     scoped_refptr<const PolicyDomainDescriptor> descriptor) {
    174   // Store the current list of components in the cache.
    175   StringSet ids;
    176   std::string policy_type;
    177   if (ComponentCloudPolicyStore::GetPolicyType(descriptor->domain(),
    178                                                &policy_type)) {
    179     GetComponentIds(descriptor, &ids);
    180     Pickle pickle;
    181     for (StringSet::const_iterator it = ids.begin(); it != ids.end(); ++it)
    182       pickle.WriteString(*it);
    183     std::string data(reinterpret_cast<const char*>(pickle.data()),
    184                      pickle.size());
    185     cache_->Store(kComponentNamespaceCache, policy_type, data);
    186   }
    187 
    188   domain_map_[descriptor->domain()] = descriptor;
    189 
    190   // Purge any components that have been removed.
    191   if (store_)
    192     store_->Purge(descriptor->domain(), ids);
    193 }
    194 
    195 scoped_ptr<ComponentCloudPolicyService::ComponentMap>
    196     ComponentCloudPolicyService::Backend::ReadCachedComponents() {
    197   scoped_ptr<ComponentMap> components(new ComponentMap);
    198   std::map<std::string, std::string> contents;
    199   cache_->LoadAllSubkeys(kComponentNamespaceCache, &contents);
    200   for (std::map<std::string, std::string>::iterator it = contents.begin();
    201        it != contents.end(); ++it) {
    202     PolicyDomain domain;
    203     if (ComponentCloudPolicyStore::GetPolicyDomain(it->first, &domain)) {
    204       StringSet& set = (*components)[domain];
    205       const Pickle pickle(it->second.data(), it->second.size());
    206       PickleIterator pickit(pickle);
    207       std::string id;
    208       while (pickit.ReadString(&id))
    209         set.insert(id);
    210     } else {
    211       cache_->Delete(kComponentNamespaceCache, it->first);
    212     }
    213   }
    214   return components.Pass();
    215 }
    216 
    217 ComponentCloudPolicyService::ComponentCloudPolicyService(
    218     Delegate* delegate,
    219     CloudPolicyStore* store,
    220     scoped_ptr<ResourceCache> cache)
    221     : delegate_(delegate),
    222       backend_(NULL),
    223       client_(NULL),
    224       store_(store),
    225       is_initialized_(false),
    226       weak_ptr_factory_(this) {
    227   store_->AddObserver(this);
    228 
    229   // TODO(joaodasilva): this can't currently live on the blocking pool because
    230   // creating URLFetchers requires a MessageLoop.
    231   backend_task_runner_ = content::BrowserThread::GetMessageLoopProxyForThread(
    232       content::BrowserThread::FILE);
    233   backend_ = new Backend(weak_ptr_factory_.GetWeakPtr(),
    234                          backend_task_runner_,
    235                          cache.Pass());
    236   backend_task_runner_->PostTask(
    237       FROM_HERE, base::Bind(&Backend::Init, base::Unretained(backend_)));
    238 
    239   if (store_->is_initialized())
    240     InitializeBackend();
    241 }
    242 
    243 ComponentCloudPolicyService::~ComponentCloudPolicyService() {
    244   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    245   store_->RemoveObserver(this);
    246   if (client_)
    247     client_->RemoveObserver(this);
    248   backend_task_runner_->DeleteSoon(FROM_HERE, backend_);
    249   backend_ = NULL;
    250 }
    251 
    252 // static
    253 bool ComponentCloudPolicyService::SupportsDomain(PolicyDomain domain) {
    254   return ComponentCloudPolicyStore::SupportsDomain(domain);
    255 }
    256 
    257 void ComponentCloudPolicyService::Connect(
    258     CloudPolicyClient* client,
    259     scoped_refptr<net::URLRequestContextGetter> request_context) {
    260   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    261   DCHECK(!client_);
    262   client_ = client;
    263   client_->AddObserver(this);
    264   // Create the updater in the backend.
    265   backend_task_runner_->PostTask(FROM_HERE,
    266                                  base::Bind(&Backend::Connect,
    267                                             base::Unretained(backend_),
    268                                             request_context));
    269   if (is_initialized())
    270     InitializeClient();
    271 }
    272 
    273 void ComponentCloudPolicyService::Disconnect() {
    274   if (client_) {
    275     // Unregister all the current components.
    276     for (ComponentMap::iterator it = registered_components_.begin();
    277          it != registered_components_.end(); ++it) {
    278       RemoveNamespacesToFetch(it->first, it->second);
    279     }
    280 
    281     client_->RemoveObserver(this);
    282     client_ = NULL;
    283 
    284     backend_task_runner_->PostTask(
    285         FROM_HERE,
    286         base::Bind(&Backend::Disconnect, base::Unretained(backend_)));
    287   }
    288 }
    289 
    290 void ComponentCloudPolicyService::RegisterPolicyDomain(
    291     scoped_refptr<const PolicyDomainDescriptor> descriptor) {
    292   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    293   DCHECK(SupportsDomain(descriptor->domain()));
    294 
    295   // Send the new descriptor to the backend, to purge the cache.
    296   backend_task_runner_->PostTask(FROM_HERE,
    297                                  base::Bind(&Backend::RegisterPolicyDomain,
    298                                             base::Unretained(backend_),
    299                                             descriptor));
    300 
    301   // Register the current list of components for |domain| at the |client_|.
    302   StringSet current_ids;
    303   GetComponentIds(descriptor, &current_ids);
    304   StringSet& registered_ids = registered_components_[descriptor->domain()];
    305   if (client_ && is_initialized()) {
    306     if (UpdateClientNamespaces(
    307             descriptor->domain(), registered_ids, current_ids)) {
    308       delegate_->OnComponentCloudPolicyRefreshNeeded();
    309     }
    310   }
    311 
    312   registered_ids = current_ids;
    313 }
    314 
    315 void ComponentCloudPolicyService::OnPolicyFetched(CloudPolicyClient* client) {
    316   DCHECK_EQ(client_, client);
    317   // Pass each PolicyFetchResponse whose policy type is registered to the
    318   // Backend.
    319   const CloudPolicyClient::ResponseMap& responses = client_->responses();
    320   for (CloudPolicyClient::ResponseMap::const_iterator it = responses.begin();
    321        it != responses.end(); ++it) {
    322     const PolicyNamespaceKey& key(it->first);
    323     PolicyDomain domain;
    324     if (ComponentCloudPolicyStore::GetPolicyDomain(key.first, &domain) &&
    325         ContainsKey(registered_components_[domain], key.second)) {
    326       scoped_ptr<em::PolicyFetchResponse> response(
    327           new em::PolicyFetchResponse(*it->second));
    328       backend_task_runner_->PostTask(FROM_HERE,
    329                                      base::Bind(&Backend::UpdateExternalPolicy,
    330                                                 base::Unretained(backend_),
    331                                                 base::Passed(&response)));
    332     }
    333   }
    334 }
    335 
    336 void ComponentCloudPolicyService::OnRegistrationStateChanged(
    337     CloudPolicyClient* client) {
    338   // Ignored.
    339 }
    340 
    341 void ComponentCloudPolicyService::OnClientError(CloudPolicyClient* client) {
    342   // Ignored.
    343 }
    344 
    345 void ComponentCloudPolicyService::OnStoreLoaded(CloudPolicyStore* store) {
    346   DCHECK_EQ(store_, store);
    347   if (store_->is_initialized()) {
    348     if (is_initialized()) {
    349       // The backend is already initialized; update the credentials, in case
    350       // a new dmtoken or server key became available.
    351       SetCredentialsAndReloadClient();
    352     } else {
    353       // The |store_| just became ready; initialize the backend now.
    354       InitializeBackend();
    355     }
    356   }
    357 }
    358 
    359 void ComponentCloudPolicyService::OnStoreError(CloudPolicyStore* store) {
    360   OnStoreLoaded(store);
    361 }
    362 
    363 void ComponentCloudPolicyService::InitializeBackend() {
    364   DCHECK(!is_initialized());
    365   DCHECK(store_->is_initialized());
    366 
    367   // Set the credentials for the initial policy load, if available.
    368   SetCredentialsAndReloadClient();
    369 
    370   backend_task_runner_->PostTask(
    371       FROM_HERE,
    372       base::Bind(&Backend::FinalizeInit, base::Unretained(backend_)));
    373 }
    374 
    375 void ComponentCloudPolicyService::OnBackendInitialized(
    376     scoped_ptr<ComponentMap> cached_components,
    377     scoped_ptr<PolicyBundle> initial_policy) {
    378   // InitializeBackend() may be called multiple times if the |store_| fires
    379   // events while the backend is loading.
    380   if (is_initialized())
    381     return;
    382 
    383   // RegisterPolicyDomain() may have been called while the backend was
    384   // initializing; only update |registered_components_| from |cached_components|
    385   // for domains that haven't registered yet.
    386   for (ComponentMap::iterator it = cached_components->begin();
    387        it != cached_components->end(); ++it) {
    388     // Lookup without inserting an empty set.
    389     if (registered_components_.find(it->first) != registered_components_.end())
    390       continue;  // Ignore the cached list if a more recent one was registered.
    391     registered_components_[it->first].swap(it->second);
    392   }
    393 
    394   // A client may have already connected while the backend was initializing.
    395   if (client_)
    396     InitializeClient();
    397 
    398   // Set the initial policy, and send the initial update callback.
    399   is_initialized_ = true;
    400   OnPolicyUpdated(initial_policy.Pass());
    401 }
    402 
    403 void ComponentCloudPolicyService::InitializeClient() {
    404   // Register all the current components.
    405   bool added = false;
    406   for (ComponentMap::iterator it = registered_components_.begin();
    407        it != registered_components_.end(); ++it) {
    408     added |= !it->second.empty();
    409     AddNamespacesToFetch(it->first, it->second);
    410   }
    411   // The client may already have PolicyFetchResponses for registered components;
    412   // load them now.
    413   OnPolicyFetched(client_);
    414   if (added && is_initialized())
    415     delegate_->OnComponentCloudPolicyRefreshNeeded();
    416 }
    417 
    418 void ComponentCloudPolicyService::OnPolicyUpdated(
    419     scoped_ptr<PolicyBundle> policy) {
    420   policy_.Swap(policy.get());
    421   // Don't propagate updates until the initial store Load() has been done.
    422   if (is_initialized())
    423     delegate_->OnComponentCloudPolicyUpdated();
    424 }
    425 
    426 void ComponentCloudPolicyService::SetCredentialsAndReloadClient() {
    427   const em::PolicyData* policy = store_->policy();
    428   if (!policy || !policy->has_username() || !policy->has_request_token())
    429     return;
    430   backend_task_runner_->PostTask(FROM_HERE,
    431                                  base::Bind(&Backend::SetCredentials,
    432                                             base::Unretained(backend_),
    433                                             policy->username(),
    434                                             policy->request_token()));
    435   // If this was the initial register, or if the signing key changed, then the
    436   // previous OnPolicyFetched() call had its PolicyFetchResponses rejected
    437   // because the credentials weren't updated yet. Reload all the responses in
    438   // the client now to handle those cases; if those responses have already been
    439   // validated then they will be ignored.
    440   if (client_)
    441     OnPolicyFetched(client_);
    442 }
    443 
    444 bool ComponentCloudPolicyService::UpdateClientNamespaces(
    445     PolicyDomain domain,
    446     const StringSet& old_set,
    447     const StringSet& new_set) {
    448   StringSet added = base::STLSetDifference<StringSet>(new_set, old_set);
    449   StringSet removed = base::STLSetDifference<StringSet>(old_set, new_set);
    450   AddNamespacesToFetch(domain, added);
    451   RemoveNamespacesToFetch(domain, removed);
    452   return !added.empty();
    453 }
    454 
    455 void ComponentCloudPolicyService::AddNamespacesToFetch(PolicyDomain domain,
    456                                                        const StringSet& set) {
    457   std::string policy_type;
    458   if (ComponentCloudPolicyStore::GetPolicyType(domain, &policy_type)) {
    459     for (StringSet::const_iterator it = set.begin(); it != set.end(); ++it)
    460       client_->AddNamespaceToFetch(PolicyNamespaceKey(policy_type, *it));
    461   }
    462 }
    463 
    464 void ComponentCloudPolicyService::RemoveNamespacesToFetch(
    465     PolicyDomain domain,
    466     const StringSet& set) {
    467   std::string policy_type;
    468   if (ComponentCloudPolicyStore::GetPolicyType(domain, &policy_type)) {
    469     for (StringSet::const_iterator it = set.begin(); it != set.end(); ++it)
    470       client_->RemoveNamespaceToFetch(PolicyNamespaceKey(policy_type, *it));
    471   }
    472 }
    473 
    474 }  // namespace policy
    475