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