Cleanup BrowserPluginEmbedder
[chromium-blink-merge.git] / chrome / browser / password_manager / native_backend_libsecret_unittest.cc
blob603b5457a03de0b402923f2e4b99ba23a62b4019
1 // Copyright (c) 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include <stdarg.h>
7 #include "base/basictypes.h"
8 #include "base/prefs/pref_service.h"
9 #include "base/stl_util.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/time/time.h"
15 #include "chrome/browser/password_manager/native_backend_libsecret.h"
16 #include "chrome/test/base/testing_profile.h"
17 #include "components/autofill/core/common/password_form.h"
18 #include "components/password_manager/core/browser/psl_matching_helper.h"
19 #include "components/password_manager/core/common/password_manager_pref_names.h"
20 #include "testing/gtest/include/gtest/gtest.h"
22 using autofill::PasswordForm;
23 using base::UTF8ToUTF16;
24 using base::UTF16ToUTF8;
25 using password_manager::PasswordStoreChange;
26 using password_manager::PasswordStoreChangeList;
28 namespace {
30 // What follows is a very simple implementation of the subset of the Libsecret
31 // API that we actually use. It gets substituted for the real one by
32 // MockLibsecretLoader, which hooks into the facility normally used to load
33 // the libsecret library at runtime to avoid a static dependency on it.
35 struct MockSecretValue {
36 gchar* password;
37 explicit MockSecretValue(gchar* password) : password(password) {}
38 ~MockSecretValue() { g_free(password); }
41 struct MockSecretItem {
42 MockSecretValue* value;
43 GHashTable* attributes;
45 MockSecretItem(MockSecretValue* value, GHashTable* attributes)
46 : value(value), attributes(attributes) {}
47 ~MockSecretItem() {
48 delete value;
49 g_hash_table_destroy(attributes);
52 void RemoveAttribute(const char* keyname) {
53 g_hash_table_remove(attributes, keyname);
57 bool Matches(MockSecretItem* item, GHashTable* query) {
58 GHashTable* attributes = item->attributes;
59 GHashTableIter iter;
60 gchar* name;
61 gchar* query_value;
62 g_hash_table_iter_init(&iter, query);
64 while (g_hash_table_iter_next(&iter, reinterpret_cast<gpointer*>(&name),
65 reinterpret_cast<gpointer*>(&query_value))) {
66 gchar* value = static_cast<gchar*>(g_hash_table_lookup(attributes, name));
67 if (value == nullptr || strcmp(value, query_value) != 0)
68 return false;
70 return true;
73 bool IsStringAttribute(const SecretSchema* schema, const std::string& name) {
74 for (size_t i = 0; schema->attributes[i].name; ++i)
75 if (name == schema->attributes[i].name)
76 return schema->attributes[i].type == SECRET_SCHEMA_ATTRIBUTE_STRING;
77 NOTREACHED() << "Requested type of nonexistent attribute";
78 return false;
81 // The list of all libsecret items we have stored.
82 std::vector<MockSecretItem*> global_mock_libsecret_items;
83 bool global_mock_libsecret_reject_local_ids = false;
85 void ClearMockObjects() {
86 for (size_t i = 0; i < global_mock_libsecret_items.size(); ++i)
87 delete global_mock_libsecret_items[i];
88 global_mock_libsecret_items.clear();
91 gboolean mock_secret_password_store_sync(const SecretSchema* schema,
92 const gchar* collection,
93 const gchar* label,
94 const gchar* password,
95 GCancellable* cancellable,
96 GError** error,
97 ...) {
98 GHashTable* attributes =
99 g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
100 va_list ap;
101 va_start(ap, error);
102 char* name;
103 while ((name = va_arg(ap, gchar*))) {
104 char* value;
105 if (IsStringAttribute(schema, name)) {
106 value = g_strdup(va_arg(ap, gchar*));
107 VLOG(1) << "Adding item attribute " << name << ", value '" << value
108 << "'";
109 } else {
110 uint32_t intvalue = va_arg(ap, uint32_t);
111 VLOG(1) << "Adding item attribute " << name << ", value " << intvalue;
112 value = g_strdup_printf("%u", intvalue);
114 g_hash_table_insert(attributes, g_strdup(name), value);
116 va_end(ap);
117 MockSecretValue* secret_value = new MockSecretValue(g_strdup(password));
118 MockSecretItem* item = new MockSecretItem(secret_value, attributes);
119 global_mock_libsecret_items.push_back(item);
120 return true;
123 GList* mock_secret_service_search_sync(SecretService* service,
124 const SecretSchema* schema,
125 GHashTable* attributes,
126 SecretSearchFlags flags,
127 GCancellable* cancellable,
128 GError** error) {
129 GList* result = nullptr;
130 for (uint32_t i = 0; i < global_mock_libsecret_items.size(); ++i) {
131 if (Matches(global_mock_libsecret_items[i], attributes))
132 result = g_list_append(result, global_mock_libsecret_items[i]);
134 return result;
137 gboolean mock_secret_password_clear_sync(const SecretSchema* schema,
138 GCancellable* cancellable,
139 GError** error,
140 ...) {
141 GHashTable* attributes =
142 g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
143 va_list ap;
144 va_start(ap, error);
145 char* name;
146 while ((name = va_arg(ap, gchar*))) {
147 char* value;
148 if (IsStringAttribute(schema, name)) {
149 value = g_strdup(va_arg(ap, gchar*));
150 VLOG(1) << "Adding item attribute " << name << ", value '" << value
151 << "'";
152 } else {
153 uint32_t intvalue = va_arg(ap, uint32_t);
154 VLOG(1) << "Adding item attribute " << name << ", value " << intvalue;
155 value = g_strdup_printf("%u", intvalue);
157 g_hash_table_insert(attributes, g_strdup(name), value);
159 va_end(ap);
160 for (uint32_t i = 0; i < global_mock_libsecret_items.size();)
161 if (Matches(global_mock_libsecret_items[i], attributes)) {
162 delete global_mock_libsecret_items[i];
163 global_mock_libsecret_items.erase(global_mock_libsecret_items.begin() +
165 } else {
166 ++i;
168 g_hash_table_unref(attributes);
169 return true;
172 MockSecretValue* mock_secret_item_get_secret(MockSecretItem* self) {
173 return self->value;
176 const gchar* mock_secret_value_get_text(MockSecretValue* value) {
177 return value->password;
180 GHashTable* mock_secret_item_get_attributes(MockSecretItem* self) {
181 // Libsecret backend will make unreference of received attributes, so in
182 // order to save them we need to increase their reference number.
183 g_hash_table_ref(self->attributes);
184 return self->attributes;
187 gboolean mock_secret_item_load_secret_sync(MockSecretItem* self,
188 GCancellable* cancellable,
189 GError** error) {
190 return true;
193 void mock_secret_value_unref(gpointer value) {
196 // Inherit to get access to protected fields.
197 class MockLibsecretLoader : public LibsecretLoader {
198 public:
199 static bool LoadMockLibsecret() {
200 secret_password_store_sync = &mock_secret_password_store_sync;
201 secret_service_search_sync = &mock_secret_service_search_sync;
202 secret_password_clear_sync = &mock_secret_password_clear_sync;
203 secret_item_get_secret =
204 (decltype(&::secret_item_get_secret)) & mock_secret_item_get_secret;
205 secret_value_get_text =
206 (decltype(&::secret_value_get_text)) & mock_secret_value_get_text;
207 secret_item_get_attributes = (decltype(&::secret_item_get_attributes)) &
208 mock_secret_item_get_attributes;
209 secret_item_load_secret_sync = (decltype(&::secret_item_load_secret_sync)) &
210 mock_secret_item_load_secret_sync;
211 secret_value_unref =
212 (decltype(&::secret_value_unref)) & mock_secret_value_unref;
213 libsecret_loaded = true;
214 // Reset the state of the mock library.
215 ClearMockObjects();
216 global_mock_libsecret_reject_local_ids = false;
217 return true;
221 void CheckPasswordChanges(const PasswordStoreChangeList& expected_list,
222 const PasswordStoreChangeList& actual_list) {
223 ASSERT_EQ(expected_list.size(), actual_list.size());
224 for (size_t i = 0; i < expected_list.size(); ++i) {
225 EXPECT_EQ(expected_list[i].type(), actual_list[i].type());
226 const PasswordForm& expected = expected_list[i].form();
227 const PasswordForm& actual = actual_list[i].form();
229 EXPECT_EQ(expected.origin, actual.origin);
230 EXPECT_EQ(expected.password_value, actual.password_value);
231 EXPECT_EQ(expected.action, actual.action);
232 EXPECT_EQ(expected.username_element, actual.username_element);
233 EXPECT_EQ(expected.username_value, actual.username_value);
234 EXPECT_EQ(expected.password_element, actual.password_element);
235 EXPECT_EQ(expected.submit_element, actual.submit_element);
236 EXPECT_EQ(expected.signon_realm, actual.signon_realm);
237 EXPECT_EQ(expected.ssl_valid, actual.ssl_valid);
238 EXPECT_EQ(expected.preferred, actual.preferred);
239 EXPECT_EQ(expected.date_created, actual.date_created);
240 EXPECT_EQ(expected.blacklisted_by_user, actual.blacklisted_by_user);
241 EXPECT_EQ(expected.type, actual.type);
242 EXPECT_EQ(expected.times_used, actual.times_used);
243 EXPECT_EQ(expected.scheme, actual.scheme);
244 EXPECT_EQ(expected.date_synced, actual.date_synced);
245 EXPECT_EQ(expected.display_name, actual.display_name);
246 EXPECT_EQ(expected.avatar_url, actual.avatar_url);
247 EXPECT_EQ(expected.federation_url, actual.federation_url);
248 EXPECT_EQ(expected.skip_zero_click, actual.skip_zero_click);
249 EXPECT_EQ(expected.generation_upload_status,
250 actual.generation_upload_status);
254 void CheckPasswordChangesWithResult(const PasswordStoreChangeList* expected,
255 const PasswordStoreChangeList* actual,
256 bool result) {
257 EXPECT_TRUE(result);
258 CheckPasswordChanges(*expected, *actual);
261 } // anonymous namespace
263 class NativeBackendLibsecretTest : public testing::Test {
264 protected:
265 enum UpdateType { // Used in CheckPSLUpdate().
266 UPDATE_BY_UPDATELOGIN,
267 UPDATE_BY_ADDLOGIN,
269 enum RemoveBetweenMethod { // Used in CheckRemoveLoginsBetween().
270 CREATED,
271 SYNCED,
274 NativeBackendLibsecretTest() {}
276 void SetUp() override {
277 ASSERT_TRUE(MockLibsecretLoader::LoadMockLibsecret());
279 form_google_.origin = GURL("http://www.google.com/");
280 form_google_.action = GURL("http://www.google.com/login");
281 form_google_.username_element = UTF8ToUTF16("user");
282 form_google_.username_value = UTF8ToUTF16("joeschmoe");
283 form_google_.password_element = UTF8ToUTF16("pass");
284 form_google_.password_value = UTF8ToUTF16("seekrit");
285 form_google_.submit_element = UTF8ToUTF16("submit");
286 form_google_.signon_realm = "http://www.google.com/";
287 form_google_.type = PasswordForm::TYPE_GENERATED;
288 form_google_.date_created = base::Time::Now();
289 form_google_.date_synced = base::Time::Now();
290 form_google_.display_name = UTF8ToUTF16("Joe Schmoe");
291 form_google_.avatar_url = GURL("http://www.google.com/avatar");
292 form_google_.federation_url = GURL("http://www.google.com/federation_url");
293 form_google_.skip_zero_click = true;
294 form_google_.generation_upload_status = PasswordForm::POSITIVE_SIGNAL_SENT;
296 form_facebook_.origin = GURL("http://www.facebook.com/");
297 form_facebook_.action = GURL("http://www.facebook.com/login");
298 form_facebook_.username_element = UTF8ToUTF16("user");
299 form_facebook_.username_value = UTF8ToUTF16("a");
300 form_facebook_.password_element = UTF8ToUTF16("password");
301 form_facebook_.password_value = UTF8ToUTF16("b");
302 form_facebook_.submit_element = UTF8ToUTF16("submit");
303 form_facebook_.signon_realm = "http://www.facebook.com/";
304 form_facebook_.date_created = base::Time::Now();
305 form_facebook_.date_synced = base::Time::Now();
306 form_facebook_.display_name = UTF8ToUTF16("Joe Schmoe");
307 form_facebook_.avatar_url = GURL("http://www.facebook.com/avatar");
308 form_facebook_.federation_url = GURL("http://www.facebook.com/federation");
309 form_facebook_.skip_zero_click = true;
310 form_facebook_.generation_upload_status = PasswordForm::NO_SIGNAL_SENT;
312 form_isc_.origin = GURL("http://www.isc.org/");
313 form_isc_.action = GURL("http://www.isc.org/auth");
314 form_isc_.username_element = UTF8ToUTF16("id");
315 form_isc_.username_value = UTF8ToUTF16("janedoe");
316 form_isc_.password_element = UTF8ToUTF16("passwd");
317 form_isc_.password_value = UTF8ToUTF16("ihazabukkit");
318 form_isc_.submit_element = UTF8ToUTF16("login");
319 form_isc_.signon_realm = "http://www.isc.org/";
320 form_isc_.date_created = base::Time::Now();
321 form_isc_.date_synced = base::Time::Now();
323 other_auth_.origin = GURL("http://www.example.com/");
324 other_auth_.username_value = UTF8ToUTF16("username");
325 other_auth_.password_value = UTF8ToUTF16("pass");
326 other_auth_.signon_realm = "http://www.example.com/Realm";
327 other_auth_.date_created = base::Time::Now();
328 other_auth_.date_synced = base::Time::Now();
331 void TearDown() override {
332 base::MessageLoop::current()->PostTask(FROM_HERE,
333 base::MessageLoop::QuitClosure());
334 base::MessageLoop::current()->Run();
335 ClearMockObjects();
338 void RunUIThread() { base::MessageLoop::current()->Run(); }
340 void CheckUint32Attribute(const MockSecretItem* item,
341 const std::string& attribute,
342 uint32_t value) {
343 gpointer item_value =
344 g_hash_table_lookup(item->attributes, attribute.c_str());
345 EXPECT_TRUE(item_value) << " in attribute " << attribute;
346 if (item_value) {
347 uint32_t int_value;
348 bool conversion_ok = base::StringToUint((char*)item_value, &int_value);
349 EXPECT_TRUE(conversion_ok);
350 EXPECT_EQ(value, int_value);
354 void CheckStringAttribute(const MockSecretItem* item,
355 const std::string& attribute,
356 const std::string& value) {
357 gpointer item_value =
358 g_hash_table_lookup(item->attributes, attribute.c_str());
359 EXPECT_TRUE(item_value) << " in attribute " << attribute;
360 if (item_value) {
361 EXPECT_EQ(value, static_cast<char*>(item_value));
365 void CheckMockSecretItem(const MockSecretItem* item,
366 const PasswordForm& form,
367 const std::string& app_string) {
368 EXPECT_EQ(UTF16ToUTF8(form.password_value), item->value->password);
369 EXPECT_EQ(21u, g_hash_table_size(item->attributes));
370 CheckStringAttribute(item, "origin_url", form.origin.spec());
371 CheckStringAttribute(item, "action_url", form.action.spec());
372 CheckStringAttribute(item, "username_element",
373 UTF16ToUTF8(form.username_element));
374 CheckStringAttribute(item, "username_value",
375 UTF16ToUTF8(form.username_value));
376 CheckStringAttribute(item, "password_element",
377 UTF16ToUTF8(form.password_element));
378 CheckStringAttribute(item, "submit_element",
379 UTF16ToUTF8(form.submit_element));
380 CheckStringAttribute(item, "signon_realm", form.signon_realm);
381 CheckUint32Attribute(item, "ssl_valid", form.ssl_valid);
382 CheckUint32Attribute(item, "preferred", form.preferred);
383 // We don't check the date created. It varies.
384 CheckUint32Attribute(item, "blacklisted_by_user", form.blacklisted_by_user);
385 CheckUint32Attribute(item, "type", form.type);
386 CheckUint32Attribute(item, "times_used", form.times_used);
387 CheckUint32Attribute(item, "scheme", form.scheme);
388 CheckStringAttribute(
389 item, "date_synced",
390 base::Int64ToString(form.date_synced.ToInternalValue()));
391 CheckStringAttribute(item, "display_name", UTF16ToUTF8(form.display_name));
392 CheckStringAttribute(item, "avatar_url", form.avatar_url.spec());
393 CheckStringAttribute(item, "federation_url", form.federation_url.spec());
394 CheckUint32Attribute(item, "skip_zero_click", form.skip_zero_click);
395 CheckUint32Attribute(item, "generation_upload_status",
396 form.generation_upload_status);
397 CheckStringAttribute(item, "application", app_string);
400 // Saves |credentials| and then gets logins matching |url| and |scheme|.
401 // Returns true when something is found, and in such case copies the result to
402 // |result| when |result| is not nullptr. (Note that there can be max. 1
403 // result derived from |credentials|.)
404 bool CheckCredentialAvailability(const PasswordForm& credentials,
405 const GURL& url,
406 const PasswordForm::Scheme& scheme,
407 PasswordForm* result) {
408 NativeBackendLibsecret backend(321);
410 backend.AddLogin(credentials);
412 PasswordForm target_form;
413 target_form.origin = url;
414 target_form.signon_realm = url.spec();
415 if (scheme != PasswordForm::SCHEME_HTML) {
416 // For non-HTML forms, the realm used for authentication
417 // (http://tools.ietf.org/html/rfc1945#section-10.2) is appended to the
418 // signon_realm. Just use a default value for now.
419 target_form.signon_realm.append("Realm");
420 target_form.scheme = scheme;
422 ScopedVector<autofill::PasswordForm> form_list;
423 backend.GetLogins(target_form, &form_list);
425 EXPECT_EQ(1u, global_mock_libsecret_items.size());
426 if (global_mock_libsecret_items.size() > 0)
427 CheckMockSecretItem(global_mock_libsecret_items[0], credentials,
428 "chrome-321");
429 ClearMockObjects();
431 if (form_list.empty())
432 return false;
433 EXPECT_EQ(1u, form_list.size());
434 if (result)
435 *result = *form_list[0];
436 return true;
439 // Test that updating does not use PSL matching: Add a www.facebook.com
440 // password, then use PSL matching to get a copy of it for m.facebook.com, and
441 // add that copy as well. Now update the www.facebook.com password -- the
442 // m.facebook.com password should not get updated. Depending on the argument,
443 // the credential update is done via UpdateLogin or AddLogin.
444 void CheckPSLUpdate(UpdateType update_type) {
445 NativeBackendLibsecret backend(321);
447 backend.AddLogin(form_facebook_);
449 // Get the PSL-matched copy of the saved login for m.facebook.
450 const GURL kMobileURL("http://m.facebook.com/");
451 PasswordForm m_facebook_lookup;
452 m_facebook_lookup.origin = kMobileURL;
453 m_facebook_lookup.signon_realm = kMobileURL.spec();
454 ScopedVector<autofill::PasswordForm> form_list;
455 backend.GetLogins(m_facebook_lookup, &form_list);
457 EXPECT_EQ(1u, global_mock_libsecret_items.size());
458 EXPECT_EQ(1u, form_list.size());
459 PasswordForm m_facebook = *form_list[0];
460 form_list.clear();
461 EXPECT_EQ(kMobileURL, m_facebook.origin);
462 EXPECT_EQ(kMobileURL.spec(), m_facebook.signon_realm);
464 // Add the PSL-matched copy to saved logins.
465 backend.AddLogin(m_facebook);
466 EXPECT_EQ(2u, global_mock_libsecret_items.size());
468 // Update www.facebook.com login.
469 PasswordForm new_facebook(form_facebook_);
470 const base::string16 kOldPassword(form_facebook_.password_value);
471 const base::string16 kNewPassword(UTF8ToUTF16("new_b"));
472 EXPECT_NE(kOldPassword, kNewPassword);
473 new_facebook.password_value = kNewPassword;
474 scoped_ptr<PasswordStoreChangeList> not_used(new PasswordStoreChangeList());
475 switch (update_type) {
476 case UPDATE_BY_UPDATELOGIN:
477 backend.UpdateLogin(new_facebook, not_used.get());
478 break;
479 case UPDATE_BY_ADDLOGIN:
480 backend.AddLogin(new_facebook);
481 break;
484 EXPECT_EQ(2u, global_mock_libsecret_items.size());
486 // Check that m.facebook.com login was not modified by the update.
487 backend.GetLogins(m_facebook_lookup, &form_list);
489 // There should be two results -- the exact one, and the PSL-matched one.
490 EXPECT_EQ(2u, form_list.size());
491 size_t index_non_psl = 0;
492 if (!form_list[index_non_psl]->original_signon_realm.empty())
493 index_non_psl = 1;
494 EXPECT_EQ(kMobileURL, form_list[index_non_psl]->origin);
495 EXPECT_EQ(kMobileURL.spec(), form_list[index_non_psl]->signon_realm);
496 EXPECT_EQ(kOldPassword, form_list[index_non_psl]->password_value);
497 form_list.clear();
499 // Check that www.facebook.com login was modified by the update.
500 backend.GetLogins(form_facebook_, &form_list);
501 // There should be two results -- the exact one, and the PSL-matched one.
502 EXPECT_EQ(2u, form_list.size());
503 index_non_psl = 0;
504 if (!form_list[index_non_psl]->original_signon_realm.empty())
505 index_non_psl = 1;
506 EXPECT_EQ(form_facebook_.origin, form_list[index_non_psl]->origin);
507 EXPECT_EQ(form_facebook_.signon_realm,
508 form_list[index_non_psl]->signon_realm);
509 EXPECT_EQ(kNewPassword, form_list[index_non_psl]->password_value);
510 form_list.clear();
513 // Checks various types of matching for forms with a non-HTML |scheme|.
514 void CheckMatchingWithScheme(const PasswordForm::Scheme& scheme) {
515 ASSERT_NE(PasswordForm::SCHEME_HTML, scheme);
516 other_auth_.scheme = scheme;
518 // Don't match a non-HTML form with an HTML form.
519 EXPECT_FALSE(
520 CheckCredentialAvailability(other_auth_, GURL("http://www.example.com"),
521 PasswordForm::SCHEME_HTML, nullptr));
522 // Don't match an HTML form with non-HTML auth form.
523 EXPECT_FALSE(CheckCredentialAvailability(
524 form_google_, GURL("http://www.google.com/"), scheme, nullptr));
525 // Don't match two different non-HTML auth forms with different origin.
526 EXPECT_FALSE(CheckCredentialAvailability(
527 other_auth_, GURL("http://first.example.com"), scheme, nullptr));
528 // Do match non-HTML forms from the same origin.
529 EXPECT_TRUE(CheckCredentialAvailability(
530 other_auth_, GURL("http://www.example.com/"), scheme, nullptr));
533 void CheckRemoveLoginsBetween(RemoveBetweenMethod date_to_test) {
534 NativeBackendLibsecret backend(42);
536 base::Time now = base::Time::Now();
537 base::Time next_day = now + base::TimeDelta::FromDays(1);
538 form_google_.date_synced = base::Time();
539 form_isc_.date_synced = base::Time();
540 form_google_.date_created = now;
541 form_isc_.date_created = now;
542 if (date_to_test == CREATED) {
543 form_google_.date_created = now;
544 form_isc_.date_created = next_day;
545 } else {
546 form_google_.date_synced = now;
547 form_isc_.date_synced = next_day;
550 backend.AddLogin(form_google_);
551 backend.AddLogin(form_isc_);
553 PasswordStoreChangeList expected_changes;
554 expected_changes.push_back(
555 PasswordStoreChange(PasswordStoreChange::REMOVE, form_google_));
556 PasswordStoreChangeList changes;
557 bool (NativeBackendLibsecret::*method)(
558 base::Time, base::Time, password_manager::PasswordStoreChangeList*) =
559 date_to_test == CREATED
560 ? &NativeBackendLibsecret::RemoveLoginsCreatedBetween
561 : &NativeBackendLibsecret::RemoveLoginsSyncedBetween;
563 bool result = base::Bind(method, base::Unretained(&backend), base::Time(),
564 next_day, &changes).Run();
565 CheckPasswordChangesWithResult(&expected_changes, &changes, result);
567 EXPECT_EQ(1u, global_mock_libsecret_items.size());
568 if (global_mock_libsecret_items.size() > 0)
569 CheckMockSecretItem(global_mock_libsecret_items[0], form_isc_,
570 "chrome-42");
572 // Remove form_isc_.
573 expected_changes.clear();
574 expected_changes.push_back(
575 PasswordStoreChange(PasswordStoreChange::REMOVE, form_isc_));
577 result = base::Bind(method, base::Unretained(&backend), next_day,
578 base::Time(), &changes).Run();
579 CheckPasswordChangesWithResult(&expected_changes, &changes, result);
581 EXPECT_EQ(0u, global_mock_libsecret_items.size());
584 base::MessageLoopForUI message_loop_;
586 // Provide some test forms to avoid having to set them up in each test.
587 PasswordForm form_google_;
588 PasswordForm form_facebook_;
589 PasswordForm form_isc_;
590 PasswordForm other_auth_;
593 TEST_F(NativeBackendLibsecretTest, BasicAddLogin) {
594 NativeBackendLibsecret backend(42);
596 backend.AddLogin(form_google_);
598 EXPECT_EQ(1u, global_mock_libsecret_items.size());
599 if (global_mock_libsecret_items.size() > 0)
600 CheckMockSecretItem(global_mock_libsecret_items[0], form_google_,
601 "chrome-42");
604 TEST_F(NativeBackendLibsecretTest, BasicListLogins) {
605 NativeBackendLibsecret backend(42);
607 backend.AddLogin(form_google_);
609 ScopedVector<autofill::PasswordForm> form_list;
610 backend.GetAutofillableLogins(&form_list);
612 // Quick check that we got something back.
613 EXPECT_EQ(1u, form_list.size());
614 form_list.clear();
616 EXPECT_EQ(1u, global_mock_libsecret_items.size());
617 if (global_mock_libsecret_items.size() > 0)
618 CheckMockSecretItem(global_mock_libsecret_items[0], form_google_,
619 "chrome-42");
622 // Save a password for www.facebook.com and see it suggested for m.facebook.com.
623 TEST_F(NativeBackendLibsecretTest, PSLMatchingPositive) {
624 PasswordForm result;
625 const GURL kMobileURL("http://m.facebook.com/");
626 EXPECT_TRUE(CheckCredentialAvailability(form_facebook_, kMobileURL,
627 PasswordForm::SCHEME_HTML, &result));
628 EXPECT_EQ(kMobileURL, result.origin);
629 EXPECT_EQ(kMobileURL.spec(), result.signon_realm);
632 // Save a password for www.facebook.com and see it not suggested for
633 // m-facebook.com.
634 TEST_F(NativeBackendLibsecretTest, PSLMatchingNegativeDomainMismatch) {
635 EXPECT_FALSE(CheckCredentialAvailability(form_facebook_,
636 GURL("http://m-facebook.com/"),
637 PasswordForm::SCHEME_HTML, nullptr));
640 // Test PSL matching is off for domains excluded from it.
641 TEST_F(NativeBackendLibsecretTest, PSLMatchingDisabledDomains) {
642 EXPECT_FALSE(CheckCredentialAvailability(form_google_,
643 GURL("http://one.google.com/"),
644 PasswordForm::SCHEME_HTML, nullptr));
647 // Make sure PSL matches aren't available for non-HTML forms.
648 TEST_F(NativeBackendLibsecretTest, PSLMatchingDisabledForNonHTMLForms) {
649 CheckMatchingWithScheme(PasswordForm::SCHEME_BASIC);
650 CheckMatchingWithScheme(PasswordForm::SCHEME_DIGEST);
651 CheckMatchingWithScheme(PasswordForm::SCHEME_OTHER);
654 TEST_F(NativeBackendLibsecretTest, PSLUpdatingStrictUpdateLogin) {
655 CheckPSLUpdate(UPDATE_BY_UPDATELOGIN);
658 TEST_F(NativeBackendLibsecretTest, PSLUpdatingStrictAddLogin) {
659 // TODO(vabr): if AddLogin becomes no longer valid for existing logins, then
660 // just delete this test.
661 CheckPSLUpdate(UPDATE_BY_ADDLOGIN);
664 TEST_F(NativeBackendLibsecretTest, BasicUpdateLogin) {
665 NativeBackendLibsecret backend(42);
667 backend.AddLogin(form_google_);
669 PasswordForm new_form_google(form_google_);
670 new_form_google.times_used = 1;
671 new_form_google.action = GURL("http://www.google.com/different/login");
673 EXPECT_EQ(1u, global_mock_libsecret_items.size());
674 if (global_mock_libsecret_items.size() > 0)
675 CheckMockSecretItem(global_mock_libsecret_items[0], form_google_,
676 "chrome-42");
678 // Update login
679 PasswordStoreChangeList changes;
680 backend.UpdateLogin(new_form_google, &changes);
682 ASSERT_EQ(1u, changes.size());
683 EXPECT_EQ(PasswordStoreChange::UPDATE, changes.front().type());
684 EXPECT_EQ(new_form_google, changes.front().form());
685 EXPECT_EQ(1u, global_mock_libsecret_items.size());
686 if (global_mock_libsecret_items.size() > 0)
687 CheckMockSecretItem(global_mock_libsecret_items[0], new_form_google,
688 "chrome-42");
691 TEST_F(NativeBackendLibsecretTest, BasicRemoveLogin) {
692 NativeBackendLibsecret backend(42);
694 backend.AddLogin(form_google_);
696 EXPECT_EQ(1u, global_mock_libsecret_items.size());
697 if (global_mock_libsecret_items.size() > 0)
698 CheckMockSecretItem(global_mock_libsecret_items[0], form_google_,
699 "chrome-42");
701 backend.RemoveLogin(form_google_);
703 EXPECT_EQ(0u, global_mock_libsecret_items.size());
706 // Verify fix for http://crbug.com/408783.
707 TEST_F(NativeBackendLibsecretTest, RemoveLoginActionMismatch) {
708 NativeBackendLibsecret backend(42);
710 backend.AddLogin(form_google_);
712 EXPECT_EQ(1u, global_mock_libsecret_items.size());
713 if (global_mock_libsecret_items.size() > 0)
714 CheckMockSecretItem(global_mock_libsecret_items[0], form_google_,
715 "chrome-42");
717 // Action url match not required for removal.
718 form_google_.action = GURL("https://some.other.url.com/path");
720 backend.RemoveLogin(form_google_);
722 EXPECT_EQ(0u, global_mock_libsecret_items.size());
725 TEST_F(NativeBackendLibsecretTest, RemoveNonexistentLogin) {
726 NativeBackendLibsecret backend(42);
728 // First add an unrelated login.
729 backend.AddLogin(form_google_);
731 EXPECT_EQ(1u, global_mock_libsecret_items.size());
732 if (global_mock_libsecret_items.size() > 0)
733 CheckMockSecretItem(global_mock_libsecret_items[0], form_google_,
734 "chrome-42");
736 // Attempt to remove a login that doesn't exist.
737 backend.RemoveLogin(form_isc_);
739 // Make sure we can still get the first form back.
740 ScopedVector<autofill::PasswordForm> form_list;
741 backend.GetAutofillableLogins(&form_list);
743 // Quick check that we got something back.
744 EXPECT_EQ(1u, form_list.size());
745 form_list.clear();
747 EXPECT_EQ(1u, global_mock_libsecret_items.size());
748 if (global_mock_libsecret_items.size() > 0)
749 CheckMockSecretItem(global_mock_libsecret_items[0], form_google_,
750 "chrome-42");
753 TEST_F(NativeBackendLibsecretTest, UpdateNonexistentLogin) {
754 NativeBackendLibsecret backend(42);
756 // First add an unrelated login.
757 backend.AddLogin(form_google_);
759 EXPECT_EQ(1u, global_mock_libsecret_items.size());
760 if (global_mock_libsecret_items.size() > 0)
761 CheckMockSecretItem(global_mock_libsecret_items[0], form_google_,
762 "chrome-42");
764 // Attempt to update a login that doesn't exist.
765 PasswordStoreChangeList changes;
766 backend.UpdateLogin(form_isc_, &changes);
768 EXPECT_EQ(PasswordStoreChangeList(), changes);
769 EXPECT_EQ(1u, global_mock_libsecret_items.size());
770 if (global_mock_libsecret_items.size() > 0)
771 CheckMockSecretItem(global_mock_libsecret_items[0], form_google_,
772 "chrome-42");
775 TEST_F(NativeBackendLibsecretTest, AddDuplicateLogin) {
776 NativeBackendLibsecret backend(42);
778 PasswordStoreChangeList expected_changes, actual_changes;
779 expected_changes.push_back(
780 PasswordStoreChange(PasswordStoreChange::ADD, form_google_));
781 actual_changes = backend.AddLogin(form_google_);
782 CheckPasswordChanges(expected_changes, actual_changes);
784 expected_changes.clear();
785 expected_changes.push_back(
786 PasswordStoreChange(PasswordStoreChange::REMOVE, form_google_));
787 form_google_.times_used++;
788 expected_changes.push_back(
789 PasswordStoreChange(PasswordStoreChange::ADD, form_google_));
791 actual_changes = backend.AddLogin(form_google_);
792 CheckPasswordChanges(expected_changes, actual_changes);
794 EXPECT_EQ(1u, global_mock_libsecret_items.size());
795 if (global_mock_libsecret_items.size() > 0)
796 CheckMockSecretItem(global_mock_libsecret_items[0], form_google_,
797 "chrome-42");
800 TEST_F(NativeBackendLibsecretTest, ListLoginsAppends) {
801 NativeBackendLibsecret backend(42);
803 backend.AddLogin(form_google_);
805 // Send the same request twice with the same list both times.
806 ScopedVector<autofill::PasswordForm> form_list;
807 backend.GetAutofillableLogins(&form_list);
808 backend.GetAutofillableLogins(&form_list);
810 // Quick check that we got two results back.
811 EXPECT_EQ(2u, form_list.size());
812 form_list.clear();
814 EXPECT_EQ(1u, global_mock_libsecret_items.size());
815 if (global_mock_libsecret_items.size() > 0)
816 CheckMockSecretItem(global_mock_libsecret_items[0], form_google_,
817 "chrome-42");
820 TEST_F(NativeBackendLibsecretTest, AndroidCredentials) {
821 NativeBackendLibsecret backend(42);
822 backend.Init();
824 PasswordForm observed_android_form;
825 observed_android_form.scheme = PasswordForm::SCHEME_HTML;
826 observed_android_form.signon_realm =
827 "android://7x7IDboo8u9YKraUsbmVkuf1-@net.rateflix.app/";
828 PasswordForm saved_android_form = observed_android_form;
829 saved_android_form.username_value = base::UTF8ToUTF16("randomusername");
830 saved_android_form.password_value = base::UTF8ToUTF16("password");
831 saved_android_form.date_created = base::Time::Now();
833 backend.AddLogin(saved_android_form);
835 ScopedVector<autofill::PasswordForm> form_list;
836 backend.GetAutofillableLogins(&form_list);
838 EXPECT_EQ(1u, form_list.size());
839 EXPECT_EQ(saved_android_form, *form_list[0]);
843 TEST_F(NativeBackendLibsecretTest, RemoveLoginsCreatedBetween) {
844 CheckRemoveLoginsBetween(CREATED);
847 TEST_F(NativeBackendLibsecretTest, RemoveLoginsSyncedBetween) {
848 CheckRemoveLoginsBetween(SYNCED);
851 TEST_F(NativeBackendLibsecretTest, SomeKeyringAttributesAreMissing) {
852 // Absent attributes should be filled with default values.
853 NativeBackendLibsecret backend(42);
855 backend.AddLogin(form_google_);
857 EXPECT_EQ(1u, global_mock_libsecret_items.size());
858 // Remove a string attribute.
859 global_mock_libsecret_items[0]->RemoveAttribute("avatar_url");
860 // Remove an integer attribute.
861 global_mock_libsecret_items[0]->RemoveAttribute("ssl_valid");
863 ScopedVector<autofill::PasswordForm> form_list;
864 backend.GetAutofillableLogins(&form_list);
866 EXPECT_EQ(1u, form_list.size());
867 EXPECT_EQ(GURL(""), form_list[0]->avatar_url);
868 EXPECT_FALSE(form_list[0]->ssl_valid);
871 // TODO(mdm): add more basic tests here at some point.