OLD | NEW |
(Empty) | |
| 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 "base/bind.h" |
| 6 #include "base/memory/scoped_ptr.h" |
| 7 #include "base/message_loop.h" |
| 8 #include "base/stringprintf.h" |
| 9 #include "base/strings/utf_string_conversions.h" |
| 10 #include "chrome/browser/managed_mode/managed_user_refresh_token_fetcher.h" |
| 11 #include "chrome/browser/signin/oauth2_token_service.h" |
| 12 #include "chrome/test/base/testing_profile.h" |
| 13 #include "content/public/test/test_browser_thread.h" |
| 14 #include "google_apis/gaia/gaia_urls.h" |
| 15 #include "google_apis/gaia/google_service_auth_error.h" |
| 16 #include "net/base/net_errors.h" |
| 17 #include "net/base/url_util.h" |
| 18 #include "net/http/http_request_headers.h" |
| 19 #include "net/http/http_status_code.h" |
| 20 #include "net/url_request/test_url_fetcher_factory.h" |
| 21 #include "net/url_request/url_fetcher_delegate.h" |
| 22 #include "testing/gtest/include/gtest/gtest.h" |
| 23 |
| 24 namespace { |
| 25 |
| 26 const char kManagedUserId[] = "abcdef"; |
| 27 const char kName[] = "Homestar"; |
| 28 const char kDeviceName[] = "Compy"; |
| 29 |
| 30 const char kAccessToken[] = "accesstoken"; |
| 31 const char kAuthorizationCode[] = "authorizationcode"; |
| 32 const char kManagedUserToken[] = "managedusertoken"; |
| 33 |
| 34 const char kIssueTokenResponseFormat[] = |
| 35 "{" |
| 36 " \"code\": \"%s\"" |
| 37 "}"; |
| 38 |
| 39 const char kGetRefreshTokenResponseFormat[] = |
| 40 "{" |
| 41 " \"access_token\": \"<ignored>\"," |
| 42 " \"expires_in\": 12345," |
| 43 " \"refresh_token\": \"%s\"" |
| 44 "}"; |
| 45 |
| 46 // MockOAuth2TokenService --------------------------------------------- |
| 47 |
| 48 class MockOAuth2TokenService : public OAuth2TokenService { |
| 49 public: |
| 50 class Request : public OAuth2TokenService::Request { |
| 51 public: |
| 52 Request(const OAuth2TokenService::ScopeSet& scopes, |
| 53 OAuth2TokenService::Consumer* consumer, |
| 54 MockOAuth2TokenService* owner); |
| 55 virtual ~Request(); |
| 56 |
| 57 void Succeed(); |
| 58 void Fail(GoogleServiceAuthError::State error); |
| 59 |
| 60 const OAuth2TokenService::ScopeSet& scopes() const { return scopes_; } |
| 61 |
| 62 private: |
| 63 OAuth2TokenService::ScopeSet scopes_; |
| 64 |
| 65 OAuth2TokenService::Consumer* consumer_; |
| 66 |
| 67 MockOAuth2TokenService* owner_; |
| 68 }; |
| 69 |
| 70 MockOAuth2TokenService(); |
| 71 virtual ~MockOAuth2TokenService(); |
| 72 |
| 73 Request* request() const { return request_; } |
| 74 |
| 75 void ClearRequest(Request* request); |
| 76 |
| 77 private: |
| 78 // OAuth2TokenService overrides: |
| 79 virtual scoped_ptr<OAuth2TokenService::Request> StartRequest( |
| 80 const OAuth2TokenService::ScopeSet& scopes, |
| 81 OAuth2TokenService::Consumer* consumer) OVERRIDE; |
| 82 virtual std::string GetRefreshToken() OVERRIDE; |
| 83 |
| 84 Request* request_; |
| 85 |
| 86 DISALLOW_COPY_AND_ASSIGN(MockOAuth2TokenService); |
| 87 }; |
| 88 |
| 89 MockOAuth2TokenService::Request::Request( |
| 90 const OAuth2TokenService::ScopeSet& scopes, |
| 91 OAuth2TokenService::Consumer* consumer, |
| 92 MockOAuth2TokenService* owner) |
| 93 : scopes_(scopes), |
| 94 consumer_(consumer), |
| 95 owner_(owner) {} |
| 96 |
| 97 MockOAuth2TokenService::Request::~Request() { |
| 98 owner_->ClearRequest(this); |
| 99 } |
| 100 |
| 101 void MockOAuth2TokenService::Request::Succeed() { |
| 102 base::Time expiration_date = base::Time::Now() + |
| 103 base::TimeDelta::FromHours(1); |
| 104 consumer_->OnGetTokenSuccess(this, kAccessToken, expiration_date); |
| 105 } |
| 106 |
| 107 void MockOAuth2TokenService::Request::Fail( |
| 108 GoogleServiceAuthError::State error) { |
| 109 consumer_->OnGetTokenFailure(this, GoogleServiceAuthError(error)); |
| 110 } |
| 111 |
| 112 MockOAuth2TokenService::MockOAuth2TokenService() |
| 113 : OAuth2TokenService(NULL), |
| 114 request_(NULL) {} |
| 115 |
| 116 MockOAuth2TokenService::~MockOAuth2TokenService() { |
| 117 EXPECT_FALSE(request_); |
| 118 } |
| 119 |
| 120 void MockOAuth2TokenService::ClearRequest( |
| 121 MockOAuth2TokenService::Request* request) { |
| 122 if (request_ == request) |
| 123 request_ = NULL; |
| 124 } |
| 125 |
| 126 scoped_ptr<OAuth2TokenService::Request> MockOAuth2TokenService::StartRequest( |
| 127 const OAuth2TokenService::ScopeSet& scopes, |
| 128 OAuth2TokenService::Consumer* consumer) { |
| 129 scoped_ptr<Request> request(new Request(scopes, consumer, this)); |
| 130 request_ = request.get(); |
| 131 return request.PassAs<OAuth2TokenService::Request>(); |
| 132 } |
| 133 |
| 134 std::string MockOAuth2TokenService::GetRefreshToken() { |
| 135 NOTREACHED(); |
| 136 return std::string(); |
| 137 } |
| 138 |
| 139 // Utility methods -------------------------------------------------- |
| 140 |
| 141 // Slightly hacky way to extract a value from a URL-encoded POST request body. |
| 142 bool GetValueForKey(const std::string& encoded_string, |
| 143 const std::string& key, |
| 144 std::string* value) { |
| 145 GURL url("http://example.com/?" + encoded_string); |
| 146 return net::GetValueForKeyInQuery(url, key, value); |
| 147 } |
| 148 |
| 149 void SendResponse(net::TestURLFetcher* url_fetcher, |
| 150 const std::string& response) { |
| 151 url_fetcher->set_status( |
| 152 net::URLRequestStatus(net::URLRequestStatus::SUCCESS, 0)); |
| 153 url_fetcher->set_response_code(net::HTTP_OK); |
| 154 url_fetcher->SetResponseString(response); |
| 155 url_fetcher->delegate()->OnURLFetchComplete(url_fetcher); |
| 156 } |
| 157 |
| 158 void SetNetworkError(net::TestURLFetcher* url_fetcher, int error) { |
| 159 url_fetcher->set_status( |
| 160 net::URLRequestStatus(net::URLRequestStatus::FAILED, error)); |
| 161 url_fetcher->delegate()->OnURLFetchComplete(url_fetcher); |
| 162 } |
| 163 |
| 164 void SetHttpError(net::TestURLFetcher* url_fetcher, int error) { |
| 165 url_fetcher->set_status(net::URLRequestStatus()); |
| 166 url_fetcher->set_response_code(error); |
| 167 url_fetcher->delegate()->OnURLFetchComplete(url_fetcher); |
| 168 } |
| 169 |
| 170 } // namespace |
| 171 |
| 172 class ManagedUserRefreshTokenFetcherTest : public testing::Test { |
| 173 public: |
| 174 ManagedUserRefreshTokenFetcherTest(); |
| 175 virtual ~ManagedUserRefreshTokenFetcherTest() {} |
| 176 |
| 177 protected: |
| 178 void StartFetching(); |
| 179 |
| 180 MockOAuth2TokenService::Request* GetOAuth2TokenServiceRequest(); |
| 181 net::TestURLFetcher* GetIssueTokenRequest(); |
| 182 net::TestURLFetcher* GetRefreshTokenRequest(); |
| 183 |
| 184 void MakeIssueTokenRequestSucceed(); |
| 185 void MakeRefreshTokenFetchSucceed(); |
| 186 |
| 187 void Reset(); |
| 188 |
| 189 const GoogleServiceAuthError& error() const { return error_; } |
| 190 const std::string& token() const { return token_; } |
| 191 |
| 192 private: |
| 193 void OnTokenFetched(const GoogleServiceAuthError& error, |
| 194 const std::string& token); |
| 195 |
| 196 base::WeakPtrFactory<ManagedUserRefreshTokenFetcherTest> weak_ptr_factory_; |
| 197 base::MessageLoop message_loop_; |
| 198 content::TestBrowserThread ui_thread_; |
| 199 TestingProfile profile_; |
| 200 MockOAuth2TokenService oauth2_token_service_; |
| 201 net::TestURLFetcherFactory url_fetcher_factory_; |
| 202 scoped_ptr<ManagedUserRefreshTokenFetcher> token_fetcher_; |
| 203 |
| 204 GoogleServiceAuthError error_; |
| 205 std::string token_; |
| 206 }; |
| 207 |
| 208 ManagedUserRefreshTokenFetcherTest::ManagedUserRefreshTokenFetcherTest() |
| 209 : weak_ptr_factory_(this), |
| 210 ui_thread_(content::BrowserThread::UI, &message_loop_), |
| 211 token_fetcher_( |
| 212 ManagedUserRefreshTokenFetcher::Create(&oauth2_token_service_, |
| 213 profile_.GetRequestContext())), |
| 214 error_(GoogleServiceAuthError::NONE) {} |
| 215 |
| 216 void ManagedUserRefreshTokenFetcherTest::StartFetching() { |
| 217 token_fetcher_->Start(kManagedUserId, UTF8ToUTF16(kName), kDeviceName, |
| 218 base::Bind( |
| 219 &ManagedUserRefreshTokenFetcherTest::OnTokenFetched, |
| 220 weak_ptr_factory_.GetWeakPtr())); |
| 221 } |
| 222 |
| 223 MockOAuth2TokenService::Request* |
| 224 ManagedUserRefreshTokenFetcherTest::GetOAuth2TokenServiceRequest() { |
| 225 MockOAuth2TokenService::Request* request = oauth2_token_service_.request(); |
| 226 |
| 227 OAuth2TokenService::ScopeSet scopes = request->scopes(); |
| 228 EXPECT_EQ(1u, scopes.size()); |
| 229 EXPECT_EQ(1u, scopes.count(GaiaUrls::GetInstance()->oauth1_login_scope())); |
| 230 return request; |
| 231 } |
| 232 |
| 233 net::TestURLFetcher* |
| 234 ManagedUserRefreshTokenFetcherTest::GetIssueTokenRequest() { |
| 235 net::TestURLFetcher* url_fetcher = url_fetcher_factory_.GetFetcherByID(1); |
| 236 if (!url_fetcher) |
| 237 return NULL; |
| 238 |
| 239 EXPECT_EQ(GaiaUrls::GetInstance()->oauth2_issue_token_url(), |
| 240 url_fetcher->GetOriginalURL().spec()); |
| 241 std::string access_token; |
| 242 net::HttpRequestHeaders headers; |
| 243 url_fetcher->GetExtraRequestHeaders(&headers); |
| 244 EXPECT_TRUE(headers.GetHeader("Authorization", &access_token)); |
| 245 EXPECT_EQ(std::string("Bearer ") + kAccessToken, access_token); |
| 246 const std::string upload_data = url_fetcher->upload_data(); |
| 247 std::string managed_user_id; |
| 248 EXPECT_TRUE(GetValueForKey(upload_data, "profile_id", &managed_user_id)); |
| 249 EXPECT_EQ(kManagedUserId, managed_user_id); |
| 250 std::string name; |
| 251 EXPECT_TRUE(GetValueForKey(upload_data, "profile_name", &name)); |
| 252 EXPECT_EQ(kName, name); |
| 253 std::string device_name; |
| 254 EXPECT_TRUE(GetValueForKey(upload_data, "device_name", &device_name)); |
| 255 EXPECT_EQ(kDeviceName, device_name); |
| 256 return url_fetcher; |
| 257 } |
| 258 |
| 259 net::TestURLFetcher* |
| 260 ManagedUserRefreshTokenFetcherTest::GetRefreshTokenRequest() { |
| 261 net::TestURLFetcher* url_fetcher = url_fetcher_factory_.GetFetcherByID(0); |
| 262 if (!url_fetcher) |
| 263 return NULL; |
| 264 |
| 265 EXPECT_EQ(GaiaUrls::GetInstance()->oauth2_token_url(), |
| 266 url_fetcher->GetOriginalURL().spec()); |
| 267 std::string auth_code; |
| 268 EXPECT_TRUE(GetValueForKey(url_fetcher->upload_data(), "code", &auth_code)); |
| 269 EXPECT_EQ(kAuthorizationCode, auth_code); |
| 270 return url_fetcher; |
| 271 } |
| 272 |
| 273 void ManagedUserRefreshTokenFetcherTest::MakeIssueTokenRequestSucceed() { |
| 274 SendResponse(GetIssueTokenRequest(), |
| 275 base::StringPrintf(kIssueTokenResponseFormat, |
| 276 kAuthorizationCode)); |
| 277 } |
| 278 |
| 279 void ManagedUserRefreshTokenFetcherTest::MakeRefreshTokenFetchSucceed() { |
| 280 SendResponse(GetRefreshTokenRequest(), |
| 281 base::StringPrintf(kGetRefreshTokenResponseFormat, |
| 282 kManagedUserToken)); |
| 283 } |
| 284 |
| 285 void ManagedUserRefreshTokenFetcherTest::Reset() { |
| 286 token_fetcher_.reset(); |
| 287 } |
| 288 |
| 289 void ManagedUserRefreshTokenFetcherTest::OnTokenFetched( |
| 290 const GoogleServiceAuthError& error, |
| 291 const std::string& token) { |
| 292 error_ = error; |
| 293 token_ = token; |
| 294 } |
| 295 |
| 296 // Tests -------------------------------------------------------- |
| 297 |
| 298 TEST_F(ManagedUserRefreshTokenFetcherTest, Success) { |
| 299 StartFetching(); |
| 300 GetOAuth2TokenServiceRequest()->Succeed(); |
| 301 MakeIssueTokenRequestSucceed(); |
| 302 MakeRefreshTokenFetchSucceed(); |
| 303 |
| 304 EXPECT_EQ(GoogleServiceAuthError::NONE, error().state()); |
| 305 EXPECT_EQ(kManagedUserToken, token()); |
| 306 } |
| 307 |
| 308 TEST_F(ManagedUserRefreshTokenFetcherTest, ExpiredAccessToken) { |
| 309 StartFetching(); |
| 310 GetOAuth2TokenServiceRequest()->Succeed(); |
| 311 SetHttpError(GetIssueTokenRequest(), net::HTTP_UNAUTHORIZED); |
| 312 GetOAuth2TokenServiceRequest()->Succeed(); |
| 313 MakeIssueTokenRequestSucceed(); |
| 314 MakeRefreshTokenFetchSucceed(); |
| 315 |
| 316 EXPECT_EQ(GoogleServiceAuthError::NONE, error().state()); |
| 317 EXPECT_EQ(kManagedUserToken, token()); |
| 318 } |
| 319 |
| 320 TEST_F(ManagedUserRefreshTokenFetcherTest, ExpiredAccessTokenRetry) { |
| 321 // If we get a 401 error for the second time, we should give up instead of |
| 322 // retrying again. |
| 323 StartFetching(); |
| 324 GetOAuth2TokenServiceRequest()->Succeed(); |
| 325 SetHttpError(GetIssueTokenRequest(), net::HTTP_UNAUTHORIZED); |
| 326 GetOAuth2TokenServiceRequest()->Succeed(); |
| 327 SetHttpError(GetIssueTokenRequest(), net::HTTP_UNAUTHORIZED); |
| 328 |
| 329 EXPECT_EQ(GoogleServiceAuthError::CONNECTION_FAILED, error().state()); |
| 330 EXPECT_EQ(net::ERR_FAILED, error().network_error()); |
| 331 EXPECT_EQ(std::string(), token()); |
| 332 } |
| 333 |
| 334 TEST_F(ManagedUserRefreshTokenFetcherTest, MalformedIssueTokenResponse) { |
| 335 StartFetching(); |
| 336 GetOAuth2TokenServiceRequest()->Succeed(); |
| 337 SendResponse(GetIssueTokenRequest(), "choke"); |
| 338 |
| 339 EXPECT_EQ(GoogleServiceAuthError::CONNECTION_FAILED, error().state()); |
| 340 EXPECT_EQ(net::ERR_INVALID_RESPONSE, error().network_error()); |
| 341 EXPECT_EQ(std::string(), token()); |
| 342 } |
| 343 |
| 344 TEST_F(ManagedUserRefreshTokenFetcherTest, FetchAccessTokenFailure) { |
| 345 StartFetching(); |
| 346 GetOAuth2TokenServiceRequest()->Fail( |
| 347 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); |
| 348 |
| 349 EXPECT_EQ(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS, error().state()); |
| 350 EXPECT_EQ(std::string(), token()); |
| 351 } |
| 352 |
| 353 TEST_F(ManagedUserRefreshTokenFetcherTest, IssueTokenNetworkError) { |
| 354 StartFetching(); |
| 355 GetOAuth2TokenServiceRequest()->Succeed(); |
| 356 SetNetworkError(GetIssueTokenRequest(), net::ERR_SSL_PROTOCOL_ERROR); |
| 357 |
| 358 EXPECT_EQ(GoogleServiceAuthError::CONNECTION_FAILED, error().state()); |
| 359 EXPECT_EQ(net::ERR_SSL_PROTOCOL_ERROR, error().network_error()); |
| 360 EXPECT_EQ(std::string(), token()); |
| 361 } |
| 362 |
| 363 TEST_F(ManagedUserRefreshTokenFetcherTest, FetchRefreshTokenNetworkError) { |
| 364 StartFetching(); |
| 365 GetOAuth2TokenServiceRequest()->Succeed(); |
| 366 MakeIssueTokenRequestSucceed(); |
| 367 SetNetworkError(GetRefreshTokenRequest(), net::ERR_CONNECTION_REFUSED); |
| 368 EXPECT_EQ(GoogleServiceAuthError::NONE, error().state()); |
| 369 SetNetworkError(GetRefreshTokenRequest(), net::ERR_CONNECTION_REFUSED); |
| 370 |
| 371 EXPECT_EQ(GoogleServiceAuthError::CONNECTION_FAILED, error().state()); |
| 372 EXPECT_EQ(net::ERR_FAILED, error().network_error()); |
| 373 EXPECT_EQ(std::string(), token()); |
| 374 } |
| 375 |
| 376 TEST_F(ManagedUserRefreshTokenFetcherTest, |
| 377 FetchRefreshTokenTransientNetworkError) { |
| 378 StartFetching(); |
| 379 GetOAuth2TokenServiceRequest()->Succeed(); |
| 380 MakeIssueTokenRequestSucceed(); |
| 381 SetNetworkError(GetRefreshTokenRequest(), net::ERR_CONNECTION_REFUSED); |
| 382 |
| 383 EXPECT_EQ(GoogleServiceAuthError::NONE, error().state()); |
| 384 MakeRefreshTokenFetchSucceed(); |
| 385 |
| 386 EXPECT_EQ(GoogleServiceAuthError::NONE, error().state()); |
| 387 EXPECT_EQ(kManagedUserToken, token()); |
| 388 } |
| 389 |
| 390 TEST_F(ManagedUserRefreshTokenFetcherTest, FetchRefreshTokenBadRequest) { |
| 391 StartFetching(); |
| 392 GetOAuth2TokenServiceRequest()->Succeed(); |
| 393 MakeIssueTokenRequestSucceed(); |
| 394 SetHttpError(GetRefreshTokenRequest(), net::HTTP_BAD_REQUEST); |
| 395 |
| 396 EXPECT_EQ(GoogleServiceAuthError::CONNECTION_FAILED, error().state()); |
| 397 EXPECT_EQ(net::ERR_FAILED, error().network_error()); |
| 398 EXPECT_EQ(std::string(), token()); |
| 399 } |
| 400 |
| 401 TEST_F(ManagedUserRefreshTokenFetcherTest, CancelWhileFetchingAccessToken) { |
| 402 StartFetching(); |
| 403 Reset(); |
| 404 |
| 405 EXPECT_EQ(GoogleServiceAuthError::NONE, error().state()); |
| 406 EXPECT_EQ(std::string(), token()); |
| 407 } |
| 408 |
| 409 TEST_F(ManagedUserRefreshTokenFetcherTest, CancelWhileCallingIssueToken) { |
| 410 StartFetching(); |
| 411 GetOAuth2TokenServiceRequest()->Succeed(); |
| 412 Reset(); |
| 413 |
| 414 EXPECT_EQ(GoogleServiceAuthError::NONE, error().state()); |
| 415 EXPECT_EQ(std::string(), token()); |
| 416 } |
| 417 |
| 418 TEST_F(ManagedUserRefreshTokenFetcherTest, CancelWhileFetchingRefreshToken) { |
| 419 StartFetching(); |
| 420 GetOAuth2TokenServiceRequest()->Succeed(); |
| 421 MakeIssueTokenRequestSucceed(); |
| 422 Reset(); |
| 423 |
| 424 EXPECT_EQ(GoogleServiceAuthError::NONE, error().state()); |
| 425 EXPECT_EQ(std::string(), token()); |
| 426 } |
OLD | NEW |