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