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 <stddef.h>
8 #include <stdint.h>
9
10 #include <memory>
11 #include <utility>
12
13 #include "base/files/file_util.h"
14 #include "base/files/scoped_temp_dir.h"
15 #include "base/path_service.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/test/metrics/histogram_tester.h"
20 #include "base/test/scoped_feature_list.h"
21 #include "base/test/task_environment.h"
22 #include "base/threading/thread_task_runner_handle.h"
23 #include "base/time/time.h"
24 #include "build/build_config.h"
25 #include "components/autofill/core/common/password_form.h"
26 #include "components/os_crypt/os_crypt.h"
27 #include "components/os_crypt/os_crypt_mocker.h"
28 #include "components/password_manager/core/browser/password_manager_test_utils.h"
29 #include "components/password_manager/core/browser/psl_matching_helper.h"
30 #include "components/password_manager/core/common/password_manager_features.h"
31 #include "components/password_manager/core/common/password_manager_pref_names.h"
32 #include "components/prefs/pref_registry_simple.h"
33 #include "components/prefs/testing_pref_service.h"
34 #include "sql/database.h"
35 #include "sql/statement.h"
36 #include "sql/test/test_helpers.h"
37 #include "testing/gmock/include/gmock/gmock.h"
38 #include "testing/gtest/include/gtest/gtest.h"
39 #include "url/origin.h"
40
41 using autofill::PasswordForm;
42 using autofill::ValueElementPair;
43 using autofill::ValueElementVector;
44 using base::ASCIIToUTF16;
45 using base::UTF16ToASCII;
46 using ::testing::Eq;
47 using ::testing::Ne;
48 using ::testing::Pointee;
49 using ::testing::SizeIs;
50 using ::testing::UnorderedElementsAre;
51
52 namespace password_manager {
53 namespace {
54
AddChangeForForm(const PasswordForm & form)55 PasswordStoreChangeList AddChangeForForm(const PasswordForm& form) {
56 return PasswordStoreChangeList(
57 1, PasswordStoreChange(PasswordStoreChange::ADD, form));
58 }
59
UpdateChangeForForm(const PasswordForm & form,const bool password_changed)60 PasswordStoreChangeList UpdateChangeForForm(const PasswordForm& form,
61 const bool password_changed) {
62 return PasswordStoreChangeList(
63 1,
64 PasswordStoreChange(PasswordStoreChange::UPDATE, form, password_changed));
65 }
66
RemoveChangeForForm(const PasswordForm & form)67 PasswordStoreChangeList RemoveChangeForForm(const PasswordForm& form) {
68 return PasswordStoreChangeList(
69 1, PasswordStoreChange(PasswordStoreChange::REMOVE, form));
70 }
71
GenerateExamplePasswordForm(PasswordForm * form)72 void GenerateExamplePasswordForm(PasswordForm* form) {
73 form->origin = GURL("http://accounts.google.com/LoginAuth");
74 form->action = GURL("http://accounts.google.com/Login");
75 form->username_element = ASCIIToUTF16("Email");
76 form->username_value = ASCIIToUTF16("test@gmail.com");
77 form->password_element = ASCIIToUTF16("Passwd");
78 form->password_value = ASCIIToUTF16("test");
79 form->submit_element = ASCIIToUTF16("signIn");
80 form->signon_realm = "http://www.google.com/";
81 form->scheme = PasswordForm::Scheme::kHtml;
82 form->times_used = 1;
83 form->form_data.name = ASCIIToUTF16("form_name");
84 form->date_synced = base::Time::Now();
85 form->date_last_used = base::Time::Now();
86 form->display_name = ASCIIToUTF16("Mr. Smith");
87 form->icon_url = GURL("https://accounts.google.com/Icon");
88 form->federation_origin =
89 url::Origin::Create(GURL("https://accounts.google.com/"));
90 form->skip_zero_click = true;
91 form->in_store = PasswordForm::Store::kProfileStore;
92 }
93
94 // Helper functions to read the value of the first column of an executed
95 // statement if we know its type. You must implement a specialization for
96 // every column type you use.
97 template <class T>
98 struct must_be_specialized {
99 static const bool is_specialized = false;
100 };
101
102 template <class T>
GetFirstColumn(const sql::Statement & s)103 T GetFirstColumn(const sql::Statement& s) {
104 static_assert(must_be_specialized<T>::is_specialized,
105 "Implement a specialization.");
106 }
107
108 template <>
GetFirstColumn(const sql::Statement & s)109 int64_t GetFirstColumn(const sql::Statement& s) {
110 return s.ColumnInt64(0);
111 }
112
113 template <>
GetFirstColumn(const sql::Statement & s)114 std::string GetFirstColumn(const sql::Statement& s) {
115 return s.ColumnString(0);
116 }
117
118 // Returns an empty vector on failure. Otherwise returns values in the column
119 // |column_name| of the logins table. The order of the
120 // returned rows is well-defined.
121 template <class T>
GetColumnValuesFromDatabase(const base::FilePath & database_path,const std::string & column_name)122 std::vector<T> GetColumnValuesFromDatabase(const base::FilePath& database_path,
123 const std::string& column_name) {
124 sql::Database db;
125 std::vector<T> results;
126 CHECK(db.Open(database_path));
127
128 std::string statement = base::StringPrintf(
129 "SELECT %s FROM logins ORDER BY username_value, %s DESC",
130 column_name.c_str(), column_name.c_str());
131 sql::Statement s(db.GetCachedStatement(SQL_FROM_HERE, statement.c_str()));
132 EXPECT_TRUE(s.is_valid());
133
134 while (s.Step())
135 results.push_back(GetFirstColumn<T>(s));
136
137 return results;
138 }
139
AddZeroClickableLogin(LoginDatabase * db,const std::string & unique_string,const GURL & origin)140 bool AddZeroClickableLogin(LoginDatabase* db,
141 const std::string& unique_string,
142 const GURL& origin) {
143 // Example password form.
144 PasswordForm form;
145 form.origin = origin;
146 form.username_element = ASCIIToUTF16(unique_string);
147 form.username_value = ASCIIToUTF16(unique_string);
148 form.password_element = ASCIIToUTF16(unique_string);
149 form.submit_element = ASCIIToUTF16("signIn");
150 form.signon_realm = form.origin.spec();
151 form.display_name = ASCIIToUTF16(unique_string);
152 form.icon_url = origin;
153 form.federation_origin = url::Origin::Create(origin);
154 form.date_created = base::Time::Now();
155
156 form.skip_zero_click = false;
157
158 return db->AddLogin(form) == AddChangeForForm(form);
159 }
160
161 MATCHER(IsGoogle1Account, "") {
162 return arg.origin.spec() == "https://accounts.google.com/ServiceLogin" &&
163 arg.action.spec() == "https://accounts.google.com/ServiceLoginAuth" &&
164 arg.username_value == ASCIIToUTF16("theerikchen") &&
165 arg.scheme == PasswordForm::Scheme::kHtml;
166 }
167
168 MATCHER(IsGoogle2Account, "") {
169 return arg.origin.spec() == "https://accounts.google.com/ServiceLogin" &&
170 arg.action.spec() == "https://accounts.google.com/ServiceLoginAuth" &&
171 arg.username_value == ASCIIToUTF16("theerikchen2") &&
172 arg.scheme == PasswordForm::Scheme::kHtml;
173 }
174
175 MATCHER(IsBasicAuthAccount, "") {
176 return arg.scheme == PasswordForm::Scheme::kBasic;
177 }
178
179 } // namespace
180
181 // Serialization routines for vectors implemented in login_database.cc.
182 base::Pickle SerializeValueElementPairs(const ValueElementVector& vec);
183 ValueElementVector DeserializeValueElementPairs(const base::Pickle& pickle);
184
185 class LoginDatabaseTest : public testing::Test {
186 protected:
SetUp()187 void SetUp() override {
188 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
189 file_ = temp_dir_.GetPath().AppendASCII("TestMetadataStoreMacDatabase");
190 OSCryptMocker::SetUp();
191
192 db_.reset(new LoginDatabase(file_, IsAccountStore(false)));
193 ASSERT_TRUE(db_->Init());
194 }
195
TearDown()196 void TearDown() override { OSCryptMocker::TearDown(); }
197
db()198 LoginDatabase& db() { return *db_; }
199
TestNonHTMLFormPSLMatching(const PasswordForm::Scheme & scheme)200 void TestNonHTMLFormPSLMatching(const PasswordForm::Scheme& scheme) {
201 std::vector<std::unique_ptr<PasswordForm>> result;
202
203 base::Time now = base::Time::Now();
204
205 // Simple non-html auth form.
206 PasswordForm non_html_auth;
207 non_html_auth.origin = GURL("http://example.com");
208 non_html_auth.username_value = ASCIIToUTF16("test@gmail.com");
209 non_html_auth.password_value = ASCIIToUTF16("test");
210 non_html_auth.signon_realm = "http://example.com/Realm";
211 non_html_auth.scheme = scheme;
212 non_html_auth.date_created = now;
213
214 // Simple password form.
215 PasswordForm html_form(non_html_auth);
216 html_form.action = GURL("http://example.com/login");
217 html_form.username_element = ASCIIToUTF16("username");
218 html_form.username_value = ASCIIToUTF16("test2@gmail.com");
219 html_form.password_element = ASCIIToUTF16("password");
220 html_form.submit_element = ASCIIToUTF16("");
221 html_form.signon_realm = "http://example.com/";
222 html_form.scheme = PasswordForm::Scheme::kHtml;
223 html_form.date_created = now;
224
225 // Add them and make sure they are there.
226 EXPECT_EQ(AddChangeForForm(non_html_auth), db().AddLogin(non_html_auth));
227 EXPECT_EQ(AddChangeForForm(html_form), db().AddLogin(html_form));
228 EXPECT_TRUE(db().GetAutofillableLogins(&result));
229 EXPECT_EQ(2U, result.size());
230 result.clear();
231
232 PasswordStore::FormDigest second_non_html_auth = {
233 scheme, "http://second.example.com/Realm",
234 GURL("http://second.example.com")};
235
236 // This shouldn't match anything.
237 EXPECT_TRUE(db().GetLogins(second_non_html_auth, &result));
238 EXPECT_EQ(0U, result.size());
239
240 // non-html auth still matches against itself.
241 EXPECT_TRUE(
242 db().GetLogins(PasswordStore::FormDigest(non_html_auth), &result));
243 ASSERT_EQ(1U, result.size());
244 EXPECT_EQ(result[0]->signon_realm, "http://example.com/Realm");
245
246 // Clear state.
247 db().RemoveLoginsCreatedBetween(now, base::Time(), /*changes=*/nullptr);
248 }
249
250 // Checks that a form of a given |scheme|, once stored, can be successfully
251 // retrieved from the database.
TestRetrievingIPAddress(const PasswordForm::Scheme & scheme)252 void TestRetrievingIPAddress(const PasswordForm::Scheme& scheme) {
253 SCOPED_TRACE(testing::Message() << "scheme = " << scheme);
254 std::vector<std::unique_ptr<PasswordForm>> result;
255
256 base::Time now = base::Time::Now();
257 std::string origin("http://56.7.8.90");
258
259 PasswordForm ip_form;
260 ip_form.origin = GURL(origin);
261 ip_form.username_value = ASCIIToUTF16("test@gmail.com");
262 ip_form.password_value = ASCIIToUTF16("test");
263 ip_form.signon_realm = origin;
264 ip_form.scheme = scheme;
265 ip_form.date_created = now;
266
267 EXPECT_EQ(AddChangeForForm(ip_form), db().AddLogin(ip_form));
268 EXPECT_TRUE(db().GetLogins(PasswordStore::FormDigest(ip_form), &result));
269 ASSERT_EQ(1U, result.size());
270 EXPECT_EQ(result[0]->signon_realm, origin);
271
272 // Clear state.
273 db().RemoveLoginsCreatedBetween(now, base::Time(), /*changes=*/nullptr);
274 }
275
276 base::ScopedTempDir temp_dir_;
277 base::FilePath file_;
278 std::unique_ptr<LoginDatabase> db_;
279 base::test::TaskEnvironment task_environment_;
280 };
281
TEST_F(LoginDatabaseTest,Logins)282 TEST_F(LoginDatabaseTest, Logins) {
283 std::vector<std::unique_ptr<PasswordForm>> result;
284 PrimaryKeyToFormMap key_to_form_map;
285
286 // Verify the database is empty.
287 EXPECT_TRUE(db().GetAutofillableLogins(&result));
288 EXPECT_EQ(0U, result.size());
289 EXPECT_TRUE(db().IsEmpty());
290
291 EXPECT_EQ(db().GetAllLogins(&key_to_form_map), FormRetrievalResult::kSuccess);
292 EXPECT_EQ(0U, key_to_form_map.size());
293
294 // Example password form.
295 PasswordForm form;
296 GenerateExamplePasswordForm(&form);
297
298 // Add it and make sure it is there and that all the fields were retrieved
299 // correctly.
300 PasswordStoreChangeList changes = db().AddLogin(form);
301 ASSERT_EQ(AddChangeForForm(form), changes);
302 EXPECT_EQ(1, changes[0].primary_key());
303 EXPECT_TRUE(db().GetAutofillableLogins(&result));
304 ASSERT_EQ(1U, result.size());
305 EXPECT_EQ(form, *result[0]);
306 EXPECT_FALSE(db().IsEmpty());
307 result.clear();
308
309 EXPECT_EQ(db().GetAllLogins(&key_to_form_map), FormRetrievalResult::kSuccess);
310 EXPECT_EQ(1U, key_to_form_map.size());
311 EXPECT_EQ(form, *key_to_form_map[1]);
312 key_to_form_map.clear();
313
314 // Match against an exact copy.
315 EXPECT_TRUE(db().GetLogins(PasswordStore::FormDigest(form), &result));
316 ASSERT_EQ(1U, result.size());
317 EXPECT_EQ(form, *result[0]);
318 result.clear();
319
320 // The example site changes...
321 PasswordForm form2(form);
322 form2.origin = GURL("http://www.google.com/new/accounts/LoginAuth");
323 form2.submit_element = ASCIIToUTF16("reallySignIn");
324
325 // Match against an inexact copy
326 EXPECT_TRUE(db().GetLogins(PasswordStore::FormDigest(form2), &result));
327 EXPECT_EQ(1U, result.size());
328 result.clear();
329
330 // Uh oh, the site changed origin & action URLs all at once!
331 PasswordForm form3(form2);
332 form3.action = GURL("http://www.google.com/new/accounts/Login");
333
334 // signon_realm is the same, should match.
335 EXPECT_TRUE(db().GetLogins(PasswordStore::FormDigest(form3), &result));
336 EXPECT_EQ(1U, result.size());
337 result.clear();
338
339 // Imagine the site moves to a secure server for login.
340 PasswordForm form4(form3);
341 form4.signon_realm = "https://www.google.com/";
342
343 // We have only an http record, so no match for this.
344 EXPECT_TRUE(db().GetLogins(PasswordStore::FormDigest(form4), &result));
345 EXPECT_EQ(0U, result.size());
346
347 // Let's imagine the user logs into the secure site.
348 changes = db().AddLogin(form4);
349 ASSERT_EQ(AddChangeForForm(form4), changes);
350 EXPECT_EQ(2, changes[0].primary_key());
351 EXPECT_TRUE(db().GetAutofillableLogins(&result));
352 EXPECT_EQ(2U, result.size());
353 result.clear();
354
355 // Now the match works
356 EXPECT_TRUE(db().GetLogins(PasswordStore::FormDigest(form4), &result));
357 EXPECT_EQ(1U, result.size());
358 result.clear();
359
360 // The user chose to forget the original but not the new.
361 EXPECT_TRUE(db().RemoveLogin(form, &changes));
362 ASSERT_EQ(1U, changes.size());
363 EXPECT_EQ(1, changes[0].primary_key());
364 EXPECT_TRUE(db().GetAutofillableLogins(&result));
365 EXPECT_EQ(1U, result.size());
366 result.clear();
367
368 // The old form wont match the new site (http vs https).
369 EXPECT_TRUE(db().GetLogins(PasswordStore::FormDigest(form), &result));
370 EXPECT_EQ(0U, result.size());
371
372 // User changes their password.
373 PasswordForm form5(form4);
374 form5.password_value = ASCIIToUTF16("test6");
375 const base::Time kNow = base::Time::Now();
376 form5.date_last_used = kNow;
377
378 // We update, and check to make sure it matches the
379 // old form, and there is only one record.
380 EXPECT_EQ(UpdateChangeForForm(form5, /*passwordchanged=*/true),
381 db().UpdateLogin(form5));
382 // matches
383 EXPECT_TRUE(db().GetLogins(PasswordStore::FormDigest(form5), &result));
384 EXPECT_EQ(1U, result.size());
385 result.clear();
386 // Only one record.
387 EXPECT_TRUE(db().GetAutofillableLogins(&result));
388 EXPECT_EQ(1U, result.size());
389 // Password element was updated.
390 EXPECT_EQ(form5.password_value, result[0]->password_value);
391 // Date last used.
392 EXPECT_EQ(kNow, form5.date_last_used);
393 result.clear();
394
395 // Make sure everything can disappear.
396 EXPECT_TRUE(db().RemoveLogin(form4, &changes));
397 ASSERT_EQ(1U, changes.size());
398 EXPECT_EQ(2, changes[0].primary_key());
399 EXPECT_TRUE(db().GetAutofillableLogins(&result));
400 EXPECT_EQ(0U, result.size());
401 EXPECT_TRUE(db().IsEmpty());
402 }
403
TEST_F(LoginDatabaseTest,AddLoginReturnsPrimaryKey)404 TEST_F(LoginDatabaseTest, AddLoginReturnsPrimaryKey) {
405 std::vector<std::unique_ptr<PasswordForm>> result;
406
407 // Verify the database is empty.
408 EXPECT_TRUE(db().GetAutofillableLogins(&result));
409 EXPECT_EQ(0U, result.size());
410
411 // Example password form.
412 PasswordForm form;
413 GenerateExamplePasswordForm(&form);
414
415 // Add it and make sure the primary key is returned in the
416 // PasswordStoreChange.
417 PasswordStoreChangeList change_list = db().AddLogin(form);
418 ASSERT_EQ(1U, change_list.size());
419 EXPECT_EQ(AddChangeForForm(form), change_list);
420 EXPECT_EQ(1, change_list[0].primary_key());
421 }
422
TEST_F(LoginDatabaseTest,RemoveLoginsByPrimaryKey)423 TEST_F(LoginDatabaseTest, RemoveLoginsByPrimaryKey) {
424 std::vector<std::unique_ptr<PasswordForm>> result;
425
426 // Verify the database is empty.
427 EXPECT_TRUE(db().GetAutofillableLogins(&result));
428 EXPECT_EQ(0U, result.size());
429
430 // Example password form.
431 PasswordForm form;
432 GenerateExamplePasswordForm(&form);
433
434 // Add it and make sure it is there and that all the fields were retrieved
435 // correctly.
436 PasswordStoreChangeList change_list = db().AddLogin(form);
437 ASSERT_EQ(1U, change_list.size());
438 int primary_key = change_list[0].primary_key();
439 EXPECT_EQ(AddChangeForForm(form), change_list);
440 EXPECT_TRUE(db().GetAutofillableLogins(&result));
441 ASSERT_EQ(1U, result.size());
442 EXPECT_EQ(form, *result[0]);
443 result.clear();
444
445 // RemoveLoginByPrimaryKey() doesn't decrypt or fill the password value.
446 form.password_value = ASCIIToUTF16("");
447
448 EXPECT_TRUE(db().RemoveLoginByPrimaryKey(primary_key, &change_list));
449 EXPECT_EQ(RemoveChangeForForm(form), change_list);
450 EXPECT_TRUE(db().GetAutofillableLogins(&result));
451 EXPECT_EQ(0U, result.size());
452 }
453
TEST_F(LoginDatabaseTest,ShouldNotRecyclePrimaryKeys)454 TEST_F(LoginDatabaseTest, ShouldNotRecyclePrimaryKeys) {
455 std::vector<std::unique_ptr<PasswordForm>> result;
456
457 // Example password form.
458 PasswordForm form;
459 GenerateExamplePasswordForm(&form);
460
461 // Add the form.
462 PasswordStoreChangeList change_list = db().AddLogin(form);
463 ASSERT_EQ(1U, change_list.size());
464 int primary_key1 = change_list[0].primary_key();
465 change_list.clear();
466 // Delete the form
467 EXPECT_TRUE(db().RemoveLoginByPrimaryKey(primary_key1, &change_list));
468 ASSERT_EQ(1U, change_list.size());
469 // Add it again.
470 change_list = db().AddLogin(form);
471 ASSERT_EQ(1U, change_list.size());
472 EXPECT_NE(primary_key1, change_list[0].primary_key());
473 }
474
TEST_F(LoginDatabaseTest,TestPublicSuffixDomainMatching)475 TEST_F(LoginDatabaseTest, TestPublicSuffixDomainMatching) {
476 std::vector<std::unique_ptr<PasswordForm>> result;
477
478 // Verify the database is empty.
479 EXPECT_TRUE(db().GetAutofillableLogins(&result));
480 EXPECT_EQ(0U, result.size());
481
482 // Example password form.
483 PasswordForm form;
484 form.origin = GURL("https://foo.com/");
485 form.action = GURL("https://foo.com/login");
486 form.username_element = ASCIIToUTF16("username");
487 form.username_value = ASCIIToUTF16("test@gmail.com");
488 form.password_element = ASCIIToUTF16("password");
489 form.password_value = ASCIIToUTF16("test");
490 form.submit_element = ASCIIToUTF16("");
491 form.signon_realm = "https://foo.com/";
492 form.scheme = PasswordForm::Scheme::kHtml;
493
494 // Add it and make sure it is there.
495 EXPECT_EQ(AddChangeForForm(form), db().AddLogin(form));
496 EXPECT_TRUE(db().GetAutofillableLogins(&result));
497 EXPECT_EQ(1U, result.size());
498 result.clear();
499
500 // Match against an exact copy.
501 EXPECT_TRUE(db().GetLogins(PasswordStore::FormDigest(form), &result));
502 EXPECT_EQ(1U, result.size());
503 result.clear();
504
505 // We go to the mobile site.
506 PasswordForm form2(form);
507 form2.origin = GURL("https://mobile.foo.com/");
508 form2.action = GURL("https://mobile.foo.com/login");
509 form2.signon_realm = "https://mobile.foo.com/";
510
511 // Match against the mobile site.
512 EXPECT_TRUE(db().GetLogins(PasswordStore::FormDigest(form2), &result));
513 EXPECT_EQ(1U, result.size());
514 EXPECT_EQ("https://foo.com/", result[0]->signon_realm);
515 EXPECT_TRUE(result[0]->is_public_suffix_match);
516 }
517
TEST_F(LoginDatabaseTest,TestFederatedMatching)518 TEST_F(LoginDatabaseTest, TestFederatedMatching) {
519 std::vector<std::unique_ptr<PasswordForm>> result;
520
521 // Example password form.
522 PasswordForm form;
523 form.origin = GURL("https://foo.com/");
524 form.action = GURL("https://foo.com/login");
525 form.username_value = ASCIIToUTF16("test@gmail.com");
526 form.password_value = ASCIIToUTF16("test");
527 form.signon_realm = "https://foo.com/";
528 form.scheme = PasswordForm::Scheme::kHtml;
529
530 // We go to the mobile site.
531 PasswordForm form2(form);
532 form2.origin = GURL("https://mobile.foo.com/");
533 form2.action = GURL("https://mobile.foo.com/login");
534 form2.signon_realm = "federation://mobile.foo.com/accounts.google.com";
535 form2.username_value = ASCIIToUTF16("test1@gmail.com");
536 form2.type = PasswordForm::Type::kApi;
537 form2.federation_origin =
538 url::Origin::Create(GURL("https://accounts.google.com/"));
539
540 // Add it and make sure it is there.
541 EXPECT_EQ(AddChangeForForm(form), db().AddLogin(form));
542 EXPECT_EQ(AddChangeForForm(form2), db().AddLogin(form2));
543 EXPECT_TRUE(db().GetAutofillableLogins(&result));
544 EXPECT_EQ(2U, result.size());
545
546 // When we retrieve the forms from the store, |in_store| should be set.
547 form.in_store = PasswordForm::Store::kProfileStore;
548 form2.in_store = PasswordForm::Store::kProfileStore;
549
550 // Match against desktop.
551 PasswordStore::FormDigest form_request = {PasswordForm::Scheme::kHtml,
552 "https://foo.com/",
553 GURL("https://foo.com/")};
554 EXPECT_TRUE(db().GetLogins(form_request, &result));
555 // Both forms are matched, only form2 is a PSL match.
556 form.is_public_suffix_match = false;
557 form2.is_public_suffix_match = true;
558 EXPECT_THAT(result, UnorderedElementsAre(Pointee(form), Pointee(form2)));
559
560 // Match against the mobile site.
561 form_request.origin = GURL("https://mobile.foo.com/");
562 form_request.signon_realm = "https://mobile.foo.com/";
563 EXPECT_TRUE(db().GetLogins(form_request, &result));
564 // Both forms are matched, only form is a PSL match.
565 form.is_public_suffix_match = true;
566 form2.is_public_suffix_match = false;
567 EXPECT_THAT(result, UnorderedElementsAre(Pointee(form), Pointee(form2)));
568 }
569
TEST_F(LoginDatabaseTest,TestFederatedMatchingLocalhost)570 TEST_F(LoginDatabaseTest, TestFederatedMatchingLocalhost) {
571 PasswordForm form;
572 form.origin = GURL("http://localhost/");
573 form.signon_realm = "federation://localhost/accounts.google.com";
574 form.federation_origin =
575 url::Origin::Create(GURL("https://accounts.google.com/"));
576 form.username_value = ASCIIToUTF16("test@gmail.com");
577 form.type = PasswordForm::Type::kApi;
578 form.scheme = PasswordForm::Scheme::kHtml;
579
580 PasswordForm form_with_port(form);
581 form_with_port.origin = GURL("http://localhost:8080/");
582 form_with_port.signon_realm = "federation://localhost/accounts.google.com";
583
584 EXPECT_EQ(AddChangeForForm(form), db().AddLogin(form));
585 EXPECT_EQ(AddChangeForForm(form_with_port), db().AddLogin(form_with_port));
586
587 // When we retrieve the forms from the store, |in_store| should be set.
588 form.in_store = PasswordForm::Store::kProfileStore;
589 form_with_port.in_store = PasswordForm::Store::kProfileStore;
590
591 // Match localhost with and without port.
592 PasswordStore::FormDigest form_request(PasswordForm::Scheme::kHtml,
593 "http://localhost/",
594 GURL("http://localhost/"));
595 std::vector<std::unique_ptr<PasswordForm>> result;
596 EXPECT_TRUE(db().GetLogins(form_request, &result));
597 EXPECT_THAT(result, UnorderedElementsAre(Pointee(form)));
598
599 form_request.origin = GURL("http://localhost:8080/");
600 form_request.signon_realm = "http://localhost:8080/";
601 EXPECT_TRUE(db().GetLogins(form_request, &result));
602 EXPECT_THAT(result, UnorderedElementsAre(Pointee(form_with_port)));
603 }
604
TEST_F(LoginDatabaseTest,TestPublicSuffixDisabledForNonHTMLForms)605 TEST_F(LoginDatabaseTest, TestPublicSuffixDisabledForNonHTMLForms) {
606 TestNonHTMLFormPSLMatching(PasswordForm::Scheme::kBasic);
607 TestNonHTMLFormPSLMatching(PasswordForm::Scheme::kDigest);
608 TestNonHTMLFormPSLMatching(PasswordForm::Scheme::kOther);
609 }
610
TEST_F(LoginDatabaseTest,TestIPAddressMatches_HTML)611 TEST_F(LoginDatabaseTest, TestIPAddressMatches_HTML) {
612 TestRetrievingIPAddress(PasswordForm::Scheme::kHtml);
613 }
614
TEST_F(LoginDatabaseTest,TestIPAddressMatches_basic)615 TEST_F(LoginDatabaseTest, TestIPAddressMatches_basic) {
616 TestRetrievingIPAddress(PasswordForm::Scheme::kBasic);
617 }
618
TEST_F(LoginDatabaseTest,TestIPAddressMatches_digest)619 TEST_F(LoginDatabaseTest, TestIPAddressMatches_digest) {
620 TestRetrievingIPAddress(PasswordForm::Scheme::kDigest);
621 }
622
TEST_F(LoginDatabaseTest,TestIPAddressMatches_other)623 TEST_F(LoginDatabaseTest, TestIPAddressMatches_other) {
624 TestRetrievingIPAddress(PasswordForm::Scheme::kOther);
625 }
626
TEST_F(LoginDatabaseTest,TestPublicSuffixDomainMatchingShouldMatchingApply)627 TEST_F(LoginDatabaseTest, TestPublicSuffixDomainMatchingShouldMatchingApply) {
628 std::vector<std::unique_ptr<PasswordForm>> result;
629
630 // Verify the database is empty.
631 EXPECT_TRUE(db().GetAutofillableLogins(&result));
632 EXPECT_EQ(0U, result.size());
633
634 // Saved password form on Google sign-in page.
635 PasswordForm form;
636 form.origin = GURL("https://accounts.google.com/");
637 form.username_value = ASCIIToUTF16("test@gmail.com");
638 form.password_value = ASCIIToUTF16("test");
639 form.signon_realm = "https://accounts.google.com/";
640 form.scheme = PasswordForm::Scheme::kHtml;
641
642 // Add it and make sure it is there.
643 EXPECT_EQ(AddChangeForForm(form), db().AddLogin(form));
644 EXPECT_TRUE(db().GetAutofillableLogins(&result));
645 EXPECT_EQ(1U, result.size());
646 result.clear();
647
648 // Match against an exact copy.
649 EXPECT_TRUE(db().GetLogins(PasswordStore::FormDigest(form), &result));
650 ASSERT_EQ(1U, result.size());
651 EXPECT_EQ(form.signon_realm, result[0]->signon_realm);
652 result.clear();
653
654 // Google change password should match to the saved sign-in form.
655 PasswordStore::FormDigest form2 = {PasswordForm::Scheme::kHtml,
656 "https://myaccount.google.com/",
657 GURL("https://myaccount.google.com/")};
658
659 EXPECT_TRUE(db().GetLogins(form2, &result));
660 ASSERT_EQ(1U, result.size());
661 EXPECT_EQ(form.signon_realm, result[0]->signon_realm);
662 EXPECT_TRUE(result[0]->is_public_suffix_match);
663
664 // There should be no PSL match on other subdomains.
665 PasswordStore::FormDigest form3 = {PasswordForm::Scheme::kHtml,
666 "https://some.other.google.com/",
667 GURL("https://some.other.google.com/")};
668
669 EXPECT_TRUE(db().GetLogins(form3, &result));
670 EXPECT_EQ(0U, result.size());
671 }
672
TEST_F(LoginDatabaseTest,TestFederatedMatchingWithoutPSLMatching)673 TEST_F(LoginDatabaseTest, TestFederatedMatchingWithoutPSLMatching) {
674 std::vector<std::unique_ptr<PasswordForm>> result;
675
676 // Example password form.
677 PasswordForm form;
678 form.origin = GURL("https://accounts.google.com/");
679 form.action = GURL("https://accounts.google.com/login");
680 form.username_value = ASCIIToUTF16("test@gmail.com");
681 form.password_value = ASCIIToUTF16("test");
682 form.signon_realm = "https://accounts.google.com/";
683 form.scheme = PasswordForm::Scheme::kHtml;
684
685 // We go to a different site on the same domain where PSL is disabled.
686 PasswordForm form2(form);
687 form2.origin = GURL("https://some.other.google.com/");
688 form2.action = GURL("https://some.other.google.com/login");
689 form2.signon_realm = "federation://some.other.google.com/accounts.google.com";
690 form2.username_value = ASCIIToUTF16("test1@gmail.com");
691 form2.type = PasswordForm::Type::kApi;
692 form2.federation_origin =
693 url::Origin::Create(GURL("https://accounts.google.com/"));
694
695 // Add it and make sure it is there.
696 EXPECT_EQ(AddChangeForForm(form), db().AddLogin(form));
697 EXPECT_EQ(AddChangeForForm(form2), db().AddLogin(form2));
698 EXPECT_TRUE(db().GetAutofillableLogins(&result));
699 EXPECT_EQ(2U, result.size());
700
701 // When we retrieve the forms from the store, |in_store| should be set.
702 form.in_store = PasswordForm::Store::kProfileStore;
703 form2.in_store = PasswordForm::Store::kProfileStore;
704
705 // Match against the first one.
706 PasswordStore::FormDigest form_request = {PasswordForm::Scheme::kHtml,
707 form.signon_realm, form.origin};
708 EXPECT_TRUE(db().GetLogins(form_request, &result));
709 EXPECT_THAT(result, testing::ElementsAre(Pointee(form)));
710
711 // Match against the second one.
712 form_request.origin = form2.origin;
713 form_request.signon_realm = form2.signon_realm;
714 EXPECT_TRUE(db().GetLogins(form_request, &result));
715 form.is_public_suffix_match = true;
716 EXPECT_THAT(result, testing::ElementsAre(Pointee(form2)));
717 }
718
TEST_F(LoginDatabaseTest,TestFederatedPSLMatching)719 TEST_F(LoginDatabaseTest, TestFederatedPSLMatching) {
720 // Save a federated credential for the PSL matched site.
721 PasswordForm form;
722 form.origin = GURL("https://psl.example.com/");
723 form.action = GURL("https://psl.example.com/login");
724 form.signon_realm = "federation://psl.example.com/accounts.google.com";
725 form.username_value = ASCIIToUTF16("test1@gmail.com");
726 form.type = PasswordForm::Type::kApi;
727 form.federation_origin =
728 url::Origin::Create(GURL("https://accounts.google.com/"));
729 form.scheme = PasswordForm::Scheme::kHtml;
730 EXPECT_EQ(AddChangeForForm(form), db().AddLogin(form));
731
732 // When we retrieve the form from the store, it should have |in_store| set.
733 form.in_store = PasswordForm::Store::kProfileStore;
734
735 // Match against.
736 PasswordStore::FormDigest form_request = {PasswordForm::Scheme::kHtml,
737 "https://example.com/",
738 GURL("https://example.com/login")};
739 std::vector<std::unique_ptr<PasswordForm>> result;
740 EXPECT_TRUE(db().GetLogins(form_request, &result));
741 form.is_public_suffix_match = true;
742 EXPECT_THAT(result, testing::ElementsAre(Pointee(form)));
743 }
744
745 // This test fails if the implementation of GetLogins uses GetCachedStatement
746 // instead of GetUniqueStatement, since REGEXP is in use. See
747 // http://crbug.com/248608.
TEST_F(LoginDatabaseTest,TestPublicSuffixDomainMatchingDifferentSites)748 TEST_F(LoginDatabaseTest, TestPublicSuffixDomainMatchingDifferentSites) {
749 std::vector<std::unique_ptr<PasswordForm>> result;
750
751 // Verify the database is empty.
752 EXPECT_TRUE(db().GetAutofillableLogins(&result));
753 EXPECT_EQ(0U, result.size());
754
755 // Example password form.
756 PasswordForm form;
757 form.origin = GURL("https://foo.com/");
758 form.action = GURL("https://foo.com/login");
759 form.username_element = ASCIIToUTF16("username");
760 form.username_value = ASCIIToUTF16("test@gmail.com");
761 form.password_element = ASCIIToUTF16("password");
762 form.password_value = ASCIIToUTF16("test");
763 form.submit_element = ASCIIToUTF16("");
764 form.signon_realm = "https://foo.com/";
765 form.scheme = PasswordForm::Scheme::kHtml;
766
767 // Add it and make sure it is there.
768 EXPECT_EQ(AddChangeForForm(form), db().AddLogin(form));
769 EXPECT_TRUE(db().GetAutofillableLogins(&result));
770 EXPECT_EQ(1U, result.size());
771 result.clear();
772
773 // Match against an exact copy.
774 EXPECT_TRUE(db().GetLogins(PasswordStore::FormDigest(form), &result));
775 EXPECT_EQ(1U, result.size());
776 result.clear();
777
778 // We go to the mobile site.
779 PasswordStore::FormDigest form2(form);
780 form2.origin = GURL("https://mobile.foo.com/");
781 form2.signon_realm = "https://mobile.foo.com/";
782
783 // Match against the mobile site.
784 EXPECT_TRUE(db().GetLogins(form2, &result));
785 EXPECT_EQ(1U, result.size());
786 EXPECT_EQ("https://foo.com/", result[0]->signon_realm);
787 EXPECT_TRUE(result[0]->is_public_suffix_match);
788 result.clear();
789
790 // Add baz.com desktop site.
791 form.origin = GURL("https://baz.com/login/");
792 form.action = GURL("https://baz.com/login/");
793 form.username_element = ASCIIToUTF16("email");
794 form.username_value = ASCIIToUTF16("test@gmail.com");
795 form.password_element = ASCIIToUTF16("password");
796 form.password_value = ASCIIToUTF16("test");
797 form.submit_element = ASCIIToUTF16("");
798 form.signon_realm = "https://baz.com/";
799 form.scheme = PasswordForm::Scheme::kHtml;
800
801 // Add it and make sure it is there.
802 EXPECT_EQ(AddChangeForForm(form), db().AddLogin(form));
803 EXPECT_TRUE(db().GetAutofillableLogins(&result));
804 EXPECT_EQ(2U, result.size());
805 result.clear();
806
807 // We go to the mobile site of baz.com.
808 PasswordStore::FormDigest form3(form);
809 form3.origin = GURL("https://m.baz.com/login/");
810 form3.signon_realm = "https://m.baz.com/";
811
812 // Match against the mobile site of baz.com.
813 EXPECT_TRUE(db().GetLogins(form3, &result));
814 EXPECT_EQ(1U, result.size());
815 EXPECT_EQ("https://baz.com/", result[0]->signon_realm);
816 EXPECT_TRUE(result[0]->is_public_suffix_match);
817 result.clear();
818 }
819
GetFormWithNewSignonRealm(PasswordForm form,std::string signon_realm)820 PasswordForm GetFormWithNewSignonRealm(PasswordForm form,
821 std::string signon_realm) {
822 PasswordForm form2(form);
823 form2.origin = GURL(signon_realm);
824 form2.action = GURL(signon_realm);
825 form2.signon_realm = signon_realm;
826 return form2;
827 }
828
TEST_F(LoginDatabaseTest,TestPublicSuffixDomainMatchingRegexp)829 TEST_F(LoginDatabaseTest, TestPublicSuffixDomainMatchingRegexp) {
830 std::vector<std::unique_ptr<PasswordForm>> result;
831
832 // Verify the database is empty.
833 EXPECT_TRUE(db().GetAutofillableLogins(&result));
834 EXPECT_EQ(0U, result.size());
835
836 // Example password form.
837 PasswordForm form;
838 form.origin = GURL("http://foo.com/");
839 form.action = GURL("http://foo.com/login");
840 form.username_element = ASCIIToUTF16("username");
841 form.username_value = ASCIIToUTF16("test@gmail.com");
842 form.password_element = ASCIIToUTF16("password");
843 form.password_value = ASCIIToUTF16("test");
844 form.submit_element = ASCIIToUTF16("");
845 form.signon_realm = "http://foo.com/";
846 form.scheme = PasswordForm::Scheme::kHtml;
847
848 // Add it and make sure it is there.
849 EXPECT_EQ(AddChangeForForm(form), db().AddLogin(form));
850 EXPECT_TRUE(db().GetAutofillableLogins(&result));
851 EXPECT_EQ(1U, result.size());
852 result.clear();
853
854 // Example password form that has - in the domain name.
855 PasswordForm form_dash =
856 GetFormWithNewSignonRealm(form, "http://www.foo-bar.com/");
857
858 // Add it and make sure it is there.
859 EXPECT_EQ(AddChangeForForm(form_dash), db().AddLogin(form_dash));
860 EXPECT_TRUE(db().GetAutofillableLogins(&result));
861 EXPECT_EQ(2U, result.size());
862 result.clear();
863
864 // Match against an exact copy.
865 EXPECT_TRUE(db().GetLogins(PasswordStore::FormDigest(form), &result));
866 EXPECT_EQ(1U, result.size());
867 result.clear();
868
869 // www.foo.com should match.
870 PasswordForm form2 = GetFormWithNewSignonRealm(form, "http://www.foo.com/");
871 EXPECT_TRUE(db().GetLogins(PasswordStore::FormDigest(form2), &result));
872 EXPECT_EQ(1U, result.size());
873 result.clear();
874
875 // a.b.foo.com should match.
876 form2 = GetFormWithNewSignonRealm(form, "http://a.b.foo.com/");
877 EXPECT_TRUE(db().GetLogins(PasswordStore::FormDigest(form2), &result));
878 EXPECT_EQ(1U, result.size());
879 result.clear();
880
881 // a-b.foo.com should match.
882 form2 = GetFormWithNewSignonRealm(form, "http://a-b.foo.com/");
883 EXPECT_TRUE(db().GetLogins(PasswordStore::FormDigest(form2), &result));
884 EXPECT_EQ(1U, result.size());
885 result.clear();
886
887 // foo-bar.com should match.
888 form2 = GetFormWithNewSignonRealm(form, "http://foo-bar.com/");
889 EXPECT_TRUE(db().GetLogins(PasswordStore::FormDigest(form2), &result));
890 EXPECT_EQ(1U, result.size());
891 result.clear();
892
893 // www.foo-bar.com should match.
894 form2 = GetFormWithNewSignonRealm(form, "http://www.foo-bar.com/");
895 EXPECT_TRUE(db().GetLogins(PasswordStore::FormDigest(form2), &result));
896 EXPECT_EQ(1U, result.size());
897 result.clear();
898
899 // a.b.foo-bar.com should match.
900 form2 = GetFormWithNewSignonRealm(form, "http://a.b.foo-bar.com/");
901 EXPECT_TRUE(db().GetLogins(PasswordStore::FormDigest(form2), &result));
902 EXPECT_EQ(1U, result.size());
903 result.clear();
904
905 // a-b.foo-bar.com should match.
906 form2 = GetFormWithNewSignonRealm(form, "http://a-b.foo-bar.com/");
907 EXPECT_TRUE(db().GetLogins(PasswordStore::FormDigest(form2), &result));
908 EXPECT_EQ(1U, result.size());
909 result.clear();
910
911 // foo.com with port 1337 should not match.
912 form2 = GetFormWithNewSignonRealm(form, "http://foo.com:1337/");
913 EXPECT_TRUE(db().GetLogins(PasswordStore::FormDigest(form2), &result));
914 EXPECT_EQ(0U, result.size());
915
916 // http://foo.com should not match since the scheme is wrong.
917 form2 = GetFormWithNewSignonRealm(form, "https://foo.com/");
918 EXPECT_TRUE(db().GetLogins(PasswordStore::FormDigest(form2), &result));
919 EXPECT_EQ(0U, result.size());
920
921 // notfoo.com should not match.
922 form2 = GetFormWithNewSignonRealm(form, "http://notfoo.com/");
923 EXPECT_TRUE(db().GetLogins(PasswordStore::FormDigest(form2), &result));
924 EXPECT_EQ(0U, result.size());
925
926 // baz.com should not match.
927 form2 = GetFormWithNewSignonRealm(form, "http://baz.com/");
928 EXPECT_TRUE(db().GetLogins(PasswordStore::FormDigest(form2), &result));
929 EXPECT_EQ(0U, result.size());
930
931 // foo-baz.com should not match.
932 form2 = GetFormWithNewSignonRealm(form, "http://foo-baz.com/");
933 EXPECT_TRUE(db().GetLogins(PasswordStore::FormDigest(form2), &result));
934 EXPECT_EQ(0U, result.size());
935 }
936
AddTimestampedLogin(LoginDatabase * db,std::string url,const std::string & unique_string,const base::Time & time,bool date_is_creation)937 static bool AddTimestampedLogin(LoginDatabase* db,
938 std::string url,
939 const std::string& unique_string,
940 const base::Time& time,
941 bool date_is_creation) {
942 // Example password form.
943 PasswordForm form;
944 form.origin = GURL(url + std::string("/LoginAuth"));
945 form.username_element = ASCIIToUTF16(unique_string);
946 form.username_value = ASCIIToUTF16(unique_string);
947 form.password_element = ASCIIToUTF16(unique_string);
948 form.submit_element = ASCIIToUTF16("signIn");
949 form.signon_realm = url;
950 form.display_name = ASCIIToUTF16(unique_string);
951 form.icon_url = GURL("https://accounts.google.com/Icon");
952 form.federation_origin =
953 url::Origin::Create(GURL("https://accounts.google.com/"));
954 form.skip_zero_click = true;
955
956 if (date_is_creation)
957 form.date_created = time;
958 else
959 form.date_synced = time;
960 return db->AddLogin(form) == AddChangeForForm(form);
961 }
962
TEST_F(LoginDatabaseTest,ClearPrivateData_SavedPasswords)963 TEST_F(LoginDatabaseTest, ClearPrivateData_SavedPasswords) {
964 std::vector<std::unique_ptr<PasswordForm>> result;
965
966 // Verify the database is empty.
967 EXPECT_TRUE(db().GetAutofillableLogins(&result));
968 EXPECT_EQ(0U, result.size());
969
970 base::Time now = base::Time::Now();
971 base::TimeDelta one_day = base::TimeDelta::FromDays(1);
972 base::Time back_30_days = now - base::TimeDelta::FromDays(30);
973 base::Time back_31_days = now - base::TimeDelta::FromDays(31);
974
975 // Create one with a 0 time.
976 EXPECT_TRUE(
977 AddTimestampedLogin(&db(), "http://1.com", "foo1", base::Time(), true));
978 // Create one for now and +/- 1 day.
979 EXPECT_TRUE(
980 AddTimestampedLogin(&db(), "http://2.com", "foo2", now - one_day, true));
981 EXPECT_TRUE(AddTimestampedLogin(&db(), "http://3.com", "foo3", now, true));
982 EXPECT_TRUE(
983 AddTimestampedLogin(&db(), "http://4.com", "foo4", now + one_day, true));
984 // Create one with 31 days old.
985 EXPECT_TRUE(
986 AddTimestampedLogin(&db(), "http://5.com", "foo5", back_31_days, true));
987
988 // Verify inserts worked.
989 EXPECT_TRUE(db().GetAutofillableLogins(&result));
990 EXPECT_EQ(5U, result.size());
991 result.clear();
992
993 // Get everything from today's date and on.
994 PrimaryKeyToFormMap key_to_form_map;
995 EXPECT_TRUE(
996 db().GetLoginsCreatedBetween(now, base::Time(), &key_to_form_map));
997 EXPECT_EQ(2U, key_to_form_map.size());
998 key_to_form_map.clear();
999
1000 // Get all logins created more than 30 days back.
1001 EXPECT_TRUE(db().GetLoginsCreatedBetween(base::Time(), back_30_days,
1002 &key_to_form_map));
1003 EXPECT_EQ(2U, key_to_form_map.size());
1004 key_to_form_map.clear();
1005
1006 // Delete everything from today's date and on.
1007 PasswordStoreChangeList changes;
1008 db().RemoveLoginsCreatedBetween(now, base::Time(), &changes);
1009 ASSERT_EQ(2U, changes.size());
1010 // The 3rd and the 4th should have been deleted.
1011 EXPECT_EQ(3, changes[0].primary_key());
1012 EXPECT_EQ(4, changes[1].primary_key());
1013
1014 // Should have deleted two logins.
1015 EXPECT_TRUE(db().GetAutofillableLogins(&result));
1016 EXPECT_EQ(3U, result.size());
1017 result.clear();
1018
1019 // Delete all logins created more than 30 days back.
1020 db().RemoveLoginsCreatedBetween(base::Time(), back_30_days, &changes);
1021 ASSERT_EQ(2U, changes.size());
1022 // The 1st and the 5th should have been deleted.
1023 EXPECT_EQ(1, changes[0].primary_key());
1024 EXPECT_EQ(5, changes[1].primary_key());
1025
1026 // Should have deleted two logins.
1027 EXPECT_TRUE(db().GetAutofillableLogins(&result));
1028 EXPECT_EQ(1U, result.size());
1029 result.clear();
1030
1031 // Delete with 0 date (should delete all).
1032 db().RemoveLoginsCreatedBetween(base::Time(), base::Time(), &changes);
1033 ASSERT_EQ(1U, changes.size());
1034 // The 2nd should have been deleted.
1035 EXPECT_EQ(2, changes[0].primary_key());
1036
1037 // Verify nothing is left.
1038 EXPECT_TRUE(db().GetAutofillableLogins(&result));
1039 EXPECT_EQ(0U, result.size());
1040 }
1041
TEST_F(LoginDatabaseTest,GetAutoSignInLogins)1042 TEST_F(LoginDatabaseTest, GetAutoSignInLogins) {
1043 PrimaryKeyToFormMap key_to_form_map;
1044
1045 GURL origin("https://example.com");
1046 EXPECT_TRUE(AddZeroClickableLogin(&db(), "foo1", origin));
1047 EXPECT_TRUE(AddZeroClickableLogin(&db(), "foo2", origin));
1048 EXPECT_TRUE(AddZeroClickableLogin(&db(), "foo3", origin));
1049 EXPECT_TRUE(AddZeroClickableLogin(&db(), "foo4", origin));
1050
1051 EXPECT_TRUE(db().GetAutoSignInLogins(&key_to_form_map));
1052 EXPECT_EQ(4U, key_to_form_map.size());
1053 for (const auto& pair : key_to_form_map)
1054 EXPECT_FALSE(pair.second->skip_zero_click);
1055
1056 EXPECT_TRUE(db().DisableAutoSignInForOrigin(origin));
1057 EXPECT_TRUE(db().GetAutoSignInLogins(&key_to_form_map));
1058 EXPECT_EQ(0U, key_to_form_map.size());
1059 }
1060
TEST_F(LoginDatabaseTest,DisableAutoSignInForOrigin)1061 TEST_F(LoginDatabaseTest, DisableAutoSignInForOrigin) {
1062 std::vector<std::unique_ptr<PasswordForm>> result;
1063
1064 GURL origin1("https://google.com");
1065 GURL origin2("https://chrome.com");
1066 GURL origin3("http://example.com");
1067 GURL origin4("http://localhost");
1068 EXPECT_TRUE(AddZeroClickableLogin(&db(), "foo1", origin1));
1069 EXPECT_TRUE(AddZeroClickableLogin(&db(), "foo2", origin2));
1070 EXPECT_TRUE(AddZeroClickableLogin(&db(), "foo3", origin3));
1071 EXPECT_TRUE(AddZeroClickableLogin(&db(), "foo4", origin4));
1072
1073 EXPECT_TRUE(db().GetAutofillableLogins(&result));
1074 for (const auto& form : result)
1075 EXPECT_FALSE(form->skip_zero_click);
1076
1077 EXPECT_TRUE(db().DisableAutoSignInForOrigin(origin1));
1078 EXPECT_TRUE(db().DisableAutoSignInForOrigin(origin3));
1079 EXPECT_TRUE(db().GetAutofillableLogins(&result));
1080 for (const auto& form : result) {
1081 if (form->origin == origin1 || form->origin == origin3)
1082 EXPECT_TRUE(form->skip_zero_click);
1083 else
1084 EXPECT_FALSE(form->skip_zero_click);
1085 }
1086 }
1087
TEST_F(LoginDatabaseTest,BlacklistedLogins)1088 TEST_F(LoginDatabaseTest, BlacklistedLogins) {
1089 std::vector<std::unique_ptr<PasswordForm>> result;
1090
1091 // Verify the database is empty.
1092 EXPECT_TRUE(db().GetBlacklistLogins(&result));
1093 ASSERT_EQ(0U, result.size());
1094
1095 // Save a form as blacklisted.
1096 PasswordForm form;
1097 form.origin = GURL("http://accounts.google.com/LoginAuth");
1098 form.action = GURL("http://accounts.google.com/Login");
1099 form.username_element = ASCIIToUTF16("Email");
1100 form.password_element = ASCIIToUTF16("Passwd");
1101 form.submit_element = ASCIIToUTF16("signIn");
1102 form.signon_realm = "http://www.google.com/";
1103 form.blacklisted_by_user = true;
1104 form.scheme = PasswordForm::Scheme::kHtml;
1105 form.date_synced = base::Time::Now();
1106 form.date_last_used = base::Time::Now();
1107 form.display_name = ASCIIToUTF16("Mr. Smith");
1108 form.icon_url = GURL("https://accounts.google.com/Icon");
1109 form.federation_origin =
1110 url::Origin::Create(GURL("https://accounts.google.com/"));
1111 form.skip_zero_click = true;
1112 EXPECT_EQ(AddChangeForForm(form), db().AddLogin(form));
1113
1114 // Get all non-blacklisted logins (should be none).
1115 EXPECT_TRUE(db().GetAutofillableLogins(&result));
1116 ASSERT_EQ(0U, result.size());
1117
1118 // When we retrieve the form from the store, it should have |in_store| set.
1119 form.in_store = PasswordForm::Store::kProfileStore;
1120
1121 // GetLogins should give the blacklisted result.
1122 EXPECT_TRUE(db().GetLogins(PasswordStore::FormDigest(form), &result));
1123 ASSERT_EQ(1U, result.size());
1124 EXPECT_EQ(form, *result[0]);
1125 result.clear();
1126
1127 // So should GetBlacklistedLogins.
1128 EXPECT_TRUE(db().GetBlacklistLogins(&result));
1129 ASSERT_EQ(1U, result.size());
1130 EXPECT_EQ(form, *result[0]);
1131 result.clear();
1132 }
1133
TEST_F(LoginDatabaseTest,VectorSerialization)1134 TEST_F(LoginDatabaseTest, VectorSerialization) {
1135 // Empty vector.
1136 ValueElementVector vec;
1137 base::Pickle temp = SerializeValueElementPairs(vec);
1138 ValueElementVector output = DeserializeValueElementPairs(temp);
1139 EXPECT_THAT(output, Eq(vec));
1140
1141 // Normal data.
1142 vec.push_back({ASCIIToUTF16("first"), ASCIIToUTF16("id1")});
1143 vec.push_back({ASCIIToUTF16("second"), ASCIIToUTF16("id2")});
1144 vec.push_back({ASCIIToUTF16("third"), ASCIIToUTF16("id3")});
1145
1146 temp = SerializeValueElementPairs(vec);
1147 output = DeserializeValueElementPairs(temp);
1148 EXPECT_THAT(output, Eq(vec));
1149 }
1150
TEST_F(LoginDatabaseTest,UpdateIncompleteCredentials)1151 TEST_F(LoginDatabaseTest, UpdateIncompleteCredentials) {
1152 std::vector<std::unique_ptr<PasswordForm>> result;
1153 // Verify the database is empty.
1154 EXPECT_TRUE(db().GetAutofillableLogins(&result));
1155 ASSERT_EQ(0U, result.size());
1156
1157 // Save an incomplete form. Note that it only has a few fields set, ex. it's
1158 // missing 'action', 'username_element' and 'password_element'. Such forms
1159 // are sometimes inserted during import from other browsers (which may not
1160 // store this info).
1161 PasswordForm incomplete_form;
1162 incomplete_form.origin = GURL("http://accounts.google.com/LoginAuth");
1163 incomplete_form.signon_realm = "http://accounts.google.com/";
1164 incomplete_form.username_value = ASCIIToUTF16("my_username");
1165 incomplete_form.password_value = ASCIIToUTF16("my_password");
1166 incomplete_form.date_last_used = base::Time::Now();
1167 incomplete_form.blacklisted_by_user = false;
1168 incomplete_form.scheme = PasswordForm::Scheme::kHtml;
1169 EXPECT_EQ(AddChangeForForm(incomplete_form), db().AddLogin(incomplete_form));
1170
1171 // A form on some website. It should trigger a match with the stored one.
1172 PasswordForm encountered_form;
1173 encountered_form.origin = GURL("http://accounts.google.com/LoginAuth");
1174 encountered_form.signon_realm = "http://accounts.google.com/";
1175 encountered_form.action = GURL("http://accounts.google.com/Login");
1176 encountered_form.username_element = ASCIIToUTF16("Email");
1177 encountered_form.password_element = ASCIIToUTF16("Passwd");
1178 encountered_form.submit_element = ASCIIToUTF16("signIn");
1179
1180 // Get matches for encountered_form.
1181 EXPECT_TRUE(
1182 db().GetLogins(PasswordStore::FormDigest(encountered_form), &result));
1183 ASSERT_EQ(1U, result.size());
1184 EXPECT_EQ(incomplete_form.origin, result[0]->origin);
1185 EXPECT_EQ(incomplete_form.signon_realm, result[0]->signon_realm);
1186 EXPECT_EQ(incomplete_form.username_value, result[0]->username_value);
1187 EXPECT_EQ(incomplete_form.password_value, result[0]->password_value);
1188 EXPECT_EQ(incomplete_form.date_last_used, result[0]->date_last_used);
1189
1190 // We should return empty 'action', 'username_element', 'password_element'
1191 // and 'submit_element' as we can't be sure if the credentials were entered
1192 // in this particular form on the page.
1193 EXPECT_EQ(GURL(), result[0]->action);
1194 EXPECT_TRUE(result[0]->username_element.empty());
1195 EXPECT_TRUE(result[0]->password_element.empty());
1196 EXPECT_TRUE(result[0]->submit_element.empty());
1197 result.clear();
1198
1199 // Let's say this login form worked. Now update the stored credentials with
1200 // 'action', 'username_element', 'password_element' and 'submit_element' from
1201 // the encountered form.
1202 PasswordForm completed_form(incomplete_form);
1203 completed_form.action = encountered_form.action;
1204 completed_form.username_element = encountered_form.username_element;
1205 completed_form.password_element = encountered_form.password_element;
1206 completed_form.submit_element = encountered_form.submit_element;
1207 EXPECT_EQ(AddChangeForForm(completed_form), db().AddLogin(completed_form));
1208 EXPECT_TRUE(db().RemoveLogin(incomplete_form, /*changes=*/nullptr));
1209
1210 // Get matches for encountered_form again.
1211 EXPECT_TRUE(
1212 db().GetLogins(PasswordStore::FormDigest(encountered_form), &result));
1213 ASSERT_EQ(1U, result.size());
1214
1215 // This time we should have all the info available.
1216 PasswordForm expected_form(completed_form);
1217 // When we retrieve the form from the store, it should have |in_store| set.
1218 expected_form.in_store = PasswordForm::Store::kProfileStore;
1219 EXPECT_EQ(expected_form, *result[0]);
1220 result.clear();
1221 }
1222
TEST_F(LoginDatabaseTest,UpdateOverlappingCredentials)1223 TEST_F(LoginDatabaseTest, UpdateOverlappingCredentials) {
1224 // Save an incomplete form. Note that it only has a few fields set, ex. it's
1225 // missing 'action', 'username_element' and 'password_element'. Such forms
1226 // are sometimes inserted during import from other browsers (which may not
1227 // store this info).
1228 PasswordForm incomplete_form;
1229 incomplete_form.origin = GURL("http://accounts.google.com/LoginAuth");
1230 incomplete_form.signon_realm = "http://accounts.google.com/";
1231 incomplete_form.username_value = ASCIIToUTF16("my_username");
1232 incomplete_form.password_value = ASCIIToUTF16("my_password");
1233 incomplete_form.date_last_used = base::Time::Now();
1234 incomplete_form.blacklisted_by_user = false;
1235 incomplete_form.scheme = PasswordForm::Scheme::kHtml;
1236 EXPECT_EQ(AddChangeForForm(incomplete_form), db().AddLogin(incomplete_form));
1237
1238 // Save a complete version of the previous form. Both forms could exist if
1239 // the user created the complete version before importing the incomplete
1240 // version from a different browser.
1241 PasswordForm complete_form = incomplete_form;
1242 complete_form.action = GURL("http://accounts.google.com/Login");
1243 complete_form.username_element = ASCIIToUTF16("username_element");
1244 complete_form.password_element = ASCIIToUTF16("password_element");
1245 complete_form.submit_element = ASCIIToUTF16("submit");
1246
1247 // An update fails because the primary key for |complete_form| is different.
1248 EXPECT_EQ(PasswordStoreChangeList(), db().UpdateLogin(complete_form));
1249 EXPECT_EQ(AddChangeForForm(complete_form), db().AddLogin(complete_form));
1250
1251 // Make sure both passwords exist.
1252 std::vector<std::unique_ptr<PasswordForm>> result;
1253 EXPECT_TRUE(db().GetAutofillableLogins(&result));
1254 ASSERT_EQ(2U, result.size());
1255 result.clear();
1256
1257 // Simulate the user changing their password.
1258 complete_form.password_value = ASCIIToUTF16("new_password");
1259 complete_form.date_synced = base::Time::Now();
1260 EXPECT_EQ(UpdateChangeForForm(complete_form, /*passwordchanged=*/true),
1261 db().UpdateLogin(complete_form));
1262
1263 // When we retrieve the forms from the store, |in_store| should be set.
1264 complete_form.in_store = PasswordForm::Store::kProfileStore;
1265 incomplete_form.in_store = PasswordForm::Store::kProfileStore;
1266
1267 // Both still exist now.
1268 EXPECT_TRUE(db().GetAutofillableLogins(&result));
1269 ASSERT_EQ(2U, result.size());
1270
1271 if (result[0]->username_element.empty())
1272 std::swap(result[0], result[1]);
1273 EXPECT_EQ(complete_form, *result[0]);
1274 EXPECT_EQ(incomplete_form, *result[1]);
1275 }
1276
TEST_F(LoginDatabaseTest,DoubleAdd)1277 TEST_F(LoginDatabaseTest, DoubleAdd) {
1278 PasswordForm form;
1279 form.origin = GURL("http://accounts.google.com/LoginAuth");
1280 form.signon_realm = "http://accounts.google.com/";
1281 form.username_value = ASCIIToUTF16("my_username");
1282 form.password_value = ASCIIToUTF16("my_password");
1283 form.blacklisted_by_user = false;
1284 form.scheme = PasswordForm::Scheme::kHtml;
1285 EXPECT_EQ(AddChangeForForm(form), db().AddLogin(form));
1286
1287 // Add almost the same form again.
1288 form.times_used++;
1289 PasswordStoreChangeList list;
1290 list.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, form));
1291 list.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form));
1292 EXPECT_EQ(list, db().AddLogin(form));
1293 }
1294
TEST_F(LoginDatabaseTest,AddWrongForm)1295 TEST_F(LoginDatabaseTest, AddWrongForm) {
1296 PasswordForm form;
1297 // |origin| shouldn't be empty.
1298 form.origin = GURL();
1299 form.signon_realm = "http://accounts.google.com/";
1300 form.username_value = ASCIIToUTF16("my_username");
1301 form.password_value = ASCIIToUTF16("my_password");
1302 form.blacklisted_by_user = false;
1303 form.scheme = PasswordForm::Scheme::kHtml;
1304 EXPECT_EQ(PasswordStoreChangeList(), db().AddLogin(form));
1305
1306 // |signon_realm| shouldn't be empty.
1307 form.origin = GURL("http://accounts.google.com/LoginAuth");
1308 form.signon_realm.clear();
1309 EXPECT_EQ(PasswordStoreChangeList(), db().AddLogin(form));
1310 }
1311
TEST_F(LoginDatabaseTest,UpdateLogin)1312 TEST_F(LoginDatabaseTest, UpdateLogin) {
1313 PasswordForm form;
1314 form.origin = GURL("http://accounts.google.com/LoginAuth");
1315 form.signon_realm = "http://accounts.google.com/";
1316 form.username_value = ASCIIToUTF16("my_username");
1317 form.password_value = ASCIIToUTF16("my_password");
1318 form.blacklisted_by_user = false;
1319 form.scheme = PasswordForm::Scheme::kHtml;
1320 form.date_last_used = base::Time::Now();
1321 EXPECT_EQ(AddChangeForForm(form), db().AddLogin(form));
1322
1323 form.action = GURL("http://accounts.google.com/login");
1324 form.password_value = ASCIIToUTF16("my_new_password");
1325 form.all_possible_usernames.push_back(autofill::ValueElementPair(
1326 ASCIIToUTF16("my_new_username"), ASCIIToUTF16("new_username_id")));
1327 form.times_used = 20;
1328 form.submit_element = ASCIIToUTF16("submit_element");
1329 form.date_synced = base::Time::Now();
1330 form.date_created = base::Time::Now() - base::TimeDelta::FromDays(1);
1331 form.date_last_used = base::Time::Now() + base::TimeDelta::FromDays(1);
1332 form.blacklisted_by_user = true;
1333 form.scheme = PasswordForm::Scheme::kBasic;
1334 form.type = PasswordForm::Type::kGenerated;
1335 form.display_name = ASCIIToUTF16("Mr. Smith");
1336 form.icon_url = GURL("https://accounts.google.com/Icon");
1337 form.federation_origin =
1338 url::Origin::Create(GURL("https://accounts.google.com/"));
1339 form.skip_zero_click = true;
1340
1341 PasswordStoreChangeList changes = db().UpdateLogin(form);
1342 EXPECT_EQ(UpdateChangeForForm(form, /*passwordchanged=*/true), changes);
1343 ASSERT_EQ(1U, changes.size());
1344 EXPECT_EQ(1, changes[0].primary_key());
1345
1346 // When we retrieve the form from the store, it should have |in_store| set.
1347 form.in_store = PasswordForm::Store::kProfileStore;
1348
1349 std::vector<std::unique_ptr<PasswordForm>> result;
1350 EXPECT_TRUE(db().GetLogins(PasswordStore::FormDigest(form), &result));
1351 ASSERT_EQ(1U, result.size());
1352 EXPECT_EQ(form, *result[0]);
1353 }
1354
TEST_F(LoginDatabaseTest,RemoveWrongForm)1355 TEST_F(LoginDatabaseTest, RemoveWrongForm) {
1356 PasswordForm form;
1357 // |origin| shouldn't be empty.
1358 form.origin = GURL("http://accounts.google.com/LoginAuth");
1359 form.signon_realm = "http://accounts.google.com/";
1360 form.username_value = ASCIIToUTF16("my_username");
1361 form.password_value = ASCIIToUTF16("my_password");
1362 form.blacklisted_by_user = false;
1363 form.scheme = PasswordForm::Scheme::kHtml;
1364 // The form isn't in the database.
1365 EXPECT_FALSE(db().RemoveLogin(form, /*changes=*/nullptr));
1366
1367 EXPECT_EQ(AddChangeForForm(form), db().AddLogin(form));
1368 EXPECT_TRUE(db().RemoveLogin(form, /*changes=*/nullptr));
1369 EXPECT_FALSE(db().RemoveLogin(form, /*changes=*/nullptr));
1370 }
1371
TEST_F(LoginDatabaseTest,ReportMetricsTest)1372 TEST_F(LoginDatabaseTest, ReportMetricsTest) {
1373 PasswordForm password_form;
1374 password_form.origin = GURL("http://example.com");
1375 password_form.username_value = ASCIIToUTF16("test1@gmail.com");
1376 password_form.password_value = ASCIIToUTF16("test");
1377 password_form.signon_realm = "http://example.com/";
1378 password_form.times_used = 0;
1379 EXPECT_EQ(AddChangeForForm(password_form), db().AddLogin(password_form));
1380
1381 password_form.username_value = ASCIIToUTF16("test2@gmail.com");
1382 password_form.times_used = 1;
1383 EXPECT_EQ(AddChangeForForm(password_form), db().AddLogin(password_form));
1384
1385 password_form.origin = GURL("http://second.example.com");
1386 password_form.signon_realm = "http://second.example.com";
1387 password_form.times_used = 3;
1388 EXPECT_EQ(AddChangeForForm(password_form), db().AddLogin(password_form));
1389
1390 password_form.username_value = ASCIIToUTF16("test3@gmail.com");
1391 password_form.type = PasswordForm::Type::kGenerated;
1392 password_form.times_used = 2;
1393 EXPECT_EQ(AddChangeForForm(password_form), db().AddLogin(password_form));
1394
1395 password_form.origin = GURL("ftp://third.example.com/");
1396 password_form.signon_realm = "ftp://third.example.com/";
1397 password_form.times_used = 4;
1398 password_form.scheme = PasswordForm::Scheme::kOther;
1399 EXPECT_EQ(AddChangeForForm(password_form), db().AddLogin(password_form));
1400
1401 password_form.origin = GURL("http://fourth.example.com/");
1402 password_form.signon_realm = "http://fourth.example.com/";
1403 password_form.type = PasswordForm::Type::kManual;
1404 password_form.username_value = ASCIIToUTF16("");
1405 password_form.times_used = 10;
1406 password_form.scheme = PasswordForm::Scheme::kHtml;
1407 EXPECT_EQ(AddChangeForForm(password_form), db().AddLogin(password_form));
1408
1409 password_form.origin = GURL("https://fifth.example.com/");
1410 password_form.signon_realm = "https://fifth.example.com/";
1411 password_form.password_value = ASCIIToUTF16("");
1412 password_form.blacklisted_by_user = true;
1413 EXPECT_EQ(AddChangeForForm(password_form), db().AddLogin(password_form));
1414
1415 password_form.origin = GURL("https://sixth.example.com/");
1416 password_form.signon_realm = "https://sixth.example.com/";
1417 password_form.username_value = ASCIIToUTF16("my_username");
1418 password_form.password_value = ASCIIToUTF16("my_password");
1419 password_form.blacklisted_by_user = false;
1420 EXPECT_EQ(AddChangeForForm(password_form), db().AddLogin(password_form));
1421
1422 password_form.origin = GURL();
1423 password_form.signon_realm = "android://hash@com.example.android/";
1424 password_form.username_value = ASCIIToUTF16("JohnDoe");
1425 password_form.password_value = ASCIIToUTF16("my_password");
1426 password_form.blacklisted_by_user = false;
1427 EXPECT_EQ(AddChangeForForm(password_form), db().AddLogin(password_form));
1428
1429 password_form.username_value = ASCIIToUTF16("JaneDoe");
1430 EXPECT_EQ(AddChangeForForm(password_form), db().AddLogin(password_form));
1431
1432 password_form.origin = GURL("http://rsolomakhin.github.io/autofill/");
1433 password_form.signon_realm = "http://rsolomakhin.github.io/";
1434 password_form.blacklisted_by_user = true;
1435 EXPECT_EQ(AddChangeForForm(password_form), db().AddLogin(password_form));
1436
1437 password_form.origin = GURL("https://rsolomakhin.github.io/autofill/");
1438 password_form.signon_realm = "https://rsolomakhin.github.io/";
1439 password_form.blacklisted_by_user = true;
1440 EXPECT_EQ(AddChangeForForm(password_form), db().AddLogin(password_form));
1441
1442 password_form.origin = GURL("http://rsolomakhin.github.io/autofill/123");
1443 password_form.signon_realm = "http://rsolomakhin.github.io/";
1444 password_form.blacklisted_by_user = true;
1445 EXPECT_EQ(AddChangeForForm(password_form),
1446 db().AddBlacklistedLoginForTesting(password_form));
1447
1448 password_form.origin = GURL("https://rsolomakhin.github.io/autofill/1234");
1449 password_form.signon_realm = "https://rsolomakhin.github.io/";
1450 password_form.blacklisted_by_user = true;
1451 EXPECT_EQ(AddChangeForForm(password_form),
1452 db().AddBlacklistedLoginForTesting(password_form));
1453
1454 StatisticsTable& stats_table = db().stats_table();
1455 InteractionsStats stats;
1456 stats.origin_domain = GURL("https://example.com");
1457 stats.username_value = base::ASCIIToUTF16("user1");
1458 stats.dismissal_count = 10;
1459 stats.update_time = base::Time::FromTimeT(1);
1460 EXPECT_TRUE(stats_table.AddRow(stats));
1461 stats.username_value = base::ASCIIToUTF16("user2");
1462 stats.dismissal_count = 1;
1463 EXPECT_TRUE(stats_table.AddRow(stats));
1464 stats.username_value = base::ASCIIToUTF16("user3");
1465 stats.dismissal_count = 10;
1466 EXPECT_TRUE(stats_table.AddRow(stats));
1467 stats.origin_domain = GURL("https://foo.com");
1468 stats.dismissal_count = 10;
1469 EXPECT_TRUE(stats_table.AddRow(stats));
1470
1471 base::HistogramTester histogram_tester;
1472 db().ReportMetrics("", false, BulkCheckDone(false));
1473
1474 histogram_tester.ExpectUniqueSample(
1475 "PasswordManager.AccountsPerSiteHiRes.AutoGenerated."
1476 "WithoutCustomPassphrase",
1477 1, 2);
1478
1479 histogram_tester.ExpectBucketCount(
1480 "PasswordManager.AccountsPerSiteHiRes.UserCreated."
1481 "WithoutCustomPassphrase",
1482 1, 3);
1483 histogram_tester.ExpectBucketCount(
1484 "PasswordManager.AccountsPerSiteHiRes.UserCreated."
1485 "WithoutCustomPassphrase",
1486 2, 2);
1487
1488 histogram_tester.ExpectBucketCount(
1489 "PasswordManager.AccountsPerSiteHiRes.Overall.WithoutCustomPassphrase", 1,
1490 5);
1491 histogram_tester.ExpectBucketCount(
1492 "PasswordManager.AccountsPerSiteHiRes.Overall.WithoutCustomPassphrase", 2,
1493 2);
1494
1495 histogram_tester.ExpectUniqueSample(
1496 "PasswordManager.TotalAccountsHiRes.ByType.AutoGenerated."
1497 "WithoutCustomPassphrase",
1498 2, 1);
1499
1500 histogram_tester.ExpectUniqueSample(
1501 "PasswordManager.TotalAccountsHiRes.ByType.UserCreated."
1502 "WithoutCustomPassphrase",
1503 7, 1);
1504
1505 histogram_tester.ExpectUniqueSample(
1506 "PasswordManager.TotalAccountsHiRes.ByType.Overall."
1507 "WithoutCustomPassphrase",
1508 9, 1);
1509
1510 histogram_tester.ExpectUniqueSample(
1511 "PasswordManager.TotalAccountsHiRes.WithScheme.Android", 2, 1);
1512 histogram_tester.ExpectUniqueSample(
1513 "PasswordManager.TotalAccountsHiRes.WithScheme.Ftp", 1, 1);
1514 histogram_tester.ExpectUniqueSample(
1515 "PasswordManager.TotalAccountsHiRes.WithScheme.Http", 5, 1);
1516 histogram_tester.ExpectUniqueSample(
1517 "PasswordManager.TotalAccountsHiRes.WithScheme.Https", 1, 1);
1518 histogram_tester.ExpectUniqueSample(
1519 "PasswordManager.TotalAccountsHiRes.WithScheme.Other", 0, 1);
1520
1521 histogram_tester.ExpectBucketCount(
1522 "PasswordManager.TimesPasswordUsed.AutoGenerated.WithoutCustomPassphrase",
1523 2, 1);
1524 histogram_tester.ExpectBucketCount(
1525 "PasswordManager.TimesPasswordUsed.AutoGenerated.WithoutCustomPassphrase",
1526 4, 1);
1527
1528 histogram_tester.ExpectBucketCount(
1529 "PasswordManager.TimesPasswordUsed.UserCreated.WithoutCustomPassphrase",
1530 0, 1);
1531 histogram_tester.ExpectBucketCount(
1532 "PasswordManager.TimesPasswordUsed.UserCreated.WithoutCustomPassphrase",
1533 1, 1);
1534 histogram_tester.ExpectBucketCount(
1535 "PasswordManager.TimesPasswordUsed.UserCreated.WithoutCustomPassphrase",
1536 3, 1);
1537
1538 histogram_tester.ExpectBucketCount(
1539 "PasswordManager.TimesPasswordUsed.Overall.WithoutCustomPassphrase", 0,
1540 1);
1541 histogram_tester.ExpectBucketCount(
1542 "PasswordManager.TimesPasswordUsed.Overall.WithoutCustomPassphrase", 1,
1543 1);
1544 histogram_tester.ExpectBucketCount(
1545 "PasswordManager.TimesPasswordUsed.Overall.WithoutCustomPassphrase", 2,
1546 1);
1547 // The bucket for 3 and 4 is the same. Thus we expect two samples here.
1548 histogram_tester.ExpectBucketCount(
1549 "PasswordManager.TimesPasswordUsed.Overall.WithoutCustomPassphrase", 3,
1550 2);
1551
1552 histogram_tester.ExpectUniqueSample(
1553 "PasswordManager.EmptyUsernames.CountInDatabase", 1, 1);
1554 histogram_tester.ExpectUniqueSample("PasswordManager.InaccessiblePasswords",
1555 0, 1);
1556 #if !defined(OS_IOS) && !defined(OS_ANDROID)
1557 histogram_tester.ExpectUniqueSample(
1558 "PasswordManager.BubbleSuppression.DomainsWithSuppressedBubble", 2, 1);
1559 histogram_tester.ExpectUniqueSample(
1560 "PasswordManager.BubbleSuppression.AccountsWithSuppressedBubble", 3, 1);
1561 histogram_tester.ExpectUniqueSample(
1562 "PasswordManager.BubbleSuppression.AccountsInStatisticsTable", 4, 1);
1563 #endif // !defined(OS_IOS) && !defined(OS_ANDROID)
1564 }
1565
TEST_F(LoginDatabaseTest,DuplicatesMetrics_NoDuplicates)1566 TEST_F(LoginDatabaseTest, DuplicatesMetrics_NoDuplicates) {
1567 // No duplicate.
1568 PasswordForm password_form;
1569 password_form.signon_realm = "http://example1.com/";
1570 password_form.origin = GURL("http://example1.com/");
1571 password_form.username_element = ASCIIToUTF16("userelem_1");
1572 password_form.username_value = ASCIIToUTF16("username_1");
1573 password_form.password_value = ASCIIToUTF16("password_1");
1574 ASSERT_EQ(AddChangeForForm(password_form), db().AddLogin(password_form));
1575
1576 // Different username -> no duplicate.
1577 password_form.signon_realm = "http://example2.com/";
1578 password_form.origin = GURL("http://example2.com/");
1579 password_form.username_value = ASCIIToUTF16("username_1");
1580 ASSERT_EQ(AddChangeForForm(password_form), db().AddLogin(password_form));
1581 password_form.username_value = ASCIIToUTF16("username_2");
1582 ASSERT_EQ(AddChangeForForm(password_form), db().AddLogin(password_form));
1583
1584 // Blacklisted forms don't count as duplicates (neither against other
1585 // blacklisted forms nor against actual saved credentials).
1586 password_form.signon_realm = "http://example3.com/";
1587 password_form.origin = GURL("http://example3.com/");
1588 password_form.username_value = ASCIIToUTF16("username_1");
1589 ASSERT_EQ(AddChangeForForm(password_form), db().AddLogin(password_form));
1590 password_form.blacklisted_by_user = true;
1591 password_form.username_value = ASCIIToUTF16("username_2");
1592 ASSERT_EQ(AddChangeForForm(password_form), db().AddLogin(password_form));
1593 password_form.username_value = ASCIIToUTF16("username_3");
1594 ASSERT_EQ(AddChangeForForm(password_form), db().AddLogin(password_form));
1595
1596 base::HistogramTester histogram_tester;
1597 db().ReportMetrics("", false, BulkCheckDone(false));
1598
1599 EXPECT_THAT(histogram_tester.GetAllSamples(
1600 "PasswordManager.CredentialsWithDuplicates"),
1601 testing::ElementsAre(base::Bucket(0, 1)));
1602 EXPECT_THAT(histogram_tester.GetAllSamples(
1603 "PasswordManager.CredentialsWithMismatchedDuplicates"),
1604 testing::ElementsAre(base::Bucket(0, 1)));
1605 }
1606
TEST_F(LoginDatabaseTest,DuplicatesMetrics_ExactDuplicates)1607 TEST_F(LoginDatabaseTest, DuplicatesMetrics_ExactDuplicates) {
1608 // Add some PasswordForms that are "exact" duplicates (only the
1609 // username_element is different, which doesn't matter).
1610 PasswordForm password_form;
1611 password_form.signon_realm = "http://example1.com/";
1612 password_form.origin = GURL("http://example1.com/");
1613 password_form.username_element = ASCIIToUTF16("userelem_1");
1614 password_form.username_value = ASCIIToUTF16("username_1");
1615 ASSERT_EQ(AddChangeForForm(password_form), db().AddLogin(password_form));
1616 password_form.username_element = ASCIIToUTF16("userelem_2");
1617 ASSERT_EQ(AddChangeForForm(password_form), db().AddLogin(password_form));
1618 // The number of "identical" credentials doesn't matter; we count the *sets*
1619 // of duplicates.
1620 password_form.username_element = ASCIIToUTF16("userelem_3");
1621 ASSERT_EQ(AddChangeForForm(password_form), db().AddLogin(password_form));
1622
1623 // Similarly, origin doesn't make forms "different" either.
1624 password_form.signon_realm = "http://example2.com/";
1625 password_form.origin = GURL("http://example2.com/path1");
1626 ASSERT_EQ(AddChangeForForm(password_form), db().AddLogin(password_form));
1627 password_form.origin = GURL("http://example2.com/path2");
1628 ASSERT_EQ(AddChangeForForm(password_form), db().AddLogin(password_form));
1629
1630 base::HistogramTester histogram_tester;
1631 db().ReportMetrics("", false, BulkCheckDone(false));
1632
1633 // There should be 2 groups of "exact" duplicates.
1634 EXPECT_THAT(histogram_tester.GetAllSamples(
1635 "PasswordManager.CredentialsWithDuplicates"),
1636 testing::ElementsAre(base::Bucket(2, 1)));
1637 EXPECT_THAT(histogram_tester.GetAllSamples(
1638 "PasswordManager.CredentialsWithMismatchedDuplicates"),
1639 testing::ElementsAre(base::Bucket(0, 1)));
1640 }
1641
TEST_F(LoginDatabaseTest,DuplicatesMetrics_MismatchedDuplicates)1642 TEST_F(LoginDatabaseTest, DuplicatesMetrics_MismatchedDuplicates) {
1643 // Mismatched duplicates: Identical except for the password.
1644 PasswordForm password_form;
1645 password_form.signon_realm = "http://example1.com/";
1646 password_form.origin = GURL("http://example1.com/");
1647 password_form.username_element = ASCIIToUTF16("userelem_1");
1648 password_form.username_value = ASCIIToUTF16("username_1");
1649 password_form.password_element = ASCIIToUTF16("passelem_1");
1650 password_form.password_value = ASCIIToUTF16("password_1");
1651 ASSERT_EQ(AddChangeForForm(password_form), db().AddLogin(password_form));
1652 // Note: password_value is not part of the unique key, so we need to change
1653 // some other value to be able to insert the duplicate into the DB.
1654 password_form.password_element = ASCIIToUTF16("passelem_2");
1655 password_form.password_value = ASCIIToUTF16("password_2");
1656 ASSERT_EQ(AddChangeForForm(password_form), db().AddLogin(password_form));
1657 // The number of "identical" credentials doesn't matter; we count the *sets*
1658 // of duplicates.
1659 password_form.password_element = ASCIIToUTF16("passelem_3");
1660 password_form.password_value = ASCIIToUTF16("password_3");
1661 ASSERT_EQ(AddChangeForForm(password_form), db().AddLogin(password_form));
1662
1663 base::HistogramTester histogram_tester;
1664 db().ReportMetrics("", false, BulkCheckDone(false));
1665
1666 EXPECT_THAT(histogram_tester.GetAllSamples(
1667 "PasswordManager.CredentialsWithDuplicates"),
1668 testing::ElementsAre(base::Bucket(0, 1)));
1669 EXPECT_THAT(histogram_tester.GetAllSamples(
1670 "PasswordManager.CredentialsWithMismatchedDuplicates"),
1671 testing::ElementsAre(base::Bucket(1, 1)));
1672 }
1673
TEST_F(LoginDatabaseTest,NoMetadata)1674 TEST_F(LoginDatabaseTest, NoMetadata) {
1675 std::unique_ptr<syncer::MetadataBatch> metadata_batch =
1676 db().GetAllSyncMetadata();
1677 ASSERT_THAT(metadata_batch, testing::NotNull());
1678 EXPECT_EQ(0u, metadata_batch->TakeAllMetadata().size());
1679 EXPECT_EQ(sync_pb::ModelTypeState().SerializeAsString(),
1680 metadata_batch->GetModelTypeState().SerializeAsString());
1681 }
1682
TEST_F(LoginDatabaseTest,GetAllSyncMetadata)1683 TEST_F(LoginDatabaseTest, GetAllSyncMetadata) {
1684 sync_pb::EntityMetadata metadata;
1685 // Storage keys must be integers.
1686 const std::string kStorageKey1 = "1";
1687 const std::string kStorageKey2 = "2";
1688 metadata.set_sequence_number(1);
1689
1690 EXPECT_TRUE(
1691 db().UpdateSyncMetadata(syncer::PASSWORDS, kStorageKey1, metadata));
1692
1693 sync_pb::ModelTypeState model_type_state;
1694 model_type_state.set_initial_sync_done(true);
1695
1696 EXPECT_TRUE(db().UpdateModelTypeState(syncer::PASSWORDS, model_type_state));
1697
1698 metadata.set_sequence_number(2);
1699 EXPECT_TRUE(
1700 db().UpdateSyncMetadata(syncer::PASSWORDS, kStorageKey2, metadata));
1701
1702 std::unique_ptr<syncer::MetadataBatch> metadata_batch =
1703 db().GetAllSyncMetadata();
1704 ASSERT_THAT(metadata_batch, testing::NotNull());
1705
1706 EXPECT_TRUE(metadata_batch->GetModelTypeState().initial_sync_done());
1707
1708 syncer::EntityMetadataMap metadata_records =
1709 metadata_batch->TakeAllMetadata();
1710
1711 EXPECT_EQ(metadata_records.size(), 2u);
1712 EXPECT_EQ(metadata_records[kStorageKey1]->sequence_number(), 1);
1713 EXPECT_EQ(metadata_records[kStorageKey2]->sequence_number(), 2);
1714
1715 // Now check that a model type state update replaces the old value
1716 model_type_state.set_initial_sync_done(false);
1717 EXPECT_TRUE(db().UpdateModelTypeState(syncer::PASSWORDS, model_type_state));
1718
1719 metadata_batch = db().GetAllSyncMetadata();
1720 ASSERT_THAT(metadata_batch, testing::NotNull());
1721 EXPECT_FALSE(metadata_batch->GetModelTypeState().initial_sync_done());
1722 }
1723
TEST_F(LoginDatabaseTest,DeleteAllSyncMetadata)1724 TEST_F(LoginDatabaseTest, DeleteAllSyncMetadata) {
1725 sync_pb::EntityMetadata metadata;
1726 // Storage keys must be integers.
1727 const std::string kStorageKey1 = "1";
1728 const std::string kStorageKey2 = "2";
1729 metadata.set_sequence_number(1);
1730
1731 EXPECT_TRUE(
1732 db().UpdateSyncMetadata(syncer::PASSWORDS, kStorageKey1, metadata));
1733
1734 sync_pb::ModelTypeState model_type_state;
1735 model_type_state.set_initial_sync_done(true);
1736
1737 EXPECT_TRUE(db().UpdateModelTypeState(syncer::PASSWORDS, model_type_state));
1738
1739 metadata.set_sequence_number(2);
1740 EXPECT_TRUE(
1741 db().UpdateSyncMetadata(syncer::PASSWORDS, kStorageKey2, metadata));
1742
1743 std::unique_ptr<syncer::MetadataBatch> metadata_batch =
1744 db().GetAllSyncMetadata();
1745 ASSERT_THAT(metadata_batch, testing::NotNull());
1746 ASSERT_EQ(metadata_batch->TakeAllMetadata().size(), 2u);
1747
1748 db().DeleteAllSyncMetadata();
1749
1750 std::unique_ptr<syncer::MetadataBatch> empty_metadata_batch =
1751 db().GetAllSyncMetadata();
1752 ASSERT_THAT(empty_metadata_batch, testing::NotNull());
1753 EXPECT_EQ(empty_metadata_batch->TakeAllMetadata().size(), 0u);
1754 }
1755
TEST_F(LoginDatabaseTest,WriteThenDeleteSyncMetadata)1756 TEST_F(LoginDatabaseTest, WriteThenDeleteSyncMetadata) {
1757 sync_pb::EntityMetadata metadata;
1758 const std::string kStorageKey = "1";
1759 sync_pb::ModelTypeState model_type_state;
1760
1761 model_type_state.set_initial_sync_done(true);
1762
1763 metadata.set_client_tag_hash("client_hash");
1764
1765 // Write the data into the store.
1766 EXPECT_TRUE(
1767 db().UpdateSyncMetadata(syncer::PASSWORDS, kStorageKey, metadata));
1768 EXPECT_TRUE(db().UpdateModelTypeState(syncer::PASSWORDS, model_type_state));
1769 // Delete the data we just wrote.
1770 EXPECT_TRUE(db().ClearSyncMetadata(syncer::PASSWORDS, kStorageKey));
1771
1772 std::unique_ptr<syncer::MetadataBatch> metadata_batch =
1773 db().GetAllSyncMetadata();
1774 ASSERT_THAT(metadata_batch, testing::NotNull());
1775
1776 // It shouldn't be there any more.
1777 syncer::EntityMetadataMap metadata_records =
1778 metadata_batch->TakeAllMetadata();
1779 EXPECT_EQ(metadata_records.size(), 0u);
1780
1781 // Now delete the model type state.
1782 EXPECT_TRUE(db().ClearModelTypeState(syncer::PASSWORDS));
1783 metadata_batch = db().GetAllSyncMetadata();
1784 ASSERT_THAT(metadata_batch, testing::NotNull());
1785
1786 EXPECT_EQ(sync_pb::ModelTypeState().SerializeAsString(),
1787 metadata_batch->GetModelTypeState().SerializeAsString());
1788 }
1789
1790 #if defined(OS_POSIX)
1791 // Only the current user has permission to read the database.
1792 //
1793 // Only POSIX because GetPosixFilePermissions() only exists on POSIX.
1794 // This tests that sql::Database::set_restrict_to_user() was called,
1795 // and that function is a noop on non-POSIX platforms in any case.
TEST_F(LoginDatabaseTest,FilePermissions)1796 TEST_F(LoginDatabaseTest, FilePermissions) {
1797 int mode = base::FILE_PERMISSION_MASK;
1798 EXPECT_TRUE(base::GetPosixFilePermissions(file_, &mode));
1799 EXPECT_EQ((mode & base::FILE_PERMISSION_USER_MASK), mode);
1800 }
1801 #endif // defined(OS_POSIX)
1802
1803 #if !defined(OS_IOS)
1804 // Test that LoginDatabase encrypts the password values that it stores.
TEST_F(LoginDatabaseTest,EncryptionEnabled)1805 TEST_F(LoginDatabaseTest, EncryptionEnabled) {
1806 PasswordForm password_form;
1807 GenerateExamplePasswordForm(&password_form);
1808 base::FilePath file = temp_dir_.GetPath().AppendASCII("TestUnencryptedDB");
1809 {
1810 LoginDatabase db(file, IsAccountStore(false));
1811 ASSERT_TRUE(db.Init());
1812 EXPECT_EQ(AddChangeForForm(password_form), db.AddLogin(password_form));
1813 }
1814 base::string16 decrypted_pw;
1815 ASSERT_TRUE(OSCrypt::DecryptString16(
1816 GetColumnValuesFromDatabase<std::string>(file, "password_value").at(0),
1817 &decrypted_pw));
1818 EXPECT_EQ(decrypted_pw, password_form.password_value);
1819 }
1820 #endif // !defined(OS_IOS)
1821
1822 #if defined(OS_LINUX)
1823 // Test that LoginDatabase does not encrypt values when encryption is disabled.
1824 // TODO(crbug.com/829857) This is supported only for Linux, while transitioning
1825 // into LoginDB with full encryption.
TEST_F(LoginDatabaseTest,EncryptionDisabled)1826 TEST_F(LoginDatabaseTest, EncryptionDisabled) {
1827 PasswordForm password_form;
1828 GenerateExamplePasswordForm(&password_form);
1829 base::FilePath file = temp_dir_.GetPath().AppendASCII("TestUnencryptedDB");
1830 {
1831 LoginDatabase db(file, IsAccountStore(false));
1832 db.disable_encryption();
1833 ASSERT_TRUE(db.Init());
1834 EXPECT_EQ(AddChangeForForm(password_form), db.AddLogin(password_form));
1835 }
1836 EXPECT_EQ(
1837 GetColumnValuesFromDatabase<std::string>(file, "password_value").at(0),
1838 base::UTF16ToUTF8(password_form.password_value));
1839 }
1840 #endif // defined(OS_LINUX)
1841
1842 #if defined(OS_ANDROID) || defined(OS_CHROMEOS)
1843 // On Android and ChromeOS there is a mix of plain-text and obfuscated
1844 // passwords. Verify that they can both be accessed. Obfuscated passwords start
1845 // with "v10". Some password values also start with "v10". Test that both are
1846 // accessible (this doesn't work for any plain-text value).
TEST_F(LoginDatabaseTest,HandleObfuscationMix)1847 TEST_F(LoginDatabaseTest, HandleObfuscationMix) {
1848 const char k_obfuscated_pw[] = "v10pass1";
1849 const char k_plain_text_pw1[] = "v10pass2";
1850 const char k_plain_text_pw2[] = "v11pass3";
1851
1852 base::FilePath file = temp_dir_.GetPath().AppendASCII("TestUnencryptedDB");
1853 {
1854 LoginDatabase db(file, IsAccountStore(false));
1855 ASSERT_TRUE(db.Init());
1856 // Add obfuscated (new) entries.
1857 PasswordForm password_form;
1858 GenerateExamplePasswordForm(&password_form);
1859 password_form.password_value = ASCIIToUTF16(k_obfuscated_pw);
1860 EXPECT_EQ(AddChangeForForm(password_form), db.AddLogin(password_form));
1861 // Add plain-text (old) entries.
1862 db.disable_encryption();
1863 GenerateExamplePasswordForm(&password_form);
1864 password_form.username_value = ASCIIToUTF16("other_username");
1865 password_form.password_value = ASCIIToUTF16(k_plain_text_pw1);
1866 EXPECT_EQ(AddChangeForForm(password_form), db.AddLogin(password_form));
1867 GenerateExamplePasswordForm(&password_form);
1868 password_form.username_value = ASCIIToUTF16("other_username2");
1869 password_form.password_value = ASCIIToUTF16(k_plain_text_pw2);
1870 EXPECT_EQ(AddChangeForForm(password_form), db.AddLogin(password_form));
1871 }
1872
1873 std::vector<std::unique_ptr<autofill::PasswordForm>> forms;
1874 {
1875 LoginDatabase db(file, IsAccountStore(false));
1876 ASSERT_TRUE(db.Init());
1877 ASSERT_TRUE(db.GetAutofillableLogins(&forms));
1878 }
1879
1880 // On disk, unobfuscated passwords are as-is, while obfuscated passwords have
1881 // been changed (obfuscated).
1882 EXPECT_THAT(GetColumnValuesFromDatabase<std::string>(file, "password_value"),
1883 UnorderedElementsAre(Ne(k_obfuscated_pw), k_plain_text_pw1,
1884 k_plain_text_pw2));
1885 // LoginDatabase serves the original values.
1886 ASSERT_THAT(forms, SizeIs(3));
1887 EXPECT_EQ(k_obfuscated_pw, UTF16ToASCII(forms[0]->password_value));
1888 EXPECT_EQ(k_plain_text_pw1, UTF16ToASCII(forms[1]->password_value));
1889 EXPECT_EQ(k_plain_text_pw2, UTF16ToASCII(forms[2]->password_value));
1890 }
1891 #endif // defined(OS_ANDROID) || defined(OS_CHROMEOS)
1892
1893 // If the database initialisation fails, the initialisation transaction should
1894 // roll back without crashing.
TEST(LoginDatabaseFailureTest,Init_NoCrashOnFailedRollback)1895 TEST(LoginDatabaseFailureTest, Init_NoCrashOnFailedRollback) {
1896 base::ScopedTempDir temp_dir;
1897 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
1898 base::FilePath database_path = temp_dir.GetPath().AppendASCII("test.db");
1899
1900 // To cause an init failure, set the compatible version to be higher than the
1901 // current version (in reality, this could happen if, e.g., someone opened a
1902 // Canary-created profile with Chrome Stable.
1903 {
1904 sql::Database connection;
1905 sql::MetaTable meta_table;
1906 ASSERT_TRUE(connection.Open(database_path));
1907 ASSERT_TRUE(meta_table.Init(&connection, kCurrentVersionNumber + 1,
1908 kCurrentVersionNumber + 1));
1909 }
1910
1911 // Now try to init the database with the file. The test succeeds if it does
1912 // not crash.
1913 LoginDatabase db(database_path, IsAccountStore(false));
1914 EXPECT_FALSE(db.Init());
1915 }
1916
1917 // If the database version is from the future, it shouldn't be downgraded.
TEST(LoginDatabaseFutureLoginDatabase,ShouldNotDowngradeDatabaseVersion)1918 TEST(LoginDatabaseFutureLoginDatabase, ShouldNotDowngradeDatabaseVersion) {
1919 base::ScopedTempDir temp_dir;
1920 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
1921 base::FilePath database_path = temp_dir.GetPath().AppendASCII("test.db");
1922
1923 const int kDBFutureVersion = kCurrentVersionNumber + 1000;
1924
1925 {
1926 // Open a database with the current version.
1927 LoginDatabase db(database_path, IsAccountStore(false));
1928 EXPECT_TRUE(db.Init());
1929 }
1930 {
1931 // Overwrite the current version to be |kDBFutureVersion|
1932 sql::Database connection;
1933 sql::MetaTable meta_table;
1934 ASSERT_TRUE(connection.Open(database_path));
1935 // Set the DB version to be coming from the future.
1936 ASSERT_TRUE(meta_table.Init(&connection, kDBFutureVersion,
1937 kCompatibleVersionNumber));
1938 meta_table.SetVersionNumber(kDBFutureVersion);
1939 }
1940 {
1941 // Open the database again.
1942 LoginDatabase db(database_path, IsAccountStore(false));
1943 EXPECT_TRUE(db.Init());
1944 }
1945 {
1946 // The DB version should remain the same.
1947 sql::Database connection;
1948 sql::MetaTable meta_table;
1949 ASSERT_TRUE(connection.Open(database_path));
1950 ASSERT_TRUE(meta_table.Init(&connection, kDBFutureVersion,
1951 kCompatibleVersionNumber));
1952 EXPECT_EQ(kDBFutureVersion, meta_table.GetVersionNumber());
1953 }
1954 }
1955
1956 // Test the migration from GetParam() version to kCurrentVersionNumber.
1957 class LoginDatabaseMigrationTest : public testing::TestWithParam<int> {
1958 protected:
SetUp()1959 void SetUp() override {
1960 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
1961 database_dump_location_ = database_dump_location_.AppendASCII("components")
1962 .AppendASCII("test")
1963 .AppendASCII("data")
1964 .AppendASCII("password_manager");
1965 database_path_ = temp_dir_.GetPath().AppendASCII("test.db");
1966 OSCryptMocker::SetUp();
1967 }
1968
TearDown()1969 void TearDown() override { OSCryptMocker::TearDown(); }
1970
1971 // Creates the database from |sql_file|.
CreateDatabase(base::StringPiece sql_file)1972 void CreateDatabase(base::StringPiece sql_file) {
1973 base::FilePath database_dump;
1974 ASSERT_TRUE(base::PathService::Get(base::DIR_SOURCE_ROOT, &database_dump));
1975 database_dump =
1976 database_dump.Append(database_dump_location_).AppendASCII(sql_file);
1977 ASSERT_TRUE(
1978 sql::test::CreateDatabaseFromSQL(database_path_, database_dump));
1979 }
1980
DestroyDatabase()1981 void DestroyDatabase() {
1982 if (!database_path_.empty())
1983 sql::Database::Delete(database_path_);
1984 }
1985
1986 // Returns the database version for the test.
version() const1987 int version() const { return GetParam(); }
1988
1989 // Actual test body.
1990 void MigrationToVCurrent(base::StringPiece sql_file);
1991
1992 base::FilePath database_path_;
1993
1994 private:
1995 base::FilePath database_dump_location_;
1996 base::ScopedTempDir temp_dir_;
1997 base::test::TaskEnvironment task_environment_;
1998 };
1999
MigrationToVCurrent(base::StringPiece sql_file)2000 void LoginDatabaseMigrationTest::MigrationToVCurrent(
2001 base::StringPiece sql_file) {
2002 SCOPED_TRACE(testing::Message("Version file = ") << sql_file);
2003 CreateDatabase(sql_file);
2004 // Original date, in seconds since UTC epoch.
2005 std::vector<int64_t> date_created(
2006 GetColumnValuesFromDatabase<int64_t>(database_path_, "date_created"));
2007 if (version() == 10) // Version 10 has a duplicate entry.
2008 ASSERT_EQ(4U, date_created.size());
2009 else
2010 ASSERT_EQ(3U, date_created.size());
2011 // Migration to version 8 performs changes dates to the new format.
2012 // So for versions less of equal to 8 create date should be in old
2013 // format before migration and in new format after.
2014 if (version() <= 8) {
2015 ASSERT_EQ(1402955745, date_created[0]);
2016 ASSERT_EQ(1402950000, date_created[1]);
2017 ASSERT_EQ(1402950000, date_created[2]);
2018 } else {
2019 ASSERT_EQ(13047429345000000, date_created[0]);
2020 ASSERT_EQ(13047423600000000, date_created[1]);
2021 ASSERT_EQ(13047423600000000, date_created[2]);
2022 }
2023
2024 {
2025 // Assert that the database was successfully opened and updated
2026 // to current version.
2027 LoginDatabase db(database_path_, IsAccountStore(false));
2028 ASSERT_TRUE(db.Init());
2029
2030 // Check that the contents was preserved.
2031 std::vector<std::unique_ptr<PasswordForm>> result;
2032 EXPECT_TRUE(db.GetAutofillableLogins(&result));
2033 EXPECT_THAT(result, UnorderedElementsAre(Pointee(IsGoogle1Account()),
2034 Pointee(IsGoogle2Account()),
2035 Pointee(IsBasicAuthAccount())));
2036
2037 // Verifies that the final version can save all the appropriate fields.
2038 PasswordForm form;
2039 GenerateExamplePasswordForm(&form);
2040 // Add the same form twice to test the constraints in the database.
2041 EXPECT_EQ(AddChangeForForm(form), db.AddLogin(form));
2042 PasswordStoreChangeList list;
2043 list.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, form));
2044 list.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form));
2045 EXPECT_EQ(list, db.AddLogin(form));
2046
2047 result.clear();
2048 EXPECT_TRUE(db.GetLogins(PasswordStore::FormDigest(form), &result));
2049 ASSERT_EQ(1U, result.size());
2050 EXPECT_EQ(form, *result[0]);
2051 EXPECT_TRUE(db.RemoveLogin(form, /*changes=*/nullptr));
2052 }
2053 // New date, in microseconds since platform independent epoch.
2054 std::vector<int64_t> new_date_created(
2055 GetColumnValuesFromDatabase<int64_t>(database_path_, "date_created"));
2056 ASSERT_EQ(3U, new_date_created.size());
2057 if (version() <= 8) {
2058 // Check that the two dates match up.
2059 for (size_t i = 0; i < date_created.size(); ++i) {
2060 EXPECT_EQ(base::Time::FromInternalValue(new_date_created[i]),
2061 base::Time::FromTimeT(date_created[i]));
2062 }
2063 } else {
2064 ASSERT_EQ(13047429345000000, new_date_created[0]);
2065 ASSERT_EQ(13047423600000000, new_date_created[1]);
2066 ASSERT_EQ(13047423600000000, new_date_created[2]);
2067 }
2068
2069 if (version() >= 7 && version() <= 13) {
2070 // The "avatar_url" column first appeared in version 7. In version 14,
2071 // it was renamed to "icon_url". Migration from a version <= 13
2072 // to >= 14 should not break theses URLs.
2073 std::vector<std::string> urls(
2074 GetColumnValuesFromDatabase<std::string>(database_path_, "icon_url"));
2075
2076 EXPECT_THAT(urls, UnorderedElementsAre("", "https://www.google.com/icon",
2077 "https://www.google.com/icon"));
2078 }
2079
2080 {
2081 // On versions < 15 |kCompatibleVersionNumber| was set to 1, but
2082 // the migration should bring it to the correct value.
2083 sql::Database db;
2084 sql::MetaTable meta_table;
2085 ASSERT_TRUE(db.Open(database_path_));
2086 ASSERT_TRUE(
2087 meta_table.Init(&db, kCurrentVersionNumber, kCompatibleVersionNumber));
2088 EXPECT_EQ(password_manager::kCompatibleVersionNumber,
2089 meta_table.GetCompatibleVersionNumber());
2090 }
2091 DestroyDatabase();
2092 }
2093
2094 // Tests the migration of the login database from version() to
2095 // kCurrentVersionNumber.
TEST_P(LoginDatabaseMigrationTest,MigrationToVCurrent)2096 TEST_P(LoginDatabaseMigrationTest, MigrationToVCurrent) {
2097 MigrationToVCurrent(base::StringPrintf("login_db_v%d.sql", version()));
2098 }
2099
2100 class LoginDatabaseMigrationTestV9 : public LoginDatabaseMigrationTest {};
2101
2102 // Tests migration from the alternative version #9, see crbug.com/423716.
TEST_P(LoginDatabaseMigrationTestV9,V9WithoutUseAdditionalAuthField)2103 TEST_P(LoginDatabaseMigrationTestV9, V9WithoutUseAdditionalAuthField) {
2104 ASSERT_EQ(9, version());
2105 MigrationToVCurrent("login_db_v9_without_use_additional_auth_field.sql");
2106 }
2107
2108 class LoginDatabaseMigrationTestBroken : public LoginDatabaseMigrationTest {};
2109
2110 // Test migrating certain databases with incorrect version.
2111 // http://crbug.com/295851
TEST_P(LoginDatabaseMigrationTestBroken,Broken)2112 TEST_P(LoginDatabaseMigrationTestBroken, Broken) {
2113 MigrationToVCurrent(base::StringPrintf("login_db_v%d_broken.sql", version()));
2114 }
2115
2116 INSTANTIATE_TEST_SUITE_P(MigrationToVCurrent,
2117 LoginDatabaseMigrationTest,
2118 testing::Range(1, kCurrentVersionNumber + 1));
2119 INSTANTIATE_TEST_SUITE_P(MigrationToVCurrent,
2120 LoginDatabaseMigrationTestV9,
2121 testing::Values(9));
2122 INSTANTIATE_TEST_SUITE_P(MigrationToVCurrent,
2123 LoginDatabaseMigrationTestBroken,
2124 testing::Values(1, 2, 3, 24));
2125
2126 class LoginDatabaseUndecryptableLoginsTest : public testing::Test {
2127 protected:
LoginDatabaseUndecryptableLoginsTest()2128 LoginDatabaseUndecryptableLoginsTest() {}
2129
SetUp()2130 void SetUp() override {
2131 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
2132 database_path_ = temp_dir_.GetPath().AppendASCII("test.db");
2133 OSCryptMocker::SetUp();
2134 }
2135
TearDown()2136 void TearDown() override { OSCryptMocker::TearDown(); }
2137
2138 // Generates login depending on |unique_string| and |origin| parameters and
2139 // adds it to the database. Changes encrypted password in the database if the
2140 // |should_be_corrupted| flag is active.
2141 PasswordForm AddDummyLogin(const std::string& unique_string,
2142 const GURL& origin,
2143 bool should_be_corrupted);
2144
database_path() const2145 base::FilePath database_path() const { return database_path_; }
2146
testing_local_state()2147 TestingPrefServiceSimple& testing_local_state() {
2148 return testing_local_state_;
2149 }
2150
RunUntilIdle()2151 void RunUntilIdle() { task_environment_.RunUntilIdle(); }
2152
2153 private:
2154 base::FilePath database_path_;
2155 base::ScopedTempDir temp_dir_;
2156 base::test::TaskEnvironment task_environment_;
2157 TestingPrefServiceSimple testing_local_state_;
2158
2159 DISALLOW_COPY_AND_ASSIGN(LoginDatabaseUndecryptableLoginsTest);
2160 };
2161
AddDummyLogin(const std::string & unique_string,const GURL & origin,bool should_be_corrupted)2162 PasswordForm LoginDatabaseUndecryptableLoginsTest::AddDummyLogin(
2163 const std::string& unique_string,
2164 const GURL& origin,
2165 bool should_be_corrupted) {
2166 // Create a dummy password form.
2167 const base::string16 unique_string16 = ASCIIToUTF16(unique_string);
2168 PasswordForm form;
2169 form.origin = origin;
2170 form.username_element = unique_string16;
2171 form.username_value = unique_string16;
2172 form.password_element = unique_string16;
2173 form.password_value = unique_string16;
2174 form.signon_realm = origin.GetOrigin().spec();
2175
2176 {
2177 LoginDatabase db(database_path(), IsAccountStore(false));
2178 EXPECT_TRUE(db.Init());
2179 EXPECT_EQ(db.AddLogin(form), AddChangeForForm(form));
2180 }
2181
2182 if (should_be_corrupted) {
2183 sql::Database db;
2184 EXPECT_TRUE(db.Open(database_path()));
2185
2186 // Change encrypted password in the database if the login should be
2187 // corrupted.
2188 std::string statement =
2189 "UPDATE logins SET password_value = password_value || 'trash' "
2190 "WHERE signon_realm = ? AND username_value = ?";
2191 sql::Statement s(db.GetCachedStatement(SQL_FROM_HERE, statement.c_str()));
2192 s.BindString(0, form.signon_realm);
2193 s.BindString(1, base::UTF16ToUTF8(form.username_value));
2194
2195 EXPECT_TRUE(s.is_valid());
2196 EXPECT_TRUE(s.Run());
2197 EXPECT_EQ(db.GetLastChangeCount(), 1);
2198 }
2199
2200 // When we retrieve the form from the store, |in_store| should be set.
2201 form.in_store = PasswordForm::Store::kProfileStore;
2202
2203 return form;
2204 }
2205
TEST_F(LoginDatabaseUndecryptableLoginsTest,DeleteUndecryptableLoginsTest)2206 TEST_F(LoginDatabaseUndecryptableLoginsTest, DeleteUndecryptableLoginsTest) {
2207 // Disable feature for deleting corrupted passwords, so GetAutofillableLogins
2208 // doesn't remove any passwords.
2209 base::test::ScopedFeatureList scoped_feature_list;
2210 scoped_feature_list.InitAndDisableFeature(
2211 features::kDeleteCorruptedPasswords);
2212
2213 auto form1 = AddDummyLogin("foo1", GURL("https://foo1.com/"), false);
2214 auto form2 = AddDummyLogin("foo2", GURL("https://foo2.com/"), true);
2215 auto form3 = AddDummyLogin("foo3", GURL("https://foo3.com/"), false);
2216
2217 LoginDatabase db(database_path(), IsAccountStore(false));
2218 base::HistogramTester histogram_tester;
2219 ASSERT_TRUE(db.Init());
2220
2221 #if defined(OS_MACOSX) && !defined(OS_IOS)
2222 testing_local_state().registry()->RegisterTimePref(prefs::kPasswordRecovery,
2223 base::Time());
2224 db.InitPasswordRecoveryUtil(std::make_unique<PasswordRecoveryUtilMac>(
2225 &testing_local_state(), base::ThreadTaskRunnerHandle::Get()));
2226
2227 // Make sure that we can't get any logins when database is corrupted.
2228 std::vector<std::unique_ptr<PasswordForm>> result;
2229 EXPECT_FALSE(db.GetAutofillableLogins(&result));
2230 EXPECT_TRUE(result.empty());
2231
2232 // Delete undecryptable logins and make sure we can get valid logins.
2233 EXPECT_EQ(DatabaseCleanupResult::kSuccess, db.DeleteUndecryptableLogins());
2234 EXPECT_TRUE(db.GetAutofillableLogins(&result));
2235 EXPECT_THAT(result, UnorderedElementsAre(Pointee(form1), Pointee(form3)));
2236
2237 RunUntilIdle();
2238
2239 // Make sure that password recovery pref is set.
2240 ASSERT_TRUE(testing_local_state().HasPrefPath(prefs::kPasswordRecovery));
2241 #else
2242 EXPECT_EQ(DatabaseCleanupResult::kSuccess, db.DeleteUndecryptableLogins());
2243 #endif
2244
2245 // Check histograms.
2246 #if defined(OS_MACOSX) && !defined(OS_IOS)
2247 histogram_tester.ExpectUniqueSample("PasswordManager.CleanedUpPasswords", 1,
2248 1);
2249 histogram_tester.ExpectUniqueSample(
2250 "PasswordManager.DeleteUndecryptableLoginsReturnValue",
2251 metrics_util::DeleteCorruptedPasswordsResult::kSuccessPasswordsDeleted,
2252 1);
2253 #else
2254 EXPECT_TRUE(
2255 histogram_tester.GetAllSamples("PasswordManager.CleanedUpPasswords")
2256 .empty());
2257 #endif
2258 }
2259
2260 #if defined(OS_MACOSX) && !defined(OS_IOS)
TEST_F(LoginDatabaseUndecryptableLoginsTest,PasswordRecoveryEnabledGetLogins)2261 TEST_F(LoginDatabaseUndecryptableLoginsTest, PasswordRecoveryEnabledGetLogins) {
2262 base::HistogramTester histogram_tester;
2263 base::test::ScopedFeatureList scoped_feature_list;
2264 scoped_feature_list.InitAndEnableFeature(features::kDeleteCorruptedPasswords);
2265
2266 auto form1 = AddDummyLogin("foo1", GURL("https://foo1.com/"), false);
2267 auto form2 = AddDummyLogin("foo2", GURL("https://foo2.com/"), true);
2268 auto form3 = AddDummyLogin("foo3", GURL("https://foo3.com/"), false);
2269
2270 LoginDatabase db(database_path(), IsAccountStore(false));
2271 ASSERT_TRUE(db.Init());
2272
2273 testing_local_state().registry()->RegisterTimePref(prefs::kPasswordRecovery,
2274 base::Time());
2275 db.InitPasswordRecoveryUtil(std::make_unique<PasswordRecoveryUtilMac>(
2276 &testing_local_state(), base::ThreadTaskRunnerHandle::Get()));
2277
2278 std::vector<std::unique_ptr<PasswordForm>> result;
2279 EXPECT_TRUE(db.GetAutofillableLogins(&result));
2280 EXPECT_THAT(result, UnorderedElementsAre(Pointee(form1), Pointee(form3)));
2281
2282 RunUntilIdle();
2283 EXPECT_TRUE(testing_local_state().HasPrefPath(prefs::kPasswordRecovery));
2284
2285 histogram_tester.ExpectUniqueSample(
2286 "PasswordManager.RemovedCorruptedPasswords", 1, 1);
2287 histogram_tester.ExpectUniqueSample(
2288 "PasswordManager.DeleteCorruptedPasswordsResult",
2289 metrics_util::DeleteCorruptedPasswordsResult::kSuccessPasswordsDeleted,
2290 1);
2291 }
2292
TEST_F(LoginDatabaseUndecryptableLoginsTest,PasswordRecoveryDisabledGetLogins)2293 TEST_F(LoginDatabaseUndecryptableLoginsTest,
2294 PasswordRecoveryDisabledGetLogins) {
2295 base::HistogramTester histogram_tester;
2296 base::test::ScopedFeatureList scoped_feature_list;
2297 scoped_feature_list.InitAndDisableFeature(
2298 features::kDeleteCorruptedPasswords);
2299
2300 AddDummyLogin("foo1", GURL("https://foo1.com/"), false);
2301 AddDummyLogin("foo2", GURL("https://foo2.com/"), true);
2302
2303 LoginDatabase db(database_path(), IsAccountStore(false));
2304 ASSERT_TRUE(db.Init());
2305
2306 testing_local_state().registry()->RegisterTimePref(prefs::kPasswordRecovery,
2307 base::Time());
2308 db.InitPasswordRecoveryUtil(std::make_unique<PasswordRecoveryUtilMac>(
2309 &testing_local_state(), base::ThreadTaskRunnerHandle::Get()));
2310
2311 std::vector<std::unique_ptr<PasswordForm>> result;
2312 EXPECT_FALSE(db.GetAutofillableLogins(&result));
2313 EXPECT_TRUE(result.empty());
2314
2315 RunUntilIdle();
2316 EXPECT_FALSE(testing_local_state().HasPrefPath(prefs::kPasswordRecovery));
2317
2318 EXPECT_TRUE(histogram_tester
2319 .GetAllSamples("PasswordManager.RemovedCorruptedPasswords")
2320 .empty());
2321 EXPECT_TRUE(
2322 histogram_tester
2323 .GetAllSamples("PasswordManager.DeleteCorruptedPasswordsResult")
2324 .empty());
2325 }
2326
TEST_F(LoginDatabaseUndecryptableLoginsTest,PasswordRecoveryEnabledKeychainLocked)2327 TEST_F(LoginDatabaseUndecryptableLoginsTest,
2328 PasswordRecoveryEnabledKeychainLocked) {
2329 base::HistogramTester histogram_tester;
2330 base::test::ScopedFeatureList scoped_feature_list;
2331 scoped_feature_list.InitAndEnableFeature(features::kDeleteCorruptedPasswords);
2332
2333 // This is a valid entry.
2334 auto form = AddDummyLogin("foo", GURL("https://foo.com/"), false);
2335
2336 OSCryptMocker::SetBackendLocked(true);
2337
2338 LoginDatabase db(database_path(), IsAccountStore(false));
2339 ASSERT_TRUE(db.Init());
2340
2341 testing_local_state().registry()->RegisterTimePref(prefs::kPasswordRecovery,
2342 base::Time());
2343 db.InitPasswordRecoveryUtil(std::make_unique<PasswordRecoveryUtilMac>(
2344 &testing_local_state(), base::ThreadTaskRunnerHandle::Get()));
2345
2346 std::vector<std::unique_ptr<PasswordForm>> result;
2347 EXPECT_FALSE(db.GetAutofillableLogins(&result));
2348 EXPECT_TRUE(result.empty());
2349
2350 RunUntilIdle();
2351 EXPECT_FALSE(testing_local_state().HasPrefPath(prefs::kPasswordRecovery));
2352
2353 EXPECT_TRUE(histogram_tester
2354 .GetAllSamples("PasswordManager.RemovedCorruptedPasswords")
2355 .empty());
2356 EXPECT_TRUE(
2357 histogram_tester
2358 .GetAllSamples("PasswordManager.DeleteCorruptedPasswordsResult")
2359 .empty());
2360
2361 // Note: it's not possible that encryption suddenly becomes available. This is
2362 // only used to check that the form is not removed from the database.
2363 OSCryptMocker::SetBackendLocked(false);
2364
2365 EXPECT_TRUE(db.GetAutofillableLogins(&result));
2366 EXPECT_THAT(result, UnorderedElementsAre(Pointee(form)));
2367 }
2368
TEST_F(LoginDatabaseUndecryptableLoginsTest,KeychainLockedTest)2369 TEST_F(LoginDatabaseUndecryptableLoginsTest, KeychainLockedTest) {
2370 AddDummyLogin("foo1", GURL("https://foo1.com/"), false);
2371 AddDummyLogin("foo2", GURL("https://foo2.com/"), true);
2372
2373 OSCryptMocker::SetBackendLocked(true);
2374 LoginDatabase db(database_path(), IsAccountStore(false));
2375 base::HistogramTester histogram_tester;
2376 ASSERT_TRUE(db.Init());
2377 EXPECT_EQ(DatabaseCleanupResult::kEncryptionUnavailable,
2378 db.DeleteUndecryptableLogins());
2379
2380 EXPECT_TRUE(
2381 histogram_tester.GetAllSamples("PasswordManager.CleanedUpPasswords")
2382 .empty());
2383 histogram_tester.ExpectUniqueSample(
2384 "PasswordManager.DeleteUndecryptableLoginsReturnValue",
2385 metrics_util::DeleteCorruptedPasswordsResult::kEncryptionUnavailable, 1);
2386 }
2387 #endif // defined(OS_MACOSX) && !defined(OS_IOS)
2388
2389 // Test retrieving password forms by supplied password.
TEST_F(LoginDatabaseTest,GetLoginsByPassword)2390 TEST_F(LoginDatabaseTest, GetLoginsByPassword) {
2391 std::vector<std::unique_ptr<PasswordForm>> result;
2392 PrimaryKeyToFormMap key_to_form_map;
2393
2394 const base::string16 duplicated_password =
2395 base::ASCIIToUTF16("duplicated_password");
2396
2397 // Insert first logins.
2398 PasswordForm form1;
2399 GenerateExamplePasswordForm(&form1);
2400 form1.password_value = duplicated_password;
2401 PasswordStoreChangeList changes = db().AddLogin(form1);
2402 ASSERT_EQ(AddChangeForForm(form1), changes);
2403
2404 // Check if there is exactly one form with this password.
2405 std::vector<std::unique_ptr<PasswordForm>> forms;
2406 EXPECT_TRUE(db().GetLoginsByPassword(duplicated_password, &forms));
2407 EXPECT_THAT(forms, UnorderedElementsAre(Pointee(form1)));
2408
2409 // Insert another form with a different password for a different origin.
2410 PasswordForm form2;
2411 GenerateExamplePasswordForm(&form2);
2412 form2.origin = GURL("https://myrandomsite.com/login.php");
2413 form2.signon_realm = form2.origin.GetOrigin().spec();
2414 form2.password_value = base::ASCIIToUTF16("my-unique-random-password");
2415 changes = db().AddLogin(form2);
2416 ASSERT_EQ(AddChangeForForm(form2), changes);
2417
2418 // Check if there is still exactly one form with the duplicated_password.
2419 EXPECT_TRUE(db().GetLoginsByPassword(duplicated_password, &forms));
2420 EXPECT_THAT(forms, UnorderedElementsAre(Pointee(form1)));
2421
2422 // Insert another form with the target password for a different origin.
2423 PasswordForm form3;
2424 GenerateExamplePasswordForm(&form3);
2425 form3.origin = GURL("https://myrandomsite1.com/login.php");
2426 form3.signon_realm = form3.origin.GetOrigin().spec();
2427 form3.password_value = duplicated_password;
2428 changes = db().AddLogin(form3);
2429 ASSERT_EQ(AddChangeForForm(form3), changes);
2430
2431 // Check if there are exactly two forms with the duplicated_password.
2432 EXPECT_TRUE(db().GetLoginsByPassword(duplicated_password, &forms));
2433 EXPECT_THAT(forms, UnorderedElementsAre(Pointee(form1), Pointee(form3)));
2434 }
2435
2436 } // namespace password_manager
2437