Commit a9a13e17 authored by Owen Min's avatar Owen Min Committed by Commit Bot

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 <zmin@chromium.org>
Reviewed-by: default avatarDevlin <rdevlin.cronin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#604696}
parent b45e1827
...@@ -24,12 +24,15 @@ ...@@ -24,12 +24,15 @@
#include "chrome/browser/extensions/standard_management_policy_provider.h" #include "chrome/browser/extensions/standard_management_policy_provider.h"
#include "chrome/browser/profiles/incognito_helpers.h" #include "chrome/browser/profiles/incognito_helpers.h"
#include "chrome/browser/profiles/profile.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/crx_file/id_util.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h" #include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "components/pref_registry/pref_registry_syncable.h" #include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h" #include "components/prefs/pref_service.h"
#include "extensions/browser/pref_names.h" #include "extensions/browser/pref_names.h"
#include "extensions/common/extension.h" #include "extensions/common/extension.h"
#include "extensions/common/extension_urls.h"
#include "extensions/common/manifest_constants.h" #include "extensions/common/manifest_constants.h"
#include "extensions/common/permissions/api_permission_set.h" #include "extensions/common/permissions/api_permission_set.h"
#include "extensions/common/permissions/permission_set.h" #include "extensions/common/permissions/permission_set.h"
...@@ -65,6 +68,10 @@ ExtensionManagement::ExtensionManagement(Profile* profile) ...@@ -65,6 +68,10 @@ ExtensionManagement::ExtensionManagement(Profile* profile)
pref_change_registrar_.Add(pref_names::kAllowedTypes, pref_change_callback); pref_change_registrar_.Add(pref_names::kAllowedTypes, pref_change_callback);
pref_change_registrar_.Add(pref_names::kExtensionManagement, pref_change_registrar_.Add(pref_names::kExtensionManagement,
pref_change_callback); 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 // Note that both |global_settings_| and |default_settings_| will be null
// before first call to Refresh(), so in order to resolve this, Refresh() must // before first call to Refresh(), so in order to resolve this, Refresh() must
// be called in the initialization of ExtensionManagement. // be called in the initialization of ExtensionManagement.
...@@ -159,7 +166,13 @@ bool ExtensionManagement::IsOffstoreInstallAllowed( ...@@ -159,7 +166,13 @@ bool ExtensionManagement::IsOffstoreInstallAllowed(
} }
bool ExtensionManagement::IsAllowedManifestType( 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) if (!global_settings_->has_restricted_allowed_types)
return true; return true;
const std::vector<Manifest::Type>& allowed_types = const std::vector<Manifest::Type>& allowed_types =
...@@ -169,6 +182,13 @@ bool ExtensionManagement::IsAllowedManifestType( ...@@ -169,6 +182,13 @@ bool ExtensionManagement::IsAllowedManifestType(
APIPermissionSet ExtensionManagement::GetBlockedAPIPermissions( APIPermissionSet ExtensionManagement::GetBlockedAPIPermissions(
const Extension* extension) const { 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. // Fetch per-extension blocked permissions setting.
auto iter_id = settings_by_id_.find(extension->id()); auto iter_id = settings_by_id_.find(extension->id());
...@@ -441,12 +461,14 @@ void ExtensionManagement::Refresh() { ...@@ -441,12 +461,14 @@ void ExtensionManagement::Refresh() {
} }
} }
} }
UpdateForcedCloudReportingExtension();
} }
const base::Value* ExtensionManagement::LoadPreference( const base::Value* ExtensionManagement::LoadPreference(
const char* pref_name, const char* pref_name,
bool force_managed, bool force_managed,
base::Value::Type expected_type) { base::Value::Type expected_type) const {
if (!pref_service_) if (!pref_service_)
return nullptr; return nullptr;
const PrefService::Preference* pref = const PrefService::Preference* pref =
...@@ -507,6 +529,32 @@ void ExtensionManagement::UpdateForcedExtensions( ...@@ -507,6 +529,32 @@ void ExtensionManagement::UpdateForcedExtensions(
} }
} }
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( internal::IndividualSettings* ExtensionManagement::AccessById(
const ExtensionId& id) { const ExtensionId& id) {
DCHECK(crx_file::id_util::IdIsValid(id)) << "Invalid ID: " << id; DCHECK(crx_file::id_util::IdIsValid(id)) << "Invalid ID: " << id;
......
...@@ -108,9 +108,10 @@ class ExtensionManagement : public KeyedService { ...@@ -108,9 +108,10 @@ class ExtensionManagement : public KeyedService {
bool IsOffstoreInstallAllowed(const GURL& url, bool IsOffstoreInstallAllowed(const GURL& url,
const GURL& referrer_url) const; const GURL& referrer_url) const;
// Returns true if an extension with manifest type |manifest_type| is // Returns true if an extension with manifest type |manifest_type| and
// allowed to be installed. // id |extension_id| is allowed to be installed.
bool IsAllowedManifestType(Manifest::Type manifest_type) const; bool IsAllowedManifestType(Manifest::Type manifest_type,
const std::string& extension_id) const;
// Returns the list of blocked API permissions for |extension|. // Returns the list of blocked API permissions for |extension|.
APIPermissionSet GetBlockedAPIPermissions(const Extension* extension) const; APIPermissionSet GetBlockedAPIPermissions(const Extension* extension) const;
...@@ -182,7 +183,7 @@ class ExtensionManagement : public KeyedService { ...@@ -182,7 +183,7 @@ class ExtensionManagement : public KeyedService {
// be loaded from or has the wrong type. // be loaded from or has the wrong type.
const base::Value* LoadPreference(const char* pref_name, const base::Value* LoadPreference(const char* pref_name,
bool force_managed, bool force_managed,
base::Value::Type expected_type); base::Value::Type expected_type) const;
void OnExtensionPrefChanged(); void OnExtensionPrefChanged();
void NotifyExtensionManagementPrefChanged(); void NotifyExtensionManagementPrefChanged();
...@@ -195,6 +196,12 @@ class ExtensionManagement : public KeyedService { ...@@ -195,6 +196,12 @@ class ExtensionManagement : public KeyedService {
// Helper to update |extension_dict| for forced installs. // Helper to update |extension_dict| for forced installs.
void UpdateForcedExtensions(const base::DictionaryValue* extension_dict); 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. // Helper function to access |settings_by_id_| with |id| as key.
// Adds a new IndividualSettings entry to |settings_by_id_| if none exists for // Adds a new IndividualSettings entry to |settings_by_id_| if none exists for
// |id| yet. // |id| yet.
......
...@@ -15,10 +15,13 @@ ...@@ -15,10 +15,13 @@
#include "chrome/browser/extensions/extension_management_test_util.h" #include "chrome/browser/extensions/extension_management_test_util.h"
#include "chrome/browser/extensions/external_policy_loader.h" #include "chrome/browser/extensions/external_policy_loader.h"
#include "chrome/browser/extensions/standard_management_policy_provider.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 "chrome/test/base/testing_profile.h"
#include "components/sync_preferences/testing_pref_service_syncable.h" #include "components/sync_preferences/testing_pref_service_syncable.h"
#include "content/public/test/test_browser_thread_bundle.h" #include "content/public/test/test_browser_thread_bundle.h"
#include "extensions/browser/pref_names.h" #include "extensions/browser/pref_names.h"
#include "extensions/common/extension_urls.h"
#include "extensions/common/manifest.h" #include "extensions/common/manifest.h"
#include "extensions/common/manifest_constants.h" #include "extensions/common/manifest_constants.h"
#include "extensions/common/permissions/api_permission.h" #include "extensions/common/permissions/api_permission.h"
...@@ -89,6 +92,7 @@ const char kExampleDictNoCustomError[] = ...@@ -89,6 +92,7 @@ const char kExampleDictNoCustomError[] =
" \"installation_mode\": \"blocked\"," " \"installation_mode\": \"blocked\","
" }," " },"
"}"; "}";
} // namespace } // namespace
class ExtensionManagementServiceTest : public testing::Test { class ExtensionManagementServiceTest : public testing::Test {
...@@ -188,6 +192,14 @@ class ExtensionManagementServiceTest : public testing::Test { ...@@ -188,6 +192,14 @@ class ExtensionManagementServiceTest : public testing::Test {
return extension_management_->GetPolicyBlockedHosts(extension.get()); 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 // Wrapper of ExtensionManagement::BlockedInstallMessage, |id| is used
// in case the message is extension specific. // in case the message is extension specific.
const std::string GetBlockedInstallMessage(const std::string& id) { const std::string GetBlockedInstallMessage(const std::string& id) {
...@@ -218,13 +230,6 @@ class ExtensionManagementServiceTest : public testing::Test { ...@@ -218,13 +230,6 @@ class ExtensionManagementServiceTest : public testing::Test {
} }
protected: 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 // Create an extension with specified |location|, |version|, |id| and
// |update_url|. // |update_url|.
scoped_refptr<const Extension> CreateExtension( scoped_refptr<const Extension> CreateExtension(
...@@ -244,6 +249,12 @@ class ExtensionManagementServiceTest : public testing::Test { ...@@ -244,6 +249,12 @@ class ExtensionManagementServiceTest : public testing::Test {
CHECK(extension.get()) << error; CHECK(extension.get()) << error;
return extension; 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 { class ExtensionAdminPolicyTest : public ExtensionManagementServiceTest {
...@@ -794,6 +805,124 @@ TEST_F(ExtensionManagementServiceTest, IsInstallationExplicitlyAllowed) { ...@@ -794,6 +805,124 @@ TEST_F(ExtensionManagementServiceTest, IsInstallationExplicitlyAllowed) {
extension_management_->IsInstallationExplicitlyAllowed(not_specified)); 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. // Tests the flag value indicating that extensions are blacklisted by default.
TEST_F(ExtensionAdminPolicyTest, BlacklistedByDefault) { TEST_F(ExtensionAdminPolicyTest, BlacklistedByDefault) {
EXPECT_FALSE(BlacklistedByDefault(NULL)); EXPECT_FALSE(BlacklistedByDefault(NULL));
......
...@@ -118,7 +118,8 @@ bool StandardManagementPolicyProvider::UserMayLoad( ...@@ -118,7 +118,8 @@ bool StandardManagementPolicyProvider::UserMayLoad(
case Manifest::TYPE_LEGACY_PACKAGED_APP: case Manifest::TYPE_LEGACY_PACKAGED_APP:
case Manifest::TYPE_PLATFORM_APP: case Manifest::TYPE_PLATFORM_APP:
case Manifest::TYPE_SHARED_MODULE: { case Manifest::TYPE_SHARED_MODULE: {
if (!settings_->IsAllowedManifestType(extension->GetType())) if (!settings_->IsAllowedManifestType(extension->GetType(),
extension->id()))
return ReturnLoadError(extension, error); return ReturnLoadError(extension, error);
break; break;
} }
......
...@@ -44,6 +44,7 @@ const char kCroshBuiltinAppId[] = "nkoccljplnhpfnfiajclkommnmllphnl"; ...@@ -44,6 +44,7 @@ const char kCroshBuiltinAppId[] = "nkoccljplnhpfnfiajclkommnmllphnl";
const char kTextEditorAppId[] = "mmfbcljfglbokpmkimbfghdkjmjhdgbg"; const char kTextEditorAppId[] = "mmfbcljfglbokpmkimbfghdkjmjhdgbg";
const char kInAppPaymentsSupportAppId[] = "nmmhkkegccagdldgiimedpiccmgmieda"; const char kInAppPaymentsSupportAppId[] = "nmmhkkegccagdldgiimedpiccmgmieda";
const char kMediaRouterStableExtensionId[] = "pkedcjkdefgpdelpbcmbmeomcjbeemfm"; const char kMediaRouterStableExtensionId[] = "pkedcjkdefgpdelpbcmbmeomcjbeemfm";
const char kCloudReportingExtensionId[] = "kigjhoekjcpdfjpimbdjegmgecmlicaf";
#if defined(OS_CHROMEOS) #if defined(OS_CHROMEOS)
const char kAssessmentAssistantExtensionId[] = const char kAssessmentAssistantExtensionId[] =
......
...@@ -105,6 +105,9 @@ extern const char kInAppPaymentsSupportAppId[]; ...@@ -105,6 +105,9 @@ extern const char kInAppPaymentsSupportAppId[];
// The extension id of the stable media router extension. // The extension id of the stable media router extension.
extern const char kMediaRouterStableExtensionId[]; extern const char kMediaRouterStableExtensionId[];
// The extension id of the Chrome Reporting extension.
extern const char kCloudReportingExtensionId[];
// The buckets used for app launches. // The buckets used for app launches.
enum AppLaunchBucket { enum AppLaunchBucket {
// Launch from NTP apps section while maximized. // Launch from NTP apps section while maximized.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment