OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2017 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/access_token_fetcher.h" | |
6 | |
7 #include <memory> | |
8 #include <utility> | |
9 | |
10 #include "base/memory/ptr_util.h" | |
11 #include "base/message_loop/message_loop.h" | |
12 #include "base/run_loop.h" | |
13 #include "base/test/mock_callback.h" | |
14 #include "components/prefs/testing_pref_service.h" | |
15 #include "components/signin/core/browser/account_tracker_service.h" | |
16 #include "components/signin/core/browser/fake_profile_oauth2_token_service.h" | |
17 #include "components/signin/core/browser/fake_signin_manager.h" | |
18 #include "components/signin/core/browser/test_signin_client.h" | |
19 #include "components/sync_preferences/testing_pref_service_syncable.h" | |
20 #include "testing/gmock/include/gmock/gmock.h" | |
21 #include "testing/gmock_mutant.h" | |
22 #include "testing/gtest/include/gtest/gtest.h" | |
23 | |
24 using base::MockCallback; | |
25 using sync_preferences::TestingPrefServiceSyncable; | |
26 using testing::CallbackToFunctor; | |
27 using testing::InvokeWithoutArgs; | |
28 using testing::StrictMock; | |
29 | |
30 #if defined(OS_CHROMEOS) | |
31 // ChromeOS doesn't have SigninManager. | |
32 using SigninManagerForTest = FakeSigninManagerBase; | |
33 #else | |
34 using SigninManagerForTest = FakeSigninManager; | |
35 #endif // OS_CHROMEOS | |
36 | |
37 class AccessTokenFetcherTest : public testing::Test { | |
38 public: | |
39 using TestTokenCallback = | |
40 StrictMock<MockCallback<AccessTokenFetcher::TokenCallback>>; | |
41 | |
42 AccessTokenFetcherTest() : signin_client_(&pref_service_) { | |
43 AccountTrackerService::RegisterPrefs(pref_service_.registry()); | |
44 #if defined(OS_CHROMEOS) | |
45 SigninManagerBase::RegisterProfilePrefs(pref_service_.registry()); | |
46 SigninManagerBase::RegisterPrefs(pref_service_.registry()); | |
47 #else | |
48 SigninManager::RegisterProfilePrefs(pref_service_.registry()); | |
49 SigninManager::RegisterPrefs(pref_service_.registry()); | |
50 #endif // OS_CHROMEOS | |
51 | |
52 account_tracker_ = base::MakeUnique<AccountTrackerService>(); | |
53 account_tracker_->Initialize(&signin_client_); | |
54 | |
55 #if defined(OS_CHROMEOS) | |
56 signin_manager_ = base::MakeUnique<FakeSigninManagerBase>( | |
57 &signin_client_, account_tracker_.get()); | |
58 #else | |
59 signin_manager_ = base::MakeUnique<FakeSigninManager>( | |
60 &signin_client_, &token_service_, account_tracker_.get(), | |
61 /*cookie_manager_service=*/nullptr); | |
62 #endif // OS_CHROMEOS | |
63 } | |
64 | |
65 ~AccessTokenFetcherTest() override {} | |
66 | |
67 std::unique_ptr<AccessTokenFetcher> CreateFetcher( | |
68 AccessTokenFetcher::TokenCallback callback) { | |
69 std::set<std::string> scopes{"scope"}; | |
70 return base::MakeUnique<AccessTokenFetcher>( | |
71 "test_consumer", signin_manager_.get(), &token_service_, scopes, | |
72 std::move(callback)); | |
73 } | |
74 | |
75 FakeProfileOAuth2TokenService* token_service() { return &token_service_; } | |
76 SigninManagerForTest* signin_manager() { return signin_manager_.get(); } | |
77 | |
78 void SignIn(const std::string& account) { | |
79 #if defined(OS_CHROMEOS) | |
80 signin_manager_->SignIn(account); | |
81 #else | |
82 signin_manager_->SignIn(account, "user", "pass"); | |
83 #endif // OS_CHROMEOS | |
84 } | |
85 | |
86 private: | |
87 TestingPrefServiceSyncable pref_service_; | |
88 TestSigninClient signin_client_; | |
89 FakeProfileOAuth2TokenService token_service_; | |
90 | |
91 std::unique_ptr<AccountTrackerService> account_tracker_; | |
92 std::unique_ptr<SigninManagerForTest> signin_manager_; | |
93 }; | |
94 | |
95 TEST_F(AccessTokenFetcherTest, ShouldReturnAccessToken) { | |
96 TestTokenCallback callback; | |
97 | |
98 SignIn("account"); | |
99 token_service()->GetDelegate()->UpdateCredentials("account", "refresh token"); | |
100 | |
101 // Signed in and refresh token already exists, so this should result in a | |
102 // request for an access token. | |
103 auto fetcher = CreateFetcher(callback.Get()); | |
104 | |
105 // Once the access token request is fulfilled, we should get called back with | |
106 // the access token. | |
107 EXPECT_CALL(callback, | |
108 Run(GoogleServiceAuthError::AuthErrorNone(), "access token")); | |
109 token_service()->IssueAllTokensForAccount( | |
110 "account", "access token", | |
111 base::Time::Now() + base::TimeDelta::FromHours(1)); | |
112 } | |
113 | |
114 TEST_F(AccessTokenFetcherTest, ShouldNotReplyIfDestroyed) { | |
115 TestTokenCallback callback; | |
116 | |
117 SignIn("account"); | |
118 token_service()->GetDelegate()->UpdateCredentials("account", "refresh token"); | |
119 | |
120 // Signed in and refresh token already exists, so this should result in a | |
121 // request for an access token. | |
122 auto fetcher = CreateFetcher(callback.Get()); | |
123 | |
124 // Destroy the fetcher before the access token request is fulfilled. | |
125 fetcher.reset(); | |
126 | |
127 // Now fulfilling the access token request should have no effect. | |
128 token_service()->IssueAllTokensForAccount( | |
129 "account", "access token", | |
130 base::Time::Now() + base::TimeDelta::FromHours(1)); | |
131 } | |
132 | |
133 TEST_F(AccessTokenFetcherTest, ShouldNotReturnWhenSignedOut) { | |
134 TestTokenCallback callback; | |
135 | |
136 // Signed out -> the fetcher should wait for a sign-in which never happens | |
137 // in this test, so we shouldn't get called back. | |
138 auto fetcher = CreateFetcher(callback.Get()); | |
139 } | |
140 | |
141 // Tests related to waiting for sign-in don't apply on ChromeOS (it doesn't have | |
142 // that concept). | |
143 #if !defined(OS_CHROMEOS) | |
144 | |
145 TEST_F(AccessTokenFetcherTest, ShouldWaitForSignIn) { | |
146 TestTokenCallback callback; | |
147 | |
148 // Not signed in, so this should wait for a sign-in to complete. | |
149 auto fetcher = CreateFetcher(callback.Get()); | |
150 | |
151 SignIn("account"); | |
152 token_service()->GetDelegate()->UpdateCredentials("account", "refresh token"); | |
153 | |
154 // Once the access token request is fulfilled, we should get called back with | |
155 // the access token. | |
156 EXPECT_CALL(callback, | |
157 Run(GoogleServiceAuthError::AuthErrorNone(), "access token")); | |
158 token_service()->IssueAllTokensForAccount( | |
159 "account", "access token", | |
160 base::Time::Now() + base::TimeDelta::FromHours(1)); | |
161 } | |
162 | |
163 TEST_F(AccessTokenFetcherTest, ShouldWaitForSignInInProgress) { | |
164 TestTokenCallback callback; | |
165 | |
166 signin_manager()->set_auth_in_progress("account"); | |
167 | |
168 // A sign-in is currently in progress, so this should wait for the sign-in to | |
169 // complete. | |
170 auto fetcher = CreateFetcher(callback.Get()); | |
171 | |
172 SignIn("account"); | |
173 token_service()->GetDelegate()->UpdateCredentials("account", "refresh token"); | |
174 | |
175 // Once the access token request is fulfilled, we should get called back with | |
176 // the access token. | |
177 EXPECT_CALL(callback, | |
178 Run(GoogleServiceAuthError::AuthErrorNone(), "access token")); | |
179 token_service()->IssueAllTokensForAccount( | |
180 "account", "access token", | |
181 base::Time::Now() + base::TimeDelta::FromHours(1)); | |
182 } | |
183 | |
184 TEST_F(AccessTokenFetcherTest, ShouldWaitForFailedSignIn) { | |
185 TestTokenCallback callback; | |
186 | |
187 signin_manager()->set_auth_in_progress("account"); | |
188 | |
189 // A sign-in is currently in progress, so this should wait for the sign-in to | |
190 // complete. | |
191 auto fetcher = CreateFetcher(callback.Get()); | |
192 | |
193 // The fetcher should detect the failed sign-in and call us with an empty | |
194 // access token. | |
195 EXPECT_CALL( | |
196 callback, | |
197 Run(GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED), | |
198 std::string())); | |
199 | |
200 signin_manager()->FailSignin( | |
201 GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED)); | |
202 } | |
203 | |
204 #endif // !OS_CHROMEOS | |
205 | |
206 TEST_F(AccessTokenFetcherTest, ShouldWaitForRefreshToken) { | |
207 TestTokenCallback callback; | |
208 | |
209 SignIn("account"); | |
210 | |
211 // Signed in, but there is no refresh token -> we should not get called back | |
212 // (yet). | |
213 auto fetcher = CreateFetcher(callback.Get()); | |
214 | |
215 // Getting a refresh token should result in a request for an access token. | |
216 token_service()->GetDelegate()->UpdateCredentials("account", "refresh token"); | |
217 | |
218 // Once the access token request is fulfilled, we should get called back with | |
219 // the access token. | |
220 EXPECT_CALL(callback, | |
221 Run(GoogleServiceAuthError::AuthErrorNone(), "access token")); | |
222 token_service()->IssueAllTokensForAccount( | |
223 "account", "access token", | |
224 base::Time::Now() + base::TimeDelta::FromHours(1)); | |
225 } | |
226 | |
227 TEST_F(AccessTokenFetcherTest, ShouldIgnoreRefreshTokensForOtherAccounts) { | |
228 TestTokenCallback callback; | |
229 | |
230 // Signed-in to "account", but there's only a refresh token for a different | |
231 // account. | |
232 SignIn("account"); | |
233 token_service()->GetDelegate()->UpdateCredentials("account 2", "refresh"); | |
234 | |
235 // The fetcher should wait for the correct refresh token. | |
236 auto fetcher = CreateFetcher(callback.Get()); | |
237 | |
238 // A refresh token for yet another account shouldn't matter either. | |
239 token_service()->GetDelegate()->UpdateCredentials("account 3", "refresh"); | |
240 } | |
241 | |
242 TEST_F(AccessTokenFetcherTest, ShouldReturnWhenNoRefreshTokenAvailable) { | |
243 TestTokenCallback callback; | |
244 | |
245 SignIn("account"); | |
246 | |
247 // Signed in, but there is no refresh token -> we should not get called back | |
248 // (yet). | |
249 auto fetcher = CreateFetcher(callback.Get()); | |
250 | |
251 // Getting a refresh token for some other account should have no effect. | |
252 token_service()->GetDelegate()->UpdateCredentials("different account", | |
253 "refresh token"); | |
254 | |
255 // The OAuth2TokenService posts a task to the current thread when we try to | |
256 // get an access token for an account without a refresh token, so we need a | |
257 // MessageLoop in this test to not crash. | |
258 base::MessageLoop message_loop; | |
Marc Treib
2017/01/27 13:16:39
This is unfortunate :(
But I don't see any other s
| |
259 | |
260 // When all refresh tokens have been loaded by the token service, but the one | |
261 // for our account wasn't among them, we should get called back with an empty | |
262 // access token. | |
263 EXPECT_CALL(callback, Run(testing::_, std::string())); | |
264 token_service()->GetDelegate()->LoadCredentials("account doesn't matter"); | |
265 | |
266 // Wait for the task posted by OAuth2TokenService to run. | |
267 base::RunLoop().RunUntilIdle(); | |
268 } | |
269 | |
270 TEST_F(AccessTokenFetcherTest, ShouldRetryCanceledAccessTokenRequest) { | |
271 TestTokenCallback callback; | |
272 | |
273 SignIn("account"); | |
274 token_service()->GetDelegate()->UpdateCredentials("account", "refresh token"); | |
275 | |
276 // Signed in and refresh token already exists, so this should result in a | |
277 // request for an access token. | |
278 auto fetcher = CreateFetcher(callback.Get()); | |
279 | |
280 // A canceled access token request should get retried once. | |
281 token_service()->IssueErrorForAllPendingRequestsForAccount( | |
282 "account", | |
283 GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED)); | |
284 | |
285 // Once the access token request is fulfilled, we should get called back with | |
286 // the access token. | |
287 EXPECT_CALL(callback, | |
288 Run(GoogleServiceAuthError::AuthErrorNone(), "access token")); | |
289 token_service()->IssueAllTokensForAccount( | |
290 "account", "access token", | |
291 base::Time::Now() + base::TimeDelta::FromHours(1)); | |
292 } | |
293 | |
294 TEST_F(AccessTokenFetcherTest, ShouldRetryCanceledAccessTokenRequestOnlyOnce) { | |
295 TestTokenCallback callback; | |
296 | |
297 SignIn("account"); | |
298 token_service()->GetDelegate()->UpdateCredentials("account", "refresh token"); | |
299 | |
300 // Signed in and refresh token already exists, so this should result in a | |
301 // request for an access token. | |
302 auto fetcher = CreateFetcher(callback.Get()); | |
303 | |
304 // A canceled access token request should get retried once. | |
305 token_service()->IssueErrorForAllPendingRequestsForAccount( | |
306 "account", | |
307 GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED)); | |
308 | |
309 // On the second failure, we should get called back with an empty access | |
310 // token. | |
311 EXPECT_CALL( | |
312 callback, | |
313 Run(GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED), | |
314 std::string())); | |
315 token_service()->IssueErrorForAllPendingRequestsForAccount( | |
316 "account", | |
317 GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED)); | |
318 } | |
319 | |
320 #if !defined(OS_CHROMEOS) | |
321 | |
322 TEST_F(AccessTokenFetcherTest, | |
323 ShouldNotRetryCanceledAccessTokenRequestIfSignedOut) { | |
324 TestTokenCallback callback; | |
325 | |
326 SignIn("account"); | |
327 token_service()->GetDelegate()->UpdateCredentials("account", "refresh token"); | |
328 | |
329 // Signed in and refresh token already exists, so this should result in a | |
330 // request for an access token. | |
331 auto fetcher = CreateFetcher(callback.Get()); | |
332 | |
333 // Simulate the user signing out while the access token request is pending. | |
334 // In this case, the pending request gets canceled, and the fetcher should | |
335 // *not* retry. | |
336 signin_manager()->ForceSignOut(); | |
337 EXPECT_CALL( | |
338 callback, | |
339 Run(GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED), | |
340 std::string())); | |
341 token_service()->IssueErrorForAllPendingRequestsForAccount( | |
342 "account", | |
343 GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED)); | |
344 } | |
345 | |
346 #endif // !OS_CHROMEOS | |
347 | |
348 TEST_F(AccessTokenFetcherTest, | |
349 ShouldNotRetryCanceledAccessTokenRequestIfRefreshTokenRevoked) { | |
350 TestTokenCallback callback; | |
351 | |
352 SignIn("account"); | |
353 token_service()->GetDelegate()->UpdateCredentials("account", "refresh token"); | |
354 | |
355 // Signed in and refresh token already exists, so this should result in a | |
356 // request for an access token. | |
357 auto fetcher = CreateFetcher(callback.Get()); | |
358 | |
359 // Simulate the refresh token getting invalidated. In this case, pending | |
360 // access token requests get canceled, and the fetcher should *not* retry. | |
361 token_service()->GetDelegate()->RevokeCredentials("account"); | |
362 EXPECT_CALL( | |
363 callback, | |
364 Run(GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED), | |
365 std::string())); | |
366 token_service()->IssueErrorForAllPendingRequestsForAccount( | |
367 "account", | |
368 GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED)); | |
369 } | |
370 | |
371 TEST_F(AccessTokenFetcherTest, ShouldNotRetryFailedAccessTokenRequest) { | |
372 TestTokenCallback callback; | |
373 | |
374 SignIn("account"); | |
375 token_service()->GetDelegate()->UpdateCredentials("account", "refresh token"); | |
376 | |
377 // Signed in and refresh token already exists, so this should result in a | |
378 // request for an access token. | |
379 auto fetcher = CreateFetcher(callback.Get()); | |
380 | |
381 // An access token failure other than "canceled" should not be retried; we | |
382 // should immediately get called back with an empty access token. | |
383 EXPECT_CALL( | |
384 callback, | |
385 Run(GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE), | |
386 std::string())); | |
387 token_service()->IssueErrorForAllPendingRequestsForAccount( | |
388 "account", | |
389 GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE)); | |
390 } | |
OLD | NEW |