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_settings_service.h"
14 #include "chrome/browser/chromeos/settings/device_settings_test_helper.h"
15 #include "chrome/browser/chromeos/settings/token_encryptor.h"
16 #include "chrome/common/pref_names.h"
17 #include "chrome/test/base/scoped_testing_local_state.h"
18 #include "chrome/test/base/testing_browser_process.h"
19 #include "chromeos/cryptohome/system_salt_getter.h"
20 #include "chromeos/dbus/dbus_thread_manager.h"
21 #include "chromeos/dbus/fake_cryptohome_client.h"
22 #include "components/ownership/mock_owner_key_util.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "content/public/test/test_browser_thread.h"
25 #include "google_apis/gaia/gaia_oauth_client.h"
26 #include "google_apis/gaia/oauth2_token_service_test_util.h"
27 #include "net/http/http_status_code.h"
28 #include "net/url_request/test_url_fetcher_factory.h"
29 #include "net/url_request/url_fetcher_delegate.h"
30 #include "net/url_request/url_request_test_util.h"
31 #include "testing/gmock/include/gmock/gmock.h"
32 #include "testing/gtest/include/gtest/gtest.h"
38 class MockOAuth2TokenServiceObserver
: public OAuth2TokenService::Observer
{
40 MockOAuth2TokenServiceObserver();
41 ~MockOAuth2TokenServiceObserver() override
;
43 MOCK_METHOD1(OnRefreshTokenAvailable
, void(const std::string
&));
46 MockOAuth2TokenServiceObserver::MockOAuth2TokenServiceObserver() {
49 MockOAuth2TokenServiceObserver::~MockOAuth2TokenServiceObserver() {
54 static const int kOAuthTokenServiceUrlFetcherId
= 0;
55 static const int kValidatorUrlFetcherId
= gaia::GaiaOAuthClient::kUrlFetcherId
;
57 class DeviceOAuth2TokenServiceTest
: public testing::Test
{
59 DeviceOAuth2TokenServiceTest()
60 : scoped_testing_local_state_(TestingBrowserProcess::GetGlobal()),
61 request_context_getter_(new net::TestURLRequestContextGetter(
62 message_loop_
.message_loop_proxy())) {}
63 ~DeviceOAuth2TokenServiceTest() override
{}
65 // Most tests just want a noop crypto impl with a dummy refresh token value in
66 // Local State (if the value is an empty string, it will be ignored).
67 void SetUpDefaultValues() {
68 SetDeviceRefreshTokenInLocalState("device_refresh_token_4_test");
69 SetRobotAccountId("service_acct@g.com");
71 AssertConsumerTokensAndErrors(0, 0);
73 base::RunLoop().RunUntilIdle();
76 void SetUpWithPendingSalt() {
77 fake_cryptohome_client_
->set_system_salt(std::vector
<uint8
>());
78 fake_cryptohome_client_
->SetServiceIsAvailable(false);
82 void SetRobotAccountId(const std::string
& account_id
) {
83 device_policy_
.policy_data().set_service_account_identity(account_id
);
84 device_policy_
.Build();
85 device_settings_test_helper_
.set_policy_blob(device_policy_
.GetBlob());
86 DeviceSettingsService::Get()->Load();
87 device_settings_test_helper_
.Flush();
90 scoped_ptr
<OAuth2TokenService::Request
> StartTokenRequest() {
91 return oauth2_service_
->StartRequest(oauth2_service_
->GetRobotAccountId(),
92 std::set
<std::string
>(),
96 void SetUp() override
{
97 fake_cryptohome_client_
= new FakeCryptohomeClient
;
98 fake_cryptohome_client_
->SetServiceIsAvailable(true);
99 fake_cryptohome_client_
->set_system_salt(
100 FakeCryptohomeClient::GetStubSystemSalt());
101 chromeos::DBusThreadManager::GetSetterForTesting()->SetCryptohomeClient(
102 scoped_ptr
<CryptohomeClient
>(fake_cryptohome_client_
));
104 SystemSaltGetter::Initialize();
106 DeviceSettingsService::Initialize();
107 scoped_refptr
<ownership::MockOwnerKeyUtil
> owner_key_util_(
108 new ownership::MockOwnerKeyUtil());
109 owner_key_util_
->SetPublicKeyFromPrivateKey(
110 *device_policy_
.GetSigningKey());
111 DeviceSettingsService::Get()->SetSessionManager(
112 &device_settings_test_helper_
, owner_key_util_
);
114 CrosSettings::Initialize();
117 void TearDown() override
{
118 oauth2_service_
.reset();
119 CrosSettings::Shutdown();
120 TestingBrowserProcess::GetGlobal()->SetBrowserPolicyConnector(NULL
);
121 content::BrowserThread::GetBlockingPool()->FlushForTesting();
122 DeviceSettingsService::Get()->UnsetSessionManager();
123 DeviceSettingsService::Shutdown();
124 SystemSaltGetter::Shutdown();
125 DBusThreadManager::Shutdown();
126 base::RunLoop().RunUntilIdle();
129 void CreateService() {
130 oauth2_service_
.reset(new DeviceOAuth2TokenService(
131 request_context_getter_
.get(), scoped_testing_local_state_
.Get()));
132 oauth2_service_
->max_refresh_token_validation_retries_
= 0;
133 oauth2_service_
->set_max_authorization_token_fetch_retries_for_testing(0);
136 // Utility method to set a value in Local State for the device refresh token
137 // (it must have a non-empty value or it won't be used).
138 void SetDeviceRefreshTokenInLocalState(const std::string
& refresh_token
) {
139 scoped_testing_local_state_
.Get()->SetUserPref(
140 prefs::kDeviceRobotAnyApiRefreshToken
,
141 new base::StringValue(refresh_token
));
144 std::string
GetValidTokenInfoResponse(const std::string email
) {
145 return "{ \"email\": \"" + email
+ "\","
146 " \"user_id\": \"1234567890\" }";
149 bool RefreshTokenIsAvailable() {
150 return oauth2_service_
->RefreshTokenIsAvailable(
151 oauth2_service_
->GetRobotAccountId());
154 std::string
GetRefreshToken() {
155 if (!RefreshTokenIsAvailable())
156 return std::string();
158 return oauth2_service_
->GetRefreshToken(
159 oauth2_service_
->GetRobotAccountId());
162 // A utility method to return fake URL results, for testing the refresh token
163 // validation logic. For a successful validation attempt, this method will be
164 // called three times for the steps listed below (steps 1 and 2 happen in
167 // Step 1a: fetch the access token for the tokeninfo API.
168 // Step 1b: call the tokeninfo API.
169 // Step 2: Fetch the access token for the requested scope
170 // (in this case, cloudprint).
171 void ReturnOAuthUrlFetchResults(int fetcher_id
,
172 net::HttpStatusCode response_code
,
173 const std::string
& response_string
);
175 // Generates URL fetch replies with the specified results for requests
176 // generated by the token service.
177 void PerformURLFetchesWithResults(
178 net::HttpStatusCode tokeninfo_access_token_status
,
179 const std::string
& tokeninfo_access_token_response
,
180 net::HttpStatusCode tokeninfo_fetch_status
,
181 const std::string
& tokeninfo_fetch_response
,
182 net::HttpStatusCode service_access_token_status
,
183 const std::string
& service_access_token_response
);
185 // Generates URL fetch replies for the success path.
186 void PerformURLFetches();
188 void AssertConsumerTokensAndErrors(int num_tokens
, int num_errors
);
191 // This is here because DeviceOAuth2TokenService's destructor is private;
192 // base::DefaultDeleter therefore doesn't work. However, the test class is
193 // declared friend in DeviceOAuth2TokenService, so this deleter works.
194 struct TokenServiceDeleter
{
195 inline void operator()(DeviceOAuth2TokenService
* ptr
) const {
200 base::MessageLoop message_loop_
;
201 ScopedTestingLocalState scoped_testing_local_state_
;
202 scoped_refptr
<net::TestURLRequestContextGetter
> request_context_getter_
;
203 net::TestURLFetcherFactory factory_
;
204 FakeCryptohomeClient
* fake_cryptohome_client_
;
205 DeviceSettingsTestHelper device_settings_test_helper_
;
206 policy::DevicePolicyBuilder device_policy_
;
207 scoped_ptr
<DeviceOAuth2TokenService
, TokenServiceDeleter
> oauth2_service_
;
208 TestingOAuth2TokenServiceConsumer consumer_
;
211 void DeviceOAuth2TokenServiceTest::ReturnOAuthUrlFetchResults(
213 net::HttpStatusCode response_code
,
214 const std::string
& response_string
) {
215 net::TestURLFetcher
* fetcher
= factory_
.GetFetcherByID(fetcher_id
);
217 factory_
.RemoveFetcherFromMap(fetcher_id
);
218 fetcher
->set_response_code(response_code
);
219 fetcher
->SetResponseString(response_string
);
220 fetcher
->delegate()->OnURLFetchComplete(fetcher
);
221 base::RunLoop().RunUntilIdle();
225 void DeviceOAuth2TokenServiceTest::PerformURLFetchesWithResults(
226 net::HttpStatusCode tokeninfo_access_token_status
,
227 const std::string
& tokeninfo_access_token_response
,
228 net::HttpStatusCode tokeninfo_fetch_status
,
229 const std::string
& tokeninfo_fetch_response
,
230 net::HttpStatusCode service_access_token_status
,
231 const std::string
& service_access_token_response
) {
232 ReturnOAuthUrlFetchResults(
233 kValidatorUrlFetcherId
,
234 tokeninfo_access_token_status
,
235 tokeninfo_access_token_response
);
237 ReturnOAuthUrlFetchResults(
238 kValidatorUrlFetcherId
,
239 tokeninfo_fetch_status
,
240 tokeninfo_fetch_response
);
242 ReturnOAuthUrlFetchResults(
243 kOAuthTokenServiceUrlFetcherId
,
244 service_access_token_status
,
245 service_access_token_response
);
248 void DeviceOAuth2TokenServiceTest::PerformURLFetches() {
249 PerformURLFetchesWithResults(
250 net::HTTP_OK
, GetValidTokenResponse("tokeninfo_access_token", 3600),
251 net::HTTP_OK
, GetValidTokenInfoResponse("service_acct@g.com"),
252 net::HTTP_OK
, GetValidTokenResponse("scoped_access_token", 3600));
255 void DeviceOAuth2TokenServiceTest::AssertConsumerTokensAndErrors(
258 EXPECT_EQ(num_tokens
, consumer_
.number_of_successful_tokens_
);
259 EXPECT_EQ(num_errors
, consumer_
.number_of_errors_
);
262 TEST_F(DeviceOAuth2TokenServiceTest
, SaveEncryptedToken
) {
265 oauth2_service_
->SetAndSaveRefreshToken(
266 "test-token", DeviceOAuth2TokenService::StatusCallback());
267 EXPECT_EQ("test-token", GetRefreshToken());
270 TEST_F(DeviceOAuth2TokenServiceTest
, SaveEncryptedTokenEarly
) {
271 // Set a new refresh token without the system salt available.
272 SetUpWithPendingSalt();
274 oauth2_service_
->SetAndSaveRefreshToken(
275 "test-token", DeviceOAuth2TokenService::StatusCallback());
276 EXPECT_EQ("test-token", GetRefreshToken());
278 // Make the system salt available.
279 fake_cryptohome_client_
->set_system_salt(
280 FakeCryptohomeClient::GetStubSystemSalt());
281 fake_cryptohome_client_
->SetServiceIsAvailable(true);
282 base::RunLoop().RunUntilIdle();
284 // The original token should still be present.
285 EXPECT_EQ("test-token", GetRefreshToken());
287 // Reloading shouldn't change the token either.
289 base::RunLoop().RunUntilIdle();
290 EXPECT_EQ("test-token", GetRefreshToken());
293 TEST_F(DeviceOAuth2TokenServiceTest
, RefreshTokenValidation_Success
) {
294 SetUpDefaultValues();
295 scoped_ptr
<OAuth2TokenService::Request
> request
= StartTokenRequest();
298 AssertConsumerTokensAndErrors(1, 0);
300 EXPECT_EQ("scoped_access_token", consumer_
.last_token_
);
303 TEST_F(DeviceOAuth2TokenServiceTest
, RefreshTokenValidation_SuccessAsyncLoad
) {
304 SetUpWithPendingSalt();
306 scoped_ptr
<OAuth2TokenService::Request
> request
= StartTokenRequest();
308 AssertConsumerTokensAndErrors(0, 0);
310 fake_cryptohome_client_
->set_system_salt(
311 FakeCryptohomeClient::GetStubSystemSalt());
312 fake_cryptohome_client_
->SetServiceIsAvailable(true);
313 base::RunLoop().RunUntilIdle();
316 AssertConsumerTokensAndErrors(1, 0);
318 EXPECT_EQ("scoped_access_token", consumer_
.last_token_
);
321 TEST_F(DeviceOAuth2TokenServiceTest
, RefreshTokenValidation_Cancel
) {
322 SetUpDefaultValues();
323 scoped_ptr
<OAuth2TokenService::Request
> request
= StartTokenRequest();
328 // Test succeeds if this line is reached without a crash.
331 TEST_F(DeviceOAuth2TokenServiceTest
, RefreshTokenValidation_NoSalt
) {
332 fake_cryptohome_client_
->set_system_salt(std::vector
<uint8
>());
333 fake_cryptohome_client_
->SetServiceIsAvailable(true);
334 SetUpDefaultValues();
336 EXPECT_FALSE(RefreshTokenIsAvailable());
338 scoped_ptr
<OAuth2TokenService::Request
> request
= StartTokenRequest();
339 base::RunLoop().RunUntilIdle();
341 AssertConsumerTokensAndErrors(0, 1);
344 TEST_F(DeviceOAuth2TokenServiceTest
,
345 RefreshTokenValidation_Failure_TokenInfoAccessTokenHttpError
) {
346 SetUpDefaultValues();
347 scoped_ptr
<OAuth2TokenService::Request
> request
= StartTokenRequest();
349 PerformURLFetchesWithResults(
350 net::HTTP_UNAUTHORIZED
, "",
351 net::HTTP_OK
, GetValidTokenInfoResponse("service_acct@g.com"),
352 net::HTTP_OK
, GetValidTokenResponse("ignored", 3600));
354 AssertConsumerTokensAndErrors(0, 1);
357 TEST_F(DeviceOAuth2TokenServiceTest
,
358 RefreshTokenValidation_Failure_TokenInfoAccessTokenInvalidResponse
) {
359 SetUpDefaultValues();
360 scoped_ptr
<OAuth2TokenService::Request
> request
= StartTokenRequest();
362 PerformURLFetchesWithResults(
363 net::HTTP_OK
, "invalid response",
364 net::HTTP_OK
, GetValidTokenInfoResponse("service_acct@g.com"),
365 net::HTTP_OK
, GetValidTokenResponse("ignored", 3600));
367 AssertConsumerTokensAndErrors(0, 1);
370 TEST_F(DeviceOAuth2TokenServiceTest
,
371 RefreshTokenValidation_Failure_TokenInfoApiCallHttpError
) {
372 SetUpDefaultValues();
373 scoped_ptr
<OAuth2TokenService::Request
> request
= StartTokenRequest();
375 PerformURLFetchesWithResults(
376 net::HTTP_OK
, GetValidTokenResponse("tokeninfo_access_token", 3600),
377 net::HTTP_INTERNAL_SERVER_ERROR
, "",
378 net::HTTP_OK
, GetValidTokenResponse("ignored", 3600));
380 AssertConsumerTokensAndErrors(0, 1);
383 TEST_F(DeviceOAuth2TokenServiceTest
,
384 RefreshTokenValidation_Failure_TokenInfoApiCallInvalidResponse
) {
385 SetUpDefaultValues();
386 scoped_ptr
<OAuth2TokenService::Request
> request
= StartTokenRequest();
388 PerformURLFetchesWithResults(
389 net::HTTP_OK
, GetValidTokenResponse("tokeninfo_access_token", 3600),
390 net::HTTP_OK
, "invalid response",
391 net::HTTP_OK
, GetValidTokenResponse("ignored", 3600));
393 AssertConsumerTokensAndErrors(0, 1);
396 TEST_F(DeviceOAuth2TokenServiceTest
,
397 RefreshTokenValidation_Failure_CloudPrintAccessTokenHttpError
) {
398 SetUpDefaultValues();
399 scoped_ptr
<OAuth2TokenService::Request
> request
= StartTokenRequest();
401 PerformURLFetchesWithResults(
402 net::HTTP_OK
, GetValidTokenResponse("tokeninfo_access_token", 3600),
403 net::HTTP_OK
, GetValidTokenInfoResponse("service_acct@g.com"),
404 net::HTTP_BAD_REQUEST
, "");
406 AssertConsumerTokensAndErrors(0, 1);
409 TEST_F(DeviceOAuth2TokenServiceTest
,
410 RefreshTokenValidation_Failure_CloudPrintAccessTokenInvalidResponse
) {
411 SetUpDefaultValues();
412 scoped_ptr
<OAuth2TokenService::Request
> request
= StartTokenRequest();
414 PerformURLFetchesWithResults(
415 net::HTTP_OK
, GetValidTokenResponse("tokeninfo_access_token", 3600),
416 net::HTTP_OK
, GetValidTokenInfoResponse("service_acct@g.com"),
417 net::HTTP_OK
, "invalid request");
419 AssertConsumerTokensAndErrors(0, 1);
422 TEST_F(DeviceOAuth2TokenServiceTest
, RefreshTokenValidation_Failure_BadOwner
) {
423 SetUpDefaultValues();
424 scoped_ptr
<OAuth2TokenService::Request
> request
= StartTokenRequest();
426 SetRobotAccountId("WRONG_service_acct@g.com");
428 PerformURLFetchesWithResults(
429 net::HTTP_OK
, GetValidTokenResponse("tokeninfo_access_token", 3600),
430 net::HTTP_OK
, GetValidTokenInfoResponse("service_acct@g.com"),
431 net::HTTP_OK
, GetValidTokenResponse("ignored", 3600));
433 AssertConsumerTokensAndErrors(0, 1);
436 TEST_F(DeviceOAuth2TokenServiceTest
, RefreshTokenValidation_Retry
) {
437 SetUpDefaultValues();
438 scoped_ptr
<OAuth2TokenService::Request
> request
= StartTokenRequest();
440 PerformURLFetchesWithResults(
441 net::HTTP_INTERNAL_SERVER_ERROR
, "",
442 net::HTTP_OK
, GetValidTokenInfoResponse("service_acct@g.com"),
443 net::HTTP_OK
, GetValidTokenResponse("ignored", 3600));
445 AssertConsumerTokensAndErrors(0, 1);
447 // Retry should succeed.
448 request
= StartTokenRequest();
450 AssertConsumerTokensAndErrors(1, 1);
453 TEST_F(DeviceOAuth2TokenServiceTest
, DoNotAnnounceTokenWithoutAccountID
) {
456 testing::StrictMock
<MockOAuth2TokenServiceObserver
> observer
;
457 oauth2_service_
->AddObserver(&observer
);
459 // Make a token available during enrollment. Verify that the token is not
461 oauth2_service_
->SetAndSaveRefreshToken(
462 "test-token", DeviceOAuth2TokenService::StatusCallback());
463 testing::Mock::VerifyAndClearExpectations(&observer
);
465 // Also make the robot account ID available. Verify that the token is
467 EXPECT_CALL(observer
, OnRefreshTokenAvailable("robot@example.com"));
468 SetRobotAccountId("robot@example.com");
469 testing::Mock::VerifyAndClearExpectations(&observer
);
471 oauth2_service_
->RemoveObserver(&observer
);
474 } // namespace chromeos