| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <string> |
| #include <tuple> |
| |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/metrics/user_action_tester.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "chrome/browser/extensions/api/webstore_private/webstore_private_api.h" |
| #include "chrome/browser/extensions/extension_service.h" |
| #include "chrome/browser/extensions/extension_service_test_with_install.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/supervised_user/supervised_user_extensions_delegate_impl.h" |
| #include "chrome/browser/supervised_user/supervised_user_extensions_metrics_recorder.h" |
| #include "chrome/browser/supervised_user/supervised_user_test_util.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/test/base/testing_profile.h" |
| #include "components/supervised_user/core/common/features.h" |
| #include "components/supervised_user/core/common/pref_names.h" |
| #include "extensions/browser/api_test_utils.h" |
| #include "extensions/browser/disable_reason.h" |
| #include "extensions/browser/extension_prefs.h" |
| #include "extensions/browser/extension_registrar.h" |
| #include "extensions/browser/extension_registry.h" |
| #include "extensions/browser/supervised_user_extensions_delegate.h" |
| #include "extensions/common/extension_builder.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using extensions::api_test_utils::RunFunctionAndReturnSingleResult; |
| |
| namespace { |
| const char good_crx[] = "ldnnhddmnhbkjipkidpdiheffobcpfmf"; |
| const char autoupdate[] = "ogjcoiohnmldgjemafoockdghcjciccf"; |
| const char permissions_increase[] = "pgdpcfcocojkjfbgpiianjngphoopgmo"; |
| } // namespace |
| |
| namespace extensions { |
| |
| // Base class for the extension parental controls tests for supervised users. |
| class SupervisedUserExtensionTestBase : public ExtensionServiceTestWithInstall { |
| public: |
| void InitServices(bool profile_is_supervised) { |
| ExtensionServiceInitParams params; |
| params.profile_is_supervised = profile_is_supervised; |
| InitializeExtensionService(std::move(params)); |
| CreateExtensionManager(); |
| } |
| |
| void CreateExtensionManager() { |
| supervised_user_extensions_delegate_ = |
| std::make_unique<SupervisedUserExtensionsDelegateImpl>(profile()); |
| } |
| |
| ExtensionServiceTestWithInstall::InstallState GetDefaultInstalledState() { |
| // Extensions will be installed but disabled until custodian approvals |
| // are performed in the default extensions parental control mode. |
| return INSTALL_WITHOUT_LOAD; |
| } |
| |
| const Extension* InstallPermissionsTestExtension( |
| ExtensionServiceTestWithInstall::InstallState install_state) { |
| const Extension* extension = InstallTestExtension( |
| permissions_increase, dir_path("1"), pem_path(), install_state); |
| return extension; |
| } |
| |
| void UpdatePermissionsTestExtension(const std::string& id, |
| const std::string& version, |
| UpdateState expected_state) { |
| UpdateTestExtension(dir_path(version), pem_path(), id, version, |
| expected_state); |
| } |
| |
| const Extension* InstallNoPermissionsTestExtension( |
| ExtensionServiceTestWithInstall::InstallState install_state) { |
| base::FilePath base_path = data_dir().AppendASCII("autoupdate"); |
| base::FilePath pem_path = base_path.AppendASCII("key.pem"); |
| base::FilePath dir_path = base_path.AppendASCII("v1"); |
| |
| return InstallTestExtension(autoupdate, dir_path, pem_path, install_state); |
| } |
| |
| void UpdateNoPermissionsTestExtension(const std::string& id, |
| const std::string& version, |
| UpdateState expected_state) { |
| base::FilePath base_path = data_dir().AppendASCII("autoupdate"); |
| base::FilePath pem_path = base_path.AppendASCII("key.pem"); |
| base::FilePath dir_path = base_path.AppendASCII("v" + version); |
| |
| UpdateTestExtension(dir_path, pem_path, id, version, expected_state); |
| } |
| |
| void UpdateTestExtension(const base::FilePath& dir_path, |
| const base::FilePath& pem_path, |
| const std::string& id, |
| const std::string& version, |
| const UpdateState& expected_state) { |
| PackCRXAndUpdateExtension(id, dir_path, pem_path, expected_state); |
| const Extension* extension = registry()->GetInstalledExtension(id); |
| ASSERT_TRUE(extension); |
| // The version should have been updated. |
| EXPECT_EQ(base::Version(version), extension->version()); |
| } |
| |
| const Extension* CheckEnabled(const std::string& extension_id) { |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(extension_id)); |
| EXPECT_FALSE(IsPendingCustodianApproval(extension_id)); |
| ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(profile()); |
| EXPECT_TRUE(extension_prefs->GetDisableReasons(extension_id).empty()); |
| return registry()->enabled_extensions().GetByID(extension_id); |
| } |
| |
| const Extension* CheckDisabledForCustodianApproval( |
| const std::string& extension_id) { |
| EXPECT_TRUE(registry()->disabled_extensions().Contains(extension_id)); |
| EXPECT_TRUE(IsPendingCustodianApproval(extension_id)); |
| ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(profile()); |
| EXPECT_TRUE(extension_prefs->HasDisableReason( |
| extension_id, disable_reason::DISABLE_CUSTODIAN_APPROVAL_REQUIRED)); |
| return registry()->disabled_extensions().GetByID(extension_id); |
| } |
| |
| const Extension* CheckDisabledForPermissionsIncrease( |
| const std::string& extension_id) { |
| EXPECT_TRUE(registry()->disabled_extensions().Contains(extension_id)); |
| EXPECT_TRUE(IsPendingCustodianApproval(extension_id)); |
| ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(profile()); |
| EXPECT_TRUE(extension_prefs->HasDisableReason( |
| extension_id, disable_reason::DISABLE_PERMISSIONS_INCREASE)); |
| return registry()->disabled_extensions().GetByID(extension_id); |
| } |
| |
| SupervisedUserExtensionsDelegate* supervised_user_extensions_delegate() { |
| return supervised_user_extensions_delegate_.get(); |
| } |
| |
| const Extension* InstallTestExtension( |
| const std::string& id, |
| const base::FilePath& dir_path, |
| const base::FilePath& pem_path, |
| ExtensionServiceTestWithInstall::InstallState install_state) { |
| const Extension* extension = |
| PackAndInstallCRX(dir_path, pem_path, install_state); |
| // The extension must now be installed. |
| EXPECT_TRUE(extension); |
| EXPECT_EQ(extension->id(), id); |
| if (install_state != INSTALL_NEW) { |
| CheckDisabledForCustodianApproval(id); |
| } else { |
| CheckEnabled(id); |
| } |
| EXPECT_EQ(base::Version("1"), extension->version()); |
| return extension; |
| } |
| |
| void SetDefaultParentalControlSettings() { |
| supervised_user_test_util:: |
| SetSupervisedUserExtensionsMayRequestPermissionsPref(profile(), true); |
| } |
| |
| private: |
| // Returns true if the extension has disable reason permissions_increase or |
| // custodian_approval_required. Tests the Webstore Private Api. |
| bool IsPendingCustodianApproval(const std::string& extension_id) { |
| auto function = base::MakeRefCounted< |
| WebstorePrivateIsPendingCustodianApprovalFunction>(); |
| |
| std::optional<base::Value> result(RunFunctionAndReturnSingleResult( |
| function.get(), "[\"" + extension_id + "\"]", browser_context())); |
| return result->GetBool(); |
| } |
| |
| base::FilePath base_path() const { |
| return data_dir().AppendASCII("permissions_increase"); |
| } |
| base::FilePath dir_path(const std::string& version) const { |
| return base_path().AppendASCII("v" + version); |
| } |
| base::FilePath pem_path() const { |
| return base_path().AppendASCII("permissions.pem"); |
| } |
| |
| std::unique_ptr<SupervisedUserExtensionsDelegateImpl> |
| supervised_user_extensions_delegate_; |
| }; |
| |
| class SupervisedUserExtensionTest : public SupervisedUserExtensionTestBase {}; |
| |
| // Tests that regular users are not affecting supervised user UMA metrics. |
| TEST_F(SupervisedUserExtensionTest, |
| RegularUsersNotAffectingSupervisedUserMetrics) { |
| InitServices(/*profile_is_supervised=*/false); |
| |
| base::HistogramTester histogram_tester; |
| |
| base::FilePath path = data_dir().AppendASCII("good.crx"); |
| // The extensions will be installed and enabled. |
| const Extension* extension = InstallCRX(path, INSTALL_NEW); |
| ASSERT_TRUE(extension); |
| |
| supervised_user_extensions_delegate()->AddExtensionApproval(*extension); |
| |
| histogram_tester.ExpectTotalCount( |
| SupervisedUserExtensionsMetricsRecorder::kExtensionsHistogramName, 0); |
| |
| supervised_user_extensions_delegate()->RemoveExtensionApproval(*extension); |
| |
| histogram_tester.ExpectTotalCount( |
| SupervisedUserExtensionsMetricsRecorder::kExtensionsHistogramName, 0); |
| } |
| |
| // Tests that simulating custodian approval for regular users doesn't cause any |
| // unexpected behavior. |
| TEST_F(SupervisedUserExtensionTest, |
| CustodianApprovalDoesNotAffectRegularUsers) { |
| InitServices(/*profile_is_supervised=*/false); |
| supervised_user_test_util:: |
| SetSupervisedUserExtensionsMayRequestPermissionsPref(profile(), false); |
| supervised_user_test_util::SetSkipParentApprovalToInstallExtensionsPref( |
| profile(), false); |
| |
| // Install an extension, it should be enabled because this is a regular user. |
| base::FilePath path = data_dir().AppendASCII("good.crx"); |
| const Extension* extension = InstallCRX(path, INSTALL_NEW); |
| std::string id = extension->id(); |
| |
| CheckEnabled(id); |
| |
| // Simulate custodian approval and removal. |
| supervised_user_extensions_delegate()->AddExtensionApproval(*extension); |
| supervised_user_extensions_delegate()->RemoveExtensionApproval(*extension); |
| // The extension should still be enabled. |
| CheckEnabled(id); |
| } |
| |
| // Tests that adding supervision to a regular account (Gellerization) |
| // disables previously installed extensions. |
| TEST_F(SupervisedUserExtensionTest, ExtensionsStateAfterGellerization) { |
| InitServices(/*profile_is_supervised=*/false); |
| SetDefaultParentalControlSettings(); |
| |
| // Install an extension, it should be enabled because this is a regular user. |
| base::FilePath path = data_dir().AppendASCII("good.crx"); |
| const Extension* extension = InstallCRX(path, INSTALL_NEW); |
| ASSERT_TRUE(extension); |
| std::string id = extension->id(); |
| CheckEnabled(id); |
| |
| // Now make the profile supervised. |
| profile()->AsTestingProfile()->SetIsSupervisedProfile(); |
| |
| // The extension should be disabled now pending custodian approval. |
| CheckDisabledForCustodianApproval(id); |
| |
| // Grant parent approval. |
| supervised_user_extensions_delegate()->AddExtensionApproval(*extension); |
| |
| // The extension should be enabled now. |
| CheckEnabled(id); |
| |
| // Remove parent approval. |
| supervised_user_extensions_delegate()->RemoveExtensionApproval(*extension); |
| |
| // The extension should be disabled again now. |
| CheckDisabledForCustodianApproval(id); |
| } |
| |
| // Tests that extensions that are disabled pending parent approval |
| // for supervised users, become re-enabled if the user becomes unsupervised. |
| TEST_F(SupervisedUserExtensionTest, ExtensionsStateAfterGraduation) { |
| InitServices(/*profile_is_supervised=*/true); |
| SetDefaultParentalControlSettings(); |
| |
| // The extensions will be installed, but will be disabled until the custodian |
| // approval. |
| auto install_state = INSTALL_WITHOUT_LOAD; |
| base::FilePath path = data_dir().AppendASCII("good.crx"); |
| const Extension* extension = InstallCRX(path, install_state); |
| ASSERT_TRUE(extension); |
| std::string id = extension->id(); |
| // This extension is a supervised user initiated install and should remain |
| // disabled. |
| CheckDisabledForCustodianApproval(id); |
| |
| // Make the profile un-supervised. |
| profile()->AsTestingProfile()->SetIsSupervisedProfile(false); |
| |
| // The extension should become enabled. |
| CheckEnabled(id); |
| } |
| |
| // Tests that a child user is allowed to install extensions under the default |
| // value of the Family Link "Extensions" toggles. |
| // The newly-installed extensions are disabled until approved by the parent. |
| TEST_F(SupervisedUserExtensionTest, InstallAllowedForSupervisedUser) { |
| InitServices(/*profile_is_supervised=*/true); |
| SetDefaultParentalControlSettings(); |
| |
| auto install_state = GetDefaultInstalledState(); |
| base::FilePath path = data_dir().AppendASCII("good.crx"); |
| const Extension* extension = InstallCRX(path, install_state); |
| ASSERT_TRUE(extension); |
| std::string id = extension->id(); |
| |
| // This extension is a supervised user initiated install and should remain |
| // disabled. |
| CheckDisabledForCustodianApproval(id); |
| |
| // Grant parent approval. |
| supervised_user_extensions_delegate()->AddExtensionApproval(*extension); |
| // The extension is now enabled. |
| CheckEnabled(id); |
| |
| // Remove parent approval. |
| supervised_user_extensions_delegate()->RemoveExtensionApproval(*extension); |
| |
| // The extension should be disabled again now. |
| CheckDisabledForCustodianApproval(id); |
| } |
| |
| // Tests that supervised users may approve permission updates without parent |
| // approval under the default values of the Family Link "Extensions" toggle. |
| TEST_F(SupervisedUserExtensionTest, UpdateWithPermissionsIncrease) { |
| InitServices(true); |
| SetDefaultParentalControlSettings(); |
| |
| // Preconditions. |
| base::HistogramTester histogram_tester; |
| histogram_tester.ExpectTotalCount( |
| SupervisedUserExtensionsMetricsRecorder::kExtensionsHistogramName, 0); |
| base::UserActionTester user_action_tester; |
| EXPECT_EQ( |
| 0, |
| user_action_tester.GetActionCount( |
| SupervisedUserExtensionsMetricsRecorder::kApprovalGrantedActionName)); |
| EXPECT_EQ( |
| 0, |
| user_action_tester.GetActionCount( |
| SupervisedUserExtensionsMetricsRecorder::kApprovalRemovedActionName)); |
| |
| std::string id = |
| InstallPermissionsTestExtension(GetDefaultInstalledState())->id(); |
| // Simulate parent approval. |
| supervised_user_extensions_delegate()->AddExtensionApproval( |
| *registry()->GetInstalledExtension(id)); |
| |
| // Should see 1 kApprovalGranted metric count. |
| histogram_tester.ExpectUniqueSample( |
| SupervisedUserExtensionsMetricsRecorder::kExtensionsHistogramName, |
| SupervisedUserExtensionsMetricsRecorder::UmaExtensionState:: |
| kApprovalGranted, |
| 1); |
| histogram_tester.ExpectTotalCount( |
| SupervisedUserExtensionsMetricsRecorder::kExtensionsHistogramName, 1); |
| EXPECT_EQ(1, user_action_tester.GetActionCount( |
| SupervisedUserExtensionsMetricsRecorder:: |
| kApprovalGrantedActionName)); |
| // The extension should be enabled. |
| CheckEnabled(id); |
| |
| // Update to a new version with increased permissions. |
| UpdatePermissionsTestExtension(id, "2", DISABLED); |
| const Extension* extension = CheckDisabledForPermissionsIncrease(id); |
| ASSERT_TRUE(extension); |
| |
| // Simulate supervised user approving the extension without further parent |
| // approval. |
| registrar()->GrantPermissionsAndEnableExtension(*extension); |
| |
| // The extension should be enabled. |
| CheckEnabled(id); |
| |
| // Remove extension approval. |
| supervised_user_extensions_delegate()->RemoveExtensionApproval( |
| *registry()->GetInstalledExtension(id)); |
| |
| // Should see 1 kApprovalRemoved metric count. |
| histogram_tester.ExpectBucketCount( |
| SupervisedUserExtensionsMetricsRecorder::kExtensionsHistogramName, |
| SupervisedUserExtensionsMetricsRecorder::UmaExtensionState:: |
| kApprovalRemoved, |
| 1); |
| histogram_tester.ExpectTotalCount( |
| SupervisedUserExtensionsMetricsRecorder::kExtensionsHistogramName, 2); |
| EXPECT_EQ(1, user_action_tester.GetActionCount( |
| SupervisedUserExtensionsMetricsRecorder:: |
| kApprovalRemovedActionName)); |
| |
| // The extension should be disabled now. |
| CheckDisabledForCustodianApproval(id); |
| } |
| |
| // Tests that if an approved extension is updated to a newer version that |
| // doesn't require additional permissions, it is still enabled. |
| TEST_F(SupervisedUserExtensionTest, UpdateWithoutPermissionIncrease) { |
| InitServices(/*profile_is_supervised=*/true); |
| SetDefaultParentalControlSettings(); |
| |
| const Extension* extension = |
| InstallNoPermissionsTestExtension(GetDefaultInstalledState()); |
| // Save the id, as the extension object will be destroyed during updating. |
| std::string id = extension->id(); |
| supervised_user_extensions_delegate()->AddExtensionApproval(*extension); |
| // The extension should be enabled now. |
| CheckEnabled(id); |
| |
| // Update to a new version. |
| std::string version2("2"); |
| UpdateNoPermissionsTestExtension(id, version2, ENABLED); |
| |
| // The extension should still be there and enabled. |
| const Extension* extension1 = CheckEnabled(id); |
| ASSERT_TRUE(extension1); |
| // The version should have changed. |
| EXPECT_EQ(base::Version(version2), extension1->version()); |
| |
| supervised_user_test_util:: |
| SetSupervisedUserExtensionsMayRequestPermissionsPref(profile(), false); |
| std::string version3("3"); |
| UpdateNoPermissionsTestExtension(id, version3, ENABLED); |
| |
| // The extension should still be there and enabled. |
| const Extension* extension2 = CheckEnabled(id); |
| ASSERT_TRUE(extension2); |
| // The version should have changed again. |
| EXPECT_EQ(base::Version(version3), extension2->version()); |
| |
| // Check that the approved extension id has been updated in the prefs as |
| // well. Prefs are updated via sync. |
| PrefService* pref_service = profile()->GetPrefs(); |
| ASSERT_TRUE(pref_service); |
| const base::Value::Dict& approved_extensions = |
| pref_service->GetDict(prefs::kSupervisedUserApprovedExtensions); |
| EXPECT_TRUE(approved_extensions.FindString(id)); |
| } |
| |
| // Tests that toggling the "Permissions" switch has no longer effect on the |
| // extensions' installation. |
| TEST_F(SupervisedUserExtensionTest, |
| SupervisedUserCannotInstallExtensionUnderPermissionsToggle) { |
| InitServices(/*profile_is_supervised=*/true); |
| supervised_user_test_util::SetSkipParentApprovalToInstallExtensionsPref( |
| profile(), false); |
| supervised_user_test_util:: |
| SetSupervisedUserExtensionsMayRequestPermissionsPref(profile(), false); |
| |
| base::FilePath path = data_dir().AppendASCII("good.crx"); |
| // Installation is successful. The extension is installed disabled in the |
| // default mode. |
| const Extension* extension = InstallCRX(path, INSTALL_WITHOUT_LOAD); |
| EXPECT_TRUE(extension); |
| CheckDisabledForCustodianApproval(extension->id()); |
| } |
| |
| // Tests that disabling the "Permissions" Family Link toggle |
| // has no effect on regular users. |
| TEST_F(SupervisedUserExtensionTest, RegularUserCanInstallExtension) { |
| InitServices(/*profile_is_supervised=*/false); |
| supervised_user_test_util:: |
| SetSupervisedUserExtensionsMayRequestPermissionsPref(profile(), false); |
| supervised_user_test_util::SetSkipParentApprovalToInstallExtensionsPref( |
| profile(), false); |
| |
| base::FilePath path = data_dir().AppendASCII("good.crx"); |
| // The extension should be installed and enabled. |
| const Extension* extension = InstallCRX(path, INSTALL_NEW); |
| |
| ASSERT_TRUE(extension); |
| CheckEnabled(extension->id()); |
| } |
| |
| // Tests that if the "Permissions" Family Link toggle becomes disabled |
| // previously approved extensions are still enabled. |
| TEST_F(SupervisedUserExtensionTest, |
| PermissionsToggleOffDoesNotAffectAlreadyEnabled) { |
| InitServices(/*profile_is_supervised=*/true); |
| supervised_user_test_util:: |
| SetSupervisedUserExtensionsMayRequestPermissionsPref(profile(), false); |
| supervised_user_test_util::SetSkipParentApprovalToInstallExtensionsPref( |
| profile(), false); |
| |
| // The installation helper function checks that the extension is initially |
| // disabled. |
| const Extension* extension = |
| InstallNoPermissionsTestExtension(GetDefaultInstalledState()); |
| std::string id = extension->id(); |
| |
| // Now approve the extension. |
| supervised_user_extensions_delegate()->AddExtensionApproval(*extension); |
| |
| // The extension should be enabled now. |
| CheckEnabled(id); |
| |
| // Custodian toggles "Permissions for sites, apps and extensions" to false. |
| supervised_user_test_util:: |
| SetSupervisedUserExtensionsMayRequestPermissionsPref(profile(), false); |
| |
| // Already installed and enabled extensions should remain that way. |
| CheckEnabled(id); |
| } |
| |
| // Tests that extensions installed when the "Extensions" Family Link toggle |
| // is "On" are installed enabled and are granted parent approval. |
| TEST_F(SupervisedUserExtensionTest, |
| ExtensionsToggleOnGrantsParentApprovalOnInstallation) { |
| InitServices(/*profile_is_supervised=*/true); |
| // Set the "Extensions" toggle to true, allowing installation without parental |
| // approval. |
| supervised_user_test_util::SetSkipParentApprovalToInstallExtensionsPref( |
| profile(), true); |
| const Extension* extension = InstallNoPermissionsTestExtension(INSTALL_NEW); |
| std::string id = extension->id(); |
| |
| // The extension has already been granted approval on its installation. |
| CheckEnabled(id); |
| } |
| |
| // Tests that installed extensions remain parent-approved and enabled |
| // if the "Extensions" switch is toggled to false. |
| TEST_F(SupervisedUserExtensionTest, |
| ExtensionsToggleOffDoesNotAffectAlreadyEnabled) { |
| InitServices(/*profile_is_supervised=*/true); |
| // Set the "Extensions" toggle to true, allowing installation without parental |
| // approval. |
| supervised_user_test_util::SetSkipParentApprovalToInstallExtensionsPref( |
| profile(), true); |
| |
| // If the Extensions toggle applies, the extension is installed and enabled. |
| const Extension* extension = InstallNoPermissionsTestExtension(INSTALL_NEW); |
| std::string id = extension->id(); |
| |
| // The extension has already been granted approval on its installation. |
| CheckEnabled(id); |
| |
| // Custodian sets the "Extensions" toggle to false. |
| supervised_user_test_util::SetSkipParentApprovalToInstallExtensionsPref( |
| profile(), false); |
| |
| // Already installed and enabled extensions should remain that way. |
| CheckEnabled(id); |
| } |
| |
| // Tests toggling the "Extensions" Family Link switch from false to true grants |
| // parental approval. |
| TEST_F(SupervisedUserExtensionTest, |
| ExtensionsToggleOnGrantsMissingParentalApproval) { |
| InitServices(/*profile_is_supervised=*/true); |
| SetDefaultParentalControlSettings(); |
| |
| const Extension* extension = |
| InstallNoPermissionsTestExtension(GetDefaultInstalledState()); |
| std::string id = extension->id(); |
| |
| CheckDisabledForCustodianApproval(id); |
| // Custodian sets the "Extensions" toggle to True. |
| supervised_user_test_util::SetSkipParentApprovalToInstallExtensionsPref( |
| profile(), true); |
| |
| // The extension has been |
| // granted approval and becomes enabled on toggling the switch. |
| CheckEnabled(id); |
| } |
| |
| // Tests the case when the extension approval arrives through sync before the |
| // extension itself is installed. |
| TEST_F(SupervisedUserExtensionTest, ExtensionApprovalBeforeInstallation) { |
| InitServices(/*profile_is_supervised=*/true); |
| SetDefaultParentalControlSettings(); |
| |
| scoped_refptr<const Extension> extension = |
| ExtensionBuilder(good_crx).SetID(good_crx).SetVersion("0").Build(); |
| // Approve the extension. |
| supervised_user_extensions_delegate()->AddExtensionApproval(*extension); |
| |
| // Now install an extension, it should be enabled upon installation. |
| base::FilePath path = data_dir().AppendASCII("good.crx"); |
| InstallCRX(path, INSTALL_NEW); |
| |
| // Make sure it's enabled. |
| CheckEnabled(good_crx); |
| } |
| |
| // Tests that when the parental controls start applying on Desktop (solution |
| // release), existing extensions remain enabled. On ChromeOS they are disabled. |
| TEST_F(SupervisedUserExtensionTest, |
| ExtensionsOnDesktopRemainEnabledOnSkipParentApprovalRelease) { |
| ExtensionServiceInitParams params; |
| params.profile_is_supervised = true; |
| InitializeExtensionService(std::move(params)); |
| SetDefaultParentalControlSettings(); |
| // Install an extension. It should be enabled as we haven't created the SU |
| // extension manager yet. Treated as a pre-existing extension. |
| base::FilePath path = data_dir().AppendASCII("good.crx"); |
| const Extension* extension = InstallCRX(path, INSTALL_NEW); |
| ASSERT_TRUE(extension); |
| |
| // Make sure it's enabled. |
| CheckEnabled(good_crx); |
| |
| // Create the extensions manager. If the |
| // `SkipParentApprovalToInstallExtensions` feature applies for the first time, |
| // the existing extensions remain enabled on Desktop. |
| CreateExtensionManager(); |
| |
| #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) |
| // On Desktop, before the release of parental controls the extensions would be |
| // installed and enabled by default. |
| CheckEnabled(good_crx); |
| #else |
| CheckDisabledForCustodianApproval(good_crx); |
| #endif |
| |
| // Parent approval can be granted even if the extension behaves already as |
| // parent-approved on Win/Linux/Mac. |
| supervised_user_extensions_delegate()->AddExtensionApproval(*extension); |
| CheckEnabled(good_crx); |
| } |
| |
| // Tests that when the parental controls start applying on Desktop (solution |
| // release), existing extensions that have been marked parent-approved on |
| // Desktop by default can be upgraded without further parental approval. |
| TEST_F(SupervisedUserExtensionTest, |
| ExtensionsEnabledOnSkipParentApprovalReleaseCanBeUpgraded) { |
| ExtensionServiceInitParams params; |
| params.profile_is_supervised = true; |
| InitializeExtensionService(std::move(params)); |
| SetDefaultParentalControlSettings(); |
| // Install an extension. It should be enabled as we haven't created the SU |
| // extension manager yet. Treated as a pre-existing extension. |
| const Extension* extension = InstallPermissionsTestExtension(INSTALL_NEW); |
| ASSERT_TRUE(extension); |
| std::string extension_id = extension->id(); |
| |
| // Make sure it's enabled. |
| CheckEnabled(extension_id); |
| |
| // Create the extensions manager. If the |
| // `SkipParentApprovalToInstallExtensions` feature applies for the first time, |
| // the existing extensions remain enabled on Desktop. |
| CreateExtensionManager(); |
| |
| #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) |
| // On Desktop, before the release of parental controls the extensions would be |
| // installed and enabled by default. |
| CheckEnabled(extension_id); |
| #else |
| CheckDisabledForCustodianApproval(extension_id); |
| #endif |
| |
| // Update to a new version with increased permissions. |
| UpdatePermissionsTestExtension(extension_id, "2", DISABLED); |
| const Extension* extension2 = |
| CheckDisabledForPermissionsIncrease(extension_id); |
| ASSERT_TRUE(extension2); |
| |
| // Grant the upgraded permissions. |
| registrar()->GrantPermissionsAndEnableExtension(*extension2); |
| #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) |
| // The parental approval granted at SU Extension manager still applies. |
| CheckEnabled(extension_id); |
| #else |
| // In ChromeOS the extension is still disabled as parent approval was never |
| // granted. |
| CheckDisabledForCustodianApproval(extension_id); |
| #endif |
| } |
| |
| // Tests that uninstalling a parent-approved extension removes the parental |
| // approval. |
| TEST_F(SupervisedUserExtensionTest, UnistallingRevokesParentApproval) { |
| InitServices(/*profile_is_supervised=*/true); |
| SetDefaultParentalControlSettings(); |
| |
| const Extension* extension = |
| InstallNoPermissionsTestExtension(GetDefaultInstalledState()); |
| std::string extension_id = extension->id(); |
| |
| CheckDisabledForCustodianApproval(extension_id); |
| // Simulate parent approval. |
| supervised_user_extensions_delegate()->AddExtensionApproval(*extension); |
| |
| CheckEnabled(extension_id); |
| EXPECT_TRUE(profile() |
| ->GetPrefs() |
| ->GetDict(prefs::kSupervisedUserApprovedExtensions) |
| .contains(extension_id)); |
| |
| // Uninstall the extension. |
| std::u16string error; |
| registrar()->UninstallExtension( |
| extension_id, UninstallReason::UNINSTALL_REASON_FOR_TESTING, &error); |
| EXPECT_FALSE(profile() |
| ->GetPrefs() |
| ->GetDict(prefs::kSupervisedUserApprovedExtensions) |
| .contains(extension_id)); |
| } |
| |
| // Tests that the kApprovalGranted UMA metric only increments once without |
| // duplication for the same extension id. |
| TEST_F(SupervisedUserExtensionTest, DontTriggerMetricsIfAlreadyApproved) { |
| InitServices(/*profile_is_supervised=*/true); |
| SetDefaultParentalControlSettings(); |
| |
| base::HistogramTester histogram_tester; |
| |
| base::FilePath path = data_dir().AppendASCII("good.crx"); |
| // The extension should be installed but disabled until we perform custodian |
| // approval. |
| const Extension* extension = InstallCRX(path, INSTALL_WITHOUT_LOAD); |
| ASSERT_TRUE(extension); |
| CheckDisabledForCustodianApproval(extension->id()); |
| |
| // Simulate parent approval for the extension installation. |
| supervised_user_extensions_delegate()->AddExtensionApproval(*extension); |
| // The extension should be enabled now. |
| CheckEnabled(extension->id()); |
| |
| // Should see 1 kApprovalGranted metric count recorded. |
| histogram_tester.ExpectUniqueSample( |
| SupervisedUserExtensionsMetricsRecorder::kExtensionsHistogramName, |
| SupervisedUserExtensionsMetricsRecorder::UmaExtensionState:: |
| kApprovalGranted, |
| 1); |
| histogram_tester.ExpectTotalCount( |
| SupervisedUserExtensionsMetricsRecorder::kExtensionsHistogramName, 1); |
| |
| // Simulate the supervised user disabling and re-enabling the extension |
| // without changing anything else. |
| supervised_user_extensions_delegate()->AddExtensionApproval(*extension); |
| |
| // Should not see another kApprovalGranted metric count recorded because it |
| // was already approved. The previous step should be a no-op. |
| histogram_tester.ExpectBucketCount( |
| SupervisedUserExtensionsMetricsRecorder::kExtensionsHistogramName, |
| SupervisedUserExtensionsMetricsRecorder::UmaExtensionState:: |
| kApprovalGranted, |
| 1); |
| histogram_tester.ExpectTotalCount( |
| SupervisedUserExtensionsMetricsRecorder::kExtensionsHistogramName, 1); |
| |
| // Now remove approval. |
| supervised_user_extensions_delegate()->RemoveExtensionApproval(*extension); |
| |
| // There should be a kApprovalRemoved metric count. |
| histogram_tester.ExpectBucketCount( |
| SupervisedUserExtensionsMetricsRecorder::kExtensionsHistogramName, |
| SupervisedUserExtensionsMetricsRecorder::UmaExtensionState:: |
| kApprovalRemoved, |
| 1); |
| histogram_tester.ExpectTotalCount( |
| SupervisedUserExtensionsMetricsRecorder::kExtensionsHistogramName, 2); |
| } |
| |
| // Tests that parent approval is necessary but not sufficient to enable |
| // extensions when both disable reasons custodian_approval_required and |
| // permissions_increase are present. |
| TEST_F(SupervisedUserExtensionTest, ParentApprovalNecessaryButNotSufficient) { |
| InitServices(/*profile_is_supervised=*/true); |
| SetDefaultParentalControlSettings(); |
| |
| std::string id = |
| InstallPermissionsTestExtension(GetDefaultInstalledState())->id(); |
| // Update to a new version with increased permissions. |
| UpdatePermissionsTestExtension(id, "2", DISABLED); |
| // Expect both disable reasons. |
| CheckDisabledForCustodianApproval(id); |
| const Extension* extension = CheckDisabledForPermissionsIncrease(id); |
| ASSERT_TRUE(extension); |
| |
| // Try to enable the extension without parent approval to prove that it's |
| // necessary. |
| registrar()->GrantPermissionsAndEnableExtension(*extension); |
| // The extension is still disabled. |
| CheckDisabledForCustodianApproval(id); |
| CheckDisabledForPermissionsIncrease(id); |
| |
| // Simulate parent approval. |
| supervised_user_extensions_delegate()->AddExtensionApproval( |
| *registry()->GetInstalledExtension(id)); |
| // The extension is still disabled (not sufficient). |
| CheckDisabledForPermissionsIncrease(id); |
| |
| // Now grant permissions and try to enable again. |
| registrar()->GrantPermissionsAndEnableExtension(*extension); |
| // The extension should be enabled. |
| CheckEnabled(id); |
| } |
| } // namespace extensions |