diff options
Diffstat (limited to 'remoting/host/setup')
-rw-r--r-- | remoting/host/setup/auth_code_getter_win.cc | 94 | ||||
-rw-r--r-- | remoting/host/setup/auth_code_getter_win.h | 58 | ||||
-rw-r--r-- | remoting/host/setup/host_starter.cc | 5 | ||||
-rw-r--r-- | remoting/host/setup/oauth_helper.cc | 79 | ||||
-rw-r--r-- | remoting/host/setup/oauth_helper.h | 32 | ||||
-rw-r--r-- | remoting/host/setup/oauth_helper_unittest.cc | 66 |
6 files changed, 331 insertions, 3 deletions
diff --git a/remoting/host/setup/auth_code_getter_win.cc b/remoting/host/setup/auth_code_getter_win.cc new file mode 100644 index 0000000..88a766d --- /dev/null +++ b/remoting/host/setup/auth_code_getter_win.cc @@ -0,0 +1,94 @@ +// 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 "base/time.h" +#include "base/utf_string_conversions.h" +#include "base/win/scoped_bstr.h" +#include "base/win/scoped_variant.h" +#include "remoting/host/setup/auth_code_getter_win.h" +#include "remoting/host/setup/oauth_helper.h" + +namespace { +const int kUrlPollIntervalMs = 100; +} // namespace remoting + +namespace remoting { + +AuthCodeGetter::AuthCodeGetter() : + browser_(NULL), + browser_running_(false), + timer_interval_(base::TimeDelta::FromMilliseconds(kUrlPollIntervalMs)) { +} + +AuthCodeGetter::~AuthCodeGetter() { + KillBrowser(); +} + +void AuthCodeGetter::GetAuthCode( + base::Callback<void(const std::string&)> on_auth_code) { + if (browser_running_) { + on_auth_code.Run(""); + return; + } + on_auth_code_ = on_auth_code; + HRESULT hr = browser_.CreateInstance(CLSID_InternetExplorer, NULL, + CLSCTX_LOCAL_SERVER); + if (FAILED(hr)) { + on_auth_code_.Run(""); + return; + } + browser_running_ = true; + base::win::ScopedBstr url(UTF8ToWide(GetOauthStartUrl()).c_str()); + base::win::ScopedVariant empty_variant; + hr = browser_->Navigate(url, empty_variant.AsInput(), empty_variant.AsInput(), + empty_variant.AsInput(), empty_variant.AsInput()); + if (FAILED(hr)) { + KillBrowser(); + on_auth_code_.Run(""); + return; + } + browser_->put_Visible(VARIANT_TRUE); + StartTimer(); +} + +void AuthCodeGetter::StartTimer() { + timer_.Start(FROM_HERE, timer_interval_, this, &AuthCodeGetter::OnTimer); +} + +void AuthCodeGetter::OnTimer() { + std::string auth_code; + if (TestBrowserUrl(&auth_code)) { + on_auth_code_.Run(auth_code); + } else { + StartTimer(); + } +} + +bool AuthCodeGetter::TestBrowserUrl(std::string* auth_code) { + *auth_code = ""; + if (!browser_running_) { + return true; + } + base::win::ScopedBstr url; + HRESULT hr = browser_->get_LocationName(url.Receive()); + if (!SUCCEEDED(hr)) { + KillBrowser(); + return true; + } + *auth_code = GetOauthCodeInUrl(WideToUTF8(static_cast<BSTR>(url))); + if (!auth_code->empty()) { + KillBrowser(); + return true; + } + return false; +} + +void AuthCodeGetter::KillBrowser() { + if (browser_running_) { + browser_->Quit(); + browser_running_ = false; + } +} + +} // namespace remoting diff --git a/remoting/host/setup/auth_code_getter_win.h b/remoting/host/setup/auth_code_getter_win.h new file mode 100644 index 0000000..16fdcfd --- /dev/null +++ b/remoting/host/setup/auth_code_getter_win.h @@ -0,0 +1,58 @@ +// 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_WIN_AUTH_CODE_GETTER_WIN_H +#define REMOTING_HOST_WIN_AUTH_CODE_GETTER_WIN_H + +#include <ole2.h> +#include <exdisp.h> + +#include <string> + +#include "base/callback.h" +#include "base/threading/non_thread_safe.h" +#include "base/timer.h" +#include "base/win/scoped_comptr.h" + +namespace remoting { + +// A class for getting an OAuth authorization code. +class AuthCodeGetter : public base::NonThreadSafe { + public: + AuthCodeGetter(); + ~AuthCodeGetter(); + + // Starts a browser and navigates it to a URL that starts an Installed + // Application OAuth flow. |on_auth_code| will be called with an + // authorization code, or an empty string on error. + void GetAuthCode(base::Callback<void(const std::string&)> on_auth_code); + + private: + // Starts a timer used to poll the browser's URL. + void StartTimer(); + // Called when that timer fires. + void OnTimer(); + // Returns whether to stop polling the browser's URL. If true, then + // |auth_code| is an authorization code, or the empty string on an error. + bool TestBrowserUrl(std::string* auth_code); + // Kills the browser. + void KillBrowser(); + + // The authorization code callback. + base::Callback<void(const std::string&)> on_auth_code_; + // The browser through which the user requests an authorization code. + base::win::ScopedComPtr<IWebBrowser2, &IID_IWebBrowser2> browser_; + // Whether the browser is running. + bool browser_running_; + // A timer used to poll the browser's URL. + base::OneShotTimer<AuthCodeGetter> timer_; + // The interval at which the timer fires. + base::TimeDelta timer_interval_; + + DISALLOW_COPY_AND_ASSIGN(AuthCodeGetter); +}; + +} // namespace remoting + +#endif // REMOTING_HOST_WIN_AUTH_CODE_GETTER_WIN_H diff --git a/remoting/host/setup/host_starter.cc b/remoting/host/setup/host_starter.cc index 54de146..cb631dc 100644 --- a/remoting/host/setup/host_starter.cc +++ b/remoting/host/setup/host_starter.cc @@ -8,6 +8,7 @@ #include "crypto/random.h" #include "google_apis/google_api_keys.h" #include "remoting/host/pin_hash.h" +#include "remoting/host/setup/oauth_helper.h" namespace { @@ -70,9 +71,7 @@ HostStarter::HostStarter( google_apis::GetOAuth2ClientID(google_apis::CLIENT_REMOTING); oauth_client_info_.client_secret = google_apis::GetOAuth2ClientSecret(google_apis::CLIENT_REMOTING); - oauth_client_info_.redirect_uri = - "https://chromoting-oauth.talkgadget.google.com/talkgadget/oauth/" - "chrome-remote-desktop/rel/kgngmbheleoaphbjbaiobfdepmghbfah"; + oauth_client_info_.redirect_uri = GetOauthRedirectUrl(); } HostStarter::~HostStarter() { diff --git a/remoting/host/setup/oauth_helper.cc b/remoting/host/setup/oauth_helper.cc new file mode 100644 index 0000000..3228384 --- /dev/null +++ b/remoting/host/setup/oauth_helper.cc @@ -0,0 +1,79 @@ +// 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/setup/oauth_helper.h" + +#include "base/stringprintf.h" +#include "google_apis/google_api_keys.h" +#include "googleurl/src/url_parse.h" +#include "net/base/escape.h" + +namespace { + +std::string GetComponent(const std::string& url, + const url_parse::Component component) { + if (component.len < 0) { + return ""; + } + return url.substr(component.begin, component.len); +} + +} // namespace + +namespace remoting { + +std::string GetOauthScope() { + return + "https://www.googleapis.com/auth/chromoting " + "https://www.googleapis.com/auth/googletalk " + "https://www.googleapis.com/auth/userinfo.email "; +} + +std::string GetOauthStartUrl() { + return base::StringPrintf( + "https://accounts.google.com/o/oauth2/auth" + "?scope=%s" + "&redirect_uri=%s" + "&response_type=code" + "&client_id=%s" + "&access_type=offline" + "&approval_prompt=force", + net::EscapeUrlEncodedData(GetOauthScope(), true).c_str(), + net::EscapeUrlEncodedData(GetOauthRedirectUrl(), true).c_str(), + net::EscapeUrlEncodedData(google_apis::GetOAuth2ClientID( + google_apis::CLIENT_REMOTING), true).c_str()); +} + +std::string GetOauthRedirectUrl() { + return + "https://chromoting-oauth.talkgadget.google.com/talkgadget/oauth/" + "chrome-remote-desktop/rel/kgngmbheleoaphbjbaiobfdepmghbfah"; +} + +std::string GetOauthCodeInUrl(const std::string& url) { + url_parse::Parsed url_parsed; + ParseStandardURL(url.c_str(), url.length(), &url_parsed); + std::string redirect = GetOauthRedirectUrl(); + url_parse::Parsed redirect_parsed; + ParseStandardURL(redirect.c_str(), redirect.length(), &redirect_parsed); + if (GetComponent(url, url_parsed.scheme) != + GetComponent(redirect, redirect_parsed.scheme)) { + return ""; + } + if (GetComponent(url, url_parsed.host) != + GetComponent(redirect, redirect_parsed.host)) { + return ""; + } + url_parse::Component query = url_parsed.query; + url_parse::Component key; + url_parse::Component value; + while (ExtractQueryKeyValue(url.c_str(), &query, &key, &value)) { + if (GetComponent(url, key) == "code") { + return GetComponent(url, value); + } + } + return ""; +} + +} // namespace remoting diff --git a/remoting/host/setup/oauth_helper.h b/remoting/host/setup/oauth_helper.h new file mode 100644 index 0000000..a762c23 --- /dev/null +++ b/remoting/host/setup/oauth_helper.h @@ -0,0 +1,32 @@ +// 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_SETUP_OAUTH_HELPER_H +#define REMOTING_HOST_SETUP_OAUTH_HELPER_H + +#include <string> + +namespace remoting { + +// Gets the OAuth scope of the host's refresh token. +std::string GetOauthScope(); + +// Gets a URL at which the OAuth dance starts. +std::string GetOauthStartUrl(); + +// Gets a redirect URL for the OAuth dance. +std::string GetOauthRedirectUrl(); + +// Returns the OAuth authorization code embedded in a URL, or the empty string +// if there is no such code. +// To get an OAuth authorization code, (i) start a browser, (ii) navigate it +// to |GetOauthStartUrl()|, (iii) ask the user to sign on to their account, +// and grant the requested permissions, (iv) monitor the URLs that the browser +// shows, passing each one to |GetOauthCodeInUrl()|, until that function returns +// a non-empty string. That string is the authorization code. +std::string GetOauthCodeInUrl(const std::string& url); + +} // namespace remoting + +#endif // REMOTING_HOST_SETUP_OAUTH_HELPER_H diff --git a/remoting/host/setup/oauth_helper_unittest.cc b/remoting/host/setup/oauth_helper_unittest.cc new file mode 100644 index 0000000..aff85d1 --- /dev/null +++ b/remoting/host/setup/oauth_helper_unittest.cc @@ -0,0 +1,66 @@ +// 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/setup/oauth_helper.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +std::string Replace(const std::string& s, const std::string& old_substr, + const std::string& new_substr) { + size_t pos = s.find(old_substr); + if (pos == std::string::npos) { + return s; + } + return s.substr(0, pos) + new_substr + + s.substr(pos + old_substr.length(), std::string::npos); +} + +} // namespace + +namespace remoting { + +TEST(OauthHelperTest, TestNotCode) { + ASSERT_EQ("", GetOauthCodeInUrl("notURL")); +} + +TEST(OauthHelperTest, TestVeryShort) { + ASSERT_EQ("", GetOauthCodeInUrl(GetOauthRedirectUrl())); +} + +TEST(OauthHelperTest, TestEmptyQuery) { + ASSERT_EQ("", GetOauthCodeInUrl(GetOauthRedirectUrl() + "?")); +} + +TEST(OauthHelperTest, TestNoQueryValue) { + ASSERT_EQ("", GetOauthCodeInUrl(GetOauthRedirectUrl() + "?code")); +} + +TEST(OauthHelperTest, TestEmptyCode) { + ASSERT_EQ("", GetOauthCodeInUrl(GetOauthRedirectUrl() + "?code=")); +} + +TEST(OauthHelperTest, TestCode) { + ASSERT_EQ("Dummy", GetOauthCodeInUrl(GetOauthRedirectUrl() + "?code=Dummy")); +} + +TEST(OauthHelperTest, TestCodeInLongQuery) { + ASSERT_EQ("Dummy", GetOauthCodeInUrl(GetOauthRedirectUrl() + + "?x=1&code=Dummy&y=2")); +} + +TEST(OauthHelperTest, TestBadScheme) { + std::string url = GetOauthRedirectUrl() + "?code=Dummy"; + url = Replace(url, "https:", "http"); + ASSERT_EQ("", GetOauthCodeInUrl(url)); +} + +TEST(OauthHelperTest, TestBadHost) { + std::string url = GetOauthRedirectUrl() + "?code=Dummy"; + url = Replace(url, "google", "goggle"); + ASSERT_EQ("", GetOauthCodeInUrl(url)); +} + +} // namespace remoting |