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

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

Issue 12209070: Fix cloud policy duplicate registrations issue. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: 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
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/policy/device_management_service.h" 5 #include "chrome/browser/policy/device_management_service.h"
6 6
7 #include <utility> 7 #include <utility>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/compiler_specific.h" 10 #include "base/compiler_specific.h"
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
46 namespace { 46 namespace {
47 47
48 const char kValueAgent[] = "%s %s(%s)"; 48 const char kValueAgent[] = "%s %s(%s)";
49 const char kValuePlatform[] = "%s|%s|%s"; 49 const char kValuePlatform[] = "%s|%s|%s";
50 50
51 const char kPostContentType[] = "application/protobuf"; 51 const char kPostContentType[] = "application/protobuf";
52 52
53 const char kServiceTokenAuthHeader[] = "Authorization: GoogleLogin auth="; 53 const char kServiceTokenAuthHeader[] = "Authorization: GoogleLogin auth=";
54 const char kDMTokenAuthHeader[] = "Authorization: GoogleDMToken token="; 54 const char kDMTokenAuthHeader[] = "Authorization: GoogleDMToken token=";
55 55
56 // Number of times to retry on ERR_NETWORK_CHANGED errors.
57 const int kMaxNetworkChangedRetries = 3;
58
56 // HTTP Error Codes of the DM Server with their concrete meanings in the context 59 // HTTP Error Codes of the DM Server with their concrete meanings in the context
57 // of the DM Server communication. 60 // of the DM Server communication.
58 const int kSuccess = 200; 61 const int kSuccess = 200;
59 const int kInvalidArgument = 400; 62 const int kInvalidArgument = 400;
60 const int kInvalidAuthCookieOrDMToken = 401; 63 const int kInvalidAuthCookieOrDMToken = 401;
61 const int kMissingLicenses = 402; 64 const int kMissingLicenses = 402;
62 const int kDeviceManagementNotAllowed = 403; 65 const int kDeviceManagementNotAllowed = 403;
63 const int kInvalidURL = 404; // This error is not coming from the GFE. 66 const int kInvalidURL = 404; // This error is not coming from the GFE.
64 const int kInvalidSerialNumber = 405; 67 const int kInvalidSerialNumber = 405;
65 const int kDeviceIdConflict = 409; 68 const int kDeviceIdConflict = 409;
(...skipping 17 matching lines...) Expand all
83 case net::ERR_HTTPS_PROXY_TUNNEL_RESPONSE: 86 case net::ERR_HTTPS_PROXY_TUNNEL_RESPONSE:
84 case net::ERR_MANDATORY_PROXY_CONFIGURATION_FAILED: 87 case net::ERR_MANDATORY_PROXY_CONFIGURATION_FAILED:
85 case net::ERR_PROXY_CERTIFICATE_INVALID: 88 case net::ERR_PROXY_CERTIFICATE_INVALID:
86 case net::ERR_SOCKS_CONNECTION_FAILED: 89 case net::ERR_SOCKS_CONNECTION_FAILED:
87 case net::ERR_SOCKS_CONNECTION_HOST_UNREACHABLE: 90 case net::ERR_SOCKS_CONNECTION_HOST_UNREACHABLE:
88 return true; 91 return true;
89 } 92 }
90 return false; 93 return false;
91 } 94 }
92 95
93 bool IsProtobufMimeType(const net::URLFetcher* source) { 96 bool IsProtobufMimeType(const net::URLFetcher* fetcher) {
94 return source->GetResponseHeaders()->HasHeaderValue( 97 return fetcher->GetResponseHeaders()->HasHeaderValue(
95 "content-type", "application/x-protobuffer"); 98 "content-type", "application/x-protobuffer");
96 } 99 }
97 100
101 bool FailedWithProxy(const net::URLFetcher* fetcher) {
102 if ((fetcher->GetLoadFlags() & net::LOAD_BYPASS_PROXY) != 0) {
103 // The request didn't use a proxy.
104 return false;
105 }
106
107 if (!fetcher->GetStatus().is_success() &&
108 IsProxyError(fetcher->GetStatus())) {
109 LOG(WARNING) << "Proxy failed while contacting dmserver.";
110 return true;
111 }
112
113 if (fetcher->GetStatus().is_success() &&
114 fetcher->GetResponseCode() == kSuccess &&
115 fetcher->WasFetchedViaProxy() &&
116 !IsProtobufMimeType(fetcher)) {
117 // The proxy server can be misconfigured but pointing to an existing
118 // server that replies to requests. Try to recover if a successful
119 // request that went through a proxy returns an unexpected mime type.
120 LOG(WARNING) << "Got bad mime-type in response from dmserver that was "
121 << "fetched via a proxy.";
122 return true;
123 }
124
125 return false;
126 }
127
98 const char* UserAffiliationToString(UserAffiliation affiliation) { 128 const char* UserAffiliationToString(UserAffiliation affiliation) {
99 switch (affiliation) { 129 switch (affiliation) {
100 case USER_AFFILIATION_MANAGED: 130 case USER_AFFILIATION_MANAGED:
101 return dm_protocol::kValueUserAffiliationManaged; 131 return dm_protocol::kValueUserAffiliationManaged;
102 case USER_AFFILIATION_NONE: 132 case USER_AFFILIATION_NONE:
103 return dm_protocol::kValueUserAffiliationNone; 133 return dm_protocol::kValueUserAffiliationNone;
104 } 134 }
105 NOTREACHED() << "Invalid user affiliation " << affiliation; 135 NOTREACHED() << "Invalid user affiliation " << affiliation;
106 return dm_protocol::kValueUserAffiliationNone; 136 return dm_protocol::kValueUserAffiliationNone;
107 } 137 }
(...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after
268 int response_code, 298 int response_code,
269 const net::ResponseCookies& cookies, 299 const net::ResponseCookies& cookies,
270 const std::string& data); 300 const std::string& data);
271 301
272 // Gets the URL to contact. 302 // Gets the URL to contact.
273 GURL GetURL(const std::string& server_url); 303 GURL GetURL(const std::string& server_url);
274 304
275 // Configures the fetcher, setting up payload and headers. 305 // Configures the fetcher, setting up payload and headers.
276 void ConfigureRequest(net::URLFetcher* fetcher); 306 void ConfigureRequest(net::URLFetcher* fetcher);
277 307
308 // Returns true if this job should be retried. |fetcher| has just completed,
309 // and can be inspected to determine if the request failed and should be
310 // retried.
311 bool ShouldRetry(const net::URLFetcher* fetcher);
312
313 // Invoked right before retrying this job.
314 void PrepareRetry();
315
278 protected: 316 protected:
279 // DeviceManagementRequestJob: 317 // DeviceManagementRequestJob:
280 virtual void Run() OVERRIDE; 318 virtual void Run() OVERRIDE;
281 319
282 private: 320 private:
283 // Invokes the callback with the given error code. 321 // Invokes the callback with the given error code.
284 void ReportError(DeviceManagementStatus code); 322 void ReportError(DeviceManagementStatus code);
285 323
286 // Pointer to the service this job is associated with. 324 // Pointer to the service this job is associated with.
287 DeviceManagementService* service_; 325 DeviceManagementService* service_;
288 326
327 // Whether the BYPASS_PROXY flag should be set by ConfigureRequest().
328 bool bypass_proxy_;
329
330 // Number of times that this job has been retried due to ERR_NETWORK_CHANGED.
331 int retries_count_;
332
289 DISALLOW_COPY_AND_ASSIGN(DeviceManagementRequestJobImpl); 333 DISALLOW_COPY_AND_ASSIGN(DeviceManagementRequestJobImpl);
290 }; 334 };
291 335
292 DeviceManagementRequestJobImpl::DeviceManagementRequestJobImpl( 336 DeviceManagementRequestJobImpl::DeviceManagementRequestJobImpl(
293 JobType type, 337 JobType type,
294 DeviceManagementService* service) 338 DeviceManagementService* service)
295 : DeviceManagementRequestJob(type), 339 : DeviceManagementRequestJob(type),
296 service_(service) {} 340 service_(service),
341 bypass_proxy_(false),
342 retries_count_(0) {}
297 343
298 DeviceManagementRequestJobImpl::~DeviceManagementRequestJobImpl() { 344 DeviceManagementRequestJobImpl::~DeviceManagementRequestJobImpl() {
299 service_->RemoveJob(this); 345 service_->RemoveJob(this);
300 } 346 }
301 347
302 void DeviceManagementRequestJobImpl::Run() { 348 void DeviceManagementRequestJobImpl::Run() {
303 service_->AddJob(this); 349 service_->AddJob(this);
304 } 350 }
305 351
306 void DeviceManagementRequestJobImpl::HandleResponse( 352 void DeviceManagementRequestJobImpl::HandleResponse(
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
382 result += '&'; 428 result += '&';
383 result += net::EscapeQueryParamValue(entry->first, true); 429 result += net::EscapeQueryParamValue(entry->first, true);
384 result += '='; 430 result += '=';
385 result += net::EscapeQueryParamValue(entry->second, true); 431 result += net::EscapeQueryParamValue(entry->second, true);
386 } 432 }
387 return GURL(result); 433 return GURL(result);
388 } 434 }
389 435
390 void DeviceManagementRequestJobImpl::ConfigureRequest( 436 void DeviceManagementRequestJobImpl::ConfigureRequest(
391 net::URLFetcher* fetcher) { 437 net::URLFetcher* fetcher) {
438 fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
439 net::LOAD_DO_NOT_SAVE_COOKIES |
440 net::LOAD_DISABLE_CACHE |
441 (bypass_proxy_ ? net::LOAD_BYPASS_PROXY : 0));
392 std::string payload; 442 std::string payload;
393 CHECK(request_.SerializeToString(&payload)); 443 CHECK(request_.SerializeToString(&payload));
394 fetcher->SetUploadData(kPostContentType, payload); 444 fetcher->SetUploadData(kPostContentType, payload);
395 std::string extra_headers; 445 std::string extra_headers;
396 if (!gaia_token_.empty()) 446 if (!gaia_token_.empty())
397 extra_headers += kServiceTokenAuthHeader + gaia_token_ + "\n"; 447 extra_headers += kServiceTokenAuthHeader + gaia_token_ + "\n";
398 if (!dm_token_.empty()) 448 if (!dm_token_.empty())
399 extra_headers += kDMTokenAuthHeader + dm_token_ + "\n"; 449 extra_headers += kDMTokenAuthHeader + dm_token_ + "\n";
400 fetcher->SetExtraRequestHeaders(extra_headers); 450 fetcher->SetExtraRequestHeaders(extra_headers);
401 } 451 }
402 452
453 bool DeviceManagementRequestJobImpl::ShouldRetry(
454 const net::URLFetcher* fetcher) {
455 if (FailedWithProxy(fetcher) && !bypass_proxy_) {
456 // Retry the job if it failed due to a broken proxy, by bypassing the
457 // proxy on the next try.
458 bypass_proxy_ = true;
459 return true;
460 }
461
462 // Early device policy fetches on ChromeOS and Auto-Enrollment checks are
463 // often interrupted during ChromeOS startup when network change notifications
464 // are sent. Allowing the fetcher to retry once after that is enough to
465 // recover; allow it to retry up to 3 times just in case.
466 if (fetcher->GetStatus().error() == net::ERR_NETWORK_CHANGED &&
467 retries_count_ < kMaxNetworkChangedRetries) {
468 ++retries_count_;
469 return true;
470 }
471
472 // The request didn't fail, or the limit of retry attempts has been reached;
473 // forward the result to the job owner.
474 return false;
475 }
476
477 void DeviceManagementRequestJobImpl::PrepareRetry() {
478 if (!retry_callback_.is_null())
479 retry_callback_.Run(this);
480 }
481
403 void DeviceManagementRequestJobImpl::ReportError(DeviceManagementStatus code) { 482 void DeviceManagementRequestJobImpl::ReportError(DeviceManagementStatus code) {
404 em::DeviceManagementResponse dummy_response; 483 em::DeviceManagementResponse dummy_response;
405 callback_.Run(code, dummy_response); 484 callback_.Run(code, dummy_response);
406 } 485 }
407 486
408 DeviceManagementRequestJob::~DeviceManagementRequestJob() {} 487 DeviceManagementRequestJob::~DeviceManagementRequestJob() {}
409 488
410 void DeviceManagementRequestJob::SetGaiaToken(const std::string& gaia_token) { 489 void DeviceManagementRequestJob::SetGaiaToken(const std::string& gaia_token) {
411 gaia_token_ = gaia_token; 490 gaia_token_ = gaia_token;
412 } 491 }
(...skipping 21 matching lines...) Expand all
434 } 513 }
435 514
436 DeviceManagementRequestJob::DeviceManagementRequestJob(JobType type) { 515 DeviceManagementRequestJob::DeviceManagementRequestJob(JobType type) {
437 AddParameter(dm_protocol::kParamRequest, JobTypeToRequestType(type)); 516 AddParameter(dm_protocol::kParamRequest, JobTypeToRequestType(type));
438 AddParameter(dm_protocol::kParamDeviceType, dm_protocol::kValueDeviceType); 517 AddParameter(dm_protocol::kParamDeviceType, dm_protocol::kValueDeviceType);
439 AddParameter(dm_protocol::kParamAppType, dm_protocol::kValueAppType); 518 AddParameter(dm_protocol::kParamAppType, dm_protocol::kValueAppType);
440 AddParameter(dm_protocol::kParamAgent, GetAgentString()); 519 AddParameter(dm_protocol::kParamAgent, GetAgentString());
441 AddParameter(dm_protocol::kParamPlatform, GetPlatformString()); 520 AddParameter(dm_protocol::kParamPlatform, GetPlatformString());
442 } 521 }
443 522
523 void DeviceManagementRequestJob::SetRetryCallback(
524 const RetryCallback& retry_callback) {
525 retry_callback_ = retry_callback;
526 }
527
444 void DeviceManagementRequestJob::Start(const Callback& callback) { 528 void DeviceManagementRequestJob::Start(const Callback& callback) {
445 callback_ = callback; 529 callback_ = callback;
446 Run(); 530 Run();
447 } 531 }
448 532
449 void DeviceManagementRequestJob::AddParameter(const std::string& name, 533 void DeviceManagementRequestJob::AddParameter(const std::string& name,
450 const std::string& value) { 534 const std::string& value) {
451 query_params_.push_back(std::make_pair(name, value)); 535 query_params_.push_back(std::make_pair(name, value));
452 } 536 }
453 537
(...skipping 20 matching lines...) Expand all
474 558
475 void DeviceManagementService::Initialize() { 559 void DeviceManagementService::Initialize() {
476 if (initialized_) 560 if (initialized_)
477 return; 561 return;
478 DCHECK(!request_context_getter_); 562 DCHECK(!request_context_getter_);
479 request_context_getter_ = new DeviceManagementRequestContextGetter( 563 request_context_getter_ = new DeviceManagementRequestContextGetter(
480 g_browser_process->system_request_context()); 564 g_browser_process->system_request_context());
481 initialized_ = true; 565 initialized_ = true;
482 566
483 while (!queued_jobs_.empty()) { 567 while (!queued_jobs_.empty()) {
484 StartJob(queued_jobs_.front(), false); 568 StartJob(queued_jobs_.front());
485 queued_jobs_.pop_front(); 569 queued_jobs_.pop_front();
486 } 570 }
487 } 571 }
488 572
489 void DeviceManagementService::Shutdown() { 573 void DeviceManagementService::Shutdown() {
490 for (JobFetcherMap::iterator job(pending_jobs_.begin()); 574 for (JobFetcherMap::iterator job(pending_jobs_.begin());
491 job != pending_jobs_.end(); 575 job != pending_jobs_.end();
492 ++job) { 576 ++job) {
493 delete job->first; 577 delete job->first;
494 queued_jobs_.push_back(job->second); 578 queued_jobs_.push_back(job->second);
495 } 579 }
496 pending_jobs_.clear(); 580 pending_jobs_.clear();
497 } 581 }
498 582
499 DeviceManagementService::DeviceManagementService( 583 DeviceManagementService::DeviceManagementService(
500 const std::string& server_url) 584 const std::string& server_url)
501 : server_url_(server_url), 585 : server_url_(server_url),
502 initialized_(false), 586 initialized_(false),
503 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) { 587 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
504 } 588 }
505 589
506 void DeviceManagementService::StartJob(DeviceManagementRequestJobImpl* job, 590 void DeviceManagementService::StartJob(DeviceManagementRequestJobImpl* job) {
507 bool bypass_proxy) {
508 net::URLFetcher* fetcher = net::URLFetcher::Create( 591 net::URLFetcher* fetcher = net::URLFetcher::Create(
509 0, job->GetURL(server_url_), net::URLFetcher::POST, this); 592 0, job->GetURL(server_url_), net::URLFetcher::POST, this);
510 fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
511 net::LOAD_DO_NOT_SAVE_COOKIES |
512 net::LOAD_DISABLE_CACHE |
513 (bypass_proxy ? net::LOAD_BYPASS_PROXY : 0));
514 fetcher->SetRequestContext(request_context_getter_.get()); 593 fetcher->SetRequestContext(request_context_getter_.get());
515 // Early device policy fetches on ChromeOS and Auto-Enrollment checks are
516 // often interrupted during ChromeOS startup when network change notifications
517 // are sent. Allowing the fetcher to retry once after that is enough to
518 // recover; allow it to retry up to 3 times just in case.
519 // http://crosbug.com/16114
520 fetcher->SetAutomaticallyRetryOnNetworkChanges(3);
521 job->ConfigureRequest(fetcher); 594 job->ConfigureRequest(fetcher);
522 pending_jobs_[fetcher] = job; 595 pending_jobs_[fetcher] = job;
523 fetcher->Start(); 596 fetcher->Start();
524 } 597 }
525 598
526 void DeviceManagementService::OnURLFetchComplete( 599 void DeviceManagementService::OnURLFetchComplete(
527 const net::URLFetcher* source) { 600 const net::URLFetcher* source) {
528 JobFetcherMap::iterator entry(pending_jobs_.find(source)); 601 JobFetcherMap::iterator entry(pending_jobs_.find(source));
529 if (entry == pending_jobs_.end()) { 602 if (entry == pending_jobs_.end()) {
530 NOTREACHED() << "Callback from foreign URL fetcher"; 603 NOTREACHED() << "Callback from foreign URL fetcher";
531 return; 604 return;
532 } 605 }
533 606
534 DeviceManagementRequestJobImpl* job = entry->second; 607 DeviceManagementRequestJobImpl* job = entry->second;
535 pending_jobs_.erase(entry); 608 pending_jobs_.erase(entry);
536 609
537 // Retry the job if it failed due to a broken proxy, by bypassing the 610 if (job->ShouldRetry(source)) {
538 // proxy on the next try. Don't retry if this URLFetcher already bypassed 611 VLOG(1) << "Retrying dmserver request.";
539 // the proxy. 612 job->PrepareRetry();
540 bool retry = false; 613 StartJob(job);
541 if ((source->GetLoadFlags() & net::LOAD_BYPASS_PROXY) == 0) {
542 if (!source->GetStatus().is_success() &&
543 IsProxyError(source->GetStatus())) {
544 LOG(WARNING) << "Proxy failed while contacting dmserver.";
545 retry = true;
546 } else if (source->GetStatus().is_success() &&
547 source->GetResponseCode() == kSuccess &&
548 source->WasFetchedViaProxy() &&
549 !IsProtobufMimeType(source)) {
550 // The proxy server can be misconfigured but pointing to an existing
551 // server that replies to requests. Try to recover if a successful
552 // request that went through a proxy returns an unexpected mime type.
553 LOG(WARNING) << "Got bad mime-type in response from dmserver that was "
554 << "fetched via a proxy.";
555 retry = true;
556 }
557 }
558
559 if (retry) {
560 LOG(WARNING) << "Retrying dmserver request without using a proxy.";
561 StartJob(job, true);
562 } else { 614 } else {
563 std::string data; 615 std::string data;
564 source->GetResponseAsString(&data); 616 source->GetResponseAsString(&data);
565 job->HandleResponse(source->GetStatus(), source->GetResponseCode(), 617 job->HandleResponse(source->GetStatus(), source->GetResponseCode(),
566 source->GetCookies(), data); 618 source->GetCookies(), data);
567 } 619 }
568 delete source; 620 delete source;
569 } 621 }
570 622
571 void DeviceManagementService::AddJob(DeviceManagementRequestJobImpl* job) { 623 void DeviceManagementService::AddJob(DeviceManagementRequestJobImpl* job) {
572 if (initialized_) 624 if (initialized_)
573 StartJob(job, false); 625 StartJob(job);
574 else 626 else
575 queued_jobs_.push_back(job); 627 queued_jobs_.push_back(job);
576 } 628 }
577 629
578 void DeviceManagementService::RemoveJob(DeviceManagementRequestJobImpl* job) { 630 void DeviceManagementService::RemoveJob(DeviceManagementRequestJobImpl* job) {
579 for (JobFetcherMap::iterator entry(pending_jobs_.begin()); 631 for (JobFetcherMap::iterator entry(pending_jobs_.begin());
580 entry != pending_jobs_.end(); 632 entry != pending_jobs_.end();
581 ++entry) { 633 ++entry) {
582 if (entry->second == job) { 634 if (entry->second == job) {
583 delete entry->first; 635 delete entry->first;
584 pending_jobs_.erase(entry); 636 pending_jobs_.erase(entry);
585 return; 637 return;
586 } 638 }
587 } 639 }
588 640
589 const JobQueue::iterator elem = 641 const JobQueue::iterator elem =
590 std::find(queued_jobs_.begin(), queued_jobs_.end(), job); 642 std::find(queued_jobs_.begin(), queued_jobs_.end(), job);
591 if (elem != queued_jobs_.end()) 643 if (elem != queued_jobs_.end())
592 queued_jobs_.erase(elem); 644 queued_jobs_.erase(elem);
593 } 645 }
594 646
595 } // namespace policy 647 } // namespace policy
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698