diff options
author | mnissler@chromium.org <mnissler@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-11-03 14:40:44 +0000 |
---|---|---|
committer | mnissler@chromium.org <mnissler@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-11-03 14:40:44 +0000 |
commit | bb58e6e839255777617a32a77ccb85a9a06ce041 (patch) | |
tree | ed462618eb7238b8b65b6897a96c709c1ad5ae2d /chrome/browser/policy | |
parent | 2976306404950c06c6ce91374e977fd1ce85809d (diff) | |
download | chromium_src-bb58e6e839255777617a32a77ccb85a9a06ce041.zip chromium_src-bb58e6e839255777617a32a77ccb85a9a06ce041.tar.gz chromium_src-bb58e6e839255777617a32a77ccb85a9a06ce041.tar.bz2 |
Implement device management backend.
This adds a device management backend implementation running over HTTP.
BUG=None
TEST=unit tests in device_management_backend_impl_unittest.cc
Review URL: http://codereview.chromium.org/4098004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@64912 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/policy')
5 files changed, 1053 insertions, 0 deletions
diff --git a/chrome/browser/policy/device_management_backend_impl.cc b/chrome/browser/policy/device_management_backend_impl.cc new file mode 100644 index 0000000..c0e9b07 --- /dev/null +++ b/chrome/browser/policy/device_management_backend_impl.cc @@ -0,0 +1,415 @@ +// Copyright (c) 2010 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 "chrome/browser/policy/device_management_backend_impl.h" + +#include <utility> +#include <vector> + +#include "base/stl_util-inl.h" +#include "base/stringprintf.h" +#include "chrome/browser/browser_thread.h" +#include "chrome/common/net/url_request_context_getter.h" +#include "net/base/cookie_monster.h" +#include "net/base/escape.h" +#include "net/base/host_resolver.h" +#include "net/base/ssl_config_service_defaults.h" +#include "net/http/http_auth_handler_factory.h" +#include "net/http/http_network_layer.h" +#include "net/proxy/proxy_service.h" +#include "net/url_request/url_request_context.h" +#include "net/url_request/url_request_status.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/io_thread.h" +#include "chrome/browser/net/chrome_net_log.h" +#include "chrome/common/chrome_version_info.h" + +namespace policy { + +namespace { + +// Name constants for URL query parameters. +const char kServiceParamRequest[] = "request"; +const char kServiceParamDeviceType[] = "devicetype"; +const char kServiceParamDeviceID[] = "deviceid"; +const char kServiceParamAgent[] = "agent"; + +// String constants for the device type and agent we report to the service. +const char kServiceValueDeviceType[] = "Chrome"; +const char kServiceValueAgent[] = + "%s enterprise management client version %s (%s)"; + +const char kServiceTokenAuthHeader[] = "Authorization: GoogleLogin auth="; +const char kDMTokenAuthHeader[] = "Authorization: GoogleDMToken token="; + +} // namespace + +// Custom request context implementation that allows to override the user agent, +// amongst others. Using the default request context is not an option since this +// service may be constructed before the default request context is created +// (i.e. before the profile has been loaded). +class DeviceManagementBackendRequestContext : public URLRequestContext { + public: + explicit DeviceManagementBackendRequestContext(IOThread::Globals* io_globals); + virtual ~DeviceManagementBackendRequestContext(); + + private: + virtual const std::string& GetUserAgent(const GURL& url) const; + + std::string user_agent_; +}; + +DeviceManagementBackendRequestContext::DeviceManagementBackendRequestContext( + IOThread::Globals* io_globals) { + net_log_ = io_globals->net_log.get(); + host_resolver_ = io_globals->host_resolver.get(); + proxy_service_ = net::ProxyService::CreateDirect(); + ssl_config_service_ = net::SSLConfigService::CreateSystemSSLConfigService(); + http_auth_handler_factory_ = + net::HttpAuthHandlerFactory::CreateDefault(host_resolver_); + http_transaction_factory_ = + net::HttpNetworkLayer::CreateFactory(host_resolver_, + io_globals->dnsrr_resolver.get(), + NULL /* ssl_host_info_factory */, + proxy_service_, + ssl_config_service_, + http_auth_handler_factory_, + NULL /* network_delegate */, + net_log_); + cookie_store_ = new net::CookieMonster(NULL, NULL); + user_agent_ = DeviceManagementBackendImpl::GetAgentString(); + accept_language_ = "*"; + accept_charset_ = "*"; +} + +DeviceManagementBackendRequestContext + ::~DeviceManagementBackendRequestContext() { + delete http_transaction_factory_; + delete http_auth_handler_factory_; +} + +const std::string& +DeviceManagementBackendRequestContext::GetUserAgent(const GURL& url) const { + return user_agent_; +} + +// Request context holder. +class DeviceManagementBackendRequestContextGetter + : public URLRequestContextGetter { + public: + DeviceManagementBackendRequestContextGetter() + : io_thread_(g_browser_process->io_thread()) {} + + // URLRequestContextGetter overrides. + virtual URLRequestContext* GetURLRequestContext(); + virtual scoped_refptr<base::MessageLoopProxy> GetIOMessageLoopProxy() const; + + private: + scoped_refptr<URLRequestContext> context_; + IOThread* io_thread_; +}; + + +URLRequestContext* +DeviceManagementBackendRequestContextGetter::GetURLRequestContext() { + if (!context_) + context_ = new DeviceManagementBackendRequestContext(io_thread_->globals()); + + return context_.get(); +} + +scoped_refptr<base::MessageLoopProxy> +DeviceManagementBackendRequestContextGetter::GetIOMessageLoopProxy() const { + return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO); +} + +// Helper class for URL query parameter encoding/decoding. +class URLQueryParameters { + public: + URLQueryParameters() {} + + // Add a query parameter. + void Put(const std::string& name, const std::string& value); + + // Produce the query string, taking care of properly encoding and assembling + // the names and values. + std::string Encode(); + + private: + typedef std::vector<std::pair<std::string, std::string> > ParameterMap; + ParameterMap params_; + + DISALLOW_COPY_AND_ASSIGN(URLQueryParameters); +}; + +void URLQueryParameters::Put(const std::string& name, + const std::string& value) { + params_.push_back(std::make_pair(name, value)); +} + +std::string URLQueryParameters::Encode() { + std::string result; + for (ParameterMap::const_iterator entry(params_.begin()); + entry != params_.end(); + ++entry) { + if (entry != params_.begin()) + result += '&'; + result += EscapeUrlEncodedData(entry->first); + result += '='; + result += EscapeUrlEncodedData(entry->second); + } + return result; +} + +// Wraps common response parsing and handling functionality. +class ResponseHandler { + public: + ResponseHandler() {} + virtual ~ResponseHandler() {} + + // Handles the URL request response. + void HandleResponse(const URLRequestStatus& status, + int response_code, + const ResponseCookies& cookies, + const std::string& data); + + // Forwards the given error to the delegate. + virtual void OnError(DeviceManagementBackend::ErrorCode error) = 0; + + private: + // Implemented by subclasses to handle the decoded response. + virtual void ProcessResponse( + const em::DeviceManagementResponse& response) = 0; +}; + +void ResponseHandler::HandleResponse(const URLRequestStatus& status, + int response_code, + const ResponseCookies& cookies, + const std::string& data) { + if (status.status() != URLRequestStatus::SUCCESS) { + OnError(DeviceManagementBackend::kErrorRequestFailed); + return; + } + + if (response_code != 200) { + OnError(DeviceManagementBackend::kErrorHttpStatus); + return; + } + + em::DeviceManagementResponse response; + if (!response.ParseFromString(data)) { + OnError(DeviceManagementBackend::kErrorResponseDecoding); + return; + } + + // Check service error code. + switch (response.error()) { + case em::DeviceManagementResponse::SUCCESS: + break; + case em::DeviceManagementResponse::DEVICE_MANAGEMENT_NOT_SUPPORTED: + OnError(DeviceManagementBackend::kErrorServiceManagementNotSupported); + return; + case em::DeviceManagementResponse::DEVICE_NOT_FOUND: + OnError(DeviceManagementBackend::kErrorServiceDeviceNotFound); + return; + case em::DeviceManagementResponse::DEVICE_MANAGEMENT_TOKEN_INVALID: + OnError(DeviceManagementBackend::kErrorServiceManagementTokenInvalid); + return; + case em::DeviceManagementResponse::ACTIVATION_PENDING: + OnError(DeviceManagementBackend::kErrorServiceActivationPending); + return; + default: + // This should be caught by the protobuf decoder. + NOTREACHED(); + OnError(DeviceManagementBackend::kErrorResponseDecoding); + return; + } + + ProcessResponse(response); +} + +// Handles device registration responses. +class RegisterResponseHandler : public ResponseHandler { + public: + RegisterResponseHandler( + DeviceManagementBackend::DeviceRegisterResponseDelegate* delegate) + : delegate_(delegate) {} + + private: + // ResponseHandler overrides. + virtual void OnError(DeviceManagementBackend::ErrorCode error) { + delegate_->OnError(error); + } + virtual void ProcessResponse(const em::DeviceManagementResponse& response) { + delegate_->HandleRegisterResponse(response.register_response()); + } + + DeviceManagementBackend::DeviceRegisterResponseDelegate* delegate_; +}; + +// Handles device unregister responses. +class UnregisterResponseHandler : public ResponseHandler { + public: + UnregisterResponseHandler( + DeviceManagementBackend::DeviceUnregisterResponseDelegate* delegate) + : delegate_(delegate) {} + + private: + // ResponseHandler overrides. + virtual void OnError(DeviceManagementBackend::ErrorCode error) { + delegate_->OnError(error); + } + virtual void ProcessResponse(const em::DeviceManagementResponse& response) { + delegate_->HandleUnregisterResponse(response.unregister_response()); + } + + DeviceManagementBackend::DeviceUnregisterResponseDelegate* delegate_; +}; + +// Handles device policy responses. +class PolicyResponseHandler : public ResponseHandler { + public: + PolicyResponseHandler( + DeviceManagementBackend::DevicePolicyResponseDelegate* delegate) + : delegate_(delegate) {} + + private: + // ResponseHandler overrides. + virtual void OnError(DeviceManagementBackend::ErrorCode error) { + delegate_->OnError(error); + } + virtual void ProcessResponse(const em::DeviceManagementResponse& response) { + delegate_->HandlePolicyResponse(response.policy_response()); + } + + DeviceManagementBackend::DevicePolicyResponseDelegate* delegate_; +}; + +DeviceManagementBackendImpl::DeviceManagementBackendImpl( + const std::string& server_url) + : server_url_(server_url), + request_context_getter_( + new DeviceManagementBackendRequestContextGetter()) { +} + +DeviceManagementBackendImpl::~DeviceManagementBackendImpl() { + // Cancel all pending requests. + STLDeleteContainerPairPointers(response_handlers_.begin(), + response_handlers_.end()); +} + +void DeviceManagementBackendImpl::ProcessRegisterRequest( + const std::string& auth_token, + const std::string& device_id, + const em::DeviceRegisterRequest& request, + DeviceRegisterResponseDelegate* delegate) { + em::DeviceManagementRequest request_wrapper; + request_wrapper.mutable_register_request()->CopyFrom(request); + + URLQueryParameters params; + PutCommonQueryParameters(¶ms); + params.Put(kServiceParamRequest, "register"); + params.Put(kServiceParamDeviceID, device_id); + + CreateFetcher(request_wrapper, + new RegisterResponseHandler(delegate), + params.Encode(), + kServiceTokenAuthHeader + auth_token); +} + +void DeviceManagementBackendImpl::ProcessUnregisterRequest( + const std::string& device_management_token, + const em::DeviceUnregisterRequest& request, + DeviceUnregisterResponseDelegate* delegate) { + em::DeviceManagementRequest request_wrapper; + request_wrapper.mutable_unregister_request()->CopyFrom(request); + + URLQueryParameters params; + PutCommonQueryParameters(¶ms); + params.Put(kServiceParamRequest, "unregister"); + + CreateFetcher(request_wrapper, + new UnregisterResponseHandler(delegate), + params.Encode(), + kDMTokenAuthHeader + device_management_token); +} + +void DeviceManagementBackendImpl::ProcessPolicyRequest( + const std::string& device_management_token, + const em::DevicePolicyRequest& request, + DevicePolicyResponseDelegate* delegate) { + em::DeviceManagementRequest request_wrapper; + request_wrapper.mutable_policy_request()->CopyFrom(request); + + URLQueryParameters params; + PutCommonQueryParameters(¶ms); + params.Put(kServiceParamRequest, "policy"); + + CreateFetcher(request_wrapper, + new PolicyResponseHandler(delegate), + params.Encode(), + kDMTokenAuthHeader + device_management_token); +} + +// static +std::string DeviceManagementBackendImpl::GetAgentString() { + chrome::VersionInfo version_info; + return base::StringPrintf(kServiceValueAgent, + version_info.Name().c_str(), + version_info.Version().c_str(), + version_info.LastChange().c_str()); +} + +void DeviceManagementBackendImpl::OnURLFetchComplete( + const URLFetcher* source, + const GURL& url, + const URLRequestStatus& status, + int response_code, + const ResponseCookies& cookies, + const std::string& data) { + ResponseHandlerMap::iterator entry(response_handlers_.find(source)); + if (entry != response_handlers_.end()) { + ResponseHandler* handler = entry->second; + handler->HandleResponse(status, response_code, cookies, data); + response_handlers_.erase(entry); + delete handler; + } else { + NOTREACHED() << "Callback from foreign URL fetcher"; + } + delete source; +} + +void DeviceManagementBackendImpl::CreateFetcher( + const em::DeviceManagementRequest& request, + ResponseHandler* handler, + const std::string& query_params, + const std::string& extra_headers) { + scoped_ptr<ResponseHandler> handler_ptr(handler); + + // Construct the payload. + std::string payload; + if (!request.SerializeToString(&payload)) { + handler->OnError(DeviceManagementBackend::kErrorRequestInvalid); + return; + } + + // Instantiate the fetcher. + GURL url(server_url_ + '?' + query_params); + URLFetcher* fetcher = URLFetcher::Create(0, url, URLFetcher::POST, this); + fetcher->set_request_context(request_context_getter_.get()); + fetcher->set_upload_data("application/octet-stream", payload); + fetcher->set_extra_request_headers(extra_headers); + response_handlers_[fetcher] = handler_ptr.release(); + + // Start the request. The fetcher will call OnURLFetchComplete when done. + fetcher->Start(); +} + +void DeviceManagementBackendImpl::PutCommonQueryParameters( + URLQueryParameters* params) { + params->Put(kServiceParamDeviceType, kServiceValueDeviceType); + params->Put(kServiceParamAgent, GetAgentString()); +} + +} // namespace policy diff --git a/chrome/browser/policy/device_management_backend_impl.h b/chrome/browser/policy/device_management_backend_impl.h new file mode 100644 index 0000000..648ca55 --- /dev/null +++ b/chrome/browser/policy/device_management_backend_impl.h @@ -0,0 +1,84 @@ +// Copyright (c) 2010 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. + +#ifndef CHROME_BROWSER_POLICY_DEVICE_MANAGEMENT_BACKEND_IMPL_H_ +#define CHROME_BROWSER_POLICY_DEVICE_MANAGEMENT_BACKEND_IMPL_H_ +#pragma once + +#include <map> +#include <string> + +#include "chrome/browser/policy/device_management_backend.h" +#include "chrome/common/net/url_fetcher.h" +#include "googleurl/src/gurl.h" + +class URLRequestContextGetter; + +namespace policy { + +class ResponseHandler; +class URLQueryParameters; + +// Device management backend implementation. This implementation makes HTTP +// requests to the policy server through the net layer. +class DeviceManagementBackendImpl : public DeviceManagementBackend, + public URLFetcher::Delegate { + public: + explicit DeviceManagementBackendImpl(const std::string& server_url); + virtual ~DeviceManagementBackendImpl(); + + // GoogleAppsPolicyService overrides: + virtual void ProcessRegisterRequest( + const std::string& auth_token, + const std::string& device_id, + const em::DeviceRegisterRequest& request, + DeviceRegisterResponseDelegate* response_delegate); + virtual void ProcessUnregisterRequest( + const std::string& device_management_token, + const em::DeviceUnregisterRequest& request, + DeviceUnregisterResponseDelegate* response_delegate); + virtual void ProcessPolicyRequest( + const std::string& device_management_token, + const em::DevicePolicyRequest& request, + DevicePolicyResponseDelegate* response_delegate); + + // Get the agent string (used for HTTP user agent and as agent passed to the + // server). + static std::string GetAgentString(); + + private: + typedef std::map<const URLFetcher*, ResponseHandler*> ResponseHandlerMap; + + // URLFetcher::Delegate override. + virtual void OnURLFetchComplete(const URLFetcher* source, + const GURL& url, + const URLRequestStatus& status, + int response_code, + const ResponseCookies& cookies, + const std::string& data); + + // Create a URLFetcher for a given request message and response handler. + void CreateFetcher(const em::DeviceManagementRequest& request, + ResponseHandler* handler, + const std::string& query_params, + const std::string& extra_headers); + + // Fill in the common query parameters. + void PutCommonQueryParameters(URLQueryParameters* params); + + // Server at which to contact the service. + const std::string server_url_; + + // The request context we use. + scoped_refptr<URLRequestContextGetter> request_context_getter_; + + // Keeps track of all in-flight requests an their response handlers. + ResponseHandlerMap response_handlers_; + + DISALLOW_COPY_AND_ASSIGN(DeviceManagementBackendImpl); +}; + +} // namespace policy + +#endif // CHROME_BROWSER_POLICY_DEVICE_MANAGEMENT_BACKEND_IMPL_H_ diff --git a/chrome/browser/policy/device_management_backend_impl_browsertest.cc b/chrome/browser/policy/device_management_backend_impl_browsertest.cc new file mode 100644 index 0000000..43df7be --- /dev/null +++ b/chrome/browser/policy/device_management_backend_impl_browsertest.cc @@ -0,0 +1,141 @@ +// Copyright (c) 2010 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 "chrome/browser/policy/device_management_backend_impl.h" + +#include "base/message_loop.h" +#include "chrome/browser/browser_thread.h" +#include "chrome/browser/policy/device_management_backend_mock.h" +#include "chrome/test/in_process_browser_test.h" +#include "net/url_request/url_request.h" +#include "net/url_request/url_request_test_job.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/gmock/include/gmock/gmock.h" + +using testing::_; +using testing::DoAll; +using testing::Invoke; +using testing::InvokeWithoutArgs; + +namespace policy { + +namespace { + +const char kServiceUrl[] = "http://example.com/service"; + +// Binary representation of successful register response containing a token. +const char kServiceResponseRegister[] = + "\x08\x00\x1a\x22\x0a\x20\x64\x64\x32\x63\x38\x63\x33\x65\x64\x61" + "\x63\x63\x34\x61\x33\x32\x38\x31\x66\x33\x38\x62\x36\x35\x31\x31" + "\x36\x64\x61\x62\x66\x63"; +// Contains a single policy setting, namely HomepageIsNewTabPage: false. +const char kServiceResponsePolicy[] = + "\x08\x00\x2a\x2a\x0a\x28\x0a\x06\x70\x6f\x6c\x69\x63\x79\x12\x1e" + "\x0a\x1c\x0a\x14\x48\x6f\x6d\x65\x70\x61\x67\x65\x49\x73\x4e\x65" + "\x77\x54\x61\x62\x50\x61\x67\x65\x12\x04\x08\x01\x10\x00"; +// Successful unregister response. +const char kServiceResponseUnregister[] = + "\x08\x00\x22\x00"; + +#define PROTO_STRING(name) (std::string(name, arraysize(name) - 1)) + +} // namespace + +// Interceptor implementation that returns test data back to the service. +class CannedResponseInterceptor : public URLRequest::Interceptor { + public: + CannedResponseInterceptor(const GURL& service_url, + const std::string& response_data) + : service_url_(service_url), + response_data_(response_data) { + URLRequest::RegisterRequestInterceptor(this); + } + + virtual ~CannedResponseInterceptor() { + URLRequest::UnregisterRequestInterceptor(this); + } + + private: + // URLRequest::Interceptor overrides. + virtual URLRequestJob* MaybeIntercept(URLRequest* request) { + if (request->url().GetOrigin() == service_url_.GetOrigin() && + request->url().path() == service_url_.path()) { + return new URLRequestTestJob(request, + URLRequestTestJob::test_headers(), + response_data_, + true); + } + + return NULL; + } + + const GURL service_url_; + const std::string response_data_; +}; + +class DeviceManagementBackendImplIntegrationTest : public InProcessBrowserTest { + public: + void CaptureToken(const em::DeviceRegisterResponse& response) { + token_ = response.device_management_token(); + } + + protected: + DeviceManagementBackendImplIntegrationTest() { + URLFetcher::enable_interception_for_tests(true); + } + + std::string token_; +}; + +static void QuitMessageLoop() { + MessageLoop::current()->Quit(); +} + +IN_PROC_BROWSER_TEST_F(DeviceManagementBackendImplIntegrationTest, + RegisterAndFetchPolicy) { + DeviceManagementBackendImpl service(kServiceUrl); + + { + CannedResponseInterceptor interceptor( + GURL(kServiceUrl), PROTO_STRING(kServiceResponseRegister)); + DeviceRegisterResponseDelegateMock delegate; + EXPECT_CALL(delegate, HandleRegisterResponse(_)) + .WillOnce(DoAll(Invoke(this, &DeviceManagementBackendImplIntegrationTest + ::CaptureToken), + InvokeWithoutArgs(QuitMessageLoop))); + em::DeviceRegisterRequest request; + service.ProcessRegisterRequest("token", "device id", request, &delegate); + MessageLoop::current()->Run(); + } + + { + CannedResponseInterceptor interceptor( + GURL(kServiceUrl), PROTO_STRING(kServiceResponsePolicy)); + DevicePolicyResponseDelegateMock delegate; + EXPECT_CALL(delegate, HandlePolicyResponse(_)) + .WillOnce(InvokeWithoutArgs(QuitMessageLoop)); + em::DevicePolicyRequest request; + request.set_policy_scope("chrome"); + em::DevicePolicySettingRequest* setting_request = + request.add_setting_request(); + setting_request->set_key("policy"); + service.ProcessPolicyRequest(token_, request, &delegate); + + MessageLoop::current()->Run(); + } + + { + CannedResponseInterceptor interceptor( + GURL(kServiceUrl), PROTO_STRING(kServiceResponseUnregister)); + DeviceUnregisterResponseDelegateMock delegate; + EXPECT_CALL(delegate, HandleUnregisterResponse(_)) + .WillOnce(InvokeWithoutArgs(QuitMessageLoop)); + em::DeviceUnregisterRequest request; + service.ProcessUnregisterRequest(token_, request, &delegate); + + MessageLoop::current()->Run(); + } +} + +} // namespace policy diff --git a/chrome/browser/policy/device_management_backend_impl_unittest.cc b/chrome/browser/policy/device_management_backend_impl_unittest.cc new file mode 100644 index 0000000..1e93f3a --- /dev/null +++ b/chrome/browser/policy/device_management_backend_impl_unittest.cc @@ -0,0 +1,375 @@ +// Copyright (c) 2010 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 "chrome/browser/policy/device_management_backend_impl.h" + +#include "base/message_loop.h" +#include "base/string_split.h" +#include "chrome/browser/browser_thread.h" +#include "chrome/browser/policy/device_management_backend_mock.h" +#include "chrome/common/net/test_url_fetcher_factory.h" +#include "net/base/escape.h" +#include "net/url_request/url_request_status.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/gmock/include/gmock/gmock.h" + +using testing::_; + +namespace policy { + +namespace { + +const char kServiceURL[] = "https://example.com/management_service"; + +// Encoded error response messages for testing the error code paths. +const char kResponseEmpty[] = "\x08\x00"; +const char kResponseErrorManagementNotSupported[] = "\x08\x01"; +const char kResponseErrorDeviceNotFound[] = "\x08\x02"; +const char kResponseErrorManagementTokenInvalid[] = "\x08\x03"; +const char kResponseErrorActivationPending[] = "\x08\x04"; + +#define PROTO_STRING(name) (std::string(name, arraysize(name) - 1)) + +} // namespace + +// Unit tests for the google apps policy backend. The pattern here is each test +// case triggeres a request and installs a mock delegate. The test will run and +// the default action installed on the test delegate will quit the loop. +template<typename TESTBASE> +class DeviceManagementBackendImplTestBase : public TESTBASE { + protected: + DeviceManagementBackendImplTestBase() + : io_thread_(BrowserThread::IO, &loop_), + service_(kServiceURL) {} + + virtual void SetUp() { + URLFetcher::set_factory(&factory_); + } + + virtual void TearDown() { + URLFetcher::set_factory(NULL); + loop_.RunAllPending(); + } + + MessageLoopForUI loop_; + BrowserThread io_thread_; + TestURLFetcherFactory factory_; + DeviceManagementBackendImpl service_; +}; + +struct FailedRequestParams { + FailedRequestParams(DeviceManagementBackend::ErrorCode expected_error, + URLRequestStatus::Status request_status, + int http_status, + const std::string& response) + : expected_error_(expected_error), + request_status_(request_status, 0), + http_status_(http_status), + response_(response) {} + + DeviceManagementBackend::ErrorCode expected_error_; + URLRequestStatus request_status_; + int http_status_; + std::string response_; +}; + +// A parameterized test case for erroneous response situations, they're mostly +// the same for all kinds of requests. +class DeviceManagementBackendImplFailedRequestTest + : public DeviceManagementBackendImplTestBase< + testing::TestWithParam<FailedRequestParams> > { +}; + +TEST_P(DeviceManagementBackendImplFailedRequestTest, RegisterRequest) { + DeviceRegisterResponseDelegateMock mock; + EXPECT_CALL(mock, OnError(GetParam().expected_error_)); + em::DeviceRegisterRequest request; + service_.ProcessRegisterRequest("token", "device id", request, &mock); + TestURLFetcher* fetcher = factory_.GetFetcherByID(0); + ASSERT_TRUE(fetcher); + + fetcher->delegate()->OnURLFetchComplete(fetcher, + GURL(kServiceURL), + GetParam().request_status_, + GetParam().http_status_, + ResponseCookies(), + GetParam().response_); +} + +TEST_P(DeviceManagementBackendImplFailedRequestTest, UnregisterRequest) { + DeviceUnregisterResponseDelegateMock mock; + EXPECT_CALL(mock, OnError(GetParam().expected_error_)); + em::DeviceUnregisterRequest request; + service_.ProcessUnregisterRequest("token", request, &mock); + TestURLFetcher* fetcher = factory_.GetFetcherByID(0); + ASSERT_TRUE(fetcher); + + fetcher->delegate()->OnURLFetchComplete(fetcher, + GURL(kServiceURL), + GetParam().request_status_, + GetParam().http_status_, + ResponseCookies(), + GetParam().response_); +} + +TEST_P(DeviceManagementBackendImplFailedRequestTest, PolicyRequest) { + DevicePolicyResponseDelegateMock mock; + EXPECT_CALL(mock, OnError(GetParam().expected_error_)); + em::DevicePolicyRequest request; + request.set_policy_scope("Chrome"); + em::DevicePolicySettingRequest* setting_request = + request.add_setting_request(); + setting_request->set_key("policy"); + service_.ProcessPolicyRequest("token", request, &mock); + TestURLFetcher* fetcher = factory_.GetFetcherByID(0); + ASSERT_TRUE(fetcher); + + fetcher->delegate()->OnURLFetchComplete(fetcher, + GURL(kServiceURL), + GetParam().request_status_, + GetParam().http_status_, + ResponseCookies(), + GetParam().response_); +} + +INSTANTIATE_TEST_CASE_P( + DeviceManagementBackendImplFailedRequestTestInstance, + DeviceManagementBackendImplFailedRequestTest, + testing::Values( + FailedRequestParams( + DeviceManagementBackend::kErrorRequestFailed, + URLRequestStatus::FAILED, + 200, + PROTO_STRING(kResponseEmpty)), + FailedRequestParams( + DeviceManagementBackend::kErrorHttpStatus, + URLRequestStatus::SUCCESS, + 500, + PROTO_STRING(kResponseEmpty)), + FailedRequestParams( + DeviceManagementBackend::kErrorResponseDecoding, + URLRequestStatus::SUCCESS, + 200, + PROTO_STRING("Not a protobuf.")), + FailedRequestParams( + DeviceManagementBackend::kErrorServiceManagementNotSupported, + URLRequestStatus::SUCCESS, + 200, + PROTO_STRING(kResponseErrorManagementNotSupported)), + FailedRequestParams( + DeviceManagementBackend::kErrorServiceDeviceNotFound, + URLRequestStatus::SUCCESS, + 200, + PROTO_STRING(kResponseErrorDeviceNotFound)), + FailedRequestParams( + DeviceManagementBackend::kErrorServiceManagementTokenInvalid, + URLRequestStatus::SUCCESS, + 200, + PROTO_STRING(kResponseErrorManagementTokenInvalid)), + FailedRequestParams( + DeviceManagementBackend::kErrorServiceActivationPending, + URLRequestStatus::SUCCESS, + 200, + PROTO_STRING(kResponseErrorActivationPending)))); + +class DeviceManagementBackendImplTest + : public DeviceManagementBackendImplTestBase<testing::Test> { +}; + +MATCHER_P(MessageEquals, reference, "") { + std::string reference_data; + std::string arg_data; + return arg.SerializeToString(&arg_data) && + reference.SerializeToString(&reference_data) && + arg_data == reference_data; +} + +// Simple query parameter parser for testing. +class QueryParams { + public: + explicit QueryParams(const std::string& query) { + base::SplitStringIntoKeyValuePairs(query, '=', '&', ¶ms_); + } + + bool Check(const std::string& name, const std::string& expected_value) { + bool found = false; + for (ParamMap::const_iterator i(params_.begin()); i != params_.end(); ++i) { + std::string unescaped_name( + UnescapeURLComponent(i->first, + UnescapeRule::NORMAL | + UnescapeRule::SPACES | + UnescapeRule::URL_SPECIAL_CHARS | + UnescapeRule::CONTROL_CHARS | + UnescapeRule::REPLACE_PLUS_WITH_SPACE)); + if (unescaped_name == name) { + if (found) + return false; + found = true; + std::string unescaped_value( + UnescapeURLComponent(i->second, + UnescapeRule::NORMAL | + UnescapeRule::SPACES | + UnescapeRule::URL_SPECIAL_CHARS | + UnescapeRule::CONTROL_CHARS | + UnescapeRule::REPLACE_PLUS_WITH_SPACE)); + if (unescaped_value != expected_value) + return false; + } + } + return found; + } + + private: + typedef std::vector<std::pair<std::string, std::string> > ParamMap; + ParamMap params_; +}; + +TEST_F(DeviceManagementBackendImplTest, RegisterRequest) { + DeviceRegisterResponseDelegateMock mock; + em::DeviceRegisterResponse expected_response; + expected_response.set_device_management_token("mtoken"); + EXPECT_CALL(mock, HandleRegisterResponse(MessageEquals(expected_response))); + em::DeviceRegisterRequest request; + service_.ProcessRegisterRequest("token", "device id", request, &mock); + TestURLFetcher* fetcher = factory_.GetFetcherByID(0); + ASSERT_TRUE(fetcher); + + // Check the data the fetcher received. + const GURL& request_url(fetcher->original_url()); + const GURL service_url(kServiceURL); + EXPECT_EQ(service_url.scheme(), request_url.scheme()); + EXPECT_EQ(service_url.host(), request_url.host()); + EXPECT_EQ(service_url.port(), request_url.port()); + EXPECT_EQ(service_url.path(), request_url.path()); + + QueryParams query_params(request_url.query()); + EXPECT_TRUE(query_params.Check("request", "register")); + + em::DeviceManagementRequest expected_request_wrapper; + expected_request_wrapper.mutable_register_request()->CopyFrom(request); + std::string expected_request_data; + ASSERT_TRUE(expected_request_wrapper.SerializeToString( + &expected_request_data)); + EXPECT_EQ(expected_request_data, fetcher->upload_data()); + + // Generate the response. + std::string response_data; + em::DeviceManagementResponse response_wrapper; + response_wrapper.set_error(em::DeviceManagementResponse::SUCCESS); + response_wrapper.mutable_register_response()->CopyFrom(expected_response); + ASSERT_TRUE(response_wrapper.SerializeToString(&response_data)); + URLRequestStatus status(URLRequestStatus::SUCCESS, 0); + fetcher->delegate()->OnURLFetchComplete(fetcher, + GURL(kServiceURL), + status, + 200, + ResponseCookies(), + response_data); +} + +TEST_F(DeviceManagementBackendImplTest, UnregisterRequest) { + DeviceUnregisterResponseDelegateMock mock; + em::DeviceUnregisterResponse expected_response; + EXPECT_CALL(mock, HandleUnregisterResponse(MessageEquals(expected_response))); + em::DeviceUnregisterRequest request; + service_.ProcessUnregisterRequest("dmtokenvalue", request, &mock); + TestURLFetcher* fetcher = factory_.GetFetcherByID(0); + ASSERT_TRUE(fetcher); + + // Check the data the fetcher received. + const GURL& request_url(fetcher->original_url()); + const GURL service_url(kServiceURL); + EXPECT_EQ(service_url.scheme(), request_url.scheme()); + EXPECT_EQ(service_url.host(), request_url.host()); + EXPECT_EQ(service_url.port(), request_url.port()); + EXPECT_EQ(service_url.path(), request_url.path()); + + QueryParams query_params(request_url.query()); + EXPECT_TRUE(query_params.Check("request", "unregister")); + + em::DeviceManagementRequest expected_request_wrapper; + expected_request_wrapper.mutable_unregister_request()->CopyFrom(request); + std::string expected_request_data; + ASSERT_TRUE(expected_request_wrapper.SerializeToString( + &expected_request_data)); + EXPECT_EQ(expected_request_data, fetcher->upload_data()); + + // Generate the response. + std::string response_data; + em::DeviceManagementResponse response_wrapper; + response_wrapper.set_error(em::DeviceManagementResponse::SUCCESS); + response_wrapper.mutable_unregister_response()->CopyFrom(expected_response); + ASSERT_TRUE(response_wrapper.SerializeToString(&response_data)); + URLRequestStatus status(URLRequestStatus::SUCCESS, 0); + fetcher->delegate()->OnURLFetchComplete(fetcher, + GURL(kServiceURL), + status, + 200, + ResponseCookies(), + response_data); +} + +TEST_F(DeviceManagementBackendImplTest, PolicyRequest) { + DevicePolicyResponseDelegateMock mock; + em::DevicePolicyResponse expected_response; + em::DevicePolicySetting* policy_setting = expected_response.add_setting(); + policy_setting->set_policy_key("policy"); + policy_setting->set_watermark("fresh"); + em::GenericSetting* policy_value = policy_setting->mutable_policy_value(); + em::GenericNamedValue* named_value = policy_value->add_named_value(); + named_value->set_name("HomepageLocation"); + named_value->mutable_value()->set_value_type( + em::GenericValue::VALUE_TYPE_STRING); + named_value->mutable_value()->set_string_value("http://www.chromium.org"); + named_value = policy_value->add_named_value(); + named_value->set_name("HomepageIsNewTabPage"); + named_value->mutable_value()->set_value_type( + em::GenericValue::VALUE_TYPE_BOOL); + named_value->mutable_value()->set_bool_value(false); + EXPECT_CALL(mock, HandlePolicyResponse(MessageEquals(expected_response))); + + em::DevicePolicyRequest request; + request.set_policy_scope("chromium"); + em::DevicePolicySettingRequest* setting_request = + request.add_setting_request(); + setting_request->set_key("policy"); + setting_request->set_watermark("stale"); + service_.ProcessPolicyRequest("dmtokenvalue", request, &mock); + TestURLFetcher* fetcher = factory_.GetFetcherByID(0); + ASSERT_TRUE(fetcher); + + // Check the data the fetcher received. + const GURL& request_url(fetcher->original_url()); + const GURL service_url(kServiceURL); + EXPECT_EQ(service_url.scheme(), request_url.scheme()); + EXPECT_EQ(service_url.host(), request_url.host()); + EXPECT_EQ(service_url.port(), request_url.port()); + EXPECT_EQ(service_url.path(), request_url.path()); + + QueryParams query_params(request_url.query()); + EXPECT_TRUE(query_params.Check("request", "policy")); + + em::DeviceManagementRequest expected_request_wrapper; + expected_request_wrapper.mutable_policy_request()->CopyFrom(request); + std::string expected_request_data; + ASSERT_TRUE(expected_request_wrapper.SerializeToString( + &expected_request_data)); + EXPECT_EQ(expected_request_data, fetcher->upload_data()); + + // Generate the response. + std::string response_data; + em::DeviceManagementResponse response_wrapper; + response_wrapper.set_error(em::DeviceManagementResponse::SUCCESS); + response_wrapper.mutable_policy_response()->CopyFrom(expected_response); + ASSERT_TRUE(response_wrapper.SerializeToString(&response_data)); + URLRequestStatus status(URLRequestStatus::SUCCESS, 0); + fetcher->delegate()->OnURLFetchComplete(fetcher, + GURL(kServiceURL), + status, + 200, + ResponseCookies(), + response_data); +} + +} // namespace policy diff --git a/chrome/browser/policy/device_management_backend_mock.h b/chrome/browser/policy/device_management_backend_mock.h new file mode 100644 index 0000000..170fe3a --- /dev/null +++ b/chrome/browser/policy/device_management_backend_mock.h @@ -0,0 +1,38 @@ +// Copyright (c) 2010 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. + +#ifndef CHROME_BROWSER_POLICY_DEVICE_MANAGEMENT_BACKEND_MOCK_H_ +#define CHROME_BROWSER_POLICY_DEVICE_MANAGEMENT_BACKEND_MOCK_H_ + +#include "testing/gmock/include/gmock/gmock.h" + +namespace policy { + +// Mock classes for the various DeviceManagementBackend delegates that allow to +// capture callbacks using gmock. +class DeviceRegisterResponseDelegateMock + : public DeviceManagementBackend::DeviceRegisterResponseDelegate { + public: + MOCK_METHOD1(HandleRegisterResponse, void(const em::DeviceRegisterResponse&)); + MOCK_METHOD1(OnError, void(DeviceManagementBackend::ErrorCode error)); +}; + +class DeviceUnregisterResponseDelegateMock + : public DeviceManagementBackend::DeviceUnregisterResponseDelegate { + public: + MOCK_METHOD1(HandleUnregisterResponse, + void(const em::DeviceUnregisterResponse&)); + MOCK_METHOD1(OnError, void(DeviceManagementBackend::ErrorCode error)); +}; + +class DevicePolicyResponseDelegateMock + : public DeviceManagementBackend::DevicePolicyResponseDelegate { + public: + MOCK_METHOD1(HandlePolicyResponse, void(const em::DevicePolicyResponse&)); + MOCK_METHOD1(OnError, void(DeviceManagementBackend::ErrorCode error)); +}; + +} // namespace policy + +#endif // CHROME_BROWSER_POLICY_DEVICE_MANAGEMENT_BACKEND_MOCK_H_ |