Commit b5b650e0 authored by Toby Huang's avatar Toby Huang Committed by Commit Bot

Add tests for parent permission and error dialogs on Management API

This CL adds browser tests to parent_permission_dialog_view_
browsertest.cc for the Parent Permission and the Extension Install
Blocked By Parent dialogs launching from the extension Management API.

This CL also adds unit tests to management_api_unittest.cc for the
same two dialogs launching from a background page, where there's no
active web contents.

Bug: 1083275
Change-Id: I34db7aee30d5153b8dbc348572522e0f16555d10
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2220816
Commit-Queue: Toby Huang <tobyhuang@chromium.org>
Reviewed-by: default avatarScott Violet <sky@chromium.org>
Reviewed-by: default avatarDan S <danan@chromium.org>
Reviewed-by: default avatarDevlin <rdevlin.cronin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#786157}
parent 47a0e3e7
......@@ -46,6 +46,8 @@
#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
namespace extensions {
......@@ -129,6 +131,7 @@ bool ManagementApiUnitTest::RunSetEnabledFunction(
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);
......@@ -1518,6 +1521,143 @@ TEST_F(ManagementApiSupervisedUserTestWithSetup, SetEnabled_PreviouslyAllowed) {
// 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
......
......@@ -7,12 +7,15 @@
#include <memory>
#include <vector>
#include "base/memory/scoped_refptr.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/metrics/user_action_tester.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/chromeos/login/test/fake_gaia_mixin.h"
#include "chrome/browser/extensions/chrome_test_extension_loader.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_util.h"
#include "chrome/browser/profiles/profile.h"
......@@ -28,15 +31,19 @@
#include "chrome/browser/ui/extensions/extension_enable_flow_test_delegate.h"
#include "chrome/browser/ui/test/test_browser_dialog.h"
#include "chrome/browser/ui/views/supervised_user/parent_permission_dialog_view.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/test/base/mixin_based_in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/signin/public/identity_manager/identity_test_environment.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/test_launcher.h"
#include "content/public/test/test_utils.h"
#include "extensions/browser/disable_reason.h"
#include "extensions/browser/extension_dialog_auto_confirm.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_system.h"
#include "extensions/common/extension_builder.h"
#include "extensions/test/result_catcher.h"
#include "google_apis/gaia/fake_gaia.h"
#include "google_apis/gaia/gaia_auth_consumer.h"
......@@ -255,7 +262,10 @@ class ParentPermissionDialogViewTest
ParentPermissionDialog::Result result_;
chromeos::LoggedInUserMixin logged_in_user_mixin_{
&mixin_host_, chromeos::LoggedInUserMixin::LogInType::kChild,
&mixin_host_,
// Simulate Gellerization / Adding Supervision to load extensions.
content::IsPreTest() ? chromeos::LoggedInUserMixin::LogInType::kRegular
: chromeos::LoggedInUserMixin::LogInType::kChild,
embedded_test_server(), this};
// Closure that is triggered once the dialog is shown.
......@@ -333,6 +343,8 @@ IN_PROC_BROWSER_TEST_F(ParentPermissionDialogViewTest,
SupervisedUserExtensionsMetricsRecorder::ParentPermissionDialogState::
kParentApproved,
1);
// The total histogram count is 2 (one for kOpened and one for
// kParentApproved).
histogram_tester.ExpectTotalCount(SupervisedUserExtensionsMetricsRecorder::
kParentPermissionDialogHistogramName,
2);
......@@ -371,6 +383,7 @@ IN_PROC_BROWSER_TEST_F(ParentPermissionDialogViewTest,
SupervisedUserExtensionsMetricsRecorder::
ParentPermissionDialogState::kFailed,
1);
// The total histogram count is 2 (one for kOpened and one for kFailed).
histogram_tester.ExpectTotalCount(SupervisedUserExtensionsMetricsRecorder::
kParentPermissionDialogHistogramName,
2);
......@@ -405,6 +418,8 @@ IN_PROC_BROWSER_TEST_F(ParentPermissionDialogViewTest,
SupervisedUserExtensionsMetricsRecorder::ParentPermissionDialogState::
kParentCanceled,
1);
// The total histogram count is 2 (one for kOpened and one for
// kParentCanceled).
histogram_tester.ExpectTotalCount(SupervisedUserExtensionsMetricsRecorder::
kParentPermissionDialogHistogramName,
2);
......@@ -459,6 +474,8 @@ IN_PROC_BROWSER_TEST_F(ExtensionEnableFlowTestSupervised,
SupervisedUserExtensionsMetricsRecorder::ParentPermissionDialogState::
kParentApproved,
1);
// The total histogram count is 2 (one for kOpened and one for
// kParentApproved).
histogram_tester.ExpectTotalCount(SupervisedUserExtensionsMetricsRecorder::
kParentPermissionDialogHistogramName,
2);
......@@ -500,6 +517,8 @@ IN_PROC_BROWSER_TEST_F(ExtensionEnableFlowTestSupervised,
SupervisedUserExtensionsMetricsRecorder::ParentPermissionDialogState::
kParentCanceled,
1);
// The total histogram count is 2 (one for kOpened and one for
// kParentCanceled).
histogram_tester.ExpectTotalCount(SupervisedUserExtensionsMetricsRecorder::
kParentPermissionDialogHistogramName,
2);
......@@ -548,3 +567,198 @@ IN_PROC_BROWSER_TEST_F(ExtensionEnableFlowTestSupervised,
SupervisedUserExtensionsMetricsRecorder::EnablementState::kFailedToEnable,
1);
}
class ExtensionManagementApiTestSupervised
: public ParentPermissionDialogViewTest {
public:
void SetUpOnMainThread() override {
ParentPermissionDialogViewTest::SetUpOnMainThread();
// Loads the extension as a regular user and then simulates Gellerization /
// Adding Supervision since supervised users can't load extensions directly.
if (content::IsPreTest()) {
LoadNamedExtension("disabled_extension");
LoadNamedExtension("test");
} else {
// In addition to the two extensions from the PRE test, there's one more
// test extension from the ParentPermissionDialogViewTest parent class.
EXPECT_EQ(3u, extension_registry()->disabled_extensions().size());
for (const auto& e : extension_registry()->disabled_extensions()) {
if (e->name() == "disabled_extension") {
disabled_extension_id_ = e->id();
} else if (e->name() == "Extension Management API Test") {
test_extension_id_ = e->id();
}
}
EXPECT_FALSE(disabled_extension_id_.empty());
EXPECT_FALSE(test_extension_id_.empty());
// Approve the extension for running the test.
GetSupervisedUserService()->UpdateApprovedExtensionForTesting(
test_extension_id_,
SupervisedUserService::ApprovedExtensionChange::kAdd);
}
}
protected:
void LoadNamedExtension(const std::string& name) {
base::FilePath test_data_dir;
base::PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir);
test_data_dir = test_data_dir.AppendASCII("extensions");
test_data_dir = test_data_dir.AppendASCII("api_test");
extensions::ChromeTestExtensionLoader loader(browser()->profile());
base::FilePath basedir = test_data_dir.AppendASCII("management");
scoped_refptr<const extensions::Extension> extension =
loader.LoadExtension(basedir.AppendASCII(name));
ASSERT_TRUE(extension);
}
bool RunManagementSubtest(const std::string& page_url,
std::string* error_message) {
DCHECK(!test_extension_id_.empty()) << "test_extension_id_ is required";
DCHECK(!page_url.empty()) << "Argument page_url is required.";
const extensions::Extension* test_extension =
extension_registry()->enabled_extensions().GetByID(test_extension_id_);
DCHECK(test_extension) << "Test extension is not enabled";
extensions::ResultCatcher catcher;
GURL url = test_extension->GetResourceURL(page_url);
DCHECK(url.is_valid());
ui_test_utils::NavigateToURL(browser(), url);
if (catcher.GetNextResult())
return true;
if (error_message)
*error_message = catcher.message();
return false;
}
std::string disabled_extension_id_;
std::string test_extension_id_;
};
IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTestSupervised,
PRE_ParentPermissionGrantedForEnable) {
ASSERT_FALSE(browser()->profile()->IsChild());
}
// Tests launching the Parent Permission Dialog from the management api when the
// extension hasn't already been approved.
IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTestSupervised,
ParentPermissionGrantedForEnable) {
base::HistogramTester histogram_tester;
ASSERT_TRUE(browser()->profile()->IsChild());
set_next_reauth_status(GaiaAuthConsumer::ReAuthProofTokenStatus::kSuccess);
set_next_dialog_action(NextDialogAction::kAccept);
std::string error;
EXPECT_TRUE(RunManagementSubtest(
"supervised_user_permission_granted_for_enable.html", &error))
<< error;
// The extension should be enabled now.
EXPECT_TRUE(extension_registry()->enabled_extensions().Contains(
disabled_extension_id_));
// Proof that the Parent Permission Dialog launched.
histogram_tester.ExpectBucketCount(SupervisedUserExtensionsMetricsRecorder::
kParentPermissionDialogHistogramName,
SupervisedUserExtensionsMetricsRecorder::
ParentPermissionDialogState::kOpened,
1);
histogram_tester.ExpectBucketCount(
SupervisedUserExtensionsMetricsRecorder::
kParentPermissionDialogHistogramName,
SupervisedUserExtensionsMetricsRecorder::ParentPermissionDialogState::
kParentApproved,
1);
// The total histogram count is 2 (one for kOpened and one for
// kParentApproved).
histogram_tester.ExpectTotalCount(SupervisedUserExtensionsMetricsRecorder::
kParentPermissionDialogHistogramName,
2);
}
IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTestSupervised,
PRE_ParentPermissionNotGrantedForEnable) {
ASSERT_FALSE(browser()->profile()->IsChild());
}
// Tests that extensions are not enabled after the parent permission dialog is
// cancelled.
IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTestSupervised,
ParentPermissionNotGrantedForEnable) {
base::HistogramTester histogram_tester;
ASSERT_TRUE(browser()->profile()->IsChild());
set_next_dialog_action(NextDialogAction::kCancel);
std::string error;
EXPECT_TRUE(RunManagementSubtest(
"supervised_user_permission_not_granted_for_enable.html", &error))
<< error;
// The extension should still be disabled.
EXPECT_TRUE(extension_registry()->disabled_extensions().Contains(
disabled_extension_id_));
// Proof that the Parent Permission Dialog launched.
histogram_tester.ExpectBucketCount(SupervisedUserExtensionsMetricsRecorder::
kParentPermissionDialogHistogramName,
SupervisedUserExtensionsMetricsRecorder::
ParentPermissionDialogState::kOpened,
1);
histogram_tester.ExpectBucketCount(
SupervisedUserExtensionsMetricsRecorder::
kParentPermissionDialogHistogramName,
SupervisedUserExtensionsMetricsRecorder::ParentPermissionDialogState::
kParentCanceled,
1);
// The total histogram count is 2 (one for kOpened and one for
// kParentCanceled).
histogram_tester.ExpectTotalCount(SupervisedUserExtensionsMetricsRecorder::
kParentPermissionDialogHistogramName,
2);
}
IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTestSupervised,
PRE_ParentBlockedExtensionEnable) {
ASSERT_FALSE(browser()->profile()->IsChild());
}
// Tests that the Parent Permission Dialog doesn't appear at all when the parent
// has disabled the "Permissions for sites, apps and extensions" toggle, and the
// supervised user sees the Extension Install Blocked By Parent error dialog
// instead.
IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTestSupervised,
ParentBlockedExtensionEnable) {
base::HistogramTester histogram_tester;
ASSERT_TRUE(browser()->profile()->IsChild());
// Simulate the parent disabling the "Permissions for sites, apps and
// extensions" toggle.
SetSupervisedUserExtensionsMayRequestPermissionsPref(false);
extensions::ScopedTestDialogAutoConfirm auto_confirm(
extensions::ScopedTestDialogAutoConfirm::ACCEPT);
std::string error;
EXPECT_TRUE(RunManagementSubtest(
"supervised_user_parent_disabled_permission_for_enable.html", &error))
<< error;
// The extension should still be disabled.
EXPECT_TRUE(extension_registry()->disabled_extensions().Contains(
disabled_extension_id_));
// Proof that the Parent Permission Dialog didn't launch.
histogram_tester.ExpectTotalCount(SupervisedUserExtensionsMetricsRecorder::
kParentPermissionDialogHistogramName,
0);
// Proof that the Extension Install Blocked By Parent Dialog launched instead.
histogram_tester.ExpectUniqueSample(
SupervisedUserExtensionsMetricsRecorder::kEnablementHistogramName,
SupervisedUserExtensionsMetricsRecorder::EnablementState::kFailedToEnable,
1);
}
......@@ -4,6 +4,7 @@
var assertEq = chrome.test.assertEq;
var assertFalse = chrome.test.assertFalse;
var assertLastError = chrome.test.assertLastError;
var assertNoLastError = chrome.test.assertNoLastError;
var assertTrue = chrome.test.assertTrue;
var fail = chrome.test.fail;
......
<!--
* Copyright 2020 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.
-->
<script src="common.js"></script>
<script src="supervised_user_parent_disabled_permission_for_enable.js"></script>
// Copyright 2020 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.
var tests = [
// Tries to enable a disabled extension.
function enable() {
chrome.management.getAll(function(items) {
var disabledItem = getItemNamed(items, 'disabled_extension');
var expectedError =
`Extension ${disabledItem.id} cannot be modified by user.`;
checkItem(disabledItem, 'disabled_extension', false, 'extension');
chrome.management.setEnabled(disabledItem.id, true, function() {
assertLastError(expectedError);
chrome.management.get(disabledItem.id, function(stillDisabledItem) {
checkItem(
stillDisabledItem, 'disabled_extension', false, 'extension');
succeed();
});
});
});
}
];
chrome.test.runTests(tests);
<!--
* Copyright 2020 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.
-->
<script src="common.js"></script>
<script src="supervised_user_permission_granted_for_enable.js"></script>
// Copyright 2020 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.
var tests = [
// Tries to enable a disabled extension.
function enable() {
let onEnabledPromise = new Promise((resolve) => {
chrome.management.onEnabled.addListener(function(info) {
assertEq(info.name, 'disabled_extension');
resolve();
});
});
let setEnabledPromise = new Promise((resolve) => {
chrome.management.getAll(function(items) {
var disabledItem = getItemNamed(items, 'disabled_extension');
checkItem(disabledItem, 'disabled_extension', false, 'extension');
chrome.management.setEnabled(disabledItem.id, true, function() {
chrome.management.get(disabledItem.id, function(nowEnabledItem) {
checkItem(nowEnabledItem, 'disabled_extension', true, 'extension');
resolve();
});
});
});
});
Promise.all([onEnabledPromise, setEnabledPromise]).then(() => {
succeed();
});
}
];
chrome.test.runTests(tests);
<!--
* Copyright 2020 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.
-->
<script src="common.js"></script>
<script src="supervised_user_permission_not_granted_for_enable.js"></script>
// Copyright 2020 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.
var tests = [
// Tries to enable a disabled extension.
function enable() {
chrome.management.getAll(function(items) {
var disabledItem = getItemNamed(items, 'disabled_extension');
var expectedError = 'The user did not accept the re-enable dialog.';
checkItem(disabledItem, 'disabled_extension', false, 'extension');
chrome.management.setEnabled(disabledItem.id, true, function() {
assertLastError(expectedError);
chrome.management.get(disabledItem.id, function(stillDisabledItem) {
checkItem(
stillDisabledItem, 'disabled_extension', false, 'extension');
succeed();
});
});
});
}
];
chrome.test.runTests(tests);
......@@ -513,7 +513,7 @@ void ManagementSetEnabledFunction::OnInstallPromptDone(bool did_accept) {
->Get(browser_context())
->GetDelegate()
->EnableExtension(browser_context(), extension_id_);
Respond(OneArgument(std::make_unique<base::Value>(true)));
Respond(NoArguments());
} else {
Respond(Error(keys::kUserDidNotReEnableError));
}
......@@ -552,7 +552,7 @@ void ManagementSetEnabledFunction::OnParentPermissionDialogDone(
->Get(browser_context())
->GetDelegate();
delegate->EnableExtension(browser_context(), extension_id_);
Respond(OneArgument(std::make_unique<base::Value>(true)));
Respond(NoArguments());
break;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment