summaryrefslogtreecommitdiffstats
path: root/chrome/browser/policy
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/policy')
-rw-r--r--chrome/browser/policy/config_dir_policy_provider.cc246
-rw-r--r--chrome/browser/policy/config_dir_policy_provider.h166
-rw-r--r--chrome/browser/policy/config_dir_policy_provider_unittest.cc229
-rw-r--r--chrome/browser/policy/configuration_policy_pref_store.cc62
-rw-r--r--chrome/browser/policy/configuration_policy_pref_store.h10
-rw-r--r--chrome/browser/policy/configuration_policy_provider.cc11
-rw-r--r--chrome/browser/policy/configuration_policy_provider.h25
-rw-r--r--chrome/browser/policy/configuration_policy_provider_mac.cc82
-rw-r--r--chrome/browser/policy/configuration_policy_provider_mac.h45
-rw-r--r--chrome/browser/policy/configuration_policy_provider_mac_unittest.cc27
-rw-r--r--chrome/browser/policy/configuration_policy_provider_win.cc10
-rw-r--r--chrome/browser/policy/configuration_policy_provider_win.h2
-rw-r--r--chrome/browser/policy/configuration_policy_provider_win_unittest.cc5
-rw-r--r--chrome/browser/policy/dummy_configuration_policy_provider.h4
-rw-r--r--chrome/browser/policy/file_based_policy_provider.cc258
-rw-r--r--chrome/browser/policy/file_based_policy_provider.h203
-rw-r--r--chrome/browser/policy/file_based_policy_provider_unittest.cc131
-rw-r--r--chrome/browser/policy/mock_configuration_policy_provider.h2
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_);