summaryrefslogtreecommitdiff
path: root/chromium/google_apis
diff options
context:
space:
mode:
authorAndras Becsi <andras.becsi@digia.com>2013-12-11 21:33:03 +0100
committerAndras Becsi <andras.becsi@digia.com>2013-12-13 12:34:07 +0100
commitf2a33ff9cbc6d19943f1c7fbddd1f23d23975577 (patch)
tree0586a32aa390ade8557dfd6b4897f43a07449578 /chromium/google_apis
parent5362912cdb5eea702b68ebe23702468d17c3017a (diff)
downloadqtwebengine-chromium-f2a33ff9cbc6d19943f1c7fbddd1f23d23975577.tar.gz
Update Chromium to branch 1650 (31.0.1650.63)
Change-Id: I57d8c832eaec1eb2364e0a8e7352a6dd354db99f Reviewed-by: Jocelyn Turcotte <jocelyn.turcotte@digia.com>
Diffstat (limited to 'chromium/google_apis')
-rw-r--r--chromium/google_apis/gaia/fake_gaia.cc143
-rw-r--r--chromium/google_apis/gaia/fake_gaia.h86
-rw-r--r--chromium/google_apis/gaia/gaia_auth_fetcher.cc23
-rw-r--r--chromium/google_apis/gaia/gaia_auth_fetcher_unittest.cc8
-rw-r--r--chromium/google_apis/gaia/gaia_constants.cc3
-rw-r--r--chromium/google_apis/gaia/gaia_constants.h1
-rw-r--r--chromium/google_apis/gaia/gaia_switches.cc7
-rw-r--r--chromium/google_apis/gaia/gaia_switches.h19
-rw-r--r--chromium/google_apis/gaia/gaia_urls.cc217
-rw-r--r--chromium/google_apis/gaia/gaia_urls.h97
-rw-r--r--chromium/google_apis/gaia/google_service_auth_error.h4
-rw-r--r--chromium/google_apis/gaia/oauth2_access_token_fetcher.cc2
-rw-r--r--chromium/google_apis/gaia/oauth2_mint_token_flow.cc2
-rw-r--r--chromium/google_apis/gaia/oauth2_token_service.cc737
-rw-r--r--chromium/google_apis/gaia/oauth2_token_service.h341
-rw-r--r--chromium/google_apis/gaia/oauth2_token_service_test_util.cc44
-rw-r--r--chromium/google_apis/gaia/oauth2_token_service_test_util.h34
-rw-r--r--chromium/google_apis/gaia/oauth2_token_service_unittest.cc635
-rw-r--r--chromium/google_apis/google_api_keys.cc23
-rw-r--r--chromium/google_apis/google_api_keys.h1
-rwxr-xr-xchromium/google_apis/google_api_keys.py5
-rw-r--r--chromium/google_apis/google_api_keys_unittest.cc63
-rw-r--r--chromium/google_apis/google_apis.gyp37
23 files changed, 2331 insertions, 201 deletions
diff --git a/chromium/google_apis/gaia/fake_gaia.cc b/chromium/google_apis/gaia/fake_gaia.cc
new file mode 100644
index 00000000000..9f4a8122a22
--- /dev/null
+++ b/chromium/google_apis/gaia/fake_gaia.cc
@@ -0,0 +1,143 @@
+// Copyright (c) 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 "google_apis/gaia/fake_gaia.h"
+
+#include "base/base_paths.h"
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/json/json_writer.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/strings/string_util.h"
+#include "base/values.h"
+#include "google_apis/gaia/gaia_urls.h"
+#include "net/base/url_util.h"
+#include "net/http/http_status_code.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
+#include "url/url_parse.h"
+
+using namespace net::test_server;
+
+namespace {
+const base::FilePath::CharType kServiceLogin[] =
+ FILE_PATH_LITERAL("google_apis/test/service_login.html");
+}
+
+FakeGaia::AccessTokenInfo::AccessTokenInfo()
+ : expires_in(3600) {}
+
+FakeGaia::AccessTokenInfo::~AccessTokenInfo() {}
+
+FakeGaia::FakeGaia() {
+ base::FilePath source_root_dir;
+ PathService::Get(base::DIR_SOURCE_ROOT, &source_root_dir);
+ CHECK(base::ReadFileToString(
+ source_root_dir.Append(base::FilePath(kServiceLogin)),
+ &service_login_response_));
+}
+
+FakeGaia::~FakeGaia() {}
+
+scoped_ptr<HttpResponse> FakeGaia::HandleRequest(const HttpRequest& request) {
+ GaiaUrls* gaia_urls = GaiaUrls::GetInstance();
+
+ // The scheme and host of the URL is actually not important but required to
+ // get a valid GURL in order to parse |request.relative_url|.
+ GURL request_url = GURL("http://localhost").Resolve(request.relative_url);
+ std::string request_path = request_url.path();
+
+ scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse());
+ if (request_path == gaia_urls->service_login_url().path()) {
+ http_response->set_code(net::HTTP_OK);
+ http_response->set_content(service_login_response_);
+ http_response->set_content_type("text/html");
+ } else if (request_path == gaia_urls->service_login_auth_url().path()) {
+ std::string continue_url = gaia_urls->service_login_url().spec();
+ GetQueryParameter(request.content, "continue", &continue_url);
+ http_response->set_code(net::HTTP_OK);
+ const std::string redirect_js =
+ "document.location.href = '" + continue_url + "'";
+ http_response->set_content(
+ "<HTML><HEAD><SCRIPT>\n" + redirect_js + "\n</SCRIPT></HEAD></HTML>");
+ http_response->set_content_type("text/html");
+ } else if (request_path == gaia_urls->oauth2_token_url().path()) {
+ std::string refresh_token;
+ const AccessTokenInfo* token_info = NULL;
+ if (GetQueryParameter(request.content, "refresh_token", &refresh_token) &&
+ (token_info = GetAccessTokenInfo(refresh_token))) {
+ base::DictionaryValue response_dict;
+ response_dict.SetString("access_token", token_info->token);
+ response_dict.SetInteger("expires_in", 3600);
+ FormatJSONResponse(response_dict, http_response.get());
+ } else {
+ http_response->set_code(net::HTTP_BAD_REQUEST);
+ }
+ } else if (request_path == gaia_urls->oauth2_token_info_url().path()) {
+ const AccessTokenInfo* token_info = NULL;
+ std::string access_token;
+ if (GetQueryParameter(request.content, "access_token", &access_token)) {
+ for (AccessTokenInfoMap::const_iterator entry(
+ access_token_info_map_.begin());
+ entry != access_token_info_map_.end();
+ ++entry) {
+ if (entry->second.token == access_token) {
+ token_info = &(entry->second);
+ break;
+ }
+ }
+ }
+
+ if (token_info) {
+ base::DictionaryValue response_dict;
+ response_dict.SetString("issued_to", token_info->issued_to);
+ response_dict.SetString("audience", token_info->audience);
+ response_dict.SetString("user_id", token_info->user_id);
+ std::vector<std::string> scope_vector(token_info->scopes.begin(),
+ token_info->scopes.end());
+ response_dict.SetString("scope", JoinString(scope_vector, " "));
+ response_dict.SetInteger("expires_in", token_info->expires_in);
+ response_dict.SetString("email", token_info->email);
+ FormatJSONResponse(response_dict, http_response.get());
+ } else {
+ http_response->set_code(net::HTTP_BAD_REQUEST);
+ }
+ } else {
+ // Request not understood.
+ return scoped_ptr<HttpResponse>();
+ }
+
+ return http_response.PassAs<HttpResponse>();
+}
+
+void FakeGaia::IssueOAuthToken(const std::string& refresh_token,
+ const AccessTokenInfo& token_info) {
+ access_token_info_map_[refresh_token] = token_info;
+}
+
+void FakeGaia::FormatJSONResponse(const base::DictionaryValue& response_dict,
+ BasicHttpResponse* http_response) {
+ std::string response_json;
+ base::JSONWriter::Write(&response_dict, &response_json);
+ http_response->set_content(response_json);
+ http_response->set_code(net::HTTP_OK);
+}
+
+const FakeGaia::AccessTokenInfo* FakeGaia::GetAccessTokenInfo(
+ const std::string& refresh_token) const {
+ AccessTokenInfoMap::const_iterator entry =
+ access_token_info_map_.find(refresh_token);
+ return entry == access_token_info_map_.end() ? NULL : &entry->second;
+}
+
+// static
+bool FakeGaia::GetQueryParameter(const std::string& query,
+ const std::string& key,
+ std::string* value) {
+ // Name and scheme actually don't matter, but are required to get a valid URL
+ // for parsing.
+ GURL query_url("http://localhost?" + query);
+ return net::GetValueForKeyInQuery(query_url, key, value);
+}
diff --git a/chromium/google_apis/gaia/fake_gaia.h b/chromium/google_apis/gaia/fake_gaia.h
new file mode 100644
index 00000000000..24f6891c420
--- /dev/null
+++ b/chromium/google_apis/gaia/fake_gaia.h
@@ -0,0 +1,86 @@
+// Copyright (c) 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 GOOGLE_APIS_GAIA_FAKE_GAIA_H_
+#define GOOGLE_APIS_GAIA_FAKE_GAIA_H_
+
+#include <map>
+#include <set>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace net {
+namespace test_server {
+class BasicHttpResponse;
+struct HttpRequest;
+class HttpResponse;
+}
+}
+
+// This is a test helper that implements a fake GAIA service for use in browser
+// tests. It's mainly intended for use with EmbeddedTestServer, for which it can
+// be registered as an additional request handler.
+class FakeGaia {
+ public:
+ typedef std::set<std::string> ScopeSet;
+
+ // Access token details used for token minting and the token info endpoint.
+ struct AccessTokenInfo {
+ AccessTokenInfo();
+ ~AccessTokenInfo();
+
+ std::string token;
+ std::string issued_to;
+ std::string audience;
+ std::string user_id;
+ ScopeSet scopes;
+ int expires_in;
+ std::string email;
+ };
+
+ FakeGaia();
+ ~FakeGaia();
+
+ // Handles a request and returns a response if the request was recognized as a
+ // GAIA request. Note that this respects the switches::kGaiaUrl and friends so
+ // that this can used with EmbeddedTestServer::RegisterRequestHandler().
+ scoped_ptr<net::test_server::HttpResponse> HandleRequest(
+ const net::test_server::HttpRequest& request);
+
+ // Configures an OAuth2 token that'll be returned when a client requests an
+ // access token for the given refresh token.
+ void IssueOAuthToken(const std::string& refresh_token,
+ const AccessTokenInfo& token_info);
+
+ private:
+ typedef std::map<std::string, AccessTokenInfo> AccessTokenInfoMap;
+
+ // Formats a JSON response with the data in |response_dict|.
+ void FormatJSONResponse(const base::DictionaryValue& response_dict,
+ net::test_server::BasicHttpResponse* http_response);
+
+ // Returns the access token associated with |refresh_token| or NULL if not
+ // found.
+ const AccessTokenInfo* GetAccessTokenInfo(
+ const std::string& refresh_token) const;
+
+ // Extracts the parameter named |key| from |query| and places it in |value|.
+ // Returns false if no parameter is found.
+ static bool GetQueryParameter(const std::string& query,
+ const std::string& key,
+ std::string* value);
+
+ AccessTokenInfoMap access_token_info_map_;
+ std::string service_login_response_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeGaia);
+};
+
+#endif // GOOGLE_APIS_GAIA_FAKE_GAIA_H_
diff --git a/chromium/google_apis/gaia/gaia_auth_fetcher.cc b/chromium/google_apis/gaia/gaia_auth_fetcher.cc
index 67756deb99b..f0e26445a52 100644
--- a/chromium/google_apis/gaia/gaia_auth_fetcher.cc
+++ b/chromium/google_apis/gaia/gaia_auth_fetcher.cc
@@ -103,7 +103,7 @@ const char GaiaAuthFetcher::kMergeSessionFormat[] =
"source=%s";
// static
const char GaiaAuthFetcher::kUberAuthTokenURLFormat[] =
- "%s?source=%s&"
+ "?source=%s&"
"issueuberauth=1";
const char GaiaAuthFetcher::kOAuthLoginFormat[] = "service=%s&source=%s";
@@ -179,8 +179,8 @@ GaiaAuthFetcher::GaiaAuthFetcher(GaiaAuthConsumer* consumer,
oauth2_revoke_gurl_(GaiaUrls::GetInstance()->oauth2_revoke_url()),
get_user_info_gurl_(GaiaUrls::GetInstance()->get_user_info_url()),
merge_session_gurl_(GaiaUrls::GetInstance()->merge_session_url()),
- uberauth_token_gurl_(base::StringPrintf(kUberAuthTokenURLFormat,
- GaiaUrls::GetInstance()->oauth1_login_url().c_str(), source.c_str())),
+ uberauth_token_gurl_(GaiaUrls::GetInstance()->oauth1_login_url().Resolve(
+ base::StringPrintf(kUberAuthTokenURLFormat, source.c_str()))),
oauth_login_gurl_(GaiaUrls::GetInstance()->oauth1_login_url()),
client_login_to_oauth2_gurl_(
GaiaUrls::GetInstance()->client_login_to_oauth2_url()),
@@ -517,7 +517,7 @@ void GaiaAuthFetcher::StartLsoForOAuthLoginTokenExchange(
DVLOG(1) << "Starting OAuth login token exchange with auth_token";
request_body_ = MakeGetAuthCodeBody();
client_login_to_oauth2_gurl_ =
- GURL(GaiaUrls::GetInstance()->client_login_to_oauth2_url());
+ GaiaUrls::GetInstance()->client_login_to_oauth2_url();
fetcher_.reset(CreateGaiaFetcher(getter_,
request_body_,
@@ -551,11 +551,12 @@ void GaiaAuthFetcher::StartCookieForOAuthLoginTokenExchange(
DVLOG(1) << "Starting OAuth login token fetch with cookie jar";
request_body_ = MakeGetAuthCodeBody();
- std::string url = GaiaUrls::GetInstance()->client_login_to_oauth2_url();
- if (!session_index.empty())
- url += "?authuser=" + session_index;
-
- client_login_to_oauth2_gurl_ = GURL(url);
+ client_login_to_oauth2_gurl_ =
+ GaiaUrls::GetInstance()->client_login_to_oauth2_url();
+ if (!session_index.empty()) {
+ client_login_to_oauth2_gurl_ =
+ client_login_to_oauth2_gurl_.Resolve("?authuser=" + session_index);
+ }
fetcher_.reset(CreateGaiaFetcher(getter_,
request_body_,
@@ -684,7 +685,7 @@ GoogleServiceAuthError GaiaAuthFetcher::GenerateAuthError(
if (error == kCaptchaError) {
GURL image_url(
- GaiaUrls::GetInstance()->captcha_url_prefix() + captcha_url);
+ GaiaUrls::GetInstance()->captcha_base_url().Resolve(captcha_url));
GURL unlock_url(url);
return GoogleServiceAuthError::FromClientLoginCaptchaChallenge(
captcha_token, image_url, unlock_url);
@@ -737,7 +738,7 @@ GoogleServiceAuthError GaiaAuthFetcher::GenerateOAuthLoginError(
if (error == kCaptchaErrorCode) {
GURL image_url(
- GaiaUrls::GetInstance()->captcha_url_prefix() + captcha_url);
+ GaiaUrls::GetInstance()->captcha_base_url().Resolve(captcha_url));
GURL unlock_url(url);
return GoogleServiceAuthError::FromClientLoginCaptchaChallenge(
captcha_token, image_url, unlock_url);
diff --git a/chromium/google_apis/gaia/gaia_auth_fetcher_unittest.cc b/chromium/google_apis/gaia/gaia_auth_fetcher_unittest.cc
index 74fed98f47c..f554b5ed855 100644
--- a/chromium/google_apis/gaia/gaia_auth_fetcher_unittest.cc
+++ b/chromium/google_apis/gaia/gaia_auth_fetcher_unittest.cc
@@ -27,8 +27,8 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
-using ::testing::_;
using ::testing::Invoke;
+using ::testing::_;
namespace {
static const char kGetAuthCodeValidCookie[] =
@@ -111,9 +111,9 @@ class GaiaAuthFetcherTest : public testing::Test {
oauth2_token_source_(GaiaUrls::GetInstance()->oauth2_token_url()),
token_auth_source_(GaiaUrls::GetInstance()->token_auth_url()),
merge_session_source_(GaiaUrls::GetInstance()->merge_session_url()),
- uberauth_token_source_(base::StringPrintf(
- "%s?source=&issueuberauth=1",
- GaiaUrls::GetInstance()->oauth1_login_url().c_str())),
+ uberauth_token_source_(
+ GaiaUrls::GetInstance()->oauth1_login_url().Resolve(
+ "?source=&issueuberauth=1")),
oauth_login_gurl_(GaiaUrls::GetInstance()->oauth1_login_url()) {}
void RunParsingTest(const std::string& data,
diff --git a/chromium/google_apis/gaia/gaia_constants.cc b/chromium/google_apis/gaia/gaia_constants.cc
index d1d932fb037..1b1581d1545 100644
--- a/chromium/google_apis/gaia/gaia_constants.cc
+++ b/chromium/google_apis/gaia/gaia_constants.cc
@@ -40,9 +40,6 @@ const char kChromeSyncManagedOAuth2Scope[] =
const char kGoogleTalkOAuth2Scope[] =
"https://www.googleapis.com/auth/googletalk";
-// Service for LSO endpoint of Google that exposes OAuth APIs.
-const char kLSOService[] = "lso";
-
// Used to mint uber auth tokens when needed.
const char kGaiaSid[] = "sid";
const char kGaiaLsid[] = "lsid";
diff --git a/chromium/google_apis/gaia/gaia_constants.h b/chromium/google_apis/gaia/gaia_constants.h
index 72cae741b9b..3b45eee0dcd 100644
--- a/chromium/google_apis/gaia/gaia_constants.h
+++ b/chromium/google_apis/gaia/gaia_constants.h
@@ -23,7 +23,6 @@ extern const char kAnyApiOAuth2Scope[];
extern const char kChromeSyncOAuth2Scope[];
extern const char kChromeSyncManagedOAuth2Scope[];
extern const char kGoogleTalkOAuth2Scope[];
-extern const char kLSOService[];
// Used with uber auth tokens when needed.
extern const char kGaiaSid[];
diff --git a/chromium/google_apis/gaia/gaia_switches.cc b/chromium/google_apis/gaia/gaia_switches.cc
index c2430b0b737..16228c3837b 100644
--- a/chromium/google_apis/gaia/gaia_switches.cc
+++ b/chromium/google_apis/gaia/gaia_switches.cc
@@ -8,11 +8,10 @@ namespace switches {
const char kClientLoginToOAuth2Url[] = "client-login-to-oauth2-url";
const char kGaiaUrl[] = "gaia-url";
-const char kGoogleApisHost[] = "google-apis-host";
+const char kGoogleApisUrl[] = "google-apis-url";
const char kLsoUrl[] = "lso-url";
const char kOAuth1LoginScope[] = "oauth1-login-scope";
-const char kOAuth2IssueTokenUrl[] = "oauth2-issue-token-url";
-const char kOAuth2TokenUrl[] = "oauth2-token-url";
-const char kOAuthUserInfoUrl[] = "oauth-user-info-url";
+const char kOAuthWrapBridgeUserInfoScope[] =
+ "oauth-wrap-bridge-user-info-scope";
} // namespace switches
diff --git a/chromium/google_apis/gaia/gaia_switches.h b/chromium/google_apis/gaia/gaia_switches.h
index 727a35557a9..0700f36c70c 100644
--- a/chromium/google_apis/gaia/gaia_switches.h
+++ b/chromium/google_apis/gaia/gaia_switches.h
@@ -14,29 +14,20 @@ extern const char kClientLoginToOAuth2Url[];
// "https://accounts.google.com".
extern const char kGaiaUrl[];
-// Specifies the backend server used for Google API calls. The https:// prefix
-// and the trailing slash should be omitted.
-// The default value is "www.googleapis.com".
-extern const char kGoogleApisHost[];
+// Specifies the backend server used for Google API calls.
+// The default value is "https://www.googleapis.com".
+extern const char kGoogleApisUrl[];
// Specifies the backend server used for lso authentication calls.
// "https://accounts.google.com".
extern const char kLsoUrl[];
-// TODO(zelidrag): Get rid of all following since all URLs should be
-// controlled only with --gaia-host, --lso-host and --google-apis-host.
-
// Specifies custom OAuth1 login scope for testing purposes.
extern const char kOAuth1LoginScope[];
-// Specifies custom OAuth2 issue token URL for testing purposes.
-extern const char kOAuth2IssueTokenUrl[];
-
-// Specifies custom OAuth2 token URL for testing purposes.
-extern const char kOAuth2TokenUrl[];
+// Overrides OAuth wrap bridge user info scope.
+extern const char kOAuthWrapBridgeUserInfoScope[];
-// Specifies custom OAuth user info URL for testing purposes.
-extern const char kOAuthUserInfoUrl[];
} // namespace switches
#endif // GOOGLE_APIS_GAIA_GAIA_SWITCHES_H_
diff --git a/chromium/google_apis/gaia/gaia_urls.cc b/chromium/google_apis/gaia/gaia_urls.cc
index 578205cb09b..e5e5b8f32d0 100644
--- a/chromium/google_apis/gaia/gaia_urls.cc
+++ b/chromium/google_apis/gaia/gaia_urls.cc
@@ -13,38 +13,39 @@ namespace {
// Gaia service constants
const char kDefaultGaiaUrl[] = "https://accounts.google.com";
-const char kDefaultGoogleApisBaseUrl[] = "www.googleapis.com";
-const char kCaptchaUrlPrefixSuffix[] = "/";
+const char kDefaultGoogleApisBaseUrl[] = "https://www.googleapis.com";
// API calls from accounts.google.com
-const char kClientLoginUrlSuffix[] = "/ClientLogin";
-const char kServiceLoginUrlSuffix[] = "/ServiceLogin";
-const char kServiceLogoutUrlSuffix[] = "/Logout";
-const char kIssueAuthTokenUrlSuffix[] = "/IssueAuthToken";
-const char kGetUserInfoUrlSuffix[] = "/GetUserInfo";
-const char kTokenAuthUrlSuffix[] = "/TokenAuth";
-const char kMergeSessionUrlSuffix[] = "/MergeSession";
-const char kOAuthGetAccessTokenUrlSuffix[] = "/OAuthGetAccessToken";
-const char kOAuthWrapBridgeUrlSuffix[] = "/OAuthWrapBridge";
-const char kOAuth1LoginUrlSuffix[] = "/OAuthLogin";
-const char kOAuthRevokeTokenUrlSuffix[] = "/AuthSubRevokeToken";
+const char kClientLoginUrlSuffix[] = "ClientLogin";
+const char kServiceLoginUrlSuffix[] = "ServiceLogin";
+const char kServiceLoginAuthUrlSuffix[] = "ServiceLoginAuth";
+const char kServiceLogoutUrlSuffix[] = "Logout";
+const char kIssueAuthTokenUrlSuffix[] = "IssueAuthToken";
+const char kGetUserInfoUrlSuffix[] = "GetUserInfo";
+const char kTokenAuthUrlSuffix[] = "TokenAuth";
+const char kMergeSessionUrlSuffix[] = "MergeSession";
+const char kOAuthGetAccessTokenUrlSuffix[] = "OAuthGetAccessToken";
+const char kOAuthWrapBridgeUrlSuffix[] = "OAuthWrapBridge";
+const char kOAuth1LoginUrlSuffix[] = "OAuthLogin";
+const char kOAuthRevokeTokenUrlSuffix[] = "AuthSubRevokeToken";
+
+// OAuth scopes
+const char kOAuth1LoginScope[] = "https://www.google.com/accounts/OAuthLogin";
+const char kOAuthWrapBridgeUserInfoScope[] =
+ "https://www.googleapis.com/auth/userinfo.email";
// API calls from accounts.google.com (LSO)
-const char kGetOAuthTokenUrlSuffix[] = "/o/oauth/GetOAuthToken/";
-const char kClientLoginToOAuth2UrlSuffix[] = "/o/oauth2/programmatic_auth";
-const char kOAuth2AuthUrlSuffix[] = "/o/oauth2/auth";
-const char kOAuth2RevokeUrlSuffix[] = "/o/oauth2/revoke";
-const char kOAuth2TokenUrlSuffix[] = "/o/oauth2/token";
-const char kClientOAuthUrlSuffix[] = "/ClientOAuth";
+const char kGetOAuthTokenUrlSuffix[] = "o/oauth/GetOAuthToken/";
+const char kClientLoginToOAuth2UrlSuffix[] = "o/oauth2/programmatic_auth";
+const char kOAuth2AuthUrlSuffix[] = "o/oauth2/auth";
+const char kOAuth2RevokeUrlSuffix[] = "o/oauth2/revoke";
+const char kOAuth2TokenUrlSuffix[] = "o/oauth2/token";
+const char kClientOAuthUrlSuffix[] = "ClientOAuth";
// API calls from www.googleapis.com
-const char kOAuth2IssueTokenUrlSuffix[] = "/oauth2/v2/IssueToken";
-const char kOAuth2TokenInfoUrlSuffix[] = "/oauth2/v2/tokeninfo";
-const char kOAuthUserInfoUrlSuffix[] = "/oauth2/v1/userinfo";
-const char kOAuthWrapBridgeUserInfoScopeUrlSuffix[] = "/auth/userinfo.email";
-
-const char kOAuth1LoginScope[] =
- "https://www.google.com/accounts/OAuthLogin";
+const char kOAuth2IssueTokenUrlSuffix[] = "oauth2/v2/IssueToken";
+const char kOAuth2TokenInfoUrlSuffix[] = "oauth2/v2/tokeninfo";
+const char kOAuthUserInfoUrlSuffix[] = "oauth2/v1/userinfo";
void GetSwitchValueWithDefault(const char* switch_value,
const char* default_value,
@@ -57,6 +58,16 @@ void GetSwitchValueWithDefault(const char* switch_value,
}
}
+GURL GetURLSwitchValueWithDefault(const char* switch_value,
+ const char* default_value) {
+ std::string string_value;
+ GetSwitchValueWithDefault(switch_value, default_value, &string_value);
+ const GURL result(string_value);
+ DCHECK(result.is_valid());
+ return result;
+}
+
+
} // namespace
GaiaUrls* GaiaUrls::GetInstance() {
@@ -64,27 +75,15 @@ GaiaUrls* GaiaUrls::GetInstance() {
}
GaiaUrls::GaiaUrls() {
- std::string gaia_url_str;
- GetSwitchValueWithDefault(switches::kGaiaUrl,
- kDefaultGaiaUrl,
- &gaia_url_str);
- gaia_url_ = GURL(gaia_url_str);
- DCHECK(gaia_url_.is_valid());
-
- GetSwitchValueWithDefault(switches::kLsoUrl,
- kDefaultGaiaUrl,
- &lso_origin_url_);
-
- std::string google_apis_base;
- GetSwitchValueWithDefault(switches::kGoogleApisHost,
- kDefaultGoogleApisBaseUrl,
- &google_apis_base);
-
- captcha_url_prefix_ = "http://" + gaia_url_.host() +
- (gaia_url_.has_port() ? ":" + gaia_url_.port() : "") +
- kCaptchaUrlPrefixSuffix;
+ gaia_url_ = GetURLSwitchValueWithDefault(switches::kGaiaUrl, kDefaultGaiaUrl);
+ lso_origin_url_ =
+ GetURLSwitchValueWithDefault(switches::kLsoUrl, kDefaultGaiaUrl);
+ google_apis_origin_url_ = GetURLSwitchValueWithDefault(
+ switches::kGoogleApisUrl, kDefaultGoogleApisBaseUrl);
- google_apis_origin_url_ = "https://" + google_apis_base;
+ captcha_base_url_ =
+ GURL("http://" + gaia_url_.host() +
+ (gaia_url_.has_port() ? ":" + gaia_url_.port() : ""));
oauth2_chrome_client_id_ =
google_apis::GetOAuth2ClientID(google_apis::CLIENT_MAIN);
@@ -92,115 +91,111 @@ GaiaUrls::GaiaUrls() {
google_apis::GetOAuth2ClientSecret(google_apis::CLIENT_MAIN);
// URLs from accounts.google.com.
- gaia_login_form_realm_ = gaia_url_str + "/";
- client_login_url_ = gaia_url_str + kClientLoginUrlSuffix;
- service_login_url_ = gaia_url_str + kServiceLoginUrlSuffix;
- service_logout_url_ = gaia_url_str + kServiceLogoutUrlSuffix;
- issue_auth_token_url_ = gaia_url_str + kIssueAuthTokenUrlSuffix;
- get_user_info_url_ = gaia_url_str + kGetUserInfoUrlSuffix;
- token_auth_url_ = gaia_url_str + kTokenAuthUrlSuffix;
- merge_session_url_ = gaia_url_str + kMergeSessionUrlSuffix;
- oauth_get_access_token_url_ = gaia_url_str + kOAuthGetAccessTokenUrlSuffix;
- oauth_wrap_bridge_url_ = gaia_url_str + kOAuthWrapBridgeUrlSuffix;
- oauth_revoke_token_url_ = gaia_url_str + kOAuthRevokeTokenUrlSuffix;
- oauth1_login_url_ = gaia_url_str + kOAuth1LoginUrlSuffix;
+ client_login_url_ = gaia_url_.Resolve(kClientLoginUrlSuffix);
+ service_login_url_ = gaia_url_.Resolve(kServiceLoginUrlSuffix);
+ service_login_auth_url_ = gaia_url_.Resolve(kServiceLoginAuthUrlSuffix);
+ service_logout_url_ = gaia_url_.Resolve(kServiceLogoutUrlSuffix);
+ issue_auth_token_url_ = gaia_url_.Resolve(kIssueAuthTokenUrlSuffix);
+ get_user_info_url_ = gaia_url_.Resolve(kGetUserInfoUrlSuffix);
+ token_auth_url_ = gaia_url_.Resolve(kTokenAuthUrlSuffix);
+ merge_session_url_ = gaia_url_.Resolve(kMergeSessionUrlSuffix);
+ oauth_get_access_token_url_ =
+ gaia_url_.Resolve(kOAuthGetAccessTokenUrlSuffix);
+ oauth_wrap_bridge_url_ = gaia_url_.Resolve(kOAuthWrapBridgeUrlSuffix);
+ oauth_revoke_token_url_ = gaia_url_.Resolve(kOAuthRevokeTokenUrlSuffix);
+ oauth1_login_url_ = gaia_url_.Resolve(kOAuth1LoginUrlSuffix);
// URLs from accounts.google.com (LSO).
- get_oauth_token_url_ = lso_origin_url_ + kGetOAuthTokenUrlSuffix;
- std::string client_login_to_oauth2_url = lso_origin_url_ +
- kClientLoginToOAuth2UrlSuffix;
- oauth2_auth_url_ = lso_origin_url_ + kOAuth2AuthUrlSuffix;
- std::string oauth2_token_url = lso_origin_url_ + kOAuth2TokenUrlSuffix;
- oauth2_revoke_url_ = lso_origin_url_ + kOAuth2RevokeUrlSuffix;
+ get_oauth_token_url_ = lso_origin_url_.Resolve(kGetOAuthTokenUrlSuffix);
+ client_login_to_oauth2_url_ =
+ lso_origin_url_.Resolve(kClientLoginToOAuth2UrlSuffix);
+ oauth2_auth_url_ = lso_origin_url_.Resolve(kOAuth2AuthUrlSuffix);
+ oauth2_token_url_ = lso_origin_url_.Resolve(kOAuth2TokenUrlSuffix);
+ oauth2_revoke_url_ = lso_origin_url_.Resolve(kOAuth2RevokeUrlSuffix);
// URLs from www.googleapis.com.
- oauth_wrap_bridge_user_info_scope_ = google_apis_origin_url_ +
- kOAuthWrapBridgeUserInfoScopeUrlSuffix;
- std::string oauth2_issue_token_url = google_apis_origin_url_ +
- kOAuth2IssueTokenUrlSuffix;
- oauth2_token_info_url_ = google_apis_origin_url_ + kOAuth2TokenInfoUrlSuffix;
- std::string oauth_user_info_url = google_apis_origin_url_ +
- kOAuthUserInfoUrlSuffix;
-
- // TODO(zelidrag): Get rid of all these switches since all URLs should be
- // controlled only with --gaia-url, --lso-url and --google-apis-host.
+ oauth2_issue_token_url_ =
+ google_apis_origin_url_.Resolve(kOAuth2IssueTokenUrlSuffix);
+ oauth2_token_info_url_ =
+ google_apis_origin_url_.Resolve(kOAuth2TokenInfoUrlSuffix);
+ oauth_user_info_url_ =
+ google_apis_origin_url_.Resolve(kOAuthUserInfoUrlSuffix);
+
+ gaia_login_form_realm_ = gaia_url_;
+
+ // OAuth scopes.
+ GetSwitchValueWithDefault(switches::kOAuthWrapBridgeUserInfoScope,
+ kOAuthWrapBridgeUserInfoScope,
+ &oauth_wrap_bridge_user_info_scope_);
GetSwitchValueWithDefault(switches::kOAuth1LoginScope,
kOAuth1LoginScope,
&oauth1_login_scope_);
- GetSwitchValueWithDefault(switches::kClientLoginToOAuth2Url,
- client_login_to_oauth2_url.c_str(),
- &client_login_to_oauth2_url_);
- GetSwitchValueWithDefault(switches::kOAuth2TokenUrl,
- oauth2_token_url.c_str(),
- &oauth2_token_url_);
- GetSwitchValueWithDefault(switches::kOAuth2IssueTokenUrl,
- oauth2_issue_token_url.c_str(),
- &oauth2_issue_token_url_);
- GetSwitchValueWithDefault(switches::kOAuthUserInfoUrl,
- oauth_user_info_url.c_str(),
- &oauth_user_info_url_);
}
GaiaUrls::~GaiaUrls() {
}
-const std::string& GaiaUrls::captcha_url_prefix() const {
- return captcha_url_prefix_;
-}
-
const GURL& GaiaUrls::gaia_url() const {
return gaia_url_;
}
-const std::string& GaiaUrls::client_login_url() const {
+const GURL& GaiaUrls::captcha_base_url() const {
+ return captcha_base_url_;
+}
+
+const GURL& GaiaUrls::client_login_url() const {
return client_login_url_;
}
-const std::string& GaiaUrls::service_login_url() const {
+const GURL& GaiaUrls::service_login_url() const {
return service_login_url_;
}
-const std::string& GaiaUrls::service_logout_url() const {
+const GURL& GaiaUrls::service_login_auth_url() const {
+ return service_login_auth_url_;
+}
+
+const GURL& GaiaUrls::service_logout_url() const {
return service_logout_url_;
}
-const std::string& GaiaUrls::issue_auth_token_url() const {
+const GURL& GaiaUrls::issue_auth_token_url() const {
return issue_auth_token_url_;
}
-const std::string& GaiaUrls::get_user_info_url() const {
+const GURL& GaiaUrls::get_user_info_url() const {
return get_user_info_url_;
}
-const std::string& GaiaUrls::token_auth_url() const {
+const GURL& GaiaUrls::token_auth_url() const {
return token_auth_url_;
}
-const std::string& GaiaUrls::merge_session_url() const {
+const GURL& GaiaUrls::merge_session_url() const {
return merge_session_url_;
}
-const std::string& GaiaUrls::get_oauth_token_url() const {
+const GURL& GaiaUrls::get_oauth_token_url() const {
return get_oauth_token_url_;
}
-const std::string& GaiaUrls::oauth_get_access_token_url() const {
+const GURL& GaiaUrls::oauth_get_access_token_url() const {
return oauth_get_access_token_url_;
}
-const std::string& GaiaUrls::oauth_wrap_bridge_url() const {
+const GURL& GaiaUrls::oauth_wrap_bridge_url() const {
return oauth_wrap_bridge_url_;
}
-const std::string& GaiaUrls::oauth_user_info_url() const {
+const GURL& GaiaUrls::oauth_user_info_url() const {
return oauth_user_info_url_;
}
-const std::string& GaiaUrls::oauth_revoke_token_url() const {
+const GURL& GaiaUrls::oauth_revoke_token_url() const {
return oauth_revoke_token_url_;
}
-const std::string& GaiaUrls::oauth1_login_url() const {
+const GURL& GaiaUrls::oauth1_login_url() const {
return oauth1_login_url_;
}
@@ -220,30 +215,30 @@ const std::string& GaiaUrls::oauth2_chrome_client_secret() const {
return oauth2_chrome_client_secret_;
}
-const std::string& GaiaUrls::client_login_to_oauth2_url() const {
+const GURL& GaiaUrls::client_login_to_oauth2_url() const {
return client_login_to_oauth2_url_;
}
-const std::string& GaiaUrls::oauth2_auth_url() const {
+const GURL& GaiaUrls::oauth2_auth_url() const {
return oauth2_auth_url_;
}
-const std::string& GaiaUrls::oauth2_token_url() const {
+const GURL& GaiaUrls::oauth2_token_url() const {
return oauth2_token_url_;
}
-const std::string& GaiaUrls::oauth2_issue_token_url() const {
+const GURL& GaiaUrls::oauth2_issue_token_url() const {
return oauth2_issue_token_url_;
}
-const std::string& GaiaUrls::oauth2_token_info_url() const {
+const GURL& GaiaUrls::oauth2_token_info_url() const {
return oauth2_token_info_url_;
}
-const std::string& GaiaUrls::oauth2_revoke_url() const {
+const GURL& GaiaUrls::oauth2_revoke_url() const {
return oauth2_revoke_url_;
}
-const std::string& GaiaUrls::gaia_login_form_realm() const {
- return gaia_login_form_realm_;
+const GURL& GaiaUrls::gaia_login_form_realm() const {
+ return gaia_url_;
}
diff --git a/chromium/google_apis/gaia/gaia_urls.h b/chromium/google_apis/gaia/gaia_urls.h
index 28c72792f0b..e06b95d11e1 100644
--- a/chromium/google_apis/gaia/gaia_urls.h
+++ b/chromium/google_apis/gaia/gaia_urls.h
@@ -16,36 +16,36 @@ class GaiaUrls {
static GaiaUrls* GetInstance();
// The URLs for different calls in the Google Accounts programmatic login API.
- const std::string& captcha_url_prefix() const;
-
const GURL& gaia_url() const;
- const std::string& client_login_url() const;
- const std::string& service_login_url() const;
- const std::string& service_logout_url() const;
- const std::string& issue_auth_token_url() const;
- const std::string& get_user_info_url() const;
- const std::string& token_auth_url() const;
- const std::string& merge_session_url() const;
- const std::string& get_oauth_token_url() const;
- const std::string& oauth_get_access_token_url() const;
- const std::string& oauth_wrap_bridge_url() const;
- const std::string& oauth_user_info_url() const;
- const std::string& oauth_revoke_token_url() const;
- const std::string& oauth1_login_url() const;
+ const GURL& captcha_base_url() const;
+ const GURL& client_login_url() const;
+ const GURL& service_login_url() const;
+ const GURL& service_login_auth_url() const;
+ const GURL& service_logout_url() const;
+ const GURL& issue_auth_token_url() const;
+ const GURL& get_user_info_url() const;
+ const GURL& token_auth_url() const;
+ const GURL& merge_session_url() const;
+ const GURL& get_oauth_token_url() const;
+ const GURL& oauth_get_access_token_url() const;
+ const GURL& oauth_wrap_bridge_url() const;
+ const GURL& oauth_user_info_url() const;
+ const GURL& oauth_revoke_token_url() const;
+ const GURL& oauth1_login_url() const;
const std::string& oauth1_login_scope() const;
const std::string& oauth_wrap_bridge_user_info_scope() const;
const std::string& oauth2_chrome_client_id() const;
const std::string& oauth2_chrome_client_secret() const;
- const std::string& client_login_to_oauth2_url() const;
- const std::string& oauth2_auth_url() const;
- const std::string& oauth2_token_url() const;
- const std::string& oauth2_issue_token_url() const;
- const std::string& oauth2_token_info_url() const;
- const std::string& oauth2_revoke_url() const;
+ const GURL& client_login_to_oauth2_url() const;
+ const GURL& oauth2_auth_url() const;
+ const GURL& oauth2_token_url() const;
+ const GURL& oauth2_issue_token_url() const;
+ const GURL& oauth2_token_info_url() const;
+ const GURL& oauth2_revoke_url() const;
- const std::string& gaia_login_form_realm() const;
+ const GURL& gaia_login_form_realm() const;
private:
GaiaUrls();
@@ -53,38 +53,41 @@ class GaiaUrls {
friend struct DefaultSingletonTraits<GaiaUrls>;
- std::string captcha_url_prefix_;
-
GURL gaia_url_;
- std::string lso_origin_url_;
- std::string google_apis_origin_url_;
- std::string client_login_url_;
- std::string service_login_url_;
- std::string service_logout_url_;
- std::string issue_auth_token_url_;
- std::string get_user_info_url_;
- std::string token_auth_url_;
- std::string merge_session_url_;
- std::string get_oauth_token_url_;
- std::string oauth_get_access_token_url_;
- std::string oauth_wrap_bridge_url_;
- std::string oauth_user_info_url_;
- std::string oauth_revoke_token_url_;
- std::string oauth1_login_url_;
+ GURL captcha_base_url_;
+
+ GURL lso_origin_url_;
+ GURL google_apis_origin_url_;
+
+ GURL client_login_url_;
+ GURL service_login_url_;
+ GURL service_login_auth_url_;
+ GURL service_logout_url_;
+ GURL issue_auth_token_url_;
+ GURL get_user_info_url_;
+ GURL token_auth_url_;
+ GURL merge_session_url_;
+ GURL get_oauth_token_url_;
+ GURL oauth_get_access_token_url_;
+ GURL oauth_wrap_bridge_url_;
+ GURL oauth_user_info_url_;
+ GURL oauth_revoke_token_url_;
+ GURL oauth1_login_url_;
std::string oauth1_login_scope_;
std::string oauth_wrap_bridge_user_info_scope_;
std::string oauth2_chrome_client_id_;
std::string oauth2_chrome_client_secret_;
- std::string client_login_to_oauth2_url_;
- std::string oauth2_auth_url_;
- std::string oauth2_token_url_;
- std::string oauth2_issue_token_url_;
- std::string oauth2_token_info_url_;
- std::string oauth2_revoke_url_;
-
- std::string gaia_login_form_realm_;
+
+ GURL client_login_to_oauth2_url_;
+ GURL oauth2_auth_url_;
+ GURL oauth2_token_url_;
+ GURL oauth2_issue_token_url_;
+ GURL oauth2_token_info_url_;
+ GURL oauth2_revoke_url_;
+
+ GURL gaia_login_form_realm_;
DISALLOW_COPY_AND_ASSIGN(GaiaUrls);
};
diff --git a/chromium/google_apis/gaia/google_service_auth_error.h b/chromium/google_apis/gaia/google_service_auth_error.h
index a99e515f31d..19fdb515405 100644
--- a/chromium/google_apis/gaia/google_service_auth_error.h
+++ b/chromium/google_apis/gaia/google_service_auth_error.h
@@ -33,8 +33,8 @@ class DictionaryValue;
class GoogleServiceAuthError {
public:
//
- // These enumerations are referenced by integer value in HTML login code.
- // Do not change the numeric values.
+ // These enumerations are referenced by integer value in HTML login code and
+ // in UMA histograms. Do not change the numeric values.
//
enum State {
// The user is authenticated.
diff --git a/chromium/google_apis/gaia/oauth2_access_token_fetcher.cc b/chromium/google_apis/gaia/oauth2_access_token_fetcher.cc
index 456251f0134..44f2d4a7795 100644
--- a/chromium/google_apis/gaia/oauth2_access_token_fetcher.cc
+++ b/chromium/google_apis/gaia/oauth2_access_token_fetcher.cc
@@ -182,7 +182,7 @@ void OAuth2AccessTokenFetcher::OnURLFetchComplete(
// static
GURL OAuth2AccessTokenFetcher::MakeGetAccessTokenUrl() {
- return GURL(GaiaUrls::GetInstance()->oauth2_token_url());
+ return GaiaUrls::GetInstance()->oauth2_token_url();
}
// static
diff --git a/chromium/google_apis/gaia/oauth2_mint_token_flow.cc b/chromium/google_apis/gaia/oauth2_mint_token_flow.cc
index f66e0fb11d2..b705ab872b0 100644
--- a/chromium/google_apis/gaia/oauth2_mint_token_flow.cc
+++ b/chromium/google_apis/gaia/oauth2_mint_token_flow.cc
@@ -151,7 +151,7 @@ void OAuth2MintTokenFlow::ReportFailure(
}
GURL OAuth2MintTokenFlow::CreateApiCallUrl() {
- return GURL(GaiaUrls::GetInstance()->oauth2_issue_token_url());
+ return GaiaUrls::GetInstance()->oauth2_issue_token_url();
}
std::string OAuth2MintTokenFlow::CreateApiCallBody() {
diff --git a/chromium/google_apis/gaia/oauth2_token_service.cc b/chromium/google_apis/gaia/oauth2_token_service.cc
new file mode 100644
index 00000000000..3259e283b64
--- /dev/null
+++ b/chromium/google_apis/gaia/oauth2_token_service.cc
@@ -0,0 +1,737 @@
+// 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 "google_apis/gaia/oauth2_token_service.h"
+
+#include <vector>
+
+#include "base/bind.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/rand_util.h"
+#include "base/stl_util.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "google_apis/gaia/gaia_urls.h"
+#include "google_apis/gaia/google_service_auth_error.h"
+#include "net/url_request/url_request_context_getter.h"
+
+int OAuth2TokenService::max_fetch_retry_num_ = 5;
+
+OAuth2TokenService::RequestParameters::RequestParameters(
+ const std::string& client_id,
+ const std::string& account_id,
+ const ScopeSet& scopes)
+ : client_id(client_id),
+ account_id(account_id),
+ scopes(scopes) {
+}
+
+OAuth2TokenService::RequestParameters::~RequestParameters() {
+}
+
+bool OAuth2TokenService::RequestParameters::operator<(
+ const RequestParameters& p) const {
+ if (client_id < p.client_id)
+ return true;
+ else if (p.client_id < client_id)
+ return false;
+
+ if (account_id < p.account_id)
+ return true;
+ else if (p.account_id < account_id)
+ return false;
+
+ return scopes < p.scopes;
+}
+
+OAuth2TokenService::RequestImpl::RequestImpl(
+ OAuth2TokenService::Consumer* consumer)
+ : consumer_(consumer) {
+}
+
+OAuth2TokenService::RequestImpl::~RequestImpl() {
+ DCHECK(CalledOnValidThread());
+}
+
+void OAuth2TokenService::RequestImpl::InformConsumer(
+ const GoogleServiceAuthError& error,
+ const std::string& access_token,
+ const base::Time& expiration_date) {
+ DCHECK(CalledOnValidThread());
+ if (error.state() == GoogleServiceAuthError::NONE)
+ consumer_->OnGetTokenSuccess(this, access_token, expiration_date);
+ else
+ consumer_->OnGetTokenFailure(this, error);
+}
+
+// Class that fetches an OAuth2 access token for a given set of scopes and
+// OAuth2 refresh token.
+
+// Class that fetches OAuth2 access tokens for given scopes and refresh token.
+//
+// It aims to meet OAuth2TokenService's requirements on token fetching. Retry
+// mechanism is used to handle failures.
+//
+// To use this class, call CreateAndStart() to create and start a Fetcher.
+//
+// The Fetcher will call back the service by calling
+// OAuth2TokenService::OnFetchComplete() when it completes fetching, if it is
+// not destructed before it completes fetching; if the Fetcher is destructed
+// before it completes fetching, the service will never be called back. The
+// Fetcher destructs itself after calling back the service when finishes
+// fetching.
+//
+// Requests that are waiting for the fetching results of this Fetcher can be
+// added to the Fetcher by calling
+// OAuth2TokenService::Fetcher::AddWaitingRequest() before the Fetcher
+// completes fetching.
+//
+// The waiting requests are taken as weak pointers and they can be deleted.
+// The waiting requests will be called back with fetching results if they are
+// not deleted
+// - when the Fetcher completes fetching, if the Fetcher is not destructed
+// before it completes fetching, or
+// - when the Fetcher is destructed if the Fetcher is destructed before it
+// completes fetching (in this case, the waiting requests will be called
+// back with error).
+class OAuth2TokenService::Fetcher : public OAuth2AccessTokenConsumer {
+ public:
+ // Creates a Fetcher and starts fetching an OAuth2 access token for
+ // |refresh_token| and |scopes| in the request context obtained by |getter|.
+ // The given |oauth2_token_service| will be informed when fetching is done.
+ static Fetcher* CreateAndStart(OAuth2TokenService* oauth2_token_service,
+ const std::string& account_id,
+ net::URLRequestContextGetter* getter,
+ const std::string& client_id,
+ const std::string& client_secret,
+ const std::string& refresh_token,
+ const ScopeSet& scopes,
+ base::WeakPtr<RequestImpl> waiting_request);
+ virtual ~Fetcher();
+
+ // Add a request that is waiting for the result of this Fetcher.
+ void AddWaitingRequest(base::WeakPtr<RequestImpl> waiting_request);
+
+ // Returns count of waiting requests.
+ size_t GetWaitingRequestCount() const;
+
+ void Cancel();
+
+ const ScopeSet& GetScopeSet() const;
+ const std::string& GetRefreshToken() const;
+ const std::string& GetClientId() const;
+ const std::string& GetAccountId() const;
+
+ // The error result from this fetcher.
+ const GoogleServiceAuthError& error() const { return error_; }
+
+ protected:
+ // OAuth2AccessTokenConsumer
+ virtual void OnGetTokenSuccess(const std::string& access_token,
+ const base::Time& expiration_date) OVERRIDE;
+ virtual void OnGetTokenFailure(
+ const GoogleServiceAuthError& error) OVERRIDE;
+
+ private:
+ Fetcher(OAuth2TokenService* oauth2_token_service,
+ const std::string& account_id,
+ net::URLRequestContextGetter* getter,
+ const std::string& client_id,
+ const std::string& client_secret,
+ const std::string& refresh_token,
+ const OAuth2TokenService::ScopeSet& scopes,
+ base::WeakPtr<RequestImpl> waiting_request);
+ void Start();
+ void InformWaitingRequests();
+ void InformWaitingRequestsAndDelete();
+ static bool ShouldRetry(const GoogleServiceAuthError& error);
+ int64 ComputeExponentialBackOffMilliseconds(int retry_num);
+
+ // |oauth2_token_service_| remains valid for the life of this Fetcher, since
+ // this Fetcher is destructed in the dtor of the OAuth2TokenService or is
+ // scheduled for deletion at the end of OnGetTokenFailure/OnGetTokenSuccess
+ // (whichever comes first).
+ OAuth2TokenService* const oauth2_token_service_;
+ scoped_refptr<net::URLRequestContextGetter> getter_;
+ const std::string account_id_;
+ const std::string refresh_token_;
+ const ScopeSet scopes_;
+ std::vector<base::WeakPtr<RequestImpl> > waiting_requests_;
+
+ int retry_number_;
+ base::OneShotTimer<Fetcher> retry_timer_;
+ scoped_ptr<OAuth2AccessTokenFetcher> fetcher_;
+
+ // Variables that store fetch results.
+ // Initialized to be GoogleServiceAuthError::SERVICE_UNAVAILABLE to handle
+ // destruction.
+ GoogleServiceAuthError error_;
+ std::string access_token_;
+ base::Time expiration_date_;
+
+ // OAuth2 client id and secret.
+ std::string client_id_;
+ std::string client_secret_;
+
+ DISALLOW_COPY_AND_ASSIGN(Fetcher);
+};
+
+// static
+OAuth2TokenService::Fetcher* OAuth2TokenService::Fetcher::CreateAndStart(
+ OAuth2TokenService* oauth2_token_service,
+ const std::string& account_id,
+ net::URLRequestContextGetter* getter,
+ const std::string& client_id,
+ const std::string& client_secret,
+ const std::string& refresh_token,
+ const OAuth2TokenService::ScopeSet& scopes,
+ base::WeakPtr<RequestImpl> waiting_request) {
+ OAuth2TokenService::Fetcher* fetcher = new Fetcher(
+ oauth2_token_service,
+ account_id,
+ getter,
+ client_id,
+ client_secret,
+ refresh_token,
+ scopes,
+ waiting_request);
+ fetcher->Start();
+ return fetcher;
+}
+
+OAuth2TokenService::Fetcher::Fetcher(
+ OAuth2TokenService* oauth2_token_service,
+ const std::string& account_id,
+ net::URLRequestContextGetter* getter,
+ const std::string& client_id,
+ const std::string& client_secret,
+ const std::string& refresh_token,
+ const OAuth2TokenService::ScopeSet& scopes,
+ base::WeakPtr<RequestImpl> waiting_request)
+ : oauth2_token_service_(oauth2_token_service),
+ getter_(getter),
+ account_id_(account_id),
+ refresh_token_(refresh_token),
+ scopes_(scopes),
+ retry_number_(0),
+ error_(GoogleServiceAuthError::SERVICE_UNAVAILABLE),
+ client_id_(client_id),
+ client_secret_(client_secret) {
+ DCHECK(oauth2_token_service_);
+ DCHECK(getter_.get());
+ DCHECK(refresh_token_.length());
+ waiting_requests_.push_back(waiting_request);
+}
+
+OAuth2TokenService::Fetcher::~Fetcher() {
+ // Inform the waiting requests if it has not done so.
+ if (waiting_requests_.size())
+ InformWaitingRequests();
+}
+
+void OAuth2TokenService::Fetcher::Start() {
+ fetcher_.reset(new OAuth2AccessTokenFetcher(this, getter_.get()));
+ fetcher_->Start(client_id_,
+ client_secret_,
+ refresh_token_,
+ std::vector<std::string>(scopes_.begin(), scopes_.end()));
+ retry_timer_.Stop();
+}
+
+void OAuth2TokenService::Fetcher::OnGetTokenSuccess(
+ const std::string& access_token,
+ const base::Time& expiration_date) {
+ fetcher_.reset();
+
+ // Fetch completes.
+ error_ = GoogleServiceAuthError::AuthErrorNone();
+ access_token_ = access_token;
+ expiration_date_ = expiration_date;
+
+ // Subclasses may override this method to skip caching in some cases, but
+ // we still inform all waiting Consumers of a successful token fetch below.
+ // This is intentional -- some consumers may need the token for cleanup
+ // tasks. https://chromiumcodereview.appspot.com/11312124/
+ oauth2_token_service_->RegisterCacheEntry(client_id_,
+ account_id_,
+ scopes_,
+ access_token_,
+ expiration_date_);
+ InformWaitingRequestsAndDelete();
+}
+
+void OAuth2TokenService::Fetcher::OnGetTokenFailure(
+ const GoogleServiceAuthError& error) {
+ fetcher_.reset();
+
+ if (ShouldRetry(error) && retry_number_ < max_fetch_retry_num_) {
+ int64 backoff = ComputeExponentialBackOffMilliseconds(retry_number_);
+ ++retry_number_;
+ retry_timer_.Stop();
+ retry_timer_.Start(FROM_HERE,
+ base::TimeDelta::FromMilliseconds(backoff),
+ this,
+ &OAuth2TokenService::Fetcher::Start);
+ return;
+ }
+
+ error_ = error;
+ InformWaitingRequestsAndDelete();
+}
+
+// Returns an exponential backoff in milliseconds including randomness less than
+// 1000 ms when retrying fetching an OAuth2 access token.
+int64 OAuth2TokenService::Fetcher::ComputeExponentialBackOffMilliseconds(
+ int retry_num) {
+ DCHECK(retry_num < max_fetch_retry_num_);
+ int64 exponential_backoff_in_seconds = 1 << retry_num;
+ // Returns a backoff with randomness < 1000ms
+ return (exponential_backoff_in_seconds + base::RandDouble()) * 1000;
+}
+
+// static
+bool OAuth2TokenService::Fetcher::ShouldRetry(
+ const GoogleServiceAuthError& error) {
+ GoogleServiceAuthError::State error_state = error.state();
+ return error_state == GoogleServiceAuthError::CONNECTION_FAILED ||
+ error_state == GoogleServiceAuthError::REQUEST_CANCELED ||
+ error_state == GoogleServiceAuthError::SERVICE_UNAVAILABLE;
+}
+
+void OAuth2TokenService::Fetcher::InformWaitingRequests() {
+ std::vector<base::WeakPtr<RequestImpl> >::const_iterator iter =
+ waiting_requests_.begin();
+ for (; iter != waiting_requests_.end(); ++iter) {
+ base::WeakPtr<RequestImpl> waiting_request = *iter;
+ if (waiting_request.get())
+ waiting_request->InformConsumer(error_, access_token_, expiration_date_);
+ }
+ waiting_requests_.clear();
+}
+
+void OAuth2TokenService::Fetcher::InformWaitingRequestsAndDelete() {
+ // Deregisters itself from the service to prevent more waiting requests to
+ // be added when it calls back the waiting requests.
+ oauth2_token_service_->OnFetchComplete(this);
+ InformWaitingRequests();
+ base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
+}
+
+void OAuth2TokenService::Fetcher::AddWaitingRequest(
+ base::WeakPtr<OAuth2TokenService::RequestImpl> waiting_request) {
+ waiting_requests_.push_back(waiting_request);
+}
+
+size_t OAuth2TokenService::Fetcher::GetWaitingRequestCount() const {
+ return waiting_requests_.size();
+}
+
+void OAuth2TokenService::Fetcher::Cancel() {
+ fetcher_.reset();
+ retry_timer_.Stop();
+ error_ = GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED);
+ InformWaitingRequestsAndDelete();
+}
+
+const OAuth2TokenService::ScopeSet& OAuth2TokenService::Fetcher::GetScopeSet()
+ const {
+ return scopes_;
+}
+
+const std::string& OAuth2TokenService::Fetcher::GetRefreshToken() const {
+ return refresh_token_;
+}
+
+const std::string& OAuth2TokenService::Fetcher::GetClientId() const {
+ return client_id_;
+}
+
+const std::string& OAuth2TokenService::Fetcher::GetAccountId() const {
+ return account_id_;
+}
+
+OAuth2TokenService::Request::Request() {
+}
+
+OAuth2TokenService::Request::~Request() {
+}
+
+OAuth2TokenService::Consumer::Consumer() {
+}
+
+OAuth2TokenService::Consumer::~Consumer() {
+}
+
+OAuth2TokenService::OAuth2TokenService() {
+}
+
+OAuth2TokenService::~OAuth2TokenService() {
+ // Release all the pending fetchers.
+ STLDeleteContainerPairSecondPointers(
+ pending_fetchers_.begin(), pending_fetchers_.end());
+}
+
+void OAuth2TokenService::AddObserver(Observer* observer) {
+ observer_list_.AddObserver(observer);
+}
+
+void OAuth2TokenService::RemoveObserver(Observer* observer) {
+ observer_list_.RemoveObserver(observer);
+}
+
+bool OAuth2TokenService::RefreshTokenIsAvailable(
+ const std::string& account_id) {
+ DCHECK(CalledOnValidThread());
+ return !GetRefreshToken(account_id).empty();
+}
+
+std::vector<std::string> OAuth2TokenService::GetAccounts() {
+ return std::vector<std::string>();
+}
+
+scoped_ptr<OAuth2TokenService::Request> OAuth2TokenService::StartRequest(
+ const std::string& account_id,
+ const OAuth2TokenService::ScopeSet& scopes,
+ OAuth2TokenService::Consumer* consumer) {
+ return StartRequestForClientWithContext(
+ account_id,
+ GetRequestContext(),
+ GaiaUrls::GetInstance()->oauth2_chrome_client_id(),
+ GaiaUrls::GetInstance()->oauth2_chrome_client_secret(),
+ scopes,
+ consumer);
+}
+
+scoped_ptr<OAuth2TokenService::Request>
+OAuth2TokenService::StartRequestForClient(
+ const std::string& account_id,
+ const std::string& client_id,
+ const std::string& client_secret,
+ const OAuth2TokenService::ScopeSet& scopes,
+ OAuth2TokenService::Consumer* consumer) {
+ return StartRequestForClientWithContext(
+ account_id,
+ GetRequestContext(),
+ client_id,
+ client_secret,
+ scopes,
+ consumer);
+}
+
+scoped_ptr<OAuth2TokenService::Request>
+OAuth2TokenService::StartRequestWithContext(
+ const std::string& account_id,
+ net::URLRequestContextGetter* getter,
+ const ScopeSet& scopes,
+ Consumer* consumer) {
+ return StartRequestForClientWithContext(
+ account_id,
+ getter,
+ GaiaUrls::GetInstance()->oauth2_chrome_client_id(),
+ GaiaUrls::GetInstance()->oauth2_chrome_client_secret(),
+ scopes,
+ consumer);
+}
+
+scoped_ptr<OAuth2TokenService::Request>
+OAuth2TokenService::StartRequestForClientWithContext(
+ const std::string& account_id,
+ net::URLRequestContextGetter* getter,
+ const std::string& client_id,
+ const std::string& client_secret,
+ const ScopeSet& scopes,
+ Consumer* consumer) {
+ DCHECK(CalledOnValidThread());
+
+ scoped_ptr<RequestImpl> request(new RequestImpl(consumer));
+
+ if (!RefreshTokenIsAvailable(account_id)) {
+ base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
+ &RequestImpl::InformConsumer,
+ request->AsWeakPtr(),
+ GoogleServiceAuthError(GoogleServiceAuthError::USER_NOT_SIGNED_UP),
+ std::string(),
+ base::Time()));
+ return request.PassAs<Request>();
+ }
+
+ RequestParameters request_parameters(client_id,
+ account_id,
+ scopes);
+ if (HasCacheEntry(request_parameters)) {
+ StartCacheLookupRequest(request.get(), request_parameters, consumer);
+ } else {
+ FetchOAuth2Token(request.get(),
+ account_id,
+ getter,
+ client_id,
+ client_secret,
+ scopes);
+ }
+ return request.PassAs<Request>();
+}
+
+void OAuth2TokenService::FetchOAuth2Token(RequestImpl* request,
+ const std::string& account_id,
+ net::URLRequestContextGetter* getter,
+ const std::string& client_id,
+ const std::string& client_secret,
+ const ScopeSet& scopes) {
+ std::string refresh_token = GetRefreshToken(account_id);
+
+ // If there is already a pending fetcher for |scopes| and |account_id|,
+ // simply register this |request| for those results rather than starting
+ // a new fetcher.
+ RequestParameters request_parameters = RequestParameters(client_id,
+ account_id,
+ scopes);
+ std::map<RequestParameters, Fetcher*>::iterator iter =
+ pending_fetchers_.find(request_parameters);
+ if (iter != pending_fetchers_.end()) {
+ iter->second->AddWaitingRequest(request->AsWeakPtr());
+ return;
+ }
+
+ pending_fetchers_[request_parameters] =
+ Fetcher::CreateAndStart(this,
+ account_id,
+ getter,
+ client_id,
+ client_secret,
+ refresh_token,
+ scopes,
+ request->AsWeakPtr());
+}
+
+void OAuth2TokenService::StartCacheLookupRequest(
+ RequestImpl* request,
+ const OAuth2TokenService::RequestParameters& request_parameters,
+ OAuth2TokenService::Consumer* consumer) {
+ CHECK(HasCacheEntry(request_parameters));
+ const CacheEntry* cache_entry = GetCacheEntry(request_parameters);
+ base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
+ &RequestImpl::InformConsumer,
+ request->AsWeakPtr(),
+ GoogleServiceAuthError(GoogleServiceAuthError::NONE),
+ cache_entry->access_token,
+ cache_entry->expiration_date));
+}
+
+void OAuth2TokenService::InvalidateToken(const std::string& account_id,
+ const ScopeSet& scopes,
+ const std::string& access_token) {
+ InvalidateOAuth2Token(account_id,
+ GaiaUrls::GetInstance()->oauth2_chrome_client_id(),
+ scopes,
+ access_token);
+}
+
+void OAuth2TokenService::InvalidateTokenForClient(
+ const std::string& account_id,
+ const std::string& client_id,
+ const ScopeSet& scopes,
+ const std::string& access_token) {
+ InvalidateOAuth2Token(account_id, client_id, scopes, access_token);
+}
+
+void OAuth2TokenService::InvalidateOAuth2Token(
+ const std::string& account_id,
+ const std::string& client_id,
+ const ScopeSet& scopes,
+ const std::string& access_token) {
+ DCHECK(CalledOnValidThread());
+ RemoveCacheEntry(
+ RequestParameters(client_id,
+ account_id,
+ scopes),
+ access_token);
+}
+
+void OAuth2TokenService::OnFetchComplete(Fetcher* fetcher) {
+ DCHECK(CalledOnValidThread());
+
+ // Update the auth error state so auth errors are appropriately communicated
+ // to the user.
+ UpdateAuthError(fetcher->GetAccountId(), fetcher->error());
+
+ // Note |fetcher| is recorded in |pending_fetcher_| mapped to its refresh
+ // token and scope set. This is guaranteed as follows; here a Fetcher is said
+ // to be uncompleted if it has not finished calling back
+ // OAuth2TokenService::OnFetchComplete().
+ //
+ // (1) All the live Fetchers are created by this service.
+ // This is because (1) all the live Fetchers are created by a live
+ // service, as all the fetchers created by a service are destructed in the
+ // service's dtor.
+ //
+ // (2) All the uncompleted Fetchers created by this service are recorded in
+ // |pending_fetchers_|.
+ // This is because (1) all the created Fetchers are added to
+ // |pending_fetchers_| (in method StartRequest()) and (2) method
+ // OnFetchComplete() is the only place where a Fetcher is erased from
+ // |pending_fetchers_|. Note no Fetcher is erased in method
+ // StartRequest().
+ //
+ // (3) Each of the Fetchers recorded in |pending_fetchers_| is mapped to its
+ // refresh token and ScopeSet. This is guaranteed by Fetcher creation in
+ // method StartRequest().
+ //
+ // When this method is called, |fetcher| is alive and uncompleted.
+ // By (1), |fetcher| is created by this service.
+ // Then by (2), |fetcher| is recorded in |pending_fetchers_|.
+ // Then by (3), |fetcher_| is mapped to its refresh token and ScopeSet.
+ std::map<RequestParameters, Fetcher*>::iterator iter =
+ pending_fetchers_.find(RequestParameters(
+ fetcher->GetClientId(),
+ fetcher->GetAccountId(),
+ fetcher->GetScopeSet()));
+ DCHECK(iter != pending_fetchers_.end());
+ DCHECK_EQ(fetcher, iter->second);
+ pending_fetchers_.erase(iter);
+}
+
+bool OAuth2TokenService::HasCacheEntry(
+ const RequestParameters& request_parameters) {
+ const CacheEntry* cache_entry = GetCacheEntry(request_parameters);
+ return cache_entry && cache_entry->access_token.length();
+}
+
+const OAuth2TokenService::CacheEntry* OAuth2TokenService::GetCacheEntry(
+ const RequestParameters& request_parameters) {
+ DCHECK(CalledOnValidThread());
+ TokenCache::iterator token_iterator = token_cache_.find(request_parameters);
+ if (token_iterator == token_cache_.end())
+ return NULL;
+ if (token_iterator->second.expiration_date <= base::Time::Now()) {
+ token_cache_.erase(token_iterator);
+ return NULL;
+ }
+ return &token_iterator->second;
+}
+
+bool OAuth2TokenService::RemoveCacheEntry(
+ const RequestParameters& request_parameters,
+ const std::string& token_to_remove) {
+ DCHECK(CalledOnValidThread());
+ TokenCache::iterator token_iterator = token_cache_.find(request_parameters);
+ if (token_iterator != token_cache_.end() &&
+ token_iterator->second.access_token == token_to_remove) {
+ token_cache_.erase(token_iterator);
+ return true;
+ }
+ return false;
+}
+
+void OAuth2TokenService::RegisterCacheEntry(
+ const std::string& client_id,
+ const std::string& account_id,
+ const OAuth2TokenService::ScopeSet& scopes,
+ const std::string& access_token,
+ const base::Time& expiration_date) {
+ DCHECK(CalledOnValidThread());
+
+ CacheEntry& token = token_cache_[RequestParameters(client_id,
+ account_id,
+ scopes)];
+ token.access_token = access_token;
+ token.expiration_date = expiration_date;
+}
+
+void OAuth2TokenService::UpdateAuthError(
+ const std::string& account_id,
+ const GoogleServiceAuthError& error) {
+ // Default implementation does nothing.
+}
+
+void OAuth2TokenService::ClearCache() {
+ DCHECK(CalledOnValidThread());
+ token_cache_.clear();
+}
+
+void OAuth2TokenService::ClearCacheForAccount(const std::string& account_id) {
+ DCHECK(CalledOnValidThread());
+ for (TokenCache::iterator iter = token_cache_.begin();
+ iter != token_cache_.end();
+ /* iter incremented in body */) {
+ if (iter->first.account_id == account_id) {
+ token_cache_.erase(iter++);
+ } else {
+ ++iter;
+ }
+ }
+}
+
+void OAuth2TokenService::CancelAllRequests() {
+ std::vector<Fetcher*> fetchers_to_cancel;
+ for (std::map<RequestParameters, Fetcher*>::iterator iter =
+ pending_fetchers_.begin();
+ iter != pending_fetchers_.end();
+ ++iter) {
+ fetchers_to_cancel.push_back(iter->second);
+ }
+ CancelFetchers(fetchers_to_cancel);
+}
+
+void OAuth2TokenService::CancelRequestsForAccount(
+ const std::string& account_id) {
+ std::vector<Fetcher*> fetchers_to_cancel;
+ for (std::map<RequestParameters, Fetcher*>::iterator iter =
+ pending_fetchers_.begin();
+ iter != pending_fetchers_.end();
+ ++iter) {
+ if (iter->first.account_id == account_id)
+ fetchers_to_cancel.push_back(iter->second);
+ }
+ CancelFetchers(fetchers_to_cancel);
+}
+
+void OAuth2TokenService::CancelFetchers(
+ std::vector<Fetcher*> fetchers_to_cancel) {
+ for (std::vector<OAuth2TokenService::Fetcher*>::iterator iter =
+ fetchers_to_cancel.begin();
+ iter != fetchers_to_cancel.end();
+ ++iter) {
+ (*iter)->Cancel();
+ }
+}
+
+void OAuth2TokenService::FireRefreshTokenAvailable(
+ const std::string& account_id) {
+ FOR_EACH_OBSERVER(Observer, observer_list_,
+ OnRefreshTokenAvailable(account_id));
+}
+
+void OAuth2TokenService::FireRefreshTokenRevoked(
+ const std::string& account_id) {
+ FOR_EACH_OBSERVER(Observer, observer_list_,
+ OnRefreshTokenRevoked(account_id));
+}
+
+void OAuth2TokenService::FireRefreshTokensLoaded() {
+ FOR_EACH_OBSERVER(Observer, observer_list_, OnRefreshTokensLoaded());
+}
+
+int OAuth2TokenService::cache_size_for_testing() const {
+ return token_cache_.size();
+}
+
+void OAuth2TokenService::set_max_authorization_token_fetch_retries_for_testing(
+ int max_retries) {
+ DCHECK(CalledOnValidThread());
+ max_fetch_retry_num_ = max_retries;
+}
+
+size_t OAuth2TokenService::GetNumPendingRequestsForTesting(
+ const std::string& client_id,
+ const std::string& account_id,
+ const ScopeSet& scopes) const {
+ PendingFetcherMap::const_iterator iter = pending_fetchers_.find(
+ OAuth2TokenService::RequestParameters(
+ client_id,
+ account_id,
+ scopes));
+ return iter == pending_fetchers_.end() ?
+ 0 : iter->second->GetWaitingRequestCount();
+}
diff --git a/chromium/google_apis/gaia/oauth2_token_service.h b/chromium/google_apis/gaia/oauth2_token_service.h
new file mode 100644
index 00000000000..d092430f9a5
--- /dev/null
+++ b/chromium/google_apis/gaia/oauth2_token_service.h
@@ -0,0 +1,341 @@
+// 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 GOOGLE_APIS_GAIA_OAUTH2_TOKEN_SERVICE_H_
+#define GOOGLE_APIS_GAIA_OAUTH2_TOKEN_SERVICE_H_
+
+#include <map>
+#include <set>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "base/threading/non_thread_safe.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "google_apis/gaia/google_service_auth_error.h"
+#include "google_apis/gaia/oauth2_access_token_consumer.h"
+#include "google_apis/gaia/oauth2_access_token_fetcher.h"
+
+namespace net {
+class URLRequestContextGetter;
+}
+
+class GoogleServiceAuthError;
+
+// Abstract base class for a service that fetches and caches OAuth2 access
+// tokens. Concrete subclasses should implement GetRefreshToken to return
+// the appropriate refresh token. Derived services might maintain refresh tokens
+// for multiple accounts.
+//
+// All calls are expected from the UI thread.
+//
+// To use this service, call StartRequest() with a given set of scopes and a
+// consumer of the request results. The consumer is required to outlive the
+// request. The request can be deleted. The consumer may be called back
+// asynchronously with the fetch results.
+//
+// - If the consumer is not called back before the request is deleted, it will
+// never be called back.
+// Note in this case, the actual network requests are not canceled and the
+// cache will be populated with the fetched results; it is just the consumer
+// callback that is aborted.
+//
+// - Otherwise the consumer will be called back with the request and the fetch
+// results.
+//
+// The caller of StartRequest() owns the returned request and is responsible to
+// delete the request even once the callback has been invoked.
+class OAuth2TokenService : public base::NonThreadSafe {
+ public:
+ // Class representing a request that fetches an OAuth2 access token.
+ class Request {
+ public:
+ virtual ~Request();
+ protected:
+ Request();
+ };
+
+ // Class representing the consumer of a Request passed to |StartRequest|,
+ // which will be called back when the request completes.
+ class Consumer {
+ public:
+ Consumer();
+ virtual ~Consumer();
+ // |request| is a Request that is started by this consumer and has
+ // completed.
+ virtual void OnGetTokenSuccess(const Request* request,
+ const std::string& access_token,
+ const base::Time& expiration_time) = 0;
+ virtual void OnGetTokenFailure(const Request* request,
+ const GoogleServiceAuthError& error) = 0;
+ };
+
+ // Classes that want to listen for token availability should implement this
+ // interface and register with the AddObserver() call.
+ class Observer {
+ public:
+ // Called whenever a new login-scoped refresh token is available for
+ // account |account_id|. Once available, access tokens can be retrieved for
+ // this account. This is called during initial startup for each token
+ // loaded.
+ virtual void OnRefreshTokenAvailable(const std::string& account_id) {}
+ // Called whenever the login-scoped refresh token becomes unavailable for
+ // account |account_id|.
+ virtual void OnRefreshTokenRevoked(const std::string& account_id) {}
+ // Called after all refresh tokens are loaded during OAuth2TokenService
+ // startup.
+ virtual void OnRefreshTokensLoaded() {}
+ protected:
+ virtual ~Observer() {}
+ };
+
+ // A set of scopes in OAuth2 authentication.
+ typedef std::set<std::string> ScopeSet;
+
+ OAuth2TokenService();
+ virtual ~OAuth2TokenService();
+
+ // Add or remove observers of this token service.
+ void AddObserver(Observer* observer);
+ void RemoveObserver(Observer* observer);
+
+ // Checks in the cache for a valid access token for a specified |account_id|
+ // and |scopes|, and if not found starts a request for an OAuth2 access token
+ // using the OAuth2 refresh token maintained by this instance for that
+ // |account_id|. The caller owns the returned Request.
+ // |scopes| is the set of scopes to get an access token for, |consumer| is
+ // the object that will be called back with results if the returned request
+ // is not deleted.
+ // TODO(atwilson): Make this non-virtual when we change
+ // ProfileOAuth2TokenServiceRequestTest to use FakeProfileOAuth2TokenService.
+ virtual scoped_ptr<Request> StartRequest(const std::string& account_id,
+ const ScopeSet& scopes,
+ Consumer* consumer);
+
+ // This method does the same as |StartRequest| except it uses |client_id| and
+ // |client_secret| to identify OAuth client app instead of using
+ // Chrome's default values.
+ scoped_ptr<Request> StartRequestForClient(
+ const std::string& account_id,
+ const std::string& client_id,
+ const std::string& client_secret,
+ const ScopeSet& scopes,
+ Consumer* consumer);
+
+ // This method does the same as |StartRequest| except it uses the request
+ // context given by |getter| instead of using the one returned by
+ // |GetRequestContext| implemented by derived classes.
+ scoped_ptr<Request> StartRequestWithContext(
+ const std::string& account_id,
+ net::URLRequestContextGetter* getter,
+ const ScopeSet& scopes,
+ Consumer* consumer);
+
+ // Lists account IDs of all accounts with a refresh token maintained by this
+ // instance.
+ virtual std::vector<std::string> GetAccounts();
+
+ // Returns true if a refresh token exists for |account_id|. If false, calls to
+ // |StartRequest| will result in a Consumer::OnGetTokenFailure callback.
+ virtual bool RefreshTokenIsAvailable(const std::string& account_id);
+
+ // Mark an OAuth2 |access_token| issued for |account_id| and |scopes| as
+ // invalid. This should be done if the token was received from this class,
+ // but was not accepted by the server (e.g., the server returned
+ // 401 Unauthorized). The token will be removed from the cache for the given
+ // scopes.
+ void InvalidateToken(const std::string& account_id,
+ const ScopeSet& scopes,
+ const std::string& access_token);
+
+ // Like |InvalidateToken| except is uses |client_id| to identity OAuth2 client
+ // app that issued the request instead of Chrome's default values.
+ void InvalidateTokenForClient(const std::string& account_id,
+ const std::string& client_id,
+ const ScopeSet& scopes,
+ const std::string& access_token);
+
+
+ // Return the current number of entries in the cache.
+ int cache_size_for_testing() const;
+ void set_max_authorization_token_fetch_retries_for_testing(int max_retries);
+ // Returns the current number of pending fetchers matching given params.
+ size_t GetNumPendingRequestsForTesting(
+ const std::string& client_id,
+ const std::string& account_id,
+ const ScopeSet& scopes) const;
+
+ protected:
+ // Implements a cancelable |OAuth2TokenService::Request|, which should be
+ // operated on the UI thread.
+ // TODO(davidroche): move this out of header file.
+ class RequestImpl : public base::SupportsWeakPtr<RequestImpl>,
+ public base::NonThreadSafe,
+ public Request {
+ public:
+ // |consumer| is required to outlive this.
+ explicit RequestImpl(Consumer* consumer);
+ virtual ~RequestImpl();
+
+ // Informs |consumer_| that this request is completed.
+ void InformConsumer(const GoogleServiceAuthError& error,
+ const std::string& access_token,
+ const base::Time& expiration_date);
+
+ private:
+ // |consumer_| to call back when this request completes.
+ Consumer* const consumer_;
+ };
+
+ // Subclasses should return the maintained refresh token for |account_id|.
+ // If no token is available, return an empty string.
+ virtual std::string GetRefreshToken(const std::string& account_id) = 0;
+
+ // Subclasses can override if they want to report errors to the user.
+ virtual void UpdateAuthError(
+ const std::string& account_id,
+ const GoogleServiceAuthError& error);
+
+ // Add a new entry to the cache.
+ // Subclasses can override if there are implementation-specific reasons
+ // that an access token should ever not be cached.
+ virtual void RegisterCacheEntry(const std::string& client_id,
+ const std::string& account_id,
+ const ScopeSet& scopes,
+ const std::string& access_token,
+ const base::Time& expiration_date);
+
+ // Clears the internal token cache.
+ void ClearCache();
+
+ // Clears all of the tokens belonging to |account_id| from the internal token
+ // cache. It does not matter what other parameters, like |client_id| were
+ // used to request the tokens.
+ void ClearCacheForAccount(const std::string& account_id);
+
+ // Cancels all requests that are currently in progress.
+ void CancelAllRequests();
+
+ // Cancels all requests related to a given |account_id|.
+ void CancelRequestsForAccount(const std::string& account_id);
+
+ // Called by subclasses to notify observers.
+ void FireRefreshTokenAvailable(const std::string& account_id);
+ void FireRefreshTokenRevoked(const std::string& account_id);
+ void FireRefreshTokensLoaded();
+
+ // Fetches an OAuth token for the specified client/scopes. Virtual so it can
+ // be overridden for tests and for platform-specific behavior on Android.
+ virtual void FetchOAuth2Token(RequestImpl* request,
+ const std::string& account_id,
+ net::URLRequestContextGetter* getter,
+ const std::string& client_id,
+ const std::string& client_secret,
+ const ScopeSet& scopes);
+
+ // Invalidates the |access_token| issued for |account_id|, |client_id| and
+ // |scopes|. Virtual so it can be overriden for tests and for platform-
+ // specifc behavior.
+ virtual void InvalidateOAuth2Token(const std::string& account_id,
+ const std::string& client_id,
+ const ScopeSet& scopes,
+ const std::string& access_token);
+
+ private:
+ class Fetcher;
+ friend class Fetcher;
+
+ // The parameters used to fetch an OAuth2 access token.
+ struct RequestParameters {
+ RequestParameters(const std::string& client_id,
+ const std::string& account_id,
+ const ScopeSet& scopes);
+ ~RequestParameters();
+ bool operator<(const RequestParameters& params) const;
+
+ // OAuth2 client id.
+ std::string client_id;
+ // Account id for which the request is made.
+ std::string account_id;
+ // URL scopes for the requested access token.
+ ScopeSet scopes;
+ };
+
+ typedef std::map<RequestParameters, Fetcher*> PendingFetcherMap;
+
+ // Derived classes must provide a request context used for fetching access
+ // tokens with the |StartRequest| method.
+ virtual net::URLRequestContextGetter* GetRequestContext() = 0;
+
+ // Struct that contains the information of an OAuth2 access token.
+ struct CacheEntry {
+ std::string access_token;
+ base::Time expiration_date;
+ };
+
+ // This method does the same as |StartRequestWithContext| except it
+ // uses |client_id| and |client_secret| to identify OAuth
+ // client app instead of using Chrome's default values.
+ scoped_ptr<Request> StartRequestForClientWithContext(
+ const std::string& account_id,
+ net::URLRequestContextGetter* getter,
+ const std::string& client_id,
+ const std::string& client_secret,
+ const ScopeSet& scopes,
+ Consumer* consumer);
+
+ // Returns true if GetCacheEntry would return a valid cache entry for the
+ // given scopes.
+ bool HasCacheEntry(const RequestParameters& client_scopes);
+
+ // Posts a task to fire the Consumer callback with the cached token. Must
+ // Must only be called if HasCacheEntry() returns true.
+ void StartCacheLookupRequest(RequestImpl* request,
+ const RequestParameters& client_scopes,
+ Consumer* consumer);
+
+ // Returns a currently valid OAuth2 access token for the given set of scopes,
+ // or NULL if none have been cached. Note the user of this method should
+ // ensure no entry with the same |client_scopes| is added before the usage of
+ // the returned entry is done.
+ const CacheEntry* GetCacheEntry(const RequestParameters& client_scopes);
+
+ // Removes an access token for the given set of scopes from the cache.
+ // Returns true if the entry was removed, otherwise false.
+ bool RemoveCacheEntry(const RequestParameters& client_scopes,
+ const std::string& token_to_remove);
+
+ // Called when |fetcher| finishes fetching.
+ void OnFetchComplete(Fetcher* fetcher);
+
+ // Called when a number of fetchers need to be canceled.
+ void CancelFetchers(std::vector<Fetcher*> fetchers_to_cancel);
+
+ // The cache of currently valid tokens.
+ typedef std::map<RequestParameters, CacheEntry> TokenCache;
+ TokenCache token_cache_;
+
+ // A map from fetch parameters to a fetcher that is fetching an OAuth2 access
+ // token using these parameters.
+ PendingFetcherMap pending_fetchers_;
+
+ // List of observers to notify when token availability changes.
+ // Makes sure list is empty on destruction.
+ ObserverList<Observer, true> observer_list_;
+
+ // Maximum number of retries in fetching an OAuth2 access token.
+ static int max_fetch_retry_num_;
+
+ FRIEND_TEST_ALL_PREFIXES(OAuth2TokenServiceTest, RequestParametersOrderTest);
+ FRIEND_TEST_ALL_PREFIXES(OAuth2TokenServiceTest,
+ SameScopesRequestedForDifferentClients);
+
+ DISALLOW_COPY_AND_ASSIGN(OAuth2TokenService);
+};
+
+#endif // GOOGLE_APIS_GAIA_OAUTH2_TOKEN_SERVICE_H_
diff --git a/chromium/google_apis/gaia/oauth2_token_service_test_util.cc b/chromium/google_apis/gaia/oauth2_token_service_test_util.cc
new file mode 100644
index 00000000000..2aae59bb41a
--- /dev/null
+++ b/chromium/google_apis/gaia/oauth2_token_service_test_util.cc
@@ -0,0 +1,44 @@
+// 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 "google_apis/gaia/oauth2_token_service_test_util.h"
+
+#include "base/strings/stringprintf.h"
+
+namespace {
+const char kValidTokenResponse[] =
+ "{"
+ " \"access_token\": \"%s\","
+ " \"expires_in\": %d,"
+ " \"token_type\": \"Bearer\""
+ "}";
+}
+
+std::string GetValidTokenResponse(std::string token, int expiration) {
+ return base::StringPrintf(kValidTokenResponse, token.c_str(), expiration);
+}
+
+TestingOAuth2TokenServiceConsumer::TestingOAuth2TokenServiceConsumer()
+ : number_of_successful_tokens_(0),
+ last_error_(GoogleServiceAuthError::AuthErrorNone()),
+ number_of_errors_(0) {
+}
+
+TestingOAuth2TokenServiceConsumer::~TestingOAuth2TokenServiceConsumer() {
+}
+
+void TestingOAuth2TokenServiceConsumer::OnGetTokenSuccess(
+ const OAuth2TokenService::Request* request,
+ const std::string& token,
+ const base::Time& expiration_date) {
+ last_token_ = token;
+ ++number_of_successful_tokens_;
+}
+
+void TestingOAuth2TokenServiceConsumer::OnGetTokenFailure(
+ const OAuth2TokenService::Request* request,
+ const GoogleServiceAuthError& error) {
+ last_error_ = error;
+ ++number_of_errors_;
+}
diff --git a/chromium/google_apis/gaia/oauth2_token_service_test_util.h b/chromium/google_apis/gaia/oauth2_token_service_test_util.h
new file mode 100644
index 00000000000..53ccdf8228a
--- /dev/null
+++ b/chromium/google_apis/gaia/oauth2_token_service_test_util.h
@@ -0,0 +1,34 @@
+// 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 GOOGLE_APIS_GAIA_OAUTH2_TOKEN_SERVICE_TEST_UTIL_H_
+#define GOOGLE_APIS_GAIA_OAUTH2_TOKEN_SERVICE_TEST_UTIL_H_
+
+#include <string>
+
+#include "google_apis/gaia/google_service_auth_error.h"
+#include "google_apis/gaia/oauth2_token_service.h"
+
+std::string GetValidTokenResponse(std::string token, int expiration);
+
+// A simple testing consumer.
+class TestingOAuth2TokenServiceConsumer : public OAuth2TokenService::Consumer {
+ public:
+ TestingOAuth2TokenServiceConsumer();
+ virtual ~TestingOAuth2TokenServiceConsumer();
+
+ // OAuth2TokenService::Consumer overrides.
+ virtual void OnGetTokenSuccess(const OAuth2TokenService::Request* request,
+ const std::string& token,
+ const base::Time& expiration_date) OVERRIDE;
+ virtual void OnGetTokenFailure(const OAuth2TokenService::Request* request,
+ const GoogleServiceAuthError& error) OVERRIDE;
+
+ std::string last_token_;
+ int number_of_successful_tokens_;
+ GoogleServiceAuthError last_error_;
+ int number_of_errors_;
+};
+
+#endif // GOOGLE_APIS_GAIA_OAUTH2_TOKEN_SERVICE_TEST_UTIL_H_
diff --git a/chromium/google_apis/gaia/oauth2_token_service_unittest.cc b/chromium/google_apis/gaia/oauth2_token_service_unittest.cc
new file mode 100644
index 00000000000..dcd21b12e93
--- /dev/null
+++ b/chromium/google_apis/gaia/oauth2_token_service_unittest.cc
@@ -0,0 +1,635 @@
+// 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 <string>
+
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "google_apis/gaia/gaia_constants.h"
+#include "google_apis/gaia/google_service_auth_error.h"
+#include "google_apis/gaia/oauth2_access_token_consumer.h"
+#include "google_apis/gaia/oauth2_access_token_fetcher.h"
+#include "google_apis/gaia/oauth2_token_service.h"
+#include "google_apis/gaia/oauth2_token_service_test_util.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 "net/url_request/url_request_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// A testing consumer that retries on error.
+class RetryingTestingOAuth2TokenServiceConsumer
+ : public TestingOAuth2TokenServiceConsumer {
+ public:
+ RetryingTestingOAuth2TokenServiceConsumer(
+ OAuth2TokenService* oauth2_service,
+ const std::string& account_id)
+ : oauth2_service_(oauth2_service),
+ account_id_(account_id) {}
+ virtual ~RetryingTestingOAuth2TokenServiceConsumer() {}
+
+ virtual void OnGetTokenFailure(const OAuth2TokenService::Request* request,
+ const GoogleServiceAuthError& error) OVERRIDE {
+ TestingOAuth2TokenServiceConsumer::OnGetTokenFailure(request, error);
+ request_.reset(oauth2_service_->StartRequest(
+ account_id_, OAuth2TokenService::ScopeSet(), this).release());
+ }
+
+ OAuth2TokenService* oauth2_service_;
+ std::string account_id_;
+ scoped_ptr<OAuth2TokenService::Request> request_;
+};
+
+class TestOAuth2TokenService : public OAuth2TokenService {
+ public:
+ explicit TestOAuth2TokenService(net::TestURLRequestContextGetter* getter)
+ : request_context_getter_(getter) {
+ }
+
+ void CancelAllRequestsForTest() { CancelAllRequests(); }
+
+ void CancelRequestsForAccountForTest(const std::string& account_id) {
+ CancelRequestsForAccount(account_id);
+ }
+
+ // For testing: set the refresh token to be used.
+ void set_refresh_token(const std::string& account_id,
+ const std::string& refresh_token) {
+ refresh_tokens_[account_id] = refresh_token;
+ }
+
+ protected:
+ virtual std::string GetRefreshToken(const std::string& account_id) OVERRIDE {
+ // account_id explicitly ignored.
+ return refresh_tokens_[account_id];
+ }
+
+ private:
+ // OAuth2TokenService implementation.
+ virtual net::URLRequestContextGetter* GetRequestContext() OVERRIDE {
+ return request_context_getter_.get();
+ }
+
+ std::map<std::string, std::string> refresh_tokens_;
+ scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_;
+};
+
+class OAuth2TokenServiceTest : public testing::Test {
+ public:
+ virtual void SetUp() OVERRIDE {
+ oauth2_service_.reset(
+ new TestOAuth2TokenService(new net::TestURLRequestContextGetter(
+ message_loop_.message_loop_proxy())));
+ account_id_ = "test_user@gmail.com";
+ }
+
+ virtual void TearDown() OVERRIDE {
+ // Makes sure that all the clean up tasks are run.
+ base::RunLoop().RunUntilIdle();
+ }
+
+ protected:
+ base::MessageLoopForIO message_loop_; // net:: stuff needs IO message loop.
+ net::TestURLFetcherFactory factory_;
+ scoped_ptr<TestOAuth2TokenService> oauth2_service_;
+ std::string account_id_;
+ TestingOAuth2TokenServiceConsumer consumer_;
+};
+
+TEST_F(OAuth2TokenServiceTest, NoOAuth2RefreshToken) {
+ scoped_ptr<OAuth2TokenService::Request> request(
+ oauth2_service_->StartRequest(account_id_, OAuth2TokenService::ScopeSet(),
+ &consumer_));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
+ EXPECT_EQ(1, consumer_.number_of_errors_);
+}
+
+TEST_F(OAuth2TokenServiceTest, FailureShouldNotRetry) {
+ oauth2_service_->set_refresh_token(account_id_, "refreshToken");
+ scoped_ptr<OAuth2TokenService::Request> request(
+ oauth2_service_->StartRequest(account_id_,
+ OAuth2TokenService::ScopeSet(),
+ &consumer_));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
+ EXPECT_EQ(0, consumer_.number_of_errors_);
+ net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
+ 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(1, consumer_.number_of_errors_);
+ EXPECT_EQ(fetcher, factory_.GetFetcherByID(0));
+}
+
+TEST_F(OAuth2TokenServiceTest, SuccessWithoutCaching) {
+ oauth2_service_->set_refresh_token(account_id_, "refreshToken");
+ scoped_ptr<OAuth2TokenService::Request> request(
+ oauth2_service_->StartRequest(account_id_,
+ OAuth2TokenService::ScopeSet(),
+ &consumer_));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
+ EXPECT_EQ(0, consumer_.number_of_errors_);
+ net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
+ ASSERT_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_);
+}
+
+TEST_F(OAuth2TokenServiceTest, SuccessWithCaching) {
+ OAuth2TokenService::ScopeSet scopes1;
+ scopes1.insert("s1");
+ scopes1.insert("s2");
+ OAuth2TokenService::ScopeSet scopes1_same;
+ scopes1_same.insert("s2");
+ scopes1_same.insert("s1");
+ OAuth2TokenService::ScopeSet scopes2;
+ scopes2.insert("s3");
+
+ oauth2_service_->set_refresh_token(account_id_, "refreshToken");
+
+ // First request.
+ scoped_ptr<OAuth2TokenService::Request> request(
+ oauth2_service_->StartRequest(account_id_, scopes1, &consumer_));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
+ EXPECT_EQ(0, consumer_.number_of_errors_);
+ net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
+ ASSERT_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 to the same set of scopes, should return the same token
+ // without needing a network request.
+ scoped_ptr<OAuth2TokenService::Request> request2(
+ oauth2_service_->StartRequest(account_id_, scopes1_same, &consumer_));
+ base::RunLoop().RunUntilIdle();
+
+ // No new network fetcher.
+ EXPECT_EQ(2, consumer_.number_of_successful_tokens_);
+ EXPECT_EQ(0, consumer_.number_of_errors_);
+ EXPECT_EQ("token", consumer_.last_token_);
+
+ // Third request to a new set of scopes, should return another token.
+ scoped_ptr<OAuth2TokenService::Request> request3(
+ oauth2_service_->StartRequest(account_id_, scopes2, &consumer_));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(2, consumer_.number_of_successful_tokens_);
+ EXPECT_EQ(0, consumer_.number_of_errors_);
+ fetcher = factory_.GetFetcherByID(0);
+ ASSERT_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_);
+}
+
+TEST_F(OAuth2TokenServiceTest, SuccessAndExpirationAndFailure) {
+ oauth2_service_->set_refresh_token(account_id_, "refreshToken");
+
+ // First request.
+ scoped_ptr<OAuth2TokenService::Request> request(
+ oauth2_service_->StartRequest(account_id_,
+ OAuth2TokenService::ScopeSet(),
+ &consumer_));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
+ EXPECT_EQ(0, consumer_.number_of_errors_);
+ net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
+ ASSERT_TRUE(fetcher);
+ fetcher->set_response_code(net::HTTP_OK);
+ fetcher->SetResponseString(GetValidTokenResponse("token", 0));
+ 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 must try to access the network as the token has expired.
+ scoped_ptr<OAuth2TokenService::Request> request2(
+ oauth2_service_->StartRequest(account_id_,
+ OAuth2TokenService::ScopeSet(),
+ &consumer_));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(1, consumer_.number_of_successful_tokens_);
+ EXPECT_EQ(0, consumer_.number_of_errors_);
+
+ // Network failure.
+ fetcher = factory_.GetFetcherByID(0);
+ ASSERT_TRUE(fetcher);
+ fetcher->set_response_code(net::HTTP_UNAUTHORIZED);
+ fetcher->SetResponseString(std::string());
+ fetcher->delegate()->OnURLFetchComplete(fetcher);
+ EXPECT_EQ(1, consumer_.number_of_successful_tokens_);
+ EXPECT_EQ(1, consumer_.number_of_errors_);
+}
+
+TEST_F(OAuth2TokenServiceTest, SuccessAndExpirationAndSuccess) {
+ oauth2_service_->set_refresh_token(account_id_, "refreshToken");
+
+ // First request.
+ scoped_ptr<OAuth2TokenService::Request> request(
+ oauth2_service_->StartRequest(account_id_,
+ OAuth2TokenService::ScopeSet(),
+ &consumer_));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
+ EXPECT_EQ(0, consumer_.number_of_errors_);
+ net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
+ ASSERT_TRUE(fetcher);
+ fetcher->set_response_code(net::HTTP_OK);
+ fetcher->SetResponseString(GetValidTokenResponse("token", 0));
+ 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 must try to access the network as the token has expired.
+ scoped_ptr<OAuth2TokenService::Request> request2(
+ oauth2_service_->StartRequest(account_id_,
+ OAuth2TokenService::ScopeSet(),
+ &consumer_));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(1, consumer_.number_of_successful_tokens_);
+ EXPECT_EQ(0, consumer_.number_of_errors_);
+
+ fetcher = factory_.GetFetcherByID(0);
+ ASSERT_TRUE(fetcher);
+ fetcher->set_response_code(net::HTTP_OK);
+ fetcher->SetResponseString(GetValidTokenResponse("another token", 0));
+ fetcher->delegate()->OnURLFetchComplete(fetcher);
+ EXPECT_EQ(2, consumer_.number_of_successful_tokens_);
+ EXPECT_EQ(0, consumer_.number_of_errors_);
+ EXPECT_EQ("another token", consumer_.last_token_);
+}
+
+TEST_F(OAuth2TokenServiceTest, RequestDeletedBeforeCompletion) {
+ oauth2_service_->set_refresh_token(account_id_, "refreshToken");
+
+ scoped_ptr<OAuth2TokenService::Request> request(
+ oauth2_service_->StartRequest(account_id_,
+ OAuth2TokenService::ScopeSet(),
+ &consumer_));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
+ EXPECT_EQ(0, consumer_.number_of_errors_);
+ net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
+ ASSERT_TRUE(fetcher);
+
+ request.reset();
+
+ fetcher->set_response_code(net::HTTP_OK);
+ fetcher->SetResponseString(GetValidTokenResponse("token", 3600));
+ fetcher->delegate()->OnURLFetchComplete(fetcher);
+ EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
+ EXPECT_EQ(0, consumer_.number_of_errors_);
+}
+
+TEST_F(OAuth2TokenServiceTest, RequestDeletedAfterCompletion) {
+ oauth2_service_->set_refresh_token(account_id_, "refreshToken");
+
+ scoped_ptr<OAuth2TokenService::Request> request(oauth2_service_->StartRequest(
+ account_id_, OAuth2TokenService::ScopeSet(), &consumer_));
+ base::RunLoop().RunUntilIdle();
+ net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
+ ASSERT_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_);
+
+ request.reset();
+
+ EXPECT_EQ(1, consumer_.number_of_successful_tokens_);
+ EXPECT_EQ(0, consumer_.number_of_errors_);
+ EXPECT_EQ("token", consumer_.last_token_);
+}
+
+TEST_F(OAuth2TokenServiceTest, MultipleRequestsForTheSameScopesWithOneDeleted) {
+ oauth2_service_->set_refresh_token(account_id_, "refreshToken");
+
+ scoped_ptr<OAuth2TokenService::Request> request(oauth2_service_->StartRequest(
+ account_id_, OAuth2TokenService::ScopeSet(), &consumer_));
+ base::RunLoop().RunUntilIdle();
+ scoped_ptr<OAuth2TokenService::Request> request2(
+ oauth2_service_->StartRequest(account_id_, OAuth2TokenService::ScopeSet(),
+ &consumer_));
+ base::RunLoop().RunUntilIdle();
+
+ request.reset();
+
+ net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
+ ASSERT_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_);
+}
+
+TEST_F(OAuth2TokenServiceTest, ClearedRefreshTokenFailsSubsequentRequests) {
+ // We have a valid refresh token; the first request is successful.
+ oauth2_service_->set_refresh_token(account_id_, "refreshToken");
+ scoped_ptr<OAuth2TokenService::Request> request(oauth2_service_->StartRequest(
+ account_id_, OAuth2TokenService::ScopeSet(), &consumer_));
+ base::RunLoop().RunUntilIdle();
+ net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
+ ASSERT_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_);
+
+ // The refresh token is no longer available; subsequent requests fail.
+ oauth2_service_->set_refresh_token(account_id_, "");
+ request = oauth2_service_->StartRequest(account_id_,
+ OAuth2TokenService::ScopeSet(), &consumer_);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(1, consumer_.number_of_successful_tokens_);
+ EXPECT_EQ(1, consumer_.number_of_errors_);
+}
+
+TEST_F(OAuth2TokenServiceTest,
+ ChangedRefreshTokenDoesNotAffectInFlightRequests) {
+ oauth2_service_->set_refresh_token(account_id_, "first refreshToken");
+ OAuth2TokenService::ScopeSet scopes;
+ scopes.insert("s1");
+ scopes.insert("s2");
+ OAuth2TokenService::ScopeSet scopes1;
+ scopes.insert("s3");
+ scopes.insert("s4");
+
+ scoped_ptr<OAuth2TokenService::Request> request(oauth2_service_->StartRequest(
+ account_id_, scopes, &consumer_));
+ base::RunLoop().RunUntilIdle();
+ net::TestURLFetcher* fetcher1 = factory_.GetFetcherByID(0);
+ ASSERT_TRUE(fetcher1);
+
+ // Note |request| is still pending when the refresh token changes.
+ oauth2_service_->set_refresh_token(account_id_, "second refreshToken");
+
+ // A 2nd request (using the new refresh token) that occurs and completes
+ // while the 1st request is in flight is successful.
+ TestingOAuth2TokenServiceConsumer consumer2;
+ scoped_ptr<OAuth2TokenService::Request> request2(
+ oauth2_service_->StartRequest(account_id_, scopes1, &consumer2));
+ base::RunLoop().RunUntilIdle();
+
+ net::TestURLFetcher* fetcher2 = factory_.GetFetcherByID(0);
+ fetcher2->set_response_code(net::HTTP_OK);
+ fetcher2->SetResponseString(GetValidTokenResponse("second token", 3600));
+ fetcher2->delegate()->OnURLFetchComplete(fetcher2);
+ EXPECT_EQ(1, consumer2.number_of_successful_tokens_);
+ EXPECT_EQ(0, consumer2.number_of_errors_);
+ EXPECT_EQ("second token", consumer2.last_token_);
+
+ fetcher1->set_response_code(net::HTTP_OK);
+ fetcher1->SetResponseString(GetValidTokenResponse("first token", 3600));
+ fetcher1->delegate()->OnURLFetchComplete(fetcher1);
+ EXPECT_EQ(1, consumer_.number_of_successful_tokens_);
+ EXPECT_EQ(0, consumer_.number_of_errors_);
+ EXPECT_EQ("first token", consumer_.last_token_);
+}
+
+TEST_F(OAuth2TokenServiceTest, ServiceShutDownBeforeFetchComplete) {
+ oauth2_service_->set_refresh_token(account_id_, "refreshToken");
+ scoped_ptr<OAuth2TokenService::Request> request(oauth2_service_->StartRequest(
+ account_id_, OAuth2TokenService::ScopeSet(), &consumer_));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
+ EXPECT_EQ(0, consumer_.number_of_errors_);
+
+ // The destructor should cancel all in-flight fetchers.
+ oauth2_service_.reset(NULL);
+
+ EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
+ EXPECT_EQ(1, consumer_.number_of_errors_);
+}
+
+TEST_F(OAuth2TokenServiceTest, RetryingConsumer) {
+ oauth2_service_->set_refresh_token(account_id_, "refreshToken");
+ RetryingTestingOAuth2TokenServiceConsumer consumer(oauth2_service_.get(),
+ account_id_);
+ scoped_ptr<OAuth2TokenService::Request> request(oauth2_service_->StartRequest(
+ account_id_, OAuth2TokenService::ScopeSet(), &consumer));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(0, consumer.number_of_successful_tokens_);
+ EXPECT_EQ(0, consumer.number_of_errors_);
+
+ net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
+ 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(1, consumer.number_of_errors_);
+
+ fetcher = factory_.GetFetcherByID(0);
+ 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) {
+ OAuth2TokenService::ScopeSet scopes;
+ oauth2_service_->set_refresh_token(account_id_, "refreshToken");
+
+ // First request.
+ scoped_ptr<OAuth2TokenService::Request> request(
+ oauth2_service_->StartRequest(account_id_, scopes, &consumer_));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
+ EXPECT_EQ(0, consumer_.number_of_errors_);
+ net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
+ ASSERT_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(account_id_, scopes, &consumer_));
+ base::RunLoop().RunUntilIdle();
+
+ // No new network fetcher.
+ 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(account_id_, scopes, consumer_.last_token_);
+ scoped_ptr<OAuth2TokenService::Request> request3(
+ oauth2_service_->StartRequest(account_id_, scopes, &consumer_));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(2, consumer_.number_of_successful_tokens_);
+ EXPECT_EQ(0, consumer_.number_of_errors_);
+ fetcher = factory_.GetFetcherByID(0);
+ ASSERT_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_);
+}
+
+TEST_F(OAuth2TokenServiceTest, CancelAllRequests) {
+ oauth2_service_->set_refresh_token(account_id_, "refreshToken");
+ scoped_ptr<OAuth2TokenService::Request> request(
+ oauth2_service_->StartRequest(account_id_, OAuth2TokenService::ScopeSet(),
+ &consumer_));
+
+ oauth2_service_->set_refresh_token("account_id_2", "refreshToken2");
+ scoped_ptr<OAuth2TokenService::Request> request2(
+ oauth2_service_->StartRequest(account_id_, OAuth2TokenService::ScopeSet(),
+ &consumer_));
+
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
+ EXPECT_EQ(0, consumer_.number_of_errors_);
+
+ oauth2_service_->CancelAllRequestsForTest();
+
+ EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
+ EXPECT_EQ(2, consumer_.number_of_errors_);
+}
+
+TEST_F(OAuth2TokenServiceTest, CancelRequestsForAccount) {
+ OAuth2TokenService::ScopeSet scope_set_1;
+ scope_set_1.insert("scope1");
+ scope_set_1.insert("scope2");
+ OAuth2TokenService::ScopeSet scope_set_2(scope_set_1.begin(),
+ scope_set_1.end());
+ scope_set_2.insert("scope3");
+
+ oauth2_service_->set_refresh_token(account_id_, "refreshToken");
+ scoped_ptr<OAuth2TokenService::Request> request1(
+ oauth2_service_->StartRequest(account_id_, scope_set_1, &consumer_));
+ scoped_ptr<OAuth2TokenService::Request> request2(
+ oauth2_service_->StartRequest(account_id_, scope_set_2, &consumer_));
+
+ std::string account_id_2("account_id_2");
+ oauth2_service_->set_refresh_token(account_id_2, "refreshToken2");
+ scoped_ptr<OAuth2TokenService::Request> request3(
+ oauth2_service_->StartRequest(account_id_2, scope_set_1, &consumer_));
+
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
+ EXPECT_EQ(0, consumer_.number_of_errors_);
+
+ oauth2_service_->CancelRequestsForAccountForTest(account_id_);
+
+ EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
+ EXPECT_EQ(2, consumer_.number_of_errors_);
+
+ oauth2_service_->CancelRequestsForAccountForTest(account_id_2);
+
+ EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
+ EXPECT_EQ(3, consumer_.number_of_errors_);
+}
+
+TEST_F(OAuth2TokenServiceTest, SameScopesRequestedForDifferentClients) {
+ std::string client_id_1("client1");
+ std::string client_secret_1("secret1");
+ std::string client_id_2("client2");
+ std::string client_secret_2("secret2");
+ std::set<std::string> scope_set;
+ scope_set.insert("scope1");
+ scope_set.insert("scope2");
+
+ std::string refresh_token("refreshToken");
+ oauth2_service_->set_refresh_token(account_id_, refresh_token);
+
+ scoped_ptr<OAuth2TokenService::Request> request1(
+ oauth2_service_->StartRequestForClient(account_id_,
+ client_id_1,
+ client_secret_1,
+ scope_set,
+ &consumer_));
+ scoped_ptr<OAuth2TokenService::Request> request2(
+ oauth2_service_->StartRequestForClient(account_id_,
+ client_id_2,
+ client_secret_2,
+ scope_set,
+ &consumer_));
+ // Start a request that should be duplicate of |request1|.
+ scoped_ptr<OAuth2TokenService::Request> request3(
+ oauth2_service_->StartRequestForClient(account_id_,
+ client_id_1,
+ client_secret_1,
+ scope_set,
+ &consumer_));
+ base::RunLoop().RunUntilIdle();
+
+ ASSERT_EQ(2U,
+ oauth2_service_->GetNumPendingRequestsForTesting(
+ client_id_1,
+ account_id_,
+ scope_set));
+ ASSERT_EQ(1U,
+ oauth2_service_->GetNumPendingRequestsForTesting(
+ client_id_2,
+ account_id_,
+ scope_set));
+}
+
+TEST_F(OAuth2TokenServiceTest, RequestParametersOrderTest) {
+ OAuth2TokenService::ScopeSet set_0;
+ OAuth2TokenService::ScopeSet set_1;
+ set_1.insert("1");
+
+ OAuth2TokenService::RequestParameters params[] = {
+ OAuth2TokenService::RequestParameters("0", "0", set_0),
+ OAuth2TokenService::RequestParameters("0", "0", set_1),
+ OAuth2TokenService::RequestParameters("0", "1", set_0),
+ OAuth2TokenService::RequestParameters("0", "1", set_1),
+ OAuth2TokenService::RequestParameters("1", "0", set_0),
+ OAuth2TokenService::RequestParameters("1", "0", set_1),
+ OAuth2TokenService::RequestParameters("1", "1", set_0),
+ OAuth2TokenService::RequestParameters("1", "1", set_1),
+ };
+
+ for (size_t i = 0; i < arraysize(params); i++) {
+ for (size_t j = 0; j < arraysize(params); j++) {
+ if (i == j) {
+ EXPECT_FALSE(params[i] < params[j]) << " i=" << i << ", j=" << j;
+ EXPECT_FALSE(params[j] < params[i]) << " i=" << i << ", j=" << j;
+ } else if (i < j) {
+ EXPECT_TRUE(params[i] < params[j]) << " i=" << i << ", j=" << j;
+ EXPECT_FALSE(params[j] < params[i]) << " i=" << i << ", j=" << j;
+ } else {
+ EXPECT_TRUE(params[j] < params[i]) << " i=" << i << ", j=" << j;
+ EXPECT_FALSE(params[i] < params[j]) << " i=" << i << ", j=" << j;
+ }
+ }
+ }
+}
diff --git a/chromium/google_apis/google_api_keys.cc b/chromium/google_apis/google_api_keys.cc
index fc4b54735b4..0baf00dc393 100644
--- a/chromium/google_apis/google_api_keys.cc
+++ b/chromium/google_apis/google_api_keys.cc
@@ -49,6 +49,14 @@
#define GOOGLE_CLIENT_SECRET_REMOTING DUMMY_API_TOKEN
#endif
+#if !defined(GOOGLE_CLIENT_ID_REMOTING_HOST)
+#define GOOGLE_CLIENT_ID_REMOTING_HOST DUMMY_API_TOKEN
+#endif
+
+#if !defined(GOOGLE_CLIENT_SECRET_REMOTING_HOST)
+#define GOOGLE_CLIENT_SECRET_REMOTING_HOST DUMMY_API_TOKEN
+#endif
+
// These are used as shortcuts for developers and users providing
// OAuth credentials via preprocessor defines or environment
// variables. If set, they will be used to replace any of the client
@@ -152,6 +160,21 @@ class APIKeyCache {
default_client_secret,
environment.get(),
command_line);
+
+ client_ids_[CLIENT_REMOTING_HOST] = CalculateKeyValue(
+ GOOGLE_CLIENT_ID_REMOTING_HOST,
+ STRINGIZE_NO_EXPANSION(GOOGLE_CLIENT_ID_REMOTING_HOST),
+ NULL,
+ default_client_id,
+ environment.get(),
+ command_line);
+ client_secrets_[CLIENT_REMOTING_HOST] = CalculateKeyValue(
+ GOOGLE_CLIENT_SECRET_REMOTING_HOST,
+ STRINGIZE_NO_EXPANSION(GOOGLE_CLIENT_SECRET_REMOTING_HOST),
+ NULL,
+ default_client_secret,
+ environment.get(),
+ command_line);
}
std::string api_key() const { return api_key_; }
diff --git a/chromium/google_apis/google_api_keys.h b/chromium/google_apis/google_api_keys.h
index 28421e8e222..8f4bc7e9cac 100644
--- a/chromium/google_apis/google_api_keys.h
+++ b/chromium/google_apis/google_api_keys.h
@@ -71,6 +71,7 @@ enum OAuth2Client {
CLIENT_MAIN, // Several different features use this.
CLIENT_CLOUD_PRINT,
CLIENT_REMOTING,
+ CLIENT_REMOTING_HOST,
CLIENT_NUM_ITEMS // Must be last item.
};
diff --git a/chromium/google_apis/google_api_keys.py b/chromium/google_apis/google_api_keys.py
index 6776aaec343..94170ea2864 100755
--- a/chromium/google_apis/google_api_keys.py
+++ b/chromium/google_apis/google_api_keys.py
@@ -88,3 +88,8 @@ if __name__ == "__main__":
print 'GOOGLE_CLIENT_SECRET_CLOUD_PRINT=%s' % GetClientSecret('CLOUD_PRINT')
print 'GOOGLE_CLIENT_ID_REMOTING=%s' % GetClientID('REMOTING')
print 'GOOGLE_CLIENT_SECRET_REMOTING=%s' % GetClientSecret('REMOTING')
+ print 'GOOGLE_CLIENT_ID_REMOTING_HOST=%s' % GetClientID('REMOTING_HOST')
+ print 'GOOGLE_CLIENT_SECRET_REMOTING_HOST=%s' % GetClientSecret(
+ 'REMOTING_HOST')
+ print 'GOOGLE_CLIENT_ID_REMOTING_IDENTITY_API=%s' %GetClientID(
+ 'REMOTING_IDENTITY_API')
diff --git a/chromium/google_apis/google_api_keys_unittest.cc b/chromium/google_apis/google_api_keys_unittest.cc
index 4d338f5289b..7432f93e830 100644
--- a/chromium/google_apis/google_api_keys_unittest.cc
+++ b/chromium/google_apis/google_api_keys_unittest.cc
@@ -59,8 +59,10 @@ class GoogleAPIKeysTest : public testing::Test {
env_cache_[4].variable_name = "GOOGLE_CLIENT_SECRET_CLOUD_PRINT";
env_cache_[5].variable_name = "GOOGLE_CLIENT_ID_REMOTING";
env_cache_[6].variable_name = "GOOGLE_CLIENT_SECRET_REMOTING";
- env_cache_[7].variable_name = "GOOGLE_DEFAULT_CLIENT_ID";
- env_cache_[8].variable_name = "GOOGLE_DEFAULT_CLIENT_SECRET";
+ env_cache_[7].variable_name = "GOOGLE_CLIENT_ID_REMOTING_HOST";
+ env_cache_[8].variable_name = "GOOGLE_CLIENT_SECRET_REMOTING_HOST";
+ env_cache_[9].variable_name = "GOOGLE_DEFAULT_CLIENT_ID";
+ env_cache_[10].variable_name = "GOOGLE_DEFAULT_CLIENT_SECRET";
}
virtual void SetUp() {
@@ -114,6 +116,8 @@ namespace official_build {
#undef GOOGLE_CLIENT_SECRET_CLOUD_PRINT
#undef GOOGLE_CLIENT_ID_REMOTING
#undef GOOGLE_CLIENT_SECRET_REMOTING
+#undef GOOGLE_CLIENT_ID_REMOTING_HOST
+#undef GOOGLE_CLIENT_SECRET_REMOTING_HOST
#undef GOOGLE_DEFAULT_CLIENT_ID
#undef GOOGLE_DEFAULT_CLIENT_SECRET
@@ -150,6 +154,11 @@ TEST_F(GoogleAPIKeysTest, OfficialKeys) {
std::string secret_remoting =
testcase::g_api_key_cache.Get().GetClientSecret(
testcase::CLIENT_REMOTING);
+ std::string id_remoting_host = testcase::g_api_key_cache.Get().GetClientID(
+ testcase::CLIENT_REMOTING_HOST);
+ std::string secret_remoting_host =
+ testcase::g_api_key_cache.Get().GetClientSecret(
+ testcase::CLIENT_REMOTING_HOST);
EXPECT_NE(0u, api_key.size());
EXPECT_NE(DUMMY_API_TOKEN, api_key);
@@ -180,6 +189,14 @@ TEST_F(GoogleAPIKeysTest, OfficialKeys) {
EXPECT_NE(0u, secret_remoting.size());
EXPECT_NE(DUMMY_API_TOKEN, secret_remoting);
EXPECT_NE(kDummyToken, secret_remoting);
+
+ EXPECT_NE(0u, id_remoting_host.size());
+ EXPECT_NE(DUMMY_API_TOKEN, id_remoting_host);
+ EXPECT_NE(kDummyToken, id_remoting_host);
+
+ EXPECT_NE(0u, secret_remoting_host.size());
+ EXPECT_NE(DUMMY_API_TOKEN, secret_remoting_host);
+ EXPECT_NE(kDummyToken, secret_remoting_host);
}
#endif // defined(GOOGLE_CHROME_BUILD) || defined(USE_OFFICIAL_GOOGLE_API_KEYS)
@@ -201,6 +218,8 @@ namespace default_keys {
#undef GOOGLE_CLIENT_SECRET_CLOUD_PRINT
#undef GOOGLE_CLIENT_ID_REMOTING
#undef GOOGLE_CLIENT_SECRET_REMOTING
+#undef GOOGLE_CLIENT_ID_REMOTING_HOST
+#undef GOOGLE_CLIENT_SECRET_REMOTING_HOST
#undef GOOGLE_DEFAULT_CLIENT_ID
#undef GOOGLE_DEFAULT_CLIENT_SECRET
@@ -232,6 +251,11 @@ TEST_F(GoogleAPIKeysTest, DefaultKeys) {
std::string secret_remoting =
testcase::g_api_key_cache.Get().GetClientSecret(
testcase::CLIENT_REMOTING);
+ std::string id_remoting_host = testcase::g_api_key_cache.Get().GetClientID(
+ testcase::CLIENT_REMOTING_HOST);
+ std::string secret_remoting_host =
+ testcase::g_api_key_cache.Get().GetClientSecret(
+ testcase::CLIENT_REMOTING_HOST);
EXPECT_EQ(kDummyToken, api_key);
EXPECT_EQ(kDummyToken, id_main);
@@ -240,6 +264,8 @@ TEST_F(GoogleAPIKeysTest, DefaultKeys) {
EXPECT_EQ(kDummyToken, secret_cloud_print);
EXPECT_EQ(kDummyToken, id_remoting);
EXPECT_EQ(kDummyToken, secret_remoting);
+ EXPECT_EQ(kDummyToken, id_remoting_host);
+ EXPECT_EQ(kDummyToken, secret_remoting_host);
}
// Override a couple of keys, leave the rest default.
@@ -255,6 +281,8 @@ namespace override_some_keys {
#undef GOOGLE_CLIENT_SECRET_CLOUD_PRINT
#undef GOOGLE_CLIENT_ID_REMOTING
#undef GOOGLE_CLIENT_SECRET_REMOTING
+#undef GOOGLE_CLIENT_ID_REMOTING_HOST
+#undef GOOGLE_CLIENT_SECRET_REMOTING_HOST
#undef GOOGLE_DEFAULT_CLIENT_ID
#undef GOOGLE_DEFAULT_CLIENT_SECRET
@@ -289,6 +317,11 @@ TEST_F(GoogleAPIKeysTest, OverrideSomeKeys) {
std::string secret_remoting =
testcase::g_api_key_cache.Get().GetClientSecret(
testcase::CLIENT_REMOTING);
+ std::string id_remoting_host = testcase::g_api_key_cache.Get().GetClientID(
+ testcase::CLIENT_REMOTING_HOST);
+ std::string secret_remoting_host =
+ testcase::g_api_key_cache.Get().GetClientSecret(
+ testcase::CLIENT_REMOTING_HOST);
EXPECT_EQ("API_KEY override", api_key);
EXPECT_EQ(kDummyToken, id_main);
@@ -297,6 +330,8 @@ TEST_F(GoogleAPIKeysTest, OverrideSomeKeys) {
EXPECT_EQ(kDummyToken, secret_cloud_print);
EXPECT_EQ("CLIENT_ID_REMOTING override", id_remoting);
EXPECT_EQ(kDummyToken, secret_remoting);
+ EXPECT_EQ(kDummyToken, id_remoting_host);
+ EXPECT_EQ(kDummyToken, secret_remoting_host);
}
// Override all keys.
@@ -312,6 +347,8 @@ namespace override_all_keys {
#undef GOOGLE_CLIENT_SECRET_CLOUD_PRINT
#undef GOOGLE_CLIENT_ID_REMOTING
#undef GOOGLE_CLIENT_SECRET_REMOTING
+#undef GOOGLE_CLIENT_ID_REMOTING_HOST
+#undef GOOGLE_CLIENT_SECRET_REMOTING_HOST
#undef GOOGLE_DEFAULT_CLIENT_ID
#undef GOOGLE_DEFAULT_CLIENT_SECRET
@@ -322,6 +359,8 @@ namespace override_all_keys {
#define GOOGLE_CLIENT_SECRET_CLOUD_PRINT "SECRET_CLOUD_PRINT"
#define GOOGLE_CLIENT_ID_REMOTING "ID_REMOTING"
#define GOOGLE_CLIENT_SECRET_REMOTING "SECRET_REMOTING"
+#define GOOGLE_CLIENT_ID_REMOTING_HOST "ID_REMOTING_HOST"
+#define GOOGLE_CLIENT_SECRET_REMOTING_HOST "SECRET_REMOTING_HOST"
// Undef include guard so things get defined again, within this namespace.
#undef GOOGLE_APIS_GOOGLE_API_KEYS_H_
@@ -351,6 +390,11 @@ TEST_F(GoogleAPIKeysTest, OverrideAllKeys) {
std::string secret_remoting =
testcase::g_api_key_cache.Get().GetClientSecret(
testcase::CLIENT_REMOTING);
+ std::string id_remoting_host = testcase::g_api_key_cache.Get().GetClientID(
+ testcase::CLIENT_REMOTING_HOST);
+ std::string secret_remoting_host =
+ testcase::g_api_key_cache.Get().GetClientSecret(
+ testcase::CLIENT_REMOTING_HOST);
EXPECT_EQ("API_KEY", api_key);
EXPECT_EQ("ID_MAIN", id_main);
@@ -359,6 +403,8 @@ TEST_F(GoogleAPIKeysTest, OverrideAllKeys) {
EXPECT_EQ("SECRET_CLOUD_PRINT", secret_cloud_print);
EXPECT_EQ("ID_REMOTING", id_remoting);
EXPECT_EQ("SECRET_REMOTING", secret_remoting);
+ EXPECT_EQ("ID_REMOTING_HOST", id_remoting_host);
+ EXPECT_EQ("SECRET_REMOTING_HOST", secret_remoting_host);
}
// Override all keys using both preprocessor defines and environment
@@ -375,6 +421,8 @@ namespace override_all_keys_env {
#undef GOOGLE_CLIENT_SECRET_CLOUD_PRINT
#undef GOOGLE_CLIENT_ID_REMOTING
#undef GOOGLE_CLIENT_SECRET_REMOTING
+#undef GOOGLE_CLIENT_ID_REMOTING_HOST
+#undef GOOGLE_CLIENT_SECRET_REMOTING_HOST
#undef GOOGLE_DEFAULT_CLIENT_ID
#undef GOOGLE_DEFAULT_CLIENT_SECRET
@@ -385,6 +433,8 @@ namespace override_all_keys_env {
#define GOOGLE_CLIENT_SECRET_CLOUD_PRINT "SECRET_CLOUD_PRINT"
#define GOOGLE_CLIENT_ID_REMOTING "ID_REMOTING"
#define GOOGLE_CLIENT_SECRET_REMOTING "SECRET_REMOTING"
+#define GOOGLE_CLIENT_ID_REMOTING_HOST "ID_REMOTING_HOST"
+#define GOOGLE_CLIENT_SECRET_REMOTING_HOST "SECRET_REMOTING_HOST"
// Undef include guard so things get defined again, within this namespace.
#undef GOOGLE_APIS_GOOGLE_API_KEYS_H_
@@ -401,9 +451,11 @@ TEST_F(GoogleAPIKeysTest, OverrideAllKeysUsingEnvironment) {
env->SetVar("GOOGLE_CLIENT_ID_MAIN", "env-ID_MAIN");
env->SetVar("GOOGLE_CLIENT_ID_CLOUD_PRINT", "env-ID_CLOUD_PRINT");
env->SetVar("GOOGLE_CLIENT_ID_REMOTING", "env-ID_REMOTING");
+ env->SetVar("GOOGLE_CLIENT_ID_REMOTING_HOST", "env-ID_REMOTING_HOST");
env->SetVar("GOOGLE_CLIENT_SECRET_MAIN", "env-SECRET_MAIN");
env->SetVar("GOOGLE_CLIENT_SECRET_CLOUD_PRINT", "env-SECRET_CLOUD_PRINT");
env->SetVar("GOOGLE_CLIENT_SECRET_REMOTING", "env-SECRET_REMOTING");
+ env->SetVar("GOOGLE_CLIENT_SECRET_REMOTING_HOST", "env-SECRET_REMOTING_HOST");
EXPECT_TRUE(testcase::HasKeysConfigured());
@@ -425,6 +477,11 @@ TEST_F(GoogleAPIKeysTest, OverrideAllKeysUsingEnvironment) {
std::string secret_remoting =
testcase::g_api_key_cache.Get().GetClientSecret(
testcase::CLIENT_REMOTING);
+ std::string id_remoting_host = testcase::g_api_key_cache.Get().GetClientID(
+ testcase::CLIENT_REMOTING_HOST);
+ std::string secret_remoting_host =
+ testcase::g_api_key_cache.Get().GetClientSecret(
+ testcase::CLIENT_REMOTING_HOST);
EXPECT_EQ("env-API_KEY", api_key);
EXPECT_EQ("env-ID_MAIN", id_main);
@@ -433,6 +490,8 @@ TEST_F(GoogleAPIKeysTest, OverrideAllKeysUsingEnvironment) {
EXPECT_EQ("env-SECRET_CLOUD_PRINT", secret_cloud_print);
EXPECT_EQ("env-ID_REMOTING", id_remoting);
EXPECT_EQ("env-SECRET_REMOTING", secret_remoting);
+ EXPECT_EQ("env-ID_REMOTING_HOST", id_remoting_host);
+ EXPECT_EQ("env-SECRET_REMOTING_HOST", secret_remoting_host);
}
#endif // defined(OS_LINUX) || defined(OS_MACOSX)
diff --git a/chromium/google_apis/google_apis.gyp b/chromium/google_apis/google_apis.gyp
index c67232ffb5f..762c3695847 100644
--- a/chromium/google_apis/google_apis.gyp
+++ b/chromium/google_apis/google_apis.gyp
@@ -83,11 +83,48 @@
'gaia/oauth2_api_call_flow.h',
'gaia/oauth2_mint_token_flow.cc',
'gaia/oauth2_mint_token_flow.h',
+ 'gaia/oauth2_token_service.cc',
+ 'gaia/oauth2_token_service.h',
'google_api_keys.cc',
'google_api_keys.h',
],
# TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
'msvs_disabled_warnings': [4267, ],
},
+ {
+ 'target_name': 'google_apis_unittests',
+ 'type': 'executable',
+ 'dependencies': [
+ '../base/base.gyp:run_all_unittests',
+ '../testing/gtest.gyp:gtest',
+ 'google_apis',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'google_api_keys_unittest.cc',
+ ],
+ },
+ {
+ 'target_name': 'google_apis_test_support',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../base/base.gyp:test_support_base',
+ '../net/net.gyp:net',
+ '../net/net.gyp:net_test_support',
+ ],
+ 'export_dependent_settings': [
+ '../base/base.gyp:base',
+ '../base/base.gyp:test_support_base',
+ '../net/net.gyp:net',
+ '../net/net.gyp:net_test_support',
+ ],
+ 'sources': [
+ 'gaia/fake_gaia.cc',
+ 'gaia/fake_gaia.h',
+ ],
+ },
],
}