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