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