Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2724)

Unified Diff: chrome/browser/policy/policy_loader_win_unittest.cc

Issue 10656046: Use a schema to decode 3rd party policy on windows, when present. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Addressed comments Created 8 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chrome/browser/policy/policy_loader_win.cc ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/browser/policy/policy_loader_win_unittest.cc
diff --git a/chrome/browser/policy/policy_loader_win_unittest.cc b/chrome/browser/policy/policy_loader_win_unittest.cc
index 888fa8a05580460948ba309f9bd1d02bbccd2077..9173c26a7c124f740e5de4f10761ab4666e55fe6 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
« no previous file with comments | « chrome/browser/policy/policy_loader_win.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698