c478ac44834340166de6b10ac410695f54da1d5c
[platform/framework/web/crosswalk.git] / src / chrome / browser / prefs / profile_pref_store_manager_unittest.cc
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 "chrome/browser/prefs/profile_pref_store_manager.h"
6
7 #include <vector>
8
9 #include "base/compiler_specific.h"
10 #include "base/file_util.h"
11 #include "base/files/file_enumerator.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/message_loop/message_loop.h"
16 #include "base/prefs/json_pref_store.h"
17 #include "base/prefs/persistent_pref_store.h"
18 #include "base/prefs/pref_service.h"
19 #include "base/prefs/pref_service_factory.h"
20 #include "base/prefs/pref_store.h"
21 #include "base/prefs/testing_pref_service.h"
22 #include "base/run_loop.h"
23 #include "base/strings/string_util.h"
24 #include "base/values.h"
25 #include "chrome/browser/prefs/mock_validation_delegate.h"
26 #include "chrome/browser/prefs/pref_hash_filter.h"
27 #include "chrome/browser/prefs/tracked/pref_service_hash_store_contents.h"
28 #include "chrome/common/pref_names.h"
29 #include "components/pref_registry/pref_registry_syncable.h"
30 #include "testing/gtest/include/gtest/gtest.h"
31
32 namespace {
33
34 class FirstEqualsPredicate {
35  public:
36   explicit FirstEqualsPredicate(const std::string& expected)
37       : expected_(expected) {}
38   bool operator()(const std::pair<std::string, base::Value*>& pair) {
39     return pair.first == expected_;
40   }
41
42  private:
43   const std::string expected_;
44 };
45
46 // Observes changes to the PrefStore and verifies that only registered prefs are
47 // written.
48 class RegistryVerifier : public PrefStore::Observer {
49  public:
50   explicit RegistryVerifier(PrefRegistry* pref_registry)
51       : pref_registry_(pref_registry) {}
52
53   // PrefStore::Observer implementation
54   virtual void OnPrefValueChanged(const std::string& key) OVERRIDE {
55     EXPECT_TRUE(pref_registry_->end() !=
56                 std::find_if(pref_registry_->begin(),
57                              pref_registry_->end(),
58                              FirstEqualsPredicate(key)))
59         << "Unregistered key " << key << " was changed.";
60   }
61
62   virtual void OnInitializationCompleted(bool succeeded) OVERRIDE {}
63
64  private:
65   scoped_refptr<PrefRegistry> pref_registry_;
66 };
67
68 const char kUnprotectedPref[] = "unprotected_pref";
69 const char kTrackedAtomic[] = "tracked_atomic";
70 const char kProtectedAtomic[] = "protected_atomic";
71
72 const char kFoobar[] = "FOOBAR";
73 const char kBarfoo[] = "BARFOO";
74 const char kHelloWorld[] = "HELLOWORLD";
75 const char kGoodbyeWorld[] = "GOODBYEWORLD";
76
77 const PrefHashFilter::TrackedPreferenceMetadata kConfiguration[] = {
78     {0u, kTrackedAtomic, PrefHashFilter::NO_ENFORCEMENT,
79      PrefHashFilter::TRACKING_STRATEGY_ATOMIC},
80     {1u, kProtectedAtomic, PrefHashFilter::ENFORCE_ON_LOAD,
81      PrefHashFilter::TRACKING_STRATEGY_ATOMIC}};
82
83 const size_t kExtraReportingId = 2u;
84 const size_t kReportingIdCount = 3u;
85
86 }  // namespace
87
88 class ProfilePrefStoreManagerTest : public testing::Test {
89  public:
90   ProfilePrefStoreManagerTest()
91       : configuration_(kConfiguration,
92                        kConfiguration + arraysize(kConfiguration)),
93         profile_pref_registry_(new user_prefs::PrefRegistrySyncable),
94         registry_verifier_(profile_pref_registry_),
95         seed_("seed") {}
96
97   virtual void SetUp() OVERRIDE {
98     ProfilePrefStoreManager::RegisterPrefs(local_state_.registry());
99     ProfilePrefStoreManager::RegisterProfilePrefs(profile_pref_registry_);
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(
105             it->name,
106             std::string(),
107             user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
108       } else {
109         profile_pref_registry_->RegisterDictionaryPref(
110             it->name, user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
111       }
112     }
113     profile_pref_registry_->RegisterStringPref(
114         kUnprotectedPref,
115         std::string(),
116         user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
117
118     // As in chrome_pref_service_factory.cc, kPreferencesResetTime needs to be
119     // declared as protected in order to be read from the proper store by the
120     // SegregatedPrefStore. Only declare it after configured prefs have been
121     // registered above for this test as kPreferenceResetTime is already
122     // registered in ProfilePrefStoreManager::RegisterProfilePrefs.
123     PrefHashFilter::TrackedPreferenceMetadata pref_reset_time_config =
124         {configuration_.rbegin()->reporting_id + 1, prefs::kPreferenceResetTime,
125          PrefHashFilter::ENFORCE_ON_LOAD,
126          PrefHashFilter::TRACKING_STRATEGY_ATOMIC};
127     configuration_.push_back(pref_reset_time_config);
128
129     ASSERT_TRUE(profile_dir_.CreateUniqueTempDir());
130     ReloadConfiguration();
131   }
132
133   void ReloadConfiguration() {
134     manager_.reset(new ProfilePrefStoreManager(profile_dir_.path(),
135                                                configuration_,
136                                                kReportingIdCount,
137                                                seed_,
138                                                "device_id",
139                                                &local_state_));
140   }
141
142   virtual void TearDown() OVERRIDE { DestroyPrefStore(); }
143
144  protected:
145   bool WasResetRecorded() {
146     base::PrefServiceFactory pref_service_factory;
147     pref_service_factory.set_user_prefs(pref_store_);
148
149     scoped_ptr<PrefService> pref_service(
150         pref_service_factory.Create(profile_pref_registry_));
151
152     return !ProfilePrefStoreManager::GetResetTime(pref_service.get()).is_null();
153   }
154
155   void ClearResetRecorded() {
156     base::PrefServiceFactory pref_service_factory;
157     pref_service_factory.set_user_prefs(pref_store_);
158
159     scoped_ptr<PrefService> pref_service(
160         pref_service_factory.Create(profile_pref_registry_));
161
162     ProfilePrefStoreManager::ClearResetTime(pref_service.get());
163   }
164
165   void InitializePrefs() {
166     // According to the implementation of ProfilePrefStoreManager, this is
167     // actually a SegregatedPrefStore backed by two underlying pref stores.
168     scoped_refptr<PersistentPrefStore> pref_store =
169         manager_->CreateProfilePrefStore(
170             main_message_loop_.message_loop_proxy(),
171             &mock_validation_delegate_);
172     InitializePrefStore(pref_store);
173     pref_store = NULL;
174     base::RunLoop().RunUntilIdle();
175   }
176
177   void DestroyPrefStore() {
178     if (pref_store_) {
179       ClearResetRecorded();
180       // Force everything to be written to disk, triggering the PrefHashFilter
181       // while our RegistryVerifier is watching.
182       pref_store_->CommitPendingWrite();
183       base::RunLoop().RunUntilIdle();
184
185       pref_store_->RemoveObserver(&registry_verifier_);
186       pref_store_ = NULL;
187       // Nothing should have to happen on the background threads, but just in
188       // case...
189       base::RunLoop().RunUntilIdle();
190     }
191   }
192
193   void InitializeDeprecatedCombinedProfilePrefStore() {
194     scoped_refptr<PersistentPrefStore> pref_store =
195         manager_->CreateDeprecatedCombinedProfilePrefStore(
196             main_message_loop_.message_loop_proxy());
197     InitializePrefStore(pref_store);
198     pref_store = NULL;
199     base::RunLoop().RunUntilIdle();
200   }
201
202   void InitializePrefStore(PersistentPrefStore* pref_store) {
203     pref_store->AddObserver(&registry_verifier_);
204     PersistentPrefStore::PrefReadError error = pref_store->ReadPrefs();
205     EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE, error);
206     pref_store->SetValue(kTrackedAtomic, new base::StringValue(kFoobar));
207     pref_store->SetValue(kProtectedAtomic, new base::StringValue(kHelloWorld));
208     pref_store->SetValue(kUnprotectedPref, new base::StringValue(kFoobar));
209     pref_store->RemoveObserver(&registry_verifier_);
210     pref_store->CommitPendingWrite();
211     base::RunLoop().RunUntilIdle();
212   }
213
214   void LoadExistingPrefs() {
215     DestroyPrefStore();
216     pref_store_ = manager_->CreateProfilePrefStore(
217         main_message_loop_.message_loop_proxy(), NULL);
218     pref_store_->AddObserver(&registry_verifier_);
219     pref_store_->ReadPrefs();
220   }
221
222   void ReplaceStringInPrefs(const std::string& find,
223                             const std::string& replace) {
224     base::FileEnumerator file_enum(
225         profile_dir_.path(), true, base::FileEnumerator::FILES);
226
227     for (base::FilePath path = file_enum.Next(); !path.empty();
228          path = file_enum.Next()) {
229       // Tamper with the file's contents
230       std::string contents;
231       EXPECT_TRUE(base::ReadFileToString(path, &contents));
232       ReplaceSubstringsAfterOffset(&contents, 0u, find, replace);
233       EXPECT_EQ(static_cast<int>(contents.length()),
234                 base::WriteFile(path, contents.c_str(), contents.length()));
235     }
236   }
237
238   void ExpectStringValueEquals(const std::string& name,
239                                const std::string& expected) {
240     const base::Value* value = NULL;
241     std::string as_string;
242     if (!pref_store_->GetValue(name, &value)) {
243       ADD_FAILURE() << name << " is not a defined value.";
244     } else if (!value->GetAsString(&as_string)) {
245       ADD_FAILURE() << name << " could not be coerced to a string.";
246     } else {
247       EXPECT_EQ(expected, as_string);
248     }
249   }
250
251   void ExpectValidationObserved(const std::string& pref_path) {
252     // No validations are expected for platforms that do not support tracking.
253     if (!ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking)
254       return;
255     if (!mock_validation_delegate_.GetEventForPath(pref_path))
256       ADD_FAILURE() << "No validation observed for preference: " << pref_path;
257   }
258
259   base::MessageLoop main_message_loop_;
260   std::vector<PrefHashFilter::TrackedPreferenceMetadata> configuration_;
261   base::ScopedTempDir profile_dir_;
262   TestingPrefServiceSimple local_state_;
263   scoped_refptr<user_prefs::PrefRegistrySyncable> profile_pref_registry_;
264   RegistryVerifier registry_verifier_;
265   MockValidationDelegate mock_validation_delegate_;
266   scoped_ptr<ProfilePrefStoreManager> manager_;
267   scoped_refptr<PersistentPrefStore> pref_store_;
268
269   std::string seed_;
270 };
271
272 TEST_F(ProfilePrefStoreManagerTest, StoreValues) {
273   InitializePrefs();
274
275   LoadExistingPrefs();
276
277   ExpectStringValueEquals(kTrackedAtomic, kFoobar);
278   ExpectStringValueEquals(kProtectedAtomic, kHelloWorld);
279   EXPECT_FALSE(WasResetRecorded());
280   ExpectValidationObserved(kTrackedAtomic);
281   ExpectValidationObserved(kProtectedAtomic);
282 }
283
284 TEST_F(ProfilePrefStoreManagerTest, GetPrefFilePathFromProfilePath) {
285   base::FilePath pref_file_path =
286       ProfilePrefStoreManager::GetPrefFilePathFromProfilePath(
287           profile_dir_.path());
288
289   EXPECT_FALSE(base::PathExists(pref_file_path));
290
291   InitializePrefs();
292
293   EXPECT_TRUE(base::PathExists(pref_file_path));
294 }
295
296 TEST_F(ProfilePrefStoreManagerTest, ProtectValues) {
297   InitializePrefs();
298
299   ReplaceStringInPrefs(kFoobar, kBarfoo);
300   ReplaceStringInPrefs(kHelloWorld, kGoodbyeWorld);
301
302   LoadExistingPrefs();
303
304   // kTrackedAtomic is unprotected and thus will be loaded as it appears on
305   // disk.
306   ExpectStringValueEquals(kTrackedAtomic, kBarfoo);
307
308   // If preference tracking is supported, the tampered value of kProtectedAtomic
309   // will be discarded at load time, leaving this preference undefined.
310   EXPECT_NE(ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
311             pref_store_->GetValue(kProtectedAtomic, NULL));
312   EXPECT_EQ(ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
313             WasResetRecorded());
314
315   ExpectValidationObserved(kTrackedAtomic);
316   ExpectValidationObserved(kProtectedAtomic);
317 }
318
319 TEST_F(ProfilePrefStoreManagerTest, MigrateFromOneFile) {
320   InitializeDeprecatedCombinedProfilePrefStore();
321
322   // The deprecated model stores hashes in local state (on supported
323   // platforms)..
324   ASSERT_EQ(
325       ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
326       local_state_.GetUserPrefValue(
327           PrefServiceHashStoreContents::kProfilePreferenceHashes) != NULL);
328
329   LoadExistingPrefs();
330
331   // After a first migration, the hashes were copied to the two user preference
332   // files but were not cleaned.
333   ASSERT_EQ(
334       ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
335       local_state_.GetUserPrefValue(
336           PrefServiceHashStoreContents::kProfilePreferenceHashes) != NULL);
337
338   ExpectStringValueEquals(kTrackedAtomic, kFoobar);
339   ExpectStringValueEquals(kProtectedAtomic, kHelloWorld);
340   EXPECT_FALSE(WasResetRecorded());
341
342   LoadExistingPrefs();
343
344   // In a subsequent launch, the local state hash store should be reset.
345   ASSERT_FALSE(local_state_.GetUserPrefValue(
346       PrefServiceHashStoreContents::kProfilePreferenceHashes));
347
348   ExpectStringValueEquals(kTrackedAtomic, kFoobar);
349   ExpectStringValueEquals(kProtectedAtomic, kHelloWorld);
350   EXPECT_FALSE(WasResetRecorded());
351 }
352
353 TEST_F(ProfilePrefStoreManagerTest, MigrateWithTampering) {
354   InitializeDeprecatedCombinedProfilePrefStore();
355
356   ReplaceStringInPrefs(kFoobar, kBarfoo);
357   ReplaceStringInPrefs(kHelloWorld, kGoodbyeWorld);
358
359   // The deprecated model stores hashes in local state (on supported
360   // platforms)..
361   ASSERT_EQ(
362       ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
363       local_state_.GetUserPrefValue(
364           PrefServiceHashStoreContents::kProfilePreferenceHashes) != NULL);
365
366   LoadExistingPrefs();
367
368   // After a first migration, the hashes were copied to the two user preference
369   // files but were not cleaned.
370   ASSERT_EQ(
371       ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
372       local_state_.GetUserPrefValue(
373           PrefServiceHashStoreContents::kProfilePreferenceHashes) != NULL);
374
375   // kTrackedAtomic is unprotected and thus will be loaded as it appears on
376   // disk.
377   ExpectStringValueEquals(kTrackedAtomic, kBarfoo);
378
379   // If preference tracking is supported, the tampered value of kProtectedAtomic
380   // will be discarded at load time, leaving this preference undefined.
381   EXPECT_NE(ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
382             pref_store_->GetValue(kProtectedAtomic, NULL));
383   EXPECT_EQ(ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
384             WasResetRecorded());
385
386   LoadExistingPrefs();
387
388   // In a subsequent launch, the local state hash store would be reset.
389   ASSERT_FALSE(local_state_.GetUserPrefValue(
390       PrefServiceHashStoreContents::kProfilePreferenceHashes));
391
392   ExpectStringValueEquals(kTrackedAtomic, kBarfoo);
393   EXPECT_FALSE(WasResetRecorded());
394 }
395
396 TEST_F(ProfilePrefStoreManagerTest, InitializePrefsFromMasterPrefs) {
397   base::DictionaryValue master_prefs;
398   master_prefs.Set(kTrackedAtomic, new base::StringValue(kFoobar));
399   master_prefs.Set(kProtectedAtomic, new base::StringValue(kHelloWorld));
400   EXPECT_TRUE(manager_->InitializePrefsFromMasterPrefs(master_prefs));
401
402   LoadExistingPrefs();
403
404   // Verify that InitializePrefsFromMasterPrefs correctly applied the MACs
405   // necessary to authenticate these values.
406   ExpectStringValueEquals(kTrackedAtomic, kFoobar);
407   ExpectStringValueEquals(kProtectedAtomic, kHelloWorld);
408   EXPECT_FALSE(WasResetRecorded());
409 }
410
411 TEST_F(ProfilePrefStoreManagerTest, UnprotectedToProtected) {
412   InitializePrefs();
413
414   ExpectValidationObserved(kTrackedAtomic);
415   ExpectValidationObserved(kProtectedAtomic);
416
417   LoadExistingPrefs();
418   ExpectStringValueEquals(kUnprotectedPref, kFoobar);
419
420   // Ensure everything is written out to disk.
421   DestroyPrefStore();
422
423   ReplaceStringInPrefs(kFoobar, kBarfoo);
424
425   // It's unprotected, so we can load the modified value.
426   LoadExistingPrefs();
427   ExpectStringValueEquals(kUnprotectedPref, kBarfoo);
428
429   // Now update the configuration to protect it.
430   PrefHashFilter::TrackedPreferenceMetadata new_protected = {
431       kExtraReportingId, kUnprotectedPref, PrefHashFilter::ENFORCE_ON_LOAD,
432       PrefHashFilter::TRACKING_STRATEGY_ATOMIC};
433   configuration_.push_back(new_protected);
434   ReloadConfiguration();
435
436   // And try loading with the new configuration.
437   LoadExistingPrefs();
438
439   // Since there was a valid super MAC we were able to extend the existing trust
440   // to the newly protected preference.
441   ExpectStringValueEquals(kUnprotectedPref, kBarfoo);
442   EXPECT_FALSE(WasResetRecorded());
443
444   // Ensure everything is written out to disk.
445   DestroyPrefStore();
446
447   // It's protected now, so (if the platform supports it) any tampering should
448   // lead to a reset.
449   ReplaceStringInPrefs(kBarfoo, kFoobar);
450   LoadExistingPrefs();
451   EXPECT_NE(ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
452             pref_store_->GetValue(kUnprotectedPref, NULL));
453   EXPECT_EQ(ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
454             WasResetRecorded());
455 }
456
457 TEST_F(ProfilePrefStoreManagerTest, NewPrefWhenFirstProtecting) {
458   std::vector<PrefHashFilter::TrackedPreferenceMetadata>
459       original_configuration = configuration_;
460   for (std::vector<PrefHashFilter::TrackedPreferenceMetadata>::iterator it =
461            configuration_.begin();
462        it != configuration_.end();
463        ++it) {
464     it->enforcement_level = PrefHashFilter::NO_ENFORCEMENT;
465   }
466   ReloadConfiguration();
467
468   InitializePrefs();
469
470   ExpectValidationObserved(kTrackedAtomic);
471   ExpectValidationObserved(kProtectedAtomic);
472
473   LoadExistingPrefs();
474   ExpectStringValueEquals(kUnprotectedPref, kFoobar);
475
476   // Ensure everything is written out to disk.
477   DestroyPrefStore();
478
479   // Now introduce protection, including the never-before tracked "new_pref".
480   configuration_ = original_configuration;
481   PrefHashFilter::TrackedPreferenceMetadata new_protected = {
482       kExtraReportingId, kUnprotectedPref, PrefHashFilter::ENFORCE_ON_LOAD,
483       PrefHashFilter::TRACKING_STRATEGY_ATOMIC};
484   configuration_.push_back(new_protected);
485   ReloadConfiguration();
486
487   // And try loading with the new configuration.
488   LoadExistingPrefs();
489
490   // Since there was a valid super MAC we were able to extend the existing trust
491   // to the newly tracked & protected preference.
492   ExpectStringValueEquals(kUnprotectedPref, kFoobar);
493   EXPECT_FALSE(WasResetRecorded());
494 }
495
496 TEST_F(ProfilePrefStoreManagerTest, UnprotectedToProtectedWithoutTrust) {
497   InitializePrefs();
498
499   ExpectValidationObserved(kTrackedAtomic);
500   ExpectValidationObserved(kProtectedAtomic);
501
502   // Now update the configuration to protect it.
503   PrefHashFilter::TrackedPreferenceMetadata new_protected = {
504       kExtraReportingId, kUnprotectedPref, PrefHashFilter::ENFORCE_ON_LOAD,
505       PrefHashFilter::TRACKING_STRATEGY_ATOMIC};
506   configuration_.push_back(new_protected);
507   seed_ = "new-seed-to-break-trust";
508   ReloadConfiguration();
509
510   // And try loading with the new configuration.
511   LoadExistingPrefs();
512
513   // If preference tracking is supported, kUnprotectedPref will have been
514   // discarded because new values are not accepted without a valid super MAC.
515   EXPECT_NE(ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
516             pref_store_->GetValue(kUnprotectedPref, NULL));
517   EXPECT_EQ(ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking,
518             WasResetRecorded());
519 }
520
521 // This test verifies that preference values are correctly maintained when a
522 // preference's protection state changes from protected to unprotected.
523 TEST_F(ProfilePrefStoreManagerTest, ProtectedToUnprotected) {
524   InitializePrefs();
525
526   ExpectValidationObserved(kTrackedAtomic);
527   ExpectValidationObserved(kProtectedAtomic);
528
529   DestroyPrefStore();
530
531   // Unconfigure protection for kProtectedAtomic
532   for (std::vector<PrefHashFilter::TrackedPreferenceMetadata>::iterator it =
533            configuration_.begin();
534        it != configuration_.end();
535        ++it) {
536     if (it->name == kProtectedAtomic) {
537       it->enforcement_level = PrefHashFilter::NO_ENFORCEMENT;
538       break;
539     }
540   }
541
542   seed_ = "new-seed-to-break-trust";
543   ReloadConfiguration();
544   LoadExistingPrefs();
545
546   // Verify that the value was not reset.
547   ExpectStringValueEquals(kProtectedAtomic, kHelloWorld);
548   EXPECT_FALSE(WasResetRecorded());
549
550   // Accessing the value of the previously protected pref didn't trigger its
551   // move to the unprotected preferences file, though the loading of the pref
552   // store should still have caused the MAC store to be recalculated.
553   LoadExistingPrefs();
554   ExpectStringValueEquals(kProtectedAtomic, kHelloWorld);
555
556   // Trigger the logic that migrates it back to the unprotected preferences
557   // file.
558   pref_store_->SetValue(kProtectedAtomic, new base::StringValue(kGoodbyeWorld));
559   LoadExistingPrefs();
560   ExpectStringValueEquals(kProtectedAtomic, kGoodbyeWorld);
561   EXPECT_FALSE(WasResetRecorded());
562 }