| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/password_manager/password_store_mac.h" | 5 #include "chrome/browser/password_manager/password_store_mac.h" |
| 6 | 6 |
| 7 #include "base/basictypes.h" | 7 #include "base/basictypes.h" |
| 8 #include "base/files/scoped_temp_dir.h" | 8 #include "base/files/scoped_temp_dir.h" |
| 9 #include "base/scoped_observer.h" | 9 #include "base/scoped_observer.h" |
| 10 #include "base/stl_util.h" | 10 #include "base/stl_util.h" |
| (...skipping 279 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 290 {kSecAuthenticationTypeDefault, | 290 {kSecAuthenticationTypeDefault, |
| 291 "a.server.com", | 291 "a.server.com", |
| 292 kSecProtocolTypeFTP, | 292 kSecProtocolTypeFTP, |
| 293 NULL, | 293 NULL, |
| 294 0, | 294 0, |
| 295 NULL, | 295 NULL, |
| 296 "20010203040", | 296 "20010203040", |
| 297 "abc", | 297 "abc", |
| 298 "123", | 298 "123", |
| 299 false}, | 299 false}, |
| 300 // Password for an Android application. |
| 301 {kSecAuthenticationTypeHTMLForm, |
| 302 "android://hash@com.domain.some/", |
| 303 kSecProtocolTypeHTTP, |
| 304 "", |
| 305 0, |
| 306 NULL, |
| 307 "20150515141312Z", |
| 308 "joe_user", |
| 309 "secret", |
| 310 false}, |
| 300 }; | 311 }; |
| 301 | 312 |
| 302 keychain_ = new MockAppleKeychain(); | 313 keychain_ = new MockAppleKeychain(); |
| 303 | 314 |
| 304 for (unsigned int i = 0; i < arraysize(test_data); ++i) { | 315 for (unsigned int i = 0; i < arraysize(test_data); ++i) { |
| 305 keychain_->AddTestItem(test_data[i]); | 316 keychain_->AddTestItem(test_data[i]); |
| 306 } | 317 } |
| 307 } | 318 } |
| 308 | 319 |
| 309 void TearDown() override { | 320 void TearDown() override { |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 372 "http://some.domain.com:4567/insecure.html", L"basic_auth_user", L"basic", | 383 "http://some.domain.com:4567/insecure.html", L"basic_auth_user", L"basic", |
| 373 false, 1998, 03, 30, 10, 00, 00 }, | 384 false, 1998, 03, 30, 10, 00, 00 }, |
| 374 { PasswordForm::SCHEME_DIGEST, "https://some.domain.com/high_security", | 385 { PasswordForm::SCHEME_DIGEST, "https://some.domain.com/high_security", |
| 375 "https://some.domain.com/", L"digest_auth_user", L"digest", true, | 386 "https://some.domain.com/", L"digest_auth_user", L"digest", true, |
| 376 1998, 3, 30, 10, 0, 0 }, | 387 1998, 3, 30, 10, 0, 0 }, |
| 377 // This one gives us an invalid date, which we will treat as a "NULL" date | 388 // This one gives us an invalid date, which we will treat as a "NULL" date |
| 378 // which is 1601. | 389 // which is 1601. |
| 379 { PasswordForm::SCHEME_OTHER, "http://a.server.com/", | 390 { PasswordForm::SCHEME_OTHER, "http://a.server.com/", |
| 380 "http://a.server.com/", L"abc", L"123", false, | 391 "http://a.server.com/", L"abc", L"123", false, |
| 381 1601, 1, 1, 0, 0, 0 }, | 392 1601, 1, 1, 0, 0, 0 }, |
| 393 { PasswordForm::SCHEME_HTML, "android://hash@com.domain.some/", |
| 394 "", L"joe_user", L"secret", true, |
| 395 2015, 5, 15, 14, 13, 12 }, |
| 382 }; | 396 }; |
| 383 | 397 |
| 384 for (unsigned int i = 0; i < arraysize(expected); ++i) { | 398 for (unsigned int i = 0; i < arraysize(expected); ++i) { |
| 385 // Create our fake KeychainItemRef; see MockAppleKeychain docs. | 399 // Create our fake KeychainItemRef; see MockAppleKeychain docs. |
| 386 SecKeychainItemRef keychain_item = | 400 SecKeychainItemRef keychain_item = |
| 387 reinterpret_cast<SecKeychainItemRef>(i + 1); | 401 reinterpret_cast<SecKeychainItemRef>(i + 1); |
| 388 PasswordForm form; | 402 PasswordForm form; |
| 389 bool parsed = internal_keychain_helpers::FillPasswordFormFromKeychainItem( | 403 bool parsed = internal_keychain_helpers::FillPasswordFormFromKeychainItem( |
| 390 *keychain_, keychain_item, &form, true); | 404 *keychain_, keychain_item, &form, true); |
| 391 | 405 |
| (...skipping 207 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 599 << " of base form " << i; | 613 << " of base form " << i; |
| 600 } | 614 } |
| 601 } | 615 } |
| 602 } | 616 } |
| 603 | 617 |
| 604 TEST_F(PasswordStoreMacInternalsTest, TestKeychainAdd) { | 618 TEST_F(PasswordStoreMacInternalsTest, TestKeychainAdd) { |
| 605 struct TestDataAndExpectation { | 619 struct TestDataAndExpectation { |
| 606 PasswordFormData data; | 620 PasswordFormData data; |
| 607 bool should_succeed; | 621 bool should_succeed; |
| 608 }; | 622 }; |
| 623 /* clang-format off */ |
| 609 TestDataAndExpectation test_data[] = { | 624 TestDataAndExpectation test_data[] = { |
| 610 // Test a variety of scheme/port/protocol/path variations. | 625 // Test a variety of scheme/port/protocol/path variations. |
| 611 { { PasswordForm::SCHEME_HTML, "http://web.site.com/", | 626 { { PasswordForm::SCHEME_HTML, "http://web.site.com/", |
| 612 "http://web.site.com/path/to/page.html", NULL, NULL, NULL, NULL, | 627 "http://web.site.com/path/to/page.html", NULL, NULL, NULL, NULL, |
| 613 L"anonymous", L"knock-knock", false, false, 0 }, true }, | 628 L"anonymous", L"knock-knock", false, false, 0 }, true }, |
| 614 { { PasswordForm::SCHEME_HTML, "https://web.site.com/", | 629 { { PasswordForm::SCHEME_HTML, "https://web.site.com/", |
| 615 "https://web.site.com/", NULL, NULL, NULL, NULL, | 630 "https://web.site.com/", NULL, NULL, NULL, NULL, |
| 616 L"admin", L"p4ssw0rd", false, false, 0 }, true }, | 631 L"admin", L"p4ssw0rd", false, false, 0 }, true }, |
| 617 { { PasswordForm::SCHEME_BASIC, "http://a.site.com:2222/therealm", | 632 { { PasswordForm::SCHEME_BASIC, "http://a.site.com:2222/therealm", |
| 618 "http://a.site.com:2222/", NULL, NULL, NULL, NULL, | 633 "http://a.site.com:2222/", NULL, NULL, NULL, NULL, |
| 619 L"username", L"password", false, false, 0 }, true }, | 634 L"username", L"password", false, false, 0 }, true }, |
| 620 { { PasswordForm::SCHEME_DIGEST, "https://digest.site.com/differentrealm", | 635 { { PasswordForm::SCHEME_DIGEST, "https://digest.site.com/differentrealm", |
| 621 "https://digest.site.com/secure.html", NULL, NULL, NULL, NULL, | 636 "https://digest.site.com/secure.html", NULL, NULL, NULL, NULL, |
| 622 L"testname", L"testpass", false, false, 0 }, true }, | 637 L"testname", L"testpass", false, false, 0 }, true }, |
| 638 // Test that Android credentials can be stored. Also check the legacy form |
| 639 // when |origin| was still filled with the Android URI (and not left empty). |
| 640 { { PasswordForm::SCHEME_HTML, "android://hash@com.example.alpha/", |
| 641 "", NULL, NULL, NULL, NULL, |
| 642 L"joe_user", L"password", false, true, 0 }, true }, |
| 643 { { PasswordForm::SCHEME_HTML, "android://hash@com.example.beta/", |
| 644 "android://hash@com.example.beta/", NULL, NULL, NULL, NULL, |
| 645 L"jane_user", L"password2", false, true, 0 }, true }, |
| 623 // Make sure that garbage forms are rejected. | 646 // Make sure that garbage forms are rejected. |
| 624 { { PasswordForm::SCHEME_HTML, "gobbledygook", | 647 { { PasswordForm::SCHEME_HTML, "gobbledygook", |
| 625 "gobbledygook", NULL, NULL, NULL, NULL, | 648 "gobbledygook", NULL, NULL, NULL, NULL, |
| 626 L"anonymous", L"knock-knock", false, false, 0 }, false }, | 649 L"anonymous", L"knock-knock", false, false, 0 }, false }, |
| 627 // Test that failing to update a duplicate (forced using the magic failure | 650 // Test that failing to update a duplicate (forced using the magic failure |
| 628 // password; see MockAppleKeychain::ItemModifyAttributesAndData) is | 651 // password; see MockAppleKeychain::ItemModifyAttributesAndData) is |
| 629 // reported. | 652 // reported. |
| 630 { { PasswordForm::SCHEME_HTML, "http://some.domain.com", | 653 { { PasswordForm::SCHEME_HTML, "http://some.domain.com", |
| 631 "http://some.domain.com/insecure.html", NULL, NULL, NULL, NULL, | 654 "http://some.domain.com/insecure.html", NULL, NULL, NULL, NULL, |
| 632 L"joe_user", L"fail_me", false, false, 0 }, false }, | 655 L"joe_user", L"fail_me", false, false, 0 }, false }, |
| 633 }; | 656 }; |
| 657 /* clang-format on */ |
| 634 | 658 |
| 635 MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_); | 659 MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_); |
| 636 owned_keychain_adapter.SetFindsOnlyOwnedItems(true); | 660 owned_keychain_adapter.SetFindsOnlyOwnedItems(true); |
| 637 | 661 |
| 638 for (unsigned int i = 0; i < arraysize(test_data); ++i) { | 662 for (unsigned int i = 0; i < arraysize(test_data); ++i) { |
| 639 scoped_ptr<PasswordForm> in_form = | 663 scoped_ptr<PasswordForm> in_form = |
| 640 CreatePasswordFormFromDataForTesting(test_data[i].data); | 664 CreatePasswordFormFromDataForTesting(test_data[i].data); |
| 641 bool add_succeeded = owned_keychain_adapter.AddPassword(*in_form); | 665 bool add_succeeded = owned_keychain_adapter.AddPassword(*in_form); |
| 642 EXPECT_EQ(test_data[i].should_succeed, add_succeeded); | 666 EXPECT_EQ(test_data[i].should_succeed, add_succeeded); |
| 643 if (add_succeeded) { | 667 if (add_succeeded) { |
| 644 EXPECT_TRUE(owned_keychain_adapter.HasPasswordsMergeableWithForm( | 668 EXPECT_TRUE(owned_keychain_adapter.HasPasswordsMergeableWithForm( |
| 645 *in_form)); | 669 *in_form)); |
| 646 EXPECT_TRUE(owned_keychain_adapter.HasPasswordExactlyMatchingForm( | 670 EXPECT_TRUE(owned_keychain_adapter.HasPasswordExactlyMatchingForm( |
| 647 *in_form)); | 671 *in_form)); |
| 648 } | 672 } |
| 649 } | 673 } |
| 650 | 674 |
| 651 // Test that adding duplicate item updates the existing item. | 675 // Test that adding duplicate item updates the existing item. |
| 676 // TODO(engedy): Add a test to verify that updating Android credentials work. |
| 677 // See: https://crbug.com/476851. |
| 652 { | 678 { |
| 653 PasswordFormData data = { | 679 PasswordFormData data = { |
| 654 PasswordForm::SCHEME_HTML, "http://some.domain.com", | 680 PasswordForm::SCHEME_HTML, "http://some.domain.com", |
| 655 "http://some.domain.com/insecure.html", NULL, | 681 "http://some.domain.com/insecure.html", NULL, |
| 656 NULL, NULL, NULL, L"joe_user", L"updated_password", false, false, 0 | 682 NULL, NULL, NULL, L"joe_user", L"updated_password", false, false, 0 |
| 657 }; | 683 }; |
| 658 scoped_ptr<PasswordForm> update_form = | 684 scoped_ptr<PasswordForm> update_form = |
| 659 CreatePasswordFormFromDataForTesting(data); | 685 CreatePasswordFormFromDataForTesting(data); |
| 660 MacKeychainPasswordFormAdapter keychain_adapter(keychain_); | 686 MacKeychainPasswordFormAdapter keychain_adapter(keychain_); |
| 661 EXPECT_TRUE(keychain_adapter.AddPassword(*update_form)); | 687 EXPECT_TRUE(keychain_adapter.AddPassword(*update_form)); |
| 662 SecKeychainItemRef keychain_item = reinterpret_cast<SecKeychainItemRef>(2); | 688 SecKeychainItemRef keychain_item = reinterpret_cast<SecKeychainItemRef>(2); |
| 663 PasswordForm stored_form; | 689 PasswordForm stored_form; |
| 664 internal_keychain_helpers::FillPasswordFormFromKeychainItem(*keychain_, | 690 internal_keychain_helpers::FillPasswordFormFromKeychainItem(*keychain_, |
| 665 keychain_item, | 691 keychain_item, |
| 666 &stored_form, | 692 &stored_form, |
| 667 true); | 693 true); |
| 668 EXPECT_EQ(update_form->password_value, stored_form.password_value); | 694 EXPECT_EQ(update_form->password_value, stored_form.password_value); |
| 669 } | 695 } |
| 670 } | 696 } |
| 671 | 697 |
| 672 TEST_F(PasswordStoreMacInternalsTest, TestKeychainRemove) { | 698 TEST_F(PasswordStoreMacInternalsTest, TestKeychainRemove) { |
| 673 struct TestDataAndExpectation { | 699 struct TestDataAndExpectation { |
| 674 PasswordFormData data; | 700 PasswordFormData data; |
| 675 bool should_succeed; | 701 bool should_succeed; |
| 676 }; | 702 }; |
| 703 /* clang-format off */ |
| 677 TestDataAndExpectation test_data[] = { | 704 TestDataAndExpectation test_data[] = { |
| 678 // Test deletion of an item that we add. | 705 // Test deletion of an item that we add. |
| 679 { { PasswordForm::SCHEME_HTML, "http://web.site.com/", | 706 { { PasswordForm::SCHEME_HTML, "http://web.site.com/", |
| 680 "http://web.site.com/path/to/page.html", NULL, NULL, NULL, NULL, | 707 "http://web.site.com/path/to/page.html", NULL, NULL, NULL, NULL, |
| 681 L"anonymous", L"knock-knock", false, false, 0 }, true }, | 708 L"anonymous", L"knock-knock", false, false, 0 }, true }, |
| 709 // Test that Android credentials can be removed. Also check the legacy case |
| 710 // when |origin| was still filled with the Android URI (and not left empty). |
| 711 { { PasswordForm::SCHEME_HTML, "android://hash@com.example.alpha/", |
| 712 "", NULL, NULL, NULL, NULL, |
| 713 L"joe_user", L"secret", false, true, 0 }, true }, |
| 714 { { PasswordForm::SCHEME_HTML, "android://hash@com.example.beta/", |
| 715 "android://hash@com.example.beta/", NULL, NULL, NULL, NULL, |
| 716 L"jane_user", L"secret", false, true, 0 }, true }, |
| 682 // Make sure we don't delete items we don't own. | 717 // Make sure we don't delete items we don't own. |
| 683 { { PasswordForm::SCHEME_HTML, "http://some.domain.com/", | 718 { { PasswordForm::SCHEME_HTML, "http://some.domain.com/", |
| 684 "http://some.domain.com/insecure.html", NULL, NULL, NULL, NULL, | 719 "http://some.domain.com/insecure.html", NULL, NULL, NULL, NULL, |
| 685 L"joe_user", NULL, true, false, 0 }, false }, | 720 L"joe_user", NULL, true, false, 0 }, false }, |
| 686 }; | 721 }; |
| 722 /* clang-format on */ |
| 687 | 723 |
| 688 MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_); | 724 MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_); |
| 689 owned_keychain_adapter.SetFindsOnlyOwnedItems(true); | 725 owned_keychain_adapter.SetFindsOnlyOwnedItems(true); |
| 690 | 726 |
| 691 // Add our test item so that we can delete it. | 727 // Add our test items (except the last one) so that we can delete them. |
| 692 scoped_ptr<PasswordForm> add_form = | 728 for (unsigned int i = 0; i + 1 < arraysize(test_data); ++i) { |
| 693 CreatePasswordFormFromDataForTesting(test_data[0].data); | 729 scoped_ptr<PasswordForm> add_form = |
| 694 EXPECT_TRUE(owned_keychain_adapter.AddPassword(*add_form)); | 730 CreatePasswordFormFromDataForTesting(test_data[i].data); |
| 731 EXPECT_TRUE(owned_keychain_adapter.AddPassword(*add_form)); |
| 732 } |
| 695 | 733 |
| 696 for (unsigned int i = 0; i < arraysize(test_data); ++i) { | 734 for (unsigned int i = 0; i < arraysize(test_data); ++i) { |
| 697 scoped_ptr<PasswordForm> form = | 735 scoped_ptr<PasswordForm> form = |
| 698 CreatePasswordFormFromDataForTesting(test_data[i].data); | 736 CreatePasswordFormFromDataForTesting(test_data[i].data); |
| 699 EXPECT_EQ(test_data[i].should_succeed, | 737 EXPECT_EQ(test_data[i].should_succeed, |
| 700 owned_keychain_adapter.RemovePassword(*form)); | 738 owned_keychain_adapter.RemovePassword(*form)); |
| 701 | 739 |
| 702 MacKeychainPasswordFormAdapter keychain_adapter(keychain_); | 740 MacKeychainPasswordFormAdapter keychain_adapter(keychain_); |
| 703 bool match = keychain_adapter.HasPasswordExactlyMatchingForm(*form); | 741 bool match = keychain_adapter.HasPasswordExactlyMatchingForm(*form); |
| 704 EXPECT_EQ(test_data[i].should_succeed, !match); | 742 EXPECT_EQ(test_data[i].should_succeed, !match); |
| (...skipping 374 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1079 L"testname", L"testpass", false, false, 0 }, | 1117 L"testname", L"testpass", false, false, 0 }, |
| 1080 }; | 1118 }; |
| 1081 for (unsigned int i = 0; i < arraysize(owned_password_data); ++i) { | 1119 for (unsigned int i = 0; i < arraysize(owned_password_data); ++i) { |
| 1082 scoped_ptr<PasswordForm> form = | 1120 scoped_ptr<PasswordForm> form = |
| 1083 CreatePasswordFormFromDataForTesting(owned_password_data[i]); | 1121 CreatePasswordFormFromDataForTesting(owned_password_data[i]); |
| 1084 owned_keychain_adapter.AddPassword(*form); | 1122 owned_keychain_adapter.AddPassword(*form); |
| 1085 } | 1123 } |
| 1086 | 1124 |
| 1087 ScopedVector<autofill::PasswordForm> all_passwords = | 1125 ScopedVector<autofill::PasswordForm> all_passwords = |
| 1088 keychain_adapter.GetAllPasswordFormPasswords(); | 1126 keychain_adapter.GetAllPasswordFormPasswords(); |
| 1089 EXPECT_EQ(8 + arraysize(owned_password_data), all_passwords.size()); | 1127 EXPECT_EQ(9 + arraysize(owned_password_data), all_passwords.size()); |
| 1090 | 1128 |
| 1091 ScopedVector<autofill::PasswordForm> owned_passwords = | 1129 ScopedVector<autofill::PasswordForm> owned_passwords = |
| 1092 owned_keychain_adapter.GetAllPasswordFormPasswords(); | 1130 owned_keychain_adapter.GetAllPasswordFormPasswords(); |
| 1093 EXPECT_EQ(arraysize(owned_password_data), owned_passwords.size()); | 1131 EXPECT_EQ(arraysize(owned_password_data), owned_passwords.size()); |
| 1094 } | 1132 } |
| 1095 | 1133 |
| 1096 #pragma mark - | 1134 #pragma mark - |
| 1097 | 1135 |
| 1098 class PasswordStoreMacTest : public testing::Test { | 1136 class PasswordStoreMacTest : public testing::Test { |
| 1099 public: | 1137 public: |
| (...skipping 577 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1677 query_form.password_value.clear(); | 1715 query_form.password_value.clear(); |
| 1678 query_form.username_value.clear(); | 1716 query_form.username_value.clear(); |
| 1679 EXPECT_CALL(mock_consumer, OnGetPasswordStoreResultsConstRef(SizeIs(1u))) | 1717 EXPECT_CALL(mock_consumer, OnGetPasswordStoreResultsConstRef(SizeIs(1u))) |
| 1680 .WillOnce( | 1718 .WillOnce( |
| 1681 DoAll(SaveACopyOfFirstForm(&returned_form), QuitUIMessageLoop())); | 1719 DoAll(SaveACopyOfFirstForm(&returned_form), QuitUIMessageLoop())); |
| 1682 store()->GetLogins(query_form, PasswordStore::ALLOW_PROMPT, &mock_consumer); | 1720 store()->GetLogins(query_form, PasswordStore::ALLOW_PROMPT, &mock_consumer); |
| 1683 base::MessageLoop::current()->Run(); | 1721 base::MessageLoop::current()->Run(); |
| 1684 ::testing::Mock::VerifyAndClearExpectations(&mock_consumer); | 1722 ::testing::Mock::VerifyAndClearExpectations(&mock_consumer); |
| 1685 EXPECT_EQ(form, returned_form); | 1723 EXPECT_EQ(form, returned_form); |
| 1686 } | 1724 } |
| OLD | NEW |