blob: 9c082cacc17a55a645dd868c21721afbc751e739 [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/printing/cloud_print/gcd_api_flow_impl.h"
#include <stddef.h>
#include <memory>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/json/json_reader.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/values.h"
#include "chrome/browser/printing/cloud_print/gcd_constants.h"
#include "chrome/common/cloud_print/cloud_print_constants.h"
#include "components/cloud_devices/common/cloud_devices_urls.h"
#include "components/data_use_measurement/core/data_use_user_data.h"
#include "google_apis/gaia/google_service_auth_error.h"
#include "net/base/load_flags.h"
#include "net/base/url_util.h"
#include "net/http/http_status_code.h"
#include "net/url_request/url_request_status.h"
#include "services/identity/public/cpp/identity_manager.h"
#include "services/identity/public/cpp/primary_account_access_token_fetcher.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/simple_url_loader.h"
using net::DefineNetworkTrafficAnnotation;
namespace cloud_print {
namespace {
const char kCloudPrintOAuthHeaderKey[] = "Authorization";
const char kCloudPrintOAuthHeaderValueFormat[] = "Bearer %s";
constexpr size_t kMaxContentSize = 1 * 1024 * 1024;
const std::string GetOAuthHeaderValue(const std::string& token) {
return base::StringPrintf(kCloudPrintOAuthHeaderValueFormat, token.c_str());
}
net::NetworkTrafficAnnotationTag GetNetworkTrafficAnnotation(
GCDApiFlow::Request::NetworkTrafficAnnotation type) {
if (type == CloudPrintApiFlowRequest::TYPE_PRIVET_REGISTER) {
return DefineNetworkTrafficAnnotation("cloud_print_privet_register", R"(
semantics {
sender: "Cloud Print"
description:
"Registers a locally discovered Privet printer with a Cloud Print "
"Server."
trigger:
"Users can select Privet printers on chrome://devices/ and "
"register them."
data:
"Token id for a printer retrieved from a previous request to a "
"Cloud Print Server."
destination: OTHER
}
policy {
cookies_allowed: NO
setting: "User triggered requests cannot be disabled."
policy_exception_justification: "Not implemented, it's good to do so."
})");
} else {
DCHECK_EQ(CloudPrintApiFlowRequest::TYPE_SEARCH, type);
return DefineNetworkTrafficAnnotation("cloud_print_search", R"(
semantics {
sender: "Cloud Print"
description:
"Queries a Cloud Print Server for the list of printers."
trigger:
"chrome://devices/ fetches the list when the user logs in, "
"re-enable the Cloud Print service, or manually requests a printer "
"list refresh."
data: "None"
destination: OTHER
}
policy {
cookies_allowed: NO
setting: "User triggered requests cannot be disabled."
policy_exception_justification: "Not implemented, it's good to do so."
})");
}
}
} // namespace
GCDApiFlowImpl::GCDApiFlowImpl(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
identity::IdentityManager* identity_manager)
: url_loader_factory_(url_loader_factory),
identity_manager_(identity_manager),
weak_factory_(this) {}
GCDApiFlowImpl::~GCDApiFlowImpl() {}
void GCDApiFlowImpl::Start(std::unique_ptr<Request> request) {
request_ = std::move(request);
identity::ScopeSet oauth_scopes;
oauth_scopes.insert(request_->GetOAuthScope());
DCHECK(identity_manager_);
token_fetcher_ = std::make_unique<identity::PrimaryAccountAccessTokenFetcher>(
"cloud_print", identity_manager_, oauth_scopes,
base::BindOnce(&GCDApiFlowImpl::OnAccessTokenFetchComplete,
base::Unretained(this)),
identity::PrimaryAccountAccessTokenFetcher::Mode::kImmediate);
}
void GCDApiFlowImpl::OnAccessTokenFetchComplete(
GoogleServiceAuthError error,
identity::AccessTokenInfo access_token_info) {
token_fetcher_.reset();
if (error.state() != GoogleServiceAuthError::NONE) {
request_->OnGCDApiFlowError(ERROR_TOKEN);
return;
}
auto request = std::make_unique<network::ResourceRequest>();
request->url = request_->GetURL();
request->load_flags =
net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES;
request->headers.SetHeader(kCloudPrintOAuthHeaderKey,
GetOAuthHeaderValue(access_token_info.token));
std::vector<std::string> extra_headers = request_->GetExtraRequestHeaders();
for (const std::string& header : extra_headers)
request->headers.AddHeaderFromString(header);
// TODO(https://crbug.com/808498): Re-add data use measurement once
// SimpleURLLoader supports it.
// ID=data_use_measurement::DataUseUserData::CLOUD_PRINT
url_loader_ = network::SimpleURLLoader::Create(
std::move(request),
GetNetworkTrafficAnnotation(request_->GetNetworkTrafficAnnotationType()));
url_loader_->SetAllowHttpErrorResults(true);
url_loader_->DownloadToString(
url_loader_factory_.get(),
base::BindOnce(&GCDApiFlowImpl::OnDownloadedToString,
weak_factory_.GetWeakPtr()),
kMaxContentSize);
}
void GCDApiFlowImpl::OnDownloadedToString(
std::unique_ptr<std::string> response_body) {
const network::ResourceResponseHead* response_info =
url_loader_->ResponseInfo();
if (url_loader_->NetError() != net::OK || !response_info) {
request_->OnGCDApiFlowError(ERROR_NETWORK);
return;
}
if (response_info->headers &&
response_info->headers->response_code() != net::HTTP_OK) {
request_->OnGCDApiFlowError(ERROR_HTTP_CODE);
return;
}
base::JSONReader reader;
std::unique_ptr<const base::Value> value(reader.Read(*response_body));
const base::DictionaryValue* dictionary_value = NULL;
if (!value || !value->GetAsDictionary(&dictionary_value)) {
request_->OnGCDApiFlowError(ERROR_MALFORMED_RESPONSE);
return;
}
request_->OnGCDApiFlowComplete(*dictionary_value);
}
} // namespace cloud_print