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