#include <stddef.h>
#include <memory>
#include <utility>
#include <vector>
#include "base/cfi_buildflags.h"
#include "base/containers/flat_map.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/callback.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "base/test/simple_test_clock.h"
#include "base/threading/thread_restrictions.h"
#include "base/time/clock.h"
#include "base/time/time.h"
#include "base/values.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/policy/profile_policy_connector_builder.h"
#include "chrome/browser/policy/schema_registry_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/base/chrome_test_utils.h"
#include "chrome/test/base/platform_browser_test.h"
#include "components/policy/core/browser/browser_policy_connector.h"
#include "components/policy/core/browser/webui/policy_status_provider.h"
#include "components/policy/core/common/cloud/cloud_policy_refresh_scheduler.h"
#include "components/policy/core/common/external_data_fetcher.h"
#include "components/policy/core/common/mock_configuration_policy_provider.h"
#include "components/policy/core/common/policy_map.h"
#include "components/policy/core/common/policy_namespace.h"
#include "components/policy/core/common/policy_types.h"
#include "components/policy/core/common/schema.h"
#include "components/policy/policy_constants.h"
#include "components/strings/grit/components_strings.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/l10n/l10n_util.h"
#include "url/gurl.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "chrome/browser/ash/login/test/device_state_mixin.h"
#include "chrome/browser/ash/login/test/logged_in_user_mixin.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/test/base/mixin_based_in_process_browser_test.h"
#endif
#if !BUILDFLAG(IS_ANDROID)
#include "chrome/browser/download/download_prefs.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/install_verifier.h"
#include "chrome/browser/extensions/test_extension_system.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/account_id/account_id.h"
#include "extensions/common/extension_builder.h"
#include "extensions/common/features/simple_feature.h"
#endif
_;
Return;
namespace {
class PolicySchemaAvailableWaiter : public policy::SchemaRegistry::Observer { … };
std::vector<std::string> PopulateExpectedPolicy(
const std::string& name,
const std::string& value,
const std::string& source,
const policy::PolicyMap::Entry* policy_map_entry,
bool unknown) { … }
}
class PolicyUITest : public PlatformBrowserTest { … };
PolicyUITest::PolicyUITest() = default;
PolicyUITest::~PolicyUITest() = default;
void PolicyUITest::SetUpInProcessBrowserTestFixture() { … }
void PolicyUITest::UpdateProviderPolicyForNamespace(
const policy::PolicyNamespace& policy_namespace,
const policy::PolicyMap& policy) { … }
void PolicyUITest::VerifyPolicies(
const std::vector<std::vector<std::string>>& expected_policies) { … }
void PolicyUITest::VerifyReportButton(bool visible) { … }
#if BUILDFLAG(IS_CHROMEOS_ASH)
class PolicyUIStatusTest : public MixinBasedInProcessBrowserTest {
public:
void SetUpOnMainThread() override {
MixinBasedInProcessBrowserTest::SetUpOnMainThread();
logged_in_user_mixin_.LogInUser();
device_state_.RequestDevicePolicyUpdate()
->policy_data()
->set_public_key_version(1);
}
bool ReadStatusFor(const std::string& policy_legend,
base::flat_map<std::string, std::string>* policy_status);
bool ReloadPolicies();
bool ReloadPolicies(content::WebContents* contents);
protected:
ash::DeviceStateMixin device_state_{
&mixin_host_,
ash::DeviceStateMixin::State::OOBE_COMPLETED_CLOUD_ENROLLED};
ash::LoggedInUserMixin logged_in_user_mixin_{
&mixin_host_, this, embedded_test_server(),
ash::LoggedInUserMixin::LogInType::kManaged};
};
bool PolicyUIStatusTest::ReadStatusFor(
const std::string& policy_legend,
base::flat_map<std::string, std::string>* policy_status) {
const std::string javascript = R"JS(
(function() {
function readStatus() {
// Wait for the status box to appear in case page just loaded.
const statusSection = document.getElementById('status-section');
if (statusSection.hidden) {
return new Promise(resolve => {
window.requestIdleCallback(resolve);
}).then(readStatus);
}
const policies = getPolicyFieldsets();
const statuses = {};
for (let i = 0; i < policies.length; ++i) {
const statusHeading = policies[i]
.querySelector('.status-box-heading').textContent;
const entries = {};
const rows = policies[i]
.querySelectorAll('.status-entry div:nth-child(2)');
for (let j = 0; j < rows.length; ++j) {
entries[rows[j].className.split(' ')[0]] = rows[j].textContent
.trim();
}
statuses[statusHeading.trim()] = entries;
}
return JSON.stringify(statuses);
};
return new Promise(resolve => {
window.requestIdleCallback(resolve);
}).then(readStatus);
})();
)JS";
content::WebContents* contents =
chrome_test_utils::GetActiveWebContents(this);
std::string json = content::EvalJs(contents, javascript).ExtractString();
std::optional<base::Value> statuses = base::JSONReader::Read(json);
if (!statuses.has_value() || !statuses->is_dict())
return false;
const base::Value::Dict& status_dict = statuses->GetDict();
const base::Value::Dict* actual_entries = status_dict.FindDict(policy_legend);
if (!actual_entries) {
return false;
}
for (const auto entry : *actual_entries) {
policy_status->insert_or_assign(entry.first, entry.second.GetString());
}
return true;
}
bool PolicyUIStatusTest::ReloadPolicies() {
content::WebContents* contents =
chrome_test_utils::GetActiveWebContents(this);
return ReloadPolicies(contents);
}
bool PolicyUIStatusTest::ReloadPolicies(content::WebContents* contents) {
const std::string javascript = R"JS(
(function() {
const reloadPoliciesBtn = document.getElementById('reload-policies');
reloadPoliciesBtn.click();
// Wait until reload button becomes enabled again, i.e. policies reloaded.
function waitForPoliciesToReload() {
if (reloadPoliciesBtn.disabled) {
return new Promise(resolve => {
window.requestIdleCallback(resolve);
}).then(waitForPoliciesToReload);
} else {
return true;
}
}
return new Promise(resolve => {
window.requestIdleCallback(resolve);
}).then(waitForPoliciesToReload);
})();
)JS";
return content::ExecJs(contents, javascript);
}
#if !BUILDFLAG(IS_ANDROID)
IN_PROC_BROWSER_TEST_F(PolicyUIStatusTest, CheckPolicyUiInGuestProfile) {
const Browser* policy_browser = OpenURLOffTheRecord(
browser()->profile(), GURL(chrome::kChromeUIPolicyURL));
ASSERT_TRUE(policy_browser);
content::WebContents* contents =
policy_browser->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(ReloadPolicies(contents));
}
#endif
IN_PROC_BROWSER_TEST_F(PolicyUIStatusTest,
ShowsZeroSecondsSinceRefreshAfterReloadingPolicies) {
base::Time now = base::Time::Now();
logged_in_user_mixin_.GetEmbeddedPolicyTestServerMixin()
->UpdatePolicyTimestamp(now);
base::SimpleTestClock status_provider_clock_mock;
status_provider_clock_mock.SetNow(now);
auto status_provider_clock_mock_closure =
policy::PolicyStatusProvider::OverrideClockForTesting(
&status_provider_clock_mock);
base::SimpleTestClock policy_refresher_clock_mock;
policy_refresher_clock_mock.SetNow(now);
auto policy_refresher_clock_mock_closure =
policy::CloudPolicyRefreshScheduler::OverrideClockForTesting(
&policy_refresher_clock_mock);
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(),
GURL(chrome::kChromeUIPolicyURL)));
ASSERT_TRUE(ReloadPolicies());
base::flat_map<std::string, std::string> status;
ASSERT_TRUE(ReadStatusFor("User policies", &status));
EXPECT_EQ(status["time-since-last-refresh"], "0 secs ago");
EXPECT_EQ(status["time-since-last-fetch-attempt"], "0 secs ago");
ASSERT_TRUE(ReadStatusFor("Device policies", &status));
EXPECT_EQ(status["time-since-last-refresh"], "0 secs ago");
EXPECT_EQ(status["time-since-last-fetch-attempt"], "0 secs ago");
}
IN_PROC_BROWSER_TEST_F(PolicyUIStatusTest, ShowsCorrectTimesSinceRefresh) {
base::Time now = base::Time::Now();
logged_in_user_mixin_.GetEmbeddedPolicyTestServerMixin()
->UpdatePolicyTimestamp(now);
base::SimpleTestClock status_provider_clock_mock;
status_provider_clock_mock.SetNow(now);
auto status_provider_clock_mock_closure =
policy::PolicyStatusProvider::OverrideClockForTesting(
&status_provider_clock_mock);
base::SimpleTestClock policy_refresher_clock_mock;
policy_refresher_clock_mock.SetNow(now);
auto policy_refresher_clock_mock_closure =
policy::CloudPolicyRefreshScheduler::OverrideClockForTesting(
&policy_refresher_clock_mock);
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(),
GURL(chrome::kChromeUIPolicyURL)));
ASSERT_TRUE(ReloadPolicies());
status_provider_clock_mock.Advance(base::Hours(1));
policy_refresher_clock_mock.Advance(base::Hours(1));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(),
GURL(chrome::kChromeUIPolicyURL)));
base::RunLoop().RunUntilIdle();
base::flat_map<std::string, std::string> status;
ASSERT_TRUE(ReadStatusFor("User policies", &status));
EXPECT_EQ(status["time-since-last-refresh"], "1 hour ago");
EXPECT_EQ(status["time-since-last-fetch-attempt"], "1 hour ago");
ASSERT_TRUE(ReadStatusFor("Device policies", &status));
EXPECT_EQ(status["time-since-last-refresh"], "1 hour ago");
EXPECT_EQ(status["time-since-last-fetch-attempt"], "1 hour ago");
}
IN_PROC_BROWSER_TEST_F(PolicyUIStatusTest,
ShowsCorrectRefreshTimesAfterFailedReload) {
base::Time now = base::Time::Now();
logged_in_user_mixin_.GetEmbeddedPolicyTestServerMixin()
->UpdatePolicyTimestamp(now);
base::SimpleTestClock status_provider_clock_mock;
status_provider_clock_mock.SetNow(now);
auto status_provider_clock_mock_closure =
policy::PolicyStatusProvider::OverrideClockForTesting(
&status_provider_clock_mock);
base::SimpleTestClock policy_refresher_clock_mock;
policy_refresher_clock_mock.SetNow(now);
auto policy_refresher_clock_mock_closure =
policy::CloudPolicyRefreshScheduler::OverrideClockForTesting(
&policy_refresher_clock_mock);
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(),
GURL(chrome::kChromeUIPolicyURL)));
ASSERT_TRUE(ReloadPolicies());
logged_in_user_mixin_.GetEmbeddedPolicyTestServerMixin()->SetPolicyFetchError(
500);
status_provider_clock_mock.Advance(base::Hours(1));
policy_refresher_clock_mock.Advance(base::Hours(1));
ASSERT_TRUE(ReloadPolicies());
base::flat_map<std::string, std::string> status;
ASSERT_TRUE(ReadStatusFor("User policies", &status));
EXPECT_EQ(status["time-since-last-refresh"], "1 hour ago");
EXPECT_EQ(status["time-since-last-fetch-attempt"], "0 secs ago");
ASSERT_TRUE(ReadStatusFor("Device policies", &status));
EXPECT_EQ(status["time-since-last-refresh"], "1 hour ago");
EXPECT_EQ(status["time-since-last-fetch-attempt"], "0 secs ago");
}
#endif
IN_PROC_BROWSER_TEST_F(PolicyUITest, SendPolicyNames) { … }
IN_PROC_BROWSER_TEST_F(PolicyUITest, SendPolicyValues) { … }
IN_PROC_BROWSER_TEST_F(PolicyUITest, ReportButton) { … }
IN_PROC_BROWSER_TEST_F(PolicyUITest, ReportButtonWithProfileReporting) { … }
#if !BUILDFLAG(IS_CHROMEOS)
class PolicyPrecedenceUITest
: public PolicyUITest,
public ::testing::WithParamInterface<std::tuple<
bool,
bool,
bool>> { … };
IN_PROC_BROWSER_TEST_P(PolicyPrecedenceUITest, PrecedenceOrder) { … }
INSTANTIATE_TEST_SUITE_P(…);
#endif
#if !BUILDFLAG(IS_ANDROID)
class ExtensionPolicyUITest : public PolicyUITest,
public ::testing::WithParamInterface<bool> { … };
#if defined(ADDRESS_SANITIZER)
#define MAYBE_ExtensionLoadAndSendPolicy …
#else
#define MAYBE_ExtensionLoadAndSendPolicy …
#endif
IN_PROC_BROWSER_TEST_P(ExtensionPolicyUITest,
MAYBE_ExtensionLoadAndSendPolicy) { … }
INSTANTIATE_TEST_SUITE_P(…);
#endif