1 // Copyright 2014 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/extension_management.h"
11 #include "base/bind.h"
12 #include "base/bind_helpers.h"
13 #include "base/logging.h"
14 #include "base/prefs/pref_service.h"
15 #include "base/strings/string_util.h"
16 #include "chrome/browser/extensions/extension_management_constants.h"
17 #include "chrome/browser/extensions/extension_management_internal.h"
18 #include "chrome/browser/extensions/external_policy_loader.h"
19 #include "chrome/browser/extensions/external_provider_impl.h"
20 #include "chrome/browser/extensions/standard_management_policy_provider.h"
21 #include "chrome/browser/profiles/incognito_helpers.h"
22 #include "chrome/browser/profiles/profile.h"
23 #include "components/crx_file/id_util.h"
24 #include "components/keyed_service/content/browser_context_dependency_manager.h"
25 #include "components/pref_registry/pref_registry_syncable.h"
26 #include "extensions/browser/pref_names.h"
27 #include "extensions/common/url_pattern.h"
30 namespace extensions
{
32 ExtensionManagement::ExtensionManagement(PrefService
* pref_service
)
33 : pref_service_(pref_service
) {
34 pref_change_registrar_
.Init(pref_service_
);
35 base::Closure pref_change_callback
= base::Bind(
36 &ExtensionManagement::OnExtensionPrefChanged
, base::Unretained(this));
37 pref_change_registrar_
.Add(pref_names::kInstallAllowList
,
38 pref_change_callback
);
39 pref_change_registrar_
.Add(pref_names::kInstallDenyList
,
40 pref_change_callback
);
41 pref_change_registrar_
.Add(pref_names::kInstallForceList
,
42 pref_change_callback
);
43 pref_change_registrar_
.Add(pref_names::kAllowedInstallSites
,
44 pref_change_callback
);
45 pref_change_registrar_
.Add(pref_names::kAllowedTypes
, pref_change_callback
);
46 pref_change_registrar_
.Add(pref_names::kExtensionManagement
,
47 pref_change_callback
);
48 // Note that both |global_settings_| and |default_settings_| will be null
49 // before first call to Refresh(), so in order to resolve this, Refresh() must
50 // be called in the initialization of ExtensionManagement.
52 provider_
.reset(new StandardManagementPolicyProvider(this));
55 ExtensionManagement::~ExtensionManagement() {
58 void ExtensionManagement::AddObserver(Observer
* observer
) {
59 observer_list_
.AddObserver(observer
);
62 void ExtensionManagement::RemoveObserver(Observer
* observer
) {
63 observer_list_
.RemoveObserver(observer
);
66 ManagementPolicy::Provider
* ExtensionManagement::GetProvider() const {
67 return provider_
.get();
70 bool ExtensionManagement::BlacklistedByDefault() const {
71 return default_settings_
->installation_mode
== INSTALLATION_BLOCKED
;
74 ExtensionManagement::InstallationMode
ExtensionManagement::GetInstallationMode(
75 const ExtensionId
& id
) const {
76 return ReadById(id
)->installation_mode
;
79 scoped_ptr
<base::DictionaryValue
> ExtensionManagement::GetForceInstallList()
81 scoped_ptr
<base::DictionaryValue
> install_list(new base::DictionaryValue());
82 for (SettingsIdMap::const_iterator it
= settings_by_id_
.begin();
83 it
!= settings_by_id_
.end();
85 if (it
->second
->installation_mode
== INSTALLATION_FORCED
) {
86 ExternalPolicyLoader::AddExtension(
87 install_list
.get(), it
->first
, it
->second
->update_url
);
90 return install_list
.Pass();
93 scoped_ptr
<base::DictionaryValue
>
94 ExtensionManagement::GetRecommendedInstallList() const {
95 scoped_ptr
<base::DictionaryValue
> install_list(new base::DictionaryValue());
96 for (SettingsIdMap::const_iterator it
= settings_by_id_
.begin();
97 it
!= settings_by_id_
.end();
99 if (it
->second
->installation_mode
== INSTALLATION_RECOMMENDED
) {
100 ExternalPolicyLoader::AddExtension(
101 install_list
.get(), it
->first
, it
->second
->update_url
);
104 return install_list
.Pass();
107 bool ExtensionManagement::IsInstallationAllowed(const ExtensionId
& id
) const {
108 return ReadById(id
)->installation_mode
!= INSTALLATION_BLOCKED
;
111 bool ExtensionManagement::IsOffstoreInstallAllowed(
113 const GURL
& referrer_url
) const {
114 // No allowed install sites specified, disallow by default.
115 if (!global_settings_
->has_restricted_install_sources
)
118 const URLPatternSet
& url_patterns
= global_settings_
->install_sources
;
120 if (!url_patterns
.MatchesURL(url
))
123 // The referrer URL must also be whitelisted, unless the URL has the file
124 // scheme (there's no referrer for those URLs).
125 return url
.SchemeIsFile() || url_patterns
.MatchesURL(referrer_url
);
128 bool ExtensionManagement::IsAllowedManifestType(
129 Manifest::Type manifest_type
) const {
130 if (!global_settings_
->has_restricted_allowed_types
)
132 const std::vector
<Manifest::Type
>& allowed_types
=
133 global_settings_
->allowed_types
;
134 return std::find(allowed_types
.begin(), allowed_types
.end(), manifest_type
) !=
138 void ExtensionManagement::Refresh() {
139 // Load all extension management settings preferences.
140 const base::ListValue
* allowed_list_pref
=
141 static_cast<const base::ListValue
*>(LoadPreference(
142 pref_names::kInstallAllowList
, true, base::Value::TYPE_LIST
));
143 // Allow user to use preference to block certain extensions. Note that policy
144 // managed forcelist or whitelist will always override this.
145 const base::ListValue
* denied_list_pref
=
146 static_cast<const base::ListValue
*>(LoadPreference(
147 pref_names::kInstallDenyList
, false, base::Value::TYPE_LIST
));
148 const base::DictionaryValue
* forced_list_pref
=
149 static_cast<const base::DictionaryValue
*>(LoadPreference(
150 pref_names::kInstallForceList
, true, base::Value::TYPE_DICTIONARY
));
151 const base::ListValue
* install_sources_pref
=
152 static_cast<const base::ListValue
*>(LoadPreference(
153 pref_names::kAllowedInstallSites
, true, base::Value::TYPE_LIST
));
154 const base::ListValue
* allowed_types_pref
=
155 static_cast<const base::ListValue
*>(LoadPreference(
156 pref_names::kAllowedTypes
, true, base::Value::TYPE_LIST
));
157 const base::DictionaryValue
* dict_pref
=
158 static_cast<const base::DictionaryValue
*>(
159 LoadPreference(pref_names::kExtensionManagement
,
161 base::Value::TYPE_DICTIONARY
));
163 // Reset all settings.
164 global_settings_
.reset(new internal::GlobalSettings());
165 settings_by_id_
.clear();
166 default_settings_
.reset(new internal::IndividualSettings());
168 // Parse default settings.
169 const base::StringValue
wildcard("*");
170 if (denied_list_pref
&&
171 denied_list_pref
->Find(wildcard
) != denied_list_pref
->end()) {
172 default_settings_
->installation_mode
= INSTALLATION_BLOCKED
;
175 const base::DictionaryValue
* subdict
= NULL
;
177 dict_pref
->GetDictionary(schema_constants::kWildcard
, &subdict
)) {
178 if (!default_settings_
->Parse(
179 subdict
, internal::IndividualSettings::SCOPE_DEFAULT
)) {
180 LOG(WARNING
) << "Default extension management settings parsing error.";
181 default_settings_
->Reset();
184 // Settings from new preference have higher priority over legacy ones.
185 const base::ListValue
* list_value
= NULL
;
186 if (subdict
->GetList(schema_constants::kInstallSources
, &list_value
))
187 install_sources_pref
= list_value
;
188 if (subdict
->GetList(schema_constants::kAllowedTypes
, &list_value
))
189 allowed_types_pref
= list_value
;
192 // Parse legacy preferences.
195 if (allowed_list_pref
) {
196 for (base::ListValue::const_iterator it
= allowed_list_pref
->begin();
197 it
!= allowed_list_pref
->end(); ++it
) {
198 if ((*it
)->GetAsString(&id
) && crx_file::id_util::IdIsValid(id
))
199 AccessById(id
)->installation_mode
= INSTALLATION_ALLOWED
;
203 if (denied_list_pref
) {
204 for (base::ListValue::const_iterator it
= denied_list_pref
->begin();
205 it
!= denied_list_pref
->end(); ++it
) {
206 if ((*it
)->GetAsString(&id
) && crx_file::id_util::IdIsValid(id
))
207 AccessById(id
)->installation_mode
= INSTALLATION_BLOCKED
;
211 if (forced_list_pref
) {
212 std::string update_url
;
213 for (base::DictionaryValue::Iterator
it(*forced_list_pref
); !it
.IsAtEnd();
215 if (!crx_file::id_util::IdIsValid(it
.key()))
217 const base::DictionaryValue
* dict_value
= NULL
;
218 if (it
.value().GetAsDictionary(&dict_value
) &&
219 dict_value
->GetStringWithoutPathExpansion(
220 ExternalProviderImpl::kExternalUpdateUrl
, &update_url
)) {
221 internal::IndividualSettings
* by_id
= AccessById(it
.key());
222 by_id
->installation_mode
= INSTALLATION_FORCED
;
223 by_id
->update_url
= update_url
;
228 if (install_sources_pref
) {
229 global_settings_
->has_restricted_install_sources
= true;
230 for (base::ListValue::const_iterator it
= install_sources_pref
->begin();
231 it
!= install_sources_pref
->end(); ++it
) {
232 std::string url_pattern
;
233 if ((*it
)->GetAsString(&url_pattern
)) {
234 URLPattern
entry(URLPattern::SCHEME_ALL
);
235 if (entry
.Parse(url_pattern
) == URLPattern::PARSE_SUCCESS
) {
236 global_settings_
->install_sources
.AddPattern(entry
);
238 LOG(WARNING
) << "Invalid URL pattern in for preference "
239 << pref_names::kAllowedInstallSites
<< ": "
240 << url_pattern
<< ".";
246 if (allowed_types_pref
) {
247 global_settings_
->has_restricted_allowed_types
= true;
248 for (base::ListValue::const_iterator it
= allowed_types_pref
->begin();
249 it
!= allowed_types_pref
->end(); ++it
) {
251 std::string string_value
;
252 if ((*it
)->GetAsInteger(&int_value
) && int_value
>= 0 &&
253 int_value
< Manifest::Type::NUM_LOAD_TYPES
) {
254 global_settings_
->allowed_types
.push_back(
255 static_cast<Manifest::Type
>(int_value
));
256 } else if ((*it
)->GetAsString(&string_value
)) {
257 Manifest::Type manifest_type
=
258 schema_constants::GetManifestType(string_value
);
259 if (manifest_type
!= Manifest::TYPE_UNKNOWN
)
260 global_settings_
->allowed_types
.push_back(manifest_type
);
266 // Parse new extension management preference.
267 for (base::DictionaryValue::Iterator
iter(*dict_pref
); !iter
.IsAtEnd();
269 if (iter
.key() == schema_constants::kWildcard
)
271 if (!iter
.value().GetAsDictionary(&subdict
))
273 if (StartsWithASCII(iter
.key(), schema_constants::kUpdateUrlPrefix
, true))
275 const std::string
& extension_id
= iter
.key();
276 if (!crx_file::id_util::IdIsValid(extension_id
)) {
277 LOG(WARNING
) << "Invalid extension ID : " << extension_id
<< ".";
280 internal::IndividualSettings
* by_id
= AccessById(extension_id
);
281 if (!by_id
->Parse(subdict
,
282 internal::IndividualSettings::SCOPE_INDIVIDUAL
)) {
283 settings_by_id_
.erase(extension_id
);
284 LOG(WARNING
) << "Malformed Extension Management settings for "
285 << extension_id
<< ".";
291 const base::Value
* ExtensionManagement::LoadPreference(
292 const char* pref_name
,
294 base::Value::Type expected_type
) {
295 const PrefService::Preference
* pref
=
296 pref_service_
->FindPreference(pref_name
);
297 if (pref
&& !pref
->IsDefaultValue() &&
298 (!force_managed
|| pref
->IsManaged())) {
299 const base::Value
* value
= pref
->GetValue();
300 if (value
&& value
->IsType(expected_type
))
306 void ExtensionManagement::OnExtensionPrefChanged() {
308 NotifyExtensionManagementPrefChanged();
311 void ExtensionManagement::NotifyExtensionManagementPrefChanged() {
313 Observer
, observer_list_
, OnExtensionManagementSettingsChanged());
316 const internal::IndividualSettings
* ExtensionManagement::ReadById(
317 const ExtensionId
& id
) const {
318 DCHECK(crx_file::id_util::IdIsValid(id
)) << "Invalid ID: " << id
;
319 SettingsIdMap::const_iterator it
= settings_by_id_
.find(id
);
320 if (it
!= settings_by_id_
.end())
322 return default_settings_
.get();
325 const internal::GlobalSettings
* ExtensionManagement::ReadGlobalSettings()
327 return global_settings_
.get();
330 internal::IndividualSettings
* ExtensionManagement::AccessById(
331 const ExtensionId
& id
) {
332 DCHECK(crx_file::id_util::IdIsValid(id
)) << "Invalid ID: " << id
;
333 SettingsIdMap::iterator it
= settings_by_id_
.find(id
);
334 if (it
== settings_by_id_
.end()) {
335 scoped_ptr
<internal::IndividualSettings
> settings(
336 new internal::IndividualSettings(*default_settings_
));
337 it
= settings_by_id_
.add(id
, settings
.Pass()).first
;
342 ExtensionManagement
* ExtensionManagementFactory::GetForBrowserContext(
343 content::BrowserContext
* context
) {
344 return static_cast<ExtensionManagement
*>(
345 GetInstance()->GetServiceForBrowserContext(context
, true));
348 ExtensionManagementFactory
* ExtensionManagementFactory::GetInstance() {
349 return Singleton
<ExtensionManagementFactory
>::get();
352 ExtensionManagementFactory::ExtensionManagementFactory()
353 : BrowserContextKeyedServiceFactory(
354 "ExtensionManagement",
355 BrowserContextDependencyManager::GetInstance()) {
358 ExtensionManagementFactory::~ExtensionManagementFactory() {
361 KeyedService
* ExtensionManagementFactory::BuildServiceInstanceFor(
362 content::BrowserContext
* context
) const {
363 return new ExtensionManagement(
364 Profile::FromBrowserContext(context
)->GetPrefs());
367 content::BrowserContext
* ExtensionManagementFactory::GetBrowserContextToUse(
368 content::BrowserContext
* context
) const {
369 return chrome::GetBrowserContextRedirectedInIncognito(context
);
372 void ExtensionManagementFactory::RegisterProfilePrefs(
373 user_prefs::PrefRegistrySyncable
* user_prefs
) {
374 user_prefs
->RegisterDictionaryPref(
375 pref_names::kExtensionManagement
,
376 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF
);
379 } // namespace extensions