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.
5 #include "components/policy/core/common/cloud/cloud_policy_client.h"
9 #include "base/logging.h"
10 #include "base/stl_util.h"
11 #include "components/policy/core/common/cloud/device_management_service.h"
12 #include "google_apis/gaia/gaia_constants.h"
13 #include "google_apis/gaia/gaia_urls.h"
14 #include "net/url_request/url_request_context_getter.h"
16 namespace em
= enterprise_management
;
22 // Translates the DeviceRegisterResponse::DeviceMode |mode| to the enum used
23 // internally to represent different device modes.
24 DeviceMode
TranslateProtobufDeviceMode(
25 em::DeviceRegisterResponse::DeviceMode mode
) {
27 case em::DeviceRegisterResponse::ENTERPRISE
:
28 return DEVICE_MODE_ENTERPRISE
;
29 case em::DeviceRegisterResponse::RETAIL
:
30 return DEVICE_MODE_RETAIL_KIOSK
;
32 LOG(ERROR
) << "Unknown enrollment mode in registration response: " << mode
;
33 return DEVICE_MODE_NOT_SET
;
36 bool IsChromePolicy(const std::string
& type
) {
37 return type
== dm_protocol::kChromeDevicePolicyType
||
38 type
== GetChromeUserPolicyType();
43 CloudPolicyClient::Observer::~Observer() {}
45 void CloudPolicyClient::Observer::OnRobotAuthCodesFetched(
46 CloudPolicyClient
* client
) {}
48 CloudPolicyClient::StatusProvider::~StatusProvider() {}
50 CloudPolicyClient::CloudPolicyClient(
51 const std::string
& machine_id
,
52 const std::string
& machine_model
,
53 const std::string
& verification_key_hash
,
54 UserAffiliation user_affiliation
,
55 StatusProvider
* status_provider
,
56 DeviceManagementService
* service
,
57 scoped_refptr
<net::URLRequestContextGetter
> request_context
)
58 : machine_id_(machine_id
),
59 machine_model_(machine_model
),
60 verification_key_hash_(verification_key_hash
),
61 user_affiliation_(user_affiliation
),
62 device_mode_(DEVICE_MODE_NOT_SET
),
63 submit_machine_id_(false),
64 public_key_version_(-1),
65 public_key_version_valid_(false),
66 invalidation_version_(0),
67 fetched_invalidation_version_(0),
68 service_(service
), // Can be NULL for unit tests.
69 status_provider_(status_provider
), // Can be NULL for unit tests.
70 status_(DM_STATUS_SUCCESS
),
71 request_context_(request_context
) {
74 CloudPolicyClient::~CloudPolicyClient() {
75 STLDeleteValues(&responses_
);
78 void CloudPolicyClient::SetupRegistration(const std::string
& dm_token
,
79 const std::string
& client_id
) {
80 DCHECK(!dm_token
.empty());
81 DCHECK(!client_id
.empty());
82 DCHECK(!is_registered());
85 client_id_
= client_id
;
87 STLDeleteValues(&responses_
);
89 NotifyRegistrationStateChanged();
92 void CloudPolicyClient::Register(em::DeviceRegisterRequest::Type type
,
93 const std::string
& auth_token
,
94 const std::string
& client_id
,
95 bool is_auto_enrollement
,
96 const std::string
& requisition
,
97 const std::string
& current_state_key
) {
99 DCHECK(!auth_token
.empty());
100 DCHECK(!is_registered());
102 if (client_id
.empty()) {
103 // Generate a new client ID. This is intentionally done on each new
104 // registration request in order to preserve privacy. Reusing IDs would mean
105 // the server could track clients by their registration attempts.
106 client_id_
= base::GenerateGUID();
108 client_id_
= client_id
;
112 service_
->CreateJob(DeviceManagementRequestJob::TYPE_REGISTRATION
,
113 GetRequestContext()));
114 request_job_
->SetOAuthToken(auth_token
);
115 request_job_
->SetClientID(client_id_
);
117 em::DeviceRegisterRequest
* request
=
118 request_job_
->GetRequest()->mutable_register_request();
119 if (!client_id
.empty())
120 request
->set_reregister(true);
121 request
->set_type(type
);
122 if (!machine_id_
.empty())
123 request
->set_machine_id(machine_id_
);
124 if (!machine_model_
.empty())
125 request
->set_machine_model(machine_model_
);
126 if (is_auto_enrollement
)
127 request
->set_auto_enrolled(true);
128 if (!requisition
.empty())
129 request
->set_requisition(requisition
);
130 if (!current_state_key
.empty())
131 request
->set_server_backed_state_key(current_state_key
);
133 request_job_
->SetRetryCallback(
134 base::Bind(&CloudPolicyClient::OnRetryRegister
, base::Unretained(this)));
136 request_job_
->Start(base::Bind(&CloudPolicyClient::OnRegisterCompleted
,
137 base::Unretained(this)));
140 void CloudPolicyClient::SetInvalidationInfo(
142 const std::string
& payload
) {
143 invalidation_version_
= version
;
144 invalidation_payload_
= payload
;
147 void CloudPolicyClient::FetchPolicy() {
148 CHECK(is_registered());
149 CHECK(!namespaces_to_fetch_
.empty());
152 service_
->CreateJob(DeviceManagementRequestJob::TYPE_POLICY_FETCH
,
153 GetRequestContext()));
154 request_job_
->SetDMToken(dm_token_
);
155 request_job_
->SetClientID(client_id_
);
156 request_job_
->SetUserAffiliation(user_affiliation_
);
158 em::DeviceManagementRequest
* request
= request_job_
->GetRequest();
160 // Build policy fetch requests.
161 em::DevicePolicyRequest
* policy_request
= request
->mutable_policy_request();
162 for (NamespaceSet::iterator it
= namespaces_to_fetch_
.begin();
163 it
!= namespaces_to_fetch_
.end(); ++it
) {
164 em::PolicyFetchRequest
* fetch_request
= policy_request
->add_request();
165 fetch_request
->set_policy_type(it
->first
);
166 if (!it
->second
.empty())
167 fetch_request
->set_settings_entity_id(it
->second
);
169 // Request signed policy blobs to help prevent tampering on the client.
170 fetch_request
->set_signature_type(em::PolicyFetchRequest::SHA1_RSA
);
171 if (public_key_version_valid_
)
172 fetch_request
->set_public_key_version(public_key_version_
);
174 if (!verification_key_hash_
.empty())
175 fetch_request
->set_verification_key_hash(verification_key_hash_
);
177 // These fields are included only in requests for chrome policy.
178 if (IsChromePolicy(it
->first
)) {
179 if (submit_machine_id_
&& !machine_id_
.empty())
180 fetch_request
->set_machine_id(machine_id_
);
181 if (!last_policy_timestamp_
.is_null()) {
182 base::TimeDelta
timestamp(
183 last_policy_timestamp_
- base::Time::UnixEpoch());
184 fetch_request
->set_timestamp(timestamp
.InMilliseconds());
186 if (!invalidation_payload_
.empty()) {
187 fetch_request
->set_invalidation_version(invalidation_version_
);
188 fetch_request
->set_invalidation_payload(invalidation_payload_
);
194 if (status_provider_
) {
195 if (!status_provider_
->GetDeviceStatus(
196 request
->mutable_device_status_report_request())) {
197 request
->clear_device_status_report_request();
199 if (!status_provider_
->GetSessionStatus(
200 request
->mutable_session_status_report_request())) {
201 request
->clear_session_status_report_request();
205 // Add device state keys.
206 if (!state_keys_to_upload_
.empty()) {
207 em::DeviceStateKeyUpdateRequest
* key_update_request
=
208 request
->mutable_device_state_key_update_request();
209 for (std::vector
<std::string
>::const_iterator
key(
210 state_keys_to_upload_
.begin());
211 key
!= state_keys_to_upload_
.end();
213 key_update_request
->add_server_backed_state_key(*key
);
217 // Set the fetched invalidation version to the latest invalidation version
218 // since it is now the invalidation version used for the latest fetch.
219 fetched_invalidation_version_
= invalidation_version_
;
222 request_job_
->Start(base::Bind(&CloudPolicyClient::OnPolicyFetchCompleted
,
223 base::Unretained(this)));
226 void CloudPolicyClient::FetchRobotAuthCodes(const std::string
& auth_token
) {
227 CHECK(is_registered());
228 DCHECK(!auth_token
.empty());
230 request_job_
.reset(service_
->CreateJob(
231 DeviceManagementRequestJob::TYPE_API_AUTH_CODE_FETCH
,
232 GetRequestContext()));
233 // The credentials of a domain user are needed in order to mint a new OAuth2
234 // authorization token for the robot account.
235 request_job_
->SetOAuthToken(auth_token
);
236 request_job_
->SetDMToken(dm_token_
);
237 request_job_
->SetClientID(client_id_
);
239 em::DeviceServiceApiAccessRequest
* request
=
240 request_job_
->GetRequest()->mutable_service_api_access_request();
241 request
->set_oauth2_client_id(
242 GaiaUrls::GetInstance()->oauth2_chrome_client_id());
243 request
->add_auth_scope(GaiaConstants::kAnyApiOAuth2Scope
);
246 base::Bind(&CloudPolicyClient::OnFetchRobotAuthCodesCompleted
,
247 base::Unretained(this)));
250 void CloudPolicyClient::Unregister() {
253 service_
->CreateJob(DeviceManagementRequestJob::TYPE_UNREGISTRATION
,
254 GetRequestContext()));
255 request_job_
->SetDMToken(dm_token_
);
256 request_job_
->SetClientID(client_id_
);
257 request_job_
->GetRequest()->mutable_unregister_request();
258 request_job_
->Start(base::Bind(&CloudPolicyClient::OnUnregisterCompleted
,
259 base::Unretained(this)));
262 void CloudPolicyClient::UploadCertificate(
263 const std::string
& certificate_data
,
264 const CloudPolicyClient::StatusCallback
& callback
) {
265 CHECK(is_registered());
267 service_
->CreateJob(DeviceManagementRequestJob::TYPE_UPLOAD_CERTIFICATE
,
268 GetRequestContext()));
269 request_job_
->SetDMToken(dm_token_
);
270 request_job_
->SetClientID(client_id_
);
272 em::DeviceManagementRequest
* request
= request_job_
->GetRequest();
273 request
->mutable_cert_upload_request()->set_device_certificate(
276 DeviceManagementRequestJob::Callback job_callback
= base::Bind(
277 &CloudPolicyClient::OnCertificateUploadCompleted
,
278 base::Unretained(this),
280 request_job_
->Start(job_callback
);
283 void CloudPolicyClient::AddObserver(Observer
* observer
) {
284 observers_
.AddObserver(observer
);
287 void CloudPolicyClient::RemoveObserver(Observer
* observer
) {
288 observers_
.RemoveObserver(observer
);
291 void CloudPolicyClient::AddNamespaceToFetch(const PolicyNamespaceKey
& key
) {
292 namespaces_to_fetch_
.insert(key
);
295 void CloudPolicyClient::RemoveNamespaceToFetch(const PolicyNamespaceKey
& key
) {
296 namespaces_to_fetch_
.erase(key
);
299 void CloudPolicyClient::SetStateKeysToUpload(
300 const std::vector
<std::string
>& keys
) {
301 state_keys_to_upload_
= keys
;
304 const em::PolicyFetchResponse
* CloudPolicyClient::GetPolicyFor(
305 const PolicyNamespaceKey
& key
) const {
306 ResponseMap::const_iterator it
= responses_
.find(key
);
307 return it
== responses_
.end() ? NULL
: it
->second
;
310 scoped_refptr
<net::URLRequestContextGetter
>
311 CloudPolicyClient::GetRequestContext() {
312 return request_context_
;
315 void CloudPolicyClient::OnRetryRegister(DeviceManagementRequestJob
* job
) {
316 DCHECK_EQ(request_job_
.get(), job
);
317 // If the initial request managed to get to the server but the response didn't
318 // arrive at the client then retrying with the same client ID will fail.
319 // Set the re-registration flag so that the server accepts it.
320 // If the server hasn't seen the client ID before then it will also accept
321 // the re-registration.
322 job
->GetRequest()->mutable_register_request()->set_reregister(true);
325 void CloudPolicyClient::OnRegisterCompleted(
326 DeviceManagementStatus status
,
328 const em::DeviceManagementResponse
& response
) {
329 if (status
== DM_STATUS_SUCCESS
&&
330 (!response
.has_register_response() ||
331 !response
.register_response().has_device_management_token())) {
332 LOG(WARNING
) << "Invalid registration response.";
333 status
= DM_STATUS_RESPONSE_DECODING_ERROR
;
337 if (status
== DM_STATUS_SUCCESS
) {
338 dm_token_
= response
.register_response().device_management_token();
339 DVLOG(1) << "Client registration complete - DMToken = " << dm_token_
;
341 // Device mode is only relevant for device policy really, it's the
342 // responsibility of the consumer of the field to check validity.
343 device_mode_
= DEVICE_MODE_NOT_SET
;
344 if (response
.register_response().has_enrollment_type()) {
345 device_mode_
= TranslateProtobufDeviceMode(
346 response
.register_response().enrollment_type());
349 NotifyRegistrationStateChanged();
355 void CloudPolicyClient::OnFetchRobotAuthCodesCompleted(
356 DeviceManagementStatus status
,
358 const em::DeviceManagementResponse
& response
) {
359 if (status
== DM_STATUS_SUCCESS
&&
360 (!response
.has_service_api_access_response() ||
361 response
.service_api_access_response().auth_code().empty())) {
362 LOG(WARNING
) << "Invalid service api access response.";
363 status
= DM_STATUS_RESPONSE_DECODING_ERROR
;
367 if (status
== DM_STATUS_SUCCESS
) {
368 robot_api_auth_code_
= response
.service_api_access_response().auth_code();
369 DVLOG(1) << "Device robot account auth code fetch complete - code = "
370 << robot_api_auth_code_
;
372 NotifyRobotAuthCodesFetched();
378 void CloudPolicyClient::OnPolicyFetchCompleted(
379 DeviceManagementStatus status
,
381 const em::DeviceManagementResponse
& response
) {
382 if (status
== DM_STATUS_SUCCESS
) {
383 if (!response
.has_policy_response() ||
384 response
.policy_response().response_size() == 0) {
385 LOG(WARNING
) << "Empty policy response.";
386 status
= DM_STATUS_RESPONSE_DECODING_ERROR
;
391 if (status
== DM_STATUS_SUCCESS
) {
392 const em::DevicePolicyResponse
& policy_response
=
393 response
.policy_response();
394 STLDeleteValues(&responses_
);
395 for (int i
= 0; i
< policy_response
.response_size(); ++i
) {
396 const em::PolicyFetchResponse
& response
= policy_response
.response(i
);
397 em::PolicyData policy_data
;
398 if (!policy_data
.ParseFromString(response
.policy_data()) ||
399 !policy_data
.IsInitialized() ||
400 !policy_data
.has_policy_type()) {
401 LOG(WARNING
) << "Invalid PolicyData received, ignoring";
404 const std::string
& type
= policy_data
.policy_type();
405 std::string entity_id
;
406 if (policy_data
.has_settings_entity_id())
407 entity_id
= policy_data
.settings_entity_id();
408 PolicyNamespaceKey
key(type
, entity_id
);
409 if (ContainsKey(responses_
, key
)) {
410 LOG(WARNING
) << "Duplicate PolicyFetchResponse for type: "
411 << type
<< ", entity: " << entity_id
<< ", ignoring";
414 responses_
[key
] = new em::PolicyFetchResponse(response
);
416 if (status_provider_
)
417 status_provider_
->OnSubmittedSuccessfully();
418 state_keys_to_upload_
.clear();
419 NotifyPolicyFetched();
425 void CloudPolicyClient::OnUnregisterCompleted(
426 DeviceManagementStatus status
,
428 const em::DeviceManagementResponse
& response
) {
429 if (status
== DM_STATUS_SUCCESS
&& !response
.has_unregister_response()) {
430 // Assume unregistration has succeeded either way.
431 LOG(WARNING
) << "Empty unregistration response.";
435 if (status
== DM_STATUS_SUCCESS
) {
437 NotifyRegistrationStateChanged();
443 void CloudPolicyClient::OnCertificateUploadCompleted(
444 const CloudPolicyClient::StatusCallback
& callback
,
445 DeviceManagementStatus status
,
447 const enterprise_management::DeviceManagementResponse
& response
) {
448 if (status
== DM_STATUS_SUCCESS
&& !response
.has_cert_upload_response()) {
449 LOG(WARNING
) << "Empty upload certificate response.";
455 if (status
!= DM_STATUS_SUCCESS
) {
463 void CloudPolicyClient::NotifyPolicyFetched() {
464 FOR_EACH_OBSERVER(Observer
, observers_
, OnPolicyFetched(this));
467 void CloudPolicyClient::NotifyRegistrationStateChanged() {
468 FOR_EACH_OBSERVER(Observer
, observers_
, OnRegistrationStateChanged(this));
471 void CloudPolicyClient::NotifyRobotAuthCodesFetched() {
472 FOR_EACH_OBSERVER(Observer
, observers_
, OnRobotAuthCodesFetched(this));
475 void CloudPolicyClient::NotifyClientError() {
476 FOR_EACH_OBSERVER(Observer
, observers_
, OnClientError(this));
479 } // namespace policy