diff options
Diffstat (limited to 'chrome/browser/policy')
18 files changed, 864 insertions, 654 deletions
diff --git a/chrome/browser/policy/config_dir_policy_provider.cc b/chrome/browser/policy/config_dir_policy_provider.cc index a3f3e04..6c1a4f3 100644 --- a/chrome/browser/policy/config_dir_policy_provider.cc +++ b/chrome/browser/policy/config_dir_policy_provider.cc @@ -7,116 +7,19 @@ #include <set> #include "base/file_util.h" -#include "base/logging.h" -#include "base/message_loop.h" -#include "base/task.h" -#include "base/utf_string_conversions.h" #include "base/values.h" -#include "chrome/browser/browser_thread.h" #include "chrome/common/json_value_serializer.h" namespace policy { -// Amount of time we wait for the files in the policy directory to settle before -// trying to load it. This alleviates the problem of reading partially written -// files and allows to batch quasi-simultaneous changes. -const int kSettleIntervalSeconds = 5; - -// The time interval for rechecking policy. This is our fallback in case the -// directory watch fails or doesn't report a change. -const int kReloadIntervalMinutes = 15; - -// PolicyDirLoader implementation: - -PolicyDirLoader::PolicyDirLoader( - base::WeakPtr<ConfigDirPolicyProvider> provider, - const FilePath& config_dir, - int settle_interval_seconds, - int reload_interval_minutes) - : provider_(provider), - origin_loop_(MessageLoop::current()), - config_dir_(config_dir), - reload_task_(NULL), - settle_interval_seconds_(settle_interval_seconds), - reload_interval_minutes_(reload_interval_minutes) { - // Force an initial load, so GetPolicy() works. - policy_.reset(Load()); - DCHECK(policy_.get()); -} - -PolicyDirLoader::~PolicyDirLoader() {} - -void PolicyDirLoader::Stop() { - if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) { - BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, - NewRunnableMethod(this, &PolicyDirLoader::Stop)); - return; - } - - if (reload_task_) { - reload_task_->Cancel(); - reload_task_ = NULL; - } -} - -void PolicyDirLoader::Reload() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - - // Check the directory time in order to see whether a reload is required. - base::TimeDelta delay; - base::Time now = base::Time::Now(); - if (!IsSafeToReloadPolicy(now, &delay)) { - ScheduleReloadTask(delay); - return; - } - - // Load the policy definitions. - scoped_ptr<DictionaryValue> new_policy(Load()); - - // Check again in case the directory has changed while reading it. - if (!IsSafeToReloadPolicy(now, &delay)) { - ScheduleReloadTask(delay); - return; - } - - // Replace policy definition. - bool changed = false; - { - AutoLock lock(lock_); - changed = !policy_->Equals(new_policy.get()); - policy_.reset(new_policy.release()); - } - - // There's a change, report it! - if (changed) { - VLOG(1) << "Policy reload from " << config_dir_.value() << " succeeded."; - origin_loop_->PostTask(FROM_HERE, - NewRunnableMethod(this, &PolicyDirLoader::NotifyPolicyChanged)); - } - - // As a safeguard in case the file watcher fails, schedule a reload task - // that'll make us recheck after a reasonable interval. - ScheduleReloadTask(base::TimeDelta::FromMinutes(reload_interval_minutes_)); -} - -DictionaryValue* PolicyDirLoader::GetPolicy() { - AutoLock lock(lock_); - return static_cast<DictionaryValue*>(policy_->DeepCopy()); +ConfigDirPolicyLoader::ConfigDirPolicyLoader(const FilePath& config_dir) + : FileBasedPolicyProvider::Delegate(config_dir) { } -void PolicyDirLoader::OnFilePathChanged(const FilePath& path) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - Reload(); -} - -void PolicyDirLoader::OnError() { - LOG(ERROR) << "FileWatcher on " << config_dir_.value() << " failed."; -} - -DictionaryValue* PolicyDirLoader::Load() { +DictionaryValue* ConfigDirPolicyLoader::Load() { // Enumerate the files and sort them lexicographically. std::set<FilePath> files; - file_util::FileEnumerator file_enumerator(config_dir_, false, + file_util::FileEnumerator file_enumerator(config_file_path(), false, file_util::FileEnumerator::FILES); for (FilePath config_file_path = file_enumerator.Next(); !config_file_path.empty(); config_file_path = file_enumerator.Next()) @@ -146,135 +49,38 @@ DictionaryValue* PolicyDirLoader::Load() { return policy; } -bool PolicyDirLoader::IsSafeToReloadPolicy(const base::Time& now, - base::TimeDelta* delay) { - DCHECK(delay); - base::PlatformFileInfo dir_info; +base::Time ConfigDirPolicyLoader::GetLastModification() { + base::Time last_modification = base::Time(); + base::PlatformFileInfo file_info; - // Reading an empty directory or a file is always safe. - if (!file_util::GetFileInfo(config_dir_, &dir_info) || - !dir_info.is_directory) { - last_modification_file_ = base::Time(); - return true; + // If the path does not exist or points to a directory, it's safe to load. + if (!file_util::GetFileInfo(config_file_path(), &file_info) || + !file_info.is_directory) { + return last_modification; } - // If there was a change since the last recorded modification, wait some more. - base::TimeDelta settleInterval( - base::TimeDelta::FromSeconds(settle_interval_seconds_)); - if (dir_info.last_modified != last_modification_file_) { - last_modification_file_ = dir_info.last_modified; - last_modification_clock_ = now; - *delay = settleInterval; - return false; - } - - // Check whether the settle interval has elapsed. - base::TimeDelta age = now - last_modification_clock_; - if (age < settleInterval) { - *delay = settleInterval - age; - return false; - } - - return true; -} - -void PolicyDirLoader::ScheduleReloadTask(const base::TimeDelta& delay) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - - if (reload_task_) - reload_task_->Cancel(); - - reload_task_ = NewRunnableMethod(this, &PolicyDirLoader::ReloadFromTask); - BrowserThread::PostDelayedTask(BrowserThread::FILE, FROM_HERE, reload_task_, - delay.InMilliseconds()); -} - -void PolicyDirLoader::NotifyPolicyChanged() { - DCHECK_EQ(origin_loop_, MessageLoop::current()); - if (provider_) - provider_->NotifyStoreOfPolicyChange(); -} - -void PolicyDirLoader::ReloadFromTask() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - - // Drop the reference to the reload task, since the task might be the only - // referer that keeps us alive, so we should not Cancel() it. - reload_task_ = NULL; - - Reload(); -} - -// PolicyDirWatcher implementation: - -PolicyDirWatcher::PolicyDirWatcher() {} - -void PolicyDirWatcher::Init(PolicyDirLoader* loader) { - // Initialization can happen early when the file thread is not yet available. - // So post a task to ourselves on the UI thread which will run after threading - // is up and schedule watch initialization on the file thread. - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - NewRunnableMethod(this, - &PolicyDirWatcher::InitWatcher, - scoped_refptr<PolicyDirLoader>(loader))); -} - -PolicyDirWatcher::~PolicyDirWatcher() {} - -void PolicyDirWatcher::InitWatcher( - const scoped_refptr<PolicyDirLoader>& loader) { - if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) { - BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, - NewRunnableMethod(this, &PolicyDirWatcher::InitWatcher, loader)); - return; + // Enumerate the files and find the most recent modification timestamp. + file_util::FileEnumerator file_enumerator(config_file_path(), + false, + file_util::FileEnumerator::FILES); + for (FilePath config_file = file_enumerator.Next(); + !config_file.empty(); + config_file = file_enumerator.Next()) { + if (file_util::GetFileInfo(config_file, &file_info) && + !file_info.is_directory) { + last_modification = std::min(last_modification, file_info.last_modified); + } } - if (!watcher_.Watch(loader->config_dir(), loader.get())) - loader->OnError(); - - // There might have been changes to the directory in the time between - // construction of the loader and initialization of the watcher. Call reload - // to detect if that is the case. - loader->Reload(); + return last_modification; } -// ConfigDirPolicyProvider implementation: - ConfigDirPolicyProvider::ConfigDirPolicyProvider( - const ConfigurationPolicyProvider::StaticPolicyValueMap& policy_map, + const ConfigurationPolicyProvider::PolicyDefinitionList* policy_list, const FilePath& config_dir) - : ConfigurationPolicyProvider(policy_map) { - loader_ = new PolicyDirLoader(AsWeakPtr(), config_dir, kSettleIntervalSeconds, - kReloadIntervalMinutes); - watcher_ = new PolicyDirWatcher; - watcher_->Init(loader_.get()); + : FileBasedPolicyProvider(policy_list, + new ConfigDirPolicyLoader(config_dir)) { } -ConfigDirPolicyProvider::~ConfigDirPolicyProvider() { - loader_->Stop(); -} - -bool ConfigDirPolicyProvider::Provide(ConfigurationPolicyStore* store) { - scoped_ptr<DictionaryValue> policy(loader_->GetPolicy()); - DCHECK(policy.get()); - DecodePolicyValueTree(policy.get(), store); - return true; -} - -void ConfigDirPolicyProvider::DecodePolicyValueTree( - DictionaryValue* policies, - ConfigurationPolicyStore* store) { - const PolicyValueMap& mapping(policy_value_map()); - for (PolicyValueMap::const_iterator i = mapping.begin(); - i != mapping.end(); ++i) { - const PolicyValueMapEntry& entry(*i); - Value* value; - if (policies->Get(entry.name, &value) && value->IsType(entry.value_type)) - store->Apply(entry.policy_type, value->DeepCopy()); - } - - // TODO(mnissler): Handle preference overrides once |ConfigurationPolicyStore| - // supports it. -} } // namespace policy diff --git a/chrome/browser/policy/config_dir_policy_provider.h b/chrome/browser/policy/config_dir_policy_provider.h index 055ce06..6096513 100644 --- a/chrome/browser/policy/config_dir_policy_provider.h +++ b/chrome/browser/policy/config_dir_policy_provider.h @@ -6,169 +6,35 @@ #define CHROME_BROWSER_POLICY_CONFIG_DIR_POLICY_PROVIDER_H_ #pragma once -#include "base/basictypes.h" -#include "base/file_path.h" -#include "base/lock.h" -#include "base/ref_counted.h" -#include "base/scoped_ptr.h" -#include "base/time.h" -#include "base/weak_ptr.h" -#include "chrome/browser/file_path_watcher.h" -#include "chrome/browser/policy/configuration_policy_provider.h" - -class CancelableTask; -class DictionaryValue; -class MessageLoop; +#include "chrome/browser/policy/file_based_policy_provider.h" namespace policy { -class ConfigDirPolicyProvider; - -// FilePathWatcher delegate implementation that handles change notifications for -// the configuration directory. It keeps the authorative version of the -// currently effective policy dictionary and updates it as appropriate. -class PolicyDirLoader : public FilePathWatcher::Delegate { - public: - // Creates a new loader that'll load its data from |config_dir|. The - // parameters |settle_interval_seconds| and |reload_interval_minutes| specify - // the time to wait before reading the directory contents after a change and - // the period for checking |config_dir| for changes, respectively. - PolicyDirLoader(base::WeakPtr<ConfigDirPolicyProvider> provider, - const FilePath& config_dir, - int settle_interval_seconds, - int reload_interval_minutes); - - virtual ~PolicyDirLoader(); - - // Stops any pending reload tasks. - void Stop(); - - // Reloads the policies and sends out a notification, if appropriate. Must be - // called on the file thread. - void Reload(); - - // Gets the current dictionary value object. Ownership of the returned value - // is transferred to the caller. - DictionaryValue* GetPolicy(); - - const FilePath& config_dir() { return config_dir_; } - - // FilePathWatcher::Delegate implementation: - void OnFilePathChanged(const FilePath& path); - void OnError(); - - private: - // Loads the policy information. Ownership of the return value is transferred - // to the caller. - DictionaryValue* Load(); - - // Checks the directory modification time to see whether reading the - // configuration directory is safe. If not, returns false and the delay until - // it is considered safe to reload in |delay|. - bool IsSafeToReloadPolicy(const base::Time& now, base::TimeDelta* delay); - - // Schedules a reload task to run when |delay| expires. Must be called on the - // file thread. - void ScheduleReloadTask(const base::TimeDelta& delay); - - // Notifies the policy provider to send out a policy changed notification. - // Must be called on |origin_loop_|. - void NotifyPolicyChanged(); - - // Invoked from the reload task on the file thread. - void ReloadFromTask(); - - // The provider this loader is associated with. Access only on the thread that - // called the constructor. See |origin_loop_| below. - base::WeakPtr<ConfigDirPolicyProvider> provider_; - - // The message loop on which this object was constructed and |provider_| - // received on. Recorded so we can call back into the non thread safe provider - // to fire the notification. - MessageLoop* origin_loop_; - - // The directory in which we look for configuration files. - const FilePath config_dir_; - - // Records last known modification timestamp of |config_dir_|. - base::Time last_modification_file_; - - // The wall clock time at which the last modification timestamp was recorded. - // It's better to not assume the file notification time and the wall clock - // times come from the same source, just in case there is some non-local - // filesystem involved. - base::Time last_modification_clock_; - - // Protects |policy_|. - Lock lock_; - - // The current policy definition. - scoped_ptr<DictionaryValue> policy_; - - // The reload task. Access only on the file thread. Holds a reference to the - // currently posted task, so we can cancel and repost it if necessary. - CancelableTask* reload_task_; - - // Settle and reload intervals. - const int settle_interval_seconds_; - const int reload_interval_minutes_; - - DISALLOW_COPY_AND_ASSIGN(PolicyDirLoader); -}; - -// Wraps a FilePathWatcher for the configuration directory and takes care of -// initializing the watcher object on the file thread. -class PolicyDirWatcher : public base::RefCountedThreadSafe<PolicyDirWatcher> { +// A policy loader implementation backed by a set of files in a given directory. +// The files should contain JSON-formatted policy settings. They are merged +// together and the result is returned via the PolicyLoader interface. The files +// are consulted in lexicographic file name order, so the last value read takes +// precedence in case of preference key collisions. +class ConfigDirPolicyLoader : public FileBasedPolicyProvider::Delegate { public: - PolicyDirWatcher(); + explicit ConfigDirPolicyLoader(const FilePath& config_dir); - // Runs initialization. This is in a separate method since we need to post a - // task (which cannot be done from the constructor). - void Init(PolicyDirLoader* loader); + // FileBasedPolicyLoader::Delegate implementation. + virtual DictionaryValue* Load(); + virtual base::Time GetLastModification(); private: - // PolicyDirWatcher objects should only be deleted by RefCountedThreadSafe. - friend class base::RefCountedThreadSafe<PolicyDirWatcher>; - virtual ~PolicyDirWatcher(); - - // Actually sets up the watch with the FilePathWatcher code. - void InitWatcher(const scoped_refptr<PolicyDirLoader>& loader); - - // Wrapped watcher that takes care of the actual watching. - FilePathWatcher watcher_; - - DISALLOW_COPY_AND_ASSIGN(PolicyDirWatcher); + DISALLOW_COPY_AND_ASSIGN(ConfigDirPolicyLoader); }; -// A policy provider implementation backed by a set of files in a given -// directory. The files should contain JSON-formatted policy settings. They are -// merged together and the result is returned via the -// ConfigurationPolicyProvider interface. The files are consulted in -// lexicographic file name order, so the last value read takes precedence in -// case of preference key collisions. -class ConfigDirPolicyProvider - : public ConfigurationPolicyProvider, - public base::SupportsWeakPtr<ConfigDirPolicyProvider> { +// Policy provider backed by JSON files in a configuration directory. +class ConfigDirPolicyProvider : public FileBasedPolicyProvider { public: - explicit ConfigDirPolicyProvider( - const ConfigurationPolicyProvider::StaticPolicyValueMap& policy_map, + ConfigDirPolicyProvider( + const ConfigurationPolicyProvider::PolicyDefinitionList* policy_list, const FilePath& config_dir); - virtual ~ConfigDirPolicyProvider(); - - // ConfigurationPolicyProvider implementation. - virtual bool Provide(ConfigurationPolicyStore* store); private: - // Decodes the value tree and writes the configuration to the given |store|. - void DecodePolicyValueTree(DictionaryValue* policies, - ConfigurationPolicyStore* store); - - // Watches for changes to the configuration directory. - scoped_refptr<PolicyDirWatcher> watcher_; - - // The loader object we use internally. - scoped_refptr<PolicyDirLoader> loader_; - DISALLOW_COPY_AND_ASSIGN(ConfigDirPolicyProvider); }; diff --git a/chrome/browser/policy/config_dir_policy_provider_unittest.cc b/chrome/browser/policy/config_dir_policy_provider_unittest.cc index 2c755ab..655c919 100644 --- a/chrome/browser/policy/config_dir_policy_provider_unittest.cc +++ b/chrome/browser/policy/config_dir_policy_provider_unittest.cc @@ -6,47 +6,24 @@ #include "base/file_util.h" #include "base/path_service.h" +#include "base/scoped_temp_dir.h" #include "base/string_number_conversions.h" #include "chrome/browser/policy/config_dir_policy_provider.h" #include "chrome/browser/policy/configuration_policy_pref_store.h" #include "chrome/browser/policy/mock_configuration_policy_store.h" #include "chrome/common/json_value_serializer.h" #include "chrome/common/policy_constants.h" -#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -using testing::Mock; - namespace policy { -// Shorter reload intervals for testing PolicyDirWatcher. -const int kSettleIntervalSecondsForTesting = 0; -const int kReloadIntervalMinutesForTesting = 1; - template<typename BASE> class ConfigDirPolicyProviderTestBase : public BASE { protected: - ConfigDirPolicyProviderTestBase() - : ui_thread_(BrowserThread::UI, &loop_), - file_thread_(BrowserThread::FILE, &loop_) {} + ConfigDirPolicyProviderTestBase() {} virtual void SetUp() { - // Determine the directory to use for testing. - ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &test_dir_)); - test_dir_ = - test_dir_.Append(FILE_PATH_LITERAL("ConfigDirPolicyProviderTest")); - - // Make sure the directory is fresh. - file_util::Delete(test_dir_, true); - file_util::CreateDirectory(test_dir_); - ASSERT_TRUE(file_util::DirectoryExists(test_dir_)); - } - - virtual void TearDown() { - loop_.RunAllPending(); - // Clean up test directory. - ASSERT_TRUE(file_util::Delete(test_dir_, true)); - ASSERT_FALSE(file_util::PathExists(test_dir_)); + ASSERT_TRUE(test_dir_.CreateUniqueTempDir()); } // JSON-encode a dictionary and write it to a file. @@ -55,155 +32,53 @@ class ConfigDirPolicyProviderTestBase : public BASE { std::string data; JSONStringValueSerializer serializer(&data); serializer.Serialize(dict); - FilePath file_path(test_dir_.AppendASCII(file_name)); + FilePath file_path(test_dir().AppendASCII(file_name)); ASSERT_TRUE(file_util::WriteFile(file_path, data.c_str(), data.size())); } - FilePath test_dir_; - MessageLoop loop_; - BrowserThread ui_thread_; - BrowserThread file_thread_; -}; - -// A mock provider that allows us to capture reload notifications. -class MockConfigDirPolicyProvider : public ConfigDirPolicyProvider { - public: - explicit MockConfigDirPolicyProvider(const FilePath& config_dir_) - : ConfigDirPolicyProvider( - ConfigurationPolicyPrefStore::GetChromePolicyValueMap(), - config_dir_) { - } + const FilePath& test_dir() { return test_dir_.path(); } - MOCK_METHOD0(NotifyStoreOfPolicyChange, void()); + private: + ScopedTempDir test_dir_; }; -class PolicyDirLoaderTest +class ConfigDirPolicyLoaderTest : public ConfigDirPolicyProviderTestBase<testing::Test> { - protected: - PolicyDirLoaderTest() {} - - virtual void SetUp() { - ConfigDirPolicyProviderTestBase<testing::Test>::SetUp(); - provider_.reset(new MockConfigDirPolicyProvider(test_dir_)); - } - - virtual void TearDown() { - provider_.reset(NULL); - ConfigDirPolicyProviderTestBase<testing::Test>::TearDown(); - } - - scoped_ptr<MockConfigDirPolicyProvider> provider_; -}; - -TEST_F(PolicyDirLoaderTest, BasicLoad) { - DictionaryValue test_dict; - test_dict.SetString("HomepageLocation", "http://www.google.com"); - WriteConfigFile(test_dict, "config_file"); - - scoped_refptr<PolicyDirLoader> loader_( - new PolicyDirLoader(provider_->AsWeakPtr(), test_dir_, - kSettleIntervalSecondsForTesting, - kReloadIntervalMinutesForTesting)); - scoped_ptr<DictionaryValue> policy(loader_->GetPolicy()); - EXPECT_TRUE(policy.get()); - EXPECT_EQ(1U, policy->size()); - - std::string str_value; - EXPECT_TRUE(policy->GetString("HomepageLocation", &str_value)); - EXPECT_EQ("http://www.google.com", str_value); - - loader_->Stop(); -} - -TEST_F(PolicyDirLoaderTest, TestRefresh) { - scoped_refptr<PolicyDirLoader> loader_( - new PolicyDirLoader(provider_->AsWeakPtr(), test_dir_, - kSettleIntervalSecondsForTesting, - kReloadIntervalMinutesForTesting)); - scoped_ptr<DictionaryValue> policy(loader_->GetPolicy()); - EXPECT_TRUE(policy.get()); - EXPECT_EQ(0U, policy->size()); - - DictionaryValue test_dict; - test_dict.SetString("HomepageLocation", "http://www.google.com"); - WriteConfigFile(test_dict, "config_file"); - - EXPECT_CALL(*provider_, NotifyStoreOfPolicyChange()).Times(1); - loader_->OnFilePathChanged(test_dir_.AppendASCII("config_file")); - - // Run the loop. The refresh should be handled immediately since the settle - // interval has been disabled. - loop_.RunAllPending(); - Mock::VerifyAndClearExpectations(provider_.get()); - - policy.reset(loader_->GetPolicy()); - EXPECT_TRUE(policy.get()); - EXPECT_EQ(1U, policy->size()); - - std::string str_value; - EXPECT_TRUE(policy->GetString("HomepageLocation", &str_value)); - EXPECT_EQ("http://www.google.com", str_value); - - loader_->Stop(); -} - -template<typename BASE> -class ConfigDirPolicyProviderTestWithMockStore - : public ConfigDirPolicyProviderTestBase<BASE> { - protected: - virtual void SetUp() { - ConfigDirPolicyProviderTestBase<BASE>::SetUp(); - // Create a fresh policy store mock. - policy_store_.reset(new MockConfigurationPolicyStore()); - } - - scoped_ptr<MockConfigurationPolicyStore> policy_store_; -}; - -class ConfigDirPolicyProviderTest - : public ConfigDirPolicyProviderTestWithMockStore<testing::Test> { }; // The preferences dictionary is expected to be empty when there are no files to // load. -TEST_F(ConfigDirPolicyProviderTest, ReadPrefsEmpty) { - ConfigDirPolicyProvider provider( - ConfigurationPolicyPrefStore::GetChromePolicyValueMap(), test_dir_); - EXPECT_TRUE(provider.Provide(policy_store_.get())); - EXPECT_TRUE(policy_store_->policy_map().empty()); +TEST_F(ConfigDirPolicyLoaderTest, ReadPrefsEmpty) { + ConfigDirPolicyLoader loader(test_dir()); + scoped_ptr<DictionaryValue> policy(loader.Load()); + EXPECT_TRUE(policy.get()); + EXPECT_TRUE(policy->empty()); } // Reading from a non-existent directory should result in an empty preferences // dictionary. -TEST_F(ConfigDirPolicyProviderTest, ReadPrefsNonExistentDirectory) { - FilePath non_existent_dir(test_dir_.Append(FILE_PATH_LITERAL("not_there"))); - ConfigDirPolicyProvider provider( - ConfigurationPolicyPrefStore::GetChromePolicyValueMap(), - non_existent_dir); - EXPECT_TRUE(provider.Provide(policy_store_.get())); - EXPECT_TRUE(policy_store_->policy_map().empty()); +TEST_F(ConfigDirPolicyLoaderTest, ReadPrefsNonExistentDirectory) { + FilePath non_existent_dir(test_dir().Append(FILE_PATH_LITERAL("not_there"))); + ConfigDirPolicyLoader loader(non_existent_dir); + scoped_ptr<DictionaryValue> policy(loader.Load()); + EXPECT_TRUE(policy.get()); + EXPECT_TRUE(policy->empty()); } // Test reading back a single preference value. -TEST_F(ConfigDirPolicyProviderTest, ReadPrefsSinglePref) { +TEST_F(ConfigDirPolicyLoaderTest, ReadPrefsSinglePref) { DictionaryValue test_dict; test_dict.SetString("HomepageLocation", "http://www.google.com"); WriteConfigFile(test_dict, "config_file"); - ConfigDirPolicyProvider provider( - ConfigurationPolicyPrefStore::GetChromePolicyValueMap(), test_dir_); - EXPECT_TRUE(provider.Provide(policy_store_.get())); - EXPECT_EQ(1U, policy_store_->policy_map().size()); - const Value* value = - policy_store_->Get(ConfigurationPolicyStore::kPolicyHomePage); - ASSERT_TRUE(value); - std::string str_value; - EXPECT_TRUE(value->GetAsString(&str_value)); - EXPECT_EQ("http://www.google.com", str_value); + ConfigDirPolicyLoader loader(test_dir()); + scoped_ptr<DictionaryValue> policy(loader.Load()); + EXPECT_TRUE(policy.get()); + EXPECT_TRUE(policy->Equals(&test_dict)); } // Test merging values from different files. -TEST_F(ConfigDirPolicyProviderTest, ReadPrefsMergePrefs) { +TEST_F(ConfigDirPolicyLoaderTest, ReadPrefsMergePrefs) { // Write a bunch of data files in order to increase the chance to detect the // provider not respecting lexicographic ordering when reading them. Since the // filesystem may return files in arbitrary order, there is no way to be sure, @@ -217,17 +92,11 @@ TEST_F(ConfigDirPolicyProviderTest, ReadPrefsMergePrefs) { WriteConfigFile(test_dict_foo, "9"); for (unsigned int i = 5; i <= 8; ++i) WriteConfigFile(test_dict_bar, base::IntToString(i)); - ConfigDirPolicyProvider provider( - ConfigurationPolicyPrefStore::GetChromePolicyValueMap(), test_dir_); - EXPECT_TRUE(provider.Provide(policy_store_.get())); - EXPECT_EQ(1U, policy_store_->policy_map().size()); - const Value* value = - policy_store_->Get(ConfigurationPolicyStore::kPolicyHomePage); - ASSERT_TRUE(value); - std::string str_value; - EXPECT_TRUE(value->GetAsString(&str_value)); - EXPECT_EQ("http://foo.com", str_value); + ConfigDirPolicyLoader loader(test_dir()); + scoped_ptr<DictionaryValue> policy(loader.Load()); + EXPECT_TRUE(policy.get()); + EXPECT_TRUE(policy->Equals(&test_dict_foo)); } // Holds policy type, corresponding policy key string and a valid value for use @@ -298,15 +167,31 @@ class ValueTestParams { // Tests whether the provider correctly reads a value from the file and forwards // it to the store. class ConfigDirPolicyProviderValueTest - : public ConfigDirPolicyProviderTestWithMockStore< + : public ConfigDirPolicyProviderTestBase< testing::TestWithParam<ValueTestParams> > { + protected: + ConfigDirPolicyProviderValueTest() + : ui_thread_(BrowserThread::UI, &loop_), + file_thread_(BrowserThread::FILE, &loop_) {} + + virtual void TearDown() { + loop_.RunAllPending(); + } + + MockConfigurationPolicyStore policy_store_; + + private: + MessageLoop loop_; + BrowserThread ui_thread_; + BrowserThread file_thread_; }; TEST_P(ConfigDirPolicyProviderValueTest, Default) { ConfigDirPolicyProvider provider( - ConfigurationPolicyPrefStore::GetChromePolicyValueMap(), test_dir_); - EXPECT_TRUE(provider.Provide(policy_store_.get())); - EXPECT_TRUE(policy_store_->policy_map().empty()); + ConfigurationPolicyPrefStore::GetChromePolicyDefinitionList(), + test_dir()); + EXPECT_TRUE(provider.Provide(&policy_store_)); + EXPECT_TRUE(policy_store_.policy_map().empty()); } TEST_P(ConfigDirPolicyProviderValueTest, NullValue) { @@ -314,9 +199,10 @@ TEST_P(ConfigDirPolicyProviderValueTest, NullValue) { dict.Set(GetParam().policy_key(), Value::CreateNullValue()); WriteConfigFile(dict, "empty"); ConfigDirPolicyProvider provider( - ConfigurationPolicyPrefStore::GetChromePolicyValueMap(), test_dir_); - EXPECT_TRUE(provider.Provide(policy_store_.get())); - EXPECT_TRUE(policy_store_->policy_map().empty()); + ConfigurationPolicyPrefStore::GetChromePolicyDefinitionList(), + test_dir()); + EXPECT_TRUE(provider.Provide(&policy_store_)); + EXPECT_TRUE(policy_store_.policy_map().empty()); } TEST_P(ConfigDirPolicyProviderValueTest, TestValue) { @@ -324,10 +210,11 @@ TEST_P(ConfigDirPolicyProviderValueTest, TestValue) { dict.Set(GetParam().policy_key(), GetParam().test_value()->DeepCopy()); WriteConfigFile(dict, "policy"); ConfigDirPolicyProvider provider( - ConfigurationPolicyPrefStore::GetChromePolicyValueMap(), test_dir_); - EXPECT_TRUE(provider.Provide(policy_store_.get())); - EXPECT_EQ(1U, policy_store_->policy_map().size()); - const Value* value = policy_store_->Get(GetParam().type()); + ConfigurationPolicyPrefStore::GetChromePolicyDefinitionList(), + test_dir()); + EXPECT_TRUE(provider.Provide(&policy_store_)); + EXPECT_EQ(1U, policy_store_.policy_map().size()); + const Value* value = policy_store_.Get(GetParam().type()); ASSERT_TRUE(value); EXPECT_TRUE(GetParam().test_value()->Equals(value)); } diff --git a/chrome/browser/policy/configuration_policy_pref_store.cc b/chrome/browser/policy/configuration_policy_pref_store.cc index cd27359..8a72ff6 100644 --- a/chrome/browser/policy/configuration_policy_pref_store.cc +++ b/chrome/browser/policy/configuration_policy_pref_store.cc @@ -52,38 +52,38 @@ class ConfigurationPolicyProviderKeeper { scoped_ptr<ConfigurationPolicyProvider> recommended_provider_; static ConfigurationPolicyProvider* CreateManagedProvider() { - const ConfigurationPolicyProvider::StaticPolicyValueMap& policy_map = - ConfigurationPolicyPrefStore::GetChromePolicyValueMap(); + const ConfigurationPolicyProvider::PolicyDefinitionList* policy_list = + ConfigurationPolicyPrefStore::GetChromePolicyDefinitionList(); #if defined(OS_WIN) - return new ConfigurationPolicyProviderWin(policy_map); + return new ConfigurationPolicyProviderWin(policy_list); #elif defined(OS_MACOSX) - return new ConfigurationPolicyProviderMac(policy_map); + return new ConfigurationPolicyProviderMac(policy_list); #elif defined(OS_POSIX) FilePath config_dir_path; if (PathService::Get(chrome::DIR_POLICY_FILES, &config_dir_path)) { - return new ConfigDirPolicyProvider(policy_map, + return new ConfigDirPolicyProvider(policy_list, config_dir_path.Append(FILE_PATH_LITERAL("managed"))); } else { - return new DummyConfigurationPolicyProvider(policy_map); + return new DummyConfigurationPolicyProvider(policy_list); } #else - return new DummyConfigurationPolicyProvider(policy_map); + return new DummyConfigurationPolicyProvider(policy_list); #endif } static ConfigurationPolicyProvider* CreateRecommendedProvider() { - const ConfigurationPolicyProvider::StaticPolicyValueMap& policy_map = - ConfigurationPolicyPrefStore::GetChromePolicyValueMap(); + const ConfigurationPolicyProvider::PolicyDefinitionList* policy_list = + ConfigurationPolicyPrefStore::GetChromePolicyDefinitionList(); #if defined(OS_POSIX) && !defined(OS_MACOSX) FilePath config_dir_path; if (PathService::Get(chrome::DIR_POLICY_FILES, &config_dir_path)) { - return new ConfigDirPolicyProvider(policy_map, + return new ConfigDirPolicyProvider(policy_list, config_dir_path.Append(FILE_PATH_LITERAL("recommended"))); } else { - return new DummyConfigurationPolicyProvider(policy_map); + return new DummyConfigurationPolicyProvider(policy_list); } #else - return new DummyConfigurationPolicyProvider(policy_map); + return new DummyConfigurationPolicyProvider(policy_list); #endif } @@ -168,9 +168,9 @@ const ConfigurationPolicyPrefStore::PolicyToPreferenceMapEntry }; /* static */ -ConfigurationPolicyProvider::StaticPolicyValueMap -ConfigurationPolicyPrefStore::GetChromePolicyValueMap() { - static ConfigurationPolicyProvider::StaticPolicyValueMap::Entry entries[] = { +ConfigurationPolicyProvider::PolicyDefinitionList* +ConfigurationPolicyPrefStore::GetChromePolicyDefinitionList() { + static ConfigurationPolicyProvider::PolicyDefinitionList::Entry entries[] = { { ConfigurationPolicyStore::kPolicyHomePage, Value::TYPE_STRING, key::kHomepageLocation }, { ConfigurationPolicyStore::kPolicyHomepageIsNewTabPage, @@ -250,21 +250,11 @@ ConfigurationPolicyPrefStore::GetChromePolicyValueMap() { #endif }; - ConfigurationPolicyProvider::StaticPolicyValueMap map = { - arraysize(entries), - entries + static ConfigurationPolicyProvider::PolicyDefinitionList policy_list = { + entries, + entries + arraysize(entries), }; - return map; -} - -void ConfigurationPolicyPrefStore::GetProxyPreferenceSet( - ProxyPreferenceSet* proxy_pref_set) { - proxy_pref_set->clear(); - for (size_t current = 0; current < arraysize(proxy_policy_map_); ++current) { - proxy_pref_set->insert(proxy_policy_map_[current].preference_path); - } - proxy_pref_set->insert(prefs::kNoProxyServer); - proxy_pref_set->insert(prefs::kProxyAutoDetect); + return &policy_list; } ConfigurationPolicyPrefStore::ConfigurationPolicyPrefStore( @@ -277,7 +267,8 @@ ConfigurationPolicyPrefStore::ConfigurationPolicyPrefStore( use_system_proxy_(false) { } -ConfigurationPolicyPrefStore::~ConfigurationPolicyPrefStore() {} +ConfigurationPolicyPrefStore::~ConfigurationPolicyPrefStore() { +} PrefStore::PrefReadError ConfigurationPolicyPrefStore::ReadPrefs() { proxy_disabled_ = false; @@ -329,6 +320,17 @@ ConfigurationPolicyPrefStore::CreateRecommendedPolicyPrefStore() { return new ConfigurationPolicyPrefStore(keeper->recommended_provider()); } +// static +void ConfigurationPolicyPrefStore::GetProxyPreferenceSet( + ProxyPreferenceSet* proxy_pref_set) { + proxy_pref_set->clear(); + for (size_t current = 0; current < arraysize(proxy_policy_map_); ++current) { + proxy_pref_set->insert(proxy_policy_map_[current].preference_path); + } + proxy_pref_set->insert(prefs::kNoProxyServer); + proxy_pref_set->insert(prefs::kProxyAutoDetect); +} + const ConfigurationPolicyPrefStore::PolicyToPreferenceMapEntry* ConfigurationPolicyPrefStore::FindPolicyInMap(PolicyType policy, const PolicyToPreferenceMapEntry* map, int table_size) { diff --git a/chrome/browser/policy/configuration_policy_pref_store.h b/chrome/browser/policy/configuration_policy_pref_store.h index 83ab847..85edeec 100644 --- a/chrome/browser/policy/configuration_policy_pref_store.h +++ b/chrome/browser/policy/configuration_policy_pref_store.h @@ -41,9 +41,9 @@ class ConfigurationPolicyPrefStore : public PrefStore, // Creates a ConfigurationPolicyPrefStore that reads recommended policy. static ConfigurationPolicyPrefStore* CreateRecommendedPolicyPrefStore(); - // Returns the default policy value map for Chrome. - static ConfigurationPolicyProvider::StaticPolicyValueMap - GetChromePolicyValueMap(); + // Returns the default policy definition list for Chrome. + static ConfigurationPolicyProvider::PolicyDefinitionList* + GetChromePolicyDefinitionList(); typedef std::set<const char*> ProxyPreferenceSet; @@ -64,8 +64,8 @@ class ConfigurationPolicyPrefStore : public PrefStore, static const PolicyToPreferenceMapEntry simple_policy_map_[]; static const PolicyToPreferenceMapEntry proxy_policy_map_[]; static const PolicyToPreferenceMapEntry default_search_policy_map_[]; - static const ConfigurationPolicyProvider::StaticPolicyValueMap - policy_value_map_; + static const ConfigurationPolicyProvider::PolicyDefinitionList + policy_definition_list_; ConfigurationPolicyProvider* provider_; scoped_ptr<DictionaryValue> prefs_; diff --git a/chrome/browser/policy/configuration_policy_provider.cc b/chrome/browser/policy/configuration_policy_provider.cc index 0c7f2fd..2b77db8 100644 --- a/chrome/browser/policy/configuration_policy_provider.cc +++ b/chrome/browser/policy/configuration_policy_provider.cc @@ -10,15 +10,8 @@ namespace policy { ConfigurationPolicyProvider::ConfigurationPolicyProvider( - const StaticPolicyValueMap& policy_map) { - for (size_t i = 0; i < policy_map.entry_count; ++i) { - PolicyValueMapEntry entry = { - policy_map.entries[i].policy_type, - policy_map.entries[i].value_type, - std::string(policy_map.entries[i].name) - }; - policy_value_map_.push_back(entry); - } + const PolicyDefinitionList* policy_list) + : policy_definition_list_(policy_list) { } ConfigurationPolicyProvider::~ConfigurationPolicyProvider() {} diff --git a/chrome/browser/policy/configuration_policy_provider.h b/chrome/browser/policy/configuration_policy_provider.h index 4115107..da8d69eb 100644 --- a/chrome/browser/policy/configuration_policy_provider.h +++ b/chrome/browser/policy/configuration_policy_provider.h @@ -6,9 +6,11 @@ #define CHROME_BROWSER_POLICY_CONFIGURATION_POLICY_PROVIDER_H_ #pragma once +#include <string> #include <vector> #include "base/basictypes.h" +#include "base/scoped_ptr.h" #include "base/values.h" #include "chrome/browser/policy/configuration_policy_store.h" @@ -21,18 +23,18 @@ class ConfigurationPolicyProvider { public: // Used for static arrays of policy values that is used to initialize an // instance of the ConfigurationPolicyProvider. - struct StaticPolicyValueMap { + struct PolicyDefinitionList { struct Entry { ConfigurationPolicyStore::PolicyType policy_type; Value::ValueType value_type; const char* name; }; - size_t entry_count; - const Entry* entries; + const Entry* begin; + const Entry* end; }; - explicit ConfigurationPolicyProvider(const StaticPolicyValueMap& policy_map); + explicit ConfigurationPolicyProvider(const PolicyDefinitionList* policy_list); virtual ~ConfigurationPolicyProvider(); @@ -51,21 +53,14 @@ class ConfigurationPolicyProvider { virtual void NotifyStoreOfPolicyChange(); protected: - // A structure mapping policies to their implementations by providers. - struct PolicyValueMapEntry { - ConfigurationPolicyStore::PolicyType policy_type; - Value::ValueType value_type; - std::string name; - }; - typedef std::vector<PolicyValueMapEntry> PolicyValueMap; - - const PolicyValueMap& policy_value_map() const { - return policy_value_map_; + const PolicyDefinitionList* policy_definition_list() const { + return policy_definition_list_; } private: // Contains the default mapping from policy values to the actual names. - PolicyValueMap policy_value_map_; + const ConfigurationPolicyProvider::PolicyDefinitionList* + policy_definition_list_; private: DISALLOW_COPY_AND_ASSIGN(ConfigurationPolicyProvider); diff --git a/chrome/browser/policy/configuration_policy_provider_mac.cc b/chrome/browser/policy/configuration_policy_provider_mac.cc index 552e33c..62b7436 100644 --- a/chrome/browser/policy/configuration_policy_provider_mac.cc +++ b/chrome/browser/policy/configuration_policy_provider_mac.cc @@ -4,28 +4,50 @@ #include "chrome/browser/policy/configuration_policy_provider_mac.h" +#include "base/file_util.h" #include "base/logging.h" #include "base/mac/scoped_cftyperef.h" +#include "base/path_service.h" #include "base/sys_string_conversions.h" +#include "chrome/common/chrome_paths.h" namespace policy { -ConfigurationPolicyProviderMac::ConfigurationPolicyProviderMac( - const StaticPolicyValueMap& policy_map) - : ConfigurationPolicyProvider(policy_map), - preferences_(new MacPreferences()) { +static FilePath GetManagedPolicyPath() { + // This constructs the path to the plist file in which Mac OS X stores the + // managed preference for the application. This is undocumented and therefore + // fragile, but if it doesn't work out, FileBasedPolicyLoader has a task that + // polls periodically in order to reload managed preferences later even if we + // missed the change. + FilePath path; + if (!PathService::Get(chrome::DIR_MANAGED_PREFS, &path)) + return FilePath(); + + CFBundleRef bundle(CFBundleGetMainBundle()); + if (!bundle) + return FilePath(); + + CFStringRef bundle_id = CFBundleGetIdentifier(bundle); + if (!bundle_id) + return FilePath(); + + return path.Append(base::SysCFStringRefToUTF8(bundle_id) + ".plist"); } -ConfigurationPolicyProviderMac::ConfigurationPolicyProviderMac( - const StaticPolicyValueMap& policy_map, MacPreferences* preferences) - : ConfigurationPolicyProvider(policy_map), preferences_(preferences) { +MacPreferencesPolicyLoader::MacPreferencesPolicyLoader( + MacPreferences* preferences, + const ConfigurationPolicyProvider::PolicyDefinitionList* policy_list) + : FileBasedPolicyProvider::Delegate(GetManagedPolicyPath()), + policy_list_(policy_list), + preferences_(preferences) { } -bool ConfigurationPolicyProviderMac::Provide(ConfigurationPolicyStore* store) { - const PolicyValueMap& mapping = policy_value_map(); +DictionaryValue* MacPreferencesPolicyLoader::Load() { + preferences_->AppSynchronize(kCFPreferencesCurrentApplication); + DictionaryValue* policy = new DictionaryValue; - for (PolicyValueMap::const_iterator current = mapping.begin(); - current != mapping.end(); ++current) { + const ConfigurationPolicyProvider::PolicyDefinitionList::Entry* current; + for (current = policy_list_->begin; current != policy_list_->end; ++current) { base::mac::ScopedCFTypeRef<CFStringRef> name( base::SysUTF8ToCFStringRef(current->name)); base::mac::ScopedCFTypeRef<CFPropertyListRef> value( @@ -40,16 +62,13 @@ bool ConfigurationPolicyProviderMac::Provide(ConfigurationPolicyStore* store) { if (CFGetTypeID(value) == CFStringGetTypeID()) { std::string string_value = base::SysCFStringRefToUTF8((CFStringRef)value.get()); - store->Apply( - current->policy_type, - Value::CreateStringValue(string_value)); + policy->SetString(current->name, string_value); } break; case Value::TYPE_BOOLEAN: if (CFGetTypeID(value) == CFBooleanGetTypeID()) { bool bool_value = CFBooleanGetValue((CFBooleanRef)value.get()); - store->Apply(current->policy_type, - Value::CreateBooleanValue(bool_value)); + policy->SetBoolean(current->name, bool_value); } break; case Value::TYPE_INTEGER: @@ -59,8 +78,7 @@ bool ConfigurationPolicyProviderMac::Provide(ConfigurationPolicyStore* store) { kCFNumberIntType, &int_value); if (cast) - store->Apply(current->policy_type, - Value::CreateIntegerValue(int_value)); + policy->SetInteger(current->name, int_value); } break; case Value::TYPE_LIST: @@ -81,16 +99,38 @@ bool ConfigurationPolicyProviderMac::Provide(ConfigurationPolicyStore* store) { } } if (valid_array) - store->Apply(current->policy_type, list_value.release()); + policy->Set(current->name, list_value.release()); } break; default: NOTREACHED(); - return false; } } - return true; + return policy; +} + +base::Time MacPreferencesPolicyLoader::GetLastModification() { + base::PlatformFileInfo file_info; + if (!file_util::GetFileInfo(config_file_path(), &file_info) || + file_info.is_directory) { + return base::Time(); + } + + return file_info.last_modified; +} + +ConfigurationPolicyProviderMac::ConfigurationPolicyProviderMac( + const ConfigurationPolicyProvider::PolicyDefinitionList* policy_list) + : FileBasedPolicyProvider(policy_list, + new MacPreferencesPolicyLoader(new MacPreferences, policy_list)) { +} + +ConfigurationPolicyProviderMac::ConfigurationPolicyProviderMac( + const ConfigurationPolicyProvider::PolicyDefinitionList* policy_list, + MacPreferences* preferences) + : FileBasedPolicyProvider(policy_list, + new MacPreferencesPolicyLoader(preferences, policy_list)) { } } // namespace policy diff --git a/chrome/browser/policy/configuration_policy_provider_mac.h b/chrome/browser/policy/configuration_policy_provider_mac.h index 4f0c4b1..0d6d15b 100644 --- a/chrome/browser/policy/configuration_policy_provider_mac.h +++ b/chrome/browser/policy/configuration_policy_provider_mac.h @@ -7,28 +7,49 @@ #pragma once #include "base/scoped_ptr.h" -#include "chrome/browser/policy/configuration_policy_store.h" -#include "chrome/browser/policy/configuration_policy_provider.h" +#include "chrome/browser/policy/file_based_policy_provider.h" #include "chrome/browser/preferences_mac.h" namespace policy { +// A policy loader implementation that read Mac OS X's managed preferences. +class MacPreferencesPolicyLoader : public FileBasedPolicyProvider::Delegate { + public: + // Takes ownership of |preferences|. + MacPreferencesPolicyLoader( + MacPreferences* preferences, + const ConfigurationPolicyProvider::PolicyDefinitionList* policy_list); + + // FileBasedPolicyLoader::Delegate implementation. + virtual DictionaryValue* Load(); + virtual base::Time GetLastModification(); + + private: + // In order to access the application preferences API, the names and values of + // the policies that are recognized must be known to the loader. + // Unfortunately, we cannot get the policy list at load time from the + // provider, because the loader may outlive the provider, so we store our own + // pointer to the list. + const ConfigurationPolicyProvider::PolicyDefinitionList* policy_list_; + + scoped_ptr<MacPreferences> preferences_; + + DISALLOW_COPY_AND_ASSIGN(MacPreferencesPolicyLoader); +}; + // An implementation of |ConfigurationPolicyProvider| using the mechanism // provided by Mac OS X's managed preferences. -class ConfigurationPolicyProviderMac : public ConfigurationPolicyProvider { +class ConfigurationPolicyProviderMac + : public FileBasedPolicyProvider { public: explicit ConfigurationPolicyProviderMac( - const StaticPolicyValueMap& policy_map); + const ConfigurationPolicyProvider::PolicyDefinitionList* policy_list); // For testing; takes ownership of |preferences|. - ConfigurationPolicyProviderMac(const StaticPolicyValueMap& policy_map, - MacPreferences* preferences); - virtual ~ConfigurationPolicyProviderMac() { } + ConfigurationPolicyProviderMac( + const ConfigurationPolicyProvider::PolicyDefinitionList* policy_list, + MacPreferences* preferences); - // ConfigurationPolicyProvider method overrides: - virtual bool Provide(ConfigurationPolicyStore* store); - - protected: - scoped_ptr<MacPreferences> preferences_; + DISALLOW_COPY_AND_ASSIGN(ConfigurationPolicyProviderMac); }; } // namespace policy diff --git a/chrome/browser/policy/configuration_policy_provider_mac_unittest.cc b/chrome/browser/policy/configuration_policy_provider_mac_unittest.cc index 86794a0..e081cdf 100644 --- a/chrome/browser/policy/configuration_policy_provider_mac_unittest.cc +++ b/chrome/browser/policy/configuration_policy_provider_mac_unittest.cc @@ -133,20 +133,17 @@ class ConfigurationPolicyProviderMacTest virtual void SetUp() { prefs_ = new MockPreferences; store_.reset(new MockConfigurationPolicyStore); - provider_.reset( - new ConfigurationPolicyProviderMac( - ConfigurationPolicyPrefStore::GetChromePolicyValueMap(), - prefs_)); } protected: MockPreferences* prefs_; scoped_ptr<MockConfigurationPolicyStore> store_; - scoped_ptr<ConfigurationPolicyProviderMac> provider_; }; TEST_P(ConfigurationPolicyProviderMacTest, Default) { - EXPECT_TRUE(provider_->Provide(store_.get())); + ConfigurationPolicyProviderMac provider( + ConfigurationPolicyPrefStore::GetChromePolicyDefinitionList(), prefs_); + EXPECT_TRUE(provider.Provide(store_.get())); EXPECT_TRUE(store_->policy_map().empty()); } @@ -156,7 +153,11 @@ TEST_P(ConfigurationPolicyProviderMacTest, Invalid) { base::mac::ScopedCFTypeRef<CFDataRef> invalid_data( CFDataCreate(NULL, NULL, 0)); prefs_->AddTestItem(name, invalid_data.get(), true); - EXPECT_TRUE(provider_->Provide(store_.get())); + + // Create the provider and have it read |prefs_|. + ConfigurationPolicyProviderMac provider( + ConfigurationPolicyPrefStore::GetChromePolicyDefinitionList(), prefs_); + EXPECT_TRUE(provider.Provide(store_.get())); EXPECT_TRUE(store_->policy_map().empty()); } @@ -167,7 +168,11 @@ TEST_P(ConfigurationPolicyProviderMacTest, TestNonForcedValue) { GetParam().GetPropertyListValue()); ASSERT_TRUE(test_value.get()); prefs_->AddTestItem(name, test_value.get(), false); - EXPECT_TRUE(provider_->Provide(store_.get())); + + // Create the provider and have it read |prefs_|. + ConfigurationPolicyProviderMac provider( + ConfigurationPolicyPrefStore::GetChromePolicyDefinitionList(), prefs_); + EXPECT_TRUE(provider.Provide(store_.get())); EXPECT_TRUE(store_->policy_map().empty()); } @@ -178,7 +183,11 @@ TEST_P(ConfigurationPolicyProviderMacTest, TestValue) { GetParam().GetPropertyListValue()); ASSERT_TRUE(test_value.get()); prefs_->AddTestItem(name, test_value, true); - EXPECT_TRUE(provider_->Provide(store_.get())); + + // Create the provider and have it read |prefs_|. + ConfigurationPolicyProviderMac provider( + ConfigurationPolicyPrefStore::GetChromePolicyDefinitionList(), prefs_); + EXPECT_TRUE(provider.Provide(store_.get())); ASSERT_EQ(1U, store_->policy_map().size()); const Value* value = store_->Get(GetParam().type()); ASSERT_TRUE(value); diff --git a/chrome/browser/policy/configuration_policy_provider_win.cc b/chrome/browser/policy/configuration_policy_provider_win.cc index c1ae3705..50e2fc5 100644 --- a/chrome/browser/policy/configuration_policy_provider_win.cc +++ b/chrome/browser/policy/configuration_policy_provider_win.cc @@ -156,8 +156,8 @@ void ConfigurationPolicyProviderWin::GroupPolicyChangeWatcher:: } ConfigurationPolicyProviderWin::ConfigurationPolicyProviderWin( - const StaticPolicyValueMap& policy_map) - : ConfigurationPolicyProvider(policy_map) { + const PolicyDefinitionList* policy_list) + : ConfigurationPolicyProvider(policy_list) { watcher_ = new GroupPolicyChangeWatcher(this->AsWeakPtr(), kReloadIntervalMinutes); watcher_->Start(); @@ -239,9 +239,9 @@ bool ConfigurationPolicyProviderWin::GetRegistryPolicyInteger( bool ConfigurationPolicyProviderWin::Provide( ConfigurationPolicyStore* store) { - const PolicyValueMap& mapping(policy_value_map()); - for (PolicyValueMap::const_iterator current = mapping.begin(); - current != mapping.end(); ++current) { + const PolicyDefinitionList* policy_list(policy_definition_list()); + for (const PolicyDefinitionList::Entry* current = policy_list->begin; + current != policy_list->end; ++current) { std::wstring name = UTF8ToWide(current->name); switch (current->value_type) { case Value::TYPE_STRING: { diff --git a/chrome/browser/policy/configuration_policy_provider_win.h b/chrome/browser/policy/configuration_policy_provider_win.h index 26bc3e6..c30f447 100644 --- a/chrome/browser/policy/configuration_policy_provider_win.h +++ b/chrome/browser/policy/configuration_policy_provider_win.h @@ -84,7 +84,7 @@ class ConfigurationPolicyProviderWin }; explicit ConfigurationPolicyProviderWin( - const StaticPolicyValueMap& policy_map); + const PolicyDefinitionList* policy_list); virtual ~ConfigurationPolicyProviderWin(); // ConfigurationPolicyProvider method overrides: diff --git a/chrome/browser/policy/configuration_policy_provider_win_unittest.cc b/chrome/browser/policy/configuration_policy_provider_win_unittest.cc index f7a3524..d0cda3b 100644 --- a/chrome/browser/policy/configuration_policy_provider_win_unittest.cc +++ b/chrome/browser/policy/configuration_policy_provider_win_unittest.cc @@ -183,9 +183,8 @@ void ConfigurationPolicyProviderWinTest::SetUp() { ActivateOverrides(); store_.reset(new MockConfigurationPolicyStore); - provider_.reset( - new ConfigurationPolicyProviderWin( - ConfigurationPolicyPrefStore::GetChromePolicyValueMap())); + provider_.reset(new ConfigurationPolicyProviderWin( + ConfigurationPolicyPrefStore::GetChromePolicyDefinitionList())); } void ConfigurationPolicyProviderWinTest::TearDown() { diff --git a/chrome/browser/policy/dummy_configuration_policy_provider.h b/chrome/browser/policy/dummy_configuration_policy_provider.h index fde6c40..46ab2fb 100644 --- a/chrome/browser/policy/dummy_configuration_policy_provider.h +++ b/chrome/browser/policy/dummy_configuration_policy_provider.h @@ -14,8 +14,8 @@ namespace policy { class DummyConfigurationPolicyProvider : public ConfigurationPolicyProvider { public: explicit DummyConfigurationPolicyProvider( - const StaticPolicyValueMap& policy_map) - : ConfigurationPolicyProvider(policy_map) { + const PolicyDefinitionList* policy_list) + : ConfigurationPolicyProvider(policy_list) { } virtual ~DummyConfigurationPolicyProvider() {} diff --git a/chrome/browser/policy/file_based_policy_provider.cc b/chrome/browser/policy/file_based_policy_provider.cc new file mode 100644 index 0000000..285b918 --- /dev/null +++ b/chrome/browser/policy/file_based_policy_provider.cc @@ -0,0 +1,258 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/policy/file_based_policy_provider.h" + +#include <set> + +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/task.h" +#include "base/utf_string_conversions.h" +#include "base/values.h" +#include "chrome/browser/browser_thread.h" +#include "chrome/common/json_value_serializer.h" + +namespace policy { + +// Amount of time we wait for the files on disk to settle before trying to load +// it. This alleviates the problem of reading partially written files and allows +// to batch quasi-simultaneous changes. +const int kSettleIntervalSeconds = 5; + +// The time interval for rechecking policy. This is our fallback in case the +// file path watch fails or doesn't report a change. +const int kReloadIntervalMinutes = 15; + +// FileBasedPolicyProvider implementation: + +FileBasedPolicyProvider::Delegate::~Delegate() { +} + +FileBasedPolicyProvider::Delegate::Delegate(const FilePath& config_file_path) + : config_file_path_(config_file_path) { +} + +FileBasedPolicyProvider::FileBasedPolicyProvider( + const ConfigurationPolicyProvider::PolicyDefinitionList* policy_list, + FileBasedPolicyProvider::Delegate* delegate) + : ConfigurationPolicyProvider(policy_list) { + loader_ = new FileBasedPolicyLoader(AsWeakPtr(), + delegate, + kSettleIntervalSeconds, + kReloadIntervalMinutes); + watcher_ = new FileBasedPolicyWatcher; + watcher_->Init(loader_.get()); +} + +FileBasedPolicyProvider::~FileBasedPolicyProvider() { + loader_->Stop(); +} + +bool FileBasedPolicyProvider::Provide(ConfigurationPolicyStore* store) { + scoped_ptr<DictionaryValue> policy(loader_->GetPolicy()); + DCHECK(policy.get()); + DecodePolicyValueTree(policy.get(), store); + return true; +} + +void FileBasedPolicyProvider::DecodePolicyValueTree( + DictionaryValue* policies, + ConfigurationPolicyStore* store) { + const PolicyDefinitionList* policy_list(policy_definition_list()); + for (const PolicyDefinitionList::Entry* i = policy_list->begin; + i != policy_list->end; ++i) { + Value* value; + if (policies->Get(i->name, &value) && value->IsType(i->value_type)) + store->Apply(i->policy_type, value->DeepCopy()); + } + + // TODO(mnissler): Handle preference overrides once |ConfigurationPolicyStore| + // supports it. +} + +// FileBasedPolicyLoader implementation: + +FileBasedPolicyLoader::FileBasedPolicyLoader( + base::WeakPtr<ConfigurationPolicyProvider> provider, + FileBasedPolicyProvider::Delegate* delegate, + int settle_interval_seconds, + int reload_interval_minutes) + : delegate_(delegate), + provider_(provider), + origin_loop_(MessageLoop::current()), + reload_task_(NULL), + settle_interval_seconds_(settle_interval_seconds), + reload_interval_minutes_(reload_interval_minutes) { + // Force an initial load, so GetPolicy() works. + policy_.reset(delegate_->Load()); + DCHECK(policy_.get()); +} + +void FileBasedPolicyLoader::Stop() { + if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) { + BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, + NewRunnableMethod(this, &FileBasedPolicyLoader::Stop)); + return; + } + + if (reload_task_) { + reload_task_->Cancel(); + reload_task_ = NULL; + } +} + +void FileBasedPolicyLoader::Reload() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + + // Check the directory time in order to see whether a reload is required. + base::TimeDelta delay; + base::Time now = base::Time::Now(); + if (!IsSafeToReloadPolicy(now, &delay)) { + ScheduleReloadTask(delay); + return; + } + + // Load the policy definitions. + scoped_ptr<DictionaryValue> new_policy(delegate_->Load()); + + // Check again in case the directory has changed while reading it. + if (!IsSafeToReloadPolicy(now, &delay)) { + ScheduleReloadTask(delay); + return; + } + + // Replace policy definition. + bool changed = false; + { + AutoLock lock(lock_); + changed = !policy_->Equals(new_policy.get()); + policy_.reset(new_policy.release()); + } + + // There's a change, report it! + if (changed) { + VLOG(0) << "Policy reload from " << config_file_path().value() + << " succeeded."; + origin_loop_->PostTask(FROM_HERE, + NewRunnableMethod(this, &FileBasedPolicyLoader::NotifyPolicyChanged)); + } + + // As a safeguard in case the file watcher fails, schedule a reload task + // that'll make us recheck after a reasonable interval. + ScheduleReloadTask(base::TimeDelta::FromMinutes(reload_interval_minutes_)); +} + +DictionaryValue* FileBasedPolicyLoader::GetPolicy() { + AutoLock lock(lock_); + return static_cast<DictionaryValue*>(policy_->DeepCopy()); +} + +void FileBasedPolicyLoader::OnFilePathChanged(const FilePath& path) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + Reload(); +} + +void FileBasedPolicyLoader::OnError() { + LOG(ERROR) << "FilePathWatcher on " << config_file_path().value() + << " failed."; +} + +FileBasedPolicyLoader::~FileBasedPolicyLoader() { +} + +bool FileBasedPolicyLoader::IsSafeToReloadPolicy(const base::Time& now, + base::TimeDelta* delay) { + DCHECK(delay); + + // A null modification time indicates there's no data. + base::Time last_modification(delegate_->GetLastModification()); + if (last_modification.is_null()) + return true; + + // If there was a change since the last recorded modification, wait some more. + base::TimeDelta settleInterval( + base::TimeDelta::FromSeconds(settle_interval_seconds_)); + if (last_modification != last_modification_file_) { + last_modification_file_ = last_modification; + last_modification_clock_ = now; + *delay = settleInterval; + return false; + } + + // Check whether the settle interval has elapsed. + base::TimeDelta age = now - last_modification_clock_; + if (age < settleInterval) { + *delay = settleInterval - age; + return false; + } + + return true; +} + +void FileBasedPolicyLoader::ScheduleReloadTask(const base::TimeDelta& delay) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + + if (reload_task_) + reload_task_->Cancel(); + + reload_task_ = + NewRunnableMethod(this, &FileBasedPolicyLoader::ReloadFromTask); + BrowserThread::PostDelayedTask(BrowserThread::FILE, FROM_HERE, reload_task_, + delay.InMilliseconds()); +} + +void FileBasedPolicyLoader::NotifyPolicyChanged() { + DCHECK_EQ(origin_loop_, MessageLoop::current()); + if (provider_) + provider_->NotifyStoreOfPolicyChange(); +} + +void FileBasedPolicyLoader::ReloadFromTask() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + + // Drop the reference to the reload task, since the task might be the only + // referer that keeps us alive, so we should not Cancel() it. + reload_task_ = NULL; + + Reload(); +} + +// FileBasedPolicyWatcher implementation: + +FileBasedPolicyWatcher::FileBasedPolicyWatcher() { +} + +void FileBasedPolicyWatcher::Init(FileBasedPolicyLoader* loader) { + // Initialization can happen early when the file thread is not yet available. + // So post a task to ourselves on the UI thread which will run after threading + // is up and schedule watch initialization on the file thread. + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + NewRunnableMethod(this, + &FileBasedPolicyWatcher::InitWatcher, + scoped_refptr<FileBasedPolicyLoader>(loader))); +} + +FileBasedPolicyWatcher::~FileBasedPolicyWatcher() { +} + +void FileBasedPolicyWatcher::InitWatcher( + const scoped_refptr<FileBasedPolicyLoader>& loader) { + if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) { + BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, + NewRunnableMethod(this, &FileBasedPolicyWatcher::InitWatcher, loader)); + return; + } + + if (!loader->config_file_path().empty() && + !watcher_.Watch(loader->config_file_path(), loader.get())) + loader->OnError(); + + // There might have been changes to the directory in the time between + // construction of the loader and initialization of the watcher. Call reload + // to detect if that is the case. + loader->Reload(); +} + +} // namespace policy diff --git a/chrome/browser/policy/file_based_policy_provider.h b/chrome/browser/policy/file_based_policy_provider.h new file mode 100644 index 0000000..2f3dfae --- /dev/null +++ b/chrome/browser/policy/file_based_policy_provider.h @@ -0,0 +1,203 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_POLICY_FILE_BASED_POLICY_PROVIDER_H_ +#define CHROME_BROWSER_POLICY_FILE_BASED_POLICY_PROVIDER_H_ +#pragma once + +#include "base/basictypes.h" +#include "base/file_path.h" +#include "base/lock.h" +#include "base/ref_counted.h" +#include "base/scoped_ptr.h" +#include "base/time.h" +#include "base/weak_ptr.h" +#include "chrome/browser/file_path_watcher.h" +#include "chrome/browser/policy/configuration_policy_provider.h" + +class CancelableTask; +class DictionaryValue; +class MessageLoop; + +namespace policy { + +class FileBasedPolicyLoader; +class FileBasedPolicyWatcher; + +// File based policy provider that coordinates watching and reloading policy +// information from the configuration path. Actual logic for loading policy +// information is handled by a delegate passed at construction time. +class FileBasedPolicyProvider + : public ConfigurationPolicyProvider, + public base::SupportsWeakPtr<FileBasedPolicyProvider> { + public: + // Delegate interface for actual policy loading from the system. + class Delegate { + public: + virtual ~Delegate(); + + // Loads the policy information. Ownership of the return value is + // transferred to the caller. + virtual DictionaryValue* Load() = 0; + + // Gets the last modification timestamp for the policy information from the + // filesystem. Returns base::Time() if the information is not present, in + // which case Load() should return an empty dictionary. + virtual base::Time GetLastModification() = 0; + + const FilePath& config_file_path() { return config_file_path_; } + + protected: + explicit Delegate(const FilePath& config_file_path); + + private: + // The path at which we look for configuration files. + const FilePath config_file_path_; + + DISALLOW_COPY_AND_ASSIGN(Delegate); + }; + + // Assumes ownership of |delegate|. + FileBasedPolicyProvider(const PolicyDefinitionList* policy_list, + Delegate* delegate); + virtual ~FileBasedPolicyProvider(); + + // ConfigurationPolicyProvider implementation. + virtual bool Provide(ConfigurationPolicyStore* store); + + private: + // Decodes the value tree and writes the configuration to the given |store|. + void DecodePolicyValueTree(DictionaryValue* policies, + ConfigurationPolicyStore* store); + + // Watches for changes to the configuration directory. + scoped_refptr<FileBasedPolicyWatcher> watcher_; + + // The loader object we use internally. + scoped_refptr<FileBasedPolicyLoader> loader_; + + DISALLOW_COPY_AND_ASSIGN(FileBasedPolicyProvider); +}; + +// FilePathWatcher delegate implementation that handles change notifications for +// the configuration file or directory. It keeps the authorative version of the +// currently effective policy dictionary and updates it as appropriate. The +// actual loading logic is handled by a delegate. +class FileBasedPolicyLoader : public FilePathWatcher::Delegate { + public: + // Creates a new loader that'll load its data from |config_file_path|. + // Assumes ownership of |delegate|, which provides the actual loading logic. + // The parameters |settle_interval_seconds| and |reload_interval_minutes| + // specify the time to wait before reading the file contents after a change + // and the period for checking |config_file_path| for changes, respectively. + FileBasedPolicyLoader(base::WeakPtr<ConfigurationPolicyProvider> provider, + FileBasedPolicyProvider::Delegate* delegate, + int settle_interval_seconds, + int reload_interval_minutes); + + // Stops any pending reload tasks. + void Stop(); + + // Reloads the policies and sends out a notification, if appropriate. Must be + // called on the file thread. + void Reload(); + + // Gets the current dictionary value object. Ownership of the returned value + // is transferred to the caller. + DictionaryValue* GetPolicy(); + + const FilePath& config_file_path() { return delegate_->config_file_path(); } + + // FilePathWatcher::Delegate implementation: + void OnFilePathChanged(const FilePath& path); + void OnError(); + + private: + // FileBasedPolicyLoader objects should only be deleted by + // RefCountedThreadSafe. + friend class base::RefCountedThreadSafe<FileBasedPolicyLoader>; + virtual ~FileBasedPolicyLoader(); + + // Checks whether reading policy information is safe to do. If not, returns + // false and the delay until it is considered safe to reload in |delay|. + bool IsSafeToReloadPolicy(const base::Time& now, base::TimeDelta* delay); + + // Schedules a reload task to run when |delay| expires. Must be called on the + // file thread. + void ScheduleReloadTask(const base::TimeDelta& delay); + + // Notifies the policy provider to send out a policy changed notification. + // Must be called on |origin_loop_|. + void NotifyPolicyChanged(); + + // Invoked from the reload task on the file thread. + void ReloadFromTask(); + + // The delegate. + scoped_ptr<FileBasedPolicyProvider::Delegate> delegate_; + + // The provider this loader is associated with. Access only on the thread that + // called the constructor. See |origin_loop_| below. + base::WeakPtr<ConfigurationPolicyProvider> provider_; + + // The message loop on which this object was constructed and |provider_| + // received on. Recorded so we can call back into the non thread safe provider + // to fire the notification. + MessageLoop* origin_loop_; + + // Records last known modification timestamp of |config_file_path_|. + base::Time last_modification_file_; + + // The wall clock time at which the last modification timestamp was recorded. + // It's better to not assume the file notification time and the wall clock + // times come from the same source, just in case there is some non-local + // filesystem involved. + base::Time last_modification_clock_; + + // Protects |policy_|. + Lock lock_; + + // The current policy definition. + scoped_ptr<DictionaryValue> policy_; + + // The reload task. Access only on the file thread. Holds a reference to the + // currently posted task, so we can cancel and repost it if necessary. + CancelableTask* reload_task_; + + // Settle and reload intervals. + const int settle_interval_seconds_; + const int reload_interval_minutes_; + + DISALLOW_COPY_AND_ASSIGN(FileBasedPolicyLoader); +}; + +// Wraps a FilePathWatcher for the configuration path and takes care of +// initializing the watcher object on the file thread. +class FileBasedPolicyWatcher + : public base::RefCountedThreadSafe<FileBasedPolicyWatcher> { + public: + FileBasedPolicyWatcher(); + + // Runs initialization. This is in a separate method since we need to post a + // task (which cannot be done from the constructor). + void Init(FileBasedPolicyLoader* loader); + + private: + // FileBasedPolicyWatcher objects should only be deleted by + // RefCountedThreadSafe. + friend class base::RefCountedThreadSafe<FileBasedPolicyWatcher>; + virtual ~FileBasedPolicyWatcher(); + + // Actually sets up the watch with the FilePathWatcher code. + void InitWatcher(const scoped_refptr<FileBasedPolicyLoader>& loader); + + // Wrapped watcher that takes care of the actual watching. + FilePathWatcher watcher_; + + DISALLOW_COPY_AND_ASSIGN(FileBasedPolicyWatcher); +}; + +} // namespace policy + +#endif // CHROME_BROWSER_POLICY_FILE_BASED_POLICY_PROVIDER_H_ diff --git a/chrome/browser/policy/file_based_policy_provider_unittest.cc b/chrome/browser/policy/file_based_policy_provider_unittest.cc new file mode 100644 index 0000000..1aa812d --- /dev/null +++ b/chrome/browser/policy/file_based_policy_provider_unittest.cc @@ -0,0 +1,131 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/policy/configuration_policy_pref_store.h" +#include "chrome/browser/policy/file_based_policy_provider.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::Mock; + +namespace policy { + +// Shorter reload intervals for testing FileBasedPolicyLoader. +const int kSettleIntervalSecondsForTesting = 0; +const int kReloadIntervalMinutesForTesting = 1; + +// A delegate for testing that can feed arbitrary information to the loader. +class TestDelegate : public FileBasedPolicyProvider::Delegate { + public: + TestDelegate() + : FileBasedPolicyProvider::Delegate(FilePath(FILE_PATH_LITERAL("fake"))) { + } + + // FileBasedPolicyProvider::Delegate implementation: + virtual DictionaryValue* Load() { + return static_cast<DictionaryValue*>(dict_.DeepCopy()); + } + + virtual base::Time GetLastModification() { + return last_modification_; + } + + DictionaryValue* dict() { return &dict_; } + void set_last_modification(const base::Time& last_modification) { + last_modification_ = last_modification; + } + + private: + DictionaryValue dict_; + base::Time last_modification_; +}; + +// A mock provider that allows us to capture reload notifications. +class MockPolicyProvider : public ConfigurationPolicyProvider, + public base::SupportsWeakPtr<MockPolicyProvider> { + public: + explicit MockPolicyProvider() + : ConfigurationPolicyProvider( + ConfigurationPolicyPrefStore::GetChromePolicyDefinitionList()) { + } + + virtual bool Provide(ConfigurationPolicyStore* store) { + return true; + } + + MOCK_METHOD0(NotifyStoreOfPolicyChange, void()); +}; + +class FileBasedPolicyLoaderTest : public testing::Test { + protected: + FileBasedPolicyLoaderTest() + : ui_thread_(BrowserThread::UI, &loop_), + file_thread_(BrowserThread::FILE, &loop_) {} + + virtual void TearDown() { + loop_.RunAllPending(); + } + + MessageLoop loop_; + + private: + BrowserThread ui_thread_; + BrowserThread file_thread_; +}; + +TEST_F(FileBasedPolicyLoaderTest, BasicLoad) { + TestDelegate* test_delegate = new TestDelegate; + test_delegate->dict()->SetString("HomepageLocation", "http://www.google.com"); + + scoped_refptr<FileBasedPolicyLoader> loader( + new FileBasedPolicyLoader(base::WeakPtr<FileBasedPolicyProvider>(), + test_delegate, + kSettleIntervalSecondsForTesting, + kReloadIntervalMinutesForTesting)); + scoped_ptr<DictionaryValue> policy(loader->GetPolicy()); + EXPECT_TRUE(policy.get()); + EXPECT_EQ(1U, policy->size()); + + std::string str_value; + EXPECT_TRUE(policy->GetString("HomepageLocation", &str_value)); + EXPECT_EQ("http://www.google.com", str_value); + + loader->Stop(); +} + +TEST_F(FileBasedPolicyLoaderTest, TestRefresh) { + MockPolicyProvider provider; + TestDelegate* test_delegate = new TestDelegate; + + scoped_refptr<FileBasedPolicyLoader> loader( + new FileBasedPolicyLoader(provider.AsWeakPtr(), + test_delegate, + kSettleIntervalSecondsForTesting, + kReloadIntervalMinutesForTesting)); + scoped_ptr<DictionaryValue> policy(loader->GetPolicy()); + EXPECT_TRUE(policy.get()); + EXPECT_EQ(0U, policy->size()); + + test_delegate->dict()->SetString("HomepageLocation", "http://www.google.com"); + + EXPECT_CALL(provider, NotifyStoreOfPolicyChange()).Times(1); + loader->OnFilePathChanged(FilePath(FILE_PATH_LITERAL("fake"))); + + // Run the loop. The refresh should be handled immediately since the settle + // interval has been disabled. + loop_.RunAllPending(); + Mock::VerifyAndClearExpectations(&provider); + + policy.reset(loader->GetPolicy()); + EXPECT_TRUE(policy.get()); + EXPECT_EQ(1U, policy->size()); + + std::string str_value; + EXPECT_TRUE(policy->GetString("HomepageLocation", &str_value)); + EXPECT_EQ("http://www.google.com", str_value); + + loader->Stop(); +} + +} // namespace policy diff --git a/chrome/browser/policy/mock_configuration_policy_provider.h b/chrome/browser/policy/mock_configuration_policy_provider.h index 6b23613..a7fe270 100644 --- a/chrome/browser/policy/mock_configuration_policy_provider.h +++ b/chrome/browser/policy/mock_configuration_policy_provider.h @@ -20,7 +20,7 @@ class MockConfigurationPolicyProvider : public ConfigurationPolicyProvider { public: MockConfigurationPolicyProvider() : ConfigurationPolicyProvider( - ConfigurationPolicyPrefStore::GetChromePolicyValueMap()) { + ConfigurationPolicyPrefStore::GetChromePolicyDefinitionList()) { } ~MockConfigurationPolicyProvider() { STLDeleteValues(&policy_map_); |
