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
15 using base::Time;
16 using base::TimeDelta;
17 using webkit_glue::PasswordForm;
18
19 class LoginsTableTest : public testing::Test {
20 public:
LoginsTableTest()21 LoginsTableTest() {}
~LoginsTableTest()22 virtual ~LoginsTableTest() {}
23
24 protected:
SetUp()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
TearDown()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
TEST_F(LoginsTableTest,Logins)44 TEST_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
AddTimestampedLogin(WebDatabase * db,std::string url,const std::string & unique_string,const Time & time)177 static 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
ClearResults(std::vector<PasswordForm * > * results)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
TEST_F(LoginsTableTest,ClearPrivateData_SavedPasswords)199 TEST_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
TEST_F(LoginsTableTest,BlacklistedLogins)241 TEST_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