blob: da16d945ec0d16263d8b6ebb0bee523ac4753609 [file] [log] [blame]
// 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 "base/files/file_path.h"
#include "base/functional/callback_forward.h"
#include "base/test/gtest_util.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/extensions/extension_browsertest.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_test_util.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/views/supervised_user/parent_permission_dialog_view.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/mixin_based_in_process_browser_test.h"
#include "chrome/test/supervised_user/supervision_mixin.h"
#include "components/supervised_user/core/browser/supervised_user_preferences.h"
#include "components/supervised_user/core/browser/supervised_user_utils.h"
#include "components/supervised_user/core/common/features.h"
#include "components/supervised_user/core/common/pref_names.h"
#include "components/supervised_user/core/common/supervised_user_constants.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/test_launcher.h"
#include "content/public/test/web_contents_tester.h"
#include "extensions/browser/disable_reason.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/supervised_user_extensions_delegate.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_builder.h"
#if BUILDFLAG(IS_CHROMEOS)
#include "chrome/browser/supervised_user/chromeos/parent_access_extension_approvals_manager.h"
#include "chromeos/crosapi/mojom/parent_access.mojom.h"
#endif // BUILDFLAG(IS_CHROMEOS)
namespace {
constexpr char kGoodCrxId[] = "ldnnhddmnhbkjipkidpdiheffobcpfmf";
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
constexpr char kSimpleWithIconCrxId[] = "dehdlahnlebladnfleagmjdapdjdcnlp";
#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
} // namespace
namespace extensions {
enum class ExtensionsParentalControlState : int { kEnabled = 0, kDisabled };
enum class ExtensionManagementSwitch : int {
kManagedByExtensions = 0,
kManagedByPermissions = 1
};
std::string CreateTestSuffixFromParam(const auto& info) {
return std::string(std::get<0>(info.param) ==
ExtensionsParentalControlState::kEnabled
? "WithParentalControlsOnExtensions"
: "WithoutParentalControlsOnExtensions") +
std::string(std::get<1>(info.param) ==
ExtensionManagementSwitch::kManagedByExtensions
? "ManagedByExtensions"
: "ManagedByPermissions");
}
using SupervisionMixinSigninModeCallback =
base::RepeatingCallback<supervised_user::SupervisionMixin::SignInMode()>;
// Tests interaction between supervised users and extensions.
class SupervisionExtensionTestBase
: public ExtensionBrowserTest,
public ::testing::WithParamInterface<
std::tuple<ExtensionsParentalControlState,
ExtensionManagementSwitch,
SupervisionMixinSigninModeCallback>> {
public:
SupervisionExtensionTestBase() {
std::vector<base::test::FeatureRef> enabled_features;
std::vector<base::test::FeatureRef> disabled_features;
if (ApplyParentalControlsOnExtensions()) {
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
enabled_features.push_back(
supervised_user::
kEnableExtensionsPermissionsForSupervisedUsersOnDesktop);
#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
if (GetExtensionManagementSwitch() ==
ExtensionManagementSwitch::kManagedByExtensions) {
// Managed by preference `SkipParentApprovalToInstallExtensions` (new
// flow).
enabled_features.push_back(
supervised_user::
kEnableSupervisedUserSkipParentApprovalToInstallExtensions);
} else {
disabled_features.push_back(
supervised_user::
kEnableSupervisedUserSkipParentApprovalToInstallExtensions);
}
} else {
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
disabled_features.push_back(
supervised_user::
kEnableExtensionsPermissionsForSupervisedUsersOnDesktop);
disabled_features.push_back(
supervised_user::
kEnableSupervisedUserSkipParentApprovalToInstallExtensions);
#else
// For ChromeOS, the parental controls should always apply to extensions
// and this case should not be reached. See the instantiation of the test suite.
NOTREACHED();
#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
}
scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features);
}
~SupervisionExtensionTestBase() override { scoped_feature_list_.Reset(); }
// We have to essentially replicate what MixinBasedInProcessBrowserTest does
// here because ExtensionBrowserTest doesn't inherit from that class.
void SetUp() override {
mixin_host_.SetUp();
ExtensionBrowserTest::SetUp();
}
void SetUpCommandLine(base::CommandLine* command_line) override {
mixin_host_.SetUpCommandLine(command_line);
ExtensionBrowserTest::SetUpCommandLine(command_line);
}
void SetUpDefaultCommandLine(base::CommandLine* command_line) override {
mixin_host_.SetUpDefaultCommandLine(command_line);
ExtensionBrowserTest::SetUpDefaultCommandLine(command_line);
}
bool SetUpUserDataDirectory() override {
return mixin_host_.SetUpUserDataDirectory() &&
ExtensionBrowserTest::SetUpUserDataDirectory();
}
void SetUpInProcessBrowserTestFixture() override {
mixin_host_.SetUpInProcessBrowserTestFixture();
ExtensionBrowserTest::SetUpInProcessBrowserTestFixture();
}
void CreatedBrowserMainParts(
content::BrowserMainParts* browser_main_parts) override {
mixin_host_.CreatedBrowserMainParts(browser_main_parts);
ExtensionBrowserTest::CreatedBrowserMainParts(browser_main_parts);
}
void SetUpOnMainThread() override {
mixin_host_.SetUpOnMainThread();
ExtensionBrowserTest::SetUpOnMainThread();
}
void TearDownOnMainThread() override {
mixin_host_.TearDownOnMainThread();
ExtensionBrowserTest::TearDownOnMainThread();
}
void TearDownInProcessBrowserTestFixture() override {
mixin_host_.TearDownInProcessBrowserTestFixture();
ExtensionBrowserTest::TearDownInProcessBrowserTestFixture();
}
void TearDown() override {
mixin_host_.TearDown();
ExtensionBrowserTest::TearDown();
}
protected:
bool IsDisabledForCustodianApproval(const std::string& extension_id) {
ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(profile());
return extension_prefs->HasDisableReason(
extension_id,
extensions::disable_reason::DISABLE_CUSTODIAN_APPROVAL_REQUIRED);
}
void InstallExtensionAndCheckStatus(
bool should_be_loaded,
bool should_be_enabled,
const std::string& extension_id = kGoodCrxId,
const std::string& extension_crx = "good.crx") {
base::FilePath path = test_data_dir_.AppendASCII(extension_crx);
EXPECT_EQ(LoadExtension(path) != nullptr, should_be_loaded);
const Extension* extension =
extension_registry()->GetInstalledExtension(extension_id);
EXPECT_TRUE(extension);
EXPECT_EQ(
extension_registry()->disabled_extensions().Contains(extension_id),
!should_be_enabled);
EXPECT_EQ(IsDisabledForCustodianApproval(extension_id), !should_be_enabled);
EXPECT_EQ(extension_registry()->enabled_extensions().Contains(extension_id),
should_be_enabled);
}
bool ApplyParentalControlsOnExtensions() {
return std::get<0>(GetParam()) == ExtensionsParentalControlState::kEnabled;
}
ExtensionManagementSwitch GetExtensionManagementSwitch() {
return std::get<1>(GetParam());
}
supervised_user::SupervisionMixin::SignInMode GetMixinSigninMode() {
return std::get<2>(GetParam()).Run();
}
InProcessBrowserTestMixinHost mixin_host_;
private:
base::test::ScopedFeatureList scoped_feature_list_;
supervised_user::SupervisionMixin supervision_mixin_{
mixin_host_,
this,
embedded_test_server(),
{.sign_in_mode = GetMixinSigninMode()}};
};
// Tests interaction between supervised users and extensions after the optional
// supervision is removed from the account.
class SupervisionRemovalExtensionTest : public SupervisionExtensionTestBase {
public:
SupervisionRemovalExtensionTest() = default;
};
// If extension restrictions apply to supervised users, removing supervision
// should also remove associated disable reasons, such as
// DISABLE_CUSTODIAN_APPROVAL_REQUIRED. Extensions should become enabled again
// after removing supervision. Prevents a regression to crbug/1045625.
// If extension restrictions are disabled, removing supervision leaves the
// extension unchanged and enabled.
IN_PROC_BROWSER_TEST_P(SupervisionRemovalExtensionTest,
PRE_RemoveCustodianApprovalRequirement) {
ASSERT_TRUE(profile()->IsChild());
// Set the preference that manages the extensions to the value,
// that allows installations (pending approval), if extensions are subject
// to parental controls.
if (ApplyParentalControlsOnExtensions()) {
if (GetExtensionManagementSwitch() ==
ExtensionManagementSwitch::kManagedByExtensions) {
// Note: Setting to true would have the same effect as the extension
// will be installed disabled (pending approval) by `LoadExtension`,
// as `LoadExtension` does not reach the point where we grant the parent
// approval on installation success in this mode (in WebstorePrivateApi).
supervised_user_test_util::SetSkipParentApprovalToInstallExtensionsPref(
profile(), false);
} else {
supervised_user_test_util::
SetSupervisedUserExtensionsMayRequestPermissionsPref(profile(), true);
}
}
base::FilePath path = test_data_dir_.AppendASCII("good.crx");
bool extension_should_be_loaded = !ApplyParentalControlsOnExtensions();
EXPECT_EQ(LoadExtension(path) != nullptr, extension_should_be_loaded);
const Extension* extension =
extension_registry()->GetInstalledExtension(kGoodCrxId);
EXPECT_TRUE(extension);
if (ApplyParentalControlsOnExtensions()) {
// This extension is a supervised user initiated install and should remain
// disabled.
EXPECT_TRUE(
extension_registry()->disabled_extensions().Contains(kGoodCrxId));
EXPECT_TRUE(IsDisabledForCustodianApproval(kGoodCrxId));
} else {
// When extension permissions are disabled, the extension is installed and
// enabled.
EXPECT_TRUE(
extension_registry()->enabled_extensions().Contains(kGoodCrxId));
}
}
IN_PROC_BROWSER_TEST_P(SupervisionRemovalExtensionTest,
RemoveCustodianApprovalRequirement) {
ASSERT_FALSE(profile()->IsChild());
// The extension should still be installed since we are sharing the same data
// directory as the PRE test.
const Extension* extension =
extension_registry()->GetInstalledExtension(kGoodCrxId);
EXPECT_TRUE(extension);
// The extension should be enabled now after removing supervision.
EXPECT_TRUE(extension_registry()->enabled_extensions().Contains(kGoodCrxId));
EXPECT_FALSE(
extension_registry()->disabled_extensions().Contains(kGoodCrxId));
EXPECT_FALSE(IsDisabledForCustodianApproval(kGoodCrxId));
}
INSTANTIATE_TEST_SUITE_P(
All,
SupervisionRemovalExtensionTest,
testing::Combine(
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
testing::Values(ExtensionsParentalControlState::kDisabled,
ExtensionsParentalControlState::kEnabled),
#else
// Extensions parental controls always enabled on ChromeOS.
testing::Values(ExtensionsParentalControlState::kEnabled),
#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
testing::Values(ExtensionManagementSwitch::kManagedByExtensions,
ExtensionManagementSwitch::kManagedByPermissions),
testing::Values(base::BindRepeating([]() {
// The test covers the removal of supervision. Pre-test should start with a
// supervised profile, main test with a regular profile.
return content::IsPreTest()
? supervised_user::SupervisionMixin::SignInMode::
kSupervised
: supervised_user::SupervisionMixin::SignInMode::kRegular;
}))),
[](const auto& info) { return CreateTestSuffixFromParam(info); });
// Tests interaction between supervised users and extensions after the optional
// supervision is added to an the account.
class UserGellerizationExtensionTest : public SupervisionExtensionTestBase {
public:
UserGellerizationExtensionTest() = default;
};
IN_PROC_BROWSER_TEST_P(UserGellerizationExtensionTest,
PRE_UserGellerizationDisablesExistingExtensions) {
ASSERT_FALSE(profile()->IsChild());
base::FilePath path = test_data_dir_.AppendASCII("good.crx");
EXPECT_TRUE(LoadExtension(path) != nullptr);
const Extension* extension =
extension_registry()->GetInstalledExtension(kGoodCrxId);
EXPECT_TRUE(extension);
// The extension is installed and enabled.
EXPECT_TRUE(extension_registry()->enabled_extensions().Contains(kGoodCrxId));
}
IN_PROC_BROWSER_TEST_P(UserGellerizationExtensionTest,
UserGellerizationDisablesExistingExtensions) {
ASSERT_TRUE(profile()->IsChild());
// The extension should still be installed since we are sharing the same data
// directory as the PRE test.
const Extension* extension =
extension_registry()->GetInstalledExtension(kGoodCrxId);
EXPECT_TRUE(extension);
// The extension should be disabled, pending parent approval.
EXPECT_EQ(ApplyParentalControlsOnExtensions(),
extension_registry()->disabled_extensions().Contains(kGoodCrxId));
EXPECT_EQ(ApplyParentalControlsOnExtensions(),
IsDisabledForCustodianApproval(kGoodCrxId));
}
INSTANTIATE_TEST_SUITE_P(
All,
UserGellerizationExtensionTest,
testing::Combine(
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
testing::Values(ExtensionsParentalControlState::kDisabled,
ExtensionsParentalControlState::kEnabled),
#else
// Extensions parental controls always enabled on ChromeOS.
testing::Values(ExtensionsParentalControlState::kEnabled),
#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
testing::Values(ExtensionManagementSwitch::kManagedByExtensions,
ExtensionManagementSwitch::kManagedByPermissions),
testing::Values(base::BindRepeating([]() {
// Pre-test should start with a regular
// profile, main test with a supervised profile.
return content::IsPreTest()
? supervised_user::SupervisionMixin::SignInMode::kRegular
: supervised_user::SupervisionMixin::SignInMode::
kSupervised;
}))),
[](const auto& info) { return CreateTestSuffixFromParam(info); });
// Tests the parental controls applied on extensions for supervised users
// under different values of the Family Link Extensions Switch
// ("Allow to add extensions without asking for permission").
class ParentApprovalHandlingByExtensionSwitchTest
: public SupervisionExtensionTestBase {
public:
ParentApprovalHandlingByExtensionSwitchTest() = default;
};
IN_PROC_BROWSER_TEST_P(
ParentApprovalHandlingByExtensionSwitchTest,
PRE_GrantParentApprovalWhenExtensionSwitchBecomesEnabled) {
ASSERT_TRUE(profile()->IsChild());
// Set the Extensions preference to OFF, requiring parent approval for
// installations.
supervised_user_test_util::SetSkipParentApprovalToInstallExtensionsPref(
profile(), false);
// If parental controls apply the extensions should be disabled, pending
// approval.
bool should_be_loaded = !ApplyParentalControlsOnExtensions();
bool should_be_enabled = !ApplyParentalControlsOnExtensions();
InstallExtensionAndCheckStatus(should_be_loaded, should_be_enabled);
}
IN_PROC_BROWSER_TEST_P(ParentApprovalHandlingByExtensionSwitchTest,
GrantParentApprovalWhenExtensionSwitchBecomesEnabled) {
ASSERT_TRUE(profile()->IsChild());
// Flip the Extensions preference to ON.
supervised_user_test_util::SetSkipParentApprovalToInstallExtensionsPref(
profile(), true);
// If extensions are managed by the Extensions
// Family Link switch, the extension should become enabled and
// parent-approved.
bool should_be_enabled = !ApplyParentalControlsOnExtensions() ||
GetExtensionManagementSwitch() ==
ExtensionManagementSwitch::kManagedByExtensions;
EXPECT_EQ(should_be_enabled,
extension_registry()->enabled_extensions().Contains(kGoodCrxId));
// Flip the Extensions preference to OFF.
// The previously approved and enabled extensions should remain enabled.
supervised_user_test_util::SetSkipParentApprovalToInstallExtensionsPref(
profile(), false);
EXPECT_EQ(should_be_enabled,
extension_registry()->enabled_extensions().Contains(kGoodCrxId));
}
IN_PROC_BROWSER_TEST_P(
ParentApprovalHandlingByExtensionSwitchTest,
GrantParentParentApprovalOnInstallationIfExtensionSwitchEnabled) {
// Set the Extensions preference to ON.
supervised_user_test_util::SetSkipParentApprovalToInstallExtensionsPref(
profile(), true);
// If parental controls apply and the extensions are managed by the Permission
// switch, they should be disabled, pending approval. IF not parental controls
// apply, or the extensions are managed by the Extensions switch, it will be
// installed enabled with parent approval.
bool should_be_loaded = !ApplyParentalControlsOnExtensions() ||
GetExtensionManagementSwitch() ==
ExtensionManagementSwitch::kManagedByExtensions;
bool should_be_enabled = should_be_loaded;
InstallExtensionAndCheckStatus(should_be_loaded, should_be_enabled);
// Flip the Extensions preference to OFF.
// The previously approved and enabled extensions should remain enabled.
supervised_user_test_util::SetSkipParentApprovalToInstallExtensionsPref(
profile(), false);
EXPECT_EQ(should_be_enabled,
extension_registry()->enabled_extensions().Contains(kGoodCrxId));
}
// TODO(b/321240025): Add test case on permission increase.
// TODO(b/321240025): Add test case on an extension trying to change settings of
// a website.
INSTANTIATE_TEST_SUITE_P(
All,
ParentApprovalHandlingByExtensionSwitchTest,
testing::Combine(
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
testing::Values(ExtensionsParentalControlState::kDisabled,
ExtensionsParentalControlState::kEnabled),
#else
// Extensions parental controls always enabled on ChromeOS.
testing::Values(ExtensionsParentalControlState::kEnabled),
#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
testing::Values(ExtensionManagementSwitch::kManagedByExtensions,
ExtensionManagementSwitch::kManagedByPermissions),
testing::Values(base::BindRepeating([]() {
return supervised_user::SupervisionMixin::SignInMode::kSupervised;
}))),
[](const auto& info) { return CreateTestSuffixFromParam(info); });
class ParentApprovalRequestTest
: public SupervisionExtensionTestBase,
#if BUILDFLAG(IS_CHROMEOS)
public TestExtensionApprovalsManagerObserver,
#endif
public TestParentPermissionDialogViewObserver {
public:
ParentApprovalRequestTest()
:
#if BUILDFLAG(IS_CHROMEOS)
TestExtensionApprovalsManagerObserver(this),
#endif
TestParentPermissionDialogViewObserver(this) {
}
#if BUILDFLAG(IS_CHROMEOS)
// TestExtensionApprovalsManagerObserver implementation:
void OnTestParentAccessDialogCreated() override {
parent_permission_dialog_appeared_ = true;
SetParentAccessDialogResult(crosapi::mojom::ParentAccessResult::NewCanceled(
crosapi::mojom::ParentAccessCanceledResult::New()));
}
#endif
// TestParentPermissionDialogViewObserver implementation:
void OnTestParentPermissionDialogViewCreated(
ParentPermissionDialogView* view) override {
parent_permission_dialog_appeared_ = true;
}
void ClearCustodianPrefs() {
// Clears the preferences relating to both custodians.
profile()->GetPrefs()->ClearPref(prefs::kSupervisedUserCustodianProfileURL);
profile()->GetPrefs()->ClearPref(prefs::kSupervisedUserCustodianName);
profile()->GetPrefs()->ClearPref(prefs::kSupervisedUserCustodianEmail);
profile()->GetPrefs()->ClearPref(
prefs::kSupervisedUserCustodianObfuscatedGaiaId);
profile()->GetPrefs()->ClearPref(
prefs::kSupervisedUserCustodianProfileImageURL);
profile()->GetPrefs()->ClearPref(
prefs::kSupervisedUserSecondCustodianProfileURL);
profile()->GetPrefs()->ClearPref(prefs::kSupervisedUserSecondCustodianName);
profile()->GetPrefs()->ClearPref(
prefs::kSupervisedUserSecondCustodianEmail);
profile()->GetPrefs()->ClearPref(
prefs::kSupervisedUserSecondCustodianObfuscatedGaiaId);
profile()->GetPrefs()->ClearPref(
prefs::kSupervisedUserSecondCustodianProfileImageURL);
}
bool parent_permission_dialog_appeared_ = false;
};
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
// Tests that installation fails when the custodian info is missing and the
// right histograms are recorded. Regression test for crbug.com/35071637.
IN_PROC_BROWSER_TEST_P(ParentApprovalRequestTest,
RequestToInstallExtensionMissingCustodianInfo) {
// Set the preferences to the default values from Family Link.
supervised_user_test_util::
SetSupervisedUserExtensionsMayRequestPermissionsPref(profile(), true);
supervised_user_test_util::SetSkipParentApprovalToInstallExtensionsPref(
profile(), false);
scoped_refptr<const extensions::Extension> extension =
extensions::ExtensionBuilder("An extension").Build();
CHECK(extension);
ClearCustodianPrefs();
// Request Approval to add a new extension.
base::HistogramTester histogram_tester;
SkBitmap icon;
auto supervised_user_extensions_delegate =
std::make_unique<SupervisedUserExtensionsDelegateImpl>(profile());
supervised_user_extensions_delegate->RequestToAddExtensionOrShowError(
*extension.get(), browser()->tab_strip_model()->GetActiveWebContents(),
gfx::ImageSkia::CreateFrom1xBitmap(icon),
SupervisedUserExtensionParentApprovalEntryPoint::kOnWebstoreInstallation,
base::DoNothing());
// The dialog should not have appeared.
EXPECT_FALSE(parent_permission_dialog_appeared_);
histogram_tester.ExpectBucketCount(SupervisedUserExtensionsMetricsRecorder::
kParentPermissionDialogHistogramName,
SupervisedUserExtensionsMetricsRecorder::
ParentPermissionDialogState::kFailed,
1);
histogram_tester.ExpectBucketCount(
SupervisedUserExtensionsMetricsRecorder::
kParentPermissionDialogHistogramName,
SupervisedUserExtensionsMetricsRecorder::ParentPermissionDialogState::
kNoParentError,
1);
}
#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
// Tests that the method to request extension approval can be triggered
// without errors for new (uninstalled) extensions that already have been
// granted parent approval.
// Prevents regressions to b/321016032.
IN_PROC_BROWSER_TEST_P(ParentApprovalRequestTest,
RequestToInstallApprovedExtension) {
// Create and extension and give it parent approval.
scoped_refptr<const extensions::Extension> extension =
extensions::ExtensionBuilder("An extension").Build();
CHECK(extension);
base::Value::Dict approved_extensions;
approved_extensions.Set(extension->id(), true);
profile()->GetPrefs()->SetDict(prefs::kSupervisedUserApprovedExtensions,
std::move(approved_extensions));
ASSERT_FALSE(extension_registry()->GetInstalledExtension(extension->id()));
// Request Approval to add a new extension,
SkBitmap icon;
auto supervised_user_extensions_delegate =
std::make_unique<SupervisedUserExtensionsDelegateImpl>(profile());
supervised_user_extensions_delegate->RequestToAddExtensionOrShowError(
*extension.get(), browser()->tab_strip_model()->GetActiveWebContents(),
gfx::ImageSkia::CreateFrom1xBitmap(icon),
SupervisedUserExtensionParentApprovalEntryPoint::kOnWebstoreInstallation,
base::DoNothing());
// Confirm that the parent approval dialog for extensions for each OS is
// created.
EXPECT_TRUE(parent_permission_dialog_appeared_);
}
INSTANTIATE_TEST_SUITE_P(
All,
ParentApprovalRequestTest,
testing::Combine(
testing::Values(ExtensionsParentalControlState::kEnabled),
testing::Values(ExtensionManagementSwitch::kManagedByExtensions,
ExtensionManagementSwitch::kManagedByPermissions),
testing::Values(base::BindRepeating([]() {
return supervised_user::SupervisionMixin::SignInMode::kSupervised;
}))),
[](const auto& info) { return CreateTestSuffixFromParam(info); });
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
// Tests the behavior of existing and new extensions for a supervised user
// on the release of the `SkipParentalApproval` feature.
class SupervisedUserSkipParentalApprovalModeReleaseTest
: public SupervisionExtensionTestBase {
public:
SupervisedUserSkipParentalApprovalModeReleaseTest() {
// Over-writes any feature enabling of the parent class.
std::vector<base::test::FeatureRef> enabled_features;
std::vector<base::test::FeatureRef> disabled_features;
if (content::IsPreTest()) {
// Start with inactive features on Pre-test.
disabled_features.push_back(
supervised_user::
kEnableExtensionsPermissionsForSupervisedUsersOnDesktop);
disabled_features.push_back(
supervised_user::
kEnableSupervisedUserSkipParentApprovalToInstallExtensions);
} else {
// Simulate feature release on Main test.
enabled_features.push_back(
supervised_user::
kEnableExtensionsPermissionsForSupervisedUsersOnDesktop);
enabled_features.push_back(
supervised_user::
kEnableSupervisedUserSkipParentApprovalToInstallExtensions);
}
scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features);
}
~SupervisedUserSkipParentalApprovalModeReleaseTest() override {
scoped_feature_list_.Reset();
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
IN_PROC_BROWSER_TEST_P(SupervisedUserSkipParentalApprovalModeReleaseTest,
PRE_OnFeatureReleaseForSupervisedUserWithExtensions) {
ASSERT_TRUE(profile()->IsChild());
// Before the release of features `SkipParentalApprovalToInstallExtensions`
// and `EnableExtensionsPermissionsForSupervisedUsersOnDesktop` no parental
// controls apply.
InstallExtensionAndCheckStatus(/*should_be_loaded=*/true,
/*should_be_enabled=*/true);
}
IN_PROC_BROWSER_TEST_P(SupervisedUserSkipParentalApprovalModeReleaseTest,
OnFeatureReleaseForSupervisedUserWithExtensions) {
ASSERT_TRUE(profile()->IsChild());
// On feature release the extensions are enabled (due to the local parent
// approval migration).
EXPECT_TRUE(extension_registry()->enabled_extensions().Contains(kGoodCrxId));
// Extensions installed after the feature release (i.e. local installation,
// synced extensions) are disabled and pending approval.
InstallExtensionAndCheckStatus(/*should_be_loaded=*/false,
/*should_be_enabled=*/false,
kSimpleWithIconCrxId, "simple_with_icon.crx");
}
INSTANTIATE_TEST_SUITE_P(
All,
SupervisedUserSkipParentalApprovalModeReleaseTest,
testing::Combine(
testing::Values(ExtensionsParentalControlState::kEnabled),
testing::Values(ExtensionManagementSwitch::kManagedByExtensions),
testing::Values(base::BindRepeating([]() {
return supervised_user::SupervisionMixin::SignInMode::kSupervised;
}))),
[](const auto& info) { return CreateTestSuffixFromParam(info); });
#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
} // namespace extensions