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 "components/autofill/core/common/password_form.h"
20 #include "content/public/test/test_browser_thread.h"
21 #include "dbus/message.h"
22 #include "dbus/mock_bus.h"
23 #include "dbus/mock_object_proxy.h"
24 #include "dbus/object_path.h"
25 #include "dbus/object_proxy.h"
26 #include "testing/gmock/include/gmock/gmock.h"
27 #include "testing/gtest/include/gtest/gtest.h"
29 using autofill::PasswordForm
;
30 using base::UTF8ToUTF16
;
31 using content::BrowserThread
;
33 using testing::Invoke
;
34 using testing::Return
;
38 // This class implements a very simple version of KWallet in memory.
39 // We only provide the parts we actually use; the real version has more.
42 typedef std::basic_string
<uint8_t> Blob
; // std::string is binary-safe.
44 TestKWallet() : reject_local_folders_(false) {}
46 void set_reject_local_folders(bool value
) { reject_local_folders_
= value
; }
48 // NOTE: The method names here are the same as the corresponding DBus
49 // methods, and therefore have names that don't match our style guide.
51 // Check for presence of a given password folder.
52 bool hasFolder(const std::string
& folder
) const {
53 return data_
.find(folder
) != data_
.end();
56 // Check for presence of a given password in a given password folder.
57 bool hasEntry(const std::string
& folder
, const std::string
& key
) const {
58 Data::const_iterator it
= data_
.find(folder
);
59 return it
!= data_
.end() && it
->second
.find(key
) != it
->second
.end();
62 // Get a list of password keys in a given password folder.
63 bool entryList(const std::string
& folder
,
64 std::vector
<std::string
>* entries
) const {
65 Data::const_iterator it
= data_
.find(folder
);
66 if (it
== data_
.end()) return false;
67 for (Folder::const_iterator fit
= it
->second
.begin();
68 fit
!= it
->second
.end(); ++fit
)
69 entries
->push_back(fit
->first
);
73 // Read the password data for a given password in a given password folder.
74 bool readEntry(const std::string
& folder
, const std::string
& key
,
76 Data::const_iterator it
= data_
.find(folder
);
77 if (it
== data_
.end()) return false;
78 Folder::const_iterator fit
= it
->second
.find(key
);
79 if (fit
== it
->second
.end()) return false;
84 // Create the given password folder.
85 bool createFolder(const std::string
& folder
) {
86 if (reject_local_folders_
&& folder
.find('(') != std::string::npos
)
88 return data_
.insert(make_pair(folder
, Folder())).second
;
91 // Remove the given password from the given password folder.
92 bool removeEntry(const std::string
& folder
, const std::string
& key
) {
93 Data::iterator it
= data_
.find(folder
);
94 if (it
== data_
.end()) return false;
95 return it
->second
.erase(key
) > 0;
98 // Write the given password data to the given password folder.
99 bool writeEntry(const std::string
& folder
, const std::string
& key
,
101 Data::iterator it
= data_
.find(folder
);
102 if (it
== data_
.end()) return false;
103 it
->second
[key
] = value
;
108 typedef std::map
<std::string
, Blob
> Folder
;
109 typedef std::map
<std::string
, Folder
> Data
;
112 // "Local" folders are folders containing local profile IDs in their names. We
113 // can reject attempts to create them in order to make it easier to create
114 // legacy shared passwords in these tests, for testing the migration code.
115 bool reject_local_folders_
;
117 // No need to disallow copy and assign. This class is safe to copy and assign.
120 } // anonymous namespace
122 // Obscure magic: we need to declare storage for this constant because we use it
123 // in ways that require its address in this test, but not in the actual code.
124 const int NativeBackendKWallet::kInvalidKWalletHandle
;
126 // Subclass NativeBackendKWallet to promote some members to public for testing.
127 class NativeBackendKWalletStub
: public NativeBackendKWallet
{
129 NativeBackendKWalletStub(LocalProfileId id
, PrefService
* pref_service
)
130 : NativeBackendKWallet(id
, pref_service
) {
132 using NativeBackendKWallet::InitWithBus
;
133 using NativeBackendKWallet::kInvalidKWalletHandle
;
134 using NativeBackendKWallet::DeserializeValue
;
137 // Provide some test forms to avoid having to set them up in each test.
138 class NativeBackendKWalletTestBase
: public testing::Test
{
140 NativeBackendKWalletTestBase() {
141 form_google_
.origin
= GURL("http://www.google.com/");
142 form_google_
.action
= GURL("http://www.google.com/login");
143 form_google_
.username_element
= UTF8ToUTF16("user");
144 form_google_
.username_value
= UTF8ToUTF16("joeschmoe");
145 form_google_
.password_element
= UTF8ToUTF16("pass");
146 form_google_
.password_value
= UTF8ToUTF16("seekrit");
147 form_google_
.submit_element
= UTF8ToUTF16("submit");
148 form_google_
.signon_realm
= "Google";
150 form_isc_
.origin
= GURL("http://www.isc.org/");
151 form_isc_
.action
= GURL("http://www.isc.org/auth");
152 form_isc_
.username_element
= UTF8ToUTF16("id");
153 form_isc_
.username_value
= UTF8ToUTF16("janedoe");
154 form_isc_
.password_element
= UTF8ToUTF16("passwd");
155 form_isc_
.password_value
= UTF8ToUTF16("ihazabukkit");
156 form_isc_
.submit_element
= UTF8ToUTF16("login");
157 form_isc_
.signon_realm
= "ISC";
160 void CheckPasswordForm(const PasswordForm
& expected
,
161 const PasswordForm
& actual
);
163 PasswordForm form_google_
;
164 PasswordForm form_isc_
;
167 void NativeBackendKWalletTestBase::CheckPasswordForm(
168 const PasswordForm
& expected
, const PasswordForm
& actual
) {
169 EXPECT_EQ(expected
.origin
, actual
.origin
);
170 EXPECT_EQ(expected
.password_value
, actual
.password_value
);
171 EXPECT_EQ(expected
.action
, actual
.action
);
172 EXPECT_EQ(expected
.username_element
, actual
.username_element
);
173 EXPECT_EQ(expected
.username_value
, actual
.username_value
);
174 EXPECT_EQ(expected
.password_element
, actual
.password_element
);
175 EXPECT_EQ(expected
.submit_element
, actual
.submit_element
);
176 EXPECT_EQ(expected
.signon_realm
, actual
.signon_realm
);
177 EXPECT_EQ(expected
.ssl_valid
, actual
.ssl_valid
);
178 EXPECT_EQ(expected
.preferred
, actual
.preferred
);
179 // We don't check the date created. It varies.
180 EXPECT_EQ(expected
.blacklisted_by_user
, actual
.blacklisted_by_user
);
181 EXPECT_EQ(expected
.scheme
, actual
.scheme
);
184 class NativeBackendKWalletTest
: public NativeBackendKWalletTestBase
{
186 NativeBackendKWalletTest()
187 : ui_thread_(BrowserThread::UI
, &message_loop_
),
188 db_thread_(BrowserThread::DB
), klauncher_ret_(0),
189 klauncher_contacted_(false), kwallet_runnable_(true),
190 kwallet_running_(true), kwallet_enabled_(true) {
193 virtual void SetUp();
194 virtual void TearDown();
196 // Let the DB thread run to completion of all current tasks.
198 base::WaitableEvent
event(false, false);
199 BrowserThread::PostTask(BrowserThread::DB
, FROM_HERE
,
200 base::Bind(ThreadDone
, &event
));
202 // Some of the tests may post messages to the UI thread, but we don't need
203 // to run those until after the DB thread is finished. So run it here.
204 message_loop_
.RunUntilIdle();
206 static void ThreadDone(base::WaitableEvent
* event
) {
210 // Utilities to help verify sets of expectations.
212 std::pair
<std::string
,
213 std::vector
<const PasswordForm
*> > > ExpectationArray
;
214 void CheckPasswordForms(const std::string
& folder
,
215 const ExpectationArray
& sorted_expected
);
217 base::MessageLoopForUI message_loop_
;
218 content::TestBrowserThread ui_thread_
;
219 content::TestBrowserThread db_thread_
;
220 TestingProfile profile_
;
222 scoped_refptr
<dbus::MockBus
> mock_session_bus_
;
223 scoped_refptr
<dbus::MockObjectProxy
> mock_klauncher_proxy_
;
224 scoped_refptr
<dbus::MockObjectProxy
> mock_kwallet_proxy_
;
227 std::string klauncher_error_
;
228 bool klauncher_contacted_
;
230 bool kwallet_runnable_
;
231 bool kwallet_running_
;
232 bool kwallet_enabled_
;
237 dbus::Response
* KLauncherMethodCall(
238 dbus::MethodCall
* method_call
, testing::Unused
);
240 dbus::Response
* KWalletMethodCall(
241 dbus::MethodCall
* method_call
, testing::Unused
);
244 void NativeBackendKWalletTest::SetUp() {
245 ASSERT_TRUE(db_thread_
.Start());
247 dbus::Bus::Options options
;
248 options
.bus_type
= dbus::Bus::SESSION
;
249 mock_session_bus_
= new dbus::MockBus(options
);
251 mock_klauncher_proxy_
=
252 new dbus::MockObjectProxy(mock_session_bus_
.get(),
254 dbus::ObjectPath("/KLauncher"));
255 EXPECT_CALL(*mock_klauncher_proxy_
.get(), MockCallMethodAndBlock(_
, _
))
257 Invoke(this, &NativeBackendKWalletTest::KLauncherMethodCall
));
259 mock_kwallet_proxy_
=
260 new dbus::MockObjectProxy(mock_session_bus_
.get(),
262 dbus::ObjectPath("/modules/kwalletd"));
263 EXPECT_CALL(*mock_kwallet_proxy_
.get(), MockCallMethodAndBlock(_
, _
))
265 Invoke(this, &NativeBackendKWalletTest::KWalletMethodCall
));
268 *mock_session_bus_
.get(),
269 GetObjectProxy("org.kde.klauncher", dbus::ObjectPath("/KLauncher")))
270 .WillRepeatedly(Return(mock_klauncher_proxy_
.get()));
272 *mock_session_bus_
.get(),
273 GetObjectProxy("org.kde.kwalletd", dbus::ObjectPath("/modules/kwalletd")))
274 .WillRepeatedly(Return(mock_kwallet_proxy_
.get()));
276 EXPECT_CALL(*mock_session_bus_
.get(), ShutdownAndBlock()).WillOnce(Return())
277 .WillRepeatedly(Return());
280 void NativeBackendKWalletTest::TearDown() {
281 base::MessageLoop::current()->PostTask(FROM_HERE
,
282 base::MessageLoop::QuitClosure());
283 base::MessageLoop::current()->Run();
287 dbus::Response
* NativeBackendKWalletTest::KLauncherMethodCall(
288 dbus::MethodCall
* method_call
, testing::Unused
) {
289 EXPECT_EQ("org.kde.KLauncher", method_call
->GetInterface());
290 EXPECT_EQ("start_service_by_desktop_name", method_call
->GetMember());
292 klauncher_contacted_
= true;
294 dbus::MessageReader
reader(method_call
);
295 std::string service_name
;
296 std::vector
<std::string
> urls
;
297 std::vector
<std::string
> envs
;
298 std::string startup_id
;
301 EXPECT_TRUE(reader
.PopString(&service_name
));
302 EXPECT_TRUE(reader
.PopArrayOfStrings(&urls
));
303 EXPECT_TRUE(reader
.PopArrayOfStrings(&envs
));
304 EXPECT_TRUE(reader
.PopString(&startup_id
));
305 EXPECT_TRUE(reader
.PopBool(&blind
));
307 EXPECT_EQ("kwalletd", service_name
);
308 EXPECT_TRUE(urls
.empty());
309 EXPECT_TRUE(envs
.empty());
310 EXPECT_TRUE(startup_id
.empty());
313 if (kwallet_runnable_
)
314 kwallet_running_
= true;
316 scoped_ptr
<dbus::Response
> response(dbus::Response::CreateEmpty());
317 dbus::MessageWriter
writer(response
.get());
318 writer
.AppendInt32(klauncher_ret_
);
319 writer
.AppendString(std::string()); // dbus_name
320 writer
.AppendString(klauncher_error_
);
321 writer
.AppendInt32(1234); // pid
322 return response
.release();
325 dbus::Response
* NativeBackendKWalletTest::KWalletMethodCall(
326 dbus::MethodCall
* method_call
, testing::Unused
) {
327 if (!kwallet_running_
)
329 EXPECT_EQ("org.kde.KWallet", method_call
->GetInterface());
331 scoped_ptr
<dbus::Response
> response
;
332 if (method_call
->GetMember() == "isEnabled") {
333 response
= dbus::Response::CreateEmpty();
334 dbus::MessageWriter
writer(response
.get());
335 writer
.AppendBool(kwallet_enabled_
);
336 } else if (method_call
->GetMember() == "networkWallet") {
337 response
= dbus::Response::CreateEmpty();
338 dbus::MessageWriter
writer(response
.get());
339 writer
.AppendString("test_wallet"); // Should match |open| below.
340 } else if (method_call
->GetMember() == "open") {
341 dbus::MessageReader
reader(method_call
);
342 std::string wallet_name
;
344 std::string app_name
;
345 EXPECT_TRUE(reader
.PopString(&wallet_name
));
346 EXPECT_TRUE(reader
.PopInt64(&wallet_id
));
347 EXPECT_TRUE(reader
.PopString(&app_name
));
348 EXPECT_EQ("test_wallet", wallet_name
); // Should match |networkWallet|.
349 response
= dbus::Response::CreateEmpty();
350 dbus::MessageWriter
writer(response
.get());
351 writer
.AppendInt32(1); // Can be anything but kInvalidKWalletHandle.
352 } else if (method_call
->GetMember() == "hasFolder" ||
353 method_call
->GetMember() == "createFolder") {
354 dbus::MessageReader
reader(method_call
);
355 int handle
= NativeBackendKWalletStub::kInvalidKWalletHandle
;
356 std::string folder_name
;
357 std::string app_name
;
358 EXPECT_TRUE(reader
.PopInt32(&handle
));
359 EXPECT_TRUE(reader
.PopString(&folder_name
));
360 EXPECT_TRUE(reader
.PopString(&app_name
));
361 EXPECT_NE(NativeBackendKWalletStub::kInvalidKWalletHandle
, handle
);
362 response
= dbus::Response::CreateEmpty();
363 dbus::MessageWriter
writer(response
.get());
364 if (method_call
->GetMember() == "hasFolder")
365 writer
.AppendBool(wallet_
.hasFolder(folder_name
));
367 writer
.AppendBool(wallet_
.createFolder(folder_name
));
368 } else if (method_call
->GetMember() == "hasEntry" ||
369 method_call
->GetMember() == "removeEntry") {
370 dbus::MessageReader
reader(method_call
);
371 int handle
= NativeBackendKWalletStub::kInvalidKWalletHandle
;
372 std::string folder_name
;
374 std::string app_name
;
375 EXPECT_TRUE(reader
.PopInt32(&handle
));
376 EXPECT_TRUE(reader
.PopString(&folder_name
));
377 EXPECT_TRUE(reader
.PopString(&key
));
378 EXPECT_TRUE(reader
.PopString(&app_name
));
379 EXPECT_NE(NativeBackendKWalletStub::kInvalidKWalletHandle
, handle
);
380 response
= dbus::Response::CreateEmpty();
381 dbus::MessageWriter
writer(response
.get());
382 if (method_call
->GetMember() == "hasEntry")
383 writer
.AppendBool(wallet_
.hasEntry(folder_name
, key
));
385 writer
.AppendInt32(wallet_
.removeEntry(folder_name
, key
) ? 0 : 1);
386 } else if (method_call
->GetMember() == "entryList") {
387 dbus::MessageReader
reader(method_call
);
388 int handle
= NativeBackendKWalletStub::kInvalidKWalletHandle
;
389 std::string folder_name
;
390 std::string app_name
;
391 EXPECT_TRUE(reader
.PopInt32(&handle
));
392 EXPECT_TRUE(reader
.PopString(&folder_name
));
393 EXPECT_TRUE(reader
.PopString(&app_name
));
394 EXPECT_NE(NativeBackendKWalletStub::kInvalidKWalletHandle
, handle
);
395 std::vector
<std::string
> entries
;
396 if (wallet_
.entryList(folder_name
, &entries
)) {
397 response
= dbus::Response::CreateEmpty();
398 dbus::MessageWriter
writer(response
.get());
399 writer
.AppendArrayOfStrings(entries
);
401 } else if (method_call
->GetMember() == "readEntry") {
402 dbus::MessageReader
reader(method_call
);
403 int handle
= NativeBackendKWalletStub::kInvalidKWalletHandle
;
404 std::string folder_name
;
406 std::string app_name
;
407 EXPECT_TRUE(reader
.PopInt32(&handle
));
408 EXPECT_TRUE(reader
.PopString(&folder_name
));
409 EXPECT_TRUE(reader
.PopString(&key
));
410 EXPECT_TRUE(reader
.PopString(&app_name
));
411 EXPECT_NE(NativeBackendKWalletStub::kInvalidKWalletHandle
, handle
);
412 TestKWallet::Blob value
;
413 if (wallet_
.readEntry(folder_name
, key
, &value
)) {
414 response
= dbus::Response::CreateEmpty();
415 dbus::MessageWriter
writer(response
.get());
416 writer
.AppendArrayOfBytes(value
.data(), value
.size());
418 } else if (method_call
->GetMember() == "writeEntry") {
419 dbus::MessageReader
reader(method_call
);
420 int handle
= NativeBackendKWalletStub::kInvalidKWalletHandle
;
421 std::string folder_name
;
423 uint8_t* bytes
= NULL
;
425 std::string app_name
;
426 EXPECT_TRUE(reader
.PopInt32(&handle
));
427 EXPECT_TRUE(reader
.PopString(&folder_name
));
428 EXPECT_TRUE(reader
.PopString(&key
));
429 EXPECT_TRUE(reader
.PopArrayOfBytes(&bytes
, &length
));
430 EXPECT_TRUE(reader
.PopString(&app_name
));
431 EXPECT_NE(NativeBackendKWalletStub::kInvalidKWalletHandle
, handle
);
432 response
= dbus::Response::CreateEmpty();
433 dbus::MessageWriter
writer(response
.get());
435 wallet_
.writeEntry(folder_name
, key
,
436 TestKWallet::Blob(bytes
, length
)) ? 0 : 1);
439 EXPECT_FALSE(response
.get() == NULL
);
440 return response
.release();
443 void NativeBackendKWalletTest::CheckPasswordForms(
444 const std::string
& folder
, const ExpectationArray
& sorted_expected
) {
445 EXPECT_TRUE(wallet_
.hasFolder(folder
));
446 std::vector
<std::string
> entries
;
447 EXPECT_TRUE(wallet_
.entryList(folder
, &entries
));
448 EXPECT_EQ(sorted_expected
.size(), entries
.size());
449 std::sort(entries
.begin(), entries
.end());
450 for (size_t i
= 0; i
< entries
.size() && i
< sorted_expected
.size(); ++i
) {
451 EXPECT_EQ(sorted_expected
[i
].first
, entries
[i
]);
452 TestKWallet::Blob value
;
453 EXPECT_TRUE(wallet_
.readEntry(folder
, entries
[i
], &value
));
454 Pickle
pickle(reinterpret_cast<const char*>(value
.data()), value
.size());
455 std::vector
<PasswordForm
*> forms
;
456 NativeBackendKWalletStub::DeserializeValue(entries
[i
], pickle
, &forms
);
457 const std::vector
<const PasswordForm
*>& expect
= sorted_expected
[i
].second
;
458 EXPECT_EQ(expect
.size(), forms
.size());
459 for (size_t j
= 0; j
< forms
.size() && j
< expect
.size(); ++j
)
460 CheckPasswordForm(*expect
[j
], *forms
[j
]);
461 STLDeleteElements(&forms
);
465 TEST_F(NativeBackendKWalletTest
, NotEnabled
) {
466 NativeBackendKWalletStub
kwallet(42, profile_
.GetPrefs());
467 kwallet_enabled_
= false;
468 EXPECT_FALSE(kwallet
.InitWithBus(mock_session_bus_
));
469 EXPECT_FALSE(klauncher_contacted_
);
472 TEST_F(NativeBackendKWalletTest
, NotRunnable
) {
473 NativeBackendKWalletStub
kwallet(42, profile_
.GetPrefs());
474 kwallet_runnable_
= false;
475 kwallet_running_
= false;
476 EXPECT_FALSE(kwallet
.InitWithBus(mock_session_bus_
));
477 EXPECT_TRUE(klauncher_contacted_
);
480 TEST_F(NativeBackendKWalletTest
, NotRunningOrEnabled
) {
481 NativeBackendKWalletStub
kwallet(42, profile_
.GetPrefs());
482 kwallet_running_
= false;
483 kwallet_enabled_
= false;
484 EXPECT_FALSE(kwallet
.InitWithBus(mock_session_bus_
));
485 EXPECT_TRUE(klauncher_contacted_
);
488 TEST_F(NativeBackendKWalletTest
, NotRunning
) {
489 NativeBackendKWalletStub
kwallet(42, profile_
.GetPrefs());
490 kwallet_running_
= false;
491 EXPECT_TRUE(kwallet
.InitWithBus(mock_session_bus_
));
492 EXPECT_TRUE(klauncher_contacted_
);
495 TEST_F(NativeBackendKWalletTest
, BasicStartup
) {
496 NativeBackendKWalletStub
kwallet(42, profile_
.GetPrefs());
497 EXPECT_TRUE(kwallet
.InitWithBus(mock_session_bus_
));
498 EXPECT_FALSE(klauncher_contacted_
);
501 TEST_F(NativeBackendKWalletTest
, BasicAddLogin
) {
502 // Pretend that the migration has already taken place.
503 profile_
.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId
, true);
505 NativeBackendKWalletStub
backend(42, profile_
.GetPrefs());
506 EXPECT_TRUE(backend
.InitWithBus(mock_session_bus_
));
508 BrowserThread::PostTask(
509 BrowserThread::DB
, FROM_HERE
,
510 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin
),
511 base::Unretained(&backend
), form_google_
));
515 EXPECT_FALSE(wallet_
.hasFolder("Chrome Form Data"));
517 std::vector
<const PasswordForm
*> forms
;
518 forms
.push_back(&form_google_
);
519 ExpectationArray expected
;
520 expected
.push_back(make_pair(std::string(form_google_
.signon_realm
), forms
));
521 CheckPasswordForms("Chrome Form Data (42)", expected
);
524 TEST_F(NativeBackendKWalletTest
, BasicListLogins
) {
525 // Pretend that the migration has already taken place.
526 profile_
.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId
, true);
528 NativeBackendKWalletStub
backend(42, profile_
.GetPrefs());
529 EXPECT_TRUE(backend
.InitWithBus(mock_session_bus_
));
531 BrowserThread::PostTask(
532 BrowserThread::DB
, FROM_HERE
,
533 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin
),
534 base::Unretained(&backend
), form_google_
));
536 std::vector
<PasswordForm
*> form_list
;
537 BrowserThread::PostTask(
538 BrowserThread::DB
, FROM_HERE
,
540 base::IgnoreResult(&NativeBackendKWalletStub::GetAutofillableLogins
),
541 base::Unretained(&backend
), &form_list
));
545 // Quick check that we got something back.
546 EXPECT_EQ(1u, form_list
.size());
547 STLDeleteElements(&form_list
);
549 EXPECT_FALSE(wallet_
.hasFolder("Chrome Form Data"));
551 std::vector
<const PasswordForm
*> forms
;
552 forms
.push_back(&form_google_
);
553 ExpectationArray expected
;
554 expected
.push_back(make_pair(std::string(form_google_
.signon_realm
), forms
));
555 CheckPasswordForms("Chrome Form Data (42)", expected
);
558 TEST_F(NativeBackendKWalletTest
, BasicRemoveLogin
) {
559 // Pretend that the migration has already taken place.
560 profile_
.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId
, true);
562 NativeBackendKWalletStub
backend(42, profile_
.GetPrefs());
563 EXPECT_TRUE(backend
.InitWithBus(mock_session_bus_
));
565 BrowserThread::PostTask(
566 BrowserThread::DB
, FROM_HERE
,
567 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin
),
568 base::Unretained(&backend
), form_google_
));
572 EXPECT_FALSE(wallet_
.hasFolder("Chrome Form Data"));
574 std::vector
<const PasswordForm
*> forms
;
575 forms
.push_back(&form_google_
);
576 ExpectationArray expected
;
577 expected
.push_back(make_pair(std::string(form_google_
.signon_realm
), forms
));
578 CheckPasswordForms("Chrome Form Data (42)", expected
);
580 BrowserThread::PostTask(
581 BrowserThread::DB
, FROM_HERE
,
582 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::RemoveLogin
),
583 base::Unretained(&backend
), form_google_
));
588 CheckPasswordForms("Chrome Form Data (42)", expected
);
591 TEST_F(NativeBackendKWalletTest
, RemoveNonexistentLogin
) {
592 // Pretend that the migration has already taken place.
593 profile_
.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId
, true);
595 NativeBackendKWalletStub
backend(42, profile_
.GetPrefs());
596 EXPECT_TRUE(backend
.InitWithBus(mock_session_bus_
));
598 // First add an unrelated login.
599 BrowserThread::PostTask(
600 BrowserThread::DB
, FROM_HERE
,
601 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin
),
602 base::Unretained(&backend
), form_google_
));
606 EXPECT_FALSE(wallet_
.hasFolder("Chrome Form Data"));
608 std::vector
<const PasswordForm
*> forms
;
609 forms
.push_back(&form_google_
);
610 ExpectationArray expected
;
611 expected
.push_back(make_pair(std::string(form_google_
.signon_realm
), forms
));
612 CheckPasswordForms("Chrome Form Data (42)", expected
);
614 // Attempt to remove a login that doesn't exist.
615 BrowserThread::PostTask(
616 BrowserThread::DB
, FROM_HERE
,
617 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::RemoveLogin
),
618 base::Unretained(&backend
), form_isc_
));
620 // Make sure we can still get the first form back.
621 std::vector
<PasswordForm
*> form_list
;
622 BrowserThread::PostTask(
623 BrowserThread::DB
, FROM_HERE
,
625 base::IgnoreResult(&NativeBackendKWalletStub::GetAutofillableLogins
),
626 base::Unretained(&backend
), &form_list
));
630 // Quick check that we got something back.
631 EXPECT_EQ(1u, form_list
.size());
632 STLDeleteElements(&form_list
);
634 CheckPasswordForms("Chrome Form Data (42)", expected
);
637 TEST_F(NativeBackendKWalletTest
, AddDuplicateLogin
) {
638 // Pretend that the migration has already taken place.
639 profile_
.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId
, true);
641 NativeBackendKWalletStub
backend(42, profile_
.GetPrefs());
642 EXPECT_TRUE(backend
.InitWithBus(mock_session_bus_
));
644 BrowserThread::PostTask(
645 BrowserThread::DB
, FROM_HERE
,
646 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin
),
647 base::Unretained(&backend
), form_google_
));
648 BrowserThread::PostTask(
649 BrowserThread::DB
, FROM_HERE
,
650 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin
),
651 base::Unretained(&backend
), form_google_
));
655 EXPECT_FALSE(wallet_
.hasFolder("Chrome Form Data"));
657 std::vector
<const PasswordForm
*> forms
;
658 forms
.push_back(&form_google_
);
659 ExpectationArray expected
;
660 expected
.push_back(make_pair(std::string(form_google_
.signon_realm
), forms
));
661 CheckPasswordForms("Chrome Form Data (42)", expected
);
664 TEST_F(NativeBackendKWalletTest
, ListLoginsAppends
) {
665 // Pretend that the migration has already taken place.
666 profile_
.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId
, true);
668 NativeBackendKWalletStub
backend(42, profile_
.GetPrefs());
669 EXPECT_TRUE(backend
.InitWithBus(mock_session_bus_
));
671 BrowserThread::PostTask(
672 BrowserThread::DB
, FROM_HERE
,
673 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin
),
674 base::Unretained(&backend
), form_google_
));
676 // Send the same request twice with the same list both times.
677 std::vector
<PasswordForm
*> form_list
;
678 BrowserThread::PostTask(
679 BrowserThread::DB
, FROM_HERE
,
681 base::IgnoreResult(&NativeBackendKWalletStub::GetAutofillableLogins
),
682 base::Unretained(&backend
), &form_list
));
683 BrowserThread::PostTask(
684 BrowserThread::DB
, FROM_HERE
,
686 base::IgnoreResult(&NativeBackendKWalletStub::GetAutofillableLogins
),
687 base::Unretained(&backend
), &form_list
));
691 // Quick check that we got two results back.
692 EXPECT_EQ(2u, form_list
.size());
693 STLDeleteElements(&form_list
);
695 EXPECT_FALSE(wallet_
.hasFolder("Chrome Form Data"));
697 std::vector
<const PasswordForm
*> forms
;
698 forms
.push_back(&form_google_
);
699 ExpectationArray expected
;
700 expected
.push_back(make_pair(std::string(form_google_
.signon_realm
), forms
));
701 CheckPasswordForms("Chrome Form Data (42)", expected
);
704 // TODO(mdm): add more basic (i.e. non-migration) tests here at some point.
705 // (For example tests for storing >1 password per realm pickle.)
707 TEST_F(NativeBackendKWalletTest
, DISABLED_MigrateOneLogin
) {
708 // Reject attempts to migrate so we can populate the store.
709 wallet_
.set_reject_local_folders(true);
712 NativeBackendKWalletStub
backend(42, profile_
.GetPrefs());
713 EXPECT_TRUE(backend
.InitWithBus(mock_session_bus_
));
715 BrowserThread::PostTask(
716 BrowserThread::DB
, FROM_HERE
,
717 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin
),
718 base::Unretained(&backend
), form_google_
));
720 // Make sure we can get the form back even when migration is failing.
721 std::vector
<PasswordForm
*> form_list
;
722 BrowserThread::PostTask(
723 BrowserThread::DB
, FROM_HERE
,
726 &NativeBackendKWalletStub::GetAutofillableLogins
),
727 base::Unretained(&backend
), &form_list
));
731 // Quick check that we got something back.
732 EXPECT_EQ(1u, form_list
.size());
733 STLDeleteElements(&form_list
);
736 EXPECT_FALSE(wallet_
.hasFolder("Chrome Form Data (42)"));
738 std::vector
<const PasswordForm
*> forms
;
739 forms
.push_back(&form_google_
);
740 ExpectationArray expected
;
741 expected
.push_back(make_pair(std::string(form_google_
.signon_realm
), forms
));
742 CheckPasswordForms("Chrome Form Data", expected
);
744 // Now allow the migration.
745 wallet_
.set_reject_local_folders(false);
748 NativeBackendKWalletStub
backend(42, profile_
.GetPrefs());
749 EXPECT_TRUE(backend
.InitWithBus(mock_session_bus_
));
751 // Trigger the migration by looking something up.
752 std::vector
<PasswordForm
*> form_list
;
753 BrowserThread::PostTask(
754 BrowserThread::DB
, FROM_HERE
,
757 &NativeBackendKWalletStub::GetAutofillableLogins
),
758 base::Unretained(&backend
), &form_list
));
762 // Quick check that we got something back.
763 EXPECT_EQ(1u, form_list
.size());
764 STLDeleteElements(&form_list
);
767 CheckPasswordForms("Chrome Form Data", expected
);
768 CheckPasswordForms("Chrome Form Data (42)", expected
);
770 // Check that we have set the persistent preference.
772 profile_
.GetPrefs()->GetBoolean(prefs::kPasswordsUseLocalProfileId
));
775 TEST_F(NativeBackendKWalletTest
, DISABLED_MigrateToMultipleProfiles
) {
776 // Reject attempts to migrate so we can populate the store.
777 wallet_
.set_reject_local_folders(true);
780 NativeBackendKWalletStub
backend(42, profile_
.GetPrefs());
781 EXPECT_TRUE(backend
.InitWithBus(mock_session_bus_
));
783 BrowserThread::PostTask(
784 BrowserThread::DB
, FROM_HERE
,
785 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin
),
786 base::Unretained(&backend
), form_google_
));
791 EXPECT_FALSE(wallet_
.hasFolder("Chrome Form Data (42)"));
793 std::vector
<const PasswordForm
*> forms
;
794 forms
.push_back(&form_google_
);
795 ExpectationArray expected
;
796 expected
.push_back(make_pair(std::string(form_google_
.signon_realm
), forms
));
797 CheckPasswordForms("Chrome Form Data", expected
);
799 // Now allow the migration.
800 wallet_
.set_reject_local_folders(false);
803 NativeBackendKWalletStub
backend(42, profile_
.GetPrefs());
804 EXPECT_TRUE(backend
.InitWithBus(mock_session_bus_
));
806 // Trigger the migration by looking something up.
807 std::vector
<PasswordForm
*> form_list
;
808 BrowserThread::PostTask(
809 BrowserThread::DB
, FROM_HERE
,
812 &NativeBackendKWalletStub::GetAutofillableLogins
),
813 base::Unretained(&backend
), &form_list
));
817 // Quick check that we got something back.
818 EXPECT_EQ(1u, form_list
.size());
819 STLDeleteElements(&form_list
);
822 CheckPasswordForms("Chrome Form Data", expected
);
823 CheckPasswordForms("Chrome Form Data (42)", expected
);
825 // Check that we have set the persistent preference.
827 profile_
.GetPrefs()->GetBoolean(prefs::kPasswordsUseLocalProfileId
));
829 // Normally we'd actually have a different profile. But in the test just reset
830 // the profile's persistent pref; we pass in the local profile id anyway.
831 profile_
.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId
, false);
834 NativeBackendKWalletStub
backend(24, profile_
.GetPrefs());
835 EXPECT_TRUE(backend
.InitWithBus(mock_session_bus_
));
837 // Trigger the migration by looking something up.
838 std::vector
<PasswordForm
*> form_list
;
839 BrowserThread::PostTask(
840 BrowserThread::DB
, FROM_HERE
,
843 &NativeBackendKWalletStub::GetAutofillableLogins
),
844 base::Unretained(&backend
), &form_list
));
848 // Quick check that we got something back.
849 EXPECT_EQ(1u, form_list
.size());
850 STLDeleteElements(&form_list
);
853 CheckPasswordForms("Chrome Form Data", expected
);
854 CheckPasswordForms("Chrome Form Data (42)", expected
);
855 CheckPasswordForms("Chrome Form Data (24)", expected
);
858 TEST_F(NativeBackendKWalletTest
, DISABLED_NoMigrationWithPrefSet
) {
859 // Reject attempts to migrate so we can populate the store.
860 wallet_
.set_reject_local_folders(true);
863 NativeBackendKWalletStub
backend(42, profile_
.GetPrefs());
864 EXPECT_TRUE(backend
.InitWithBus(mock_session_bus_
));
866 BrowserThread::PostTask(
867 BrowserThread::DB
, FROM_HERE
,
868 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin
),
869 base::Unretained(&backend
), form_google_
));
874 EXPECT_FALSE(wallet_
.hasFolder("Chrome Form Data (42)"));
876 std::vector
<const PasswordForm
*> forms
;
877 forms
.push_back(&form_google_
);
878 ExpectationArray expected
;
879 expected
.push_back(make_pair(std::string(form_google_
.signon_realm
), forms
));
880 CheckPasswordForms("Chrome Form Data", expected
);
882 // Now allow migration, but also pretend that the it has already taken place.
883 wallet_
.set_reject_local_folders(false);
884 profile_
.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId
, true);
887 NativeBackendKWalletStub
backend(42, profile_
.GetPrefs());
888 EXPECT_TRUE(backend
.InitWithBus(mock_session_bus_
));
890 // Trigger the migration by adding a new login.
891 BrowserThread::PostTask(
892 BrowserThread::DB
, FROM_HERE
,
893 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin
),
894 base::Unretained(&backend
), form_isc_
));
896 // Look up all logins; we expect only the one we added.
897 std::vector
<PasswordForm
*> form_list
;
898 BrowserThread::PostTask(
899 BrowserThread::DB
, FROM_HERE
,
902 &NativeBackendKWalletStub::GetAutofillableLogins
),
903 base::Unretained(&backend
), &form_list
));
907 // Quick check that we got the right thing back.
908 EXPECT_EQ(1u, form_list
.size());
909 if (form_list
.size() > 0)
910 EXPECT_EQ(form_isc_
.signon_realm
, form_list
[0]->signon_realm
);
911 STLDeleteElements(&form_list
);
914 CheckPasswordForms("Chrome Form Data", expected
);
916 forms
[0] = &form_isc_
;
918 expected
.push_back(make_pair(std::string(form_isc_
.signon_realm
), forms
));
919 CheckPasswordForms("Chrome Form Data (42)", expected
);
922 TEST_F(NativeBackendKWalletTest
, DISABLED_DeleteMigratedPasswordIsIsolated
) {
923 // Reject attempts to migrate so we can populate the store.
924 wallet_
.set_reject_local_folders(true);
927 NativeBackendKWalletStub
backend(42, profile_
.GetPrefs());
928 EXPECT_TRUE(backend
.InitWithBus(mock_session_bus_
));
930 BrowserThread::PostTask(
931 BrowserThread::DB
, FROM_HERE
,
933 base::IgnoreResult(&NativeBackendKWalletStub::AddLogin
),
934 base::Unretained(&backend
), form_google_
));
939 EXPECT_FALSE(wallet_
.hasFolder("Chrome Form Data (42)"));
941 std::vector
<const PasswordForm
*> forms
;
942 forms
.push_back(&form_google_
);
943 ExpectationArray expected
;
944 expected
.push_back(make_pair(std::string(form_google_
.signon_realm
), forms
));
945 CheckPasswordForms("Chrome Form Data", expected
);
947 // Now allow the migration.
948 wallet_
.set_reject_local_folders(false);
951 NativeBackendKWalletStub
backend(42, profile_
.GetPrefs());
952 EXPECT_TRUE(backend
.InitWithBus(mock_session_bus_
));
954 // Trigger the migration by looking something up.
955 std::vector
<PasswordForm
*> form_list
;
956 BrowserThread::PostTask(
957 BrowserThread::DB
, FROM_HERE
,
960 &NativeBackendKWalletStub::GetAutofillableLogins
),
961 base::Unretained(&backend
), &form_list
));
965 // Quick check that we got something back.
966 EXPECT_EQ(1u, form_list
.size());
967 STLDeleteElements(&form_list
);
970 CheckPasswordForms("Chrome Form Data", expected
);
971 CheckPasswordForms("Chrome Form Data (42)", expected
);
973 // Check that we have set the persistent preference.
975 profile_
.GetPrefs()->GetBoolean(prefs::kPasswordsUseLocalProfileId
));
977 // Normally we'd actually have a different profile. But in the test just reset
978 // the profile's persistent pref; we pass in the local profile id anyway.
979 profile_
.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId
, false);
982 NativeBackendKWalletStub
backend(24, profile_
.GetPrefs());
983 EXPECT_TRUE(backend
.InitWithBus(mock_session_bus_
));
985 // Trigger the migration by looking something up.
986 std::vector
<PasswordForm
*> form_list
;
987 BrowserThread::PostTask(
988 BrowserThread::DB
, FROM_HERE
,
991 &NativeBackendKWalletStub::GetAutofillableLogins
),
992 base::Unretained(&backend
), &form_list
));
996 // Quick check that we got something back.
997 EXPECT_EQ(1u, form_list
.size());
998 STLDeleteElements(&form_list
);
1000 // There should be three passwords now.
1001 CheckPasswordForms("Chrome Form Data", expected
);
1002 CheckPasswordForms("Chrome Form Data (42)", expected
);
1003 CheckPasswordForms("Chrome Form Data (24)", expected
);
1005 // Now delete the password from this second profile.
1006 BrowserThread::PostTask(
1007 BrowserThread::DB
, FROM_HERE
,
1009 base::IgnoreResult(&NativeBackendKWalletStub::RemoveLogin
),
1010 base::Unretained(&backend
), form_google_
));
1014 // The other two copies of the password in different profiles should remain.
1015 CheckPasswordForms("Chrome Form Data", expected
);
1016 CheckPasswordForms("Chrome Form Data (42)", expected
);
1018 CheckPasswordForms("Chrome Form Data (24)", expected
);
1022 class NativeBackendKWalletPickleTest
: public NativeBackendKWalletTestBase
{
1024 void CreateVersion0Pickle(bool size_32
,
1025 const PasswordForm
& form
,
1027 void CheckVersion0Pickle(bool size_32
, PasswordForm::Scheme scheme
);
1030 void NativeBackendKWalletPickleTest::CreateVersion0Pickle(
1031 bool size_32
, const PasswordForm
& form
, Pickle
* pickle
) {
1032 const int kPickleVersion0
= 0;
1033 pickle
->WriteInt(kPickleVersion0
);
1035 pickle
->WriteUInt32(1); // Size of form list. 32 bits.
1037 pickle
->WriteUInt64(1); // Size of form list. 64 bits.
1038 pickle
->WriteInt(form
.scheme
);
1039 pickle
->WriteString(form
.origin
.spec());
1040 pickle
->WriteString(form
.action
.spec());
1041 pickle
->WriteString16(form
.username_element
);
1042 pickle
->WriteString16(form
.username_value
);
1043 pickle
->WriteString16(form
.password_element
);
1044 pickle
->WriteString16(form
.password_value
);
1045 pickle
->WriteString16(form
.submit_element
);
1046 pickle
->WriteBool(form
.ssl_valid
);
1047 pickle
->WriteBool(form
.preferred
);
1048 pickle
->WriteBool(form
.blacklisted_by_user
);
1049 pickle
->WriteInt64(form
.date_created
.ToTimeT());
1052 void NativeBackendKWalletPickleTest::CheckVersion0Pickle(
1053 bool size_32
, PasswordForm::Scheme scheme
) {
1055 PasswordForm form
= form_google_
;
1056 form
.scheme
= scheme
;
1057 CreateVersion0Pickle(size_32
, form
, &pickle
);
1058 std::vector
<PasswordForm
*> form_list
;
1059 NativeBackendKWalletStub::DeserializeValue(form
.signon_realm
,
1060 pickle
, &form_list
);
1061 EXPECT_EQ(1u, form_list
.size());
1062 if (form_list
.size() > 0)
1063 CheckPasswordForm(form
, *form_list
[0]);
1064 STLDeleteElements(&form_list
);
1067 // We try both SCHEME_HTML and SCHEME_BASIC since the scheme is stored right
1068 // after the size in the pickle, so it's what gets read as part of the count
1069 // when reading 32-bit pickles on 64-bit systems. SCHEME_HTML is 0 (so we'll
1070 // detect errors later) while SCHEME_BASIC is 1 (so we'll detect it then). We
1071 // try both 32-bit and 64-bit pickles since only one will be the "other" size
1072 // for whatever architecture we're running on, but we want to make sure we can
1073 // read all combinations in any event.
1075 TEST_F(NativeBackendKWalletPickleTest
, ReadsOld32BitHTMLPickles
) {
1076 CheckVersion0Pickle(true, PasswordForm::SCHEME_HTML
);
1079 TEST_F(NativeBackendKWalletPickleTest
, ReadsOld32BitHTTPPickles
) {
1080 CheckVersion0Pickle(true, PasswordForm::SCHEME_BASIC
);
1083 TEST_F(NativeBackendKWalletPickleTest
, ReadsOld64BitHTMLPickles
) {
1084 CheckVersion0Pickle(false, PasswordForm::SCHEME_HTML
);
1087 TEST_F(NativeBackendKWalletPickleTest
, ReadsOld64BitHTTPPickles
) {
1088 CheckVersion0Pickle(false, PasswordForm::SCHEME_BASIC
);