1// Copyright (c) 2011 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#include <vector>
9
10#include "base/file_path.h"
11#include "base/file_util.h"
12#include "base/memory/scoped_ptr.h"
13#include "base/message_loop.h"
14#include "base/path_service.h"
15#include "base/string_util.h"
16#include "base/stringprintf.h"
17#include "chrome/browser/chromeos/cros/mock_cryptohome_library.h"
18#include "chrome/browser/chromeos/cros/mock_library_loader.h"
19#include "chrome/browser/chromeos/login/mock_auth_response_handler.h"
20#include "chrome/browser/chromeos/login/mock_login_status_consumer.h"
21#include "chrome/browser/chromeos/login/mock_url_fetchers.h"
22#include "chrome/browser/chromeos/login/test_attempt_state.h"
23#include "chrome/common/chrome_paths.h"
24#include "chrome/common/net/gaia/gaia_auth_fetcher_unittest.h"
25#include "chrome/common/net/url_fetcher.h"
26#include "chrome/test/testing_profile.h"
27#include "chrome/test/thread_test_helper.h"
28#include "content/browser/browser_thread.h"
29#include "googleurl/src/gurl.h"
30#include "net/base/net_errors.h"
31#include "net/url_request/url_request_status.h"
32#include "testing/gmock/include/gmock/gmock.h"
33#include "testing/gtest/include/gtest/gtest.h"
34
35using namespace file_util;
36using ::testing::AnyNumber;
37using ::testing::DoAll;
38using ::testing::Invoke;
39using ::testing::Return;
40using ::testing::SetArgumentPointee;
41using ::testing::_;
42
43namespace chromeos {
44class ResolveChecker : public ThreadTestHelper {
45 public:
46  ResolveChecker(TestAttemptState* state,
47                 ParallelAuthenticator* auth,
48                 ParallelAuthenticator::AuthState expected)
49      : ThreadTestHelper(BrowserThread::IO),
50        state_(state),
51        auth_(auth),
52        expected_(expected) {
53  }
54  ~ResolveChecker() {}
55
56  virtual void RunTest() {
57    auth_->set_attempt_state(state_);
58    set_test_result(expected_ == auth_->ResolveState());
59  }
60
61 private:
62  TestAttemptState* state_;
63  ParallelAuthenticator* auth_;
64  ParallelAuthenticator::AuthState expected_;
65};
66
67class ParallelAuthenticatorTest : public ::testing::Test {
68 public:
69  ParallelAuthenticatorTest()
70      : message_loop_(MessageLoop::TYPE_UI),
71        ui_thread_(BrowserThread::UI, &message_loop_),
72        file_thread_(BrowserThread::FILE),
73        io_thread_(BrowserThread::IO),
74        username_("me@nowhere.org"),
75        password_("fakepass") {
76    hash_ascii_.assign("0a010000000000a0");
77    hash_ascii_.append(std::string(16, '0'));
78  }
79
80  ~ParallelAuthenticatorTest() {}
81
82  virtual void SetUp() {
83    chromeos::CrosLibrary::TestApi* test_api =
84        chromeos::CrosLibrary::Get()->GetTestApi();
85
86    loader_ = new MockLibraryLoader();
87    ON_CALL(*loader_, Load(_))
88        .WillByDefault(Return(true));
89    EXPECT_CALL(*loader_, Load(_))
90        .Times(AnyNumber());
91
92    test_api->SetLibraryLoader(loader_, true);
93
94    mock_library_ = new MockCryptohomeLibrary();
95    test_api->SetCryptohomeLibrary(mock_library_, true);
96    file_thread_.Start();
97    io_thread_.Start();
98
99    auth_ = new ParallelAuthenticator(&consumer_);
100    state_.reset(new TestAttemptState(username_,
101                                      password_,
102                                      hash_ascii_,
103                                      "",
104                                      "",
105                                      false));
106  }
107
108  // Tears down the test fixture.
109  virtual void TearDown() {
110    // Prevent bogus gMock leak check from firing.
111    chromeos::CrosLibrary::TestApi* test_api =
112        chromeos::CrosLibrary::Get()->GetTestApi();
113    test_api->SetLibraryLoader(NULL, false);
114    test_api->SetCryptohomeLibrary(NULL, false);
115  }
116
117  FilePath PopulateTempFile(const char* data, int data_len) {
118    FilePath out;
119    FILE* tmp_file = CreateAndOpenTemporaryFile(&out);
120    EXPECT_NE(tmp_file, static_cast<FILE*>(NULL));
121    EXPECT_EQ(WriteFile(out, data, data_len), data_len);
122    EXPECT_TRUE(CloseFile(tmp_file));
123    return out;
124  }
125
126  FilePath FakeLocalaccountFile(const std::string& ascii) {
127    FilePath exe_dir;
128    FilePath local_account_file;
129    PathService::Get(base::DIR_EXE, &exe_dir);
130    FILE* tmp_file = CreateAndOpenTemporaryFileInDir(exe_dir,
131                                                     &local_account_file);
132    int ascii_len = ascii.length();
133    EXPECT_NE(tmp_file, static_cast<FILE*>(NULL));
134    EXPECT_EQ(WriteFile(local_account_file, ascii.c_str(), ascii_len),
135              ascii_len);
136    EXPECT_TRUE(CloseFile(tmp_file));
137    return local_account_file;
138  }
139
140  void ReadLocalaccountFile(ParallelAuthenticator* auth,
141                            const std::string& filename) {
142    BrowserThread::PostTask(
143        BrowserThread::FILE, FROM_HERE,
144        NewRunnableMethod(auth,
145                          &ParallelAuthenticator::LoadLocalaccount,
146                          filename));
147    file_thread_.Stop();
148    file_thread_.Start();
149  }
150
151  // Allow test to fail and exit gracefully, even if OnLoginFailure()
152  // wasn't supposed to happen.
153  void FailOnLoginFailure() {
154    ON_CALL(consumer_, OnLoginFailure(_))
155        .WillByDefault(Invoke(MockConsumer::OnFailQuitAndFail));
156  }
157
158  // Allow test to fail and exit gracefully, even if OnLoginSuccess()
159  // wasn't supposed to happen.
160  void FailOnLoginSuccess() {
161    ON_CALL(consumer_, OnLoginSuccess(_, _, _, _))
162        .WillByDefault(Invoke(MockConsumer::OnSuccessQuitAndFail));
163  }
164
165  // Allow test to fail and exit gracefully, even if
166  // OnOffTheRecordLoginSuccess() wasn't supposed to happen.
167  void FailOnGuestLoginSuccess() {
168    ON_CALL(consumer_, OnOffTheRecordLoginSuccess())
169        .WillByDefault(Invoke(MockConsumer::OnGuestSuccessQuitAndFail));
170  }
171
172  void ExpectLoginFailure(const LoginFailure& failure) {
173    EXPECT_CALL(consumer_, OnLoginFailure(failure))
174        .WillOnce(Invoke(MockConsumer::OnFailQuit))
175        .RetiresOnSaturation();
176  }
177
178  void ExpectLoginSuccess(const std::string& username,
179                          const std::string& password,
180                          const GaiaAuthConsumer::ClientLoginResult& result,
181                          bool pending) {
182    EXPECT_CALL(consumer_, OnLoginSuccess(username, password, result, pending))
183        .WillOnce(Invoke(MockConsumer::OnSuccessQuit))
184        .RetiresOnSaturation();
185  }
186
187  void ExpectGuestLoginSuccess() {
188    EXPECT_CALL(consumer_, OnOffTheRecordLoginSuccess())
189        .WillOnce(Invoke(MockConsumer::OnGuestSuccessQuit))
190        .RetiresOnSaturation();
191  }
192
193  void ExpectPasswordChange() {
194    EXPECT_CALL(consumer_, OnPasswordChangeDetected(result_))
195        .WillOnce(Invoke(MockConsumer::OnMigrateQuit))
196        .RetiresOnSaturation();
197  }
198
199  void RunResolve(ParallelAuthenticator* auth, MessageLoop* loop) {
200    BrowserThread::PostTask(
201        BrowserThread::IO, FROM_HERE,
202        NewRunnableMethod(auth, &ParallelAuthenticator::Resolve));
203    loop->Run();
204  }
205
206  void SetAttemptState(ParallelAuthenticator* auth, TestAttemptState* state) {
207    auth->set_attempt_state(state);
208  }
209
210  MessageLoop message_loop_;
211  BrowserThread ui_thread_;
212  BrowserThread file_thread_;
213  BrowserThread io_thread_;
214
215  std::string username_;
216  std::string password_;
217  std::string hash_ascii_;
218  GaiaAuthConsumer::ClientLoginResult result_;
219
220  // Mocks, destroyed by CrosLibrary class.
221  MockCryptohomeLibrary* mock_library_;
222  MockLibraryLoader* loader_;
223
224  MockConsumer consumer_;
225  scoped_refptr<ParallelAuthenticator> auth_;
226  scoped_ptr<TestAttemptState> state_;
227};
228
229TEST_F(ParallelAuthenticatorTest, SaltToAscii) {
230  unsigned char fake_salt[8] = { 0 };
231  fake_salt[0] = 10;
232  fake_salt[1] = 1;
233  fake_salt[7] = 10 << 4;
234  std::vector<unsigned char> salt_v(fake_salt, fake_salt + sizeof(fake_salt));
235
236  ON_CALL(*mock_library_, GetSystemSalt())
237      .WillByDefault(Return(salt_v));
238  EXPECT_CALL(*mock_library_, GetSystemSalt())
239      .Times(1)
240      .RetiresOnSaturation();
241
242  EXPECT_EQ("0a010000000000a0", auth_->SaltAsAscii());
243}
244
245TEST_F(ParallelAuthenticatorTest, ReadLocalaccount) {
246  FilePath tmp_file_path = FakeLocalaccountFile(username_);
247
248  ReadLocalaccountFile(auth_.get(), tmp_file_path.BaseName().value());
249  EXPECT_EQ(auth_->localaccount_, username_);
250  Delete(tmp_file_path, false);
251}
252
253TEST_F(ParallelAuthenticatorTest, ReadLocalaccountTrailingWS) {
254  FilePath tmp_file_path =
255      FakeLocalaccountFile(base::StringPrintf("%s\n", username_.c_str()));
256
257  ReadLocalaccountFile(auth_.get(), tmp_file_path.BaseName().value());
258  EXPECT_EQ(auth_->localaccount_, username_);
259  Delete(tmp_file_path, false);
260}
261
262TEST_F(ParallelAuthenticatorTest, ReadNoLocalaccount) {
263  FilePath tmp_file_path = FakeLocalaccountFile(username_);
264  EXPECT_TRUE(Delete(tmp_file_path, false));  // Ensure non-existent file.
265
266  ReadLocalaccountFile(auth_.get(), tmp_file_path.BaseName().value());
267  EXPECT_EQ(auth_->localaccount_, std::string());
268}
269
270TEST_F(ParallelAuthenticatorTest, OnLoginSuccess) {
271  EXPECT_CALL(consumer_, OnLoginSuccess(username_, password_, result_, false))
272      .Times(1)
273      .RetiresOnSaturation();
274
275  SetAttemptState(auth_, state_.release());
276  auth_->OnLoginSuccess(result_, false);
277}
278
279TEST_F(ParallelAuthenticatorTest, OnPasswordChangeDetected) {
280  EXPECT_CALL(consumer_, OnPasswordChangeDetected(result_))
281      .Times(1)
282      .RetiresOnSaturation();
283  SetAttemptState(auth_, state_.release());
284  auth_->OnPasswordChangeDetected(result_);
285}
286
287TEST_F(ParallelAuthenticatorTest, ResolveNothingDone) {
288  scoped_refptr<ResolveChecker> checker(
289      new ResolveChecker(state_.release(),
290                         auth_.get(),
291                         ParallelAuthenticator::CONTINUE));
292  EXPECT_TRUE(checker->Run());
293}
294
295TEST_F(ParallelAuthenticatorTest, ResolvePossiblePwChange) {
296  // Set up state as though a cryptohome mount attempt has occurred
297  // and been rejected.
298  state_->PresetCryptohomeStatus(false,
299                                 chromeos::kCryptohomeMountErrorKeyFailure);
300  scoped_refptr<ResolveChecker> checker(
301      new ResolveChecker(state_.release(),
302                         auth_.get(),
303                         ParallelAuthenticator::POSSIBLE_PW_CHANGE));
304  EXPECT_TRUE(checker->Run());
305}
306
307TEST_F(ParallelAuthenticatorTest, DriveFailedMount) {
308  FailOnLoginSuccess();
309  ExpectLoginFailure(LoginFailure(LoginFailure::COULD_NOT_MOUNT_CRYPTOHOME));
310
311  // Set up state as though a cryptohome mount attempt has occurred
312  // and failed.
313  state_->PresetCryptohomeStatus(false, 0);
314  SetAttemptState(auth_, state_.release());
315
316  RunResolve(auth_.get(), &message_loop_);
317}
318
319TEST_F(ParallelAuthenticatorTest, DriveGuestLogin) {
320  ExpectGuestLoginSuccess();
321  FailOnLoginFailure();
322
323  // Set up mock cryptohome library to respond as though a tmpfs mount
324  // attempt has occurred and succeeded.
325  mock_library_->SetUp(true, 0);
326  EXPECT_CALL(*mock_library_, AsyncMountForBwsi(_))
327      .Times(1)
328      .RetiresOnSaturation();
329
330  auth_->LoginOffTheRecord();
331  message_loop_.Run();
332}
333
334TEST_F(ParallelAuthenticatorTest, DriveGuestLoginButFail) {
335  FailOnGuestLoginSuccess();
336  ExpectLoginFailure(LoginFailure(LoginFailure::COULD_NOT_MOUNT_TMPFS));
337
338  // Set up mock cryptohome library to respond as though a tmpfs mount
339  // attempt has occurred and failed.
340  mock_library_->SetUp(false, 0);
341  EXPECT_CALL(*mock_library_, AsyncMountForBwsi(_))
342      .Times(1)
343      .RetiresOnSaturation();
344
345  auth_->LoginOffTheRecord();
346  message_loop_.Run();
347}
348
349TEST_F(ParallelAuthenticatorTest, DriveDataResync) {
350  ExpectLoginSuccess(username_, password_, result_, false);
351  FailOnLoginFailure();
352
353  // Set up mock cryptohome library to respond successfully to a cryptohome
354  // remove attempt and a cryptohome create attempt (specified by the |true|
355  // argument to AsyncMount).
356  mock_library_->SetUp(true, 0);
357  EXPECT_CALL(*mock_library_, AsyncRemove(username_, _))
358      .Times(1)
359      .RetiresOnSaturation();
360  EXPECT_CALL(*mock_library_, AsyncMount(username_, hash_ascii_, true, _))
361      .Times(1)
362      .RetiresOnSaturation();
363
364  state_->PresetOnlineLoginStatus(result_, LoginFailure::None());
365  SetAttemptState(auth_, state_.release());
366
367  auth_->ResyncEncryptedData(result_);
368  message_loop_.Run();
369}
370
371TEST_F(ParallelAuthenticatorTest, DriveResyncFail) {
372  FailOnLoginSuccess();
373  ExpectLoginFailure(LoginFailure(LoginFailure::DATA_REMOVAL_FAILED));
374
375  // Set up mock cryptohome library to fail a cryptohome remove attempt.
376  mock_library_->SetUp(false, 0);
377  EXPECT_CALL(*mock_library_, AsyncRemove(username_, _))
378      .Times(1)
379      .RetiresOnSaturation();
380
381  SetAttemptState(auth_, state_.release());
382
383  auth_->ResyncEncryptedData(result_);
384  message_loop_.Run();
385}
386
387TEST_F(ParallelAuthenticatorTest, DriveRequestOldPassword) {
388  FailOnLoginSuccess();
389  ExpectPasswordChange();
390
391  state_->PresetCryptohomeStatus(false,
392                                chromeos::kCryptohomeMountErrorKeyFailure);
393  state_->PresetOnlineLoginStatus(result_, LoginFailure::None());
394  SetAttemptState(auth_, state_.release());
395
396  RunResolve(auth_.get(), &message_loop_);
397}
398
399TEST_F(ParallelAuthenticatorTest, DriveDataRecover) {
400  ExpectLoginSuccess(username_, password_, result_, false);
401  FailOnLoginFailure();
402
403  // Set up mock cryptohome library to respond successfully to a key migration.
404  mock_library_->SetUp(true, 0);
405  EXPECT_CALL(*mock_library_, AsyncMigrateKey(username_, _, hash_ascii_, _))
406      .Times(1)
407      .RetiresOnSaturation();
408  EXPECT_CALL(*mock_library_, AsyncMount(username_, hash_ascii_, false, _))
409      .Times(1)
410      .RetiresOnSaturation();
411  EXPECT_CALL(*mock_library_, GetSystemSalt())
412      .WillOnce(Return(CryptohomeBlob(2, 0)))
413      .RetiresOnSaturation();
414
415  state_->PresetOnlineLoginStatus(result_, LoginFailure::None());
416  SetAttemptState(auth_, state_.release());
417
418  auth_->RecoverEncryptedData(std::string(), result_);
419  message_loop_.Run();
420}
421
422TEST_F(ParallelAuthenticatorTest, DriveDataRecoverButFail) {
423  FailOnLoginSuccess();
424  ExpectPasswordChange();
425
426  // Set up mock cryptohome library to fail a key migration attempt,
427  // asserting that the wrong password was used.
428  mock_library_->SetUp(false, chromeos::kCryptohomeMountErrorKeyFailure);
429  EXPECT_CALL(*mock_library_, AsyncMigrateKey(username_, _, hash_ascii_, _))
430      .Times(1)
431      .RetiresOnSaturation();
432  EXPECT_CALL(*mock_library_, GetSystemSalt())
433      .WillOnce(Return(CryptohomeBlob(2, 0)))
434      .RetiresOnSaturation();
435
436  SetAttemptState(auth_, state_.release());
437
438  auth_->RecoverEncryptedData(std::string(), result_);
439  message_loop_.Run();
440}
441
442TEST_F(ParallelAuthenticatorTest, ResolveNoMount) {
443  // Set up state as though a cryptohome mount attempt has occurred
444  // and been rejected because the user doesn't exist.
445  state_->PresetCryptohomeStatus(
446      false,
447      chromeos::kCryptohomeMountErrorUserDoesNotExist);
448  scoped_refptr<ResolveChecker> checker(
449      new ResolveChecker(state_.release(),
450                         auth_.get(),
451                         ParallelAuthenticator::NO_MOUNT));
452  EXPECT_TRUE(checker->Run());
453}
454
455TEST_F(ParallelAuthenticatorTest, ResolveCreateNew) {
456  // Set up state as though a cryptohome mount attempt has occurred
457  // and been rejected because the user doesn't exist; additionally,
458  // an online auth attempt has completed successfully.
459  state_->PresetCryptohomeStatus(
460      false,
461      chromeos::kCryptohomeMountErrorUserDoesNotExist);
462  state_->PresetOnlineLoginStatus(GaiaAuthConsumer::ClientLoginResult(),
463                                 LoginFailure::None());
464  scoped_refptr<ResolveChecker> checker(
465      new ResolveChecker(state_.release(),
466                         auth_.get(),
467                         ParallelAuthenticator::CREATE_NEW));
468  EXPECT_TRUE(checker->Run());
469}
470
471TEST_F(ParallelAuthenticatorTest, DriveCreateForNewUser) {
472  ExpectLoginSuccess(username_, password_, result_, false);
473  FailOnLoginFailure();
474
475  // Set up mock cryptohome library to respond successfully to a cryptohome
476  // create attempt (specified by the |true| argument to AsyncMount).
477  mock_library_->SetUp(true, 0);
478  EXPECT_CALL(*mock_library_, AsyncMount(username_, hash_ascii_, true, _))
479      .Times(1)
480      .RetiresOnSaturation();
481
482  // Set up state as though a cryptohome mount attempt has occurred
483  // and been rejected because the user doesn't exist; additionally,
484  // an online auth attempt has completed successfully.
485  state_->PresetCryptohomeStatus(
486      false,
487      chromeos::kCryptohomeMountErrorUserDoesNotExist);
488  state_->PresetOnlineLoginStatus(GaiaAuthConsumer::ClientLoginResult(),
489                                 LoginFailure::None());
490  SetAttemptState(auth_, state_.release());
491
492  RunResolve(auth_.get(), &message_loop_);
493}
494
495TEST_F(ParallelAuthenticatorTest, DriveOfflineLogin) {
496  ExpectLoginSuccess(username_, password_, result_, false);
497  FailOnLoginFailure();
498
499  // Set up state as though a cryptohome mount attempt has occurred and
500  // succeeded.
501  state_->PresetCryptohomeStatus(true, 0);
502  GoogleServiceAuthError error =
503      GoogleServiceAuthError::FromConnectionError(net::ERR_CONNECTION_RESET);
504  state_->PresetOnlineLoginStatus(result_,
505                                 LoginFailure::FromNetworkAuthFailure(error));
506  SetAttemptState(auth_, state_.release());
507
508  RunResolve(auth_.get(), &message_loop_);
509}
510
511TEST_F(ParallelAuthenticatorTest, DriveOfflineLoginDelayedOnline) {
512  ExpectLoginSuccess(username_, password_, result_, true);
513  FailOnLoginFailure();
514
515  // Set up state as though a cryptohome mount attempt has occurred and
516  // succeeded.
517  state_->PresetCryptohomeStatus(true, 0);
518  // state_ is released further down.
519  SetAttemptState(auth_, state_.get());
520  RunResolve(auth_.get(), &message_loop_);
521
522  // Offline login has completed, so now we "complete" the online request.
523  GoogleServiceAuthError error(
524      GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
525  LoginFailure failure = LoginFailure::FromNetworkAuthFailure(error);
526  state_.release()->PresetOnlineLoginStatus(result_, failure);
527  ExpectLoginFailure(failure);
528
529  RunResolve(auth_.get(), &message_loop_);
530}
531
532TEST_F(ParallelAuthenticatorTest, DriveOfflineLoginGetNewPassword) {
533  ExpectLoginSuccess(username_, password_, result_, true);
534  FailOnLoginFailure();
535
536  // Set up mock cryptohome library to respond successfully to a key migration.
537  mock_library_->SetUp(true, 0);
538  EXPECT_CALL(*mock_library_, AsyncMigrateKey(username_,
539                                              state_->ascii_hash,
540                                              _,
541                                              _))
542      .Times(1)
543      .RetiresOnSaturation();
544  EXPECT_CALL(*mock_library_, GetSystemSalt())
545      .WillOnce(Return(CryptohomeBlob(2, 0)))
546      .RetiresOnSaturation();
547
548  // Set up state as though a cryptohome mount attempt has occurred and
549  // succeeded; also, an online request that never made it.
550  state_->PresetCryptohomeStatus(true, 0);
551  // state_ is released further down.
552  SetAttemptState(auth_, state_.get());
553  RunResolve(auth_.get(), &message_loop_);
554
555  // Offline login has completed, so now we "complete" the online request.
556  GoogleServiceAuthError error(
557      GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
558  LoginFailure failure = LoginFailure::FromNetworkAuthFailure(error);
559  state_.release()->PresetOnlineLoginStatus(result_, failure);
560  ExpectLoginFailure(failure);
561
562  RunResolve(auth_.get(), &message_loop_);
563
564  // After the request below completes, OnLoginSuccess gets called again.
565  ExpectLoginSuccess(username_, password_, result_, false);
566
567  MockFactory<SuccessFetcher> factory;
568  URLFetcher::set_factory(&factory);
569  TestingProfile profile;
570
571  auth_->RetryAuth(&profile,
572                   username_,
573                   std::string(),
574                   std::string(),
575                   std::string());
576  message_loop_.Run();
577}
578
579TEST_F(ParallelAuthenticatorTest, DriveOfflineLoginGetCaptchad) {
580  ExpectLoginSuccess(username_, password_, result_, true);
581  FailOnLoginFailure();
582  EXPECT_CALL(*mock_library_, GetSystemSalt())
583      .WillOnce(Return(CryptohomeBlob(2, 0)))
584      .RetiresOnSaturation();
585
586  // Set up state as though a cryptohome mount attempt has occurred and
587  // succeeded; also, an online request that never made it.
588  state_->PresetCryptohomeStatus(true, 0);
589  // state_ is released further down.
590  SetAttemptState(auth_, state_.get());
591  RunResolve(auth_.get(), &message_loop_);
592
593  // Offline login has completed, so now we "complete" the online request.
594  GoogleServiceAuthError error(
595      GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
596  LoginFailure failure = LoginFailure::FromNetworkAuthFailure(error);
597  state_.release()->PresetOnlineLoginStatus(result_, failure);
598  ExpectLoginFailure(failure);
599
600  RunResolve(auth_.get(), &message_loop_);
601
602  // After the request below completes, OnLoginSuccess gets called again.
603  failure = LoginFailure::FromNetworkAuthFailure(
604      GoogleServiceAuthError::FromCaptchaChallenge(
605          CaptchaFetcher::GetCaptchaToken(),
606          GURL(CaptchaFetcher::GetCaptchaUrl()),
607          GURL(CaptchaFetcher::GetUnlockUrl())));
608  ExpectLoginFailure(failure);
609
610  MockFactory<CaptchaFetcher> factory;
611  URLFetcher::set_factory(&factory);
612  TestingProfile profile;
613
614  auth_->RetryAuth(&profile,
615                   username_,
616                   std::string(),
617                   std::string(),
618                   std::string());
619  message_loop_.Run();
620}
621
622TEST_F(ParallelAuthenticatorTest, DriveOnlineLogin) {
623  GaiaAuthConsumer::ClientLoginResult success("sid", "lsid", "", "");
624  ExpectLoginSuccess(username_, password_, success, false);
625  FailOnLoginFailure();
626
627  // Set up state as though a cryptohome mount attempt has occurred and
628  // succeeded.
629  state_->PresetCryptohomeStatus(true, 0);
630  state_->PresetOnlineLoginStatus(success, LoginFailure::None());
631  SetAttemptState(auth_, state_.release());
632
633  RunResolve(auth_.get(), &message_loop_);
634}
635
636TEST_F(ParallelAuthenticatorTest, DriveNeedNewPassword) {
637  FailOnLoginSuccess();  // Set failing on success as the default...
638  // ...but expect ONE successful login first.
639  ExpectLoginSuccess(username_, password_, result_, true);
640  GoogleServiceAuthError error(
641      GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
642  LoginFailure failure = LoginFailure::FromNetworkAuthFailure(error);
643  ExpectLoginFailure(failure);
644
645  // Set up state as though a cryptohome mount attempt has occurred and
646  // succeeded.
647  state_->PresetCryptohomeStatus(true, 0);
648  state_->PresetOnlineLoginStatus(result_, failure);
649  SetAttemptState(auth_, state_.release());
650
651  RunResolve(auth_.get(), &message_loop_);
652}
653
654TEST_F(ParallelAuthenticatorTest, DriveLocalLogin) {
655  ExpectGuestLoginSuccess();
656  FailOnLoginFailure();
657
658  // Set up mock cryptohome library to respond as though a tmpfs mount
659  // attempt has occurred and succeeded.
660  mock_library_->SetUp(true, 0);
661  EXPECT_CALL(*mock_library_, AsyncMountForBwsi(_))
662      .Times(1)
663      .RetiresOnSaturation();
664
665  // Pre-set test state as though an online login attempt failed to complete,
666  // and that a cryptohome mount attempt failed because the user doesn't exist.
667  GoogleServiceAuthError error =
668      GoogleServiceAuthError::FromConnectionError(net::ERR_CONNECTION_RESET);
669  LoginFailure failure =
670      LoginFailure::FromNetworkAuthFailure(error);
671  state_->PresetOnlineLoginStatus(result_, failure);
672  state_->PresetCryptohomeStatus(
673      false,
674      chromeos::kCryptohomeMountErrorUserDoesNotExist);
675  SetAttemptState(auth_, state_.release());
676
677  // Deal with getting the localaccount file
678  FilePath tmp_file_path = FakeLocalaccountFile(username_);
679  ReadLocalaccountFile(auth_.get(), tmp_file_path.BaseName().value());
680
681  RunResolve(auth_.get(), &message_loop_);
682
683  Delete(tmp_file_path, false);
684}
685
686TEST_F(ParallelAuthenticatorTest, DriveUnlock) {
687  ExpectLoginSuccess(username_, std::string(), result_, false);
688  FailOnLoginFailure();
689
690  // Set up mock cryptohome library to respond successfully to a cryptohome
691  // key-check attempt.
692  mock_library_->SetUp(true, 0);
693  EXPECT_CALL(*mock_library_, AsyncCheckKey(username_, _, _))
694      .Times(1)
695      .RetiresOnSaturation();
696  EXPECT_CALL(*mock_library_, GetSystemSalt())
697      .WillOnce(Return(CryptohomeBlob(2, 0)))
698      .RetiresOnSaturation();
699
700  auth_->AuthenticateToUnlock(username_, "");
701  message_loop_.Run();
702}
703
704TEST_F(ParallelAuthenticatorTest, DriveLocalUnlock) {
705  ExpectLoginSuccess(username_, std::string(), result_, false);
706  FailOnLoginFailure();
707
708  // Set up mock cryptohome library to fail a cryptohome key-check
709  // attempt.
710  mock_library_->SetUp(false, 0);
711  EXPECT_CALL(*mock_library_, AsyncCheckKey(username_, _, _))
712      .Times(1)
713      .RetiresOnSaturation();
714  EXPECT_CALL(*mock_library_, GetSystemSalt())
715      .WillOnce(Return(CryptohomeBlob(2, 0)))
716      .RetiresOnSaturation();
717
718  // Deal with getting the localaccount file
719  FilePath tmp_file_path = FakeLocalaccountFile(username_);
720  ReadLocalaccountFile(auth_.get(), tmp_file_path.BaseName().value());
721
722  auth_->AuthenticateToUnlock(username_, "");
723  message_loop_.Run();
724
725  Delete(tmp_file_path, false);
726}
727
728}  // namespace chromeos
729