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 "base/basictypes.h"
      6 #include "base/bind.h"
      7 #include "base/bind_helpers.h"
      8 #include "base/file_util.h"
      9 #include "base/files/scoped_temp_dir.h"
     10 #include "base/prefs/pref_service.h"
     11 #include "base/run_loop.h"
     12 #include "base/stl_util.h"
     13 #include "base/strings/string_util.h"
     14 #include "base/strings/stringprintf.h"
     15 #include "base/strings/utf_string_conversions.h"
     16 #include "base/time/time.h"
     17 #include "chrome/browser/password_manager/password_store_x.h"
     18 #include "chrome/test/base/testing_browser_process.h"
     19 #include "components/password_manager/core/browser/password_form_data.h"
     20 #include "components/password_manager/core/browser/password_store_change.h"
     21 #include "components/password_manager/core/browser/password_store_consumer.h"
     22 #include "components/password_manager/core/common/password_manager_pref_names.h"
     23 #include "content/public/browser/browser_thread.h"
     24 #include "content/public/test/test_browser_thread_bundle.h"
     25 #include "testing/gmock/include/gmock/gmock.h"
     26 #include "testing/gtest/include/gtest/gtest.h"
     27 
     28 using autofill::PasswordForm;
     29 using content::BrowserThread;
     30 using password_manager::ContainsAllPasswordForms;
     31 using password_manager::PasswordStoreChange;
     32 using password_manager::PasswordStoreChangeList;
     33 using testing::_;
     34 using testing::DoAll;
     35 using testing::ElementsAreArray;
     36 using testing::Pointee;
     37 using testing::Property;
     38 using testing::WithArg;
     39 
     40 typedef std::vector<PasswordForm*> VectorOfForms;
     41 
     42 namespace {
     43 
     44 class MockPasswordStoreConsumer
     45     : public password_manager::PasswordStoreConsumer {
     46  public:
     47   MOCK_METHOD1(OnGetPasswordStoreResults,
     48                void(const std::vector<PasswordForm*>&));
     49 };
     50 
     51 class MockPasswordStoreObserver
     52     : public password_manager::PasswordStore::Observer {
     53  public:
     54   MOCK_METHOD1(OnLoginsChanged,
     55                void(const password_manager::PasswordStoreChangeList& changes));
     56 };
     57 
     58 class FailingBackend : public PasswordStoreX::NativeBackend {
     59  public:
     60   virtual bool Init() OVERRIDE { return true; }
     61 
     62   virtual PasswordStoreChangeList AddLogin(const PasswordForm& form) OVERRIDE {
     63     return PasswordStoreChangeList();
     64   }
     65   virtual bool UpdateLogin(const PasswordForm& form,
     66                            PasswordStoreChangeList* changes) OVERRIDE {
     67     return false;
     68   }
     69   virtual bool RemoveLogin(const PasswordForm& form) OVERRIDE { return false; }
     70 
     71   virtual bool RemoveLoginsCreatedBetween(
     72       base::Time delete_begin,
     73       base::Time delete_end,
     74       password_manager::PasswordStoreChangeList* changes) OVERRIDE {
     75     return false;
     76   }
     77 
     78   virtual bool RemoveLoginsSyncedBetween(
     79       base::Time delete_begin,
     80       base::Time delete_end,
     81       password_manager::PasswordStoreChangeList* changes) OVERRIDE {
     82     return false;
     83   }
     84 
     85   virtual bool GetLogins(const PasswordForm& form,
     86                          PasswordFormList* forms) OVERRIDE {
     87     return false;
     88   }
     89 
     90   virtual bool GetAutofillableLogins(PasswordFormList* forms) OVERRIDE {
     91     return false;
     92   }
     93   virtual bool GetBlacklistLogins(PasswordFormList* forms) OVERRIDE {
     94     return false;
     95   }
     96 };
     97 
     98 class MockBackend : public PasswordStoreX::NativeBackend {
     99  public:
    100   virtual bool Init() OVERRIDE { return true; }
    101 
    102   virtual PasswordStoreChangeList AddLogin(const PasswordForm& form) OVERRIDE {
    103     all_forms_.push_back(form);
    104     PasswordStoreChange change(PasswordStoreChange::ADD, form);
    105     return PasswordStoreChangeList(1, change);
    106   }
    107 
    108   virtual bool UpdateLogin(const PasswordForm& form,
    109                            PasswordStoreChangeList* changes) OVERRIDE {
    110     for (size_t i = 0; i < all_forms_.size(); ++i)
    111       if (CompareForms(all_forms_[i], form, true)) {
    112         all_forms_[i] = form;
    113         changes->push_back(PasswordStoreChange(PasswordStoreChange::UPDATE,
    114                                                form));
    115       }
    116     return true;
    117   }
    118 
    119   virtual bool RemoveLogin(const PasswordForm& form) OVERRIDE {
    120     for (size_t i = 0; i < all_forms_.size(); ++i)
    121       if (CompareForms(all_forms_[i], form, false))
    122         erase(i--);
    123     return true;
    124   }
    125 
    126   virtual bool RemoveLoginsCreatedBetween(
    127       base::Time delete_begin,
    128       base::Time delete_end,
    129       password_manager::PasswordStoreChangeList* changes) OVERRIDE {
    130     for (size_t i = 0; i < all_forms_.size(); ++i) {
    131       if (delete_begin <= all_forms_[i].date_created &&
    132           (delete_end.is_null() || all_forms_[i].date_created < delete_end))
    133         erase(i--);
    134     }
    135     return true;
    136   }
    137 
    138   virtual bool RemoveLoginsSyncedBetween(
    139       base::Time delete_begin,
    140       base::Time delete_end,
    141       password_manager::PasswordStoreChangeList* changes) OVERRIDE {
    142     DCHECK(changes);
    143     for (size_t i = 0; i < all_forms_.size(); ++i) {
    144       if (delete_begin <= all_forms_[i].date_synced &&
    145           (delete_end.is_null() || all_forms_[i].date_synced < delete_end)) {
    146         changes->push_back(password_manager::PasswordStoreChange(
    147             password_manager::PasswordStoreChange::REMOVE, all_forms_[i]));
    148         erase(i--);
    149       }
    150     }
    151     return true;
    152   }
    153 
    154   virtual bool GetLogins(const PasswordForm& form,
    155                          PasswordFormList* forms) OVERRIDE {
    156     for (size_t i = 0; i < all_forms_.size(); ++i)
    157       if (all_forms_[i].signon_realm == form.signon_realm)
    158         forms->push_back(new PasswordForm(all_forms_[i]));
    159     return true;
    160   }
    161 
    162   virtual bool GetAutofillableLogins(PasswordFormList* forms) OVERRIDE {
    163     for (size_t i = 0; i < all_forms_.size(); ++i)
    164       if (!all_forms_[i].blacklisted_by_user)
    165         forms->push_back(new PasswordForm(all_forms_[i]));
    166     return true;
    167   }
    168 
    169   virtual bool GetBlacklistLogins(PasswordFormList* forms) OVERRIDE {
    170     for (size_t i = 0; i < all_forms_.size(); ++i)
    171       if (all_forms_[i].blacklisted_by_user)
    172         forms->push_back(new PasswordForm(all_forms_[i]));
    173     return true;
    174   }
    175 
    176  private:
    177   void erase(size_t index) {
    178     if (index < all_forms_.size() - 1)
    179       all_forms_[index] = all_forms_[all_forms_.size() - 1];
    180     all_forms_.pop_back();
    181   }
    182 
    183   bool CompareForms(const PasswordForm& a, const PasswordForm& b, bool update) {
    184     // An update check doesn't care about the submit element.
    185     if (!update && a.submit_element != b.submit_element)
    186       return false;
    187     return a.origin           == b.origin &&
    188            a.password_element == b.password_element &&
    189            a.signon_realm     == b.signon_realm &&
    190            a.username_element == b.username_element &&
    191            a.username_value   == b.username_value;
    192   }
    193 
    194   std::vector<PasswordForm> all_forms_;
    195 };
    196 
    197 class MockLoginDatabaseReturn {
    198  public:
    199   MOCK_METHOD1(OnLoginDatabaseQueryDone,
    200                void(const std::vector<PasswordForm*>&));
    201 };
    202 
    203 void LoginDatabaseQueryCallback(password_manager::LoginDatabase* login_db,
    204                                 bool autofillable,
    205                                 MockLoginDatabaseReturn* mock_return) {
    206   std::vector<PasswordForm*> forms;
    207   if (autofillable)
    208     login_db->GetAutofillableLogins(&forms);
    209   else
    210     login_db->GetBlacklistLogins(&forms);
    211   mock_return->OnLoginDatabaseQueryDone(forms);
    212 }
    213 
    214 // Generate |count| expected logins, either auto-fillable or blacklisted.
    215 void InitExpectedForms(bool autofillable, size_t count, VectorOfForms* forms) {
    216   const char* domain = autofillable ? "example" : "blacklisted";
    217   for (size_t i = 0; i < count; ++i) {
    218     std::string realm = base::StringPrintf("http://%zu.%s.com", i, domain);
    219     std::string origin = base::StringPrintf("http://%zu.%s.com/origin",
    220                                             i, domain);
    221     std::string action = base::StringPrintf("http://%zu.%s.com/action",
    222                                             i, domain);
    223     password_manager::PasswordFormData data = {
    224         PasswordForm::SCHEME_HTML,
    225         realm.c_str(),
    226         origin.c_str(),
    227         action.c_str(),
    228         L"submit_element",
    229         L"username_element",
    230         L"password_element",
    231         autofillable ? L"username_value" : NULL,
    232         autofillable ? L"password_value" : NULL,
    233         autofillable,
    234         false,
    235         static_cast<double>(i + 1)};
    236     forms->push_back(CreatePasswordFormFromData(data));
    237   }
    238 }
    239 
    240 }  // anonymous namespace
    241 
    242 enum BackendType {
    243   NO_BACKEND,
    244   FAILING_BACKEND,
    245   WORKING_BACKEND
    246 };
    247 
    248 class PasswordStoreXTest : public testing::TestWithParam<BackendType> {
    249  protected:
    250   virtual void SetUp() {
    251     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
    252 
    253     login_db_.reset(new password_manager::LoginDatabase());
    254     ASSERT_TRUE(login_db_->Init(temp_dir_.path().Append("login_test")));
    255   }
    256 
    257   virtual void TearDown() {
    258     base::RunLoop().RunUntilIdle();
    259   }
    260 
    261   PasswordStoreX::NativeBackend* GetBackend() {
    262     switch (GetParam()) {
    263       case FAILING_BACKEND:
    264         return new FailingBackend();
    265       case WORKING_BACKEND:
    266         return new MockBackend();
    267       default:
    268         return NULL;
    269     }
    270   }
    271 
    272   content::TestBrowserThreadBundle thread_bundle_;
    273 
    274   scoped_ptr<password_manager::LoginDatabase> login_db_;
    275   base::ScopedTempDir temp_dir_;
    276 };
    277 
    278 ACTION(STLDeleteElements0) {
    279   STLDeleteContainerPointers(arg0.begin(), arg0.end());
    280 }
    281 
    282 TEST_P(PasswordStoreXTest, Notifications) {
    283   scoped_refptr<PasswordStoreX> store(
    284       new PasswordStoreX(base::MessageLoopProxy::current(),
    285                          base::MessageLoopProxy::current(),
    286                          login_db_.release(),
    287                          GetBackend()));
    288   store->Init(syncer::SyncableService::StartSyncFlare());
    289 
    290   password_manager::PasswordFormData form_data = {
    291       PasswordForm::SCHEME_HTML,       "http://bar.example.com",
    292       "http://bar.example.com/origin", "http://bar.example.com/action",
    293       L"submit_element",               L"username_element",
    294       L"password_element",             L"username_value",
    295       L"password_value",               true,
    296       false,                           1};
    297   scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(form_data));
    298 
    299   MockPasswordStoreObserver observer;
    300   store->AddObserver(&observer);
    301 
    302   const PasswordStoreChange expected_add_changes[] = {
    303     PasswordStoreChange(PasswordStoreChange::ADD, *form),
    304   };
    305 
    306   EXPECT_CALL(
    307       observer,
    308       OnLoginsChanged(ElementsAreArray(expected_add_changes)));
    309 
    310   // Adding a login should trigger a notification.
    311   store->AddLogin(*form);
    312 
    313   // The PasswordStore schedules tasks to run on the DB thread. Wait for them
    314   // to complete.
    315   base::RunLoop().RunUntilIdle();
    316 
    317   // Change the password.
    318   form->password_value = base::ASCIIToUTF16("a different password");
    319 
    320   const PasswordStoreChange expected_update_changes[] = {
    321     PasswordStoreChange(PasswordStoreChange::UPDATE, *form),
    322   };
    323 
    324   EXPECT_CALL(
    325       observer,
    326       OnLoginsChanged(ElementsAreArray(expected_update_changes)));
    327 
    328   // Updating the login with the new password should trigger a notification.
    329   store->UpdateLogin(*form);
    330 
    331   // Wait for PasswordStore to send execute.
    332   base::RunLoop().RunUntilIdle();
    333 
    334   const PasswordStoreChange expected_delete_changes[] = {
    335     PasswordStoreChange(PasswordStoreChange::REMOVE, *form),
    336   };
    337 
    338   EXPECT_CALL(
    339       observer,
    340       OnLoginsChanged(ElementsAreArray(expected_delete_changes)));
    341 
    342   // Deleting the login should trigger a notification.
    343   store->RemoveLogin(*form);
    344 
    345   // Wait for PasswordStore to execute.
    346   base::RunLoop().RunUntilIdle();
    347 
    348   store->RemoveObserver(&observer);
    349 
    350   store->Shutdown();
    351 }
    352 
    353 TEST_P(PasswordStoreXTest, NativeMigration) {
    354   VectorOfForms expected_autofillable;
    355   InitExpectedForms(true, 50, &expected_autofillable);
    356 
    357   VectorOfForms expected_blacklisted;
    358   InitExpectedForms(false, 50, &expected_blacklisted);
    359 
    360   // Get the initial size of the login DB file, before we populate it.
    361   // This will be used later to make sure it gets back to this size.
    362   const base::FilePath login_db_file = temp_dir_.path().Append("login_test");
    363   base::File::Info db_file_start_info;
    364   ASSERT_TRUE(base::GetFileInfo(login_db_file, &db_file_start_info));
    365 
    366   password_manager::LoginDatabase* login_db = login_db_.get();
    367 
    368   // Populate the login DB with logins that should be migrated.
    369   for (VectorOfForms::iterator it = expected_autofillable.begin();
    370        it != expected_autofillable.end(); ++it) {
    371     login_db->AddLogin(**it);
    372   }
    373   for (VectorOfForms::iterator it = expected_blacklisted.begin();
    374        it != expected_blacklisted.end(); ++it) {
    375     login_db->AddLogin(**it);
    376   }
    377 
    378   // Get the new size of the login DB file. We expect it to be larger.
    379   base::File::Info db_file_full_info;
    380   ASSERT_TRUE(base::GetFileInfo(login_db_file, &db_file_full_info));
    381   EXPECT_GT(db_file_full_info.size, db_file_start_info.size);
    382 
    383   // Initializing the PasswordStore shouldn't trigger a native migration (yet).
    384   scoped_refptr<PasswordStoreX> store(
    385       new PasswordStoreX(base::MessageLoopProxy::current(),
    386                          base::MessageLoopProxy::current(),
    387                          login_db_.release(),
    388                          GetBackend()));
    389   store->Init(syncer::SyncableService::StartSyncFlare());
    390 
    391   MockPasswordStoreConsumer consumer;
    392 
    393   // The autofillable forms should have been migrated to the native backend.
    394   EXPECT_CALL(consumer,
    395       OnGetPasswordStoreResults(
    396           ContainsAllPasswordForms(expected_autofillable)))
    397       .WillOnce(WithArg<0>(STLDeleteElements0()));
    398 
    399   store->GetAutofillableLogins(&consumer);
    400   base::RunLoop().RunUntilIdle();
    401 
    402   // The blacklisted forms should have been migrated to the native backend.
    403   EXPECT_CALL(consumer,
    404       OnGetPasswordStoreResults(ContainsAllPasswordForms(expected_blacklisted)))
    405       .WillOnce(WithArg<0>(STLDeleteElements0()));
    406 
    407   store->GetBlacklistLogins(&consumer);
    408   base::RunLoop().RunUntilIdle();
    409 
    410   VectorOfForms empty;
    411   MockLoginDatabaseReturn ld_return;
    412 
    413   if (GetParam() == WORKING_BACKEND) {
    414     // No autofillable logins should be left in the login DB.
    415     EXPECT_CALL(ld_return,
    416                 OnLoginDatabaseQueryDone(ContainsAllPasswordForms(empty)));
    417   } else {
    418     // The autofillable logins should still be in the login DB.
    419     EXPECT_CALL(ld_return,
    420                 OnLoginDatabaseQueryDone(
    421                     ContainsAllPasswordForms(expected_autofillable)))
    422         .WillOnce(WithArg<0>(STLDeleteElements0()));
    423   }
    424 
    425   LoginDatabaseQueryCallback(login_db, true, &ld_return);
    426 
    427   // Wait for the login DB methods to execute.
    428   base::RunLoop().RunUntilIdle();
    429 
    430   if (GetParam() == WORKING_BACKEND) {
    431     // Likewise, no blacklisted logins should be left in the login DB.
    432     EXPECT_CALL(ld_return,
    433                 OnLoginDatabaseQueryDone(ContainsAllPasswordForms(empty)));
    434   } else {
    435     // The blacklisted logins should still be in the login DB.
    436     EXPECT_CALL(ld_return,
    437                 OnLoginDatabaseQueryDone(
    438                     ContainsAllPasswordForms(expected_blacklisted)))
    439         .WillOnce(WithArg<0>(STLDeleteElements0()));
    440   }
    441 
    442   LoginDatabaseQueryCallback(login_db, false, &ld_return);
    443 
    444   // Wait for the login DB methods to execute.
    445   base::RunLoop().RunUntilIdle();
    446 
    447   if (GetParam() == WORKING_BACKEND) {
    448     // If the migration succeeded, then not only should there be no logins left
    449     // in the login DB, but also the file should have been deleted and then
    450     // recreated. We approximate checking for this by checking that the file
    451     // size is equal to the size before we populated it, even though it was
    452     // larger after populating it.
    453     base::File::Info db_file_end_info;
    454     ASSERT_TRUE(base::GetFileInfo(login_db_file, &db_file_end_info));
    455     EXPECT_EQ(db_file_start_info.size, db_file_end_info.size);
    456   }
    457 
    458   STLDeleteElements(&expected_autofillable);
    459   STLDeleteElements(&expected_blacklisted);
    460 
    461   store->Shutdown();
    462 }
    463 
    464 INSTANTIATE_TEST_CASE_P(NoBackend,
    465                         PasswordStoreXTest,
    466                         testing::Values(NO_BACKEND));
    467 INSTANTIATE_TEST_CASE_P(FailingBackend,
    468                         PasswordStoreXTest,
    469                         testing::Values(FAILING_BACKEND));
    470 INSTANTIATE_TEST_CASE_P(WorkingBackend,
    471                         PasswordStoreXTest,
    472                         testing::Values(WORKING_BACKEND));
    473