diff options
21 files changed, 477 insertions, 90 deletions
diff --git a/chrome/browser/chromeos/external_cookie_handler.cc b/chrome/browser/chromeos/external_cookie_handler.cc index 525ed80..5881532 100644 --- a/chrome/browser/chromeos/external_cookie_handler.cc +++ b/chrome/browser/chromeos/external_cookie_handler.cc @@ -68,6 +68,7 @@ bool ExternalCookieHandler::HandleCookies(net::CookieStore *cookie_store) { // it in to the cookie jar. std::string cookie_line = ReadLine(kChunkSize); while (!cookie_line.empty()) { + LOG(WARNING) << cookie_line; if (!cookie_store->SetCookieWithOptions(url, cookie_line, options)) return false; cookie_line = ReadLine(kChunkSize); diff --git a/chrome/browser/chromeos/login/auth_response_handler.cc b/chrome/browser/chromeos/login/auth_response_handler.cc new file mode 100644 index 0000000..fea29af --- /dev/null +++ b/chrome/browser/chromeos/login/auth_response_handler.cc @@ -0,0 +1,15 @@ +// 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/browser/chromeos/login/auth_response_handler.h" + +const char AuthResponseHandler::kClientLoginUrl[] = + "https://www.google.com/accounts/ClientLogin"; +const char AuthResponseHandler::kIssueAuthTokenUrl[] = + "https://www.google.com/accounts/IssueAuthToken"; +// 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 AuthResponseHandler::kTokenAuthUrl[] = + "https://www.google.com/accounts/TokenAuth?" + "continue=http://www.google.com/webhp&source=chromeos&auth="; diff --git a/chrome/browser/chromeos/login/auth_response_handler.h b/chrome/browser/chromeos/login/auth_response_handler.h new file mode 100644 index 0000000..0c672c7 --- /dev/null +++ b/chrome/browser/chromeos/login/auth_response_handler.h @@ -0,0 +1,36 @@ +// 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_AUTH_RESPONSE_HANDLER_H_ +#define CHROME_BROWSER_CHROMEOS_LOGIN_AUTH_RESPONSE_HANDLER_H_ + +#include <string> + +#include "chrome/browser/net/url_fetcher.h" + +class GURL; + +class AuthResponseHandler { + public: + AuthResponseHandler() {} + virtual ~AuthResponseHandler() {} + + // True if this object can handle responses from |url|, false otherwise. + virtual bool CanHandle(const GURL& url) = 0; + + // Caller takes ownership of return value. + // Takes in |to_process|, creates an appropriate URLFetcher to handle + // the next step, sets |catcher| to get called back when that fetcher is done. + // Starts the fetch and returns the fetcher, so the the caller can handle + // the object lifetime. + virtual URLFetcher* Handle(const std::string& to_process, + URLFetcher::Delegate* catcher) = 0; + + // The URLs for different calls in the Google Accounts programmatic login API. + static const char kClientLoginUrl[]; + static const char kIssueAuthTokenUrl[]; + static const char kTokenAuthUrl[]; +}; + +#endif // CHROME_BROWSER_CHROMEOS_LOGIN_AUTH_RESPONSE_HANDLER_H_ diff --git a/chrome/browser/chromeos/login/authenticator.h b/chrome/browser/chromeos/login/authenticator.h index d03be58..d335127 100644 --- a/chrome/browser/chromeos/login/authenticator.h +++ b/chrome/browser/chromeos/login/authenticator.h @@ -5,6 +5,8 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_AUTHENTICATOR_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_AUTHENTICATOR_H_ +#include <vector> + #include "base/logging.h" #include "chrome/browser/chromeos/login/login_status_consumer.h" @@ -42,7 +44,7 @@ class StubAuthenticator : public Authenticator { // Returns true after calling OnLoginSuccess(). virtual bool Authenticate(const std::string& username, const std::string& password) { - consumer_->OnLoginSuccess(username); + consumer_->OnLoginSuccess(username, std::vector<std::string>()); return true; } diff --git a/chrome/browser/chromeos/login/client_login_response_handler.cc b/chrome/browser/chromeos/login/client_login_response_handler.cc new file mode 100644 index 0000000..97a2edc --- /dev/null +++ b/chrome/browser/chromeos/login/client_login_response_handler.cc @@ -0,0 +1,45 @@ +// 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/browser/chromeos/login/client_login_response_handler.h" + +#include <algorithm> +#include <string> + +#include "chrome/browser/chromeos/login/google_authenticator.h" +#include "chrome/browser/net/chrome_url_request_context.h" +#include "chrome/browser/net/url_fetcher.h" +#include "net/base/load_flags.h" + +// By setting "service=gaia", we get an uber-auth-token back. +const char ClientLoginResponseHandler::kService[] = "service=gaia"; + +// Overridden from AuthResponseHandler. +bool ClientLoginResponseHandler::CanHandle(const GURL& url) { + return (url.spec().find(AuthResponseHandler::kClientLoginUrl) != + std::string::npos); +} + +// Overridden from AuthResponseHandler. +URLFetcher* ClientLoginResponseHandler::Handle( + const std::string& to_process, + URLFetcher::Delegate* catcher) { + LOG(INFO) << "ClientLogin successful!"; + payload_.assign(to_process); + std::replace(payload_.begin(), payload_.end(), '\n', '&'); + payload_.append(kService); + + URLFetcher* fetcher = + new URLFetcher(GURL(AuthResponseHandler::kIssueAuthTokenUrl), + URLFetcher::POST, + catcher); + fetcher->set_load_flags(net::LOAD_DO_NOT_SEND_COOKIES); + fetcher->set_upload_data("application/x-www-form-urlencoded", payload_); + if (getter_) { + LOG(INFO) << "Fetching " << fetcher->url().spec(); + fetcher->set_request_context(getter_); + fetcher->Start(); + } + return fetcher; +} diff --git a/chrome/browser/chromeos/login/client_login_response_handler.h b/chrome/browser/chromeos/login/client_login_response_handler.h new file mode 100644 index 0000000..2650261 --- /dev/null +++ b/chrome/browser/chromeos/login/client_login_response_handler.h @@ -0,0 +1,42 @@ +// 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_CLIENT_LOGIN_RESPONSE_HANDLER_H_ +#define CHROME_BROWSER_CHROMEOS_LOGIN_CLIENT_LOGIN_RESPONSE_HANDLER_H_ + +#include <string> + +#include "base/logging.h" +#include "chrome/browser/chromeos/login/auth_response_handler.h" + +class URLRequestContextGetter; + +class ClientLoginResponseHandler : public AuthResponseHandler { + public: + explicit ClientLoginResponseHandler(URLRequestContextGetter* getter) + : getter_(getter) {} + ~ClientLoginResponseHandler() {} + + // Overridden from AuthResponseHandler. + virtual bool CanHandle(const GURL& url); + + // Overridden from AuthResponseHandler. + // Takes in a response from ClientLogin, formats into an appropriate query + // to sent to IssueAuthToken and issues said query. |catcher| will receive + // the response to the fetch. + virtual URLFetcher* Handle(const std::string& to_process, + URLFetcher::Delegate* catcher); + + // exposed for tests. + std::string payload() { return payload_; } + + // A string that tells the Accounts backend to which service we're trying to + // authenticate. + static const char kService[]; + private: + std::string payload_; + URLRequestContextGetter* getter_; +}; + +#endif // CHROME_BROWSER_CHROMEOS_LOGIN_CLIENT_LOGIN_RESPONSE_HANDLER_H_ diff --git a/chrome/browser/chromeos/login/existing_user_controller.cc b/chrome/browser/chromeos/login/existing_user_controller.cc index 5fd0e65..e8df4bc 100644 --- a/chrome/browser/chromeos/login/existing_user_controller.cc +++ b/chrome/browser/chromeos/login/existing_user_controller.cc @@ -144,13 +144,14 @@ void ExistingUserController::OnLoginFailure(const std::string error) { chromeos::WmIpc::instance()->SendMessage(message); } -void ExistingUserController::OnLoginSuccess(const std::string username) { +void ExistingUserController::OnLoginSuccess(const std::string username, + std::vector<std::string> cookies) { // Hide the login windows now. STLDeleteElements(&controllers_); background_window_->Close(); - chromeos::login_utils::CompleteLogin(username); + chromeos::login_utils::CompleteLogin(username, cookies); // Delay deletion as we're on the stack. MessageLoop::current()->DeleteSoon(FROM_HERE, this); diff --git a/chrome/browser/chromeos/login/existing_user_controller.h b/chrome/browser/chromeos/login/existing_user_controller.h index ea25d99..9e587fd 100644 --- a/chrome/browser/chromeos/login/existing_user_controller.h +++ b/chrome/browser/chromeos/login/existing_user_controller.h @@ -65,7 +65,8 @@ class ExistingUserController : public WmMessageListener::Observer, // LoginStatusConsumer: virtual void OnLoginFailure(const std::string error); - virtual void OnLoginSuccess(const std::string username); + virtual void OnLoginSuccess(const std::string username, + std::vector<std::string> cookies); // Bounds of the background window. const gfx::Rect background_bounds_; diff --git a/chrome/browser/chromeos/login/google_authenticator.cc b/chrome/browser/chromeos/login/google_authenticator.cc index 0cb2ae4..347f5e9 100644 --- a/chrome/browser/chromeos/login/google_authenticator.cc +++ b/chrome/browser/chromeos/login/google_authenticator.cc @@ -5,6 +5,7 @@ #include <errno.h> #include <string> #include <vector> +#include <time.h> #include "base/logging.h" #include "base/file_path.h" @@ -14,9 +15,12 @@ #include "base/string_util.h" #include "base/third_party/nss/blapi.h" #include "base/third_party/nss/sha256.h" +#include "base/time.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chrome_thread.h" #include "chrome/browser/chromeos/cros/cryptohome_library.h" +#include "chrome/browser/chromeos/login/client_login_response_handler.h" +#include "chrome/browser/chromeos/login/issue_response_handler.h" #include "chrome/browser/chromeos/login/google_authenticator.h" #include "chrome/browser/chromeos/login/login_status_consumer.h" #include "chrome/browser/net/chrome_url_request_context.h" @@ -28,11 +32,11 @@ #include "net/url_request/url_request_status.h" #include "third_party/libjingle/files/talk/base/urlencode.h" +using base::Time; +using base::TimeDelta; using namespace chromeos; using namespace file_util; -const char GoogleAuthenticator::kClientLoginUrl[] = - "https://www.google.com/accounts/ClientLogin"; const char GoogleAuthenticator::kFormat[] = "Email=%s&" "Passwd=%s&" @@ -42,6 +46,7 @@ const char GoogleAuthenticator::kFormat[] = const char GoogleAuthenticator::kCookiePersistence[] = "true"; const char GoogleAuthenticator::kAccountType[] = "HOSTED_OR_GOOGLE"; const char GoogleAuthenticator::kSource[] = "chromeos"; +const int GoogleAuthenticator::kHttpSuccess = 200; // Chromium OS system salt stored here const char GoogleAuthenticator::kSystemSalt[] = "/home/.shadow/salt"; @@ -55,12 +60,11 @@ bool GoogleAuthenticator::Authenticate(const std::string& username, PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); ProfileManager* profile_manager = g_browser_process->profile_manager(); Profile* profile = profile_manager->GetDefaultProfile(user_data_dir); - URLRequestContextGetter *getter = - profile->GetOffTheRecordProfile()->GetRequestContext(); - fetcher_.reset(new URLFetcher(GURL(kClientLoginUrl), + getter_ = profile->GetOffTheRecordProfile()->GetRequestContext(); + fetcher_.reset(new URLFetcher(GURL(AuthResponseHandler::kClientLoginUrl), URLFetcher::POST, this)); - fetcher_->set_request_context(getter); + fetcher_->set_request_context(getter_); fetcher_->set_load_flags(net::LOAD_DO_NOT_SEND_COOKIES); // TODO(cmasone): be more careful about zeroing memory that stores // the user's password. @@ -72,6 +76,10 @@ bool GoogleAuthenticator::Authenticate(const std::string& username, kSource); fetcher_->set_upload_data("application/x-www-form-urlencoded", body); fetcher_->Start(); + if (!client_login_handler_.get()) + client_login_handler_.reset(new ClientLoginResponseHandler(getter_)); + if (!issue_handler_.get()) + issue_handler_.reset(new IssueResponseHandler(getter_)); username_.assign(username); StoreHashedPassword(password); return true; @@ -84,27 +92,32 @@ void GoogleAuthenticator::OnURLFetchComplete(const URLFetcher* source, const ResponseCookies& cookies, const std::string& data) { if (status.is_success() && response_code == 200) { - LOG(INFO) << "ClientLogin successful!"; - ChromeThread::PostTask( - ChromeThread::UI, FROM_HERE, - NewRunnableFunction(GoogleAuthenticator::OnLoginSuccess, - consumer_, - library_, - username_, - ascii_hash_, - data)); - } else if (!status.is_success() && - library_->CheckKey(username_.c_str(), ascii_hash_.c_str())) { - // The fetch didn't succeed, but offline login did. - LOG(INFO) << "Offline login successful!"; + if (client_login_handler_->CanHandle(url)) { + fetcher_.reset(client_login_handler_->Handle(data, this)); + } else if (issue_handler_->CanHandle(url)) { + fetcher_.reset(issue_handler_->Handle(data, this)); + } else { + LOG(INFO) << "Online login successful!"; + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableFunction(GoogleAuthenticator::OnLoginSuccess, + consumer_, + library_, + username_, + ascii_hash_, + cookies)); + } + } else if (!status.is_success()) { + LOG(INFO) << "Network fail"; + // The fetch failed for network reasons, try offline login. ChromeThread::PostTask( ChromeThread::UI, FROM_HERE, - NewRunnableFunction(GoogleAuthenticator::OnLoginSuccess, + NewRunnableFunction(GoogleAuthenticator::CheckOffline, consumer_, library_, username_, ascii_hash_, - data)); + status)); } else { std::string error; if (status.is_success()) { @@ -119,30 +132,46 @@ void GoogleAuthenticator::OnURLFetchComplete(const URLFetcher* source, NewRunnableFunction( GoogleAuthenticator::OnLoginFailure, consumer_, error)); } - fetcher_.reset(NULL); } // static -void GoogleAuthenticator::OnLoginSuccess( - LoginStatusConsumer* consumer, - CryptohomeLibrary* library, - const std::string& username, - const std::string& passhash, - const std::string& client_login_data) { - bool success = library->Mount(username.c_str(), passhash.c_str()); - - // Also, convert client_login_data into legit cookies. - - if (success) { - consumer->OnLoginSuccess(username); +void GoogleAuthenticator::OnLoginSuccess(LoginStatusConsumer* consumer, + CryptohomeLibrary* library, + const std::string& username, + const std::string& passhash, + const ResponseCookies& cookies) { + if (library->Mount(username.c_str(), passhash.c_str())) + consumer->OnLoginSuccess(username, cookies); + else + GoogleAuthenticator::OnLoginFailure(consumer, "Could not mount cryptohome"); +} + +// static +void GoogleAuthenticator::CheckOffline(LoginStatusConsumer* consumer, + CryptohomeLibrary* library, + const std::string& username, + const std::string& passhash, + const URLRequestStatus& status) { + if (library->CheckKey(username.c_str(), passhash.c_str())) { + // The fetch didn't succeed, but offline login did. + LOG(INFO) << "Offline login successful!"; + ResponseCookies cookies; + GoogleAuthenticator::OnLoginSuccess(consumer, + library, + username, + passhash, + cookies); } else { - GoogleAuthenticator::OnLoginFailure(consumer, client_login_data); + // We couldn't hit the network, and offline login failed. + GoogleAuthenticator::OnLoginFailure(consumer, strerror(status.os_error())); } } // static void GoogleAuthenticator::OnLoginFailure(LoginStatusConsumer* consumer, const std::string& data) { + // TODO(cmasone): what can we do to expose these OS/server-side error strings + // in an internationalizable way? consumer->OnLoginFailure(data); } diff --git a/chrome/browser/chromeos/login/google_authenticator.h b/chrome/browser/chromeos/login/google_authenticator.h index ec1806b..f8b6e10 100644 --- a/chrome/browser/chromeos/login/google_authenticator.h +++ b/chrome/browser/chromeos/login/google_authenticator.h @@ -15,6 +15,7 @@ #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/browser/chromeos/login/auth_response_handler.h" #include "chrome/browser/net/url_fetcher.h" class LoginStatusConsumer; @@ -25,17 +26,25 @@ class GoogleAuthenticator : public Authenticator, public URLFetcher::Delegate { public: GoogleAuthenticator(LoginStatusConsumer* consumer, - chromeos::CryptohomeLibrary* library) + chromeos::CryptohomeLibrary* library, + AuthResponseHandler* cl_handler, + AuthResponseHandler* i_handler) : Authenticator(consumer), library_(library), - fetcher_(NULL) { + fetcher_(NULL), + getter_(NULL), + client_login_handler_(cl_handler), + issue_handler_(i_handler) { if (!library && chromeos::CrosLibrary::EnsureLoaded()) library_ = chromeos::CryptohomeLibrary::Get(); } explicit GoogleAuthenticator(LoginStatusConsumer* consumer) : Authenticator(consumer), - fetcher_(NULL) { + fetcher_(NULL), + getter_(NULL), + client_login_handler_(NULL), + issue_handler_(NULL) { CHECK(chromeos::CrosLibrary::EnsureLoaded()); library_ = chromeos::CryptohomeLibrary::Get(); } @@ -43,7 +52,11 @@ class GoogleAuthenticator : public Authenticator, virtual ~GoogleAuthenticator() {} // Given a |username| and |password|, this method attempts to authenticate to - // the Google accounts servers. + // the Google accounts servers. The ultimate result is either a callback to + // consumer_->OnLoginSuccess() with the |username| and a vector of + // authentication cookies or a callback to consumer_->OnLoginFailure() with + // an error message. + // // Returns true if the attempt gets sent successfully and false if not. bool Authenticate(const std::string& username, const std::string& password); @@ -59,6 +72,9 @@ class GoogleAuthenticator : public Authenticator, const ResponseCookies& cookies, const std::string& data); + // Returns the ascii encoding of the system salt. + std::string SaltAsAscii(); + // I need these static methods so I can PostTasks that use methods // of sublcasses of LoginStatusConsumer. I can't seem to make // RunnableMethods out of methods belonging to subclasses without referring @@ -68,7 +84,12 @@ class GoogleAuthenticator : public Authenticator, chromeos::CryptohomeLibrary* library, const std::string& username, const std::string& passhash, - const std::string& client_login_data); + const ResponseCookies& cookies); + static void CheckOffline(LoginStatusConsumer* consumer, + chromeos::CryptohomeLibrary* library, + const std::string& username, + const std::string& passhash, + const URLRequestStatus& status); static void OnLoginFailure(LoginStatusConsumer* consumer, const std::string& data); @@ -76,23 +97,25 @@ class GoogleAuthenticator : public Authenticator, void set_system_salt(const std::vector<unsigned char>& fake_salt) { system_salt_ = fake_salt; } + void set_username(const std::string& fake_user) { username_ = fake_user; } void set_password_hash(const std::string& fake_hash) { ascii_hash_ = fake_hash; } - // Returns the ascii encoding of the system salt. - std::string SaltAsAscii(); private: static const char kCookiePersistence[]; static const char kAccountType[]; static const char kSource[]; - static const char kClientLoginUrl[]; static const char kFormat[]; + static const int kHttpSuccess; static const char kSystemSalt[]; static const char kOpenSSLMagic[]; chromeos::CryptohomeLibrary* library_; scoped_ptr<URLFetcher> fetcher_; + URLRequestContextGetter* getter_; + scoped_ptr<AuthResponseHandler> client_login_handler_; + scoped_ptr<AuthResponseHandler> issue_handler_; std::vector<unsigned char> system_salt_; std::string username_; std::string ascii_hash_; diff --git a/chrome/browser/chromeos/login/google_authenticator_unittest.cc b/chrome/browser/chromeos/login/google_authenticator_unittest.cc index bfc4a19..570463f 100644 --- a/chrome/browser/chromeos/login/google_authenticator_unittest.cc +++ b/chrome/browser/chromeos/login/google_authenticator_unittest.cc @@ -3,6 +3,8 @@ // found in the LICENSE file. #include "chrome/browser/chromeos/login/google_authenticator.h" +#include "chrome/browser/chromeos/login/client_login_response_handler.h" +#include "chrome/browser/chromeos/login/issue_response_handler.h" #include <errno.h> #include <string> @@ -11,7 +13,9 @@ #include "base/message_loop.h" #include "base/scoped_ptr.h" #include "chrome/browser/chromeos/cros/mock_cryptohome_library.h" +#include "chrome/browser/chromeos/login/mock_auth_response_handler.h" #include "chrome/browser/chrome_thread.h" +#include "chrome/browser/net/url_fetcher.h" #include "googleurl/src/gurl.h" #include "net/url_request/url_request_status.h" #include "testing/gtest/include/gtest/gtest.h" @@ -27,12 +31,13 @@ class MockConsumer : public LoginStatusConsumer { MockConsumer() {} ~MockConsumer() {} MOCK_METHOD1(OnLoginFailure, void(const std::string error)); - MOCK_METHOD1(OnLoginSuccess, void(const std::string username)); + MOCK_METHOD2(OnLoginSuccess, void(const std::string username, + const std::vector<std::string> cookies)); }; class GoogleAuthenticatorTest : public ::testing::Test { public: - GoogleAuthenticatorTest() { + GoogleAuthenticatorTest() : username_("me@nowhere.org") { memset(fake_hash_, 0, sizeof(fake_hash_)); fake_hash_[0] = 10; fake_hash_[1] = 1; @@ -45,7 +50,7 @@ class GoogleAuthenticatorTest : public ::testing::Test { unsigned char fake_hash_[32]; std::string hash_ascii_; std::string username_; - std::string error_; + ResponseCookies cookies_; }; TEST_F(GoogleAuthenticatorTest, SaltToAsciiTest) { @@ -56,38 +61,54 @@ TEST_F(GoogleAuthenticatorTest, SaltToAsciiTest) { std::vector<unsigned char> salt_v(fake_salt, fake_salt + sizeof(fake_salt)); MockCryptohomeLibrary library; - GoogleAuthenticator auth(NULL, &library); + GoogleAuthenticator auth(NULL, &library, NULL, NULL); auth.set_system_salt(salt_v); EXPECT_EQ("0a010000000000a0", auth.SaltAsAscii()); } -TEST_F(GoogleAuthenticatorTest, OnLoginSuccessTest) { - error_.assign("Unexpected error"); +TEST_F(GoogleAuthenticatorTest, ClientLoginResponseHandlerTest) { + ClientLoginResponseHandler handler(NULL); + std::string input("a\nb\n"); + std::string expected("a&b&"); + expected.append(ClientLoginResponseHandler::kService); + + scoped_ptr<URLFetcher> fetcher(handler.Handle(input, NULL)); + EXPECT_EQ(expected, handler.payload()); +} + +TEST_F(GoogleAuthenticatorTest, IssueResponseHandlerTest) { + IssueResponseHandler handler(NULL); + std::string input("a\n"); + std::string expected(IssueResponseHandler::kTokenAuthUrl); + expected.append(input); + + scoped_ptr<URLFetcher> fetcher(handler.Handle(input, NULL)); + EXPECT_EQ(expected, handler.token_url()); +} +TEST_F(GoogleAuthenticatorTest, OnLoginSuccessTest) { MockConsumer consumer; - EXPECT_CALL(consumer, OnLoginSuccess(username_)); + EXPECT_CALL(consumer, OnLoginSuccess(username_, _)); MockCryptohomeLibrary library; EXPECT_CALL(library, Mount(username_, hash_ascii_)) .WillOnce(Return(true)); - GoogleAuthenticator auth(&consumer, &library); - auth.OnLoginSuccess(&consumer, &library, username_, hash_ascii_, error_); + GoogleAuthenticator auth(&consumer, &library, NULL, NULL); + auth.OnLoginSuccess(&consumer, &library, username_, hash_ascii_, cookies_); } TEST_F(GoogleAuthenticatorTest, MountFailureTest) { - error_.assign("Expected error"); - MockConsumer consumer; - EXPECT_CALL(consumer, OnLoginFailure(error_)); + EXPECT_CALL(consumer, OnLoginFailure(_)); MockCryptohomeLibrary library; EXPECT_CALL(library, Mount(username_, hash_ascii_)) .WillOnce(Return(false)); - GoogleAuthenticator auth(&consumer, &library); - auth.OnLoginSuccess(&consumer, &library, username_, hash_ascii_, error_); + GoogleAuthenticator auth(&consumer, &library, NULL, NULL); + auth.OnLoginSuccess(&consumer, &library, username_, hash_ascii_, cookies_); } static void Quit() { MessageLoop::current()->Quit(); } @@ -96,22 +117,22 @@ TEST_F(GoogleAuthenticatorTest, LoginNetFailureTest) { ChromeThread ui_thread(ChromeThread::UI, &message_loop); int error_no = ECONNRESET; - error_.assign(strerror(error_no)); + std::string data(strerror(error_no)); GURL source; - ResponseCookies cookies; URLRequestStatus status(URLRequestStatus::FAILED, error_no); MockConsumer consumer; - EXPECT_CALL(consumer, OnLoginFailure(error_)) + EXPECT_CALL(consumer, OnLoginFailure(data)) .WillOnce(InvokeWithoutArgs(Quit)); MockCryptohomeLibrary library; EXPECT_CALL(library, CheckKey(username_, hash_ascii_)) .WillOnce(Return(false)); - GoogleAuthenticator auth(&consumer, &library); + GoogleAuthenticator auth(&consumer, &library, NULL, NULL); auth.set_password_hash(hash_ascii_); - auth.OnURLFetchComplete(NULL, source, status, 0, cookies, std::string()); + auth.set_username(username_); + auth.OnURLFetchComplete(NULL, source, status, 0, cookies_, data); MessageLoop::current()->Run(); // So tasks can be posted. } @@ -119,19 +140,18 @@ TEST_F(GoogleAuthenticatorTest, LoginDeniedTest) { MessageLoopForUI message_loop; ChromeThread ui_thread(ChromeThread::UI, &message_loop); - error_.assign("Error: NO!"); - GURL source; - ResponseCookies cookies; + std::string data("Error: NO!"); + GURL source(AuthResponseHandler::kTokenAuthUrl); URLRequestStatus status(URLRequestStatus::SUCCESS, 0); MockConsumer consumer; - EXPECT_CALL(consumer, OnLoginFailure(error_)) + EXPECT_CALL(consumer, OnLoginFailure(data)) .WillOnce(InvokeWithoutArgs(Quit)); MockCryptohomeLibrary library; - GoogleAuthenticator auth(&consumer, &library); - auth.OnURLFetchComplete(NULL, source, status, 403, cookies, error_); + GoogleAuthenticator auth(&consumer, &library, NULL, NULL); + auth.OnURLFetchComplete(NULL, source, status, 403, cookies_, data); MessageLoop::current()->Run(); // So tasks can be posted. } @@ -140,14 +160,13 @@ TEST_F(GoogleAuthenticatorTest, OfflineLoginTest) { ChromeThread ui_thread(ChromeThread::UI, &message_loop); int error_no = ECONNRESET; - error_.assign(strerror(error_no)); + std::string data(strerror(error_no)); GURL source; - ResponseCookies cookies; URLRequestStatus status(URLRequestStatus::FAILED, error_no); MockConsumer consumer; - EXPECT_CALL(consumer, OnLoginSuccess(username_)) + EXPECT_CALL(consumer, OnLoginSuccess(username_, cookies_)) .WillOnce(InvokeWithoutArgs(Quit)); MockCryptohomeLibrary library; EXPECT_CALL(library, CheckKey(username_, hash_ascii_)) @@ -155,9 +174,49 @@ TEST_F(GoogleAuthenticatorTest, OfflineLoginTest) { EXPECT_CALL(library, Mount(username_, hash_ascii_)) .WillOnce(Return(true)); - GoogleAuthenticator auth(&consumer, &library); + GoogleAuthenticator auth(&consumer, &library, NULL, NULL); + auth.set_password_hash(hash_ascii_); + auth.set_username(username_); + auth.OnURLFetchComplete(NULL, source, status, 0, cookies_, data); + MessageLoop::current()->Run(); // So tasks can be posted. +} + +TEST_F(GoogleAuthenticatorTest, ClientLoginPassIssueAuthTokenFailTest) { + MessageLoopForUI message_loop; + ChromeThread ui_thread(ChromeThread::UI, &message_loop); + + std::string data("Error: NO!"); + GURL cl_source(AuthResponseHandler::kClientLoginUrl); + GURL iat_source(AuthResponseHandler::kIssueAuthTokenUrl); + URLRequestStatus status(URLRequestStatus::SUCCESS, 0); + + MockConsumer consumer; + EXPECT_CALL(consumer, OnLoginFailure(data)) + .WillOnce(InvokeWithoutArgs(Quit)); + MockCryptohomeLibrary library; + MockAuthResponseHandler* cl_handler = new MockAuthResponseHandler; + EXPECT_CALL(*cl_handler, CanHandle(cl_source)) + .WillOnce(Return(true)); + + GoogleAuthenticator auth(&consumer, + &library, + cl_handler, // takes ownership. + new IssueResponseHandler(NULL)); auth.set_password_hash(hash_ascii_); - auth.OnURLFetchComplete(NULL, source, status, 0, cookies, std::string()); + auth.set_username(username_); + + EXPECT_CALL(*cl_handler, Handle(_, &auth)) + .WillOnce(Return(new URLFetcher(GURL(""), + URLFetcher::POST, + &auth))); + + auth.OnURLFetchComplete(NULL, + cl_source, + status, + 200, + cookies_, + std::string()); + auth.OnURLFetchComplete(NULL, iat_source, status, 403, cookies_, data); MessageLoop::current()->Run(); // So tasks can be posted. } @@ -165,20 +224,23 @@ TEST_F(GoogleAuthenticatorTest, OnlineLoginTest) { MessageLoopForUI message_loop; ChromeThread ui_thread(ChromeThread::UI, &message_loop); - GURL source; - ResponseCookies cookies; + GURL source(AuthResponseHandler::kTokenAuthUrl); URLRequestStatus status(URLRequestStatus::SUCCESS, 0); MockConsumer consumer; - EXPECT_CALL(consumer, OnLoginSuccess(username_)) + EXPECT_CALL(consumer, OnLoginSuccess(username_, cookies_)) .WillOnce(InvokeWithoutArgs(Quit)); MockCryptohomeLibrary library; EXPECT_CALL(library, Mount(username_, hash_ascii_)) .WillOnce(Return(true)); - GoogleAuthenticator auth(&consumer, &library); + GoogleAuthenticator auth(&consumer, + &library, + new ClientLoginResponseHandler(NULL), + new IssueResponseHandler(NULL)); auth.set_password_hash(hash_ascii_); - auth.OnURLFetchComplete(NULL, source, status, 200, cookies, std::string()); + auth.set_username(username_); + auth.OnURLFetchComplete(NULL, source, status, 200, cookies_, std::string()); MessageLoop::current()->Run(); // So tasks can be posted. } diff --git a/chrome/browser/chromeos/login/issue_response_handler.cc b/chrome/browser/chromeos/login/issue_response_handler.cc new file mode 100644 index 0000000..7c0850a --- /dev/null +++ b/chrome/browser/chromeos/login/issue_response_handler.cc @@ -0,0 +1,38 @@ +// 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/browser/chromeos/login/issue_response_handler.h" + +#include <string> + +#include "chrome/browser/net/chrome_url_request_context.h" +#include "chrome/browser/net/url_fetcher.h" +#include "net/base/load_flags.h" + +const int kMaxRedirs = 2; +const int kTimeout = 2; + +// Overridden from AuthResponseHandler. +bool IssueResponseHandler::CanHandle(const GURL& url) { + return (url.spec().find(AuthResponseHandler::kIssueAuthTokenUrl) != + std::string::npos); +} + +// Overridden from AuthResponseHandler. +URLFetcher* IssueResponseHandler::Handle( + const std::string& to_process, + URLFetcher::Delegate* catcher) { + LOG(INFO) << "IssueAuthToken successful!"; + token_url_.assign(StringPrintf("%s%s", + AuthResponseHandler::kTokenAuthUrl, + to_process.c_str())); + URLFetcher* fetcher = + new URLFetcher(GURL(token_url_), URLFetcher::GET, catcher); + fetcher->set_load_flags(net::LOAD_DO_NOT_SEND_COOKIES); + if (getter_) { + fetcher->set_request_context(getter_); + fetcher->Start(); + } + return fetcher; +} diff --git a/chrome/browser/chromeos/login/issue_response_handler.h b/chrome/browser/chromeos/login/issue_response_handler.h new file mode 100644 index 0000000..3153b97 --- /dev/null +++ b/chrome/browser/chromeos/login/issue_response_handler.h @@ -0,0 +1,38 @@ +// 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_ISSUE_RESPONSE_HANDLER_H_ +#define CHROME_BROWSER_CHROMEOS_LOGIN_ISSUE_RESPONSE_HANDLER_H_ + +#include <string> + +#include "base/logging.h" +#include "chrome/browser/chromeos/login/auth_response_handler.h" + +class URLRequestContextGetter; + +class IssueResponseHandler : public AuthResponseHandler { + public: + explicit IssueResponseHandler(URLRequestContextGetter* getter) + : getter_(getter) {} + virtual ~IssueResponseHandler() {} + + // Overridden from AuthResponseHandler. + virtual bool CanHandle(const GURL& url); + + // Overridden from AuthResponseHandler. + // Takes in a response from IssueAuthToken, formats into an appropriate query + // to sent to TokenAuth, and issues said query. |catcher| will receive + // the response to the fetch. + virtual URLFetcher* Handle(const std::string& to_process, + URLFetcher::Delegate* catcher); + + // exposed for testing + std::string token_url() { return token_url_; } + private: + std::string token_url_; + URLRequestContextGetter* getter_; +}; + +#endif // CHROME_BROWSER_CHROMEOS_LOGIN_ISSUE_RESPONSE_HANDLER_H_ diff --git a/chrome/browser/chromeos/login/login_manager_view.cc b/chrome/browser/chromeos/login/login_manager_view.cc index d2bd8a6..179c9ad 100644 --- a/chrome/browser/chromeos/login/login_manager_view.cc +++ b/chrome/browser/chromeos/login/login_manager_view.cc @@ -79,6 +79,7 @@ LoginManagerView::LoginManagerView(ScreenObserver* observer) authenticator_.reset(new StubAuthenticator(this)); else authenticator_.reset(login_utils::CreateAuthenticator(this)); + } LoginManagerView::~LoginManagerView() { @@ -334,12 +335,13 @@ void LoginManagerView::OnLoginFailure(const std::string error) { // TODO(someone): get |error| onto the UI somehow? } -void LoginManagerView::OnLoginSuccess(const std::string username) { +void LoginManagerView::OnLoginSuccess(const std::string username, + std::vector<std::string> cookies) { // TODO(cmasone): something sensible if errors occur. if (observer_) { observer_->OnExit(ScreenObserver::LOGIN_SIGN_IN_SELECTED); } - login_utils::CompleteLogin(username); + login_utils::CompleteLogin(username, cookies); } void LoginManagerView::ShowError(int error_id) { diff --git a/chrome/browser/chromeos/login/login_manager_view.h b/chrome/browser/chromeos/login/login_manager_view.h index be971eb..3c9a64c 100644 --- a/chrome/browser/chromeos/login/login_manager_view.h +++ b/chrome/browser/chromeos/login/login_manager_view.h @@ -6,6 +6,7 @@ #define CHROME_BROWSER_CHROMEOS_LOGIN_LOGIN_MANAGER_VIEW_H_ #include <string> +#include <vector> #include "base/scoped_ptr.h" #include "chrome/browser/chromeos/login/authenticator.h" @@ -70,7 +71,8 @@ class LoginManagerView : public views::View, // Overriden from LoginStatusConsumer. virtual void OnLoginFailure(const std::string error); - virtual void OnLoginSuccess(const std::string username); + virtual void OnLoginSuccess(const std::string username, + std::vector<std::string> cookies); protected: // views::View overrides: diff --git a/chrome/browser/chromeos/login/login_status_consumer.h b/chrome/browser/chromeos/login/login_status_consumer.h index 5e3b07a..bf8f7e3 100644 --- a/chrome/browser/chromeos/login/login_status_consumer.h +++ b/chrome/browser/chromeos/login/login_status_consumer.h @@ -15,7 +15,8 @@ class LoginStatusConsumer { virtual ~LoginStatusConsumer() {} // These copy data in, so as to avoid potential object lifetime problems. virtual void OnLoginFailure(const std::string error) = 0; - virtual void OnLoginSuccess(const std::string username) = 0; + virtual void OnLoginSuccess(const std::string username, + std::vector<std::string> cookies) = 0; }; diff --git a/chrome/browser/chromeos/login/mock_auth_response_handler.h b/chrome/browser/chromeos/login/mock_auth_response_handler.h new file mode 100644 index 0000000..394dc7c --- /dev/null +++ b/chrome/browser/chromeos/login/mock_auth_response_handler.h @@ -0,0 +1,28 @@ +// 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_AUTH_RESPONSE_HANDLER_H_ +#define CHROME_BROWSER_CHROMEOS_LOGIN_MOCK_AUTH_RESPONSE_HANDLER_H_ + +#include "chrome/browser/chromeos/login/auth_response_handler.h" + +#include <string> + +#include "base/logging.h" +#include "testing/gmock/include/gmock/gmock.h" + +class GURL; +class URLFetcher; + +class MockAuthResponseHandler : public AuthResponseHandler { + public: + MockAuthResponseHandler() {} + virtual ~MockAuthResponseHandler() {} + + MOCK_METHOD1(CanHandle, bool(const GURL& url)); + MOCK_METHOD2(Handle, URLFetcher*(const std::string& to_process, + URLFetcher::Delegate* catcher)); +}; + +#endif // CHROME_BROWSER_CHROMEOS_LOGIN_MOCK_AUTH_RESPONSE_HANDLER_H_ diff --git a/chrome/browser/chromeos/login/pam_google_authenticator.cc b/chrome/browser/chromeos/login/pam_google_authenticator.cc index 2bd6ae0..689fde3 100644 --- a/chrome/browser/chromeos/login/pam_google_authenticator.cc +++ b/chrome/browser/chromeos/login/pam_google_authenticator.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include <string> +#include <vector> #include "base/logging.h" #include "base/path_service.h" @@ -27,7 +28,7 @@ bool PamGoogleAuthenticator::Authenticate(const std::string& username, child_exit_code == 0); if (ret) - consumer_->OnLoginSuccess(username); + consumer_->OnLoginSuccess(username, std::vector<std::string>()); else consumer_->OnLoginFailure(""); return ret; diff --git a/chrome/browser/chromeos/login/utils.cc b/chrome/browser/chromeos/login/utils.cc index 0fd5126..d94252e 100644 --- a/chrome/browser/chromeos/login/utils.cc +++ b/chrome/browser/chromeos/login/utils.cc @@ -15,18 +15,23 @@ #include "chrome/browser/chromeos/login/google_authenticator.h" #include "chrome/browser/chromeos/login/pam_google_authenticator.h" #include "chrome/browser/chromeos/login/user_manager.h" +#include "chrome/browser/net/url_request_context_getter.h" #include "chrome/browser/profile.h" #include "chrome/browser/profile_manager.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/notification_service.h" +#include "googleurl/src/gurl.h" +#include "net/base/cookie_store.h" +#include "net/url_request/url_request_context.h" #include "views/widget/widget_gtk.h" namespace chromeos { namespace login_utils { -void CompleteLogin(const std::string& username) { +void CompleteLogin(const std::string& username, + std::vector<std::string> cookies) { LOG(INFO) << "LoginManagerView: OnLoginSuccess()"; if (CrosLibrary::EnsureLoaded()) @@ -52,8 +57,15 @@ void CompleteLogin(const std::string& username) { Profile* profile = profile_manager->GetDefaultProfile(user_data_dir); int return_code; - if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kInChromeAuth)) + if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kInChromeAuth)) { ExternalCookieHandler::GetCookies(command_line, profile); + } else { + GURL url(ExternalCookieHandler::kGoogleAccountsUrl); + net::CookieOptions options; + options.set_include_httponly(); + profile->GetRequestContext()->GetCookieStore()->SetCookiesWithOptions( + url, cookies, options); + } browser_init.LaunchBrowser(command_line, profile, std::wstring(), true, &return_code); } diff --git a/chrome/browser/chromeos/login/utils.h b/chrome/browser/chromeos/login/utils.h index b7e28a9..c062982 100644 --- a/chrome/browser/chromeos/login/utils.h +++ b/chrome/browser/chromeos/login/utils.h @@ -6,6 +6,7 @@ #define CHROME_BROWSER_CHROMEOS_LOGIN_UTILS_H_ #include <string> +#include <vector> class Authenticator; class LoginStatusConsumer; @@ -20,7 +21,8 @@ namespace login_utils { // Invoked after the user has successfully logged in. This launches a browser // and does other bookkeeping after logging in. -void CompleteLogin(const std::string& username); +void CompleteLogin(const std::string& username, + std::vector<std::string> cookies); // Creates and returns the authenticator to use. The caller owns the returned // Authenticator and must delete it when done. diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index a6f0e91..492c4311 100755 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -361,8 +361,14 @@ 'browser/chromeos/login/authenticator.h', 'browser/chromeos/login/background_view.cc', 'browser/chromeos/login/background_view.h', + 'browser/chromeos/login/auth_response_handler.cc', + 'browser/chromeos/login/auth_response_handler.h', + 'browser/chromeos/login/client_login_response_handler.cc', + 'browser/chromeos/login/client_login_response_handler.h', 'browser/chromeos/login/existing_user_controller.cc', 'browser/chromeos/login/existing_user_controller.h', + 'browser/chromeos/login/issue_response_handler.cc', + 'browser/chromeos/login/issue_response_handler.h', 'browser/chromeos/login/google_authenticator.cc', 'browser/chromeos/login/google_authenticator.h', 'browser/chromeos/login/pam_google_authenticator.cc', |