| // Copyright (c) 2012 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/auto_enrollment_client.h" |
| |
| #include <stdint.h> |
| |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #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/browser/chromeos/policy/server_backed_device_state.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/test/base/scoped_testing_local_state.h" |
| #include "chrome/test/base/testing_browser_process.h" |
| #include "components/policy/core/common/cloud/mock_device_management_service.h" |
| #include "content/public/test/test_browser_thread_bundle.h" |
| #include "crypto/sha2.h" |
| #include "net/url_request/url_request_context_getter.h" |
| #include "net/url_request/url_request_test_util.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace em = enterprise_management; |
| |
| namespace policy { |
| |
| namespace { |
| |
| 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"; |
| const char kDisabledMessage[] = "This device has been disabled."; |
| |
| using ::testing::InSequence; |
| using ::testing::Mock; |
| using ::testing::SaveArg; |
| using ::testing::_; |
| |
| class AutoEnrollmentClientTest : public testing::Test { |
| protected: |
| AutoEnrollmentClientTest() |
| : scoped_testing_local_state_( |
| TestingBrowserProcess::GetGlobal()), |
| local_state_(scoped_testing_local_state_.Get()), |
| state_(AUTO_ENROLLMENT_STATE_PENDING) {} |
| |
| void SetUp() override { |
| CreateClient(kStateKey, 4, 8); |
| ASSERT_FALSE(local_state_->GetUserPref(prefs::kShouldAutoEnroll)); |
| ASSERT_FALSE(local_state_->GetUserPref(prefs::kAutoEnrollmentPowerLimit)); |
| } |
| |
| void TearDown() override { |
| // Flush any deletion tasks. |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| void CreateClient(const std::string& state_key, |
| int power_initial, |
| int power_limit) { |
| state_ = AUTO_ENROLLMENT_STATE_PENDING; |
| service_.reset(new MockDeviceManagementService()); |
| EXPECT_CALL(*service_, StartJob(_, _, _, _, _, _)) |
| .WillRepeatedly(SaveArg<5>(&last_request_)); |
| client_.reset(new AutoEnrollmentClient( |
| base::Bind(&AutoEnrollmentClientTest::ProgressCallback, |
| base::Unretained(this)), |
| service_.get(), local_state_, |
| new net::TestURLRequestContextGetter( |
| base::MessageLoop::current()->task_runner()), |
| state_key, power_initial, power_limit)); |
| } |
| |
| void ProgressCallback(AutoEnrollmentState state) { |
| state_ = state; |
| } |
| |
| void ServerWillFail(DeviceManagementStatus error) { |
| em::DeviceManagementResponse dummy_response; |
| EXPECT_CALL(*service_, |
| CreateJob(DeviceManagementRequestJob::TYPE_AUTO_ENROLLMENT, _)) |
| .WillOnce(service_->FailJob(error)); |
| } |
| |
| void ServerWillReply(int64 modulus, bool with_hashes, bool with_id_hash) { |
| em::DeviceManagementResponse response; |
| em::DeviceAutoEnrollmentResponse* enrollment_response = |
| response.mutable_auto_enrollment_response(); |
| if (modulus >= 0) |
| enrollment_response->set_expected_modulus(modulus); |
| if (with_hashes) { |
| for (int i = 0; i < 10; ++i) { |
| std::string state_key = base::StringPrintf("state_key %d", i); |
| std::string hash = crypto::SHA256HashString(state_key); |
| enrollment_response->mutable_hash()->Add()->assign(hash); |
| } |
| } |
| 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, |
| const std::string& device_disabled_message) { |
| 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); |
| state_response->mutable_disabled_state()->set_message( |
| device_disabled_message); |
| 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, _)) |
| .WillOnce(service_->CreateAsyncJob(job)); |
| } |
| |
| bool HasCachedDecision() { |
| return local_state_->GetUserPref(prefs::kShouldAutoEnroll); |
| } |
| |
| void VerifyCachedResult(bool should_enroll, int power_limit) { |
| base::FundamentalValue value_should_enroll(should_enroll); |
| base::FundamentalValue value_power_limit(power_limit); |
| EXPECT_TRUE(base::Value::Equals( |
| &value_should_enroll, |
| local_state_->GetUserPref(prefs::kShouldAutoEnroll))); |
| EXPECT_TRUE(base::Value::Equals( |
| &value_power_limit, |
| local_state_->GetUserPref(prefs::kAutoEnrollmentPowerLimit))); |
| } |
| |
| bool HasServerBackedState() { |
| return local_state_->GetUserPref(prefs::kServerBackedDeviceState); |
| } |
| |
| void VerifyServerBackedState( |
| const std::string& expected_management_domain, |
| const std::string& expected_restore_mode, |
| const std::string& expected_disabled_message) { |
| const base::Value* state = |
| local_state_->GetUserPref(prefs::kServerBackedDeviceState); |
| ASSERT_TRUE(state); |
| const base::DictionaryValue* state_dict = nullptr; |
| ASSERT_TRUE(state->GetAsDictionary(&state_dict)); |
| |
| std::string actual_management_domain; |
| EXPECT_TRUE(state_dict->GetString(kDeviceStateManagementDomain, |
| &actual_management_domain)); |
| EXPECT_EQ(expected_management_domain, actual_management_domain); |
| |
| if (!expected_restore_mode.empty()) { |
| std::string actual_restore_mode; |
| EXPECT_TRUE(state_dict->GetString(kDeviceStateRestoreMode, |
| &actual_restore_mode)); |
| EXPECT_EQ(expected_restore_mode, actual_restore_mode); |
| } else { |
| EXPECT_FALSE(state_dict->HasKey(kDeviceStateRestoreMode)); |
| } |
| |
| std::string actual_disabled_message; |
| EXPECT_TRUE(state_dict->GetString(kDeviceStateDisabledMessage, |
| &actual_disabled_message)); |
| EXPECT_EQ(expected_disabled_message, actual_disabled_message); |
| } |
| |
| const em::DeviceAutoEnrollmentRequest& auto_enrollment_request() { |
| return last_request_.auto_enrollment_request(); |
| } |
| |
| content::TestBrowserThreadBundle browser_threads_; |
| ScopedTestingLocalState scoped_testing_local_state_; |
| TestingPrefServiceSimple* local_state_; |
| scoped_ptr<MockDeviceManagementService> service_; |
| scoped_ptr<AutoEnrollmentClient> client_; |
| em::DeviceManagementRequest last_request_; |
| AutoEnrollmentState state_; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(AutoEnrollmentClientTest); |
| }; |
| |
| TEST_F(AutoEnrollmentClientTest, NetworkFailure) { |
| ServerWillFail(DM_STATUS_TEMPORARY_UNAVAILABLE); |
| client_->Start(); |
| EXPECT_EQ(AUTO_ENROLLMENT_STATE_SERVER_ERROR, state_); |
| EXPECT_FALSE(HasCachedDecision()); |
| EXPECT_FALSE(HasServerBackedState()); |
| } |
| |
| TEST_F(AutoEnrollmentClientTest, EmptyReply) { |
| ServerWillReply(-1, false, false); |
| client_->Start(); |
| EXPECT_EQ(AUTO_ENROLLMENT_STATE_NO_ENROLLMENT, state_); |
| VerifyCachedResult(false, 8); |
| EXPECT_FALSE(HasServerBackedState()); |
| } |
| |
| TEST_F(AutoEnrollmentClientTest, ClientUploadsRightBits) { |
| ServerWillReply(-1, false, false); |
| client_->Start(); |
| EXPECT_EQ(AUTO_ENROLLMENT_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(kStateKeyHash[31] & 0xf, auto_enrollment_request().remainder()); |
| VerifyCachedResult(false, 8); |
| EXPECT_FALSE(HasServerBackedState()); |
| } |
| |
| TEST_F(AutoEnrollmentClientTest, AskForMoreThenFail) { |
| InSequence sequence; |
| ServerWillReply(32, false, false); |
| ServerWillFail(DM_STATUS_TEMPORARY_UNAVAILABLE); |
| client_->Start(); |
| EXPECT_EQ(AUTO_ENROLLMENT_STATE_SERVER_ERROR, state_); |
| EXPECT_FALSE(HasCachedDecision()); |
| EXPECT_FALSE(HasServerBackedState()); |
| } |
| |
| TEST_F(AutoEnrollmentClientTest, AskForMoreThenEvenMore) { |
| InSequence sequence; |
| ServerWillReply(32, false, false); |
| ServerWillReply(64, false, false); |
| client_->Start(); |
| EXPECT_EQ(AUTO_ENROLLMENT_STATE_SERVER_ERROR, state_); |
| EXPECT_FALSE(HasCachedDecision()); |
| EXPECT_FALSE(HasServerBackedState()); |
| } |
| |
| TEST_F(AutoEnrollmentClientTest, AskForLess) { |
| InSequence sequence; |
| ServerWillReply(8, false, false); |
| ServerWillReply(-1, true, true); |
| ServerWillSendState( |
| "example.com", |
| em::DeviceStateRetrievalResponse::RESTORE_MODE_REENROLLMENT_ENFORCED, |
| kDisabledMessage); |
| client_->Start(); |
| EXPECT_EQ(AUTO_ENROLLMENT_STATE_TRIGGER_ENROLLMENT, state_); |
| VerifyCachedResult(true, 8); |
| VerifyServerBackedState("example.com", |
| kDeviceStateRestoreModeReEnrollmentEnforced, |
| kDisabledMessage); |
| } |
| |
| TEST_F(AutoEnrollmentClientTest, AskForSame) { |
| InSequence sequence; |
| ServerWillReply(16, false, false); |
| ServerWillReply(-1, true, true); |
| ServerWillSendState( |
| "example.com", |
| em::DeviceStateRetrievalResponse::RESTORE_MODE_REENROLLMENT_ENFORCED, |
| kDisabledMessage); |
| client_->Start(); |
| EXPECT_EQ(AUTO_ENROLLMENT_STATE_TRIGGER_ENROLLMENT, state_); |
| VerifyCachedResult(true, 8); |
| VerifyServerBackedState("example.com", |
| kDeviceStateRestoreModeReEnrollmentEnforced, |
| kDisabledMessage); |
| } |
| |
| TEST_F(AutoEnrollmentClientTest, AskForSameTwice) { |
| InSequence sequence; |
| ServerWillReply(16, false, false); |
| ServerWillReply(16, false, false); |
| client_->Start(); |
| EXPECT_EQ(AUTO_ENROLLMENT_STATE_SERVER_ERROR, state_); |
| EXPECT_FALSE(HasCachedDecision()); |
| EXPECT_FALSE(HasServerBackedState()); |
| } |
| |
| TEST_F(AutoEnrollmentClientTest, AskForTooMuch) { |
| ServerWillReply(512, false, false); |
| client_->Start(); |
| EXPECT_EQ(AUTO_ENROLLMENT_STATE_SERVER_ERROR, state_); |
| EXPECT_FALSE(HasCachedDecision()); |
| EXPECT_FALSE(HasServerBackedState()); |
| } |
| |
| TEST_F(AutoEnrollmentClientTest, AskNonPowerOf2) { |
| InSequence sequence; |
| ServerWillReply(100, false, false); |
| ServerWillReply(-1, false, false); |
| client_->Start(); |
| EXPECT_EQ(AUTO_ENROLLMENT_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(kStateKeyHash[31] & 0x7f, auto_enrollment_request().remainder()); |
| VerifyCachedResult(false, 8); |
| EXPECT_FALSE(HasServerBackedState()); |
| } |
| |
| TEST_F(AutoEnrollmentClientTest, ConsumerDevice) { |
| ServerWillReply(-1, true, false); |
| client_->Start(); |
| EXPECT_EQ(AUTO_ENROLLMENT_STATE_NO_ENROLLMENT, state_); |
| VerifyCachedResult(false, 8); |
| EXPECT_FALSE(HasServerBackedState()); |
| |
| // Network changes don't trigger retries after obtaining a response from |
| // the server. |
| client_->OnNetworkChanged(net::NetworkChangeNotifier::CONNECTION_ETHERNET); |
| EXPECT_EQ(AUTO_ENROLLMENT_STATE_NO_ENROLLMENT, state_); |
| } |
| |
| TEST_F(AutoEnrollmentClientTest, ForcedReEnrollment) { |
| ServerWillReply(-1, true, true); |
| ServerWillSendState( |
| "example.com", |
| em::DeviceStateRetrievalResponse::RESTORE_MODE_REENROLLMENT_ENFORCED, |
| kDisabledMessage); |
| client_->Start(); |
| EXPECT_EQ(AUTO_ENROLLMENT_STATE_TRIGGER_ENROLLMENT, state_); |
| VerifyCachedResult(true, 8); |
| VerifyServerBackedState("example.com", |
| kDeviceStateRestoreModeReEnrollmentEnforced, |
| kDisabledMessage); |
| |
| // Network changes don't trigger retries after obtaining a response from |
| // the server. |
| client_->OnNetworkChanged(net::NetworkChangeNotifier::CONNECTION_ETHERNET); |
| EXPECT_EQ(AUTO_ENROLLMENT_STATE_TRIGGER_ENROLLMENT, state_); |
| } |
| |
| TEST_F(AutoEnrollmentClientTest, RequestedReEnrollment) { |
| ServerWillReply(-1, true, true); |
| ServerWillSendState( |
| "example.com", |
| em::DeviceStateRetrievalResponse::RESTORE_MODE_REENROLLMENT_REQUESTED, |
| kDisabledMessage); |
| client_->Start(); |
| EXPECT_EQ(AUTO_ENROLLMENT_STATE_TRIGGER_ENROLLMENT, state_); |
| VerifyCachedResult(true, 8); |
| VerifyServerBackedState("example.com", |
| kDeviceStateRestoreModeReEnrollmentRequested, |
| kDisabledMessage); |
| } |
| |
| TEST_F(AutoEnrollmentClientTest, DeviceDisabled) { |
| ServerWillReply(-1, true, true); |
| ServerWillSendState( |
| "example.com", |
| em::DeviceStateRetrievalResponse::RESTORE_MODE_DISABLED, |
| kDisabledMessage); |
| client_->Start(); |
| EXPECT_EQ(AUTO_ENROLLMENT_STATE_NO_ENROLLMENT, state_); |
| VerifyCachedResult(true, 8); |
| VerifyServerBackedState("example.com", |
| kDeviceStateRestoreModeDisabled, |
| kDisabledMessage); |
| } |
| |
| TEST_F(AutoEnrollmentClientTest, NoBitsUploaded) { |
| CreateClient(kStateKey, 0, 0); |
| ServerWillReply(-1, false, false); |
| client_->Start(); |
| EXPECT_EQ(AUTO_ENROLLMENT_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()); |
| EXPECT_EQ(0, auto_enrollment_request().remainder()); |
| VerifyCachedResult(false, 0); |
| EXPECT_FALSE(HasServerBackedState()); |
| } |
| |
| TEST_F(AutoEnrollmentClientTest, ManyBitsUploaded) { |
| int64 bottom62 = INT64_C(0x386e7244d097c3e6); |
| for (int i = 0; i <= 62; ++i) { |
| CreateClient(kStateKey, i, i); |
| ServerWillReply(-1, false, false); |
| client_->Start(); |
| EXPECT_EQ(AUTO_ENROLLMENT_STATE_NO_ENROLLMENT, state_); |
| EXPECT_TRUE(auto_enrollment_request().has_remainder()); |
| EXPECT_TRUE(auto_enrollment_request().has_modulus()); |
| EXPECT_EQ(INT64_C(1) << i, auto_enrollment_request().modulus()); |
| EXPECT_EQ(bottom62 % (INT64_C(1) << i), |
| auto_enrollment_request().remainder()); |
| VerifyCachedResult(false, i); |
| EXPECT_FALSE(HasServerBackedState()); |
| } |
| } |
| |
| TEST_F(AutoEnrollmentClientTest, MoreThan32BitsUploaded) { |
| CreateClient(kStateKey, 10, 37); |
| InSequence sequence; |
| ServerWillReply(INT64_C(1) << 37, false, false); |
| ServerWillReply(-1, true, true); |
| ServerWillSendState( |
| "example.com", |
| em::DeviceStateRetrievalResponse::RESTORE_MODE_REENROLLMENT_ENFORCED, |
| kDisabledMessage); |
| client_->Start(); |
| EXPECT_EQ(AUTO_ENROLLMENT_STATE_TRIGGER_ENROLLMENT, state_); |
| VerifyCachedResult(true, 37); |
| VerifyServerBackedState("example.com", |
| kDeviceStateRestoreModeReEnrollmentEnforced, |
| kDisabledMessage); |
| } |
| |
| TEST_F(AutoEnrollmentClientTest, ReuseCachedDecision) { |
| // No bucket download requests should be issued. |
| EXPECT_CALL(*service_, |
| CreateJob(DeviceManagementRequestJob::TYPE_AUTO_ENROLLMENT, _)) |
| .Times(0); |
| local_state_->SetUserPref(prefs::kShouldAutoEnroll, |
| new base::FundamentalValue(true)); |
| local_state_->SetUserPref(prefs::kAutoEnrollmentPowerLimit, |
| new base::FundamentalValue(8)); |
| |
| // Note that device state will be retrieved every time, regardless of any |
| // cached information. This is intentional, the idea is that device state on |
| // the server may change. |
| ServerWillSendState( |
| "example.com", |
| em::DeviceStateRetrievalResponse::RESTORE_MODE_REENROLLMENT_ENFORCED, |
| kDisabledMessage); |
| |
| client_->Start(); |
| EXPECT_EQ(AUTO_ENROLLMENT_STATE_TRIGGER_ENROLLMENT, state_); |
| VerifyServerBackedState("example.com", |
| kDeviceStateRestoreModeReEnrollmentEnforced, |
| kDisabledMessage); |
| } |
| |
| TEST_F(AutoEnrollmentClientTest, RetryIfPowerLargerThanCached) { |
| local_state_->SetUserPref(prefs::kShouldAutoEnroll, |
| new base::FundamentalValue(false)); |
| local_state_->SetUserPref(prefs::kAutoEnrollmentPowerLimit, |
| new base::FundamentalValue(8)); |
| CreateClient(kStateKey, 5, 10); |
| ServerWillReply(-1, true, true); |
| ServerWillSendState( |
| "example.com", |
| em::DeviceStateRetrievalResponse::RESTORE_MODE_REENROLLMENT_ENFORCED, |
| kDisabledMessage); |
| client_->Start(); |
| EXPECT_EQ(AUTO_ENROLLMENT_STATE_TRIGGER_ENROLLMENT, state_); |
| VerifyServerBackedState("example.com", |
| kDeviceStateRestoreModeReEnrollmentEnforced, |
| kDisabledMessage); |
| } |
| |
| TEST_F(AutoEnrollmentClientTest, NetworkChangeRetryAfterErrors) { |
| ServerWillFail(DM_STATUS_TEMPORARY_UNAVAILABLE); |
| client_->Start(); |
| // Don't invoke the callback if there was a network failure. |
| EXPECT_EQ(AUTO_ENROLLMENT_STATE_SERVER_ERROR, state_); |
| EXPECT_FALSE(HasCachedDecision()); |
| EXPECT_FALSE(HasServerBackedState()); |
| |
| // The client doesn't retry if no new connection became available. |
| client_->OnNetworkChanged(net::NetworkChangeNotifier::CONNECTION_NONE); |
| EXPECT_EQ(AUTO_ENROLLMENT_STATE_SERVER_ERROR, state_); |
| EXPECT_FALSE(HasCachedDecision()); |
| EXPECT_FALSE(HasServerBackedState()); |
| |
| // Retry once the network is back. |
| ServerWillReply(-1, true, true); |
| ServerWillSendState( |
| "example.com", |
| em::DeviceStateRetrievalResponse::RESTORE_MODE_REENROLLMENT_ENFORCED, |
| kDisabledMessage); |
| client_->OnNetworkChanged(net::NetworkChangeNotifier::CONNECTION_ETHERNET); |
| EXPECT_EQ(AUTO_ENROLLMENT_STATE_TRIGGER_ENROLLMENT, state_); |
| EXPECT_TRUE(HasCachedDecision()); |
| VerifyServerBackedState("example.com", |
| kDeviceStateRestoreModeReEnrollmentEnforced, |
| kDisabledMessage); |
| |
| // Subsequent network changes don't trigger retries. |
| client_->OnNetworkChanged(net::NetworkChangeNotifier::CONNECTION_NONE); |
| client_->OnNetworkChanged(net::NetworkChangeNotifier::CONNECTION_ETHERNET); |
| EXPECT_EQ(AUTO_ENROLLMENT_STATE_TRIGGER_ENROLLMENT, state_); |
| EXPECT_TRUE(HasCachedDecision()); |
| VerifyServerBackedState("example.com", |
| kDeviceStateRestoreModeReEnrollmentEnforced, |
| kDisabledMessage); |
| } |
| |
| TEST_F(AutoEnrollmentClientTest, CancelAndDeleteSoonWithPendingRequest) { |
| MockDeviceManagementJob* job = NULL; |
| ServerWillReplyAsync(&job); |
| EXPECT_FALSE(job); |
| client_->Start(); |
| ASSERT_TRUE(job); |
| EXPECT_EQ(AUTO_ENROLLMENT_STATE_PENDING, state_); |
| |
| // Cancel while a request is in flight. |
| EXPECT_TRUE(base::MessageLoop::current()->IsIdleForTesting()); |
| client_.release()->CancelAndDeleteSoon(); |
| EXPECT_TRUE(base::MessageLoop::current()->IsIdleForTesting()); |
| |
| // 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(AUTO_ENROLLMENT_STATE_PENDING, state_); |
| } |
| |
| TEST_F(AutoEnrollmentClientTest, NetworkChangedAfterCancelAndDeleteSoon) { |
| MockDeviceManagementJob* job = NULL; |
| ServerWillReplyAsync(&job); |
| EXPECT_FALSE(job); |
| client_->Start(); |
| ASSERT_TRUE(job); |
| EXPECT_EQ(AUTO_ENROLLMENT_STATE_PENDING, state_); |
| |
| // Cancel while a request is in flight. |
| EXPECT_TRUE(base::MessageLoop::current()->IsIdleForTesting()); |
| AutoEnrollmentClient* client = client_.release(); |
| client->CancelAndDeleteSoon(); |
| EXPECT_TRUE(base::MessageLoop::current()->IsIdleForTesting()); |
| |
| // Network change events are ignored while a request is pending. |
| client->OnNetworkChanged(net::NetworkChangeNotifier::CONNECTION_ETHERNET); |
| EXPECT_EQ(AUTO_ENROLLMENT_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(AUTO_ENROLLMENT_STATE_PENDING, state_); |
| |
| // Network changes that have been posted before are also ignored: |
| client->OnNetworkChanged(net::NetworkChangeNotifier::CONNECTION_ETHERNET); |
| EXPECT_EQ(AUTO_ENROLLMENT_STATE_PENDING, state_); |
| } |
| |
| TEST_F(AutoEnrollmentClientTest, CancelAndDeleteSoonAfterCompletion) { |
| ServerWillReply(-1, true, true); |
| ServerWillSendState( |
| "example.com", |
| em::DeviceStateRetrievalResponse::RESTORE_MODE_REENROLLMENT_ENFORCED, |
| kDisabledMessage); |
| client_->Start(); |
| EXPECT_EQ(AUTO_ENROLLMENT_STATE_TRIGGER_ENROLLMENT, state_); |
| VerifyServerBackedState("example.com", |
| kDeviceStateRestoreModeReEnrollmentEnforced, |
| kDisabledMessage); |
| |
| // The client will delete itself immediately if there are no pending |
| // requests. |
| EXPECT_TRUE(base::MessageLoop::current()->IsIdleForTesting()); |
| client_.release()->CancelAndDeleteSoon(); |
| EXPECT_TRUE(base::MessageLoop::current()->IsIdleForTesting()); |
| } |
| |
| TEST_F(AutoEnrollmentClientTest, CancelAndDeleteSoonAfterNetworkFailure) { |
| ServerWillFail(DM_STATUS_TEMPORARY_UNAVAILABLE); |
| client_->Start(); |
| EXPECT_EQ(AUTO_ENROLLMENT_STATE_SERVER_ERROR, state_); |
| |
| // The client will delete itself immediately if there are no pending |
| // requests. |
| EXPECT_TRUE(base::MessageLoop::current()->IsIdleForTesting()); |
| client_.release()->CancelAndDeleteSoon(); |
| EXPECT_TRUE(base::MessageLoop::current()->IsIdleForTesting()); |
| } |
| |
| TEST_F(AutoEnrollmentClientTest, NetworkFailureThenRequireUpdatedModulus) { |
| // This test verifies that if the first request fails due to a network |
| // problem then the second request will correctly handle an updated |
| // modulus request from the server. |
| |
| ServerWillFail(DM_STATUS_REQUEST_FAILED); |
| client_->Start(); |
| // Callback should signal the connection error. |
| EXPECT_EQ(AUTO_ENROLLMENT_STATE_CONNECTION_ERROR, state_); |
| EXPECT_FALSE(HasCachedDecision()); |
| EXPECT_FALSE(HasServerBackedState()); |
| Mock::VerifyAndClearExpectations(service_.get()); |
| |
| InSequence sequence; |
| // The default client uploads 4 bits. Make the server ask for 5. |
| ServerWillReply(1 << 5, false, false); |
| EXPECT_CALL(*service_, StartJob(_, _, _, _, _, _)); |
| // 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, |
| kDisabledMessage); |
| EXPECT_CALL(*service_, StartJob(_, _, _, _, _, _)); |
| |
| // Trigger a network change event. |
| client_->OnNetworkChanged(net::NetworkChangeNotifier::CONNECTION_ETHERNET); |
| EXPECT_EQ(AUTO_ENROLLMENT_STATE_TRIGGER_ENROLLMENT, state_); |
| EXPECT_TRUE(HasCachedDecision()); |
| VerifyServerBackedState("example.com", |
| kDeviceStateRestoreModeReEnrollmentEnforced, |
| kDisabledMessage); |
| Mock::VerifyAndClearExpectations(service_.get()); |
| } |
| |
| } // namespace |
| } // namespace policy |