Home | History | Annotate | Download | only in integration
      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