diff options
author | cmasone@google.com <cmasone@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-04-01 20:47:09 +0000 |
---|---|---|
committer | cmasone@google.com <cmasone@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-04-01 20:47:09 +0000 |
commit | 82e1e1135c3565a0064761d8a4d919d414446d99 (patch) | |
tree | e8a706db2fb3a41d304404402259687ace4a7f22 /chrome/browser/chromeos | |
parent | 60a0438e453b5006cff34972df6188e2fb4e02ce (diff) | |
download | chromium_src-82e1e1135c3565a0064761d8a4d919d414446d99.zip chromium_src-82e1e1135c3565a0064761d8a4d919d414446d99.tar.gz chromium_src-82e1e1135c3565a0064761d8a4d919d414446d99.tar.bz2 |
Move fetching of full-fledged auth cookies to a time when we have the user's real profile available. Also, enable the use of a localaccount on Chrome OS
many of the changes here are just callsite fixes, because I changed the signature of a function. I also moved my code into the chromeos namespace, which accounts for several other files. The important stuff is in:
1) google_authenticator*
2) cookie_fetcher*
3) login_utils.cc
Review URL: http://codereview.chromium.org/1515003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@43382 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/chromeos')
25 files changed, 1067 insertions, 330 deletions
diff --git a/chrome/browser/chromeos/login/auth_response_handler.cc b/chrome/browser/chromeos/login/auth_response_handler.cc index fea29af..d1d3708 100644 --- a/chrome/browser/chromeos/login/auth_response_handler.cc +++ b/chrome/browser/chromeos/login/auth_response_handler.cc @@ -4,6 +4,9 @@ #include "chrome/browser/chromeos/login/auth_response_handler.h" +namespace chromeos { + +const int kHttpSuccess = 200; const char AuthResponseHandler::kClientLoginUrl[] = "https://www.google.com/accounts/ClientLogin"; const char AuthResponseHandler::kIssueAuthTokenUrl[] = @@ -13,3 +16,5 @@ const char AuthResponseHandler::kIssueAuthTokenUrl[] = const char AuthResponseHandler::kTokenAuthUrl[] = "https://www.google.com/accounts/TokenAuth?" "continue=http://www.google.com/webhp&source=chromeos&auth="; + +} // namespace chromeos diff --git a/chrome/browser/chromeos/login/auth_response_handler.h b/chrome/browser/chromeos/login/auth_response_handler.h index 0c672c7..04e9d1d 100644 --- a/chrome/browser/chromeos/login/auth_response_handler.h +++ b/chrome/browser/chromeos/login/auth_response_handler.h @@ -11,6 +11,11 @@ class GURL; +namespace chromeos { + +// The success code specified by the HTTP spec. +extern const int kHttpSuccess; + class AuthResponseHandler { public: AuthResponseHandler() {} @@ -33,4 +38,6 @@ class AuthResponseHandler { static const char kTokenAuthUrl[]; }; +} // namespace chromeos + #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 d335127..cf6531e 100644 --- a/chrome/browser/chromeos/login/authenticator.h +++ b/chrome/browser/chromeos/login/authenticator.h @@ -8,14 +8,19 @@ #include <vector> #include "base/logging.h" +#include "base/ref_counted.h" +#include "chrome/browser/chrome_thread.h" #include "chrome/browser/chromeos/login/login_status_consumer.h" +class Profile; + +namespace chromeos { + // An interface for objects that will authenticate a Chromium OS user. // When authentication successfully completes, will call -// consumer_->OnLoginSuccess(|username|). -// On failure, will call consumer_->OnLoginFailure(). - -class Authenticator { +// consumer_->OnLoginSuccess(|username|) on the UI thread. +// On failure, will call consumer_->OnLoginFailure() on the UI thread. +class Authenticator : public base::RefCountedThreadSafe<Authenticator> { public: explicit Authenticator(LoginStatusConsumer* consumer) : consumer_(consumer) { @@ -24,9 +29,16 @@ class Authenticator { // Given a |username| and |password|, this method attempts to authenticate // Returns true if we kick off the attempt successfully and false if we can't. - virtual bool Authenticate(const std::string& username, + // Must be called on the FILE thread. + virtual bool Authenticate(Profile* profile, + const std::string& username, const std::string& password) = 0; + // 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 OnLoginFailure(const std::string& data) = 0; + protected: LoginStatusConsumer* consumer_; @@ -42,14 +54,29 @@ class StubAuthenticator : public Authenticator { virtual ~StubAuthenticator() {} // Returns true after calling OnLoginSuccess(). - virtual bool Authenticate(const std::string& username, + virtual bool Authenticate(Profile* profile, + const std::string& username, const std::string& password) { - consumer_->OnLoginSuccess(username, std::vector<std::string>()); + username_ = username; + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableMethod(this, + &StubAuthenticator::OnLoginSuccess, + std::string())); return true; } + void OnLoginSuccess(const std::string& credentials) { + consumer_->OnLoginSuccess(username_, credentials); + } + + void OnLoginFailure(const std::string& data) {} + private: + std::string username_; DISALLOW_COPY_AND_ASSIGN(StubAuthenticator); }; +} // namespace chromeos + #endif // CHROME_BROWSER_CHROMEOS_LOGIN_AUTHENTICATOR_H_ diff --git a/chrome/browser/chromeos/login/client_login_response_handler.cc b/chrome/browser/chromeos/login/client_login_response_handler.cc index 97a2edc..611662f 100644 --- a/chrome/browser/chromeos/login/client_login_response_handler.cc +++ b/chrome/browser/chromeos/login/client_login_response_handler.cc @@ -12,6 +12,8 @@ #include "chrome/browser/net/url_fetcher.h" #include "net/base/load_flags.h" +namespace chromeos { + // By setting "service=gaia", we get an uber-auth-token back. const char ClientLoginResponseHandler::kService[] = "service=gaia"; @@ -37,9 +39,11 @@ URLFetcher* ClientLoginResponseHandler::Handle( 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(); + LOG(INFO) << "Fetching"; fetcher->set_request_context(getter_); fetcher->Start(); } return fetcher; } + +} // namespace chromeos diff --git a/chrome/browser/chromeos/login/client_login_response_handler.h b/chrome/browser/chromeos/login/client_login_response_handler.h index 2650261..c0f1227 100644 --- a/chrome/browser/chromeos/login/client_login_response_handler.h +++ b/chrome/browser/chromeos/login/client_login_response_handler.h @@ -12,6 +12,12 @@ class URLRequestContextGetter; +namespace chromeos { + +// Handles responses to a fetch executed upon the Google Accounts ClientLogin +// endpoint. The cookies that are sent back in the response body are +// reformatted into a request for an time-limited authorization token, which +// is then sent to the IssueAuthToken endpoint. class ClientLoginResponseHandler : public AuthResponseHandler { public: explicit ClientLoginResponseHandler(URLRequestContextGetter* getter) @@ -37,6 +43,10 @@ class ClientLoginResponseHandler : public AuthResponseHandler { private: std::string payload_; URLRequestContextGetter* getter_; + + DISALLOW_COPY_AND_ASSIGN(ClientLoginResponseHandler); }; +} // namespace chromeos + #endif // CHROME_BROWSER_CHROMEOS_LOGIN_CLIENT_LOGIN_RESPONSE_HANDLER_H_ diff --git a/chrome/browser/chromeos/login/cookie_fetcher.cc b/chrome/browser/chromeos/login/cookie_fetcher.cc new file mode 100644 index 0000000..d27dfed --- /dev/null +++ b/chrome/browser/chromeos/login/cookie_fetcher.cc @@ -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. + +#include "chrome/browser/chromeos/login/cookie_fetcher.h" + +#include "base/command_line.h" +#include "base/file_path.h" +#include "base/path_service.h" +#include "chrome/browser/browser_process.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/login_utils.h" +#include "chrome/browser/net/chrome_url_request_context.h" +#include "chrome/browser/net/url_fetcher.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/profile_manager.h" +#include "chrome/common/chrome_paths.h" +#include "net/url_request/url_request_status.h" + +namespace chromeos { + +CookieFetcher::CookieFetcher(Profile* profile) : profile_(profile) { + CHECK(profile_); + client_login_handler_.reset( + new ClientLoginResponseHandler(profile_->GetRequestContext())); + issue_handler_.reset( + new IssueResponseHandler(profile_->GetRequestContext())); + launcher_.reset(new DelegateImpl); +} + +void CookieFetcher::AttemptFetch(const std::string& credentials) { + LOG(INFO) << "getting auth token..."; + fetcher_.reset(client_login_handler_->Handle(credentials, this)); +} + +void CookieFetcher::OnURLFetchComplete(const URLFetcher* source, + const GURL& url, + const URLRequestStatus& status, + int response_code, + const ResponseCookies& cookies, + const std::string& data) { + if (status.is_success() && response_code == kHttpSuccess) { + if (issue_handler_->CanHandle(url)) { + LOG(INFO) << "Handling auth token"; + fetcher_.reset(issue_handler_->Handle(data, this)); + return; + } + } + LOG(INFO) << "Calling DoLaunch"; + launcher_->DoLaunch(profile_); + delete this; +} + +void CookieFetcher::DelegateImpl::DoLaunch(Profile* profile) { + FilePath user_data_dir; + PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); + ProfileManager* profile_manager = g_browser_process->profile_manager(); + if (profile == profile_manager->GetDefaultProfile(user_data_dir)) { + LoginUtils::DoBrowserLaunch(profile); + } else { + LOG(ERROR) << + "Profile has changed since we started populating it with cookies"; + } +} + +} // namespace chromeos diff --git a/chrome/browser/chromeos/login/cookie_fetcher.h b/chrome/browser/chromeos/login/cookie_fetcher.h new file mode 100644 index 0000000..434da44 --- /dev/null +++ b/chrome/browser/chromeos/login/cookie_fetcher.h @@ -0,0 +1,89 @@ +// 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_COOKIE_FETCHER_H_ +#define CHROME_BROWSER_CHROMEOS_LOGIN_COOKIE_FETCHER_H_ + +#include <string> +#include "base/scoped_ptr.h" +#include "chrome/browser/chromeos/login/auth_response_handler.h" +#include "chrome/browser/chromeos/login/client_login_response_handler.h" +#include "chrome/browser/chromeos/login/issue_response_handler.h" +#include "chrome/browser/net/url_fetcher.h" +#include "chrome/browser/profile.h" + +namespace chromeos { + +// Given a SID/LSID pair, this class will attempt to turn them into a +// full-fledged set of Google AuthN cookies. +// +// A CookieFetcher manages its own lifecycle. It deletes itself once it's +// done attempting to fetch URLs. +class CookieFetcher : public URLFetcher::Delegate { + public: + // This class is a very thin wrapper around posting a task to the UI thread + // to call LoginUtils::DoBrowserLaunch(). It's here to allow mocking. + // + // In normal usage, instances of this class are owned by a CookieFetcher. + class Delegate { + public: + Delegate() {} + virtual ~Delegate() {} + virtual void DoLaunch(Profile* profile) = 0; + }; + + // |profile| is the Profile whose cookie jar you want the cookies in. + explicit CookieFetcher(Profile* profile); + + // |profile| is the Profile whose cookie jar you want the cookies in. + // Takes ownership of |cl_handler|, |i_handler|, and |launcher|. + CookieFetcher(Profile* profile, + AuthResponseHandler* cl_handler, + AuthResponseHandler* i_handler, + Delegate* launcher) + : profile_(profile), + client_login_handler_(cl_handler), + issue_handler_(i_handler), + launcher_(launcher) { + } + + // Given a newline-delineated SID/LSID pair of Google cookies (like + // those that come back from ClientLogin), try to use them to fetch + // a full-fledged set of Google AuthN cookies. These cookies will wind up + // stored in the cookie jar associated with |profile_|, if we get them. + // Either way, we end up by calling launcher_->DoLaunch() + void AttemptFetch(const std::string& credentials); + + // Overloaded from URLFetcher::Delegate. + virtual void OnURLFetchComplete(const URLFetcher* source, + const GURL& url, + const URLRequestStatus& status, + int response_code, + const ResponseCookies& cookies, + const std::string& data); + + private: + class DelegateImpl : public Delegate { + public: + DelegateImpl() {} + ~DelegateImpl() {} + void DoLaunch(Profile* profile); + private: + DISALLOW_COPY_AND_ASSIGN(DelegateImpl); + }; + + virtual ~CookieFetcher() {} + + scoped_ptr<URLFetcher> fetcher_; + Profile* profile_; + scoped_ptr<AuthResponseHandler> client_login_handler_; + scoped_ptr<AuthResponseHandler> issue_handler_; + scoped_ptr<Delegate> launcher_; + + DISALLOW_COPY_AND_ASSIGN(CookieFetcher); +}; + +} // namespace chromeos + +#endif // CHROME_BROWSER_CHROMEOS_LOGIN_COOKIE_FETCHER_H_ diff --git a/chrome/browser/chromeos/login/cookie_fetcher_unittest.cc b/chrome/browser/chromeos/login/cookie_fetcher_unittest.cc new file mode 100644 index 0000000..ecb7d36 --- /dev/null +++ b/chrome/browser/chromeos/login/cookie_fetcher_unittest.cc @@ -0,0 +1,211 @@ +// 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 <errno.h> +#include <string> +#include "chrome/browser/browser_process.h" +#include "chrome/browser/chromeos/login/cookie_fetcher.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/mock_auth_response_handler.h" +#include "chrome/test/testing_profile.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" +#include "testing/gmock/include/gmock/gmock.h" + +namespace chromeos { +using ::testing::Return; +using ::testing::Invoke; +using ::testing::Unused; +using ::testing::_; + +class MockDelegate : public CookieFetcher::Delegate { + public: + MockDelegate() {} + virtual ~MockDelegate() {} + MOCK_METHOD1(DoLaunch, void(Profile* profile)); +}; + +class CookieFetcherTest : public ::testing::Test { + public: + CookieFetcherTest() + : iat_url_(AuthResponseHandler::kIssueAuthTokenUrl), + ta_url_(AuthResponseHandler::kTokenAuthUrl), + client_login_data_("SID n' LSID"), + token_("auth token") { + } + + const GURL iat_url_; + const GURL ta_url_; + const std::string client_login_data_; + const std::string token_; + TestingProfile profile_; +}; + +// Check that successful HTTP responses from both end points results in +// the browser window getting put up. +TEST_F(CookieFetcherTest, SuccessfulFetchTest) { + URLRequestStatus status(URLRequestStatus::SUCCESS, 0); + + MockAuthResponseHandler* cl_handler = + new MockAuthResponseHandler(iat_url_, status, kHttpSuccess, token_); + MockAuthResponseHandler* i_handler = + new MockAuthResponseHandler(ta_url_, status, kHttpSuccess, std::string()); + MockDelegate* delegate = new MockDelegate; + + CookieFetcher* cf = new CookieFetcher(NULL, cl_handler, i_handler, delegate); + + EXPECT_CALL(*cl_handler, Handle(client_login_data_, cf)) + .Times(1); + + EXPECT_CALL(*i_handler, CanHandle(iat_url_)) + .WillOnce(Return(true)); + EXPECT_CALL(*i_handler, CanHandle(ta_url_)) + .WillOnce(Return(false)); + EXPECT_CALL(*i_handler, Handle(token_, cf)) + .Times(1); + + EXPECT_CALL(*delegate, DoLaunch(_)) + .Times(1); + + cf->AttemptFetch(client_login_data_); +} + +// Check that a network failure when trying IssueAuthToken results in us bailing +// and putting up the browser window. +TEST_F(CookieFetcherTest, IssueAuthTokenNetworkFailureTest) { + URLRequestStatus failed(URLRequestStatus::FAILED, ECONNRESET); + + MockAuthResponseHandler* cl_handler = + new MockAuthResponseHandler(iat_url_, failed, kHttpSuccess, token_); + MockDelegate* delegate = new MockDelegate; + // I expect nothing in i_handler to get called anyway + MockAuthResponseHandler* i_handler = + new MockAuthResponseHandler(ta_url_, failed, kHttpSuccess, std::string()); + + CookieFetcher* cf = new CookieFetcher(&profile_, + cl_handler, + i_handler, + delegate); + + EXPECT_CALL(*cl_handler, Handle(client_login_data_, cf)) + .Times(1); + EXPECT_CALL(*delegate, DoLaunch(_)) + .Times(1); + + cf->AttemptFetch(client_login_data_); +} + +// Check that a network failure when trying TokenAuth results in us bailing +// and putting up the browser window. +TEST_F(CookieFetcherTest, TokenAuthNetworkFailureTest) { + URLRequestStatus success; + URLRequestStatus failed(URLRequestStatus::FAILED, ECONNRESET); + + MockAuthResponseHandler* cl_handler = + new MockAuthResponseHandler(iat_url_, success, kHttpSuccess, token_); + MockAuthResponseHandler* i_handler = + new MockAuthResponseHandler(ta_url_, failed, 0, std::string()); + MockDelegate* delegate = new MockDelegate; + + CookieFetcher* cf = new CookieFetcher(&profile_, + cl_handler, + i_handler, + delegate); + + EXPECT_CALL(*cl_handler, Handle(client_login_data_, cf)) + .Times(1); + + EXPECT_CALL(*i_handler, CanHandle(iat_url_)) + .WillOnce(Return(true)); + EXPECT_CALL(*i_handler, Handle(token_, cf)) + .Times(1); + + EXPECT_CALL(*delegate, DoLaunch(_)) + .Times(1); + + cf->AttemptFetch(client_login_data_); +} + +// Check that an unsuccessful HTTP response when trying IssueAuthToken results +// in us bailing and putting up the browser window. +TEST_F(CookieFetcherTest, IssueAuthTokenDeniedTest) { + URLRequestStatus success; + + MockAuthResponseHandler* cl_handler = + new MockAuthResponseHandler(iat_url_, success, 403, std::string()); + MockDelegate* delegate = new MockDelegate; + // I expect nothing in i_handler to get called anyway. + MockAuthResponseHandler* i_handler = + new MockAuthResponseHandler(ta_url_, success, 0, std::string()); + + CookieFetcher* cf = new CookieFetcher(&profile_, + cl_handler, + i_handler, + delegate); + + EXPECT_CALL(*cl_handler, Handle(client_login_data_, cf)) + .Times(1); + EXPECT_CALL(*delegate, DoLaunch(_)) + .Times(1); + + cf->AttemptFetch(client_login_data_); +} + +// Check that an unsuccessful HTTP response when trying TokenAuth results +// in us bailing and putting up the browser window. +TEST_F(CookieFetcherTest, TokenAuthDeniedTest) { + URLRequestStatus success; + + MockAuthResponseHandler* cl_handler = + new MockAuthResponseHandler(iat_url_, + success, + kHttpSuccess, + token_); + MockAuthResponseHandler* i_handler = + new MockAuthResponseHandler(ta_url_, success, 403, std::string()); + MockDelegate* delegate = new MockDelegate; + + CookieFetcher* cf = new CookieFetcher(&profile_, + cl_handler, + i_handler, + delegate); + + EXPECT_CALL(*cl_handler, Handle(client_login_data_, cf)) + .Times(1); + + EXPECT_CALL(*i_handler, CanHandle(iat_url_)) + .WillOnce(Return(true)); + EXPECT_CALL(*i_handler, Handle(token_, cf)) + .Times(1); + + EXPECT_CALL(*delegate, DoLaunch(_)) + .Times(1); + + cf->AttemptFetch(client_login_data_); +} + +TEST_F(CookieFetcherTest, 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(CookieFetcherTest, 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()); +} + +} // namespace chromeos diff --git a/chrome/browser/chromeos/login/existing_user_controller.cc b/chrome/browser/chromeos/login/existing_user_controller.cc index 66a7978..ee29b0f 100644 --- a/chrome/browser/chromeos/login/existing_user_controller.cc +++ b/chrome/browser/chromeos/login/existing_user_controller.cc @@ -10,6 +10,8 @@ #include "base/message_loop.h" #include "base/stl_util-inl.h" #include "base/utf_string_conversions.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/chrome_thread.h" #include "chrome/browser/chromeos/cros/cros_library.h" #include "chrome/browser/chromeos/cros/login_library.h" #include "chrome/browser/chromeos/login/authenticator.h" @@ -17,6 +19,8 @@ #include "chrome/browser/chromeos/login/login_utils.h" #include "chrome/browser/chromeos/login/wizard_controller.h" #include "chrome/browser/chromeos/wm_ipc.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/profile_manager.h" #include "views/screen.h" #include "views/widget/widget.h" @@ -118,10 +122,15 @@ void ExistingUserController::Login(UserController* source, DCHECK(i != controllers_.end()); index_of_view_logging_in_ = i - controllers_.begin(); - authenticator_.reset(LoginUtils::Get()->CreateAuthenticator(this)); - authenticator_->Authenticate( - controllers_[index_of_view_logging_in_]->user().email(), - UTF16ToUTF8(password)); + authenticator_ = LoginUtils::Get()->CreateAuthenticator(this); + Profile* profile = g_browser_process->profile_manager()->GetWizardProfile(); + ChromeThread::PostTask( + ChromeThread::FILE, FROM_HERE, + NewRunnableMethod(authenticator_.get(), + &Authenticator::Authenticate, + profile, + controllers_[index_of_view_logging_in_]->user().email(), + UTF16ToUTF8(password))); // Disable clicking on other windows. chromeos::WmIpc::Message message( @@ -130,7 +139,7 @@ void ExistingUserController::Login(UserController* source, chromeos::WmIpc::instance()->SendMessage(message); } -void ExistingUserController::OnLoginFailure(const std::string error) { +void ExistingUserController::OnLoginFailure(const std::string& error) { LOG(INFO) << "OnLoginFailure"; // TODO(sky): alert the user in some way to the failure. @@ -144,14 +153,14 @@ void ExistingUserController::OnLoginFailure(const std::string error) { chromeos::WmIpc::instance()->SendMessage(message); } -void ExistingUserController::OnLoginSuccess(const std::string username, - std::vector<std::string> cookies) { +void ExistingUserController::OnLoginSuccess(const std::string& username, + const std::string& credentials) { // Hide the login windows now. STLDeleteElements(&controllers_); background_window_->Close(); - chromeos::LoginUtils::Get()->CompleteLogin(username, cookies); + chromeos::LoginUtils::Get()->CompleteLogin(username, credentials); // 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 9e587fd..1f4c903 100644 --- a/chrome/browser/chromeos/login/existing_user_controller.h +++ b/chrome/browser/chromeos/login/existing_user_controller.h @@ -8,6 +8,7 @@ #include <string> #include <vector> +#include "base/ref_counted.h" #include "base/task.h" #include "base/timer.h" #include "chrome/browser/chromeos/login/login_status_consumer.h" @@ -16,14 +17,13 @@ #include "chrome/browser/chromeos/wm_message_listener.h" #include "gfx/size.h" -class Authenticator; - namespace views { class Wiget; } namespace chromeos { +class Authenticator; class BackgroundView; // ExistingUserController is used to handle login when someone has already @@ -64,9 +64,9 @@ class ExistingUserController : public WmMessageListener::Observer, virtual void Login(UserController* source, const string16& password); // LoginStatusConsumer: - virtual void OnLoginFailure(const std::string error); - virtual void OnLoginSuccess(const std::string username, - std::vector<std::string> cookies); + virtual void OnLoginFailure(const std::string& error); + virtual void OnLoginSuccess(const std::string& username, + const std::string& credentials); // Bounds of the background window. const gfx::Rect background_bounds_; @@ -79,7 +79,7 @@ class ExistingUserController : public WmMessageListener::Observer, std::vector<UserController*> controllers_; // Used for logging in. - scoped_ptr<Authenticator> authenticator_; + scoped_refptr<Authenticator> authenticator_; // Index of view loggin in. size_t index_of_view_logging_in_; diff --git a/chrome/browser/chromeos/login/google_authenticator.cc b/chrome/browser/chromeos/login/google_authenticator.cc index 409d382..15d95c4f 100644 --- a/chrome/browser/chromeos/login/google_authenticator.cc +++ b/chrome/browser/chromeos/login/google_authenticator.cc @@ -2,11 +2,14 @@ // 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/google_authenticator.h" + #include <errno.h> #include <string> #include <vector> -#include <time.h> +#include "base/condition_variable.h" +#include "base/lock.h" #include "base/logging.h" #include "base/file_path.h" #include "base/file_util.h" @@ -15,13 +18,10 @@ #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/auth_response_handler.h" #include "chrome/browser/chromeos/login/login_status_consumer.h" #include "chrome/browser/net/chrome_url_request_context.h" #include "chrome/browser/net/url_fetcher.h" @@ -34,37 +34,56 @@ using base::Time; using base::TimeDelta; -using namespace chromeos; using namespace file_util; +namespace chromeos { + +// static +const char GoogleAuthenticator::kCookiePersistence[] = "true"; +// static +const char GoogleAuthenticator::kAccountType[] = "HOSTED_OR_GOOGLE"; +// static +const char GoogleAuthenticator::kSource[] = "chromeos"; +// static const char GoogleAuthenticator::kFormat[] = "Email=%s&" "Passwd=%s&" "PersistentCookie=%s&" "accountType=%s&" "source=%s&"; -const char GoogleAuthenticator::kCookiePersistence[] = "true"; -const char GoogleAuthenticator::kAccountType[] = "HOSTED_OR_GOOGLE"; -const char GoogleAuthenticator::kSource[] = "chromeos"; -const int GoogleAuthenticator::kHttpSuccess = 200; -const int kPassHashLen = 32; -// Chromium OS system salt stored here +// static const char GoogleAuthenticator::kSystemSalt[] = "/home/.shadow/salt"; - -// String that appears at the start of OpenSSL cipher text with embedded salt +// static const char GoogleAuthenticator::kOpenSSLMagic[] = "Salted__"; +// static +const char GoogleAuthenticator::kLocalaccountFile[] = "localaccount"; +// static +const char GoogleAuthenticator::kTmpfsTrigger[] = "incognito"; + +const int kPassHashLen = 32; + +GoogleAuthenticator::GoogleAuthenticator(LoginStatusConsumer* consumer) + : Authenticator(consumer), + fetcher_(NULL), + getter_(NULL), + checked_for_localaccount_(false) { + CHECK(chromeos::CrosLibrary::Get()->EnsureLoaded()); +} + +GoogleAuthenticator::~GoogleAuthenticator() { + ChromeThread::DeleteSoon(ChromeThread::FILE, FROM_HERE, fetcher_); +} -bool GoogleAuthenticator::Authenticate(const std::string& username, +bool GoogleAuthenticator::Authenticate(Profile* profile, + const std::string& username, const std::string& password) { - FilePath user_data_dir; - 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); + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); getter_ = profile->GetRequestContext(); - fetcher_.reset(new URLFetcher(GURL(AuthResponseHandler::kClientLoginUrl), + fetcher_ = URLFetcher::Create(0, + GURL(AuthResponseHandler::kClientLoginUrl), URLFetcher::POST, - this)); + this); 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 @@ -76,13 +95,9 @@ bool GoogleAuthenticator::Authenticate(const std::string& username, kAccountType, 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); + fetcher_->Start(); return true; } @@ -92,93 +107,70 @@ void GoogleAuthenticator::OnURLFetchComplete(const URLFetcher* source, int response_code, const ResponseCookies& cookies, const std::string& data) { - if (status.is_success() && response_code == 200) { - 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_, - username_, - ascii_hash_, - cookies)); - } + 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()) { LOG(INFO) << "Network fail"; // The fetch failed for network reasons, try offline login. + LoadLocalaccount(kLocalaccountFile); ChromeThread::PostTask( ChromeThread::UI, FROM_HERE, - NewRunnableFunction(GoogleAuthenticator::CheckOffline, - consumer_, - username_, - ascii_hash_, - status)); + NewRunnableMethod(this, &GoogleAuthenticator::CheckOffline, status)); } else { - std::string error; - if (status.is_success()) { - // The fetch succeeded, but ClientLogin said no. - error.assign(data); - } else { - // We couldn't hit the network, and offline login failed. - error.assign(strerror(status.os_error())); - } + // The fetch succeeded, but ClientLogin said no. + LoadLocalaccount(kLocalaccountFile); ChromeThread::PostTask( ChromeThread::UI, FROM_HERE, - NewRunnableFunction( - GoogleAuthenticator::OnLoginFailure, consumer_, error)); + NewRunnableMethod(this, &GoogleAuthenticator::CheckLocalaccount, data)); } } -// static -void GoogleAuthenticator::OnLoginSuccess(LoginStatusConsumer* consumer, - const std::string& username, - const std::string& passhash, - const ResponseCookies& cookies) { - if (CrosLibrary::Get()->GetCryptohomeLibrary()->Mount(username.c_str(), - passhash.c_str())) - consumer->OnLoginSuccess(username, cookies); - else - GoogleAuthenticator::OnLoginFailure(consumer, "Could not mount cryptohome"); +void GoogleAuthenticator::OnLoginSuccess(const std::string& data) { + if (CrosLibrary::Get()->GetCryptohomeLibrary()->Mount(username_.c_str(), + ascii_hash_.c_str())) { + consumer_->OnLoginSuccess(username_, data); + } else { + OnLoginFailure("Could not mount cryptohome"); + } } -// static -void GoogleAuthenticator::CheckOffline(LoginStatusConsumer* consumer, - const std::string& username, - const std::string& passhash, - const URLRequestStatus& status) { - if (CrosLibrary::Get()->GetCryptohomeLibrary()->CheckKey(username.c_str(), - passhash.c_str())) { +void GoogleAuthenticator::CheckOffline(const URLRequestStatus& status) { + if (CrosLibrary::Get()->GetCryptohomeLibrary()->CheckKey( + username_.c_str(), + ascii_hash_.c_str())) { // The fetch didn't succeed, but offline login did. LOG(INFO) << "Offline login successful!"; - ResponseCookies cookies; - GoogleAuthenticator::OnLoginSuccess(consumer, - username, - passhash, - cookies); + OnLoginSuccess(std::string()); } else { // We couldn't hit the network, and offline login failed. - GoogleAuthenticator::OnLoginFailure(consumer, strerror(status.os_error())); + GoogleAuthenticator::CheckLocalaccount(strerror(status.os_error())); } } -// static -void GoogleAuthenticator::OnLoginFailure(LoginStatusConsumer* consumer, - const std::string& data) { +void GoogleAuthenticator::CheckLocalaccount(const std::string& error) { + if (!localaccount_.empty() && localaccount_ == username_ && + CrosLibrary::Get()->GetCryptohomeLibrary()->Mount(kTmpfsTrigger, "")) { + LOG(WARNING) << "Logging in with localaccount: " << localaccount_; + consumer_->OnLoginSuccess(username_, std::string()); + } else { + OnLoginFailure(error); + } +} + +void GoogleAuthenticator::OnLoginFailure(const std::string& data) { + LOG(WARNING) << "Login failed: " << data; // TODO(cmasone): what can we do to expose these OS/server-side error strings // in an internationalizable way? - consumer->OnLoginFailure(data); + consumer_->OnLoginFailure(data); } -void GoogleAuthenticator::LoadSystemSalt(FilePath& path) { +void GoogleAuthenticator::LoadSystemSalt(const FilePath& path) { if (!system_salt_.empty()) return; - CHECK(PathExists(path)) << path.value() << " does not exist!"; - int64 file_size; CHECK(GetFileSize(path, &file_size)) << "Could not get size of " << path.value(); @@ -190,19 +182,22 @@ void GoogleAuthenticator::LoadSystemSalt(FilePath& path) { system_salt_.assign(salt, salt + data_read); } -std::string GoogleAuthenticator::SaltAsAscii() { - FilePath salt_path(kSystemSalt); - LoadSystemSalt(salt_path); - unsigned int salt_len = system_salt_.size(); - char ascii_salt[2 * salt_len + 1]; - if (GoogleAuthenticator::BinaryToHex(system_salt_, - salt_len, - ascii_salt, - sizeof(ascii_salt))) { - return std::string(ascii_salt, sizeof(ascii_salt) - 1); +void GoogleAuthenticator::LoadLocalaccount(const std::string& filename) { + if (checked_for_localaccount_) + return; + FilePath localaccount_file; + std::string localaccount; + if (PathService::Get(base::DIR_EXE, &localaccount_file)) { + localaccount_file = localaccount_file.Append(filename); + LOG(INFO) << "looking for localaccount in " << localaccount_file.value(); + + ReadFileToString(localaccount_file, &localaccount); + TrimWhitespaceASCII(localaccount, TRIM_TRAILING, &localaccount); + LOG(INFO) << "Loading localaccount: " << localaccount; } else { - return std::string(); + LOG(INFO) << "Assuming no localaccount"; } + set_localaccount(localaccount); } void GoogleAuthenticator::StoreHashedPassword(const std::string& password) { @@ -235,6 +230,20 @@ void GoogleAuthenticator::StoreHashedPassword(const std::string& password) { ascii_hash_.assign(ascii_buf, sizeof(ascii_buf) - 1); } +std::string GoogleAuthenticator::SaltAsAscii() { + LoadSystemSalt(FilePath(kSystemSalt)); // no-op if it's already loaded. + unsigned int salt_len = system_salt_.size(); + char ascii_salt[2 * salt_len + 1]; + if (GoogleAuthenticator::BinaryToHex(system_salt_, + salt_len, + ascii_salt, + sizeof(ascii_salt))) { + return std::string(ascii_salt, sizeof(ascii_salt) - 1); + } else { + return std::string(); + } +} + // static bool GoogleAuthenticator::BinaryToHex(const std::vector<unsigned char>& binary, const unsigned int binary_len, @@ -247,3 +256,5 @@ bool GoogleAuthenticator::BinaryToHex(const std::vector<unsigned char>& binary, snprintf(hex_string + j, len - j, "%02x", binary[i]); return true; } + +} // namespace chromeos diff --git a/chrome/browser/chromeos/login/google_authenticator.h b/chrome/browser/chromeos/login/google_authenticator.h index f00ae8d..c89b7f0 100644 --- a/chrome/browser/chromeos/login/google_authenticator.h +++ b/chrome/browser/chromeos/login/google_authenticator.h @@ -10,53 +10,38 @@ #include "base/basictypes.h" #include "base/file_path.h" #include "base/ref_counted.h" -#include "base/scoped_ptr.h" #include "base/sha2.h" #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; +#include "testing/gtest/include/gtest/gtest_prod.h" // For FRIEND_TEST // Authenticates a Chromium OS user against the Google Accounts ClientLogin API. +class Profile; + +namespace chromeos { + +class GoogleAuthenticatorTest; +class LoginStatusConsumer; + class GoogleAuthenticator : public Authenticator, public URLFetcher::Delegate { public: - GoogleAuthenticator(LoginStatusConsumer* consumer, - AuthResponseHandler* cl_handler, - AuthResponseHandler* i_handler) - : Authenticator(consumer), - fetcher_(NULL), - getter_(NULL), - client_login_handler_(cl_handler), - issue_handler_(i_handler) { - CHECK(chromeos::CrosLibrary::Get()->EnsureLoaded()); - library_ = chromeos::CrosLibrary::Get()->GetCryptohomeLibrary(); - } - - explicit GoogleAuthenticator(LoginStatusConsumer* consumer) - : Authenticator(consumer), - fetcher_(NULL), - getter_(NULL), - client_login_handler_(NULL), - issue_handler_(NULL) { - CHECK(chromeos::CrosLibrary::Get()->EnsureLoaded()); - library_ = chromeos::CrosLibrary::Get()->GetCryptohomeLibrary(); - } - - virtual ~GoogleAuthenticator() {} + explicit GoogleAuthenticator(LoginStatusConsumer* consumer); + virtual ~GoogleAuthenticator(); // Given a |username| and |password|, this method attempts to authenticate to // 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. + // an error message. Uses |profile| when doing URL fetches. + // Should be called on the FILE thread! // // Returns true if the attempt gets sent successfully and false if not. - bool Authenticate(const std::string& username, + bool Authenticate(Profile* profile, + const std::string& username, const std::string& password); // Overridden from URLFetcher::Delegate @@ -70,58 +55,51 @@ 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 - // to the subclasses specifically, and I want to allow mocked out - // LoginStatusConsumers to be used here as well. - static void OnLoginSuccess(LoginStatusConsumer* consumer, - const std::string& username, - const std::string& passhash, - const ResponseCookies& cookies); - static void CheckOffline(LoginStatusConsumer* consumer, - const std::string& username, - const std::string& passhash, - const URLRequestStatus& status); - static void OnLoginFailure(LoginStatusConsumer* consumer, - const std::string& data); - - // Meant for testing. - void set_system_salt(const std::vector<unsigned char>& fake_salt) { - system_salt_ = fake_salt; + + // Public for testing. + void set_system_salt(const std::vector<unsigned char>& new_salt) { + system_salt_ = new_salt; + } + void set_localaccount(const std::string& new_name) { + localaccount_ = new_name; + checked_for_localaccount_ = true; } void set_username(const std::string& fake_user) { username_ = fake_user; } void set_password_hash(const std::string& fake_hash) { ascii_hash_ = fake_hash; } - private: - static const char kCookiePersistence[]; - static const char kAccountType[]; - static const char kSource[]; - static const char kFormat[]; - static const int kHttpSuccess; - static const char kSystemSalt[]; - static const char kOpenSSLMagic[]; + // 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 CheckOffline(const URLRequestStatus& status); + void CheckLocalaccount(const std::string& error); + void OnLoginFailure(const std::string& data); - 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_; + // The signal to cryptohomed that we want a tmpfs. + // TODO(cmasone): revisit this after cryptohome re-impl + static const char kTmpfsTrigger[]; + private: // If we don't have the system salt yet, loads it from |path|. - void LoadSystemSalt(FilePath& path); + // Should only be called on the FILE thread. + void LoadSystemSalt(const FilePath& path); + + // If we haven't already, looks in a file called |filename| next to + // the browser executable for a "localaccount" name, and retrieves it + // if one is present. If someone attempts to authenticate with this + // username, we will mount a tmpfs for them and let them use the + // browser. + // Should only be called on the FILE thread. + void LoadLocalaccount(const std::string& filename); // Stores a hash of |password|, salted with the ascii of |system_salt_|. void StoreHashedPassword(const std::string& password); + // Returns the ascii encoding of the system salt. + std::string SaltAsAscii(); + // Converts the binary data |binary| into an ascii hex string and stores // it in |hex_string|. Not guaranteed to be NULL-terminated. // Returns false if |hex_string| is too small, true otherwise. @@ -130,7 +108,42 @@ class GoogleAuthenticator : public Authenticator, char* hex_string, const unsigned int len); + // Constants to use in the ClientLogin request POST body. + static const char kCookiePersistence[]; + static const char kAccountType[]; + static const char kSource[]; + + // The format of said POST body. + static const char kFormat[]; + + // Chromium OS system salt stored here. + static const char kSystemSalt[]; + // String that appears at the start of OpenSSL cipher text with embedded salt. + static const char kOpenSSLMagic[]; + + // Name of a file, next to chrome, that contains a local account username. + static const char kLocalaccountFile[]; + + URLFetcher* fetcher_; + URLRequestContextGetter* getter_; + std::string username_; + std::string ascii_hash_; + std::vector<unsigned char> system_salt_; + std::string localaccount_; + bool checked_for_localaccount_; // needed becasuse empty localaccount_ is ok. + + friend class GoogleAuthenticatorTest; + FRIEND_TEST(GoogleAuthenticatorTest, SaltToAsciiTest); + FRIEND_TEST(GoogleAuthenticatorTest, ReadSaltTest); + FRIEND_TEST(GoogleAuthenticatorTest, ReadLocalaccountTest); + FRIEND_TEST(GoogleAuthenticatorTest, ReadLocalaccountTrailingWSTest); + FRIEND_TEST(GoogleAuthenticatorTest, ReadNoLocalaccountTest); + FRIEND_TEST(GoogleAuthenticatorTest, LoginNetFailureTest); + FRIEND_TEST(GoogleAuthenticatorTest, LoginDeniedTest); + DISALLOW_COPY_AND_ASSIGN(GoogleAuthenticator); }; +} // namespace chromeos + #endif // CHROME_BROWSER_CHROMEOS_LOGIN_GOOGLE_AUTHENTICATOR_H_ diff --git a/chrome/browser/chromeos/login/google_authenticator_unittest.cc b/chrome/browser/chromeos/login/google_authenticator_unittest.cc index 8139abd..a41ebe6 100644 --- a/chrome/browser/chromeos/login/google_authenticator_unittest.cc +++ b/chrome/browser/chromeos/login/google_authenticator_unittest.cc @@ -2,50 +2,62 @@ // 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/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> #include <vector> +#include "base/file_path.h" +#include "base/file_util.h" #include "base/message_loop.h" +#include "base/path_service.h" +#include "base/ref_counted.h" #include "base/scoped_ptr.h" +#include "base/string_util.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/browser/net/url_fetcher.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/test/testing_profile.h" #include "googleurl/src/gurl.h" #include "net/url_request/url_request_status.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/gmock/include/gmock/gmock.h" -namespace chromeos { +using namespace file_util; using ::testing::AnyNumber; using ::testing::InvokeWithoutArgs; using ::testing::Return; using ::testing::_; +namespace chromeos { + class MockConsumer : public LoginStatusConsumer { public: MockConsumer() {} ~MockConsumer() {} - MOCK_METHOD1(OnLoginFailure, void(const std::string error)); - MOCK_METHOD2(OnLoginSuccess, void(const std::string username, - const std::vector<std::string> cookies)); + MOCK_METHOD1(OnLoginFailure, void(const std::string& error)); + MOCK_METHOD2(OnLoginSuccess, void(const std::string& username, + const std::string& data)); }; class GoogleAuthenticatorTest : public ::testing::Test { public: - GoogleAuthenticatorTest() : username_("me@nowhere.org") { + GoogleAuthenticatorTest() + : username_("me@nowhere.org"), + bytes_as_ascii_("ffff") { memset(fake_hash_, 0, sizeof(fake_hash_)); fake_hash_[0] = 10; fake_hash_[1] = 1; fake_hash_[7] = 10 << 4; hash_ascii_.assign("0a010000000000a0"); - hash_ascii_.append(std::string(16,'0')); + hash_ascii_.append(std::string(16, '0')); + + memset(raw_bytes_, 0xff, sizeof(raw_bytes_)); } ~GoogleAuthenticatorTest() {} @@ -74,13 +86,39 @@ class GoogleAuthenticatorTest : public ::testing::Test { test_api->SetCryptohomeLibrary(NULL); } + FilePath PopulateTempFile(const char* data, int data_len) { + FilePath out; + FILE* tmp_file = CreateAndOpenTemporaryFile(&out); + EXPECT_NE(tmp_file, reinterpret_cast<FILE*>(NULL)); + EXPECT_EQ(WriteFile(out, data, data_len), data_len); + EXPECT_TRUE(CloseFile(tmp_file)); + return out; + } + + FilePath FakeLocalaccountFile(const std::string& ascii) { + FilePath exe_dir; + FilePath local_account_file; + PathService::Get(base::DIR_EXE, &exe_dir); + FILE* tmp_file = CreateAndOpenTemporaryFileInDir(exe_dir, + &local_account_file); + int ascii_len = ascii.length(); + EXPECT_NE(tmp_file, reinterpret_cast<FILE*>(NULL)); + EXPECT_EQ(WriteFile(local_account_file, ascii.c_str(), ascii_len), + ascii_len); + EXPECT_TRUE(CloseFile(tmp_file)); + return local_account_file; + } + unsigned char fake_hash_[32]; std::string hash_ascii_; std::string username_; + std::string data_; ResponseCookies cookies_; // Mocks, destroyed by CrosLibrary class. MockCryptohomeLibrary* mock_library_; MockLibraryLoader* loader_; + char raw_bytes_[2]; + std::string bytes_as_ascii_; }; TEST_F(GoogleAuthenticatorTest, SaltToAsciiTest) { @@ -90,30 +128,47 @@ TEST_F(GoogleAuthenticatorTest, SaltToAsciiTest) { fake_salt[7] = 10 << 4; std::vector<unsigned char> salt_v(fake_salt, fake_salt + sizeof(fake_salt)); - GoogleAuthenticator auth(NULL, NULL, NULL); - auth.set_system_salt(salt_v); + scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(NULL)); + auth->set_system_salt(salt_v); - EXPECT_EQ("0a010000000000a0", auth.SaltAsAscii()); + EXPECT_EQ("0a010000000000a0", auth->SaltAsAscii()); } -TEST_F(GoogleAuthenticatorTest, ClientLoginResponseHandlerTest) { - ClientLoginResponseHandler handler(NULL); - std::string input("a\nb\n"); - std::string expected("a&b&"); - expected.append(ClientLoginResponseHandler::kService); +TEST_F(GoogleAuthenticatorTest, ReadSaltTest) { + FilePath tmp_file_path = PopulateTempFile(raw_bytes_, sizeof(raw_bytes_)); - scoped_ptr<URLFetcher> fetcher(handler.Handle(input, NULL)); - EXPECT_EQ(expected, handler.payload()); + scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(NULL)); + auth->LoadSystemSalt(tmp_file_path); + EXPECT_EQ(auth->SaltAsAscii(), bytes_as_ascii_); + Delete(tmp_file_path, false); } -TEST_F(GoogleAuthenticatorTest, IssueResponseHandlerTest) { - IssueResponseHandler handler(NULL); - std::string input("a\n"); - std::string expected(IssueResponseHandler::kTokenAuthUrl); - expected.append(input); +TEST_F(GoogleAuthenticatorTest, ReadLocalaccountTest) { + FilePath tmp_file_path = FakeLocalaccountFile(bytes_as_ascii_); - scoped_ptr<URLFetcher> fetcher(handler.Handle(input, NULL)); - EXPECT_EQ(expected, handler.token_url()); + scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(NULL)); + auth->LoadLocalaccount(tmp_file_path.BaseName().value()); + EXPECT_EQ(auth->localaccount_, bytes_as_ascii_); + Delete(tmp_file_path, false); +} + +TEST_F(GoogleAuthenticatorTest, ReadLocalaccountTrailingWSTest) { + FilePath tmp_file_path = + FakeLocalaccountFile(StringPrintf("%s\n", bytes_as_ascii_.c_str())); + + scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(NULL)); + auth->LoadLocalaccount(tmp_file_path.BaseName().value()); + EXPECT_EQ(auth->localaccount_, bytes_as_ascii_); + Delete(tmp_file_path, false); +} + +TEST_F(GoogleAuthenticatorTest, ReadNoLocalaccountTest) { + FilePath tmp_file_path = FakeLocalaccountFile(bytes_as_ascii_); + EXPECT_TRUE(Delete(tmp_file_path, false)); // Ensure non-existent file. + + scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(NULL)); + auth->LoadLocalaccount(tmp_file_path.BaseName().value()); + EXPECT_EQ(auth->localaccount_, std::string()); } TEST_F(GoogleAuthenticatorTest, OnLoginSuccessTest) { @@ -123,9 +178,10 @@ TEST_F(GoogleAuthenticatorTest, OnLoginSuccessTest) { EXPECT_CALL(*mock_library_, Mount(username_, hash_ascii_)) .WillOnce(Return(true)); - GoogleAuthenticator auth(&consumer, NULL, NULL); - auth.OnLoginSuccess(&consumer, username_, hash_ascii_, - cookies_); + scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer)); + auth->set_password_hash(hash_ascii_); + auth->set_username(username_); + auth->OnLoginSuccess(data_); } TEST_F(GoogleAuthenticatorTest, MountFailureTest) { @@ -135,12 +191,12 @@ TEST_F(GoogleAuthenticatorTest, MountFailureTest) { EXPECT_CALL(*mock_library_, Mount(username_, hash_ascii_)) .WillOnce(Return(false)); - GoogleAuthenticator auth(&consumer, NULL, NULL); - auth.OnLoginSuccess(&consumer, username_, hash_ascii_, - cookies_); + scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer)); + auth->set_password_hash(hash_ascii_); + auth->set_username(username_); + auth->OnLoginSuccess(data_); } -static void Quit() { MessageLoop::current()->Quit(); } TEST_F(GoogleAuthenticatorTest, LoginNetFailureTest) { MessageLoopForUI message_loop; ChromeThread ui_thread(ChromeThread::UI, &message_loop); @@ -153,15 +209,15 @@ TEST_F(GoogleAuthenticatorTest, LoginNetFailureTest) { MockConsumer consumer; EXPECT_CALL(consumer, OnLoginFailure(data)) - .WillOnce(InvokeWithoutArgs(Quit)); + .Times(1); EXPECT_CALL(*mock_library_, CheckKey(username_, hash_ascii_)) .WillOnce(Return(false)); - GoogleAuthenticator auth(&consumer, 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. + scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer)); + auth->set_password_hash(hash_ascii_); + auth->set_username(username_); + auth->OnURLFetchComplete(NULL, source, status, 0, cookies_, data); + message_loop.RunAllPending(); } TEST_F(GoogleAuthenticatorTest, LoginDeniedTest) { @@ -175,11 +231,13 @@ TEST_F(GoogleAuthenticatorTest, LoginDeniedTest) { MockConsumer consumer; EXPECT_CALL(consumer, OnLoginFailure(data)) - .WillOnce(InvokeWithoutArgs(Quit)); + .Times(1); - GoogleAuthenticator auth(&consumer, NULL, NULL); - auth.OnURLFetchComplete(NULL, source, status, 403, cookies_, data); - MessageLoop::current()->Run(); // So tasks can be posted. + scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer)); + auth->set_password_hash(hash_ascii_); + auth->set_username(username_); + auth->OnURLFetchComplete(NULL, source, status, 403, cookies_, data); + message_loop.RunAllPending(); } TEST_F(GoogleAuthenticatorTest, OfflineLoginTest) { @@ -193,77 +251,135 @@ TEST_F(GoogleAuthenticatorTest, OfflineLoginTest) { URLRequestStatus status(URLRequestStatus::FAILED, error_no); MockConsumer consumer; - EXPECT_CALL(consumer, OnLoginSuccess(username_, cookies_)) - .WillOnce(InvokeWithoutArgs(Quit)); + EXPECT_CALL(consumer, OnLoginSuccess(username_, 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)); - GoogleAuthenticator auth(&consumer, 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. + scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer)); + auth->set_password_hash(hash_ascii_); + auth->set_username(username_); + auth->OnURLFetchComplete(NULL, source, status, 0, cookies_, data); + message_loop.RunAllPending(); } -TEST_F(GoogleAuthenticatorTest, ClientLoginPassIssueAuthTokenFailTest) { +TEST_F(GoogleAuthenticatorTest, OnlineLoginTest) { 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); + GURL source(AuthResponseHandler::kTokenAuthUrl); URLRequestStatus status(URLRequestStatus::SUCCESS, 0); MockConsumer consumer; - EXPECT_CALL(consumer, OnLoginFailure(data)) - .WillOnce(InvokeWithoutArgs(Quit)); - MockAuthResponseHandler* cl_handler = new MockAuthResponseHandler; - EXPECT_CALL(*cl_handler, CanHandle(cl_source)) + EXPECT_CALL(consumer, OnLoginSuccess(username_, data_)) + .Times(1); + EXPECT_CALL(*mock_library_, Mount(username_, hash_ascii_)) .WillOnce(Return(true)); - GoogleAuthenticator auth(&consumer, - cl_handler, // takes ownership. - new IssueResponseHandler(NULL)); - auth.set_password_hash(hash_ascii_); - auth.set_username(username_); - - EXPECT_CALL(*cl_handler, Handle(_, &auth)) - .WillOnce(Return(new URLFetcher(GURL(""), - URLFetcher::POST, - &auth))); - - auth.OnURLFetchComplete(NULL, - cl_source, + scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer)); + auth->set_password_hash(hash_ascii_); + auth->set_username(username_); + auth->OnURLFetchComplete(NULL, + source, status, - 200, + kHttpSuccess, cookies_, std::string()); - auth.OnURLFetchComplete(NULL, iat_source, status, 403, cookies_, data); - MessageLoop::current()->Run(); // So tasks can be posted. + message_loop.RunAllPending(); } -TEST_F(GoogleAuthenticatorTest, OnlineLoginTest) { +TEST_F(GoogleAuthenticatorTest, LocalaccountLoginTest) { + GURL source(AuthResponseHandler::kTokenAuthUrl); + URLRequestStatus status(URLRequestStatus::SUCCESS, 0); + + std::string trigger(GoogleAuthenticator::kTmpfsTrigger); + + MockConsumer consumer; + EXPECT_CALL(consumer, OnLoginSuccess(username_, _)) + .Times(1); + EXPECT_CALL(*mock_library_, Mount(trigger, _)) + .WillOnce(Return(true)); + + scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer)); + auth->set_password_hash(hash_ascii_); + auth->set_username(username_); + auth->set_localaccount(username_); + + 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, FullLoginTest) { MessageLoopForUI message_loop; ChromeThread ui_thread(ChromeThread::UI, &message_loop); + ChromeThread file_thread(ChromeThread::FILE); + file_thread.Start(); GURL source(AuthResponseHandler::kTokenAuthUrl); URLRequestStatus status(URLRequestStatus::SUCCESS, 0); MockConsumer consumer; - EXPECT_CALL(consumer, OnLoginSuccess(username_, cookies_)) - .WillOnce(InvokeWithoutArgs(Quit)); - EXPECT_CALL(*mock_library_, Mount(username_, hash_ascii_)) + EXPECT_CALL(consumer, OnLoginSuccess(username_, data_)) + .Times(1); + EXPECT_CALL(*mock_library_, Mount(username_, _)) .WillOnce(Return(true)); - GoogleAuthenticator auth(&consumer, - new ClientLoginResponseHandler(NULL), - new IssueResponseHandler(NULL)); - auth.set_password_hash(hash_ascii_); - auth.set_username(username_); - auth.OnURLFetchComplete(NULL, source, status, 200, cookies_, std::string()); - MessageLoop::current()->Run(); // So tasks can be posted. + TestingProfile profile; + + MockFactory factory; + URLFetcher::set_factory(&factory); + std::vector<unsigned char> salt_v(fake_hash_, + fake_hash_ + sizeof(fake_hash_)); + + scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer)); + auth->set_system_salt(salt_v); + + ChromeThread::PostTask( + ChromeThread::FILE, FROM_HERE, + NewRunnableMethod(auth.get(), + &Authenticator::Authenticate, + &profile, username_, hash_ascii_)); + file_thread.Stop(); + message_loop.RunAllPending(); + URLFetcher::set_factory(NULL); } } // namespace chromeos diff --git a/chrome/browser/chromeos/login/issue_response_handler.cc b/chrome/browser/chromeos/login/issue_response_handler.cc index 7c0850a..20fa619 100644 --- a/chrome/browser/chromeos/login/issue_response_handler.cc +++ b/chrome/browser/chromeos/login/issue_response_handler.cc @@ -10,8 +10,7 @@ #include "chrome/browser/net/url_fetcher.h" #include "net/base/load_flags.h" -const int kMaxRedirs = 2; -const int kTimeout = 2; +namespace chromeos { // Overridden from AuthResponseHandler. bool IssueResponseHandler::CanHandle(const GURL& url) { @@ -31,8 +30,11 @@ URLFetcher* IssueResponseHandler::Handle( new URLFetcher(GURL(token_url_), URLFetcher::GET, catcher); fetcher->set_load_flags(net::LOAD_DO_NOT_SEND_COOKIES); if (getter_) { + LOG(INFO) << "Fetching"; fetcher->set_request_context(getter_); fetcher->Start(); } return fetcher; } + +} // namespace chromeos diff --git a/chrome/browser/chromeos/login/issue_response_handler.h b/chrome/browser/chromeos/login/issue_response_handler.h index 3153b97..4741c80 100644 --- a/chrome/browser/chromeos/login/issue_response_handler.h +++ b/chrome/browser/chromeos/login/issue_response_handler.h @@ -12,6 +12,13 @@ class URLRequestContextGetter; +namespace chromeos { + +// Handles responses to a fetch executed upon the Google Accounts IssueAuthToken +// endpoint. The token that's sent back in the response body is used as an +// URL query parameter in a request that, ultimately, results in a full set +// of authorization cookies for Google services being left in the cookie jar +// associated with |getter_|. class IssueResponseHandler : public AuthResponseHandler { public: explicit IssueResponseHandler(URLRequestContextGetter* getter) @@ -24,15 +31,20 @@ class IssueResponseHandler : public AuthResponseHandler { // 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. + // the response to the fetch. This fetch will follow redirects, which is + // necesary to support GAFYD and corp accounts. 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_; + DISALLOW_COPY_AND_ASSIGN(IssueResponseHandler); }; +} // namespace chromeos + #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 c3c3be1..f89b6b9 100644 --- a/chrome/browser/chromeos/login/login_manager_view.cc +++ b/chrome/browser/chromeos/login/login_manager_view.cc @@ -18,6 +18,8 @@ #include "base/logging.h" #include "base/process_util.h" #include "base/utf_string_conversions.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/chrome_thread.h" #include "chrome/browser/chromeos/cros/cros_library.h" #include "chrome/browser/chromeos/cros/network_library.h" #include "chrome/browser/chromeos/login/authentication_notification_details.h" @@ -25,6 +27,8 @@ #include "chrome/browser/chromeos/login/rounded_rect_painter.h" #include "chrome/browser/chromeos/login/screen_observer.h" #include "chrome/browser/chromeos/login/user_manager.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/profile_manager.h" #include "chrome/common/notification_service.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" @@ -78,9 +82,9 @@ LoginManagerView::LoginManagerView(ScreenObserver* observer) ALLOW_THIS_IN_INITIALIZER_LIST(focus_grabber_factory_(this)), focus_delayed_(false) { if (kStubOutLogin) - authenticator_.reset(new StubAuthenticator(this)); + authenticator_ = new StubAuthenticator(this); else - authenticator_.reset(LoginUtils::Get()->CreateAuthenticator(this)); + authenticator_ = LoginUtils::Get()->CreateAuthenticator(this); } LoginManagerView::~LoginManagerView() { @@ -320,7 +324,12 @@ void LoginManagerView::Login() { username_field_->SetText(UTF8ToUTF16(username)); } - authenticator_->Authenticate(username, password); + Profile* profile = g_browser_process->profile_manager()->GetWizardProfile(); + ChromeThread::PostTask( + ChromeThread::FILE, FROM_HERE, + NewRunnableMethod(authenticator_.get(), + &Authenticator::Authenticate, + profile, username, password)); } // Sign in button causes a login attempt. @@ -333,7 +342,7 @@ void LoginManagerView::ButtonPressed( } } -void LoginManagerView::OnLoginFailure(const std::string error) { +void LoginManagerView::OnLoginFailure(const std::string& error) { LOG(INFO) << "LoginManagerView: OnLoginFailure() " << error; NetworkLibrary* network = CrosLibrary::Get()->GetNetworkLibrary(); @@ -357,13 +366,14 @@ void LoginManagerView::OnLoginFailure(const std::string error) { password_field_->RequestFocus(); } -void LoginManagerView::OnLoginSuccess(const std::string username, - std::vector<std::string> cookies) { +void LoginManagerView::OnLoginSuccess(const std::string& username, + const std::string& credentials) { // TODO(cmasone): something sensible if errors occur. if (observer_) { observer_->OnExit(ScreenObserver::LOGIN_SIGN_IN_SELECTED); } - LoginUtils::Get()->CompleteLogin(username, cookies); + + LoginUtils::Get()->CompleteLogin(username, credentials); } 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 53082fd..a76196f 100644 --- a/chrome/browser/chromeos/login/login_manager_view.h +++ b/chrome/browser/chromeos/login/login_manager_view.h @@ -8,6 +8,7 @@ #include <string> #include <vector> +#include "base/ref_counted.h" #include "base/scoped_ptr.h" #include "chrome/browser/chromeos/login/authenticator.h" #include "chrome/browser/chromeos/login/login_status_consumer.h" @@ -71,9 +72,9 @@ class LoginManagerView : public views::View, virtual bool AcceleratorPressed(const views::Accelerator& accelerator); // Overriden from LoginStatusConsumer. - virtual void OnLoginFailure(const std::string error); - virtual void OnLoginSuccess(const std::string username, - std::vector<std::string> cookies); + virtual void OnLoginFailure(const std::string& error); + virtual void OnLoginSuccess(const std::string& username, + const std::string& credentials); protected: // views::View overrides: @@ -133,7 +134,7 @@ class LoginManagerView : public views::View, // (on the hidden tab, for example). bool focus_delayed_; - scoped_ptr<Authenticator> authenticator_; + scoped_refptr<Authenticator> authenticator_; DISALLOW_COPY_AND_ASSIGN(LoginManagerView); }; diff --git a/chrome/browser/chromeos/login/login_manager_view_browsertest.cc b/chrome/browser/chromeos/login/login_manager_view_browsertest.cc index b5cb691..679051474 100644 --- a/chrome/browser/chromeos/login/login_manager_view_browsertest.cc +++ b/chrome/browser/chromeos/login/login_manager_view_browsertest.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "base/message_loop.h" +#include "chrome/browser/chrome_thread.h" #include "chrome/browser/chromeos/cros/mock_cryptohome_library.h" #include "chrome/browser/chromeos/cros/mock_login_library.h" #include "chrome/browser/chromeos/login/login_manager_view.h" @@ -14,9 +15,12 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +class Profile; + namespace chromeos { using ::testing::AnyNumber; +using ::testing::InvokeWithoutArgs; using ::testing::Return; const char kUsername[] = "test_user@gmail.com"; @@ -32,15 +36,25 @@ class MockAuthenticator : public Authenticator { expected_password_(expected_password) { } - // Returns true after calling OnLoginSuccess(). - virtual bool Authenticate(const std::string& username, + // Returns true after posting task to UI thread to call OnLoginSuccess(). + // This is called on the FILE thread now, so we need to do this. + virtual bool Authenticate(Profile* profile, + const std::string& username, const std::string& password) { EXPECT_EQ(expected_username_, username); EXPECT_EQ(expected_password_, password); - consumer_->OnLoginSuccess(username, std::vector<std::string>()); + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableMethod(this, &MockAuthenticator::OnLoginSuccess, username)); return true; } + void OnLoginSuccess(const std::string& username) { + consumer_->OnLoginSuccess(username, std::string()); + } + + void OnLoginFailure(const std::string& data) {} + private: std::string expected_username_; std::string expected_password_; @@ -57,7 +71,7 @@ class MockLoginUtils : public LoginUtils { } virtual void CompleteLogin(const std::string& username, - std::vector<std::string> cookies) { + const std::string& cookies) { EXPECT_EQ(expected_username_, username); } @@ -111,6 +125,11 @@ class LoginManagerViewTest : public WizardInProcessBrowserTest { DISALLOW_COPY_AND_ASSIGN(LoginManagerViewTest); }; +static void Quit() { + LOG(INFO) << "Posting a QuitTask to UI thread"; + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, new MessageLoop::QuitTask); +} IN_PROC_BROWSER_TEST_F(LoginManagerViewTest, TestBasic) { ASSERT_TRUE(controller() != NULL); ASSERT_EQ(controller()->current_screen(), controller()->GetLoginScreen()); @@ -119,13 +138,19 @@ IN_PROC_BROWSER_TEST_F(LoginManagerViewTest, TestBasic) { new MockScreenObserver()); EXPECT_CALL(*mock_screen_observer, OnExit(ScreenObserver::LOGIN_SIGN_IN_SELECTED)) - .Times(1); + .WillOnce(InvokeWithoutArgs(Quit)); LoginManagerView* login = controller()->GetLoginScreen()->view(); login->set_observer(mock_screen_observer.get()); login->SetUsername(kUsername); login->SetPassword(kPassword); + + bool old_state = MessageLoop::current()->NestableTasksAllowed(); + MessageLoop::current()->SetNestableTasksAllowed(true); login->Login(); + MessageLoop::current()->Run(); + MessageLoop::current()->SetNestableTasksAllowed(old_state); + login->set_observer(NULL); } diff --git a/chrome/browser/chromeos/login/login_status_consumer.h b/chrome/browser/chromeos/login/login_status_consumer.h index bf8f7e3..5c28c62 100644 --- a/chrome/browser/chromeos/login/login_status_consumer.h +++ b/chrome/browser/chromeos/login/login_status_consumer.h @@ -7,18 +7,19 @@ #include <string> +namespace chromeos { + // An interface that defines the callbacks for objects that the // Authenticator class will call to report the success/failure of // authentication for Chromium OS. class LoginStatusConsumer { public: 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, - std::vector<std::string> cookies) = 0; + virtual void OnLoginFailure(const std::string& error) = 0; + virtual void OnLoginSuccess(const std::string& username, + const std::string& credentials) = 0; }; - +} // namespace chromeos #endif // CHROME_BROWSER_CHROMEOS_LOGIN_LOGIN_STATUS_CONSUMER_H_ diff --git a/chrome/browser/chromeos/login/login_utils.cc b/chrome/browser/chromeos/login/login_utils.cc index bc970f6..006f2bb 100644 --- a/chrome/browser/chromeos/login/login_utils.cc +++ b/chrome/browser/chromeos/login/login_utils.cc @@ -14,6 +14,7 @@ #include "chrome/browser/chromeos/cros/login_library.h" #include "chrome/browser/chromeos/external_cookie_handler.h" #include "chrome/browser/chromeos/login/authentication_notification_details.h" +#include "chrome/browser/chromeos/login/cookie_fetcher.h" #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" @@ -37,7 +38,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, - std::vector<std::string> cookies); + const std::string& credentials); // Creates and returns the authenticator to use. The caller owns the returned // Authenticator and must delete it when done. @@ -47,9 +48,9 @@ class LoginUtilsImpl : public LoginUtils { DISALLOW_COPY_AND_ASSIGN(LoginUtilsImpl); }; -class LoginUtilsWraper { +class LoginUtilsWrapper { public: - LoginUtilsWraper() : ptr_(new LoginUtilsImpl) { + LoginUtilsWrapper() : ptr_(new LoginUtilsImpl) { } LoginUtils* get() { @@ -63,12 +64,12 @@ class LoginUtilsWraper { private: scoped_ptr<LoginUtils> ptr_; - DISALLOW_COPY_AND_ASSIGN(LoginUtilsWraper); + DISALLOW_COPY_AND_ASSIGN(LoginUtilsWrapper); }; void LoginUtilsImpl::CompleteLogin(const std::string& username, - std::vector<std::string> cookies) { - LOG(INFO) << "LoginManagerView: OnLoginSuccess()"; + const std::string& credentials) { + LOG(INFO) << "Completing login for " << username; if (CrosLibrary::Get()->EnsureLoaded()) CrosLibrary::Get()->GetLoginLibrary()->StartSession(username, ""); @@ -83,7 +84,6 @@ void LoginUtilsImpl::CompleteLogin(const std::string& username, Details<AuthenticationNotificationDetails>(&details)); // Now launch the initial browser window. - BrowserInit browser_init; const CommandLine& command_line = *CommandLine::ForCurrentProcess(); FilePath user_data_dir; PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); @@ -91,19 +91,20 @@ void LoginUtilsImpl::CompleteLogin(const std::string& username, // The default profile will have been changed because the ProfileManager // will process the notification that the UserManager sends out. Profile* profile = profile_manager->GetDefaultProfile(user_data_dir); - int return_code; if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kInChromeAuth)) { ExternalCookieHandler::GetCookies(command_line, profile); + DoBrowserLaunch(profile); } else { - GURL url(ExternalCookieHandler::kGoogleAccountsUrl); - net::CookieOptions options; - options.set_include_httponly(); - profile->GetRequestContext()->GetCookieStore()->SetCookiesWithOptions( - url, cookies, options); + // Take the credentials passed in and try to exchange them for + // full-fledged Google authentication cookies. This is + // best-effort; it's possible that we'll fail due to network + // troubles or some such. Either way, |cf| will call + // DoBrowserLaunch on the UI thread when it's done, and then + // delete itself. + CookieFetcher* cf = new CookieFetcher(profile); + cf->AttemptFetch(credentials); } - browser_init.LaunchBrowser(command_line, profile, std::wstring(), true, - &return_code); } Authenticator* LoginUtilsImpl::CreateAuthenticator( @@ -114,11 +115,22 @@ Authenticator* LoginUtilsImpl::CreateAuthenticator( } LoginUtils* LoginUtils::Get() { - return Singleton<LoginUtilsWraper>::get()->get(); + return Singleton<LoginUtilsWrapper>::get()->get(); } void LoginUtils::Set(LoginUtils* mock) { - Singleton<LoginUtilsWraper>::get()->reset(mock); + Singleton<LoginUtilsWrapper>::get()->reset(mock); +} + +void LoginUtils::DoBrowserLaunch(Profile* profile) { + LOG(INFO) << "Launching browser..."; + BrowserInit browser_init; + int return_code; + browser_init.LaunchBrowser(*CommandLine::ForCurrentProcess(), + profile, + std::wstring(), + true, + &return_code); } } // namespace chromeos diff --git a/chrome/browser/chromeos/login/login_utils.h b/chrome/browser/chromeos/login/login_utils.h index bb1b1d3..6d7791c 100644 --- a/chrome/browser/chromeos/login/login_utils.h +++ b/chrome/browser/chromeos/login/login_utils.h @@ -8,15 +8,13 @@ #include <string> #include <vector> -class Authenticator; -class LoginStatusConsumer; - -namespace views { -class Widget; -} +class Profile; namespace chromeos { +class Authenticator; +class LoginStatusConsumer; + class LoginUtils { public: // Get LoginUtils singleton object. If it was not set before, new default @@ -26,12 +24,16 @@ class LoginUtils { // Set LoginUtils singleton object for test purpose only! static void Set(LoginUtils* ptr); + // Thin wrapper around BrowserInit::LaunchBrowser(). Meant to be used in a + // Task posted to the UI thread. + static void DoBrowserLaunch(Profile* profile); + virtual ~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, - std::vector<std::string> cookies) = 0; + const std::string& credentials) = 0; // Creates and returns the authenticator to use. The caller owns the returned // Authenticator and must delete it when done. diff --git a/chrome/browser/chromeos/login/mock_auth_response_handler.cc b/chrome/browser/chromeos/login/mock_auth_response_handler.cc new file mode 100644 index 0000000..8148fc2 --- /dev/null +++ b/chrome/browser/chromeos/login/mock_auth_response_handler.cc @@ -0,0 +1,46 @@ +// 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/mock_auth_response_handler.h" + +#include <string> + +#include "chrome/browser/net/url_fetcher.h" +#include "googleurl/src/gurl.h" +#include "net/url_request/url_request_status.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace chromeos { + +using ::testing::_; +using ::testing::Invoke; + +MockAuthResponseHandler::MockAuthResponseHandler(const GURL& url, + const URLRequestStatus& status, + const int code, + const std::string& data) + : remote_(url), + status_(status), + http_response_code_(code), + data_(data) { + // Take the args sent to Handle() and pass them to MockNetwork(), which will + // use the data passed to the constructor here to fill out the call to + // OnURLFetchComplete(). + ON_CALL(*this, Handle(_,_)) + .WillByDefault(Invoke(this, &MockAuthResponseHandler::MockNetwork)); +} + +URLFetcher* MockAuthResponseHandler::MockNetwork( + std::string data, + URLFetcher::Delegate* delegate) { + delegate->OnURLFetchComplete(NULL, + remote_, + status_, + http_response_code_, + ResponseCookies(), + data_); + return new URLFetcher(GURL(), URLFetcher::GET, delegate); +} + +} // namespace chromeos diff --git a/chrome/browser/chromeos/login/mock_auth_response_handler.h b/chrome/browser/chromeos/login/mock_auth_response_handler.h index 394dc7c..dc8a2b8 100644 --- a/chrome/browser/chromeos/login/mock_auth_response_handler.h +++ b/chrome/browser/chromeos/login/mock_auth_response_handler.h @@ -9,20 +9,44 @@ #include <string> -#include "base/logging.h" +#include "googleurl/src/gurl.h" +#include "net/url_request/url_request_status.h" #include "testing/gmock/include/gmock/gmock.h" -class GURL; class URLFetcher; +namespace chromeos { + +// In real AuthResponseHandler subclasses, Handle(data, delegate) +// initiates an HTTP fetch. To mock this, we would like to set up +// appropriate state and then call the OnURLFetchComplete method of +// |delegate| directly. Rather than using some kind of global state, we +// allow a MockAuthResponseHandler to be instantiated with the state we +// want to send to OnURLFetchComplete, and then have Handle() Invoke a helper +// method that will do this work. class MockAuthResponseHandler : public AuthResponseHandler { public: - MockAuthResponseHandler() {} + MockAuthResponseHandler(const GURL& url, + const URLRequestStatus& status, + const int code, + const std::string& data); virtual ~MockAuthResponseHandler() {} MOCK_METHOD1(CanHandle, bool(const GURL& url)); MOCK_METHOD2(Handle, URLFetcher*(const std::string& to_process, URLFetcher::Delegate* catcher)); + + URLFetcher* MockNetwork(std::string data, URLFetcher::Delegate* delegate); + + private: + const GURL remote_; + const URLRequestStatus status_; + const int http_response_code_; + const std::string data_; + + DISALLOW_COPY_AND_ASSIGN(MockAuthResponseHandler); }; +} // namespace chromeos + #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 689fde3..ef344cb 100644 --- a/chrome/browser/chromeos/login/pam_google_authenticator.cc +++ b/chrome/browser/chromeos/login/pam_google_authenticator.cc @@ -10,8 +10,12 @@ #include "base/process_util.h" #include "chrome/browser/chromeos/login/pam_google_authenticator.h" #include "chrome/browser/chromeos/login/login_status_consumer.h" +#include "chrome/browser/profile.h" -bool PamGoogleAuthenticator::Authenticate(const std::string& username, +namespace chromeos { + +bool PamGoogleAuthenticator::Authenticate(Profile* profile, + const std::string& username, const std::string& password) { base::ProcessHandle handle; std::vector<std::string> argv; @@ -27,9 +31,21 @@ bool PamGoogleAuthenticator::Authenticate(const std::string& username, bool ret = (base::WaitForExitCode(handle, &child_exit_code) && child_exit_code == 0); - if (ret) - consumer_->OnLoginSuccess(username, std::vector<std::string>()); - else - consumer_->OnLoginFailure(""); + if (ret) { + username_ = username; + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableMethod(this, + &PamGoogleAuthenticator::OnLoginSuccess, + std::string())); + } else { + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableMethod(this, + &PamGoogleAuthenticator::OnLoginFailure, + std::string())); + } return ret; } + +} // namespace chromeos diff --git a/chrome/browser/chromeos/login/pam_google_authenticator.h b/chrome/browser/chromeos/login/pam_google_authenticator.h index 688f0c3..bb5bd79 100644 --- a/chrome/browser/chromeos/login/pam_google_authenticator.h +++ b/chrome/browser/chromeos/login/pam_google_authenticator.h @@ -8,9 +8,14 @@ #include <string> #include "chrome/browser/chromeos/login/authenticator.h" +class Profile; + +namespace chromeos { + class LoginStatusConsumer; -// Authenticates a Chromium OS user against the Google Accounts ClientLogin API. +// Authenticates a Chromium OS user against the Google Accounts ClientLogin API +// using a setuid helper binary and a pre-installed pam module. class PamGoogleAuthenticator : public Authenticator { public: @@ -22,11 +27,23 @@ class PamGoogleAuthenticator : public Authenticator { // Given a |username| and |password|, this method attempts to authenticate to // the Google accounts servers. // Returns true if the attempt gets sent successfully and false if not. - bool Authenticate(const std::string& username, + bool Authenticate(Profile* profile, + const std::string& username, const std::string& password); + void OnLoginSuccess(const std::string& credentials) { + consumer_->OnLoginSuccess(username_, credentials); + } + + void OnLoginFailure(const std::string& data) { + consumer_->OnLoginFailure(data); + } + private: + std::string username_; DISALLOW_COPY_AND_ASSIGN(PamGoogleAuthenticator); }; +} // namespace chromeos + #endif // CHROME_BROWSER_CHROMEOS_LOGIN_PAM_GOOGLE_AUTHENTICATOR_H_ |