1 // Copyright 2015 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/android_affiliation/affiliated_match_helper.h"
6
7 #include <stddef.h>
8
9 #include <memory>
10 #include <utility>
11
12 #include "base/bind.h"
13 #include "base/macros.h"
14 #include "base/memory/ref_counted.h"
15 #include "base/run_loop.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/test/scoped_mock_time_message_loop_task_runner.h"
18 #include "base/test/task_environment.h"
19 #include "components/password_manager/core/browser/android_affiliation/affiliation_service.h"
20 #include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h"
21 #include "components/password_manager/core/browser/test_password_store.h"
22 #include "testing/gmock/include/gmock/gmock.h"
23 #include "testing/gtest/include/gtest/gtest.h"
24
25 namespace password_manager {
26
27 namespace {
28
29 using StrategyOnCacheMiss = AffiliationService::StrategyOnCacheMiss;
30
31 class MockAffiliationService : public testing::StrictMock<AffiliationService> {
32 public:
MockAffiliationService()33 MockAffiliationService() : testing::StrictMock<AffiliationService>(nullptr) {
34 testing::DefaultValue<AffiliatedFacets>::Set(AffiliatedFacets());
35 }
36
~MockAffiliationService()37 ~MockAffiliationService() override {}
38
39 MOCK_METHOD2(OnGetAffiliationsAndBrandingCalled,
40 AffiliatedFacets(const FacetURI&, StrategyOnCacheMiss));
41 MOCK_METHOD2(Prefetch, void(const FacetURI&, const base::Time&));
42 MOCK_METHOD2(CancelPrefetch, void(const FacetURI&, const base::Time&));
43 MOCK_METHOD1(TrimCacheForFacetURI, void(const FacetURI&));
44
GetAffiliationsAndBranding(const FacetURI & facet_uri,StrategyOnCacheMiss cache_miss_strategy,ResultCallback result_callback)45 void GetAffiliationsAndBranding(const FacetURI& facet_uri,
46 StrategyOnCacheMiss cache_miss_strategy,
47 ResultCallback result_callback) override {
48 AffiliatedFacets affiliation =
49 OnGetAffiliationsAndBrandingCalled(facet_uri, cache_miss_strategy);
50 std::move(result_callback).Run(affiliation, !affiliation.empty());
51 }
52
ExpectCallToGetAffiliationsAndBrandingAndSucceedWithResult(const FacetURI & expected_facet_uri,StrategyOnCacheMiss expected_cache_miss_strategy,const AffiliatedFacets & affiliations_to_return)53 void ExpectCallToGetAffiliationsAndBrandingAndSucceedWithResult(
54 const FacetURI& expected_facet_uri,
55 StrategyOnCacheMiss expected_cache_miss_strategy,
56 const AffiliatedFacets& affiliations_to_return) {
57 EXPECT_CALL(*this, OnGetAffiliationsAndBrandingCalled(
58 expected_facet_uri, expected_cache_miss_strategy))
59 .WillOnce(testing::Return(affiliations_to_return));
60 }
61
ExpectCallToGetAffiliationsAndBrandingAndEmulateFailure(const FacetURI & expected_facet_uri,StrategyOnCacheMiss expected_cache_miss_strategy)62 void ExpectCallToGetAffiliationsAndBrandingAndEmulateFailure(
63 const FacetURI& expected_facet_uri,
64 StrategyOnCacheMiss expected_cache_miss_strategy) {
65 EXPECT_CALL(*this, OnGetAffiliationsAndBrandingCalled(
66 expected_facet_uri, expected_cache_miss_strategy))
67 .WillOnce(testing::Return(AffiliatedFacets()));
68 }
69
ExpectCallToPrefetch(const char * expected_facet_uri_spec)70 void ExpectCallToPrefetch(const char* expected_facet_uri_spec) {
71 EXPECT_CALL(*this,
72 Prefetch(FacetURI::FromCanonicalSpec(expected_facet_uri_spec),
73 base::Time::Max()))
74 .RetiresOnSaturation();
75 }
76
ExpectCallToCancelPrefetch(const char * expected_facet_uri_spec)77 void ExpectCallToCancelPrefetch(const char* expected_facet_uri_spec) {
78 EXPECT_CALL(*this, CancelPrefetch(
79 FacetURI::FromCanonicalSpec(expected_facet_uri_spec),
80 base::Time::Max()))
81 .RetiresOnSaturation();
82 }
83
ExpectCallToTrimCacheForFacetURI(const char * expected_facet_uri_spec)84 void ExpectCallToTrimCacheForFacetURI(const char* expected_facet_uri_spec) {
85 EXPECT_CALL(*this, TrimCacheForFacetURI(FacetURI::FromCanonicalSpec(
86 expected_facet_uri_spec)))
87 .RetiresOnSaturation();
88 }
89
90 private:
91 DISALLOW_ASSIGN(MockAffiliationService);
92 };
93
94 const char kTestWebFacetURIAlpha1[] = "https://one.alpha.example.com";
95 const char kTestWebFacetURIAlpha2[] = "https://two.alpha.example.com";
96 const char kTestAndroidFacetURIAlpha3[] =
97 "android://hash@com.example.alpha.android";
98 const char kTestAndroidFacetNameAlpha3[] = "Facet Name Alpha 3";
99 const char kTestAndroidFacetIconURLAlpha3[] = "https://example.com/alpha_3.png";
100 const char kTestWebRealmAlpha1[] = "https://one.alpha.example.com/";
101 const char kTestWebRealmAlpha2[] = "https://two.alpha.example.com/";
102 const char kTestAndroidRealmAlpha3[] =
103 "android://hash@com.example.alpha.android/";
104
105 const char kTestWebFacetURIBeta1[] = "https://one.beta.example.com";
106 const char kTestAndroidFacetURIBeta2[] =
107 "android://hash@com.example.beta.android";
108 const char kTestAndroidFacetNameBeta2[] = "Facet Name Beta 2";
109 const char kTestAndroidFacetIconURLBeta2[] = "https://example.com/beta_2.png";
110 const char kTestAndroidFacetURIBeta3[] =
111 "android://hash@com.yetanother.beta.android";
112 const char kTestAndroidFacetNameBeta3[] = "Facet Name Beta 3";
113 const char kTestAndroidFacetIconURLBeta3[] = "https://example.com/beta_3.png";
114 const char kTestWebRealmBeta1[] = "https://one.beta.example.com/";
115 const char kTestAndroidRealmBeta2[] =
116 "android://hash@com.example.beta.android/";
117 const char kTestAndroidRealmBeta3[] =
118 "android://hash@com.yetanother.beta.android/";
119
120 const char kTestAndroidFacetURIGamma[] =
121 "android://hash@com.example.gamma.android";
122 const char kTestAndroidRealmGamma[] =
123 "android://hash@com.example.gamma.android";
124
125 const char kTestUsername[] = "JohnDoe";
126 const char kTestPassword[] = "secret";
127
GetTestEquivalenceClassAlpha()128 AffiliatedFacets GetTestEquivalenceClassAlpha() {
129 return {
130 {FacetURI::FromCanonicalSpec(kTestWebFacetURIAlpha1)},
131 {FacetURI::FromCanonicalSpec(kTestWebFacetURIAlpha2)},
132 {FacetURI::FromCanonicalSpec(kTestAndroidFacetURIAlpha3),
133 FacetBrandingInfo{kTestAndroidFacetNameAlpha3,
134 GURL(kTestAndroidFacetIconURLAlpha3)}},
135 };
136 }
137
GetTestEquivalenceClassBeta()138 AffiliatedFacets GetTestEquivalenceClassBeta() {
139 return {
140 {FacetURI::FromCanonicalSpec(kTestWebFacetURIBeta1)},
141 {FacetURI::FromCanonicalSpec(kTestAndroidFacetURIBeta2),
142 FacetBrandingInfo{kTestAndroidFacetNameBeta2,
143 GURL(kTestAndroidFacetIconURLBeta2)}},
144 {FacetURI::FromCanonicalSpec(kTestAndroidFacetURIBeta3),
145 FacetBrandingInfo{kTestAndroidFacetNameBeta3,
146 GURL(kTestAndroidFacetIconURLBeta3)}},
147 };
148 }
149
GetTestAndroidCredentials(const char * signon_realm)150 autofill::PasswordForm GetTestAndroidCredentials(const char* signon_realm) {
151 autofill::PasswordForm form;
152 form.scheme = autofill::PasswordForm::Scheme::kHtml;
153 form.signon_realm = signon_realm;
154 form.username_value = base::ASCIIToUTF16(kTestUsername);
155 form.password_value = base::ASCIIToUTF16(kTestPassword);
156 return form;
157 }
158
GetTestBlacklistedAndroidCredentials(const char * signon_realm)159 autofill::PasswordForm GetTestBlacklistedAndroidCredentials(
160 const char* signon_realm) {
161 autofill::PasswordForm form = GetTestAndroidCredentials(signon_realm);
162 form.blacklisted_by_user = true;
163 return form;
164 }
165
GetTestObservedWebForm(const char * signon_realm,const char * origin)166 PasswordStore::FormDigest GetTestObservedWebForm(const char* signon_realm,
167 const char* origin) {
168 return {autofill::PasswordForm::Scheme::kHtml, signon_realm,
169 origin ? GURL(origin) : GURL()};
170 }
171
172 } // namespace
173
174 class AffiliatedMatchHelperTest : public testing::Test {
175 public:
AffiliatedMatchHelperTest()176 AffiliatedMatchHelperTest()
177 : expecting_result_callback_(false), mock_affiliation_service_(nullptr) {}
~AffiliatedMatchHelperTest()178 ~AffiliatedMatchHelperTest() override {}
179
180 protected:
RunDeferredInitialization()181 void RunDeferredInitialization() {
182 mock_time_task_runner_->RunUntilIdle();
183 ASSERT_EQ(AffiliatedMatchHelper::kInitializationDelayOnStartup,
184 mock_time_task_runner_->NextPendingTaskDelay());
185 mock_time_task_runner_->FastForwardBy(
186 AffiliatedMatchHelper::kInitializationDelayOnStartup);
187 }
188
ExpectNoDeferredTasks()189 void ExpectNoDeferredTasks() {
190 mock_time_task_runner_->RunUntilIdle();
191 ASSERT_FALSE(mock_time_task_runner_->HasPendingTask());
192 }
193
RunUntilIdle()194 void RunUntilIdle() {
195 // TODO(gab): Add support for base::RunLoop().RunUntilIdle() in scope of
196 // ScopedMockTimeMessageLoopTaskRunner and use it instead of this helper
197 // method.
198 mock_time_task_runner_->RunUntilIdle();
199 }
200
AddLogin(const autofill::PasswordForm & form)201 void AddLogin(const autofill::PasswordForm& form) {
202 password_store_->AddLogin(form);
203 RunUntilIdle();
204 }
205
UpdateLoginWithPrimaryKey(const autofill::PasswordForm & new_form,const autofill::PasswordForm & old_primary_key)206 void UpdateLoginWithPrimaryKey(
207 const autofill::PasswordForm& new_form,
208 const autofill::PasswordForm& old_primary_key) {
209 password_store_->UpdateLoginWithPrimaryKey(new_form, old_primary_key);
210 RunUntilIdle();
211 }
212
RemoveLogin(const autofill::PasswordForm & form)213 void RemoveLogin(const autofill::PasswordForm& form) {
214 password_store_->RemoveLogin(form);
215 RunUntilIdle();
216 }
217
AddAndroidAndNonAndroidTestLogins()218 void AddAndroidAndNonAndroidTestLogins() {
219 AddLogin(GetTestAndroidCredentials(kTestAndroidRealmAlpha3));
220 AddLogin(GetTestAndroidCredentials(kTestAndroidRealmBeta2));
221 AddLogin(GetTestBlacklistedAndroidCredentials(kTestAndroidRealmBeta3));
222 AddLogin(GetTestAndroidCredentials(kTestAndroidRealmGamma));
223
224 AddLogin(GetTestAndroidCredentials(kTestWebRealmAlpha1));
225 AddLogin(GetTestAndroidCredentials(kTestWebRealmAlpha2));
226 }
227
RemoveAndroidAndNonAndroidTestLogins()228 void RemoveAndroidAndNonAndroidTestLogins() {
229 RemoveLogin(GetTestAndroidCredentials(kTestAndroidRealmAlpha3));
230 RemoveLogin(GetTestAndroidCredentials(kTestAndroidRealmBeta2));
231 RemoveLogin(GetTestBlacklistedAndroidCredentials(kTestAndroidRealmBeta3));
232 RemoveLogin(GetTestAndroidCredentials(kTestAndroidRealmGamma));
233
234 RemoveLogin(GetTestAndroidCredentials(kTestWebRealmAlpha1));
235 RemoveLogin(GetTestAndroidCredentials(kTestWebRealmAlpha2));
236 }
237
ExpectPrefetchForAndroidTestLogins()238 void ExpectPrefetchForAndroidTestLogins() {
239 mock_affiliation_service()->ExpectCallToPrefetch(
240 kTestAndroidFacetURIAlpha3);
241 mock_affiliation_service()->ExpectCallToPrefetch(kTestAndroidFacetURIBeta2);
242 mock_affiliation_service()->ExpectCallToPrefetch(kTestAndroidFacetURIBeta3);
243 mock_affiliation_service()->ExpectCallToPrefetch(kTestAndroidFacetURIGamma);
244 }
245
ExpectCancelPrefetchForAndroidTestLogins()246 void ExpectCancelPrefetchForAndroidTestLogins() {
247 mock_affiliation_service()->ExpectCallToCancelPrefetch(
248 kTestAndroidFacetURIAlpha3);
249 mock_affiliation_service()->ExpectCallToCancelPrefetch(
250 kTestAndroidFacetURIBeta2);
251 mock_affiliation_service()->ExpectCallToCancelPrefetch(
252 kTestAndroidFacetURIBeta3);
253 mock_affiliation_service()->ExpectCallToCancelPrefetch(
254 kTestAndroidFacetURIGamma);
255 }
256
ExpectTrimCacheForAndroidTestLogins()257 void ExpectTrimCacheForAndroidTestLogins() {
258 mock_affiliation_service()->ExpectCallToTrimCacheForFacetURI(
259 kTestAndroidFacetURIAlpha3);
260 mock_affiliation_service()->ExpectCallToTrimCacheForFacetURI(
261 kTestAndroidFacetURIBeta2);
262 mock_affiliation_service()->ExpectCallToTrimCacheForFacetURI(
263 kTestAndroidFacetURIBeta3);
264 mock_affiliation_service()->ExpectCallToTrimCacheForFacetURI(
265 kTestAndroidFacetURIGamma);
266 }
267
GetAffiliatedAndroidRealms(const PasswordStore::FormDigest & observed_form)268 std::vector<std::string> GetAffiliatedAndroidRealms(
269 const PasswordStore::FormDigest& observed_form) {
270 expecting_result_callback_ = true;
271 match_helper()->GetAffiliatedAndroidRealms(
272 observed_form,
273 base::BindOnce(&AffiliatedMatchHelperTest::OnAffiliatedRealmsCallback,
274 base::Unretained(this)));
275 RunUntilIdle();
276 EXPECT_FALSE(expecting_result_callback_);
277 return last_result_realms_;
278 }
279
GetAffiliatedWebRealms(const PasswordStore::FormDigest & android_form)280 std::vector<std::string> GetAffiliatedWebRealms(
281 const PasswordStore::FormDigest& android_form) {
282 expecting_result_callback_ = true;
283 match_helper()->GetAffiliatedWebRealms(
284 android_form,
285 base::BindOnce(&AffiliatedMatchHelperTest::OnAffiliatedRealmsCallback,
286 base::Unretained(this)));
287 RunUntilIdle();
288 EXPECT_FALSE(expecting_result_callback_);
289 return last_result_realms_;
290 }
291
292 std::vector<std::unique_ptr<autofill::PasswordForm>>
InjectAffiliationAndBrandingInformation(std::vector<std::unique_ptr<autofill::PasswordForm>> forms)293 InjectAffiliationAndBrandingInformation(
294 std::vector<std::unique_ptr<autofill::PasswordForm>> forms) {
295 expecting_result_callback_ = true;
296 match_helper()->InjectAffiliationAndBrandingInformation(
297 std::move(forms),
298 base::BindOnce(&AffiliatedMatchHelperTest::OnFormsCallback,
299 base::Unretained(this)));
300 RunUntilIdle();
301 EXPECT_FALSE(expecting_result_callback_);
302 return std::move(last_result_forms_);
303 }
304
DestroyMatchHelper()305 void DestroyMatchHelper() { match_helper_.reset(); }
306
password_store()307 TestPasswordStore* password_store() { return password_store_.get(); }
308
mock_affiliation_service()309 MockAffiliationService* mock_affiliation_service() {
310 return mock_affiliation_service_;
311 }
312
match_helper()313 AffiliatedMatchHelper* match_helper() { return match_helper_.get(); }
314
315 private:
OnAffiliatedRealmsCallback(const std::vector<std::string> & affiliated_realms)316 void OnAffiliatedRealmsCallback(
317 const std::vector<std::string>& affiliated_realms) {
318 EXPECT_TRUE(expecting_result_callback_);
319 expecting_result_callback_ = false;
320 last_result_realms_ = affiliated_realms;
321 }
322
OnFormsCallback(std::vector<std::unique_ptr<autofill::PasswordForm>> forms)323 void OnFormsCallback(
324 std::vector<std::unique_ptr<autofill::PasswordForm>> forms) {
325 EXPECT_TRUE(expecting_result_callback_);
326 expecting_result_callback_ = false;
327 last_result_forms_.swap(forms);
328 }
329
330 // testing::Test:
SetUp()331 void SetUp() override {
332 std::unique_ptr<MockAffiliationService> service(
333 new MockAffiliationService());
334 mock_affiliation_service_ = service.get();
335
336 password_store_ = new TestPasswordStore;
337 password_store_->Init(nullptr);
338
339 match_helper_.reset(
340 new AffiliatedMatchHelper(password_store_.get(), std::move(service)));
341 }
342
TearDown()343 void TearDown() override {
344 match_helper_.reset();
345 password_store_->ShutdownOnUIThread();
346 password_store_ = nullptr;
347 // Clean up on the background thread.
348 RunUntilIdle();
349 }
350
351 base::test::SingleThreadTaskEnvironment task_environment_;
352 base::ScopedMockTimeMessageLoopTaskRunner mock_time_task_runner_;
353
354 std::vector<std::string> last_result_realms_;
355 std::vector<std::unique_ptr<autofill::PasswordForm>> last_result_forms_;
356 bool expecting_result_callback_;
357
358 scoped_refptr<TestPasswordStore> password_store_;
359 std::unique_ptr<AffiliatedMatchHelper> match_helper_;
360
361 // Owned by |match_helper_|.
362 MockAffiliationService* mock_affiliation_service_;
363
364 DISALLOW_COPY_AND_ASSIGN(AffiliatedMatchHelperTest);
365 };
366
367 // GetAffiliatedAndroidRealm* tests verify that GetAffiliatedAndroidRealms()
368 // returns the realms of affiliated Android applications, but only Android
369 // applications, and only if the observed form is a secure HTML login form.
370
TEST_F(AffiliatedMatchHelperTest,GetAffiliatedAndroidRealmsYieldsResults)371 TEST_F(AffiliatedMatchHelperTest, GetAffiliatedAndroidRealmsYieldsResults) {
372 mock_affiliation_service()
373 ->ExpectCallToGetAffiliationsAndBrandingAndSucceedWithResult(
374 FacetURI::FromCanonicalSpec(kTestWebFacetURIBeta1),
375 StrategyOnCacheMiss::FAIL, GetTestEquivalenceClassBeta());
376 EXPECT_THAT(GetAffiliatedAndroidRealms(
377 GetTestObservedWebForm(kTestWebRealmBeta1, nullptr)),
378 testing::UnorderedElementsAre(kTestAndroidRealmBeta2,
379 kTestAndroidRealmBeta3));
380 }
381
TEST_F(AffiliatedMatchHelperTest,GetAffiliatedAndroidRealmsYieldsOnlyAndroidApps)382 TEST_F(AffiliatedMatchHelperTest,
383 GetAffiliatedAndroidRealmsYieldsOnlyAndroidApps) {
384 mock_affiliation_service()
385 ->ExpectCallToGetAffiliationsAndBrandingAndSucceedWithResult(
386 FacetURI::FromCanonicalSpec(kTestWebFacetURIAlpha1),
387 StrategyOnCacheMiss::FAIL, GetTestEquivalenceClassAlpha());
388 // This verifies that |kTestWebRealmAlpha2| is not returned.
389 EXPECT_THAT(GetAffiliatedAndroidRealms(
390 GetTestObservedWebForm(kTestWebRealmAlpha1, nullptr)),
391 testing::UnorderedElementsAre(kTestAndroidRealmAlpha3));
392 }
393
TEST_F(AffiliatedMatchHelperTest,GetAffiliatedAndroidRealmsYieldsEmptyResultsForHTTPBasicAuthForms)394 TEST_F(AffiliatedMatchHelperTest,
395 GetAffiliatedAndroidRealmsYieldsEmptyResultsForHTTPBasicAuthForms) {
396 PasswordStore::FormDigest http_auth_observed_form(
397 GetTestObservedWebForm(kTestWebRealmAlpha1, nullptr));
398 http_auth_observed_form.scheme = autofill::PasswordForm::Scheme::kBasic;
399 EXPECT_THAT(GetAffiliatedAndroidRealms(http_auth_observed_form),
400 testing::IsEmpty());
401 }
402
TEST_F(AffiliatedMatchHelperTest,GetAffiliatedAndroidRealmsYieldsEmptyResultsForHTTPDigestAuthForms)403 TEST_F(AffiliatedMatchHelperTest,
404 GetAffiliatedAndroidRealmsYieldsEmptyResultsForHTTPDigestAuthForms) {
405 PasswordStore::FormDigest http_auth_observed_form(
406 GetTestObservedWebForm(kTestWebRealmAlpha1, nullptr));
407 http_auth_observed_form.scheme = autofill::PasswordForm::Scheme::kDigest;
408 EXPECT_THAT(GetAffiliatedAndroidRealms(http_auth_observed_form),
409 testing::IsEmpty());
410 }
411
TEST_F(AffiliatedMatchHelperTest,GetAffiliatedAndroidRealmsYieldsEmptyResultsForAndroidKeyedForms)412 TEST_F(AffiliatedMatchHelperTest,
413 GetAffiliatedAndroidRealmsYieldsEmptyResultsForAndroidKeyedForms) {
414 PasswordStore::FormDigest android_observed_form(
415 GetTestAndroidCredentials(kTestAndroidRealmBeta2));
416 EXPECT_THAT(GetAffiliatedAndroidRealms(android_observed_form),
417 testing::IsEmpty());
418 }
419
TEST_F(AffiliatedMatchHelperTest,GetAffiliatedAndroidRealmsYieldsEmptyResultsWhenNoPrefetch)420 TEST_F(AffiliatedMatchHelperTest,
421 GetAffiliatedAndroidRealmsYieldsEmptyResultsWhenNoPrefetch) {
422 mock_affiliation_service()
423 ->ExpectCallToGetAffiliationsAndBrandingAndEmulateFailure(
424 FacetURI::FromCanonicalSpec(kTestWebFacetURIAlpha1),
425 StrategyOnCacheMiss::FAIL);
426 EXPECT_THAT(GetAffiliatedAndroidRealms(
427 GetTestObservedWebForm(kTestWebRealmAlpha1, nullptr)),
428 testing::IsEmpty());
429 }
430
431 // GetAffiliatedWebRealms* tests verify that GetAffiliatedWebRealms() returns
432 // the realms of web sites affiliated with the given Android application, but
433 // only web sites, and only if an Android application is queried.
434
TEST_F(AffiliatedMatchHelperTest,GetAffiliatedWebRealmsYieldsResults)435 TEST_F(AffiliatedMatchHelperTest, GetAffiliatedWebRealmsYieldsResults) {
436 mock_affiliation_service()
437 ->ExpectCallToGetAffiliationsAndBrandingAndSucceedWithResult(
438 FacetURI::FromCanonicalSpec(kTestAndroidFacetURIAlpha3),
439 StrategyOnCacheMiss::FETCH_OVER_NETWORK,
440 GetTestEquivalenceClassAlpha());
441 PasswordStore::FormDigest android_form(
442 GetTestAndroidCredentials(kTestAndroidRealmAlpha3));
443 EXPECT_THAT(
444 GetAffiliatedWebRealms(android_form),
445 testing::UnorderedElementsAre(kTestWebRealmAlpha1, kTestWebRealmAlpha2));
446 }
447
TEST_F(AffiliatedMatchHelperTest,GetAffiliatedWebRealmsYieldsOnlyWebsites)448 TEST_F(AffiliatedMatchHelperTest, GetAffiliatedWebRealmsYieldsOnlyWebsites) {
449 mock_affiliation_service()
450 ->ExpectCallToGetAffiliationsAndBrandingAndSucceedWithResult(
451 FacetURI::FromCanonicalSpec(kTestAndroidFacetURIBeta2),
452 StrategyOnCacheMiss::FETCH_OVER_NETWORK,
453 GetTestEquivalenceClassBeta());
454 PasswordStore::FormDigest android_form(
455 GetTestAndroidCredentials(kTestAndroidRealmBeta2));
456 // This verifies that |kTestAndroidRealmBeta3| is not returned.
457 EXPECT_THAT(GetAffiliatedWebRealms(android_form),
458 testing::UnorderedElementsAre(kTestWebRealmBeta1));
459 }
460
TEST_F(AffiliatedMatchHelperTest,GetAffiliatedWebRealmsYieldsEmptyResultsForWebKeyedForms)461 TEST_F(AffiliatedMatchHelperTest,
462 GetAffiliatedWebRealmsYieldsEmptyResultsForWebKeyedForms) {
463 EXPECT_THAT(GetAffiliatedWebRealms(
464 GetTestObservedWebForm(kTestWebRealmBeta1, nullptr)),
465 testing::IsEmpty());
466 }
467
468 // Verifies that InjectAffiliationAndBrandingInformation() injects the realms of
469 // web sites affiliated with the given Android application into the password
470 // forms, as well as branding information corresponding to the application, if
471 // any.
TEST_F(AffiliatedMatchHelperTest,InjectAffiliationAndBrandingInformation)472 TEST_F(AffiliatedMatchHelperTest, InjectAffiliationAndBrandingInformation) {
473 std::vector<std::unique_ptr<autofill::PasswordForm>> forms;
474
475 forms.push_back(std::make_unique<autofill::PasswordForm>(
476 GetTestAndroidCredentials(kTestAndroidRealmAlpha3)));
477 mock_affiliation_service()
478 ->ExpectCallToGetAffiliationsAndBrandingAndSucceedWithResult(
479 FacetURI::FromCanonicalSpec(kTestAndroidFacetURIAlpha3),
480 StrategyOnCacheMiss::FAIL, GetTestEquivalenceClassAlpha());
481
482 forms.push_back(std::make_unique<autofill::PasswordForm>(
483 GetTestAndroidCredentials(kTestAndroidRealmBeta2)));
484 mock_affiliation_service()
485 ->ExpectCallToGetAffiliationsAndBrandingAndSucceedWithResult(
486 FacetURI::FromCanonicalSpec(kTestAndroidFacetURIBeta2),
487 StrategyOnCacheMiss::FAIL, GetTestEquivalenceClassBeta());
488
489 forms.push_back(std::make_unique<autofill::PasswordForm>(
490 GetTestAndroidCredentials(kTestAndroidRealmBeta3)));
491 mock_affiliation_service()
492 ->ExpectCallToGetAffiliationsAndBrandingAndSucceedWithResult(
493 FacetURI::FromCanonicalSpec(kTestAndroidFacetURIBeta3),
494 StrategyOnCacheMiss::FAIL, GetTestEquivalenceClassBeta());
495
496 forms.push_back(std::make_unique<autofill::PasswordForm>(
497 GetTestAndroidCredentials(kTestAndroidRealmGamma)));
498 mock_affiliation_service()
499 ->ExpectCallToGetAffiliationsAndBrandingAndEmulateFailure(
500 FacetURI::FromCanonicalSpec(kTestAndroidFacetURIGamma),
501 StrategyOnCacheMiss::FAIL);
502
503 PasswordStore::FormDigest digest =
504 GetTestObservedWebForm(kTestWebRealmBeta1, nullptr);
505 autofill::PasswordForm web_form;
506 web_form.scheme = digest.scheme;
507 web_form.signon_realm = digest.signon_realm;
508 web_form.origin = digest.origin;
509 forms.push_back(std::make_unique<autofill::PasswordForm>(web_form));
510
511 size_t expected_form_count = forms.size();
512 std::vector<std::unique_ptr<autofill::PasswordForm>> results(
513 InjectAffiliationAndBrandingInformation(std::move(forms)));
514 ASSERT_EQ(expected_form_count, results.size());
515 EXPECT_THAT(results[0]->affiliated_web_realm,
516 testing::AnyOf(kTestWebRealmAlpha1, kTestWebRealmAlpha2));
517 EXPECT_EQ(kTestAndroidFacetNameAlpha3, results[0]->app_display_name);
518 EXPECT_EQ(kTestAndroidFacetIconURLAlpha3,
519 results[0]->app_icon_url.possibly_invalid_spec());
520 EXPECT_THAT(results[1]->affiliated_web_realm,
521 testing::Eq(kTestWebRealmBeta1));
522 EXPECT_EQ(kTestAndroidFacetNameBeta2, results[1]->app_display_name);
523 EXPECT_EQ(kTestAndroidFacetIconURLBeta2,
524 results[1]->app_icon_url.possibly_invalid_spec());
525 EXPECT_THAT(results[2]->affiliated_web_realm,
526 testing::Eq(kTestWebRealmBeta1));
527 EXPECT_EQ(kTestAndroidFacetNameBeta3, results[2]->app_display_name);
528 EXPECT_EQ(kTestAndroidFacetIconURLBeta3,
529 results[2]->app_icon_url.possibly_invalid_spec());
530 EXPECT_THAT(results[3]->affiliated_web_realm, testing::IsEmpty());
531 EXPECT_THAT(results[4]->affiliated_web_realm, testing::IsEmpty());
532 }
533
534 // Note: IsValidWebCredential() is tested as part of GetAffiliatedAndroidRealms
535 // tests above.
TEST_F(AffiliatedMatchHelperTest,IsValidAndroidCredential)536 TEST_F(AffiliatedMatchHelperTest, IsValidAndroidCredential) {
537 EXPECT_FALSE(AffiliatedMatchHelper::IsValidAndroidCredential(
538 GetTestObservedWebForm(kTestWebRealmBeta1, nullptr)));
539 PasswordStore::FormDigest android_credential(
540 GetTestAndroidCredentials(kTestAndroidRealmBeta2));
541 EXPECT_TRUE(
542 AffiliatedMatchHelper::IsValidAndroidCredential(android_credential));
543 }
544
545 // Verifies that affiliations for Android applications with pre-existing
546 // credentials on start-up are prefetched.
TEST_F(AffiliatedMatchHelperTest,PrefetchAffiliationsAndBrandingForPreexistingAndroidCredentialsOnStartup)547 TEST_F(
548 AffiliatedMatchHelperTest,
549 PrefetchAffiliationsAndBrandingForPreexistingAndroidCredentialsOnStartup) {
550 AddAndroidAndNonAndroidTestLogins();
551
552 match_helper()->Initialize();
553 RunUntilIdle();
554
555 ExpectPrefetchForAndroidTestLogins();
556 ASSERT_NO_FATAL_FAILURE(RunDeferredInitialization());
557 }
558
559 // Stores credentials for Android applications between Initialize() and
560 // DoDeferredInitialization(). Verifies that corresponding affiliation
561 // information gets prefetched.
TEST_F(AffiliatedMatchHelperTest,PrefetchAffiliationsForAndroidCredentialsAddedInInitializationDelay)562 TEST_F(AffiliatedMatchHelperTest,
563 PrefetchAffiliationsForAndroidCredentialsAddedInInitializationDelay) {
564 match_helper()->Initialize();
565 RunUntilIdle();
566
567 AddAndroidAndNonAndroidTestLogins();
568
569 ExpectPrefetchForAndroidTestLogins();
570 ASSERT_NO_FATAL_FAILURE(RunDeferredInitialization());
571 }
572
573 // Stores credentials for Android applications after DoDeferredInitialization().
574 // Verifies that corresponding affiliation information gets prefetched.
TEST_F(AffiliatedMatchHelperTest,PrefetchAffiliationsForAndroidCredentialsAddedAfterInitialization)575 TEST_F(AffiliatedMatchHelperTest,
576 PrefetchAffiliationsForAndroidCredentialsAddedAfterInitialization) {
577 match_helper()->Initialize();
578 ASSERT_NO_FATAL_FAILURE(RunDeferredInitialization());
579
580 ExpectPrefetchForAndroidTestLogins();
581 AddAndroidAndNonAndroidTestLogins();
582 }
583
TEST_F(AffiliatedMatchHelperTest,CancelPrefetchingAffiliationsAndBrandingForRemovedAndroidCredentials)584 TEST_F(AffiliatedMatchHelperTest,
585 CancelPrefetchingAffiliationsAndBrandingForRemovedAndroidCredentials) {
586 AddAndroidAndNonAndroidTestLogins();
587 match_helper()->Initialize();
588 ExpectPrefetchForAndroidTestLogins();
589 ASSERT_NO_FATAL_FAILURE(RunDeferredInitialization());
590
591 ExpectCancelPrefetchForAndroidTestLogins();
592 ExpectTrimCacheForAndroidTestLogins();
593 RemoveAndroidAndNonAndroidTestLogins();
594 }
595
596 // Verify that whenever the primary key is updated for a credential (in which
597 // case both REMOVE and ADD change notifications are sent out), then Prefetch()
598 // is called in response to the addition before the call to
599 // TrimCacheForFacetURI() in response to the removal, so that cached data is not
600 // deleted and then immediately re-fetched.
TEST_F(AffiliatedMatchHelperTest,PrefetchBeforeTrimForPrimaryKeyUpdates)601 TEST_F(AffiliatedMatchHelperTest, PrefetchBeforeTrimForPrimaryKeyUpdates) {
602 AddAndroidAndNonAndroidTestLogins();
603 match_helper()->Initialize();
604 ExpectPrefetchForAndroidTestLogins();
605 ASSERT_NO_FATAL_FAILURE(RunDeferredInitialization());
606
607 mock_affiliation_service()->ExpectCallToCancelPrefetch(
608 kTestAndroidFacetURIAlpha3);
609
610 {
611 testing::InSequence in_sequence;
612 mock_affiliation_service()->ExpectCallToPrefetch(
613 kTestAndroidFacetURIAlpha3);
614 mock_affiliation_service()->ExpectCallToTrimCacheForFacetURI(
615 kTestAndroidFacetURIAlpha3);
616 }
617
618 autofill::PasswordForm old_form(
619 GetTestAndroidCredentials(kTestAndroidRealmAlpha3));
620 autofill::PasswordForm new_form(old_form);
621 new_form.username_value = base::ASCIIToUTF16("NewUserName");
622 UpdateLoginWithPrimaryKey(new_form, old_form);
623 }
624
625 // Stores and removes four credentials for the same an Android application, and
626 // expects that Prefetch() and CancelPrefetch() will each be called four times.
TEST_F(AffiliatedMatchHelperTest,DuplicateCredentialsArePrefetchWithMultiplicity)627 TEST_F(AffiliatedMatchHelperTest,
628 DuplicateCredentialsArePrefetchWithMultiplicity) {
629 EXPECT_CALL(*mock_affiliation_service(),
630 Prefetch(FacetURI::FromCanonicalSpec(kTestAndroidFacetURIAlpha3),
631 base::Time::Max()))
632 .Times(4);
633
634 autofill::PasswordForm android_form(
635 GetTestAndroidCredentials(kTestAndroidRealmAlpha3));
636 AddLogin(android_form);
637
638 // Store two credentials before initialization.
639 autofill::PasswordForm android_form2(android_form);
640 android_form2.username_value = base::ASCIIToUTF16("JohnDoe2");
641 AddLogin(android_form2);
642
643 match_helper()->Initialize();
644 RunUntilIdle();
645
646 // Store one credential between initialization and deferred initialization.
647 autofill::PasswordForm android_form3(android_form);
648 android_form3.username_value = base::ASCIIToUTF16("JohnDoe3");
649 AddLogin(android_form3);
650
651 ASSERT_NO_FATAL_FAILURE(RunDeferredInitialization());
652
653 // Store one credential after deferred initialization.
654 autofill::PasswordForm android_form4(android_form);
655 android_form4.username_value = base::ASCIIToUTF16("JohnDoe4");
656 AddLogin(android_form4);
657
658 for (size_t i = 0; i < 4; ++i) {
659 mock_affiliation_service()->ExpectCallToCancelPrefetch(
660 kTestAndroidFacetURIAlpha3);
661 mock_affiliation_service()->ExpectCallToTrimCacheForFacetURI(
662 kTestAndroidFacetURIAlpha3);
663 }
664
665 RemoveLogin(android_form);
666 RemoveLogin(android_form2);
667 RemoveLogin(android_form3);
668 RemoveLogin(android_form4);
669 }
670
TEST_F(AffiliatedMatchHelperTest,DestroyBeforeDeferredInitialization)671 TEST_F(AffiliatedMatchHelperTest, DestroyBeforeDeferredInitialization) {
672 match_helper()->Initialize();
673 RunUntilIdle();
674 DestroyMatchHelper();
675 ASSERT_NO_FATAL_FAILURE(ExpectNoDeferredTasks());
676 }
677
678 } // namespace password_manager
679