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