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