profile_sync_service_password_unittest.cc revision 72a454cd3513ac24fbdd0e0cb9ad70b86a99b801
1// Copyright (c) 2010 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 <vector>
6
7#include "testing/gtest/include/gtest/gtest.h"
8
9#include "base/task.h"
10#include "base/time.h"
11#include "base/utf_string_conversions.h"
12#include "chrome/browser/password_manager/password_store.h"
13#include "chrome/browser/prefs/pref_service.h"
14#include "chrome/browser/sync/abstract_profile_sync_service_test.h"
15#include "chrome/browser/sync/engine/syncapi.h"
16#include "chrome/browser/sync/glue/password_change_processor.h"
17#include "chrome/browser/sync/glue/password_data_type_controller.h"
18#include "chrome/browser/sync/glue/password_model_associator.h"
19#include "chrome/browser/sync/profile_sync_factory.h"
20#include "chrome/browser/sync/profile_sync_factory_mock.h"
21#include "chrome/browser/sync/profile_sync_service.h"
22#include "chrome/browser/sync/profile_sync_test_util.h"
23#include "chrome/browser/sync/protocol/password_specifics.pb.h"
24#include "chrome/browser/sync/syncable/directory_manager.h"
25#include "chrome/browser/sync/syncable/syncable.h"
26#include "chrome/browser/sync/test_profile_sync_service.h"
27#include "chrome/common/net/gaia/gaia_constants.h"
28#include "chrome/common/notification_observer_mock.h"
29#include "chrome/common/notification_source.h"
30#include "chrome/common/notification_type.h"
31#include "chrome/common/pref_names.h"
32#include "chrome/test/sync/engine/test_id_factory.h"
33#include "chrome/test/profile_mock.h"
34#include "testing/gmock/include/gmock/gmock.h"
35#include "webkit/glue/password_form.h"
36
37using base::Time;
38using browser_sync::PasswordChangeProcessor;
39using browser_sync::PasswordDataTypeController;
40using browser_sync::PasswordModelAssociator;
41using browser_sync::TestIdFactory;
42using browser_sync::UnrecoverableErrorHandler;
43using sync_api::SyncManager;
44using sync_api::UserShare;
45using syncable::BASE_VERSION;
46using syncable::CREATE;
47using syncable::DirectoryManager;
48using syncable::IS_DEL;
49using syncable::IS_DIR;
50using syncable::IS_UNAPPLIED_UPDATE;
51using syncable::IS_UNSYNCED;
52using syncable::MutableEntry;
53using syncable::SERVER_IS_DIR;
54using syncable::SERVER_VERSION;
55using syncable::SPECIFICS;
56using syncable::ScopedDirLookup;
57using syncable::UNIQUE_SERVER_TAG;
58using syncable::UNITTEST;
59using syncable::WriteTransaction;
60using testing::_;
61using testing::DoAll;
62using testing::DoDefault;
63using testing::ElementsAre;
64using testing::Eq;
65using testing::Invoke;
66using testing::Return;
67using testing::SaveArg;
68using testing::SetArgumentPointee;
69using webkit_glue::PasswordForm;
70
71ACTION_P3(MakePasswordSyncComponents, service, ps, dtc) {
72  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
73  PasswordModelAssociator* model_associator =
74      new PasswordModelAssociator(service, ps);
75  PasswordChangeProcessor* change_processor =
76      new PasswordChangeProcessor(model_associator, ps, dtc);
77  return ProfileSyncFactory::SyncComponents(model_associator,
78                                            change_processor);
79}
80
81class MockPasswordStore : public PasswordStore {
82 public:
83  MOCK_METHOD1(RemoveLogin, void(const PasswordForm&));
84  MOCK_METHOD2(GetLogins, int(const PasswordForm&, PasswordStoreConsumer*));
85  MOCK_METHOD1(AddLogin, void(const PasswordForm&));
86  MOCK_METHOD1(UpdateLogin, void(const PasswordForm&));
87  MOCK_METHOD0(ReportMetrics, void());
88  MOCK_METHOD0(ReportMetricsImpl, void());
89  MOCK_METHOD1(AddLoginImpl, void(const PasswordForm&));
90  MOCK_METHOD1(UpdateLoginImpl, void(const PasswordForm&));
91  MOCK_METHOD1(RemoveLoginImpl, void(const PasswordForm&));
92  MOCK_METHOD2(RemoveLoginsCreatedBetweenImpl, void(const base::Time&,
93               const base::Time&));
94  MOCK_METHOD2(GetLoginsImpl, void(GetLoginsRequest*, const PasswordForm&));
95  MOCK_METHOD1(GetAutofillableLoginsImpl, void(GetLoginsRequest*));
96  MOCK_METHOD1(GetBlacklistLoginsImpl, void(GetLoginsRequest*));
97  MOCK_METHOD1(FillAutofillableLogins,
98      bool(std::vector<PasswordForm*>*));
99  MOCK_METHOD1(FillBlacklistLogins,
100      bool(std::vector<PasswordForm*>*));
101};
102
103class PasswordTestProfileSyncService : public TestProfileSyncService {
104 public:
105  PasswordTestProfileSyncService(ProfileSyncFactory* factory,
106                                 Profile* profile,
107                                 const std::string& test_user,
108                                 bool synchronous_backend_initialization,
109                                 Task* initial_condition_setup_task,
110                                 Task* passphrase_accept_task)
111      : TestProfileSyncService(factory, profile, test_user,
112                               synchronous_backend_initialization,
113                               initial_condition_setup_task),
114        passphrase_accept_task_(passphrase_accept_task) {}
115
116  virtual ~PasswordTestProfileSyncService() {}
117
118  virtual void OnPassphraseRequired(bool for_decryption) {
119    TestProfileSyncService::OnPassphraseRequired(for_decryption);
120    ADD_FAILURE();
121  }
122
123  virtual void OnPassphraseAccepted() {
124    TestProfileSyncService::OnPassphraseAccepted();
125
126    if (passphrase_accept_task_) {
127      passphrase_accept_task_->Run();
128    }
129  }
130
131 private:
132  Task* passphrase_accept_task_;
133};
134
135class ProfileSyncServicePasswordTest : public AbstractProfileSyncServiceTest {
136 protected:
137  ProfileSyncServicePasswordTest()
138      : db_thread_(BrowserThread::DB) {
139  }
140
141  virtual void SetUp() {
142    password_store_ = new MockPasswordStore();
143    db_thread_.Start();
144
145    notification_service_ = new ThreadNotificationService(&db_thread_);
146    notification_service_->Init();
147    registrar_.Add(&observer_,
148        NotificationType::SYNC_CONFIGURE_DONE,
149        NotificationService::AllSources());
150  }
151
152  virtual void TearDown() {
153    service_.reset();
154    notification_service_->TearDown();
155    db_thread_.Stop();
156    MessageLoop::current()->RunAllPending();
157  }
158
159  void StartSyncService(Task* root_task, Task* node_task) {
160    StartSyncService(root_task, node_task, 2, 2);
161  }
162
163  void StartSyncService(Task* root_task, Task* node_task,
164                        int num_resume_expectations,
165                        int num_pause_expectations) {
166    if (!service_.get()) {
167      service_.reset(new PasswordTestProfileSyncService(
168          &factory_, &profile_, "test_user", false, root_task, node_task));
169      service_->RegisterPreferences();
170      profile_.GetPrefs()->SetBoolean(prefs::kSyncPasswords, true);
171      service_->set_num_expected_resumes(num_resume_expectations);
172      service_->set_num_expected_pauses(num_pause_expectations);
173      PasswordDataTypeController* data_type_controller =
174          new PasswordDataTypeController(&factory_,
175                                         &profile_,
176                                         service_.get());
177
178      EXPECT_CALL(factory_, CreatePasswordSyncComponents(_, _, _)).
179          WillOnce(MakePasswordSyncComponents(service_.get(),
180                                              password_store_.get(),
181                                              data_type_controller));
182      EXPECT_CALL(factory_, CreateDataTypeManager(_, _)).
183          WillOnce(ReturnNewDataTypeManager());
184
185      // We need tokens to get the tests going
186      token_service_.IssueAuthTokenForTest(
187          GaiaConstants::kSyncService, "token");
188
189      EXPECT_CALL(profile_, GetTokenService()).
190          WillRepeatedly(Return(&token_service_));
191
192      EXPECT_CALL(profile_, GetPasswordStore(_)).
193          Times(3).
194          WillRepeatedly(Return(password_store_.get()));
195
196      EXPECT_CALL(observer_,
197          Observe(
198              NotificationType(NotificationType::SYNC_CONFIGURE_DONE),_,_));
199
200      service_->RegisterDataTypeController(data_type_controller);
201      service_->Initialize();
202      MessageLoop::current()->Run();
203
204      EXPECT_CALL(
205          observer_,
206          Observe(
207              NotificationType(NotificationType::SYNC_CONFIGURE_DONE),
208              _,_)).
209          WillOnce(QuitUIMessageLoop());
210      service_->SetPassphrase("foo", false, true);
211      MessageLoop::current()->Run();
212    }
213  }
214
215  void AddPasswordSyncNode(const PasswordForm& entry) {
216    sync_api::WriteTransaction trans(service_->GetUserShare());
217    sync_api::ReadNode password_root(&trans);
218    ASSERT_TRUE(password_root.InitByTagLookup(browser_sync::kPasswordTag));
219
220    sync_api::WriteNode node(&trans);
221    std::string tag = PasswordModelAssociator::MakeTag(entry);
222    ASSERT_TRUE(node.InitUniqueByCreation(syncable::PASSWORDS,
223                                          password_root,
224                                          tag));
225    PasswordModelAssociator::WriteToSyncNode(entry, &node);
226  }
227
228  void GetPasswordEntriesFromSyncDB(std::vector<PasswordForm>* entries) {
229    sync_api::ReadTransaction trans(service_->GetUserShare());
230    sync_api::ReadNode password_root(&trans);
231    ASSERT_TRUE(password_root.InitByTagLookup(browser_sync::kPasswordTag));
232
233    int64 child_id = password_root.GetFirstChildId();
234    while (child_id != sync_api::kInvalidId) {
235      sync_api::ReadNode child_node(&trans);
236      ASSERT_TRUE(child_node.InitByIdLookup(child_id));
237
238      const sync_pb::PasswordSpecificsData& password =
239          child_node.GetPasswordSpecifics();
240
241      PasswordForm form;
242      PasswordModelAssociator::CopyPassword(password, &form);
243
244      entries->push_back(form);
245
246      child_id = child_node.GetSuccessorId();
247    }
248  }
249
250  bool ComparePasswords(const PasswordForm& lhs, const PasswordForm& rhs) {
251    return lhs.scheme == rhs.scheme &&
252           lhs.signon_realm == rhs.signon_realm &&
253           lhs.origin == rhs.origin &&
254           lhs.action == rhs.action &&
255           lhs.username_element == rhs.username_element &&
256           lhs.username_value == rhs.username_value &&
257           lhs.password_element == rhs.password_element &&
258           lhs.password_value == rhs.password_value &&
259           lhs.ssl_valid == rhs.ssl_valid &&
260           lhs.preferred == rhs.preferred &&
261           lhs.date_created == rhs.date_created &&
262           lhs.blacklisted_by_user == rhs.blacklisted_by_user;
263  }
264
265  void SetIdleChangeProcessorExpectations() {
266    EXPECT_CALL(*password_store_, AddLoginImpl(_)).Times(0);
267    EXPECT_CALL(*password_store_, UpdateLoginImpl(_)).Times(0);
268    EXPECT_CALL(*password_store_, RemoveLoginImpl(_)).Times(0);
269  }
270
271  friend class AddPasswordEntriesTask;
272
273  BrowserThread db_thread_;
274  scoped_refptr<ThreadNotificationService> notification_service_;
275  NotificationObserverMock observer_;
276  ProfileMock profile_;
277  scoped_refptr<MockPasswordStore> password_store_;
278  NotificationRegistrar registrar_;
279
280};
281
282class AddPasswordEntriesTask : public Task {
283 public:
284  AddPasswordEntriesTask(ProfileSyncServicePasswordTest* test,
285                         const std::vector<PasswordForm>& entries)
286      : test_(test), entries_(entries) {
287  }
288
289  virtual void Run() {
290    for (size_t i = 0; i < entries_.size(); ++i) {
291      test_->AddPasswordSyncNode(entries_[i]);
292    }
293  }
294
295 private:
296  ProfileSyncServicePasswordTest* test_;
297  const std::vector<PasswordForm>& entries_;
298};
299
300TEST_F(ProfileSyncServicePasswordTest, FailModelAssociation) {
301  StartSyncService(NULL, NULL, 1, 2);
302  EXPECT_TRUE(service_->unrecoverable_error_detected());
303}
304
305TEST_F(ProfileSyncServicePasswordTest, EmptyNativeEmptySync) {
306  EXPECT_CALL(*password_store_, FillAutofillableLogins(_))
307      .WillOnce(Return(true));
308  EXPECT_CALL(*password_store_, FillBlacklistLogins(_))
309      .WillOnce(Return(true));
310  SetIdleChangeProcessorExpectations();
311  CreateRootTask task(this, syncable::PASSWORDS);
312  StartSyncService(&task, NULL);
313  std::vector<PasswordForm> sync_entries;
314  GetPasswordEntriesFromSyncDB(&sync_entries);
315  EXPECT_EQ(0U, sync_entries.size());
316}
317
318TEST_F(ProfileSyncServicePasswordTest, HasNativeEntriesEmptySync) {
319  std::vector<PasswordForm*> forms;
320  std::vector<PasswordForm> expected_forms;
321  PasswordForm* new_form = new PasswordForm;
322  new_form->scheme = PasswordForm::SCHEME_HTML;
323  new_form->signon_realm = "pie";
324  new_form->origin = GURL("http://pie.com");
325  new_form->action = GURL("http://pie.com/submit");
326  new_form->username_element = UTF8ToUTF16("name");
327  new_form->username_value = UTF8ToUTF16("tom");
328  new_form->password_element = UTF8ToUTF16("cork");
329  new_form->password_value = UTF8ToUTF16("password1");
330  new_form->ssl_valid = true;
331  new_form->preferred = false;
332  new_form->date_created = base::Time::FromInternalValue(1234);
333  new_form->blacklisted_by_user = false;
334  forms.push_back(new_form);
335  expected_forms.push_back(*new_form);
336  EXPECT_CALL(*password_store_, FillAutofillableLogins(_))
337      .WillOnce(DoAll(SetArgumentPointee<0>(forms), Return(true)));
338  EXPECT_CALL(*password_store_, FillBlacklistLogins(_))
339      .WillOnce(Return(true));
340  SetIdleChangeProcessorExpectations();
341  CreateRootTask task(this, syncable::PASSWORDS);
342  StartSyncService(&task, NULL);
343  std::vector<PasswordForm> sync_forms;
344  GetPasswordEntriesFromSyncDB(&sync_forms);
345  ASSERT_EQ(1U, sync_forms.size());
346  EXPECT_TRUE(ComparePasswords(expected_forms[0], sync_forms[0]));
347}
348
349TEST_F(ProfileSyncServicePasswordTest, HasNativeEntriesEmptySyncSameUsername) {
350  std::vector<PasswordForm*> forms;
351  std::vector<PasswordForm> expected_forms;
352
353  {
354    PasswordForm* new_form = new PasswordForm;
355    new_form->scheme = PasswordForm::SCHEME_HTML;
356    new_form->signon_realm = "pie";
357    new_form->origin = GURL("http://pie.com");
358    new_form->action = GURL("http://pie.com/submit");
359    new_form->username_element = UTF8ToUTF16("name");
360    new_form->username_value = UTF8ToUTF16("tom");
361    new_form->password_element = UTF8ToUTF16("cork");
362    new_form->password_value = UTF8ToUTF16("password1");
363    new_form->ssl_valid = true;
364    new_form->preferred = false;
365    new_form->date_created = base::Time::FromInternalValue(1234);
366    new_form->blacklisted_by_user = false;
367    forms.push_back(new_form);
368    expected_forms.push_back(*new_form);
369  }
370  {
371    PasswordForm* new_form = new PasswordForm;
372    new_form->scheme = PasswordForm::SCHEME_HTML;
373    new_form->signon_realm = "pie";
374    new_form->origin = GURL("http://pie.com");
375    new_form->action = GURL("http://pie.com/submit");
376    new_form->username_element = UTF8ToUTF16("name");
377    new_form->username_value = UTF8ToUTF16("pete");
378    new_form->password_element = UTF8ToUTF16("cork");
379    new_form->password_value = UTF8ToUTF16("password2");
380    new_form->ssl_valid = true;
381    new_form->preferred = false;
382    new_form->date_created = base::Time::FromInternalValue(1234);
383    new_form->blacklisted_by_user = false;
384    forms.push_back(new_form);
385    expected_forms.push_back(*new_form);
386  }
387
388  EXPECT_CALL(*password_store_, FillAutofillableLogins(_))
389      .WillOnce(DoAll(SetArgumentPointee<0>(forms), Return(true)));
390  EXPECT_CALL(*password_store_, FillBlacklistLogins(_))
391      .WillOnce(Return(true));
392  SetIdleChangeProcessorExpectations();
393  CreateRootTask task(this, syncable::PASSWORDS);
394  StartSyncService(&task, NULL);
395  std::vector<PasswordForm> sync_forms;
396  GetPasswordEntriesFromSyncDB(&sync_forms);
397  ASSERT_EQ(2U, sync_forms.size());
398  EXPECT_TRUE(ComparePasswords(expected_forms[0], sync_forms[1]));
399  EXPECT_TRUE(ComparePasswords(expected_forms[1], sync_forms[0]));
400}
401
402TEST_F(ProfileSyncServicePasswordTest, HasNativeHasSyncNoMerge) {
403  std::vector<PasswordForm*> native_forms;
404  std::vector<PasswordForm> sync_forms;
405  std::vector<PasswordForm> expected_forms;
406  {
407    PasswordForm* new_form = new PasswordForm;
408    new_form->scheme = PasswordForm::SCHEME_HTML;
409    new_form->signon_realm = "pie";
410    new_form->origin = GURL("http://pie.com");
411    new_form->action = GURL("http://pie.com/submit");
412    new_form->username_element = UTF8ToUTF16("name");
413    new_form->username_value = UTF8ToUTF16("tom");
414    new_form->password_element = UTF8ToUTF16("cork");
415    new_form->password_value = UTF8ToUTF16("password1");
416    new_form->ssl_valid = true;
417    new_form->preferred = false;
418    new_form->date_created = base::Time::FromInternalValue(1234);
419    new_form->blacklisted_by_user = false;
420
421    native_forms.push_back(new_form);
422    expected_forms.push_back(*new_form);
423  }
424
425  {
426    PasswordForm new_form;
427    new_form.scheme = PasswordForm::SCHEME_HTML;
428    new_form.signon_realm = "pie2";
429    new_form.origin = GURL("http://pie2.com");
430    new_form.action = GURL("http://pie2.com/submit");
431    new_form.username_element = UTF8ToUTF16("name2");
432    new_form.username_value = UTF8ToUTF16("tom2");
433    new_form.password_element = UTF8ToUTF16("cork2");
434    new_form.password_value = UTF8ToUTF16("password12");
435    new_form.ssl_valid = false;
436    new_form.preferred = true;
437    new_form.date_created = base::Time::FromInternalValue(12345);
438    new_form.blacklisted_by_user = false;
439    sync_forms.push_back(new_form);
440    expected_forms.push_back(new_form);
441  }
442
443  EXPECT_CALL(*password_store_, FillAutofillableLogins(_))
444      .WillOnce(DoAll(SetArgumentPointee<0>(native_forms), Return(true)));
445  EXPECT_CALL(*password_store_, FillBlacklistLogins(_)).WillOnce(Return(true));
446  EXPECT_CALL(*password_store_, AddLoginImpl(_)).Times(1);
447
448  CreateRootTask root_task(this, syncable::PASSWORDS);
449  AddPasswordEntriesTask node_task(this, sync_forms);
450  StartSyncService(&root_task, &node_task);
451
452  std::vector<PasswordForm> new_sync_forms;
453  GetPasswordEntriesFromSyncDB(&new_sync_forms);
454
455  EXPECT_EQ(2U, new_sync_forms.size());
456  EXPECT_TRUE(ComparePasswords(expected_forms[0], new_sync_forms[0]));
457  EXPECT_TRUE(ComparePasswords(expected_forms[1], new_sync_forms[1]));
458}
459
460TEST_F(ProfileSyncServicePasswordTest, HasNativeHasSyncMergeEntry) {
461  std::vector<PasswordForm*> native_forms;
462  std::vector<PasswordForm> sync_forms;
463  std::vector<PasswordForm> expected_forms;
464  {
465    PasswordForm* new_form = new PasswordForm;
466    new_form->scheme = PasswordForm::SCHEME_HTML;
467    new_form->signon_realm = "pie";
468    new_form->origin = GURL("http://pie.com");
469    new_form->action = GURL("http://pie.com/submit");
470    new_form->username_element = UTF8ToUTF16("name");
471    new_form->username_value = UTF8ToUTF16("tom");
472    new_form->password_element = UTF8ToUTF16("cork");
473    new_form->password_value = UTF8ToUTF16("password1");
474    new_form->ssl_valid = true;
475    new_form->preferred = false;
476    new_form->date_created = base::Time::FromInternalValue(1234);
477    new_form->blacklisted_by_user = false;
478
479    native_forms.push_back(new_form);
480  }
481
482  {
483    PasswordForm new_form;
484    new_form.scheme = PasswordForm::SCHEME_HTML;
485    new_form.signon_realm = "pie";
486    new_form.origin = GURL("http://pie.com");
487    new_form.action = GURL("http://pie.com/submit");
488    new_form.username_element = UTF8ToUTF16("name");
489    new_form.username_value = UTF8ToUTF16("tom");
490    new_form.password_element = UTF8ToUTF16("cork");
491    new_form.password_value = UTF8ToUTF16("password12");
492    new_form.ssl_valid = false;
493    new_form.preferred = true;
494    new_form.date_created = base::Time::FromInternalValue(12345);
495    new_form.blacklisted_by_user = false;
496    sync_forms.push_back(new_form);
497  }
498
499  {
500    PasswordForm new_form;
501    new_form.scheme = PasswordForm::SCHEME_HTML;
502    new_form.signon_realm = "pie";
503    new_form.origin = GURL("http://pie.com");
504    new_form.action = GURL("http://pie.com/submit");
505    new_form.username_element = UTF8ToUTF16("name");
506    new_form.username_value = UTF8ToUTF16("tom");
507    new_form.password_element = UTF8ToUTF16("cork");
508    new_form.password_value = UTF8ToUTF16("password12");
509    new_form.ssl_valid = false;
510    new_form.preferred = true;
511    new_form.date_created = base::Time::FromInternalValue(12345);
512    new_form.blacklisted_by_user = false;
513    expected_forms.push_back(new_form);
514  }
515
516  EXPECT_CALL(*password_store_, FillAutofillableLogins(_))
517      .WillOnce(DoAll(SetArgumentPointee<0>(native_forms), Return(true)));
518  EXPECT_CALL(*password_store_, FillBlacklistLogins(_)).WillOnce(Return(true));
519  EXPECT_CALL(*password_store_, UpdateLoginImpl(_)).Times(1);
520
521  CreateRootTask root_task(this, syncable::PASSWORDS);
522  AddPasswordEntriesTask node_task(this, sync_forms);
523
524  StartSyncService(&root_task, &node_task);
525
526  std::vector<PasswordForm> new_sync_forms;
527  GetPasswordEntriesFromSyncDB(&new_sync_forms);
528
529  EXPECT_EQ(1U, new_sync_forms.size());
530  EXPECT_TRUE(ComparePasswords(expected_forms[0], new_sync_forms[0]));
531}
532