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