summaryrefslogtreecommitdiffstats
path: root/chrome/browser/policy
diff options
context:
space:
mode:
authorjoaodasilva@chromium.org <joaodasilva@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-06-28 11:22:06 +0000
committerjoaodasilva@chromium.org <joaodasilva@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-06-28 11:22:06 +0000
commit00591dd79fb7232c54b24d64c84e978b3944858a (patch)
treeda3e4b51c2f8570d40ababe92e11c454c6a29ee9 /chrome/browser/policy
parentda9011c6377f05411065d9ed49b7a8cee9a0c66f (diff)
downloadchromium_src-00591dd79fb7232c54b24d64c84e978b3944858a.zip
chromium_src-00591dd79fb7232c54b24d64c84e978b3944858a.tar.gz
chromium_src-00591dd79fb7232c54b24d64c84e978b3944858a.tar.bz2
Use a schema to decode 3rd party policy on windows, when present.
BUG=108994 TEST=unit_tests green Review URL: https://chromiumcodereview.appspot.com/10656046 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@144703 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/policy')
-rw-r--r--chrome/browser/policy/config_dir_policy_loader_unittest.cc83
-rw-r--r--chrome/browser/policy/configuration_policy_provider_test.cc67
-rw-r--r--chrome/browser/policy/configuration_policy_provider_test.h17
-rw-r--r--chrome/browser/policy/policy_loader_mac_unittest.cc3
-rw-r--r--chrome/browser/policy/policy_loader_win.cc368
-rw-r--r--chrome/browser/policy/policy_loader_win.h26
-rw-r--r--chrome/browser/policy/policy_loader_win_unittest.cc366
7 files changed, 758 insertions, 172 deletions
diff --git a/chrome/browser/policy/config_dir_policy_loader_unittest.cc b/chrome/browser/policy/config_dir_policy_loader_unittest.cc
index 6c996d8..a2e7b91 100644
--- a/chrome/browser/policy/config_dir_policy_loader_unittest.cc
+++ b/chrome/browser/policy/config_dir_policy_loader_unittest.cc
@@ -45,6 +45,8 @@ class TestHarness : public PolicyProviderTestHarness {
virtual void InstallDictionaryPolicy(
const std::string& policy_name,
const base::DictionaryValue* policy_value) OVERRIDE;
+ virtual void Install3rdPartyPolicy(
+ const base::DictionaryValue* policies) OVERRIDE;
const FilePath& test_dir() { return test_dir_.path(); }
@@ -52,16 +54,22 @@ class TestHarness : public PolicyProviderTestHarness {
void WriteConfigFile(const base::DictionaryValue& dict,
const std::string& file_name);
+ // Returns a unique name for a policy file. Each subsequent call returns a new
+ // name that comes lexicographically after the previous one.
+ std::string NextConfigFileName();
+
static PolicyProviderTestHarness* Create();
private:
ScopedTempDir test_dir_;
+ int next_policy_file_index_;
DISALLOW_COPY_AND_ASSIGN(TestHarness);
};
TestHarness::TestHarness()
- : PolicyProviderTestHarness(POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE) {}
+ : PolicyProviderTestHarness(POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE),
+ next_policy_file_index_(100) {}
TestHarness::~TestHarness() {}
@@ -78,35 +86,35 @@ ConfigurationPolicyProvider* TestHarness::CreateProvider(
void TestHarness::InstallEmptyPolicy() {
base::DictionaryValue dict;
- WriteConfigFile(dict, "policy");
+ WriteConfigFile(dict, NextConfigFileName());
}
void TestHarness::InstallStringPolicy(const std::string& policy_name,
const std::string& policy_value) {
base::DictionaryValue dict;
dict.SetString(policy_name, policy_value);
- WriteConfigFile(dict, "policy");
+ WriteConfigFile(dict, NextConfigFileName());
}
void TestHarness::InstallIntegerPolicy(const std::string& policy_name,
int policy_value) {
base::DictionaryValue dict;
dict.SetInteger(policy_name, policy_value);
- WriteConfigFile(dict, "policy");
+ WriteConfigFile(dict, NextConfigFileName());
}
void TestHarness::InstallBooleanPolicy(const std::string& policy_name,
bool policy_value) {
base::DictionaryValue dict;
dict.SetBoolean(policy_name, policy_value);
- WriteConfigFile(dict, "policy");
+ WriteConfigFile(dict, NextConfigFileName());
}
void TestHarness::InstallStringListPolicy(const std::string& policy_name,
const base::ListValue* policy_value) {
base::DictionaryValue dict;
dict.Set(policy_name, policy_value->DeepCopy());
- WriteConfigFile(dict, "policy");
+ WriteConfigFile(dict, NextConfigFileName());
}
void TestHarness::InstallDictionaryPolicy(
@@ -114,7 +122,13 @@ void TestHarness::InstallDictionaryPolicy(
const base::DictionaryValue* policy_value) {
base::DictionaryValue dict;
dict.Set(policy_name, policy_value->DeepCopy());
- WriteConfigFile(dict, "policy");
+ WriteConfigFile(dict, NextConfigFileName());
+}
+
+void TestHarness::Install3rdPartyPolicy(const base::DictionaryValue* policies) {
+ base::DictionaryValue dict;
+ dict.Set("3rdparty", policies->DeepCopy());
+ WriteConfigFile(dict, NextConfigFileName());
}
void TestHarness::WriteConfigFile(const base::DictionaryValue& dict,
@@ -129,6 +143,11 @@ void TestHarness::WriteConfigFile(const base::DictionaryValue& dict,
file_util::WriteFile(file_path, data.c_str(), data.size()));
}
+std::string TestHarness::NextConfigFileName() {
+ EXPECT_LE(next_policy_file_index_, 999);
+ return std::string("policy") + base::IntToString(next_policy_file_index_++);
+}
+
// static
PolicyProviderTestHarness* TestHarness::Create() {
return new TestHarness();
@@ -142,6 +161,12 @@ INSTANTIATE_TEST_CASE_P(
ConfigurationPolicyProviderTest,
testing::Values(TestHarness::Create));
+// Instantiate abstract test case for 3rd party policy reading tests.
+INSTANTIATE_TEST_CASE_P(
+ ConfigDir3rdPartyPolicyLoaderTest,
+ Configuration3rdPartyPolicyProviderTest,
+ testing::Values(TestHarness::Create));
+
// Some tests that exercise special functionality in ConfigDirPolicyLoader.
class ConfigDirPolicyLoaderTest : public PolicyTestBase {
protected:
@@ -200,48 +225,4 @@ TEST_F(ConfigDirPolicyLoaderTest, ReadPrefsMergePrefs) {
EXPECT_TRUE(bundle->Equals(expected_bundle));
}
-// Tests loading of policy for 3rd parties.
-TEST_F(ConfigDirPolicyLoaderTest, Load3rdParty) {
- base::DictionaryValue policy_dict;
- policy_dict.SetBoolean("bool", true);
- policy_dict.SetString("str", "string value");
- policy_dict.SetDouble("double", 123.456);
- policy_dict.SetInteger("int", 789);
-
- base::ListValue* list = new base::ListValue();
- for (int i = 0; i < 5; ++i) {
- base::DictionaryValue* dict = new base::DictionaryValue();
- dict->SetInteger("subdictindex", i);
- dict->Set("subdict", policy_dict.DeepCopy());
- list->Append(dict);
- }
- policy_dict.Set("list", list);
-
- base::DictionaryValue json_dict;
- // Merge |policy_dict|, which will become the chrome policies.
- json_dict.MergeDictionary(&policy_dict);
- json_dict.Set("3rdparty.extensions.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
- policy_dict.DeepCopy());
- json_dict.Set("3rdparty.extensions.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
- policy_dict.DeepCopy());
-
- harness_.WriteConfigFile(json_dict, "policy.json");
- ConfigDirPolicyLoader loader(harness_.test_dir(), POLICY_SCOPE_USER);
- scoped_ptr<PolicyBundle> bundle(loader.Load());
- ASSERT_TRUE(bundle.get());
- PolicyMap expected_policy;
- expected_policy.LoadFrom(&policy_dict,
- POLICY_LEVEL_MANDATORY,
- POLICY_SCOPE_USER);
- PolicyBundle expected_bundle;
- expected_bundle.Get(POLICY_DOMAIN_CHROME, "").CopyFrom(expected_policy);
- expected_bundle.Get(POLICY_DOMAIN_EXTENSIONS,
- "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
- .CopyFrom(expected_policy);
- expected_bundle.Get(POLICY_DOMAIN_EXTENSIONS,
- "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")
- .CopyFrom(expected_policy);
- EXPECT_TRUE(bundle->Equals(expected_bundle));
-}
-
} // namespace policy
diff --git a/chrome/browser/policy/configuration_policy_provider_test.cc b/chrome/browser/policy/configuration_policy_provider_test.cc
index c6c9ab1..5e6d3a4 100644
--- a/chrome/browser/policy/configuration_policy_provider_test.cc
+++ b/chrome/browser/policy/configuration_policy_provider_test.cc
@@ -66,6 +66,11 @@ PolicyScope PolicyProviderTestHarness::policy_scope() const {
return scope_;
}
+void PolicyProviderTestHarness::Install3rdPartyPolicy(
+ const base::DictionaryValue* policies) {
+ FAIL();
+}
+
ConfigurationPolicyProviderTest::ConfigurationPolicyProviderTest() {}
ConfigurationPolicyProviderTest::~ConfigurationPolicyProviderTest() {}
@@ -256,4 +261,66 @@ TEST(ConfigurationPolicyProviderTest, FixDeprecatedPolicies) {
EXPECT_TRUE(provider.policies().Equals(expected_bundle));
}
+Configuration3rdPartyPolicyProviderTest::
+ Configuration3rdPartyPolicyProviderTest() {}
+
+Configuration3rdPartyPolicyProviderTest::
+ ~Configuration3rdPartyPolicyProviderTest() {}
+
+TEST_P(Configuration3rdPartyPolicyProviderTest, Load3rdParty) {
+ base::DictionaryValue policy_dict;
+ policy_dict.SetBoolean("bool", true);
+ policy_dict.SetDouble("double", 123.456);
+ policy_dict.SetInteger("int", 789);
+ policy_dict.SetString("str", "string value");
+
+ base::ListValue* list = new base::ListValue();
+ for (int i = 0; i < 2; ++i) {
+ base::DictionaryValue* dict = new base::DictionaryValue();
+ dict->SetInteger("subdictindex", i);
+ dict->Set("subdict", policy_dict.DeepCopy());
+ list->Append(dict);
+ }
+ policy_dict.Set("list", list);
+ policy_dict.Set("dict", policy_dict.DeepCopy());
+
+ // Install these policies as a Chrome policy.
+ test_harness_->InstallDictionaryPolicy(
+ test_policy_definitions::kKeyDictionary, &policy_dict);
+ // Install them as 3rd party policies too.
+ base::DictionaryValue policy_3rdparty;
+ policy_3rdparty.Set("extensions.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ policy_dict.DeepCopy());
+ policy_3rdparty.Set("extensions.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+ policy_dict.DeepCopy());
+ // Install invalid 3rd party policies that shouldn't be loaded. These also
+ // help detecting memory leaks in the code paths that detect invalid input.
+ policy_3rdparty.Set("invalid-domain.component", policy_dict.DeepCopy());
+ policy_3rdparty.Set("extensions.cccccccccccccccccccccccccccccccc",
+ base::Value::CreateStringValue("invalid-value"));
+ test_harness_->Install3rdPartyPolicy(&policy_3rdparty);
+
+ provider_->RefreshPolicies();
+ loop_.RunAllPending();
+
+ PolicyMap expected_policy;
+ expected_policy.Set(test_policy_definitions::kKeyDictionary,
+ test_harness_->policy_level(),
+ test_harness_->policy_scope(),
+ policy_dict.DeepCopy());
+ PolicyBundle expected_bundle;
+ expected_bundle.Get(POLICY_DOMAIN_CHROME, "").CopyFrom(expected_policy);
+ expected_policy.Clear();
+ expected_policy.LoadFrom(&policy_dict,
+ test_harness_->policy_level(),
+ test_harness_->policy_scope());
+ expected_bundle.Get(POLICY_DOMAIN_EXTENSIONS,
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
+ .CopyFrom(expected_policy);
+ expected_bundle.Get(POLICY_DOMAIN_EXTENSIONS,
+ "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")
+ .CopyFrom(expected_policy);
+ EXPECT_TRUE(provider_->policies().Equals(expected_bundle));
+}
+
} // namespace policy
diff --git a/chrome/browser/policy/configuration_policy_provider_test.h b/chrome/browser/policy/configuration_policy_provider_test.h
index a24519c..8b330f1 100644
--- a/chrome/browser/policy/configuration_policy_provider_test.h
+++ b/chrome/browser/policy/configuration_policy_provider_test.h
@@ -97,6 +97,10 @@ class PolicyProviderTestHarness {
const std::string& policy_name,
const base::DictionaryValue* policy_value) = 0;
+ // Not every provider supports installing 3rd party policy. Those who do
+ // should override this method; the default just makes the test fail.
+ virtual void Install3rdPartyPolicy(const base::DictionaryValue* policies);
+
private:
PolicyLevel level_;
PolicyScope scope_;
@@ -133,6 +137,19 @@ class ConfigurationPolicyProviderTest
DISALLOW_COPY_AND_ASSIGN(ConfigurationPolicyProviderTest);
};
+// An extension of ConfigurationPolicyProviderTest that also tests loading of
+// 3rd party policy. Policy provider implementations that support loading of
+// 3rd party policy should also instantiate these tests.
+class Configuration3rdPartyPolicyProviderTest
+ : public ConfigurationPolicyProviderTest {
+ protected:
+ Configuration3rdPartyPolicyProviderTest();
+ virtual ~Configuration3rdPartyPolicyProviderTest();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Configuration3rdPartyPolicyProviderTest);
+};
+
} // namespace policy
#endif // CHROME_BROWSER_POLICY_CONFIGURATION_POLICY_PROVIDER_TEST_H_
diff --git a/chrome/browser/policy/policy_loader_mac_unittest.cc b/chrome/browser/policy/policy_loader_mac_unittest.cc
index 4d90a3e..32006d7 100644
--- a/chrome/browser/policy/policy_loader_mac_unittest.cc
+++ b/chrome/browser/policy/policy_loader_mac_unittest.cc
@@ -220,6 +220,9 @@ INSTANTIATE_TEST_CASE_P(
ConfigurationPolicyProviderTest,
testing::Values(TestHarness::Create));
+// TODO(joaodasilva): instantiate Configuration3rdPartyPolicyProviderTest too
+// once the mac loader supports 3rd party policy. http://crbug.com/108995
+
// Special test cases for some mac preferences details.
class PolicyLoaderMacTest : public PolicyTestBase {
protected:
diff --git a/chrome/browser/policy/policy_loader_win.cc b/chrome/browser/policy/policy_loader_win.cc
index df5754e..36a44fe 100644
--- a/chrome/browser/policy/policy_loader_win.cc
+++ b/chrome/browser/policy/policy_loader_win.cc
@@ -29,16 +29,23 @@
using base::win::RegKey;
using base::win::RegistryKeyIterator;
using base::win::RegistryValueIterator;
+using namespace policy::registry_constants;
namespace policy {
-namespace {
-
-// Suffix of kRegistryMandatorySubKey where 3rd party policies are stored.
-const char k3rdPartyPolicySubKey[] = "\\3rdparty\\";
+namespace registry_constants {
+ const wchar_t kPathSep[] = L"\\";
+ const wchar_t kThirdParty[] = L"3rdparty";
+ const wchar_t kMandatory[] = L"policy";
+ const wchar_t kRecommended[] = L"recommended";
+ const wchar_t kSchema[] = L"schema";
+ const char kType[] = "type";
+ const char kProperties[] = "properties";
+ const char kAdditionalProperties[] = "additionalProperties";
+ const char kItems[] = "items";
+} // namespace registry_constants
-// Path separator for registry keys.
-const wchar_t kPathSep[] = L"\\";
+namespace {
// Map of registry hives to their corresponding policy scope, in decreasing
// order of priority.
@@ -80,7 +87,7 @@ bool LoadHighestPriorityKey(const string16& key_path,
for (size_t h = 0; h < arraysize(kHives); ++h) {
string16 path(kKeyPaths[k].path);
if (!key_path.empty())
- path += ASCIIToUTF16("\\") + key_path;
+ path += kPathSep + key_path;
key->Open(kHives[h].hive, path.c_str(), KEY_READ);
if (!key->Valid())
continue;
@@ -133,9 +140,9 @@ bool ReadRegistryInteger(RegKey* key,
// Returns the Value for a Chrome string policy named |name|, or NULL if
// it wasn't found. The caller owns the returned value.
-base::Value* ReadStringValue(const string16& name,
- PolicyLevel* level,
- PolicyScope* scope) {
+base::Value* ReadChromeStringValue(const string16& name,
+ PolicyLevel* level,
+ PolicyScope* scope) {
RegKey key;
if (!LoadHighestPriorityKey(string16(), name, &key, level, scope))
return NULL;
@@ -147,9 +154,9 @@ base::Value* ReadStringValue(const string16& name,
// Returns the Value for a Chrome string list policy named |name|,
// or NULL if it wasn't found. The caller owns the returned value.
-base::Value* ReadStringListValue(const string16& name,
- PolicyLevel* level,
- PolicyScope* scope) {
+base::Value* ReadChromeStringListValue(const string16& name,
+ PolicyLevel* level,
+ PolicyScope* scope) {
RegKey key;
if (!LoadHighestPriorityKey(name, string16(), &key, level, scope))
return NULL;
@@ -163,9 +170,9 @@ base::Value* ReadStringListValue(const string16& name,
// Returns the Value for a Chrome boolean policy named |name|,
// or NULL if it wasn't found. The caller owns the returned value.
-base::Value* ReadBooleanValue(const string16& name,
- PolicyLevel* level,
- PolicyScope* scope) {
+base::Value* ReadChromeBooleanValue(const string16& name,
+ PolicyLevel* level,
+ PolicyScope* scope) {
RegKey key;
if (!LoadHighestPriorityKey(string16(), name, &key, level, scope))
return NULL;
@@ -177,9 +184,9 @@ base::Value* ReadBooleanValue(const string16& name,
// Returns the Value for a Chrome integer policy named |name|,
// or NULL if it wasn't found. The caller owns the returned value.
-base::Value* ReadIntegerValue(const string16& name,
- PolicyLevel* level,
- PolicyScope* scope) {
+base::Value* ReadChromeIntegerValue(const string16& name,
+ PolicyLevel* level,
+ PolicyScope* scope) {
RegKey key;
if (!LoadHighestPriorityKey(string16(), name, &key, level, scope))
return NULL;
@@ -191,9 +198,9 @@ base::Value* ReadIntegerValue(const string16& name,
// Returns the Value for a Chrome dictionary policy named |name|,
// or NULL if it wasn't found. The caller owns the returned value.
-base::Value* ReadDictionaryValue(const string16& name,
- PolicyLevel* level,
- PolicyScope* scope) {
+base::Value* ReadChromeDictionaryValue(const string16& name,
+ PolicyLevel* level,
+ PolicyScope* scope) {
// Dictionaries are encoded as JSON strings on Windows.
//
// A dictionary could be stored as a subkey, with each of its entries
@@ -216,10 +223,219 @@ base::Value* ReadDictionaryValue(const string16& name,
return base::JSONReader::Read(UTF16ToUTF8(value));
}
-// Loads the dictionary at |path| in the given |hive|. Ownership is transferred
-// to the caller.
-base::DictionaryValue* ReadRegistryDictionaryValue(HKEY hive,
- const string16& path) {
+// Returns the Value type described in |schema|, or |default_type| if not found.
+base::Value::Type GetType(const base::DictionaryValue* schema,
+ base::Value::Type default_type) {
+ // JSON-schema types to base::Value::Type mapping.
+ static const struct {
+ // JSON schema type.
+ const char* schema_type;
+ // Correspondent value type.
+ base::Value::Type value_type;
+ } kSchemaToValueTypeMap[] = {
+ { "array", base::Value::TYPE_LIST },
+ { "boolean", base::Value::TYPE_BOOLEAN },
+ { "integer", base::Value::TYPE_INTEGER },
+ { "null", base::Value::TYPE_NULL },
+ { "number", base::Value::TYPE_DOUBLE },
+ { "object", base::Value::TYPE_DICTIONARY },
+ { "string", base::Value::TYPE_STRING },
+ };
+
+ if (!schema)
+ return default_type;
+ std::string type;
+ if (!schema->GetString(kType, &type))
+ return default_type;
+ for (size_t i = 0; i < arraysize(kSchemaToValueTypeMap); ++i) {
+ if (type == kSchemaToValueTypeMap[i].schema_type)
+ return kSchemaToValueTypeMap[i].value_type;
+ }
+ return default_type;
+}
+
+// Returns the default type for registry entries of |reg_type|, when there is
+// no schema defined type for a policy.
+base::Value::Type GetDefaultFor(DWORD reg_type) {
+ return reg_type == REG_DWORD ? base::Value::TYPE_INTEGER :
+ base::Value::TYPE_STRING;
+}
+
+// Returns the entry with key |name| in |dictionary| (can be NULL), or NULL.
+base::DictionaryValue* GetEntry(const base::DictionaryValue* dictionary,
+ const std::string& name) {
+ if (!dictionary)
+ return NULL;
+ base::DictionaryValue* entry = NULL;
+ dictionary->GetDictionary(name, &entry);
+ return entry;
+}
+
+// Returns the schema for property |name| given the |schema| of an object.
+// Returns the "additionalProperties" schema if no specific schema for
+// |name| is present. Returns NULL if no schema is found.
+base::DictionaryValue* GetSchemaFor(const base::DictionaryValue* schema,
+ const std::string& name) {
+ base::DictionaryValue* properties = GetEntry(schema, kProperties);
+ base::DictionaryValue* sub_schema = GetEntry(properties, name);
+ if (sub_schema)
+ return sub_schema;
+ // "additionalProperties" can be a boolean, but that case is ignored.
+ return GetEntry(schema, kAdditionalProperties);
+}
+
+// Converts string |value| to another |type|, if possible.
+base::Value* ConvertComponentStringValue(const string16& value,
+ base::Value::Type type) {
+ switch (type) {
+ case base::Value::TYPE_NULL:
+ return base::Value::CreateNullValue();
+
+ case base::Value::TYPE_BOOLEAN: {
+ int int_value;
+ if (base::StringToInt(value, &int_value))
+ return base::Value::CreateBooleanValue(int_value != 0);
+ return NULL;
+ }
+
+ case base::Value::TYPE_INTEGER: {
+ int int_value;
+ if (base::StringToInt(value, &int_value))
+ return base::Value::CreateIntegerValue(int_value);
+ return NULL;
+ }
+
+ case base::Value::TYPE_DOUBLE: {
+ double double_value;
+ if (base::StringToDouble(UTF16ToUTF8(value), &double_value))
+ return base::Value::CreateDoubleValue(double_value);
+ DLOG(WARNING) << "Failed to read policy value as double: " << value;
+ return NULL;
+ }
+
+ case base::Value::TYPE_STRING:
+ return base::Value::CreateStringValue(value);
+
+ case base::Value::TYPE_DICTIONARY:
+ case base::Value::TYPE_LIST:
+ return base::JSONReader::Read(UTF16ToUTF8(value));
+
+ case base::Value::TYPE_BINARY:
+ DLOG(WARNING) << "Cannot convert REG_SZ entry to type " << type;
+ return NULL;
+ }
+ NOTREACHED();
+ return NULL;
+}
+
+// Converts an integer |value| to another |type|, if possible.
+base::Value* ConvertComponentIntegerValue(uint32 value,
+ base::Value::Type type) {
+ switch (type) {
+ case base::Value::TYPE_BOOLEAN:
+ return base::Value::CreateBooleanValue(value != 0);
+
+ case base::Value::TYPE_INTEGER:
+ return base::Value::CreateIntegerValue(value);
+
+ case base::Value::TYPE_DOUBLE:
+ return base::Value::CreateDoubleValue(value);
+
+ case base::Value::TYPE_NULL:
+ case base::Value::TYPE_STRING:
+ case base::Value::TYPE_BINARY:
+ case base::Value::TYPE_DICTIONARY:
+ case base::Value::TYPE_LIST:
+ DLOG(WARNING) << "Cannot convert REG_DWORD entry to type " << type;
+ return NULL;
+ }
+ NOTREACHED();
+ return NULL;
+}
+
+// Reads a simple (non-Dictionary, non-Array) value from the registry |key|
+// named |name| with registry type |reg_type| as a value of type |type|.
+// Returns NULL if the value could not be loaded or converted.
+base::Value* ReadComponentSimpleValue(RegKey* key,
+ const string16& name,
+ DWORD reg_type,
+ base::Value::Type type) {
+ switch (reg_type) {
+ case REG_SZ: {
+ string16 value;
+ if (ReadRegistryString(key, name, &value))
+ return ConvertComponentStringValue(value, type);
+ break;
+ }
+
+ case REG_DWORD: {
+ uint32 value;
+ if (ReadRegistryInteger(key, name, &value))
+ return ConvertComponentIntegerValue(value, type);
+ break;
+ }
+
+ default:
+ DLOG(WARNING) << "Registry type not supported for key " << name;
+ break;
+ }
+ NOTREACHED();
+ return NULL;
+}
+
+// Forward declaration for ReadComponentListValue().
+base::DictionaryValue* ReadComponentDictionaryValue(
+ HKEY hive,
+ const string16& path,
+ const base::DictionaryValue* schema);
+
+// Loads the list at |path| in the given |hive|. |schema| is a JSON schema
+// (http://json-schema.org/) that describes the expected type of the list.
+// Ownership of the result is transferred to the caller.
+base::ListValue* ReadComponentListValue(HKEY hive,
+ const string16& path,
+ const base::DictionaryValue* schema) {
+ // The sub-elements are indexed from 1 to N. They can be represented as
+ // registry values or registry keys though; use |schema| first to try to
+ // determine the right type, and if that fails default to STRING.
+
+ RegKey key(hive, path.c_str(), KEY_READ);
+ if (!key.Valid())
+ return NULL;
+
+ // Get the schema for list items.
+ schema = GetEntry(schema, kItems);
+ base::Value::Type type = GetType(schema, base::Value::TYPE_STRING);
+ base::ListValue* list = new base::ListValue();
+ for (int i = 1; ; ++i) {
+ string16 name = base::IntToString16(i);
+ base::Value* value = NULL;
+ if (type == base::Value::TYPE_DICTIONARY) {
+ value =
+ ReadComponentDictionaryValue(hive, path + kPathSep + name, schema);
+ } else if (type == base::Value::TYPE_LIST) {
+ value = ReadComponentListValue(hive, path + kPathSep + name, schema);
+ } else {
+ DWORD reg_type;
+ key.ReadValue(name.c_str(), NULL, NULL, &reg_type);
+ value = ReadComponentSimpleValue(&key, name, reg_type, type);
+ }
+ if (!value)
+ break;
+ list->Append(value);
+ }
+ return list;
+}
+
+// Loads the dictionary at |path| in the given |hive|. |schema| is a JSON
+// schema (http://json-schema.org/) that describes the expected types for the
+// dictionary entries. When the type for a certain entry isn't described in the
+// schema, a default conversion takes place. |schema| can be NULL.
+// Ownership of the result is transferred to the caller.
+base::DictionaryValue* ReadComponentDictionaryValue(
+ HKEY hive,
+ const string16& path,
+ const base::DictionaryValue* schema) {
// A "value" in the registry is like a file in a filesystem, and a "key" is
// like a directory, that contains other "values" and "keys".
// Unfortunately it is possible to have a name both as a "value" and a "key".
@@ -228,43 +444,42 @@ base::DictionaryValue* ReadRegistryDictionaryValue(HKEY hive,
// First iterate over all the "values" in |path| and convert them; then
// recurse into each "key" in |path| and convert them as dictionaries.
- base::DictionaryValue* dict = new base::DictionaryValue();
RegKey key(hive, path.c_str(), KEY_READ);
+ if (!key.Valid())
+ return NULL;
+ base::DictionaryValue* dict = new base::DictionaryValue();
for (RegistryValueIterator it(hive, path.c_str()); it.Valid(); ++it) {
- string16 name(it.Name());
- switch (it.Type()) {
- case REG_SZ: {
- string16 value;
- if (ReadRegistryString(&key, name, &value))
- dict->SetString(UTF16ToUTF8(name), value);
- break;
- }
-
- case REG_DWORD: {
- uint32 value;
- if (ReadRegistryInteger(&key, name, &value))
- dict->SetInteger(UTF16ToUTF8(name), value);
- break;
- }
-
- default:
- // TODO(joaodasilva): use a schema to determine the correct types.
- LOG(WARNING) << "Ignoring registry value with unsupported type: "
- << path << kPathSep << name;
- }
+ string16 name16(it.Name());
+ std::string name(UTF16ToUTF8(name16));
+ const base::DictionaryValue* sub_schema = GetSchemaFor(schema, name);
+ base::Value::Type type = GetType(sub_schema, GetDefaultFor(it.Type()));
+ base::Value* value =
+ ReadComponentSimpleValue(&key, name16, it.Type(), type);
+ if (value)
+ dict->Set(name, value);
}
for (RegistryKeyIterator it(hive, path.c_str()); it.Valid(); ++it) {
string16 name16(it.Name());
std::string name(UTF16ToUTF8(name16));
if (dict->HasKey(name)) {
- LOG(WARNING) << "Ignoring registry key because a value exists with the "
- "same name: " << path << kPathSep << name;
+ DLOG(WARNING) << "Ignoring registry key because a value exists with the "
+ "same name: " << path << kPathSep << name;
continue;
}
- base::DictionaryValue* value =
- ReadRegistryDictionaryValue(hive, path + kPathSep + name16);
+
+ base::DictionaryValue* sub_schema = GetSchemaFor(schema, name);
+ base::Value::Type type = GetType(sub_schema, base::Value::TYPE_DICTIONARY);
+ base::Value* value = NULL;
+ const string16 sub_path = path + kPathSep + name16;
+ if (type == base::Value::TYPE_DICTIONARY) {
+ value = ReadComponentDictionaryValue(hive, sub_path, sub_schema);
+ } else if (type == base::Value::TYPE_LIST) {
+ value = ReadComponentListValue(hive, sub_path, sub_schema);
+ } else {
+ DLOG(WARNING) << "Can't read a simple type in registry key at " << path;
+ }
if (value)
dict->Set(name, value);
}
@@ -272,6 +487,35 @@ base::DictionaryValue* ReadRegistryDictionaryValue(HKEY hive,
return dict;
}
+// Reads a JSON schema from the given |registry_value|, at the given
+// |registry_key| in |hive|. |registry_value| must be a string (REG_SZ), and
+// is decoded as JSON data. Returns NULL on failure. Ownership is transferred
+// to the caller.
+base::DictionaryValue* ReadRegistrySchema(HKEY hive,
+ const string16& registry_key,
+ const string16& registry_value) {
+ RegKey key(hive, registry_key.c_str(), KEY_READ);
+ string16 schema;
+ if (!ReadRegistryString(&key, registry_value, &schema))
+ return NULL;
+ // A JSON schema is represented in JSON too.
+ scoped_ptr<base::Value> value(base::JSONReader::Read(UTF16ToUTF8(schema)));
+ if (!value.get())
+ return NULL;
+ base::DictionaryValue* dict = NULL;
+ if (!value->GetAsDictionary(&dict))
+ return NULL;
+ // The top-level entry must be an object, and each of its properties maps
+ // a policy name to its schema.
+ if (GetType(dict, base::Value::TYPE_DICTIONARY) !=
+ base::Value::TYPE_DICTIONARY) {
+ DLOG(WARNING) << "schema top-level type isn't \"object\"";
+ return NULL;
+ }
+ value.release();
+ return dict;
+}
+
} // namespace
PolicyLoaderWin::PolicyLoaderWin(const PolicyDefinitionList* policy_list)
@@ -323,23 +567,23 @@ void PolicyLoaderWin::LoadChromePolicy(PolicyMap* chrome_policies) {
switch (current->value_type) {
case base::Value::TYPE_STRING:
- value = ReadStringValue(name, &level, &scope);
+ value = ReadChromeStringValue(name, &level, &scope);
break;
case base::Value::TYPE_LIST:
- value = ReadStringListValue(name, &level, &scope);
+ value = ReadChromeStringListValue(name, &level, &scope);
break;
case base::Value::TYPE_BOOLEAN:
- value = ReadBooleanValue(name, &level, &scope);
+ value = ReadChromeBooleanValue(name, &level, &scope);
break;
case base::Value::TYPE_INTEGER:
- value = ReadIntegerValue(name, &level, &scope);
+ value = ReadChromeIntegerValue(name, &level, &scope);
break;
case base::Value::TYPE_DICTIONARY:
- value = ReadDictionaryValue(name, &level, &scope);
+ value = ReadChromeDictionaryValue(name, &level, &scope);
break;
default:
@@ -374,8 +618,8 @@ void PolicyLoaderWin::Load3rdPartyPolicies(PolicyBundle* bundle) {
};
// Path where policies for components are stored.
- const string16 kPathPrefix =
- kRegistryMandatorySubKey + ASCIIToUTF16(k3rdPartyPolicySubKey);
+ const string16 kPathPrefix = string16(kRegistryMandatorySubKey) + kPathSep +
+ kThirdParty + kPathSep;
for (size_t h = 0; h < arraysize(kHives); ++h) {
HKEY hkey = kHives[h].hive;
@@ -390,12 +634,16 @@ void PolicyLoaderWin::Load3rdPartyPolicies(PolicyBundle* bundle) {
string16 component(domain_iterator.Name());
string16 component_path = domain_path + kPathSep + component;
+ // Load the schema for this component's policy, if present.
+ scoped_ptr<base::DictionaryValue> schema(
+ ReadRegistrySchema(hkey, component_path, kSchema));
+
for (size_t k = 0; k < arraysize(kKeyPaths); ++k) {
string16 path =
component_path + kPathSep + ASCIIToUTF16(kKeyPaths[k].path);
scoped_ptr<base::DictionaryValue> dictionary(
- ReadRegistryDictionaryValue(hkey, path));
+ ReadComponentDictionaryValue(hkey, path, schema.get()));
if (dictionary.get()) {
PolicyMap policies;
policies.LoadFrom(
diff --git a/chrome/browser/policy/policy_loader_win.h b/chrome/browser/policy/policy_loader_win.h
index a90dd1a..94c9965 100644
--- a/chrome/browser/policy/policy_loader_win.h
+++ b/chrome/browser/policy/policy_loader_win.h
@@ -50,6 +50,32 @@ class PolicyLoaderWin : public AsyncPolicyLoader,
DISALLOW_COPY_AND_ASSIGN(PolicyLoaderWin);
};
+// Constants shared with tests.
+namespace registry_constants {
+ // Path separator for registry keys.
+ extern const wchar_t kPathSep[];
+ // Registry key within Chrome's key that contains 3rd party policy.
+ extern const wchar_t kThirdParty[];
+ // Registry key within an extension's namespace that contains mandatory
+ // policy.
+ extern const wchar_t kMandatory[];
+ // Registry key within an extension's namespace that contains recommended
+ // policy.
+ extern const wchar_t kRecommended[];
+ // Registry key within an extension's namespace that contains the policy
+ // schema.
+ extern const wchar_t kSchema[];
+ // Key in a JSON schema that indicates the expected type.
+ extern const char kType[];
+ // Key in a JSON schema that indicates the expected properties of an object.
+ extern const char kProperties[];
+ // Key in a JSON schema that indicates the default schema for object
+ // properties.
+ extern const char kAdditionalProperties[];
+ // Key in a JSON schema that indicates the expected type of list items.
+ extern const char kItems[];
+} // namespace registry_constants
+
} // namespace policy
#endif // CHROME_BROWSER_POLICY_POLICY_LOADER_WIN_H_
diff --git a/chrome/browser/policy/policy_loader_win_unittest.cc b/chrome/browser/policy/policy_loader_win_unittest.cc
index 888fa8a..9173c26 100644
--- a/chrome/browser/policy/policy_loader_win_unittest.cc
+++ b/chrome/browser/policy/policy_loader_win_unittest.cc
@@ -9,6 +9,7 @@
#include "base/json/json_writer.h"
#include "base/string16.h"
#include "base/string_number_conversions.h"
+#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "base/win/registry.h"
#include "chrome/browser/policy/async_policy_provider.h"
@@ -19,6 +20,7 @@
#include "testing/gtest/include/gtest/gtest.h"
using base::win::RegKey;
+using namespace policy::registry_constants;
namespace policy {
@@ -30,54 +32,162 @@ const wchar_t kUnitTestMachineOverrideSubKey[] =
const wchar_t kUnitTestUserOverrideSubKey[] =
L"SOFTWARE\\Chromium Unit Tests\\HKCU Override";
-// Installs |dict| at the given |path|, in the given |hive|. Currently only
-// string, int and dictionary types are converted; other types cause a failure.
-// Returns false if there was any failure, and true if |dict| was successfully
-// written.
-// TODO(joaodasilva): generate a schema for |dict| too, so that all types can
-// be retrieved.
-bool InstallDictionary(const base::DictionaryValue& dict,
- HKEY hive,
- const string16& path) {
+// Installs |value| in the given registry |path| and |hive|, under the key
+// |name|. Returns false on errors.
+// Some of the possible Value types are stored after a conversion (e.g. doubles
+// are stored as strings), and can only be retrieved if a corresponding schema
+// is written.
+bool InstallValue(const base::Value& value,
+ HKEY hive,
+ const string16& path,
+ const string16& name) {
// KEY_ALL_ACCESS causes the ctor to create the key if it does not exist yet.
RegKey key(hive, path.c_str(), KEY_ALL_ACCESS);
- const string16 kPathSep = ASCIIToUTF16("\\");
-
- for (base::DictionaryValue::Iterator it(dict); it.HasNext(); it.Advance()) {
- string16 name(UTF8ToUTF16(it.key()));
- switch (it.value().GetType()) {
- case base::Value::TYPE_STRING: {
- string16 value;
- if (!it.value().GetAsString(&value))
- return false;
- if (key.WriteValue(name.c_str(), value.c_str()) != ERROR_SUCCESS)
+ switch (value.GetType()) {
+ case base::Value::TYPE_NULL:
+ return key.WriteValue(name.c_str(), L"") == ERROR_SUCCESS;
+
+ case base::Value::TYPE_BOOLEAN: {
+ bool bool_value;
+ if (!value.GetAsBoolean(&bool_value))
+ return false;
+ return key.WriteValue(name.c_str(), bool_value ? 1 : 0) == ERROR_SUCCESS;
+ }
+
+ case base::Value::TYPE_INTEGER: {
+ int int_value;
+ if (!value.GetAsInteger(&int_value))
+ return false;
+ return key.WriteValue(name.c_str(), int_value) == ERROR_SUCCESS;
+ }
+
+ case base::Value::TYPE_DOUBLE: {
+ double double_value;
+ if (!value.GetAsDouble(&double_value))
+ return false;
+ string16 str_value = UTF8ToUTF16(base::DoubleToString(double_value));
+ return key.WriteValue(name.c_str(), str_value.c_str()) == ERROR_SUCCESS;
+ }
+
+ case base::Value::TYPE_STRING: {
+ string16 str_value;
+ if (!value.GetAsString(&str_value))
+ return false;
+ return key.WriteValue(name.c_str(), str_value.c_str()) == ERROR_SUCCESS;
+ }
+
+ case base::Value::TYPE_DICTIONARY: {
+ const base::DictionaryValue* sub_dict = NULL;
+ if (!value.GetAsDictionary(&sub_dict))
+ return false;
+ for (base::DictionaryValue::Iterator it(*sub_dict);
+ it.HasNext(); it.Advance()) {
+ if (!InstallValue(it.value(), hive, path + kPathSep + name,
+ UTF8ToUTF16(it.key()))) {
return false;
- break;
+ }
}
+ return true;
+ }
- case base::Value::TYPE_INTEGER: {
- int value;
- if (!it.value().GetAsInteger(&value))
+ case base::Value::TYPE_LIST: {
+ const base::ListValue* list = NULL;
+ if (!value.GetAsList(&list))
+ return false;
+ for (size_t i = 0; i < list->GetSize(); ++i) {
+ base::Value* item;
+ if (!list->Get(i, &item))
return false;
- if (key.WriteValue(name.c_str(), value) != ERROR_SUCCESS)
+ if (!InstallValue(*item, hive, path + kPathSep + name,
+ base::UintToString16(i + 1))) {
return false;
- break;
+ }
}
+ return true;
+ }
- case base::Value::TYPE_DICTIONARY: {
- const base::DictionaryValue* sub_dict = NULL;
- if (!it.value().GetAsDictionary(&sub_dict))
- return false;
- if (!InstallDictionary(*sub_dict, hive, path + kPathSep + name))
- return false;
- break;
+ case base::Value::TYPE_BINARY:
+ return false;
+ }
+ NOTREACHED();
+ return false;
+}
+
+// Builds a JSON schema that represents the types contained in |value|.
+// Ownership is transferred to the caller.
+base::DictionaryValue* BuildSchema(const base::Value& value) {
+ base::DictionaryValue* schema = new base::DictionaryValue();
+ switch (value.GetType()) {
+ case base::Value::TYPE_NULL:
+ schema->SetString(kType, "null");
+ break;
+ case base::Value::TYPE_BOOLEAN:
+ schema->SetString(kType, "boolean");
+ break;
+ case base::Value::TYPE_INTEGER:
+ schema->SetString(kType, "integer");
+ break;
+ case base::Value::TYPE_DOUBLE:
+ schema->SetString(kType, "number");
+ break;
+ case base::Value::TYPE_STRING:
+ schema->SetString(kType, "string");
+ break;
+
+ case base::Value::TYPE_LIST: {
+ // Assumes every list element has the same type.
+ const base::ListValue* list = NULL;
+ if (value.GetAsList(&list) && !list->empty()) {
+ schema->SetString(kType, "array");
+ schema->Set(kItems, BuildSchema(**list->begin()));
}
+ break;
+ }
- default:
- return false;
+ case base::Value::TYPE_DICTIONARY: {
+ const base::DictionaryValue* dict = NULL;
+ if (value.GetAsDictionary(&dict)) {
+ base::DictionaryValue* properties = new base::DictionaryValue();
+ for (base::DictionaryValue::Iterator it(*dict);
+ it.HasNext(); it.Advance()) {
+ properties->Set(it.key(), BuildSchema(it.value()));
+ }
+ schema->SetString(kType, "object");
+ schema->Set(kProperties, properties);
+ }
+ break;
}
+
+ case base::Value::TYPE_BINARY:
+ break;
}
- return true;
+ return schema;
+}
+
+// Writes a JSON |schema| at the registry entry |name| at |path|
+// in the given |hive|. Returns false on failure.
+bool WriteSchema(const base::DictionaryValue& schema,
+ HKEY hive,
+ const string16& path,
+ const string16& name) {
+ std::string encoded;
+ base::JSONWriter::Write(&schema, &encoded);
+ if (encoded.empty())
+ return false;
+ string16 encoded16 = UTF8ToUTF16(encoded);
+ // KEY_ALL_ACCESS causes the ctor to create the key if it does not exist yet.
+ RegKey key(hive, path.c_str(), KEY_ALL_ACCESS);
+ return key.WriteValue(name.c_str(), encoded16.c_str()) == ERROR_SUCCESS;
+}
+
+// Builds a JSON schema for |value| and writes it at the registry entry |name|
+// at |path| in the given |hive|. Returns false on failure.
+bool InstallSchema(const base::Value& value,
+ HKEY hive,
+ const string16& path,
+ const string16& name) {
+ scoped_ptr<base::DictionaryValue> schema_dict(BuildSchema(value));
+ return WriteSchema(*schema_dict, hive, path, name);
}
// This class provides sandboxing and mocking for the parts of the Windows
@@ -130,6 +240,8 @@ class TestHarness : public PolicyProviderTestHarness {
virtual void InstallDictionaryPolicy(
const std::string& policy_name,
const base::DictionaryValue* policy_value) OVERRIDE;
+ virtual void Install3rdPartyPolicy(
+ const base::DictionaryValue* policies) OVERRIDE;
// Creates a harness instance that will install policy in HKCU or HKLM,
// respectively.
@@ -247,6 +359,30 @@ void TestHarness::InstallDictionaryPolicy(
UTF8ToUTF16(json).c_str());
}
+void TestHarness::Install3rdPartyPolicy(const base::DictionaryValue* policies) {
+ // The first level entries are domains, and the second level entries map
+ // components to their policy.
+ const string16 kPathPrefix = string16(kRegistryMandatorySubKey) + kPathSep +
+ kThirdParty + kPathSep;
+ for (base::DictionaryValue::Iterator domain(*policies);
+ domain.HasNext(); domain.Advance()) {
+ const base::DictionaryValue* components = NULL;
+ if (!domain.value().GetAsDictionary(&components)) {
+ ADD_FAILURE();
+ continue;
+ }
+ for (base::DictionaryValue::Iterator component(*components);
+ component.HasNext(); component.Advance()) {
+ const string16 path = string16(kRegistryMandatorySubKey) + kPathSep +
+ kThirdParty + kPathSep +
+ UTF8ToUTF16(domain.key()) + kPathSep +
+ UTF8ToUTF16(component.key());
+ InstallValue(component.value(), hive_, path, kMandatory);
+ EXPECT_TRUE(InstallSchema(component.value(), hive_, path, kSchema));
+ }
+ }
+}
+
// static
PolicyProviderTestHarness* TestHarness::CreateHKCU() {
return new TestHarness(HKEY_CURRENT_USER, POLICY_SCOPE_USER);
@@ -265,12 +401,24 @@ INSTANTIATE_TEST_CASE_P(
ConfigurationPolicyProviderTest,
testing::Values(TestHarness::CreateHKCU, TestHarness::CreateHKLM));
+// Instantiate abstract test case for 3rd party policy reading tests.
+INSTANTIATE_TEST_CASE_P(
+ ThirdPartyPolicyProviderWinTest,
+ Configuration3rdPartyPolicyProviderTest,
+ testing::Values(TestHarness::CreateHKCU, TestHarness::CreateHKLM));
+
// Test cases for windows policy provider specific functionality.
class PolicyLoaderWinTest : public PolicyTestBase {
protected:
PolicyLoaderWinTest() {}
virtual ~PolicyLoaderWinTest() {}
+ bool Matches(const PolicyBundle& expected) {
+ PolicyLoaderWin loader(&test_policy_definitions::kList);
+ scoped_ptr<PolicyBundle> loaded(loader.Load());
+ return loaded->Equals(expected);
+ }
+
ScopedGroupPolicyRegistrySandbox registry_sandbox_;
};
@@ -282,21 +430,16 @@ TEST_F(PolicyLoaderWinTest, HKLMOverHKCU) {
hkcu_key.WriteValue(UTF8ToUTF16(test_policy_definitions::kKeyString).c_str(),
UTF8ToUTF16("hkcu").c_str());
- PolicyLoaderWin loader(&test_policy_definitions::kList);
- scoped_ptr<PolicyBundle> bundle(loader.Load());
-
- PolicyBundle expected_bundle;
- expected_bundle.Get(POLICY_DOMAIN_CHROME, "")
+ PolicyBundle expected;
+ expected.Get(POLICY_DOMAIN_CHROME, "")
.Set(test_policy_definitions::kKeyString,
POLICY_LEVEL_MANDATORY,
POLICY_SCOPE_MACHINE,
base::Value::CreateStringValue("hklm"));
- EXPECT_TRUE(bundle->Equals(expected_bundle));
+ EXPECT_TRUE(Matches(expected));
}
-// TODO(joaodasilva): share tests for 3rd party policy with
-// ConfigDirPolicyProvider once PolicyLoaderWin is able to load all types.
-TEST_F(PolicyLoaderWinTest, Load3rdParty) {
+TEST_F(PolicyLoaderWinTest, Load3rdPartyWithoutSchema) {
base::DictionaryValue dict;
dict.SetString("str", "string value");
dict.SetInteger("int", 123);
@@ -305,22 +448,19 @@ TEST_F(PolicyLoaderWinTest, Load3rdParty) {
dict.Set("subsubsubdict", dict.DeepCopy());
base::DictionaryValue policy_dict;
- policy_dict.Set("3rdparty.extensions.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.policy",
+ policy_dict.Set("extensions.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.policy",
dict.DeepCopy());
- policy_dict.Set("3rdparty.extensions.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.policy",
+ policy_dict.Set("extensions.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.policy",
dict.DeepCopy());
- EXPECT_TRUE(InstallDictionary(policy_dict, HKEY_LOCAL_MACHINE,
- kRegistryMandatorySubKey));
+ EXPECT_TRUE(InstallValue(policy_dict, HKEY_LOCAL_MACHINE,
+ kRegistryMandatorySubKey, kThirdParty));
PolicyBundle expected;
expected.Get(POLICY_DOMAIN_EXTENSIONS, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
.LoadFrom(&dict, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE);
expected.Get(POLICY_DOMAIN_EXTENSIONS, "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")
.LoadFrom(&dict, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE);
-
- PolicyLoaderWin loader(&test_policy_definitions::kList);
- scoped_ptr<PolicyBundle> loaded(loader.Load());
- EXPECT_TRUE(loaded->Equals(expected));
+ EXPECT_TRUE(Matches(expected));
}
TEST_F(PolicyLoaderWinTest, Merge3rdPartyPolicies) {
@@ -329,8 +469,6 @@ TEST_F(PolicyLoaderWinTest, Merge3rdPartyPolicies) {
const string16 kPathSuffix =
kRegistryMandatorySubKey + ASCIIToUTF16("\\3rdparty\\extensions\\merge");
- const string16 kMandatoryPath = kPathSuffix + ASCIIToUTF16("\\policy");
- const string16 kRecommendedPath = kPathSuffix + ASCIIToUTF16("\\recommended");
const char kUserMandatory[] = "user-mandatory";
const char kUserRecommended[] = "user-recommended";
@@ -339,19 +477,23 @@ TEST_F(PolicyLoaderWinTest, Merge3rdPartyPolicies) {
base::DictionaryValue policy;
policy.SetString("a", kMachineMandatory);
- EXPECT_TRUE(InstallDictionary(policy, HKEY_LOCAL_MACHINE, kMandatoryPath));
+ EXPECT_TRUE(InstallValue(policy, HKEY_LOCAL_MACHINE,
+ kPathSuffix, kMandatory));
policy.SetString("a", kUserMandatory);
policy.SetString("b", kUserMandatory);
- EXPECT_TRUE(InstallDictionary(policy, HKEY_CURRENT_USER, kMandatoryPath));
+ EXPECT_TRUE(InstallValue(policy, HKEY_CURRENT_USER,
+ kPathSuffix, kMandatory));
policy.SetString("a", kMachineRecommended);
policy.SetString("b", kMachineRecommended);
policy.SetString("c", kMachineRecommended);
- EXPECT_TRUE(InstallDictionary(policy, HKEY_LOCAL_MACHINE, kRecommendedPath));
+ EXPECT_TRUE(InstallValue(policy, HKEY_LOCAL_MACHINE,
+ kPathSuffix, kRecommended));
policy.SetString("a", kUserRecommended);
policy.SetString("b", kUserRecommended);
policy.SetString("c", kUserRecommended);
policy.SetString("d", kUserRecommended);
- EXPECT_TRUE(InstallDictionary(policy, HKEY_CURRENT_USER, kRecommendedPath));
+ EXPECT_TRUE(InstallValue(policy, HKEY_CURRENT_USER,
+ kPathSuffix, kRecommended));
PolicyBundle expected;
PolicyMap& expected_policy = expected.Get(POLICY_DOMAIN_EXTENSIONS, "merge");
@@ -363,10 +505,112 @@ TEST_F(PolicyLoaderWinTest, Merge3rdPartyPolicies) {
base::Value::CreateStringValue(kMachineRecommended));
expected_policy.Set("d", POLICY_LEVEL_RECOMMENDED, POLICY_SCOPE_USER,
base::Value::CreateStringValue(kUserRecommended));
+ EXPECT_TRUE(Matches(expected));
+}
+
+TEST_F(PolicyLoaderWinTest, LoadStringEncodedValues) {
+ // Create a dictionary with all the types that can be stored encoded in a
+ // string, to pass to InstallSchema(). Also build an equivalent dictionary
+ // with the encoded values, to pass to InstallValue().
+ base::DictionaryValue policy;
+ policy.Set("null", base::Value::CreateNullValue());
+ policy.SetBoolean("bool", true);
+ policy.SetInteger("int", -123);
+ policy.SetDouble("double", 456.78e9);
+ base::ListValue list;
+ list.Append(policy.DeepCopy());
+ list.Append(policy.DeepCopy());
+ policy.Set("list", list.DeepCopy());
+ // Encode |policy| before adding the "dict" entry.
+ std::string encoded_dict;
+ base::JSONWriter::Write(&policy, &encoded_dict);
+ ASSERT_FALSE(encoded_dict.empty());
+ policy.Set("dict", policy.DeepCopy());
+
+ std::string encoded_list;
+ base::JSONWriter::Write(&list, &encoded_list);
+ ASSERT_FALSE(encoded_list.empty());
+ base::DictionaryValue encoded_policy;
+ encoded_policy.SetString("null", "");
+ encoded_policy.SetString("bool", "1");
+ encoded_policy.SetString("int", "-123");
+ encoded_policy.SetString("double", "456.78e9");
+ encoded_policy.SetString("list", encoded_list);
+ encoded_policy.SetString("dict", encoded_dict);
+
+ const string16 kPathSuffix =
+ kRegistryMandatorySubKey + ASCIIToUTF16("\\3rdparty\\extensions\\string");
+ EXPECT_TRUE(InstallSchema(policy, HKEY_CURRENT_USER, kPathSuffix, kSchema));
+ EXPECT_TRUE(
+ InstallValue(encoded_policy, HKEY_CURRENT_USER, kPathSuffix, kMandatory));
+
+ PolicyBundle expected;
+ expected.Get(POLICY_DOMAIN_EXTENSIONS, "string")
+ .LoadFrom(&policy, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER);
+ EXPECT_TRUE(Matches(expected));
+}
+
+TEST_F(PolicyLoaderWinTest, LoadIntegerEncodedValues) {
+ base::DictionaryValue policy;
+ policy.SetBoolean("bool", true);
+ policy.SetInteger("int", 123);
+ policy.SetDouble("double", 456.0);
+
+ base::DictionaryValue encoded_policy;
+ encoded_policy.SetInteger("bool", 1);
+ encoded_policy.SetInteger("int", 123);
+ encoded_policy.SetInteger("double", 456);
+
+ const string16 kPathSuffix =
+ kRegistryMandatorySubKey + ASCIIToUTF16("\\3rdparty\\extensions\\int");
+ EXPECT_TRUE(InstallSchema(policy, HKEY_CURRENT_USER, kPathSuffix, kSchema));
+ EXPECT_TRUE(
+ InstallValue(encoded_policy, HKEY_CURRENT_USER, kPathSuffix, kMandatory));
- PolicyLoaderWin loader(&test_policy_definitions::kList);
- scoped_ptr<PolicyBundle> loaded(loader.Load());
- EXPECT_TRUE(loaded->Equals(expected));
+ PolicyBundle expected;
+ expected.Get(POLICY_DOMAIN_EXTENSIONS, "int")
+ .LoadFrom(&policy, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER);
+ EXPECT_TRUE(Matches(expected));
+}
+
+TEST_F(PolicyLoaderWinTest, DefaultPropertySchemaType) {
+ // Build a schema for an "object" with a default schema for its properties.
+ base::DictionaryValue default_schema;
+ default_schema.SetString(kType, "number");
+ base::DictionaryValue integer_schema;
+ integer_schema.SetString(kType, "integer");
+ base::DictionaryValue properties;
+ properties.Set("special-int1", integer_schema.DeepCopy());
+ properties.Set("special-int2", integer_schema.DeepCopy());
+ base::DictionaryValue schema;
+ schema.SetString(kType, "object");
+ schema.Set(kProperties, properties.DeepCopy());
+ schema.Set(kAdditionalProperties, default_schema.DeepCopy());
+
+ const string16 kPathSuffix =
+ kRegistryMandatorySubKey + ASCIIToUTF16("\\3rdparty\\extensions\\test");
+ EXPECT_TRUE(WriteSchema(schema, HKEY_CURRENT_USER, kPathSuffix, kSchema));
+
+ // Write some test values.
+ base::DictionaryValue policy;
+ // These special values have a specific schema for them.
+ policy.SetInteger("special-int1", 123);
+ policy.SetString("special-int2", "-456");
+ // Other values default to be loaded as doubles.
+ policy.SetInteger("double1", 789.0);
+ policy.SetString("double2", "123.456e7");
+ policy.SetString("invalid", "omg");
+ EXPECT_TRUE(InstallValue(policy, HKEY_CURRENT_USER, kPathSuffix, kMandatory));
+
+ base::DictionaryValue expected_policy;
+ expected_policy.SetInteger("special-int1", 123);
+ expected_policy.SetInteger("special-int2", -456);
+ expected_policy.SetDouble("double1", 789.0);
+ expected_policy.SetDouble("double2", 123.456e7);
+ PolicyBundle expected;
+ expected.Get(POLICY_DOMAIN_EXTENSIONS, "test")
+ .LoadFrom(&expected_policy, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER);
+ EXPECT_TRUE(Matches(expected));
}
} // namespace policy