summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos_unittest.cc5
-rw-r--r--chrome/browser/managed_mode/managed_user_refresh_token_fetcher.cc285
-rw-r--r--chrome/browser/managed_mode/managed_user_refresh_token_fetcher.h50
-rw-r--r--chrome/browser/managed_mode/managed_user_refresh_token_fetcher_unittest.cc426
-rw-r--r--chrome/browser/managed_mode/managed_user_registration_service.cc31
-rw-r--r--chrome/browser/managed_mode/managed_user_registration_service.h19
-rw-r--r--chrome/browser/managed_mode/managed_user_registration_service_factory.cc15
-rw-r--r--chrome/browser/managed_mode/managed_user_registration_service_unittest.cc31
-rw-r--r--chrome/browser/signin/android_profile_oauth2_token_service.cc2
-rw-r--r--chrome/browser/signin/oauth2_token_service.cc18
-rw-r--r--chrome/browser/signin/oauth2_token_service.h6
-rw-r--r--chrome/browser/signin/oauth2_token_service_unittest.cc53
-rw-r--r--chrome/chrome_browser.gypi2
-rw-r--r--chrome/chrome_tests_unit.gypi1
-rw-r--r--google_apis/gaia/gaia_oauth_client.cc2
15 files changed, 908 insertions, 38 deletions
diff --git a/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos_unittest.cc b/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos_unittest.cc
index dadb7a9..6600274 100644
--- a/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos_unittest.cc
+++ b/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos_unittest.cc
@@ -324,10 +324,7 @@ class DeviceCloudPolicyManagerChromeOSEnrollmentTest
if (robot_auth_fetch_status_ == DM_STATUS_SUCCESS) {
net::TestURLFetcher* url_fetcher = url_fetcher_factory_.GetFetcherByID(0);
ASSERT_TRUE(url_fetcher);
- // The logic in GaiaOAuthClient seems broken, it always retries 1x on
- // non-200 response codes, even if the retries are set to 0. Seems like
- // its num_retries_ > source->GetMaxRetriesOn5xx() should have a >=?
- url_fetcher->SetMaxRetriesOn5xx(-2);
+ url_fetcher->SetMaxRetriesOn5xx(0);
url_fetcher->set_status(net::URLRequestStatus());
url_fetcher->set_response_code(url_fetcher_response_code_);
url_fetcher->SetResponseString(url_fetcher_response_string_);
diff --git a/chrome/browser/managed_mode/managed_user_refresh_token_fetcher.cc b/chrome/browser/managed_mode/managed_user_refresh_token_fetcher.cc
new file mode 100644
index 0000000..b79faf2
--- /dev/null
+++ b/chrome/browser/managed_mode/managed_user_refresh_token_fetcher.cc
@@ -0,0 +1,285 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/managed_mode/managed_user_refresh_token_fetcher.h"
+
+#include "base/callback.h"
+#include "base/json/json_reader.h"
+#include "base/logging.h"
+#include "base/string16.h"
+#include "base/stringprintf.h"
+#include "base/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/signin/oauth2_token_service.h"
+#include "google_apis/gaia/gaia_oauth_client.h"
+#include "google_apis/gaia/gaia_urls.h"
+#include "google_apis/gaia/google_service_auth_error.h"
+#include "google_apis/gaia/oauth2_api_call_flow.h"
+#include "net/base/escape.h"
+#include "net/base/load_flags.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_status_code.h"
+#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_request_status.h"
+
+using base::Time;
+using gaia::GaiaOAuthClient;
+using net::URLFetcher;
+using net::URLFetcherDelegate;
+using net::URLRequestContextGetter;
+
+namespace {
+
+const int kNumRetries = 1;
+
+static const char kChromeSyncManagedScope[] =
+ "https://www.googleapis.com/auth/chromesync_playpen";
+
+static const char kIssueTokenBodyFormat[] =
+ "client_id=%s"
+ "&scope=&%s"
+ "&response_type=code"
+ "&profile_id=%s"
+ "&profile_name=%s"
+ "&device_name=%s";
+
+static const char kAuthorizationHeaderFormat[] =
+ "Authorization: Bearer %s";
+
+static const char kCodeKey[] = "code";
+
+class ManagedUserRefreshTokenFetcherImpl
+ : public ManagedUserRefreshTokenFetcher,
+ public OAuth2TokenService::Consumer,
+ public URLFetcherDelegate,
+ public GaiaOAuthClient::Delegate {
+ public:
+ ManagedUserRefreshTokenFetcherImpl(OAuth2TokenService* oauth2_token_service,
+ URLRequestContextGetter* context);
+ virtual ~ManagedUserRefreshTokenFetcherImpl();
+
+ // ManagedUserRefreshTokenFetcher implementation:
+ virtual void Start(const std::string& managed_user_id,
+ const string16& name,
+ const std::string& device_name,
+ const TokenCallback& callback) OVERRIDE;
+
+ protected:
+ // OAuth2TokenService::Consumer implementation:
+ virtual void OnGetTokenSuccess(const OAuth2TokenService::Request* request,
+ const std::string& access_token,
+ const Time& expiration_time) OVERRIDE;
+ virtual void OnGetTokenFailure(const OAuth2TokenService::Request* request,
+ const GoogleServiceAuthError& error) OVERRIDE;
+
+ // net::URLFetcherDelegate implementation.
+ virtual void OnURLFetchComplete(const URLFetcher* source) OVERRIDE;
+
+ // GaiaOAuthClient::Delegate implementation:
+ virtual void OnGetTokensResponse(const std::string& refresh_token,
+ const std::string& access_token,
+ int expires_in_seconds) OVERRIDE;
+ virtual void OnRefreshTokenResponse(const std::string& access_token,
+ int expires_in_seconds) OVERRIDE;
+ virtual void OnOAuthError() OVERRIDE;
+ virtual void OnNetworkError(int response_code) OVERRIDE;
+
+ private:
+ // Requests an access token, which is the first thing we need. This is where
+ // we restart when the returned access token has expired.
+ void StartFetching();
+
+ void DispatchNetworkError(int error_code);
+ void DispatchGoogleServiceAuthError(const GoogleServiceAuthError& error,
+ const std::string& token);
+ OAuth2TokenService* oauth2_token_service_;
+ URLRequestContextGetter* context_;
+
+ std::string device_name_;
+ std::string managed_user_id_;
+ string16 name_;
+ TokenCallback callback_;
+
+ scoped_ptr<OAuth2TokenService::Request> access_token_request_;
+ std::string access_token_;
+ bool access_token_expired_;
+ scoped_ptr<URLFetcher> url_fetcher_;
+ scoped_ptr<GaiaOAuthClient> gaia_oauth_client_;
+};
+
+ManagedUserRefreshTokenFetcherImpl::ManagedUserRefreshTokenFetcherImpl(
+ OAuth2TokenService* oauth2_token_service,
+ URLRequestContextGetter* context)
+ : oauth2_token_service_(oauth2_token_service),
+ context_(context),
+ access_token_expired_(false) {}
+
+ManagedUserRefreshTokenFetcherImpl::~ManagedUserRefreshTokenFetcherImpl() {}
+
+void ManagedUserRefreshTokenFetcherImpl::Start(
+ const std::string& managed_user_id,
+ const string16& name,
+ const std::string& device_name,
+ const TokenCallback& callback) {
+ DCHECK(callback_.is_null());
+ managed_user_id_ = managed_user_id;
+ name_ = name;
+ device_name_ = device_name;
+ callback_ = callback;
+ StartFetching();
+}
+
+void ManagedUserRefreshTokenFetcherImpl::StartFetching() {
+ OAuth2TokenService::ScopeSet scopes;
+ scopes.insert(GaiaUrls::GetInstance()->oauth1_login_scope());
+ access_token_request_ = oauth2_token_service_->StartRequest(scopes, this);
+}
+
+void ManagedUserRefreshTokenFetcherImpl::OnGetTokenSuccess(
+ const OAuth2TokenService::Request* request,
+ const std::string& access_token,
+ const Time& expiration_time) {
+ DCHECK_EQ(access_token_request_.get(), request);
+ access_token_ = access_token;
+
+ GURL url(GaiaUrls::GetInstance()->oauth2_issue_token_url());
+ // GaiaOAuthClient uses id 0, so we use 1 to distinguish the requests in
+ // unit tests.
+ const int id = 1;
+
+ url_fetcher_.reset(URLFetcher::Create(id, url, URLFetcher::POST, this));
+
+ url_fetcher_->SetRequestContext(context_);
+ url_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
+ net::LOAD_DO_NOT_SAVE_COOKIES);
+ url_fetcher_->SetAutomaticallyRetryOnNetworkChanges(kNumRetries);
+ url_fetcher_->AddExtraRequestHeader(
+ base::StringPrintf(kAuthorizationHeaderFormat, access_token.c_str()));
+
+ std::string body = base::StringPrintf(
+ kIssueTokenBodyFormat,
+ net::EscapeUrlEncodedData(
+ GaiaUrls::GetInstance()->oauth2_chrome_client_id(), true).c_str(),
+ net::EscapeUrlEncodedData(kChromeSyncManagedScope, true).c_str(),
+ net::EscapeUrlEncodedData(managed_user_id_, true).c_str(),
+ net::EscapeUrlEncodedData(UTF16ToUTF8(name_), true).c_str(),
+ net::EscapeUrlEncodedData(device_name_, true).c_str());
+ url_fetcher_->SetUploadData("application/x-www-form-urlencoded", body);
+
+ url_fetcher_->Start();
+}
+
+void ManagedUserRefreshTokenFetcherImpl::OnGetTokenFailure(
+ const OAuth2TokenService::Request* request,
+ const GoogleServiceAuthError& error) {
+ DCHECK_EQ(access_token_request_.get(), request);
+ callback_.Run(error, std::string());
+ callback_.Reset();
+}
+
+void ManagedUserRefreshTokenFetcherImpl::OnURLFetchComplete(
+ const URLFetcher* source) {
+ const net::URLRequestStatus& status = source->GetStatus();
+ if (!status.is_success()) {
+ DispatchNetworkError(status.error());
+ return;
+ }
+
+ int response_code = source->GetResponseCode();
+ if (response_code == net::HTTP_UNAUTHORIZED && !access_token_expired_) {
+ access_token_expired_ = true;
+ oauth2_token_service_->InvalidateToken(OAuth2TokenService::ScopeSet(),
+ access_token_);
+ StartFetching();
+ return;
+ }
+
+ if (response_code != net::HTTP_OK) {
+ // TODO(bauerb): We should return the HTTP response code somehow.
+ DLOG(WARNING) << "HTTP error " << response_code;
+ DispatchGoogleServiceAuthError(
+ GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED),
+ std::string());
+ return;
+ }
+
+ std::string response_body;
+ source->GetResponseAsString(&response_body);
+ scoped_ptr<base::Value> value(base::JSONReader::Read(response_body));
+ DictionaryValue* dict = NULL;
+ if (!value.get() || !value->GetAsDictionary(&dict)) {
+ DispatchNetworkError(net::ERR_INVALID_RESPONSE);
+ return;
+ }
+ std::string auth_code;
+ if (!dict->GetString(kCodeKey, &auth_code)) {
+ DispatchNetworkError(net::ERR_INVALID_RESPONSE);
+ return;
+ }
+
+ gaia::OAuthClientInfo client_info;
+ GaiaUrls* urls = GaiaUrls::GetInstance();
+ client_info.client_id = urls->oauth2_chrome_client_id();
+ client_info.client_secret = urls->oauth2_chrome_client_secret();
+ gaia_oauth_client_.reset(
+ new gaia::GaiaOAuthClient(GaiaUrls::GetInstance()->oauth2_token_url(),
+ context_));
+ gaia_oauth_client_->GetTokensFromAuthCode(client_info, auth_code, kNumRetries,
+ this);
+}
+
+void ManagedUserRefreshTokenFetcherImpl::OnGetTokensResponse(
+ const std::string& refresh_token,
+ const std::string& access_token,
+ int expires_in_seconds) {
+ // TODO(bauerb): It would be nice if we could pass the access token as well,
+ // so we don't need to fetch another one immediately.
+ DispatchGoogleServiceAuthError(GoogleServiceAuthError::AuthErrorNone(),
+ refresh_token);
+}
+
+void ManagedUserRefreshTokenFetcherImpl::OnRefreshTokenResponse(
+ const std::string& access_token,
+ int expires_in_seconds) {
+ NOTREACHED();
+}
+
+void ManagedUserRefreshTokenFetcherImpl::OnOAuthError() {
+ DispatchGoogleServiceAuthError(
+ GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED),
+ std::string());
+}
+
+void ManagedUserRefreshTokenFetcherImpl::OnNetworkError(int response_code) {
+ // TODO(bauerb): We should return the HTTP response code somehow.
+ DLOG(WARNING) << "HTTP error " << response_code;
+ DispatchGoogleServiceAuthError(
+ GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED),
+ std::string());
+}
+
+void ManagedUserRefreshTokenFetcherImpl::DispatchNetworkError(int error_code) {
+ DispatchGoogleServiceAuthError(
+ GoogleServiceAuthError::FromConnectionError(error_code), std::string());
+}
+
+void ManagedUserRefreshTokenFetcherImpl::DispatchGoogleServiceAuthError(
+ const GoogleServiceAuthError& error,
+ const std::string& token) {
+ callback_.Run(error, token);
+ callback_.Reset();
+}
+
+} // namespace
+
+// static
+scoped_ptr<ManagedUserRefreshTokenFetcher>
+ManagedUserRefreshTokenFetcher::Create(OAuth2TokenService* oauth2_token_service,
+ URLRequestContextGetter* context) {
+ scoped_ptr<ManagedUserRefreshTokenFetcher> fetcher(
+ new ManagedUserRefreshTokenFetcherImpl(oauth2_token_service, context));
+ return fetcher.Pass();
+}
+
+ManagedUserRefreshTokenFetcher::~ManagedUserRefreshTokenFetcher() {}
diff --git a/chrome/browser/managed_mode/managed_user_refresh_token_fetcher.h b/chrome/browser/managed_mode/managed_user_refresh_token_fetcher.h
new file mode 100644
index 0000000..392f383
--- /dev/null
+++ b/chrome/browser/managed_mode/managed_user_refresh_token_fetcher.h
@@ -0,0 +1,50 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_MANAGED_MODE_MANAGED_USER_REFRESH_TOKEN_FETCHER_H_
+#define CHROME_BROWSER_MANAGED_MODE_MANAGED_USER_REFRESH_TOKEN_FETCHER_H_
+
+#include <string>
+
+#include "base/callback_forward.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/string16.h"
+
+class GoogleServiceAuthError;
+class OAuth2TokenService;
+
+namespace net {
+class URLRequestContextGetter;
+}
+
+// This class fetches an OAuth2 refresh token that is tied to a managed user ID
+// and downscoped to a special scope for Chrome Sync for managed users.
+// Fetching the token consists of the following steps:
+// 1. Get an access token for the custodian from OAuth2TokenService
+// (either cached or fetched).
+// 2. Call the IssueToken API to mint a scoped authorization code for a
+// refresh token for the managed user from the custodian's access token.
+// 3. Exchange the authorization code for a refresh token for the managed
+// user and return it to the caller. The refresh token can only be used to
+// mint tokens with the special managed user Sync scope.
+class ManagedUserRefreshTokenFetcher {
+ public:
+ typedef base::Callback<void(const GoogleServiceAuthError& /* error */,
+ const std::string& /* refresh_token */)>
+ TokenCallback;
+
+ static scoped_ptr<ManagedUserRefreshTokenFetcher> Create(
+ OAuth2TokenService* oauth2_token_service,
+ net::URLRequestContextGetter* context);
+
+ virtual ~ManagedUserRefreshTokenFetcher();
+
+ virtual void Start(const std::string& managed_user_id,
+ const string16& name,
+ const std::string& device_name,
+ const TokenCallback& callback) = 0;
+};
+
+#endif // CHROME_BROWSER_MANAGED_MODE_MANAGED_USER_REFRESH_TOKEN_FETCHER_H_
diff --git a/chrome/browser/managed_mode/managed_user_refresh_token_fetcher_unittest.cc b/chrome/browser/managed_mode/managed_user_refresh_token_fetcher_unittest.cc
new file mode 100644
index 0000000..83c8285
--- /dev/null
+++ b/chrome/browser/managed_mode/managed_user_refresh_token_fetcher_unittest.cc
@@ -0,0 +1,426 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "base/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/managed_mode/managed_user_refresh_token_fetcher.h"
+#include "chrome/browser/signin/oauth2_token_service.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/test/test_browser_thread.h"
+#include "google_apis/gaia/gaia_urls.h"
+#include "google_apis/gaia/google_service_auth_error.h"
+#include "net/base/net_errors.h"
+#include "net/base/url_util.h"
+#include "net/http/http_request_headers.h"
+#include "net/http/http_status_code.h"
+#include "net/url_request/test_url_fetcher_factory.h"
+#include "net/url_request/url_fetcher_delegate.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const char kManagedUserId[] = "abcdef";
+const char kName[] = "Homestar";
+const char kDeviceName[] = "Compy";
+
+const char kAccessToken[] = "accesstoken";
+const char kAuthorizationCode[] = "authorizationcode";
+const char kManagedUserToken[] = "managedusertoken";
+
+const char kIssueTokenResponseFormat[] =
+ "{"
+ " \"code\": \"%s\""
+ "}";
+
+const char kGetRefreshTokenResponseFormat[] =
+ "{"
+ " \"access_token\": \"<ignored>\","
+ " \"expires_in\": 12345,"
+ " \"refresh_token\": \"%s\""
+ "}";
+
+// MockOAuth2TokenService ---------------------------------------------
+
+class MockOAuth2TokenService : public OAuth2TokenService {
+ public:
+ class Request : public OAuth2TokenService::Request {
+ public:
+ Request(const OAuth2TokenService::ScopeSet& scopes,
+ OAuth2TokenService::Consumer* consumer,
+ MockOAuth2TokenService* owner);
+ virtual ~Request();
+
+ void Succeed();
+ void Fail(GoogleServiceAuthError::State error);
+
+ const OAuth2TokenService::ScopeSet& scopes() const { return scopes_; }
+
+ private:
+ OAuth2TokenService::ScopeSet scopes_;
+
+ OAuth2TokenService::Consumer* consumer_;
+
+ MockOAuth2TokenService* owner_;
+ };
+
+ MockOAuth2TokenService();
+ virtual ~MockOAuth2TokenService();
+
+ Request* request() const { return request_; }
+
+ void ClearRequest(Request* request);
+
+ private:
+ // OAuth2TokenService overrides:
+ virtual scoped_ptr<OAuth2TokenService::Request> StartRequest(
+ const OAuth2TokenService::ScopeSet& scopes,
+ OAuth2TokenService::Consumer* consumer) OVERRIDE;
+ virtual std::string GetRefreshToken() OVERRIDE;
+
+ Request* request_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockOAuth2TokenService);
+};
+
+MockOAuth2TokenService::Request::Request(
+ const OAuth2TokenService::ScopeSet& scopes,
+ OAuth2TokenService::Consumer* consumer,
+ MockOAuth2TokenService* owner)
+ : scopes_(scopes),
+ consumer_(consumer),
+ owner_(owner) {}
+
+MockOAuth2TokenService::Request::~Request() {
+ owner_->ClearRequest(this);
+}
+
+void MockOAuth2TokenService::Request::Succeed() {
+ base::Time expiration_date = base::Time::Now() +
+ base::TimeDelta::FromHours(1);
+ consumer_->OnGetTokenSuccess(this, kAccessToken, expiration_date);
+}
+
+void MockOAuth2TokenService::Request::Fail(
+ GoogleServiceAuthError::State error) {
+ consumer_->OnGetTokenFailure(this, GoogleServiceAuthError(error));
+}
+
+MockOAuth2TokenService::MockOAuth2TokenService()
+ : OAuth2TokenService(NULL),
+ request_(NULL) {}
+
+MockOAuth2TokenService::~MockOAuth2TokenService() {
+ EXPECT_FALSE(request_);
+}
+
+void MockOAuth2TokenService::ClearRequest(
+ MockOAuth2TokenService::Request* request) {
+ if (request_ == request)
+ request_ = NULL;
+}
+
+scoped_ptr<OAuth2TokenService::Request> MockOAuth2TokenService::StartRequest(
+ const OAuth2TokenService::ScopeSet& scopes,
+ OAuth2TokenService::Consumer* consumer) {
+ scoped_ptr<Request> request(new Request(scopes, consumer, this));
+ request_ = request.get();
+ return request.PassAs<OAuth2TokenService::Request>();
+}
+
+std::string MockOAuth2TokenService::GetRefreshToken() {
+ NOTREACHED();
+ return std::string();
+}
+
+// Utility methods --------------------------------------------------
+
+// Slightly hacky way to extract a value from a URL-encoded POST request body.
+bool GetValueForKey(const std::string& encoded_string,
+ const std::string& key,
+ std::string* value) {
+ GURL url("http://example.com/?" + encoded_string);
+ return net::GetValueForKeyInQuery(url, key, value);
+}
+
+void SendResponse(net::TestURLFetcher* url_fetcher,
+ const std::string& response) {
+ url_fetcher->set_status(
+ net::URLRequestStatus(net::URLRequestStatus::SUCCESS, 0));
+ url_fetcher->set_response_code(net::HTTP_OK);
+ url_fetcher->SetResponseString(response);
+ url_fetcher->delegate()->OnURLFetchComplete(url_fetcher);
+}
+
+void SetNetworkError(net::TestURLFetcher* url_fetcher, int error) {
+ url_fetcher->set_status(
+ net::URLRequestStatus(net::URLRequestStatus::FAILED, error));
+ url_fetcher->delegate()->OnURLFetchComplete(url_fetcher);
+}
+
+void SetHttpError(net::TestURLFetcher* url_fetcher, int error) {
+ url_fetcher->set_status(net::URLRequestStatus());
+ url_fetcher->set_response_code(error);
+ url_fetcher->delegate()->OnURLFetchComplete(url_fetcher);
+}
+
+} // namespace
+
+class ManagedUserRefreshTokenFetcherTest : public testing::Test {
+ public:
+ ManagedUserRefreshTokenFetcherTest();
+ virtual ~ManagedUserRefreshTokenFetcherTest() {}
+
+ protected:
+ void StartFetching();
+
+ MockOAuth2TokenService::Request* GetOAuth2TokenServiceRequest();
+ net::TestURLFetcher* GetIssueTokenRequest();
+ net::TestURLFetcher* GetRefreshTokenRequest();
+
+ void MakeIssueTokenRequestSucceed();
+ void MakeRefreshTokenFetchSucceed();
+
+ void Reset();
+
+ const GoogleServiceAuthError& error() const { return error_; }
+ const std::string& token() const { return token_; }
+
+ private:
+ void OnTokenFetched(const GoogleServiceAuthError& error,
+ const std::string& token);
+
+ base::WeakPtrFactory<ManagedUserRefreshTokenFetcherTest> weak_ptr_factory_;
+ base::MessageLoop message_loop_;
+ content::TestBrowserThread ui_thread_;
+ TestingProfile profile_;
+ MockOAuth2TokenService oauth2_token_service_;
+ net::TestURLFetcherFactory url_fetcher_factory_;
+ scoped_ptr<ManagedUserRefreshTokenFetcher> token_fetcher_;
+
+ GoogleServiceAuthError error_;
+ std::string token_;
+};
+
+ManagedUserRefreshTokenFetcherTest::ManagedUserRefreshTokenFetcherTest()
+ : weak_ptr_factory_(this),
+ ui_thread_(content::BrowserThread::UI, &message_loop_),
+ token_fetcher_(
+ ManagedUserRefreshTokenFetcher::Create(&oauth2_token_service_,
+ profile_.GetRequestContext())),
+ error_(GoogleServiceAuthError::NONE) {}
+
+void ManagedUserRefreshTokenFetcherTest::StartFetching() {
+ token_fetcher_->Start(kManagedUserId, UTF8ToUTF16(kName), kDeviceName,
+ base::Bind(
+ &ManagedUserRefreshTokenFetcherTest::OnTokenFetched,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+MockOAuth2TokenService::Request*
+ManagedUserRefreshTokenFetcherTest::GetOAuth2TokenServiceRequest() {
+ MockOAuth2TokenService::Request* request = oauth2_token_service_.request();
+
+ OAuth2TokenService::ScopeSet scopes = request->scopes();
+ EXPECT_EQ(1u, scopes.size());
+ EXPECT_EQ(1u, scopes.count(GaiaUrls::GetInstance()->oauth1_login_scope()));
+ return request;
+}
+
+net::TestURLFetcher*
+ManagedUserRefreshTokenFetcherTest::GetIssueTokenRequest() {
+ net::TestURLFetcher* url_fetcher = url_fetcher_factory_.GetFetcherByID(1);
+ if (!url_fetcher)
+ return NULL;
+
+ EXPECT_EQ(GaiaUrls::GetInstance()->oauth2_issue_token_url(),
+ url_fetcher->GetOriginalURL().spec());
+ std::string access_token;
+ net::HttpRequestHeaders headers;
+ url_fetcher->GetExtraRequestHeaders(&headers);
+ EXPECT_TRUE(headers.GetHeader("Authorization", &access_token));
+ EXPECT_EQ(std::string("Bearer ") + kAccessToken, access_token);
+ const std::string upload_data = url_fetcher->upload_data();
+ std::string managed_user_id;
+ EXPECT_TRUE(GetValueForKey(upload_data, "profile_id", &managed_user_id));
+ EXPECT_EQ(kManagedUserId, managed_user_id);
+ std::string name;
+ EXPECT_TRUE(GetValueForKey(upload_data, "profile_name", &name));
+ EXPECT_EQ(kName, name);
+ std::string device_name;
+ EXPECT_TRUE(GetValueForKey(upload_data, "device_name", &device_name));
+ EXPECT_EQ(kDeviceName, device_name);
+ return url_fetcher;
+}
+
+net::TestURLFetcher*
+ManagedUserRefreshTokenFetcherTest::GetRefreshTokenRequest() {
+ net::TestURLFetcher* url_fetcher = url_fetcher_factory_.GetFetcherByID(0);
+ if (!url_fetcher)
+ return NULL;
+
+ EXPECT_EQ(GaiaUrls::GetInstance()->oauth2_token_url(),
+ url_fetcher->GetOriginalURL().spec());
+ std::string auth_code;
+ EXPECT_TRUE(GetValueForKey(url_fetcher->upload_data(), "code", &auth_code));
+ EXPECT_EQ(kAuthorizationCode, auth_code);
+ return url_fetcher;
+}
+
+void ManagedUserRefreshTokenFetcherTest::MakeIssueTokenRequestSucceed() {
+ SendResponse(GetIssueTokenRequest(),
+ base::StringPrintf(kIssueTokenResponseFormat,
+ kAuthorizationCode));
+}
+
+void ManagedUserRefreshTokenFetcherTest::MakeRefreshTokenFetchSucceed() {
+ SendResponse(GetRefreshTokenRequest(),
+ base::StringPrintf(kGetRefreshTokenResponseFormat,
+ kManagedUserToken));
+}
+
+void ManagedUserRefreshTokenFetcherTest::Reset() {
+ token_fetcher_.reset();
+}
+
+void ManagedUserRefreshTokenFetcherTest::OnTokenFetched(
+ const GoogleServiceAuthError& error,
+ const std::string& token) {
+ error_ = error;
+ token_ = token;
+}
+
+// Tests --------------------------------------------------------
+
+TEST_F(ManagedUserRefreshTokenFetcherTest, Success) {
+ StartFetching();
+ GetOAuth2TokenServiceRequest()->Succeed();
+ MakeIssueTokenRequestSucceed();
+ MakeRefreshTokenFetchSucceed();
+
+ EXPECT_EQ(GoogleServiceAuthError::NONE, error().state());
+ EXPECT_EQ(kManagedUserToken, token());
+}
+
+TEST_F(ManagedUserRefreshTokenFetcherTest, ExpiredAccessToken) {
+ StartFetching();
+ GetOAuth2TokenServiceRequest()->Succeed();
+ SetHttpError(GetIssueTokenRequest(), net::HTTP_UNAUTHORIZED);
+ GetOAuth2TokenServiceRequest()->Succeed();
+ MakeIssueTokenRequestSucceed();
+ MakeRefreshTokenFetchSucceed();
+
+ EXPECT_EQ(GoogleServiceAuthError::NONE, error().state());
+ EXPECT_EQ(kManagedUserToken, token());
+}
+
+TEST_F(ManagedUserRefreshTokenFetcherTest, ExpiredAccessTokenRetry) {
+ // If we get a 401 error for the second time, we should give up instead of
+ // retrying again.
+ StartFetching();
+ GetOAuth2TokenServiceRequest()->Succeed();
+ SetHttpError(GetIssueTokenRequest(), net::HTTP_UNAUTHORIZED);
+ GetOAuth2TokenServiceRequest()->Succeed();
+ SetHttpError(GetIssueTokenRequest(), net::HTTP_UNAUTHORIZED);
+
+ EXPECT_EQ(GoogleServiceAuthError::CONNECTION_FAILED, error().state());
+ EXPECT_EQ(net::ERR_FAILED, error().network_error());
+ EXPECT_EQ(std::string(), token());
+}
+
+TEST_F(ManagedUserRefreshTokenFetcherTest, MalformedIssueTokenResponse) {
+ StartFetching();
+ GetOAuth2TokenServiceRequest()->Succeed();
+ SendResponse(GetIssueTokenRequest(), "choke");
+
+ EXPECT_EQ(GoogleServiceAuthError::CONNECTION_FAILED, error().state());
+ EXPECT_EQ(net::ERR_INVALID_RESPONSE, error().network_error());
+ EXPECT_EQ(std::string(), token());
+}
+
+TEST_F(ManagedUserRefreshTokenFetcherTest, FetchAccessTokenFailure) {
+ StartFetching();
+ GetOAuth2TokenServiceRequest()->Fail(
+ GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
+
+ EXPECT_EQ(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS, error().state());
+ EXPECT_EQ(std::string(), token());
+}
+
+TEST_F(ManagedUserRefreshTokenFetcherTest, IssueTokenNetworkError) {
+ StartFetching();
+ GetOAuth2TokenServiceRequest()->Succeed();
+ SetNetworkError(GetIssueTokenRequest(), net::ERR_SSL_PROTOCOL_ERROR);
+
+ EXPECT_EQ(GoogleServiceAuthError::CONNECTION_FAILED, error().state());
+ EXPECT_EQ(net::ERR_SSL_PROTOCOL_ERROR, error().network_error());
+ EXPECT_EQ(std::string(), token());
+}
+
+TEST_F(ManagedUserRefreshTokenFetcherTest, FetchRefreshTokenNetworkError) {
+ StartFetching();
+ GetOAuth2TokenServiceRequest()->Succeed();
+ MakeIssueTokenRequestSucceed();
+ SetNetworkError(GetRefreshTokenRequest(), net::ERR_CONNECTION_REFUSED);
+ EXPECT_EQ(GoogleServiceAuthError::NONE, error().state());
+ SetNetworkError(GetRefreshTokenRequest(), net::ERR_CONNECTION_REFUSED);
+
+ EXPECT_EQ(GoogleServiceAuthError::CONNECTION_FAILED, error().state());
+ EXPECT_EQ(net::ERR_FAILED, error().network_error());
+ EXPECT_EQ(std::string(), token());
+}
+
+TEST_F(ManagedUserRefreshTokenFetcherTest,
+ FetchRefreshTokenTransientNetworkError) {
+ StartFetching();
+ GetOAuth2TokenServiceRequest()->Succeed();
+ MakeIssueTokenRequestSucceed();
+ SetNetworkError(GetRefreshTokenRequest(), net::ERR_CONNECTION_REFUSED);
+
+ EXPECT_EQ(GoogleServiceAuthError::NONE, error().state());
+ MakeRefreshTokenFetchSucceed();
+
+ EXPECT_EQ(GoogleServiceAuthError::NONE, error().state());
+ EXPECT_EQ(kManagedUserToken, token());
+}
+
+TEST_F(ManagedUserRefreshTokenFetcherTest, FetchRefreshTokenBadRequest) {
+ StartFetching();
+ GetOAuth2TokenServiceRequest()->Succeed();
+ MakeIssueTokenRequestSucceed();
+ SetHttpError(GetRefreshTokenRequest(), net::HTTP_BAD_REQUEST);
+
+ EXPECT_EQ(GoogleServiceAuthError::CONNECTION_FAILED, error().state());
+ EXPECT_EQ(net::ERR_FAILED, error().network_error());
+ EXPECT_EQ(std::string(), token());
+}
+
+TEST_F(ManagedUserRefreshTokenFetcherTest, CancelWhileFetchingAccessToken) {
+ StartFetching();
+ Reset();
+
+ EXPECT_EQ(GoogleServiceAuthError::NONE, error().state());
+ EXPECT_EQ(std::string(), token());
+}
+
+TEST_F(ManagedUserRefreshTokenFetcherTest, CancelWhileCallingIssueToken) {
+ StartFetching();
+ GetOAuth2TokenServiceRequest()->Succeed();
+ Reset();
+
+ EXPECT_EQ(GoogleServiceAuthError::NONE, error().state());
+ EXPECT_EQ(std::string(), token());
+}
+
+TEST_F(ManagedUserRefreshTokenFetcherTest, CancelWhileFetchingRefreshToken) {
+ StartFetching();
+ GetOAuth2TokenServiceRequest()->Succeed();
+ MakeIssueTokenRequestSucceed();
+ Reset();
+
+ EXPECT_EQ(GoogleServiceAuthError::NONE, error().state());
+ EXPECT_EQ(std::string(), token());
+}
diff --git a/chrome/browser/managed_mode/managed_user_registration_service.cc b/chrome/browser/managed_mode/managed_user_registration_service.cc
index a4c9eaf..093d2d1f 100644
--- a/chrome/browser/managed_mode/managed_user_registration_service.cc
+++ b/chrome/browser/managed_mode/managed_user_registration_service.cc
@@ -9,11 +9,14 @@
#include "base/prefs/pref_service.h"
#include "base/rand_util.h"
#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/managed_mode/managed_user_refresh_token_fetcher.h"
#include "chrome/browser/managed_mode/managed_user_service.h"
#include "chrome/browser/managed_mode/managed_user_service_factory.h"
#include "chrome/browser/prefs/scoped_user_pref_update.h"
+#include "chrome/browser/sync/glue/device_info.h"
#include "chrome/common/pref_names.h"
#include "components/user_prefs/pref_registry_syncable.h"
+#include "google_apis/gaia/gaia_urls.h"
#include "google_apis/gaia/google_service_auth_error.h"
#include "sync/api/sync_change.h"
#include "sync/api/sync_error_factory.h"
@@ -52,9 +55,11 @@ SyncData CreateLocalSyncData(const std::string& id,
} // namespace
ManagedUserRegistrationService::ManagedUserRegistrationService(
- PrefService* prefs)
+ PrefService* prefs,
+ scoped_ptr<ManagedUserRefreshTokenFetcher> token_fetcher)
: weak_ptr_factory_(this),
prefs_(prefs),
+ token_fetcher_(token_fetcher.Pass()),
pending_managed_user_acknowledged_(false) {
pref_change_registrar_.Init(prefs);
pref_change_registrar_.Add(
@@ -103,7 +108,9 @@ void ManagedUserRegistrationService::Register(
}
callback_ = callback;
- OnReceivedToken("abcdef"); // TODO(bauerb): This is a stub implementation.
+ browser_sync::DeviceInfo::CreateLocalDeviceInfo(
+ base::Bind(&ManagedUserRegistrationService::FetchToken,
+ weak_ptr_factory_.GetWeakPtr(), name));
}
ProfileManager::CreateCallback
@@ -268,8 +275,24 @@ void ManagedUserRegistrationService::OnManagedUserAcknowledged(
DispatchCallbackIfReady();
}
-void ManagedUserRegistrationService::OnReceivedToken(const std::string& token) {
- DCHECK(pending_managed_user_token_.empty());
+void ManagedUserRegistrationService::FetchToken(
+ const string16& name,
+ const browser_sync::DeviceInfo& device_info) {
+ token_fetcher_->Start(
+ pending_managed_user_id_, name, device_info.client_name(),
+ base::Bind(&ManagedUserRegistrationService::OnReceivedToken,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void ManagedUserRegistrationService::OnReceivedToken(
+ const GoogleServiceAuthError& error,
+ const std::string& token) {
+ if (error.state() != GoogleServiceAuthError::NONE) {
+ DispatchCallback(error);
+ return;
+ }
+
+ DCHECK(!token.empty());
pending_managed_user_token_ = token;
DispatchCallbackIfReady();
}
diff --git a/chrome/browser/managed_mode/managed_user_registration_service.h b/chrome/browser/managed_mode/managed_user_registration_service.h
index fcdefb0..d6f9221 100644
--- a/chrome/browser/managed_mode/managed_user_registration_service.h
+++ b/chrome/browser/managed_mode/managed_user_registration_service.h
@@ -17,8 +17,13 @@
#include "sync/api/syncable_service.h"
class GoogleServiceAuthError;
+class ManagedUserRefreshTokenFetcher;
class PrefService;
+namespace browser_sync {
+class DeviceInfo;
+}
+
namespace user_prefs {
class PrefRegistrySyncable;
}
@@ -37,13 +42,17 @@ class ManagedUserRegistrationService : public BrowserContextKeyedService,
const std::string& /* token */)>
RegistrationCallback;
- explicit ManagedUserRegistrationService(PrefService* prefs);
+ ManagedUserRegistrationService(
+ PrefService* prefs,
+ scoped_ptr<ManagedUserRefreshTokenFetcher> token_fetcher);
virtual ~ManagedUserRegistrationService();
static void RegisterUserPrefs(user_prefs::PrefRegistrySyncable* registry);
// Registers a new managed user with the server. |name| is the display name of
// the user. |callback| is called with the result of the registration.
+ // TODO(bauerb): There should be a way to cancel a pending managed user
+ // registration.
void Register(const string16& name, const RegistrationCallback& callback);
// Convenience method that registers a new managed user with the server and
@@ -77,8 +86,13 @@ class ManagedUserRegistrationService : public BrowserContextKeyedService,
// Called when the Sync server has acknowledged a newly created managed user.
void OnManagedUserAcknowledged(const std::string& managed_user_id);
+ // Fetches the managed user token when we have the device info.
+ void FetchToken(const string16& name,
+ const browser_sync::DeviceInfo& device_info);
+
// Called when we have received a token for the managed user.
- void OnReceivedToken(const std::string& token);
+ void OnReceivedToken(const GoogleServiceAuthError& error,
+ const std::string& token);
// Dispatches the callback if all the conditions have been met.
void DispatchCallbackIfReady();
@@ -94,6 +108,7 @@ class ManagedUserRegistrationService : public BrowserContextKeyedService,
base::WeakPtrFactory<ManagedUserRegistrationService> weak_ptr_factory_;
PrefService* prefs_;
PrefChangeRegistrar pref_change_registrar_;
+ scoped_ptr<ManagedUserRefreshTokenFetcher> token_fetcher_;
scoped_ptr<syncer::SyncChangeProcessor> sync_processor_;
scoped_ptr<syncer::SyncErrorFactory> error_handler_;
diff --git a/chrome/browser/managed_mode/managed_user_registration_service_factory.cc b/chrome/browser/managed_mode/managed_user_registration_service_factory.cc
index 2ed577a..9e4cb41 100644
--- a/chrome/browser/managed_mode/managed_user_registration_service_factory.cc
+++ b/chrome/browser/managed_mode/managed_user_registration_service_factory.cc
@@ -4,7 +4,11 @@
#include "chrome/browser/managed_mode/managed_user_registration_service_factory.h"
+#include "chrome/browser/managed_mode/managed_user_refresh_token_fetcher.h"
#include "chrome/browser/managed_mode/managed_user_registration_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/profile_oauth2_token_service.h"
+#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
#include "components/browser_context_keyed_service/browser_context_dependency_manager.h"
// static
@@ -23,13 +27,20 @@ ManagedUserRegistrationServiceFactory::GetInstance() {
// static
BrowserContextKeyedService*
ManagedUserRegistrationServiceFactory::BuildInstanceFor(Profile* profile) {
- return new ManagedUserRegistrationService(profile->GetPrefs());
+ OAuth2TokenService* oauth2_token_service =
+ ProfileOAuth2TokenServiceFactory::GetForProfile(profile);
+ return new ManagedUserRegistrationService(
+ profile->GetPrefs(),
+ ManagedUserRefreshTokenFetcher::Create(oauth2_token_service,
+ profile->GetRequestContext()));
}
ManagedUserRegistrationServiceFactory::ManagedUserRegistrationServiceFactory()
: BrowserContextKeyedServiceFactory(
"ManagedUserRegistrationService",
- BrowserContextDependencyManager::GetInstance()) {}
+ BrowserContextDependencyManager::GetInstance()) {
+ DependsOn(ProfileOAuth2TokenServiceFactory::GetInstance());
+}
ManagedUserRegistrationServiceFactory::
~ManagedUserRegistrationServiceFactory() {}
diff --git a/chrome/browser/managed_mode/managed_user_registration_service_unittest.cc b/chrome/browser/managed_mode/managed_user_registration_service_unittest.cc
index f3b3f3c..3947786 100644
--- a/chrome/browser/managed_mode/managed_user_registration_service_unittest.cc
+++ b/chrome/browser/managed_mode/managed_user_registration_service_unittest.cc
@@ -5,7 +5,10 @@
#include <string>
#include "base/bind.h"
+#include "base/message_loop.h"
+#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/managed_mode/managed_user_refresh_token_fetcher.h"
#include "chrome/browser/managed_mode/managed_user_registration_service.h"
#include "chrome/browser/prefs/scoped_user_pref_update.h"
#include "chrome/common/pref_names.h"
@@ -29,6 +32,8 @@ using syncer::SyncMergeResult;
namespace {
+const char kManagedUserToken[] = "managedusertoken";
+
class MockChangeProcessor : public SyncChangeProcessor {
public:
MockChangeProcessor() {}
@@ -62,6 +67,22 @@ SyncChange MockChangeProcessor::GetChange(const std::string& id) const {
return SyncChange();
}
+class MockManagedUserRefreshTokenFetcher
+ : public ManagedUserRefreshTokenFetcher {
+ public:
+ MockManagedUserRefreshTokenFetcher() {}
+ virtual ~MockManagedUserRefreshTokenFetcher() {}
+
+ // ManagedUserRefreshTokenFetcher implementation:
+ virtual void Start(const std::string& managed_user_id,
+ const string16& name,
+ const std::string& device_name,
+ const TokenCallback& callback) OVERRIDE {
+ GoogleServiceAuthError error(GoogleServiceAuthError::NONE);
+ callback.Run(error, kManagedUserToken);
+ }
+};
+
} // namespace
class ManagedUserRegistrationServiceTest : public ::testing::Test {
@@ -94,6 +115,8 @@ class ManagedUserRegistrationServiceTest : public ::testing::Test {
void OnManagedUserRegistered(const GoogleServiceAuthError& error,
const std::string& token);
+ base::MessageLoop message_loop_;
+ base::RunLoop run_loop_;
base::WeakPtrFactory<ManagedUserRegistrationServiceTest> weak_ptr_factory_;
TestingPrefServiceSyncable prefs_;
scoped_ptr<ManagedUserRegistrationService> service_;
@@ -114,12 +137,15 @@ class ManagedUserRegistrationServiceTest : public ::testing::Test {
ManagedUserRegistrationServiceTest::ManagedUserRegistrationServiceTest()
: weak_ptr_factory_(this),
- service_(new ManagedUserRegistrationService(&prefs_)),
change_processor_(NULL),
sync_data_id_(0),
received_callback_(false),
error_(GoogleServiceAuthError::NUM_STATES) {
ManagedUserRegistrationService::RegisterUserPrefs(prefs_.registry());
+ scoped_ptr<ManagedUserRefreshTokenFetcher> token_fetcher(
+ new MockManagedUserRefreshTokenFetcher);
+ service_.reset(
+ new ManagedUserRegistrationService(&prefs_, token_fetcher.Pass()));
}
ManagedUserRegistrationServiceTest::~ManagedUserRegistrationServiceTest() {
@@ -180,6 +206,8 @@ void ManagedUserRegistrationServiceTest::Acknowledge() {
SyncData::CreateRemoteData(++sync_data_id_, specifics)));
}
service()->ProcessSyncChanges(FROM_HERE, new_changes);
+
+ run_loop_.Run();
}
void ManagedUserRegistrationServiceTest::ResetService() {
@@ -194,6 +222,7 @@ void ManagedUserRegistrationServiceTest::OnManagedUserRegistered(
received_callback_ = true;
error_ = error;
token_ = token;
+ run_loop_.Quit();
}
TEST_F(ManagedUserRegistrationServiceTest, MergeEmpty) {
diff --git a/chrome/browser/signin/android_profile_oauth2_token_service.cc b/chrome/browser/signin/android_profile_oauth2_token_service.cc
index 698ee44..c013185 100644
--- a/chrome/browser/signin/android_profile_oauth2_token_service.cc
+++ b/chrome/browser/signin/android_profile_oauth2_token_service.cc
@@ -33,7 +33,7 @@ scoped_ptr<OAuth2TokenService::Request>
ProfileSyncServiceAndroid::GetProfileSyncServiceAndroid();
sync_service->FetchOAuth2Token(
scope_list.front(),
- base::Bind(&OAuth2TokenService::InformConsumer,
+ base::Bind(&RequestImpl::InformConsumer,
request->AsWeakPtr()));
return request.PassAs<Request>();
}
diff --git a/chrome/browser/signin/oauth2_token_service.cc b/chrome/browser/signin/oauth2_token_service.cc
index aeacd41..141c813 100644
--- a/chrome/browser/signin/oauth2_token_service.cc
+++ b/chrome/browser/signin/oauth2_token_service.cc
@@ -303,18 +303,6 @@ bool OAuth2TokenService::RefreshTokenIsAvailable() {
return !GetRefreshToken().empty();
}
-// static
-void OAuth2TokenService::InformConsumer(
- base::WeakPtr<OAuth2TokenService::RequestImpl> request,
- const GoogleServiceAuthError& error,
- const std::string& access_token,
- const base::Time& expiration_date) {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-
- if (request)
- request->InformConsumer(error, access_token, expiration_date);
-}
-
scoped_ptr<OAuth2TokenService::Request> OAuth2TokenService::StartRequest(
const OAuth2TokenService::ScopeSet& scopes,
OAuth2TokenService::Consumer* consumer) {
@@ -325,7 +313,7 @@ scoped_ptr<OAuth2TokenService::Request> OAuth2TokenService::StartRequest(
std::string refresh_token = GetRefreshToken();
if (refresh_token.empty()) {
base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
- &OAuth2TokenService::InformConsumer,
+ &RequestImpl::InformConsumer,
request->AsWeakPtr(),
GoogleServiceAuthError(
GoogleServiceAuthError::USER_NOT_SIGNED_UP),
@@ -361,7 +349,7 @@ scoped_ptr<OAuth2TokenService::Request>
const CacheEntry* cache_entry = GetCacheEntry(scopes);
scoped_ptr<RequestImpl> request(new RequestImpl(consumer));
base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
- &OAuth2TokenService::InformConsumer,
+ &RequestImpl::InformConsumer,
request->AsWeakPtr(),
GoogleServiceAuthError(GoogleServiceAuthError::NONE),
cache_entry->access_token,
@@ -440,7 +428,7 @@ bool OAuth2TokenService::RemoveCacheEntry(
const std::string& token_to_remove) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
TokenCache::iterator token_iterator = token_cache_.find(scopes);
- if (token_iterator == token_cache_.end() &&
+ if (token_iterator != token_cache_.end() &&
token_iterator->second.access_token == token_to_remove) {
token_cache_.erase(token_iterator);
return true;
diff --git a/chrome/browser/signin/oauth2_token_service.h b/chrome/browser/signin/oauth2_token_service.h
index 9260f41..e96b9c1 100644
--- a/chrome/browser/signin/oauth2_token_service.h
+++ b/chrome/browser/signin/oauth2_token_service.h
@@ -148,12 +148,6 @@ class OAuth2TokenService {
Consumer* const consumer_;
};
- // Informs the consumer of |request| fetch results.
- static void InformConsumer(base::WeakPtr<RequestImpl> request,
- const GoogleServiceAuthError& error,
- const std::string& access_token,
- const base::Time& expiration_date);
-
private:
// Class that fetches an OAuth2 access token for a given set of scopes and
// OAuth2 refresh token.
diff --git a/chrome/browser/signin/oauth2_token_service_unittest.cc b/chrome/browser/signin/oauth2_token_service_unittest.cc
index 4acffb9..5808b9c 100644
--- a/chrome/browser/signin/oauth2_token_service_unittest.cc
+++ b/chrome/browser/signin/oauth2_token_service_unittest.cc
@@ -404,7 +404,7 @@ TEST_F(OAuth2TokenServiceTest, RetryingConsumer) {
EXPECT_EQ(0, consumer.number_of_errors_);
net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
- EXPECT_TRUE(fetcher);
+ ASSERT_TRUE(fetcher);
fetcher->set_response_code(net::HTTP_UNAUTHORIZED);
fetcher->SetResponseString(std::string());
fetcher->delegate()->OnURLFetchComplete(fetcher);
@@ -412,10 +412,59 @@ TEST_F(OAuth2TokenServiceTest, RetryingConsumer) {
EXPECT_EQ(1, consumer.number_of_errors_);
fetcher = factory_.GetFetcherByID(0);
- EXPECT_TRUE(fetcher);
+ ASSERT_TRUE(fetcher);
fetcher->set_response_code(net::HTTP_UNAUTHORIZED);
fetcher->SetResponseString(std::string());
fetcher->delegate()->OnURLFetchComplete(fetcher);
EXPECT_EQ(0, consumer.number_of_successful_tokens_);
EXPECT_EQ(2, consumer.number_of_errors_);
}
+
+TEST_F(OAuth2TokenServiceTest, InvalidateToken) {
+ std::set<std::string> scopes;
+ oauth2_service_->set_refresh_token("refreshToken");
+
+ // First request.
+ scoped_ptr<OAuth2TokenService::Request> request(
+ oauth2_service_->StartRequest(scopes, &consumer_));
+ message_loop_.RunUntilIdle();
+
+ EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
+ EXPECT_EQ(0, consumer_.number_of_errors_);
+ net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
+ EXPECT_TRUE(fetcher);
+ fetcher->set_response_code(net::HTTP_OK);
+ fetcher->SetResponseString(GetValidTokenResponse("token", 3600));
+ fetcher->delegate()->OnURLFetchComplete(fetcher);
+ EXPECT_EQ(1, consumer_.number_of_successful_tokens_);
+ EXPECT_EQ(0, consumer_.number_of_errors_);
+ EXPECT_EQ("token", consumer_.last_token_);
+
+ // Second request, should return the same token without needing a network
+ // request.
+ scoped_ptr<OAuth2TokenService::Request> request2(
+ oauth2_service_->StartRequest(scopes, &consumer_));
+ message_loop_.RunUntilIdle();
+
+ // No new network fetcher.
+ EXPECT_EQ(fetcher, factory_.GetFetcherByID(0));
+ EXPECT_EQ(2, consumer_.number_of_successful_tokens_);
+ EXPECT_EQ(0, consumer_.number_of_errors_);
+ EXPECT_EQ("token", consumer_.last_token_);
+
+ // Invalidating the token should return a new token on the next request.
+ oauth2_service_->InvalidateToken(scopes, consumer_.last_token_);
+ scoped_ptr<OAuth2TokenService::Request> request3(
+ oauth2_service_->StartRequest(scopes, &consumer_));
+ message_loop_.RunUntilIdle();
+ EXPECT_EQ(2, consumer_.number_of_successful_tokens_);
+ EXPECT_EQ(0, consumer_.number_of_errors_);
+ fetcher = factory_.GetFetcherByID(0);
+ EXPECT_TRUE(fetcher);
+ fetcher->set_response_code(net::HTTP_OK);
+ fetcher->SetResponseString(GetValidTokenResponse("token2", 3600));
+ fetcher->delegate()->OnURLFetchComplete(fetcher);
+ EXPECT_EQ(3, consumer_.number_of_successful_tokens_);
+ EXPECT_EQ(0, consumer_.number_of_errors_);
+ EXPECT_EQ("token2", consumer_.last_token_);
+}
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 979337d..b321f35 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -965,6 +965,8 @@
'browser/managed_mode/managed_user_service.h',
'browser/managed_mode/managed_user_service_factory.cc',
'browser/managed_mode/managed_user_service_factory.h',
+ 'browser/managed_mode/managed_user_refresh_token_fetcher.cc',
+ 'browser/managed_mode/managed_user_refresh_token_fetcher.h',
'browser/media/audio_stream_indicator.cc',
'browser/media/audio_stream_indicator.h',
'browser/media/media_capture_devices_dispatcher.cc',
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi
index 22860fd..4695541 100644
--- a/chrome/chrome_tests_unit.gypi
+++ b/chrome/chrome_tests_unit.gypi
@@ -915,6 +915,7 @@
'browser/managed_mode/managed_user_passphrase_unittest.cc',
'browser/managed_mode/managed_user_registration_service_unittest.cc',
'browser/managed_mode/managed_user_service_unittest.cc',
+ 'browser/managed_mode/managed_user_refresh_token_fetcher_unittest.cc',
'browser/media_galleries/fileapi/itunes_finder_win_unittest.cc',
'browser/media_galleries/fileapi/native_media_file_util_unittest.cc',
'browser/media_galleries/fileapi/picasa/picasa_album_table_reader_unittest.cc',
diff --git a/google_apis/gaia/gaia_oauth_client.cc b/google_apis/gaia/gaia_oauth_client.cc
index a9bae4f..7e15c9d 100644
--- a/google_apis/gaia/gaia_oauth_client.cc
+++ b/google_apis/gaia/gaia_oauth_client.cc
@@ -203,7 +203,7 @@ void GaiaOAuthClient::Core::HandleResponse(
// If we don't have an access token yet and the the error was not
// RC_BAD_REQUEST, we may need to retry.
if ((source->GetMaxRetriesOn5xx() != -1) &&
- (num_retries_ > source->GetMaxRetriesOn5xx())) {
+ (num_retries_ >= source->GetMaxRetriesOn5xx())) {
// Retry limit reached. Give up.
delegate_->OnNetworkError(source->GetResponseCode());
} else {