1 // Copyright 2019 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 "chrome/browser/endpoint_fetcher/endpoint_fetcher.h"
6 
7 #include "base/strings/string_util.h"
8 #include "build/build_config.h"
9 #include "chrome/browser/profiles/profile.h"
10 #include "chrome/browser/signin/identity_manager_factory.h"
11 #include "chrome/common/channel_info.h"
12 #include "components/signin/public/identity_manager/access_token_info.h"
13 #include "components/signin/public/identity_manager/identity_manager.h"
14 #include "components/version_info/channel.h"
15 #include "content/public/browser/storage_partition.h"
16 #include "google_apis/gaia/gaia_urls.h"
17 #include "google_apis/google_api_keys.h"
18 #include "services/network/public/cpp/simple_url_loader.h"
19 
20 #if defined(OS_ANDROID)
21 
22 #include "base/android/callback_android.h"
23 #include "base/android/jni_array.h"
24 #include "base/android/jni_string.h"
25 #include "chrome/browser/endpoint_fetcher/jni_headers/EndpointFetcher_jni.h"
26 #include "chrome/browser/endpoint_fetcher/jni_headers/EndpointResponse_jni.h"
27 #include "chrome/browser/profiles/profile_android.h"
28 
29 #endif  // defined(OS_ANDROID)
30 
31 namespace {
32 const char kContentTypeKey[] = "Content-Type";
33 const char kDeveloperKey[] = "X-Developer-Key";
34 const int kNumRetries = 3;
35 }  // namespace
36 
EndpointFetcher(Profile * const profile,const std::string & oauth_consumer_name,const GURL & url,const std::string & http_method,const std::string & content_type,const std::vector<std::string> & scopes,int64_t timeout_ms,const std::string & post_data,const net::NetworkTrafficAnnotationTag & annotation_tag)37 EndpointFetcher::EndpointFetcher(
38     Profile* const profile,
39     const std::string& oauth_consumer_name,
40     const GURL& url,
41     const std::string& http_method,
42     const std::string& content_type,
43     const std::vector<std::string>& scopes,
44     int64_t timeout_ms,
45     const std::string& post_data,
46     const net::NetworkTrafficAnnotationTag& annotation_tag)
47     : EndpointFetcher(
48           oauth_consumer_name,
49           url,
50           http_method,
51           content_type,
52           scopes,
53           timeout_ms,
54           post_data,
55           annotation_tag,
56           content::BrowserContext::GetDefaultStoragePartition(profile)
57               ->GetURLLoaderFactoryForBrowserProcess(),
58           IdentityManagerFactory::GetForProfile(profile)) {}
59 
EndpointFetcher(Profile * const profile,const GURL & url,const std::string & http_method,const std::string & content_type,int64_t timeout_ms,const std::string & post_data,const std::vector<std::string> & headers,const net::NetworkTrafficAnnotationTag & annotation_tag)60 EndpointFetcher::EndpointFetcher(
61     Profile* const profile,
62     const GURL& url,
63     const std::string& http_method,
64     const std::string& content_type,
65     int64_t timeout_ms,
66     const std::string& post_data,
67     const std::vector<std::string>& headers,
68     const net::NetworkTrafficAnnotationTag& annotation_tag)
69     : auth_type_(CHROME_API_KEY),
70       url_(url),
71       http_method_(http_method),
72       content_type_(content_type),
73       timeout_ms_(timeout_ms),
74       post_data_(post_data),
75       headers_(headers),
76       annotation_tag_(annotation_tag),
77       url_loader_factory_(
78           content::BrowserContext::GetDefaultStoragePartition(profile)
79               ->GetURLLoaderFactoryForBrowserProcess()),
80       identity_manager_(nullptr),
81       sanitize_response_(true) {}
82 
EndpointFetcher(Profile * const profile,const GURL & url,const net::NetworkTrafficAnnotationTag & annotation_tag)83 EndpointFetcher::EndpointFetcher(
84     Profile* const profile,
85     const GURL& url,
86     const net::NetworkTrafficAnnotationTag& annotation_tag)
87     : auth_type_(NO_AUTH),
88       url_(url),
89       http_method_("GET"),
90       content_type_(std::string()),
91       timeout_ms_(0),
92       post_data_(std::string()),
93       annotation_tag_(annotation_tag),
94       url_loader_factory_(
95           content::BrowserContext::GetDefaultStoragePartition(profile)
96               ->GetURLLoaderFactoryForBrowserProcess()),
97       identity_manager_(nullptr),
98       sanitize_response_(false) {}
99 
EndpointFetcher(const std::string & oauth_consumer_name,const GURL & url,const std::string & http_method,const std::string & content_type,const std::vector<std::string> & scopes,int64_t timeout_ms,const std::string & post_data,const net::NetworkTrafficAnnotationTag & annotation_tag,const scoped_refptr<network::SharedURLLoaderFactory> & url_loader_factory,signin::IdentityManager * const identity_manager)100 EndpointFetcher::EndpointFetcher(
101     const std::string& oauth_consumer_name,
102     const GURL& url,
103     const std::string& http_method,
104     const std::string& content_type,
105     const std::vector<std::string>& scopes,
106     int64_t timeout_ms,
107     const std::string& post_data,
108     const net::NetworkTrafficAnnotationTag& annotation_tag,
109     const scoped_refptr<network::SharedURLLoaderFactory>& url_loader_factory,
110     signin::IdentityManager* const identity_manager)
111     : auth_type_(OAUTH),
112       oauth_consumer_name_(oauth_consumer_name),
113       url_(url),
114       http_method_(http_method),
115       content_type_(content_type),
116       timeout_ms_(timeout_ms),
117       post_data_(post_data),
118       annotation_tag_(annotation_tag),
119       url_loader_factory_(url_loader_factory),
120       identity_manager_(identity_manager),
121       sanitize_response_(true) {
122   for (auto scope : scopes) {
123     oauth_scopes_.insert(scope);
124   }
125 }
126 
127 EndpointFetcher::~EndpointFetcher() = default;
128 
Fetch(EndpointFetcherCallback endpoint_fetcher_callback)129 void EndpointFetcher::Fetch(EndpointFetcherCallback endpoint_fetcher_callback) {
130   signin::AccessTokenFetcher::TokenCallback token_callback = base::BindOnce(
131       &EndpointFetcher::OnAuthTokenFetched, weak_ptr_factory_.GetWeakPtr(),
132       std::move(endpoint_fetcher_callback));
133   DCHECK(!access_token_fetcher_);
134   DCHECK(!simple_url_loader_);
135   // TODO(crbug.com/997018) Make access_token_fetcher_ local variable passed
136   // to callback
137   access_token_fetcher_ =
138       std::make_unique<signin::PrimaryAccountAccessTokenFetcher>(
139           oauth_consumer_name_, identity_manager_, oauth_scopes_,
140           std::move(token_callback),
141           signin::PrimaryAccountAccessTokenFetcher::Mode::kImmediate);
142 }
143 
OnAuthTokenFetched(EndpointFetcherCallback endpoint_fetcher_callback,GoogleServiceAuthError error,signin::AccessTokenInfo access_token_info)144 void EndpointFetcher::OnAuthTokenFetched(
145     EndpointFetcherCallback endpoint_fetcher_callback,
146     GoogleServiceAuthError error,
147     signin::AccessTokenInfo access_token_info) {
148   access_token_fetcher_.reset();
149   if (error.state() != GoogleServiceAuthError::NONE) {
150     auto response = std::make_unique<EndpointResponse>();
151     response->response = "There was an authentication error";
152     // TODO(crbug.com/993393) Add more detailed error messaging
153     std::move(endpoint_fetcher_callback).Run(std::move(response));
154     return;
155   }
156   PerformRequest(std::move(endpoint_fetcher_callback),
157                  access_token_info.token.c_str());
158 }
159 
PerformRequest(EndpointFetcherCallback endpoint_fetcher_callback,const char * key)160 void EndpointFetcher::PerformRequest(
161     EndpointFetcherCallback endpoint_fetcher_callback,
162     const char* key) {
163   auto resource_request = std::make_unique<network::ResourceRequest>();
164   resource_request->method = http_method_;
165   resource_request->url = url_;
166   resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
167   if (base::EqualsCaseInsensitiveASCII(http_method_, "POST")) {
168     resource_request->headers.SetHeader(kContentTypeKey, content_type_);
169   }
170   DCHECK(headers_.size() % 2 == 0);
171   for (size_t i = 0; i + 1 < headers_.size(); i += 2) {
172     resource_request->headers.SetHeader(headers_[i], headers_[i + 1]);
173   }
174   switch (auth_type_) {
175     case OAUTH:
176       resource_request->headers.SetHeader(
177           kDeveloperKey, GaiaUrls::GetInstance()->oauth2_chrome_client_id());
178       resource_request->headers.SetHeader(
179           net::HttpRequestHeaders::kAuthorization,
180           base::StringPrintf("Bearer %s", key));
181       break;
182     case CHROME_API_KEY: {
183       bool is_stable_channel =
184           chrome::GetChannel() == version_info::Channel::STABLE;
185       std::string api_key = is_stable_channel
186                                 ? google_apis::GetAPIKey()
187                                 : google_apis::GetNonStableAPIKey();
188       resource_request->headers.SetHeader("x-goog-api-key", api_key);
189       break;
190     }
191     default:
192       break;
193   }
194   // TODO(crbug.com/997018) Make simple_url_loader_ local variable passed to
195   // callback
196   simple_url_loader_ = network::SimpleURLLoader::Create(
197       std::move(resource_request), annotation_tag_);
198 
199   if (base::EqualsCaseInsensitiveASCII(http_method_, "POST")) {
200     simple_url_loader_->AttachStringForUpload(post_data_, content_type_);
201   }
202   simple_url_loader_->SetRetryOptions(kNumRetries,
203                                       network::SimpleURLLoader::RETRY_ON_5XX);
204   simple_url_loader_->SetTimeoutDuration(
205       base::TimeDelta::FromMilliseconds(timeout_ms_));
206   network::SimpleURLLoader::BodyAsStringCallback body_as_string_callback =
207       base::BindOnce(&EndpointFetcher::OnResponseFetched,
208                      weak_ptr_factory_.GetWeakPtr(),
209                      std::move(endpoint_fetcher_callback));
210   simple_url_loader_->DownloadToString(
211       url_loader_factory_.get(), std::move(body_as_string_callback),
212       network::SimpleURLLoader::kMaxBoundedStringDownloadSize);
213 }
214 
OnResponseFetched(EndpointFetcherCallback endpoint_fetcher_callback,std::unique_ptr<std::string> response_body)215 void EndpointFetcher::OnResponseFetched(
216     EndpointFetcherCallback endpoint_fetcher_callback,
217     std::unique_ptr<std::string> response_body) {
218   simple_url_loader_.reset();
219   if (response_body) {
220     if (sanitize_response_) {
221       data_decoder::JsonSanitizer::Sanitize(
222           std::move(*response_body),
223           base::BindOnce(&EndpointFetcher::OnSanitizationResult,
224                          weak_ptr_factory_.GetWeakPtr(),
225                          std::move(endpoint_fetcher_callback)));
226     } else {
227       auto response = std::make_unique<EndpointResponse>();
228       response->response = *response_body;
229       std::move(endpoint_fetcher_callback).Run(std::move(response));
230     }
231   } else {
232     auto response = std::make_unique<EndpointResponse>();
233     // TODO(crbug.com/993393) Add more detailed error messaging
234     response->response = "There was a response error";
235     std::move(endpoint_fetcher_callback).Run(std::move(response));
236   }
237 }
238 
OnSanitizationResult(EndpointFetcherCallback endpoint_fetcher_callback,data_decoder::JsonSanitizer::Result result)239 void EndpointFetcher::OnSanitizationResult(
240     EndpointFetcherCallback endpoint_fetcher_callback,
241     data_decoder::JsonSanitizer::Result result) {
242   auto response = std::make_unique<EndpointResponse>();
243   if (result.value.has_value())
244     response->response = result.value.value();
245   else if (result.error.has_value())
246     response->response =
247         "There was a sanitization error: " + result.error.value();
248   else
249     response->response = "There was an unknown sanitization error";
250   std::move(endpoint_fetcher_callback).Run(std::move(response));
251 }
252 
253 #if defined(OS_ANDROID)
254 namespace {
OnEndpointFetcherComplete(const base::android::JavaRef<jobject> & jcaller,std::unique_ptr<EndpointFetcher> endpoint_fetcher,std::unique_ptr<EndpointResponse> endpoint_response)255 static void OnEndpointFetcherComplete(
256     const base::android::JavaRef<jobject>& jcaller,
257     // Passing the endpoint_fetcher ensures the endpoint_fetcher's
258     // lifetime extends to the callback and is not destroyed
259     // prematurely (which would result in cancellation of the request).
260     std::unique_ptr<EndpointFetcher> endpoint_fetcher,
261     std::unique_ptr<EndpointResponse> endpoint_response) {
262   base::android::RunObjectCallbackAndroid(
263       jcaller, Java_EndpointResponse_createEndpointResponse(
264                    base::android::AttachCurrentThread(),
265                    base::android::ConvertUTF8ToJavaString(
266                        base::android::AttachCurrentThread(),
267                        std::move(endpoint_response->response))));
268 }
269 }  // namespace
270 
271 // TODO(crbug.com/1077537) Create a KeyProvider so
272 // we can have one centralized API.
273 
JNI_EndpointFetcher_NativeFetchOAuth(JNIEnv * env,const base::android::JavaParamRef<jobject> & jprofile,const base::android::JavaParamRef<jstring> & joauth_consumer_name,const base::android::JavaParamRef<jstring> & jurl,const base::android::JavaParamRef<jstring> & jhttps_method,const base::android::JavaParamRef<jstring> & jcontent_type,const base::android::JavaParamRef<jobjectArray> & jscopes,const base::android::JavaParamRef<jstring> & jpost_data,jlong jtimeout,const base::android::JavaParamRef<jobject> & jcallback)274 static void JNI_EndpointFetcher_NativeFetchOAuth(
275     JNIEnv* env,
276     const base::android::JavaParamRef<jobject>& jprofile,
277     const base::android::JavaParamRef<jstring>& joauth_consumer_name,
278     const base::android::JavaParamRef<jstring>& jurl,
279     const base::android::JavaParamRef<jstring>& jhttps_method,
280     const base::android::JavaParamRef<jstring>& jcontent_type,
281     const base::android::JavaParamRef<jobjectArray>& jscopes,
282     const base::android::JavaParamRef<jstring>& jpost_data,
283     jlong jtimeout,
284     const base::android::JavaParamRef<jobject>& jcallback) {
285   std::vector<std::string> scopes;
286   base::android::AppendJavaStringArrayToStringVector(env, jscopes, &scopes);
287   auto endpoint_fetcher = std::make_unique<EndpointFetcher>(
288       ProfileAndroid::FromProfileAndroid(jprofile),
289       base::android::ConvertJavaStringToUTF8(env, joauth_consumer_name),
290       GURL(base::android::ConvertJavaStringToUTF8(env, jurl)),
291       base::android::ConvertJavaStringToUTF8(env, jhttps_method),
292       base::android::ConvertJavaStringToUTF8(env, jcontent_type), scopes,
293       jtimeout, base::android::ConvertJavaStringToUTF8(env, jpost_data),
294       // TODO(crbug.com/995852) Create a traffic annotation tag and configure it
295       // as part of the EndpointFetcher call over JNI.
296       NO_TRAFFIC_ANNOTATION_YET);
297   endpoint_fetcher->Fetch(
298       base::BindOnce(&OnEndpointFetcherComplete,
299                      base::android::ScopedJavaGlobalRef<jobject>(jcallback),
300                      // unique_ptr endpoint_fetcher is passed until the callback
301                      // to ensure its lifetime across the request.
302                      std::move(endpoint_fetcher)));
303 }
304 
JNI_EndpointFetcher_NativeFetchChromeAPIKey(JNIEnv * env,const base::android::JavaParamRef<jobject> & jprofile,const base::android::JavaParamRef<jstring> & jurl,const base::android::JavaParamRef<jstring> & jhttps_method,const base::android::JavaParamRef<jstring> & jcontent_type,const base::android::JavaParamRef<jstring> & jpost_data,jlong jtimeout,const base::android::JavaParamRef<jobjectArray> & jheaders,const base::android::JavaParamRef<jobject> & jcallback)305 static void JNI_EndpointFetcher_NativeFetchChromeAPIKey(
306     JNIEnv* env,
307     const base::android::JavaParamRef<jobject>& jprofile,
308     const base::android::JavaParamRef<jstring>& jurl,
309     const base::android::JavaParamRef<jstring>& jhttps_method,
310     const base::android::JavaParamRef<jstring>& jcontent_type,
311     const base::android::JavaParamRef<jstring>& jpost_data,
312     jlong jtimeout,
313     const base::android::JavaParamRef<jobjectArray>& jheaders,
314     const base::android::JavaParamRef<jobject>& jcallback) {
315   std::vector<std::string> headers;
316   base::android::AppendJavaStringArrayToStringVector(env, jheaders, &headers);
317   auto endpoint_fetcher = std::make_unique<EndpointFetcher>(
318       ProfileAndroid::FromProfileAndroid(jprofile),
319       GURL(base::android::ConvertJavaStringToUTF8(env, jurl)),
320       base::android::ConvertJavaStringToUTF8(env, jhttps_method),
321       base::android::ConvertJavaStringToUTF8(env, jcontent_type), jtimeout,
322       base::android::ConvertJavaStringToUTF8(env, jpost_data), headers,
323       NO_TRAFFIC_ANNOTATION_YET);
324   endpoint_fetcher->PerformRequest(
325       base::BindOnce(&OnEndpointFetcherComplete,
326                      base::android::ScopedJavaGlobalRef<jobject>(jcallback),
327                      // unique_ptr endpoint_fetcher is passed until the callback
328                      // to ensure its lifetime across the request.
329                      std::move(endpoint_fetcher)),
330       nullptr);
331 }
332 
JNI_EndpointFetcher_NativeFetchWithNoAuth(JNIEnv * env,const base::android::JavaParamRef<jobject> & jprofile,const base::android::JavaParamRef<jstring> & jurl,const base::android::JavaParamRef<jobject> & jcallback)333 static void JNI_EndpointFetcher_NativeFetchWithNoAuth(
334     JNIEnv* env,
335     const base::android::JavaParamRef<jobject>& jprofile,
336     const base::android::JavaParamRef<jstring>& jurl,
337     const base::android::JavaParamRef<jobject>& jcallback) {
338   auto endpoint_fetcher = std::make_unique<EndpointFetcher>(
339       ProfileAndroid::FromProfileAndroid(jprofile),
340       GURL(base::android::ConvertJavaStringToUTF8(env, jurl)),
341       NO_TRAFFIC_ANNOTATION_YET);
342   endpoint_fetcher->PerformRequest(
343       base::BindOnce(&OnEndpointFetcherComplete,
344                      base::android::ScopedJavaGlobalRef<jobject>(jcallback),
345                      // unique_ptr endpoint_fetcher is passed until the callback
346                      // to ensure its lifetime across the request.
347                      std::move(endpoint_fetcher)),
348       nullptr);
349 }
350 
351 #endif  // defined(OS_ANDROID)
352