passwords_helper.cc revision f8ee788a64d60abd8f2d742a5fdedde054ecd910
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 24using autofill::PasswordForm; 25using password_manager::PasswordStore; 26using sync_datatype_helper::test; 27 28const std::string kFakeSignonRealm = "http://fake-signon-realm.google.com/"; 29const char* kIndexedFakeOrigin = "http://fake-signon-realm.google.com/%d"; 30 31namespace { 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. 36void PasswordStoreCallback(base::WaitableEvent* wait_event) { 37 // Wake up passwords_helper::AddLogin. 38 wait_event->Signal(); 39} 40 41class 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. 69void 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 79namespace passwords_helper { 80 81void 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 89void 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 97void 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 106void 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 114void 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 123void SetEncryptionPassphrase(int index, 124 const std::string& passphrase, 125 ProfileSyncService::PassphraseType type) { 126 ProfileSyncServiceFactory::GetForProfile( 127 test()->GetProfile(index))->SetEncryptionPassphrase(passphrase, type); 128} 129 130bool SetDecryptionPassphrase(int index, const std::string& passphrase) { 131 return ProfileSyncServiceFactory::GetForProfile( 132 test()->GetProfile(index))->SetDecryptionPassphrase(passphrase); 133} 134 135PasswordStore* GetPasswordStore(int index) { 136 return PasswordStoreFactory::GetForProfile(test()->GetProfile(index), 137 Profile::IMPLICIT_ACCESS).get(); 138} 139 140PasswordStore* GetVerifierPasswordStore() { 141 return PasswordStoreFactory::GetForProfile(test()->verifier(), 142 Profile::IMPLICIT_ACCESS).get(); 143} 144 145bool 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 168bool 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 191bool 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 202bool 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 213namespace { 214 215// Helper class used in the implementation of 216// AwaitAllProfilesContainSamePasswordForms. 217class 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 230SamePasswordFormsChecker::SamePasswordFormsChecker() 231 : MultiClientStatusChangeChecker( 232 sync_datatype_helper::test()->GetSyncServices()), 233 in_progress_(false), 234 needs_recheck_(false) {} 235 236SamePasswordFormsChecker::~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. 252bool 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 270std::string SamePasswordFormsChecker::GetDebugMessage() const { 271 return "Waiting for matching passwords"; 272} 273 274} // namespace 275 276bool AwaitAllProfilesContainSamePasswordForms() { 277 SamePasswordFormsChecker checker; 278 checker.Wait(); 279 return !checker.TimedOut(); 280} 281 282namespace { 283 284// Helper class used in the implementation of 285// AwaitProfileContainSamePasswordFormsAsVerifier. 286class 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 302SamePasswordFormsAsVerifierChecker::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 310SamePasswordFormsAsVerifierChecker::~SamePasswordFormsAsVerifierChecker() { 311} 312 313// This method uses the same re-entrancy prevention trick as 314// the SamePasswordFormsChecker. 315bool 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 333std::string SamePasswordFormsAsVerifierChecker::GetDebugMessage() const { 334 return "Waiting for passwords to match verifier"; 335} 336 337} // namespace 338 339bool AwaitProfileContainsSamePasswordFormsAsVerifier(int index) { 340 SamePasswordFormsAsVerifierChecker checker(index); 341 checker.Wait(); 342 return !checker.TimedOut(); 343} 344 345int GetPasswordCount(int index) { 346 std::vector<PasswordForm> forms; 347 GetLogins(GetPasswordStore(index), forms); 348 return forms.size(); 349} 350 351int GetVerifierPasswordCount() { 352 std::vector<PasswordForm> verifier_forms; 353 GetLogins(GetVerifierPasswordStore(), verifier_forms); 354 return verifier_forms.size(); 355} 356 357PasswordForm 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