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