Home | History | Annotate | Download | only in settings
      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 
     33 namespace chromeos {
     34 
     35 static const int kOAuthTokenServiceUrlFetcherId = 0;
     36 static const int kValidatorUrlFetcherId = gaia::GaiaOAuthClient::kUrlFetcherId;
     37 
     38 class 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 (at) 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 
    191 void 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 
    205 void 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 
    228 void DeviceOAuth2TokenServiceTest::PerformURLFetches() {
    229   PerformURLFetchesWithResults(
    230       net::HTTP_OK, GetValidTokenResponse("tokeninfo_access_token", 3600),
    231       net::HTTP_OK, GetValidTokenInfoResponse("service_acct (at) g.com"),
    232       net::HTTP_OK, GetValidTokenResponse("scoped_access_token", 3600));
    233 }
    234 
    235 void 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 
    242 TEST_F(DeviceOAuth2TokenServiceTest, SaveEncryptedToken) {
    243   CreateService();
    244 
    245   oauth2_service_->SetAndSaveRefreshToken(
    246       "test-token", DeviceOAuth2TokenService::StatusCallback());
    247   EXPECT_EQ("test-token", GetRefreshToken());
    248 }
    249 
    250 TEST_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 
    273 TEST_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 
    283 TEST_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 
    301 TEST_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 
    311 TEST_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 
    324 TEST_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 (at) g.com"),
    332       net::HTTP_OK, GetValidTokenResponse("ignored", 3600));
    333 
    334   AssertConsumerTokensAndErrors(0, 1);
    335 }
    336 
    337 TEST_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 (at) g.com"),
    345       net::HTTP_OK, GetValidTokenResponse("ignored", 3600));
    346 
    347   AssertConsumerTokensAndErrors(0, 1);
    348 }
    349 
    350 TEST_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 
    363 TEST_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 
    376 TEST_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 (at) g.com"),
    384       net::HTTP_BAD_REQUEST, "");
    385 
    386   AssertConsumerTokensAndErrors(0, 1);
    387 }
    388 
    389 TEST_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 (at) g.com"),
    397       net::HTTP_OK, "invalid request");
    398 
    399   AssertConsumerTokensAndErrors(0, 1);
    400 }
    401 
    402 TEST_F(DeviceOAuth2TokenServiceTest, RefreshTokenValidation_Failure_BadOwner) {
    403   SetUpDefaultValues();
    404   scoped_ptr<OAuth2TokenService::Request> request = StartTokenRequest();
    405 
    406   SetRobotAccountId("WRONG_service_acct (at) g.com");
    407 
    408   PerformURLFetchesWithResults(
    409       net::HTTP_OK, GetValidTokenResponse("tokeninfo_access_token", 3600),
    410       net::HTTP_OK, GetValidTokenInfoResponse("service_acct (at) g.com"),
    411       net::HTTP_OK, GetValidTokenResponse("ignored", 3600));
    412 
    413   AssertConsumerTokensAndErrors(0, 1);
    414 }
    415 
    416 TEST_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 (at) 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