| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <memory> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/macros.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/optional.h" |
| #include "base/strings/stringprintf.h" |
| #include "chrome/browser/extensions/extension_function_test_utils.h" |
| #include "chrome/browser/extensions/extension_service.h" |
| #include "chrome/browser/extensions/extension_service_test_base.h" |
| #include "chrome/browser/extensions/extension_service_test_with_install.h" |
| #include "chrome/browser/extensions/extension_uninstall_dialog.h" |
| #include "chrome/browser/extensions/test_extension_system.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/test/base/test_browser_window.h" |
| #include "content/public/test/web_contents_tester.h" |
| #include "extensions/browser/api/management/management_api.h" |
| #include "extensions/browser/api/management/management_api_constants.h" |
| #include "extensions/browser/disable_reason.h" |
| #include "extensions/browser/event_router_factory.h" |
| #include "extensions/browser/extension_dialog_auto_confirm.h" |
| #include "extensions/browser/extension_prefs.h" |
| #include "extensions/browser/extension_registry.h" |
| #include "extensions/browser/extension_system.h" |
| #include "extensions/browser/management_policy.h" |
| #include "extensions/browser/test_management_policy.h" |
| #include "extensions/common/api/management.h" |
| #include "extensions/common/error_utils.h" |
| #include "extensions/common/extension.h" |
| #include "extensions/common/extension_builder.h" |
| #include "extensions/common/extension_set.h" |
| #include "extensions/common/permissions/permission_set.h" |
| |
| #if BUILDFLAG(ENABLE_SUPERVISED_USERS) |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/metrics/user_action_tester.h" |
| #include "chrome/browser/background/background_contents.h" |
| #include "chrome/browser/supervised_user/supervised_user_extensions_metrics_recorder.h" |
| #include "chrome/browser/supervised_user/supervised_user_service.h" |
| #include "chrome/browser/supervised_user/supervised_user_service_factory.h" |
| #include "chrome/browser/supervised_user/supervised_user_test_util.h" |
| #include "content/public/browser/gpu_data_manager.h" |
| #include "extensions/browser/api/management/management_api_constants.h" |
| #include "extensions/common/error_utils.h" |
| #endif |
| |
| using extensions::mojom::ManifestLocation; |
| |
| namespace extensions { |
| |
| namespace { |
| |
| std::unique_ptr<KeyedService> BuildManagementApi( |
| content::BrowserContext* context) { |
| return std::make_unique<ManagementAPI>(context); |
| } |
| |
| std::unique_ptr<KeyedService> BuildEventRouter( |
| content::BrowserContext* profile) { |
| return std::make_unique<extensions::EventRouter>( |
| profile, ExtensionPrefs::Get(profile)); |
| } |
| |
| namespace constants = extension_management_api_constants; |
| |
| // TODO(devlin): Unittests are awesome. Test more with unittests and less with |
| // heavy api/browser tests. |
| class ManagementApiUnitTest : public ExtensionServiceTestWithInstall { |
| protected: |
| ManagementApiUnitTest() {} |
| ~ManagementApiUnitTest() override {} |
| |
| // A wrapper around extension_function_test_utils::RunFunction that runs with |
| // the associated browser, no flags, and can take stack-allocated arguments. |
| bool RunFunction(const scoped_refptr<ExtensionFunction>& function, |
| const base::ListValue& args); |
| |
| // Runs the management.setEnabled() function to enable an extension. |
| bool RunSetEnabledFunction(content::WebContents* web_contents, |
| const std::string& extension_id, |
| bool use_user_gesture, |
| bool accept_dialog, |
| std::string* error, |
| bool enabled = true); |
| |
| Browser* browser() { return browser_.get(); } |
| |
| // Returns the initialization parameters for the extension service. |
| virtual ExtensionServiceInitParams GetExtensionServiceInitParams() { |
| return CreateDefaultInitParams(); |
| } |
| |
| // ExtensionServiceTestBase: |
| void SetUp() override; |
| void TearDown() override; |
| |
| private: |
| // The browser (and accompanying window). |
| std::unique_ptr<TestBrowserWindow> browser_window_; |
| std::unique_ptr<Browser> browser_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ManagementApiUnitTest); |
| }; |
| |
| bool ManagementApiUnitTest::RunFunction( |
| const scoped_refptr<ExtensionFunction>& function, |
| const base::ListValue& args) { |
| return extension_function_test_utils::RunFunction( |
| function.get(), base::WrapUnique(args.DeepCopy()), browser(), |
| api_test_utils::NONE); |
| } |
| |
| bool ManagementApiUnitTest::RunSetEnabledFunction( |
| content::WebContents* web_contents, |
| const std::string& extension_id, |
| bool use_user_gesture, |
| bool accept_dialog, |
| std::string* error, |
| bool enabled) { |
| ScopedTestDialogAutoConfirm auto_confirm( |
| accept_dialog ? ScopedTestDialogAutoConfirm::ACCEPT |
| : ScopedTestDialogAutoConfirm::CANCEL); |
| base::Optional<ExtensionFunction::ScopedUserGestureForTests> gesture = |
| base::nullopt; |
| if (use_user_gesture) |
| gesture.emplace(); |
| scoped_refptr<ManagementSetEnabledFunction> function = |
| base::MakeRefCounted<ManagementSetEnabledFunction>(); |
| function->set_browser_context(profile()); |
| if (web_contents) |
| function->SetRenderFrameHost(web_contents->GetMainFrame()); |
| base::ListValue args; |
| args.AppendString(extension_id); |
| args.AppendBoolean(enabled); |
| bool result = RunFunction(function, args); |
| if (error) |
| *error = function->GetError(); |
| return result; |
| } |
| |
| void ManagementApiUnitTest::SetUp() { |
| ExtensionServiceTestBase::SetUp(); |
| InitializeExtensionService(GetExtensionServiceInitParams()); |
| ManagementAPI::GetFactoryInstance()->SetTestingFactory( |
| profile(), base::BindRepeating(&BuildManagementApi)); |
| |
| EventRouterFactory::GetInstance()->SetTestingFactory( |
| profile(), base::BindRepeating(&BuildEventRouter)); |
| |
| browser_window_ = std::make_unique<TestBrowserWindow>(); |
| Browser::CreateParams params(profile(), true); |
| params.type = Browser::TYPE_NORMAL; |
| params.window = browser_window_.get(); |
| browser_.reset(Browser::Create(params)); |
| } |
| |
| void ManagementApiUnitTest::TearDown() { |
| browser_.reset(); |
| browser_window_.reset(); |
| ExtensionServiceTestBase::TearDown(); |
| } |
| |
| // Test the basic parts of management.setEnabled. |
| TEST_F(ManagementApiUnitTest, ManagementSetEnabled) { |
| scoped_refptr<const Extension> extension = ExtensionBuilder("Test").Build(); |
| service()->AddExtension(extension.get()); |
| scoped_refptr<const Extension> source_extension = |
| ExtensionBuilder("Test").Build(); |
| service()->AddExtension(source_extension.get()); |
| std::string extension_id = extension->id(); |
| scoped_refptr<ManagementSetEnabledFunction> function( |
| new ManagementSetEnabledFunction()); |
| function->set_extension(source_extension); |
| |
| base::ListValue disable_args; |
| disable_args.AppendString(extension_id); |
| disable_args.AppendBoolean(false); |
| |
| // Test disabling an (enabled) extension. |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(extension_id)); |
| EXPECT_TRUE(RunFunction(function, disable_args)) << function->GetError(); |
| EXPECT_TRUE(registry()->disabled_extensions().Contains(extension_id)); |
| |
| base::ListValue enable_args; |
| enable_args.AppendString(extension_id); |
| enable_args.AppendBoolean(true); |
| |
| // Test re-enabling it. |
| function = new ManagementSetEnabledFunction(); |
| EXPECT_TRUE(RunFunction(function, enable_args)) << function->GetError(); |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(extension_id)); |
| |
| // Test that the enable function checks management policy, so that we can't |
| // disable an extension that is required. |
| TestManagementPolicyProvider provider( |
| TestManagementPolicyProvider::PROHIBIT_MODIFY_STATUS); |
| ManagementPolicy* policy = |
| ExtensionSystem::Get(profile())->management_policy(); |
| policy->RegisterProvider(&provider); |
| |
| function = new ManagementSetEnabledFunction(); |
| EXPECT_FALSE(RunFunction(function, disable_args)); |
| EXPECT_EQ(ErrorUtils::FormatErrorMessage(constants::kUserCantModifyError, |
| extension_id), |
| function->GetError()); |
| policy->UnregisterProvider(&provider); |
| } |
| |
| // Test that component extensions cannot be disabled, and that policy extensions |
| // can be disabled only by component/policy extensions. |
| TEST_F(ManagementApiUnitTest, ComponentPolicyDisabling) { |
| auto component = ExtensionBuilder("component") |
| .SetLocation(ManifestLocation::kComponent) |
| .Build(); |
| auto component2 = ExtensionBuilder("component2") |
| .SetLocation(ManifestLocation::kComponent) |
| .Build(); |
| auto policy = ExtensionBuilder("policy") |
| .SetLocation(ManifestLocation::kExternalPolicy) |
| .Build(); |
| auto policy2 = ExtensionBuilder("policy2") |
| .SetLocation(ManifestLocation::kExternalPolicy) |
| .Build(); |
| auto internal = ExtensionBuilder("internal") |
| .SetLocation(ManifestLocation::kInternal) |
| .Build(); |
| |
| service()->AddExtension(component.get()); |
| service()->AddExtension(component2.get()); |
| service()->AddExtension(policy.get()); |
| service()->AddExtension(policy2.get()); |
| service()->AddExtension(internal.get()); |
| |
| auto extension_can_disable_extension = |
| [this](scoped_refptr<const Extension> source_extension, |
| scoped_refptr<const Extension> target_extension) { |
| std::string id = target_extension->id(); |
| base::ListValue args; |
| args.AppendString(id); |
| args.AppendBoolean(false /* disable the extension */); |
| auto function = base::MakeRefCounted<ManagementSetEnabledFunction>(); |
| function->set_extension(source_extension); |
| bool did_disable = RunFunction(function, args); |
| // If the extension was disabled, re-enable it. |
| if (did_disable) { |
| EXPECT_TRUE(registry()->disabled_extensions().Contains(id)); |
| service()->EnableExtension(id); |
| } else { |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(id)); |
| } |
| return did_disable; |
| }; |
| |
| // Component extension cannot be disabled. |
| EXPECT_FALSE(extension_can_disable_extension(component2, component)); |
| EXPECT_FALSE(extension_can_disable_extension(policy, component)); |
| EXPECT_FALSE(extension_can_disable_extension(internal, component)); |
| |
| // Policy extension can be disabled by component/policy extensions, but not |
| // others. |
| EXPECT_TRUE(extension_can_disable_extension(component, policy)); |
| EXPECT_TRUE(extension_can_disable_extension(policy2, policy)); |
| EXPECT_FALSE(extension_can_disable_extension(internal, policy)); |
| } |
| |
| // Test that policy extensions can be enabled only by component/policy |
| // extensions. |
| TEST_F(ManagementApiUnitTest, ComponentPolicyEnabling) { |
| auto component = ExtensionBuilder("component") |
| .SetLocation(ManifestLocation::kComponent) |
| .Build(); |
| auto policy = ExtensionBuilder("policy") |
| .SetLocation(ManifestLocation::kExternalPolicy) |
| .Build(); |
| auto policy2 = ExtensionBuilder("policy2") |
| .SetLocation(ManifestLocation::kExternalPolicy) |
| .Build(); |
| auto internal = ExtensionBuilder("internal") |
| .SetLocation(ManifestLocation::kInternal) |
| .Build(); |
| |
| service()->AddExtension(component.get()); |
| service()->AddExtension(policy.get()); |
| service()->AddExtension(policy2.get()); |
| service()->AddExtension(internal.get()); |
| service()->DisableExtensionWithSource( |
| component.get(), policy->id(), disable_reason::DISABLE_BLOCKED_BY_POLICY); |
| |
| auto extension_can_enable_extension = |
| [this, component](scoped_refptr<const Extension> source_extension, |
| scoped_refptr<const Extension> target_extension) { |
| std::string id = target_extension->id(); |
| base::ListValue args; |
| args.AppendString(id); |
| args.AppendBoolean(true /* enable the extension */); |
| auto function = base::MakeRefCounted<ManagementSetEnabledFunction>(); |
| function->set_extension(source_extension); |
| bool did_enable = RunFunction(function, args); |
| // If the extension was enabled, disable it. |
| if (did_enable) { |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(id)); |
| service()->DisableExtensionWithSource( |
| component.get(), id, disable_reason::DISABLE_BLOCKED_BY_POLICY); |
| } else { |
| EXPECT_TRUE(registry()->disabled_extensions().Contains(id)); |
| } |
| return did_enable; |
| }; |
| |
| // Policy extension can be enabled by component/policy extensions, but not |
| // others. |
| EXPECT_TRUE(extension_can_enable_extension(component, policy)); |
| EXPECT_TRUE(extension_can_enable_extension(policy2, policy)); |
| EXPECT_FALSE(extension_can_enable_extension(internal, policy)); |
| } |
| |
| // Tests management.uninstall. |
| TEST_F(ManagementApiUnitTest, ManagementUninstall) { |
| scoped_refptr<const Extension> extension = ExtensionBuilder("Test").Build(); |
| service()->AddExtension(extension.get()); |
| std::string extension_id = extension->id(); |
| |
| base::ListValue uninstall_args; |
| uninstall_args.AppendString(extension->id()); |
| |
| // Auto-accept any uninstalls. |
| { |
| ScopedTestDialogAutoConfirm auto_confirm( |
| ScopedTestDialogAutoConfirm::ACCEPT); |
| |
| // Uninstall requires a user gesture, so this should fail. |
| scoped_refptr<ExtensionFunction> function( |
| new ManagementUninstallFunction()); |
| EXPECT_FALSE(RunFunction(function, uninstall_args)); |
| EXPECT_EQ(std::string(constants::kGestureNeededForUninstallError), |
| function->GetError()); |
| |
| ExtensionFunction::ScopedUserGestureForTests scoped_user_gesture; |
| |
| function = new ManagementUninstallFunction(); |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(extension_id)); |
| EXPECT_TRUE(RunFunction(function, uninstall_args)) << function->GetError(); |
| // The extension should be uninstalled. |
| EXPECT_FALSE(registry()->GetExtensionById(extension_id, |
| ExtensionRegistry::EVERYTHING)); |
| } |
| |
| // Install the extension again, and try uninstalling, auto-canceling the |
| // dialog. |
| { |
| ScopedTestDialogAutoConfirm auto_confirm( |
| ScopedTestDialogAutoConfirm::CANCEL); |
| ExtensionFunction::ScopedUserGestureForTests scoped_user_gesture; |
| |
| service()->AddExtension(extension.get()); |
| scoped_refptr<ExtensionFunction> function = |
| new ManagementUninstallFunction(); |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(extension_id)); |
| EXPECT_FALSE(RunFunction(function, uninstall_args)); |
| // The uninstall should have failed. |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(extension_id)); |
| EXPECT_EQ(ErrorUtils::FormatErrorMessage(constants::kUninstallCanceledError, |
| extension_id), |
| function->GetError()); |
| |
| // Try again, using showConfirmDialog: false. |
| std::unique_ptr<base::DictionaryValue> options(new base::DictionaryValue()); |
| options->SetBoolean("showConfirmDialog", false); |
| uninstall_args.Append(std::move(options)); |
| function = new ManagementUninstallFunction(); |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(extension_id)); |
| EXPECT_FALSE(RunFunction(function, uninstall_args)); |
| // This should still fail, since extensions can only suppress the dialog for |
| // uninstalling themselves. |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(extension_id)); |
| EXPECT_EQ(ErrorUtils::FormatErrorMessage(constants::kUninstallCanceledError, |
| extension_id), |
| function->GetError()); |
| |
| // If we try uninstall the extension itself, the uninstall should succeed |
| // (even though we auto-cancel any dialog), because the dialog is never |
| // shown. |
| uninstall_args.Remove(0u, nullptr); |
| function = new ManagementUninstallSelfFunction(); |
| function->set_extension(extension); |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(extension_id)); |
| EXPECT_TRUE(RunFunction(function, uninstall_args)) << function->GetError(); |
| EXPECT_FALSE(registry()->GetExtensionById(extension_id, |
| ExtensionRegistry::EVERYTHING)); |
| } |
| } |
| |
| // Tests management.uninstall from Web Store |
| TEST_F(ManagementApiUnitTest, ManagementWebStoreUninstall) { |
| scoped_refptr<const Extension> triggering_extension = |
| ExtensionBuilder("Test").SetID(extensions::kWebStoreAppId).Build(); |
| scoped_refptr<const Extension> extension = ExtensionBuilder("Test").Build(); |
| service()->AddExtension(extension.get()); |
| std::string extension_id = extension->id(); |
| base::ListValue uninstall_args; |
| uninstall_args.AppendString(extension->id()); |
| |
| { |
| ScopedTestDialogAutoConfirm auto_confirm( |
| ScopedTestDialogAutoConfirm::CANCEL); |
| ExtensionFunction::ScopedUserGestureForTests scoped_user_gesture; |
| |
| scoped_refptr<ExtensionFunction> function( |
| new ManagementUninstallFunction()); |
| function->set_extension(triggering_extension); |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(extension_id)); |
| EXPECT_FALSE(RunFunction(function, uninstall_args)); |
| // Webstore does not suppress the dialog for uninstalling extensions. |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(extension_id)); |
| EXPECT_EQ(ErrorUtils::FormatErrorMessage(constants::kUninstallCanceledError, |
| extension_id), |
| function->GetError()); |
| } |
| |
| { |
| scoped_refptr<ExtensionFunction> function( |
| new ManagementUninstallFunction()); |
| function->set_extension(triggering_extension); |
| |
| bool did_show = false; |
| auto callback = base::BindRepeating( |
| [](bool* did_show, extensions::ExtensionUninstallDialog* dialog) { |
| EXPECT_EQ("Remove \"Test\"?", dialog->GetHeadingText()); |
| *did_show = true; |
| }, |
| &did_show); |
| extensions::ExtensionUninstallDialog::SetOnShownCallbackForTesting( |
| &callback); |
| |
| ScopedTestDialogAutoConfirm auto_confirm( |
| ScopedTestDialogAutoConfirm::ACCEPT); |
| ExtensionFunction::ScopedUserGestureForTests scoped_user_gesture; |
| EXPECT_TRUE(RunFunction(function, uninstall_args)) << function->GetError(); |
| // The extension should be uninstalled. |
| EXPECT_EQ(nullptr, registry()->GetInstalledExtension(extension_id)); |
| EXPECT_TRUE(did_show); |
| |
| // Reset the callback. |
| extensions::ExtensionUninstallDialog::SetOnShownCallbackForTesting(nullptr); |
| } |
| } |
| |
| // Tests management.uninstall with programmatic uninstall. |
| TEST_F(ManagementApiUnitTest, ManagementProgrammaticUninstall) { |
| scoped_refptr<const Extension> triggering_extension = |
| ExtensionBuilder("Triggering Extension").SetID("123").Build(); |
| scoped_refptr<const Extension> extension = ExtensionBuilder("Test").Build(); |
| service()->AddExtension(extension.get()); |
| std::string extension_id = extension->id(); |
| base::ListValue uninstall_args; |
| uninstall_args.AppendString(extension->id()); |
| { |
| scoped_refptr<ExtensionFunction> function( |
| new ManagementUninstallFunction()); |
| function->set_extension(triggering_extension); |
| |
| bool did_show = false; |
| auto callback = base::BindRepeating( |
| [](bool* did_show, extensions::ExtensionUninstallDialog* dialog) { |
| EXPECT_EQ("\"Triggering Extension\" would like to remove \"Test\".", |
| dialog->GetHeadingText()); |
| *did_show = true; |
| }, |
| &did_show); |
| extensions::ExtensionUninstallDialog::SetOnShownCallbackForTesting( |
| &callback); |
| |
| ScopedTestDialogAutoConfirm auto_confirm( |
| ScopedTestDialogAutoConfirm::ACCEPT); |
| ExtensionFunction::ScopedUserGestureForTests scoped_user_gesture; |
| EXPECT_TRUE(RunFunction(function, uninstall_args)) << function->GetError(); |
| // The extension should be uninstalled. |
| EXPECT_EQ(nullptr, registry()->GetInstalledExtension(extension_id)); |
| EXPECT_TRUE(did_show); |
| |
| // Reset the callback. |
| extensions::ExtensionUninstallDialog::SetOnShownCallbackForTesting(nullptr); |
| } |
| } |
| // Tests uninstalling a blocklisted extension via management.uninstall. |
| TEST_F(ManagementApiUnitTest, ManagementUninstallBlocklisted) { |
| scoped_refptr<const Extension> extension = ExtensionBuilder("Test").Build(); |
| service()->AddExtension(extension.get()); |
| std::string id = extension->id(); |
| |
| service()->BlocklistExtensionForTest(id); |
| EXPECT_NE(nullptr, registry()->GetInstalledExtension(id)); |
| |
| ScopedTestDialogAutoConfirm auto_confirm(ScopedTestDialogAutoConfirm::ACCEPT); |
| ExtensionFunction::ScopedUserGestureForTests scoped_user_gesture; |
| scoped_refptr<ExtensionFunction> function(new ManagementUninstallFunction()); |
| base::ListValue uninstall_args; |
| uninstall_args.AppendString(id); |
| EXPECT_TRUE(RunFunction(function, uninstall_args)) << function->GetError(); |
| |
| EXPECT_EQ(nullptr, registry()->GetInstalledExtension(id)); |
| } |
| |
| TEST_F(ManagementApiUnitTest, ManagementEnableOrDisableBlocklisted) { |
| scoped_refptr<const Extension> extension = ExtensionBuilder("Test").Build(); |
| service()->AddExtension(extension.get()); |
| std::string id = extension->id(); |
| |
| service()->BlocklistExtensionForTest(id); |
| EXPECT_NE(nullptr, registry()->GetInstalledExtension(id)); |
| |
| scoped_refptr<ExtensionFunction> function; |
| |
| // Test enabling it. |
| { |
| base::ListValue enable_args; |
| enable_args.AppendString(id); |
| enable_args.AppendBoolean(true); |
| function = new ManagementSetEnabledFunction(); |
| EXPECT_TRUE(RunFunction(function, enable_args)) << function->GetError(); |
| EXPECT_FALSE(registry()->enabled_extensions().Contains(id)); |
| EXPECT_FALSE(registry()->disabled_extensions().Contains(id)); |
| } |
| |
| // Test disabling it |
| { |
| base::ListValue disable_args; |
| disable_args.AppendString(id); |
| disable_args.AppendBoolean(false); |
| |
| function = new ManagementSetEnabledFunction(); |
| EXPECT_TRUE(RunFunction(function, disable_args)) << function->GetError(); |
| EXPECT_FALSE(registry()->enabled_extensions().Contains(id)); |
| EXPECT_FALSE(registry()->disabled_extensions().Contains(id)); |
| } |
| } |
| |
| TEST_F(ManagementApiUnitTest, ExtensionInfo_MayEnable) { |
| using ExtensionInfo = api::management::ExtensionInfo; |
| |
| scoped_refptr<const Extension> extension = ExtensionBuilder("Test").Build(); |
| service()->AddExtension(extension.get()); |
| |
| const std::string args = |
| base::StringPrintf("[\"%s\"]", extension->id().c_str()); |
| scoped_refptr<ExtensionFunction> function; |
| |
| // Initially the extension should show as enabled. |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(extension->id())); |
| { |
| function = new ManagementGetFunction(); |
| std::unique_ptr<base::Value> value( |
| extension_function_test_utils::RunFunctionAndReturnSingleResult( |
| function.get(), args, browser())); |
| ASSERT_TRUE(value); |
| std::unique_ptr<ExtensionInfo> info = ExtensionInfo::FromValue(*value); |
| ASSERT_TRUE(info); |
| EXPECT_TRUE(info->enabled); |
| // |may_enable| is only returned for extensions which are not enabled. |
| EXPECT_FALSE(info->may_enable.get()); |
| } |
| |
| // Simulate blocklisting the extension and verify that the extension shows as |
| // disabled with a false value of |may_enable|. |
| ManagementPolicy* policy = |
| ExtensionSystem::Get(profile())->management_policy(); |
| policy->UnregisterAllProviders(); |
| TestManagementPolicyProvider provider( |
| TestManagementPolicyProvider::PROHIBIT_LOAD); |
| policy->RegisterProvider(&provider); |
| service()->CheckManagementPolicy(); |
| EXPECT_TRUE(registry()->disabled_extensions().Contains(extension->id())); |
| { |
| function = new ManagementGetFunction(); |
| std::unique_ptr<base::Value> value( |
| extension_function_test_utils::RunFunctionAndReturnSingleResult( |
| function.get(), args, browser())); |
| ASSERT_TRUE(value); |
| std::unique_ptr<ExtensionInfo> info = ExtensionInfo::FromValue(*value); |
| ASSERT_TRUE(info); |
| EXPECT_FALSE(info->enabled); |
| ASSERT_TRUE(info->may_enable.get()); |
| EXPECT_FALSE(*(info->may_enable)); |
| } |
| |
| // Re-enable the extension. |
| policy->UnregisterAllProviders(); |
| service()->CheckManagementPolicy(); |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(extension->id())); |
| |
| // Disable the extension with a normal user action. Verify the extension shows |
| // as disabled with |may_enable| as true. |
| service()->DisableExtension(extension->id(), |
| disable_reason::DISABLE_USER_ACTION); |
| EXPECT_TRUE(registry()->disabled_extensions().Contains(extension->id())); |
| { |
| function = new ManagementGetFunction(); |
| std::unique_ptr<base::Value> value( |
| extension_function_test_utils::RunFunctionAndReturnSingleResult( |
| function.get(), args, browser())); |
| ASSERT_TRUE(value); |
| std::unique_ptr<ExtensionInfo> info = ExtensionInfo::FromValue(*value); |
| ASSERT_TRUE(info); |
| EXPECT_FALSE(info->enabled); |
| ASSERT_TRUE(info->may_enable.get()); |
| EXPECT_TRUE(*(info->may_enable)); |
| } |
| } |
| |
| TEST_F(ManagementApiUnitTest, ExtensionInfo_MayDisable) { |
| using ExtensionInfo = api::management::ExtensionInfo; |
| |
| scoped_refptr<const Extension> extension = ExtensionBuilder("Test").Build(); |
| service()->AddExtension(extension.get()); |
| |
| const std::string args = |
| base::StringPrintf("[\"%s\"]", extension->id().c_str()); |
| |
| // Initially the extension should show as enabled, so it may be disabled |
| // freely. |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(extension->id())); |
| { |
| scoped_refptr<ExtensionFunction> function = new ManagementGetFunction(); |
| std::unique_ptr<base::Value> value( |
| extension_function_test_utils::RunFunctionAndReturnSingleResult( |
| function.get(), args, browser())); |
| ASSERT_TRUE(value); |
| std::unique_ptr<ExtensionInfo> info = ExtensionInfo::FromValue(*value); |
| ASSERT_TRUE(info); |
| EXPECT_TRUE(info->enabled); |
| EXPECT_TRUE(info->may_disable); |
| } |
| |
| // Simulate forcing the extension and verify that the extension shows with |
| // a false value of |may_disable|. |
| ManagementPolicy* policy = |
| ExtensionSystem::Get(profile())->management_policy(); |
| policy->UnregisterAllProviders(); |
| TestManagementPolicyProvider provider( |
| TestManagementPolicyProvider::MUST_REMAIN_ENABLED); |
| policy->RegisterProvider(&provider); |
| service()->CheckManagementPolicy(); |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(extension->id())); |
| { |
| scoped_refptr<ExtensionFunction> function = new ManagementGetFunction(); |
| std::unique_ptr<base::Value> value( |
| extension_function_test_utils::RunFunctionAndReturnSingleResult( |
| function.get(), args, browser())); |
| ASSERT_TRUE(value); |
| std::unique_ptr<ExtensionInfo> info = ExtensionInfo::FromValue(*value); |
| ASSERT_TRUE(info); |
| EXPECT_TRUE(info->enabled); |
| EXPECT_FALSE(info->may_disable); |
| } |
| } |
| |
| // Tests enabling an extension via management API after it was disabled due to |
| // permission increase. |
| TEST_F(ManagementApiUnitTest, SetEnabledAfterIncreasedPermissions) { |
| ExtensionPrefs* prefs = ExtensionPrefs::Get(profile()); |
| std::unique_ptr<content::WebContents> web_contents( |
| content::WebContentsTester::CreateTestWebContents(profile(), nullptr)); |
| |
| base::FilePath base_path = data_dir().AppendASCII("permissions_increase"); |
| base::FilePath pem_path = base_path.AppendASCII("permissions.pem"); |
| |
| base::FilePath path = base_path.AppendASCII("v1"); |
| const Extension* extension = PackAndInstallCRX(path, pem_path, INSTALL_NEW); |
| // The extension must now be installed and enabled. |
| ASSERT_TRUE(extension); |
| ASSERT_TRUE(registry()->enabled_extensions().Contains(extension->id())); |
| |
| // Save the id, as |extension| will be destroyed during updating. |
| std::string extension_id = extension->id(); |
| |
| std::unique_ptr<const PermissionSet> known_perms = |
| prefs->GetGrantedPermissions(extension_id); |
| ASSERT_TRUE(known_perms); |
| // v1 extension doesn't have any permissions. |
| EXPECT_TRUE(known_perms->IsEmpty()); |
| |
| // Update to a new version with increased permissions. |
| path = base_path.AppendASCII("v2"); |
| PackCRXAndUpdateExtension(extension_id, path, pem_path, DISABLED); |
| |
| // The extension should be disabled. |
| ASSERT_FALSE(registry()->enabled_extensions().Contains(extension_id)); |
| |
| // Due to a permission increase, prefs will contain escalation information. |
| EXPECT_TRUE(prefs->DidExtensionEscalatePermissions(extension_id)); |
| |
| // 1) Confirm re-enable prompt without user gesture, expect the extension to |
| // stay disabled. |
| { |
| std::string error; |
| bool success = RunSetEnabledFunction(web_contents.get(), extension_id, |
| false /* use_user_gesture */, |
| true /* accept_dialog */, &error); |
| EXPECT_FALSE(success); |
| EXPECT_FALSE(error.empty()); |
| EXPECT_FALSE(registry()->enabled_extensions().Contains(extension_id)); |
| // Prefs should still contain permissions escalation information. |
| EXPECT_TRUE(prefs->DidExtensionEscalatePermissions(extension_id)); |
| } |
| |
| // 2) Deny re-enable prompt without user gesture, expect the extension to stay |
| // disabled. |
| { |
| std::string error; |
| bool success = RunSetEnabledFunction(web_contents.get(), extension_id, |
| false /* use_user_gesture */, |
| false /* accept_dialog */, &error); |
| EXPECT_FALSE(success); |
| EXPECT_FALSE(error.empty()); |
| EXPECT_FALSE(registry()->enabled_extensions().Contains(extension_id)); |
| // Prefs should still contain permissions escalation information. |
| EXPECT_TRUE(prefs->DidExtensionEscalatePermissions(extension_id)); |
| } |
| |
| // 3) Deny re-enable prompt with user gesture, expect the extension to stay |
| // disabled. |
| { |
| std::string error; |
| bool success = RunSetEnabledFunction(web_contents.get(), extension_id, |
| true /* use_user_gesture */, |
| false /* accept_dialog */, &error); |
| EXPECT_FALSE(success); |
| EXPECT_FALSE(error.empty()); |
| EXPECT_FALSE(registry()->enabled_extensions().Contains(extension_id)); |
| // Prefs should still contain permissions escalation information. |
| EXPECT_TRUE(prefs->DidExtensionEscalatePermissions(extension_id)); |
| } |
| |
| // 4) Accept re-enable prompt with user gesture, expect the extension to be |
| // enabled. |
| { |
| std::string error; |
| bool success = RunSetEnabledFunction(web_contents.get(), extension_id, |
| true /* use_user_gesture */, |
| true /* accept_dialog */, &error); |
| EXPECT_TRUE(success) << error; |
| EXPECT_TRUE(error.empty()); |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(extension_id)); |
| // Prefs will no longer contain the escalation information as user has |
| // accepted increased permissions. |
| EXPECT_FALSE(prefs->DidExtensionEscalatePermissions(extension_id)); |
| } |
| |
| // Some permissions for v2 extension should be granted by now. |
| known_perms = prefs->GetGrantedPermissions(extension_id); |
| ASSERT_TRUE(known_perms); |
| EXPECT_FALSE(known_perms->IsEmpty()); |
| } |
| |
| #if BUILDFLAG(ENABLE_SUPERVISED_USERS) |
| |
| // A delegate that senses when extensions are enabled or disabled. |
| class TestManagementAPIDelegate : public ManagementAPIDelegate { |
| public: |
| TestManagementAPIDelegate() = default; |
| ~TestManagementAPIDelegate() override = default; |
| |
| void LaunchAppFunctionDelegate( |
| const Extension* extension, |
| content::BrowserContext* context) const override {} |
| GURL GetFullLaunchURL(const Extension* extension) const override { |
| return GURL(); |
| } |
| LaunchType GetLaunchType(const ExtensionPrefs* prefs, |
| const Extension* extension) const override { |
| return LaunchType::LAUNCH_TYPE_DEFAULT; |
| } |
| void GetPermissionWarningsByManifestFunctionDelegate( |
| ManagementGetPermissionWarningsByManifestFunction* function, |
| const std::string& manifest_str) const override {} |
| std::unique_ptr<InstallPromptDelegate> SetEnabledFunctionDelegate( |
| content::WebContents* web_contents, |
| content::BrowserContext* browser_context, |
| const Extension* extension, |
| base::OnceCallback<void(bool)> callback) const override { |
| return nullptr; |
| } |
| void EnableExtension(content::BrowserContext* context, |
| const std::string& extension_id) const override { |
| ++enable_count_; |
| } |
| void DisableExtension( |
| content::BrowserContext* context, |
| const Extension* source_extension, |
| const std::string& extension_id, |
| disable_reason::DisableReason disable_reason) const override {} |
| std::unique_ptr<UninstallDialogDelegate> UninstallFunctionDelegate( |
| ManagementUninstallFunctionBase* function, |
| const Extension* target_extension, |
| bool show_programmatic_uninstall_ui) const override { |
| return nullptr; |
| } |
| bool UninstallExtension(content::BrowserContext* context, |
| const std::string& transient_extension_id, |
| UninstallReason reason, |
| std::u16string* error) const override { |
| return true; |
| } |
| bool CreateAppShortcutFunctionDelegate( |
| ManagementCreateAppShortcutFunction* function, |
| const Extension* extension, |
| std::string* error) const override { |
| return true; |
| } |
| void SetLaunchType(content::BrowserContext* context, |
| const std::string& extension_id, |
| LaunchType launch_type) const override {} |
| std::unique_ptr<AppForLinkDelegate> GenerateAppForLinkFunctionDelegate( |
| ManagementGenerateAppForLinkFunction* function, |
| content::BrowserContext* context, |
| const std::string& title, |
| const GURL& launch_url) const override { |
| return nullptr; |
| } |
| bool CanContextInstallWebApps( |
| content::BrowserContext* context) const override { |
| return true; |
| } |
| void InstallOrLaunchReplacementWebApp( |
| content::BrowserContext* context, |
| const GURL& web_app_url, |
| InstallOrLaunchWebAppCallback callback) const override {} |
| bool CanContextInstallAndroidApps( |
| content::BrowserContext* context) const override { |
| return true; |
| } |
| void CheckAndroidAppInstallStatus( |
| const std::string& package_name, |
| AndroidAppInstallStatusCallback callback) const override {} |
| void InstallReplacementAndroidApp( |
| const std::string& package_name, |
| InstallAndroidAppCallback callback) const override {} |
| GURL GetIconURL(const Extension* extension, |
| int icon_size, |
| ExtensionIconSet::MatchType match, |
| bool grayscale) const override { |
| return GURL(); |
| } |
| GURL GetEffectiveUpdateURL(const extensions::Extension& extension, |
| content::BrowserContext* context) const override { |
| return GURL(); |
| } |
| |
| // EnableExtension is const, so this is mutable. |
| mutable int enable_count_ = 0; |
| }; |
| |
| // A delegate that allows a child to try to install an extension and tracks |
| // whether the parent permission dialog would have opened. |
| class TestSupervisedUserExtensionsDelegate |
| : public SupervisedUserExtensionsDelegate { |
| public: |
| TestSupervisedUserExtensionsDelegate() = default; |
| ~TestSupervisedUserExtensionsDelegate() override = default; |
| |
| // SupervisedUserExtensionsDelegate: |
| bool IsChild(content::BrowserContext* context) const override { return true; } |
| |
| bool IsExtensionAllowedByParent( |
| const extensions::Extension& extension, |
| content::BrowserContext* context) const override { |
| SupervisedUserService* supervised_user_service = |
| SupervisedUserServiceFactory::GetForBrowserContext(context); |
| return supervised_user_service->IsExtensionAllowed(extension); |
| } |
| |
| void PromptForParentPermissionOrShowError( |
| const extensions::Extension& extension, |
| content::BrowserContext* context, |
| content::WebContents* contents, |
| ParentPermissionDialogDoneCallback parent_permission_callback, |
| base::OnceClosure error_callback) override { |
| // Preconditions. |
| DCHECK(IsChild(context)); |
| DCHECK(!IsExtensionAllowedByParent(extension, context)); |
| |
| if (CanInstallExtensions(context)) { |
| ShowParentPermissionDialogForExtension( |
| extension, context, contents, std::move(parent_permission_callback)); |
| } else { |
| ShowExtensionEnableBlockedByParentDialogForExtension( |
| extension, contents, std::move(error_callback)); |
| } |
| } |
| |
| void set_next_parent_permission_dialog_result( |
| ParentPermissionDialogResult result) { |
| dialog_result_ = result; |
| } |
| |
| int show_dialog_count() const { return show_dialog_count_; } |
| int show_block_dialog_count() const { return show_block_dialog_count_; } |
| |
| private: |
| // Returns true if |context| represents a supervised child account who may |
| // install extensions with parent permission. |
| bool CanInstallExtensions(content::BrowserContext* context) const { |
| SupervisedUserService* supervised_user_service = |
| SupervisedUserServiceFactory::GetForBrowserContext(context); |
| return supervised_user_service->CanInstallExtensions(); |
| } |
| |
| // Shows a parent permission dialog for |extension| and call |done_callback| |
| // when it completes. |
| void ShowParentPermissionDialogForExtension( |
| const extensions::Extension& extension, |
| content::BrowserContext* context, |
| content::WebContents* contents, |
| ParentPermissionDialogDoneCallback done_callback) { |
| ++show_dialog_count_; |
| std::move(done_callback).Run(dialog_result_); |
| } |
| |
| // Shows a dialog indicating that |extension| has been blocked and call |
| // |done_callback| when it completes. |
| void ShowExtensionEnableBlockedByParentDialogForExtension( |
| const extensions::Extension& extension, |
| content::WebContents* contents, |
| base::OnceClosure done_callback) { |
| show_block_dialog_count_++; |
| SupervisedUserExtensionsMetricsRecorder::RecordEnablementUmaMetrics( |
| SupervisedUserExtensionsMetricsRecorder::EnablementState:: |
| kFailedToEnable); |
| std::move(done_callback).Run(); |
| } |
| |
| ParentPermissionDialogResult dialog_result_ = |
| ParentPermissionDialogResult::kParentPermissionFailed; |
| int show_dialog_count_ = 0; |
| int show_block_dialog_count_ = 0; |
| }; |
| |
| // Tests for supervised users (child accounts). Supervised users are not allowed |
| // to install apps or extensions unless their parent approves. |
| class ManagementApiSupervisedUserTest : public ManagementApiUnitTest { |
| public: |
| ManagementApiSupervisedUserTest() = default; |
| ~ManagementApiSupervisedUserTest() override = default; |
| |
| // ManagementApiUnitTest: |
| ExtensionServiceInitParams GetExtensionServiceInitParams() override { |
| ExtensionServiceInitParams params = CreateDefaultInitParams(); |
| // Force a TestingPrefServiceSyncable to be created. |
| params.pref_file.clear(); |
| params.profile_is_supervised = true; |
| return params; |
| } |
| |
| SupervisedUserService* GetSupervisedUserService() { |
| return SupervisedUserServiceFactory::GetForProfile(profile()); |
| } |
| |
| void SetUp() override { |
| ManagementApiUnitTest::SetUp(); |
| |
| // Set up custodians (parents) for the child. |
| supervised_user_test_util::AddCustodians(browser()->profile()); |
| |
| GetSupervisedUserService()->Init(); |
| // Set the pref to allow the child to request extension install. |
| GetSupervisedUserService() |
| ->SetSupervisedUserExtensionsMayRequestPermissionsPrefForTesting(true); |
| |
| // Create a WebContents to simulate the Chrome Web Store. |
| web_contents_ = |
| content::WebContentsTester::CreateTestWebContents(profile(), nullptr); |
| |
| management_api_ = ManagementAPI::GetFactoryInstance()->Get(profile()); |
| |
| // Install a SupervisedUserExtensionsDelegate to sense the dialog state. |
| supervised_user_delegate_ = new TestSupervisedUserExtensionsDelegate; |
| management_api_->set_supervised_user_extensions_delegate_for_test( |
| base::WrapUnique(supervised_user_delegate_)); |
| } |
| |
| std::unique_ptr<content::WebContents> web_contents_; |
| ManagementAPI* management_api_ = nullptr; |
| TestSupervisedUserExtensionsDelegate* supervised_user_delegate_ = nullptr; |
| }; |
| |
| TEST_F(ManagementApiSupervisedUserTest, SetEnabled_BlockedByParent) { |
| // Preconditions. |
| ASSERT_TRUE(profile()->IsChild()); |
| |
| base::HistogramTester histogram_tester; |
| base::UserActionTester user_action_tester; |
| |
| base::FilePath base_path = data_dir().AppendASCII("permissions_increase"); |
| base::FilePath pem_path = base_path.AppendASCII("permissions.pem"); |
| |
| base::FilePath path = base_path.AppendASCII("v1"); |
| const Extension* extension = |
| PackAndInstallCRX(path, pem_path, INSTALL_WITHOUT_LOAD); |
| ASSERT_TRUE(extension); |
| // The extension should be installed but disabled. |
| EXPECT_TRUE(registry()->disabled_extensions().Contains(extension->id())); |
| const std::string extension_id = extension->id(); |
| ExtensionPrefs* prefs = ExtensionPrefs::Get(profile()); |
| EXPECT_TRUE(prefs->HasDisableReason( |
| extension_id, disable_reason::DISABLE_CUSTODIAN_APPROVAL_REQUIRED)); |
| |
| // Simulate disabling Permissions for sites, apps and extensions |
| // in the testing supervised user service delegate used by the Management API. |
| GetSupervisedUserService() |
| ->SetSupervisedUserExtensionsMayRequestPermissionsPrefForTesting(false); |
| |
| // The supervised user trying to enable while Permissions for sites, apps and |
| // extensions is disabled should fail. |
| { |
| std::string error; |
| bool success = RunSetEnabledFunction(web_contents_.get(), extension_id, |
| /*use_user_gesture=*/true, |
| /*accept_dialog=*/true, &error); |
| EXPECT_FALSE(success); |
| EXPECT_FALSE(error.empty()); |
| EXPECT_TRUE(registry()->disabled_extensions().Contains(extension_id)); |
| |
| // The block dialog should have been shown. |
| EXPECT_EQ(supervised_user_delegate_->show_block_dialog_count(), 1); |
| } |
| |
| histogram_tester.ExpectUniqueSample( |
| SupervisedUserExtensionsMetricsRecorder::kEnablementHistogramName, |
| SupervisedUserExtensionsMetricsRecorder::EnablementState::kFailedToEnable, |
| 1); |
| histogram_tester.ExpectTotalCount( |
| SupervisedUserExtensionsMetricsRecorder::kEnablementHistogramName, 1); |
| EXPECT_EQ( |
| 1, |
| user_action_tester.GetActionCount( |
| SupervisedUserExtensionsMetricsRecorder::kFailedToEnableActionName)); |
| } |
| |
| // Tests enabling an extension via management API after it was disabled due to |
| // permission increase for supervised users. |
| // Prevents a regression to crbug/1068660. |
| TEST_F(ManagementApiSupervisedUserTest, SetEnabled_AfterIncreasedPermissions) { |
| // Preconditions. |
| ASSERT_TRUE(profile()->IsChild()); |
| |
| base::HistogramTester histogram_tester; |
| |
| base::FilePath base_path = data_dir().AppendASCII("permissions_increase"); |
| base::FilePath pem_path = base_path.AppendASCII("permissions.pem"); |
| |
| base::FilePath path = base_path.AppendASCII("v1"); |
| const Extension* extension = |
| PackAndInstallCRX(path, pem_path, INSTALL_WITHOUT_LOAD); |
| ASSERT_TRUE(extension); |
| // The extension should be installed but disabled pending custodian approval. |
| EXPECT_TRUE(registry()->disabled_extensions().Contains(extension->id())); |
| // Save the id, as |extension| will be destroyed during updating. |
| const std::string extension_id = extension->id(); |
| |
| // Simulate parent approval for the extension installation. |
| GetSupervisedUserService()->AddExtensionApproval(*extension); |
| // The extension should be enabled now. |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(extension_id)); |
| |
| // Should see 1 kApprovalGranted UMA metric. |
| histogram_tester.ExpectUniqueSample( |
| SupervisedUserExtensionsMetricsRecorder::kExtensionsHistogramName, |
| SupervisedUserExtensionsMetricsRecorder::UmaExtensionState:: |
| kApprovalGranted, |
| 1); |
| histogram_tester.ExpectTotalCount( |
| SupervisedUserExtensionsMetricsRecorder::kExtensionsHistogramName, 1); |
| |
| ExtensionPrefs* prefs = ExtensionPrefs::Get(profile()); |
| std::unique_ptr<const PermissionSet> known_perms = |
| prefs->GetGrantedPermissions(extension_id); |
| ASSERT_TRUE(known_perms); |
| // v1 extension doesn't have any permissions. |
| EXPECT_TRUE(known_perms->IsEmpty()); |
| |
| // Update to a new version with increased permissions. |
| path = base_path.AppendASCII("v2"); |
| PackCRXAndUpdateExtension(extension_id, path, pem_path, DISABLED); |
| // The extension should be disabled. |
| EXPECT_TRUE(registry()->disabled_extensions().Contains(extension_id)); |
| // Due to a permission increase, prefs will contain escalation information. |
| EXPECT_TRUE(prefs->DidExtensionEscalatePermissions(extension_id)); |
| |
| // Accept re-enable prompt with user gesture, expect the extension to be |
| // enabled. |
| { |
| // The supervised user will approve the additional permissions without |
| // parent approval. |
| std::string error; |
| bool success = RunSetEnabledFunction(web_contents_.get(), extension_id, |
| /*use_user_gesture=*/true, |
| /*accept_dialog=*/true, &error); |
| EXPECT_TRUE(success) << error; |
| EXPECT_TRUE(error.empty()); |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(extension_id)); |
| // Prefs will no longer contain the escalation information as the supervised |
| // user has accepted the increased permissions. |
| EXPECT_FALSE(prefs->DidExtensionEscalatePermissions(extension_id)); |
| } |
| |
| // Permissions for v2 extension should be granted now. |
| known_perms = prefs->GetGrantedPermissions(extension_id); |
| ASSERT_TRUE(known_perms); |
| EXPECT_FALSE(known_perms->IsEmpty()); |
| |
| // The parent approval dialog should have not appeared. |
| EXPECT_EQ(0, supervised_user_delegate_->show_dialog_count()); |
| |
| // Should see 1 kPermissionsIncreaseGranted UMA metric. |
| histogram_tester.ExpectBucketCount( |
| SupervisedUserExtensionsMetricsRecorder::kExtensionsHistogramName, |
| SupervisedUserExtensionsMetricsRecorder::UmaExtensionState:: |
| kPermissionsIncreaseGranted, |
| 1); |
| histogram_tester.ExpectTotalCount( |
| SupervisedUserExtensionsMetricsRecorder::kExtensionsHistogramName, 2); |
| } |
| |
| // Tests that supervised users can't approve permission updates by themselves |
| // when the "Permissions for sites, apps and extensions" toggle is off. |
| TEST_F(ManagementApiSupervisedUserTest, |
| SetEnabled_CantApprovePermissionUpdatesToggleOff) { |
| // Preconditions. |
| ASSERT_TRUE(profile()->IsChild()); |
| |
| base::HistogramTester histogram_tester; |
| |
| base::FilePath base_path = data_dir().AppendASCII("permissions_increase"); |
| base::FilePath pem_path = base_path.AppendASCII("permissions.pem"); |
| |
| base::FilePath path = base_path.AppendASCII("v1"); |
| const Extension* extension = |
| PackAndInstallCRX(path, pem_path, INSTALL_WITHOUT_LOAD); |
| ASSERT_TRUE(extension); |
| // The extension should be installed but disabled pending custodian approval. |
| EXPECT_TRUE(registry()->disabled_extensions().Contains(extension->id())); |
| // Save the id, as |extension| will be destroyed during updating. |
| const std::string extension_id = extension->id(); |
| |
| // Simulate parent approval for the extension installation. |
| GetSupervisedUserService()->AddExtensionApproval(*extension); |
| // The extension should be enabled now. |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(extension_id)); |
| |
| // There should be 1 kApprovalGranted UMA metric. |
| histogram_tester.ExpectUniqueSample( |
| SupervisedUserExtensionsMetricsRecorder::kExtensionsHistogramName, |
| SupervisedUserExtensionsMetricsRecorder::UmaExtensionState:: |
| kApprovalGranted, |
| 1); |
| histogram_tester.ExpectTotalCount( |
| SupervisedUserExtensionsMetricsRecorder::kExtensionsHistogramName, 1); |
| |
| ExtensionPrefs* prefs = ExtensionPrefs::Get(profile()); |
| std::unique_ptr<const PermissionSet> known_perms = |
| prefs->GetGrantedPermissions(extension_id); |
| ASSERT_TRUE(known_perms); |
| // v1 extension doesn't have any permissions. |
| EXPECT_TRUE(known_perms->IsEmpty()); |
| |
| // Update to a new version with increased permissions. |
| path = base_path.AppendASCII("v2"); |
| PackCRXAndUpdateExtension(extension_id, path, pem_path, DISABLED); |
| // The extension should be disabled. |
| EXPECT_TRUE(registry()->disabled_extensions().Contains(extension_id)); |
| // Due to a permission increase, prefs will contain escalation information. |
| EXPECT_TRUE(prefs->DidExtensionEscalatePermissions(extension_id)); |
| |
| // If the "Permissions for sites, apps and extensions" toggle is off, then the |
| // enable attempt should fail. |
| { |
| GetSupervisedUserService() |
| ->SetSupervisedUserExtensionsMayRequestPermissionsPrefForTesting(false); |
| std::string error; |
| bool success = RunSetEnabledFunction(web_contents_.get(), extension_id, |
| /*use_user_gesture=*/true, |
| /*accept_dialog=*/true, &error); |
| EXPECT_FALSE(success); |
| EXPECT_FALSE(error.empty()); |
| EXPECT_TRUE(registry()->disabled_extensions().Contains(extension_id)); |
| // Prefs will still contain the escalation information as the enable attempt |
| // failed. |
| EXPECT_TRUE(prefs->DidExtensionEscalatePermissions(extension_id)); |
| } |
| |
| // Permissions for v2 extension should not be granted. |
| known_perms = prefs->GetGrantedPermissions(extension_id); |
| ASSERT_TRUE(known_perms); |
| EXPECT_TRUE(known_perms->IsEmpty()); |
| |
| // The parent approval dialog should have not appeared. The parent approval |
| // dialog should never appear when the "Permissions for sites, apps and |
| // extensions" toggle is off. |
| EXPECT_EQ(0, supervised_user_delegate_->show_dialog_count()); |
| |
| // There should be no new UMA metrics. |
| histogram_tester.ExpectTotalCount( |
| SupervisedUserExtensionsMetricsRecorder::kExtensionsHistogramName, 1); |
| } |
| |
| // Tests that if an extension still requires parental consent, the supervised |
| // user approving it for permissions increase won't enable the extension and |
| // bypass parental consent. |
| // Prevents a regression to crbug/1070760. |
| TEST_F(ManagementApiSupervisedUserTest, |
| SetEnabled_CustodianApprovalRequiredAndPermissionsIncrease) { |
| // Preconditions. |
| ASSERT_TRUE(profile()->IsChild()); |
| |
| base::FilePath base_path = data_dir().AppendASCII("permissions_increase"); |
| base::FilePath pem_path = base_path.AppendASCII("permissions.pem"); |
| |
| base::FilePath path = base_path.AppendASCII("v1"); |
| const Extension* extension = |
| PackAndInstallCRX(path, pem_path, INSTALL_WITHOUT_LOAD); |
| ASSERT_TRUE(extension); |
| // The extension should be installed but disabled pending custodian approval. |
| EXPECT_TRUE(registry()->disabled_extensions().Contains(extension->id())); |
| // Save the id, as |extension| will be destroyed during updating. |
| const std::string extension_id = extension->id(); |
| |
| ExtensionPrefs* prefs = ExtensionPrefs::Get(profile()); |
| std::unique_ptr<const PermissionSet> known_perms = |
| prefs->GetGrantedPermissions(extension_id); |
| ASSERT_TRUE(known_perms); |
| // v1 extension doesn't have any permissions. |
| EXPECT_TRUE(known_perms->IsEmpty()); |
| |
| // Update to a new version with increased permissions. |
| path = base_path.AppendASCII("v2"); |
| PackCRXAndUpdateExtension(extension_id, path, pem_path, DISABLED); |
| // The extension should still be disabled. |
| EXPECT_TRUE(registry()->disabled_extensions().Contains(extension_id)); |
| // This extension has two concurrent disable reasons. |
| EXPECT_TRUE(prefs->HasDisableReason( |
| extension_id, disable_reason::DISABLE_PERMISSIONS_INCREASE)); |
| EXPECT_TRUE(prefs->HasDisableReason( |
| extension_id, disable_reason::DISABLE_CUSTODIAN_APPROVAL_REQUIRED)); |
| |
| // The supervised user trying to enable without parent approval should fail. |
| { |
| std::string error; |
| bool success = RunSetEnabledFunction(web_contents_.get(), extension_id, |
| /*use_user_gesture=*/true, |
| /*accept_dialog=*/true, &error); |
| EXPECT_FALSE(success); |
| EXPECT_FALSE(error.empty()); |
| EXPECT_TRUE(registry()->disabled_extensions().Contains(extension_id)); |
| // Both disable reasons should still be present. |
| EXPECT_TRUE(prefs->HasDisableReason( |
| extension_id, disable_reason::DISABLE_PERMISSIONS_INCREASE)); |
| EXPECT_TRUE(prefs->HasDisableReason( |
| extension_id, disable_reason::DISABLE_CUSTODIAN_APPROVAL_REQUIRED)); |
| } |
| |
| // Permissions for v2 extension should not be granted. |
| known_perms = prefs->GetGrantedPermissions(extension_id); |
| ASSERT_TRUE(known_perms); |
| EXPECT_TRUE(known_perms->IsEmpty()); |
| |
| // The parent approval dialog should have appeared. |
| EXPECT_EQ(1, supervised_user_delegate_->show_dialog_count()); |
| |
| // Now try again with parent approval, and this should succeed. |
| { |
| supervised_user_delegate_->set_next_parent_permission_dialog_result( |
| SupervisedUserExtensionsDelegate::ParentPermissionDialogResult:: |
| kParentPermissionReceived); |
| std::string error; |
| bool success = RunSetEnabledFunction(web_contents_.get(), extension_id, |
| /*use_user_gesture=*/true, |
| /*accept_dialog=*/true, &error); |
| EXPECT_TRUE(success) << error; |
| EXPECT_TRUE(error.empty()); |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(extension_id)); |
| // All disable reasons are gone. |
| EXPECT_FALSE(prefs->DidExtensionEscalatePermissions(extension_id)); |
| EXPECT_FALSE(prefs->HasDisableReason( |
| extension_id, disable_reason::DISABLE_CUSTODIAN_APPROVAL_REQUIRED)); |
| } |
| |
| // Permissions for v2 extension should now be granted. |
| known_perms = prefs->GetGrantedPermissions(extension_id); |
| ASSERT_TRUE(known_perms); |
| EXPECT_FALSE(known_perms->IsEmpty()); |
| |
| // The parent approval dialog should have appeared again. |
| EXPECT_EQ(2, supervised_user_delegate_->show_dialog_count()); |
| } |
| |
| // Tests that trying to enable an extension with parent approval for supervised |
| // users still fails, if there's unsupported requirements. |
| TEST_F(ManagementApiSupervisedUserTest, SetEnabled_UnsupportedRequirement) { |
| // Preconditions. |
| ASSERT_TRUE(profile()->IsChild()); |
| ASSERT_EQ(0, supervised_user_delegate_->show_dialog_count()); |
| |
| // No WebGL will be the unsupported requirement. |
| content::GpuDataManager::GetInstance()->BlocklistWebGLForTesting(); |
| |
| base::FilePath base_path = data_dir().AppendASCII("requirements"); |
| base::FilePath pem_path = base_path.AppendASCII("v1_good.pem"); |
| base::FilePath path = base_path.AppendASCII("v2_bad_requirements"); |
| const Extension* extension = |
| PackAndInstallCRX(path, pem_path, INSTALL_WITHOUT_LOAD); |
| ASSERT_TRUE(extension); |
| // The extension should be installed but disabled pending custodian approval |
| // and unsupported requirements. |
| EXPECT_TRUE(registry()->disabled_extensions().Contains(extension->id())); |
| ExtensionPrefs* prefs = ExtensionPrefs::Get(profile()); |
| EXPECT_TRUE(prefs->HasDisableReason( |
| extension->id(), disable_reason::DISABLE_CUSTODIAN_APPROVAL_REQUIRED)); |
| EXPECT_TRUE(prefs->HasDisableReason( |
| extension->id(), disable_reason::DISABLE_UNSUPPORTED_REQUIREMENT)); |
| |
| // Parent approval should fail because of the unsupported requirements. |
| { |
| supervised_user_delegate_->set_next_parent_permission_dialog_result( |
| SupervisedUserExtensionsDelegate::ParentPermissionDialogResult:: |
| kParentPermissionReceived); |
| std::string error; |
| bool success = RunSetEnabledFunction(web_contents_.get(), extension->id(), |
| /*user_user_gesture=*/true, |
| /*accept_dialog=*/true, &error); |
| EXPECT_FALSE(success); |
| EXPECT_FALSE(error.empty()); |
| // The parent permission dialog was never opened. |
| EXPECT_EQ(0, supervised_user_delegate_->show_dialog_count()); |
| EXPECT_TRUE(registry()->disabled_extensions().Contains(extension->id())); |
| // The extension should still require parent approval. |
| EXPECT_TRUE(prefs->HasDisableReason( |
| extension->id(), disable_reason::DISABLE_CUSTODIAN_APPROVAL_REQUIRED)); |
| EXPECT_TRUE(prefs->HasDisableReason( |
| extension->id(), disable_reason::DISABLE_UNSUPPORTED_REQUIREMENT)); |
| } |
| } |
| |
| // Tests UMA metrics related to supervised users enabling and disabling |
| // extensions. |
| TEST_F(ManagementApiSupervisedUserTest, SetEnabledDisabled_UmaMetrics) { |
| base::HistogramTester histogram_tester; |
| base::UserActionTester user_action_tester; |
| |
| base::FilePath base_path = data_dir().AppendASCII("permissions_increase"); |
| base::FilePath pem_path = base_path.AppendASCII("permissions.pem"); |
| |
| base::FilePath path = base_path.AppendASCII("v1"); |
| const Extension* extension = |
| PackAndInstallCRX(path, pem_path, INSTALL_WITHOUT_LOAD); |
| ASSERT_TRUE(extension); |
| |
| // The parent will approve. |
| supervised_user_delegate_->set_next_parent_permission_dialog_result( |
| SupervisedUserExtensionsDelegate::ParentPermissionDialogResult:: |
| kParentPermissionReceived); |
| |
| RunSetEnabledFunction(web_contents_.get(), extension->id(), |
| /*use_user_gesture=*/true, /*accept_dialog=*/true, |
| nullptr, /*enabled=*/true); |
| histogram_tester.ExpectUniqueSample( |
| SupervisedUserExtensionsMetricsRecorder::kEnablementHistogramName, |
| SupervisedUserExtensionsMetricsRecorder::EnablementState::kEnabled, 1); |
| histogram_tester.ExpectTotalCount( |
| SupervisedUserExtensionsMetricsRecorder::kEnablementHistogramName, 1); |
| EXPECT_EQ(1, |
| user_action_tester.GetActionCount( |
| SupervisedUserExtensionsMetricsRecorder::kEnabledActionName)); |
| EXPECT_EQ(0, |
| user_action_tester.GetActionCount( |
| SupervisedUserExtensionsMetricsRecorder::kDisabledActionName)); |
| |
| // Simulate supervised user disabling extension. |
| RunSetEnabledFunction(web_contents_.get(), extension->id(), |
| /*use_user_gesture=*/true, /*accept_dialog=*/true, |
| nullptr, /*enabled=*/false); |
| histogram_tester.ExpectBucketCount( |
| SupervisedUserExtensionsMetricsRecorder::kEnablementHistogramName, |
| SupervisedUserExtensionsMetricsRecorder::EnablementState::kDisabled, 1); |
| histogram_tester.ExpectTotalCount( |
| SupervisedUserExtensionsMetricsRecorder::kEnablementHistogramName, 2); |
| EXPECT_EQ(1, |
| user_action_tester.GetActionCount( |
| SupervisedUserExtensionsMetricsRecorder::kEnabledActionName)); |
| EXPECT_EQ(1, |
| user_action_tester.GetActionCount( |
| SupervisedUserExtensionsMetricsRecorder::kDisabledActionName)); |
| |
| // Simulate supervised user re-enabling extension. |
| RunSetEnabledFunction(web_contents_.get(), extension->id(), |
| /*use_user_gesture=*/true, /*accept_dialog=*/true, |
| nullptr, /*enabled=*/true); |
| histogram_tester.ExpectBucketCount( |
| SupervisedUserExtensionsMetricsRecorder::kEnablementHistogramName, |
| SupervisedUserExtensionsMetricsRecorder::EnablementState::kEnabled, 2); |
| histogram_tester.ExpectTotalCount( |
| SupervisedUserExtensionsMetricsRecorder::kEnablementHistogramName, 3); |
| EXPECT_EQ(2, |
| user_action_tester.GetActionCount( |
| SupervisedUserExtensionsMetricsRecorder::kEnabledActionName)); |
| EXPECT_EQ(1, |
| user_action_tester.GetActionCount( |
| SupervisedUserExtensionsMetricsRecorder::kDisabledActionName)); |
| } |
| |
| // Tests for supervised users (child accounts) with additional setup code. |
| class ManagementApiSupervisedUserTestWithSetup |
| : public ManagementApiSupervisedUserTest { |
| public: |
| ManagementApiSupervisedUserTestWithSetup() = default; |
| ~ManagementApiSupervisedUserTestWithSetup() override = default; |
| |
| void SetUp() override { |
| ManagementApiSupervisedUserTest::SetUp(); |
| |
| // Install a ManagementAPIDelegate to sense extension enable. |
| delegate_ = new TestManagementAPIDelegate; |
| management_api_->set_delegate_for_test(base::WrapUnique(delegate_)); |
| |
| // Add a generic extension. |
| extension_ = ExtensionBuilder("Test").Build(); |
| service()->AddExtension(extension_.get()); |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(extension_->id())); |
| } |
| |
| TestManagementAPIDelegate* delegate_ = nullptr; |
| scoped_refptr<const Extension> extension_; |
| }; |
| |
| TEST_F(ManagementApiSupervisedUserTestWithSetup, SetEnabled_ParentApproves) { |
| // Preconditions. |
| ASSERT_TRUE(profile()->IsChild()); |
| ASSERT_EQ(0, delegate_->enable_count_); |
| ASSERT_EQ(0, supervised_user_delegate_->show_dialog_count()); |
| |
| // Start with a disabled extension that needs parent permission. |
| service()->DisableExtension( |
| extension_->id(), disable_reason::DISABLE_CUSTODIAN_APPROVAL_REQUIRED); |
| |
| // The parent will approve. |
| supervised_user_delegate_->set_next_parent_permission_dialog_result( |
| SupervisedUserExtensionsDelegate::ParentPermissionDialogResult:: |
| kParentPermissionReceived); |
| |
| // Simulate a call to chrome.management.setEnabled(). It should succeed. |
| std::string error; |
| bool success = RunSetEnabledFunction(web_contents_.get(), extension_->id(), |
| /*use_user_gesture=*/true, |
| /*accept_dialog=*/true, &error); |
| EXPECT_TRUE(success) << error; |
| EXPECT_TRUE(error.empty()); |
| |
| // Parent permission dialog was opened. |
| EXPECT_EQ(1, supervised_user_delegate_->show_dialog_count()); |
| |
| // Extension was enabled. |
| EXPECT_EQ(1, delegate_->enable_count_); |
| } |
| |
| TEST_F(ManagementApiSupervisedUserTestWithSetup, SetEnabled_ParentDenies) { |
| // Start with a disabled extension that needs parent permission. |
| service()->DisableExtension( |
| extension_->id(), disable_reason::DISABLE_CUSTODIAN_APPROVAL_REQUIRED); |
| |
| // The parent will deny the next dialog. |
| supervised_user_delegate_->set_next_parent_permission_dialog_result( |
| SupervisedUserExtensionsDelegate::ParentPermissionDialogResult:: |
| kParentPermissionCanceled); |
| |
| // Simulate a call to chrome.management.setEnabled(). It should not succeed. |
| std::string error; |
| bool success = RunSetEnabledFunction(web_contents_.get(), extension_->id(), |
| /*use_user_gesture=*/true, |
| /*accept_dialog=*/true, &error); |
| EXPECT_FALSE(success); |
| EXPECT_FALSE(error.empty()); |
| |
| // Parent permission dialog was opened. |
| EXPECT_EQ(1, supervised_user_delegate_->show_dialog_count()); |
| |
| // Extension was not enabled. |
| EXPECT_EQ(0, delegate_->enable_count_); |
| } |
| |
| TEST_F(ManagementApiSupervisedUserTestWithSetup, SetEnabled_DialogFails) { |
| // Start with a disabled extension that needs parent permission. |
| service()->DisableExtension( |
| extension_->id(), disable_reason::DISABLE_CUSTODIAN_APPROVAL_REQUIRED); |
| |
| // The next dialog will close due to a failure (e.g. network failure while |
| // looking up parent information). |
| supervised_user_delegate_->set_next_parent_permission_dialog_result( |
| SupervisedUserExtensionsDelegate::ParentPermissionDialogResult:: |
| kParentPermissionFailed); |
| |
| // Simulate a call to chrome.management.setEnabled(). It should not succeed. |
| std::string error; |
| bool success = RunSetEnabledFunction(web_contents_.get(), extension_->id(), |
| /*use_user_gesture=*/true, |
| /*accept_dialog=*/true, &error); |
| EXPECT_FALSE(success); |
| EXPECT_FALSE(error.empty()); |
| |
| // Extension was not enabled. |
| EXPECT_EQ(0, delegate_->enable_count_); |
| } |
| |
| TEST_F(ManagementApiSupervisedUserTestWithSetup, SetEnabled_PreviouslyAllowed) { |
| // Disable the extension. |
| service()->DisableExtension(extension_->id(), |
| disable_reason::DISABLE_USER_ACTION); |
| |
| // Simulate previous parent approval. |
| GetSupervisedUserService()->AddExtensionApproval(*extension_); |
| |
| // Simulate a call to chrome.management.setEnabled(). |
| std::string error; |
| bool success = RunSetEnabledFunction(web_contents_.get(), extension_->id(), |
| /*use_user_gesture=*/true, |
| /*accept_dialog=*/true, &error); |
| EXPECT_TRUE(success) << error; |
| EXPECT_TRUE(error.empty()); |
| |
| // Parent permission dialog was not opened. |
| EXPECT_EQ(0, supervised_user_delegate_->show_dialog_count()); |
| } |
| |
| // Tests launching the Parent Permission Dialog from a background page, where |
| // there isn't active web contents. The parent approves the request. |
| TEST_F(ManagementApiSupervisedUserTestWithSetup, |
| SetEnabled_ParentPermissionApprovedFromBackgroundPage) { |
| // Preconditions. |
| ASSERT_TRUE(profile()->IsChild()); |
| |
| // Start with a disabled extension that needs parent permission. |
| service()->DisableExtension( |
| extension_->id(), disable_reason::DISABLE_CUSTODIAN_APPROVAL_REQUIRED); |
| |
| // The parent will approve. |
| supervised_user_delegate_->set_next_parent_permission_dialog_result( |
| SupervisedUserExtensionsDelegate::ParentPermissionDialogResult:: |
| kParentPermissionReceived); |
| |
| // Simulate a call to chrome.management.setEnabled(). It should succeed |
| // despite a lack of web contents. |
| std::string error; |
| bool success = RunSetEnabledFunction( |
| /*web_contents=*/nullptr, extension_->id(), /*use_user_gesture=*/true, |
| /*accept_dialog=*/true, &error); |
| EXPECT_TRUE(success); |
| EXPECT_TRUE(error.empty()); |
| |
| // Parent Permission Dialog still opened despite the lack of web contents. |
| EXPECT_EQ(1, supervised_user_delegate_->show_dialog_count()); |
| EXPECT_EQ(0, supervised_user_delegate_->show_block_dialog_count()); |
| |
| // Extension is now enabled. |
| EXPECT_EQ(1, delegate_->enable_count_); |
| } |
| |
| // Tests launching the Parent Permission Dialog from a background page, where |
| // there isn't active web contents. The parent cancels the request. |
| TEST_F(ManagementApiSupervisedUserTestWithSetup, |
| SetEnabled_ParentPermissionCanceledFromBackgroundPage) { |
| // Preconditions. |
| ASSERT_TRUE(profile()->IsChild()); |
| |
| // Start with a disabled extension that needs parent permission. |
| service()->DisableExtension( |
| extension_->id(), disable_reason::DISABLE_CUSTODIAN_APPROVAL_REQUIRED); |
| |
| // The parent will cancel. |
| supervised_user_delegate_->set_next_parent_permission_dialog_result( |
| SupervisedUserExtensionsDelegate::ParentPermissionDialogResult:: |
| kParentPermissionCanceled); |
| |
| // Simulate a call to chrome.management.setEnabled() with no web contents. |
| std::string error; |
| bool success = RunSetEnabledFunction( |
| /*web_contents=*/nullptr, extension_->id(), /*use_user_gesture=*/true, |
| /*accept_dialog=*/true, &error); |
| EXPECT_FALSE(success); |
| EXPECT_EQ(extension_management_api_constants::kUserDidNotReEnableError, |
| error); |
| |
| // Parent Permission Dialog still opened despite the lack of web contents. |
| EXPECT_EQ(1, supervised_user_delegate_->show_dialog_count()); |
| EXPECT_EQ(0, supervised_user_delegate_->show_block_dialog_count()); |
| |
| // Extension was not enabled. |
| EXPECT_EQ(0, delegate_->enable_count_); |
| } |
| |
| // Tests launching the Parent Permission Dialog from a background page, where |
| // there isn't active web contents. The request will fail due to some sort of |
| // error, such as a network error. |
| TEST_F(ManagementApiSupervisedUserTestWithSetup, |
| SetEnabled_ParentPermissionFailedFromBackgroundPage) { |
| // Preconditions. |
| ASSERT_TRUE(profile()->IsChild()); |
| |
| // Start with a disabled extension that needs parent permission. |
| service()->DisableExtension( |
| extension_->id(), disable_reason::DISABLE_CUSTODIAN_APPROVAL_REQUIRED); |
| |
| // The request will fail. |
| supervised_user_delegate_->set_next_parent_permission_dialog_result( |
| SupervisedUserExtensionsDelegate::ParentPermissionDialogResult:: |
| kParentPermissionFailed); |
| |
| // Simulate a call to chrome.management.setEnabled() with no web contents. |
| std::string error; |
| bool success = RunSetEnabledFunction( |
| /*web_contents=*/nullptr, extension_->id(), /*use_user_gesture=*/true, |
| /*accept_dialog=*/true, &error); |
| EXPECT_FALSE(success); |
| EXPECT_EQ(extension_management_api_constants::kParentPermissionFailedError, |
| error); |
| |
| // Parent Permission Dialog still opened despite the lack of web contents. |
| EXPECT_EQ(1, supervised_user_delegate_->show_dialog_count()); |
| EXPECT_EQ(0, supervised_user_delegate_->show_block_dialog_count()); |
| |
| // Extension was not enabled. |
| EXPECT_EQ(0, delegate_->enable_count_); |
| } |
| |
| // Tests launching the Extension Install Blocked By Parent Dialog from a |
| // background page, where there isn't active web contents. |
| TEST_F(ManagementApiSupervisedUserTestWithSetup, |
| SetEnabled_ExtensionInstallBlockedByParentFromBackgroundPage) { |
| // Preconditions. |
| ASSERT_TRUE(profile()->IsChild()); |
| |
| // Start with a disabled extension that needs parent permission. |
| service()->DisableExtension( |
| extension_->id(), disable_reason::DISABLE_CUSTODIAN_APPROVAL_REQUIRED); |
| |
| // Simulate the parent disabling the "Permissions for sites, apps and |
| // extensions" toggle. |
| GetSupervisedUserService() |
| ->SetSupervisedUserExtensionsMayRequestPermissionsPrefForTesting(false); |
| |
| // Simulate a call to chrome.management.setEnabled(). The enable attempt |
| // should be blocked. |
| std::string error; |
| bool success = RunSetEnabledFunction( |
| /*web_contents=*/nullptr, extension_->id(), /*use_user_gesture=*/true, |
| /*accept_dialog=*/true, &error); |
| EXPECT_FALSE(success); |
| const std::string expected_error = ErrorUtils::FormatErrorMessage( |
| extension_management_api_constants::kUserCantModifyError, |
| extension_->id()); |
| EXPECT_EQ(expected_error, error); |
| |
| // The Extension Install Blocked By Parent Dialog should have opened despite |
| // the lack of web contents. |
| EXPECT_EQ(1, supervised_user_delegate_->show_block_dialog_count()); |
| EXPECT_EQ(0, supervised_user_delegate_->show_dialog_count()); |
| |
| // Extension was not enabled. |
| EXPECT_EQ(0, delegate_->enable_count_); |
| } |
| #endif // BUILDFLAG(ENABLE_SUPERVISED_USERS) |
| |
| } // namespace |
| } // namespace extensions |