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 {
AddChangeForForm(const PasswordForm & form)26 PasswordStoreChangeList AddChangeForForm(const PasswordForm& form) {
27 return PasswordStoreChangeList(1,
28 PasswordStoreChange(PasswordStoreChange::ADD,
29 form));
30 }
31
UpdateChangeForForm(const PasswordForm & form)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:
SetUp()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
FormsAreEqual(const PasswordForm & expected,const PasswordForm & actual)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
TestNonHTMLFormPSLMatching(const PasswordForm::Scheme & scheme)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
TEST_F(LoginDatabaseTest,Logins)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@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
TEST_F(LoginDatabaseTest,TestPublicSuffixDomainMatching)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@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
TEST_F(LoginDatabaseTest,TestPublicSuffixDisabledForNonHTMLForms)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
TEST_F(LoginDatabaseTest,TestPublicSuffixDomainMatchingShouldMatchingApply)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@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.
TEST_F(LoginDatabaseTest,TestPublicSuffixDomainMatchingDifferentSites)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@gmail.com");
378 form.password_element = ASCIIToUTF16("password");
379 form.password_value = ASCIIToUTF16("test");
380 form.submit_element = ASCIIToUTF16("");
381 form.signon_realm = "https://foo.com/";
382 form.ssl_valid = true;
383 form.preferred = false;
384 form.scheme = PasswordForm::SCHEME_HTML;
385
386 // Add it and make sure it is there.
387 EXPECT_EQ(AddChangeForForm(form), db_.AddLogin(form));
388 EXPECT_TRUE(db_.GetAutofillableLogins(&result));
389 EXPECT_EQ(1U, result.size());
390 delete result[0];
391 result.clear();
392
393 // Match against an exact copy.
394 EXPECT_TRUE(db_.GetLogins(form, &result));
395 EXPECT_EQ(1U, result.size());
396 delete result[0];
397 result.clear();
398
399 // We go to the mobile site.
400 PasswordForm form2(form);
401 form2.origin = GURL("https://mobile.foo.com/");
402 form2.action = GURL("https://mobile.foo.com/login");
403 form2.signon_realm = "https://mobile.foo.com/";
404
405 // Match against the mobile site.
406 EXPECT_TRUE(db_.GetLogins(form2, &result));
407 EXPECT_EQ(1U, result.size());
408 EXPECT_EQ("https://mobile.foo.com/", result[0]->signon_realm);
409 EXPECT_EQ("https://foo.com/", result[0]->original_signon_realm);
410 delete result[0];
411 result.clear();
412
413 // Add baz.com desktop site.
414 form.origin = GURL("https://baz.com/login/");
415 form.action = GURL("https://baz.com/login/");
416 form.username_element = ASCIIToUTF16("email");
417 form.username_value = ASCIIToUTF16("test@gmail.com");
418 form.password_element = ASCIIToUTF16("password");
419 form.password_value = ASCIIToUTF16("test");
420 form.submit_element = ASCIIToUTF16("");
421 form.signon_realm = "https://baz.com/";
422 form.ssl_valid = true;
423 form.preferred = false;
424 form.scheme = PasswordForm::SCHEME_HTML;
425
426 // Add it and make sure it is there.
427 EXPECT_EQ(AddChangeForForm(form), db_.AddLogin(form));
428 EXPECT_TRUE(db_.GetAutofillableLogins(&result));
429 EXPECT_EQ(2U, result.size());
430 delete result[0];
431 delete result[1];
432 result.clear();
433
434 // We go to the mobile site of baz.com.
435 PasswordForm form3(form);
436 form3.origin = GURL("https://m.baz.com/login/");
437 form3.action = GURL("https://m.baz.com/login/");
438 form3.signon_realm = "https://m.baz.com/";
439
440 // Match against the mobile site of baz.com.
441 EXPECT_TRUE(db_.GetLogins(form3, &result));
442 EXPECT_EQ(1U, result.size());
443 EXPECT_EQ("https://m.baz.com/", result[0]->signon_realm);
444 EXPECT_EQ("https://baz.com/", result[0]->original_signon_realm);
445 delete result[0];
446 result.clear();
447 }
448
GetFormWithNewSignonRealm(PasswordForm form,std::string signon_realm)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
TEST_F(LoginDatabaseTest,TestPublicSuffixDomainMatchingRegexp)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@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
AddTimestampedLogin(LoginDatabase * db,std::string url,const std::string & unique_string,const base::Time & time,bool date_is_creation)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
ClearResults(std::vector<PasswordForm * > * results)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
TEST_F(LoginDatabaseTest,ClearPrivateData_SavedPasswords)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
TEST_F(LoginDatabaseTest,RemoveLoginsSyncedBetween)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
TEST_F(LoginDatabaseTest,BlacklistedLogins)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
TEST_F(LoginDatabaseTest,VectorSerialization)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
TEST_F(LoginDatabaseTest,UpdateIncompleteCredentials)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
TEST_F(LoginDatabaseTest,UpdateOverlappingCredentials)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
TEST_F(LoginDatabaseTest,DoubleAdd)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.
TEST_F(LoginDatabaseTest,FilePermissions)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