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.
5 #include "components/password_manager/core/browser/login_database.h"
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"
20 using autofill::PasswordForm;
21 using base::ASCIIToUTF16;
24 namespace password_manager {
26 PasswordStoreChangeList AddChangeForForm(const PasswordForm& form) {
27 return PasswordStoreChangeList(1,
28 PasswordStoreChange(PasswordStoreChange::ADD,
32 PasswordStoreChangeList UpdateChangeForForm(const PasswordForm& form) {
33 return PasswordStoreChangeList(1, PasswordStoreChange(
34 PasswordStoreChange::UPDATE, form));
39 // Serialization routines for vectors implemented in login_database.cc.
40 Pickle SerializeVector(const std::vector<base::string16>& vec);
41 std::vector<base::string16> DeserializeVector(const Pickle& pickle);
43 class LoginDatabaseTest : public testing::Test {
45 virtual void SetUp() {
46 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
47 file_ = temp_dir_.path().AppendASCII("TestMetadataStoreMacDatabase");
49 ASSERT_TRUE(db_.Init(file_));
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("");
58 EXPECT_EQ(expected_copy, actual);
61 void TestNonHTMLFormPSLMatching(const PasswordForm::Scheme& scheme) {
62 ScopedVector<PasswordForm> result;
64 base::Time now = base::Time::Now();
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;
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;
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());
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";
97 // This shouldn't match anything.
98 EXPECT_TRUE(db_.GetLogins(second_non_html_auth, &result.get()));
99 EXPECT_EQ(0U, result.size());
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");
107 db_.RemoveLoginsCreatedBetween(now, base::Time());
110 base::ScopedTempDir temp_dir_;
111 base::FilePath file_;
115 TEST_F(LoginDatabaseTest, Logins) {
116 std::vector<PasswordForm*> result;
118 // Verify the database is empty.
119 EXPECT_TRUE(db_.GetAutofillableLogins(&result));
120 EXPECT_EQ(0U, result.size());
122 // Example password 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;
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;
143 // Add it and make sure it is there and that all the fields were retrieved
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]);
152 // Match against an exact copy.
153 EXPECT_TRUE(db_.GetLogins(form, &result));
154 ASSERT_EQ(1U, result.size());
155 FormsAreEqual(form, *result[0]);
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");
164 // Match against an inexact copy
165 EXPECT_TRUE(db_.GetLogins(form2, &result));
166 EXPECT_EQ(1U, result.size());
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");
174 // signon_realm is the same, should match.
175 EXPECT_TRUE(db_.GetLogins(form3, &result));
176 EXPECT_EQ(1U, result.size());
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;
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());
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());
197 // Now the match works
198 EXPECT_TRUE(db_.GetLogins(form4, &result));
199 EXPECT_EQ(1U, result.size());
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());
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());
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);
219 // It will match in this case.
220 EXPECT_TRUE(db_.GetLogins(form5, &result));
221 EXPECT_EQ(1U, result.size());
225 // User changes his password.
226 PasswordForm form6(form5);
227 form6.password_value = ASCIIToUTF16("test6");
228 form6.preferred = true;
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));
234 EXPECT_TRUE(db_.GetLogins(form5, &result));
235 EXPECT_EQ(1U, result.size());
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);
246 EXPECT_EQ(form6.password_value, result[0]->password_value);
249 EXPECT_TRUE(form6.preferred);
253 // Make sure everything can disappear.
254 EXPECT_TRUE(db_.RemoveLogin(form4));
255 EXPECT_TRUE(db_.GetAutofillableLogins(&result));
256 EXPECT_EQ(0U, result.size());
259 TEST_F(LoginDatabaseTest, TestPublicSuffixDomainMatching) {
260 std::vector<PasswordForm*> result;
262 // Verify the database is empty.
263 EXPECT_TRUE(db_.GetAutofillableLogins(&result));
264 EXPECT_EQ(0U, result.size());
266 // Example password 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;
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());
287 // Match against an exact copy.
288 EXPECT_TRUE(db_.GetLogins(form, &result));
289 EXPECT_EQ(1U, result.size());
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/";
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);
308 TEST_F(LoginDatabaseTest, TestPublicSuffixDisabledForNonHTMLForms) {
309 TestNonHTMLFormPSLMatching(PasswordForm::SCHEME_BASIC);
310 TestNonHTMLFormPSLMatching(PasswordForm::SCHEME_DIGEST);
311 TestNonHTMLFormPSLMatching(PasswordForm::SCHEME_OTHER);
314 TEST_F(LoginDatabaseTest, TestPublicSuffixDomainMatchingShouldMatchingApply) {
315 std::vector<PasswordForm*> result;
317 // Verify the database is empty.
318 EXPECT_TRUE(db_.GetAutofillableLogins(&result));
319 EXPECT_EQ(0U, result.size());
321 // Example password 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;
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());
342 // Match against an exact copy.
343 EXPECT_TRUE(db_.GetLogins(form, &result));
344 EXPECT_EQ(1U, result.size());
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/";
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());
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.
363 TEST_F(LoginDatabaseTest, TestPublicSuffixDomainMatchingDifferentSites) {
364 std::vector<PasswordForm*> result;
366 // Verify the database is empty.
367 EXPECT_TRUE(db_.GetAutofillableLogins(&result));
368 EXPECT_EQ(0U, result.size());
370 // Example password 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;
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());
391 // Match against an exact copy.
392 EXPECT_TRUE(db_.GetLogins(form, &result));
393 EXPECT_EQ(1U, result.size());
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/";
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);
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;
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());
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/";
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);
447 PasswordForm 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;
456 TEST_F(LoginDatabaseTest, TestPublicSuffixDomainMatchingRegexp) {
457 std::vector<PasswordForm*> result;
459 // Verify the database is empty.
460 EXPECT_TRUE(db_.GetAutofillableLogins(&result));
461 EXPECT_EQ(0U, result.size());
463 // Example password 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;
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());
484 // Example password form that has - in the domain name.
485 PasswordForm form_dash =
486 GetFormWithNewSignonRealm(form, "http://www.foo-bar.com/");
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());
496 // Match against an exact copy.
497 EXPECT_TRUE(db_.GetLogins(form, &result));
498 EXPECT_EQ(1U, result.size());
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());
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());
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());
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());
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());
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());
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());
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());
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());
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());
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());
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());
577 static bool AddTimestampedLogin(LoginDatabase* db,
579 const std::string& unique_string,
580 const base::Time& time,
581 bool date_is_creation) {
582 // Example password 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;
595 if (date_is_creation)
596 form.date_created = time;
598 form.date_synced = time;
599 return db->AddLogin(form) == AddChangeForForm(form);
602 static void ClearResults(std::vector<PasswordForm*>* results) {
603 for (size_t i = 0; i < results->size(); ++i) {
604 delete (*results)[i];
609 TEST_F(LoginDatabaseTest, ClearPrivateData_SavedPasswords) {
610 std::vector<PasswordForm*> result;
612 // Verify the database is empty.
613 EXPECT_TRUE(db_.GetAutofillableLogins(&result));
614 EXPECT_EQ(0U, result.size());
616 base::Time now = base::Time::Now();
617 base::TimeDelta one_day = base::TimeDelta::FromDays(1);
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));
626 // Verify inserts worked.
627 EXPECT_TRUE(db_.GetAutofillableLogins(&result));
628 EXPECT_EQ(4U, result.size());
629 ClearResults(&result);
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);
636 // Delete everything from today's date and on.
637 db_.RemoveLoginsCreatedBetween(now, base::Time());
639 // Should have deleted half of what we inserted.
640 EXPECT_TRUE(db_.GetAutofillableLogins(&result));
641 EXPECT_EQ(2U, result.size());
642 ClearResults(&result);
644 // Delete with 0 date (should delete all).
645 db_.RemoveLoginsCreatedBetween(base::Time(), base::Time());
647 // Verify nothing is left.
648 EXPECT_TRUE(db_.GetAutofillableLogins(&result));
649 EXPECT_EQ(0U, result.size());
652 TEST_F(LoginDatabaseTest, RemoveLoginsSyncedBetween) {
653 ScopedVector<autofill::PasswordForm> result;
655 base::Time now = base::Time::Now();
656 base::TimeDelta one_day = base::TimeDelta::FromDays(1);
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));
665 // Verify inserts worked.
666 EXPECT_TRUE(db_.GetAutofillableLogins(&result.get()));
667 EXPECT_EQ(4U, result.size());
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);
677 // Delete everything from today's date and on.
678 db_.RemoveLoginsSyncedBetween(now, base::Time());
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);
687 // Delete with 0 date (should delete all).
688 db_.RemoveLoginsSyncedBetween(base::Time(), now);
690 // Verify nothing is left.
691 EXPECT_TRUE(db_.GetAutofillableLogins(&result.get()));
692 EXPECT_EQ(0U, result.size());
695 TEST_F(LoginDatabaseTest, BlacklistedLogins) {
696 std::vector<PasswordForm*> result;
698 // Verify the database is empty.
699 EXPECT_TRUE(db_.GetBlacklistLogins(&result));
700 ASSERT_EQ(0U, result.size());
702 // Save a form as blacklisted.
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));
721 // Get all non-blacklisted logins (should be none).
722 EXPECT_TRUE(db_.GetAutofillableLogins(&result));
723 ASSERT_EQ(0U, result.size());
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);
731 // So should GetAllBlacklistedLogins.
732 EXPECT_TRUE(db_.GetBlacklistLogins(&result));
733 ASSERT_EQ(1U, result.size());
734 FormsAreEqual(form, *result[0]);
735 ClearResults(&result);
738 TEST_F(LoginDatabaseTest, VectorSerialization) {
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));
746 vec.push_back(ASCIIToUTF16("first"));
747 vec.push_back(ASCIIToUTF16("second"));
748 vec.push_back(ASCIIToUTF16("third"));
750 temp = SerializeVector(vec);
751 output = DeserializeVector(temp);
752 EXPECT_THAT(output, Eq(vec));
755 TEST_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());
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
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));
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");
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
794 EXPECT_TRUE(result[0]->password_value.empty());
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);
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);
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));
821 // Get matches for encountered_form again.
822 EXPECT_TRUE(db_.GetLogins(encountered_form, &result));
823 ASSERT_EQ(1U, result.size());
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);
834 TEST_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
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));
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");
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));
863 // Make sure both passwords exist.
864 ScopedVector<autofill::PasswordForm> result;
865 EXPECT_TRUE(db_.GetAutofillableLogins(&result.get()));
866 ASSERT_EQ(2U, result.size());
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));
874 // Both still exist now.
875 EXPECT_TRUE(db_.GetAutofillableLogins(&result.get()));
876 ASSERT_EQ(2U, result.size());
878 #if defined(OS_MACOSX) && !defined(OS_IOS)
879 // On Mac, passwords are not stored in login database, instead they're in
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]);
890 TEST_F(LoginDatabaseTest, DoubleAdd) {
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));
902 // Add almost the same form again.
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));
910 TEST_F(LoginDatabaseTest, UpdateLogin) {
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));
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));
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
948 form.password_value.clear();
949 #endif // OS_MACOSX && !OS_IOS
950 EXPECT_EQ(form, *result[0]);
953 #if defined(OS_POSIX)
954 // Only the current user has permission to read the database.
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.
959 TEST_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);
964 #endif // defined(OS_POSIX)
966 } // namespace password_manager