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 |