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. 4 5 #include <algorithm> 6 #include <map> 7 #include <string> 8 #include <vector> 9 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" 27 28 using content::BrowserThread; 29 using testing::_; 30 using testing::Invoke; 31 using testing::Return; 32 using content::PasswordForm; 33 34 namespace { 35 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. 38 class TestKWallet { 39 public: 40 typedef std::basic_string<uint8_t> Blob; // std::string is binary-safe. 41 42 TestKWallet() : reject_local_folders_(false) {} 43 44 void set_reject_local_folders(bool value) { reject_local_folders_ = value; } 45 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. 48 49 // Check for presence of a given password folder. 50 bool hasFolder(const std::string& folder) const { 51 return data_.find(folder) != data_.end(); 52 } 53 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(); 58 } 59 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); 68 return true; 69 } 70 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, 73 Blob* value) const { 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; 78 *value = fit->second; 79 return true; 80 } 81 82 // Create the given password folder. 83 bool createFolder(const std::string& folder) { 84 if (reject_local_folders_ && folder.find('(') != std::string::npos) 85 return false; 86 return data_.insert(make_pair(folder, Folder())).second; 87 } 88 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; 94 } 95 96 // Write the given password data to the given password folder. 97 bool writeEntry(const std::string& folder, const std::string& key, 98 const Blob& value) { 99 Data::iterator it = data_.find(folder); 100 if (it == data_.end()) return false; 101 it->second[key] = value; 102 return true; 103 } 104 105 private: 106 typedef std::map<std::string, Blob> Folder; 107 typedef std::map<std::string, Folder> Data; 108 109 Data 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_; 114 115 // No need to disallow copy and assign. This class is safe to copy and assign. 116 }; 117 118 } // anonymous namespace 119 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; 123 124 // Subclass NativeBackendKWallet to promote some members to public for testing. 125 class NativeBackendKWalletStub : public NativeBackendKWallet { 126 public: 127 NativeBackendKWalletStub(LocalProfileId id, PrefService* pref_service) 128 : NativeBackendKWallet(id, pref_service) { 129 } 130 using NativeBackendKWallet::InitWithBus; 131 using NativeBackendKWallet::kInvalidKWalletHandle; 132 using NativeBackendKWallet::DeserializeValue; 133 }; 134 135 // Provide some test forms to avoid having to set them up in each test. 136 class NativeBackendKWalletTestBase : public testing::Test { 137 protected: 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"; 147 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"; 156 } 157 158 void CheckPasswordForm(const PasswordForm& expected, 159 const PasswordForm& actual); 160 161 PasswordForm form_google_; 162 PasswordForm form_isc_; 163 }; 164 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); 180 } 181 182 class NativeBackendKWalletTest : public NativeBackendKWalletTestBase { 183 protected: 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) { 189 } 190 191 virtual void SetUp(); 192 virtual void TearDown(); 193 194 // Let the DB thread run to completion of all current tasks. 195 void RunDBThread() { 196 base::WaitableEvent event(false, false); 197 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, 198 base::Bind(ThreadDone, &event)); 199 event.Wait(); 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(); 203 } 204 static void ThreadDone(base::WaitableEvent* event) { 205 event->Signal(); 206 } 207 208 // Utilities to help verify sets of expectations. 209 typedef std::vector< 210 std::pair<std::string, 211 std::vector<const PasswordForm*> > > ExpectationArray; 212 void CheckPasswordForms(const std::string& folder, 213 const ExpectationArray& sorted_expected); 214 215 base::MessageLoopForUI message_loop_; 216 content::TestBrowserThread ui_thread_; 217 content::TestBrowserThread db_thread_; 218 TestingProfile profile_; 219 220 scoped_refptr<dbus::MockBus> mock_session_bus_; 221 scoped_refptr<dbus::MockObjectProxy> mock_klauncher_proxy_; 222 scoped_refptr<dbus::MockObjectProxy> mock_kwallet_proxy_; 223 224 int klauncher_ret_; 225 std::string klauncher_error_; 226 bool klauncher_contacted_; 227 228 bool kwallet_runnable_; 229 bool kwallet_running_; 230 bool kwallet_enabled_; 231 232 TestKWallet wallet_; 233 234 private: 235 dbus::Response* KLauncherMethodCall( 236 dbus::MethodCall* method_call, testing::Unused); 237 238 dbus::Response* KWalletMethodCall( 239 dbus::MethodCall* method_call, testing::Unused); 240 }; 241 242 void NativeBackendKWalletTest::SetUp() { 243 ASSERT_TRUE(db_thread_.Start()); 244 245 dbus::Bus::Options options; 246 options.bus_type = dbus::Bus::SESSION; 247 mock_session_bus_ = new dbus::MockBus(options); 248 249 mock_klauncher_proxy_ = 250 new dbus::MockObjectProxy(mock_session_bus_.get(), 251 "org.kde.klauncher", 252 dbus::ObjectPath("/KLauncher")); 253 EXPECT_CALL(*mock_klauncher_proxy_.get(), MockCallMethodAndBlock(_, _)) 254 .WillRepeatedly( 255 Invoke(this, &NativeBackendKWalletTest::KLauncherMethodCall)); 256 257 mock_kwallet_proxy_ = 258 new dbus::MockObjectProxy(mock_session_bus_.get(), 259 "org.kde.kwalletd", 260 dbus::ObjectPath("/modules/kwalletd")); 261 EXPECT_CALL(*mock_kwallet_proxy_.get(), MockCallMethodAndBlock(_, _)) 262 .WillRepeatedly( 263 Invoke(this, &NativeBackendKWalletTest::KWalletMethodCall)); 264 265 EXPECT_CALL( 266 *mock_session_bus_.get(), 267 GetObjectProxy("org.kde.klauncher", dbus::ObjectPath("/KLauncher"))) 268 .WillRepeatedly(Return(mock_klauncher_proxy_.get())); 269 EXPECT_CALL( 270 *mock_session_bus_.get(), 271 GetObjectProxy("org.kde.kwalletd", dbus::ObjectPath("/modules/kwalletd"))) 272 .WillRepeatedly(Return(mock_kwallet_proxy_.get())); 273 274 EXPECT_CALL(*mock_session_bus_.get(), ShutdownAndBlock()).WillOnce(Return()) 275 .WillRepeatedly(Return()); 276 } 277 278 void NativeBackendKWalletTest::TearDown() { 279 base::MessageLoop::current()->PostTask(FROM_HERE, 280 base::MessageLoop::QuitClosure()); 281 base::MessageLoop::current()->Run(); 282 db_thread_.Stop(); 283 } 284 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()); 289 290 klauncher_contacted_ = true; 291 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; 297 bool blind = false; 298 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)); 304 305 EXPECT_EQ("kwalletd", service_name); 306 EXPECT_TRUE(urls.empty()); 307 EXPECT_TRUE(envs.empty()); 308 EXPECT_TRUE(startup_id.empty()); 309 EXPECT_FALSE(blind); 310 311 if (kwallet_runnable_) 312 kwallet_running_ = true; 313 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(); 321 } 322 323 dbus::Response* NativeBackendKWalletTest::KWalletMethodCall( 324 dbus::MethodCall* method_call, testing::Unused) { 325 if (!kwallet_running_) 326 return NULL; 327 EXPECT_EQ("org.kde.KWallet", method_call->GetInterface()); 328 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; 341 int64_t wallet_id; 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)); 364 else 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; 371 std::string key; 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)); 382 else 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); 398 } 399 } else if (method_call->GetMember() == "readEntry") { 400 dbus::MessageReader reader(method_call); 401 int handle = NativeBackendKWalletStub::kInvalidKWalletHandle; 402 std::string folder_name; 403 std::string key; 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()); 415 } 416 } else if (method_call->GetMember() == "writeEntry") { 417 dbus::MessageReader reader(method_call); 418 int handle = NativeBackendKWalletStub::kInvalidKWalletHandle; 419 std::string folder_name; 420 std::string key; 421 uint8_t* bytes = NULL; 422 size_t length = 0; 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()); 432 writer.AppendInt32( 433 wallet_.writeEntry(folder_name, key, 434 TestKWallet::Blob(bytes, length)) ? 0 : 1); 435 } 436 437 EXPECT_FALSE(response.get() == NULL); 438 return response.release(); 439 } 440 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); 460 } 461 } 462 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_); 468 } 469 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_); 476 } 477 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_); 484 } 485 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_); 491 } 492 493 TEST_F(NativeBackendKWalletTest, BasicStartup) { 494 NativeBackendKWalletStub kwallet(42, profile_.GetPrefs()); 495 EXPECT_TRUE(kwallet.InitWithBus(mock_session_bus_)); 496 EXPECT_FALSE(klauncher_contacted_); 497 } 498 499 TEST_F(NativeBackendKWalletTest, BasicAddLogin) { 500 // Pretend that the migration has already taken place. 501 profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true); 502 503 NativeBackendKWalletStub backend(42, profile_.GetPrefs()); 504 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_)); 505 506 BrowserThread::PostTask( 507 BrowserThread::DB, FROM_HERE, 508 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin), 509 base::Unretained(&backend), form_google_)); 510 511 RunDBThread(); 512 513 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data")); 514 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); 520 } 521 522 TEST_F(NativeBackendKWalletTest, BasicListLogins) { 523 // Pretend that the migration has already taken place. 524 profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true); 525 526 NativeBackendKWalletStub backend(42, profile_.GetPrefs()); 527 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_)); 528 529 BrowserThread::PostTask( 530 BrowserThread::DB, FROM_HERE, 531 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin), 532 base::Unretained(&backend), form_google_)); 533 534 std::vector<PasswordForm*> form_list; 535 BrowserThread::PostTask( 536 BrowserThread::DB, FROM_HERE, 537 base::Bind( 538 base::IgnoreResult(&NativeBackendKWalletStub::GetAutofillableLogins), 539 base::Unretained(&backend), &form_list)); 540 541 RunDBThread(); 542 543 // Quick check that we got something back. 544 EXPECT_EQ(1u, form_list.size()); 545 STLDeleteElements(&form_list); 546 547 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data")); 548 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); 554 } 555 556 TEST_F(NativeBackendKWalletTest, BasicRemoveLogin) { 557 // Pretend that the migration has already taken place. 558 profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true); 559 560 NativeBackendKWalletStub backend(42, profile_.GetPrefs()); 561 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_)); 562 563 BrowserThread::PostTask( 564 BrowserThread::DB, FROM_HERE, 565 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin), 566 base::Unretained(&backend), form_google_)); 567 568 RunDBThread(); 569 570 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data")); 571 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); 577 578 BrowserThread::PostTask( 579 BrowserThread::DB, FROM_HERE, 580 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::RemoveLogin), 581 base::Unretained(&backend), form_google_)); 582 583 RunDBThread(); 584 585 expected.clear(); 586 CheckPasswordForms("Chrome Form Data (42)", expected); 587 } 588 589 TEST_F(NativeBackendKWalletTest, RemoveNonexistentLogin) { 590 // Pretend that the migration has already taken place. 591 profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true); 592 593 NativeBackendKWalletStub backend(42, profile_.GetPrefs()); 594 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_)); 595 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_)); 601 602 RunDBThread(); 603 604 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data")); 605 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); 611 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_)); 617 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, 622 base::Bind( 623 base::IgnoreResult(&NativeBackendKWalletStub::GetAutofillableLogins), 624 base::Unretained(&backend), &form_list)); 625 626 RunDBThread(); 627 628 // Quick check that we got something back. 629 EXPECT_EQ(1u, form_list.size()); 630 STLDeleteElements(&form_list); 631 632 CheckPasswordForms("Chrome Form Data (42)", expected); 633 } 634 635 TEST_F(NativeBackendKWalletTest, AddDuplicateLogin) { 636 // Pretend that the migration has already taken place. 637 profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true); 638 639 NativeBackendKWalletStub backend(42, profile_.GetPrefs()); 640 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_)); 641 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_)); 650 651 RunDBThread(); 652 653 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data")); 654 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); 660 } 661 662 TEST_F(NativeBackendKWalletTest, ListLoginsAppends) { 663 // Pretend that the migration has already taken place. 664 profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true); 665 666 NativeBackendKWalletStub backend(42, profile_.GetPrefs()); 667 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_)); 668 669 BrowserThread::PostTask( 670 BrowserThread::DB, FROM_HERE, 671 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin), 672 base::Unretained(&backend), form_google_)); 673 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, 678 base::Bind( 679 base::IgnoreResult(&NativeBackendKWalletStub::GetAutofillableLogins), 680 base::Unretained(&backend), &form_list)); 681 BrowserThread::PostTask( 682 BrowserThread::DB, FROM_HERE, 683 base::Bind( 684 base::IgnoreResult(&NativeBackendKWalletStub::GetAutofillableLogins), 685 base::Unretained(&backend), &form_list)); 686 687 RunDBThread(); 688 689 // Quick check that we got two results back. 690 EXPECT_EQ(2u, form_list.size()); 691 STLDeleteElements(&form_list); 692 693 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data")); 694 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); 700 } 701 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.) 704 705 TEST_F(NativeBackendKWalletTest, DISABLED_MigrateOneLogin) { 706 // Reject attempts to migrate so we can populate the store. 707 wallet_.set_reject_local_folders(true); 708 709 { 710 NativeBackendKWalletStub backend(42, profile_.GetPrefs()); 711 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_)); 712 713 BrowserThread::PostTask( 714 BrowserThread::DB, FROM_HERE, 715 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin), 716 base::Unretained(&backend), form_google_)); 717 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, 722 base::Bind( 723 base::IgnoreResult( 724 &NativeBackendKWalletStub::GetAutofillableLogins), 725 base::Unretained(&backend), &form_list)); 726 727 RunDBThread(); 728 729 // Quick check that we got something back. 730 EXPECT_EQ(1u, form_list.size()); 731 STLDeleteElements(&form_list); 732 } 733 734 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data (42)")); 735 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); 741 742 // Now allow the migration. 743 wallet_.set_reject_local_folders(false); 744 745 { 746 NativeBackendKWalletStub backend(42, profile_.GetPrefs()); 747 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_)); 748 749 // Trigger the migration by looking something up. 750 std::vector<PasswordForm*> form_list; 751 BrowserThread::PostTask( 752 BrowserThread::DB, FROM_HERE, 753 base::Bind( 754 base::IgnoreResult( 755 &NativeBackendKWalletStub::GetAutofillableLogins), 756 base::Unretained(&backend), &form_list)); 757 758 RunDBThread(); 759 760 // Quick check that we got something back. 761 EXPECT_EQ(1u, form_list.size()); 762 STLDeleteElements(&form_list); 763 } 764 765 CheckPasswordForms("Chrome Form Data", expected); 766 CheckPasswordForms("Chrome Form Data (42)", expected); 767 768 // Check that we have set the persistent preference. 769 EXPECT_TRUE( 770 profile_.GetPrefs()->GetBoolean(prefs::kPasswordsUseLocalProfileId)); 771 } 772 773 TEST_F(NativeBackendKWalletTest, DISABLED_MigrateToMultipleProfiles) { 774 // Reject attempts to migrate so we can populate the store. 775 wallet_.set_reject_local_folders(true); 776 777 { 778 NativeBackendKWalletStub backend(42, profile_.GetPrefs()); 779 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_)); 780 781 BrowserThread::PostTask( 782 BrowserThread::DB, FROM_HERE, 783 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin), 784 base::Unretained(&backend), form_google_)); 785 786 RunDBThread(); 787 } 788 789 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data (42)")); 790 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); 796 797 // Now allow the migration. 798 wallet_.set_reject_local_folders(false); 799 800 { 801 NativeBackendKWalletStub backend(42, profile_.GetPrefs()); 802 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_)); 803 804 // Trigger the migration by looking something up. 805 std::vector<PasswordForm*> form_list; 806 BrowserThread::PostTask( 807 BrowserThread::DB, FROM_HERE, 808 base::Bind( 809 base::IgnoreResult( 810 &NativeBackendKWalletStub::GetAutofillableLogins), 811 base::Unretained(&backend), &form_list)); 812 813 RunDBThread(); 814 815 // Quick check that we got something back. 816 EXPECT_EQ(1u, form_list.size()); 817 STLDeleteElements(&form_list); 818 } 819 820 CheckPasswordForms("Chrome Form Data", expected); 821 CheckPasswordForms("Chrome Form Data (42)", expected); 822 823 // Check that we have set the persistent preference. 824 EXPECT_TRUE( 825 profile_.GetPrefs()->GetBoolean(prefs::kPasswordsUseLocalProfileId)); 826 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); 830 831 { 832 NativeBackendKWalletStub backend(24, profile_.GetPrefs()); 833 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_)); 834 835 // Trigger the migration by looking something up. 836 std::vector<PasswordForm*> form_list; 837 BrowserThread::PostTask( 838 BrowserThread::DB, FROM_HERE, 839 base::Bind( 840 base::IgnoreResult( 841 &NativeBackendKWalletStub::GetAutofillableLogins), 842 base::Unretained(&backend), &form_list)); 843 844 RunDBThread(); 845 846 // Quick check that we got something back. 847 EXPECT_EQ(1u, form_list.size()); 848 STLDeleteElements(&form_list); 849 } 850 851 CheckPasswordForms("Chrome Form Data", expected); 852 CheckPasswordForms("Chrome Form Data (42)", expected); 853 CheckPasswordForms("Chrome Form Data (24)", expected); 854 } 855 856 TEST_F(NativeBackendKWalletTest, DISABLED_NoMigrationWithPrefSet) { 857 // Reject attempts to migrate so we can populate the store. 858 wallet_.set_reject_local_folders(true); 859 860 { 861 NativeBackendKWalletStub backend(42, profile_.GetPrefs()); 862 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_)); 863 864 BrowserThread::PostTask( 865 BrowserThread::DB, FROM_HERE, 866 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin), 867 base::Unretained(&backend), form_google_)); 868 869 RunDBThread(); 870 } 871 872 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data (42)")); 873 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); 879 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); 883 884 { 885 NativeBackendKWalletStub backend(42, profile_.GetPrefs()); 886 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_)); 887 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_)); 893 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, 898 base::Bind( 899 base::IgnoreResult( 900 &NativeBackendKWalletStub::GetAutofillableLogins), 901 base::Unretained(&backend), &form_list)); 902 903 RunDBThread(); 904 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); 910 } 911 912 CheckPasswordForms("Chrome Form Data", expected); 913 914 forms[0] = &form_isc_; 915 expected.clear(); 916 expected.push_back(make_pair(std::string(form_isc_.signon_realm), forms)); 917 CheckPasswordForms("Chrome Form Data (42)", expected); 918 } 919 920 TEST_F(NativeBackendKWalletTest, DISABLED_DeleteMigratedPasswordIsIsolated) { 921 // Reject attempts to migrate so we can populate the store. 922 wallet_.set_reject_local_folders(true); 923 924 { 925 NativeBackendKWalletStub backend(42, profile_.GetPrefs()); 926 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_)); 927 928 BrowserThread::PostTask( 929 BrowserThread::DB, FROM_HERE, 930 base::Bind( 931 base::IgnoreResult(&NativeBackendKWalletStub::AddLogin), 932 base::Unretained(&backend), form_google_)); 933 934 RunDBThread(); 935 } 936 937 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data (42)")); 938 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); 944 945 // Now allow the migration. 946 wallet_.set_reject_local_folders(false); 947 948 { 949 NativeBackendKWalletStub backend(42, profile_.GetPrefs()); 950 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_)); 951 952 // Trigger the migration by looking something up. 953 std::vector<PasswordForm*> form_list; 954 BrowserThread::PostTask( 955 BrowserThread::DB, FROM_HERE, 956 base::Bind( 957 base::IgnoreResult( 958 &NativeBackendKWalletStub::GetAutofillableLogins), 959 base::Unretained(&backend), &form_list)); 960 961 RunDBThread(); 962 963 // Quick check that we got something back. 964 EXPECT_EQ(1u, form_list.size()); 965 STLDeleteElements(&form_list); 966 } 967 968 CheckPasswordForms("Chrome Form Data", expected); 969 CheckPasswordForms("Chrome Form Data (42)", expected); 970 971 // Check that we have set the persistent preference. 972 EXPECT_TRUE( 973 profile_.GetPrefs()->GetBoolean(prefs::kPasswordsUseLocalProfileId)); 974 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); 978 979 { 980 NativeBackendKWalletStub backend(24, profile_.GetPrefs()); 981 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_)); 982 983 // Trigger the migration by looking something up. 984 std::vector<PasswordForm*> form_list; 985 BrowserThread::PostTask( 986 BrowserThread::DB, FROM_HERE, 987 base::Bind( 988 base::IgnoreResult( 989 &NativeBackendKWalletStub::GetAutofillableLogins), 990 base::Unretained(&backend), &form_list)); 991 992 RunDBThread(); 993 994 // Quick check that we got something back. 995 EXPECT_EQ(1u, form_list.size()); 996 STLDeleteElements(&form_list); 997 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); 1002 1003 // Now delete the password from this second profile. 1004 BrowserThread::PostTask( 1005 BrowserThread::DB, FROM_HERE, 1006 base::Bind( 1007 base::IgnoreResult(&NativeBackendKWalletStub::RemoveLogin), 1008 base::Unretained(&backend), form_google_)); 1009 1010 RunDBThread(); 1011 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); 1015 expected.clear(); 1016 CheckPasswordForms("Chrome Form Data (24)", expected); 1017 } 1018 } 1019 1020 class NativeBackendKWalletPickleTest : public NativeBackendKWalletTestBase { 1021 protected: 1022 void CreateVersion0Pickle(bool size_32, 1023 const PasswordForm& form, 1024 Pickle* pickle); 1025 void CheckVersion0Pickle(bool size_32, PasswordForm::Scheme scheme); 1026 }; 1027 1028 void NativeBackendKWalletPickleTest::CreateVersion0Pickle( 1029 bool size_32, const PasswordForm& form, Pickle* pickle) { 1030 const int kPickleVersion0 = 0; 1031 pickle->WriteInt(kPickleVersion0); 1032 if (size_32) 1033 pickle->WriteUInt32(1); // Size of form list. 32 bits. 1034 else 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()); 1048 } 1049 1050 void NativeBackendKWalletPickleTest::CheckVersion0Pickle( 1051 bool size_32, PasswordForm::Scheme scheme) { 1052 Pickle pickle; 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); 1063 } 1064 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. 1072 1073 TEST_F(NativeBackendKWalletPickleTest, ReadsOld32BitHTMLPickles) { 1074 CheckVersion0Pickle(true, PasswordForm::SCHEME_HTML); 1075 } 1076 1077 TEST_F(NativeBackendKWalletPickleTest, ReadsOld32BitHTTPPickles) { 1078 CheckVersion0Pickle(true, PasswordForm::SCHEME_BASIC); 1079 } 1080 1081 TEST_F(NativeBackendKWalletPickleTest, ReadsOld64BitHTMLPickles) { 1082 CheckVersion0Pickle(false, PasswordForm::SCHEME_HTML); 1083 } 1084 1085 TEST_F(NativeBackendKWalletPickleTest, ReadsOld64BitHTTPPickles) { 1086 CheckVersion0Pickle(false, PasswordForm::SCHEME_BASIC); 1087 } 1088