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 "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"
33 static const int kOAuthTokenServiceUrlFetcherId = 0;
34 static const int kValidatorUrlFetcherId = gaia::GaiaOAuthClient::kUrlFetcherId;
36 class DeviceOAuth2TokenServiceTest : public testing::Test {
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() {}
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");
50 AssertConsumerTokensAndErrors(0, 0);
52 base::RunLoop().RunUntilIdle();
55 void SetUpWithPendingSalt() {
56 fake_cryptohome_client_->set_system_salt(std::vector<uint8>());
57 fake_cryptohome_client_->SetServiceIsAvailable(false);
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();
69 scoped_ptr<OAuth2TokenService::Request> StartTokenRequest() {
70 return oauth2_service_->StartRequest(oauth2_service_->GetRobotAccountId(),
71 std::set<std::string>(),
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_));
85 DBusThreadManager::InitializeForTesting(fake_dbus_thread_manager.release());
87 SystemSaltGetter::Initialize();
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_);
96 CrosSettings::Initialize();
99 virtual void TearDown() OVERRIDE {
100 CrosSettings::Shutdown();
101 TestingBrowserProcess::GetGlobal()->SetBrowserPolicyConnector(NULL);
102 DeviceSettingsService::Get()->UnsetSessionManager();
103 DeviceSettingsService::Shutdown();
104 SystemSaltGetter::Shutdown();
105 DBusThreadManager::Shutdown();
106 base::RunLoop().RunUntilIdle();
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);
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));
124 std::string GetValidTokenInfoResponse(const std::string email) {
125 return "{ \"email\": \"" + email + "\","
126 " \"user_id\": \"1234567890\" }";
129 bool RefreshTokenIsAvailable() {
130 return oauth2_service_->RefreshTokenIsAvailable(
131 oauth2_service_->GetRobotAccountId());
134 std::string GetRefreshToken() {
135 if (!RefreshTokenIsAvailable())
136 return std::string();
138 return oauth2_service_->GetRefreshToken(
139 oauth2_service_->GetRobotAccountId());
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
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);
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);
165 // Generates URL fetch replies for the success path.
166 void PerformURLFetches();
168 void AssertConsumerTokensAndErrors(int num_tokens, int num_errors);
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 {
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_;
191 void DeviceOAuth2TokenServiceTest::ReturnOAuthUrlFetchResults(
193 net::HttpStatusCode response_code,
194 const std::string& response_string) {
195 net::TestURLFetcher* fetcher = factory_.GetFetcherByID(fetcher_id);
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();
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);
217 ReturnOAuthUrlFetchResults(
218 kValidatorUrlFetcherId,
219 tokeninfo_fetch_status,
220 tokeninfo_fetch_response);
222 ReturnOAuthUrlFetchResults(
223 kOAuthTokenServiceUrlFetcherId,
224 service_access_token_status,
225 service_access_token_response);
228 void 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));
235 void DeviceOAuth2TokenServiceTest::AssertConsumerTokensAndErrors(
238 EXPECT_EQ(num_tokens, consumer_.number_of_successful_tokens_);
239 EXPECT_EQ(num_errors, consumer_.number_of_errors_);
242 TEST_F(DeviceOAuth2TokenServiceTest, SaveEncryptedToken) {
245 oauth2_service_->SetAndSaveRefreshToken(
246 "test-token", DeviceOAuth2TokenService::StatusCallback());
247 EXPECT_EQ("test-token", GetRefreshToken());
250 TEST_F(DeviceOAuth2TokenServiceTest, SaveEncryptedTokenEarly) {
251 // Set a new refresh token without the system salt available.
252 SetUpWithPendingSalt();
254 oauth2_service_->SetAndSaveRefreshToken(
255 "test-token", DeviceOAuth2TokenService::StatusCallback());
256 EXPECT_EQ("test-token", GetRefreshToken());
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();
264 // The original token should still be present.
265 EXPECT_EQ("test-token", GetRefreshToken());
267 // Reloading shouldn't change the token either.
269 base::RunLoop().RunUntilIdle();
270 EXPECT_EQ("test-token", GetRefreshToken());
273 TEST_F(DeviceOAuth2TokenServiceTest, RefreshTokenValidation_Success) {
274 SetUpDefaultValues();
275 scoped_ptr<OAuth2TokenService::Request> request = StartTokenRequest();
278 AssertConsumerTokensAndErrors(1, 0);
280 EXPECT_EQ("scoped_access_token", consumer_.last_token_);
283 TEST_F(DeviceOAuth2TokenServiceTest, RefreshTokenValidation_SuccessAsyncLoad) {
284 SetUpWithPendingSalt();
286 scoped_ptr<OAuth2TokenService::Request> request = StartTokenRequest();
288 AssertConsumerTokensAndErrors(0, 0);
290 fake_cryptohome_client_->set_system_salt(
291 FakeCryptohomeClient::GetStubSystemSalt());
292 fake_cryptohome_client_->SetServiceIsAvailable(true);
293 base::RunLoop().RunUntilIdle();
296 AssertConsumerTokensAndErrors(1, 0);
298 EXPECT_EQ("scoped_access_token", consumer_.last_token_);
301 TEST_F(DeviceOAuth2TokenServiceTest, RefreshTokenValidation_Cancel) {
302 SetUpDefaultValues();
303 scoped_ptr<OAuth2TokenService::Request> request = StartTokenRequest();
308 // Test succeeds if this line is reached without a crash.
311 TEST_F(DeviceOAuth2TokenServiceTest, RefreshTokenValidation_NoSalt) {
312 fake_cryptohome_client_->set_system_salt(std::vector<uint8>());
313 fake_cryptohome_client_->SetServiceIsAvailable(true);
314 SetUpDefaultValues();
316 EXPECT_FALSE(RefreshTokenIsAvailable());
318 scoped_ptr<OAuth2TokenService::Request> request = StartTokenRequest();
319 base::RunLoop().RunUntilIdle();
321 AssertConsumerTokensAndErrors(0, 1);
324 TEST_F(DeviceOAuth2TokenServiceTest,
325 RefreshTokenValidation_Failure_TokenInfoAccessTokenHttpError) {
326 SetUpDefaultValues();
327 scoped_ptr<OAuth2TokenService::Request> request = StartTokenRequest();
329 PerformURLFetchesWithResults(
330 net::HTTP_UNAUTHORIZED, "",
331 net::HTTP_OK, GetValidTokenInfoResponse("service_acct@g.com"),
332 net::HTTP_OK, GetValidTokenResponse("ignored", 3600));
334 AssertConsumerTokensAndErrors(0, 1);
337 TEST_F(DeviceOAuth2TokenServiceTest,
338 RefreshTokenValidation_Failure_TokenInfoAccessTokenInvalidResponse) {
339 SetUpDefaultValues();
340 scoped_ptr<OAuth2TokenService::Request> request = StartTokenRequest();
342 PerformURLFetchesWithResults(
343 net::HTTP_OK, "invalid response",
344 net::HTTP_OK, GetValidTokenInfoResponse("service_acct@g.com"),
345 net::HTTP_OK, GetValidTokenResponse("ignored", 3600));
347 AssertConsumerTokensAndErrors(0, 1);
350 TEST_F(DeviceOAuth2TokenServiceTest,
351 RefreshTokenValidation_Failure_TokenInfoApiCallHttpError) {
352 SetUpDefaultValues();
353 scoped_ptr<OAuth2TokenService::Request> request = StartTokenRequest();
355 PerformURLFetchesWithResults(
356 net::HTTP_OK, GetValidTokenResponse("tokeninfo_access_token", 3600),
357 net::HTTP_INTERNAL_SERVER_ERROR, "",
358 net::HTTP_OK, GetValidTokenResponse("ignored", 3600));
360 AssertConsumerTokensAndErrors(0, 1);
363 TEST_F(DeviceOAuth2TokenServiceTest,
364 RefreshTokenValidation_Failure_TokenInfoApiCallInvalidResponse) {
365 SetUpDefaultValues();
366 scoped_ptr<OAuth2TokenService::Request> request = StartTokenRequest();
368 PerformURLFetchesWithResults(
369 net::HTTP_OK, GetValidTokenResponse("tokeninfo_access_token", 3600),
370 net::HTTP_OK, "invalid response",
371 net::HTTP_OK, GetValidTokenResponse("ignored", 3600));
373 AssertConsumerTokensAndErrors(0, 1);
376 TEST_F(DeviceOAuth2TokenServiceTest,
377 RefreshTokenValidation_Failure_CloudPrintAccessTokenHttpError) {
378 SetUpDefaultValues();
379 scoped_ptr<OAuth2TokenService::Request> request = StartTokenRequest();
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, "");
386 AssertConsumerTokensAndErrors(0, 1);
389 TEST_F(DeviceOAuth2TokenServiceTest,
390 RefreshTokenValidation_Failure_CloudPrintAccessTokenInvalidResponse) {
391 SetUpDefaultValues();
392 scoped_ptr<OAuth2TokenService::Request> request = StartTokenRequest();
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");
399 AssertConsumerTokensAndErrors(0, 1);
402 TEST_F(DeviceOAuth2TokenServiceTest, RefreshTokenValidation_Failure_BadOwner) {
403 SetUpDefaultValues();
404 scoped_ptr<OAuth2TokenService::Request> request = StartTokenRequest();
406 SetRobotAccountId("WRONG_service_acct@g.com");
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));
413 AssertConsumerTokensAndErrors(0, 1);
416 TEST_F(DeviceOAuth2TokenServiceTest, RefreshTokenValidation_Retry) {
417 SetUpDefaultValues();
418 scoped_ptr<OAuth2TokenService::Request> request = StartTokenRequest();
420 PerformURLFetchesWithResults(
421 net::HTTP_INTERNAL_SERVER_ERROR, "",
422 net::HTTP_OK, GetValidTokenInfoResponse("service_acct@g.com"),
423 net::HTTP_OK, GetValidTokenResponse("ignored", 3600));
425 AssertConsumerTokensAndErrors(0, 1);
427 // Retry should succeed.
428 request = StartTokenRequest();
430 AssertConsumerTokensAndErrors(1, 1);
433 } // namespace chromeos