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.
4
5#include "components/signin/core/browser/signin_manager.h"
6
7#include <vector>
8
9#include "base/bind.h"
10#include "base/bind_helpers.h"
11#include "base/compiler_specific.h"
12#include "base/prefs/pref_service.h"
13#include "base/prefs/testing_pref_service.h"
14#include "base/run_loop.h"
15#include "base/strings/stringprintf.h"
16#include "chrome/browser/browser_process.h"
17#include "chrome/browser/chrome_notification_types.h"
18#include "chrome/browser/prefs/browser_prefs.h"
19#include "chrome/browser/signin/chrome_signin_client_factory.h"
20#include "chrome/browser/signin/fake_profile_oauth2_token_service.h"
21#include "chrome/browser/signin/fake_profile_oauth2_token_service_builder.h"
22#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
23#include "chrome/browser/signin/signin_manager_factory.h"
24#include "chrome/browser/signin/test_signin_client_builder.h"
25#include "chrome/common/pref_names.h"
26#include "chrome/common/url_constants.h"
27#include "chrome/test/base/testing_browser_process.h"
28#include "chrome/test/base/testing_profile.h"
29#include "components/signin/core/browser/profile_oauth2_token_service.h"
30#include "components/signin/core/browser/test_signin_client.h"
31#include "content/public/browser/child_process_security_policy.h"
32#include "content/public/browser/notification_source.h"
33#include "content/public/test/test_browser_thread_bundle.h"
34#include "google_apis/gaia/gaia_constants.h"
35#include "google_apis/gaia/gaia_urls.h"
36#include "net/cookies/cookie_monster.h"
37#include "net/url_request/test_url_fetcher_factory.h"
38#include "net/url_request/url_request.h"
39#include "net/url_request/url_request_context_getter.h"
40#include "net/url_request/url_request_status.h"
41
42#include "testing/gmock/include/gmock/gmock.h"
43#include "testing/gtest/include/gtest/gtest.h"
44
45namespace {
46
47KeyedService* SigninManagerBuild(content::BrowserContext* context) {
48  SigninManager* service = NULL;
49  Profile* profile = static_cast<Profile*>(context);
50  service = new SigninManager(
51      ChromeSigninClientFactory::GetInstance()->GetForProfile(profile),
52      ProfileOAuth2TokenServiceFactory::GetForProfile(profile));
53  service->Initialize(NULL);
54  return service;
55}
56
57class TestSigninManagerObserver : public SigninManagerBase::Observer {
58 public:
59  TestSigninManagerObserver() : num_failed_signins_(0),
60                                num_successful_signins_(0),
61                                num_signouts_(0) {
62  }
63
64  virtual ~TestSigninManagerObserver() {}
65
66  int num_failed_signins_;
67  int num_successful_signins_;
68  int num_signouts_;
69
70 private:
71  // SigninManagerBase::Observer:
72  virtual void GoogleSigninFailed(
73      const GoogleServiceAuthError& error) OVERRIDE {
74    num_failed_signins_++;
75  }
76
77  virtual void GoogleSigninSucceeded(
78      const std::string& account_id,
79      const std::string& username,
80      const std::string& password) OVERRIDE {
81    num_successful_signins_++;
82  }
83
84  virtual void GoogleSignedOut(const std::string& account_id,
85                               const std::string& username) OVERRIDE {
86    num_signouts_++;
87  }
88};
89
90}  // namespace
91
92
93class SigninManagerTest : public testing::Test {
94 public:
95  SigninManagerTest() : manager_(NULL) {}
96  virtual ~SigninManagerTest() {}
97
98  virtual void SetUp() OVERRIDE {
99    manager_ = NULL;
100    prefs_.reset(new TestingPrefServiceSimple);
101    chrome::RegisterLocalState(prefs_->registry());
102    TestingBrowserProcess::GetGlobal()->SetLocalState(
103        prefs_.get());
104    TestingProfile::Builder builder;
105    builder.AddTestingFactory(ProfileOAuth2TokenServiceFactory::GetInstance(),
106                              BuildFakeProfileOAuth2TokenService);
107    builder.AddTestingFactory(ChromeSigninClientFactory::GetInstance(),
108                              signin::BuildTestSigninClient);
109    builder.AddTestingFactory(SigninManagerFactory::GetInstance(),
110                              SigninManagerBuild);
111    profile_ = builder.Build();
112
113    static_cast<TestSigninClient*>(
114        ChromeSigninClientFactory::GetInstance()->GetForProfile(profile()))->
115            SetURLRequestContext(profile_->GetRequestContext());
116  }
117
118  virtual void TearDown() OVERRIDE {
119    if (manager_)
120      manager_->RemoveObserver(&test_observer_);
121
122    // Destroy the SigninManager here, because it relies on profile() which is
123    // freed in the base class.
124    if (naked_manager_) {
125      naked_manager_->Shutdown();
126      naked_manager_.reset(NULL);
127    }
128    TestingBrowserProcess::GetGlobal()->SetLocalState(NULL);
129
130    // Manually destroy PrefService and Profile so that they are shutdown
131    // in the correct order.  Both need to be destroyed before the
132    // |thread_bundle_| member.
133    profile_.reset();
134    prefs_.reset();  // LocalState needs to outlive the profile.
135  }
136
137  TestingProfile* profile() { return profile_.get(); }
138
139  // Sets up the signin manager as a service if other code will try to get it as
140  // a PKS.
141  void SetUpSigninManagerAsService() {
142    DCHECK(!manager_);
143    DCHECK(!naked_manager_);
144    manager_ = static_cast<SigninManager*>(
145        SigninManagerFactory::GetForProfile(profile()));
146    manager_->AddObserver(&test_observer_);
147  }
148
149  // Create a naked signin manager if integration with PKSs is not needed.
150  void CreateNakedSigninManager() {
151    DCHECK(!manager_);
152    naked_manager_.reset(new SigninManager(
153        ChromeSigninClientFactory::GetInstance()->GetForProfile(profile()),
154        ProfileOAuth2TokenServiceFactory::GetForProfile(profile())));
155
156    manager_ = naked_manager_.get();
157    manager_->AddObserver(&test_observer_);
158  }
159
160  // Shuts down |manager_|.
161  void ShutDownManager() {
162    DCHECK(manager_);
163    manager_->RemoveObserver(&test_observer_);
164    manager_->Shutdown();
165    if (naked_manager_)
166      naked_manager_.reset(NULL);
167    manager_ = NULL;
168  }
169
170  void ExpectSignInWithRefreshTokenSuccess() {
171    EXPECT_TRUE(manager_->IsAuthenticated());
172
173    ProfileOAuth2TokenService* token_service =
174        ProfileOAuth2TokenServiceFactory::GetForProfile(profile());
175    EXPECT_TRUE(token_service->RefreshTokenIsAvailable(
176        manager_->GetAuthenticatedUsername()));
177
178    // Should go into token service and stop.
179    EXPECT_EQ(1, test_observer_.num_successful_signins_);
180    EXPECT_EQ(0, test_observer_.num_failed_signins_);
181  }
182
183  void CompleteSigninCallback(const std::string& oauth_token) {
184    oauth_tokens_fetched_.push_back(oauth_token);
185    manager_->CompletePendingSignin();
186  }
187
188  content::TestBrowserThreadBundle thread_bundle_;
189  net::TestURLFetcherFactory factory_;
190  scoped_ptr<SigninManager> naked_manager_;
191  SigninManager* manager_;
192  TestSigninManagerObserver test_observer_;
193  scoped_ptr<TestingProfile> profile_;
194  std::vector<std::string> oauth_tokens_fetched_;
195  scoped_ptr<TestingPrefServiceSimple> prefs_;
196  std::vector<std::string> cookies_;
197};
198
199TEST_F(SigninManagerTest, SignInWithRefreshToken) {
200  SetUpSigninManagerAsService();
201  EXPECT_FALSE(manager_->IsAuthenticated());
202
203  manager_->StartSignInWithRefreshToken(
204      "rt1",
205      "user@gmail.com",
206      "password",
207      SigninManager::OAuthTokenFetchedCallback());
208
209  ExpectSignInWithRefreshTokenSuccess();
210
211  // Should persist across resets.
212  ShutDownManager();
213  CreateNakedSigninManager();
214  manager_->Initialize(NULL);
215  EXPECT_EQ("user@gmail.com", manager_->GetAuthenticatedUsername());
216}
217
218TEST_F(SigninManagerTest, SignInWithRefreshTokenCallbackComplete) {
219  SetUpSigninManagerAsService();
220  EXPECT_FALSE(manager_->IsAuthenticated());
221
222  // Since the password is empty, must verify the gaia cookies first.
223  SigninManager::OAuthTokenFetchedCallback callback =
224      base::Bind(&SigninManagerTest::CompleteSigninCallback,
225                 base::Unretained(this));
226  manager_->StartSignInWithRefreshToken(
227      "rt1",
228      "user@gmail.com",
229      "password",
230      callback);
231
232  ExpectSignInWithRefreshTokenSuccess();
233  ASSERT_EQ(1U, oauth_tokens_fetched_.size());
234  EXPECT_EQ(oauth_tokens_fetched_[0], "rt1");
235}
236
237TEST_F(SigninManagerTest, SignOut) {
238  SetUpSigninManagerAsService();
239  manager_->StartSignInWithRefreshToken(
240      "rt1",
241      "user@gmail.com",
242      "password",
243      SigninManager::OAuthTokenFetchedCallback());
244  manager_->SignOut(signin_metrics::SIGNOUT_TEST);
245  EXPECT_FALSE(manager_->IsAuthenticated());
246  // Should not be persisted anymore
247  ShutDownManager();
248  CreateNakedSigninManager();
249  manager_->Initialize(NULL);
250  EXPECT_FALSE(manager_->IsAuthenticated());
251}
252
253TEST_F(SigninManagerTest, SignOutWhileProhibited) {
254  SetUpSigninManagerAsService();
255  EXPECT_FALSE(manager_->IsAuthenticated());
256
257  manager_->SetAuthenticatedUsername("user@gmail.com");
258  manager_->ProhibitSignout(true);
259  manager_->SignOut(signin_metrics::SIGNOUT_TEST);
260  EXPECT_TRUE(manager_->IsAuthenticated());
261  manager_->ProhibitSignout(false);
262  manager_->SignOut(signin_metrics::SIGNOUT_TEST);
263  EXPECT_FALSE(manager_->IsAuthenticated());
264}
265
266TEST_F(SigninManagerTest, TestIsWebBasedSigninFlowURL) {
267  EXPECT_FALSE(SigninManager::IsWebBasedSigninFlowURL(
268      GURL("http://www.google.com")));
269  EXPECT_TRUE(SigninManager::IsWebBasedSigninFlowURL(
270      GURL("https://accounts.google.com/ServiceLogin?service=chromiumsync")));
271  EXPECT_FALSE(SigninManager::IsWebBasedSigninFlowURL(
272      GURL("http://accounts.google.com/ServiceLogin?service=chromiumsync")));
273  // http, not https, should not be treated as web based signin.
274  EXPECT_FALSE(SigninManager::IsWebBasedSigninFlowURL(
275      GURL("http://accounts.google.com/ServiceLogin?service=googlemail")));
276  // chromiumsync is double-embedded in a continue query param.
277  EXPECT_TRUE(SigninManager::IsWebBasedSigninFlowURL(
278      GURL("https://accounts.google.com/CheckCookie?"
279           "continue=https%3A%2F%2Fwww.google.com%2Fintl%2Fen-US%2Fchrome"
280           "%2Fblank.html%3Fsource%3D3%26nonadv%3D1&service=chromiumsync")));
281}
282
283TEST_F(SigninManagerTest, Prohibited) {
284  g_browser_process->local_state()->SetString(
285      prefs::kGoogleServicesUsernamePattern, ".*@google.com");
286  CreateNakedSigninManager();
287  manager_->Initialize(g_browser_process->local_state());
288  EXPECT_TRUE(manager_->IsAllowedUsername("test@google.com"));
289  EXPECT_TRUE(manager_->IsAllowedUsername("happy@google.com"));
290  EXPECT_FALSE(manager_->IsAllowedUsername("test@invalid.com"));
291  EXPECT_FALSE(manager_->IsAllowedUsername("test@notgoogle.com"));
292  EXPECT_FALSE(manager_->IsAllowedUsername(std::string()));
293}
294
295TEST_F(SigninManagerTest, TestAlternateWildcard) {
296  // Test to make sure we accept "*@google.com" as a pattern (treat it as if
297  // the admin entered ".*@google.com").
298  g_browser_process->local_state()->SetString(
299      prefs::kGoogleServicesUsernamePattern, "*@google.com");
300  CreateNakedSigninManager();
301  manager_->Initialize(g_browser_process->local_state());
302  EXPECT_TRUE(manager_->IsAllowedUsername("test@google.com"));
303  EXPECT_TRUE(manager_->IsAllowedUsername("happy@google.com"));
304  EXPECT_FALSE(manager_->IsAllowedUsername("test@invalid.com"));
305  EXPECT_FALSE(manager_->IsAllowedUsername("test@notgoogle.com"));
306  EXPECT_FALSE(manager_->IsAllowedUsername(std::string()));
307}
308
309TEST_F(SigninManagerTest, ProhibitedAtStartup) {
310  profile()->GetPrefs()->SetString(prefs::kGoogleServicesUsername,
311                                   "monkey@invalid.com");
312  g_browser_process->local_state()->SetString(
313      prefs::kGoogleServicesUsernamePattern, ".*@google.com");
314  CreateNakedSigninManager();
315  manager_->Initialize(g_browser_process->local_state());
316  // Currently signed in user is prohibited by policy, so should be signed out.
317  EXPECT_EQ("", manager_->GetAuthenticatedUsername());
318}
319
320TEST_F(SigninManagerTest, ProhibitedAfterStartup) {
321  std::string user("monkey@invalid.com");
322  profile()->GetPrefs()->SetString(prefs::kGoogleServicesUsername, user);
323  CreateNakedSigninManager();
324  manager_->Initialize(g_browser_process->local_state());
325  EXPECT_EQ(user, manager_->GetAuthenticatedUsername());
326  // Update the profile - user should be signed out.
327  g_browser_process->local_state()->SetString(
328      prefs::kGoogleServicesUsernamePattern, ".*@google.com");
329  EXPECT_EQ("", manager_->GetAuthenticatedUsername());
330}
331
332TEST_F(SigninManagerTest, ExternalSignIn) {
333  CreateNakedSigninManager();
334  manager_->Initialize(g_browser_process->local_state());
335  EXPECT_EQ("",
336            profile()->GetPrefs()->GetString(prefs::kGoogleServicesUsername));
337  EXPECT_EQ("", manager_->GetAuthenticatedUsername());
338  EXPECT_EQ(0, test_observer_.num_successful_signins_);
339
340  manager_->OnExternalSigninCompleted("external@example.com");
341  EXPECT_EQ(1, test_observer_.num_successful_signins_);
342  EXPECT_EQ(0, test_observer_.num_failed_signins_);
343  EXPECT_EQ("external@example.com",
344            profile()->GetPrefs()->GetString(prefs::kGoogleServicesUsername));
345  EXPECT_EQ("external@example.com", manager_->GetAuthenticatedUsername());
346}
347
348TEST_F(SigninManagerTest, SigninNotAllowed) {
349  std::string user("user@google.com");
350  profile()->GetPrefs()->SetString(prefs::kGoogleServicesUsername, user);
351  profile()->GetPrefs()->SetBoolean(prefs::kSigninAllowed, false);
352  SetUpSigninManagerAsService();
353}
354