1// Copyright 2014 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/mutable_profile_oauth2_token_service.h"
6
7#include "base/run_loop.h"
8#include "components/signin/core/browser/profile_oauth2_token_service.h"
9#include "components/signin/core/browser/signin_error_controller.h"
10#include "components/signin/core/browser/test_signin_client.h"
11#include "components/signin/core/browser/webdata/token_web_data.h"
12#include "google_apis/gaia/gaia_constants.h"
13#include "google_apis/gaia/gaia_urls.h"
14#include "google_apis/gaia/oauth2_token_service_test_util.h"
15#include "net/http/http_status_code.h"
16#include "net/url_request/test_url_fetcher_factory.h"
17#include "testing/gtest/include/gtest/gtest.h"
18
19#if defined(OS_MACOSX)
20#include "components/os_crypt/os_crypt.h"
21#endif
22
23// Defining constant here to handle backward compatiblity tests, but this
24// constant is no longer used in current versions of chrome.
25static const char kLSOService[] = "lso";
26static const char kEmail[] = "user@gmail.com";
27
28class MutableProfileOAuth2TokenServiceTest
29    : public testing::Test,
30      public OAuth2TokenService::Observer {
31 public:
32  MutableProfileOAuth2TokenServiceTest()
33      : factory_(NULL),
34        token_available_count_(0),
35        token_revoked_count_(0),
36        tokens_loaded_count_(0),
37        start_batch_changes_(0),
38        end_batch_changes_(0) {}
39
40  virtual void SetUp() OVERRIDE {
41#if defined(OS_MACOSX)
42    OSCrypt::UseMockKeychain(true);
43#endif
44
45    factory_.SetFakeResponse(GaiaUrls::GetInstance()->oauth2_revoke_url(),
46                             "",
47                             net::HTTP_OK,
48                             net::URLRequestStatus::SUCCESS);
49    oauth2_service_.Initialize(&client_);
50    // Make sure PO2TS has a chance to load itself before continuing.
51    base::RunLoop().RunUntilIdle();
52    oauth2_service_.AddObserver(this);
53  }
54
55  virtual void TearDown() OVERRIDE {
56    oauth2_service_.RemoveObserver(this);
57    oauth2_service_.Shutdown();
58  }
59
60  void AddAuthTokenManually(const std::string& service,
61                            const std::string& value) {
62    scoped_refptr<TokenWebData> token_web_data = client_.GetDatabase();
63    if (token_web_data.get())
64      token_web_data->SetTokenForService(service, value);
65  }
66
67  // OAuth2TokenService::Observer implementation.
68  virtual void OnRefreshTokenAvailable(const std::string& account_id) OVERRIDE {
69    ++token_available_count_;
70  }
71  virtual void OnRefreshTokenRevoked(const std::string& account_id) OVERRIDE {
72    ++token_revoked_count_;
73  }
74  virtual void OnRefreshTokensLoaded() OVERRIDE { ++tokens_loaded_count_; }
75
76  virtual void OnStartBatchChanges() OVERRIDE {
77    ++start_batch_changes_;
78  }
79
80  virtual void OnEndBatchChanges() OVERRIDE {
81    ++end_batch_changes_;
82  }
83
84  void ResetObserverCounts() {
85    token_available_count_ = 0;
86    token_revoked_count_ = 0;
87    tokens_loaded_count_ = 0;
88    start_batch_changes_ = 0;
89    end_batch_changes_ = 0;
90  }
91
92  void ExpectNoNotifications() {
93    EXPECT_EQ(0, token_available_count_);
94    EXPECT_EQ(0, token_revoked_count_);
95    EXPECT_EQ(0, tokens_loaded_count_);
96    ResetObserverCounts();
97  }
98
99  void ExpectOneTokenAvailableNotification() {
100    EXPECT_EQ(1, token_available_count_);
101    EXPECT_EQ(0, token_revoked_count_);
102    EXPECT_EQ(0, tokens_loaded_count_);
103    ResetObserverCounts();
104  }
105
106  void ExpectOneTokenRevokedNotification() {
107    EXPECT_EQ(0, token_available_count_);
108    EXPECT_EQ(1, token_revoked_count_);
109    EXPECT_EQ(0, tokens_loaded_count_);
110    ResetObserverCounts();
111  }
112
113  void ExpectOneTokensLoadedNotification() {
114    EXPECT_EQ(0, token_available_count_);
115    EXPECT_EQ(0, token_revoked_count_);
116    EXPECT_EQ(1, tokens_loaded_count_);
117    ResetObserverCounts();
118  }
119
120 protected:
121  base::MessageLoop message_loop_;
122  net::FakeURLFetcherFactory factory_;
123  TestSigninClient client_;
124  MutableProfileOAuth2TokenService oauth2_service_;
125  TestingOAuth2TokenServiceConsumer consumer_;
126  int token_available_count_;
127  int token_revoked_count_;
128  int tokens_loaded_count_;
129  int start_batch_changes_;
130  int end_batch_changes_;
131};
132
133TEST_F(MutableProfileOAuth2TokenServiceTest, PersistenceDBUpgrade) {
134  std::string main_account_id(kEmail);
135  std::string main_refresh_token("old_refresh_token");
136
137  // Populate DB with legacy tokens.
138  AddAuthTokenManually(GaiaConstants::kSyncService, "syncServiceToken");
139  AddAuthTokenManually(kLSOService, "lsoToken");
140  AddAuthTokenManually(GaiaConstants::kGaiaOAuth2LoginRefreshToken,
141                       main_refresh_token);
142
143  // Force LoadCredentials.
144  oauth2_service_.LoadCredentials(main_account_id);
145  base::RunLoop().RunUntilIdle();
146
147  // Legacy tokens get discarded, but the old refresh token is kept.
148  EXPECT_EQ(1, tokens_loaded_count_);
149  EXPECT_EQ(1, token_available_count_);
150  EXPECT_EQ(1, start_batch_changes_);
151  EXPECT_EQ(1, end_batch_changes_);
152  EXPECT_TRUE(oauth2_service_.RefreshTokenIsAvailable(main_account_id));
153  EXPECT_EQ(1U, oauth2_service_.refresh_tokens().size());
154  EXPECT_EQ(main_refresh_token,
155            oauth2_service_.refresh_tokens()[main_account_id]->refresh_token());
156
157  // Add an old legacy token to the DB, to ensure it will not overwrite existing
158  // credentials for main account.
159  AddAuthTokenManually(GaiaConstants::kGaiaOAuth2LoginRefreshToken,
160                       "secondOldRefreshToken");
161  // Add some other legacy token. (Expected to get discarded).
162  AddAuthTokenManually(kLSOService, "lsoToken");
163  // Also add a token using PO2TS.UpdateCredentials and make sure upgrade does
164  // not wipe it.
165  std::string other_account_id("other_account_id");
166  std::string other_refresh_token("other_refresh_token");
167  oauth2_service_.UpdateCredentials(other_account_id, other_refresh_token);
168  ResetObserverCounts();
169
170  // Force LoadCredentials.
171  oauth2_service_.LoadCredentials(main_account_id);
172  base::RunLoop().RunUntilIdle();
173
174  // Again legacy tokens get discarded, but since the main porfile account
175  // token is present it is not overwritten.
176  EXPECT_EQ(2, token_available_count_);
177  EXPECT_EQ(1, tokens_loaded_count_);
178  EXPECT_EQ(1, start_batch_changes_);
179  EXPECT_EQ(1, end_batch_changes_);
180  EXPECT_TRUE(oauth2_service_.RefreshTokenIsAvailable(main_account_id));
181  // TODO(fgorski): cover both using RefreshTokenIsAvailable() and then get the
182  // tokens using GetRefreshToken()
183  EXPECT_EQ(2U, oauth2_service_.refresh_tokens().size());
184  EXPECT_EQ(main_refresh_token,
185            oauth2_service_.refresh_tokens()[main_account_id]->refresh_token());
186  EXPECT_EQ(
187      other_refresh_token,
188      oauth2_service_.refresh_tokens()[other_account_id]->refresh_token());
189
190  oauth2_service_.RevokeAllCredentials();
191  EXPECT_EQ(2, start_batch_changes_);
192  EXPECT_EQ(2, end_batch_changes_);
193}
194
195TEST_F(MutableProfileOAuth2TokenServiceTest, PersistenceRevokeCredentials) {
196  std::string account_id_1 = "account_id_1";
197  std::string refresh_token_1 = "refresh_token_1";
198  std::string account_id_2 = "account_id_2";
199  std::string refresh_token_2 = "refresh_token_2";
200
201  // TODO(fgorski): Enable below when implemented:
202  // EXPECT_FALSE(oauth2_servive_->RefreshTokenIsAvailable(account_id_1));
203  // EXPECT_FALSE(oauth2_servive_->RefreshTokenIsAvailable(account_id_2));
204
205  oauth2_service_.UpdateCredentials(account_id_1, refresh_token_1);
206  oauth2_service_.UpdateCredentials(account_id_2, refresh_token_2);
207  EXPECT_EQ(2, start_batch_changes_);
208  EXPECT_EQ(2, end_batch_changes_);
209
210  // TODO(fgorski): Enable below when implemented:
211  // EXPECT_TRUE(oauth2_servive_->RefreshTokenIsAvailable(account_id_1));
212  // EXPECT_TRUE(oauth2_service_.RefreshTokenIsAvailable(account_id_2));
213
214  ResetObserverCounts();
215  oauth2_service_.RevokeCredentials(account_id_1);
216  EXPECT_EQ(1, start_batch_changes_);
217  EXPECT_EQ(1, end_batch_changes_);
218  ExpectOneTokenRevokedNotification();
219
220  // TODO(fgorski): Enable below when implemented:
221  // EXPECT_FALSE(oauth2_servive_->RefreshTokenIsAvailable(account_id_1));
222  // EXPECT_TRUE(oauth2_service_.RefreshTokenIsAvailable(account_id_2));
223
224  oauth2_service_.RevokeAllCredentials();
225  EXPECT_EQ(0, token_available_count_);
226  EXPECT_EQ(1, token_revoked_count_);
227  EXPECT_EQ(0, tokens_loaded_count_);
228  EXPECT_EQ(1, start_batch_changes_);
229  EXPECT_EQ(1, end_batch_changes_);
230  ResetObserverCounts();
231}
232
233TEST_F(MutableProfileOAuth2TokenServiceTest, PersistenceLoadCredentials) {
234  // Ensure DB is clean.
235  oauth2_service_.RevokeAllCredentials();
236  ResetObserverCounts();
237  // Perform a load from an empty DB.
238  oauth2_service_.LoadCredentials("account_id");
239  base::RunLoop().RunUntilIdle();
240  EXPECT_EQ(1, start_batch_changes_);
241  EXPECT_EQ(1, end_batch_changes_);
242  ExpectOneTokensLoadedNotification();
243  // LoadCredentials() guarantees that the account given to it as argument
244  // is in the refresh_token map.
245  EXPECT_EQ(1U, oauth2_service_.refresh_tokens().size());
246  EXPECT_TRUE(
247      oauth2_service_.refresh_tokens()["account_id"]->refresh_token().empty());
248  // Setup a DB with tokens that don't require upgrade and clear memory.
249  oauth2_service_.UpdateCredentials("account_id", "refresh_token");
250  oauth2_service_.UpdateCredentials("account_id2", "refresh_token2");
251  oauth2_service_.refresh_tokens().clear();
252  EXPECT_EQ(2, start_batch_changes_);
253  EXPECT_EQ(2, end_batch_changes_);
254  ResetObserverCounts();
255
256  oauth2_service_.LoadCredentials("account_id");
257  base::RunLoop().RunUntilIdle();
258  EXPECT_EQ(2, token_available_count_);
259  EXPECT_EQ(0, token_revoked_count_);
260  EXPECT_EQ(1, tokens_loaded_count_);
261  EXPECT_EQ(1, start_batch_changes_);
262  EXPECT_EQ(1, end_batch_changes_);
263  ResetObserverCounts();
264
265  // TODO(fgorski): Enable below when implemented:
266  // EXPECT_TRUE(oauth2_servive_->RefreshTokenIsAvailable("account_id"));
267  // EXPECT_TRUE(oauth2_service_.RefreshTokenIsAvailable("account_id2"));
268
269  oauth2_service_.RevokeAllCredentials();
270  EXPECT_EQ(0, token_available_count_);
271  EXPECT_EQ(2, token_revoked_count_);
272  EXPECT_EQ(0, tokens_loaded_count_);
273  EXPECT_EQ(1, start_batch_changes_);
274  EXPECT_EQ(1, end_batch_changes_);
275  ResetObserverCounts();
276}
277
278TEST_F(MutableProfileOAuth2TokenServiceTest, PersistanceNotifications) {
279  EXPECT_EQ(0, oauth2_service_.cache_size_for_testing());
280  oauth2_service_.UpdateCredentials("account_id", "refresh_token");
281  ExpectOneTokenAvailableNotification();
282
283  oauth2_service_.UpdateCredentials("account_id", "refresh_token");
284  ExpectNoNotifications();
285
286  oauth2_service_.UpdateCredentials("account_id", "refresh_token2");
287  ExpectOneTokenAvailableNotification();
288
289  oauth2_service_.RevokeCredentials("account_id");
290  ExpectOneTokenRevokedNotification();
291
292  oauth2_service_.UpdateCredentials("account_id", "refresh_token2");
293  ExpectOneTokenAvailableNotification();
294
295  oauth2_service_.RevokeAllCredentials();
296  ResetObserverCounts();
297}
298
299TEST_F(MutableProfileOAuth2TokenServiceTest, GetAccounts) {
300  EXPECT_TRUE(oauth2_service_.GetAccounts().empty());
301  oauth2_service_.UpdateCredentials("account_id1", "refresh_token1");
302  oauth2_service_.UpdateCredentials("account_id2", "refresh_token2");
303  std::vector<std::string> accounts = oauth2_service_.GetAccounts();
304  EXPECT_EQ(2u, accounts.size());
305  EXPECT_EQ(1, count(accounts.begin(), accounts.end(), "account_id1"));
306  EXPECT_EQ(1, count(accounts.begin(), accounts.end(), "account_id2"));
307  oauth2_service_.RevokeCredentials("account_id2");
308  accounts = oauth2_service_.GetAccounts();
309  EXPECT_EQ(1u, oauth2_service_.GetAccounts().size());
310  EXPECT_EQ(1, count(accounts.begin(), accounts.end(), "account_id1"));
311}
312
313TEST_F(MutableProfileOAuth2TokenServiceTest, TokenServiceUpdateClearsCache) {
314  EXPECT_EQ(0, oauth2_service_.cache_size_for_testing());
315  std::set<std::string> scope_list;
316  scope_list.insert("scope");
317  oauth2_service_.UpdateCredentials(kEmail, "refreshToken");
318  ExpectOneTokenAvailableNotification();
319  factory_.SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_url(),
320                           GetValidTokenResponse("token", 3600),
321                           net::HTTP_OK,
322                           net::URLRequestStatus::SUCCESS);
323
324  scoped_ptr<OAuth2TokenService::Request> request(
325      oauth2_service_.StartRequest(kEmail, scope_list, &consumer_));
326  base::RunLoop().RunUntilIdle();
327  EXPECT_EQ(1, consumer_.number_of_successful_tokens_);
328  EXPECT_EQ(0, consumer_.number_of_errors_);
329  EXPECT_EQ("token", consumer_.last_token_);
330  EXPECT_EQ(1, oauth2_service_.cache_size_for_testing());
331
332  // Signs out and signs in
333  oauth2_service_.RevokeCredentials(kEmail);
334  ExpectOneTokenRevokedNotification();
335
336  EXPECT_EQ(0, oauth2_service_.cache_size_for_testing());
337  oauth2_service_.UpdateCredentials(kEmail, "refreshToken");
338  ExpectOneTokenAvailableNotification();
339  factory_.SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_url(),
340                           GetValidTokenResponse("another token", 3600),
341                           net::HTTP_OK,
342                           net::URLRequestStatus::SUCCESS);
343
344  request = oauth2_service_.StartRequest(kEmail, scope_list, &consumer_);
345  base::RunLoop().RunUntilIdle();
346  EXPECT_EQ(2, consumer_.number_of_successful_tokens_);
347  EXPECT_EQ(0, consumer_.number_of_errors_);
348  EXPECT_EQ("another token", consumer_.last_token_);
349  EXPECT_EQ(1, oauth2_service_.cache_size_for_testing());
350}
351
352TEST_F(MutableProfileOAuth2TokenServiceTest, FetchTransientError) {
353  factory_.SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_url(),
354                           "",
355                           net::HTTP_FORBIDDEN,
356                           net::URLRequestStatus::FAILED);
357
358  EXPECT_EQ(0, oauth2_service_.cache_size_for_testing());
359  std::set<std::string> scope_list;
360  scope_list.insert("scope");
361  oauth2_service_.set_max_authorization_token_fetch_retries_for_testing(0);
362  oauth2_service_.UpdateCredentials(kEmail, "refreshToken");
363  ExpectOneTokenAvailableNotification();
364
365  scoped_ptr<OAuth2TokenService::Request> request(
366      oauth2_service_.StartRequest(kEmail, scope_list, &consumer_));
367  base::RunLoop().RunUntilIdle();
368  EXPECT_EQ(GoogleServiceAuthError::AuthErrorNone(),
369            oauth2_service_.signin_error_controller()->auth_error());
370}
371