blob: 8b9feaf8b9a1c43e700abbb907198cd09730604d [file] [log] [blame]
// Copyright 2013 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/configuration_policy_provider_test.h"
#include <utility>
#include "base/bind.h"
#include "base/callback.h"
#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
#include "base/values.h"
#include "components/policy/core/common/configuration_policy_provider.h"
#include "components/policy/core/common/external_data_fetcher.h"
#include "components/policy/core/common/mock_configuration_policy_provider.h"
#include "components/policy/core/common/policy_bundle.h"
#include "components/policy/core/common/policy_map.h"
#include "components/policy/core/common/policy_namespace.h"
#include "components/policy/core/common/policy_test_utils.h"
#include "components/policy/core/common/policy_types.h"
#include "testing/gmock/include/gmock/gmock.h"
using ::testing::Mock;
using ::testing::_;
namespace {
const char kTestChromeSchema[] =
"{"
" \"type\": \"object\","
" \"properties\": {"
" \"StringPolicy\": { \"type\": \"string\" },"
" \"BooleanPolicy\": { \"type\": \"boolean\" },"
" \"IntegerPolicy\": { \"type\": \"integer\" },"
" \"StringListPolicy\": {"
" \"type\": \"array\","
" \"items\": { \"type\": \"string\" }"
" },"
" \"DictionaryPolicy\": {"
" \"type\": \"object\","
" \"properties\": {"
" \"bool\": { \"type\": \"boolean\" },"
" \"double\": { \"type\": \"number\" },"
" \"int\": { \"type\": \"integer\" },"
" \"string\": { \"type\": \"string\" },"
" \"array\": {"
" \"type\": \"array\","
" \"items\": { \"type\": \"string\" }"
" },"
" \"dictionary\": {"
" \"type\": \"object\","
" \"properties\": {"
" \"sub\": { \"type\": \"string\" },"
" \"sublist\": {"
" \"type\": \"array\","
" \"items\": {"
" \"type\": \"object\","
" \"properties\": {"
" \"aaa\": { \"type\": \"integer\" },"
" \"bbb\": { \"type\": \"integer\" },"
" \"ccc\": { \"type\": \"string\" },"
" \"ddd\": { \"type\": \"string\" }"
" }"
" }"
" }"
" }"
" },"
" \"list\": {"
" \"type\": \"array\","
" \"items\": {"
" \"type\": \"object\","
" \"properties\": {"
" \"subdictindex\": { \"type\": \"integer\" },"
" \"subdict\": {"
" \"type\": \"object\","
" \"properties\": {"
" \"bool\": { \"type\": \"boolean\" },"
" \"double\": { \"type\": \"number\" },"
" \"int\": { \"type\": \"integer\" },"
" \"string\": { \"type\": \"string\" }"
" }"
" }"
" }"
" }"
" },"
" \"dict\": {"
" \"type\": \"object\","
" \"properties\": {"
" \"bool\": { \"type\": \"boolean\" },"
" \"double\": { \"type\": \"number\" },"
" \"int\": { \"type\": \"integer\" },"
" \"string\": { \"type\": \"string\" },"
" \"list\": {"
" \"type\": \"array\","
" \"items\": {"
" \"type\": \"object\","
" \"properties\": {"
" \"subdictindex\": { \"type\": \"integer\" },"
" \"subdict\": {"
" \"type\": \"object\","
" \"properties\": {"
" \"bool\": { \"type\": \"boolean\" },"
" \"double\": { \"type\": \"number\" },"
" \"int\": { \"type\": \"integer\" },"
" \"string\": { \"type\": \"string\" }"
" }"
" }"
" }"
" }"
" }"
" }"
" }"
" }"
" }"
" }"
"}";
} // anonymous namespace
namespace policy {
namespace test_keys {
// These are the defaults for PolicyProviderTestHarness but they are generally
// available for use in other tests. Subclasses of PolicyProviderTestHarness
// may use different values.
const char kKeyString[] = "StringPolicy";
const char kKeyBoolean[] = "BooleanPolicy";
const char kKeyInteger[] = "IntegerPolicy";
const char kKeyStringList[] = "StringListPolicy";
const char kKeyDictionary[] = "DictionaryPolicy";
} // namespace test_keys
PolicyTestBase::PolicyTestBase() {}
PolicyTestBase::~PolicyTestBase() {}
void PolicyTestBase::SetUp() {
const PolicyNamespace ns(POLICY_DOMAIN_CHROME, "");
ASSERT_TRUE(RegisterSchema(ns, kTestChromeSchema));
}
void PolicyTestBase::TearDown() {
base::RunLoop().RunUntilIdle();
}
bool PolicyTestBase::RegisterSchema(const PolicyNamespace& ns,
const std::string& schema_string) {
std::string error;
Schema schema = Schema::Parse(schema_string, &error);
if (schema.valid()) {
schema_registry_.RegisterComponent(ns, schema);
return true;
}
ADD_FAILURE() << error;
return false;
}
PolicyProviderTestHarness::PolicyProviderTestHarness(PolicyLevel level,
PolicyScope scope,
PolicySource source)
: key_string_(test_keys::kKeyString),
key_boolean_(test_keys::kKeyBoolean),
key_integer_(test_keys::kKeyInteger),
key_stringlist_(test_keys::kKeyStringList),
key_dictionary_(test_keys::kKeyDictionary),
test_schema_(kTestChromeSchema),
level_(level),
scope_(scope),
source_(source) {
}
PolicyProviderTestHarness::~PolicyProviderTestHarness() {}
void PolicyProviderTestHarness::Install3rdPartyPolicy(
const base::DictionaryValue* policies) {
FAIL();
}
ConfigurationPolicyProviderTest::ConfigurationPolicyProviderTest() {}
ConfigurationPolicyProviderTest::~ConfigurationPolicyProviderTest() {}
void ConfigurationPolicyProviderTest::SetUp() {
harness_.reset((*GetParam())());
harness_->SetUp();
const PolicyNamespace chrome_ns(POLICY_DOMAIN_CHROME, "");
ASSERT_TRUE(RegisterSchema(chrome_ns, harness_->test_schema()));
Schema chrome_schema = *schema_registry_.schema_map()->GetSchema(chrome_ns);
Schema extension_schema =
chrome_schema.GetKnownProperty(harness_->key_dictionary());
ASSERT_TRUE(extension_schema.valid());
schema_registry_.RegisterComponent(
PolicyNamespace(POLICY_DOMAIN_EXTENSIONS,
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
extension_schema);
schema_registry_.RegisterComponent(
PolicyNamespace(POLICY_DOMAIN_EXTENSIONS,
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"),
extension_schema);
schema_registry_.RegisterComponent(
PolicyNamespace(POLICY_DOMAIN_EXTENSIONS,
"cccccccccccccccccccccccccccccccc"),
extension_schema);
provider_.reset(
harness_->CreateProvider(&schema_registry_, loop_.task_runner()));
provider_->Init(&schema_registry_);
// Some providers do a reload on init. Make sure any notifications generated
// are fired now.
base::RunLoop().RunUntilIdle();
const PolicyBundle kEmptyBundle;
EXPECT_TRUE(provider_->policies().Equals(kEmptyBundle));
}
void ConfigurationPolicyProviderTest::TearDown() {
// Give providers the chance to clean up after themselves on the file thread.
if (provider_) {
provider_->Shutdown();
provider_.reset();
}
PolicyTestBase::TearDown();
}
void ConfigurationPolicyProviderTest::CheckValue(
const char* policy_name,
const base::Value& expected_value,
base::Closure install_value) {
// Install the value, reload policy and check the provider for the value.
install_value.Run();
provider_->RefreshPolicies();
base::RunLoop().RunUntilIdle();
PolicyBundle expected_bundle;
expected_bundle.Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()))
.Set(policy_name, harness_->policy_level(), harness_->policy_scope(),
harness_->policy_source(), expected_value.CreateDeepCopy(), nullptr);
bool match = provider_->policies().Equals(expected_bundle);
if (!match) {
LOG(ERROR) << "EXPECTED: " << expected_bundle;
LOG(ERROR) << "ACTUAL: " << provider_->policies();
}
EXPECT_TRUE(match);
// TODO(joaodasilva): set the policy in the POLICY_DOMAIN_EXTENSIONS too,
// and extend the |expected_bundle|, once all providers are ready.
}
TEST_P(ConfigurationPolicyProviderTest, Empty) {
provider_->RefreshPolicies();
base::RunLoop().RunUntilIdle();
const PolicyBundle kEmptyBundle;
EXPECT_TRUE(provider_->policies().Equals(kEmptyBundle));
}
TEST_P(ConfigurationPolicyProviderTest, StringValue) {
const char kTestString[] = "string_value";
base::StringValue expected_value(kTestString);
CheckValue(harness_->key_string(),
expected_value,
base::Bind(&PolicyProviderTestHarness::InstallStringPolicy,
base::Unretained(harness_.get()),
harness_->key_string(),
kTestString));
}
TEST_P(ConfigurationPolicyProviderTest, BooleanValue) {
base::FundamentalValue expected_value(true);
CheckValue(harness_->key_boolean(),
expected_value,
base::Bind(&PolicyProviderTestHarness::InstallBooleanPolicy,
base::Unretained(harness_.get()),
harness_->key_boolean(),
true));
}
TEST_P(ConfigurationPolicyProviderTest, IntegerValue) {
base::FundamentalValue expected_value(42);
CheckValue(harness_->key_integer(),
expected_value,
base::Bind(&PolicyProviderTestHarness::InstallIntegerPolicy,
base::Unretained(harness_.get()),
harness_->key_integer(),
42));
}
TEST_P(ConfigurationPolicyProviderTest, StringListValue) {
base::ListValue expected_value;
expected_value.Set(0U, new base::StringValue("first"));
expected_value.Set(1U, new base::StringValue("second"));
CheckValue(harness_->key_stringlist(),
expected_value,
base::Bind(&PolicyProviderTestHarness::InstallStringListPolicy,
base::Unretained(harness_.get()),
harness_->key_stringlist(),
&expected_value));
}
TEST_P(ConfigurationPolicyProviderTest, DictionaryValue) {
base::DictionaryValue expected_value;
expected_value.SetBoolean("bool", true);
expected_value.SetDouble("double", 123.456);
expected_value.SetInteger("int", 123);
expected_value.SetString("string", "omg");
base::ListValue* list = new base::ListValue();
list->Set(0U, new base::StringValue("first"));
list->Set(1U, new base::StringValue("second"));
expected_value.Set("array", list);
base::DictionaryValue* dict = new base::DictionaryValue();
dict->SetString("sub", "value");
list = new base::ListValue();
std::unique_ptr<base::DictionaryValue> sub(new base::DictionaryValue());
sub->SetInteger("aaa", 111);
sub->SetInteger("bbb", 222);
list->Append(std::move(sub));
sub.reset(new base::DictionaryValue());
sub->SetString("ccc", "333");
sub->SetString("ddd", "444");
list->Append(std::move(sub));
dict->Set("sublist", list);
expected_value.Set("dictionary", dict);
CheckValue(harness_->key_dictionary(),
expected_value,
base::Bind(&PolicyProviderTestHarness::InstallDictionaryPolicy,
base::Unretained(harness_.get()),
harness_->key_dictionary(),
&expected_value));
}
TEST_P(ConfigurationPolicyProviderTest, RefreshPolicies) {
PolicyBundle bundle;
EXPECT_TRUE(provider_->policies().Equals(bundle));
// OnUpdatePolicy is called even when there are no changes.
MockConfigurationPolicyObserver observer;
provider_->AddObserver(&observer);
EXPECT_CALL(observer, OnUpdatePolicy(provider_.get())).Times(1);
provider_->RefreshPolicies();
base::RunLoop().RunUntilIdle();
Mock::VerifyAndClearExpectations(&observer);
EXPECT_TRUE(provider_->policies().Equals(bundle));
// OnUpdatePolicy is called when there are changes.
harness_->InstallStringPolicy(harness_->key_string(), "value");
EXPECT_CALL(observer, OnUpdatePolicy(provider_.get())).Times(1);
provider_->RefreshPolicies();
base::RunLoop().RunUntilIdle();
Mock::VerifyAndClearExpectations(&observer);
bundle.Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()))
.Set(harness_->key_string(), harness_->policy_level(),
harness_->policy_scope(), harness_->policy_source(),
base::WrapUnique(new base::StringValue("value")), nullptr);
EXPECT_TRUE(provider_->policies().Equals(bundle));
provider_->RemoveObserver(&observer);
}
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("string", "string value");
base::ListValue* list = new base::ListValue();
for (int i = 0; i < 2; ++i) {
std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
dict->SetInteger("subdictindex", i);
dict->Set("subdict", policy_dict.DeepCopy());
list->Append(std::move(dict));
}
policy_dict.Set("list", list);
policy_dict.Set("dict", policy_dict.DeepCopy());
// Install these policies as a Chrome policy.
harness_->InstallDictionaryPolicy(harness_->key_dictionary(), &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",
new base::StringValue("invalid-value"));
harness_->Install3rdPartyPolicy(&policy_3rdparty);
provider_->RefreshPolicies();
base::RunLoop().RunUntilIdle();
PolicyMap expected_policy;
expected_policy.Set(harness_->key_dictionary(), harness_->policy_level(),
harness_->policy_scope(), harness_->policy_source(),
policy_dict.CreateDeepCopy(), nullptr);
PolicyBundle expected_bundle;
expected_bundle.Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()))
.CopyFrom(expected_policy);
expected_policy.Clear();
expected_policy.LoadFrom(&policy_dict,
harness_->policy_level(),
harness_->policy_scope(),
harness_->policy_source());
expected_bundle.Get(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS,
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"))
.CopyFrom(expected_policy);
expected_bundle.Get(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS,
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"))
.CopyFrom(expected_policy);
EXPECT_TRUE(provider_->policies().Equals(expected_bundle));
}
} // namespace policy