blob: 5c68984920dea55c3ca2339319c9febb8a1038d5 [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
dcheng1fc00f12015-12-26 22:18:037#include <utility>
binjin81d7c552014-10-02 11:47:128
binjin1569c9b2014-09-05 13:33:189#include "base/bind.h"
10#include "base/bind_helpers.h"
binjin5f405ef2014-09-03 21:23:1611#include "base/logging.h"
rkaplowdd66a1342015-03-05 00:31:4912#include "base/metrics/histogram_macros.h"
tripta.g0ac673a2017-07-07 05:45:0913#include "base/stl_util.h"
binjine6b58b52014-10-31 01:55:5714#include "base/strings/string16.h"
Hans Wennborg9208992ba2019-10-14 14:02:4915#include "base/strings/string_split.h"
binjinb2454382014-09-22 15:17:4316#include "base/strings/string_util.h"
Kyle Spiers3bc96a192019-04-11 23:43:2117#include "base/syslog_logging.h"
rkaplowdd66a1342015-03-05 00:31:4918#include "base/trace_event/trace_event.h"
binjin8e3d0182014-12-04 16:44:2819#include "base/version.h"
binjinb2454382014-09-22 15:17:4320#include "chrome/browser/extensions/extension_management_constants.h"
binjin81d7c552014-10-02 11:47:1221#include "chrome/browser/extensions/extension_management_internal.h"
binjin30301062014-09-08 20:27:3422#include "chrome/browser/extensions/external_policy_loader.h"
binjin5f405ef2014-09-03 21:23:1623#include "chrome/browser/extensions/external_provider_impl.h"
Oleg Davydovc00866812019-04-04 10:47:2724#include "chrome/browser/extensions/forced_extensions/installation_reporter.h"
Oleg Davydov66246bd92019-09-26 16:31:5125#include "chrome/browser/extensions/forced_extensions/installation_reporter_factory.h"
binjine6b58b52014-10-31 01:55:5726#include "chrome/browser/extensions/permissions_based_management_policy_provider.h"
binjin1569c9b2014-09-05 13:33:1827#include "chrome/browser/extensions/standard_management_policy_provider.h"
binjin9733df12014-09-08 15:21:2128#include "chrome/browser/profiles/incognito_helpers.h"
binjin1569c9b2014-09-05 13:33:1829#include "chrome/browser/profiles/profile.h"
Owen Min94627f82019-09-09 21:44:5130#include "chrome/common/chrome_features.h"
Owen Mina9a13e12018-11-01 20:43:5231#include "chrome/common/extensions/extension_constants.h"
32#include "chrome/common/pref_names.h"
binjin5f405ef2014-09-03 21:23:1633#include "components/crx_file/id_util.h"
binjin1569c9b2014-09-05 13:33:1834#include "components/keyed_service/content/browser_context_dependency_manager.h"
binjinb2454382014-09-22 15:17:4335#include "components/pref_registry/pref_registry_syncable.h"
brettwb1fc1b82016-02-02 00:19:0836#include "components/prefs/pref_service.h"
binjin5f405ef2014-09-03 21:23:1637#include "extensions/browser/pref_names.h"
rdevlin.cronin0670b562016-07-02 02:05:4338#include "extensions/common/extension.h"
Owen Mina9a13e12018-11-01 20:43:5239#include "extensions/common/extension_urls.h"
binjin685ade82014-11-06 09:53:5640#include "extensions/common/manifest_constants.h"
binjine6b58b52014-10-31 01:55:5741#include "extensions/common/permissions/api_permission_set.h"
42#include "extensions/common/permissions/permission_set.h"
binjin5f405ef2014-09-03 21:23:1643#include "extensions/common/url_pattern.h"
binjin311ecdf2014-09-12 22:56:5244#include "url/gurl.h"
binjin5f405ef2014-09-03 21:23:1645
achuith4607f072017-03-08 11:49:1346#if defined(OS_CHROMEOS)
47#include "chrome/browser/chromeos/profiles/profile_helper.h"
48#endif
49
binjin5f405ef2014-09-03 21:23:1650namespace extensions {
51
Sergey Poromov741e70702018-10-11 20:11:5452ExtensionManagement::ExtensionManagement(Profile* profile)
53 : profile_(profile), pref_service_(profile_->GetPrefs()) {
rkaplowdd66a1342015-03-05 00:31:4954 TRACE_EVENT0("browser,startup",
55 "ExtensionManagement::ExtensionManagement::ctor");
Sergey Poromov741e70702018-10-11 20:11:5456#if defined(OS_CHROMEOS)
57 is_signin_profile_ = chromeos::ProfileHelper::IsSigninProfile(profile);
58#endif
binjin1569c9b2014-09-05 13:33:1859 pref_change_registrar_.Init(pref_service_);
60 base::Closure pref_change_callback = base::Bind(
61 &ExtensionManagement::OnExtensionPrefChanged, base::Unretained(this));
62 pref_change_registrar_.Add(pref_names::kInstallAllowList,
63 pref_change_callback);
64 pref_change_registrar_.Add(pref_names::kInstallDenyList,
65 pref_change_callback);
66 pref_change_registrar_.Add(pref_names::kInstallForceList,
67 pref_change_callback);
Alexander Hendrichb07fd55b2019-04-01 09:24:3768 pref_change_registrar_.Add(pref_names::kLoginScreenExtensions,
achuith4607f072017-03-08 11:49:1369 pref_change_callback);
binjin1569c9b2014-09-05 13:33:1870 pref_change_registrar_.Add(pref_names::kAllowedInstallSites,
71 pref_change_callback);
72 pref_change_registrar_.Add(pref_names::kAllowedTypes, pref_change_callback);
binjinb2454382014-09-22 15:17:4373 pref_change_registrar_.Add(pref_names::kExtensionManagement,
74 pref_change_callback);
Owen Min4792c392019-10-24 22:49:1475 pref_change_registrar_.Add(prefs::kCloudExtensionRequestEnabled,
76 pref_change_callback);
Owen Mina9a13e12018-11-01 20:43:5277#if !defined(OS_CHROMEOS)
78 pref_change_registrar_.Add(prefs::kCloudReportingEnabled,
79 pref_change_callback);
80#endif
binjin81d7c552014-10-02 11:47:1281 // Note that both |global_settings_| and |default_settings_| will be null
82 // before first call to Refresh(), so in order to resolve this, Refresh() must
83 // be called in the initialization of ExtensionManagement.
binjin1569c9b2014-09-05 13:33:1884 Refresh();
lazyboy4aeef202016-09-07 21:28:5985 providers_.push_back(
Jinho Bangb5216cec2018-01-17 19:43:1186 std::make_unique<StandardManagementPolicyProvider>(this));
lazyboy4aeef202016-09-07 21:28:5987 providers_.push_back(
Jinho Bangb5216cec2018-01-17 19:43:1188 std::make_unique<PermissionsBasedManagementPolicyProvider>(this));
binjin5f405ef2014-09-03 21:23:1689}
90
91ExtensionManagement::~ExtensionManagement() {
92}
93
binjine6b58b52014-10-31 01:55:5794void ExtensionManagement::Shutdown() {
95 pref_change_registrar_.RemoveAll();
96 pref_service_ = nullptr;
97}
98
binjin1569c9b2014-09-05 13:33:1899void ExtensionManagement::AddObserver(Observer* observer) {
100 observer_list_.AddObserver(observer);
101}
102
103void ExtensionManagement::RemoveObserver(Observer* observer) {
104 observer_list_.RemoveObserver(observer);
105}
106
lazyboy4aeef202016-09-07 21:28:59107const std::vector<std::unique_ptr<ManagementPolicy::Provider>>&
108ExtensionManagement::GetProviders() const {
109 return providers_;
binjin1569c9b2014-09-05 13:33:18110}
111
binjin81d7c552014-10-02 11:47:12112bool ExtensionManagement::BlacklistedByDefault() const {
Kyle Spiersbb4b9f5f52019-05-02 17:17:15113 return (default_settings_->installation_mode == INSTALLATION_BLOCKED ||
114 default_settings_->installation_mode == INSTALLATION_REMOVED);
binjin81d7c552014-10-02 11:47:12115}
116
117ExtensionManagement::InstallationMode ExtensionManagement::GetInstallationMode(
binjin685ade82014-11-06 09:53:56118 const Extension* extension) const {
Owen Minf42d09462019-11-05 21:20:43119 std::string update_url;
120 if (extension->manifest()->GetString(manifest_keys::kUpdateURL, &update_url))
121 return GetInstallationMode(extension->id(), update_url);
122 return GetInstallationMode(extension->id(), std::string());
123}
124
125ExtensionManagement::InstallationMode ExtensionManagement::GetInstallationMode(
126 const ExtensionId& extension_id,
127 const std::string& update_url) const {
binjin685ade82014-11-06 09:53:56128 // Check per-extension installation mode setting first.
Owen Minf42d09462019-11-05 21:20:43129 auto iter_id = settings_by_id_.find(extension_id);
binjin685ade82014-11-06 09:53:56130 if (iter_id != settings_by_id_.end())
131 return iter_id->second->installation_mode;
binjin685ade82014-11-06 09:53:56132 // Check per-update-url installation mode setting.
Owen Minf42d09462019-11-05 21:20:43133 if (!update_url.empty()) {
binjin685ade82014-11-06 09:53:56134 auto iter_update_url = settings_by_update_url_.find(update_url);
135 if (iter_update_url != settings_by_update_url_.end())
136 return iter_update_url->second->installation_mode;
137 }
138 // Fall back to default installation mode setting.
139 return default_settings_->installation_mode;
binjin1569c9b2014-09-05 13:33:18140}
141
dchengc963c7142016-04-08 03:55:22142std::unique_ptr<base::DictionaryValue>
hashimoto146e05a2016-11-18 07:42:53143ExtensionManagement::GetForceInstallList() const {
achuith4607f072017-03-08 11:49:13144 return GetInstallListByMode(INSTALLATION_FORCED);
binjincccacef2014-10-13 19:00:20145}
146
dchengc963c7142016-04-08 03:55:22147std::unique_ptr<base::DictionaryValue>
binjincccacef2014-10-13 19:00:20148ExtensionManagement::GetRecommendedInstallList() const {
achuith4607f072017-03-08 11:49:13149 return GetInstallListByMode(INSTALLATION_RECOMMENDED);
binjin30301062014-09-08 20:27:34150}
151
Yann Dago726278c62019-03-18 14:59:55152bool ExtensionManagement::HasWhitelistedExtension() const {
Kyle Spiersbb4b9f5f52019-05-02 17:17:15153 if (default_settings_->installation_mode != INSTALLATION_BLOCKED &&
154 default_settings_->installation_mode != INSTALLATION_REMOVED) {
Yann Dago726278c62019-03-18 14:59:55155 return true;
Kyle Spiersbb4b9f5f52019-05-02 17:17:15156 }
Yann Dago726278c62019-03-18 14:59:55157
158 for (const auto& it : settings_by_id_) {
159 if (it.second->installation_mode == INSTALLATION_ALLOWED)
160 return true;
161 }
162 return false;
163}
164
binjinc641add2014-10-15 16:20:45165bool ExtensionManagement::IsInstallationExplicitlyAllowed(
166 const ExtensionId& id) const {
avi3ec9c0d2016-12-27 22:38:06167 auto it = settings_by_id_.find(id);
binjinc641add2014-10-15 16:20:45168 // No settings explicitly specified for |id|.
169 if (it == settings_by_id_.end())
170 return false;
171 // Checks if the extension is on the automatically installed list or
172 // install white-list.
173 InstallationMode mode = it->second->installation_mode;
174 return mode == INSTALLATION_FORCED || mode == INSTALLATION_RECOMMENDED ||
175 mode == INSTALLATION_ALLOWED;
binjin30301062014-09-08 20:27:34176}
177
Owen Minf8c0ec592019-10-17 15:25:30178bool ExtensionManagement::IsInstallationExplicitlyBlocked(
179 const ExtensionId& id) const {
180 auto it = settings_by_id_.find(id);
181 // No settings explicitly specified for |id|.
182 if (it == settings_by_id_.end())
183 return false;
184 // Checks if the extension is on the black list or removed list.
185 InstallationMode mode = it->second->installation_mode;
186 return mode == INSTALLATION_BLOCKED || mode == INSTALLATION_REMOVED;
187}
188
binjin81d7c552014-10-02 11:47:12189bool ExtensionManagement::IsOffstoreInstallAllowed(
190 const GURL& url,
191 const GURL& referrer_url) const {
binjin311ecdf2014-09-12 22:56:52192 // No allowed install sites specified, disallow by default.
binjin81d7c552014-10-02 11:47:12193 if (!global_settings_->has_restricted_install_sources)
binjin311ecdf2014-09-12 22:56:52194 return false;
195
binjin81d7c552014-10-02 11:47:12196 const URLPatternSet& url_patterns = global_settings_->install_sources;
binjin311ecdf2014-09-12 22:56:52197
198 if (!url_patterns.MatchesURL(url))
199 return false;
200
201 // The referrer URL must also be whitelisted, unless the URL has the file
202 // scheme (there's no referrer for those URLs).
203 return url.SchemeIsFile() || url_patterns.MatchesURL(referrer_url);
204}
205
binjin81d7c552014-10-02 11:47:12206bool ExtensionManagement::IsAllowedManifestType(
Owen Mina9a13e12018-11-01 20:43:52207 Manifest::Type manifest_type,
208 const std::string& extension_id) const {
209 if (extension_id == extension_misc::kCloudReportingExtensionId &&
210 IsCloudReportingPolicyEnabled()) {
211 return true;
212 }
213
binjin81d7c552014-10-02 11:47:12214 if (!global_settings_->has_restricted_allowed_types)
215 return true;
216 const std::vector<Manifest::Type>& allowed_types =
217 global_settings_->allowed_types;
Jan Wilken Dörrieade79222019-06-06 19:01:12218 return base::Contains(allowed_types, manifest_type);
binjin1569c9b2014-09-05 13:33:18219}
220
binjin685ade82014-11-06 09:53:56221APIPermissionSet ExtensionManagement::GetBlockedAPIPermissions(
222 const Extension* extension) const {
Owen Mina9a13e12018-11-01 20:43:52223 // The Chrome Reporting extension is sideloaded via the CloudReportingEnabled
224 // policy and is not subject to permission withholding.
225 if (extension->id() == extension_misc::kCloudReportingExtensionId &&
226 IsCloudReportingPolicyEnabled()) {
227 return APIPermissionSet();
228 }
229
binjin685ade82014-11-06 09:53:56230 // Fetch per-extension blocked permissions setting.
231 auto iter_id = settings_by_id_.find(extension->id());
232
233 // Fetch per-update-url blocked permissions setting.
234 std::string update_url;
235 auto iter_update_url = settings_by_update_url_.end();
236 if (extension->manifest()->GetString(manifest_keys::kUpdateURL,
237 &update_url)) {
238 iter_update_url = settings_by_update_url_.find(update_url);
239 }
240
241 if (iter_id != settings_by_id_.end() &&
242 iter_update_url != settings_by_update_url_.end()) {
243 // Blocked permissions setting are specified in both per-extension and
244 // per-update-url settings, try to merge them.
245 APIPermissionSet merged;
246 APIPermissionSet::Union(iter_id->second->blocked_permissions,
247 iter_update_url->second->blocked_permissions,
248 &merged);
249 return merged;
250 }
251 // Check whether if in one of them, setting is specified.
252 if (iter_id != settings_by_id_.end())
Devlin Cronin32708b02018-12-05 17:58:04253 return iter_id->second->blocked_permissions.Clone();
binjin685ade82014-11-06 09:53:56254 if (iter_update_url != settings_by_update_url_.end())
Devlin Cronin32708b02018-12-05 17:58:04255 return iter_update_url->second->blocked_permissions.Clone();
binjin685ade82014-11-06 09:53:56256 // Fall back to the default blocked permissions setting.
Devlin Cronin32708b02018-12-05 17:58:04257 return default_settings_->blocked_permissions.Clone();
binjine6b58b52014-10-31 01:55:57258}
259
Devlin Cronin7e0f41ff2018-05-16 17:19:36260const URLPatternSet& ExtensionManagement::GetDefaultPolicyBlockedHosts() const {
261 return default_settings_->policy_blocked_hosts;
nrpetere33d2a5b2017-04-25 00:12:31262}
263
Devlin Cronin7e0f41ff2018-05-16 17:19:36264const URLPatternSet& ExtensionManagement::GetDefaultPolicyAllowedHosts() const {
265 return default_settings_->policy_allowed_hosts;
nrpetere33d2a5b2017-04-25 00:12:31266}
267
Devlin Cronin7e0f41ff2018-05-16 17:19:36268const URLPatternSet& ExtensionManagement::GetPolicyBlockedHosts(
nrpeter40e16382017-04-13 17:34:58269 const Extension* extension) const {
270 auto iter_id = settings_by_id_.find(extension->id());
271 if (iter_id != settings_by_id_.end())
Devlin Cronin7e0f41ff2018-05-16 17:19:36272 return iter_id->second->policy_blocked_hosts;
273 return default_settings_->policy_blocked_hosts;
nrpeter40e16382017-04-13 17:34:58274}
275
Devlin Cronin7e0f41ff2018-05-16 17:19:36276const URLPatternSet& ExtensionManagement::GetPolicyAllowedHosts(
nrpeter40e16382017-04-13 17:34:58277 const Extension* extension) const {
278 auto iter_id = settings_by_id_.find(extension->id());
279 if (iter_id != settings_by_id_.end())
Devlin Cronin7e0f41ff2018-05-16 17:19:36280 return iter_id->second->policy_allowed_hosts;
281 return default_settings_->policy_allowed_hosts;
nrpeter40e16382017-04-13 17:34:58282}
283
Devlin Cronin7e0f41ff2018-05-16 17:19:36284bool ExtensionManagement::UsesDefaultPolicyHostRestrictions(
nrpetere33d2a5b2017-04-25 00:12:31285 const Extension* extension) const {
286 return settings_by_id_.find(extension->id()) == settings_by_id_.end();
287}
288
Devlin Cronin7e0f41ff2018-05-16 17:19:36289bool ExtensionManagement::IsPolicyBlockedHost(const Extension* extension,
290 const GURL& url) const {
nrpeter40e16382017-04-13 17:34:58291 auto iter_id = settings_by_id_.find(extension->id());
292 if (iter_id != settings_by_id_.end())
Devlin Cronin7e0f41ff2018-05-16 17:19:36293 return iter_id->second->policy_blocked_hosts.MatchesURL(url);
294 return default_settings_->policy_blocked_hosts.MatchesURL(url);
nrpeter40e16382017-04-13 17:34:58295}
296
dchengc963c7142016-04-08 03:55:22297std::unique_ptr<const PermissionSet> ExtensionManagement::GetBlockedPermissions(
binjin685ade82014-11-06 09:53:56298 const Extension* extension) const {
binjine6b58b52014-10-31 01:55:57299 // Only api permissions are supported currently.
dchengc963c7142016-04-08 03:55:22300 return std::unique_ptr<const PermissionSet>(new PermissionSet(
binjin685ade82014-11-06 09:53:56301 GetBlockedAPIPermissions(extension), ManifestPermissionSet(),
302 URLPatternSet(), URLPatternSet()));
binjine6b58b52014-10-31 01:55:57303}
304
305bool ExtensionManagement::IsPermissionSetAllowed(
binjin685ade82014-11-06 09:53:56306 const Extension* extension,
rdevlin.cronine2d0fd02015-09-24 22:35:49307 const PermissionSet& perms) const {
vmpstrae72b082016-07-25 21:55:47308 for (auto* blocked_api : GetBlockedAPIPermissions(extension)) {
rdevlin.cronine2d0fd02015-09-24 22:35:49309 if (perms.HasAPIPermission(blocked_api->id()))
binjine6b58b52014-10-31 01:55:57310 return false;
311 }
312 return true;
313}
314
nrpeter2362e7e2017-05-10 17:21:26315const std::string ExtensionManagement::BlockedInstallMessage(
316 const ExtensionId& id) const {
317 auto iter_id = settings_by_id_.find(id);
318 if (iter_id != settings_by_id_.end())
319 return iter_id->second->blocked_install_message;
320 return default_settings_->blocked_install_message;
321}
322
binjin8e3d0182014-12-04 16:44:28323bool ExtensionManagement::CheckMinimumVersion(
324 const Extension* extension,
325 std::string* required_version) const {
326 auto iter = settings_by_id_.find(extension->id());
327 // If there are no minimum version required for |extension|, return true.
328 if (iter == settings_by_id_.end() || !iter->second->minimum_version_required)
329 return true;
Devlin Cronin03bf2d22017-12-20 08:21:05330 bool meets_requirement = extension->version().CompareTo(
331 *iter->second->minimum_version_required) >= 0;
binjin8e3d0182014-12-04 16:44:28332 // Output a human readable version string for prompting if necessary.
333 if (!meets_requirement && required_version)
334 *required_version = iter->second->minimum_version_required->GetString();
335 return meets_requirement;
336}
337
binjin5f405ef2014-09-03 21:23:16338void ExtensionManagement::Refresh() {
rkaplowdd66a1342015-03-05 00:31:49339 TRACE_EVENT0("browser,startup", "ExtensionManagement::Refresh");
binjin5f405ef2014-09-03 21:23:16340 // Load all extension management settings preferences.
341 const base::ListValue* allowed_list_pref =
342 static_cast<const base::ListValue*>(LoadPreference(
jdoerriedc72ee942016-12-07 15:43:28343 pref_names::kInstallAllowList, true, base::Value::Type::LIST));
binjin5f405ef2014-09-03 21:23:16344 // Allow user to use preference to block certain extensions. Note that policy
345 // managed forcelist or whitelist will always override this.
346 const base::ListValue* denied_list_pref =
347 static_cast<const base::ListValue*>(LoadPreference(
jdoerriedc72ee942016-12-07 15:43:28348 pref_names::kInstallDenyList, false, base::Value::Type::LIST));
binjin5f405ef2014-09-03 21:23:16349 const base::DictionaryValue* forced_list_pref =
350 static_cast<const base::DictionaryValue*>(LoadPreference(
jdoerriedc72ee942016-12-07 15:43:28351 pref_names::kInstallForceList, true, base::Value::Type::DICTIONARY));
Alexander Hendrichb07fd55b2019-04-01 09:24:37352 const base::DictionaryValue* login_screen_extensions_pref = nullptr;
achuith4607f072017-03-08 11:49:13353 if (is_signin_profile_) {
Alexander Hendrichb07fd55b2019-04-01 09:24:37354 login_screen_extensions_pref = static_cast<const base::DictionaryValue*>(
355 LoadPreference(pref_names::kLoginScreenExtensions, true,
achuith4607f072017-03-08 11:49:13356 base::Value::Type::DICTIONARY));
357 }
binjin5f405ef2014-09-03 21:23:16358 const base::ListValue* install_sources_pref =
359 static_cast<const base::ListValue*>(LoadPreference(
jdoerriedc72ee942016-12-07 15:43:28360 pref_names::kAllowedInstallSites, true, base::Value::Type::LIST));
binjin5f405ef2014-09-03 21:23:16361 const base::ListValue* allowed_types_pref =
362 static_cast<const base::ListValue*>(LoadPreference(
jdoerriedc72ee942016-12-07 15:43:28363 pref_names::kAllowedTypes, true, base::Value::Type::LIST));
binjinb2454382014-09-22 15:17:43364 const base::DictionaryValue* dict_pref =
365 static_cast<const base::DictionaryValue*>(
366 LoadPreference(pref_names::kExtensionManagement,
367 true,
jdoerriedc72ee942016-12-07 15:43:28368 base::Value::Type::DICTIONARY));
Owen Min4792c392019-10-24 22:49:14369 const base::Value* extension_request_pref = LoadPreference(
370 prefs::kCloudExtensionRequestEnabled, false, base::Value::Type::BOOLEAN);
binjin5f405ef2014-09-03 21:23:16371
372 // Reset all settings.
binjin81d7c552014-10-02 11:47:12373 global_settings_.reset(new internal::GlobalSettings());
binjin5f405ef2014-09-03 21:23:16374 settings_by_id_.clear();
binjin81d7c552014-10-02 11:47:12375 default_settings_.reset(new internal::IndividualSettings());
binjin5f405ef2014-09-03 21:23:16376
377 // Parse default settings.
jdoerrie122c4da2017-03-06 11:12:04378 const base::Value wildcard("*");
Owen Min4792c392019-10-24 22:49:14379 if ((denied_list_pref &&
380 denied_list_pref->Find(wildcard) != denied_list_pref->end()) ||
381 (extension_request_pref && extension_request_pref->GetBool())) {
binjin81d7c552014-10-02 11:47:12382 default_settings_->installation_mode = INSTALLATION_BLOCKED;
binjin5f405ef2014-09-03 21:23:16383 }
384
binjinb2454382014-09-22 15:17:43385 const base::DictionaryValue* subdict = NULL;
386 if (dict_pref &&
387 dict_pref->GetDictionary(schema_constants::kWildcard, &subdict)) {
binjin81d7c552014-10-02 11:47:12388 if (!default_settings_->Parse(
389 subdict, internal::IndividualSettings::SCOPE_DEFAULT)) {
binjinb2454382014-09-22 15:17:43390 LOG(WARNING) << "Default extension management settings parsing error.";
binjin81d7c552014-10-02 11:47:12391 default_settings_->Reset();
binjinb2454382014-09-22 15:17:43392 }
393
394 // Settings from new preference have higher priority over legacy ones.
395 const base::ListValue* list_value = NULL;
396 if (subdict->GetList(schema_constants::kInstallSources, &list_value))
397 install_sources_pref = list_value;
398 if (subdict->GetList(schema_constants::kAllowedTypes, &list_value))
399 allowed_types_pref = list_value;
400 }
401
binjin5f405ef2014-09-03 21:23:16402 // Parse legacy preferences.
403 ExtensionId id;
404
405 if (allowed_list_pref) {
jdoerrie13cd648c82018-10-02 21:21:02406 for (auto it = allowed_list_pref->begin(); it != allowed_list_pref->end();
407 ++it) {
jdoerriea5676c62017-04-11 18:09:14408 if (it->GetAsString(&id) && crx_file::id_util::IdIsValid(id))
binjin5f405ef2014-09-03 21:23:16409 AccessById(id)->installation_mode = INSTALLATION_ALLOWED;
410 }
411 }
412
413 if (denied_list_pref) {
jdoerrie13cd648c82018-10-02 21:21:02414 for (auto it = denied_list_pref->begin(); it != denied_list_pref->end();
415 ++it) {
jdoerriea5676c62017-04-11 18:09:14416 if (it->GetAsString(&id) && crx_file::id_util::IdIsValid(id))
binjin5f405ef2014-09-03 21:23:16417 AccessById(id)->installation_mode = INSTALLATION_BLOCKED;
418 }
419 }
420
achuith4607f072017-03-08 11:49:13421 UpdateForcedExtensions(forced_list_pref);
Alexander Hendrichb07fd55b2019-04-01 09:24:37422 UpdateForcedExtensions(login_screen_extensions_pref);
binjin5f405ef2014-09-03 21:23:16423
424 if (install_sources_pref) {
binjin81d7c552014-10-02 11:47:12425 global_settings_->has_restricted_install_sources = true;
jdoerrie13cd648c82018-10-02 21:21:02426 for (auto it = install_sources_pref->begin();
binjin5f405ef2014-09-03 21:23:16427 it != install_sources_pref->end(); ++it) {
binjinb2454382014-09-22 15:17:43428 std::string url_pattern;
jdoerriea5676c62017-04-11 18:09:14429 if (it->GetAsString(&url_pattern)) {
binjinb2454382014-09-22 15:17:43430 URLPattern entry(URLPattern::SCHEME_ALL);
Devlin Croninbd7f2b5fa2018-09-05 17:41:18431 if (entry.Parse(url_pattern) == URLPattern::ParseResult::kSuccess) {
binjin81d7c552014-10-02 11:47:12432 global_settings_->install_sources.AddPattern(entry);
binjin5f405ef2014-09-03 21:23:16433 } else {
434 LOG(WARNING) << "Invalid URL pattern in for preference "
435 << pref_names::kAllowedInstallSites << ": "
436 << url_pattern << ".";
437 }
438 }
439 }
440 }
441
442 if (allowed_types_pref) {
binjin81d7c552014-10-02 11:47:12443 global_settings_->has_restricted_allowed_types = true;
jdoerrie13cd648c82018-10-02 21:21:02444 for (auto it = allowed_types_pref->begin(); it != allowed_types_pref->end();
445 ++it) {
binjin5f405ef2014-09-03 21:23:16446 int int_value;
binjinb2454382014-09-22 15:17:43447 std::string string_value;
jdoerriea5676c62017-04-11 18:09:14448 if (it->GetAsInteger(&int_value) && int_value >= 0 &&
binjin5f405ef2014-09-03 21:23:16449 int_value < Manifest::Type::NUM_LOAD_TYPES) {
binjin81d7c552014-10-02 11:47:12450 global_settings_->allowed_types.push_back(
binjin5f405ef2014-09-03 21:23:16451 static_cast<Manifest::Type>(int_value));
jdoerriea5676c62017-04-11 18:09:14452 } else if (it->GetAsString(&string_value)) {
binjinb2454382014-09-22 15:17:43453 Manifest::Type manifest_type =
454 schema_constants::GetManifestType(string_value);
455 if (manifest_type != Manifest::TYPE_UNKNOWN)
binjin81d7c552014-10-02 11:47:12456 global_settings_->allowed_types.push_back(manifest_type);
binjin5f405ef2014-09-03 21:23:16457 }
458 }
459 }
460
binjinb2454382014-09-22 15:17:43461 if (dict_pref) {
462 // Parse new extension management preference.
463 for (base::DictionaryValue::Iterator iter(*dict_pref); !iter.IsAtEnd();
464 iter.Advance()) {
465 if (iter.key() == schema_constants::kWildcard)
466 continue;
binjin81d7c552014-10-02 11:47:12467 if (!iter.value().GetAsDictionary(&subdict))
binjinb2454382014-09-22 15:17:43468 continue;
brettw66d1b81b2015-07-06 19:29:40469 if (base::StartsWith(iter.key(), schema_constants::kUpdateUrlPrefix,
470 base::CompareCase::SENSITIVE)) {
binjin685ade82014-11-06 09:53:56471 const std::string& update_url =
472 iter.key().substr(strlen(schema_constants::kUpdateUrlPrefix));
473 if (!GURL(update_url).is_valid()) {
474 LOG(WARNING) << "Invalid update URL: " << update_url << ".";
475 continue;
476 }
477 internal::IndividualSettings* by_update_url =
478 AccessByUpdateUrl(update_url);
479 if (!by_update_url->Parse(
480 subdict, internal::IndividualSettings::SCOPE_UPDATE_URL)) {
481 settings_by_update_url_.erase(update_url);
482 LOG(WARNING) << "Malformed Extension Management settings for "
483 "extensions with update url: " << update_url << ".";
484 }
485 } else {
Kyle Spiers3bc96a192019-04-11 23:43:21486 std::vector<std::string> extension_ids = base::SplitString(
487 iter.key(), ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
Oleg Davydov66246bd92019-09-26 16:31:51488 InstallationReporter* installation_reporter =
489 InstallationReporter::Get(profile_);
Kyle Spiers3bc96a192019-04-11 23:43:21490 for (const auto& extension_id : extension_ids) {
491 if (!crx_file::id_util::IdIsValid(extension_id)) {
492 SYSLOG(WARNING) << "Invalid extension ID : " << extension_id << ".";
493 continue;
494 }
495 internal::IndividualSettings* by_id = AccessById(extension_id);
496 if (!by_id->Parse(subdict,
497 internal::IndividualSettings::SCOPE_INDIVIDUAL)) {
498 settings_by_id_.erase(extension_id);
Oleg Davydov66246bd92019-09-26 16:31:51499 installation_reporter->ReportFailure(
500 extension_id, InstallationReporter::FailureReason::
501 MALFORMED_EXTENSION_SETTINGS);
Kyle Spiers3bc96a192019-04-11 23:43:21502 SYSLOG(WARNING) << "Malformed Extension Management settings for "
503 << extension_id << ".";
504 }
binjin685ade82014-11-06 09:53:56505 }
binjinb2454382014-09-22 15:17:43506 }
507 }
508 }
Owen Mina9a13e12018-11-01 20:43:52509
510 UpdateForcedCloudReportingExtension();
binjin5f405ef2014-09-03 21:23:16511}
512
binjin5f405ef2014-09-03 21:23:16513const base::Value* ExtensionManagement::LoadPreference(
514 const char* pref_name,
515 bool force_managed,
Owen Mina9a13e12018-11-01 20:43:52516 base::Value::Type expected_type) const {
binjine6b58b52014-10-31 01:55:57517 if (!pref_service_)
518 return nullptr;
binjin5f405ef2014-09-03 21:23:16519 const PrefService::Preference* pref =
520 pref_service_->FindPreference(pref_name);
521 if (pref && !pref->IsDefaultValue() &&
522 (!force_managed || pref->IsManaged())) {
523 const base::Value* value = pref->GetValue();
jdoerrie1f536b22017-10-23 17:15:11524 if (value && value->type() == expected_type)
binjin5f405ef2014-09-03 21:23:16525 return value;
526 }
binjine6b58b52014-10-31 01:55:57527 return nullptr;
binjin5f405ef2014-09-03 21:23:16528}
529
binjin1569c9b2014-09-05 13:33:18530void ExtensionManagement::OnExtensionPrefChanged() {
531 Refresh();
532 NotifyExtensionManagementPrefChanged();
533}
534
535void ExtensionManagement::NotifyExtensionManagementPrefChanged() {
Oleg Davydov66246bd92019-09-26 16:31:51536 InstallationReporter* installation_reporter =
537 InstallationReporter::Get(profile_);
Oleg Davydov52db8742019-08-20 09:14:40538 for (const auto& entry : settings_by_id_) {
539 if (entry.second->installation_mode == INSTALLATION_FORCED) {
Oleg Davydov66246bd92019-09-26 16:31:51540 installation_reporter->ReportInstallationStage(
541 entry.first, InstallationReporter::Stage::NOTIFIED_FROM_MANAGEMENT);
Oleg Davydov52db8742019-08-20 09:14:40542 } else {
Oleg Davydov66246bd92019-09-26 16:31:51543 installation_reporter->ReportInstallationStage(
544 entry.first,
Oleg Davydov52db8742019-08-20 09:14:40545 InstallationReporter::Stage::NOTIFIED_FROM_MANAGEMENT_NOT_FORCED);
546 }
547 }
ericwilligersb5f79de2016-10-19 04:15:10548 for (auto& observer : observer_list_)
549 observer.OnExtensionManagementSettingsChanged();
binjin1569c9b2014-09-05 13:33:18550}
551
achuith4607f072017-03-08 11:49:13552std::unique_ptr<base::DictionaryValue>
553ExtensionManagement::GetInstallListByMode(
554 InstallationMode installation_mode) const {
Jinho Bangb5216cec2018-01-17 19:43:11555 auto extension_dict = std::make_unique<base::DictionaryValue>();
achuith4607f072017-03-08 11:49:13556 for (const auto& entry : settings_by_id_) {
557 if (entry.second->installation_mode == installation_mode) {
558 ExternalPolicyLoader::AddExtension(extension_dict.get(), entry.first,
559 entry.second->update_url);
560 }
561 }
562 return extension_dict;
563}
564
565void ExtensionManagement::UpdateForcedExtensions(
566 const base::DictionaryValue* extension_dict) {
567 if (!extension_dict)
568 return;
569
570 std::string update_url;
Oleg Davydov66246bd92019-09-26 16:31:51571 InstallationReporter* installation_reporter =
572 InstallationReporter::Get(profile_);
achuith4607f072017-03-08 11:49:13573 for (base::DictionaryValue::Iterator it(*extension_dict); !it.IsAtEnd();
574 it.Advance()) {
Sergey Poromov296de0a2018-10-11 21:44:08575 if (!crx_file::id_util::IdIsValid(it.key())) {
Oleg Davydov66246bd92019-09-26 16:31:51576 installation_reporter->ReportFailure(
577 it.key(), InstallationReporter::FailureReason::INVALID_ID);
achuith4607f072017-03-08 11:49:13578 continue;
Sergey Poromov296de0a2018-10-11 21:44:08579 }
achuith4607f072017-03-08 11:49:13580 const base::DictionaryValue* dict_value = nullptr;
581 if (it.value().GetAsDictionary(&dict_value) &&
582 dict_value->GetStringWithoutPathExpansion(
583 ExternalProviderImpl::kExternalUpdateUrl, &update_url)) {
584 internal::IndividualSettings* by_id = AccessById(it.key());
585 by_id->installation_mode = INSTALLATION_FORCED;
586 by_id->update_url = update_url;
Oleg Davydov66246bd92019-09-26 16:31:51587 installation_reporter->ReportInstallationStage(
588 it.key(), InstallationReporter::Stage::CREATED);
Oleg Davydovc00866812019-04-04 10:47:27589 } else {
Oleg Davydov66246bd92019-09-26 16:31:51590 installation_reporter->ReportFailure(
591 it.key(), InstallationReporter::FailureReason::NO_UPDATE_URL);
achuith4607f072017-03-08 11:49:13592 }
593 }
594}
595
Owen Mina9a13e12018-11-01 20:43:52596void ExtensionManagement::UpdateForcedCloudReportingExtension() {
597 if (!IsCloudReportingPolicyEnabled())
598 return;
599
600 // Adds the Chrome Reporting extension to the force install list if
601 // CloudReportingEnabled policy is set to True. Overrides any existing setting
602 // for that extension from other policies.
603 internal::IndividualSettings* settings =
604 AccessById(extension_misc::kCloudReportingExtensionId);
605 settings->Reset();
606 settings->minimum_version_required.reset();
607 settings->installation_mode = INSTALLATION_FORCED;
608 settings->update_url = extension_urls::kChromeWebstoreUpdateURL;
609}
610
611bool ExtensionManagement::IsCloudReportingPolicyEnabled() const {
612#if !defined(OS_CHROMEOS)
Owen Min94627f82019-09-09 21:44:51613 if (base::FeatureList::IsEnabled(features::kEnterpriseReportingInBrowser))
614 return false;
Owen Mina9a13e12018-11-01 20:43:52615 const base::Value* policy_value =
616 LoadPreference(prefs::kCloudReportingEnabled,
617 /* force_managed = */ true, base::Value::Type::BOOLEAN);
618 return policy_value && policy_value->GetBool();
619#else
620 return false;
621#endif
622}
623
binjin81d7c552014-10-02 11:47:12624internal::IndividualSettings* ExtensionManagement::AccessById(
binjin5f405ef2014-09-03 21:23:16625 const ExtensionId& id) {
626 DCHECK(crx_file::id_util::IdIsValid(id)) << "Invalid ID: " << id;
avi3ec9c0d2016-12-27 22:38:06627 std::unique_ptr<internal::IndividualSettings>& settings = settings_by_id_[id];
628 if (!settings) {
629 settings =
Jinho Bangb5216cec2018-01-17 19:43:11630 std::make_unique<internal::IndividualSettings>(default_settings_.get());
binjin81d7c552014-10-02 11:47:12631 }
avi3ec9c0d2016-12-27 22:38:06632 return settings.get();
binjin5f405ef2014-09-03 21:23:16633}
634
binjin685ade82014-11-06 09:53:56635internal::IndividualSettings* ExtensionManagement::AccessByUpdateUrl(
636 const std::string& update_url) {
637 DCHECK(GURL(update_url).is_valid()) << "Invalid update URL: " << update_url;
avi3ec9c0d2016-12-27 22:38:06638 std::unique_ptr<internal::IndividualSettings>& settings =
639 settings_by_update_url_[update_url];
640 if (!settings) {
641 settings =
Jinho Bangb5216cec2018-01-17 19:43:11642 std::make_unique<internal::IndividualSettings>(default_settings_.get());
binjin685ade82014-11-06 09:53:56643 }
avi3ec9c0d2016-12-27 22:38:06644 return settings.get();
binjin685ade82014-11-06 09:53:56645}
646
Oleg Davydovbf103f92019-07-30 15:55:08647// static
binjin1569c9b2014-09-05 13:33:18648ExtensionManagement* ExtensionManagementFactory::GetForBrowserContext(
649 content::BrowserContext* context) {
650 return static_cast<ExtensionManagement*>(
651 GetInstance()->GetServiceForBrowserContext(context, true));
652}
653
Oleg Davydovbf103f92019-07-30 15:55:08654// static
binjin1569c9b2014-09-05 13:33:18655ExtensionManagementFactory* ExtensionManagementFactory::GetInstance() {
olli.raula36aa8be2015-09-10 11:14:22656 return base::Singleton<ExtensionManagementFactory>::get();
binjin1569c9b2014-09-05 13:33:18657}
658
659ExtensionManagementFactory::ExtensionManagementFactory()
660 : BrowserContextKeyedServiceFactory(
661 "ExtensionManagement",
662 BrowserContextDependencyManager::GetInstance()) {
Oleg Davydov66246bd92019-09-26 16:31:51663 DependsOn(InstallationReporterFactory::GetInstance());
binjin1569c9b2014-09-05 13:33:18664}
665
666ExtensionManagementFactory::~ExtensionManagementFactory() {
667}
668
669KeyedService* ExtensionManagementFactory::BuildServiceInstanceFor(
670 content::BrowserContext* context) const {
rkaplowdd66a1342015-03-05 00:31:49671 TRACE_EVENT0("browser,startup",
672 "ExtensionManagementFactory::BuildServiceInstanceFor");
Sergey Poromov741e70702018-10-11 20:11:54673 return new ExtensionManagement(Profile::FromBrowserContext(context));
binjin1569c9b2014-09-05 13:33:18674}
675
binjin9733df12014-09-08 15:21:21676content::BrowserContext* ExtensionManagementFactory::GetBrowserContextToUse(
677 content::BrowserContext* context) const {
678 return chrome::GetBrowserContextRedirectedInIncognito(context);
679}
680
binjinb2454382014-09-22 15:17:43681void ExtensionManagementFactory::RegisterProfilePrefs(
682 user_prefs::PrefRegistrySyncable* user_prefs) {
raymesaa608722015-04-27 03:00:25683 user_prefs->RegisterDictionaryPref(pref_names::kExtensionManagement);
binjinb2454382014-09-22 15:17:43684}
685
binjin5f405ef2014-09-03 21:23:16686} // namespace extensions