diff options
author | jamiewalch@google.com <jamiewalch@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-16 21:53:51 +0000 |
---|---|---|
committer | jamiewalch@google.com <jamiewalch@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-16 21:53:51 +0000 |
commit | 53021f822333a6e12a552b37a0e82b41bc85c867 (patch) | |
tree | 38278a89a6c379b286c8077ac40e4951796736d1 /remoting | |
parent | 90f53ef834d187fe71d4deb102c822475295aa8a (diff) | |
download | chromium_src-53021f822333a6e12a552b37a0e82b41bc85c867.zip chromium_src-53021f822333a6e12a552b37a0e82b41bc85c867.tar.gz chromium_src-53021f822333a6e12a552b37a0e82b41bc85c867.tar.bz2 |
Added helper classes to allow OAuth to be used in Me2Me.
These classes are heavily based on equivalents under chrome/service, although
they're somewhat simpler.
This is part of the groundwork needed to let us use OAuth instead of Client
Login for Me2Me. The web-app can present a refresh token, and these classes
will allow us to turn it into an access token on-demand.
BUG=None
TEST=None
Review URL: https://chromiumcodereview.appspot.com/9677027
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@127260 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting')
-rw-r--r-- | remoting/host/DEPS | 5 | ||||
-rw-r--r-- | remoting/host/gaia_oauth_client.cc | 207 | ||||
-rw-r--r-- | remoting/host/gaia_oauth_client.h | 78 | ||||
-rw-r--r-- | remoting/host/oauth_client.cc | 44 | ||||
-rw-r--r-- | remoting/host/oauth_client.h | 40 | ||||
-rw-r--r-- | remoting/host/url_request_context.cc | 73 | ||||
-rw-r--r-- | remoting/host/url_request_context.h | 53 | ||||
-rw-r--r-- | remoting/remoting.gyp | 7 |
8 files changed, 507 insertions, 0 deletions
diff --git a/remoting/host/DEPS b/remoting/host/DEPS index bc54a47..c84c993 100644 --- a/remoting/host/DEPS +++ b/remoting/host/DEPS @@ -1,7 +1,12 @@ include_rules = [ "+ui", "+base/crypto", + "+content/public/common", "+net/base", + "+net/ftp", + "+net/http", + "+net/proxy", + "+net/url_request", "+remoting/protocol", "+remoting/jingle_glue", diff --git a/remoting/host/gaia_oauth_client.cc b/remoting/host/gaia_oauth_client.cc new file mode 100644 index 0000000..8b2844b --- /dev/null +++ b/remoting/host/gaia_oauth_client.cc @@ -0,0 +1,207 @@ +// Copyright (c) 2012 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 "remoting/host/gaia_oauth_client.h" + +#include "base/json/json_reader.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/values.h" +#include "content/public/common/url_fetcher.h" +#include "content/public/common/url_fetcher_delegate.h" +#include "googleurl/src/gurl.h" +#include "net/base/escape.h" +#include "net/http/http_status_code.h" +#include "net/url_request/url_request_context_getter.h" + +namespace { +const char kAccessTokenValue[] = "access_token"; +const char kRefreshTokenValue[] = "refresh_token"; +const char kExpiresInValue[] = "expires_in"; +} // namespace + +namespace remoting { + +class GaiaOAuthClient::Core + : public base::RefCountedThreadSafe<GaiaOAuthClient::Core>, + public content::URLFetcherDelegate { + public: + Core(const std::string& gaia_url, + net::URLRequestContextGetter* request_context_getter) + : gaia_url_(gaia_url), + num_retries_(0), + request_context_getter_(request_context_getter), + delegate_(NULL) { } + + virtual ~Core() { } + + void GetTokensFromAuthCode(const OAuthClientInfo& oauth_client_info, + const std::string& auth_code, + int max_retries, + GaiaOAuthClient::Delegate* delegate); + void RefreshToken(const OAuthClientInfo& oauth_client_info, + const std::string& refresh_token, + int max_retries, + GaiaOAuthClient::Delegate* delegate); + + // content::URLFetcherDelegate implementation. + virtual void OnURLFetchComplete(const content::URLFetcher* source); + + private: + void MakeGaiaRequest(std::string post_body, + int max_retries, + GaiaOAuthClient::Delegate* delegate); + void HandleResponse(const content::URLFetcher* source, + bool* should_retry_request); + + GURL gaia_url_; + int num_retries_; + scoped_refptr<net::URLRequestContextGetter> request_context_getter_; + GaiaOAuthClient::Delegate* delegate_; + scoped_ptr<content::URLFetcher> request_; +}; + +void GaiaOAuthClient::Core::GetTokensFromAuthCode( + const OAuthClientInfo& oauth_client_info, + const std::string& auth_code, + int max_retries, + GaiaOAuthClient::Delegate* delegate) { + std::string post_body = + "code=" + net::EscapeUrlEncodedData(auth_code, true) + + "&client_id=" + net::EscapeUrlEncodedData(oauth_client_info.client_id, + true) + + "&client_secret=" + + net::EscapeUrlEncodedData(oauth_client_info.client_secret, true) + + "&redirect_uri=oob&grant_type=authorization_code"; + MakeGaiaRequest(post_body, max_retries, delegate); +} + +void GaiaOAuthClient::Core::RefreshToken( + const OAuthClientInfo& oauth_client_info, + const std::string& refresh_token, + int max_retries, + GaiaOAuthClient::Delegate* delegate) { + std::string post_body = + "refresh_token=" + net::EscapeUrlEncodedData(refresh_token, true) + + "&client_id=" + net::EscapeUrlEncodedData(oauth_client_info.client_id, + true) + + "&client_secret=" + + net::EscapeUrlEncodedData(oauth_client_info.client_secret, true) + + "&grant_type=refresh_token"; + MakeGaiaRequest(post_body, max_retries, delegate); +} + +void GaiaOAuthClient::Core::MakeGaiaRequest( + std::string post_body, + int max_retries, + GaiaOAuthClient::Delegate* delegate) { + DCHECK(!request_.get()) << "Tried to fetch two things at once!"; + delegate_ = delegate; + num_retries_ = 0; + request_.reset(content::URLFetcher::Create( + 0, gaia_url_, content::URLFetcher::POST, this)); + request_->SetRequestContext(request_context_getter_); + request_->SetUploadData("application/x-www-form-urlencoded", post_body); + request_->SetMaxRetries(max_retries); + request_->Start(); +} + +// URLFetcher::Delegate implementation. +void GaiaOAuthClient::Core::OnURLFetchComplete( + const content::URLFetcher* source) { + bool should_retry = false; + HandleResponse(source, &should_retry); + if (should_retry) { + // Explicitly call ReceivedContentWasMalformed() to ensure the current + // request gets counted as a failure for calculation of the back-off + // period. If it was already a failure by status code, this call will + // be ignored. + request_->ReceivedContentWasMalformed(); + num_retries_++; + // We must set our request_context_getter_ again because + // URLFetcher::Core::RetryOrCompleteUrlFetch resets it to NULL... + request_->SetRequestContext(request_context_getter_); + request_->Start(); + } else { + request_.reset(); + } +} + +void GaiaOAuthClient::Core::HandleResponse( + const content::URLFetcher* source, + bool* should_retry_request) { + *should_retry_request = false; + // RC_BAD_REQUEST means the arguments are invalid. No point retrying. We are + // done here. + if (source->GetResponseCode() == net::HTTP_BAD_REQUEST) { + delegate_->OnOAuthError(); + return; + } + std::string access_token; + std::string refresh_token; + int expires_in_seconds = 0; + if (source->GetResponseCode() == net::HTTP_OK) { + std::string data; + source->GetResponseAsString(&data); + scoped_ptr<Value> message_value(base::JSONReader::Read(data, false)); + if (message_value.get() && + message_value->IsType(Value::TYPE_DICTIONARY)) { + scoped_ptr<DictionaryValue> response_dict( + static_cast<DictionaryValue*>(message_value.release())); + response_dict->GetString(kAccessTokenValue, &access_token); + response_dict->GetString(kRefreshTokenValue, &refresh_token); + response_dict->GetInteger(kExpiresInValue, &expires_in_seconds); + } + } + if (access_token.empty()) { + // If we don't have an access token yet and the the error was not + // RC_BAD_REQUEST, we may need to retry. + if ((-1 != source->GetMaxRetries()) && + (num_retries_ > source->GetMaxRetries())) { + // Retry limit reached. Give up. + delegate_->OnNetworkError(source->GetResponseCode()); + } else { + *should_retry_request = true; + } + } else if (refresh_token.empty()) { + // If we only have an access token, then this was a refresh request. + delegate_->OnRefreshTokenResponse(access_token, expires_in_seconds); + } else { + delegate_->OnGetTokensResponse(refresh_token, + access_token, + expires_in_seconds); + } +} + +GaiaOAuthClient::GaiaOAuthClient(const std::string& gaia_url, + net::URLRequestContextGetter* context_getter) { + core_ = new Core(gaia_url, context_getter); +} + +GaiaOAuthClient::~GaiaOAuthClient() { +} + + +void GaiaOAuthClient::GetTokensFromAuthCode( + const OAuthClientInfo& oauth_client_info, + const std::string& auth_code, + int max_retries, + Delegate* delegate) { + return core_->GetTokensFromAuthCode(oauth_client_info, + auth_code, + max_retries, + delegate); +} + +void GaiaOAuthClient::RefreshToken(const OAuthClientInfo& oauth_client_info, + const std::string& refresh_token, + int max_retries, + Delegate* delegate) { + return core_->RefreshToken(oauth_client_info, + refresh_token, + max_retries, + delegate); +} + +} // namespace remoting diff --git a/remoting/host/gaia_oauth_client.h b/remoting/host/gaia_oauth_client.h new file mode 100644 index 0000000..f893037 --- /dev/null +++ b/remoting/host/gaia_oauth_client.h @@ -0,0 +1,78 @@ +// Copyright (c) 2012 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 REMOTING_HOST_GAIA_OAUTH_CLIENT_H_ +#define REMOTING_HOST_GAIA_OAUTH_CLIENT_H_ + +#include <string> + +#include "base/memory/ref_counted.h" +#include "base/message_loop_proxy.h" + +namespace net { +class URLRequestContextGetter; +} // namespace net + +// A helper class to get and refresh OAuth tokens given an authorization code. +// +// TODO(jamiewalch): This is copied from chrome/common/net to avoid a dependency +// on chrome. It would be better for this class to be moved into net to avoid +// this duplication. +namespace remoting { + +// TODO(jamiewalch): Make this configurable if we ever support other providers. +static const char kGaiaOAuth2Url[] = + "https://accounts.google.com/o/oauth2/token"; + +struct OAuthClientInfo { + std::string client_id; + std::string client_secret; +}; + +class GaiaOAuthClient { + public: + class Delegate { + public: + virtual ~Delegate() { } + + // Invoked on a successful response to the GetTokensFromAuthCode request. + virtual void OnGetTokensResponse(const std::string& refresh_token, + const std::string& access_token, + int expires_in_seconds) = 0; + // Invoked on a successful response to the RefreshToken request. + virtual void OnRefreshTokenResponse(const std::string& access_token, + int expires_in_seconds) = 0; + // Invoked when there is an OAuth error with one of the requests. + virtual void OnOAuthError() = 0; + // Invoked when there is a network error or upon receiving an invalid + // response. This is invoked when the maximum number of retries have been + // exhausted. If max_retries is -1, this is never invoked. + virtual void OnNetworkError(int response_code) = 0; + }; + GaiaOAuthClient(const std::string& gaia_url, + net::URLRequestContextGetter* context_getter); + ~GaiaOAuthClient(); + + // In the below methods, |max_retries| specifies the maximum number of times + // we should retry on a network error in invalid response. This does not + // apply in the case of an OAuth error (i.e. there was something wrong with + // the input arguments). Setting |max_retries| to -1 implies infinite retries. + void GetTokensFromAuthCode(const OAuthClientInfo& oauth_client_info, + const std::string& auth_code, + int max_retries, + Delegate* delegate); + void RefreshToken(const OAuthClientInfo& oauth_client_info, + const std::string& refresh_token, + int max_retries, + Delegate* delegate); + + private: + // The guts of the implementation live in this class. + class Core; + scoped_refptr<Core> core_; + DISALLOW_COPY_AND_ASSIGN(GaiaOAuthClient); +}; +} // namespace remoting + +#endif // CHROME_COMMON_NET_GAIA_GAIA_OAUTH_CLIENT_H_ diff --git a/remoting/host/oauth_client.cc b/remoting/host/oauth_client.cc new file mode 100644 index 0000000..342b0eb --- /dev/null +++ b/remoting/host/oauth_client.cc @@ -0,0 +1,44 @@ +// Copyright (c) 2012 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 "remoting/host/oauth_client.h" + +#include "remoting/host/chromoting_host_context.h" +#include "remoting/host/url_request_context.h" + +namespace remoting { + +OAuthClient::OAuthClient() + : network_thread_("OAuthNetworkThread"), + file_thread_("OAuthFileThread") { + network_thread_.StartWithOptions( + base::Thread::Options(MessageLoop::TYPE_IO, 0)); + file_thread_.StartWithOptions( + base::Thread::Options(MessageLoop::TYPE_IO, 0)); + url_request_context_getter_.reset(new URLRequestContextGetter( + network_thread_.message_loop(), file_thread_.message_loop())); + gaia_oauth_client_.reset( + new GaiaOAuthClient(kGaiaOAuth2Url, url_request_context_getter_.get())); +} + +OAuthClient::~OAuthClient() { +} + +void OAuthClient::GetAccessToken(const std::string& refresh_token, + GaiaOAuthClient::Delegate* delegate) { +#ifdef OFFICIAL_BUILD + OAuthClientInfo client_info = { + "440925447803-avn2sj1kc099s0r7v62je5s339mu0am1.apps.googleusercontent.com", + "Bgur6DFiOMM1h8x-AQpuTQlK" + }; +#else + OAuthClientInfo client_info = { + "440925447803-2pi3v45bff6tp1rde2f7q6lgbor3o5uj.apps.googleusercontent.com", + "W2ieEsG-R1gIA4MMurGrgMc_" + }; +#endif + gaia_oauth_client_->RefreshToken(client_info, refresh_token, -1, delegate); +} + +} // namespace remoting diff --git a/remoting/host/oauth_client.h b/remoting/host/oauth_client.h new file mode 100644 index 0000000..c377733 --- /dev/null +++ b/remoting/host/oauth_client.h @@ -0,0 +1,40 @@ +// Copyright (c) 2012 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. +// +// This class implements the OAuth2 client for the Chromoting host. + +#ifndef REMOTING_HOST_OAUTH_CLIENT_H_ +#define REMOTING_HOST_OAUTH_CLIENT_H_ + +#include <string> + +#include "base/memory/scoped_ptr.h" +#include "base/threading/thread.h" +#include "remoting/host/gaia_oauth_client.h" +#include "remoting/host/url_request_context.h" + +namespace remoting { + +class OAuthClient { + public: + OAuthClient(); + ~OAuthClient(); + + void GetAccessToken(const std::string& refresh_token, + GaiaOAuthClient::Delegate* delegate); + + private: + // TODO(jamiewalch): Move these to the ChromotingHostContext class so + // that the URLRequestContextGetter is available for other purposes. + base::Thread network_thread_; + base::Thread file_thread_; + scoped_ptr<URLRequestContextGetter> url_request_context_getter_; + scoped_ptr<GaiaOAuthClient> gaia_oauth_client_; + + DISALLOW_COPY_AND_ASSIGN(OAuthClient); +}; + +} // namespace remoting + +#endif // REMOTING_HOST_OAUTH_CLIENT_H_ diff --git a/remoting/host/url_request_context.cc b/remoting/host/url_request_context.cc new file mode 100644 index 0000000..4ba46ff --- /dev/null +++ b/remoting/host/url_request_context.cc @@ -0,0 +1,73 @@ +// Copyright (c) 2012 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 "remoting/host/url_request_context.h" + +#include "base/message_loop_proxy.h" +#include "net/base/cert_verifier.h" +#include "net/base/host_resolver.h" +#include "net/base/ssl_config_service_defaults.h" +#include "net/http/http_auth_handler_factory.h" +#include "net/http/http_network_layer.h" +#include "net/http/http_network_session.h" +#include "net/http/http_server_properties_impl.h" +#include "net/proxy/proxy_config_service.h" +#include "net/proxy/proxy_service.h" + +namespace remoting { + +// TODO(willchan): This is largely copied from service_url_request_context.cc, +// which is in turn copied from some test code. Move it somewhere reusable. +URLRequestContext::URLRequestContext( + net::ProxyConfigService* proxy_config_service) + : ALLOW_THIS_IN_INITIALIZER_LIST(storage_(this)) { + storage_.set_host_resolver( + net::CreateSystemHostResolver(net::HostResolver::kDefaultParallelism, + net::HostResolver::kDefaultRetryAttempts, + NULL)); + storage_.set_proxy_service(net::ProxyService::CreateUsingSystemProxyResolver( + proxy_config_service, 0u, NULL)); + storage_.set_cert_verifier(new net::CertVerifier); + storage_.set_ssl_config_service(new net::SSLConfigServiceDefaults); + storage_.set_http_auth_handler_factory( + net::HttpAuthHandlerFactory::CreateDefault(host_resolver())); + storage_.set_http_server_properties(new net::HttpServerPropertiesImpl); + + net::HttpNetworkSession::Params session_params; + session_params.host_resolver = host_resolver(); + session_params.cert_verifier = cert_verifier(); + session_params.proxy_service = proxy_service(); + session_params.ssl_config_service = ssl_config_service(); + session_params.http_auth_handler_factory = http_auth_handler_factory(); + session_params.http_server_properties = http_server_properties(); + scoped_refptr<net::HttpNetworkSession> network_session( + new net::HttpNetworkSession(session_params)); + storage_.set_http_transaction_factory( + new net::HttpNetworkLayer(network_session)); +} + +URLRequestContextGetter::URLRequestContextGetter( + MessageLoop* io_message_loop, + MessageLoop* file_message_loop) + : io_message_loop_proxy_(io_message_loop->message_loop_proxy()) { + proxy_config_service_.reset( + net::ProxyService::CreateSystemProxyConfigService( + io_message_loop, file_message_loop)); +} + +net::URLRequestContext* URLRequestContextGetter::GetURLRequestContext() { + if (!url_request_context_) + url_request_context_ = + new URLRequestContext(proxy_config_service_.get()); + return url_request_context_; +} + +scoped_refptr<base::MessageLoopProxy> +URLRequestContextGetter::GetIOMessageLoopProxy() const { + return io_message_loop_proxy_; +} + +URLRequestContextGetter::~URLRequestContextGetter() {} + +} // namespace remoting diff --git a/remoting/host/url_request_context.h b/remoting/host/url_request_context.h new file mode 100644 index 0000000..c8aaeaf --- /dev/null +++ b/remoting/host/url_request_context.h @@ -0,0 +1,53 @@ +// Copyright (c) 2012 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 REMOTING_HOST_URL_REQUEST_CONTEXT_H_ +#define REMOTING_HOST_URL_REQUEST_CONTEXT_H_ + +#include <string> + +#include "base/memory/scoped_ptr.h" +#include "base/message_loop.h" +#include "net/proxy/proxy_config_service.h" +#include "net/url_request/url_request_context.h" +#include "net/url_request/url_request_context_getter.h" +#include "net/url_request/url_request_context_storage.h" + +namespace base { +class MessageLoopProxy; +} // namespace base + +namespace remoting { + +// Subclass of net::URLRequestContext which can be used to store extra +// information for requests. This subclass is meant to be used in the +// remoting Me2Me host process where the profile is not available. +class URLRequestContext : public net::URLRequestContext { + public: + explicit URLRequestContext(net::ProxyConfigService* net_proxy_config_service); + + private: + net::URLRequestContextStorage storage_; +}; + +class URLRequestContextGetter : public net::URLRequestContextGetter { + public: + URLRequestContextGetter(MessageLoop* io_message_loop, + MessageLoop* file_message_loop); + virtual ~URLRequestContextGetter(); + + // Overridden from net::URLRequestContextGetter: + virtual net::URLRequestContext* GetURLRequestContext() OVERRIDE; + virtual scoped_refptr<base::MessageLoopProxy> + GetIOMessageLoopProxy() const OVERRIDE; + + private: + scoped_refptr<net::URLRequestContext> url_request_context_; + scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_; + scoped_ptr<net::ProxyConfigService> proxy_config_service_; +}; + +} // namespace remoting + +#endif // CHROME_SERVICE_NET_SERVICE_URL_REQUEST_CONTEXT_H_ diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp index c3ce7f6..5f959ec 100644 --- a/remoting/remoting.gyp +++ b/remoting/remoting.gyp @@ -505,6 +505,7 @@ 'remoting_protocol', 'differ_block', '../crypto/crypto.gyp:crypto', + '../content/content.gyp:content_common' ], 'sources': [ 'host/capturer.h', @@ -546,6 +547,8 @@ 'host/event_executor_win.cc', 'host/heartbeat_sender.cc', 'host/heartbeat_sender.h', + 'host/gaia_oauth_client.cc', + 'host/gaia_oauth_client.h', 'host/host_config.cc', 'host/host_config.h', 'host/host_key_pair.cc', @@ -567,6 +570,8 @@ 'host/local_input_monitor_win.cc', 'host/log_to_server.cc', 'host/log_to_server.h', + 'host/oauth_client.cc', + 'host/oauth_client.h', 'host/policy_hack/nat_policy.h', 'host/policy_hack/nat_policy.cc', 'host/policy_hack/nat_policy_linux.cc', @@ -582,6 +587,8 @@ 'host/signaling_connector.h', 'host/ui_strings.cc', 'host/ui_strings.h', + 'host/url_request_context.cc', + 'host/url_request_context.h', 'host/usb_keycode_map.h', 'host/user_authenticator.h', 'host/user_authenticator_linux.cc', |