OLD | NEW |
(Empty) | |
| 1 // Copyright 2014 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 "components/password_manager/core/browser/affiliation_fetcher.h" |
| 6 |
| 7 #include "components/password_manager/core/browser/affiliation_api.pb.h" |
| 8 #include "components/password_manager/core/browser/affiliation_utils.h" |
| 9 #include "google_apis/google_api_keys.h" |
| 10 #include "net/base/load_flags.h" |
| 11 #include "net/base/url_util.h" |
| 12 #include "net/http/http_status_code.h" |
| 13 #include "net/url_request/url_fetcher.h" |
| 14 #include "net/url_request/url_request_context_getter.h" |
| 15 #include "url/gurl.h" |
| 16 |
| 17 namespace password_manager { |
| 18 |
| 19 AffiliationFetcher::AffiliationFetcher( |
| 20 net::URLRequestContextGetter* request_context_getter, |
| 21 const std::vector<FacetURI>& facet_uris, |
| 22 AffiliationFetcherDelegate* delegate) |
| 23 : request_context_getter_(request_context_getter), |
| 24 requested_facet_uris_(facet_uris), |
| 25 delegate_(delegate) { |
| 26 for (const FacetURI& uri : requested_facet_uris_) { |
| 27 DCHECK(uri.is_valid()); |
| 28 } |
| 29 } |
| 30 |
| 31 AffiliationFetcher::~AffiliationFetcher() { |
| 32 } |
| 33 |
| 34 // static |
| 35 AffiliationFetcher* AffiliationFetcher::Create( |
| 36 net::URLRequestContextGetter* context_getter, |
| 37 const std::vector<FacetURI>& facet_uris, |
| 38 AffiliationFetcherDelegate* delegate) { |
| 39 return new AffiliationFetcher(context_getter, facet_uris, delegate); |
| 40 } |
| 41 |
| 42 void AffiliationFetcher::StartRequest() { |
| 43 DCHECK(!fetcher_); |
| 44 |
| 45 fetcher_.reset( |
| 46 net::URLFetcher::Create(BuildQueryURL(), net::URLFetcher::POST, this)); |
| 47 fetcher_->SetRequestContext(request_context_getter_.get()); |
| 48 fetcher_->SetUploadData("application/x-protobuf", PreparePayload()); |
| 49 fetcher_->SetLoadFlags( |
| 50 net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES | |
| 51 net::LOAD_DO_NOT_SEND_AUTH_DATA | net::LOAD_BYPASS_CACHE | |
| 52 net::LOAD_DISABLE_CACHE | net::LOAD_DO_NOT_PROMPT_FOR_LOGIN); |
| 53 fetcher_->SetAutomaticallyRetryOn5xx(false); |
| 54 fetcher_->SetAutomaticallyRetryOnNetworkChanges(0); |
| 55 fetcher_->Start(); |
| 56 } |
| 57 |
| 58 GURL AffiliationFetcher::BuildQueryURL() const { |
| 59 return net::AppendQueryParameter( |
| 60 GURL("https://www.googleapis.com/affiliation/v1/affiliation:lookup"), |
| 61 "key", google_apis::GetAPIKey()); |
| 62 } |
| 63 |
| 64 std::string AffiliationFetcher::PreparePayload() const { |
| 65 affiliation_pb::LookupAffiliationRequest lookup_request; |
| 66 for (const FacetURI& uri : requested_facet_uris_) |
| 67 lookup_request.add_facet(uri.canonical_spec()); |
| 68 |
| 69 std::string serialized_request; |
| 70 bool success = lookup_request.SerializeToString(&serialized_request); |
| 71 DCHECK(success); |
| 72 return serialized_request; |
| 73 } |
| 74 |
| 75 bool AffiliationFetcher::ParseResponse( |
| 76 AffiliationFetcherDelegate::Result* result) const { |
| 77 // This function parses the response protocol buffer message for a list of |
| 78 // equivalence classes, and stores them into |results| after performing some |
| 79 // validation and sanitization steps to make sure that the contract of |
| 80 // AffiliationFetcherDelegate is fulfilled. Possible discrepancies are: |
| 81 // * The server response will not have anything for facets that are not |
| 82 // affiliated with any other facet, while |result| must have them. |
| 83 // * The server response might contain future, unknown kinds of facet URIs, |
| 84 // while |result| must contain only those that are FacetURI::is_valid(). |
| 85 // * The server response being ill-formed or self-inconsistent (in the sense |
| 86 // that there are overlapping equivalence classes) is indicative of server |
| 87 // side issues likely not remedied by re-fetching. Report failure in this |
| 88 // case so the caller can be notified and it can act accordingly. |
| 89 // * The |result| will be free of duplicate or empty equivalence classes. |
| 90 |
| 91 std::string serialized_response; |
| 92 if (!fetcher_->GetResponseAsString(&serialized_response)) { |
| 93 NOTREACHED(); |
| 94 } |
| 95 |
| 96 affiliation_pb::LookupAffiliationResponse response; |
| 97 if (!response.ParseFromString(serialized_response)) |
| 98 return false; |
| 99 |
| 100 result->reserve(requested_facet_uris_.size()); |
| 101 |
| 102 std::map<FacetURI, size_t> facet_uri_to_class_index; |
| 103 for (int i = 0; i < response.affiliation_size(); ++i) { |
| 104 const affiliation_pb::Affiliation& equivalence_class( |
| 105 response.affiliation(i)); |
| 106 |
| 107 AffiliatedFacets affiliated_uris; |
| 108 for (int j = 0; j < equivalence_class.facet_size(); ++j) { |
| 109 const std::string& uri_spec(equivalence_class.facet(j)); |
| 110 FacetURI uri = FacetURI::FromPotentiallyInvalidSpec(uri_spec); |
| 111 // Ignore potential future kinds of facet URIs (e.g. for new platforms). |
| 112 if (!uri.is_valid()) |
| 113 continue; |
| 114 affiliated_uris.push_back(uri); |
| 115 } |
| 116 |
| 117 // Be lenient and ignore empty (after filtering) equivalence classes. |
| 118 if (affiliated_uris.empty()) |
| 119 continue; |
| 120 |
| 121 // Ignore equivalence classes that are duplicates of earlier ones. However, |
| 122 // fail in the case of a partial overlap, which violates the invariant that |
| 123 // affiliations must form an equivalence relation. |
| 124 for (const FacetURI& uri : affiliated_uris) { |
| 125 if (!facet_uri_to_class_index.count(uri)) |
| 126 facet_uri_to_class_index[uri] = result->size(); |
| 127 if (facet_uri_to_class_index[uri] != |
| 128 facet_uri_to_class_index[affiliated_uris[0]]) { |
| 129 return false; |
| 130 } |
| 131 } |
| 132 |
| 133 // Filter out duplicate equivalence classes in the response. |
| 134 if (facet_uri_to_class_index[affiliated_uris[0]] == result->size()) |
| 135 result->push_back(affiliated_uris); |
| 136 } |
| 137 |
| 138 // Synthesize an equivalence class (of size one) for each facet that did not |
| 139 // appear in the server response due to not being affiliated with any others. |
| 140 for (const FacetURI& uri : requested_facet_uris_) { |
| 141 if (!facet_uri_to_class_index.count(uri)) |
| 142 result->push_back(AffiliatedFacets(1, uri)); |
| 143 } |
| 144 |
| 145 return true; |
| 146 } |
| 147 |
| 148 void AffiliationFetcher::OnURLFetchComplete(const net::URLFetcher* source) { |
| 149 DCHECK_EQ(source, fetcher_.get()); |
| 150 |
| 151 scoped_ptr<AffiliationFetcherDelegate::Result> result( |
| 152 new AffiliationFetcherDelegate::Result); |
| 153 if (fetcher_->GetStatus().status() == net::URLRequestStatus::SUCCESS && |
| 154 fetcher_->GetResponseCode() == net::HTTP_OK) { |
| 155 if (ParseResponse(result.get())) |
| 156 delegate_->OnFetchSucceeded(result.Pass()); |
| 157 else |
| 158 delegate_->OnMalformedResponse(); |
| 159 } else { |
| 160 delegate_->OnFetchFailed(); |
| 161 } |
| 162 } |
| 163 |
| 164 } // namespace password_manager |
OLD | NEW |