Commit 5f405efc authored by binjin's avatar binjin Committed by Commit bot

Initial ExtensionManagement class implementation, with legacy preference...

Initial ExtensionManagement class implementation, with legacy preference parsing and interface for obtaining basic structure of extension management settings.

BUG=177351
TEST=new unit test

Review URL: https://codereview.chromium.org/499313002

Cr-Commit-Position: refs/heads/master@{#293198}
parent 2f5c8d65
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/extensions/extension_management.h"
#include "base/logging.h"
#include "base/prefs/pref_service.h"
#include "chrome/browser/extensions/external_provider_impl.h"
#include "components/crx_file/id_util.h"
#include "extensions/browser/pref_names.h"
#include "extensions/common/url_pattern.h"
namespace extensions {
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) {
}
ExtensionManagement::~ExtensionManagement() {
}
void ExtensionManagement::Refresh() {
// Load all extension management settings preferences.
const base::ListValue* allowed_list_pref =
static_cast<const base::ListValue*>(LoadPreference(
pref_names::kInstallAllowList, true, base::Value::TYPE_LIST));
// Allow user to use preference to block certain extensions. Note that policy
// managed forcelist or whitelist will always override this.
const base::ListValue* denied_list_pref =
static_cast<const base::ListValue*>(LoadPreference(
pref_names::kInstallDenyList, false, base::Value::TYPE_LIST));
const base::DictionaryValue* forced_list_pref =
static_cast<const base::DictionaryValue*>(LoadPreference(
pref_names::kInstallForceList, true, base::Value::TYPE_DICTIONARY));
const base::ListValue* install_sources_pref =
static_cast<const base::ListValue*>(LoadPreference(
pref_names::kAllowedInstallSites, true, base::Value::TYPE_LIST));
const base::ListValue* allowed_types_pref =
static_cast<const base::ListValue*>(LoadPreference(
pref_names::kAllowedTypes, true, base::Value::TYPE_LIST));
// Reset all settings.
global_settings_.Reset();
settings_by_id_.clear();
default_settings_.Reset();
// 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;
}
// Parse legacy preferences.
ExtensionId id;
if (allowed_list_pref) {
for (base::ListValue::const_iterator it = allowed_list_pref->begin();
it != allowed_list_pref->end(); ++it) {
if ((*it)->GetAsString(&id) && crx_file::id_util::IdIsValid(id))
AccessById(id)->installation_mode = INSTALLATION_ALLOWED;
}
}
if (denied_list_pref) {
for (base::ListValue::const_iterator it = denied_list_pref->begin();
it != denied_list_pref->end(); ++it) {
if ((*it)->GetAsString(&id) && crx_file::id_util::IdIsValid(id))
AccessById(id)->installation_mode = INSTALLATION_BLOCKED;
}
}
if (forced_list_pref) {
std::string update_url;
for (base::DictionaryValue::Iterator it(*forced_list_pref); !it.IsAtEnd();
it.Advance()) {
if (!crx_file::id_util::IdIsValid(it.key()))
continue;
const base::DictionaryValue* dict_value = NULL;
if (it.value().GetAsDictionary(&dict_value) &&
dict_value->GetStringWithoutPathExpansion(
ExternalProviderImpl::kExternalUpdateUrl, &update_url)) {
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;
std::string url_pattern;
for (base::ListValue::const_iterator it = install_sources_pref->begin();
it != install_sources_pref->end(); ++it) {
URLPattern entry(URLPattern::SCHEME_ALL);
if ((*it)->GetAsString(&url_pattern)) {
if (entry.Parse(url_pattern) == URLPattern::PARSE_SUCCESS) {
global_settings_.install_sources.AddPattern(entry);
} else {
LOG(WARNING) << "Invalid URL pattern in for preference "
<< pref_names::kAllowedInstallSites << ": "
<< url_pattern << ".";
}
}
}
}
if (allowed_types_pref) {
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;
if ((*it)->GetAsInteger(&int_value) && int_value >= 0 &&
int_value < Manifest::Type::NUM_LOAD_TYPES) {
global_settings_.allowed_types.push_back(
static_cast<Manifest::Type>(int_value));
}
}
}
// TODO(binjin): Add parsing of new ExtensionManagement preference after the
// new ExtensionManagement policy is added.
}
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_;
}
const ExtensionManagement::GlobalSettings&
ExtensionManagement::ReadGlobalSettings() const {
return global_settings_;
}
const base::Value* ExtensionManagement::LoadPreference(
const char* pref_name,
bool force_managed,
base::Value::Type expected_type) {
const PrefService::Preference* pref =
pref_service_->FindPreference(pref_name);
if (pref && !pref->IsDefaultValue() &&
(!force_managed || pref->IsManaged())) {
const base::Value* value = pref->GetValue();
if (value && value->IsType(expected_type))
return value;
}
return NULL;
}
ExtensionManagement::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;
}
} // namespace extensions
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_MANAGEMENT_H_
#define CHROME_BROWSER_EXTENSIONS_EXTENSION_MANAGEMENT_H_
#include <map>
#include <string>
#include <vector>
#include "base/macros.h"
#include "base/values.h"
#include "extensions/common/extension.h"
#include "extensions/common/manifest.h"
#include "extensions/common/url_pattern_set.h"
class PrefService;
namespace extensions {
// Tracks the management policies that affect extensions and provides interfaces
// for observing and obtaining the global settings for all extensions, as well
// as per-extension settings.
class ExtensionManagement {
public:
// Installation mode for extensions, default is INSTALLATION_ALLOWED.
// * INSTALLATION_ALLOWED: Extension can be installed.
// * INSTALLATION_BLOCKED: Extension cannot be installed.
// * INSTALLATION_FORCED: Extension will be installed automatically
// and cannot be disabled.
// * INSTALLATION_RECOMMENDED: Extension will be installed automatically but
// can be disabled.
enum InstallationMode {
INSTALLATION_ALLOWED = 0,
INSTALLATION_BLOCKED,
INSTALLATION_FORCED,
INSTALLATION_RECOMMENDED,
};
// Class to hold extension management settings for one or a group of
// extensions. Settings can be applied to an individual extension identified
// by an ID, a group of extensions with specific |update_url| or all
// extensions at once.
struct IndividualSettings {
void Reset();
// Extension installation mode. Setting this to INSTALLATION_FORCED or
// INSTALLATION_RECOMMENDED will enable extension auto-loading (only
// applicable to single extension), and in this case the |update_url| must
// be specified, containing the update URL for this extension.
// Note that |update_url| will be ignored for INSTALLATION_ALLOWED and
// INSTALLATION_BLOCKED installation mode.
InstallationMode installation_mode;
std::string update_url;
};
// Global extension management settings, applicable to all extensions.
struct GlobalSettings {
GlobalSettings();
~GlobalSettings();
void Reset();
// Settings specifying which URLs are allowed to install extensions, will be
// enforced only if |has_restricted_install_sources| is set to true.
URLPatternSet install_sources;
bool has_restricted_install_sources;
// Settings specifying all allowed app/extension types, will be enforced
// only of |has_restricted_allowed_types| is set to true.
std::vector<Manifest::Type> allowed_types;
bool has_restricted_allowed_types;
};
typedef std::map<ExtensionId, IndividualSettings> SettingsIdMap;
explicit ExtensionManagement(PrefService* pref_service);
virtual ~ExtensionManagement();
// Load all extension management preferences from |pref_service|, and
// refresh the settings.
void Refresh();
// Helper function to read |settings_by_id_| with |id| as key. Returns a
// constant reference to default settings if |id| does not exist.
const IndividualSettings& ReadById(const ExtensionId& id) const;
// Returns a constant reference to |global_settings_|.
const GlobalSettings& ReadGlobalSettings() const;
private:
// Load preference with name |pref_name| and expected type |expected_type|.
// If |force_managed| is true, only loading from the managed preference store
// is allowed. Returns NULL if the preference is not present, not allowed to
// be loaded from or has the wrong type.
const base::Value* LoadPreference(const char* pref_name,
bool force_managed,
base::Value::Type expected_type);
// Helper function to access |settings_by_id_| with |id| as key.
// Adds a new IndividualSettings entry to |settings_by_id_| if none exists for
// |id| yet.
IndividualSettings* AccessById(const ExtensionId& id);
// A map containing all IndividualSettings applied to an individual extension
// identified by extension ID. The extension ID is used as index key of the
// map.
// TODO(binjin): Add |settings_by_update_url_|, and implement mechanism for
// it.
SettingsIdMap settings_by_id_;
// The default IndividualSettings.
// For extension settings applied to an individual extension (identified by
// extension ID) or a group of extension (with specified extension update
// URL), all unspecified part will take value from |default_settings_|.
// For all other extensions, all settings from |default_settings_| will be
// enforced.
IndividualSettings default_settings_;
// Extension settings applicable to all extensions.
GlobalSettings global_settings_;
PrefService* pref_service_;
DISALLOW_COPY_AND_ASSIGN(ExtensionManagement);
};
} // namespace extensions
#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_MANAGEMENT_H_
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <algorithm>
#include "base/memory/scoped_ptr.h"
#include "base/prefs/pref_registry_simple.h"
#include "base/prefs/testing_pref_service.h"
#include "base/values.h"
#include "chrome/browser/extensions/extension_management.h"
#include "chrome/browser/extensions/external_policy_loader.h"
#include "extensions/browser/pref_names.h"
#include "extensions/common/url_pattern.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace extensions {
namespace {
const char kTargetExtension[] = "abcdefghijklmnopabcdefghijklmnop";
const char kOtherExtension[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
const char kExampleUpdateUrl[] = "http://example.com/update_url";
} // namespace
class ExtensionManagementTest : public testing::Test {
public:
ExtensionManagementTest() {}
virtual ~ExtensionManagementTest() {}
// testing::Test:
virtual void SetUp() OVERRIDE {
pref_service_.reset(new TestingPrefServiceSimple());
pref_service_->registry()->RegisterListPref(
pref_names::kAllowedInstallSites);
pref_service_->registry()->RegisterListPref(pref_names::kAllowedTypes);
pref_service_->registry()->RegisterListPref(pref_names::kInstallDenyList);
pref_service_->registry()->RegisterListPref(pref_names::kInstallAllowList);
pref_service_->registry()->RegisterDictionaryPref(
pref_names::kInstallForceList);
extension_management_.reset(new ExtensionManagement(pref_service_.get()));
}
void SetPref(bool managed, const char* path, base::Value* value) {
if (managed)
pref_service_->SetManagedPref(path, value);
else
pref_service_->SetUserPref(path, value);
}
void RemovePref(bool managed, const char* path) {
if (managed)
pref_service_->RemoveManagedPref(path);
else
pref_service_->RemoveUserPref(path);
}
void Refresh() {
extension_management_->Refresh();
}
protected:
scoped_ptr<TestingPrefServiceSimple> pref_service_;
scoped_ptr<ExtensionManagement> extension_management_;
};
// Verify that preference controlled by legacy ExtensionInstallSources policy is
// handled well.
TEST_F(ExtensionManagementTest, LegacyInstallSources) {
base::ListValue allowed_sites_pref;
allowed_sites_pref.AppendString("https://www.example.com/foo");
allowed_sites_pref.AppendString("https://corp.mycompany.com/*");
SetPref(
true, pref_names::kAllowedInstallSites, allowed_sites_pref.DeepCopy());
Refresh();
const URLPatternSet& allowed_sites =
extension_management_->ReadGlobalSettings().install_sources;
ASSERT_TRUE(extension_management_->ReadGlobalSettings()
.has_restricted_install_sources);
EXPECT_FALSE(allowed_sites.is_empty());
EXPECT_TRUE(allowed_sites.MatchesURL(GURL("https://www.example.com/foo")));
EXPECT_FALSE(allowed_sites.MatchesURL(GURL("https://www.example.com/bar")));
EXPECT_TRUE(
allowed_sites.MatchesURL(GURL("https://corp.mycompany.com/entry")));
EXPECT_FALSE(
allowed_sites.MatchesURL(GURL("https://www.mycompany.com/entry")));
}
// Verify that preference controlled by legacy ExtensionAllowedTypes policy is
// handled well.
TEST_F(ExtensionManagementTest, LegacyAllowedTypes) {
base::ListValue allowed_types_pref;
allowed_types_pref.AppendInteger(Manifest::TYPE_THEME);
allowed_types_pref.AppendInteger(Manifest::TYPE_USER_SCRIPT);
SetPref(true, pref_names::kAllowedTypes, allowed_types_pref.DeepCopy());
Refresh();
const std::vector<Manifest::Type>& allowed_types =
extension_management_->ReadGlobalSettings().allowed_types;
ASSERT_TRUE(
extension_management_->ReadGlobalSettings().has_restricted_allowed_types);
EXPECT_TRUE(allowed_types.size() == 2);
EXPECT_FALSE(std::find(allowed_types.begin(),
allowed_types.end(),
Manifest::TYPE_EXTENSION) != allowed_types.end());
EXPECT_TRUE(std::find(allowed_types.begin(),
allowed_types.end(),
Manifest::TYPE_THEME) != allowed_types.end());
EXPECT_TRUE(std::find(allowed_types.begin(),
allowed_types.end(),
Manifest::TYPE_USER_SCRIPT) != allowed_types.end());
}
// Verify that preference controlled by legacy ExtensionInstallBlacklist policy
// is handled well.
TEST_F(ExtensionManagementTest, LegacyInstallBlacklist) {
base::ListValue denied_list_pref;
denied_list_pref.AppendString(kTargetExtension);
SetPref(true, pref_names::kInstallDenyList, denied_list_pref.DeepCopy());
Refresh();
EXPECT_EQ(extension_management_->ReadById(kTargetExtension).installation_mode,
ExtensionManagement::INSTALLATION_BLOCKED);
EXPECT_EQ(extension_management_->ReadById(kOtherExtension).installation_mode,
ExtensionManagement::INSTALLATION_ALLOWED);
}
// Verify that preference controlled by legacy ExtensionInstallWhitelist policy
// is handled well.
TEST_F(ExtensionManagementTest, LegacyInstallWhitelist) {
base::ListValue denied_list_pref;
denied_list_pref.AppendString("*");
base::ListValue allowed_list_pref;
allowed_list_pref.AppendString(kTargetExtension);
SetPref(true, pref_names::kInstallDenyList, denied_list_pref.DeepCopy());
SetPref(true, pref_names::kInstallAllowList, allowed_list_pref.DeepCopy());
Refresh();
EXPECT_EQ(extension_management_->ReadById(kTargetExtension).installation_mode,
ExtensionManagement::INSTALLATION_ALLOWED);
EXPECT_EQ(extension_management_->ReadById(kOtherExtension).installation_mode,
ExtensionManagement::INSTALLATION_BLOCKED);
// Verify that install whitelist preference set by user is ignored.
RemovePref(true, pref_names::kInstallAllowList);
SetPref(false, pref_names::kInstallAllowList, allowed_list_pref.DeepCopy());
Refresh();
EXPECT_EQ(extension_management_->ReadById(kTargetExtension).installation_mode,
ExtensionManagement::INSTALLATION_BLOCKED);
}
// Verify that preference controlled by legacy ExtensionInstallForcelist policy
// is handled well.
TEST_F(ExtensionManagementTest, LegacyInstallForcelist) {
base::DictionaryValue forced_list_pref;
ExternalPolicyLoader::AddExtension(
&forced_list_pref, kTargetExtension, kExampleUpdateUrl);
SetPref(true, pref_names::kInstallForceList, forced_list_pref.DeepCopy());
Refresh();
EXPECT_EQ(extension_management_->ReadById(kTargetExtension).installation_mode,
ExtensionManagement::INSTALLATION_FORCED);
EXPECT_EQ(extension_management_->ReadById(kTargetExtension).update_url,
kExampleUpdateUrl);
EXPECT_EQ(extension_management_->ReadById(kOtherExtension).installation_mode,
ExtensionManagement::INSTALLATION_ALLOWED);
// Verify that install forcelist preference set by user is ignored.
RemovePref(true, pref_names::kInstallForceList);
SetPref(false, pref_names::kInstallForceList, forced_list_pref.DeepCopy());
Refresh();
EXPECT_EQ(extension_management_->ReadById(kTargetExtension).installation_mode,
ExtensionManagement::INSTALLATION_ALLOWED);
}
} // namespace extensions
......@@ -684,6 +684,8 @@
'browser/extensions/extension_install_ui_util.h',
'browser/extensions/extension_keybinding_registry.cc',
'browser/extensions/extension_keybinding_registry.h',
'browser/extensions/extension_management.cc',
'browser/extensions/extension_management.h',
'browser/extensions/extension_message_bubble_controller.cc',
'browser/extensions/extension_message_bubble_controller.h',
'browser/extensions/extension_renderer_state.cc',
......
......@@ -937,6 +937,7 @@
'browser/extensions/extension_icon_manager_unittest.cc',
'browser/extensions/extension_install_checker_unittest.cc',
'browser/extensions/extension_install_prompt_unittest.cc',
'browser/extensions/extension_management_unittest.cc',
'browser/extensions/extension_message_bubble_controller_unittest.cc',
'browser/extensions/extension_path_util_unittest.cc',
'browser/extensions/extension_prefs_unittest.cc',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment