diff options
Diffstat (limited to 'chromium/components/policy/core/common/policy_loader_win_unittest.cc')
-rw-r--r-- | chromium/components/policy/core/common/policy_loader_win_unittest.cc | 744 |
1 files changed, 744 insertions, 0 deletions
diff --git a/chromium/components/policy/core/common/policy_loader_win_unittest.cc b/chromium/components/policy/core/common/policy_loader_win_unittest.cc new file mode 100644 index 00000000000..71472eeb856 --- /dev/null +++ b/chromium/components/policy/core/common/policy_loader_win_unittest.cc @@ -0,0 +1,744 @@ +// Copyright (c) 2012 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 "components/policy/core/common/policy_loader_win.h" + +#include <windows.h> +#include <stddef.h> +#include <stdint.h> +#include <userenv.h> + +#include <algorithm> +#include <cstring> +#include <functional> +#include <string> +#include <utility> + +#include "base/callback.h" +#include "base/json/json_writer.h" +#include "base/path_service.h" +#include "base/process/process_handle.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "base/sys_byteorder.h" +#include "base/task/sequenced_task_runner.h" +#include "base/values.h" +#include "base/win/registry.h" +#include "base/win/win_util.h" +#include "components/policy/core/common/async_policy_provider.h" +#include "components/policy/core/common/configuration_policy_provider_test.h" +#include "components/policy/core/common/external_data_fetcher.h" +#include "components/policy/core/common/management/platform_management_service.h" +#include "components/policy/core/common/policy_bundle.h" +#include "components/policy/core/common/policy_map.h" +#include "components/policy/core/common/policy_types.h" +#include "components/policy/core/common/schema_map.h" +#include "components/strings/grit/components_strings.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::win::RegKey; + +namespace policy { + +namespace { + +// Constants for registry key names. +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 kTestPolicyKey[] = L"chrome.policy.key"; + +// 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 std::wstring& path, + const std::wstring& 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); + EXPECT_TRUE(key.Valid()); + switch (value.type()) { + case base::Value::Type::NONE: + return key.WriteValue(name.c_str(), L"") == ERROR_SUCCESS; + + case base::Value::Type::BOOLEAN: { + if (!value.is_bool()) + return false; + return key.WriteValue(name.c_str(), value.GetBool() ? 1 : 0) == + ERROR_SUCCESS; + } + + case base::Value::Type::INTEGER: { + if (!value.is_int()) + return false; + return key.WriteValue(name.c_str(), value.GetInt()) == ERROR_SUCCESS; + } + + case base::Value::Type::DOUBLE: { + std::wstring str_value = base::NumberToWString(value.GetDouble()); + return key.WriteValue(name.c_str(), str_value.c_str()) == ERROR_SUCCESS; + } + + case base::Value::Type::STRING: { + if (!value.is_string()) + return false; + return key.WriteValue( + name.c_str(), + base::as_wcstr(base::UTF8ToUTF16(value.GetString()))) == + ERROR_SUCCESS; + } + + case base::Value::Type::DICTIONARY: { + if (!value.is_dict()) + return false; + for (auto key_value : value.DictItems()) { + if (!InstallValue(key_value.second, hive, path + kPathSep + name, + base::UTF8ToWide(key_value.first))) { + return false; + } + } + return true; + } + + case base::Value::Type::LIST: { + if (!value.is_list()) + return false; + const base::Value::ConstListView& list_view = value.GetListDeprecated(); + for (size_t i = 0; i < list_view.size(); ++i) { + if (!InstallValue(list_view[i], hive, path + kPathSep + name, + base::NumberToWString(i + 1))) { + return false; + } + } + return true; + } + + case base::Value::Type::BINARY: + return false; + } + NOTREACHED(); + return false; +} + +// This class provides sandboxing and mocking for the parts of the Windows +// Registry implementing Group Policy. It prepares two temporary sandbox keys, +// one for HKLM and one for HKCU. A test's calls to the registry are redirected +// by Windows to these sandboxes, allowing the tests to manipulate and access +// policy as if it were active, but without actually changing the parts of the +// Registry that are managed by Group Policy. +class ScopedGroupPolicyRegistrySandbox { + public: + ScopedGroupPolicyRegistrySandbox(); + ScopedGroupPolicyRegistrySandbox(const ScopedGroupPolicyRegistrySandbox&) = + delete; + ScopedGroupPolicyRegistrySandbox& operator=( + const ScopedGroupPolicyRegistrySandbox&) = delete; + ~ScopedGroupPolicyRegistrySandbox(); + + // Activates the registry keys overrides. This must be called before doing any + // writes to registry and the call should be wrapped in + // ASSERT_NO_FATAL_FAILURE. + void ActivateOverrides(); + + private: + void RemoveOverrides(); + + // Deletes the sandbox keys. + void DeleteKeys(); + + std::wstring key_name_; + + // Keys are created for the lifetime of a test to contain + // the sandboxed HKCU and HKLM hives, respectively. + RegKey temp_hkcu_hive_key_; + RegKey temp_hklm_hive_key_; +}; + +// A test harness that feeds policy via the Chrome GPO registry subtree. +class RegistryTestHarness : public PolicyProviderTestHarness { + public: + RegistryTestHarness(HKEY hive, PolicyScope scope); + RegistryTestHarness(const RegistryTestHarness&) = delete; + RegistryTestHarness& operator=(const RegistryTestHarness&) = delete; + ~RegistryTestHarness() override; + + // PolicyProviderTestHarness: + void SetUp() override; + + ConfigurationPolicyProvider* CreateProvider( + SchemaRegistry* registry, + scoped_refptr<base::SequencedTaskRunner> task_runner) override; + + void InstallEmptyPolicy() override; + void InstallStringPolicy(const std::string& policy_name, + const std::string& policy_value) override; + void InstallIntegerPolicy(const std::string& policy_name, + int policy_value) override; + void InstallBooleanPolicy(const std::string& policy_name, + bool policy_value) override; + void InstallStringListPolicy(const std::string& policy_name, + const base::ListValue* policy_value) override; + void InstallDictionaryPolicy(const std::string& policy_name, + const base::Value* policy_value) override; + void Install3rdPartyPolicy(const base::DictionaryValue* policies) override; + + // Creates a harness instance that will install policy in HKCU or HKLM, + // respectively. + static PolicyProviderTestHarness* CreateHKCU(); + static PolicyProviderTestHarness* CreateHKLM(); + + private: + HKEY hive_; + + ScopedGroupPolicyRegistrySandbox registry_sandbox_; +}; + +ScopedGroupPolicyRegistrySandbox::ScopedGroupPolicyRegistrySandbox() {} + +ScopedGroupPolicyRegistrySandbox::~ScopedGroupPolicyRegistrySandbox() { + RemoveOverrides(); + DeleteKeys(); +} + +void ScopedGroupPolicyRegistrySandbox::ActivateOverrides() { + // Generate a unique registry key for the override for each test. This + // makes sure that tests executing in parallel won't delete each other's + // key, at DeleteKeys(). + key_name_ = base::ASCIIToWide(base::StringPrintf( + "SOFTWARE\\chromium unittest %" CrPRIdPid, base::GetCurrentProcId())); + std::wstring hklm_key_name = key_name_ + L"\\HKLM"; + std::wstring hkcu_key_name = key_name_ + L"\\HKCU"; + + // Delete the registry test keys if they already exist (this could happen if + // the process id got recycled and the last test running under the same + // process id crashed ). + DeleteKeys(); + + // Create the subkeys to hold the overridden HKLM and HKCU + // policy settings. + temp_hklm_hive_key_.Create(HKEY_CURRENT_USER, + hklm_key_name.c_str(), + KEY_ALL_ACCESS); + temp_hkcu_hive_key_.Create(HKEY_CURRENT_USER, + hkcu_key_name.c_str(), + KEY_ALL_ACCESS); + + auto result_override_hklm = + RegOverridePredefKey(HKEY_LOCAL_MACHINE, temp_hklm_hive_key_.Handle()); + auto result_override_hkcu = + RegOverridePredefKey(HKEY_CURRENT_USER, temp_hkcu_hive_key_.Handle()); + + if (result_override_hklm != ERROR_SUCCESS || + result_override_hkcu != ERROR_SUCCESS) { + // We need to remove the overrides first in case one succeeded and one + // failed, otherwise deleting the keys fails. + RemoveOverrides(); + DeleteKeys(); + + // Assert on the actual results to print the error code in failure case. + ASSERT_HRESULT_SUCCEEDED(result_override_hklm); + ASSERT_HRESULT_SUCCEEDED(result_override_hkcu); + } +} + +void ScopedGroupPolicyRegistrySandbox::RemoveOverrides() { + ASSERT_HRESULT_SUCCEEDED(RegOverridePredefKey(HKEY_LOCAL_MACHINE, nullptr)); + ASSERT_HRESULT_SUCCEEDED(RegOverridePredefKey(HKEY_CURRENT_USER, nullptr)); +} + +void ScopedGroupPolicyRegistrySandbox::DeleteKeys() { + RegKey key(HKEY_CURRENT_USER, key_name_.c_str(), KEY_ALL_ACCESS); + ASSERT_TRUE(key.Valid()); + key.DeleteKey(L""); +} + +RegistryTestHarness::RegistryTestHarness(HKEY hive, PolicyScope scope) + : PolicyProviderTestHarness(POLICY_LEVEL_MANDATORY, scope, + POLICY_SOURCE_PLATFORM), + hive_(hive) { +} + +RegistryTestHarness::~RegistryTestHarness() {} + +void RegistryTestHarness::SetUp() { + // SetUp is called at gtest SetUp time, and gtest documentation guarantees + // that the test will not be executed if SetUp has a fatal failure. This is + // important, see crbug.com/721691. + ASSERT_NO_FATAL_FAILURE(registry_sandbox_.ActivateOverrides()); +} + +ConfigurationPolicyProvider* RegistryTestHarness::CreateProvider( + SchemaRegistry* registry, + scoped_refptr<base::SequencedTaskRunner> task_runner) { + base::win::ScopedDomainStateForTesting scoped_domain(true); + std::unique_ptr<AsyncPolicyLoader> loader(new PolicyLoaderWin( + task_runner, PlatformManagementService::GetInstance(), kTestPolicyKey)); + return new AsyncPolicyProvider(registry, std::move(loader)); +} + +void RegistryTestHarness::InstallEmptyPolicy() {} + +void RegistryTestHarness::InstallStringPolicy( + const std::string& policy_name, + const std::string& policy_value) { + RegKey key(hive_, kTestPolicyKey, KEY_ALL_ACCESS); + ASSERT_TRUE(key.Valid()); + ASSERT_HRESULT_SUCCEEDED( + key.WriteValue(base::UTF8ToWide(policy_name).c_str(), + base::UTF8ToWide(policy_value).c_str())); +} + +void RegistryTestHarness::InstallIntegerPolicy( + const std::string& policy_name, + int policy_value) { + RegKey key(hive_, kTestPolicyKey, KEY_ALL_ACCESS); + ASSERT_TRUE(key.Valid()); + key.WriteValue(base::UTF8ToWide(policy_name).c_str(), + static_cast<DWORD>(policy_value)); +} + +void RegistryTestHarness::InstallBooleanPolicy( + const std::string& policy_name, + bool policy_value) { + RegKey key(hive_, kTestPolicyKey, KEY_ALL_ACCESS); + ASSERT_TRUE(key.Valid()); + key.WriteValue(base::UTF8ToWide(policy_name).c_str(), + static_cast<DWORD>(policy_value)); +} + +void RegistryTestHarness::InstallStringListPolicy( + const std::string& policy_name, + const base::ListValue* policy_value) { + RegKey key( + hive_, + (std::wstring(kTestPolicyKey) + L"\\" + base::UTF8ToWide(policy_name)) + .c_str(), + KEY_ALL_ACCESS); + ASSERT_TRUE(key.Valid()); + int index = 1; + for (const auto& element : policy_value->GetListDeprecated()) { + if (!element.is_string()) + continue; + + std::string name(base::NumberToString(index++)); + key.WriteValue(base::UTF8ToWide(name).c_str(), + base::UTF8ToWide(element.GetString()).c_str()); + } +} + +void RegistryTestHarness::InstallDictionaryPolicy( + const std::string& policy_name, + const base::Value* policy_value) { + std::string json; + base::JSONWriter::Write(*policy_value, &json); + RegKey key(hive_, kTestPolicyKey, KEY_ALL_ACCESS); + ASSERT_TRUE(key.Valid()); + key.WriteValue(base::UTF8ToWide(policy_name).c_str(), + base::UTF8ToWide(json).c_str()); +} + +void RegistryTestHarness::Install3rdPartyPolicy( + const base::DictionaryValue* policies) { + // The first level entries are domains, and the second level entries map + // components to their policy. + const std::wstring kPathPrefix = + std::wstring(kTestPolicyKey) + kPathSep + kThirdParty + kPathSep; + for (auto domain : policies->DictItems()) { + const base::Value& components = domain.second; + if (!components.is_dict()) { + ADD_FAILURE(); + continue; + } + for (auto component : components.DictItems()) { + const std::wstring path = kPathPrefix + base::UTF8ToWide(domain.first) + + kPathSep + base::UTF8ToWide(component.first); + InstallValue(component.second, hive_, path, kMandatory); + } + } +} + +// static +PolicyProviderTestHarness* RegistryTestHarness::CreateHKCU() { + return new RegistryTestHarness(HKEY_CURRENT_USER, POLICY_SCOPE_USER); +} + +// static +PolicyProviderTestHarness* RegistryTestHarness::CreateHKLM() { + return new RegistryTestHarness(HKEY_LOCAL_MACHINE, POLICY_SCOPE_MACHINE); +} + +} // namespace + +// Instantiate abstract test case for basic policy reading tests. +INSTANTIATE_TEST_SUITE_P(PolicyProviderWinTest, + ConfigurationPolicyProviderTest, + testing::Values(RegistryTestHarness::CreateHKCU, + RegistryTestHarness::CreateHKLM)); + +// Instantiate abstract test case for 3rd party policy reading tests. +INSTANTIATE_TEST_SUITE_P(ThirdPartyPolicyProviderWinTest, + Configuration3rdPartyPolicyProviderTest, + testing::Values(RegistryTestHarness::CreateHKCU, + RegistryTestHarness::CreateHKLM)); + +// Test cases for windows policy provider specific functionality. +class PolicyLoaderWinTest : public PolicyTestBase { + protected: + // The policy key this tests places data under. This must match the data + // files in chrome/test/data/policy/gpo. + static const wchar_t kTestPolicyKey[]; + + PolicyLoaderWinTest() : scoped_domain_(false) {} + ~PolicyLoaderWinTest() override {} + + void SetUp() override { + PolicyTestBase::SetUp(); + + // Activate overrides of registry keys. gtest documentation guarantees + // that the test will not be executed if SetUp has a fatal failure. This is + // important, see crbug.com/721691. + ASSERT_NO_FATAL_FAILURE(registry_sandbox_.ActivateOverrides()); + } + + bool Matches(const PolicyBundle& expected) { + PolicyLoaderWin loader(task_environment_.GetMainThreadTaskRunner(), + PlatformManagementService::GetInstance(), + kTestPolicyKey); + std::unique_ptr<PolicyBundle> loaded( + loader.InitialLoad(schema_registry_.schema_map())); + return loaded->Equals(expected); + } + + ScopedGroupPolicyRegistrySandbox registry_sandbox_; + base::win::ScopedDomainStateForTesting scoped_domain_; +}; + +const wchar_t PolicyLoaderWinTest::kTestPolicyKey[] = + L"SOFTWARE\\Policies\\Chromium"; + +TEST_F(PolicyLoaderWinTest, HKLMOverHKCU) { + RegKey hklm_key(HKEY_LOCAL_MACHINE, kTestPolicyKey, KEY_ALL_ACCESS); + ASSERT_TRUE(hklm_key.Valid()); + hklm_key.WriteValue(base::UTF8ToWide(test_keys::kKeyString).c_str(), + base::UTF8ToWide("hklm").c_str()); + RegKey hkcu_key(HKEY_CURRENT_USER, kTestPolicyKey, KEY_ALL_ACCESS); + ASSERT_TRUE(hkcu_key.Valid()); + hkcu_key.WriteValue(base::UTF8ToWide(test_keys::kKeyString).c_str(), + base::UTF8ToWide("hkcu").c_str()); + + PolicyBundle expected; + expected.Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string())) + .Set(test_keys::kKeyString, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE, + POLICY_SOURCE_PLATFORM, base::Value("hklm"), nullptr); + expected.Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string())) + .GetMutable(test_keys::kKeyString) + ->AddMessage(PolicyMap::MessageType::kWarning, + IDS_POLICY_CONFLICT_DIFF_VALUE); + + PolicyMap::Entry conflict(POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, + POLICY_SOURCE_PLATFORM, base::Value("hkcu"), + nullptr); + expected.Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string())) + .GetMutable(test_keys::kKeyString) + ->AddConflictingPolicy(std::move(conflict)); + EXPECT_TRUE(Matches(expected)); +} + +TEST_F(PolicyLoaderWinTest, Merge3rdPartyPolicies) { + // Policy for the same extension will be provided at the 4 level/scope + // combinations, to verify that they overlap as expected. + const PolicyNamespace ns(POLICY_DOMAIN_EXTENSIONS, "merge"); + ASSERT_TRUE(RegisterSchema( + ns, + "{" + " \"type\": \"object\"," + " \"properties\": {" + " \"a\": { \"type\": \"string\" }," + " \"b\": { \"type\": \"string\" }," + " \"c\": { \"type\": \"string\" }," + " \"d\": { \"type\": \"string\" }" + " }" + "}")); + + const std::wstring kPathSuffix = + kTestPolicyKey + std::wstring(L"\\3rdparty\\extensions\\merge"); + + const char kUserMandatory[] = "user-mandatory"; + const char kUserRecommended[] = "user-recommended"; + const char kMachineMandatory[] = "machine-mandatory"; + const char kMachineRecommended[] = "machine-recommended"; + + base::DictionaryValue policy; + policy.SetStringKey("a", kMachineMandatory); + EXPECT_TRUE(InstallValue(policy, HKEY_LOCAL_MACHINE, + kPathSuffix, kMandatory)); + policy.SetStringKey("a", kUserMandatory); + policy.SetStringKey("b", kUserMandatory); + EXPECT_TRUE(InstallValue(policy, HKEY_CURRENT_USER, + kPathSuffix, kMandatory)); + policy.SetStringKey("a", kMachineRecommended); + policy.SetStringKey("b", kMachineRecommended); + policy.SetStringKey("c", kMachineRecommended); + EXPECT_TRUE(InstallValue(policy, HKEY_LOCAL_MACHINE, + kPathSuffix, kRecommended)); + policy.SetStringKey("a", kUserRecommended); + policy.SetStringKey("b", kUserRecommended); + policy.SetStringKey("c", kUserRecommended); + policy.SetStringKey("d", kUserRecommended); + EXPECT_TRUE(InstallValue(policy, HKEY_CURRENT_USER, + kPathSuffix, kRecommended)); + + PolicyBundle expected; + PolicyMap& expected_policy = expected.Get(ns); + expected_policy.Set("a", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE, + POLICY_SOURCE_PLATFORM, base::Value(kMachineMandatory), + nullptr); + expected_policy.GetMutable("a")->AddMessage(PolicyMap::MessageType::kWarning, + IDS_POLICY_CONFLICT_DIFF_VALUE); + expected_policy.GetMutable("a")->AddMessage(PolicyMap::MessageType::kWarning, + IDS_POLICY_CONFLICT_DIFF_VALUE); + expected_policy.GetMutable("a")->AddMessage(PolicyMap::MessageType::kWarning, + IDS_POLICY_CONFLICT_DIFF_VALUE); + + PolicyMap::Entry a_conflict_1(POLICY_LEVEL_RECOMMENDED, POLICY_SCOPE_MACHINE, + POLICY_SOURCE_PLATFORM, + base::Value(kMachineRecommended), nullptr); + PolicyMap::Entry a_conflict_2(POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, + POLICY_SOURCE_PLATFORM, + base::Value(kUserMandatory), nullptr); + PolicyMap::Entry a_conflict_3(POLICY_LEVEL_RECOMMENDED, POLICY_SCOPE_USER, + POLICY_SOURCE_PLATFORM, + base::Value(kUserRecommended), nullptr); + expected_policy.GetMutable("a")->AddConflictingPolicy( + std::move(a_conflict_1)); + expected_policy.GetMutable("a")->AddConflictingPolicy( + std::move(a_conflict_2)); + expected_policy.GetMutable("a")->AddConflictingPolicy( + std::move(a_conflict_3)); + + expected_policy.Set("b", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, + POLICY_SOURCE_PLATFORM, base::Value(kUserMandatory), + nullptr); + expected_policy.GetMutable("b")->AddMessage(PolicyMap::MessageType::kWarning, + IDS_POLICY_CONFLICT_DIFF_VALUE); + expected_policy.GetMutable("b")->AddMessage(PolicyMap::MessageType::kWarning, + IDS_POLICY_CONFLICT_DIFF_VALUE); + + PolicyMap::Entry b_conflict_1(POLICY_LEVEL_RECOMMENDED, POLICY_SCOPE_MACHINE, + POLICY_SOURCE_PLATFORM, + base::Value(kMachineRecommended), nullptr); + PolicyMap::Entry b_conflict_2(POLICY_LEVEL_RECOMMENDED, POLICY_SCOPE_USER, + POLICY_SOURCE_PLATFORM, + base::Value(kUserRecommended), nullptr); + expected_policy.GetMutable("b")->AddConflictingPolicy( + std::move(b_conflict_1)); + expected_policy.GetMutable("b")->AddConflictingPolicy( + std::move(b_conflict_2)); + + expected_policy.Set("c", POLICY_LEVEL_RECOMMENDED, POLICY_SCOPE_MACHINE, + POLICY_SOURCE_PLATFORM, base::Value(kMachineRecommended), + nullptr); + expected_policy.GetMutable("c")->AddMessage(PolicyMap::MessageType::kWarning, + IDS_POLICY_CONFLICT_DIFF_VALUE); + + PolicyMap::Entry c_conflict_1(POLICY_LEVEL_RECOMMENDED, POLICY_SCOPE_USER, + POLICY_SOURCE_PLATFORM, + base::Value(kUserRecommended), nullptr); + expected_policy.GetMutable("c")->AddConflictingPolicy( + std::move(c_conflict_1)); + + expected_policy.Set("d", POLICY_LEVEL_RECOMMENDED, POLICY_SCOPE_USER, + POLICY_SOURCE_PLATFORM, base::Value(kUserRecommended), + nullptr); + EXPECT_TRUE(Matches(expected)); +} + +TEST_F(PolicyLoaderWinTest, LoadStringEncodedValues) { + // Create a dictionary with all the types that can be stored encoded in a + // string. + const PolicyNamespace ns(POLICY_DOMAIN_EXTENSIONS, "string"); + ASSERT_TRUE(RegisterSchema(ns, + R"({ + "type": "object", + "id": "MainType", + "properties": { + "bool": { "type": "boolean" }, + "int": { "type": "integer" }, + "double": { "type": "number" }, + "list": { + "type": "array", + "items": { "$ref": "MainType" } + }, + "dict": { "$ref": "MainType" } + } + })")); + + base::DictionaryValue policy; + policy.SetBoolKey("bool", true); + policy.SetIntKey("int", -123); + policy.SetDoubleKey("double", 456.78e9); + base::ListValue list; + list.Append(std::make_unique<base::Value>(policy.Clone())); + list.Append(std::make_unique<base::Value>(policy.Clone())); + policy.SetKey("list", list.Clone()); + // Encode |policy| before adding the "dict" entry. + std::string encoded_dict; + base::JSONWriter::Write(policy, &encoded_dict); + ASSERT_FALSE(encoded_dict.empty()); + policy.SetKey("dict", policy.Clone()); + std::string encoded_list; + base::JSONWriter::Write(list, &encoded_list); + ASSERT_FALSE(encoded_list.empty()); + base::DictionaryValue encoded_policy; + encoded_policy.SetStringKey("bool", "1"); + encoded_policy.SetStringKey("int", "-123"); + encoded_policy.SetStringKey("double", "456.78e9"); + encoded_policy.SetStringKey("list", encoded_list); + encoded_policy.SetStringKey("dict", encoded_dict); + + const std::wstring kPathSuffix = + kTestPolicyKey + std::wstring(L"\\3rdparty\\extensions\\string"); + EXPECT_TRUE( + InstallValue(encoded_policy, HKEY_CURRENT_USER, kPathSuffix, kMandatory)); + + PolicyBundle expected; + expected.Get(ns).LoadFrom(&policy, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, + POLICY_SOURCE_PLATFORM); + EXPECT_TRUE(Matches(expected)); +} + +TEST_F(PolicyLoaderWinTest, LoadIntegerEncodedValues) { + const PolicyNamespace ns(POLICY_DOMAIN_EXTENSIONS, "int"); + ASSERT_TRUE(RegisterSchema( + ns, + "{" + " \"type\": \"object\"," + " \"properties\": {" + " \"bool\": { \"type\": \"boolean\" }," + " \"int\": { \"type\": \"integer\" }," + " \"double\": { \"type\": \"number\" }" + " }" + "}")); + + base::DictionaryValue encoded_policy; + encoded_policy.SetIntKey("bool", 1); + encoded_policy.SetIntKey("int", 123); + encoded_policy.SetIntKey("double", 456); + + const std::wstring kPathSuffix = + kTestPolicyKey + std::wstring(L"\\3rdparty\\extensions\\int"); + EXPECT_TRUE( + InstallValue(encoded_policy, HKEY_CURRENT_USER, kPathSuffix, kMandatory)); + + base::DictionaryValue policy; + policy.SetBoolKey("bool", true); + policy.SetIntKey("int", 123); + policy.SetDoubleKey("double", 456.0); + PolicyBundle expected; + expected.Get(ns).LoadFrom(&policy, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, + POLICY_SOURCE_PLATFORM); + EXPECT_TRUE(Matches(expected)); +} + +TEST_F(PolicyLoaderWinTest, DefaultPropertySchemaType) { + // Build a schema for an "object" with a default schema for its properties. + // Note that the top-level object can't have "additionalProperties", so + // a "policy" property is used instead. + const PolicyNamespace ns(POLICY_DOMAIN_EXTENSIONS, "test"); + ASSERT_TRUE(RegisterSchema( + ns, + "{" + " \"type\": \"object\"," + " \"properties\": {" + " \"policy\": {" + " \"type\": \"object\"," + " \"properties\": {" + " \"special-int1\": { \"type\": \"integer\" }," + " \"special-int2\": { \"type\": \"integer\" }" + " }," + " \"additionalProperties\": { \"type\": \"number\" }" + " }" + " }" + "}")); + + // Write some test values. + base::DictionaryValue policy; + // These special values have a specific schema for them. + policy.SetIntKey("special-int1", 123); + policy.SetStringKey("special-int2", "-456"); + // Other values default to be loaded as doubles. + policy.SetIntKey("double1", 789.0); + policy.SetStringKey("double2", "123.456e7"); + policy.SetStringKey("invalid", "omg"); + base::DictionaryValue all_policies; + all_policies.SetKey("policy", policy.Clone()); + + const std::wstring kPathSuffix = + kTestPolicyKey + std::wstring(L"\\3rdparty\\extensions\\test"); + EXPECT_TRUE( + InstallValue(all_policies, HKEY_CURRENT_USER, kPathSuffix, kMandatory)); + + base::DictionaryValue expected_policy; + expected_policy.SetIntKey("special-int1", 123); + expected_policy.SetIntKey("special-int2", -456); + expected_policy.SetDoubleKey("double1", 789.0); + expected_policy.SetDoubleKey("double2", 123.456e7); + base::DictionaryValue expected_policies; + expected_policies.SetKey("policy", expected_policy.Clone()); + PolicyBundle expected; + expected.Get(ns).LoadFrom(&expected_policies, POLICY_LEVEL_MANDATORY, + POLICY_SCOPE_USER, POLICY_SOURCE_PLATFORM); + EXPECT_TRUE(Matches(expected)); +} + +TEST_F(PolicyLoaderWinTest, AlternativePropertySchemaType) { + const char kTestSchema[] = + "{" + " \"type\": \"object\"," + " \"properties\": {" + " \"policy 1\": { \"type\": \"integer\" }," + " \"policy 2\": { \"type\": \"integer\" }" + " }" + "}"; + // Register two namespaces. One will be completely populated with all defined + // properties and the second will be only partially populated. + const PolicyNamespace ns_a( + POLICY_DOMAIN_EXTENSIONS, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + const PolicyNamespace ns_b( + POLICY_DOMAIN_EXTENSIONS, "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"); + ASSERT_TRUE(RegisterSchema(ns_a, kTestSchema)); + ASSERT_TRUE(RegisterSchema(ns_b, kTestSchema)); + + PolicyBundle expected; + base::DictionaryValue expected_a; + expected_a.SetIntKey("policy 1", 3); + expected_a.SetIntKey("policy 2", 3); + expected.Get(ns_a).LoadFrom(&expected_a, POLICY_LEVEL_MANDATORY, + POLICY_SCOPE_MACHINE, POLICY_SOURCE_PLATFORM); + base::DictionaryValue expected_b; + expected_b.SetIntKey("policy 1", 2); + expected.Get(ns_b).LoadFrom(&expected_b, POLICY_LEVEL_MANDATORY, + POLICY_SCOPE_MACHINE, POLICY_SOURCE_PLATFORM); + + const std::wstring kPathSuffix = + kTestPolicyKey + + std::wstring(L"\\3rdparty\\extensions\\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + EXPECT_TRUE( + InstallValue(expected_a, HKEY_LOCAL_MACHINE, kPathSuffix, kMandatory)); + const std::wstring kPathSuffix2 = + kTestPolicyKey + + std::wstring(L"\\3rdparty\\extensions\\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"); + EXPECT_TRUE( + InstallValue(expected_b, HKEY_LOCAL_MACHINE, kPathSuffix2, kMandatory)); + + EXPECT_TRUE(Matches(expected)); +} + +} // namespace policy |