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