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

Unified Diff: components/password_manager/core/browser/password_store_unittest.cc

Issue 1066543004: Update affiliated web credentials when Android credentials are updated. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix the correct MessageLoopProxy occurrence this time. Created 5 years, 8 months 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/password_store_unittest.cc
diff --git a/components/password_manager/core/browser/password_store_unittest.cc b/components/password_manager/core/browser/password_store_unittest.cc
index f5e2a14eed75bd8d2bc520bbbbf70fc7375dd8c8..6d1a350bc5d0b9670828d59a0edbd6e7197b7f8e 100644
--- a/components/password_manager/core/browser/password_store_unittest.cc
+++ b/components/password_manager/core/browser/password_store_unittest.cc
@@ -12,7 +12,9 @@
#include "base/files/scoped_temp_dir.h"
#include "base/stl_util.h"
#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/waitable_event.h"
+#include "base/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "components/password_manager/core/browser/affiliated_match_helper.h"
#include "components/password_manager/core/browser/affiliation_service.h"
@@ -36,6 +38,18 @@ const char kTestWebRealm1[] = "https://one.example.com/";
const char kTestWebOrigin1[] = "https://one.example.com/origin";
const char kTestWebRealm2[] = "https://two.example.com/";
const char kTestWebOrigin2[] = "https://two.example.com/origin";
+const char kTestWebRealm3[] = "https://three.example.com/";
+const char kTestWebOrigin3[] = "https://three.example.com/origin";
+const char kTestWebRealm4[] = "https://four.example.com/";
+const char kTestWebOrigin4[] = "https://four.example.com/origin";
+const char kTestWebRealm5[] = "https://five.example.com/";
+const char kTestWebOrigin5[] = "https://five.example.com/origin";
+const char kTestPSLMatchingWebRealm[] = "https://psl.example.com/";
+const char kTestPSLMatchingWebOrigin[] = "https://psl.example.com/origin";
+const char kTestUnrelatedWebRealm[] = "https://notexample.com/";
+const char kTestUnrelatedWebOrigin[] = "https:/notexample.com/origin";
+const char kTestInsecureWebRealm[] = "http://one.example.com/";
+const char kTestInsecureWebOrigin[] = "http://one.example.com/origin";
const char kTestAndroidRealm1[] = "android://hash@com.example.android/";
const char kTestAndroidRealm2[] = "android://hash@com.example.two.android/";
const char kTestAndroidRealm3[] = "android://hash@com.example.three.android/";
@@ -53,6 +67,13 @@ class MockPasswordStoreConsumer : public PasswordStoreConsumer {
}
};
+class MockPasswordStoreObserver
+ : public password_manager::PasswordStore::Observer {
+ public:
+ MOCK_METHOD1(OnLoginsChanged,
+ void(const password_manager::PasswordStoreChangeList& changes));
+};
+
class MockAffiliatedMatchHelper : public AffiliatedMatchHelper {
public:
MockAffiliatedMatchHelper()
@@ -70,9 +91,21 @@ class MockAffiliatedMatchHelper : public AffiliatedMatchHelper {
.WillOnce(testing::Return(results_to_return));
}
+ // Expects GetAffiliatedWebRealms() to be called with the
+ // |expected_android_form|, and will cause the result callback supplied to
+ // GetAffiliatedWebRealms() to be invoked with |results_to_return|.
+ void ExpectCallToGetAffiliatedWebRealms(
+ const autofill::PasswordForm& expected_android_form,
+ const std::vector<std::string>& results_to_return) {
+ EXPECT_CALL(*this, OnGetAffiliatedWebRealmsCalled(expected_android_form))
+ .WillOnce(testing::Return(results_to_return));
+ }
+
private:
MOCK_METHOD1(OnGetAffiliatedAndroidRealmsCalled,
std::vector<std::string>(const PasswordForm&));
+ MOCK_METHOD1(OnGetAffiliatedWebRealmsCalled,
+ std::vector<std::string>(const PasswordForm&));
void GetAffiliatedAndroidRealms(
const autofill::PasswordForm& observed_form,
@@ -82,6 +115,14 @@ class MockAffiliatedMatchHelper : public AffiliatedMatchHelper {
result_callback.Run(affiliated_android_realms);
}
+ void GetAffiliatedWebRealms(
+ const autofill::PasswordForm& android_form,
+ const AffiliatedRealmsCallback& result_callback) override {
+ std::vector<std::string> affiliated_web_realms =
+ OnGetAffiliatedWebRealmsCalled(android_form);
+ result_callback.Run(affiliated_web_realms);
+ }
+
DISALLOW_COPY_AND_ASSIGN(MockAffiliatedMatchHelper);
};
@@ -258,6 +299,56 @@ TEST_F(PasswordStoreTest, StartSyncFlare) {
base::MessageLoop::current()->RunUntilIdle();
}
+TEST_F(PasswordStoreTest, GetLoginImpl) {
+ /* clang-format off */
+ static const PasswordFormData kTestCredential = {
+ PasswordForm::SCHEME_HTML,
+ kTestWebRealm1,
+ kTestWebOrigin1,
+ "", L"", L"username_element", L"password_element",
+ L"username_value",
+ L"", true, true, 1};
+ /* clang-format on */
+
+ scoped_refptr<PasswordStoreDefault> store(new PasswordStoreDefault(
+ base::ThreadTaskRunnerHandle::Get(), base::ThreadTaskRunnerHandle::Get(),
+ make_scoped_ptr(new LoginDatabase(test_login_db_file_path()))));
+ store->Init(syncer::SyncableService::StartSyncFlare());
+
+ // For each attribute in the primary key, create one form that mismatches on
+ // that attribute.
+ scoped_ptr<PasswordForm> test_form(
+ CreatePasswordFormFromDataForTesting(kTestCredential));
+ scoped_ptr<PasswordForm> mismatching_form_1(new PasswordForm(*test_form));
+ mismatching_form_1->signon_realm = kTestPSLMatchingWebRealm;
+ scoped_ptr<PasswordForm> mismatching_form_2(new PasswordForm(*test_form));
+ mismatching_form_2->origin = GURL(kTestPSLMatchingWebOrigin);
+ scoped_ptr<PasswordForm> mismatching_form_3(new PasswordForm(*test_form));
+ mismatching_form_3->username_element = base::ASCIIToUTF16("other_element");
+ scoped_ptr<PasswordForm> mismatching_form_4(new PasswordForm(*test_form));
+ mismatching_form_4->password_element = base::ASCIIToUTF16("other_element");
+ scoped_ptr<PasswordForm> mismatching_form_5(new PasswordForm(*test_form));
+ mismatching_form_5->username_value =
+ base::ASCIIToUTF16("other_username_value");
+
+ store->AddLogin(*mismatching_form_1);
+ store->AddLogin(*mismatching_form_2);
+ store->AddLogin(*mismatching_form_3);
+ store->AddLogin(*mismatching_form_4);
+ store->AddLogin(*mismatching_form_5);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_FALSE(store->GetLoginImpl(*test_form));
+
+ store->AddLogin(*test_form);
+ base::MessageLoop::current()->RunUntilIdle();
+ scoped_ptr<PasswordForm> returned_form = store->GetLoginImpl(*test_form);
+ ASSERT_TRUE(returned_form);
+ EXPECT_EQ(*test_form, *returned_form);
+
+ store->Shutdown();
+ base::MessageLoop::current()->RunUntilIdle();
+}
+
// When no Android applications are actually affiliated with the realm of the
// observed form, GetLoginsWithAffiliations() should still return the exact and
// PSL matching results, but not any stored Android credentials.
@@ -273,8 +364,8 @@ TEST_F(PasswordStoreTest, GetLoginsWithoutAffiliations) {
L"", true, true, 1},
// Credential that is a PSL match of the observed form.
{PasswordForm::SCHEME_HTML,
- kTestWebRealm2,
- kTestWebOrigin2,
+ kTestPSLMatchingWebRealm,
+ kTestPSLMatchingWebOrigin,
"", L"", L"", L"",
L"username_value_2",
L"", true, true, 1},
@@ -349,8 +440,8 @@ TEST_F(PasswordStoreTest, GetLoginsWithAffiliations) {
L"", true, true, 1},
// Credential that is a PSL match of the observed form.
{PasswordForm::SCHEME_HTML,
- kTestWebRealm2,
- kTestWebOrigin2,
+ kTestPSLMatchingWebRealm,
+ kTestPSLMatchingWebOrigin,
"", L"", L"", L"",
L"username_value_2",
L"", true, true, 1},
@@ -435,4 +526,222 @@ TEST_F(PasswordStoreTest, GetLoginsWithAffiliations) {
base::MessageLoop::current()->RunUntilIdle();
}
+// This test must use passwords, which are not stored on Mac, therefore the test
+// is disabled on Mac. This should not be a huge issue as functionality in the
+// platform-independent base class is tested. See also the file-level comment.
+#if defined(OS_MACOSX)
+#define MAYBE_UpdatePasswordsStoredForAffiliatedWebsites \
+ DISABLED_UpdatePasswordsStoredForAffiliatedWebsites
+#else
+#define MAYBE_UpdatePasswordsStoredForAffiliatedWebsites \
+ UpdatePasswordsStoredForAffiliatedWebsites
+#endif
+
+// When the password stored for an Android application is updated, credentials
+// with the same username stored for affiliated web sites should also be updated
+// automatically.
+TEST_F(PasswordStoreTest, MAYBE_UpdatePasswordsStoredForAffiliatedWebsites) {
+ const wchar_t kTestUsername[] = L"username_value_1";
+ const wchar_t kTestOtherUsername[] = L"username_value_2";
+ const wchar_t kTestOldPassword[] = L"old_password_value";
+ const wchar_t kTestNewPassword[] = L"new_password_value";
+ const wchar_t kTestOtherPassword[] = L"other_password_value";
+
+ /* clang-format off */
+ static const PasswordFormData kTestCredentials[] = {
+ // The credential for the Android application that will be updated
+ // explicitly so it can be tested if the changes are correctly propagated
+ // to affiliated Web credentials.
+ {PasswordForm::SCHEME_HTML,
+ kTestAndroidRealm1,
+ "", "", L"", L"", L"",
+ kTestUsername,
+ kTestOldPassword, true, true, 2},
+
+ // --- Positive samples --- Credentials that the password update should be
+ // automatically propagated to.
+
+ // Credential for an affiliated web site with the same username.
+ {PasswordForm::SCHEME_HTML,
+ kTestWebRealm1,
+ kTestWebOrigin1,
+ "", L"", L"", L"",
+ kTestUsername,
+ kTestOldPassword, true, true, 1},
+ // Credential for another affiliated web site with the same username.
+ // Although the password is different than the current/old password for
+ // the Android application, it should be updated regardless.
+ {PasswordForm::SCHEME_HTML,
+ kTestWebRealm2,
+ kTestWebOrigin2,
+ "", L"", L"", L"",
+ kTestUsername,
+ kTestOtherPassword, true, true, 1},
+
+ // --- Negative samples --- Credentials that the password update should
+ // not be propagated to.
+
+ // Credential for another affiliated web site, but one that already has
+ // the new password.
+ {PasswordForm::SCHEME_HTML,
+ kTestWebRealm3,
+ kTestWebOrigin3,
+ "", L"", L"", L"",
+ kTestUsername,
+ kTestNewPassword, true, true, 1},
+ // Credential for another affiliated web site, but one that was saved
+ // under insecure conditions.
+ {PasswordForm::SCHEME_HTML,
+ kTestWebRealm4,
+ kTestWebOrigin4,
+ "", L"", L"", L"",
+ kTestUsername,
+ kTestOldPassword, true, false, 1},
+ // Credential for the HTTP version of an affiliated web site.
+ {PasswordForm::SCHEME_HTML,
+ kTestInsecureWebRealm,
+ kTestInsecureWebOrigin,
+ "", L"", L"", L"",
+ kTestUsername,
+ kTestOldPassword, true, false, 1},
+ // Credential for an affiliated web site, but with a different username.
+ {PasswordForm::SCHEME_HTML,
+ kTestWebRealm1,
+ kTestWebOrigin1,
+ "", L"", L"", L"",
+ kTestOtherUsername,
+ kTestOldPassword, true, true, 1},
+ // Credential for a web site that is a PSL match to a web sites affiliated
+ // with the Android application.
+ {PasswordForm::SCHEME_HTML,
+ kTestPSLMatchingWebRealm,
+ kTestPSLMatchingWebOrigin,
+ "poisoned", L"poisoned", L"", L"",
+ kTestUsername,
+ kTestOldPassword, true, true, 1},
+ // Credential for an unrelated web site.
+ {PasswordForm::SCHEME_HTML,
+ kTestUnrelatedWebRealm,
+ kTestUnrelatedWebOrigin,
+ "", L"", L"", L"",
+ kTestUsername,
+ kTestOldPassword, true, true, 1},
+ // Credential for an affiliated Android application.
+ {PasswordForm::SCHEME_HTML,
+ kTestAndroidRealm2,
+ "", "", L"", L"", L"",
+ kTestUsername,
+ kTestOldPassword, true, true, 1},
+ // Credential for an unrelated Android application.
+ {PasswordForm::SCHEME_HTML,
+ kTestUnrelatedAndroidRealm,
+ "", "", L"", L"", L"",
+ kTestUsername,
+ kTestOldPassword, true, true, 1},
+ // Credential for an affiliated web site with the same username, but one
+ // that was updated at the same time via Sync as the Android credential.
+ {PasswordForm::SCHEME_HTML,
+ kTestWebRealm5,
+ kTestWebOrigin5,
+ "", L"", L"", L"",
+ kTestUsername,
+ kTestOtherPassword, true, true, 2}};
+ /* clang-format on */
+
+ // The number of positive samples in |kTestCredentials|.
+ const size_t kExpectedNumberOfPropagatedUpdates = 2u;
+
+ const bool kFalseTrue[] = {false, true};
+ for (bool propagation_enabled : kFalseTrue) {
+ for (bool test_remove_and_add_login : kFalseTrue) {
+ SCOPED_TRACE(testing::Message("propagation_enabled: ")
+ << propagation_enabled);
+ SCOPED_TRACE(testing::Message("test_remove_and_add_login: ")
+ << test_remove_and_add_login);
+
+ scoped_refptr<PasswordStoreDefault> store(new PasswordStoreDefault(
+ base::ThreadTaskRunnerHandle::Get(),
+ base::ThreadTaskRunnerHandle::Get(),
+ make_scoped_ptr(new LoginDatabase(test_login_db_file_path()))));
+ store->Init(syncer::SyncableService::StartSyncFlare());
+ store->RemoveLoginsCreatedBetween(base::Time(), base::Time::Max());
+
+ // Set up the initial test data set.
+ ScopedVector<PasswordForm> all_credentials;
+ for (size_t i = 0; i < arraysize(kTestCredentials); ++i) {
+ all_credentials.push_back(
+ CreatePasswordFormFromDataForTesting(kTestCredentials[i]));
+ all_credentials.back()->date_synced =
+ all_credentials.back()->date_created;
+ store->AddLogin(*all_credentials.back());
+ base::MessageLoop::current()->RunUntilIdle();
+ }
+
+ // The helper must be injected after the initial test data is set up,
+ // otherwise it will already start propagating updates as new Android
+ // credentials are added.
+ MockAffiliatedMatchHelper* mock_helper = new MockAffiliatedMatchHelper;
+ store->SetAffiliatedMatchHelper(make_scoped_ptr(mock_helper));
+ store->enable_propagating_password_changes_to_web_credentials(
+ propagation_enabled);
+
+ // Calculate how the correctly updated test data set should look like.
+ size_t expected_number_of_propageted_updates =
+ propagation_enabled ? kExpectedNumberOfPropagatedUpdates : 0u;
+ ScopedVector<PasswordForm> expected_credentials_after_update;
+ for (size_t i = 0; i < all_credentials.size(); ++i) {
+ expected_credentials_after_update.push_back(
+ new autofill::PasswordForm(*all_credentials[i]));
+ if (i < 1 + expected_number_of_propageted_updates) {
+ expected_credentials_after_update.back()->password_value =
+ base::WideToUTF16(kTestNewPassword);
+ }
+ }
+
+ if (propagation_enabled) {
+ std::vector<std::string> affiliated_web_realms;
+ affiliated_web_realms.push_back(kTestWebRealm1);
+ affiliated_web_realms.push_back(kTestWebRealm2);
+ affiliated_web_realms.push_back(kTestWebRealm3);
+ affiliated_web_realms.push_back(kTestWebRealm4);
+ affiliated_web_realms.push_back(kTestWebRealm5);
+ mock_helper->ExpectCallToGetAffiliatedWebRealms(
+ *expected_credentials_after_update[0], affiliated_web_realms);
+ }
+
+ // Explicitly update the Android credential, wait until things calm down,
+ // then query all passwords and expect that:
+ // 1.) The positive samples in |kTestCredentials| have the new password,
+ // but the negative samples do not.
+ // 2.) Change notifications are sent about the updates. Note that as the
+ // test interacts with the (Update|Add)LoginSync methods, only the
+ // derived changes should trigger notifications, the first one would
+ // normally be trigger by Sync.
+ MockPasswordStoreObserver mock_observer;
+ store->AddObserver(&mock_observer);
+ if (propagation_enabled) {
+ EXPECT_CALL(mock_observer, OnLoginsChanged(testing::SizeIs(
+ expected_number_of_propageted_updates)));
+ }
+ if (test_remove_and_add_login) {
+ store->RemoveLoginSync(*all_credentials[0]);
+ store->AddLoginSync(*expected_credentials_after_update[0]);
+ } else {
+ store->UpdateLoginSync(*expected_credentials_after_update[0]);
+ }
+ base::MessageLoop::current()->RunUntilIdle();
+ store->RemoveObserver(&mock_observer);
+
+ MockPasswordStoreConsumer mock_consumer;
+ EXPECT_CALL(
+ mock_consumer,
+ OnGetPasswordStoreResultsConstRef(UnorderedPasswordFormElementsAre(
+ expected_credentials_after_update.get())));
+ store->GetAutofillableLogins(&mock_consumer);
+ store->Shutdown();
+ base::MessageLoop::current()->RunUntilIdle();
+ }
+ }
+}
+
} // namespace password_manager

Powered by Google App Engine
This is Rietveld 408576698