1 // Copyright 2014 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 "components/password_manager/core/browser/password_syncable_service.h" 6 7 #include <algorithm> 8 #include <string> 9 #include <vector> 10 11 #include "base/basictypes.h" 12 #include "base/location.h" 13 #include "base/memory/ref_counted.h" 14 #include "base/memory/scoped_ptr.h" 15 #include "base/strings/utf_string_conversions.h" 16 #include "components/password_manager/core/browser/mock_password_store.h" 17 #include "sync/api/sync_change_processor.h" 18 #include "sync/api/sync_error.h" 19 #include "sync/api/sync_error_factory_mock.h" 20 #include "testing/gmock/include/gmock/gmock.h" 21 #include "testing/gtest/include/gtest/gtest.h" 22 23 using syncer::SyncChange; 24 using syncer::SyncData; 25 using syncer::SyncDataList; 26 using syncer::SyncError; 27 using testing::AnyNumber; 28 using testing::ElementsAre; 29 using testing::Invoke; 30 using testing::IsEmpty; 31 using testing::Matches; 32 using testing::Return; 33 using testing::SetArgPointee; 34 using testing::UnorderedElementsAre; 35 using testing::_; 36 37 namespace password_manager { 38 39 // Defined in the implementation file corresponding to this test. 40 syncer::SyncData SyncDataFromPassword(const autofill::PasswordForm& password); 41 autofill::PasswordForm PasswordFromSpecifics( 42 const sync_pb::PasswordSpecificsData& password); 43 std::string MakePasswordSyncTag(const sync_pb::PasswordSpecificsData& password); 44 std::string MakePasswordSyncTag(const autofill::PasswordForm& password); 45 46 namespace { 47 48 // PasswordForm values for tests. 49 const autofill::PasswordForm::Type kArbitraryType = 50 autofill::PasswordForm::TYPE_GENERATED; 51 const char kAvatarUrl[] = "https://fb.com/Avatar"; 52 const char kDisplayName[] = "Agent Smith"; 53 const char kFederationUrl[] = "https://fb.com/federation_url"; 54 const char kSignonRealm[] = "abc"; 55 const char kSignonRealm2[] = "def"; 56 const char kSignonRealm3[] = "xyz"; 57 const int kTimesUsed = 5; 58 59 typedef std::vector<SyncChange> SyncChangeList; 60 61 const sync_pb::PasswordSpecificsData& GetPasswordSpecifics( 62 const syncer::SyncData& sync_data) { 63 return sync_data.GetSpecifics().password().client_only_encrypted_data(); 64 } 65 66 MATCHER(HasDateSynced, "") { 67 return !arg.date_synced.is_null() && !arg.date_synced.is_max(); 68 } 69 70 MATCHER_P(PasswordIs, form, "") { 71 sync_pb::PasswordSpecificsData actual_password = 72 GetPasswordSpecifics(SyncDataFromPassword(arg)); 73 sync_pb::PasswordSpecificsData expected_password = 74 GetPasswordSpecifics(SyncDataFromPassword(form)); 75 if (expected_password.scheme() == actual_password.scheme() && 76 expected_password.signon_realm() == actual_password.signon_realm() && 77 expected_password.origin() == actual_password.origin() && 78 expected_password.action() == actual_password.action() && 79 expected_password.username_element() == 80 actual_password.username_element() && 81 expected_password.password_element() == 82 actual_password.password_element() && 83 expected_password.username_value() == actual_password.username_value() && 84 expected_password.password_value() == actual_password.password_value() && 85 expected_password.ssl_valid() == actual_password.ssl_valid() && 86 expected_password.preferred() == actual_password.preferred() && 87 expected_password.date_created() == actual_password.date_created() && 88 expected_password.blacklisted() == actual_password.blacklisted() && 89 expected_password.type() == actual_password.type() && 90 expected_password.times_used() == actual_password.times_used() && 91 expected_password.display_name() == actual_password.display_name() && 92 expected_password.avatar_url() == actual_password.avatar_url() && 93 expected_password.federation_url() == actual_password.federation_url()) 94 return true; 95 96 *result_listener << "Password protobuf does not match; expected:\n" 97 << form << '\n' 98 << "actual:" << '\n' 99 << arg; 100 return false; 101 } 102 103 MATCHER_P2(SyncChangeIs, change_type, password, "") { 104 const SyncData& data = arg.sync_data(); 105 autofill::PasswordForm form = PasswordFromSpecifics( 106 GetPasswordSpecifics(data)); 107 return (arg.change_type() == change_type && 108 syncer::SyncDataLocal(data).GetTag() == 109 MakePasswordSyncTag(password) && 110 (change_type == SyncChange::ACTION_DELETE || 111 Matches(PasswordIs(password))(form))); 112 } 113 114 // The argument is std::vector<autofill::PasswordForm*>*. The caller is 115 // responsible for the lifetime of all the password forms. 116 ACTION_P(AppendForm, form) { 117 arg0->push_back(new autofill::PasswordForm(form)); 118 return true; 119 } 120 121 // Creates a sync data consisting of password specifics. The sign on realm is 122 // set to |signon_realm|. 123 SyncData CreateSyncData(const std::string& signon_realm) { 124 sync_pb::EntitySpecifics password_data; 125 sync_pb::PasswordSpecificsData* password_specifics = 126 password_data.mutable_password()->mutable_client_only_encrypted_data(); 127 password_specifics->set_signon_realm(signon_realm); 128 password_specifics->set_type(autofill::PasswordForm::TYPE_GENERATED); 129 password_specifics->set_times_used(3); 130 password_specifics->set_display_name("Mr. X"); 131 password_specifics->set_avatar_url("https://accounts.google.com/Avatar"); 132 password_specifics->set_federation_url("https://google.com/federation"); 133 134 std::string tag = MakePasswordSyncTag(*password_specifics); 135 return syncer::SyncData::CreateLocalData(tag, tag, password_data); 136 } 137 138 SyncChange CreateSyncChange(const autofill::PasswordForm& password, 139 SyncChange::SyncChangeType type) { 140 SyncData data = SyncDataFromPassword(password); 141 return SyncChange(FROM_HERE, type, data); 142 } 143 144 // A testable implementation of the |PasswordSyncableService| that mocks 145 // out all interaction with the password database. 146 class MockPasswordSyncableService : public PasswordSyncableService { 147 public: 148 explicit MockPasswordSyncableService(PasswordStoreSync* password_store) 149 : PasswordSyncableService(password_store) {} 150 151 MOCK_METHOD1(StartSyncFlare, void(syncer::ModelType)); 152 153 private: 154 DISALLOW_COPY_AND_ASSIGN(MockPasswordSyncableService); 155 }; 156 157 // Mock implementation of SyncChangeProcessor. 158 class MockSyncChangeProcessor : public syncer::SyncChangeProcessor { 159 public: 160 MockSyncChangeProcessor() {} 161 162 MOCK_METHOD2(ProcessSyncChanges, 163 SyncError(const tracked_objects::Location&, 164 const SyncChangeList& list)); 165 virtual SyncDataList GetAllSyncData(syncer::ModelType type) const OVERRIDE { 166 NOTREACHED(); 167 return SyncDataList(); 168 } 169 170 private: 171 DISALLOW_COPY_AND_ASSIGN(MockSyncChangeProcessor); 172 }; 173 174 // Convenience wrapper around a PasswordSyncableService and PasswordStore 175 // pair. 176 class PasswordSyncableServiceWrapper { 177 public: 178 PasswordSyncableServiceWrapper() { 179 password_store_ = new testing::StrictMock<MockPasswordStore>; 180 service_.reset(new MockPasswordSyncableService( 181 password_store_->GetSyncInterface())); 182 ON_CALL(*password_store_, AddLoginImpl(HasDateSynced())) 183 .WillByDefault(Return(PasswordStoreChangeList())); 184 ON_CALL(*password_store_, RemoveLoginImpl(_)) 185 .WillByDefault(Return(PasswordStoreChangeList())); 186 ON_CALL(*password_store_, UpdateLoginImpl(HasDateSynced())) 187 .WillByDefault(Return(PasswordStoreChangeList())); 188 EXPECT_CALL(*password_store(), NotifyLoginsChanged(_)).Times(AnyNumber()); 189 } 190 191 ~PasswordSyncableServiceWrapper() { 192 password_store_->Shutdown(); 193 } 194 195 MockPasswordStore* password_store() { return password_store_.get(); } 196 197 MockPasswordSyncableService* service() { return service_.get(); } 198 199 // Returnes the scoped_ptr to |service_| thus NULLing out it. 200 scoped_ptr<syncer::SyncChangeProcessor> ReleaseSyncableService() { 201 return service_.PassAs<syncer::SyncChangeProcessor>(); 202 } 203 204 private: 205 scoped_refptr<MockPasswordStore> password_store_; 206 scoped_ptr<MockPasswordSyncableService> service_; 207 208 DISALLOW_COPY_AND_ASSIGN(PasswordSyncableServiceWrapper); 209 }; 210 211 class PasswordSyncableServiceTest : public testing::Test { 212 public: 213 PasswordSyncableServiceTest() 214 : processor_(new testing::StrictMock<MockSyncChangeProcessor>) { 215 ON_CALL(*processor_, ProcessSyncChanges(_, _)) 216 .WillByDefault(Return(SyncError())); 217 } 218 MockPasswordStore* password_store() { return wrapper_.password_store(); } 219 MockPasswordSyncableService* service() { return wrapper_.service(); } 220 221 protected: 222 scoped_ptr<MockSyncChangeProcessor> processor_; 223 224 private: 225 PasswordSyncableServiceWrapper wrapper_; 226 }; 227 228 229 // Both sync and password db have data that are not present in the other. 230 TEST_F(PasswordSyncableServiceTest, AdditionsInBoth) { 231 autofill::PasswordForm form; 232 form.signon_realm = kSignonRealm; 233 234 SyncDataList list; 235 list.push_back(CreateSyncData(kSignonRealm2)); 236 autofill::PasswordForm new_from_sync = PasswordFromSpecifics( 237 GetPasswordSpecifics(list.back())); 238 239 EXPECT_CALL(*password_store(), FillAutofillableLogins(_)) 240 .WillOnce(AppendForm(form)); 241 EXPECT_CALL(*password_store(), FillBlacklistLogins(_)) 242 .WillOnce(Return(true)); 243 EXPECT_CALL(*password_store(), AddLoginImpl(PasswordIs(new_from_sync))); 244 EXPECT_CALL(*processor_, ProcessSyncChanges(_, ElementsAre( 245 SyncChangeIs(SyncChange::ACTION_ADD, form)))); 246 247 service()->MergeDataAndStartSyncing( 248 syncer::PASSWORDS, 249 list, 250 processor_.PassAs<syncer::SyncChangeProcessor>(), 251 scoped_ptr<syncer::SyncErrorFactory>()); 252 } 253 254 // Sync has data that is not present in the password db. 255 TEST_F(PasswordSyncableServiceTest, AdditionOnlyInSync) { 256 SyncDataList list; 257 list.push_back(CreateSyncData(kSignonRealm)); 258 autofill::PasswordForm new_from_sync = PasswordFromSpecifics( 259 GetPasswordSpecifics(list.back())); 260 261 EXPECT_CALL(*password_store(), FillAutofillableLogins(_)) 262 .WillOnce(Return(true)); 263 EXPECT_CALL(*password_store(), FillBlacklistLogins(_)) 264 .WillOnce(Return(true)); 265 EXPECT_CALL(*password_store(), AddLoginImpl(PasswordIs(new_from_sync))); 266 EXPECT_CALL(*processor_, ProcessSyncChanges(_, IsEmpty())); 267 268 service()->MergeDataAndStartSyncing( 269 syncer::PASSWORDS, 270 list, 271 processor_.PassAs<syncer::SyncChangeProcessor>(), 272 scoped_ptr<syncer::SyncErrorFactory>()); 273 } 274 275 // Passwords db has data that is not present in sync. 276 TEST_F(PasswordSyncableServiceTest, AdditionOnlyInPasswordStore) { 277 autofill::PasswordForm form; 278 form.signon_realm = kSignonRealm; 279 form.times_used = kTimesUsed; 280 form.type = kArbitraryType; 281 form.display_name = base::ASCIIToUTF16(kDisplayName); 282 form.avatar_url = GURL(kAvatarUrl); 283 form.federation_url = GURL(kFederationUrl); 284 EXPECT_CALL(*password_store(), FillAutofillableLogins(_)) 285 .WillOnce(AppendForm(form)); 286 EXPECT_CALL(*password_store(), FillBlacklistLogins(_)) 287 .WillOnce(Return(true)); 288 289 EXPECT_CALL(*processor_, ProcessSyncChanges(_, ElementsAre( 290 SyncChangeIs(SyncChange::ACTION_ADD, form)))); 291 292 service()->MergeDataAndStartSyncing( 293 syncer::PASSWORDS, 294 SyncDataList(), 295 processor_.PassAs<syncer::SyncChangeProcessor>(), 296 scoped_ptr<syncer::SyncErrorFactory>()); 297 } 298 299 // Both passwords db and sync contain the same data. 300 TEST_F(PasswordSyncableServiceTest, BothInSync) { 301 autofill::PasswordForm form; 302 form.signon_realm = kSignonRealm; 303 form.times_used = kTimesUsed; 304 form.type = kArbitraryType; 305 EXPECT_CALL(*password_store(), FillAutofillableLogins(_)) 306 .WillOnce(AppendForm(form)); 307 EXPECT_CALL(*password_store(), FillBlacklistLogins(_)) 308 .WillOnce(Return(true)); 309 310 EXPECT_CALL(*processor_, ProcessSyncChanges(_, IsEmpty())); 311 312 service()->MergeDataAndStartSyncing( 313 syncer::PASSWORDS, 314 SyncDataList(1, SyncDataFromPassword(form)), 315 processor_.PassAs<syncer::SyncChangeProcessor>(), 316 scoped_ptr<syncer::SyncErrorFactory>()); 317 } 318 319 // Both passwords db and sync have the same data but they need to be merged 320 // as some fields of the data differ. 321 TEST_F(PasswordSyncableServiceTest, Merge) { 322 autofill::PasswordForm form1; 323 form1.signon_realm = kSignonRealm; 324 form1.action = GURL("http://pie.com"); 325 form1.date_created = base::Time::Now(); 326 form1.preferred = true; 327 328 autofill::PasswordForm form2(form1); 329 form2.preferred = false; 330 EXPECT_CALL(*password_store(), FillAutofillableLogins(_)) 331 .WillOnce(AppendForm(form1)); 332 EXPECT_CALL(*password_store(), FillBlacklistLogins(_)) 333 .WillOnce(Return(true)); 334 EXPECT_CALL(*password_store(), UpdateLoginImpl(PasswordIs(form2))); 335 EXPECT_CALL(*processor_, ProcessSyncChanges(_, IsEmpty())); 336 337 service()->MergeDataAndStartSyncing( 338 syncer::PASSWORDS, 339 SyncDataList(1, SyncDataFromPassword(form2)), 340 processor_.PassAs<syncer::SyncChangeProcessor>(), 341 scoped_ptr<syncer::SyncErrorFactory>()); 342 } 343 344 // Initiate sync due to local DB changes. 345 TEST_F(PasswordSyncableServiceTest, PasswordStoreChanges) { 346 // Save the reference to the processor because |processor_| is NULL after 347 // MergeDataAndStartSyncing(). 348 MockSyncChangeProcessor& weak_processor = *processor_; 349 EXPECT_CALL(weak_processor, ProcessSyncChanges(_, IsEmpty())); 350 EXPECT_CALL(*password_store(), FillAutofillableLogins(_)) 351 .WillOnce(Return(true)); 352 EXPECT_CALL(*password_store(), FillBlacklistLogins(_)) 353 .WillOnce(Return(true)); 354 service()->MergeDataAndStartSyncing( 355 syncer::PASSWORDS, 356 SyncDataList(), 357 processor_.PassAs<syncer::SyncChangeProcessor>(), 358 scoped_ptr<syncer::SyncErrorFactory>()); 359 360 autofill::PasswordForm form1; 361 form1.signon_realm = kSignonRealm; 362 autofill::PasswordForm form2; 363 form2.signon_realm = kSignonRealm2; 364 autofill::PasswordForm form3; 365 form3.signon_realm = kSignonRealm3; 366 367 SyncChangeList sync_list; 368 sync_list.push_back(CreateSyncChange(form1, SyncChange::ACTION_ADD)); 369 sync_list.push_back(CreateSyncChange(form2, SyncChange::ACTION_UPDATE)); 370 sync_list.push_back(CreateSyncChange(form3, SyncChange::ACTION_DELETE)); 371 372 PasswordStoreChangeList list; 373 list.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form1)); 374 list.push_back(PasswordStoreChange(PasswordStoreChange::UPDATE, form2)); 375 list.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, form3)); 376 EXPECT_CALL(weak_processor, ProcessSyncChanges(_, ElementsAre( 377 SyncChangeIs(SyncChange::ACTION_ADD, form1), 378 SyncChangeIs(SyncChange::ACTION_UPDATE, form2), 379 SyncChangeIs(SyncChange::ACTION_DELETE, form3)))); 380 service()->ActOnPasswordStoreChanges(list); 381 } 382 383 // Process all types of changes from sync. 384 TEST_F(PasswordSyncableServiceTest, ProcessSyncChanges) { 385 autofill::PasswordForm updated_form; 386 updated_form.signon_realm = kSignonRealm; 387 updated_form.action = GURL("http://foo.com"); 388 updated_form.date_created = base::Time::Now(); 389 autofill::PasswordForm deleted_form; 390 deleted_form.signon_realm = kSignonRealm2; 391 deleted_form.action = GURL("http://bar.com"); 392 deleted_form.blacklisted_by_user = true; 393 394 SyncData add_data = CreateSyncData(kSignonRealm3); 395 autofill::PasswordForm new_from_sync = PasswordFromSpecifics( 396 GetPasswordSpecifics(add_data)); 397 398 SyncChangeList list; 399 list.push_back(SyncChange(FROM_HERE, 400 syncer::SyncChange::ACTION_ADD, 401 add_data)); 402 list.push_back(CreateSyncChange(updated_form, 403 syncer::SyncChange::ACTION_UPDATE)); 404 list.push_back(CreateSyncChange(deleted_form, 405 syncer::SyncChange::ACTION_DELETE)); 406 EXPECT_CALL(*password_store(), AddLoginImpl(PasswordIs(new_from_sync))); 407 EXPECT_CALL(*password_store(), UpdateLoginImpl(PasswordIs(updated_form))); 408 EXPECT_CALL(*password_store(), RemoveLoginImpl(PasswordIs(deleted_form))); 409 service()->ProcessSyncChanges(FROM_HERE, list); 410 } 411 412 // Retrives sync data from the model. 413 TEST_F(PasswordSyncableServiceTest, GetAllSyncData) { 414 autofill::PasswordForm form1; 415 form1.signon_realm = kSignonRealm; 416 form1.action = GURL("http://foo.com"); 417 form1.times_used = kTimesUsed; 418 form1.type = kArbitraryType; 419 form1.display_name = base::ASCIIToUTF16(kDisplayName); 420 form1.avatar_url = GURL(kAvatarUrl); 421 form1.federation_url = GURL(kFederationUrl); 422 autofill::PasswordForm form2; 423 form2.signon_realm = kSignonRealm2; 424 form2.action = GURL("http://bar.com"); 425 form2.blacklisted_by_user = true; 426 EXPECT_CALL(*password_store(), FillAutofillableLogins(_)) 427 .WillOnce(AppendForm(form1)); 428 EXPECT_CALL(*password_store(), FillBlacklistLogins(_)) 429 .WillOnce(AppendForm(form2)); 430 431 SyncDataList actual_list = service()->GetAllSyncData(syncer::PASSWORDS); 432 std::vector<autofill::PasswordForm> actual_form_list; 433 for (SyncDataList::iterator it = actual_list.begin(); 434 it != actual_list.end(); ++it) { 435 actual_form_list.push_back( 436 PasswordFromSpecifics(GetPasswordSpecifics(*it))); 437 } 438 EXPECT_THAT(actual_form_list, UnorderedElementsAre(PasswordIs(form1), 439 PasswordIs(form2))); 440 } 441 442 // Creates 2 PasswordSyncableService instances, merges the content of the first 443 // one to the second one and back. 444 TEST_F(PasswordSyncableServiceTest, MergeDataAndPushBack) { 445 autofill::PasswordForm form1; 446 form1.signon_realm = kSignonRealm; 447 form1.action = GURL("http://foo.com"); 448 449 PasswordSyncableServiceWrapper other_service_wrapper; 450 autofill::PasswordForm form2; 451 form2.signon_realm = kSignonRealm2; 452 form2.action = GURL("http://bar.com"); 453 EXPECT_CALL(*password_store(), FillAutofillableLogins(_)) 454 .WillOnce(AppendForm(form1)); 455 EXPECT_CALL(*password_store(), FillBlacklistLogins(_)) 456 .WillOnce(Return(true)); 457 EXPECT_CALL(*other_service_wrapper.password_store(), 458 FillAutofillableLogins(_)).WillOnce(AppendForm(form2)); 459 EXPECT_CALL(*other_service_wrapper.password_store(), 460 FillBlacklistLogins(_)).WillOnce(Return(true)); 461 462 EXPECT_CALL(*password_store(), AddLoginImpl(PasswordIs(form2))); 463 EXPECT_CALL(*other_service_wrapper.password_store(), 464 AddLoginImpl(PasswordIs(form1))); 465 466 syncer::SyncDataList other_service_data = 467 other_service_wrapper.service()->GetAllSyncData(syncer::PASSWORDS); 468 service()->MergeDataAndStartSyncing( 469 syncer::PASSWORDS, 470 other_service_data, 471 other_service_wrapper.ReleaseSyncableService(), 472 scoped_ptr<syncer::SyncErrorFactory>()); 473 } 474 475 // Calls ActOnPasswordStoreChanges without SyncChangeProcessor. StartSyncFlare 476 // should be called. 477 TEST_F(PasswordSyncableServiceTest, StartSyncFlare) { 478 autofill::PasswordForm form; 479 form.signon_realm = kSignonRealm; 480 PasswordStoreChangeList list; 481 list.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form)); 482 483 // No flare and no SyncChangeProcessor, the call shouldn't crash. 484 service()->ActOnPasswordStoreChanges(list); 485 486 // Set the flare. It should be called as there is no SyncChangeProcessor. 487 service()->InjectStartSyncFlare( 488 base::Bind(&MockPasswordSyncableService::StartSyncFlare, 489 base::Unretained(service()))); 490 EXPECT_CALL(*service(), StartSyncFlare(syncer::PASSWORDS)); 491 service()->ActOnPasswordStoreChanges(list); 492 } 493 494 // Start syncing with an error. Subsequent password store updates shouldn't be 495 // propagated to Sync. 496 TEST_F(PasswordSyncableServiceTest, FailedReadFromPasswordStore) { 497 scoped_ptr<syncer::SyncErrorFactoryMock> error_factory( 498 new syncer::SyncErrorFactoryMock); 499 EXPECT_CALL(*password_store(), FillAutofillableLogins(_)) 500 .WillOnce(Return(false)); 501 EXPECT_CALL(*error_factory, CreateAndUploadError(_, _)) 502 .WillOnce(Return(SyncError())); 503 // ActOnPasswordStoreChanges() below shouldn't generate any changes for Sync. 504 // |processor_| will be destroyed in MergeDataAndStartSyncing(). 505 EXPECT_CALL(*processor_, ProcessSyncChanges(_, _)).Times(0); 506 service()->MergeDataAndStartSyncing( 507 syncer::PASSWORDS, 508 syncer::SyncDataList(), 509 processor_.PassAs<syncer::SyncChangeProcessor>(), 510 error_factory.PassAs<syncer::SyncErrorFactory>()); 511 512 autofill::PasswordForm form; 513 form.signon_realm = kSignonRealm; 514 PasswordStoreChangeList list; 515 list.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form)); 516 service()->ActOnPasswordStoreChanges(list); 517 } 518 519 } // namespace 520 521 } // namespace password_manager 522