diff options
author | binjin <binjin@chromium.org> | 2014-10-30 18:55:57 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-10-31 01:56:20 +0000 |
commit | e6b58b5a41f010118c5caea9ba78bc077a5f551b (patch) | |
tree | 126ea761cfa5b0be561ea347dd1e9831ca868f3a | |
parent | 7393cee9845330bbe5e4712f5e16751256e6cb7c (diff) | |
download | chromium_src-e6b58b5a41f010118c5caea9ba78bc077a5f551b.zip chromium_src-e6b58b5a41f010118c5caea9ba78bc077a5f551b.tar.gz chromium_src-e6b58b5a41f010118c5caea9ba78bc077a5f551b.tar.bz2 |
Add policy controlled permission block list for extensions
This CL adds permissions block list for extensions. Currently only simple API permissions are supported, and the block list applies to both required and optional permissions of extensions.
BUG=177351
Review URL: https://codereview.chromium.org/595363002
Cr-Commit-Position: refs/heads/master@{#302211}
30 files changed, 1034 insertions, 46 deletions
diff --git a/chrome/browser/extensions/api/permissions/permissions_api.cc b/chrome/browser/extensions/api/permissions/permissions_api.cc index 0f23d4c..2b0b6a0 100644 --- a/chrome/browser/extensions/api/permissions/permissions_api.cc +++ b/chrome/browser/extensions/api/permissions/permissions_api.cc @@ -7,6 +7,7 @@ #include "base/memory/scoped_ptr.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/extensions/api/permissions/permissions_api_helpers.h" +#include "chrome/browser/extensions/extension_management.h" #include "chrome/browser/extensions/permissions_updater.h" #include "chrome/browser/profiles/profile.h" #include "chrome/common/extensions/api/permissions.h" @@ -30,6 +31,8 @@ namespace helpers = permissions_api_helpers; namespace { +const char kBlockedByEnterprisePolicy[] = + "Permissions are blocked by enterprise policy."; const char kCantRemoveRequiredPermissionsError[] = "You cannot remove required permissions."; const char kNotInOptionalPermissionsError[] = @@ -180,6 +183,15 @@ bool PermissionsRequestFunction::RunAsync() { return false; } + // Automatically declines api permissions requests, which are blocked by + // enterprise policy. + if (!ExtensionManagementFactory::GetForBrowserContext(GetProfile()) + ->IsPermissionSetAllowed(extension()->id(), + requested_permissions_)) { + error_ = kBlockedByEnterprisePolicy; + return false; + } + // We don't need to prompt the user if the requested permissions are a subset // of the granted permissions set. scoped_refptr<const PermissionSet> granted = diff --git a/chrome/browser/extensions/api/permissions/permissions_apitest.cc b/chrome/browser/extensions/api/permissions/permissions_apitest.cc index 798b2b5..7b6a35b 100644 --- a/chrome/browser/extensions/api/permissions/permissions_apitest.cc +++ b/chrome/browser/extensions/api/permissions/permissions_apitest.cc @@ -4,8 +4,11 @@ #include "chrome/browser/extensions/api/permissions/permissions_api.h" #include "chrome/browser/extensions/extension_apitest.h" +#include "chrome/browser/extensions/extension_management_test_util.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" +#include "components/policy/core/browser/browser_policy_connector.h" +#include "components/policy/core/common/mock_configuration_policy_provider.h" #include "extensions/browser/extension_prefs.h" #include "extensions/common/permissions/permission_set.h" #include "extensions/common/switches.h" @@ -23,13 +26,28 @@ static void AddPattern(URLPatternSet* extent, const std::string& pattern) { } // namespace class ExperimentalApiTest : public ExtensionApiTest { -public: - void SetUpCommandLine(CommandLine* command_line) override { + public: + void SetUpCommandLine(CommandLine* command_line) override { ExtensionApiTest::SetUpCommandLine(command_line); command_line->AppendSwitch(switches::kEnableExperimentalExtensionApis); } }; +class ExtensionApiTestWithManagementPolicy : public ExtensionApiTest { + public: + void SetUpInProcessBrowserTestFixture() override { + ExtensionApiTest::SetUpInProcessBrowserTestFixture(); + EXPECT_CALL(policy_provider_, IsInitializationComplete(testing::_)) + .WillRepeatedly(testing::Return(true)); + policy_provider_.SetAutoRefresh(); + policy::BrowserPolicyConnector::SetPolicyProviderForTesting( + &policy_provider_); + } + + protected: + policy::MockConfigurationPolicyProvider policy_provider_; +}; + IN_PROC_BROWSER_TEST_F(ExtensionApiTest, PermissionsFail) { ASSERT_TRUE(RunExtensionTest("permissions/disabled")) << message_; @@ -128,6 +146,22 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, OptionalPermissionsRetainGesture) { << message_; } +// Test that optional permissions blocked by enterprise policy will be denied +// automatically. +IN_PROC_BROWSER_TEST_F(ExtensionApiTestWithManagementPolicy, + OptionalPermissionsPolicyBlocked) { + // Set enterprise policy to block some API permissions. + { + ExtensionManagementPolicyUpdater pref(&policy_provider_); + pref.AddBlockedPermission("*", "management"); + } + // Set auto confirm UI flag. + PermissionsRequestFunction::SetAutoConfirmForTests(true); + PermissionsRequestFunction::SetIgnoreUserGestureForTests(true); + EXPECT_TRUE(RunExtensionTest("permissions/optional_policy_blocked")) + << message_; +} + // Tests that an extension can't gain access to file: URLs without the checkbox // entry in prefs. There shouldn't be a warning either. IN_PROC_BROWSER_TEST_F(ExtensionApiTest, OptionalPermissionsFileAccess) { diff --git a/chrome/browser/extensions/extension_management.cc b/chrome/browser/extensions/extension_management.cc index dff3946..6904f5e 100644 --- a/chrome/browser/extensions/extension_management.cc +++ b/chrome/browser/extensions/extension_management.cc @@ -6,17 +6,18 @@ #include <algorithm> #include <string> -#include <vector> #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" @@ -24,6 +25,8 @@ #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/permissions/api_permission_set.h" +#include "extensions/common/permissions/permission_set.h" #include "extensions/common/url_pattern.h" #include "url/gurl.h" @@ -49,12 +52,18 @@ ExtensionManagement::ExtensionManagement(PrefService* pref_service) // 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); } @@ -63,8 +72,9 @@ void ExtensionManagement::RemoveObserver(Observer* observer) { observer_list_.RemoveObserver(observer); } -ManagementPolicy::Provider* ExtensionManagement::GetProvider() const { - return provider_.get(); +std::vector<ManagementPolicy::Provider*> ExtensionManagement::GetProviders() + const { + return providers_.get(); } bool ExtensionManagement::BlacklistedByDefault() const { @@ -144,6 +154,31 @@ bool ExtensionManagement::IsAllowedManifestType( allowed_types.end(); } +const APIPermissionSet& ExtensionManagement::GetBlockedAPIPermissions( + const ExtensionId& id) const { + return ReadById(id)->blocked_permissions; +} + +scoped_refptr<const PermissionSet> ExtensionManagement::GetBlockedPermissions( + const ExtensionId& id) const { + // Only api permissions are supported currently. + return scoped_refptr<const PermissionSet>( + new PermissionSet(GetBlockedAPIPermissions(id), + ManifestPermissionSet(), + URLPatternSet(), + URLPatternSet())); +} + +bool ExtensionManagement::IsPermissionSetAllowed( + const ExtensionId& id, + scoped_refptr<const PermissionSet> perms) const { + for (const auto& blocked_api : GetBlockedAPIPermissions(id)) { + if (perms->HasAPIPermission(blocked_api->id())) + return false; + } + return true; +} + void ExtensionManagement::Refresh() { // Load all extension management settings preferences. const base::ListValue* allowed_list_pref = @@ -301,6 +336,8 @@ const base::Value* ExtensionManagement::LoadPreference( 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() && @@ -309,7 +346,7 @@ const base::Value* ExtensionManagement::LoadPreference( if (value && value->IsType(expected_type)) return value; } - return NULL; + return nullptr; } void ExtensionManagement::OnExtensionPrefChanged() { diff --git a/chrome/browser/extensions/extension_management.h b/chrome/browser/extensions/extension_management.h index 879cffb..5de7462 100644 --- a/chrome/browser/extensions/extension_management.h +++ b/chrome/browser/extensions/extension_management.h @@ -5,9 +5,13 @@ #ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_MANAGEMENT_H_ #define CHROME_BROWSER_EXTENSIONS_EXTENSION_MANAGEMENT_H_ +#include <vector> + #include "base/containers/scoped_ptr_hash_map.h" #include "base/macros.h" +#include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_vector.h" #include "base/memory/singleton.h" #include "base/observer_list.h" #include "base/prefs/pref_change_registrar.h" @@ -34,6 +38,9 @@ struct GlobalSettings; } // namespace internal +class APIPermissionSet; +class PermissionSet; + // 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. @@ -65,12 +72,15 @@ class ExtensionManagement : public KeyedService { explicit ExtensionManagement(PrefService* pref_service); ~ExtensionManagement() override; + // KeyedService implementations: + void Shutdown() override; + void AddObserver(Observer* observer); void RemoveObserver(Observer* observer); - // Get the ManagementPolicy::Provider controlled by extension management - // policy settings. - ManagementPolicy::Provider* GetProvider() const; + // Get the list of ManagementPolicy::Provider controlled by extension + // management policy settings. + std::vector<ManagementPolicy::Provider*> GetProviders() const; // Checks if extensions are blacklisted by default, by policy. When true, // this means that even extensions without an ID should be blacklisted (e.g. @@ -99,6 +109,17 @@ class ExtensionManagement : public KeyedService { // allowed to be installed. bool IsAllowedManifestType(Manifest::Type manifest_type) const; + // Returns the list of blocked API permissions for the extension |id|. + const APIPermissionSet& GetBlockedAPIPermissions(const ExtensionId& id) const; + + // Returns blocked permission set for extension |id|. + scoped_refptr<const PermissionSet> GetBlockedPermissions( + const ExtensionId& id) const; + + // Returns true if every permission in |perms| is allowed for extension |id|. + bool IsPermissionSetAllowed(const ExtensionId& id, + scoped_refptr<const PermissionSet> perms) const; + private: typedef base::ScopedPtrHashMap<ExtensionId, internal::IndividualSettings> SettingsIdMap; @@ -153,7 +174,7 @@ class ExtensionManagement : public KeyedService { ObserverList<Observer, true> observer_list_; PrefChangeRegistrar pref_change_registrar_; - scoped_ptr<ManagementPolicy::Provider> provider_; + ScopedVector<ManagementPolicy::Provider> providers_; DISALLOW_COPY_AND_ASSIGN(ExtensionManagement); }; diff --git a/chrome/browser/extensions/extension_management_constants.cc b/chrome/browser/extensions/extension_management_constants.cc index 399bb7c..707b3c0 100644 --- a/chrome/browser/extensions/extension_management_constants.cc +++ b/chrome/browser/extensions/extension_management_constants.cc @@ -17,6 +17,9 @@ const char kBlocked[] = "blocked"; const char kForceInstalled[] = "force_installed"; const char kNormalInstalled[] = "normal_installed"; +const char kBlockedPermissions[] = "blocked_permissions"; +const char kAllowedPermissions[] = "allowed_permissions"; + const char kUpdateUrl[] = "update_url"; const char kInstallSources[] = "install_sources"; const char kAllowedTypes[] = "allowed_types"; diff --git a/chrome/browser/extensions/extension_management_constants.h b/chrome/browser/extensions/extension_management_constants.h index 63a50aa..7df7e7d 100644 --- a/chrome/browser/extensions/extension_management_constants.h +++ b/chrome/browser/extensions/extension_management_constants.h @@ -20,6 +20,9 @@ extern const char kBlocked[]; extern const char kForceInstalled[]; extern const char kNormalInstalled[]; +extern const char kBlockedPermissions[]; +extern const char kAllowedPermissions[]; + extern const char kUpdateUrl[]; extern const char kInstallSources[]; extern const char kAllowedTypes[]; diff --git a/chrome/browser/extensions/extension_management_internal.cc b/chrome/browser/extensions/extension_management_internal.cc index 73bd132..ca7c5fb 100644 --- a/chrome/browser/extensions/extension_management_internal.cc +++ b/chrome/browser/extensions/extension_management_internal.cc @@ -67,12 +67,64 @@ bool IndividualSettings::Parse(const base::DictionaryValue* dict, } } + // Parses the blocked permission settings. + const base::ListValue* list_value = nullptr; + base::string16 error; + + // If applicable, inherit from global block list and remove all explicitly + // allowed permissions. + if (scope != SCOPE_DEFAULT && + dict->GetListWithoutPathExpansion(schema_constants::kAllowedPermissions, + &list_value)) { + // It is assumed that Parse() is already called for SCOPE_DEFAULT and + // settings specified for |this| is initialized by copying from default + // settings, including the |blocked_permissions| setting here. + // That is, |blocked_permissions| should be the default block permissions + // list settings here. + APIPermissionSet globally_blocked_permissions = blocked_permissions; + APIPermissionSet explicitly_allowed_permissions; + // Reuses code for parsing API permissions from manifest. But note that we + // only support list of strings type. + if (!APIPermissionSet::ParseFromJSON( + list_value, + APIPermissionSet::kDisallowInternalPermissions, + &explicitly_allowed_permissions, + &error, + nullptr)) { + // There might be unknown permissions, warn and just ignore them; + LOG(WARNING) << error; + } + APIPermissionSet::Difference(globally_blocked_permissions, + explicitly_allowed_permissions, + &blocked_permissions); + } + + // Then add all newly blocked permissions to the list. + if (dict->GetListWithoutPathExpansion(schema_constants::kBlockedPermissions, + &list_value)) { + // The |blocked_permissions| might be the result of the routines above, + // or remains the same as default block permissions settings. + APIPermissionSet permissions_to_merge_from = blocked_permissions; + APIPermissionSet permissions_parsed; + if (!APIPermissionSet::ParseFromJSON( + list_value, + APIPermissionSet::kDisallowInternalPermissions, + &permissions_parsed, + &error, + nullptr)) { + LOG(WARNING) << error; + } + APIPermissionSet::Union( + permissions_to_merge_from, permissions_parsed, &blocked_permissions); + } + return true; } void IndividualSettings::Reset() { installation_mode = ExtensionManagement::INSTALLATION_ALLOWED; update_url.clear(); + blocked_permissions.clear(); } GlobalSettings::GlobalSettings() { diff --git a/chrome/browser/extensions/extension_management_internal.h b/chrome/browser/extensions/extension_management_internal.h index d01bb33..0b3eaff 100644 --- a/chrome/browser/extensions/extension_management_internal.h +++ b/chrome/browser/extensions/extension_management_internal.h @@ -10,6 +10,7 @@ #include "base/macros.h" #include "chrome/browser/extensions/extension_management.h" #include "extensions/common/manifest.h" +#include "extensions/common/permissions/api_permission_set.h" namespace base { class DictionaryValue; @@ -42,6 +43,8 @@ struct IndividualSettings { // 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, |this| will NOT be left untouched. + // This method is required to be called in order of ParsingScope, i.e. first + // SCOPE_DEFAULT, then SCOPE_INDIVIDUAL. bool Parse(const base::DictionaryValue* dict, ParsingScope scope); // Extension installation mode. Setting this to INSTALLATION_FORCED or @@ -55,6 +58,17 @@ struct IndividualSettings { ExtensionManagement::InstallationMode installation_mode; std::string update_url; + // Permissions settings for extensions. These settings won't grant permissions + // to extensions automatically. Instead, these settings will provide a list of + // blocked permissions for each extension. That is, if an extension requires a + // permission which has been blacklisted, this extension will not be allowed + // to load. And if it contains a blocked permission as optional requirement, + // it will be allowed to load (of course, with permission granted from user if + // necessary), but conflicting permissions will be dropped. These settings + // will merge from the default settings, and unspecified settings will take + // value from default settings. + APIPermissionSet blocked_permissions; + private: DISALLOW_ASSIGN(IndividualSettings); }; diff --git a/chrome/browser/extensions/extension_management_test_util.cc b/chrome/browser/extensions/extension_management_test_util.cc index e60f9fc..930208d 100644 --- a/chrome/browser/extensions/extension_management_test_util.cc +++ b/chrome/browser/extensions/extension_management_test_util.cc @@ -4,7 +4,16 @@ #include "chrome/browser/extensions/extension_management_test_util.h" +#include <string> + #include "components/crx_file/id_util.h" +#include "components/policy/core/common/configuration_policy_provider.h" +#include "components/policy/core/common/mock_configuration_policy_provider.h" +#include "components/policy/core/common/policy_bundle.h" +#include "components/policy/core/common/policy_map.h" +#include "components/policy/core/common/policy_namespace.h" +#include "components/policy/core/common/policy_types.h" +#include "policy/policy/policy_constants.h" namespace extensions { @@ -12,13 +21,13 @@ namespace schema = schema_constants; namespace { +const char kInstallSourcesPath[] = "*.install_sources"; +const char kAllowedTypesPath[] = "*.allowed_types"; + std::string make_path(std::string a, std::string b) { return a + "." + b; } -const char kInstallSourcesPath[] = "*.install_sources"; -const char kAllowedTypesPath[] = "*.allowed_types"; - } // namespace ExtensionManagementPrefUpdaterBase::ExtensionManagementPrefUpdaterBase() { @@ -27,6 +36,8 @@ ExtensionManagementPrefUpdaterBase::ExtensionManagementPrefUpdaterBase() { ExtensionManagementPrefUpdaterBase::~ExtensionManagementPrefUpdaterBase() { } +// Helper functions for per extension settings --------------------------------- + void ExtensionManagementPrefUpdaterBase::UnsetPerExtensionSettings( const ExtensionId& id) { DCHECK(crx_file::id_util::IdIsValid(id)); @@ -39,6 +50,8 @@ void ExtensionManagementPrefUpdaterBase::ClearPerExtensionSettings( pref_->SetWithoutPathExpansion(id, new base::DictionaryValue()); } +// Helper functions for 'installation_mode' manipulation ----------------------- + void ExtensionManagementPrefUpdaterBase::SetBlacklistedByDefault(bool value) { pref_->SetString(make_path(schema::kWildcard, schema::kInstallationMode), value ? schema::kBlocked : schema::kAllowed); @@ -77,6 +90,8 @@ void ExtensionManagementPrefUpdaterBase::SetIndividualExtensionAutoInstalled( pref_->SetString(make_path(id, schema::kUpdateUrl), update_url); } +// Helper functions for 'install_sources' manipulation ------------------------- + void ExtensionManagementPrefUpdaterBase::UnsetInstallSources() { pref_->Remove(kInstallSourcesPath, NULL); } @@ -95,6 +110,8 @@ void ExtensionManagementPrefUpdaterBase::RemoveInstallSource( RemoveStringFromList(kInstallSourcesPath, install_source); } +// Helper functions for 'allowed_types' manipulation --------------------------- + void ExtensionManagementPrefUpdaterBase::UnsetAllowedTypes() { pref_->Remove(kAllowedTypesPath, NULL); } @@ -108,10 +125,76 @@ void ExtensionManagementPrefUpdaterBase::AddAllowedType( AddStringToList(kAllowedTypesPath, allowed_type); } +void ExtensionManagementPrefUpdaterBase::RemoveAllowedType( + const std::string& allowed_type) { + RemoveStringFromList(kAllowedTypesPath, allowed_type); +} + +// Helper functions for 'blocked_permissions' manipulation --------------------- + +void ExtensionManagementPrefUpdaterBase::UnsetBlockedPermissions( + const std::string& prefix) { + DCHECK(prefix == schema::kWildcard || crx_file::id_util::IdIsValid(prefix)); + pref_->Remove(make_path(prefix, schema::kBlockedPermissions), NULL); +} + +void ExtensionManagementPrefUpdaterBase::ClearBlockedPermissions( + const std::string& prefix) { + DCHECK(prefix == schema::kWildcard || crx_file::id_util::IdIsValid(prefix)); + ClearList(make_path(prefix, schema::kBlockedPermissions)); +} + +void ExtensionManagementPrefUpdaterBase::AddBlockedPermission( + const std::string& prefix, + const std::string& permission) { + DCHECK(prefix == schema::kWildcard || crx_file::id_util::IdIsValid(prefix)); + AddStringToList(make_path(prefix, schema::kBlockedPermissions), permission); +} + +void ExtensionManagementPrefUpdaterBase::RemoveBlockedPermission( + const std::string& prefix, + const std::string& permission) { + DCHECK(prefix == schema::kWildcard || crx_file::id_util::IdIsValid(prefix)); + RemoveStringFromList(make_path(prefix, schema::kBlockedPermissions), + permission); +} + +// Helper functions for 'allowed_permissions' manipulation --------------------- + +void ExtensionManagementPrefUpdaterBase::UnsetAllowedPermissions( + const std::string& id) { + DCHECK(crx_file::id_util::IdIsValid(id)); + pref_->Remove(make_path(id, schema::kAllowedPermissions), NULL); +} + +void ExtensionManagementPrefUpdaterBase::ClearAllowedPermissions( + const std::string& id) { + DCHECK(crx_file::id_util::IdIsValid(id)); + ClearList(make_path(id, schema::kAllowedPermissions)); +} + +void ExtensionManagementPrefUpdaterBase::AddAllowedPermission( + const std::string& id, + const std::string& permission) { + DCHECK(crx_file::id_util::IdIsValid(id)); + AddStringToList(make_path(id, schema::kAllowedPermissions), permission); +} + +void ExtensionManagementPrefUpdaterBase::RemoveAllowedPermission( + const std::string& id, + const std::string& permission) { + DCHECK(crx_file::id_util::IdIsValid(id)); + RemoveStringFromList(make_path(id, schema::kAllowedPermissions), permission); +} + +// Expose a read-only preference to user --------------------------------------- + const base::DictionaryValue* ExtensionManagementPrefUpdaterBase::GetPref() { return pref_.get(); } +// Private section functions --------------------------------------------------- + void ExtensionManagementPrefUpdaterBase::SetPref(base::DictionaryValue* pref) { pref_.reset(pref); } @@ -121,11 +204,6 @@ ExtensionManagementPrefUpdaterBase::TakePref() { return pref_.Pass(); } -void ExtensionManagementPrefUpdaterBase::RemoveAllowedType( - const std::string& allowd_type) { - RemoveStringFromList(kAllowedTypesPath, allowd_type); -} - void ExtensionManagementPrefUpdaterBase::ClearList(const std::string& path) { pref_->Set(path, new base::ListValue()); } @@ -149,4 +227,29 @@ void ExtensionManagementPrefUpdaterBase::RemoveStringFromList( CHECK(list_value->Remove(base::StringValue(str), NULL)); } +// ExtensionManagementPolicyUpdater -------------------------------------------- + +ExtensionManagementPolicyUpdater::ExtensionManagementPolicyUpdater( + policy::MockConfigurationPolicyProvider* policy_provider) + : provider_(policy_provider), policies_(new policy::PolicyBundle) { + policies_->CopyFrom(provider_->policies()); + const base::Value* policy_value = + policies_->Get(policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME, + std::string())) + .GetValue(policy::key::kExtensionSettings); + const base::DictionaryValue* dict_value = nullptr; + if (policy_value && policy_value->GetAsDictionary(&dict_value)) + SetPref(dict_value->DeepCopy()); + else + SetPref(new base::DictionaryValue); +} + +ExtensionManagementPolicyUpdater::~ExtensionManagementPolicyUpdater() { + policies_->Get(policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME, + std::string())) + .Set(policy::key::kExtensionSettings, policy::POLICY_LEVEL_MANDATORY, + policy::POLICY_SCOPE_USER, TakePref().release(), nullptr); + provider_->UpdatePolicy(policies_.Pass()); +} + } // namespace extensions diff --git a/chrome/browser/extensions/extension_management_test_util.h b/chrome/browser/extensions/extension_management_test_util.h index 5347f42..ded3ea4 100644 --- a/chrome/browser/extensions/extension_management_test_util.h +++ b/chrome/browser/extensions/extension_management_test_util.h @@ -14,6 +14,11 @@ #include "extensions/browser/pref_names.h" #include "extensions/common/extension.h" +namespace policy { +class MockConfigurationPolicyProvider; +class PolicyBundle; +} // namespace policy + namespace extensions { // Base class for essential routines on preference manipulation. @@ -45,7 +50,25 @@ class ExtensionManagementPrefUpdaterBase { void UnsetAllowedTypes(); void ClearAllowedTypes(); void AddAllowedType(const std::string& allowed_type); - void RemoveAllowedType(const std::string& allowd_type); + void RemoveAllowedType(const std::string& allowed_type); + + // Helper functions for 'blocked_permissions' manipulation. |prefix| can be + // kWildCard or a valid extension ID. + void UnsetBlockedPermissions(const std::string& prefix); + void ClearBlockedPermissions(const std::string& prefix); + void AddBlockedPermission(const std::string& prefix, + const std::string& permission); + void RemoveBlockedPermission(const std::string& prefix, + const std::string& permission); + + // Helper functions for 'allowed_permissions' manipulation. |id| must be a + // valid extension id. + void UnsetAllowedPermissions(const std::string& id); + void ClearAllowedPermissions(const std::string& id); + void AddAllowedPermission(const std::string& id, + const std::string& permission); + void RemoveAllowedPermission(const std::string& id, + const std::string& permission); // Expose a read-only preference to user. const base::DictionaryValue* GetPref(); @@ -82,13 +105,11 @@ class ExtensionManagementPrefUpdater : service_(service) { const base::Value* pref_value = service_->GetManagedPref(pref_names::kExtensionManagement); - if (pref_value) { - const base::DictionaryValue* dict_value = NULL; - pref_value->GetAsDictionary(&dict_value); + const base::DictionaryValue* dict_value = nullptr; + if (pref_value && pref_value->GetAsDictionary(&dict_value)) SetPref(dict_value->DeepCopy()); - } else { + else SetPref(new base::DictionaryValue); - } } virtual ~ExtensionManagementPrefUpdater() { @@ -102,6 +123,22 @@ class ExtensionManagementPrefUpdater DISALLOW_COPY_AND_ASSIGN(ExtensionManagementPrefUpdater); }; +// A helper class to manipulate the extension management policy in browser +// tests. +class ExtensionManagementPolicyUpdater + : public ExtensionManagementPrefUpdaterBase { + public: + explicit ExtensionManagementPolicyUpdater( + policy::MockConfigurationPolicyProvider* provider); + ~ExtensionManagementPolicyUpdater() override; + + private: + policy::MockConfigurationPolicyProvider* provider_; + scoped_ptr<policy::PolicyBundle> policies_; + + DISALLOW_COPY_AND_ASSIGN(ExtensionManagementPolicyUpdater); +}; + } // namespace extensions #endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_MANAGEMENT_TEST_UTIL_H_ diff --git a/chrome/browser/extensions/extension_management_unittest.cc b/chrome/browser/extensions/extension_management_unittest.cc index b3a59e4..451b1a8 100644 --- a/chrome/browser/extensions/extension_management_unittest.cc +++ b/chrome/browser/extensions/extension_management_unittest.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include <algorithm> +#include <string> #include <vector> #include "base/json/json_parser.h" @@ -14,9 +15,12 @@ #include "chrome/browser/extensions/extension_management_internal.h" #include "chrome/browser/extensions/extension_management_test_util.h" #include "chrome/browser/extensions/external_policy_loader.h" +#include "chrome/browser/extensions/standard_management_policy_provider.h" #include "extensions/browser/pref_names.h" #include "extensions/common/manifest.h" #include "extensions/common/manifest_constants.h" +#include "extensions/common/permissions/api_permission.h" +#include "extensions/common/permissions/permissions_info.h" #include "extensions/common/url_pattern.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" @@ -36,14 +40,18 @@ const char kExampleDictPreference[] = "{" " \"abcdefghijklmnopabcdefghijklmnop\": {" // kTargetExtension " \"installation_mode\": \"allowed\"," + " \"blocked_permissions\": [\"fileSystem\", \"bookmarks\"]," " }," " \"bcdefghijklmnopabcdefghijklmnopa\": {" // kTargetExtension2 " \"installation_mode\": \"force_installed\"," " \"update_url\": \"http://example.com/update_url\"," + " \"allowed_permissions\": [\"fileSystem\", \"bookmarks\"]," " }," " \"cdefghijklmnopabcdefghijklmnopab\": {" // kTargetExtension3 " \"installation_mode\": \"normal_installed\"," " \"update_url\": \"http://example.com/update_url\"," + " \"allowed_permissions\": [\"fileSystem\", \"downloads\"]," + " \"blocked_permissions\": [\"fileSystem\", \"history\"]," " }," " \"defghijklmnopabcdefghijklmnopabc\": {" // kTargetExtension4 " \"installation_mode\": \"blocked\"," @@ -52,6 +60,7 @@ const char kExampleDictPreference[] = " \"installation_mode\": \"blocked\"," " \"install_sources\": [\"*://foo.com/*\"]," " \"allowed_types\": [\"theme\", \"user_script\"]," + " \"blocked_permissions\": [\"fileSystem\", \"downloads\"]," " }," "}"; @@ -126,6 +135,11 @@ class ExtensionAdminPolicyTest : public ExtensionManagementServiceTest { ExtensionAdminPolicyTest() {} ~ExtensionAdminPolicyTest() override {} + void SetUpPolicyProvider() { + provider_.reset( + new StandardManagementPolicyProvider(extension_management_.get())); + } + void CreateExtension(Manifest::Location location) { base::DictionaryValue values; CreateExtensionFromValues(location, &values); @@ -161,12 +175,13 @@ class ExtensionAdminPolicyTest : public ExtensionManagementServiceTest { bool MustRemainEnabled(const Extension* extension, base::string16* error); protected: + scoped_ptr<StandardManagementPolicyProvider> provider_; scoped_refptr<Extension> extension_; }; bool ExtensionAdminPolicyTest::BlacklistedByDefault( const base::ListValue* blacklist) { - InitPrefService(); + SetUpPolicyProvider(); if (blacklist) SetPref(true, pref_names::kInstallDenyList, blacklist->DeepCopy()); return extension_management_->BlacklistedByDefault(); @@ -179,7 +194,7 @@ bool ExtensionAdminPolicyTest::UserMayLoad( const base::ListValue* allowed_types, const Extension* extension, base::string16* error) { - InitPrefService(); + SetUpPolicyProvider(); if (blacklist) SetPref(true, pref_names::kInstallDenyList, blacklist->DeepCopy()); if (whitelist) @@ -188,21 +203,19 @@ bool ExtensionAdminPolicyTest::UserMayLoad( SetPref(true, pref_names::kInstallForceList, forcelist->DeepCopy()); if (allowed_types) SetPref(true, pref_names::kAllowedTypes, allowed_types->DeepCopy()); - return extension_management_->GetProvider()->UserMayLoad(extension, error); + return provider_->UserMayLoad(extension, error); } bool ExtensionAdminPolicyTest::UserMayModifySettings(const Extension* extension, base::string16* error) { - InitPrefService(); - return extension_management_->GetProvider()->UserMayModifySettings(extension, - error); + SetUpPolicyProvider(); + return provider_->UserMayModifySettings(extension, error); } bool ExtensionAdminPolicyTest::MustRemainEnabled(const Extension* extension, base::string16* error) { - InitPrefService(); - return extension_management_->GetProvider()->MustRemainEnabled(extension, - error); + SetUpPolicyProvider(); + return provider_->MustRemainEnabled(extension, error); } // Verify that preference controlled by legacy ExtensionInstallSources policy is @@ -235,7 +248,7 @@ TEST_F(ExtensionManagementServiceTest, LegacyAllowedTypes) { const std::vector<Manifest::Type>& allowed_types = ReadGlobalSettings()->allowed_types; ASSERT_TRUE(ReadGlobalSettings()->has_restricted_allowed_types); - EXPECT_TRUE(allowed_types.size() == 2); + EXPECT_EQ(allowed_types.size(), 2u); EXPECT_FALSE(std::find(allowed_types.begin(), allowed_types.end(), Manifest::TYPE_EXTENSION) != allowed_types.end()); @@ -337,6 +350,32 @@ TEST_F(ExtensionManagementServiceTest, PreferenceParsing) { EXPECT_TRUE(std::find(allowed_types.begin(), allowed_types.end(), Manifest::TYPE_USER_SCRIPT) != allowed_types.end()); + + // Verifies blocked permission list settings. + APIPermissionSet api_permission_set; + api_permission_set.clear(); + api_permission_set.insert(APIPermission::kFileSystem); + api_permission_set.insert(APIPermission::kDownloads); + EXPECT_EQ(api_permission_set, + extension_management_->GetBlockedAPIPermissions(kOtherExtension)); + + api_permission_set.clear(); + api_permission_set.insert(APIPermission::kFileSystem); + api_permission_set.insert(APIPermission::kDownloads); + api_permission_set.insert(APIPermission::kBookmark); + EXPECT_EQ(api_permission_set, + extension_management_->GetBlockedAPIPermissions(kTargetExtension)); + + api_permission_set.clear(); + api_permission_set.insert(APIPermission::kDownloads); + EXPECT_EQ(api_permission_set, + extension_management_->GetBlockedAPIPermissions(kTargetExtension2)); + + api_permission_set.clear(); + api_permission_set.insert(APIPermission::kFileSystem); + api_permission_set.insert(APIPermission::kHistory); + EXPECT_EQ(api_permission_set, + extension_management_->GetBlockedAPIPermissions(kTargetExtension3)); } // Tests functionality of new preference as to deprecate legacy diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc index 7636dfe..76ac9a2 100644 --- a/chrome/browser/extensions/extension_service.cc +++ b/chrome/browser/extensions/extension_service.cc @@ -1709,6 +1709,23 @@ void ExtensionService::OnExtensionInstalled( void ExtensionService::OnExtensionManagementSettingsChanged() { error_controller_->ShowErrorIfNeeded(); + + // Revokes blocked permissions from active_permissions for all extensions. + extensions::ExtensionManagement* settings = + extensions::ExtensionManagementFactory::GetForBrowserContext(profile()); + CHECK(settings); + scoped_ptr<ExtensionSet> all_extensions( + registry_->GenerateInstalledExtensionsSet()); + for (const auto& extension : *all_extensions.get()) { + if (!settings->IsPermissionSetAllowed( + extension->id(), + extension->permissions_data()->active_permissions())) { + extensions::PermissionsUpdater(profile()).RemovePermissions( + extension.get(), + settings->GetBlockedPermissions(extension->id()).get()); + } + } + CheckManagementPolicy(); } diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc index d23b98e..0fde625 100644 --- a/chrome/browser/extensions/extension_service_unittest.cc +++ b/chrome/browser/extensions/extension_service_unittest.cc @@ -55,6 +55,7 @@ #include "chrome/browser/extensions/pack_extension_job.h" #include "chrome/browser/extensions/pending_extension_info.h" #include "chrome/browser/extensions/pending_extension_manager.h" +#include "chrome/browser/extensions/permissions_updater.h" #include "chrome/browser/extensions/test_blacklist.h" #include "chrome/browser/extensions/test_extension_system.h" #include "chrome/browser/extensions/unpacked_installer.h" @@ -99,6 +100,7 @@ #include "extensions/common/feature_switch.h" #include "extensions/common/manifest_constants.h" #include "extensions/common/manifest_handlers/background_info.h" +#include "extensions/common/manifest_handlers/permissions_parser.h" #include "extensions/common/manifest_url_handlers.h" #include "extensions/common/permissions/permission_set.h" #include "extensions/common/permissions/permissions_data.h" @@ -189,6 +191,7 @@ const char theme2_crx[] = "pjpgmfcmabopnnfonnhmdjglfpjjfkbf"; const char permissions_crx[] = "eagpmdpfmaekmmcejjbmjoecnejeiiin"; const char unpacked[] = "cbcdidchbppangcjoddlpdjlenngjldk"; const char updates_from_webstore[] = "akjooamlhcgeopfifcmlggaebeocgokj"; +const char permissions_blocklist[] = "noffkehfcaggllbcojjbopcmlhcnhcdn"; struct ExtensionsOrder { bool operator()(const scoped_refptr<const Extension>& a, @@ -767,6 +770,16 @@ class ExtensionServiceTest : public extensions::ExtensionServiceTestBase, json_blacklist, gpu_info); } + // Grants all optional permissions stated in manifest to active permission + // set for extension |id|. + void GrantAllOptionalPermissions(const std::string& id) { + const Extension* extension = service()->GetInstalledExtension(id); + scoped_refptr<const PermissionSet> all_optional_permissions = + extensions::PermissionsParser::GetOptionalPermissions(extension); + extensions::PermissionsUpdater perms_updater(profile()); + perms_updater.AddPermissions(extension, all_optional_permissions.get()); + } + // Helper method to set up a WindowedNotificationObserver to wait for a // specific CrxInstaller to finish if we don't know the value of the // |installer| yet. @@ -3843,6 +3856,219 @@ TEST_F(ExtensionServiceTest, ManagementPolicyRequiresEnable) { EXPECT_EQ(0u, registry()->disabled_extensions().size()); } +// Tests that extensions with conflicting required permissions by enterprise +// policy cannot be installed. +TEST_F(ExtensionServiceTest, PolicyBlockedPermissionNewExtensionInstall) { + InitializeEmptyExtensionServiceWithTestingPrefs(); + base::FilePath path = data_dir().AppendASCII("permissions_blocklist"); + + { + // Update policy to block one of the required permissions of target. + ManagementPrefUpdater pref(profile_->GetTestingPrefService()); + pref.AddBlockedPermission("*", "tabs"); + } + + // The extension should be failed to install. + PackAndInstallCRX(path, INSTALL_FAILED); + + { + // Update policy to block one of the optional permissions instead. + ManagementPrefUpdater pref(profile_->GetTestingPrefService()); + pref.ClearBlockedPermissions("*"); + pref.AddBlockedPermission("*", "history"); + } + + // The extension should succeed to install this time. + std::string id = PackAndInstallCRX(path, INSTALL_NEW)->id(); + + // Uninstall the extension and update policy to block some arbitrary + // unknown permission. + UninstallExtension(id, false); + { + ManagementPrefUpdater pref(profile_->GetTestingPrefService()); + pref.ClearBlockedPermissions("*"); + pref.AddBlockedPermission("*", "unknown.permission.for.testing"); + } + + // The extension should succeed to install as well. + PackAndInstallCRX(path, INSTALL_NEW); +} + +// Tests that extension supposed to be force installed but with conflicting +// required permissions cannot be installed. +TEST_F(ExtensionServiceTest, PolicyBlockedPermissionConflictsWithForceInstall) { + InitializeEmptyExtensionServiceWithTestingPrefs(); + + // Pack the crx file. + base::FilePath path = data_dir().AppendASCII("permissions_blocklist"); + base::FilePath pem_path = data_dir().AppendASCII("permissions_blocklist.pem"); + base::ScopedTempDir temp_dir; + EXPECT_TRUE(temp_dir.CreateUniqueTempDir()); + base::FilePath crx_path = temp_dir.path().AppendASCII("temp.crx"); + + PackCRX(path, pem_path, crx_path); + + { + // Block one of the required permissions. + ManagementPrefUpdater pref(profile_->GetTestingPrefService()); + pref.AddBlockedPermission("*", "tabs"); + } + + // Use MockExtensionProvider to simulate force installing extension. + MockExtensionProvider* provider = + new MockExtensionProvider(service(), Manifest::EXTERNAL_POLICY_DOWNLOAD); + AddMockExternalProvider(provider); + provider->UpdateOrAddExtension(permissions_blocklist, "1.0", crx_path); + + { + // Attempts to force install this extension. + content::WindowedNotificationObserver observer( + extensions::NOTIFICATION_CRX_INSTALLER_DONE, + content::NotificationService::AllSources()); + service()->CheckForExternalUpdates(); + observer.Wait(); + } + + // The extension should not be installed. + ASSERT_FALSE(service()->GetInstalledExtension(permissions_blocklist)); + + // Remove this extension from pending extension manager as we would like to + // give another attempt later. + service()->pending_extension_manager()->Remove(permissions_blocklist); + + { + // Clears the permission block list. + ManagementPrefUpdater pref(profile_->GetTestingPrefService()); + pref.ClearBlockedPermissions("*"); + } + + { + // Attempts to force install this extension again. + content::WindowedNotificationObserver observer( + extensions::NOTIFICATION_CRX_INSTALLER_DONE, + content::NotificationService::AllSources()); + service()->CheckForExternalUpdates(); + observer.Wait(); + } + + const Extension* installed = + service()->GetInstalledExtension(permissions_blocklist); + ASSERT_TRUE(installed); + EXPECT_EQ(installed->location(), Manifest::EXTERNAL_POLICY_DOWNLOAD); +} + +// Tests that newer versions of an extension with conflicting required +// permissions by enterprise policy cannot be updated to. +TEST_F(ExtensionServiceTest, PolicyBlockedPermissionExtensionUpdate) { + InitializeEmptyExtensionServiceWithTestingPrefs(); + + base::FilePath path = data_dir().AppendASCII("permissions_blocklist"); + base::FilePath path2 = data_dir().AppendASCII("permissions_blocklist2"); + base::FilePath pem_path = data_dir().AppendASCII("permissions_blocklist.pem"); + + // Install 'permissions_blocklist'. + const Extension* installed = PackAndInstallCRX(path, pem_path, INSTALL_NEW); + EXPECT_EQ(installed->id(), permissions_blocklist); + + { + // Block one of the required permissions of 'permissions_blocklist2'. + ManagementPrefUpdater pref(profile_->GetTestingPrefService()); + pref.AddBlockedPermission("*", "downloads"); + } + + // Install 'permissions_blocklist' again, should be updated. + const Extension* updated = PackAndInstallCRX(path, pem_path, INSTALL_UPDATED); + EXPECT_EQ(updated->id(), permissions_blocklist); + + std::string old_version = updated->VersionString(); + + // Attempts to update to 'permissions_blocklist2' should fail. + PackAndInstallCRX(path2, pem_path, INSTALL_FAILED); + + // Verify that the old version is still enabled. + updated = service()->GetExtensionById(permissions_blocklist, false); + ASSERT_TRUE(updated); + EXPECT_EQ(old_version, updated->VersionString()); +} + +// Tests that policy update with additional permissions blocked revoke +// conflicting granted optional permissions and unload extensions with +// conflicting required permissions, including the force installed ones. +TEST_F(ExtensionServiceTest, PolicyBlockedPermissionPolicyUpdate) { + InitializeEmptyExtensionServiceWithTestingPrefs(); + + base::FilePath path = data_dir().AppendASCII("permissions_blocklist"); + base::FilePath path2 = data_dir().AppendASCII("permissions_blocklist2"); + base::FilePath pem_path = data_dir().AppendASCII("permissions_blocklist.pem"); + + // Pack the crx file. + base::ScopedTempDir temp_dir; + EXPECT_TRUE(temp_dir.CreateUniqueTempDir()); + base::FilePath crx_path = temp_dir.path().AppendASCII("temp.crx"); + + PackCRX(path2, pem_path, crx_path); + + // Install two arbitary extensions with specified manifest. + std::string ext1 = PackAndInstallCRX(path, INSTALL_NEW)->id(); + std::string ext2 = PackAndInstallCRX(path2, INSTALL_NEW)->id(); + ASSERT_NE(ext1, permissions_blocklist); + ASSERT_NE(ext2, permissions_blocklist); + ASSERT_NE(ext1, ext2); + + // Force install another extension with known id and same manifest as 'ext2'. + std::string ext2_forced = permissions_blocklist; + MockExtensionProvider* provider = + new MockExtensionProvider(service(), Manifest::EXTERNAL_POLICY_DOWNLOAD); + AddMockExternalProvider(provider); + provider->UpdateOrAddExtension(ext2_forced, "2.0", crx_path); + + content::WindowedNotificationObserver observer( + extensions::NOTIFICATION_CRX_INSTALLER_DONE, + content::NotificationService::AllSources()); + service()->CheckForExternalUpdates(); + observer.Wait(); + + extensions::ExtensionRegistry* registry = + extensions::ExtensionRegistry::Get(profile()); + + // Verify all three extensions are installed and enabled. + ASSERT_TRUE(registry->enabled_extensions().GetByID(ext1)); + ASSERT_TRUE(registry->enabled_extensions().GetByID(ext2)); + ASSERT_TRUE(registry->enabled_extensions().GetByID(ext2_forced)); + + // Grant all optional permissions to each extension. + GrantAllOptionalPermissions(ext1); + GrantAllOptionalPermissions(ext2); + GrantAllOptionalPermissions(ext2_forced); + + scoped_refptr<const PermissionSet> active_permissions( + ExtensionPrefs::Get(profile())->GetActivePermissions(ext1)); + EXPECT_TRUE(active_permissions->HasAPIPermission( + extensions::APIPermission::kDownloads)); + + // Set policy to block 'downloads' permission. + { + ManagementPrefUpdater pref(profile_->GetTestingPrefService()); + pref.AddBlockedPermission("*", "downloads"); + } + + base::RunLoop().RunUntilIdle(); + + // 'ext1' should still be enabled, but with 'downloads' permission revoked. + EXPECT_TRUE(registry->enabled_extensions().GetByID(ext1)); + active_permissions = + ExtensionPrefs::Get(profile())->GetActivePermissions(ext1); + EXPECT_FALSE(active_permissions->HasAPIPermission( + extensions::APIPermission::kDownloads)); + + // 'ext2' should be disabled because one of its required permissions is + // blocked. + EXPECT_FALSE(registry->enabled_extensions().GetByID(ext2)); + + // 'ext2_forced' should be handled the same as 'ext2' + EXPECT_FALSE(registry->enabled_extensions().GetByID(ext2_forced)); +} + // Flaky on windows; http://crbug.com/309833 #if defined(OS_WIN) #define MAYBE_ExternalExtensionAutoAcknowledgement DISABLED_ExternalExtensionAutoAcknowledgement diff --git a/chrome/browser/extensions/extension_system_impl.cc b/chrome/browser/extensions/extension_system_impl.cc index 11d69fe..02de369 100644 --- a/chrome/browser/extensions/extension_system_impl.cc +++ b/chrome/browser/extensions/extension_system_impl.cc @@ -137,9 +137,9 @@ void ExtensionSystemImpl::Shared::InitPrefs() { } void ExtensionSystemImpl::Shared::RegisterManagementPolicyProviders() { - management_policy_->RegisterProvider( + management_policy_->RegisterProviders( ExtensionManagementFactory::GetForBrowserContext(profile_) - ->GetProvider()); + ->GetProviders()); #if defined(OS_CHROMEOS) if (device_local_account_management_policy_provider_) { diff --git a/chrome/browser/extensions/permissions_based_management_policy_provider.cc b/chrome/browser/extensions/permissions_based_management_policy_provider.cc new file mode 100644 index 0000000..4b29945 --- /dev/null +++ b/chrome/browser/extensions/permissions_based_management_policy_provider.cc @@ -0,0 +1,61 @@ +// 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/permissions_based_management_policy_provider.h" + +#include "base/strings/string16.h" +#include "base/strings/utf_string_conversions.h" +#include "chrome/browser/extensions/extension_management.h" +#include "extensions/common/extension.h" +#include "extensions/common/manifest_handlers/permissions_parser.h" +#include "extensions/common/permissions/permission_set.h" +#include "grit/extensions_strings.h" +#include "ui/base/l10n/l10n_util.h" + +namespace extensions { + +PermissionsBasedManagementPolicyProvider:: + PermissionsBasedManagementPolicyProvider(ExtensionManagement* settings) + : settings_(settings) { +} + +PermissionsBasedManagementPolicyProvider:: + ~PermissionsBasedManagementPolicyProvider() { +} + +std::string +PermissionsBasedManagementPolicyProvider::GetDebugPolicyProviderName() const { +#ifdef NDEBUG + NOTREACHED(); + return std::string(); +#else + return "Controlled by enterprise policy, restricting extension permissions."; +#endif +} + +bool PermissionsBasedManagementPolicyProvider::UserMayLoad( + const Extension* extension, + base::string16* error) const { + // Component extensions are always allowed. + if (Manifest::IsComponentLocation(extension->location())) + return true; + + scoped_refptr<const PermissionSet> required_permissions = + PermissionsParser::GetRequiredPermissions(extension); + + if (!settings_->IsPermissionSetAllowed(extension->id(), + required_permissions)) { + if (error) { + *error = + l10n_util::GetStringFUTF16(IDS_EXTENSION_CANT_INSTALL_POLICY_BLOCKED, + base::UTF8ToUTF16(extension->name()), + base::UTF8ToUTF16(extension->id())); + } + return false; + } + + return true; +} + +} // namespace extensions diff --git a/chrome/browser/extensions/permissions_based_management_policy_provider.h b/chrome/browser/extensions/permissions_based_management_policy_provider.h new file mode 100644 index 0000000..fc8ca72 --- /dev/null +++ b/chrome/browser/extensions/permissions_based_management_policy_provider.h @@ -0,0 +1,41 @@ +// 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_PERMISSIONS_BASED_MANAGEMENT_POLICY_PROVIDER_H_ +#define CHROME_BROWSER_EXTENSIONS_PERMISSIONS_BASED_MANAGEMENT_POLICY_PROVIDER_H_ + +#include <string> + +#include "base/macros.h" +#include "extensions/browser/management_policy.h" + +namespace extensions { + +class Extension; +class ExtensionManagement; + +// A ManagementPolicyProvider controlled by enterprise policy, and prevent +// certain extensions from loading by checking if its permission data conflicts +// with policy or not. +class PermissionsBasedManagementPolicyProvider + : public ManagementPolicy::Provider { + public: + explicit PermissionsBasedManagementPolicyProvider( + ExtensionManagement* settings); + ~PermissionsBasedManagementPolicyProvider() override; + + // ManagementPolicy::Provider implementation. + std::string GetDebugPolicyProviderName() const override; + bool UserMayLoad(const Extension* extension, + base::string16* error) const override; + + private: + ExtensionManagement* settings_; + + DISALLOW_COPY_AND_ASSIGN(PermissionsBasedManagementPolicyProvider); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_PERMISSIONS_BASED_MANAGEMENT_POLICY_PROVIDER_H_ diff --git a/chrome/browser/extensions/permissions_based_management_policy_provider_unittest.cc b/chrome/browser/extensions/permissions_based_management_policy_provider_unittest.cc new file mode 100644 index 0000000..e7eacfd --- /dev/null +++ b/chrome/browser/extensions/permissions_based_management_policy_provider_unittest.cc @@ -0,0 +1,169 @@ +// 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 <string> +#include <vector> + +#include "base/logging.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/prefs/pref_registry_simple.h" +#include "base/prefs/testing_pref_service.h" +#include "base/stl_util.h" +#include "base/strings/string16.h" +#include "base/values.h" +#include "chrome/browser/extensions/extension_management.h" +#include "chrome/browser/extensions/extension_management_test_util.h" +#include "chrome/browser/extensions/permissions_based_management_policy_provider.h" +#include "chrome/common/extensions/permissions/chrome_api_permissions.h" +#include "extensions/common/extension.h" +#include "extensions/common/manifest.h" +#include "extensions/common/manifest_constants.h" +#include "extensions/common/permissions/api_permission.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace extensions { + +class PermissionsBasedManagementPolicyProviderTest : public testing::Test { + public: + typedef ExtensionManagementPrefUpdater<TestingPrefServiceSimple> PrefUpdater; + + PermissionsBasedManagementPolicyProviderTest() + : pref_service_(new TestingPrefServiceSimple()), + settings_(new ExtensionManagement(pref_service_.get())), + provider_(settings_.get()) {} + + void SetUp() override { + ChromeAPIPermissions api_permissions; + perm_list_ = api_permissions.GetAllPermissions(); + pref_service_->registry()->RegisterDictionaryPref( + pref_names::kExtensionManagement); + } + + void TearDown() override { + STLDeleteElements(&perm_list_); + } + + // Get API permissions name for |id|, we cannot use arbitrary strings since + // they will be ignored by ExtensionManagementService. + std::string GetAPIPermissionName(APIPermission::ID id) { + for (const auto& perm : perm_list_) { + if (perm->id() == id) + return perm->name(); + } + ADD_FAILURE() << "Permission not found: " << id; + return std::string(); + } + + // Create an extension with specified |location|, |required_permissions| and + // |optional_permissions|. + scoped_refptr<const Extension> CreateExtensionWithPermission( + Manifest::Location location, + const base::ListValue* required_permissions, + const base::ListValue* optional_permissions) { + base::DictionaryValue manifest_dict; + manifest_dict.SetString(manifest_keys::kName, "test"); + manifest_dict.SetString(manifest_keys::kVersion, "0.1"); + if (required_permissions) { + manifest_dict.Set(manifest_keys::kPermissions, + required_permissions->DeepCopy()); + } + if (optional_permissions) { + manifest_dict.Set(manifest_keys::kOptionalPermissions, + optional_permissions->DeepCopy()); + } + std::string error; + scoped_refptr<const Extension> extension = Extension::Create( + base::FilePath(), location, manifest_dict, Extension::NO_FLAGS, &error); + CHECK(extension.get()) << error; + return extension; + } + + protected: + std::vector<APIPermissionInfo*> perm_list_; + + scoped_ptr<TestingPrefServiceSimple> pref_service_; + scoped_ptr<ExtensionManagement> settings_; + + PermissionsBasedManagementPolicyProvider provider_; +}; + +// Verifies that extensions with conflicting permissions cannot be loaded. +TEST_F(PermissionsBasedManagementPolicyProviderTest, APIPermissions) { + // Prepares the extension manifest. + base::ListValue required_permissions; + required_permissions.AppendString( + GetAPIPermissionName(APIPermission::kDownloads)); + required_permissions.AppendString( + GetAPIPermissionName(APIPermission::kCookie)); + base::ListValue optional_permissions; + optional_permissions.AppendString( + GetAPIPermissionName(APIPermission::kProxy)); + + scoped_refptr<const Extension> extension = + CreateExtensionWithPermission(Manifest::EXTERNAL_POLICY_DOWNLOAD, + &required_permissions, + &optional_permissions); + + base::string16 error16; + // The extension should be allowed to be loaded by default. + error16.clear(); + EXPECT_TRUE(provider_.UserMayLoad(extension.get(), &error16)); + EXPECT_TRUE(error16.empty()); + + // Blocks kProxy by default. The test extension should still be allowed. + { + PrefUpdater pref(pref_service_.get()); + pref.AddBlockedPermission("*", + GetAPIPermissionName(APIPermission::kProxy)); + } + error16.clear(); + EXPECT_TRUE(provider_.UserMayLoad(extension.get(), &error16)); + EXPECT_TRUE(error16.empty()); + + // Blocks kCookie this time. The test extension should not be allowed now. + { + PrefUpdater pref(pref_service_.get()); + pref.AddBlockedPermission("*", + GetAPIPermissionName(APIPermission::kCookie)); + } + error16.clear(); + EXPECT_FALSE(provider_.UserMayLoad(extension.get(), &error16)); + EXPECT_FALSE(error16.empty()); + + // Explictly allows kCookie for test extension. It should be allowed again. + { + PrefUpdater pref(pref_service_.get()); + pref.AddAllowedPermission(extension->id(), + GetAPIPermissionName(APIPermission::kCookie)); + } + error16.clear(); + EXPECT_TRUE(provider_.UserMayLoad(extension.get(), &error16)); + EXPECT_TRUE(error16.empty()); + + // Explictly blocks kCookie for test extension. It should be blocked again. + { + PrefUpdater pref(pref_service_.get()); + pref.AddBlockedPermission(extension->id(), + GetAPIPermissionName(APIPermission::kCookie)); + } + error16.clear(); + EXPECT_FALSE(provider_.UserMayLoad(extension.get(), &error16)); + EXPECT_FALSE(error16.empty()); + + // Blocks kDownloads by default. It should be blocked. + { + PrefUpdater pref(pref_service_.get()); + pref.UnsetBlockedPermissions(extension->id()); + pref.UnsetAllowedPermissions(extension->id()); + pref.ClearBlockedPermissions("*"); + pref.AddBlockedPermission("*", + GetAPIPermissionName(APIPermission::kDownloads)); + } + error16.clear(); + EXPECT_FALSE(provider_.UserMayLoad(extension.get(), &error16)); + EXPECT_FALSE(error16.empty()); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/test_extension_system.cc b/chrome/browser/extensions/test_extension_system.cc index 0f513e4..d174257 100644 --- a/chrome/browser/extensions/test_extension_system.cc +++ b/chrome/browser/extensions/test_extension_system.cc @@ -89,9 +89,9 @@ ExtensionService* TestExtensionSystem::CreateExtensionService( state_store_.reset(new StateStore(profile_, value_store.Pass())); blacklist_.reset(new Blacklist(ExtensionPrefs::Get(profile_))); management_policy_.reset(new ManagementPolicy()); - management_policy_->RegisterProvider( + management_policy_->RegisterProviders( ExtensionManagementFactory::GetForBrowserContext(profile_) - ->GetProvider()); + ->GetProviders()); runtime_data_.reset(new RuntimeData(ExtensionRegistry::Get(profile_))); extension_service_.reset(new ExtensionService(profile_, command_line, diff --git a/chrome/chrome_browser_extensions.gypi b/chrome/chrome_browser_extensions.gypi index c4b73f5..fd1035e 100644 --- a/chrome/chrome_browser_extensions.gypi +++ b/chrome/chrome_browser_extensions.gypi @@ -734,6 +734,8 @@ 'browser/extensions/pending_extension_info.h', 'browser/extensions/pending_extension_manager.cc', 'browser/extensions/pending_extension_manager.h', + 'browser/extensions/permissions_based_management_policy_provider.cc', + 'browser/extensions/permissions_based_management_policy_provider.h', 'browser/extensions/permissions_updater.cc', 'browser/extensions/permissions_updater.h', 'browser/extensions/plugin_manager.cc', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index a2144f9..df19165 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -377,6 +377,8 @@ 'browser/extensions/extension_install_ui_browsertest.cc', 'browser/extensions/extension_javascript_url_apitest.cc', 'browser/extensions/extension_loading_browsertest.cc', + 'browser/extensions/extension_management_test_util.cc', + 'browser/extensions/extension_management_test_util.h', 'browser/extensions/extension_messages_apitest.cc', 'browser/extensions/extension_override_apitest.cc', 'browser/extensions/extension_resource_request_policy_apitest.cc', diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi index 7098c86..642808f 100644 --- a/chrome/chrome_tests_unit.gypi +++ b/chrome/chrome_tests_unit.gypi @@ -423,6 +423,7 @@ 'browser/extensions/menu_manager_unittest.cc', 'browser/extensions/pack_extension_unittest.cc', 'browser/extensions/permission_messages_unittest.cc', + 'browser/extensions/permissions_based_management_policy_provider_unittest.cc', 'browser/extensions/permissions_updater_unittest.cc', 'browser/extensions/policy_handlers_unittest.cc', 'browser/extensions/sandboxed_unpacker_unittest.cc', diff --git a/chrome/test/data/extensions/api_test/permissions/optional_policy_blocked/background.js b/chrome/test/data/extensions/api_test/permissions/optional_policy_blocked/background.js new file mode 100644 index 0000000..8c02692 --- /dev/null +++ b/chrome/test/data/extensions/api_test/permissions/optional_policy_blocked/background.js @@ -0,0 +1,27 @@ +// 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. + +var assertTrue = chrome.test.assertTrue; +var fail = chrome.test.callbackFail; +var pass = chrome.test.callbackPass; + +var BLOCKED_BY_ENTERPRISE_ERROR = + "Permissions are blocked by enterprise policy."; + +chrome.test.getConfig(function(config) { + + chrome.test.runTests([ + function allowedPermission() { + chrome.permissions.request( + {permissions:['bookmarks']}, + pass(function(granted) { assertTrue(granted); })); + }, + + function allowedPermission() { + chrome.permissions.request( + {permissions:['management']}, + fail(BLOCKED_BY_ENTERPRISE_ERROR)); + } + ]); +}); diff --git a/chrome/test/data/extensions/api_test/permissions/optional_policy_blocked/manifest.json b/chrome/test/data/extensions/api_test/permissions/optional_policy_blocked/manifest.json new file mode 100644 index 0000000..a45a29f --- /dev/null +++ b/chrome/test/data/extensions/api_test/permissions/optional_policy_blocked/manifest.json @@ -0,0 +1,13 @@ +{ + "name": "permissions/optional_policy_blocked", + "description": "permissions/optional_policy_blocked", + "version": "0.1", + "manifest_version": 2, + "background": { + "scripts": ["background.js"] + }, + "optional_permissions": [ + "bookmarks", + "management" + ] +} diff --git a/chrome/test/data/extensions/permissions_blocklist.pem b/chrome/test/data/extensions/permissions_blocklist.pem new file mode 100644 index 0000000..ea0e73b --- /dev/null +++ b/chrome/test/data/extensions/permissions_blocklist.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCqPZEHZSW9LnsZ +QAfO1n0CTCEVeY2V5ydfGUkm3tuJC38NmRUCJkyWVVnD8hTknWarI1viFB2mf7// +T6seCnOPpRd/uzz4yYjgUTSMaRllIcXzWDv7iD4Yg2076zA/TOU6MsLluu+Ur2rq +UMU92yu8Nd3XekygWYmqJW4x7cNqc4VNLwFLc7TmemdAUeDpfOOmyPTPELkNRip7 +rLLRDAEQ/W0XPoPU9GCgyaY0DMw+/rcUicUTlo+fQKig8+vOZPNkuAenYAoeeZsT +QlMvC8EXvf5lGmUlKN6iQT6n60YPZPAOF4tnRL4rMHnlQfYqcWLE2fyY9xIKIZ+S +88l/qD9HAgMBAAECggEAYv+Aehtw0c3Y2fspTzfd5FsfFhzkoBqXA69xocARTGW6 +Ec9Gr0RUX0vs8oyxZ6y8yqK6SQNHps49eZv7t3F+eDcaLjZVI5wXE9NFSq3Hjc8R +DQzvfLti2OB8wk5ndTgjSS/BtNR7hCxqcS5dNTZh/YYZprfBHbJT4vT3WuOYMytd +VrKeil6seZiNlPjEGLWy6ioAUysenko21qnp5mu33GTtg8IQG8J9oJga9suuMWzM +s3Ep+pDAvNO196zbiv3O/n6MI7FduNB9wDsILZZGKCnWYg4B2haJVEszc2aHDYJ9 +0Vt7/ZnP+LajyzHnLr8rzDzls3iNelwUOg3K3zbOUQKBgQDc2HmzdqtYtz68ymcl +Z9b3SQESRBcMzx6xwLvS9VFIhQ4dnBlamf+PYj6LWCTQvBopWbauATTwt8MQnBbW +wjFHnDVqzln1ydji9RzyJ6Mk+gi+WvPqJgTPX8xn8sBOY6RbG/Nf+RxIoe2xHBas +GwlrEygzWwSJXr6A344hiCiV6QKBgQDFVurpnfZEQYYck3GIfh97DDiK+d3s6B8D +hbOu+ZAon2jRotHlMVi/Mc8v2dpkw0Ha4Bzehy6ijfXhZuw6Y8PiFU33L6AfZjty +U+RXE5qkM11HG10qfXgRr4buDX4jFYSZ/4v9Gd8mdthJPYbejayPZll2NvP8MV15 +OJzmJ599rwKBgQCV8EdYZ/ZXijoHFWhIRiL1y1P14dLAeJK/XjpnNPUVxnhZ9BRm +pe2TnMEX/CUqrrsYXfegVeNql2jRixgagMfSdaTyudzr+jnNhVs8sVqbjUKIctnt +nBhh7wmpfW/BCPeaf/SRsWDZk078kzR1bvnK/7uoemAvH+s5Ng6ah4X+mQKBgBzA +CQF4LgTLtwo6wPOtzFBU+3fj5104hCwzMnZvjX0dnFvOaN4HTnKsgw7polPDzrhg +plQVRcoQ/J2WLRxkpIkvqWg13BI3BrolVsMI7CK4CQLvDae/F9sbw4CzHoll/Sei +f3akakpkTiZ9fES4R0etFaOPZgR7/K5IfTNa2nWpAoGAI4AKRWL0TOGJQnByzFT1 +5QBjJ8Q9U3X40RYwjDXEJjWtWSoA7HTb7w3gvRyL3ljijqao0BxM0zIRoxLVLZSW +1sOx0YlYhDeaeydbDB/h/YYl4vOi8zOFeCK5p9/S7xxPi/LUtGYLuAluxmKF5/S2 +pTm3N5zLsXqZR9yiSkj8gBE= +-----END PRIVATE KEY----- diff --git a/chrome/test/data/extensions/permissions_blocklist/manifest.json b/chrome/test/data/extensions/permissions_blocklist/manifest.json new file mode 100644 index 0000000..9f7d895 --- /dev/null +++ b/chrome/test/data/extensions/permissions_blocklist/manifest.json @@ -0,0 +1,7 @@ +{ + "name": "Permissions Blocked by Policy Test", + "permissions": [ "tabs", "bookmarks" ], + "optional_permissions": [ "history", "downloads" ], + "manifest_version": 2, + "version": "1.0" +} diff --git a/chrome/test/data/extensions/permissions_blocklist2/manifest.json b/chrome/test/data/extensions/permissions_blocklist2/manifest.json new file mode 100644 index 0000000..01b8403 --- /dev/null +++ b/chrome/test/data/extensions/permissions_blocklist2/manifest.json @@ -0,0 +1,7 @@ +{ + "name": "Permissions Blocked by Policy Test 2", + "permissions": [ "tabs", "bookmarks", "downloads", "management" ], + "optional_permissions": [ "history" ], + "manifest_version": 2, + "version": "2.0" +} diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json index 3ad037d..433468d 100644 --- a/chrome/test/data/policy/policy_test_cases.json +++ b/chrome/test/data/policy/policy_test_cases.json @@ -704,14 +704,17 @@ "test_policy": { "ExtensionSettings": { "abcdefghijklmnopabcdefghijklmnop" : { - "installation_mode": "allowed" + "installation_mode": "allowed", + "blocked_permissions": ["history"] }, "bcdefghijklmnopabcdefghijklmnopa" : { "installation_mode": "force_installed", - "update_url": "http://example.com/update_url" + "update_url": "http://example.com/update_url", + "allowed_permissions": ["downloads"] }, "*": { "installation_mode": "blocked", + "blocked_permissions": ["downloads", "bookmarks"], "install_sources": ["http://company-intranet/chromeapps"], "allowed_types": ["hosted_app"] } diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json index 4beed68..5da8972 100644 --- a/components/policy/resources/policy_templates.json +++ b/components/policy/resources/policy_templates.json @@ -1750,8 +1750,19 @@ 'enum': ['blocked', 'allowed', 'force_installed', 'normal_installed'] }, 'update_url': { 'type': 'string' }, - } - } + 'blocked_permissions': { + 'type': 'array', + 'items': { + 'type': 'string', + 'pattern': '^[a-z][a-zA-Z.]*$', + }, + 'id': 'ListOfPermissions', + }, + 'allowed_permissions': { + '$ref': 'ListOfPermissions', + }, + }, + }, }, 'properties': { '*': { @@ -1761,15 +1772,18 @@ 'type': 'string', 'enum': ['blocked', 'allowed'] }, + 'blocked_permissions': { + '$ref': 'ListOfPermissions', + }, 'install_sources': { '$ref': 'ExtensionInstallSources', }, 'allowed_types': { '$ref': 'ExtensionAllowedTypes', }, - } - } - } + }, + }, + }, }, 'future': True, 'supported_on': ['chrome.*:40-', 'chrome_os:40-'], @@ -1780,13 +1794,16 @@ 'example_value': { 'abcdefghijklmnopabcdefghijklmnop' : { 'installation_mode': 'allowed', + 'blocked_permissions': ['history'], }, 'bcdefghijklmnopabcdefghijklmnopa' : { 'installation_mode': 'force_installed', 'update_url': 'http://example.com/update_url', + 'allowed_permissions': ['downloads'], }, '*': { 'installation_mode': 'blocked', + 'blocked_permissions': ['downloads', 'bookmarks'], 'install_sources': ['http://company-intranet/chromeapps'], 'allowed_types': ['hosted_app'], }, @@ -1809,6 +1826,10 @@ If the mode is set to "force_installed" or "normal_installed" then an "update_url" must be configured too. The update URL should point to an Update Manifest XML document as described at <ph name="LINK_TO_EXTENSION_DOC1">https://developer.chrome.com/extensions/autoupdate</ph>. Note that the update URL set in this policy is only used for the initial installation; subsequent updates of the extension will use the update URL indicated in the extension's manifest. + "blocked_permissions": maps to a list of strings indicating the blocked API permissions for the extension. The permissions names are same as the permission strings declared in manifest of extension as described at <ph name="LINK_TO_EXTENSION_DOC3">https://developer.chrome.com/extensions/declare_permissions</ph>. This setting also can be configured for "*" extension. If the extension requires a permission which is on the blocklist, it will not be allowed to load. If it contains a blocked permission as optional requirement, it will be handled in the normal way, but requesting conflicting permissions will be declined automatically at runtime. + + "allowed_permissions": similar to "blocked_permissions", but instead explicitly allow some permissions which might be blocked by global blocked permission list, thus can not be configured for "*" extension. Note that this setting doesn't give granted permissions to extensions automatically. + The following settings can be used only for the default "*" configuration: "install_sources": Each item in this list is an extension-style match pattern (see https://developer.chrome.com/extensions/match_patterns). Users will be able to easily install items from any URL that matches an item in this list. Both the location of the *.crx file and the page where the download is started from (i.e. the referrer) must be allowed by these patterns. diff --git a/extensions/browser/management_policy.cc b/extensions/browser/management_policy.cc index 156ba64..b09734a 100644 --- a/extensions/browser/management_policy.cc +++ b/extensions/browser/management_policy.cc @@ -61,6 +61,10 @@ void ManagementPolicy::UnregisterProvider(Provider* provider) { providers_.erase(provider); } +void ManagementPolicy::RegisterProviders(std::vector<Provider*> providers) { + providers_.insert(providers.begin(), providers.end()); +} + bool ManagementPolicy::UserMayLoad(const Extension* extension, base::string16* error) const { return ApplyToProviderList( diff --git a/extensions/browser/management_policy.h b/extensions/browser/management_policy.h index a857822..09b59b1 100644 --- a/extensions/browser/management_policy.h +++ b/extensions/browser/management_policy.h @@ -7,6 +7,7 @@ #include <set> #include <string> +#include <vector> #include "base/basictypes.h" #include "extensions/common/extension.h" @@ -92,6 +93,9 @@ class ManagementPolicy { void RegisterProvider(Provider* provider); void UnregisterProvider(Provider* provider); + // Like RegisterProvider(), but registers multiple providers instead. + void RegisterProviders(std::vector<Provider*> providers); + // Returns true if the user is permitted to install, load, and run the given // extension. If not, |error| may be set to an appropriate message. bool UserMayLoad(const Extension* extension, base::string16* error) const; |