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

Unified Diff: chrome/browser/password_manager/password_store_mac_unittest.cc

Issue 2909283002: Delete PasswordStoreMac and SimplePasswordStoreMac. (Closed)
Patch Set: test Created 3 years, 7 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: chrome/browser/password_manager/password_store_mac_unittest.cc
diff --git a/chrome/browser/password_manager/password_store_mac_unittest.cc b/chrome/browser/password_manager/password_store_mac_unittest.cc
deleted file mode 100644
index 02241aba111d201eb4b7eb22ab2e745c0e2eb01b..0000000000000000000000000000000000000000
--- a/chrome/browser/password_manager/password_store_mac_unittest.cc
+++ /dev/null
@@ -1,2113 +0,0 @@
-// Copyright (c) 2012 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 "chrome/browser/password_manager/password_store_mac.h"
-
-#include <stddef.h>
-
-#include <string>
-
-#include "base/files/scoped_temp_dir.h"
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "base/message_loop/message_loop.h"
-#include "base/run_loop.h"
-#include "base/scoped_observer.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/test/histogram_tester.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "chrome/browser/password_manager/password_store_mac_internal.h"
-#include "chrome/common/chrome_paths.h"
-#include "components/os_crypt/os_crypt_mocker.h"
-#include "components/password_manager/core/browser/login_database.h"
-#include "components/password_manager/core/browser/password_manager_test_utils.h"
-#include "components/password_manager/core/browser/password_manager_util.h"
-#include "components/password_manager/core/browser/password_store_consumer.h"
-#include "components/password_manager/core/browser/password_store_origin_unittest.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/test/test_browser_thread_bundle.h"
-#include "content/public/test/test_utils.h"
-#include "crypto/mock_apple_keychain.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/origin.h"
-
-using autofill::PasswordForm;
-using base::ASCIIToUTF16;
-using base::WideToUTF16;
-using content::BrowserThread;
-using crypto::MockAppleKeychain;
-using internal_keychain_helpers::FormsMatchForMerge;
-using internal_keychain_helpers::STRICT_FORM_MATCH;
-using password_manager::CreatePasswordFormFromDataForTesting;
-using password_manager::LoginDatabase;
-using password_manager::PasswordFormData;
-using password_manager::PasswordStore;
-using password_manager::PasswordStoreChange;
-using password_manager::PasswordStoreChangeList;
-using password_manager::PasswordStoreConsumer;
-using testing::_;
-using testing::DoAll;
-using testing::Invoke;
-using testing::IsEmpty;
-using testing::SizeIs;
-using testing::WithArg;
-
-namespace {
-
-ACTION(QuitUIMessageLoop) {
- DCHECK_CURRENTLY_ON(BrowserThread::UI);
- base::MessageLoop::current()->QuitWhenIdle();
-}
-
-// From the mock's argument #0 of type const std::vector<PasswordForm*>& takes
-// the first form and copies it to the form pointed to by |target_form_ptr|.
-ACTION_P(SaveACopyOfFirstForm, target_form_ptr) {
- ASSERT_FALSE(arg0.empty());
- *target_form_ptr = *arg0[0];
-}
-
-void Noop() {}
-
-class MockPasswordStoreConsumer : public PasswordStoreConsumer {
- public:
- MOCK_METHOD1(OnGetPasswordStoreResultsConstRef,
- void(const std::vector<std::unique_ptr<PasswordForm>>&));
-
- // GMock cannot mock methods with move-only args.
- void OnGetPasswordStoreResults(
- std::vector<std::unique_ptr<PasswordForm>> results) override {
- OnGetPasswordStoreResultsConstRef(results);
- }
-};
-
-// A LoginDatabase that simulates an Init() method that takes a long time.
-class SlowToInitLoginDatabase : public password_manager::LoginDatabase {
- public:
- // Creates an instance whose Init() method will block until |event| is
- // signaled. |event| must outlive |this|.
- SlowToInitLoginDatabase(const base::FilePath& db_path,
- base::WaitableEvent* event)
- : password_manager::LoginDatabase(db_path), event_(event) {}
- ~SlowToInitLoginDatabase() override {}
-
- // LoginDatabase:
- bool Init() override {
- event_->Wait();
- return password_manager::LoginDatabase::Init();
- }
-
- private:
- base::WaitableEvent* event_;
-
- DISALLOW_COPY_AND_ASSIGN(SlowToInitLoginDatabase);
-};
-
-#pragma mark -
-
-// Macro to simplify calling CheckFormsAgainstExpectations with a useful label.
-#define CHECK_FORMS(forms, expectations, i) \
- CheckFormsAgainstExpectations(forms, expectations, #forms, i)
-
-// Ensures that the data in |forms| match |expectations|, causing test failures
-// for any discrepencies.
-// TODO(stuartmorgan): This is current order-dependent; ideally it shouldn't
-// matter if |forms| and |expectations| are scrambled.
-void CheckFormsAgainstExpectations(
- const std::vector<std::unique_ptr<PasswordForm>>& forms,
- const std::vector<PasswordFormData*>& expectations,
-
- const char* forms_label,
- unsigned int test_number) {
- EXPECT_EQ(expectations.size(), forms.size()) << forms_label << " in test "
- << test_number;
- if (expectations.size() != forms.size())
- return;
-
- for (unsigned int i = 0; i < expectations.size(); ++i) {
- SCOPED_TRACE(testing::Message() << forms_label << " in test " << test_number
- << ", item " << i);
- PasswordForm* form = forms[i].get();
- PasswordFormData* expectation = expectations[i];
- EXPECT_EQ(expectation->scheme, form->scheme);
- EXPECT_EQ(std::string(expectation->signon_realm), form->signon_realm);
- EXPECT_EQ(GURL(expectation->origin), form->origin);
- EXPECT_EQ(GURL(expectation->action), form->action);
- EXPECT_EQ(WideToUTF16(expectation->submit_element), form->submit_element);
- EXPECT_EQ(WideToUTF16(expectation->username_element),
- form->username_element);
- EXPECT_EQ(WideToUTF16(expectation->password_element),
- form->password_element);
- if (expectation->username_value) {
- EXPECT_EQ(WideToUTF16(expectation->username_value), form->username_value);
- EXPECT_EQ(WideToUTF16(expectation->username_value), form->display_name);
- EXPECT_TRUE(form->skip_zero_click);
- if (expectation->password_value &&
- wcscmp(expectation->password_value,
- password_manager::kTestingFederatedLoginMarker) == 0) {
- EXPECT_TRUE(form->password_value.empty());
- EXPECT_EQ(
- url::Origin(GURL(password_manager::kTestingFederationUrlSpec)),
- form->federation_origin);
- } else {
- EXPECT_EQ(WideToUTF16(expectation->password_value),
- form->password_value);
- EXPECT_TRUE(form->federation_origin.unique());
- }
- } else {
- EXPECT_TRUE(form->blacklisted_by_user);
- }
- EXPECT_EQ(expectation->preferred, form->preferred);
- EXPECT_DOUBLE_EQ(expectation->creation_time,
- form->date_created.ToDoubleT());
- base::Time created = base::Time::FromDoubleT(expectation->creation_time);
- EXPECT_EQ(
- created + base::TimeDelta::FromDays(
- password_manager::kTestingDaysAfterPasswordsAreSynced),
- form->date_synced);
- EXPECT_EQ(GURL(password_manager::kTestingIconUrlSpec), form->icon_url);
- }
-}
-
-PasswordStoreChangeList AddChangeForForm(const PasswordForm& form) {
- return PasswordStoreChangeList(
- 1, PasswordStoreChange(PasswordStoreChange::ADD, form));
-}
-
-class PasswordStoreMacTestDelegate {
- public:
- PasswordStoreMacTestDelegate();
- ~PasswordStoreMacTestDelegate();
-
- PasswordStoreMac* store() { return store_.get(); }
-
- static void FinishAsyncProcessing();
-
- private:
- void Initialize();
-
- void ClosePasswordStore();
-
- base::FilePath test_login_db_file_path() const;
-
- base::MessageLoopForUI message_loop_;
- base::ScopedTempDir db_dir_;
- std::unique_ptr<LoginDatabase> login_db_;
- scoped_refptr<PasswordStoreMac> store_;
-
- DISALLOW_COPY_AND_ASSIGN(PasswordStoreMacTestDelegate);
-};
-
-PasswordStoreMacTestDelegate::PasswordStoreMacTestDelegate() {
- Initialize();
-}
-
-PasswordStoreMacTestDelegate::~PasswordStoreMacTestDelegate() {
- ClosePasswordStore();
-}
-
-void PasswordStoreMacTestDelegate::FinishAsyncProcessing() {
- base::RunLoop().RunUntilIdle();
-}
-
-void PasswordStoreMacTestDelegate::Initialize() {
- ASSERT_TRUE(db_dir_.CreateUniqueTempDir());
-
- // Ensure that LoginDatabase will use the mock keychain if it needs to
- // encrypt/decrypt a password.
- OSCryptMocker::SetUpWithSingleton();
- login_db_.reset(new LoginDatabase(test_login_db_file_path()));
- ASSERT_TRUE(login_db_->Init());
-
- // Create and initialize the password store.
- store_ = new PasswordStoreMac(base::ThreadTaskRunnerHandle::Get(),
- base::ThreadTaskRunnerHandle::Get(),
- base::WrapUnique(new MockAppleKeychain));
- store_->set_login_metadata_db(login_db_.get());
- store_->login_metadata_db()->set_clear_password_values(false);
-}
-
-void PasswordStoreMacTestDelegate::ClosePasswordStore() {
- store_->ShutdownOnUIThread();
- FinishAsyncProcessing();
- OSCryptMocker::TearDown();
-}
-
-base::FilePath PasswordStoreMacTestDelegate::test_login_db_file_path() const {
- return db_dir_.GetPath().Append(FILE_PATH_LITERAL("login.db"));
-}
-
-} // namespace
-
-namespace password_manager {
-
-INSTANTIATE_TYPED_TEST_CASE_P(Mac,
- PasswordStoreOriginTest,
- PasswordStoreMacTestDelegate);
-
-} // namespace password_manager
-
-#pragma mark -
-
-class PasswordStoreMacInternalsTest : public testing::Test {
- public:
- void SetUp() override {
- MockAppleKeychain::KeychainTestData test_data[] = {
- // Basic HTML form.
- {kSecAuthenticationTypeHTMLForm, "some.domain.com",
- kSecProtocolTypeHTTP, NULL, 0, NULL, "20020601171500Z", "joe_user",
- "sekrit", false},
- // HTML form with path.
- {kSecAuthenticationTypeHTMLForm, "some.domain.com",
- kSecProtocolTypeHTTP, "/insecure.html", 0, NULL, "19991231235959Z",
- "joe_user", "sekrit", false},
- // Secure HTML form with path.
- {kSecAuthenticationTypeHTMLForm, "some.domain.com",
- kSecProtocolTypeHTTPS, "/secure.html", 0, NULL, "20100908070605Z",
- "secure_user", "password", false},
- // True negative item.
- {kSecAuthenticationTypeHTMLForm, "dont.remember.com",
- kSecProtocolTypeHTTP, NULL, 0, NULL, "20000101000000Z", "", "", true},
- // De-facto negative item, type one.
- {kSecAuthenticationTypeHTMLForm, "dont.remember.com",
- kSecProtocolTypeHTTP, NULL, 0, NULL, "20000101000000Z",
- "Password Not Stored", "", false},
- // De-facto negative item, type two.
- {kSecAuthenticationTypeHTMLForm, "dont.remember.com",
- kSecProtocolTypeHTTPS, NULL, 0, NULL, "20000101000000Z",
- "Password Not Stored", " ", false},
- // HTTP auth basic, with port and path.
- {kSecAuthenticationTypeHTTPBasic, "some.domain.com",
- kSecProtocolTypeHTTP, "/insecure.html", 4567, "low_security",
- "19980330100000Z", "basic_auth_user", "basic", false},
- // HTTP auth digest, secure.
- {kSecAuthenticationTypeHTTPDigest, "some.domain.com",
- kSecProtocolTypeHTTPS, NULL, 0, "high_security", "19980330100000Z",
- "digest_auth_user", "digest", false},
- // An FTP password with an invalid date, for edge-case testing.
- {kSecAuthenticationTypeDefault, "a.server.com", kSecProtocolTypeFTP,
- NULL, 0, NULL, "20010203040", "abc", "123", false},
- // Password for an Android application.
- {kSecAuthenticationTypeHTMLForm, "android://hash@com.domain.some/",
- kSecProtocolTypeHTTPS, "", 0, NULL, "20150515141312Z", "joe_user",
- "secret", false},
- };
-
- keychain_ = new MockAppleKeychain();
-
- for (unsigned int i = 0; i < arraysize(test_data); ++i) {
- keychain_->AddTestItem(test_data[i]);
- }
- }
-
- void TearDown() override {
- ExpectCreatesAndFreesBalanced();
- ExpectCreatorCodesSet();
- delete keychain_;
- }
-
- protected:
- // Causes a test failure unless everything returned from keychain_'s
- // ItemCopyAttributesAndData, SearchCreateFromAttributes, and SearchCopyNext
- // was correctly freed.
- void ExpectCreatesAndFreesBalanced() {
- EXPECT_EQ(0, keychain_->UnfreedSearchCount());
- EXPECT_EQ(0, keychain_->UnfreedKeychainItemCount());
- EXPECT_EQ(0, keychain_->UnfreedAttributeDataCount());
- }
-
- // Causes a test failure unless any Keychain items added during the test have
- // their creator code set.
- void ExpectCreatorCodesSet() {
- EXPECT_TRUE(keychain_->CreatorCodesSetForAddedItems());
- }
-
- MockAppleKeychain* keychain_;
-};
-
-#pragma mark -
-
-TEST_F(PasswordStoreMacInternalsTest, TestKeychainToFormTranslation) {
- typedef struct {
- const PasswordForm::Scheme scheme;
- const char* signon_realm;
- const char* origin;
- const wchar_t* username; // Set to NULL to check for a blacklist entry.
- const wchar_t* password;
- const int creation_year;
- const int creation_month;
- const int creation_day;
- const int creation_hour;
- const int creation_minute;
- const int creation_second;
- } TestExpectations;
-
- TestExpectations expected[] = {
- {PasswordForm::SCHEME_HTML, "http://some.domain.com/",
- "http://some.domain.com/", L"joe_user", L"sekrit", 2002, 6, 1, 17, 15,
- 0},
- {PasswordForm::SCHEME_HTML, "http://some.domain.com/",
- "http://some.domain.com/insecure.html", L"joe_user", L"sekrit", 1999, 12,
- 31, 23, 59, 59},
- {PasswordForm::SCHEME_HTML, "https://some.domain.com/",
- "https://some.domain.com/secure.html", L"secure_user", L"password", 2010,
- 9, 8, 7, 6, 5},
- {PasswordForm::SCHEME_HTML, "http://dont.remember.com/",
- "http://dont.remember.com/", NULL, NULL, 2000, 1, 1, 0, 0, 0},
- {PasswordForm::SCHEME_HTML, "http://dont.remember.com/",
- "http://dont.remember.com/", NULL, NULL, 2000, 1, 1, 0, 0, 0},
- {PasswordForm::SCHEME_HTML, "https://dont.remember.com/",
- "https://dont.remember.com/", NULL, NULL, 2000, 1, 1, 0, 0, 0},
- {PasswordForm::SCHEME_BASIC, "http://some.domain.com:4567/low_security",
- "http://some.domain.com:4567/insecure.html", L"basic_auth_user",
- L"basic", 1998, 03, 30, 10, 00, 00},
- {PasswordForm::SCHEME_DIGEST, "https://some.domain.com/high_security",
- "https://some.domain.com/", L"digest_auth_user", L"digest", 1998, 3, 30,
- 10, 0, 0},
- // This one gives us an invalid date, which we will treat as a "NULL" date
- // which is 1601.
- {PasswordForm::SCHEME_OTHER, "http://a.server.com/",
- "http://a.server.com/", L"abc", L"123", 1601, 1, 1, 0, 0, 0},
- {PasswordForm::SCHEME_HTML, "android://hash@com.domain.some/", "",
- L"joe_user", L"secret", 2015, 5, 15, 14, 13, 12},
- };
-
- for (unsigned int i = 0; i < arraysize(expected); ++i) {
- SCOPED_TRACE(testing::Message("In iteration ") << i);
- // Create our fake KeychainItemRef; see MockAppleKeychain docs.
- SecKeychainItemRef keychain_item =
- reinterpret_cast<SecKeychainItemRef>(i + 1);
- PasswordForm form;
- bool parsed = internal_keychain_helpers::FillPasswordFormFromKeychainItem(
- *keychain_, keychain_item, &form, true);
-
- EXPECT_TRUE(parsed);
-
- EXPECT_EQ(expected[i].scheme, form.scheme);
- EXPECT_EQ(GURL(expected[i].origin), form.origin);
- EXPECT_EQ(std::string(expected[i].signon_realm), form.signon_realm);
- if (expected[i].username) {
- EXPECT_EQ(WideToUTF16(expected[i].username), form.username_value);
- EXPECT_EQ(WideToUTF16(expected[i].password), form.password_value);
- EXPECT_FALSE(form.blacklisted_by_user);
- } else {
- EXPECT_TRUE(form.blacklisted_by_user);
- }
- base::Time::Exploded exploded_time;
- form.date_created.UTCExplode(&exploded_time);
- EXPECT_EQ(expected[i].creation_year, exploded_time.year);
- EXPECT_EQ(expected[i].creation_month, exploded_time.month);
- EXPECT_EQ(expected[i].creation_day, exploded_time.day_of_month);
- EXPECT_EQ(expected[i].creation_hour, exploded_time.hour);
- EXPECT_EQ(expected[i].creation_minute, exploded_time.minute);
- EXPECT_EQ(expected[i].creation_second, exploded_time.second);
- }
-
- {
- // Use an invalid ref, to make sure errors are reported.
- SecKeychainItemRef keychain_item = reinterpret_cast<SecKeychainItemRef>(99);
- PasswordForm form;
- bool parsed = internal_keychain_helpers::FillPasswordFormFromKeychainItem(
- *keychain_, keychain_item, &form, true);
- EXPECT_FALSE(parsed);
- }
-}
-
-TEST_F(PasswordStoreMacInternalsTest, TestKeychainSearch) {
- struct TestDataAndExpectation {
- const PasswordFormData data;
- const size_t expected_fill_matches;
- const size_t expected_merge_matches;
- };
- // Most fields are left blank because we don't care about them for searching.
- TestDataAndExpectation test_data[] = {
- // An HTML form we've seen.
- {{PasswordForm::SCHEME_HTML, "http://some.domain.com/", NULL, NULL, NULL,
- NULL, NULL, L"joe_user", NULL, false, 0},
- 2,
- 2},
- {{PasswordForm::SCHEME_HTML, "http://some.domain.com/", NULL, NULL, NULL,
- NULL, NULL, L"wrong_user", NULL, false, 0},
- 2,
- 0},
- // An HTML form we haven't seen
- {{PasswordForm::SCHEME_HTML, "http://www.unseendomain.com/", NULL, NULL,
- NULL, NULL, NULL, L"joe_user", NULL, false, 0},
- 0,
- 0},
- // Basic auth that should match.
- {{PasswordForm::SCHEME_BASIC, "http://some.domain.com:4567/low_security",
- NULL, NULL, NULL, NULL, NULL, L"basic_auth_user", NULL, false, 0},
- 1,
- 1},
- // Basic auth with the wrong port.
- {{PasswordForm::SCHEME_BASIC, "http://some.domain.com:1111/low_security",
- NULL, NULL, NULL, NULL, NULL, L"basic_auth_user", NULL, false, 0},
- 0,
- 0},
- // Digest auth we've saved under https, visited with http.
- {{PasswordForm::SCHEME_DIGEST, "http://some.domain.com/high_security",
- NULL, NULL, NULL, NULL, NULL, L"digest_auth_user", NULL, false, 0},
- 0,
- 0},
- // Digest auth that should match.
- {{PasswordForm::SCHEME_DIGEST, "https://some.domain.com/high_security",
- NULL, NULL, NULL, NULL, NULL, L"wrong_user", NULL, false, 0},
- 1,
- 0},
- // Digest auth with the wrong domain.
- {{PasswordForm::SCHEME_DIGEST, "https://some.domain.com/other_domain",
- NULL, NULL, NULL, NULL, NULL, L"digest_auth_user", NULL, false, 0},
- 0,
- 0},
- // Android credentials (both legacy ones with origin, and without).
- {{PasswordForm::SCHEME_HTML, "android://hash@com.domain.some/",
- "android://hash@com.domain.some/", NULL, NULL, NULL, NULL, L"joe_user",
- NULL, false, 0},
- 1,
- 1},
- {{PasswordForm::SCHEME_HTML, "android://hash@com.domain.some/", NULL,
- NULL, NULL, NULL, NULL, L"joe_user", NULL, false, 0},
- 1,
- 1},
- // Federated logins do not have a corresponding Keychain entry, and should
- // not match the username/password stored for the same application. Note
- // that it will match for filling, however, because that part does not
- // know
- // that it is a federated login.
- {{PasswordForm::SCHEME_HTML, "android://hash@com.domain.some/", NULL,
- NULL, NULL, NULL, NULL, L"joe_user",
- password_manager::kTestingFederatedLoginMarker, false, 0},
- 1,
- 0},
- /// Garbage forms should have no matches.
- {{PasswordForm::SCHEME_HTML, "foo/bar/baz", NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, false, 0},
- 0,
- 0},
- };
-
- MacKeychainPasswordFormAdapter keychain_adapter(keychain_);
- MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_);
- owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
- for (unsigned int i = 0; i < arraysize(test_data); ++i) {
- std::unique_ptr<PasswordForm> query_form =
- CreatePasswordFormFromDataForTesting(test_data[i].data);
-
- // Check matches treating the form as a fill target.
- std::vector<std::unique_ptr<PasswordForm>> matching_items =
- keychain_adapter.PasswordsFillingForm(query_form->signon_realm,
- query_form->scheme);
- EXPECT_EQ(test_data[i].expected_fill_matches, matching_items.size());
-
- // Check matches treating the form as a merging target.
- EXPECT_EQ(test_data[i].expected_merge_matches > 0,
- keychain_adapter.HasPasswordsMergeableWithForm(*query_form));
- std::vector<SecKeychainItemRef> keychain_items;
- std::vector<internal_keychain_helpers::ItemFormPair> item_form_pairs =
- internal_keychain_helpers::
- ExtractAllKeychainItemAttributesIntoPasswordForms(&keychain_items,
- *keychain_);
- matching_items =
- internal_keychain_helpers::ExtractPasswordsMergeableWithForm(
- *keychain_, item_form_pairs, *query_form);
- EXPECT_EQ(test_data[i].expected_merge_matches, matching_items.size());
- for (std::vector<SecKeychainItemRef>::iterator i = keychain_items.begin();
- i != keychain_items.end(); ++i) {
- keychain_->Free(*i);
- }
-
- // None of the pre-seeded items are owned by us, so none should match an
- // owned-passwords-only search.
- matching_items = owned_keychain_adapter.PasswordsFillingForm(
- query_form->signon_realm, query_form->scheme);
- EXPECT_EQ(0U, matching_items.size());
- }
-}
-
-// Changes just the origin path of |form|.
-static void SetPasswordFormPath(PasswordForm* form, const char* path) {
- GURL::Replacements replacement;
- std::string new_value(path);
- replacement.SetPathStr(new_value);
- form->origin = form->origin.ReplaceComponents(replacement);
-}
-
-// Changes just the signon_realm port of |form|.
-static void SetPasswordFormPort(PasswordForm* form, const char* port) {
- GURL::Replacements replacement;
- std::string new_value(port);
- replacement.SetPortStr(new_value);
- GURL signon_gurl = GURL(form->signon_realm);
- form->signon_realm = signon_gurl.ReplaceComponents(replacement).spec();
-}
-
-// Changes just the signon_ream auth realm of |form|.
-static void SetPasswordFormRealm(PasswordForm* form, const char* realm) {
- GURL::Replacements replacement;
- std::string new_value(realm);
- replacement.SetPathStr(new_value);
- GURL signon_gurl = GURL(form->signon_realm);
- form->signon_realm = signon_gurl.ReplaceComponents(replacement).spec();
-}
-
-TEST_F(PasswordStoreMacInternalsTest, TestKeychainExactSearch) {
- MacKeychainPasswordFormAdapter keychain_adapter(keychain_);
-
- PasswordFormData base_form_data[] = {
- {PasswordForm::SCHEME_HTML, "http://some.domain.com/",
- "http://some.domain.com/insecure.html", NULL, NULL, NULL, NULL,
- L"joe_user", NULL, true, 0},
- {PasswordForm::SCHEME_BASIC, "http://some.domain.com:4567/low_security",
- "http://some.domain.com:4567/insecure.html", NULL, NULL, NULL, NULL,
- L"basic_auth_user", NULL, true, 0},
- {PasswordForm::SCHEME_DIGEST, "https://some.domain.com/high_security",
- "https://some.domain.com", NULL, NULL, NULL, NULL, L"digest_auth_user",
- NULL, true, 0},
- };
-
- for (unsigned int i = 0; i < arraysize(base_form_data); ++i) {
- // Create a base form and make sure we find a match.
- std::unique_ptr<PasswordForm> base_form =
- CreatePasswordFormFromDataForTesting(base_form_data[i]);
- EXPECT_TRUE(keychain_adapter.HasPasswordsMergeableWithForm(*base_form));
- EXPECT_TRUE(keychain_adapter.HasPasswordExactlyMatchingForm(*base_form));
-
- // Make sure that the matching isn't looser than it should be by checking
- // that slightly altered forms don't match.
- std::vector<std::unique_ptr<PasswordForm>> modified_forms;
-
- modified_forms.push_back(base::MakeUnique<PasswordForm>(*base_form));
- modified_forms.back()->username_value = ASCIIToUTF16("wrong_user");
-
- modified_forms.push_back(base::MakeUnique<PasswordForm>(*base_form));
- SetPasswordFormPath(modified_forms.back().get(), "elsewhere.html");
-
- modified_forms.push_back(base::MakeUnique<PasswordForm>(*base_form));
- modified_forms.back()->scheme = PasswordForm::SCHEME_OTHER;
-
- modified_forms.push_back(base::MakeUnique<PasswordForm>(*base_form));
- SetPasswordFormPort(modified_forms.back().get(), "1234");
-
- modified_forms.push_back(base::MakeUnique<PasswordForm>(*base_form));
- modified_forms.back()->blacklisted_by_user = true;
-
- if (base_form->scheme == PasswordForm::SCHEME_BASIC ||
- base_form->scheme == PasswordForm::SCHEME_DIGEST) {
- modified_forms.push_back(base::MakeUnique<PasswordForm>(*base_form));
- SetPasswordFormRealm(modified_forms.back().get(), "incorrect");
- }
-
- for (unsigned int j = 0; j < modified_forms.size(); ++j) {
- bool match =
- keychain_adapter.HasPasswordExactlyMatchingForm(*modified_forms[j]);
- EXPECT_FALSE(match) << "In modified version " << j << " of base form "
- << i;
- }
- }
-}
-
-TEST_F(PasswordStoreMacInternalsTest, TestKeychainAdd) {
- struct TestDataAndExpectation {
- PasswordFormData data;
- bool should_succeed;
- };
- TestDataAndExpectation test_data[] = {
- // Test a variety of scheme/port/protocol/path variations.
- {{PasswordForm::SCHEME_HTML, "http://web.site.com/",
- "http://web.site.com/path/to/page.html", NULL, NULL, NULL, NULL,
- L"anonymous", L"knock-knock", false, 0},
- true},
- {{PasswordForm::SCHEME_HTML, "https://web.site.com/",
- "https://web.site.com/", NULL, NULL, NULL, NULL, L"admin", L"p4ssw0rd",
- false, 0},
- true},
- {{PasswordForm::SCHEME_BASIC, "http://a.site.com:2222/therealm",
- "http://a.site.com:2222/", NULL, NULL, NULL, NULL, L"username",
- L"password", false, 0},
- true},
- {{PasswordForm::SCHEME_DIGEST, "https://digest.site.com/differentrealm",
- "https://digest.site.com/secure.html", NULL, NULL, NULL, NULL,
- L"testname", L"testpass", false, 0},
- true},
- // Test that Android credentials can be stored. Also check the legacy form
- // when |origin| was still filled with the Android URI (and not left
- // empty).
- {{PasswordForm::SCHEME_HTML, "android://hash@com.example.alpha/", "",
- NULL, NULL, NULL, NULL, L"joe_user", L"password", false, 0},
- true},
- {{PasswordForm::SCHEME_HTML, "android://hash@com.example.beta/",
- "android://hash@com.example.beta/", NULL, NULL, NULL, NULL,
- L"jane_user", L"password2", false, 0},
- true},
- // Make sure that garbage forms are rejected.
- {{PasswordForm::SCHEME_HTML, "gobbledygook", "gobbledygook", NULL, NULL,
- NULL, NULL, L"anonymous", L"knock-knock", false, 0},
- false},
- // Test that failing to update a duplicate (forced using the magic failure
- // password; see MockAppleKeychain::ItemModifyAttributesAndData) is
- // reported.
- {{PasswordForm::SCHEME_HTML, "http://some.domain.com",
- "http://some.domain.com/insecure.html", NULL, NULL, NULL, NULL,
- L"joe_user", L"fail_me", false, 0},
- false},
- };
-
- MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_);
- owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
-
- for (unsigned int i = 0; i < arraysize(test_data); ++i) {
- std::unique_ptr<PasswordForm> in_form =
- CreatePasswordFormFromDataForTesting(test_data[i].data);
- bool add_succeeded = owned_keychain_adapter.AddPassword(*in_form);
- EXPECT_EQ(test_data[i].should_succeed, add_succeeded);
- if (add_succeeded) {
- EXPECT_TRUE(
- owned_keychain_adapter.HasPasswordsMergeableWithForm(*in_form));
- EXPECT_TRUE(
- owned_keychain_adapter.HasPasswordExactlyMatchingForm(*in_form));
- }
- }
-
- // Test that adding duplicate item updates the existing item.
- // TODO(engedy): Add a test to verify that updating Android credentials work.
- // See: https://crbug.com/476851.
- {
- PasswordFormData data = {PasswordForm::SCHEME_HTML,
- "http://some.domain.com",
- "http://some.domain.com/insecure.html",
- NULL,
- NULL,
- NULL,
- NULL,
- L"joe_user",
- L"updated_password",
- false,
- 0};
- std::unique_ptr<PasswordForm> update_form =
- CreatePasswordFormFromDataForTesting(data);
- MacKeychainPasswordFormAdapter keychain_adapter(keychain_);
- EXPECT_TRUE(keychain_adapter.AddPassword(*update_form));
- SecKeychainItemRef keychain_item = reinterpret_cast<SecKeychainItemRef>(2);
- PasswordForm stored_form;
- internal_keychain_helpers::FillPasswordFormFromKeychainItem(
- *keychain_, keychain_item, &stored_form, true);
- EXPECT_EQ(update_form->password_value, stored_form.password_value);
- }
-}
-
-TEST_F(PasswordStoreMacInternalsTest, TestKeychainRemove) {
- struct TestDataAndExpectation {
- PasswordFormData data;
- bool should_succeed;
- };
- TestDataAndExpectation test_data[] = {
- // Test deletion of an item that we add.
- {{PasswordForm::SCHEME_HTML, "http://web.site.com/",
- "http://web.site.com/path/to/page.html", NULL, NULL, NULL, NULL,
- L"anonymous", L"knock-knock", false, 0},
- true},
- // Test that Android credentials can be removed. Also check the legacy
- // case when |origin| was still filled with the Android URI (and not left
- // empty).
- {{PasswordForm::SCHEME_HTML, "android://hash@com.example.alpha/", "",
- NULL, NULL, NULL, NULL, L"joe_user", L"secret", false, 0},
- true},
- {{PasswordForm::SCHEME_HTML, "android://hash@com.example.beta/",
- "android://hash@com.example.beta/", NULL, NULL, NULL, NULL,
- L"jane_user", L"secret", false, 0},
- true},
- // Make sure we don't delete items we don't own.
- {{PasswordForm::SCHEME_HTML, "http://some.domain.com/",
- "http://some.domain.com/insecure.html", NULL, NULL, NULL, NULL,
- L"joe_user", NULL, true, 0},
- false},
- };
-
- MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_);
- owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
-
- // Add our test items (except the last one) so that we can delete them.
- for (unsigned int i = 0; i + 1 < arraysize(test_data); ++i) {
- std::unique_ptr<PasswordForm> add_form =
- CreatePasswordFormFromDataForTesting(test_data[i].data);
- EXPECT_TRUE(owned_keychain_adapter.AddPassword(*add_form));
- }
-
- for (unsigned int i = 0; i < arraysize(test_data); ++i) {
- std::unique_ptr<PasswordForm> form =
- CreatePasswordFormFromDataForTesting(test_data[i].data);
- EXPECT_EQ(test_data[i].should_succeed,
- owned_keychain_adapter.RemovePassword(*form));
-
- MacKeychainPasswordFormAdapter keychain_adapter(keychain_);
- bool match = keychain_adapter.HasPasswordExactlyMatchingForm(*form);
- EXPECT_EQ(test_data[i].should_succeed, !match);
- }
-}
-
-TEST_F(PasswordStoreMacInternalsTest, TestFormMatch) {
- PasswordForm base_form;
- base_form.signon_realm = std::string("http://some.domain.com/");
- base_form.origin = GURL("http://some.domain.com/page.html");
- base_form.username_value = ASCIIToUTF16("joe_user");
-
- {
- // Check that everything unimportant can be changed.
- PasswordForm different_form(base_form);
- different_form.username_element = ASCIIToUTF16("username");
- different_form.submit_element = ASCIIToUTF16("submit");
- different_form.username_element = ASCIIToUTF16("password");
- different_form.password_value = ASCIIToUTF16("sekrit");
- different_form.action = GURL("http://some.domain.com/action.cgi");
- different_form.preferred = true;
- different_form.date_created = base::Time::Now();
- EXPECT_TRUE(
- FormsMatchForMerge(base_form, different_form, STRICT_FORM_MATCH));
-
- // Check that path differences don't prevent a match.
- base_form.origin = GURL("http://some.domain.com/other_page.html");
- EXPECT_TRUE(
- FormsMatchForMerge(base_form, different_form, STRICT_FORM_MATCH));
- }
-
- // Check that any one primary key changing is enough to prevent matching.
- {
- PasswordForm different_form(base_form);
- different_form.scheme = PasswordForm::SCHEME_DIGEST;
- EXPECT_FALSE(
- FormsMatchForMerge(base_form, different_form, STRICT_FORM_MATCH));
- }
- {
- PasswordForm different_form(base_form);
- different_form.signon_realm = std::string("http://some.domain.com:8080/");
- EXPECT_FALSE(
- FormsMatchForMerge(base_form, different_form, STRICT_FORM_MATCH));
- }
- {
- PasswordForm different_form(base_form);
- different_form.username_value = ASCIIToUTF16("john.doe");
- EXPECT_FALSE(
- FormsMatchForMerge(base_form, different_form, STRICT_FORM_MATCH));
- }
- {
- PasswordForm different_form(base_form);
- different_form.blacklisted_by_user = true;
- EXPECT_FALSE(
- FormsMatchForMerge(base_form, different_form, STRICT_FORM_MATCH));
- }
-
- // Blacklist forms should *never* match for merging, even when identical
- // (and certainly not when only one is a blacklist entry).
- {
- PasswordForm form_a(base_form);
- form_a.blacklisted_by_user = true;
- PasswordForm form_b(form_a);
- EXPECT_FALSE(FormsMatchForMerge(form_a, form_b, STRICT_FORM_MATCH));
- }
-
- // Federated login forms should never match for merging either.
- {
- PasswordForm form_b(base_form);
- form_b.federation_origin =
- url::Origin(GURL(password_manager::kTestingFederationUrlSpec));
- EXPECT_FALSE(FormsMatchForMerge(base_form, form_b, STRICT_FORM_MATCH));
- EXPECT_FALSE(FormsMatchForMerge(form_b, base_form, STRICT_FORM_MATCH));
- EXPECT_FALSE(FormsMatchForMerge(form_b, form_b, STRICT_FORM_MATCH));
- }
-}
-
-TEST_F(PasswordStoreMacInternalsTest, TestFormMerge) {
- // Set up a bunch of test data to use in varying combinations.
- PasswordFormData keychain_user_1 = {PasswordForm::SCHEME_HTML,
- "http://some.domain.com/",
- "http://some.domain.com/",
- "",
- L"",
- L"",
- L"",
- L"joe_user",
- L"sekrit",
- false,
- 1010101010};
- PasswordFormData keychain_user_1_with_path = {
- PasswordForm::SCHEME_HTML,
- "http://some.domain.com/",
- "http://some.domain.com/page.html",
- "",
- L"",
- L"",
- L"",
- L"joe_user",
- L"otherpassword",
- false,
- 1010101010};
- PasswordFormData keychain_user_2 = {PasswordForm::SCHEME_HTML,
- "http://some.domain.com/",
- "http://some.domain.com/",
- "",
- L"",
- L"",
- L"",
- L"john.doe",
- L"sesame",
- false,
- 958739876};
- PasswordFormData keychain_blacklist = {PasswordForm::SCHEME_HTML,
- "http://some.domain.com/",
- "http://some.domain.com/",
- "",
- L"",
- L"",
- L"",
- NULL,
- NULL,
- false,
- 1010101010};
- PasswordFormData keychain_android = {PasswordForm::SCHEME_HTML,
- "android://hash@com.domain.some/",
- "",
- "",
- L"",
- L"",
- L"",
- L"joe_user",
- L"secret",
- false,
- 1234567890};
-
- PasswordFormData db_user_1 = {PasswordForm::SCHEME_HTML,
- "http://some.domain.com/",
- "http://some.domain.com/",
- "http://some.domain.com/action.cgi",
- L"submit",
- L"username",
- L"password",
- L"joe_user",
- L"",
- true,
- 1212121212};
- PasswordFormData db_user_1_with_path = {
- PasswordForm::SCHEME_HTML,
- "http://some.domain.com/",
- "http://some.domain.com/page.html",
- "http://some.domain.com/handlepage.cgi",
- L"submit",
- L"username",
- L"password",
- L"joe_user",
- L"",
- true,
- 1234567890};
- PasswordFormData db_user_3_with_path = {
- PasswordForm::SCHEME_HTML,
- "http://some.domain.com/",
- "http://some.domain.com/page.html",
- "http://some.domain.com/handlepage.cgi",
- L"submit",
- L"username",
- L"password",
- L"second-account",
- L"",
- true,
- 1240000000};
- PasswordFormData database_blacklist_with_path = {
- PasswordForm::SCHEME_HTML,
- "http://some.domain.com/",
- "http://some.domain.com/path.html",
- "http://some.domain.com/action.cgi",
- L"submit",
- L"username",
- L"password",
- NULL,
- NULL,
- true,
- 1212121212};
- PasswordFormData db_android = {PasswordForm::SCHEME_HTML,
- "android://hash@com.domain.some/",
- "android://hash@com.domain.some/",
- "",
- L"",
- L"",
- L"",
- L"joe_user",
- L"",
- false,
- 1234567890};
- PasswordFormData db_federated = {
- PasswordForm::SCHEME_HTML,
- "android://hash@com.domain.some/",
- "android://hash@com.domain.some/",
- "",
- L"",
- L"",
- L"",
- L"joe_user",
- password_manager::kTestingFederatedLoginMarker,
- false,
- 3434343434};
-
- PasswordFormData merged_user_1 = {PasswordForm::SCHEME_HTML,
- "http://some.domain.com/",
- "http://some.domain.com/",
- "http://some.domain.com/action.cgi",
- L"submit",
- L"username",
- L"password",
- L"joe_user",
- L"sekrit",
- true,
- 1212121212};
- PasswordFormData merged_user_1_with_db_path = {
- PasswordForm::SCHEME_HTML,
- "http://some.domain.com/",
- "http://some.domain.com/page.html",
- "http://some.domain.com/handlepage.cgi",
- L"submit",
- L"username",
- L"password",
- L"joe_user",
- L"sekrit",
- true,
- 1234567890};
- PasswordFormData merged_user_1_with_both_paths = {
- PasswordForm::SCHEME_HTML,
- "http://some.domain.com/",
- "http://some.domain.com/page.html",
- "http://some.domain.com/handlepage.cgi",
- L"submit",
- L"username",
- L"password",
- L"joe_user",
- L"otherpassword",
- true,
- 1234567890};
- PasswordFormData merged_android = {PasswordForm::SCHEME_HTML,
- "android://hash@com.domain.some/",
- "android://hash@com.domain.some/",
- "",
- L"",
- L"",
- L"",
- L"joe_user",
- L"secret",
- false,
- 1234567890};
-
- // Build up the big multi-dimensional array of data sets that will actually
- // drive the test. Use vectors rather than arrays so that initialization is
- // simple.
- enum {
- KEYCHAIN_INPUT = 0,
- DATABASE_INPUT,
- MERGE_OUTPUT,
- KEYCHAIN_OUTPUT,
- DATABASE_OUTPUT,
- MERGE_IO_ARRAY_COUNT // termination marker
- };
- const unsigned int kTestCount = 5;
- std::vector<std::vector<std::vector<PasswordFormData*>>> test_data(
- MERGE_IO_ARRAY_COUNT, std::vector<std::vector<PasswordFormData*>>(
- kTestCount, std::vector<PasswordFormData*>()));
- unsigned int current_test = 0;
-
- // Test a merge with a few accounts in both systems, with partial overlap.
- CHECK(current_test < kTestCount);
- test_data[KEYCHAIN_INPUT][current_test].push_back(&keychain_user_1);
- test_data[KEYCHAIN_INPUT][current_test].push_back(&keychain_user_2);
- test_data[DATABASE_INPUT][current_test].push_back(&db_user_1);
- test_data[DATABASE_INPUT][current_test].push_back(&db_user_1_with_path);
- test_data[DATABASE_INPUT][current_test].push_back(&db_user_3_with_path);
- test_data[MERGE_OUTPUT][current_test].push_back(&merged_user_1);
- test_data[MERGE_OUTPUT][current_test].push_back(&merged_user_1_with_db_path);
- test_data[KEYCHAIN_OUTPUT][current_test].push_back(&keychain_user_2);
- test_data[DATABASE_OUTPUT][current_test].push_back(&db_user_3_with_path);
-
- // Test a merge where Chrome has a blacklist entry, and the keychain has
- // a stored account.
- ++current_test;
- CHECK(current_test < kTestCount);
- test_data[KEYCHAIN_INPUT][current_test].push_back(&keychain_user_1);
- test_data[DATABASE_INPUT][current_test].push_back(
- &database_blacklist_with_path);
- // We expect both to be present because a blacklist could be specific to a
- // subpath, and we want access to the password on other paths.
- test_data[MERGE_OUTPUT][current_test].push_back(
- &database_blacklist_with_path);
- test_data[KEYCHAIN_OUTPUT][current_test].push_back(&keychain_user_1);
-
- // Test a merge where Chrome has an account, and Keychain has a blacklist
- // (from another browser) and the Chrome password data.
- ++current_test;
- CHECK(current_test < kTestCount);
- test_data[KEYCHAIN_INPUT][current_test].push_back(&keychain_blacklist);
- test_data[KEYCHAIN_INPUT][current_test].push_back(&keychain_user_1);
- test_data[DATABASE_INPUT][current_test].push_back(&db_user_1);
- test_data[MERGE_OUTPUT][current_test].push_back(&merged_user_1);
- test_data[KEYCHAIN_OUTPUT][current_test].push_back(&keychain_blacklist);
-
- // Test that matches are done using exact path when possible.
- ++current_test;
- CHECK(current_test < kTestCount);
- test_data[KEYCHAIN_INPUT][current_test].push_back(&keychain_user_1);
- test_data[KEYCHAIN_INPUT][current_test].push_back(&keychain_user_1_with_path);
- test_data[DATABASE_INPUT][current_test].push_back(&db_user_1);
- test_data[DATABASE_INPUT][current_test].push_back(&db_user_1_with_path);
- test_data[MERGE_OUTPUT][current_test].push_back(&merged_user_1);
- test_data[MERGE_OUTPUT][current_test].push_back(
- &merged_user_1_with_both_paths);
-
- // Test that Android credentails are matched correctly and that federated
- // credentials are not tried to be matched with a Keychain item.
- ++current_test;
- CHECK(current_test < kTestCount);
- test_data[KEYCHAIN_INPUT][current_test].push_back(&keychain_android);
- test_data[DATABASE_INPUT][current_test].push_back(&db_federated);
- test_data[DATABASE_INPUT][current_test].push_back(&db_android);
- test_data[MERGE_OUTPUT][current_test].push_back(&db_federated);
- test_data[MERGE_OUTPUT][current_test].push_back(&merged_android);
-
- for (unsigned int test_case = 0; test_case <= current_test; ++test_case) {
- std::vector<std::unique_ptr<PasswordForm>> keychain_forms;
- for (std::vector<PasswordFormData*>::iterator i =
- test_data[KEYCHAIN_INPUT][test_case].begin();
- i != test_data[KEYCHAIN_INPUT][test_case].end(); ++i) {
- keychain_forms.push_back(CreatePasswordFormFromDataForTesting(*(*i)));
- }
- std::vector<std::unique_ptr<PasswordForm>> database_forms;
- for (std::vector<PasswordFormData*>::iterator i =
- test_data[DATABASE_INPUT][test_case].begin();
- i != test_data[DATABASE_INPUT][test_case].end(); ++i) {
- database_forms.push_back(CreatePasswordFormFromDataForTesting(*(*i)));
- }
-
- std::vector<std::unique_ptr<PasswordForm>> merged_forms;
- internal_keychain_helpers::MergePasswordForms(
- &keychain_forms, &database_forms, &merged_forms);
-
- CHECK_FORMS(keychain_forms, test_data[KEYCHAIN_OUTPUT][test_case],
- test_case);
- CHECK_FORMS(database_forms, test_data[DATABASE_OUTPUT][test_case],
- test_case);
- CHECK_FORMS(merged_forms, test_data[MERGE_OUTPUT][test_case], test_case);
- }
-}
-
-TEST_F(PasswordStoreMacInternalsTest, TestPasswordBulkLookup) {
- PasswordFormData db_data[] = {
- {PasswordForm::SCHEME_HTML, "http://some.domain.com/",
- "http://some.domain.com/", "http://some.domain.com/action.cgi",
- L"submit", L"username", L"password", L"joe_user", L"", true, 1212121212},
- {PasswordForm::SCHEME_HTML, "http://some.domain.com/",
- "http://some.domain.com/page.html",
- "http://some.domain.com/handlepage.cgi", L"submit", L"username",
- L"password", L"joe_user", L"", true, 1234567890},
- {PasswordForm::SCHEME_HTML, "http://some.domain.com/",
- "http://some.domain.com/page.html",
- "http://some.domain.com/handlepage.cgi", L"submit", L"username",
- L"password", L"second-account", L"", true, 1240000000},
- {PasswordForm::SCHEME_HTML, "http://dont.remember.com/",
- "http://dont.remember.com/", "http://dont.remember.com/handlepage.cgi",
- L"submit", L"username", L"password", L"joe_user", L"", true, 1240000000},
- {PasswordForm::SCHEME_HTML, "http://some.domain.com/",
- "http://some.domain.com/path.html", "http://some.domain.com/action.cgi",
- L"submit", L"username", L"password", NULL, NULL, true, 1212121212},
- };
- std::vector<std::unique_ptr<PasswordForm>> database_forms;
- for (const PasswordFormData& form_data : db_data) {
- database_forms.push_back(CreatePasswordFormFromDataForTesting(form_data));
- }
- std::vector<std::unique_ptr<PasswordForm>> merged_forms;
- internal_keychain_helpers::GetPasswordsForForms(*keychain_, &database_forms,
- &merged_forms);
- EXPECT_EQ(2U, database_forms.size());
- ASSERT_EQ(3U, merged_forms.size());
- EXPECT_EQ(ASCIIToUTF16("sekrit"), merged_forms[0]->password_value);
- EXPECT_EQ(ASCIIToUTF16("sekrit"), merged_forms[1]->password_value);
- EXPECT_TRUE(merged_forms[2]->blacklisted_by_user);
-}
-
-TEST_F(PasswordStoreMacInternalsTest, TestBlacklistedFiltering) {
- PasswordFormData db_data[] = {
- {PasswordForm::SCHEME_HTML, "http://dont.remember.com/",
- "http://dont.remember.com/", "http://dont.remember.com/handlepage.cgi",
- L"submit", L"username", L"password", L"joe_user", L"non_empty_password",
- true, 1240000000},
- {PasswordForm::SCHEME_HTML, "https://dont.remember.com/",
- "https://dont.remember.com/",
- "https://dont.remember.com/handlepage_secure.cgi", L"submit",
- L"username", L"password", L"joe_user", L"non_empty_password", true,
- 1240000000},
- };
- std::vector<std::unique_ptr<PasswordForm>> database_forms;
- for (const PasswordFormData& form_data : db_data) {
- database_forms.push_back(CreatePasswordFormFromDataForTesting(form_data));
- }
- std::vector<std::unique_ptr<PasswordForm>> merged_forms;
- internal_keychain_helpers::GetPasswordsForForms(*keychain_, &database_forms,
- &merged_forms);
- EXPECT_EQ(2U, database_forms.size());
- ASSERT_EQ(0U, merged_forms.size());
-}
-
-TEST_F(PasswordStoreMacInternalsTest, TestFillPasswordFormFromKeychainItem) {
- // When |extract_password_data| is false, the password field must be empty,
- // and |blacklisted_by_user| must be false.
- SecKeychainItemRef keychain_item = reinterpret_cast<SecKeychainItemRef>(1);
- PasswordForm form_without_extracted_password;
- bool parsed = internal_keychain_helpers::FillPasswordFormFromKeychainItem(
- *keychain_, keychain_item, &form_without_extracted_password,
- false); // Do not extract password.
- EXPECT_TRUE(parsed);
- ASSERT_TRUE(form_without_extracted_password.password_value.empty());
- ASSERT_FALSE(form_without_extracted_password.blacklisted_by_user);
-
- // When |extract_password_data| is true and the keychain entry has a non-empty
- // password, the password field must be non-empty, and the value of
- // |blacklisted_by_user| must be false.
- keychain_item = reinterpret_cast<SecKeychainItemRef>(1);
- PasswordForm form_with_extracted_password;
- parsed = internal_keychain_helpers::FillPasswordFormFromKeychainItem(
- *keychain_, keychain_item, &form_with_extracted_password,
- true); // Extract password.
- EXPECT_TRUE(parsed);
- ASSERT_EQ(ASCIIToUTF16("sekrit"),
- form_with_extracted_password.password_value);
- ASSERT_FALSE(form_with_extracted_password.blacklisted_by_user);
-
- // When |extract_password_data| is true and the keychain entry has an empty
- // username and password (""), the password field must be empty, and the value
- // of |blacklisted_by_user| must be true.
- keychain_item = reinterpret_cast<SecKeychainItemRef>(4);
- PasswordForm negative_form;
- parsed = internal_keychain_helpers::FillPasswordFormFromKeychainItem(
- *keychain_, keychain_item, &negative_form,
- true); // Extract password.
- EXPECT_TRUE(parsed);
- ASSERT_TRUE(negative_form.username_value.empty());
- ASSERT_TRUE(negative_form.password_value.empty());
- ASSERT_TRUE(negative_form.blacklisted_by_user);
-
- // When |extract_password_data| is true and the keychain entry has an empty
- // password (""), the password field must be empty (""), and the value of
- // |blacklisted_by_user| must be true.
- keychain_item = reinterpret_cast<SecKeychainItemRef>(5);
- PasswordForm form_with_empty_password_a;
- parsed = internal_keychain_helpers::FillPasswordFormFromKeychainItem(
- *keychain_, keychain_item, &form_with_empty_password_a,
- true); // Extract password.
- EXPECT_TRUE(parsed);
- ASSERT_TRUE(form_with_empty_password_a.password_value.empty());
- ASSERT_TRUE(form_with_empty_password_a.blacklisted_by_user);
-
- // When |extract_password_data| is true and the keychain entry has a single
- // space password (" "), the password field must be a single space (" "), and
- // the value of |blacklisted_by_user| must be true.
- keychain_item = reinterpret_cast<SecKeychainItemRef>(6);
- PasswordForm form_with_empty_password_b;
- parsed = internal_keychain_helpers::FillPasswordFormFromKeychainItem(
- *keychain_, keychain_item, &form_with_empty_password_b,
- true); // Extract password.
- EXPECT_TRUE(parsed);
- ASSERT_EQ(ASCIIToUTF16(" "), form_with_empty_password_b.password_value);
- ASSERT_TRUE(form_with_empty_password_b.blacklisted_by_user);
-}
-
-TEST_F(PasswordStoreMacInternalsTest, TestPasswordGetAll) {
- MacKeychainPasswordFormAdapter keychain_adapter(keychain_);
- MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_);
- owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
-
- // Add a few passwords of various types so that we own some.
- PasswordFormData owned_password_data[] = {
- {PasswordForm::SCHEME_HTML, "http://web.site.com/",
- "http://web.site.com/path/to/page.html", NULL, NULL, NULL, NULL,
- L"anonymous", L"knock-knock", false, 0},
- {PasswordForm::SCHEME_BASIC, "http://a.site.com:2222/therealm",
- "http://a.site.com:2222/", NULL, NULL, NULL, NULL, L"username",
- L"password", false, 0},
- {PasswordForm::SCHEME_DIGEST, "https://digest.site.com/differentrealm",
- "https://digest.site.com/secure.html", NULL, NULL, NULL, NULL,
- L"testname", L"testpass", false, 0},
- };
- for (unsigned int i = 0; i < arraysize(owned_password_data); ++i) {
- std::unique_ptr<PasswordForm> form =
- CreatePasswordFormFromDataForTesting(owned_password_data[i]);
- owned_keychain_adapter.AddPassword(*form);
- }
-
- std::vector<std::unique_ptr<PasswordForm>> all_passwords =
- keychain_adapter.GetAllPasswordFormPasswords();
- EXPECT_EQ(9 + arraysize(owned_password_data), all_passwords.size());
-
- std::vector<std::unique_ptr<PasswordForm>> owned_passwords =
- owned_keychain_adapter.GetAllPasswordFormPasswords();
- EXPECT_EQ(arraysize(owned_password_data), owned_passwords.size());
-}
-
-#pragma mark -
-
-class PasswordStoreMacTest : public testing::Test {
- public:
- PasswordStoreMacTest() = default;
-
- void SetUp() override {
- ASSERT_TRUE(db_dir_.CreateUniqueTempDir());
- histogram_tester_.reset(new base::HistogramTester);
-
- // Ensure that LoginDatabase will use the mock keychain if it needs to
- // encrypt/decrypt a password.
- OSCryptMocker::SetUpWithSingleton();
- login_db_.reset(
- new password_manager::LoginDatabase(test_login_db_file_path()));
- thread_.reset(new base::Thread("Chrome_PasswordStore_Thread"));
- ASSERT_TRUE(thread_->Start());
- ASSERT_TRUE(thread_->task_runner()->PostTask(
- FROM_HERE, base::Bind(&PasswordStoreMacTest::InitLoginDatabase,
- base::Unretained(login_db_.get()))));
- CreateAndInitPasswordStore(login_db_.get());
- // Make sure deferred initialization is performed before some tests start
- // accessing the |login_db| directly.
- FinishAsyncProcessing();
- }
-
- void TearDown() override {
- ClosePasswordStore();
- thread_.reset();
- login_db_.reset();
- // Whatever a test did, PasswordStoreMac stores only empty password values
- // in LoginDatabase. The empty valus do not require encryption and therefore
- // OSCrypt shouldn't call the Keychain. The histogram doesn't cover the
- // internet passwords.
- if (histogram_tester_) {
- histogram_tester_->ExpectTotalCount("OSX.Keychain.Access", 0);
- }
- OSCryptMocker::TearDown();
- }
-
- static void InitLoginDatabase(password_manager::LoginDatabase* login_db) {
- ASSERT_TRUE(login_db->Init());
- }
-
- void CreateAndInitPasswordStore(password_manager::LoginDatabase* login_db) {
- store_ = new PasswordStoreMac(
- base::ThreadTaskRunnerHandle::Get(), nullptr,
- base::WrapUnique<AppleKeychain>(new MockAppleKeychain));
- ASSERT_TRUE(thread_->task_runner()->PostTask(
- FROM_HERE, base::Bind(&PasswordStoreMac::InitWithTaskRunner, store_,
- thread_->task_runner())));
-
- ASSERT_TRUE(thread_->task_runner()->PostTask(
- FROM_HERE, base::Bind(&PasswordStoreMac::set_login_metadata_db, store_,
- base::Unretained(login_db))));
- }
-
- void ClosePasswordStore() {
- if (!store_)
- return;
-
- store_->ShutdownOnUIThread();
- store_ = nullptr;
- }
-
- // Verifies that the given |form| can be properly stored so that it can be
- // retrieved by FillMatchingLogins() and GetAutofillableLogins(), and then it
- // can be properly removed.
- void VerifyCredentialLifecycle(const PasswordForm& form) {
- // Run everything twice to make sure no garbage is left behind that would
- // prevent storing the form a second time.
- for (size_t iteration = 0; iteration < 2; ++iteration) {
- SCOPED_TRACE(testing::Message("Iteration: ") << iteration);
-
- MockPasswordStoreConsumer mock_consumer;
- EXPECT_CALL(mock_consumer, OnGetPasswordStoreResultsConstRef(IsEmpty()))
- .WillOnce(QuitUIMessageLoop());
- store()->GetAutofillableLogins(&mock_consumer);
- base::RunLoop().Run();
- ::testing::Mock::VerifyAndClearExpectations(&mock_consumer);
-
- store()->AddLogin(form);
- FinishAsyncProcessing();
-
- PasswordForm returned_form;
- EXPECT_CALL(mock_consumer, OnGetPasswordStoreResultsConstRef(SizeIs(1u)))
- .WillOnce(
- DoAll(SaveACopyOfFirstForm(&returned_form), QuitUIMessageLoop()));
-
- // The query operations will also do some housekeeping: they will remove
- // dangling credentials in the LoginDatabase without a matching Keychain
- // item when one is expected. If the logic that stores the Keychain item
- // is incorrect, this will wipe the newly added form before the second
- // query.
- store()->GetAutofillableLogins(&mock_consumer);
- base::RunLoop().Run();
- ::testing::Mock::VerifyAndClearExpectations(&mock_consumer);
- EXPECT_EQ(form, returned_form);
-
- PasswordStore::FormDigest query_form(form);
- EXPECT_CALL(mock_consumer, OnGetPasswordStoreResultsConstRef(SizeIs(1u)))
- .WillOnce(
- DoAll(SaveACopyOfFirstForm(&returned_form), QuitUIMessageLoop()));
- store()->GetLogins(query_form, &mock_consumer);
- base::RunLoop().Run();
- ::testing::Mock::VerifyAndClearExpectations(&mock_consumer);
- EXPECT_EQ(form, returned_form);
-
- store()->RemoveLogin(form);
- }
- }
-
- base::FilePath test_login_db_file_path() const {
- return db_dir_.GetPath().Append(FILE_PATH_LITERAL("login.db"));
- }
-
- password_manager::LoginDatabase* login_db() const {
- return store_->login_metadata_db();
- }
-
- MockAppleKeychain* keychain() {
- return static_cast<MockAppleKeychain*>(store_->keychain());
- }
-
- void FinishAsyncProcessing() {
- scoped_refptr<content::MessageLoopRunner> runner =
- new content::MessageLoopRunner;
- ASSERT_TRUE(thread_->task_runner()->PostTaskAndReply(
- FROM_HERE, base::Bind(&Noop), runner->QuitClosure()));
- runner->Run();
- }
-
- PasswordStoreMac* store() { return store_.get(); }
-
- protected:
- content::TestBrowserThreadBundle test_browser_thread_bundle_;
- // Thread that the synchronous methods are run on.
- std::unique_ptr<base::Thread> thread_;
-
- base::ScopedTempDir db_dir_;
- std::unique_ptr<password_manager::LoginDatabase> login_db_;
- scoped_refptr<PasswordStoreMac> store_;
- std::unique_ptr<base::HistogramTester> histogram_tester_;
-};
-
-TEST_F(PasswordStoreMacTest, TestStoreUpdate) {
- // Insert a password into both the database and the keychain.
- // This is done manually, rather than through store_->AddLogin, because the
- // Mock Keychain isn't smart enough to be able to support update generically,
- // so some.domain.com triggers special handling to test it that make inserting
- // fail.
- PasswordFormData joint_data = {PasswordForm::SCHEME_HTML,
- "http://some.domain.com/",
- "http://some.domain.com/insecure.html",
- "login.cgi",
- L"username",
- L"password",
- L"submit",
- L"joe_user",
- L"sekrit",
- true,
- 1};
- std::unique_ptr<PasswordForm> joint_form =
- CreatePasswordFormFromDataForTesting(joint_data);
- EXPECT_EQ(AddChangeForForm(*joint_form), login_db()->AddLogin(*joint_form));
- MockAppleKeychain::KeychainTestData joint_keychain_data = {
- kSecAuthenticationTypeHTMLForm,
- "some.domain.com",
- kSecProtocolTypeHTTP,
- "/insecure.html",
- 0,
- NULL,
- "20020601171500Z",
- "joe_user",
- "sekrit",
- false};
- keychain()->AddTestItem(joint_keychain_data);
-
- // Insert a password into the keychain only.
- MockAppleKeychain::KeychainTestData keychain_only_data = {
- kSecAuthenticationTypeHTMLForm,
- "keychain.only.com",
- kSecProtocolTypeHTTP,
- NULL,
- 0,
- NULL,
- "20020601171500Z",
- "keychain",
- "only",
- false};
- keychain()->AddTestItem(keychain_only_data);
-
- struct UpdateData {
- PasswordFormData form_data;
- const char* password; // NULL indicates no entry should be present.
- };
-
- // Make a series of update calls.
- UpdateData updates[] = {
- // Update the keychain+db passwords (the normal password update case).
- {
- {PasswordForm::SCHEME_HTML, "http://some.domain.com/",
- "http://some.domain.com/insecure.html", "login.cgi", L"username",
- L"password", L"submit", L"joe_user", L"53krit", true, 2},
- "53krit",
- },
- // Update the keychain-only password; this simulates the initial use of a
- // password stored by another browsers.
- {
- {PasswordForm::SCHEME_HTML, "http://keychain.only.com/",
- "http://keychain.only.com/login.html", "login.cgi", L"username",
- L"password", L"submit", L"keychain", L"only", true, 2},
- "only",
- },
- // Update a password that doesn't exist in either location. This tests the
- // case where a form is filled, then the stored login is removed, then the
- // form is submitted.
- {
- {PasswordForm::SCHEME_HTML, "http://different.com/",
- "http://different.com/index.html", "login.cgi", L"username",
- L"password", L"submit", L"abc", L"123", true, 2},
- NULL,
- },
- };
- for (unsigned int i = 0; i < arraysize(updates); ++i) {
- std::unique_ptr<PasswordForm> form =
- CreatePasswordFormFromDataForTesting(updates[i].form_data);
- store_->UpdateLogin(*form);
- }
-
- FinishAsyncProcessing();
-
- MacKeychainPasswordFormAdapter keychain_adapter(keychain());
- for (unsigned int i = 0; i < arraysize(updates); ++i) {
- SCOPED_TRACE(testing::Message("iteration ") << i);
- std::unique_ptr<PasswordForm> query_form =
- CreatePasswordFormFromDataForTesting(updates[i].form_data);
-
- std::vector<std::unique_ptr<PasswordForm>> matching_items =
- keychain_adapter.PasswordsFillingForm(query_form->signon_realm,
- query_form->scheme);
- if (updates[i].password) {
- EXPECT_GT(matching_items.size(), 0U);
- if (matching_items.size() >= 1)
- EXPECT_EQ(ASCIIToUTF16(updates[i].password),
- matching_items[0]->password_value);
- } else {
- EXPECT_EQ(0U, matching_items.size());
- }
-
- std::vector<std::unique_ptr<PasswordForm>> matching_db_items;
- EXPECT_TRUE(login_db()->GetLogins(PasswordStore::FormDigest(*query_form),
- &matching_db_items));
- EXPECT_EQ(updates[i].password ? 1U : 0U, matching_db_items.size());
- }
-}
-
-TEST_F(PasswordStoreMacTest, TestDBKeychainAssociation) {
- // Tests that association between the keychain and login database parts of a
- // password added by fuzzy (PSL) matching works.
- // 1. Add a password for www.facebook.com
- // 2. Get a password for m.facebook.com. This fuzzy matches and returns the
- // www.facebook.com password.
- // 3. Add the returned password for m.facebook.com.
- // 4. Remove both passwords.
- // -> check: that both are gone from the login DB and the keychain
- // This test should in particular ensure that we don't keep passwords in the
- // keychain just before we think we still have other (fuzzy-)matching entries
- // for them in the login database. (For example, here if we deleted the
- // www.facebook.com password from the login database, we should not be blocked
- // from deleting it from the keystore just becaus the m.facebook.com password
- // fuzzy-matches the www.facebook.com one.)
-
- // 1. Add a password for www.facebook.com
- PasswordFormData www_form_data = {PasswordForm::SCHEME_HTML,
- "http://www.facebook.com/",
- "http://www.facebook.com/index.html",
- "login",
- L"username",
- L"password",
- L"submit",
- L"joe_user",
- L"sekrit",
- true,
- 1};
- std::unique_ptr<PasswordForm> www_form =
- CreatePasswordFormFromDataForTesting(www_form_data);
- EXPECT_EQ(AddChangeForForm(*www_form), login_db()->AddLogin(*www_form));
- MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain());
- owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
- owned_keychain_adapter.AddPassword(*www_form);
-
- // 2. Get a password for m.facebook.com.
- PasswordForm m_form(*www_form);
- m_form.signon_realm = "http://m.facebook.com";
- m_form.origin = GURL("http://m.facebook.com/index.html");
-
- MockPasswordStoreConsumer consumer;
- store_->GetLogins(PasswordStore::FormDigest(m_form), &consumer);
- PasswordForm returned_form;
- EXPECT_CALL(consumer, OnGetPasswordStoreResultsConstRef(SizeIs(1u)))
- .WillOnce(
- DoAll(SaveACopyOfFirstForm(&returned_form), QuitUIMessageLoop()));
- base::RunLoop().Run();
-
- // 3. Add the returned password for m.facebook.com.
- returned_form.signon_realm = "http://m.facebook.com";
- returned_form.origin = GURL("http://m.facebook.com/index.html");
- EXPECT_EQ(AddChangeForForm(returned_form),
- login_db()->AddLogin(returned_form));
- owned_keychain_adapter.AddPassword(m_form);
-
- // 4. Remove both passwords.
- store_->RemoveLogin(*www_form);
- store_->RemoveLogin(m_form);
- FinishAsyncProcessing();
-
- // No trace of www.facebook.com.
- std::vector<std::unique_ptr<PasswordForm>> matching_items =
- owned_keychain_adapter.PasswordsFillingForm(www_form->signon_realm,
- www_form->scheme);
- EXPECT_EQ(0u, matching_items.size());
-
- std::vector<std::unique_ptr<PasswordForm>> matching_db_items;
- EXPECT_TRUE(login_db()->GetLogins(PasswordStore::FormDigest(*www_form),
- &matching_db_items));
- EXPECT_EQ(0u, matching_db_items.size());
-
- // No trace of m.facebook.com.
- matching_items = owned_keychain_adapter.PasswordsFillingForm(
- m_form.signon_realm, m_form.scheme);
- EXPECT_EQ(0u, matching_items.size());
-
- EXPECT_TRUE(login_db()->GetLogins(PasswordStore::FormDigest(m_form),
- &matching_db_items));
- EXPECT_EQ(0u, matching_db_items.size());
-}
-
-namespace {
-
-class PasswordsChangeObserver
- : public password_manager::PasswordStore::Observer {
- public:
- explicit PasswordsChangeObserver(PasswordStoreMac* store) : observer_(this) {
- observer_.Add(store);
- }
-
- void WaitAndVerify(PasswordStoreMacTest* test) {
- test->FinishAsyncProcessing();
- ::testing::Mock::VerifyAndClearExpectations(this);
- }
-
- // password_manager::PasswordStore::Observer:
- MOCK_METHOD1(OnLoginsChanged,
- void(const password_manager::PasswordStoreChangeList& changes));
-
- private:
- ScopedObserver<password_manager::PasswordStore, PasswordsChangeObserver>
- observer_;
-};
-
-password_manager::PasswordStoreChangeList GetAddChangeList(
- const PasswordForm& form) {
- password_manager::PasswordStoreChange change(
- password_manager::PasswordStoreChange::ADD, form);
- return password_manager::PasswordStoreChangeList(1, change);
-}
-
-// Tests RemoveLoginsCreatedBetween or RemoveLoginsSyncedBetween depending on
-// |check_created|.
-void CheckRemoveLoginsBetween(PasswordStoreMacTest* test, bool check_created) {
- PasswordFormData www_form_data_facebook = {
- PasswordForm::SCHEME_HTML,
- "http://www.facebook.com/",
- "http://www.facebook.com/index.html",
- "login",
- L"submit",
- L"username",
- L"password",
- L"joe_user",
- L"sekrit",
- true,
- 0};
- // The old form doesn't have elements names.
- PasswordFormData www_form_data_facebook_old = {
- PasswordForm::SCHEME_HTML,
- "http://www.facebook.com/",
- "http://www.facebook.com/index.html",
- "login",
- L"",
- L"",
- L"",
- L"joe_user",
- L"oldsekrit",
- true,
- 0};
- PasswordFormData www_form_data_other = {PasswordForm::SCHEME_HTML,
- "http://different.com/",
- "http://different.com/index.html",
- "login",
- L"submit",
- L"username",
- L"password",
- L"different_joe_user",
- L"sekrit",
- true,
- 0};
- std::unique_ptr<PasswordForm> form_facebook =
- CreatePasswordFormFromDataForTesting(www_form_data_facebook);
- std::unique_ptr<PasswordForm> form_facebook_old =
- CreatePasswordFormFromDataForTesting(www_form_data_facebook_old);
- std::unique_ptr<PasswordForm> form_other =
- CreatePasswordFormFromDataForTesting(www_form_data_other);
- base::Time now = base::Time::Now();
- base::Time next_day = now + base::TimeDelta::FromDays(1);
- if (check_created) {
- form_facebook_old->date_created = now;
- form_facebook->date_created = next_day;
- form_other->date_created = next_day;
- } else {
- form_facebook_old->date_synced = now;
- form_facebook->date_synced = next_day;
- form_other->date_synced = next_day;
- }
-
- PasswordsChangeObserver observer(test->store());
- test->store()->AddLogin(*form_facebook_old);
- test->store()->AddLogin(*form_facebook);
- test->store()->AddLogin(*form_other);
- EXPECT_CALL(observer, OnLoginsChanged(GetAddChangeList(*form_facebook_old)));
- EXPECT_CALL(observer, OnLoginsChanged(GetAddChangeList(*form_facebook)));
- EXPECT_CALL(observer, OnLoginsChanged(GetAddChangeList(*form_other)));
- observer.WaitAndVerify(test);
-
- // Check the keychain content.
- MacKeychainPasswordFormAdapter owned_keychain_adapter(test->keychain());
- owned_keychain_adapter.SetFindsOnlyOwnedItems(false);
- std::vector<std::unique_ptr<PasswordForm>> matching_items(
- owned_keychain_adapter.PasswordsFillingForm(form_facebook->signon_realm,
- form_facebook->scheme));
- EXPECT_EQ(1u, matching_items.size());
- matching_items = owned_keychain_adapter.PasswordsFillingForm(
- form_other->signon_realm, form_other->scheme);
- EXPECT_EQ(1u, matching_items.size());
-
- // Remove facebook.
- if (check_created) {
- test->store()->RemoveLoginsCreatedBetween(base::Time(), next_day,
- base::Closure());
- } else {
- test->store()->RemoveLoginsSyncedBetween(base::Time(), next_day);
- }
- password_manager::PasswordStoreChangeList list;
- form_facebook_old->password_value.clear();
- form_facebook->password_value.clear();
- list.push_back(password_manager::PasswordStoreChange(
- password_manager::PasswordStoreChange::REMOVE, *form_facebook_old));
- list.push_back(password_manager::PasswordStoreChange(
- password_manager::PasswordStoreChange::REMOVE, *form_facebook));
- EXPECT_CALL(observer, OnLoginsChanged(list));
- list.clear();
- observer.WaitAndVerify(test);
-
- matching_items = owned_keychain_adapter.PasswordsFillingForm(
- form_facebook->signon_realm, form_facebook->scheme);
- EXPECT_EQ(0u, matching_items.size());
- matching_items = owned_keychain_adapter.PasswordsFillingForm(
- form_other->signon_realm, form_other->scheme);
- EXPECT_EQ(1u, matching_items.size());
-
- // Remove form_other.
- if (check_created) {
- test->store()->RemoveLoginsCreatedBetween(next_day, base::Time(),
- base::Closure());
- } else {
- test->store()->RemoveLoginsSyncedBetween(next_day, base::Time());
- }
- form_other->password_value.clear();
- list.push_back(password_manager::PasswordStoreChange(
- password_manager::PasswordStoreChange::REMOVE, *form_other));
- EXPECT_CALL(observer, OnLoginsChanged(list));
- observer.WaitAndVerify(test);
- matching_items = owned_keychain_adapter.PasswordsFillingForm(
- form_other->signon_realm, form_other->scheme);
- EXPECT_EQ(0u, matching_items.size());
-}
-
-} // namespace
-
-TEST_F(PasswordStoreMacTest, TestRemoveLoginsCreatedBetween) {
- CheckRemoveLoginsBetween(this, true);
-}
-
-TEST_F(PasswordStoreMacTest, TestRemoveLoginsSyncedBetween) {
- CheckRemoveLoginsBetween(this, false);
-}
-
-TEST_F(PasswordStoreMacTest, TestDisableAutoSignInForOrigins) {
- PasswordFormData www_form_data_facebook = {
- PasswordForm::SCHEME_HTML,
- "http://www.facebook.com/",
- "http://www.facebook.com/index.html",
- "login",
- L"submit",
- L"username",
- L"password",
- L"joe_user",
- L"sekrit",
- true,
- 0};
- std::unique_ptr<PasswordForm> form_facebook =
- CreatePasswordFormFromDataForTesting(www_form_data_facebook);
- form_facebook->skip_zero_click = false;
-
- PasswordFormData www_form_data_google = {
- PasswordForm::SCHEME_HTML,
- "http://www.google.com/",
- "http://www.google.com/foo/bar/index.html",
- "login",
- L"submit",
- L"username",
- L"password",
- L"joe_user",
- L"sekrit",
- true,
- 0};
- std::unique_ptr<PasswordForm> form_google =
- CreatePasswordFormFromDataForTesting(www_form_data_google);
- form_google->skip_zero_click = false;
-
- // Add the zero-clickable forms to the database.
- PasswordsChangeObserver observer(store());
- store()->AddLogin(*form_facebook);
- store()->AddLogin(*form_google);
- EXPECT_CALL(observer, OnLoginsChanged(GetAddChangeList(*form_facebook)));
- EXPECT_CALL(observer, OnLoginsChanged(GetAddChangeList(*form_google)));
- observer.WaitAndVerify(this);
-
- std::vector<std::unique_ptr<PasswordForm>> forms;
- EXPECT_TRUE(login_db()->GetAutoSignInLogins(&forms));
- EXPECT_EQ(2u, forms.size());
- EXPECT_FALSE(forms[0]->skip_zero_click);
- EXPECT_FALSE(forms[1]->skip_zero_click);
-
- store()->DisableAutoSignInForOrigins(
- base::Bind(static_cast<bool (*)(const GURL&, const GURL&)>(operator==),
- form_google->origin),
- base::Closure());
- FinishAsyncProcessing();
-
- EXPECT_TRUE(login_db()->GetAutoSignInLogins(&forms));
- EXPECT_EQ(1u, forms.size());
- EXPECT_EQ(form_facebook->origin, forms[0]->origin);
-}
-
-TEST_F(PasswordStoreMacTest, TestRemoveLoginsMultiProfile) {
- // Make sure that RemoveLoginsCreatedBetween does affect only the correct
- // profile.
-
- // Add a third-party password.
- MockAppleKeychain::KeychainTestData keychain_data = {
- kSecAuthenticationTypeHTMLForm,
- "some.domain.com",
- kSecProtocolTypeHTTP,
- "/insecure.html",
- 0,
- NULL,
- "20020601171500Z",
- "joe_user",
- "sekrit",
- false};
- keychain()->AddTestItem(keychain_data);
-
- // Add a password through the adapter. It has the "Chrome" creator tag.
- // However, it's not referenced by the password database.
- MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain());
- owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
- PasswordFormData www_form_data1 = {PasswordForm::SCHEME_HTML,
- "http://www.facebook.com/",
- "http://www.facebook.com/index.html",
- "login",
- L"username",
- L"password",
- L"submit",
- L"joe_user",
- L"sekrit",
- true,
- 1};
- std::unique_ptr<PasswordForm> www_form =
- CreatePasswordFormFromDataForTesting(www_form_data1);
- EXPECT_TRUE(owned_keychain_adapter.AddPassword(*www_form));
-
- // Add a password from the current profile.
- PasswordFormData www_form_data2 = {PasswordForm::SCHEME_HTML,
- "http://www.facebook.com/",
- "http://www.facebook.com/index.html",
- "login",
- L"username",
- L"password",
- L"submit",
- L"not_joe_user",
- L"12345",
- true,
- 1};
- www_form = CreatePasswordFormFromDataForTesting(www_form_data2);
- store_->AddLogin(*www_form);
- FinishAsyncProcessing();
-
- std::vector<std::unique_ptr<PasswordForm>> matching_items;
- EXPECT_TRUE(login_db()->GetLogins(PasswordStore::FormDigest(*www_form),
- &matching_items));
- EXPECT_EQ(1u, matching_items.size());
-
- store_->RemoveLoginsCreatedBetween(base::Time(), base::Time(),
- base::Closure());
- FinishAsyncProcessing();
-
- // Check the second facebook form is gone.
- EXPECT_TRUE(login_db()->GetLogins(PasswordStore::FormDigest(*www_form),
- &matching_items));
- EXPECT_EQ(0u, matching_items.size());
-
- // Check the first facebook form is still there.
- std::vector<std::unique_ptr<PasswordForm>> matching_keychain_items;
- matching_keychain_items = owned_keychain_adapter.PasswordsFillingForm(
- www_form->signon_realm, www_form->scheme);
- ASSERT_EQ(1u, matching_keychain_items.size());
- EXPECT_EQ(ASCIIToUTF16("joe_user"),
- matching_keychain_items[0]->username_value);
-
- // Check the third-party password is still there.
- owned_keychain_adapter.SetFindsOnlyOwnedItems(false);
- matching_keychain_items = owned_keychain_adapter.PasswordsFillingForm(
- "http://some.domain.com/insecure.html", PasswordForm::SCHEME_HTML);
- ASSERT_EQ(1u, matching_keychain_items.size());
-}
-
-// Add a facebook form to the store but not to the keychain. The form is to be
-// implicitly deleted. However, the observers shouldn't get notified about
-// deletion of non-existent forms like m.facebook.com.
-TEST_F(PasswordStoreMacTest, SilentlyRemoveOrphanedForm) {
- testing::StrictMock<password_manager::MockPasswordStoreObserver>
- mock_observer;
- store()->AddObserver(&mock_observer);
-
- // 1. Add a password for www.facebook.com to the LoginDatabase.
- PasswordFormData www_form_data = {PasswordForm::SCHEME_HTML,
- "http://www.facebook.com/",
- "http://www.facebook.com/index.html",
- "login",
- L"username",
- L"password",
- L"submit",
- L"joe_user",
- L"",
- true,
- 1};
- std::unique_ptr<PasswordForm> www_form(
- CreatePasswordFormFromDataForTesting(www_form_data));
- EXPECT_EQ(AddChangeForForm(*www_form), login_db()->AddLogin(*www_form));
-
- // 2. Get a PSL-matched password for m.facebook.com. The observer isn't
- // notified because the form isn't in the database.
- PasswordForm m_form(*www_form);
- m_form.signon_realm = "http://m.facebook.com";
- m_form.origin = GURL("http://m.facebook.com/index.html");
-
- MockPasswordStoreConsumer consumer;
- ON_CALL(consumer, OnGetPasswordStoreResultsConstRef(_))
- .WillByDefault(QuitUIMessageLoop());
- EXPECT_CALL(mock_observer, OnLoginsChanged(_)).Times(0);
- // The PSL-matched form isn't returned because there is no actual password in
- // the keychain.
- EXPECT_CALL(consumer, OnGetPasswordStoreResultsConstRef(IsEmpty()));
- store_->GetLogins(PasswordStore::FormDigest(m_form), &consumer);
- base::RunLoop().Run();
- std::vector<std::unique_ptr<PasswordForm>> all_forms;
- EXPECT_TRUE(login_db()->GetAutofillableLogins(&all_forms));
- EXPECT_EQ(1u, all_forms.size());
- ::testing::Mock::VerifyAndClearExpectations(&mock_observer);
-
- // 3. Get a password for www.facebook.com. The form is implicitly removed and
- // the observer is notified.
- password_manager::PasswordStoreChangeList list;
- list.push_back(password_manager::PasswordStoreChange(
- password_manager::PasswordStoreChange::REMOVE, *www_form));
- EXPECT_CALL(mock_observer, OnLoginsChanged(list));
- EXPECT_CALL(consumer, OnGetPasswordStoreResultsConstRef(IsEmpty()));
- store_->GetLogins(PasswordStore::FormDigest(*www_form), &consumer);
- base::RunLoop().Run();
- EXPECT_TRUE(login_db()->GetAutofillableLogins(&all_forms));
- EXPECT_EQ(0u, all_forms.size());
-}
-
-// Verify that Android app passwords can be stored, retrieved, and deleted.
-// Regression test for http://crbug.com/455551
-TEST_F(PasswordStoreMacTest, StoringAndRetrievingAndroidCredentials) {
- PasswordForm form;
- form.signon_realm = "android://7x7IDboo8u9YKraUsbmVkuf1@net.rateflix.app/";
- form.username_value = base::UTF8ToUTF16("randomusername");
- form.password_value = base::UTF8ToUTF16("password");
-
- VerifyCredentialLifecycle(form);
-}
-
-// Verify that federated credentials can be stored, retrieved and deleted.
-TEST_F(PasswordStoreMacTest, StoringAndRetrievingFederatedCredentials) {
- PasswordForm form;
- form.signon_realm = "android://7x7IDboo8u9YKraUsbmVkuf1@net.rateflix.app/";
- form.federation_origin =
- url::Origin(GURL(password_manager::kTestingFederationUrlSpec));
- form.username_value = base::UTF8ToUTF16("randomusername");
- form.password_value = base::UTF8ToUTF16(""); // No password.
-
- VerifyCredentialLifecycle(form);
-}
-
-void CheckMigrationResult(PasswordStoreMac::MigrationResult expected_result,
- PasswordStoreMac::MigrationResult result) {
- EXPECT_EQ(expected_result, result);
- QuitUIMessageLoop();
-}
-
-// Import the passwords from the Keychain to LoginDatabase.
-TEST_F(PasswordStoreMacTest, ImportFromKeychain) {
- PasswordForm form1;
- form1.origin = GURL("http://accounts.google.com/LoginAuth");
- form1.signon_realm = "http://accounts.google.com/";
- form1.username_value = ASCIIToUTF16("my_username");
- form1.password_value = ASCIIToUTF16("my_password");
-
- PasswordForm form2;
- form2.origin = GURL("http://facebook.com/Login");
- form2.signon_realm = "http://facebook.com/";
- form2.username_value = ASCIIToUTF16("my_username");
- form2.password_value = ASCIIToUTF16("my_password");
-
- PasswordForm blacklisted_form;
- blacklisted_form.origin = GURL("http://badsite.com/Login");
- blacklisted_form.signon_realm = "http://badsite.com/";
- blacklisted_form.blacklisted_by_user = true;
-
- store()->AddLogin(form1);
- store()->AddLogin(form2);
- store()->AddLogin(blacklisted_form);
- FinishAsyncProcessing();
-
- ASSERT_TRUE(base::PostTaskAndReplyWithResult(
- thread_->task_runner().get(), FROM_HERE,
- base::Bind(&PasswordStoreMac::ImportFromKeychain, login_db(), keychain()),
- base::Bind(&CheckMigrationResult, PasswordStoreMac::MIGRATION_OK)));
- FinishAsyncProcessing();
-
- // The password should be stored in the database by now.
- std::vector<std::unique_ptr<PasswordForm>> matching_items;
- EXPECT_TRUE(
- login_db()->GetLogins(PasswordStore::FormDigest(form1), &matching_items));
- ASSERT_EQ(1u, matching_items.size());
- EXPECT_EQ(form1, *matching_items[0]);
-
- EXPECT_TRUE(
- login_db()->GetLogins(PasswordStore::FormDigest(form2), &matching_items));
- ASSERT_EQ(1u, matching_items.size());
- EXPECT_EQ(form2, *matching_items[0]);
-
- EXPECT_TRUE(login_db()->GetLogins(PasswordStore::FormDigest(blacklisted_form),
- &matching_items));
- ASSERT_EQ(1u, matching_items.size());
- EXPECT_EQ(blacklisted_form, *matching_items[0]);
-
- // The passwords are encrypted using a key from the Keychain.
- EXPECT_TRUE(
- histogram_tester_->GetHistogramSamplesSinceCreation("OSX.Keychain.Access")
- ->TotalCount());
- histogram_tester_.reset();
-}
-
-// Import a federated credential while the Keychain is locked.
-TEST_F(PasswordStoreMacTest, ImportFederatedFromLockedKeychain) {
- keychain()->set_locked(true);
- PasswordForm form1;
- form1.origin = GURL("http://example.com/Login");
- form1.signon_realm = "http://example.com/";
- form1.username_value = ASCIIToUTF16("my_username");
- form1.federation_origin = url::Origin(GURL("https://accounts.google.com/"));
-
- store()->AddLogin(form1);
- FinishAsyncProcessing();
- ASSERT_TRUE(base::PostTaskAndReplyWithResult(
- thread_->task_runner().get(), FROM_HERE,
- base::Bind(&PasswordStoreMac::ImportFromKeychain, login_db(), keychain()),
- base::Bind(&CheckMigrationResult, PasswordStoreMac::MIGRATION_OK)));
- FinishAsyncProcessing();
-
- std::vector<std::unique_ptr<PasswordForm>> matching_items;
- EXPECT_TRUE(
- login_db()->GetLogins(PasswordStore::FormDigest(form1), &matching_items));
- ASSERT_EQ(1u, matching_items.size());
- EXPECT_EQ(form1, *matching_items[0]);
-}
-
-// Try to import while the Keychain is locked but the encryption key had been
-// read earlier.
-TEST_F(PasswordStoreMacTest, ImportFromLockedKeychainError) {
- PasswordForm form1;
- form1.origin = GURL("http://accounts.google.com/LoginAuth");
- form1.signon_realm = "http://accounts.google.com/";
- form1.username_value = ASCIIToUTF16("my_username");
- form1.password_value = ASCIIToUTF16("my_password");
- store()->AddLogin(form1);
- FinishAsyncProcessing();
-
- // Add a second keychain item matching the Database entry.
- PasswordForm form2 = form1;
- form2.origin = GURL("http://accounts.google.com/Login");
- form2.password_value = ASCIIToUTF16("1234");
- MacKeychainPasswordFormAdapter adapter(keychain());
- EXPECT_TRUE(adapter.AddPassword(form2));
-
- keychain()->set_locked(true);
- ASSERT_TRUE(base::PostTaskAndReplyWithResult(
- thread_->task_runner().get(), FROM_HERE,
- base::Bind(&PasswordStoreMac::ImportFromKeychain, login_db(), keychain()),
- base::Bind(&CheckMigrationResult, PasswordStoreMac::MIGRATION_PARTIAL)));
- FinishAsyncProcessing();
-
- std::vector<std::unique_ptr<PasswordForm>> matching_items;
- EXPECT_TRUE(
- login_db()->GetLogins(PasswordStore::FormDigest(form1), &matching_items));
- EXPECT_EQ(0u, matching_items.size());
-
- histogram_tester_->ExpectUniqueSample(
- "PasswordManager.KeychainMigration.NumPasswordsOnFailure", 1, 1);
- histogram_tester_->ExpectUniqueSample(
- "PasswordManager.KeychainMigration.NumFailedPasswords", 1, 1);
- // Don't test the encryption key access.
- histogram_tester_.reset();
-}
-
-// Delete the Chrome-owned password from the Keychain.
-TEST_F(PasswordStoreMacTest, CleanUpKeychain) {
- MockAppleKeychain::KeychainTestData data1 = { kSecAuthenticationTypeHTMLForm,
- "some.domain.com", kSecProtocolTypeHTTP, NULL, 0, NULL, "20020601171500Z",
- "joe_user", "sekrit", false};
- keychain()->AddTestItem(data1);
-
- MacKeychainPasswordFormAdapter keychain_adapter(keychain());
- PasswordFormData data2 = { PasswordForm::SCHEME_HTML, "http://web.site.com/",
- "http://web.site.com/path/to/page.html", NULL, NULL, NULL, NULL,
- L"anonymous", L"knock-knock", false, 0 };
- keychain_adapter.AddPassword(*CreatePasswordFormFromDataForTesting(data2));
- std::vector<std::unique_ptr<PasswordForm>> passwords =
- keychain_adapter.GetAllPasswordFormPasswords();
- EXPECT_EQ(2u, passwords.size());
-
- // Delete everyhting but only the Chrome-owned item should be affected.
- PasswordStoreMac::CleanUpKeychain(keychain(), passwords);
- passwords = keychain_adapter.GetAllPasswordFormPasswords();
- ASSERT_EQ(1u, passwords.size());
- EXPECT_EQ("http://some.domain.com/", passwords[0]->signon_realm);
- EXPECT_EQ(ASCIIToUTF16("sekrit"), passwords[0]->password_value);
-}

Powered by Google App Engine
This is Rietveld 408576698