diff options
author | sanjeevr@chromium.org <sanjeevr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-07 02:53:55 +0000 |
---|---|---|
committer | sanjeevr@chromium.org <sanjeevr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-07 02:53:55 +0000 |
commit | f3d0174b68971b96dd2e8f518d81abee1fc87645 (patch) | |
tree | dc8b5e279042af86343784c90c2b85553a100ab9 /chrome/common | |
parent | 92e83e01a65d77e69978360250979ddea41987ca (diff) | |
download | chromium_src-f3d0174b68971b96dd2e8f518d81abee1fc87645.zip chromium_src-f3d0174b68971b96dd2e8f518d81abee1fc87645.tar.gz chromium_src-f3d0174b68971b96dd2e8f518d81abee1fc87645.tar.bz2 |
Moved GaiaAuthenticator from chrome/browser/sync/engine/net to chrome/common/net/gaia.
BUG=None
TEST=None, no functional change.
Review URL: http://codereview.chromium.org/1998004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@46654 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/common')
-rw-r--r-- | chrome/common/net/gaia/gaia_authenticator.cc | 404 | ||||
-rw-r--r-- | chrome/common/net/gaia/gaia_authenticator.h | 331 | ||||
-rw-r--r-- | chrome/common/net/gaia/gaia_authenticator_unittest.cc | 49 | ||||
-rw-r--r-- | chrome/common/net/gaia/signin.h | 18 | ||||
-rw-r--r-- | chrome/common/net/http_return.h | 17 |
5 files changed, 819 insertions, 0 deletions
diff --git a/chrome/common/net/gaia/gaia_authenticator.cc b/chrome/common/net/gaia/gaia_authenticator.cc new file mode 100644 index 0000000..38fc19b --- /dev/null +++ b/chrome/common/net/gaia/gaia_authenticator.cc @@ -0,0 +1,404 @@ +// Copyright (c) 2009 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_authenticator.h" + +#include <string> +#include <utility> +#include <vector> + +#include "base/basictypes.h" +#include "base/port.h" +#include "base/string_split.h" +#include "chrome/common/deprecated/event_sys-inl.h" +#include "chrome/common/net/http_return.h" +#include "googleurl/src/gurl.h" +#include "net/base/escape.h" + +using std::pair; +using std::string; +using std::vector; + +namespace gaia { + +static const char kGaiaV1IssueAuthTokenPath[] = "/accounts/IssueAuthToken"; + +static const char kGetUserInfoPath[] = "/accounts/GetUserInfo"; + +// Sole constructor with initializers for all fields. +GaiaAuthenticator::GaiaAuthenticator(const string& user_agent, + const string& service_id, + const string& gaia_url) + : user_agent_(user_agent), + service_id_(service_id), + gaia_url_(gaia_url), + request_count_(0), + delay_(0), + next_allowed_auth_attempt_time_(0), + early_auth_attempt_count_(0), + message_loop_(NULL) { + GaiaAuthEvent done = { GaiaAuthEvent::GAIA_AUTHENTICATOR_DESTROYED, None, + this }; + channel_ = new Channel(done); +} + +GaiaAuthenticator::~GaiaAuthenticator() { + delete channel_; +} + +// mutex_ must be entered before calling this function. +GaiaAuthenticator::AuthParams GaiaAuthenticator::MakeParams( + const string& user_name, + const string& password, + SaveCredentials should_save_credentials, + const string& captcha_token, + const string& captcha_value, + SignIn try_first) { + AuthParams params; + params.request_id = ++request_count_; + params.email = user_name; + params.password = password; + params.should_save_credentials = should_save_credentials; + params.captcha_token = captcha_token; + params.captcha_value = captcha_value; + params.authenticator = this; + params.try_first = try_first; + return params; +} + +bool GaiaAuthenticator::Authenticate(const string& user_name, + const string& password, + SaveCredentials should_save_credentials, + const string& captcha_token, + const string& captcha_value, + SignIn try_first) { + DCHECK_EQ(MessageLoop::current(), message_loop_); + + AuthParams const params = + MakeParams(user_name, password, should_save_credentials, captcha_token, + captcha_value, try_first); + return AuthenticateImpl(params); +} + +bool GaiaAuthenticator::AuthenticateWithLsid(const string& lsid, + bool long_lived) { + auth_results_.lsid = lsid; + // We need to lookup the email associated with this LSID cookie in order to + // update |auth_results_| with the correct values. + if (LookupEmail(&auth_results_)) { + auth_results_.email = auth_results_.primary_email; + return IssueAuthToken(&auth_results_, service_id_, long_lived); + } + return false; +} + + +bool GaiaAuthenticator::AuthenticateImpl(const AuthParams& params) { + DCHECK_EQ(MessageLoop::current(), message_loop_); + AuthResults results; + const bool succeeded = AuthenticateImpl(params, &results); + if (params.request_id == request_count_) { + auth_results_ = results; + GaiaAuthEvent event = { succeeded ? GaiaAuthEvent::GAIA_AUTH_SUCCEEDED + : GaiaAuthEvent::GAIA_AUTH_FAILED, + results.auth_error, this }; + channel_->NotifyListeners(event); + } + return succeeded; +} + +// This method makes an HTTP request to the Gaia server, and calls other +// methods to help parse the response. If authentication succeeded, then +// Gaia-issued cookies are available in the respective variables; if +// authentication failed, then the exact error is available as an enum. If the +// client wishes to save the credentials, the last parameter must be true. +// If a subsequent request is made with fresh credentials, the saved credentials +// are wiped out; any subsequent request to the zero-parameter overload of this +// method preserves the saved credentials. +bool GaiaAuthenticator::AuthenticateImpl(const AuthParams& params, + AuthResults* results) { + DCHECK_EQ(MessageLoop::current(), message_loop_); + results->credentials_saved = params.should_save_credentials; + results->auth_error = ConnectionUnavailable; + // Save credentials if so requested. + if (params.should_save_credentials != DONT_SAVE_CREDENTIALS) { + results->email = params.email.data(); + results->password = params.password; + } else { // Explicitly clear previously-saved credentials. + results->email = ""; + results->password = ""; + } + + // The aim of this code is to start failing requests if due to a logic error + // in the program we're hammering GAIA. +#if defined(OS_WIN) + __time32_t now = _time32(0); +#else // defined(OS_WIN) + time_t now = time(0); +#endif // defined(OS_WIN) + + if (now > next_allowed_auth_attempt_time_) { + next_allowed_auth_attempt_time_ = now + 1; + // If we're more than 2 minutes past the allowed time we reset the early + // attempt count. + if (now - next_allowed_auth_attempt_time_ > 2 * 60) { + delay_ = 1; + early_auth_attempt_count_ = 0; + } + } else { + ++early_auth_attempt_count_; + // Allow 3 attempts, but then limit. + if (early_auth_attempt_count_ > 3) { + delay_ = GetBackoffDelaySeconds(delay_); + next_allowed_auth_attempt_time_ = now + delay_; + return false; + } + } + + return PerformGaiaRequest(params, results); +} + +bool GaiaAuthenticator::PerformGaiaRequest(const AuthParams& params, + AuthResults* results) { + DCHECK_EQ(MessageLoop::current(), message_loop_); + GURL gaia_auth_url(gaia_url_); + + string post_body; + post_body += "Email=" + EscapeUrlEncodedData(params.email); + post_body += "&Passwd=" + EscapeUrlEncodedData(params.password); + post_body += "&source=" + EscapeUrlEncodedData(user_agent_); + post_body += "&service=" + service_id_; + if (!params.captcha_token.empty() && !params.captcha_value.empty()) { + post_body += "&logintoken=" + EscapeUrlEncodedData(params.captcha_token); + post_body += "&logincaptcha=" + EscapeUrlEncodedData(params.captcha_value); + } + post_body += "&PersistentCookie=true"; + // We set it to GOOGLE (and not HOSTED or HOSTED_OR_GOOGLE) because we only + // allow consumer logins. + post_body += "&accountType=GOOGLE"; + + string message_text; + unsigned long server_response_code; + if (!Post(gaia_auth_url, post_body, &server_response_code, &message_text)) { + results->auth_error = ConnectionUnavailable; + return false; + } + + // Parse reply in two different ways, depending on if request failed or + // succeeded. + if (RC_FORBIDDEN == server_response_code) { + ExtractAuthErrorFrom(message_text, results); + return false; + } else if (RC_REQUEST_OK == server_response_code) { + ExtractTokensFrom(message_text, results); + const bool old_gaia = + results->auth_token.empty() && !results->lsid.empty(); + const bool long_lived_token = + params.should_save_credentials == PERSIST_TO_DISK; + if ((old_gaia || long_lived_token) && + !IssueAuthToken(results, service_id_, long_lived_token)) + return false; + + return LookupEmail(results); + } else { + results->auth_error = Unknown; + return false; + } +} + +bool GaiaAuthenticator::LookupEmail(AuthResults* results) { + DCHECK_EQ(MessageLoop::current(), message_loop_); + // Use the provided Gaia server, but change the path to what V1 expects. + GURL url(gaia_url_); // Gaia server. + GURL::Replacements repl; + // Needs to stay in scope till GURL is out of scope. + string path(kGetUserInfoPath); + repl.SetPathStr(path); + url = url.ReplaceComponents(repl); + + string post_body; + post_body += "LSID="; + post_body += EscapeUrlEncodedData(results->lsid); + + unsigned long server_response_code; + string message_text; + if (!Post(url, post_body, &server_response_code, &message_text)) { + return false; + } + + // Check if we received a valid AuthToken; if not, ignore it. + if (RC_FORBIDDEN == server_response_code) { + // Server says we're not authenticated. + ExtractAuthErrorFrom(message_text, results); + return false; + } else if (RC_REQUEST_OK == server_response_code) { + typedef vector<pair<string, string> > Tokens; + Tokens tokens; + base::SplitStringIntoKeyValuePairs(message_text, '=', '\n', &tokens); + for (Tokens::iterator i = tokens.begin(); i != tokens.end(); ++i) { + if ("accountType" == i->first) { + // We never authenticate an email as a hosted account. + DCHECK_EQ("GOOGLE", i->second); + results->signin = GMAIL_SIGNIN; + } else if ("email" == i->first) { + results->primary_email = i->second; + } + } + return true; + } + return false; +} + +// We need to call this explicitly when we need to obtain a long-lived session +// token. +bool GaiaAuthenticator::IssueAuthToken(AuthResults* results, + const string& service_id, + bool long_lived) { + DCHECK_EQ(MessageLoop::current(), message_loop_); + // Use the provided Gaia server, but change the path to what V1 expects. + GURL url(gaia_url_); // Gaia server. + GURL::Replacements repl; + // Needs to stay in scope till GURL is out of scope. + string path(kGaiaV1IssueAuthTokenPath); + repl.SetPathStr(path); + url = url.ReplaceComponents(repl); + + string post_body; + post_body += "LSID="; + post_body += EscapeUrlEncodedData(results->lsid); + post_body += "&service=" + service_id; + if (long_lived) { + post_body += "&Session=true"; + } + + unsigned long server_response_code; + string message_text; + if (!Post(url, post_body, &server_response_code, &message_text)) { + return false; + } + + // Check if we received a valid AuthToken; if not, ignore it. + if (RC_FORBIDDEN == server_response_code) { + // Server says we're not authenticated. + ExtractAuthErrorFrom(message_text, results); + return false; + } else if (RC_REQUEST_OK == server_response_code) { + // Note that the format of message_text is different from what is returned + // in the first request, or to the sole request that is made to Gaia V2. + // Specifically, the entire string is the AuthToken, and looks like: + // "<token>" rather than "AuthToken=<token>". Thus, we need not use + // ExtractTokensFrom(...), but simply assign the token. + int last_index = message_text.length() - 1; + if ('\n' == message_text[last_index]) + message_text.erase(last_index); + results->auth_token = message_text; + return true; + } + return false; +} + +// Helper method that extracts tokens from a successful reply, and saves them +// in the right fields. +void GaiaAuthenticator::ExtractTokensFrom(const string& response, + AuthResults* results) { + vector<pair<string, string> > tokens; + base::SplitStringIntoKeyValuePairs(response, '=', '\n', &tokens); + for (vector<pair<string, string> >::iterator i = tokens.begin(); + i != tokens.end(); ++i) { + if (i->first == "SID") { + results->sid = i->second; + } else if (i->first == "LSID") { + results->lsid = i->second; + } else if (i->first == "Auth") { + results->auth_token = i->second; + } + } +} + +// Helper method that extracts tokens from a failure response, and saves them +// in the right fields. +void GaiaAuthenticator::ExtractAuthErrorFrom(const string& response, + AuthResults* results) { + vector<pair<string, string> > tokens; + base::SplitStringIntoKeyValuePairs(response, '=', '\n', &tokens); + for (vector<pair<string, string> >::iterator i = tokens.begin(); + i != tokens.end(); ++i) { + if (i->first == "Error") { + results->error_msg = i->second; + } else if (i->first == "Url") { + results->auth_error_url = i->second; + } else if (i->first == "CaptchaToken") { + results->captcha_token = i->second; + } else if (i->first == "CaptchaUrl") { + results->captcha_url = i->second; + } + } + + // Convert string error messages to enum values. Each case has two different + // strings; the first one is the most current and the second one is + // deprecated, but available. + const string& error_msg = results->error_msg; + if (error_msg == "BadAuthentication" || error_msg == "badauth") { + results->auth_error = BadAuthentication; + } else if (error_msg == "NotVerified" || error_msg == "nv") { + results->auth_error = NotVerified; + } else if (error_msg == "TermsNotAgreed" || error_msg == "tna") { + results->auth_error = TermsNotAgreed; + } else if (error_msg == "Unknown" || error_msg == "unknown") { + results->auth_error = Unknown; + } else if (error_msg == "AccountDeleted" || error_msg == "adel") { + results->auth_error = AccountDeleted; + } else if (error_msg == "AccountDisabled" || error_msg == "adis") { + results->auth_error = AccountDisabled; + } else if (error_msg == "CaptchaRequired" || error_msg == "cr") { + results->auth_error = CaptchaRequired; + } else if (error_msg == "ServiceUnavailable" || error_msg == "ire") { + results->auth_error = ServiceUnavailable; + } +} + +// Reset all stored credentials, perhaps in preparation for letting a different +// user sign in. +void GaiaAuthenticator::ResetCredentials() { + DCHECK_EQ(MessageLoop::current(), message_loop_); + AuthResults blank; + auth_results_ = blank; +} + +void GaiaAuthenticator::SetUsernamePassword(const string& username, + const string& password) { + DCHECK_EQ(MessageLoop::current(), message_loop_); + auth_results_.password = password; + auth_results_.email = username; +} + +void GaiaAuthenticator::SetUsername(const string& username) { + DCHECK_EQ(MessageLoop::current(), message_loop_); + auth_results_.email = username; +} + +void GaiaAuthenticator::RenewAuthToken(const string& auth_token) { + DCHECK_EQ(MessageLoop::current(), message_loop_); + DCHECK(!this->auth_token().empty()); + auth_results_.auth_token = auth_token; +} +void GaiaAuthenticator::SetAuthToken(const string& auth_token, + SaveCredentials save) { + DCHECK_EQ(MessageLoop::current(), message_loop_); + auth_results_.auth_token = auth_token; + auth_results_.credentials_saved = save; +} + +bool GaiaAuthenticator::Authenticate(const string& user_name, + const string& password, + SaveCredentials should_save_credentials, + SignIn try_first) { + DCHECK_EQ(MessageLoop::current(), message_loop_); + const string empty; + return Authenticate(user_name, password, should_save_credentials, empty, + empty, try_first); +} + +} // namepace gaia + diff --git a/chrome/common/net/gaia/gaia_authenticator.h b/chrome/common/net/gaia/gaia_authenticator.h new file mode 100644 index 0000000..b165cf6 --- /dev/null +++ b/chrome/common/net/gaia/gaia_authenticator.h @@ -0,0 +1,331 @@ +// Copyright (c) 2009 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. +// +// Use this class to authenticate users with Gaia and access cookies sent +// by the Gaia servers. This class cannot be used on its own becaue it relies +// on a subclass to provide the virtual Post and GetBackoffDelaySeconds methods. +// +// Sample usage: +// class ActualGaiaAuthenticator : public gaia::GaiaAuthenticator { +// Provides actual implementation of Post and GetBackoffDelaySeconds. +// }; +// ActualGaiaAuthenticator gaia_auth("User-Agent", SERVICE_NAME, kGaiaUrl); +// if (gaia_auth.Authenticate("email", "passwd", SAVE_IN_MEMORY_ONLY, +// true)) { // Synchronous +// // Do something with: gaia_auth.auth_token(), or gaia_auth.sid(), +// // or gaia_auth.lsid() +// } +// +// Credentials can also be preserved for subsequent requests, though these are +// saved in plain-text in memory, and not very secure on client systems. The +// email address associated with the Gaia account can be read; the password is +// write-only. + +// TODO(sanjeevr): This class has been moved here from the bookmarks sync code. +// While it is a generic class that handles GAIA authentication, there are some +// artifacts of the sync code such as the SaveCredentials enum which needs to +// be cleaned up. +#ifndef CHROME_COMMON_NET_GAIA_GAIA_AUTHENTICATOR_H_ +#define CHROME_COMMON_NET_GAIA_GAIA_AUTHENTICATOR_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/message_loop.h" +#include "chrome/common/net/gaia/signin.h" +#include "chrome/common/deprecated/event_sys.h" +#include "googleurl/src/gurl.h" +#include "testing/gtest/include/gtest/gtest_prod.h" // For FRIEND_TEST + +namespace gaia { + +static const char kGaiaUrl[] = + "https://www.google.com:443/accounts/ClientLogin"; + +// Use of the following enum is odd. GaiaAuthenticator only looks at +// and DONT_SAVE_CREDENTIALS and SAVE_IN_MEMORY_ONLY (PERSIST_TO_DISK is == to +// SAVE_IN_MEMORY_ONLY for GaiaAuthenticator). + +enum SaveCredentials { + DONT_SAVE_CREDENTIALS, + SAVE_IN_MEMORY_ONLY, + PERSIST_TO_DISK // Saved in both memory and disk +}; + +// Error codes from Gaia. These will be set correctly for both Gaia V1 +// (/ClientAuth) and V2 (/ClientLogin) +enum AuthenticationError { + None = 0, + BadAuthentication = 1, + NotVerified = 2, + TermsNotAgreed = 3, + Unknown = 4, + AccountDeleted = 5, + AccountDisabled = 6, + CaptchaRequired = 7, + ServiceUnavailable = 8, + // Errors generated by this class not Gaia. + CredentialsNotSet = 9, + ConnectionUnavailable = 10 +}; + +class GaiaAuthenticator; + +struct GaiaAuthEvent { + enum { + GAIA_AUTH_FAILED, + GAIA_AUTH_SUCCEEDED, + GAIA_AUTHENTICATOR_DESTROYED + } + what_happened; + AuthenticationError error; + const GaiaAuthenticator* authenticator; + + // Lets us use GaiaAuthEvent as its own traits type in hookups. + typedef GaiaAuthEvent EventType; + static inline bool IsChannelShutdownEvent(const GaiaAuthEvent& event) { + return event.what_happened == GAIA_AUTHENTICATOR_DESTROYED; + } +}; + +// GaiaAuthenticator can be used to pass user credentials to Gaia and obtain +// cookies set by the Gaia servers. +class GaiaAuthenticator { + FRIEND_TEST(GaiaAuthenticatorTest, TestNewlineAtEndOfAuthTokenRemoved); + public: + + // Since GaiaAuthenticator can be used for any service, or by any client, you + // must include a user-agent and a service-id when creating one. The + // user_agent is a short string used for simple log analysis. gaia_url is used + // to choose the server to authenticate with (e.g. + // http://www.google.com/accounts/ClientLogin). + GaiaAuthenticator(const std::string& user_agent, + const std::string& service_id, + const std::string& gaia_url); + + virtual ~GaiaAuthenticator(); + + // This object should only be invoked from the AuthWatcherThread message + // loop, which is injected here. + void set_message_loop(const MessageLoop* loop) { + message_loop_ = loop; + } + + // Pass credentials to authenticate with, or use saved credentials via an + // overload. If authentication succeeds, you can retrieve the authentication + // token via the respective accessors. Returns a boolean indicating whether + // authentication succeeded or not. + bool Authenticate(const std::string& user_name, const std::string& password, + SaveCredentials should_save_credentials, + const std::string& captcha_token, + const std::string& captcha_value, + SignIn try_first); + + bool Authenticate(const std::string& user_name, const std::string& password, + SaveCredentials should_save_credentials, + SignIn try_first); + + // Pass the LSID to authenticate with. If the authentication succeeds, you can + // retrieve the authetication token via the respective accessors. Returns a + // boolean indicating whether authentication succeeded or not. + bool AuthenticateWithLsid(const std::string& lsid, bool long_lived_token); + + // Resets all stored cookies to their default values. + void ResetCredentials(); + + void SetUsernamePassword(const std::string& username, + const std::string& password); + + void SetUsername(const std::string& username); + + // Virtual for testing + virtual void RenewAuthToken(const std::string& auth_token); + void SetAuthToken(const std::string& auth_token, SaveCredentials); + + struct AuthResults { + SaveCredentials credentials_saved; + std::string email; + std::string password; + + // Fields that store various cookies. + std::string sid; + std::string lsid; + std::string auth_token; + + std::string primary_email; + + // Fields for items returned when authentication fails. + std::string error_msg; + enum AuthenticationError auth_error; + std::string auth_error_url; + std::string captcha_token; + std::string captcha_url; + SignIn signin; + + // TODO(skrul): When auth fails, the "signin" field of the results + // struct never gets set, which causes valgrind to complain. Give + // this field a value here so the error is suppressed. It turns + // out that the signin field has only one possible value, so the + // correct fix here would be to to remove it entirely. + AuthResults() : credentials_saved(DONT_SAVE_CREDENTIALS), + auth_error(None), + signin(GMAIL_SIGNIN) { } + }; + + protected: + + struct AuthParams { + GaiaAuthenticator* authenticator; + uint32 request_id; + SaveCredentials should_save_credentials; + std::string email; + std::string password; + std::string captcha_token; + std::string captcha_value; + SignIn try_first; + }; + + // mutex_ must be entered before calling this function. + AuthParams MakeParams(const std::string& user_name, + const std::string& password, + SaveCredentials should_save_credentials, + const std::string& captcha_token, + const std::string& captcha_value, + SignIn try_first); + + // The real Authenticate implementations. + bool AuthenticateImpl(const AuthParams& params); + bool AuthenticateImpl(const AuthParams& params, AuthResults* results); + + // virtual for testing purposes. + virtual bool PerformGaiaRequest(const AuthParams& params, + AuthResults* results); + virtual bool Post(const GURL& url, const std::string& post_body, + unsigned long* response_code, std::string* response_body) { + return false; + } + + // Caller should fill in results->LSID before calling. Result in + // results->primary_email. + virtual bool LookupEmail(AuthResults* results); + + // Subclasses must override to provide a backoff delay. It is virtual instead + // of pure virtual for testing purposes. + // TODO(sanjeevr): This should be made pure virtual. But this class is + // currently directly being used in sync/engine/authenticator.cc, which is + // wrong. + virtual int GetBackoffDelaySeconds(int current_backoff_delay) { + NOTREACHED(); + return current_backoff_delay; + } + + public: + // Retrieve email. + inline std::string email() const { + DCHECK_EQ(MessageLoop::current(), message_loop_); + return auth_results_.email; + } + + // Retrieve password. + inline std::string password() const { + DCHECK_EQ(MessageLoop::current(), message_loop_); + return auth_results_.password; + } + + // Retrieve AuthToken, if previously authenticated; otherwise returns "". + inline std::string auth_token() const { + DCHECK_EQ(MessageLoop::current(), message_loop_); + return auth_results_.auth_token; + } + + // Retrieve SID cookie. For details, see the Google Accounts documentation. + inline std::string sid() const { + DCHECK_EQ(MessageLoop::current(), message_loop_); + return auth_results_.sid; + } + + // Retrieve LSID cookie. For details, see the Google Accounts documentation. + inline std::string lsid() const { + DCHECK_EQ(MessageLoop::current(), message_loop_); + return auth_results_.lsid; + } + + // Get last authentication error. + inline enum AuthenticationError auth_error() const { + DCHECK_EQ(MessageLoop::current(), message_loop_); + return auth_results_.auth_error; + } + + inline std::string auth_error_url() const { + DCHECK_EQ(MessageLoop::current(), message_loop_); + return auth_results_.auth_error_url; + } + + inline std::string captcha_token() const { + DCHECK_EQ(MessageLoop::current(), message_loop_); + return auth_results_.captcha_token; + } + + inline std::string captcha_url() const { + DCHECK_EQ(MessageLoop::current(), message_loop_); + return auth_results_.captcha_url; + } + + inline AuthResults results() const { + DCHECK_EQ(MessageLoop::current(), message_loop_); + return auth_results_; + } + + typedef EventChannel<GaiaAuthEvent, Lock> Channel; + + inline Channel* channel() const { + return channel_; + } + + private: + bool IssueAuthToken(AuthResults* results, const std::string& service_id, + bool long_lived_token); + + // Helper method to parse response when authentication succeeds. + void ExtractTokensFrom(const std::string& response, AuthResults* results); + // Helper method to parse response when authentication fails. + void ExtractAuthErrorFrom(const std::string& response, AuthResults* results); + + // Fields for the obvious data items. + const std::string user_agent_; + const std::string service_id_; + const std::string gaia_url_; + + AuthResults auth_results_; + + // When multiple async requests are running, only the one that started most + // recently updates the values. + // + // Note that even though this code was written to handle multiple requests + // simultaneously, the sync code issues auth requests one at a time. + uint32 request_count_; + + Channel* channel_; + + // Used to compute backoff time for next allowed authentication. + int delay_; // In seconds. + // On Windows, time_t is 64-bit by default. Even though we have defined the + // _USE_32BIT_TIME_T preprocessor flag, other libraries including this header + // may not have that preprocessor flag defined resulting in mismatched class + // sizes. So we explicitly define it as 32-bit on Windows. + // TODO(sanjeevr): Change this to to use base::Time +#if defined(OS_WIN) + __time32_t next_allowed_auth_attempt_time_; +#else // defined(OS_WIN) + time_t next_allowed_auth_attempt_time_; +#endif // defined(OS_WIN) + int early_auth_attempt_count_; + + // The message loop all our methods are invoked on. + const MessageLoop* message_loop_; +}; + +} // namespace gaia +#endif // CHROME_COMMON_NET_GAIA_GAIA_AUTHENTICATOR_H_ + diff --git a/chrome/common/net/gaia/gaia_authenticator_unittest.cc b/chrome/common/net/gaia/gaia_authenticator_unittest.cc new file mode 100644 index 0000000..1f9813d --- /dev/null +++ b/chrome/common/net/gaia/gaia_authenticator_unittest.cc @@ -0,0 +1,49 @@ +// Copyright (c) 2009 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_authenticator.h" + +#include <string> + +#include "chrome/common/net/http_return.h" +#include "googleurl/src/gurl.h" +#include "testing/gtest/include/gtest/gtest.h" + +using std::string; + +namespace gaia { + +class GaiaAuthenticatorTest : public testing::Test { }; + +class GaiaAuthMockForGaiaAuthenticator : public GaiaAuthenticator { + public: + GaiaAuthMockForGaiaAuthenticator() + : GaiaAuthenticator("useragent", "serviceid", "http://gaia_url") {} + ~GaiaAuthMockForGaiaAuthenticator() {} + protected: + bool Post(const GURL& url, const string& post_body, + unsigned long* response_code, string* response_body) { + *response_code = RC_REQUEST_OK; + response_body->assign("body\n"); + return true; + } + + int GetBackoffDelaySeconds( + int current_backoff_delay) { + // Dummy delay value. + return 5; + } +}; + +TEST(GaiaAuthenticatorTest, TestNewlineAtEndOfAuthTokenRemoved) { + GaiaAuthMockForGaiaAuthenticator mock_auth; + MessageLoop message_loop; + mock_auth.set_message_loop(&message_loop); + GaiaAuthenticator::AuthResults results; + EXPECT_TRUE(mock_auth.IssueAuthToken(&results, "sid", true)); + EXPECT_EQ(0, results.auth_token.compare("body")); +} + +} // namespace gaia + diff --git a/chrome/common/net/gaia/signin.h b/chrome/common/net/gaia/signin.h new file mode 100644 index 0000000..48f3654 --- /dev/null +++ b/chrome/common/net/gaia/signin.h @@ -0,0 +1,18 @@ +// Copyright (c) 2009 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_SIGNIN_H_ +#define CHROME_COMMON_NET_GAIA_SIGNIN_H_ + +namespace gaia { +// This enumeration is here since we used to support hosted and non-hosted +// accounts, but now only the latter is supported. +enum SignIn { + // The account foo@domain is authenticated as a consumer account. + GMAIL_SIGNIN +}; + +} // namespace gaia +#endif // CHROME_COMMON_NET_GAIA_SIGNIN_H_ + diff --git a/chrome/common/net/http_return.h b/chrome/common/net/http_return.h new file mode 100644 index 0000000..f47d1bb --- /dev/null +++ b/chrome/common/net/http_return.h @@ -0,0 +1,17 @@ +// Copyright (c) 2009 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_HTTP_RETURN_H_ +#define CHROME_COMMON_NET_HTTP_RETURN_H_ + +// TODO(sanjeevr): This has been moved from the sync library so it only +// contains a few HTTP return codes. Add more HTTP return codes. +enum HTTPReturnCode { + RC_REQUEST_OK = 200, + RC_UNAUTHORIZED = 401, + RC_FORBIDDEN = 403, +}; + +#endif // CHROME_COMMON_NET_HTTP_RETURN_H_ + |