summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorvasilii <vasilii@chromium.org>2016-03-07 05:54:20 -0800
committerCommit bot <commit-bot@chromium.org>2016-03-07 13:55:30 +0000
commit8037efe8c78edae8993540b99d13341499e9edb2 (patch)
tree3580868f5b8ddd532c1c40177c9ba3d950a51b1b
parent8cc8a0e9395a2fc70d1a048e7e2862a9081cc70d (diff)
downloadchromium_src-8037efe8c78edae8993540b99d13341499e9edb2.zip
chromium_src-8037efe8c78edae8993540b99d13341499e9edb2.tar.gz
chromium_src-8037efe8c78edae8993540b99d13341499e9edb2.tar.bz2
Show the federated credentials in the Manage passwords bubble.
The only unsupported backend is KWallet. PSL matching isn't implemented for it as well. BUG=582087 Review URL: https://codereview.chromium.org/1758313004 Cr-Commit-Position: refs/heads/master@{#379550}
-rw-r--r--chrome/browser/password_manager/native_backend_gnome_x.cc40
-rw-r--r--chrome/browser/password_manager/native_backend_gnome_x_unittest.cc8
-rw-r--r--chrome/browser/password_manager/native_backend_libsecret.cc39
-rw-r--r--chrome/browser/password_manager/native_backend_libsecret_unittest.cc8
-rw-r--r--components/password_manager/core/browser/login_database.cc61
-rw-r--r--components/password_manager/core/browser/login_database.h9
-rw-r--r--components/password_manager/core/browser/login_database_unittest.cc96
-rw-r--r--components/password_manager/core/browser/psl_matching_helper.cc9
-rw-r--r--components/password_manager/core/browser/psl_matching_helper.h4
-rw-r--r--components/password_manager/core/browser/psl_matching_helper_unittest.cc42
10 files changed, 250 insertions, 66 deletions
diff --git a/chrome/browser/password_manager/native_backend_gnome_x.cc b/chrome/browser/password_manager/native_backend_gnome_x.cc
index 53c671b..a0d4f1f 100644
--- a/chrome/browser/password_manager/native_backend_gnome_x.cc
+++ b/chrome/browser/password_manager/native_backend_gnome_x.cc
@@ -185,6 +185,10 @@ ScopedVector<PasswordForm> ConvertFormList(GList* found,
ScopedVector<PasswordForm> forms;
password_manager::PSLDomainMatchMetric psl_domain_match_metric =
password_manager::PSL_DOMAIN_MATCH_NONE;
+ const bool allow_psl_match =
+ lookup_form && password_manager::ShouldPSLDomainMatchingApply(
+ password_manager::GetRegistryControlledDomain(
+ GURL(lookup_form->signon_realm)));
for (GList* element = g_list_first(found); element;
element = g_list_next(element)) {
GnomeKeyringFound* data = static_cast<GnomeKeyringFound*>(element->data);
@@ -193,15 +197,21 @@ ScopedVector<PasswordForm> ConvertFormList(GList* found,
scoped_ptr<PasswordForm> form(FormFromAttributes(attrs));
if (form) {
if (lookup_form && form->signon_realm != lookup_form->signon_realm) {
- // This is not an exact match, we try PSL matching.
if (lookup_form->scheme != PasswordForm::SCHEME_HTML ||
- form->scheme != PasswordForm::SCHEME_HTML ||
- !(password_manager::IsPublicSuffixDomainMatch(
- lookup_form->signon_realm, form->signon_realm))) {
+ form->scheme != PasswordForm::SCHEME_HTML)
+ continue; // Ignore non-HTML matches.
+ // This is not an exact match, we try PSL matching and federated match.
+ if (allow_psl_match &&
+ password_manager::IsPublicSuffixDomainMatch(
+ form->signon_realm, lookup_form->signon_realm)) {
+ psl_domain_match_metric = password_manager::PSL_DOMAIN_MATCH_FOUND;
+ form->is_public_suffix_match = true;
+ } else if (!form->federation_origin.unique() &&
+ password_manager::IsFederatedMatch(form->signon_realm,
+ lookup_form->origin)) {
+ } else {
continue;
}
- psl_domain_match_metric = password_manager::PSL_DOMAIN_MATCH_FOUND;
- form->is_public_suffix_match = true;
}
if (data->secret) {
form->password_value = UTF8ToUTF16(data->secret);
@@ -214,15 +224,11 @@ ScopedVector<PasswordForm> ConvertFormList(GList* found,
}
}
if (lookup_form) {
- const GURL signon_realm(lookup_form->signon_realm);
- std::string registered_domain =
- password_manager::GetRegistryControlledDomain(signon_realm);
- UMA_HISTOGRAM_ENUMERATION(
- "PasswordManager.PslDomainMatchTriggering",
- password_manager::ShouldPSLDomainMatchingApply(registered_domain)
- ? psl_domain_match_metric
- : password_manager::PSL_DOMAIN_MATCH_NOT_USED,
- password_manager::PSL_DOMAIN_MATCH_COUNT);
+ UMA_HISTOGRAM_ENUMERATION("PasswordManager.PslDomainMatchTriggering",
+ allow_psl_match
+ ? psl_domain_match_metric
+ : password_manager::PSL_DOMAIN_MATCH_NOT_USED,
+ password_manager::PSL_DOMAIN_MATCH_COUNT);
}
return forms;
}
@@ -426,7 +432,9 @@ void GKRMethod::GetLogins(const PasswordForm& form, const char* app_string) {
ScopedAttributeList attrs(gnome_keyring_attribute_list_new());
if (!password_manager::ShouldPSLDomainMatchingApply(
password_manager::GetRegistryControlledDomain(
- GURL(form.signon_realm)))) {
+ GURL(form.signon_realm))) &&
+ form.scheme != PasswordForm::SCHEME_HTML) {
+ // Don't retrieve the PSL matched and federated credentials.
AppendString(&attrs, "signon_realm", form.signon_realm);
}
AppendString(&attrs, "application", app_string);
diff --git a/chrome/browser/password_manager/native_backend_gnome_x_unittest.cc b/chrome/browser/password_manager/native_backend_gnome_x_unittest.cc
index 36c0a36..85e027c 100644
--- a/chrome/browser/password_manager/native_backend_gnome_x_unittest.cc
+++ b/chrome/browser/password_manager/native_backend_gnome_x_unittest.cc
@@ -884,6 +884,14 @@ TEST_F(NativeBackendGnomeTest, PSLUpdatingStrictAddLogin) {
CheckPSLUpdate(UPDATE_BY_ADDLOGIN);
}
+TEST_F(NativeBackendGnomeTest, FetchFederatedCredential) {
+ other_auth_.signon_realm = "federation://www.example.com/google.com";
+ other_auth_.federation_origin = url::Origin(GURL("https://google.com/"));
+ EXPECT_TRUE(CheckCredentialAvailability(other_auth_,
+ GURL("http://www.example.com/"),
+ PasswordForm::SCHEME_HTML, nullptr));
+}
+
TEST_F(NativeBackendGnomeTest, BasicUpdateLogin) {
NativeBackendGnome backend(42);
backend.Init();
diff --git a/chrome/browser/password_manager/native_backend_libsecret.cc b/chrome/browser/password_manager/native_backend_libsecret.cc
index 8c65356..9887500 100644
--- a/chrome/browser/password_manager/native_backend_libsecret.cc
+++ b/chrome/browser/password_manager/native_backend_libsecret.cc
@@ -541,7 +541,8 @@ bool NativeBackendLibsecret::GetLoginsList(
if (lookup_form &&
!password_manager::ShouldPSLDomainMatchingApply(
password_manager::GetRegistryControlledDomain(
- GURL(lookup_form->signon_realm))))
+ GURL(lookup_form->signon_realm))) &&
+ lookup_form->scheme != PasswordForm::SCHEME_HTML)
attrs.Append("signon_realm", lookup_form->signon_realm);
GError* error = nullptr;
@@ -631,6 +632,10 @@ ScopedVector<autofill::PasswordForm> NativeBackendLibsecret::ConvertFormList(
password_manager::PSLDomainMatchMetric psl_domain_match_metric =
password_manager::PSL_DOMAIN_MATCH_NONE;
GError* error = nullptr;
+ const bool allow_psl_match =
+ lookup_form && password_manager::ShouldPSLDomainMatchingApply(
+ password_manager::GetRegistryControlledDomain(
+ GURL(lookup_form->signon_realm)));
for (GList* element = g_list_first(found); element != nullptr;
element = g_list_next(element)) {
SecretItem* secretItem = static_cast<SecretItem*>(element->data);
@@ -646,15 +651,21 @@ ScopedVector<autofill::PasswordForm> NativeBackendLibsecret::ConvertFormList(
g_hash_table_unref(attrs);
if (form) {
if (lookup_form && form->signon_realm != lookup_form->signon_realm) {
- // This is not an exact match, we try PSL matching.
if (lookup_form->scheme != PasswordForm::SCHEME_HTML ||
- form->scheme != PasswordForm::SCHEME_HTML ||
- !(password_manager::IsPublicSuffixDomainMatch(
- lookup_form->signon_realm, form->signon_realm))) {
+ form->scheme != PasswordForm::SCHEME_HTML)
+ continue;
+ // This is not an exact match, we try PSL matching and federated match.
+ if (allow_psl_match &&
+ password_manager::IsPublicSuffixDomainMatch(
+ form->signon_realm, lookup_form->signon_realm)) {
+ psl_domain_match_metric = password_manager::PSL_DOMAIN_MATCH_FOUND;
+ form->is_public_suffix_match = true;
+ } else if (!form->federation_origin.unique() &&
+ password_manager::IsFederatedMatch(form->signon_realm,
+ lookup_form->origin)) {
+ } else {
continue;
}
- psl_domain_match_metric = password_manager::PSL_DOMAIN_MATCH_FOUND;
- form->is_public_suffix_match = true;
}
SecretValue* secretValue = secret_item_get_secret(secretItem);
if (secretValue) {
@@ -670,15 +681,11 @@ ScopedVector<autofill::PasswordForm> NativeBackendLibsecret::ConvertFormList(
}
if (lookup_form) {
- const GURL signon_realm(lookup_form->signon_realm);
- std::string registered_domain =
- password_manager::GetRegistryControlledDomain(signon_realm);
- UMA_HISTOGRAM_ENUMERATION(
- "PasswordManager.PslDomainMatchTriggering",
- password_manager::ShouldPSLDomainMatchingApply(registered_domain)
- ? psl_domain_match_metric
- : password_manager::PSL_DOMAIN_MATCH_NOT_USED,
- password_manager::PSL_DOMAIN_MATCH_COUNT);
+ UMA_HISTOGRAM_ENUMERATION("PasswordManager.PslDomainMatchTriggering",
+ allow_psl_match
+ ? psl_domain_match_metric
+ : password_manager::PSL_DOMAIN_MATCH_NOT_USED,
+ password_manager::PSL_DOMAIN_MATCH_COUNT);
}
g_list_free(found);
return forms;
diff --git a/chrome/browser/password_manager/native_backend_libsecret_unittest.cc b/chrome/browser/password_manager/native_backend_libsecret_unittest.cc
index 2fa1cf4..d032803 100644
--- a/chrome/browser/password_manager/native_backend_libsecret_unittest.cc
+++ b/chrome/browser/password_manager/native_backend_libsecret_unittest.cc
@@ -694,6 +694,14 @@ TEST_F(NativeBackendLibsecretTest, PSLUpdatingStrictAddLogin) {
CheckPSLUpdate(UPDATE_BY_ADDLOGIN);
}
+TEST_F(NativeBackendLibsecretTest, FetchFederatedCredential) {
+ other_auth_.signon_realm = "federation://www.example.com/google.com";
+ other_auth_.federation_origin = url::Origin(GURL("https://google.com/"));
+ EXPECT_TRUE(CheckCredentialAvailability(other_auth_,
+ GURL("http://www.example.com/"),
+ PasswordForm::SCHEME_HTML, nullptr));
+}
+
TEST_F(NativeBackendLibsecretTest, BasicUpdateLogin) {
NativeBackendLibsecret backend(42);
diff --git a/components/password_manager/core/browser/login_database.cc b/components/password_manager/core/browser/login_database.cc
index fa1eb1b..9e781d5 100644
--- a/components/password_manager/core/browser/login_database.cc
+++ b/components/password_manager/core/browser/login_database.cc
@@ -1127,7 +1127,7 @@ bool LoginDatabase::GetLogins(
ScopedVector<autofill::PasswordForm>* forms) const {
DCHECK(forms);
// You *must* change LoginTableColumns if this query changes.
- const std::string sql_query =
+ std::string sql_query =
"SELECT origin_url, action_url, "
"username_element, username_value, "
"password_element, password_value, submit_element, "
@@ -1136,12 +1136,23 @@ bool LoginDatabase::GetLogins(
"date_synced, display_name, icon_url, "
"federation_url, skip_zero_click, generation_upload_status "
"FROM logins WHERE signon_realm == ? ";
- sql::Statement s;
const GURL signon_realm(form.signon_realm);
std::string registered_domain = GetRegistryControlledDomain(signon_realm);
const bool should_PSL_matching_apply =
form.scheme == PasswordForm::SCHEME_HTML &&
ShouldPSLDomainMatchingApply(registered_domain);
+ const bool should_federated_apply = form.scheme == PasswordForm::SCHEME_HTML;
+ if (should_PSL_matching_apply)
+ sql_query += "OR signon_realm REGEXP ? ";
+ if (should_federated_apply)
+ sql_query += "OR (signon_realm LIKE ? AND password_type == 2) ";
+
+ // TODO(nyquist) Consider usage of GetCachedStatement when
+ // http://crbug.com/248608 is fixed.
+ sql::Statement s(db_.GetUniqueStatement(sql_query.c_str()));
+ s.BindString(0, form.signon_realm);
+ int placeholder = 1;
+
// PSL matching only applies to HTML forms.
if (should_PSL_matching_apply) {
// We are extending the original SQL query with one that includes more
@@ -1150,11 +1161,6 @@ bool LoginDatabase::GetLogins(
// in the |logins| table. The result (scheme, domain and port) is verified
// further down using GURL. See the functions SchemeMatches,
// RegistryControlledDomainMatches and PortMatches.
- const std::string extended_sql_query =
- sql_query + "OR signon_realm REGEXP ? ";
- // TODO(nyquist) Re-enable usage of GetCachedStatement when
- // http://crbug.com/248608 is fixed.
- s.Assign(db_.GetUniqueStatement(extended_sql_query.c_str()));
// We need to escape . in the domain. Since the domain has already been
// sanitized using GURL, we do not need to escape any other characters.
base::ReplaceChars(registered_domain, ".", "\\.", &registered_domain);
@@ -1170,18 +1176,24 @@ bool LoginDatabase::GetLogins(
// The scheme and port has to be the same as the observed form.
std::string regexp = "^(" + scheme + ":\\/\\/)([\\w-]+\\.)*" +
registered_domain + "(:" + port + ")?\\/$";
- s.BindString(0, form.signon_realm);
- s.BindString(1, regexp);
- } else {
+ s.BindString(placeholder++, regexp);
+ }
+ if (should_federated_apply) {
+ std::string expression =
+ base::StringPrintf("federation://%s/%%", form.origin.host().c_str());
+ s.BindString(placeholder++, expression);
+ }
+
+ if (!should_PSL_matching_apply && !should_federated_apply) {
+ // Otherwise the histogram is reported in StatementToForms.
UMA_HISTOGRAM_ENUMERATION("PasswordManager.PslDomainMatchTriggering",
PSL_DOMAIN_MATCH_NOT_USED,
PSL_DOMAIN_MATCH_COUNT);
- s.Assign(db_.GetCachedStatement(SQL_FROM_HERE, sql_query.c_str()));
- s.BindString(0, form.signon_realm);
}
- return StatementToForms(&s, should_PSL_matching_apply ? &form : nullptr,
- forms);
+ return StatementToForms(
+ &s, should_PSL_matching_apply || should_federated_apply ? &form : nullptr,
+ forms);
}
bool LoginDatabase::GetLoginsCreatedBetween(
@@ -1297,7 +1309,7 @@ std::string LoginDatabase::GetEncryptedPassword(
// static
bool LoginDatabase::StatementToForms(
sql::Statement* statement,
- const autofill::PasswordForm* psl_match,
+ const autofill::PasswordForm* matched_form,
ScopedVector<autofill::PasswordForm>* forms) {
PSLDomainMatchMetric psl_domain_match_metric = PSL_DOMAIN_MATCH_NONE;
@@ -1310,23 +1322,26 @@ bool LoginDatabase::StatementToForms(
return false;
if (result == ENCRYPTION_RESULT_ITEM_FAILURE)
continue;
- DCHECK(result == ENCRYPTION_RESULT_SUCCESS);
- if (psl_match && psl_match->signon_realm != new_form->signon_realm) {
+ DCHECK_EQ(ENCRYPTION_RESULT_SUCCESS, result);
+ if (matched_form && matched_form->signon_realm != new_form->signon_realm) {
if (new_form->scheme != PasswordForm::SCHEME_HTML)
continue; // Ignore non-HTML matches.
- if (!IsPublicSuffixDomainMatch(new_form->signon_realm,
- psl_match->signon_realm)) {
+ if (IsPublicSuffixDomainMatch(new_form->signon_realm,
+ matched_form->signon_realm)) {
+ psl_domain_match_metric = PSL_DOMAIN_MATCH_FOUND;
+ new_form->is_public_suffix_match = true;
+ } else if (!new_form->federation_origin.unique() &&
+ IsFederatedMatch(new_form->signon_realm,
+ matched_form->origin)) {
+ } else {
continue;
}
-
- psl_domain_match_metric = PSL_DOMAIN_MATCH_FOUND;
- new_form->is_public_suffix_match = true;
}
forms->push_back(std::move(new_form));
}
- if (psl_match) {
+ if (matched_form) {
UMA_HISTOGRAM_ENUMERATION("PasswordManager.PslDomainMatchTriggering",
psl_domain_match_metric, PSL_DOMAIN_MATCH_COUNT);
}
diff --git a/components/password_manager/core/browser/login_database.h b/components/password_manager/core/browser/login_database.h
index 3568fc3..c992f3e 100644
--- a/components/password_manager/core/browser/login_database.h
+++ b/components/password_manager/core/browser/login_database.h
@@ -83,7 +83,8 @@ class LoginDatabase {
// All Get* methods below overwrite |forms| with the returned credentials. On
// success, those methods return true.
- // Gets a list of credentials matching |form|, including blacklisted matches.
+ // Gets a list of credentials matching |form|, including blacklisted matches
+ // and federated credentials.
bool GetLogins(const autofill::PasswordForm& form,
ScopedVector<autofill::PasswordForm>* forms) const
WARN_UNUSED_RESULT;
@@ -188,10 +189,10 @@ class LoginDatabase {
ScopedVector<autofill::PasswordForm>* forms) const;
// Overwrites |forms| with credentials retrieved from |statement|. If
- // |psl_match| is not null, filters out all results but thos PSL-matching
- // |*psl_match|. On success returns true.
+ // |matched_form| is not null, filters out all results but those PSL-matching
+ // |*matched_form| or federated credentials for it. On success returns true.
static bool StatementToForms(sql::Statement* statement,
- const autofill::PasswordForm* psl_match,
+ const autofill::PasswordForm* matched_form,
ScopedVector<autofill::PasswordForm>* forms);
base::FilePath db_path_;
diff --git a/components/password_manager/core/browser/login_database_unittest.cc b/components/password_manager/core/browser/login_database_unittest.cc
index 859aca1..46ba8f5 100644
--- a/components/password_manager/core/browser/login_database_unittest.cc
+++ b/components/password_manager/core/browser/login_database_unittest.cc
@@ -373,6 +373,52 @@ TEST_F(LoginDatabaseTest, TestPublicSuffixDomainMatching) {
result.clear();
}
+TEST_F(LoginDatabaseTest, TestFederatedMatching) {
+ ScopedVector<autofill::PasswordForm> result;
+
+ // Example password form.
+ PasswordForm form;
+ form.origin = GURL("https://foo.com/");
+ form.action = GURL("https://foo.com/login");
+ form.username_value = ASCIIToUTF16("test@gmail.com");
+ form.password_value = ASCIIToUTF16("test");
+ form.signon_realm = "https://foo.com/";
+ form.ssl_valid = true;
+ form.preferred = false;
+ form.scheme = PasswordForm::SCHEME_HTML;
+
+ // We go to the mobile site.
+ PasswordForm form2(form);
+ form2.origin = GURL("https://mobile.foo.com/");
+ form2.action = GURL("https://mobile.foo.com/login");
+ form2.signon_realm = "federation://mobile.foo.com/accounts.google.com";
+ form2.username_value = ASCIIToUTF16("test1@gmail.com");
+ form2.type = autofill::PasswordForm::TYPE_API;
+ form2.federation_origin = url::Origin(GURL("https://accounts.google.com/"));
+
+ // Add it and make sure it is there.
+ EXPECT_EQ(AddChangeForForm(form), db().AddLogin(form));
+ EXPECT_EQ(AddChangeForForm(form2), db().AddLogin(form2));
+ EXPECT_TRUE(db().GetAutofillableLogins(&result));
+ EXPECT_EQ(2U, result.size());
+
+ // Match against desktop.
+ PasswordForm form_request;
+ form_request.origin = GURL("https://foo.com/");
+ form_request.signon_realm = "https://foo.com/";
+ form_request.scheme = PasswordForm::SCHEME_HTML;
+ EXPECT_TRUE(db().GetLogins(form_request, &result));
+ EXPECT_THAT(result, testing::ElementsAre(testing::Pointee(form)));
+
+ // Match against the mobile site.
+ form_request.origin = GURL("https://mobile.foo.com/");
+ form_request.signon_realm = "https://mobile.foo.com/";
+ EXPECT_TRUE(db().GetLogins(form_request, &result));
+ form.is_public_suffix_match = true;
+ EXPECT_THAT(result, testing::UnorderedElementsAre(testing::Pointee(form),
+ testing::Pointee(form2)));
+}
+
TEST_F(LoginDatabaseTest, TestPublicSuffixDisabledForNonHTMLForms) {
TestNonHTMLFormPSLMatching(PasswordForm::SCHEME_BASIC);
TestNonHTMLFormPSLMatching(PasswordForm::SCHEME_DIGEST);
@@ -435,10 +481,60 @@ TEST_F(LoginDatabaseTest, TestPublicSuffixDomainMatchingShouldMatchingApply) {
// Match against the other site. Should not match since feature should not be
// enabled for this domain.
+ ASSERT_FALSE(ShouldPSLDomainMatchingApply(
+ GetRegistryControlledDomain(GURL(form2.signon_realm))));
+
EXPECT_TRUE(db().GetLogins(form2, &result));
EXPECT_EQ(0U, result.size());
}
+TEST_F(LoginDatabaseTest, TestFederatedMatchingWithoutPSLMatching) {
+ ScopedVector<autofill::PasswordForm> result;
+
+ // Example password form.
+ PasswordForm form;
+ form.origin = GURL("https://accounts.google.com/");
+ form.action = GURL("https://accounts.google.com/login");
+ form.username_value = ASCIIToUTF16("test@gmail.com");
+ form.password_value = ASCIIToUTF16("test");
+ form.signon_realm = "https://accounts.google.com/";
+ form.ssl_valid = true;
+ form.preferred = false;
+ form.scheme = PasswordForm::SCHEME_HTML;
+
+ // We go to a different site on the same domain where PSL is disabled.
+ PasswordForm form2(form);
+ form2.origin = GURL("https://some.other.google.com/");
+ form2.action = GURL("https://some.other.google.com/login");
+ form2.signon_realm = "federation://some.other.google.com/accounts.google.com";
+ form2.username_value = ASCIIToUTF16("test1@gmail.com");
+ form2.type = autofill::PasswordForm::TYPE_API;
+ form2.federation_origin = url::Origin(GURL("https://accounts.google.com/"));
+
+ // Add it and make sure it is there.
+ EXPECT_EQ(AddChangeForForm(form), db().AddLogin(form));
+ EXPECT_EQ(AddChangeForForm(form2), db().AddLogin(form2));
+ EXPECT_TRUE(db().GetAutofillableLogins(&result));
+ EXPECT_EQ(2U, result.size());
+
+ // Match against the first one.
+ PasswordForm form_request;
+ form_request.origin = form.origin;
+ form_request.signon_realm = form.signon_realm;
+ form_request.scheme = PasswordForm::SCHEME_HTML;
+ EXPECT_TRUE(db().GetLogins(form_request, &result));
+ EXPECT_THAT(result, testing::ElementsAre(testing::Pointee(form)));
+
+ // Match against the second one.
+ ASSERT_FALSE(ShouldPSLDomainMatchingApply(
+ GetRegistryControlledDomain(GURL(form2.signon_realm))));
+ form_request.origin = form2.origin;
+ form_request.signon_realm = form2.signon_realm;
+ EXPECT_TRUE(db().GetLogins(form_request, &result));
+ form.is_public_suffix_match = true;
+ EXPECT_THAT(result, testing::ElementsAre(testing::Pointee(form2)));
+}
+
// This test fails if the implementation of GetLogins uses GetCachedStatement
// instead of GetUniqueStatement, since REGEXP is in use. See
// http://crbug.com/248608.
diff --git a/components/password_manager/core/browser/psl_matching_helper.cc b/components/password_manager/core/browser/psl_matching_helper.cc
index 2f7f9d6..5482feb 100644
--- a/components/password_manager/core/browser/psl_matching_helper.cc
+++ b/components/password_manager/core/browser/psl_matching_helper.cc
@@ -7,6 +7,7 @@
#include "base/command_line.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_util.h"
#include "components/autofill/core/common/password_form.h"
#include "components/password_manager/core/common/password_manager_switches.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
@@ -49,4 +50,12 @@ std::string GetRegistryControlledDomain(const GURL& signon_realm) {
net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
}
+bool IsFederatedMatch(const std::string& signon_realm, const GURL& origin) {
+ // The format should be "federation://origin.host/federation.host;
+ std::string federated_realm = "federation://" + origin.host() + "/";
+ return signon_realm.size() > federated_realm.size() &&
+ base::StartsWith(signon_realm, federated_realm,
+ base::CompareCase::INSENSITIVE_ASCII);
+}
+
} // namespace password_manager
diff --git a/components/password_manager/core/browser/psl_matching_helper.h b/components/password_manager/core/browser/psl_matching_helper.h
index 9e9a10e..22071b6 100644
--- a/components/password_manager/core/browser/psl_matching_helper.h
+++ b/components/password_manager/core/browser/psl_matching_helper.h
@@ -44,6 +44,10 @@ bool IsPublicSuffixDomainMatch(const std::string& url1,
// registry-controlled domain part.
std::string GetRegistryControlledDomain(const GURL& signon_realm);
+// Returns true iff |signon_realm| designates a federated credential for the
+// |origin|.
+bool IsFederatedMatch(const std::string& signon_realm, const GURL& origin);
+
} // namespace password_manager
#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PSL_MATCHING_HELPER_H_
diff --git a/components/password_manager/core/browser/psl_matching_helper_unittest.cc b/components/password_manager/core/browser/psl_matching_helper_unittest.cc
index 01f4e8a..68381d2 100644
--- a/components/password_manager/core/browser/psl_matching_helper_unittest.cc
+++ b/components/password_manager/core/browser/psl_matching_helper_unittest.cc
@@ -21,7 +21,7 @@ TEST(PSLMatchingUtilsTest, IsPublicSuffixDomainMatch) {
bool should_match;
};
- TestPair pairs[] = {
+ const TestPair pairs[] = {
{"http://facebook.com", "http://facebook.com", true},
{"http://facebook.com/path", "http://facebook.com/path", true},
{"http://facebook.com/path1", "http://facebook.com/path2", true},
@@ -44,17 +44,45 @@ TEST(PSLMatchingUtilsTest, IsPublicSuffixDomainMatch) {
{"", "http://www.example.com", false},
{"http://www.example.com", "bad url", false},
{"http://www.example.com/%00", "http://www.example.com/%00", false},
+ {"federation://example.com/google.com", "https://example.com/", false},
};
- for (size_t i = 0; i < arraysize(pairs); ++i) {
+ for (const TestPair& pair : pairs) {
autofill::PasswordForm form1;
- form1.signon_realm = pairs[i].url1;
+ form1.signon_realm = pair.url1;
autofill::PasswordForm form2;
- form2.signon_realm = pairs[i].url2;
- EXPECT_EQ(pairs[i].should_match,
+ form2.signon_realm = pair.url2;
+ EXPECT_EQ(pair.should_match,
IsPublicSuffixDomainMatch(form1.signon_realm, form2.signon_realm))
- << "First URL = " << pairs[i].url1
- << ", second URL = " << pairs[i].url2;
+ << "First URL = " << pair.url1 << ", second URL = " << pair.url2;
+ }
+}
+
+TEST(PSLMatchingUtilsTest, IsFederatedMatch) {
+ struct TestPair {
+ const char* signon_realm;
+ const char* origin;
+ bool should_match;
+ };
+
+ const TestPair pairs[] = {
+ {"https://facebook.com", "https://facebook.com", false},
+ {"", "", false},
+ {"", "https://facebook.com/", false},
+ {"https://facebook.com/", "", false},
+ {"federation://example.com/google.com", "https://example.com/", true},
+ {"federation://example.com/google.com", "http://example.com/", true},
+ {"federation://example.com/google.com", "example.com", false},
+ {"federation://example.com/", "http://example.com/", false},
+ {"federation://example.com/google.com", "example", false},
+ };
+
+ for (const TestPair& pair : pairs) {
+ std::string signon_realm = pair.signon_realm;
+ GURL origin(pair.origin);
+ EXPECT_EQ(pair.should_match, IsFederatedMatch(signon_realm, origin))
+ << "signon_realm = " << pair.signon_realm
+ << ", origin = " << pair.origin;
}
}