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

Unified Diff: chrome/browser/policy/device_management_service.cc

Issue 8952027: base::Callback-ify policy::DeviceManagementService. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: address comments. Created 8 years, 12 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 side-by-side diff with in-line comments
Download patch
Index: chrome/browser/policy/device_management_service.cc
diff --git a/chrome/browser/policy/device_management_service.cc b/chrome/browser/policy/device_management_service.cc
index 73cbbf986cbaa51ffc900c8aebeb867d184f2d67..94607eb83257be1aae472c2ebce36d6a9b365c45 100644
--- a/chrome/browser/policy/device_management_service.cc
+++ b/chrome/browser/policy/device_management_service.cc
@@ -4,17 +4,25 @@
#include "chrome/browser/policy/device_management_service.h"
+#include <utility>
+
#include "base/bind.h"
+#include "base/compiler_specific.h"
#include "base/message_loop.h"
#include "base/message_loop_proxy.h"
+#include "base/stringprintf.h"
+#include "base/sys_info.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/net/chrome_net_log.h"
#include "chrome/browser/policy/device_management_backend.h"
#include "chrome/browser/policy/device_management_backend_impl.h"
+#include "chrome/common/chrome_version_info.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/common/content_client.h"
#include "content/public/common/url_fetcher.h"
+#include "googleurl/src/gurl.h"
#include "net/base/cookie_monster.h"
+#include "net/base/escape.h"
#include "net/base/host_resolver.h"
#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
@@ -26,6 +34,10 @@
#include "net/url_request/url_request_context_getter.h"
#include "net/url_request/url_request_status.h"
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/system/statistics_provider.h"
+#endif
+
using content::BrowserThread;
namespace em = enterprise_management;
@@ -34,6 +46,40 @@ namespace policy {
namespace {
+const char kValueAgent[] = "%s %s(%s)";
+const char kValuePlatform[] = "%s|%s|%s";
+
+const char kPostContentType[] = "application/protobuf";
+
+const char kServiceTokenAuthHeader[] = "Authorization: GoogleLogin auth=";
+const char kDMTokenAuthHeader[] = "Authorization: GoogleDMToken token=";
+
+// HTTP Error Codes of the DM Server with their concrete meanings in the context
+// of the DM Server communication.
+const int kSuccess = 200;
+const int kInvalidArgument = 400;
+const int kInvalidAuthCookieOrDMToken = 401;
+const int kDeviceManagementNotAllowed = 403;
+const int kInvalidURL = 404; // This error is not coming from the GFE.
+const int kInvalidSerialNumber = 405;
+const int kDeviceIdConflict = 409;
+const int kDeviceNotFound = 410;
+const int kPendingApproval = 412;
+const int kInternalServerError = 500;
+const int kServiceUnavailable = 503;
+const int kPolicyNotFound = 902; // This error is not sent as HTTP status code.
+
+// TODO(pastarmovj): Legacy error codes are here for compatibility only. They
+// should be removed once the DM Server has been updated.
+const int kPendingApprovalLegacy = 491;
+const int kDeviceNotFoundLegacy = 901;
+
+#if defined(OS_CHROMEOS)
+// Machine info keys.
+const char kMachineInfoHWClass[] = "hardware_class";
+const char kMachineInfoBoard[] = "CHROMEOS_RELEASE_BOARD";
+#endif
+
bool IsProxyError(const net::URLRequestStatus status) {
switch (status.error()) {
case net::ERR_PROXY_CONNECTION_FAILED:
@@ -54,6 +100,88 @@ bool IsProtobufMimeType(const content::URLFetcher* source) {
"content-type", "application/x-protobuffer");
}
+const char* UserAffiliationToString(UserAffiliation affiliation) {
+ switch (affiliation) {
+ case USER_AFFILIATION_MANAGED:
+ return dm_protocol::kValueUserAffiliationManaged;
+ case USER_AFFILIATION_NONE:
+ return dm_protocol::kValueUserAffiliationNone;
+ }
+ NOTREACHED() << "Invalid user affiliation " << affiliation;
+ return dm_protocol::kValueUserAffiliationNone;
+}
+
+const char* JobTypeToRequestType(DeviceManagementRequestJob::JobType type) {
+ switch (type) {
+ case DeviceManagementRequestJob::TYPE_AUTO_ENROLLMENT:
+ return dm_protocol::kValueRequestAutoEnrollment;
+ case DeviceManagementRequestJob::TYPE_REGISTRATION:
+ return dm_protocol::kValueRequestRegister;
+ case DeviceManagementRequestJob::TYPE_POLICY_FETCH:
+ return dm_protocol::kValueRequestPolicy;
+ case DeviceManagementRequestJob::TYPE_UNREGISTRATION:
+ return dm_protocol::kValueRequestUnregister;
+ }
+ NOTREACHED() << "Invalid job type " << type;
+ return "";
+}
+
+const std::string& GetAgentString() {
+ CR_DEFINE_STATIC_LOCAL(std::string, agent, ());
+ if (!agent.empty())
+ return agent;
+
+ chrome::VersionInfo version_info;
+ agent = base::StringPrintf(kValueAgent,
+ version_info.Name().c_str(),
+ version_info.Version().c_str(),
+ version_info.LastChange().c_str());
+ return agent;
+}
+
+const std::string& GetPlatformString() {
+ CR_DEFINE_STATIC_LOCAL(std::string, platform, ());
+ if (!platform.empty())
+ return platform;
+
+ std::string os_name(base::SysInfo::OperatingSystemName());
+ std::string os_hardware(base::SysInfo::CPUArchitecture());
+
+#if defined(OS_CHROMEOS)
+ chromeos::system::StatisticsProvider* provider =
+ chromeos::system::StatisticsProvider::GetInstance();
+
+ std::string hwclass;
+ std::string board;
+ if (!provider->GetMachineStatistic(kMachineInfoHWClass, &hwclass) ||
+ !provider->GetMachineStatistic(kMachineInfoBoard, &board)) {
+ LOG(ERROR) << "Failed to get machine information";
+ }
+ os_name += ",CrOS," + board;
+ os_hardware += "," + hwclass;
+#endif
+
+ std::string os_version("-");
+#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS)
+ int32 os_major_version = 0;
+ int32 os_minor_version = 0;
+ int32 os_bugfix_version = 0;
+ base::SysInfo::OperatingSystemVersionNumbers(&os_major_version,
+ &os_minor_version,
+ &os_bugfix_version);
+ os_version = base::StringPrintf("%d.%d.%d",
+ os_major_version,
+ os_minor_version,
+ os_bugfix_version);
+#endif
+
+ platform = base::StringPrintf(kValuePlatform,
+ os_name.c_str(),
+ os_hardware.c_str(),
+ os_version.c_str());
+ return platform;
+}
+
// Custom request context implementation that allows to override the user agent,
// amongst others. Wraps a baseline request context from which we reuse the
// networking components.
@@ -136,9 +264,199 @@ DeviceManagementRequestContextGetter::GetIOMessageLoopProxy() const {
} // namespace
+// Request job implementation used with DeviceManagementService.
+class DeviceManagementRequestJobImpl : public DeviceManagementRequestJob {
+ public:
+ DeviceManagementRequestJobImpl(JobType type,
+ DeviceManagementService* service);
+ virtual ~DeviceManagementRequestJobImpl();
+
+ // Handles the URL request response.
+ void HandleResponse(const net::URLRequestStatus& status,
+ int response_code,
+ const net::ResponseCookies& cookies,
+ const std::string& data);
+
+ // Gets the URL to contact.
+ GURL GetURL(const std::string& server_url);
+
+ // Configures the fetcher, setting up payload and headers.
+ void ConfigureRequest(content::URLFetcher* fetcher);
+
+ protected:
+ // DeviceManagementRequestJob:
+ virtual void Run() OVERRIDE;
+
+ private:
+ // Invokes the callback with the given error code.
+ void ReportError(DeviceManagementStatus code);
+
+ // Pointer to the service this job is associated with.
+ DeviceManagementService* service_;
+
+ DISALLOW_COPY_AND_ASSIGN(DeviceManagementRequestJobImpl);
+};
+
+DeviceManagementRequestJobImpl::DeviceManagementRequestJobImpl(
+ JobType type,
+ DeviceManagementService* service)
+ : DeviceManagementRequestJob(type),
+ service_(service) {}
+
+DeviceManagementRequestJobImpl::~DeviceManagementRequestJobImpl() {
+ service_->RemoveJob(this);
+}
+
+void DeviceManagementRequestJobImpl::Run() {
+ service_->AddJob(this);
+}
+
+void DeviceManagementRequestJobImpl::HandleResponse(
+ const net::URLRequestStatus& status,
+ int response_code,
+ const net::ResponseCookies& cookies,
+ const std::string& data) {
+ if (status.status() != net::URLRequestStatus::SUCCESS) {
+ ReportError(DM_STATUS_REQUEST_FAILED);
+ return;
+ }
+
+ switch (response_code) {
+ case kSuccess: {
+ em::DeviceManagementResponse response;
+ if (!response.ParseFromString(data)) {
+ ReportError(DM_STATUS_RESPONSE_DECODING_ERROR);
+ return;
+ }
+ callback_.Run(DM_STATUS_SUCCESS, response);
+ return;
+ }
+ case kInvalidArgument:
+ ReportError(DM_STATUS_REQUEST_INVALID);
+ return;
+ case kInvalidAuthCookieOrDMToken:
+ ReportError(DM_STATUS_SERVICE_MANAGEMENT_TOKEN_INVALID);
+ return;
+ case kDeviceManagementNotAllowed:
+ ReportError(DM_STATUS_SERVICE_MANAGEMENT_NOT_SUPPORTED);
+ return;
+ case kPendingApprovalLegacy:
+ case kPendingApproval:
+ ReportError(DM_STATUS_SERVICE_ACTIVATION_PENDING);
+ return;
+ case kInvalidURL:
+ case kInternalServerError:
+ case kServiceUnavailable:
+ ReportError(DM_STATUS_TEMPORARY_UNAVAILABLE);
+ return;
+ case kDeviceNotFoundLegacy:
+ case kDeviceNotFound:
+ ReportError(DM_STATUS_SERVICE_DEVICE_NOT_FOUND);
+ return;
+ case kPolicyNotFound:
+ ReportError(DM_STATUS_SERVICE_POLICY_NOT_FOUND);
+ return;
+ case kInvalidSerialNumber:
+ ReportError(DM_STATUS_SERVICE_INVALID_SERIAL_NUMBER);
+ return;
+ case kDeviceIdConflict:
+ ReportError(DM_STATUS_SERVICE_DEVICE_ID_CONFLICT);
+ return;
+ default:
+ VLOG(1) << "Unexpected HTTP status in response from DMServer : "
+ << response_code << ".";
+ // Handle all unknown 5xx HTTP error codes as temporary and any other
+ // unknown error as one that needs more time to recover.
+ if (response_code >= 500 && response_code <= 599)
+ ReportError(DM_STATUS_TEMPORARY_UNAVAILABLE);
+ else
+ ReportError(DM_STATUS_HTTP_STATUS_ERROR);
+ return;
+ }
+}
+
+GURL DeviceManagementRequestJobImpl::GetURL(
+ const std::string& server_url) {
+ std::string result(server_url);
+ result += '?';
+ for (ParameterMap::const_iterator entry(query_params_.begin());
+ entry != query_params_.end();
+ ++entry) {
+ if (entry != query_params_.begin())
+ result += '&';
+ result += net::EscapeQueryParamValue(entry->first, true);
+ result += '=';
+ result += net::EscapeQueryParamValue(entry->second, true);
+ }
+ return GURL(result);
+}
+
+void DeviceManagementRequestJobImpl::ConfigureRequest(
+ content::URLFetcher* fetcher) {
+ std::string payload;
+ CHECK(request_.SerializeToString(&payload));
+ fetcher->SetUploadData(kPostContentType, payload);
+ std::string extra_headers;
+ if (!gaia_token_.empty())
+ extra_headers += kServiceTokenAuthHeader + gaia_token_ + "\n";
+ if (!dm_token_.empty())
+ extra_headers += kDMTokenAuthHeader + dm_token_ + "\n";
+ fetcher->SetExtraRequestHeaders(extra_headers);
+}
+
+void DeviceManagementRequestJobImpl::ReportError(DeviceManagementStatus code) {
+ em::DeviceManagementResponse dummy_response;
+ callback_.Run(code, dummy_response);
+}
+
+DeviceManagementRequestJob::~DeviceManagementRequestJob() {}
+
+void DeviceManagementRequestJob::SetGaiaToken(const std::string& gaia_token) {
+ gaia_token_ = gaia_token;
+}
+
+void DeviceManagementRequestJob::SetOAuthToken(const std::string& oauth_token) {
+ AddParameter(dm_protocol::kParamOAuthToken, oauth_token);
+}
+
+void DeviceManagementRequestJob::SetUserAffiliation(
+ UserAffiliation user_affiliation) {
+ AddParameter(dm_protocol::kParamUserAffiliation,
+ UserAffiliationToString(user_affiliation));
+}
+
+void DeviceManagementRequestJob::SetDMToken(const std::string& dm_token) {
+ dm_token_ = dm_token;
+}
+
+void DeviceManagementRequestJob::SetClientID(const std::string& client_id) {
+ AddParameter(dm_protocol::kParamDeviceID, client_id);
+}
+
+em::DeviceManagementRequest* DeviceManagementRequestJob::GetRequest() {
+ return &request_;
+}
+
+DeviceManagementRequestJob::DeviceManagementRequestJob(JobType type) {
+ AddParameter(dm_protocol::kParamRequest, JobTypeToRequestType(type));
+ AddParameter(dm_protocol::kParamDeviceType, dm_protocol::kValueDeviceType);
+ AddParameter(dm_protocol::kParamAppType, dm_protocol::kValueAppType);
+ AddParameter(dm_protocol::kParamAgent, GetAgentString());
+ AddParameter(dm_protocol::kParamPlatform, GetPlatformString());
+}
+
+void DeviceManagementRequestJob::Start(const Callback& callback) {
+ callback_ = callback;
+ Run();
+}
+
+void DeviceManagementRequestJob::AddParameter(const std::string& name,
+ const std::string& value) {
+ query_params_.push_back(std::make_pair(name, value));
+}
+
DeviceManagementService::~DeviceManagementService() {
- // All running jobs should have been cancelled by now. If not, there are
- // backend objects still around, which is an error.
+ // All running jobs should have been cancelled by now.
DCHECK(pending_jobs_.empty());
DCHECK(queued_jobs_.empty());
}
@@ -147,6 +465,11 @@ DeviceManagementBackend* DeviceManagementService::CreateBackend() {
return new DeviceManagementBackendImpl(this);
}
+DeviceManagementRequestJob* DeviceManagementService::CreateJob(
+ DeviceManagementRequestJob::JobType type) {
+ return new DeviceManagementRequestJobImpl(type, this);
+}
+
void DeviceManagementService::ScheduleInitialization(int64 delay_milliseconds) {
if (initialized_)
return;
@@ -188,31 +511,7 @@ DeviceManagementService::DeviceManagementService(
ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
}
-void DeviceManagementService::AddJob(DeviceManagementJob* job) {
- if (initialized_)
- StartJob(job, false);
- else
- queued_jobs_.push_back(job);
-}
-
-void DeviceManagementService::RemoveJob(DeviceManagementJob* job) {
- for (JobFetcherMap::iterator entry(pending_jobs_.begin());
- entry != pending_jobs_.end();
- ++entry) {
- if (entry->second == job) {
- delete entry->first;
- pending_jobs_.erase(entry);
- return;
- }
- }
-
- const JobQueue::iterator elem =
- std::find(queued_jobs_.begin(), queued_jobs_.end(), job);
- if (elem != queued_jobs_.end())
- queued_jobs_.erase(elem);
-}
-
-void DeviceManagementService::StartJob(DeviceManagementJob* job,
+void DeviceManagementService::StartJob(DeviceManagementRequestJobImpl* job,
bool bypass_proxy) {
content::URLFetcher* fetcher = content::URLFetcher::Create(
0, job->GetURL(server_url_), content::URLFetcher::POST, this);
@@ -229,44 +528,69 @@ void DeviceManagementService::StartJob(DeviceManagementJob* job,
void DeviceManagementService::OnURLFetchComplete(
const content::URLFetcher* source) {
JobFetcherMap::iterator entry(pending_jobs_.find(source));
- if (entry != pending_jobs_.end()) {
- DeviceManagementJob* job = entry->second;
- pending_jobs_.erase(entry);
-
- // Retry the job if it failed due to a broken proxy, by bypassing the
- // proxy on the next try. Don't retry if this URLFetcher already bypassed
- // the proxy.
- bool retry = false;
- if ((source->GetLoadFlags() & net::LOAD_BYPASS_PROXY) == 0) {
- if (!source->GetStatus().is_success() &&
- IsProxyError(source->GetStatus())) {
- LOG(WARNING) << "Proxy failed while contacting dmserver.";
- retry = true;
- } else if (source->GetStatus().is_success() &&
- source->WasFetchedViaProxy() &&
- !IsProtobufMimeType(source)) {
- // The proxy server can be misconfigured but pointing to an existing
- // server that replies to requests. Try to recover if a successful
- // request that went through a proxy returns an unexpected mime type.
- LOG(WARNING) << "Got bad mime-type in response from dmserver that was "
- << "fetched via a proxy.";
- retry = true;
- }
- }
+ if (entry == pending_jobs_.end()) {
+ NOTREACHED() << "Callback from foreign URL fetcher";
+ return;
+ }
- if (retry) {
- LOG(WARNING) << "Retrying dmserver request without using a proxy.";
- StartJob(job, true);
- } else {
- std::string data;
- source->GetResponseAsString(&data);
- job->HandleResponse(source->GetStatus(), source->GetResponseCode(),
- source->GetCookies(), data);
+ DeviceManagementRequestJobImpl* job = entry->second;
+ pending_jobs_.erase(entry);
+
+ // Retry the job if it failed due to a broken proxy, by bypassing the
+ // proxy on the next try. Don't retry if this URLFetcher already bypassed
+ // the proxy.
+ bool retry = false;
+ if ((source->GetLoadFlags() & net::LOAD_BYPASS_PROXY) == 0) {
+ if (!source->GetStatus().is_success() &&
+ IsProxyError(source->GetStatus())) {
+ LOG(WARNING) << "Proxy failed while contacting dmserver.";
+ retry = true;
+ } else if (source->GetStatus().is_success() &&
+ source->WasFetchedViaProxy() &&
+ !IsProtobufMimeType(source)) {
+ // The proxy server can be misconfigured but pointing to an existing
+ // server that replies to requests. Try to recover if a successful
+ // request that went through a proxy returns an unexpected mime type.
+ LOG(WARNING) << "Got bad mime-type in response from dmserver that was "
+ << "fetched via a proxy.";
+ retry = true;
}
+ }
+
+ if (retry) {
+ LOG(WARNING) << "Retrying dmserver request without using a proxy.";
+ StartJob(job, true);
} else {
- NOTREACHED() << "Callback from foreign URL fetcher";
+ std::string data;
+ source->GetResponseAsString(&data);
+ job->HandleResponse(source->GetStatus(), source->GetResponseCode(),
+ source->GetCookies(), data);
}
delete source;
}
+void DeviceManagementService::AddJob(DeviceManagementRequestJobImpl* job) {
+ if (initialized_)
+ StartJob(job, false);
+ else
+ queued_jobs_.push_back(job);
+}
+
+void DeviceManagementService::RemoveJob(DeviceManagementRequestJobImpl* job) {
+ for (JobFetcherMap::iterator entry(pending_jobs_.begin());
+ entry != pending_jobs_.end();
+ ++entry) {
+ if (entry->second == job) {
+ delete entry->first;
+ pending_jobs_.erase(entry);
+ return;
+ }
+ }
+
+ const JobQueue::iterator elem =
+ std::find(queued_jobs_.begin(), queued_jobs_.end(), job);
+ if (elem != queued_jobs_.end())
+ queued_jobs_.erase(elem);
+}
+
} // namespace policy
« no previous file with comments | « chrome/browser/policy/device_management_service.h ('k') | chrome/browser/policy/device_management_service_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698