Home | History | Annotate | Download | only in password_manager
      1 // Copyright (c) 2009 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 "testing/gtest/include/gtest/gtest.h"
      6 
      7 #include "base/basictypes.h"
      8 #include "base/file_util.h"
      9 #include "base/path_service.h"
     10 #include "base/string_number_conversions.h"
     11 #include "base/time.h"
     12 #include "base/utf_string_conversions.h"
     13 #include "chrome/browser/password_manager/login_database.h"
     14 #include "chrome/common/chrome_paths.h"
     15 #include "webkit/glue/password_form.h"
     16 
     17 using webkit_glue::PasswordForm;
     18 
     19 class LoginDatabaseTest : public testing::Test {
     20  protected:
     21   virtual void SetUp() {
     22     PathService::Get(chrome::DIR_TEST_DATA, &file_);
     23     const std::string test_db =
     24         "TestMetadataStoreMacDatabase" +
     25         base::Int64ToString(base::Time::Now().ToInternalValue()) + ".db";
     26     file_ = file_.AppendASCII(test_db);
     27     file_util::Delete(file_, false);
     28   }
     29 
     30   virtual void TearDown() {
     31     file_util::Delete(file_, false);
     32   }
     33 
     34   FilePath file_;
     35 };
     36 
     37 TEST_F(LoginDatabaseTest, Logins) {
     38   scoped_ptr<LoginDatabase> db(new LoginDatabase());
     39 
     40   ASSERT_TRUE(db->Init(file_));
     41 
     42   std::vector<PasswordForm*> result;
     43 
     44   // Verify the database is empty.
     45   EXPECT_TRUE(db->GetAutofillableLogins(&result));
     46   EXPECT_EQ(0U, result.size());
     47 
     48   // Example password form.
     49   PasswordForm form;
     50   form.origin = GURL("http://www.google.com/accounts/LoginAuth");
     51   form.action = GURL("http://www.google.com/accounts/Login");
     52   form.username_element = ASCIIToUTF16("Email");
     53   form.username_value = ASCIIToUTF16("test (at) gmail.com");
     54   form.password_element = ASCIIToUTF16("Passwd");
     55   form.password_value = ASCIIToUTF16("test");
     56   form.submit_element = ASCIIToUTF16("signIn");
     57   form.signon_realm = "http://www.google.com/";
     58   form.ssl_valid = false;
     59   form.preferred = false;
     60   form.scheme = PasswordForm::SCHEME_HTML;
     61 
     62   // Add it and make sure it is there.
     63   EXPECT_TRUE(db->AddLogin(form));
     64   EXPECT_TRUE(db->GetAutofillableLogins(&result));
     65   EXPECT_EQ(1U, result.size());
     66   delete result[0];
     67   result.clear();
     68 
     69   // Match against an exact copy.
     70   EXPECT_TRUE(db->GetLogins(form, &result));
     71   EXPECT_EQ(1U, result.size());
     72   delete result[0];
     73   result.clear();
     74 
     75   // The example site changes...
     76   PasswordForm form2(form);
     77   form2.origin = GURL("http://www.google.com/new/accounts/LoginAuth");
     78   form2.submit_element = ASCIIToUTF16("reallySignIn");
     79 
     80   // Match against an inexact copy
     81   EXPECT_TRUE(db->GetLogins(form2, &result));
     82   EXPECT_EQ(1U, result.size());
     83   delete result[0];
     84   result.clear();
     85 
     86   // Uh oh, the site changed origin & action URLs all at once!
     87   PasswordForm form3(form2);
     88   form3.action = GURL("http://www.google.com/new/accounts/Login");
     89 
     90   // signon_realm is the same, should match.
     91   EXPECT_TRUE(db->GetLogins(form3, &result));
     92   EXPECT_EQ(1U, result.size());
     93   delete result[0];
     94   result.clear();
     95 
     96   // Imagine the site moves to a secure server for login.
     97   PasswordForm form4(form3);
     98   form4.signon_realm = "https://www.google.com/";
     99   form4.ssl_valid = true;
    100 
    101   // We have only an http record, so no match for this.
    102   EXPECT_TRUE(db->GetLogins(form4, &result));
    103   EXPECT_EQ(0U, result.size());
    104 
    105   // Let's imagine the user logs into the secure site.
    106   EXPECT_TRUE(db->AddLogin(form4));
    107   EXPECT_TRUE(db->GetAutofillableLogins(&result));
    108   EXPECT_EQ(2U, result.size());
    109   delete result[0];
    110   delete result[1];
    111   result.clear();
    112 
    113   // Now the match works
    114   EXPECT_TRUE(db->GetLogins(form4, &result));
    115   EXPECT_EQ(1U, result.size());
    116   delete result[0];
    117   result.clear();
    118 
    119   // The user chose to forget the original but not the new.
    120   EXPECT_TRUE(db->RemoveLogin(form));
    121   EXPECT_TRUE(db->GetAutofillableLogins(&result));
    122   EXPECT_EQ(1U, result.size());
    123   delete result[0];
    124   result.clear();
    125 
    126   // The old form wont match the new site (http vs https).
    127   EXPECT_TRUE(db->GetLogins(form, &result));
    128   EXPECT_EQ(0U, result.size());
    129 
    130   // The user's request for the HTTPS site is intercepted
    131   // by an attacker who presents an invalid SSL cert.
    132   PasswordForm form5(form4);
    133   form5.ssl_valid = 0;
    134 
    135   // It will match in this case.
    136   EXPECT_TRUE(db->GetLogins(form5, &result));
    137   EXPECT_EQ(1U, result.size());
    138   delete result[0];
    139   result.clear();
    140 
    141   // User changes his password.
    142   PasswordForm form6(form5);
    143   form6.password_value = ASCIIToUTF16("test6");
    144   form6.preferred = true;
    145 
    146   // We update, and check to make sure it matches the
    147   // old form, and there is only one record.
    148   int rows_changed = 0;
    149   EXPECT_TRUE(db->UpdateLogin(form6, &rows_changed));
    150   EXPECT_EQ(1, rows_changed);
    151   // matches
    152   EXPECT_TRUE(db->GetLogins(form5, &result));
    153   EXPECT_EQ(1U, result.size());
    154   delete result[0];
    155   result.clear();
    156   // Only one record.
    157   EXPECT_TRUE(db->GetAutofillableLogins(&result));
    158   EXPECT_EQ(1U, result.size());
    159   // Password element was updated.
    160 #if defined(OS_MACOSX)
    161   // On the Mac we should never be storing passwords in the database.
    162   EXPECT_EQ(string16(), result[0]->password_value);
    163 #else
    164   EXPECT_EQ(form6.password_value, result[0]->password_value);
    165 #endif
    166   // Preferred login.
    167   EXPECT_TRUE(form6.preferred);
    168   delete result[0];
    169   result.clear();
    170 
    171   // Make sure everything can disappear.
    172   EXPECT_TRUE(db->RemoveLogin(form4));
    173   EXPECT_TRUE(db->GetAutofillableLogins(&result));
    174   EXPECT_EQ(0U, result.size());
    175 }
    176 
    177 static bool AddTimestampedLogin(LoginDatabase* db, std::string url,
    178                                 const std::string& unique_string,
    179                                 const base::Time& time) {
    180   // Example password form.
    181   PasswordForm form;
    182   form.origin = GURL(url + std::string("/LoginAuth"));
    183   form.username_element = ASCIIToUTF16(unique_string);
    184   form.username_value = ASCIIToUTF16(unique_string);
    185   form.password_element = ASCIIToUTF16(unique_string);
    186   form.submit_element = ASCIIToUTF16("signIn");
    187   form.signon_realm = url;
    188   form.date_created = time;
    189   return db->AddLogin(form);
    190 }
    191 
    192 static void ClearResults(std::vector<PasswordForm*>* results) {
    193   for (size_t i = 0; i < results->size(); ++i) {
    194     delete (*results)[i];
    195   }
    196   results->clear();
    197 }
    198 
    199 TEST_F(LoginDatabaseTest, ClearPrivateData_SavedPasswords) {
    200   scoped_ptr<LoginDatabase> db(new LoginDatabase());
    201 
    202   EXPECT_TRUE(db->Init(file_));
    203 
    204   std::vector<PasswordForm*> result;
    205 
    206   // Verify the database is empty.
    207   EXPECT_TRUE(db->GetAutofillableLogins(&result));
    208   EXPECT_EQ(0U, result.size());
    209 
    210   base::Time now = base::Time::Now();
    211   base::TimeDelta one_day = base::TimeDelta::FromDays(1);
    212 
    213   // Create one with a 0 time.
    214   EXPECT_TRUE(AddTimestampedLogin(db.get(), "1", "foo1", base::Time()));
    215   // Create one for now and +/- 1 day.
    216   EXPECT_TRUE(AddTimestampedLogin(db.get(), "2", "foo2", now - one_day));
    217   EXPECT_TRUE(AddTimestampedLogin(db.get(), "3", "foo3", now));
    218   EXPECT_TRUE(AddTimestampedLogin(db.get(), "4", "foo4", now + one_day));
    219 
    220   // Verify inserts worked.
    221   EXPECT_TRUE(db->GetAutofillableLogins(&result));
    222   EXPECT_EQ(4U, result.size());
    223   ClearResults(&result);
    224 
    225   // Get everything from today's date and on.
    226   EXPECT_TRUE(db->GetLoginsCreatedBetween(now, base::Time(), &result));
    227   EXPECT_EQ(2U, result.size());
    228   ClearResults(&result);
    229 
    230   // Delete everything from today's date and on.
    231   db->RemoveLoginsCreatedBetween(now, base::Time());
    232 
    233   // Should have deleted half of what we inserted.
    234   EXPECT_TRUE(db->GetAutofillableLogins(&result));
    235   EXPECT_EQ(2U, result.size());
    236   ClearResults(&result);
    237 
    238   // Delete with 0 date (should delete all).
    239   db->RemoveLoginsCreatedBetween(base::Time(), base::Time());
    240 
    241   // Verify nothing is left.
    242   EXPECT_TRUE(db->GetAutofillableLogins(&result));
    243   EXPECT_EQ(0U, result.size());
    244 }
    245 
    246 TEST_F(LoginDatabaseTest, BlacklistedLogins) {
    247   scoped_ptr<LoginDatabase> db(new LoginDatabase());
    248 
    249   EXPECT_TRUE(db->Init(file_));
    250   std::vector<PasswordForm*> result;
    251 
    252   // Verify the database is empty.
    253   EXPECT_TRUE(db->GetBlacklistLogins(&result));
    254   ASSERT_EQ(0U, result.size());
    255 
    256   // Save a form as blacklisted.
    257   PasswordForm form;
    258   form.origin = GURL("http://www.google.com/accounts/LoginAuth");
    259   form.action = GURL("http://www.google.com/accounts/Login");
    260   form.username_element = ASCIIToUTF16("Email");
    261   form.password_element = ASCIIToUTF16("Passwd");
    262   form.submit_element = ASCIIToUTF16("signIn");
    263   form.signon_realm = "http://www.google.com/";
    264   form.ssl_valid = false;
    265   form.preferred = true;
    266   form.blacklisted_by_user = true;
    267   form.scheme = PasswordForm::SCHEME_HTML;
    268   EXPECT_TRUE(db->AddLogin(form));
    269 
    270   // Get all non-blacklisted logins (should be none).
    271   EXPECT_TRUE(db->GetAutofillableLogins(&result));
    272   ASSERT_EQ(0U, result.size());
    273 
    274   // GetLogins should give the blacklisted result.
    275   EXPECT_TRUE(db->GetLogins(form, &result));
    276   EXPECT_EQ(1U, result.size());
    277   ClearResults(&result);
    278 
    279   // So should GetAllBlacklistedLogins.
    280   EXPECT_TRUE(db->GetBlacklistLogins(&result));
    281   EXPECT_EQ(1U, result.size());
    282   ClearResults(&result);
    283 }
    284