OLD | NEW |
| (Empty) |
1 // Copyright 2014 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 "components/password_manager/content/browser/credential_manager_dispatc
her.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/command_line.h" | |
9 #include "base/prefs/pref_registry_simple.h" | |
10 #include "base/prefs/testing_pref_service.h" | |
11 #include "base/run_loop.h" | |
12 #include "base/strings/string16.h" | |
13 #include "base/strings/utf_string_conversions.h" | |
14 #include "components/password_manager/content/browser/credential_manager_passwor
d_form_manager.h" | |
15 #include "components/password_manager/content/common/credential_manager_messages
.h" | |
16 #include "components/password_manager/content/common/credential_manager_types.h" | |
17 #include "components/password_manager/core/browser/stub_password_manager_client.
h" | |
18 #include "components/password_manager/core/browser/stub_password_manager_driver.
h" | |
19 #include "components/password_manager/core/browser/test_password_store.h" | |
20 #include "components/password_manager/core/common/password_manager_pref_names.h" | |
21 #include "content/public/browser/web_contents.h" | |
22 #include "content/public/test/mock_render_process_host.h" | |
23 #include "content/public/test/test_renderer_host.h" | |
24 #include "testing/gmock/include/gmock/gmock.h" | |
25 #include "testing/gtest/include/gtest/gtest.h" | |
26 | |
27 using content::BrowserContext; | |
28 using content::WebContents; | |
29 | |
30 namespace { | |
31 | |
32 // Chosen by fair dice roll. Guaranteed to be random. | |
33 const int kRequestId = 4; | |
34 | |
35 class TestPasswordManagerClient | |
36 : public password_manager::StubPasswordManagerClient { | |
37 public: | |
38 TestPasswordManagerClient(password_manager::PasswordStore* store) | |
39 : did_prompt_user_to_save_(false), | |
40 did_prompt_user_to_choose_(false), | |
41 is_off_the_record_(false), | |
42 store_(store) { | |
43 prefs_.registry()->RegisterBooleanPref( | |
44 password_manager::prefs::kPasswordManagerAutoSignin, true); | |
45 } | |
46 ~TestPasswordManagerClient() override {} | |
47 | |
48 password_manager::PasswordStore* GetPasswordStore() override { | |
49 return store_; | |
50 } | |
51 | |
52 PrefService* GetPrefs() override { return &prefs_; } | |
53 | |
54 bool PromptUserToSavePassword( | |
55 scoped_ptr<password_manager::PasswordFormManager> manager) override { | |
56 did_prompt_user_to_save_ = true; | |
57 manager_.reset(manager.release()); | |
58 return true; | |
59 } | |
60 | |
61 bool PromptUserToChooseCredentials( | |
62 const std::vector<autofill::PasswordForm*>& local_forms, | |
63 const std::vector<autofill::PasswordForm*>& federated_forms, | |
64 base::Callback<void(const password_manager::CredentialInfo&)> callback) | |
65 override { | |
66 // TODO(melandory): Use ScopedVector instead of std::vector in arguments. | |
67 // ContentCredentialManagerDispatcher::OnGetPasswordStoreResults contains a | |
68 // memory leak because of this. | |
69 EXPECT_FALSE(local_forms.empty() && federated_forms.empty()); | |
70 did_prompt_user_to_choose_ = true; | |
71 ScopedVector<autofill::PasswordForm> local_entries; | |
72 local_entries.assign(local_forms.begin(), local_forms.end()); | |
73 ScopedVector<autofill::PasswordForm> federated_entries; | |
74 federated_entries.assign(federated_forms.begin(), federated_forms.end()); | |
75 password_manager::CredentialInfo info( | |
76 local_forms.empty() ? *federated_forms[0] : *local_entries[0], | |
77 local_forms.empty() | |
78 ? password_manager::CredentialType::CREDENTIAL_TYPE_FEDERATED | |
79 : password_manager::CredentialType::CREDENTIAL_TYPE_LOCAL); | |
80 base::MessageLoop::current()->PostTask(FROM_HERE, | |
81 base::Bind(callback, info)); | |
82 return true; | |
83 } | |
84 | |
85 bool IsOffTheRecord() override { return is_off_the_record_; } | |
86 | |
87 bool did_prompt_user_to_save() const { return did_prompt_user_to_save_; } | |
88 bool did_prompt_user_to_choose() const { return did_prompt_user_to_choose_; } | |
89 | |
90 password_manager::PasswordFormManager* pending_manager() const { | |
91 return manager_.get(); | |
92 } | |
93 | |
94 void set_off_the_record(bool off_the_record) { | |
95 is_off_the_record_ = off_the_record; | |
96 } | |
97 | |
98 void set_zero_click_enabled(bool zero_click_enabled) { | |
99 prefs_.SetBoolean(password_manager::prefs::kPasswordManagerAutoSignin, | |
100 zero_click_enabled); | |
101 } | |
102 | |
103 private: | |
104 TestingPrefServiceSimple prefs_; | |
105 bool did_prompt_user_to_save_; | |
106 bool did_prompt_user_to_choose_; | |
107 bool is_off_the_record_; | |
108 password_manager::PasswordStore* store_; | |
109 scoped_ptr<password_manager::PasswordFormManager> manager_; | |
110 }; | |
111 | |
112 class TestCredentialManagerDispatcher | |
113 : public password_manager::CredentialManagerDispatcher { | |
114 public: | |
115 TestCredentialManagerDispatcher( | |
116 content::WebContents* web_contents, | |
117 password_manager::PasswordManagerClient* client, | |
118 password_manager::PasswordManagerDriver* driver); | |
119 | |
120 private: | |
121 base::WeakPtr<password_manager::PasswordManagerDriver> GetDriver() override; | |
122 | |
123 base::WeakPtr<password_manager::PasswordManagerDriver> driver_; | |
124 }; | |
125 | |
126 TestCredentialManagerDispatcher::TestCredentialManagerDispatcher( | |
127 content::WebContents* web_contents, | |
128 password_manager::PasswordManagerClient* client, | |
129 password_manager::PasswordManagerDriver* driver) | |
130 : CredentialManagerDispatcher(web_contents, client), | |
131 driver_(driver->AsWeakPtr()) { | |
132 } | |
133 | |
134 base::WeakPtr<password_manager::PasswordManagerDriver> | |
135 TestCredentialManagerDispatcher::GetDriver() { | |
136 return driver_; | |
137 } | |
138 | |
139 void RunAllPendingTasks() { | |
140 base::RunLoop run_loop; | |
141 base::MessageLoop::current()->PostTask( | |
142 FROM_HERE, base::MessageLoop::QuitWhenIdleClosure()); | |
143 run_loop.Run(); | |
144 } | |
145 | |
146 } // namespace | |
147 | |
148 namespace password_manager { | |
149 | |
150 class CredentialManagerDispatcherTest | |
151 : public content::RenderViewHostTestHarness { | |
152 public: | |
153 CredentialManagerDispatcherTest() {} | |
154 | |
155 void SetUp() override { | |
156 content::RenderViewHostTestHarness::SetUp(); | |
157 store_ = new TestPasswordStore; | |
158 client_.reset(new TestPasswordManagerClient(store_.get())); | |
159 dispatcher_.reset(new TestCredentialManagerDispatcher( | |
160 web_contents(), client_.get(), &stub_driver_)); | |
161 | |
162 NavigateAndCommit(GURL("https://example.com/test.html")); | |
163 | |
164 form_.username_value = base::ASCIIToUTF16("Username"); | |
165 form_.display_name = base::ASCIIToUTF16("Display Name"); | |
166 form_.password_value = base::ASCIIToUTF16("Password"); | |
167 form_.origin = web_contents()->GetLastCommittedURL().GetOrigin(); | |
168 form_.signon_realm = form_.origin.spec(); | |
169 form_.scheme = autofill::PasswordForm::SCHEME_HTML; | |
170 | |
171 form2_.username_value = base::ASCIIToUTF16("Username 2"); | |
172 form2_.display_name = base::ASCIIToUTF16("Display Name 2"); | |
173 form2_.password_value = base::ASCIIToUTF16("Password 2"); | |
174 form2_.origin = web_contents()->GetLastCommittedURL().GetOrigin(); | |
175 form2_.signon_realm = form_.origin.spec(); | |
176 form2_.scheme = autofill::PasswordForm::SCHEME_HTML; | |
177 | |
178 cross_origin_form_.username_value = base::ASCIIToUTF16("Username"); | |
179 cross_origin_form_.display_name = base::ASCIIToUTF16("Display Name"); | |
180 cross_origin_form_.password_value = base::ASCIIToUTF16("Password"); | |
181 cross_origin_form_.origin = GURL("https://example.net/"); | |
182 cross_origin_form_.signon_realm = cross_origin_form_.origin.spec(); | |
183 cross_origin_form_.scheme = autofill::PasswordForm::SCHEME_HTML; | |
184 | |
185 store_->Clear(); | |
186 EXPECT_TRUE(store_->IsEmpty()); | |
187 } | |
188 | |
189 void TearDown() override { | |
190 store_->Shutdown(); | |
191 content::RenderViewHostTestHarness::TearDown(); | |
192 } | |
193 | |
194 CredentialManagerDispatcher* dispatcher() { return dispatcher_.get(); } | |
195 | |
196 protected: | |
197 autofill::PasswordForm form_; | |
198 autofill::PasswordForm form2_; | |
199 autofill::PasswordForm cross_origin_form_; | |
200 scoped_refptr<TestPasswordStore> store_; | |
201 scoped_ptr<TestPasswordManagerClient> client_; | |
202 StubPasswordManagerDriver stub_driver_; | |
203 scoped_ptr<CredentialManagerDispatcher> dispatcher_; | |
204 }; | |
205 | |
206 TEST_F(CredentialManagerDispatcherTest, CredentialManagerOnNotifyFailedSignIn) { | |
207 CredentialInfo info; | |
208 info.type = CredentialType::CREDENTIAL_TYPE_LOCAL; | |
209 dispatcher()->OnNotifyFailedSignIn(kRequestId, info); | |
210 | |
211 const uint32 kMsgID = CredentialManagerMsg_AcknowledgeFailedSignIn::ID; | |
212 const IPC::Message* message = | |
213 process()->sink().GetFirstMessageMatching(kMsgID); | |
214 EXPECT_TRUE(message); | |
215 process()->sink().ClearMessages(); | |
216 } | |
217 | |
218 TEST_F(CredentialManagerDispatcherTest, CredentialManagerOnNotifySignedIn) { | |
219 CredentialInfo info(form_, | |
220 password_manager::CredentialType::CREDENTIAL_TYPE_LOCAL); | |
221 dispatcher()->OnNotifySignedIn(kRequestId, info); | |
222 | |
223 const uint32 kMsgID = CredentialManagerMsg_AcknowledgeSignedIn::ID; | |
224 const IPC::Message* message = | |
225 process()->sink().GetFirstMessageMatching(kMsgID); | |
226 EXPECT_TRUE(message); | |
227 process()->sink().ClearMessages(); | |
228 | |
229 // Allow the PasswordFormManager to talk to the password store, determine | |
230 // that the form is new, and set it as pending. | |
231 RunAllPendingTasks(); | |
232 | |
233 EXPECT_TRUE(client_->did_prompt_user_to_save()); | |
234 EXPECT_TRUE(client_->pending_manager()->HasCompletedMatching()); | |
235 | |
236 autofill::PasswordForm new_form = | |
237 client_->pending_manager()->pending_credentials(); | |
238 EXPECT_EQ(form_.username_value, new_form.username_value); | |
239 EXPECT_EQ(form_.display_name, new_form.display_name); | |
240 EXPECT_EQ(form_.password_value, new_form.password_value); | |
241 EXPECT_EQ(form_.origin, new_form.origin); | |
242 EXPECT_EQ(form_.signon_realm, new_form.signon_realm); | |
243 EXPECT_EQ(autofill::PasswordForm::SCHEME_HTML, new_form.scheme); | |
244 } | |
245 | |
246 TEST_F(CredentialManagerDispatcherTest, CredentialManagerIncognitoSignedIn) { | |
247 CredentialInfo info(form_, CredentialType::CREDENTIAL_TYPE_LOCAL); | |
248 client_->set_off_the_record(true); | |
249 dispatcher()->OnNotifySignedIn(kRequestId, info); | |
250 | |
251 const uint32 kMsgID = CredentialManagerMsg_AcknowledgeSignedIn::ID; | |
252 const IPC::Message* message = | |
253 process()->sink().GetFirstMessageMatching(kMsgID); | |
254 EXPECT_TRUE(message); | |
255 process()->sink().ClearMessages(); | |
256 | |
257 RunAllPendingTasks(); | |
258 | |
259 EXPECT_FALSE(client_->did_prompt_user_to_save()); | |
260 EXPECT_FALSE(client_->pending_manager()); | |
261 } | |
262 | |
263 TEST_F(CredentialManagerDispatcherTest, CredentialManagerOnNotifySignedOut) { | |
264 store_->AddLogin(form_); | |
265 store_->AddLogin(cross_origin_form_); | |
266 RunAllPendingTasks(); | |
267 | |
268 TestPasswordStore::PasswordMap passwords = store_->stored_passwords(); | |
269 EXPECT_EQ(2U, passwords.size()); | |
270 EXPECT_EQ(1U, passwords[form_.signon_realm].size()); | |
271 EXPECT_EQ(1U, passwords[cross_origin_form_.signon_realm].size()); | |
272 EXPECT_FALSE(passwords[form_.signon_realm][0].skip_zero_click); | |
273 EXPECT_FALSE(passwords[cross_origin_form_.signon_realm][0].skip_zero_click); | |
274 | |
275 dispatcher()->OnNotifySignedOut(kRequestId); | |
276 RunAllPendingTasks(); | |
277 | |
278 const uint32 kMsgID = CredentialManagerMsg_AcknowledgeSignedOut::ID; | |
279 const IPC::Message* message = | |
280 process()->sink().GetFirstMessageMatching(kMsgID); | |
281 EXPECT_TRUE(message); | |
282 process()->sink().ClearMessages(); | |
283 | |
284 passwords = store_->stored_passwords(); | |
285 EXPECT_EQ(2U, passwords.size()); | |
286 EXPECT_EQ(1U, passwords[form_.signon_realm].size()); | |
287 EXPECT_EQ(1U, passwords[cross_origin_form_.signon_realm].size()); | |
288 EXPECT_TRUE(passwords[form_.signon_realm][0].skip_zero_click); | |
289 EXPECT_FALSE(passwords[cross_origin_form_.signon_realm][0].skip_zero_click); | |
290 } | |
291 | |
292 TEST_F(CredentialManagerDispatcherTest, | |
293 CredentialManagerOnRequestCredentialWithEmptyPasswordStore) { | |
294 std::vector<GURL> federations; | |
295 dispatcher()->OnRequestCredential(kRequestId, false, federations); | |
296 | |
297 RunAllPendingTasks(); | |
298 | |
299 const uint32 kMsgID = CredentialManagerMsg_SendCredential::ID; | |
300 const IPC::Message* message = | |
301 process()->sink().GetFirstMessageMatching(kMsgID); | |
302 EXPECT_TRUE(message); | |
303 CredentialManagerMsg_SendCredential::Param param; | |
304 CredentialManagerMsg_SendCredential::Read(message, ¶m); | |
305 EXPECT_EQ(CredentialType::CREDENTIAL_TYPE_EMPTY, get<1>(param).type); | |
306 process()->sink().ClearMessages(); | |
307 EXPECT_FALSE(client_->did_prompt_user_to_choose()); | |
308 } | |
309 | |
310 TEST_F(CredentialManagerDispatcherTest, | |
311 CredentialManagerOnRequestCredentialWithCrossOriginPasswordStore) { | |
312 store_->AddLogin(cross_origin_form_); | |
313 | |
314 std::vector<GURL> federations; | |
315 dispatcher()->OnRequestCredential(kRequestId, false, federations); | |
316 | |
317 RunAllPendingTasks(); | |
318 | |
319 const uint32 kMsgID = CredentialManagerMsg_SendCredential::ID; | |
320 const IPC::Message* message = | |
321 process()->sink().GetFirstMessageMatching(kMsgID); | |
322 EXPECT_TRUE(message); | |
323 CredentialManagerMsg_SendCredential::Param param; | |
324 CredentialManagerMsg_SendCredential::Read(message, ¶m); | |
325 EXPECT_EQ(CredentialType::CREDENTIAL_TYPE_EMPTY, get<1>(param).type); | |
326 process()->sink().ClearMessages(); | |
327 EXPECT_FALSE(client_->did_prompt_user_to_choose()); | |
328 } | |
329 | |
330 TEST_F(CredentialManagerDispatcherTest, | |
331 CredentialManagerOnRequestCredentialWithFullPasswordStore) { | |
332 client_->set_zero_click_enabled(false); | |
333 store_->AddLogin(form_); | |
334 | |
335 std::vector<GURL> federations; | |
336 dispatcher()->OnRequestCredential(kRequestId, false, federations); | |
337 | |
338 RunAllPendingTasks(); | |
339 | |
340 const uint32 kMsgID = CredentialManagerMsg_SendCredential::ID; | |
341 const IPC::Message* message = | |
342 process()->sink().GetFirstMessageMatching(kMsgID); | |
343 EXPECT_TRUE(message); | |
344 EXPECT_TRUE(client_->did_prompt_user_to_choose()); | |
345 } | |
346 | |
347 TEST_F( | |
348 CredentialManagerDispatcherTest, | |
349 CredentialManagerOnRequestCredentialWithZeroClickOnlyEmptyPasswordStore) { | |
350 std::vector<GURL> federations; | |
351 dispatcher()->OnRequestCredential(kRequestId, true, federations); | |
352 | |
353 RunAllPendingTasks(); | |
354 | |
355 const uint32 kMsgID = CredentialManagerMsg_SendCredential::ID; | |
356 const IPC::Message* message = | |
357 process()->sink().GetFirstMessageMatching(kMsgID); | |
358 EXPECT_TRUE(message); | |
359 EXPECT_FALSE(client_->did_prompt_user_to_choose()); | |
360 CredentialManagerMsg_SendCredential::Param send_param; | |
361 CredentialManagerMsg_SendCredential::Read(message, &send_param); | |
362 EXPECT_EQ(CredentialType::CREDENTIAL_TYPE_EMPTY, get<1>(send_param).type); | |
363 } | |
364 | |
365 TEST_F(CredentialManagerDispatcherTest, | |
366 CredentialManagerOnRequestCredentialWithZeroClickOnlyFullPasswordStore) { | |
367 store_->AddLogin(form_); | |
368 | |
369 std::vector<GURL> federations; | |
370 dispatcher()->OnRequestCredential(kRequestId, true, federations); | |
371 | |
372 RunAllPendingTasks(); | |
373 | |
374 const uint32 kMsgID = CredentialManagerMsg_SendCredential::ID; | |
375 const IPC::Message* message = | |
376 process()->sink().GetFirstMessageMatching(kMsgID); | |
377 EXPECT_TRUE(message); | |
378 EXPECT_FALSE(client_->did_prompt_user_to_choose()); | |
379 CredentialManagerMsg_SendCredential::Param send_param; | |
380 CredentialManagerMsg_SendCredential::Read(message, &send_param); | |
381 EXPECT_EQ(CredentialType::CREDENTIAL_TYPE_LOCAL, get<1>(send_param).type); | |
382 } | |
383 | |
384 TEST_F(CredentialManagerDispatcherTest, | |
385 CredentialManagerOnRequestCredentialWithZeroClickOnlyTwoPasswordStore) { | |
386 store_->AddLogin(form_); | |
387 store_->AddLogin(form2_); | |
388 | |
389 std::vector<GURL> federations; | |
390 dispatcher()->OnRequestCredential(kRequestId, true, federations); | |
391 | |
392 RunAllPendingTasks(); | |
393 | |
394 const uint32 kMsgID = CredentialManagerMsg_SendCredential::ID; | |
395 const IPC::Message* message = | |
396 process()->sink().GetFirstMessageMatching(kMsgID); | |
397 EXPECT_TRUE(message); | |
398 EXPECT_FALSE(client_->did_prompt_user_to_choose()); | |
399 CredentialManagerMsg_SendCredential::Param send_param; | |
400 CredentialManagerMsg_SendCredential::Read(message, &send_param); | |
401 | |
402 // With two items in the password store, we shouldn't get credentials back. | |
403 EXPECT_EQ(CredentialType::CREDENTIAL_TYPE_EMPTY, get<1>(send_param).type); | |
404 } | |
405 | |
406 TEST_F(CredentialManagerDispatcherTest, | |
407 CredentialManagerOnRequestCredentialWhileRequestPending) { | |
408 client_->set_zero_click_enabled(false); | |
409 store_->AddLogin(form_); | |
410 | |
411 std::vector<GURL> federations; | |
412 dispatcher()->OnRequestCredential(kRequestId, false, federations); | |
413 dispatcher()->OnRequestCredential(kRequestId, false, federations); | |
414 | |
415 // Check that the second request triggered a rejection. | |
416 uint32 kMsgID = CredentialManagerMsg_RejectCredentialRequest::ID; | |
417 const IPC::Message* message = | |
418 process()->sink().GetFirstMessageMatching(kMsgID); | |
419 EXPECT_TRUE(message); | |
420 CredentialManagerMsg_RejectCredentialRequest::Param reject_param; | |
421 CredentialManagerMsg_RejectCredentialRequest::Read(message, &reject_param); | |
422 EXPECT_EQ(blink::WebCredentialManagerError::ErrorTypePendingRequest, | |
423 get<1>(reject_param)); | |
424 EXPECT_FALSE(client_->did_prompt_user_to_choose()); | |
425 | |
426 process()->sink().ClearMessages(); | |
427 | |
428 // Execute the PasswordStore asynchronousness. | |
429 RunAllPendingTasks(); | |
430 | |
431 // Check that the first request resolves. | |
432 kMsgID = CredentialManagerMsg_SendCredential::ID; | |
433 message = process()->sink().GetFirstMessageMatching(kMsgID); | |
434 EXPECT_TRUE(message); | |
435 CredentialManagerMsg_SendCredential::Param send_param; | |
436 CredentialManagerMsg_SendCredential::Read(message, &send_param); | |
437 EXPECT_NE(CredentialType::CREDENTIAL_TYPE_EMPTY, get<1>(send_param).type); | |
438 process()->sink().ClearMessages(); | |
439 EXPECT_TRUE(client_->did_prompt_user_to_choose()); | |
440 } | |
441 | |
442 TEST_F(CredentialManagerDispatcherTest, IncognitoRequestCredential) { | |
443 client_->set_off_the_record(true); | |
444 store_->AddLogin(form_); | |
445 | |
446 std::vector<GURL> federations; | |
447 dispatcher()->OnRequestCredential(kRequestId, true, federations); | |
448 | |
449 RunAllPendingTasks(); | |
450 | |
451 const uint32 kMsgID = CredentialManagerMsg_SendCredential::ID; | |
452 const IPC::Message* message = | |
453 process()->sink().GetFirstMessageMatching(kMsgID); | |
454 ASSERT_TRUE(message); | |
455 CredentialManagerMsg_SendCredential::Param param; | |
456 CredentialManagerMsg_SendCredential::Read(message, ¶m); | |
457 EXPECT_EQ(CredentialType::CREDENTIAL_TYPE_EMPTY, get<1>(param).type); | |
458 EXPECT_FALSE(client_->did_prompt_user_to_choose()); | |
459 } | |
460 | |
461 } // namespace password_manager | |
OLD | NEW |