1 // Copyright (c) 2012 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.
5 #include "base/basictypes.h"
7 #include "base/stl_util.h"
8 #include "base/strings/string_util.h"
9 #include "base/synchronization/waitable_event.h"
10 #include "base/time/time.h"
11 #include "chrome/browser/chrome_notification_types.h"
12 #include "chrome/browser/password_manager/password_form_data.h"
13 #include "chrome/browser/password_manager/password_store_consumer.h"
14 #include "chrome/browser/password_manager/password_store_default.h"
15 #include "chrome/test/base/testing_profile.h"
16 #include "content/public/browser/notification_details.h"
17 #include "content/public/browser/notification_registrar.h"
18 #include "content/public/browser/notification_source.h"
19 #include "content/public/test/mock_notification_observer.h"
20 #include "content/public/test/test_browser_thread.h"
21 #include "testing/gmock/include/gmock/gmock.h"
22 #include "testing/gtest/include/gtest/gtest.h"
24 using autofill::PasswordForm;
25 using base::WaitableEvent;
26 using content::BrowserThread;
29 using testing::WithArg;
33 class MockPasswordStoreConsumer : public PasswordStoreConsumer {
35 MOCK_METHOD1(OnGetPasswordStoreResults,
36 void(const std::vector<PasswordForm*>&));
39 // This class will add and remove a mock notification observer from
41 class DBThreadObserverHelper
42 : public base::RefCountedThreadSafe<DBThreadObserverHelper,
43 BrowserThread::DeleteOnDBThread> {
45 DBThreadObserverHelper() : done_event_(true, false) {}
47 void Init(PasswordStore* password_store) {
48 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
49 BrowserThread::PostTask(
52 base::Bind(&DBThreadObserverHelper::AddObserverTask,
54 make_scoped_refptr(password_store)));
58 content::MockNotificationObserver& observer() {
63 virtual ~DBThreadObserverHelper() {
64 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
65 registrar_.RemoveAll();
68 void AddObserverTask(PasswordStore* password_store) {
69 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
70 registrar_.Add(&observer_,
71 chrome::NOTIFICATION_LOGINS_CHANGED,
72 content::Source<PasswordStore>(password_store));
76 WaitableEvent done_event_;
77 content::NotificationRegistrar registrar_;
78 content::MockNotificationObserver observer_;
81 friend struct BrowserThread::DeleteOnThread<BrowserThread::DB>;
82 friend class base::DeleteHelper<DBThreadObserverHelper>;
85 } // anonymous namespace
87 class PasswordStoreTest : public testing::Test {
90 : ui_thread_(BrowserThread::UI, &message_loop_),
91 db_thread_(BrowserThread::DB) {
94 virtual void SetUp() {
95 ASSERT_TRUE(db_thread_.Start());
97 profile_.reset(new TestingProfile());
99 login_db_.reset(new LoginDatabase());
100 ASSERT_TRUE(login_db_->Init(profile_->GetPath().Append(
101 FILE_PATH_LITERAL("login_test"))));
104 virtual void TearDown() {
106 base::MessageLoop::current()->PostTask(FROM_HERE,
107 base::MessageLoop::QuitClosure());
108 base::MessageLoop::current()->Run();
111 base::MessageLoopForUI message_loop_;
112 content::TestBrowserThread ui_thread_;
113 // PasswordStore schedules work on this thread.
114 content::TestBrowserThread db_thread_;
116 scoped_ptr<LoginDatabase> login_db_;
117 scoped_ptr<TestingProfile> profile_;
120 ACTION(STLDeleteElements0) {
121 STLDeleteContainerPointers(arg0.begin(), arg0.end());
124 ACTION(QuitUIMessageLoop) {
125 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
126 base::MessageLoop::current()->Quit();
129 TEST_F(PasswordStoreTest, IgnoreOldWwwGoogleLogins) {
130 scoped_refptr<PasswordStoreDefault> store(
131 new PasswordStoreDefault(login_db_.release(), profile_.get()));
134 const time_t cutoff = 1325376000; // 00:00 Jan 1 2012 UTC
135 // The passwords are all empty because PasswordStoreDefault doesn't store the
136 // actual passwords on OS X (they're stored in the Keychain instead). We could
137 // special-case it, but it's easier to just have empty passwords.
138 static const PasswordFormData form_data[] = {
139 // A form on https://www.google.com/ older than the cutoff. Will be ignored.
140 { PasswordForm::SCHEME_HTML,
141 "https://www.google.com",
142 "https://www.google.com/origin",
143 "https://www.google.com/action",
149 true, true, cutoff - 1 },
150 // A form on https://www.google.com/ older than the cutoff. Will be ignored.
151 { PasswordForm::SCHEME_HTML,
152 "https://www.google.com",
153 "https://www.google.com/origin",
154 "https://www.google.com/action",
160 true, true, cutoff - 1 },
161 // A form on https://www.google.com/ newer than the cutoff.
162 { PasswordForm::SCHEME_HTML,
163 "https://www.google.com",
164 "https://www.google.com/origin",
165 "https://www.google.com/action",
171 true, true, cutoff + 1 },
172 // A form on https://accounts.google.com/ older than the cutoff.
173 { PasswordForm::SCHEME_HTML,
174 "https://accounts.google.com",
175 "https://accounts.google.com/origin",
176 "https://accounts.google.com/action",
182 true, true, cutoff - 1 },
183 // A form on http://bar.example.com/ older than the cutoff.
184 { PasswordForm::SCHEME_HTML,
185 "http://bar.example.com",
186 "http://bar.example.com/origin",
187 "http://bar.example.com/action",
193 true, false, cutoff - 1 },
196 // Build the forms vector and add the forms to the store.
197 std::vector<PasswordForm*> all_forms;
198 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(form_data); ++i) {
199 PasswordForm* form = CreatePasswordFormFromData(form_data[i]);
200 all_forms.push_back(form);
201 store->AddLogin(*form);
204 // The PasswordStore schedules tasks to run on the DB thread so we schedule
205 // yet another task to notify us that it's safe to carry on with the test.
206 // The PasswordStore doesn't really understand that it's "done" once the tasks
207 // we posted above have completed, so there's no formal notification for that.
208 WaitableEvent done(false, false);
209 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
210 base::Bind(&WaitableEvent::Signal, base::Unretained(&done)));
213 // We expect to get back only the "recent" www.google.com login.
214 // Theoretically these should never actually exist since there are no longer
215 // any login forms on www.google.com to save, but we technically allow them.
216 // We should not get back the older saved password though.
217 PasswordForm www_google;
218 www_google.scheme = PasswordForm::SCHEME_HTML;
219 www_google.signon_realm = "https://www.google.com";
220 std::vector<PasswordForm*> www_google_expected;
221 www_google_expected.push_back(all_forms[2]);
223 // We should still get the accounts.google.com login even though it's older
224 // than our cutoff - this is the new location of all Google login forms.
225 PasswordForm accounts_google;
226 accounts_google.scheme = PasswordForm::SCHEME_HTML;
227 accounts_google.signon_realm = "https://accounts.google.com";
228 std::vector<PasswordForm*> accounts_google_expected;
229 accounts_google_expected.push_back(all_forms[3]);
231 // Same thing for a generic saved login.
232 PasswordForm bar_example;
233 bar_example.scheme = PasswordForm::SCHEME_HTML;
234 bar_example.signon_realm = "http://bar.example.com";
235 std::vector<PasswordForm*> bar_example_expected;
236 bar_example_expected.push_back(all_forms[4]);
238 MockPasswordStoreConsumer consumer;
240 // Make sure we quit the MessageLoop even if the test fails.
241 ON_CALL(consumer, OnGetPasswordStoreResults(_))
242 .WillByDefault(QuitUIMessageLoop());
244 // Expect the appropriate replies, as above, in reverse order than we will
245 // issue the queries. Each retires on saturation to avoid matcher spew, except
246 // the last which quits the message loop.
247 EXPECT_CALL(consumer,
248 OnGetPasswordStoreResults(ContainsAllPasswordForms(bar_example_expected)))
249 .WillOnce(DoAll(WithArg<0>(STLDeleteElements0()), QuitUIMessageLoop()));
250 EXPECT_CALL(consumer,
251 OnGetPasswordStoreResults(
252 ContainsAllPasswordForms(accounts_google_expected)))
253 .WillOnce(WithArg<0>(STLDeleteElements0())).RetiresOnSaturation();
254 EXPECT_CALL(consumer,
255 OnGetPasswordStoreResults(
256 ContainsAllPasswordForms(www_google_expected)))
257 .WillOnce(WithArg<0>(STLDeleteElements0())).RetiresOnSaturation();
259 store->GetLogins(www_google, PasswordStore::ALLOW_PROMPT, &consumer);
260 store->GetLogins(accounts_google, PasswordStore::ALLOW_PROMPT, &consumer);
261 store->GetLogins(bar_example, PasswordStore::ALLOW_PROMPT, &consumer);
263 base::MessageLoop::current()->Run();
265 STLDeleteElements(&all_forms);