parallel_authenticator_unittest.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/chromeos/login/parallel_authenticator.h"
6
7#include <string>
8
9#include "base/file_util.h"
10#include "base/files/file_path.h"
11#include "base/memory/scoped_ptr.h"
12#include "base/message_loop.h"
13#include "base/string_util.h"
14#include "base/stringprintf.h"
15#include "chrome/browser/chromeos/cros/cros_library.h"
16#include "chrome/browser/chromeos/login/mock_login_status_consumer.h"
17#include "chrome/browser/chromeos/login/mock_url_fetchers.h"
18#include "chrome/browser/chromeos/login/mock_user_manager.h"
19#include "chrome/browser/chromeos/login/test_attempt_state.h"
20#include "chrome/browser/chromeos/login/user.h"
21#include "chrome/browser/chromeos/login/user_manager.h"
22#include "chrome/browser/chromeos/settings/cros_settings.h"
23#include "chrome/browser/chromeos/settings/device_settings_test_helper.h"
24#include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h"
25#include "chrome/test/base/testing_profile.h"
26#include "chromeos/cryptohome/mock_async_method_caller.h"
27#include "chromeos/cryptohome/mock_cryptohome_library.h"
28#include "chromeos/dbus/mock_cryptohome_client.h"
29#include "chromeos/dbus/mock_dbus_thread_manager.h"
30#include "content/public/test/test_browser_thread.h"
31#include "google_apis/gaia/mock_url_fetcher_factory.h"
32#include "googleurl/src/gurl.h"
33#include "net/base/net_errors.h"
34#include "net/url_request/url_request_status.h"
35#include "testing/gmock/include/gmock/gmock.h"
36#include "testing/gtest/include/gtest/gtest.h"
37#include "third_party/cros_system_api/dbus/service_constants.h"
38
39using ::testing::AnyNumber;
40using ::testing::DoAll;
41using ::testing::Invoke;
42using ::testing::Return;
43using ::testing::SetArgPointee;
44using ::testing::_;
45using content::BrowserThread;
46
47namespace chromeos {
48
49class TestOnlineAttempt : public OnlineAttempt {
50 public:
51  TestOnlineAttempt(AuthAttemptState* state,
52                    AuthAttemptStateResolver* resolver)
53      : OnlineAttempt(state, resolver) {
54  }
55};
56
57class ParallelAuthenticatorTest : public testing::Test {
58 public:
59  ParallelAuthenticatorTest()
60      : message_loop_(MessageLoop::TYPE_UI),
61        ui_thread_(BrowserThread::UI, &message_loop_),
62        file_thread_(BrowserThread::FILE, &message_loop_),
63        io_thread_(BrowserThread::IO),
64        username_("me@nowhere.org"),
65        password_("fakepass"),
66        user_manager_enabler_(new MockUserManager) {
67    hash_ascii_.assign("0a010000000000a0");
68    hash_ascii_.append(std::string(16, '0'));
69  }
70
71  virtual ~ParallelAuthenticatorTest() {
72    DCHECK(!mock_caller_);
73  }
74
75  virtual void SetUp() {
76    mock_caller_ = new cryptohome::MockAsyncMethodCaller;
77    cryptohome::AsyncMethodCaller::InitializeForTesting(mock_caller_);
78
79    mock_cryptohome_library_ .reset(new MockCryptohomeLibrary());
80    CryptohomeLibrary::SetForTest(mock_cryptohome_library_.get());
81
82    io_thread_.Start();
83
84    auth_ = new ParallelAuthenticator(&consumer_);
85    auth_->set_using_oauth(false);
86    state_.reset(new TestAttemptState(UserContext(username_,
87                                                  password_,
88                                                  std::string()),
89                                      hash_ascii_,
90                                      "",
91                                      "",
92                                      User::USER_TYPE_REGULAR,
93                                      false));
94  }
95
96  // Tears down the test fixture.
97  virtual void TearDown() {
98    CryptohomeLibrary::SetForTest(NULL);
99
100    cryptohome::AsyncMethodCaller::Shutdown();
101    mock_caller_ = NULL;
102  }
103
104  base::FilePath PopulateTempFile(const char* data, int data_len) {
105    base::FilePath out;
106    FILE* tmp_file = file_util::CreateAndOpenTemporaryFile(&out);
107    EXPECT_NE(tmp_file, static_cast<FILE*>(NULL));
108    EXPECT_EQ(file_util::WriteFile(out, data, data_len), data_len);
109    EXPECT_TRUE(file_util::CloseFile(tmp_file));
110    return out;
111  }
112
113  // Allow test to fail and exit gracefully, even if OnLoginFailure()
114  // wasn't supposed to happen.
115  void FailOnLoginFailure() {
116    ON_CALL(consumer_, OnLoginFailure(_))
117        .WillByDefault(Invoke(MockConsumer::OnFailQuitAndFail));
118  }
119
120  // Allow test to fail and exit gracefully, even if
121  // OnRetailModeLoginSuccess() wasn't supposed to happen.
122  void FailOnRetailModeLoginSuccess() {
123    ON_CALL(consumer_, OnRetailModeLoginSuccess(_))
124        .WillByDefault(Invoke(MockConsumer::OnRetailModeSuccessQuitAndFail));
125  }
126
127  // Allow test to fail and exit gracefully, even if OnLoginSuccess()
128  // wasn't supposed to happen.
129  void FailOnLoginSuccess() {
130    ON_CALL(consumer_, OnLoginSuccess(_, _, _))
131        .WillByDefault(Invoke(MockConsumer::OnSuccessQuitAndFail));
132  }
133
134  // Allow test to fail and exit gracefully, even if
135  // OnOffTheRecordLoginSuccess() wasn't supposed to happen.
136  void FailOnGuestLoginSuccess() {
137    ON_CALL(consumer_, OnOffTheRecordLoginSuccess())
138        .WillByDefault(Invoke(MockConsumer::OnGuestSuccessQuitAndFail));
139  }
140
141  void ExpectLoginFailure(const LoginFailure& failure) {
142    EXPECT_CALL(consumer_, OnLoginFailure(failure))
143        .WillOnce(Invoke(MockConsumer::OnFailQuit))
144        .RetiresOnSaturation();
145  }
146
147  void ExpectRetailModeLoginSuccess() {
148    EXPECT_CALL(consumer_, OnRetailModeLoginSuccess(_))
149        .WillOnce(Invoke(MockConsumer::OnRetailModeSuccessQuit))
150        .RetiresOnSaturation();
151  }
152
153  void ExpectLoginSuccess(const std::string& username,
154                          const std::string& password,
155                          const std::string& username_hash_,
156                          bool pending) {
157    EXPECT_CALL(consumer_, OnLoginSuccess(UserContext(username,
158                                                      password,
159                                                      std::string(),
160                                                      username_hash_),
161                                          pending,
162                                          false))
163        .WillOnce(Invoke(MockConsumer::OnSuccessQuit))
164        .RetiresOnSaturation();
165  }
166
167  void ExpectGuestLoginSuccess() {
168    EXPECT_CALL(consumer_, OnOffTheRecordLoginSuccess())
169        .WillOnce(Invoke(MockConsumer::OnGuestSuccessQuit))
170        .RetiresOnSaturation();
171  }
172
173  void ExpectPasswordChange() {
174    EXPECT_CALL(consumer_, OnPasswordChangeDetected())
175        .WillOnce(Invoke(MockConsumer::OnMigrateQuit))
176        .RetiresOnSaturation();
177  }
178
179  void RunResolve(ParallelAuthenticator* auth) {
180    auth->Resolve();
181    message_loop_.RunUntilIdle();
182  }
183
184  void SetAttemptState(ParallelAuthenticator* auth, TestAttemptState* state) {
185    auth->set_attempt_state(state);
186  }
187
188  ParallelAuthenticator::AuthState SetAndResolveState(
189      ParallelAuthenticator* auth, TestAttemptState* state) {
190    auth->set_attempt_state(state);
191    return auth->ResolveState();
192  }
193
194  void SetOwnerState(bool owner_check_finished, bool check_result) {
195    auth_->SetOwnerState(owner_check_finished, check_result);
196  }
197
198  void FakeOnlineAttempt() {
199    auth_->set_online_attempt(new TestOnlineAttempt(state_.get(), auth_.get()));
200  }
201
202  MessageLoop message_loop_;
203  content::TestBrowserThread ui_thread_;
204  content::TestBrowserThread file_thread_;
205  content::TestBrowserThread io_thread_;
206
207  std::string username_;
208  std::string password_;
209  std::string username_hash_;
210  std::string hash_ascii_;
211
212  ScopedStubCrosEnabler stub_cros_enabler_;
213  ScopedDeviceSettingsTestHelper device_settings_test_helper_;
214  ScopedTestCrosSettings test_cros_settings_;
215
216  // Mocks, destroyed by CrosLibrary class.
217  ScopedUserManagerEnabler user_manager_enabler_;
218
219  scoped_ptr<MockCryptohomeLibrary> mock_cryptohome_library_;
220
221  cryptohome::MockAsyncMethodCaller* mock_caller_;
222
223  MockConsumer consumer_;
224  scoped_refptr<ParallelAuthenticator> auth_;
225  scoped_ptr<TestAttemptState> state_;
226};
227
228TEST_F(ParallelAuthenticatorTest, OnLoginSuccess) {
229  EXPECT_CALL(consumer_, OnLoginSuccess(UserContext(username_,
230                                                    password_,
231                                                    std::string(),
232                                                    username_hash_),
233                                        false, false))
234      .Times(1)
235      .RetiresOnSaturation();
236
237  SetAttemptState(auth_, state_.release());
238  auth_->OnLoginSuccess(false);
239}
240
241TEST_F(ParallelAuthenticatorTest, OnPasswordChangeDetected) {
242  EXPECT_CALL(consumer_, OnPasswordChangeDetected())
243      .Times(1)
244      .RetiresOnSaturation();
245  SetAttemptState(auth_, state_.release());
246  auth_->OnPasswordChangeDetected();
247}
248
249TEST_F(ParallelAuthenticatorTest, ResolveNothingDone) {
250  EXPECT_EQ(ParallelAuthenticator::CONTINUE,
251            SetAndResolveState(auth_, state_.release()));
252}
253
254TEST_F(ParallelAuthenticatorTest, ResolvePossiblePwChange) {
255  // Set a fake online attempt so that we return intermediate cryptohome state.
256  FakeOnlineAttempt();
257
258  // Set up state as though a cryptohome mount attempt has occurred
259  // and been rejected.
260  state_->PresetCryptohomeStatus(false, cryptohome::MOUNT_ERROR_KEY_FAILURE);
261
262  EXPECT_EQ(ParallelAuthenticator::POSSIBLE_PW_CHANGE,
263            SetAndResolveState(auth_, state_.release()));
264}
265
266TEST_F(ParallelAuthenticatorTest, ResolvePossiblePwChangeToFailedMount) {
267  // Set up state as though a cryptohome mount attempt has occurred
268  // and been rejected.
269  state_->PresetCryptohomeStatus(false, cryptohome::MOUNT_ERROR_KEY_FAILURE);
270
271  // When there is no online attempt and online results, POSSIBLE_PW_CHANGE
272  EXPECT_EQ(ParallelAuthenticator::FAILED_MOUNT,
273            SetAndResolveState(auth_, state_.release()));
274}
275
276TEST_F(ParallelAuthenticatorTest, ResolveNeedOldPw) {
277  // Set up state as though a cryptohome mount attempt has occurred
278  // and been rejected because of unmatched key; additionally,
279  // an online auth attempt has completed successfully.
280  state_->PresetCryptohomeStatus(false, cryptohome::MOUNT_ERROR_KEY_FAILURE);
281  state_->PresetOnlineLoginStatus(LoginFailure::LoginFailureNone());
282
283  EXPECT_EQ(ParallelAuthenticator::NEED_OLD_PW,
284            SetAndResolveState(auth_, state_.release()));
285}
286
287TEST_F(ParallelAuthenticatorTest, ResolveOwnerNeededDirectFailedMount) {
288  // Set up state as though a cryptohome mount attempt has occurred
289  // and succeeded but we are in safe mode and the current user is not owner.
290  // This is a high level test to verify the proper transitioning in this mode
291  // only. It is not testing that we properly verify that the user is an owner
292  // or that we really are in "safe-mode".
293  state_->PresetCryptohomeStatus(true, cryptohome::MOUNT_ERROR_NONE);
294  SetOwnerState(true, false);
295
296  EXPECT_EQ(ParallelAuthenticator::OWNER_REQUIRED,
297            SetAndResolveState(auth_, state_.release()));
298}
299
300TEST_F(ParallelAuthenticatorTest, ResolveOwnerNeededMount) {
301  // Set up state as though a cryptohome mount attempt has occurred
302  // and succeeded but we are in safe mode and the current user is not owner.
303  // This test will check that the "safe-mode" policy is not set and will let
304  // the mount finish successfully.
305  state_->PresetCryptohomeStatus(true, cryptohome::MOUNT_ERROR_NONE);
306  SetOwnerState(false, false);
307  // and test that the mount has succeeded.
308  state_.reset(new TestAttemptState(UserContext(username_,
309                                                password_,
310                                                std::string()),
311                                    hash_ascii_,
312                                    "",
313                                    "",
314                                    User::USER_TYPE_REGULAR,
315                                    false));
316  state_->PresetCryptohomeStatus(true, cryptohome::MOUNT_ERROR_NONE);
317  EXPECT_EQ(ParallelAuthenticator::OFFLINE_LOGIN,
318            SetAndResolveState(auth_, state_.release()));
319}
320
321TEST_F(ParallelAuthenticatorTest, ResolveOwnerNeededFailedMount) {
322  FailOnLoginSuccess();  // Set failing on success as the default...
323  LoginFailure failure = LoginFailure(LoginFailure::OWNER_REQUIRED);
324  ExpectLoginFailure(failure);
325
326  MockDBusThreadManager* mock_dbus_thread_manager = new MockDBusThreadManager;
327  EXPECT_CALL(*mock_dbus_thread_manager, GetSystemBus())
328      .WillRepeatedly(Return(reinterpret_cast<dbus::Bus*>(NULL)));
329  DBusThreadManager::InitializeForTesting(mock_dbus_thread_manager);
330  EXPECT_CALL(*mock_dbus_thread_manager->mock_cryptohome_client(), Unmount(_))
331      .WillOnce(DoAll(SetArgPointee<0>(true), Return(true)));
332
333  CrosSettingsProvider* device_settings_provider;
334  StubCrosSettingsProvider stub_settings_provider;
335  // Set up state as though a cryptohome mount attempt has occurred
336  // and succeeded but we are in safe mode and the current user is not owner.
337  state_->PresetCryptohomeStatus(true, cryptohome::MOUNT_ERROR_NONE);
338  SetOwnerState(false, false);
339  // Remove the real DeviceSettingsProvider and replace it with a stub.
340  device_settings_provider =
341      CrosSettings::Get()->GetProvider(chromeos::kReportDeviceVersionInfo);
342  EXPECT_TRUE(device_settings_provider != NULL);
343  EXPECT_TRUE(
344      CrosSettings::Get()->RemoveSettingsProvider(device_settings_provider));
345  CrosSettings::Get()->AddSettingsProvider(&stub_settings_provider);
346  CrosSettings::Get()->SetBoolean(kPolicyMissingMitigationMode, true);
347
348  EXPECT_EQ(ParallelAuthenticator::CONTINUE,
349            SetAndResolveState(auth_, state_.release()));
350  // Let the owner verification run.
351  device_settings_test_helper_.Flush();
352  // and test that the mount has succeeded.
353  state_.reset(new TestAttemptState(UserContext(username_,
354                                                password_,
355                                                std::string()),
356                                    hash_ascii_,
357                                    "",
358                                    "",
359                                    User::USER_TYPE_REGULAR,
360                                    false));
361  state_->PresetCryptohomeStatus(true, cryptohome::MOUNT_ERROR_NONE);
362  EXPECT_EQ(ParallelAuthenticator::OWNER_REQUIRED,
363            SetAndResolveState(auth_, state_.release()));
364
365  EXPECT_TRUE(
366      CrosSettings::Get()->RemoveSettingsProvider(&stub_settings_provider));
367  CrosSettings::Get()->AddSettingsProvider(device_settings_provider);
368  DBusThreadManager::Get()->Shutdown();
369}
370
371TEST_F(ParallelAuthenticatorTest, DriveFailedMount) {
372  FailOnLoginSuccess();
373  ExpectLoginFailure(LoginFailure(LoginFailure::COULD_NOT_MOUNT_CRYPTOHOME));
374
375  // Set up state as though a cryptohome mount attempt has occurred
376  // and failed.
377  state_->PresetCryptohomeStatus(false, cryptohome::MOUNT_ERROR_NONE);
378  SetAttemptState(auth_, state_.release());
379
380  RunResolve(auth_.get());
381}
382
383TEST_F(ParallelAuthenticatorTest, DriveGuestLogin) {
384  ExpectGuestLoginSuccess();
385  FailOnLoginFailure();
386
387  // Set up mock cryptohome library to respond as though a tmpfs mount
388  // attempt has occurred and succeeded.
389  mock_caller_->SetUp(true, cryptohome::MOUNT_ERROR_NONE);
390  EXPECT_CALL(*mock_caller_, AsyncMountGuest(_))
391      .Times(1)
392      .RetiresOnSaturation();
393
394  auth_->LoginOffTheRecord();
395  message_loop_.Run();
396}
397
398TEST_F(ParallelAuthenticatorTest, DriveGuestLoginButFail) {
399  FailOnGuestLoginSuccess();
400  ExpectLoginFailure(LoginFailure(LoginFailure::COULD_NOT_MOUNT_TMPFS));
401
402  // Set up mock cryptohome library to respond as though a tmpfs mount
403  // attempt has occurred and failed.
404  mock_caller_->SetUp(false, cryptohome::MOUNT_ERROR_NONE);
405  EXPECT_CALL(*mock_caller_, AsyncMountGuest(_))
406      .Times(1)
407      .RetiresOnSaturation();
408
409  auth_->LoginOffTheRecord();
410  message_loop_.Run();
411}
412
413TEST_F(ParallelAuthenticatorTest, DriveRetailModeUserLogin) {
414  ExpectRetailModeLoginSuccess();
415  FailOnLoginFailure();
416
417  // Set up mock cryptohome library to respond as though a tmpfs mount
418  // attempt has occurred and succeeded.
419  mock_caller_->SetUp(true, cryptohome::MOUNT_ERROR_NONE);
420  EXPECT_CALL(*mock_caller_, AsyncMountGuest(_))
421      .Times(1)
422      .RetiresOnSaturation();
423
424  auth_->LoginRetailMode();
425  message_loop_.Run();
426}
427
428TEST_F(ParallelAuthenticatorTest, DriveRetailModeLoginButFail) {
429  FailOnRetailModeLoginSuccess();
430  ExpectLoginFailure(LoginFailure(LoginFailure::COULD_NOT_MOUNT_TMPFS));
431
432  // Set up mock cryptohome library to respond as though a tmpfs mount
433  // attempt has occurred and failed.
434  mock_caller_->SetUp(false, cryptohome::MOUNT_ERROR_NONE);
435  EXPECT_CALL(*mock_caller_, AsyncMountGuest(_))
436      .Times(1)
437      .RetiresOnSaturation();
438
439  auth_->LoginRetailMode();
440  message_loop_.Run();
441}
442
443TEST_F(ParallelAuthenticatorTest, DriveDataResync) {
444  ExpectLoginSuccess(username_,
445                     password_,
446                     cryptohome::MockAsyncMethodCaller::kFakeSanitizedUsername,
447                     false);
448  FailOnLoginFailure();
449
450  // Set up mock cryptohome library to respond successfully to a cryptohome
451  // remove attempt and a cryptohome create attempt (indicated by the
452  // |CREATE_IF_MISSING| flag to AsyncMount).
453  mock_caller_->SetUp(true, cryptohome::MOUNT_ERROR_NONE);
454  EXPECT_CALL(*mock_caller_, AsyncRemove(username_, _))
455      .Times(1)
456      .RetiresOnSaturation();
457  EXPECT_CALL(*mock_caller_, AsyncMount(username_, hash_ascii_,
458                                        cryptohome::CREATE_IF_MISSING, _))
459      .Times(1)
460      .RetiresOnSaturation();
461  EXPECT_CALL(*mock_caller_, AsyncGetSanitizedUsername(username_, _))
462      .Times(1)
463      .RetiresOnSaturation();
464
465  state_->PresetOnlineLoginStatus(LoginFailure::LoginFailureNone());
466  SetAttemptState(auth_, state_.release());
467
468  auth_->ResyncEncryptedData();
469  message_loop_.Run();
470}
471
472TEST_F(ParallelAuthenticatorTest, DriveResyncFail) {
473  FailOnLoginSuccess();
474  ExpectLoginFailure(LoginFailure(LoginFailure::DATA_REMOVAL_FAILED));
475
476  // Set up mock cryptohome library to fail a cryptohome remove attempt.
477  mock_caller_->SetUp(false, cryptohome::MOUNT_ERROR_NONE);
478  EXPECT_CALL(*mock_caller_, AsyncRemove(username_, _))
479      .Times(1)
480      .RetiresOnSaturation();
481
482  SetAttemptState(auth_, state_.release());
483
484  auth_->ResyncEncryptedData();
485  message_loop_.Run();
486}
487
488TEST_F(ParallelAuthenticatorTest, DriveRequestOldPassword) {
489  FailOnLoginSuccess();
490  ExpectPasswordChange();
491
492  state_->PresetCryptohomeStatus(false, cryptohome::MOUNT_ERROR_KEY_FAILURE);
493  state_->PresetOnlineLoginStatus(LoginFailure::LoginFailureNone());
494  SetAttemptState(auth_, state_.release());
495
496  RunResolve(auth_.get());
497}
498
499TEST_F(ParallelAuthenticatorTest, DriveDataRecover) {
500  ExpectLoginSuccess(username_,
501                     password_,
502                     cryptohome::MockAsyncMethodCaller::kFakeSanitizedUsername,
503                     false);
504  FailOnLoginFailure();
505
506  // Set up mock cryptohome library to respond successfully to a key migration.
507  mock_caller_->SetUp(true, cryptohome::MOUNT_ERROR_NONE);
508  EXPECT_CALL(*mock_caller_, AsyncMigrateKey(username_, _, hash_ascii_, _))
509      .Times(1)
510      .RetiresOnSaturation();
511  EXPECT_CALL(*mock_caller_, AsyncMount(username_, hash_ascii_,
512                                        cryptohome::MOUNT_FLAGS_NONE, _))
513      .Times(1)
514      .RetiresOnSaturation();
515  EXPECT_CALL(*mock_caller_, AsyncGetSanitizedUsername(username_, _))
516        .Times(1)
517        .RetiresOnSaturation();
518  EXPECT_CALL(*mock_cryptohome_library_, GetSystemSalt())
519      .WillOnce(Return(std::string()))
520      .RetiresOnSaturation();
521
522  state_->PresetOnlineLoginStatus(LoginFailure::LoginFailureNone());
523  SetAttemptState(auth_, state_.release());
524
525  auth_->RecoverEncryptedData(std::string());
526  message_loop_.Run();
527}
528
529TEST_F(ParallelAuthenticatorTest, DriveDataRecoverButFail) {
530  FailOnLoginSuccess();
531  ExpectPasswordChange();
532
533  // Set up mock cryptohome library to fail a key migration attempt,
534  // asserting that the wrong password was used.
535  mock_caller_->SetUp(false, cryptohome::MOUNT_ERROR_KEY_FAILURE);
536  EXPECT_CALL(*mock_caller_, AsyncMigrateKey(username_, _, hash_ascii_, _))
537      .Times(1)
538      .RetiresOnSaturation();
539  EXPECT_CALL(*mock_cryptohome_library_, GetSystemSalt())
540      .WillOnce(Return(std::string()))
541      .RetiresOnSaturation();
542
543  SetAttemptState(auth_, state_.release());
544
545  auth_->RecoverEncryptedData(std::string());
546  message_loop_.Run();
547}
548
549TEST_F(ParallelAuthenticatorTest, ResolveNoMount) {
550  // Set a fake online attempt so that we return intermediate cryptohome state.
551  FakeOnlineAttempt();
552
553  // Set up state as though a cryptohome mount attempt has occurred
554  // and been rejected because the user doesn't exist.
555  state_->PresetCryptohomeStatus(false,
556                                 cryptohome::MOUNT_ERROR_USER_DOES_NOT_EXIST);
557
558  EXPECT_EQ(ParallelAuthenticator::NO_MOUNT,
559            SetAndResolveState(auth_, state_.release()));
560}
561
562TEST_F(ParallelAuthenticatorTest, ResolveNoMountToFailedMount) {
563  // Set up state as though a cryptohome mount attempt has occurred
564  // and been rejected because the user doesn't exist.
565  state_->PresetCryptohomeStatus(false,
566                                 cryptohome::MOUNT_ERROR_USER_DOES_NOT_EXIST);
567
568  // When there is no online attempt and online results, NO_MOUNT will be
569  // resolved to FAILED_MOUNT.
570  EXPECT_EQ(ParallelAuthenticator::FAILED_MOUNT,
571            SetAndResolveState(auth_, state_.release()));
572}
573
574TEST_F(ParallelAuthenticatorTest, ResolveCreateNew) {
575  // Set up state as though a cryptohome mount attempt has occurred
576  // and been rejected because the user doesn't exist; additionally,
577  // an online auth attempt has completed successfully.
578  state_->PresetCryptohomeStatus(false,
579                                 cryptohome::MOUNT_ERROR_USER_DOES_NOT_EXIST);
580  state_->PresetOnlineLoginStatus(LoginFailure::LoginFailureNone());
581
582  EXPECT_EQ(ParallelAuthenticator::CREATE_NEW,
583            SetAndResolveState(auth_, state_.release()));
584}
585
586TEST_F(ParallelAuthenticatorTest, DriveCreateForNewUser) {
587  ExpectLoginSuccess(username_,
588                     password_,
589                     cryptohome::MockAsyncMethodCaller::kFakeSanitizedUsername,
590                     false);
591  FailOnLoginFailure();
592
593  // Set up mock cryptohome library to respond successfully to a cryptohome
594  // create attempt (indicated by the |CREATE_IF_MISSING| flag to AsyncMount).
595  mock_caller_->SetUp(true, cryptohome::MOUNT_ERROR_NONE);
596  EXPECT_CALL(*mock_caller_, AsyncMount(username_, hash_ascii_,
597                                        cryptohome::CREATE_IF_MISSING, _))
598      .Times(1)
599      .RetiresOnSaturation();
600  EXPECT_CALL(*mock_caller_, AsyncGetSanitizedUsername(username_, _))
601      .Times(1)
602      .RetiresOnSaturation();
603
604  // Set up state as though a cryptohome mount attempt has occurred
605  // and been rejected because the user doesn't exist; additionally,
606  // an online auth attempt has completed successfully.
607  state_->PresetCryptohomeStatus(false,
608                                 cryptohome::MOUNT_ERROR_USER_DOES_NOT_EXIST);
609  state_->PresetOnlineLoginStatus(LoginFailure::LoginFailureNone());
610  SetAttemptState(auth_, state_.release());
611
612  RunResolve(auth_.get());
613}
614
615TEST_F(ParallelAuthenticatorTest, DriveOfflineLogin) {
616  ExpectLoginSuccess(username_, password_, username_hash_, false);
617  FailOnLoginFailure();
618
619  // Set up state as though a cryptohome mount attempt has occurred and
620  // succeeded.
621  state_->PresetCryptohomeStatus(true, cryptohome::MOUNT_ERROR_NONE);
622  GoogleServiceAuthError error =
623      GoogleServiceAuthError::FromConnectionError(net::ERR_CONNECTION_RESET);
624  state_->PresetOnlineLoginStatus(LoginFailure::FromNetworkAuthFailure(error));
625  SetAttemptState(auth_, state_.release());
626
627  RunResolve(auth_.get());
628}
629
630TEST_F(ParallelAuthenticatorTest, DriveOfflineLoginDelayedOnline) {
631  ExpectLoginSuccess(username_, password_, username_hash_, true);
632  FailOnLoginFailure();
633
634  // Set up state as though a cryptohome mount attempt has occurred and
635  // succeeded.
636  state_->PresetCryptohomeStatus(true, cryptohome::MOUNT_ERROR_NONE);
637  // state_ is released further down.
638  SetAttemptState(auth_, state_.get());
639  RunResolve(auth_.get());
640
641  // Offline login has completed, so now we "complete" the online request.
642  GoogleServiceAuthError error(
643      GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
644  LoginFailure failure = LoginFailure::FromNetworkAuthFailure(error);
645  state_.release()->PresetOnlineLoginStatus(failure);
646  ExpectLoginFailure(failure);
647
648  RunResolve(auth_.get());
649}
650
651TEST_F(ParallelAuthenticatorTest, DriveOfflineLoginGetNewPassword) {
652  ExpectLoginSuccess(username_, password_, username_hash_, true);
653  FailOnLoginFailure();
654
655  // Set up mock cryptohome library to respond successfully to a key migration.
656  mock_caller_->SetUp(true, cryptohome::MOUNT_ERROR_NONE);
657  EXPECT_CALL(*mock_caller_, AsyncMigrateKey(username_,
658                                              state_->ascii_hash,
659                                              _,
660                                              _))
661      .Times(1)
662      .RetiresOnSaturation();
663  EXPECT_CALL(*mock_cryptohome_library_, GetSystemSalt())
664      .WillOnce(Return(std::string()))
665      .RetiresOnSaturation();
666
667  // Set up state as though a cryptohome mount attempt has occurred and
668  // succeeded; also, an online request that never made it.
669  state_->PresetCryptohomeStatus(true, cryptohome::MOUNT_ERROR_NONE);
670  // state_ is released further down.
671  SetAttemptState(auth_, state_.get());
672  RunResolve(auth_.get());
673
674  // Offline login has completed, so now we "complete" the online request.
675  GoogleServiceAuthError error(
676      GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
677  LoginFailure failure = LoginFailure::FromNetworkAuthFailure(error);
678  state_.release()->PresetOnlineLoginStatus(failure);
679  ExpectLoginFailure(failure);
680
681  RunResolve(auth_.get());
682
683  // After the request below completes, OnLoginSuccess gets called again.
684  ExpectLoginSuccess(username_, password_, username_hash_, false);
685
686  MockURLFetcherFactory<SuccessFetcher> factory;
687  TestingProfile profile;
688
689  auth_->RetryAuth(&profile,
690                   UserContext(username_,
691                               std::string(),
692                               std::string()),
693                   std::string(),
694                   std::string());
695  message_loop_.Run();
696  message_loop_.RunUntilIdle();
697}
698
699TEST_F(ParallelAuthenticatorTest, DriveOfflineLoginGetCaptchad) {
700  ExpectLoginSuccess(username_, password_, username_hash_, true);
701  FailOnLoginFailure();
702  EXPECT_CALL(*mock_cryptohome_library_, GetSystemSalt())
703      .WillOnce(Return(std::string()))
704      .RetiresOnSaturation();
705
706  // Set up state as though a cryptohome mount attempt has occurred and
707  // succeeded; also, an online request that never made it.
708  state_->PresetCryptohomeStatus(true, cryptohome::MOUNT_ERROR_NONE);
709  // state_ is released further down.
710  SetAttemptState(auth_, state_.get());
711  RunResolve(auth_.get());
712
713  // Offline login has completed, so now we "complete" the online request.
714  GoogleServiceAuthError error(
715      GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
716  LoginFailure failure = LoginFailure::FromNetworkAuthFailure(error);
717  state_.release()->PresetOnlineLoginStatus(failure);
718  ExpectLoginFailure(failure);
719
720  RunResolve(auth_.get());
721
722  // After the request below completes, OnLoginSuccess gets called again.
723  failure = LoginFailure::FromNetworkAuthFailure(
724      GoogleServiceAuthError::FromClientLoginCaptchaChallenge(
725          CaptchaFetcher::GetCaptchaToken(),
726          GURL(CaptchaFetcher::GetCaptchaUrl()),
727          GURL(CaptchaFetcher::GetUnlockUrl())));
728  ExpectLoginFailure(failure);
729
730  MockURLFetcherFactory<CaptchaFetcher> factory;
731  TestingProfile profile;
732
733  auth_->RetryAuth(&profile,
734                   UserContext(username_,
735                               std::string(),
736                               std::string()),
737                   std::string(),
738                   std::string());
739  message_loop_.Run();
740  message_loop_.RunUntilIdle();
741}
742
743TEST_F(ParallelAuthenticatorTest, DriveOnlineLogin) {
744  ExpectLoginSuccess(username_, password_, username_hash_, false);
745  FailOnLoginFailure();
746
747  // Set up state as though a cryptohome mount attempt has occurred and
748  // succeeded.
749  state_->PresetCryptohomeStatus(true, cryptohome::MOUNT_ERROR_NONE);
750  state_->PresetOnlineLoginStatus(LoginFailure::LoginFailureNone());
751  SetAttemptState(auth_, state_.release());
752
753  RunResolve(auth_.get());
754}
755
756// http://crbug.com/106538
757TEST_F(ParallelAuthenticatorTest, DISABLED_DriveNeedNewPassword) {
758  FailOnLoginSuccess();  // Set failing on success as the default...
759  // ...but expect ONE successful login first.
760  ExpectLoginSuccess(username_, password_, username_hash_, true);
761  GoogleServiceAuthError error(
762      GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
763  LoginFailure failure = LoginFailure::FromNetworkAuthFailure(error);
764  ExpectLoginFailure(failure);
765
766  // Set up state as though a cryptohome mount attempt has occurred and
767  // succeeded.
768  state_->PresetCryptohomeStatus(true, cryptohome::MOUNT_ERROR_NONE);
769  state_->PresetOnlineLoginStatus(failure);
770  SetAttemptState(auth_, state_.release());
771
772  RunResolve(auth_.get());
773}
774
775TEST_F(ParallelAuthenticatorTest, DriveUnlock) {
776  ExpectLoginSuccess(username_, std::string(), std::string(), false);
777  FailOnLoginFailure();
778
779  // Set up mock cryptohome library to respond successfully to a cryptohome
780  // key-check attempt.
781  mock_caller_->SetUp(true, cryptohome::MOUNT_ERROR_NONE);
782  EXPECT_CALL(*mock_caller_, AsyncCheckKey(username_, _, _))
783      .Times(1)
784      .RetiresOnSaturation();
785  EXPECT_CALL(*mock_cryptohome_library_, GetSystemSalt())
786      .WillOnce(Return(std::string()))
787      .RetiresOnSaturation();
788
789  auth_->AuthenticateToUnlock(UserContext(username_,
790                                          std::string(),
791                                          std::string()));
792  message_loop_.Run();
793}
794
795}  // namespace chromeos
796