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

Powered by Google App Engine
This is Rietveld 408576698