xref: /aosp_15_r20/external/libchrome/components/policy/core/common/schema_map_unittest.cc (revision 635a864187cb8b6c713ff48b7e790a6b21769273)
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "components/policy/core/common/schema_map.h"
6 #include <memory>
7 
8 #include "base/memory/weak_ptr.h"
9 #include "base/values.h"
10 #include "components/policy/core/common/external_data_fetcher.h"
11 #include "components/policy/core/common/external_data_manager.h"
12 #include "components/policy/core/common/policy_bundle.h"
13 #include "components/policy/core/common/policy_map.h"
14 #include "components/policy/core/common/policy_types.h"
15 #include "components/policy/core/common/schema.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17 
18 namespace policy {
19 
20 namespace {
21 
22 const char kTestSchema[] =
23     "{"
24     "  \"type\": \"object\","
25     "  \"properties\": {"
26     "    \"string\": { \"type\": \"string\" },"
27     "    \"integer\": { \"type\": \"integer\" },"
28     "    \"boolean\": { \"type\": \"boolean\" },"
29     "    \"null\": { \"type\": \"null\" },"
30     "    \"double\": { \"type\": \"number\" },"
31     "    \"list\": {"
32     "      \"type\": \"array\","
33     "      \"items\": { \"type\": \"string\" }"
34     "    },"
35     "    \"object\": {"
36     "      \"type\": \"object\","
37     "      \"properties\": {"
38     "        \"a\": { \"type\": \"string\" },"
39     "        \"b\": { \"type\": \"integer\" }"
40     "      }"
41     "    }"
42     "  }"
43     "}";
44 
45 }  // namespace
46 
47 class SchemaMapTest : public testing::Test {
48  protected:
CreateTestSchema()49   Schema CreateTestSchema() {
50     std::string error;
51     Schema schema = Schema::Parse(kTestSchema, &error);
52     if (!schema.valid())
53       ADD_FAILURE() << error;
54     return schema;
55   }
56 
CreateTestMap()57   scoped_refptr<SchemaMap> CreateTestMap() {
58     Schema schema = CreateTestSchema();
59     ComponentMap component_map;
60     component_map["extension-1"] = schema;
61     component_map["extension-2"] = schema;
62     component_map["legacy-extension"] = Schema();
63 
64     DomainMap domain_map;
65     domain_map[POLICY_DOMAIN_EXTENSIONS] = component_map;
66 
67     return new SchemaMap(domain_map);
68   }
69 };
70 
TEST_F(SchemaMapTest,Empty)71 TEST_F(SchemaMapTest, Empty) {
72   scoped_refptr<SchemaMap> map = new SchemaMap();
73   EXPECT_TRUE(map->GetDomains().empty());
74   EXPECT_FALSE(map->GetComponents(POLICY_DOMAIN_CHROME));
75   EXPECT_FALSE(map->GetComponents(POLICY_DOMAIN_EXTENSIONS));
76   EXPECT_FALSE(map->GetSchema(PolicyNamespace(POLICY_DOMAIN_CHROME, "")));
77   EXPECT_FALSE(map->HasComponents());
78 }
79 
TEST_F(SchemaMapTest,HasComponents)80 TEST_F(SchemaMapTest, HasComponents) {
81   scoped_refptr<SchemaMap> map = new SchemaMap();
82   EXPECT_FALSE(map->HasComponents());
83 
84   // The Chrome schema does not count as a component.
85   Schema schema = CreateTestSchema();
86   ComponentMap component_map;
87   component_map[""] = schema;
88   DomainMap domain_map;
89   domain_map[POLICY_DOMAIN_CHROME] = component_map;
90   map = new SchemaMap(domain_map);
91   EXPECT_FALSE(map->HasComponents());
92 
93   // An extension schema does.
94   domain_map[POLICY_DOMAIN_EXTENSIONS] = component_map;
95   map = new SchemaMap(domain_map);
96   EXPECT_TRUE(map->HasComponents());
97 }
98 
TEST_F(SchemaMapTest,Lookups)99 TEST_F(SchemaMapTest, Lookups) {
100   scoped_refptr<SchemaMap> map = CreateTestMap();
101   ASSERT_TRUE(map.get());
102   EXPECT_TRUE(map->HasComponents());
103 
104   EXPECT_FALSE(map->GetSchema(
105       PolicyNamespace(POLICY_DOMAIN_CHROME, "")));
106   EXPECT_FALSE(map->GetSchema(
107       PolicyNamespace(POLICY_DOMAIN_CHROME, "extension-1")));
108   EXPECT_FALSE(map->GetSchema(
109       PolicyNamespace(POLICY_DOMAIN_CHROME, "legacy-extension")));
110   EXPECT_FALSE(map->GetSchema(
111       PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "")));
112   EXPECT_FALSE(map->GetSchema(
113       PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "extension-3")));
114 
115   const Schema* schema =
116       map->GetSchema(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "extension-1"));
117   ASSERT_TRUE(schema);
118   EXPECT_TRUE(schema->valid());
119 
120   schema = map->GetSchema(
121       PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "legacy-extension"));
122   ASSERT_TRUE(schema);
123   EXPECT_FALSE(schema->valid());
124 }
125 
TEST_F(SchemaMapTest,FilterBundle)126 TEST_F(SchemaMapTest, FilterBundle) {
127   std::string error;
128   Schema schema = Schema::Parse(kTestSchema, &error);
129   ASSERT_TRUE(schema.valid()) << error;
130 
131   DomainMap domain_map;
132   domain_map[POLICY_DOMAIN_EXTENSIONS]["abc"] = schema;
133   scoped_refptr<SchemaMap> schema_map = new SchemaMap(domain_map);
134 
135   PolicyBundle bundle;
136   schema_map->FilterBundle(&bundle);
137   const PolicyBundle empty_bundle;
138   EXPECT_TRUE(bundle.Equals(empty_bundle));
139 
140   // The Chrome namespace isn't filtered.
141   PolicyBundle expected_bundle;
142   PolicyNamespace chrome_ns(POLICY_DOMAIN_CHROME, "");
143   expected_bundle.Get(chrome_ns).Set(
144       "ChromePolicy", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
145       POLICY_SOURCE_CLOUD, std::make_unique<base::Value>("value"), nullptr);
146   bundle.CopyFrom(expected_bundle);
147 
148   // Unknown components are filtered out.
149   PolicyNamespace another_extension_ns(POLICY_DOMAIN_EXTENSIONS, "xyz");
150   bundle.Get(another_extension_ns)
151       .Set("AnotherExtensionPolicy", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
152            POLICY_SOURCE_CLOUD, std::make_unique<base::Value>("value"),
153            nullptr);
154   schema_map->FilterBundle(&bundle);
155   EXPECT_TRUE(bundle.Equals(expected_bundle));
156 
157   PolicyNamespace extension_ns(POLICY_DOMAIN_EXTENSIONS, "abc");
158   PolicyMap& map = expected_bundle.Get(extension_ns);
159   base::ListValue list;
160   list.AppendString("a");
161   list.AppendString("b");
162   map.Set("list", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
163           POLICY_SOURCE_CLOUD, list.CreateDeepCopy(), nullptr);
164   map.Set("boolean", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
165           POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(true), nullptr);
166   map.Set("integer", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
167           POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(1), nullptr);
168   map.Set("null", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
169           POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(), nullptr);
170   map.Set("double", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
171           POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(1.2), nullptr);
172   base::DictionaryValue dict;
173   dict.SetString("a", "b");
174   dict.SetInteger("b", 2);
175   map.Set("object", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
176           POLICY_SOURCE_CLOUD, dict.CreateDeepCopy(), nullptr);
177   map.Set("string", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
178           POLICY_SOURCE_CLOUD, std::make_unique<base::Value>("value"), nullptr);
179 
180   bundle.MergeFrom(expected_bundle);
181   bundle.Get(extension_ns)
182       .Set("Unexpected", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
183            POLICY_SOURCE_CLOUD, std::make_unique<base::Value>("to-be-removed"),
184            nullptr);
185 
186   schema_map->FilterBundle(&bundle);
187   EXPECT_TRUE(bundle.Equals(expected_bundle));
188 
189   // Mismatched types are also removed.
190   bundle.Clear();
191   PolicyMap& badmap = bundle.Get(extension_ns);
192   badmap.Set("list", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
193              POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(false),
194              nullptr);
195   badmap.Set("boolean", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
196              POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(0), nullptr);
197   badmap.Set("integer", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
198              POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(false),
199              nullptr);
200   badmap.Set("null", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
201              POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(false),
202              nullptr);
203   badmap.Set("double", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
204              POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(false),
205              nullptr);
206   badmap.Set("object", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
207              POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(false),
208              nullptr);
209   badmap.Set("string", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
210              POLICY_SOURCE_CLOUD, nullptr,
211              std::make_unique<ExternalDataFetcher>(nullptr, std::string()));
212 
213   schema_map->FilterBundle(&bundle);
214   EXPECT_TRUE(bundle.Equals(empty_bundle));
215 }
216 
TEST_F(SchemaMapTest,LegacyComponents)217 TEST_F(SchemaMapTest, LegacyComponents) {
218   std::string error;
219   Schema schema = Schema::Parse(
220       "{"
221       "  \"type\":\"object\","
222       "  \"properties\": {"
223       "    \"String\": { \"type\": \"string\" }"
224       "  }"
225       "}", &error);
226   ASSERT_TRUE(schema.valid()) << error;
227 
228   DomainMap domain_map;
229   domain_map[POLICY_DOMAIN_EXTENSIONS]["with-schema"] = schema;
230   domain_map[POLICY_DOMAIN_EXTENSIONS]["without-schema"] = Schema();
231   scoped_refptr<SchemaMap> schema_map = new SchemaMap(domain_map);
232 
233   // |bundle| contains policies loaded by a policy provider.
234   PolicyBundle bundle;
235 
236   // Known components with schemas are filtered.
237   PolicyNamespace extension_ns(POLICY_DOMAIN_EXTENSIONS, "with-schema");
238   bundle.Get(extension_ns)
239       .Set("String", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
240            POLICY_SOURCE_CLOUD, std::make_unique<base::Value>("value 1"),
241            nullptr);
242 
243   // The Chrome namespace isn't filtered.
244   PolicyNamespace chrome_ns(POLICY_DOMAIN_CHROME, "");
245   bundle.Get(chrome_ns).Set("ChromePolicy", POLICY_LEVEL_MANDATORY,
246                             POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
247                             std::make_unique<base::Value>("value 3"), nullptr);
248 
249   PolicyBundle expected_bundle;
250   expected_bundle.MergeFrom(bundle);
251 
252   // Known components without a schema are filtered out completely.
253   PolicyNamespace without_schema_ns(POLICY_DOMAIN_EXTENSIONS, "without-schema");
254   bundle.Get(without_schema_ns)
255       .Set("Schemaless", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
256            POLICY_SOURCE_CLOUD, std::make_unique<base::Value>("value 2"),
257            nullptr);
258 
259   // Unknown policies of known components with a schema are removed.
260   bundle.Get(extension_ns)
261       .Set("Surprise", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
262            POLICY_SOURCE_CLOUD, std::make_unique<base::Value>("value 4"),
263            nullptr);
264 
265   // Unknown components are removed.
266   PolicyNamespace unknown_ns(POLICY_DOMAIN_EXTENSIONS, "unknown");
267   bundle.Get(unknown_ns)
268       .Set("Surprise", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
269            POLICY_SOURCE_CLOUD, std::make_unique<base::Value>("value 5"),
270            nullptr);
271 
272   schema_map->FilterBundle(&bundle);
273   EXPECT_TRUE(bundle.Equals(expected_bundle));
274 }
275 
TEST_F(SchemaMapTest,GetChanges)276 TEST_F(SchemaMapTest, GetChanges) {
277   DomainMap map;
278   map[POLICY_DOMAIN_CHROME][""] = Schema();
279   scoped_refptr<SchemaMap> older = new SchemaMap(map);
280   map[POLICY_DOMAIN_CHROME][""] = Schema();
281   scoped_refptr<SchemaMap> newer = new SchemaMap(map);
282 
283   PolicyNamespaceList removed;
284   PolicyNamespaceList added;
285   newer->GetChanges(older, &removed, &added);
286   EXPECT_TRUE(removed.empty());
287   EXPECT_TRUE(added.empty());
288 
289   map[POLICY_DOMAIN_CHROME][""] = Schema();
290   map[POLICY_DOMAIN_EXTENSIONS]["xyz"] = Schema();
291   newer = new SchemaMap(map);
292   newer->GetChanges(older, &removed, &added);
293   EXPECT_TRUE(removed.empty());
294   ASSERT_EQ(1u, added.size());
295   EXPECT_EQ(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "xyz"), added[0]);
296 
297   older = newer;
298   map[POLICY_DOMAIN_EXTENSIONS]["abc"] = Schema();
299   newer = new SchemaMap(map);
300   newer->GetChanges(older, &removed, &added);
301   ASSERT_EQ(2u, removed.size());
302   EXPECT_EQ(PolicyNamespace(POLICY_DOMAIN_CHROME, ""), removed[0]);
303   EXPECT_EQ(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "xyz"), removed[1]);
304   ASSERT_EQ(1u, added.size());
305   EXPECT_EQ(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc"), added[0]);
306 }
307 
308 }  // namespace policy
309