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);
~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(
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
ExtensionTracker(Profile * profile)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
OnExtensionWillBeInstalled(content::BrowserContext * browser_context,const Extension * extension,bool is_update,bool from_ephemeral,const std::string & old_name)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
OnExtensionUninstalled(content::BrowserContext * browser_context,const Extension * extension,extensions::UninstallReason reason)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
OnExtensionsReady()152 void ManagedValueStoreCache::ExtensionTracker::OnExtensionsReady() {
153 // Load schemas for all installed extensions.
154 LoadSchemas(
155 ExtensionRegistry::Get(profile_)->GenerateInstalledExtensionsSet());
156 }
157
LoadSchemas(scoped_ptr<ExtensionSet> added)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
UsesManagedStorage(const Extension * extension) const179 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
LoadSchemasOnBlockingPool(scoped_ptr<ExtensionSet> extensions,base::WeakPtr<ExtensionTracker> self)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
Register(const policy::ComponentMap * components)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
ManagedValueStoreCache(BrowserContext * context,const scoped_refptr<SettingsStorageFactory> & factory,const scoped_refptr<SettingsObserverList> & observers)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
~ManagedValueStoreCache()259 ManagedValueStoreCache::~ManagedValueStoreCache() {
260 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
261 // Delete the PolicyValueStores on FILE.
262 store_map_.clear();
263 }
264
ShutdownOnUI()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
RunWithValueStoreForExtension(const StorageCallback & callback,scoped_refptr<const Extension> extension)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
DeleteStorageSoon(const std::string & extension_id)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
OnPolicyServiceInitialized(policy::PolicyDomain domain)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
OnPolicyUpdated(const policy::PolicyNamespace & ns,const policy::PolicyMap & previous,const policy::PolicyMap & current)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
UpdatePolicyOnFILE(const std::string & extension_id,scoped_ptr<policy::PolicyMap> current_policy)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
GetStoreFor(const std::string & extension_id)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
HasStore(const std::string & extension_id) const372 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