blob: 955d02683a6a0743ac0db6276393b90b3b2e4c31 [file] [log] [blame]
// Copyright 2014 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 <string>
#include <utility>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/files/dir_reader_posix.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/test/null_task_runner.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_process_platform_part.h"
#include "chrome/browser/chromeos/extensions/signin_screen_policy_provider.h"
#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
#include "chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h"
#include "chrome/browser/chromeos/policy/device_policy_builder.h"
#include "chrome/browser/chromeos/policy/device_policy_cros_browser_test.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/chromeos/settings/device_settings_service.h"
#include "chrome/browser/extensions/chrome_test_extension_loader.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/policy/test/local_policy_test_server.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "chromeos/chromeos_paths.h"
#include "chromeos/chromeos_switches.h"
#include "chromeos/dbus/fake_session_manager_client.h"
#include "components/ownership/owner_key_util.h"
#include "components/policy/core/browser/browser_policy_connector.h"
#include "components/policy/core/common/cloud/cloud_policy_client.h"
#include "components/policy/core/common/cloud/device_management_service.h"
#include "components/policy/core/common/cloud/mock_cloud_policy_client.h"
#include "components/policy/core/common/cloud/policy_builder.h"
#include "components/policy/core/common/cloud/resource_cache.h"
#include "components/policy/core/common/policy_service.h"
#include "components/policy/core/common/policy_switches.h"
#include "components/policy/policy_constants.h"
#include "components/policy/proto/chrome_device_policy.pb.h"
#include "components/policy/proto/chrome_extension_policy.pb.h"
#include "components/policy/proto/device_management_backend.pb.h"
#include "crypto/rsa_private_key.h"
#include "crypto/sha2.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_system.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension.h"
#include "extensions/test/result_catcher.h"
#include "net/http/http_status_code.h"
#include "net/url_request/test_url_fetcher_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace policy {
namespace {
class DeviceCloudPolicyBrowserTest : public InProcessBrowserTest {
protected:
DeviceCloudPolicyBrowserTest()
: mock_client_(base::MakeUnique<MockCloudPolicyClient>()) {}
std::unique_ptr<MockCloudPolicyClient> mock_client_;
private:
DISALLOW_COPY_AND_ASSIGN(DeviceCloudPolicyBrowserTest);
};
} // namespace
IN_PROC_BROWSER_TEST_F(DeviceCloudPolicyBrowserTest, Initializer) {
BrowserPolicyConnectorChromeOS* connector =
g_browser_process->platform_part()->browser_policy_connector_chromeos();
// Initializer exists at first.
EXPECT_TRUE(connector->GetDeviceCloudPolicyInitializer());
// Initializer is deleted when the manager connects.
connector->GetDeviceCloudPolicyManager()->StartConnection(
std::move(mock_client_), connector->GetInstallAttributes());
EXPECT_FALSE(connector->GetDeviceCloudPolicyInitializer());
// Initializer is restarted when the manager disconnects.
connector->GetDeviceCloudPolicyManager()->Disconnect();
EXPECT_TRUE(connector->GetDeviceCloudPolicyInitializer());
}
namespace {
// Tests for the rotation of the signing keys used for the device policy.
//
// The test is performed against a test policy server, which is set up for
// rotating the policy key automatically with each policy fetch.
class KeyRotationDeviceCloudPolicyTest : public DevicePolicyCrosBrowserTest {
protected:
const int kTestPolicyValue = 123;
const int kTestPolicyOtherValue = 456;
const char* const kTestPolicyKey = key::kDevicePolicyRefreshRate;
KeyRotationDeviceCloudPolicyTest() {}
void SetUp() override {
UpdateBuiltTestPolicyValue(kTestPolicyValue);
StartPolicyTestServer();
DevicePolicyCrosBrowserTest::SetUp();
}
void SetUpCommandLine(base::CommandLine* command_line) override {
DevicePolicyCrosBrowserTest::SetUpCommandLine(command_line);
command_line->AppendSwitchASCII(policy::switches::kDeviceManagementUrl,
policy_test_server_.GetServiceURL().spec());
}
void SetUpInProcessBrowserTestFixture() override {
DevicePolicyCrosBrowserTest::SetUpInProcessBrowserTestFixture();
InstallOwnerKey();
MarkAsEnterpriseOwned();
SetFakeDevicePolicy();
}
void SetUpOnMainThread() override {
DevicePolicyCrosBrowserTest::SetUpOnMainThread();
g_browser_process->platform_part()
->browser_policy_connector_chromeos()
->device_management_service()
->ScheduleInitialization(0);
StartObservingTestPolicy();
}
void TearDownOnMainThread() override {
policy_change_registrar_.reset();
DevicePolicyCrosBrowserTest::TearDownOnMainThread();
}
void UpdateBuiltTestPolicyValue(int test_policy_value) {
device_policy()
->payload()
.mutable_device_policy_refresh_rate()
->set_device_policy_refresh_rate(test_policy_value);
device_policy()->Build();
}
void UpdateServedTestPolicy() {
EXPECT_TRUE(policy_test_server_.UpdatePolicy(
dm_protocol::kChromeDevicePolicyType, std::string() /* entity_id */,
device_policy()->payload().SerializeAsString()));
}
int GetTestPolicyValue() {
PolicyService* const policy_service =
g_browser_process->platform_part()
->browser_policy_connector_chromeos()
->GetPolicyService();
const base::Value* policy_value =
policy_service
->GetPolicies(PolicyNamespace(POLICY_DOMAIN_CHROME,
std::string() /* component_id */))
.GetValue(kTestPolicyKey);
EXPECT_TRUE(policy_value);
int refresh_rate = -1;
EXPECT_TRUE(policy_value->GetAsInteger(&refresh_rate));
return refresh_rate;
}
void WaitForTestPolicyValue(int expected_policy_value) {
if (GetTestPolicyValue() == expected_policy_value)
return;
awaited_policy_value_ = expected_policy_value;
// The run loop will be terminated by TestPolicyChangedCallback() once the
// policy value becomes equal to the awaited value.
DCHECK(!policy_change_waiting_run_loop_);
policy_change_waiting_run_loop_ = base::MakeUnique<base::RunLoop>();
policy_change_waiting_run_loop_->Run();
}
private:
void StartPolicyTestServer() {
policy_test_server_.RegisterClient(PolicyBuilder::kFakeToken,
PolicyBuilder::kFakeDeviceId);
UpdateServedTestPolicy();
policy_test_server_.EnableAutomaticRotationOfSigningKeys();
EXPECT_TRUE(policy_test_server_.Start());
}
void SetFakeDevicePolicy() {
device_policy()
->payload()
.mutable_device_policy_refresh_rate()
->set_device_policy_refresh_rate(kTestPolicyValue);
device_policy()->Build();
session_manager_client()->set_device_policy(device_policy()->GetBlob());
}
void StartObservingTestPolicy() {
policy_change_registrar_ = base::MakeUnique<PolicyChangeRegistrar>(
g_browser_process->platform_part()
->browser_policy_connector_chromeos()
->GetPolicyService(),
PolicyNamespace(POLICY_DOMAIN_CHROME,
std::string() /* component_id */));
policy_change_registrar_->Observe(
kTestPolicyKey,
base::BindRepeating(
&KeyRotationDeviceCloudPolicyTest::TestPolicyChangedCallback,
base::Unretained(this)));
}
void TestPolicyChangedCallback(const base::Value* old_value,
const base::Value* new_value) {
if (policy_change_waiting_run_loop_ &&
GetTestPolicyValue() == awaited_policy_value_) {
policy_change_waiting_run_loop_->Quit();
}
}
LocalPolicyTestServer policy_test_server_;
std::unique_ptr<PolicyChangeRegistrar> policy_change_registrar_;
int awaited_policy_value_ = -1;
std::unique_ptr<base::RunLoop> policy_change_waiting_run_loop_;
DISALLOW_COPY_AND_ASSIGN(KeyRotationDeviceCloudPolicyTest);
};
} // namespace
IN_PROC_BROWSER_TEST_F(KeyRotationDeviceCloudPolicyTest, Basic) {
// Initially, the policy has the first value.
EXPECT_EQ(kTestPolicyValue, GetTestPolicyValue());
const std::string original_owner_public_key =
chromeos::DeviceSettingsService::Get()->GetPublicKey()->as_string();
// The server is updated to serve the new policy value, and the client fetches
// it.
UpdateBuiltTestPolicyValue(kTestPolicyOtherValue);
UpdateServedTestPolicy();
g_browser_process->platform_part()
->browser_policy_connector_chromeos()
->GetDeviceCloudPolicyManager()
->RefreshPolicies();
WaitForTestPolicyValue(kTestPolicyOtherValue);
EXPECT_EQ(kTestPolicyOtherValue, GetTestPolicyValue());
// The owner key has changed due to the key rotation performed by the policy
// test server.
EXPECT_NE(
original_owner_public_key,
chromeos::DeviceSettingsService::Get()->GetPublicKey()->as_string());
}
namespace {
// This class is the base class for the tests of the behavior regarding
// extensions installed on the signin screen (which is generally possible only
// through an admin policy, but the tests may install the extensions directly
// for the test purposes).
class SigninExtensionsDeviceCloudPolicyBrowserTestBase
: public DevicePolicyCrosBrowserTest {
protected:
static constexpr const char* kTestExtensionId =
"baogpbmpccplckhhehfipokjaflkmbno";
static constexpr const char* kTestExtensionSourceDir =
"extensions/signin_screen_managed_storage";
static constexpr const char* kTestExtensionVersion = "1.0";
static constexpr const char* kTestExtensionTestPage = "test.html";
static constexpr const char* kFakePolicyUrl =
"http://example.org/test-policy.json";
static constexpr const char* kFakePolicy =
"{\"string-policy\": {\"Value\": \"value\"}}";
static constexpr int kFakePolicyPublicKeyVersion = 1;
static constexpr const char* kPolicyProtoCacheKey = "signinextension-policy";
static constexpr const char* kPolicyDataCacheKey =
"signinextension-policy-data";
SigninExtensionsDeviceCloudPolicyBrowserTestBase() {}
void SetUpCommandLine(base::CommandLine* command_line) override {
DevicePolicyCrosBrowserTest::SetUpCommandLine(command_line);
command_line->AppendSwitch(chromeos::switches::kLoginManager);
command_line->AppendSwitch(chromeos::switches::kForceLoginManagerInTests);
// This flag is required for the successful installation of the test
// extension into the signin profile, due to some checks in
// |ExtensionService|.
command_line->AppendSwitchASCII(::switches::kDisableExtensionsExcept,
GetTestExtensionSourcePath().value());
}
void SetUpInProcessBrowserTestFixture() override {
DevicePolicyCrosBrowserTest::SetUpInProcessBrowserTestFixture();
InstallOwnerKey();
MarkAsEnterpriseOwned();
SetFakeDevicePolicy();
}
void SetUpOnMainThread() override {
DevicePolicyCrosBrowserTest::SetUpOnMainThread();
BrowserPolicyConnectorChromeOS* connector =
g_browser_process->platform_part()->browser_policy_connector_chromeos();
connector->device_management_service()->ScheduleInitialization(0);
}
static base::FilePath GetTestExtensionSourcePath() {
base::FilePath test_data_dir;
EXPECT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir));
return test_data_dir.AppendASCII(kTestExtensionSourceDir);
}
static Profile* GetSigninProfile() {
Profile* signin_profile =
chromeos::ProfileHelper::GetSigninProfile()->GetOriginalProfile();
EXPECT_TRUE(signin_profile);
return signin_profile;
}
static const extensions::Extension* GetTestExtension() {
extensions::ExtensionRegistry* registry =
extensions::ExtensionRegistry::Get(GetSigninProfile());
const extensions::Extension* extension =
registry->enabled_extensions().GetByID(kTestExtensionId);
EXPECT_TRUE(extension);
return extension;
}
static enterprise_management::PolicyFetchResponse BuildTestComponentPolicy() {
ComponentPolicyBuilder builder;
MakeTestComponentPolicyBuilder(&builder);
return builder.policy();
}
static enterprise_management::ExternalPolicyData
BuildTestComponentPolicyPayload() {
ComponentPolicyBuilder builder;
MakeTestComponentPolicyBuilder(&builder);
return builder.payload();
}
private:
void SetFakeDevicePolicy() {
device_policy()->policy_data().set_public_key_version(
kFakePolicyPublicKeyVersion);
device_policy()->Build();
session_manager_client()->set_device_policy(device_policy()->GetBlob());
}
static void MakeTestComponentPolicyBuilder(ComponentPolicyBuilder* builder) {
builder->policy_data().set_policy_type(
dm_protocol::kChromeSigninExtensionPolicyType);
builder->policy_data().set_settings_entity_id(kTestExtensionId);
builder->policy_data().set_public_key_version(kFakePolicyPublicKeyVersion);
builder->payload().set_download_url(kFakePolicyUrl);
builder->payload().set_secure_hash(crypto::SHA256HashString(kFakePolicy));
builder->Build();
}
DISALLOW_COPY_AND_ASSIGN(SigninExtensionsDeviceCloudPolicyBrowserTestBase);
};
// This class tests whether the component policy is successfully processed and
// passed to the extension that is installed into the signin profile after the
// initialization finishes.
//
// The fake component policy is injected by using the local policy test server.
// The test extension is installed into the profile manually by the test code
// (i.e. this class doesn't test the installation of the signin screen
// extensions through the admin policy).
class SigninExtensionsDeviceCloudPolicyBrowserTest
: public SigninExtensionsDeviceCloudPolicyBrowserTestBase {
protected:
SigninExtensionsDeviceCloudPolicyBrowserTest()
: fetcher_factory_(&fetcher_impl_factory_) {}
scoped_refptr<const extensions::Extension> InstallAndLoadTestExtension()
const {
extensions::ChromeTestExtensionLoader loader(GetSigninProfile());
return loader.LoadExtension(GetTestExtensionSourcePath());
}
private:
void SetUp() override {
StartPolicyTestServer();
DevicePolicyCrosBrowserTest::SetUp();
}
void SetUpCommandLine(base::CommandLine* command_line) override {
SigninExtensionsDeviceCloudPolicyBrowserTestBase::SetUpCommandLine(
command_line);
command_line->AppendSwitchASCII(policy::switches::kDeviceManagementUrl,
policy_test_server_.GetServiceURL().spec());
}
void SetUpInProcessBrowserTestFixture() override {
SigninExtensionsDeviceCloudPolicyBrowserTestBase::
SetUpInProcessBrowserTestFixture();
signin_policy_provided_disabler_ =
chromeos::GetScopedSigninScreenPolicyProviderDisablerForTesting();
EXPECT_TRUE(PathService::Get(chromeos::DIR_SIGNIN_PROFILE_COMPONENT_POLICY,
&component_policy_cache_dir_));
PrepareFakeComponentPolicyResponse();
}
void TearDownInProcessBrowserTestFixture() override {
// Check that the component policy cache was not cleared during browser
// teardown.
ResourceCache cache(component_policy_cache_dir_, new base::NullTaskRunner);
std::string stub;
EXPECT_TRUE(cache.Load(kPolicyProtoCacheKey, kTestExtensionId, &stub));
EXPECT_TRUE(cache.Load(kPolicyDataCacheKey, kTestExtensionId, &stub));
SigninExtensionsDeviceCloudPolicyBrowserTestBase::
TearDownInProcessBrowserTestFixture();
}
void StartPolicyTestServer() {
std::unique_ptr<crypto::RSAPrivateKey> signing_key(
PolicyBuilder::CreateTestSigningKey());
EXPECT_TRUE(policy_test_server_.SetSigningKeyAndSignature(
signing_key.get(), PolicyBuilder::GetTestSigningKeySignature()));
policy_test_server_.RegisterClient(PolicyBuilder::kFakeToken,
PolicyBuilder::kFakeDeviceId);
EXPECT_TRUE(policy_test_server_.Start());
}
void PrepareFakeComponentPolicyResponse() {
EXPECT_TRUE(policy_test_server_.UpdatePolicy(
dm_protocol::kChromeSigninExtensionPolicyType, kTestExtensionId,
BuildTestComponentPolicyPayload().SerializeAsString()));
fetcher_factory_.SetFakeResponse(GURL(kFakePolicyUrl), kFakePolicy,
net::HTTP_OK,
net::URLRequestStatus::SUCCESS);
}
LocalPolicyTestServer policy_test_server_;
net::URLFetcherImplFactory fetcher_impl_factory_;
net::FakeURLFetcherFactory fetcher_factory_;
base::FilePath component_policy_cache_dir_;
std::unique_ptr<base::AutoReset<bool>> signin_policy_provided_disabler_;
};
} // namespace
IN_PROC_BROWSER_TEST_F(SigninExtensionsDeviceCloudPolicyBrowserTest,
InstallAndRunInWindow) {
scoped_refptr<const extensions::Extension> extension =
InstallAndLoadTestExtension();
ASSERT_TRUE(extension);
Browser* browser = CreateBrowser(GetSigninProfile());
extensions::ResultCatcher result_catcher;
ui_test_utils::NavigateToURL(
browser, extension->GetResourceURL(kTestExtensionTestPage));
EXPECT_TRUE(result_catcher.GetNextResult());
CloseBrowserSynchronously(browser);
}
namespace {
// This class tests that the cached component policy is successfully loaded and
// passed to the extension that is already installed into the signin profile.
//
// To accomplish this, the class prefills the signin profile with, first, the
// installed test extension, and, second, with the cached component policy.
class PreinstalledSigninExtensionsDeviceCloudPolicyBrowserTest
: public SigninExtensionsDeviceCloudPolicyBrowserTestBase {
protected:
static constexpr const char* kFakeProfileSourceDir =
"extensions/profiles/signin_screen_managed_storage";
bool SetUpUserDataDirectory() override {
PrefillSigninProfile();
PrefillComponentPolicyCache();
return DevicePolicyCrosBrowserTest::SetUpUserDataDirectory();
}
void SetUpInProcessBrowserTestFixture() override {
SigninExtensionsDeviceCloudPolicyBrowserTestBase::
SetUpInProcessBrowserTestFixture();
signin_policy_provided_disabler_ =
chromeos::GetScopedSigninScreenPolicyProviderDisablerForTesting();
}
private:
static void PrefillSigninProfile() {
base::FilePath profile_source_path;
EXPECT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &profile_source_path));
profile_source_path = profile_source_path.AppendASCII(kFakeProfileSourceDir)
.AppendASCII(chrome::kInitialProfile);
base::FilePath profile_target_path;
EXPECT_TRUE(PathService::Get(chrome::DIR_USER_DATA, &profile_target_path));
EXPECT_TRUE(
base::CopyDirectory(profile_source_path, profile_target_path, true));
base::FilePath extension_target_path =
profile_target_path.AppendASCII(chrome::kInitialProfile)
.AppendASCII(extensions::kInstallDirectoryName)
.AppendASCII(kTestExtensionId)
.AppendASCII(kTestExtensionVersion);
EXPECT_TRUE(base::CopyDirectory(GetTestExtensionSourcePath(),
extension_target_path, true));
}
void PrefillComponentPolicyCache() {
base::FilePath user_data_dir;
EXPECT_TRUE(PathService::Get(chrome::DIR_USER_DATA, &user_data_dir));
chromeos::RegisterStubPathOverrides(user_data_dir);
base::FilePath cache_dir;
EXPECT_TRUE(PathService::Get(chromeos::DIR_SIGNIN_PROFILE_COMPONENT_POLICY,
&cache_dir));
ResourceCache cache(cache_dir, new base::NullTaskRunner);
EXPECT_TRUE(cache.Store(kPolicyProtoCacheKey, kTestExtensionId,
BuildTestComponentPolicy().SerializeAsString()));
EXPECT_TRUE(
cache.Store(kPolicyDataCacheKey, kTestExtensionId, kFakePolicy));
}
std::unique_ptr<base::AutoReset<bool>> signin_policy_provided_disabler_;
};
} // namespace
IN_PROC_BROWSER_TEST_F(PreinstalledSigninExtensionsDeviceCloudPolicyBrowserTest,
OfflineStart) {
const extensions::Extension* extension = GetTestExtension();
ASSERT_TRUE(extension);
Browser* browser = CreateBrowser(GetSigninProfile());
extensions::ResultCatcher result_catcher;
ui_test_utils::NavigateToURL(
browser, extension->GetResourceURL(kTestExtensionTestPage));
EXPECT_TRUE(result_catcher.GetNextResult());
CloseBrowserSynchronously(browser);
}
} // namespace policy