Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(207)

Side by Side Diff: components/password_manager/core/browser/affiliation_utils.cc

Issue 771173002: Added utility functions related to working with "facets". (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Have proper includes. Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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_utils.h"
6
7 #include "base/base64.h"
8 #include "base/strings/string_piece.h"
9 #include "base/strings/string_util.h"
10 #include "net/base/escape.h"
11 #include "url/third_party/mozilla/url_parse.h"
12 #include "url/url_canon_stdstring.h"
13 #include "url/url_util.h"
14
15 namespace password_manager {
16
17 namespace {
18
19 // The scheme used for identifying Android applications.
20 const char kAndroidAppScheme[] = "android";
21
22 // Returns a StringPiece corresponding to |component| in |uri|, or the empty
23 // string in case there is no such component.
24 base::StringPiece ComponentString(const std::string& uri,
25 const url::Component& component) {
26 if (!component.is_valid())
27 return base::StringPiece();
28 return base::StringPiece(uri.c_str() + component.begin, component.len);
29 }
30
31 // Extracts the scheme of an unparsed |uri| as a StringPiece, or returns the
32 // empty string on failure.
33 base::StringPiece ExtractScheme(const std::string& uri) {
34 url::Component scheme_component;
35 if (url::ExtractScheme(uri.c_str(), uri.size(), &scheme_component))
36 return ComponentString(uri, scheme_component);
37 return base::StringPiece();
38 }
39
40 // Returns true if the passed ASCII |input| string contains nothing else than
41 // alphanumeric characters and those in |other_characters|.
42 bool ContainsOnlyAlphanumericAnd(const base::StringPiece& input,
Mike West 2014/12/09 12:13:34 I'd suggest that this method accept an enum rather
engedy 2014/12/09 14:54:46 I have added name constants at the call sites, PTA
43 const base::StringPiece& other_characters) {
44 for (char c : input) {
45 if (!IsAsciiAlpha(c) && !IsAsciiDigit(c) &&
46 other_characters.find(c) == base::StringPiece::npos)
47 return false;
48 }
49 return true;
50 }
51
52 // Canonicalizes a Web facet URI, and returns true if canonicalization was
53 // successful and produced a valid URI. Even in case of a failure, something
54 // meaningful will be written to |canonical_uri|.
55 bool CanonicalizeWebFacetURI(const std::string& input_uri,
56 const url::Parsed& input_parsed,
57 std::string* canonical_uri) {
58 url::Parsed canonical_parsed;
59 url::StdStringCanonOutput canonical_output(canonical_uri);
60
61 bool canonicalization_succeeded = url::CanonicalizeStandardURL(
62 input_uri.c_str(), input_uri.size(), input_parsed, NULL,
63 &canonical_output, &canonical_parsed);
64 canonical_output.Complete();
65
66 if (canonicalization_succeeded &&
67 canonical_parsed.host.is_nonempty() &&
68 !canonical_parsed.username.is_valid() &&
69 !canonical_parsed.password.is_valid() &&
70 ComponentString(*canonical_uri, canonical_parsed.path) == "/" &&
71 !canonical_parsed.query.is_valid() && !canonical_parsed.ref.is_valid()) {
72 // Get rid of the trailing slash added by url::CanonicalizeStandardURL().
73 DCHECK_EQ((size_t)canonical_parsed.path.begin, canonical_uri->size() - 1);
74 canonical_uri->erase(canonical_parsed.path.begin,
75 canonical_parsed.path.len);
76 return true;
77 }
78 return false;
79 }
80
81 // Canonicalizes the username component in an Android facet URI (containing the
82 // certificate hash), and returns true if canonicalization was successful and
83 // produced a valid non-empty component. Even in case of a failure, something
84 // meaningful will be written to |canonical_output|.
85 bool CanonicalizeHashComponent(const base::StringPiece& input_hash,
Mike West 2014/12/09 12:13:34 It doesn't look like you're canonicalizing the str
engedy 2014/12/09 14:54:46 Done.
86 url::CanonOutput* canonical_output) {
87 // We need net::UnescapeRule::URL_SPECIAL_CHARS for the '='.
Mike West 2014/12/09 12:13:34 For which '='? The padding at the end of the hash?
engedy 2014/12/09 14:54:46 Yes, clarified.
88 std::string base64_encoded_hash = net::UnescapeURLComponent(
89 input_hash.as_string(), net::UnescapeRule::URL_SPECIAL_CHARS);
90 if (!base64_encoded_hash.empty()) {
91 canonical_output->Append(base64_encoded_hash.data(),
92 base64_encoded_hash.size());
93 canonical_output->push_back('@');
94 }
95
96 size_t first_padding = base64_encoded_hash.find_first_of('=');
97 return !base64_encoded_hash.empty() && base64_encoded_hash.size() % 4 == 0 &&
98 ContainsOnlyAlphanumericAnd(base64_encoded_hash, "-_=") &&
99 (first_padding == std::string::npos ||
Mike West 2014/12/09 12:13:34 I think it makes sense to extract the padding chec
engedy 2014/12/09 14:54:46 Done.
100 first_padding == base64_encoded_hash.size() - 1 ||
101 (first_padding == base64_encoded_hash.size() - 2 &&
102 base64_encoded_hash[base64_encoded_hash.size() - 1] == '='));
103 }
104
105 // Canonicalizes the host component in an Android facet URI (containing the
106 // package name), and returns true if canonicalization was successful and
107 // produced a valid non-empty component. Even in case of a failure, something
108 // meaningful will be written to |canonical_output|.
Mike West 2014/12/09 12:13:34 Why would you write something even in a failure ca
engedy 2014/12/09 14:54:46 As discussed, this was for consistency with url_ca
109 bool CanonicalizePackageNameComponent(
110 const base::StringPiece& input_package_name,
111 url::CanonOutput* canonical_output) {
112 std::string package_name = net::UnescapeURLComponent(
113 input_package_name.as_string(), net::UnescapeRule::NORMAL);
114 canonical_output->Append(package_name.data(), package_name.size());
115
116 // TODO(engedy): We might want to use a regex to check this more throughly.
117 return !package_name.empty() &&
118 ContainsOnlyAlphanumericAnd(package_name, "_.");
119 }
120
121 // Canonicalizes an Android facet URI, and returns true if canonicalization was
122 // successful and produced a valid URI. Even in case of a failure, something
123 // meaningful will be written to |canonical_uri|.
Mike West 2014/12/09 12:13:34 Ditto: if you've failed, why write anything to the
engedy 2014/12/09 14:54:46 Done.
124 bool CanonicalizeAndroidFacetURI(const std::string& input_uri,
125 const url::Parsed& input_parsed,
126 std::string* canonical_uri) {
127 url::StdStringCanonOutput canonical_output(canonical_uri);
128
129 url::Component unused;
130 bool success = url::CanonicalizeScheme(
131 input_uri.c_str(), input_parsed.scheme, &canonical_output, &unused);
132
133 canonical_output.push_back('/');
134 canonical_output.push_back('/');
135
136 // We cannot use url::CanonicalizeUserInfo as that would percent encode the
137 // the base64 padding characters ('=').
138 success &= CanonicalizeHashComponent(
139 ComponentString(input_uri, input_parsed.username), &canonical_output);
140
141 // We cannot use url::CanonicalizeHost as that would convert the package name
142 // to lower case, but the package name is case sensitive.
143 success &= CanonicalizePackageNameComponent(
144 ComponentString(input_uri.data(), input_parsed.host), &canonical_output);
145
146 canonical_output.Complete();
147
148 return success && !input_parsed.password.is_nonempty() &&
149 (!input_parsed.path.is_nonempty() ||
150 ComponentString(input_uri, input_parsed.path) == "/") &&
151 !input_parsed.port.is_nonempty() && !input_parsed.query.is_valid() &&
152 !input_parsed.ref.is_valid();
153 }
154
155 // Computes the canonicalized form of |uri| into |canonical_uri|, and returns
156 // whether or not the result is a valid facet URI. Even if the result is not
157 // valid, |canonical_uri| will contain something reasonable.
158 bool ParseAndCanonicalizeFacetURI(const std::string& input_uri,
159 std::string* canonical_uri) {
160 DCHECK(canonical_uri);
161 canonical_uri->clear();
162 canonical_uri->reserve(input_uri.size() + 32);
163
164 url::Parsed input_parsed;
165 url::ParseStandardURL(input_uri.c_str(), input_uri.size(), &input_parsed);
166
167 base::StringPiece scheme = ComponentString(input_uri, input_parsed.scheme);
168 if (url::LowerCaseEqualsASCII(scheme.begin(), scheme.end(),
169 url::kHttpsScheme)) {
170 return CanonicalizeWebFacetURI(input_uri, input_parsed, canonical_uri);
171 } else if (url::LowerCaseEqualsASCII(scheme.begin(), scheme.end(),
172 kAndroidAppScheme)) {
173 return CanonicalizeAndroidFacetURI(input_uri, input_parsed, canonical_uri);
174 }
175 return false;
176 }
177
178 } // namespace
179
180 FacetURI::FacetURI() : is_valid_(false) {
181 }
182
183 // static
184 FacetURI FacetURI::FromPotentiallyInvalidSpec(const std::string& spec) {
185 std::string canonical_spec;
186 bool is_valid = ParseAndCanonicalizeFacetURI(spec, &canonical_spec);
187 return FacetURI(canonical_spec, is_valid);
188 }
189
190 // static
191 FacetURI FacetURI::FromCanonicalSpec(const std::string& canonical_spec) {
192 return FacetURI(canonical_spec, true);
193 }
194
195 bool FacetURI::operator==(const FacetURI& other) const {
196 DCHECK(is_valid_);
197 DCHECK(other.is_valid_);
Mike West 2014/12/09 12:13:34 Hrm. DCHECK seems like a bit much here; perhaps it
engedy 2014/12/09 14:54:46 I would prefer to keep it. The factory method clea
198 return canonical_spec_ == other.canonical_spec_;
199 }
200
201 bool FacetURI::operator!=(const FacetURI& other) const {
202 DCHECK(is_valid_);
203 DCHECK(other.is_valid_);
204 return canonical_spec_ != other.canonical_spec_;
205 }
206
207 bool FacetURI::operator<(const FacetURI& other) const {
208 DCHECK(is_valid_);
209 DCHECK(other.is_valid_);
210 return canonical_spec_ < other.canonical_spec_;
211 }
212
213 bool FacetURI::operator>(const FacetURI& other) const {
214 DCHECK(is_valid_);
215 DCHECK(other.is_valid_);
216 return canonical_spec_ > other.canonical_spec_;
217 }
218
219 bool FacetURI::IsValidWebFacetURI() const {
220 return is_valid_ && ExtractScheme(canonical_spec_) == url::kHttpsScheme;
221 }
222
223 bool FacetURI::IsValidAndroidFacetURI() const {
224 return is_valid_ && ExtractScheme(canonical_spec_) == kAndroidAppScheme;
225 }
226
227 FacetURI::FacetURI(const std::string& canonical_spec, bool is_valid)
228 : is_valid_(is_valid), canonical_spec_(canonical_spec) {
229 }
230
231 bool AreEquivalenceClassesEqual(const AffiliatedFacets& a,
232 const AffiliatedFacets& b) {
233 if (a.size() != b.size())
234 return false;
235
236 std::vector<FacetURI> a_sorted(a.begin(), a.end());
237 std::vector<FacetURI> b_sorted(b.begin(), b.end());
238 std::sort(a_sorted.begin(), a_sorted.end());
239 std::sort(b_sorted.begin(), b_sorted.end());
240 return std::equal(a_sorted.begin(), a_sorted.end(), b_sorted.begin());
241 }
242
243 } // namespace password_manager
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698