From e4f86d4fe91f102a64bf4b536f94a65e8ebdcccc Mon Sep 17 00:00:00 2001 From: "aruslan@chromium.org" Date: Wed, 27 Mar 2013 07:30:38 +0000 Subject: [PATCH] Sign-in helper for the account chooser. The helper - Performs passive sign-in via the Online Wallet passive auth. - Performs sign-in with LSID+SID for Android. - Retrieves the information about the currently signed-in user The helper uses - Toolbar GetAccountInfo API to fetch the currently signed-in user name, - Online Wallet PassiveAuth API to attempt to passively sign-in the user, and - Gaia TokenAuth API to sign-in the user given user's LSID+SID from Android. BUG=166717,171528 Review URL: https://chromiumcodereview.appspot.com/12810009 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@190847 0039d316-1c4b-4281-b951-d872f2087c98 --- chrome/chrome_browser.gypi | 2 + chrome/chrome_tests_unit.gypi | 3 +- components/autofill/browser/DEPS | 1 + .../browser/wallet/wallet_signin_helper.cc | 298 +++++++++++++++++++++ .../autofill/browser/wallet/wallet_signin_helper.h | 146 ++++++++++ .../browser/wallet/wallet_signin_helper_delegate.h | 48 ++++ .../wallet/wallet_signin_helper_unittest.cc | 250 +++++++++++++++++ 7 files changed, 747 insertions(+), 1 deletion(-) create mode 100644 components/autofill/browser/wallet/wallet_signin_helper.cc create mode 100644 components/autofill/browser/wallet/wallet_signin_helper.h create mode 100644 components/autofill/browser/wallet/wallet_signin_helper_delegate.h create mode 100644 components/autofill/browser/wallet/wallet_signin_helper_unittest.cc diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 188b80f08abb..5486388b2f33 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -2246,6 +2246,8 @@ '../components/autofill/browser/wallet/wallet_items.h', '../components/autofill/browser/wallet/wallet_service_url.cc', '../components/autofill/browser/wallet/wallet_service_url.h', + '../components/autofill/browser/wallet/wallet_signin_helper.cc', + '../components/autofill/browser/wallet/wallet_signin_helper.h', # These files are generated by GRIT. '<(grit_out_dir)/grit/component_extension_resources_map.cc', diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi index f302839cd0b4..fd5fe5d9c8c9 100644 --- a/chrome/chrome_tests_unit.gypi +++ b/chrome/chrome_tests_unit.gypi @@ -1206,9 +1206,9 @@ 'browser/ui/ash/launcher/launcher_context_menu_unittest.cc', 'browser/ui/ash/window_positioner_unittest.cc', 'browser/ui/autofill/autocheckout_bubble_controller_unittest.cc', + 'browser/ui/autofill/autofill_dialog_controller_unittest.cc', 'browser/ui/autofill/autofill_dialog_models_unittest.cc', 'browser/ui/autofill/autofill_popup_controller_unittest.cc', - 'browser/ui/autofill/autofill_dialog_controller_unittest.cc', 'browser/ui/autofill/data_model_wrapper_unittest.cc', 'browser/ui/bookmarks/bookmark_context_menu_controller_unittest.cc', 'browser/ui/bookmarks/bookmark_prompt_controller_unittest.cc', @@ -1680,6 +1680,7 @@ '../components/autofill/browser/wallet/wallet_client_unittest.cc', '../components/autofill/browser/wallet/wallet_items_unittest.cc', '../components/autofill/browser/wallet/wallet_service_url_unittest.cc', + '../components/autofill/browser/wallet/wallet_signin_helper_unittest.cc', '../components/autofill/browser/wallet/wallet_test_util.cc', '../components/autofill/browser/wallet/wallet_test_util.h', diff --git a/components/autofill/browser/DEPS b/components/autofill/browser/DEPS index 5a773aa3e081..9aa86cd20daf 100644 --- a/components/autofill/browser/DEPS +++ b/components/autofill/browser/DEPS @@ -1,6 +1,7 @@ include_rules = [ "+content/public/browser", "+crypto/random.h", + "+google_apis/gaia", "+google_apis/google_api_keys.h", "+net", "+third_party/libjingle", diff --git a/components/autofill/browser/wallet/wallet_signin_helper.cc b/components/autofill/browser/wallet/wallet_signin_helper.cc new file mode 100644 index 000000000000..d4694a2dfdf7 --- /dev/null +++ b/components/autofill/browser/wallet/wallet_signin_helper.cc @@ -0,0 +1,298 @@ +// Copyright 2013 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 "components/autofill/browser/wallet/wallet_signin_helper.h" + +#include "base/callback_helpers.h" +#include "base/json/json_reader.h" +#include "base/logging.h" +#include "base/rand_util.h" +#include "base/stringprintf.h" +#include "base/time.h" +#include "base/values.h" +#include "components/autofill/browser/wallet/wallet_service_url.h" +#include "components/autofill/browser/wallet/wallet_signin_helper_delegate.h" +#include "google_apis/gaia/gaia_auth_fetcher.h" +#include "google_apis/gaia/gaia_auth_util.h" +#include "google_apis/gaia/gaia_constants.h" +#include "google_apis/gaia/gaia_urls.h" +#include "google_apis/gaia/google_service_auth_error.h" +#include "net/base/escape.h" +#include "net/url_request/url_fetcher.h" +#include "net/url_request/url_request_context.h" + +namespace autofill { +namespace wallet { + +namespace { + +// Toolbar::GetAccountInfo API URL (JSON). +const char kGetAccountInfoUrlFormat[] = + "https://clients1.google.com/tbproxy/getaccountinfo?key=%d&rv=2"; + +} // namespace + +WalletSigninHelper::WalletSigninHelper( + WalletSigninHelperDelegate* delegate, + net::URLRequestContextGetter* getter) + : delegate_(delegate), + getter_(getter), + state_(IDLE) { + DCHECK(delegate_); +} + +WalletSigninHelper::~WalletSigninHelper() { +} + +void WalletSigninHelper::StartPassiveSignin() { + DCHECK_EQ(IDLE, state_); + DCHECK(!url_fetcher_); + DCHECK(!gaia_fetcher_); + + state_ = PASSIVE_EXECUTING_SIGNIN; + sid_.clear(); + lsid_.clear(); + username_.clear(); + const GURL& url = wallet::GetPassiveAuthUrl(); + url_fetcher_.reset(net::URLFetcher::Create( + 0, url, net::URLFetcher::GET, this)); + url_fetcher_->SetRequestContext(getter_); + url_fetcher_->Start(); +} + +void WalletSigninHelper::StartAutomaticSignin( + const std::string& sid, const std::string& lsid) { + DCHECK(!sid.empty()); + DCHECK(!lsid.empty()); + DCHECK_EQ(state_, IDLE); + DCHECK(!url_fetcher_); + DCHECK(!gaia_fetcher_); + + state_ = AUTOMATIC_FETCHING_USERINFO; + sid_ = sid; + lsid_ = lsid; + username_.clear(); + gaia_fetcher_.reset(new GaiaAuthFetcher( + this, GaiaConstants::kChromeSource, getter_)); + gaia_fetcher_->StartGetUserInfo(lsid_); +} + +void WalletSigninHelper::StartUserNameFetch() { + DCHECK_EQ(state_, IDLE); + DCHECK(!url_fetcher_); + DCHECK(!gaia_fetcher_); + + state_ = USERNAME_FETCHING_USERINFO; + sid_.clear(); + lsid_.clear(); + username_.clear(); + StartFetchingUserNameFromSession(); +} + +std::string WalletSigninHelper::GetGetAccountInfoUrlForTesting() const { + return base::StringPrintf(kGetAccountInfoUrlFormat, 0); +} + +void WalletSigninHelper::OnServiceError(const GoogleServiceAuthError& error) { + const State state_with_error = state_; + state_ = IDLE; + url_fetcher_.reset(); + gaia_fetcher_.reset(); + + switch(state_with_error) { + case IDLE: + NOTREACHED(); + break; + + case PASSIVE_EXECUTING_SIGNIN: /*FALLTHROUGH*/ + case PASSIVE_FETCHING_USERINFO: + delegate_->OnPassiveSigninFailure(error); + break; + + case AUTOMATIC_FETCHING_USERINFO: /*FALLTHROUGH*/ + case AUTOMATIC_ISSUING_AUTH_TOKEN: /*FALLTHROUGH*/ + case AUTOMATIC_EXECUTING_SIGNIN: + delegate_->OnAutomaticSigninFailure(error); + break; + + case USERNAME_FETCHING_USERINFO: + delegate_->OnUserNameFetchFailure(error); + break; + } +} + +void WalletSigninHelper::OnOtherError() { + OnServiceError(GoogleServiceAuthError::AuthErrorNone()); +} + +void WalletSigninHelper::OnGetUserInfoSuccess( + const UserInfoMap& data) { + DCHECK_EQ(AUTOMATIC_FETCHING_USERINFO, state_); + + UserInfoMap::const_iterator email_iter = + data.find(GaiaConstants::kClientOAuthEmailKey); + if (email_iter != data.end()) { + username_ = email_iter->second; + DCHECK(!url_fetcher_); + state_ = AUTOMATIC_ISSUING_AUTH_TOKEN; + gaia_fetcher_.reset(new GaiaAuthFetcher( + this, GaiaConstants::kChromeSource, getter_)); + gaia_fetcher_->StartIssueAuthToken( + sid_, lsid_, GaiaConstants::kGaiaService); + } else { + LOG(ERROR) << "GetUserInfoFailure: email field not found"; + OnOtherError(); + } +} + +void WalletSigninHelper::OnGetUserInfoFailure( + const GoogleServiceAuthError& error) { + LOG(ERROR) << "GetUserInfoFailure: " << error.ToString(); + DCHECK_EQ(AUTOMATIC_FETCHING_USERINFO, state_); + OnServiceError(error); +} + +void WalletSigninHelper::OnIssueAuthTokenSuccess( + const std::string& service, + const std::string& auth_token) { + DCHECK_EQ(AUTOMATIC_ISSUING_AUTH_TOKEN, state_); + + state_ = AUTOMATIC_EXECUTING_SIGNIN; + std::string encoded_auth_token = net::EscapeUrlEncodedData(auth_token, true); + std::string encoded_continue_url = + net::EscapeUrlEncodedData(wallet::GetPassiveAuthUrl().spec(), true); + std::string encoded_source = net::EscapeUrlEncodedData( + GaiaConstants::kChromeSource, true); + std::string body = base::StringPrintf( + "auth=%s&" + "continue=%s&" + "source=%s", + encoded_auth_token.c_str(), + encoded_continue_url.c_str(), + encoded_source.c_str()); + + gaia_fetcher_.reset(); + DCHECK(!url_fetcher_); + url_fetcher_.reset(net::URLFetcher::Create( + 0, + GURL(GaiaUrls::GetInstance()->token_auth_url()), + net::URLFetcher::POST, + this)); + url_fetcher_->SetUploadData("application/x-www-form-urlencoded", body); + url_fetcher_->SetRequestContext(getter_); + url_fetcher_->Start(); // This will result in OnURLFetchComplete callback. +} + +void WalletSigninHelper::OnIssueAuthTokenFailure( + const std::string& service, + const GoogleServiceAuthError& error) { + LOG(ERROR) << "IssueAuthTokenFailure: " << error.ToString(); + DCHECK_EQ(AUTOMATIC_ISSUING_AUTH_TOKEN, state_); + OnServiceError(error); +} + +void WalletSigninHelper::OnURLFetchComplete( + const net::URLFetcher* fetcher) { + DCHECK_EQ(url_fetcher_.get(), fetcher); + DCHECK(!gaia_fetcher_); + if (!fetcher->GetStatus().is_success() || + fetcher->GetResponseCode() < 200 || + fetcher->GetResponseCode() >= 300) { + LOG(ERROR) << "URLFetchFailure: state=" << state_ + << " r=" << fetcher->GetResponseCode() + << " s=" << fetcher->GetStatus().status() + << " e=" << fetcher->GetStatus().error(); + OnOtherError(); + return; + } + + switch (state_) { + case USERNAME_FETCHING_USERINFO: /*FALLTHROUGH*/ + case PASSIVE_FETCHING_USERINFO: + ProcessGetAccountInfoResponseAndFinish(); + break; + + case PASSIVE_EXECUTING_SIGNIN: + url_fetcher_.reset(); + state_ = PASSIVE_FETCHING_USERINFO; + StartFetchingUserNameFromSession(); + break; + + case AUTOMATIC_EXECUTING_SIGNIN: + state_ = IDLE; + url_fetcher_.reset(); + delegate_->OnAutomaticSigninSuccess(username_); + break; + + default: + NOTREACHED() << "unexpected state_=" << state_; + } +} + +void WalletSigninHelper::StartFetchingUserNameFromSession() { + DCHECK(!gaia_fetcher_); + const int random_number = static_cast(base::RandUint64() % INT_MAX); + url_fetcher_.reset( + net::URLFetcher::Create( + 0, + GURL(base::StringPrintf(kGetAccountInfoUrlFormat, random_number)), + net::URLFetcher::GET, + this)); + url_fetcher_->SetRequestContext(getter_); + url_fetcher_->Start(); // This will result in OnURLFetchComplete callback. +} + +void WalletSigninHelper::ProcessGetAccountInfoResponseAndFinish() { + std::string email; + if (!ParseGetAccountInfoResponse(url_fetcher_.get(), &email)) { + LOG(ERROR) << "failed to get the user email"; + OnOtherError(); + return; + } + + username_ = email; + const State finishing_state = state_; + state_ = IDLE; + url_fetcher_.reset(); + switch(finishing_state) { + case USERNAME_FETCHING_USERINFO: + delegate_->OnUserNameFetchSuccess(username_); + break; + + case PASSIVE_FETCHING_USERINFO: + delegate_->OnPassiveSigninSuccess(username_); + break; + + default: + NOTREACHED() << "unexpected state_=" << finishing_state; + } +} + +bool WalletSigninHelper::ParseGetAccountInfoResponse( + const net::URLFetcher* fetcher, std::string* email) { + DCHECK(email); + + std::string data; + if (!fetcher->GetResponseAsString(&data)) { + LOG(ERROR) << "failed to GetResponseAsString"; + return false; + } + + scoped_ptr value(base::JSONReader::Read(data)); + if (!value.get() || value->GetType() != base::Value::TYPE_DICTIONARY) { + LOG(ERROR) << "failed to parse JSON response"; + return false; + } + + DictionaryValue* dict = static_cast(value.get()); + if (!dict->GetStringWithoutPathExpansion("email", email)) { + LOG(ERROR) << "no email in JSON response"; + return false; + } + + return !email->empty(); +} + +} // namespace wallet +} // namespace autofill diff --git a/components/autofill/browser/wallet/wallet_signin_helper.h b/components/autofill/browser/wallet/wallet_signin_helper.h new file mode 100644 index 000000000000..dffbd7513ec4 --- /dev/null +++ b/components/autofill/browser/wallet/wallet_signin_helper.h @@ -0,0 +1,146 @@ +// Copyright 2013 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 COMPONENTS_AUTOFILL_BROWSER_WALLET_WALLET_SIGNIN_HELPER_H_ +#define COMPONENTS_AUTOFILL_BROWSER_WALLET_WALLET_SIGNIN_HELPER_H_ + +#include + +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "google_apis/gaia/gaia_auth_consumer.h" +#include "net/url_request/url_fetcher_delegate.h" + +namespace net { +class URLFetcher; +class URLRequestContextGetter; +class URLRequestStatus; +} + +class GaiaAuthFetcher; +class GoogleServiceAuthError; + +namespace autofill { +namespace wallet { + +class WalletSigninHelperDelegate; + +// Authenticates the user against the Online Wallet service. +// This class is not thread-safe. An instance may be used on any thread, but +// should not be accessed from multiple threads. +class WalletSigninHelper : public GaiaAuthConsumer, + public net::URLFetcherDelegate { + public: + // Constructs a helper that works with a given |delegate| and uses + // a given |getter| to obtain a context for URL and GAIA fetchers. + // Both |delegate| and |getter| shall remain valid over the entire + // lifetime of the created instance. + WalletSigninHelper(WalletSigninHelperDelegate* delegate, + net::URLRequestContextGetter* getter); + + virtual ~WalletSigninHelper(); + + // Initiates an attempt to passively sign the user into the Online Wallet. + // A passive sign-in is a non-interactive refresh of content area cookies, + // and it succeeds as long as the Online Wallet service could safely accept + // or refresh the existing area cookies, and the user doesn't need to be + // fully reauthenticated with the service. + // Either OnPassiveSigninSuccess or OnPassiveSigninFailure will be called + // on the original thread. + void StartPassiveSignin(); + + // Initiates an attempt to automatically sign in a user into the service + // given the SID and LSID tokens obtained from the platform GAIA service. + // Please refer to GaiaAuthFetcher documentation for more details. + // Either OnAutomaticSigninSuccess or OnAutomaticSigninFailure will be called + // on the original thread. + void StartAutomaticSignin(const std::string& sid, const std::string& lsid); + + // Initiates a fetch of the user name of a signed-in user. + // Either OnUserNameFetchSuccess or OnUserNameFetchFailure will + // be called on the original thread. + void StartUserNameFetch(); + + protected: + // Sign-in helper states (for tests). + enum State { + IDLE, + PASSIVE_EXECUTING_SIGNIN, + PASSIVE_FETCHING_USERINFO, + AUTOMATIC_FETCHING_USERINFO, + AUTOMATIC_ISSUING_AUTH_TOKEN, + AUTOMATIC_EXECUTING_SIGNIN, + USERNAME_FETCHING_USERINFO, + }; + + // (For tests) Current state of the sign-in helper. + State state() const { return state_; } + + // (For tests) URL used to fetch the currently signed-in user info. + std::string GetGetAccountInfoUrlForTesting() const; + + private: + // Called if a service authentication error occurs. + void OnServiceError(const GoogleServiceAuthError& error); + + // Called if any other error occurs. + void OnOtherError(); + + // GaiaAuthConsumer implementation. + virtual void OnGetUserInfoSuccess(const UserInfoMap& data) OVERRIDE; + virtual void OnGetUserInfoFailure( + const GoogleServiceAuthError& error) OVERRIDE; + virtual void OnIssueAuthTokenSuccess( + const std::string& service, + const std::string& auth_token) OVERRIDE; + virtual void OnIssueAuthTokenFailure( + const std::string& service, + const GoogleServiceAuthError& error) OVERRIDE; + + // URLFetcherDelegate implementation. + virtual void OnURLFetchComplete(const net::URLFetcher* fetcher) OVERRIDE; + + // Initiates fetching of the currently signed-in user information. + void StartFetchingUserNameFromSession(); + + // Processes the user information received from the server by url_fetcher_ + // and calls the delegate callbacks on success/failure. + void ProcessGetAccountInfoResponseAndFinish(); + + // Attempts to parse the GetAccountInfo response from the server. + // Returns true on success; the obtained email address is stored into |email|. + bool ParseGetAccountInfoResponse(const net::URLFetcher* fetcher, + std::string* email); + + // Should be valid throughout the lifetime of the instance. + WalletSigninHelperDelegate* const delegate_; + + // URLRequestContextGetter to be used for URLFetchers. + net::URLRequestContextGetter* const getter_; + + // While passive login/merge session URL fetches are going on: + scoped_ptr url_fetcher_; + + // While Gaia authentication/userinfo fetches are going on: + scoped_ptr gaia_fetcher_; + + // SID from StartAutomaticSignin(). + std::string sid_; + + // LSID from StartAutomaticSignin(). + std::string lsid_; + + // User account name (email) fetched from OnGetUserInfoSuccess(). + std::string username_; + + // Current internal state of the helper. + State state_; + + DISALLOW_COPY_AND_ASSIGN(WalletSigninHelper); +}; + +} // namespace wallet +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_BROWSER_WALLET_WALLET_SIGNIN_HELPER_H_ diff --git a/components/autofill/browser/wallet/wallet_signin_helper_delegate.h b/components/autofill/browser/wallet/wallet_signin_helper_delegate.h new file mode 100644 index 000000000000..47c5b66d7a66 --- /dev/null +++ b/components/autofill/browser/wallet/wallet_signin_helper_delegate.h @@ -0,0 +1,48 @@ +// Copyright 2013 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_UI_AUTOFILL_WALLET_SIGNIN_HELPER_DELEGATE_H_ +#define CHROME_BROWSER_UI_AUTOFILL_WALLET_SIGNIN_HELPER_DELEGATE_H_ + +#include + +class GoogleServiceAuthError; + +namespace autofill { +namespace wallet { + +// An interface that defines the callbacks for objects that +// WalletSigninHelper can return data to. +class WalletSigninHelperDelegate { + public: + virtual ~WalletSigninHelperDelegate() {} + + // Called on a successful passive sign-in. + // |username| is the signed-in user account name (email). + virtual void OnPassiveSigninSuccess(const std::string& username) = 0; + + // Called on a failed passive sign-in; |error| describes the error. + virtual void OnPassiveSigninFailure(const GoogleServiceAuthError& error) = 0; + + // Called on a successful automatic sign-in. + // |username| is the signed-in user account name (email). + virtual void OnAutomaticSigninSuccess(const std::string& username) = 0; + + // Called on a failed automatic sign-in; |error| describes the error. + virtual void OnAutomaticSigninFailure( + const GoogleServiceAuthError& error) = 0; + + // Called on a successful fetch of the signed-in account name. + // |username| is the signed-in user account name (email). + virtual void OnUserNameFetchSuccess(const std::string& username) = 0; + + // Called on a failed fetch of the signed-in account name. + // |error| described the error. + virtual void OnUserNameFetchFailure(const GoogleServiceAuthError& error) = 0; +}; + +} // namespace wallet +} // namespace autofill + +#endif // CHROME_BROWSER_UI_AUTOFILL_WALLET_SIGNIN_HELPER_DELEGATE_H_ diff --git a/components/autofill/browser/wallet/wallet_signin_helper_unittest.cc b/components/autofill/browser/wallet/wallet_signin_helper_unittest.cc new file mode 100644 index 000000000000..e574d55e2d77 --- /dev/null +++ b/components/autofill/browser/wallet/wallet_signin_helper_unittest.cc @@ -0,0 +1,250 @@ +// Copyright 2013 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 "components/autofill/browser/wallet/wallet_signin_helper.h" + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/stringprintf.h" +#include "chrome/test/base/testing_profile.h" +#include "components/autofill/browser/wallet/wallet_service_url.h" +#include "components/autofill/browser/wallet/wallet_signin_helper_delegate.h" +#include "content/public/test/test_browser_thread.h" +#include "google_apis/gaia/gaia_constants.h" +#include "google_apis/gaia/gaia_urls.h" +#include "google_apis/gaia/google_service_auth_error.h" +#include "net/url_request/test_url_fetcher_factory.h" +#include "net/url_request/url_request.h" +#include "net/url_request/url_request_context_getter.h" +#include "net/url_request/url_request_status.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using content::BrowserThread; +using testing::_; + +namespace autofill { +namespace wallet { + +namespace { + +const char kGetTokenPairValidResponse[] = + "{" + " \"refresh_token\": \"rt1\"," + " \"access_token\": \"at1\"," + " \"expires_in\": 3600," + " \"token_type\": \"Bearer\"" + "}"; + +const char kGetAccountInfoValidResponseFormat[] = + "{" + " \"email\": \"%s\"" + "}"; + +class MockWalletSigninHelperDelegate : public WalletSigninHelperDelegate { + public: + MOCK_METHOD1(OnPassiveSigninSuccess, void(const std::string& username)); + MOCK_METHOD1(OnAutomaticSigninSuccess, void(const std::string& username)); + MOCK_METHOD1(OnUserNameFetchSuccess, void(const std::string& username)); + MOCK_METHOD1(OnPassiveSigninFailure, + void(const GoogleServiceAuthError& error)); + MOCK_METHOD1(OnAutomaticSigninFailure, + void(const GoogleServiceAuthError& error)); + MOCK_METHOD1(OnUserNameFetchFailure, + void(const GoogleServiceAuthError& error)); +}; + +class WalletSigninHelperForTesting : public WalletSigninHelper { + public: + WalletSigninHelperForTesting(WalletSigninHelperDelegate* delegate, + net::URLRequestContextGetter* getter) + : WalletSigninHelper(delegate, getter) { + } + + // Bring in the test-only getters. + using WalletSigninHelper::GetGetAccountInfoUrlForTesting; + using WalletSigninHelper::state; + + // Bring in the State enum. + using WalletSigninHelper::State; + using WalletSigninHelper::IDLE; +}; + +} // namespace + +class WalletSigninHelperTest : public testing::Test { + public: + WalletSigninHelperTest() : io_thread_(content::BrowserThread::IO) {} + + virtual void SetUp() OVERRIDE { + io_thread_.StartIOThread(); + profile_.CreateRequestContext(); + signin_helper_.reset(new WalletSigninHelperForTesting( + &mock_delegate_, + profile_.GetRequestContext())); + EXPECT_EQ(WalletSigninHelperForTesting::IDLE, state()); + } + + virtual void TearDown() OVERRIDE { + signin_helper_.reset(); + profile_.ResetRequestContext(); + io_thread_.Stop(); + } + + protected: + // Sets up a response for the mock URLFetcher and completes the request. + void SetUpFetcherResponseAndCompleteRequest( + const std::string& url, + int response_code, + const net::ResponseCookies& cookies, + const std::string& response_string) { + net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0); + ASSERT_TRUE(fetcher); + ASSERT_TRUE(fetcher->delegate()); + + fetcher->set_url(GURL(url)); + fetcher->set_status(net::URLRequestStatus()); + fetcher->set_response_code(response_code); + fetcher->SetResponseString(response_string); + fetcher->set_cookies(cookies); + fetcher->delegate()->OnURLFetchComplete(fetcher); + } + + void MockSuccessfulOAuthLoginResponse() { + SetUpFetcherResponseAndCompleteRequest( + GaiaUrls::GetInstance()->client_login_url(), 200, + net::ResponseCookies(), + "SID=sid\nLSID=lsid\nAuth=auth"); + } + + void MockFailedOAuthLoginResponse404() { + SetUpFetcherResponseAndCompleteRequest( + GaiaUrls::GetInstance()->client_login_url(), 404, + net::ResponseCookies(), + ""); + } + + void MockSuccessfulGaiaUserInfoResponse(const std::string& username) { + SetUpFetcherResponseAndCompleteRequest( + GaiaUrls::GetInstance()->get_user_info_url(), 200, + net::ResponseCookies(), + "email=" + username); + } + + void MockFailedGaiaUserInfoResponse404() { + SetUpFetcherResponseAndCompleteRequest( + GaiaUrls::GetInstance()->get_user_info_url(), 404, + net::ResponseCookies(), + ""); + } + + void MockSuccessfulGetAccountInfoResponse(const std::string& username) { + SetUpFetcherResponseAndCompleteRequest( + signin_helper_->GetGetAccountInfoUrlForTesting(), 200, + net::ResponseCookies(), + base::StringPrintf( + kGetAccountInfoValidResponseFormat, + username.c_str())); + } + + void MockFailedGetAccountInfoResponse404() { + SetUpFetcherResponseAndCompleteRequest( + signin_helper_->GetGetAccountInfoUrlForTesting(), 404, + net::ResponseCookies(), + ""); + } + + void MockSuccessfulPassiveAuthUrlMergeAndRedirectResponse() { + SetUpFetcherResponseAndCompleteRequest( + wallet::GetPassiveAuthUrl().spec(), 200, + net::ResponseCookies(), + ""); + } + + void MockFailedPassiveAuthUrlMergeAndRedirectResponse404() { + SetUpFetcherResponseAndCompleteRequest( + wallet::GetPassiveAuthUrl().spec(), 404, + net::ResponseCookies(), + ""); + } + + WalletSigninHelperForTesting::State state() const { + return signin_helper_->state(); + } + + scoped_ptr signin_helper_; + MockWalletSigninHelperDelegate mock_delegate_; + + private: + // The profile's request context must be released on the IO thread. + content::TestBrowserThread io_thread_; + net::TestURLFetcherFactory factory_; + TestingProfile profile_; +}; + +TEST_F(WalletSigninHelperTest, PassiveSigninSuccessful) { + EXPECT_CALL(mock_delegate_, OnPassiveSigninSuccess("user@gmail.com")); + signin_helper_->StartPassiveSignin(); + MockSuccessfulPassiveAuthUrlMergeAndRedirectResponse(); + MockSuccessfulGetAccountInfoResponse("user@gmail.com"); +} + +TEST_F(WalletSigninHelperTest, PassiveSigninFailedSignin) { + EXPECT_CALL(mock_delegate_, OnPassiveSigninFailure(_)); + signin_helper_->StartPassiveSignin(); + MockFailedPassiveAuthUrlMergeAndRedirectResponse404(); +} + +TEST_F(WalletSigninHelperTest, PassiveSigninFailedUserInfo) { + EXPECT_CALL(mock_delegate_, OnPassiveSigninFailure(_)); + signin_helper_->StartPassiveSignin(); + MockSuccessfulPassiveAuthUrlMergeAndRedirectResponse(); + MockFailedGetAccountInfoResponse404(); +} + +TEST_F(WalletSigninHelperTest, PassiveUserInfoSuccessful) { + EXPECT_CALL(mock_delegate_, OnUserNameFetchSuccess("user@gmail.com")); + signin_helper_->StartUserNameFetch(); + MockSuccessfulGetAccountInfoResponse("user@gmail.com"); +} + +TEST_F(WalletSigninHelperTest, PassiveUserInfoFailedUserInfo) { + EXPECT_CALL(mock_delegate_, OnUserNameFetchFailure(_)); + signin_helper_->StartUserNameFetch(); + MockFailedGetAccountInfoResponse404(); +} + +TEST_F(WalletSigninHelperTest, AutomaticSigninSuccessful) { + EXPECT_CALL(mock_delegate_, OnAutomaticSigninSuccess("user@gmail.com")); + signin_helper_->StartAutomaticSignin("123SID", "123LSID"); + MockSuccessfulGaiaUserInfoResponse("user@gmail.com"); + MockSuccessfulOAuthLoginResponse(); + MockSuccessfulPassiveAuthUrlMergeAndRedirectResponse(); +} + +TEST_F(WalletSigninHelperTest, AutomaticSigninFailedGetUserInfo) { + EXPECT_CALL(mock_delegate_, OnAutomaticSigninFailure(_)); + signin_helper_->StartAutomaticSignin("123SID", "123LSID"); + MockFailedGaiaUserInfoResponse404(); +} + +TEST_F(WalletSigninHelperTest, AutomaticSigninFailedOAuthLogin) { + EXPECT_CALL(mock_delegate_, OnAutomaticSigninFailure(_)); + signin_helper_->StartAutomaticSignin("123SID", "123LSID"); + MockSuccessfulGaiaUserInfoResponse("user@gmail.com"); + MockFailedOAuthLoginResponse404(); +} + +TEST_F(WalletSigninHelperTest, AutomaticSigninFailedSignin) { + EXPECT_CALL(mock_delegate_, OnAutomaticSigninFailure(_)); + signin_helper_->StartAutomaticSignin("123SID", "123LSID"); + MockSuccessfulGaiaUserInfoResponse("user@gmail.com"); + MockSuccessfulOAuthLoginResponse(); + MockFailedPassiveAuthUrlMergeAndRedirectResponse404(); +} + +// TODO(aruslan): http://crbug.com/188317 Need more tests. + +} // namespace wallet +} // namespace autofill -- 2.11.4.GIT