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 <stdint.h> | |
8 | |
9 #include <string> | |
10 #include <tuple> | |
11 #include <vector> | |
12 | |
13 #include "base/bind.h" | |
14 #include "base/command_line.h" | |
15 #include "base/macros.h" | |
16 #include "base/run_loop.h" | |
17 #include "base/strings/string16.h" | |
18 #include "base/strings/utf_string_conversions.h" | |
19 #include "base/thread_task_runner_handle.h" | |
20 #include "components/password_manager/content/common/credential_manager_messages
.h" | |
21 #include "components/password_manager/core/browser/credential_manager_password_f
orm_manager.h" | |
22 #include "components/password_manager/core/browser/mock_affiliated_match_helper.
h" | |
23 #include "components/password_manager/core/browser/password_manager.h" | |
24 #include "components/password_manager/core/browser/stub_password_manager_client.
h" | |
25 #include "components/password_manager/core/browser/stub_password_manager_driver.
h" | |
26 #include "components/password_manager/core/browser/test_password_store.h" | |
27 #include "components/password_manager/core/common/credential_manager_types.h" | |
28 #include "components/password_manager/core/common/password_manager_pref_names.h" | |
29 #include "components/prefs/pref_registry_simple.h" | |
30 #include "components/prefs/testing_pref_service.h" | |
31 #include "content/public/browser/web_contents.h" | |
32 #include "content/public/test/mock_render_process_host.h" | |
33 #include "content/public/test/test_renderer_host.h" | |
34 #include "testing/gmock/include/gmock/gmock.h" | |
35 #include "testing/gtest/include/gtest/gtest.h" | |
36 | |
37 using content::BrowserContext; | |
38 using content::WebContents; | |
39 | |
40 using testing::_; | |
41 | |
42 namespace password_manager { | |
43 | |
44 namespace { | |
45 | |
46 // Chosen by fair dice roll. Guaranteed to be random. | |
47 const int kRequestId = 4; | |
48 | |
49 const char kTestWebOrigin[] = "https://example.com/"; | |
50 const char kTestAndroidRealm1[] = "android://hash@com.example.one.android/"; | |
51 const char kTestAndroidRealm2[] = "android://hash@com.example.two.android/"; | |
52 | |
53 class MockPasswordManagerClient : public StubPasswordManagerClient { | |
54 public: | |
55 MOCK_CONST_METHOD0(IsSavingAndFillingEnabledForCurrentPage, bool()); | |
56 MOCK_CONST_METHOD0(IsOffTheRecord, bool()); | |
57 MOCK_CONST_METHOD0(DidLastPageLoadEncounterSSLErrors, bool()); | |
58 MOCK_METHOD1(NotifyUserAutoSigninPtr, | |
59 bool(const std::vector<autofill::PasswordForm*>& local_forms)); | |
60 MOCK_METHOD1(NotifyUserCouldBeAutoSignedInPtr, | |
61 bool(autofill::PasswordForm* form)); | |
62 MOCK_METHOD2(PromptUserToSavePasswordPtr, | |
63 void(PasswordFormManager*, CredentialSourceType type)); | |
64 MOCK_METHOD4(PromptUserToChooseCredentialsPtr, | |
65 bool(const std::vector<autofill::PasswordForm*>& local_forms, | |
66 const std::vector<autofill::PasswordForm*>& federated_forms, | |
67 const GURL& origin, | |
68 base::Callback<void(const CredentialInfo&)> callback)); | |
69 | |
70 explicit MockPasswordManagerClient(PasswordStore* store) : store_(store) { | |
71 prefs_.registry()->RegisterBooleanPref(prefs::kCredentialsEnableAutosignin, | |
72 true); | |
73 prefs_.registry()->RegisterBooleanPref( | |
74 prefs::kWasAutoSignInFirstRunExperienceShown, true); | |
75 } | |
76 ~MockPasswordManagerClient() override {} | |
77 | |
78 bool PromptUserToSaveOrUpdatePassword(scoped_ptr<PasswordFormManager> manager, | |
79 CredentialSourceType type, | |
80 bool update_password) override { | |
81 manager_.swap(manager); | |
82 PromptUserToSavePasswordPtr(manager_.get(), type); | |
83 return true; | |
84 } | |
85 | |
86 void NotifyUserCouldBeAutoSignedIn( | |
87 scoped_ptr<autofill::PasswordForm> form) override { | |
88 NotifyUserCouldBeAutoSignedInPtr(form.get()); | |
89 } | |
90 | |
91 PasswordStore* GetPasswordStore() const override { return store_; } | |
92 | |
93 PrefService* GetPrefs() override { return &prefs_; } | |
94 | |
95 bool PromptUserToChooseCredentials( | |
96 ScopedVector<autofill::PasswordForm> local_forms, | |
97 ScopedVector<autofill::PasswordForm> federated_forms, | |
98 const GURL& origin, | |
99 base::Callback<void(const CredentialInfo&)> callback) { | |
100 EXPECT_FALSE(local_forms.empty() && federated_forms.empty()); | |
101 CredentialInfo info( | |
102 local_forms.empty() ? *federated_forms[0] : *local_forms[0], | |
103 local_forms.empty() ? CredentialType::CREDENTIAL_TYPE_FEDERATED | |
104 : CredentialType::CREDENTIAL_TYPE_PASSWORD); | |
105 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, | |
106 base::Bind(callback, info)); | |
107 PromptUserToChooseCredentialsPtr(local_forms.get(), federated_forms.get(), | |
108 origin, callback); | |
109 return true; | |
110 } | |
111 | |
112 void NotifyUserAutoSignin( | |
113 ScopedVector<autofill::PasswordForm> local_forms) override { | |
114 EXPECT_FALSE(local_forms.empty()); | |
115 NotifyUserAutoSigninPtr(local_forms.get()); | |
116 } | |
117 | |
118 PasswordFormManager* pending_manager() const { return manager_.get(); } | |
119 | |
120 void set_zero_click_enabled(bool zero_click_enabled) { | |
121 prefs_.SetBoolean(prefs::kCredentialsEnableAutosignin, zero_click_enabled); | |
122 } | |
123 | |
124 void set_first_run_seen(bool first_run_seen) { | |
125 prefs_.SetBoolean(prefs::kWasAutoSignInFirstRunExperienceShown, | |
126 first_run_seen); | |
127 } | |
128 | |
129 private: | |
130 TestingPrefServiceSimple prefs_; | |
131 PasswordStore* store_; | |
132 scoped_ptr<PasswordFormManager> manager_; | |
133 | |
134 DISALLOW_COPY_AND_ASSIGN(MockPasswordManagerClient); | |
135 }; | |
136 | |
137 class TestCredentialManagerDispatcher : public CredentialManagerDispatcher { | |
138 public: | |
139 TestCredentialManagerDispatcher(content::WebContents* web_contents, | |
140 PasswordManagerClient* client, | |
141 PasswordManagerDriver* driver); | |
142 | |
143 private: | |
144 base::WeakPtr<PasswordManagerDriver> GetDriver() override; | |
145 | |
146 base::WeakPtr<PasswordManagerDriver> driver_; | |
147 }; | |
148 | |
149 TestCredentialManagerDispatcher::TestCredentialManagerDispatcher( | |
150 content::WebContents* web_contents, | |
151 PasswordManagerClient* client, | |
152 PasswordManagerDriver* driver) | |
153 : CredentialManagerDispatcher(web_contents, client), | |
154 driver_(driver->AsWeakPtr()) {} | |
155 | |
156 base::WeakPtr<PasswordManagerDriver> | |
157 TestCredentialManagerDispatcher::GetDriver() { | |
158 return driver_; | |
159 } | |
160 | |
161 void RunAllPendingTasks() { | |
162 base::RunLoop run_loop; | |
163 base::MessageLoop::current()->PostTask( | |
164 FROM_HERE, base::MessageLoop::QuitWhenIdleClosure()); | |
165 run_loop.Run(); | |
166 } | |
167 | |
168 class SlightlyLessStubbyPasswordManagerDriver | |
169 : public StubPasswordManagerDriver { | |
170 public: | |
171 explicit SlightlyLessStubbyPasswordManagerDriver( | |
172 PasswordManagerClient* client) | |
173 : password_manager_(client) {} | |
174 | |
175 PasswordManager* GetPasswordManager() override { return &password_manager_; } | |
176 | |
177 private: | |
178 PasswordManager password_manager_; | |
179 }; | |
180 | |
181 } // namespace | |
182 | |
183 class CredentialManagerDispatcherTest | |
184 : public content::RenderViewHostTestHarness { | |
185 public: | |
186 CredentialManagerDispatcherTest() {} | |
187 | |
188 void SetUp() override { | |
189 content::RenderViewHostTestHarness::SetUp(); | |
190 store_ = new TestPasswordStore; | |
191 client_.reset( | |
192 new testing::NiceMock<MockPasswordManagerClient>(store_.get())); | |
193 stub_driver_.reset( | |
194 new SlightlyLessStubbyPasswordManagerDriver(client_.get())); | |
195 dispatcher_.reset(new TestCredentialManagerDispatcher( | |
196 web_contents(), client_.get(), stub_driver_.get())); | |
197 ON_CALL(*client_, IsSavingAndFillingEnabledForCurrentPage()) | |
198 .WillByDefault(testing::Return(true)); | |
199 ON_CALL(*client_, IsOffTheRecord()).WillByDefault(testing::Return(false)); | |
200 ON_CALL(*client_, DidLastPageLoadEncounterSSLErrors()) | |
201 .WillByDefault(testing::Return(false)); | |
202 | |
203 NavigateAndCommit(GURL("https://example.com/test.html")); | |
204 | |
205 form_.username_value = base::ASCIIToUTF16("Username"); | |
206 form_.display_name = base::ASCIIToUTF16("Display Name"); | |
207 form_.password_value = base::ASCIIToUTF16("Password"); | |
208 form_.origin = web_contents()->GetLastCommittedURL().GetOrigin(); | |
209 form_.signon_realm = form_.origin.spec(); | |
210 form_.scheme = autofill::PasswordForm::SCHEME_HTML; | |
211 form_.skip_zero_click = false; | |
212 form_.ssl_valid = true; | |
213 | |
214 affiliated_form1_.username_value = base::ASCIIToUTF16("Affiliated 1"); | |
215 affiliated_form1_.display_name = base::ASCIIToUTF16("Display Name"); | |
216 affiliated_form1_.password_value = base::ASCIIToUTF16("Password"); | |
217 affiliated_form1_.origin = GURL(); | |
218 affiliated_form1_.signon_realm = kTestAndroidRealm1; | |
219 affiliated_form1_.scheme = autofill::PasswordForm::SCHEME_HTML; | |
220 affiliated_form1_.skip_zero_click = false; | |
221 affiliated_form1_.ssl_valid = true; | |
222 | |
223 affiliated_form2_.username_value = base::ASCIIToUTF16("Affiliated 2"); | |
224 affiliated_form2_.display_name = base::ASCIIToUTF16("Display Name"); | |
225 affiliated_form2_.password_value = base::ASCIIToUTF16("Password"); | |
226 affiliated_form2_.origin = GURL(); | |
227 affiliated_form2_.signon_realm = kTestAndroidRealm2; | |
228 affiliated_form2_.scheme = autofill::PasswordForm::SCHEME_HTML; | |
229 affiliated_form2_.skip_zero_click = false; | |
230 affiliated_form2_.ssl_valid = true; | |
231 | |
232 origin_path_form_.username_value = base::ASCIIToUTF16("Username 2"); | |
233 origin_path_form_.display_name = base::ASCIIToUTF16("Display Name 2"); | |
234 origin_path_form_.password_value = base::ASCIIToUTF16("Password 2"); | |
235 origin_path_form_.origin = GURL("https://example.com/path"); | |
236 origin_path_form_.signon_realm = origin_path_form_.origin.spec(); | |
237 origin_path_form_.scheme = autofill::PasswordForm::SCHEME_HTML; | |
238 origin_path_form_.skip_zero_click = false; | |
239 | |
240 cross_origin_form_.username_value = base::ASCIIToUTF16("Username"); | |
241 cross_origin_form_.display_name = base::ASCIIToUTF16("Display Name"); | |
242 cross_origin_form_.password_value = base::ASCIIToUTF16("Password"); | |
243 cross_origin_form_.origin = GURL("https://example.net/"); | |
244 cross_origin_form_.signon_realm = cross_origin_form_.origin.spec(); | |
245 cross_origin_form_.scheme = autofill::PasswordForm::SCHEME_HTML; | |
246 cross_origin_form_.skip_zero_click = false; | |
247 | |
248 store_->Clear(); | |
249 EXPECT_TRUE(store_->IsEmpty()); | |
250 } | |
251 | |
252 void TearDown() override { | |
253 store_->ShutdownOnUIThread(); | |
254 content::RenderViewHostTestHarness::TearDown(); | |
255 } | |
256 | |
257 void ExpectZeroClickSignInFailure() { | |
258 EXPECT_CALL(*client_, PromptUserToChooseCredentialsPtr(_, _, _, _)) | |
259 .Times(testing::Exactly(0)); | |
260 EXPECT_CALL(*client_, NotifyUserAutoSigninPtr(_)) | |
261 .Times(testing::Exactly(0)); | |
262 | |
263 RunAllPendingTasks(); | |
264 | |
265 const uint32_t kMsgID = CredentialManagerMsg_SendCredential::ID; | |
266 const IPC::Message* message = | |
267 process()->sink().GetFirstMessageMatching(kMsgID); | |
268 ASSERT_TRUE(message); | |
269 CredentialManagerMsg_SendCredential::Param send_param; | |
270 CredentialManagerMsg_SendCredential::Read(message, &send_param); | |
271 | |
272 EXPECT_EQ(CredentialType::CREDENTIAL_TYPE_EMPTY, | |
273 std::get<1>(send_param).type); | |
274 } | |
275 | |
276 void ExpectZeroClickSignInSuccess(CredentialType type) { | |
277 EXPECT_CALL(*client_, PromptUserToChooseCredentialsPtr(_, _, _, _)) | |
278 .Times(testing::Exactly(0)); | |
279 EXPECT_CALL(*client_, NotifyUserAutoSigninPtr(_)) | |
280 .Times(testing::Exactly(1)); | |
281 | |
282 RunAllPendingTasks(); | |
283 | |
284 const uint32_t kMsgID = CredentialManagerMsg_SendCredential::ID; | |
285 const IPC::Message* message = | |
286 process()->sink().GetFirstMessageMatching(kMsgID); | |
287 ASSERT_TRUE(message); | |
288 CredentialManagerMsg_SendCredential::Param send_param; | |
289 CredentialManagerMsg_SendCredential::Read(message, &send_param); | |
290 | |
291 EXPECT_EQ(type, std::get<1>(send_param).type); | |
292 } | |
293 | |
294 CredentialManagerDispatcher* dispatcher() { return dispatcher_.get(); } | |
295 | |
296 protected: | |
297 autofill::PasswordForm form_; | |
298 autofill::PasswordForm affiliated_form1_; | |
299 autofill::PasswordForm affiliated_form2_; | |
300 autofill::PasswordForm origin_path_form_; | |
301 autofill::PasswordForm cross_origin_form_; | |
302 scoped_refptr<TestPasswordStore> store_; | |
303 scoped_ptr<testing::NiceMock<MockPasswordManagerClient>> client_; | |
304 scoped_ptr<SlightlyLessStubbyPasswordManagerDriver> stub_driver_; | |
305 scoped_ptr<CredentialManagerDispatcher> dispatcher_; | |
306 }; | |
307 | |
308 TEST_F(CredentialManagerDispatcherTest, IsZeroClickAllowed) { | |
309 // IsZeroClickAllowed is uneffected by the first-run status. | |
310 client_->set_zero_click_enabled(true); | |
311 client_->set_first_run_seen(true); | |
312 EXPECT_TRUE(dispatcher()->IsZeroClickAllowed()); | |
313 | |
314 client_->set_zero_click_enabled(true); | |
315 client_->set_first_run_seen(false); | |
316 EXPECT_TRUE(dispatcher()->IsZeroClickAllowed()); | |
317 | |
318 client_->set_zero_click_enabled(false); | |
319 client_->set_first_run_seen(true); | |
320 EXPECT_FALSE(dispatcher()->IsZeroClickAllowed()); | |
321 | |
322 client_->set_zero_click_enabled(false); | |
323 client_->set_first_run_seen(false); | |
324 EXPECT_FALSE(dispatcher()->IsZeroClickAllowed()); | |
325 } | |
326 | |
327 TEST_F(CredentialManagerDispatcherTest, CredentialManagerOnStore) { | |
328 CredentialInfo info(form_, CredentialType::CREDENTIAL_TYPE_PASSWORD); | |
329 EXPECT_CALL(*client_, PromptUserToSavePasswordPtr( | |
330 _, CredentialSourceType::CREDENTIAL_SOURCE_API)) | |
331 .Times(testing::Exactly(1)); | |
332 | |
333 dispatcher()->OnStore(kRequestId, info); | |
334 | |
335 const uint32_t kMsgID = CredentialManagerMsg_AcknowledgeStore::ID; | |
336 const IPC::Message* message = | |
337 process()->sink().GetFirstMessageMatching(kMsgID); | |
338 EXPECT_TRUE(message); | |
339 process()->sink().ClearMessages(); | |
340 | |
341 // Allow the PasswordFormManager to talk to the password store, determine | |
342 // that the form is new, and set it as pending. | |
343 RunAllPendingTasks(); | |
344 | |
345 EXPECT_TRUE(client_->pending_manager()->HasCompletedMatching()); | |
346 | |
347 autofill::PasswordForm new_form = | |
348 client_->pending_manager()->pending_credentials(); | |
349 EXPECT_EQ(form_.username_value, new_form.username_value); | |
350 EXPECT_EQ(form_.display_name, new_form.display_name); | |
351 EXPECT_EQ(form_.password_value, new_form.password_value); | |
352 EXPECT_EQ(form_.origin, new_form.origin); | |
353 EXPECT_EQ(form_.signon_realm, new_form.signon_realm); | |
354 EXPECT_EQ(autofill::PasswordForm::SCHEME_HTML, new_form.scheme); | |
355 } | |
356 | |
357 TEST_F(CredentialManagerDispatcherTest, CredentialManagerStoreOverwrite) { | |
358 // Populate the PasswordStore with a form. | |
359 store_->AddLogin(form_); | |
360 RunAllPendingTasks(); | |
361 | |
362 // Calling 'OnStore' with a credential that matches |form_| should update | |
363 // the password without prompting the user. | |
364 CredentialInfo info(form_, CredentialType::CREDENTIAL_TYPE_PASSWORD); | |
365 info.password = base::ASCIIToUTF16("Totally new password."); | |
366 dispatcher()->OnStore(kRequestId, info); | |
367 | |
368 EXPECT_CALL(*client_, PromptUserToSavePasswordPtr( | |
369 _, CredentialSourceType::CREDENTIAL_SOURCE_API)) | |
370 .Times(testing::Exactly(0)); | |
371 | |
372 const uint32_t kMsgID = CredentialManagerMsg_AcknowledgeStore::ID; | |
373 const IPC::Message* message = | |
374 process()->sink().GetFirstMessageMatching(kMsgID); | |
375 EXPECT_TRUE(message); | |
376 process()->sink().ClearMessages(); | |
377 | |
378 // Allow the PasswordFormManager to talk to the password store, determine | |
379 // the form is a match for an existing form, and update the PasswordStore. | |
380 RunAllPendingTasks(); | |
381 | |
382 TestPasswordStore::PasswordMap passwords = store_->stored_passwords(); | |
383 EXPECT_EQ(1U, passwords.size()); | |
384 EXPECT_EQ(1U, passwords[form_.signon_realm].size()); | |
385 EXPECT_EQ(base::ASCIIToUTF16("Totally new password."), | |
386 passwords[form_.signon_realm][0].password_value); | |
387 } | |
388 | |
389 TEST_F(CredentialManagerDispatcherTest, | |
390 CredentialManagerStoreOverwriteZeroClick) { | |
391 // Set the global zero click flag on, and populate the PasswordStore with a | |
392 // form that's set to skip zero click. | |
393 client_->set_zero_click_enabled(true); | |
394 form_.skip_zero_click = true; | |
395 store_->AddLogin(form_); | |
396 RunAllPendingTasks(); | |
397 | |
398 // Calling 'OnStore' with a credential that matches |form_| should update | |
399 // the password without prompting the user. | |
400 CredentialInfo info(form_, CredentialType::CREDENTIAL_TYPE_PASSWORD); | |
401 info.password = base::ASCIIToUTF16("Totally new password."); | |
402 dispatcher()->OnStore(kRequestId, info); | |
403 process()->sink().ClearMessages(); | |
404 | |
405 // Allow the PasswordFormManager to talk to the password store, determine | |
406 // the form is a match for an existing form, and update the PasswordStore. | |
407 RunAllPendingTasks(); | |
408 | |
409 // Verify that the update didn't toggle the skip_zero_click flag off. | |
410 TestPasswordStore::PasswordMap passwords = store_->stored_passwords(); | |
411 EXPECT_TRUE(passwords[form_.signon_realm][0].skip_zero_click); | |
412 } | |
413 | |
414 TEST_F(CredentialManagerDispatcherTest, | |
415 CredentialManagerSignInWithSavingDisabledForCurrentPage) { | |
416 CredentialInfo info(form_, CredentialType::CREDENTIAL_TYPE_PASSWORD); | |
417 EXPECT_CALL(*client_, IsSavingAndFillingEnabledForCurrentPage()) | |
418 .WillRepeatedly(testing::Return(false)); | |
419 EXPECT_CALL(*client_, PromptUserToSavePasswordPtr( | |
420 _, CredentialSourceType::CREDENTIAL_SOURCE_API)) | |
421 .Times(testing::Exactly(0)); | |
422 | |
423 dispatcher()->OnStore(kRequestId, info); | |
424 | |
425 const uint32_t kMsgID = CredentialManagerMsg_AcknowledgeStore::ID; | |
426 const IPC::Message* message = | |
427 process()->sink().GetFirstMessageMatching(kMsgID); | |
428 EXPECT_TRUE(message); | |
429 process()->sink().ClearMessages(); | |
430 | |
431 RunAllPendingTasks(); | |
432 | |
433 EXPECT_FALSE(client_->pending_manager()); | |
434 } | |
435 | |
436 TEST_F(CredentialManagerDispatcherTest, | |
437 CredentialManagerOnRequireUserMediation) { | |
438 store_->AddLogin(form_); | |
439 store_->AddLogin(cross_origin_form_); | |
440 RunAllPendingTasks(); | |
441 | |
442 TestPasswordStore::PasswordMap passwords = store_->stored_passwords(); | |
443 EXPECT_EQ(2U, passwords.size()); | |
444 EXPECT_EQ(1U, passwords[form_.signon_realm].size()); | |
445 EXPECT_EQ(1U, passwords[cross_origin_form_.signon_realm].size()); | |
446 EXPECT_FALSE(passwords[form_.signon_realm][0].skip_zero_click); | |
447 EXPECT_FALSE(passwords[cross_origin_form_.signon_realm][0].skip_zero_click); | |
448 | |
449 dispatcher()->OnRequireUserMediation(kRequestId); | |
450 RunAllPendingTasks(); | |
451 | |
452 const uint32_t kMsgID = | |
453 CredentialManagerMsg_AcknowledgeRequireUserMediation::ID; | |
454 const IPC::Message* message = | |
455 process()->sink().GetFirstMessageMatching(kMsgID); | |
456 EXPECT_TRUE(message); | |
457 process()->sink().ClearMessages(); | |
458 | |
459 passwords = store_->stored_passwords(); | |
460 EXPECT_EQ(2U, passwords.size()); | |
461 EXPECT_EQ(1U, passwords[form_.signon_realm].size()); | |
462 EXPECT_EQ(1U, passwords[cross_origin_form_.signon_realm].size()); | |
463 EXPECT_TRUE(passwords[form_.signon_realm][0].skip_zero_click); | |
464 EXPECT_FALSE(passwords[cross_origin_form_.signon_realm][0].skip_zero_click); | |
465 } | |
466 | |
467 TEST_F(CredentialManagerDispatcherTest, | |
468 CredentialManagerOnRequireUserMediationIncognito) { | |
469 EXPECT_CALL(*client_, IsOffTheRecord()).WillRepeatedly(testing::Return(true)); | |
470 store_->AddLogin(form_); | |
471 RunAllPendingTasks(); | |
472 | |
473 TestPasswordStore::PasswordMap passwords = store_->stored_passwords(); | |
474 ASSERT_EQ(1U, passwords.size()); | |
475 ASSERT_EQ(1U, passwords[form_.signon_realm].size()); | |
476 EXPECT_FALSE(passwords[form_.signon_realm][0].skip_zero_click); | |
477 | |
478 dispatcher()->OnRequireUserMediation(kRequestId); | |
479 RunAllPendingTasks(); | |
480 | |
481 const uint32_t kMsgID = | |
482 CredentialManagerMsg_AcknowledgeRequireUserMediation::ID; | |
483 const IPC::Message* message = | |
484 process()->sink().GetFirstMessageMatching(kMsgID); | |
485 EXPECT_TRUE(message); | |
486 process()->sink().ClearMessages(); | |
487 | |
488 passwords = store_->stored_passwords(); | |
489 ASSERT_EQ(1U, passwords.size()); | |
490 ASSERT_EQ(1U, passwords[form_.signon_realm].size()); | |
491 EXPECT_FALSE(passwords[form_.signon_realm][0].skip_zero_click); | |
492 } | |
493 | |
494 TEST_F(CredentialManagerDispatcherTest, | |
495 CredentialManagerOnRequireUserMediationWithAffiliation) { | |
496 store_->AddLogin(form_); | |
497 store_->AddLogin(cross_origin_form_); | |
498 store_->AddLogin(affiliated_form1_); | |
499 store_->AddLogin(affiliated_form2_); | |
500 | |
501 auto mock_helper = make_scoped_ptr(new MockAffiliatedMatchHelper); | |
502 store_->SetAffiliatedMatchHelper(std::move(mock_helper)); | |
503 | |
504 std::vector<GURL> federations; | |
505 std::vector<std::string> affiliated_realms; | |
506 affiliated_realms.push_back(kTestAndroidRealm1); | |
507 static_cast<MockAffiliatedMatchHelper*>(store_->affiliated_match_helper()) | |
508 ->ExpectCallToGetAffiliatedAndroidRealms( | |
509 dispatcher_->GetSynthesizedFormForOrigin(), affiliated_realms); | |
510 RunAllPendingTasks(); | |
511 | |
512 TestPasswordStore::PasswordMap passwords = store_->stored_passwords(); | |
513 EXPECT_EQ(4U, passwords.size()); | |
514 EXPECT_FALSE(passwords[form_.signon_realm][0].skip_zero_click); | |
515 EXPECT_FALSE(passwords[cross_origin_form_.signon_realm][0].skip_zero_click); | |
516 EXPECT_FALSE(passwords[affiliated_form1_.signon_realm][0].skip_zero_click); | |
517 EXPECT_FALSE(passwords[affiliated_form2_.signon_realm][0].skip_zero_click); | |
518 | |
519 dispatcher()->OnRequireUserMediation(kRequestId); | |
520 RunAllPendingTasks(); | |
521 process()->sink().ClearMessages(); | |
522 | |
523 passwords = store_->stored_passwords(); | |
524 EXPECT_EQ(4U, passwords.size()); | |
525 EXPECT_TRUE(passwords[form_.signon_realm][0].skip_zero_click); | |
526 EXPECT_FALSE(passwords[cross_origin_form_.signon_realm][0].skip_zero_click); | |
527 EXPECT_TRUE(passwords[affiliated_form1_.signon_realm][0].skip_zero_click); | |
528 EXPECT_FALSE(passwords[affiliated_form2_.signon_realm][0].skip_zero_click); | |
529 } | |
530 | |
531 TEST_F(CredentialManagerDispatcherTest, | |
532 CredentialManagerOnRequestCredentialWithEmptyPasswordStore) { | |
533 std::vector<GURL> federations; | |
534 EXPECT_CALL(*client_, PromptUserToSavePasswordPtr( | |
535 _, CredentialSourceType::CREDENTIAL_SOURCE_API)) | |
536 .Times(testing::Exactly(0)); | |
537 EXPECT_CALL(*client_, PromptUserToChooseCredentialsPtr(_, _, _, _)) | |
538 .Times(testing::Exactly(0)); | |
539 EXPECT_CALL(*client_, NotifyUserAutoSigninPtr(_)).Times(testing::Exactly(0)); | |
540 | |
541 dispatcher()->OnRequestCredential(kRequestId, false, true, federations); | |
542 | |
543 RunAllPendingTasks(); | |
544 | |
545 const uint32_t kMsgID = CredentialManagerMsg_SendCredential::ID; | |
546 const IPC::Message* message = | |
547 process()->sink().GetFirstMessageMatching(kMsgID); | |
548 EXPECT_TRUE(message); | |
549 CredentialManagerMsg_SendCredential::Param param; | |
550 CredentialManagerMsg_SendCredential::Read(message, ¶m); | |
551 EXPECT_EQ(CredentialType::CREDENTIAL_TYPE_EMPTY, std::get<1>(param).type); | |
552 process()->sink().ClearMessages(); | |
553 } | |
554 | |
555 TEST_F(CredentialManagerDispatcherTest, | |
556 CredentialManagerOnRequestCredentialWithCrossOriginPasswordStore) { | |
557 store_->AddLogin(cross_origin_form_); | |
558 | |
559 std::vector<GURL> federations; | |
560 EXPECT_CALL(*client_, PromptUserToSavePasswordPtr( | |
561 _, CredentialSourceType::CREDENTIAL_SOURCE_API)) | |
562 .Times(testing::Exactly(0)); | |
563 EXPECT_CALL(*client_, PromptUserToChooseCredentialsPtr(_, _, _, _)) | |
564 .Times(testing::Exactly(0)); | |
565 EXPECT_CALL(*client_, NotifyUserAutoSigninPtr(_)).Times(testing::Exactly(0)); | |
566 | |
567 dispatcher()->OnRequestCredential(kRequestId, false, true, federations); | |
568 | |
569 RunAllPendingTasks(); | |
570 | |
571 const uint32_t kMsgID = CredentialManagerMsg_SendCredential::ID; | |
572 const IPC::Message* message = | |
573 process()->sink().GetFirstMessageMatching(kMsgID); | |
574 EXPECT_TRUE(message); | |
575 CredentialManagerMsg_SendCredential::Param param; | |
576 CredentialManagerMsg_SendCredential::Read(message, ¶m); | |
577 EXPECT_EQ(CredentialType::CREDENTIAL_TYPE_EMPTY, std::get<1>(param).type); | |
578 process()->sink().ClearMessages(); | |
579 } | |
580 | |
581 TEST_F(CredentialManagerDispatcherTest, | |
582 CredentialManagerOnRequestCredentialWithFullPasswordStore) { | |
583 client_->set_zero_click_enabled(false); | |
584 store_->AddLogin(form_); | |
585 | |
586 std::vector<GURL> federations; | |
587 EXPECT_CALL(*client_, PromptUserToChooseCredentialsPtr(_, _, _, _)) | |
588 .Times(testing::Exactly(1)); | |
589 EXPECT_CALL(*client_, NotifyUserAutoSigninPtr(_)).Times(testing::Exactly(0)); | |
590 | |
591 dispatcher()->OnRequestCredential(kRequestId, false, true, federations); | |
592 | |
593 RunAllPendingTasks(); | |
594 | |
595 const uint32_t kMsgID = CredentialManagerMsg_SendCredential::ID; | |
596 const IPC::Message* message = | |
597 process()->sink().GetFirstMessageMatching(kMsgID); | |
598 EXPECT_TRUE(message); | |
599 } | |
600 | |
601 TEST_F( | |
602 CredentialManagerDispatcherTest, | |
603 CredentialManagerOnRequestCredentialWithZeroClickOnlyEmptyPasswordStore) { | |
604 std::vector<GURL> federations; | |
605 EXPECT_CALL(*client_, PromptUserToChooseCredentialsPtr(_, _, _, _)) | |
606 .Times(testing::Exactly(0)); | |
607 EXPECT_CALL(*client_, NotifyUserAutoSigninPtr(_)).Times(testing::Exactly(0)); | |
608 | |
609 dispatcher()->OnRequestCredential(kRequestId, true, true, federations); | |
610 | |
611 RunAllPendingTasks(); | |
612 | |
613 const uint32_t kMsgID = CredentialManagerMsg_SendCredential::ID; | |
614 const IPC::Message* message = | |
615 process()->sink().GetFirstMessageMatching(kMsgID); | |
616 EXPECT_TRUE(message); | |
617 CredentialManagerMsg_SendCredential::Param send_param; | |
618 CredentialManagerMsg_SendCredential::Read(message, &send_param); | |
619 EXPECT_EQ(CredentialType::CREDENTIAL_TYPE_EMPTY, | |
620 std::get<1>(send_param).type); | |
621 } | |
622 | |
623 TEST_F(CredentialManagerDispatcherTest, | |
624 CredentialManagerOnRequestCredentialWithZeroClickOnlyFullPasswordStore) { | |
625 store_->AddLogin(form_); | |
626 client_->set_first_run_seen(true); | |
627 | |
628 std::vector<GURL> federations; | |
629 | |
630 EXPECT_CALL(*client_, NotifyUserCouldBeAutoSignedInPtr(_)).Times(0); | |
631 dispatcher()->OnRequestCredential(kRequestId, true, true, federations); | |
632 | |
633 ExpectZeroClickSignInSuccess(CredentialType::CREDENTIAL_TYPE_PASSWORD); | |
634 } | |
635 | |
636 TEST_F(CredentialManagerDispatcherTest, | |
637 CredentialManagerOnRequestCredentialWithoutPasswords) { | |
638 store_->AddLogin(form_); | |
639 client_->set_first_run_seen(true); | |
640 | |
641 std::vector<GURL> federations; | |
642 | |
643 EXPECT_CALL(*client_, NotifyUserCouldBeAutoSignedInPtr(_)).Times(0); | |
644 dispatcher()->OnRequestCredential(kRequestId, true, false, federations); | |
645 | |
646 ExpectZeroClickSignInFailure(); | |
647 } | |
648 | |
649 TEST_F(CredentialManagerDispatcherTest, | |
650 CredentialManagerOnRequestCredentialFederatedMatch) { | |
651 form_.federation_origin = url::Origin(GURL("https://example.com/")); | |
652 store_->AddLogin(form_); | |
653 client_->set_first_run_seen(true); | |
654 | |
655 std::vector<GURL> federations; | |
656 federations.push_back(GURL("https://example.com/")); | |
657 | |
658 EXPECT_CALL(*client_, NotifyUserCouldBeAutoSignedInPtr(_)).Times(0); | |
659 dispatcher()->OnRequestCredential(kRequestId, true, true, federations); | |
660 | |
661 ExpectZeroClickSignInSuccess(CredentialType::CREDENTIAL_TYPE_FEDERATED); | |
662 } | |
663 | |
664 TEST_F(CredentialManagerDispatcherTest, | |
665 CredentialManagerOnRequestCredentialFederatedNoMatch) { | |
666 form_.federation_origin = url::Origin(GURL("https://example.com/")); | |
667 store_->AddLogin(form_); | |
668 client_->set_first_run_seen(true); | |
669 | |
670 std::vector<GURL> federations; | |
671 federations.push_back(GURL("https://not-example.com/")); | |
672 | |
673 EXPECT_CALL(*client_, NotifyUserCouldBeAutoSignedInPtr(_)).Times(0); | |
674 dispatcher()->OnRequestCredential(kRequestId, true, true, federations); | |
675 | |
676 ExpectZeroClickSignInFailure(); | |
677 } | |
678 | |
679 TEST_F(CredentialManagerDispatcherTest, | |
680 CredentialManagerOnRequestCredentialAffiliatedPasswordMatch) { | |
681 store_->AddLogin(affiliated_form1_); | |
682 client_->set_first_run_seen(true); | |
683 auto mock_helper = make_scoped_ptr(new MockAffiliatedMatchHelper); | |
684 store_->SetAffiliatedMatchHelper(std::move(mock_helper)); | |
685 | |
686 std::vector<GURL> federations; | |
687 std::vector<std::string> affiliated_realms; | |
688 affiliated_realms.push_back(kTestAndroidRealm1); | |
689 static_cast<MockAffiliatedMatchHelper*>(store_->affiliated_match_helper()) | |
690 ->ExpectCallToGetAffiliatedAndroidRealms( | |
691 dispatcher_->GetSynthesizedFormForOrigin(), affiliated_realms); | |
692 | |
693 // We pass in 'true' for the 'include_passwords' argument to ensure that | |
694 // password-type credentials are included as potential matches. | |
695 dispatcher()->OnRequestCredential(kRequestId, true, true, federations); | |
696 | |
697 ExpectZeroClickSignInSuccess(CredentialType::CREDENTIAL_TYPE_PASSWORD); | |
698 } | |
699 | |
700 TEST_F(CredentialManagerDispatcherTest, | |
701 CredentialManagerOnRequestCredentialAffiliatedPasswordNoMatch) { | |
702 store_->AddLogin(affiliated_form1_); | |
703 client_->set_first_run_seen(true); | |
704 auto mock_helper = make_scoped_ptr(new MockAffiliatedMatchHelper); | |
705 store_->SetAffiliatedMatchHelper(std::move(mock_helper)); | |
706 | |
707 std::vector<GURL> federations; | |
708 std::vector<std::string> affiliated_realms; | |
709 affiliated_realms.push_back(kTestAndroidRealm1); | |
710 static_cast<MockAffiliatedMatchHelper*>(store_->affiliated_match_helper()) | |
711 ->ExpectCallToGetAffiliatedAndroidRealms( | |
712 dispatcher_->GetSynthesizedFormForOrigin(), affiliated_realms); | |
713 | |
714 // We pass in 'false' for the 'include_passwords' argument to ensure that | |
715 // password-type credentials are excluded as potential matches. | |
716 dispatcher()->OnRequestCredential(kRequestId, true, false, federations); | |
717 | |
718 ExpectZeroClickSignInFailure(); | |
719 } | |
720 | |
721 TEST_F(CredentialManagerDispatcherTest, | |
722 CredentialManagerOnRequestCredentialAffiliatedFederatedMatch) { | |
723 affiliated_form1_.federation_origin = | |
724 url::Origin(GURL("https://example.com/")); | |
725 store_->AddLogin(affiliated_form1_); | |
726 client_->set_first_run_seen(true); | |
727 auto mock_helper = make_scoped_ptr(new MockAffiliatedMatchHelper); | |
728 store_->SetAffiliatedMatchHelper(std::move(mock_helper)); | |
729 | |
730 std::vector<GURL> federations; | |
731 federations.push_back(GURL("https://example.com/")); | |
732 | |
733 std::vector<std::string> affiliated_realms; | |
734 affiliated_realms.push_back(kTestAndroidRealm1); | |
735 static_cast<MockAffiliatedMatchHelper*>(store_->affiliated_match_helper()) | |
736 ->ExpectCallToGetAffiliatedAndroidRealms( | |
737 dispatcher_->GetSynthesizedFormForOrigin(), affiliated_realms); | |
738 | |
739 dispatcher()->OnRequestCredential(kRequestId, true, true, federations); | |
740 | |
741 ExpectZeroClickSignInSuccess(CredentialType::CREDENTIAL_TYPE_FEDERATED); | |
742 } | |
743 | |
744 TEST_F(CredentialManagerDispatcherTest, | |
745 CredentialManagerOnRequestCredentialAffiliatedFederatedNoMatch) { | |
746 affiliated_form1_.federation_origin = | |
747 url::Origin(GURL("https://example.com/")); | |
748 store_->AddLogin(affiliated_form1_); | |
749 client_->set_first_run_seen(true); | |
750 auto mock_helper = make_scoped_ptr(new MockAffiliatedMatchHelper); | |
751 store_->SetAffiliatedMatchHelper(std::move(mock_helper)); | |
752 | |
753 std::vector<GURL> federations; | |
754 federations.push_back(GURL("https://not-example.com/")); | |
755 | |
756 std::vector<std::string> affiliated_realms; | |
757 affiliated_realms.push_back(kTestAndroidRealm1); | |
758 static_cast<MockAffiliatedMatchHelper*>(store_->affiliated_match_helper()) | |
759 ->ExpectCallToGetAffiliatedAndroidRealms( | |
760 dispatcher_->GetSynthesizedFormForOrigin(), affiliated_realms); | |
761 | |
762 dispatcher()->OnRequestCredential(kRequestId, true, true, federations); | |
763 | |
764 ExpectZeroClickSignInFailure(); | |
765 } | |
766 | |
767 TEST_F(CredentialManagerDispatcherTest, RequestCredentialWithoutFirstRun) { | |
768 client_->set_first_run_seen(false); | |
769 | |
770 store_->AddLogin(form_); | |
771 | |
772 std::vector<GURL> federations; | |
773 EXPECT_CALL(*client_, | |
774 NotifyUserCouldBeAutoSignedInPtr(testing::Pointee(form_))) | |
775 .Times(1); | |
776 dispatcher()->OnRequestCredential(kRequestId, true, true, federations); | |
777 | |
778 ExpectZeroClickSignInFailure(); | |
779 } | |
780 | |
781 TEST_F(CredentialManagerDispatcherTest, RequestCredentialWithFirstRunAndSkip) { | |
782 client_->set_first_run_seen(true); | |
783 | |
784 form_.skip_zero_click = true; | |
785 store_->AddLogin(form_); | |
786 | |
787 std::vector<GURL> federations; | |
788 EXPECT_CALL(*client_, | |
789 NotifyUserCouldBeAutoSignedInPtr(testing::Pointee(form_))) | |
790 .Times(1); | |
791 dispatcher()->OnRequestCredential(kRequestId, true, true, federations); | |
792 | |
793 ExpectZeroClickSignInFailure(); | |
794 } | |
795 | |
796 TEST_F(CredentialManagerDispatcherTest, RequestCredentialWithTLSErrors) { | |
797 // If we encounter TLS errors, we won't return credentials. | |
798 EXPECT_CALL(*client_, DidLastPageLoadEncounterSSLErrors()) | |
799 .WillRepeatedly(testing::Return(true)); | |
800 | |
801 store_->AddLogin(form_); | |
802 | |
803 std::vector<GURL> federations; | |
804 dispatcher()->OnRequestCredential(kRequestId, true, true, federations); | |
805 | |
806 ExpectZeroClickSignInFailure(); | |
807 } | |
808 | |
809 TEST_F(CredentialManagerDispatcherTest, | |
810 CredentialManagerOnRequestCredentialWithZeroClickOnlyTwoPasswordStore) { | |
811 store_->AddLogin(form_); | |
812 store_->AddLogin(origin_path_form_); | |
813 | |
814 std::vector<GURL> federations; | |
815 EXPECT_CALL(*client_, PromptUserToChooseCredentialsPtr(_, _, _, _)) | |
816 .Times(testing::Exactly(0)); | |
817 EXPECT_CALL(*client_, NotifyUserAutoSigninPtr(_)).Times(testing::Exactly(0)); | |
818 | |
819 dispatcher()->OnRequestCredential(kRequestId, true, true, federations); | |
820 | |
821 RunAllPendingTasks(); | |
822 | |
823 const uint32_t kMsgID = CredentialManagerMsg_SendCredential::ID; | |
824 const IPC::Message* message = | |
825 process()->sink().GetFirstMessageMatching(kMsgID); | |
826 EXPECT_TRUE(message); | |
827 CredentialManagerMsg_SendCredential::Param send_param; | |
828 CredentialManagerMsg_SendCredential::Read(message, &send_param); | |
829 | |
830 // With two items in the password store, we shouldn't get credentials back. | |
831 EXPECT_EQ(CredentialType::CREDENTIAL_TYPE_EMPTY, | |
832 std::get<1>(send_param).type); | |
833 } | |
834 | |
835 TEST_F(CredentialManagerDispatcherTest, | |
836 OnRequestCredentialWithZeroClickOnlyAndSkipZeroClickPasswordStore) { | |
837 form_.skip_zero_click = true; | |
838 store_->AddLogin(form_); | |
839 store_->AddLogin(origin_path_form_); | |
840 | |
841 std::vector<GURL> federations; | |
842 EXPECT_CALL(*client_, PromptUserToChooseCredentialsPtr(_, _, _, _)) | |
843 .Times(testing::Exactly(0)); | |
844 EXPECT_CALL(*client_, NotifyUserAutoSigninPtr(_)).Times(testing::Exactly(0)); | |
845 | |
846 dispatcher()->OnRequestCredential(kRequestId, true, true, federations); | |
847 | |
848 RunAllPendingTasks(); | |
849 | |
850 const uint32_t kMsgID = CredentialManagerMsg_SendCredential::ID; | |
851 const IPC::Message* message = | |
852 process()->sink().GetFirstMessageMatching(kMsgID); | |
853 EXPECT_TRUE(message); | |
854 CredentialManagerMsg_SendCredential::Param send_param; | |
855 CredentialManagerMsg_SendCredential::Read(message, &send_param); | |
856 | |
857 // With two items in the password store, we shouldn't get credentials back, | |
858 // even though only one item has |skip_zero_click| set |false|. | |
859 EXPECT_EQ(CredentialType::CREDENTIAL_TYPE_EMPTY, | |
860 std::get<1>(send_param).type); | |
861 } | |
862 | |
863 TEST_F(CredentialManagerDispatcherTest, | |
864 OnRequestCredentialWithZeroClickOnlyCrossOriginPasswordStore) { | |
865 store_->AddLogin(cross_origin_form_); | |
866 | |
867 form_.skip_zero_click = true; | |
868 store_->AddLogin(form_); | |
869 | |
870 std::vector<GURL> federations; | |
871 EXPECT_CALL(*client_, PromptUserToChooseCredentialsPtr(_, _, _, _)) | |
872 .Times(testing::Exactly(0)); | |
873 EXPECT_CALL(*client_, NotifyUserAutoSigninPtr(_)).Times(testing::Exactly(0)); | |
874 | |
875 dispatcher()->OnRequestCredential(kRequestId, true, true, federations); | |
876 | |
877 RunAllPendingTasks(); | |
878 | |
879 const uint32_t kMsgID = CredentialManagerMsg_SendCredential::ID; | |
880 const IPC::Message* message = | |
881 process()->sink().GetFirstMessageMatching(kMsgID); | |
882 EXPECT_TRUE(message); | |
883 CredentialManagerMsg_SendCredential::Param send_param; | |
884 CredentialManagerMsg_SendCredential::Read(message, &send_param); | |
885 | |
886 // We only have cross-origin zero-click credentials; they should not be | |
887 // returned. | |
888 EXPECT_EQ(CredentialType::CREDENTIAL_TYPE_EMPTY, | |
889 std::get<1>(send_param).type); | |
890 } | |
891 | |
892 TEST_F(CredentialManagerDispatcherTest, | |
893 CredentialManagerOnRequestCredentialWhileRequestPending) { | |
894 client_->set_zero_click_enabled(false); | |
895 store_->AddLogin(form_); | |
896 | |
897 std::vector<GURL> federations; | |
898 EXPECT_CALL(*client_, PromptUserToChooseCredentialsPtr(_, _, _, _)) | |
899 .Times(testing::Exactly(0)); | |
900 EXPECT_CALL(*client_, NotifyUserAutoSigninPtr(_)).Times(testing::Exactly(0)); | |
901 | |
902 dispatcher()->OnRequestCredential(kRequestId, false, true, federations); | |
903 dispatcher()->OnRequestCredential(kRequestId + 1, false, true, federations); | |
904 | |
905 // Check that the second request triggered a rejection. | |
906 uint32_t kMsgID = CredentialManagerMsg_RejectCredentialRequest::ID; | |
907 const IPC::Message* message = | |
908 process()->sink().GetFirstMessageMatching(kMsgID); | |
909 EXPECT_TRUE(message); | |
910 | |
911 CredentialManagerMsg_RejectCredentialRequest::Param reject_param; | |
912 CredentialManagerMsg_RejectCredentialRequest::Read(message, &reject_param); | |
913 EXPECT_EQ(blink::WebCredentialManagerPendingRequestError, | |
914 std::get<1>(reject_param)); | |
915 EXPECT_CALL(*client_, PromptUserToChooseCredentialsPtr(_, _, _, _)) | |
916 .Times(testing::Exactly(1)); | |
917 EXPECT_CALL(*client_, NotifyUserAutoSigninPtr(_)).Times(testing::Exactly(0)); | |
918 | |
919 process()->sink().ClearMessages(); | |
920 | |
921 // Execute the PasswordStore asynchronousness. | |
922 RunAllPendingTasks(); | |
923 | |
924 // Check that the first request resolves. | |
925 kMsgID = CredentialManagerMsg_SendCredential::ID; | |
926 message = process()->sink().GetFirstMessageMatching(kMsgID); | |
927 EXPECT_TRUE(message); | |
928 CredentialManagerMsg_SendCredential::Param send_param; | |
929 CredentialManagerMsg_SendCredential::Read(message, &send_param); | |
930 EXPECT_NE(CredentialType::CREDENTIAL_TYPE_EMPTY, | |
931 std::get<1>(send_param).type); | |
932 process()->sink().ClearMessages(); | |
933 } | |
934 | |
935 TEST_F(CredentialManagerDispatcherTest, ResetSkipZeroClickAfterPrompt) { | |
936 // Turn on the global zero-click flag, and add two credentials in separate | |
937 // origins, both set to skip zero-click. | |
938 client_->set_zero_click_enabled(true); | |
939 form_.skip_zero_click = true; | |
940 store_->AddLogin(form_); | |
941 cross_origin_form_.skip_zero_click = true; | |
942 store_->AddLogin(cross_origin_form_); | |
943 | |
944 // Execute the PasswordStore asynchronousness to ensure everything is | |
945 // written before proceeding. | |
946 RunAllPendingTasks(); | |
947 | |
948 // Sanity check. | |
949 TestPasswordStore::PasswordMap passwords = store_->stored_passwords(); | |
950 EXPECT_EQ(2U, passwords.size()); | |
951 EXPECT_EQ(1U, passwords[form_.signon_realm].size()); | |
952 EXPECT_EQ(1U, passwords[cross_origin_form_.signon_realm].size()); | |
953 EXPECT_TRUE(passwords[form_.signon_realm][0].skip_zero_click); | |
954 EXPECT_TRUE(passwords[cross_origin_form_.signon_realm][0].skip_zero_click); | |
955 | |
956 // Trigger a request which should return the credential found in |form_|, and | |
957 // wait for it to process. | |
958 std::vector<GURL> federations; | |
959 // Check that the form in the database has been updated. `OnRequestCredential` | |
960 // generates a call to prompt the user to choose a credential. | |
961 // MockPasswordManagerClient mocks a user choice, and when users choose a | |
962 // credential (and have the global zero-click flag enabled), we make sure that | |
963 // they'll be logged in again next time. | |
964 EXPECT_CALL(*client_, PromptUserToChooseCredentialsPtr(_, _, _, _)) | |
965 .Times(testing::Exactly(1)); | |
966 EXPECT_CALL(*client_, NotifyUserAutoSigninPtr(_)).Times(testing::Exactly(0)); | |
967 | |
968 dispatcher()->OnRequestCredential(kRequestId, false, true, federations); | |
969 RunAllPendingTasks(); | |
970 | |
971 passwords = store_->stored_passwords(); | |
972 EXPECT_EQ(2U, passwords.size()); | |
973 EXPECT_EQ(1U, passwords[form_.signon_realm].size()); | |
974 EXPECT_EQ(1U, passwords[cross_origin_form_.signon_realm].size()); | |
975 EXPECT_FALSE(passwords[form_.signon_realm][0].skip_zero_click); | |
976 EXPECT_TRUE(passwords[cross_origin_form_.signon_realm][0].skip_zero_click); | |
977 } | |
978 | |
979 TEST_F(CredentialManagerDispatcherTest, | |
980 NoResetSkipZeroClickAfterPromptInIncognito) { | |
981 EXPECT_CALL(*client_, IsOffTheRecord()).WillRepeatedly(testing::Return(true)); | |
982 // Turn on the global zero-click flag which should be overriden by Incognito. | |
983 client_->set_zero_click_enabled(true); | |
984 form_.skip_zero_click = true; | |
985 store_->AddLogin(form_); | |
986 RunAllPendingTasks(); | |
987 | |
988 // Sanity check. | |
989 TestPasswordStore::PasswordMap passwords = store_->stored_passwords(); | |
990 ASSERT_EQ(1U, passwords.size()); | |
991 ASSERT_EQ(1U, passwords[form_.signon_realm].size()); | |
992 EXPECT_TRUE(passwords[form_.signon_realm][0].skip_zero_click); | |
993 | |
994 // Trigger a request which should return the credential found in |form_|, and | |
995 // wait for it to process. | |
996 EXPECT_CALL(*client_, PromptUserToChooseCredentialsPtr(_, _, _, _)) | |
997 .Times(testing::Exactly(1)); | |
998 EXPECT_CALL(*client_, NotifyUserAutoSigninPtr(_)).Times(testing::Exactly(0)); | |
999 | |
1000 dispatcher()->OnRequestCredential(kRequestId, false, true, | |
1001 std::vector<GURL>()); | |
1002 RunAllPendingTasks(); | |
1003 | |
1004 // The form shouldn't become a zero-click one. | |
1005 passwords = store_->stored_passwords(); | |
1006 ASSERT_EQ(1U, passwords.size()); | |
1007 ASSERT_EQ(1U, passwords[form_.signon_realm].size()); | |
1008 EXPECT_TRUE(passwords[form_.signon_realm][0].skip_zero_click); | |
1009 } | |
1010 | |
1011 TEST_F(CredentialManagerDispatcherTest, IncognitoZeroClickRequestCredential) { | |
1012 EXPECT_CALL(*client_, IsOffTheRecord()).WillRepeatedly(testing::Return(true)); | |
1013 store_->AddLogin(form_); | |
1014 | |
1015 std::vector<GURL> federations; | |
1016 EXPECT_CALL(*client_, PromptUserToChooseCredentialsPtr(_, _, _, _)) | |
1017 .Times(testing::Exactly(0)); | |
1018 EXPECT_CALL(*client_, NotifyUserAutoSigninPtr(_)).Times(testing::Exactly(0)); | |
1019 | |
1020 dispatcher()->OnRequestCredential(kRequestId, true, true, federations); | |
1021 | |
1022 RunAllPendingTasks(); | |
1023 | |
1024 const uint32_t kMsgID = CredentialManagerMsg_SendCredential::ID; | |
1025 const IPC::Message* message = | |
1026 process()->sink().GetFirstMessageMatching(kMsgID); | |
1027 ASSERT_TRUE(message); | |
1028 CredentialManagerMsg_SendCredential::Param param; | |
1029 CredentialManagerMsg_SendCredential::Read(message, ¶m); | |
1030 EXPECT_EQ(CredentialType::CREDENTIAL_TYPE_EMPTY, std::get<1>(param).type); | |
1031 } | |
1032 | |
1033 TEST_F(CredentialManagerDispatcherTest, | |
1034 ZeroClickWithAffiliatedFormInPasswordStore) { | |
1035 // Insert the affiliated form into the store, and mock out the association | |
1036 // with the current origin. As it's the only form matching the origin, it | |
1037 // ought to be returned automagically. | |
1038 store_->AddLogin(affiliated_form1_); | |
1039 | |
1040 auto mock_helper = make_scoped_ptr(new MockAffiliatedMatchHelper); | |
1041 store_->SetAffiliatedMatchHelper(std::move(mock_helper)); | |
1042 | |
1043 std::vector<GURL> federations; | |
1044 std::vector<std::string> affiliated_realms; | |
1045 affiliated_realms.push_back(kTestAndroidRealm1); | |
1046 static_cast<MockAffiliatedMatchHelper*>(store_->affiliated_match_helper()) | |
1047 ->ExpectCallToGetAffiliatedAndroidRealms( | |
1048 dispatcher_->GetSynthesizedFormForOrigin(), affiliated_realms); | |
1049 | |
1050 dispatcher()->OnRequestCredential(kRequestId, true, true, federations); | |
1051 | |
1052 ExpectZeroClickSignInSuccess(CredentialType::CREDENTIAL_TYPE_PASSWORD); | |
1053 } | |
1054 | |
1055 TEST_F(CredentialManagerDispatcherTest, | |
1056 ZeroClickWithTwoAffiliatedFormsInPasswordStore) { | |
1057 // Insert two affiliated forms into the store, and mock out the association | |
1058 // with the current origin. Multiple forms === no zero-click sign in. | |
1059 store_->AddLogin(affiliated_form1_); | |
1060 store_->AddLogin(affiliated_form2_); | |
1061 | |
1062 auto mock_helper = make_scoped_ptr(new MockAffiliatedMatchHelper); | |
1063 store_->SetAffiliatedMatchHelper(std::move(mock_helper)); | |
1064 | |
1065 std::vector<GURL> federations; | |
1066 std::vector<std::string> affiliated_realms; | |
1067 affiliated_realms.push_back(kTestAndroidRealm1); | |
1068 affiliated_realms.push_back(kTestAndroidRealm2); | |
1069 static_cast<MockAffiliatedMatchHelper*>(store_->affiliated_match_helper()) | |
1070 ->ExpectCallToGetAffiliatedAndroidRealms( | |
1071 dispatcher_->GetSynthesizedFormForOrigin(), affiliated_realms); | |
1072 | |
1073 dispatcher()->OnRequestCredential(kRequestId, true, true, federations); | |
1074 | |
1075 ExpectZeroClickSignInFailure(); | |
1076 } | |
1077 | |
1078 TEST_F(CredentialManagerDispatcherTest, | |
1079 ZeroClickWithUnaffiliatedFormsInPasswordStore) { | |
1080 // Insert the affiliated form into the store, but don't mock out the | |
1081 // association with the current origin. No association === no zero-click sign | |
1082 // in. | |
1083 store_->AddLogin(affiliated_form1_); | |
1084 | |
1085 auto mock_helper = make_scoped_ptr(new MockAffiliatedMatchHelper); | |
1086 store_->SetAffiliatedMatchHelper(std::move(mock_helper)); | |
1087 | |
1088 std::vector<GURL> federations; | |
1089 std::vector<std::string> affiliated_realms; | |
1090 static_cast<MockAffiliatedMatchHelper*>(store_->affiliated_match_helper()) | |
1091 ->ExpectCallToGetAffiliatedAndroidRealms( | |
1092 dispatcher_->GetSynthesizedFormForOrigin(), affiliated_realms); | |
1093 | |
1094 dispatcher()->OnRequestCredential(kRequestId, true, true, federations); | |
1095 | |
1096 ExpectZeroClickSignInFailure(); | |
1097 } | |
1098 | |
1099 TEST_F(CredentialManagerDispatcherTest, | |
1100 ZeroClickWithFormAndUnaffiliatedFormsInPasswordStore) { | |
1101 // Insert the affiliated form into the store, along with a real form for the | |
1102 // origin, and don't mock out the association with the current origin. No | |
1103 // association + existing form === zero-click sign in. | |
1104 store_->AddLogin(form_); | |
1105 store_->AddLogin(affiliated_form1_); | |
1106 | |
1107 auto mock_helper = make_scoped_ptr(new MockAffiliatedMatchHelper); | |
1108 store_->SetAffiliatedMatchHelper(std::move(mock_helper)); | |
1109 | |
1110 std::vector<GURL> federations; | |
1111 std::vector<std::string> affiliated_realms; | |
1112 static_cast<MockAffiliatedMatchHelper*>(store_->affiliated_match_helper()) | |
1113 ->ExpectCallToGetAffiliatedAndroidRealms( | |
1114 dispatcher_->GetSynthesizedFormForOrigin(), affiliated_realms); | |
1115 | |
1116 dispatcher()->OnRequestCredential(kRequestId, true, true, federations); | |
1117 | |
1118 ExpectZeroClickSignInSuccess(CredentialType::CREDENTIAL_TYPE_PASSWORD); | |
1119 } | |
1120 | |
1121 TEST_F(CredentialManagerDispatcherTest, GetSynthesizedFormForOrigin) { | |
1122 autofill::PasswordForm synthesized = | |
1123 dispatcher_->GetSynthesizedFormForOrigin(); | |
1124 EXPECT_EQ(kTestWebOrigin, synthesized.origin.spec()); | |
1125 EXPECT_EQ(kTestWebOrigin, synthesized.signon_realm); | |
1126 EXPECT_EQ(autofill::PasswordForm::SCHEME_HTML, synthesized.scheme); | |
1127 EXPECT_TRUE(synthesized.ssl_valid); | |
1128 } | |
1129 | |
1130 } // namespace password_manager | |
OLD | NEW |