Implement CloudReportingEnabled policy
The policy force installs Chrome Reporting Extension and overrides any
other extension policies about extensions black list,
blcoked permissions, allowed/blocked runtime hosts,
minimum version requirment, allowed type and update url.
The policy does not override install source policy as it does not affect
force-installed extension.
Bug: 898673
Change-Id: I8f17d2073b200448d268a34dc07ab61ff65753d8
Reviewed-on: https://chromium-review.googlesource.com/c/1306233
Commit-Queue: Owen Min <[email protected]>
Reviewed-by: Devlin <[email protected]>
Cr-Commit-Position: refs/heads/master@{#604696}
diff --git a/chrome/browser/extensions/extension_management.cc b/chrome/browser/extensions/extension_management.cc
index b168e8da8..1eb1235 100644
--- a/chrome/browser/extensions/extension_management.cc
+++ b/chrome/browser/extensions/extension_management.cc
@@ -24,12 +24,15 @@
#include "chrome/browser/extensions/standard_management_policy_provider.h"
#include "chrome/browser/profiles/incognito_helpers.h"
#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "chrome/common/pref_names.h"
#include "components/crx_file/id_util.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h"
#include "extensions/browser/pref_names.h"
#include "extensions/common/extension.h"
+#include "extensions/common/extension_urls.h"
#include "extensions/common/manifest_constants.h"
#include "extensions/common/permissions/api_permission_set.h"
#include "extensions/common/permissions/permission_set.h"
@@ -65,6 +68,10 @@
pref_change_registrar_.Add(pref_names::kAllowedTypes, pref_change_callback);
pref_change_registrar_.Add(pref_names::kExtensionManagement,
pref_change_callback);
+#if !defined(OS_CHROMEOS)
+ pref_change_registrar_.Add(prefs::kCloudReportingEnabled,
+ pref_change_callback);
+#endif
// Note that both |global_settings_| and |default_settings_| will be null
// before first call to Refresh(), so in order to resolve this, Refresh() must
// be called in the initialization of ExtensionManagement.
@@ -159,7 +166,13 @@
}
bool ExtensionManagement::IsAllowedManifestType(
- Manifest::Type manifest_type) const {
+ Manifest::Type manifest_type,
+ const std::string& extension_id) const {
+ if (extension_id == extension_misc::kCloudReportingExtensionId &&
+ IsCloudReportingPolicyEnabled()) {
+ return true;
+ }
+
if (!global_settings_->has_restricted_allowed_types)
return true;
const std::vector<Manifest::Type>& allowed_types =
@@ -169,6 +182,13 @@
APIPermissionSet ExtensionManagement::GetBlockedAPIPermissions(
const Extension* extension) const {
+ // The Chrome Reporting extension is sideloaded via the CloudReportingEnabled
+ // policy and is not subject to permission withholding.
+ if (extension->id() == extension_misc::kCloudReportingExtensionId &&
+ IsCloudReportingPolicyEnabled()) {
+ return APIPermissionSet();
+ }
+
// Fetch per-extension blocked permissions setting.
auto iter_id = settings_by_id_.find(extension->id());
@@ -441,12 +461,14 @@
}
}
}
+
+ UpdateForcedCloudReportingExtension();
}
const base::Value* ExtensionManagement::LoadPreference(
const char* pref_name,
bool force_managed,
- base::Value::Type expected_type) {
+ base::Value::Type expected_type) const {
if (!pref_service_)
return nullptr;
const PrefService::Preference* pref =
@@ -507,6 +529,32 @@
}
}
+void ExtensionManagement::UpdateForcedCloudReportingExtension() {
+ if (!IsCloudReportingPolicyEnabled())
+ return;
+
+ // Adds the Chrome Reporting extension to the force install list if
+ // CloudReportingEnabled policy is set to True. Overrides any existing setting
+ // for that extension from other policies.
+ internal::IndividualSettings* settings =
+ AccessById(extension_misc::kCloudReportingExtensionId);
+ settings->Reset();
+ settings->minimum_version_required.reset();
+ settings->installation_mode = INSTALLATION_FORCED;
+ settings->update_url = extension_urls::kChromeWebstoreUpdateURL;
+}
+
+bool ExtensionManagement::IsCloudReportingPolicyEnabled() const {
+#if !defined(OS_CHROMEOS)
+ const base::Value* policy_value =
+ LoadPreference(prefs::kCloudReportingEnabled,
+ /* force_managed = */ true, base::Value::Type::BOOLEAN);
+ return policy_value && policy_value->GetBool();
+#else
+ return false;
+#endif
+}
+
internal::IndividualSettings* ExtensionManagement::AccessById(
const ExtensionId& id) {
DCHECK(crx_file::id_util::IdIsValid(id)) << "Invalid ID: " << id;
diff --git a/chrome/browser/extensions/extension_management.h b/chrome/browser/extensions/extension_management.h
index 68accd1..7894fd13 100644
--- a/chrome/browser/extensions/extension_management.h
+++ b/chrome/browser/extensions/extension_management.h
@@ -108,9 +108,10 @@
bool IsOffstoreInstallAllowed(const GURL& url,
const GURL& referrer_url) const;
- // Returns true if an extension with manifest type |manifest_type| is
- // allowed to be installed.
- bool IsAllowedManifestType(Manifest::Type manifest_type) const;
+ // Returns true if an extension with manifest type |manifest_type| and
+ // id |extension_id| is allowed to be installed.
+ bool IsAllowedManifestType(Manifest::Type manifest_type,
+ const std::string& extension_id) const;
// Returns the list of blocked API permissions for |extension|.
APIPermissionSet GetBlockedAPIPermissions(const Extension* extension) const;
@@ -182,7 +183,7 @@
// be loaded from or has the wrong type.
const base::Value* LoadPreference(const char* pref_name,
bool force_managed,
- base::Value::Type expected_type);
+ base::Value::Type expected_type) const;
void OnExtensionPrefChanged();
void NotifyExtensionManagementPrefChanged();
@@ -195,6 +196,12 @@
// Helper to update |extension_dict| for forced installs.
void UpdateForcedExtensions(const base::DictionaryValue* extension_dict);
+ // Helper to update |settings_by_id_| for forced cloud reporting extension.
+ void UpdateForcedCloudReportingExtension();
+
+ // Returns true if cloud reporting policy is enabled.
+ bool IsCloudReportingPolicyEnabled() const;
+
// Helper function to access |settings_by_id_| with |id| as key.
// Adds a new IndividualSettings entry to |settings_by_id_| if none exists for
// |id| yet.
diff --git a/chrome/browser/extensions/extension_management_unittest.cc b/chrome/browser/extensions/extension_management_unittest.cc
index 9e1efd134..2b3c4e0 100644
--- a/chrome/browser/extensions/extension_management_unittest.cc
+++ b/chrome/browser/extensions/extension_management_unittest.cc
@@ -15,10 +15,13 @@
#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 "chrome/common/extensions/extension_constants.h"
+#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_profile.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "extensions/browser/pref_names.h"
+#include "extensions/common/extension_urls.h"
#include "extensions/common/manifest.h"
#include "extensions/common/manifest_constants.h"
#include "extensions/common/permissions/api_permission.h"
@@ -89,6 +92,7 @@
" \"installation_mode\": \"blocked\","
" },"
"}";
+
} // namespace
class ExtensionManagementServiceTest : public testing::Test {
@@ -188,6 +192,14 @@
return extension_management_->GetPolicyBlockedHosts(extension.get());
}
+ // Wrapper of ExtensionManagement::GetPolicyAllowedHosts, |id| is used
+ // to construct an Extension for testing.
+ URLPatternSet GetPolicyAllowedHosts(const std::string& id) {
+ scoped_refptr<const Extension> extension =
+ CreateExtension(Manifest::UNPACKED, "0.1", id, kNonExistingUpdateUrl);
+ return extension_management_->GetPolicyAllowedHosts(extension.get());
+ }
+
// Wrapper of ExtensionManagement::BlockedInstallMessage, |id| is used
// in case the message is extension specific.
const std::string GetBlockedInstallMessage(const std::string& id) {
@@ -218,13 +230,6 @@
}
protected:
- content::TestBrowserThreadBundle test_browser_thread_bundle_;
-
- std::unique_ptr<TestingProfile> profile_;
- sync_preferences::TestingPrefServiceSyncable* pref_service_;
- std::unique_ptr<ExtensionManagement> extension_management_;
-
- private:
// Create an extension with specified |location|, |version|, |id| and
// |update_url|.
scoped_refptr<const Extension> CreateExtension(
@@ -244,6 +249,12 @@
CHECK(extension.get()) << error;
return extension;
}
+
+ content::TestBrowserThreadBundle test_browser_thread_bundle_;
+
+ std::unique_ptr<TestingProfile> profile_;
+ sync_preferences::TestingPrefServiceSyncable* pref_service_;
+ std::unique_ptr<ExtensionManagement> extension_management_;
};
class ExtensionAdminPolicyTest : public ExtensionManagementServiceTest {
@@ -794,6 +805,124 @@
extension_management_->IsInstallationExplicitlyAllowed(not_specified));
}
+#if !defined(OS_CHROMEOS)
+TEST_F(ExtensionManagementServiceTest, CloudReportingEnabledPolicy) {
+ // Enables the policy put the extension into forced list.
+ SetPref(true, prefs::kCloudReportingEnabled,
+ std::make_unique<base::Value>(true));
+ CheckAutomaticallyInstalledUpdateUrl(
+ extension_misc::kCloudReportingExtensionId,
+ extension_urls::kChromeWebstoreUpdateURL);
+ EXPECT_EQ(
+ ExtensionManagement::INSTALLATION_FORCED,
+ GetInstallationModeById(extension_misc::kCloudReportingExtensionId));
+
+ // Disabling the policy should remove the extension from the forced list.
+ RemovePref(true, prefs::kCloudReportingEnabled);
+ EXPECT_EQ(
+ ExtensionManagement::INSTALLATION_ALLOWED,
+ GetInstallationModeById(extension_misc::kCloudReportingExtensionId));
+
+ // Recommended policy does not force install the policy.
+ pref_service_->SetRecommendedPref(prefs::kCloudReportingEnabled,
+ std::make_unique<base::Value>(true));
+ EXPECT_EQ(
+ ExtensionManagement::INSTALLATION_ALLOWED,
+ GetInstallationModeById(extension_misc::kCloudReportingExtensionId));
+}
+
+TEST_F(ExtensionManagementServiceTest,
+ CloudReportingEnabledPolicyOverridesBlacklist) {
+ base::ListValue denied_list_pref;
+ denied_list_pref.AppendString(extension_misc::kCloudReportingExtensionId);
+ SetPref(true, pref_names::kInstallDenyList,
+ denied_list_pref.CreateDeepCopy());
+ EXPECT_EQ(
+ ExtensionManagement::INSTALLATION_BLOCKED,
+ GetInstallationModeById(extension_misc::kCloudReportingExtensionId));
+ SetPref(true, prefs::kCloudReportingEnabled,
+ std::make_unique<base::Value>(true));
+ EXPECT_EQ(
+ ExtensionManagement::INSTALLATION_FORCED,
+ GetInstallationModeById(extension_misc::kCloudReportingExtensionId));
+}
+
+TEST_F(ExtensionManagementServiceTest,
+ CloudReportingEnabledPolicyOverridesAllowedTypes) {
+ scoped_refptr<const Extension> extension =
+ CreateExtension(Manifest::EXTERNAL_POLICY, "1.0",
+ extension_misc::kCloudReportingExtensionId,
+ extension_urls::kChromeWebstoreUpdateURL);
+ StandardManagementPolicyProvider provider(extension_management_.get());
+
+ base::ListValue allowed_type_pref;
+ base::string16 error;
+ allowed_type_pref.AppendInteger(Manifest::TYPE_THEME);
+ SetPref(true, pref_names::kAllowedTypes, allowed_type_pref.CreateDeepCopy());
+ EXPECT_FALSE(provider.UserMayLoad(extension.get(), &error));
+
+ SetPref(true, prefs::kCloudReportingEnabled,
+ std::make_unique<base::Value>(true));
+
+ EXPECT_TRUE(provider.UserMayLoad(extension.get(), nullptr));
+}
+
+TEST_F(ExtensionManagementServiceTest,
+ CloudReportingenabledOverridesExtensionSettings) {
+ SetExampleDictPref(R"({
+ "kigjhoekjcpdfjpimbdjegmgecmlicaf": {
+ "installation_mode": "allowed",
+ "blocked_permissions": ["bookmarks"],
+ "minimum_version_required": "100.0",
+ "runtime_blocked_hosts": ["https://a.com"],
+ "runtime_allowed_hosts": ["https://b.com"],
+ "update_url": "http://example.com/update_url",
+ },
+ "update_url:https://clients2.google.com/service/update2/crx": {
+ "blocked_permissions": ["downloads"],
+ },
+ "update_url:http://example.com/update_url": {
+ "blocked_permissions": ["downloads"],
+ }
+ })");
+
+ EXPECT_EQ(
+ ExtensionManagement::INSTALLATION_ALLOWED,
+ GetInstallationModeById(extension_misc::kCloudReportingExtensionId));
+ EXPECT_EQ(2u,
+ GetBlockedAPIPermissions(extension_misc::kCloudReportingExtensionId,
+ extension_urls::kChromeWebstoreUpdateURL)
+ .size());
+ EXPECT_FALSE(
+ CheckMinimumVersion(extension_misc::kCloudReportingExtensionId, "99.0"));
+ EXPECT_EQ(
+ 1u,
+ GetPolicyBlockedHosts(extension_misc::kCloudReportingExtensionId).size());
+ EXPECT_EQ(
+ 1u,
+ GetPolicyAllowedHosts(extension_misc::kCloudReportingExtensionId).size());
+
+ SetPref(true, prefs::kCloudReportingEnabled,
+ std::make_unique<base::Value>(true));
+ CheckAutomaticallyInstalledUpdateUrl(
+ extension_misc::kCloudReportingExtensionId,
+ extension_urls::kChromeWebstoreUpdateURL);
+ EXPECT_EQ(
+ ExtensionManagement::INSTALLATION_FORCED,
+ GetInstallationModeById(extension_misc::kCloudReportingExtensionId));
+ EXPECT_TRUE(
+ GetBlockedAPIPermissions(extension_misc::kCloudReportingExtensionId,
+ extension_urls::kChromeWebstoreUpdateURL)
+ .empty());
+ EXPECT_TRUE(
+ CheckMinimumVersion(extension_misc::kCloudReportingExtensionId, "99.0"));
+ EXPECT_TRUE(GetPolicyBlockedHosts(extension_misc::kCloudReportingExtensionId)
+ .is_empty());
+ EXPECT_TRUE(GetPolicyAllowedHosts(extension_misc::kCloudReportingExtensionId)
+ .is_empty());
+}
+#endif
+
// Tests the flag value indicating that extensions are blacklisted by default.
TEST_F(ExtensionAdminPolicyTest, BlacklistedByDefault) {
EXPECT_FALSE(BlacklistedByDefault(NULL));
diff --git a/chrome/browser/extensions/standard_management_policy_provider.cc b/chrome/browser/extensions/standard_management_policy_provider.cc
index 4e09531..ba136d4 100644
--- a/chrome/browser/extensions/standard_management_policy_provider.cc
+++ b/chrome/browser/extensions/standard_management_policy_provider.cc
@@ -118,7 +118,8 @@
case Manifest::TYPE_LEGACY_PACKAGED_APP:
case Manifest::TYPE_PLATFORM_APP:
case Manifest::TYPE_SHARED_MODULE: {
- if (!settings_->IsAllowedManifestType(extension->GetType()))
+ if (!settings_->IsAllowedManifestType(extension->GetType(),
+ extension->id()))
return ReturnLoadError(extension, error);
break;
}
diff --git a/chrome/common/extensions/extension_constants.cc b/chrome/common/extensions/extension_constants.cc
index 9c7c828..65afef42 100644
--- a/chrome/common/extensions/extension_constants.cc
+++ b/chrome/common/extensions/extension_constants.cc
@@ -44,6 +44,7 @@
const char kTextEditorAppId[] = "mmfbcljfglbokpmkimbfghdkjmjhdgbg";
const char kInAppPaymentsSupportAppId[] = "nmmhkkegccagdldgiimedpiccmgmieda";
const char kMediaRouterStableExtensionId[] = "pkedcjkdefgpdelpbcmbmeomcjbeemfm";
+const char kCloudReportingExtensionId[] = "kigjhoekjcpdfjpimbdjegmgecmlicaf";
#if defined(OS_CHROMEOS)
const char kAssessmentAssistantExtensionId[] =
diff --git a/chrome/common/extensions/extension_constants.h b/chrome/common/extensions/extension_constants.h
index b5dec13..fbedb89 100644
--- a/chrome/common/extensions/extension_constants.h
+++ b/chrome/common/extensions/extension_constants.h
@@ -105,6 +105,9 @@
// The extension id of the stable media router extension.
extern const char kMediaRouterStableExtensionId[];
+// The extension id of the Chrome Reporting extension.
+extern const char kCloudReportingExtensionId[];
+
// The buckets used for app launches.
enum AppLaunchBucket {
// Launch from NTP apps section while maximized.