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 3b06f4ab1b18cd6c8521ee2844052cf2a4245dc8..4d8cf7ab6ab92383b28d4dadd97ce4c713849360 100644 |
--- a/components/password_manager/core/browser/password_store_unittest.cc |
+++ b/components/password_manager/core/browser/password_store_unittest.cc |
@@ -2,6 +2,11 @@ |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
+// The passwords in the tests below are all empty because PasswordStoreDefault |
+// does not store the actual passwords on OS X (they are stored in the Keychain |
+// instead). We could special-case it, but it is easier to just have empty |
+// passwords. This will not be needed anymore if crbug.com/466638 is fixed. |
+ |
#include "base/basictypes.h" |
#include "base/bind.h" |
#include "base/files/scoped_temp_dir.h" |
@@ -9,6 +14,8 @@ |
#include "base/strings/string_util.h" |
#include "base/synchronization/waitable_event.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" |
#include "components/password_manager/core/browser/password_manager_test_utils.h" |
#include "components/password_manager/core/browser/password_store_consumer.h" |
#include "components/password_manager/core/browser/password_store_default.h" |
@@ -25,6 +32,16 @@ namespace password_manager { |
namespace { |
+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 kTestAndroidRealm1[] = "android://hash@com.example.android/"; |
+const char kTestAndroidRealm2[] = "android://hash@com.example.two.android/"; |
+const char kTestAndroidRealm3[] = "android://hash@com.example.three.android/"; |
+const char kTestUnrelatedAndroidRealm[] = |
+ "android://hash@com.notexample.android/"; |
+ |
class MockPasswordStoreConsumer : public PasswordStoreConsumer { |
public: |
MOCK_METHOD1(OnGetPasswordStoreResultsConstRef, |
@@ -36,6 +53,38 @@ class MockPasswordStoreConsumer : public PasswordStoreConsumer { |
} |
}; |
+class MockAffiliatedMatchHelper : public AffiliatedMatchHelper { |
+ public: |
+ MockAffiliatedMatchHelper() |
+ : AffiliatedMatchHelper(nullptr, |
+ make_scoped_ptr<AffiliationService>(nullptr)) {} |
+ |
+ // Expects GetAffiliatedAndroidRealms() to be called with the |
+ // |expected_observed_form|, and will cause the result callback supplied to |
+ // GetAffiliatedAndroidRealms() to be invoked with |results_to_return|. |
+ void ExpectCallToGetAffiliatedAndroidRealms( |
+ const autofill::PasswordForm& expected_observed_form, |
+ const std::vector<std::string>& results_to_return) { |
+ EXPECT_CALL(*this, |
+ OnGetAffiliatedAndroidRealmsCalled(expected_observed_form)) |
+ .WillOnce(testing::Return(results_to_return)); |
+ } |
+ |
+ private: |
+ MOCK_METHOD1(OnGetAffiliatedAndroidRealmsCalled, |
+ std::vector<std::string>(const PasswordForm&)); |
+ |
+ void GetAffiliatedAndroidRealms( |
+ const autofill::PasswordForm& observed_form, |
+ const AffiliatedRealmsCallback& result_callback) override { |
+ std::vector<std::string> affiliated_android_realms = |
+ OnGetAffiliatedAndroidRealmsCalled(observed_form); |
+ result_callback.Run(affiliated_android_realms); |
+ } |
+ |
+ DISALLOW_COPY_AND_ASSIGN(MockAffiliatedMatchHelper); |
+}; |
+ |
class StartSyncFlareMock { |
public: |
StartSyncFlareMock() {} |
@@ -73,9 +122,6 @@ TEST_F(PasswordStoreTest, IgnoreOldWwwGoogleLogins) { |
store->Init(syncer::SyncableService::StartSyncFlare()); |
const time_t cutoff = 1325376000; // 00:00 Jan 1 2012 UTC |
- // The passwords are all empty because PasswordStoreDefault doesn't store the |
- // actual passwords on OS X (they're stored in the Keychain instead). We could |
- // special-case it, but it's easier to just have empty passwords. |
static const PasswordFormData form_data[] = { |
// A form on https://www.google.com/ older than the cutoff. Will be ignored. |
{ PasswordForm::SCHEME_HTML, |
@@ -209,4 +255,181 @@ TEST_F(PasswordStoreTest, StartSyncFlare) { |
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. |
+TEST_F(PasswordStoreTest, GetLoginsWithoutAffiliations) { |
+ /* clang-format off */ |
+ static const PasswordFormData kTestCredentials[] = { |
+ // Credential that is an exact match of the observed form. |
+ {PasswordForm::SCHEME_HTML, |
+ kTestWebRealm1, |
+ kTestWebOrigin1, |
+ "", L"", L"", L"", |
+ L"username_value_1", |
+ L"", true, true, 1}, |
+ // Credential that is a PSL match of the observed form. |
+ {PasswordForm::SCHEME_HTML, |
+ kTestWebRealm2, |
+ kTestWebOrigin2, |
+ "", L"", L"", L"", |
+ L"username_value_2", |
+ L"", true, true, 1}, |
+ // Credential for an unrelated Android application. |
+ {PasswordForm::SCHEME_HTML, |
+ kTestUnrelatedAndroidRealm, |
+ "", "", L"", L"", L"", |
+ L"username_value_3", |
+ L"", true, true, 1}}; |
+ /* clang-format on */ |
+ |
+ scoped_refptr<PasswordStoreDefault> store(new PasswordStoreDefault( |
+ base::MessageLoopProxy::current(), base::MessageLoopProxy::current(), |
+ make_scoped_ptr(new LoginDatabase(test_login_db_file_path())))); |
+ store->Init(syncer::SyncableService::StartSyncFlare()); |
+ |
+ MockAffiliatedMatchHelper* mock_helper = new MockAffiliatedMatchHelper; |
+ store->SetAffiliatedMatchHelper(make_scoped_ptr(mock_helper)); |
+ |
+ ScopedVector<PasswordForm> all_credentials; |
+ for (size_t i = 0; i < arraysize(kTestCredentials); ++i) { |
+ all_credentials.push_back( |
+ CreatePasswordFormFromDataForTesting(kTestCredentials[i])); |
+ store->AddLogin(*all_credentials.back()); |
+ base::MessageLoop::current()->RunUntilIdle(); |
+ } |
+ |
+ PasswordForm observed_form; |
+ observed_form.scheme = PasswordForm::SCHEME_HTML; |
+ observed_form.origin = GURL(kTestWebOrigin1); |
+ observed_form.ssl_valid = true; |
+ observed_form.signon_realm = kTestWebRealm1; |
+ |
+ MockPasswordStoreConsumer mock_consumer; |
+ ScopedVector<PasswordForm> expected_results; |
+ expected_results.push_back(new PasswordForm(*all_credentials[0])); |
+ expected_results.push_back(new PasswordForm(*all_credentials[1])); |
+ for (PasswordForm* result : expected_results) { |
+ if (result->signon_realm == observed_form.signon_realm) |
+ continue; |
+ result->original_signon_realm = result->signon_realm; |
+ result->origin = observed_form.origin; |
+ result->signon_realm = observed_form.signon_realm; |
+ } |
+ |
+ std::vector<std::string> no_affiliated_android_realms; |
+ mock_helper->ExpectCallToGetAffiliatedAndroidRealms( |
+ observed_form, no_affiliated_android_realms); |
+ |
+ EXPECT_CALL(mock_consumer, |
+ OnGetPasswordStoreResultsConstRef( |
+ ContainsSamePasswordForms(expected_results.get()))); |
+ store->GetLogins(observed_form, PasswordStore::ALLOW_PROMPT, &mock_consumer); |
+ store->Shutdown(); |
+ base::MessageLoop::current()->RunUntilIdle(); |
+} |
+ |
+// There are 3 Android applications affiliated with the realm of the observed |
+// form, with the PasswordStore having credentials for two of these (even two |
+// credentials for one). GetLoginsWithAffiliations() should return the exact, |
+// and PSL matching credentials, and the credentials for these two Android |
+// applications, but not for the unaffiliated Android application. |
+TEST_F(PasswordStoreTest, GetLoginsWithAffiliations) { |
+ /* clang-format off */ |
+ static const PasswordFormData kTestCredentials[] = { |
+ // Credential that is an exact match of the observed form. |
+ {PasswordForm::SCHEME_HTML, |
+ kTestWebRealm1, |
+ kTestWebOrigin1, |
+ "", L"", L"", L"", |
+ L"username_value_1", |
+ L"", true, true, 1}, |
+ // Credential that is a PSL match of the observed form. |
+ {PasswordForm::SCHEME_HTML, |
+ kTestWebRealm2, |
+ kTestWebOrigin2, |
+ "", L"", L"", L"", |
+ L"username_value_2", |
+ L"", true, true, 1}, |
+ // Credential for an Android application affiliated with the realm of the |
+ // observed from. |
+ {PasswordForm::SCHEME_HTML, |
+ kTestAndroidRealm1, |
+ "", "", L"", L"", L"", |
+ L"username_value_3", |
+ L"", true, true, 1}, |
+ // Second credential for the same Android application. |
+ {PasswordForm::SCHEME_HTML, |
+ kTestAndroidRealm1, |
+ "", "", L"", L"", L"", |
+ L"username_value_3b", |
+ L"", true, true, 1}, |
+ // Credential for another Android application affiliated with the realm |
+ // of the observed from. |
+ {PasswordForm::SCHEME_HTML, |
+ kTestAndroidRealm2, |
+ "", "", L"", L"", L"", |
+ L"username_value_4", |
+ L"", true, true, 1}, |
+ // Credential for an unrelated Android application. |
+ {PasswordForm::SCHEME_HTML, |
+ kTestUnrelatedAndroidRealm, |
+ "", "", L"", L"", L"", |
+ L"username_value_5", |
+ L"", true, true, 1}}; |
+ /* clang-format on */ |
+ |
+ scoped_refptr<PasswordStoreDefault> store(new PasswordStoreDefault( |
+ base::MessageLoopProxy::current(), base::MessageLoopProxy::current(), |
+ make_scoped_ptr(new LoginDatabase(test_login_db_file_path())))); |
+ store->Init(syncer::SyncableService::StartSyncFlare()); |
+ |
+ MockAffiliatedMatchHelper* mock_helper = new MockAffiliatedMatchHelper; |
+ store->SetAffiliatedMatchHelper(make_scoped_ptr(mock_helper)); |
+ |
+ ScopedVector<PasswordForm> all_credentials; |
+ for (size_t i = 0; i < arraysize(kTestCredentials); ++i) { |
+ all_credentials.push_back( |
+ CreatePasswordFormFromDataForTesting(kTestCredentials[i])); |
+ store->AddLogin(*all_credentials.back()); |
+ base::MessageLoop::current()->RunUntilIdle(); |
+ } |
+ |
+ PasswordForm observed_form; |
+ observed_form.scheme = PasswordForm::SCHEME_HTML; |
+ observed_form.origin = GURL(kTestWebOrigin1); |
+ observed_form.ssl_valid = true; |
+ observed_form.signon_realm = kTestWebRealm1; |
+ |
+ MockPasswordStoreConsumer mock_consumer; |
+ ScopedVector<PasswordForm> expected_results; |
+ expected_results.push_back(new PasswordForm(*all_credentials[0])); |
+ expected_results.push_back(new PasswordForm(*all_credentials[1])); |
+ expected_results.push_back(new PasswordForm(*all_credentials[2])); |
+ expected_results.push_back(new PasswordForm(*all_credentials[3])); |
+ expected_results.push_back(new PasswordForm(*all_credentials[4])); |
+ for (PasswordForm* result : expected_results) { |
+ if (result->signon_realm == observed_form.signon_realm) |
+ continue; |
+ result->original_signon_realm = result->signon_realm; |
+ result->signon_realm = observed_form.signon_realm; |
+ if (!result->origin.is_empty()) |
+ result->origin = observed_form.origin; |
+ } |
+ |
+ std::vector<std::string> affiliated_android_realms; |
+ affiliated_android_realms.push_back(kTestAndroidRealm1); |
+ affiliated_android_realms.push_back(kTestAndroidRealm2); |
+ affiliated_android_realms.push_back(kTestAndroidRealm3); |
+ mock_helper->ExpectCallToGetAffiliatedAndroidRealms( |
+ observed_form, affiliated_android_realms); |
+ |
+ EXPECT_CALL(mock_consumer, |
+ OnGetPasswordStoreResultsConstRef( |
+ ContainsSamePasswordForms(expected_results.get()))); |
+ store->GetLogins(observed_form, PasswordStore::ALLOW_PROMPT, &mock_consumer); |
+ store->Shutdown(); |
+ base::MessageLoop::current()->RunUntilIdle(); |
+} |
+ |
} // namespace password_manager |