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/files/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(
     82       content::BrowserContext* browser_context,
     83       const Extension* extension,
     84       extensions::UninstallReason reason) OVERRIDE;
     85 
     86   // Handler for the signal from ExtensionSystem::ready().
     87   void OnExtensionsReady();
     88 
     89   // Starts a schema load for all extensions that use managed storage.
     90   void LoadSchemas(scoped_ptr<ExtensionSet> added);
     91 
     92   bool UsesManagedStorage(const Extension* extension) const;
     93 
     94   // Loads the schemas of the |extensions| and passes a ComponentMap to
     95   // Register().
     96   static void LoadSchemasOnBlockingPool(scoped_ptr<ExtensionSet> extensions,
     97                                         base::WeakPtr<ExtensionTracker> self);
     98   void Register(const policy::ComponentMap* components);
     99 
    100   Profile* profile_;
    101   ScopedObserver<ExtensionRegistry, ExtensionRegistryObserver>
    102       extension_registry_observer_;
    103   policy::SchemaRegistry* schema_registry_;
    104   base::WeakPtrFactory<ExtensionTracker> weak_factory_;
    105 
    106   DISALLOW_COPY_AND_ASSIGN(ExtensionTracker);
    107 };
    108 
    109 ManagedValueStoreCache::ExtensionTracker::ExtensionTracker(Profile* profile)
    110     : profile_(profile),
    111       extension_registry_observer_(this),
    112       schema_registry_(policy::SchemaRegistryServiceFactory::GetForContext(
    113                            profile)->registry()),
    114       weak_factory_(this) {
    115   extension_registry_observer_.Add(ExtensionRegistry::Get(profile_));
    116   // Load schemas when the extension system is ready. It might be ready now.
    117   ExtensionSystem::Get(profile_)->ready().Post(
    118       FROM_HERE,
    119       base::Bind(&ExtensionTracker::OnExtensionsReady,
    120                  weak_factory_.GetWeakPtr()));
    121 }
    122 
    123 void ManagedValueStoreCache::ExtensionTracker::OnExtensionWillBeInstalled(
    124     content::BrowserContext* browser_context,
    125     const Extension* extension,
    126     bool is_update,
    127     bool from_ephemeral,
    128     const std::string& old_name) {
    129   // Some extensions are installed on the first run before the ExtensionSystem
    130   // becomes ready. Wait until all of them are ready before registering the
    131   // schemas of managed extensions, so that the policy loaders are reloaded at
    132   // most once.
    133   if (!ExtensionSystem::Get(profile_)->ready().is_signaled())
    134     return;
    135   scoped_ptr<ExtensionSet> added(new ExtensionSet);
    136   added->Insert(extension);
    137   LoadSchemas(added.Pass());
    138 }
    139 
    140 void ManagedValueStoreCache::ExtensionTracker::OnExtensionUninstalled(
    141     content::BrowserContext* browser_context,
    142     const Extension* extension,
    143     extensions::UninstallReason reason) {
    144   if (!ExtensionSystem::Get(profile_)->ready().is_signaled())
    145     return;
    146   if (extension && UsesManagedStorage(extension)) {
    147     schema_registry_->UnregisterComponent(policy::PolicyNamespace(
    148         policy::POLICY_DOMAIN_EXTENSIONS, extension->id()));
    149   }
    150 }
    151 
    152 void ManagedValueStoreCache::ExtensionTracker::OnExtensionsReady() {
    153   // Load schemas for all installed extensions.
    154   LoadSchemas(
    155       ExtensionRegistry::Get(profile_)->GenerateInstalledExtensionsSet());
    156 }
    157 
    158 void ManagedValueStoreCache::ExtensionTracker::LoadSchemas(
    159     scoped_ptr<ExtensionSet> added) {
    160   // Filter out extensions that don't use managed storage.
    161   ExtensionSet::const_iterator it = added->begin();
    162   while (it != added->end()) {
    163     std::string to_remove;
    164     if (!UsesManagedStorage(it->get()))
    165       to_remove = (*it)->id();
    166     ++it;
    167     if (!to_remove.empty())
    168       added->Remove(to_remove);
    169   }
    170 
    171   // Load the schema files in a background thread.
    172   BrowserThread::PostBlockingPoolSequencedTask(
    173       kLoadSchemasBackgroundTaskTokenName, FROM_HERE,
    174       base::Bind(&ExtensionTracker::LoadSchemasOnBlockingPool,
    175                  base::Passed(&added),
    176                  weak_factory_.GetWeakPtr()));
    177 }
    178 
    179 bool ManagedValueStoreCache::ExtensionTracker::UsesManagedStorage(
    180     const Extension* extension) const {
    181   if (extension->manifest()->HasPath(manifest_keys::kStorageManagedSchema))
    182     return true;
    183 
    184   // TODO(joaodasilva): remove this by M35.
    185   return extension->id() == kLegacyBrowserSupportExtensionId;
    186 }
    187 
    188 // static
    189 void ManagedValueStoreCache::ExtensionTracker::LoadSchemasOnBlockingPool(
    190     scoped_ptr<ExtensionSet> extensions,
    191     base::WeakPtr<ExtensionTracker> self) {
    192   DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
    193   scoped_ptr<policy::ComponentMap> components(new policy::ComponentMap);
    194 
    195   for (ExtensionSet::const_iterator it = extensions->begin();
    196        it != extensions->end(); ++it) {
    197     std::string schema_file;
    198     if (!(*it)->manifest()->GetString(
    199             manifest_keys::kStorageManagedSchema, &schema_file)) {
    200       // TODO(joaodasilva): Remove this. http://crbug.com/325349
    201       (*components)[(*it)->id()] = policy::Schema();
    202       continue;
    203     }
    204     // The extension should have been validated, so assume the schema exists
    205     // and is valid.
    206     std::string error;
    207     policy::Schema schema =
    208         StorageSchemaManifestHandler::GetSchema(it->get(), &error);
    209     // If the schema is invalid then proceed with an empty schema. The extension
    210     // will be listed in chrome://policy but won't be able to load any policies.
    211     if (!schema.valid())
    212       schema = policy::Schema();
    213     (*components)[(*it)->id()] = schema;
    214   }
    215 
    216   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
    217                           base::Bind(&ExtensionTracker::Register, self,
    218                                      base::Owned(components.release())));
    219 }
    220 
    221 void ManagedValueStoreCache::ExtensionTracker::Register(
    222     const policy::ComponentMap* components) {
    223   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    224   schema_registry_->RegisterComponents(policy::POLICY_DOMAIN_EXTENSIONS,
    225                                        *components);
    226 
    227   // The first SetReady() call is performed after the ExtensionSystem is ready,
    228   // even if there are no managed extensions. It will trigger a loading of the
    229   // initial policy for any managed extensions, and eventually the PolicyService
    230   // will become ready for POLICY_DOMAIN_EXTENSIONS, and
    231   // OnPolicyServiceInitialized() will be invoked.
    232   // Subsequent calls to SetReady() are ignored.
    233   schema_registry_->SetReady(policy::POLICY_DOMAIN_EXTENSIONS);
    234 }
    235 
    236 ManagedValueStoreCache::ManagedValueStoreCache(
    237     BrowserContext* context,
    238     const scoped_refptr<SettingsStorageFactory>& factory,
    239     const scoped_refptr<SettingsObserverList>& observers)
    240     : profile_(Profile::FromBrowserContext(context)),
    241       policy_service_(policy::ProfilePolicyConnectorFactory::GetForProfile(
    242                           profile_)->policy_service()),
    243       storage_factory_(factory),
    244       observers_(observers),
    245       base_path_(profile_->GetPath().AppendASCII(
    246           extensions::kManagedSettingsDirectoryName)) {
    247   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    248 
    249   policy_service_->AddObserver(policy::POLICY_DOMAIN_EXTENSIONS, this);
    250 
    251   extension_tracker_.reset(new ExtensionTracker(profile_));
    252 
    253   if (policy_service_->IsInitializationComplete(
    254           policy::POLICY_DOMAIN_EXTENSIONS)) {
    255     OnPolicyServiceInitialized(policy::POLICY_DOMAIN_EXTENSIONS);
    256   }
    257 }
    258 
    259 ManagedValueStoreCache::~ManagedValueStoreCache() {
    260   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
    261   // Delete the PolicyValueStores on FILE.
    262   store_map_.clear();
    263 }
    264 
    265 void ManagedValueStoreCache::ShutdownOnUI() {
    266   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    267   policy_service_->RemoveObserver(policy::POLICY_DOMAIN_EXTENSIONS, this);
    268   extension_tracker_.reset();
    269 }
    270 
    271 void ManagedValueStoreCache::RunWithValueStoreForExtension(
    272     const StorageCallback& callback,
    273     scoped_refptr<const Extension> extension) {
    274   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
    275   callback.Run(GetStoreFor(extension->id()));
    276 }
    277 
    278 void ManagedValueStoreCache::DeleteStorageSoon(
    279     const std::string& extension_id) {
    280   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
    281   // It's possible that the store exists, but hasn't been loaded yet
    282   // (because the extension is unloaded, for example). Open the database to
    283   // clear it if it exists.
    284   if (!HasStore(extension_id))
    285     return;
    286   GetStoreFor(extension_id)->DeleteStorage();
    287   store_map_.erase(extension_id);
    288 }
    289 
    290 void ManagedValueStoreCache::OnPolicyServiceInitialized(
    291     policy::PolicyDomain domain) {
    292   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    293 
    294   if (domain != policy::POLICY_DOMAIN_EXTENSIONS)
    295     return;
    296 
    297   // The PolicyService now has all the initial policies ready. Send policy
    298   // for all the managed extensions to their backing stores now.
    299   policy::SchemaRegistry* registry =
    300       policy::SchemaRegistryServiceFactory::GetForContext(profile_)->registry();
    301   const policy::ComponentMap* map = registry->schema_map()->GetComponents(
    302       policy::POLICY_DOMAIN_EXTENSIONS);
    303   if (!map)
    304     return;
    305 
    306   const policy::PolicyMap empty_map;
    307   for (policy::ComponentMap::const_iterator it = map->begin();
    308        it != map->end(); ++it) {
    309     const policy::PolicyNamespace ns(policy::POLICY_DOMAIN_EXTENSIONS,
    310                                      it->first);
    311     // If there is no policy for |ns| then this will clear the previous store,
    312     // if there is one.
    313     OnPolicyUpdated(ns, empty_map, policy_service_->GetPolicies(ns));
    314   }
    315 }
    316 
    317 void ManagedValueStoreCache::OnPolicyUpdated(const policy::PolicyNamespace& ns,
    318                                              const policy::PolicyMap& previous,
    319                                              const policy::PolicyMap& current) {
    320   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    321 
    322   if (!policy_service_->IsInitializationComplete(
    323            policy::POLICY_DOMAIN_EXTENSIONS)) {
    324     // OnPolicyUpdated is called whenever a policy changes, but it doesn't
    325     // mean that all the policy providers are ready; wait until we get the
    326     // final policy values before passing them to the store.
    327     return;
    328   }
    329 
    330   BrowserThread::PostTask(
    331       BrowserThread::FILE, FROM_HERE,
    332       base::Bind(&ManagedValueStoreCache::UpdatePolicyOnFILE,
    333                  base::Unretained(this),
    334                  ns.component_id,
    335                  base::Passed(current.DeepCopy())));
    336 }
    337 
    338 void ManagedValueStoreCache::UpdatePolicyOnFILE(
    339     const std::string& extension_id,
    340     scoped_ptr<policy::PolicyMap> current_policy) {
    341   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
    342 
    343   if (!HasStore(extension_id) && current_policy->empty()) {
    344     // Don't create the store now if there are no policies configured for this
    345     // extension. If the extension uses the storage.managed API then the store
    346     // will be created at RunWithValueStoreForExtension().
    347     return;
    348   }
    349 
    350   GetStoreFor(extension_id)->SetCurrentPolicy(*current_policy);
    351 }
    352 
    353 PolicyValueStore* ManagedValueStoreCache::GetStoreFor(
    354     const std::string& extension_id) {
    355   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
    356 
    357   PolicyValueStoreMap::iterator it = store_map_.find(extension_id);
    358   if (it != store_map_.end())
    359     return it->second.get();
    360 
    361   // Create the store now, and serve the cached policy until the PolicyService
    362   // sends updated values.
    363   PolicyValueStore* store = new PolicyValueStore(
    364       extension_id,
    365       observers_,
    366       make_scoped_ptr(storage_factory_->Create(base_path_, extension_id)));
    367   store_map_[extension_id] = make_linked_ptr(store);
    368 
    369   return store;
    370 }
    371 
    372 bool ManagedValueStoreCache::HasStore(const std::string& extension_id) const {
    373   // TODO(joaodasilva): move this check to a ValueStore method.
    374   return base::DirectoryExists(base_path_.AppendASCII(extension_id));
    375 }
    376 
    377 }  // namespace extensions
    378