Home | History | Annotate | Download | only in storage
      1 // Copyright (c) 2012 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/extensions/api/storage/managed_value_store_cache.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/bind_helpers.h"
      9 #include "base/callback.h"
     10 #include "base/file_util.h"
     11 #include "base/logging.h"
     12 #include "base/memory/weak_ptr.h"
     13 #include "base/scoped_observer.h"
     14 #include "chrome/browser/extensions/api/storage/policy_value_store.h"
     15 #include "chrome/browser/policy/profile_policy_connector.h"
     16 #include "chrome/browser/policy/profile_policy_connector_factory.h"
     17 #include "chrome/browser/policy/schema_registry_service.h"
     18 #include "chrome/browser/policy/schema_registry_service_factory.h"
     19 #include "chrome/browser/profiles/profile.h"
     20 #include "chrome/common/extensions/api/storage/storage_schema_manifest_handler.h"
     21 #include "components/policy/core/common/policy_namespace.h"
     22 #include "components/policy/core/common/schema.h"
     23 #include "components/policy/core/common/schema_map.h"
     24 #include "components/policy/core/common/schema_registry.h"
     25 #include "content/public/browser/browser_thread.h"
     26 #include "extensions/browser/api/storage/settings_storage_factory.h"
     27 #include "extensions/browser/extension_prefs.h"
     28 #include "extensions/browser/extension_registry.h"
     29 #include "extensions/browser/extension_registry_observer.h"
     30 #include "extensions/browser/extension_system.h"
     31 #include "extensions/browser/value_store/value_store_change.h"
     32 #include "extensions/common/api/storage.h"
     33 #include "extensions/common/constants.h"
     34 #include "extensions/common/extension.h"
     35 #include "extensions/common/extension_set.h"
     36 #include "extensions/common/manifest.h"
     37 #include "extensions/common/manifest_constants.h"
     38 #include "extensions/common/one_shot_event.h"
     39 
     40 using content::BrowserContext;
     41 using content::BrowserThread;
     42 
     43 namespace extensions {
     44 class ExtensionRegistry;
     45 
     46 namespace storage = core_api::storage;
     47 
     48 namespace {
     49 
     50 const char kLoadSchemasBackgroundTaskTokenName[] =
     51     "load_managed_storage_schemas_token";
     52 
     53 // The Legacy Browser Support was the first user of the policy-for-extensions
     54 // API, and relied on behavior that will be phased out. If this extension is
     55 // present then its policies will be loaded in a special way.
     56 // TODO(joaodasilva): remove this for M35. http://crbug.com/325349
     57 const char kLegacyBrowserSupportExtensionId[] =
     58     "heildphpnddilhkemkielfhnkaagiabh";
     59 
     60 }  // namespace
     61 
     62 // This helper observes initialization of all the installed extensions and
     63 // subsequent loads and unloads, and keeps the SchemaRegistry of the Profile
     64 // in sync with the current list of extensions. This allows the PolicyService
     65 // to fetch cloud policy for those extensions, and allows its providers to
     66 // selectively load only extension policy that has users.
     67 class ManagedValueStoreCache::ExtensionTracker
     68     : public ExtensionRegistryObserver {
     69  public:
     70   explicit ExtensionTracker(Profile* profile);
     71   virtual ~ExtensionTracker() {}
     72 
     73  private:
     74   // ExtensionRegistryObserver implementation.
     75   virtual void OnExtensionWillBeInstalled(
     76       content::BrowserContext* browser_context,
     77       const Extension* extension,
     78       bool is_update,
     79       bool from_ephemeral,
     80       const std::string& old_name) OVERRIDE;
     81   virtual void OnExtensionUninstalled(content::BrowserContext* browser_context,
     82                                       const Extension* extension) OVERRIDE;
     83 
     84   // Handler for the signal from ExtensionSystem::ready().
     85   void OnExtensionsReady();
     86 
     87   // Starts a schema load for all extensions that use managed storage.
     88   void LoadSchemas(scoped_ptr<ExtensionSet> added);
     89 
     90   bool UsesManagedStorage(const Extension* extension) const;
     91 
     92   // Loads the schemas of the |extensions| and passes a ComponentMap to
     93   // Register().
     94   static void LoadSchemasOnBlockingPool(scoped_ptr<ExtensionSet> extensions,
     95                                         base::WeakPtr<ExtensionTracker> self);
     96   void Register(const policy::ComponentMap* components);
     97 
     98   Profile* profile_;
     99   ScopedObserver<ExtensionRegistry, ExtensionRegistryObserver>
    100       extension_registry_observer_;
    101   policy::SchemaRegistry* schema_registry_;
    102   base::WeakPtrFactory<ExtensionTracker> weak_factory_;
    103 
    104   DISALLOW_COPY_AND_ASSIGN(ExtensionTracker);
    105 };
    106 
    107 ManagedValueStoreCache::ExtensionTracker::ExtensionTracker(Profile* profile)
    108     : profile_(profile),
    109       extension_registry_observer_(this),
    110       schema_registry_(policy::SchemaRegistryServiceFactory::GetForContext(
    111                            profile)->registry()),
    112       weak_factory_(this) {
    113   extension_registry_observer_.Add(ExtensionRegistry::Get(profile_));
    114   // Load schemas when the extension system is ready. It might be ready now.
    115   ExtensionSystem::Get(profile_)->ready().Post(
    116       FROM_HERE,
    117       base::Bind(&ExtensionTracker::OnExtensionsReady,
    118                  weak_factory_.GetWeakPtr()));
    119 }
    120 
    121 void ManagedValueStoreCache::ExtensionTracker::OnExtensionWillBeInstalled(
    122     content::BrowserContext* browser_context,
    123     const Extension* extension,
    124     bool is_update,
    125     bool from_ephemeral,
    126     const std::string& old_name) {
    127   // Some extensions are installed on the first run before the ExtensionSystem
    128   // becomes ready. Wait until all of them are ready before registering the
    129   // schemas of managed extensions, so that the policy loaders are reloaded at
    130   // most once.
    131   if (!ExtensionSystem::Get(profile_)->ready().is_signaled())
    132     return;
    133   scoped_ptr<ExtensionSet> added(new ExtensionSet);
    134   added->Insert(extension);
    135   LoadSchemas(added.Pass());
    136 }
    137 
    138 void ManagedValueStoreCache::ExtensionTracker::OnExtensionUninstalled(
    139     content::BrowserContext* browser_context,
    140     const Extension* extension) {
    141   if (!ExtensionSystem::Get(profile_)->ready().is_signaled())
    142     return;
    143   if (extension && UsesManagedStorage(extension)) {
    144     schema_registry_->UnregisterComponent(policy::PolicyNamespace(
    145         policy::POLICY_DOMAIN_EXTENSIONS, extension->id()));
    146   }
    147 }
    148 
    149 void ManagedValueStoreCache::ExtensionTracker::OnExtensionsReady() {
    150   // Load schemas for all installed extensions.
    151   LoadSchemas(
    152       ExtensionRegistry::Get(profile_)->GenerateInstalledExtensionsSet());
    153 }
    154 
    155 void ManagedValueStoreCache::ExtensionTracker::LoadSchemas(
    156     scoped_ptr<ExtensionSet> added) {
    157   // Filter out extensions that don't use managed storage.
    158   ExtensionSet::const_iterator it = added->begin();
    159   while (it != added->end()) {
    160     std::string to_remove;
    161     if (!UsesManagedStorage(*it))
    162       to_remove = (*it)->id();
    163     ++it;
    164     if (!to_remove.empty())
    165       added->Remove(to_remove);
    166   }
    167 
    168   // Load the schema files in a background thread.
    169   BrowserThread::PostBlockingPoolSequencedTask(
    170       kLoadSchemasBackgroundTaskTokenName, FROM_HERE,
    171       base::Bind(&ExtensionTracker::LoadSchemasOnBlockingPool,
    172                  base::Passed(&added),
    173                  weak_factory_.GetWeakPtr()));
    174 }
    175 
    176 bool ManagedValueStoreCache::ExtensionTracker::UsesManagedStorage(
    177     const Extension* extension) const {
    178   if (extension->manifest()->HasPath(manifest_keys::kStorageManagedSchema))
    179     return true;
    180 
    181   // TODO(joaodasilva): remove this by M35.
    182   return extension->id() == kLegacyBrowserSupportExtensionId;
    183 }
    184 
    185 // static
    186 void ManagedValueStoreCache::ExtensionTracker::LoadSchemasOnBlockingPool(
    187     scoped_ptr<ExtensionSet> extensions,
    188     base::WeakPtr<ExtensionTracker> self) {
    189   DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
    190   scoped_ptr<policy::ComponentMap> components(new policy::ComponentMap);
    191 
    192   for (ExtensionSet::const_iterator it = extensions->begin();
    193        it != extensions->end(); ++it) {
    194     std::string schema_file;
    195     if (!(*it)->manifest()->GetString(
    196             manifest_keys::kStorageManagedSchema, &schema_file)) {
    197       // TODO(joaodasilva): Remove this. http://crbug.com/325349
    198       (*components)[(*it)->id()] = policy::Schema();
    199       continue;
    200     }
    201     // The extension should have been validated, so assume the schema exists
    202     // and is valid.
    203     std::string error;
    204     policy::Schema schema =
    205         StorageSchemaManifestHandler::GetSchema(it->get(), &error);
    206     CHECK(schema.valid()) << error;
    207     (*components)[(*it)->id()] = schema;
    208   }
    209 
    210   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
    211                           base::Bind(&ExtensionTracker::Register, self,
    212                                      base::Owned(components.release())));
    213 }
    214 
    215 void ManagedValueStoreCache::ExtensionTracker::Register(
    216     const policy::ComponentMap* components) {
    217   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    218   schema_registry_->RegisterComponents(policy::POLICY_DOMAIN_EXTENSIONS,
    219                                        *components);
    220 
    221   // The first SetReady() call is performed after the ExtensionSystem is ready,
    222   // even if there are no managed extensions. It will trigger a loading of the
    223   // initial policy for any managed extensions, and eventually the PolicyService
    224   // will become ready for POLICY_DOMAIN_EXTENSIONS, and
    225   // OnPolicyServiceInitialized() will be invoked.
    226   // Subsequent calls to SetReady() are ignored.
    227   schema_registry_->SetReady(policy::POLICY_DOMAIN_EXTENSIONS);
    228 }
    229 
    230 ManagedValueStoreCache::ManagedValueStoreCache(
    231     BrowserContext* context,
    232     const scoped_refptr<SettingsStorageFactory>& factory,
    233     const scoped_refptr<SettingsObserverList>& observers)
    234     : profile_(Profile::FromBrowserContext(context)),
    235       policy_service_(policy::ProfilePolicyConnectorFactory::GetForProfile(
    236                           profile_)->policy_service()),
    237       storage_factory_(factory),
    238       observers_(observers),
    239       base_path_(profile_->GetPath().AppendASCII(
    240           extensions::kManagedSettingsDirectoryName)) {
    241   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    242 
    243   policy_service_->AddObserver(policy::POLICY_DOMAIN_EXTENSIONS, this);
    244 
    245   extension_tracker_.reset(new ExtensionTracker(profile_));
    246 
    247   if (policy_service_->IsInitializationComplete(
    248           policy::POLICY_DOMAIN_EXTENSIONS)) {
    249     OnPolicyServiceInitialized(policy::POLICY_DOMAIN_EXTENSIONS);
    250   }
    251 }
    252 
    253 ManagedValueStoreCache::~ManagedValueStoreCache() {
    254   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
    255   // Delete the PolicyValueStores on FILE.
    256   store_map_.clear();
    257 }
    258 
    259 void ManagedValueStoreCache::ShutdownOnUI() {
    260   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    261   policy_service_->RemoveObserver(policy::POLICY_DOMAIN_EXTENSIONS, this);
    262   extension_tracker_.reset();
    263 }
    264 
    265 void ManagedValueStoreCache::RunWithValueStoreForExtension(
    266     const StorageCallback& callback,
    267     scoped_refptr<const Extension> extension) {
    268   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
    269   callback.Run(GetStoreFor(extension->id()));
    270 }
    271 
    272 void ManagedValueStoreCache::DeleteStorageSoon(
    273     const std::string& extension_id) {
    274   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
    275   // It's possible that the store exists, but hasn't been loaded yet
    276   // (because the extension is unloaded, for example). Open the database to
    277   // clear it if it exists.
    278   if (!HasStore(extension_id))
    279     return;
    280   GetStoreFor(extension_id)->DeleteStorage();
    281   store_map_.erase(extension_id);
    282 }
    283 
    284 void ManagedValueStoreCache::OnPolicyServiceInitialized(
    285     policy::PolicyDomain domain) {
    286   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    287 
    288   if (domain != policy::POLICY_DOMAIN_EXTENSIONS)
    289     return;
    290 
    291   // The PolicyService now has all the initial policies ready. Send policy
    292   // for all the managed extensions to their backing stores now.
    293   policy::SchemaRegistry* registry =
    294       policy::SchemaRegistryServiceFactory::GetForContext(profile_)->registry();
    295   const policy::ComponentMap* map = registry->schema_map()->GetComponents(
    296       policy::POLICY_DOMAIN_EXTENSIONS);
    297   if (!map)
    298     return;
    299 
    300   const policy::PolicyMap empty_map;
    301   for (policy::ComponentMap::const_iterator it = map->begin();
    302        it != map->end(); ++it) {
    303     const policy::PolicyNamespace ns(policy::POLICY_DOMAIN_EXTENSIONS,
    304                                      it->first);
    305     // If there is no policy for |ns| then this will clear the previous store,
    306     // if there is one.
    307     OnPolicyUpdated(ns, empty_map, policy_service_->GetPolicies(ns));
    308   }
    309 }
    310 
    311 void ManagedValueStoreCache::OnPolicyUpdated(const policy::PolicyNamespace& ns,
    312                                              const policy::PolicyMap& previous,
    313                                              const policy::PolicyMap& current) {
    314   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    315 
    316   if (!policy_service_->IsInitializationComplete(
    317            policy::POLICY_DOMAIN_EXTENSIONS)) {
    318     // OnPolicyUpdated is called whenever a policy changes, but it doesn't
    319     // mean that all the policy providers are ready; wait until we get the
    320     // final policy values before passing them to the store.
    321     return;
    322   }
    323 
    324   BrowserThread::PostTask(
    325       BrowserThread::FILE, FROM_HERE,
    326       base::Bind(&ManagedValueStoreCache::UpdatePolicyOnFILE,
    327                  base::Unretained(this),
    328                  ns.component_id,
    329                  base::Passed(current.DeepCopy())));
    330 }
    331 
    332 void ManagedValueStoreCache::UpdatePolicyOnFILE(
    333     const std::string& extension_id,
    334     scoped_ptr<policy::PolicyMap> current_policy) {
    335   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
    336 
    337   if (!HasStore(extension_id) && current_policy->empty()) {
    338     // Don't create the store now if there are no policies configured for this
    339     // extension. If the extension uses the storage.managed API then the store
    340     // will be created at RunWithValueStoreForExtension().
    341     return;
    342   }
    343 
    344   GetStoreFor(extension_id)->SetCurrentPolicy(*current_policy);
    345 }
    346 
    347 PolicyValueStore* ManagedValueStoreCache::GetStoreFor(
    348     const std::string& extension_id) {
    349   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
    350 
    351   PolicyValueStoreMap::iterator it = store_map_.find(extension_id);
    352   if (it != store_map_.end())
    353     return it->second.get();
    354 
    355   // Create the store now, and serve the cached policy until the PolicyService
    356   // sends updated values.
    357   PolicyValueStore* store = new PolicyValueStore(
    358       extension_id,
    359       observers_,
    360       make_scoped_ptr(storage_factory_->Create(base_path_, extension_id)));
    361   store_map_[extension_id] = make_linked_ptr(store);
    362 
    363   return store;
    364 }
    365 
    366 bool ManagedValueStoreCache::HasStore(const std::string& extension_id) const {
    367   // TODO(joaodasilva): move this check to a ValueStore method.
    368   return base::DirectoryExists(base_path_.AppendASCII(extension_id));
    369 }
    370 
    371 }  // namespace extensions
    372