summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--remoting/host/setup/auth_code_getter_win.cc94
-rw-r--r--remoting/host/setup/auth_code_getter_win.h58
-rw-r--r--remoting/host/setup/host_starter.cc5
-rw-r--r--remoting/host/setup/oauth_helper.cc79
-rw-r--r--remoting/host/setup/oauth_helper.h32
-rw-r--r--remoting/host/setup/oauth_helper_unittest.cc66
-rw-r--r--remoting/remoting.gyp7
7 files changed, 338 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
diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp
index cdad93a..e19ae07 100644
--- a/remoting/remoting.gyp
+++ b/remoting/remoting.gyp
@@ -1019,9 +1019,12 @@
'variables': { 'enable_wexit_time_destructors': 1, },
'dependencies': [
'../base/base.gyp:base',
+ '../google_apis/google_apis.gyp:google_apis',
'remoting_host',
],
'sources': [
+ 'host/setup/auth_code_getter_win.cc',
+ 'host/setup/auth_code_getter_win.h',
'host/setup/daemon_controller.h',
'host/setup/daemon_controller_linux.cc',
'host/setup/daemon_controller_mac.cc',
@@ -1030,6 +1033,8 @@
'host/setup/daemon_installer_win.h',
'host/setup/host_starter.cc',
'host/setup/host_starter.h',
+ 'host/setup/oauth_helper.cc',
+ 'host/setup/oauth_helper.h',
],
'conditions': [
['OS=="win"', {
@@ -1862,6 +1867,7 @@
'remoting_host',
'remoting_jingle_glue',
'remoting_protocol',
+ 'remoting_host_setup_base',
'../base/base.gyp:base',
'../base/base.gyp:base_i18n',
'../base/base.gyp:test_support_base',
@@ -1917,6 +1923,7 @@
'host/resizing_host_observer_unittest.cc',
'host/screen_recorder_unittest.cc',
'host/server_log_entry_unittest.cc',
+ 'host/setup/oauth_helper_unittest.cc',
'host/test_key_pair.h',
'host/video_frame_capturer_helper_unittest.cc',
'host/video_frame_capturer_mac_unittest.cc',