1 // Copyright (c) 2012 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.
10 #include "base/bind.h"
11 #include "base/pickle.h"
12 #include "base/prefs/pref_service.h"
13 #include "base/stl_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/synchronization/waitable_event.h"
16 #include "chrome/browser/password_manager/native_backend_kwallet_x.h"
17 #include "chrome/common/pref_names.h"
18 #include "chrome/test/base/testing_profile.h"
19 #include "content/public/test/test_browser_thread.h"
20 #include "dbus/message.h"
21 #include "dbus/mock_bus.h"
22 #include "dbus/mock_object_proxy.h"
23 #include "dbus/object_path.h"
24 #include "dbus/object_proxy.h"
25 #include "testing/gmock/include/gmock/gmock.h"
26 #include "testing/gtest/include/gtest/gtest.h"
28 using autofill::PasswordForm;
29 using content::BrowserThread;
31 using testing::Invoke;
32 using testing::Return;
36 // This class implements a very simple version of KWallet in memory.
37 // We only provide the parts we actually use; the real version has more.
40 typedef std::basic_string<uint8_t> Blob; // std::string is binary-safe.
42 TestKWallet() : reject_local_folders_(false) {}
44 void set_reject_local_folders(bool value) { reject_local_folders_ = value; }
46 // NOTE: The method names here are the same as the corresponding DBus
47 // methods, and therefore have names that don't match our style guide.
49 // Check for presence of a given password folder.
50 bool hasFolder(const std::string& folder) const {
51 return data_.find(folder) != data_.end();
54 // Check for presence of a given password in a given password folder.
55 bool hasEntry(const std::string& folder, const std::string& key) const {
56 Data::const_iterator it = data_.find(folder);
57 return it != data_.end() && it->second.find(key) != it->second.end();
60 // Get a list of password keys in a given password folder.
61 bool entryList(const std::string& folder,
62 std::vector<std::string>* entries) const {
63 Data::const_iterator it = data_.find(folder);
64 if (it == data_.end()) return false;
65 for (Folder::const_iterator fit = it->second.begin();
66 fit != it->second.end(); ++fit)
67 entries->push_back(fit->first);
71 // Read the password data for a given password in a given password folder.
72 bool readEntry(const std::string& folder, const std::string& key,
74 Data::const_iterator it = data_.find(folder);
75 if (it == data_.end()) return false;
76 Folder::const_iterator fit = it->second.find(key);
77 if (fit == it->second.end()) return false;
82 // Create the given password folder.
83 bool createFolder(const std::string& folder) {
84 if (reject_local_folders_ && folder.find('(') != std::string::npos)
86 return data_.insert(make_pair(folder, Folder())).second;
89 // Remove the given password from the given password folder.
90 bool removeEntry(const std::string& folder, const std::string& key) {
91 Data::iterator it = data_.find(folder);
92 if (it == data_.end()) return false;
93 return it->second.erase(key) > 0;
96 // Write the given password data to the given password folder.
97 bool writeEntry(const std::string& folder, const std::string& key,
99 Data::iterator it = data_.find(folder);
100 if (it == data_.end()) return false;
101 it->second[key] = value;
106 typedef std::map<std::string, Blob> Folder;
107 typedef std::map<std::string, Folder> Data;
110 // "Local" folders are folders containing local profile IDs in their names. We
111 // can reject attempts to create them in order to make it easier to create
112 // legacy shared passwords in these tests, for testing the migration code.
113 bool reject_local_folders_;
115 // No need to disallow copy and assign. This class is safe to copy and assign.
118 } // anonymous namespace
120 // Obscure magic: we need to declare storage for this constant because we use it
121 // in ways that require its address in this test, but not in the actual code.
122 const int NativeBackendKWallet::kInvalidKWalletHandle;
124 // Subclass NativeBackendKWallet to promote some members to public for testing.
125 class NativeBackendKWalletStub : public NativeBackendKWallet {
127 NativeBackendKWalletStub(LocalProfileId id, PrefService* pref_service)
128 : NativeBackendKWallet(id, pref_service) {
130 using NativeBackendKWallet::InitWithBus;
131 using NativeBackendKWallet::kInvalidKWalletHandle;
132 using NativeBackendKWallet::DeserializeValue;
135 // Provide some test forms to avoid having to set them up in each test.
136 class NativeBackendKWalletTestBase : public testing::Test {
138 NativeBackendKWalletTestBase() {
139 form_google_.origin = GURL("http://www.google.com/");
140 form_google_.action = GURL("http://www.google.com/login");
141 form_google_.username_element = UTF8ToUTF16("user");
142 form_google_.username_value = UTF8ToUTF16("joeschmoe");
143 form_google_.password_element = UTF8ToUTF16("pass");
144 form_google_.password_value = UTF8ToUTF16("seekrit");
145 form_google_.submit_element = UTF8ToUTF16("submit");
146 form_google_.signon_realm = "Google";
148 form_isc_.origin = GURL("http://www.isc.org/");
149 form_isc_.action = GURL("http://www.isc.org/auth");
150 form_isc_.username_element = UTF8ToUTF16("id");
151 form_isc_.username_value = UTF8ToUTF16("janedoe");
152 form_isc_.password_element = UTF8ToUTF16("passwd");
153 form_isc_.password_value = UTF8ToUTF16("ihazabukkit");
154 form_isc_.submit_element = UTF8ToUTF16("login");
155 form_isc_.signon_realm = "ISC";
158 void CheckPasswordForm(const PasswordForm& expected,
159 const PasswordForm& actual);
161 PasswordForm form_google_;
162 PasswordForm form_isc_;
165 void NativeBackendKWalletTestBase::CheckPasswordForm(
166 const PasswordForm& expected, const PasswordForm& actual) {
167 EXPECT_EQ(expected.origin, actual.origin);
168 EXPECT_EQ(expected.password_value, actual.password_value);
169 EXPECT_EQ(expected.action, actual.action);
170 EXPECT_EQ(expected.username_element, actual.username_element);
171 EXPECT_EQ(expected.username_value, actual.username_value);
172 EXPECT_EQ(expected.password_element, actual.password_element);
173 EXPECT_EQ(expected.submit_element, actual.submit_element);
174 EXPECT_EQ(expected.signon_realm, actual.signon_realm);
175 EXPECT_EQ(expected.ssl_valid, actual.ssl_valid);
176 EXPECT_EQ(expected.preferred, actual.preferred);
177 // We don't check the date created. It varies.
178 EXPECT_EQ(expected.blacklisted_by_user, actual.blacklisted_by_user);
179 EXPECT_EQ(expected.scheme, actual.scheme);
182 class NativeBackendKWalletTest : public NativeBackendKWalletTestBase {
184 NativeBackendKWalletTest()
185 : ui_thread_(BrowserThread::UI, &message_loop_),
186 db_thread_(BrowserThread::DB), klauncher_ret_(0),
187 klauncher_contacted_(false), kwallet_runnable_(true),
188 kwallet_running_(true), kwallet_enabled_(true) {
191 virtual void SetUp();
192 virtual void TearDown();
194 // Let the DB thread run to completion of all current tasks.
196 base::WaitableEvent event(false, false);
197 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
198 base::Bind(ThreadDone, &event));
200 // Some of the tests may post messages to the UI thread, but we don't need
201 // to run those until after the DB thread is finished. So run it here.
202 message_loop_.RunUntilIdle();
204 static void ThreadDone(base::WaitableEvent* event) {
208 // Utilities to help verify sets of expectations.
210 std::pair<std::string,
211 std::vector<const PasswordForm*> > > ExpectationArray;
212 void CheckPasswordForms(const std::string& folder,
213 const ExpectationArray& sorted_expected);
215 base::MessageLoopForUI message_loop_;
216 content::TestBrowserThread ui_thread_;
217 content::TestBrowserThread db_thread_;
218 TestingProfile profile_;
220 scoped_refptr<dbus::MockBus> mock_session_bus_;
221 scoped_refptr<dbus::MockObjectProxy> mock_klauncher_proxy_;
222 scoped_refptr<dbus::MockObjectProxy> mock_kwallet_proxy_;
225 std::string klauncher_error_;
226 bool klauncher_contacted_;
228 bool kwallet_runnable_;
229 bool kwallet_running_;
230 bool kwallet_enabled_;
235 dbus::Response* KLauncherMethodCall(
236 dbus::MethodCall* method_call, testing::Unused);
238 dbus::Response* KWalletMethodCall(
239 dbus::MethodCall* method_call, testing::Unused);
242 void NativeBackendKWalletTest::SetUp() {
243 ASSERT_TRUE(db_thread_.Start());
245 dbus::Bus::Options options;
246 options.bus_type = dbus::Bus::SESSION;
247 mock_session_bus_ = new dbus::MockBus(options);
249 mock_klauncher_proxy_ =
250 new dbus::MockObjectProxy(mock_session_bus_.get(),
252 dbus::ObjectPath("/KLauncher"));
253 EXPECT_CALL(*mock_klauncher_proxy_.get(), MockCallMethodAndBlock(_, _))
255 Invoke(this, &NativeBackendKWalletTest::KLauncherMethodCall));
257 mock_kwallet_proxy_ =
258 new dbus::MockObjectProxy(mock_session_bus_.get(),
260 dbus::ObjectPath("/modules/kwalletd"));
261 EXPECT_CALL(*mock_kwallet_proxy_.get(), MockCallMethodAndBlock(_, _))
263 Invoke(this, &NativeBackendKWalletTest::KWalletMethodCall));
266 *mock_session_bus_.get(),
267 GetObjectProxy("org.kde.klauncher", dbus::ObjectPath("/KLauncher")))
268 .WillRepeatedly(Return(mock_klauncher_proxy_.get()));
270 *mock_session_bus_.get(),
271 GetObjectProxy("org.kde.kwalletd", dbus::ObjectPath("/modules/kwalletd")))
272 .WillRepeatedly(Return(mock_kwallet_proxy_.get()));
274 EXPECT_CALL(*mock_session_bus_.get(), ShutdownAndBlock()).WillOnce(Return())
275 .WillRepeatedly(Return());
278 void NativeBackendKWalletTest::TearDown() {
279 base::MessageLoop::current()->PostTask(FROM_HERE,
280 base::MessageLoop::QuitClosure());
281 base::MessageLoop::current()->Run();
285 dbus::Response* NativeBackendKWalletTest::KLauncherMethodCall(
286 dbus::MethodCall* method_call, testing::Unused) {
287 EXPECT_EQ("org.kde.KLauncher", method_call->GetInterface());
288 EXPECT_EQ("start_service_by_desktop_name", method_call->GetMember());
290 klauncher_contacted_ = true;
292 dbus::MessageReader reader(method_call);
293 std::string service_name;
294 std::vector<std::string> urls;
295 std::vector<std::string> envs;
296 std::string startup_id;
299 EXPECT_TRUE(reader.PopString(&service_name));
300 EXPECT_TRUE(reader.PopArrayOfStrings(&urls));
301 EXPECT_TRUE(reader.PopArrayOfStrings(&envs));
302 EXPECT_TRUE(reader.PopString(&startup_id));
303 EXPECT_TRUE(reader.PopBool(&blind));
305 EXPECT_EQ("kwalletd", service_name);
306 EXPECT_TRUE(urls.empty());
307 EXPECT_TRUE(envs.empty());
308 EXPECT_TRUE(startup_id.empty());
311 if (kwallet_runnable_)
312 kwallet_running_ = true;
314 scoped_ptr<dbus::Response> response(dbus::Response::CreateEmpty());
315 dbus::MessageWriter writer(response.get());
316 writer.AppendInt32(klauncher_ret_);
317 writer.AppendString(std::string()); // dbus_name
318 writer.AppendString(klauncher_error_);
319 writer.AppendInt32(1234); // pid
320 return response.release();
323 dbus::Response* NativeBackendKWalletTest::KWalletMethodCall(
324 dbus::MethodCall* method_call, testing::Unused) {
325 if (!kwallet_running_)
327 EXPECT_EQ("org.kde.KWallet", method_call->GetInterface());
329 scoped_ptr<dbus::Response> response;
330 if (method_call->GetMember() == "isEnabled") {
331 response = dbus::Response::CreateEmpty();
332 dbus::MessageWriter writer(response.get());
333 writer.AppendBool(kwallet_enabled_);
334 } else if (method_call->GetMember() == "networkWallet") {
335 response = dbus::Response::CreateEmpty();
336 dbus::MessageWriter writer(response.get());
337 writer.AppendString("test_wallet"); // Should match |open| below.
338 } else if (method_call->GetMember() == "open") {
339 dbus::MessageReader reader(method_call);
340 std::string wallet_name;
342 std::string app_name;
343 EXPECT_TRUE(reader.PopString(&wallet_name));
344 EXPECT_TRUE(reader.PopInt64(&wallet_id));
345 EXPECT_TRUE(reader.PopString(&app_name));
346 EXPECT_EQ("test_wallet", wallet_name); // Should match |networkWallet|.
347 response = dbus::Response::CreateEmpty();
348 dbus::MessageWriter writer(response.get());
349 writer.AppendInt32(1); // Can be anything but kInvalidKWalletHandle.
350 } else if (method_call->GetMember() == "hasFolder" ||
351 method_call->GetMember() == "createFolder") {
352 dbus::MessageReader reader(method_call);
353 int handle = NativeBackendKWalletStub::kInvalidKWalletHandle;
354 std::string folder_name;
355 std::string app_name;
356 EXPECT_TRUE(reader.PopInt32(&handle));
357 EXPECT_TRUE(reader.PopString(&folder_name));
358 EXPECT_TRUE(reader.PopString(&app_name));
359 EXPECT_NE(NativeBackendKWalletStub::kInvalidKWalletHandle, handle);
360 response = dbus::Response::CreateEmpty();
361 dbus::MessageWriter writer(response.get());
362 if (method_call->GetMember() == "hasFolder")
363 writer.AppendBool(wallet_.hasFolder(folder_name));
365 writer.AppendBool(wallet_.createFolder(folder_name));
366 } else if (method_call->GetMember() == "hasEntry" ||
367 method_call->GetMember() == "removeEntry") {
368 dbus::MessageReader reader(method_call);
369 int handle = NativeBackendKWalletStub::kInvalidKWalletHandle;
370 std::string folder_name;
372 std::string app_name;
373 EXPECT_TRUE(reader.PopInt32(&handle));
374 EXPECT_TRUE(reader.PopString(&folder_name));
375 EXPECT_TRUE(reader.PopString(&key));
376 EXPECT_TRUE(reader.PopString(&app_name));
377 EXPECT_NE(NativeBackendKWalletStub::kInvalidKWalletHandle, handle);
378 response = dbus::Response::CreateEmpty();
379 dbus::MessageWriter writer(response.get());
380 if (method_call->GetMember() == "hasEntry")
381 writer.AppendBool(wallet_.hasEntry(folder_name, key));
383 writer.AppendInt32(wallet_.removeEntry(folder_name, key) ? 0 : 1);
384 } else if (method_call->GetMember() == "entryList") {
385 dbus::MessageReader reader(method_call);
386 int handle = NativeBackendKWalletStub::kInvalidKWalletHandle;
387 std::string folder_name;
388 std::string app_name;
389 EXPECT_TRUE(reader.PopInt32(&handle));
390 EXPECT_TRUE(reader.PopString(&folder_name));
391 EXPECT_TRUE(reader.PopString(&app_name));
392 EXPECT_NE(NativeBackendKWalletStub::kInvalidKWalletHandle, handle);
393 std::vector<std::string> entries;
394 if (wallet_.entryList(folder_name, &entries)) {
395 response = dbus::Response::CreateEmpty();
396 dbus::MessageWriter writer(response.get());
397 writer.AppendArrayOfStrings(entries);
399 } else if (method_call->GetMember() == "readEntry") {
400 dbus::MessageReader reader(method_call);
401 int handle = NativeBackendKWalletStub::kInvalidKWalletHandle;
402 std::string folder_name;
404 std::string app_name;
405 EXPECT_TRUE(reader.PopInt32(&handle));
406 EXPECT_TRUE(reader.PopString(&folder_name));
407 EXPECT_TRUE(reader.PopString(&key));
408 EXPECT_TRUE(reader.PopString(&app_name));
409 EXPECT_NE(NativeBackendKWalletStub::kInvalidKWalletHandle, handle);
410 TestKWallet::Blob value;
411 if (wallet_.readEntry(folder_name, key, &value)) {
412 response = dbus::Response::CreateEmpty();
413 dbus::MessageWriter writer(response.get());
414 writer.AppendArrayOfBytes(value.data(), value.size());
416 } else if (method_call->GetMember() == "writeEntry") {
417 dbus::MessageReader reader(method_call);
418 int handle = NativeBackendKWalletStub::kInvalidKWalletHandle;
419 std::string folder_name;
421 uint8_t* bytes = NULL;
423 std::string app_name;
424 EXPECT_TRUE(reader.PopInt32(&handle));
425 EXPECT_TRUE(reader.PopString(&folder_name));
426 EXPECT_TRUE(reader.PopString(&key));
427 EXPECT_TRUE(reader.PopArrayOfBytes(&bytes, &length));
428 EXPECT_TRUE(reader.PopString(&app_name));
429 EXPECT_NE(NativeBackendKWalletStub::kInvalidKWalletHandle, handle);
430 response = dbus::Response::CreateEmpty();
431 dbus::MessageWriter writer(response.get());
433 wallet_.writeEntry(folder_name, key,
434 TestKWallet::Blob(bytes, length)) ? 0 : 1);
437 EXPECT_FALSE(response.get() == NULL);
438 return response.release();
441 void NativeBackendKWalletTest::CheckPasswordForms(
442 const std::string& folder, const ExpectationArray& sorted_expected) {
443 EXPECT_TRUE(wallet_.hasFolder(folder));
444 std::vector<std::string> entries;
445 EXPECT_TRUE(wallet_.entryList(folder, &entries));
446 EXPECT_EQ(sorted_expected.size(), entries.size());
447 std::sort(entries.begin(), entries.end());
448 for (size_t i = 0; i < entries.size() && i < sorted_expected.size(); ++i) {
449 EXPECT_EQ(sorted_expected[i].first, entries[i]);
450 TestKWallet::Blob value;
451 EXPECT_TRUE(wallet_.readEntry(folder, entries[i], &value));
452 Pickle pickle(reinterpret_cast<const char*>(value.data()), value.size());
453 std::vector<PasswordForm*> forms;
454 NativeBackendKWalletStub::DeserializeValue(entries[i], pickle, &forms);
455 const std::vector<const PasswordForm*>& expect = sorted_expected[i].second;
456 EXPECT_EQ(expect.size(), forms.size());
457 for (size_t j = 0; j < forms.size() && j < expect.size(); ++j)
458 CheckPasswordForm(*expect[j], *forms[j]);
459 STLDeleteElements(&forms);
463 TEST_F(NativeBackendKWalletTest, NotEnabled) {
464 NativeBackendKWalletStub kwallet(42, profile_.GetPrefs());
465 kwallet_enabled_ = false;
466 EXPECT_FALSE(kwallet.InitWithBus(mock_session_bus_));
467 EXPECT_FALSE(klauncher_contacted_);
470 TEST_F(NativeBackendKWalletTest, NotRunnable) {
471 NativeBackendKWalletStub kwallet(42, profile_.GetPrefs());
472 kwallet_runnable_ = false;
473 kwallet_running_ = false;
474 EXPECT_FALSE(kwallet.InitWithBus(mock_session_bus_));
475 EXPECT_TRUE(klauncher_contacted_);
478 TEST_F(NativeBackendKWalletTest, NotRunningOrEnabled) {
479 NativeBackendKWalletStub kwallet(42, profile_.GetPrefs());
480 kwallet_running_ = false;
481 kwallet_enabled_ = false;
482 EXPECT_FALSE(kwallet.InitWithBus(mock_session_bus_));
483 EXPECT_TRUE(klauncher_contacted_);
486 TEST_F(NativeBackendKWalletTest, NotRunning) {
487 NativeBackendKWalletStub kwallet(42, profile_.GetPrefs());
488 kwallet_running_ = false;
489 EXPECT_TRUE(kwallet.InitWithBus(mock_session_bus_));
490 EXPECT_TRUE(klauncher_contacted_);
493 TEST_F(NativeBackendKWalletTest, BasicStartup) {
494 NativeBackendKWalletStub kwallet(42, profile_.GetPrefs());
495 EXPECT_TRUE(kwallet.InitWithBus(mock_session_bus_));
496 EXPECT_FALSE(klauncher_contacted_);
499 TEST_F(NativeBackendKWalletTest, BasicAddLogin) {
500 // Pretend that the migration has already taken place.
501 profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true);
503 NativeBackendKWalletStub backend(42, profile_.GetPrefs());
504 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
506 BrowserThread::PostTask(
507 BrowserThread::DB, FROM_HERE,
508 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
509 base::Unretained(&backend), form_google_));
513 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data"));
515 std::vector<const PasswordForm*> forms;
516 forms.push_back(&form_google_);
517 ExpectationArray expected;
518 expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
519 CheckPasswordForms("Chrome Form Data (42)", expected);
522 TEST_F(NativeBackendKWalletTest, BasicListLogins) {
523 // Pretend that the migration has already taken place.
524 profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true);
526 NativeBackendKWalletStub backend(42, profile_.GetPrefs());
527 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
529 BrowserThread::PostTask(
530 BrowserThread::DB, FROM_HERE,
531 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
532 base::Unretained(&backend), form_google_));
534 std::vector<PasswordForm*> form_list;
535 BrowserThread::PostTask(
536 BrowserThread::DB, FROM_HERE,
538 base::IgnoreResult(&NativeBackendKWalletStub::GetAutofillableLogins),
539 base::Unretained(&backend), &form_list));
543 // Quick check that we got something back.
544 EXPECT_EQ(1u, form_list.size());
545 STLDeleteElements(&form_list);
547 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data"));
549 std::vector<const PasswordForm*> forms;
550 forms.push_back(&form_google_);
551 ExpectationArray expected;
552 expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
553 CheckPasswordForms("Chrome Form Data (42)", expected);
556 TEST_F(NativeBackendKWalletTest, BasicRemoveLogin) {
557 // Pretend that the migration has already taken place.
558 profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true);
560 NativeBackendKWalletStub backend(42, profile_.GetPrefs());
561 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
563 BrowserThread::PostTask(
564 BrowserThread::DB, FROM_HERE,
565 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
566 base::Unretained(&backend), form_google_));
570 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data"));
572 std::vector<const PasswordForm*> forms;
573 forms.push_back(&form_google_);
574 ExpectationArray expected;
575 expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
576 CheckPasswordForms("Chrome Form Data (42)", expected);
578 BrowserThread::PostTask(
579 BrowserThread::DB, FROM_HERE,
580 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::RemoveLogin),
581 base::Unretained(&backend), form_google_));
586 CheckPasswordForms("Chrome Form Data (42)", expected);
589 TEST_F(NativeBackendKWalletTest, RemoveNonexistentLogin) {
590 // Pretend that the migration has already taken place.
591 profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true);
593 NativeBackendKWalletStub backend(42, profile_.GetPrefs());
594 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
596 // First add an unrelated login.
597 BrowserThread::PostTask(
598 BrowserThread::DB, FROM_HERE,
599 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
600 base::Unretained(&backend), form_google_));
604 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data"));
606 std::vector<const PasswordForm*> forms;
607 forms.push_back(&form_google_);
608 ExpectationArray expected;
609 expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
610 CheckPasswordForms("Chrome Form Data (42)", expected);
612 // Attempt to remove a login that doesn't exist.
613 BrowserThread::PostTask(
614 BrowserThread::DB, FROM_HERE,
615 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::RemoveLogin),
616 base::Unretained(&backend), form_isc_));
618 // Make sure we can still get the first form back.
619 std::vector<PasswordForm*> form_list;
620 BrowserThread::PostTask(
621 BrowserThread::DB, FROM_HERE,
623 base::IgnoreResult(&NativeBackendKWalletStub::GetAutofillableLogins),
624 base::Unretained(&backend), &form_list));
628 // Quick check that we got something back.
629 EXPECT_EQ(1u, form_list.size());
630 STLDeleteElements(&form_list);
632 CheckPasswordForms("Chrome Form Data (42)", expected);
635 TEST_F(NativeBackendKWalletTest, AddDuplicateLogin) {
636 // Pretend that the migration has already taken place.
637 profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true);
639 NativeBackendKWalletStub backend(42, profile_.GetPrefs());
640 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
642 BrowserThread::PostTask(
643 BrowserThread::DB, FROM_HERE,
644 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
645 base::Unretained(&backend), form_google_));
646 BrowserThread::PostTask(
647 BrowserThread::DB, FROM_HERE,
648 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
649 base::Unretained(&backend), form_google_));
653 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data"));
655 std::vector<const PasswordForm*> forms;
656 forms.push_back(&form_google_);
657 ExpectationArray expected;
658 expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
659 CheckPasswordForms("Chrome Form Data (42)", expected);
662 TEST_F(NativeBackendKWalletTest, ListLoginsAppends) {
663 // Pretend that the migration has already taken place.
664 profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true);
666 NativeBackendKWalletStub backend(42, profile_.GetPrefs());
667 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
669 BrowserThread::PostTask(
670 BrowserThread::DB, FROM_HERE,
671 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
672 base::Unretained(&backend), form_google_));
674 // Send the same request twice with the same list both times.
675 std::vector<PasswordForm*> form_list;
676 BrowserThread::PostTask(
677 BrowserThread::DB, FROM_HERE,
679 base::IgnoreResult(&NativeBackendKWalletStub::GetAutofillableLogins),
680 base::Unretained(&backend), &form_list));
681 BrowserThread::PostTask(
682 BrowserThread::DB, FROM_HERE,
684 base::IgnoreResult(&NativeBackendKWalletStub::GetAutofillableLogins),
685 base::Unretained(&backend), &form_list));
689 // Quick check that we got two results back.
690 EXPECT_EQ(2u, form_list.size());
691 STLDeleteElements(&form_list);
693 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data"));
695 std::vector<const PasswordForm*> forms;
696 forms.push_back(&form_google_);
697 ExpectationArray expected;
698 expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
699 CheckPasswordForms("Chrome Form Data (42)", expected);
702 // TODO(mdm): add more basic (i.e. non-migration) tests here at some point.
703 // (For example tests for storing >1 password per realm pickle.)
705 TEST_F(NativeBackendKWalletTest, DISABLED_MigrateOneLogin) {
706 // Reject attempts to migrate so we can populate the store.
707 wallet_.set_reject_local_folders(true);
710 NativeBackendKWalletStub backend(42, profile_.GetPrefs());
711 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
713 BrowserThread::PostTask(
714 BrowserThread::DB, FROM_HERE,
715 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
716 base::Unretained(&backend), form_google_));
718 // Make sure we can get the form back even when migration is failing.
719 std::vector<PasswordForm*> form_list;
720 BrowserThread::PostTask(
721 BrowserThread::DB, FROM_HERE,
724 &NativeBackendKWalletStub::GetAutofillableLogins),
725 base::Unretained(&backend), &form_list));
729 // Quick check that we got something back.
730 EXPECT_EQ(1u, form_list.size());
731 STLDeleteElements(&form_list);
734 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data (42)"));
736 std::vector<const PasswordForm*> forms;
737 forms.push_back(&form_google_);
738 ExpectationArray expected;
739 expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
740 CheckPasswordForms("Chrome Form Data", expected);
742 // Now allow the migration.
743 wallet_.set_reject_local_folders(false);
746 NativeBackendKWalletStub backend(42, profile_.GetPrefs());
747 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
749 // Trigger the migration by looking something up.
750 std::vector<PasswordForm*> form_list;
751 BrowserThread::PostTask(
752 BrowserThread::DB, FROM_HERE,
755 &NativeBackendKWalletStub::GetAutofillableLogins),
756 base::Unretained(&backend), &form_list));
760 // Quick check that we got something back.
761 EXPECT_EQ(1u, form_list.size());
762 STLDeleteElements(&form_list);
765 CheckPasswordForms("Chrome Form Data", expected);
766 CheckPasswordForms("Chrome Form Data (42)", expected);
768 // Check that we have set the persistent preference.
770 profile_.GetPrefs()->GetBoolean(prefs::kPasswordsUseLocalProfileId));
773 TEST_F(NativeBackendKWalletTest, DISABLED_MigrateToMultipleProfiles) {
774 // Reject attempts to migrate so we can populate the store.
775 wallet_.set_reject_local_folders(true);
778 NativeBackendKWalletStub backend(42, profile_.GetPrefs());
779 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
781 BrowserThread::PostTask(
782 BrowserThread::DB, FROM_HERE,
783 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
784 base::Unretained(&backend), form_google_));
789 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data (42)"));
791 std::vector<const PasswordForm*> forms;
792 forms.push_back(&form_google_);
793 ExpectationArray expected;
794 expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
795 CheckPasswordForms("Chrome Form Data", expected);
797 // Now allow the migration.
798 wallet_.set_reject_local_folders(false);
801 NativeBackendKWalletStub backend(42, profile_.GetPrefs());
802 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
804 // Trigger the migration by looking something up.
805 std::vector<PasswordForm*> form_list;
806 BrowserThread::PostTask(
807 BrowserThread::DB, FROM_HERE,
810 &NativeBackendKWalletStub::GetAutofillableLogins),
811 base::Unretained(&backend), &form_list));
815 // Quick check that we got something back.
816 EXPECT_EQ(1u, form_list.size());
817 STLDeleteElements(&form_list);
820 CheckPasswordForms("Chrome Form Data", expected);
821 CheckPasswordForms("Chrome Form Data (42)", expected);
823 // Check that we have set the persistent preference.
825 profile_.GetPrefs()->GetBoolean(prefs::kPasswordsUseLocalProfileId));
827 // Normally we'd actually have a different profile. But in the test just reset
828 // the profile's persistent pref; we pass in the local profile id anyway.
829 profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, false);
832 NativeBackendKWalletStub backend(24, profile_.GetPrefs());
833 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
835 // Trigger the migration by looking something up.
836 std::vector<PasswordForm*> form_list;
837 BrowserThread::PostTask(
838 BrowserThread::DB, FROM_HERE,
841 &NativeBackendKWalletStub::GetAutofillableLogins),
842 base::Unretained(&backend), &form_list));
846 // Quick check that we got something back.
847 EXPECT_EQ(1u, form_list.size());
848 STLDeleteElements(&form_list);
851 CheckPasswordForms("Chrome Form Data", expected);
852 CheckPasswordForms("Chrome Form Data (42)", expected);
853 CheckPasswordForms("Chrome Form Data (24)", expected);
856 TEST_F(NativeBackendKWalletTest, DISABLED_NoMigrationWithPrefSet) {
857 // Reject attempts to migrate so we can populate the store.
858 wallet_.set_reject_local_folders(true);
861 NativeBackendKWalletStub backend(42, profile_.GetPrefs());
862 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
864 BrowserThread::PostTask(
865 BrowserThread::DB, FROM_HERE,
866 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
867 base::Unretained(&backend), form_google_));
872 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data (42)"));
874 std::vector<const PasswordForm*> forms;
875 forms.push_back(&form_google_);
876 ExpectationArray expected;
877 expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
878 CheckPasswordForms("Chrome Form Data", expected);
880 // Now allow migration, but also pretend that the it has already taken place.
881 wallet_.set_reject_local_folders(false);
882 profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true);
885 NativeBackendKWalletStub backend(42, profile_.GetPrefs());
886 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
888 // Trigger the migration by adding a new login.
889 BrowserThread::PostTask(
890 BrowserThread::DB, FROM_HERE,
891 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
892 base::Unretained(&backend), form_isc_));
894 // Look up all logins; we expect only the one we added.
895 std::vector<PasswordForm*> form_list;
896 BrowserThread::PostTask(
897 BrowserThread::DB, FROM_HERE,
900 &NativeBackendKWalletStub::GetAutofillableLogins),
901 base::Unretained(&backend), &form_list));
905 // Quick check that we got the right thing back.
906 EXPECT_EQ(1u, form_list.size());
907 if (form_list.size() > 0)
908 EXPECT_EQ(form_isc_.signon_realm, form_list[0]->signon_realm);
909 STLDeleteElements(&form_list);
912 CheckPasswordForms("Chrome Form Data", expected);
914 forms[0] = &form_isc_;
916 expected.push_back(make_pair(std::string(form_isc_.signon_realm), forms));
917 CheckPasswordForms("Chrome Form Data (42)", expected);
920 TEST_F(NativeBackendKWalletTest, DISABLED_DeleteMigratedPasswordIsIsolated) {
921 // Reject attempts to migrate so we can populate the store.
922 wallet_.set_reject_local_folders(true);
925 NativeBackendKWalletStub backend(42, profile_.GetPrefs());
926 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
928 BrowserThread::PostTask(
929 BrowserThread::DB, FROM_HERE,
931 base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
932 base::Unretained(&backend), form_google_));
937 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data (42)"));
939 std::vector<const PasswordForm*> forms;
940 forms.push_back(&form_google_);
941 ExpectationArray expected;
942 expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
943 CheckPasswordForms("Chrome Form Data", expected);
945 // Now allow the migration.
946 wallet_.set_reject_local_folders(false);
949 NativeBackendKWalletStub backend(42, profile_.GetPrefs());
950 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
952 // Trigger the migration by looking something up.
953 std::vector<PasswordForm*> form_list;
954 BrowserThread::PostTask(
955 BrowserThread::DB, FROM_HERE,
958 &NativeBackendKWalletStub::GetAutofillableLogins),
959 base::Unretained(&backend), &form_list));
963 // Quick check that we got something back.
964 EXPECT_EQ(1u, form_list.size());
965 STLDeleteElements(&form_list);
968 CheckPasswordForms("Chrome Form Data", expected);
969 CheckPasswordForms("Chrome Form Data (42)", expected);
971 // Check that we have set the persistent preference.
973 profile_.GetPrefs()->GetBoolean(prefs::kPasswordsUseLocalProfileId));
975 // Normally we'd actually have a different profile. But in the test just reset
976 // the profile's persistent pref; we pass in the local profile id anyway.
977 profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, false);
980 NativeBackendKWalletStub backend(24, profile_.GetPrefs());
981 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
983 // Trigger the migration by looking something up.
984 std::vector<PasswordForm*> form_list;
985 BrowserThread::PostTask(
986 BrowserThread::DB, FROM_HERE,
989 &NativeBackendKWalletStub::GetAutofillableLogins),
990 base::Unretained(&backend), &form_list));
994 // Quick check that we got something back.
995 EXPECT_EQ(1u, form_list.size());
996 STLDeleteElements(&form_list);
998 // There should be three passwords now.
999 CheckPasswordForms("Chrome Form Data", expected);
1000 CheckPasswordForms("Chrome Form Data (42)", expected);
1001 CheckPasswordForms("Chrome Form Data (24)", expected);
1003 // Now delete the password from this second profile.
1004 BrowserThread::PostTask(
1005 BrowserThread::DB, FROM_HERE,
1007 base::IgnoreResult(&NativeBackendKWalletStub::RemoveLogin),
1008 base::Unretained(&backend), form_google_));
1012 // The other two copies of the password in different profiles should remain.
1013 CheckPasswordForms("Chrome Form Data", expected);
1014 CheckPasswordForms("Chrome Form Data (42)", expected);
1016 CheckPasswordForms("Chrome Form Data (24)", expected);
1020 class NativeBackendKWalletPickleTest : public NativeBackendKWalletTestBase {
1022 void CreateVersion0Pickle(bool size_32,
1023 const PasswordForm& form,
1025 void CheckVersion0Pickle(bool size_32, PasswordForm::Scheme scheme);
1028 void NativeBackendKWalletPickleTest::CreateVersion0Pickle(
1029 bool size_32, const PasswordForm& form, Pickle* pickle) {
1030 const int kPickleVersion0 = 0;
1031 pickle->WriteInt(kPickleVersion0);
1033 pickle->WriteUInt32(1); // Size of form list. 32 bits.
1035 pickle->WriteUInt64(1); // Size of form list. 64 bits.
1036 pickle->WriteInt(form.scheme);
1037 pickle->WriteString(form.origin.spec());
1038 pickle->WriteString(form.action.spec());
1039 pickle->WriteString16(form.username_element);
1040 pickle->WriteString16(form.username_value);
1041 pickle->WriteString16(form.password_element);
1042 pickle->WriteString16(form.password_value);
1043 pickle->WriteString16(form.submit_element);
1044 pickle->WriteBool(form.ssl_valid);
1045 pickle->WriteBool(form.preferred);
1046 pickle->WriteBool(form.blacklisted_by_user);
1047 pickle->WriteInt64(form.date_created.ToTimeT());
1050 void NativeBackendKWalletPickleTest::CheckVersion0Pickle(
1051 bool size_32, PasswordForm::Scheme scheme) {
1053 PasswordForm form = form_google_;
1054 form.scheme = scheme;
1055 CreateVersion0Pickle(size_32, form, &pickle);
1056 std::vector<PasswordForm*> form_list;
1057 NativeBackendKWalletStub::DeserializeValue(form.signon_realm,
1058 pickle, &form_list);
1059 EXPECT_EQ(1u, form_list.size());
1060 if (form_list.size() > 0)
1061 CheckPasswordForm(form, *form_list[0]);
1062 STLDeleteElements(&form_list);
1065 // We try both SCHEME_HTML and SCHEME_BASIC since the scheme is stored right
1066 // after the size in the pickle, so it's what gets read as part of the count
1067 // when reading 32-bit pickles on 64-bit systems. SCHEME_HTML is 0 (so we'll
1068 // detect errors later) while SCHEME_BASIC is 1 (so we'll detect it then). We
1069 // try both 32-bit and 64-bit pickles since only one will be the "other" size
1070 // for whatever architecture we're running on, but we want to make sure we can
1071 // read all combinations in any event.
1073 TEST_F(NativeBackendKWalletPickleTest, ReadsOld32BitHTMLPickles) {
1074 CheckVersion0Pickle(true, PasswordForm::SCHEME_HTML);
1077 TEST_F(NativeBackendKWalletPickleTest, ReadsOld32BitHTTPPickles) {
1078 CheckVersion0Pickle(true, PasswordForm::SCHEME_BASIC);
1081 TEST_F(NativeBackendKWalletPickleTest, ReadsOld64BitHTMLPickles) {
1082 CheckVersion0Pickle(false, PasswordForm::SCHEME_HTML);
1085 TEST_F(NativeBackendKWalletPickleTest, ReadsOld64BitHTTPPickles) {
1086 CheckVersion0Pickle(false, PasswordForm::SCHEME_BASIC);