diff options
22 files changed, 991 insertions, 365 deletions
diff --git a/chrome/browser/chromeos/login/authenticator.h b/chrome/browser/chromeos/login/authenticator.h index 33dfe13..06a0041 100644 --- a/chrome/browser/chromeos/login/authenticator.h +++ b/chrome/browser/chromeos/login/authenticator.h @@ -11,6 +11,7 @@ #include "base/ref_counted.h" #include "chrome/browser/chrome_thread.h" #include "chrome/browser/chromeos/login/login_status_consumer.h" +#include "chrome/common/net/gaia/gaia_auth_consumer.h" class Profile; @@ -50,7 +51,8 @@ class Authenticator : public base::RefCountedThreadSafe<Authenticator> { // These methods must be called on the UI thread, as they make DBus calls // and also call back to the login UI. - virtual void OnLoginSuccess(const std::string& credentials) = 0; + virtual void OnLoginSuccess( + const GaiaAuthConsumer::ClientLoginResult& credentials) = 0; virtual void OnLoginFailure(const std::string& data) = 0; protected: diff --git a/chrome/browser/chromeos/login/existing_user_controller.cc b/chrome/browser/chromeos/login/existing_user_controller.cc index 23be7a5..ad3ab4d 100644 --- a/chrome/browser/chromeos/login/existing_user_controller.cc +++ b/chrome/browser/chromeos/login/existing_user_controller.cc @@ -333,7 +333,8 @@ void ExistingUserController::ShowError(int error_id, } void ExistingUserController::OnLoginSuccess(const std::string& username, - const std::string& credentials) { + const GaiaAuthConsumer::ClientLoginResult& credentials) { + AppendStartUrlToCmdline(); if (selected_view_index_ + 1 == controllers_.size()) { // For new user login don't launch browser until we pass image screen. diff --git a/chrome/browser/chromeos/login/existing_user_controller.h b/chrome/browser/chromeos/login/existing_user_controller.h index 82a6589..188c9f2 100644 --- a/chrome/browser/chromeos/login/existing_user_controller.h +++ b/chrome/browser/chromeos/login/existing_user_controller.h @@ -82,7 +82,7 @@ class ExistingUserController : public WmMessageListener::Observer, // LoginStatusConsumer: virtual void OnLoginFailure(const std::string& error); virtual void OnLoginSuccess(const std::string& username, - const std::string& credentials); + const GaiaAuthConsumer::ClientLoginResult& credentials); virtual void OnOffTheRecordLoginSuccess(); // Overridden from views::InfoBubbleDelegate. diff --git a/chrome/browser/chromeos/login/google_authenticator.cc b/chrome/browser/chromeos/login/google_authenticator.cc index 57b7edd..a6238db 100644 --- a/chrome/browser/chromeos/login/google_authenticator.cc +++ b/chrome/browser/chromeos/login/google_authenticator.cc @@ -21,11 +21,10 @@ #include "chrome/browser/chromeos/login/auth_response_handler.h" #include "chrome/browser/chromeos/login/authentication_notification_details.h" #include "chrome/browser/chromeos/login/login_status_consumer.h" -#include "chrome/browser/net/chrome_url_request_context.h" #include "chrome/browser/profile.h" #include "chrome/browser/profile_manager.h" #include "chrome/common/chrome_paths.h" -#include "chrome/common/net/url_fetcher.h" +#include "chrome/common/net/gaia/gaia_authenticator2.h" #include "chrome/common/notification_service.h" #include "net/base/load_flags.h" #include "net/base/net_errors.h" @@ -42,35 +41,6 @@ using file_util::ReadFileToString; namespace chromeos { // static -const char GoogleAuthenticator::kCookiePersistence[] = "true"; -// static -const char GoogleAuthenticator::kAccountType[] = "HOSTED_OR_GOOGLE"; -// static -const char GoogleAuthenticator::kSource[] = "chromeos"; -// static -// Service name for Google Contacts API. API is used to get user's image. -const char GoogleAuthenticator::kService[] = "cp"; -// static -const char GoogleAuthenticator::kFormat[] = - "Email=%s&" - "Passwd=%s&" - "PersistentCookie=%s&" - "accountType=%s&" - "source=%s&" - "service=%s"; -// static -const char GoogleAuthenticator::kFormatCaptcha[] = - "Email=%s&" - "Passwd=%s&" - "PersistentCookie=%s&" - "accountType=%s&" - "source=%s&" - "service=%s&" - "logintoken=%s&" - "logincaptcha=%s"; -// static -const char GoogleAuthenticator::kSecondFactor[] = "Info=InvalidSecondFactor"; -// static const char GoogleAuthenticator::kLocalaccountFile[] = "localaccount"; // static @@ -80,33 +50,49 @@ const int kPassHashLen = 32; GoogleAuthenticator::GoogleAuthenticator(LoginStatusConsumer* consumer) : Authenticator(consumer), - fetcher_(NULL), - getter_(NULL), checked_for_localaccount_(false), unlock_(false), - try_again_(true), - fetch_completed_(false) { + try_again_(true) { CHECK(chromeos::CrosLibrary::Get()->EnsureLoaded()); } -GoogleAuthenticator::~GoogleAuthenticator() { - delete fetcher_; +GoogleAuthenticator::~GoogleAuthenticator() {} + +void GoogleAuthenticator::CancelClientLogin() { + if (gaia_authenticator_->HasPendingFetch()) { + gaia_authenticator_->CancelRequest(); + OnLoginFailure("Login has timed out; please try again!"); + } } -// static -URLFetcher* GoogleAuthenticator::CreateClientLoginFetcher( - URLRequestContextGetter* getter, - const std::string& body, - URLFetcher::Delegate* delegate) { - URLFetcher* to_return = - URLFetcher::Create(0, - GURL(AuthResponseHandler::kClientLoginUrl), - URLFetcher::POST, - delegate); - to_return->set_request_context(getter); - to_return->set_load_flags(net::LOAD_DO_NOT_SEND_COOKIES); - to_return->set_upload_data("application/x-www-form-urlencoded", body); - return to_return; +void GoogleAuthenticator::TryClientLogin() { + gaia_authenticator_->StartClientLogin(username_, + password_, + GaiaAuthenticator2::kContactsService, + login_token_, + login_captcha_); + ChromeThread::PostDelayedTask( + ChromeThread::UI, FROM_HERE, + NewRunnableMethod(this, + &GoogleAuthenticator::CancelClientLogin), + kClientLoginTimeoutMs); +} + +void GoogleAuthenticator::PrepareClientLoginAttempt( + const std::string& password, + const std::string& token, + const std::string& captcha) { + + // Save so we can retry. + password_.assign(password); + login_token_.assign(token); + login_captcha_.assign(captcha); +} + +void GoogleAuthenticator::ClearClientLoginAttempt() { + password_.clear(); + login_token_.clear(); + login_captcha_.clear(); } bool GoogleAuthenticator::AuthenticateToLogin( @@ -116,33 +102,18 @@ bool GoogleAuthenticator::AuthenticateToLogin( const std::string& login_token, const std::string& login_captcha) { unlock_ = false; - getter_ = profile->GetRequestContext(); - - // TODO(cmasone): be more careful about zeroing memory that stores - // the user's password. - if (login_token.empty() || login_captcha.empty()) { - request_body_ = StringPrintf(kFormat, - UrlEncodeString(username).c_str(), - UrlEncodeString(password).c_str(), - kCookiePersistence, - kAccountType, - kSource, - kService); - } else { - request_body_ = StringPrintf(kFormatCaptcha, - UrlEncodeString(username).c_str(), - UrlEncodeString(password).c_str(), - kCookiePersistence, - kAccountType, - kSource, - kService, - UrlEncodeString(login_token).c_str(), - UrlEncodeString(login_captcha).c_str()); - } + // TODO(cmasone): Figure out how to parallelize fetch, username/password // processing without impacting testability. username_.assign(Canonicalize(username)); ascii_hash_.assign(HashPassword(password)); + + gaia_authenticator_.reset( + new GaiaAuthenticator2(this, + GaiaAuthenticator2::kChromeOSSource, + profile->GetRequestContext())); + // Will be used for retries. + PrepareClientLoginAttempt(password, login_token, login_captcha); TryClientLogin(); return true; } @@ -175,57 +146,59 @@ void GoogleAuthenticator::LoginOffTheRecord() { } } -void GoogleAuthenticator::OnURLFetchComplete(const URLFetcher* source, - const GURL& url, - const URLRequestStatus& status, - int response_code, - const ResponseCookies& cookies, - const std::string& data) { - fetch_completed_ = true; - if (status.is_success() && response_code == kHttpSuccess) { - LOG(INFO) << "Online login successful!"; - ChromeThread::PostTask( - ChromeThread::UI, FROM_HERE, - NewRunnableMethod(this, &GoogleAuthenticator::OnLoginSuccess, data)); - } else if (!status.is_success()) { - if (status.status() == URLRequestStatus::CANCELED) { - if (try_again_) { - try_again_ = false; - LOG(ERROR) << "Login attempt canceled!?!? Trying again."; - TryClientLogin(); - fetch_completed_ = false; - return; - } - LOG(ERROR) << "Login attempt canceled again? Already retried..."; +void GoogleAuthenticator::OnClientLoginSuccess( + const GaiaAuthConsumer::ClientLoginResult& result) { + + LOG(INFO) << "Online login successful!"; + ClearClientLoginAttempt(); + + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableMethod(this, &GoogleAuthenticator::OnLoginSuccess, result)); +} + +void GoogleAuthenticator::OnClientLoginFailure( + const GaiaAuthConsumer::ClientLoginError& error) { + + if (error.code == GaiaAuthConsumer::REQUEST_CANCELED) { + if (try_again_) { + try_again_ = false; + LOG(ERROR) << "Login attempt canceled!?!? Trying again."; + TryClientLogin(); + return; } - LOG(WARNING) << "Could not reach Google Accounts servers: errno " - << status.os_error(); + LOG(ERROR) << "Login attempt canceled again? Already retried..."; + } + + ClearClientLoginAttempt(); + + if (error.code == GaiaAuthConsumer::TWO_FACTOR) { + OnClientLoginSuccess(GaiaAuthConsumer::ClientLoginResult()); + return; + } + + if (error.code == GaiaAuthConsumer::NETWORK_ERROR) { // The fetch failed for network reasons, try offline login. LoadLocalaccount(kLocalaccountFile); ChromeThread::PostTask( ChromeThread::UI, FROM_HERE, NewRunnableMethod(this, &GoogleAuthenticator::CheckOffline, - net::ErrorToString(status.os_error()))); - } else { - if (IsSecondFactorSuccess(data)) { - ChromeThread::PostTask( - ChromeThread::UI, FROM_HERE, - NewRunnableMethod(this, - &GoogleAuthenticator::OnLoginSuccess, - std::string())); - } else { - // The fetch succeeded, but ClientLogin said no. - LoadLocalaccount(kLocalaccountFile); - ChromeThread::PostTask( - ChromeThread::UI, FROM_HERE, - NewRunnableMethod(this, - &GoogleAuthenticator::CheckLocalaccount, - data)); - } + net::ErrorToString(error.network_error))); + return; } + + // The fetch succeeded, but ClientLogin said no, or we exhausted retries. + LoadLocalaccount(kLocalaccountFile); + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableMethod(this, + &GoogleAuthenticator::CheckLocalaccount, + error.data)); } -void GoogleAuthenticator::OnLoginSuccess(const std::string& data) { +void GoogleAuthenticator::OnLoginSuccess( + const GaiaAuthConsumer::ClientLoginResult& result) { + // Send notification of success AuthenticationNotificationDetails details(true); NotificationService::current()->Notify( @@ -238,10 +211,10 @@ void GoogleAuthenticator::OnLoginSuccess(const std::string& data) { (CrosLibrary::Get()->GetCryptohomeLibrary()->Mount(username_.c_str(), ascii_hash_.c_str(), &mount_error))) { - consumer_->OnLoginSuccess(username_, data); + consumer_->OnLoginSuccess(username_, result); } else if (!unlock_ && mount_error == chromeos::kCryptohomeMountErrorKeyFailure) { - consumer_->OnPasswordChangeDetected(data); + consumer_->OnPasswordChangeDetected(result); } else { OnLoginFailure("Could not mount cryptohome"); } @@ -254,7 +227,7 @@ void GoogleAuthenticator::CheckOffline(const std::string& error) { ascii_hash_.c_str())) { // The fetch didn't succeed, but offline login did. LOG(INFO) << "Offline login successful!"; - OnLoginSuccess(std::string()); + OnLoginSuccess(GaiaAuthConsumer::ClientLoginResult()); } else { // We couldn't hit the network, and offline login failed. GoogleAuthenticator::CheckLocalaccount(error); @@ -266,41 +239,44 @@ void GoogleAuthenticator::CheckLocalaccount(const std::string& error) { if (!localaccount_.empty() && localaccount_ == username_ && CrosLibrary::Get()->GetCryptohomeLibrary()->MountForBwsi(&mount_error)) { LOG(WARNING) << "Logging in with localaccount: " << localaccount_; - consumer_->OnLoginSuccess(username_, std::string()); + consumer_->OnLoginSuccess(username_, GaiaAuthConsumer::ClientLoginResult()); } else { OnLoginFailure(error); } } -void GoogleAuthenticator::OnLoginFailure(const std::string& data) { +void GoogleAuthenticator::OnLoginFailure(const std::string& error) { // Send notification of failure AuthenticationNotificationDetails details(false); NotificationService::current()->Notify( NotificationType::LOGIN_AUTHENTICATION, NotificationService::AllSources(), Details<AuthenticationNotificationDetails>(&details)); - LOG(WARNING) << "Login failed: " << data; + LOG(WARNING) << "Login failed: " << error; // TODO(cmasone): what can we do to expose these OS/server-side error strings // in an internationalizable way? - consumer_->OnLoginFailure(data); + consumer_->OnLoginFailure(error); } void GoogleAuthenticator::DoPasswordChange(const std::string& old_password, - const std::string& credentials) { + const GaiaAuthConsumer::ClientLoginResult& result) { + std::string old_hash = HashPassword(old_password); if (CrosLibrary::Get()->GetCryptohomeLibrary()->MigrateKey(username_, old_hash, ascii_hash_)) { - OnLoginSuccess(credentials); + OnLoginSuccess(result); return; } // User seems to have given us the wrong old password... - consumer_->OnPasswordChangeDetected(credentials); + consumer_->OnPasswordChangeDetected(result); } -void GoogleAuthenticator::SkipPasswordChange(const std::string& credentials) { +void GoogleAuthenticator::SkipPasswordChange( + const GaiaAuthConsumer::ClientLoginResult& result) { + if (CrosLibrary::Get()->GetCryptohomeLibrary()->Remove(username_)) { - OnLoginSuccess(credentials); + OnLoginSuccess(result); } else { OnLoginFailure("Could not destroy your old data!"); } @@ -376,35 +352,6 @@ std::string GoogleAuthenticator::SaltAsAscii() { } } -void GoogleAuthenticator::Cancel() { - if (fetch_completed_) - return; - - if (fetcher_) { - delete fetcher_; - fetcher_ = NULL; - } - OnLoginFailure("Login has timed out; please try again!"); -} - -void GoogleAuthenticator::TryClientLogin() { - if (fetcher_) - delete fetcher_; - fetcher_ = CreateClientLoginFetcher(getter_, request_body_, this); - fetcher_->Start(); - ChromeThread::PostDelayedTask( - ChromeThread::UI, FROM_HERE, - NewRunnableMethod(this, - &GoogleAuthenticator::Cancel), kClientLoginTimeoutMs); -} - -// static -bool GoogleAuthenticator::IsSecondFactorSuccess( - const std::string& alleged_error) { - return alleged_error.find(GoogleAuthenticator::kSecondFactor) != - std::string::npos; -} - // static bool GoogleAuthenticator::BinaryToHex(const std::vector<unsigned char>& binary, const unsigned int binary_len, diff --git a/chrome/browser/chromeos/login/google_authenticator.h b/chrome/browser/chromeos/login/google_authenticator.h index f696981..f3b6e19 100644 --- a/chrome/browser/chromeos/login/google_authenticator.h +++ b/chrome/browser/chromeos/login/google_authenticator.h @@ -16,19 +16,20 @@ #include "chrome/browser/chromeos/cros/cros_library.h" #include "chrome/browser/chromeos/cros/cryptohome_library.h" #include "chrome/browser/chromeos/login/authenticator.h" -#include "chrome/common/net/url_fetcher.h" +#include "chrome/common/net/gaia/gaia_auth_consumer.h" // Authenticates a Chromium OS user against the Google Accounts ClientLogin API. class Profile; +class GaiaAuthenticator2; namespace chromeos { class GoogleAuthenticatorTest; class LoginStatusConsumer; -class GoogleAuthenticator : public Authenticator, - public URLFetcher::Delegate { +class GoogleAuthenticator : public Authenticator, public GaiaAuthConsumer { + public: explicit GoogleAuthenticator(LoginStatusConsumer* consumer); virtual ~GoogleAuthenticator(); @@ -38,7 +39,6 @@ class GoogleAuthenticator : public Authenticator, // consumer_->OnLoginSuccess() with the |username| and a vector of // authentication cookies or a callback to consumer_->OnLoginFailure() with // an error message. Uses |profile| when doing URL fetches. - // Should be called on the FILE thread! // Optionally could pass CAPTCHA challenge token - |login_token| and // |login_captcha| string that user has entered. // @@ -60,17 +60,6 @@ class GoogleAuthenticator : public Authenticator, // Mounts tmpfs and notifies consumer on the success/failure. void LoginOffTheRecord(); - // Overridden from URLFetcher::Delegate - // When the authentication attempt comes back, will call - // consumer_->OnLoginSuccess(|username|). - // On any failure, will call consumer_->OnLoginFailure(). - virtual void OnURLFetchComplete(const URLFetcher* source, - const GURL& url, - const URLRequestStatus& status, - int response_code, - const ResponseCookies& cookies, - const std::string& data); - // Public for testing. void set_system_salt(const chromeos::CryptohomeBlob& new_salt) { system_salt_ = new_salt; @@ -86,30 +75,30 @@ class GoogleAuthenticator : public Authenticator, // These methods must be called on the UI thread, as they make DBus calls // and also call back to the login UI. - void OnLoginSuccess(const std::string& credentials); + void OnLoginSuccess(const GaiaAuthConsumer::ClientLoginResult& result); void CheckOffline(const std::string& error); void CheckLocalaccount(const std::string& error); - void OnLoginFailure(const std::string& data); + void OnLoginFailure(const std::string& error); // If a password logs the user in online, but cannot be used to // mount his cryptohome, we expect that a password change has // occurred. Call this method to migrate the user's encrypted data // forward to use his new password. |old_password| is the password - // his data was last encrypted with, |credentials| is the blob of auth + // his data was last encrypted with, |result| is the blob of auth // data passed back through OnPasswordChangeDetected(). // // Call this on the UI thread. void DoPasswordChange(const std::string& old_password, - const std::string& credentials); + const GaiaAuthConsumer::ClientLoginResult& result); // If a password logs the user in online, but cannot be used to // mount his cryptohome, we expect that a password change has // occurred. Call this method to erase the user's encrypted data - // and create a new cryptohome. |credentials| is the blob of auth + // and create a new cryptohome. |result| is the blob of auth // data passed back through OnPasswordChangeDetected(). // // Call this on the UI thread. - void SkipPasswordChange(const std::string& credentials); + void SkipPasswordChange(const GaiaAuthConsumer::ClientLoginResult& result); void Cancel(); @@ -119,7 +108,14 @@ class GoogleAuthenticator : public Authenticator, // http://mail.google.com/support/bin/answer.py?hl=en&ctx=mail&answer=10313# static std::string Canonicalize(const std::string& email_address); + // Callbacks from GaiaAuthenticator2 + virtual void OnClientLoginFailure( + const GaiaAuthConsumer::ClientLoginError& error); + virtual void OnClientLoginSuccess( + const GaiaAuthConsumer::ClientLoginResult& result); + private: + // If we don't have the system salt yet, loads it from the CryptohomeLibrary. void LoadSystemSalt(); @@ -137,14 +133,24 @@ class GoogleAuthenticator : public Authenticator, // Returns the ascii encoding of the system salt. std::string SaltAsAscii(); - // Kicks off a new ClientLogin attempt, canceling any existing attempts. - // It is an error to call this before populating |getter_| and |request_body_| + // Save the current login attempt for use on the next TryClientLogin + // attempt. + void PrepareClientLoginAttempt(const std::string& password, + const std::string& login_token, + const std::string& login_captcha); + // Clear any cached credentials after we've given up trying to authenticate. + void ClearClientLoginAttempt(); + + // Start a client login attempt. You should set up the + // GaiaAuthenticator2 first. + // Reuses existing credentials from the last attempt. You should + // PrepareClientLoginAttempt before calling this. void TryClientLogin(); - // To support internal two-factor authentication, check to see if - // |alleged_error| is actually indicating the successful completion - // of the first half of a two-factor login. - static bool IsSecondFactorSuccess(const std::string& alleged_error); + // A callback for use on the UI thread. Cancel the current login + // attempt, and produce a login failure. + void CancelClientLogin(); + // Converts the binary data |binary| into an ascii hex string and stores // it in |hex_string|. Not guaranteed to be NULL-terminated. @@ -154,21 +160,6 @@ class GoogleAuthenticator : public Authenticator, char* hex_string, const unsigned int len); - // Create and return a URLFetcher that is set up to perform a ClientLogin - // authentication attempt. Caller takes ownership. - static URLFetcher* CreateClientLoginFetcher(URLRequestContextGetter* getter, - const std::string& body, - URLFetcher::Delegate* delegate); - - // Constants to use in the ClientLogin request POST body. - static const char kCookiePersistence[]; - static const char kAccountType[]; - static const char kSource[]; - static const char kService[]; - - // The format of said POST body. - static const char kFormat[]; - // The format of said POST body when CAPTCHA token & answer are specified. static const char kFormatCaptcha[]; @@ -179,20 +170,24 @@ class GoogleAuthenticator : public Authenticator, // Name of a file, next to chrome, that contains a local account username. static const char kLocalaccountFile[]; + // Handles all net communications with Gaia. + scoped_ptr<GaiaAuthenticator2> gaia_authenticator_; + // Milliseconds until we timeout our attempt to hit ClientLogin. static const int kClientLoginTimeoutMs; - URLFetcher* fetcher_; - URLRequestContextGetter* getter_; std::string username_; + // These fields are saved so we can retry client login. + std::string password_; + std::string login_token_; + std::string login_captcha_; + std::string ascii_hash_; - std::string request_body_; chromeos::CryptohomeBlob system_salt_; std::string localaccount_; - bool checked_for_localaccount_; // needed becasuse empty localaccount_ is ok. + bool checked_for_localaccount_; // needed because empty localaccount_ is ok. bool unlock_; // True if authenticating to unlock the computer. bool try_again_; // True if we're willing to retry the login attempt. - bool fetch_completed_; friend class GoogleAuthenticatorTest; FRIEND_TEST_ALL_PREFIXES(GoogleAuthenticatorTest, SaltToAscii); diff --git a/chrome/browser/chromeos/login/google_authenticator_unittest.cc b/chrome/browser/chromeos/login/google_authenticator_unittest.cc index 047f837..7dc58b0 100644 --- a/chrome/browser/chromeos/login/google_authenticator_unittest.cc +++ b/chrome/browser/chromeos/login/google_authenticator_unittest.cc @@ -12,21 +12,22 @@ #include "base/ref_counted.h" #include "base/scoped_ptr.h" #include "base/string_util.h" +#include "chrome/browser/chrome_thread.h" #include "chrome/browser/chromeos/cros/mock_cryptohome_library.h" #include "chrome/browser/chromeos/cros/mock_library_loader.h" #include "chrome/browser/chromeos/login/client_login_response_handler.h" #include "chrome/browser/chromeos/login/google_authenticator.h" #include "chrome/browser/chromeos/login/issue_response_handler.h" #include "chrome/browser/chromeos/login/mock_auth_response_handler.h" -#include "chrome/browser/chrome_thread.h" #include "chrome/common/chrome_paths.h" +#include "chrome/common/net/gaia/gaia_authenticator2_unittest.h" #include "chrome/common/net/url_fetcher.h" #include "chrome/test/testing_profile.h" #include "googleurl/src/gurl.h" #include "net/base/net_errors.h" #include "net/url_request/url_request_status.h" -#include "testing/gtest/include/gtest/gtest.h" #include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" using namespace file_util; using ::testing::AnyNumber; @@ -44,9 +45,10 @@ class MockConsumer : public LoginStatusConsumer { ~MockConsumer() {} MOCK_METHOD1(OnLoginFailure, void(const std::string& error)); MOCK_METHOD2(OnLoginSuccess, void(const std::string& username, - const std::string& data)); + const GaiaAuthConsumer::ClientLoginResult& result)); MOCK_METHOD0(OnOffTheRecordLoginSuccess, void(void)); - MOCK_METHOD1(OnPasswordChangeDetected, void(const std::string& credentials)); + MOCK_METHOD1(OnPasswordChangeDetected, + void(const GaiaAuthConsumer::ClientLoginResult& result)); }; class GoogleAuthenticatorTest : public ::testing::Test { @@ -121,8 +123,7 @@ class GoogleAuthenticatorTest : public ::testing::Test { unsigned char fake_hash_[32]; std::string hash_ascii_; std::string username_; - std::string data_; - ResponseCookies cookies_; + GaiaAuthConsumer::ClientLoginResult result_; // Mocks, destroyed by CrosLibrary class. MockCryptohomeLibrary* mock_library_; MockLibraryLoader* loader_; @@ -147,18 +148,6 @@ TEST_F(GoogleAuthenticatorTest, SaltToAscii) { EXPECT_EQ("0a010000000000a0", auth->SaltAsAscii()); } -TEST_F(GoogleAuthenticatorTest, CheckTwoFactorResponse) { - std::string response = - StringPrintf("Error=BadAuthentication\n%s\n", - GoogleAuthenticator::kSecondFactor); - EXPECT_TRUE(GoogleAuthenticator::IsSecondFactorSuccess(response)); -} - -TEST_F(GoogleAuthenticatorTest, CheckNormalErrorCode) { - std::string response = "Error=BadAuthentication\n"; - EXPECT_FALSE(GoogleAuthenticator::IsSecondFactorSuccess(response)); -} - TEST_F(GoogleAuthenticatorTest, EmailAddressNoOp) { const char lower_case[] = "user@what.com"; EXPECT_EQ(lower_case, GoogleAuthenticator::Canonicalize(lower_case)); @@ -242,7 +231,7 @@ TEST_F(GoogleAuthenticatorTest, OnLoginSuccess) { scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer)); auth->set_password_hash(hash_ascii_); auth->set_username(username_); - auth->OnLoginSuccess(data_); + auth->OnLoginSuccess(result_); } TEST_F(GoogleAuthenticatorTest, MountFailure) { @@ -254,13 +243,13 @@ TEST_F(GoogleAuthenticatorTest, MountFailure) { scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer)); PrepForLogin(auth.get()); - auth->OnLoginSuccess(data_); + auth->OnLoginSuccess(result_); } TEST_F(GoogleAuthenticatorTest, PasswordChange) { MockConsumer consumer; - EXPECT_CALL(consumer, OnPasswordChangeDetected(data_)); - EXPECT_CALL(consumer, OnLoginSuccess(username_, data_)); + EXPECT_CALL(consumer, OnPasswordChangeDetected(result_)); + EXPECT_CALL(consumer, OnLoginSuccess(username_, result_)); EXPECT_CALL(*mock_library_, Mount(username_, hash_ascii_, _)) .WillOnce( @@ -276,13 +265,13 @@ TEST_F(GoogleAuthenticatorTest, PasswordChange) { scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer)); PrepForLogin(auth.get()); - auth->OnLoginSuccess(data_); - auth->DoPasswordChange("whaty", data_); + auth->OnLoginSuccess(result_); + auth->DoPasswordChange("whaty", result_); } TEST_F(GoogleAuthenticatorTest, PasswordChangeWrongPassword) { MockConsumer consumer; - EXPECT_CALL(consumer, OnPasswordChangeDetected(data_)) + EXPECT_CALL(consumer, OnPasswordChangeDetected(result_)) .Times(2); EXPECT_CALL(*mock_library_, Mount(username_, hash_ascii_, _)) @@ -297,14 +286,14 @@ TEST_F(GoogleAuthenticatorTest, PasswordChangeWrongPassword) { scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer)); PrepForLogin(auth.get()); - auth->OnLoginSuccess(data_); - auth->DoPasswordChange("whaty", data_); + auth->OnLoginSuccess(result_); + auth->DoPasswordChange("whaty", result_); } TEST_F(GoogleAuthenticatorTest, ForgetOldData) { MockConsumer consumer; - EXPECT_CALL(consumer, OnPasswordChangeDetected(data_)); - EXPECT_CALL(consumer, OnLoginSuccess(username_, data_)); + EXPECT_CALL(consumer, OnPasswordChangeDetected(result_)); + EXPECT_CALL(consumer, OnLoginSuccess(username_, result_)); EXPECT_CALL(*mock_library_, Mount(username_, hash_ascii_, _)) .WillOnce( @@ -318,8 +307,8 @@ TEST_F(GoogleAuthenticatorTest, ForgetOldData) { scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer)); PrepForLogin(auth.get()); - auth->OnLoginSuccess(data_); - auth->SkipPasswordChange(data_); + auth->OnLoginSuccess(result_); + auth->SkipPasswordChange(result_); } TEST_F(GoogleAuthenticatorTest, LoginNetFailure) { @@ -328,9 +317,10 @@ TEST_F(GoogleAuthenticatorTest, LoginNetFailure) { int error_no = net::ERR_CONNECTION_RESET; std::string data(net::ErrorToString(error_no)); - GURL source; - URLRequestStatus status(URLRequestStatus::FAILED, error_no); + GaiaAuthConsumer::ClientLoginError error; + error.code = GaiaAuthConsumer::NETWORK_ERROR; + error.network_error = error_no; MockConsumer consumer; EXPECT_CALL(consumer, OnLoginFailure(data)) @@ -340,7 +330,7 @@ TEST_F(GoogleAuthenticatorTest, LoginNetFailure) { scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer)); PrepForLogin(auth.get()); - auth->OnURLFetchComplete(NULL, source, status, 0, cookies_, data); + auth->OnClientLoginFailure(error); message_loop.RunAllPending(); } @@ -348,96 +338,76 @@ TEST_F(GoogleAuthenticatorTest, LoginDenied) { MessageLoopForUI message_loop; ChromeThread ui_thread(ChromeThread::UI, &message_loop); - std::string data("Error: NO!"); - GURL source(AuthResponseHandler::kTokenAuthUrl); - - URLRequestStatus status(URLRequestStatus::SUCCESS, 0); + GaiaAuthConsumer::ClientLoginError error; + error.code = GaiaAuthConsumer::PERMISSION_DENIED; MockConsumer consumer; - EXPECT_CALL(consumer, OnLoginFailure(data)) + EXPECT_CALL(consumer, OnLoginFailure(_)) .Times(1); scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer)); PrepForLogin(auth.get()); - auth->OnURLFetchComplete(NULL, source, status, 403, cookies_, data); + auth->OnClientLoginFailure(error); message_loop.RunAllPending(); } -TEST_F(GoogleAuthenticatorTest, OfflineLogin) { +TEST_F(GoogleAuthenticatorTest, CaptchaErrorOutputted) { MessageLoopForUI message_loop; ChromeThread ui_thread(ChromeThread::UI, &message_loop); - int error_no = net::ERR_CONNECTION_RESET; - std::string data(net::ErrorToString(error_no)); - GURL source; - - URLRequestStatus status(URLRequestStatus::FAILED, error_no); + // TODO(chron): Swap out this captcha passing for actual captcha parsing. + GaiaAuthConsumer::ClientLoginError error; + error.code = GaiaAuthConsumer::PERMISSION_DENIED; + error.data = "Url=http://www.google.com/login/captcha\n" + "Error=CaptchaRequired\n" + "CaptchaToken=DQAAAGgA...dkI1LK9\n" + "CaptchaUrl=Captcha?ctoken=...\n"; MockConsumer consumer; - EXPECT_CALL(consumer, OnLoginSuccess(username_, data_)) + EXPECT_CALL(consumer, OnLoginFailure(error.data)) .Times(1); - EXPECT_CALL(*mock_library_, CheckKey(username_, hash_ascii_)) - .WillOnce(Return(true)); - EXPECT_CALL(*mock_library_, Mount(username_, hash_ascii_, _)) - .WillOnce(Return(true)); scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer)); PrepForLogin(auth.get()); - auth->OnURLFetchComplete(NULL, source, status, 0, cookies_, data); + auth->OnClientLoginFailure(error); message_loop.RunAllPending(); } -TEST_F(GoogleAuthenticatorTest, OnlineLogin) { +TEST_F(GoogleAuthenticatorTest, OfflineLogin) { MessageLoopForUI message_loop; ChromeThread ui_thread(ChromeThread::UI, &message_loop); - GURL source(AuthResponseHandler::kTokenAuthUrl); - URLRequestStatus status(URLRequestStatus::SUCCESS, 0); + GaiaAuthConsumer::ClientLoginError error; + error.code = GaiaAuthConsumer::NETWORK_ERROR; + error.network_error = net::ERR_CONNECTION_RESET; MockConsumer consumer; - EXPECT_CALL(consumer, OnLoginSuccess(username_, data_)) + EXPECT_CALL(consumer, OnLoginSuccess(username_, result_)) .Times(1); + EXPECT_CALL(*mock_library_, CheckKey(username_, hash_ascii_)) + .WillOnce(Return(true)); EXPECT_CALL(*mock_library_, Mount(username_, hash_ascii_, _)) .WillOnce(Return(true)); scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer)); PrepForLogin(auth.get()); - auth->OnURLFetchComplete(NULL, - source, - status, - kHttpSuccess, - cookies_, - std::string()); + auth->OnClientLoginFailure(error); message_loop.RunAllPending(); } -TEST_F(GoogleAuthenticatorTest, TwoFactorLogin) { +TEST_F(GoogleAuthenticatorTest, OnlineLogin) { MessageLoopForUI message_loop; ChromeThread ui_thread(ChromeThread::UI, &message_loop); - GURL source(AuthResponseHandler::kTokenAuthUrl); - URLRequestStatus status(URLRequestStatus::SUCCESS, 0); - - std::string response = - StringPrintf("Error=BadAuthentication\n%s\n", - GoogleAuthenticator::kSecondFactor); - MockConsumer consumer; - EXPECT_CALL(consumer, OnLoginSuccess(username_, data_)) + EXPECT_CALL(consumer, OnLoginSuccess(username_, result_)) .Times(1); EXPECT_CALL(*mock_library_, Mount(username_, hash_ascii_, _)) .WillOnce(Return(true)); scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer)); - auth->set_password_hash(hash_ascii_); - auth->set_username(username_); PrepForLogin(auth.get()); - auth->OnURLFetchComplete(NULL, - source, - status, - 403, - cookies_, - response); + auth->OnClientLoginSuccess(result_); message_loop.RunAllPending(); } @@ -458,43 +428,6 @@ TEST_F(GoogleAuthenticatorTest, LocalaccountLogin) { auth->CheckLocalaccount(std::string()); } -// Responds as though ClientLogin was successful. -class MockFetcher : public URLFetcher { - public: - MockFetcher(const GURL& url, - URLFetcher::RequestType request_type, - URLFetcher::Delegate* d) - : URLFetcher(url, request_type, d) { - } - ~MockFetcher() {} - void Start() { - GURL source(AuthResponseHandler::kClientLoginUrl); - URLRequestStatus status(URLRequestStatus::SUCCESS, 0); - delegate()->OnURLFetchComplete(NULL, - source, - status, - kHttpSuccess, - ResponseCookies(), - std::string()); - } - private: - DISALLOW_COPY_AND_ASSIGN(MockFetcher); -}; - -class MockFactory : public URLFetcher::Factory { - public: - MockFactory() {} - ~MockFactory() {} - URLFetcher* CreateURLFetcher(int id, - const GURL& url, - URLFetcher::RequestType request_type, - URLFetcher::Delegate* d) { - return new MockFetcher(url, request_type, d); - } - private: - DISALLOW_COPY_AND_ASSIGN(MockFactory); -}; - TEST_F(GoogleAuthenticatorTest, FullLogin) { MessageLoopForUI message_loop; ChromeThread ui_thread(ChromeThread::UI, &message_loop); @@ -503,7 +436,7 @@ TEST_F(GoogleAuthenticatorTest, FullLogin) { chromeos::CryptohomeBlob salt_v(fake_hash_, fake_hash_ + sizeof(fake_hash_)); MockConsumer consumer; - EXPECT_CALL(consumer, OnLoginSuccess(username_, data_)) + EXPECT_CALL(consumer, OnLoginSuccess(username_, result_)) .Times(1); EXPECT_CALL(*mock_library_, Mount(username_, _, _)) .WillOnce(Return(true)); diff --git a/chrome/browser/chromeos/login/login_screen.cc b/chrome/browser/chromeos/login/login_screen.cc index 4ed8f4b..29ee360 100644 --- a/chrome/browser/chromeos/login/login_screen.cc +++ b/chrome/browser/chromeos/login/login_screen.cc @@ -92,7 +92,8 @@ void LoginScreen::OnLoginFailure(const std::string& error) { } void LoginScreen::OnLoginSuccess(const std::string& username, - const std::string& credentials) { + const GaiaAuthConsumer::ClientLoginResult& credentials) { + delegate()->GetObserver(this)->OnExit(ScreenObserver::LOGIN_SIGN_IN_SELECTED); AppendStartUrlToCmdline(); LoginUtils::Get()->CompleteLogin(username, credentials); diff --git a/chrome/browser/chromeos/login/login_screen.h b/chrome/browser/chromeos/login/login_screen.h index 240786b..3b3645c 100644 --- a/chrome/browser/chromeos/login/login_screen.h +++ b/chrome/browser/chromeos/login/login_screen.h @@ -41,7 +41,7 @@ class LoginScreen : public ViewScreen<NewUserView>, // Overridden from LoginStatusConsumer. virtual void OnLoginFailure(const std::string& error); virtual void OnLoginSuccess(const std::string& username, - const std::string& credentials); + const GaiaAuthConsumer::ClientLoginResult& credentials); virtual void OnOffTheRecordLoginSuccess(); // Overridden from views::InfoBubbleDelegate. diff --git a/chrome/browser/chromeos/login/login_status_consumer.h b/chrome/browser/chromeos/login/login_status_consumer.h index b25a18f..0d85b8f 100644 --- a/chrome/browser/chromeos/login/login_status_consumer.h +++ b/chrome/browser/chromeos/login/login_status_consumer.h @@ -6,6 +6,7 @@ #define CHROME_BROWSER_CHROMEOS_LOGIN_LOGIN_STATUS_CONSUMER_H_ #include <string> +#include "chrome/common/net/gaia/gaia_auth_consumer.h" namespace chromeos { @@ -17,9 +18,10 @@ class LoginStatusConsumer { virtual ~LoginStatusConsumer() {} virtual void OnLoginFailure(const std::string& error) = 0; virtual void OnLoginSuccess(const std::string& username, - const std::string& credentials) = 0; + const GaiaAuthConsumer::ClientLoginResult& result) = 0; virtual void OnOffTheRecordLoginSuccess() {} - virtual void OnPasswordChangeDetected(const std::string& credentials) { + virtual void OnPasswordChangeDetected( + const GaiaAuthConsumer::ClientLoginResult& result) { // TODO(nkostylev): uncomment NOTREACHED once UI is in place. // NOTREACHED(); }; diff --git a/chrome/browser/chromeos/login/login_utils.cc b/chrome/browser/chromeos/login/login_utils.cc index 5e2628a..8a7071f 100644 --- a/chrome/browser/chromeos/login/login_utils.cc +++ b/chrome/browser/chromeos/login/login_utils.cc @@ -69,7 +69,7 @@ class LoginUtilsImpl : public LoginUtils, // Invoked after the user has successfully logged in. This launches a browser // and does other bookkeeping after logging in. virtual void CompleteLogin(const std::string& username, - const std::string& credentials); + const GaiaAuthConsumer::ClientLoginResult& credentials); // Invoked after the tmpfs is successfully mounted. // Launches a browser in the off the record (incognito) mode. @@ -174,7 +174,8 @@ bool LoginUtilsImpl::ShouldWaitForWifi() { } void LoginUtilsImpl::CompleteLogin(const std::string& username, - const std::string& credentials) { + const GaiaAuthConsumer::ClientLoginResult& credentials) { + LOG(INFO) << "Completing login for " << username; if (CrosLibrary::Get()->EnsureLoaded()) @@ -204,8 +205,8 @@ void LoginUtilsImpl::CompleteLogin(const std::string& username, // DoBrowserLaunch on the UI thread when it's done, and then // delete itself. CookieFetcher* cf = new CookieFetcher(profile); - cf->AttemptFetch(credentials); - auth_token_ = ExtractClientLoginParam(credentials, kAuthPrefix, kAuthSuffix); + cf->AttemptFetch(credentials.data); + auth_token_ = credentials.token; } void LoginUtilsImpl::CompleteOffTheRecordLogin() { diff --git a/chrome/browser/chromeos/login/login_utils.h b/chrome/browser/chromeos/login/login_utils.h index 306623c..f6262c5 100644 --- a/chrome/browser/chromeos/login/login_utils.h +++ b/chrome/browser/chromeos/login/login_utils.h @@ -8,6 +8,8 @@ #include <string> #include <vector> +#include "chrome/common/net/gaia/gaia_auth_consumer.h" + class Profile; namespace chromeos { @@ -43,7 +45,7 @@ class LoginUtils { // Invoked after the user has successfully logged in. This launches a browser // and does other bookkeeping after logging in. virtual void CompleteLogin(const std::string& username, - const std::string& credentials) = 0; + const GaiaAuthConsumer::ClientLoginResult& credentials) = 0; // Invoked after the tmpfs is successfully mounted. // Launches a browser in the off the record (incognito) mode. diff --git a/chrome/browser/chromeos/login/mock_authenticator.h b/chrome/browser/chromeos/login/mock_authenticator.h index aa06387..ec4253a 100644 --- a/chrome/browser/chromeos/login/mock_authenticator.h +++ b/chrome/browser/chromeos/login/mock_authenticator.h @@ -40,7 +40,7 @@ class MockAuthenticator : public Authenticator { ChromeThread::UI, FROM_HERE, NewRunnableMethod(this, &MockAuthenticator::OnLoginSuccess, - username)); + GaiaAuthConsumer::ClientLoginResult())); return true; } else { ChromeThread::PostTask( @@ -62,8 +62,10 @@ class MockAuthenticator : public Authenticator { consumer_->OnOffTheRecordLoginSuccess(); } - void OnLoginSuccess(const std::string& username) { - consumer_->OnLoginSuccess(username, std::string()); + void OnLoginSuccess(const GaiaAuthConsumer::ClientLoginResult& result) { + // If we want to be more like the real thing, we could save username + // in AuthenticateToLogin, but there's not much of a point. + consumer_->OnLoginSuccess(expected_username_, result); } void OnLoginFailure(const std::string& data) { @@ -93,7 +95,7 @@ class MockLoginUtils : public LoginUtils { } virtual void CompleteLogin(const std::string& username, - const std::string& cookies) { + const GaiaAuthConsumer::ClientLoginResult& res) { EXPECT_EQ(expected_username_, username); } diff --git a/chrome/browser/chromeos/login/mock_login_utils.h b/chrome/browser/chromeos/login/mock_login_utils.h deleted file mode 100644 index 09ac2af..0000000 --- a/chrome/browser/chromeos/login/mock_login_utils.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_CHROMEOS_LOGIN_MOCK_LOGIN_UTILS_H_ -#define CHROME_BROWSER_CHROMEOS_LOGIN_MOCK_LOGIN_UTILS_H_ - -#include <string> -#include <vector> -#include "chrome/browser/chromeos/login/login_utils.h" -#include "testing/gmock/include/gmock/gmock.h" - -class MockLoginUtils : public chromeos::LoginUtils { - public: - virtual ~MockLoginUtils() {} - MOCK_METHOD0(ShouldWaitForWifi, bool(void)); - MOCK_METHOD2(CompleteLogin, void(const std::string& username, - std::vector<std::string> cookies)); - MOCK_METHOD0(CompleteOffTheRecordLogin, void(void)); - MOCK_METHOD1(CreateAuthenticator, - Authenticator*(LoginStatusConsumer* consumer)); -}; - -#endif // CHROME_BROWSER_CHROMEOS_LOGIN_MOCK_LOGIN_UTILS_H_ diff --git a/chrome/browser/chromeos/login/screen_locker.cc b/chrome/browser/chromeos/login/screen_locker.cc index 869a1d9..be138b0 100644 --- a/chrome/browser/chromeos/login/screen_locker.cc +++ b/chrome/browser/chromeos/login/screen_locker.cc @@ -363,8 +363,9 @@ class InputEventObserver : public MessageLoopForUI::Observer { event->type == GDK_MOTION_NOTIFY) && !activated_) { activated_ = true; - std::string not_used; - screen_locker_->OnLoginSuccess(not_used, not_used); + std::string not_used_string; + GaiaAuthConsumer::ClientLoginResult not_used; + screen_locker_->OnLoginSuccess(not_used_string, not_used); } } @@ -488,7 +489,8 @@ void ScreenLocker::OnLoginFailure(const std::string& error) { } void ScreenLocker::OnLoginSuccess(const std::string& username, - const std::string& credentials) { + const GaiaAuthConsumer::ClientLoginResult& unused) { + DLOG(INFO) << "OnLoginSuccess"; if (CrosLibrary::Get()->EnsureLoaded()) diff --git a/chrome/browser/chromeos/login/screen_locker.h b/chrome/browser/chromeos/login/screen_locker.h index 787876c..89047ec 100644 --- a/chrome/browser/chromeos/login/screen_locker.h +++ b/chrome/browser/chromeos/login/screen_locker.h @@ -46,7 +46,7 @@ class ScreenLocker : public LoginStatusConsumer, // LoginStatusConsumer implements: virtual void OnLoginFailure(const std::string& error); virtual void OnLoginSuccess(const std::string& username, - const std::string& credentials); + const GaiaAuthConsumer::ClientLoginResult& result); // Overridden from views::InfoBubbleDelegate. virtual void InfoBubbleClosing(InfoBubble* info_bubble, diff --git a/chrome/chrome_common.gypi b/chrome/chrome_common.gypi index 31572eb..240280e 100644 --- a/chrome/chrome_common.gypi +++ b/chrome/chrome_common.gypi @@ -338,8 +338,11 @@ 'common/net/url_request_context_getter.h', 'common/net/url_request_intercept_job.cc', 'common/net/url_request_intercept_job.h', + 'common/net/gaia/gaia_auth_consumer.h', 'common/net/gaia/gaia_authenticator.cc', 'common/net/gaia/gaia_authenticator.h', + 'common/net/gaia/gaia_authenticator2.cc', + 'common/net/gaia/gaia_authenticator2.h', 'common/net/gaia/signin.h', ], 'dependencies': [ diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index cecc99c..1491221 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -1032,6 +1032,8 @@ 'common/json_value_serializer_unittest.cc', 'common/mru_cache_unittest.cc', 'common/net/gaia/gaia_authenticator_unittest.cc', + 'common/net/gaia/gaia_authenticator2_unittest.cc', + 'common/net/gaia/gaia_authenticator2_unittest.h', 'common/net/url_fetcher_unittest.cc', 'common/net/test_url_fetcher_factory.cc', 'common/net/test_url_fetcher_factory.h', diff --git a/chrome/common/net/gaia/gaia_auth_consumer.h b/chrome/common/net/gaia/gaia_auth_consumer.h new file mode 100644 index 0000000..31302c7 --- /dev/null +++ b/chrome/common/net/gaia/gaia_auth_consumer.h @@ -0,0 +1,67 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_NET_GAIA_GAIA_AUTH_CONSUMER_H_ +#define CHROME_COMMON_NET_GAIA_GAIA_AUTH_CONSUMER_H_ + +#include <string> + +// An interface that defines the callbacks for objects that +// GaiaAuthenticator2 can return data to. +class GaiaAuthConsumer { + public: + struct ClientLoginResult { + inline ClientLoginResult() {} + inline ClientLoginResult(const std::string& new_sid, + const std::string& new_lsid, + const std::string& new_token, + const std::string& new_data) + : sid(new_sid), + lsid(new_lsid), + token(new_token), + data(new_data) {} + + inline bool operator==(const ClientLoginResult &b) const { + return sid == b.sid && + lsid == b.lsid && + token == b.token && + data == b.data; + } + + std::string sid; + std::string lsid; + std::string token; + // TODO(chron): Remove the data field later. Don't use it if possible. + std::string data; // Full contents of ClientLogin return. + }; + + enum ClientLoginErrorCode { + NETWORK_ERROR, + REQUEST_CANCELED, + TWO_FACTOR, // Callers can treat this as a success. + PERMISSION_DENIED + }; + + struct ClientLoginError { + inline bool operator==(const ClientLoginError &b) const { + if (code != b.code) { + return false; + } + if (code == NETWORK_ERROR) { + return network_error == b.network_error; + } + return true; + } + + ClientLoginErrorCode code; + int network_error; // This field is only valid if NETWORK_ERROR occured. + std::string data; // TODO(chron): Remove this field. Should preparse data. + }; + + virtual ~GaiaAuthConsumer() {} + virtual void OnClientLoginFailure(const ClientLoginError& error) = 0; + virtual void OnClientLoginSuccess(const ClientLoginResult& result) = 0; +}; + +#endif // CHROME_COMMON_NET_GAIA_GAIA_AUTH_CONSUMER_H_ diff --git a/chrome/common/net/gaia/gaia_authenticator2.cc b/chrome/common/net/gaia/gaia_authenticator2.cc new file mode 100644 index 0000000..fd480f1 --- /dev/null +++ b/chrome/common/net/gaia/gaia_authenticator2.cc @@ -0,0 +1,239 @@ +// Copyright (c) 2010 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/common/net/gaia/gaia_authenticator2.h" + +#include <string> + +#include "base/string_split.h" +#include "base/string_util.h" +#include "chrome/common/net/gaia/gaia_auth_consumer.h" +#include "chrome/common/net/http_return.h" +#include "chrome/common/net/url_request_context_getter.h" +#include "net/base/load_flags.h" +#include "net/url_request/url_request_status.h" +#include "third_party/libjingle/source/talk/base/urlencode.h" + +// TODO(chron): Add sourceless version of this formatter. +// static +const char GaiaAuthenticator2::kClientLoginFormat[] = + "Email=%s&" + "Passwd=%s&" + "PersistentCookie=%s&" + "accountType=%s&" + "source=%s&" + "service=%s"; +// static +const char GaiaAuthenticator2::kClientLoginCaptchaFormat[] = + "Email=%s&" + "Passwd=%s&" + "PersistentCookie=%s&" + "accountType=%s&" + "source=%s&" + "service=%s&" + "logintoken=%s&" + "logincaptcha=%s"; + +// static +const char GaiaAuthenticator2::kCookiePersistence[] = "true"; +// static +const char GaiaAuthenticator2::kAccountType[] = "HOSTED_OR_GOOGLE"; +// static +const char GaiaAuthenticator2::kChromeOSSource[] = "chromeos"; +// static +// Service name for Gaia Contacts API. API is used to get user's image. +const char GaiaAuthenticator2::kContactsService[] = "cp"; +// static +const char GaiaAuthenticator2::kSecondFactor[] = "Info=InvalidSecondFactor"; + +// TODO(chron): These urls are also in auth_response_handler.h. +// The URLs for different calls in the Google Accounts programmatic login API. +const char GaiaAuthenticator2::kClientLoginUrl[] = + "https://www.google.com/accounts/ClientLogin"; +const char GaiaAuthenticator2::kIssueAuthTokenUrl[] = + "https://www.google.com/accounts/IssueAuthToken"; +// TODO(chron): Fix this URL not to hardcode source +// TODO(cmasone): make sure that using an http:// URL in the "continue" +// parameter here doesn't open the system up to attack long-term. +const char GaiaAuthenticator2::kTokenAuthUrl[] = + "https://www.google.com/accounts/TokenAuth?" + "continue=http://www.google.com/webhp&source=chromeos&auth="; + +GaiaAuthenticator2::GaiaAuthenticator2(GaiaAuthConsumer* consumer, + const std::string& source, + URLRequestContextGetter* getter) + : consumer_(consumer), + getter_(getter), + source_(source), + client_login_gurl_(kClientLoginUrl), + fetch_pending_(false){} + +GaiaAuthenticator2::~GaiaAuthenticator2() {} + +bool GaiaAuthenticator2::HasPendingFetch() { + return fetch_pending_; +} + +void GaiaAuthenticator2::CancelRequest() { + fetcher_.reset(); + fetch_pending_ = false; +} + +// static +URLFetcher* GaiaAuthenticator2::CreateClientLoginFetcher( + URLRequestContextGetter* getter, + const std::string& body, + const GURL& client_login_gurl, + URLFetcher::Delegate* delegate) { + + URLFetcher* to_return = + URLFetcher::Create(0, + client_login_gurl, + URLFetcher::POST, + delegate); + to_return->set_request_context(getter); + to_return->set_load_flags(net::LOAD_DO_NOT_SEND_COOKIES); + to_return->set_upload_data("application/x-www-form-urlencoded", body); + return to_return; +} + +std::string GaiaAuthenticator2::GenerateRequestBody( + const std::string& username, + const std::string& password, + const std::string& source, + const char* service, + const std::string& login_token, + const std::string& login_captcha) { + + if (login_token.empty() || login_captcha.empty()) { + return StringPrintf(kClientLoginFormat, + UrlEncodeString(username).c_str(), + UrlEncodeString(password).c_str(), + kCookiePersistence, + kAccountType, + source.c_str(), + service); + } + + return StringPrintf(kClientLoginCaptchaFormat, + UrlEncodeString(username).c_str(), + UrlEncodeString(password).c_str(), + kCookiePersistence, + kAccountType, + source.c_str(), + service, + UrlEncodeString(login_token).c_str(), + UrlEncodeString(login_captcha).c_str()); + +} + +// Helper method that extracts tokens from a successful reply, and saves them +// in the right fields. +// static +void GaiaAuthenticator2::ParseClientLoginResponse(const std::string& data, + std::string* sid, + std::string* lsid, + std::string* token) { + using std::vector; + using std::pair; + using std::string; + + vector<pair<string, string> > tokens; + base::SplitStringIntoKeyValuePairs(data, '=', '\n', &tokens); + for (vector<pair<string, string> >::iterator i = tokens.begin(); + i != tokens.end(); ++i) { + if (i->first == "SID") { + sid->assign(i->second); + } else if (i->first == "LSID") { + lsid->assign(i->second); + } else if (i->first == "Auth") { + token->assign(i->second); + } + } +} + +void GaiaAuthenticator2::StartClientLogin(const std::string& username, + const std::string& password, + const char* service, + const std::string& login_token, + const std::string& login_captcha) { + + // This class is thread agnostic, so be sure to call this only on the + // same thread each time. + LOG(INFO) << "Starting new ClientLogin fetch."; + + // Must outlive fetcher_. + request_body_ = GenerateRequestBody(username, + password, + source_, + service, + login_token, + login_captcha); + + fetcher_.reset(CreateClientLoginFetcher(getter_, + request_body_, + client_login_gurl_, + this)); + fetch_pending_ = true; + fetcher_->Start(); +} + +void GaiaAuthenticator2::OnClientLoginFetched(const std::string& data, + const URLRequestStatus& status, + int response_code) { + + if (status.is_success() && response_code == RC_REQUEST_OK) { + LOG(INFO) << "ClientLogin successful!"; + std::string sid; + std::string lsid; + std::string token; + ParseClientLoginResponse(data, &sid, &lsid, &token); + consumer_->OnClientLoginSuccess( + GaiaAuthConsumer::ClientLoginResult(sid, lsid, token, data)); + return; + } + + GaiaAuthConsumer::ClientLoginError error; + error.data = data; + + if (!status.is_success()) { + if (status.status() == URLRequestStatus::CANCELED) { + error.code = GaiaAuthConsumer::REQUEST_CANCELED; + } else { + error.code = GaiaAuthConsumer::NETWORK_ERROR; + error.network_error = status.os_error(); + LOG(WARNING) << "Could not reach Google Accounts servers: errno " + << status.os_error(); + } + } else { + if (IsSecondFactorSuccess(data)) { + error.code = GaiaAuthConsumer::TWO_FACTOR; + } else { + error.code = GaiaAuthConsumer::PERMISSION_DENIED; + } + } + + consumer_->OnClientLoginFailure(error); +} + +void GaiaAuthenticator2::OnURLFetchComplete(const URLFetcher* source, + const GURL& url, + const URLRequestStatus& status, + int response_code, + const ResponseCookies& cookies, + const std::string& data) { + fetch_pending_ = false; + if (url == client_login_gurl_) { + OnClientLoginFetched(data, status, response_code); + return; + } + NOTREACHED(); +} + +// static +bool GaiaAuthenticator2::IsSecondFactorSuccess( + const std::string& alleged_error) { + return alleged_error.find(kSecondFactor) != + std::string::npos; +} diff --git a/chrome/common/net/gaia/gaia_authenticator2.h b/chrome/common/net/gaia/gaia_authenticator2.h new file mode 100644 index 0000000..bbb3761 --- /dev/null +++ b/chrome/common/net/gaia/gaia_authenticator2.h @@ -0,0 +1,123 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_NET_GAIA_GAIA_AUTHENTICATOR2_H_ +#define CHROME_COMMON_NET_GAIA_GAIA_AUTHENTICATOR2_H_ + +#include <string> + +#include "base/gtest_prod_util.h" +#include "chrome/common/net/url_fetcher.h" +#include "googleurl/src/gurl.h" + +// Authenticate a user against the Google Accounts ClientLogin API +// with various capabilities and return results to a GaiaAuthConsumer. +// +// In the future, we will also issue auth tokens from this class. +// This class should be used on a single thread, but it can be whichever thread +// that you like. + +class GaiaAuthConsumer; +class GaiaAuthenticator2Test; + +class GaiaAuthenticator2 : public URLFetcher::Delegate { + public: + // Constants to use in the ClientLogin request POST body. + static const char kChromeOSSource[]; + static const char kContactsService[]; + + // The URLs for different calls in the Google Accounts programmatic login API. + static const char kClientLoginUrl[]; + static const char kIssueAuthTokenUrl[]; + static const char kTokenAuthUrl[]; + + + // Magic string indicating that, while a second factor is still + // needed to complete authentication, the user provided the right password. + static const char kSecondFactor[]; + + // This will later be hidden behind an auth service which caches + // tokens. + GaiaAuthenticator2(GaiaAuthConsumer* consumer, + const std::string& source, + URLRequestContextGetter* getter); + virtual ~GaiaAuthenticator2(); + + // GaiaAuthConsumer will be called on the original thread + // after results come back. This class is thread agnostic. + void StartClientLogin(const std::string& username, + const std::string& password, + const char* const service, + const std::string& login_token, + const std::string& login_captcha); + + // Implementation of URLFetcher::Delegate + void OnURLFetchComplete(const URLFetcher* source, + const GURL& url, + const URLRequestStatus& status, + int response_code, + const ResponseCookies& cookies, + const std::string& data); + + // StartClientLogin been called && results not back yet? + bool HasPendingFetch(); + + // Stop any URL fetches in progress. + void CancelRequest(); + + private: + // ClientLogin body constants that don't change + static const char kCookiePersistence[]; + static const char kAccountType[]; + + // The format of the POST body for ClientLogin. + static const char kClientLoginFormat[]; + // The format of said POST body when CAPTCHA token & answer are specified. + static const char kClientLoginCaptchaFormat[]; + + // Process the results of a ClientLogin fetch. + void OnClientLoginFetched(const std::string& data, + const URLRequestStatus& status, + int response_code); + + // Tokenize the results of a ClientLogin fetch. + static void ParseClientLoginResponse(const std::string& data, + std::string* sid, + std::string* lsid, + std::string* token); + + // Is this a special case Gaia error for TwoFactor auth? + static bool IsSecondFactorSuccess(const std::string& alleged_error); + + // Given parameters, create a ClientLogin request body. + static std::string GenerateRequestBody(const std::string& username, + const std::string& password, + const std::string& source, + const char* service, + const std::string& login_token, + const std::string& login_captcha); + + // Create a fetcher useable for making a ClientLogin request. + static URLFetcher* CreateClientLoginFetcher(URLRequestContextGetter* getter, + const std::string& body, + const GURL& client_login_gurl_, + URLFetcher::Delegate* delegate); + + GaiaAuthConsumer* const consumer_; + scoped_ptr<URLFetcher> fetcher_; + URLRequestContextGetter* const getter_; + std::string source_; + const GURL client_login_gurl_; + std::string request_body_; + bool fetch_pending_; + + friend class GaiaAuthenticator2Test; + FRIEND_TEST_ALL_PREFIXES(GaiaAuthenticator2Test, LoginNetFailure); + FRIEND_TEST_ALL_PREFIXES(GaiaAuthenticator2Test, CheckNormalErrorCode); + FRIEND_TEST_ALL_PREFIXES(GaiaAuthenticator2Test, CheckTwoFactorResponse); + + DISALLOW_COPY_AND_ASSIGN(GaiaAuthenticator2); +}; + +#endif // CHROME_COMMON_NET_GAIA_GAIA_AUTHENTICATOR2_H_ diff --git a/chrome/common/net/gaia/gaia_authenticator2_unittest.cc b/chrome/common/net/gaia/gaia_authenticator2_unittest.cc new file mode 100644 index 0000000..54d9211 --- /dev/null +++ b/chrome/common/net/gaia/gaia_authenticator2_unittest.cc @@ -0,0 +1,258 @@ +// Copyright (c) 2010 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. +// +// A complete set of unit tests for GaiaAuthenticator2. +// Originally ported from GoogleAuthenticator tests. + +#include <string> + +#include "base/message_loop.h" +#include "base/string_util.h" +#include "chrome/common/net/gaia/gaia_auth_consumer.h" +#include "chrome/common/net/gaia/gaia_authenticator2.h" +#include "chrome/common/net/gaia/gaia_authenticator2_unittest.h" +#include "chrome/common/net/http_return.h" +#include "chrome/common/net/test_url_fetcher_factory.h" +#include "chrome/common/net/url_fetcher.h" +#include "chrome/test/testing_profile.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" + +using ::testing::_; + +class GaiaAuthenticator2Test : public testing::Test { + public: + GaiaAuthenticator2Test() + : source_(GaiaAuthenticator2::kClientLoginUrl) {} + + void RunParsingTest(const std::string& data, + const std::string& sid, + const std::string& lsid, + const std::string& token) { + std::string out_sid; + std::string out_lsid; + std::string out_token; + + GaiaAuthenticator2::ParseClientLoginResponse(data, + &out_sid, + &out_lsid, + &out_token); + EXPECT_EQ(lsid, out_lsid); + EXPECT_EQ(sid, out_sid); + EXPECT_EQ(token, out_token); + } + + ResponseCookies cookies_; + GURL source_; + TestingProfile profile_; +}; + +class MockGaiaConsumer : public GaiaAuthConsumer { + public: + MockGaiaConsumer() {} + ~MockGaiaConsumer() {} + MOCK_METHOD1(OnClientLoginFailure, void(const ClientLoginError& error)); + MOCK_METHOD1(OnClientLoginSuccess, void(const ClientLoginResult& result)); +}; + +TEST_F(GaiaAuthenticator2Test, ErrorComparator) { + GaiaAuthConsumer::ClientLoginError expected_error; + expected_error.code = GaiaAuthConsumer::NETWORK_ERROR; + expected_error.network_error = -101; + + GaiaAuthConsumer::ClientLoginError matching_error; + matching_error.code = GaiaAuthConsumer::NETWORK_ERROR; + matching_error.network_error = -101; + + EXPECT_TRUE(expected_error == matching_error); + + expected_error.network_error = 6; + + EXPECT_FALSE(expected_error == matching_error); + + expected_error.code = GaiaAuthConsumer::PERMISSION_DENIED; + matching_error.code = GaiaAuthConsumer::PERMISSION_DENIED; + + EXPECT_TRUE(expected_error == matching_error); +} + +TEST_F(GaiaAuthenticator2Test, LoginNetFailure) { + int error_no = net::ERR_CONNECTION_RESET; + URLRequestStatus status(URLRequestStatus::FAILED, error_no); + + GaiaAuthConsumer::ClientLoginError expected_error; + expected_error.code = GaiaAuthConsumer::NETWORK_ERROR; + expected_error.network_error = error_no; + + MockGaiaConsumer consumer; + EXPECT_CALL(consumer, OnClientLoginFailure(expected_error)) + .Times(1); + + GaiaAuthenticator2 auth(&consumer, std::string(), + profile_.GetRequestContext()); + + auth.OnURLFetchComplete(NULL, source_, status, 0, cookies_, std::string()); +} + + +TEST_F(GaiaAuthenticator2Test, LoginDenied) { + std::string data("Error: NO!"); + URLRequestStatus status(URLRequestStatus::SUCCESS, 0); + + GaiaAuthConsumer::ClientLoginError expected_error; + expected_error.code = GaiaAuthConsumer::PERMISSION_DENIED; + + MockGaiaConsumer consumer; + EXPECT_CALL(consumer, OnClientLoginFailure(expected_error)) + .Times(1); + + GaiaAuthenticator2 auth(&consumer, std::string(), + profile_.GetRequestContext()); + auth.OnURLFetchComplete(NULL, source_, status, RC_FORBIDDEN, cookies_, data); +} + +TEST_F(GaiaAuthenticator2Test, ParseRequest) { + RunParsingTest("SID=sid\nLSID=lsid\nAuth=auth\n", "sid", "lsid", "auth"); + RunParsingTest("LSID=lsid\nSID=sid\nAuth=auth\n", "sid", "lsid", "auth"); + RunParsingTest("SID=sid\nLSID=lsid\nAuth=auth", "sid", "lsid", "auth"); + RunParsingTest("SID=sid\nAuth=auth\n", "sid", "", "auth"); + RunParsingTest("LSID=lsid\nAuth=auth\n", "", "lsid", "auth"); + RunParsingTest("\nAuth=auth\n", "", "", "auth"); + RunParsingTest("SID=sid", "sid", "", ""); +} + +TEST_F(GaiaAuthenticator2Test, OnlineLogin) { + std::string data("SID=sid\nLSID=lsid\nAuth=auth\n"); + + GaiaAuthConsumer::ClientLoginResult result; + result.lsid = "lsid"; + result.sid = "sid"; + result.token = "auth"; + result.data = data; + + MockGaiaConsumer consumer; + EXPECT_CALL(consumer, OnClientLoginSuccess(result)) + .Times(1); + + GaiaAuthenticator2 auth(&consumer, std::string(), + profile_.GetRequestContext()); + URLRequestStatus status(URLRequestStatus::SUCCESS, 0); + auth.OnURLFetchComplete(NULL, + source_, + status, + RC_REQUEST_OK, + cookies_, + data); +} + +TEST_F(GaiaAuthenticator2Test, CheckTwoFactorResponse) { + std::string response = + StringPrintf("Error=BadAuthentication\n%s\n", + GaiaAuthenticator2::kSecondFactor); + EXPECT_TRUE(GaiaAuthenticator2::IsSecondFactorSuccess(response)); +} + +TEST_F(GaiaAuthenticator2Test, CheckNormalErrorCode) { + std::string response = "Error=BadAuthentication\n"; + EXPECT_FALSE(GaiaAuthenticator2::IsSecondFactorSuccess(response)); +} + +TEST_F(GaiaAuthenticator2Test, TwoFactorLogin) { + std::string response = + StringPrintf("Error=BadAuthentication\n%s\n", + GaiaAuthenticator2::kSecondFactor); + + GaiaAuthConsumer::ClientLoginError error; + error.code = GaiaAuthConsumer::TWO_FACTOR; + + MockGaiaConsumer consumer; + EXPECT_CALL(consumer, OnClientLoginFailure(error)) + .Times(1); + + GaiaAuthenticator2 auth(&consumer, std::string(), + profile_.GetRequestContext()); + URLRequestStatus status(URLRequestStatus::SUCCESS, 0); + auth.OnURLFetchComplete(NULL, + source_, + status, + RC_FORBIDDEN, + cookies_, + response); +} + +TEST_F(GaiaAuthenticator2Test, FullLogin) { + MockGaiaConsumer consumer; + EXPECT_CALL(consumer, OnClientLoginSuccess(_)) + .Times(1); + + TestingProfile profile; + + MockFactory factory; + URLFetcher::set_factory(&factory); + + GaiaAuthenticator2 auth(&consumer, std::string(), + profile_.GetRequestContext()); + auth.StartClientLogin("username", + "password", + "service", + std::string(), + std::string()); + + URLFetcher::set_factory(NULL); +} + +TEST_F(GaiaAuthenticator2Test, FullLoginFailure) { + MockGaiaConsumer consumer; + EXPECT_CALL(consumer, OnClientLoginFailure(_)) + .Times(1); + + TestingProfile profile; + + MockFactory factory; + URLFetcher::set_factory(&factory); + factory.set_success(false); + + GaiaAuthenticator2 auth(&consumer, std::string(), + profile_.GetRequestContext()); + auth.StartClientLogin("username", + "password", + "service", + std::string(), + std::string()); + + URLFetcher::set_factory(NULL); +} + +TEST_F(GaiaAuthenticator2Test, FetchPending) { + MockGaiaConsumer consumer; + EXPECT_CALL(consumer, OnClientLoginSuccess(_)) + .Times(1); + + TestingProfile profile; + TestURLFetcherFactory factory; + URLFetcher::set_factory(&factory); + + GaiaAuthenticator2 auth(&consumer, std::string(), + profile_.GetRequestContext()); + auth.StartClientLogin("username", + "password", + "service", + std::string(), + std::string()); + + URLFetcher::set_factory(NULL); + EXPECT_TRUE(auth.HasPendingFetch()); + auth.OnURLFetchComplete(NULL, + source_, + URLRequestStatus(URLRequestStatus::SUCCESS, 0), + RC_REQUEST_OK, + cookies_, + std::string()); + EXPECT_FALSE(auth.HasPendingFetch()); +} + + diff --git a/chrome/common/net/gaia/gaia_authenticator2_unittest.h b/chrome/common/net/gaia/gaia_authenticator2_unittest.h new file mode 100644 index 0000000..6a2cf78 --- /dev/null +++ b/chrome/common/net/gaia/gaia_authenticator2_unittest.h @@ -0,0 +1,70 @@ +// Copyright (c) 2010 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. +// +// A collection of classes that are useful when testing things that use a +// GaiaAuthenticator2. + +#ifndef CHROME_COMMON_NET_GAIA_GAIA_AUTHENTICATOR2_UNITTEST_H_ +#define CHROME_COMMON_NET_GAIA_GAIA_AUTHENTICATOR2_UNITTEST_H_ + +#include "chrome/common/net/gaia/gaia_authenticator2.h" +#include "chrome/common/net/url_fetcher.h" +#include "chrome/common/net/http_return.h" +#include "net/url_request/url_request_status.h" + +// Responds as though ClientLogin returned from the server. +class MockClientLoginFetcher : public URLFetcher { + public: + MockClientLoginFetcher(bool success, + const GURL& url, + URLFetcher::RequestType request_type, + URLFetcher::Delegate* d) + : URLFetcher(url, request_type, d), + success_(success) {} + ~MockClientLoginFetcher() {} + void Start() { + GURL source(GaiaAuthenticator2::kClientLoginUrl); + URLRequestStatus::Status code; + int http_code; + if (success_) { + http_code = RC_REQUEST_OK; + code = URLRequestStatus::SUCCESS; + } else { + http_code = RC_FORBIDDEN; + code = URLRequestStatus::FAILED; + } + + URLRequestStatus status(code, 0); + delegate()->OnURLFetchComplete(NULL, + source, + status, + http_code, + ResponseCookies(), + std::string()); + } + private: + bool success_; + DISALLOW_COPY_AND_ASSIGN(MockClientLoginFetcher); +}; + +class MockFactory : public URLFetcher::Factory { + public: + MockFactory() + : success_(true) {} + ~MockFactory() {} + URLFetcher* CreateURLFetcher(int id, + const GURL& url, + URLFetcher::RequestType request_type, + URLFetcher::Delegate* d) { + return new MockClientLoginFetcher(success_, url, request_type, d); + } + void set_success(bool success) { + success_ = success; + } + private: + bool success_; + DISALLOW_COPY_AND_ASSIGN(MockFactory); +}; + +#endif // CHROME_COMMON_NET_GAIA_GAIA_AUTHENTICATOR2_UNITTEST_H_ |