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/profile_oauth2_token_service.h"
20 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
21 #include "chrome/browser/signin/signin_manager_factory.h"
22 #include "chrome/common/pref_names.h"
23 #include "chrome/common/url_constants.h"
24 #include "chrome/test/base/testing_browser_process.h"
25 #include "chrome/test/base/testing_profile.h"
26 #include "components/webdata/encryptor/encryptor.h"
27 #include "content/public/browser/child_process_security_policy.h"
28 #include "content/public/browser/notification_source.h"
29 #include "content/public/test/test_browser_thread_bundle.h"
30 #include "content/public/test/test_notification_tracker.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
);
68 class SigninManagerTest
: public testing::Test
{
70 SigninManagerTest() : manager_(NULL
) {}
71 virtual ~SigninManagerTest() {}
73 virtual void SetUp() OVERRIDE
{
75 prefs_
.reset(new TestingPrefServiceSimple
);
76 chrome::RegisterLocalState(prefs_
->registry());
77 TestingBrowserProcess::GetGlobal()->SetLocalState(
79 TestingProfile::Builder builder
;
80 builder
.AddTestingFactory(ProfileOAuth2TokenServiceFactory::GetInstance(),
81 FakeProfileOAuth2TokenService::Build
);
82 profile_
= builder
.Build();
83 google_login_success_
.ListenFor(
84 chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL
,
85 content::Source
<Profile
>(profile()));
86 google_login_failure_
.ListenFor(chrome::NOTIFICATION_GOOGLE_SIGNIN_FAILED
,
87 content::Source
<Profile
>(profile()));
90 virtual void TearDown() OVERRIDE
{
91 // Destroy the SigninManager here, because it relies on profile() which is
92 // freed in the base class.
94 naked_manager_
->Shutdown();
95 naked_manager_
.reset(NULL
);
97 TestingBrowserProcess::GetGlobal()->SetLocalState(NULL
);
99 // Manually destroy PrefService and Profile so that they are shutdown
100 // in the correct order. Both need to be destroyed before the
101 // |thread_bundle_| member.
103 prefs_
.reset(); // LocalState needs to outlive the profile.
106 TestingProfile
* profile() { return profile_
.get(); }
108 // Create a signin manager as a service if other code will try to get it as
110 void CreateSigninManagerAsService() {
112 DCHECK(!naked_manager_
);
113 manager_
= static_cast<SigninManager
*>(
114 SigninManagerFactory::GetInstance()->SetTestingFactoryAndUse(
115 profile(), SigninManagerBuild
));
118 // Create a naked signin manager if integration with PKSs is not needed.
119 void CreateNakedSigninManager() {
121 naked_manager_
.reset(new SigninManager(
122 scoped_ptr
<SigninManagerDelegate
>(
123 new ChromeSigninManagerDelegate(profile()))));
125 manager_
= naked_manager_
.get();
128 void SetupFetcherAndComplete(const GURL
& url
,
130 const net::ResponseCookies
& cookies
,
131 const std::string
& response_string
) {
132 net::TestURLFetcher
* fetcher
= factory_
.GetFetcherByID(0);
134 DCHECK(fetcher
->delegate());
136 cookies_
.insert(cookies_
.end(), cookies
.begin(), cookies
.end());
137 fetcher
->set_url(url
);
138 fetcher
->set_status(net::URLRequestStatus());
139 fetcher
->set_response_code(response_code
);
140 fetcher
->SetResponseString(response_string
);
141 fetcher
->set_cookies(cookies
);
142 fetcher
->delegate()->OnURLFetchComplete(fetcher
);
145 void SimulateValidResponseSignInWithCredentials() {
146 // Simulate the correct StartOAuthLoginTokenFetch response. This involves
147 // two separate fetches.
148 SetupFetcherAndComplete(
149 GaiaUrls::GetInstance()->client_login_to_oauth2_url(), 200,
150 net::ResponseCookies(), kGetTokenPairValidResponse
);
152 SetupFetcherAndComplete(GaiaUrls::GetInstance()->oauth2_token_url(), 200,
153 net::ResponseCookies(), kGetTokenPairValidResponse
);
155 // Simulate the correct StartOAuthLogin response.
156 SetupFetcherAndComplete(GaiaUrls::GetInstance()->oauth1_login_url(), 200,
157 net::ResponseCookies(),
158 "SID=sid\nLSID=lsid\nAuth=auth_token");
160 SimulateValidResponseGetClientInfo(false);
163 void SimulateValidResponseClientLogin(bool isGPlusUser
) {
164 SetupFetcherAndComplete(GaiaUrls::GetInstance()->client_login_url(), 200,
165 net::ResponseCookies(),
166 "SID=sid\nLSID=lsid\nAuth=auth");
167 SimulateValidResponseGetClientInfo(isGPlusUser
);
170 void SimulateValidResponseGetClientInfo(bool isGPlusUser
) {
171 // Simulate the correct ClientLogin response.
172 std::string response_string
= isGPlusUser
?
173 "email=user@gmail.com\ndisplayEmail=USER@gmail.com\n"
174 "allServices=googleme" :
175 "email=user@gmail.com\ndisplayEmail=USER@gmail.com\n"
177 SetupFetcherAndComplete(GaiaUrls::GetInstance()->get_user_info_url(), 200,
178 net::ResponseCookies(), response_string
);
181 void SimulateValidUberToken() {
182 SetupFetcherAndComplete(GaiaUrls::GetInstance()->oauth2_token_url(), 200,
183 net::ResponseCookies(), kGetTokenPairValidResponse
);
184 const GURL uberauth_token_gurl
=
185 GaiaUrls::GetInstance()->oauth1_login_url().Resolve(
186 base::StringPrintf(kUberAuthTokenURLFormat
, "source"));
187 SetupFetcherAndComplete(uberauth_token_gurl
, 200,
188 net::ResponseCookies(), "ut1");
190 net::ResponseCookies cookies
;
191 cookies
.push_back("checkCookie = true");
192 SetupFetcherAndComplete(GaiaUrls::GetInstance()->merge_session_url(), 200,
193 cookies
, "<html></html>");
196 void ExpectSignInWithCredentialsSuccess() {
197 EXPECT_TRUE(manager_
->GetAuthenticatedUsername().empty());
199 SimulateValidResponseSignInWithCredentials();
201 EXPECT_FALSE(manager_
->GetAuthenticatedUsername().empty());
203 ProfileOAuth2TokenService
* token_service
=
204 ProfileOAuth2TokenServiceFactory::GetForProfile(profile());
205 EXPECT_TRUE(token_service
->RefreshTokenIsAvailable(
206 manager_
->GetAuthenticatedUsername()));
208 // Should go into token service and stop.
209 EXPECT_EQ(1U, google_login_success_
.size());
210 EXPECT_EQ(0U, google_login_failure_
.size());
213 // Helper method that wraps the logic when signin with credentials
214 // should fail. If |requestSent| is true, then simulate valid resopnse.
215 // Otherwise the sign-in is aborted before any request is sent, thus no need
216 // to simulatate response.
217 void ExpectSignInWithCredentialsFail(bool requestSent
) {
218 EXPECT_TRUE(manager_
->GetAuthenticatedUsername().empty());
221 SimulateValidResponseSignInWithCredentials();
223 ProfileOAuth2TokenService
* token_service
=
224 ProfileOAuth2TokenServiceFactory::GetForProfile(profile());
225 EXPECT_FALSE(token_service
->RefreshTokenIsAvailable(
226 manager_
->GetAuthenticatedUsername()));
228 // Should go into token service and stop.
229 EXPECT_EQ(0U, google_login_success_
.size());
230 EXPECT_EQ(1U, google_login_failure_
.size());
233 void CompleteSigninCallback(const std::string
& oauth_token
) {
234 oauth_tokens_fetched_
.push_back(oauth_token
);
235 manager_
->CompletePendingSignin();
238 void CancelSigninCallback(const std::string
& oauth_token
) {
239 oauth_tokens_fetched_
.push_back(oauth_token
);
243 content::TestBrowserThreadBundle thread_bundle_
;
244 net::TestURLFetcherFactory factory_
;
245 scoped_ptr
<SigninManager
> naked_manager_
;
246 SigninManager
* manager_
;
247 scoped_ptr
<TestingProfile
> profile_
;
248 content::TestNotificationTracker google_login_success_
;
249 content::TestNotificationTracker google_login_failure_
;
250 std::vector
<std::string
> oauth_tokens_fetched_
;
251 scoped_ptr
<TestingPrefServiceSimple
> prefs_
;
252 std::vector
<std::string
> cookies_
;
255 TEST_F(SigninManagerTest
, SignInWithCredentials
) {
256 CreateSigninManagerAsService();
257 EXPECT_TRUE(manager_
->GetAuthenticatedUsername().empty());
259 manager_
->StartSignInWithCredentials(
263 SigninManager::OAuthTokenFetchedCallback());
265 ExpectSignInWithCredentialsSuccess();
267 // Should persist across resets.
268 manager_
->Shutdown();
270 CreateNakedSigninManager();
271 manager_
->Initialize(profile(), NULL
);
272 EXPECT_EQ("user@gmail.com", manager_
->GetAuthenticatedUsername());
275 TEST_F(SigninManagerTest
, SignInWithCredentialsNonCanonicalEmail
) {
276 CreateSigninManagerAsService();
277 EXPECT_TRUE(manager_
->GetAuthenticatedUsername().empty());
279 manager_
->StartSignInWithCredentials(
283 SigninManager::OAuthTokenFetchedCallback());
285 ExpectSignInWithCredentialsSuccess();
288 TEST_F(SigninManagerTest
, SignInWithCredentialsWrongEmail
) {
289 CreateSigninManagerAsService();
290 EXPECT_TRUE(manager_
->GetAuthenticatedUsername().empty());
292 // If the email address used to start the sign in does not match the
293 // email address returned by /GetUserInfo, the sign in should fail.
294 manager_
->StartSignInWithCredentials(
298 SigninManager::OAuthTokenFetchedCallback());
300 ExpectSignInWithCredentialsFail(true /* requestSent */);
303 TEST_F(SigninManagerTest
, SignInWithCredentialsEmptyPasswordValidCookie
) {
304 CreateSigninManagerAsService();
305 EXPECT_TRUE(manager_
->GetAuthenticatedUsername().empty());
307 // Set a valid LSID cookie in the test cookie store.
308 scoped_refptr
<net::CookieMonster
> cookie_monster
=
309 profile()->GetCookieMonster();
310 net::CookieOptions options
;
311 options
.set_include_httponly();
312 cookie_monster
->SetCookieWithOptionsAsync(
313 GURL("https://accounts.google.com"),
314 "LSID=1234; secure; httponly", options
,
315 net::CookieMonster::SetCookiesCallback());
317 // Since the password is empty, will verify the gaia cookies first.
318 manager_
->StartSignInWithCredentials(
322 SigninManager::OAuthTokenFetchedCallback());
324 base::RunLoop().RunUntilIdle();
326 // Verification should succeed and continue with auto signin.
327 ExpectSignInWithCredentialsSuccess();
330 TEST_F(SigninManagerTest
, SignInWithCredentialsEmptyPasswordNoValidCookie
) {
331 CreateSigninManagerAsService();
332 EXPECT_TRUE(manager_
->GetAuthenticatedUsername().empty());
334 // Since the password is empty, will verify the gaia cookies first.
335 manager_
->StartSignInWithCredentials(
339 SigninManager::OAuthTokenFetchedCallback());
341 base::RunLoop().RunUntilIdle();
343 // Since the test cookie store is empty, verification should fail and throws
345 ExpectSignInWithCredentialsFail(false /* requestSent */);
348 TEST_F(SigninManagerTest
, SignInWithCredentialsEmptyPasswordInValidCookie
) {
349 CreateSigninManagerAsService();
350 EXPECT_TRUE(manager_
->GetAuthenticatedUsername().empty());
352 // Set an invalid LSID cookie in the test cookie store.
353 scoped_refptr
<net::CookieMonster
> cookie_monster
=
354 profile()->GetCookieMonster();
355 net::CookieOptions options
;
356 options
.set_include_httponly();
357 cookie_monster
->SetCookieWithOptionsAsync(
358 GURL("https://accounts.google.com"),
359 "LSID=1234; domain=google.com; secure; httponly", options
,
360 net::CookieMonster::SetCookiesCallback());
362 // Since the password is empty, must verify the gaia cookies first.
363 manager_
->StartSignInWithCredentials(
367 SigninManager::OAuthTokenFetchedCallback());
369 base::RunLoop().RunUntilIdle();
371 // Since the LSID cookie is invalid, verification should fail and throws
373 ExpectSignInWithCredentialsFail(false /* requestSent */);
376 TEST_F(SigninManagerTest
, SignInWithCredentialsCallbackComplete
) {
377 CreateSigninManagerAsService();
378 EXPECT_TRUE(manager_
->GetAuthenticatedUsername().empty());
380 // Since the password is empty, must verify the gaia cookies first.
381 SigninManager::OAuthTokenFetchedCallback callback
=
382 base::Bind(&SigninManagerTest::CompleteSigninCallback
,
383 base::Unretained(this));
384 manager_
->StartSignInWithCredentials(
390 ExpectSignInWithCredentialsSuccess();
391 ASSERT_EQ(1U, oauth_tokens_fetched_
.size());
392 EXPECT_EQ(oauth_tokens_fetched_
[0], "rt1");
395 TEST_F(SigninManagerTest
, SignInWithCredentialsCallbackCancel
) {
396 CreateSigninManagerAsService();
397 EXPECT_TRUE(manager_
->GetAuthenticatedUsername().empty());
399 // Since the password is empty, must verify the gaia cookies first.
400 SigninManager::OAuthTokenFetchedCallback callback
=
401 base::Bind(&SigninManagerTest::CancelSigninCallback
,
402 base::Unretained(this));
403 manager_
->StartSignInWithCredentials(
409 // Signin should fail since it would be cancelled by the callback.
410 ExpectSignInWithCredentialsFail(true);
411 ASSERT_EQ(1U, oauth_tokens_fetched_
.size());
412 EXPECT_EQ(oauth_tokens_fetched_
[0], "rt1");
415 TEST_F(SigninManagerTest
, SignOut
) {
416 CreateSigninManagerAsService();
417 SigninManager::OAuthTokenFetchedCallback dummy
;
418 manager_
->StartSignInWithCredentials("0", "user@gmail.com", "password",
420 ExpectSignInWithCredentialsSuccess();
423 EXPECT_TRUE(manager_
->GetAuthenticatedUsername().empty());
424 // Should not be persisted anymore
425 manager_
->Shutdown();
427 CreateNakedSigninManager();
428 manager_
->Initialize(profile(), NULL
);
429 EXPECT_TRUE(manager_
->GetAuthenticatedUsername().empty());
432 TEST_F(SigninManagerTest
, SignOutMidConnect
) {
433 CreateSigninManagerAsService();
434 SigninManager::OAuthTokenFetchedCallback dummy
;
435 manager_
->StartSignInWithCredentials("0", "user@gmail.com", "password",
439 EXPECT_EQ(0U, google_login_success_
.size());
440 EXPECT_EQ(1U, google_login_failure_
.size());
442 EXPECT_TRUE(manager_
->GetAuthenticatedUsername().empty());
443 EXPECT_TRUE(manager_
->GetUsernameForAuthInProgress().empty());
446 TEST_F(SigninManagerTest
, SignOutWhileProhibited
) {
447 CreateSigninManagerAsService();
448 EXPECT_TRUE(manager_
->GetAuthenticatedUsername().empty());
450 manager_
->SetAuthenticatedUsername("user@gmail.com");
451 manager_
->ProhibitSignout(true);
453 EXPECT_FALSE(manager_
->GetAuthenticatedUsername().empty());
454 manager_
->ProhibitSignout(false);
456 EXPECT_TRUE(manager_
->GetAuthenticatedUsername().empty());
459 TEST_F(SigninManagerTest
, TestIsWebBasedSigninFlowURL
) {
460 EXPECT_FALSE(SigninManager::IsWebBasedSigninFlowURL(
461 GURL("http://www.google.com")));
462 EXPECT_TRUE(SigninManager::IsWebBasedSigninFlowURL(
463 GURL("https://accounts.google.com/ServiceLogin?service=chromiumsync")));
464 EXPECT_FALSE(SigninManager::IsWebBasedSigninFlowURL(
465 GURL("http://accounts.google.com/ServiceLogin?service=chromiumsync")));
466 // http, not https, should not be treated as web based signin.
467 EXPECT_FALSE(SigninManager::IsWebBasedSigninFlowURL(
468 GURL("http://accounts.google.com/ServiceLogin?service=googlemail")));
469 // chromiumsync is double-embedded in a continue query param.
470 EXPECT_TRUE(SigninManager::IsWebBasedSigninFlowURL(
471 GURL("https://accounts.google.com/CheckCookie?"
472 "continue=https%3A%2F%2Fwww.google.com%2Fintl%2Fen-US%2Fchrome"
473 "%2Fblank.html%3Fsource%3D3%26nonadv%3D1&service=chromiumsync")));
476 TEST_F(SigninManagerTest
, Prohibited
) {
477 g_browser_process
->local_state()->SetString(
478 prefs::kGoogleServicesUsernamePattern
, ".*@google.com");
479 CreateNakedSigninManager();
480 manager_
->Initialize(profile(), g_browser_process
->local_state());
481 EXPECT_TRUE(manager_
->IsAllowedUsername("test@google.com"));
482 EXPECT_TRUE(manager_
->IsAllowedUsername("happy@google.com"));
483 EXPECT_FALSE(manager_
->IsAllowedUsername("test@invalid.com"));
484 EXPECT_FALSE(manager_
->IsAllowedUsername("test@notgoogle.com"));
485 EXPECT_FALSE(manager_
->IsAllowedUsername(std::string()));
488 TEST_F(SigninManagerTest
, TestAlternateWildcard
) {
489 // Test to make sure we accept "*@google.com" as a pattern (treat it as if
490 // the admin entered ".*@google.com").
491 g_browser_process
->local_state()->SetString(
492 prefs::kGoogleServicesUsernamePattern
, "*@google.com");
493 CreateNakedSigninManager();
494 manager_
->Initialize(profile(), g_browser_process
->local_state());
495 EXPECT_TRUE(manager_
->IsAllowedUsername("test@google.com"));
496 EXPECT_TRUE(manager_
->IsAllowedUsername("happy@google.com"));
497 EXPECT_FALSE(manager_
->IsAllowedUsername("test@invalid.com"));
498 EXPECT_FALSE(manager_
->IsAllowedUsername("test@notgoogle.com"));
499 EXPECT_FALSE(manager_
->IsAllowedUsername(std::string()));
502 TEST_F(SigninManagerTest
, ProhibitedAtStartup
) {
503 profile()->GetPrefs()->SetString(prefs::kGoogleServicesUsername
,
504 "monkey@invalid.com");
505 g_browser_process
->local_state()->SetString(
506 prefs::kGoogleServicesUsernamePattern
, ".*@google.com");
507 CreateNakedSigninManager();
508 manager_
->Initialize(profile(), g_browser_process
->local_state());
509 // Currently signed in user is prohibited by policy, so should be signed out.
510 EXPECT_EQ("", manager_
->GetAuthenticatedUsername());
513 TEST_F(SigninManagerTest
, ProhibitedAfterStartup
) {
514 std::string
user("monkey@invalid.com");
515 profile()->GetPrefs()->SetString(prefs::kGoogleServicesUsername
, user
);
516 CreateNakedSigninManager();
517 manager_
->Initialize(profile(), g_browser_process
->local_state());
518 EXPECT_EQ(user
, manager_
->GetAuthenticatedUsername());
519 // Update the profile - user should be signed out.
520 g_browser_process
->local_state()->SetString(
521 prefs::kGoogleServicesUsernamePattern
, ".*@google.com");
522 EXPECT_EQ("", manager_
->GetAuthenticatedUsername());
525 TEST_F(SigninManagerTest
, ExternalSignIn
) {
526 CreateNakedSigninManager();
527 manager_
->Initialize(profile(), g_browser_process
->local_state());
529 profile()->GetPrefs()->GetString(prefs::kGoogleServicesUsername
));
530 EXPECT_EQ("", manager_
->GetAuthenticatedUsername());
531 EXPECT_EQ(0u, google_login_success_
.size());
533 manager_
->OnExternalSigninCompleted("external@example.com");
534 EXPECT_EQ(1u, google_login_success_
.size());
535 EXPECT_EQ(0u, google_login_failure_
.size());
536 EXPECT_EQ("external@example.com",
537 profile()->GetPrefs()->GetString(prefs::kGoogleServicesUsername
));
538 EXPECT_EQ("external@example.com", manager_
->GetAuthenticatedUsername());
541 TEST_F(SigninManagerTest
, SigninNotAllowed
) {
542 std::string
user("user@google.com");
543 profile()->GetPrefs()->SetString(prefs::kGoogleServicesUsername
, user
);
544 profile()->GetPrefs()->SetBoolean(prefs::kSigninAllowed
, false);
545 CreateSigninManagerAsService();