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/bulk_leak_check_service_adapter.h"
6 
7 #include <memory>
8 #include <tuple>
9 #include <vector>
10 
11 #include "base/containers/span.h"
12 #include "base/memory/ptr_util.h"
13 #include "base/strings/string_piece_forward.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/test/gmock_move_support.h"
16 #include "base/test/task_environment.h"
17 #include "components/autofill/core/common/password_form.h"
18 #include "components/password_manager/core/browser/bulk_leak_check_service.h"
19 #include "components/password_manager/core/browser/leak_detection/bulk_leak_check.h"
20 #include "components/password_manager/core/browser/leak_detection/leak_detection_check_factory.h"
21 #include "components/password_manager/core/browser/leak_detection/mock_leak_detection_check_factory.h"
22 #include "components/password_manager/core/browser/test_password_store.h"
23 #include "components/password_manager/core/browser/ui/saved_passwords_presenter.h"
24 #include "components/password_manager/core/common/password_manager_pref_names.h"
25 #include "components/prefs/pref_registry_simple.h"
26 #include "components/prefs/testing_pref_service.h"
27 #include "components/safe_browsing/core/common/safe_browsing_prefs.h"
28 #include "components/signin/public/identity_manager/identity_test_environment.h"
29 #include "services/network/test/test_shared_url_loader_factory.h"
30 #include "testing/gmock/include/gmock/gmock.h"
31 #include "testing/gtest/include/gtest/gtest.h"
32 
33 namespace password_manager {
34 namespace {
35 
36 constexpr char kExampleCom[] = "https://example.com";
37 constexpr char kExampleOrg[] = "https://example.org";
38 
39 constexpr char kUsername1[] = "alice";
40 constexpr char kUsername2[] = "bob";
41 
42 constexpr char kPassword1[] = "f00b4r";
43 constexpr char kPassword2[] = "s3cr3t";
44 
45 using autofill::PasswordForm;
46 using ::testing::ByMove;
47 using ::testing::NiceMock;
48 using ::testing::Return;
49 
50 MATCHER_P(CredentialsAre, credentials, "") {
51   return std::equal(arg.begin(), arg.end(), credentials.get().begin(),
52                     credentials.get().end(),
__anon61c2ae730202(const auto& lhs, const auto& rhs) 53                     [](const auto& lhs, const auto& rhs) {
54                       return lhs.username() == rhs.username() &&
55                              lhs.password() == rhs.password();
56                     });
57 }
58 
59 MATCHER_P(SavedPasswordsAre, passwords, "") {
60   return std::equal(arg.begin(), arg.end(), passwords.begin(), passwords.end(),
__anon61c2ae730302(const auto& lhs, const auto& rhs) 61                     [](const auto& lhs, const auto& rhs) {
62                       return lhs.signon_realm == rhs.signon_realm &&
63                              lhs.username_value == rhs.username_value &&
64                              lhs.password_value == rhs.password_value;
65                     });
66 }
67 
MakeSavedPassword(base::StringPiece signon_realm,base::StringPiece username,base::StringPiece password)68 PasswordForm MakeSavedPassword(base::StringPiece signon_realm,
69                                base::StringPiece username,
70                                base::StringPiece password) {
71   PasswordForm form;
72   form.signon_realm = std::string(signon_realm);
73   form.username_value = base::ASCIIToUTF16(username);
74   form.password_value = base::ASCIIToUTF16(password);
75   return form;
76 }
77 
MakeLeakCheckCredential(base::StringPiece username,base::StringPiece password)78 LeakCheckCredential MakeLeakCheckCredential(base::StringPiece username,
79                                             base::StringPiece password) {
80   return LeakCheckCredential(base::ASCIIToUTF16(username),
81                              base::ASCIIToUTF16(password));
82 }
83 
84 struct MockBulkLeakCheck : BulkLeakCheck {
85   MOCK_METHOD(void,
86               CheckCredentials,
87               (std::vector<LeakCheckCredential> credentials),
88               (override));
89   MOCK_METHOD(size_t, GetPendingChecksCount, (), (const override));
90 };
91 
92 using NiceMockBulkLeakCheck = ::testing::NiceMock<MockBulkLeakCheck>;
93 
94 class BulkLeakCheckServiceAdapterTest : public ::testing::Test {
95  public:
BulkLeakCheckServiceAdapterTest()96   BulkLeakCheckServiceAdapterTest() {
97     auto factory = std::make_unique<MockLeakDetectionCheckFactory>();
98     factory_ = factory.get();
99     service_.set_leak_factory(std::move(factory));
100     store_->Init(/*prefs=*/nullptr);
101     prefs_.registry()->RegisterBooleanPref(prefs::kPasswordLeakDetectionEnabled,
102                                            true);
103     prefs_.registry()->RegisterBooleanPref(::prefs::kSafeBrowsingEnabled, true);
104     prefs_.registry()->RegisterBooleanPref(::prefs::kSafeBrowsingEnhanced,
105                                            false);
106   }
107 
~BulkLeakCheckServiceAdapterTest()108   ~BulkLeakCheckServiceAdapterTest() override {
109     store_->ShutdownOnUIThread();
110     task_env_.RunUntilIdle();
111   }
112 
store()113   TestPasswordStore& store() { return *store_; }
presenter()114   SavedPasswordsPresenter& presenter() { return presenter_; }
factory()115   MockLeakDetectionCheckFactory& factory() { return *factory_; }
prefs()116   PrefService& prefs() { return prefs_; }
adapter()117   BulkLeakCheckServiceAdapter& adapter() { return adapter_; }
118 
RunUntilIdle()119   void RunUntilIdle() { task_env_.RunUntilIdle(); }
120 
121  private:
122   base::test::TaskEnvironment task_env_;
123   signin::IdentityTestEnvironment identity_test_env_;
124   scoped_refptr<TestPasswordStore> store_ =
125       base::MakeRefCounted<TestPasswordStore>();
126   SavedPasswordsPresenter presenter_{store_};
127   BulkLeakCheckService service_{
128       identity_test_env_.identity_manager(),
129       base::MakeRefCounted<network::TestSharedURLLoaderFactory>()};
130   MockLeakDetectionCheckFactory* factory_ = nullptr;
131   TestingPrefServiceSimple prefs_;
132   BulkLeakCheckServiceAdapter adapter_{&presenter_, &service_, &prefs_};
133 };
134 
135 }  // namespace
136 
TEST_F(BulkLeakCheckServiceAdapterTest,OnCreation)137 TEST_F(BulkLeakCheckServiceAdapterTest, OnCreation) {
138   EXPECT_EQ(0u, adapter().GetPendingChecksCount());
139   EXPECT_EQ(BulkLeakCheckService::State::kIdle,
140             adapter().GetBulkLeakCheckState());
141 }
142 
143 // Checks that starting a leak check correctly transforms the list of saved
144 // passwords into LeakCheckCredentials and attaches the underlying password
145 // forms as user data.
TEST_F(BulkLeakCheckServiceAdapterTest,StartBulkLeakCheck)146 TEST_F(BulkLeakCheckServiceAdapterTest, StartBulkLeakCheck) {
147   std::vector<PasswordForm> passwords = {
148       MakeSavedPassword(kExampleCom, kUsername1, kPassword1),
149       MakeSavedPassword(kExampleOrg, kUsername2, kPassword2)};
150   store().AddLogin(passwords[0]);
151   store().AddLogin(passwords[1]);
152   RunUntilIdle();
153 
154   auto leak_check = std::make_unique<NiceMockBulkLeakCheck>();
155   std::vector<LeakCheckCredential> credentials;
156   EXPECT_CALL(*leak_check, CheckCredentials).WillOnce(MoveArg(&credentials));
157   EXPECT_CALL(factory(), TryCreateBulkLeakCheck)
158       .WillOnce(Return(ByMove(std::move(leak_check))));
159   adapter().StartBulkLeakCheck();
160 
161   std::vector<LeakCheckCredential> expected;
162   expected.push_back(MakeLeakCheckCredential(kUsername1, kPassword1));
163   expected.push_back(MakeLeakCheckCredential(kUsername2, kPassword2));
164 
165   EXPECT_THAT(credentials, CredentialsAre(std::cref(expected)));
166 }
167 
TEST_F(BulkLeakCheckServiceAdapterTest,StartBulkLeakCheckAttachesData)168 TEST_F(BulkLeakCheckServiceAdapterTest, StartBulkLeakCheckAttachesData) {
169   constexpr char kKey[] = "key";
170   struct UserData : LeakCheckCredential::Data {
171     std::unique_ptr<Data> Clone() override { return std::make_unique<Data>(); }
172   } data;
173 
174   std::vector<PasswordForm> passwords = {
175       MakeSavedPassword(kExampleCom, kUsername1, kPassword1)};
176   store().AddLogin(passwords[0]);
177   RunUntilIdle();
178 
179   auto leak_check = std::make_unique<NiceMockBulkLeakCheck>();
180   std::vector<LeakCheckCredential> credentials;
181   EXPECT_CALL(*leak_check, CheckCredentials).WillOnce(MoveArg(&credentials));
182   EXPECT_CALL(factory(), TryCreateBulkLeakCheck)
183       .WillOnce(Return(ByMove(std::move(leak_check))));
184   adapter().StartBulkLeakCheck(kKey, &data);
185 
186   EXPECT_NE(nullptr, credentials.at(0).GetUserData(kKey));
187 }
188 
189 // Tests that multiple credentials with effectively the same username are
190 // correctly deduped before starting the leak check.
TEST_F(BulkLeakCheckServiceAdapterTest,StartBulkLeakCheckDedupes)191 TEST_F(BulkLeakCheckServiceAdapterTest, StartBulkLeakCheckDedupes) {
192   std::vector<PasswordForm> passwords = {
193       MakeSavedPassword(kExampleCom, "alice", kPassword1),
194       MakeSavedPassword(kExampleCom, "ALICE", kPassword1),
195       MakeSavedPassword(kExampleCom, "Alice@example.com", kPassword1)};
196 
197   store().AddLogin(passwords[0]);
198   store().AddLogin(passwords[1]);
199   store().AddLogin(passwords[2]);
200   RunUntilIdle();
201 
202   auto leak_check = std::make_unique<NiceMockBulkLeakCheck>();
203   std::vector<LeakCheckCredential> credentials;
204   EXPECT_CALL(*leak_check, CheckCredentials).WillOnce(MoveArg(&credentials));
205   EXPECT_CALL(factory(), TryCreateBulkLeakCheck)
206       .WillOnce(Return(ByMove(std::move(leak_check))));
207   adapter().StartBulkLeakCheck();
208 
209   std::vector<LeakCheckCredential> expected;
210   expected.push_back(MakeLeakCheckCredential("alice", kPassword1));
211   EXPECT_THAT(credentials, CredentialsAre(std::cref(expected)));
212 }
213 
214 // Checks that trying to start a leak check when another check is already
215 // running does nothing and returns false to the caller.
TEST_F(BulkLeakCheckServiceAdapterTest,MultipleStarts)216 TEST_F(BulkLeakCheckServiceAdapterTest, MultipleStarts) {
217   store().AddLogin(MakeSavedPassword(kExampleCom, "alice", kPassword1));
218   RunUntilIdle();
219 
220   auto leak_check = std::make_unique<NiceMockBulkLeakCheck>();
221   auto& leak_check_ref = *leak_check;
222   EXPECT_CALL(leak_check_ref, CheckCredentials);
223   EXPECT_CALL(factory(), TryCreateBulkLeakCheck)
224       .WillOnce(Return(ByMove(std::move(leak_check))));
225   EXPECT_TRUE(adapter().StartBulkLeakCheck());
226 
227   EXPECT_CALL(leak_check_ref, CheckCredentials).Times(0);
228   EXPECT_FALSE(adapter().StartBulkLeakCheck());
229 }
230 
231 // Checks that stopping the leak check correctly resets the state of the bulk
232 // leak check.
TEST_F(BulkLeakCheckServiceAdapterTest,StopBulkLeakCheck)233 TEST_F(BulkLeakCheckServiceAdapterTest, StopBulkLeakCheck) {
234   store().AddLogin(MakeSavedPassword(kExampleCom, "alice", kPassword1));
235   RunUntilIdle();
236 
237   auto leak_check = std::make_unique<NiceMockBulkLeakCheck>();
238   EXPECT_CALL(*leak_check, CheckCredentials);
239   EXPECT_CALL(factory(), TryCreateBulkLeakCheck)
240       .WillOnce(Return(ByMove(std::move(leak_check))));
241   adapter().StartBulkLeakCheck();
242   EXPECT_EQ(BulkLeakCheckService::State::kRunning,
243             adapter().GetBulkLeakCheckState());
244 
245   adapter().StopBulkLeakCheck();
246   EXPECT_EQ(BulkLeakCheckService::State::kCanceled,
247             adapter().GetBulkLeakCheckState());
248 }
249 
250 // Tests that editing a password through the presenter does not result in
251 // another call to CheckCredentials with a corresponding change to the checked
252 // password if the corresponding prefs are not set.
TEST_F(BulkLeakCheckServiceAdapterTest,OnEditedNoPrefs)253 TEST_F(BulkLeakCheckServiceAdapterTest, OnEditedNoPrefs) {
254   prefs().SetBoolean(prefs::kPasswordLeakDetectionEnabled, false);
255   prefs().SetBoolean(::prefs::kSafeBrowsingEnabled, false);
256 
257   PasswordForm password =
258       MakeSavedPassword(kExampleCom, kUsername1, kPassword1);
259   store().AddLogin(password);
260   // When |password| is read back from the store, its |in_store| member will be
261   // set, and SavedPasswordsPresenter::EditPassword() actually depends on that.
262   // So set it here too.
263   password.in_store = PasswordForm::Store::kProfileStore;
264   RunUntilIdle();
265 
266   EXPECT_CALL(factory(), TryCreateBulkLeakCheck).Times(0);
267   presenter().EditPassword(password, base::ASCIIToUTF16(kPassword2));
268 }
269 
270 // Tests that editing a password through the presenter will result in another
271 // call to CheckCredentials with a corresponding change to the checked password
272 // if the corresponding prefs are set.
TEST_F(BulkLeakCheckServiceAdapterTest,OnEditedWithPrefs)273 TEST_F(BulkLeakCheckServiceAdapterTest, OnEditedWithPrefs) {
274   PasswordForm password =
275       MakeSavedPassword(kExampleCom, kUsername1, kPassword1);
276   store().AddLogin(password);
277   // When |password| is read back from the store, its |in_store| member will be
278   // set, and SavedPasswordsPresenter::EditPassword() actually depends on that.
279   // So set it here too.
280   password.in_store = PasswordForm::Store::kProfileStore;
281   RunUntilIdle();
282 
283   std::vector<LeakCheckCredential> expected;
284   expected.push_back(MakeLeakCheckCredential(kUsername1, kPassword2));
285 
286   auto leak_check = std::make_unique<NiceMockBulkLeakCheck>();
287   EXPECT_CALL(*leak_check,
288               CheckCredentials(CredentialsAre(std::cref(expected))));
289   EXPECT_CALL(factory(), TryCreateBulkLeakCheck)
290       .WillOnce(Return(ByMove(std::move(leak_check))));
291   presenter().EditPassword(password, base::ASCIIToUTF16(kPassword2));
292 }
293 
294 }  // namespace password_manager
295