Commit ac986648 authored by Nela Kaczmarek's avatar Nela Kaczmarek Committed by Commit Bot

Add AffiliationFetcherBase implementation of AffiliationFetcherInterface.

This change creates AffiliationFetcherBase which derives from AffiliationFetcherInterface. This is a base class for existing AffiliationFetcher, which allows a derived class to implement the request part of the fetcher. The response part stays the same.

Bug: 1108279
Change-Id: Ia54a4d88f6684c0c95345b6c26eb5132d1ec6233
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2404843Reviewed-by: default avatarJan Wilken Dörrie <jdoerrie@chromium.org>
Commit-Queue: Nela Kaczmarek <nelakaczmarek@google.com>
Cr-Commit-Position: refs/heads/master@{#811290}
parent 53726bf6
...@@ -201,6 +201,8 @@ static_library("browser") { ...@@ -201,6 +201,8 @@ static_library("browser") {
"psl_matching_helper.cc", "psl_matching_helper.cc",
"psl_matching_helper.h", "psl_matching_helper.h",
"reauth_purpose.h", "reauth_purpose.h",
"site_affiliation/affiliation_fetcher_base.cc",
"site_affiliation/affiliation_fetcher_base.h",
"site_affiliation/affiliation_fetcher_factory.h", "site_affiliation/affiliation_fetcher_factory.h",
"site_affiliation/affiliation_fetcher_factory_impl.cc", "site_affiliation/affiliation_fetcher_factory_impl.cc",
"site_affiliation/affiliation_fetcher_factory_impl.h", "site_affiliation/affiliation_fetcher_factory_impl.h",
......
...@@ -4,57 +4,30 @@ ...@@ -4,57 +4,30 @@
#include "components/password_manager/core/browser/android_affiliation/affiliation_fetcher.h" #include "components/password_manager/core/browser/android_affiliation/affiliation_fetcher.h"
#include <stddef.h>
#include <map>
#include <memory> #include <memory>
#include <string> #include <string>
#include <utility>
#include "base/bind.h" #include "base/bind.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "components/password_manager/core/browser/android_affiliation/affiliation_api.pb.h" #include "components/password_manager/core/browser/android_affiliation/affiliation_api.pb.h"
#include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h" #include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h"
#include "components/password_manager/core/browser/android_affiliation/lookup_affiliation_response_parser.h"
#include "google_apis/google_api_keys.h" #include "google_apis/google_api_keys.h"
#include "net/base/load_flags.h"
#include "net/base/url_util.h" #include "net/base/url_util.h"
#include "net/http/http_status_code.h"
#include "net/traffic_annotation/network_traffic_annotation.h" #include "net/traffic_annotation/network_traffic_annotation.h"
#include "services/network/public/cpp/resource_request.h" #include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/shared_url_loader_factory.h" #include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/simple_url_loader.h"
#include "url/gurl.h" #include "url/gurl.h"
namespace password_manager { namespace password_manager {
// Enumeration listing the possible outcomes of fetching affiliation information
// from the Affiliation API. This is used in UMA histograms, so do not change
// existing values, only add new values at the end.
enum AffiliationFetchResult {
AFFILIATION_FETCH_RESULT_SUCCESS,
AFFILIATION_FETCH_RESULT_FAILURE,
AFFILIATION_FETCH_RESULT_MALFORMED,
AFFILIATION_FETCH_RESULT_MAX
};
AffiliationFetcher::AffiliationFetcher( AffiliationFetcher::AffiliationFetcher(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
AffiliationFetcherDelegate* delegate) AffiliationFetcherDelegate* delegate)
: url_loader_factory_(std::move(url_loader_factory)), : AffiliationFetcherBase(std::move(url_loader_factory), delegate) {}
delegate_(delegate) {
for (const FacetURI& uri : requested_facet_uris_) {
DCHECK(uri.is_valid());
}
}
AffiliationFetcher::~AffiliationFetcher() = default; AffiliationFetcher::~AffiliationFetcher() = default;
void AffiliationFetcher::StartRequest(const std::vector<FacetURI>& facet_uris, void AffiliationFetcher::StartRequest(const std::vector<FacetURI>& facet_uris,
RequestInfo request_info) { RequestInfo request_info) {
DCHECK(!simple_url_loader_);
fetch_timer_ = base::ElapsedTimer();
requested_facet_uris_ = facet_uris; requested_facet_uris_ = facet_uris;
net::NetworkTrafficAnnotationTag traffic_annotation = net::NetworkTrafficAnnotationTag traffic_annotation =
...@@ -88,121 +61,30 @@ void AffiliationFetcher::StartRequest(const std::vector<FacetURI>& facet_uris, ...@@ -88,121 +61,30 @@ void AffiliationFetcher::StartRequest(const std::vector<FacetURI>& facet_uris,
} }
} }
})"); })");
auto resource_request = std::make_unique<network::ResourceRequest>();
resource_request->url = BuildQueryURL();
resource_request->load_flags =
net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE;
resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
resource_request->method = "POST";
simple_url_loader_ = network::SimpleURLLoader::Create(
std::move(resource_request), traffic_annotation);
simple_url_loader_->AttachStringForUpload(PreparePayload(request_info),
"application/x-protobuf");
simple_url_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
url_loader_factory_.get(),
base::BindOnce(&AffiliationFetcher::OnSimpleLoaderComplete,
base::Unretained(this)));
}
const std::vector<FacetURI>& AffiliationFetcher::GetRequestedFacetURIs() const {
return requested_facet_uris_;
}
AffiliationFetcherDelegate* AffiliationFetcher::delegate() const { // Prepare the payload based on |facet_uris| and |request_info|.
return delegate_;
}
// static
GURL AffiliationFetcher::BuildQueryURL() {
return net::AppendQueryParameter(
GURL("https://www.googleapis.com/affiliation/v1/affiliation:lookup"),
"key", google_apis::GetAPIKey());
}
std::string AffiliationFetcher::PreparePayload(RequestInfo request_info) const {
affiliation_pb::LookupAffiliationRequest lookup_request; affiliation_pb::LookupAffiliationRequest lookup_request;
for (const FacetURI& uri : requested_facet_uris_) for (const FacetURI& uri : facet_uris)
lookup_request.add_facet(uri.canonical_spec()); lookup_request.add_facet(uri.canonical_spec());
auto mask = std::make_unique<affiliation_pb::LookupAffiliationMask>(); *lookup_request.mutable_mask() = CreateLookupMask(request_info);
mask->set_branding_info(request_info.branding_info);
// Change password info requires grouping info enabled.
mask->set_grouping_info(request_info.change_password_info);
mask->set_change_password_info(request_info.change_password_info);
lookup_request.set_allocated_mask(mask.release());
std::string serialized_request; std::string serialized_request;
bool success = lookup_request.SerializeToString(&serialized_request); bool success = lookup_request.SerializeToString(&serialized_request);
DCHECK(success); DCHECK(success);
return serialized_request;
}
bool AffiliationFetcher::ParseResponse(
const std::string& serialized_response,
AffiliationFetcherDelegate::Result* result) const {
// This function parses the response protocol buffer message for a list of
// equivalence classes, and stores them into |results| after performing some
// validation and sanitization steps to make sure that the contract of
// AffiliationFetcherDelegate is fulfilled. Possible discrepancies are:
// * The server response will not have anything for facets that are not
// affiliated with any other facet, while |result| must have them.
// * The server response might contain future, unknown kinds of facet URIs,
// while |result| must contain only those that are FacetURI::is_valid().
// * The server response being ill-formed or self-inconsistent (in the sense
// that there are overlapping equivalence classes) is indicative of server
// side issues likely not remedied by re-fetching. Report failure in this
// case so the caller can be notified and it can act accordingly.
// * The |result| will be free of duplicate or empty equivalence classes.
affiliation_pb::LookupAffiliationResponse response; FinalizeRequest(serialized_request, BuildQueryURL(), traffic_annotation);
if (!response.ParseFromString(serialized_response)) }
return false;
return ParseLookupAffiliationResponse(requested_facet_uris_, response, const std::vector<FacetURI>& AffiliationFetcher::GetRequestedFacetURIs() const {
result); return requested_facet_uris_;
} }
void AffiliationFetcher::OnSimpleLoaderComplete( // static
std::unique_ptr<std::string> response_body) { GURL AffiliationFetcher::BuildQueryURL() {
base::UmaHistogramTimes("PasswordManager.AffiliationFetcher.FetchTime", return net::AppendQueryParameter(
fetch_timer_.Elapsed()); GURL("https://www.googleapis.com/affiliation/v1/affiliation:lookup"),
// Note that invoking the |delegate_| may destroy |this| synchronously, so the "key", google_apis::GetAPIKey());
// invocation must happen last.
std::unique_ptr<AffiliationFetcherDelegate::Result> result_data(
new AffiliationFetcherDelegate::Result);
if (response_body) {
if (ParseResponse(*response_body, result_data.get())) {
UMA_HISTOGRAM_ENUMERATION(
"PasswordManager.AffiliationFetcher.FetchResult",
AFFILIATION_FETCH_RESULT_SUCCESS, AFFILIATION_FETCH_RESULT_MAX);
delegate_->OnFetchSucceeded(std::move(result_data));
} else {
UMA_HISTOGRAM_ENUMERATION(
"PasswordManager.AffiliationFetcher.FetchResult",
AFFILIATION_FETCH_RESULT_MALFORMED, AFFILIATION_FETCH_RESULT_MAX);
delegate_->OnMalformedResponse();
}
} else {
UMA_HISTOGRAM_ENUMERATION("PasswordManager.AffiliationFetcher.FetchResult",
AFFILIATION_FETCH_RESULT_FAILURE,
AFFILIATION_FETCH_RESULT_MAX);
int response_code = -1;
if (simple_url_loader_->ResponseInfo() &&
simple_url_loader_->ResponseInfo()->headers) {
response_code =
simple_url_loader_->ResponseInfo()->headers->response_code();
}
base::UmaHistogramSparse(
"PasswordManager.AffiliationFetcher.FetchHttpResponseCode",
response_code);
// Network error codes are negative. See: src/net/base/net_error_list.h.
base::UmaHistogramSparse(
"PasswordManager.AffiliationFetcher.FetchErrorCode",
-simple_url_loader_->NetError());
delegate_->OnFetchFailed();
}
} }
} // namespace password_manager } // namespace password_manager
...@@ -5,78 +5,29 @@ ...@@ -5,78 +5,29 @@
#ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_AFFILIATION_FETCHER_H_ #ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_AFFILIATION_FETCHER_H_
#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_AFFILIATION_FETCHER_H_ #define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_ANDROID_AFFILIATION_AFFILIATION_FETCHER_H_
#include <memory> #include "components/password_manager/core/browser/site_affiliation/affiliation_fetcher_base.h"
#include <string>
#include <vector>
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/timer/elapsed_timer.h"
#include "components/password_manager/core/browser/android_affiliation/affiliation_fetcher_interface.h"
class GURL;
namespace network {
class SharedURLLoaderFactory;
class SimpleURLLoader;
} // namespace network
namespace password_manager { namespace password_manager {
// Fetches authoritative information regarding which facets are affiliated with class AffiliationFetcher : public AffiliationFetcherBase {
// each other, that is, which facets belong to the same logical application.
// See affiliation_utils.h for a definition of what this means.
//
// An instance is good for exactly one fetch, and may be used from any thread
// that runs a message loop (i.e. not a worker pool thread).
class AffiliationFetcher : public AffiliationFetcherInterface {
public: public:
AffiliationFetcher( AffiliationFetcher(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
AffiliationFetcherDelegate* delegate); AffiliationFetcherDelegate* delegate);
~AffiliationFetcher() override; ~AffiliationFetcher() override;
// Builds the URL for the Affiliation API's lookup method. // AffiliationFetcherInterface
static GURL BuildQueryURL();
// Actually starts the request to retrieve affiliations and optionally
// groupings for each facet in |facet_uris| along with the details based on
// |request_info|. Calls the delegate with the results on the same thread when
// done. If |this| is destroyed before completion, the in-flight request is
// cancelled, and the delegate will not be called. Further details:
// * No cookies are sent/saved with the request.
// * In case of network/server errors, the request will not be retried.
// * Results are guaranteed to be always fresh and will never be cached.
void StartRequest(const std::vector<FacetURI>& facet_uris, void StartRequest(const std::vector<FacetURI>& facet_uris,
RequestInfo request_info) override; RequestInfo request_info) override;
// AffiliationFetcherInterface
const std::vector<FacetURI>& GetRequestedFacetURIs() const override; const std::vector<FacetURI>& GetRequestedFacetURIs() const override;
AffiliationFetcherDelegate* delegate() const; // Builds the URL for the Affiliation API's lookup method.
static GURL BuildQueryURL();
private: private:
// Prepares and returns the serialized protocol buffer message that will be
// the payload of the POST request. Sets mask request based on |request_info|.
std::string PreparePayload(RequestInfo request_info) const;
// Parses and validates the response protocol buffer message for a list of
// equivalence classes, stores them into |result| and returns true on success.
// It is guaranteed that every one of the requested Facet URIs will be a
// member of exactly one returned equivalence class.
// Returns false if the response was gravely ill-formed or self-inconsistent.
// Unknown kinds of facet URIs and new protocol buffer fields will be ignored.
bool ParseResponse(const std::string& serialized_response,
AffiliationFetcherDelegate::Result* result) const;
void OnSimpleLoaderComplete(std::unique_ptr<std::string> response_body);
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
std::vector<FacetURI> requested_facet_uris_; std::vector<FacetURI> requested_facet_uris_;
AffiliationFetcherDelegate* const delegate_;
// Timer to track the response time of the request.
base::ElapsedTimer fetch_timer_;
std::unique_ptr<network::SimpleURLLoader> simple_url_loader_;
}; };
} // namespace password_manager } // namespace password_manager
......
// Copyright 2020 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 "components/password_manager/core/browser/site_affiliation/affiliation_fetcher_base.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "components/password_manager/core/browser/android_affiliation/lookup_affiliation_response_parser.h"
#include "net/base/load_flags.h"
#include "net/base/url_util.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"
namespace password_manager {
// Enumeration listing the possible outcomes of fetching affiliation information
// from the Affiliation API. This is used in UMA histograms, so do not change
// existing values, only add new values at the end.
enum class AffiliationFetchResult {
kSuccess = 0,
kFailure = 1,
kMalformed = 2,
kMaxValue = kMalformed,
};
namespace {
void LogFetchResult(AffiliationFetchResult result) {
base::UmaHistogramEnumeration(
"PasswordManager.AffiliationFetcher.FetchResult", result);
}
} // namespace
affiliation_pb::LookupAffiliationMask CreateLookupMask(
const AffiliationFetcherInterface::RequestInfo& request_info) {
affiliation_pb::LookupAffiliationMask mask;
mask.set_branding_info(request_info.branding_info);
// Change password info requires grouping info enabled.
mask.set_grouping_info(request_info.change_password_info);
mask.set_change_password_info(request_info.change_password_info);
return mask;
}
AffiliationFetcherBase::AffiliationFetcherBase(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
AffiliationFetcherDelegate* delegate)
: url_loader_factory_(std::move(url_loader_factory)), delegate_(delegate) {}
AffiliationFetcherBase::~AffiliationFetcherBase() = default;
AffiliationFetcherDelegate* AffiliationFetcherBase::delegate() const {
return delegate_;
}
void AffiliationFetcherBase::FinalizeRequest(
const std::string& payload,
const GURL& query_url,
net::NetworkTrafficAnnotationTag traffic_annotation) {
fetch_timer_ = base::ElapsedTimer();
auto resource_request = std::make_unique<network::ResourceRequest>();
resource_request->url = query_url;
resource_request->load_flags =
net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE;
resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
resource_request->method = "POST";
DCHECK(!simple_url_loader_);
simple_url_loader_ = network::SimpleURLLoader::Create(
std::move(resource_request), traffic_annotation);
simple_url_loader_->AttachStringForUpload(payload, "application/x-protobuf");
simple_url_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
url_loader_factory_.get(),
base::BindOnce(&AffiliationFetcherBase::OnSimpleLoaderComplete,
base::Unretained(this)));
}
bool AffiliationFetcherBase::ParseResponse(
const std::string& serialized_response,
AffiliationFetcherDelegate::Result* result) const {
// This function parses the response protocol buffer message for a list of
// equivalence classes, and stores them into |results| after performing some
// validation and sanitization steps to make sure that the contract of
// AffiliationFetcherDelegate is fulfilled. Possible discrepancies are:
// * The server response will not have anything for facets that are not
// affiliated with any other facet, while |result| must have them.
// * The server response might contain future, unknown kinds of facet URIs,
// while |result| must contain only those that are FacetURI::is_valid().
// * The server response being ill-formed or self-inconsistent (in the sense
// that there are overlapping equivalence classes) is indicative of server
// side issues likely not remedied by re-fetching. Report failure in this
// case so the caller can be notified and it can act accordingly.
// * The |result| will be free of duplicate or empty equivalence classes.
affiliation_pb::LookupAffiliationResponse response;
if (!response.ParseFromString(serialized_response))
return false;
return ParseLookupAffiliationResponse(GetRequestedFacetURIs(), response,
result);
}
void AffiliationFetcherBase::OnSimpleLoaderComplete(
std::unique_ptr<std::string> response_body) {
base::UmaHistogramTimes("PasswordManager.AffiliationFetcher.FetchTime",
fetch_timer_.Elapsed());
// Note that invoking the |delegate_| may destroy |this| synchronously, so the
// invocation must happen last.
auto result_data = std::make_unique<AffiliationFetcherDelegate::Result>();
if (response_body) {
if (ParseResponse(*response_body, result_data.get())) {
LogFetchResult(AffiliationFetchResult::kSuccess);
delegate_->OnFetchSucceeded(std::move(result_data));
} else {
LogFetchResult(AffiliationFetchResult::kMalformed);
delegate_->OnMalformedResponse();
}
} else {
LogFetchResult(AffiliationFetchResult::kFailure);
int response_code = -1;
if (simple_url_loader_->ResponseInfo() &&
simple_url_loader_->ResponseInfo()->headers) {
response_code =
simple_url_loader_->ResponseInfo()->headers->response_code();
}
base::UmaHistogramSparse(
"PasswordManager.AffiliationFetcher.FetchHttpResponseCode",
response_code);
// Network error codes are negative. See: src/net/base/net_error_list.h.
base::UmaHistogramSparse(
"PasswordManager.AffiliationFetcher.FetchErrorCode",
-simple_url_loader_->NetError());
delegate_->OnFetchFailed();
}
}
} // namespace password_manager
// Copyright 2020 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.
#ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_SITE_AFFILIATION_AFFILIATION_FETCHER_BASE_H_
#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_SITE_AFFILIATION_AFFILIATION_FETCHER_BASE_H_
#include <string>
#include "components/password_manager/core/browser/android_affiliation/affiliation_fetcher_interface.h"
#include "base/memory/ref_counted.h"
#include "base/timer/elapsed_timer.h"
#include "components/password_manager/core/browser/android_affiliation/affiliation_api.pb.h"
namespace net {
struct NetworkTrafficAnnotationTag;
}
namespace network {
class SharedURLLoaderFactory;
class SimpleURLLoader;
} // namespace network
namespace password_manager {
// Creates lookup mask based on |request_info|.
affiliation_pb::LookupAffiliationMask CreateLookupMask(
const AffiliationFetcherInterface::RequestInfo& request_info);
// A base class for affiliation fetcher. Should not be used directly.
//
// Fetches authoritative information regarding which facets are affiliated with
// each other, that is, which facets belong to the same logical application.
// Apart from affiliations the service also supports groups and other details,
// all of which have to be specified when starting a request.
// See affiliation_utils.h for the definitions.
//
// An instance is good for exactly one fetch, and may be used from any thread
// that runs a message loop (i.e. not a worker pool thread).
class AffiliationFetcherBase : public virtual AffiliationFetcherInterface {
public:
~AffiliationFetcherBase() override;
AffiliationFetcherDelegate* delegate() const;
protected:
AffiliationFetcherBase(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
AffiliationFetcherDelegate* delegate);
// Actually starts the request to retrieve affiliations and optionally
// groupings for each facet in |facet_uris| along with the details based on
// |request_info|. Calls the delegate with the results on the same thread when
// done. If |this| is destroyed before completion, the in-flight request is
// cancelled, and the delegate will not be called. Further details:
// * No cookies are sent/saved with the request.
// * In case of network/server errors, the request will not be retried.
// * Results are guaranteed to be always fresh and will never be cached.
void FinalizeRequest(const std::string& payload,
const GURL& query_url,
net::NetworkTrafficAnnotationTag traffic_annotation);
private:
// Parses and validates the response protocol buffer message for a list of
// equivalence classes, stores them into |result| and returns true on success.
// It is guaranteed that every one of the requested Facet URIs will be a
// member of exactly one returned equivalence class.
// Returns false if the response was gravely ill-formed or self-inconsistent.
// Unknown kinds of facet URIs and new protocol buffer fields will be ignored.
bool ParseResponse(const std::string& serialized_response,
AffiliationFetcherDelegate::Result* result) const;
void OnSimpleLoaderComplete(std::unique_ptr<std::string> response_body);
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
AffiliationFetcherDelegate* const delegate_;
base::ElapsedTimer fetch_timer_;
std::unique_ptr<network::SimpleURLLoader> simple_url_loader_;
};
} // namespace password_manager
#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_SITE_AFFILIATION_AFFILIATION_FETCHER_BASE_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment