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