blob: dff3946aebc7b63e0667c4513baa4f6ae6b6ea78 [file] [log] [blame]
binjin5f405ef2014-09-03 21:23:161// 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.
4
5#include "chrome/browser/extensions/extension_management.h"
6
binjin81d7c552014-10-02 11:47:127#include <algorithm>
8#include <string>
9#include <vector>
10
binjin1569c9b2014-09-05 13:33:1811#include "base/bind.h"
12#include "base/bind_helpers.h"
binjin5f405ef2014-09-03 21:23:1613#include "base/logging.h"
14#include "base/prefs/pref_service.h"
binjinb2454382014-09-22 15:17:4315#include "base/strings/string_util.h"
16#include "chrome/browser/extensions/extension_management_constants.h"
binjin81d7c552014-10-02 11:47:1217#include "chrome/browser/extensions/extension_management_internal.h"
binjin30301062014-09-08 20:27:3418#include "chrome/browser/extensions/external_policy_loader.h"
binjin5f405ef2014-09-03 21:23:1619#include "chrome/browser/extensions/external_provider_impl.h"
binjin1569c9b2014-09-05 13:33:1820#include "chrome/browser/extensions/standard_management_policy_provider.h"
binjin9733df12014-09-08 15:21:2121#include "chrome/browser/profiles/incognito_helpers.h"
binjin1569c9b2014-09-05 13:33:1822#include "chrome/browser/profiles/profile.h"
binjin5f405ef2014-09-03 21:23:1623#include "components/crx_file/id_util.h"
binjin1569c9b2014-09-05 13:33:1824#include "components/keyed_service/content/browser_context_dependency_manager.h"
binjinb2454382014-09-22 15:17:4325#include "components/pref_registry/pref_registry_syncable.h"
binjin5f405ef2014-09-03 21:23:1626#include "extensions/browser/pref_names.h"
27#include "extensions/common/url_pattern.h"
binjin311ecdf2014-09-12 22:56:5228#include "url/gurl.h"
binjin5f405ef2014-09-03 21:23:1629
30namespace extensions {
31
binjin5f405ef2014-09-03 21:23:1632ExtensionManagement::ExtensionManagement(PrefService* pref_service)
33 : pref_service_(pref_service) {
binjin1569c9b2014-09-05 13:33:1834 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);
binjinb2454382014-09-22 15:17:4346 pref_change_registrar_.Add(pref_names::kExtensionManagement,
47 pref_change_callback);
binjin81d7c552014-10-02 11:47:1248 // 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.
binjin1569c9b2014-09-05 13:33:1851 Refresh();
52 provider_.reset(new StandardManagementPolicyProvider(this));
binjin5f405ef2014-09-03 21:23:1653}
54
55ExtensionManagement::~ExtensionManagement() {
56}
57
binjin1569c9b2014-09-05 13:33:1858void ExtensionManagement::AddObserver(Observer* observer) {
59 observer_list_.AddObserver(observer);
60}
61
62void ExtensionManagement::RemoveObserver(Observer* observer) {
63 observer_list_.RemoveObserver(observer);
64}
65
binjin81d7c552014-10-02 11:47:1266ManagementPolicy::Provider* ExtensionManagement::GetProvider() const {
binjin1569c9b2014-09-05 13:33:1867 return provider_.get();
68}
69
binjin81d7c552014-10-02 11:47:1270bool ExtensionManagement::BlacklistedByDefault() const {
71 return default_settings_->installation_mode == INSTALLATION_BLOCKED;
72}
73
74ExtensionManagement::InstallationMode ExtensionManagement::GetInstallationMode(
75 const ExtensionId& id) const {
76 return ReadById(id)->installation_mode;
binjin1569c9b2014-09-05 13:33:1877}
78
binjin30301062014-09-08 20:27:3479scoped_ptr<base::DictionaryValue> ExtensionManagement::GetForceInstallList()
80 const {
binjincccacef2014-10-13 19:00:2081 scoped_ptr<base::DictionaryValue> install_list(new base::DictionaryValue());
binjin30301062014-09-08 20:27:3482 for (SettingsIdMap::const_iterator it = settings_by_id_.begin();
83 it != settings_by_id_.end();
84 ++it) {
binjin81d7c552014-10-02 11:47:1285 if (it->second->installation_mode == INSTALLATION_FORCED) {
binjin30301062014-09-08 20:27:3486 ExternalPolicyLoader::AddExtension(
binjincccacef2014-10-13 19:00:2087 install_list.get(), it->first, it->second->update_url);
binjin30301062014-09-08 20:27:3488 }
89 }
binjincccacef2014-10-13 19:00:2090 return install_list.Pass();
91}
92
93scoped_ptr<base::DictionaryValue>
94ExtensionManagement::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);
102 }
103 }
104 return install_list.Pass();
binjin30301062014-09-08 20:27:34105}
106
binjinc641add2014-10-15 16:20:45107bool ExtensionManagement::IsInstallationExplicitlyAllowed(
108 const ExtensionId& id) const {
109 SettingsIdMap::const_iterator it = settings_by_id_.find(id);
110 // No settings explicitly specified for |id|.
111 if (it == settings_by_id_.end())
112 return false;
113 // Checks if the extension is on the automatically installed list or
114 // install white-list.
115 InstallationMode mode = it->second->installation_mode;
116 return mode == INSTALLATION_FORCED || mode == INSTALLATION_RECOMMENDED ||
117 mode == INSTALLATION_ALLOWED;
binjin30301062014-09-08 20:27:34118}
119
binjin81d7c552014-10-02 11:47:12120bool ExtensionManagement::IsOffstoreInstallAllowed(
121 const GURL& url,
122 const GURL& referrer_url) const {
binjin311ecdf2014-09-12 22:56:52123 // No allowed install sites specified, disallow by default.
binjin81d7c552014-10-02 11:47:12124 if (!global_settings_->has_restricted_install_sources)
binjin311ecdf2014-09-12 22:56:52125 return false;
126
binjin81d7c552014-10-02 11:47:12127 const URLPatternSet& url_patterns = global_settings_->install_sources;
binjin311ecdf2014-09-12 22:56:52128
129 if (!url_patterns.MatchesURL(url))
130 return false;
131
132 // The referrer URL must also be whitelisted, unless the URL has the file
133 // scheme (there's no referrer for those URLs).
134 return url.SchemeIsFile() || url_patterns.MatchesURL(referrer_url);
135}
136
binjin81d7c552014-10-02 11:47:12137bool ExtensionManagement::IsAllowedManifestType(
138 Manifest::Type manifest_type) const {
139 if (!global_settings_->has_restricted_allowed_types)
140 return true;
141 const std::vector<Manifest::Type>& allowed_types =
142 global_settings_->allowed_types;
143 return std::find(allowed_types.begin(), allowed_types.end(), manifest_type) !=
144 allowed_types.end();
binjin1569c9b2014-09-05 13:33:18145}
146
binjin5f405ef2014-09-03 21:23:16147void ExtensionManagement::Refresh() {
148 // Load all extension management settings preferences.
149 const base::ListValue* allowed_list_pref =
150 static_cast<const base::ListValue*>(LoadPreference(
151 pref_names::kInstallAllowList, true, base::Value::TYPE_LIST));
152 // Allow user to use preference to block certain extensions. Note that policy
153 // managed forcelist or whitelist will always override this.
154 const base::ListValue* denied_list_pref =
155 static_cast<const base::ListValue*>(LoadPreference(
156 pref_names::kInstallDenyList, false, base::Value::TYPE_LIST));
157 const base::DictionaryValue* forced_list_pref =
158 static_cast<const base::DictionaryValue*>(LoadPreference(
159 pref_names::kInstallForceList, true, base::Value::TYPE_DICTIONARY));
160 const base::ListValue* install_sources_pref =
161 static_cast<const base::ListValue*>(LoadPreference(
162 pref_names::kAllowedInstallSites, true, base::Value::TYPE_LIST));
163 const base::ListValue* allowed_types_pref =
164 static_cast<const base::ListValue*>(LoadPreference(
165 pref_names::kAllowedTypes, true, base::Value::TYPE_LIST));
binjinb2454382014-09-22 15:17:43166 const base::DictionaryValue* dict_pref =
167 static_cast<const base::DictionaryValue*>(
168 LoadPreference(pref_names::kExtensionManagement,
169 true,
170 base::Value::TYPE_DICTIONARY));
binjin5f405ef2014-09-03 21:23:16171
172 // Reset all settings.
binjin81d7c552014-10-02 11:47:12173 global_settings_.reset(new internal::GlobalSettings());
binjin5f405ef2014-09-03 21:23:16174 settings_by_id_.clear();
binjin81d7c552014-10-02 11:47:12175 default_settings_.reset(new internal::IndividualSettings());
binjin5f405ef2014-09-03 21:23:16176
177 // Parse default settings.
178 const base::StringValue wildcard("*");
179 if (denied_list_pref &&
180 denied_list_pref->Find(wildcard) != denied_list_pref->end()) {
binjin81d7c552014-10-02 11:47:12181 default_settings_->installation_mode = INSTALLATION_BLOCKED;
binjin5f405ef2014-09-03 21:23:16182 }
183
binjinb2454382014-09-22 15:17:43184 const base::DictionaryValue* subdict = NULL;
185 if (dict_pref &&
186 dict_pref->GetDictionary(schema_constants::kWildcard, &subdict)) {
binjin81d7c552014-10-02 11:47:12187 if (!default_settings_->Parse(
188 subdict, internal::IndividualSettings::SCOPE_DEFAULT)) {
binjinb2454382014-09-22 15:17:43189 LOG(WARNING) << "Default extension management settings parsing error.";
binjin81d7c552014-10-02 11:47:12190 default_settings_->Reset();
binjinb2454382014-09-22 15:17:43191 }
192
193 // Settings from new preference have higher priority over legacy ones.
194 const base::ListValue* list_value = NULL;
195 if (subdict->GetList(schema_constants::kInstallSources, &list_value))
196 install_sources_pref = list_value;
197 if (subdict->GetList(schema_constants::kAllowedTypes, &list_value))
198 allowed_types_pref = list_value;
199 }
200
binjin5f405ef2014-09-03 21:23:16201 // Parse legacy preferences.
202 ExtensionId id;
203
204 if (allowed_list_pref) {
205 for (base::ListValue::const_iterator it = allowed_list_pref->begin();
206 it != allowed_list_pref->end(); ++it) {
207 if ((*it)->GetAsString(&id) && crx_file::id_util::IdIsValid(id))
208 AccessById(id)->installation_mode = INSTALLATION_ALLOWED;
209 }
210 }
211
212 if (denied_list_pref) {
213 for (base::ListValue::const_iterator it = denied_list_pref->begin();
214 it != denied_list_pref->end(); ++it) {
215 if ((*it)->GetAsString(&id) && crx_file::id_util::IdIsValid(id))
216 AccessById(id)->installation_mode = INSTALLATION_BLOCKED;
217 }
218 }
219
220 if (forced_list_pref) {
221 std::string update_url;
222 for (base::DictionaryValue::Iterator it(*forced_list_pref); !it.IsAtEnd();
223 it.Advance()) {
224 if (!crx_file::id_util::IdIsValid(it.key()))
225 continue;
226 const base::DictionaryValue* dict_value = NULL;
227 if (it.value().GetAsDictionary(&dict_value) &&
228 dict_value->GetStringWithoutPathExpansion(
229 ExternalProviderImpl::kExternalUpdateUrl, &update_url)) {
binjin81d7c552014-10-02 11:47:12230 internal::IndividualSettings* by_id = AccessById(it.key());
binjin5f405ef2014-09-03 21:23:16231 by_id->installation_mode = INSTALLATION_FORCED;
232 by_id->update_url = update_url;
233 }
234 }
235 }
236
237 if (install_sources_pref) {
binjin81d7c552014-10-02 11:47:12238 global_settings_->has_restricted_install_sources = true;
binjin5f405ef2014-09-03 21:23:16239 for (base::ListValue::const_iterator it = install_sources_pref->begin();
240 it != install_sources_pref->end(); ++it) {
binjinb2454382014-09-22 15:17:43241 std::string url_pattern;
binjin5f405ef2014-09-03 21:23:16242 if ((*it)->GetAsString(&url_pattern)) {
binjinb2454382014-09-22 15:17:43243 URLPattern entry(URLPattern::SCHEME_ALL);
binjin5f405ef2014-09-03 21:23:16244 if (entry.Parse(url_pattern) == URLPattern::PARSE_SUCCESS) {
binjin81d7c552014-10-02 11:47:12245 global_settings_->install_sources.AddPattern(entry);
binjin5f405ef2014-09-03 21:23:16246 } else {
247 LOG(WARNING) << "Invalid URL pattern in for preference "
248 << pref_names::kAllowedInstallSites << ": "
249 << url_pattern << ".";
250 }
251 }
252 }
253 }
254
255 if (allowed_types_pref) {
binjin81d7c552014-10-02 11:47:12256 global_settings_->has_restricted_allowed_types = true;
binjin5f405ef2014-09-03 21:23:16257 for (base::ListValue::const_iterator it = allowed_types_pref->begin();
258 it != allowed_types_pref->end(); ++it) {
259 int int_value;
binjinb2454382014-09-22 15:17:43260 std::string string_value;
binjin5f405ef2014-09-03 21:23:16261 if ((*it)->GetAsInteger(&int_value) && int_value >= 0 &&
262 int_value < Manifest::Type::NUM_LOAD_TYPES) {
binjin81d7c552014-10-02 11:47:12263 global_settings_->allowed_types.push_back(
binjin5f405ef2014-09-03 21:23:16264 static_cast<Manifest::Type>(int_value));
binjinb2454382014-09-22 15:17:43265 } else if ((*it)->GetAsString(&string_value)) {
266 Manifest::Type manifest_type =
267 schema_constants::GetManifestType(string_value);
268 if (manifest_type != Manifest::TYPE_UNKNOWN)
binjin81d7c552014-10-02 11:47:12269 global_settings_->allowed_types.push_back(manifest_type);
binjin5f405ef2014-09-03 21:23:16270 }
271 }
272 }
273
binjinb2454382014-09-22 15:17:43274 if (dict_pref) {
275 // Parse new extension management preference.
276 for (base::DictionaryValue::Iterator iter(*dict_pref); !iter.IsAtEnd();
277 iter.Advance()) {
278 if (iter.key() == schema_constants::kWildcard)
279 continue;
binjin81d7c552014-10-02 11:47:12280 if (!iter.value().GetAsDictionary(&subdict))
binjinb2454382014-09-22 15:17:43281 continue;
binjin6a6eb152014-09-24 18:36:34282 if (StartsWithASCII(iter.key(), schema_constants::kUpdateUrlPrefix, true))
binjinb2454382014-09-22 15:17:43283 continue;
284 const std::string& extension_id = iter.key();
285 if (!crx_file::id_util::IdIsValid(extension_id)) {
binjin81d7c552014-10-02 11:47:12286 LOG(WARNING) << "Invalid extension ID : " << extension_id << ".";
binjinb2454382014-09-22 15:17:43287 continue;
288 }
binjin81d7c552014-10-02 11:47:12289 internal::IndividualSettings* by_id = AccessById(extension_id);
290 if (!by_id->Parse(subdict,
291 internal::IndividualSettings::SCOPE_INDIVIDUAL)) {
292 settings_by_id_.erase(extension_id);
binjinb2454382014-09-22 15:17:43293 LOG(WARNING) << "Malformed Extension Management settings for "
binjin6a6eb152014-09-24 18:36:34294 << extension_id << ".";
binjinb2454382014-09-22 15:17:43295 }
296 }
297 }
binjin5f405ef2014-09-03 21:23:16298}
299
binjin5f405ef2014-09-03 21:23:16300const base::Value* ExtensionManagement::LoadPreference(
301 const char* pref_name,
302 bool force_managed,
303 base::Value::Type expected_type) {
304 const PrefService::Preference* pref =
305 pref_service_->FindPreference(pref_name);
306 if (pref && !pref->IsDefaultValue() &&
307 (!force_managed || pref->IsManaged())) {
308 const base::Value* value = pref->GetValue();
309 if (value && value->IsType(expected_type))
310 return value;
311 }
312 return NULL;
313}
314
binjin1569c9b2014-09-05 13:33:18315void ExtensionManagement::OnExtensionPrefChanged() {
316 Refresh();
317 NotifyExtensionManagementPrefChanged();
318}
319
320void ExtensionManagement::NotifyExtensionManagementPrefChanged() {
321 FOR_EACH_OBSERVER(
322 Observer, observer_list_, OnExtensionManagementSettingsChanged());
323}
324
binjin81d7c552014-10-02 11:47:12325const internal::IndividualSettings* ExtensionManagement::ReadById(
326 const ExtensionId& id) const {
327 DCHECK(crx_file::id_util::IdIsValid(id)) << "Invalid ID: " << id;
328 SettingsIdMap::const_iterator it = settings_by_id_.find(id);
329 if (it != settings_by_id_.end())
330 return it->second;
331 return default_settings_.get();
332}
333
334const internal::GlobalSettings* ExtensionManagement::ReadGlobalSettings()
335 const {
336 return global_settings_.get();
337}
338
339internal::IndividualSettings* ExtensionManagement::AccessById(
binjin5f405ef2014-09-03 21:23:16340 const ExtensionId& id) {
341 DCHECK(crx_file::id_util::IdIsValid(id)) << "Invalid ID: " << id;
342 SettingsIdMap::iterator it = settings_by_id_.find(id);
binjin81d7c552014-10-02 11:47:12343 if (it == settings_by_id_.end()) {
344 scoped_ptr<internal::IndividualSettings> settings(
345 new internal::IndividualSettings(*default_settings_));
346 it = settings_by_id_.add(id, settings.Pass()).first;
347 }
348 return it->second;
binjin5f405ef2014-09-03 21:23:16349}
350
binjin1569c9b2014-09-05 13:33:18351ExtensionManagement* ExtensionManagementFactory::GetForBrowserContext(
352 content::BrowserContext* context) {
353 return static_cast<ExtensionManagement*>(
354 GetInstance()->GetServiceForBrowserContext(context, true));
355}
356
357ExtensionManagementFactory* ExtensionManagementFactory::GetInstance() {
358 return Singleton<ExtensionManagementFactory>::get();
359}
360
361ExtensionManagementFactory::ExtensionManagementFactory()
362 : BrowserContextKeyedServiceFactory(
363 "ExtensionManagement",
364 BrowserContextDependencyManager::GetInstance()) {
365}
366
367ExtensionManagementFactory::~ExtensionManagementFactory() {
368}
369
370KeyedService* ExtensionManagementFactory::BuildServiceInstanceFor(
371 content::BrowserContext* context) const {
372 return new ExtensionManagement(
373 Profile::FromBrowserContext(context)->GetPrefs());
374}
375
binjin9733df12014-09-08 15:21:21376content::BrowserContext* ExtensionManagementFactory::GetBrowserContextToUse(
377 content::BrowserContext* context) const {
378 return chrome::GetBrowserContextRedirectedInIncognito(context);
379}
380
binjinb2454382014-09-22 15:17:43381void ExtensionManagementFactory::RegisterProfilePrefs(
382 user_prefs::PrefRegistrySyncable* user_prefs) {
383 user_prefs->RegisterDictionaryPref(
384 pref_names::kExtensionManagement,
385 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
386}
387
binjin5f405ef2014-09-03 21:23:16388} // namespace extensions