password_store_unittest.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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 "base/basictypes.h"
6#include "base/bind.h"
7#include "base/files/scoped_temp_dir.h"
8#include "base/stl_util.h"
9#include "base/strings/string_util.h"
10#include "base/synchronization/waitable_event.h"
11#include "base/time/time.h"
12#include "components/password_manager/core/browser/password_form_data.h"
13#include "components/password_manager/core/browser/password_store_consumer.h"
14#include "components/password_manager/core/browser/password_store_default.h"
15#include "testing/gmock/include/gmock/gmock.h"
16#include "testing/gtest/include/gtest/gtest.h"
17
18using autofill::PasswordForm;
19using base::WaitableEvent;
20using testing::_;
21using testing::DoAll;
22using testing::WithArg;
23
24namespace {
25
26class MockPasswordStoreConsumer : public PasswordStoreConsumer {
27 public:
28  MOCK_METHOD1(OnGetPasswordStoreResults,
29               void(const std::vector<PasswordForm*>&));
30};
31
32class StartSyncFlareMock {
33 public:
34  StartSyncFlareMock() {}
35  ~StartSyncFlareMock() {}
36
37  MOCK_METHOD1(StartSyncFlare, void(syncer::ModelType));
38};
39
40}  // namespace
41
42class PasswordStoreTest : public testing::Test {
43 protected:
44  virtual void SetUp() OVERRIDE {
45    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
46    login_db_.reset(new LoginDatabase());
47    ASSERT_TRUE(login_db_->Init(temp_dir_.path().Append(
48        FILE_PATH_LITERAL("login_test"))));
49  }
50
51  virtual void TearDown() OVERRIDE {
52    ASSERT_TRUE(temp_dir_.Delete());
53  }
54
55  base::MessageLoopForUI message_loop_;
56  scoped_ptr<LoginDatabase> login_db_;
57  base::ScopedTempDir temp_dir_;
58};
59
60ACTION(STLDeleteElements0) {
61  STLDeleteContainerPointers(arg0.begin(), arg0.end());
62}
63
64TEST_F(PasswordStoreTest, IgnoreOldWwwGoogleLogins) {
65  scoped_refptr<PasswordStoreDefault> store(new PasswordStoreDefault(
66      base::MessageLoopProxy::current(),
67      base::MessageLoopProxy::current(),
68      login_db_.release()));
69  store->Init(syncer::SyncableService::StartSyncFlare());
70
71  const time_t cutoff = 1325376000;  // 00:00 Jan 1 2012 UTC
72  // The passwords are all empty because PasswordStoreDefault doesn't store the
73  // actual passwords on OS X (they're stored in the Keychain instead). We could
74  // special-case it, but it's easier to just have empty passwords.
75  static const PasswordFormData form_data[] = {
76    // A form on https://www.google.com/ older than the cutoff. Will be ignored.
77    { PasswordForm::SCHEME_HTML,
78      "https://www.google.com",
79      "https://www.google.com/origin",
80      "https://www.google.com/action",
81      L"submit_element",
82      L"username_element",
83      L"password_element",
84      L"username_value_1",
85      L"",
86      true, true, cutoff - 1 },
87    // A form on https://www.google.com/ older than the cutoff. Will be ignored.
88    { PasswordForm::SCHEME_HTML,
89      "https://www.google.com",
90      "https://www.google.com/origin",
91      "https://www.google.com/action",
92      L"submit_element",
93      L"username_element",
94      L"password_element",
95      L"username_value_2",
96      L"",
97      true, true, cutoff - 1 },
98    // A form on https://www.google.com/ newer than the cutoff.
99    { PasswordForm::SCHEME_HTML,
100      "https://www.google.com",
101      "https://www.google.com/origin",
102      "https://www.google.com/action",
103      L"submit_element",
104      L"username_element",
105      L"password_element",
106      L"username_value_3",
107      L"",
108      true, true, cutoff + 1 },
109    // A form on https://accounts.google.com/ older than the cutoff.
110    { PasswordForm::SCHEME_HTML,
111      "https://accounts.google.com",
112      "https://accounts.google.com/origin",
113      "https://accounts.google.com/action",
114      L"submit_element",
115      L"username_element",
116      L"password_element",
117      L"username_value",
118      L"",
119      true, true, cutoff - 1 },
120    // A form on http://bar.example.com/ older than the cutoff.
121    { PasswordForm::SCHEME_HTML,
122      "http://bar.example.com",
123      "http://bar.example.com/origin",
124      "http://bar.example.com/action",
125      L"submit_element",
126      L"username_element",
127      L"password_element",
128      L"username_value",
129      L"",
130      true, false, cutoff - 1 },
131  };
132
133  // Build the forms vector and add the forms to the store.
134  std::vector<PasswordForm*> all_forms;
135  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(form_data); ++i) {
136    PasswordForm* form = CreatePasswordFormFromData(form_data[i]);
137    all_forms.push_back(form);
138    store->AddLogin(*form);
139  }
140  base::MessageLoop::current()->RunUntilIdle();
141
142  // We expect to get back only the "recent" www.google.com login.
143  // Theoretically these should never actually exist since there are no longer
144  // any login forms on www.google.com to save, but we technically allow them.
145  // We should not get back the older saved password though.
146  PasswordForm www_google;
147  www_google.scheme = PasswordForm::SCHEME_HTML;
148  www_google.signon_realm = "https://www.google.com";
149  std::vector<PasswordForm*> www_google_expected;
150  www_google_expected.push_back(all_forms[2]);
151
152  // We should still get the accounts.google.com login even though it's older
153  // than our cutoff - this is the new location of all Google login forms.
154  PasswordForm accounts_google;
155  accounts_google.scheme = PasswordForm::SCHEME_HTML;
156  accounts_google.signon_realm = "https://accounts.google.com";
157  std::vector<PasswordForm*> accounts_google_expected;
158  accounts_google_expected.push_back(all_forms[3]);
159
160  // Same thing for a generic saved login.
161  PasswordForm bar_example;
162  bar_example.scheme = PasswordForm::SCHEME_HTML;
163  bar_example.signon_realm = "http://bar.example.com";
164  std::vector<PasswordForm*> bar_example_expected;
165  bar_example_expected.push_back(all_forms[4]);
166
167  MockPasswordStoreConsumer consumer;
168
169  // Expect the appropriate replies, as above, in reverse order than we will
170  // issue the queries. Each retires on saturation to avoid matcher spew.
171  EXPECT_CALL(consumer,
172      OnGetPasswordStoreResults(ContainsAllPasswordForms(bar_example_expected)))
173      .WillOnce(WithArg<0>(STLDeleteElements0())).RetiresOnSaturation();
174  EXPECT_CALL(consumer,
175      OnGetPasswordStoreResults(
176          ContainsAllPasswordForms(accounts_google_expected)))
177      .WillOnce(WithArg<0>(STLDeleteElements0())).RetiresOnSaturation();
178  EXPECT_CALL(consumer,
179      OnGetPasswordStoreResults(
180          ContainsAllPasswordForms(www_google_expected)))
181      .WillOnce(WithArg<0>(STLDeleteElements0())).RetiresOnSaturation();
182
183  store->GetLogins(www_google, PasswordStore::ALLOW_PROMPT, &consumer);
184  store->GetLogins(accounts_google, PasswordStore::ALLOW_PROMPT, &consumer);
185  store->GetLogins(bar_example, PasswordStore::ALLOW_PROMPT, &consumer);
186
187  base::MessageLoop::current()->RunUntilIdle();
188
189  STLDeleteElements(&all_forms);
190  store->Shutdown();
191  base::MessageLoop::current()->RunUntilIdle();
192}
193
194TEST_F(PasswordStoreTest, StartSyncFlare) {
195  scoped_refptr<PasswordStoreDefault> store(new PasswordStoreDefault(
196      base::MessageLoopProxy::current(),
197      base::MessageLoopProxy::current(),
198      login_db_.release()));
199  StartSyncFlareMock mock;
200  store->Init(base::Bind(&StartSyncFlareMock::StartSyncFlare,
201                         base::Unretained(&mock)));
202  {
203    PasswordForm form;
204    EXPECT_CALL(mock, StartSyncFlare(syncer::PASSWORDS));
205    store->AddLogin(form);
206    base::MessageLoop::current()->RunUntilIdle();
207  }
208  store->Shutdown();
209  base::MessageLoop::current()->RunUntilIdle();
210}
211