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.
PasswordStoreCallback(base::WaitableEvent * wait_event)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:
PasswordStoreConsumerHelper(std::vector<PasswordForm> * result)44 explicit PasswordStoreConsumerHelper(std::vector<PasswordForm>* result)
45 : password_manager::PasswordStoreConsumer(), result_(result) {}
46
OnGetPasswordStoreResults(const std::vector<PasswordForm * > & result)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.
ClearSyncDateField(std::vector<PasswordForm> * forms)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
AddLogin(PasswordStore * store,const PasswordForm & form)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
UpdateLogin(PasswordStore * store,const PasswordForm & form)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
GetLogins(PasswordStore * store,std::vector<PasswordForm> & matches)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
RemoveLogin(PasswordStore * store,const PasswordForm & form)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
RemoveLogins(PasswordStore * store)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
SetEncryptionPassphrase(int index,const std::string & passphrase,ProfileSyncService::PassphraseType type)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
SetDecryptionPassphrase(int index,const std::string & passphrase)130 bool SetDecryptionPassphrase(int index, const std::string& passphrase) {
131 return ProfileSyncServiceFactory::GetForProfile(
132 test()->GetProfile(index))->SetDecryptionPassphrase(passphrase);
133 }
134
GetPasswordStore(int index)135 PasswordStore* GetPasswordStore(int index) {
136 return PasswordStoreFactory::GetForProfile(test()->GetProfile(index),
137 Profile::IMPLICIT_ACCESS).get();
138 }
139
GetVerifierPasswordStore()140 PasswordStore* GetVerifierPasswordStore() {
141 return PasswordStoreFactory::GetForProfile(test()->verifier(),
142 Profile::IMPLICIT_ACCESS).get();
143 }
144
ProfileContainsSamePasswordFormsAsVerifier(int index)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
ProfilesContainSamePasswordForms(int index_a,int index_b)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
AllProfilesContainSamePasswordFormsAsVerifier()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
AllProfilesContainSamePasswordForms()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
SamePasswordFormsChecker()230 SamePasswordFormsChecker::SamePasswordFormsChecker()
231 : MultiClientStatusChangeChecker(
232 sync_datatype_helper::test()->GetSyncServices()),
233 in_progress_(false),
234 needs_recheck_(false) {}
235
~SamePasswordFormsChecker()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.
IsExitConditionSatisfied()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
GetDebugMessage() const270 std::string SamePasswordFormsChecker::GetDebugMessage() const {
271 return "Waiting for matching passwords";
272 }
273
274 } // namespace
275
AwaitAllProfilesContainSamePasswordForms()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
SamePasswordFormsAsVerifierChecker(int i)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
~SamePasswordFormsAsVerifierChecker()310 SamePasswordFormsAsVerifierChecker::~SamePasswordFormsAsVerifierChecker() {
311 }
312
313 // This method uses the same re-entrancy prevention trick as
314 // the SamePasswordFormsChecker.
IsExitConditionSatisfied()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
GetDebugMessage() const333 std::string SamePasswordFormsAsVerifierChecker::GetDebugMessage() const {
334 return "Waiting for passwords to match verifier";
335 }
336
337 } // namespace
338
AwaitProfileContainsSamePasswordFormsAsVerifier(int index)339 bool AwaitProfileContainsSamePasswordFormsAsVerifier(int index) {
340 SamePasswordFormsAsVerifierChecker checker(index);
341 checker.Wait();
342 return !checker.TimedOut();
343 }
344
GetPasswordCount(int index)345 int GetPasswordCount(int index) {
346 std::vector<PasswordForm> forms;
347 GetLogins(GetPasswordStore(index), forms);
348 return forms.size();
349 }
350
GetVerifierPasswordCount()351 int GetVerifierPasswordCount() {
352 std::vector<PasswordForm> verifier_forms;
353 GetLogins(GetVerifierPasswordStore(), verifier_forms);
354 return verifier_forms.size();
355 }
356
CreateTestPasswordForm(int index)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