blob: f451cae2ac4a3fe22d11e51e74e08292f4b83893 [file] [log] [blame]
// Copyright 2014 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/feedback/feedback_uploader_chrome.h"
#include "base/bind.h"
#include "base/strings/stringprintf.h"
#include "build/chromeos_buildflags.h"
#include "build/config/chromebox_for_meetings/buildflags.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "components/feedback/feedback_report.h"
#include "components/signin/public/base/consent_level.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/signin/public/identity_manager/primary_account_access_token_fetcher.h"
#include "components/signin/public/identity_manager/scope_set.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/storage_partition.h"
#include "google_apis/gaia/gaia_constants.h"
#include "services/network/public/cpp/resource_request.h"
#if BUILDFLAG(PLATFORM_CFM)
#include "chrome/browser/ash/policy/enrollment/enrollment_requisition_manager.h"
#include "chrome/browser/device_identity/device_identity_provider.h"
#include "chrome/browser/device_identity/device_oauth2_token_service_factory.h"
#endif // BUILDFLAG(PLATFORM_CFM)
namespace feedback {
namespace {
constexpr char kAuthenticationErrorLogMessage[] =
"Feedback report will be sent without authentication.";
constexpr char kConsumer[] = "feedback_uploader_chrome";
void QueueSingleReport(base::WeakPtr<feedback::FeedbackUploader> uploader,
scoped_refptr<FeedbackReport> report) {
content::GetUIThreadTaskRunner({})->PostTask(
FROM_HERE, base::BindOnce(&FeedbackUploaderChrome::RequeueReport,
std::move(uploader), std::move(report)));
}
// Helper function to create an URLLoaderFactory for the FeedbackUploader from
// the BrowserContext storage partition. As creating the storage partition can
// be expensive, this is delayed so that it does not happen during startup.
scoped_refptr<network::SharedURLLoaderFactory>
CreateURLLoaderFactoryForBrowserContext(content::BrowserContext* context) {
return context->GetDefaultStoragePartition()
->GetURLLoaderFactoryForBrowserProcess();
}
} // namespace
FeedbackUploaderChrome::FeedbackUploaderChrome(content::BrowserContext* context)
// The FeedbackUploaderChrome lifetime is bound to that of BrowserContext
// by the KeyedServiceFactory infrastructure. The FeedbackUploaderChrome
// will be destroyed before the BrowserContext, thus base::Unretained()
// usage is safe.
: FeedbackUploader(/*is_off_the_record=*/false,
context->GetPath(),
base::BindOnce(&CreateURLLoaderFactoryForBrowserContext,
base::Unretained(context))),
context_(context) {
DCHECK(!context_->IsOffTheRecord());
task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&FeedbackReport::LoadReportsAndQueue,
feedback_reports_path(),
base::BindRepeating(&QueueSingleReport, AsWeakPtr())));
}
FeedbackUploaderChrome::~FeedbackUploaderChrome() = default;
void FeedbackUploaderChrome::PrimaryAccountAccessTokenAvailable(
GoogleServiceAuthError error,
signin::AccessTokenInfo access_token_info) {
DCHECK(primary_account_token_fetcher_);
primary_account_token_fetcher_.reset();
AccessTokenAvailable(error, access_token_info.token);
}
#if BUILDFLAG(PLATFORM_CFM)
void FeedbackUploaderChrome::ActiveAccountAccessTokenAvailable(
GoogleServiceAuthError error,
std::string token) {
DCHECK(active_account_token_fetcher_);
active_account_token_fetcher_.reset();
AccessTokenAvailable(error, token);
}
#endif // BUILDFLAG(PLATFORM_CFM)
void FeedbackUploaderChrome::AccessTokenAvailable(GoogleServiceAuthError error,
std::string token) {
if (error.state() == GoogleServiceAuthError::NONE) {
DCHECK(!token.empty());
access_token_ = token;
} else {
LOG(ERROR) << "Failed to get the access token. "
<< kAuthenticationErrorLogMessage;
}
FeedbackUploader::StartDispatchingReport();
}
void FeedbackUploaderChrome::StartDispatchingReport() {
if (delegate_)
delegate_->OnStartDispatchingReport();
access_token_.clear();
// TODO(crbug.com/849591): Instead of getting the IdentityManager from the
// profile, we should pass the IdentityManager to FeedbackUploaderChrome's
// ctor.
Profile* profile = Profile::FromBrowserContext(context_);
DCHECK(profile);
signin::IdentityManager* identity_manager =
IdentityManagerFactory::GetForProfile(profile);
// Sync consent is not required to send feedback because the feedback dialog
// has its own privacy notice.
if (identity_manager &&
identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSignin)) {
signin::ScopeSet scopes;
scopes.insert(GaiaConstants::kSupportContentOAuth2Scope);
primary_account_token_fetcher_ =
std::make_unique<signin::PrimaryAccountAccessTokenFetcher>(
kConsumer, identity_manager, scopes,
base::BindOnce(
&FeedbackUploaderChrome::PrimaryAccountAccessTokenAvailable,
base::Unretained(this)),
signin::PrimaryAccountAccessTokenFetcher::Mode::kImmediate,
signin::ConsentLevel::kSignin);
return;
}
#if BUILDFLAG(PLATFORM_CFM)
// CFM Devices may need to acquire the auth token for their robot account
// before they submit feedback.
DeviceOAuth2TokenService* deviceTokenService =
DeviceOAuth2TokenServiceFactory::Get();
DCHECK(deviceTokenService);
auto device_identity_provider =
std::make_unique<DeviceIdentityProvider>(deviceTokenService);
// Flag indicating that a device was intended to be used as a CFM.
bool isMeetDevice =
policy::EnrollmentRequisitionManager::IsRemoraRequisition();
if (isMeetDevice && !device_identity_provider->GetActiveAccountId().empty()) {
OAuth2AccessTokenManager::ScopeSet scopes;
scopes.insert(GaiaConstants::kSupportContentOAuth2Scope);
active_account_token_fetcher_ = device_identity_provider->FetchAccessToken(
kConsumer, scopes,
base::BindOnce(
&FeedbackUploaderChrome::ActiveAccountAccessTokenAvailable,
base::Unretained(this)));
return;
}
#endif // BUILDFLAG(PLATFORM_CFM)
LOG(ERROR) << "Failed to request oauth access token. "
<< kAuthenticationErrorLogMessage;
FeedbackUploader::StartDispatchingReport();
}
void FeedbackUploaderChrome::AppendExtraHeadersToUploadRequest(
network::ResourceRequest* resource_request) {
if (access_token_.empty())
return;
resource_request->headers.SetHeader(
net::HttpRequestHeaders::kAuthorization,
base::StringPrintf("Bearer %s", access_token_.c_str()));
}
} // namespace feedback