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/test/base/testing_profile.h" 18 #include "components/autofill/core/common/password_form.h" 19 #include "components/password_manager/core/common/password_manager_pref_names.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" 28 29 using autofill::PasswordForm; 30 using base::UTF8ToUTF16; 31 using content::BrowserThread; 32 using password_manager::PasswordStoreChange; 33 using password_manager::PasswordStoreChangeList; 34 using testing::_; 35 using testing::Invoke; 36 using testing::Return; 37 38 namespace { 39 40 // This class implements a very simple version of KWallet in memory. 41 // We only provide the parts we actually use; the real version has more. 42 class TestKWallet { 43 public: 44 typedef std::basic_string<uint8_t> Blob; // std::string is binary-safe. 45 46 TestKWallet() : reject_local_folders_(false) {} 47 48 void set_reject_local_folders(bool value) { reject_local_folders_ = value; } 49 50 // NOTE: The method names here are the same as the corresponding DBus 51 // methods, and therefore have names that don't match our style guide. 52 53 // Check for presence of a given password folder. 54 bool hasFolder(const std::string& folder) const { 55 return data_.find(folder) != data_.end(); 56 } 57 58 // Check for presence of a given password in a given password folder. 59 bool hasEntry(const std::string& folder, const std::string& key) const { 60 Data::const_iterator it = data_.find(folder); 61 return it != data_.end() && it->second.find(key) != it->second.end(); 62 } 63 64 // Get a list of password keys in a given password folder. 65 bool entryList(const std::string& folder, 66 std::vector<std::string>* entries) const { 67 Data::const_iterator it = data_.find(folder); 68 if (it == data_.end()) return false; 69 for (Folder::const_iterator fit = it->second.begin(); 70 fit != it->second.end(); ++fit) 71 entries->push_back(fit->first); 72 return true; 73 } 74 75 // Read the password data for a given password in a given password folder. 76 bool readEntry(const std::string& folder, const std::string& key, 77 Blob* value) const { 78 Data::const_iterator it = data_.find(folder); 79 if (it == data_.end()) return false; 80 Folder::const_iterator fit = it->second.find(key); 81 if (fit == it->second.end()) return false; 82 *value = fit->second; 83 return true; 84 } 85 86 // Create the given password folder. 87 bool createFolder(const std::string& folder) { 88 if (reject_local_folders_ && folder.find('(') != std::string::npos) 89 return false; 90 return data_.insert(make_pair(folder, Folder())).second; 91 } 92 93 // Remove the given password from the given password folder. 94 bool removeEntry(const std::string& folder, const std::string& key) { 95 Data::iterator it = data_.find(folder); 96 if (it == data_.end()) return false; 97 return it->second.erase(key) > 0; 98 } 99 100 // Write the given password data to the given password folder. 101 bool writeEntry(const std::string& folder, const std::string& key, 102 const Blob& value) { 103 Data::iterator it = data_.find(folder); 104 if (it == data_.end()) return false; 105 it->second[key] = value; 106 return true; 107 } 108 109 private: 110 typedef std::map<std::string, Blob> Folder; 111 typedef std::map<std::string, Folder> Data; 112 113 Data data_; 114 // "Local" folders are folders containing local profile IDs in their names. We 115 // can reject attempts to create them in order to make it easier to create 116 // legacy shared passwords in these tests, for testing the migration code. 117 bool reject_local_folders_; 118 119 // No need to disallow copy and assign. This class is safe to copy and assign. 120 }; 121 122 } // anonymous namespace 123 124 // Obscure magic: we need to declare storage for this constant because we use it 125 // in ways that require its address in this test, but not in the actual code. 126 const int NativeBackendKWallet::kInvalidKWalletHandle; 127 128 // Subclass NativeBackendKWallet to promote some members to public for testing. 129 class NativeBackendKWalletStub : public NativeBackendKWallet { 130 public: 131 explicit NativeBackendKWalletStub(LocalProfileId id) 132 : NativeBackendKWallet(id) { 133 } 134 using NativeBackendKWallet::InitWithBus; 135 using NativeBackendKWallet::kInvalidKWalletHandle; 136 using NativeBackendKWallet::DeserializeValue; 137 }; 138 139 // Provide some test forms to avoid having to set them up in each test. 140 class NativeBackendKWalletTestBase : public testing::Test { 141 protected: 142 NativeBackendKWalletTestBase() { 143 old_form_google_.origin = GURL("http://www.google.com/"); 144 old_form_google_.action = GURL("http://www.google.com/login"); 145 old_form_google_.username_element = UTF8ToUTF16("user"); 146 old_form_google_.username_value = UTF8ToUTF16("joeschmoe"); 147 old_form_google_.password_element = UTF8ToUTF16("pass"); 148 old_form_google_.password_value = UTF8ToUTF16("seekrit"); 149 old_form_google_.submit_element = UTF8ToUTF16("submit"); 150 old_form_google_.signon_realm = "Google"; 151 152 form_google_ = old_form_google_; 153 form_google_.times_used = 3; 154 form_google_.type = PasswordForm::TYPE_GENERATED; 155 form_google_.form_data.name = UTF8ToUTF16("form_name"); 156 form_google_.form_data.user_submitted = true; 157 form_google_.date_synced = base::Time::Now(); 158 159 form_isc_.origin = GURL("http://www.isc.org/"); 160 form_isc_.action = GURL("http://www.isc.org/auth"); 161 form_isc_.username_element = UTF8ToUTF16("id"); 162 form_isc_.username_value = UTF8ToUTF16("janedoe"); 163 form_isc_.password_element = UTF8ToUTF16("passwd"); 164 form_isc_.password_value = UTF8ToUTF16("ihazabukkit"); 165 form_isc_.submit_element = UTF8ToUTF16("login"); 166 form_isc_.signon_realm = "ISC"; 167 form_isc_.date_synced = base::Time::Now(); 168 } 169 170 static void CheckPasswordForm(const PasswordForm& expected, 171 const PasswordForm& actual); 172 static void CheckPasswordChanges(const PasswordStoreChangeList& expected, 173 const PasswordStoreChangeList& actual); 174 static void CheckPasswordChangesWithResult( 175 const PasswordStoreChangeList* expected, 176 const PasswordStoreChangeList* actual, 177 bool result); 178 179 PasswordForm old_form_google_; 180 PasswordForm form_google_; 181 PasswordForm form_isc_; 182 }; 183 184 // static 185 void NativeBackendKWalletTestBase::CheckPasswordForm( 186 const PasswordForm& expected, const PasswordForm& actual) { 187 EXPECT_EQ(expected.origin, actual.origin); 188 EXPECT_EQ(expected.password_value, actual.password_value); 189 EXPECT_EQ(expected.action, actual.action); 190 EXPECT_EQ(expected.username_element, actual.username_element); 191 EXPECT_EQ(expected.username_value, actual.username_value); 192 EXPECT_EQ(expected.password_element, actual.password_element); 193 EXPECT_EQ(expected.submit_element, actual.submit_element); 194 EXPECT_EQ(expected.signon_realm, actual.signon_realm); 195 EXPECT_EQ(expected.ssl_valid, actual.ssl_valid); 196 EXPECT_EQ(expected.preferred, actual.preferred); 197 // We don't check the date created. It varies. 198 EXPECT_EQ(expected.blacklisted_by_user, actual.blacklisted_by_user); 199 EXPECT_EQ(expected.type, actual.type); 200 EXPECT_EQ(expected.times_used, actual.times_used); 201 EXPECT_EQ(expected.scheme, actual.scheme); 202 EXPECT_EQ(expected.date_synced, actual.date_synced); 203 } 204 205 // static 206 void NativeBackendKWalletTestBase::CheckPasswordChanges( 207 const PasswordStoreChangeList& expected, 208 const PasswordStoreChangeList& actual) { 209 ASSERT_EQ(expected.size(), actual.size()); 210 for (size_t i = 0; i < expected.size(); ++i) { 211 EXPECT_EQ(expected[i].type(), actual[i].type()); 212 CheckPasswordForm(expected[i].form(), actual[i].form()); 213 } 214 } 215 216 // static 217 void NativeBackendKWalletTestBase::CheckPasswordChangesWithResult( 218 const PasswordStoreChangeList* expected, 219 const PasswordStoreChangeList* actual, 220 bool result) { 221 EXPECT_TRUE(result); 222 CheckPasswordChanges(*expected, *actual); 223 } 224 225 class NativeBackendKWalletTest : public NativeBackendKWalletTestBase { 226 protected: 227 NativeBackendKWalletTest() 228 : ui_thread_(BrowserThread::UI, &message_loop_), 229 db_thread_(BrowserThread::DB), klauncher_ret_(0), 230 klauncher_contacted_(false), kwallet_runnable_(true), 231 kwallet_running_(true), kwallet_enabled_(true) { 232 } 233 234 virtual void SetUp(); 235 virtual void TearDown(); 236 237 // Let the DB thread run to completion of all current tasks. 238 void RunDBThread() { 239 base::WaitableEvent event(false, false); 240 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, 241 base::Bind(ThreadDone, &event)); 242 event.Wait(); 243 // Some of the tests may post messages to the UI thread, but we don't need 244 // to run those until after the DB thread is finished. So run it here. 245 message_loop_.RunUntilIdle(); 246 } 247 static void ThreadDone(base::WaitableEvent* event) { 248 event->Signal(); 249 } 250 251 // Utilities to help verify sets of expectations. 252 typedef std::vector< 253 std::pair<std::string, 254 std::vector<const PasswordForm*> > > ExpectationArray; 255 void CheckPasswordForms(const std::string& folder, 256 const ExpectationArray& sorted_expected); 257 258 enum RemoveBetweenMethod { 259 CREATED, 260 SYNCED, 261 }; 262 263 // Tests RemoveLoginsCreatedBetween or RemoveLoginsSyncedBetween. 264 void TestRemoveLoginsBetween(RemoveBetweenMethod date_to_test); 265 266 base::MessageLoopForUI message_loop_; 267 content::TestBrowserThread ui_thread_; 268 content::TestBrowserThread db_thread_; 269 270 scoped_refptr<dbus::MockBus> mock_session_bus_; 271 scoped_refptr<dbus::MockObjectProxy> mock_klauncher_proxy_; 272 scoped_refptr<dbus::MockObjectProxy> mock_kwallet_proxy_; 273 274 int klauncher_ret_; 275 std::string klauncher_error_; 276 bool klauncher_contacted_; 277 278 bool kwallet_runnable_; 279 bool kwallet_running_; 280 bool kwallet_enabled_; 281 282 TestKWallet wallet_; 283 284 private: 285 dbus::Response* KLauncherMethodCall( 286 dbus::MethodCall* method_call, testing::Unused); 287 288 dbus::Response* KWalletMethodCall( 289 dbus::MethodCall* method_call, testing::Unused); 290 }; 291 292 void NativeBackendKWalletTest::SetUp() { 293 ASSERT_TRUE(db_thread_.Start()); 294 295 dbus::Bus::Options options; 296 options.bus_type = dbus::Bus::SESSION; 297 mock_session_bus_ = new dbus::MockBus(options); 298 299 mock_klauncher_proxy_ = 300 new dbus::MockObjectProxy(mock_session_bus_.get(), 301 "org.kde.klauncher", 302 dbus::ObjectPath("/KLauncher")); 303 EXPECT_CALL(*mock_klauncher_proxy_.get(), MockCallMethodAndBlock(_, _)) 304 .WillRepeatedly( 305 Invoke(this, &NativeBackendKWalletTest::KLauncherMethodCall)); 306 307 mock_kwallet_proxy_ = 308 new dbus::MockObjectProxy(mock_session_bus_.get(), 309 "org.kde.kwalletd", 310 dbus::ObjectPath("/modules/kwalletd")); 311 EXPECT_CALL(*mock_kwallet_proxy_.get(), MockCallMethodAndBlock(_, _)) 312 .WillRepeatedly( 313 Invoke(this, &NativeBackendKWalletTest::KWalletMethodCall)); 314 315 EXPECT_CALL( 316 *mock_session_bus_.get(), 317 GetObjectProxy("org.kde.klauncher", dbus::ObjectPath("/KLauncher"))) 318 .WillRepeatedly(Return(mock_klauncher_proxy_.get())); 319 EXPECT_CALL( 320 *mock_session_bus_.get(), 321 GetObjectProxy("org.kde.kwalletd", dbus::ObjectPath("/modules/kwalletd"))) 322 .WillRepeatedly(Return(mock_kwallet_proxy_.get())); 323 324 EXPECT_CALL(*mock_session_bus_.get(), ShutdownAndBlock()).WillOnce(Return()) 325 .WillRepeatedly(Return()); 326 } 327 328 void NativeBackendKWalletTest::TearDown() { 329 base::MessageLoop::current()->PostTask(FROM_HERE, 330 base::MessageLoop::QuitClosure()); 331 base::MessageLoop::current()->Run(); 332 db_thread_.Stop(); 333 } 334 335 void NativeBackendKWalletTest::TestRemoveLoginsBetween( 336 RemoveBetweenMethod date_to_test) { 337 NativeBackendKWalletStub backend(42); 338 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_)); 339 340 form_google_.date_synced = base::Time(); 341 form_isc_.date_synced = base::Time(); 342 form_google_.date_created = base::Time(); 343 form_isc_.date_created = base::Time(); 344 base::Time now = base::Time::Now(); 345 base::Time next_day = now + base::TimeDelta::FromDays(1); 346 if (date_to_test == CREATED) { 347 // crbug/374132. Remove the next line once it's fixed. 348 next_day = base::Time::FromTimeT(next_day.ToTimeT()); 349 form_google_.date_created = now; 350 form_isc_.date_created = next_day; 351 } else { 352 form_google_.date_synced = now; 353 form_isc_.date_synced = next_day; 354 } 355 356 BrowserThread::PostTask( 357 BrowserThread::DB, 358 FROM_HERE, 359 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin), 360 base::Unretained(&backend), 361 form_google_)); 362 BrowserThread::PostTask( 363 BrowserThread::DB, 364 FROM_HERE, 365 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin), 366 base::Unretained(&backend), 367 form_isc_)); 368 369 PasswordStoreChangeList expected_changes; 370 expected_changes.push_back( 371 PasswordStoreChange(PasswordStoreChange::REMOVE, form_google_)); 372 PasswordStoreChangeList changes; 373 bool (NativeBackendKWallet::*method)( 374 base::Time, base::Time, password_manager::PasswordStoreChangeList*) = 375 date_to_test == CREATED 376 ? &NativeBackendKWalletStub::RemoveLoginsCreatedBetween 377 : &NativeBackendKWalletStub::RemoveLoginsSyncedBetween; 378 BrowserThread::PostTaskAndReplyWithResult( 379 BrowserThread::DB, 380 FROM_HERE, 381 base::Bind( 382 method, base::Unretained(&backend), base::Time(), next_day, &changes), 383 base::Bind(&NativeBackendKWalletTest::CheckPasswordChangesWithResult, 384 &expected_changes, 385 &changes)); 386 RunDBThread(); 387 388 std::vector<const PasswordForm*> forms; 389 forms.push_back(&form_isc_); 390 ExpectationArray expected; 391 expected.push_back(make_pair(std::string(form_isc_.signon_realm), forms)); 392 CheckPasswordForms("Chrome Form Data (42)", expected); 393 394 // Remove form_isc_. 395 expected_changes.clear(); 396 expected_changes.push_back( 397 PasswordStoreChange(PasswordStoreChange::REMOVE, form_isc_)); 398 BrowserThread::PostTaskAndReplyWithResult( 399 BrowserThread::DB, 400 FROM_HERE, 401 base::Bind( 402 method, base::Unretained(&backend), next_day, base::Time(), &changes), 403 base::Bind(&NativeBackendKWalletTest::CheckPasswordChangesWithResult, 404 &expected_changes, 405 &changes)); 406 RunDBThread(); 407 408 CheckPasswordForms("Chrome Form Data (42)", ExpectationArray()); 409 } 410 411 dbus::Response* NativeBackendKWalletTest::KLauncherMethodCall( 412 dbus::MethodCall* method_call, testing::Unused) { 413 EXPECT_EQ("org.kde.KLauncher", method_call->GetInterface()); 414 EXPECT_EQ("start_service_by_desktop_name", method_call->GetMember()); 415 416 klauncher_contacted_ = true; 417 418 dbus::MessageReader reader(method_call); 419 std::string service_name; 420 std::vector<std::string> urls; 421 std::vector<std::string> envs; 422 std::string startup_id; 423 bool blind = false; 424 425 EXPECT_TRUE(reader.PopString(&service_name)); 426 EXPECT_TRUE(reader.PopArrayOfStrings(&urls)); 427 EXPECT_TRUE(reader.PopArrayOfStrings(&envs)); 428 EXPECT_TRUE(reader.PopString(&startup_id)); 429 EXPECT_TRUE(reader.PopBool(&blind)); 430 431 EXPECT_EQ("kwalletd", service_name); 432 EXPECT_TRUE(urls.empty()); 433 EXPECT_TRUE(envs.empty()); 434 EXPECT_TRUE(startup_id.empty()); 435 EXPECT_FALSE(blind); 436 437 if (kwallet_runnable_) 438 kwallet_running_ = true; 439 440 scoped_ptr<dbus::Response> response(dbus::Response::CreateEmpty()); 441 dbus::MessageWriter writer(response.get()); 442 writer.AppendInt32(klauncher_ret_); 443 writer.AppendString(std::string()); // dbus_name 444 writer.AppendString(klauncher_error_); 445 writer.AppendInt32(1234); // pid 446 return response.release(); 447 } 448 449 dbus::Response* NativeBackendKWalletTest::KWalletMethodCall( 450 dbus::MethodCall* method_call, testing::Unused) { 451 if (!kwallet_running_) 452 return NULL; 453 EXPECT_EQ("org.kde.KWallet", method_call->GetInterface()); 454 455 scoped_ptr<dbus::Response> response; 456 if (method_call->GetMember() == "isEnabled") { 457 response = dbus::Response::CreateEmpty(); 458 dbus::MessageWriter writer(response.get()); 459 writer.AppendBool(kwallet_enabled_); 460 } else if (method_call->GetMember() == "networkWallet") { 461 response = dbus::Response::CreateEmpty(); 462 dbus::MessageWriter writer(response.get()); 463 writer.AppendString("test_wallet"); // Should match |open| below. 464 } else if (method_call->GetMember() == "open") { 465 dbus::MessageReader reader(method_call); 466 std::string wallet_name; 467 int64_t wallet_id; 468 std::string app_name; 469 EXPECT_TRUE(reader.PopString(&wallet_name)); 470 EXPECT_TRUE(reader.PopInt64(&wallet_id)); 471 EXPECT_TRUE(reader.PopString(&app_name)); 472 EXPECT_EQ("test_wallet", wallet_name); // Should match |networkWallet|. 473 response = dbus::Response::CreateEmpty(); 474 dbus::MessageWriter writer(response.get()); 475 writer.AppendInt32(1); // Can be anything but kInvalidKWalletHandle. 476 } else if (method_call->GetMember() == "hasFolder" || 477 method_call->GetMember() == "createFolder") { 478 dbus::MessageReader reader(method_call); 479 int handle = NativeBackendKWalletStub::kInvalidKWalletHandle; 480 std::string folder_name; 481 std::string app_name; 482 EXPECT_TRUE(reader.PopInt32(&handle)); 483 EXPECT_TRUE(reader.PopString(&folder_name)); 484 EXPECT_TRUE(reader.PopString(&app_name)); 485 EXPECT_NE(NativeBackendKWalletStub::kInvalidKWalletHandle, handle); 486 response = dbus::Response::CreateEmpty(); 487 dbus::MessageWriter writer(response.get()); 488 if (method_call->GetMember() == "hasFolder") 489 writer.AppendBool(wallet_.hasFolder(folder_name)); 490 else 491 writer.AppendBool(wallet_.createFolder(folder_name)); 492 } else if (method_call->GetMember() == "hasEntry" || 493 method_call->GetMember() == "removeEntry") { 494 dbus::MessageReader reader(method_call); 495 int handle = NativeBackendKWalletStub::kInvalidKWalletHandle; 496 std::string folder_name; 497 std::string key; 498 std::string app_name; 499 EXPECT_TRUE(reader.PopInt32(&handle)); 500 EXPECT_TRUE(reader.PopString(&folder_name)); 501 EXPECT_TRUE(reader.PopString(&key)); 502 EXPECT_TRUE(reader.PopString(&app_name)); 503 EXPECT_NE(NativeBackendKWalletStub::kInvalidKWalletHandle, handle); 504 response = dbus::Response::CreateEmpty(); 505 dbus::MessageWriter writer(response.get()); 506 if (method_call->GetMember() == "hasEntry") 507 writer.AppendBool(wallet_.hasEntry(folder_name, key)); 508 else 509 writer.AppendInt32(wallet_.removeEntry(folder_name, key) ? 0 : 1); 510 } else if (method_call->GetMember() == "entryList") { 511 dbus::MessageReader reader(method_call); 512 int handle = NativeBackendKWalletStub::kInvalidKWalletHandle; 513 std::string folder_name; 514 std::string app_name; 515 EXPECT_TRUE(reader.PopInt32(&handle)); 516 EXPECT_TRUE(reader.PopString(&folder_name)); 517 EXPECT_TRUE(reader.PopString(&app_name)); 518 EXPECT_NE(NativeBackendKWalletStub::kInvalidKWalletHandle, handle); 519 std::vector<std::string> entries; 520 if (wallet_.entryList(folder_name, &entries)) { 521 response = dbus::Response::CreateEmpty(); 522 dbus::MessageWriter writer(response.get()); 523 writer.AppendArrayOfStrings(entries); 524 } 525 } else if (method_call->GetMember() == "readEntry") { 526 dbus::MessageReader reader(method_call); 527 int handle = NativeBackendKWalletStub::kInvalidKWalletHandle; 528 std::string folder_name; 529 std::string key; 530 std::string app_name; 531 EXPECT_TRUE(reader.PopInt32(&handle)); 532 EXPECT_TRUE(reader.PopString(&folder_name)); 533 EXPECT_TRUE(reader.PopString(&key)); 534 EXPECT_TRUE(reader.PopString(&app_name)); 535 EXPECT_NE(NativeBackendKWalletStub::kInvalidKWalletHandle, handle); 536 TestKWallet::Blob value; 537 if (wallet_.readEntry(folder_name, key, &value)) { 538 response = dbus::Response::CreateEmpty(); 539 dbus::MessageWriter writer(response.get()); 540 writer.AppendArrayOfBytes(value.data(), value.size()); 541 } 542 } else if (method_call->GetMember() == "writeEntry") { 543 dbus::MessageReader reader(method_call); 544 int handle = NativeBackendKWalletStub::kInvalidKWalletHandle; 545 std::string folder_name; 546 std::string key; 547 const uint8_t* bytes = NULL; 548 size_t length = 0; 549 std::string app_name; 550 EXPECT_TRUE(reader.PopInt32(&handle)); 551 EXPECT_TRUE(reader.PopString(&folder_name)); 552 EXPECT_TRUE(reader.PopString(&key)); 553 EXPECT_TRUE(reader.PopArrayOfBytes(&bytes, &length)); 554 EXPECT_TRUE(reader.PopString(&app_name)); 555 EXPECT_NE(NativeBackendKWalletStub::kInvalidKWalletHandle, handle); 556 response = dbus::Response::CreateEmpty(); 557 dbus::MessageWriter writer(response.get()); 558 writer.AppendInt32( 559 wallet_.writeEntry(folder_name, key, 560 TestKWallet::Blob(bytes, length)) ? 0 : 1); 561 } 562 563 EXPECT_FALSE(response.get() == NULL); 564 return response.release(); 565 } 566 567 void NativeBackendKWalletTest::CheckPasswordForms( 568 const std::string& folder, const ExpectationArray& sorted_expected) { 569 EXPECT_TRUE(wallet_.hasFolder(folder)); 570 std::vector<std::string> entries; 571 EXPECT_TRUE(wallet_.entryList(folder, &entries)); 572 EXPECT_EQ(sorted_expected.size(), entries.size()); 573 std::sort(entries.begin(), entries.end()); 574 for (size_t i = 0; i < entries.size() && i < sorted_expected.size(); ++i) { 575 EXPECT_EQ(sorted_expected[i].first, entries[i]); 576 TestKWallet::Blob value; 577 EXPECT_TRUE(wallet_.readEntry(folder, entries[i], &value)); 578 Pickle pickle(reinterpret_cast<const char*>(value.data()), value.size()); 579 std::vector<PasswordForm*> forms; 580 NativeBackendKWalletStub::DeserializeValue(entries[i], pickle, &forms); 581 const std::vector<const PasswordForm*>& expect = sorted_expected[i].second; 582 EXPECT_EQ(expect.size(), forms.size()); 583 for (size_t j = 0; j < forms.size() && j < expect.size(); ++j) 584 CheckPasswordForm(*expect[j], *forms[j]); 585 STLDeleteElements(&forms); 586 } 587 } 588 589 TEST_F(NativeBackendKWalletTest, NotEnabled) { 590 NativeBackendKWalletStub kwallet(42); 591 kwallet_enabled_ = false; 592 EXPECT_FALSE(kwallet.InitWithBus(mock_session_bus_)); 593 EXPECT_FALSE(klauncher_contacted_); 594 } 595 596 TEST_F(NativeBackendKWalletTest, NotRunnable) { 597 NativeBackendKWalletStub kwallet(42); 598 kwallet_runnable_ = false; 599 kwallet_running_ = false; 600 EXPECT_FALSE(kwallet.InitWithBus(mock_session_bus_)); 601 EXPECT_TRUE(klauncher_contacted_); 602 } 603 604 TEST_F(NativeBackendKWalletTest, NotRunningOrEnabled) { 605 NativeBackendKWalletStub kwallet(42); 606 kwallet_running_ = false; 607 kwallet_enabled_ = false; 608 EXPECT_FALSE(kwallet.InitWithBus(mock_session_bus_)); 609 EXPECT_TRUE(klauncher_contacted_); 610 } 611 612 TEST_F(NativeBackendKWalletTest, NotRunning) { 613 NativeBackendKWalletStub kwallet(42); 614 kwallet_running_ = false; 615 EXPECT_TRUE(kwallet.InitWithBus(mock_session_bus_)); 616 EXPECT_TRUE(klauncher_contacted_); 617 } 618 619 TEST_F(NativeBackendKWalletTest, BasicStartup) { 620 NativeBackendKWalletStub kwallet(42); 621 EXPECT_TRUE(kwallet.InitWithBus(mock_session_bus_)); 622 EXPECT_FALSE(klauncher_contacted_); 623 } 624 625 TEST_F(NativeBackendKWalletTest, BasicAddLogin) { 626 NativeBackendKWalletStub backend(42); 627 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_)); 628 629 BrowserThread::PostTask( 630 BrowserThread::DB, FROM_HERE, 631 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin), 632 base::Unretained(&backend), form_google_)); 633 634 RunDBThread(); 635 636 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data")); 637 638 std::vector<const PasswordForm*> forms; 639 forms.push_back(&form_google_); 640 ExpectationArray expected; 641 expected.push_back(make_pair(std::string(form_google_.signon_realm), forms)); 642 CheckPasswordForms("Chrome Form Data (42)", expected); 643 } 644 645 TEST_F(NativeBackendKWalletTest, BasicUpdateLogin) { 646 NativeBackendKWalletStub backend(42); 647 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_)); 648 649 BrowserThread::PostTask( 650 BrowserThread::DB, FROM_HERE, 651 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin), 652 base::Unretained(&backend), form_google_)); 653 654 RunDBThread(); 655 656 PasswordForm new_form_google(form_google_); 657 new_form_google.times_used = 10; 658 new_form_google.action = GURL("http://www.google.com/different/login"); 659 660 // Update login 661 PasswordStoreChangeList changes; 662 BrowserThread::PostTask( 663 BrowserThread::DB, FROM_HERE, 664 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::UpdateLogin), 665 base::Unretained(&backend), 666 new_form_google, 667 base::Unretained(&changes))); 668 RunDBThread(); 669 670 ASSERT_EQ(1u, changes.size()); 671 EXPECT_EQ(PasswordStoreChange::UPDATE, changes.front().type()); 672 EXPECT_EQ(new_form_google, changes.front().form()); 673 674 std::vector<const PasswordForm*> forms; 675 forms.push_back(&new_form_google); 676 ExpectationArray expected; 677 expected.push_back(make_pair(std::string(form_google_.signon_realm), forms)); 678 CheckPasswordForms("Chrome Form Data (42)", expected); 679 } 680 681 TEST_F(NativeBackendKWalletTest, BasicListLogins) { 682 NativeBackendKWalletStub backend(42); 683 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_)); 684 685 BrowserThread::PostTask( 686 BrowserThread::DB, FROM_HERE, 687 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin), 688 base::Unretained(&backend), form_google_)); 689 690 std::vector<PasswordForm*> form_list; 691 BrowserThread::PostTask( 692 BrowserThread::DB, FROM_HERE, 693 base::Bind( 694 base::IgnoreResult(&NativeBackendKWalletStub::GetAutofillableLogins), 695 base::Unretained(&backend), &form_list)); 696 697 RunDBThread(); 698 699 // Quick check that we got something back. 700 EXPECT_EQ(1u, form_list.size()); 701 STLDeleteElements(&form_list); 702 703 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data")); 704 705 std::vector<const PasswordForm*> forms; 706 forms.push_back(&form_google_); 707 ExpectationArray expected; 708 expected.push_back(make_pair(std::string(form_google_.signon_realm), forms)); 709 CheckPasswordForms("Chrome Form Data (42)", expected); 710 } 711 712 TEST_F(NativeBackendKWalletTest, BasicRemoveLogin) { 713 NativeBackendKWalletStub backend(42); 714 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_)); 715 716 BrowserThread::PostTask( 717 BrowserThread::DB, FROM_HERE, 718 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin), 719 base::Unretained(&backend), form_google_)); 720 721 RunDBThread(); 722 723 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data")); 724 725 std::vector<const PasswordForm*> forms; 726 forms.push_back(&form_google_); 727 ExpectationArray expected; 728 expected.push_back(make_pair(std::string(form_google_.signon_realm), forms)); 729 CheckPasswordForms("Chrome Form Data (42)", expected); 730 731 BrowserThread::PostTask( 732 BrowserThread::DB, FROM_HERE, 733 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::RemoveLogin), 734 base::Unretained(&backend), form_google_)); 735 736 RunDBThread(); 737 738 expected.clear(); 739 CheckPasswordForms("Chrome Form Data (42)", expected); 740 } 741 742 TEST_F(NativeBackendKWalletTest, UpdateNonexistentLogin) { 743 NativeBackendKWalletStub backend(42); 744 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_)); 745 746 // First add an unrelated login. 747 BrowserThread::PostTask( 748 BrowserThread::DB, FROM_HERE, 749 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin), 750 base::Unretained(&backend), form_google_)); 751 752 RunDBThread(); 753 754 std::vector<const PasswordForm*> forms; 755 forms.push_back(&form_google_); 756 ExpectationArray expected; 757 expected.push_back(make_pair(std::string(form_google_.signon_realm), forms)); 758 CheckPasswordForms("Chrome Form Data (42)", expected); 759 760 // Attempt to update a login that doesn't exist. 761 PasswordStoreChangeList changes; 762 BrowserThread::PostTask( 763 BrowserThread::DB, FROM_HERE, 764 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::UpdateLogin), 765 base::Unretained(&backend), 766 form_isc_, 767 base::Unretained(&changes))); 768 769 RunDBThread(); 770 771 EXPECT_EQ(PasswordStoreChangeList(), changes); 772 CheckPasswordForms("Chrome Form Data (42)", expected); 773 } 774 775 TEST_F(NativeBackendKWalletTest, RemoveNonexistentLogin) { 776 NativeBackendKWalletStub backend(42); 777 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_)); 778 779 // First add an unrelated login. 780 BrowserThread::PostTask( 781 BrowserThread::DB, FROM_HERE, 782 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin), 783 base::Unretained(&backend), form_google_)); 784 785 RunDBThread(); 786 787 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data")); 788 789 std::vector<const PasswordForm*> forms; 790 forms.push_back(&form_google_); 791 ExpectationArray expected; 792 expected.push_back(make_pair(std::string(form_google_.signon_realm), forms)); 793 CheckPasswordForms("Chrome Form Data (42)", expected); 794 795 // Attempt to remove a login that doesn't exist. 796 BrowserThread::PostTask( 797 BrowserThread::DB, FROM_HERE, 798 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::RemoveLogin), 799 base::Unretained(&backend), form_isc_)); 800 801 // Make sure we can still get the first form back. 802 std::vector<PasswordForm*> form_list; 803 BrowserThread::PostTask( 804 BrowserThread::DB, FROM_HERE, 805 base::Bind( 806 base::IgnoreResult(&NativeBackendKWalletStub::GetAutofillableLogins), 807 base::Unretained(&backend), &form_list)); 808 809 RunDBThread(); 810 811 // Quick check that we got something back. 812 EXPECT_EQ(1u, form_list.size()); 813 STLDeleteElements(&form_list); 814 815 CheckPasswordForms("Chrome Form Data (42)", expected); 816 } 817 818 TEST_F(NativeBackendKWalletTest, AddDuplicateLogin) { 819 NativeBackendKWalletStub backend(42); 820 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_)); 821 822 PasswordStoreChangeList changes; 823 changes.push_back(PasswordStoreChange(PasswordStoreChange::ADD, 824 form_google_)); 825 BrowserThread::PostTaskAndReplyWithResult( 826 BrowserThread::DB, FROM_HERE, 827 base::Bind(&NativeBackendKWalletStub::AddLogin, 828 base::Unretained(&backend), form_google_), 829 base::Bind(&NativeBackendKWalletTest::CheckPasswordChanges, 830 changes)); 831 832 changes.clear(); 833 changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, 834 form_google_)); 835 form_google_.times_used++; 836 changes.push_back(PasswordStoreChange(PasswordStoreChange::ADD, 837 form_google_)); 838 839 BrowserThread::PostTaskAndReplyWithResult( 840 BrowserThread::DB, FROM_HERE, 841 base::Bind(&NativeBackendKWalletStub::AddLogin, 842 base::Unretained(&backend), form_google_), 843 base::Bind(&NativeBackendKWalletTest::CheckPasswordChanges, 844 changes)); 845 846 RunDBThread(); 847 848 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data")); 849 850 std::vector<const PasswordForm*> forms; 851 forms.push_back(&form_google_); 852 ExpectationArray expected; 853 expected.push_back(make_pair(std::string(form_google_.signon_realm), forms)); 854 CheckPasswordForms("Chrome Form Data (42)", expected); 855 } 856 857 TEST_F(NativeBackendKWalletTest, ListLoginsAppends) { 858 NativeBackendKWalletStub backend(42); 859 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_)); 860 861 BrowserThread::PostTask( 862 BrowserThread::DB, FROM_HERE, 863 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin), 864 base::Unretained(&backend), form_google_)); 865 866 // Send the same request twice with the same list both times. 867 std::vector<PasswordForm*> form_list; 868 BrowserThread::PostTask( 869 BrowserThread::DB, FROM_HERE, 870 base::Bind( 871 base::IgnoreResult(&NativeBackendKWalletStub::GetAutofillableLogins), 872 base::Unretained(&backend), &form_list)); 873 BrowserThread::PostTask( 874 BrowserThread::DB, FROM_HERE, 875 base::Bind( 876 base::IgnoreResult(&NativeBackendKWalletStub::GetAutofillableLogins), 877 base::Unretained(&backend), &form_list)); 878 879 RunDBThread(); 880 881 // Quick check that we got two results back. 882 EXPECT_EQ(2u, form_list.size()); 883 STLDeleteElements(&form_list); 884 885 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data")); 886 887 std::vector<const PasswordForm*> forms; 888 forms.push_back(&form_google_); 889 ExpectationArray expected; 890 expected.push_back(make_pair(std::string(form_google_.signon_realm), forms)); 891 CheckPasswordForms("Chrome Form Data (42)", expected); 892 } 893 894 TEST_F(NativeBackendKWalletTest, RemoveLoginsCreatedBetween) { 895 TestRemoveLoginsBetween(CREATED); 896 } 897 898 TEST_F(NativeBackendKWalletTest, RemoveLoginsSyncedBetween) { 899 TestRemoveLoginsBetween(SYNCED); 900 } 901 902 // TODO(mdm): add more basic tests here at some point. 903 // (For example tests for storing >1 password per realm pickle.) 904 905 class NativeBackendKWalletPickleTest : public NativeBackendKWalletTestBase { 906 protected: 907 void CreateVersion2Pickle(const PasswordForm& form, Pickle* pickle); 908 void CreateVersion1Pickle(const PasswordForm& form, Pickle* pickle); 909 void CreateVersion0Pickle(bool size_32, 910 const PasswordForm& form, 911 Pickle* pickle); 912 void CheckVersion2Pickle(); 913 void CheckVersion1Pickle(); 914 void CheckVersion0Pickle(bool size_32, PasswordForm::Scheme scheme); 915 916 private: 917 void CreatePickle(bool size_32, const PasswordForm& form, Pickle* pickle); 918 }; 919 920 void NativeBackendKWalletPickleTest::CreateVersion2Pickle( 921 const PasswordForm& form, Pickle* pickle) { 922 pickle->WriteInt(2); 923 CreatePickle(false, form, pickle); 924 pickle->WriteInt(form.type); 925 pickle->WriteInt(form.times_used); 926 autofill::SerializeFormData(form.form_data, pickle); 927 } 928 929 void NativeBackendKWalletPickleTest::CreateVersion1Pickle( 930 const PasswordForm& form, Pickle* pickle) { 931 pickle->WriteInt(1); 932 CreatePickle(false, form, pickle); 933 } 934 935 void NativeBackendKWalletPickleTest::CreateVersion0Pickle( 936 bool size_32, const PasswordForm& form, Pickle* pickle) { 937 pickle->WriteInt(0); 938 CreatePickle(size_32, form, pickle); 939 } 940 941 void NativeBackendKWalletPickleTest::CreatePickle( 942 bool size_32, const PasswordForm& form, Pickle* pickle) { 943 if (size_32) 944 pickle->WriteUInt32(1); // Size of form list. 32 bits. 945 else 946 pickle->WriteUInt64(1); // Size of form list. 64 bits. 947 pickle->WriteInt(form.scheme); 948 pickle->WriteString(form.origin.spec()); 949 pickle->WriteString(form.action.spec()); 950 pickle->WriteString16(form.username_element); 951 pickle->WriteString16(form.username_value); 952 pickle->WriteString16(form.password_element); 953 pickle->WriteString16(form.password_value); 954 pickle->WriteString16(form.submit_element); 955 pickle->WriteBool(form.ssl_valid); 956 pickle->WriteBool(form.preferred); 957 pickle->WriteBool(form.blacklisted_by_user); 958 pickle->WriteInt64(form.date_created.ToTimeT()); 959 } 960 961 void NativeBackendKWalletPickleTest::CheckVersion2Pickle() { 962 Pickle pickle; 963 PasswordForm form = form_google_; 964 form.date_synced = base::Time(); 965 CreateVersion2Pickle(form, &pickle); 966 967 std::vector<PasswordForm*> form_list; 968 NativeBackendKWalletStub::DeserializeValue(form.signon_realm, 969 pickle, &form_list); 970 971 // This will match |old_form_google_| because not all the fields present in 972 // |form_google_| will be deserialized. 973 EXPECT_EQ(1u, form_list.size()); 974 if (form_list.size() > 0) 975 CheckPasswordForm(form, *form_list[0]); 976 STLDeleteElements(&form_list); 977 } 978 979 // Make sure that we can still read version 1 pickles. 980 void NativeBackendKWalletPickleTest::CheckVersion1Pickle() { 981 Pickle pickle; 982 PasswordForm form = form_google_; 983 CreateVersion1Pickle(form, &pickle); 984 985 std::vector<PasswordForm*> form_list; 986 NativeBackendKWalletStub::DeserializeValue(form.signon_realm, 987 pickle, &form_list); 988 989 // This will match |old_form_google_| because not all the fields present in 990 // |form_google_| will be deserialized. 991 EXPECT_EQ(1u, form_list.size()); 992 if (form_list.size() > 0) 993 CheckPasswordForm(old_form_google_, *form_list[0]); 994 STLDeleteElements(&form_list); 995 } 996 997 void NativeBackendKWalletPickleTest::CheckVersion0Pickle( 998 bool size_32, PasswordForm::Scheme scheme) { 999 Pickle pickle; 1000 PasswordForm form = old_form_google_; 1001 form.scheme = scheme; 1002 CreateVersion0Pickle(size_32, form, &pickle); 1003 std::vector<PasswordForm*> form_list; 1004 NativeBackendKWalletStub::DeserializeValue(form.signon_realm, 1005 pickle, &form_list); 1006 EXPECT_EQ(1u, form_list.size()); 1007 if (form_list.size() > 0) 1008 CheckPasswordForm(form, *form_list[0]); 1009 STLDeleteElements(&form_list); 1010 } 1011 1012 // We try both SCHEME_HTML and SCHEME_BASIC since the scheme is stored right 1013 // after the size in the pickle, so it's what gets read as part of the count 1014 // when reading 32-bit pickles on 64-bit systems. SCHEME_HTML is 0 (so we'll 1015 // detect errors later) while SCHEME_BASIC is 1 (so we'll detect it then). We 1016 // try both 32-bit and 64-bit pickles since only one will be the "other" size 1017 // for whatever architecture we're running on, but we want to make sure we can 1018 // read all combinations in any event. 1019 1020 TEST_F(NativeBackendKWalletPickleTest, ReadsOld32BitHTMLPickles) { 1021 CheckVersion0Pickle(true, PasswordForm::SCHEME_HTML); 1022 } 1023 1024 TEST_F(NativeBackendKWalletPickleTest, ReadsOld32BitHTTPPickles) { 1025 CheckVersion0Pickle(true, PasswordForm::SCHEME_BASIC); 1026 } 1027 1028 TEST_F(NativeBackendKWalletPickleTest, ReadsOld64BitHTMLPickles) { 1029 CheckVersion0Pickle(false, PasswordForm::SCHEME_HTML); 1030 } 1031 1032 TEST_F(NativeBackendKWalletPickleTest, ReadsOld64BitHTTPPickles) { 1033 CheckVersion0Pickle(false, PasswordForm::SCHEME_BASIC); 1034 } 1035 1036 TEST_F(NativeBackendKWalletPickleTest, CheckVersion1Pickle) { 1037 CheckVersion1Pickle(); 1038 } 1039 1040 TEST_F(NativeBackendKWalletPickleTest, CheckVersion2Pickle) { 1041 CheckVersion2Pickle(); 1042 } 1043