1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/signin/signin_manager.h"
8 #include "base/bind_helpers.h"
9 #include "base/compiler_specific.h"
10 #include "base/prefs/pref_service.h"
11 #include "base/prefs/testing_pref_service.h"
12 #include "base/run_loop.h"
13 #include "base/strings/stringprintf.h"
14 #include "chrome/browser/browser_process.h"
15 #include "chrome/browser/chrome_notification_types.h"
16 #include "chrome/browser/prefs/browser_prefs.h"
17 #include "chrome/browser/signin/chrome_signin_manager_delegate.h"
18 #include "chrome/browser/signin/fake_profile_oauth2_token_service.h"
19 #include "chrome/browser/signin/fake_profile_oauth2_token_service_wrapper.h"
20 #include "chrome/browser/signin/profile_oauth2_token_service.h"
21 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
22 #include "chrome/browser/signin/signin_manager_factory.h"
23 #include "chrome/common/pref_names.h"
24 #include "chrome/common/url_constants.h"
25 #include "chrome/test/base/testing_browser_process.h"
26 #include "chrome/test/base/testing_profile.h"
27 #include "components/webdata/encryptor/encryptor.h"
28 #include "content/public/browser/child_process_security_policy.h"
29 #include "content/public/browser/notification_source.h"
30 #include "content/public/test/test_browser_thread_bundle.h"
31 #include "google_apis/gaia/gaia_constants.h"
32 #include "google_apis/gaia/gaia_urls.h"
33 #include "net/cookies/cookie_monster.h"
34 #include "net/url_request/test_url_fetcher_factory.h"
35 #include "net/url_request/url_request.h"
36 #include "net/url_request/url_request_context_getter.h"
37 #include "net/url_request/url_request_status.h"
39 #include "testing/gmock/include/gmock/gmock.h"
40 #include "testing/gtest/include/gtest/gtest.h"
44 const char kGetTokenPairValidResponse[] =
46 " \"refresh_token\": \"rt1\","
47 " \"access_token\": \"at1\","
48 " \"expires_in\": 3600,"
49 " \"token_type\": \"Bearer\""
52 const char kUberAuthTokenURLFormat[] = "?source=%s&issueuberauth=1";
54 BrowserContextKeyedService* SigninManagerBuild(
55 content::BrowserContext* context) {
56 SigninManager* service = NULL;
57 Profile* profile = static_cast<Profile*>(context);
58 service = new SigninManager(
59 scoped_ptr<SigninManagerDelegate>(
60 new ChromeSigninManagerDelegate(profile)));
61 service->Initialize(profile, NULL);
65 class TestSigninManagerObserver : public SigninManagerBase::Observer {
67 TestSigninManagerObserver() : num_failed_signins_(0),
68 num_successful_signins_(0),
72 virtual ~TestSigninManagerObserver() {}
74 int num_failed_signins_;
75 int num_successful_signins_;
79 // SigninManagerBase::Observer:
80 virtual void GoogleSigninFailed(
81 const GoogleServiceAuthError& error) OVERRIDE {
82 num_failed_signins_++;
85 virtual void GoogleSigninSucceeded(
86 const std::string& username, const std::string& password) OVERRIDE {
87 num_successful_signins_++;
90 virtual void GoogleSignedOut(const std::string& username) OVERRIDE {
98 class SigninManagerTest : public testing::Test {
100 SigninManagerTest() : manager_(NULL) {}
101 virtual ~SigninManagerTest() {}
103 virtual void SetUp() OVERRIDE {
105 prefs_.reset(new TestingPrefServiceSimple);
106 chrome::RegisterLocalState(prefs_->registry());
107 TestingBrowserProcess::GetGlobal()->SetLocalState(
109 TestingProfile::Builder builder;
110 builder.AddTestingFactory(ProfileOAuth2TokenServiceFactory::GetInstance(),
111 FakeProfileOAuth2TokenServiceWrapper::Build);
112 profile_ = builder.Build();
115 virtual void TearDown() OVERRIDE {
117 manager_->RemoveObserver(&test_observer_);
119 // Destroy the SigninManager here, because it relies on profile() which is
120 // freed in the base class.
121 if (naked_manager_) {
122 naked_manager_->Shutdown();
123 naked_manager_.reset(NULL);
125 TestingBrowserProcess::GetGlobal()->SetLocalState(NULL);
127 // Manually destroy PrefService and Profile so that they are shutdown
128 // in the correct order. Both need to be destroyed before the
129 // |thread_bundle_| member.
131 prefs_.reset(); // LocalState needs to outlive the profile.
134 TestingProfile* profile() { return profile_.get(); }
136 // Create a signin manager as a service if other code will try to get it as
138 void CreateSigninManagerAsService() {
140 DCHECK(!naked_manager_);
141 manager_ = static_cast<SigninManager*>(
142 SigninManagerFactory::GetInstance()->SetTestingFactoryAndUse(
143 profile(), SigninManagerBuild));
144 manager_->AddObserver(&test_observer_);
147 // Create a naked signin manager if integration with PKSs is not needed.
148 void CreateNakedSigninManager() {
150 naked_manager_.reset(new SigninManager(
151 scoped_ptr<SigninManagerDelegate>(
152 new ChromeSigninManagerDelegate(profile()))));
154 manager_ = naked_manager_.get();
155 manager_->AddObserver(&test_observer_);
158 // Shuts down |manager_|.
159 void ShutDownManager() {
161 manager_->RemoveObserver(&test_observer_);
162 manager_->Shutdown();
164 naked_manager_.reset(NULL);
168 void SetupFetcherAndComplete(const GURL& url,
170 const net::ResponseCookies& cookies,
171 const std::string& response_string) {
172 net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
174 DCHECK(fetcher->delegate());
176 cookies_.insert(cookies_.end(), cookies.begin(), cookies.end());
177 fetcher->set_url(url);
178 fetcher->set_status(net::URLRequestStatus());
179 fetcher->set_response_code(response_code);
180 fetcher->SetResponseString(response_string);
181 fetcher->set_cookies(cookies);
182 fetcher->delegate()->OnURLFetchComplete(fetcher);
185 void SimulateValidResponseSignInWithCredentials() {
186 // Simulate the correct StartOAuthLoginTokenFetch response. This involves
187 // two separate fetches.
188 SetupFetcherAndComplete(
189 GaiaUrls::GetInstance()->client_login_to_oauth2_url(), 200,
190 net::ResponseCookies(), kGetTokenPairValidResponse);
192 SetupFetcherAndComplete(GaiaUrls::GetInstance()->oauth2_token_url(), 200,
193 net::ResponseCookies(), kGetTokenPairValidResponse);
195 // Simulate the correct StartOAuthLogin response.
196 SetupFetcherAndComplete(GaiaUrls::GetInstance()->oauth1_login_url(), 200,
197 net::ResponseCookies(),
198 "SID=sid\nLSID=lsid\nAuth=auth_token");
200 SimulateValidResponseGetClientInfo(false);
203 void SimulateValidResponseClientLogin(bool isGPlusUser) {
204 SetupFetcherAndComplete(GaiaUrls::GetInstance()->client_login_url(), 200,
205 net::ResponseCookies(),
206 "SID=sid\nLSID=lsid\nAuth=auth");
207 SimulateValidResponseGetClientInfo(isGPlusUser);
210 void SimulateValidResponseGetClientInfo(bool isGPlusUser) {
211 // Simulate the correct ClientLogin response.
212 std::string response_string = isGPlusUser ?
213 "email=user@gmail.com\ndisplayEmail=USER@gmail.com\n"
214 "allServices=googleme" :
215 "email=user@gmail.com\ndisplayEmail=USER@gmail.com\n"
217 SetupFetcherAndComplete(GaiaUrls::GetInstance()->get_user_info_url(), 200,
218 net::ResponseCookies(), response_string);
221 void SimulateValidUberToken() {
222 SetupFetcherAndComplete(GaiaUrls::GetInstance()->oauth2_token_url(), 200,
223 net::ResponseCookies(), kGetTokenPairValidResponse);
224 const GURL uberauth_token_gurl =
225 GaiaUrls::GetInstance()->oauth1_login_url().Resolve(
226 base::StringPrintf(kUberAuthTokenURLFormat, "source"));
227 SetupFetcherAndComplete(uberauth_token_gurl, 200,
228 net::ResponseCookies(), "ut1");
230 net::ResponseCookies cookies;
231 cookies.push_back("checkCookie = true");
232 SetupFetcherAndComplete(GaiaUrls::GetInstance()->merge_session_url(), 200,
233 cookies, "<html></html>");
236 void ExpectSignInWithCredentialsSuccess() {
237 EXPECT_TRUE(manager_->GetAuthenticatedUsername().empty());
239 SimulateValidResponseSignInWithCredentials();
241 EXPECT_FALSE(manager_->GetAuthenticatedUsername().empty());
243 ProfileOAuth2TokenService* token_service =
244 ProfileOAuth2TokenServiceFactory::GetForProfile(profile());
245 EXPECT_TRUE(token_service->RefreshTokenIsAvailable(
246 manager_->GetAuthenticatedUsername()));
248 // Should go into token service and stop.
249 EXPECT_EQ(1, test_observer_.num_successful_signins_);
250 EXPECT_EQ(0, test_observer_.num_failed_signins_);
253 // Helper method that wraps the logic when signin with credentials
254 // should fail. If |requestSent| is true, then simulate valid resopnse.
255 // Otherwise the sign-in is aborted before any request is sent, thus no need
256 // to simulatate response.
257 void ExpectSignInWithCredentialsFail(bool requestSent) {
258 EXPECT_TRUE(manager_->GetAuthenticatedUsername().empty());
261 SimulateValidResponseSignInWithCredentials();
263 ProfileOAuth2TokenService* token_service =
264 ProfileOAuth2TokenServiceFactory::GetForProfile(profile());
265 EXPECT_FALSE(token_service->RefreshTokenIsAvailable(
266 manager_->GetAuthenticatedUsername()));
268 // Should go into token service and stop.
269 EXPECT_EQ(0, test_observer_.num_successful_signins_);
270 EXPECT_EQ(1, test_observer_.num_failed_signins_);
273 void CompleteSigninCallback(const std::string& oauth_token) {
274 oauth_tokens_fetched_.push_back(oauth_token);
275 manager_->CompletePendingSignin();
278 void CancelSigninCallback(const std::string& oauth_token) {
279 oauth_tokens_fetched_.push_back(oauth_token);
283 content::TestBrowserThreadBundle thread_bundle_;
284 net::TestURLFetcherFactory factory_;
285 scoped_ptr<SigninManager> naked_manager_;
286 SigninManager* manager_;
287 TestSigninManagerObserver test_observer_;
288 scoped_ptr<TestingProfile> profile_;
289 std::vector<std::string> oauth_tokens_fetched_;
290 scoped_ptr<TestingPrefServiceSimple> prefs_;
291 std::vector<std::string> cookies_;
294 TEST_F(SigninManagerTest, SignInWithCredentials) {
295 CreateSigninManagerAsService();
296 EXPECT_TRUE(manager_->GetAuthenticatedUsername().empty());
298 manager_->StartSignInWithCredentials(
302 SigninManager::OAuthTokenFetchedCallback());
304 ExpectSignInWithCredentialsSuccess();
306 // Should persist across resets.
308 CreateNakedSigninManager();
309 manager_->Initialize(profile(), NULL);
310 EXPECT_EQ("user@gmail.com", manager_->GetAuthenticatedUsername());
313 TEST_F(SigninManagerTest, SignInWithCredentialsNonCanonicalEmail) {
314 CreateSigninManagerAsService();
315 EXPECT_TRUE(manager_->GetAuthenticatedUsername().empty());
317 manager_->StartSignInWithCredentials(
321 SigninManager::OAuthTokenFetchedCallback());
323 ExpectSignInWithCredentialsSuccess();
326 TEST_F(SigninManagerTest, SignInWithCredentialsWrongEmail) {
327 CreateSigninManagerAsService();
328 EXPECT_TRUE(manager_->GetAuthenticatedUsername().empty());
330 // If the email address used to start the sign in does not match the
331 // email address returned by /GetUserInfo, the sign in should fail.
332 manager_->StartSignInWithCredentials(
336 SigninManager::OAuthTokenFetchedCallback());
338 ExpectSignInWithCredentialsFail(true /* requestSent */);
341 TEST_F(SigninManagerTest, SignInWithCredentialsEmptyPasswordValidCookie) {
342 CreateSigninManagerAsService();
343 EXPECT_TRUE(manager_->GetAuthenticatedUsername().empty());
345 // Set a valid LSID cookie in the test cookie store.
346 scoped_refptr<net::CookieMonster> cookie_monster =
347 profile()->GetCookieMonster();
348 net::CookieOptions options;
349 options.set_include_httponly();
350 cookie_monster->SetCookieWithOptionsAsync(
351 GURL("https://accounts.google.com"),
352 "LSID=1234; secure; httponly", options,
353 net::CookieMonster::SetCookiesCallback());
355 // Since the password is empty, will verify the gaia cookies first.
356 manager_->StartSignInWithCredentials(
360 SigninManager::OAuthTokenFetchedCallback());
362 base::RunLoop().RunUntilIdle();
364 // Verification should succeed and continue with auto signin.
365 ExpectSignInWithCredentialsSuccess();
368 TEST_F(SigninManagerTest, SignInWithCredentialsEmptyPasswordNoValidCookie) {
369 CreateSigninManagerAsService();
370 EXPECT_TRUE(manager_->GetAuthenticatedUsername().empty());
372 // Since the password is empty, will verify the gaia cookies first.
373 manager_->StartSignInWithCredentials(
377 SigninManager::OAuthTokenFetchedCallback());
379 base::RunLoop().RunUntilIdle();
381 // Since the test cookie store is empty, verification should fail and throws
383 ExpectSignInWithCredentialsFail(false /* requestSent */);
386 TEST_F(SigninManagerTest, SignInWithCredentialsEmptyPasswordInValidCookie) {
387 CreateSigninManagerAsService();
388 EXPECT_TRUE(manager_->GetAuthenticatedUsername().empty());
390 // Set an invalid LSID cookie in the test cookie store.
391 scoped_refptr<net::CookieMonster> cookie_monster =
392 profile()->GetCookieMonster();
393 net::CookieOptions options;
394 options.set_include_httponly();
395 cookie_monster->SetCookieWithOptionsAsync(
396 GURL("https://accounts.google.com"),
397 "LSID=1234; domain=google.com; secure; httponly", options,
398 net::CookieMonster::SetCookiesCallback());
400 // Since the password is empty, must verify the gaia cookies first.
401 manager_->StartSignInWithCredentials(
405 SigninManager::OAuthTokenFetchedCallback());
407 base::RunLoop().RunUntilIdle();
409 // Since the LSID cookie is invalid, verification should fail and throws
411 ExpectSignInWithCredentialsFail(false /* requestSent */);
414 TEST_F(SigninManagerTest, SignInWithCredentialsCallbackComplete) {
415 CreateSigninManagerAsService();
416 EXPECT_TRUE(manager_->GetAuthenticatedUsername().empty());
418 // Since the password is empty, must verify the gaia cookies first.
419 SigninManager::OAuthTokenFetchedCallback callback =
420 base::Bind(&SigninManagerTest::CompleteSigninCallback,
421 base::Unretained(this));
422 manager_->StartSignInWithCredentials(
428 ExpectSignInWithCredentialsSuccess();
429 ASSERT_EQ(1U, oauth_tokens_fetched_.size());
430 EXPECT_EQ(oauth_tokens_fetched_[0], "rt1");
433 TEST_F(SigninManagerTest, SignInWithCredentialsCallbackCancel) {
434 CreateSigninManagerAsService();
435 EXPECT_TRUE(manager_->GetAuthenticatedUsername().empty());
437 // Since the password is empty, must verify the gaia cookies first.
438 SigninManager::OAuthTokenFetchedCallback callback =
439 base::Bind(&SigninManagerTest::CancelSigninCallback,
440 base::Unretained(this));
441 manager_->StartSignInWithCredentials(
447 // Signin should fail since it would be cancelled by the callback.
448 ExpectSignInWithCredentialsFail(true);
449 ASSERT_EQ(1U, oauth_tokens_fetched_.size());
450 EXPECT_EQ(oauth_tokens_fetched_[0], "rt1");
453 TEST_F(SigninManagerTest, SignOut) {
454 CreateSigninManagerAsService();
455 SigninManager::OAuthTokenFetchedCallback dummy;
456 manager_->StartSignInWithCredentials("0", "user@gmail.com", "password",
458 ExpectSignInWithCredentialsSuccess();
461 EXPECT_TRUE(manager_->GetAuthenticatedUsername().empty());
462 // Should not be persisted anymore
464 CreateNakedSigninManager();
465 manager_->Initialize(profile(), NULL);
466 EXPECT_TRUE(manager_->GetAuthenticatedUsername().empty());
469 TEST_F(SigninManagerTest, SignOutMidConnect) {
470 CreateSigninManagerAsService();
471 SigninManager::OAuthTokenFetchedCallback dummy;
472 manager_->StartSignInWithCredentials("0", "user@gmail.com", "password",
476 EXPECT_EQ(0, test_observer_.num_successful_signins_);
477 EXPECT_EQ(1, test_observer_.num_failed_signins_);
479 EXPECT_TRUE(manager_->GetAuthenticatedUsername().empty());
480 EXPECT_TRUE(manager_->GetUsernameForAuthInProgress().empty());
483 TEST_F(SigninManagerTest, SignOutWhileProhibited) {
484 CreateSigninManagerAsService();
485 EXPECT_TRUE(manager_->GetAuthenticatedUsername().empty());
487 manager_->SetAuthenticatedUsername("user@gmail.com");
488 manager_->ProhibitSignout(true);
490 EXPECT_FALSE(manager_->GetAuthenticatedUsername().empty());
491 manager_->ProhibitSignout(false);
493 EXPECT_TRUE(manager_->GetAuthenticatedUsername().empty());
496 TEST_F(SigninManagerTest, TestIsWebBasedSigninFlowURL) {
497 EXPECT_FALSE(SigninManager::IsWebBasedSigninFlowURL(
498 GURL("http://www.google.com")));
499 EXPECT_TRUE(SigninManager::IsWebBasedSigninFlowURL(
500 GURL("https://accounts.google.com/ServiceLogin?service=chromiumsync")));
501 EXPECT_FALSE(SigninManager::IsWebBasedSigninFlowURL(
502 GURL("http://accounts.google.com/ServiceLogin?service=chromiumsync")));
503 // http, not https, should not be treated as web based signin.
504 EXPECT_FALSE(SigninManager::IsWebBasedSigninFlowURL(
505 GURL("http://accounts.google.com/ServiceLogin?service=googlemail")));
506 // chromiumsync is double-embedded in a continue query param.
507 EXPECT_TRUE(SigninManager::IsWebBasedSigninFlowURL(
508 GURL("https://accounts.google.com/CheckCookie?"
509 "continue=https%3A%2F%2Fwww.google.com%2Fintl%2Fen-US%2Fchrome"
510 "%2Fblank.html%3Fsource%3D3%26nonadv%3D1&service=chromiumsync")));
513 TEST_F(SigninManagerTest, Prohibited) {
514 g_browser_process->local_state()->SetString(
515 prefs::kGoogleServicesUsernamePattern, ".*@google.com");
516 CreateNakedSigninManager();
517 manager_->Initialize(profile(), g_browser_process->local_state());
518 EXPECT_TRUE(manager_->IsAllowedUsername("test@google.com"));
519 EXPECT_TRUE(manager_->IsAllowedUsername("happy@google.com"));
520 EXPECT_FALSE(manager_->IsAllowedUsername("test@invalid.com"));
521 EXPECT_FALSE(manager_->IsAllowedUsername("test@notgoogle.com"));
522 EXPECT_FALSE(manager_->IsAllowedUsername(std::string()));
525 TEST_F(SigninManagerTest, TestAlternateWildcard) {
526 // Test to make sure we accept "*@google.com" as a pattern (treat it as if
527 // the admin entered ".*@google.com").
528 g_browser_process->local_state()->SetString(
529 prefs::kGoogleServicesUsernamePattern, "*@google.com");
530 CreateNakedSigninManager();
531 manager_->Initialize(profile(), g_browser_process->local_state());
532 EXPECT_TRUE(manager_->IsAllowedUsername("test@google.com"));
533 EXPECT_TRUE(manager_->IsAllowedUsername("happy@google.com"));
534 EXPECT_FALSE(manager_->IsAllowedUsername("test@invalid.com"));
535 EXPECT_FALSE(manager_->IsAllowedUsername("test@notgoogle.com"));
536 EXPECT_FALSE(manager_->IsAllowedUsername(std::string()));
539 TEST_F(SigninManagerTest, ProhibitedAtStartup) {
540 profile()->GetPrefs()->SetString(prefs::kGoogleServicesUsername,
541 "monkey@invalid.com");
542 g_browser_process->local_state()->SetString(
543 prefs::kGoogleServicesUsernamePattern, ".*@google.com");
544 CreateNakedSigninManager();
545 manager_->Initialize(profile(), g_browser_process->local_state());
546 // Currently signed in user is prohibited by policy, so should be signed out.
547 EXPECT_EQ("", manager_->GetAuthenticatedUsername());
550 TEST_F(SigninManagerTest, ProhibitedAfterStartup) {
551 std::string user("monkey@invalid.com");
552 profile()->GetPrefs()->SetString(prefs::kGoogleServicesUsername, user);
553 CreateNakedSigninManager();
554 manager_->Initialize(profile(), g_browser_process->local_state());
555 EXPECT_EQ(user, manager_->GetAuthenticatedUsername());
556 // Update the profile - user should be signed out.
557 g_browser_process->local_state()->SetString(
558 prefs::kGoogleServicesUsernamePattern, ".*@google.com");
559 EXPECT_EQ("", manager_->GetAuthenticatedUsername());
562 TEST_F(SigninManagerTest, ExternalSignIn) {
563 CreateNakedSigninManager();
564 manager_->Initialize(profile(), g_browser_process->local_state());
566 profile()->GetPrefs()->GetString(prefs::kGoogleServicesUsername));
567 EXPECT_EQ("", manager_->GetAuthenticatedUsername());
568 EXPECT_EQ(0, test_observer_.num_successful_signins_);
570 manager_->OnExternalSigninCompleted("external@example.com");
571 EXPECT_EQ(1, test_observer_.num_successful_signins_);
572 EXPECT_EQ(0, test_observer_.num_failed_signins_);
573 EXPECT_EQ("external@example.com",
574 profile()->GetPrefs()->GetString(prefs::kGoogleServicesUsername));
575 EXPECT_EQ("external@example.com", manager_->GetAuthenticatedUsername());
578 TEST_F(SigninManagerTest, SigninNotAllowed) {
579 std::string user("user@google.com");
580 profile()->GetPrefs()->SetString(prefs::kGoogleServicesUsername, user);
581 profile()->GetPrefs()->SetBoolean(prefs::kSigninAllowed, false);
582 CreateSigninManagerAsService();