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.
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/files/scoped_temp_dir.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/prefs/pref_service.h"
16 #include "base/stl_util.h"
17 #include "base/synchronization/waitable_event.h"
18 #include "base/time/time.h"
19 #include "chrome/browser/password_manager/password_store_win.h"
20 #include "chrome/test/base/testing_profile.h"
21 #include "components/os_crypt/ie7_password_win.h"
22 #include "components/password_manager/core/browser/password_manager_test_utils.h"
23 #include "components/password_manager/core/browser/password_store_consumer.h"
24 #include "components/password_manager/core/browser/webdata/logins_table.h"
25 #include "components/password_manager/core/browser/webdata/password_web_data_service_win.h"
26 #include "components/password_manager/core/common/password_manager_pref_names.h"
27 #include "components/webdata/common/web_database_service.h"
28 #include "content/public/test/test_browser_thread.h"
29 #include "crypto/wincrypt_shim.h"
30 #include "testing/gmock/include/gmock/gmock.h"
31 #include "testing/gtest/include/gtest/gtest.h"
33 using autofill::PasswordForm
;
34 using base::WaitableEvent
;
35 using content::BrowserThread
;
36 using password_manager::ContainsSamePasswordForms
;
37 using password_manager::LoginDatabase
;
38 using password_manager::PasswordFormData
;
39 using password_manager::PasswordStore
;
40 using password_manager::PasswordStoreConsumer
;
43 using testing::IsEmpty
;
44 using testing::WithArg
;
48 class MockPasswordStoreConsumer
: public PasswordStoreConsumer
{
50 MOCK_METHOD1(OnGetPasswordStoreResultsConstRef
,
51 void(const std::vector
<PasswordForm
*>&));
53 // GMock cannot mock methods with move-only args.
54 void OnGetPasswordStoreResults(ScopedVector
<PasswordForm
> results
) override
{
55 OnGetPasswordStoreResultsConstRef(results
.get());
59 class MockWebDataServiceConsumer
: public WebDataServiceConsumer
{
61 MOCK_METHOD2(OnWebDataServiceRequestDone
,
62 void(PasswordWebDataService::Handle
, const WDTypedResult
*));
65 } // anonymous namespace
67 class PasswordStoreWinTest
: public testing::Test
{
69 PasswordStoreWinTest()
70 : ui_thread_(BrowserThread::UI
, &message_loop_
),
71 db_thread_(BrowserThread::DB
) {
74 bool CreateIE7PasswordInfo(const std::wstring
& url
, const base::Time
& created
,
75 IE7PasswordInfo
* info
) {
76 // Copied from chrome/browser/importer/importer_unittest.cc
77 // The username is "abcdefgh" and the password "abcdefghijkl".
78 unsigned char data
[] = "\x0c\x00\x00\x00\x38\x00\x00\x00\x2c\x00\x00\x00"
79 "\x57\x49\x43\x4b\x18\x00\x00\x00\x02\x00\x00\x00"
80 "\x67\x00\x72\x00\x01\x00\x00\x00\x00\x00\x00\x00"
81 "\x00\x00\x00\x00\x4e\xfa\x67\x76\x22\x94\xc8\x01"
82 "\x08\x00\x00\x00\x12\x00\x00\x00\x4e\xfa\x67\x76"
83 "\x22\x94\xc8\x01\x0c\x00\x00\x00\x61\x00\x62\x00"
84 "\x63\x00\x64\x00\x65\x00\x66\x00\x67\x00\x68\x00"
85 "\x00\x00\x61\x00\x62\x00\x63\x00\x64\x00\x65\x00"
86 "\x66\x00\x67\x00\x68\x00\x69\x00\x6a\x00\x6b\x00"
88 DATA_BLOB input
= {0};
89 DATA_BLOB url_key
= {0};
90 DATA_BLOB output
= {0};
93 input
.cbData
= sizeof(data
);
95 url_key
.pbData
= reinterpret_cast<unsigned char*>(
96 const_cast<wchar_t*>(url
.data()));
97 url_key
.cbData
= static_cast<DWORD
>((url
.size() + 1) *
98 sizeof(std::wstring::value_type
));
100 if (!CryptProtectData(&input
, nullptr, &url_key
, nullptr, nullptr,
101 CRYPTPROTECT_UI_FORBIDDEN
, &output
))
104 std::vector
<unsigned char> encrypted_data
;
105 encrypted_data
.resize(output
.cbData
);
106 memcpy(&encrypted_data
.front(), output
.pbData
, output
.cbData
);
108 LocalFree(output
.pbData
);
110 info
->url_hash
= ie7_password::GetUrlHash(url
);
111 info
->encrypted_data
= encrypted_data
;
112 info
->date_created
= created
;
117 virtual void SetUp() {
118 ASSERT_TRUE(db_thread_
.Start());
119 ASSERT_TRUE(temp_dir_
.CreateUniqueTempDir());
121 profile_
.reset(new TestingProfile());
123 base::FilePath path
= temp_dir_
.path().AppendASCII("web_data_test");
124 wdbs_
= new WebDatabaseService(path
,
125 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI
),
126 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB
));
127 // Need to add at least one table so the database gets created.
128 wdbs_
->AddTable(scoped_ptr
<WebDatabaseTable
>(new LoginsTable()));
129 wdbs_
->LoadDatabase();
130 wds_
= new PasswordWebDataService(
132 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI
),
133 WebDataServiceBase::ProfileErrorCallback());
137 virtual void TearDown() {
140 wds_
->ShutdownOnUIThread();
141 wdbs_
->ShutdownDatabase();
144 base::WaitableEvent
done(false, false);
145 BrowserThread::PostTask(BrowserThread::DB
, FROM_HERE
,
146 base::Bind(&base::WaitableEvent::Signal
, base::Unretained(&done
)));
148 base::MessageLoop::current()->PostTask(FROM_HERE
,
149 base::MessageLoop::QuitClosure());
150 base::MessageLoop::current()->Run();
154 base::FilePath
test_login_db_file_path() const {
155 return temp_dir_
.path().Append(FILE_PATH_LITERAL("login_test"));
158 PasswordStoreWin
* CreatePasswordStore() {
159 return new PasswordStoreWin(
160 base::MessageLoopProxy::current(),
161 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB
),
162 make_scoped_ptr(new LoginDatabase(test_login_db_file_path())),
166 base::MessageLoopForUI message_loop_
;
167 content::TestBrowserThread ui_thread_
;
168 // PasswordStore, WDS schedule work on this thread.
169 content::TestBrowserThread db_thread_
;
171 base::ScopedTempDir temp_dir_
;
172 scoped_ptr
<TestingProfile
> profile_
;
173 scoped_refptr
<PasswordWebDataService
> wds_
;
174 scoped_refptr
<WebDatabaseService
> wdbs_
;
175 scoped_refptr
<PasswordStore
> store_
;
178 ACTION(STLDeleteElements0
) {
179 STLDeleteContainerPointers(arg0
.begin(), arg0
.end());
182 ACTION(QuitUIMessageLoop
) {
183 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
184 base::MessageLoop::current()->Quit();
187 MATCHER(EmptyWDResult
, "") {
188 return static_cast<const WDResult
<std::vector
<PasswordForm
*> >*>(
189 arg
)->GetValue().empty();
192 // Hangs flakily, http://crbug.com/71385.
193 TEST_F(PasswordStoreWinTest
, DISABLED_ConvertIE7Login
) {
194 IE7PasswordInfo password_info
;
195 ASSERT_TRUE(CreateIE7PasswordInfo(L
"http://example.com/origin",
196 base::Time::FromDoubleT(1),
198 // Verify the URL hash
199 ASSERT_EQ(L
"39471418FF5453FEEB3731E382DEB5D53E14FAF9B5",
200 password_info
.url_hash
);
202 // This IE7 password will be retrieved by the GetLogins call.
203 wds_
->AddIE7Login(password_info
);
205 // The WDS schedules tasks to run on the DB thread so we schedule yet another
206 // task to notify us that it's safe to carry on with the test.
207 WaitableEvent
done(false, false);
208 BrowserThread::PostTask(BrowserThread::DB
, FROM_HERE
,
209 base::Bind(&WaitableEvent::Signal
, base::Unretained(&done
)));
212 store_
= CreatePasswordStore();
213 EXPECT_TRUE(store_
->Init(syncer::SyncableService::StartSyncFlare()));
215 MockPasswordStoreConsumer consumer
;
217 // Make sure we quit the MessageLoop even if the test fails.
218 ON_CALL(consumer
, OnGetPasswordStoreResultsConstRef(_
))
219 .WillByDefault(QuitUIMessageLoop());
221 PasswordFormData form_data
= {
222 PasswordForm::SCHEME_HTML
,
223 "http://example.com/",
224 "http://example.com/origin",
225 "http://example.com/action",
233 scoped_ptr
<PasswordForm
> form
=
234 CreatePasswordFormFromDataForTesting(form_data
);
236 // The returned form will not have 'action' or '*_element' fields set. This
237 // is because credentials imported from IE don't have this information.
238 PasswordFormData expected_form_data
= {
239 PasswordForm::SCHEME_HTML
,
240 "http://example.com/",
241 "http://example.com/origin",
250 ScopedVector
<autofill::PasswordForm
> expected_forms
;
251 expected_forms
.push_back(
252 CreatePasswordFormFromDataForTesting(expected_form_data
));
254 // The IE7 password should be returned.
255 EXPECT_CALL(consumer
, OnGetPasswordStoreResultsConstRef(
256 ContainsSamePasswordForms(expected_forms
.get())));
258 store_
->GetLogins(*form
, PasswordStore::DISALLOW_PROMPT
, &consumer
);
259 base::MessageLoop::current()->Run();
262 // Crashy. http://crbug.com/86558
263 TEST_F(PasswordStoreWinTest
, DISABLED_OutstandingWDSQueries
) {
264 store_
= CreatePasswordStore();
265 EXPECT_TRUE(store_
->Init(syncer::SyncableService::StartSyncFlare()));
267 PasswordFormData form_data
= {
268 PasswordForm::SCHEME_HTML
,
269 "http://example.com/",
270 "http://example.com/origin",
271 "http://example.com/action",
279 scoped_ptr
<PasswordForm
> form
=
280 CreatePasswordFormFromDataForTesting(form_data
);
282 MockPasswordStoreConsumer consumer
;
283 store_
->GetLogins(*form
, PasswordStore::DISALLOW_PROMPT
, &consumer
);
285 // Release the PSW and the WDS before the query can return.
290 base::MessageLoop::current()->RunUntilIdle();
293 // Hangs flakily, see http://crbug.com/43836.
294 TEST_F(PasswordStoreWinTest
, DISABLED_MultipleWDSQueriesOnDifferentThreads
) {
295 IE7PasswordInfo password_info
;
296 ASSERT_TRUE(CreateIE7PasswordInfo(L
"http://example.com/origin",
297 base::Time::FromDoubleT(1),
299 wds_
->AddIE7Login(password_info
);
301 // The WDS schedules tasks to run on the DB thread so we schedule yet another
302 // task to notify us that it's safe to carry on with the test.
303 WaitableEvent
done(false, false);
304 BrowserThread::PostTask(BrowserThread::DB
, FROM_HERE
,
305 base::Bind(&WaitableEvent::Signal
, base::Unretained(&done
)));
308 store_
= CreatePasswordStore();
309 EXPECT_TRUE(store_
->Init(syncer::SyncableService::StartSyncFlare()));
311 MockPasswordStoreConsumer password_consumer
;
312 // Make sure we quit the MessageLoop even if the test fails.
313 ON_CALL(password_consumer
, OnGetPasswordStoreResultsConstRef(_
))
314 .WillByDefault(QuitUIMessageLoop());
316 PasswordFormData form_data
= {
317 PasswordForm::SCHEME_HTML
,
318 "http://example.com/",
319 "http://example.com/origin",
320 "http://example.com/action",
328 scoped_ptr
<PasswordForm
> form
=
329 CreatePasswordFormFromDataForTesting(form_data
);
331 PasswordFormData expected_form_data
= {
332 PasswordForm::SCHEME_HTML
,
333 "http://example.com/",
334 "http://example.com/origin",
335 "http://example.com/action",
343 ScopedVector
<autofill::PasswordForm
> expected_forms
;
344 expected_forms
.push_back(
345 CreatePasswordFormFromDataForTesting(expected_form_data
));
347 // The IE7 password should be returned.
348 EXPECT_CALL(password_consumer
,
349 OnGetPasswordStoreResultsConstRef(
350 ContainsSamePasswordForms(expected_forms
.get())));
352 store_
->GetLogins(*form
, PasswordStore::DISALLOW_PROMPT
, &password_consumer
);
354 MockWebDataServiceConsumer wds_consumer
;
356 EXPECT_CALL(wds_consumer
, OnWebDataServiceRequestDone(_
, _
))
357 .WillOnce(QuitUIMessageLoop());
359 wds_
->GetIE7Login(password_info
, &wds_consumer
);
361 // Run the MessageLoop twice: once for the GetIE7Login that PasswordStoreWin
362 // schedules on the DB thread and once for the one we just scheduled on the UI
364 base::MessageLoop::current()->Run();
365 base::MessageLoop::current()->Run();
368 TEST_F(PasswordStoreWinTest
, EmptyLogins
) {
369 store_
= CreatePasswordStore();
370 store_
->Init(syncer::SyncableService::StartSyncFlare());
372 PasswordFormData form_data
= {
373 PasswordForm::SCHEME_HTML
,
374 "http://example.com/",
375 "http://example.com/origin",
376 "http://example.com/action",
384 scoped_ptr
<PasswordForm
> form
=
385 CreatePasswordFormFromDataForTesting(form_data
);
387 MockPasswordStoreConsumer consumer
;
389 // Make sure we quit the MessageLoop even if the test fails.
390 ON_CALL(consumer
, OnGetPasswordStoreResultsConstRef(_
))
391 .WillByDefault(QuitUIMessageLoop());
393 EXPECT_CALL(consumer
, OnGetPasswordStoreResultsConstRef(IsEmpty()));
395 store_
->GetLogins(*form
, PasswordStore::DISALLOW_PROMPT
, &consumer
);
396 base::MessageLoop::current()->Run();
399 TEST_F(PasswordStoreWinTest
, EmptyBlacklistLogins
) {
400 store_
= CreatePasswordStore();
401 store_
->Init(syncer::SyncableService::StartSyncFlare());
403 MockPasswordStoreConsumer consumer
;
405 // Make sure we quit the MessageLoop even if the test fails.
406 ON_CALL(consumer
, OnGetPasswordStoreResultsConstRef(_
))
407 .WillByDefault(QuitUIMessageLoop());
409 EXPECT_CALL(consumer
, OnGetPasswordStoreResultsConstRef(IsEmpty()));
411 store_
->GetBlacklistLogins(&consumer
);
412 base::MessageLoop::current()->Run();
415 TEST_F(PasswordStoreWinTest
, EmptyAutofillableLogins
) {
416 store_
= CreatePasswordStore();
417 store_
->Init(syncer::SyncableService::StartSyncFlare());
419 MockPasswordStoreConsumer consumer
;
421 // Make sure we quit the MessageLoop even if the test fails.
422 ON_CALL(consumer
, OnGetPasswordStoreResultsConstRef(_
))
423 .WillByDefault(QuitUIMessageLoop());
425 EXPECT_CALL(consumer
, OnGetPasswordStoreResultsConstRef(IsEmpty()));
427 store_
->GetAutofillableLogins(&consumer
);
428 base::MessageLoop::current()->Run();