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 "base/compiler_specific.h" 8 #include "base/strings/stringprintf.h" 9 #include "base/strings/utf_string_conversions.h" 10 #include "base/synchronization/waitable_event.h" 11 #include "base/time/time.h" 12 #include "chrome/browser/password_manager/password_store_factory.h" 13 #include "chrome/browser/sync/profile_sync_service.h" 14 #include "chrome/browser/sync/profile_sync_service_factory.h" 15 #include "chrome/browser/sync/test/integration/multi_client_status_change_checker.h" 16 #include "chrome/browser/sync/test/integration/profile_sync_service_harness.h" 17 #include "chrome/browser/sync/test/integration/single_client_status_change_checker.h" 18 #include "chrome/browser/sync/test/integration/sync_datatype_helper.h" 19 #include "chrome/test/base/ui_test_utils.h" 20 #include "components/password_manager/core/browser/password_form_data.h" 21 #include "components/password_manager/core/browser/password_store.h" 22 #include "components/password_manager/core/browser/password_store_consumer.h" 23 24 using autofill::PasswordForm; 25 using password_manager::PasswordStore; 26 using sync_datatype_helper::test; 27 28 const std::string kFakeSignonRealm = "http://fake-signon-realm.google.com/"; 29 const char* kIndexedFakeOrigin = "http://fake-signon-realm.google.com/%d"; 30 31 namespace { 32 33 // We use a WaitableEvent to wait when logins are added, removed, or updated 34 // instead of running the UI message loop because of a restriction that 35 // prevents a DB thread from initiating a quit of the UI message loop. 36 void PasswordStoreCallback(base::WaitableEvent* wait_event) { 37 // Wake up passwords_helper::AddLogin. 38 wait_event->Signal(); 39 } 40 41 class PasswordStoreConsumerHelper 42 : public password_manager::PasswordStoreConsumer { 43 public: 44 explicit PasswordStoreConsumerHelper(std::vector<PasswordForm>* result) 45 : password_manager::PasswordStoreConsumer(), result_(result) {} 46 47 virtual void OnGetPasswordStoreResults( 48 const std::vector<PasswordForm*>& result) OVERRIDE { 49 result_->clear(); 50 for (std::vector<PasswordForm*>::const_iterator it = result.begin(); 51 it != result.end(); 52 ++it) { 53 result_->push_back(**it); 54 delete *it; 55 } 56 57 // Quit the message loop to wake up passwords_helper::GetLogins. 58 base::MessageLoopForUI::current()->Quit(); 59 } 60 61 private: 62 std::vector<PasswordForm>* result_; 63 64 DISALLOW_COPY_AND_ASSIGN(PasswordStoreConsumerHelper); 65 }; 66 67 // PasswordForm::date_synced is a local field. Therefore it may be different 68 // across clients. 69 void ClearSyncDateField(std::vector<PasswordForm>* forms) { 70 for (std::vector<PasswordForm>::iterator it = forms->begin(); 71 it != forms->end(); 72 ++it) { 73 it->date_synced = base::Time(); 74 } 75 } 76 77 } // namespace 78 79 namespace passwords_helper { 80 81 void AddLogin(PasswordStore* store, const PasswordForm& form) { 82 ASSERT_TRUE(store); 83 base::WaitableEvent wait_event(true, false); 84 store->AddLogin(form); 85 store->ScheduleTask(base::Bind(&PasswordStoreCallback, &wait_event)); 86 wait_event.Wait(); 87 } 88 89 void UpdateLogin(PasswordStore* store, const PasswordForm& form) { 90 ASSERT_TRUE(store); 91 base::WaitableEvent wait_event(true, false); 92 store->UpdateLogin(form); 93 store->ScheduleTask(base::Bind(&PasswordStoreCallback, &wait_event)); 94 wait_event.Wait(); 95 } 96 97 void GetLogins(PasswordStore* store, std::vector<PasswordForm>& matches) { 98 ASSERT_TRUE(store); 99 PasswordForm matcher_form; 100 matcher_form.signon_realm = kFakeSignonRealm; 101 PasswordStoreConsumerHelper consumer(&matches); 102 store->GetLogins(matcher_form, PasswordStore::DISALLOW_PROMPT, &consumer); 103 content::RunMessageLoop(); 104 } 105 106 void RemoveLogin(PasswordStore* store, const PasswordForm& form) { 107 ASSERT_TRUE(store); 108 base::WaitableEvent wait_event(true, false); 109 store->RemoveLogin(form); 110 store->ScheduleTask(base::Bind(&PasswordStoreCallback, &wait_event)); 111 wait_event.Wait(); 112 } 113 114 void RemoveLogins(PasswordStore* store) { 115 std::vector<PasswordForm> forms; 116 GetLogins(store, forms); 117 for (std::vector<PasswordForm>::iterator it = forms.begin(); 118 it != forms.end(); ++it) { 119 RemoveLogin(store, *it); 120 } 121 } 122 123 void SetEncryptionPassphrase(int index, 124 const std::string& passphrase, 125 ProfileSyncService::PassphraseType type) { 126 ProfileSyncServiceFactory::GetForProfile( 127 test()->GetProfile(index))->SetEncryptionPassphrase(passphrase, type); 128 } 129 130 bool SetDecryptionPassphrase(int index, const std::string& passphrase) { 131 return ProfileSyncServiceFactory::GetForProfile( 132 test()->GetProfile(index))->SetDecryptionPassphrase(passphrase); 133 } 134 135 PasswordStore* GetPasswordStore(int index) { 136 return PasswordStoreFactory::GetForProfile(test()->GetProfile(index), 137 Profile::IMPLICIT_ACCESS).get(); 138 } 139 140 PasswordStore* GetVerifierPasswordStore() { 141 return PasswordStoreFactory::GetForProfile(test()->verifier(), 142 Profile::IMPLICIT_ACCESS).get(); 143 } 144 145 bool ProfileContainsSamePasswordFormsAsVerifier(int index) { 146 std::vector<PasswordForm> verifier_forms; 147 std::vector<PasswordForm> forms; 148 GetLogins(GetVerifierPasswordStore(), verifier_forms); 149 GetLogins(GetPasswordStore(index), forms); 150 ClearSyncDateField(&forms); 151 bool result = 152 password_manager::ContainsSamePasswordForms(verifier_forms, forms); 153 if (!result) { 154 LOG(ERROR) << "Password forms in Verifier Profile:"; 155 for (std::vector<PasswordForm>::iterator it = verifier_forms.begin(); 156 it != verifier_forms.end(); ++it) { 157 LOG(ERROR) << *it << std::endl; 158 } 159 LOG(ERROR) << "Password forms in Profile" << index << ":"; 160 for (std::vector<PasswordForm>::iterator it = forms.begin(); 161 it != forms.end(); ++it) { 162 LOG(ERROR) << *it << std::endl; 163 } 164 } 165 return result; 166 } 167 168 bool ProfilesContainSamePasswordForms(int index_a, int index_b) { 169 std::vector<PasswordForm> forms_a; 170 std::vector<PasswordForm> forms_b; 171 GetLogins(GetPasswordStore(index_a), forms_a); 172 GetLogins(GetPasswordStore(index_b), forms_b); 173 ClearSyncDateField(&forms_a); 174 ClearSyncDateField(&forms_b); 175 bool result = password_manager::ContainsSamePasswordForms(forms_a, forms_b); 176 if (!result) { 177 LOG(ERROR) << "Password forms in Profile" << index_a << ":"; 178 for (std::vector<PasswordForm>::iterator it = forms_a.begin(); 179 it != forms_a.end(); ++it) { 180 LOG(ERROR) << *it << std::endl; 181 } 182 LOG(ERROR) << "Password forms in Profile" << index_b << ":"; 183 for (std::vector<PasswordForm>::iterator it = forms_b.begin(); 184 it != forms_b.end(); ++it) { 185 LOG(ERROR) << *it << std::endl; 186 } 187 } 188 return result; 189 } 190 191 bool AllProfilesContainSamePasswordFormsAsVerifier() { 192 for (int i = 0; i < test()->num_clients(); ++i) { 193 if (!ProfileContainsSamePasswordFormsAsVerifier(i)) { 194 DVLOG(1) << "Profile " << i << " does not contain the same password" 195 " forms as the verifier."; 196 return false; 197 } 198 } 199 return true; 200 } 201 202 bool AllProfilesContainSamePasswordForms() { 203 for (int i = 1; i < test()->num_clients(); ++i) { 204 if (!ProfilesContainSamePasswordForms(0, i)) { 205 DVLOG(1) << "Profile " << i << " does not contain the same password" 206 " forms as Profile 0."; 207 return false; 208 } 209 } 210 return true; 211 } 212 213 namespace { 214 215 // Helper class used in the implementation of 216 // AwaitAllProfilesContainSamePasswordForms. 217 class SamePasswordFormsChecker : public MultiClientStatusChangeChecker { 218 public: 219 SamePasswordFormsChecker(); 220 virtual ~SamePasswordFormsChecker(); 221 222 virtual bool IsExitConditionSatisfied() OVERRIDE; 223 virtual std::string GetDebugMessage() const OVERRIDE; 224 225 private: 226 bool in_progress_; 227 bool needs_recheck_; 228 }; 229 230 SamePasswordFormsChecker::SamePasswordFormsChecker() 231 : MultiClientStatusChangeChecker( 232 sync_datatype_helper::test()->GetSyncServices()), 233 in_progress_(false), 234 needs_recheck_(false) {} 235 236 SamePasswordFormsChecker::~SamePasswordFormsChecker() {} 237 238 // This method needs protection against re-entrancy. 239 // 240 // This function indirectly calls GetLogins(), which starts a RunLoop on the UI 241 // thread. This can be a problem, since the next task to execute could very 242 // well contain a ProfileSyncService::OnStateChanged() event, which would 243 // trigger another call to this here function, and start another layer of 244 // nested RunLoops. That makes the StatusChangeChecker's Quit() method 245 // ineffective. 246 // 247 // The work-around is to not allow re-entrancy. But we can't just drop 248 // IsExitConditionSatisifed() calls if one is already in progress. Instead, we 249 // set a flag to ask the current execution of IsExitConditionSatisfied() to be 250 // re-run. This ensures that the return value is always based on the most 251 // up-to-date state. 252 bool SamePasswordFormsChecker::IsExitConditionSatisfied() { 253 if (in_progress_) { 254 LOG(WARNING) << "Setting flag and returning early to prevent nesting."; 255 needs_recheck_ = true; 256 return false; 257 } 258 259 // Keep retrying until we get a good reading. 260 bool result = false; 261 in_progress_ = true; 262 do { 263 needs_recheck_ = false; 264 result = AllProfilesContainSamePasswordForms(); 265 } while (needs_recheck_); 266 in_progress_ = false; 267 return result; 268 } 269 270 std::string SamePasswordFormsChecker::GetDebugMessage() const { 271 return "Waiting for matching passwords"; 272 } 273 274 } // namespace 275 276 bool AwaitAllProfilesContainSamePasswordForms() { 277 SamePasswordFormsChecker checker; 278 checker.Wait(); 279 return !checker.TimedOut(); 280 } 281 282 namespace { 283 284 // Helper class used in the implementation of 285 // AwaitProfileContainSamePasswordFormsAsVerifier. 286 class SamePasswordFormsAsVerifierChecker 287 : public SingleClientStatusChangeChecker { 288 public: 289 explicit SamePasswordFormsAsVerifierChecker(int index); 290 virtual ~SamePasswordFormsAsVerifierChecker(); 291 292 virtual bool IsExitConditionSatisfied() OVERRIDE; 293 virtual std::string GetDebugMessage() const OVERRIDE; 294 295 private: 296 int index_; 297 298 bool in_progress_; 299 bool needs_recheck_; 300 }; 301 302 SamePasswordFormsAsVerifierChecker::SamePasswordFormsAsVerifierChecker(int i) 303 : SingleClientStatusChangeChecker( 304 sync_datatype_helper::test()->GetSyncService(i)), 305 index_(i), 306 in_progress_(false), 307 needs_recheck_(false) { 308 } 309 310 SamePasswordFormsAsVerifierChecker::~SamePasswordFormsAsVerifierChecker() { 311 } 312 313 // This method uses the same re-entrancy prevention trick as 314 // the SamePasswordFormsChecker. 315 bool SamePasswordFormsAsVerifierChecker::IsExitConditionSatisfied() { 316 if (in_progress_) { 317 LOG(WARNING) << "Setting flag and returning early to prevent nesting."; 318 needs_recheck_ = true; 319 return false; 320 } 321 322 // Keep retrying until we get a good reading. 323 bool result = false; 324 in_progress_ = true; 325 do { 326 needs_recheck_ = false; 327 result = ProfileContainsSamePasswordFormsAsVerifier(index_); 328 } while (needs_recheck_); 329 in_progress_ = false; 330 return result; 331 } 332 333 std::string SamePasswordFormsAsVerifierChecker::GetDebugMessage() const { 334 return "Waiting for passwords to match verifier"; 335 } 336 337 } // namespace 338 339 bool AwaitProfileContainsSamePasswordFormsAsVerifier(int index) { 340 SamePasswordFormsAsVerifierChecker checker(index); 341 checker.Wait(); 342 return !checker.TimedOut(); 343 } 344 345 int GetPasswordCount(int index) { 346 std::vector<PasswordForm> forms; 347 GetLogins(GetPasswordStore(index), forms); 348 return forms.size(); 349 } 350 351 int GetVerifierPasswordCount() { 352 std::vector<PasswordForm> verifier_forms; 353 GetLogins(GetVerifierPasswordStore(), verifier_forms); 354 return verifier_forms.size(); 355 } 356 357 PasswordForm CreateTestPasswordForm(int index) { 358 PasswordForm form; 359 form.signon_realm = kFakeSignonRealm; 360 form.origin = GURL(base::StringPrintf(kIndexedFakeOrigin, index)); 361 form.username_value = 362 base::ASCIIToUTF16(base::StringPrintf("username%d", index)); 363 form.password_value = 364 base::ASCIIToUTF16(base::StringPrintf("password%d", index)); 365 form.date_created = base::Time::Now(); 366 return form; 367 } 368 369 } // namespace passwords_helper 370