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