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_form_data.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::LoginDatabase;
37 using password_manager::ContainsAllPasswordForms;
38 using password_manager::PasswordFormData;
39 using password_manager::PasswordStore;
40 using password_manager::PasswordStoreConsumer;
43 using testing::WithArg;
47 class MockPasswordStoreConsumer : public PasswordStoreConsumer {
49 MOCK_METHOD1(OnGetPasswordStoreResults,
50 void(const std::vector<autofill::PasswordForm*>&));
53 class MockWebDataServiceConsumer : public WebDataServiceConsumer {
55 MOCK_METHOD2(OnWebDataServiceRequestDone,
56 void(PasswordWebDataService::Handle, const WDTypedResult*));
59 } // anonymous namespace
61 typedef std::vector<PasswordForm*> VectorOfForms;
63 class PasswordStoreWinTest : public testing::Test {
65 PasswordStoreWinTest()
66 : ui_thread_(BrowserThread::UI, &message_loop_),
67 db_thread_(BrowserThread::DB) {
70 bool CreateIE7PasswordInfo(const std::wstring& url, const base::Time& created,
71 IE7PasswordInfo* info) {
72 // Copied from chrome/browser/importer/importer_unittest.cc
73 // The username is "abcdefgh" and the password "abcdefghijkl".
74 unsigned char data[] = "\x0c\x00\x00\x00\x38\x00\x00\x00\x2c\x00\x00\x00"
75 "\x57\x49\x43\x4b\x18\x00\x00\x00\x02\x00\x00\x00"
76 "\x67\x00\x72\x00\x01\x00\x00\x00\x00\x00\x00\x00"
77 "\x00\x00\x00\x00\x4e\xfa\x67\x76\x22\x94\xc8\x01"
78 "\x08\x00\x00\x00\x12\x00\x00\x00\x4e\xfa\x67\x76"
79 "\x22\x94\xc8\x01\x0c\x00\x00\x00\x61\x00\x62\x00"
80 "\x63\x00\x64\x00\x65\x00\x66\x00\x67\x00\x68\x00"
81 "\x00\x00\x61\x00\x62\x00\x63\x00\x64\x00\x65\x00"
82 "\x66\x00\x67\x00\x68\x00\x69\x00\x6a\x00\x6b\x00"
84 DATA_BLOB input = {0};
85 DATA_BLOB url_key = {0};
86 DATA_BLOB output = {0};
89 input.cbData = sizeof(data);
91 url_key.pbData = reinterpret_cast<unsigned char*>(
92 const_cast<wchar_t*>(url.data()));
93 url_key.cbData = static_cast<DWORD>((url.size() + 1) *
94 sizeof(std::wstring::value_type));
96 if (!CryptProtectData(&input, NULL, &url_key, NULL, NULL,
97 CRYPTPROTECT_UI_FORBIDDEN, &output))
100 std::vector<unsigned char> encrypted_data;
101 encrypted_data.resize(output.cbData);
102 memcpy(&encrypted_data.front(), output.pbData, output.cbData);
104 LocalFree(output.pbData);
106 info->url_hash = ie7_password::GetUrlHash(url);
107 info->encrypted_data = encrypted_data;
108 info->date_created = created;
113 virtual void SetUp() {
114 ASSERT_TRUE(db_thread_.Start());
115 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
117 profile_.reset(new TestingProfile());
119 login_db_.reset(new LoginDatabase());
120 ASSERT_TRUE(login_db_->Init(temp_dir_.path().Append(
121 FILE_PATH_LITERAL("login_test"))));
122 base::FilePath path = temp_dir_.path().AppendASCII("web_data_test");
123 wdbs_ = new WebDatabaseService(path,
124 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI),
125 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB));
126 // Need to add at least one table so the database gets created.
127 wdbs_->AddTable(scoped_ptr<WebDatabaseTable>(new LoginsTable()));
128 wdbs_->LoadDatabase();
129 wds_ = new PasswordWebDataService(
131 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI),
132 WebDataServiceBase::ProfileErrorCallback());
136 virtual void TearDown() {
139 wds_->ShutdownOnUIThread();
140 wdbs_->ShutdownDatabase();
143 base::WaitableEvent done(false, false);
144 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
145 base::Bind(&base::WaitableEvent::Signal, base::Unretained(&done)));
147 base::MessageLoop::current()->PostTask(FROM_HERE,
148 base::MessageLoop::QuitClosure());
149 base::MessageLoop::current()->Run();
153 PasswordStoreWin* CreatePasswordStore() {
154 return new PasswordStoreWin(
155 base::MessageLoopProxy::current(),
156 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB),
161 base::MessageLoopForUI message_loop_;
162 content::TestBrowserThread ui_thread_;
163 // PasswordStore, WDS schedule work on this thread.
164 content::TestBrowserThread db_thread_;
166 base::ScopedTempDir temp_dir_;
167 scoped_ptr<TestingProfile> profile_;
168 scoped_ptr<LoginDatabase> login_db_;
169 scoped_refptr<PasswordWebDataService> wds_;
170 scoped_refptr<WebDatabaseService> wdbs_;
171 scoped_refptr<PasswordStore> store_;
174 ACTION(STLDeleteElements0) {
175 STLDeleteContainerPointers(arg0.begin(), arg0.end());
178 ACTION(QuitUIMessageLoop) {
179 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
180 base::MessageLoop::current()->Quit();
183 MATCHER(EmptyWDResult, "") {
184 return static_cast<const WDResult<std::vector<PasswordForm*> >*>(
185 arg)->GetValue().empty();
188 // Hangs flakily, http://crbug.com/71385.
189 TEST_F(PasswordStoreWinTest, DISABLED_ConvertIE7Login) {
190 IE7PasswordInfo password_info;
191 ASSERT_TRUE(CreateIE7PasswordInfo(L"http://example.com/origin",
192 base::Time::FromDoubleT(1),
194 // Verify the URL hash
195 ASSERT_EQ(L"39471418FF5453FEEB3731E382DEB5D53E14FAF9B5",
196 password_info.url_hash);
198 // This IE7 password will be retrieved by the GetLogins call.
199 wds_->AddIE7Login(password_info);
201 // The WDS schedules tasks to run on the DB thread so we schedule yet another
202 // task to notify us that it's safe to carry on with the test.
203 WaitableEvent done(false, false);
204 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
205 base::Bind(&WaitableEvent::Signal, base::Unretained(&done)));
208 store_ = CreatePasswordStore();
209 EXPECT_TRUE(store_->Init(syncer::SyncableService::StartSyncFlare()));
211 MockPasswordStoreConsumer consumer;
213 // Make sure we quit the MessageLoop even if the test fails.
214 ON_CALL(consumer, OnGetPasswordStoreResults(_))
215 .WillByDefault(QuitUIMessageLoop());
217 PasswordFormData form_data = {
218 PasswordForm::SCHEME_HTML,
219 "http://example.com/",
220 "http://example.com/origin",
221 "http://example.com/action",
229 scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(form_data));
231 // The returned form will not have 'action' or '*_element' fields set. This
232 // is because credentials imported from IE don't have this information.
233 PasswordFormData expected_form_data = {
234 PasswordForm::SCHEME_HTML,
235 "http://example.com/",
236 "http://example.com/origin",
245 std::vector<PasswordForm*> forms;
246 forms.push_back(CreatePasswordFormFromData(expected_form_data));
248 // The IE7 password should be returned.
249 EXPECT_CALL(consumer,
250 OnGetPasswordStoreResults(ContainsAllPasswordForms(forms)))
251 .WillOnce(QuitUIMessageLoop());
253 store_->GetLogins(*form, PasswordStore::DISALLOW_PROMPT, &consumer);
254 base::MessageLoop::current()->Run();
256 STLDeleteElements(&forms);
259 // Crashy. http://crbug.com/86558
260 TEST_F(PasswordStoreWinTest, DISABLED_OutstandingWDSQueries) {
261 store_ = CreatePasswordStore();
262 EXPECT_TRUE(store_->Init(syncer::SyncableService::StartSyncFlare()));
264 PasswordFormData form_data = {
265 PasswordForm::SCHEME_HTML,
266 "http://example.com/",
267 "http://example.com/origin",
268 "http://example.com/action",
276 scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(form_data));
278 MockPasswordStoreConsumer consumer;
279 store_->GetLogins(*form, PasswordStore::DISALLOW_PROMPT, &consumer);
281 // Release the PSW and the WDS before the query can return.
286 base::MessageLoop::current()->RunUntilIdle();
289 // Hangs flakily, see http://crbug.com/43836.
290 TEST_F(PasswordStoreWinTest, DISABLED_MultipleWDSQueriesOnDifferentThreads) {
291 IE7PasswordInfo password_info;
292 ASSERT_TRUE(CreateIE7PasswordInfo(L"http://example.com/origin",
293 base::Time::FromDoubleT(1),
295 wds_->AddIE7Login(password_info);
297 // The WDS schedules tasks to run on the DB thread so we schedule yet another
298 // task to notify us that it's safe to carry on with the test.
299 WaitableEvent done(false, false);
300 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
301 base::Bind(&WaitableEvent::Signal, base::Unretained(&done)));
304 store_ = CreatePasswordStore();
305 EXPECT_TRUE(store_->Init(syncer::SyncableService::StartSyncFlare()));
307 MockPasswordStoreConsumer password_consumer;
308 // Make sure we quit the MessageLoop even if the test fails.
309 ON_CALL(password_consumer, OnGetPasswordStoreResults(_))
310 .WillByDefault(QuitUIMessageLoop());
312 PasswordFormData form_data = {
313 PasswordForm::SCHEME_HTML,
314 "http://example.com/",
315 "http://example.com/origin",
316 "http://example.com/action",
324 scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(form_data));
326 PasswordFormData expected_form_data = {
327 PasswordForm::SCHEME_HTML,
328 "http://example.com/",
329 "http://example.com/origin",
330 "http://example.com/action",
338 std::vector<PasswordForm*> forms;
339 forms.push_back(CreatePasswordFormFromData(expected_form_data));
341 // The IE7 password should be returned.
342 EXPECT_CALL(password_consumer,
343 OnGetPasswordStoreResults(ContainsAllPasswordForms(forms)))
344 .WillOnce(QuitUIMessageLoop());
346 store_->GetLogins(*form, PasswordStore::DISALLOW_PROMPT, &password_consumer);
348 MockWebDataServiceConsumer wds_consumer;
350 EXPECT_CALL(wds_consumer,
351 OnWebDataServiceRequestDone(_, _))
352 .WillOnce(QuitUIMessageLoop());
354 wds_->GetIE7Login(password_info, &wds_consumer);
356 // Run the MessageLoop twice: once for the GetIE7Login that PasswordStoreWin
357 // schedules on the DB thread and once for the one we just scheduled on the UI
359 base::MessageLoop::current()->Run();
360 base::MessageLoop::current()->Run();
362 STLDeleteElements(&forms);
365 TEST_F(PasswordStoreWinTest, EmptyLogins) {
366 store_ = CreatePasswordStore();
367 store_->Init(syncer::SyncableService::StartSyncFlare());
369 PasswordFormData form_data = {
370 PasswordForm::SCHEME_HTML,
371 "http://example.com/",
372 "http://example.com/origin",
373 "http://example.com/action",
381 scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(form_data));
383 MockPasswordStoreConsumer consumer;
385 // Make sure we quit the MessageLoop even if the test fails.
386 ON_CALL(consumer, OnGetPasswordStoreResults(_))
387 .WillByDefault(QuitUIMessageLoop());
389 VectorOfForms expect_none;
390 // expect that we get no results;
391 EXPECT_CALL(consumer,
392 OnGetPasswordStoreResults(ContainsAllPasswordForms(expect_none)))
393 .WillOnce(DoAll(WithArg<0>(STLDeleteElements0()), QuitUIMessageLoop()));
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, OnGetPasswordStoreResults(_))
407 .WillByDefault(QuitUIMessageLoop());
409 VectorOfForms expect_none;
410 // expect that we get no results;
413 OnGetPasswordStoreResults(ContainsAllPasswordForms(expect_none)))
414 .WillOnce(DoAll(WithArg<0>(STLDeleteElements0()), QuitUIMessageLoop()));
416 store_->GetBlacklistLogins(&consumer);
417 base::MessageLoop::current()->Run();
420 TEST_F(PasswordStoreWinTest, EmptyAutofillableLogins) {
421 store_ = CreatePasswordStore();
422 store_->Init(syncer::SyncableService::StartSyncFlare());
424 MockPasswordStoreConsumer consumer;
426 // Make sure we quit the MessageLoop even if the test fails.
427 ON_CALL(consumer, OnGetPasswordStoreResults(_))
428 .WillByDefault(QuitUIMessageLoop());
430 VectorOfForms expect_none;
431 // expect that we get no results;
434 OnGetPasswordStoreResults(ContainsAllPasswordForms(expect_none)))
435 .WillOnce(DoAll(WithArg<0>(STLDeleteElements0()), QuitUIMessageLoop()));
437 store_->GetAutofillableLogins(&consumer);
438 base::MessageLoop::current()->Run();