Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(60)

Side by Side Diff: chrome/browser/chromeos/settings/device_oauth2_token_service_unittest.cc

Issue 17109006: Device robot refresh token integrity validation. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Moved validation logic out of OAuthTokenService base class. Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/chromeos/settings/device_oauth2_token_service.h" 5 #include "chrome/browser/chromeos/settings/device_oauth2_token_service.h"
6 6
7 #include "base/message_loop.h" 7 #include "base/message_loop.h"
8 #include "base/prefs/testing_pref_service.h" 8 #include "base/prefs/testing_pref_service.h"
9 #include "chrome/browser/signin/oauth2_token_service_test_util.h"
10 #include "chrome/common/pref_names.h"
9 #include "chrome/test/base/scoped_testing_local_state.h" 11 #include "chrome/test/base/scoped_testing_local_state.h"
10 #include "chrome/test/base/testing_browser_process.h" 12 #include "chrome/test/base/testing_browser_process.h"
11 #include "chromeos/cryptohome/mock_cryptohome_library.h" 13 #include "chromeos/cryptohome/mock_cryptohome_library.h"
12 #include "content/public/browser/browser_thread.h" 14 #include "content/public/browser/browser_thread.h"
13 #include "content/public/test/test_browser_thread.h" 15 #include "content/public/test/test_browser_thread.h"
16 #include "google_apis/gaia/gaia_oauth_client.h"
17 #include "net/http/http_status_code.h"
18 #include "net/url_request/test_url_fetcher_factory.h"
19 #include "net/url_request/url_fetcher_delegate.h"
14 #include "net/url_request/url_request_test_util.h" 20 #include "net/url_request/url_request_test_util.h"
15 #include "testing/gmock/include/gmock/gmock.h" 21 #include "testing/gmock/include/gmock/gmock.h"
16 #include "testing/gtest/include/gtest/gtest.h" 22 #include "testing/gtest/include/gtest/gtest.h"
17 23
18 using ::testing::_; 24 using ::testing::_;
19 using ::testing::AnyNumber; 25 using ::testing::AnyNumber;
20 using ::testing::Return; 26 using ::testing::Return;
21 using ::testing::StrEq; 27 using ::testing::StrEq;
22 using ::testing::StrictMock; 28 using ::testing::StrictMock;
23 29
24 namespace chromeos { 30 namespace chromeos {
25 31
32 static const int kOAuthTokenServiceUrlFetcherId = 0;
33 static const int kValidatorUrlFetcherId = gaia::GaiaOAuthClient::kUrlFetcherId;
34
35 class TestDeviceOAuth2TokenService : public DeviceOAuth2TokenService {
36 public:
37 explicit TestDeviceOAuth2TokenService(net::URLRequestContextGetter* getter,
38 PrefService* local_state)
39 : DeviceOAuth2TokenService(getter, local_state) {
40 }
41 void SetRobotAccountIdPolicyValue(const std::string& id) {
42 robot_account_id_ = id;
43 }
44 protected:
45 // Skip calling into the policy subsystem and return our test value.
46 virtual std::string GetRobotAccountId() OVERRIDE {
47 return robot_account_id_;
48 }
49 private:
50 std::string robot_account_id_;
51 DISALLOW_COPY_AND_ASSIGN(TestDeviceOAuth2TokenService);
52 };
53
26 class DeviceOAuth2TokenServiceTest : public testing::Test { 54 class DeviceOAuth2TokenServiceTest : public testing::Test {
27 public: 55 public:
28 DeviceOAuth2TokenServiceTest() 56 DeviceOAuth2TokenServiceTest()
29 : ui_thread_(content::BrowserThread::UI, &message_loop_), 57 : ui_thread_(content::BrowserThread::UI, &message_loop_),
30 scoped_testing_local_state_(TestingBrowserProcess::GetGlobal()) {} 58 scoped_testing_local_state_(TestingBrowserProcess::GetGlobal()),
59 request_context_getter_(new net::TestURLRequestContextGetter(
60 message_loop_.message_loop_proxy())) { }
31 virtual ~DeviceOAuth2TokenServiceTest() {} 61 virtual ~DeviceOAuth2TokenServiceTest() {}
32 62
33 virtual void SetUp() OVERRIDE { 63 virtual void SetUp() OVERRIDE {
64 oauth2_service_ = new TestDeviceOAuth2TokenService(
65 request_context_getter_,
66 scoped_testing_local_state_.Get());
67 oauth2_service_->max_refresh_token_validation_retries_ = 0;
68 oauth2_service_->set_max_authorization_token_fetch_retries_for_testing(0);
69 }
70
71 // Most tests just want a noop crypto impl with a dummy refresh token value in
72 // Local State (if the value is an empty string, it will be ignored).
73 void SetUpDefaultValues() {
74 cryptohome_library_.reset(chromeos::CryptohomeLibrary::GetTestImpl());
75 chromeos::CryptohomeLibrary::SetForTest(cryptohome_library_.get());
76 SetDeviceRefreshTokenInLocalState("device_refresh_token_4_test");
77 oauth2_service_->SetRobotAccountIdPolicyValue("service_acct@g.com");
78 AssertConsumerTokensAndErrors(0, 0);
79 }
80
81 scoped_ptr<OAuth2TokenService::Request> StartTokenRequest() {
82 return oauth2_service_->StartRequest(std::set<std::string>(), &consumer_);
34 } 83 }
35 84
36 virtual void TearDown() OVERRIDE { 85 virtual void TearDown() OVERRIDE {
86 delete oauth2_service_;
87 CryptohomeLibrary::SetForTest(NULL);
37 } 88 }
38 89
90 // Utility method to set a value in Local State for the device refresh token
91 // (it must have a non-empty value or it won't be used).
92 void SetDeviceRefreshTokenInLocalState(const std::string& refresh_token) {
93 scoped_testing_local_state_.Get()->SetManagedPref(
94 prefs::kDeviceRobotAnyApiRefreshToken,
95 Value::CreateStringValue(refresh_token));
96 }
97
98 std::string GetValidTokenInfoResponse(const std::string issued_to) {
99 return "{ \"issued_to\": \"" + issued_to + "\","
100 " \"user_id\": \"1234567890\" }";
101 }
102
103 // A utility method to return fake URL results, for testing the refresh token
104 // validation logic. For a successful validation attempt, this method will be
105 // called three times for the steps listed below (steps 1 and 2 happen in
106 // parallel).
107 //
108 // Step 1a: fetch the access token for the tokeninfo API.
109 // Step 1b: call the tokeninfo API.
110 // Step 2: Fetch the access token for the requested scope
111 // (in this case, cloudprint).
112 void ReturnOAuthUrlFetchResults(int fetcher_id,
113 net::HttpStatusCode response_code,
114 const std::string& response_string);
115
116 void AssertConsumerTokensAndErrors(int num_tokens, int num_errors);
117
39 protected: 118 protected:
40 base::MessageLoop message_loop_; 119 base::MessageLoop message_loop_;
41 content::TestBrowserThread ui_thread_; 120 content::TestBrowserThread ui_thread_;
42 ScopedTestingLocalState scoped_testing_local_state_; 121 ScopedTestingLocalState scoped_testing_local_state_;
122 scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_;
123 net::TestURLFetcherFactory factory_;
124 TestDeviceOAuth2TokenService* oauth2_service_;
125 TestingOAuth2TokenServiceConsumer consumer_;
126 scoped_ptr<chromeos::CryptohomeLibrary> cryptohome_library_;
127
43 }; 128 };
44 129
130 void DeviceOAuth2TokenServiceTest::ReturnOAuthUrlFetchResults(
131 int fetcher_id,
132 net::HttpStatusCode response_code,
133 const std::string& response_string) {
134
135 net::TestURLFetcher* fetcher = factory_.GetFetcherByID(fetcher_id);
136 ASSERT_TRUE(fetcher);
137 fetcher->set_response_code(response_code);
138 fetcher->SetResponseString(response_string);
139 fetcher->delegate()->OnURLFetchComplete(fetcher);
140 }
141
142 void DeviceOAuth2TokenServiceTest::AssertConsumerTokensAndErrors(
143 int num_tokens,
144 int num_errors) {
145 ASSERT_EQ(num_tokens, consumer_.number_of_successful_tokens_);
146 ASSERT_EQ(num_errors, consumer_.number_of_errors_);
147 }
148
45 TEST_F(DeviceOAuth2TokenServiceTest, SaveEncryptedToken) { 149 TEST_F(DeviceOAuth2TokenServiceTest, SaveEncryptedToken) {
46 StrictMock<MockCryptohomeLibrary> mock_cryptohome_library; 150 StrictMock<MockCryptohomeLibrary> mock_cryptohome_library;
47 CryptohomeLibrary::SetForTest(&mock_cryptohome_library); 151 CryptohomeLibrary::SetForTest(&mock_cryptohome_library);
48 152
49 EXPECT_CALL(mock_cryptohome_library, DecryptWithSystemSalt(StrEq(""))) 153 EXPECT_CALL(mock_cryptohome_library, DecryptWithSystemSalt(StrEq("")))
50 .Times(1) 154 .Times(1)
51 .WillOnce(Return("")); 155 .WillOnce(Return(""));
52 EXPECT_CALL(mock_cryptohome_library, 156 EXPECT_CALL(mock_cryptohome_library,
53 EncryptWithSystemSalt(StrEq("test-token"))) 157 EncryptWithSystemSalt(StrEq("test-token")))
54 .Times(1) 158 .Times(1)
55 .WillOnce(Return("encrypted")); 159 .WillOnce(Return("encrypted"));
56 EXPECT_CALL(mock_cryptohome_library, 160 EXPECT_CALL(mock_cryptohome_library,
57 DecryptWithSystemSalt(StrEq("encrypted"))) 161 DecryptWithSystemSalt(StrEq("encrypted")))
58 .Times(1) 162 .Times(1)
59 .WillOnce(Return("test-token")); 163 .WillOnce(Return("test-token"));
60 164
61 DeviceOAuth2TokenService oauth2_service( 165 ASSERT_EQ("", oauth2_service_->GetRefreshToken());
62 new net::TestURLRequestContextGetter(message_loop_.message_loop_proxy()), 166 oauth2_service_->SetAndSaveRefreshToken("test-token");
63 scoped_testing_local_state_.Get()); 167 ASSERT_EQ("test-token", oauth2_service_->GetRefreshToken());
64
65 ASSERT_EQ("", oauth2_service.GetRefreshToken());
66 oauth2_service.SetAndSaveRefreshToken("test-token");
67 ASSERT_EQ("test-token", oauth2_service.GetRefreshToken());
68 168
69 // This call won't invoke decrypt again, since the value is cached. 169 // This call won't invoke decrypt again, since the value is cached.
70 ASSERT_EQ("test-token", oauth2_service.GetRefreshToken()); 170 ASSERT_EQ("test-token", oauth2_service_->GetRefreshToken());
171 }
172
173 TEST_F(DeviceOAuth2TokenServiceTest, RefreshTokenValidation_Success) {
174 SetUpDefaultValues();
175 scoped_ptr<OAuth2TokenService::Request> request = StartTokenRequest();
176
177 ReturnOAuthUrlFetchResults(
178 kValidatorUrlFetcherId,
179 net::HTTP_OK,
180 GetValidTokenResponse("tokeninfo_access_token", 3600));
181
182 ReturnOAuthUrlFetchResults(
183 kValidatorUrlFetcherId,
184 net::HTTP_OK,
185 GetValidTokenInfoResponse("service_acct@g.com"));
186
187 ReturnOAuthUrlFetchResults(
188 kOAuthTokenServiceUrlFetcherId,
189 net::HTTP_OK,
190 GetValidTokenResponse("scoped_access_token", 3600));
191
192 AssertConsumerTokensAndErrors(1, 0);
193
194 EXPECT_EQ("scoped_access_token", consumer_.last_token_);
195 }
196
197 TEST_F(DeviceOAuth2TokenServiceTest,
198 RefreshTokenValidation_Failure_TokenInfoAccessTokenHttpError) {
199 SetUpDefaultValues();
200 scoped_ptr<OAuth2TokenService::Request> request = StartTokenRequest();
201
202 ReturnOAuthUrlFetchResults(
203 kValidatorUrlFetcherId,
204 net::HTTP_UNAUTHORIZED,
205 "");
206
207 // TokenInfo API call skipped (error returned in previous step).
208
209 // CloudPrint access token fetch is successful, but consumer still given error
210 // due to bad refresh token.
211 ReturnOAuthUrlFetchResults(
212 kOAuthTokenServiceUrlFetcherId,
213 net::HTTP_OK,
214 GetValidTokenResponse("ignored_scoped_access_token", 3600));
215
216 AssertConsumerTokensAndErrors(0, 1);
217 }
218
219 TEST_F(DeviceOAuth2TokenServiceTest,
220 RefreshTokenValidation_Failure_TokenInfoAccessTokenInvalidResponse) {
221 SetUpDefaultValues();
222 scoped_ptr<OAuth2TokenService::Request> request = StartTokenRequest();
223
224 ReturnOAuthUrlFetchResults(
225 kValidatorUrlFetcherId,
226 net::HTTP_OK,
227 "invalid response");
228
229 // TokenInfo API call skipped (error returned in previous step).
230
231 ReturnOAuthUrlFetchResults(
232 kOAuthTokenServiceUrlFetcherId,
233 net::HTTP_OK,
234 GetValidTokenResponse("ignored_scoped_access_token", 3600));
235
236 // CloudPrint access token fetch is successful, but consumer still given error
237 // due to bad refresh token.
238 AssertConsumerTokensAndErrors(0, 1);
239 }
240
241 TEST_F(DeviceOAuth2TokenServiceTest,
242 RefreshTokenValidation_Failure_TokenInfoApiCallHttpError) {
243 SetUpDefaultValues();
244 scoped_ptr<OAuth2TokenService::Request> request = StartTokenRequest();
245
246 ReturnOAuthUrlFetchResults(
247 kValidatorUrlFetcherId,
248 net::HTTP_OK,
249 GetValidTokenResponse("tokeninfo_access_token", 3600));
250
251 ReturnOAuthUrlFetchResults(
252 kValidatorUrlFetcherId,
253 net::HTTP_INTERNAL_SERVER_ERROR,
254 "");
255
256 ReturnOAuthUrlFetchResults(
257 kOAuthTokenServiceUrlFetcherId,
258 net::HTTP_OK,
259 GetValidTokenResponse("ignored_scoped_access_token", 3600));
260
261 // CloudPrint access token fetch is successful, but consumer still given error
262 // due to bad refresh token.
263 AssertConsumerTokensAndErrors(0, 1);
264 }
265
266 TEST_F(DeviceOAuth2TokenServiceTest,
267 RefreshTokenValidation_Failure_TokenInfoApiCallInvalidResponse) {
268 SetUpDefaultValues();
269 scoped_ptr<OAuth2TokenService::Request> request = StartTokenRequest();
270
271 ReturnOAuthUrlFetchResults(
272 kValidatorUrlFetcherId,
273 net::HTTP_OK,
274 GetValidTokenResponse("tokeninfo_access_token", 3600));
275
276 ReturnOAuthUrlFetchResults(
277 kValidatorUrlFetcherId,
278 net::HTTP_OK,
279 "invalid response");
280
281 ReturnOAuthUrlFetchResults(
282 kOAuthTokenServiceUrlFetcherId,
283 net::HTTP_OK,
284 GetValidTokenResponse("ignored_scoped_access_token", 3600));
285
286 // CloudPrint access token fetch is successful, but consumer still given error
287 // due to bad refresh token.
288 AssertConsumerTokensAndErrors(0, 1);
289 }
290
291 TEST_F(DeviceOAuth2TokenServiceTest,
292 RefreshTokenValidation_Failure_CloudPrintAccessTokenHttpError) {
293 SetUpDefaultValues();
294 scoped_ptr<OAuth2TokenService::Request> request = StartTokenRequest();
295
296 ReturnOAuthUrlFetchResults(
297 kValidatorUrlFetcherId,
298 net::HTTP_OK,
299 GetValidTokenResponse("tokeninfo_access_token", 3600));
300
301 ReturnOAuthUrlFetchResults(
302 kValidatorUrlFetcherId,
303 net::HTTP_OK,
304 GetValidTokenInfoResponse("service_acct@g.com"));
305
306 ReturnOAuthUrlFetchResults(
307 kOAuthTokenServiceUrlFetcherId,
308 net::HTTP_BAD_REQUEST,
309 "");
310
311 AssertConsumerTokensAndErrors(0, 1);
312 }
313
314 TEST_F(DeviceOAuth2TokenServiceTest,
315 RefreshTokenValidation_Failure_CloudPrintAccessTokenInvalidResponse) {
316 SetUpDefaultValues();
317 scoped_ptr<OAuth2TokenService::Request> request = StartTokenRequest();
318
319 ReturnOAuthUrlFetchResults(
320 kValidatorUrlFetcherId,
321 net::HTTP_OK,
322 GetValidTokenResponse("tokeninfo_access_token", 3600));
323
324 ReturnOAuthUrlFetchResults(
325 kValidatorUrlFetcherId,
326 net::HTTP_OK,
327 GetValidTokenInfoResponse("service_acct@g.com"));
328
329 ReturnOAuthUrlFetchResults(
330 kOAuthTokenServiceUrlFetcherId,
331 net::HTTP_OK,
332 "invalid request");
333
334 AssertConsumerTokensAndErrors(0, 1);
335 }
336
337 TEST_F(DeviceOAuth2TokenServiceTest, RefreshTokenValidation_Failure_BadOwner) {
338 SetUpDefaultValues();
339 scoped_ptr<OAuth2TokenService::Request> request = StartTokenRequest();
340
341 oauth2_service_->SetRobotAccountIdPolicyValue("WRONG_service_acct@g.com");
342
343 // The requested token comes in before any of the validation calls complete,
344 // but the consumer still gets an error, since the results don't get returned
345 // until validation is over.
346 ReturnOAuthUrlFetchResults(
347 kOAuthTokenServiceUrlFetcherId,
348 net::HTTP_OK,
349 GetValidTokenResponse("ignored_scoped_access_token", 3600));
350 AssertConsumerTokensAndErrors(0, 0);
351
352 ReturnOAuthUrlFetchResults(
353 kValidatorUrlFetcherId,
354 net::HTTP_OK,
355 GetValidTokenResponse("tokeninfo_access_token", 3600));
356 AssertConsumerTokensAndErrors(0, 0);
357
358 ReturnOAuthUrlFetchResults(
359 kValidatorUrlFetcherId,
360 net::HTTP_OK,
361 GetValidTokenInfoResponse("service_acct@g.com"));
362
363 // All fetches were successful, but consumer still given error since
364 // the token owner doesn't match the policy value.
365 AssertConsumerTokensAndErrors(0, 1);
71 } 366 }
72 367
73 } // namespace chromeos 368 } // namespace chromeos
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698