#include "chrome/browser/extensions/extension_management.h"
+#include <algorithm>
+
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/logging.h"
#include "base/prefs/pref_service.h"
+#include "base/strings/string16.h"
#include "base/strings/string_util.h"
#include "chrome/browser/extensions/extension_management_constants.h"
+#include "chrome/browser/extensions/extension_management_internal.h"
#include "chrome/browser/extensions/external_policy_loader.h"
#include "chrome/browser/extensions/external_provider_impl.h"
+#include "chrome/browser/extensions/permissions_based_management_policy_provider.h"
#include "chrome/browser/extensions/standard_management_policy_provider.h"
#include "chrome/browser/profiles/incognito_helpers.h"
#include "chrome/browser/profiles/profile.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "extensions/browser/pref_names.h"
+#include "extensions/common/manifest_constants.h"
+#include "extensions/common/permissions/api_permission_set.h"
+#include "extensions/common/permissions/permission_set.h"
#include "extensions/common/url_pattern.h"
#include "url/gurl.h"
namespace extensions {
-namespace {
-
-const char kMalformedPreferenceWarning[] =
- "Malformed extension management preference.";
-
-enum Scope {
- // Parses the default settings.
- SCOPE_DEFAULT = 0,
- // Parses the settings for an extension with specified extension ID.
- SCOPE_INDIVIDUAL,
-};
-
-// Parse the individual settings for |settings|. |dict| is the a
-// sub-dictionary in extension management preference and |scope| represents
-// the applicable range of the settings, a single extension, a group of
-// extensions or default settings.
-// Note that in case of parsing errors, |settings| will NOT be left untouched.
-bool ParseIndividualSettings(
- const base::DictionaryValue* dict,
- Scope scope,
- ExtensionManagement::IndividualSettings* settings) {
- std::string installation_mode;
- if (dict->GetStringWithoutPathExpansion(schema_constants::kInstallationMode,
- &installation_mode)) {
- if (installation_mode == schema_constants::kAllowed) {
- settings->installation_mode = ExtensionManagement::INSTALLATION_ALLOWED;
- } else if (installation_mode == schema_constants::kBlocked) {
- settings->installation_mode = ExtensionManagement::INSTALLATION_BLOCKED;
- } else if (installation_mode == schema_constants::kForceInstalled) {
- settings->installation_mode = ExtensionManagement::INSTALLATION_FORCED;
- } else if (installation_mode == schema_constants::kNormalInstalled) {
- settings->installation_mode =
- ExtensionManagement::INSTALLATION_RECOMMENDED;
- } else {
- // Invalid value for 'installation_mode'.
- LOG(WARNING) << kMalformedPreferenceWarning;
- return false;
- }
- }
-
- if (settings->installation_mode == ExtensionManagement::INSTALLATION_FORCED ||
- settings->installation_mode ==
- ExtensionManagement::INSTALLATION_RECOMMENDED) {
- if (scope != SCOPE_INDIVIDUAL) {
- // Only individual extensions are allowed to be automatically installed.
- LOG(WARNING) << kMalformedPreferenceWarning;
- return false;
- }
- std::string update_url;
- if (dict->GetStringWithoutPathExpansion(schema_constants::kUpdateUrl,
- &update_url) &&
- GURL(update_url).is_valid()) {
- settings->update_url = update_url;
- } else {
- // No valid update URL for extension.
- LOG(WARNING) << kMalformedPreferenceWarning;
- return false;
- }
- }
-
- return true;
-}
-
-} // namespace
-
-ExtensionManagement::IndividualSettings::IndividualSettings() {
- Reset();
-}
-
-ExtensionManagement::IndividualSettings::~IndividualSettings() {
-}
-
-void ExtensionManagement::IndividualSettings::Reset() {
- installation_mode = ExtensionManagement::INSTALLATION_ALLOWED;
- update_url.clear();
-}
-
-ExtensionManagement::GlobalSettings::GlobalSettings() {
- Reset();
-}
-
-ExtensionManagement::GlobalSettings::~GlobalSettings() {
-}
-
-void ExtensionManagement::GlobalSettings::Reset() {
- has_restricted_install_sources = false;
- install_sources.ClearPatterns();
- has_restricted_allowed_types = false;
- allowed_types.clear();
-}
-
ExtensionManagement::ExtensionManagement(PrefService* pref_service)
: pref_service_(pref_service) {
pref_change_registrar_.Init(pref_service_);
pref_change_registrar_.Add(pref_names::kAllowedTypes, pref_change_callback);
pref_change_registrar_.Add(pref_names::kExtensionManagement,
pref_change_callback);
+ // Note that both |global_settings_| and |default_settings_| will be null
+ // before first call to Refresh(), so in order to resolve this, Refresh() must
+ // be called in the initialization of ExtensionManagement.
Refresh();
- provider_.reset(new StandardManagementPolicyProvider(this));
+ providers_.push_back(new StandardManagementPolicyProvider(this));
+ providers_.push_back(new PermissionsBasedManagementPolicyProvider(this));
}
ExtensionManagement::~ExtensionManagement() {
}
+void ExtensionManagement::Shutdown() {
+ pref_change_registrar_.RemoveAll();
+ pref_service_ = nullptr;
+}
+
void ExtensionManagement::AddObserver(Observer* observer) {
observer_list_.AddObserver(observer);
}
observer_list_.RemoveObserver(observer);
}
-ManagementPolicy::Provider* ExtensionManagement::GetProvider() {
- return provider_.get();
+std::vector<ManagementPolicy::Provider*> ExtensionManagement::GetProviders()
+ const {
+ return providers_.get();
+}
+
+bool ExtensionManagement::BlacklistedByDefault() const {
+ return default_settings_->installation_mode == INSTALLATION_BLOCKED;
}
-bool ExtensionManagement::BlacklistedByDefault() {
- return default_settings_.installation_mode == INSTALLATION_BLOCKED;
+ExtensionManagement::InstallationMode ExtensionManagement::GetInstallationMode(
+ const Extension* extension) const {
+ // Check per-extension installation mode setting first.
+ auto iter_id = settings_by_id_.find(extension->id());
+ if (iter_id != settings_by_id_.end())
+ return iter_id->second->installation_mode;
+ std::string update_url;
+ // Check per-update-url installation mode setting.
+ if (extension->manifest()->GetString(manifest_keys::kUpdateURL,
+ &update_url)) {
+ auto iter_update_url = settings_by_update_url_.find(update_url);
+ if (iter_update_url != settings_by_update_url_.end())
+ return iter_update_url->second->installation_mode;
+ }
+ // Fall back to default installation mode setting.
+ return default_settings_->installation_mode;
}
scoped_ptr<base::DictionaryValue> ExtensionManagement::GetForceInstallList()
const {
- scoped_ptr<base::DictionaryValue> forcelist(new base::DictionaryValue());
+ scoped_ptr<base::DictionaryValue> install_list(new base::DictionaryValue());
for (SettingsIdMap::const_iterator it = settings_by_id_.begin();
it != settings_by_id_.end();
++it) {
- if (it->second.installation_mode == INSTALLATION_FORCED) {
+ if (it->second->installation_mode == INSTALLATION_FORCED) {
ExternalPolicyLoader::AddExtension(
- forcelist.get(), it->first, it->second.update_url);
+ install_list.get(), it->first, it->second->update_url);
}
}
- return forcelist.Pass();
+ return install_list.Pass();
+}
+
+scoped_ptr<base::DictionaryValue>
+ExtensionManagement::GetRecommendedInstallList() const {
+ scoped_ptr<base::DictionaryValue> install_list(new base::DictionaryValue());
+ for (SettingsIdMap::const_iterator it = settings_by_id_.begin();
+ it != settings_by_id_.end();
+ ++it) {
+ if (it->second->installation_mode == INSTALLATION_RECOMMENDED) {
+ ExternalPolicyLoader::AddExtension(
+ install_list.get(), it->first, it->second->update_url);
+ }
+ }
+ return install_list.Pass();
}
bool ExtensionManagement::IsInstallationExplicitlyAllowed(
return false;
// Checks if the extension is on the automatically installed list or
// install white-list.
- InstallationMode mode = it->second.installation_mode;
+ InstallationMode mode = it->second->installation_mode;
return mode == INSTALLATION_FORCED || mode == INSTALLATION_RECOMMENDED ||
mode == INSTALLATION_ALLOWED;
}
-bool ExtensionManagement::IsOffstoreInstallAllowed(const GURL& url,
- const GURL& referrer_url) {
+bool ExtensionManagement::IsOffstoreInstallAllowed(
+ const GURL& url,
+ const GURL& referrer_url) const {
// No allowed install sites specified, disallow by default.
- if (!global_settings_.has_restricted_install_sources)
+ if (!global_settings_->has_restricted_install_sources)
return false;
- const extensions::URLPatternSet& url_patterns =
- global_settings_.install_sources;
+ const URLPatternSet& url_patterns = global_settings_->install_sources;
if (!url_patterns.MatchesURL(url))
return false;
return url.SchemeIsFile() || url_patterns.MatchesURL(referrer_url);
}
-const ExtensionManagement::IndividualSettings& ExtensionManagement::ReadById(
- const ExtensionId& id) const {
- DCHECK(crx_file::id_util::IdIsValid(id)) << "Invalid ID: " << id;
- SettingsIdMap::const_iterator it = settings_by_id_.find(id);
- if (it != settings_by_id_.end())
- return it->second;
- return default_settings_;
+bool ExtensionManagement::IsAllowedManifestType(
+ Manifest::Type manifest_type) const {
+ if (!global_settings_->has_restricted_allowed_types)
+ return true;
+ const std::vector<Manifest::Type>& allowed_types =
+ global_settings_->allowed_types;
+ return std::find(allowed_types.begin(), allowed_types.end(), manifest_type) !=
+ allowed_types.end();
+}
+
+APIPermissionSet ExtensionManagement::GetBlockedAPIPermissions(
+ const Extension* extension) const {
+ // Fetch per-extension blocked permissions setting.
+ auto iter_id = settings_by_id_.find(extension->id());
+
+ // Fetch per-update-url blocked permissions setting.
+ std::string update_url;
+ auto iter_update_url = settings_by_update_url_.end();
+ if (extension->manifest()->GetString(manifest_keys::kUpdateURL,
+ &update_url)) {
+ iter_update_url = settings_by_update_url_.find(update_url);
+ }
+
+ if (iter_id != settings_by_id_.end() &&
+ iter_update_url != settings_by_update_url_.end()) {
+ // Blocked permissions setting are specified in both per-extension and
+ // per-update-url settings, try to merge them.
+ APIPermissionSet merged;
+ APIPermissionSet::Union(iter_id->second->blocked_permissions,
+ iter_update_url->second->blocked_permissions,
+ &merged);
+ return merged;
+ }
+ // Check whether if in one of them, setting is specified.
+ if (iter_id != settings_by_id_.end())
+ return iter_id->second->blocked_permissions;
+ if (iter_update_url != settings_by_update_url_.end())
+ return iter_update_url->second->blocked_permissions;
+ // Fall back to the default blocked permissions setting.
+ return default_settings_->blocked_permissions;
+}
+
+scoped_refptr<const PermissionSet> ExtensionManagement::GetBlockedPermissions(
+ const Extension* extension) const {
+ // Only api permissions are supported currently.
+ return scoped_refptr<const PermissionSet>(new PermissionSet(
+ GetBlockedAPIPermissions(extension), ManifestPermissionSet(),
+ URLPatternSet(), URLPatternSet()));
}
-const ExtensionManagement::GlobalSettings&
-ExtensionManagement::ReadGlobalSettings() const {
- return global_settings_;
+bool ExtensionManagement::IsPermissionSetAllowed(
+ const Extension* extension,
+ scoped_refptr<const PermissionSet> perms) const {
+ for (const auto& blocked_api : GetBlockedAPIPermissions(extension)) {
+ if (perms->HasAPIPermission(blocked_api->id()))
+ return false;
+ }
+ return true;
}
void ExtensionManagement::Refresh() {
base::Value::TYPE_DICTIONARY));
// Reset all settings.
- global_settings_.Reset();
+ global_settings_.reset(new internal::GlobalSettings());
settings_by_id_.clear();
- default_settings_.Reset();
+ default_settings_.reset(new internal::IndividualSettings());
// Parse default settings.
const base::StringValue wildcard("*");
if (denied_list_pref &&
denied_list_pref->Find(wildcard) != denied_list_pref->end()) {
- default_settings_.installation_mode = INSTALLATION_BLOCKED;
+ default_settings_->installation_mode = INSTALLATION_BLOCKED;
}
const base::DictionaryValue* subdict = NULL;
if (dict_pref &&
dict_pref->GetDictionary(schema_constants::kWildcard, &subdict)) {
- if (!ParseIndividualSettings(subdict, SCOPE_DEFAULT, &default_settings_)) {
+ if (!default_settings_->Parse(
+ subdict, internal::IndividualSettings::SCOPE_DEFAULT)) {
LOG(WARNING) << "Default extension management settings parsing error.";
- default_settings_.Reset();
+ default_settings_->Reset();
}
// Settings from new preference have higher priority over legacy ones.
if (it.value().GetAsDictionary(&dict_value) &&
dict_value->GetStringWithoutPathExpansion(
ExternalProviderImpl::kExternalUpdateUrl, &update_url)) {
- IndividualSettings* by_id = AccessById(it.key());
+ internal::IndividualSettings* by_id = AccessById(it.key());
by_id->installation_mode = INSTALLATION_FORCED;
by_id->update_url = update_url;
}
}
if (install_sources_pref) {
- global_settings_.has_restricted_install_sources = true;
+ global_settings_->has_restricted_install_sources = true;
for (base::ListValue::const_iterator it = install_sources_pref->begin();
it != install_sources_pref->end(); ++it) {
std::string url_pattern;
if ((*it)->GetAsString(&url_pattern)) {
URLPattern entry(URLPattern::SCHEME_ALL);
if (entry.Parse(url_pattern) == URLPattern::PARSE_SUCCESS) {
- global_settings_.install_sources.AddPattern(entry);
+ global_settings_->install_sources.AddPattern(entry);
} else {
LOG(WARNING) << "Invalid URL pattern in for preference "
<< pref_names::kAllowedInstallSites << ": "
}
if (allowed_types_pref) {
- global_settings_.has_restricted_allowed_types = true;
+ global_settings_->has_restricted_allowed_types = true;
for (base::ListValue::const_iterator it = allowed_types_pref->begin();
it != allowed_types_pref->end(); ++it) {
int int_value;
std::string string_value;
if ((*it)->GetAsInteger(&int_value) && int_value >= 0 &&
int_value < Manifest::Type::NUM_LOAD_TYPES) {
- global_settings_.allowed_types.push_back(
+ global_settings_->allowed_types.push_back(
static_cast<Manifest::Type>(int_value));
} else if ((*it)->GetAsString(&string_value)) {
Manifest::Type manifest_type =
schema_constants::GetManifestType(string_value);
if (manifest_type != Manifest::TYPE_UNKNOWN)
- global_settings_.allowed_types.push_back(manifest_type);
+ global_settings_->allowed_types.push_back(manifest_type);
}
}
}
iter.Advance()) {
if (iter.key() == schema_constants::kWildcard)
continue;
- if (!iter.value().GetAsDictionary(&subdict)) {
- LOG(WARNING) << kMalformedPreferenceWarning;
+ if (!iter.value().GetAsDictionary(&subdict))
continue;
- }
- if (StartsWithASCII(iter.key(), schema_constants::kUpdateUrlPrefix, true))
- continue;
- const std::string& extension_id = iter.key();
- if (!crx_file::id_util::IdIsValid(extension_id)) {
- LOG(WARNING) << kMalformedPreferenceWarning;
- continue;
- }
- IndividualSettings* by_id = AccessById(extension_id);
- if (!ParseIndividualSettings(subdict, SCOPE_INDIVIDUAL, by_id)) {
- settings_by_id_.erase(settings_by_id_.find(extension_id));
- LOG(WARNING) << "Malformed Extension Management settings for "
- << extension_id << ".";
+ if (StartsWithASCII(iter.key(), schema_constants::kUpdateUrlPrefix,
+ true)) {
+ const std::string& update_url =
+ iter.key().substr(strlen(schema_constants::kUpdateUrlPrefix));
+ if (!GURL(update_url).is_valid()) {
+ LOG(WARNING) << "Invalid update URL: " << update_url << ".";
+ continue;
+ }
+ internal::IndividualSettings* by_update_url =
+ AccessByUpdateUrl(update_url);
+ if (!by_update_url->Parse(
+ subdict, internal::IndividualSettings::SCOPE_UPDATE_URL)) {
+ settings_by_update_url_.erase(update_url);
+ LOG(WARNING) << "Malformed Extension Management settings for "
+ "extensions with update url: " << update_url << ".";
+ }
+ } else {
+ const std::string& extension_id = iter.key();
+ if (!crx_file::id_util::IdIsValid(extension_id)) {
+ LOG(WARNING) << "Invalid extension ID : " << extension_id << ".";
+ continue;
+ }
+ internal::IndividualSettings* by_id = AccessById(extension_id);
+ if (!by_id->Parse(subdict,
+ internal::IndividualSettings::SCOPE_INDIVIDUAL)) {
+ settings_by_id_.erase(extension_id);
+ LOG(WARNING) << "Malformed Extension Management settings for "
+ << extension_id << ".";
+ }
}
}
}
const char* pref_name,
bool force_managed,
base::Value::Type expected_type) {
+ if (!pref_service_)
+ return nullptr;
const PrefService::Preference* pref =
pref_service_->FindPreference(pref_name);
if (pref && !pref->IsDefaultValue() &&
if (value && value->IsType(expected_type))
return value;
}
- return NULL;
+ return nullptr;
}
void ExtensionManagement::OnExtensionPrefChanged() {
Observer, observer_list_, OnExtensionManagementSettingsChanged());
}
-ExtensionManagement::IndividualSettings* ExtensionManagement::AccessById(
+internal::IndividualSettings* ExtensionManagement::AccessById(
const ExtensionId& id) {
DCHECK(crx_file::id_util::IdIsValid(id)) << "Invalid ID: " << id;
SettingsIdMap::iterator it = settings_by_id_.find(id);
- if (it == settings_by_id_.end())
- it = settings_by_id_.insert(std::make_pair(id, default_settings_)).first;
- return &it->second;
+ if (it == settings_by_id_.end()) {
+ scoped_ptr<internal::IndividualSettings> settings(
+ new internal::IndividualSettings(*default_settings_));
+ it = settings_by_id_.add(id, settings.Pass()).first;
+ }
+ return it->second;
+}
+
+internal::IndividualSettings* ExtensionManagement::AccessByUpdateUrl(
+ const std::string& update_url) {
+ DCHECK(GURL(update_url).is_valid()) << "Invalid update URL: " << update_url;
+ SettingsUpdateUrlMap::iterator it = settings_by_update_url_.find(update_url);
+ if (it == settings_by_update_url_.end()) {
+ scoped_ptr<internal::IndividualSettings> settings(
+ new internal::IndividualSettings(*default_settings_));
+ it = settings_by_update_url_.add(update_url, settings.Pass()).first;
+ }
+ return it->second;
}
ExtensionManagement* ExtensionManagementFactory::GetForBrowserContext(