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