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