Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / prefs / profile_pref_store_manager_unittest.cc
blob5d633bc6556c968e64f94ea633384eed8666ef11
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.
5 #include "chrome/browser/prefs/profile_pref_store_manager.h"
7 #include <vector>
9 #include "base/compiler_specific.h"
10 #include "base/files/file_enumerator.h"
11 #include "base/files/file_util.h"
12 #include "base/files/scoped_temp_dir.h"
13 #include "base/memory/ref_counted.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/prefs/json_pref_store.h"
16 #include "base/prefs/persistent_pref_store.h"
17 #include "base/prefs/pref_service.h"
18 #include "base/prefs/pref_service_factory.h"
19 #include "base/prefs/pref_store.h"
20 #include "base/prefs/testing_pref_service.h"
21 #include "base/run_loop.h"
22 #include "base/strings/string_util.h"
23 #include "base/values.h"
24 #include "components/pref_registry/pref_registry_syncable.h"
25 #include "components/user_prefs/tracked/mock_validation_delegate.h"
26 #include "components/user_prefs/tracked/pref_hash_filter.h"
27 #include "components/user_prefs/tracked/pref_names.h"
28 #include "components/user_prefs/tracked/pref_service_hash_store_contents.h"
29 #include "testing/gtest/include/gtest/gtest.h"
31 namespace {
33 class FirstEqualsPredicate {
34 public:
35 explicit FirstEqualsPredicate(const std::string& expected)
36 : expected_(expected) {}
37 bool operator()(const std::pair<std::string, base::Value*>& pair) {
38 return pair.first == expected_;
41 private:
42 const std::string expected_;
45 // Observes changes to the PrefStore and verifies that only registered prefs are
46 // written.
47 class RegistryVerifier : public PrefStore::Observer {
48 public:
49 explicit RegistryVerifier(PrefRegistry* pref_registry)
50 : pref_registry_(pref_registry) {}
52 // PrefStore::Observer implementation
53 void OnPrefValueChanged(const std::string& key) override {
54 EXPECT_TRUE(pref_registry_->end() !=
55 std::find_if(pref_registry_->begin(),
56 pref_registry_->end(),
57 FirstEqualsPredicate(key)))
58 << "Unregistered key " << key << " was changed.";
61 void OnInitializationCompleted(bool succeeded) override {}
63 private:
64 scoped_refptr<PrefRegistry> pref_registry_;
67 const char kUnprotectedPref[] = "unprotected_pref";
68 const char kTrackedAtomic[] = "tracked_atomic";
69 const char kProtectedAtomic[] = "protected_atomic";
71 const char kFoobar[] = "FOOBAR";
72 const char kBarfoo[] = "BARFOO";
73 const char kHelloWorld[] = "HELLOWORLD";
74 const char kGoodbyeWorld[] = "GOODBYEWORLD";
76 const PrefHashFilter::TrackedPreferenceMetadata kConfiguration[] = {
77 {0u, kTrackedAtomic, PrefHashFilter::NO_ENFORCEMENT,
78 PrefHashFilter::TRACKING_STRATEGY_ATOMIC},
79 {1u, kProtectedAtomic, PrefHashFilter::ENFORCE_ON_LOAD,
80 PrefHashFilter::TRACKING_STRATEGY_ATOMIC}};
82 const size_t kExtraReportingId = 2u;
83 const size_t kReportingIdCount = 3u;
85 } // namespace
87 class ProfilePrefStoreManagerTest : public testing::Test {
88 public:
89 ProfilePrefStoreManagerTest()
90 : configuration_(kConfiguration,
91 kConfiguration + arraysize(kConfiguration)),
92 profile_pref_registry_(new user_prefs::PrefRegistrySyncable),
93 registry_verifier_(profile_pref_registry_.get()),
94 seed_("seed"),
95 reset_recorded_(false) {}
97 void SetUp() override {
98 ProfilePrefStoreManager::RegisterPrefs(local_state_.registry());
99 ProfilePrefStoreManager::RegisterProfilePrefs(profile_pref_registry_.get());
100 for (const PrefHashFilter::TrackedPreferenceMetadata* it = kConfiguration;
101 it != kConfiguration + arraysize(kConfiguration);
102 ++it) {
103 if (it->strategy == PrefHashFilter::TRACKING_STRATEGY_ATOMIC) {
104 profile_pref_registry_->RegisterStringPref(it->name, std::string());
105 } else {
106 profile_pref_registry_->RegisterDictionaryPref(it->name);
109 profile_pref_registry_->RegisterStringPref(kUnprotectedPref, std::string());
111 // As in chrome_pref_service_factory.cc, kPreferencesResetTime needs to be
112 // declared as protected in order to be read from the proper store by the
113 // SegregatedPrefStore. Only declare it after configured prefs have been
114 // registered above for this test as kPreferenceResetTime is already
115 // registered in ProfilePrefStoreManager::RegisterProfilePrefs.
116 PrefHashFilter::TrackedPreferenceMetadata pref_reset_time_config =
117 {configuration_.rbegin()->reporting_id + 1,
118 user_prefs::kPreferenceResetTime,
119 PrefHashFilter::ENFORCE_ON_LOAD,
120 PrefHashFilter::TRACKING_STRATEGY_ATOMIC};
121 configuration_.push_back(pref_reset_time_config);
123 ASSERT_TRUE(profile_dir_.CreateUniqueTempDir());
124 ReloadConfiguration();
127 void ReloadConfiguration() {
128 manager_.reset(new ProfilePrefStoreManager(profile_dir_.path(),
129 configuration_,
130 kReportingIdCount,
131 seed_,
132 "device_id",
133 &local_state_));
136 void TearDown() override { DestroyPrefStore(); }
138 protected:
139 // Verifies whether a reset was reported via the RecordReset() hook. Also
140 // verifies that GetResetTime() was set (or not) accordingly.
141 void VerifyResetRecorded(bool reset_expected) {
142 EXPECT_EQ(reset_expected, reset_recorded_);
144 base::PrefServiceFactory pref_service_factory;
145 pref_service_factory.set_user_prefs(pref_store_);
147 scoped_ptr<PrefService> pref_service(
148 pref_service_factory.Create(profile_pref_registry_.get()));
150 EXPECT_EQ(
151 reset_expected,
152 !ProfilePrefStoreManager::GetResetTime(pref_service.get()).is_null());
155 void ClearResetRecorded() {
156 reset_recorded_ = false;
158 base::PrefServiceFactory pref_service_factory;
159 pref_service_factory.set_user_prefs(pref_store_);
161 scoped_ptr<PrefService> pref_service(
162 pref_service_factory.Create(profile_pref_registry_.get()));
164 ProfilePrefStoreManager::ClearResetTime(pref_service.get());
167 void InitializePrefs() {
168 // According to the implementation of ProfilePrefStoreManager, this is
169 // actually a SegregatedPrefStore backed by two underlying pref stores.
170 scoped_refptr<PersistentPrefStore> pref_store =
171 manager_->CreateProfilePrefStore(
172 main_message_loop_.task_runner(),
173 base::Bind(&ProfilePrefStoreManagerTest::RecordReset,
174 base::Unretained(this)),
175 &mock_validation_delegate_);
176 InitializePrefStore(pref_store.get());
177 pref_store = NULL;
178 base::RunLoop().RunUntilIdle();
181 void DestroyPrefStore() {
182 if (pref_store_.get()) {
183 ClearResetRecorded();
184 // Force everything to be written to disk, triggering the PrefHashFilter
185 // while our RegistryVerifier is watching.
186 pref_store_->CommitPendingWrite();
187 base::RunLoop().RunUntilIdle();
189 pref_store_->RemoveObserver(&registry_verifier_);
190 pref_store_ = NULL;
191 // Nothing should have to happen on the background threads, but just in
192 // case...
193 base::RunLoop().RunUntilIdle();
197 void InitializeDeprecatedCombinedProfilePrefStore() {
198 scoped_refptr<PersistentPrefStore> pref_store =
199 manager_->CreateDeprecatedCombinedProfilePrefStore(
200 main_message_loop_.task_runner());
201 InitializePrefStore(pref_store.get());
202 pref_store = NULL;
203 base::RunLoop().RunUntilIdle();
206 void InitializePrefStore(PersistentPrefStore* pref_store) {
207 pref_store->AddObserver(&registry_verifier_);
208 PersistentPrefStore::PrefReadError error = pref_store->ReadPrefs();
209 EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE, error);
210 pref_store->SetValue(kTrackedAtomic,
211 make_scoped_ptr(new base::StringValue(kFoobar)),
212 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
213 pref_store->SetValue(kProtectedAtomic,
214 make_scoped_ptr(new base::StringValue(kHelloWorld)),
215 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
216 pref_store->SetValue(kUnprotectedPref,
217 make_scoped_ptr(new base::StringValue(kFoobar)),
218 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
219 pref_store->RemoveObserver(&registry_verifier_);
220 pref_store->CommitPendingWrite();
221 base::RunLoop().RunUntilIdle();
224 void LoadExistingPrefs() {
225 DestroyPrefStore();
226 pref_store_ = manager_->CreateProfilePrefStore(
227 main_message_loop_.task_runner(),
228 base::Bind(&ProfilePrefStoreManagerTest::RecordReset,
229 base::Unretained(this)),
230 NULL);
231 pref_store_->AddObserver(&registry_verifier_);
232 pref_store_->ReadPrefs();
235 void ReplaceStringInPrefs(const std::string& find,
236 const std::string& replace) {
237 base::FileEnumerator file_enum(
238 profile_dir_.path(), true, base::FileEnumerator::FILES);
240 for (base::FilePath path = file_enum.Next(); !path.empty();
241 path = file_enum.Next()) {
242 // Tamper with the file's contents
243 std::string contents;
244 EXPECT_TRUE(base::ReadFileToString(path, &contents));
245 base::ReplaceSubstringsAfterOffset(&contents, 0u, find, replace);
246 EXPECT_EQ(static_cast<int>(contents.length()),
247 base::WriteFile(path, contents.c_str(), contents.length()));
251 void ExpectStringValueEquals(const std::string& name,
252 const std::string& expected) {
253 const base::Value* value = NULL;
254 std::string as_string;
255 if (!pref_store_->GetValue(name, &value)) {
256 ADD_FAILURE() << name << " is not a defined value.";
257 } else if (!value->GetAsString(&as_string)) {
258 ADD_FAILURE() << name << " could not be coerced to a string.";
259 } else {
260 EXPECT_EQ(expected, as_string);
264 void ExpectValidationObserved(const std::string& pref_path) {
265 // No validations are expected for platforms that do not support tracking.
266 if (!ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking)
267 return;
268 if (!mock_validation_delegate_.GetEventForPath(pref_path))
269 ADD_FAILURE() << "No validation observed for preference: " << pref_path;
272 base::MessageLoop main_message_loop_;
273 std::vector<PrefHashFilter::TrackedPreferenceMetadata> configuration_;
274 base::ScopedTempDir profile_dir_;
275 TestingPrefServiceSimple local_state_;
276 scoped_refptr<user_prefs::PrefRegistrySyncable> profile_pref_registry_;
277 RegistryVerifier registry_verifier_;
278 MockValidationDelegate mock_validation_delegate_;
279 scoped_ptr<ProfilePrefStoreManager> manager_;
280 scoped_refptr<PersistentPrefStore> pref_store_;
282 std::string seed_;
284 private:
285 void RecordReset() {
286 // As-is |reset_recorded_| is only designed to remember a single reset, make
287 // sure none was previously recorded (or that ClearResetRecorded() was
288 // called).
289 EXPECT_FALSE(reset_recorded_);
290 reset_recorded_ = true;
293 bool reset_recorded_;
296 TEST_F(ProfilePrefStoreManagerTest, StoreValues) {
297 InitializePrefs();
299 LoadExistingPrefs();
301 ExpectStringValueEquals(kTrackedAtomic, kFoobar);
302 ExpectStringValueEquals(kProtectedAtomic, kHelloWorld);
303 VerifyResetRecorded(false);
304 ExpectValidationObserved(kTrackedAtomic);
305 ExpectValidationObserved(kProtectedAtomic);
308 TEST_F(ProfilePrefStoreManagerTest, GetPrefFilePathFromProfilePath) {
309 base::FilePath pref_file_path =
310 ProfilePrefStoreManager::GetPrefFilePathFromProfilePath(
311 profile_dir_.path());
313 EXPECT_FALSE(base::PathExists(pref_file_path));
315 InitializePrefs();
317 EXPECT_TRUE(base::PathExists(pref_file_path));
320 TEST_F(ProfilePrefStoreManagerTest, ProtectValues) {
321 InitializePrefs();
323 ReplaceStringInPrefs(kFoobar, kBarfoo);
324 ReplaceStringInPrefs(kHelloWorld, kGoodbyeWorld);
326 LoadExistingPrefs();
328 // kTrackedAtomic is unprotected and thus will be loaded as it appears on
329 // disk.
330 ExpectStringValueEquals(kTrackedAtomic, kBarfoo);
332 // If preference tracking is supported, the tampered value of kProtectedAtomic
333 // will be discarded at load time, leaving this preference undefined.
334 EXPECT_NE(ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
335 pref_store_->GetValue(kProtectedAtomic, NULL));
336 VerifyResetRecorded(
337 ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking);
339 ExpectValidationObserved(kTrackedAtomic);
340 ExpectValidationObserved(kProtectedAtomic);
343 TEST_F(ProfilePrefStoreManagerTest, MigrateFromOneFile) {
344 InitializeDeprecatedCombinedProfilePrefStore();
346 // The deprecated model stores hashes in local state (on supported
347 // platforms)..
348 ASSERT_EQ(
349 ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
350 local_state_.GetUserPrefValue(
351 PrefServiceHashStoreContents::kProfilePreferenceHashes) != NULL);
353 LoadExistingPrefs();
355 // After a first migration, the hashes were copied to the two user preference
356 // files but were not cleaned.
357 ASSERT_EQ(
358 ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
359 local_state_.GetUserPrefValue(
360 PrefServiceHashStoreContents::kProfilePreferenceHashes) != NULL);
362 ExpectStringValueEquals(kTrackedAtomic, kFoobar);
363 ExpectStringValueEquals(kProtectedAtomic, kHelloWorld);
364 VerifyResetRecorded(false);
366 LoadExistingPrefs();
368 // In a subsequent launch, the local state hash store should be reset.
369 ASSERT_FALSE(local_state_.GetUserPrefValue(
370 PrefServiceHashStoreContents::kProfilePreferenceHashes));
372 ExpectStringValueEquals(kTrackedAtomic, kFoobar);
373 ExpectStringValueEquals(kProtectedAtomic, kHelloWorld);
374 VerifyResetRecorded(false);
377 TEST_F(ProfilePrefStoreManagerTest, MigrateWithTampering) {
378 InitializeDeprecatedCombinedProfilePrefStore();
380 ReplaceStringInPrefs(kFoobar, kBarfoo);
381 ReplaceStringInPrefs(kHelloWorld, kGoodbyeWorld);
383 // The deprecated model stores hashes in local state (on supported
384 // platforms)..
385 ASSERT_EQ(
386 ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
387 local_state_.GetUserPrefValue(
388 PrefServiceHashStoreContents::kProfilePreferenceHashes) != NULL);
390 LoadExistingPrefs();
392 // After a first migration, the hashes were copied to the two user preference
393 // files but were not cleaned.
394 ASSERT_EQ(
395 ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
396 local_state_.GetUserPrefValue(
397 PrefServiceHashStoreContents::kProfilePreferenceHashes) != NULL);
399 // kTrackedAtomic is unprotected and thus will be loaded as it appears on
400 // disk.
401 ExpectStringValueEquals(kTrackedAtomic, kBarfoo);
403 // If preference tracking is supported, the tampered value of kProtectedAtomic
404 // will be discarded at load time, leaving this preference undefined.
405 EXPECT_NE(ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
406 pref_store_->GetValue(kProtectedAtomic, NULL));
407 VerifyResetRecorded(
408 ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking);
410 LoadExistingPrefs();
412 // In a subsequent launch, the local state hash store would be reset.
413 ASSERT_FALSE(local_state_.GetUserPrefValue(
414 PrefServiceHashStoreContents::kProfilePreferenceHashes));
416 ExpectStringValueEquals(kTrackedAtomic, kBarfoo);
417 VerifyResetRecorded(false);
420 TEST_F(ProfilePrefStoreManagerTest, InitializePrefsFromMasterPrefs) {
421 base::DictionaryValue master_prefs;
422 master_prefs.Set(kTrackedAtomic, new base::StringValue(kFoobar));
423 master_prefs.Set(kProtectedAtomic, new base::StringValue(kHelloWorld));
424 EXPECT_TRUE(manager_->InitializePrefsFromMasterPrefs(master_prefs));
426 LoadExistingPrefs();
428 // Verify that InitializePrefsFromMasterPrefs correctly applied the MACs
429 // necessary to authenticate these values.
430 ExpectStringValueEquals(kTrackedAtomic, kFoobar);
431 ExpectStringValueEquals(kProtectedAtomic, kHelloWorld);
432 VerifyResetRecorded(false);
435 TEST_F(ProfilePrefStoreManagerTest, UnprotectedToProtected) {
436 InitializePrefs();
438 ExpectValidationObserved(kTrackedAtomic);
439 ExpectValidationObserved(kProtectedAtomic);
441 LoadExistingPrefs();
442 ExpectStringValueEquals(kUnprotectedPref, kFoobar);
444 // Ensure everything is written out to disk.
445 DestroyPrefStore();
447 ReplaceStringInPrefs(kFoobar, kBarfoo);
449 // It's unprotected, so we can load the modified value.
450 LoadExistingPrefs();
451 ExpectStringValueEquals(kUnprotectedPref, kBarfoo);
453 // Now update the configuration to protect it.
454 PrefHashFilter::TrackedPreferenceMetadata new_protected = {
455 kExtraReportingId, kUnprotectedPref, PrefHashFilter::ENFORCE_ON_LOAD,
456 PrefHashFilter::TRACKING_STRATEGY_ATOMIC};
457 configuration_.push_back(new_protected);
458 ReloadConfiguration();
460 // And try loading with the new configuration.
461 LoadExistingPrefs();
463 // Since there was a valid super MAC we were able to extend the existing trust
464 // to the newly protected preference.
465 ExpectStringValueEquals(kUnprotectedPref, kBarfoo);
466 VerifyResetRecorded(false);
468 // Ensure everything is written out to disk.
469 DestroyPrefStore();
471 // It's protected now, so (if the platform supports it) any tampering should
472 // lead to a reset.
473 ReplaceStringInPrefs(kBarfoo, kFoobar);
474 LoadExistingPrefs();
475 EXPECT_NE(ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
476 pref_store_->GetValue(kUnprotectedPref, NULL));
477 VerifyResetRecorded(
478 ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking);
481 TEST_F(ProfilePrefStoreManagerTest, NewPrefWhenFirstProtecting) {
482 std::vector<PrefHashFilter::TrackedPreferenceMetadata>
483 original_configuration = configuration_;
484 for (std::vector<PrefHashFilter::TrackedPreferenceMetadata>::iterator it =
485 configuration_.begin();
486 it != configuration_.end();
487 ++it) {
488 it->enforcement_level = PrefHashFilter::NO_ENFORCEMENT;
490 ReloadConfiguration();
492 InitializePrefs();
494 ExpectValidationObserved(kTrackedAtomic);
495 ExpectValidationObserved(kProtectedAtomic);
497 LoadExistingPrefs();
498 ExpectStringValueEquals(kUnprotectedPref, kFoobar);
500 // Ensure everything is written out to disk.
501 DestroyPrefStore();
503 // Now introduce protection, including the never-before tracked "new_pref".
504 configuration_ = original_configuration;
505 PrefHashFilter::TrackedPreferenceMetadata new_protected = {
506 kExtraReportingId, kUnprotectedPref, PrefHashFilter::ENFORCE_ON_LOAD,
507 PrefHashFilter::TRACKING_STRATEGY_ATOMIC};
508 configuration_.push_back(new_protected);
509 ReloadConfiguration();
511 // And try loading with the new configuration.
512 LoadExistingPrefs();
514 // Since there was a valid super MAC we were able to extend the existing trust
515 // to the newly tracked & protected preference.
516 ExpectStringValueEquals(kUnprotectedPref, kFoobar);
517 VerifyResetRecorded(false);
520 TEST_F(ProfilePrefStoreManagerTest, UnprotectedToProtectedWithoutTrust) {
521 InitializePrefs();
523 ExpectValidationObserved(kTrackedAtomic);
524 ExpectValidationObserved(kProtectedAtomic);
526 // Now update the configuration to protect it.
527 PrefHashFilter::TrackedPreferenceMetadata new_protected = {
528 kExtraReportingId, kUnprotectedPref, PrefHashFilter::ENFORCE_ON_LOAD,
529 PrefHashFilter::TRACKING_STRATEGY_ATOMIC};
530 configuration_.push_back(new_protected);
531 seed_ = "new-seed-to-break-trust";
532 ReloadConfiguration();
534 // And try loading with the new configuration.
535 LoadExistingPrefs();
537 // If preference tracking is supported, kUnprotectedPref will have been
538 // discarded because new values are not accepted without a valid super MAC.
539 EXPECT_NE(ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
540 pref_store_->GetValue(kUnprotectedPref, NULL));
541 VerifyResetRecorded(
542 ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking);
545 // This test verifies that preference values are correctly maintained when a
546 // preference's protection state changes from protected to unprotected.
547 TEST_F(ProfilePrefStoreManagerTest, ProtectedToUnprotected) {
548 InitializePrefs();
550 ExpectValidationObserved(kTrackedAtomic);
551 ExpectValidationObserved(kProtectedAtomic);
553 DestroyPrefStore();
555 // Unconfigure protection for kProtectedAtomic
556 for (std::vector<PrefHashFilter::TrackedPreferenceMetadata>::iterator it =
557 configuration_.begin();
558 it != configuration_.end();
559 ++it) {
560 if (it->name == kProtectedAtomic) {
561 it->enforcement_level = PrefHashFilter::NO_ENFORCEMENT;
562 break;
566 seed_ = "new-seed-to-break-trust";
567 ReloadConfiguration();
568 LoadExistingPrefs();
570 // Verify that the value was not reset.
571 ExpectStringValueEquals(kProtectedAtomic, kHelloWorld);
572 VerifyResetRecorded(false);
574 // Accessing the value of the previously protected pref didn't trigger its
575 // move to the unprotected preferences file, though the loading of the pref
576 // store should still have caused the MAC store to be recalculated.
577 LoadExistingPrefs();
578 ExpectStringValueEquals(kProtectedAtomic, kHelloWorld);
580 // Trigger the logic that migrates it back to the unprotected preferences
581 // file.
582 pref_store_->SetValue(kProtectedAtomic,
583 make_scoped_ptr(new base::StringValue(kGoodbyeWorld)),
584 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
585 LoadExistingPrefs();
586 ExpectStringValueEquals(kProtectedAtomic, kGoodbyeWorld);
587 VerifyResetRecorded(false);