• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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);
~ExtensionTracker()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 
ExtensionTracker(Profile * profile)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 
OnExtensionWillBeInstalled(content::BrowserContext * browser_context,const Extension * extension,bool is_update,bool from_ephemeral,const std::string & old_name)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 
OnExtensionUninstalled(content::BrowserContext * browser_context,const Extension * extension)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 
OnExtensionsReady()149 void ManagedValueStoreCache::ExtensionTracker::OnExtensionsReady() {
150   // Load schemas for all installed extensions.
151   LoadSchemas(
152       ExtensionRegistry::Get(profile_)->GenerateInstalledExtensionsSet());
153 }
154 
LoadSchemas(scoped_ptr<ExtensionSet> added)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 
UsesManagedStorage(const Extension * extension) const176 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
LoadSchemasOnBlockingPool(scoped_ptr<ExtensionSet> extensions,base::WeakPtr<ExtensionTracker> self)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 
Register(const policy::ComponentMap * components)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 
ManagedValueStoreCache(BrowserContext * context,const scoped_refptr<SettingsStorageFactory> & factory,const scoped_refptr<SettingsObserverList> & observers)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 
~ManagedValueStoreCache()253 ManagedValueStoreCache::~ManagedValueStoreCache() {
254   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
255   // Delete the PolicyValueStores on FILE.
256   store_map_.clear();
257 }
258 
ShutdownOnUI()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 
RunWithValueStoreForExtension(const StorageCallback & callback,scoped_refptr<const Extension> extension)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 
DeleteStorageSoon(const std::string & extension_id)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 
OnPolicyServiceInitialized(policy::PolicyDomain domain)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 
OnPolicyUpdated(const policy::PolicyNamespace & ns,const policy::PolicyMap & previous,const policy::PolicyMap & current)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 
UpdatePolicyOnFILE(const std::string & extension_id,scoped_ptr<policy::PolicyMap> current_policy)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 
GetStoreFor(const std::string & extension_id)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 
HasStore(const std::string & extension_id) const366 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