From 3014321f5622f30e8e7f88fdb55bcc0e612b4f65 Mon Sep 17 00:00:00 2001 From: "mnissler@chromium.org" Date: Thu, 19 Sep 2013 11:21:12 +0000 Subject: Add a fake GAIA implementation for use with the test server. This is for the benefit of integration tests that need the browser to interface with GAIA and friends, such as for OAuth2 token minting. BUG=chromium:243889 R=joi@chromium.org, xiyuan@chromium.org, zelidrag@chromium.org Review URL: https://codereview.chromium.org/24172005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@224108 0039d316-1c4b-4281-b951-d872f2087c98 --- google_apis/gaia/fake_gaia.cc | 143 ++++++++++++++++++++++++++++++++++++ google_apis/gaia/fake_gaia.h | 86 ++++++++++++++++++++++ google_apis/gaia/gaia_urls.cc | 6 ++ google_apis/gaia/gaia_urls.h | 2 + google_apis/google_apis.gyp | 20 +++++ google_apis/test/service_login.html | 74 +++++++++++++++++++ 6 files changed, 331 insertions(+) create mode 100644 google_apis/gaia/fake_gaia.cc create mode 100644 google_apis/gaia/fake_gaia.h create mode 100644 google_apis/test/service_login.html (limited to 'google_apis') diff --git a/google_apis/gaia/fake_gaia.cc b/google_apis/gaia/fake_gaia.cc new file mode 100644 index 0000000..9f4a812 --- /dev/null +++ b/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 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 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( + ""); + 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 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(); + } + + return http_response.PassAs(); +} + +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/google_apis/gaia/fake_gaia.h b/google_apis/gaia/fake_gaia.h new file mode 100644 index 0000000..24f6891 --- /dev/null +++ b/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 +#include +#include + +#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 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 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 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/google_apis/gaia/gaia_urls.cc b/google_apis/gaia/gaia_urls.cc index f976f6c..e5e5b8f 100644 --- a/google_apis/gaia/gaia_urls.cc +++ b/google_apis/gaia/gaia_urls.cc @@ -18,6 +18,7 @@ const char kDefaultGoogleApisBaseUrl[] = "https://www.googleapis.com"; // API calls from accounts.google.com const char kClientLoginUrlSuffix[] = "ClientLogin"; const char kServiceLoginUrlSuffix[] = "ServiceLogin"; +const char kServiceLoginAuthUrlSuffix[] = "ServiceLoginAuth"; const char kServiceLogoutUrlSuffix[] = "Logout"; const char kIssueAuthTokenUrlSuffix[] = "IssueAuthToken"; const char kGetUserInfoUrlSuffix[] = "GetUserInfo"; @@ -92,6 +93,7 @@ GaiaUrls::GaiaUrls() { // URLs from accounts.google.com. 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); @@ -149,6 +151,10 @@ const GURL& GaiaUrls::service_login_url() const { return service_login_url_; } +const GURL& GaiaUrls::service_login_auth_url() const { + return service_login_auth_url_; +} + const GURL& GaiaUrls::service_logout_url() const { return service_logout_url_; } diff --git a/google_apis/gaia/gaia_urls.h b/google_apis/gaia/gaia_urls.h index 06014d2..e06b95d 100644 --- a/google_apis/gaia/gaia_urls.h +++ b/google_apis/gaia/gaia_urls.h @@ -20,6 +20,7 @@ class GaiaUrls { 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; @@ -60,6 +61,7 @@ class GaiaUrls { 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_; diff --git a/google_apis/google_apis.gyp b/google_apis/google_apis.gyp index 3479567..762c3695 100644 --- a/google_apis/google_apis.gyp +++ b/google_apis/google_apis.gyp @@ -106,5 +106,25 @@ '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', + ], + }, ], } diff --git a/google_apis/test/service_login.html b/google_apis/test/service_login.html new file mode 100644 index 0000000..d605707 --- /dev/null +++ b/google_apis/test/service_login.html @@ -0,0 +1,74 @@ + + + + + + Local Auth Server:
+
+ + + + +
+ + -- cgit v1.1