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

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

Issue 109743002: Move policy code into components/policy. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: moar fixes Created 7 years 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) 2012 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 "chrome/browser/policy/cloud/device_management_service.h"
6
7 #include <utility>
8
9 #include "base/bind.h"
10 #include "base/compiler_specific.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/message_loop/message_loop_proxy.h"
13 #include "net/base/escape.h"
14 #include "net/base/load_flags.h"
15 #include "net/base/net_errors.h"
16 #include "net/http/http_response_headers.h"
17 #include "net/url_request/url_fetcher.h"
18 #include "net/url_request/url_request_status.h"
19 #include "url/gurl.h"
20
21 namespace em = enterprise_management;
22
23 namespace policy {
24
25 namespace {
26
27 const char kPostContentType[] = "application/protobuf";
28
29 const char kServiceTokenAuthHeader[] = "Authorization: GoogleLogin auth=";
30 const char kDMTokenAuthHeader[] = "Authorization: GoogleDMToken token=";
31
32 // Number of times to retry on ERR_NETWORK_CHANGED errors.
33 const int kMaxNetworkChangedRetries = 3;
34
35 // HTTP Error Codes of the DM Server with their concrete meanings in the context
36 // of the DM Server communication.
37 const int kSuccess = 200;
38 const int kInvalidArgument = 400;
39 const int kInvalidAuthCookieOrDMToken = 401;
40 const int kMissingLicenses = 402;
41 const int kDeviceManagementNotAllowed = 403;
42 const int kInvalidURL = 404; // This error is not coming from the GFE.
43 const int kInvalidSerialNumber = 405;
44 const int kDeviceIdConflict = 409;
45 const int kDeviceNotFound = 410;
46 const int kPendingApproval = 412;
47 const int kInternalServerError = 500;
48 const int kServiceUnavailable = 503;
49 const int kPolicyNotFound = 902; // This error is not sent as HTTP status code.
50
51 bool IsProxyError(const net::URLRequestStatus status) {
52 switch (status.error()) {
53 case net::ERR_PROXY_CONNECTION_FAILED:
54 case net::ERR_TUNNEL_CONNECTION_FAILED:
55 case net::ERR_PROXY_AUTH_UNSUPPORTED:
56 case net::ERR_HTTPS_PROXY_TUNNEL_RESPONSE:
57 case net::ERR_MANDATORY_PROXY_CONFIGURATION_FAILED:
58 case net::ERR_PROXY_CERTIFICATE_INVALID:
59 case net::ERR_SOCKS_CONNECTION_FAILED:
60 case net::ERR_SOCKS_CONNECTION_HOST_UNREACHABLE:
61 return true;
62 }
63 return false;
64 }
65
66 bool IsProtobufMimeType(const net::URLFetcher* fetcher) {
67 return fetcher->GetResponseHeaders()->HasHeaderValue(
68 "content-type", "application/x-protobuffer");
69 }
70
71 bool FailedWithProxy(const net::URLFetcher* fetcher) {
72 if ((fetcher->GetLoadFlags() & net::LOAD_BYPASS_PROXY) != 0) {
73 // The request didn't use a proxy.
74 return false;
75 }
76
77 if (!fetcher->GetStatus().is_success() &&
78 IsProxyError(fetcher->GetStatus())) {
79 LOG(WARNING) << "Proxy failed while contacting dmserver.";
80 return true;
81 }
82
83 if (fetcher->GetStatus().is_success() &&
84 fetcher->GetResponseCode() == kSuccess &&
85 fetcher->WasFetchedViaProxy() &&
86 !IsProtobufMimeType(fetcher)) {
87 // The proxy server can be misconfigured but pointing to an existing
88 // server that replies to requests. Try to recover if a successful
89 // request that went through a proxy returns an unexpected mime type.
90 LOG(WARNING) << "Got bad mime-type in response from dmserver that was "
91 << "fetched via a proxy.";
92 return true;
93 }
94
95 return false;
96 }
97
98 const char* UserAffiliationToString(UserAffiliation affiliation) {
99 switch (affiliation) {
100 case USER_AFFILIATION_MANAGED:
101 return dm_protocol::kValueUserAffiliationManaged;
102 case USER_AFFILIATION_NONE:
103 return dm_protocol::kValueUserAffiliationNone;
104 }
105 NOTREACHED() << "Invalid user affiliation " << affiliation;
106 return dm_protocol::kValueUserAffiliationNone;
107 }
108
109 const char* JobTypeToRequestType(DeviceManagementRequestJob::JobType type) {
110 switch (type) {
111 case DeviceManagementRequestJob::TYPE_AUTO_ENROLLMENT:
112 return dm_protocol::kValueRequestAutoEnrollment;
113 case DeviceManagementRequestJob::TYPE_REGISTRATION:
114 return dm_protocol::kValueRequestRegister;
115 case DeviceManagementRequestJob::TYPE_POLICY_FETCH:
116 return dm_protocol::kValueRequestPolicy;
117 case DeviceManagementRequestJob::TYPE_API_AUTH_CODE_FETCH:
118 return dm_protocol::kValueRequestApiAuthorization;
119 case DeviceManagementRequestJob::TYPE_UNREGISTRATION:
120 return dm_protocol::kValueRequestUnregister;
121 case DeviceManagementRequestJob::TYPE_UPLOAD_CERTIFICATE:
122 return dm_protocol::kValueRequestUploadCertificate;
123 }
124 NOTREACHED() << "Invalid job type " << type;
125 return "";
126 }
127
128 } // namespace
129
130 // Request job implementation used with DeviceManagementService.
131 class DeviceManagementRequestJobImpl : public DeviceManagementRequestJob {
132 public:
133 DeviceManagementRequestJobImpl(
134 JobType type,
135 const std::string& agent_parameter,
136 const std::string& platform_parameter,
137 DeviceManagementService* service,
138 net::URLRequestContextGetter* request_context);
139 virtual ~DeviceManagementRequestJobImpl();
140
141 // Handles the URL request response.
142 void HandleResponse(const net::URLRequestStatus& status,
143 int response_code,
144 const net::ResponseCookies& cookies,
145 const std::string& data);
146
147 // Gets the URL to contact.
148 GURL GetURL(const std::string& server_url);
149
150 // Configures the fetcher, setting up payload and headers.
151 void ConfigureRequest(net::URLFetcher* fetcher);
152
153 // Returns true if this job should be retried. |fetcher| has just completed,
154 // and can be inspected to determine if the request failed and should be
155 // retried.
156 bool ShouldRetry(const net::URLFetcher* fetcher);
157
158 // Invoked right before retrying this job.
159 void PrepareRetry();
160
161 protected:
162 // DeviceManagementRequestJob:
163 virtual void Run() OVERRIDE;
164
165 private:
166 // Invokes the callback with the given error code.
167 void ReportError(DeviceManagementStatus code);
168
169 // Pointer to the service this job is associated with.
170 DeviceManagementService* service_;
171
172 // Whether the BYPASS_PROXY flag should be set by ConfigureRequest().
173 bool bypass_proxy_;
174
175 // Number of times that this job has been retried due to ERR_NETWORK_CHANGED.
176 int retries_count_;
177
178 // The request context to use for this job.
179 net::URLRequestContextGetter* request_context_;
180
181 DISALLOW_COPY_AND_ASSIGN(DeviceManagementRequestJobImpl);
182 };
183
184 DeviceManagementRequestJobImpl::DeviceManagementRequestJobImpl(
185 JobType type,
186 const std::string& agent_parameter,
187 const std::string& platform_parameter,
188 DeviceManagementService* service,
189 net::URLRequestContextGetter* request_context)
190 : DeviceManagementRequestJob(type, agent_parameter, platform_parameter),
191 service_(service),
192 bypass_proxy_(false),
193 retries_count_(0),
194 request_context_(request_context) {}
195
196 DeviceManagementRequestJobImpl::~DeviceManagementRequestJobImpl() {
197 service_->RemoveJob(this);
198 }
199
200 void DeviceManagementRequestJobImpl::Run() {
201 service_->AddJob(this);
202 }
203
204 void DeviceManagementRequestJobImpl::HandleResponse(
205 const net::URLRequestStatus& status,
206 int response_code,
207 const net::ResponseCookies& cookies,
208 const std::string& data) {
209 if (status.status() != net::URLRequestStatus::SUCCESS) {
210 LOG(WARNING) << "DMServer request failed, status: " << status.status()
211 << ", error: " << status.error();
212 em::DeviceManagementResponse dummy_response;
213 callback_.Run(DM_STATUS_REQUEST_FAILED, status.error(), dummy_response);
214 return;
215 }
216
217 if (response_code != kSuccess)
218 LOG(WARNING) << "DMServer sent an error response: " << response_code;
219
220 switch (response_code) {
221 case kSuccess: {
222 em::DeviceManagementResponse response;
223 if (!response.ParseFromString(data)) {
224 ReportError(DM_STATUS_RESPONSE_DECODING_ERROR);
225 return;
226 }
227 callback_.Run(DM_STATUS_SUCCESS, net::OK, response);
228 return;
229 }
230 case kInvalidArgument:
231 ReportError(DM_STATUS_REQUEST_INVALID);
232 return;
233 case kInvalidAuthCookieOrDMToken:
234 ReportError(DM_STATUS_SERVICE_MANAGEMENT_TOKEN_INVALID);
235 return;
236 case kMissingLicenses:
237 ReportError(DM_STATUS_SERVICE_MISSING_LICENSES);
238 return;
239 case kDeviceManagementNotAllowed:
240 ReportError(DM_STATUS_SERVICE_MANAGEMENT_NOT_SUPPORTED);
241 return;
242 case kPendingApproval:
243 ReportError(DM_STATUS_SERVICE_ACTIVATION_PENDING);
244 return;
245 case kInvalidURL:
246 case kInternalServerError:
247 case kServiceUnavailable:
248 ReportError(DM_STATUS_TEMPORARY_UNAVAILABLE);
249 return;
250 case kDeviceNotFound:
251 ReportError(DM_STATUS_SERVICE_DEVICE_NOT_FOUND);
252 return;
253 case kPolicyNotFound:
254 ReportError(DM_STATUS_SERVICE_POLICY_NOT_FOUND);
255 return;
256 case kInvalidSerialNumber:
257 ReportError(DM_STATUS_SERVICE_INVALID_SERIAL_NUMBER);
258 return;
259 case kDeviceIdConflict:
260 ReportError(DM_STATUS_SERVICE_DEVICE_ID_CONFLICT);
261 return;
262 default:
263 // Handle all unknown 5xx HTTP error codes as temporary and any other
264 // unknown error as one that needs more time to recover.
265 if (response_code >= 500 && response_code <= 599)
266 ReportError(DM_STATUS_TEMPORARY_UNAVAILABLE);
267 else
268 ReportError(DM_STATUS_HTTP_STATUS_ERROR);
269 return;
270 }
271 }
272
273 GURL DeviceManagementRequestJobImpl::GetURL(
274 const std::string& server_url) {
275 std::string result(server_url);
276 result += '?';
277 for (ParameterMap::const_iterator entry(query_params_.begin());
278 entry != query_params_.end();
279 ++entry) {
280 if (entry != query_params_.begin())
281 result += '&';
282 result += net::EscapeQueryParamValue(entry->first, true);
283 result += '=';
284 result += net::EscapeQueryParamValue(entry->second, true);
285 }
286 return GURL(result);
287 }
288
289 void DeviceManagementRequestJobImpl::ConfigureRequest(
290 net::URLFetcher* fetcher) {
291 fetcher->SetRequestContext(request_context_);
292 fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
293 net::LOAD_DO_NOT_SAVE_COOKIES |
294 net::LOAD_DISABLE_CACHE |
295 (bypass_proxy_ ? net::LOAD_BYPASS_PROXY : 0));
296 std::string payload;
297 CHECK(request_.SerializeToString(&payload));
298 fetcher->SetUploadData(kPostContentType, payload);
299 std::string extra_headers;
300 if (!gaia_token_.empty())
301 extra_headers += kServiceTokenAuthHeader + gaia_token_ + "\n";
302 if (!dm_token_.empty())
303 extra_headers += kDMTokenAuthHeader + dm_token_ + "\n";
304 fetcher->SetExtraRequestHeaders(extra_headers);
305 }
306
307 bool DeviceManagementRequestJobImpl::ShouldRetry(
308 const net::URLFetcher* fetcher) {
309 if (FailedWithProxy(fetcher) && !bypass_proxy_) {
310 // Retry the job if it failed due to a broken proxy, by bypassing the
311 // proxy on the next try.
312 bypass_proxy_ = true;
313 return true;
314 }
315
316 // Early device policy fetches on ChromeOS and Auto-Enrollment checks are
317 // often interrupted during ChromeOS startup when network change notifications
318 // are sent. Allowing the fetcher to retry once after that is enough to
319 // recover; allow it to retry up to 3 times just in case.
320 if (fetcher->GetStatus().error() == net::ERR_NETWORK_CHANGED &&
321 retries_count_ < kMaxNetworkChangedRetries) {
322 ++retries_count_;
323 return true;
324 }
325
326 // The request didn't fail, or the limit of retry attempts has been reached;
327 // forward the result to the job owner.
328 return false;
329 }
330
331 void DeviceManagementRequestJobImpl::PrepareRetry() {
332 if (!retry_callback_.is_null())
333 retry_callback_.Run(this);
334 }
335
336 void DeviceManagementRequestJobImpl::ReportError(DeviceManagementStatus code) {
337 em::DeviceManagementResponse dummy_response;
338 callback_.Run(code, net::OK, dummy_response);
339 }
340
341 DeviceManagementRequestJob::~DeviceManagementRequestJob() {}
342
343 void DeviceManagementRequestJob::SetGaiaToken(const std::string& gaia_token) {
344 gaia_token_ = gaia_token;
345 }
346
347 void DeviceManagementRequestJob::SetOAuthToken(const std::string& oauth_token) {
348 AddParameter(dm_protocol::kParamOAuthToken, oauth_token);
349 }
350
351 void DeviceManagementRequestJob::SetUserAffiliation(
352 UserAffiliation user_affiliation) {
353 AddParameter(dm_protocol::kParamUserAffiliation,
354 UserAffiliationToString(user_affiliation));
355 }
356
357 void DeviceManagementRequestJob::SetDMToken(const std::string& dm_token) {
358 dm_token_ = dm_token;
359 }
360
361 void DeviceManagementRequestJob::SetClientID(const std::string& client_id) {
362 AddParameter(dm_protocol::kParamDeviceID, client_id);
363 }
364
365 em::DeviceManagementRequest* DeviceManagementRequestJob::GetRequest() {
366 return &request_;
367 }
368
369 DeviceManagementRequestJob::DeviceManagementRequestJob(
370 JobType type,
371 const std::string& agent_parameter,
372 const std::string& platform_parameter) {
373 AddParameter(dm_protocol::kParamRequest, JobTypeToRequestType(type));
374 AddParameter(dm_protocol::kParamDeviceType, dm_protocol::kValueDeviceType);
375 AddParameter(dm_protocol::kParamAppType, dm_protocol::kValueAppType);
376 AddParameter(dm_protocol::kParamAgent, agent_parameter);
377 AddParameter(dm_protocol::kParamPlatform, platform_parameter);
378 }
379
380 void DeviceManagementRequestJob::SetRetryCallback(
381 const RetryCallback& retry_callback) {
382 retry_callback_ = retry_callback;
383 }
384
385 void DeviceManagementRequestJob::Start(const Callback& callback) {
386 callback_ = callback;
387 Run();
388 }
389
390 void DeviceManagementRequestJob::AddParameter(const std::string& name,
391 const std::string& value) {
392 query_params_.push_back(std::make_pair(name, value));
393 }
394
395 // A random value that other fetchers won't likely use.
396 const int DeviceManagementService::kURLFetcherID = 0xde71ce1d;
397
398 DeviceManagementService::~DeviceManagementService() {
399 // All running jobs should have been cancelled by now.
400 DCHECK(pending_jobs_.empty());
401 DCHECK(queued_jobs_.empty());
402 }
403
404 DeviceManagementRequestJob* DeviceManagementService::CreateJob(
405 DeviceManagementRequestJob::JobType type,
406 net::URLRequestContextGetter* request_context) {
407 return new DeviceManagementRequestJobImpl(
408 type,
409 configuration_->GetAgentParameter(),
410 configuration_->GetPlatformParameter(),
411 this,
412 request_context);
413 }
414
415 void DeviceManagementService::ScheduleInitialization(int64 delay_milliseconds) {
416 if (initialized_)
417 return;
418 base::MessageLoop::current()->PostDelayedTask(
419 FROM_HERE,
420 base::Bind(&DeviceManagementService::Initialize,
421 weak_ptr_factory_.GetWeakPtr()),
422 base::TimeDelta::FromMilliseconds(delay_milliseconds));
423 }
424
425 void DeviceManagementService::Initialize() {
426 if (initialized_)
427 return;
428 initialized_ = true;
429
430 while (!queued_jobs_.empty()) {
431 StartJob(queued_jobs_.front());
432 queued_jobs_.pop_front();
433 }
434 }
435
436 void DeviceManagementService::Shutdown() {
437 for (JobFetcherMap::iterator job(pending_jobs_.begin());
438 job != pending_jobs_.end();
439 ++job) {
440 delete job->first;
441 queued_jobs_.push_back(job->second);
442 }
443 pending_jobs_.clear();
444 }
445
446 DeviceManagementService::DeviceManagementService(
447 scoped_ptr<Configuration> configuration)
448 : configuration_(configuration.Pass()),
449 initialized_(false),
450 weak_ptr_factory_(this) {
451 DCHECK(configuration_);
452 }
453
454 void DeviceManagementService::StartJob(DeviceManagementRequestJobImpl* job) {
455 std::string server_url = GetServerURL();
456 net::URLFetcher* fetcher = net::URLFetcher::Create(
457 kURLFetcherID, job->GetURL(server_url), net::URLFetcher::POST, this);
458 job->ConfigureRequest(fetcher);
459 pending_jobs_[fetcher] = job;
460 fetcher->Start();
461 }
462
463 std::string DeviceManagementService::GetServerURL() {
464 return configuration_->GetServerUrl();
465 }
466
467 void DeviceManagementService::OnURLFetchComplete(
468 const net::URLFetcher* source) {
469 JobFetcherMap::iterator entry(pending_jobs_.find(source));
470 if (entry == pending_jobs_.end()) {
471 NOTREACHED() << "Callback from foreign URL fetcher";
472 return;
473 }
474
475 DeviceManagementRequestJobImpl* job = entry->second;
476 pending_jobs_.erase(entry);
477
478 if (job->ShouldRetry(source)) {
479 VLOG(1) << "Retrying dmserver request.";
480 job->PrepareRetry();
481 StartJob(job);
482 } else {
483 std::string data;
484 source->GetResponseAsString(&data);
485 job->HandleResponse(source->GetStatus(), source->GetResponseCode(),
486 source->GetCookies(), data);
487 }
488 delete source;
489 }
490
491 void DeviceManagementService::AddJob(DeviceManagementRequestJobImpl* job) {
492 if (initialized_)
493 StartJob(job);
494 else
495 queued_jobs_.push_back(job);
496 }
497
498 void DeviceManagementService::RemoveJob(DeviceManagementRequestJobImpl* job) {
499 for (JobFetcherMap::iterator entry(pending_jobs_.begin());
500 entry != pending_jobs_.end();
501 ++entry) {
502 if (entry->second == job) {
503 delete entry->first;
504 pending_jobs_.erase(entry);
505 return;
506 }
507 }
508
509 const JobQueue::iterator elem =
510 std::find(queued_jobs_.begin(), queued_jobs_.end(), job);
511 if (elem != queued_jobs_.end())
512 queued_jobs_.erase(elem);
513 }
514
515 } // namespace policy
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698