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_METHOD2(OnPasswordStoreRequestDone
,
36 void(CancelableRequestProvider::Handle
,
37 const std::vector
<PasswordForm
*>&));
38 MOCK_METHOD1(OnGetPasswordStoreResults
,
39 void(const std::vector
<PasswordForm
*>&));
42 // This class will add and remove a mock notification observer from
44 class DBThreadObserverHelper
45 : public base::RefCountedThreadSafe
<DBThreadObserverHelper
,
46 BrowserThread::DeleteOnDBThread
> {
48 DBThreadObserverHelper() : done_event_(true, false) {}
50 void Init(PasswordStore
* password_store
) {
51 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
52 BrowserThread::PostTask(
55 base::Bind(&DBThreadObserverHelper::AddObserverTask
,
57 make_scoped_refptr(password_store
)));
61 content::MockNotificationObserver
& observer() {
66 virtual ~DBThreadObserverHelper() {
67 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB
));
68 registrar_
.RemoveAll();
71 void AddObserverTask(PasswordStore
* password_store
) {
72 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB
));
73 registrar_
.Add(&observer_
,
74 chrome::NOTIFICATION_LOGINS_CHANGED
,
75 content::Source
<PasswordStore
>(password_store
));
79 WaitableEvent done_event_
;
80 content::NotificationRegistrar registrar_
;
81 content::MockNotificationObserver observer_
;
84 friend struct BrowserThread::DeleteOnThread
<BrowserThread::DB
>;
85 friend class base::DeleteHelper
<DBThreadObserverHelper
>;
88 } // anonymous namespace
90 class PasswordStoreTest
: public testing::Test
{
93 : ui_thread_(BrowserThread::UI
, &message_loop_
),
94 db_thread_(BrowserThread::DB
) {
97 virtual void SetUp() {
98 ASSERT_TRUE(db_thread_
.Start());
100 profile_
.reset(new TestingProfile());
102 login_db_
.reset(new LoginDatabase());
103 ASSERT_TRUE(login_db_
->Init(profile_
->GetPath().Append(
104 FILE_PATH_LITERAL("login_test"))));
107 virtual void TearDown() {
109 base::MessageLoop::current()->PostTask(FROM_HERE
,
110 base::MessageLoop::QuitClosure());
111 base::MessageLoop::current()->Run();
114 base::MessageLoopForUI message_loop_
;
115 content::TestBrowserThread ui_thread_
;
116 // PasswordStore schedules work on this thread.
117 content::TestBrowserThread db_thread_
;
119 scoped_ptr
<LoginDatabase
> login_db_
;
120 scoped_ptr
<TestingProfile
> profile_
;
123 ACTION(STLDeleteElements0
) {
124 STLDeleteContainerPointers(arg0
.begin(), arg0
.end());
127 ACTION(QuitUIMessageLoop
) {
128 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
129 base::MessageLoop::current()->Quit();
132 TEST_F(PasswordStoreTest
, IgnoreOldWwwGoogleLogins
) {
133 scoped_refptr
<PasswordStoreDefault
> store(
134 new PasswordStoreDefault(login_db_
.release(), profile_
.get()));
137 const time_t cutoff
= 1325376000; // 00:00 Jan 1 2012 UTC
138 // The passwords are all empty because PasswordStoreDefault doesn't store the
139 // actual passwords on OS X (they're stored in the Keychain instead). We could
140 // special-case it, but it's easier to just have empty passwords.
141 static const PasswordFormData form_data
[] = {
142 // A form on https://www.google.com/ older than the cutoff. Will be ignored.
143 { PasswordForm::SCHEME_HTML
,
144 "https://www.google.com",
145 "https://www.google.com/origin",
146 "https://www.google.com/action",
152 true, true, cutoff
- 1 },
153 // A form on https://www.google.com/ older than the cutoff. Will be ignored.
154 { PasswordForm::SCHEME_HTML
,
155 "https://www.google.com",
156 "https://www.google.com/origin",
157 "https://www.google.com/action",
163 true, true, cutoff
- 1 },
164 // A form on https://www.google.com/ newer than the cutoff.
165 { PasswordForm::SCHEME_HTML
,
166 "https://www.google.com",
167 "https://www.google.com/origin",
168 "https://www.google.com/action",
174 true, true, cutoff
+ 1 },
175 // A form on https://accounts.google.com/ older than the cutoff.
176 { PasswordForm::SCHEME_HTML
,
177 "https://accounts.google.com",
178 "https://accounts.google.com/origin",
179 "https://accounts.google.com/action",
185 true, true, cutoff
- 1 },
186 // A form on http://bar.example.com/ older than the cutoff.
187 { PasswordForm::SCHEME_HTML
,
188 "http://bar.example.com",
189 "http://bar.example.com/origin",
190 "http://bar.example.com/action",
196 true, false, cutoff
- 1 },
199 // Build the forms vector and add the forms to the store.
200 std::vector
<PasswordForm
*> all_forms
;
201 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(form_data
); ++i
) {
202 PasswordForm
* form
= CreatePasswordFormFromData(form_data
[i
]);
203 all_forms
.push_back(form
);
204 store
->AddLogin(*form
);
207 // The PasswordStore schedules tasks to run on the DB thread so we schedule
208 // yet another task to notify us that it's safe to carry on with the test.
209 // The PasswordStore doesn't really understand that it's "done" once the tasks
210 // we posted above have completed, so there's no formal notification for that.
211 WaitableEvent
done(false, false);
212 BrowserThread::PostTask(BrowserThread::DB
, FROM_HERE
,
213 base::Bind(&WaitableEvent::Signal
, base::Unretained(&done
)));
216 // We expect to get back only the "recent" www.google.com login.
217 // Theoretically these should never actually exist since there are no longer
218 // any login forms on www.google.com to save, but we technically allow them.
219 // We should not get back the older saved password though.
220 PasswordForm www_google
;
221 www_google
.scheme
= PasswordForm::SCHEME_HTML
;
222 www_google
.signon_realm
= "https://www.google.com";
223 std::vector
<PasswordForm
*> www_google_expected
;
224 www_google_expected
.push_back(all_forms
[2]);
226 // We should still get the accounts.google.com login even though it's older
227 // than our cutoff - this is the new location of all Google login forms.
228 PasswordForm accounts_google
;
229 accounts_google
.scheme
= PasswordForm::SCHEME_HTML
;
230 accounts_google
.signon_realm
= "https://accounts.google.com";
231 std::vector
<PasswordForm
*> accounts_google_expected
;
232 accounts_google_expected
.push_back(all_forms
[3]);
234 // Same thing for a generic saved login.
235 PasswordForm bar_example
;
236 bar_example
.scheme
= PasswordForm::SCHEME_HTML
;
237 bar_example
.signon_realm
= "http://bar.example.com";
238 std::vector
<PasswordForm
*> bar_example_expected
;
239 bar_example_expected
.push_back(all_forms
[4]);
241 MockPasswordStoreConsumer consumer
;
243 // Make sure we quit the MessageLoop even if the test fails.
244 ON_CALL(consumer
, OnGetPasswordStoreResults(_
))
245 .WillByDefault(QuitUIMessageLoop());
247 // Expect the appropriate replies, as above, in reverse order than we will
248 // issue the queries. Each retires on saturation to avoid matcher spew, except
249 // the last which quits the message loop.
250 EXPECT_CALL(consumer
,
251 OnGetPasswordStoreResults(ContainsAllPasswordForms(bar_example_expected
)))
252 .WillOnce(DoAll(WithArg
<0>(STLDeleteElements0()), QuitUIMessageLoop()));
253 EXPECT_CALL(consumer
,
254 OnGetPasswordStoreResults(
255 ContainsAllPasswordForms(accounts_google_expected
)))
256 .WillOnce(WithArg
<0>(STLDeleteElements0())).RetiresOnSaturation();
257 EXPECT_CALL(consumer
,
258 OnGetPasswordStoreResults(
259 ContainsAllPasswordForms(www_google_expected
)))
260 .WillOnce(WithArg
<0>(STLDeleteElements0())).RetiresOnSaturation();
262 store
->GetLogins(www_google
, PasswordStore::ALLOW_PROMPT
, &consumer
);
263 store
->GetLogins(accounts_google
, PasswordStore::ALLOW_PROMPT
, &consumer
);
264 store
->GetLogins(bar_example
, PasswordStore::ALLOW_PROMPT
, &consumer
);
266 base::MessageLoop::current()->Run();
268 STLDeleteElements(&all_forms
);