summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/extensions/api/permissions/permissions_api.cc12
-rw-r--r--chrome/browser/extensions/api/permissions/permissions_apitest.cc38
-rw-r--r--chrome/browser/extensions/extension_management.cc47
-rw-r--r--chrome/browser/extensions/extension_management.h29
-rw-r--r--chrome/browser/extensions/extension_management_constants.cc3
-rw-r--r--chrome/browser/extensions/extension_management_constants.h3
-rw-r--r--chrome/browser/extensions/extension_management_internal.cc52
-rw-r--r--chrome/browser/extensions/extension_management_internal.h14
-rw-r--r--chrome/browser/extensions/extension_management_test_util.cc119
-rw-r--r--chrome/browser/extensions/extension_management_test_util.h49
-rw-r--r--chrome/browser/extensions/extension_management_unittest.cc59
-rw-r--r--chrome/browser/extensions/extension_service.cc17
-rw-r--r--chrome/browser/extensions/extension_service_unittest.cc226
-rw-r--r--chrome/browser/extensions/extension_system_impl.cc4
-rw-r--r--chrome/browser/extensions/permissions_based_management_policy_provider.cc61
-rw-r--r--chrome/browser/extensions/permissions_based_management_policy_provider.h41
-rw-r--r--chrome/browser/extensions/permissions_based_management_policy_provider_unittest.cc169
-rw-r--r--chrome/browser/extensions/test_extension_system.cc4
-rw-r--r--chrome/chrome_browser_extensions.gypi2
-rw-r--r--chrome/chrome_tests.gypi2
-rw-r--r--chrome/chrome_tests_unit.gypi1
-rw-r--r--chrome/test/data/extensions/api_test/permissions/optional_policy_blocked/background.js27
-rw-r--r--chrome/test/data/extensions/api_test/permissions/optional_policy_blocked/manifest.json13
-rw-r--r--chrome/test/data/extensions/permissions_blocklist.pem28
-rw-r--r--chrome/test/data/extensions/permissions_blocklist/manifest.json7
-rw-r--r--chrome/test/data/extensions/permissions_blocklist2/manifest.json7
-rw-r--r--chrome/test/data/policy/policy_test_cases.json7
-rw-r--r--components/policy/resources/policy_templates.json31
-rw-r--r--extensions/browser/management_policy.cc4
-rw-r--r--extensions/browser/management_policy.h4
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;