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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: components/password_manager/core/browser/affiliation_utils.cc
diff --git a/components/password_manager/core/browser/affiliation_utils.cc b/components/password_manager/core/browser/affiliation_utils.cc
new file mode 100644
index 0000000000000000000000000000000000000000..8c87015478e39f0317f3a0e6d26c647d390c7f26
--- /dev/null
+++ b/components/password_manager/core/browser/affiliation_utils.cc
@@ -0,0 +1,243 @@
+// 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 "components/password_manager/core/browser/affiliation_utils.h"
+
+#include "base/base64.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "net/base/escape.h"
+#include "url/third_party/mozilla/url_parse.h"
+#include "url/url_canon_stdstring.h"
+#include "url/url_util.h"
+
+namespace password_manager {
+
+namespace {
+
+// The scheme used for identifying Android applications.
+const char kAndroidAppScheme[] = "android";
+
+// Returns a StringPiece corresponding to |component| in |uri|, or the empty
+// string in case there is no such component.
+base::StringPiece ComponentString(const std::string& uri,
+ const url::Component& component) {
+ if (!component.is_valid())
+ return base::StringPiece();
+ return base::StringPiece(uri.c_str() + component.begin, component.len);
+}
+
+// Extracts the scheme of an unparsed |uri| as a StringPiece, or returns the
+// empty string on failure.
+base::StringPiece ExtractScheme(const std::string& uri) {
+ url::Component scheme_component;
+ if (url::ExtractScheme(uri.c_str(), uri.size(), &scheme_component))
+ return ComponentString(uri, scheme_component);
+ return base::StringPiece();
+}
+
+// Returns true if the passed ASCII |input| string contains nothing else than
+// alphanumeric characters and those in |other_characters|.
+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
+ const base::StringPiece& other_characters) {
+ for (char c : input) {
+ if (!IsAsciiAlpha(c) && !IsAsciiDigit(c) &&
+ other_characters.find(c) == base::StringPiece::npos)
+ return false;
+ }
+ return true;
+}
+
+// Canonicalizes a Web facet URI, and returns true if canonicalization was
+// successful and produced a valid URI. Even in case of a failure, something
+// meaningful will be written to |canonical_uri|.
+bool CanonicalizeWebFacetURI(const std::string& input_uri,
+ const url::Parsed& input_parsed,
+ std::string* canonical_uri) {
+ url::Parsed canonical_parsed;
+ url::StdStringCanonOutput canonical_output(canonical_uri);
+
+ bool canonicalization_succeeded = url::CanonicalizeStandardURL(
+ input_uri.c_str(), input_uri.size(), input_parsed, NULL,
+ &canonical_output, &canonical_parsed);
+ canonical_output.Complete();
+
+ if (canonicalization_succeeded &&
+ canonical_parsed.host.is_nonempty() &&
+ !canonical_parsed.username.is_valid() &&
+ !canonical_parsed.password.is_valid() &&
+ ComponentString(*canonical_uri, canonical_parsed.path) == "/" &&
+ !canonical_parsed.query.is_valid() && !canonical_parsed.ref.is_valid()) {
+ // Get rid of the trailing slash added by url::CanonicalizeStandardURL().
+ DCHECK_EQ((size_t)canonical_parsed.path.begin, canonical_uri->size() - 1);
+ canonical_uri->erase(canonical_parsed.path.begin,
+ canonical_parsed.path.len);
+ return true;
+ }
+ return false;
+}
+
+// Canonicalizes the username component in an Android facet URI (containing the
+// certificate hash), and returns true if canonicalization was successful and
+// produced a valid non-empty component. Even in case of a failure, something
+// meaningful will be written to |canonical_output|.
+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.
+ url::CanonOutput* canonical_output) {
+ // 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.
+ std::string base64_encoded_hash = net::UnescapeURLComponent(
+ input_hash.as_string(), net::UnescapeRule::URL_SPECIAL_CHARS);
+ if (!base64_encoded_hash.empty()) {
+ canonical_output->Append(base64_encoded_hash.data(),
+ base64_encoded_hash.size());
+ canonical_output->push_back('@');
+ }
+
+ size_t first_padding = base64_encoded_hash.find_first_of('=');
+ return !base64_encoded_hash.empty() && base64_encoded_hash.size() % 4 == 0 &&
+ ContainsOnlyAlphanumericAnd(base64_encoded_hash, "-_=") &&
+ (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.
+ first_padding == base64_encoded_hash.size() - 1 ||
+ (first_padding == base64_encoded_hash.size() - 2 &&
+ base64_encoded_hash[base64_encoded_hash.size() - 1] == '='));
+}
+
+// Canonicalizes the host component in an Android facet URI (containing the
+// package name), and returns true if canonicalization was successful and
+// produced a valid non-empty component. Even in case of a failure, something
+// 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
+bool CanonicalizePackageNameComponent(
+ const base::StringPiece& input_package_name,
+ url::CanonOutput* canonical_output) {
+ std::string package_name = net::UnescapeURLComponent(
+ input_package_name.as_string(), net::UnescapeRule::NORMAL);
+ canonical_output->Append(package_name.data(), package_name.size());
+
+ // TODO(engedy): We might want to use a regex to check this more throughly.
+ return !package_name.empty() &&
+ ContainsOnlyAlphanumericAnd(package_name, "_.");
+}
+
+// Canonicalizes an Android facet URI, and returns true if canonicalization was
+// successful and produced a valid URI. Even in case of a failure, something
+// 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.
+bool CanonicalizeAndroidFacetURI(const std::string& input_uri,
+ const url::Parsed& input_parsed,
+ std::string* canonical_uri) {
+ url::StdStringCanonOutput canonical_output(canonical_uri);
+
+ url::Component unused;
+ bool success = url::CanonicalizeScheme(
+ input_uri.c_str(), input_parsed.scheme, &canonical_output, &unused);
+
+ canonical_output.push_back('/');
+ canonical_output.push_back('/');
+
+ // We cannot use url::CanonicalizeUserInfo as that would percent encode the
+ // the base64 padding characters ('=').
+ success &= CanonicalizeHashComponent(
+ ComponentString(input_uri, input_parsed.username), &canonical_output);
+
+ // We cannot use url::CanonicalizeHost as that would convert the package name
+ // to lower case, but the package name is case sensitive.
+ success &= CanonicalizePackageNameComponent(
+ ComponentString(input_uri.data(), input_parsed.host), &canonical_output);
+
+ canonical_output.Complete();
+
+ return success && !input_parsed.password.is_nonempty() &&
+ (!input_parsed.path.is_nonempty() ||
+ ComponentString(input_uri, input_parsed.path) == "/") &&
+ !input_parsed.port.is_nonempty() && !input_parsed.query.is_valid() &&
+ !input_parsed.ref.is_valid();
+}
+
+// Computes the canonicalized form of |uri| into |canonical_uri|, and returns
+// whether or not the result is a valid facet URI. Even if the result is not
+// valid, |canonical_uri| will contain something reasonable.
+bool ParseAndCanonicalizeFacetURI(const std::string& input_uri,
+ std::string* canonical_uri) {
+ DCHECK(canonical_uri);
+ canonical_uri->clear();
+ canonical_uri->reserve(input_uri.size() + 32);
+
+ url::Parsed input_parsed;
+ url::ParseStandardURL(input_uri.c_str(), input_uri.size(), &input_parsed);
+
+ base::StringPiece scheme = ComponentString(input_uri, input_parsed.scheme);
+ if (url::LowerCaseEqualsASCII(scheme.begin(), scheme.end(),
+ url::kHttpsScheme)) {
+ return CanonicalizeWebFacetURI(input_uri, input_parsed, canonical_uri);
+ } else if (url::LowerCaseEqualsASCII(scheme.begin(), scheme.end(),
+ kAndroidAppScheme)) {
+ return CanonicalizeAndroidFacetURI(input_uri, input_parsed, canonical_uri);
+ }
+ return false;
+}
+
+} // namespace
+
+FacetURI::FacetURI() : is_valid_(false) {
+}
+
+// static
+FacetURI FacetURI::FromPotentiallyInvalidSpec(const std::string& spec) {
+ std::string canonical_spec;
+ bool is_valid = ParseAndCanonicalizeFacetURI(spec, &canonical_spec);
+ return FacetURI(canonical_spec, is_valid);
+}
+
+// static
+FacetURI FacetURI::FromCanonicalSpec(const std::string& canonical_spec) {
+ return FacetURI(canonical_spec, true);
+}
+
+bool FacetURI::operator==(const FacetURI& other) const {
+ DCHECK(is_valid_);
+ 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
+ return canonical_spec_ == other.canonical_spec_;
+}
+
+bool FacetURI::operator!=(const FacetURI& other) const {
+ DCHECK(is_valid_);
+ DCHECK(other.is_valid_);
+ return canonical_spec_ != other.canonical_spec_;
+}
+
+bool FacetURI::operator<(const FacetURI& other) const {
+ DCHECK(is_valid_);
+ DCHECK(other.is_valid_);
+ return canonical_spec_ < other.canonical_spec_;
+}
+
+bool FacetURI::operator>(const FacetURI& other) const {
+ DCHECK(is_valid_);
+ DCHECK(other.is_valid_);
+ return canonical_spec_ > other.canonical_spec_;
+}
+
+bool FacetURI::IsValidWebFacetURI() const {
+ return is_valid_ && ExtractScheme(canonical_spec_) == url::kHttpsScheme;
+}
+
+bool FacetURI::IsValidAndroidFacetURI() const {
+ return is_valid_ && ExtractScheme(canonical_spec_) == kAndroidAppScheme;
+}
+
+FacetURI::FacetURI(const std::string& canonical_spec, bool is_valid)
+ : is_valid_(is_valid), canonical_spec_(canonical_spec) {
+}
+
+bool AreEquivalenceClassesEqual(const AffiliatedFacets& a,
+ const AffiliatedFacets& b) {
+ if (a.size() != b.size())
+ return false;
+
+ std::vector<FacetURI> a_sorted(a.begin(), a.end());
+ std::vector<FacetURI> b_sorted(b.begin(), b.end());
+ std::sort(a_sorted.begin(), a_sorted.end());
+ std::sort(b_sorted.begin(), b_sorted.end());
+ return std::equal(a_sorted.begin(), a_sorted.end(), b_sorted.begin());
+}
+
+} // namespace password_manager

Powered by Google App Engine
This is Rietveld 408576698