summaryrefslogtreecommitdiffstats
path: root/chrome/browser/policy
diff options
context:
space:
mode:
authormnissler@chromium.org <mnissler@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-11-03 14:40:44 +0000
committermnissler@chromium.org <mnissler@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-11-03 14:40:44 +0000
commitbb58e6e839255777617a32a77ccb85a9a06ce041 (patch)
treeed462618eb7238b8b65b6897a96c709c1ad5ae2d /chrome/browser/policy
parent2976306404950c06c6ce91374e977fd1ce85809d (diff)
downloadchromium_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')
-rw-r--r--chrome/browser/policy/device_management_backend_impl.cc415
-rw-r--r--chrome/browser/policy/device_management_backend_impl.h84
-rw-r--r--chrome/browser/policy/device_management_backend_impl_browsertest.cc141
-rw-r--r--chrome/browser/policy/device_management_backend_impl_unittest.cc375
-rw-r--r--chrome/browser/policy/device_management_backend_mock.h38
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(&params);
+ 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(&params);
+ 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(&params);
+ 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, '=', '&', &params_);
+ }
+
+ 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_