| Index: google_apis/gaia/fake_gaia.cc
|
| diff --git a/google_apis/gaia/fake_gaia.cc b/google_apis/gaia/fake_gaia.cc
|
| index 7474aca80a9b0c0f184869f1295b13b1a6142fb0..9eddd85af6ba0d109863ce9b089e51355868e7c6 100644
|
| --- a/google_apis/gaia/fake_gaia.cc
|
| +++ b/google_apis/gaia/fake_gaia.cc
|
| @@ -7,6 +7,8 @@
|
| #include <vector>
|
|
|
| #include "base/base_paths.h"
|
| +#include "base/bind.h"
|
| +#include "base/bind_helpers.h"
|
| #include "base/file_util.h"
|
| #include "base/files/file_path.h"
|
| #include "base/json/json_writer.h"
|
| @@ -15,6 +17,7 @@
|
| #include "base/strings/string_number_conversions.h"
|
| #include "base/strings/string_split.h"
|
| #include "base/strings/string_util.h"
|
| +#include "base/strings/stringprintf.h"
|
| #include "base/values.h"
|
| #include "google_apis/gaia/gaia_urls.h"
|
| #include "net/base/url_util.h"
|
| @@ -23,11 +26,25 @@
|
| #include "net/test/embedded_test_server/http_response.h"
|
| #include "url/url_parse.h"
|
|
|
| +#define REGISTER_RESPONSE_HANDLER(url, method) \
|
| + request_handlers_.insert(std::make_pair( \
|
| + url.path(), base::Bind(&FakeGaia::method, base::Unretained(this))))
|
| +
|
| +#define REGISTER_PATH_RESPONSE_HANDLER(path, method) \
|
| + request_handlers_.insert(std::make_pair( \
|
| + path, base::Bind(&FakeGaia::method, base::Unretained(this))))
|
| +
|
| using namespace net::test_server;
|
|
|
| namespace {
|
| +
|
| const base::FilePath::CharType kServiceLogin[] =
|
| FILE_PATH_LITERAL("google_apis/test/service_login.html");
|
| +
|
| +// OAuth2 Authentication header value prefix.
|
| +const char kAuthHeaderBearer[] = "Bearer ";
|
| +const char kAuthHeaderOAuth[] = "OAuth ";
|
| +
|
| }
|
|
|
| FakeGaia::AccessTokenInfo::AccessTokenInfo()
|
| @@ -45,113 +62,310 @@ FakeGaia::FakeGaia() {
|
|
|
| FakeGaia::~FakeGaia() {}
|
|
|
| -scoped_ptr<HttpResponse> FakeGaia::HandleRequest(const HttpRequest& request) {
|
| +void FakeGaia::SetAuthTokens(const std::string& auth_code,
|
| + const std::string& refresh_token,
|
| + const std::string& access_token,
|
| + const std::string& gaia_uber_token,
|
| + const std::string& session_sid_cookie,
|
| + const std::string& session_lsid_cookie) {
|
| + fake_auth_code_ = auth_code;
|
| + fake_refresh_token_ = refresh_token;
|
| + fake_access_token_ = access_token;
|
| + fake_gaia_uber_token_ = gaia_uber_token;
|
| + fake_session_sid_cookie_ = session_sid_cookie;
|
| + fake_session_lsid_cookie_ = session_lsid_cookie;
|
| +}
|
| +
|
| +void FakeGaia::Initialize() {
|
| GaiaUrls* gaia_urls = GaiaUrls::GetInstance();
|
| + // Handles /ServiceLogin GAIA call.
|
| + REGISTER_RESPONSE_HANDLER(
|
| + gaia_urls->service_login_url(), HandleServiceLogin);
|
| +
|
| + // Handles /ServiceLoginAuth GAIA call.
|
| + REGISTER_RESPONSE_HANDLER(
|
| + gaia_urls->service_login_auth_url(), HandleServiceLoginAuth);
|
| +
|
| + // Handles /o/oauth2/programmatic_auth GAIA call.
|
| + REGISTER_RESPONSE_HANDLER(
|
| + gaia_urls->client_login_to_oauth2_url(), HandleProgramaticAuth);
|
| +
|
| + // Handles /o/oauth2/token GAIA call.
|
| + REGISTER_RESPONSE_HANDLER(
|
| + gaia_urls->oauth2_token_url(), HandleAuthToken);
|
| +
|
| + // Handles /OAuthLogin GAIA call.
|
| + REGISTER_RESPONSE_HANDLER(
|
| + gaia_urls->oauth1_login_url(), HandleOAuthLogin);
|
| +
|
| + // Handles /MergeSession GAIA call.
|
| + REGISTER_RESPONSE_HANDLER(
|
| + gaia_urls->merge_session_url(), HandleMergeSession);
|
| +
|
| + // Handles /oauth2/v2/IssueToken GAIA call.
|
| + REGISTER_RESPONSE_HANDLER(
|
| + gaia_urls->oauth2_issue_token_url(), HandleIssueToken);
|
| +
|
| + // Handles /oauth2/v2/tokeninfo GAIA call.
|
| + REGISTER_RESPONSE_HANDLER(
|
| + gaia_urls->oauth2_token_info_url(), HandleTokenInfo);
|
| +
|
| + // Handles /SSO GAIA call (not GAIA, made up for SAML tests).
|
| + REGISTER_PATH_RESPONSE_HANDLER("/SSO", HandleSSO);
|
| +}
|
| +
|
| +void FakeGaia::HandleProgramaticAuth(
|
| + const HttpRequest& request,
|
| + BasicHttpResponse* http_response) {
|
| + GaiaUrls* gaia_urls = GaiaUrls::GetInstance();
|
| + std::string scope;
|
| + if (!GetQueryParameter(request.content, "scope", &scope) ||
|
| + gaia_urls->oauth1_login_scope() != scope) {
|
| + http_response->set_code(net::HTTP_BAD_REQUEST);
|
| + return;
|
| + }
|
| +
|
| + std::string client_id;
|
| + if (!GetQueryParameter(request.content, "client_id", &client_id) ||
|
| + gaia_urls->oauth2_chrome_client_id() != client_id) {
|
| + http_response->set_code(net::HTTP_BAD_REQUEST);
|
| + return;
|
| + }
|
| +
|
| + http_response->AddCustomHeader(
|
| + "Set-Cookie",
|
| + base::StringPrintf(
|
| + "oauth_code=%s; Path=/o/GetOAuth2Token; Secure; HttpOnly;",
|
| + fake_auth_code_.c_str()));
|
| + http_response->set_code(net::HTTP_OK);
|
| + http_response->set_content_type("text/html");
|
| +}
|
| +
|
| +void FakeGaia::HandleServiceLogin(const HttpRequest& request,
|
| + BasicHttpResponse* http_response) {
|
| + http_response->set_code(net::HTTP_OK);
|
| + http_response->set_content(service_login_response_);
|
| + http_response->set_content_type("text/html");
|
| +}
|
| +
|
| +void FakeGaia::HandleOAuthLogin(const HttpRequest& request,
|
| + BasicHttpResponse* http_response) {
|
| + http_response->set_code(net::HTTP_BAD_REQUEST);
|
| + std::string access_token;
|
| + if (!GetAccessToken(request, kAuthHeaderOAuth, &access_token)) {
|
| + LOG(ERROR) << "/OAuthLogin missing access token in the header";
|
| + return;
|
| + }
|
|
|
| - // 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();
|
| + std::string request_query = request_url.query();
|
|
|
| - scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse());
|
| - if (request_path == gaia_urls->service_login_url().path()) {
|
| + std::string source;
|
| + if (!GetQueryParameter(request_query, "source", &source)) {
|
| + LOG(ERROR) << "Missing 'source' param in /OAuthLogin call";
|
| + return;
|
| + }
|
| +
|
| + std::string issue_uberauth;
|
| + if (GetQueryParameter(request_query, "issueuberauth", &issue_uberauth) &&
|
| + issue_uberauth == "1") {
|
| + http_response->set_content(fake_gaia_uber_token_);
|
| 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);
|
| - std::string redirect_url = continue_url;
|
| -
|
| - std::string email;
|
| - if (GetQueryParameter(request.content, "Email", &email) &&
|
| - saml_account_idp_map_.find(email) != saml_account_idp_map_.end()) {
|
| - GURL url(saml_account_idp_map_[email]);
|
| - url = net::AppendQueryParameter(url, "SAMLRequest", "fake_request");
|
| - url = net::AppendQueryParameter(url, "RelayState", continue_url);
|
| - redirect_url = url.spec();
|
| - }
|
| + // Issue GAIA uber token.
|
| + } else {
|
| + LOG(FATAL) << "/OAuthLogin for SID/LSID is not supported";
|
| + }
|
| +}
|
| +
|
| +void FakeGaia::HandleMergeSession(const HttpRequest& request,
|
| + BasicHttpResponse* http_response) {
|
| + http_response->set_code(net::HTTP_BAD_REQUEST);
|
| +
|
| + std::string uber_token;
|
| + if (!GetQueryParameter(request.content, "uberauth", &uber_token) ||
|
| + uber_token != fake_gaia_uber_token_) {
|
| + LOG(ERROR) << "Missing or invalid 'uberauth' param in /MergeSession call";
|
| + return;
|
| + }
|
| +
|
| + std::string continue_url;
|
| + if (!GetQueryParameter(request.content, "continue", &continue_url)) {
|
| + LOG(ERROR) << "Missing or invalid 'continue' param in /MergeSession call";
|
| + return;
|
| + }
|
| +
|
| + std::string source;
|
| + if (!GetQueryParameter(request.content, "source", &source)) {
|
| + LOG(ERROR) << "Missing or invalid 'source' param in /MergeSession call";
|
| + return;
|
| + }
|
| +
|
| + http_response->AddCustomHeader(
|
| + "Set-Cookie",
|
| + base::StringPrintf(
|
| + "SID=%s; LSID=%s; Path=/; Secure; HttpOnly;",
|
| + fake_session_sid_cookie_.c_str(),
|
| + fake_session_lsid_cookie_.c_str()));
|
| + // TODO(zelidrag): Not used now.
|
| + http_response->set_content("OK");
|
| + http_response->set_code(net::HTTP_OK);
|
| +}
|
| +
|
| +
|
| +void FakeGaia::HandleServiceLoginAuth(const HttpRequest& request,
|
| + BasicHttpResponse* http_response) {
|
| + std::string continue_url =
|
| + GaiaUrls::GetInstance()->service_login_url().spec();
|
| + GetQueryParameter(request.content, "continue", &continue_url);
|
| +
|
| + std::string redirect_url = continue_url;
|
| +
|
| + std::string email;
|
| + if (GetQueryParameter(request.content, "Email", &email) &&
|
| + saml_account_idp_map_.find(email) != saml_account_idp_map_.end()) {
|
| + GURL url(saml_account_idp_map_[email]);
|
| + url = net::AppendQueryParameter(url, "SAMLRequest", "fake_request");
|
| + url = net::AppendQueryParameter(url, "RelayState", continue_url);
|
| + redirect_url = url.spec();
|
| + }
|
| +
|
| + http_response->set_code(net::HTTP_TEMPORARY_REDIRECT);
|
| + http_response->AddCustomHeader("Location", redirect_url);
|
| +}
|
| +
|
| +void FakeGaia::HandleSSO(const HttpRequest& request,
|
| + BasicHttpResponse* http_response) {
|
| + std::string relay_state;
|
| + GetQueryParameter(request.content, "RelayState", &relay_state);
|
| + std::string redirect_url = relay_state;
|
| + http_response->set_code(net::HTTP_TEMPORARY_REDIRECT);
|
| + http_response->AddCustomHeader("Location", redirect_url);
|
| +}
|
| +
|
| +void FakeGaia::HandleAuthToken(const HttpRequest& request,
|
| + BasicHttpResponse* http_response) {
|
| + std::string grant_type;
|
| + std::string refresh_token;
|
| + std::string client_id;
|
| + std::string scope;
|
| + std::string auth_code;
|
| + const AccessTokenInfo* token_info = NULL;
|
| + GetQueryParameter(request.content, "scope", &scope);
|
|
|
| - http_response->set_code(net::HTTP_TEMPORARY_REDIRECT);
|
| - http_response->AddCustomHeader("Location", redirect_url);
|
| - } else if (request_path == gaia_urls->oauth2_token_url().path()) {
|
| - std::string refresh_token;
|
| - std::string client_id;
|
| - std::string scope;
|
| - const AccessTokenInfo* token_info = NULL;
|
| - GetQueryParameter(request.content, "scope", &scope);
|
| - if (GetQueryParameter(request.content, "refresh_token", &refresh_token) &&
|
| - GetQueryParameter(request.content, "client_id", &client_id) &&
|
| - (token_info = GetAccessTokenInfo(refresh_token, client_id, scope))) {
|
| - 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 {
|
| + if (!GetQueryParameter(request.content, "grant_type", &grant_type)) {
|
| + http_response->set_code(net::HTTP_BAD_REQUEST);
|
| + LOG(ERROR) << "No 'grant_type' param in /o/oauth2/token";
|
| + return;
|
| + }
|
| +
|
| + if (grant_type == "authorization_code") {
|
| + if (!GetQueryParameter(request.content, "code", &auth_code) ||
|
| + auth_code != fake_auth_code_) {
|
| 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;
|
| - }
|
| - }
|
| + LOG(ERROR) << "No 'code' param in /o/oauth2/token";
|
| + return;
|
| }
|
|
|
| - 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 {
|
| + if (GaiaUrls::GetInstance()->oauth1_login_scope() != scope) {
|
| http_response->set_code(net::HTTP_BAD_REQUEST);
|
| - }
|
| - } else if (request_path == gaia_urls->oauth2_issue_token_url().path()) {
|
| - std::string access_token;
|
| - std::map<std::string, std::string>::const_iterator auth_header_entry =
|
| - request.headers.find("Authorization");
|
| - if (auth_header_entry != request.headers.end()) {
|
| - if (StartsWithASCII(auth_header_entry->second, "Bearer ", true))
|
| - access_token = auth_header_entry->second.substr(7);
|
| + LOG(ERROR) << "Invalid scope for /o/oauth2/token - " << scope;
|
| + return;
|
| }
|
|
|
| - std::string scope;
|
| - std::string client_id;
|
| - const AccessTokenInfo* token_info = NULL;
|
| - if (GetQueryParameter(request.content, "scope", &scope) &&
|
| - GetQueryParameter(request.content, "client_id", &client_id) &&
|
| - (token_info = GetAccessTokenInfo(access_token, client_id, scope))) {
|
| - base::DictionaryValue response_dict;
|
| - response_dict.SetString("issueAdvice", "auto");
|
| - response_dict.SetString("expiresIn",
|
| - base::IntToString(token_info->expires_in));
|
| - response_dict.SetString("token", token_info->token);
|
| - FormatJSONResponse(response_dict, http_response.get());
|
| - } else {
|
| - http_response->set_code(net::HTTP_BAD_REQUEST);
|
| + base::DictionaryValue response_dict;
|
| + response_dict.SetString("refresh_token", fake_refresh_token_);
|
| + response_dict.SetString("access_token", fake_access_token_);
|
| + response_dict.SetInteger("expires_in", 3600);
|
| + FormatJSONResponse(response_dict, http_response);
|
| + } else if (GetQueryParameter(request.content,
|
| + "refresh_token",
|
| + &refresh_token) &&
|
| + GetQueryParameter(request.content,
|
| + "client_id",
|
| + &client_id) &&
|
| + (token_info = FindAccessTokenInfo(refresh_token,
|
| + client_id,
|
| + scope))) {
|
| + base::DictionaryValue response_dict;
|
| + response_dict.SetString("access_token", token_info->token);
|
| + response_dict.SetInteger("expires_in", 3600);
|
| + FormatJSONResponse(response_dict, http_response);
|
| + } else {
|
| + LOG(ERROR) << "Bad request for /o/oauth2/token - "
|
| + << "refresh_token = " << refresh_token
|
| + << ", scope = " << scope
|
| + << ", client_id = " << client_id;
|
| + http_response->set_code(net::HTTP_BAD_REQUEST);
|
| + }
|
| +}
|
| +
|
| +void FakeGaia::HandleTokenInfo(const HttpRequest& request,
|
| + BasicHttpResponse* http_response) {
|
| + 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;
|
| + }
|
| }
|
| - } else if (request_path == "/SSO") {
|
| - std::string relay_state;
|
| - GetQueryParameter(request.content, "RelayState", &relay_state);
|
| - std::string redirect_url = relay_state;
|
| - http_response->set_code(net::HTTP_TEMPORARY_REDIRECT);
|
| - http_response->AddCustomHeader("Location", redirect_url);
|
| + }
|
| +
|
| + 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);
|
| + } else {
|
| + http_response->set_code(net::HTTP_BAD_REQUEST);
|
| + }
|
| +}
|
| +
|
| +void FakeGaia::HandleIssueToken(const HttpRequest& request,
|
| + BasicHttpResponse* http_response) {
|
| + std::string access_token;
|
| + std::string scope;
|
| + std::string client_id;
|
| + const AccessTokenInfo* token_info = NULL;
|
| + if (GetAccessToken(request, kAuthHeaderBearer, &access_token) &&
|
| + GetQueryParameter(request.content, "scope", &scope) &&
|
| + GetQueryParameter(request.content, "client_id", &client_id) &&
|
| + (token_info = FindAccessTokenInfo(access_token, client_id, scope))) {
|
| + base::DictionaryValue response_dict;
|
| + response_dict.SetString("issueAdvice", "auto");
|
| + response_dict.SetString("expiresIn",
|
| + base::IntToString(token_info->expires_in));
|
| + response_dict.SetString("token", token_info->token);
|
| + FormatJSONResponse(response_dict, http_response);
|
| + } else {
|
| + http_response->set_code(net::HTTP_BAD_REQUEST);
|
| + }
|
| +}
|
| +
|
| +
|
| +scoped_ptr<HttpResponse> FakeGaia::HandleRequest(const HttpRequest& request) {
|
| + // 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());
|
| + RequestHandlerMap::iterator iter = request_handlers_.find(request_path);
|
| + if (iter != request_handlers_.end()) {
|
| + LOG(WARNING) << "Serving request " << request_path;
|
| + iter->second.Run(request, http_response.get());
|
| } else {
|
| - // Request not understood.
|
| - return scoped_ptr<HttpResponse>();
|
| + LOG(ERROR) << "Unhandled request " << request_path;
|
| + return scoped_ptr<HttpResponse>(); // Request not understood.
|
| }
|
|
|
| return http_response.PassAs<HttpResponse>();
|
| @@ -167,16 +381,6 @@ void FakeGaia::RegisterSamlUser(const std::string& account_id,
|
| saml_account_idp_map_[account_id] = saml_idp;
|
| }
|
|
|
| -// 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);
|
| -}
|
| -
|
| void FakeGaia::FormatJSONResponse(const base::DictionaryValue& response_dict,
|
| BasicHttpResponse* http_response) {
|
| std::string response_json;
|
| @@ -185,7 +389,7 @@ void FakeGaia::FormatJSONResponse(const base::DictionaryValue& response_dict,
|
| http_response->set_code(net::HTTP_OK);
|
| }
|
|
|
| -const FakeGaia::AccessTokenInfo* FakeGaia::GetAccessTokenInfo(
|
| +const FakeGaia::AccessTokenInfo* FakeGaia::FindAccessTokenInfo(
|
| const std::string& auth_token,
|
| const std::string& client_id,
|
| const std::string& scope_string) const {
|
| @@ -208,3 +412,30 @@ const FakeGaia::AccessTokenInfo* FakeGaia::GetAccessTokenInfo(
|
|
|
| return NULL;
|
| }
|
| +
|
| +// 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);
|
| +}
|
| +
|
| +// static
|
| +bool FakeGaia::GetAccessToken(const HttpRequest& request,
|
| + const char* auth_token_prefix,
|
| + std::string* access_token) {
|
| + std::map<std::string, std::string>::const_iterator auth_header_entry =
|
| + request.headers.find("Authorization");
|
| + if (auth_header_entry != request.headers.end()) {
|
| + if (StartsWithASCII(auth_header_entry->second, auth_token_prefix, true)) {
|
| + *access_token = auth_header_entry->second.substr(
|
| + strlen(auth_token_prefix));
|
| + return true;
|
| + }
|
| + }
|
| +
|
| + return false;
|
| +}
|
|
|