1 // Copyright 2013 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/chromeos/settings/device_oauth2_token_service.h"
7 #include "base/message_loop/message_loop.h"
8 #include "base/prefs/testing_pref_service.h"
9 #include "base/run_loop.h"
10 #include "base/threading/sequenced_worker_pool.h"
11 #include "chrome/browser/chromeos/policy/device_policy_builder.h"
12 #include "chrome/browser/chromeos/settings/cros_settings.h"
13 #include "chrome/browser/chromeos/settings/device_oauth2_token_service_delegate.h"
14 #include "chrome/browser/chromeos/settings/device_settings_service.h"
15 #include "chrome/browser/chromeos/settings/device_settings_test_helper.h"
16 #include "chrome/browser/chromeos/settings/token_encryptor.h"
17 #include "chrome/common/pref_names.h"
18 #include "chrome/test/base/scoped_testing_local_state.h"
19 #include "chrome/test/base/testing_browser_process.h"
20 #include "chromeos/cryptohome/system_salt_getter.h"
21 #include "chromeos/dbus/dbus_thread_manager.h"
22 #include "chromeos/dbus/fake_cryptohome_client.h"
23 #include "components/ownership/mock_owner_key_util.h"
24 #include "content/public/browser/browser_thread.h"
25 #include "content/public/test/test_browser_thread.h"
26 #include "google_apis/gaia/gaia_oauth_client.h"
27 #include "google_apis/gaia/oauth2_token_service_test_util.h"
28 #include "net/http/http_status_code.h"
29 #include "net/url_request/test_url_fetcher_factory.h"
30 #include "net/url_request/url_fetcher_delegate.h"
31 #include "net/url_request/url_request_test_util.h"
32 #include "testing/gmock/include/gmock/gmock.h"
33 #include "testing/gtest/include/gtest/gtest.h"
39 class MockOAuth2TokenServiceObserver
: public OAuth2TokenService::Observer
{
41 MockOAuth2TokenServiceObserver();
42 ~MockOAuth2TokenServiceObserver() override
;
44 MOCK_METHOD1(OnRefreshTokenAvailable
, void(const std::string
&));
47 MockOAuth2TokenServiceObserver::MockOAuth2TokenServiceObserver() {
50 MockOAuth2TokenServiceObserver::~MockOAuth2TokenServiceObserver() {
55 static const int kOAuthTokenServiceUrlFetcherId
= 0;
56 static const int kValidatorUrlFetcherId
= gaia::GaiaOAuthClient::kUrlFetcherId
;
58 class DeviceOAuth2TokenServiceTest
: public testing::Test
{
60 DeviceOAuth2TokenServiceTest()
61 : scoped_testing_local_state_(TestingBrowserProcess::GetGlobal()),
62 request_context_getter_(
63 new net::TestURLRequestContextGetter(message_loop_
.task_runner())) {
65 ~DeviceOAuth2TokenServiceTest() override
{}
67 // Most tests just want a noop crypto impl with a dummy refresh token value in
68 // Local State (if the value is an empty string, it will be ignored).
69 void SetUpDefaultValues() {
70 SetDeviceRefreshTokenInLocalState("device_refresh_token_4_test");
71 SetRobotAccountId("service_acct@g.com");
73 AssertConsumerTokensAndErrors(0, 0);
75 base::RunLoop().RunUntilIdle();
78 void SetUpWithPendingSalt() {
79 fake_cryptohome_client_
->set_system_salt(std::vector
<uint8
>());
80 fake_cryptohome_client_
->SetServiceIsAvailable(false);
84 void SetRobotAccountId(const std::string
& account_id
) {
85 device_policy_
.policy_data().set_service_account_identity(account_id
);
86 device_policy_
.Build();
87 device_settings_test_helper_
.set_policy_blob(device_policy_
.GetBlob());
88 DeviceSettingsService::Get()->Load();
89 device_settings_test_helper_
.Flush();
92 scoped_ptr
<OAuth2TokenService::Request
> StartTokenRequest() {
93 return oauth2_service_
->StartRequest(oauth2_service_
->GetRobotAccountId(),
94 std::set
<std::string
>(),
98 void SetUp() override
{
99 fake_cryptohome_client_
= new FakeCryptohomeClient
;
100 fake_cryptohome_client_
->SetServiceIsAvailable(true);
101 fake_cryptohome_client_
->set_system_salt(
102 FakeCryptohomeClient::GetStubSystemSalt());
103 chromeos::DBusThreadManager::GetSetterForTesting()->SetCryptohomeClient(
104 scoped_ptr
<CryptohomeClient
>(fake_cryptohome_client_
));
106 SystemSaltGetter::Initialize();
108 DeviceSettingsService::Initialize();
109 scoped_refptr
<ownership::MockOwnerKeyUtil
> owner_key_util_(
110 new ownership::MockOwnerKeyUtil());
111 owner_key_util_
->SetPublicKeyFromPrivateKey(
112 *device_policy_
.GetSigningKey());
113 DeviceSettingsService::Get()->SetSessionManager(
114 &device_settings_test_helper_
, owner_key_util_
);
116 CrosSettings::Initialize();
119 void TearDown() override
{
120 oauth2_service_
.reset();
121 CrosSettings::Shutdown();
122 TestingBrowserProcess::GetGlobal()->SetBrowserPolicyConnector(NULL
);
123 content::BrowserThread::GetBlockingPool()->FlushForTesting();
124 DeviceSettingsService::Get()->UnsetSessionManager();
125 DeviceSettingsService::Shutdown();
126 SystemSaltGetter::Shutdown();
127 DBusThreadManager::Shutdown();
128 base::RunLoop().RunUntilIdle();
131 void CreateService() {
132 DeviceOAuth2TokenServiceDelegate
* delegate
=
133 new DeviceOAuth2TokenServiceDelegate(request_context_getter_
.get(),
134 scoped_testing_local_state_
.Get());
135 delegate
->max_refresh_token_validation_retries_
= 0;
136 oauth2_service_
.reset(new DeviceOAuth2TokenService(delegate
));
137 oauth2_service_
->set_max_authorization_token_fetch_retries_for_testing(0);
140 // Utility method to set a value in Local State for the device refresh token
141 // (it must have a non-empty value or it won't be used).
142 void SetDeviceRefreshTokenInLocalState(const std::string
& refresh_token
) {
143 scoped_testing_local_state_
.Get()->SetUserPref(
144 prefs::kDeviceRobotAnyApiRefreshToken
,
145 new base::StringValue(refresh_token
));
148 std::string
GetValidTokenInfoResponse(const std::string email
) {
149 return "{ \"email\": \"" + email
+ "\","
150 " \"user_id\": \"1234567890\" }";
153 bool RefreshTokenIsAvailable() {
154 return oauth2_service_
->RefreshTokenIsAvailable(
155 oauth2_service_
->GetRobotAccountId());
158 std::string
GetRefreshToken() {
159 if (!RefreshTokenIsAvailable())
160 return std::string();
162 return static_cast<DeviceOAuth2TokenServiceDelegate
*>(
163 oauth2_service_
->GetDelegate())
164 ->GetRefreshToken(oauth2_service_
->GetRobotAccountId());
167 // A utility method to return fake URL results, for testing the refresh token
168 // validation logic. For a successful validation attempt, this method will be
169 // called three times for the steps listed below (steps 1 and 2 happen in
172 // Step 1a: fetch the access token for the tokeninfo API.
173 // Step 1b: call the tokeninfo API.
174 // Step 2: Fetch the access token for the requested scope
175 // (in this case, cloudprint).
176 void ReturnOAuthUrlFetchResults(int fetcher_id
,
177 net::HttpStatusCode response_code
,
178 const std::string
& response_string
);
180 // Generates URL fetch replies with the specified results for requests
181 // generated by the token service.
182 void PerformURLFetchesWithResults(
183 net::HttpStatusCode tokeninfo_access_token_status
,
184 const std::string
& tokeninfo_access_token_response
,
185 net::HttpStatusCode tokeninfo_fetch_status
,
186 const std::string
& tokeninfo_fetch_response
,
187 net::HttpStatusCode service_access_token_status
,
188 const std::string
& service_access_token_response
);
190 // Generates URL fetch replies for the success path.
191 void PerformURLFetches();
193 void AssertConsumerTokensAndErrors(int num_tokens
, int num_errors
);
196 // This is here because DeviceOAuth2TokenService's destructor is private;
197 // base::DefaultDeleter therefore doesn't work. However, the test class is
198 // declared friend in DeviceOAuth2TokenService, so this deleter works.
199 struct TokenServiceDeleter
{
200 inline void operator()(DeviceOAuth2TokenService
* ptr
) const {
205 base::MessageLoop message_loop_
;
206 ScopedTestingLocalState scoped_testing_local_state_
;
207 scoped_refptr
<net::TestURLRequestContextGetter
> request_context_getter_
;
208 net::TestURLFetcherFactory factory_
;
209 FakeCryptohomeClient
* fake_cryptohome_client_
;
210 DeviceSettingsTestHelper device_settings_test_helper_
;
211 policy::DevicePolicyBuilder device_policy_
;
212 scoped_ptr
<DeviceOAuth2TokenService
, TokenServiceDeleter
> oauth2_service_
;
213 TestingOAuth2TokenServiceConsumer consumer_
;
216 void DeviceOAuth2TokenServiceTest::ReturnOAuthUrlFetchResults(
218 net::HttpStatusCode response_code
,
219 const std::string
& response_string
) {
220 net::TestURLFetcher
* fetcher
= factory_
.GetFetcherByID(fetcher_id
);
222 factory_
.RemoveFetcherFromMap(fetcher_id
);
223 fetcher
->set_response_code(response_code
);
224 fetcher
->SetResponseString(response_string
);
225 fetcher
->delegate()->OnURLFetchComplete(fetcher
);
226 base::RunLoop().RunUntilIdle();
230 void DeviceOAuth2TokenServiceTest::PerformURLFetchesWithResults(
231 net::HttpStatusCode tokeninfo_access_token_status
,
232 const std::string
& tokeninfo_access_token_response
,
233 net::HttpStatusCode tokeninfo_fetch_status
,
234 const std::string
& tokeninfo_fetch_response
,
235 net::HttpStatusCode service_access_token_status
,
236 const std::string
& service_access_token_response
) {
237 ReturnOAuthUrlFetchResults(
238 kValidatorUrlFetcherId
,
239 tokeninfo_access_token_status
,
240 tokeninfo_access_token_response
);
242 ReturnOAuthUrlFetchResults(
243 kValidatorUrlFetcherId
,
244 tokeninfo_fetch_status
,
245 tokeninfo_fetch_response
);
247 ReturnOAuthUrlFetchResults(
248 kOAuthTokenServiceUrlFetcherId
,
249 service_access_token_status
,
250 service_access_token_response
);
253 void DeviceOAuth2TokenServiceTest::PerformURLFetches() {
254 PerformURLFetchesWithResults(
255 net::HTTP_OK
, GetValidTokenResponse("tokeninfo_access_token", 3600),
256 net::HTTP_OK
, GetValidTokenInfoResponse("service_acct@g.com"),
257 net::HTTP_OK
, GetValidTokenResponse("scoped_access_token", 3600));
260 void DeviceOAuth2TokenServiceTest::AssertConsumerTokensAndErrors(
263 EXPECT_EQ(num_tokens
, consumer_
.number_of_successful_tokens_
);
264 EXPECT_EQ(num_errors
, consumer_
.number_of_errors_
);
267 TEST_F(DeviceOAuth2TokenServiceTest
, SaveEncryptedToken
) {
270 oauth2_service_
->SetAndSaveRefreshToken(
271 "test-token", DeviceOAuth2TokenService::StatusCallback());
272 EXPECT_EQ("test-token", GetRefreshToken());
275 TEST_F(DeviceOAuth2TokenServiceTest
, SaveEncryptedTokenEarly
) {
276 // Set a new refresh token without the system salt available.
277 SetUpWithPendingSalt();
279 oauth2_service_
->SetAndSaveRefreshToken(
280 "test-token", DeviceOAuth2TokenService::StatusCallback());
281 EXPECT_EQ("test-token", GetRefreshToken());
283 // Make the system salt available.
284 fake_cryptohome_client_
->set_system_salt(
285 FakeCryptohomeClient::GetStubSystemSalt());
286 fake_cryptohome_client_
->SetServiceIsAvailable(true);
287 base::RunLoop().RunUntilIdle();
289 // The original token should still be present.
290 EXPECT_EQ("test-token", GetRefreshToken());
292 // Reloading shouldn't change the token either.
294 base::RunLoop().RunUntilIdle();
295 EXPECT_EQ("test-token", GetRefreshToken());
298 TEST_F(DeviceOAuth2TokenServiceTest
, RefreshTokenValidation_Success
) {
299 SetUpDefaultValues();
300 scoped_ptr
<OAuth2TokenService::Request
> request
= StartTokenRequest();
303 AssertConsumerTokensAndErrors(1, 0);
305 EXPECT_EQ("scoped_access_token", consumer_
.last_token_
);
308 TEST_F(DeviceOAuth2TokenServiceTest
, RefreshTokenValidation_SuccessAsyncLoad
) {
309 SetUpWithPendingSalt();
311 scoped_ptr
<OAuth2TokenService::Request
> request
= StartTokenRequest();
313 AssertConsumerTokensAndErrors(0, 0);
315 fake_cryptohome_client_
->set_system_salt(
316 FakeCryptohomeClient::GetStubSystemSalt());
317 fake_cryptohome_client_
->SetServiceIsAvailable(true);
318 base::RunLoop().RunUntilIdle();
321 AssertConsumerTokensAndErrors(1, 0);
323 EXPECT_EQ("scoped_access_token", consumer_
.last_token_
);
326 TEST_F(DeviceOAuth2TokenServiceTest
, RefreshTokenValidation_Cancel
) {
327 SetUpDefaultValues();
328 scoped_ptr
<OAuth2TokenService::Request
> request
= StartTokenRequest();
333 // Test succeeds if this line is reached without a crash.
336 TEST_F(DeviceOAuth2TokenServiceTest
, RefreshTokenValidation_NoSalt
) {
337 fake_cryptohome_client_
->set_system_salt(std::vector
<uint8
>());
338 fake_cryptohome_client_
->SetServiceIsAvailable(true);
339 SetUpDefaultValues();
341 EXPECT_FALSE(RefreshTokenIsAvailable());
343 scoped_ptr
<OAuth2TokenService::Request
> request
= StartTokenRequest();
344 base::RunLoop().RunUntilIdle();
346 AssertConsumerTokensAndErrors(0, 1);
349 TEST_F(DeviceOAuth2TokenServiceTest
,
350 RefreshTokenValidation_Failure_TokenInfoAccessTokenHttpError
) {
351 SetUpDefaultValues();
352 scoped_ptr
<OAuth2TokenService::Request
> request
= StartTokenRequest();
354 PerformURLFetchesWithResults(
355 net::HTTP_UNAUTHORIZED
, "",
356 net::HTTP_OK
, GetValidTokenInfoResponse("service_acct@g.com"),
357 net::HTTP_OK
, GetValidTokenResponse("ignored", 3600));
359 AssertConsumerTokensAndErrors(0, 1);
362 TEST_F(DeviceOAuth2TokenServiceTest
,
363 RefreshTokenValidation_Failure_TokenInfoAccessTokenInvalidResponse
) {
364 SetUpDefaultValues();
365 scoped_ptr
<OAuth2TokenService::Request
> request
= StartTokenRequest();
367 PerformURLFetchesWithResults(
368 net::HTTP_OK
, "invalid response",
369 net::HTTP_OK
, GetValidTokenInfoResponse("service_acct@g.com"),
370 net::HTTP_OK
, GetValidTokenResponse("ignored", 3600));
372 AssertConsumerTokensAndErrors(0, 1);
375 TEST_F(DeviceOAuth2TokenServiceTest
,
376 RefreshTokenValidation_Failure_TokenInfoApiCallHttpError
) {
377 SetUpDefaultValues();
378 scoped_ptr
<OAuth2TokenService::Request
> request
= StartTokenRequest();
380 PerformURLFetchesWithResults(
381 net::HTTP_OK
, GetValidTokenResponse("tokeninfo_access_token", 3600),
382 net::HTTP_INTERNAL_SERVER_ERROR
, "",
383 net::HTTP_OK
, GetValidTokenResponse("ignored", 3600));
385 AssertConsumerTokensAndErrors(0, 1);
388 TEST_F(DeviceOAuth2TokenServiceTest
,
389 RefreshTokenValidation_Failure_TokenInfoApiCallInvalidResponse
) {
390 SetUpDefaultValues();
391 scoped_ptr
<OAuth2TokenService::Request
> request
= StartTokenRequest();
393 PerformURLFetchesWithResults(
394 net::HTTP_OK
, GetValidTokenResponse("tokeninfo_access_token", 3600),
395 net::HTTP_OK
, "invalid response",
396 net::HTTP_OK
, GetValidTokenResponse("ignored", 3600));
398 AssertConsumerTokensAndErrors(0, 1);
401 TEST_F(DeviceOAuth2TokenServiceTest
,
402 RefreshTokenValidation_Failure_CloudPrintAccessTokenHttpError
) {
403 SetUpDefaultValues();
404 scoped_ptr
<OAuth2TokenService::Request
> request
= StartTokenRequest();
406 PerformURLFetchesWithResults(
407 net::HTTP_OK
, GetValidTokenResponse("tokeninfo_access_token", 3600),
408 net::HTTP_OK
, GetValidTokenInfoResponse("service_acct@g.com"),
409 net::HTTP_BAD_REQUEST
, "");
411 AssertConsumerTokensAndErrors(0, 1);
414 TEST_F(DeviceOAuth2TokenServiceTest
,
415 RefreshTokenValidation_Failure_CloudPrintAccessTokenInvalidResponse
) {
416 SetUpDefaultValues();
417 scoped_ptr
<OAuth2TokenService::Request
> request
= StartTokenRequest();
419 PerformURLFetchesWithResults(
420 net::HTTP_OK
, GetValidTokenResponse("tokeninfo_access_token", 3600),
421 net::HTTP_OK
, GetValidTokenInfoResponse("service_acct@g.com"),
422 net::HTTP_OK
, "invalid request");
424 AssertConsumerTokensAndErrors(0, 1);
427 TEST_F(DeviceOAuth2TokenServiceTest
, RefreshTokenValidation_Failure_BadOwner
) {
428 SetUpDefaultValues();
429 scoped_ptr
<OAuth2TokenService::Request
> request
= StartTokenRequest();
431 SetRobotAccountId("WRONG_service_acct@g.com");
433 PerformURLFetchesWithResults(
434 net::HTTP_OK
, GetValidTokenResponse("tokeninfo_access_token", 3600),
435 net::HTTP_OK
, GetValidTokenInfoResponse("service_acct@g.com"),
436 net::HTTP_OK
, GetValidTokenResponse("ignored", 3600));
438 AssertConsumerTokensAndErrors(0, 1);
441 TEST_F(DeviceOAuth2TokenServiceTest
, RefreshTokenValidation_Retry
) {
442 SetUpDefaultValues();
443 scoped_ptr
<OAuth2TokenService::Request
> request
= StartTokenRequest();
445 PerformURLFetchesWithResults(
446 net::HTTP_INTERNAL_SERVER_ERROR
, "",
447 net::HTTP_OK
, GetValidTokenInfoResponse("service_acct@g.com"),
448 net::HTTP_OK
, GetValidTokenResponse("ignored", 3600));
450 AssertConsumerTokensAndErrors(0, 1);
452 // Retry should succeed.
453 request
= StartTokenRequest();
455 AssertConsumerTokensAndErrors(1, 1);
458 TEST_F(DeviceOAuth2TokenServiceTest
, DoNotAnnounceTokenWithoutAccountID
) {
461 testing::StrictMock
<MockOAuth2TokenServiceObserver
> observer
;
462 oauth2_service_
->AddObserver(&observer
);
464 // Make a token available during enrollment. Verify that the token is not
466 oauth2_service_
->SetAndSaveRefreshToken(
467 "test-token", DeviceOAuth2TokenService::StatusCallback());
468 testing::Mock::VerifyAndClearExpectations(&observer
);
470 // Also make the robot account ID available. Verify that the token is
472 EXPECT_CALL(observer
, OnRefreshTokenAvailable("robot@example.com"));
473 SetRobotAccountId("robot@example.com");
474 testing::Mock::VerifyAndClearExpectations(&observer
);
476 oauth2_service_
->RemoveObserver(&observer
);
479 } // namespace chromeos