blob: fd175f3abf28472e20f45616d43668a7d36915d0 [file] [log] [blame]
// Copyright 2019 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 "chrome/browser/complex_tasks/endpoint_fetcher/endpoint_fetcher.h"
#include "base/strings/string_util.h"
#include "build/build_config.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/common/channel_info.h"
#include "components/signin/public/identity_manager/access_token_info.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/version_info/channel.h"
#include "content/public/browser/storage_partition.h"
#include "google_apis/gaia/gaia_urls.h"
#include "google_apis/google_api_keys.h"
#include "services/network/public/cpp/simple_url_loader.h"
#if defined(OS_ANDROID)
#include "base/android/callback_android.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "chrome/android/chrome_jni_headers/EndpointFetcher_jni.h"
#include "chrome/android/chrome_jni_headers/EndpointResponse_jni.h"
#include "chrome/browser/profiles/profile_android.h"
#endif // defined(OS_ANDROID)
namespace {
const char kContentTypeKey[] = "Content-Type";
const char kDeveloperKey[] = "X-Developer-Key";
const int kNumRetries = 3;
} // namespace
EndpointFetcher::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)
: EndpointFetcher(
oauth_consumer_name,
url,
http_method,
content_type,
scopes,
timeout_ms,
post_data,
annotation_tag,
content::BrowserContext::GetDefaultStoragePartition(profile)
->GetURLLoaderFactoryForBrowserProcess(),
IdentityManagerFactory::GetForProfile(profile)) {}
EndpointFetcher::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 net::NetworkTrafficAnnotationTag& annotation_tag)
: auth_type_(CHROME_API_KEY),
url_(url),
http_method_(http_method),
content_type_(content_type),
timeout_ms_(timeout_ms),
post_data_(post_data),
annotation_tag_(annotation_tag),
url_loader_factory_(
content::BrowserContext::GetDefaultStoragePartition(profile)
->GetURLLoaderFactoryForBrowserProcess()),
identity_manager_(nullptr),
sanitize_response_(true) {}
EndpointFetcher::EndpointFetcher(
Profile* const profile,
const GURL& url,
const net::NetworkTrafficAnnotationTag& annotation_tag)
: auth_type_(NO_AUTH),
url_(url),
http_method_("GET"),
content_type_(std::string()),
timeout_ms_(0),
post_data_(std::string()),
annotation_tag_(annotation_tag),
url_loader_factory_(
content::BrowserContext::GetDefaultStoragePartition(profile)
->GetURLLoaderFactoryForBrowserProcess()),
identity_manager_(nullptr),
sanitize_response_(false) {}
EndpointFetcher::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)
: auth_type_(OAUTH),
oauth_consumer_name_(oauth_consumer_name),
url_(url),
http_method_(http_method),
content_type_(content_type),
timeout_ms_(timeout_ms),
post_data_(post_data),
annotation_tag_(annotation_tag),
url_loader_factory_(url_loader_factory),
identity_manager_(identity_manager),
sanitize_response_(true) {
for (auto scope : scopes) {
oauth_scopes_.insert(scope);
}
}
EndpointFetcher::~EndpointFetcher() = default;
void EndpointFetcher::Fetch(EndpointFetcherCallback endpoint_fetcher_callback) {
signin::AccessTokenFetcher::TokenCallback token_callback = base::BindOnce(
&EndpointFetcher::OnAuthTokenFetched, weak_ptr_factory_.GetWeakPtr(),
std::move(endpoint_fetcher_callback));
DCHECK(!access_token_fetcher_);
DCHECK(!simple_url_loader_);
// TODO(crbug.com/997018) Make access_token_fetcher_ local variable passed
// to callback
access_token_fetcher_ =
std::make_unique<signin::PrimaryAccountAccessTokenFetcher>(
oauth_consumer_name_, identity_manager_, oauth_scopes_,
std::move(token_callback),
signin::PrimaryAccountAccessTokenFetcher::Mode::kImmediate);
}
void EndpointFetcher::OnAuthTokenFetched(
EndpointFetcherCallback endpoint_fetcher_callback,
GoogleServiceAuthError error,
signin::AccessTokenInfo access_token_info) {
access_token_fetcher_.reset();
if (error.state() != GoogleServiceAuthError::NONE) {
auto response = std::make_unique<EndpointResponse>();
response->response = "There was an authentication error";
// TODO(crbug.com/993393) Add more detailed error messaging
std::move(endpoint_fetcher_callback).Run(std::move(response));
return;
}
PerformRequest(std::move(endpoint_fetcher_callback),
access_token_info.token.c_str());
}
void EndpointFetcher::PerformRequest(
EndpointFetcherCallback endpoint_fetcher_callback,
const char* key) {
auto resource_request = std::make_unique<network::ResourceRequest>();
resource_request->method = http_method_;
resource_request->url = url_;
resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
if (base::EqualsCaseInsensitiveASCII(http_method_, "POST")) {
resource_request->headers.SetHeader(kContentTypeKey, content_type_);
}
switch (auth_type_) {
case OAUTH:
resource_request->headers.SetHeader(
kDeveloperKey, GaiaUrls::GetInstance()->oauth2_chrome_client_id());
resource_request->headers.SetHeader(
net::HttpRequestHeaders::kAuthorization,
base::StringPrintf("Bearer %s", key));
break;
case CHROME_API_KEY: {
bool is_stable_channel =
chrome::GetChannel() == version_info::Channel::STABLE;
std::string api_key = is_stable_channel
? google_apis::GetAPIKey()
: google_apis::GetNonStableAPIKey();
resource_request->headers.SetHeader("x-goog-api-key", api_key);
break;
}
default:
break;
}
// TODO(crbug.com/997018) Make simple_url_loader_ local variable passed to
// callback
simple_url_loader_ = network::SimpleURLLoader::Create(
std::move(resource_request), annotation_tag_);
if (base::EqualsCaseInsensitiveASCII(http_method_, "POST")) {
simple_url_loader_->AttachStringForUpload(post_data_, content_type_);
}
simple_url_loader_->SetRetryOptions(kNumRetries,
network::SimpleURLLoader::RETRY_ON_5XX);
simple_url_loader_->SetTimeoutDuration(
base::TimeDelta::FromMilliseconds(timeout_ms_));
network::SimpleURLLoader::BodyAsStringCallback body_as_string_callback =
base::BindOnce(&EndpointFetcher::OnResponseFetched,
weak_ptr_factory_.GetWeakPtr(),
std::move(endpoint_fetcher_callback));
simple_url_loader_->DownloadToString(
url_loader_factory_.get(), std::move(body_as_string_callback),
network::SimpleURLLoader::kMaxBoundedStringDownloadSize);
}
void EndpointFetcher::OnResponseFetched(
EndpointFetcherCallback endpoint_fetcher_callback,
std::unique_ptr<std::string> response_body) {
simple_url_loader_.reset();
if (response_body) {
if (sanitize_response_) {
data_decoder::JsonSanitizer::Sanitize(
std::move(*response_body),
base::BindOnce(&EndpointFetcher::OnSanitizationResult,
weak_ptr_factory_.GetWeakPtr(),
std::move(endpoint_fetcher_callback)));
} else {
auto response = std::make_unique<EndpointResponse>();
response->response = *response_body;
std::move(endpoint_fetcher_callback).Run(std::move(response));
}
} else {
auto response = std::make_unique<EndpointResponse>();
// TODO(crbug.com/993393) Add more detailed error messaging
response->response = "There was a response error";
std::move(endpoint_fetcher_callback).Run(std::move(response));
}
}
void EndpointFetcher::OnSanitizationResult(
EndpointFetcherCallback endpoint_fetcher_callback,
data_decoder::JsonSanitizer::Result result) {
auto response = std::make_unique<EndpointResponse>();
if (result.value.has_value())
response->response = result.value.value();
else if (result.error.has_value())
response->response =
"There was a sanitization error: " + result.error.value();
else
response->response = "There was an unknown sanitization error";
std::move(endpoint_fetcher_callback).Run(std::move(response));
}
#if defined(OS_ANDROID)
namespace {
static void OnEndpointFetcherComplete(
const base::android::JavaRef<jobject>& jcaller,
// Passing the endpoint_fetcher ensures the endpoint_fetcher's
// lifetime extends to the callback and is not destroyed
// prematurely (which would result in cancellation of the request).
std::unique_ptr<EndpointFetcher> endpoint_fetcher,
std::unique_ptr<EndpointResponse> endpoint_response) {
base::android::RunObjectCallbackAndroid(
jcaller, Java_EndpointResponse_createEndpointResponse(
base::android::AttachCurrentThread(),
base::android::ConvertUTF8ToJavaString(
base::android::AttachCurrentThread(),
std::move(endpoint_response->response))));
}
} // namespace
// TODO(crbug.com/1077537) Create a KeyProvider so
// we can have one centralized API.
static void 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) {
std::vector<std::string> scopes;
base::android::AppendJavaStringArrayToStringVector(env, jscopes, &scopes);
auto endpoint_fetcher = std::make_unique<EndpointFetcher>(
ProfileAndroid::FromProfileAndroid(jprofile),
base::android::ConvertJavaStringToUTF8(env, joauth_consumer_name),
GURL(base::android::ConvertJavaStringToUTF8(env, jurl)),
base::android::ConvertJavaStringToUTF8(env, jhttps_method),
base::android::ConvertJavaStringToUTF8(env, jcontent_type), scopes,
jtimeout, base::android::ConvertJavaStringToUTF8(env, jpost_data),
// TODO(crbug.com/995852) Create a traffic annotation tag and configure it
// as part of the EndpointFetcher call over JNI.
NO_TRAFFIC_ANNOTATION_YET);
endpoint_fetcher->Fetch(
base::BindOnce(&OnEndpointFetcherComplete,
base::android::ScopedJavaGlobalRef<jobject>(jcallback),
// unique_ptr endpoint_fetcher is passed until the callback
// to ensure its lifetime across the request.
std::move(endpoint_fetcher)));
}
static void 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<jobject>& jcallback) {
auto endpoint_fetcher = std::make_unique<EndpointFetcher>(
ProfileAndroid::FromProfileAndroid(jprofile),
GURL(base::android::ConvertJavaStringToUTF8(env, jurl)),
base::android::ConvertJavaStringToUTF8(env, jhttps_method),
base::android::ConvertJavaStringToUTF8(env, jcontent_type), jtimeout,
base::android::ConvertJavaStringToUTF8(env, jpost_data),
NO_TRAFFIC_ANNOTATION_YET);
endpoint_fetcher->PerformRequest(
base::BindOnce(&OnEndpointFetcherComplete,
base::android::ScopedJavaGlobalRef<jobject>(jcallback),
// unique_ptr endpoint_fetcher is passed until the callback
// to ensure its lifetime across the request.
std::move(endpoint_fetcher)),
nullptr);
}
static void JNI_EndpointFetcher_NativeFetchWithNoAuth(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jprofile,
const base::android::JavaParamRef<jstring>& jurl,
const base::android::JavaParamRef<jobject>& jcallback) {
auto endpoint_fetcher = std::make_unique<EndpointFetcher>(
ProfileAndroid::FromProfileAndroid(jprofile),
GURL(base::android::ConvertJavaStringToUTF8(env, jurl)),
NO_TRAFFIC_ANNOTATION_YET);
endpoint_fetcher->PerformRequest(
base::BindOnce(&OnEndpointFetcherComplete,
base::android::ScopedJavaGlobalRef<jobject>(jcallback),
// unique_ptr endpoint_fetcher is passed until the callback
// to ensure its lifetime across the request.
std::move(endpoint_fetcher)),
nullptr);
}
#endif // defined(OS_ANDROID)