• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 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 "components/password_manager/core/browser/login_database.h"
6 
7 #include "base/basictypes.h"
8 #include "base/file_util.h"
9 #include "base/files/scoped_temp_dir.h"
10 #include "base/memory/scoped_vector.h"
11 #include "base/path_service.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/time/time.h"
15 #include "components/autofill/core/common/password_form.h"
16 #include "components/password_manager/core/browser/psl_matching_helper.h"
17 #include "testing/gmock/include/gmock/gmock.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 
20 using autofill::PasswordForm;
21 using base::ASCIIToUTF16;
22 using ::testing::Eq;
23 
24 namespace password_manager {
25 namespace {
AddChangeForForm(const PasswordForm & form)26 PasswordStoreChangeList AddChangeForForm(const PasswordForm& form) {
27   return PasswordStoreChangeList(1,
28                                  PasswordStoreChange(PasswordStoreChange::ADD,
29                                                      form));
30 }
31 
UpdateChangeForForm(const PasswordForm & form)32 PasswordStoreChangeList UpdateChangeForForm(const PasswordForm& form) {
33   return PasswordStoreChangeList(1, PasswordStoreChange(
34       PasswordStoreChange::UPDATE, form));
35 }
36 
37 }  // namespace
38 
39 // Serialization routines for vectors implemented in login_database.cc.
40 Pickle SerializeVector(const std::vector<base::string16>& vec);
41 std::vector<base::string16> DeserializeVector(const Pickle& pickle);
42 
43 class LoginDatabaseTest : public testing::Test {
44  protected:
SetUp()45   virtual void SetUp() {
46     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
47     file_ = temp_dir_.path().AppendASCII("TestMetadataStoreMacDatabase");
48 
49     ASSERT_TRUE(db_.Init(file_));
50   }
51 
FormsAreEqual(const PasswordForm & expected,const PasswordForm & actual)52   void FormsAreEqual(const PasswordForm& expected, const PasswordForm& actual) {
53     PasswordForm expected_copy(expected);
54 #if defined(OS_MACOSX) && !defined(OS_IOS)
55     // On the Mac we should never be storing passwords in the database.
56     expected_copy.password_value = ASCIIToUTF16("");
57 #endif
58     EXPECT_EQ(expected_copy, actual);
59   }
60 
TestNonHTMLFormPSLMatching(const PasswordForm::Scheme & scheme)61   void TestNonHTMLFormPSLMatching(const PasswordForm::Scheme& scheme) {
62     ScopedVector<PasswordForm> result;
63 
64     base::Time now = base::Time::Now();
65 
66     // Simple non-html auth form.
67     PasswordForm non_html_auth;
68     non_html_auth.origin = GURL("http://example.com");
69     non_html_auth.username_value = ASCIIToUTF16("test@gmail.com");
70     non_html_auth.password_value = ASCIIToUTF16("test");
71     non_html_auth.signon_realm = "http://example.com/Realm";
72     non_html_auth.scheme = scheme;
73     non_html_auth.date_created = now;
74 
75     // Simple password form.
76     PasswordForm html_form(non_html_auth);
77     html_form.action = GURL("http://example.com/login");
78     html_form.username_element = ASCIIToUTF16("username");
79     html_form.username_value = ASCIIToUTF16("test2@gmail.com");
80     html_form.password_element = ASCIIToUTF16("password");
81     html_form.submit_element = ASCIIToUTF16("");
82     html_form.signon_realm = "http://example.com/";
83     html_form.scheme = PasswordForm::SCHEME_HTML;
84     html_form.date_created = now;
85 
86     // Add them and make sure they are there.
87     EXPECT_EQ(AddChangeForForm(non_html_auth), db_.AddLogin(non_html_auth));
88     EXPECT_EQ(AddChangeForForm(html_form), db_.AddLogin(html_form));
89     EXPECT_TRUE(db_.GetAutofillableLogins(&result.get()));
90     EXPECT_EQ(2U, result.size());
91     result.clear();
92 
93     PasswordForm second_non_html_auth(non_html_auth);
94     second_non_html_auth.origin = GURL("http://second.example.com");
95     second_non_html_auth.signon_realm = "http://second.example.com/Realm";
96 
97     // This shouldn't match anything.
98     EXPECT_TRUE(db_.GetLogins(second_non_html_auth, &result.get()));
99     EXPECT_EQ(0U, result.size());
100 
101     // non-html auth still matches again itself.
102     EXPECT_TRUE(db_.GetLogins(non_html_auth, &result.get()));
103     ASSERT_EQ(1U, result.size());
104     EXPECT_EQ(result[0]->signon_realm, "http://example.com/Realm");
105 
106     // Clear state.
107     db_.RemoveLoginsCreatedBetween(now, base::Time());
108   }
109 
110   base::ScopedTempDir temp_dir_;
111   base::FilePath file_;
112   LoginDatabase db_;
113 };
114 
TEST_F(LoginDatabaseTest,Logins)115 TEST_F(LoginDatabaseTest, Logins) {
116   std::vector<PasswordForm*> result;
117 
118   // Verify the database is empty.
119   EXPECT_TRUE(db_.GetAutofillableLogins(&result));
120   EXPECT_EQ(0U, result.size());
121 
122   // Example password form.
123   PasswordForm form;
124   form.origin = GURL("http://accounts.google.com/LoginAuth");
125   form.action = GURL("http://accounts.google.com/Login");
126   form.username_element = ASCIIToUTF16("Email");
127   form.username_value = ASCIIToUTF16("test@gmail.com");
128   form.password_element = ASCIIToUTF16("Passwd");
129   form.password_value = ASCIIToUTF16("test");
130   form.submit_element = ASCIIToUTF16("signIn");
131   form.signon_realm = "http://www.google.com/";
132   form.ssl_valid = false;
133   form.preferred = false;
134   form.scheme = PasswordForm::SCHEME_HTML;
135   form.times_used = 1;
136   form.form_data.name = ASCIIToUTF16("form_name");
137   form.form_data.method = ASCIIToUTF16("POST");
138   form.date_synced = base::Time::Now();
139 
140   // Add it and make sure it is there and that all the fields were retrieved
141   // correctly.
142   EXPECT_EQ(AddChangeForForm(form), db_.AddLogin(form));
143   EXPECT_TRUE(db_.GetAutofillableLogins(&result));
144   ASSERT_EQ(1U, result.size());
145   FormsAreEqual(form, *result[0]);
146   delete result[0];
147   result.clear();
148 
149   // Match against an exact copy.
150   EXPECT_TRUE(db_.GetLogins(form, &result));
151   ASSERT_EQ(1U, result.size());
152   FormsAreEqual(form, *result[0]);
153   delete result[0];
154   result.clear();
155 
156   // The example site changes...
157   PasswordForm form2(form);
158   form2.origin = GURL("http://www.google.com/new/accounts/LoginAuth");
159   form2.submit_element = ASCIIToUTF16("reallySignIn");
160 
161   // Match against an inexact copy
162   EXPECT_TRUE(db_.GetLogins(form2, &result));
163   EXPECT_EQ(1U, result.size());
164   delete result[0];
165   result.clear();
166 
167   // Uh oh, the site changed origin & action URLs all at once!
168   PasswordForm form3(form2);
169   form3.action = GURL("http://www.google.com/new/accounts/Login");
170 
171   // signon_realm is the same, should match.
172   EXPECT_TRUE(db_.GetLogins(form3, &result));
173   EXPECT_EQ(1U, result.size());
174   delete result[0];
175   result.clear();
176 
177   // Imagine the site moves to a secure server for login.
178   PasswordForm form4(form3);
179   form4.signon_realm = "https://www.google.com/";
180   form4.ssl_valid = true;
181 
182   // We have only an http record, so no match for this.
183   EXPECT_TRUE(db_.GetLogins(form4, &result));
184   EXPECT_EQ(0U, result.size());
185 
186   // Let's imagine the user logs into the secure site.
187   EXPECT_EQ(AddChangeForForm(form4), db_.AddLogin(form4));
188   EXPECT_TRUE(db_.GetAutofillableLogins(&result));
189   EXPECT_EQ(2U, result.size());
190   delete result[0];
191   delete result[1];
192   result.clear();
193 
194   // Now the match works
195   EXPECT_TRUE(db_.GetLogins(form4, &result));
196   EXPECT_EQ(1U, result.size());
197   delete result[0];
198   result.clear();
199 
200   // The user chose to forget the original but not the new.
201   EXPECT_TRUE(db_.RemoveLogin(form));
202   EXPECT_TRUE(db_.GetAutofillableLogins(&result));
203   EXPECT_EQ(1U, result.size());
204   delete result[0];
205   result.clear();
206 
207   // The old form wont match the new site (http vs https).
208   EXPECT_TRUE(db_.GetLogins(form, &result));
209   EXPECT_EQ(0U, result.size());
210 
211   // The user's request for the HTTPS site is intercepted
212   // by an attacker who presents an invalid SSL cert.
213   PasswordForm form5(form4);
214   form5.ssl_valid = 0;
215 
216   // It will match in this case.
217   EXPECT_TRUE(db_.GetLogins(form5, &result));
218   EXPECT_EQ(1U, result.size());
219   delete result[0];
220   result.clear();
221 
222   // User changes his password.
223   PasswordForm form6(form5);
224   form6.password_value = ASCIIToUTF16("test6");
225   form6.preferred = true;
226 
227   // We update, and check to make sure it matches the
228   // old form, and there is only one record.
229   EXPECT_EQ(UpdateChangeForForm(form6), db_.UpdateLogin(form6));
230   // matches
231   EXPECT_TRUE(db_.GetLogins(form5, &result));
232   EXPECT_EQ(1U, result.size());
233   delete result[0];
234   result.clear();
235   // Only one record.
236   EXPECT_TRUE(db_.GetAutofillableLogins(&result));
237   EXPECT_EQ(1U, result.size());
238   // Password element was updated.
239 #if defined(OS_MACOSX) && !defined(OS_IOS)
240   // On the Mac we should never be storing passwords in the database.
241   EXPECT_EQ(base::string16(), result[0]->password_value);
242 #else
243   EXPECT_EQ(form6.password_value, result[0]->password_value);
244 #endif
245   // Preferred login.
246   EXPECT_TRUE(form6.preferred);
247   delete result[0];
248   result.clear();
249 
250   // Make sure everything can disappear.
251   EXPECT_TRUE(db_.RemoveLogin(form4));
252   EXPECT_TRUE(db_.GetAutofillableLogins(&result));
253   EXPECT_EQ(0U, result.size());
254 }
255 
TEST_F(LoginDatabaseTest,TestPublicSuffixDomainMatching)256 TEST_F(LoginDatabaseTest, TestPublicSuffixDomainMatching) {
257   PSLMatchingHelper::EnablePublicSuffixDomainMatchingForTesting();
258   std::vector<PasswordForm*> result;
259 
260   // Verify the database is empty.
261   EXPECT_TRUE(db_.GetAutofillableLogins(&result));
262   EXPECT_EQ(0U, result.size());
263 
264   // Example password form.
265   PasswordForm form;
266   form.origin = GURL("https://foo.com/");
267   form.action = GURL("https://foo.com/login");
268   form.username_element = ASCIIToUTF16("username");
269   form.username_value = ASCIIToUTF16("test@gmail.com");
270   form.password_element = ASCIIToUTF16("password");
271   form.password_value = ASCIIToUTF16("test");
272   form.submit_element = ASCIIToUTF16("");
273   form.signon_realm = "https://foo.com/";
274   form.ssl_valid = true;
275   form.preferred = false;
276   form.scheme = PasswordForm::SCHEME_HTML;
277 
278   // Add it and make sure it is there.
279   EXPECT_EQ(AddChangeForForm(form), db_.AddLogin(form));
280   EXPECT_TRUE(db_.GetAutofillableLogins(&result));
281   EXPECT_EQ(1U, result.size());
282   delete result[0];
283   result.clear();
284 
285   // Match against an exact copy.
286   EXPECT_TRUE(db_.GetLogins(form, &result));
287   EXPECT_EQ(1U, result.size());
288   delete result[0];
289   result.clear();
290 
291   // We go to the mobile site.
292   PasswordForm form2(form);
293   form2.origin = GURL("https://mobile.foo.com/");
294   form2.action = GURL("https://mobile.foo.com/login");
295   form2.signon_realm = "https://mobile.foo.com/";
296 
297   // Match against the mobile site.
298   EXPECT_TRUE(db_.GetLogins(form2, &result));
299   EXPECT_EQ(1U, result.size());
300   EXPECT_EQ("https://mobile.foo.com/", result[0]->signon_realm);
301   EXPECT_EQ("https://foo.com/", result[0]->original_signon_realm);
302   delete result[0];
303   result.clear();
304 }
305 
TEST_F(LoginDatabaseTest,TestPublicSuffixDisabledForNonHTMLForms)306 TEST_F(LoginDatabaseTest, TestPublicSuffixDisabledForNonHTMLForms) {
307   PSLMatchingHelper::EnablePublicSuffixDomainMatchingForTesting();
308 
309   TestNonHTMLFormPSLMatching(PasswordForm::SCHEME_BASIC);
310   TestNonHTMLFormPSLMatching(PasswordForm::SCHEME_DIGEST);
311   TestNonHTMLFormPSLMatching(PasswordForm::SCHEME_OTHER);
312 }
313 
TEST_F(LoginDatabaseTest,TestPublicSuffixDomainMatchingShouldMatchingApply)314 TEST_F(LoginDatabaseTest, TestPublicSuffixDomainMatchingShouldMatchingApply) {
315   PSLMatchingHelper::EnablePublicSuffixDomainMatchingForTesting();
316   std::vector<PasswordForm*> result;
317 
318   // Verify the database is empty.
319   EXPECT_TRUE(db_.GetAutofillableLogins(&result));
320   EXPECT_EQ(0U, result.size());
321 
322   // Example password form.
323   PasswordForm form;
324   form.origin = GURL("https://accounts.google.com/");
325   form.action = GURL("https://accounts.google.com/login");
326   form.username_element = ASCIIToUTF16("username");
327   form.username_value = ASCIIToUTF16("test@gmail.com");
328   form.password_element = ASCIIToUTF16("password");
329   form.password_value = ASCIIToUTF16("test");
330   form.submit_element = ASCIIToUTF16("");
331   form.signon_realm = "https://accounts.google.com/";
332   form.ssl_valid = true;
333   form.preferred = false;
334   form.scheme = PasswordForm::SCHEME_HTML;
335 
336   // Add it and make sure it is there.
337   EXPECT_EQ(AddChangeForForm(form), db_.AddLogin(form));
338   EXPECT_TRUE(db_.GetAutofillableLogins(&result));
339   EXPECT_EQ(1U, result.size());
340   delete result[0];
341   result.clear();
342 
343   // Match against an exact copy.
344   EXPECT_TRUE(db_.GetLogins(form, &result));
345   EXPECT_EQ(1U, result.size());
346   delete result[0];
347   result.clear();
348 
349   // We go to a different site on the same domain where feature is not needed.
350   PasswordForm form2(form);
351   form2.origin = GURL("https://some.other.google.com/");
352   form2.action = GURL("https://some.other.google.com/login");
353   form2.signon_realm = "https://some.other.google.com/";
354 
355   // Match against the other site. Should not match since feature should not be
356   // enabled for this domain.
357   EXPECT_TRUE(db_.GetLogins(form2, &result));
358   EXPECT_EQ(0U, result.size());
359 }
360 
361 // This test fails if the implementation of GetLogins uses GetCachedStatement
362 // instead of GetUniqueStatement, since REGEXP is in use. See
363 // http://crbug.com/248608.
TEST_F(LoginDatabaseTest,TestPublicSuffixDomainMatchingDifferentSites)364 TEST_F(LoginDatabaseTest, TestPublicSuffixDomainMatchingDifferentSites) {
365   PSLMatchingHelper::EnablePublicSuffixDomainMatchingForTesting();
366   std::vector<PasswordForm*> result;
367 
368   // Verify the database is empty.
369   EXPECT_TRUE(db_.GetAutofillableLogins(&result));
370   EXPECT_EQ(0U, result.size());
371 
372   // Example password form.
373   PasswordForm form;
374   form.origin = GURL("https://foo.com/");
375   form.action = GURL("https://foo.com/login");
376   form.username_element = ASCIIToUTF16("username");
377   form.username_value = ASCIIToUTF16("test@gmail.com");
378   form.password_element = ASCIIToUTF16("password");
379   form.password_value = ASCIIToUTF16("test");
380   form.submit_element = ASCIIToUTF16("");
381   form.signon_realm = "https://foo.com/";
382   form.ssl_valid = true;
383   form.preferred = false;
384   form.scheme = PasswordForm::SCHEME_HTML;
385 
386   // Add it and make sure it is there.
387   EXPECT_EQ(AddChangeForForm(form), db_.AddLogin(form));
388   EXPECT_TRUE(db_.GetAutofillableLogins(&result));
389   EXPECT_EQ(1U, result.size());
390   delete result[0];
391   result.clear();
392 
393   // Match against an exact copy.
394   EXPECT_TRUE(db_.GetLogins(form, &result));
395   EXPECT_EQ(1U, result.size());
396   delete result[0];
397   result.clear();
398 
399   // We go to the mobile site.
400   PasswordForm form2(form);
401   form2.origin = GURL("https://mobile.foo.com/");
402   form2.action = GURL("https://mobile.foo.com/login");
403   form2.signon_realm = "https://mobile.foo.com/";
404 
405   // Match against the mobile site.
406   EXPECT_TRUE(db_.GetLogins(form2, &result));
407   EXPECT_EQ(1U, result.size());
408   EXPECT_EQ("https://mobile.foo.com/", result[0]->signon_realm);
409   EXPECT_EQ("https://foo.com/", result[0]->original_signon_realm);
410   delete result[0];
411   result.clear();
412 
413   // Add baz.com desktop site.
414   form.origin = GURL("https://baz.com/login/");
415   form.action = GURL("https://baz.com/login/");
416   form.username_element = ASCIIToUTF16("email");
417   form.username_value = ASCIIToUTF16("test@gmail.com");
418   form.password_element = ASCIIToUTF16("password");
419   form.password_value = ASCIIToUTF16("test");
420   form.submit_element = ASCIIToUTF16("");
421   form.signon_realm = "https://baz.com/";
422   form.ssl_valid = true;
423   form.preferred = false;
424   form.scheme = PasswordForm::SCHEME_HTML;
425 
426   // Add it and make sure it is there.
427   EXPECT_EQ(AddChangeForForm(form), db_.AddLogin(form));
428   EXPECT_TRUE(db_.GetAutofillableLogins(&result));
429   EXPECT_EQ(2U, result.size());
430   delete result[0];
431   delete result[1];
432   result.clear();
433 
434   // We go to the mobile site of baz.com.
435   PasswordForm form3(form);
436   form3.origin = GURL("https://m.baz.com/login/");
437   form3.action = GURL("https://m.baz.com/login/");
438   form3.signon_realm = "https://m.baz.com/";
439 
440   // Match against the mobile site of baz.com.
441   EXPECT_TRUE(db_.GetLogins(form3, &result));
442   EXPECT_EQ(1U, result.size());
443   EXPECT_EQ("https://m.baz.com/", result[0]->signon_realm);
444   EXPECT_EQ("https://baz.com/", result[0]->original_signon_realm);
445   delete result[0];
446   result.clear();
447 }
448 
GetFormWithNewSignonRealm(PasswordForm form,std::string signon_realm)449 PasswordForm GetFormWithNewSignonRealm(PasswordForm form,
450                                        std::string signon_realm) {
451   PasswordForm form2(form);
452   form2.origin = GURL(signon_realm);
453   form2.action = GURL(signon_realm);
454   form2.signon_realm = signon_realm;
455   return form2;
456 }
457 
TEST_F(LoginDatabaseTest,TestPublicSuffixDomainMatchingRegexp)458 TEST_F(LoginDatabaseTest, TestPublicSuffixDomainMatchingRegexp) {
459   PSLMatchingHelper::EnablePublicSuffixDomainMatchingForTesting();
460   std::vector<PasswordForm*> result;
461 
462   // Verify the database is empty.
463   EXPECT_TRUE(db_.GetAutofillableLogins(&result));
464   EXPECT_EQ(0U, result.size());
465 
466   // Example password form.
467   PasswordForm form;
468   form.origin = GURL("http://foo.com/");
469   form.action = GURL("http://foo.com/login");
470   form.username_element = ASCIIToUTF16("username");
471   form.username_value = ASCIIToUTF16("test@gmail.com");
472   form.password_element = ASCIIToUTF16("password");
473   form.password_value = ASCIIToUTF16("test");
474   form.submit_element = ASCIIToUTF16("");
475   form.signon_realm = "http://foo.com/";
476   form.ssl_valid = false;
477   form.preferred = false;
478   form.scheme = PasswordForm::SCHEME_HTML;
479 
480   // Add it and make sure it is there.
481   EXPECT_EQ(AddChangeForForm(form), db_.AddLogin(form));
482   EXPECT_TRUE(db_.GetAutofillableLogins(&result));
483   EXPECT_EQ(1U, result.size());
484   delete result[0];
485   result.clear();
486 
487   // Example password form that has - in the domain name.
488   PasswordForm form_dash =
489       GetFormWithNewSignonRealm(form, "http://www.foo-bar.com/");
490 
491   // Add it and make sure it is there.
492   EXPECT_EQ(AddChangeForForm(form_dash), db_.AddLogin(form_dash));
493   EXPECT_TRUE(db_.GetAutofillableLogins(&result));
494   EXPECT_EQ(2U, result.size());
495   delete result[0];
496   delete result[1];
497   result.clear();
498 
499   // Match against an exact copy.
500   EXPECT_TRUE(db_.GetLogins(form, &result));
501   EXPECT_EQ(1U, result.size());
502   delete result[0];
503   result.clear();
504 
505   // www.foo.com should match.
506   PasswordForm form2 = GetFormWithNewSignonRealm(form, "http://www.foo.com/");
507   EXPECT_TRUE(db_.GetLogins(form2, &result));
508   EXPECT_EQ(1U, result.size());
509   delete result[0];
510   result.clear();
511 
512   // a.b.foo.com should match.
513   form2 = GetFormWithNewSignonRealm(form, "http://a.b.foo.com/");
514   EXPECT_TRUE(db_.GetLogins(form2, &result));
515   EXPECT_EQ(1U, result.size());
516   delete result[0];
517   result.clear();
518 
519   // a-b.foo.com should match.
520   form2 = GetFormWithNewSignonRealm(form, "http://a-b.foo.com/");
521   EXPECT_TRUE(db_.GetLogins(form2, &result));
522   EXPECT_EQ(1U, result.size());
523   delete result[0];
524   result.clear();
525 
526   // foo-bar.com should match.
527   form2 = GetFormWithNewSignonRealm(form, "http://foo-bar.com/");
528   EXPECT_TRUE(db_.GetLogins(form2, &result));
529   EXPECT_EQ(1U, result.size());
530   delete result[0];
531   result.clear();
532 
533   // www.foo-bar.com should match.
534   form2 = GetFormWithNewSignonRealm(form, "http://www.foo-bar.com/");
535   EXPECT_TRUE(db_.GetLogins(form2, &result));
536   EXPECT_EQ(1U, result.size());
537   delete result[0];
538   result.clear();
539 
540   // a.b.foo-bar.com should match.
541   form2 = GetFormWithNewSignonRealm(form, "http://a.b.foo-bar.com/");
542   EXPECT_TRUE(db_.GetLogins(form2, &result));
543   EXPECT_EQ(1U, result.size());
544   delete result[0];
545   result.clear();
546 
547   // a-b.foo-bar.com should match.
548   form2 = GetFormWithNewSignonRealm(form, "http://a-b.foo-bar.com/");
549   EXPECT_TRUE(db_.GetLogins(form2, &result));
550   EXPECT_EQ(1U, result.size());
551   delete result[0];
552   result.clear();
553 
554   // foo.com with port 1337 should not match.
555   form2 = GetFormWithNewSignonRealm(form, "http://foo.com:1337/");
556   EXPECT_TRUE(db_.GetLogins(form2, &result));
557   EXPECT_EQ(0U, result.size());
558 
559   // http://foo.com should not match since the scheme is wrong.
560   form2 = GetFormWithNewSignonRealm(form, "https://foo.com/");
561   EXPECT_TRUE(db_.GetLogins(form2, &result));
562   EXPECT_EQ(0U, result.size());
563 
564   // notfoo.com should not match.
565   form2 = GetFormWithNewSignonRealm(form, "http://notfoo.com/");
566   EXPECT_TRUE(db_.GetLogins(form2, &result));
567   EXPECT_EQ(0U, result.size());
568 
569   // baz.com should not match.
570   form2 = GetFormWithNewSignonRealm(form, "http://baz.com/");
571   EXPECT_TRUE(db_.GetLogins(form2, &result));
572   EXPECT_EQ(0U, result.size());
573 
574   // foo-baz.com should not match.
575   form2 = GetFormWithNewSignonRealm(form, "http://foo-baz.com/");
576   EXPECT_TRUE(db_.GetLogins(form2, &result));
577   EXPECT_EQ(0U, result.size());
578 }
579 
AddTimestampedLogin(LoginDatabase * db,std::string url,const std::string & unique_string,const base::Time & time,bool date_is_creation)580 static bool AddTimestampedLogin(LoginDatabase* db,
581                                 std::string url,
582                                 const std::string& unique_string,
583                                 const base::Time& time,
584                                 bool date_is_creation) {
585   // Example password form.
586   PasswordForm form;
587   form.origin = GURL(url + std::string("/LoginAuth"));
588   form.username_element = ASCIIToUTF16(unique_string);
589   form.username_value = ASCIIToUTF16(unique_string);
590   form.password_element = ASCIIToUTF16(unique_string);
591   form.submit_element = ASCIIToUTF16("signIn");
592   form.signon_realm = url;
593   if (date_is_creation)
594     form.date_created = time;
595   else
596     form.date_synced = time;
597   return db->AddLogin(form) == AddChangeForForm(form);
598 }
599 
ClearResults(std::vector<PasswordForm * > * results)600 static void ClearResults(std::vector<PasswordForm*>* results) {
601   for (size_t i = 0; i < results->size(); ++i) {
602     delete (*results)[i];
603   }
604   results->clear();
605 }
606 
TEST_F(LoginDatabaseTest,ClearPrivateData_SavedPasswords)607 TEST_F(LoginDatabaseTest, ClearPrivateData_SavedPasswords) {
608   std::vector<PasswordForm*> result;
609 
610   // Verify the database is empty.
611   EXPECT_TRUE(db_.GetAutofillableLogins(&result));
612   EXPECT_EQ(0U, result.size());
613 
614   base::Time now = base::Time::Now();
615   base::TimeDelta one_day = base::TimeDelta::FromDays(1);
616 
617   // Create one with a 0 time.
618   EXPECT_TRUE(AddTimestampedLogin(&db_, "1", "foo1", base::Time(), true));
619   // Create one for now and +/- 1 day.
620   EXPECT_TRUE(AddTimestampedLogin(&db_, "2", "foo2", now - one_day, true));
621   EXPECT_TRUE(AddTimestampedLogin(&db_, "3", "foo3", now, true));
622   EXPECT_TRUE(AddTimestampedLogin(&db_, "4", "foo4", now + one_day, true));
623 
624   // Verify inserts worked.
625   EXPECT_TRUE(db_.GetAutofillableLogins(&result));
626   EXPECT_EQ(4U, result.size());
627   ClearResults(&result);
628 
629   // Get everything from today's date and on.
630   EXPECT_TRUE(db_.GetLoginsCreatedBetween(now, base::Time(), &result));
631   EXPECT_EQ(2U, result.size());
632   ClearResults(&result);
633 
634   // Delete everything from today's date and on.
635   db_.RemoveLoginsCreatedBetween(now, base::Time());
636 
637   // Should have deleted half of what we inserted.
638   EXPECT_TRUE(db_.GetAutofillableLogins(&result));
639   EXPECT_EQ(2U, result.size());
640   ClearResults(&result);
641 
642   // Delete with 0 date (should delete all).
643   db_.RemoveLoginsCreatedBetween(base::Time(), base::Time());
644 
645   // Verify nothing is left.
646   EXPECT_TRUE(db_.GetAutofillableLogins(&result));
647   EXPECT_EQ(0U, result.size());
648 }
649 
TEST_F(LoginDatabaseTest,RemoveLoginsSyncedBetween)650 TEST_F(LoginDatabaseTest, RemoveLoginsSyncedBetween) {
651   ScopedVector<autofill::PasswordForm> result;
652 
653   base::Time now = base::Time::Now();
654   base::TimeDelta one_day = base::TimeDelta::FromDays(1);
655 
656   // Create one with a 0 time.
657   EXPECT_TRUE(AddTimestampedLogin(&db_, "1", "foo1", base::Time(), false));
658   // Create one for now and +/- 1 day.
659   EXPECT_TRUE(AddTimestampedLogin(&db_, "2", "foo2", now - one_day, false));
660   EXPECT_TRUE(AddTimestampedLogin(&db_, "3", "foo3", now, false));
661   EXPECT_TRUE(AddTimestampedLogin(&db_, "4", "foo4", now + one_day, false));
662 
663   // Verify inserts worked.
664   EXPECT_TRUE(db_.GetAutofillableLogins(&result.get()));
665   EXPECT_EQ(4U, result.size());
666   result.clear();
667 
668   // Get everything from today's date and on.
669   EXPECT_TRUE(db_.GetLoginsSyncedBetween(now, base::Time(), &result.get()));
670   ASSERT_EQ(2U, result.size());
671   EXPECT_EQ("3", result[0]->signon_realm);
672   EXPECT_EQ("4", result[1]->signon_realm);
673   result.clear();
674 
675   // Delete everything from today's date and on.
676   db_.RemoveLoginsSyncedBetween(now, base::Time());
677 
678   // Should have deleted half of what we inserted.
679   EXPECT_TRUE(db_.GetAutofillableLogins(&result.get()));
680   ASSERT_EQ(2U, result.size());
681   EXPECT_EQ("1", result[0]->signon_realm);
682   EXPECT_EQ("2", result[1]->signon_realm);
683   result.clear();
684 
685   // Delete with 0 date (should delete all).
686   db_.RemoveLoginsSyncedBetween(base::Time(), now);
687 
688   // Verify nothing is left.
689   EXPECT_TRUE(db_.GetAutofillableLogins(&result.get()));
690   EXPECT_EQ(0U, result.size());
691 }
692 
TEST_F(LoginDatabaseTest,BlacklistedLogins)693 TEST_F(LoginDatabaseTest, BlacklistedLogins) {
694   std::vector<PasswordForm*> result;
695 
696   // Verify the database is empty.
697   EXPECT_TRUE(db_.GetBlacklistLogins(&result));
698   ASSERT_EQ(0U, result.size());
699 
700   // Save a form as blacklisted.
701   PasswordForm form;
702   form.origin = GURL("http://accounts.google.com/LoginAuth");
703   form.action = GURL("http://accounts.google.com/Login");
704   form.username_element = ASCIIToUTF16("Email");
705   form.password_element = ASCIIToUTF16("Passwd");
706   form.submit_element = ASCIIToUTF16("signIn");
707   form.signon_realm = "http://www.google.com/";
708   form.ssl_valid = false;
709   form.preferred = true;
710   form.blacklisted_by_user = true;
711   form.scheme = PasswordForm::SCHEME_HTML;
712   form.date_synced = base::Time::Now();
713   EXPECT_EQ(AddChangeForForm(form), db_.AddLogin(form));
714 
715   // Get all non-blacklisted logins (should be none).
716   EXPECT_TRUE(db_.GetAutofillableLogins(&result));
717   ASSERT_EQ(0U, result.size());
718 
719   // GetLogins should give the blacklisted result.
720   EXPECT_TRUE(db_.GetLogins(form, &result));
721   ASSERT_EQ(1U, result.size());
722   FormsAreEqual(form, *result[0]);
723   ClearResults(&result);
724 
725   // So should GetAllBlacklistedLogins.
726   EXPECT_TRUE(db_.GetBlacklistLogins(&result));
727   ASSERT_EQ(1U, result.size());
728   FormsAreEqual(form, *result[0]);
729   ClearResults(&result);
730 }
731 
TEST_F(LoginDatabaseTest,VectorSerialization)732 TEST_F(LoginDatabaseTest, VectorSerialization) {
733   // Empty vector.
734   std::vector<base::string16> vec;
735   Pickle temp = SerializeVector(vec);
736   std::vector<base::string16> output = DeserializeVector(temp);
737   EXPECT_THAT(output, Eq(vec));
738 
739   // Normal data.
740   vec.push_back(ASCIIToUTF16("first"));
741   vec.push_back(ASCIIToUTF16("second"));
742   vec.push_back(ASCIIToUTF16("third"));
743 
744   temp = SerializeVector(vec);
745   output = DeserializeVector(temp);
746   EXPECT_THAT(output, Eq(vec));
747 }
748 
TEST_F(LoginDatabaseTest,UpdateIncompleteCredentials)749 TEST_F(LoginDatabaseTest, UpdateIncompleteCredentials) {
750   std::vector<autofill::PasswordForm*> result;
751   // Verify the database is empty.
752   EXPECT_TRUE(db_.GetAutofillableLogins(&result));
753   ASSERT_EQ(0U, result.size());
754 
755   // Save an incomplete form. Note that it only has a few fields set, ex. it's
756   // missing 'action', 'username_element' and 'password_element'. Such forms
757   // are sometimes inserted during import from other browsers (which may not
758   // store this info).
759   PasswordForm incomplete_form;
760   incomplete_form.origin = GURL("http://accounts.google.com/LoginAuth");
761   incomplete_form.signon_realm = "http://accounts.google.com/";
762   incomplete_form.username_value = ASCIIToUTF16("my_username");
763   incomplete_form.password_value = ASCIIToUTF16("my_password");
764   incomplete_form.ssl_valid = false;
765   incomplete_form.preferred = true;
766   incomplete_form.blacklisted_by_user = false;
767   incomplete_form.scheme = PasswordForm::SCHEME_HTML;
768   EXPECT_EQ(AddChangeForForm(incomplete_form), db_.AddLogin(incomplete_form));
769 
770   // A form on some website. It should trigger a match with the stored one.
771   PasswordForm encountered_form;
772   encountered_form.origin = GURL("http://accounts.google.com/LoginAuth");
773   encountered_form.signon_realm = "http://accounts.google.com/";
774   encountered_form.action = GURL("http://accounts.google.com/Login");
775   encountered_form.username_element = ASCIIToUTF16("Email");
776   encountered_form.password_element = ASCIIToUTF16("Passwd");
777   encountered_form.submit_element = ASCIIToUTF16("signIn");
778 
779   // Get matches for encountered_form.
780   EXPECT_TRUE(db_.GetLogins(encountered_form, &result));
781   ASSERT_EQ(1U, result.size());
782   EXPECT_EQ(incomplete_form.origin, result[0]->origin);
783   EXPECT_EQ(incomplete_form.signon_realm, result[0]->signon_realm);
784   EXPECT_EQ(incomplete_form.username_value, result[0]->username_value);
785 #if defined(OS_MACOSX) && !defined(OS_IOS)
786   // On Mac, passwords are not stored in login database, instead they're in
787   // the keychain.
788   EXPECT_TRUE(result[0]->password_value.empty());
789 #else
790   EXPECT_EQ(incomplete_form.password_value, result[0]->password_value);
791 #endif  // OS_MACOSX && !OS_IOS
792   EXPECT_TRUE(result[0]->preferred);
793   EXPECT_FALSE(result[0]->ssl_valid);
794 
795   // We should return empty 'action', 'username_element', 'password_element'
796   // and 'submit_element' as we can't be sure if the credentials were entered
797   // in this particular form on the page.
798   EXPECT_EQ(GURL(), result[0]->action);
799   EXPECT_TRUE(result[0]->username_element.empty());
800   EXPECT_TRUE(result[0]->password_element.empty());
801   EXPECT_TRUE(result[0]->submit_element.empty());
802   ClearResults(&result);
803 
804   // Let's say this login form worked. Now update the stored credentials with
805   // 'action', 'username_element', 'password_element' and 'submit_element' from
806   // the encountered form.
807   PasswordForm completed_form(incomplete_form);
808   completed_form.action = encountered_form.action;
809   completed_form.username_element = encountered_form.username_element;
810   completed_form.password_element = encountered_form.password_element;
811   completed_form.submit_element = encountered_form.submit_element;
812   EXPECT_EQ(AddChangeForForm(completed_form), db_.AddLogin(completed_form));
813   EXPECT_TRUE(db_.RemoveLogin(incomplete_form));
814 
815   // Get matches for encountered_form again.
816   EXPECT_TRUE(db_.GetLogins(encountered_form, &result));
817   ASSERT_EQ(1U, result.size());
818 
819   // This time we should have all the info available.
820   PasswordForm expected_form(completed_form);
821 #if defined(OS_MACOSX) && !defined(OS_IOS)
822   expected_form.password_value.clear();
823 #endif  // OS_MACOSX && !OS_IOS
824   EXPECT_EQ(expected_form, *result[0]);
825   ClearResults(&result);
826 }
827 
TEST_F(LoginDatabaseTest,UpdateOverlappingCredentials)828 TEST_F(LoginDatabaseTest, UpdateOverlappingCredentials) {
829   // Save an incomplete form. Note that it only has a few fields set, ex. it's
830   // missing 'action', 'username_element' and 'password_element'. Such forms
831   // are sometimes inserted during import from other browsers (which may not
832   // store this info).
833   PasswordForm incomplete_form;
834   incomplete_form.origin = GURL("http://accounts.google.com/LoginAuth");
835   incomplete_form.signon_realm = "http://accounts.google.com/";
836   incomplete_form.username_value = ASCIIToUTF16("my_username");
837   incomplete_form.password_value = ASCIIToUTF16("my_password");
838   incomplete_form.ssl_valid = false;
839   incomplete_form.preferred = true;
840   incomplete_form.blacklisted_by_user = false;
841   incomplete_form.scheme = PasswordForm::SCHEME_HTML;
842   EXPECT_EQ(AddChangeForForm(incomplete_form), db_.AddLogin(incomplete_form));
843 
844   // Save a complete version of the previous form. Both forms could exist if
845   // the user created the complete version before importing the incomplete
846   // version from a different browser.
847   PasswordForm complete_form = incomplete_form;
848   complete_form.action = GURL("http://accounts.google.com/Login");
849   complete_form.username_element = ASCIIToUTF16("username_element");
850   complete_form.password_element = ASCIIToUTF16("password_element");
851   complete_form.submit_element = ASCIIToUTF16("submit");
852 
853   // An update fails because the primary key for |complete_form| is different.
854   EXPECT_EQ(PasswordStoreChangeList(), db_.UpdateLogin(complete_form));
855   EXPECT_EQ(AddChangeForForm(complete_form), db_.AddLogin(complete_form));
856 
857   // Make sure both passwords exist.
858   ScopedVector<autofill::PasswordForm> result;
859   EXPECT_TRUE(db_.GetAutofillableLogins(&result.get()));
860   ASSERT_EQ(2U, result.size());
861   result.clear();
862 
863   // Simulate the user changing their password.
864   complete_form.password_value = ASCIIToUTF16("new_password");
865   complete_form.date_synced = base::Time::Now();
866   EXPECT_EQ(UpdateChangeForForm(complete_form), db_.UpdateLogin(complete_form));
867 
868   // Both still exist now.
869   EXPECT_TRUE(db_.GetAutofillableLogins(&result.get()));
870   ASSERT_EQ(2U, result.size());
871 
872 #if defined(OS_MACOSX) && !defined(OS_IOS)
873   // On Mac, passwords are not stored in login database, instead they're in
874   // the keychain.
875   complete_form.password_value.clear();
876   incomplete_form.password_value.clear();
877 #endif  // OS_MACOSX && !OS_IOS
878   if (result[0]->username_element.empty())
879     std::swap(result[0], result[1]);
880   EXPECT_EQ(complete_form, *result[0]);
881   EXPECT_EQ(incomplete_form, *result[1]);
882 }
883 
TEST_F(LoginDatabaseTest,DoubleAdd)884 TEST_F(LoginDatabaseTest, DoubleAdd) {
885   PasswordForm form;
886   form.origin = GURL("http://accounts.google.com/LoginAuth");
887   form.signon_realm = "http://accounts.google.com/";
888   form.username_value = ASCIIToUTF16("my_username");
889   form.password_value = ASCIIToUTF16("my_password");
890   form.ssl_valid = false;
891   form.preferred = true;
892   form.blacklisted_by_user = false;
893   form.scheme = PasswordForm::SCHEME_HTML;
894   EXPECT_EQ(AddChangeForForm(form), db_.AddLogin(form));
895 
896   // Add almost the same form again.
897   form.times_used++;
898   PasswordStoreChangeList list;
899   list.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, form));
900   list.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form));
901   EXPECT_EQ(list, db_.AddLogin(form));
902 }
903 
904 #if defined(OS_POSIX)
905 // Only the current user has permission to read the database.
906 //
907 // Only POSIX because GetPosixFilePermissions() only exists on POSIX.
908 // This tests that sql::Connection::set_restrict_to_user() was called,
909 // and that function is a noop on non-POSIX platforms in any case.
TEST_F(LoginDatabaseTest,FilePermissions)910 TEST_F(LoginDatabaseTest, FilePermissions) {
911   int mode = base::FILE_PERMISSION_MASK;
912   EXPECT_TRUE(base::GetPosixFilePermissions(file_, &mode));
913   EXPECT_EQ((mode & base::FILE_PERMISSION_USER_MASK), mode);
914 }
915 #endif  // defined(OS_POSIX)
916 
917 }  // namespace password_manager
918