[sql] Remove _HAS_EXCEPTIONS=0 from build info.
[chromium-blink-merge.git] / chrome / browser / prefs / profile_pref_store_manager_unittest.cc
blob83a9f3d94df1a83d38fd0e084ee147ec2592fdc7
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 "chrome/browser/prefs/tracked/mock_validation_delegate.h"
25 #include "chrome/browser/prefs/tracked/pref_hash_filter.h"
26 #include "chrome/browser/prefs/tracked/pref_service_hash_store_contents.h"
27 #include "chrome/common/pref_names.h"
28 #include "components/pref_registry/pref_registry_syncable.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, prefs::kPreferenceResetTime,
118 PrefHashFilter::ENFORCE_ON_LOAD,
119 PrefHashFilter::TRACKING_STRATEGY_ATOMIC};
120 configuration_.push_back(pref_reset_time_config);
122 ASSERT_TRUE(profile_dir_.CreateUniqueTempDir());
123 ReloadConfiguration();
126 void ReloadConfiguration() {
127 manager_.reset(new ProfilePrefStoreManager(profile_dir_.path(),
128 configuration_,
129 kReportingIdCount,
130 seed_,
131 "device_id",
132 &local_state_));
135 void TearDown() override { DestroyPrefStore(); }
137 protected:
138 // Verifies whether a reset was reported via the RecordReset() hook. Also
139 // verifies that GetResetTime() was set (or not) accordingly.
140 void VerifyResetRecorded(bool reset_expected) {
141 EXPECT_EQ(reset_expected, reset_recorded_);
143 base::PrefServiceFactory pref_service_factory;
144 pref_service_factory.set_user_prefs(pref_store_);
146 scoped_ptr<PrefService> pref_service(
147 pref_service_factory.Create(profile_pref_registry_.get()));
149 EXPECT_EQ(
150 reset_expected,
151 !ProfilePrefStoreManager::GetResetTime(pref_service.get()).is_null());
154 void ClearResetRecorded() {
155 reset_recorded_ = false;
157 base::PrefServiceFactory pref_service_factory;
158 pref_service_factory.set_user_prefs(pref_store_);
160 scoped_ptr<PrefService> pref_service(
161 pref_service_factory.Create(profile_pref_registry_.get()));
163 ProfilePrefStoreManager::ClearResetTime(pref_service.get());
166 void InitializePrefs() {
167 // According to the implementation of ProfilePrefStoreManager, this is
168 // actually a SegregatedPrefStore backed by two underlying pref stores.
169 scoped_refptr<PersistentPrefStore> pref_store =
170 manager_->CreateProfilePrefStore(
171 main_message_loop_.task_runner(),
172 base::Bind(&ProfilePrefStoreManagerTest::RecordReset,
173 base::Unretained(this)),
174 &mock_validation_delegate_);
175 InitializePrefStore(pref_store.get());
176 pref_store = NULL;
177 base::RunLoop().RunUntilIdle();
180 void DestroyPrefStore() {
181 if (pref_store_.get()) {
182 ClearResetRecorded();
183 // Force everything to be written to disk, triggering the PrefHashFilter
184 // while our RegistryVerifier is watching.
185 pref_store_->CommitPendingWrite();
186 base::RunLoop().RunUntilIdle();
188 pref_store_->RemoveObserver(&registry_verifier_);
189 pref_store_ = NULL;
190 // Nothing should have to happen on the background threads, but just in
191 // case...
192 base::RunLoop().RunUntilIdle();
196 void InitializeDeprecatedCombinedProfilePrefStore() {
197 scoped_refptr<PersistentPrefStore> pref_store =
198 manager_->CreateDeprecatedCombinedProfilePrefStore(
199 main_message_loop_.task_runner());
200 InitializePrefStore(pref_store.get());
201 pref_store = NULL;
202 base::RunLoop().RunUntilIdle();
205 void InitializePrefStore(PersistentPrefStore* pref_store) {
206 pref_store->AddObserver(&registry_verifier_);
207 PersistentPrefStore::PrefReadError error = pref_store->ReadPrefs();
208 EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE, error);
209 pref_store->SetValue(kTrackedAtomic,
210 make_scoped_ptr(new base::StringValue(kFoobar)),
211 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
212 pref_store->SetValue(kProtectedAtomic,
213 make_scoped_ptr(new base::StringValue(kHelloWorld)),
214 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
215 pref_store->SetValue(kUnprotectedPref,
216 make_scoped_ptr(new base::StringValue(kFoobar)),
217 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
218 pref_store->RemoveObserver(&registry_verifier_);
219 pref_store->CommitPendingWrite();
220 base::RunLoop().RunUntilIdle();
223 void LoadExistingPrefs() {
224 DestroyPrefStore();
225 pref_store_ = manager_->CreateProfilePrefStore(
226 main_message_loop_.task_runner(),
227 base::Bind(&ProfilePrefStoreManagerTest::RecordReset,
228 base::Unretained(this)),
229 NULL);
230 pref_store_->AddObserver(&registry_verifier_);
231 pref_store_->ReadPrefs();
234 void ReplaceStringInPrefs(const std::string& find,
235 const std::string& replace) {
236 base::FileEnumerator file_enum(
237 profile_dir_.path(), true, base::FileEnumerator::FILES);
239 for (base::FilePath path = file_enum.Next(); !path.empty();
240 path = file_enum.Next()) {
241 // Tamper with the file's contents
242 std::string contents;
243 EXPECT_TRUE(base::ReadFileToString(path, &contents));
244 base::ReplaceSubstringsAfterOffset(&contents, 0u, find, replace);
245 EXPECT_EQ(static_cast<int>(contents.length()),
246 base::WriteFile(path, contents.c_str(), contents.length()));
250 void ExpectStringValueEquals(const std::string& name,
251 const std::string& expected) {
252 const base::Value* value = NULL;
253 std::string as_string;
254 if (!pref_store_->GetValue(name, &value)) {
255 ADD_FAILURE() << name << " is not a defined value.";
256 } else if (!value->GetAsString(&as_string)) {
257 ADD_FAILURE() << name << " could not be coerced to a string.";
258 } else {
259 EXPECT_EQ(expected, as_string);
263 void ExpectValidationObserved(const std::string& pref_path) {
264 // No validations are expected for platforms that do not support tracking.
265 if (!ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking)
266 return;
267 if (!mock_validation_delegate_.GetEventForPath(pref_path))
268 ADD_FAILURE() << "No validation observed for preference: " << pref_path;
271 base::MessageLoop main_message_loop_;
272 std::vector<PrefHashFilter::TrackedPreferenceMetadata> configuration_;
273 base::ScopedTempDir profile_dir_;
274 TestingPrefServiceSimple local_state_;
275 scoped_refptr<user_prefs::PrefRegistrySyncable> profile_pref_registry_;
276 RegistryVerifier registry_verifier_;
277 MockValidationDelegate mock_validation_delegate_;
278 scoped_ptr<ProfilePrefStoreManager> manager_;
279 scoped_refptr<PersistentPrefStore> pref_store_;
281 std::string seed_;
283 private:
284 void RecordReset() {
285 // As-is |reset_recorded_| is only designed to remember a single reset, make
286 // sure none was previously recorded (or that ClearResetRecorded() was
287 // called).
288 EXPECT_FALSE(reset_recorded_);
289 reset_recorded_ = true;
292 bool reset_recorded_;
295 TEST_F(ProfilePrefStoreManagerTest, StoreValues) {
296 InitializePrefs();
298 LoadExistingPrefs();
300 ExpectStringValueEquals(kTrackedAtomic, kFoobar);
301 ExpectStringValueEquals(kProtectedAtomic, kHelloWorld);
302 VerifyResetRecorded(false);
303 ExpectValidationObserved(kTrackedAtomic);
304 ExpectValidationObserved(kProtectedAtomic);
307 TEST_F(ProfilePrefStoreManagerTest, GetPrefFilePathFromProfilePath) {
308 base::FilePath pref_file_path =
309 ProfilePrefStoreManager::GetPrefFilePathFromProfilePath(
310 profile_dir_.path());
312 EXPECT_FALSE(base::PathExists(pref_file_path));
314 InitializePrefs();
316 EXPECT_TRUE(base::PathExists(pref_file_path));
319 TEST_F(ProfilePrefStoreManagerTest, ProtectValues) {
320 InitializePrefs();
322 ReplaceStringInPrefs(kFoobar, kBarfoo);
323 ReplaceStringInPrefs(kHelloWorld, kGoodbyeWorld);
325 LoadExistingPrefs();
327 // kTrackedAtomic is unprotected and thus will be loaded as it appears on
328 // disk.
329 ExpectStringValueEquals(kTrackedAtomic, kBarfoo);
331 // If preference tracking is supported, the tampered value of kProtectedAtomic
332 // will be discarded at load time, leaving this preference undefined.
333 EXPECT_NE(ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
334 pref_store_->GetValue(kProtectedAtomic, NULL));
335 VerifyResetRecorded(
336 ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking);
338 ExpectValidationObserved(kTrackedAtomic);
339 ExpectValidationObserved(kProtectedAtomic);
342 TEST_F(ProfilePrefStoreManagerTest, MigrateFromOneFile) {
343 InitializeDeprecatedCombinedProfilePrefStore();
345 // The deprecated model stores hashes in local state (on supported
346 // platforms)..
347 ASSERT_EQ(
348 ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
349 local_state_.GetUserPrefValue(
350 PrefServiceHashStoreContents::kProfilePreferenceHashes) != NULL);
352 LoadExistingPrefs();
354 // After a first migration, the hashes were copied to the two user preference
355 // files but were not cleaned.
356 ASSERT_EQ(
357 ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
358 local_state_.GetUserPrefValue(
359 PrefServiceHashStoreContents::kProfilePreferenceHashes) != NULL);
361 ExpectStringValueEquals(kTrackedAtomic, kFoobar);
362 ExpectStringValueEquals(kProtectedAtomic, kHelloWorld);
363 VerifyResetRecorded(false);
365 LoadExistingPrefs();
367 // In a subsequent launch, the local state hash store should be reset.
368 ASSERT_FALSE(local_state_.GetUserPrefValue(
369 PrefServiceHashStoreContents::kProfilePreferenceHashes));
371 ExpectStringValueEquals(kTrackedAtomic, kFoobar);
372 ExpectStringValueEquals(kProtectedAtomic, kHelloWorld);
373 VerifyResetRecorded(false);
376 TEST_F(ProfilePrefStoreManagerTest, MigrateWithTampering) {
377 InitializeDeprecatedCombinedProfilePrefStore();
379 ReplaceStringInPrefs(kFoobar, kBarfoo);
380 ReplaceStringInPrefs(kHelloWorld, kGoodbyeWorld);
382 // The deprecated model stores hashes in local state (on supported
383 // platforms)..
384 ASSERT_EQ(
385 ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
386 local_state_.GetUserPrefValue(
387 PrefServiceHashStoreContents::kProfilePreferenceHashes) != NULL);
389 LoadExistingPrefs();
391 // After a first migration, the hashes were copied to the two user preference
392 // files but were not cleaned.
393 ASSERT_EQ(
394 ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
395 local_state_.GetUserPrefValue(
396 PrefServiceHashStoreContents::kProfilePreferenceHashes) != NULL);
398 // kTrackedAtomic is unprotected and thus will be loaded as it appears on
399 // disk.
400 ExpectStringValueEquals(kTrackedAtomic, kBarfoo);
402 // If preference tracking is supported, the tampered value of kProtectedAtomic
403 // will be discarded at load time, leaving this preference undefined.
404 EXPECT_NE(ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
405 pref_store_->GetValue(kProtectedAtomic, NULL));
406 VerifyResetRecorded(
407 ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking);
409 LoadExistingPrefs();
411 // In a subsequent launch, the local state hash store would be reset.
412 ASSERT_FALSE(local_state_.GetUserPrefValue(
413 PrefServiceHashStoreContents::kProfilePreferenceHashes));
415 ExpectStringValueEquals(kTrackedAtomic, kBarfoo);
416 VerifyResetRecorded(false);
419 TEST_F(ProfilePrefStoreManagerTest, InitializePrefsFromMasterPrefs) {
420 base::DictionaryValue master_prefs;
421 master_prefs.Set(kTrackedAtomic, new base::StringValue(kFoobar));
422 master_prefs.Set(kProtectedAtomic, new base::StringValue(kHelloWorld));
423 EXPECT_TRUE(manager_->InitializePrefsFromMasterPrefs(master_prefs));
425 LoadExistingPrefs();
427 // Verify that InitializePrefsFromMasterPrefs correctly applied the MACs
428 // necessary to authenticate these values.
429 ExpectStringValueEquals(kTrackedAtomic, kFoobar);
430 ExpectStringValueEquals(kProtectedAtomic, kHelloWorld);
431 VerifyResetRecorded(false);
434 TEST_F(ProfilePrefStoreManagerTest, UnprotectedToProtected) {
435 InitializePrefs();
437 ExpectValidationObserved(kTrackedAtomic);
438 ExpectValidationObserved(kProtectedAtomic);
440 LoadExistingPrefs();
441 ExpectStringValueEquals(kUnprotectedPref, kFoobar);
443 // Ensure everything is written out to disk.
444 DestroyPrefStore();
446 ReplaceStringInPrefs(kFoobar, kBarfoo);
448 // It's unprotected, so we can load the modified value.
449 LoadExistingPrefs();
450 ExpectStringValueEquals(kUnprotectedPref, kBarfoo);
452 // Now update the configuration to protect it.
453 PrefHashFilter::TrackedPreferenceMetadata new_protected = {
454 kExtraReportingId, kUnprotectedPref, PrefHashFilter::ENFORCE_ON_LOAD,
455 PrefHashFilter::TRACKING_STRATEGY_ATOMIC};
456 configuration_.push_back(new_protected);
457 ReloadConfiguration();
459 // And try loading with the new configuration.
460 LoadExistingPrefs();
462 // Since there was a valid super MAC we were able to extend the existing trust
463 // to the newly protected preference.
464 ExpectStringValueEquals(kUnprotectedPref, kBarfoo);
465 VerifyResetRecorded(false);
467 // Ensure everything is written out to disk.
468 DestroyPrefStore();
470 // It's protected now, so (if the platform supports it) any tampering should
471 // lead to a reset.
472 ReplaceStringInPrefs(kBarfoo, kFoobar);
473 LoadExistingPrefs();
474 EXPECT_NE(ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
475 pref_store_->GetValue(kUnprotectedPref, NULL));
476 VerifyResetRecorded(
477 ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking);
480 TEST_F(ProfilePrefStoreManagerTest, NewPrefWhenFirstProtecting) {
481 std::vector<PrefHashFilter::TrackedPreferenceMetadata>
482 original_configuration = configuration_;
483 for (std::vector<PrefHashFilter::TrackedPreferenceMetadata>::iterator it =
484 configuration_.begin();
485 it != configuration_.end();
486 ++it) {
487 it->enforcement_level = PrefHashFilter::NO_ENFORCEMENT;
489 ReloadConfiguration();
491 InitializePrefs();
493 ExpectValidationObserved(kTrackedAtomic);
494 ExpectValidationObserved(kProtectedAtomic);
496 LoadExistingPrefs();
497 ExpectStringValueEquals(kUnprotectedPref, kFoobar);
499 // Ensure everything is written out to disk.
500 DestroyPrefStore();
502 // Now introduce protection, including the never-before tracked "new_pref".
503 configuration_ = original_configuration;
504 PrefHashFilter::TrackedPreferenceMetadata new_protected = {
505 kExtraReportingId, kUnprotectedPref, PrefHashFilter::ENFORCE_ON_LOAD,
506 PrefHashFilter::TRACKING_STRATEGY_ATOMIC};
507 configuration_.push_back(new_protected);
508 ReloadConfiguration();
510 // And try loading with the new configuration.
511 LoadExistingPrefs();
513 // Since there was a valid super MAC we were able to extend the existing trust
514 // to the newly tracked & protected preference.
515 ExpectStringValueEquals(kUnprotectedPref, kFoobar);
516 VerifyResetRecorded(false);
519 TEST_F(ProfilePrefStoreManagerTest, UnprotectedToProtectedWithoutTrust) {
520 InitializePrefs();
522 ExpectValidationObserved(kTrackedAtomic);
523 ExpectValidationObserved(kProtectedAtomic);
525 // Now update the configuration to protect it.
526 PrefHashFilter::TrackedPreferenceMetadata new_protected = {
527 kExtraReportingId, kUnprotectedPref, PrefHashFilter::ENFORCE_ON_LOAD,
528 PrefHashFilter::TRACKING_STRATEGY_ATOMIC};
529 configuration_.push_back(new_protected);
530 seed_ = "new-seed-to-break-trust";
531 ReloadConfiguration();
533 // And try loading with the new configuration.
534 LoadExistingPrefs();
536 // If preference tracking is supported, kUnprotectedPref will have been
537 // discarded because new values are not accepted without a valid super MAC.
538 EXPECT_NE(ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
539 pref_store_->GetValue(kUnprotectedPref, NULL));
540 VerifyResetRecorded(
541 ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking);
544 // This test verifies that preference values are correctly maintained when a
545 // preference's protection state changes from protected to unprotected.
546 TEST_F(ProfilePrefStoreManagerTest, ProtectedToUnprotected) {
547 InitializePrefs();
549 ExpectValidationObserved(kTrackedAtomic);
550 ExpectValidationObserved(kProtectedAtomic);
552 DestroyPrefStore();
554 // Unconfigure protection for kProtectedAtomic
555 for (std::vector<PrefHashFilter::TrackedPreferenceMetadata>::iterator it =
556 configuration_.begin();
557 it != configuration_.end();
558 ++it) {
559 if (it->name == kProtectedAtomic) {
560 it->enforcement_level = PrefHashFilter::NO_ENFORCEMENT;
561 break;
565 seed_ = "new-seed-to-break-trust";
566 ReloadConfiguration();
567 LoadExistingPrefs();
569 // Verify that the value was not reset.
570 ExpectStringValueEquals(kProtectedAtomic, kHelloWorld);
571 VerifyResetRecorded(false);
573 // Accessing the value of the previously protected pref didn't trigger its
574 // move to the unprotected preferences file, though the loading of the pref
575 // store should still have caused the MAC store to be recalculated.
576 LoadExistingPrefs();
577 ExpectStringValueEquals(kProtectedAtomic, kHelloWorld);
579 // Trigger the logic that migrates it back to the unprotected preferences
580 // file.
581 pref_store_->SetValue(kProtectedAtomic,
582 make_scoped_ptr(new base::StringValue(kGoodbyeWorld)),
583 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
584 LoadExistingPrefs();
585 ExpectStringValueEquals(kProtectedAtomic, kGoodbyeWorld);
586 VerifyResetRecorded(false);