Add recommended extension installation support
[chromium-blink-merge.git] / chrome / browser / extensions / extension_management.cc
blob85c69841f75e2d99f91ce94e3fc18f6d9f9654b6
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"
7 #include <algorithm>
8 #include <string>
9 #include <vector>
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"
28 #include "url/gurl.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.
51 Refresh();
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()
80 const {
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();
84 ++it) {
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();
98 ++it) {
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(
112 const GURL& url,
113 const GURL& referrer_url) const {
114 // No allowed install sites specified, disallow by default.
115 if (!global_settings_->has_restricted_install_sources)
116 return false;
118 const URLPatternSet& url_patterns = global_settings_->install_sources;
120 if (!url_patterns.MatchesURL(url))
121 return false;
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)
131 return true;
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) !=
135 allowed_types.end();
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,
160 true,
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;
176 if (dict_pref &&
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.
193 ExtensionId id;
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();
214 it.Advance()) {
215 if (!crx_file::id_util::IdIsValid(it.key()))
216 continue;
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);
237 } else {
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) {
250 int int_value;
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);
265 if (dict_pref) {
266 // Parse new extension management preference.
267 for (base::DictionaryValue::Iterator iter(*dict_pref); !iter.IsAtEnd();
268 iter.Advance()) {
269 if (iter.key() == schema_constants::kWildcard)
270 continue;
271 if (!iter.value().GetAsDictionary(&subdict))
272 continue;
273 if (StartsWithASCII(iter.key(), schema_constants::kUpdateUrlPrefix, true))
274 continue;
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 << ".";
278 continue;
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,
293 bool force_managed,
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))
301 return value;
303 return NULL;
306 void ExtensionManagement::OnExtensionPrefChanged() {
307 Refresh();
308 NotifyExtensionManagementPrefChanged();
311 void ExtensionManagement::NotifyExtensionManagementPrefChanged() {
312 FOR_EACH_OBSERVER(
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())
321 return it->second;
322 return default_settings_.get();
325 const internal::GlobalSettings* ExtensionManagement::ReadGlobalSettings()
326 const {
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;
339 return it->second;
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