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
17using webkit_glue::PasswordForm;
18
19class 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
37TEST_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
177static 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
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(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
246TEST_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