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