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

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

Issue 115658: First phase of Mac Keychain integration. For the overall plan, see the design... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 11 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
« no previous file with comments | « chrome/browser/password_manager/password_store_mac_internal.h ('k') | chrome/chrome.gyp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/browser/password_manager/password_store_mac_unittest.cc
===================================================================
--- chrome/browser/password_manager/password_store_mac_unittest.cc (revision 0)
+++ chrome/browser/password_manager/password_store_mac_unittest.cc (revision 0)
@@ -0,0 +1,710 @@
+// Copyright (c) 2009 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 "testing/gtest/include/gtest/gtest.h"
+
+#include "base/basictypes.h"
+#include "chrome/browser/password_manager/password_store_mac.h"
+#include "chrome/browser/password_manager/password_store_mac_internal.h"
+
+#pragma mark Mock Keychain
+
+// TODO(stuartmorgan): Replace this with gMock. You know, once we have it.
+// The basic idea of this mock is that it has a static array of data to use
+// for ItemCopyAttributesAndData, and SecKeychainItemRef values are just indexes
+// into that array (offset by 1 to prevent problems with client null-checking
+// refs), cast to pointers.
+class MockKeychain : public MacKeychain {
+ public:
+ MockKeychain();
+ virtual ~MockKeychain();
+ virtual OSStatus ItemCopyAttributesAndData(
+ SecKeychainItemRef itemRef, SecKeychainAttributeInfo *info,
+ SecItemClass *itemClass, SecKeychainAttributeList **attrList,
+ UInt32 *length, void **outData) const;
+ virtual OSStatus ItemFreeAttributesAndData(SecKeychainAttributeList *attrList,
+ void *data) const;
+ virtual OSStatus SearchCreateFromAttributes(
+ CFTypeRef keychainOrArray, SecItemClass itemClass,
+ const SecKeychainAttributeList *attrList,
+ SecKeychainSearchRef *searchRef) const;
+ virtual OSStatus SearchCopyNext(SecKeychainSearchRef searchRef,
+ SecKeychainItemRef *itemRef) const;
+ virtual void Free(CFTypeRef ref) const;
+
+ // Causes a test failure unless everything returned from
+ // ItemCopyAttributesAndData, SearchCreateFromAttributes, and SearchCopyNext
+ // was correctly freed.
+ void ExpectCreatesAndFreesBalanced();
+
+ private:
+ // Sets the data and length of |tag| in the item-th test item based on
+ // |value|. The null-terminator will not be included; the Keychain Services
+ // docs don't indicate whether it is or not, so clients should not assume
+ // that it will be.
+ void SetTestDataString(int item, UInt32 tag, const char* value);
+ // Sets the data of the corresponding attribute of the item-th test item to
+ // |value|. Assumes that the space has alread been allocated, and the length
+ // set.
+ void SetTestDataPort(int item, UInt32 value);
+ void SetTestDataProtocol(int item, SecProtocolType value);
+ void SetTestDataAuthType(int item, SecAuthenticationType value);
+ void SetTestDataNegativeItem(int item, Boolean value);
+ // Sets the password for the item-th test item. As with SetTestDataString,
+ // the data will not be null-terminated.
+ void SetTestDataPassword(int item, const char* value);
+
+ // Returns the index of |tag| in |attribute_list|, or -1 if it's not found.
+ static int IndexForTag(const SecKeychainAttributeList& attribute_list,
+ UInt32 tag);
+
+ static const int kDummySearchRef = 1000;
+
+ typedef struct {
+ void* data;
+ UInt32 length;
+ } KeychainPasswordData;
+
+ SecKeychainAttributeList* keychain_attr_list_;
+ KeychainPasswordData* keychain_data_;
+ unsigned int item_count_;
+
+ // Tracks the items that should be returned in subsequent calls to
+ // SearchCopyNext, based on the last call to SearchCreateFromAttributes.
+ // We can't handle multiple active searches, since we don't track the search
+ // ref we return, but we don't need to for our mocking.
+ mutable std::vector<unsigned int> remaining_search_results_;
+
+ // Track copies and releases to make sure they balance. Really these should
+ // be maps to track per item, but this should be good enough to catch
+ // real mistakes.
+ mutable int search_copy_count_;
+ mutable int keychain_item_copy_count_;
+ mutable int attribute_data_copy_count_;
+};
+
+MockKeychain::MockKeychain()
+ : search_copy_count_(0), keychain_item_copy_count_(0),
+ attribute_data_copy_count_(0) {
+ UInt32 tags[] = { kSecAccountItemAttr,
+ kSecServerItemAttr,
+ kSecPortItemAttr,
+ kSecPathItemAttr,
+ kSecProtocolItemAttr,
+ kSecAuthenticationTypeItemAttr,
+ kSecSecurityDomainItemAttr,
+ kSecCreationDateItemAttr,
+ kSecNegativeItemAttr };
+
+ // Create the test keychain data to return from ItemCopyAttributesAndData,
+ // and set up everything that's consistent across all the items.
+ item_count_ = 8;
+ keychain_attr_list_ = static_cast<SecKeychainAttributeList*>(
+ calloc(item_count_, sizeof(SecKeychainAttributeList)));
+ keychain_data_ = static_cast<KeychainPasswordData*>(
+ calloc(item_count_, sizeof(KeychainPasswordData)));
+ for (unsigned int i = 0; i < item_count_; ++i) {
+ keychain_attr_list_[i].count = arraysize(tags);
+ keychain_attr_list_[i].attr = static_cast<SecKeychainAttribute*>(
+ calloc(keychain_attr_list_[i].count, sizeof(SecKeychainAttribute)));
+ for (unsigned int j = 0; j < keychain_attr_list_[i].count; ++j) {
+ keychain_attr_list_[i].attr[j].tag = tags[j];
+ size_t data_size = 0;
+ switch (tags[j]) {
+ case kSecPortItemAttr:
+ data_size = sizeof(UInt32);
+ break;
+ case kSecProtocolItemAttr:
+ data_size = sizeof(SecProtocolType);
+ break;
+ case kSecAuthenticationTypeItemAttr:
+ data_size = sizeof(SecAuthenticationType);
+ break;
+ case kSecNegativeItemAttr:
+ data_size = sizeof(Boolean);
+ break;
+ }
+ if (data_size > 0) {
+ keychain_attr_list_[i].attr[j].length = data_size;
+ keychain_attr_list_[i].attr[j].data = calloc(1, data_size);
+ }
+ }
+ }
+
+ // Basic HTML form.
+ unsigned int item = 0;
+ CHECK(item < item_count_);
+ SetTestDataString(item, kSecAccountItemAttr, "joe_user");
+ SetTestDataString(item, kSecServerItemAttr, "some.domain.com");
+ SetTestDataProtocol(item, kSecProtocolTypeHTTP);
+ SetTestDataAuthType(item, kSecAuthenticationTypeHTMLForm);
+ SetTestDataString(item, kSecCreationDateItemAttr, "20020601171500Z");
+ SetTestDataPassword(item, "sekrit");
+
+ // HTML form with path.
+ ++item;
+ CHECK(item < item_count_);
+ SetTestDataString(item, kSecAccountItemAttr, "joe_user");
+ SetTestDataString(item, kSecServerItemAttr, "some.domain.com");
+ SetTestDataString(item, kSecPathItemAttr, "insecure.html");
+ SetTestDataProtocol(item, kSecProtocolTypeHTTP);
+ SetTestDataAuthType(item, kSecAuthenticationTypeHTMLForm);
+ SetTestDataString(item, kSecCreationDateItemAttr, "19991231235959Z");
+ SetTestDataPassword(item, "sekrit");
+
+ // Secure HTML form with path.
+ ++item;
+ CHECK(item < item_count_);
+ SetTestDataString(item, kSecAccountItemAttr, "secure_user");
+ SetTestDataString(item, kSecServerItemAttr, "some.domain.com");
+ SetTestDataString(item, kSecPathItemAttr, "secure.html");
+ SetTestDataProtocol(item, kSecProtocolTypeHTTPS);
+ SetTestDataAuthType(item, kSecAuthenticationTypeHTMLForm);
+ SetTestDataString(item, kSecCreationDateItemAttr, "20100908070605Z");
+ SetTestDataPassword(item, "password");
+
+ // True negative item.
+ ++item;
+ CHECK(item < item_count_);
+ SetTestDataString(item, kSecServerItemAttr, "dont.remember.com");
+ SetTestDataProtocol(item, kSecProtocolTypeHTTP);
+ SetTestDataAuthType(item, kSecAuthenticationTypeHTMLForm);
+ SetTestDataString(item, kSecCreationDateItemAttr, "20000101000000Z");
+ SetTestDataNegativeItem(item, true);
+
+ // De-facto negative item, type one.
+ ++item;
+ CHECK(item < item_count_);
+ SetTestDataString(item, kSecAccountItemAttr, "Password Not Stored");
+ SetTestDataString(item, kSecServerItemAttr, "dont.remember.com");
+ SetTestDataProtocol(item, kSecProtocolTypeHTTP);
+ SetTestDataAuthType(item, kSecAuthenticationTypeHTMLForm);
+ SetTestDataString(item, kSecCreationDateItemAttr, "20000101000000Z");
+ SetTestDataPassword(item, "");
+
+ // De-facto negative item, type two.
+ ++item;
+ CHECK(item < item_count_);
+ SetTestDataString(item, kSecServerItemAttr, "dont.remember.com");
+ SetTestDataProtocol(item, kSecProtocolTypeHTTPS);
+ SetTestDataAuthType(item, kSecAuthenticationTypeHTMLForm);
+ SetTestDataString(item, kSecCreationDateItemAttr, "20000101000000Z");
+ SetTestDataPassword(item, " ");
+
+ // HTTP auth basic, with port and path.
+ ++item;
+ CHECK(item < item_count_);
+ SetTestDataString(item, kSecAccountItemAttr, "basic_auth_user");
+ SetTestDataString(item, kSecServerItemAttr, "some.domain.com");
+ SetTestDataString(item, kSecSecurityDomainItemAttr, "low_security");
+ SetTestDataString(item, kSecPathItemAttr, "insecure.html");
+ SetTestDataProtocol(item, kSecProtocolTypeHTTP);
+ SetTestDataPort(item, 4567);
+ SetTestDataAuthType(item, kSecAuthenticationTypeHTTPBasic);
+ SetTestDataString(item, kSecCreationDateItemAttr, "19980330100000Z");
+ SetTestDataPassword(item, "basic");
+
+ // HTTP auth digest, secure.
+ ++item;
+ CHECK(item < item_count_);
+ SetTestDataString(item, kSecAccountItemAttr, "digest_auth_user");
+ SetTestDataString(item, kSecServerItemAttr, "some.domain.com");
+ SetTestDataString(item, kSecSecurityDomainItemAttr, "high_security");
+ SetTestDataProtocol(item, kSecProtocolTypeHTTPS);
+ SetTestDataAuthType(item, kSecAuthenticationTypeHTTPDigest);
+ SetTestDataString(item, kSecCreationDateItemAttr, "19980330100000Z");
+ SetTestDataPassword(item, "digest");
+}
+
+MockKeychain::~MockKeychain() {
+ for (unsigned int i = 0; i < item_count_; ++i) {
+ for (unsigned int j = 0; j < keychain_attr_list_[i].count; ++j) {
+ if (keychain_attr_list_[i].attr[j].data) {
+ free(keychain_attr_list_[i].attr[j].data);
+ }
+ }
+ free(keychain_attr_list_[i].attr);
+ if (keychain_data_[i].data) {
+ free(keychain_data_[i].data);
+ }
+ }
+ free(keychain_attr_list_);
+ free(keychain_data_);
+}
+
+void MockKeychain::ExpectCreatesAndFreesBalanced() {
+ EXPECT_EQ(0, search_copy_count_);
+ EXPECT_EQ(0, keychain_item_copy_count_);
+ EXPECT_EQ(0, attribute_data_copy_count_);
+}
+
+int MockKeychain::IndexForTag(const SecKeychainAttributeList& attribute_list,
+ UInt32 tag) {
+ for (unsigned int i = 0; i < attribute_list.count; ++i) {
+ if (attribute_list.attr[i].tag == tag) {
+ return i;
+ }
+ }
+ DCHECK(false);
+ return -1;
+}
+
+void MockKeychain::SetTestDataString(int item, UInt32 tag, const char* value) {
+ int attribute_index = IndexForTag(keychain_attr_list_[item], tag);
+ size_t data_size = strlen(value);
+ keychain_attr_list_[item].attr[attribute_index].length = data_size;
+ if (data_size > 0) {
+ keychain_attr_list_[item].attr[attribute_index].data = malloc(data_size);
+ // Use memcpy rather than str*cpy because we are deliberately omitting the
+ // null-terminator (see method declaration comment).
+ CHECK(keychain_attr_list_[item].attr[attribute_index].data);
+ memcpy(keychain_attr_list_[item].attr[attribute_index].data, value,
+ data_size);
+ } else {
+ keychain_attr_list_[item].attr[attribute_index].data = NULL;
+ }
+}
+
+void MockKeychain::SetTestDataPort(int item, UInt32 value) {
+ int attribute_index = IndexForTag(keychain_attr_list_[item],
+ kSecPortItemAttr);
+ void* data = keychain_attr_list_[item].attr[attribute_index].data;
+ *(static_cast<UInt32*>(data)) = value;
+}
+
+void MockKeychain::SetTestDataProtocol(int item, SecProtocolType value) {
+ int attribute_index = IndexForTag(keychain_attr_list_[item],
+ kSecProtocolItemAttr);
+ void* data = keychain_attr_list_[item].attr[attribute_index].data;
+ *(static_cast<SecProtocolType*>(data)) = value;
+}
+
+void MockKeychain::SetTestDataAuthType(int item, SecAuthenticationType value) {
+ int attribute_index = IndexForTag(keychain_attr_list_[item],
+ kSecAuthenticationTypeItemAttr);
+ void* data = keychain_attr_list_[item].attr[attribute_index].data;
+ *(static_cast<SecAuthenticationType*>(data)) = value;
+}
+
+void MockKeychain::SetTestDataNegativeItem(int item, Boolean value) {
+ int attribute_index = IndexForTag(keychain_attr_list_[item],
+ kSecNegativeItemAttr);
+ void* data = keychain_attr_list_[item].attr[attribute_index].data;
+ *(static_cast<Boolean*>(data)) = value;
+}
+
+void MockKeychain::SetTestDataPassword(int item, const char* value) {
+ size_t data_size = strlen(value);
+ keychain_data_[item].length = data_size;
+ if (data_size > 0) {
+ keychain_data_[item].data = malloc(data_size);
+ // Use memcpy rather than str*cpy because we are deliberately omitting the
+ // null-terminator (see method declaration comment).
+ memcpy(keychain_data_[item].data, value, data_size);
+ } else {
+ keychain_data_[item].data = NULL;
+ }
+}
+
+OSStatus MockKeychain::ItemCopyAttributesAndData(
+ SecKeychainItemRef itemRef, SecKeychainAttributeInfo *info,
+ SecItemClass *itemClass, SecKeychainAttributeList **attrList,
+ UInt32 *length, void **outData) const {
+ DCHECK(itemRef);
+ unsigned int item_index = reinterpret_cast<unsigned int>(itemRef) - 1;
+ if (item_index >= item_count_) {
+ return errSecInvalidItemRef;
+ }
+
+ DCHECK(!itemClass); // itemClass not implemented in the Mock.
+ if (attrList) {
+ *attrList = &(keychain_attr_list_[item_index]);
+ }
+ if (outData) {
+ *outData = keychain_data_[item_index].data;
+ DCHECK(length);
+ *length = keychain_data_[item_index].length;
+ }
+
+ ++attribute_data_copy_count_;
+ return noErr;
+}
+
+OSStatus MockKeychain::ItemFreeAttributesAndData(
+ SecKeychainAttributeList *attrList,
+ void *data) const {
+ --attribute_data_copy_count_;
+ return noErr;
+}
+
+OSStatus MockKeychain::SearchCreateFromAttributes(
+ CFTypeRef keychainOrArray, SecItemClass itemClass,
+ const SecKeychainAttributeList *attrList,
+ SecKeychainSearchRef *searchRef) const {
+ // Figure out which of our mock items matches, and set up the array we'll use
+ // to generate results out of SearchCopyNext.
+ remaining_search_results_.clear();
+ for (unsigned int mock_item = 0; mock_item < item_count_; ++mock_item) {
+ bool mock_item_matches = true;
+ for (UInt32 search_attr = 0; search_attr < attrList->count; ++search_attr) {
+ int mock_attr = IndexForTag(keychain_attr_list_[mock_item],
+ attrList->attr[search_attr].tag);
+ SecKeychainAttribute* mock_attribute =
+ &(keychain_attr_list_[mock_item].attr[mock_attr]);
+ if (mock_attribute->length != attrList->attr[search_attr].length ||
+ memcmp(mock_attribute->data, attrList->attr[search_attr].data,
+ attrList->attr[search_attr].length) != 0) {
+ mock_item_matches = false;
+ break;
+ }
+ }
+ if (mock_item_matches) {
+ remaining_search_results_.push_back(mock_item);
+ }
+ }
+
+ DCHECK(searchRef);
+ *searchRef = reinterpret_cast<SecKeychainSearchRef>(kDummySearchRef);
+ ++search_copy_count_;
+ return noErr;
+}
+
+OSStatus MockKeychain::SearchCopyNext(SecKeychainSearchRef searchRef,
+ SecKeychainItemRef *itemRef) const {
+ if (remaining_search_results_.empty()) {
+ return errSecItemNotFound;
+ }
+ unsigned int index = remaining_search_results_.front();
+ remaining_search_results_.erase(remaining_search_results_.begin());
+ *itemRef = reinterpret_cast<SecKeychainItemRef>(index + 1);
+ ++keychain_item_copy_count_;
+ return noErr;
+}
+
+void MockKeychain::Free(CFTypeRef ref) const {
+ if (!ref) {
+ return;
+ }
+
+ if (reinterpret_cast<int>(ref) == kDummySearchRef) {
+ --search_copy_count_;
+ } else {
+ --keychain_item_copy_count_;
+ }
+}
+
+#pragma mark Unit Tests
+////////////////////////////////////////////////////////////////////////////////
+
+TEST(PasswordStoreMacTest, TestSignonRealmParsing) {
+ typedef struct {
+ const char* signon_realm;
+ const bool expected_parsed;
+ const char* expected_server;
+ const bool expected_is_secure;
+ const int expected_port;
+ const char* expected_security_domain;
+ } TestData;
+
+ TestData test_data[] = {
+ // HTML form signon realms.
+ { "http://www.domain.com/",
+ true, "www.domain.com", false, 0, "" },
+ { "https://foo.org:9999/",
+ true, "foo.org", true, 9999, "" },
+ // HTTP auth signon realms.
+ { "http://httpauth.com:8080/lowsecurity",
+ true, "httpauth.com", false, 8080, "lowsecurity" },
+ { "https://httpauth.com/highsecurity",
+ true, "httpauth.com", true, 0 , "highsecurity" },
+ // Bogus realms
+ { "blahblahblah",
+ false, false, "", 0, "" },
+ { "foo/bar/baz",
+ false, false, "", 0, "" },
+ };
+
+ for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
+ std::string server;
+ std::string security_domain;
+ bool is_secure = false;
+ int port = -1;
+ bool parsed = internal_keychain_helpers::ExtractSignonRealmComponents(
+ std::string(test_data[i].signon_realm), &server, &port, &is_secure,
+ &security_domain);
+ EXPECT_EQ(test_data[i].expected_parsed, parsed) << "In iteration " << i;
+
+ if (!parsed) {
+ continue; // If parse failed, out params are undefined.
+ }
+ EXPECT_EQ(std::string(test_data[i].expected_server), server)
+ << "In iteration " << i;
+ EXPECT_EQ(std::string(test_data[i].expected_security_domain),
+ security_domain)
+ << "In iteration " << i;
+ EXPECT_EQ(test_data[i].expected_is_secure, is_secure)
+ << "In iteration " << i;
+ EXPECT_EQ(test_data[i].expected_port, port) << "In iteration " << i;
+ }
+
+ // NULLs are allowed for out params.
+ bool parsed = internal_keychain_helpers::ExtractSignonRealmComponents(
+ std::string("http://foo.bar.com:1234/baz"), NULL, NULL, NULL, NULL);
+ EXPECT_TRUE(parsed);
+}
+
+TEST(PasswordStoreMacTest, TestURLConstruction) {
+ std::string host("exampledomain.com");
+ std::string path("/path/to/page.html");
+
+ GURL full_url = internal_keychain_helpers::URLFromComponents(false, host,
+ 1234, path);
+ EXPECT_TRUE(full_url.is_valid());
+ EXPECT_EQ(GURL("http://exampledomain.com:1234/path/to/page.html"), full_url);
+
+ GURL simple_secure_url = internal_keychain_helpers::URLFromComponents(
+ true, host, 0, std::string(""));
+ EXPECT_TRUE(simple_secure_url.is_valid());
+ EXPECT_EQ(GURL("https://exampledomain.com/"), simple_secure_url);
+}
+
+TEST(PasswordStoreMacTest, TestKeychainTime) {
+ typedef struct {
+ const char* time_string;
+ const bool expected_parsed;
+ const int expected_year;
+ const int expected_month;
+ const int expected_day;
+ const int expected_hour;
+ const int expected_minute;
+ const int expected_second;
+ } TestData;
+
+ TestData test_data[] = {
+ // HTML form signon realms.
+ { "19980330100000Z", true, 1998, 3, 30, 10, 0, 0 },
+ { "19991231235959Z", true, 1999, 12, 31, 23, 59, 59 },
+ { "20000101000000Z", true, 2000, 1, 1, 0, 0, 0 },
+ { "20011112012843Z", true, 2001, 11, 12, 1, 28, 43 },
+ { "20020601171530Z", true, 2002, 6, 1, 17, 15, 30 },
+ { "20100908070605Z", true, 2010, 9, 8, 7, 6, 5 },
+ { "20010203040", false, 0, 0, 0, 0, 0, 0 },
+ };
+
+ for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
+ base::Time time;
+ bool parsed = internal_keychain_helpers::TimeFromKeychainTimeString(
+ test_data[i].time_string, strlen(test_data[i].time_string), &time);
+ EXPECT_EQ(test_data[i].expected_parsed, parsed) << "In iteration " << i;
+ if (!parsed) {
+ continue;
+ }
+
+ base::Time::Exploded exploded_time;
+ time.UTCExplode(&exploded_time);
+ EXPECT_EQ(test_data[i].expected_year, exploded_time.year)
+ << "In iteration " << i;
+ EXPECT_EQ(test_data[i].expected_month, exploded_time.month)
+ << "In iteration " << i;
+ EXPECT_EQ(test_data[i].expected_day, exploded_time.day_of_month)
+ << "In iteration " << i;
+ EXPECT_EQ(test_data[i].expected_hour, exploded_time.hour)
+ << "In iteration " << i;
+ EXPECT_EQ(test_data[i].expected_minute, exploded_time.minute)
+ << "In iteration " << i;
+ EXPECT_EQ(test_data[i].expected_second, exploded_time.second)
+ << "In iteration " << i;
+ }
+}
+
+TEST(PasswordStoreMacTest, TestAuthTypeSchemeTranslation) {
+ // Our defined types should round-trip correctly.
+ SecAuthenticationType auth_types[] = { kSecAuthenticationTypeHTMLForm,
+ kSecAuthenticationTypeHTTPBasic,
+ kSecAuthenticationTypeHTTPDigest };
+ const int auth_count = sizeof(auth_types) / sizeof(SecAuthenticationType);
+ for (int i = 0; i < auth_count; ++i) {
+ SecAuthenticationType round_tripped_auth_type =
+ internal_keychain_helpers::AuthTypeForScheme(
+ internal_keychain_helpers::SchemeForAuthType(auth_types[i]));
+ EXPECT_EQ(auth_types[i], round_tripped_auth_type);
+ }
+ // Anything else should become SCHEME_OTHER and come back as Default.
+ PasswordForm::Scheme scheme_for_other_auth_type =
+ internal_keychain_helpers::SchemeForAuthType(kSecAuthenticationTypeNTLM);
+ SecAuthenticationType round_tripped_other_auth_type =
+ internal_keychain_helpers::AuthTypeForScheme(scheme_for_other_auth_type);
+ EXPECT_EQ(PasswordForm::SCHEME_OTHER, scheme_for_other_auth_type);
+ EXPECT_EQ(kSecAuthenticationTypeDefault, round_tripped_other_auth_type);
+}
+
+TEST(PasswordStoreMacTest, 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 bool ssl_valid;
+ 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", false,
+ 2002, 6, 1, 17, 15, 0 },
+ { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
+ "http://some.domain.com/insecure.html", L"joe_user", L"sekrit", false,
+ 1999, 12, 31, 23, 59, 59 },
+ { PasswordForm::SCHEME_HTML, "https://some.domain.com/",
+ "https://some.domain.com/secure.html", L"secure_user", L"password", true,
+ 2010, 9, 8, 7, 6, 5 },
+ { PasswordForm::SCHEME_HTML, "http://dont.remember.com/",
+ "http://dont.remember.com/", NULL, NULL, false,
+ 2000, 1, 1, 0, 0, 0 },
+ { PasswordForm::SCHEME_HTML, "http://dont.remember.com/",
+ "http://dont.remember.com/", NULL, NULL, false,
+ 2000, 1, 1, 0, 0, 0 },
+ { PasswordForm::SCHEME_HTML, "https://dont.remember.com/",
+ "https://dont.remember.com/", NULL, NULL, true,
+ 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",
+ false, 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", true,
+ 1998, 3, 30, 10, 0, 0 },
+ };
+
+ MockKeychain mock_keychain;
+
+ for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(expected); ++i) {
+ // Create our fake KeychainItemRef; see MockKeychain docs.
+ SecKeychainItemRef keychain_item =
+ reinterpret_cast<SecKeychainItemRef>(i + 1);
+ PasswordForm form;
+ bool parsed = internal_keychain_helpers::FillPasswordFormFromKeychainItem(
+ mock_keychain, keychain_item, &form);
+
+ EXPECT_TRUE(parsed) << "In iteration " << i;
+ mock_keychain.ExpectCreatesAndFreesBalanced();
+
+ EXPECT_EQ(expected[i].scheme, form.scheme) << "In iteration " << i;
+ EXPECT_EQ(GURL(expected[i].origin), form.origin) << "In iteration " << i;
+ EXPECT_EQ(expected[i].ssl_valid, form.ssl_valid) << "In iteration " << i;
+ EXPECT_EQ(std::string(expected[i].signon_realm), form.signon_realm)
+ << "In iteration " << i;
+ if (expected[i].username) {
+ EXPECT_EQ(std::wstring(expected[i].username), form.username_value)
+ << "In iteration " << i;
+ EXPECT_EQ(std::wstring(expected[i].password), form.password_value)
+ << "In iteration " << i;
+ EXPECT_FALSE(form.blacklisted_by_user) << "In iteration " << i;
+ } else {
+ EXPECT_TRUE(form.blacklisted_by_user) << "In iteration " << i;
+ }
+ base::Time::Exploded exploded_time;
+ form.date_created.UTCExplode(&exploded_time);
+ EXPECT_EQ(expected[i].creation_year, exploded_time.year)
+ << "In iteration " << i;
+ EXPECT_EQ(expected[i].creation_month, exploded_time.month)
+ << "In iteration " << i;
+ EXPECT_EQ(expected[i].creation_day, exploded_time.day_of_month)
+ << "In iteration " << i;
+ EXPECT_EQ(expected[i].creation_hour, exploded_time.hour)
+ << "In iteration " << i;
+ EXPECT_EQ(expected[i].creation_minute, exploded_time.minute)
+ << "In iteration " << i;
+ EXPECT_EQ(expected[i].creation_second, exploded_time.second)
+ << "In iteration " << i;
+ }
+
+ {
+ // 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(
+ mock_keychain, keychain_item, &form);
+ mock_keychain.ExpectCreatesAndFreesBalanced();
+ EXPECT_FALSE(parsed);
+ }
+}
+
+static void FreeKeychainItems(const MacKeychain& keychain,
+ std::vector<SecKeychainItemRef>* items) {
+ for (std::vector<SecKeychainItemRef>::iterator i = items->begin();
+ i != items->end(); ++i) {
+ keychain.Free(*i);
+ }
+ items->clear();
+}
+
+TEST(PasswordStoreMacTest, TestKeychainSearch) {
+ MockKeychain mock_keychain;
+
+ { // An HTML form we've seen.
+ std::vector<SecKeychainItemRef> matching_items;
+ internal_keychain_helpers::FindMatchingKeychainItems(
+ mock_keychain, std::string("http://some.domain.com/"),
+ PasswordForm::SCHEME_HTML, &matching_items);
+ EXPECT_EQ(static_cast<size_t>(2), matching_items.size());
+ FreeKeychainItems(mock_keychain, &matching_items);
+ mock_keychain.ExpectCreatesAndFreesBalanced();
+ }
+
+ { // An HTML form we haven't seen
+ std::vector<SecKeychainItemRef> matching_items;
+ internal_keychain_helpers::FindMatchingKeychainItems(
+ mock_keychain, std::string("http://www.unseendomain.com/"),
+ PasswordForm::SCHEME_HTML, &matching_items);
+ EXPECT_EQ(static_cast<size_t>(0), matching_items.size());
+ FreeKeychainItems(mock_keychain, &matching_items);
+ mock_keychain.ExpectCreatesAndFreesBalanced();
+ }
+
+ { // Basic auth that should match.
+ std::vector<SecKeychainItemRef> matching_items;
+ internal_keychain_helpers::FindMatchingKeychainItems(
+ mock_keychain, std::string("http://some.domain.com:4567/low_security"),
+ PasswordForm::SCHEME_BASIC, &matching_items);
+ EXPECT_EQ(static_cast<size_t>(1), matching_items.size());
+ FreeKeychainItems(mock_keychain, &matching_items);
+ mock_keychain.ExpectCreatesAndFreesBalanced();
+ }
+
+ { // Basic auth with the wrong port.
+ std::vector<SecKeychainItemRef> matching_items;
+ internal_keychain_helpers::FindMatchingKeychainItems(
+ mock_keychain, std::string("http://some.domain.com:1111/low_security"),
+ PasswordForm::SCHEME_BASIC, &matching_items);
+ EXPECT_EQ(static_cast<size_t>(0), matching_items.size());
+ FreeKeychainItems(mock_keychain, &matching_items);
+ mock_keychain.ExpectCreatesAndFreesBalanced();
+ }
+
+ { // Digest auth we've saved under https, visited with http.
+ std::vector<SecKeychainItemRef> matching_items;
+ internal_keychain_helpers::FindMatchingKeychainItems(
+ mock_keychain, std::string("http://some.domain.com/high_security"),
+ PasswordForm::SCHEME_DIGEST, &matching_items);
+ EXPECT_EQ(static_cast<size_t>(0), matching_items.size());
+ FreeKeychainItems(mock_keychain, &matching_items);
+ mock_keychain.ExpectCreatesAndFreesBalanced();
+ }
+
+ { // Digest auth that should match.
+ std::vector<SecKeychainItemRef> matching_items;
+ internal_keychain_helpers::FindMatchingKeychainItems(
+ mock_keychain, std::string("https://some.domain.com/high_security"),
+ PasswordForm::SCHEME_DIGEST, &matching_items);
+ EXPECT_EQ(static_cast<size_t>(1), matching_items.size());
+ FreeKeychainItems(mock_keychain, &matching_items);
+ mock_keychain.ExpectCreatesAndFreesBalanced();
+ }
+}
« no previous file with comments | « chrome/browser/password_manager/password_store_mac_internal.h ('k') | chrome/chrome.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698