summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormnissler@chromium.org <mnissler@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-03-14 15:22:09 +0000
committermnissler@chromium.org <mnissler@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-03-14 15:22:09 +0000
commit911807d9bccc8bbfba1fe8033bdc7b0adadb1484 (patch)
tree295e85549b7d8c48dc8051232824a84ff77b80c6
parent614a72e27ebbb8445a354edfc12f170198e869f1 (diff)
downloadchromium_src-911807d9bccc8bbfba1fe8033bdc7b0adadb1484.zip
chromium_src-911807d9bccc8bbfba1fe8033bdc7b0adadb1484.tar.gz
chromium_src-911807d9bccc8bbfba1fe8033bdc7b0adadb1484.tar.bz2
Retrieve enterprise device state in OOBE.
This extends AutoEnrollmentClient to be able to retrieve server-backed device state from the cloud during OOBE. This will allow the device to restore enrollment state after recovery. BUG=chromium:351663 TEST=Existing unit tests. R=joaodasilva@chromium.org Review URL: https://codereview.chromium.org/197043002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@257115 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/chromeos/policy/auto_enrollment_client.cc367
-rw-r--r--chrome/browser/chromeos/policy/auto_enrollment_client.h125
-rw-r--r--chrome/browser/chromeos/policy/auto_enrollment_client_unittest.cc222
-rw-r--r--chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.cc29
-rw-r--r--chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h6
-rw-r--r--chrome/browser/chromeos/policy/server_backed_device_state.cc17
-rw-r--r--chrome/browser/chromeos/policy/server_backed_device_state.h20
-rw-r--r--chrome/browser/policy/test/policy_testserver.py58
-rw-r--r--chrome/chrome_browser_chromeos.gypi2
-rw-r--r--chrome/common/pref_names.cc4
-rw-r--r--chrome/common/pref_names.h1
-rw-r--r--chromeos/chromeos_switches.cc4
-rw-r--r--chromeos/chromeos_switches.h1
-rw-r--r--components/policy/core/common/cloud/cloud_policy_constants.cc1
-rw-r--r--components/policy/core/common/cloud/cloud_policy_constants.h1
-rw-r--r--components/policy/core/common/cloud/device_management_service.cc2
-rw-r--r--components/policy/core/common/cloud/device_management_service.h1
-rw-r--r--components/policy/proto/device_management_backend.proto44
18 files changed, 654 insertions, 251 deletions
diff --git a/chrome/browser/chromeos/policy/auto_enrollment_client.cc b/chrome/browser/chromeos/policy/auto_enrollment_client.cc
index a5b9425..2a5fe30 100644
--- a/chrome/browser/chromeos/policy/auto_enrollment_client.cc
+++ b/chrome/browser/chromeos/policy/auto_enrollment_client.cc
@@ -14,9 +14,11 @@
#include "base/metrics/sparse_histogram.h"
#include "base/prefs/pref_registry_simple.h"
#include "base/prefs/pref_service.h"
+#include "base/prefs/scoped_user_pref_update.h"
#include "base/strings/string_number_conversions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h"
+#include "chrome/browser/chromeos/policy/server_backed_device_state.h"
#include "chrome/common/chrome_content_client.h"
#include "chrome/common/pref_names.h"
#include "chromeos/chromeos_switches.h"
@@ -26,12 +28,15 @@
#include "content/public/browser/browser_thread.h"
#include "crypto/sha2.h"
#include "net/url_request/url_request_context_getter.h"
+#include "policy/proto/device_management_backend.pb.h"
#include "url/gurl.h"
using content::BrowserThread;
namespace em = enterprise_management;
+namespace policy {
+
namespace {
// UMA histogram names.
@@ -82,22 +87,70 @@ int NextPowerOf2(int64 value) {
return kMaximumPower + 1;
}
-} // namespace
+// Sets or clears a value in a dictionary.
+void UpdateDict(base::DictionaryValue* dict,
+ const char* pref_path,
+ bool set_or_clear,
+ base::Value* value) {
+ scoped_ptr<base::Value> scoped_value(value);
+ if (set_or_clear)
+ dict->Set(pref_path, scoped_value.release());
+ else
+ dict->Remove(pref_path, NULL);
+}
-namespace policy {
+// Converts a restore mode enum value from the DM protocol into the
+// corresponding prefs string constant.
+std::string ConvertRestoreMode(
+ em::DeviceStateRetrievalResponse::RestoreMode restore_mode) {
+ switch (restore_mode) {
+ case em::DeviceStateRetrievalResponse::RESTORE_MODE_NONE:
+ return std::string();
+ case em::DeviceStateRetrievalResponse::RESTORE_MODE_REENROLLMENT_REQUESTED:
+ return kDeviceStateRestoreModeReEnrollmentRequested;
+ case em::DeviceStateRetrievalResponse::RESTORE_MODE_REENROLLMENT_ENFORCED:
+ return kDeviceStateRestoreModeReEnrollmentEnforced;
+ }
+
+ NOTREACHED() << "Bad restore mode " << restore_mode;
+ return std::string();
+}
+
+// Helper that calls |completion_callback| if |state| indicates termination.
+void CheckCompletion(const base::Closure& completion_callback,
+ AutoEnrollmentClient::State state) {
+ switch (state) {
+ case AutoEnrollmentClient::STATE_PENDING:
+ case AutoEnrollmentClient::STATE_CONNECTION_ERROR:
+ case AutoEnrollmentClient::STATE_SERVER_ERROR:
+ break;
+ case AutoEnrollmentClient::STATE_TRIGGER_ENROLLMENT:
+ case AutoEnrollmentClient::STATE_NO_ENROLLMENT:
+ if (!completion_callback.is_null())
+ completion_callback.Run();
+ break;
+ }
+}
+
+} // namespace
AutoEnrollmentClient::AutoEnrollmentClient(
- const base::Closure& callback,
+ const ProgressCallback& callback,
DeviceManagementService* service,
PrefService* local_state,
scoped_refptr<net::URLRequestContextGetter> system_request_context,
- const std::string& serial_number,
+ const std::string& server_backed_state_key,
+ bool retrieve_device_state,
int power_initial,
int power_limit)
- : completion_callback_(callback),
- should_auto_enroll_(false),
+ : progress_callback_(callback),
+ state_(STATE_PENDING),
+ has_server_state_(false),
+ device_state_available_(false),
device_id_(base::GenerateGUID()),
- power_initial_(power_initial),
+ server_backed_state_key_(server_backed_state_key),
+ retrieve_device_state_(retrieve_device_state),
+ current_power_(power_initial),
power_limit_(power_limit),
modulus_updates_received_(0),
device_management_service_(service),
@@ -105,10 +158,12 @@ AutoEnrollmentClient::AutoEnrollmentClient(
request_context_ = new SystemPolicyRequestContext(
system_request_context, GetUserAgent());
- DCHECK_LE(power_initial_, power_limit_);
- DCHECK(!completion_callback_.is_null());
- if (!serial_number.empty())
- serial_number_hash_ = crypto::SHA256HashString(serial_number);
+ DCHECK_LE(current_power_, power_limit_);
+ DCHECK(!progress_callback_.is_null());
+ if (!server_backed_state_key_.empty()) {
+ server_backed_state_key_hash_ =
+ crypto::SHA256HashString(server_backed_state_key_);
+ }
net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
}
@@ -128,12 +183,11 @@ bool AutoEnrollmentClient::IsDisabled() {
// Do not communicate auto-enrollment data to the server if
// 1. we are running integration or perf tests with telemetry.
// 2. modulus configuration is not present.
- return command_line->HasSwitch(
- chromeos::switches::kOobeSkipPostLogin) ||
+ return command_line->HasSwitch(chromeos::switches::kOobeSkipPostLogin) ||
(!command_line->HasSwitch(
- chromeos::switches::kEnterpriseEnrollmentInitialModulus) &&
- !command_line->HasSwitch(
- chromeos::switches::kEnterpriseEnrollmentModulusLimit));
+ chromeos::switches::kEnterpriseEnrollmentInitialModulus) &&
+ !command_line->HasSwitch(
+ chromeos::switches::kEnterpriseEnrollmentModulusLimit));
}
// static
@@ -160,12 +214,23 @@ AutoEnrollmentClient* AutoEnrollmentClient::Create(
power_initial = power_limit;
}
+ bool retrieve_device_state = false;
+ std::string device_id;
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ chromeos::switches::kEnterpriseEnableForcedReEnrollment)) {
+ retrieve_device_state = true;
+ device_id = DeviceCloudPolicyManagerChromeOS::GetDeviceStateKey();
+ } else {
+ device_id = DeviceCloudPolicyManagerChromeOS::GetMachineID();
+ }
+
return new AutoEnrollmentClient(
- completion_callback,
+ base::Bind(&CheckCompletion, completion_callback),
service,
g_browser_process->local_state(),
g_browser_process->system_request_context(),
- DeviceCloudPolicyManagerChromeOS::GetMachineID(),
+ device_id,
+ retrieve_device_state,
power_initial,
power_limit);
}
@@ -174,36 +239,24 @@ AutoEnrollmentClient* AutoEnrollmentClient::Create(
void AutoEnrollmentClient::CancelAutoEnrollment() {
PrefService* local_state = g_browser_process->local_state();
local_state->SetBoolean(prefs::kShouldAutoEnroll, false);
+ local_state->ClearPref(prefs::kServerBackedDeviceState);
local_state->CommitPendingWrite();
}
void AutoEnrollmentClient::Start() {
// Drop the previous job and reset state.
request_job_.reset();
- should_auto_enroll_ = false;
- time_start_ = base::Time(); // reset to null.
-
- if (GetCachedDecision()) {
- VLOG(1) << "AutoEnrollmentClient: using cached decision: "
- << should_auto_enroll_;
- } else if (device_management_service_) {
- if (serial_number_hash_.empty()) {
- LOG(ERROR) << "Failed to get the hash of the serial number, "
- << "will not attempt to auto-enroll.";
- } else {
- time_start_ = base::Time::Now();
- SendRequest(power_initial_);
- // Don't invoke the callback now.
- return;
- }
- }
+ state_ = STATE_PENDING;
+ time_start_ = base::Time::Now();
+ modulus_updates_received_ = 0;
+ has_server_state_ = false;
+ device_state_available_ = false;
- // Auto-enrollment can't even start, so we're done.
- OnProtocolDone();
+ NextStep();
}
void AutoEnrollmentClient::CancelAndDeleteSoon() {
- if (time_start_.is_null()) {
+ if (time_start_.is_null() || !request_job_) {
// The client isn't running, just delete it.
delete this;
} else {
@@ -211,40 +264,29 @@ void AutoEnrollmentClient::CancelAndDeleteSoon() {
// anymore. Wait until the protocol completes to measure the extra time
// needed.
time_extra_start_ = base::Time::Now();
- completion_callback_.Reset();
+ progress_callback_.Reset();
}
}
void AutoEnrollmentClient::OnNetworkChanged(
net::NetworkChangeNotifier::ConnectionType type) {
- if (GetCachedDecision()) {
- // A previous request already obtained a definitive response from the
- // server, so there is no point in retrying; it will get the same decision.
- return;
- }
-
if (type != net::NetworkChangeNotifier::CONNECTION_NONE &&
- !completion_callback_.is_null() &&
- !request_job_ &&
- device_management_service_ &&
- !serial_number_hash_.empty()) {
- VLOG(1) << "Retrying auto enrollment check after network changed";
- time_start_ = base::Time::Now();
- SendRequest(power_initial_);
+ !progress_callback_.is_null()) {
+ RetryStep();
}
}
bool AutoEnrollmentClient::GetCachedDecision() {
- const PrefService::Preference* should_enroll_pref =
+ const PrefService::Preference* has_server_state_pref =
local_state_->FindPreference(prefs::kShouldAutoEnroll);
const PrefService::Preference* previous_limit_pref =
local_state_->FindPreference(prefs::kAutoEnrollmentPowerLimit);
- bool should_auto_enroll = false;
+ bool has_server_state = false;
int previous_limit = -1;
- if (!should_enroll_pref ||
- should_enroll_pref->IsDefaultValue() ||
- !should_enroll_pref->GetValue()->GetAsBoolean(&should_auto_enroll) ||
+ if (!has_server_state_pref ||
+ has_server_state_pref->IsDefaultValue() ||
+ !has_server_state_pref->GetValue()->GetAsBoolean(&has_server_state) ||
!previous_limit_pref ||
previous_limit_pref->IsDefaultValue() ||
!previous_limit_pref->GetValue()->GetAsInteger(&previous_limit) ||
@@ -252,25 +294,77 @@ bool AutoEnrollmentClient::GetCachedDecision() {
return false;
}
- should_auto_enroll_ = should_auto_enroll;
+ has_server_state_ = has_server_state;
return true;
}
-void AutoEnrollmentClient::SendRequest(int power) {
- if (power < 0 || power > power_limit_ || serial_number_hash_.empty()) {
- NOTREACHED();
- OnRequestDone();
- return;
+bool AutoEnrollmentClient::RetryStep() {
+ // If there is a pending request job, let it finish.
+ if (request_job_)
+ return true;
+
+ if (GetCachedDecision()) {
+ // The bucket download check has completed already. If it came back
+ // positive, then device state should be (re-)downloaded.
+ if (has_server_state_) {
+ if (retrieve_device_state_ && !device_state_available_ &&
+ SendDeviceStateRequest()) {
+ return true;
+ }
+ }
+ } else {
+ // Start bucket download.
+ if (SendBucketDownloadRequest())
+ return true;
+ }
+
+ return false;
+}
+
+void AutoEnrollmentClient::ReportProgress(State state) {
+ state_ = state;
+ if (progress_callback_.is_null()) {
+ base::MessageLoopProxy::current()->DeleteSoon(FROM_HERE, this);
+ } else {
+ progress_callback_.Run(state_);
+ }
+}
+
+void AutoEnrollmentClient::NextStep() {
+ if (!RetryStep()) {
+ // Protocol finished successfully, report result.
+ bool trigger_enrollment = false;
+ if (retrieve_device_state_) {
+ const base::DictionaryValue* device_state_dict =
+ local_state_->GetDictionary(prefs::kServerBackedDeviceState);
+ std::string restore_mode;
+ device_state_dict->GetString(kDeviceStateRestoreMode, &restore_mode);
+ trigger_enrollment =
+ (restore_mode == kDeviceStateRestoreModeReEnrollmentRequested ||
+ restore_mode == kDeviceStateRestoreModeReEnrollmentEnforced);
+ } else {
+ trigger_enrollment = has_server_state_;
+ }
+
+ ReportProgress(trigger_enrollment ? STATE_TRIGGER_ENROLLMENT
+ : STATE_NO_ENROLLMENT);
}
+}
+
+bool AutoEnrollmentClient::SendBucketDownloadRequest() {
+ if (server_backed_state_key_hash_.empty())
+ return false;
// Only power-of-2 moduli are supported for now. These are computed by taking
- // the lower |power| bits of the hash.
+ // the lower |current_power_| bits of the hash.
uint64 remainder = 0;
- for (int i = 0; 8 * i < power; ++i) {
- uint64 byte = serial_number_hash_[31 - i] & 0xff;
+ for (int i = 0; 8 * i < current_power_; ++i) {
+ uint64 byte = server_backed_state_key_hash_[31 - i] & 0xff;
remainder = remainder | (byte << (8 * i));
}
- remainder = remainder & ((GG_UINT64_C(1) << power) - 1);
+ remainder = remainder & ((GG_UINT64_C(1) << current_power_) - 1);
+
+ ReportProgress(STATE_PENDING);
request_job_.reset(
device_management_service_->CreateJob(
@@ -280,28 +374,72 @@ void AutoEnrollmentClient::SendRequest(int power) {
em::DeviceAutoEnrollmentRequest* request =
request_job_->GetRequest()->mutable_auto_enrollment_request();
request->set_remainder(remainder);
- request->set_modulus(GG_INT64_C(1) << power);
- request_job_->Start(base::Bind(&AutoEnrollmentClient::OnRequestCompletion,
- base::Unretained(this)));
+ request->set_modulus(GG_INT64_C(1) << current_power_);
+ request_job_->Start(
+ base::Bind(&AutoEnrollmentClient::HandleRequestCompletion,
+ base::Unretained(this),
+ &AutoEnrollmentClient::OnBucketDownloadRequestCompletion));
+ return true;
+}
+
+bool AutoEnrollmentClient::SendDeviceStateRequest() {
+ ReportProgress(STATE_PENDING);
+
+ request_job_.reset(
+ device_management_service_->CreateJob(
+ DeviceManagementRequestJob::TYPE_DEVICE_STATE_RETRIEVAL,
+ request_context_.get()));
+ request_job_->SetClientID(device_id_);
+ em::DeviceStateRetrievalRequest* request =
+ request_job_->GetRequest()->mutable_device_state_retrieval_request();
+ request->set_server_backed_state_key(server_backed_state_key_);
+ request_job_->Start(
+ base::Bind(&AutoEnrollmentClient::HandleRequestCompletion,
+ base::Unretained(this),
+ &AutoEnrollmentClient::OnDeviceStateRequestCompletion));
+ return true;
}
-void AutoEnrollmentClient::OnRequestCompletion(
+void AutoEnrollmentClient::HandleRequestCompletion(
+ RequestCompletionHandler handler,
DeviceManagementStatus status,
int net_error,
const em::DeviceManagementResponse& response) {
- if (status != DM_STATUS_SUCCESS || !response.has_auto_enrollment_response()) {
+ UMA_HISTOGRAM_SPARSE_SLOWLY(kUMARequestStatus, status);
+ if (status != DM_STATUS_SUCCESS) {
LOG(ERROR) << "Auto enrollment error: " << status;
- UMA_HISTOGRAM_SPARSE_SLOWLY(kUMARequestStatus, status);
if (status == DM_STATUS_REQUEST_FAILED)
UMA_HISTOGRAM_SPARSE_SLOWLY(kUMANetworkErrorCode, -net_error);
- // The client will retry if a network change is detected.
- OnRequestDone();
+ request_job_.reset();
+
+ // Abort if CancelAndDeleteSoon has been called meanwhile.
+ if (progress_callback_.is_null()) {
+ base::MessageLoopProxy::current()->DeleteSoon(FROM_HERE, this);
+ } else {
+ ReportProgress(status == DM_STATUS_REQUEST_FAILED ? STATE_CONNECTION_ERROR
+ : STATE_SERVER_ERROR);
+ }
return;
}
+ bool progress = (this->*handler)(status, net_error, response);
+ request_job_.reset();
+ if (progress)
+ NextStep();
+ else
+ ReportProgress(STATE_SERVER_ERROR);
+}
+
+bool AutoEnrollmentClient::OnBucketDownloadRequestCompletion(
+ DeviceManagementStatus status,
+ int net_error,
+ const em::DeviceManagementResponse& response) {
+ bool progress = false;
const em::DeviceAutoEnrollmentResponse& enrollment_response =
response.auto_enrollment_response();
- if (enrollment_response.has_expected_modulus()) {
+ if (!response.has_auto_enrollment_response()) {
+ LOG(ERROR) << "Server failed to provide auto-enrollment response.";
+ } else if (enrollment_response.has_expected_modulus()) {
// Server is asking us to retry with a different modulus.
modulus_updates_received_++;
@@ -322,44 +460,76 @@ void AutoEnrollmentClient::OnRequestCompletion(
<< power_limit_ << ").";
} else {
// Retry at most once with the modulus that the server requested.
- if (power <= power_initial_) {
+ if (power <= current_power_) {
LOG(WARNING) << "Auto enrollment: the server asked to use a modulus ("
<< power << ") that isn't larger than the first used ("
- << power_initial_ << "). Retrying anyway.";
+ << current_power_ << "). Retrying anyway.";
}
// Remember this value, so that eventual retries start with the correct
// modulus.
- power_initial_ = power;
- SendRequest(power);
- return;
+ current_power_ = power;
+ return true;
}
} else {
// Server should have sent down a list of hashes to try.
- should_auto_enroll_ = IsSerialInProtobuf(enrollment_response.hash());
+ has_server_state_ = IsIdHashInProtobuf(enrollment_response.hash());
// Cache the current decision in local_state, so that it is reused in case
// the device reboots before enrolling.
- local_state_->SetBoolean(prefs::kShouldAutoEnroll, should_auto_enroll_);
+ local_state_->SetBoolean(prefs::kShouldAutoEnroll, has_server_state_);
local_state_->SetInteger(prefs::kAutoEnrollmentPowerLimit, power_limit_);
local_state_->CommitPendingWrite();
- VLOG(1) << "Auto enrollment complete, should_auto_enroll = "
- << should_auto_enroll_;
+ VLOG(1) << "Auto enrollment check complete, has_server_state_ = "
+ << has_server_state_;
+ progress = true;
}
- // Auto-enrollment done.
- UMA_HISTOGRAM_SPARSE_SLOWLY(kUMARequestStatus, DM_STATUS_SUCCESS);
- OnProtocolDone();
+ // Bucket download done, update UMA.
+ UpdateBucketDownloadTimingHistograms();
+ return progress;
}
-bool AutoEnrollmentClient::IsSerialInProtobuf(
+bool AutoEnrollmentClient::OnDeviceStateRequestCompletion(
+ DeviceManagementStatus status,
+ int net_error,
+ const enterprise_management::DeviceManagementResponse& response) {
+ bool progress = false;
+ if (!response.has_device_state_retrieval_response()) {
+ LOG(ERROR) << "Server failed to provide auto-enrollment response.";
+ } else {
+ const em::DeviceStateRetrievalResponse& state_response =
+ response.device_state_retrieval_response();
+ {
+ DictionaryPrefUpdate dict(local_state_, prefs::kServerBackedDeviceState);
+ UpdateDict(dict.Get(),
+ kDeviceStateManagementDomain,
+ state_response.has_management_domain(),
+ new base::StringValue(state_response.management_domain()));
+
+ std::string restore_mode =
+ ConvertRestoreMode(state_response.restore_mode());
+ UpdateDict(dict.Get(),
+ kDeviceStateRestoreMode,
+ !restore_mode.empty(),
+ new base::StringValue(restore_mode));
+ }
+ local_state_->CommitPendingWrite();
+ device_state_available_ = true;
+ progress = true;
+ }
+
+ return progress;
+}
+
+bool AutoEnrollmentClient::IsIdHashInProtobuf(
const google::protobuf::RepeatedPtrField<std::string>& hashes) {
for (int i = 0; i < hashes.size(); ++i) {
- if (hashes.Get(i) == serial_number_hash_)
+ if (hashes.Get(i) == server_backed_state_key_hash_)
return true;
}
return false;
}
-void AutoEnrollmentClient::OnProtocolDone() {
+void AutoEnrollmentClient::UpdateBucketDownloadTimingHistograms() {
// The mininum time can't be 0, must be at least 1.
static const base::TimeDelta kMin = base::TimeDelta::FromMilliseconds(1);
static const base::TimeDelta kMax = base::TimeDelta::FromMinutes(5);
@@ -379,21 +549,6 @@ void AutoEnrollmentClient::OnProtocolDone() {
// measure the ratio of users that succeeded without needing a delay to the
// total users going through OOBE.
UMA_HISTOGRAM_CUSTOM_TIMES(kUMAExtraTime, delta, kMin, kMax, kBuckets);
-
- if (!completion_callback_.is_null())
- completion_callback_.Run();
-
- OnRequestDone();
-}
-
-void AutoEnrollmentClient::OnRequestDone() {
- request_job_.reset();
- time_start_ = base::Time();
-
- if (completion_callback_.is_null()) {
- // CancelAndDeleteSoon() was invoked before.
- base::MessageLoopProxy::current()->DeleteSoon(FROM_HERE, this);
- }
}
} // namespace policy
diff --git a/chrome/browser/chromeos/policy/auto_enrollment_client.h b/chrome/browser/chromeos/policy/auto_enrollment_client.h
index e27065a..0c8a0e8 100644
--- a/chrome/browser/chromeos/policy/auto_enrollment_client.h
+++ b/chrome/browser/chromeos/policy/auto_enrollment_client.h
@@ -38,17 +38,35 @@ class DeviceManagementService;
class AutoEnrollmentClient
: public net::NetworkChangeNotifier::NetworkChangeObserver {
public:
- // |completion_callback| will be invoked on completion of the protocol, after
- // Start() is invoked.
+ // Indicates the current state of the auto-enrollment check.
+ enum State {
+ // Working, another event will be fired eventually.
+ STATE_PENDING,
+ // Failed to connect to DMServer.
+ STATE_CONNECTION_ERROR,
+ // Connection successful, but the server failed to generate a valid reply.
+ STATE_SERVER_ERROR,
+ // Check completed successfully, enrollment should be triggered.
+ STATE_TRIGGER_ENROLLMENT,
+ // Check completed successfully, enrollment not applicable.
+ STATE_NO_ENROLLMENT,
+ };
+
+ // Used for signaling progress to a consumer.
+ typedef base::Callback<void(State)> ProgressCallback;
+
+ // |progress_callback| will be invoked whenever some significant event happens
+ // as part of the protocol, after Start() is invoked.
// The result of the protocol will be cached in |local_state|.
// |power_initial| and |power_limit| are exponents of power-of-2 values which
// will be the initial modulus and the maximum modulus used by this client.
AutoEnrollmentClient(
- const base::Closure& completion_callback,
+ const ProgressCallback& progress_callback,
DeviceManagementService* device_management_service,
PrefService* local_state,
scoped_refptr<net::URLRequestContextGetter> system_request_context,
- const std::string& serial_number,
+ const std::string& server_backed_state_key,
+ bool retrieve_device_state,
int power_initial,
int power_limit);
virtual ~AutoEnrollmentClient();
@@ -61,7 +79,9 @@ class AutoEnrollmentClient
static bool IsDisabled();
// Convenience method to create instances of this class.
- static AutoEnrollmentClient* Create(const base::Closure& completion_callback);
+ // TODO(mnissler): Convert callers to pass a ProgressCallback here.
+ static AutoEnrollmentClient* Create(
+ const base::Closure& completion_callback);
// Cancels auto-enrollment.
// This function does not interrupt a running auto-enrollment check. It only
@@ -71,70 +91,111 @@ class AutoEnrollmentClient
// Starts the auto-enrollment check protocol with the device management
// service. Subsequent calls drop any previous requests. Notice that this
- // call can invoke the |completion_callback_| if errors occur.
+ // call can invoke the |progress_callback_| if errors occur.
void Start();
- // Cancels any pending requests. |completion_callback_| will not be invoked.
+ // Cancels any pending requests. |progress_callback_| will not be invoked.
// |this| will delete itself.
void CancelAndDeleteSoon();
// Returns true if the protocol completed successfully and determined that
// this device should do enterprise enrollment.
- bool should_auto_enroll() const { return should_auto_enroll_; }
+ // TODO(mnissler): Remove once callers have been converted to state().
+ bool should_auto_enroll() const {
+ return state_ == STATE_TRIGGER_ENROLLMENT;
+ };
// Returns the device_id randomly generated for the auto-enrollment requests.
// It can be reused for subsequent requests to the device management service.
std::string device_id() const { return device_id_; }
+ // Current state.
+ State state() const { return state_; }
+
// Implementation of net::NetworkChangeNotifier::NetworkChangeObserver:
virtual void OnNetworkChanged(
net::NetworkChangeNotifier::ConnectionType type) OVERRIDE;
private:
+ typedef bool (AutoEnrollmentClient::*RequestCompletionHandler)(
+ DeviceManagementStatus,
+ int,
+ const enterprise_management::DeviceManagementResponse&);
+
// Tries to load the result of a previous execution of the protocol from
// local state. Returns true if that decision has been made and is valid.
bool GetCachedDecision();
+ // Kicks protocol processing, restarting the current step if applicable.
+ // Returns true if progress has been made, false if the protocol is done.
+ bool RetryStep();
+
+ // Cleans up and invokes |progress_callback_|.
+ void ReportProgress(State state);
+
+ // Calls RetryStep() to make progress or determine that all is done. In the
+ // latter case, calls ReportProgress().
+ void NextStep();
+
// Sends an auto-enrollment check request to the device management service.
- // |power| is the power of the power-of-2 to use as a modulus for this
- // request.
- void SendRequest(int power);
+ bool SendBucketDownloadRequest();
+
+ // Sends a device state download request to the device management service.
+ bool SendDeviceStateRequest();
+
+ // Runs the response handler for device management requests and calls
+ // NextStep().
+ void HandleRequestCompletion(
+ RequestCompletionHandler handler,
+ DeviceManagementStatus status,
+ int net_error,
+ const enterprise_management::DeviceManagementResponse& response);
+
+ // Parses the server response to a bucket download request.
+ bool OnBucketDownloadRequestCompletion(
+ DeviceManagementStatus status,
+ int net_error,
+ const enterprise_management::DeviceManagementResponse& response);
- // Handles auto-enrollment request completion.
- void OnRequestCompletion(
+ // Parses the server response to a device state request.
+ bool OnDeviceStateRequestCompletion(
DeviceManagementStatus status,
int net_error,
const enterprise_management::DeviceManagementResponse& response);
- // Returns true if |serial_number_hash_| is contained in |hashes|.
- bool IsSerialInProtobuf(
+ // Returns true if |server_backed_state_key_hash_| is contained in |hashes|.
+ bool IsIdHashInProtobuf(
const google::protobuf::RepeatedPtrField<std::string>& hashes);
- // Invoked when the protocol completes. This invokes the callback and records
- // some UMA metrics.
- void OnProtocolDone();
+ // Updates UMA histograms for bucket download timings.
+ void UpdateBucketDownloadTimingHistograms();
- // Invoked when a request job completes. Resets the internal state, and
- // deletes the client if necessary.
- void OnRequestDone();
+ // Callback to invoke when the protocol generates a relevant event. This can
+ // be either successful completion or an error that requires external action.
+ ProgressCallback progress_callback_;
- // Callback to invoke when the protocol completes.
- base::Closure completion_callback_;
+ // Current state.
+ State state_;
- // Whether to auto-enroll or not. This is reset by calls to Start(), and only
- // turns true if the protocol and the serial number check succeed.
- bool should_auto_enroll_;
+ // Whether the hash bucket check succeeded, indicating that the server knows
+ // this device and might have keep state for it.
+ bool has_server_state_;
+
+ // Whether the download of server-kept device state completed successfully.
+ bool device_state_available_;
// Randomly generated device id for the auto-enrollment requests.
std::string device_id_;
- // SHA256 hash of the device's serial number. Empty if the serial couldn't be
- // retrieved.
- std::string serial_number_hash_;
+ // Stable state key and its SHA-256 digest.
+ std::string server_backed_state_key_;
+ std::string server_backed_state_key_hash_;
+
+ // Whether device state should be retrieved from the server.
+ bool retrieve_device_state_;
- // Power of the power-of-2 modulus used in the initial auto-enrollment
- // request.
- int power_initial_;
+ // Power-of-2 modulus to try next.
+ int current_power_;
// Power of the maximum power-of-2 modulus that this client will accept from
// a retry response from the server.
diff --git a/chrome/browser/chromeos/policy/auto_enrollment_client_unittest.cc b/chrome/browser/chromeos/policy/auto_enrollment_client_unittest.cc
index 91751eb..cae311e 100644
--- a/chrome/browser/chromeos/policy/auto_enrollment_client_unittest.cc
+++ b/chrome/browser/chromeos/policy/auto_enrollment_client_unittest.cc
@@ -9,6 +9,8 @@
#include "base/message_loop/message_loop.h"
#include "base/prefs/pref_service.h"
#include "base/prefs/testing_pref_service.h"
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/common/pref_names.h"
@@ -28,10 +30,10 @@ namespace policy {
namespace {
-const char* kSerial = "serial";
-const char* kSerialHash =
- "\x01\x44\xb1\xde\xfc\xf7\x56\x10\x87\x01\x5f\x8d\x83\x0d\x65\xb1"
- "\x6f\x02\x4a\xd7\xeb\x92\x45\xfc\xd4\xe4\x37\xa1\x55\x2b\x13\x8a";
+const char kStateKey[] = "state_key";
+const char kStateKeyHash[] =
+ "\xde\x74\xcd\xf0\x03\x36\x8c\x21\x79\xba\xb1\x5a\xc4\x32\xee\xd6"
+ "\xb3\x4a\x5e\xff\x73\x7e\x92\xd9\xf8\x6e\x72\x44\xd0\x97\xc3\xe6";
using ::testing::InSequence;
using ::testing::Mock;
@@ -44,36 +46,42 @@ class AutoEnrollmentClientTest : public testing::Test {
: scoped_testing_local_state_(
TestingBrowserProcess::GetGlobal()),
local_state_(scoped_testing_local_state_.Get()),
- completion_callback_count_(0) {}
+ state_(AutoEnrollmentClient::STATE_PENDING) {}
virtual void SetUp() OVERRIDE {
- CreateClient(kSerial, 4, 8);
+ CreateClient(kStateKey, true, 4, 8);
ASSERT_FALSE(local_state_->GetUserPref(prefs::kShouldAutoEnroll));
ASSERT_FALSE(local_state_->GetUserPref(prefs::kAutoEnrollmentPowerLimit));
}
- void CreateClient(const std::string& serial,
+ virtual void TearDown() OVERRIDE {
+ // Flush any deletion tasks.
+ base::RunLoop().RunUntilIdle();
+ }
+
+ void CreateClient(const std::string& state_key,
+ bool retrieve_device_state,
int power_initial,
int power_limit) {
+ state_ = AutoEnrollmentClient::STATE_PENDING;
service_.reset(new MockDeviceManagementService());
EXPECT_CALL(*service_, StartJob(_, _, _, _, _, _, _))
.WillRepeatedly(SaveArg<6>(&last_request_));
- base::Closure callback =
- base::Bind(&AutoEnrollmentClientTest::CompletionCallback,
- base::Unretained(this));
client_.reset(new AutoEnrollmentClient(
- callback,
+ base::Bind(&AutoEnrollmentClientTest::ProgressCallback,
+ base::Unretained(this)),
service_.get(),
local_state_,
new net::TestURLRequestContextGetter(
base::MessageLoop::current()->message_loop_proxy()),
- serial,
+ state_key,
+ retrieve_device_state,
power_initial,
power_limit));
}
- void CompletionCallback() {
- completion_callback_count_++;
+ void ProgressCallback(AutoEnrollmentClient::State state) {
+ state_ = state;
}
void ServerWillFail(DeviceManagementStatus error) {
@@ -83,7 +91,7 @@ class AutoEnrollmentClientTest : public testing::Test {
.WillOnce(service_->FailJob(error));
}
- void ServerWillReply(int64 modulus, bool with_hashes, bool with_serial_hash) {
+ void ServerWillReply(int64 modulus, bool with_hashes, bool with_id_hash) {
em::DeviceManagementResponse response;
em::DeviceAutoEnrollmentResponse* enrollment_response =
response.mutable_auto_enrollment_response();
@@ -91,21 +99,34 @@ class AutoEnrollmentClientTest : public testing::Test {
enrollment_response->set_expected_modulus(modulus);
if (with_hashes) {
for (size_t i = 0; i < 10; ++i) {
- std::string serial = "serial X";
- serial[7] = '0' + i;
- std::string hash = crypto::SHA256HashString(serial);
+ std::string state_key = base::StringPrintf("state_key %lu", i);
+ std::string hash = crypto::SHA256HashString(state_key);
enrollment_response->mutable_hash()->Add()->assign(hash);
}
}
- if (with_serial_hash) {
- enrollment_response->mutable_hash()->Add()->assign(kSerialHash,
- crypto::kSHA256Length);
+ if (with_id_hash) {
+ enrollment_response->mutable_hash()->Add()->assign(kStateKeyHash,
+ crypto::kSHA256Length);
}
EXPECT_CALL(*service_,
CreateJob(DeviceManagementRequestJob::TYPE_AUTO_ENROLLMENT, _))
.WillOnce(service_->SucceedJob(response));
}
+ void ServerWillSendState(
+ const std::string& management_domain,
+ em::DeviceStateRetrievalResponse::RestoreMode restore_mode) {
+ em::DeviceManagementResponse response;
+ em::DeviceStateRetrievalResponse* state_response =
+ response.mutable_device_state_retrieval_response();
+ state_response->set_restore_mode(restore_mode);
+ state_response->set_management_domain(management_domain);
+ EXPECT_CALL(
+ *service_,
+ CreateJob(DeviceManagementRequestJob::TYPE_DEVICE_STATE_RETRIEVAL, _))
+ .WillOnce(service_->SucceedJob(response));
+ }
+
void ServerWillReplyAsync(MockDeviceManagementJob** job) {
EXPECT_CALL(*service_,
CreateJob(DeviceManagementRequestJob::TYPE_AUTO_ENROLLMENT, _))
@@ -137,7 +158,7 @@ class AutoEnrollmentClientTest : public testing::Test {
scoped_ptr<MockDeviceManagementService> service_;
scoped_ptr<AutoEnrollmentClient> client_;
em::DeviceManagementRequest last_request_;
- int completion_callback_count_;
+ AutoEnrollmentClient::State state_;
private:
DISALLOW_COPY_AND_ASSIGN(AutoEnrollmentClientTest);
@@ -146,29 +167,26 @@ class AutoEnrollmentClientTest : public testing::Test {
TEST_F(AutoEnrollmentClientTest, NetworkFailure) {
ServerWillFail(DM_STATUS_TEMPORARY_UNAVAILABLE);
client_->Start();
- EXPECT_FALSE(client_->should_auto_enroll());
- EXPECT_EQ(0, completion_callback_count_);
+ EXPECT_EQ(AutoEnrollmentClient::STATE_SERVER_ERROR, state_);
EXPECT_FALSE(HasCachedDecision());
}
TEST_F(AutoEnrollmentClientTest, EmptyReply) {
ServerWillReply(-1, false, false);
client_->Start();
- EXPECT_FALSE(client_->should_auto_enroll());
- EXPECT_EQ(1, completion_callback_count_);
+ EXPECT_EQ(AutoEnrollmentClient::STATE_NO_ENROLLMENT, state_);
VerifyCachedResult(false, 8);
}
TEST_F(AutoEnrollmentClientTest, ClientUploadsRightBits) {
ServerWillReply(-1, false, false);
client_->Start();
- EXPECT_FALSE(client_->should_auto_enroll());
- EXPECT_EQ(1, completion_callback_count_);
+ EXPECT_EQ(AutoEnrollmentClient::STATE_NO_ENROLLMENT, state_);
EXPECT_TRUE(auto_enrollment_request().has_remainder());
EXPECT_TRUE(auto_enrollment_request().has_modulus());
EXPECT_EQ(16, auto_enrollment_request().modulus());
- EXPECT_EQ(kSerialHash[31] & 0xf, auto_enrollment_request().remainder());
+ EXPECT_EQ(kStateKeyHash[31] & 0xf, auto_enrollment_request().remainder());
VerifyCachedResult(false, 8);
}
@@ -177,8 +195,7 @@ TEST_F(AutoEnrollmentClientTest, AskForMoreThenFail) {
ServerWillReply(32, false, false);
ServerWillFail(DM_STATUS_TEMPORARY_UNAVAILABLE);
client_->Start();
- EXPECT_FALSE(client_->should_auto_enroll());
- EXPECT_EQ(0, completion_callback_count_);
+ EXPECT_EQ(AutoEnrollmentClient::STATE_SERVER_ERROR, state_);
EXPECT_FALSE(HasCachedDecision());
}
@@ -187,8 +204,7 @@ TEST_F(AutoEnrollmentClientTest, AskForMoreThenEvenMore) {
ServerWillReply(32, false, false);
ServerWillReply(64, false, false);
client_->Start();
- EXPECT_FALSE(client_->should_auto_enroll());
- EXPECT_EQ(1, completion_callback_count_);
+ EXPECT_EQ(AutoEnrollmentClient::STATE_SERVER_ERROR, state_);
EXPECT_FALSE(HasCachedDecision());
}
@@ -196,9 +212,11 @@ TEST_F(AutoEnrollmentClientTest, AskForLess) {
InSequence sequence;
ServerWillReply(8, false, false);
ServerWillReply(-1, true, true);
+ ServerWillSendState(
+ "example.com",
+ em::DeviceStateRetrievalResponse::RESTORE_MODE_REENROLLMENT_ENFORCED);
client_->Start();
- EXPECT_TRUE(client_->should_auto_enroll());
- EXPECT_EQ(1, completion_callback_count_);
+ EXPECT_EQ(AutoEnrollmentClient::STATE_TRIGGER_ENROLLMENT, state_);
VerifyCachedResult(true, 8);
}
@@ -206,9 +224,11 @@ TEST_F(AutoEnrollmentClientTest, AskForSame) {
InSequence sequence;
ServerWillReply(16, false, false);
ServerWillReply(-1, true, true);
+ ServerWillSendState(
+ "example.com",
+ em::DeviceStateRetrievalResponse::RESTORE_MODE_REENROLLMENT_ENFORCED);
client_->Start();
- EXPECT_TRUE(client_->should_auto_enroll());
- EXPECT_EQ(1, completion_callback_count_);
+ EXPECT_EQ(AutoEnrollmentClient::STATE_TRIGGER_ENROLLMENT, state_);
VerifyCachedResult(true, 8);
}
@@ -217,16 +237,14 @@ TEST_F(AutoEnrollmentClientTest, AskForSameTwice) {
ServerWillReply(16, false, false);
ServerWillReply(16, false, false);
client_->Start();
- EXPECT_FALSE(client_->should_auto_enroll());
- EXPECT_EQ(1, completion_callback_count_);
+ EXPECT_EQ(AutoEnrollmentClient::STATE_SERVER_ERROR, state_);
EXPECT_FALSE(HasCachedDecision());
}
TEST_F(AutoEnrollmentClientTest, AskForTooMuch) {
ServerWillReply(512, false, false);
client_->Start();
- EXPECT_FALSE(client_->should_auto_enroll());
- EXPECT_EQ(1, completion_callback_count_);
+ EXPECT_EQ(AutoEnrollmentClient::STATE_SERVER_ERROR, state_);
EXPECT_FALSE(HasCachedDecision());
}
@@ -235,55 +253,53 @@ TEST_F(AutoEnrollmentClientTest, AskNonPowerOf2) {
ServerWillReply(100, false, false);
ServerWillReply(-1, false, false);
client_->Start();
- EXPECT_FALSE(client_->should_auto_enroll());
- EXPECT_EQ(1, completion_callback_count_);
+ EXPECT_EQ(AutoEnrollmentClient::STATE_NO_ENROLLMENT, state_);
EXPECT_TRUE(auto_enrollment_request().has_remainder());
EXPECT_TRUE(auto_enrollment_request().has_modulus());
EXPECT_EQ(128, auto_enrollment_request().modulus());
- EXPECT_EQ(kSerialHash[31] & 0x7f, auto_enrollment_request().remainder());
+ EXPECT_EQ(kStateKeyHash[31] & 0x7f, auto_enrollment_request().remainder());
VerifyCachedResult(false, 8);
}
TEST_F(AutoEnrollmentClientTest, ConsumerDevice) {
ServerWillReply(-1, true, false);
client_->Start();
- EXPECT_FALSE(client_->should_auto_enroll());
- EXPECT_EQ(1, completion_callback_count_);
+ EXPECT_EQ(AutoEnrollmentClient::STATE_NO_ENROLLMENT, state_);
VerifyCachedResult(false, 8);
// Network changes don't trigger retries after obtaining a response from
// the server.
client_->OnNetworkChanged(net::NetworkChangeNotifier::CONNECTION_ETHERNET);
- EXPECT_EQ(1, completion_callback_count_);
+ EXPECT_EQ(AutoEnrollmentClient::STATE_NO_ENROLLMENT, state_);
}
TEST_F(AutoEnrollmentClientTest, EnterpriseDevice) {
ServerWillReply(-1, true, true);
+ ServerWillSendState(
+ "example.com",
+ em::DeviceStateRetrievalResponse::RESTORE_MODE_REENROLLMENT_ENFORCED);
client_->Start();
- EXPECT_TRUE(client_->should_auto_enroll());
- EXPECT_EQ(1, completion_callback_count_);
+ EXPECT_EQ(AutoEnrollmentClient::STATE_TRIGGER_ENROLLMENT, state_);
VerifyCachedResult(true, 8);
// Network changes don't trigger retries after obtaining a response from
// the server.
client_->OnNetworkChanged(net::NetworkChangeNotifier::CONNECTION_ETHERNET);
- EXPECT_EQ(1, completion_callback_count_);
+ EXPECT_EQ(AutoEnrollmentClient::STATE_TRIGGER_ENROLLMENT, state_);
}
TEST_F(AutoEnrollmentClientTest, NoSerial) {
- CreateClient("", 4, 8);
+ CreateClient("", true, 4, 8);
client_->Start();
- EXPECT_FALSE(client_->should_auto_enroll());
- EXPECT_EQ(1, completion_callback_count_);
+ EXPECT_EQ(AutoEnrollmentClient::STATE_NO_ENROLLMENT, state_);
EXPECT_FALSE(HasCachedDecision());
}
TEST_F(AutoEnrollmentClientTest, NoBitsUploaded) {
- CreateClient(kSerial, 0, 0);
+ CreateClient(kStateKey, true, 0, 0);
ServerWillReply(-1, false, false);
client_->Start();
- EXPECT_FALSE(client_->should_auto_enroll());
- EXPECT_EQ(1, completion_callback_count_);
+ EXPECT_EQ(AutoEnrollmentClient::STATE_NO_ENROLLMENT, state_);
EXPECT_TRUE(auto_enrollment_request().has_remainder());
EXPECT_TRUE(auto_enrollment_request().has_modulus());
EXPECT_EQ(1, auto_enrollment_request().modulus());
@@ -292,14 +308,12 @@ TEST_F(AutoEnrollmentClientTest, NoBitsUploaded) {
}
TEST_F(AutoEnrollmentClientTest, ManyBitsUploaded) {
- int64 bottom62 = GG_INT64_C(0x14e437a1552b138a);
+ int64 bottom62 = GG_INT64_C(0x386e7244d097c3e6);
for (int i = 0; i <= 62; ++i) {
- completion_callback_count_ = 0;
- CreateClient(kSerial, i, i);
+ CreateClient(kStateKey, true, i, i);
ServerWillReply(-1, false, false);
client_->Start();
- EXPECT_FALSE(client_->should_auto_enroll());
- EXPECT_EQ(1, completion_callback_count_);
+ EXPECT_EQ(AutoEnrollmentClient::STATE_NO_ENROLLMENT, state_);
EXPECT_TRUE(auto_enrollment_request().has_remainder());
EXPECT_TRUE(auto_enrollment_request().has_modulus());
EXPECT_EQ(GG_INT64_C(1) << i, auto_enrollment_request().modulus());
@@ -310,13 +324,15 @@ TEST_F(AutoEnrollmentClientTest, ManyBitsUploaded) {
}
TEST_F(AutoEnrollmentClientTest, MoreThan32BitsUploaded) {
- CreateClient(kSerial, 10, 37);
+ CreateClient(kStateKey, true, 10, 37);
InSequence sequence;
ServerWillReply(GG_INT64_C(1) << 37, false, false);
ServerWillReply(-1, true, true);
+ ServerWillSendState(
+ "example.com",
+ em::DeviceStateRetrievalResponse::RESTORE_MODE_REENROLLMENT_ENFORCED);
client_->Start();
- EXPECT_TRUE(client_->should_auto_enroll());
- EXPECT_EQ(1, completion_callback_count_);
+ EXPECT_EQ(AutoEnrollmentClient::STATE_TRIGGER_ENROLLMENT, state_);
VerifyCachedResult(true, 37);
}
@@ -326,13 +342,14 @@ TEST_F(AutoEnrollmentClientTest, ReuseCachedDecision) {
base::Value::CreateBooleanValue(true));
local_state_->SetUserPref(prefs::kAutoEnrollmentPowerLimit,
base::Value::CreateIntegerValue(8));
+ ServerWillSendState(
+ "example.com",
+ em::DeviceStateRetrievalResponse::RESTORE_MODE_REENROLLMENT_ENFORCED);
client_->Start();
- EXPECT_TRUE(client_->should_auto_enroll());
- EXPECT_EQ(1, completion_callback_count_);
+ EXPECT_EQ(AutoEnrollmentClient::STATE_TRIGGER_ENROLLMENT, state_);
AutoEnrollmentClient::CancelAutoEnrollment();
client_->Start();
- EXPECT_FALSE(client_->should_auto_enroll());
- EXPECT_EQ(2, completion_callback_count_);
+ EXPECT_EQ(AutoEnrollmentClient::STATE_NO_ENROLLMENT, state_);
}
TEST_F(AutoEnrollmentClientTest, RetryIfPowerLargerThanCached) {
@@ -340,39 +357,40 @@ TEST_F(AutoEnrollmentClientTest, RetryIfPowerLargerThanCached) {
base::Value::CreateBooleanValue(false));
local_state_->SetUserPref(prefs::kAutoEnrollmentPowerLimit,
base::Value::CreateIntegerValue(8));
- CreateClient(kSerial, 5, 10);
+ CreateClient(kStateKey, true, 5, 10);
ServerWillReply(-1, true, true);
+ ServerWillSendState(
+ "example.com",
+ em::DeviceStateRetrievalResponse::RESTORE_MODE_REENROLLMENT_ENFORCED);
client_->Start();
- EXPECT_TRUE(client_->should_auto_enroll());
- EXPECT_EQ(1, completion_callback_count_);
+ EXPECT_EQ(AutoEnrollmentClient::STATE_TRIGGER_ENROLLMENT, state_);
}
TEST_F(AutoEnrollmentClientTest, NetworkChangeRetryAfterErrors) {
ServerWillFail(DM_STATUS_TEMPORARY_UNAVAILABLE);
client_->Start();
- EXPECT_FALSE(client_->should_auto_enroll());
// Don't invoke the callback if there was a network failure.
- EXPECT_EQ(0, completion_callback_count_);
+ EXPECT_EQ(AutoEnrollmentClient::STATE_SERVER_ERROR, state_);
EXPECT_FALSE(HasCachedDecision());
// The client doesn't retry if no new connection became available.
client_->OnNetworkChanged(net::NetworkChangeNotifier::CONNECTION_NONE);
- EXPECT_FALSE(client_->should_auto_enroll());
- EXPECT_EQ(0, completion_callback_count_);
+ EXPECT_EQ(AutoEnrollmentClient::STATE_SERVER_ERROR, state_);
EXPECT_FALSE(HasCachedDecision());
// Retry once the network is back.
ServerWillReply(-1, true, true);
+ ServerWillSendState(
+ "example.com",
+ em::DeviceStateRetrievalResponse::RESTORE_MODE_REENROLLMENT_ENFORCED);
client_->OnNetworkChanged(net::NetworkChangeNotifier::CONNECTION_ETHERNET);
- EXPECT_TRUE(client_->should_auto_enroll());
- EXPECT_EQ(1, completion_callback_count_);
+ EXPECT_EQ(AutoEnrollmentClient::STATE_TRIGGER_ENROLLMENT, state_);
EXPECT_TRUE(HasCachedDecision());
// Subsequent network changes don't trigger retries.
client_->OnNetworkChanged(net::NetworkChangeNotifier::CONNECTION_NONE);
client_->OnNetworkChanged(net::NetworkChangeNotifier::CONNECTION_ETHERNET);
- EXPECT_TRUE(client_->should_auto_enroll());
- EXPECT_EQ(1, completion_callback_count_);
+ EXPECT_EQ(AutoEnrollmentClient::STATE_TRIGGER_ENROLLMENT, state_);
EXPECT_TRUE(HasCachedDecision());
}
@@ -382,7 +400,7 @@ TEST_F(AutoEnrollmentClientTest, CancelAndDeleteSoonWithPendingRequest) {
EXPECT_FALSE(job);
client_->Start();
ASSERT_TRUE(job);
- EXPECT_EQ(0, completion_callback_count_);
+ EXPECT_EQ(AutoEnrollmentClient::STATE_PENDING, state_);
// Cancel while a request is in flight.
EXPECT_TRUE(base::MessageLoop::current()->IsIdleForTesting());
@@ -394,7 +412,7 @@ TEST_F(AutoEnrollmentClientTest, CancelAndDeleteSoonWithPendingRequest) {
em::DeviceManagementResponse());
// The DeleteSoon task has been posted:
EXPECT_FALSE(base::MessageLoop::current()->IsIdleForTesting());
- EXPECT_EQ(0, completion_callback_count_);
+ EXPECT_EQ(AutoEnrollmentClient::STATE_PENDING, state_);
}
TEST_F(AutoEnrollmentClientTest, NetworkChangedAfterCancelAndDeleteSoon) {
@@ -403,7 +421,7 @@ TEST_F(AutoEnrollmentClientTest, NetworkChangedAfterCancelAndDeleteSoon) {
EXPECT_FALSE(job);
client_->Start();
ASSERT_TRUE(job);
- EXPECT_EQ(0, completion_callback_count_);
+ EXPECT_EQ(AutoEnrollmentClient::STATE_PENDING, state_);
// Cancel while a request is in flight.
EXPECT_TRUE(base::MessageLoop::current()->IsIdleForTesting());
@@ -413,25 +431,27 @@ TEST_F(AutoEnrollmentClientTest, NetworkChangedAfterCancelAndDeleteSoon) {
// Network change events are ignored while a request is pending.
client->OnNetworkChanged(net::NetworkChangeNotifier::CONNECTION_ETHERNET);
- EXPECT_EQ(0, completion_callback_count_);
+ EXPECT_EQ(AutoEnrollmentClient::STATE_PENDING, state_);
// The client cleans itself up once a reply is received.
job->SendResponse(DM_STATUS_TEMPORARY_UNAVAILABLE,
em::DeviceManagementResponse());
// The DeleteSoon task has been posted:
EXPECT_FALSE(base::MessageLoop::current()->IsIdleForTesting());
- EXPECT_EQ(0, completion_callback_count_);
+ EXPECT_EQ(AutoEnrollmentClient::STATE_PENDING, state_);
// Network changes that have been posted before are also ignored:
client->OnNetworkChanged(net::NetworkChangeNotifier::CONNECTION_ETHERNET);
- EXPECT_EQ(0, completion_callback_count_);
+ EXPECT_EQ(AutoEnrollmentClient::STATE_PENDING, state_);
}
TEST_F(AutoEnrollmentClientTest, CancelAndDeleteSoonAfterCompletion) {
ServerWillReply(-1, true, true);
+ ServerWillSendState(
+ "example.com",
+ em::DeviceStateRetrievalResponse::RESTORE_MODE_REENROLLMENT_ENFORCED);
client_->Start();
- EXPECT_EQ(1, completion_callback_count_);
- EXPECT_TRUE(client_->should_auto_enroll());
+ EXPECT_EQ(AutoEnrollmentClient::STATE_TRIGGER_ENROLLMENT, state_);
// The client will delete itself immediately if there are no pending
// requests.
@@ -443,8 +463,7 @@ TEST_F(AutoEnrollmentClientTest, CancelAndDeleteSoonAfterCompletion) {
TEST_F(AutoEnrollmentClientTest, CancelAndDeleteSoonAfterNetworkFailure) {
ServerWillFail(DM_STATUS_TEMPORARY_UNAVAILABLE);
client_->Start();
- EXPECT_EQ(0, completion_callback_count_);
- EXPECT_FALSE(client_->should_auto_enroll());
+ EXPECT_EQ(AutoEnrollmentClient::STATE_SERVER_ERROR, state_);
// The client will delete itself immediately if there are no pending
// requests.
@@ -460,9 +479,8 @@ TEST_F(AutoEnrollmentClientTest, NetworkFailureThenRequireUpdatedModulus) {
ServerWillFail(DM_STATUS_REQUEST_FAILED);
client_->Start();
- EXPECT_FALSE(client_->should_auto_enroll());
- // Don't invoke the callback if there was a network failure.
- EXPECT_EQ(0, completion_callback_count_);
+ // Callback should signal the connection error.
+ EXPECT_EQ(AutoEnrollmentClient::STATE_CONNECTION_ERROR, state_);
EXPECT_FALSE(HasCachedDecision());
Mock::VerifyAndClearExpectations(service_.get());
@@ -473,15 +491,29 @@ TEST_F(AutoEnrollmentClientTest, NetworkFailureThenRequireUpdatedModulus) {
// Then reply with a valid response and include the hash.
ServerWillReply(-1, true, true);
EXPECT_CALL(*service_, StartJob(_, _, _, _, _, _, _));
+ // State download triggers.
+ ServerWillSendState(
+ "example.com",
+ em::DeviceStateRetrievalResponse::RESTORE_MODE_REENROLLMENT_ENFORCED);
+ EXPECT_CALL(*service_, StartJob(_, _, _, _, _, _, _));
// Trigger a network change event.
client_->OnNetworkChanged(net::NetworkChangeNotifier::CONNECTION_ETHERNET);
- EXPECT_TRUE(client_->should_auto_enroll());
- EXPECT_EQ(1, completion_callback_count_);
+ EXPECT_EQ(AutoEnrollmentClient::STATE_TRIGGER_ENROLLMENT, state_);
EXPECT_TRUE(HasCachedDecision());
Mock::VerifyAndClearExpectations(service_.get());
}
-} // namespace
+TEST_F(AutoEnrollmentClientTest, NoDeviceStateRetrieval) {
+ CreateClient(kStateKey, false, 4, 8);
+ ServerWillReply(-1, true, true);
+ EXPECT_CALL(*service_,
+ CreateJob(DeviceManagementRequestJob::TYPE_DEVICE_STATE_RETRIEVAL,
+ _)).Times(0);
+ client_->Start();
+ EXPECT_EQ(AutoEnrollmentClient::STATE_TRIGGER_ENROLLMENT, state_);
+ VerifyCachedResult(true, 8);
+}
+} // namespace
} // namespace policy
diff --git a/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.cc b/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.cc
index e6a7514..197b279 100644
--- a/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.cc
+++ b/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.cc
@@ -14,6 +14,7 @@
#include "chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.h"
#include "chrome/browser/chromeos/policy/enrollment_handler_chromeos.h"
#include "chrome/browser/chromeos/policy/enterprise_install_attributes.h"
+#include "chrome/browser/chromeos/policy/server_backed_device_state.h"
#include "chrome/common/chrome_content_client.h"
#include "chrome/common/pref_names.h"
#include "chromeos/chromeos_constants.h"
@@ -23,6 +24,7 @@
#include "components/policy/core/common/cloud/device_management_service.h"
#include "components/policy/core/common/cloud/system_policy_request_context.h"
#include "content/public/browser/browser_thread.h"
+#include "crypto/sha2.h"
#include "policy/proto/device_management_backend.pb.h"
#include "url/gurl.h"
@@ -188,6 +190,12 @@ void DeviceCloudPolicyManagerChromeOS::SetDeviceRequisition(
}
bool DeviceCloudPolicyManagerChromeOS::ShouldAutoStartEnrollment() const {
+ std::string restore_mode = GetRestoreMode();
+ if (restore_mode == kDeviceStateRestoreModeReEnrollmentRequested ||
+ restore_mode == kDeviceStateRestoreModeReEnrollmentEnforced) {
+ return true;
+ }
+
if (local_state_->HasPrefPath(prefs::kDeviceEnrollmentAutoStart))
return local_state_->GetBoolean(prefs::kDeviceEnrollmentAutoStart);
@@ -195,6 +203,9 @@ bool DeviceCloudPolicyManagerChromeOS::ShouldAutoStartEnrollment() const {
}
bool DeviceCloudPolicyManagerChromeOS::CanExitEnrollment() const {
+ if (GetRestoreMode() == kDeviceStateRestoreModeReEnrollmentEnforced)
+ return false;
+
if (local_state_->HasPrefPath(prefs::kDeviceEnrollmentCanExit))
return local_state_->GetBoolean(prefs::kDeviceEnrollmentCanExit);
@@ -221,6 +232,7 @@ void DeviceCloudPolicyManagerChromeOS::RegisterPrefs(
std::string());
registry->RegisterBooleanPref(prefs::kDeviceEnrollmentAutoStart, false);
registry->RegisterBooleanPref(prefs::kDeviceEnrollmentCanExit, true);
+ registry->RegisterDictionaryPref(prefs::kServerBackedDeviceState);
}
// static
@@ -247,6 +259,15 @@ std::string DeviceCloudPolicyManagerChromeOS::GetMachineModel() {
return GetMachineStatistic(kMachineInfoSystemHwqual);
}
+// static
+std::string DeviceCloudPolicyManagerChromeOS::GetDeviceStateKey() {
+ // TODO(mnissler): Figure out which stable device identifiers should be used
+ // here and update the code. See http://crbug.com/352599.
+ std::string group_code_key =
+ GetMachineStatistic(chromeos::system::kOffersGroupCodeKey);
+ return crypto::SHA256HashString(group_code_key + GetMachineID());
+}
+
scoped_ptr<CloudPolicyClient> DeviceCloudPolicyManagerChromeOS::CreateClient() {
scoped_refptr<net::URLRequestContextGetter> request_context =
new SystemPolicyRequestContext(
@@ -326,4 +347,12 @@ void DeviceCloudPolicyManagerChromeOS::InitalizeRequisition() {
}
}
+std::string DeviceCloudPolicyManagerChromeOS::GetRestoreMode() const {
+ const base::DictionaryValue* device_state_dict =
+ local_state_->GetDictionary(prefs::kServerBackedDeviceState);
+ std::string restore_mode;
+ device_state_dict->GetString(kDeviceStateRestoreMode, &restore_mode);
+ return restore_mode;
+}
+
} // namespace policy
diff --git a/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h b/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h
index f5f36ed..970587a 100644
--- a/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h
+++ b/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h
@@ -98,6 +98,9 @@ class DeviceCloudPolicyManagerChromeOS : public CloudPolicyManager {
// Returns the machine model, or an empty string if not available.
static std::string GetMachineModel();
+ // Returns the stable device state key.
+ static std::string GetDeviceStateKey();
+
// Returns the robot 'email address' associated with the device robot
// account (sometimes called a service account) associated with this device
// during enterprise enrollment.
@@ -118,6 +121,9 @@ class DeviceCloudPolicyManagerChromeOS : public CloudPolicyManager {
// Initializes requisition settings at OOBE with values from VPD.
void InitalizeRequisition();
+ // Gets the device restore mode as stored in |local_state_|.
+ std::string GetRestoreMode() const;
+
// Points to the same object as the base CloudPolicyManager::store(), but with
// actual device policy specific type.
scoped_ptr<DeviceCloudPolicyStoreChromeOS> device_store_;
diff --git a/chrome/browser/chromeos/policy/server_backed_device_state.cc b/chrome/browser/chromeos/policy/server_backed_device_state.cc
new file mode 100644
index 0000000..2f023a7
--- /dev/null
+++ b/chrome/browser/chromeos/policy/server_backed_device_state.cc
@@ -0,0 +1,17 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/policy/server_backed_device_state.h"
+
+namespace policy {
+
+const char kDeviceStateManagementDomain[] = "management_domain";
+const char kDeviceStateRestoreMode[] = "device_mode";
+
+const char kDeviceStateRestoreModeReEnrollmentRequested[] =
+ "re-enrollment-requested";
+const char kDeviceStateRestoreModeReEnrollmentEnforced[] =
+ "re-enrollment-enforced";
+
+} // namespace policy
diff --git a/chrome/browser/chromeos/policy/server_backed_device_state.h b/chrome/browser/chromeos/policy/server_backed_device_state.h
new file mode 100644
index 0000000..bb6d039
--- /dev/null
+++ b/chrome/browser/chromeos/policy/server_backed_device_state.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_POLICY_SERVER_BACKED_DEVICE_STATE_H_
+#define CHROME_BROWSER_CHROMEOS_POLICY_SERVER_BACKED_DEVICE_STATE_H_
+
+namespace policy {
+
+// Dictionary key constants for prefs::kServerBackedDeviceState.
+extern const char kDeviceStateManagementDomain[];
+extern const char kDeviceStateRestoreMode[];
+
+// Values for kDeviceStateRestoreMode.
+extern const char kDeviceStateRestoreModeReEnrollmentEnforced[];
+extern const char kDeviceStateRestoreModeReEnrollmentRequested[];
+
+} // namespace policy
+
+#endif // CHROME_BROWSER_CHROMEOS_POLICY_SERVER_BACKED_DEVICE_STATE_H_
diff --git a/chrome/browser/policy/test/policy_testserver.py b/chrome/browser/policy/test/policy_testserver.py
index a246eaf..19a2971 100644
--- a/chrome/browser/policy/test/policy_testserver.py
+++ b/chrome/browser/policy/test/policy_testserver.py
@@ -285,18 +285,24 @@ class PolicyRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
len(self.GetUniqueParam('agent')) >= 64):
return (400, 'Invalid request parameter')
if request_type == 'register':
- return self.ProcessRegister(rmsg.register_request)
- if request_type == 'api_authorization':
- return self.ProcessApiAuthorization(rmsg.service_api_access_request)
+ response = self.ProcessRegister(rmsg.register_request)
+ elif request_type == 'api_authorization':
+ response = self.ProcessApiAuthorization(rmsg.service_api_access_request)
elif request_type == 'unregister':
- return self.ProcessUnregister(rmsg.unregister_request)
+ response = self.ProcessUnregister(rmsg.unregister_request)
elif request_type == 'policy' or request_type == 'ping':
- return self.ProcessPolicy(rmsg.policy_request, request_type)
+ response = self.ProcessPolicy(rmsg.policy_request, request_type)
elif request_type == 'enterprise_check':
- return self.ProcessAutoEnrollment(rmsg.auto_enrollment_request)
+ response = self.ProcessAutoEnrollment(rmsg.auto_enrollment_request)
+ elif request_type == 'device_state_retrieval':
+ response = self.ProcessDeviceStateRetrievalRequest(
+ rmsg.device_state_retrieval_request)
else:
return (400, 'Invalid request parameter')
+ self.DumpMessage('Response', response[1])
+ return (response[0], response[1].SerializeToString())
+
def CreatePolicyForExternalPolicyData(self, policy_key):
"""Returns an ExternalPolicyData protobuf for policy_key.
@@ -372,9 +378,7 @@ class PolicyRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
response.register_response.machine_name = token_info['machine_name']
response.register_response.enrollment_type = token_info['enrollment_mode']
- self.DumpMessage('Response', response)
-
- return (200, response.SerializeToString())
+ return (200, response)
def ProcessApiAuthorization(self, msg):
"""Handles an API authorization request.
@@ -392,9 +396,8 @@ class PolicyRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
response = dm.DeviceManagementResponse()
response.service_api_access_response.auth_code = policy.get(
'robot_api_auth_code', 'policy_testserver.py-auth_code')
- self.DumpMessage('Response', response)
- return (200, response.SerializeToString())
+ return (200, response)
def ProcessUnregister(self, msg):
"""Handles a register request.
@@ -420,9 +423,7 @@ class PolicyRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
response = dm.DeviceManagementResponse()
response.unregister_response.CopyFrom(dm.DeviceUnregisterResponse())
- self.DumpMessage('Response', response)
-
- return (200, response.SerializeToString())
+ return (200, response)
def ProcessPolicy(self, msg, request_type):
"""Handles a policy request.
@@ -460,7 +461,7 @@ class PolicyRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
fetch_response.error_code = 400
fetch_response.error_message = 'Invalid policy_type'
- return (200, response.SerializeToString())
+ return (200, response)
def ProcessAutoEnrollment(self, msg):
"""Handles an auto-enrollment check request.
@@ -499,7 +500,30 @@ class PolicyRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
response = dm.DeviceManagementResponse()
response.auto_enrollment_response.CopyFrom(auto_enrollment_response)
- return (200, response.SerializeToString())
+ return (200, response)
+
+ def ProcessDeviceStateRetrievalRequest(self, msg):
+ """Handles a device state retrieval request.
+
+ Response data is taken from server configuration.
+
+ Returns:
+ A tuple of HTTP status code and response data to send to the client.
+ """
+ device_state_retrieval_response = dm.DeviceStateRetrievalResponse()
+ state = self.server.GetPolicies().get('device_state', {})
+ FIELDS = [
+ 'management_domain',
+ 'device_mode',
+ ]
+ for field in FIELDS:
+ if field in state:
+ setattr(device_state_retrieval_response, field, state[field])
+
+ response = dm.DeviceManagementResponse()
+ response.device_state_retrieval_response.CopyFrom(
+ device_state_retrieval_response)
+ return (200, response)
def SetProtobufMessageField(self, group_message, field, field_value):
'''Sets a field in a protobuf message.
@@ -725,8 +749,6 @@ class PolicyRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
response.new_public_key_signature = (
req_key.hashAndSign(response.new_public_key).tostring())
- self.DumpMessage('Response', response)
-
return (200, response.SerializeToString())
def GetSignatureForDomain(self, signatures, username):
diff --git a/chrome/chrome_browser_chromeos.gypi b/chrome/chrome_browser_chromeos.gypi
index c0d37ce..fb3eb19 100644
--- a/chrome/chrome_browser_chromeos.gypi
+++ b/chrome/chrome_browser_chromeos.gypi
@@ -754,6 +754,8 @@
'browser/chromeos/policy/recommendation_restorer.h',
'browser/chromeos/policy/recommendation_restorer_factory.cc',
'browser/chromeos/policy/recommendation_restorer_factory.h',
+ 'browser/chromeos/policy/server_backed_device_state.cc',
+ 'browser/chromeos/policy/server_backed_device_state.h',
'browser/chromeos/policy/user_cloud_external_data_manager.cc',
'browser/chromeos/policy/user_cloud_external_data_manager.h',
'browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc',
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index 90cada9..2d1d044 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -2248,6 +2248,10 @@ const char kDeviceRegistered[] = "DeviceRegistered";
// This is used to prevent these users from joining multiprofile sessions.
const char kUsedPolicyCertificates[] = "policy.used_policy_certificates";
+// A dictionary containing server-provided device state pulled form the cloud
+// after recovery.
+const char kServerBackedDeviceState[] = "server_backed_device_state";
+
#endif
// Whether there is a Flash version installed that supports clearing LSO data.
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index d812f60..3536896 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -769,6 +769,7 @@ extern const char kInitialLocale[];
extern const char kOobeComplete[];
extern const char kDeviceRegistered[];
extern const char kUsedPolicyCertificates[];
+extern const char kServerBackedDeviceState[];
#endif
extern const char kClearPluginLSODataEnabled[];
diff --git a/chromeos/chromeos_switches.cc b/chromeos/chromeos_switches.cc
index 405e982..ef49542 100644
--- a/chromeos/chromeos_switches.cc
+++ b/chromeos/chromeos_switches.cc
@@ -97,6 +97,10 @@ const char kEnableKioskMode[] = "enable-kiosk-mode";
// Enables request of tablet site (via user agent override).
const char kEnableRequestTabletSite[] = "enable-request-tablet-site";
+// Whether to enable forced enterprise re-enrollment.
+const char kEnterpriseEnableForcedReEnrollment[] =
+ "enterprise-enable-forced-re-enrollment";
+
// Power of the power-of-2 initial modulus that will be used by the
// auto-enrollment client. E.g. "4" means the modulus will be 2^4 = 16.
const char kEnterpriseEnrollmentInitialModulus[] =
diff --git a/chromeos/chromeos_switches.h b/chromeos/chromeos_switches.h
index 29c87a3..651e898 100644
--- a/chromeos/chromeos_switches.h
+++ b/chromeos/chromeos_switches.h
@@ -46,6 +46,7 @@ CHROMEOS_EXPORT extern const char kEnableNetworkPortalNotification[];
CHROMEOS_EXPORT extern const char kEnableRequestTabletSite[];
CHROMEOS_EXPORT extern const char kEnableStubInteractive[];
CHROMEOS_EXPORT extern const char kEnableTouchpadThreeFingerClick[];
+CHROMEOS_EXPORT extern const char kEnterpriseEnableForcedReEnrollment[];
CHROMEOS_EXPORT extern const char kEnterpriseEnrollmentInitialModulus[];
CHROMEOS_EXPORT extern const char kEnterpriseEnrollmentModulusLimit[];
CHROMEOS_EXPORT extern const char kEnterpriseEnrollmentSkipRobotAuth[];
diff --git a/components/policy/core/common/cloud/cloud_policy_constants.cc b/components/policy/core/common/cloud/cloud_policy_constants.cc
index 4d2920e..e740677 100644
--- a/components/policy/core/common/cloud/cloud_policy_constants.cc
+++ b/components/policy/core/common/cloud/cloud_policy_constants.cc
@@ -32,6 +32,7 @@ const char kValueRequestRegister[] = "register";
const char kValueRequestApiAuthorization[] = "api_authorization";
const char kValueRequestUnregister[] = "unregister";
const char kValueRequestUploadCertificate[] = "cert_upload";
+const char kValueRequestDeviceStateRetrieval[] = "device_state_retrieval";
const char kValueUserAffiliationManaged[] = "managed";
const char kValueUserAffiliationNone[] = "none";
diff --git a/components/policy/core/common/cloud/cloud_policy_constants.h b/components/policy/core/common/cloud/cloud_policy_constants.h
index bc0f853..4f67f45 100644
--- a/components/policy/core/common/cloud/cloud_policy_constants.h
+++ b/components/policy/core/common/cloud/cloud_policy_constants.h
@@ -34,6 +34,7 @@ POLICY_EXPORT extern const char kValueRequestRegister[];
POLICY_EXPORT extern const char kValueRequestApiAuthorization[];
POLICY_EXPORT extern const char kValueRequestUnregister[];
POLICY_EXPORT extern const char kValueRequestUploadCertificate[];
+POLICY_EXPORT extern const char kValueRequestDeviceStateRetrieval[];
POLICY_EXPORT extern const char kValueUserAffiliationManaged[];
POLICY_EXPORT extern const char kValueUserAffiliationNone[];
diff --git a/components/policy/core/common/cloud/device_management_service.cc b/components/policy/core/common/cloud/device_management_service.cc
index 81cd3e2..95b9c3c 100644
--- a/components/policy/core/common/cloud/device_management_service.cc
+++ b/components/policy/core/common/cloud/device_management_service.cc
@@ -121,6 +121,8 @@ const char* JobTypeToRequestType(DeviceManagementRequestJob::JobType type) {
return dm_protocol::kValueRequestUnregister;
case DeviceManagementRequestJob::TYPE_UPLOAD_CERTIFICATE:
return dm_protocol::kValueRequestUploadCertificate;
+ case DeviceManagementRequestJob::TYPE_DEVICE_STATE_RETRIEVAL:
+ return dm_protocol::kValueRequestDeviceStateRetrieval;
}
NOTREACHED() << "Invalid job type " << type;
return "";
diff --git a/components/policy/core/common/cloud/device_management_service.h b/components/policy/core/common/cloud/device_management_service.h
index 9f124b7..ffc7bfa 100644
--- a/components/policy/core/common/cloud/device_management_service.h
+++ b/components/policy/core/common/cloud/device_management_service.h
@@ -41,6 +41,7 @@ class POLICY_EXPORT DeviceManagementRequestJob {
TYPE_POLICY_FETCH,
TYPE_UNREGISTRATION,
TYPE_UPLOAD_CERTIFICATE,
+ TYPE_DEVICE_STATE_RETRIEVAL,
};
typedef base::Callback<
diff --git a/components/policy/proto/device_management_backend.proto b/components/policy/proto/device_management_backend.proto
index 7233234..17c7ad4 100644
--- a/components/policy/proto/device_management_backend.proto
+++ b/components/policy/proto/device_management_backend.proto
@@ -555,6 +555,42 @@ message DeviceAutoEnrollmentResponse {
repeated bytes hash = 2;
}
+// Sent by the client to the server. The device management server keeps a
+// mapping of device identifiers to device state. Devices query this table after
+// hard reset in order recover state. This request is keyed just by the opaque
+// server-backed state key; there is no further authentication.
+message DeviceStateRetrievalRequest {
+ // Opaque, client-determined, unpredictable, stable and unique device
+ // identifier to retrieve state for. This field contains 32 bytes of data that
+ // looks essentially random to the server. It may be generated e.g. by running
+ // a concatenation of suitable device identifiers through a cryptographic hash
+ // algorithm such as SHA-256.
+ optional bytes server_backed_state_key = 1;
+}
+
+// Server to client message carrying the device state response. Because the
+// request is not authenticated, the only protection against state extraction
+// from server is the unpredictability of the server-backed state ID. Thus, the
+// response should not contain any sensitive data. If the server doesn't know
+// the requested identifier, it just return a message with restore_mode set to
+// RESTORE_MODE_NONE.
+message DeviceStateRetrievalResponse {
+ // Restorative action to take after device reset.
+ enum RestoreMode {
+ // No state restoration.
+ RESTORE_MODE_NONE = 0;
+ // Enterprise enrollment requested, but user may skip.
+ RESTORE_MODE_REENROLLMENT_REQUESTED = 1;
+ // Enterprise enrollment is enforced and cannot be skipped.
+ RESTORE_MODE_REENROLLMENT_ENFORCED = 2;
+ };
+ // The server-indicated restore mode.
+ optional RestoreMode restore_mode = 1 [default = RESTORE_MODE_NONE];
+
+ // Primary domain the device is associated with.
+ optional string management_domain = 2;
+}
+
// Request from the DMAgent on the device to the DMServer. This is
// container for all requests from device to server. The overall HTTP
// request MUST be in the following format:
@@ -565,6 +601,7 @@ message DeviceAutoEnrollmentResponse {
// * request: MUST BE one of
// * cert_upload
// * enterprise_check
+// * device_state_retrieval
// * ping
// * policy
// * register
@@ -592,6 +629,7 @@ message DeviceAutoEnrollmentResponse {
// container will be ignored.
// cert_upload: cert_upload_request
// enterprise_check: auto_enrollment_request
+// device_state_retrieval: device_state_retrieval_request
// ping: policy_request
// policy: policy_request
// register: register_request
@@ -621,6 +659,9 @@ message DeviceManagementRequest {
// Request for OAuth2 authorization codes to access Google services.
optional DeviceServiceApiAccessRequest service_api_access_request = 8;
+
+ // Device-state retrieval.
+ optional DeviceStateRetrievalRequest device_state_retrieval_request = 9;
}
// Response from server to device.
@@ -666,4 +707,7 @@ message DeviceManagementResponse {
// Response to OAuth2 authorization code request.
optional DeviceServiceApiAccessResponse service_api_access_response = 10;
+
+ // Device-state retrieval.
+ optional DeviceStateRetrievalResponse device_state_retrieval_response = 11;
}