login_database_unittest.cc revision f8ee788a64d60abd8f2d742a5fdedde054ecd910
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
20using autofill::PasswordForm;
21using base::ASCIIToUTF16;
22using ::testing::Eq;
23
24namespace password_manager {
25namespace {
26PasswordStoreChangeList AddChangeForForm(const PasswordForm& form) {
27  return PasswordStoreChangeList(1,
28                                 PasswordStoreChange(PasswordStoreChange::ADD,
29                                                     form));
30}
31
32PasswordStoreChangeList 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.
40Pickle SerializeVector(const std::vector<base::string16>& vec);
41std::vector<base::string16> DeserializeVector(const Pickle& pickle);
42
43class LoginDatabaseTest : public testing::Test {
44 protected:
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
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
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
115TEST_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
256TEST_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
306TEST_F(LoginDatabaseTest, TestPublicSuffixDisabledForNonHTMLForms) {
307  PSLMatchingHelper::EnablePublicSuffixDomainMatchingForTesting();
308
309  TestNonHTMLFormPSLMatching(PasswordForm::SCHEME_BASIC);
310  TestNonHTMLFormPSLMatching(PasswordForm::SCHEME_DIGEST);
311  TestNonHTMLFormPSLMatching(PasswordForm::SCHEME_OTHER);
312}
313
314TEST_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.
364TEST_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
449PasswordForm 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
458TEST_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
580static 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
600static 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
607TEST_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
650TEST_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
693TEST_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
732TEST_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
749TEST_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
828TEST_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
884TEST_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.
910TEST_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