| // Copyright 2015 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/password_manager_util.h" |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/bind_helpers.h" |
| #include "base/macros.h" |
| #include "base/stl_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/test/scoped_task_environment.h" |
| #include "components/autofill/core/common/password_form.h" |
| #include "components/password_manager/core/browser/password_manager_test_utils.h" |
| #include "components/password_manager/core/browser/test_password_store.h" |
| #include "components/password_manager/core/common/password_manager_pref_names.h" |
| #include "components/prefs/pref_registry_simple.h" |
| #include "components/prefs/testing_pref_service.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using autofill::PasswordForm; |
| |
| namespace password_manager_util { |
| namespace { |
| |
| constexpr char kTestAndroidRealm[] = "android://hash@com.example.beta.android"; |
| constexpr char kTestFederationURL[] = "https://google.com/"; |
| constexpr char kTestUsername[] = "Username"; |
| constexpr char kTestUsername2[] = "Username2"; |
| constexpr char kTestPassword[] = "12345"; |
| |
| autofill::PasswordForm GetTestAndroidCredentials(const char* signon_realm) { |
| autofill::PasswordForm form; |
| form.scheme = autofill::PasswordForm::SCHEME_HTML; |
| form.signon_realm = signon_realm; |
| form.username_value = base::ASCIIToUTF16(kTestUsername); |
| form.password_value = base::ASCIIToUTF16(kTestPassword); |
| return form; |
| } |
| |
| } // namespace |
| |
| using password_manager::UnorderedPasswordFormElementsAre; |
| using testing::_; |
| using testing::DoAll; |
| using testing::Return; |
| |
| TEST(PasswordManagerUtil, TrimUsernameOnlyCredentials) { |
| std::vector<std::unique_ptr<autofill::PasswordForm>> forms; |
| std::vector<std::unique_ptr<autofill::PasswordForm>> expected_forms; |
| forms.push_back(std::make_unique<autofill::PasswordForm>( |
| GetTestAndroidCredentials(kTestAndroidRealm))); |
| expected_forms.push_back(std::make_unique<autofill::PasswordForm>( |
| GetTestAndroidCredentials(kTestAndroidRealm))); |
| |
| autofill::PasswordForm username_only; |
| username_only.scheme = autofill::PasswordForm::SCHEME_USERNAME_ONLY; |
| username_only.signon_realm = kTestAndroidRealm; |
| username_only.username_value = base::ASCIIToUTF16(kTestUsername2); |
| forms.push_back(std::make_unique<autofill::PasswordForm>(username_only)); |
| |
| username_only.federation_origin = |
| url::Origin::Create(GURL(kTestFederationURL)); |
| username_only.skip_zero_click = false; |
| forms.push_back(std::make_unique<autofill::PasswordForm>(username_only)); |
| username_only.skip_zero_click = true; |
| expected_forms.push_back( |
| std::make_unique<autofill::PasswordForm>(username_only)); |
| |
| TrimUsernameOnlyCredentials(&forms); |
| |
| EXPECT_THAT(forms, UnorderedPasswordFormElementsAre(&expected_forms)); |
| } |
| |
| TEST(PasswordManagerUtil, GetSignonRealmWithProtocolExcluded) { |
| autofill::PasswordForm http_form; |
| http_form.origin = GURL("http://www.google.com/page-1/"); |
| http_form.signon_realm = "http://www.google.com/"; |
| EXPECT_EQ(GetSignonRealmWithProtocolExcluded(http_form), "www.google.com/"); |
| |
| autofill::PasswordForm https_form; |
| https_form.origin = GURL("https://www.google.com/page-1/"); |
| https_form.signon_realm = "https://www.google.com/"; |
| EXPECT_EQ(GetSignonRealmWithProtocolExcluded(https_form), "www.google.com/"); |
| |
| autofill::PasswordForm federated_form; |
| federated_form.origin = GURL("http://localhost:8000/"); |
| federated_form.signon_realm = |
| "federation://localhost/accounts.federation.com"; |
| EXPECT_EQ(GetSignonRealmWithProtocolExcluded(federated_form), |
| "localhost/accounts.federation.com"); |
| } |
| |
| TEST(PasswordManagerUtil, FindBestMatches) { |
| const int kNotFound = -1; |
| struct TestMatch { |
| bool is_psl_match; |
| bool preferred; |
| std::string username; |
| }; |
| struct TestCase { |
| const char* description; |
| std::vector<TestMatch> matches; |
| int expected_preferred_match_index; |
| std::map<std::string, size_t> expected_best_matches_indices; |
| } test_cases[] = { |
| {"Empty matches", {}, kNotFound, {}}, |
| {"1 preferred non-psl match", |
| {{.is_psl_match = false, .preferred = true, .username = "u"}}, |
| 0, |
| {{"u", 0}}}, |
| {"1 non-preferred psl match", |
| {{.is_psl_match = true, .preferred = false, .username = "u"}}, |
| 0, |
| {{"u", 0}}}, |
| {"2 matches with the same username", |
| {{.is_psl_match = false, .preferred = false, .username = "u"}, |
| {.is_psl_match = false, .preferred = true, .username = "u"}}, |
| 1, |
| {{"u", 1}}}, |
| {"2 matches with different usernames, preferred taken", |
| {{.is_psl_match = false, .preferred = false, .username = "u1"}, |
| {.is_psl_match = false, .preferred = true, .username = "u2"}}, |
| 1, |
| {{"u1", 0}, {"u2", 1}}}, |
| {"2 matches with different usernames, non-psl much taken", |
| {{.is_psl_match = false, .preferred = false, .username = "u1"}, |
| {.is_psl_match = true, .preferred = true, .username = "u2"}}, |
| 0, |
| {{"u1", 0}, {"u2", 1}}}, |
| {"8 matches, 3 usernames", |
| {{.is_psl_match = false, .preferred = false, .username = "u2"}, |
| {.is_psl_match = true, .preferred = false, .username = "u3"}, |
| {.is_psl_match = true, .preferred = false, .username = "u1"}, |
| {.is_psl_match = false, .preferred = true, .username = "u3"}, |
| {.is_psl_match = true, .preferred = false, .username = "u1"}, |
| {.is_psl_match = false, .preferred = false, .username = "u2"}, |
| {.is_psl_match = true, .preferred = true, .username = "u3"}, |
| {.is_psl_match = false, .preferred = false, .username = "u1"}}, |
| 3, |
| {{"u1", 7}, {"u2", 0}, {"u3", 3}}}, |
| |
| }; |
| |
| for (const TestCase& test_case : test_cases) { |
| SCOPED_TRACE(testing::Message("Test description: ") |
| << test_case.description); |
| // Convert TestMatch to PasswordForm. |
| std::vector<PasswordForm> owning_matches; |
| for (const TestMatch match : test_case.matches) { |
| PasswordForm form; |
| form.is_public_suffix_match = match.is_psl_match; |
| form.preferred = match.preferred; |
| form.username_value = base::ASCIIToUTF16(match.username); |
| owning_matches.push_back(form); |
| } |
| std::vector<const PasswordForm*> matches; |
| for (const PasswordForm& match : owning_matches) |
| matches.push_back(&match); |
| |
| std::map<base::string16, const PasswordForm*> best_matches; |
| std::vector<const PasswordForm*> not_best_matches; |
| const PasswordForm* preferred_match = nullptr; |
| |
| FindBestMatches(matches, &best_matches, ¬_best_matches, |
| &preferred_match); |
| |
| if (test_case.expected_preferred_match_index == kNotFound) { |
| // Case of empty |matches|. |
| EXPECT_FALSE(preferred_match); |
| EXPECT_TRUE(best_matches.empty()); |
| EXPECT_TRUE(not_best_matches.empty()); |
| } else { |
| // Check |preferred_match|. |
| EXPECT_EQ(matches[test_case.expected_preferred_match_index], |
| preferred_match); |
| // Check best matches. |
| ASSERT_EQ(test_case.expected_best_matches_indices.size(), |
| best_matches.size()); |
| |
| for (const auto& username_match : best_matches) { |
| std::string username = base::UTF16ToUTF8(username_match.first); |
| ASSERT_NE(test_case.expected_best_matches_indices.end(), |
| test_case.expected_best_matches_indices.find(username)); |
| size_t expected_index = |
| test_case.expected_best_matches_indices.at(username); |
| size_t actual_index = std::distance( |
| matches.begin(), |
| std::find(matches.begin(), matches.end(), username_match.second)); |
| EXPECT_EQ(expected_index, actual_index); |
| } |
| |
| // Check non-best matches. |
| ASSERT_EQ(matches.size(), best_matches.size() + not_best_matches.size()); |
| for (const PasswordForm* form : not_best_matches) { |
| // A non-best match form must not be in |best_matches|. |
| EXPECT_NE(best_matches[form->username_value], form); |
| |
| base::Erase(matches, form); |
| } |
| // Expect that all non-best matches were found in |matches| and only best |
| // matches left. |
| EXPECT_EQ(best_matches.size(), matches.size()); |
| } |
| } |
| } |
| |
| } // namespace password_manager_util |