blob: cfa59cf503d0ce3a218ef7f790b7c0d2d88e2783 [file] [log] [blame]
// Copyright (c) 2013 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 "base/command_line.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/policy/chrome_browser_policy_connector.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/in_process_browser_test.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/policy_switches.h"
#include "components/policy/core/common/policy_test_utils.h"
#include "components/policy/proto/device_management_backend.pb.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/browser_test.h"
#include "net/base/net_errors.h"
#include "net/http/http_status_code.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/test/test_url_loader_factory.h"
#include "services/network/test/test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h"
#else
#include "chrome/browser/net/system_network_context_manager.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "components/policy/core/common/cloud/user_cloud_policy_manager.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/signin/public/identity_manager/identity_test_utils.h"
#endif
using content::BrowserThread;
using testing::_;
using testing::AnyNumber;
using testing::InvokeWithoutArgs;
using testing::Mock;
namespace em = enterprise_management;
namespace policy {
namespace {
// Parses the upload data in |request| into |request_msg|, and validates the
// request. The query string in the URL must contain the |expected_type| for
// the "request" parameter. Returns true if all checks succeeded, and the
// request data has been parsed into |request_msg|.
bool ValidRequest(const std::string& method,
const GURL& url,
const std::string& data,
const std::string& expected_type,
em::DeviceManagementRequest* request_msg) {
if (method != "POST")
return false;
std::string spec = url.spec();
if (spec.find("request=" + expected_type) == std::string::npos)
return false;
if (!request_msg->ParseFromString(data))
return false;
return true;
}
void RespondWithBadResponse(const network::ResourceRequest& request,
network::TestURLLoaderFactory* factory) {
network::URLLoaderCompletionStatus status;
factory->AddResponse(
request.url, network::mojom::URLResponseHead::New(), std::string(),
network::URLLoaderCompletionStatus(net::ERR_NETWORK_CHANGED));
}
void RespondToRegisterWithSuccess(em::DeviceRegisterRequest::Type expected_type,
bool expect_reregister,
const network::ResourceRequest& request,
network::TestURLLoaderFactory* factory) {
em::DeviceManagementRequest request_msg;
if (!ValidRequest(request.method, request.url,
network::GetUploadData(request), "register",
&request_msg)) {
RespondWithBadResponse(request, factory);
return;
}
if (!request_msg.has_register_request() ||
request_msg.has_unregister_request() ||
request_msg.has_policy_request() ||
request_msg.has_device_status_report_request() ||
request_msg.has_session_status_report_request() ||
request_msg.has_auto_enrollment_request()) {
RespondWithBadResponse(request, factory);
return;
}
const em::DeviceRegisterRequest& register_request =
request_msg.register_request();
if (expect_reregister &&
(!register_request.has_reregister() || !register_request.reregister())) {
RespondWithBadResponse(request, factory);
return;
} else if (!expect_reregister && register_request.has_reregister() &&
register_request.reregister()) {
RespondWithBadResponse(request, factory);
return;
}
if (!register_request.has_type() ||
register_request.type() != expected_type) {
RespondWithBadResponse(request, factory);
return;
}
std::string content;
network::URLLoaderCompletionStatus status;
em::DeviceManagementResponse response;
em::DeviceRegisterResponse* register_response =
response.mutable_register_response();
register_response->set_device_management_token("s3cr3t70k3n");
response.SerializeToString(&content);
status.decoded_body_length = content.size();
auto head = network::CreateURLResponseHead(net::HTTP_OK);
head->mime_type = "application/protobuf";
factory->AddResponse(request.url, std::move(head), content, status);
}
} // namespace
// Tests the cloud policy stack using a URLRequestJobFactory::ProtocolHandler
// to intercept requests and produce canned responses.
class CloudPolicyManagerTest : public InProcessBrowserTest {
protected:
CloudPolicyManagerTest() {}
~CloudPolicyManagerTest() override {}
void SetUpInProcessBrowserTestFixture() override {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
command_line->AppendSwitchASCII(switches::kDeviceManagementUrl,
"http://localhost");
ChromeBrowserPolicyConnector::EnableCommandLineSupportForTesting();
// Set retry delay to prevent timeouts.
policy::DeviceManagementService::SetRetryDelayForTesting(0);
}
void SetUpOnMainThread() override {
ASSERT_TRUE(PolicyServiceIsEmpty(g_browser_process->policy_service()))
<< "Pre-existing policies in this machine will make this test fail.";
test_url_loader_factory_ =
std::make_unique<network::TestURLLoaderFactory>();
BrowserPolicyConnector* connector =
g_browser_process->browser_policy_connector();
connector->ScheduleServiceInitialization(0);
#if BUILDFLAG(IS_CHROMEOS_ASH)
policy_manager()->core()->client()->SetURLLoaderFactoryForTesting(
test_url_loader_factory_->GetSafeWeakWrapper());
#else
// Mock a signed-in user. This is used by the UserCloudPolicyStore to pass
// the username to the UserCloudPolicyValidator.
auto* identity_manager =
IdentityManagerFactory::GetForProfile(browser()->profile());
signin::SetPrimaryAccount(identity_manager, "user@example.com",
signin::ConsentLevel::kSync);
ASSERT_TRUE(policy_manager());
policy_manager()->Connect(
g_browser_process->local_state(),
UserCloudPolicyManager::CreateCloudPolicyClient(
connector->device_management_service(),
test_url_loader_factory_->GetSafeWeakWrapper()));
#endif
}
void TearDownOnMainThread() override {
// Verify that all the expected requests were handled.
EXPECT_EQ(0, test_url_loader_factory_->NumPending());
}
#if BUILDFLAG(IS_CHROMEOS_ASH)
UserCloudPolicyManagerChromeOS* policy_manager() {
return browser()->profile()->GetUserCloudPolicyManagerChromeOS();
}
#else
UserCloudPolicyManager* policy_manager() {
return browser()->profile()->GetUserCloudPolicyManager();
}
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
// Register the client of the policy_manager() using a bogus auth token, and
// returns once the registration gets a result back.
void Register() {
ASSERT_TRUE(policy_manager());
ASSERT_TRUE(policy_manager()->core()->client());
base::RunLoop run_loop;
MockCloudPolicyClientObserver observer;
EXPECT_CALL(observer, OnRegistrationStateChanged(_))
.Times(AnyNumber())
.WillRepeatedly(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
EXPECT_CALL(observer, OnClientError(_))
.Times(AnyNumber())
.WillRepeatedly(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
policy_manager()->core()->client()->AddObserver(&observer);
// Give a bogus OAuth token to the |policy_manager|. This should make its
// CloudPolicyClient fetch the DMToken.
CloudPolicyClient::RegistrationParameters parameters(
#if BUILDFLAG(IS_CHROMEOS_ASH)
em::DeviceRegisterRequest::USER,
#else
em::DeviceRegisterRequest::BROWSER,
#endif
em::DeviceRegisterRequest::FLAVOR_USER_REGISTRATION);
policy_manager()->core()->client()->Register(
parameters, std::string() /* client_id */,
"oauth_token_unused" /* oauth_token */);
run_loop.Run();
Mock::VerifyAndClearExpectations(&observer);
policy_manager()->core()->client()->RemoveObserver(&observer);
}
std::unique_ptr<network::TestURLLoaderFactory> test_url_loader_factory_;
};
IN_PROC_BROWSER_TEST_F(CloudPolicyManagerTest, Register) {
test_url_loader_factory_->SetInterceptor(
base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
// Accept one register request. The initial request should not include
// the reregister flag.
em::DeviceRegisterRequest::Type expected_type =
#if BUILDFLAG(IS_CHROMEOS_ASH)
em::DeviceRegisterRequest::USER;
#else
em::DeviceRegisterRequest::BROWSER;
#endif
RespondToRegisterWithSuccess(expected_type, /*expect_reregister=*/false,
request, test_url_loader_factory_.get());
}));
EXPECT_FALSE(policy_manager()->core()->client()->is_registered());
ASSERT_NO_FATAL_FAILURE(Register());
EXPECT_TRUE(policy_manager()->core()->client()->is_registered());
}
IN_PROC_BROWSER_TEST_F(CloudPolicyManagerTest, RegisterFails) {
test_url_loader_factory_->SetInterceptor(
base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
test_url_loader_factory_->AddResponse(request.url.spec(), std::string(),
net::HTTP_BAD_REQUEST);
}));
EXPECT_FALSE(policy_manager()->core()->client()->is_registered());
ASSERT_NO_FATAL_FAILURE(Register());
EXPECT_FALSE(policy_manager()->core()->client()->is_registered());
}
IN_PROC_BROWSER_TEST_F(CloudPolicyManagerTest, RegisterFailsWithRetries) {
// Fail 4 times with ERR_NETWORK_CHANGED; the first 3 will trigger a retry,
// the last one will forward the error to the client and unblock the
// register process.
int count = 0;
test_url_loader_factory_->SetInterceptor(
base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
network::URLLoaderCompletionStatus status(net::ERR_NETWORK_CHANGED);
test_url_loader_factory_->AddResponse(
request.url, network::mojom::URLResponseHead::New(), std::string(),
status);
++count;
}));
EXPECT_FALSE(policy_manager()->core()->client()->is_registered());
ASSERT_NO_FATAL_FAILURE(Register());
EXPECT_FALSE(policy_manager()->core()->client()->is_registered());
EXPECT_EQ(4, count);
}
IN_PROC_BROWSER_TEST_F(CloudPolicyManagerTest, RegisterWithRetry) {
test_url_loader_factory_->SetInterceptor(
base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
em::DeviceRegisterRequest::Type expected_type =
#if BUILDFLAG(IS_CHROMEOS_ASH)
em::DeviceRegisterRequest::USER;
#else
em::DeviceRegisterRequest::BROWSER;
#endif
// Accept one register request after failing once. The retry request
// should set the reregister flag.
static bool gave_error = false;
if (!gave_error) {
gave_error = true;
network::URLLoaderCompletionStatus status(net::ERR_NETWORK_CHANGED);
test_url_loader_factory_->AddResponse(
request.url, network::mojom::URLResponseHead::New(),
std::string(), status);
return;
}
RespondToRegisterWithSuccess(expected_type, /*expect_reregister=*/true,
request, test_url_loader_factory_.get());
}));
EXPECT_FALSE(policy_manager()->core()->client()->is_registered());
ASSERT_NO_FATAL_FAILURE(Register());
EXPECT_TRUE(policy_manager()->core()->client()->is_registered());
}
} // namespace policy