password_store_win_unittest.cc revision 58537e28ecd584eab876aee8be7156509866d23a
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. 4 5#include <windows.h> 6#include <wincrypt.h> 7#include <string> 8#include <vector> 9 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_form_data.h" 20#include "chrome/browser/password_manager/password_store_consumer.h" 21#include "chrome/browser/password_manager/password_store_win.h" 22#include "chrome/browser/webdata/logins_table.h" 23#include "chrome/browser/webdata/web_data_service.h" 24#include "chrome/common/pref_names.h" 25#include "chrome/test/base/testing_profile.h" 26#include "components/webdata/common/web_database_service.h" 27#include "components/webdata/encryptor/ie7_password.h" 28#include "content/public/test/test_browser_thread.h" 29#include "testing/gmock/include/gmock/gmock.h" 30#include "testing/gtest/include/gtest/gtest.h" 31 32using autofill::PasswordForm; 33using base::WaitableEvent; 34using content::BrowserThread; 35using testing::_; 36using testing::DoAll; 37using testing::WithArg; 38 39namespace { 40 41class MockPasswordStoreConsumer : public PasswordStoreConsumer { 42 public: 43 MOCK_METHOD2(OnPasswordStoreRequestDone, 44 void(CancelableRequestProvider::Handle, 45 const std::vector<autofill::PasswordForm*>&)); 46 MOCK_METHOD1(OnGetPasswordStoreResults, 47 void(const std::vector<autofill::PasswordForm*>&)); 48}; 49 50class MockWebDataServiceConsumer : public WebDataServiceConsumer { 51public: 52 MOCK_METHOD2(OnWebDataServiceRequestDone, 53 void(WebDataService::Handle, const WDTypedResult*)); 54}; 55 56} // anonymous namespace 57 58typedef std::vector<PasswordForm*> VectorOfForms; 59 60class PasswordStoreWinTest : public testing::Test { 61 protected: 62 PasswordStoreWinTest() 63 : ui_thread_(BrowserThread::UI, &message_loop_), 64 db_thread_(BrowserThread::DB) { 65 } 66 67 bool CreateIE7PasswordInfo(const std::wstring& url, const base::Time& created, 68 IE7PasswordInfo* info) { 69 // Copied from chrome/browser/importer/importer_unittest.cc 70 // The username is "abcdefgh" and the password "abcdefghijkl". 71 unsigned char data[] = "\x0c\x00\x00\x00\x38\x00\x00\x00\x2c\x00\x00\x00" 72 "\x57\x49\x43\x4b\x18\x00\x00\x00\x02\x00\x00\x00" 73 "\x67\x00\x72\x00\x01\x00\x00\x00\x00\x00\x00\x00" 74 "\x00\x00\x00\x00\x4e\xfa\x67\x76\x22\x94\xc8\x01" 75 "\x08\x00\x00\x00\x12\x00\x00\x00\x4e\xfa\x67\x76" 76 "\x22\x94\xc8\x01\x0c\x00\x00\x00\x61\x00\x62\x00" 77 "\x63\x00\x64\x00\x65\x00\x66\x00\x67\x00\x68\x00" 78 "\x00\x00\x61\x00\x62\x00\x63\x00\x64\x00\x65\x00" 79 "\x66\x00\x67\x00\x68\x00\x69\x00\x6a\x00\x6b\x00" 80 "\x6c\x00\x00\x00"; 81 DATA_BLOB input = {0}; 82 DATA_BLOB url_key = {0}; 83 DATA_BLOB output = {0}; 84 85 input.pbData = data; 86 input.cbData = sizeof(data); 87 88 url_key.pbData = reinterpret_cast<unsigned char*>( 89 const_cast<wchar_t*>(url.data())); 90 url_key.cbData = static_cast<DWORD>((url.size() + 1) * 91 sizeof(std::wstring::value_type)); 92 93 if (!CryptProtectData(&input, NULL, &url_key, NULL, NULL, 94 CRYPTPROTECT_UI_FORBIDDEN, &output)) 95 return false; 96 97 std::vector<unsigned char> encrypted_data; 98 encrypted_data.resize(output.cbData); 99 memcpy(&encrypted_data.front(), output.pbData, output.cbData); 100 101 LocalFree(output.pbData); 102 103 info->url_hash = ie7_password::GetUrlHash(url); 104 info->encrypted_data = encrypted_data; 105 info->date_created = created; 106 107 return true; 108 } 109 110 virtual void SetUp() { 111 ASSERT_TRUE(db_thread_.Start()); 112 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 113 114 profile_.reset(new TestingProfile()); 115 116 login_db_.reset(new LoginDatabase()); 117 ASSERT_TRUE(login_db_->Init(temp_dir_.path().Append( 118 FILE_PATH_LITERAL("login_test")))); 119 base::FilePath path = temp_dir_.path().AppendASCII("web_data_test"); 120 wdbs_ = new WebDatabaseService(path, 121 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI), 122 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB)); 123 // Need to add at least one table so the database gets created. 124 wdbs_->AddTable(scoped_ptr<WebDatabaseTable>(new LoginsTable())); 125 wdbs_->LoadDatabase(); 126 wds_ = new WebDataService(wdbs_, 127 WebDataServiceBase::ProfileErrorCallback()); 128 wds_->Init(); 129 } 130 131 virtual void TearDown() { 132 if (store_.get()) 133 store_->ShutdownOnUIThread(); 134 wds_->ShutdownOnUIThread(); 135 wdbs_->ShutdownDatabase(); 136 wds_ = NULL; 137 wdbs_ = NULL; 138 base::WaitableEvent done(false, false); 139 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, 140 base::Bind(&base::WaitableEvent::Signal, base::Unretained(&done))); 141 done.Wait(); 142 base::MessageLoop::current()->PostTask(FROM_HERE, 143 base::MessageLoop::QuitClosure()); 144 base::MessageLoop::current()->Run(); 145 db_thread_.Stop(); 146 } 147 148 base::MessageLoopForUI message_loop_; 149 content::TestBrowserThread ui_thread_; 150 // PasswordStore, WDS schedule work on this thread. 151 content::TestBrowserThread db_thread_; 152 153 scoped_ptr<LoginDatabase> login_db_; 154 scoped_ptr<TestingProfile> profile_; 155 scoped_refptr<WebDataService> wds_; 156 scoped_refptr<WebDatabaseService> wdbs_; 157 scoped_refptr<PasswordStore> store_; 158 base::ScopedTempDir temp_dir_; 159}; 160 161ACTION(STLDeleteElements0) { 162 STLDeleteContainerPointers(arg0.begin(), arg0.end()); 163} 164 165ACTION(QuitUIMessageLoop) { 166 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 167 base::MessageLoop::current()->Quit(); 168} 169 170MATCHER(EmptyWDResult, "") { 171 return static_cast<const WDResult<std::vector<PasswordForm*> >*>( 172 arg)->GetValue().empty(); 173} 174 175// Hangs flakily, http://crbug.com/71385. 176TEST_F(PasswordStoreWinTest, DISABLED_ConvertIE7Login) { 177 IE7PasswordInfo password_info; 178 ASSERT_TRUE(CreateIE7PasswordInfo(L"http://example.com/origin", 179 base::Time::FromDoubleT(1), 180 &password_info)); 181 // Verify the URL hash 182 ASSERT_EQ(L"39471418FF5453FEEB3731E382DEB5D53E14FAF9B5", 183 password_info.url_hash); 184 185 // This IE7 password will be retrieved by the GetLogins call. 186 wds_->AddIE7Login(password_info); 187 188 // The WDS schedules tasks to run on the DB thread so we schedule yet another 189 // task to notify us that it's safe to carry on with the test. 190 WaitableEvent done(false, false); 191 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, 192 base::Bind(&WaitableEvent::Signal, base::Unretained(&done))); 193 done.Wait(); 194 195 store_ = new PasswordStoreWin(login_db_.release(), profile_.get(), 196 wds_.get()); 197 EXPECT_TRUE(store_->Init()); 198 199 MockPasswordStoreConsumer consumer; 200 201 // Make sure we quit the MessageLoop even if the test fails. 202 ON_CALL(consumer, OnGetPasswordStoreResults(_)) 203 .WillByDefault(QuitUIMessageLoop()); 204 205 PasswordFormData form_data = { 206 PasswordForm::SCHEME_HTML, 207 "http://example.com/", 208 "http://example.com/origin", 209 "http://example.com/action", 210 L"submit_element", 211 L"username_element", 212 L"password_element", 213 L"", 214 L"", 215 true, false, 1, 216 }; 217 scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(form_data)); 218 219 PasswordFormData expected_form_data = { 220 PasswordForm::SCHEME_HTML, 221 "http://example.com/", 222 "http://example.com/origin", 223 "http://example.com/action", 224 L"submit_element", 225 L"username_element", 226 L"password_element", 227 L"abcdefgh", 228 L"abcdefghijkl", 229 true, false, 1, 230 }; 231 std::vector<PasswordForm*> forms; 232 forms.push_back(CreatePasswordFormFromData(expected_form_data)); 233 234 // The IE7 password should be returned. 235 EXPECT_CALL(consumer, 236 OnGetPasswordStoreResults(ContainsAllPasswordForms(forms))) 237 .WillOnce(QuitUIMessageLoop()); 238 239 store_->GetLogins(*form, &consumer); 240 base::MessageLoop::current()->Run(); 241 242 STLDeleteElements(&forms); 243} 244 245// Crashy. http://crbug.com/86558 246TEST_F(PasswordStoreWinTest, DISABLED_OutstandingWDSQueries) { 247 store_ = new PasswordStoreWin(login_db_.release(), profile_.get(), 248 wds_.get()); 249 EXPECT_TRUE(store_->Init()); 250 251 PasswordFormData form_data = { 252 PasswordForm::SCHEME_HTML, 253 "http://example.com/", 254 "http://example.com/origin", 255 "http://example.com/action", 256 L"submit_element", 257 L"username_element", 258 L"password_element", 259 L"", 260 L"", 261 true, false, 1, 262 }; 263 scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(form_data)); 264 265 MockPasswordStoreConsumer consumer; 266 store_->GetLogins(*form, &consumer); 267 268 // Release the PSW and the WDS before the query can return. 269 store_->ShutdownOnUIThread(); 270 store_ = NULL; 271 wds_ = NULL; 272 273 base::MessageLoop::current()->RunUntilIdle(); 274} 275 276// Hangs flakily, see http://crbug.com/43836. 277TEST_F(PasswordStoreWinTest, DISABLED_MultipleWDSQueriesOnDifferentThreads) { 278 IE7PasswordInfo password_info; 279 ASSERT_TRUE(CreateIE7PasswordInfo(L"http://example.com/origin", 280 base::Time::FromDoubleT(1), 281 &password_info)); 282 wds_->AddIE7Login(password_info); 283 284 // The WDS schedules tasks to run on the DB thread so we schedule yet another 285 // task to notify us that it's safe to carry on with the test. 286 WaitableEvent done(false, false); 287 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, 288 base::Bind(&WaitableEvent::Signal, base::Unretained(&done))); 289 done.Wait(); 290 291 store_ = new PasswordStoreWin(login_db_.release(), profile_.get(), 292 wds_.get()); 293 EXPECT_TRUE(store_->Init()); 294 295 MockPasswordStoreConsumer password_consumer; 296 // Make sure we quit the MessageLoop even if the test fails. 297 ON_CALL(password_consumer, OnGetPasswordStoreResults(_)) 298 .WillByDefault(QuitUIMessageLoop()); 299 300 PasswordFormData form_data = { 301 PasswordForm::SCHEME_HTML, 302 "http://example.com/", 303 "http://example.com/origin", 304 "http://example.com/action", 305 L"submit_element", 306 L"username_element", 307 L"password_element", 308 L"", 309 L"", 310 true, false, 1, 311 }; 312 scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(form_data)); 313 314 PasswordFormData expected_form_data = { 315 PasswordForm::SCHEME_HTML, 316 "http://example.com/", 317 "http://example.com/origin", 318 "http://example.com/action", 319 L"submit_element", 320 L"username_element", 321 L"password_element", 322 L"abcdefgh", 323 L"abcdefghijkl", 324 true, false, 1, 325 }; 326 std::vector<PasswordForm*> forms; 327 forms.push_back(CreatePasswordFormFromData(expected_form_data)); 328 329 // The IE7 password should be returned. 330 EXPECT_CALL(password_consumer, 331 OnGetPasswordStoreResults(ContainsAllPasswordForms(forms))) 332 .WillOnce(QuitUIMessageLoop()); 333 334 store_->GetLogins(*form, &password_consumer); 335 336 MockWebDataServiceConsumer wds_consumer; 337 338 EXPECT_CALL(wds_consumer, 339 OnWebDataServiceRequestDone(_, _)) 340 .WillOnce(QuitUIMessageLoop()); 341 342 wds_->GetIE7Login(password_info, &wds_consumer); 343 344 // Run the MessageLoop twice: once for the GetIE7Login that PasswordStoreWin 345 // schedules on the DB thread and once for the one we just scheduled on the UI 346 // thread. 347 base::MessageLoop::current()->Run(); 348 base::MessageLoop::current()->Run(); 349 350 STLDeleteElements(&forms); 351} 352 353TEST_F(PasswordStoreWinTest, EmptyLogins) { 354 store_ = new PasswordStoreWin(login_db_.release(), profile_.get(), 355 wds_.get()); 356 store_->Init(); 357 358 PasswordFormData form_data = { 359 PasswordForm::SCHEME_HTML, 360 "http://example.com/", 361 "http://example.com/origin", 362 "http://example.com/action", 363 L"submit_element", 364 L"username_element", 365 L"password_element", 366 L"", 367 L"", 368 true, false, 1, 369 }; 370 scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(form_data)); 371 372 MockPasswordStoreConsumer consumer; 373 374 // Make sure we quit the MessageLoop even if the test fails. 375 ON_CALL(consumer, OnGetPasswordStoreResults(_)) 376 .WillByDefault(QuitUIMessageLoop()); 377 378 VectorOfForms expect_none; 379 // expect that we get no results; 380 EXPECT_CALL(consumer, 381 OnGetPasswordStoreResults(ContainsAllPasswordForms(expect_none))) 382 .WillOnce(DoAll(WithArg<0>(STLDeleteElements0()), QuitUIMessageLoop())); 383 384 store_->GetLogins(*form, &consumer); 385 base::MessageLoop::current()->Run(); 386} 387 388TEST_F(PasswordStoreWinTest, EmptyBlacklistLogins) { 389 store_ = new PasswordStoreWin(login_db_.release(), profile_.get(), 390 wds_.get()); 391 store_->Init(); 392 393 MockPasswordStoreConsumer consumer; 394 395 // Make sure we quit the MessageLoop even if the test fails. 396 ON_CALL(consumer, OnPasswordStoreRequestDone(_, _)) 397 .WillByDefault(QuitUIMessageLoop()); 398 399 VectorOfForms expect_none; 400 // expect that we get no results; 401 EXPECT_CALL( 402 consumer, 403 OnPasswordStoreRequestDone(_, ContainsAllPasswordForms(expect_none))) 404 .WillOnce(DoAll(WithArg<1>(STLDeleteElements0()), QuitUIMessageLoop())); 405 406 store_->GetBlacklistLogins(&consumer); 407 base::MessageLoop::current()->Run(); 408} 409 410TEST_F(PasswordStoreWinTest, EmptyAutofillableLogins) { 411 store_ = new PasswordStoreWin(login_db_.release(), profile_.get(), 412 wds_.get()); 413 store_->Init(); 414 415 MockPasswordStoreConsumer consumer; 416 417 // Make sure we quit the MessageLoop even if the test fails. 418 ON_CALL(consumer, OnPasswordStoreRequestDone(_, _)) 419 .WillByDefault(QuitUIMessageLoop()); 420 421 VectorOfForms expect_none; 422 // expect that we get no results; 423 EXPECT_CALL( 424 consumer, 425 OnPasswordStoreRequestDone(_, ContainsAllPasswordForms(expect_none))) 426 .WillOnce(DoAll(WithArg<1>(STLDeleteElements0()), QuitUIMessageLoop())); 427 428 store_->GetAutofillableLogins(&consumer); 429 base::MessageLoop::current()->Run(); 430} 431