summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/chromeos/login/authenticator.h4
-rw-r--r--chrome/browser/chromeos/login/existing_user_controller.cc3
-rw-r--r--chrome/browser/chromeos/login/existing_user_controller.h2
-rw-r--r--chrome/browser/chromeos/login/google_authenticator.cc261
-rw-r--r--chrome/browser/chromeos/login/google_authenticator.h89
-rw-r--r--chrome/browser/chromeos/login/google_authenticator_unittest.cc167
-rw-r--r--chrome/browser/chromeos/login/login_screen.cc3
-rw-r--r--chrome/browser/chromeos/login/login_screen.h2
-rw-r--r--chrome/browser/chromeos/login/login_status_consumer.h6
-rw-r--r--chrome/browser/chromeos/login/login_utils.cc9
-rw-r--r--chrome/browser/chromeos/login/login_utils.h4
-rw-r--r--chrome/browser/chromeos/login/mock_authenticator.h10
-rw-r--r--chrome/browser/chromeos/login/mock_login_utils.h24
-rw-r--r--chrome/browser/chromeos/login/screen_locker.cc8
-rw-r--r--chrome/browser/chromeos/login/screen_locker.h2
-rw-r--r--chrome/chrome_common.gypi3
-rw-r--r--chrome/chrome_tests.gypi2
-rw-r--r--chrome/common/net/gaia/gaia_auth_consumer.h67
-rw-r--r--chrome/common/net/gaia/gaia_authenticator2.cc239
-rw-r--r--chrome/common/net/gaia/gaia_authenticator2.h123
-rw-r--r--chrome/common/net/gaia/gaia_authenticator2_unittest.cc258
-rw-r--r--chrome/common/net/gaia/gaia_authenticator2_unittest.h70
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_