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"
9 #include "base/compiler_specific.h"
10 #include "base/file_util.h"
11 #include "base/files/scoped_temp_dir.h"
12 #include "base/memory/ref_counted.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/message_loop/message_loop.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_store.h"
19 #include "base/prefs/testing_pref_service.h"
20 #include "base/run_loop.h"
21 #include "base/strings/string_util.h"
22 #include "base/values.h"
23 #include "chrome/browser/prefs/pref_hash_filter.h"
24 #include "components/user_prefs/pref_registry_syncable.h"
25 #include "testing/gtest/include/gtest/gtest.h"
29 class FirstEqualsPredicate {
31 explicit FirstEqualsPredicate(const std::string& expected)
32 : expected_(expected) {}
33 bool operator()(const std::pair<std::string, base::Value*>& pair) {
34 return pair.first == expected_;
38 const std::string expected_;
41 // Observes changes to the PrefStore and verifies that only registered prefs are
43 class RegistryVerifier : public PrefStore::Observer {
45 explicit RegistryVerifier(PrefRegistry* pref_registry)
46 : pref_registry_(pref_registry) {}
48 // PrefStore::Observer implementation
49 virtual void OnPrefValueChanged(const std::string& key) OVERRIDE {
50 EXPECT_TRUE(pref_registry_->end() !=
51 std::find_if(pref_registry_->begin(),
52 pref_registry_->end(),
53 FirstEqualsPredicate(key)))
54 << "Unregistered key " << key << " was changed.";
57 virtual void OnInitializationCompleted(bool succeeded) OVERRIDE {}
60 scoped_refptr<PrefRegistry> pref_registry_;
63 const char kTrackedAtomic[] = "tracked_atomic";
64 const char kProtectedAtomic[] = "protected_atomic";
65 const char kProtectedSplit[] = "protected_split";
67 const char kFoobar[] = "FOOBAR";
68 const char kBarfoo[] = "BARFOO";
69 const char kHelloWorld[] = "HELLOWORLD";
70 const char kGoodbyeWorld[] = "GOODBYEWORLD";
72 const PrefHashFilter::TrackedPreferenceMetadata kConfiguration[] = {
73 {0, kTrackedAtomic, PrefHashFilter::NO_ENFORCEMENT,
74 PrefHashFilter::TRACKING_STRATEGY_ATOMIC},
75 {1, kProtectedAtomic, PrefHashFilter::ENFORCE_ON_LOAD,
76 PrefHashFilter::TRACKING_STRATEGY_ATOMIC},
77 {2, kProtectedSplit, PrefHashFilter::ENFORCE_ON_LOAD,
78 PrefHashFilter::TRACKING_STRATEGY_SPLIT}};
82 class ProfilePrefStoreManagerTest : public testing::Test {
84 ProfilePrefStoreManagerTest()
85 : configuration_(kConfiguration,
86 kConfiguration + arraysize(kConfiguration)),
87 profile_pref_registry_(new user_prefs::PrefRegistrySyncable),
88 registry_verifier_(profile_pref_registry_) {}
90 virtual void SetUp() OVERRIDE {
91 ProfilePrefStoreManager::RegisterPrefs(local_state_.registry());
92 ProfilePrefStoreManager::RegisterProfilePrefs(profile_pref_registry_);
93 for (const PrefHashFilter::TrackedPreferenceMetadata* it = kConfiguration;
94 it != kConfiguration + arraysize(kConfiguration);
96 if (it->strategy == PrefHashFilter::TRACKING_STRATEGY_ATOMIC) {
97 profile_pref_registry_->RegisterStringPref(
98 it->name, "", user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
100 profile_pref_registry_->RegisterDictionaryPref(
101 it->name, user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
104 ASSERT_TRUE(profile_dir_.CreateUniqueTempDir());
106 manager_.reset(new ProfilePrefStoreManager(profile_dir_.path(),
108 configuration_.size(),
114 virtual void TearDown() OVERRIDE {
116 // Force everything to be written to disk, triggering the PrefHashFilter
117 // while our RegistryVerifier is watching.
118 pref_store_->CommitPendingWrite();
119 base::RunLoop().RunUntilIdle();
121 pref_store_->RemoveObserver(®istry_verifier_);
123 // Nothing should have to happen on the background threads, but just in
125 base::RunLoop().RunUntilIdle();
130 void InitializePrefs() {
131 scoped_refptr<PersistentPrefStore> pref_store =
132 manager_->CreateProfilePrefStore(
133 main_message_loop_.message_loop_proxy());
134 pref_store->AddObserver(®istry_verifier_);
135 PersistentPrefStore::PrefReadError error = pref_store->ReadPrefs();
136 ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE, error);
137 pref_store->SetValue(kTrackedAtomic, new base::StringValue(kFoobar));
138 pref_store->SetValue(kProtectedAtomic, new base::StringValue(kHelloWorld));
139 pref_store->RemoveObserver(®istry_verifier_);
141 base::RunLoop().RunUntilIdle();
144 void LoadExistingPrefs() {
145 pref_store_ = manager_->CreateProfilePrefStore(
146 main_message_loop_.message_loop_proxy());
147 pref_store_->AddObserver(®istry_verifier_);
148 EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE,
149 pref_store_->ReadPrefs());
152 void ReplaceStringInPrefs(const std::string& find,
153 const std::string& replace) {
154 // Tamper with the file's contents
155 base::FilePath pref_file_path =
156 ProfilePrefStoreManager::GetPrefFilePathFromProfilePath(
157 profile_dir_.path());
158 std::string pref_file_contents;
159 EXPECT_TRUE(base::ReadFileToString(pref_file_path, &pref_file_contents));
160 ReplaceSubstringsAfterOffset(&pref_file_contents, 0u, find, replace);
161 EXPECT_EQ(static_cast<int>(pref_file_contents.length()),
162 base::WriteFile(pref_file_path,
163 pref_file_contents.c_str(),
164 pref_file_contents.length()));
167 void ExpectStringValueEquals(const std::string& name,
168 const std::string& expected) {
169 const base::Value* value = NULL;
170 std::string as_string;
171 if (!pref_store_->GetValue(name, &value)) {
172 ADD_FAILURE() << name << " is not a defined value.";
173 } else if (!value->GetAsString(&as_string)) {
174 ADD_FAILURE() << name << " could not be coerced to a string.";
176 EXPECT_EQ(expected, as_string);
180 base::MessageLoop main_message_loop_;
181 std::vector<PrefHashFilter::TrackedPreferenceMetadata> configuration_;
182 base::ScopedTempDir profile_dir_;
183 TestingPrefServiceSimple local_state_;
184 scoped_refptr<user_prefs::PrefRegistrySyncable> profile_pref_registry_;
185 RegistryVerifier registry_verifier_;
186 scoped_ptr<ProfilePrefStoreManager> manager_;
187 scoped_refptr<PersistentPrefStore> pref_store_;
190 TEST_F(ProfilePrefStoreManagerTest, StoreValues) {
195 ExpectStringValueEquals(kTrackedAtomic, kFoobar);
196 ExpectStringValueEquals(kProtectedAtomic, kHelloWorld);
199 TEST_F(ProfilePrefStoreManagerTest, GetPrefFilePathFromProfilePath) {
200 base::FilePath pref_file_path =
201 ProfilePrefStoreManager::GetPrefFilePathFromProfilePath(
202 profile_dir_.path());
204 ASSERT_FALSE(base::PathExists(pref_file_path));
208 ASSERT_TRUE(base::PathExists(pref_file_path));
211 TEST_F(ProfilePrefStoreManagerTest, ProtectValues) {
214 ReplaceStringInPrefs(kFoobar, kBarfoo);
215 ReplaceStringInPrefs(kHelloWorld, kGoodbyeWorld);
219 // kTrackedAtomic is unprotected and thus will be loaded as it appears on
221 ExpectStringValueEquals(kTrackedAtomic, kBarfoo);
223 // If preference tracking is supported, the tampered value of kProtectedAtomic
224 // will be discarded at load time, leaving this preference undefined.
225 EXPECT_EQ(!ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
226 pref_store_->GetValue(kProtectedAtomic, NULL));
229 TEST_F(ProfilePrefStoreManagerTest, ResetPrefHashStore) {
232 manager_->ResetPrefHashStore();
236 // kTrackedAtomic is loaded as it appears on disk.
237 ExpectStringValueEquals(kTrackedAtomic, kFoobar);
238 // If preference tracking is supported, the tampered value of kProtectedAtomic
239 // will be discarded at load time, leaving this preference undefined.
240 EXPECT_EQ(!ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
241 pref_store_->GetValue(kProtectedAtomic, NULL));
244 TEST_F(ProfilePrefStoreManagerTest, ResetAllPrefHashStores) {
247 ProfilePrefStoreManager::ResetAllPrefHashStores(&local_state_);
251 // kTrackedAtomic is loaded as it appears on disk.
252 ExpectStringValueEquals(kTrackedAtomic, kFoobar);
253 // If preference tracking is supported, kProtectedAtomic will be undefined
254 // because the value was discarded due to loss of the hash store contents.
255 EXPECT_EQ(!ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
256 pref_store_->GetValue(kProtectedAtomic, NULL));
259 TEST_F(ProfilePrefStoreManagerTest, UpdateProfileHashStoreIfRequired) {
262 manager_->ResetPrefHashStore();
264 // This is a no-op if !kPlatformSupportsPreferenceTracking.
265 manager_->UpdateProfileHashStoreIfRequired(
266 main_message_loop_.message_loop_proxy());
267 base::RunLoop().RunUntilIdle();
269 // At the moment, UpdateProfileHashStoreIfRequired will accept existing
273 // These expectations hold whether or not tracking is supported.
274 ExpectStringValueEquals(kTrackedAtomic, kFoobar);
275 ExpectStringValueEquals(kProtectedAtomic, kHelloWorld);
278 TEST_F(ProfilePrefStoreManagerTest, InitializePrefsFromMasterPrefs) {
279 scoped_ptr<base::DictionaryValue> master_prefs(
280 new base::DictionaryValue);
281 master_prefs->Set(kTrackedAtomic, new base::StringValue(kFoobar));
282 master_prefs->Set(kProtectedAtomic, new base::StringValue(kHelloWorld));
284 manager_->InitializePrefsFromMasterPrefs(*master_prefs));
288 // Verify that InitializePrefsFromMasterPrefs correctly applied the MACs
289 // necessary to authenticate these values.
290 ExpectStringValueEquals(kTrackedAtomic, kFoobar);
291 ExpectStringValueEquals(kProtectedAtomic, kHelloWorld);