summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorcmasone@google.com <cmasone@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-04-01 20:47:09 +0000
committercmasone@google.com <cmasone@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-04-01 20:47:09 +0000
commit82e1e1135c3565a0064761d8a4d919d414446d99 (patch)
treee8a706db2fb3a41d304404402259687ace4a7f22
parent60a0438e453b5006cff34972df6188e2fb4e02ce (diff)
downloadchromium_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
-rw-r--r--chrome/browser/chromeos/login/auth_response_handler.cc5
-rw-r--r--chrome/browser/chromeos/login/auth_response_handler.h7
-rw-r--r--chrome/browser/chromeos/login/authenticator.h41
-rw-r--r--chrome/browser/chromeos/login/client_login_response_handler.cc6
-rw-r--r--chrome/browser/chromeos/login/client_login_response_handler.h10
-rw-r--r--chrome/browser/chromeos/login/cookie_fetcher.cc67
-rw-r--r--chrome/browser/chromeos/login/cookie_fetcher.h89
-rw-r--r--chrome/browser/chromeos/login/cookie_fetcher_unittest.cc211
-rw-r--r--chrome/browser/chromeos/login/existing_user_controller.cc25
-rw-r--r--chrome/browser/chromeos/login/existing_user_controller.h12
-rw-r--r--chrome/browser/chromeos/login/google_authenticator.cc205
-rw-r--r--chrome/browser/chromeos/login/google_authenticator.h147
-rw-r--r--chrome/browser/chromeos/login/google_authenticator_unittest.cc288
-rw-r--r--chrome/browser/chromeos/login/issue_response_handler.cc6
-rw-r--r--chrome/browser/chromeos/login/issue_response_handler.h14
-rw-r--r--chrome/browser/chromeos/login/login_manager_view.cc24
-rw-r--r--chrome/browser/chromeos/login/login_manager_view.h9
-rw-r--r--chrome/browser/chromeos/login/login_manager_view_browsertest.cc35
-rw-r--r--chrome/browser/chromeos/login/login_status_consumer.h11
-rw-r--r--chrome/browser/chromeos/login/login_utils.cc46
-rw-r--r--chrome/browser/chromeos/login/login_utils.h16
-rw-r--r--chrome/browser/chromeos/login/mock_auth_response_handler.cc46
-rw-r--r--chrome/browser/chromeos/login/mock_auth_response_handler.h30
-rw-r--r--chrome/browser/chromeos/login/pam_google_authenticator.cc26
-rw-r--r--chrome/browser/chromeos/login/pam_google_authenticator.h21
-rwxr-xr-xchrome/chrome_browser.gypi6
-rw-r--r--chrome/chrome_tests.gypi2
27 files changed, 1073 insertions, 332 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_
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 9021dc3..553c57b 100755
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -368,12 +368,14 @@
'browser/chromeos/login/account_screen.cc',
'browser/chromeos/login/account_screen.h',
'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/background_view.cc',
+ 'browser/chromeos/login/background_view.h',
'browser/chromeos/login/client_login_response_handler.cc',
'browser/chromeos/login/client_login_response_handler.h',
+ 'browser/chromeos/login/cookie_fetcher.cc',
+ 'browser/chromeos/login/cookie_fetcher.h',
'browser/chromeos/login/existing_user_controller.cc',
'browser/chromeos/login/existing_user_controller.h',
'browser/chromeos/login/issue_response_handler.cc',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index c5ac745..cf435b1 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -588,7 +588,9 @@
'browser/chromeos/external_cookie_handler_unittest.cc',
'browser/chromeos/external_metrics_unittest.cc',
'browser/chromeos/gview_request_interceptor_unittest.cc',
+ 'browser/chromeos/login/cookie_fetcher_unittest.cc',
'browser/chromeos/login/google_authenticator_unittest.cc',
+ 'browser/chromeos/login/mock_auth_response_handler.cc',
'browser/chromeos/options/language_config_view_test.cc',
'browser/chromeos/pipe_reader_unittest.cc',
'browser/chromeos/status/language_menu_l10n_util_unittest.cc',