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