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
18 using autofill::PasswordForm;
19 using base::WaitableEvent;
20 using testing::_;
21 using testing::DoAll;
22 using testing::WithArg;
23
24 namespace password_manager {
25
26 namespace {
27
28 class MockPasswordStoreConsumer : public PasswordStoreConsumer {
29 public:
30 MOCK_METHOD1(OnGetPasswordStoreResults,
31 void(const std::vector<PasswordForm*>&));
32 };
33
34 class StartSyncFlareMock {
35 public:
StartSyncFlareMock()36 StartSyncFlareMock() {}
~StartSyncFlareMock()37 ~StartSyncFlareMock() {}
38
39 MOCK_METHOD1(StartSyncFlare, void(syncer::ModelType));
40 };
41
42 } // namespace
43
44 class PasswordStoreTest : public testing::Test {
45 protected:
SetUp()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
TearDown()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
ACTION(STLDeleteElements0)62 ACTION(STLDeleteElements0) {
63 STLDeleteContainerPointers(arg0.begin(), arg0.end());
64 }
65
TEST_F(PasswordStoreTest,IgnoreOldWwwGoogleLogins)66 TEST_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
TEST_F(PasswordStoreTest,StartSyncFlare)196 TEST_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 PasswordForm form;
206 EXPECT_CALL(mock, StartSyncFlare(syncer::PASSWORDS));
207 store->AddLogin(form);
208 base::MessageLoop::current()->RunUntilIdle();
209 }
210 store->Shutdown();
211 base::MessageLoop::current()->RunUntilIdle();
212 }
213
214 } // namespace password_manager
215