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:
SetUp()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
TearDown()30 virtual void TearDown() {
31 file_util::Delete(file_, false);
32 }
33
34 FilePath file_;
35 };
36
TEST_F(LoginDatabaseTest,Logins)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@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
AddTimestampedLogin(LoginDatabase * db,std::string url,const std::string & unique_string,const base::Time & time)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
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(LoginDatabaseTest,ClearPrivateData_SavedPasswords)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
TEST_F(LoginDatabaseTest,BlacklistedLogins)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