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