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