blob: a7313663af66c7fc299c0de68b197e09fee3dbab [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/schema_map.h"
#include "base/memory/ptr_util.h"
#include "base/memory/weak_ptr.h"
#include "base/values.h"
#include "components/policy/core/common/external_data_fetcher.h"
#include "components/policy/core/common/external_data_manager.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.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace policy {
namespace {
const char kTestSchema[] =
"{"
" \"type\": \"object\","
" \"properties\": {"
" \"string\": { \"type\": \"string\" },"
" \"integer\": { \"type\": \"integer\" },"
" \"boolean\": { \"type\": \"boolean\" },"
" \"null\": { \"type\": \"null\" },"
" \"double\": { \"type\": \"number\" },"
" \"list\": {"
" \"type\": \"array\","
" \"items\": { \"type\": \"string\" }"
" },"
" \"object\": {"
" \"type\": \"object\","
" \"properties\": {"
" \"a\": { \"type\": \"string\" },"
" \"b\": { \"type\": \"integer\" }"
" }"
" }"
" }"
"}";
} // namespace
class SchemaMapTest : public testing::Test {
protected:
Schema CreateTestSchema() {
std::string error;
Schema schema = Schema::Parse(kTestSchema, &error);
if (!schema.valid())
ADD_FAILURE() << error;
return schema;
}
scoped_refptr<SchemaMap> CreateTestMap() {
Schema schema = CreateTestSchema();
ComponentMap component_map;
component_map["extension-1"] = schema;
component_map["extension-2"] = schema;
component_map["legacy-extension"] = Schema();
DomainMap domain_map;
domain_map[POLICY_DOMAIN_EXTENSIONS] = component_map;
return new SchemaMap(domain_map);
}
};
TEST_F(SchemaMapTest, Empty) {
scoped_refptr<SchemaMap> map = new SchemaMap();
EXPECT_TRUE(map->GetDomains().empty());
EXPECT_FALSE(map->GetComponents(POLICY_DOMAIN_CHROME));
EXPECT_FALSE(map->GetComponents(POLICY_DOMAIN_EXTENSIONS));
EXPECT_FALSE(map->GetSchema(PolicyNamespace(POLICY_DOMAIN_CHROME, "")));
EXPECT_FALSE(map->HasComponents());
}
TEST_F(SchemaMapTest, HasComponents) {
scoped_refptr<SchemaMap> map = new SchemaMap();
EXPECT_FALSE(map->HasComponents());
// The Chrome schema does not count as a component.
Schema schema = CreateTestSchema();
ComponentMap component_map;
component_map[""] = schema;
DomainMap domain_map;
domain_map[POLICY_DOMAIN_CHROME] = component_map;
map = new SchemaMap(domain_map);
EXPECT_FALSE(map->HasComponents());
// An extension schema does.
domain_map[POLICY_DOMAIN_EXTENSIONS] = component_map;
map = new SchemaMap(domain_map);
EXPECT_TRUE(map->HasComponents());
}
TEST_F(SchemaMapTest, Lookups) {
scoped_refptr<SchemaMap> map = CreateTestMap();
ASSERT_TRUE(map.get());
EXPECT_TRUE(map->HasComponents());
EXPECT_FALSE(map->GetSchema(
PolicyNamespace(POLICY_DOMAIN_CHROME, "")));
EXPECT_FALSE(map->GetSchema(
PolicyNamespace(POLICY_DOMAIN_CHROME, "extension-1")));
EXPECT_FALSE(map->GetSchema(
PolicyNamespace(POLICY_DOMAIN_CHROME, "legacy-extension")));
EXPECT_FALSE(map->GetSchema(
PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "")));
EXPECT_FALSE(map->GetSchema(
PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "extension-3")));
const Schema* schema =
map->GetSchema(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "extension-1"));
ASSERT_TRUE(schema);
EXPECT_TRUE(schema->valid());
schema = map->GetSchema(
PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "legacy-extension"));
ASSERT_TRUE(schema);
EXPECT_FALSE(schema->valid());
}
TEST_F(SchemaMapTest, FilterBundle) {
std::string error;
Schema schema = Schema::Parse(kTestSchema, &error);
ASSERT_TRUE(schema.valid()) << error;
DomainMap domain_map;
domain_map[POLICY_DOMAIN_EXTENSIONS]["abc"] = schema;
scoped_refptr<SchemaMap> schema_map = new SchemaMap(domain_map);
PolicyBundle bundle;
schema_map->FilterBundle(&bundle);
const PolicyBundle empty_bundle;
EXPECT_TRUE(bundle.Equals(empty_bundle));
// The Chrome namespace isn't filtered.
PolicyBundle expected_bundle;
PolicyNamespace chrome_ns(POLICY_DOMAIN_CHROME, "");
expected_bundle.Get(chrome_ns).Set(
"ChromePolicy", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
POLICY_SOURCE_CLOUD, base::WrapUnique(new base::StringValue("value")),
nullptr);
bundle.CopyFrom(expected_bundle);
// Unknown components are filtered out.
PolicyNamespace another_extension_ns(POLICY_DOMAIN_EXTENSIONS, "xyz");
bundle.Get(another_extension_ns)
.Set("AnotherExtensionPolicy", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
POLICY_SOURCE_CLOUD,
base::WrapUnique(new base::StringValue("value")), nullptr);
schema_map->FilterBundle(&bundle);
EXPECT_TRUE(bundle.Equals(expected_bundle));
PolicyNamespace extension_ns(POLICY_DOMAIN_EXTENSIONS, "abc");
PolicyMap& map = expected_bundle.Get(extension_ns);
base::ListValue list;
list.AppendString("a");
list.AppendString("b");
map.Set("list", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
POLICY_SOURCE_CLOUD, list.CreateDeepCopy(), nullptr);
map.Set("boolean", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
POLICY_SOURCE_CLOUD,
base::WrapUnique(new base::FundamentalValue(true)), nullptr);
map.Set("integer", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
POLICY_SOURCE_CLOUD, base::WrapUnique(new base::FundamentalValue(1)),
nullptr);
map.Set("null", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
POLICY_SOURCE_CLOUD, base::Value::CreateNullValue(), nullptr);
map.Set("double", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
POLICY_SOURCE_CLOUD,
base::WrapUnique(new base::FundamentalValue(1.2)), nullptr);
base::DictionaryValue dict;
dict.SetString("a", "b");
dict.SetInteger("b", 2);
map.Set("object", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
POLICY_SOURCE_CLOUD, dict.CreateDeepCopy(), nullptr);
map.Set("string", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
POLICY_SOURCE_CLOUD, base::WrapUnique(new base::StringValue("value")),
nullptr);
bundle.MergeFrom(expected_bundle);
bundle.Get(extension_ns)
.Set("Unexpected", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
POLICY_SOURCE_CLOUD,
base::WrapUnique(new base::StringValue("to-be-removed")), nullptr);
schema_map->FilterBundle(&bundle);
EXPECT_TRUE(bundle.Equals(expected_bundle));
// Mismatched types are also removed.
bundle.Clear();
PolicyMap& badmap = bundle.Get(extension_ns);
badmap.Set("list", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
POLICY_SOURCE_CLOUD,
base::WrapUnique(new base::FundamentalValue(false)), nullptr);
badmap.Set("boolean", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
POLICY_SOURCE_CLOUD,
base::WrapUnique(new base::FundamentalValue(0)), nullptr);
badmap.Set("integer", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
POLICY_SOURCE_CLOUD,
base::WrapUnique(new base::FundamentalValue(false)), nullptr);
badmap.Set("null", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
POLICY_SOURCE_CLOUD,
base::WrapUnique(new base::FundamentalValue(false)), nullptr);
badmap.Set("double", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
POLICY_SOURCE_CLOUD,
base::WrapUnique(new base::FundamentalValue(false)), nullptr);
badmap.Set("object", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
POLICY_SOURCE_CLOUD,
base::WrapUnique(new base::FundamentalValue(false)), nullptr);
badmap.Set("string", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
POLICY_SOURCE_CLOUD, nullptr,
base::WrapUnique(new ExternalDataFetcher(nullptr, std::string())));
schema_map->FilterBundle(&bundle);
EXPECT_TRUE(bundle.Equals(empty_bundle));
}
TEST_F(SchemaMapTest, LegacyComponents) {
std::string error;
Schema schema = Schema::Parse(
"{"
" \"type\":\"object\","
" \"properties\": {"
" \"String\": { \"type\": \"string\" }"
" }"
"}", &error);
ASSERT_TRUE(schema.valid()) << error;
DomainMap domain_map;
domain_map[POLICY_DOMAIN_EXTENSIONS]["with-schema"] = schema;
domain_map[POLICY_DOMAIN_EXTENSIONS]["without-schema"] = Schema();
scoped_refptr<SchemaMap> schema_map = new SchemaMap(domain_map);
// |bundle| contains policies loaded by a policy provider.
PolicyBundle bundle;
// Known components with schemas are filtered.
PolicyNamespace extension_ns(POLICY_DOMAIN_EXTENSIONS, "with-schema");
bundle.Get(extension_ns)
.Set("String", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
POLICY_SOURCE_CLOUD,
base::WrapUnique(new base::StringValue("value 1")), nullptr);
// The Chrome namespace isn't filtered.
PolicyNamespace chrome_ns(POLICY_DOMAIN_CHROME, "");
bundle.Get(chrome_ns).Set("ChromePolicy", POLICY_LEVEL_MANDATORY,
POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
base::WrapUnique(new base::StringValue("value 3")),
nullptr);
PolicyBundle expected_bundle;
expected_bundle.MergeFrom(bundle);
// Known components without a schema are filtered out completely.
PolicyNamespace without_schema_ns(POLICY_DOMAIN_EXTENSIONS, "without-schema");
bundle.Get(without_schema_ns)
.Set("Schemaless", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
POLICY_SOURCE_CLOUD,
base::WrapUnique(new base::StringValue("value 2")), nullptr);
// Unknown policies of known components with a schema are removed.
bundle.Get(extension_ns)
.Set("Surprise", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
POLICY_SOURCE_CLOUD,
base::WrapUnique(new base::StringValue("value 4")), nullptr);
// Unknown components are removed.
PolicyNamespace unknown_ns(POLICY_DOMAIN_EXTENSIONS, "unknown");
bundle.Get(unknown_ns)
.Set("Surprise", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
POLICY_SOURCE_CLOUD,
base::WrapUnique(new base::StringValue("value 5")), nullptr);
schema_map->FilterBundle(&bundle);
EXPECT_TRUE(bundle.Equals(expected_bundle));
}
TEST_F(SchemaMapTest, GetChanges) {
DomainMap map;
map[POLICY_DOMAIN_CHROME][""] = Schema();
scoped_refptr<SchemaMap> older = new SchemaMap(map);
map[POLICY_DOMAIN_CHROME][""] = Schema();
scoped_refptr<SchemaMap> newer = new SchemaMap(map);
PolicyNamespaceList removed;
PolicyNamespaceList added;
newer->GetChanges(older, &removed, &added);
EXPECT_TRUE(removed.empty());
EXPECT_TRUE(added.empty());
map[POLICY_DOMAIN_CHROME][""] = Schema();
map[POLICY_DOMAIN_EXTENSIONS]["xyz"] = Schema();
newer = new SchemaMap(map);
newer->GetChanges(older, &removed, &added);
EXPECT_TRUE(removed.empty());
ASSERT_EQ(1u, added.size());
EXPECT_EQ(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "xyz"), added[0]);
older = newer;
map[POLICY_DOMAIN_EXTENSIONS]["abc"] = Schema();
newer = new SchemaMap(map);
newer->GetChanges(older, &removed, &added);
ASSERT_EQ(2u, removed.size());
EXPECT_EQ(PolicyNamespace(POLICY_DOMAIN_CHROME, ""), removed[0]);
EXPECT_EQ(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "xyz"), removed[1]);
ASSERT_EQ(1u, added.size());
EXPECT_EQ(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc"), added[0]);
}
} // namespace policy