device_oauth2_token_service_unittest.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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. 4 5#include "chrome/browser/chromeos/settings/device_oauth2_token_service.h" 6 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/gtest/include/gtest/gtest.h" 32 33namespace chromeos { 34 35static const int kOAuthTokenServiceUrlFetcherId = 0; 36static const int kValidatorUrlFetcherId = gaia::GaiaOAuthClient::kUrlFetcherId; 37 38class DeviceOAuth2TokenServiceTest : public testing::Test { 39 public: 40 DeviceOAuth2TokenServiceTest() 41 : scoped_testing_local_state_(TestingBrowserProcess::GetGlobal()), 42 request_context_getter_(new net::TestURLRequestContextGetter( 43 message_loop_.message_loop_proxy())) {} 44 virtual ~DeviceOAuth2TokenServiceTest() {} 45 46 // Most tests just want a noop crypto impl with a dummy refresh token value in 47 // Local State (if the value is an empty string, it will be ignored). 48 void SetUpDefaultValues() { 49 SetDeviceRefreshTokenInLocalState("device_refresh_token_4_test"); 50 SetRobotAccountId("service_acct@g.com"); 51 CreateService(); 52 AssertConsumerTokensAndErrors(0, 0); 53 54 base::RunLoop().RunUntilIdle(); 55 } 56 57 void SetUpWithPendingSalt() { 58 fake_cryptohome_client_->set_system_salt(std::vector<uint8>()); 59 fake_cryptohome_client_->SetServiceIsAvailable(false); 60 SetUpDefaultValues(); 61 } 62 63 void SetRobotAccountId(const std::string& account_id) { 64 device_policy_.policy_data().set_service_account_identity(account_id); 65 device_policy_.Build(); 66 device_settings_test_helper_.set_policy_blob(device_policy_.GetBlob()); 67 DeviceSettingsService::Get()->Load(); 68 device_settings_test_helper_.Flush(); 69 } 70 71 scoped_ptr<OAuth2TokenService::Request> StartTokenRequest() { 72 return oauth2_service_->StartRequest(oauth2_service_->GetRobotAccountId(), 73 std::set<std::string>(), 74 &consumer_); 75 } 76 77 virtual void SetUp() OVERRIDE { 78 fake_cryptohome_client_ = new FakeCryptohomeClient; 79 fake_cryptohome_client_->SetServiceIsAvailable(true); 80 fake_cryptohome_client_->set_system_salt( 81 FakeCryptohomeClient::GetStubSystemSalt()); 82 chromeos::DBusThreadManager::GetSetterForTesting()->SetCryptohomeClient( 83 scoped_ptr<CryptohomeClient>(fake_cryptohome_client_)); 84 85 SystemSaltGetter::Initialize(); 86 87 DeviceSettingsService::Initialize(); 88 scoped_refptr<ownership::MockOwnerKeyUtil> owner_key_util_( 89 new ownership::MockOwnerKeyUtil()); 90 owner_key_util_->SetPublicKeyFromPrivateKey( 91 *device_policy_.GetSigningKey()); 92 DeviceSettingsService::Get()->SetSessionManager( 93 &device_settings_test_helper_, owner_key_util_); 94 95 CrosSettings::Initialize(); 96 } 97 98 virtual void TearDown() OVERRIDE { 99 CrosSettings::Shutdown(); 100 TestingBrowserProcess::GetGlobal()->SetBrowserPolicyConnector(NULL); 101 content::BrowserThread::GetBlockingPool()->FlushForTesting(); 102 DeviceSettingsService::Get()->UnsetSessionManager(); 103 DeviceSettingsService::Shutdown(); 104 SystemSaltGetter::Shutdown(); 105 DBusThreadManager::Shutdown(); 106 base::RunLoop().RunUntilIdle(); 107 } 108 109 void CreateService() { 110 oauth2_service_.reset(new DeviceOAuth2TokenService( 111 request_context_getter_.get(), scoped_testing_local_state_.Get())); 112 oauth2_service_->max_refresh_token_validation_retries_ = 0; 113 oauth2_service_->set_max_authorization_token_fetch_retries_for_testing(0); 114 } 115 116 // Utility method to set a value in Local State for the device refresh token 117 // (it must have a non-empty value or it won't be used). 118 void SetDeviceRefreshTokenInLocalState(const std::string& refresh_token) { 119 scoped_testing_local_state_.Get()->SetUserPref( 120 prefs::kDeviceRobotAnyApiRefreshToken, 121 new base::StringValue(refresh_token)); 122 } 123 124 std::string GetValidTokenInfoResponse(const std::string email) { 125 return "{ \"email\": \"" + email + "\"," 126 " \"user_id\": \"1234567890\" }"; 127 } 128 129 bool RefreshTokenIsAvailable() { 130 return oauth2_service_->RefreshTokenIsAvailable( 131 oauth2_service_->GetRobotAccountId()); 132 } 133 134 std::string GetRefreshToken() { 135 if (!RefreshTokenIsAvailable()) 136 return std::string(); 137 138 return oauth2_service_->GetRefreshToken( 139 oauth2_service_->GetRobotAccountId()); 140 } 141 142 // A utility method to return fake URL results, for testing the refresh token 143 // validation logic. For a successful validation attempt, this method will be 144 // called three times for the steps listed below (steps 1 and 2 happen in 145 // parallel). 146 // 147 // Step 1a: fetch the access token for the tokeninfo API. 148 // Step 1b: call the tokeninfo API. 149 // Step 2: Fetch the access token for the requested scope 150 // (in this case, cloudprint). 151 void ReturnOAuthUrlFetchResults(int fetcher_id, 152 net::HttpStatusCode response_code, 153 const std::string& response_string); 154 155 // Generates URL fetch replies with the specified results for requests 156 // generated by the token service. 157 void PerformURLFetchesWithResults( 158 net::HttpStatusCode tokeninfo_access_token_status, 159 const std::string& tokeninfo_access_token_response, 160 net::HttpStatusCode tokeninfo_fetch_status, 161 const std::string& tokeninfo_fetch_response, 162 net::HttpStatusCode service_access_token_status, 163 const std::string& service_access_token_response); 164 165 // Generates URL fetch replies for the success path. 166 void PerformURLFetches(); 167 168 void AssertConsumerTokensAndErrors(int num_tokens, int num_errors); 169 170 protected: 171 // This is here because DeviceOAuth2TokenService's destructor is private; 172 // base::DefaultDeleter therefore doesn't work. However, the test class is 173 // declared friend in DeviceOAuth2TokenService, so this deleter works. 174 struct TokenServiceDeleter { 175 inline void operator()(DeviceOAuth2TokenService* ptr) const { 176 delete ptr; 177 } 178 }; 179 180 base::MessageLoop message_loop_; 181 ScopedTestingLocalState scoped_testing_local_state_; 182 scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_; 183 net::TestURLFetcherFactory factory_; 184 FakeCryptohomeClient* fake_cryptohome_client_; 185 DeviceSettingsTestHelper device_settings_test_helper_; 186 policy::DevicePolicyBuilder device_policy_; 187 scoped_ptr<DeviceOAuth2TokenService, TokenServiceDeleter> oauth2_service_; 188 TestingOAuth2TokenServiceConsumer consumer_; 189}; 190 191void DeviceOAuth2TokenServiceTest::ReturnOAuthUrlFetchResults( 192 int fetcher_id, 193 net::HttpStatusCode response_code, 194 const std::string& response_string) { 195 net::TestURLFetcher* fetcher = factory_.GetFetcherByID(fetcher_id); 196 if (fetcher) { 197 factory_.RemoveFetcherFromMap(fetcher_id); 198 fetcher->set_response_code(response_code); 199 fetcher->SetResponseString(response_string); 200 fetcher->delegate()->OnURLFetchComplete(fetcher); 201 base::RunLoop().RunUntilIdle(); 202 } 203} 204 205void DeviceOAuth2TokenServiceTest::PerformURLFetchesWithResults( 206 net::HttpStatusCode tokeninfo_access_token_status, 207 const std::string& tokeninfo_access_token_response, 208 net::HttpStatusCode tokeninfo_fetch_status, 209 const std::string& tokeninfo_fetch_response, 210 net::HttpStatusCode service_access_token_status, 211 const std::string& service_access_token_response) { 212 ReturnOAuthUrlFetchResults( 213 kValidatorUrlFetcherId, 214 tokeninfo_access_token_status, 215 tokeninfo_access_token_response); 216 217 ReturnOAuthUrlFetchResults( 218 kValidatorUrlFetcherId, 219 tokeninfo_fetch_status, 220 tokeninfo_fetch_response); 221 222 ReturnOAuthUrlFetchResults( 223 kOAuthTokenServiceUrlFetcherId, 224 service_access_token_status, 225 service_access_token_response); 226} 227 228void DeviceOAuth2TokenServiceTest::PerformURLFetches() { 229 PerformURLFetchesWithResults( 230 net::HTTP_OK, GetValidTokenResponse("tokeninfo_access_token", 3600), 231 net::HTTP_OK, GetValidTokenInfoResponse("service_acct@g.com"), 232 net::HTTP_OK, GetValidTokenResponse("scoped_access_token", 3600)); 233} 234 235void DeviceOAuth2TokenServiceTest::AssertConsumerTokensAndErrors( 236 int num_tokens, 237 int num_errors) { 238 EXPECT_EQ(num_tokens, consumer_.number_of_successful_tokens_); 239 EXPECT_EQ(num_errors, consumer_.number_of_errors_); 240} 241 242TEST_F(DeviceOAuth2TokenServiceTest, SaveEncryptedToken) { 243 CreateService(); 244 245 oauth2_service_->SetAndSaveRefreshToken( 246 "test-token", DeviceOAuth2TokenService::StatusCallback()); 247 EXPECT_EQ("test-token", GetRefreshToken()); 248} 249 250TEST_F(DeviceOAuth2TokenServiceTest, SaveEncryptedTokenEarly) { 251 // Set a new refresh token without the system salt available. 252 SetUpWithPendingSalt(); 253 254 oauth2_service_->SetAndSaveRefreshToken( 255 "test-token", DeviceOAuth2TokenService::StatusCallback()); 256 EXPECT_EQ("test-token", GetRefreshToken()); 257 258 // Make the system salt available. 259 fake_cryptohome_client_->set_system_salt( 260 FakeCryptohomeClient::GetStubSystemSalt()); 261 fake_cryptohome_client_->SetServiceIsAvailable(true); 262 base::RunLoop().RunUntilIdle(); 263 264 // The original token should still be present. 265 EXPECT_EQ("test-token", GetRefreshToken()); 266 267 // Reloading shouldn't change the token either. 268 CreateService(); 269 base::RunLoop().RunUntilIdle(); 270 EXPECT_EQ("test-token", GetRefreshToken()); 271} 272 273TEST_F(DeviceOAuth2TokenServiceTest, RefreshTokenValidation_Success) { 274 SetUpDefaultValues(); 275 scoped_ptr<OAuth2TokenService::Request> request = StartTokenRequest(); 276 277 PerformURLFetches(); 278 AssertConsumerTokensAndErrors(1, 0); 279 280 EXPECT_EQ("scoped_access_token", consumer_.last_token_); 281} 282 283TEST_F(DeviceOAuth2TokenServiceTest, RefreshTokenValidation_SuccessAsyncLoad) { 284 SetUpWithPendingSalt(); 285 286 scoped_ptr<OAuth2TokenService::Request> request = StartTokenRequest(); 287 PerformURLFetches(); 288 AssertConsumerTokensAndErrors(0, 0); 289 290 fake_cryptohome_client_->set_system_salt( 291 FakeCryptohomeClient::GetStubSystemSalt()); 292 fake_cryptohome_client_->SetServiceIsAvailable(true); 293 base::RunLoop().RunUntilIdle(); 294 295 PerformURLFetches(); 296 AssertConsumerTokensAndErrors(1, 0); 297 298 EXPECT_EQ("scoped_access_token", consumer_.last_token_); 299} 300 301TEST_F(DeviceOAuth2TokenServiceTest, RefreshTokenValidation_Cancel) { 302 SetUpDefaultValues(); 303 scoped_ptr<OAuth2TokenService::Request> request = StartTokenRequest(); 304 request.reset(); 305 306 PerformURLFetches(); 307 308 // Test succeeds if this line is reached without a crash. 309} 310 311TEST_F(DeviceOAuth2TokenServiceTest, RefreshTokenValidation_NoSalt) { 312 fake_cryptohome_client_->set_system_salt(std::vector<uint8>()); 313 fake_cryptohome_client_->SetServiceIsAvailable(true); 314 SetUpDefaultValues(); 315 316 EXPECT_FALSE(RefreshTokenIsAvailable()); 317 318 scoped_ptr<OAuth2TokenService::Request> request = StartTokenRequest(); 319 base::RunLoop().RunUntilIdle(); 320 321 AssertConsumerTokensAndErrors(0, 1); 322} 323 324TEST_F(DeviceOAuth2TokenServiceTest, 325 RefreshTokenValidation_Failure_TokenInfoAccessTokenHttpError) { 326 SetUpDefaultValues(); 327 scoped_ptr<OAuth2TokenService::Request> request = StartTokenRequest(); 328 329 PerformURLFetchesWithResults( 330 net::HTTP_UNAUTHORIZED, "", 331 net::HTTP_OK, GetValidTokenInfoResponse("service_acct@g.com"), 332 net::HTTP_OK, GetValidTokenResponse("ignored", 3600)); 333 334 AssertConsumerTokensAndErrors(0, 1); 335} 336 337TEST_F(DeviceOAuth2TokenServiceTest, 338 RefreshTokenValidation_Failure_TokenInfoAccessTokenInvalidResponse) { 339 SetUpDefaultValues(); 340 scoped_ptr<OAuth2TokenService::Request> request = StartTokenRequest(); 341 342 PerformURLFetchesWithResults( 343 net::HTTP_OK, "invalid response", 344 net::HTTP_OK, GetValidTokenInfoResponse("service_acct@g.com"), 345 net::HTTP_OK, GetValidTokenResponse("ignored", 3600)); 346 347 AssertConsumerTokensAndErrors(0, 1); 348} 349 350TEST_F(DeviceOAuth2TokenServiceTest, 351 RefreshTokenValidation_Failure_TokenInfoApiCallHttpError) { 352 SetUpDefaultValues(); 353 scoped_ptr<OAuth2TokenService::Request> request = StartTokenRequest(); 354 355 PerformURLFetchesWithResults( 356 net::HTTP_OK, GetValidTokenResponse("tokeninfo_access_token", 3600), 357 net::HTTP_INTERNAL_SERVER_ERROR, "", 358 net::HTTP_OK, GetValidTokenResponse("ignored", 3600)); 359 360 AssertConsumerTokensAndErrors(0, 1); 361} 362 363TEST_F(DeviceOAuth2TokenServiceTest, 364 RefreshTokenValidation_Failure_TokenInfoApiCallInvalidResponse) { 365 SetUpDefaultValues(); 366 scoped_ptr<OAuth2TokenService::Request> request = StartTokenRequest(); 367 368 PerformURLFetchesWithResults( 369 net::HTTP_OK, GetValidTokenResponse("tokeninfo_access_token", 3600), 370 net::HTTP_OK, "invalid response", 371 net::HTTP_OK, GetValidTokenResponse("ignored", 3600)); 372 373 AssertConsumerTokensAndErrors(0, 1); 374} 375 376TEST_F(DeviceOAuth2TokenServiceTest, 377 RefreshTokenValidation_Failure_CloudPrintAccessTokenHttpError) { 378 SetUpDefaultValues(); 379 scoped_ptr<OAuth2TokenService::Request> request = StartTokenRequest(); 380 381 PerformURLFetchesWithResults( 382 net::HTTP_OK, GetValidTokenResponse("tokeninfo_access_token", 3600), 383 net::HTTP_OK, GetValidTokenInfoResponse("service_acct@g.com"), 384 net::HTTP_BAD_REQUEST, ""); 385 386 AssertConsumerTokensAndErrors(0, 1); 387} 388 389TEST_F(DeviceOAuth2TokenServiceTest, 390 RefreshTokenValidation_Failure_CloudPrintAccessTokenInvalidResponse) { 391 SetUpDefaultValues(); 392 scoped_ptr<OAuth2TokenService::Request> request = StartTokenRequest(); 393 394 PerformURLFetchesWithResults( 395 net::HTTP_OK, GetValidTokenResponse("tokeninfo_access_token", 3600), 396 net::HTTP_OK, GetValidTokenInfoResponse("service_acct@g.com"), 397 net::HTTP_OK, "invalid request"); 398 399 AssertConsumerTokensAndErrors(0, 1); 400} 401 402TEST_F(DeviceOAuth2TokenServiceTest, RefreshTokenValidation_Failure_BadOwner) { 403 SetUpDefaultValues(); 404 scoped_ptr<OAuth2TokenService::Request> request = StartTokenRequest(); 405 406 SetRobotAccountId("WRONG_service_acct@g.com"); 407 408 PerformURLFetchesWithResults( 409 net::HTTP_OK, GetValidTokenResponse("tokeninfo_access_token", 3600), 410 net::HTTP_OK, GetValidTokenInfoResponse("service_acct@g.com"), 411 net::HTTP_OK, GetValidTokenResponse("ignored", 3600)); 412 413 AssertConsumerTokensAndErrors(0, 1); 414} 415 416TEST_F(DeviceOAuth2TokenServiceTest, RefreshTokenValidation_Retry) { 417 SetUpDefaultValues(); 418 scoped_ptr<OAuth2TokenService::Request> request = StartTokenRequest(); 419 420 PerformURLFetchesWithResults( 421 net::HTTP_INTERNAL_SERVER_ERROR, "", 422 net::HTTP_OK, GetValidTokenInfoResponse("service_acct@g.com"), 423 net::HTTP_OK, GetValidTokenResponse("ignored", 3600)); 424 425 AssertConsumerTokensAndErrors(0, 1); 426 427 // Retry should succeed. 428 request = StartTokenRequest(); 429 PerformURLFetches(); 430 AssertConsumerTokensAndErrors(1, 1); 431} 432 433} // namespace chromeos 434