Upstream version 8.37.180.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / password_manager / password_store_mac_unittest.cc
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.
4
5 #include "testing/gmock/include/gmock/gmock.h"
6 #include "testing/gtest/include/gtest/gtest.h"
7
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_mac.h"
15 #include "chrome/browser/password_manager/password_store_mac_internal.h"
16 #include "chrome/common/chrome_paths.h"
17 #include "components/password_manager/core/browser/password_store_consumer.h"
18 #include "content/public/test/test_browser_thread.h"
19 #include "crypto/mock_apple_keychain.h"
20
21 using autofill::PasswordForm;
22 using base::ASCIIToUTF16;
23 using base::WideToUTF16;
24 using content::BrowserThread;
25 using crypto::MockAppleKeychain;
26 using internal_keychain_helpers::FormsMatchForMerge;
27 using internal_keychain_helpers::STRICT_FORM_MATCH;
28 using password_manager::LoginDatabase;
29 using password_manager::PasswordStore;
30 using password_manager::PasswordStoreConsumer;
31 using testing::_;
32 using testing::DoAll;
33 using testing::Invoke;
34 using testing::WithArg;
35
36 namespace {
37
38 class MockPasswordStoreConsumer : public PasswordStoreConsumer {
39  public:
40   MOCK_METHOD1(OnGetPasswordStoreResults,
41                void(const std::vector<autofill::PasswordForm*>&));
42
43   void CopyElements(const std::vector<autofill::PasswordForm*>& forms) {
44     last_result.clear();
45     for (size_t i = 0; i < forms.size(); ++i) {
46       last_result.push_back(*forms[i]);
47     }
48   }
49
50   std::vector<PasswordForm> last_result;
51 };
52
53 ACTION(STLDeleteElements0) {
54   STLDeleteContainerPointers(arg0.begin(), arg0.end());
55 }
56
57 ACTION(QuitUIMessageLoop) {
58   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
59   base::MessageLoop::current()->Quit();
60 }
61
62 class TestPasswordStoreMac : public PasswordStoreMac {
63  public:
64   TestPasswordStoreMac(
65       scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner,
66       scoped_refptr<base::SingleThreadTaskRunner> db_thread_runner,
67       crypto::AppleKeychain* keychain,
68       LoginDatabase* login_db)
69       : PasswordStoreMac(main_thread_runner,
70                          db_thread_runner,
71                          keychain,
72                          login_db) {
73   }
74
75   using PasswordStoreMac::GetBackgroundTaskRunner;
76
77  private:
78   virtual ~TestPasswordStoreMac() {}
79
80   DISALLOW_COPY_AND_ASSIGN(TestPasswordStoreMac);
81 };
82
83 }  // namespace
84
85 #pragma mark -
86
87 class PasswordStoreMacInternalsTest : public testing::Test {
88  public:
89   virtual void SetUp() {
90     MockAppleKeychain::KeychainTestData test_data[] = {
91       // Basic HTML form.
92       { kSecAuthenticationTypeHTMLForm, "some.domain.com",
93         kSecProtocolTypeHTTP, NULL, 0, NULL, "20020601171500Z",
94         "joe_user", "sekrit", false },
95       // HTML form with path.
96       { kSecAuthenticationTypeHTMLForm, "some.domain.com",
97         kSecProtocolTypeHTTP, "/insecure.html", 0, NULL, "19991231235959Z",
98         "joe_user", "sekrit", false },
99       // Secure HTML form with path.
100       { kSecAuthenticationTypeHTMLForm, "some.domain.com",
101         kSecProtocolTypeHTTPS, "/secure.html", 0, NULL, "20100908070605Z",
102         "secure_user", "password", false },
103       // True negative item.
104       { kSecAuthenticationTypeHTMLForm, "dont.remember.com",
105         kSecProtocolTypeHTTP, NULL, 0, NULL, "20000101000000Z",
106         "", "", true },
107       // De-facto negative item, type one.
108       { kSecAuthenticationTypeHTMLForm, "dont.remember.com",
109         kSecProtocolTypeHTTP, NULL, 0, NULL, "20000101000000Z",
110         "Password Not Stored", "", false },
111       // De-facto negative item, type two.
112       { kSecAuthenticationTypeHTMLForm, "dont.remember.com",
113         kSecProtocolTypeHTTPS, NULL, 0, NULL, "20000101000000Z",
114         "Password Not Stored", " ", false },
115       // HTTP auth basic, with port and path.
116       { kSecAuthenticationTypeHTTPBasic, "some.domain.com",
117         kSecProtocolTypeHTTP, "/insecure.html", 4567, "low_security",
118         "19980330100000Z",
119         "basic_auth_user", "basic", false },
120       // HTTP auth digest, secure.
121       { kSecAuthenticationTypeHTTPDigest, "some.domain.com",
122         kSecProtocolTypeHTTPS, NULL, 0, "high_security", "19980330100000Z",
123         "digest_auth_user", "digest", false },
124       // An FTP password with an invalid date, for edge-case testing.
125       { kSecAuthenticationTypeDefault, "a.server.com",
126         kSecProtocolTypeFTP, NULL, 0, NULL, "20010203040",
127         "abc", "123", false },
128     };
129
130     keychain_ = new MockAppleKeychain();
131
132     for (unsigned int i = 0; i < arraysize(test_data); ++i) {
133       keychain_->AddTestItem(test_data[i]);
134     }
135   }
136
137   virtual void TearDown() {
138     ExpectCreatesAndFreesBalanced();
139     ExpectCreatorCodesSet();
140     delete keychain_;
141   }
142
143  protected:
144   // Causes a test failure unless everything returned from keychain_'s
145   // ItemCopyAttributesAndData, SearchCreateFromAttributes, and SearchCopyNext
146   // was correctly freed.
147   void ExpectCreatesAndFreesBalanced() {
148     EXPECT_EQ(0, keychain_->UnfreedSearchCount());
149     EXPECT_EQ(0, keychain_->UnfreedKeychainItemCount());
150     EXPECT_EQ(0, keychain_->UnfreedAttributeDataCount());
151   }
152
153   // Causes a test failure unless any Keychain items added during the test have
154   // their creator code set.
155   void ExpectCreatorCodesSet() {
156     EXPECT_TRUE(keychain_->CreatorCodesSetForAddedItems());
157   }
158
159   MockAppleKeychain* keychain_;
160 };
161
162 #pragma mark -
163
164 // Struct used for creation of PasswordForms from static arrays of data.
165 struct PasswordFormData {
166   const PasswordForm::Scheme scheme;
167   const char* signon_realm;
168   const char* origin;
169   const char* action;
170   const wchar_t* submit_element;
171   const wchar_t* username_element;
172   const wchar_t* password_element;
173   const wchar_t* username_value;  // Set to NULL for a blacklist entry.
174   const wchar_t* password_value;
175   const bool preferred;
176   const bool ssl_valid;
177   const double creation_time;
178 };
179
180 // Creates and returns a new PasswordForm built from form_data. Caller is
181 // responsible for deleting the object when finished with it.
182 static PasswordForm* CreatePasswordFormFromData(
183     const PasswordFormData& form_data) {
184   PasswordForm* form = new PasswordForm();
185   form->scheme = form_data.scheme;
186   form->preferred = form_data.preferred;
187   form->ssl_valid = form_data.ssl_valid;
188   form->date_created = base::Time::FromDoubleT(form_data.creation_time);
189   form->date_synced = form->date_created + base::TimeDelta::FromDays(1);
190   if (form_data.signon_realm)
191     form->signon_realm = std::string(form_data.signon_realm);
192   if (form_data.origin)
193     form->origin = GURL(form_data.origin);
194   if (form_data.action)
195     form->action = GURL(form_data.action);
196   if (form_data.submit_element)
197     form->submit_element = WideToUTF16(form_data.submit_element);
198   if (form_data.username_element)
199     form->username_element = WideToUTF16(form_data.username_element);
200   if (form_data.password_element)
201     form->password_element = WideToUTF16(form_data.password_element);
202   if (form_data.username_value) {
203     form->username_value = WideToUTF16(form_data.username_value);
204     if (form_data.password_value)
205       form->password_value = WideToUTF16(form_data.password_value);
206   } else {
207     form->blacklisted_by_user = true;
208   }
209   return form;
210 }
211
212 // Macro to simplify calling CheckFormsAgainstExpectations with a useful label.
213 #define CHECK_FORMS(forms, expectations, i) \
214     CheckFormsAgainstExpectations(forms, expectations, #forms, i)
215
216 // Ensures that the data in |forms| match |expectations|, causing test failures
217 // for any discrepencies.
218 // TODO(stuartmorgan): This is current order-dependent; ideally it shouldn't
219 // matter if |forms| and |expectations| are scrambled.
220 static void CheckFormsAgainstExpectations(
221     const std::vector<PasswordForm*>& forms,
222     const std::vector<PasswordFormData*>& expectations,
223     const char* forms_label, unsigned int test_number) {
224   const unsigned int kBufferSize = 128;
225   char test_label[kBufferSize];
226   snprintf(test_label, kBufferSize, "%s in test %u", forms_label, test_number);
227
228   EXPECT_EQ(expectations.size(), forms.size()) << test_label;
229   if (expectations.size() != forms.size())
230     return;
231
232   for (unsigned int i = 0; i < expectations.size(); ++i) {
233     snprintf(test_label, kBufferSize, "%s in test %u, item %u",
234              forms_label, test_number, i);
235     PasswordForm* form = forms[i];
236     PasswordFormData* expectation = expectations[i];
237     EXPECT_EQ(expectation->scheme, form->scheme) << test_label;
238     EXPECT_EQ(std::string(expectation->signon_realm), form->signon_realm)
239         << test_label;
240     EXPECT_EQ(GURL(expectation->origin), form->origin) << test_label;
241     EXPECT_EQ(GURL(expectation->action), form->action) << test_label;
242     EXPECT_EQ(WideToUTF16(expectation->submit_element), form->submit_element)
243         << test_label;
244     EXPECT_EQ(WideToUTF16(expectation->username_element),
245               form->username_element) << test_label;
246     EXPECT_EQ(WideToUTF16(expectation->password_element),
247               form->password_element) << test_label;
248     if (expectation->username_value) {
249       EXPECT_EQ(WideToUTF16(expectation->username_value),
250                 form->username_value) << test_label;
251       EXPECT_EQ(WideToUTF16(expectation->password_value),
252                 form->password_value) << test_label;
253     } else {
254       EXPECT_TRUE(form->blacklisted_by_user) << test_label;
255     }
256     EXPECT_EQ(expectation->preferred, form->preferred)  << test_label;
257     EXPECT_EQ(expectation->ssl_valid, form->ssl_valid) << test_label;
258     EXPECT_DOUBLE_EQ(expectation->creation_time,
259                      form->date_created.ToDoubleT()) << test_label;
260     base::Time created = base::Time::FromDoubleT(expectation->creation_time);
261     EXPECT_EQ(created + base::TimeDelta::FromDays(1),
262               form->date_synced) << test_label;
263   }
264 }
265
266 #pragma mark -
267
268 TEST_F(PasswordStoreMacInternalsTest, TestKeychainToFormTranslation) {
269   typedef struct {
270     const PasswordForm::Scheme scheme;
271     const char* signon_realm;
272     const char* origin;
273     const wchar_t* username;  // Set to NULL to check for a blacklist entry.
274     const wchar_t* password;
275     const bool ssl_valid;
276     const int creation_year;
277     const int creation_month;
278     const int creation_day;
279     const int creation_hour;
280     const int creation_minute;
281     const int creation_second;
282   } TestExpectations;
283
284   TestExpectations expected[] = {
285     { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
286       "http://some.domain.com/", L"joe_user", L"sekrit", false,
287       2002,  6,  1, 17, 15,  0 },
288     { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
289       "http://some.domain.com/insecure.html", L"joe_user", L"sekrit", false,
290       1999, 12, 31, 23, 59, 59 },
291     { PasswordForm::SCHEME_HTML, "https://some.domain.com/",
292       "https://some.domain.com/secure.html", L"secure_user", L"password", true,
293       2010,  9,  8,  7,  6,  5 },
294     { PasswordForm::SCHEME_HTML, "http://dont.remember.com/",
295       "http://dont.remember.com/", NULL, NULL, false,
296       2000,  1,  1,  0,  0,  0 },
297     { PasswordForm::SCHEME_HTML, "http://dont.remember.com/",
298       "http://dont.remember.com/", NULL, NULL, false,
299       2000,  1,  1,  0,  0,  0 },
300     { PasswordForm::SCHEME_HTML, "https://dont.remember.com/",
301       "https://dont.remember.com/", NULL, NULL, true,
302       2000,  1,  1,  0,  0,  0 },
303     { PasswordForm::SCHEME_BASIC, "http://some.domain.com:4567/low_security",
304       "http://some.domain.com:4567/insecure.html", L"basic_auth_user", L"basic",
305       false, 1998, 03, 30, 10, 00, 00 },
306     { PasswordForm::SCHEME_DIGEST, "https://some.domain.com/high_security",
307       "https://some.domain.com/", L"digest_auth_user", L"digest", true,
308       1998,  3, 30, 10,  0,  0 },
309     // This one gives us an invalid date, which we will treat as a "NULL" date
310     // which is 1601.
311     { PasswordForm::SCHEME_OTHER, "http://a.server.com/",
312       "http://a.server.com/", L"abc", L"123", false,
313       1601,  1,  1,  0,  0,  0 },
314   };
315
316   for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(expected); ++i) {
317     // Create our fake KeychainItemRef; see MockAppleKeychain docs.
318     SecKeychainItemRef keychain_item =
319         reinterpret_cast<SecKeychainItemRef>(i + 1);
320     PasswordForm form;
321     bool parsed = internal_keychain_helpers::FillPasswordFormFromKeychainItem(
322         *keychain_, keychain_item, &form, true);
323
324     EXPECT_TRUE(parsed) << "In iteration " << i;
325
326     EXPECT_EQ(expected[i].scheme, form.scheme) << "In iteration " << i;
327     EXPECT_EQ(GURL(expected[i].origin), form.origin) << "In iteration " << i;
328     EXPECT_EQ(expected[i].ssl_valid, form.ssl_valid) << "In iteration " << i;
329     EXPECT_EQ(std::string(expected[i].signon_realm), form.signon_realm)
330         << "In iteration " << i;
331     if (expected[i].username) {
332       EXPECT_EQ(WideToUTF16(expected[i].username), form.username_value)
333           << "In iteration " << i;
334       EXPECT_EQ(WideToUTF16(expected[i].password), form.password_value)
335           << "In iteration " << i;
336       EXPECT_FALSE(form.blacklisted_by_user) << "In iteration " << i;
337     } else {
338       EXPECT_TRUE(form.blacklisted_by_user) << "In iteration " << i;
339     }
340     base::Time::Exploded exploded_time;
341     form.date_created.UTCExplode(&exploded_time);
342     EXPECT_EQ(expected[i].creation_year, exploded_time.year)
343          << "In iteration " << i;
344     EXPECT_EQ(expected[i].creation_month, exploded_time.month)
345         << "In iteration " << i;
346     EXPECT_EQ(expected[i].creation_day, exploded_time.day_of_month)
347         << "In iteration " << i;
348     EXPECT_EQ(expected[i].creation_hour, exploded_time.hour)
349         << "In iteration " << i;
350     EXPECT_EQ(expected[i].creation_minute, exploded_time.minute)
351         << "In iteration " << i;
352     EXPECT_EQ(expected[i].creation_second, exploded_time.second)
353         << "In iteration " << i;
354   }
355
356   {
357     // Use an invalid ref, to make sure errors are reported.
358     SecKeychainItemRef keychain_item = reinterpret_cast<SecKeychainItemRef>(99);
359     PasswordForm form;
360     bool parsed = internal_keychain_helpers::FillPasswordFormFromKeychainItem(
361         *keychain_, keychain_item, &form, true);
362     EXPECT_FALSE(parsed);
363   }
364 }
365
366 TEST_F(PasswordStoreMacInternalsTest, TestKeychainSearch) {
367   struct TestDataAndExpectation {
368     const PasswordFormData data;
369     const size_t expected_fill_matches;
370     const size_t expected_merge_matches;
371   };
372   // Most fields are left blank because we don't care about them for searching.
373   TestDataAndExpectation test_data[] = {
374     // An HTML form we've seen.
375     { { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
376         NULL, NULL, NULL, NULL, NULL, L"joe_user", NULL, false, false, 0 },
377       2, 2 },
378     { { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
379         NULL, NULL, NULL, NULL, NULL, L"wrong_user", NULL, false, false, 0 },
380       2, 0 },
381     // An HTML form we haven't seen
382     { { PasswordForm::SCHEME_HTML, "http://www.unseendomain.com/",
383         NULL, NULL, NULL, NULL, NULL, L"joe_user", NULL, false, false, 0 },
384       0, 0 },
385     // Basic auth that should match.
386     { { PasswordForm::SCHEME_BASIC, "http://some.domain.com:4567/low_security",
387         NULL, NULL, NULL, NULL, NULL, L"basic_auth_user", NULL, false, false,
388         0 },
389       1, 1 },
390     // Basic auth with the wrong port.
391     { { PasswordForm::SCHEME_BASIC, "http://some.domain.com:1111/low_security",
392         NULL, NULL, NULL, NULL, NULL, L"basic_auth_user", NULL, false, false,
393         0 },
394       0, 0 },
395     // Digest auth we've saved under https, visited with http.
396     { { PasswordForm::SCHEME_DIGEST, "http://some.domain.com/high_security",
397         NULL, NULL, NULL, NULL, NULL, L"digest_auth_user", NULL, false, false,
398         0 },
399       0, 0 },
400     // Digest auth that should match.
401     { { PasswordForm::SCHEME_DIGEST, "https://some.domain.com/high_security",
402         NULL, NULL, NULL, NULL, NULL, L"wrong_user", NULL, false, true, 0 },
403       1, 0 },
404     // Digest auth with the wrong domain.
405     { { PasswordForm::SCHEME_DIGEST, "https://some.domain.com/other_domain",
406         NULL, NULL, NULL, NULL, NULL, L"digest_auth_user", NULL, false, true,
407         0 },
408       0, 0 },
409     // Garbage forms should have no matches.
410     { { PasswordForm::SCHEME_HTML, "foo/bar/baz",
411         NULL, NULL, NULL, NULL, NULL, NULL, NULL, false, false, 0 }, 0, 0 },
412   };
413
414   MacKeychainPasswordFormAdapter keychain_adapter(keychain_);
415   MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_);
416   owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
417   for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
418     scoped_ptr<PasswordForm> query_form(
419         CreatePasswordFormFromData(test_data[i].data));
420
421     // Check matches treating the form as a fill target.
422     std::vector<PasswordForm*> matching_items =
423         keychain_adapter.PasswordsFillingForm(query_form->signon_realm,
424                                               query_form->scheme);
425     EXPECT_EQ(test_data[i].expected_fill_matches, matching_items.size());
426     STLDeleteElements(&matching_items);
427
428     // Check matches treating the form as a merging target.
429     EXPECT_EQ(test_data[i].expected_merge_matches > 0,
430               keychain_adapter.HasPasswordsMergeableWithForm(*query_form));
431     std::vector<SecKeychainItemRef> keychain_items;
432     std::vector<internal_keychain_helpers::ItemFormPair> item_form_pairs =
433         internal_keychain_helpers::
434             ExtractAllKeychainItemAttributesIntoPasswordForms(&keychain_items,
435                                                               *keychain_);
436     matching_items =
437         internal_keychain_helpers::ExtractPasswordsMergeableWithForm(
438             *keychain_, item_form_pairs, *query_form);
439     EXPECT_EQ(test_data[i].expected_merge_matches, matching_items.size());
440     STLDeleteContainerPairSecondPointers(item_form_pairs.begin(),
441                                          item_form_pairs.end());
442     for (std::vector<SecKeychainItemRef>::iterator i = keychain_items.begin();
443          i != keychain_items.end(); ++i) {
444       keychain_->Free(*i);
445     }
446     STLDeleteElements(&matching_items);
447
448     // None of the pre-seeded items are owned by us, so none should match an
449     // owned-passwords-only search.
450     matching_items = owned_keychain_adapter.PasswordsFillingForm(
451         query_form->signon_realm, query_form->scheme);
452     EXPECT_EQ(0U, matching_items.size());
453     STLDeleteElements(&matching_items);
454   }
455 }
456
457 // Changes just the origin path of |form|.
458 static void SetPasswordFormPath(PasswordForm* form, const char* path) {
459   GURL::Replacements replacement;
460   std::string new_value(path);
461   replacement.SetPathStr(new_value);
462   form->origin = form->origin.ReplaceComponents(replacement);
463 }
464
465 // Changes just the signon_realm port of |form|.
466 static void SetPasswordFormPort(PasswordForm* form, const char* port) {
467   GURL::Replacements replacement;
468   std::string new_value(port);
469   replacement.SetPortStr(new_value);
470   GURL signon_gurl = GURL(form->signon_realm);
471   form->signon_realm = signon_gurl.ReplaceComponents(replacement).spec();
472 }
473
474 // Changes just the signon_ream auth realm of |form|.
475 static void SetPasswordFormRealm(PasswordForm* form, const char* realm) {
476   GURL::Replacements replacement;
477   std::string new_value(realm);
478   replacement.SetPathStr(new_value);
479   GURL signon_gurl = GURL(form->signon_realm);
480   form->signon_realm = signon_gurl.ReplaceComponents(replacement).spec();
481 }
482
483 TEST_F(PasswordStoreMacInternalsTest, TestKeychainExactSearch) {
484   MacKeychainPasswordFormAdapter keychain_adapter(keychain_);
485
486   PasswordFormData base_form_data[] = {
487     { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
488       "http://some.domain.com/insecure.html",
489       NULL, NULL, NULL, NULL, L"joe_user", NULL, true, false, 0 },
490     { PasswordForm::SCHEME_BASIC, "http://some.domain.com:4567/low_security",
491       "http://some.domain.com:4567/insecure.html",
492       NULL, NULL, NULL, NULL, L"basic_auth_user", NULL, true, false, 0 },
493     { PasswordForm::SCHEME_DIGEST, "https://some.domain.com/high_security",
494       "https://some.domain.com",
495       NULL, NULL, NULL, NULL, L"digest_auth_user", NULL, true, true, 0 },
496   };
497
498   for (unsigned int i = 0; i < arraysize(base_form_data); ++i) {
499     // Create a base form and make sure we find a match.
500     scoped_ptr<PasswordForm> base_form(CreatePasswordFormFromData(
501         base_form_data[i]));
502     EXPECT_TRUE(keychain_adapter.HasPasswordsMergeableWithForm(*base_form));
503     PasswordForm* match =
504         keychain_adapter.PasswordExactlyMatchingForm(*base_form);
505     EXPECT_TRUE(match != NULL);
506     if (match) {
507       EXPECT_EQ(base_form->scheme, match->scheme);
508       EXPECT_EQ(base_form->origin, match->origin);
509       EXPECT_EQ(base_form->username_value, match->username_value);
510       delete match;
511     }
512
513     // Make sure that the matching isn't looser than it should be by checking
514     // that slightly altered forms don't match.
515     std::vector<PasswordForm*> modified_forms;
516
517     modified_forms.push_back(new PasswordForm(*base_form));
518     modified_forms.back()->username_value = ASCIIToUTF16("wrong_user");
519
520     modified_forms.push_back(new PasswordForm(*base_form));
521     SetPasswordFormPath(modified_forms.back(), "elsewhere.html");
522
523     modified_forms.push_back(new PasswordForm(*base_form));
524     modified_forms.back()->scheme = PasswordForm::SCHEME_OTHER;
525
526     modified_forms.push_back(new PasswordForm(*base_form));
527     SetPasswordFormPort(modified_forms.back(), "1234");
528
529     modified_forms.push_back(new PasswordForm(*base_form));
530     modified_forms.back()->blacklisted_by_user = true;
531
532     if (base_form->scheme == PasswordForm::SCHEME_BASIC ||
533         base_form->scheme == PasswordForm::SCHEME_DIGEST) {
534       modified_forms.push_back(new PasswordForm(*base_form));
535       SetPasswordFormRealm(modified_forms.back(), "incorrect");
536     }
537
538     for (unsigned int j = 0; j < modified_forms.size(); ++j) {
539       PasswordForm* match =
540           keychain_adapter.PasswordExactlyMatchingForm(*modified_forms[j]);
541       EXPECT_EQ(NULL, match) << "In modified version " << j << " of base form "
542                              << i;
543     }
544     STLDeleteElements(&modified_forms);
545   }
546 }
547
548 TEST_F(PasswordStoreMacInternalsTest, TestKeychainAdd) {
549   struct TestDataAndExpectation {
550     PasswordFormData data;
551     bool should_succeed;
552   };
553   TestDataAndExpectation test_data[] = {
554     // Test a variety of scheme/port/protocol/path variations.
555     { { PasswordForm::SCHEME_HTML, "http://web.site.com/",
556         "http://web.site.com/path/to/page.html", NULL, NULL, NULL, NULL,
557         L"anonymous", L"knock-knock", false, false, 0 }, true },
558     { { PasswordForm::SCHEME_HTML, "https://web.site.com/",
559         "https://web.site.com/", NULL, NULL, NULL, NULL,
560         L"admin", L"p4ssw0rd", false, false, 0 }, true },
561     { { PasswordForm::SCHEME_BASIC, "http://a.site.com:2222/therealm",
562         "http://a.site.com:2222/", NULL, NULL, NULL, NULL,
563         L"username", L"password", false, false, 0 }, true },
564     { { PasswordForm::SCHEME_DIGEST, "https://digest.site.com/differentrealm",
565         "https://digest.site.com/secure.html", NULL, NULL, NULL, NULL,
566         L"testname", L"testpass", false, false, 0 }, true },
567     // Make sure that garbage forms are rejected.
568     { { PasswordForm::SCHEME_HTML, "gobbledygook",
569         "gobbledygook", NULL, NULL, NULL, NULL,
570         L"anonymous", L"knock-knock", false, false, 0 }, false },
571     // Test that failing to update a duplicate (forced using the magic failure
572     // password; see MockAppleKeychain::ItemModifyAttributesAndData) is
573     // reported.
574     { { PasswordForm::SCHEME_HTML, "http://some.domain.com",
575         "http://some.domain.com/insecure.html", NULL, NULL, NULL, NULL,
576         L"joe_user", L"fail_me", false, false, 0 }, false },
577   };
578
579   MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_);
580   owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
581
582   for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
583     scoped_ptr<PasswordForm> in_form(
584         CreatePasswordFormFromData(test_data[i].data));
585     bool add_succeeded = owned_keychain_adapter.AddPassword(*in_form);
586     EXPECT_EQ(test_data[i].should_succeed, add_succeeded);
587     if (add_succeeded) {
588       EXPECT_TRUE(owned_keychain_adapter.HasPasswordsMergeableWithForm(
589           *in_form));
590       scoped_ptr<PasswordForm> out_form(
591           owned_keychain_adapter.PasswordExactlyMatchingForm(*in_form));
592       EXPECT_TRUE(out_form.get() != NULL);
593       EXPECT_EQ(out_form->scheme, in_form->scheme);
594       EXPECT_EQ(out_form->signon_realm, in_form->signon_realm);
595       EXPECT_EQ(out_form->origin, in_form->origin);
596       EXPECT_EQ(out_form->username_value, in_form->username_value);
597       EXPECT_EQ(out_form->password_value, in_form->password_value);
598     }
599   }
600
601   // Test that adding duplicate item updates the existing item.
602   {
603     PasswordFormData data = {
604       PasswordForm::SCHEME_HTML, "http://some.domain.com",
605       "http://some.domain.com/insecure.html", NULL,
606       NULL, NULL, NULL, L"joe_user", L"updated_password", false, false, 0
607     };
608     scoped_ptr<PasswordForm> update_form(CreatePasswordFormFromData(data));
609     MacKeychainPasswordFormAdapter keychain_adapter(keychain_);
610     EXPECT_TRUE(keychain_adapter.AddPassword(*update_form));
611     SecKeychainItemRef keychain_item = reinterpret_cast<SecKeychainItemRef>(2);
612     PasswordForm stored_form;
613     internal_keychain_helpers::FillPasswordFormFromKeychainItem(*keychain_,
614                                                                 keychain_item,
615                                                                 &stored_form,
616                                                                 true);
617     EXPECT_EQ(update_form->password_value, stored_form.password_value);
618   }
619 }
620
621 TEST_F(PasswordStoreMacInternalsTest, TestKeychainRemove) {
622   struct TestDataAndExpectation {
623     PasswordFormData data;
624     bool should_succeed;
625   };
626   TestDataAndExpectation test_data[] = {
627     // Test deletion of an item that we add.
628     { { PasswordForm::SCHEME_HTML, "http://web.site.com/",
629         "http://web.site.com/path/to/page.html", NULL, NULL, NULL, NULL,
630         L"anonymous", L"knock-knock", false, false, 0 }, true },
631     // Make sure we don't delete items we don't own.
632     { { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
633         "http://some.domain.com/insecure.html", NULL, NULL, NULL, NULL,
634         L"joe_user", NULL, true, false, 0 }, false },
635   };
636
637   MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_);
638   owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
639
640   // Add our test item so that we can delete it.
641   PasswordForm* add_form = CreatePasswordFormFromData(test_data[0].data);
642   EXPECT_TRUE(owned_keychain_adapter.AddPassword(*add_form));
643   delete add_form;
644
645   for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
646     scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(
647         test_data[i].data));
648     EXPECT_EQ(test_data[i].should_succeed,
649               owned_keychain_adapter.RemovePassword(*form));
650
651     MacKeychainPasswordFormAdapter keychain_adapter(keychain_);
652     PasswordForm* match = keychain_adapter.PasswordExactlyMatchingForm(*form);
653     EXPECT_EQ(test_data[i].should_succeed, match == NULL);
654     if (match) {
655       delete match;
656     }
657   }
658 }
659
660 TEST_F(PasswordStoreMacInternalsTest, TestFormMatch) {
661   PasswordForm base_form;
662   base_form.signon_realm = std::string("http://some.domain.com/");
663   base_form.origin = GURL("http://some.domain.com/page.html");
664   base_form.username_value = ASCIIToUTF16("joe_user");
665
666   {
667     // Check that everything unimportant can be changed.
668     PasswordForm different_form(base_form);
669     different_form.username_element = ASCIIToUTF16("username");
670     different_form.submit_element = ASCIIToUTF16("submit");
671     different_form.username_element = ASCIIToUTF16("password");
672     different_form.password_value = ASCIIToUTF16("sekrit");
673     different_form.action = GURL("http://some.domain.com/action.cgi");
674     different_form.ssl_valid = true;
675     different_form.preferred = true;
676     different_form.date_created = base::Time::Now();
677     EXPECT_TRUE(
678         FormsMatchForMerge(base_form, different_form, STRICT_FORM_MATCH));
679
680     // Check that path differences don't prevent a match.
681     base_form.origin = GURL("http://some.domain.com/other_page.html");
682     EXPECT_TRUE(
683         FormsMatchForMerge(base_form, different_form, STRICT_FORM_MATCH));
684   }
685
686   // Check that any one primary key changing is enough to prevent matching.
687   {
688     PasswordForm different_form(base_form);
689     different_form.scheme = PasswordForm::SCHEME_DIGEST;
690     EXPECT_FALSE(
691         FormsMatchForMerge(base_form, different_form, STRICT_FORM_MATCH));
692   }
693   {
694     PasswordForm different_form(base_form);
695     different_form.signon_realm = std::string("http://some.domain.com:8080/");
696     EXPECT_FALSE(
697         FormsMatchForMerge(base_form, different_form, STRICT_FORM_MATCH));
698   }
699   {
700     PasswordForm different_form(base_form);
701     different_form.username_value = ASCIIToUTF16("john.doe");
702     EXPECT_FALSE(
703         FormsMatchForMerge(base_form, different_form, STRICT_FORM_MATCH));
704   }
705   {
706     PasswordForm different_form(base_form);
707     different_form.blacklisted_by_user = true;
708     EXPECT_FALSE(
709         FormsMatchForMerge(base_form, different_form, STRICT_FORM_MATCH));
710   }
711
712   // Blacklist forms should *never* match for merging, even when identical
713   // (and certainly not when only one is a blacklist entry).
714   {
715     PasswordForm form_a(base_form);
716     form_a.blacklisted_by_user = true;
717     PasswordForm form_b(form_a);
718     EXPECT_FALSE(FormsMatchForMerge(form_a, form_b, STRICT_FORM_MATCH));
719   }
720 }
721
722 TEST_F(PasswordStoreMacInternalsTest, TestFormMerge) {
723   // Set up a bunch of test data to use in varying combinations.
724   PasswordFormData keychain_user_1 =
725       { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
726         "http://some.domain.com/", "", L"", L"", L"", L"joe_user", L"sekrit",
727         false, false, 1010101010 };
728   PasswordFormData keychain_user_1_with_path =
729       { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
730         "http://some.domain.com/page.html",
731         "", L"", L"", L"", L"joe_user", L"otherpassword",
732         false, false, 1010101010 };
733   PasswordFormData keychain_user_2 =
734       { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
735         "http://some.domain.com/", "", L"", L"", L"", L"john.doe", L"sesame",
736         false, false, 958739876 };
737   PasswordFormData keychain_blacklist =
738       { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
739         "http://some.domain.com/", "", L"", L"", L"", NULL, NULL,
740         false, false, 1010101010 };
741
742   PasswordFormData db_user_1 =
743       { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
744         "http://some.domain.com/", "http://some.domain.com/action.cgi",
745         L"submit", L"username", L"password", L"joe_user", L"",
746         true, false, 1212121212 };
747   PasswordFormData db_user_1_with_path =
748       { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
749         "http://some.domain.com/page.html",
750         "http://some.domain.com/handlepage.cgi",
751         L"submit", L"username", L"password", L"joe_user", L"",
752         true, false, 1234567890 };
753   PasswordFormData db_user_3_with_path =
754       { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
755         "http://some.domain.com/page.html",
756         "http://some.domain.com/handlepage.cgi",
757         L"submit", L"username", L"password", L"second-account", L"",
758         true, false, 1240000000 };
759   PasswordFormData database_blacklist_with_path =
760       { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
761         "http://some.domain.com/path.html", "http://some.domain.com/action.cgi",
762         L"submit", L"username", L"password", NULL, NULL,
763         true, false, 1212121212 };
764
765   PasswordFormData merged_user_1 =
766       { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
767         "http://some.domain.com/", "http://some.domain.com/action.cgi",
768         L"submit", L"username", L"password", L"joe_user", L"sekrit",
769         true, false, 1212121212 };
770   PasswordFormData merged_user_1_with_db_path =
771       { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
772         "http://some.domain.com/page.html",
773         "http://some.domain.com/handlepage.cgi",
774         L"submit", L"username", L"password", L"joe_user", L"sekrit",
775         true, false, 1234567890 };
776   PasswordFormData merged_user_1_with_both_paths =
777       { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
778         "http://some.domain.com/page.html",
779         "http://some.domain.com/handlepage.cgi",
780         L"submit", L"username", L"password", L"joe_user", L"otherpassword",
781         true, false, 1234567890 };
782
783   // Build up the big multi-dimensional array of data sets that will actually
784   // drive the test. Use vectors rather than arrays so that initialization is
785   // simple.
786   enum {
787     KEYCHAIN_INPUT = 0,
788     DATABASE_INPUT,
789     MERGE_OUTPUT,
790     KEYCHAIN_OUTPUT,
791     DATABASE_OUTPUT,
792     MERGE_IO_ARRAY_COUNT  // termination marker
793   };
794   const unsigned int kTestCount = 4;
795   std::vector< std::vector< std::vector<PasswordFormData*> > > test_data(
796       MERGE_IO_ARRAY_COUNT, std::vector< std::vector<PasswordFormData*> >(
797           kTestCount, std::vector<PasswordFormData*>()));
798   unsigned int current_test = 0;
799
800   // Test a merge with a few accounts in both systems, with partial overlap.
801   CHECK(current_test < kTestCount);
802   test_data[KEYCHAIN_INPUT][current_test].push_back(&keychain_user_1);
803   test_data[KEYCHAIN_INPUT][current_test].push_back(&keychain_user_2);
804   test_data[DATABASE_INPUT][current_test].push_back(&db_user_1);
805   test_data[DATABASE_INPUT][current_test].push_back(&db_user_1_with_path);
806   test_data[DATABASE_INPUT][current_test].push_back(&db_user_3_with_path);
807   test_data[MERGE_OUTPUT][current_test].push_back(&merged_user_1);
808   test_data[MERGE_OUTPUT][current_test].push_back(&merged_user_1_with_db_path);
809   test_data[KEYCHAIN_OUTPUT][current_test].push_back(&keychain_user_2);
810   test_data[DATABASE_OUTPUT][current_test].push_back(&db_user_3_with_path);
811
812   // Test a merge where Chrome has a blacklist entry, and the keychain has
813   // a stored account.
814   ++current_test;
815   CHECK(current_test < kTestCount);
816   test_data[KEYCHAIN_INPUT][current_test].push_back(&keychain_user_1);
817   test_data[DATABASE_INPUT][current_test].push_back(
818       &database_blacklist_with_path);
819   // We expect both to be present because a blacklist could be specific to a
820   // subpath, and we want access to the password on other paths.
821   test_data[MERGE_OUTPUT][current_test].push_back(
822       &database_blacklist_with_path);
823   test_data[KEYCHAIN_OUTPUT][current_test].push_back(&keychain_user_1);
824
825   // Test a merge where Chrome has an account, and Keychain has a blacklist
826   // (from another browser) and the Chrome password data.
827   ++current_test;
828   CHECK(current_test < kTestCount);
829   test_data[KEYCHAIN_INPUT][current_test].push_back(&keychain_blacklist);
830   test_data[KEYCHAIN_INPUT][current_test].push_back(&keychain_user_1);
831   test_data[DATABASE_INPUT][current_test].push_back(&db_user_1);
832   test_data[MERGE_OUTPUT][current_test].push_back(&merged_user_1);
833   test_data[KEYCHAIN_OUTPUT][current_test].push_back(&keychain_blacklist);
834
835   // Test that matches are done using exact path when possible.
836   ++current_test;
837   CHECK(current_test < kTestCount);
838   test_data[KEYCHAIN_INPUT][current_test].push_back(&keychain_user_1);
839   test_data[KEYCHAIN_INPUT][current_test].push_back(&keychain_user_1_with_path);
840   test_data[DATABASE_INPUT][current_test].push_back(&db_user_1);
841   test_data[DATABASE_INPUT][current_test].push_back(&db_user_1_with_path);
842   test_data[MERGE_OUTPUT][current_test].push_back(&merged_user_1);
843   test_data[MERGE_OUTPUT][current_test].push_back(
844       &merged_user_1_with_both_paths);
845
846   for (unsigned int test_case = 0; test_case <= current_test; ++test_case) {
847     std::vector<PasswordForm*> keychain_forms;
848     for (std::vector<PasswordFormData*>::iterator i =
849              test_data[KEYCHAIN_INPUT][test_case].begin();
850          i != test_data[KEYCHAIN_INPUT][test_case].end(); ++i) {
851       keychain_forms.push_back(CreatePasswordFormFromData(*(*i)));
852     }
853     std::vector<PasswordForm*> database_forms;
854     for (std::vector<PasswordFormData*>::iterator i =
855              test_data[DATABASE_INPUT][test_case].begin();
856          i != test_data[DATABASE_INPUT][test_case].end(); ++i) {
857       database_forms.push_back(CreatePasswordFormFromData(*(*i)));
858     }
859
860     std::vector<PasswordForm*> merged_forms;
861     internal_keychain_helpers::MergePasswordForms(&keychain_forms,
862                                                   &database_forms,
863                                                   &merged_forms);
864
865     CHECK_FORMS(keychain_forms, test_data[KEYCHAIN_OUTPUT][test_case],
866                 test_case);
867     CHECK_FORMS(database_forms, test_data[DATABASE_OUTPUT][test_case],
868                 test_case);
869     CHECK_FORMS(merged_forms, test_data[MERGE_OUTPUT][test_case], test_case);
870
871     STLDeleteElements(&keychain_forms);
872     STLDeleteElements(&database_forms);
873     STLDeleteElements(&merged_forms);
874   }
875 }
876
877 TEST_F(PasswordStoreMacInternalsTest, TestPasswordBulkLookup) {
878   PasswordFormData db_data[] = {
879     { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
880       "http://some.domain.com/", "http://some.domain.com/action.cgi",
881       L"submit", L"username", L"password", L"joe_user", L"",
882       true, false, 1212121212 },
883     { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
884       "http://some.domain.com/page.html",
885       "http://some.domain.com/handlepage.cgi",
886       L"submit", L"username", L"password", L"joe_user", L"",
887       true, false, 1234567890 },
888     { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
889       "http://some.domain.com/page.html",
890       "http://some.domain.com/handlepage.cgi",
891       L"submit", L"username", L"password", L"second-account", L"",
892       true, false, 1240000000 },
893     { PasswordForm::SCHEME_HTML, "http://dont.remember.com/",
894       "http://dont.remember.com/",
895       "http://dont.remember.com/handlepage.cgi",
896       L"submit", L"username", L"password", L"joe_user", L"",
897       true, false, 1240000000 },
898     { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
899       "http://some.domain.com/path.html", "http://some.domain.com/action.cgi",
900       L"submit", L"username", L"password", NULL, NULL,
901       true, false, 1212121212 },
902   };
903   std::vector<PasswordForm*> database_forms;
904   for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(db_data); ++i) {
905     database_forms.push_back(CreatePasswordFormFromData(db_data[i]));
906   }
907   std::vector<PasswordForm*> merged_forms =
908       internal_keychain_helpers::GetPasswordsForForms(*keychain_,
909                                                       &database_forms);
910   EXPECT_EQ(2U, database_forms.size());
911   ASSERT_EQ(3U, merged_forms.size());
912   EXPECT_EQ(ASCIIToUTF16("sekrit"), merged_forms[0]->password_value);
913   EXPECT_EQ(ASCIIToUTF16("sekrit"), merged_forms[1]->password_value);
914   EXPECT_TRUE(merged_forms[2]->blacklisted_by_user);
915
916   STLDeleteElements(&database_forms);
917   STLDeleteElements(&merged_forms);
918 }
919
920 TEST_F(PasswordStoreMacInternalsTest, TestBlacklistedFiltering) {
921   PasswordFormData db_data[] = {
922     { PasswordForm::SCHEME_HTML, "http://dont.remember.com/",
923       "http://dont.remember.com/",
924       "http://dont.remember.com/handlepage.cgi",
925       L"submit", L"username", L"password", L"joe_user", L"non_empty_password",
926       true, false, 1240000000 },
927     { PasswordForm::SCHEME_HTML, "https://dont.remember.com/",
928       "https://dont.remember.com/",
929       "https://dont.remember.com/handlepage_secure.cgi",
930       L"submit", L"username", L"password", L"joe_user", L"non_empty_password",
931       true, false, 1240000000 },
932   };
933   std::vector<PasswordForm*> database_forms;
934   for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(db_data); ++i) {
935     database_forms.push_back(CreatePasswordFormFromData(db_data[i]));
936   }
937   std::vector<PasswordForm*> merged_forms =
938       internal_keychain_helpers::GetPasswordsForForms(*keychain_,
939                                                       &database_forms);
940   EXPECT_EQ(2U, database_forms.size());
941   ASSERT_EQ(0U, merged_forms.size());
942
943   STLDeleteElements(&database_forms);
944   STLDeleteElements(&merged_forms);
945 }
946
947 TEST_F(PasswordStoreMacInternalsTest, TestFillPasswordFormFromKeychainItem) {
948   // When |extract_password_data| is false, the password field must be empty,
949   // and |blacklisted_by_user| must be false.
950   SecKeychainItemRef keychain_item = reinterpret_cast<SecKeychainItemRef>(1);
951   PasswordForm form_without_extracted_password;
952   bool parsed = internal_keychain_helpers::FillPasswordFormFromKeychainItem(
953       *keychain_,
954       keychain_item,
955       &form_without_extracted_password,
956       false);  // Do not extract password.
957   EXPECT_TRUE(parsed);
958   ASSERT_TRUE(form_without_extracted_password.password_value.empty());
959   ASSERT_FALSE(form_without_extracted_password.blacklisted_by_user);
960
961   // When |extract_password_data| is true and the keychain entry has a non-empty
962   // password, the password field must be non-empty, and the value of
963   // |blacklisted_by_user| must be false.
964   keychain_item = reinterpret_cast<SecKeychainItemRef>(1);
965   PasswordForm form_with_extracted_password;
966   parsed = internal_keychain_helpers::FillPasswordFormFromKeychainItem(
967       *keychain_,
968       keychain_item,
969       &form_with_extracted_password,
970       true);  // Extract password.
971   EXPECT_TRUE(parsed);
972   ASSERT_EQ(ASCIIToUTF16("sekrit"),
973             form_with_extracted_password.password_value);
974   ASSERT_FALSE(form_with_extracted_password.blacklisted_by_user);
975
976   // When |extract_password_data| is true and the keychain entry has an empty
977   // username and password (""), the password field must be empty, and the value
978   // of |blacklisted_by_user| must be true.
979   keychain_item = reinterpret_cast<SecKeychainItemRef>(4);
980   PasswordForm negative_form;
981   parsed = internal_keychain_helpers::FillPasswordFormFromKeychainItem(
982       *keychain_,
983       keychain_item,
984       &negative_form,
985       true);  // Extract password.
986   EXPECT_TRUE(parsed);
987   ASSERT_TRUE(negative_form.username_value.empty());
988   ASSERT_TRUE(negative_form.password_value.empty());
989   ASSERT_TRUE(negative_form.blacklisted_by_user);
990
991   // When |extract_password_data| is true and the keychain entry has an empty
992   // password (""), the password field must be empty (""), and the value of
993   // |blacklisted_by_user| must be true.
994   keychain_item = reinterpret_cast<SecKeychainItemRef>(5);
995   PasswordForm form_with_empty_password_a;
996   parsed = internal_keychain_helpers::FillPasswordFormFromKeychainItem(
997       *keychain_,
998       keychain_item,
999       &form_with_empty_password_a,
1000       true);  // Extract password.
1001   EXPECT_TRUE(parsed);
1002   ASSERT_TRUE(form_with_empty_password_a.password_value.empty());
1003   ASSERT_TRUE(form_with_empty_password_a.blacklisted_by_user);
1004
1005   // When |extract_password_data| is true and the keychain entry has a single
1006   // space password (" "), the password field must be a single space (" "), and
1007   // the value of |blacklisted_by_user| must be true.
1008   keychain_item = reinterpret_cast<SecKeychainItemRef>(6);
1009   PasswordForm form_with_empty_password_b;
1010   parsed = internal_keychain_helpers::FillPasswordFormFromKeychainItem(
1011       *keychain_,
1012       keychain_item,
1013       &form_with_empty_password_b,
1014       true);  // Extract password.
1015   EXPECT_TRUE(parsed);
1016   ASSERT_EQ(ASCIIToUTF16(" "),
1017             form_with_empty_password_b.password_value);
1018   ASSERT_TRUE(form_with_empty_password_b.blacklisted_by_user);
1019 }
1020
1021 TEST_F(PasswordStoreMacInternalsTest, TestPasswordGetAll) {
1022   MacKeychainPasswordFormAdapter keychain_adapter(keychain_);
1023   MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_);
1024   owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
1025
1026   // Add a few passwords of various types so that we own some.
1027   PasswordFormData owned_password_data[] = {
1028     { PasswordForm::SCHEME_HTML, "http://web.site.com/",
1029       "http://web.site.com/path/to/page.html", NULL, NULL, NULL, NULL,
1030       L"anonymous", L"knock-knock", false, false, 0 },
1031     { PasswordForm::SCHEME_BASIC, "http://a.site.com:2222/therealm",
1032       "http://a.site.com:2222/", NULL, NULL, NULL, NULL,
1033       L"username", L"password", false, false, 0 },
1034     { PasswordForm::SCHEME_DIGEST, "https://digest.site.com/differentrealm",
1035       "https://digest.site.com/secure.html", NULL, NULL, NULL, NULL,
1036       L"testname", L"testpass", false, false, 0 },
1037   };
1038   for (unsigned int i = 0; i < arraysize(owned_password_data); ++i) {
1039     scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(
1040         owned_password_data[i]));
1041     owned_keychain_adapter.AddPassword(*form);
1042   }
1043
1044   std::vector<PasswordForm*> all_passwords =
1045       keychain_adapter.GetAllPasswordFormPasswords();
1046   EXPECT_EQ(8 + arraysize(owned_password_data), all_passwords.size());
1047   STLDeleteElements(&all_passwords);
1048
1049   std::vector<PasswordForm*> owned_passwords =
1050       owned_keychain_adapter.GetAllPasswordFormPasswords();
1051   EXPECT_EQ(arraysize(owned_password_data), owned_passwords.size());
1052   STLDeleteElements(&owned_passwords);
1053 }
1054
1055 #pragma mark -
1056
1057 class PasswordStoreMacTest : public testing::Test {
1058  public:
1059   PasswordStoreMacTest() : ui_thread_(BrowserThread::UI, &message_loop_) {}
1060
1061   virtual void SetUp() {
1062     login_db_ = new LoginDatabase();
1063     ASSERT_TRUE(db_dir_.CreateUniqueTempDir());
1064     base::FilePath db_file = db_dir_.path().AppendASCII("login.db");
1065     ASSERT_TRUE(login_db_->Init(db_file));
1066
1067     keychain_ = new MockAppleKeychain();
1068
1069     store_ = new TestPasswordStoreMac(
1070         base::MessageLoopProxy::current(),
1071         base::MessageLoopProxy::current(),
1072         keychain_,
1073         login_db_);
1074     ASSERT_TRUE(store_->Init(syncer::SyncableService::StartSyncFlare()));
1075   }
1076
1077   virtual void TearDown() {
1078     store_->Shutdown();
1079     EXPECT_FALSE(store_->GetBackgroundTaskRunner());
1080   }
1081
1082   void WaitForStoreUpdate() {
1083     // Do a store-level query to wait for all the operations above to be done.
1084     MockPasswordStoreConsumer consumer;
1085     EXPECT_CALL(consumer, OnGetPasswordStoreResults(_))
1086         .WillOnce(DoAll(WithArg<0>(STLDeleteElements0()), QuitUIMessageLoop()));
1087     store_->GetLogins(PasswordForm(), PasswordStore::ALLOW_PROMPT, &consumer);
1088     base::MessageLoop::current()->Run();
1089   }
1090
1091  protected:
1092   base::MessageLoopForUI message_loop_;
1093   content::TestBrowserThread ui_thread_;
1094
1095   MockAppleKeychain* keychain_;  // Owned by store_.
1096   LoginDatabase* login_db_;  // Owned by store_.
1097   scoped_refptr<TestPasswordStoreMac> store_;
1098   base::ScopedTempDir db_dir_;
1099 };
1100
1101 TEST_F(PasswordStoreMacTest, TestStoreUpdate) {
1102   // Insert a password into both the database and the keychain.
1103   // This is done manually, rather than through store_->AddLogin, because the
1104   // Mock Keychain isn't smart enough to be able to support update generically,
1105   // so some.domain.com triggers special handling to test it that make inserting
1106   // fail.
1107   PasswordFormData joint_data = {
1108     PasswordForm::SCHEME_HTML, "http://some.domain.com/",
1109     "http://some.domain.com/insecure.html", "login.cgi",
1110     L"username", L"password", L"submit", L"joe_user", L"sekrit", true, false, 1
1111   };
1112   scoped_ptr<PasswordForm> joint_form(CreatePasswordFormFromData(joint_data));
1113   login_db_->AddLogin(*joint_form);
1114   MockAppleKeychain::KeychainTestData joint_keychain_data = {
1115     kSecAuthenticationTypeHTMLForm, "some.domain.com",
1116     kSecProtocolTypeHTTP, "/insecure.html", 0, NULL, "20020601171500Z",
1117     "joe_user", "sekrit", false };
1118   keychain_->AddTestItem(joint_keychain_data);
1119
1120   // Insert a password into the keychain only.
1121   MockAppleKeychain::KeychainTestData keychain_only_data = {
1122     kSecAuthenticationTypeHTMLForm, "keychain.only.com",
1123     kSecProtocolTypeHTTP, NULL, 0, NULL, "20020601171500Z",
1124     "keychain", "only", false
1125   };
1126   keychain_->AddTestItem(keychain_only_data);
1127
1128   struct UpdateData {
1129     PasswordFormData form_data;
1130     const char* password;  // NULL indicates no entry should be present.
1131   };
1132
1133   // Make a series of update calls.
1134   UpdateData updates[] = {
1135     // Update the keychain+db passwords (the normal password update case).
1136     { { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
1137         "http://some.domain.com/insecure.html", "login.cgi",
1138         L"username", L"password", L"submit", L"joe_user", L"53krit",
1139         true, false, 2 },
1140       "53krit",
1141     },
1142     // Update the keychain-only password; this simulates the initial use of a
1143     // password stored by another browsers.
1144     { { PasswordForm::SCHEME_HTML, "http://keychain.only.com/",
1145         "http://keychain.only.com/login.html", "login.cgi",
1146         L"username", L"password", L"submit", L"keychain", L"only",
1147         true, false, 2 },
1148       "only",
1149     },
1150     // Update a password that doesn't exist in either location. This tests the
1151     // case where a form is filled, then the stored login is removed, then the
1152     // form is submitted.
1153     { { PasswordForm::SCHEME_HTML, "http://different.com/",
1154         "http://different.com/index.html", "login.cgi",
1155         L"username", L"password", L"submit", L"abc", L"123",
1156         true, false, 2 },
1157       NULL,
1158     },
1159   };
1160   for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(updates); ++i) {
1161     scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(
1162         updates[i].form_data));
1163     store_->UpdateLogin(*form);
1164   }
1165
1166   WaitForStoreUpdate();
1167
1168   MacKeychainPasswordFormAdapter keychain_adapter(keychain_);
1169   for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(updates); ++i) {
1170     scoped_ptr<PasswordForm> query_form(
1171         CreatePasswordFormFromData(updates[i].form_data));
1172
1173     std::vector<PasswordForm*> matching_items =
1174         keychain_adapter.PasswordsFillingForm(query_form->signon_realm,
1175                                               query_form->scheme);
1176     if (updates[i].password) {
1177       EXPECT_GT(matching_items.size(), 0U) << "iteration " << i;
1178       if (matching_items.size() >= 1)
1179         EXPECT_EQ(ASCIIToUTF16(updates[i].password),
1180                   matching_items[0]->password_value) << "iteration " << i;
1181     } else {
1182       EXPECT_EQ(0U, matching_items.size()) << "iteration " << i;
1183     }
1184     STLDeleteElements(&matching_items);
1185
1186     login_db_->GetLogins(*query_form, &matching_items);
1187     EXPECT_EQ(updates[i].password ? 1U : 0U, matching_items.size())
1188         << "iteration " << i;
1189     STLDeleteElements(&matching_items);
1190   }
1191 }
1192
1193 TEST_F(PasswordStoreMacTest, TestDBKeychainAssociation) {
1194   // Tests that association between the keychain and login database parts of a
1195   // password added by fuzzy (PSL) matching works.
1196   // 1. Add a password for www.facebook.com
1197   // 2. Get a password for m.facebook.com. This fuzzy matches and returns the
1198   //    www.facebook.com password.
1199   // 3. Add the returned password for m.facebook.com.
1200   // 4. Remove both passwords.
1201   //    -> check: that both are gone from the login DB and the keychain
1202   // This test should in particular ensure that we don't keep passwords in the
1203   // keychain just before we think we still have other (fuzzy-)matching entries
1204   // for them in the login database. (For example, here if we deleted the
1205   // www.facebook.com password from the login database, we should not be blocked
1206   // from deleting it from the keystore just becaus the m.facebook.com password
1207   // fuzzy-matches the www.facebook.com one.)
1208
1209   // 1. Add a password for www.facebook.com
1210   PasswordFormData www_form_data = {
1211     PasswordForm::SCHEME_HTML, "http://www.facebook.com/",
1212     "http://www.facebook.com/index.html", "login",
1213     L"username", L"password", L"submit", L"joe_user", L"sekrit", true, false, 1
1214   };
1215   scoped_ptr<PasswordForm> www_form(CreatePasswordFormFromData(www_form_data));
1216   login_db_->AddLogin(*www_form);
1217   MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_);
1218   owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
1219   owned_keychain_adapter.AddPassword(*www_form);
1220
1221   // 2. Get a password for m.facebook.com.
1222   PasswordForm m_form(*www_form);
1223   m_form.signon_realm = "http://m.facebook.com";
1224   m_form.origin = GURL("http://m.facebook.com/index.html");
1225   MockPasswordStoreConsumer consumer;
1226   EXPECT_CALL(consumer, OnGetPasswordStoreResults(_)).WillOnce(DoAll(
1227       WithArg<0>(Invoke(&consumer, &MockPasswordStoreConsumer::CopyElements)),
1228       WithArg<0>(STLDeleteElements0()),
1229       QuitUIMessageLoop()));
1230   store_->GetLogins(m_form, PasswordStore::ALLOW_PROMPT, &consumer);
1231   base::MessageLoop::current()->Run();
1232   EXPECT_EQ(1u, consumer.last_result.size());
1233
1234   // 3. Add the returned password for m.facebook.com.
1235   login_db_->AddLogin(consumer.last_result[0]);
1236   owned_keychain_adapter.AddPassword(m_form);
1237
1238   // 4. Remove both passwords.
1239   store_->RemoveLogin(*www_form);
1240   store_->RemoveLogin(m_form);
1241   WaitForStoreUpdate();
1242
1243   std::vector<PasswordForm*> matching_items;
1244   // No trace of www.facebook.com.
1245   matching_items = owned_keychain_adapter.PasswordsFillingForm(
1246       www_form->signon_realm, www_form->scheme);
1247   EXPECT_EQ(0u, matching_items.size());
1248   login_db_->GetLogins(*www_form, &matching_items);
1249   EXPECT_EQ(0u, matching_items.size());
1250   // No trace of m.facebook.com.
1251   matching_items = owned_keychain_adapter.PasswordsFillingForm(
1252       m_form.signon_realm, m_form.scheme);
1253   EXPECT_EQ(0u, matching_items.size());
1254   login_db_->GetLogins(m_form, &matching_items);
1255   EXPECT_EQ(0u, matching_items.size());
1256 }