Home | History | Annotate | Download | only in password_manager
      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