// 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/login/parallel_authenticator.h" #include #include "base/file_util.h" #include "base/files/file_path.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "chrome/browser/chromeos/cros/cros_library.h" #include "chrome/browser/chromeos/login/mock_login_status_consumer.h" #include "chrome/browser/chromeos/login/mock_url_fetchers.h" #include "chrome/browser/chromeos/login/mock_user_manager.h" #include "chrome/browser/chromeos/login/test_attempt_state.h" #include "chrome/browser/chromeos/login/user.h" #include "chrome/browser/chromeos/login/user_manager.h" #include "chrome/browser/chromeos/settings/cros_settings.h" #include "chrome/browser/chromeos/settings/device_settings_test_helper.h" #include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h" #include "chrome/test/base/testing_profile.h" #include "chromeos/cryptohome/mock_async_method_caller.h" #include "chromeos/cryptohome/mock_cryptohome_library.h" #include "chromeos/dbus/fake_cryptohome_client.h" #include "chromeos/dbus/mock_dbus_thread_manager_without_gmock.h" #include "content/public/test/test_browser_thread.h" #include "google_apis/gaia/mock_url_fetcher_factory.h" #include "googleurl/src/gurl.h" #include "net/base/net_errors.h" #include "net/url_request/url_request_status.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/cros_system_api/dbus/service_constants.h" using ::testing::AnyNumber; using ::testing::DoAll; using ::testing::Invoke; using ::testing::Return; using ::testing::SetArgPointee; using ::testing::_; using content::BrowserThread; namespace chromeos { class TestOnlineAttempt : public OnlineAttempt { public: TestOnlineAttempt(AuthAttemptState* state, AuthAttemptStateResolver* resolver) : OnlineAttempt(state, resolver) { } }; class ParallelAuthenticatorTest : public testing::Test { public: ParallelAuthenticatorTest() : message_loop_(base::MessageLoop::TYPE_UI), ui_thread_(BrowserThread::UI, &message_loop_), file_thread_(BrowserThread::FILE, &message_loop_), io_thread_(BrowserThread::IO), username_("me@nowhere.org"), password_("fakepass"), user_manager_enabler_(new MockUserManager) { hash_ascii_.assign("0a010000000000a0"); hash_ascii_.append(std::string(16, '0')); } virtual ~ParallelAuthenticatorTest() { DCHECK(!mock_caller_); } virtual void SetUp() { mock_caller_ = new cryptohome::MockAsyncMethodCaller; cryptohome::AsyncMethodCaller::InitializeForTesting(mock_caller_); mock_cryptohome_library_ .reset(new MockCryptohomeLibrary()); CryptohomeLibrary::SetForTest(mock_cryptohome_library_.get()); io_thread_.Start(); auth_ = new ParallelAuthenticator(&consumer_); auth_->set_using_oauth(false); state_.reset(new TestAttemptState(UserContext(username_, password_, std::string()), hash_ascii_, "", "", User::USER_TYPE_REGULAR, false)); } // Tears down the test fixture. virtual void TearDown() { CryptohomeLibrary::SetForTest(NULL); cryptohome::AsyncMethodCaller::Shutdown(); mock_caller_ = NULL; } base::FilePath PopulateTempFile(const char* data, int data_len) { base::FilePath out; FILE* tmp_file = file_util::CreateAndOpenTemporaryFile(&out); EXPECT_NE(tmp_file, static_cast(NULL)); EXPECT_EQ(file_util::WriteFile(out, data, data_len), data_len); EXPECT_TRUE(file_util::CloseFile(tmp_file)); return out; } // Allow test to fail and exit gracefully, even if OnLoginFailure() // wasn't supposed to happen. void FailOnLoginFailure() { ON_CALL(consumer_, OnLoginFailure(_)) .WillByDefault(Invoke(MockConsumer::OnFailQuitAndFail)); } // Allow test to fail and exit gracefully, even if // OnRetailModeLoginSuccess() wasn't supposed to happen. void FailOnRetailModeLoginSuccess() { ON_CALL(consumer_, OnRetailModeLoginSuccess(_)) .WillByDefault(Invoke(MockConsumer::OnRetailModeSuccessQuitAndFail)); } // Allow test to fail and exit gracefully, even if OnLoginSuccess() // wasn't supposed to happen. void FailOnLoginSuccess() { ON_CALL(consumer_, OnLoginSuccess(_, _, _)) .WillByDefault(Invoke(MockConsumer::OnSuccessQuitAndFail)); } // Allow test to fail and exit gracefully, even if // OnOffTheRecordLoginSuccess() wasn't supposed to happen. void FailOnGuestLoginSuccess() { ON_CALL(consumer_, OnOffTheRecordLoginSuccess()) .WillByDefault(Invoke(MockConsumer::OnGuestSuccessQuitAndFail)); } void ExpectLoginFailure(const LoginFailure& failure) { EXPECT_CALL(consumer_, OnLoginFailure(failure)) .WillOnce(Invoke(MockConsumer::OnFailQuit)) .RetiresOnSaturation(); } void ExpectRetailModeLoginSuccess() { EXPECT_CALL(consumer_, OnRetailModeLoginSuccess(_)) .WillOnce(Invoke(MockConsumer::OnRetailModeSuccessQuit)) .RetiresOnSaturation(); } void ExpectLoginSuccess(const std::string& username, const std::string& password, const std::string& username_hash_, bool pending) { EXPECT_CALL(consumer_, OnLoginSuccess(UserContext(username, password, std::string(), username_hash_), pending, false)) .WillOnce(Invoke(MockConsumer::OnSuccessQuit)) .RetiresOnSaturation(); } void ExpectGuestLoginSuccess() { EXPECT_CALL(consumer_, OnOffTheRecordLoginSuccess()) .WillOnce(Invoke(MockConsumer::OnGuestSuccessQuit)) .RetiresOnSaturation(); } void ExpectPasswordChange() { EXPECT_CALL(consumer_, OnPasswordChangeDetected()) .WillOnce(Invoke(MockConsumer::OnMigrateQuit)) .RetiresOnSaturation(); } void RunResolve(ParallelAuthenticator* auth) { auth->Resolve(); message_loop_.RunUntilIdle(); } void SetAttemptState(ParallelAuthenticator* auth, TestAttemptState* state) { auth->set_attempt_state(state); } ParallelAuthenticator::AuthState SetAndResolveState( ParallelAuthenticator* auth, TestAttemptState* state) { auth->set_attempt_state(state); return auth->ResolveState(); } void SetOwnerState(bool owner_check_finished, bool check_result) { auth_->SetOwnerState(owner_check_finished, check_result); } void FakeOnlineAttempt() { auth_->set_online_attempt(new TestOnlineAttempt(state_.get(), auth_.get())); } base::MessageLoop message_loop_; content::TestBrowserThread ui_thread_; content::TestBrowserThread file_thread_; content::TestBrowserThread io_thread_; std::string username_; std::string password_; std::string username_hash_; std::string hash_ascii_; ScopedStubCrosEnabler stub_cros_enabler_; ScopedDeviceSettingsTestHelper device_settings_test_helper_; ScopedTestCrosSettings test_cros_settings_; // Mocks, destroyed by CrosLibrary class. ScopedUserManagerEnabler user_manager_enabler_; scoped_ptr mock_cryptohome_library_; cryptohome::MockAsyncMethodCaller* mock_caller_; MockConsumer consumer_; scoped_refptr auth_; scoped_ptr state_; }; TEST_F(ParallelAuthenticatorTest, OnLoginSuccess) { EXPECT_CALL(consumer_, OnLoginSuccess(UserContext(username_, password_, std::string(), username_hash_), false, false)) .Times(1) .RetiresOnSaturation(); SetAttemptState(auth_.get(), state_.release()); auth_->OnLoginSuccess(false); } TEST_F(ParallelAuthenticatorTest, OnPasswordChangeDetected) { EXPECT_CALL(consumer_, OnPasswordChangeDetected()) .Times(1) .RetiresOnSaturation(); SetAttemptState(auth_.get(), state_.release()); auth_->OnPasswordChangeDetected(); } TEST_F(ParallelAuthenticatorTest, ResolveNothingDone) { EXPECT_EQ(ParallelAuthenticator::CONTINUE, SetAndResolveState(auth_.get(), state_.release())); } TEST_F(ParallelAuthenticatorTest, ResolvePossiblePwChange) { // Set a fake online attempt so that we return intermediate cryptohome state. FakeOnlineAttempt(); // Set up state as though a cryptohome mount attempt has occurred // and been rejected. state_->PresetCryptohomeStatus(false, cryptohome::MOUNT_ERROR_KEY_FAILURE); EXPECT_EQ(ParallelAuthenticator::POSSIBLE_PW_CHANGE, SetAndResolveState(auth_.get(), state_.release())); } TEST_F(ParallelAuthenticatorTest, ResolvePossiblePwChangeToFailedMount) { // Set up state as though a cryptohome mount attempt has occurred // and been rejected. state_->PresetCryptohomeStatus(false, cryptohome::MOUNT_ERROR_KEY_FAILURE); // When there is no online attempt and online results, POSSIBLE_PW_CHANGE EXPECT_EQ(ParallelAuthenticator::FAILED_MOUNT, SetAndResolveState(auth_.get(), state_.release())); } TEST_F(ParallelAuthenticatorTest, ResolveNeedOldPw) { // Set up state as though a cryptohome mount attempt has occurred // and been rejected because of unmatched key; additionally, // an online auth attempt has completed successfully. state_->PresetCryptohomeStatus(false, cryptohome::MOUNT_ERROR_KEY_FAILURE); state_->PresetOnlineLoginStatus(LoginFailure::LoginFailureNone()); EXPECT_EQ(ParallelAuthenticator::NEED_OLD_PW, SetAndResolveState(auth_.get(), state_.release())); } TEST_F(ParallelAuthenticatorTest, ResolveOwnerNeededDirectFailedMount) { // Set up state as though a cryptohome mount attempt has occurred // and succeeded but we are in safe mode and the current user is not owner. // This is a high level test to verify the proper transitioning in this mode // only. It is not testing that we properly verify that the user is an owner // or that we really are in "safe-mode". state_->PresetCryptohomeStatus(true, cryptohome::MOUNT_ERROR_NONE); SetOwnerState(true, false); EXPECT_EQ(ParallelAuthenticator::OWNER_REQUIRED, SetAndResolveState(auth_.get(), state_.release())); } TEST_F(ParallelAuthenticatorTest, ResolveOwnerNeededMount) { // Set up state as though a cryptohome mount attempt has occurred // and succeeded but we are in safe mode and the current user is not owner. // This test will check that the "safe-mode" policy is not set and will let // the mount finish successfully. state_->PresetCryptohomeStatus(true, cryptohome::MOUNT_ERROR_NONE); SetOwnerState(false, false); // and test that the mount has succeeded. state_.reset(new TestAttemptState(UserContext(username_, password_, std::string()), hash_ascii_, "", "", User::USER_TYPE_REGULAR, false)); state_->PresetCryptohomeStatus(true, cryptohome::MOUNT_ERROR_NONE); EXPECT_EQ(ParallelAuthenticator::OFFLINE_LOGIN, SetAndResolveState(auth_.get(), state_.release())); } TEST_F(ParallelAuthenticatorTest, ResolveOwnerNeededFailedMount) { FailOnLoginSuccess(); // Set failing on success as the default... LoginFailure failure = LoginFailure(LoginFailure::OWNER_REQUIRED); ExpectLoginFailure(failure); MockDBusThreadManagerWithoutGMock* mock_dbus_thread_manager = new MockDBusThreadManagerWithoutGMock; DBusThreadManager::InitializeForTesting(mock_dbus_thread_manager); FakeCryptohomeClient* fake_cryptohome_client = mock_dbus_thread_manager->fake_cryptohome_client(); fake_cryptohome_client->set_unmount_result(true); CrosSettingsProvider* device_settings_provider; StubCrosSettingsProvider stub_settings_provider; // Set up state as though a cryptohome mount attempt has occurred // and succeeded but we are in safe mode and the current user is not owner. state_->PresetCryptohomeStatus(true, cryptohome::MOUNT_ERROR_NONE); SetOwnerState(false, false); // Remove the real DeviceSettingsProvider and replace it with a stub. device_settings_provider = CrosSettings::Get()->GetProvider(chromeos::kReportDeviceVersionInfo); EXPECT_TRUE(device_settings_provider != NULL); EXPECT_TRUE( CrosSettings::Get()->RemoveSettingsProvider(device_settings_provider)); CrosSettings::Get()->AddSettingsProvider(&stub_settings_provider); CrosSettings::Get()->SetBoolean(kPolicyMissingMitigationMode, true); EXPECT_EQ(ParallelAuthenticator::CONTINUE, SetAndResolveState(auth_.get(), state_.release())); // Let the owner verification run. device_settings_test_helper_.Flush(); // and test that the mount has succeeded. state_.reset(new TestAttemptState(UserContext(username_, password_, std::string()), hash_ascii_, "", "", User::USER_TYPE_REGULAR, false)); state_->PresetCryptohomeStatus(true, cryptohome::MOUNT_ERROR_NONE); EXPECT_EQ(ParallelAuthenticator::OWNER_REQUIRED, SetAndResolveState(auth_.get(), state_.release())); EXPECT_TRUE( CrosSettings::Get()->RemoveSettingsProvider(&stub_settings_provider)); CrosSettings::Get()->AddSettingsProvider(device_settings_provider); DBusThreadManager::Get()->Shutdown(); } TEST_F(ParallelAuthenticatorTest, DriveFailedMount) { FailOnLoginSuccess(); ExpectLoginFailure(LoginFailure(LoginFailure::COULD_NOT_MOUNT_CRYPTOHOME)); // Set up state as though a cryptohome mount attempt has occurred // and failed. state_->PresetCryptohomeStatus(false, cryptohome::MOUNT_ERROR_NONE); SetAttemptState(auth_.get(), state_.release()); RunResolve(auth_.get()); } TEST_F(ParallelAuthenticatorTest, DriveGuestLogin) { ExpectGuestLoginSuccess(); FailOnLoginFailure(); // Set up mock cryptohome library to respond as though a tmpfs mount // attempt has occurred and succeeded. mock_caller_->SetUp(true, cryptohome::MOUNT_ERROR_NONE); EXPECT_CALL(*mock_caller_, AsyncMountGuest(_)) .Times(1) .RetiresOnSaturation(); auth_->LoginOffTheRecord(); message_loop_.Run(); } TEST_F(ParallelAuthenticatorTest, DriveGuestLoginButFail) { FailOnGuestLoginSuccess(); ExpectLoginFailure(LoginFailure(LoginFailure::COULD_NOT_MOUNT_TMPFS)); // Set up mock cryptohome library to respond as though a tmpfs mount // attempt has occurred and failed. mock_caller_->SetUp(false, cryptohome::MOUNT_ERROR_NONE); EXPECT_CALL(*mock_caller_, AsyncMountGuest(_)) .Times(1) .RetiresOnSaturation(); auth_->LoginOffTheRecord(); message_loop_.Run(); } TEST_F(ParallelAuthenticatorTest, DriveRetailModeUserLogin) { ExpectRetailModeLoginSuccess(); FailOnLoginFailure(); // Set up mock cryptohome library to respond as though a tmpfs mount // attempt has occurred and succeeded. mock_caller_->SetUp(true, cryptohome::MOUNT_ERROR_NONE); EXPECT_CALL(*mock_caller_, AsyncMountGuest(_)) .Times(1) .RetiresOnSaturation(); auth_->LoginRetailMode(); message_loop_.Run(); } TEST_F(ParallelAuthenticatorTest, DriveRetailModeLoginButFail) { FailOnRetailModeLoginSuccess(); ExpectLoginFailure(LoginFailure(LoginFailure::COULD_NOT_MOUNT_TMPFS)); // Set up mock cryptohome library to respond as though a tmpfs mount // attempt has occurred and failed. mock_caller_->SetUp(false, cryptohome::MOUNT_ERROR_NONE); EXPECT_CALL(*mock_caller_, AsyncMountGuest(_)) .Times(1) .RetiresOnSaturation(); auth_->LoginRetailMode(); message_loop_.Run(); } TEST_F(ParallelAuthenticatorTest, DriveDataResync) { ExpectLoginSuccess(username_, password_, cryptohome::MockAsyncMethodCaller::kFakeSanitizedUsername, false); FailOnLoginFailure(); // Set up mock cryptohome library to respond successfully to a cryptohome // remove attempt and a cryptohome create attempt (indicated by the // |CREATE_IF_MISSING| flag to AsyncMount). mock_caller_->SetUp(true, cryptohome::MOUNT_ERROR_NONE); EXPECT_CALL(*mock_caller_, AsyncRemove(username_, _)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*mock_caller_, AsyncMount(username_, hash_ascii_, cryptohome::CREATE_IF_MISSING, _)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*mock_caller_, AsyncGetSanitizedUsername(username_, _)) .Times(1) .RetiresOnSaturation(); state_->PresetOnlineLoginStatus(LoginFailure::LoginFailureNone()); SetAttemptState(auth_.get(), state_.release()); auth_->ResyncEncryptedData(); message_loop_.Run(); } TEST_F(ParallelAuthenticatorTest, DriveResyncFail) { FailOnLoginSuccess(); ExpectLoginFailure(LoginFailure(LoginFailure::DATA_REMOVAL_FAILED)); // Set up mock cryptohome library to fail a cryptohome remove attempt. mock_caller_->SetUp(false, cryptohome::MOUNT_ERROR_NONE); EXPECT_CALL(*mock_caller_, AsyncRemove(username_, _)) .Times(1) .RetiresOnSaturation(); SetAttemptState(auth_.get(), state_.release()); auth_->ResyncEncryptedData(); message_loop_.Run(); } TEST_F(ParallelAuthenticatorTest, DriveRequestOldPassword) { FailOnLoginSuccess(); ExpectPasswordChange(); state_->PresetCryptohomeStatus(false, cryptohome::MOUNT_ERROR_KEY_FAILURE); state_->PresetOnlineLoginStatus(LoginFailure::LoginFailureNone()); SetAttemptState(auth_.get(), state_.release()); RunResolve(auth_.get()); } TEST_F(ParallelAuthenticatorTest, DriveDataRecover) { ExpectLoginSuccess(username_, password_, cryptohome::MockAsyncMethodCaller::kFakeSanitizedUsername, false); FailOnLoginFailure(); // Set up mock cryptohome library to respond successfully to a key migration. mock_caller_->SetUp(true, cryptohome::MOUNT_ERROR_NONE); EXPECT_CALL(*mock_caller_, AsyncMigrateKey(username_, _, hash_ascii_, _)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*mock_caller_, AsyncMount(username_, hash_ascii_, cryptohome::MOUNT_FLAGS_NONE, _)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*mock_caller_, AsyncGetSanitizedUsername(username_, _)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*mock_cryptohome_library_, GetSystemSalt()) .WillOnce(Return(std::string())) .RetiresOnSaturation(); state_->PresetOnlineLoginStatus(LoginFailure::LoginFailureNone()); SetAttemptState(auth_.get(), state_.release()); auth_->RecoverEncryptedData(std::string()); message_loop_.Run(); } TEST_F(ParallelAuthenticatorTest, DriveDataRecoverButFail) { FailOnLoginSuccess(); ExpectPasswordChange(); // Set up mock cryptohome library to fail a key migration attempt, // asserting that the wrong password was used. mock_caller_->SetUp(false, cryptohome::MOUNT_ERROR_KEY_FAILURE); EXPECT_CALL(*mock_caller_, AsyncMigrateKey(username_, _, hash_ascii_, _)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*mock_cryptohome_library_, GetSystemSalt()) .WillOnce(Return(std::string())) .RetiresOnSaturation(); SetAttemptState(auth_.get(), state_.release()); auth_->RecoverEncryptedData(std::string()); message_loop_.Run(); } TEST_F(ParallelAuthenticatorTest, ResolveNoMount) { // Set a fake online attempt so that we return intermediate cryptohome state. FakeOnlineAttempt(); // Set up state as though a cryptohome mount attempt has occurred // and been rejected because the user doesn't exist. state_->PresetCryptohomeStatus(false, cryptohome::MOUNT_ERROR_USER_DOES_NOT_EXIST); EXPECT_EQ(ParallelAuthenticator::NO_MOUNT, SetAndResolveState(auth_.get(), state_.release())); } TEST_F(ParallelAuthenticatorTest, ResolveNoMountToFailedMount) { // Set up state as though a cryptohome mount attempt has occurred // and been rejected because the user doesn't exist. state_->PresetCryptohomeStatus(false, cryptohome::MOUNT_ERROR_USER_DOES_NOT_EXIST); // When there is no online attempt and online results, NO_MOUNT will be // resolved to FAILED_MOUNT. EXPECT_EQ(ParallelAuthenticator::FAILED_MOUNT, SetAndResolveState(auth_.get(), state_.release())); } TEST_F(ParallelAuthenticatorTest, ResolveCreateNew) { // Set up state as though a cryptohome mount attempt has occurred // and been rejected because the user doesn't exist; additionally, // an online auth attempt has completed successfully. state_->PresetCryptohomeStatus(false, cryptohome::MOUNT_ERROR_USER_DOES_NOT_EXIST); state_->PresetOnlineLoginStatus(LoginFailure::LoginFailureNone()); EXPECT_EQ(ParallelAuthenticator::CREATE_NEW, SetAndResolveState(auth_.get(), state_.release())); } TEST_F(ParallelAuthenticatorTest, DriveCreateForNewUser) { ExpectLoginSuccess(username_, password_, cryptohome::MockAsyncMethodCaller::kFakeSanitizedUsername, false); FailOnLoginFailure(); // Set up mock cryptohome library to respond successfully to a cryptohome // create attempt (indicated by the |CREATE_IF_MISSING| flag to AsyncMount). mock_caller_->SetUp(true, cryptohome::MOUNT_ERROR_NONE); EXPECT_CALL(*mock_caller_, AsyncMount(username_, hash_ascii_, cryptohome::CREATE_IF_MISSING, _)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*mock_caller_, AsyncGetSanitizedUsername(username_, _)) .Times(1) .RetiresOnSaturation(); // Set up state as though a cryptohome mount attempt has occurred // and been rejected because the user doesn't exist; additionally, // an online auth attempt has completed successfully. state_->PresetCryptohomeStatus(false, cryptohome::MOUNT_ERROR_USER_DOES_NOT_EXIST); state_->PresetOnlineLoginStatus(LoginFailure::LoginFailureNone()); SetAttemptState(auth_.get(), state_.release()); RunResolve(auth_.get()); } TEST_F(ParallelAuthenticatorTest, DriveOfflineLogin) { ExpectLoginSuccess(username_, password_, username_hash_, false); FailOnLoginFailure(); // Set up state as though a cryptohome mount attempt has occurred and // succeeded. state_->PresetCryptohomeStatus(true, cryptohome::MOUNT_ERROR_NONE); GoogleServiceAuthError error = GoogleServiceAuthError::FromConnectionError(net::ERR_CONNECTION_RESET); state_->PresetOnlineLoginStatus(LoginFailure::FromNetworkAuthFailure(error)); SetAttemptState(auth_.get(), state_.release()); RunResolve(auth_.get()); } TEST_F(ParallelAuthenticatorTest, DriveOfflineLoginDelayedOnline) { ExpectLoginSuccess(username_, password_, username_hash_, true); FailOnLoginFailure(); // Set up state as though a cryptohome mount attempt has occurred and // succeeded. state_->PresetCryptohomeStatus(true, cryptohome::MOUNT_ERROR_NONE); // state_ is released further down. SetAttemptState(auth_.get(), state_.get()); RunResolve(auth_.get()); // Offline login has completed, so now we "complete" the online request. GoogleServiceAuthError error( GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); LoginFailure failure = LoginFailure::FromNetworkAuthFailure(error); state_.release()->PresetOnlineLoginStatus(failure); ExpectLoginFailure(failure); RunResolve(auth_.get()); } TEST_F(ParallelAuthenticatorTest, DriveOfflineLoginGetNewPassword) { ExpectLoginSuccess(username_, password_, username_hash_, true); FailOnLoginFailure(); // Set up mock cryptohome library to respond successfully to a key migration. mock_caller_->SetUp(true, cryptohome::MOUNT_ERROR_NONE); EXPECT_CALL(*mock_caller_, AsyncMigrateKey(username_, state_->ascii_hash, _, _)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*mock_cryptohome_library_, GetSystemSalt()) .WillOnce(Return(std::string())) .RetiresOnSaturation(); // Set up state as though a cryptohome mount attempt has occurred and // succeeded; also, an online request that never made it. state_->PresetCryptohomeStatus(true, cryptohome::MOUNT_ERROR_NONE); // state_ is released further down. SetAttemptState(auth_.get(), state_.get()); RunResolve(auth_.get()); // Offline login has completed, so now we "complete" the online request. GoogleServiceAuthError error( GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); LoginFailure failure = LoginFailure::FromNetworkAuthFailure(error); state_.release()->PresetOnlineLoginStatus(failure); ExpectLoginFailure(failure); RunResolve(auth_.get()); // After the request below completes, OnLoginSuccess gets called again. ExpectLoginSuccess(username_, password_, username_hash_, false); MockURLFetcherFactory factory; TestingProfile profile; auth_->RetryAuth(&profile, UserContext(username_, std::string(), std::string()), std::string(), std::string()); message_loop_.Run(); message_loop_.RunUntilIdle(); } TEST_F(ParallelAuthenticatorTest, DriveOfflineLoginGetCaptchad) { ExpectLoginSuccess(username_, password_, username_hash_, true); FailOnLoginFailure(); EXPECT_CALL(*mock_cryptohome_library_, GetSystemSalt()) .WillOnce(Return(std::string())) .RetiresOnSaturation(); // Set up state as though a cryptohome mount attempt has occurred and // succeeded; also, an online request that never made it. state_->PresetCryptohomeStatus(true, cryptohome::MOUNT_ERROR_NONE); // state_ is released further down. SetAttemptState(auth_.get(), state_.get()); RunResolve(auth_.get()); // Offline login has completed, so now we "complete" the online request. GoogleServiceAuthError error( GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); LoginFailure failure = LoginFailure::FromNetworkAuthFailure(error); state_.release()->PresetOnlineLoginStatus(failure); ExpectLoginFailure(failure); RunResolve(auth_.get()); // After the request below completes, OnLoginSuccess gets called again. failure = LoginFailure::FromNetworkAuthFailure( GoogleServiceAuthError::FromClientLoginCaptchaChallenge( CaptchaFetcher::GetCaptchaToken(), GURL(CaptchaFetcher::GetCaptchaUrl()), GURL(CaptchaFetcher::GetUnlockUrl()))); ExpectLoginFailure(failure); MockURLFetcherFactory factory; TestingProfile profile; auth_->RetryAuth(&profile, UserContext(username_, std::string(), std::string()), std::string(), std::string()); message_loop_.Run(); message_loop_.RunUntilIdle(); } TEST_F(ParallelAuthenticatorTest, DriveOnlineLogin) { ExpectLoginSuccess(username_, password_, username_hash_, false); FailOnLoginFailure(); // Set up state as though a cryptohome mount attempt has occurred and // succeeded. state_->PresetCryptohomeStatus(true, cryptohome::MOUNT_ERROR_NONE); state_->PresetOnlineLoginStatus(LoginFailure::LoginFailureNone()); SetAttemptState(auth_.get(), state_.release()); RunResolve(auth_.get()); } // http://crbug.com/106538 TEST_F(ParallelAuthenticatorTest, DISABLED_DriveNeedNewPassword) { FailOnLoginSuccess(); // Set failing on success as the default... // ...but expect ONE successful login first. ExpectLoginSuccess(username_, password_, username_hash_, true); GoogleServiceAuthError error( GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); LoginFailure failure = LoginFailure::FromNetworkAuthFailure(error); ExpectLoginFailure(failure); // Set up state as though a cryptohome mount attempt has occurred and // succeeded. state_->PresetCryptohomeStatus(true, cryptohome::MOUNT_ERROR_NONE); state_->PresetOnlineLoginStatus(failure); SetAttemptState(auth_.get(), state_.release()); RunResolve(auth_.get()); } TEST_F(ParallelAuthenticatorTest, DriveUnlock) { ExpectLoginSuccess(username_, std::string(), std::string(), false); FailOnLoginFailure(); // Set up mock cryptohome library to respond successfully to a cryptohome // key-check attempt. mock_caller_->SetUp(true, cryptohome::MOUNT_ERROR_NONE); EXPECT_CALL(*mock_caller_, AsyncCheckKey(username_, _, _)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*mock_cryptohome_library_, GetSystemSalt()) .WillOnce(Return(std::string())) .RetiresOnSaturation(); auth_->AuthenticateToUnlock(UserContext(username_, std::string(), std::string())); message_loop_.Run(); } } // namespace chromeos