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
3 // found in the LICENSE file.
5 #include "testing/gmock/include/gmock/gmock.h"
6 #include "testing/gtest/include/gtest/gtest.h"
8 #include "base/basictypes.h"
9 #include "base/files/scoped_temp_dir.h"
10 #include "base/path_service.h"
11 #include "base/stl_util.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "chrome/browser/password_manager/password_store_consumer.h"
15 #include "chrome/browser/password_manager/password_store_mac.h"
16 #include "chrome/browser/password_manager/password_store_mac_internal.h"
17 #include "chrome/common/chrome_paths.h"
18 #include "content/public/test/test_browser_thread.h"
19 #include "crypto/mock_apple_keychain.h"
21 using autofill::PasswordForm;
22 using content::BrowserThread;
23 using crypto::MockAppleKeychain;
26 using testing::WithArg;
30 class MockPasswordStoreConsumer : public PasswordStoreConsumer {
32 MOCK_METHOD2(OnPasswordStoreRequestDone,
33 void(CancelableRequestProvider::Handle,
34 const std::vector<autofill::PasswordForm*>&));
35 MOCK_METHOD1(OnGetPasswordStoreResults,
36 void(const std::vector<autofill::PasswordForm*>&));
39 ACTION(STLDeleteElements0) {
40 STLDeleteContainerPointers(arg0.begin(), arg0.end());
43 ACTION(QuitUIMessageLoop) {
44 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
45 base::MessageLoop::current()->Quit();
52 class PasswordStoreMacInternalsTest : public testing::Test {
54 virtual void SetUp() {
55 MockAppleKeychain::KeychainTestData test_data[] = {
57 { kSecAuthenticationTypeHTMLForm, "some.domain.com",
58 kSecProtocolTypeHTTP, NULL, 0, NULL, "20020601171500Z",
59 "joe_user", "sekrit", false },
60 // HTML form with path.
61 { kSecAuthenticationTypeHTMLForm, "some.domain.com",
62 kSecProtocolTypeHTTP, "/insecure.html", 0, NULL, "19991231235959Z",
63 "joe_user", "sekrit", false },
64 // Secure HTML form with path.
65 { kSecAuthenticationTypeHTMLForm, "some.domain.com",
66 kSecProtocolTypeHTTPS, "/secure.html", 0, NULL, "20100908070605Z",
67 "secure_user", "password", false },
68 // True negative item.
69 { kSecAuthenticationTypeHTMLForm, "dont.remember.com",
70 kSecProtocolTypeHTTP, NULL, 0, NULL, "20000101000000Z",
72 // De-facto negative item, type one.
73 { kSecAuthenticationTypeHTMLForm, "dont.remember.com",
74 kSecProtocolTypeHTTP, NULL, 0, NULL, "20000101000000Z",
75 "Password Not Stored", "", false },
76 // De-facto negative item, type two.
77 { kSecAuthenticationTypeHTMLForm, "dont.remember.com",
78 kSecProtocolTypeHTTPS, NULL, 0, NULL, "20000101000000Z",
79 "Password Not Stored", " ", false },
80 // HTTP auth basic, with port and path.
81 { kSecAuthenticationTypeHTTPBasic, "some.domain.com",
82 kSecProtocolTypeHTTP, "/insecure.html", 4567, "low_security",
84 "basic_auth_user", "basic", false },
85 // HTTP auth digest, secure.
86 { kSecAuthenticationTypeHTTPDigest, "some.domain.com",
87 kSecProtocolTypeHTTPS, NULL, 0, "high_security", "19980330100000Z",
88 "digest_auth_user", "digest", false },
89 // An FTP password with an invalid date, for edge-case testing.
90 { kSecAuthenticationTypeDefault, "a.server.com",
91 kSecProtocolTypeFTP, NULL, 0, NULL, "20010203040",
92 "abc", "123", false },
95 keychain_ = new MockAppleKeychain();
97 for (unsigned int i = 0; i < arraysize(test_data); ++i) {
98 keychain_->AddTestItem(test_data[i]);
102 virtual void TearDown() {
103 ExpectCreatesAndFreesBalanced();
104 ExpectCreatorCodesSet();
109 // Causes a test failure unless everything returned from keychain_'s
110 // ItemCopyAttributesAndData, SearchCreateFromAttributes, and SearchCopyNext
111 // was correctly freed.
112 void ExpectCreatesAndFreesBalanced() {
113 EXPECT_EQ(0, keychain_->UnfreedSearchCount());
114 EXPECT_EQ(0, keychain_->UnfreedKeychainItemCount());
115 EXPECT_EQ(0, keychain_->UnfreedAttributeDataCount());
118 // Causes a test failure unless any Keychain items added during the test have
119 // their creator code set.
120 void ExpectCreatorCodesSet() {
121 EXPECT_TRUE(keychain_->CreatorCodesSetForAddedItems());
124 MockAppleKeychain* keychain_;
129 // Struct used for creation of PasswordForms from static arrays of data.
130 struct PasswordFormData {
131 const PasswordForm::Scheme scheme;
132 const char* signon_realm;
135 const wchar_t* submit_element;
136 const wchar_t* username_element;
137 const wchar_t* password_element;
138 const wchar_t* username_value; // Set to NULL for a blacklist entry.
139 const wchar_t* password_value;
140 const bool preferred;
141 const bool ssl_valid;
142 const double creation_time;
145 // Creates and returns a new PasswordForm built from form_data. Caller is
146 // responsible for deleting the object when finished with it.
147 static PasswordForm* CreatePasswordFormFromData(
148 const PasswordFormData& form_data) {
149 PasswordForm* form = new PasswordForm();
150 form->scheme = form_data.scheme;
151 form->preferred = form_data.preferred;
152 form->ssl_valid = form_data.ssl_valid;
153 form->date_created = base::Time::FromDoubleT(form_data.creation_time);
154 if (form_data.signon_realm)
155 form->signon_realm = std::string(form_data.signon_realm);
156 if (form_data.origin)
157 form->origin = GURL(form_data.origin);
158 if (form_data.action)
159 form->action = GURL(form_data.action);
160 if (form_data.submit_element)
161 form->submit_element = WideToUTF16(form_data.submit_element);
162 if (form_data.username_element)
163 form->username_element = WideToUTF16(form_data.username_element);
164 if (form_data.password_element)
165 form->password_element = WideToUTF16(form_data.password_element);
166 if (form_data.username_value) {
167 form->username_value = WideToUTF16(form_data.username_value);
168 if (form_data.password_value)
169 form->password_value = WideToUTF16(form_data.password_value);
171 form->blacklisted_by_user = true;
176 // Macro to simplify calling CheckFormsAgainstExpectations with a useful label.
177 #define CHECK_FORMS(forms, expectations, i) \
178 CheckFormsAgainstExpectations(forms, expectations, #forms, i)
180 // Ensures that the data in |forms| match |expectations|, causing test failures
181 // for any discrepencies.
182 // TODO(stuartmorgan): This is current order-dependent; ideally it shouldn't
183 // matter if |forms| and |expectations| are scrambled.
184 static void CheckFormsAgainstExpectations(
185 const std::vector<PasswordForm*>& forms,
186 const std::vector<PasswordFormData*>& expectations,
187 const char* forms_label, unsigned int test_number) {
188 const unsigned int kBufferSize = 128;
189 char test_label[kBufferSize];
190 snprintf(test_label, kBufferSize, "%s in test %u", forms_label, test_number);
192 EXPECT_EQ(expectations.size(), forms.size()) << test_label;
193 if (expectations.size() != forms.size())
196 for (unsigned int i = 0; i < expectations.size(); ++i) {
197 snprintf(test_label, kBufferSize, "%s in test %u, item %u",
198 forms_label, test_number, i);
199 PasswordForm* form = forms[i];
200 PasswordFormData* expectation = expectations[i];
201 EXPECT_EQ(expectation->scheme, form->scheme) << test_label;
202 EXPECT_EQ(std::string(expectation->signon_realm), form->signon_realm)
204 EXPECT_EQ(GURL(expectation->origin), form->origin) << test_label;
205 EXPECT_EQ(GURL(expectation->action), form->action) << test_label;
206 EXPECT_EQ(WideToUTF16(expectation->submit_element), form->submit_element)
208 EXPECT_EQ(WideToUTF16(expectation->username_element),
209 form->username_element) << test_label;
210 EXPECT_EQ(WideToUTF16(expectation->password_element),
211 form->password_element) << test_label;
212 if (expectation->username_value) {
213 EXPECT_EQ(WideToUTF16(expectation->username_value),
214 form->username_value) << test_label;
215 EXPECT_EQ(WideToUTF16(expectation->password_value),
216 form->password_value) << test_label;
218 EXPECT_TRUE(form->blacklisted_by_user) << test_label;
220 EXPECT_EQ(expectation->preferred, form->preferred) << test_label;
221 EXPECT_EQ(expectation->ssl_valid, form->ssl_valid) << test_label;
222 EXPECT_DOUBLE_EQ(expectation->creation_time,
223 form->date_created.ToDoubleT()) << test_label;
229 TEST_F(PasswordStoreMacInternalsTest, TestKeychainToFormTranslation) {
231 const PasswordForm::Scheme scheme;
232 const char* signon_realm;
234 const wchar_t* username; // Set to NULL to check for a blacklist entry.
235 const wchar_t* password;
236 const bool ssl_valid;
237 const int creation_year;
238 const int creation_month;
239 const int creation_day;
240 const int creation_hour;
241 const int creation_minute;
242 const int creation_second;
245 TestExpectations expected[] = {
246 { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
247 "http://some.domain.com/", L"joe_user", L"sekrit", false,
248 2002, 6, 1, 17, 15, 0 },
249 { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
250 "http://some.domain.com/insecure.html", L"joe_user", L"sekrit", false,
251 1999, 12, 31, 23, 59, 59 },
252 { PasswordForm::SCHEME_HTML, "https://some.domain.com/",
253 "https://some.domain.com/secure.html", L"secure_user", L"password", true,
254 2010, 9, 8, 7, 6, 5 },
255 { PasswordForm::SCHEME_HTML, "http://dont.remember.com/",
256 "http://dont.remember.com/", NULL, NULL, false,
257 2000, 1, 1, 0, 0, 0 },
258 { PasswordForm::SCHEME_HTML, "http://dont.remember.com/",
259 "http://dont.remember.com/", NULL, NULL, false,
260 2000, 1, 1, 0, 0, 0 },
261 { PasswordForm::SCHEME_HTML, "https://dont.remember.com/",
262 "https://dont.remember.com/", NULL, NULL, true,
263 2000, 1, 1, 0, 0, 0 },
264 { PasswordForm::SCHEME_BASIC, "http://some.domain.com:4567/low_security",
265 "http://some.domain.com:4567/insecure.html", L"basic_auth_user", L"basic",
266 false, 1998, 03, 30, 10, 00, 00 },
267 { PasswordForm::SCHEME_DIGEST, "https://some.domain.com/high_security",
268 "https://some.domain.com/", L"digest_auth_user", L"digest", true,
269 1998, 3, 30, 10, 0, 0 },
270 // This one gives us an invalid date, which we will treat as a "NULL" date
272 { PasswordForm::SCHEME_OTHER, "http://a.server.com/",
273 "http://a.server.com/", L"abc", L"123", false,
274 1601, 1, 1, 0, 0, 0 },
277 for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(expected); ++i) {
278 // Create our fake KeychainItemRef; see MockAppleKeychain docs.
279 SecKeychainItemRef keychain_item =
280 reinterpret_cast<SecKeychainItemRef>(i + 1);
282 bool parsed = internal_keychain_helpers::FillPasswordFormFromKeychainItem(
283 *keychain_, keychain_item, &form, true);
285 EXPECT_TRUE(parsed) << "In iteration " << i;
287 EXPECT_EQ(expected[i].scheme, form.scheme) << "In iteration " << i;
288 EXPECT_EQ(GURL(expected[i].origin), form.origin) << "In iteration " << i;
289 EXPECT_EQ(expected[i].ssl_valid, form.ssl_valid) << "In iteration " << i;
290 EXPECT_EQ(std::string(expected[i].signon_realm), form.signon_realm)
291 << "In iteration " << i;
292 if (expected[i].username) {
293 EXPECT_EQ(WideToUTF16(expected[i].username), form.username_value)
294 << "In iteration " << i;
295 EXPECT_EQ(WideToUTF16(expected[i].password), form.password_value)
296 << "In iteration " << i;
297 EXPECT_FALSE(form.blacklisted_by_user) << "In iteration " << i;
299 EXPECT_TRUE(form.blacklisted_by_user) << "In iteration " << i;
301 base::Time::Exploded exploded_time;
302 form.date_created.UTCExplode(&exploded_time);
303 EXPECT_EQ(expected[i].creation_year, exploded_time.year)
304 << "In iteration " << i;
305 EXPECT_EQ(expected[i].creation_month, exploded_time.month)
306 << "In iteration " << i;
307 EXPECT_EQ(expected[i].creation_day, exploded_time.day_of_month)
308 << "In iteration " << i;
309 EXPECT_EQ(expected[i].creation_hour, exploded_time.hour)
310 << "In iteration " << i;
311 EXPECT_EQ(expected[i].creation_minute, exploded_time.minute)
312 << "In iteration " << i;
313 EXPECT_EQ(expected[i].creation_second, exploded_time.second)
314 << "In iteration " << i;
318 // Use an invalid ref, to make sure errors are reported.
319 SecKeychainItemRef keychain_item = reinterpret_cast<SecKeychainItemRef>(99);
321 bool parsed = internal_keychain_helpers::FillPasswordFormFromKeychainItem(
322 *keychain_, keychain_item, &form, true);
323 EXPECT_FALSE(parsed);
327 TEST_F(PasswordStoreMacInternalsTest, TestKeychainSearch) {
328 struct TestDataAndExpectation {
329 const PasswordFormData data;
330 const size_t expected_fill_matches;
331 const size_t expected_merge_matches;
333 // Most fields are left blank because we don't care about them for searching.
334 TestDataAndExpectation test_data[] = {
335 // An HTML form we've seen.
336 { { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
337 NULL, NULL, NULL, NULL, NULL, L"joe_user", NULL, false, false, 0 },
339 { { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
340 NULL, NULL, NULL, NULL, NULL, L"wrong_user", NULL, false, false, 0 },
342 // An HTML form we haven't seen
343 { { PasswordForm::SCHEME_HTML, "http://www.unseendomain.com/",
344 NULL, NULL, NULL, NULL, NULL, L"joe_user", NULL, false, false, 0 },
346 // Basic auth that should match.
347 { { PasswordForm::SCHEME_BASIC, "http://some.domain.com:4567/low_security",
348 NULL, NULL, NULL, NULL, NULL, L"basic_auth_user", NULL, false, false,
351 // Basic auth with the wrong port.
352 { { PasswordForm::SCHEME_BASIC, "http://some.domain.com:1111/low_security",
353 NULL, NULL, NULL, NULL, NULL, L"basic_auth_user", NULL, false, false,
356 // Digest auth we've saved under https, visited with http.
357 { { PasswordForm::SCHEME_DIGEST, "http://some.domain.com/high_security",
358 NULL, NULL, NULL, NULL, NULL, L"digest_auth_user", NULL, false, false,
361 // Digest auth that should match.
362 { { PasswordForm::SCHEME_DIGEST, "https://some.domain.com/high_security",
363 NULL, NULL, NULL, NULL, NULL, L"wrong_user", NULL, false, true, 0 },
365 // Digest auth with the wrong domain.
366 { { PasswordForm::SCHEME_DIGEST, "https://some.domain.com/other_domain",
367 NULL, NULL, NULL, NULL, NULL, L"digest_auth_user", NULL, false, true,
370 // Garbage forms should have no matches.
371 { { PasswordForm::SCHEME_HTML, "foo/bar/baz",
372 NULL, NULL, NULL, NULL, NULL, NULL, NULL, false, false, 0 }, 0, 0 },
375 MacKeychainPasswordFormAdapter keychain_adapter(keychain_);
376 MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_);
377 owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
378 for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
379 scoped_ptr<PasswordForm> query_form(
380 CreatePasswordFormFromData(test_data[i].data));
382 // Check matches treating the form as a fill target.
383 std::vector<PasswordForm*> matching_items =
384 keychain_adapter.PasswordsFillingForm(*query_form);
385 EXPECT_EQ(test_data[i].expected_fill_matches, matching_items.size());
386 STLDeleteElements(&matching_items);
388 // Check matches treating the form as a merging target.
389 EXPECT_EQ(test_data[i].expected_merge_matches > 0,
390 keychain_adapter.HasPasswordsMergeableWithForm(*query_form));
391 std::vector<SecKeychainItemRef> keychain_items;
392 std::vector<internal_keychain_helpers::ItemFormPair> item_form_pairs =
393 internal_keychain_helpers::
394 ExtractAllKeychainItemAttributesIntoPasswordForms(&keychain_items,
397 internal_keychain_helpers::ExtractPasswordsMergeableWithForm(
398 *keychain_, item_form_pairs, *query_form);
399 EXPECT_EQ(test_data[i].expected_merge_matches, matching_items.size());
400 STLDeleteContainerPairSecondPointers(item_form_pairs.begin(),
401 item_form_pairs.end());
402 for (std::vector<SecKeychainItemRef>::iterator i = keychain_items.begin();
403 i != keychain_items.end(); ++i) {
406 STLDeleteElements(&matching_items);
408 // None of the pre-seeded items are owned by us, so none should match an
409 // owned-passwords-only search.
410 matching_items = owned_keychain_adapter.PasswordsFillingForm(*query_form);
411 EXPECT_EQ(0U, matching_items.size());
412 STLDeleteElements(&matching_items);
416 // Changes just the origin path of |form|.
417 static void SetPasswordFormPath(PasswordForm* form, const char* path) {
418 GURL::Replacements replacement;
419 std::string new_value(path);
420 replacement.SetPathStr(new_value);
421 form->origin = form->origin.ReplaceComponents(replacement);
424 // Changes just the signon_realm port of |form|.
425 static void SetPasswordFormPort(PasswordForm* form, const char* port) {
426 GURL::Replacements replacement;
427 std::string new_value(port);
428 replacement.SetPortStr(new_value);
429 GURL signon_gurl = GURL(form->signon_realm);
430 form->signon_realm = signon_gurl.ReplaceComponents(replacement).spec();
433 // Changes just the signon_ream auth realm of |form|.
434 static void SetPasswordFormRealm(PasswordForm* form, const char* realm) {
435 GURL::Replacements replacement;
436 std::string new_value(realm);
437 replacement.SetPathStr(new_value);
438 GURL signon_gurl = GURL(form->signon_realm);
439 form->signon_realm = signon_gurl.ReplaceComponents(replacement).spec();
442 TEST_F(PasswordStoreMacInternalsTest, TestKeychainExactSearch) {
443 MacKeychainPasswordFormAdapter keychain_adapter(keychain_);
445 PasswordFormData base_form_data[] = {
446 { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
447 "http://some.domain.com/insecure.html",
448 NULL, NULL, NULL, NULL, L"joe_user", NULL, true, false, 0 },
449 { PasswordForm::SCHEME_BASIC, "http://some.domain.com:4567/low_security",
450 "http://some.domain.com:4567/insecure.html",
451 NULL, NULL, NULL, NULL, L"basic_auth_user", NULL, true, false, 0 },
452 { PasswordForm::SCHEME_DIGEST, "https://some.domain.com/high_security",
453 "https://some.domain.com",
454 NULL, NULL, NULL, NULL, L"digest_auth_user", NULL, true, true, 0 },
457 for (unsigned int i = 0; i < arraysize(base_form_data); ++i) {
458 // Create a base form and make sure we find a match.
459 scoped_ptr<PasswordForm> base_form(CreatePasswordFormFromData(
461 EXPECT_TRUE(keychain_adapter.HasPasswordsMergeableWithForm(*base_form));
462 PasswordForm* match =
463 keychain_adapter.PasswordExactlyMatchingForm(*base_form);
464 EXPECT_TRUE(match != NULL);
466 EXPECT_EQ(base_form->scheme, match->scheme);
467 EXPECT_EQ(base_form->origin, match->origin);
468 EXPECT_EQ(base_form->username_value, match->username_value);
472 // Make sure that the matching isn't looser than it should be by checking
473 // that slightly altered forms don't match.
474 std::vector<PasswordForm*> modified_forms;
476 modified_forms.push_back(new PasswordForm(*base_form));
477 modified_forms.back()->username_value = ASCIIToUTF16("wrong_user");
479 modified_forms.push_back(new PasswordForm(*base_form));
480 SetPasswordFormPath(modified_forms.back(), "elsewhere.html");
482 modified_forms.push_back(new PasswordForm(*base_form));
483 modified_forms.back()->scheme = PasswordForm::SCHEME_OTHER;
485 modified_forms.push_back(new PasswordForm(*base_form));
486 SetPasswordFormPort(modified_forms.back(), "1234");
488 modified_forms.push_back(new PasswordForm(*base_form));
489 modified_forms.back()->blacklisted_by_user = true;
491 if (base_form->scheme == PasswordForm::SCHEME_BASIC ||
492 base_form->scheme == PasswordForm::SCHEME_DIGEST) {
493 modified_forms.push_back(new PasswordForm(*base_form));
494 SetPasswordFormRealm(modified_forms.back(), "incorrect");
497 for (unsigned int j = 0; j < modified_forms.size(); ++j) {
498 PasswordForm* match =
499 keychain_adapter.PasswordExactlyMatchingForm(*modified_forms[j]);
500 EXPECT_EQ(NULL, match) << "In modified version " << j << " of base form "
503 STLDeleteElements(&modified_forms);
507 TEST_F(PasswordStoreMacInternalsTest, TestKeychainAdd) {
508 struct TestDataAndExpectation {
509 PasswordFormData data;
512 TestDataAndExpectation test_data[] = {
513 // Test a variety of scheme/port/protocol/path variations.
514 { { PasswordForm::SCHEME_HTML, "http://web.site.com/",
515 "http://web.site.com/path/to/page.html", NULL, NULL, NULL, NULL,
516 L"anonymous", L"knock-knock", false, false, 0 }, true },
517 { { PasswordForm::SCHEME_HTML, "https://web.site.com/",
518 "https://web.site.com/", NULL, NULL, NULL, NULL,
519 L"admin", L"p4ssw0rd", false, false, 0 }, true },
520 { { PasswordForm::SCHEME_BASIC, "http://a.site.com:2222/therealm",
521 "http://a.site.com:2222/", NULL, NULL, NULL, NULL,
522 L"username", L"password", false, false, 0 }, true },
523 { { PasswordForm::SCHEME_DIGEST, "https://digest.site.com/differentrealm",
524 "https://digest.site.com/secure.html", NULL, NULL, NULL, NULL,
525 L"testname", L"testpass", false, false, 0 }, true },
526 // Make sure that garbage forms are rejected.
527 { { PasswordForm::SCHEME_HTML, "gobbledygook",
528 "gobbledygook", NULL, NULL, NULL, NULL,
529 L"anonymous", L"knock-knock", false, false, 0 }, false },
530 // Test that failing to update a duplicate (forced using the magic failure
531 // password; see MockAppleKeychain::ItemModifyAttributesAndData) is
533 { { PasswordForm::SCHEME_HTML, "http://some.domain.com",
534 "http://some.domain.com/insecure.html", NULL, NULL, NULL, NULL,
535 L"joe_user", L"fail_me", false, false, 0 }, false },
538 MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_);
539 owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
541 for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
542 scoped_ptr<PasswordForm> in_form(
543 CreatePasswordFormFromData(test_data[i].data));
544 bool add_succeeded = owned_keychain_adapter.AddPassword(*in_form);
545 EXPECT_EQ(test_data[i].should_succeed, add_succeeded);
547 EXPECT_TRUE(owned_keychain_adapter.HasPasswordsMergeableWithForm(
549 scoped_ptr<PasswordForm> out_form(
550 owned_keychain_adapter.PasswordExactlyMatchingForm(*in_form));
551 EXPECT_TRUE(out_form.get() != NULL);
552 EXPECT_EQ(out_form->scheme, in_form->scheme);
553 EXPECT_EQ(out_form->signon_realm, in_form->signon_realm);
554 EXPECT_EQ(out_form->origin, in_form->origin);
555 EXPECT_EQ(out_form->username_value, in_form->username_value);
556 EXPECT_EQ(out_form->password_value, in_form->password_value);
560 // Test that adding duplicate item updates the existing item.
562 PasswordFormData data = {
563 PasswordForm::SCHEME_HTML, "http://some.domain.com",
564 "http://some.domain.com/insecure.html", NULL,
565 NULL, NULL, NULL, L"joe_user", L"updated_password", false, false, 0
567 scoped_ptr<PasswordForm> update_form(CreatePasswordFormFromData(data));
568 MacKeychainPasswordFormAdapter keychain_adapter(keychain_);
569 EXPECT_TRUE(keychain_adapter.AddPassword(*update_form));
570 SecKeychainItemRef keychain_item = reinterpret_cast<SecKeychainItemRef>(2);
571 PasswordForm stored_form;
572 internal_keychain_helpers::FillPasswordFormFromKeychainItem(*keychain_,
576 EXPECT_EQ(update_form->password_value, stored_form.password_value);
580 TEST_F(PasswordStoreMacInternalsTest, TestKeychainRemove) {
581 struct TestDataAndExpectation {
582 PasswordFormData data;
585 TestDataAndExpectation test_data[] = {
586 // Test deletion of an item that we add.
587 { { PasswordForm::SCHEME_HTML, "http://web.site.com/",
588 "http://web.site.com/path/to/page.html", NULL, NULL, NULL, NULL,
589 L"anonymous", L"knock-knock", false, false, 0 }, true },
590 // Make sure we don't delete items we don't own.
591 { { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
592 "http://some.domain.com/insecure.html", NULL, NULL, NULL, NULL,
593 L"joe_user", NULL, true, false, 0 }, false },
596 MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_);
597 owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
599 // Add our test item so that we can delete it.
600 PasswordForm* add_form = CreatePasswordFormFromData(test_data[0].data);
601 EXPECT_TRUE(owned_keychain_adapter.AddPassword(*add_form));
604 for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
605 scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(
607 EXPECT_EQ(test_data[i].should_succeed,
608 owned_keychain_adapter.RemovePassword(*form));
610 MacKeychainPasswordFormAdapter keychain_adapter(keychain_);
611 PasswordForm* match = keychain_adapter.PasswordExactlyMatchingForm(*form);
612 EXPECT_EQ(test_data[i].should_succeed, match == NULL);
619 TEST_F(PasswordStoreMacInternalsTest, TestFormMatch) {
620 PasswordForm base_form;
621 base_form.signon_realm = std::string("http://some.domain.com/");
622 base_form.origin = GURL("http://some.domain.com/page.html");
623 base_form.username_value = ASCIIToUTF16("joe_user");
626 // Check that everything unimportant can be changed.
627 PasswordForm different_form(base_form);
628 different_form.username_element = ASCIIToUTF16("username");
629 different_form.submit_element = ASCIIToUTF16("submit");
630 different_form.username_element = ASCIIToUTF16("password");
631 different_form.password_value = ASCIIToUTF16("sekrit");
632 different_form.action = GURL("http://some.domain.com/action.cgi");
633 different_form.ssl_valid = true;
634 different_form.preferred = true;
635 different_form.date_created = base::Time::Now();
636 EXPECT_TRUE(internal_keychain_helpers::FormsMatchForMerge(base_form,
639 // Check that path differences don't prevent a match.
640 base_form.origin = GURL("http://some.domain.com/other_page.html");
641 EXPECT_TRUE(internal_keychain_helpers::FormsMatchForMerge(base_form,
645 // Check that any one primary key changing is enough to prevent matching.
647 PasswordForm different_form(base_form);
648 different_form.scheme = PasswordForm::SCHEME_DIGEST;
649 EXPECT_FALSE(internal_keychain_helpers::FormsMatchForMerge(base_form,
653 PasswordForm different_form(base_form);
654 different_form.signon_realm = std::string("http://some.domain.com:8080/");
655 EXPECT_FALSE(internal_keychain_helpers::FormsMatchForMerge(base_form,
659 PasswordForm different_form(base_form);
660 different_form.username_value = ASCIIToUTF16("john.doe");
661 EXPECT_FALSE(internal_keychain_helpers::FormsMatchForMerge(base_form,
665 PasswordForm different_form(base_form);
666 different_form.blacklisted_by_user = true;
667 EXPECT_FALSE(internal_keychain_helpers::FormsMatchForMerge(base_form,
671 // Blacklist forms should *never* match for merging, even when identical
672 // (and certainly not when only one is a blacklist entry).
674 PasswordForm form_a(base_form);
675 form_a.blacklisted_by_user = true;
676 PasswordForm form_b(form_a);
677 EXPECT_FALSE(internal_keychain_helpers::FormsMatchForMerge(form_a, form_b));
681 TEST_F(PasswordStoreMacInternalsTest, TestFormMerge) {
682 // Set up a bunch of test data to use in varying combinations.
683 PasswordFormData keychain_user_1 =
684 { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
685 "http://some.domain.com/", "", L"", L"", L"", L"joe_user", L"sekrit",
686 false, false, 1010101010 };
687 PasswordFormData keychain_user_1_with_path =
688 { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
689 "http://some.domain.com/page.html",
690 "", L"", L"", L"", L"joe_user", L"otherpassword",
691 false, false, 1010101010 };
692 PasswordFormData keychain_user_2 =
693 { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
694 "http://some.domain.com/", "", L"", L"", L"", L"john.doe", L"sesame",
695 false, false, 958739876 };
696 PasswordFormData keychain_blacklist =
697 { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
698 "http://some.domain.com/", "", L"", L"", L"", NULL, NULL,
699 false, false, 1010101010 };
701 PasswordFormData db_user_1 =
702 { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
703 "http://some.domain.com/", "http://some.domain.com/action.cgi",
704 L"submit", L"username", L"password", L"joe_user", L"",
705 true, false, 1212121212 };
706 PasswordFormData db_user_1_with_path =
707 { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
708 "http://some.domain.com/page.html",
709 "http://some.domain.com/handlepage.cgi",
710 L"submit", L"username", L"password", L"joe_user", L"",
711 true, false, 1234567890 };
712 PasswordFormData db_user_3_with_path =
713 { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
714 "http://some.domain.com/page.html",
715 "http://some.domain.com/handlepage.cgi",
716 L"submit", L"username", L"password", L"second-account", L"",
717 true, false, 1240000000 };
718 PasswordFormData database_blacklist_with_path =
719 { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
720 "http://some.domain.com/path.html", "http://some.domain.com/action.cgi",
721 L"submit", L"username", L"password", NULL, NULL,
722 true, false, 1212121212 };
724 PasswordFormData merged_user_1 =
725 { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
726 "http://some.domain.com/", "http://some.domain.com/action.cgi",
727 L"submit", L"username", L"password", L"joe_user", L"sekrit",
728 true, false, 1212121212 };
729 PasswordFormData merged_user_1_with_db_path =
730 { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
731 "http://some.domain.com/page.html",
732 "http://some.domain.com/handlepage.cgi",
733 L"submit", L"username", L"password", L"joe_user", L"sekrit",
734 true, false, 1234567890 };
735 PasswordFormData merged_user_1_with_both_paths =
736 { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
737 "http://some.domain.com/page.html",
738 "http://some.domain.com/handlepage.cgi",
739 L"submit", L"username", L"password", L"joe_user", L"otherpassword",
740 true, false, 1234567890 };
742 // Build up the big multi-dimensional array of data sets that will actually
743 // drive the test. Use vectors rather than arrays so that initialization is
751 MERGE_IO_ARRAY_COUNT // termination marker
753 const unsigned int kTestCount = 4;
754 std::vector< std::vector< std::vector<PasswordFormData*> > > test_data(
755 MERGE_IO_ARRAY_COUNT, std::vector< std::vector<PasswordFormData*> >(
756 kTestCount, std::vector<PasswordFormData*>()));
757 unsigned int current_test = 0;
759 // Test a merge with a few accounts in both systems, with partial overlap.
760 CHECK(current_test < kTestCount);
761 test_data[KEYCHAIN_INPUT][current_test].push_back(&keychain_user_1);
762 test_data[KEYCHAIN_INPUT][current_test].push_back(&keychain_user_2);
763 test_data[DATABASE_INPUT][current_test].push_back(&db_user_1);
764 test_data[DATABASE_INPUT][current_test].push_back(&db_user_1_with_path);
765 test_data[DATABASE_INPUT][current_test].push_back(&db_user_3_with_path);
766 test_data[MERGE_OUTPUT][current_test].push_back(&merged_user_1);
767 test_data[MERGE_OUTPUT][current_test].push_back(&merged_user_1_with_db_path);
768 test_data[KEYCHAIN_OUTPUT][current_test].push_back(&keychain_user_2);
769 test_data[DATABASE_OUTPUT][current_test].push_back(&db_user_3_with_path);
771 // Test a merge where Chrome has a blacklist entry, and the keychain has
774 CHECK(current_test < kTestCount);
775 test_data[KEYCHAIN_INPUT][current_test].push_back(&keychain_user_1);
776 test_data[DATABASE_INPUT][current_test].push_back(
777 &database_blacklist_with_path);
778 // We expect both to be present because a blacklist could be specific to a
779 // subpath, and we want access to the password on other paths.
780 test_data[MERGE_OUTPUT][current_test].push_back(
781 &database_blacklist_with_path);
782 test_data[KEYCHAIN_OUTPUT][current_test].push_back(&keychain_user_1);
784 // Test a merge where Chrome has an account, and Keychain has a blacklist
785 // (from another browser) and the Chrome password data.
787 CHECK(current_test < kTestCount);
788 test_data[KEYCHAIN_INPUT][current_test].push_back(&keychain_blacklist);
789 test_data[KEYCHAIN_INPUT][current_test].push_back(&keychain_user_1);
790 test_data[DATABASE_INPUT][current_test].push_back(&db_user_1);
791 test_data[MERGE_OUTPUT][current_test].push_back(&merged_user_1);
792 test_data[KEYCHAIN_OUTPUT][current_test].push_back(&keychain_blacklist);
794 // Test that matches are done using exact path when possible.
796 CHECK(current_test < kTestCount);
797 test_data[KEYCHAIN_INPUT][current_test].push_back(&keychain_user_1);
798 test_data[KEYCHAIN_INPUT][current_test].push_back(&keychain_user_1_with_path);
799 test_data[DATABASE_INPUT][current_test].push_back(&db_user_1);
800 test_data[DATABASE_INPUT][current_test].push_back(&db_user_1_with_path);
801 test_data[MERGE_OUTPUT][current_test].push_back(&merged_user_1);
802 test_data[MERGE_OUTPUT][current_test].push_back(
803 &merged_user_1_with_both_paths);
805 for (unsigned int test_case = 0; test_case <= current_test; ++test_case) {
806 std::vector<PasswordForm*> keychain_forms;
807 for (std::vector<PasswordFormData*>::iterator i =
808 test_data[KEYCHAIN_INPUT][test_case].begin();
809 i != test_data[KEYCHAIN_INPUT][test_case].end(); ++i) {
810 keychain_forms.push_back(CreatePasswordFormFromData(*(*i)));
812 std::vector<PasswordForm*> database_forms;
813 for (std::vector<PasswordFormData*>::iterator i =
814 test_data[DATABASE_INPUT][test_case].begin();
815 i != test_data[DATABASE_INPUT][test_case].end(); ++i) {
816 database_forms.push_back(CreatePasswordFormFromData(*(*i)));
819 std::vector<PasswordForm*> merged_forms;
820 internal_keychain_helpers::MergePasswordForms(&keychain_forms,
824 CHECK_FORMS(keychain_forms, test_data[KEYCHAIN_OUTPUT][test_case],
826 CHECK_FORMS(database_forms, test_data[DATABASE_OUTPUT][test_case],
828 CHECK_FORMS(merged_forms, test_data[MERGE_OUTPUT][test_case], test_case);
830 STLDeleteElements(&keychain_forms);
831 STLDeleteElements(&database_forms);
832 STLDeleteElements(&merged_forms);
836 TEST_F(PasswordStoreMacInternalsTest, TestPasswordBulkLookup) {
837 PasswordFormData db_data[] = {
838 { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
839 "http://some.domain.com/", "http://some.domain.com/action.cgi",
840 L"submit", L"username", L"password", L"joe_user", L"",
841 true, false, 1212121212 },
842 { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
843 "http://some.domain.com/page.html",
844 "http://some.domain.com/handlepage.cgi",
845 L"submit", L"username", L"password", L"joe_user", L"",
846 true, false, 1234567890 },
847 { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
848 "http://some.domain.com/page.html",
849 "http://some.domain.com/handlepage.cgi",
850 L"submit", L"username", L"password", L"second-account", L"",
851 true, false, 1240000000 },
852 { PasswordForm::SCHEME_HTML, "http://dont.remember.com/",
853 "http://dont.remember.com/",
854 "http://dont.remember.com/handlepage.cgi",
855 L"submit", L"username", L"password", L"joe_user", L"",
856 true, false, 1240000000 },
857 { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
858 "http://some.domain.com/path.html", "http://some.domain.com/action.cgi",
859 L"submit", L"username", L"password", NULL, NULL,
860 true, false, 1212121212 },
862 std::vector<PasswordForm*> database_forms;
863 for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(db_data); ++i) {
864 database_forms.push_back(CreatePasswordFormFromData(db_data[i]));
866 std::vector<PasswordForm*> merged_forms =
867 internal_keychain_helpers::GetPasswordsForForms(*keychain_,
869 EXPECT_EQ(2U, database_forms.size());
870 ASSERT_EQ(3U, merged_forms.size());
871 EXPECT_EQ(ASCIIToUTF16("sekrit"), merged_forms[0]->password_value);
872 EXPECT_EQ(ASCIIToUTF16("sekrit"), merged_forms[1]->password_value);
873 EXPECT_TRUE(merged_forms[2]->blacklisted_by_user);
875 STLDeleteElements(&database_forms);
876 STLDeleteElements(&merged_forms);
879 TEST_F(PasswordStoreMacInternalsTest, TestBlacklistedFiltering) {
880 PasswordFormData db_data[] = {
881 { PasswordForm::SCHEME_HTML, "http://dont.remember.com/",
882 "http://dont.remember.com/",
883 "http://dont.remember.com/handlepage.cgi",
884 L"submit", L"username", L"password", L"joe_user", L"non_empty_password",
885 true, false, 1240000000 },
886 { PasswordForm::SCHEME_HTML, "https://dont.remember.com/",
887 "https://dont.remember.com/",
888 "https://dont.remember.com/handlepage_secure.cgi",
889 L"submit", L"username", L"password", L"joe_user", L"non_empty_password",
890 true, false, 1240000000 },
892 std::vector<PasswordForm*> database_forms;
893 for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(db_data); ++i) {
894 database_forms.push_back(CreatePasswordFormFromData(db_data[i]));
896 std::vector<PasswordForm*> merged_forms =
897 internal_keychain_helpers::GetPasswordsForForms(*keychain_,
899 EXPECT_EQ(2U, database_forms.size());
900 ASSERT_EQ(0U, merged_forms.size());
902 STLDeleteElements(&database_forms);
903 STLDeleteElements(&merged_forms);
906 TEST_F(PasswordStoreMacInternalsTest, TestFillPasswordFormFromKeychainItem) {
907 // When |extract_password_data| is false, the password field must be empty,
908 // and |blacklisted_by_user| must be false.
909 SecKeychainItemRef keychain_item = reinterpret_cast<SecKeychainItemRef>(1);
910 PasswordForm form_without_extracted_password;
911 bool parsed = internal_keychain_helpers::FillPasswordFormFromKeychainItem(
914 &form_without_extracted_password,
915 false); // Do not extract password.
917 ASSERT_TRUE(form_without_extracted_password.password_value.empty());
918 ASSERT_FALSE(form_without_extracted_password.blacklisted_by_user);
920 // When |extract_password_data| is true and the keychain entry has a non-empty
921 // password, the password field must be non-empty, and the value of
922 // |blacklisted_by_user| must be false.
923 keychain_item = reinterpret_cast<SecKeychainItemRef>(1);
924 PasswordForm form_with_extracted_password;
925 parsed = internal_keychain_helpers::FillPasswordFormFromKeychainItem(
928 &form_with_extracted_password,
929 true); // Extract password.
931 ASSERT_EQ(ASCIIToUTF16("sekrit"),
932 form_with_extracted_password.password_value);
933 ASSERT_FALSE(form_with_extracted_password.blacklisted_by_user);
935 // When |extract_password_data| is true and the keychain entry has an empty
936 // username and password (""), the password field must be empty, and the value
937 // of |blacklisted_by_user| must be true.
938 keychain_item = reinterpret_cast<SecKeychainItemRef>(4);
939 PasswordForm negative_form;
940 parsed = internal_keychain_helpers::FillPasswordFormFromKeychainItem(
944 true); // Extract password.
946 ASSERT_TRUE(negative_form.username_value.empty());
947 ASSERT_TRUE(negative_form.password_value.empty());
948 ASSERT_TRUE(negative_form.blacklisted_by_user);
950 // When |extract_password_data| is true and the keychain entry has an empty
951 // password (""), the password field must be empty (""), and the value of
952 // |blacklisted_by_user| must be true.
953 keychain_item = reinterpret_cast<SecKeychainItemRef>(5);
954 PasswordForm form_with_empty_password_a;
955 parsed = internal_keychain_helpers::FillPasswordFormFromKeychainItem(
958 &form_with_empty_password_a,
959 true); // Extract password.
961 ASSERT_TRUE(form_with_empty_password_a.password_value.empty());
962 ASSERT_TRUE(form_with_empty_password_a.blacklisted_by_user);
964 // When |extract_password_data| is true and the keychain entry has a single
965 // space password (" "), the password field must be a single space (" "), and
966 // the value of |blacklisted_by_user| must be true.
967 keychain_item = reinterpret_cast<SecKeychainItemRef>(6);
968 PasswordForm form_with_empty_password_b;
969 parsed = internal_keychain_helpers::FillPasswordFormFromKeychainItem(
972 &form_with_empty_password_b,
973 true); // Extract password.
975 ASSERT_EQ(ASCIIToUTF16(" "),
976 form_with_empty_password_b.password_value);
977 ASSERT_TRUE(form_with_empty_password_b.blacklisted_by_user);
980 TEST_F(PasswordStoreMacInternalsTest, TestPasswordGetAll) {
981 MacKeychainPasswordFormAdapter keychain_adapter(keychain_);
982 MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_);
983 owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
985 // Add a few passwords of various types so that we own some.
986 PasswordFormData owned_password_data[] = {
987 { PasswordForm::SCHEME_HTML, "http://web.site.com/",
988 "http://web.site.com/path/to/page.html", NULL, NULL, NULL, NULL,
989 L"anonymous", L"knock-knock", false, false, 0 },
990 { PasswordForm::SCHEME_BASIC, "http://a.site.com:2222/therealm",
991 "http://a.site.com:2222/", NULL, NULL, NULL, NULL,
992 L"username", L"password", false, false, 0 },
993 { PasswordForm::SCHEME_DIGEST, "https://digest.site.com/differentrealm",
994 "https://digest.site.com/secure.html", NULL, NULL, NULL, NULL,
995 L"testname", L"testpass", false, false, 0 },
997 for (unsigned int i = 0; i < arraysize(owned_password_data); ++i) {
998 scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(
999 owned_password_data[i]));
1000 owned_keychain_adapter.AddPassword(*form);
1003 std::vector<PasswordForm*> all_passwords =
1004 keychain_adapter.GetAllPasswordFormPasswords();
1005 EXPECT_EQ(8 + arraysize(owned_password_data), all_passwords.size());
1006 STLDeleteElements(&all_passwords);
1008 std::vector<PasswordForm*> owned_passwords =
1009 owned_keychain_adapter.GetAllPasswordFormPasswords();
1010 EXPECT_EQ(arraysize(owned_password_data), owned_passwords.size());
1011 STLDeleteElements(&owned_passwords);
1016 class PasswordStoreMacTest : public testing::Test {
1018 PasswordStoreMacTest() : ui_thread_(BrowserThread::UI, &message_loop_) {}
1020 virtual void SetUp() {
1021 login_db_ = new LoginDatabase();
1022 ASSERT_TRUE(db_dir_.CreateUniqueTempDir());
1023 base::FilePath db_file = db_dir_.path().AppendASCII("login.db");
1024 ASSERT_TRUE(login_db_->Init(db_file));
1026 keychain_ = new MockAppleKeychain();
1028 store_ = new PasswordStoreMac(keychain_, login_db_);
1029 ASSERT_TRUE(store_->Init());
1032 virtual void TearDown() {
1033 store_->ShutdownOnUIThread();
1034 base::MessageLoop::current()->PostTask(FROM_HERE,
1035 base::MessageLoop::QuitClosure());
1036 base::MessageLoop::current()->Run();
1040 base::MessageLoopForUI message_loop_;
1041 content::TestBrowserThread ui_thread_;
1043 MockAppleKeychain* keychain_; // Owned by store_.
1044 LoginDatabase* login_db_; // Owned by store_.
1045 scoped_refptr<PasswordStoreMac> store_;
1046 base::ScopedTempDir db_dir_;
1049 TEST_F(PasswordStoreMacTest, TestStoreUpdate) {
1050 // Insert a password into both the database and the keychain.
1051 // This is done manually, rather than through store_->AddLogin, because the
1052 // Mock Keychain isn't smart enough to be able to support update generically,
1053 // so some.domain.com triggers special handling to test it that make inserting
1055 PasswordFormData joint_data = {
1056 PasswordForm::SCHEME_HTML, "http://some.domain.com/",
1057 "http://some.domain.com/insecure.html", "login.cgi",
1058 L"username", L"password", L"submit", L"joe_user", L"sekrit", true, false, 1
1060 scoped_ptr<PasswordForm> joint_form(CreatePasswordFormFromData(joint_data));
1061 login_db_->AddLogin(*joint_form);
1062 MockAppleKeychain::KeychainTestData joint_keychain_data = {
1063 kSecAuthenticationTypeHTMLForm, "some.domain.com",
1064 kSecProtocolTypeHTTP, "/insecure.html", 0, NULL, "20020601171500Z",
1065 "joe_user", "sekrit", false };
1066 keychain_->AddTestItem(joint_keychain_data);
1068 // Insert a password into the keychain only.
1069 MockAppleKeychain::KeychainTestData keychain_only_data = {
1070 kSecAuthenticationTypeHTMLForm, "keychain.only.com",
1071 kSecProtocolTypeHTTP, NULL, 0, NULL, "20020601171500Z",
1072 "keychain", "only", false
1074 keychain_->AddTestItem(keychain_only_data);
1077 PasswordFormData form_data;
1078 const char* password; // NULL indicates no entry should be present.
1081 // Make a series of update calls.
1082 UpdateData updates[] = {
1083 // Update the keychain+db passwords (the normal password update case).
1084 { { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
1085 "http://some.domain.com/insecure.html", "login.cgi",
1086 L"username", L"password", L"submit", L"joe_user", L"53krit",
1090 // Update the keychain-only password; this simulates the initial use of a
1091 // password stored by another browsers.
1092 { { PasswordForm::SCHEME_HTML, "http://keychain.only.com/",
1093 "http://keychain.only.com/login.html", "login.cgi",
1094 L"username", L"password", L"submit", L"keychain", L"only",
1098 // Update a password that doesn't exist in either location. This tests the
1099 // case where a form is filled, then the stored login is removed, then the
1100 // form is submitted.
1101 { { PasswordForm::SCHEME_HTML, "http://different.com/",
1102 "http://different.com/index.html", "login.cgi",
1103 L"username", L"password", L"submit", L"abc", L"123",
1108 for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(updates); ++i) {
1109 scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(
1110 updates[i].form_data));
1111 store_->UpdateLogin(*form);
1114 // Do a store-level query to wait for all the operations above to be done.
1115 MockPasswordStoreConsumer consumer;
1116 ON_CALL(consumer, OnGetPasswordStoreResults(_)).WillByDefault(
1117 QuitUIMessageLoop());
1118 EXPECT_CALL(consumer, OnGetPasswordStoreResults(_)).WillOnce(
1119 DoAll(WithArg<0>(STLDeleteElements0()), QuitUIMessageLoop()));
1120 store_->GetLogins(*joint_form, &consumer);
1121 base::MessageLoop::current()->Run();
1123 MacKeychainPasswordFormAdapter keychain_adapter(keychain_);
1124 for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(updates); ++i) {
1125 scoped_ptr<PasswordForm> query_form(
1126 CreatePasswordFormFromData(updates[i].form_data));
1128 std::vector<PasswordForm*> matching_items =
1129 keychain_adapter.PasswordsFillingForm(*query_form);
1130 if (updates[i].password) {
1131 EXPECT_GT(matching_items.size(), 0U) << "iteration " << i;
1132 if (matching_items.size() >= 1)
1133 EXPECT_EQ(ASCIIToUTF16(updates[i].password),
1134 matching_items[0]->password_value) << "iteration " << i;
1136 EXPECT_EQ(0U, matching_items.size()) << "iteration " << i;
1138 STLDeleteElements(&matching_items);
1140 login_db_->GetLogins(*query_form, &matching_items);
1141 EXPECT_EQ(updates[i].password ? 1U : 0U, matching_items.size())
1142 << "iteration " << i;
1143 STLDeleteElements(&matching_items);