1 // Copyright (c) 2012 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 "chrome/browser/sync/test/integration/passwords_helper.h"
6
7 #include <sstream>
8 #include <utility>
9
10 #include "base/base64.h"
11 #include "base/bind.h"
12 #include "base/compiler_specific.h"
13 #include "base/macros.h"
14 #include "base/run_loop.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/synchronization/waitable_event.h"
18 #include "base/time/time.h"
19 #include "chrome/browser/password_manager/account_password_store_factory.h"
20 #include "chrome/browser/password_manager/password_store_factory.h"
21 #include "chrome/browser/sync/profile_sync_service_factory.h"
22 #include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
23 #include "chrome/browser/sync/test/integration/sync_datatype_helper.h"
24 #include "components/password_manager/core/browser/password_manager_test_utils.h"
25 #include "components/password_manager/core/browser/password_store.h"
26 #include "components/password_manager/core/browser/password_store_consumer.h"
27 #include "components/sync/engine_impl/loopback_server/persistent_unique_client_entity.h"
28 #include "components/sync/nigori/cryptographer_impl.h"
29 #include "content/public/test/test_utils.h"
30 #include "net/base/escape.h"
31 #include "url/gurl.h"
32
33 using password_manager::PasswordForm;
34 using password_manager::PasswordStore;
35 using sync_datatype_helper::test;
36
37 namespace {
38
39 const char kFakeSignonRealm[] = "http://fake-signon-realm.google.com/";
40 const char kIndexedFakeOrigin[] = "http://fake-signon-realm.google.com/%d";
41
42 // We use a WaitableEvent to wait when logins are added, removed, or updated
43 // instead of running the UI message loop because of a restriction that
44 // prevents a DB thread from initiating a quit of the UI message loop.
PasswordStoreCallback(base::WaitableEvent * wait_event)45 void PasswordStoreCallback(base::WaitableEvent* wait_event) {
46 // Wake up passwords_helper::AddLogin.
47 wait_event->Signal();
48 }
49
50 class PasswordStoreConsumerHelper
51 : public password_manager::PasswordStoreConsumer {
52 public:
PasswordStoreConsumerHelper()53 PasswordStoreConsumerHelper() {}
54
OnGetPasswordStoreResults(std::vector<std::unique_ptr<PasswordForm>> results)55 void OnGetPasswordStoreResults(
56 std::vector<std::unique_ptr<PasswordForm>> results) override {
57 result_.swap(results);
58 run_loop_.Quit();
59 }
60
WaitForResult()61 std::vector<std::unique_ptr<PasswordForm>> WaitForResult() {
62 DCHECK(!run_loop_.running());
63 content::RunThisRunLoop(&run_loop_);
64 return std::move(result_);
65 }
66
67 private:
68 base::RunLoop run_loop_;
69 std::vector<std::unique_ptr<PasswordForm>> result_;
70
71 DISALLOW_COPY_AND_ASSIGN(PasswordStoreConsumerHelper);
72 };
73
74 // PasswordForm::date_synced is a local field. Therefore it may be different
75 // across clients.
ClearSyncDateField(std::vector<std::unique_ptr<PasswordForm>> * forms)76 void ClearSyncDateField(std::vector<std::unique_ptr<PasswordForm>>* forms) {
77 for (auto& form : *forms) {
78 form->date_synced = base::Time();
79 }
80 }
81
SpecificsDataFromPasswordForm(const password_manager::PasswordForm & password_form)82 sync_pb::PasswordSpecificsData SpecificsDataFromPasswordForm(
83 const password_manager::PasswordForm& password_form) {
84 sync_pb::PasswordSpecificsData password_data;
85 password_data.set_scheme(static_cast<int>(password_form.scheme));
86 password_data.set_signon_realm(password_form.signon_realm);
87 password_data.set_origin(password_form.url.spec());
88 password_data.set_action(password_form.action.spec());
89 password_data.set_username_element(
90 base::UTF16ToUTF8(password_form.username_element));
91 password_data.set_password_element(
92 base::UTF16ToUTF8(password_form.password_element));
93 password_data.set_username_value(
94 base::UTF16ToUTF8(password_form.username_value));
95 password_data.set_password_value(
96 base::UTF16ToUTF8(password_form.password_value));
97 password_data.set_date_last_used(
98 password_form.date_last_used.ToDeltaSinceWindowsEpoch().InMicroseconds());
99 password_data.set_date_created(
100 password_form.date_created.ToDeltaSinceWindowsEpoch().InMicroseconds());
101 password_data.set_blacklisted(password_form.blocked_by_user);
102 password_data.set_type(static_cast<int>(password_form.type));
103 password_data.set_times_used(password_form.times_used);
104 password_data.set_display_name(base::UTF16ToUTF8(password_form.display_name));
105 password_data.set_avatar_url(password_form.icon_url.spec());
106 password_data.set_federation_url(
107 password_form.federation_origin.opaque()
108 ? std::string()
109 : password_form.federation_origin.Serialize());
110 return password_data;
111 }
112
EncryptPasswordSpecifics(const sync_pb::PasswordSpecificsData & password_data,const std::string & passphrase,const syncer::KeyDerivationParams & key_derivation_params)113 sync_pb::EntitySpecifics EncryptPasswordSpecifics(
114 const sync_pb::PasswordSpecificsData& password_data,
115 const std::string& passphrase,
116 const syncer::KeyDerivationParams& key_derivation_params) {
117 std::unique_ptr<syncer::CryptographerImpl> cryptographer =
118 syncer::CryptographerImpl::FromSingleKeyForTesting(passphrase,
119 key_derivation_params);
120 sync_pb::EntitySpecifics encrypted_specifics;
121 encrypted_specifics.mutable_password()
122 ->mutable_unencrypted_metadata()
123 ->set_url(password_data.signon_realm());
124 bool result = cryptographer->Encrypt(
125 password_data,
126 encrypted_specifics.mutable_password()->mutable_encrypted());
127 DCHECK(result);
128 return encrypted_specifics;
129 }
130
GetClientTag(const sync_pb::PasswordSpecificsData & password_data)131 std::string GetClientTag(const sync_pb::PasswordSpecificsData& password_data) {
132 return net::EscapePath(GURL(password_data.origin()).spec()) + "|" +
133 net::EscapePath(password_data.username_element()) + "|" +
134 net::EscapePath(password_data.username_value()) + "|" +
135 net::EscapePath(password_data.password_element()) + "|" +
136 net::EscapePath(password_data.signon_realm());
137 }
138
139 } // namespace
140
141 namespace passwords_helper {
142
AddLogin(PasswordStore * store,const PasswordForm & form)143 void AddLogin(PasswordStore* store, const PasswordForm& form) {
144 ASSERT_TRUE(store);
145 base::WaitableEvent wait_event(
146 base::WaitableEvent::ResetPolicy::MANUAL,
147 base::WaitableEvent::InitialState::NOT_SIGNALED);
148 store->AddLogin(form);
149 store->ScheduleTask(base::BindOnce(&PasswordStoreCallback, &wait_event));
150 wait_event.Wait();
151 }
152
UpdateLogin(PasswordStore * store,const PasswordForm & form)153 void UpdateLogin(PasswordStore* store, const PasswordForm& form) {
154 ASSERT_TRUE(store);
155 base::WaitableEvent wait_event(
156 base::WaitableEvent::ResetPolicy::MANUAL,
157 base::WaitableEvent::InitialState::NOT_SIGNALED);
158 store->UpdateLogin(form);
159 store->ScheduleTask(base::BindOnce(&PasswordStoreCallback, &wait_event));
160 wait_event.Wait();
161 }
162
UpdateLoginWithPrimaryKey(PasswordStore * store,const PasswordForm & new_form,const PasswordForm & old_form)163 void UpdateLoginWithPrimaryKey(PasswordStore* store,
164 const PasswordForm& new_form,
165 const PasswordForm& old_form) {
166 ASSERT_TRUE(store);
167 base::WaitableEvent wait_event(
168 base::WaitableEvent::ResetPolicy::MANUAL,
169 base::WaitableEvent::InitialState::NOT_SIGNALED);
170 store->UpdateLoginWithPrimaryKey(new_form, old_form);
171 store->ScheduleTask(base::BindOnce(&PasswordStoreCallback, &wait_event));
172 wait_event.Wait();
173 }
174
GetLogins(PasswordStore * store)175 std::vector<std::unique_ptr<PasswordForm>> GetLogins(PasswordStore* store) {
176 EXPECT_TRUE(store);
177 password_manager::PasswordStore::FormDigest matcher_form = {
178 PasswordForm::Scheme::kHtml, kFakeSignonRealm, GURL()};
179 PasswordStoreConsumerHelper consumer;
180 store->GetLogins(matcher_form, &consumer);
181 return consumer.WaitForResult();
182 }
183
GetAllLogins(PasswordStore * store)184 std::vector<std::unique_ptr<PasswordForm>> GetAllLogins(PasswordStore* store) {
185 EXPECT_TRUE(store);
186 PasswordStoreConsumerHelper consumer;
187 store->GetAllLogins(&consumer);
188 return consumer.WaitForResult();
189 }
190
RemoveLogin(PasswordStore * store,const PasswordForm & form)191 void RemoveLogin(PasswordStore* store, const PasswordForm& form) {
192 ASSERT_TRUE(store);
193 base::WaitableEvent wait_event(
194 base::WaitableEvent::ResetPolicy::MANUAL,
195 base::WaitableEvent::InitialState::NOT_SIGNALED);
196 store->RemoveLogin(form);
197 store->ScheduleTask(base::BindOnce(&PasswordStoreCallback, &wait_event));
198 wait_event.Wait();
199 }
200
RemoveLogins(PasswordStore * store)201 void RemoveLogins(PasswordStore* store) {
202 std::vector<std::unique_ptr<PasswordForm>> forms = GetLogins(store);
203 for (const auto& form : forms) {
204 RemoveLogin(store, *form);
205 }
206 }
207
GetPasswordStore(int index)208 PasswordStore* GetPasswordStore(int index) {
209 return PasswordStoreFactory::GetForProfile(test()->GetProfile(index),
210 ServiceAccessType::IMPLICIT_ACCESS)
211 .get();
212 }
213
GetVerifierPasswordStore()214 PasswordStore* GetVerifierPasswordStore() {
215 return PasswordStoreFactory::GetForProfile(test()->verifier(),
216 ServiceAccessType::IMPLICIT_ACCESS)
217 .get();
218 }
219
GetAccountPasswordStore(int index)220 PasswordStore* GetAccountPasswordStore(int index) {
221 return AccountPasswordStoreFactory::GetForProfile(
222 test()->GetProfile(index), ServiceAccessType::IMPLICIT_ACCESS)
223 .get();
224 }
225
ProfileContainsSamePasswordFormsAsVerifier(int index)226 bool ProfileContainsSamePasswordFormsAsVerifier(int index) {
227 std::vector<std::unique_ptr<PasswordForm>> verifier_forms =
228 GetLogins(GetVerifierPasswordStore());
229 std::vector<std::unique_ptr<PasswordForm>> forms =
230 GetLogins(GetPasswordStore(index));
231 ClearSyncDateField(&forms);
232
233 std::ostringstream mismatch_details_stream;
234 bool is_matching = password_manager::ContainsEqualPasswordFormsUnordered(
235 verifier_forms, forms, &mismatch_details_stream);
236 if (!is_matching) {
237 VLOG(1) << "Profile " << index
238 << " does not contain the same Password forms as Verifier Profile.";
239 VLOG(1) << mismatch_details_stream.str();
240 }
241 return is_matching;
242 }
243
ProfilesContainSamePasswordForms(int index_a,int index_b)244 bool ProfilesContainSamePasswordForms(int index_a, int index_b) {
245 std::vector<std::unique_ptr<PasswordForm>> forms_a =
246 GetLogins(GetPasswordStore(index_a));
247 std::vector<std::unique_ptr<PasswordForm>> forms_b =
248 GetLogins(GetPasswordStore(index_b));
249 ClearSyncDateField(&forms_a);
250 ClearSyncDateField(&forms_b);
251
252 std::ostringstream mismatch_details_stream;
253 bool is_matching = password_manager::ContainsEqualPasswordFormsUnordered(
254 forms_a, forms_b, &mismatch_details_stream);
255 if (!is_matching) {
256 VLOG(1) << "Password forms in Profile " << index_a
257 << " (listed as 'expected forms' below)"
258 << " do not match those in Profile " << index_b
259 << " (listed as 'actual forms' below)";
260 VLOG(1) << mismatch_details_stream.str();
261 }
262 return is_matching;
263 }
264
AllProfilesContainSamePasswordFormsAsVerifier()265 bool AllProfilesContainSamePasswordFormsAsVerifier() {
266 for (int i = 0; i < test()->num_clients(); ++i) {
267 if (!ProfileContainsSamePasswordFormsAsVerifier(i)) {
268 DVLOG(1) << "Profile " << i
269 << " does not contain the same password"
270 " forms as the verifier.";
271 return false;
272 }
273 }
274 return true;
275 }
276
AllProfilesContainSamePasswordForms()277 bool AllProfilesContainSamePasswordForms() {
278 for (int i = 1; i < test()->num_clients(); ++i) {
279 if (!ProfilesContainSamePasswordForms(0, i)) {
280 DVLOG(1) << "Profile " << i
281 << " does not contain the same password"
282 " forms as Profile 0.";
283 return false;
284 }
285 }
286 return true;
287 }
288
GetPasswordCount(int index)289 int GetPasswordCount(int index) {
290 return GetLogins(GetPasswordStore(index)).size();
291 }
292
GetVerifierPasswordCount()293 int GetVerifierPasswordCount() {
294 return GetLogins(GetVerifierPasswordStore()).size();
295 }
296
CreateTestPasswordForm(int index)297 PasswordForm CreateTestPasswordForm(int index) {
298 PasswordForm form;
299 form.signon_realm = kFakeSignonRealm;
300 form.url = GURL(base::StringPrintf(kIndexedFakeOrigin, index));
301 form.username_value =
302 base::ASCIIToUTF16(base::StringPrintf("username%d", index));
303 form.password_value =
304 base::ASCIIToUTF16(base::StringPrintf("password%d", index));
305 form.date_created = base::Time::Now();
306 form.in_store = password_manager::PasswordForm::Store::kProfileStore;
307 return form;
308 }
309
InjectEncryptedServerPassword(const password_manager::PasswordForm & form,const std::string & encryption_passphrase,const syncer::KeyDerivationParams & key_derivation_params,fake_server::FakeServer * fake_server)310 void InjectEncryptedServerPassword(
311 const password_manager::PasswordForm& form,
312 const std::string& encryption_passphrase,
313 const syncer::KeyDerivationParams& key_derivation_params,
314 fake_server::FakeServer* fake_server) {
315 InjectEncryptedServerPassword(SpecificsDataFromPasswordForm(form),
316 encryption_passphrase, key_derivation_params,
317 fake_server);
318 }
319
InjectEncryptedServerPassword(const sync_pb::PasswordSpecificsData & password_data,const std::string & encryption_passphrase,const syncer::KeyDerivationParams & key_derivation_params,fake_server::FakeServer * fake_server)320 void InjectEncryptedServerPassword(
321 const sync_pb::PasswordSpecificsData& password_data,
322 const std::string& encryption_passphrase,
323 const syncer::KeyDerivationParams& key_derivation_params,
324 fake_server::FakeServer* fake_server) {
325 DCHECK(fake_server);
326 const sync_pb::EntitySpecifics encrypted_specifics = EncryptPasswordSpecifics(
327 password_data, encryption_passphrase, key_derivation_params);
328 fake_server->InjectEntity(
329 syncer::PersistentUniqueClientEntity::CreateFromSpecificsForTesting(
330 /*non_unique_name=*/"encrypted", GetClientTag(password_data),
331 encrypted_specifics,
332 /*creation_time=*/0, /*last_modified_time=*/0));
333 }
334
InjectKeystoreEncryptedServerPassword(const password_manager::PasswordForm & form,fake_server::FakeServer * fake_server)335 void InjectKeystoreEncryptedServerPassword(
336 const password_manager::PasswordForm& form,
337 fake_server::FakeServer* fake_server) {
338 InjectKeystoreEncryptedServerPassword(SpecificsDataFromPasswordForm(form),
339 fake_server);
340 }
341
InjectKeystoreEncryptedServerPassword(const sync_pb::PasswordSpecificsData & password_data,fake_server::FakeServer * fake_server)342 void InjectKeystoreEncryptedServerPassword(
343 const sync_pb::PasswordSpecificsData& password_data,
344 fake_server::FakeServer* fake_server) {
345 InjectEncryptedServerPassword(
346 password_data, base::Base64Encode(fake_server->GetKeystoreKeys().back()),
347 syncer::KeyDerivationParams::CreateForPbkdf2(), fake_server);
348 }
349
350 } // namespace passwords_helper
351
PasswordSyncActiveChecker(syncer::ProfileSyncService * service)352 PasswordSyncActiveChecker::PasswordSyncActiveChecker(
353 syncer::ProfileSyncService* service)
354 : SingleClientStatusChangeChecker(service) {}
355 PasswordSyncActiveChecker::~PasswordSyncActiveChecker() = default;
356
IsExitConditionSatisfied(std::ostream * os)357 bool PasswordSyncActiveChecker::IsExitConditionSatisfied(std::ostream* os) {
358 return service()->GetActiveDataTypes().Has(syncer::PASSWORDS);
359 }
360
SamePasswordFormsChecker()361 SamePasswordFormsChecker::SamePasswordFormsChecker()
362 : MultiClientStatusChangeChecker(
363 sync_datatype_helper::test()->GetSyncServices()),
364 in_progress_(false),
365 needs_recheck_(false) {}
366
367 // This method needs protection against re-entrancy.
368 //
369 // This function indirectly calls GetLogins(), which starts a RunLoop on the UI
370 // thread. This can be a problem, since the next task to execute could very
371 // well contain a ProfileSyncService::OnStateChanged() event, which would
372 // trigger another call to this here function, and start another layer of
373 // nested RunLoops. That makes the StatusChangeChecker's Quit() method
374 // ineffective.
375 //
376 // The work-around is to not allow re-entrancy. But we can't just drop
377 // IsExitConditionSatisifed() calls if one is already in progress. Instead, we
378 // set a flag to ask the current execution of IsExitConditionSatisfied() to be
379 // re-run. This ensures that the return value is always based on the most
380 // up-to-date state.
IsExitConditionSatisfied(std::ostream * os)381 bool SamePasswordFormsChecker::IsExitConditionSatisfied(std::ostream* os) {
382 *os << "Waiting for matching passwords";
383
384 if (in_progress_) {
385 LOG(WARNING) << "Setting flag and returning early to prevent nesting.";
386 needs_recheck_ = true;
387 return false;
388 }
389
390 // Keep retrying until we get a good reading.
391 bool result = false;
392 in_progress_ = true;
393 do {
394 needs_recheck_ = false;
395 result = passwords_helper::AllProfilesContainSamePasswordForms();
396 } while (needs_recheck_);
397 in_progress_ = false;
398 return result;
399 }
400
SamePasswordFormsAsVerifierChecker(int i)401 SamePasswordFormsAsVerifierChecker::SamePasswordFormsAsVerifierChecker(int i)
402 : SingleClientStatusChangeChecker(
403 sync_datatype_helper::test()->GetSyncService(i)),
404 index_(i),
405 in_progress_(false),
406 needs_recheck_(false) {}
407
408 // This method uses the same re-entrancy prevention trick as
409 // the SamePasswordFormsChecker.
IsExitConditionSatisfied(std::ostream * os)410 bool SamePasswordFormsAsVerifierChecker::IsExitConditionSatisfied(
411 std::ostream* os) {
412 *os << "Waiting for passwords to match verifier";
413
414 if (in_progress_) {
415 LOG(WARNING) << "Setting flag and returning early to prevent nesting.";
416 needs_recheck_ = true;
417 return false;
418 }
419
420 // Keep retrying until we get a good reading.
421 bool result = false;
422 in_progress_ = true;
423 do {
424 needs_recheck_ = false;
425 result =
426 passwords_helper::ProfileContainsSamePasswordFormsAsVerifier(index_);
427 } while (needs_recheck_);
428 in_progress_ = false;
429 return result;
430 }
431
PasswordFormsChecker(int index,const std::vector<password_manager::PasswordForm> & expected_forms)432 PasswordFormsChecker::PasswordFormsChecker(
433 int index,
434 const std::vector<password_manager::PasswordForm>& expected_forms)
435 : SingleClientStatusChangeChecker(
436 sync_datatype_helper::test()->GetSyncService(index)),
437 index_(index),
438 in_progress_(false),
439 needs_recheck_(false) {
440 for (auto& password_form : expected_forms) {
441 expected_forms_.push_back(
442 std::make_unique<password_manager::PasswordForm>(password_form));
443 }
444 ClearSyncDateField(&expected_forms_);
445 }
446
447 PasswordFormsChecker::~PasswordFormsChecker() = default;
448
449 // This method uses the same re-entrancy prevention trick as
450 // the SamePasswordFormsChecker.
IsExitConditionSatisfied(std::ostream * os)451 bool PasswordFormsChecker::IsExitConditionSatisfied(std::ostream* os) {
452 if (in_progress_) {
453 LOG(WARNING) << "Setting flag and returning early to prevent nesting.";
454 *os << "Setting flag and returning early to prevent nesting.";
455 needs_recheck_ = true;
456 return false;
457 }
458
459 // Keep retrying until we get a good reading.
460 bool result = false;
461 in_progress_ = true;
462 do {
463 needs_recheck_ = false;
464 result = IsExitConditionSatisfiedImpl(os);
465 } while (needs_recheck_);
466 in_progress_ = false;
467 return result;
468 }
469
IsExitConditionSatisfiedImpl(std::ostream * os)470 bool PasswordFormsChecker::IsExitConditionSatisfiedImpl(std::ostream* os) {
471 std::vector<std::unique_ptr<PasswordForm>> forms =
472 passwords_helper::GetLogins(passwords_helper::GetPasswordStore(index_));
473 ClearSyncDateField(&forms);
474
475 std::ostringstream mismatch_details_stream;
476 bool is_matching = password_manager::ContainsEqualPasswordFormsUnordered(
477 expected_forms_, forms, &mismatch_details_stream);
478 if (!is_matching) {
479 *os << "Profile " << index_
480 << " does not contain the same Password forms as expected. "
481 << mismatch_details_stream.str();
482 }
483 return is_matching;
484 }
485