Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(154)

Side by Side Diff: chrome/browser/policy/cloud_policy_manager_browsertest.cc

Issue 12209070: Fix cloud policy duplicate registrations issue. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: fixed expected type on desktop, fixed return type Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/bind.h"
6 #include "base/bind_helpers.h"
7 #include "base/command_line.h"
8 #include "base/memory/ref_counted.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/run_loop.h"
11 #include "base/values.h"
12 #include "chrome/browser/browser_process.h"
13 #include "chrome/browser/policy/browser_policy_connector.h"
14 #include "chrome/browser/policy/cloud_policy_client.h"
15 #include "chrome/browser/policy/cloud_policy_constants.h"
16 #include "chrome/browser/policy/policy_map.h"
17 #include "chrome/browser/policy/policy_service.h"
18 #include "chrome/browser/policy/proto/device_management_backend.pb.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/browser/ui/browser.h"
21 #include "chrome/common/chrome_switches.h"
22 #include "chrome/test/base/in_process_browser_test.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "googleurl/src/gurl.h"
25 #include "net/base/io_buffer.h"
26 #include "net/base/net_errors.h"
27 #include "net/base/upload_data_stream.h"
28 #include "net/url_request/url_request_error_job.h"
29 #include "net/url_request/url_request_filter.h"
30 #include "net/url_request/url_request_job_factory.h"
31 #include "net/url_request/url_request_test_job.h"
32 #include "policy/policy_constants.h"
33 #include "testing/gmock/include/gmock/gmock.h"
34 #include "testing/gtest/include/gtest/gtest.h"
35
36 #if defined(OS_CHROMEOS)
37 #include "chrome/browser/chromeos/login/user_manager.h"
38 #include "chrome/browser/policy/user_cloud_policy_manager_chromeos.h"
39 #else
40 #include "chrome/browser/policy/user_cloud_policy_manager.h"
41 #include "chrome/browser/policy/user_cloud_policy_manager_factory.h"
42 #include "chrome/browser/signin/signin_manager.h"
43 #include "chrome/browser/signin/signin_manager_factory.h"
44 #endif
45
46 using testing::AnyNumber;
47 using testing::InvokeWithoutArgs;
48 using testing::Mock;
49 using testing::_;
50
51 namespace em = enterprise_management;
52
53 namespace policy {
54
55 namespace {
56
57 // Dummy service URL for testing with request interception enabled.
58 const char kServiceUrl[] = "http://dmserver.com/device_management";
59
60 // HTTP headers for replies simulating a bad request response.
61 const char kBadHeaders[] =
62 "HTTP/1.1 400 Bad request\0"
63 "Content-type: application/protobuf\0"
64 "\0";
65
66 // HTTP headers for replies containing good responses.
67 const char kGoodHeaders[] =
68 "HTTP/1.1 200 OK\0"
69 "Content-type: application/protobuf\0"
70 "\0";
71
72 // A callback that returns a new URLRequestJob given a URLRequest.
73 // This is used to queue callbacks that will handle expected requests.
74 typedef base::Callback<net::URLRequestJob*(net::URLRequest*,
75 net::NetworkDelegate*)> JobCallback;
76
77 // Helper callback for jobs that should fail with a network |error|.
78 net::URLRequestJob* ErrorJobCallback(int error,
79 net::URLRequest* request,
80 net::NetworkDelegate* network_delegate) {
81 return new net::URLRequestErrorJob(request, network_delegate, error);
82 }
83
84 // Helper callback for jobs that should fail with a 400 HTTP error.
85 net::URLRequestJob* BadRequestJobCallback(
86 net::URLRequest* request,
87 net::NetworkDelegate* network_delegate) {
Mattias Nissler (ping if slow) 2013/02/13 10:50:43 move kBadHeaders definition here?
Joao da Silva 2013/02/13 15:57:15 Done.
88 std::string headers(kBadHeaders, arraysize(kBadHeaders));
89 return new net::URLRequestTestJob(
90 request, network_delegate, headers, std::string(), true);
91 }
92
93 // Parses the upload data in |request| into |request_msg|, and validates the
94 // request. The query string in the URL must contain the |expected_type| for
95 // the "request" parameter. Returns true if all checks succeeded, and the
96 // request data has been parsed into |request_msg|.
97 bool ValidRequest(net::URLRequest* request,
98 const std::string& expected_type,
99 em::DeviceManagementRequest* request_msg) {
100 if (request->method() != "POST")
101 return false;
102 std::string spec = request->url().spec();
103 if (spec.find("request=" + expected_type) == std::string::npos)
104 return false;
105
106 // This destroys the UploadDataStream to read its data, but that's OK since
107 // the request isn't going anywhere.
108 net::UploadDataStream* stream =
109 const_cast<net::UploadDataStream*>(request->get_upload());
Mattias Nissler (ping if slow) 2013/02/13 10:50:43 so there's no way to read a const UploadDataStream
Joao da Silva 2013/02/13 15:57:15 It's actually possible to read from the const obje
110 if (!stream)
111 return false;
112 stream->Init(net::CompletionCallback());
113 scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(1024));
114 int size = stream->Read(buffer.get(), 1024, net::CompletionCallback());
115 std::string data(buffer->data(), size);
116 if (!request_msg->ParseFromString(data))
117 return false;
118
119 return true;
120 }
121
122 // Helper callback for register jobs that should suceed. Validates the request
123 // parameters and returns an appropriate response job. If |expect_reregister|
124 // is true then the reregister flag must be set in the DeviceRegisterRequest
125 // protobuf.
126 net::URLRequestJob* RegisterJobCallback(
127 bool expect_reregister,
128 net::URLRequest* request,
129 net::NetworkDelegate* network_delegate) {
130 em::DeviceManagementRequest request_msg;
131 if (!ValidRequest(request, "register", &request_msg))
132 return BadRequestJobCallback(request, network_delegate);
133
134 if (!request_msg.has_register_request() ||
135 request_msg.has_unregister_request() ||
136 request_msg.has_policy_request() ||
137 request_msg.has_device_status_report_request() ||
138 request_msg.has_session_status_report_request() ||
139 request_msg.has_auto_enrollment_request()) {
140 return BadRequestJobCallback(request, network_delegate);
141 }
142
143 const em::DeviceRegisterRequest& register_request =
144 request_msg.register_request();
145 if (expect_reregister &&
146 (!register_request.has_reregister() || !register_request.reregister())) {
147 return BadRequestJobCallback(request, network_delegate);
148 } else if (!expect_reregister &&
149 register_request.has_reregister() &&
150 register_request.reregister()) {
151 return BadRequestJobCallback(request, network_delegate);
152 }
153
154 em::DeviceRegisterRequest::Type expected_type =
155 #if defined(OS_CHROMEOS)
156 em::DeviceRegisterRequest::USER;
157 #else
158 em::DeviceRegisterRequest::BROWSER;
159 #endif
160 if (!register_request.has_type() || register_request.type() != expected_type)
161 return BadRequestJobCallback(request, network_delegate);
162
163 em::DeviceManagementResponse response;
164 em::DeviceRegisterResponse* register_response =
165 response.mutable_register_response();
166 register_response->set_device_management_token("s3cr3t70k3n");
167 std::string data;
168 response.SerializeToString(&data);
169
170 std::string headers(kGoodHeaders, arraysize(kGoodHeaders));
Mattias Nissler (ping if slow) 2013/02/13 10:50:43 seems like kGoodHeaders could be a static constant
Joao da Silva 2013/02/13 15:57:15 Done.
171 return new net::URLRequestTestJob(
172 request, network_delegate, headers, data, true);
173 }
174
175 class MockCloudPolicyClientObserver : public CloudPolicyClient::Observer {
176 public:
177 MockCloudPolicyClientObserver() {}
178 virtual ~MockCloudPolicyClientObserver() {}
179
180 MOCK_METHOD1(OnPolicyFetched, void(CloudPolicyClient*));
181 MOCK_METHOD1(OnRegistrationStateChanged, void(CloudPolicyClient*));
182 MOCK_METHOD1(OnClientError, void(CloudPolicyClient*));
183 };
184
185 // Intercepts all requests to "dmserver.com" while in scope. Must be created and
Mattias Nissler (ping if slow) 2013/02/13 10:50:43 Let's use localhost or example.com to be on the sa
Joao da Silva 2013/02/13 15:57:15 Done.
186 // destroyed while the IO thread is valid.
187 class RequestInterceptor {
Mattias Nissler (ping if slow) 2013/02/13 10:50:43 It seems this both large and generic enough to mov
Joao da Silva 2013/02/13 15:57:15 Done. Also updated device_management_service_brows
188 public:
189 RequestInterceptor() {
190 delegate_ = new Delegate();
191 scoped_ptr<net::URLRequestJobFactory::ProtocolHandler> handler(delegate_);
192 PostToIOAndWait(
193 base::Bind(&net::URLRequestFilter::AddHostnameProtocolHandler,
194 base::Unretained(net::URLRequestFilter::GetInstance()),
195 "http", "dmserver.com", base::Passed(&handler)));
196 }
197
198 ~RequestInterceptor() {
199 // RemoveHostnameHandler() destroys the |delegate_|, which is owned by
200 // the URLRequestFilter.
201 delegate_ = NULL;
202 PostToIOAndWait(
203 base::Bind(&net::URLRequestFilter::RemoveHostnameHandler,
204 base::Unretained(net::URLRequestFilter::GetInstance()),
205 "http", "dmserver.com"));
206 }
207
208 // Returns the number of pending callback jobs that haven't been used yet.
209 size_t GetPendingSize() {
210 size_t pending_size = 0xffff;
Mattias Nissler (ping if slow) 2013/02/13 10:50:43 That's a weird choice. How about std::numeric_limi
Joao da Silva 2013/02/13 15:57:15 Done.
211 PostToIOAndWait(base::Bind(&Delegate::GetPendingSize,
212 base::Unretained(delegate_),
213 &pending_size));
214 return pending_size;
215 }
216
217 // Queues |callback| to handle a request to "dmserver.com". Each callback is
218 // used only once, and in the order that they're pushed.
219 void PushJobCallback(const JobCallback& callback) {
220 PostToIOAndWait(base::Bind(&Delegate::PushJobCallback,
221 base::Unretained(delegate_),
222 callback));
223 }
224
225 private:
226 class Delegate : public net::URLRequestJobFactory::ProtocolHandler {
227 public:
228 virtual net::URLRequestJob* MaybeCreateJob(
229 net::URLRequest* request,
230 net::NetworkDelegate* network_delegate) const OVERRIDE {
231 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
232
233 if (request->url().host() != "dmserver.com") {
234 // Reject requests to other servers.
235 return ErrorJobCallback(
236 net::ERR_CONNECTION_REFUSED, request, network_delegate);
237 }
238
239 if (pending_job_callbacks_.empty()) {
240 // Reject dmserver requests by default.
241 return BadRequestJobCallback(request, network_delegate);
242 }
243
244 JobCallback callback = pending_job_callbacks_.front();
245 pending_job_callbacks_.pop();
246 return callback.Run(request, network_delegate);
247 }
248
249 void GetPendingSize(size_t* pending_size) const {
250 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
251 *pending_size = pending_job_callbacks_.size();
252 }
253
254 void PushJobCallback(const JobCallback& callback) {
255 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
256 pending_job_callbacks_.push(callback);
257 }
258
259 private:
260 // The queue of pending callbacks. 'mutable' because MaybeCreateJob() is a
261 // const method; it can't reenter though, because it runs exclusively on
262 // the IO thread.
263 mutable std::queue<JobCallback> pending_job_callbacks_;
264 };
265
266 // Helper to execute a |task| on IO, and return only after it has completed.
267 void PostToIOAndWait(const base::Closure& task) {
268 base::RunLoop run_loop;
269 content::BrowserThread::PostTaskAndReply(
270 content::BrowserThread::IO, FROM_HERE, task, run_loop.QuitClosure());
271 run_loop.Run();
272 }
273
274 // Owned by URLRequestFilter. This handle is valid on IO and only while the
275 // interceptor is valid.
276 Delegate* delegate_;
277
278 DISALLOW_COPY_AND_ASSIGN(RequestInterceptor);
Mattias Nissler (ping if slow) 2013/02/13 10:50:43 #include "base/basictypes.h"
Joao da Silva 2013/02/13 15:57:15 Done.
279 };
280
281 } // namespace
282
283 // Tests the cloud policy stack using a URLRequestJobFactory::ProtocolHandler
284 // to intercept requests and produce canned responses.
285 class CloudPolicyManagerTest : public InProcessBrowserTest {
286 protected:
287 CloudPolicyManagerTest() {}
288 virtual ~CloudPolicyManagerTest() {}
289
290 virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
291 CommandLine* command_line = CommandLine::ForCurrentProcess();
292 command_line->AppendSwitchASCII(switches::kDeviceManagementUrl,
293 kServiceUrl);
294 }
295
296 virtual void SetUpOnMainThread() OVERRIDE {
297 // Checks that no policies have been loaded by the other providers before
298 // setting up the cloud connection. Other policies configured in the test
299 // machine will interfere with these tests.
300 const PolicyMap& map = g_browser_process->policy_service()->GetPolicies(
301 PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()));
302 if (!map.empty()) {
303 base::DictionaryValue dict;
304 for (PolicyMap::const_iterator it = map.begin(); it != map.end(); ++it)
305 dict.SetWithoutPathExpansion(it->first, it->second.value->DeepCopy());
306 ADD_FAILURE()
307 << "There are pre-existing policies in this machine that will "
308 << "interfere with these tests. Policies found: " << dict;
309 }
Mattias Nissler (ping if slow) 2013/02/13 10:50:43 This should probably be factored out now that we h
Joao da Silva 2013/02/13 15:57:15 Done. Added policy/test_utils.h to contain helpers
310
311 interceptor_.reset(new RequestInterceptor());
312
313 BrowserPolicyConnector* connector =
314 g_browser_process->browser_policy_connector();
315 connector->ScheduleServiceInitialization(0);
316
317 #if !defined(OS_CHROMEOS)
318 // Mock a signed-in user. This is used by the UserCloudPolicyStore to pass
319 // the username to the UserCloudPolicyValidator.
320 SigninManager* signin_manager =
321 SigninManagerFactory::GetForProfile(browser()->profile());
322 ASSERT_TRUE(signin_manager);
323 signin_manager->SetAuthenticatedUsername("user@example.com");
324
325 UserCloudPolicyManager* policy_manager =
326 UserCloudPolicyManagerFactory::GetForProfile(browser()->profile());
327 ASSERT_TRUE(policy_manager);
328 policy_manager->Connect(g_browser_process->local_state(),
329 UserCloudPolicyManager::CreateCloudPolicyClient(
330 connector->device_management_service()).Pass());
331 #endif
Mattias Nissler (ping if slow) 2013/02/13 10:50:43 I'm wondering whether this test would be simpler t
Joao da Silva 2013/02/13 15:57:15 I'd prefer not to do this setup too. Unfortunately
332 }
333
334 virtual void CleanUpOnMainThread() OVERRIDE {
335 // Verify that all the expected requests were handled.
336 EXPECT_EQ(0u, interceptor_->GetPendingSize());
337
338 interceptor_.reset();
339 }
340
341 #if defined(OS_CHROMEOS)
342 UserCloudPolicyManagerChromeOS* policy_manager() {
343 return g_browser_process->browser_policy_connector()->
344 GetUserCloudPolicyManager();
345 }
346 #else
347 UserCloudPolicyManager* policy_manager() {
348 return UserCloudPolicyManagerFactory::GetForProfile(browser()->profile());
349 }
350 #endif // defined(OS_CHROMEOS)
351
352 // Register the client of the policy_manager() using a bogus auth token, and
353 // returns once the registration gets a result back.
354 void Register() {
355 ASSERT_TRUE(policy_manager());
356 ASSERT_TRUE(policy_manager()->core()->client());
357
358 base::RunLoop run_loop;
359 MockCloudPolicyClientObserver observer;
360 EXPECT_CALL(observer, OnRegistrationStateChanged(_))
361 .Times(AnyNumber())
362 .WillRepeatedly(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
363 EXPECT_CALL(observer, OnClientError(_))
364 .Times(AnyNumber())
365 .WillRepeatedly(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
366 policy_manager()->core()->client()->AddObserver(&observer);
367
368 // Give a bogus OAuth token to the |policy_manager|. This should make its
369 // CloudPolicyClient fetch the DMToken.
370 policy_manager()->RegisterClient("bogus");
371 run_loop.Run();
372 Mock::VerifyAndClearExpectations(&observer);
373 policy_manager()->core()->client()->RemoveObserver(&observer);
374 }
375
376 scoped_ptr<RequestInterceptor> interceptor_;
377 };
378
379 IN_PROC_BROWSER_TEST_F(CloudPolicyManagerTest, Register) {
380 // Accept one register request. The initial request should not include the
381 // reregister flag.
382 const bool expect_reregister = false;
383 interceptor_->PushJobCallback(
384 base::Bind(&RegisterJobCallback, expect_reregister));
385
386 EXPECT_FALSE(policy_manager()->core()->client()->is_registered());
387 ASSERT_NO_FATAL_FAILURE(Register());
388 EXPECT_TRUE(policy_manager()->core()->client()->is_registered());
389 }
390
391 IN_PROC_BROWSER_TEST_F(CloudPolicyManagerTest, RegisterFails) {
392 // The interceptor makes all requests fail by default; this will trigger
393 // an OnClientError() call on the observer.
394 EXPECT_FALSE(policy_manager()->core()->client()->is_registered());
395 ASSERT_NO_FATAL_FAILURE(Register());
396 EXPECT_FALSE(policy_manager()->core()->client()->is_registered());
397 }
398
399 IN_PROC_BROWSER_TEST_F(CloudPolicyManagerTest, RegisterFailsWithRetries) {
400 // Fail 4 times with ERR_NETWORK_CHANGED; the first 3 will trigger a retry,
401 // the last one will forward the error to the client and unblock the
402 // register process.
403 for (int i = 0; i < 4; ++i) {
404 interceptor_->PushJobCallback(base::Bind(&ErrorJobCallback,
405 net::ERR_NETWORK_CHANGED));
406 }
407
408 EXPECT_FALSE(policy_manager()->core()->client()->is_registered());
409 ASSERT_NO_FATAL_FAILURE(Register());
410 EXPECT_FALSE(policy_manager()->core()->client()->is_registered());
411 }
412
413 IN_PROC_BROWSER_TEST_F(CloudPolicyManagerTest, RegisterWithRetry) {
414 // Accept one register request after failing once. The retry request should
415 // set the reregister flag.
416 interceptor_->PushJobCallback(base::Bind(&ErrorJobCallback,
417 net::ERR_NETWORK_CHANGED));
418 const bool expect_reregister = true;
419 interceptor_->PushJobCallback(
420 base::Bind(&RegisterJobCallback, expect_reregister));
421
422 EXPECT_FALSE(policy_manager()->core()->client()->is_registered());
423 ASSERT_NO_FATAL_FAILURE(Register());
424 EXPECT_TRUE(policy_manager()->core()->client()->is_registered());
425 }
426
427 } // namespace policy
OLDNEW
« no previous file with comments | « chrome/browser/policy/cloud_policy_client_unittest.cc ('k') | chrome/browser/policy/device_management_service.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698