1 // Copyright 2020 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/core/browser/ui/saved_passwords_presenter.h"
6 
7 #include "base/memory/scoped_refptr.h"
8 #include "base/scoped_observation.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "base/test/metrics/histogram_tester.h"
11 #include "base/test/task_environment.h"
12 #include "components/password_manager/core/browser/password_form.h"
13 #include "components/password_manager/core/browser/password_manager_metrics_util.h"
14 #include "components/password_manager/core/browser/test_password_store.h"
15 #include "testing/gmock/include/gmock/gmock.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17 
18 namespace password_manager {
19 
20 namespace {
21 
22 using ::testing::ElementsAre;
23 using ::testing::ElementsAreArray;
24 using ::testing::IsEmpty;
25 using ::testing::Pair;
26 using ::testing::UnorderedElementsAre;
27 
28 struct MockSavedPasswordsPresenterObserver : SavedPasswordsPresenter::Observer {
29   MOCK_METHOD(void, OnEdited, (const PasswordForm&), (override));
30   MOCK_METHOD(void,
31               OnSavedPasswordsChanged,
32               (SavedPasswordsPresenter::SavedPasswordsView),
33               (override));
34 };
35 
36 using StrictMockSavedPasswordsPresenterObserver =
37     ::testing::StrictMock<MockSavedPasswordsPresenterObserver>;
38 
39 class SavedPasswordsPresenterTest : public ::testing::Test {
40  protected:
SavedPasswordsPresenterTest()41   SavedPasswordsPresenterTest() { store_->Init(/*prefs=*/nullptr); }
42 
~SavedPasswordsPresenterTest()43   ~SavedPasswordsPresenterTest() override {
44     store_->ShutdownOnUIThread();
45     task_env_.RunUntilIdle();
46   }
47 
store()48   TestPasswordStore& store() { return *store_; }
presenter()49   SavedPasswordsPresenter& presenter() { return presenter_; }
50 
RunUntilIdle()51   void RunUntilIdle() { task_env_.RunUntilIdle(); }
52 
53  private:
54   base::test::SingleThreadTaskEnvironment task_env_;
55   scoped_refptr<TestPasswordStore> store_ =
56       base::MakeRefCounted<TestPasswordStore>();
57   SavedPasswordsPresenter presenter_{store_};
58 };
59 
60 }  // namespace
61 
62 // Tests whether adding and removing an observer works as expected.
TEST_F(SavedPasswordsPresenterTest,NotifyObservers)63 TEST_F(SavedPasswordsPresenterTest, NotifyObservers) {
64   PasswordForm form;
65 
66   StrictMockSavedPasswordsPresenterObserver observer;
67   presenter().AddObserver(&observer);
68 
69   // Adding a credential should notify observers. Furthermore, the credential
70   // should be present of the list that is passed along.
71   EXPECT_CALL(observer, OnSavedPasswordsChanged(
72                             ElementsAre(MatchesFormExceptStore(form))));
73   store().AddLogin(form);
74   RunUntilIdle();
75   EXPECT_FALSE(store().IsEmpty());
76 
77   // Remove should notify, and observers should be passed an empty list.
78   EXPECT_CALL(observer, OnSavedPasswordsChanged(IsEmpty()));
79   store().RemoveLogin(form);
80   RunUntilIdle();
81   EXPECT_TRUE(store().IsEmpty());
82 
83   // After an observer is removed it should no longer receive notifications.
84   presenter().RemoveObserver(&observer);
85   EXPECT_CALL(observer, OnSavedPasswordsChanged).Times(0);
86   store().AddLogin(form);
87   RunUntilIdle();
88   EXPECT_FALSE(store().IsEmpty());
89 }
90 
91 // Tests whether adding federated credentials doesn't inform the observers.
TEST_F(SavedPasswordsPresenterTest,IgnoredCredentials)92 TEST_F(SavedPasswordsPresenterTest, IgnoredCredentials) {
93   PasswordForm federated_form;
94   federated_form.federation_origin =
95       url::Origin::Create(GURL("https://example.com"));
96 
97   StrictMockSavedPasswordsPresenterObserver observer;
98   presenter().AddObserver(&observer);
99 
100   // Adding a credential should notify observers. However, since federated
101   // credentials should be ignored it should not be passed a long.
102   EXPECT_CALL(observer, OnSavedPasswordsChanged(IsEmpty()));
103   store().AddLogin(federated_form);
104   RunUntilIdle();
105 
106   PasswordForm blocked_form;
107   blocked_form.blocked_by_user = true;
108   EXPECT_CALL(observer, OnSavedPasswordsChanged(IsEmpty()));
109   store().AddLogin(blocked_form);
110   RunUntilIdle();
111 
112   presenter().RemoveObserver(&observer);
113 }
114 
115 // Tests whether editing a password works and results in the right
116 // notifications.
TEST_F(SavedPasswordsPresenterTest,EditPassword)117 TEST_F(SavedPasswordsPresenterTest, EditPassword) {
118   PasswordForm form;
119 
120   StrictMockSavedPasswordsPresenterObserver observer;
121   presenter().AddObserver(&observer);
122 
123   EXPECT_CALL(observer, OnSavedPasswordsChanged);
124   store().AddLogin(form);
125   RunUntilIdle();
126   EXPECT_FALSE(store().IsEmpty());
127 
128   // When |form| is read back from the store, its |in_store| member will be set,
129   // and SavedPasswordsPresenter::EditPassword() actually depends on that. So
130   // set it here too.
131   form.in_store = PasswordForm::Store::kProfileStore;
132 
133   const base::string16 new_password = base::ASCIIToUTF16("new_password");
134   PasswordForm updated = form;
135   updated.password_value = new_password;
136 
137   // Verify that editing a password triggers the right notifications.
138   EXPECT_CALL(observer, OnEdited(updated));
139   EXPECT_CALL(observer, OnSavedPasswordsChanged(ElementsAre(updated)));
140   EXPECT_TRUE(presenter().EditPassword(form, new_password));
141   RunUntilIdle();
142   EXPECT_THAT(store().stored_passwords(),
143               ElementsAre(Pair(updated.signon_realm, ElementsAre(updated))));
144 
145   // Verify that editing a password that does not exist does not triggers
146   // notifications.
147   form.username_value = base::ASCIIToUTF16("another_username");
148   EXPECT_CALL(observer, OnEdited).Times(0);
149   EXPECT_CALL(observer, OnSavedPasswordsChanged).Times(0);
150   EXPECT_FALSE(presenter().EditPassword(form, new_password));
151   RunUntilIdle();
152 
153   presenter().RemoveObserver(&observer);
154 }
155 
TEST_F(SavedPasswordsPresenterTest,EditOnlyUsername)156 TEST_F(SavedPasswordsPresenterTest, EditOnlyUsername) {
157   PasswordForm form;
158   form.signon_realm = "https://example.com";
159   form.username_value = base::ASCIIToUTF16("test@gmail.com");
160   form.password_value = base::ASCIIToUTF16("password");
161   form.in_store = PasswordForm::Store::kProfileStore;
162 
163   StrictMockSavedPasswordsPresenterObserver observer;
164   presenter().AddObserver(&observer);
165 
166   EXPECT_CALL(observer, OnSavedPasswordsChanged);
167   store().AddLogin(form);
168   RunUntilIdle();
169   EXPECT_FALSE(store().IsEmpty());
170 
171   std::vector<PasswordForm> forms = {form};
172 
173   const base::string16 new_username = base::ASCIIToUTF16("new_username");
174   PasswordForm updated_username = form;
175   updated_username.username_value = new_username;
176 
177   // Verify that editing a username triggers the right notifications.
178   base::HistogramTester histogram_tester;
179 
180   EXPECT_CALL(observer, OnEdited(updated_username));
181   EXPECT_CALL(observer, OnSavedPasswordsChanged(ElementsAre(updated_username)));
182   EXPECT_TRUE(
183       presenter().EditSavedPasswords(forms, new_username, form.password_value));
184   RunUntilIdle();
185   EXPECT_THAT(
186       store().stored_passwords(),
187       ElementsAre(Pair(form.signon_realm, ElementsAre(updated_username))));
188 
189   histogram_tester.ExpectUniqueSample(
190       "PasswordManager.PasswordEditUpdatedValues",
191       metrics_util::PasswordEditUpdatedValues::kUsername, 1);
192 
193   presenter().RemoveObserver(&observer);
194 }
195 
TEST_F(SavedPasswordsPresenterTest,EditOnlyPassword)196 TEST_F(SavedPasswordsPresenterTest, EditOnlyPassword) {
197   PasswordForm form;
198   form.signon_realm = "https://example.com";
199   form.username_value = base::ASCIIToUTF16("test@gmail.com");
200   form.password_value = base::ASCIIToUTF16("password");
201   form.in_store = PasswordForm::Store::kProfileStore;
202 
203   StrictMockSavedPasswordsPresenterObserver observer;
204   presenter().AddObserver(&observer);
205 
206   EXPECT_CALL(observer, OnSavedPasswordsChanged);
207   store().AddLogin(form);
208   RunUntilIdle();
209   EXPECT_FALSE(store().IsEmpty());
210 
211   std::vector<PasswordForm> forms = {form};
212 
213   const base::string16 new_password = base::ASCIIToUTF16("new_password");
214   PasswordForm updated_password = form;
215   updated_password.password_value = new_password;
216 
217   base::HistogramTester histogram_tester;
218   // Verify that editing a password triggers the right notifications.
219   EXPECT_CALL(observer, OnEdited(updated_password));
220   EXPECT_CALL(observer, OnSavedPasswordsChanged(ElementsAre(updated_password)));
221   EXPECT_TRUE(
222       presenter().EditSavedPasswords(forms, form.username_value, new_password));
223   RunUntilIdle();
224   EXPECT_THAT(
225       store().stored_passwords(),
226       ElementsAre(Pair(form.signon_realm, ElementsAre(updated_password))));
227   histogram_tester.ExpectUniqueSample(
228       "PasswordManager.PasswordEditUpdatedValues",
229       metrics_util::PasswordEditUpdatedValues::kPassword, 1);
230 
231   presenter().RemoveObserver(&observer);
232 }
233 
TEST_F(SavedPasswordsPresenterTest,EditUsernameAndPassword)234 TEST_F(SavedPasswordsPresenterTest, EditUsernameAndPassword) {
235   PasswordForm form;
236   form.signon_realm = "https://example.com";
237   form.username_value = base::ASCIIToUTF16("test@gmail.com");
238   form.password_value = base::ASCIIToUTF16("password");
239   form.in_store = PasswordForm::Store::kProfileStore;
240 
241   StrictMockSavedPasswordsPresenterObserver observer;
242   presenter().AddObserver(&observer);
243 
244   EXPECT_CALL(observer, OnSavedPasswordsChanged);
245   store().AddLogin(form);
246   RunUntilIdle();
247   EXPECT_FALSE(store().IsEmpty());
248 
249   std::vector<PasswordForm> forms = {form};
250 
251   const base::string16 new_username = base::ASCIIToUTF16("new_username");
252   const base::string16 new_password = base::ASCIIToUTF16("new_password");
253 
254   PasswordForm updated_both = form;
255   updated_both.username_value = new_username;
256   updated_both.password_value = new_password;
257 
258   base::HistogramTester histogram_tester;
259   // Verify that editing username and password triggers the right notifications.
260   EXPECT_CALL(observer, OnEdited(updated_both));
261   EXPECT_CALL(observer, OnSavedPasswordsChanged(ElementsAre(updated_both)));
262   EXPECT_TRUE(
263       presenter().EditSavedPasswords(forms, new_username, new_password));
264   RunUntilIdle();
265   EXPECT_THAT(store().stored_passwords(),
266               ElementsAre(Pair(form.signon_realm, ElementsAre(updated_both))));
267   histogram_tester.ExpectBucketCount(
268       "PasswordManager.PasswordEditUpdatedValues",
269       metrics_util::PasswordEditUpdatedValues::kBoth, 1);
270 
271   presenter().RemoveObserver(&observer);
272 }
273 
TEST_F(SavedPasswordsPresenterTest,EditPasswordFails)274 TEST_F(SavedPasswordsPresenterTest, EditPasswordFails) {
275   PasswordForm form1;
276   form1.signon_realm = "https://example.com";
277   form1.username_value = base::ASCIIToUTF16("test1@gmail.com");
278   form1.password_value = base::ASCIIToUTF16("password");
279   form1.in_store = PasswordForm::Store::kProfileStore;
280 
281   PasswordForm form2;
282   form2.signon_realm = "https://example.com";
283   form2.username_value = base::ASCIIToUTF16("test2@gmail.com");
284   form2.password_value = base::ASCIIToUTF16("password");
285   form2.in_store = PasswordForm::Store::kProfileStore;
286 
287   store().AddLogin(form1);
288   store().AddLogin(form2);
289   RunUntilIdle();
290   EXPECT_FALSE(store().IsEmpty());
291 
292   std::vector<PasswordForm> forms{form1};
293 
294   // Updating the form with the username which is already used for same website
295   // fails.
296   const base::string16 new_username = base::ASCIIToUTF16("test2@gmail.com");
297   EXPECT_FALSE(presenter().EditSavedPasswords(forms, new_username,
298                                               form1.password_value));
299   RunUntilIdle();
300   EXPECT_THAT(store().stored_passwords(),
301               ElementsAre(Pair(form1.signon_realm, ElementsAre(form1, form2))));
302 
303   // Updating the form with the empty password fails.
304   EXPECT_FALSE(presenter().EditSavedPasswords(forms, form1.username_value,
305                                               base::string16()));
306   RunUntilIdle();
307   EXPECT_THAT(store().stored_passwords(),
308               ElementsAre(Pair(form1.signon_realm, ElementsAre(form1, form2))));
309 }
310 
TEST_F(SavedPasswordsPresenterTest,EditPasswordWithoutChanges)311 TEST_F(SavedPasswordsPresenterTest, EditPasswordWithoutChanges) {
312   PasswordForm form;
313   form.signon_realm = "https://example.com";
314   form.username_value = base::ASCIIToUTF16("test1@gmail.com");
315   form.password_value = base::ASCIIToUTF16("password");
316   form.in_store = PasswordForm::Store::kProfileStore;
317 
318   store().AddLogin(form);
319 
320   RunUntilIdle();
321   StrictMockSavedPasswordsPresenterObserver observer;
322   presenter().AddObserver(&observer);
323 
324   EXPECT_FALSE(store().IsEmpty());
325   // Verify that editing a form without changing the username or password does
326   // not triggers notifications.
327   base::HistogramTester histogram_tester;
328   EXPECT_CALL(observer, OnEdited).Times(0);
329   EXPECT_CALL(observer, OnSavedPasswordsChanged).Times(0);
330   std::vector<PasswordForm> forms = {form};
331   EXPECT_TRUE(presenter().EditSavedPasswords(forms, form.username_value,
332                                              form.password_value));
333   RunUntilIdle();
334   histogram_tester.ExpectBucketCount(
335       "PasswordManager.PasswordEditUpdatedValues",
336       metrics_util::PasswordEditUpdatedValues::kNone, 1);
337 
338   presenter().RemoveObserver(&observer);
339 }
340 
341 namespace {
342 
343 class SavedPasswordsPresenterWithTwoStoresTest : public ::testing::Test {
344  protected:
SavedPasswordsPresenterWithTwoStoresTest()345   SavedPasswordsPresenterWithTwoStoresTest() {
346     profile_store_->Init(/*prefs=*/nullptr);
347     account_store_->Init(/*prefs=*/nullptr);
348   }
349 
~SavedPasswordsPresenterWithTwoStoresTest()350   ~SavedPasswordsPresenterWithTwoStoresTest() override {
351     account_store_->ShutdownOnUIThread();
352     profile_store_->ShutdownOnUIThread();
353     task_env_.RunUntilIdle();
354   }
355 
profile_store()356   TestPasswordStore& profile_store() { return *profile_store_; }
account_store()357   TestPasswordStore& account_store() { return *account_store_; }
presenter()358   SavedPasswordsPresenter& presenter() { return presenter_; }
359 
RunUntilIdle()360   void RunUntilIdle() { task_env_.RunUntilIdle(); }
361 
362  private:
363   base::test::SingleThreadTaskEnvironment task_env_;
364   scoped_refptr<TestPasswordStore> profile_store_ =
365       base::MakeRefCounted<TestPasswordStore>(IsAccountStore(false));
366   scoped_refptr<TestPasswordStore> account_store_ =
367       base::MakeRefCounted<TestPasswordStore>(IsAccountStore(true));
368   SavedPasswordsPresenter presenter_{profile_store_, account_store_};
369 };
370 
371 }  // namespace
372 
373 // Tests whether adding credentials to profile or account store notifies
374 // observers with credentials in both stores.
TEST_F(SavedPasswordsPresenterWithTwoStoresTest,AddCredentialsToBothStores)375 TEST_F(SavedPasswordsPresenterWithTwoStoresTest, AddCredentialsToBothStores) {
376   PasswordForm profile_store_form;
377   profile_store_form.username_value = base::ASCIIToUTF16("profile@gmail.com");
378   profile_store_form.password_value = base::ASCIIToUTF16("profile_pass");
379   profile_store_form.in_store = PasswordForm::Store::kProfileStore;
380 
381   PasswordForm account_store_form1;
382   account_store_form1.username_value = base::ASCIIToUTF16("account@gmail.com");
383   account_store_form1.password_value = base::ASCIIToUTF16("account_pass");
384   account_store_form1.in_store = PasswordForm::Store::kAccountStore;
385 
386   PasswordForm account_store_form2 = account_store_form1;
387   account_store_form2.username_value = base::ASCIIToUTF16("account2@gmail.com");
388 
389   StrictMockSavedPasswordsPresenterObserver observer;
390   presenter().AddObserver(&observer);
391 
392   EXPECT_CALL(observer, OnSavedPasswordsChanged(
393                             UnorderedElementsAre(profile_store_form)));
394   profile_store().AddLogin(profile_store_form);
395   RunUntilIdle();
396 
397   EXPECT_CALL(observer, OnSavedPasswordsChanged(UnorderedElementsAre(
398                             profile_store_form, account_store_form1)));
399   account_store().AddLogin(account_store_form1);
400   RunUntilIdle();
401 
402   EXPECT_CALL(observer, OnSavedPasswordsChanged(UnorderedElementsAre(
403                             profile_store_form, account_store_form1,
404                             account_store_form2)));
405   account_store().AddLogin(account_store_form2);
406   RunUntilIdle();
407 
408   EXPECT_CALL(observer, OnSavedPasswordsChanged(UnorderedElementsAre(
409                             account_store_form1, account_store_form2)));
410   profile_store().RemoveLogin(profile_store_form);
411   RunUntilIdle();
412 
413   EXPECT_CALL(observer, OnSavedPasswordsChanged(UnorderedElementsAre(
414                             profile_store_form, account_store_form1,
415                             account_store_form2)));
416   profile_store().AddLogin(profile_store_form);
417   RunUntilIdle();
418 
419   presenter().RemoveObserver(&observer);
420 }
421 
422 // This tests changing the username of a credentials stored in the profile store
423 // to be equal to a username of a credential stored in the account store for the
424 // same domain.
TEST_F(SavedPasswordsPresenterWithTwoStoresTest,EditUsername)425 TEST_F(SavedPasswordsPresenterWithTwoStoresTest, EditUsername) {
426   PasswordForm profile_store_form;
427   profile_store_form.username_value = base::ASCIIToUTF16("profile@gmail.com");
428   profile_store_form.password_value = base::ASCIIToUTF16("profile_pass");
429   profile_store_form.in_store = PasswordForm::Store::kProfileStore;
430 
431   PasswordForm account_store_form;
432   account_store_form.username_value = base::ASCIIToUTF16("account@gmail.com");
433   account_store_form.password_value = base::ASCIIToUTF16("account_pass");
434   account_store_form.in_store = PasswordForm::Store::kAccountStore;
435 
436   profile_store().AddLogin(profile_store_form);
437   account_store().AddLogin(account_store_form);
438   RunUntilIdle();
439 
440   EXPECT_THAT(profile_store().stored_passwords(),
441               ElementsAre(Pair(profile_store_form.signon_realm,
442                                ElementsAre(profile_store_form))));
443 
444   auto new_username = account_store_form.username_value;
445   std::vector<PasswordForm> forms_to_edit{profile_store_form};
446   EXPECT_TRUE(presenter().EditSavedPasswords(
447       forms_to_edit, new_username, profile_store_form.password_value));
448   RunUntilIdle();
449   profile_store_form.username_value = new_username;
450   EXPECT_THAT(profile_store().stored_passwords(),
451               ElementsAre(Pair(profile_store_form.signon_realm,
452                                ElementsAre(profile_store_form))));
453 }
454 
455 }  // namespace password_manager
456