Home | History | Annotate | Download | only in gaia
      1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "google_apis/gaia/fake_gaia.h"
      6 
      7 #include "base/base_paths.h"
      8 #include "base/file_util.h"
      9 #include "base/files/file_path.h"
     10 #include "base/json/json_writer.h"
     11 #include "base/logging.h"
     12 #include "base/path_service.h"
     13 #include "base/strings/string_number_conversions.h"
     14 #include "base/strings/string_split.h"
     15 #include "base/strings/string_util.h"
     16 #include "base/values.h"
     17 #include "google_apis/gaia/gaia_urls.h"
     18 #include "net/base/url_util.h"
     19 #include "net/http/http_status_code.h"
     20 #include "net/test/embedded_test_server/http_request.h"
     21 #include "net/test/embedded_test_server/http_response.h"
     22 #include "url/url_parse.h"
     23 
     24 using namespace net::test_server;
     25 
     26 namespace {
     27 const base::FilePath::CharType kServiceLogin[] =
     28     FILE_PATH_LITERAL("google_apis/test/service_login.html");
     29 }
     30 
     31 FakeGaia::AccessTokenInfo::AccessTokenInfo()
     32   : expires_in(3600) {}
     33 
     34 FakeGaia::AccessTokenInfo::~AccessTokenInfo() {}
     35 
     36 FakeGaia::FakeGaia() {
     37   base::FilePath source_root_dir;
     38   PathService::Get(base::DIR_SOURCE_ROOT, &source_root_dir);
     39   CHECK(base::ReadFileToString(
     40       source_root_dir.Append(base::FilePath(kServiceLogin)),
     41       &service_login_response_));
     42 }
     43 
     44 FakeGaia::~FakeGaia() {}
     45 
     46 scoped_ptr<HttpResponse> FakeGaia::HandleRequest(const HttpRequest& request) {
     47   GaiaUrls* gaia_urls = GaiaUrls::GetInstance();
     48 
     49   // The scheme and host of the URL is actually not important but required to
     50   // get a valid GURL in order to parse |request.relative_url|.
     51   GURL request_url = GURL("http://localhost").Resolve(request.relative_url);
     52   std::string request_path = request_url.path();
     53 
     54   scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse());
     55   if (request_path == gaia_urls->service_login_url().path()) {
     56     http_response->set_code(net::HTTP_OK);
     57     http_response->set_content(service_login_response_);
     58     http_response->set_content_type("text/html");
     59   } else if (request_path == gaia_urls->service_login_auth_url().path()) {
     60     std::string continue_url = gaia_urls->service_login_url().spec();
     61     GetQueryParameter(request.content, "continue", &continue_url);
     62     http_response->set_code(net::HTTP_OK);
     63     const std::string redirect_js =
     64         "document.location.href = '" + continue_url + "'";
     65     http_response->set_content(
     66         "<HTML><HEAD><SCRIPT>\n" + redirect_js + "\n</SCRIPT></HEAD></HTML>");
     67     http_response->set_content_type("text/html");
     68   } else if (request_path == gaia_urls->oauth2_token_url().path()) {
     69     std::string refresh_token;
     70     std::string client_id;
     71     std::string scope;
     72     const AccessTokenInfo* token_info = NULL;
     73     GetQueryParameter(request.content, "scope", &scope);
     74     if (GetQueryParameter(request.content, "refresh_token", &refresh_token) &&
     75         GetQueryParameter(request.content, "client_id", &client_id) &&
     76         (token_info = GetAccessTokenInfo(refresh_token, client_id, scope))) {
     77       base::DictionaryValue response_dict;
     78       response_dict.SetString("access_token", token_info->token);
     79       response_dict.SetInteger("expires_in", 3600);
     80       FormatJSONResponse(response_dict, http_response.get());
     81     } else {
     82       http_response->set_code(net::HTTP_BAD_REQUEST);
     83     }
     84   } else if (request_path == gaia_urls->oauth2_token_info_url().path()) {
     85     const AccessTokenInfo* token_info = NULL;
     86     std::string access_token;
     87     if (GetQueryParameter(request.content, "access_token", &access_token)) {
     88       for (AccessTokenInfoMap::const_iterator entry(
     89                access_token_info_map_.begin());
     90            entry != access_token_info_map_.end();
     91            ++entry) {
     92         if (entry->second.token == access_token) {
     93           token_info = &(entry->second);
     94           break;
     95         }
     96       }
     97     }
     98 
     99     if (token_info) {
    100       base::DictionaryValue response_dict;
    101       response_dict.SetString("issued_to", token_info->issued_to);
    102       response_dict.SetString("audience", token_info->audience);
    103       response_dict.SetString("user_id", token_info->user_id);
    104       std::vector<std::string> scope_vector(token_info->scopes.begin(),
    105                                             token_info->scopes.end());
    106       response_dict.SetString("scope", JoinString(scope_vector, " "));
    107       response_dict.SetInteger("expires_in", token_info->expires_in);
    108       response_dict.SetString("email", token_info->email);
    109       FormatJSONResponse(response_dict, http_response.get());
    110     } else {
    111       http_response->set_code(net::HTTP_BAD_REQUEST);
    112     }
    113   } else if (request_path == gaia_urls->oauth2_issue_token_url().path()) {
    114     std::string access_token;
    115     std::map<std::string, std::string>::const_iterator auth_header_entry =
    116         request.headers.find("Authorization");
    117     if (auth_header_entry != request.headers.end()) {
    118       if (StartsWithASCII(auth_header_entry->second, "Bearer ", true))
    119         access_token = auth_header_entry->second.substr(7);
    120     }
    121 
    122     std::string scope;
    123     std::string client_id;
    124     const AccessTokenInfo* token_info = NULL;
    125     if (GetQueryParameter(request.content, "scope", &scope) &&
    126         GetQueryParameter(request.content, "client_id", &client_id) &&
    127         (token_info = GetAccessTokenInfo(access_token, client_id, scope))) {
    128       base::DictionaryValue response_dict;
    129       response_dict.SetString("issueAdvice", "auto");
    130       response_dict.SetString("expiresIn",
    131                               base::IntToString(token_info->expires_in));
    132       response_dict.SetString("token", token_info->token);
    133       FormatJSONResponse(response_dict, http_response.get());
    134     } else {
    135       http_response->set_code(net::HTTP_BAD_REQUEST);
    136     }
    137   } else {
    138     // Request not understood.
    139     return scoped_ptr<HttpResponse>();
    140   }
    141 
    142   return http_response.PassAs<HttpResponse>();
    143 }
    144 
    145 void FakeGaia::IssueOAuthToken(const std::string& auth_token,
    146                                const AccessTokenInfo& token_info) {
    147   access_token_info_map_.insert(std::make_pair(auth_token, token_info));
    148 }
    149 
    150 void FakeGaia::FormatJSONResponse(const base::DictionaryValue& response_dict,
    151                                   BasicHttpResponse* http_response) {
    152   std::string response_json;
    153   base::JSONWriter::Write(&response_dict, &response_json);
    154   http_response->set_content(response_json);
    155   http_response->set_code(net::HTTP_OK);
    156 }
    157 
    158 const FakeGaia::AccessTokenInfo* FakeGaia::GetAccessTokenInfo(
    159     const std::string& auth_token,
    160     const std::string& client_id,
    161     const std::string& scope_string) const {
    162   if (auth_token.empty() || client_id.empty())
    163     return NULL;
    164 
    165   std::vector<std::string> scope_list;
    166   base::SplitString(scope_string, ' ', &scope_list);
    167   ScopeSet scopes(scope_list.begin(), scope_list.end());
    168 
    169   for (AccessTokenInfoMap::const_iterator entry(
    170            access_token_info_map_.lower_bound(auth_token));
    171        entry != access_token_info_map_.upper_bound(auth_token);
    172        ++entry) {
    173     if (entry->second.audience == client_id &&
    174         (scope_string.empty() || entry->second.scopes == scopes)) {
    175       return &(entry->second);
    176     }
    177   }
    178 
    179   return NULL;
    180 }
    181 
    182 // static
    183 bool FakeGaia::GetQueryParameter(const std::string& query,
    184                                  const std::string& key,
    185                                  std::string* value) {
    186   // Name and scheme actually don't matter, but are required to get a valid URL
    187   // for parsing.
    188   GURL query_url("http://localhost?" + query);
    189   return net::GetValueForKeyInQuery(query_url, key, value);
    190 }
    191