Chromium Code Reviews| 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 "base/json/json_reader.h" | |
| 8 #include "base/json/json_writer.h" | |
| 9 #include "base/values.h" | |
| 10 #include "components/password_manager/core/browser/affiliation_utils.h" | |
| 11 #include "google_apis/google_api_keys.h" | |
| 12 #include "net/base/load_flags.h" | |
| 13 #include "net/base/url_util.h" | |
| 14 #include "net/http/http_status_code.h" | |
| 15 #include "net/url_request/url_fetcher.h" | |
| 16 #include "net/url_request/url_request_context_getter.h" | |
| 17 #include "url/gurl.h" | |
| 18 | |
| 19 namespace password_manager { | |
| 20 | |
| 21 namespace { | |
| 22 | |
| 23 const char kAffilitionServiceLookupURL[] = | |
| 24 "https://www.googleapis.com/affiliation/v1/affiliation:lookup"; | |
| 25 | |
| 26 // In the JSON dictionary of the request, the key that maps to the list of facet | |
| 27 // URIs to query. | |
| 28 const char kRequestFacetURIsKey[] = "facet"; | |
| 29 | |
| 30 // In the JSON dictionary of the response, the key that maps to the the list of | |
| 31 // equivalence classes, each being a dictionary itself. | |
| 32 const char kResponseEquivalenceClassesKey[] = "affiliation"; | |
| 33 | |
| 34 // In the JSON dictionary of one equivalence class, the key that maps to the | |
| 35 // list of facet URIs in that class. | |
| 36 const char kResponseEquivalenceClassMembersKey[] = "facet"; | |
|
Mike West
2014/12/02 14:32:42
Hrm. You're only using each of these keys once. Wh
engedy
2014/12/02 18:33:55
They shouldn't any other classes that need this, s
| |
| 37 | |
| 38 // If set, then this factory will be used instead of the vanilla constructor for | |
| 39 // constructing AffiliationFetcher instances. Used only for testing. | |
| 40 AffiliationFetcherFactory* g_factory = nullptr; | |
| 41 | |
| 42 } // namespace | |
| 43 | |
| 44 AffiliationFetcher::AffiliationFetcher( | |
| 45 net::URLRequestContextGetter* request_context_getter, | |
| 46 const std::vector<std::string>& facet_uris, | |
| 47 AffiliationFetcherDelegate* delegate) | |
| 48 : request_context_getter_(request_context_getter), | |
| 49 requested_facet_uris_(facet_uris), | |
| 50 delegate_(delegate) { | |
| 51 | |
|
Mike West
2014/12/02 14:32:42
Nit: Whitespace.
engedy
2014/12/02 18:33:55
Done.
| |
| 52 for (const auto& uri : requested_facet_uris_) { | |
|
Mike West
2014/12/02 14:32:42
Consider dropping "auto" here; the actual type isn
engedy
2014/12/02 18:33:55
Done.
| |
| 53 DCHECK(IsValidFacetURI(uri)); | |
| 54 } | |
|
Mike West
2014/12/02 14:32:42
Nit: No {} for a one-line body.
engedy
2014/12/02 18:33:55
Done.
| |
| 55 } | |
| 56 | |
| 57 AffiliationFetcher::~AffiliationFetcher() { | |
| 58 } | |
| 59 | |
| 60 // static | |
| 61 AffiliationFetcher* AffiliationFetcher::Create( | |
| 62 net::URLRequestContextGetter* context_getter, | |
| 63 const std::vector<std::string>& facet_uris, | |
| 64 AffiliationFetcherDelegate* delegate) { | |
| 65 if (g_factory) | |
| 66 return g_factory->CreateInstance(context_getter, facet_uris, delegate); | |
| 67 return new AffiliationFetcher(context_getter, facet_uris, delegate); | |
| 68 } | |
| 69 | |
| 70 // static | |
| 71 void AffiliationFetcher::SetFactoryForTesting( | |
| 72 AffiliationFetcherFactory* factory) { | |
| 73 g_factory = factory; | |
| 74 } | |
| 75 | |
| 76 void AffiliationFetcher::StartRequest() { | |
| 77 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 78 | |
| 79 if (fetcher_) { | |
| 80 NOTREACHED(); | |
|
Mike West
2014/12/02 14:32:42
Perhaps just `DCHECK(!fetcher_)`?
engedy
2014/12/02 18:33:55
Done.
| |
| 81 return; | |
| 82 } | |
| 83 | |
| 84 fetcher_.reset( | |
| 85 net::URLFetcher::Create(BuildQueryURL(), net::URLFetcher::POST, this)); | |
| 86 fetcher_->SetRequestContext(request_context_getter_.get()); | |
| 87 fetcher_->SetUploadData("application/json", PreparePayload()); | |
| 88 fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES | | |
| 89 net::LOAD_DO_NOT_SEND_COOKIES | | |
| 90 net::LOAD_DO_NOT_SEND_AUTH_DATA | | |
|
Mike West
2014/12/02 14:32:42
Probably should add net::LOAD_DO_NOT_PROMPT_FOR_LO
engedy
2014/12/02 18:33:55
Done.
| |
| 91 net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE); | |
|
Mike West
2014/12/02 14:32:42
Why both bypass and disable? Disabling should be e
engedy
2014/12/02 18:33:55
I think both are needed.
DISABLE alone would not
| |
| 92 fetcher_->SetAutomaticallyRetryOn5xx(false); | |
| 93 fetcher_->SetAutomaticallyRetryOnNetworkChanges(0); | |
| 94 fetcher_->Start(); | |
| 95 } | |
| 96 | |
| 97 GURL AffiliationFetcher::BuildQueryURL() const { | |
| 98 return net::AppendQueryParameter(GURL(kAffilitionServiceLookupURL), "key", | |
| 99 google_apis::GetAPIKey()); | |
| 100 } | |
| 101 | |
| 102 std::string AffiliationFetcher::PreparePayload() const { | |
| 103 scoped_ptr<base::ListValue> requested_facet_uris_list(new base::ListValue); | |
| 104 for (const auto& uri : requested_facet_uris_) { | |
|
Mike West
2014/12/02 14:32:42
Consider just using the type rather than auto.
engedy
2014/12/02 18:33:55
Done.
| |
| 105 requested_facet_uris_list->AppendString(uri); | |
| 106 } | |
|
Mike West
2014/12/02 14:32:42
Nit: No {}.
engedy
2014/12/02 18:33:55
Done.
| |
| 107 | |
| 108 base::DictionaryValue payload_dictionary; | |
| 109 payload_dictionary.Set(kRequestFacetURIsKey, | |
| 110 requested_facet_uris_list.release()); | |
| 111 | |
| 112 std::string payload_json; | |
| 113 if (!base::JSONWriter::Write(&payload_dictionary, &payload_json)) { | |
|
Mike West
2014/12/02 14:32:42
Consider using OPTIONS_PRETTY_PRINT while in Debug
engedy
2014/12/02 18:33:55
For now, I would not complicate the code with this
| |
| 114 // This should never really happen. | |
|
Mike West
2014/12/02 14:32:42
DCHECK?
engedy
2014/12/02 18:33:55
Done.
| |
| 115 NOTREACHED(); | |
| 116 } | |
| 117 return payload_json; | |
| 118 } | |
| 119 | |
| 120 bool AffiliationFetcher::ParseResponse(Result* result) const { | |
| 121 std::string response_json; | |
| 122 if (!fetcher_->GetResponseAsString(&response_json)) | |
| 123 return false; | |
| 124 | |
| 125 scoped_ptr<const base::Value> response_value( | |
| 126 (base::JSONReader::Read(response_json))); | |
|
Mike West
2014/12/02 14:32:42
Nit: Extra ().
engedy
2014/12/02 18:33:55
Done.
| |
| 127 if (!response_value) | |
| 128 return false; | |
| 129 | |
| 130 const base::DictionaryValue* response_dict; | |
| 131 if (!response_value->GetAsDictionary(&response_dict)) | |
| 132 return false; | |
| 133 | |
| 134 const base::ListValue* equivalence_classes_list; | |
| 135 if (!response_dict->GetList(kResponseEquivalenceClassesKey, | |
| 136 &equivalence_classes_list)) | |
| 137 return false; | |
|
Mike West
2014/12/02 14:32:42
You might find it helpful to have a bit more error
engedy
2014/12/02 18:33:55
Hmm, what kind of scenario do you have in mind? Th
Mike West
2014/12/03 10:44:43
I was thinking about VLOGs, as I imagine you'll be
engedy
2014/12/09 10:33:45
Done.
| |
| 138 | |
| 139 // Reserve enough space to avoid costly reallocations. We will return at most | |
| 140 // one equivalence class per requested facet, so use that as an upper bound. | |
| 141 // Note that the size of |equivalence_classes_list| is not necessarily enough, | |
| 142 // as it may be missing classes artificially added by this function later. | |
| 143 result->reserve(requested_facet_uris_.size()); | |
| 144 | |
| 145 std::map<std::string, size_t> facet_uri_to_class_index; | |
| 146 for (size_t i = 0; i < equivalence_classes_list->GetSize(); ++i) { | |
| 147 const base::DictionaryValue* equivalence_class_dict; | |
| 148 if (!equivalence_classes_list->GetDictionary(i, &equivalence_class_dict)) | |
| 149 return false; | |
| 150 | |
| 151 const base::ListValue* equivalence_class_members_list; | |
| 152 if (!equivalence_class_dict->GetList(kResponseEquivalenceClassMembersKey, | |
| 153 &equivalence_class_members_list)) | |
| 154 return false; | |
|
Mike West
2014/12/03 10:44:43
Nit: You need {} here, as the conditional is multi
engedy
2014/12/09 10:33:45
Done.
| |
| 155 | |
| 156 AffiliatedFacets affiliated_uris; | |
| 157 for (size_t j = 0; j < equivalence_class_members_list->GetSize(); ++j) { | |
| 158 std::string uri; | |
| 159 if (!equivalence_class_members_list->GetString(j, &uri)) | |
| 160 return false; | |
| 161 | |
| 162 // Ignore potential future kinds of facet URIs (e.g. for new platforms). | |
| 163 if (!IsValidFacetURI(uri)) | |
| 164 continue; | |
| 165 | |
| 166 affiliated_uris.push_back(uri); | |
| 167 } | |
| 168 | |
| 169 // Be lenient and ignore empty (after filtering) equivalence classes. | |
| 170 if (affiliated_uris.empty()) | |
| 171 continue; | |
| 172 | |
| 173 // Ignore equivalence classes if they are a duplicate of an earlier one. | |
| 174 // However, bail out if we discover partial overlapping, in which case the | |
| 175 // response cannot be part of an equivalence relation. | |
| 176 for (const auto& uri : affiliated_uris) { | |
| 177 if (!facet_uri_to_class_index.count(uri)) | |
| 178 facet_uri_to_class_index[uri] = result->size(); | |
| 179 if (facet_uri_to_class_index[uri] != | |
| 180 facet_uri_to_class_index[affiliated_uris[0]]) | |
| 181 return false; | |
|
Mike West
2014/12/03 10:44:43
Nit: {}.
engedy
2014/12/09 10:33:45
Done.
| |
| 182 } | |
| 183 | |
| 184 if (facet_uri_to_class_index[affiliated_uris[0]] == result->size()) | |
|
Mike West
2014/12/03 10:44:43
Isn't this always true if the conditions on 179-18
engedy
2014/12/09 10:33:45
I have added a comment to clarify.
| |
| 185 result->push_back(affiliated_uris); | |
| 186 } | |
| 187 | |
| 188 // The server does not return at all facet URIs that are not affiliated with | |
| 189 // anything, or facet URIs that it does not know about. However, this class | |
| 190 // promises to return an equivalence class for each requested facet, so create | |
| 191 // one for each each of these missing facets. | |
| 192 for (const auto& uri : requested_facet_uris_) { | |
| 193 if (!facet_uri_to_class_index.count(uri)) { | |
| 194 result->resize(result->size() + 1); | |
| 195 result->back().push_back(uri); | |
| 196 } | |
| 197 } | |
| 198 | |
| 199 return true; | |
| 200 } | |
| 201 | |
| 202 void AffiliationFetcher::OnURLFetchComplete(const net::URLFetcher* source) { | |
| 203 DCHECK_EQ(source, fetcher_.get()); | |
| 204 | |
| 205 scoped_ptr<Result> result(new Result); | |
| 206 if (fetcher_->GetResponseCode() == net::HTTP_OK) { | |
| 207 if (ParseResponse(result.get())) | |
| 208 delegate_->OnFetchSucceeded(result.Pass()); | |
| 209 else | |
| 210 delegate_->OnMalformedResponse(); | |
| 211 } else { | |
| 212 delegate_->OnFetchFailed(); | |
| 213 } | |
| 214 } | |
| 215 | |
| 216 } // namespace password_manager | |
| OLD | NEW |