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