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