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.
5 #include "chrome/browser/extensions/api/storage/managed_value_store_cache.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"
40 using content::BrowserContext
;
41 using content::BrowserThread
;
43 namespace extensions
{
44 class ExtensionRegistry
;
46 namespace storage
= api::storage
;
50 const char kLoadSchemasBackgroundTaskTokenName
[] =
51 "load_managed_storage_schemas_token";
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";
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
{
70 explicit ExtensionTracker(Profile
* profile
);
71 ~ExtensionTracker() override
{}
74 // ExtensionRegistryObserver implementation.
75 void OnExtensionWillBeInstalled(content::BrowserContext
* browser_context
,
76 const Extension
* extension
,
79 const std::string
& old_name
) override
;
80 void OnExtensionUninstalled(content::BrowserContext
* browser_context
,
81 const Extension
* extension
,
82 extensions::UninstallReason reason
) override
;
84 // Handler for the signal from ExtensionSystem::ready().
85 void OnExtensionsReady();
87 // Starts a schema load for all extensions that use managed storage.
88 void LoadSchemas(scoped_ptr
<ExtensionSet
> added
);
90 bool UsesManagedStorage(const Extension
* extension
) const;
92 // Loads the schemas of the |extensions| and passes a ComponentMap to
94 static void LoadSchemasOnBlockingPool(scoped_ptr
<ExtensionSet
> extensions
,
95 base::WeakPtr
<ExtensionTracker
> self
);
96 void Register(const policy::ComponentMap
* components
);
99 ScopedObserver
<ExtensionRegistry
, ExtensionRegistryObserver
>
100 extension_registry_observer_
;
101 policy::SchemaRegistry
* schema_registry_
;
102 base::WeakPtrFactory
<ExtensionTracker
> weak_factory_
;
104 DISALLOW_COPY_AND_ASSIGN(ExtensionTracker
);
107 ManagedValueStoreCache::ExtensionTracker::ExtensionTracker(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(
117 base::Bind(&ExtensionTracker::OnExtensionsReady
,
118 weak_factory_
.GetWeakPtr()));
121 void ManagedValueStoreCache::ExtensionTracker::OnExtensionWillBeInstalled(
122 content::BrowserContext
* browser_context
,
123 const Extension
* extension
,
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
131 if (!ExtensionSystem::Get(profile_
)->ready().is_signaled())
133 scoped_ptr
<ExtensionSet
> added(new ExtensionSet
);
134 added
->Insert(extension
);
135 LoadSchemas(added
.Pass());
138 void ManagedValueStoreCache::ExtensionTracker::OnExtensionUninstalled(
139 content::BrowserContext
* browser_context
,
140 const Extension
* extension
,
141 extensions::UninstallReason reason
) {
142 if (!ExtensionSystem::Get(profile_
)->ready().is_signaled())
144 if (extension
&& UsesManagedStorage(extension
)) {
145 schema_registry_
->UnregisterComponent(policy::PolicyNamespace(
146 policy::POLICY_DOMAIN_EXTENSIONS
, extension
->id()));
150 void ManagedValueStoreCache::ExtensionTracker::OnExtensionsReady() {
151 // Load schemas for all installed extensions.
153 ExtensionRegistry::Get(profile_
)->GenerateInstalledExtensionsSet());
156 void ManagedValueStoreCache::ExtensionTracker::LoadSchemas(
157 scoped_ptr
<ExtensionSet
> added
) {
158 // Filter out extensions that don't use managed storage.
159 ExtensionSet::const_iterator it
= added
->begin();
160 while (it
!= added
->end()) {
161 std::string to_remove
;
162 if (!UsesManagedStorage(it
->get()))
163 to_remove
= (*it
)->id();
165 if (!to_remove
.empty())
166 added
->Remove(to_remove
);
169 // Load the schema files in a background thread.
170 BrowserThread::PostBlockingPoolSequencedTask(
171 kLoadSchemasBackgroundTaskTokenName
, FROM_HERE
,
172 base::Bind(&ExtensionTracker::LoadSchemasOnBlockingPool
,
173 base::Passed(&added
),
174 weak_factory_
.GetWeakPtr()));
177 bool ManagedValueStoreCache::ExtensionTracker::UsesManagedStorage(
178 const Extension
* extension
) const {
179 if (extension
->manifest()->HasPath(manifest_keys::kStorageManagedSchema
))
182 // TODO(joaodasilva): remove this by M35.
183 return extension
->id() == kLegacyBrowserSupportExtensionId
;
187 void ManagedValueStoreCache::ExtensionTracker::LoadSchemasOnBlockingPool(
188 scoped_ptr
<ExtensionSet
> extensions
,
189 base::WeakPtr
<ExtensionTracker
> self
) {
190 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
191 scoped_ptr
<policy::ComponentMap
> components(new policy::ComponentMap
);
193 for (ExtensionSet::const_iterator it
= extensions
->begin();
194 it
!= extensions
->end(); ++it
) {
195 std::string schema_file
;
196 if (!(*it
)->manifest()->GetString(
197 manifest_keys::kStorageManagedSchema
, &schema_file
)) {
198 // TODO(joaodasilva): Remove this. http://crbug.com/325349
199 (*components
)[(*it
)->id()] = policy::Schema();
202 // The extension should have been validated, so assume the schema exists
205 policy::Schema schema
=
206 StorageSchemaManifestHandler::GetSchema(it
->get(), &error
);
207 // If the schema is invalid then proceed with an empty schema. The extension
208 // will be listed in chrome://policy but won't be able to load any policies.
210 schema
= policy::Schema();
211 (*components
)[(*it
)->id()] = schema
;
214 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
215 base::Bind(&ExtensionTracker::Register
, self
,
216 base::Owned(components
.release())));
219 void ManagedValueStoreCache::ExtensionTracker::Register(
220 const policy::ComponentMap
* components
) {
221 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
222 schema_registry_
->RegisterComponents(policy::POLICY_DOMAIN_EXTENSIONS
,
225 // The first SetReady() call is performed after the ExtensionSystem is ready,
226 // even if there are no managed extensions. It will trigger a loading of the
227 // initial policy for any managed extensions, and eventually the PolicyService
228 // will become ready for POLICY_DOMAIN_EXTENSIONS, and
229 // OnPolicyServiceInitialized() will be invoked.
230 // Subsequent calls to SetReady() are ignored.
231 schema_registry_
->SetReady(policy::POLICY_DOMAIN_EXTENSIONS
);
234 ManagedValueStoreCache::ManagedValueStoreCache(
235 BrowserContext
* context
,
236 const scoped_refptr
<SettingsStorageFactory
>& factory
,
237 const scoped_refptr
<SettingsObserverList
>& observers
)
238 : profile_(Profile::FromBrowserContext(context
)),
240 policy::ProfilePolicyConnectorFactory::GetForBrowserContext(context
)
242 storage_factory_(factory
),
243 observers_(observers
),
244 base_path_(profile_
->GetPath().AppendASCII(
245 extensions::kManagedSettingsDirectoryName
)) {
246 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
248 policy_service_
->AddObserver(policy::POLICY_DOMAIN_EXTENSIONS
, this);
250 extension_tracker_
.reset(new ExtensionTracker(profile_
));
252 if (policy_service_
->IsInitializationComplete(
253 policy::POLICY_DOMAIN_EXTENSIONS
)) {
254 OnPolicyServiceInitialized(policy::POLICY_DOMAIN_EXTENSIONS
);
258 ManagedValueStoreCache::~ManagedValueStoreCache() {
259 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
260 // Delete the PolicyValueStores on FILE.
264 void ManagedValueStoreCache::ShutdownOnUI() {
265 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
266 policy_service_
->RemoveObserver(policy::POLICY_DOMAIN_EXTENSIONS
, this);
267 extension_tracker_
.reset();
270 void ManagedValueStoreCache::RunWithValueStoreForExtension(
271 const StorageCallback
& callback
,
272 scoped_refptr
<const Extension
> extension
) {
273 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
274 callback
.Run(GetStoreFor(extension
->id()));
277 void ManagedValueStoreCache::DeleteStorageSoon(
278 const std::string
& extension_id
) {
279 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
280 // It's possible that the store exists, but hasn't been loaded yet
281 // (because the extension is unloaded, for example). Open the database to
282 // clear it if it exists.
283 if (!HasStore(extension_id
))
285 GetStoreFor(extension_id
)->DeleteStorage();
286 store_map_
.erase(extension_id
);
289 void ManagedValueStoreCache::OnPolicyServiceInitialized(
290 policy::PolicyDomain domain
) {
291 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
293 if (domain
!= policy::POLICY_DOMAIN_EXTENSIONS
)
296 // The PolicyService now has all the initial policies ready. Send policy
297 // for all the managed extensions to their backing stores now.
298 policy::SchemaRegistry
* registry
=
299 policy::SchemaRegistryServiceFactory::GetForContext(profile_
)->registry();
300 const policy::ComponentMap
* map
= registry
->schema_map()->GetComponents(
301 policy::POLICY_DOMAIN_EXTENSIONS
);
305 const policy::PolicyMap empty_map
;
306 for (policy::ComponentMap::const_iterator it
= map
->begin();
307 it
!= map
->end(); ++it
) {
308 const policy::PolicyNamespace
ns(policy::POLICY_DOMAIN_EXTENSIONS
,
310 // If there is no policy for |ns| then this will clear the previous store,
312 OnPolicyUpdated(ns
, empty_map
, policy_service_
->GetPolicies(ns
));
316 void ManagedValueStoreCache::OnPolicyUpdated(const policy::PolicyNamespace
& ns
,
317 const policy::PolicyMap
& previous
,
318 const policy::PolicyMap
& current
) {
319 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
321 if (!policy_service_
->IsInitializationComplete(
322 policy::POLICY_DOMAIN_EXTENSIONS
)) {
323 // OnPolicyUpdated is called whenever a policy changes, but it doesn't
324 // mean that all the policy providers are ready; wait until we get the
325 // final policy values before passing them to the store.
329 BrowserThread::PostTask(
330 BrowserThread::FILE, FROM_HERE
,
331 base::Bind(&ManagedValueStoreCache::UpdatePolicyOnFILE
,
332 base::Unretained(this),
334 base::Passed(current
.DeepCopy())));
337 void ManagedValueStoreCache::UpdatePolicyOnFILE(
338 const std::string
& extension_id
,
339 scoped_ptr
<policy::PolicyMap
> current_policy
) {
340 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
342 if (!HasStore(extension_id
) && current_policy
->empty()) {
343 // Don't create the store now if there are no policies configured for this
344 // extension. If the extension uses the storage.managed API then the store
345 // will be created at RunWithValueStoreForExtension().
349 GetStoreFor(extension_id
)->SetCurrentPolicy(*current_policy
);
352 PolicyValueStore
* ManagedValueStoreCache::GetStoreFor(
353 const std::string
& extension_id
) {
354 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
356 PolicyValueStoreMap::iterator it
= store_map_
.find(extension_id
);
357 if (it
!= store_map_
.end())
358 return it
->second
.get();
360 // Create the store now, and serve the cached policy until the PolicyService
361 // sends updated values.
362 PolicyValueStore
* store
= new PolicyValueStore(
365 make_scoped_ptr(storage_factory_
->Create(base_path_
, extension_id
)));
366 store_map_
[extension_id
] = make_linked_ptr(store
);
371 bool ManagedValueStoreCache::HasStore(const std::string
& extension_id
) const {
372 // TODO(joaodasilva): move this check to a ValueStore method.
373 return base::DirectoryExists(base_path_
.AppendASCII(extension_id
));
376 } // namespace extensions